mirror of
https://github.com/FlightControl-Master/MOOSE.git
synced 2025-08-15 10:47:21 +00:00
Compare commits
136 Commits
v2.5.0pre2
...
v2.5.3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
311ab19389 | ||
|
|
e9f92d2250 | ||
|
|
d469374ed5 | ||
|
|
a6ff84c09a | ||
|
|
3ed8ca63a8 | ||
|
|
89aa08829e | ||
|
|
5146106885 | ||
|
|
f05fc956db | ||
|
|
d10adb5de8 | ||
|
|
45aac21b63 | ||
|
|
b52643bb36 | ||
|
|
036010c39d | ||
|
|
9440d2b2c3 | ||
|
|
29727ec9a6 | ||
|
|
eb0b43cea8 | ||
|
|
a6e16fb7ad | ||
|
|
616b80710a | ||
|
|
94f206af68 | ||
|
|
ae6613fae1 | ||
|
|
b61cfc7390 | ||
|
|
e2c5688670 | ||
|
|
1d016d9fce | ||
|
|
cbc5b5bb89 | ||
|
|
218c7736e0 | ||
|
|
8e46e41b34 | ||
|
|
bcf639d076 | ||
|
|
697d12aefc | ||
|
|
89afd2b4c0 | ||
|
|
3358f98bc4 | ||
|
|
417af6a93c | ||
|
|
9a2f6c7faa | ||
|
|
e52b0edc4e | ||
|
|
c51a65f058 | ||
|
|
5b2e67df19 | ||
|
|
7b7605e501 | ||
|
|
49d7d9ee2e | ||
|
|
774c8971c8 | ||
|
|
545c0886fa | ||
|
|
721d027fb3 | ||
|
|
5185ea35fe | ||
|
|
ec039f2999 | ||
|
|
c0fc649f8b | ||
|
|
c49497e68c | ||
|
|
57bc6de12b | ||
|
|
1b80d68f50 | ||
|
|
787151597c | ||
|
|
f725039da5 | ||
|
|
a4f15a0830 | ||
|
|
084f00afad | ||
|
|
87f4ebee64 | ||
|
|
a0276821d9 | ||
|
|
01a5b523da | ||
|
|
13477e17ec | ||
|
|
c32cec4c5b | ||
|
|
e08df3f9ce | ||
|
|
f958130b92 | ||
|
|
9a19dd254e | ||
|
|
4869ae4baa | ||
|
|
1fd41eaa55 | ||
|
|
25a7260d7f | ||
|
|
478934ddd1 | ||
|
|
3d7234de19 | ||
|
|
fed1fb2839 | ||
|
|
08ba001b45 | ||
|
|
ce5aaf0b48 | ||
|
|
ee6954a4b9 | ||
|
|
6b6e845a18 | ||
|
|
c01b94518f | ||
|
|
9cf5f74e5e | ||
|
|
4fda0414a0 | ||
|
|
effa4af3a1 | ||
|
|
80b7e49eed | ||
|
|
63748c3841 | ||
|
|
1c68b66047 | ||
|
|
6c9dce70e7 | ||
|
|
5515aa43d6 | ||
|
|
47c461e504 | ||
|
|
65174ceaae | ||
|
|
2500cfb3c7 | ||
|
|
7e673271ee | ||
|
|
e165cb156c | ||
|
|
82bfb0cc55 | ||
|
|
b0fe0663f1 | ||
|
|
1bd5193786 | ||
|
|
64c3fb985a | ||
|
|
1536c94319 | ||
|
|
e7efd89d7a | ||
|
|
c486167b01 | ||
|
|
e5201f3363 | ||
|
|
ff4927dbb7 | ||
|
|
5842562ce1 | ||
|
|
5f545fd3c5 | ||
|
|
aab2f20280 | ||
|
|
76ac68ce51 | ||
|
|
f034e3680c | ||
|
|
e5c57269bb | ||
|
|
ab5167717d | ||
|
|
176abf74a1 | ||
|
|
da4f13d0d7 | ||
|
|
7ddec7609a | ||
|
|
53c3996aef | ||
|
|
bf61f22dd1 | ||
|
|
5e8a36802c | ||
|
|
b34b537a26 | ||
|
|
812fb99e66 | ||
|
|
9cb3111dd5 | ||
|
|
1312555690 | ||
|
|
aa4f8452fa | ||
|
|
e2c1097ec5 | ||
|
|
4b8b13dd68 | ||
|
|
59f795fc17 | ||
|
|
26a935c29c | ||
|
|
848336f8ac | ||
|
|
8e6e9cbb4d | ||
|
|
15cb9bec40 | ||
|
|
38f5fd8249 | ||
|
|
6a04f83280 | ||
|
|
ec4d12a0d5 | ||
|
|
999aba5d77 | ||
|
|
652f29b782 | ||
|
|
0c6aae45c1 | ||
|
|
25b8cff442 | ||
|
|
9f7471178b | ||
|
|
f3f3406d64 | ||
|
|
4eb886d0f3 | ||
|
|
5c6e50e7f9 | ||
|
|
18fd9cdc3d | ||
|
|
fb3115a0f1 | ||
|
|
fdcdf52d9a | ||
|
|
5dcefff28d | ||
|
|
833d4f7b65 | ||
|
|
61bb59d8b3 | ||
|
|
21652de804 | ||
|
|
e677e2acf4 | ||
|
|
2658230331 | ||
|
|
bb0827bb8c |
@@ -658,7 +658,9 @@ do -- AI_A2A_DISPATCHER
|
||||
-- of the race track will randomly selected between 90 (West to East) and 180 (North to South) degrees.
|
||||
-- After a random duration between 10 and 20 minutes, the flight will get a new random orbit location.
|
||||
--
|
||||
-- Note that all parameters except the squadron name are optional. If not specified, default values are taken. Speed and altitude are taken from the
|
||||
-- Note that all parameters except the squadron name are optional. If not specified, default values are taken. Speed and altitude are taken from the CAP command used earlier on, e.g.
|
||||
--
|
||||
-- A2ADispatcher:SetSquadronCap( "Mineralnye", CAPZoneEast, 4000, 10000, 500, 600, 800, 900 )
|
||||
--
|
||||
-- Also note that the center of the race track pattern is chosen randomly within the patrol zone and can be close the the boarder of the zone. Hence, it cannot be guaranteed that the
|
||||
-- whole pattern lies within the patrol zone.
|
||||
@@ -2774,7 +2776,7 @@ do -- AI_A2A_DISPATCHER
|
||||
-- A2ADispatcher = AI_A2A_DISPATCHER:New( Detection )
|
||||
--
|
||||
-- -- Now Setup the default fuel treshold.
|
||||
-- A2ADispatcher:SetSquadronRefuelThreshold( "SquadronName", 0.30 ) -- Go RTB when only 30% of fuel remaining in the tank.
|
||||
-- A2ADispatcher:SetSquadronFuelThreshold( "SquadronName", 0.30 ) -- Go RTB when only 30% of fuel remaining in the tank.
|
||||
--
|
||||
function AI_A2A_DISPATCHER:SetSquadronFuelThreshold( SquadronName, FuelThreshold )
|
||||
|
||||
@@ -2817,7 +2819,7 @@ do -- AI_A2A_DISPATCHER
|
||||
-- A2ADispatcher = AI_A2A_DISPATCHER:New( Detection )
|
||||
--
|
||||
-- -- Now Setup the squadron fuel treshold.
|
||||
-- A2ADispatcher:SetSquadronRefuelThreshold( "SquadronName", 0.30 ) -- Go RTB when only 30% of fuel remaining in the tank.
|
||||
-- A2ADispatcher:SetSquadronFuelThreshold( "SquadronName", 0.30 ) -- Go RTB when only 30% of fuel remaining in the tank.
|
||||
--
|
||||
-- -- Now Setup the squadron tanker.
|
||||
-- A2ADispatcher:SetSquadronTanker( "SquadronName", "Tanker" ) -- The group name of the tanker is "Tanker" in the Mission Editor.
|
||||
@@ -2919,9 +2921,13 @@ do -- AI_A2A_DISPATCHER
|
||||
-- @return #AI_A2A_DISPATCHER.Squadron Squadron The squadron.
|
||||
function AI_A2A_DISPATCHER:GetSquadronFromDefender( Defender )
|
||||
self.Defenders = self.Defenders or {}
|
||||
local DefenderName = Defender:GetName()
|
||||
self:F( { DefenderName = DefenderName } )
|
||||
return self.Defenders[ DefenderName ]
|
||||
if Defender ~= nil then
|
||||
local DefenderName = Defender:GetName()
|
||||
self:F( { DefenderName = DefenderName } )
|
||||
return self.Defenders[ DefenderName ]
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
@@ -431,7 +431,7 @@ do -- AI_A2G_DISPATCHER
|
||||
--
|
||||
-- ### 2.2. The **Defense Reactivity**.
|
||||
--
|
||||
-- There are 5 levels that can be configured to tweak the defense reactivity. As explained above, the threat to a defense coordinate is
|
||||
-- There are three levels that can be configured to tweak the defense reactivity. As explained above, the threat to a defense coordinate is
|
||||
-- also determined by the distance of the enemy ground target to the defense coordinate.
|
||||
-- If you want to have a **low** defense reactivity, that is, the probability that an A2G defense will engage to the enemy ground target, then
|
||||
-- use the @{#AI_A2G_DISPATCHER.SetDefenseReactivityLow}() method. For medium and high reactivity, use the methods
|
||||
@@ -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" ) )
|
||||
|
||||
@@ -595,19 +595,24 @@ function AI_AIR:onafterRTB( AIGroup, From, Event, To )
|
||||
--- Calculate the target route point.
|
||||
|
||||
local FromCoord = AIGroup:GetCoordinate()
|
||||
local ToTargetCoord = self.HomeAirbase:GetCoordinate()
|
||||
|
||||
if not self.RTBMinSpeed and not self.RTBMaxSpeed then
|
||||
local ToTargetCoord = self.HomeAirbase:GetCoordinate() -- coordinate is on land height(!)
|
||||
local ToTargetVec3 = ToTargetCoord:GetVec3()
|
||||
ToTargetVec3.y = ToTargetCoord:GetLandHeight()+1000 -- let's set this 1000m/3000 feet above ground
|
||||
local ToTargetCoord2 = COORDINATE:NewFromVec3( ToTargetVec3 )
|
||||
|
||||
if not self.RTBMinSpeed or not self.RTBMaxSpeed then
|
||||
local RTBSpeedMax = AIGroup:GetSpeedMax()
|
||||
self:SetRTBSpeed( RTBSpeedMax * 0.25, RTBSpeedMax * 0.25 )
|
||||
self:SetRTBSpeed( RTBSpeedMax * 0.5, RTBSpeedMax * 0.6 )
|
||||
end
|
||||
|
||||
local RTBSpeed = math.random( self.RTBMinSpeed, self.RTBMaxSpeed )
|
||||
local ToAirbaseAngle = FromCoord:GetAngleDegrees( FromCoord:GetDirectionVec3( ToTargetCoord ) )
|
||||
--local ToAirbaseAngle = FromCoord:GetAngleDegrees( FromCoord:GetDirectionVec3( ToTargetCoord2 ) )
|
||||
|
||||
local Distance = FromCoord:Get2DDistance( ToTargetCoord )
|
||||
local Distance = FromCoord:Get2DDistance( ToTargetCoord2 )
|
||||
|
||||
local ToAirbaseCoord = FromCoord:Translate( 5000, ToAirbaseAngle )
|
||||
--local ToAirbaseCoord = FromCoord:Translate( 5000, ToAirbaseAngle )
|
||||
local ToAirbaseCoord = ToTargetCoord2
|
||||
|
||||
if Distance < 5000 then
|
||||
self:I( "RTB and near the airbase!" )
|
||||
self:Home()
|
||||
|
||||
@@ -418,6 +418,7 @@ end
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
-- @param Core.Set#SET_UNIT AttackSetUnit Unit set to be attacked.
|
||||
function AI_AIR_ENGAGE:onafterEngageRoute( DefenderGroup, From, Event, To, AttackSetUnit )
|
||||
self:I( { DefenderGroup, From, Event, To, AttackSetUnit } )
|
||||
|
||||
@@ -425,7 +426,7 @@ function AI_AIR_ENGAGE:onafterEngageRoute( DefenderGroup, From, Event, To, Attac
|
||||
|
||||
self.AttackSetUnit = AttackSetUnit -- Kept in memory in case of resume from refuel in air!
|
||||
|
||||
local AttackCount = AttackSetUnit:Count()
|
||||
local AttackCount = AttackSetUnit:CountAlive()
|
||||
|
||||
if AttackCount > 0 then
|
||||
|
||||
@@ -510,6 +511,7 @@ end
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
-- @param Core.Set#SET_UNIT AttackSetUnit Set of units to be attacked.
|
||||
function AI_AIR_ENGAGE:onafterEngage( DefenderGroup, From, Event, To, AttackSetUnit )
|
||||
self:F( { DefenderGroup, From, Event, To, AttackSetUnit} )
|
||||
|
||||
@@ -517,8 +519,8 @@ function AI_AIR_ENGAGE:onafterEngage( DefenderGroup, From, Event, To, AttackSetU
|
||||
|
||||
self.AttackSetUnit = AttackSetUnit -- Kept in memory in case of resume from refuel in air!
|
||||
|
||||
local AttackCount = AttackSetUnit:Count()
|
||||
self:I({AttackCount = AttackCount})
|
||||
local AttackCount = AttackSetUnit:CountAlive()
|
||||
self:T({AttackCount = AttackCount})
|
||||
|
||||
if AttackCount > 0 then
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
--- **AI** -- (R2.4) - Models the intelligent transportation of infantry and other cargo.
|
||||
--- **AI** - Models the intelligent transportation of infantry and other cargo.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
@@ -35,10 +35,9 @@ AI_CARGO = {
|
||||
|
||||
--- Creates a new AI_CARGO object.
|
||||
-- @param #AI_CARGO self
|
||||
-- @param Wrapper.Group#GROUP Carrier
|
||||
-- @param Core.Set#SET_CARGO CargoSet
|
||||
-- @param #number CombatRadius
|
||||
-- @return #AI_CARGO
|
||||
-- @param Wrapper.Group#GROUP Carrier Cargo carrier group.
|
||||
-- @param Core.Set#SET_CARGO CargoSet Set of cargo(s) to transport.
|
||||
-- @return #AI_CARGO self
|
||||
function AI_CARGO:New( Carrier, CargoSet )
|
||||
|
||||
local self = BASE:Inherit( self, FSM_CONTROLLABLE:New( Carrier ) ) -- #AI_CARGO
|
||||
@@ -52,7 +51,8 @@ function AI_CARGO:New( Carrier, CargoSet )
|
||||
self:AddTransition( "Loaded", "Deploy", "*" )
|
||||
|
||||
self:AddTransition( "*", "Load", "Boarding" )
|
||||
self:AddTransition( { "Boarding", "Loaded" }, "Board", "Boarding" )
|
||||
self:AddTransition( "Boarding", "Board", "Boarding" )
|
||||
self:AddTransition( "Loaded", "Board", "Loaded" )
|
||||
self:AddTransition( "Boarding", "Loaded", "Boarding" )
|
||||
self:AddTransition( "Boarding", "PickedUp", "Loaded" )
|
||||
|
||||
@@ -393,7 +393,7 @@ end
|
||||
function AI_CARGO:onafterBoard( Carrier, From, Event, To, Cargo, CarrierUnit, PickupZone )
|
||||
self:F( { Carrier, From, Event, To, Cargo, CarrierUnit:GetName() } )
|
||||
|
||||
if Carrier and Carrier:IsAlive() then
|
||||
if Carrier and Carrier:IsAlive() and From == "Boarding" then
|
||||
self:F({ IsLoaded = Cargo:IsLoaded(), Cargo:GetName(), Carrier:GetName() } )
|
||||
if not Cargo:IsLoaded() and not Cargo:IsDestroyed() then
|
||||
self:__Board( -10, Cargo, CarrierUnit, PickupZone )
|
||||
@@ -509,7 +509,7 @@ end
|
||||
function AI_CARGO:onafterUnboard( Carrier, From, Event, To, Cargo, CarrierUnit, DeployZone, Defend )
|
||||
self:F( { Carrier, From, Event, To, Cargo:GetName(), DeployZone = DeployZone, Defend = Defend } )
|
||||
|
||||
if Carrier and Carrier:IsAlive() then
|
||||
if Carrier and Carrier:IsAlive() and From == "Unboarding" then
|
||||
if not Cargo:IsUnLoaded() then
|
||||
self:__Unboard( 10, Cargo, CarrierUnit, DeployZone, Defend )
|
||||
return
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
--- **AI** -- (R2.4) - Models the intelligent transportation of infantry and other cargo.
|
||||
--- **AI** - Models the intelligent transportation of cargo using ground vehicles.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
@@ -157,6 +157,45 @@ function AI_CARGO_APC:SetCarrier( CargoCarrier )
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set whether or not the carrier will use roads to *pickup* and *deploy* the cargo.
|
||||
-- @param #AI_CARGO_APC self
|
||||
-- @param #boolean Offroad If true, carrier will not use roads. If `nil` or `false` the carrier will use roads when available.
|
||||
-- @param #number Formation Offroad formation used. Default is `ENUMS.Formation.Vehicle.Offroad`.
|
||||
-- @return #AI_CARGO_APC self
|
||||
function AI_CARGO_APC:SetOffRoad(Offroad, Formation)
|
||||
|
||||
self:SetPickupOffRoad(Offroad, Formation)
|
||||
self:SetDeployOffRoad(Offroad, Formation)
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set whether the carrier will *not* use roads to *pickup* the cargo.
|
||||
-- @param #AI_CARGO_APC self
|
||||
-- @param #boolean Offroad If true, carrier will not use roads.
|
||||
-- @param #number Formation Offroad formation used. Default is `ENUMS.Formation.Vehicle.Offroad`.
|
||||
-- @return #AI_CARGO_APC self
|
||||
function AI_CARGO_APC:SetPickupOffRoad(Offroad, Formation)
|
||||
|
||||
self.pickupOffroad=Offroad
|
||||
self.pickupFormation=Formation or ENUMS.Formation.Vehicle.OffRoad
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set whether the carrier will *not* use roads to *deploy* the cargo.
|
||||
-- @param #AI_CARGO_APC self
|
||||
-- @param #boolean Offroad If true, carrier will not use roads.
|
||||
-- @param #number Formation Offroad formation used. Default is `ENUMS.Formation.Vehicle.Offroad`.
|
||||
-- @return #AI_CARGO_APC self
|
||||
function AI_CARGO_APC:SetDeployOffRoad(Offroad, Formation)
|
||||
|
||||
self.deployOffroad=Offroad
|
||||
self.deployFormation=Formation or ENUMS.Formation.Vehicle.OffRoad
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Find a free Carrier within a radius.
|
||||
-- @param #AI_CARGO_APC self
|
||||
@@ -350,10 +389,13 @@ function AI_CARGO_APC:onafterFollow( APC, From, Event, To )
|
||||
|
||||
end
|
||||
|
||||
|
||||
--- @param #AI_CARGO_APC
|
||||
-- @param Wrapper.Group#GROUP APC
|
||||
function AI_CARGO_APC._Pickup( APC, self, Coordinate, Speed, PickupZone )
|
||||
--- Pickup task function. Triggers Load event.
|
||||
-- @param Wrapper.Group#GROUP APC The cargo carrier group.
|
||||
-- @param #AI_CARGO_APC sel `AI_CARGO_APC` class.
|
||||
-- @param Core.Point#COORDINATE Coordinate. The coordinate (not used).
|
||||
-- @param #number Speed Speed (not used).
|
||||
-- @param Core.Zone#ZONE PickupZone Pickup zone.
|
||||
function AI_CARGO_APC._Pickup(APC, self, Coordinate, Speed, PickupZone)
|
||||
|
||||
APC:F( { "AI_CARGO_APC._Pickup:", APC:GetName() } )
|
||||
|
||||
@@ -362,8 +404,12 @@ function AI_CARGO_APC._Pickup( APC, self, Coordinate, Speed, PickupZone )
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function AI_CARGO_APC._Deploy( APC, self, Coordinate, DeployZone )
|
||||
--- Deploy task function. Triggers Unload event.
|
||||
-- @param Wrapper.Group#GROUP APC The cargo carrier group.
|
||||
-- @param #AI_CARGO_APC self `AI_CARGO_APC` class.
|
||||
-- @param Core.Point#COORDINATE Coordinate. The coordinate (not used).
|
||||
-- @param Core.Zone#ZONE DeployZone Deploy zone.
|
||||
function AI_CARGO_APC._Deploy(APC, self, Coordinate, DeployZone)
|
||||
|
||||
APC:F( { "AI_CARGO_APC._Deploy:", APC } )
|
||||
|
||||
@@ -392,12 +438,20 @@ function AI_CARGO_APC:onafterPickup( APC, From, Event, To, Coordinate, Speed, He
|
||||
self.RoutePickup = true
|
||||
|
||||
local _speed=Speed or APC:GetSpeedMax()*0.5
|
||||
|
||||
-- Route on road.
|
||||
local Waypoints = {}
|
||||
|
||||
if self.pickupOffroad then
|
||||
Waypoints[1]=APC:GetCoordinate():WaypointGround(Speed, self.pickupFormation)
|
||||
Waypoints[2]=Coordinate:WaypointGround(_speed, self.pickupFormation, DCSTasks)
|
||||
else
|
||||
Waypoints=APC:TaskGroundOnRoad(Coordinate, _speed, ENUMS.Formation.Vehicle.OffRoad, true)
|
||||
end
|
||||
|
||||
local Waypoints = APC:TaskGroundOnRoad( Coordinate, _speed, "Line abreast", true )
|
||||
|
||||
local TaskFunction = APC:TaskFunction( "AI_CARGO_APC._Pickup", self, Coordinate, Speed, PickupZone )
|
||||
|
||||
self:F({Waypoints = Waypoints})
|
||||
|
||||
local Waypoint = Waypoints[#Waypoints]
|
||||
APC:SetTaskWaypoint( Waypoint, TaskFunction ) -- Set for the given Route at Waypoint 2 the TaskRouteToZone.
|
||||
|
||||
@@ -428,18 +482,34 @@ function AI_CARGO_APC:onafterDeploy( APC, From, Event, To, Coordinate, Speed, He
|
||||
|
||||
self.RouteDeploy = true
|
||||
|
||||
local _speed=Speed or APC:GetSpeedMax()*0.5
|
||||
|
||||
local Waypoints = APC:TaskGroundOnRoad( Coordinate, _speed, "Line abreast", true )
|
||||
-- Set speed in km/h.
|
||||
local speedmax=APC:GetSpeedMax()
|
||||
local _speed=Speed or speedmax*0.5
|
||||
_speed=math.min(_speed, speedmax)
|
||||
|
||||
-- Route on road.
|
||||
local Waypoints = {}
|
||||
|
||||
if self.deployOffroad then
|
||||
Waypoints[1]=APC:GetCoordinate():WaypointGround(Speed, self.deployFormation)
|
||||
Waypoints[2]=Coordinate:WaypointGround(_speed, self.deployFormation, DCSTasks)
|
||||
else
|
||||
Waypoints=APC:TaskGroundOnRoad(Coordinate, _speed, ENUMS.Formation.Vehicle.OffRoad, true)
|
||||
end
|
||||
|
||||
-- Task function
|
||||
local TaskFunction = APC:TaskFunction( "AI_CARGO_APC._Deploy", self, Coordinate, DeployZone )
|
||||
|
||||
self:F({Waypoints = Waypoints})
|
||||
-- Last waypoint
|
||||
local Waypoint = Waypoints[#Waypoints]
|
||||
APC:SetTaskWaypoint( Waypoint, TaskFunction ) -- Set for the given Route at Waypoint 2 the TaskRouteToZone.
|
||||
|
||||
-- Set task function
|
||||
APC:SetTaskWaypoint(Waypoint, TaskFunction) -- Set for the given Route at Waypoint 2 the TaskRouteToZone.
|
||||
|
||||
-- Route group
|
||||
APC:Route( Waypoints, 1 ) -- Move after a random seconds to the Route. See the Route method for details.
|
||||
|
||||
-- Call parent function.
|
||||
self:GetParent( self, AI_CARGO_APC ).onafterDeploy( self, APC, From, Event, To, Coordinate, Speed, Height, DeployZone )
|
||||
|
||||
end
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
--- **AI** -- (R2.4) - Models the intelligent transportation of infantry (cargo).
|
||||
--- **AI** - Models the intelligent transportation of cargo using airplanes.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
@@ -408,9 +408,6 @@ function AI_CARGO_AIRPLANE:onafterUnload( Airplane, From, Event, To, DeployZone
|
||||
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
--- Route the airplane from one airport or it's current position to another airbase.
|
||||
-- @param #AI_CARGO_AIRPLANE self
|
||||
-- @param Wrapper.Group#GROUP Airplane Airplane group to be routed.
|
||||
@@ -438,14 +435,10 @@ function AI_CARGO_AIRPLANE:Route( Airplane, Airbase, Speed, Height, Uncontrolled
|
||||
|
||||
-- To point.
|
||||
local AirbasePointVec2 = Airbase:GetPointVec2()
|
||||
local ToWaypoint = AirbasePointVec2:WaypointAir(
|
||||
POINT_VEC3.RoutePointAltType.BARO,
|
||||
"Land",
|
||||
"Landing",
|
||||
Speed or Airplane:GetSpeedMax()*0.8
|
||||
)
|
||||
ToWaypoint["airdromeId"] = Airbase:GetID()
|
||||
ToWaypoint["speed_locked"] = true
|
||||
local ToWaypoint = AirbasePointVec2:WaypointAir(POINT_VEC3.RoutePointAltType.BARO, "Land", "Landing", Speed or Airplane:GetSpeedMax()*0.8, true, Airbase)
|
||||
|
||||
--ToWaypoint["airdromeId"] = Airbase:GetID()
|
||||
--ToWaypoint["speed_locked"] = true
|
||||
|
||||
|
||||
-- If self.Airbase~=nil then group is currently at an airbase, where it should be respawned.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
--- **AI** -- (R2.4) - Models the intelligent transportation of infantry and other cargo.
|
||||
--- **AI** - Models the intelligent transportation of infantry and other cargo.
|
||||
--
|
||||
-- ## Features:
|
||||
--
|
||||
@@ -1104,7 +1104,7 @@ function AI_CARGO_DISPATCHER:onafterMonitor()
|
||||
-- The Pickup sequence ...
|
||||
-- Check if this Carrier need to go and Pickup something...
|
||||
-- So, if the cargo bay is not full yet with cargo to be loaded ...
|
||||
self:I( { Carrier = CarrierGroupName, IsRelocating = AI_Cargo:IsRelocating(), IsTransporting = AI_Cargo:IsTransporting() } )
|
||||
self:T( { Carrier = CarrierGroupName, IsRelocating = AI_Cargo:IsRelocating(), IsTransporting = AI_Cargo:IsTransporting() } )
|
||||
if AI_Cargo:IsRelocating() == false and AI_Cargo:IsTransporting() == false then
|
||||
-- ok, so there is a free Carrier
|
||||
-- now find the first cargo that is Unloaded
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
--- **AI** -- (2.4) - Models the intelligent transportation of infantry and other cargo using APCs.
|
||||
--- **AI** - Models the intelligent transportation of infantry and other cargo using APCs.
|
||||
--
|
||||
-- ## Features:
|
||||
--
|
||||
@@ -181,25 +181,36 @@ function AI_CARGO_DISPATCHER_APC:New( APCSet, CargoSet, PickupZoneSet, DeployZon
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- AI cargo
|
||||
-- @param #AI_CARGO_DISPATCHER_APC self
|
||||
-- @param Wrapper.Group#GROUP APC The APC carrier.
|
||||
-- @param Core.Set#SET_CARGO CargoSet Cargo set.
|
||||
-- @return AI.AI_Cargo_APC#AI_CARGO_DISPATCHER_APC AI cargo APC object.
|
||||
function AI_CARGO_DISPATCHER_APC:AICargo( APC, CargoSet )
|
||||
|
||||
return AI_CARGO_APC:New( APC, CargoSet, self.CombatRadius )
|
||||
local aicargoapc=AI_CARGO_APC:New(APC, CargoSet, self.CombatRadius)
|
||||
|
||||
aicargoapc:SetDeployOffRoad(self.deployOffroad, self.deployFormation)
|
||||
aicargoapc:SetPickupOffRoad(self.pickupOffroad, self.pickupFormation)
|
||||
|
||||
return aicargoapc
|
||||
end
|
||||
|
||||
--- Enable/Disable unboarding of cargo (infantry) when enemies are nearby (to help defend the carrier).
|
||||
-- This is only valid for APCs and trucks etc, thus ground vehicles.
|
||||
-- @param #AI_CARGO_DISPATCHER_APC self
|
||||
-- @param #number CombatRadius Provide the combat radius to defend the carrier by unboarding the cargo when enemies are nearby.
|
||||
-- When the combat radius is 0, no defense will happen of the carrier.
|
||||
-- When the combat radius is 0 (default), no defense will happen of the carrier.
|
||||
-- When the combat radius is not provided, no defense will happen!
|
||||
-- @return #AI_CARGO_DISPATCHER_APC
|
||||
-- @usage
|
||||
--
|
||||
-- -- Disembark the infantry when the carrier is under attack.
|
||||
-- AICargoDispatcher:SetCombatRadius( true )
|
||||
-- AICargoDispatcher:SetCombatRadius( 500 )
|
||||
--
|
||||
-- -- Keep the cargo in the carrier when the carrier is under attack.
|
||||
-- AICargoDispatcher:SetCombatRadius( false )
|
||||
-- AICargoDispatcher:SetCombatRadius( 0 )
|
||||
function AI_CARGO_DISPATCHER_APC:SetCombatRadius( CombatRadius )
|
||||
|
||||
self.CombatRadius = CombatRadius or 0
|
||||
@@ -207,3 +218,41 @@ function AI_CARGO_DISPATCHER_APC:SetCombatRadius( CombatRadius )
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set whether the carrier will *not* use roads to *pickup* and *deploy* the cargo.
|
||||
-- @param #AI_CARGO_DISPATCHER_APC self
|
||||
-- @param #boolean Offroad If true, carrier will not use roads.
|
||||
-- @param #number Formation Offroad formation used. Default is `ENUMS.Formation.Vehicle.Offroad`.
|
||||
-- @return #AI_CARGO_DISPATCHER_APC self
|
||||
function AI_CARGO_DISPATCHER_APC:SetOffRoad(Offroad, Formation)
|
||||
|
||||
self:SetPickupOffRoad(Offroad, Formation)
|
||||
self:SetDeployOffRoad(Offroad, Formation)
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set whether the carrier will *not* use roads to *pickup* the cargo.
|
||||
-- @param #AI_CARGO_DISPATCHER_APC self
|
||||
-- @param #boolean Offroad If true, carrier will not use roads.
|
||||
-- @param #number Formation Offroad formation used. Default is `ENUMS.Formation.Vehicle.Offroad`.
|
||||
-- @return #AI_CARGO_DISPATCHER_APC self
|
||||
function AI_CARGO_DISPATCHER_APC:SetPickupOffRoad(Offroad, Formation)
|
||||
|
||||
self.pickupOffroad=Offroad
|
||||
self.pickupFormation=Formation or ENUMS.Formation.Vehicle.OffRoad
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set whether the carrier will *not* use roads to *deploy* the cargo.
|
||||
-- @param #AI_CARGO_DISPATCHER_APC self
|
||||
-- @param #boolean Offroad If true, carrier will not use roads.
|
||||
-- @param #number Formation Offroad formation used. Default is `ENUMS.Formation.Vehicle.Offroad`.
|
||||
-- @return #AI_CARGO_DISPATCHER_APC self
|
||||
function AI_CARGO_DISPATCHER_APC:SetDeployOffRoad(Offroad, Formation)
|
||||
|
||||
self.deployOffroad=Offroad
|
||||
self.deployFormation=Formation or ENUMS.Formation.Vehicle.OffRoad
|
||||
|
||||
return self
|
||||
end
|
||||
193
Moose Development/Moose/AI/AI_Cargo_Dispatcher_Ship.lua
Normal file
193
Moose Development/Moose/AI/AI_Cargo_Dispatcher_Ship.lua
Normal file
@@ -0,0 +1,193 @@
|
||||
--- **AI** -- (2.5.1) - Models the intelligent transportation of infantry and other cargo using Ships
|
||||
--
|
||||
-- ## Features:
|
||||
--
|
||||
-- * Transport cargo to various deploy zones using naval vehicles.
|
||||
-- * Various @{Cargo.Cargo#CARGO} types can be transported, including infantry, vehicles, and crates.
|
||||
-- * Define a deploy zone of various types to determine the destination of the cargo.
|
||||
-- * Ships will follow shipping lanes as defined in the Mission Editor.
|
||||
-- * Multiple ships can transport multiple cargo as a single group.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ## Test Missions:
|
||||
--
|
||||
-- NEED TO DO
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **acrojason** (derived from AI_Cargo_Dispatcher_APC by FlightControl)
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @module AI.AI_Cargo_Dispatcher_Ship
|
||||
-- @image AI_Cargo_Dispatching_For_Ship.JPG
|
||||
|
||||
--- @type AI_CARGO_DISPATCHER_SHIP
|
||||
-- @extends AI.AI_Cargo_Dispatcher#AI_CARGO_DISPATCHER
|
||||
|
||||
|
||||
--- A dynamic cargo transportation capability for AI groups.
|
||||
--
|
||||
-- Naval vessels can be mobilized to semi-intelligently transport cargo within the simulation.
|
||||
--
|
||||
-- The AI_CARGO_DISPATCHER_SHIP module is derived from the AI_CARGO_DISPATCHER module.
|
||||
--
|
||||
-- ## Note! In order to fully understand the mechanisms of the AI_CARGO_DISPATCHER_SHIP class, it is recommended that you first consult and READ the documentation of the @{AI.AI_Cargo_Dispatcher} module!!!
|
||||
--
|
||||
-- This will be particularly helpful in order to determine how to **Tailor the different cargo handling events**.
|
||||
--
|
||||
-- The AI_CARGO_DISPATCHER_SHIP class uses the @{Cargo.Cargo} capabilities within the MOOSE framwork.
|
||||
-- Also ensure that you fully understand how to declare and setup Cargo objects within the MOOSE framework before using this class.
|
||||
-- CARGO derived objects must generally be declared within the mission to make the AI_CARGO_DISPATCHER_SHIP object recognize the cargo.
|
||||
--
|
||||
--
|
||||
-- # 1) AI_CARGO_DISPATCHER_SHIP constructor.
|
||||
--
|
||||
-- * @{AI_CARGO_DISPATCHER_SHIP.New}(): Creates a new AI_CARGO_DISPATCHER_SHIP object.
|
||||
--
|
||||
-- ---
|
||||
--
|
||||
-- # 2) AI_CARGO_DISPATCHER_SHIP is a Finite State Machine.
|
||||
--
|
||||
-- This section must be read as follows... Each of the rows indicate a state transition, triggered through an event, and with an ending state of the event was executed.
|
||||
-- The first column is the **From** state, the second column the **Event**, and the third column the **To** state.
|
||||
--
|
||||
-- So, each of the rows have the following structure.
|
||||
--
|
||||
-- * **From** => **Event** => **To**
|
||||
--
|
||||
-- Important to know is that an event can only be executed if the **current state** is the **From** state.
|
||||
-- This, when an **Event** that is being triggered has a **From** state that is equal to the **Current** state of the state machine, the event will be executed,
|
||||
-- and the resulting state will be the **To** state.
|
||||
--
|
||||
-- These are the different possible state transitions of this state machine implementation:
|
||||
--
|
||||
-- * Idle => Start => Monitoring
|
||||
-- * Monitoring => Monitor => Monitoring
|
||||
-- * Monitoring => Stop => Idle
|
||||
--
|
||||
-- * Monitoring => Pickup => Monitoring
|
||||
-- * Monitoring => Load => Monitoring
|
||||
-- * Monitoring => Loading => Monitoring
|
||||
-- * Monitoring => Loaded => Monitoring
|
||||
-- * Monitoring => PickedUp => Monitoring
|
||||
-- * Monitoring => Deploy => Monitoring
|
||||
-- * Monitoring => Unload => Monitoring
|
||||
-- * Monitoring => Unloaded => Monitoring
|
||||
-- * Monitoring => Deployed => Monitoring
|
||||
-- * Monitoring => Home => Monitoring
|
||||
--
|
||||
--
|
||||
-- ## 2.1) AI_CARGO_DISPATCHER States.
|
||||
--
|
||||
-- * **Monitoring**: The process is dispatching.
|
||||
-- * **Idle**: The process is idle.
|
||||
--
|
||||
-- ## 2.2) AI_CARGO_DISPATCHER Events.
|
||||
--
|
||||
-- * **Start**: Start the transport process.
|
||||
-- * **Stop**: Stop the transport process.
|
||||
-- * **Monitor**: Monitor and take action.
|
||||
--
|
||||
-- * **Pickup**: Pickup cargo.
|
||||
-- * **Load**: Load the cargo.
|
||||
-- * **Loading**: The dispatcher is coordinating the loading of a cargo.
|
||||
-- * **Loaded**: Flag that the cargo is loaded.
|
||||
-- * **PickedUp**: The dispatcher has loaded all requested cargo into the CarrierGroup.
|
||||
-- * **Deploy**: Deploy cargo to a location.
|
||||
-- * **Unload**: Unload the cargo.
|
||||
-- * **Unloaded**: Flag that the cargo is unloaded.
|
||||
-- * **Deployed**: All cargo is unloaded from the carriers in the group.
|
||||
-- * **Home**: A Carrier is going home.
|
||||
--
|
||||
-- ## 2.3) Enhance your mission scripts with **Tailored** Event Handling!
|
||||
--
|
||||
-- Within your mission, you can capture these events when triggered, and tailor the events with your own code!
|
||||
-- Check out the @{AI.AI_Cargo_Dispatcher#AI_CARGO_DISPATCHER} class at chapter 3 for details on the different event handlers that are available and how to use them.
|
||||
--
|
||||
-- **There are a lot of templates available that allows you to quickly setup an event handler for a specific event type!**
|
||||
--
|
||||
-- ---
|
||||
--
|
||||
-- # 3) Set the pickup parameters.
|
||||
--
|
||||
-- Several parameters can be set to pickup cargo:
|
||||
--
|
||||
-- * @{#AI_CARGO_DISPATCHER_SHIP.SetPickupRadius}(): Sets or randomizes the pickup location for the Ship around the cargo coordinate in a radius defined an outer and optional inner radius.
|
||||
-- * @{#AI_CARGO_DISPATCHER_SHIP.SetPickupSpeed}(): Set the speed or randomizes the speed in km/h to pickup the cargo.
|
||||
--
|
||||
-- # 4) Set the deploy parameters.
|
||||
--
|
||||
-- Several parameters can be set to deploy cargo:
|
||||
--
|
||||
-- * @{#AI_CARGO_DISPATCHER_SHIP.SetDeployRadius}(): Sets or randomizes the deploy location for the Ship around the cargo coordinate in a radius defined an outer and an optional inner radius.
|
||||
-- * @{#AI_CARGO_DISPATCHER_SHIP.SetDeploySpeed}(): Set the speed or randomizes the speed in km/h to deploy the cargo.
|
||||
--
|
||||
-- # 5) Set the home zone when there isn't any more cargo to pickup.
|
||||
--
|
||||
-- A home zone can be specified to where the Ship will move when there isn't any cargo left for pickup.
|
||||
-- Use @{#AI_CARGO_DISPATCHER_SHIP.SetHomeZone}() to specify the home zone.
|
||||
--
|
||||
-- If no home zone is specified, the Ship will wait near the deploy zone for a new pickup command.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @field #AI_CARGO_DISPATCHER_SHIP
|
||||
AI_CARGO_DISPATCHER_SHIP = {
|
||||
ClassName = "AI_CARGO_DISPATCHER_SHIP"
|
||||
}
|
||||
|
||||
--- Creates a new AI_CARGO_DISPATCHER_SHIP object.
|
||||
-- @param #AI_CARGO_DISPATCHER_SHIP self
|
||||
-- @param Core.Set#SET_GROUP ShipSet The set of @{Wrapper.Group#GROUP} objects of Ships that will transport the cargo
|
||||
-- @param Core.Set#SET_CARGO CargoSet The set of @{Cargo.Cargo#CARGO} objects, which can be CARGO_GROUP, CARGO_CRATE, or CARGO_SLINGLOAD objects.
|
||||
-- @param Core.Set#SET_ZONE PickupZoneSet The set of pickup zones which are used to determine from where the cargo can be picked up by the Ship.
|
||||
-- @param Core.Set#SET_ZONE DeployZoneSet The set of deploy zones which determine where the cargo will be deployed by the Ship.
|
||||
-- @param #table ShippingLane Table containing list of Shipping Lanes to be used
|
||||
-- @return #AI_CARGO_DISPATCHER_SHIP
|
||||
-- @usage
|
||||
--
|
||||
-- -- An AI dispatcher object for a naval group, moving cargo from pickup zones to deploy zones via a predetermined Shipping Lane
|
||||
--
|
||||
-- local SetCargoInfantry = SET_CARGO:New():FilterTypes( "Infantry" ):FilterStart()
|
||||
-- local SetShip = SET_GROUP:New():FilterPrefixes( "Ship" ):FilterStart()
|
||||
-- local SetPickupZones = SET_ZONE:New():FilterPrefixes( "Pickup" ):FilterStart()
|
||||
-- local SetDeployZones = SET_ZONE:New():FilterPrefixes( "Deploy" ):FilterStart()
|
||||
-- NEED MORE THOUGHT - ShippingLane is part of Warehouse.......
|
||||
-- local ShippingLane = GROUP:New():FilterPrefixes( "ShippingLane" ):FilterStart()
|
||||
--
|
||||
-- AICargoDispatcherShip = AI_CARGO_DISPATCHER_SHIP:New( SetShip, SetCargoInfantry, SetPickupZones, SetDeployZones, ShippingLane )
|
||||
-- AICargoDispatcherShip:Start()
|
||||
--
|
||||
function AI_CARGO_DISPATCHER_SHIP:New( ShipSet, CargoSet, PickupZoneSet, DeployZoneSet, ShippingLane )
|
||||
|
||||
local self = BASE:Inherit( self, AI_CARGO_DISPATCHER:New( ShipSet, CargoSet, PickupZoneSet, DeployZoneSet ) )
|
||||
|
||||
self:SetPickupSpeed( 60, 10 )
|
||||
self:SetDeploySpeed( 60, 10 )
|
||||
|
||||
self:SetPickupRadius( 500, 6000 )
|
||||
self:SetDeployRadius( 500, 6000 )
|
||||
|
||||
self:SetPickupHeight( 0, 0 )
|
||||
self:SetDeployHeight( 0, 0 )
|
||||
|
||||
self:SetShippingLane( ShippingLane )
|
||||
|
||||
self:SetMonitorTimeInterval( 600 )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
function AI_CARGO_DISPATCHER_SHIP:SetShippingLane( ShippingLane )
|
||||
self.ShippingLane = ShippingLane
|
||||
|
||||
return self
|
||||
|
||||
end
|
||||
|
||||
function AI_CARGO_DISPATCHER_SHIP:AICargo( Ship, CargoSet )
|
||||
|
||||
return AI_CARGO_SHIP:New( Ship, CargoSet, 0, self.ShippingLane )
|
||||
end
|
||||
@@ -1,4 +1,4 @@
|
||||
--- **AI** -- (R2.4) - Models the intelligent transportation of infantry (cargo).
|
||||
--- **AI** - Models the intelligent transportation of cargo using helicopters.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
@@ -67,16 +67,23 @@ function AI_CARGO_HELICOPTER:New( Helicopter, CargoSet )
|
||||
|
||||
self:AddTransition( "Unloaded", "Pickup", "*" )
|
||||
self:AddTransition( "Loaded", "Deploy", "*" )
|
||||
|
||||
--[[
|
||||
self:AddTransition( { "Unloaded", "Loading" }, "Load", "Boarding" )
|
||||
self:AddTransition( "Boarding", "Board", "Boarding" )
|
||||
self:AddTransition( "Boarding", "Loaded", "Boarding" )
|
||||
self:AddTransition( "Boarding", "Loaded", "Loaded" )
|
||||
self:AddTransition( "Boarding", "PickedUp", "Loaded" )
|
||||
self:AddTransition( "Boarding", "Deploy", "Loaded" )
|
||||
self:AddTransition( "Loaded", "Unload", "Unboarding" )
|
||||
self:AddTransition( "Unboarding", "Unboard", "Unboarding" )
|
||||
self:AddTransition( "Unboarding", "Unloaded", "Unboarding" )
|
||||
self:AddTransition( "Unboarding", "Deployed", "Unloaded" )
|
||||
|
||||
self:AddTransition( "Unboarding", "Pickup", "Unloaded" )
|
||||
--]]
|
||||
self:AddTransition( "*", "Loaded", "Loaded" )
|
||||
self:AddTransition( "Unboarding", "Pickup", "Unloaded" )
|
||||
self:AddTransition( "Unloaded", "Unboard", "Unloaded" )
|
||||
self:AddTransition( "Unloaded", "Unloaded", "Unloaded" )
|
||||
self:AddTransition( "*", "PickedUp", "*" )
|
||||
self:AddTransition( "*", "Landed", "*" )
|
||||
self:AddTransition( "*", "Queue", "*" )
|
||||
self:AddTransition( "*", "Orbit" , "*" )
|
||||
@@ -243,7 +250,7 @@ function AI_CARGO_HELICOPTER:onafterLanded( Helicopter, From, Event, To )
|
||||
self:F( { Helicopter:GetName(), Height = Helicopter:GetHeight( true ), Velocity = Helicopter:GetVelocityKMH() } )
|
||||
|
||||
if self.RoutePickup == true then
|
||||
if Helicopter:GetHeight( true ) <= 5 and Helicopter:GetVelocityKMH() < 10 then
|
||||
if Helicopter:GetHeight( true ) <= 5.5 and Helicopter:GetVelocityKMH() < 10 then
|
||||
--self:Load( Helicopter:GetPointVec2() )
|
||||
self:Load( self.PickupZone )
|
||||
self.RoutePickup = false
|
||||
@@ -251,7 +258,7 @@ function AI_CARGO_HELICOPTER:onafterLanded( Helicopter, From, Event, To )
|
||||
end
|
||||
|
||||
if self.RouteDeploy == true then
|
||||
if Helicopter:GetHeight( true ) <= 5 and Helicopter:GetVelocityKMH() < 10 then
|
||||
if Helicopter:GetHeight( true ) <= 5.5 and Helicopter:GetVelocityKMH() < 10 then
|
||||
self:Unload( self.DeployZone )
|
||||
self.RouteDeploy = false
|
||||
end
|
||||
@@ -308,7 +315,11 @@ function AI_CARGO_HELICOPTER:onafterQueue( Helicopter, From, Event, To, Coordina
|
||||
-- true
|
||||
-- )
|
||||
-- Route[#Route+1] = WaypointFrom
|
||||
local CoordinateTo = Coordinate
|
||||
local CoordinateTo = Coordinate
|
||||
|
||||
local landheight = CoordinateTo:GetLandHeight() -- get target height
|
||||
CoordinateTo.y = landheight + 50 -- flight height should be 50m above ground
|
||||
|
||||
local WaypointTo = CoordinateTo:WaypointAir(
|
||||
"RADIO",
|
||||
POINT_VEC3.RoutePointType.TurningPoint,
|
||||
@@ -353,23 +364,11 @@ function AI_CARGO_HELICOPTER:onafterOrbit( Helicopter, From, Event, To, Coordina
|
||||
|
||||
local Route = {}
|
||||
|
||||
-- local CoordinateFrom = Helicopter:GetCoordinate()
|
||||
-- local WaypointFrom = CoordinateFrom:WaypointAir(
|
||||
-- "RADIO",
|
||||
-- POINT_VEC3.RoutePointType.TurningPoint,
|
||||
-- POINT_VEC3.RoutePointAction.TurningPoint,
|
||||
-- Speed,
|
||||
-- true
|
||||
-- )
|
||||
-- Route[#Route+1] = WaypointFrom
|
||||
local CoordinateTo = Coordinate
|
||||
local WaypointTo = CoordinateTo:WaypointAir(
|
||||
"RADIO",
|
||||
POINT_VEC3.RoutePointType.TurningPoint,
|
||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
||||
50,
|
||||
true
|
||||
)
|
||||
local CoordinateTo = Coordinate
|
||||
local landheight = CoordinateTo:GetLandHeight() -- get target height
|
||||
CoordinateTo.y = landheight + 50 -- flight height should be 50m above ground
|
||||
|
||||
local WaypointTo = CoordinateTo:WaypointAir("RADIO", POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, 50, true)
|
||||
Route[#Route+1] = WaypointTo
|
||||
|
||||
local Tasks = {}
|
||||
@@ -379,7 +378,7 @@ function AI_CARGO_HELICOPTER:onafterOrbit( Helicopter, From, Event, To, Coordina
|
||||
Route[#Route+1] = WaypointTo
|
||||
|
||||
-- Now route the helicopter
|
||||
Helicopter:Route( Route, 0 )
|
||||
Helicopter:Route(Route, 0)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -408,7 +407,6 @@ function AI_CARGO_HELICOPTER:onafterDeployed( Helicopter, From, Event, To, Deplo
|
||||
|
||||
self:GetParent( self, AI_CARGO_HELICOPTER ).onafterDeployed( self, Helicopter, From, Event, To, DeployZone )
|
||||
|
||||
|
||||
end
|
||||
|
||||
--- On after Pickup event.
|
||||
@@ -422,7 +420,8 @@ end
|
||||
-- @param #number Height Height in meters to move to the pickup coordinate. This parameter is ignored for APCs.
|
||||
-- @param Core.Zone#ZONE PickupZone (optional) The zone where the cargo will be picked up. The PickupZone can be nil, if there wasn't any PickupZoneSet provided.
|
||||
function AI_CARGO_HELICOPTER:onafterPickup( Helicopter, From, Event, To, Coordinate, Speed, Height, PickupZone )
|
||||
|
||||
self:F({Coordinate, Speed, Height, PickupZone })
|
||||
|
||||
if Helicopter and Helicopter:IsAlive() ~= nil then
|
||||
|
||||
Helicopter:Activate()
|
||||
@@ -436,25 +435,16 @@ function AI_CARGO_HELICOPTER:onafterPickup( Helicopter, From, Event, To, Coordin
|
||||
|
||||
--- Calculate the target route point.
|
||||
local CoordinateFrom = Helicopter:GetCoordinate()
|
||||
local CoordinateTo = Coordinate
|
||||
|
||||
--- Create a route point of type air.
|
||||
local WaypointFrom = CoordinateFrom:WaypointAir(
|
||||
"RADIO",
|
||||
POINT_VEC3.RoutePointType.TurningPoint,
|
||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
||||
_speed,
|
||||
true
|
||||
)
|
||||
local WaypointFrom = CoordinateFrom:WaypointAir("RADIO", POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, _speed, true)
|
||||
|
||||
--- Create a route point of type air.
|
||||
local WaypointTo = CoordinateTo:WaypointAir(
|
||||
"RADIO",
|
||||
POINT_VEC3.RoutePointType.TurningPoint,
|
||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
||||
_speed,
|
||||
true
|
||||
)
|
||||
local CoordinateTo = Coordinate
|
||||
local landheight = CoordinateTo:GetLandHeight() -- get target height
|
||||
CoordinateTo.y = landheight + 50 -- flight height should be 50m above ground
|
||||
|
||||
local WaypointTo = CoordinateTo:WaypointAir("RADIO", POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint,_speed, true)
|
||||
|
||||
Route[#Route+1] = WaypointFrom
|
||||
Route[#Route+1] = WaypointTo
|
||||
@@ -514,25 +504,17 @@ function AI_CARGO_HELICOPTER:onafterDeploy( Helicopter, From, Event, To, Coordin
|
||||
|
||||
--- Create a route point of type air.
|
||||
local CoordinateFrom = Helicopter:GetCoordinate()
|
||||
local WaypointFrom = CoordinateFrom:WaypointAir(
|
||||
"RADIO",
|
||||
POINT_VEC3.RoutePointType.TurningPoint,
|
||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
||||
_speed,
|
||||
true
|
||||
)
|
||||
local WaypointFrom = CoordinateFrom:WaypointAir("RADIO", POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, _speed, true)
|
||||
Route[#Route+1] = WaypointFrom
|
||||
Route[#Route+1] = WaypointFrom
|
||||
|
||||
--- Create a route point of type air.
|
||||
local CoordinateTo = Coordinate
|
||||
local WaypointTo = CoordinateTo:WaypointAir(
|
||||
"RADIO",
|
||||
POINT_VEC3.RoutePointType.TurningPoint,
|
||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
||||
_speed,
|
||||
true
|
||||
)
|
||||
|
||||
local CoordinateTo = Coordinate
|
||||
local landheight = CoordinateTo:GetLandHeight() -- get target height
|
||||
CoordinateTo.y = landheight + 50 -- flight height should be 50m above ground
|
||||
|
||||
local WaypointTo = CoordinateTo:WaypointAir("RADIO", POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, _speed, true)
|
||||
|
||||
Route[#Route+1] = WaypointTo
|
||||
Route[#Route+1] = WaypointTo
|
||||
@@ -542,7 +524,9 @@ function AI_CARGO_HELICOPTER:onafterDeploy( Helicopter, From, Event, To, Coordin
|
||||
|
||||
local Tasks = {}
|
||||
|
||||
-- The _Deploy function does not exist.
|
||||
Tasks[#Tasks+1] = Helicopter:TaskFunction( "AI_CARGO_HELICOPTER._Deploy", self, Coordinate, DeployZone )
|
||||
|
||||
Tasks[#Tasks+1] = Helicopter:TaskOrbitCircle( math.random( 30, 100 ), _speed, CoordinateTo:GetRandomCoordinateInRadius( 800, 500 ) )
|
||||
|
||||
--Tasks[#Tasks+1] = Helicopter:TaskLandAtVec2( CoordinateTo:GetVec2() )
|
||||
@@ -585,24 +569,16 @@ function AI_CARGO_HELICOPTER:onafterHome( Helicopter, From, Event, To, Coordinat
|
||||
|
||||
--- Create a route point of type air.
|
||||
local CoordinateFrom = Helicopter:GetCoordinate()
|
||||
local WaypointFrom = CoordinateFrom:WaypointAir(
|
||||
"RADIO",
|
||||
POINT_VEC3.RoutePointType.TurningPoint,
|
||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
||||
Speed ,
|
||||
true
|
||||
)
|
||||
|
||||
local WaypointFrom = CoordinateFrom:WaypointAir("RADIO", POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, Speed, true)
|
||||
Route[#Route+1] = WaypointFrom
|
||||
|
||||
--- Create a route point of type air.
|
||||
local CoordinateTo = Coordinate
|
||||
local WaypointTo = CoordinateTo:WaypointAir(
|
||||
"RADIO",
|
||||
POINT_VEC3.RoutePointType.TurningPoint,
|
||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
||||
Speed ,
|
||||
true
|
||||
)
|
||||
local CoordinateTo = Coordinate
|
||||
local landheight = CoordinateTo:GetLandHeight() -- get target height
|
||||
CoordinateTo.y = landheight + 50 -- flight height should be 50m above ground
|
||||
|
||||
local WaypointTo = CoordinateTo:WaypointAir("RADIO", POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, Speed, true)
|
||||
|
||||
Route[#Route+1] = WaypointTo
|
||||
|
||||
@@ -617,7 +593,7 @@ function AI_CARGO_HELICOPTER:onafterHome( Helicopter, From, Event, To, Coordinat
|
||||
Route[#Route+1] = WaypointTo
|
||||
|
||||
-- Now route the helicopter
|
||||
Helicopter:Route( Route, 0 )
|
||||
Helicopter:Route(Route, 0)
|
||||
|
||||
end
|
||||
|
||||
|
||||
397
Moose Development/Moose/AI/AI_Cargo_Ship.lua
Normal file
397
Moose Development/Moose/AI/AI_Cargo_Ship.lua
Normal file
@@ -0,0 +1,397 @@
|
||||
--- **AI** -- (R2.5.1) - Models the intelligent transportation of infantry and other cargo.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **acrojason** (derived from AI_Cargo_APC by FlightControl)
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @module AI.AI_Cargo_Ship
|
||||
-- @image AI_Cargo_Dispatching_For_Ship.JPG
|
||||
|
||||
--- @type AI_CARGO_SHIP
|
||||
-- @extends AI.AI_Cargo#AI_CARGO
|
||||
|
||||
--- Brings a dynamic cargo handling capability for an AI naval group.
|
||||
--
|
||||
-- Naval ships can be utilized to transport cargo around the map following naval shipping lanes.
|
||||
-- The AI_CARGO_SHIP class uses the @{Cargo.Cargo} capabilities within the MOOSE framework.
|
||||
-- @{Cargo.Cargo} must be declared within the mission or warehouse to make the AI_CARGO_SHIP recognize the cargo.
|
||||
-- Please consult the @{Cargo.Cargo} module for more information.
|
||||
--
|
||||
-- ## Cargo loading.
|
||||
--
|
||||
-- The module will automatically load cargo when the Ship is within boarding or loading radius.
|
||||
-- The boarding or loading radius is specified when the cargo is created in the simulation and depends on the type of
|
||||
-- cargo and the specified boarding radius.
|
||||
--
|
||||
-- ## Defending the Ship when enemies are nearby
|
||||
-- This is not supported for naval cargo because most tanks don't float. Protect your transports...
|
||||
--
|
||||
-- ## Infantry or cargo **health**.
|
||||
-- When cargo is unboarded from the Ship, the cargo is actually respawned into the battlefield.
|
||||
-- As a result, the unboarding cargo is very _healthy_ every time it unboards.
|
||||
-- This is due to the limitation of the DCS simulator, which is not able to specify the health of newly spawned units as a parameter.
|
||||
-- However, cargo that was destroyed when unboarded and following the Ship won't be respawned again (this is likely not a thing for
|
||||
-- naval cargo due to the lack of support for defending the Ship mentioned above). Destroyed is destroyed.
|
||||
-- As a result, there is some additional strength that is gained when an unboarding action happens, but in terms of simulation balance
|
||||
-- this has marginal impact on the overall battlefield simulation. Given the relatively short duration of DCS missions and the somewhat
|
||||
-- lengthy naval transport times, most units entering the Ship as cargo will be freshly en route to an amphibious landing or transporting
|
||||
-- between warehouses.
|
||||
--
|
||||
-- ## Control the Ships on the map.
|
||||
--
|
||||
-- Currently, naval transports can only be controlled via scripts due to their reliance upon predefined Shipping Lanes created in the Mission
|
||||
-- Editor. An interesting future enhancement could leverage new pathfinding functionality for ships in the Ops module.
|
||||
--
|
||||
-- ## Cargo deployment.
|
||||
--
|
||||
-- Using the @{AI_CARGO_SHIP.Deploy}() method, you are able to direct the Ship towards a Deploy zone to unboard/unload the cargo at the
|
||||
-- specified coordinate. The Ship will follow the Shipping Lane to ensure consistent cargo transportation within the simulation environment.
|
||||
--
|
||||
-- ## Cargo pickup.
|
||||
--
|
||||
-- Using the @{AI_CARGO_SHIP.Pickup}() method, you are able to direct the Ship towards a Pickup zone to board/load the cargo at the specified
|
||||
-- coordinate. The Ship will follow the Shipping Lane to ensure consistent cargo transportation within the simulation environment.
|
||||
--
|
||||
--
|
||||
-- @field #AI_CARGO_SHIP
|
||||
AI_CARGO_SHIP = {
|
||||
ClassName = "AI_CARGO_SHIP",
|
||||
Coordinate = nil -- Core.Point#COORDINATE
|
||||
}
|
||||
|
||||
--- Creates a new AI_CARGO_SHIP object.
|
||||
-- @param #AI_CARGO_SHIP self
|
||||
-- @param Wrapper.Group#GROUP Ship The carrier Ship group
|
||||
-- @param Core.Set#SET_CARGO CargoSet The set of cargo to be transported
|
||||
-- @param #number CombatRadius Provide the combat radius to defend the carrier by unboarding the cargo when enemies are nearby. When CombatRadius is 0, no defense will occur.
|
||||
-- @param #table ShippingLane Table containing list of Shipping Lanes to be used
|
||||
-- @return #AI_CARGO_SHIP
|
||||
function AI_CARGO_SHIP:New( Ship, CargoSet, CombatRadius, ShippingLane )
|
||||
|
||||
local self = BASE:Inherit( self, AI_CARGO:New( Ship, CargoSet ) ) -- #AI_CARGO_SHIP
|
||||
|
||||
self:AddTransition( "*", "Monitor", "*" )
|
||||
self:AddTransition( "*", "Destroyed", "Destroyed" )
|
||||
self:AddTransition( "*", "Home", "*" )
|
||||
|
||||
self:SetCombatRadius( 0 ) -- Don't want to deploy cargo in middle of water to defend Ship, so set CombatRadius to 0
|
||||
self:SetShippingLane ( ShippingLane )
|
||||
|
||||
self:SetCarrier( Ship )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set the Carrier
|
||||
-- @param #AI_CARGO_SHIP self
|
||||
-- @param Wrapper.Group#GROUP CargoCarrier
|
||||
-- @return #AI_CARGO_SHIP
|
||||
function AI_CARGO_SHIP:SetCarrier( CargoCarrier )
|
||||
self.CargoCarrier = CargoCarrier -- Wrapper.Group#GROUIP
|
||||
self.CargoCarrier:SetState( self.CargoCarrier, "AI_CARGO_SHIP", self )
|
||||
|
||||
CargoCarrier:HandleEvent( EVENTS.Dead )
|
||||
|
||||
function CargoCarrier:OnEventDead( EventData )
|
||||
self:F({"dead"})
|
||||
local AICargoTroops = self:GetState( self, "AI_CARGO_SHIP" )
|
||||
self:F({AICargoTroops=AICargoTroops})
|
||||
if AICargoTroops then
|
||||
self:F({})
|
||||
if not AICargoTroops:Is( "Loaded" ) then
|
||||
-- Better hope they can swim!
|
||||
AICargoTroops:Destroyed()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
self.Zone = ZONE_UNIT:New( self.CargoCarrier:GetName() .. "-Zone", self.CargoCarrier, self.CombatRadius )
|
||||
self.Coalition = self.CargoCarrier:GetCoalition()
|
||||
|
||||
self:SetControllable( CargoCarrier )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- FInd a free Carrier within a radius
|
||||
-- @param #AI_CARGO_SHIP self
|
||||
-- @param Core.Point#COORDINATE Coordinate
|
||||
-- @param #number Radius
|
||||
-- @return Wrapper.Group#GROUP NewCarrier
|
||||
function AI_CARGO_SHIP:FindCarrier( Coordinate, Radius )
|
||||
|
||||
local CoordinateZone = ZONE_RADIUS:New( "Zone", Coordinate:GetVec2(), Radius )
|
||||
CoordinateZone:Scan( { Object.Category.UNIT } )
|
||||
for _, DCSUnit in pairs( CoordinateZone:GetScannedUnits() ) do
|
||||
local NearUnit = UNIT:Find( DCSUnit )
|
||||
self:F({NearUnit=NearUnit})
|
||||
if not NearUnit:GetState( NearUnit, "AI_CARGO_SHIP" ) then
|
||||
local Attributes = NearUnit:GetDesc()
|
||||
self:F({Desc=Attributes})
|
||||
if NearUnit:HasAttributes( "Trucks" ) then
|
||||
return NearUnit:GetGroup()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
function AI_CARGO_SHIP:SetShippingLane( ShippingLane )
|
||||
self.ShippingLane = ShippingLane
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
function AI_CARGO_SHIP:SetCombatRadius( CombatRadius )
|
||||
self.CombatRadius = CombatRadius or 0
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Follow Infantry to the Carrier
|
||||
-- @param #AI_CARGO_SHIP self
|
||||
-- @param #AI_CARGO_SHIP Me
|
||||
-- @param Wrapper.Unit#UNIT ShipUnit
|
||||
-- @param Cargo.CargoGroup#CARGO_GROUP Cargo
|
||||
-- @return #AI_CARGO_SHIP
|
||||
function AI_CARGO_SHIP:FollowToCarrier( Me, ShipUnit, CargoGroup )
|
||||
|
||||
local InfantryGroup = CargoGroup:GetGroup()
|
||||
|
||||
self:F( { self=self:GetClassNameAndID(), InfantryGroup = InfantryGroup:GetName() } )
|
||||
|
||||
if ShipUnit:IsAlive() then
|
||||
-- Check if the Cargo is near the CargoCarrier
|
||||
if InfantryGroup:IsPartlyInZone( ZONE_UNIT:New( "Radius", ShipUnit, 1000 ) ) then
|
||||
|
||||
-- Cargo does not need to navigate to Carrier
|
||||
Me:Guard()
|
||||
else
|
||||
|
||||
self:F( { InfantryGroup = InfantryGroup:GetName() } )
|
||||
if InfantryGroup:IsAlive() then
|
||||
|
||||
self:F( { InfantryGroup = InfantryGroup:GetName() } )
|
||||
local Waypoints = {}
|
||||
|
||||
-- Calculate new route
|
||||
local FromCoord = InfantryGroup:GetCoordinate()
|
||||
local FromGround = FromCoord:WaypointGround( 10, "Diamond" )
|
||||
self:F({FromGround=FromGround})
|
||||
table.insert( Waypoints, FromGround )
|
||||
|
||||
local ToCoord = ShipUnit:GetCoordinate():GetRandomCoordinateInRadius( 10, 5 )
|
||||
local ToGround = ToCoord:WaypointGround( 10, "Diamond" )
|
||||
self:F({ToGround=ToGround})
|
||||
table.insert( Waypoints, ToGround )
|
||||
|
||||
local TaskRoute = InfantryGroup:TaskFunction( "AI_CARGO_SHIP.FollowToCarrier", Me, ShipUnit, CargoGroup )
|
||||
|
||||
self:F({Waypoints=Waypoints})
|
||||
local Waypoint = Waypoints[#Waypoints]
|
||||
InfantryGroup:SetTaskWaypoint( Waypoint, TaskRoute ) -- Set for the given Route at Waypoint 2 the TaskRouteToZone
|
||||
|
||||
InfantryGroup:Route( Waypoints, 1 ) -- Move after a random number of seconds to the Route. See Route method for details
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function AI_CARGO_SHIP:onafterMonitor( Ship, From, Event, To )
|
||||
self:F( { Ship, From, Event, To, IsTransporting = self:IsTransporting() } )
|
||||
|
||||
if self.CombatRadius > 0 then
|
||||
-- We really shouldn't find ourselves in here for Ships since the CombatRadius should always be 0.
|
||||
-- This is to avoid Unloading the Ship in the middle of the sea.
|
||||
if Ship and Ship:IsAlive() then
|
||||
if self.CarrierCoordinate then
|
||||
if self:IsTransporting() == true then
|
||||
local Coordinate = Ship:GetCoordinate()
|
||||
if self:Is( "Unloaded" ) or self:Is( "Loaded" ) then
|
||||
self.Zone:Scan( { Object.Category.UNIT } )
|
||||
if self.Zone:IsAllInZoneOfCoalition( self.Coalition ) then
|
||||
if self:Is( "Unloaded" ) then
|
||||
-- There are no enemies within combat radius. Reload the CargoCarrier.
|
||||
self:Reload()
|
||||
end
|
||||
else
|
||||
if self:Is( "Loaded" ) then
|
||||
-- There are enemies within combat radius. Unload the CargoCarrier.
|
||||
self:__Unload( 1, nil, true ) -- The 2nd parameter is true, which means that the unload is for defending the carrier, not to deploy!
|
||||
else
|
||||
if self:Is( "Unloaded" ) then
|
||||
--self:Follow()
|
||||
end
|
||||
self:F( "I am here" .. self:GetCurrentState() )
|
||||
if self:Is( "Following" ) then
|
||||
for Cargo, ShipUnit in pairs( self.Carrier_Cargo ) do
|
||||
local Cargo = Cargo -- Cargo.Cargo#CARGO
|
||||
local ShipUnit = ShipUnit -- Wrapper.Unit#UNIT
|
||||
if Cargo:IsAlive() then
|
||||
if not Cargo:IsNear( ShipUnit, 40 ) then
|
||||
ShipUnit:RouteStop()
|
||||
self.CarrierStopped = true
|
||||
else
|
||||
if self.CarrierStopped then
|
||||
if Cargo:IsNear( ShipUnit, 25 ) then
|
||||
ShipUnit:RouteResume()
|
||||
self.CarrierStopped = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
self.CarrierCoordinate = Ship:GetCoordinate()
|
||||
end
|
||||
self:__Monitor( -5 )
|
||||
end
|
||||
end
|
||||
|
||||
--- Check if cargo ship is alive and trigger Load event
|
||||
-- @param Wrapper.Group#Group Ship
|
||||
-- @param #AI_CARGO_SHIP self
|
||||
function AI_CARGO_SHIP._Pickup( Ship, self, Coordinate, Speed, PickupZone )
|
||||
|
||||
Ship:F( { "AI_CARGO_Ship._Pickup:", Ship:GetName() } )
|
||||
|
||||
if Ship:IsAlive() then
|
||||
self:Load( PickupZone )
|
||||
end
|
||||
end
|
||||
|
||||
--- Check if cargo ship is alive and trigger Unload event. Good time to remind people that Lua is case sensitive and Unload != UnLoad
|
||||
-- @param Wrapper.Group#GROUP Ship
|
||||
-- @param #AI_CARGO_SHIP self
|
||||
function AI_CARGO_SHIP._Deploy( Ship, self, Coordinate, DeployZone )
|
||||
Ship:F( { "AI_CARGO_Ship._Deploy:", Ship } )
|
||||
|
||||
if Ship:IsAlive() then
|
||||
self:Unload( DeployZone )
|
||||
end
|
||||
end
|
||||
|
||||
--- on after Pickup event.
|
||||
-- @param AI_CARGO_SHIP Ship
|
||||
-- @param From
|
||||
-- @param Event
|
||||
-- @param To
|
||||
-- @param Core.Point#COORDINATE Coordinate of the pickup point
|
||||
-- @param #number Speed Speed in km/h to sail to the pickup coordinate. Default is 50% of max speed for the unit
|
||||
-- @param #number Height Altitude in meters to move to the pickup coordinate. This parameter is ignored for Ships
|
||||
-- @param Core.Zone#ZONE PickupZone (optional) The zone where the cargo will be picked up. The PickupZone can be nil if there was no PickupZoneSet provided
|
||||
function AI_CARGO_SHIP:onafterPickup( Ship, From, Event, To, Coordinate, Speed, Height, PickupZone )
|
||||
|
||||
if Ship and Ship:IsAlive() then
|
||||
AI_CARGO_SHIP._Pickup( Ship, self, Coordinate, Speed, PickupZone )
|
||||
self:GetParent( self, AI_CARGO_SHIP ).onafterPickup( self, Ship, From, Event, To, Coordinate, Speed, Height, PickupZone )
|
||||
end
|
||||
end
|
||||
|
||||
--- On after Deploy event.
|
||||
-- @param #AI_CARGO_SHIP self
|
||||
-- @param Wrapper.Group#GROUP SHIP
|
||||
-- @param From
|
||||
-- @param Event
|
||||
-- @param To
|
||||
-- @param Core.Point#COORDINATE Coordinate Coordinate of the deploy point
|
||||
-- @param #number Speed Speed in km/h to sail to the deploy coordinate. Default is 50% of max speed for the unit
|
||||
-- @param #number Height Altitude in meters to move to the deploy coordinate. This parameter is ignored for Ships
|
||||
-- @param Core.Zone#ZONE DeployZone The zone where the cargo will be deployed.
|
||||
function AI_CARGO_SHIP:onafterDeploy( Ship, From, Event, To, Coordinate, Speed, Height, DeployZone )
|
||||
|
||||
if Ship and Ship:IsAlive() then
|
||||
|
||||
Speed = Speed or Ship:GetSpeedMax()*0.8
|
||||
local lane = self.ShippingLane
|
||||
|
||||
if lane then
|
||||
local Waypoints = {}
|
||||
|
||||
for i=1, #lane do
|
||||
local coord = lane[i]
|
||||
local Waypoint = coord:WaypointGround(_speed)
|
||||
table.insert(Waypoints, Waypoint)
|
||||
end
|
||||
|
||||
local TaskFunction = Ship:TaskFunction( "AI_CARGO_SHIP._Deploy", self, Coordinate, DeployZone )
|
||||
local Waypoint = Waypoints[#Waypoints]
|
||||
Ship:SetTaskWaypoint( Waypoint, TaskFunction )
|
||||
Ship:Route(Waypoints, 1)
|
||||
self:GetParent( self, AI_CARGO_SHIP ).onafterDeploy( self, Ship, From, Event, To, Coordinate, Speed, Height, DeployZone )
|
||||
else
|
||||
self:E(self.lid.."ERROR: No shipping lane defined for Naval Transport!")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- On after Unload event.
|
||||
-- @param #AI_CARGO_SHIP self
|
||||
-- @param Wrapper.Group#GROUP Ship
|
||||
-- @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.
|
||||
function AI_CARGO_SHIP:onafterUnload( Ship, From, Event, To, DeployZone, Defend )
|
||||
self:F( { Ship, From, Event, To, DeployZone, Defend = Defend } )
|
||||
|
||||
local UnboardInterval = 5
|
||||
local UnboardDelay = 5
|
||||
|
||||
if Ship and Ship:IsAlive() then
|
||||
for _, ShipUnit in pairs( Ship:GetUnits() ) do
|
||||
local ShipUnit = ShipUnit -- Wrapper.Unit#UNIT
|
||||
Ship:RouteStop()
|
||||
for _, Cargo in pairs( ShipUnit:GetCargo() ) do
|
||||
self:F( { Cargo = Cargo:GetName(), Isloaded = Cargo:IsLoaded() } )
|
||||
if Cargo:IsLoaded() then
|
||||
local unboardCoord = DeployZone:GetRandomPointVec2()
|
||||
Cargo:__UnBoard( UnboardDelay, unboardCoord, 1000)
|
||||
UnboardDelay = UnboardDelay + Cargo:GetCount() * UnboardInterval
|
||||
self:__Unboard( UnboardDelay, Cargo, ShipUnit, DeployZone, Defend )
|
||||
if not Defend == true then
|
||||
Cargo:SetDeployed( true )
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function AI_CARGO_SHIP:onafterHome( Ship, From, Event, To, Coordinate, Speed, Height, HomeZone )
|
||||
if Ship and Ship:IsAlive() then
|
||||
|
||||
self.RouteHome = true
|
||||
Speed = Speed or Ship:GetSpeedMax()*0.8
|
||||
local lane = self.ShippingLane
|
||||
|
||||
if lane then
|
||||
local Waypoints = {}
|
||||
|
||||
-- Need to find a more generalized way to do this instead of reversing the shipping lane.
|
||||
-- This only works if the Source/Dest route waypoints are numbered 1..n and not n..1
|
||||
for i=#lane, 1, -1 do
|
||||
local coord = lane[i]
|
||||
local Waypoint = coord:WaypointGround(_speed)
|
||||
table.insert(Waypoints, Waypoint)
|
||||
end
|
||||
|
||||
local Waypoint = Waypoints[#Waypoints]
|
||||
Ship:Route(Waypoints, 1)
|
||||
|
||||
else
|
||||
self:E(self.lid.."ERROR: No shipping lane defined for Naval Transport!")
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -164,7 +164,7 @@ AI_FORMATION.__Enum.ReportType = {
|
||||
--- MENUPARAM type
|
||||
-- @type MENUPARAM
|
||||
-- @field #AI_FORMATION ParamSelf
|
||||
-- @field #Distance ParamDistance
|
||||
-- @field #number ParamDistance
|
||||
-- @field #function ParamFunction
|
||||
-- @field #string ParamMessage
|
||||
|
||||
@@ -184,11 +184,13 @@ function AI_FORMATION:New( FollowUnit, FollowGroupSet, FollowName, FollowBriefin
|
||||
|
||||
self.FollowGroupSet:ForEachGroup(
|
||||
function( FollowGroup )
|
||||
self:E("Following")
|
||||
--self:E("Following")
|
||||
FollowGroup:SetState( self, "Mode", self.__Enum.Mode.Formation )
|
||||
end
|
||||
)
|
||||
|
||||
self:SetFlightModeFormation()
|
||||
|
||||
self:SetFlightRandomization( 2 )
|
||||
|
||||
self:SetStartState( "None" )
|
||||
@@ -207,9 +209,9 @@ function AI_FORMATION:New( FollowUnit, FollowGroupSet, FollowName, FollowBriefin
|
||||
-- @param #string To
|
||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
|
||||
-- @param #nubmer YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #nubmer YSpace The space between groups on the Y-axis in meters for each sequent group.
|
||||
-- @param #nubmer ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
|
||||
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
|
||||
-- @return #boolean
|
||||
|
||||
@@ -222,9 +224,9 @@ function AI_FORMATION:New( FollowUnit, FollowGroupSet, FollowName, FollowBriefin
|
||||
-- @param #string To
|
||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
|
||||
-- @param #nubmer YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #nubmer YSpace The space between groups on the Y-axis in meters for each sequent group.
|
||||
-- @param #nubmer ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
|
||||
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
|
||||
|
||||
--- FormationLine Trigger for AI_FORMATION
|
||||
@@ -232,9 +234,9 @@ function AI_FORMATION:New( FollowUnit, FollowGroupSet, FollowName, FollowBriefin
|
||||
-- @param #AI_FORMATION self
|
||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
|
||||
-- @param #nubmer YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #nubmer YSpace The space between groups on the Y-axis in meters for each sequent group.
|
||||
-- @param #nubmer ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
|
||||
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
|
||||
|
||||
--- FormationLine Asynchronous Trigger for AI_FORMATION
|
||||
@@ -243,9 +245,9 @@ function AI_FORMATION:New( FollowUnit, FollowGroupSet, FollowName, FollowBriefin
|
||||
-- @param #number Delay
|
||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
|
||||
-- @param #nubmer YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #nubmer YSpace The space between groups on the Y-axis in meters for each sequent group.
|
||||
-- @param #nubmer ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
|
||||
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
|
||||
|
||||
self:AddTransition( "*", "FormationTrail", "*" )
|
||||
@@ -257,7 +259,7 @@ function AI_FORMATION:New( FollowUnit, FollowGroupSet, FollowName, FollowBriefin
|
||||
-- @param #string To
|
||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
|
||||
-- @param #nubmer YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @return #boolean
|
||||
|
||||
--- FormationTrail Handler OnAfter for AI_FORMATION
|
||||
@@ -268,14 +270,14 @@ function AI_FORMATION:New( FollowUnit, FollowGroupSet, FollowName, FollowBriefin
|
||||
-- @param #string To
|
||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
|
||||
-- @param #nubmer YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||
|
||||
--- FormationTrail Trigger for AI_FORMATION
|
||||
-- @function [parent=#AI_FORMATION] FormationTrail
|
||||
-- @param #AI_FORMATION self
|
||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
|
||||
-- @param #nubmer YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||
|
||||
--- FormationTrail Asynchronous Trigger for AI_FORMATION
|
||||
-- @function [parent=#AI_FORMATION] __FormationTrail
|
||||
@@ -283,7 +285,7 @@ function AI_FORMATION:New( FollowUnit, FollowGroupSet, FollowName, FollowBriefin
|
||||
-- @param #number Delay
|
||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
|
||||
-- @param #nubmer YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||
|
||||
self:AddTransition( "*", "FormationStack", "*" )
|
||||
--- FormationStack Handler OnBefore for AI_FORMATION
|
||||
@@ -294,7 +296,7 @@ function AI_FORMATION:New( FollowUnit, FollowGroupSet, FollowName, FollowBriefin
|
||||
-- @param #string To
|
||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
|
||||
-- @param #nubmer YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
|
||||
-- @return #boolean
|
||||
|
||||
@@ -306,7 +308,7 @@ function AI_FORMATION:New( FollowUnit, FollowGroupSet, FollowName, FollowBriefin
|
||||
-- @param #string To
|
||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
|
||||
-- @param #nubmer YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
|
||||
|
||||
--- FormationStack Trigger for AI_FORMATION
|
||||
@@ -314,7 +316,7 @@ function AI_FORMATION:New( FollowUnit, FollowGroupSet, FollowName, FollowBriefin
|
||||
-- @param #AI_FORMATION self
|
||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
|
||||
-- @param #nubmer YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
|
||||
|
||||
--- FormationStack Asynchronous Trigger for AI_FORMATION
|
||||
@@ -323,7 +325,7 @@ function AI_FORMATION:New( FollowUnit, FollowGroupSet, FollowName, FollowBriefin
|
||||
-- @param #number Delay
|
||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
|
||||
-- @param #nubmer YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
|
||||
|
||||
self:AddTransition( "*", "FormationLeftLine", "*" )
|
||||
@@ -335,8 +337,8 @@ function AI_FORMATION:New( FollowUnit, FollowGroupSet, FollowName, FollowBriefin
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||
-- @param #nubmer YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #nubmer ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
|
||||
-- @return #boolean
|
||||
|
||||
@@ -348,16 +350,16 @@ function AI_FORMATION:New( FollowUnit, FollowGroupSet, FollowName, FollowBriefin
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||
-- @param #nubmer YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #nubmer ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
|
||||
|
||||
--- FormationLeftLine Trigger for AI_FORMATION
|
||||
-- @function [parent=#AI_FORMATION] FormationLeftLine
|
||||
-- @param #AI_FORMATION self
|
||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||
-- @param #nubmer YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #nubmer ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
|
||||
|
||||
--- FormationLeftLine Asynchronous Trigger for AI_FORMATION
|
||||
@@ -365,8 +367,8 @@ function AI_FORMATION:New( FollowUnit, FollowGroupSet, FollowName, FollowBriefin
|
||||
-- @param #AI_FORMATION self
|
||||
-- @param #number Delay
|
||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||
-- @param #nubmer YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #nubmer ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
|
||||
|
||||
self:AddTransition( "*", "FormationRightLine", "*" )
|
||||
@@ -378,8 +380,8 @@ function AI_FORMATION:New( FollowUnit, FollowGroupSet, FollowName, FollowBriefin
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||
-- @param #nubmer YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #nubmer ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
|
||||
-- @return #boolean
|
||||
|
||||
@@ -391,16 +393,16 @@ function AI_FORMATION:New( FollowUnit, FollowGroupSet, FollowName, FollowBriefin
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||
-- @param #nubmer YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #nubmer ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
|
||||
|
||||
--- FormationRightLine Trigger for AI_FORMATION
|
||||
-- @function [parent=#AI_FORMATION] FormationRightLine
|
||||
-- @param #AI_FORMATION self
|
||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||
-- @param #nubmer YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #nubmer ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
|
||||
|
||||
--- FormationRightLine Asynchronous Trigger for AI_FORMATION
|
||||
@@ -408,8 +410,8 @@ function AI_FORMATION:New( FollowUnit, FollowGroupSet, FollowName, FollowBriefin
|
||||
-- @param #AI_FORMATION self
|
||||
-- @param #number Delay
|
||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||
-- @param #nubmer YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #nubmer ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
|
||||
|
||||
self:AddTransition( "*", "FormationLeftWing", "*" )
|
||||
@@ -422,8 +424,8 @@ function AI_FORMATION:New( FollowUnit, FollowGroupSet, FollowName, FollowBriefin
|
||||
-- @param #string To
|
||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
|
||||
-- @param #nubmer YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #nubmer ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
|
||||
-- @return #boolean
|
||||
|
||||
@@ -436,8 +438,8 @@ function AI_FORMATION:New( FollowUnit, FollowGroupSet, FollowName, FollowBriefin
|
||||
-- @param #string To
|
||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
|
||||
-- @param #nubmer YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #nubmer ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
|
||||
|
||||
--- FormationLeftWing Trigger for AI_FORMATION
|
||||
@@ -445,8 +447,8 @@ function AI_FORMATION:New( FollowUnit, FollowGroupSet, FollowName, FollowBriefin
|
||||
-- @param #AI_FORMATION self
|
||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
|
||||
-- @param #nubmer YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #nubmer ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
|
||||
|
||||
--- FormationLeftWing Asynchronous Trigger for AI_FORMATION
|
||||
@@ -455,8 +457,8 @@ function AI_FORMATION:New( FollowUnit, FollowGroupSet, FollowName, FollowBriefin
|
||||
-- @param #number Delay
|
||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
|
||||
-- @param #nubmer YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #nubmer ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
|
||||
|
||||
self:AddTransition( "*", "FormationRightWing", "*" )
|
||||
@@ -469,8 +471,8 @@ function AI_FORMATION:New( FollowUnit, FollowGroupSet, FollowName, FollowBriefin
|
||||
-- @param #string To
|
||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
|
||||
-- @param #nubmer YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #nubmer ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
|
||||
-- @return #boolean
|
||||
|
||||
@@ -483,8 +485,8 @@ function AI_FORMATION:New( FollowUnit, FollowGroupSet, FollowName, FollowBriefin
|
||||
-- @param #string To
|
||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
|
||||
-- @param #nubmer YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #nubmer ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
|
||||
|
||||
--- FormationRightWing Trigger for AI_FORMATION
|
||||
@@ -492,8 +494,8 @@ function AI_FORMATION:New( FollowUnit, FollowGroupSet, FollowName, FollowBriefin
|
||||
-- @param #AI_FORMATION self
|
||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
|
||||
-- @param #nubmer YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #nubmer ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
|
||||
|
||||
--- FormationRightWing Asynchronous Trigger for AI_FORMATION
|
||||
@@ -502,8 +504,8 @@ function AI_FORMATION:New( FollowUnit, FollowGroupSet, FollowName, FollowBriefin
|
||||
-- @param #number Delay
|
||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
|
||||
-- @param #nubmer YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #nubmer ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
|
||||
|
||||
self:AddTransition( "*", "FormationCenterWing", "*" )
|
||||
@@ -516,9 +518,9 @@ function AI_FORMATION:New( FollowUnit, FollowGroupSet, FollowName, FollowBriefin
|
||||
-- @param #string To
|
||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
|
||||
-- @param #nubmer YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
|
||||
-- @param #nubmer ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
|
||||
-- @return #boolean
|
||||
|
||||
@@ -531,9 +533,9 @@ function AI_FORMATION:New( FollowUnit, FollowGroupSet, FollowName, FollowBriefin
|
||||
-- @param #string To
|
||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
|
||||
-- @param #nubmer YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
|
||||
-- @param #nubmer ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
|
||||
|
||||
--- FormationCenterWing Trigger for AI_FORMATION
|
||||
@@ -541,9 +543,9 @@ function AI_FORMATION:New( FollowUnit, FollowGroupSet, FollowName, FollowBriefin
|
||||
-- @param #AI_FORMATION self
|
||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
|
||||
-- @param #nubmer YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
|
||||
-- @param #nubmer ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
|
||||
|
||||
--- FormationCenterWing Asynchronous Trigger for AI_FORMATION
|
||||
@@ -552,9 +554,9 @@ function AI_FORMATION:New( FollowUnit, FollowGroupSet, FollowName, FollowBriefin
|
||||
-- @param #number Delay
|
||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
|
||||
-- @param #nubmer YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
|
||||
-- @param #nubmer ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
|
||||
|
||||
self:AddTransition( "*", "FormationVic", "*" )
|
||||
@@ -566,9 +568,9 @@ function AI_FORMATION:New( FollowUnit, FollowGroupSet, FollowName, FollowBriefin
|
||||
-- @param #string To
|
||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
|
||||
-- @param #nubmer YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
|
||||
-- @param #nubmer ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
|
||||
-- @return #boolean
|
||||
|
||||
@@ -580,9 +582,9 @@ function AI_FORMATION:New( FollowUnit, FollowGroupSet, FollowName, FollowBriefin
|
||||
-- @param #string To
|
||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
|
||||
-- @param #nubmer YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
|
||||
-- @param #nubmer ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
|
||||
|
||||
--- FormationVic Trigger for AI_FORMATION
|
||||
@@ -590,9 +592,9 @@ function AI_FORMATION:New( FollowUnit, FollowGroupSet, FollowName, FollowBriefin
|
||||
-- @param #AI_FORMATION self
|
||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
|
||||
-- @param #nubmer YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
|
||||
-- @param #nubmer ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
|
||||
|
||||
--- FormationVic Asynchronous Trigger for AI_FORMATION
|
||||
@@ -601,9 +603,9 @@ function AI_FORMATION:New( FollowUnit, FollowGroupSet, FollowName, FollowBriefin
|
||||
-- @param #number Delay
|
||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
|
||||
-- @param #nubmer YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
|
||||
-- @param #nubmer ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
|
||||
|
||||
self:AddTransition( "*", "FormationBox", "*" )
|
||||
@@ -615,9 +617,9 @@ function AI_FORMATION:New( FollowUnit, FollowGroupSet, FollowName, FollowBriefin
|
||||
-- @param #string To
|
||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
|
||||
-- @param #nubmer YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
|
||||
-- @param #nubmer ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
|
||||
-- @param #number ZLevels The amount of levels on the Z-axis.
|
||||
-- @return #boolean
|
||||
@@ -630,9 +632,9 @@ function AI_FORMATION:New( FollowUnit, FollowGroupSet, FollowName, FollowBriefin
|
||||
-- @param #string To
|
||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
|
||||
-- @param #nubmer YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
|
||||
-- @param #nubmer ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
|
||||
-- @param #number ZLevels The amount of levels on the Z-axis.
|
||||
|
||||
@@ -641,9 +643,9 @@ function AI_FORMATION:New( FollowUnit, FollowGroupSet, FollowName, FollowBriefin
|
||||
-- @param #AI_FORMATION self
|
||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
|
||||
-- @param #nubmer YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
|
||||
-- @param #nubmer ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
|
||||
-- @param #number ZLevels The amount of levels on the Z-axis.
|
||||
|
||||
@@ -653,9 +655,9 @@ function AI_FORMATION:New( FollowUnit, FollowGroupSet, FollowName, FollowBriefin
|
||||
-- @param #number Delay
|
||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
|
||||
-- @param #nubmer YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
|
||||
-- @param #nubmer ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
|
||||
-- @param #number ZLevels The amount of levels on the Z-axis.
|
||||
|
||||
@@ -704,9 +706,9 @@ end
|
||||
-- @param #string To
|
||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
|
||||
-- @param #nubmer YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #nubmer YSpace The space between groups on the Y-axis in meters for each sequent group.
|
||||
-- @param #nubmer ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
|
||||
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
|
||||
-- @return #AI_FORMATION
|
||||
function AI_FORMATION:onafterFormationLine( FollowGroupSet, From , Event , To, XStart, XSpace, YStart, YSpace, ZStart, ZSpace, Formation ) --R2.1
|
||||
@@ -751,7 +753,7 @@ end
|
||||
-- @param #string To
|
||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
|
||||
-- @param #nubmer YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @return #AI_FORMATION
|
||||
function AI_FORMATION:onafterFormationTrail( FollowGroupSet, From , Event , To, XStart, XSpace, YStart ) --R2.1
|
||||
|
||||
@@ -769,7 +771,7 @@ end
|
||||
-- @param #string To
|
||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
|
||||
-- @param #nubmer YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
|
||||
-- @return #AI_FORMATION
|
||||
function AI_FORMATION:onafterFormationStack( FollowGroupSet, From , Event , To, XStart, XSpace, YStart, YSpace ) --R2.1
|
||||
@@ -789,8 +791,8 @@ end
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||
-- @param #nubmer YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #nubmer ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
|
||||
-- @return #AI_FORMATION
|
||||
function AI_FORMATION:onafterFormationLeftLine( FollowGroupSet, From , Event , To, XStart, YStart, ZStart, ZSpace ) --R2.1
|
||||
@@ -808,8 +810,8 @@ end
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||
-- @param #nubmer YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #nubmer ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
|
||||
-- @return #AI_FORMATION
|
||||
function AI_FORMATION:onafterFormationRightLine( FollowGroupSet, From , Event , To, XStart, YStart, ZStart, ZSpace ) --R2.1
|
||||
@@ -828,8 +830,8 @@ end
|
||||
-- @param #string To
|
||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
|
||||
-- @param #nubmer YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #nubmer ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
|
||||
function AI_FORMATION:onafterFormationLeftWing( FollowGroupSet, From , Event , To, XStart, XSpace, YStart, ZStart, ZSpace ) --R2.1
|
||||
|
||||
@@ -848,8 +850,8 @@ end
|
||||
-- @param #string To
|
||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
|
||||
-- @param #nubmer YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #nubmer ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
|
||||
function AI_FORMATION:onafterFormationRightWing( FollowGroupSet, From , Event , To, XStart, XSpace, YStart, ZStart, ZSpace ) --R2.1
|
||||
|
||||
@@ -867,9 +869,9 @@ end
|
||||
-- @param #string To
|
||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
|
||||
-- @param #nubmer YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
|
||||
-- @param #nubmer ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
|
||||
function AI_FORMATION:onafterFormationCenterWing( FollowGroupSet, From , Event , To, XStart, XSpace, YStart, YSpace, ZStart, ZSpace ) --R2.1
|
||||
|
||||
@@ -905,9 +907,9 @@ end
|
||||
-- @param #string To
|
||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
|
||||
-- @param #nubmer YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
|
||||
-- @param #nubmer ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
|
||||
-- @return #AI_FORMATION
|
||||
function AI_FORMATION:onafterFormationVic( FollowGroupSet, From , Event , To, XStart, XSpace, YStart, YSpace, ZStart, ZSpace ) --R2.1
|
||||
@@ -924,9 +926,9 @@ end
|
||||
-- @param #string To
|
||||
-- @param #number XStart The start position on the X-axis in meters for the first group.
|
||||
-- @param #number XSpace The space between groups on the X-axis in meters for each sequent group.
|
||||
-- @param #nubmer YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number YStart The start position on the Y-axis in meters for the first group.
|
||||
-- @param #number YSpace The space between groups on the Y-axis in meters for each sequent group.
|
||||
-- @param #nubmer ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number ZStart The start position on the Z-axis in meters for the first group.
|
||||
-- @param #number ZSpace The space between groups on the Z-axis in meters for each sequent group.
|
||||
-- @param #number ZLevels The amount of levels on the Z-axis.
|
||||
-- @return #AI_FORMATION
|
||||
@@ -997,7 +999,7 @@ function AI_FORMATION:SetFlightModeMission( FollowGroup )
|
||||
FollowGroup:SetState( FollowGroup, "PreviousMode", FollowGroup:GetState( FollowGroup, "Mode" ) )
|
||||
FollowGroup:SetState( FollowGroup, "Mode", self.__Enum.Mode.Mission )
|
||||
else
|
||||
self.EscortGroupSet:ForSomeGroupAlive(
|
||||
self.FollowGroupSet:ForSomeGroupAlive(
|
||||
--- @param Core.Group#GROUP EscortGroup
|
||||
function( FollowGroup )
|
||||
FollowGroup:SetState( FollowGroup, "PreviousMode", FollowGroup:GetState( FollowGroup, "Mode" ) )
|
||||
@@ -1021,7 +1023,7 @@ function AI_FORMATION:SetFlightModeAttack( FollowGroup )
|
||||
FollowGroup:SetState( FollowGroup, "PreviousMode", FollowGroup:GetState( FollowGroup, "Mode" ) )
|
||||
FollowGroup:SetState( FollowGroup, "Mode", self.__Enum.Mode.Attack )
|
||||
else
|
||||
self.EscortGroupSet:ForSomeGroupAlive(
|
||||
self.FollowGroupSet:ForSomeGroupAlive(
|
||||
--- @param Core.Group#GROUP EscortGroup
|
||||
function( FollowGroup )
|
||||
FollowGroup:SetState( FollowGroup, "PreviousMode", FollowGroup:GetState( FollowGroup, "Mode" ) )
|
||||
@@ -1045,7 +1047,7 @@ function AI_FORMATION:SetFlightModeFormation( FollowGroup )
|
||||
FollowGroup:SetState( FollowGroup, "PreviousMode", FollowGroup:GetState( FollowGroup, "Mode" ) )
|
||||
FollowGroup:SetState( FollowGroup, "Mode", self.__Enum.Mode.Formation )
|
||||
else
|
||||
self.EscortGroupSet:ForSomeGroupAlive(
|
||||
self.FollowGroupSet:ForSomeGroupAlive(
|
||||
--- @param Core.Group#GROUP EscortGroup
|
||||
function( FollowGroup )
|
||||
FollowGroup:SetState( FollowGroup, "PreviousMode", FollowGroup:GetState( FollowGroup, "Mode" ) )
|
||||
@@ -1065,7 +1067,7 @@ end
|
||||
-- @param Core.Set#SET_GROUP FollowGroupSet The following set of groups.
|
||||
-- @param #string From From state.
|
||||
-- @param #string Event Event.
|
||||
-- @pram #string To The to state.
|
||||
-- @param #string To The to state.
|
||||
function AI_FORMATION:onafterStop(FollowGroupSet, From, Event, To) --R2.1
|
||||
self:E("Stopping formation.")
|
||||
end
|
||||
@@ -1075,7 +1077,7 @@ end
|
||||
-- @param Core.Set#SET_GROUP FollowGroupSet The following set of groups.
|
||||
-- @param #string From From state.
|
||||
-- @param #string Event Event.
|
||||
-- @pram #string To The to state.
|
||||
-- @param #string To The to state.
|
||||
function AI_FORMATION:onbeforeFollow( FollowGroupSet, From, Event, To ) --R2.1
|
||||
if From=="Stopped" then
|
||||
return false -- Deny transition.
|
||||
@@ -1083,7 +1085,12 @@ function AI_FORMATION:onbeforeFollow( FollowGroupSet, From, Event, To ) --R2.1
|
||||
return true
|
||||
end
|
||||
|
||||
--- @param #AI_FORMATION self
|
||||
--- Enter following state.
|
||||
-- @param #AI_FORMATION self
|
||||
-- @param Core.Set#SET_GROUP FollowGroupSet The following set of groups.
|
||||
-- @param #string From From state.
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To The to state.
|
||||
function AI_FORMATION:onenterFollowing( FollowGroupSet ) --R2.1
|
||||
|
||||
if self.FollowUnit:IsAlive() then
|
||||
@@ -1093,153 +1100,184 @@ function AI_FORMATION:onenterFollowing( FollowGroupSet ) --R2.1
|
||||
local CT1, CT2, CV1, CV2
|
||||
CT1 = ClientUnit:GetState( self, "CT1" )
|
||||
|
||||
local CuVec3=ClientUnit:GetVec3()
|
||||
|
||||
if CT1 == nil or CT1 == 0 then
|
||||
ClientUnit:SetState( self, "CV1", ClientUnit:GetPointVec3() )
|
||||
ClientUnit:SetState( self, "CV1", CuVec3)
|
||||
ClientUnit:SetState( self, "CT1", timer.getTime() )
|
||||
else
|
||||
CT1 = ClientUnit:GetState( self, "CT1" )
|
||||
CT2 = timer.getTime()
|
||||
CV1 = ClientUnit:GetState( self, "CV1" )
|
||||
CV2 = ClientUnit:GetPointVec3()
|
||||
CV2 = CuVec3
|
||||
|
||||
ClientUnit:SetState( self, "CT1", CT2 )
|
||||
ClientUnit:SetState( self, "CV1", CV2 )
|
||||
end
|
||||
|
||||
FollowGroupSet:ForEachGroupAlive(
|
||||
--- @param Wrapper.Group#GROUP FollowGroup
|
||||
-- @param Wrapper.Unit#UNIT ClientUnit
|
||||
function( FollowGroup, Formation, ClientUnit, CT1, CV1, CT2, CV2 )
|
||||
|
||||
if FollowGroup:GetState( FollowGroup, "Mode" ) == self.__Enum.Mode.Formation then
|
||||
|
||||
self:T({Mode=FollowGroup:GetState( FollowGroup, "Mode" )})
|
||||
|
||||
FollowGroup:OptionROTEvadeFire()
|
||||
FollowGroup:OptionROEReturnFire()
|
||||
|
||||
local GroupUnit = FollowGroup:GetUnit( 1 )
|
||||
local FollowFormation = FollowGroup:GetState( self, "FormationVec3" )
|
||||
if FollowFormation then
|
||||
local FollowDistance = FollowFormation.x
|
||||
|
||||
local GT1 = GroupUnit:GetState( self, "GT1" )
|
||||
|
||||
if CT1 == nil or CT1 == 0 or GT1 == nil or GT1 == 0 then
|
||||
GroupUnit:SetState( self, "GV1", GroupUnit:GetPointVec3() )
|
||||
GroupUnit:SetState( self, "GT1", timer.getTime() )
|
||||
else
|
||||
local CD = ( ( CV2.x - CV1.x )^2 + ( CV2.y - CV1.y )^2 + ( CV2.z - CV1.z )^2 ) ^ 0.5
|
||||
local CT = CT2 - CT1
|
||||
|
||||
local CS = ( 3600 / CT ) * ( CD / 1000 ) / 3.6
|
||||
|
||||
local CDv = { x = CV2.x - CV1.x, y = CV2.y - CV1.y, z = CV2.z - CV1.z }
|
||||
local Ca = math.atan2( CDv.x, CDv.z )
|
||||
|
||||
local GT1 = GroupUnit:GetState( self, "GT1" )
|
||||
local GT2 = timer.getTime()
|
||||
local GV1 = GroupUnit:GetState( self, "GV1" )
|
||||
local GV2 = GroupUnit:GetPointVec3()
|
||||
GV2:AddX( math.random( -Formation.FlightRandomization / 2, Formation.FlightRandomization / 2 ) )
|
||||
GV2:AddY( math.random( -Formation.FlightRandomization / 2, Formation.FlightRandomization / 2 ) )
|
||||
GV2:AddZ( math.random( -Formation.FlightRandomization / 2, Formation.FlightRandomization / 2 ) )
|
||||
GroupUnit:SetState( self, "GT1", GT2 )
|
||||
GroupUnit:SetState( self, "GV1", GV2 )
|
||||
|
||||
|
||||
local GD = ( ( GV2.x - GV1.x )^2 + ( GV2.y - GV1.y )^2 + ( GV2.z - GV1.z )^2 ) ^ 0.5
|
||||
local GT = GT2 - GT1
|
||||
|
||||
|
||||
-- Calculate the distance
|
||||
local GDv = { x = GV2.x - CV1.x, y = GV2.y - CV1.y, z = GV2.z - CV1.z }
|
||||
local Alpha_T = math.atan2( GDv.x, GDv.z ) - math.atan2( CDv.x, CDv.z )
|
||||
local Alpha_R = ( Alpha_T < 0 ) and Alpha_T + 2 * math.pi or Alpha_T
|
||||
local Position = math.cos( Alpha_R )
|
||||
local GD = ( ( GDv.x )^2 + ( GDv.z )^2 ) ^ 0.5
|
||||
local Distance = GD * Position + - CS * 0.5
|
||||
|
||||
-- Calculate the group direction vector
|
||||
local GV = { x = GV2.x - CV2.x, y = GV2.y - CV2.y, z = GV2.z - CV2.z }
|
||||
|
||||
-- Calculate GH2, GH2 with the same height as CV2.
|
||||
local GH2 = { x = GV2.x, y = CV2.y + FollowFormation.y, z = GV2.z }
|
||||
|
||||
-- Calculate the angle of GV to the orthonormal plane
|
||||
local alpha = math.atan2( GV.x, GV.z )
|
||||
|
||||
local GVx = FollowFormation.z * math.cos( Ca ) + FollowFormation.x * math.sin( Ca )
|
||||
local GVz = FollowFormation.x * math.cos( Ca ) - FollowFormation.z * math.sin( Ca )
|
||||
|
||||
|
||||
-- Now we calculate the intersecting vector between the circle around CV2 with radius FollowDistance and GH2.
|
||||
-- From the GeoGebra model: CVI = (x(CV2) + FollowDistance cos(alpha), y(GH2) + FollowDistance sin(alpha), z(CV2))
|
||||
local Inclination = ( Distance + FollowFormation.x ) / 10
|
||||
if Inclination < -30 then
|
||||
Inclination = - 30
|
||||
end
|
||||
local CVI = { x = CV2.x + CS * 10 * math.sin(Ca),
|
||||
y = GH2.y + Inclination, -- + FollowFormation.y,
|
||||
y = GH2.y,
|
||||
z = CV2.z + CS * 10 * math.cos(Ca),
|
||||
}
|
||||
|
||||
-- Calculate the direction vector DV of the escort group. We use CVI as the base and CV2 as the direction.
|
||||
local DV = { x = CV2.x - CVI.x, y = CV2.y - CVI.y, z = CV2.z - CVI.z }
|
||||
|
||||
-- We now calculate the unary direction vector DVu, so that we can multiply DVu with the speed, which is expressed in meters / s.
|
||||
-- We need to calculate this vector to predict the point the escort group needs to fly to according its speed.
|
||||
-- The distance of the destination point should be far enough not to have the aircraft starting to swipe left to right...
|
||||
local DVu = { x = DV.x / FollowDistance, y = DV.y, z = DV.z / FollowDistance }
|
||||
|
||||
-- Now we can calculate the group destination vector GDV.
|
||||
local GDV = { x = CVI.x, y = CVI.y, z = CVI.z }
|
||||
|
||||
local ADDx = FollowFormation.x * math.cos(alpha) - FollowFormation.z * math.sin(alpha)
|
||||
local ADDz = FollowFormation.z * math.cos(alpha) + FollowFormation.x * math.sin(alpha)
|
||||
|
||||
local GDV_Formation = {
|
||||
x = GDV.x - GVx,
|
||||
y = GDV.y,
|
||||
z = GDV.z - GVz
|
||||
}
|
||||
|
||||
if self.SmokeDirectionVector == true then
|
||||
trigger.action.smoke( GDV, trigger.smokeColor.Green )
|
||||
trigger.action.smoke( GDV_Formation, trigger.smokeColor.White )
|
||||
end
|
||||
|
||||
|
||||
|
||||
local Time = 120
|
||||
|
||||
local Speed = - ( Distance + FollowFormation.x ) / Time
|
||||
|
||||
if Distance > -10000 then
|
||||
Speed = - ( Distance + FollowFormation.x ) / 60
|
||||
end
|
||||
|
||||
if Distance > -2500 then
|
||||
Speed = - ( Distance + FollowFormation.x ) / 20
|
||||
end
|
||||
|
||||
local GS = Speed + CS
|
||||
|
||||
self:F( { Distance = Distance, Speed = Speed, CS = CS, GS = GS } )
|
||||
|
||||
|
||||
-- Now route the escort to the desired point with the desired speed.
|
||||
FollowGroup:RouteToVec3( GDV_Formation, GS ) -- DCS models speed in Mps (Miles per second)
|
||||
end
|
||||
end
|
||||
end
|
||||
end,
|
||||
self, ClientUnit, CT1, CV1, CT2, CV2
|
||||
)
|
||||
--FollowGroupSet:ForEachGroupAlive( bla, self, ClientUnit, CT1, CV1, CT2, CV2)
|
||||
|
||||
for _,_group in pairs(FollowGroupSet:GetSet()) do
|
||||
local group=_group --Wrapper.Group#GROUP
|
||||
if group and group:IsAlive() then
|
||||
self:FollowMe(group, ClientUnit, CT1, CV1, CT2, CV2)
|
||||
end
|
||||
end
|
||||
|
||||
self:__Follow( -self.dtFollow )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
--- Follow me.
|
||||
-- @param #AI_FORMATION self
|
||||
-- @param Wrapper.Group#GROUP FollowGroup Follow group.
|
||||
-- @param Wrapper.Unit#UNIT ClientUnit Client Unit.
|
||||
-- @param DCS#Time CT1 Time
|
||||
-- @param DCS#Vec3 CV1 Vec3
|
||||
-- @param DCS#Time CT2 Time
|
||||
-- @param DCS#Vec3 CV2 Vec3
|
||||
function AI_FORMATION:FollowMe(FollowGroup, ClientUnit, CT1, CV1, CT2, CV2)
|
||||
|
||||
if FollowGroup:GetState( FollowGroup, "Mode" ) == self.__Enum.Mode.Formation then
|
||||
|
||||
self:T({Mode=FollowGroup:GetState( FollowGroup, "Mode" )})
|
||||
|
||||
FollowGroup:OptionROTEvadeFire()
|
||||
FollowGroup:OptionROEReturnFire()
|
||||
|
||||
local GroupUnit = FollowGroup:GetUnit( 1 )
|
||||
|
||||
local GuVec3=GroupUnit:GetVec3()
|
||||
|
||||
local FollowFormation = FollowGroup:GetState( self, "FormationVec3" )
|
||||
|
||||
if FollowFormation then
|
||||
local FollowDistance = FollowFormation.x
|
||||
|
||||
local GT1 = GroupUnit:GetState( self, "GT1" )
|
||||
|
||||
if CT1 == nil or CT1 == 0 or GT1 == nil or GT1 == 0 then
|
||||
GroupUnit:SetState( self, "GV1", GuVec3)
|
||||
GroupUnit:SetState( self, "GT1", timer.getTime() )
|
||||
else
|
||||
local CD = ( ( CV2.x - CV1.x )^2 + ( CV2.y - CV1.y )^2 + ( CV2.z - CV1.z )^2 ) ^ 0.5
|
||||
local CT = CT2 - CT1
|
||||
|
||||
local CS = ( 3600 / CT ) * ( CD / 1000 ) / 3.6
|
||||
|
||||
local CDv = { x = CV2.x - CV1.x, y = CV2.y - CV1.y, z = CV2.z - CV1.z }
|
||||
local Ca = math.atan2( CDv.x, CDv.z )
|
||||
|
||||
local GT1 = GroupUnit:GetState( self, "GT1" )
|
||||
local GT2 = timer.getTime()
|
||||
|
||||
local GV1 = GroupUnit:GetState( self, "GV1" )
|
||||
local GV2 = GuVec3
|
||||
|
||||
--[[
|
||||
GV2:AddX( math.random( -Formation.FlightRandomization / 2, Formation.FlightRandomization / 2 ) )
|
||||
GV2:AddY( math.random( -Formation.FlightRandomization / 2, Formation.FlightRandomization / 2 ) )
|
||||
GV2:AddZ( math.random( -Formation.FlightRandomization / 2, Formation.FlightRandomization / 2 ) )
|
||||
]]
|
||||
|
||||
GV2.x=GV2.x+math.random( -self.FlightRandomization / 2, self.FlightRandomization / 2 )
|
||||
GV2.y=GV2.y+math.random( -self.FlightRandomization / 2, self.FlightRandomization / 2 )
|
||||
GV2.z=GV2.z+math.random( -self.FlightRandomization / 2, self.FlightRandomization / 2 )
|
||||
|
||||
|
||||
GroupUnit:SetState( self, "GT1", GT2 )
|
||||
GroupUnit:SetState( self, "GV1", GV2 )
|
||||
|
||||
|
||||
local GD = ( ( GV2.x - GV1.x )^2 + ( GV2.y - GV1.y )^2 + ( GV2.z - GV1.z )^2 ) ^ 0.5
|
||||
local GT = GT2 - GT1
|
||||
|
||||
|
||||
-- Calculate the distance
|
||||
local GDv = { x = GV2.x - CV1.x, y = GV2.y - CV1.y, z = GV2.z - CV1.z }
|
||||
local Alpha_T = math.atan2( GDv.x, GDv.z ) - math.atan2( CDv.x, CDv.z )
|
||||
local Alpha_R = ( Alpha_T < 0 ) and Alpha_T + 2 * math.pi or Alpha_T
|
||||
local Position = math.cos( Alpha_R )
|
||||
local GD = ( ( GDv.x )^2 + ( GDv.z )^2 ) ^ 0.5
|
||||
local Distance = GD * Position + - CS * 0.5
|
||||
|
||||
-- Calculate the group direction vector
|
||||
local GV = { x = GV2.x - CV2.x, y = GV2.y - CV2.y, z = GV2.z - CV2.z }
|
||||
|
||||
-- Calculate GH2, GH2 with the same height as CV2.
|
||||
local GH2 = { x = GV2.x, y = CV2.y + FollowFormation.y, z = GV2.z }
|
||||
|
||||
-- Calculate the angle of GV to the orthonormal plane
|
||||
local alpha = math.atan2( GV.x, GV.z )
|
||||
|
||||
local GVx = FollowFormation.z * math.cos( Ca ) + FollowFormation.x * math.sin( Ca )
|
||||
local GVz = FollowFormation.x * math.cos( Ca ) - FollowFormation.z * math.sin( Ca )
|
||||
|
||||
|
||||
-- Now we calculate the intersecting vector between the circle around CV2 with radius FollowDistance and GH2.
|
||||
-- From the GeoGebra model: CVI = (x(CV2) + FollowDistance cos(alpha), y(GH2) + FollowDistance sin(alpha), z(CV2))
|
||||
local Inclination = ( Distance + FollowFormation.x ) / 10
|
||||
if Inclination < -30 then
|
||||
Inclination = - 30
|
||||
end
|
||||
|
||||
local CVI = {
|
||||
x = CV2.x + CS * 10 * math.sin(Ca),
|
||||
y = GH2.y + Inclination, -- + FollowFormation.y,
|
||||
y = GH2.y,
|
||||
z = CV2.z + CS * 10 * math.cos(Ca),
|
||||
}
|
||||
|
||||
-- Calculate the direction vector DV of the escort group. We use CVI as the base and CV2 as the direction.
|
||||
local DV = { x = CV2.x - CVI.x, y = CV2.y - CVI.y, z = CV2.z - CVI.z }
|
||||
|
||||
-- We now calculate the unary direction vector DVu, so that we can multiply DVu with the speed, which is expressed in meters / s.
|
||||
-- We need to calculate this vector to predict the point the escort group needs to fly to according its speed.
|
||||
-- The distance of the destination point should be far enough not to have the aircraft starting to swipe left to right...
|
||||
local DVu = { x = DV.x / FollowDistance, y = DV.y, z = DV.z / FollowDistance }
|
||||
|
||||
-- Now we can calculate the group destination vector GDV.
|
||||
local GDV = { x = CVI.x, y = CVI.y, z = CVI.z }
|
||||
|
||||
local ADDx = FollowFormation.x * math.cos(alpha) - FollowFormation.z * math.sin(alpha)
|
||||
local ADDz = FollowFormation.z * math.cos(alpha) + FollowFormation.x * math.sin(alpha)
|
||||
|
||||
local GDV_Formation = {
|
||||
x = GDV.x - GVx,
|
||||
y = GDV.y,
|
||||
z = GDV.z - GVz
|
||||
}
|
||||
|
||||
-- Debug smoke.
|
||||
if self.SmokeDirectionVector == true then
|
||||
trigger.action.smoke( GDV, trigger.smokeColor.Green )
|
||||
trigger.action.smoke( GDV_Formation, trigger.smokeColor.White )
|
||||
end
|
||||
|
||||
|
||||
|
||||
local Time = 120
|
||||
|
||||
local Speed = - ( Distance + FollowFormation.x ) / Time
|
||||
|
||||
if Distance > -10000 then
|
||||
Speed = - ( Distance + FollowFormation.x ) / 60
|
||||
end
|
||||
|
||||
if Distance > -2500 then
|
||||
Speed = - ( Distance + FollowFormation.x ) / 20
|
||||
end
|
||||
|
||||
local GS = Speed + CS
|
||||
|
||||
--self:F( { Distance = Distance, Speed = Speed, CS = CS, GS = GS } )
|
||||
|
||||
-- Now route the escort to the desired point with the desired speed.
|
||||
FollowGroup:RouteToVec3( GDV_Formation, GS ) -- DCS models speed in Mps (Miles per second)
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
--- **Core** -- Management of CARGO logistics, that can be transported from and to transportation carriers.
|
||||
--- **Cargo** - Management of CARGO logistics, that can be transported from and to transportation carriers.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
@@ -1066,18 +1066,26 @@ do -- CARGO_REPRESENTABLE
|
||||
|
||||
--- CARGO_REPRESENTABLE Constructor.
|
||||
-- @param #CARGO_REPRESENTABLE self
|
||||
-- @param #string Type
|
||||
-- @param #string Name
|
||||
-- @param #number LoadRadius (optional)
|
||||
-- @param #number NearRadius (optional)
|
||||
-- @param Wrapper.Positionable#POSITIONABLE CargoObject The cargo object.
|
||||
-- @param #string Type Type name
|
||||
-- @param #string Name Name.
|
||||
-- @param #number LoadRadius (optional) Radius in meters.
|
||||
-- @param #number NearRadius (optional) Radius in meters when the cargo is loaded into the carrier.
|
||||
-- @return #CARGO_REPRESENTABLE
|
||||
function CARGO_REPRESENTABLE:New( CargoObject, Type, Name, LoadRadius, NearRadius )
|
||||
|
||||
-- Inherit CARGO.
|
||||
local self = BASE:Inherit( self, CARGO:New( Type, Name, 0, LoadRadius, NearRadius ) ) -- #CARGO_REPRESENTABLE
|
||||
self:F( { Type, Name, LoadRadius, NearRadius } )
|
||||
|
||||
local Desc = CargoObject:GetDesc()
|
||||
self:I( { Desc = Desc } )
|
||||
-- Descriptors.
|
||||
local Desc=CargoObject:GetDesc()
|
||||
self:T({Desc=Desc})
|
||||
|
||||
-- Weight.
|
||||
local Weight = math.random( 80, 120 )
|
||||
|
||||
-- Adjust weight..
|
||||
if Desc then
|
||||
if Desc.typeName == "2B11 mortar" then
|
||||
Weight = 210
|
||||
@@ -1086,13 +1094,8 @@ do -- CARGO_REPRESENTABLE
|
||||
end
|
||||
end
|
||||
|
||||
-- Set weight.
|
||||
self:SetWeight( Weight )
|
||||
|
||||
-- local Box = CargoUnit:GetBoundingBox()
|
||||
-- local VolumeUnit = ( Box.max.x - Box.min.x ) * ( Box.max.y - Box.min.y ) * ( Box.max.z - Box.min.z )
|
||||
-- self:I( { VolumeUnit = VolumeUnit, WeightUnit = WeightUnit } )
|
||||
--self:SetVolume( VolumeUnit )
|
||||
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
--- **Cargo** -- Management of grouped cargo logistics, which are based on a @{Wrapper.Group} object.
|
||||
--- **Cargo** - Management of grouped cargo logistics, which are based on a @{Wrapper.Group} object.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
@@ -56,6 +56,8 @@ do -- CARGO_GROUP
|
||||
-- @param #number NearRadius (optional) Once the units are within this radius of the carrier, they are actually loaded, i.e. disappear from the scene.
|
||||
-- @return #CARGO_GROUP Cargo group object.
|
||||
function CARGO_GROUP:New( CargoGroup, Type, Name, LoadRadius, NearRadius )
|
||||
|
||||
-- Inherit CAROG_REPORTABLE
|
||||
local self = BASE:Inherit( self, CARGO_REPORTABLE:New( Type, Name, 0, LoadRadius, NearRadius ) ) -- #CARGO_GROUP
|
||||
self:F( { Type, Name, LoadRadius } )
|
||||
|
||||
@@ -76,6 +78,9 @@ do -- CARGO_GROUP
|
||||
local GroupName = CargoGroup:GetName()
|
||||
self.CargoName = Name
|
||||
self.CargoTemplate = UTILS.DeepCopy( _DATABASE:GetGroupTemplate( GroupName ) )
|
||||
|
||||
-- Deactivate late activation.
|
||||
self.CargoTemplate.lateActivation=false
|
||||
|
||||
self.GroupTemplate = UTILS.DeepCopy( self.CargoTemplate )
|
||||
self.GroupTemplate.name = self.CargoName .. "#CARGO"
|
||||
@@ -481,7 +486,7 @@ do -- CARGO_GROUP
|
||||
-- @param #string Event
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
-- @param Core.Point#POINT_VEC2
|
||||
-- @param Core.Point#POINT_VEC2 ToPointVec2
|
||||
function CARGO_GROUP:onafterUnLoad( From, Event, To, ToPointVec2, ... )
|
||||
--self:F( { From, Event, To, ToPointVec2 } )
|
||||
|
||||
@@ -491,7 +496,10 @@ do -- CARGO_GROUP
|
||||
self.CargoSet:ForEach(
|
||||
function( Cargo )
|
||||
--Cargo:UnLoad( ToPointVec2 )
|
||||
local RandomVec2=ToPointVec2:GetRandomPointVec2InRadius(20, 10)
|
||||
local RandomVec2=nil
|
||||
if ToPointVec2 then
|
||||
RandomVec2=ToPointVec2:GetRandomPointVec2InRadius(20, 10)
|
||||
end
|
||||
Cargo:UnBoard( RandomVec2 )
|
||||
end
|
||||
)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
--- **Cargo** -- Management of single cargo logistics, which are based on a @{Wrapper.Unit} object.
|
||||
--- **Cargo** - Management of single cargo logistics, which are based on a @{Wrapper.Unit} object.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
@@ -46,14 +46,17 @@ do -- CARGO_UNIT
|
||||
-- @param #number NearRadius (optional)
|
||||
-- @return #CARGO_UNIT
|
||||
function CARGO_UNIT:New( CargoUnit, Type, Name, LoadRadius, NearRadius )
|
||||
local self = BASE:Inherit( self, CARGO_REPRESENTABLE:New( CargoUnit, Type, Name, LoadRadius, NearRadius ) ) -- #CARGO_UNIT
|
||||
self:I( { Type, Name, LoadRadius, NearRadius } )
|
||||
|
||||
self:T( CargoUnit )
|
||||
-- Inherit CARGO_REPRESENTABLE.
|
||||
local self = BASE:Inherit( self, CARGO_REPRESENTABLE:New( CargoUnit, Type, Name, LoadRadius, NearRadius ) ) -- #CARGO_UNIT
|
||||
|
||||
-- Debug info.
|
||||
self:T({Type=Type, Name=Name, LoadRadius=LoadRadius, NearRadius=NearRadius})
|
||||
|
||||
-- Set cargo object.
|
||||
self.CargoObject = CargoUnit
|
||||
|
||||
self:T( self.ClassName )
|
||||
|
||||
-- Set event prio.
|
||||
self:SetEventPriority( 5 )
|
||||
|
||||
return self
|
||||
@@ -100,7 +103,12 @@ do -- CARGO_UNIT
|
||||
|
||||
-- Respawn the group...
|
||||
if self.CargoObject then
|
||||
self.CargoObject:ReSpawnAt( FromPointVec2, CargoDeployHeading )
|
||||
if CargoCarrier:IsShip() then
|
||||
-- If CargoCarrier is a ship, we don't want to spawn the units in the water next to the boat. Use destination coord instead.
|
||||
self.CargoObject:ReSpawnAt( ToPointVec2, CargoDeployHeading )
|
||||
else
|
||||
self.CargoObject:ReSpawnAt( FromPointVec2, CargoDeployHeading )
|
||||
end
|
||||
self:F( { "CargoUnits:", self.CargoObject:GetGroup():GetName() } )
|
||||
self.CargoCarrier = nil
|
||||
|
||||
|
||||
@@ -26,8 +26,6 @@
|
||||
-- @module Core.Base
|
||||
-- @image Core_Base.JPG
|
||||
|
||||
|
||||
|
||||
local _TraceOnOff = true
|
||||
local _TraceLevel = 1
|
||||
local _TraceAll = false
|
||||
@@ -256,6 +254,8 @@ end
|
||||
-- @param #BASE Parent is the Parent class that the Child inherits from.
|
||||
-- @return #BASE Child
|
||||
function BASE:Inherit( Child, Parent )
|
||||
|
||||
-- Create child.
|
||||
local Child = routines.utils.deepCopy( Child )
|
||||
|
||||
if Child ~= nil then
|
||||
@@ -271,6 +271,7 @@ function BASE:Inherit( Child, Parent )
|
||||
|
||||
--Child:_SetDestructor()
|
||||
end
|
||||
|
||||
return Child
|
||||
end
|
||||
|
||||
@@ -433,7 +434,7 @@ do -- Event Handling
|
||||
-- reflecting the order of the classes subscribed to the Event to be processed.
|
||||
-- @param #BASE self
|
||||
-- @param #number EventPriority The @{Event} processing Priority.
|
||||
-- @return self
|
||||
-- @return #BASE self
|
||||
function BASE:SetEventPriority( EventPriority )
|
||||
self._.EventPriority = EventPriority
|
||||
end
|
||||
@@ -450,23 +451,23 @@ do -- Event Handling
|
||||
|
||||
--- Subscribe to a DCS Event.
|
||||
-- @param #BASE self
|
||||
-- @param Core.Event#EVENTS Event
|
||||
-- @param Core.Event#EVENTS EventID Event ID.
|
||||
-- @param #function EventFunction (optional) The function to be called when the event occurs for the unit.
|
||||
-- @return #BASE
|
||||
function BASE:HandleEvent( Event, EventFunction )
|
||||
function BASE:HandleEvent( EventID, EventFunction )
|
||||
|
||||
self:EventDispatcher():OnEventGeneric( EventFunction, self, Event )
|
||||
self:EventDispatcher():OnEventGeneric( EventFunction, self, EventID )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- UnSubscribe to a DCS event.
|
||||
-- @param #BASE self
|
||||
-- @param Core.Event#EVENTS Event
|
||||
-- @param Core.Event#EVENTS EventID Event ID.
|
||||
-- @return #BASE
|
||||
function BASE:UnHandleEvent( Event )
|
||||
function BASE:UnHandleEvent( EventID )
|
||||
|
||||
self:EventDispatcher():RemoveEvent( self, Event )
|
||||
self:EventDispatcher():RemoveEvent( self, EventID )
|
||||
|
||||
return self
|
||||
end
|
||||
@@ -675,6 +676,13 @@ do -- Event Handling
|
||||
-- @param #BASE self
|
||||
-- @param Core.Event#EVENTDATA EventData The EventData structure.
|
||||
|
||||
--- Occurs when a player enters a slot and takes control of an aircraft.
|
||||
-- **NOTE**: This is a workaround of a long standing DCS bug with the PLAYER_ENTER_UNIT event.
|
||||
-- initiator : The unit that is being taken control of.
|
||||
-- @function [parent=#BASE] OnEventPlayerEnterAircraft
|
||||
-- @param #BASE self
|
||||
-- @param Core.Event#EVENTDATA EventData The EventData structure.
|
||||
|
||||
end
|
||||
|
||||
|
||||
@@ -764,31 +772,48 @@ function BASE:CreateEventTakeoff( EventTime, Initiator )
|
||||
world.onEvent( Event )
|
||||
end
|
||||
|
||||
--- Creation of a `S_EVENT_PLAYER_ENTER_AIRCRAFT` event.
|
||||
-- @param #BASE self
|
||||
-- @param Wrapper.Unit#UNIT PlayerUnit The aircraft unit the player entered.
|
||||
function BASE:CreateEventPlayerEnterAircraft( PlayerUnit )
|
||||
self:F( { PlayerUnit } )
|
||||
|
||||
local Event = {
|
||||
id = EVENTS.PlayerEnterAircraft,
|
||||
time = timer.getTime(),
|
||||
initiator = PlayerUnit:GetDCSObject()
|
||||
}
|
||||
|
||||
world.onEvent(Event)
|
||||
end
|
||||
|
||||
-- TODO: Complete DCS#Event structure.
|
||||
--- The main event handling function... This function captures all events generated for the class.
|
||||
-- @param #BASE self
|
||||
-- @param DCS#Event event
|
||||
function BASE:onEvent(event)
|
||||
--self:F( { BaseEventCodes[event.id], event } )
|
||||
|
||||
if self then
|
||||
for EventID, EventObject in pairs( self.Events ) do
|
||||
|
||||
for EventID, EventObject in pairs(self.Events) do
|
||||
if EventObject.EventEnabled then
|
||||
--env.info( 'onEvent Table EventObject.Self = ' .. tostring(EventObject.Self) )
|
||||
--env.info( 'onEvent event.id = ' .. tostring(event.id) )
|
||||
--env.info( 'onEvent EventObject.Event = ' .. tostring(EventObject.Event) )
|
||||
|
||||
if event.id == EventObject.Event then
|
||||
|
||||
if self == EventObject.Self then
|
||||
|
||||
if event.initiator and event.initiator:isExist() then
|
||||
event.IniUnitName = event.initiator:getName()
|
||||
end
|
||||
|
||||
if event.target and event.target:isExist() then
|
||||
event.TgtUnitName = event.target:getName()
|
||||
end
|
||||
--self:T( { BaseEventCodes[event.id], event } )
|
||||
--EventObject.EventFunction( self, event )
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -192,6 +192,7 @@ world.event.S_EVENT_DELETE_ZONE = world.event.S_EVENT_MAX + 1003
|
||||
world.event.S_EVENT_NEW_ZONE_GOAL = world.event.S_EVENT_MAX + 1004
|
||||
world.event.S_EVENT_DELETE_ZONE_GOAL = world.event.S_EVENT_MAX + 1005
|
||||
world.event.S_EVENT_REMOVE_UNIT = world.event.S_EVENT_MAX + 1006
|
||||
world.event.S_EVENT_PLAYER_ENTER_AIRCRAFT = world.event.S_EVENT_MAX + 1007
|
||||
|
||||
|
||||
--- The different types of events supported by MOOSE.
|
||||
@@ -233,6 +234,7 @@ EVENTS = {
|
||||
NewZoneGoal = world.event.S_EVENT_NEW_ZONE_GOAL,
|
||||
DeleteZoneGoal = world.event.S_EVENT_DELETE_ZONE_GOAL,
|
||||
RemoveUnit = world.event.S_EVENT_REMOVE_UNIT,
|
||||
PlayerEnterAircraft = world.event.S_EVENT_PLAYER_ENTER_AIRCRAFT,
|
||||
-- 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,
|
||||
@@ -489,6 +491,11 @@ local _EVENTMETA = {
|
||||
Event = "OnEventRemoveUnit",
|
||||
Text = "S_EVENT_REMOVE_UNIT"
|
||||
},
|
||||
[EVENTS.PlayerEnterAircraft] = {
|
||||
Order = 1,
|
||||
Event = "OnEventPlayerEnterAircraft",
|
||||
Text = "S_EVENT_PLAYER_ENTER_AIRCRAFT"
|
||||
},
|
||||
-- Added with DCS 2.5.6
|
||||
[EVENTS.DetailedFailure] = {
|
||||
Order = 1,
|
||||
@@ -526,17 +533,21 @@ local _EVENTMETA = {
|
||||
-- @param #EVENT self
|
||||
-- @return #EVENT self
|
||||
function EVENT:New()
|
||||
|
||||
-- Inherit base.
|
||||
local self = BASE:Inherit( self, BASE:New() )
|
||||
self:F2()
|
||||
self.EventHandler = world.addEventHandler( self )
|
||||
|
||||
-- Add world event handler.
|
||||
self.EventHandler = world.addEventHandler(self)
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Initializes the Events structure for the event
|
||||
--- Initializes the Events structure for the event.
|
||||
-- @param #EVENT self
|
||||
-- @param DCS#world.event EventID
|
||||
-- @param Core.Base#BASE EventClass
|
||||
-- @param DCS#world.event EventID Event ID.
|
||||
-- @param Core.Base#BASE EventClass The class object for which events are handled.
|
||||
-- @return #EVENT.Events
|
||||
function EVENT:Init( EventID, EventClass )
|
||||
self:F3( { _EVENTMETA[EventID].Text, EventClass } )
|
||||
@@ -548,6 +559,7 @@ function EVENT:Init( EventID, EventClass )
|
||||
|
||||
-- Each event has a subtable of EventClasses, ordered by EventPriority.
|
||||
local EventPriority = EventClass:GetEventPriority()
|
||||
|
||||
if not self.Events[EventID][EventPriority] then
|
||||
self.Events[EventID][EventPriority] = setmetatable( {}, { __mode = "k" } )
|
||||
end
|
||||
@@ -555,40 +567,45 @@ 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
|
||||
|
||||
--- Removes a subscription
|
||||
-- @param #EVENT self
|
||||
-- @param Core.Base#BASE EventClass The self instance of the class for which the event is.
|
||||
-- @param DCS#world.event EventID
|
||||
-- @return #EVENT.Events
|
||||
-- @param DCS#world.event EventID Event ID.
|
||||
-- @return #EVENT self
|
||||
function EVENT:RemoveEvent( EventClass, EventID )
|
||||
|
||||
-- Debug info.
|
||||
self:F2( { "Removing subscription for class: ", EventClass:GetClassNameAndID() } )
|
||||
|
||||
-- Get event prio.
|
||||
local EventPriority = EventClass:GetEventPriority()
|
||||
|
||||
-- Events.
|
||||
self.Events = self.Events or {}
|
||||
self.Events[EventID] = self.Events[EventID] or {}
|
||||
self.Events[EventID][EventPriority] = self.Events[EventID][EventPriority] or {}
|
||||
|
||||
-- Remove
|
||||
self.Events[EventID][EventPriority][EventClass] = nil
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Resets subscriptions
|
||||
--- Resets subscriptions.
|
||||
-- @param #EVENT self
|
||||
-- @param Core.Base#BASE EventClass The self instance of the class for which the event is.
|
||||
-- @param DCS#world.event EventID
|
||||
-- @param DCS#world.event EventID Event ID.
|
||||
-- @return #EVENT.Events
|
||||
function EVENT:Reset( EventObject ) --R2.1
|
||||
|
||||
self:F( { "Resetting subscriptions for class: ", EventObject:GetClassNameAndID() } )
|
||||
|
||||
local EventPriority = EventObject:GetEventPriority()
|
||||
|
||||
for EventID, EventData in pairs( self.Events ) do
|
||||
if self.EventsDead then
|
||||
if self.EventsDead[EventID] then
|
||||
@@ -603,19 +620,22 @@ function EVENT:Reset( EventObject ) --R2.1
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
--- Clears all event subscriptions for a @{Core.Base#BASE} derived object.
|
||||
-- @param #EVENT self
|
||||
-- @param Core.Base#BASE EventObject
|
||||
function EVENT:RemoveAll( EventObject )
|
||||
self:F3( { EventObject:GetClassNameAndID() } )
|
||||
-- @param Core.Base#BASE EventClass The self class object for which the events are removed.
|
||||
-- @return #EVENT self
|
||||
function EVENT:RemoveAll(EventClass)
|
||||
|
||||
local EventClass = EventObject:GetClassNameAndID()
|
||||
local EventClassName = EventClass:GetClassNameAndID()
|
||||
|
||||
-- Get Event prio.
|
||||
local EventPriority = EventClass:GetEventPriority()
|
||||
|
||||
for EventID, EventData in pairs( self.Events ) do
|
||||
self.Events[EventID][EventPriority][EventClass] = nil
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
@@ -626,7 +646,7 @@ end
|
||||
-- @param #function EventFunction The function to be called when the event occurs for the unit.
|
||||
-- @param EventClass The instance of the class for which the event is.
|
||||
-- @param #function OnEventFunction
|
||||
-- @return #EVENT
|
||||
-- @return #EVENT self
|
||||
function EVENT:OnEventForTemplate( EventTemplate, EventFunction, EventClass, EventID )
|
||||
self:F2( EventTemplate.name )
|
||||
|
||||
@@ -636,7 +656,7 @@ function EVENT:OnEventForTemplate( EventTemplate, EventFunction, EventClass, Eve
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set a new listener for an S_EVENT_X event independent from a unit or a weapon.
|
||||
--- Set a new listener for an `S_EVENT_X` event independent from a unit or a weapon.
|
||||
-- @param #EVENT self
|
||||
-- @param #function EventFunction The function to be called when the event occurs for the unit.
|
||||
-- @param Core.Base#BASE EventClass The self instance of the class for which the event is captured. When the event happens, the event process will be called in this class provided.
|
||||
@@ -652,13 +672,13 @@ function EVENT:OnEventGeneric( EventFunction, EventClass, EventID )
|
||||
end
|
||||
|
||||
|
||||
--- Set a new listener for an S_EVENT_X event for a UNIT.
|
||||
--- Set a new listener for an `S_EVENT_X` event for a UNIT.
|
||||
-- @param #EVENT self
|
||||
-- @param #string UnitName The name of the UNIT.
|
||||
-- @param #function EventFunction The function to be called when the event occurs for the GROUP.
|
||||
-- @param Core.Base#BASE EventClass The self instance of the class for which the event is.
|
||||
-- @param EventID
|
||||
-- @return #EVENT
|
||||
-- @return #EVENT self
|
||||
function EVENT:OnEventForUnit( UnitName, EventFunction, EventClass, EventID )
|
||||
self:F2( UnitName )
|
||||
|
||||
@@ -673,8 +693,9 @@ end
|
||||
-- @param #string GroupName The name of the GROUP.
|
||||
-- @param #function EventFunction The function to be called when the event occurs for the GROUP.
|
||||
-- @param Core.Base#BASE EventClass The self instance of the class for which the event is.
|
||||
-- @param EventID
|
||||
-- @return #EVENT
|
||||
-- @param #number EventID Event ID.
|
||||
-- @param ... Optional arguments passed to the event function.
|
||||
-- @return #EVENT self
|
||||
function EVENT:OnEventForGroup( GroupName, EventFunction, EventClass, EventID, ... )
|
||||
|
||||
local Event = self:Init( EventID, EventClass )
|
||||
@@ -691,7 +712,7 @@ do -- OnBirth
|
||||
-- @param Wrapper.Group#GROUP EventGroup
|
||||
-- @param #function EventFunction The function to be called when the event occurs for the unit.
|
||||
-- @param EventClass The self instance of the class for which the event is.
|
||||
-- @return #EVENT
|
||||
-- @return #EVENT self
|
||||
function EVENT:OnBirthForTemplate( EventTemplate, EventFunction, EventClass )
|
||||
self:F2( EventTemplate.name )
|
||||
|
||||
@@ -724,10 +745,10 @@ do -- OnDead
|
||||
|
||||
--- Create an OnDead event handler for a group
|
||||
-- @param #EVENT self
|
||||
-- @param Wrapper.Group#GROUP EventGroup
|
||||
-- @param Wrapper.Group#GROUP EventGroup The GROUP object.
|
||||
-- @param #function EventFunction The function to be called when the event occurs for the unit.
|
||||
-- @param EventClass The self instance of the class for which the event is.
|
||||
-- @return #EVENT
|
||||
-- @param #table EventClass The self instance of the class for which the event is.
|
||||
-- @return #EVENT self
|
||||
function EVENT:OnDeadForTemplate( EventTemplate, EventFunction, EventClass )
|
||||
self:F2( EventTemplate.name )
|
||||
|
||||
@@ -740,12 +761,13 @@ end
|
||||
|
||||
|
||||
do -- OnLand
|
||||
|
||||
--- Create an OnLand event handler for a group
|
||||
-- @param #EVENT self
|
||||
-- @param #table EventTemplate
|
||||
-- @param #function EventFunction The function to be called when the event occurs for the unit.
|
||||
-- @param EventClass The self instance of the class for which the event is.
|
||||
-- @return #EVENT
|
||||
-- @param #table EventClass The self instance of the class for which the event is.
|
||||
-- @return #EVENT self
|
||||
function EVENT:OnLandForTemplate( EventTemplate, EventFunction, EventClass )
|
||||
self:F2( EventTemplate.name )
|
||||
|
||||
@@ -757,12 +779,13 @@ do -- OnLand
|
||||
end
|
||||
|
||||
do -- OnTakeOff
|
||||
|
||||
--- Create an OnTakeOff event handler for a group
|
||||
-- @param #EVENT self
|
||||
-- @param #table EventTemplate
|
||||
-- @param #table EventTemplate Template table.
|
||||
-- @param #function EventFunction The function to be called when the event occurs for the unit.
|
||||
-- @param EventClass The self instance of the class for which the event is.
|
||||
-- @return #EVENT
|
||||
-- @param #table EventClass The self instance of the class for which the event is.
|
||||
-- @return #EVENT self
|
||||
function EVENT:OnTakeOffForTemplate( EventTemplate, EventFunction, EventClass )
|
||||
self:F2( EventTemplate.name )
|
||||
|
||||
@@ -797,7 +820,7 @@ do -- Event Creation
|
||||
-- @param #EVENT self
|
||||
-- @param AI.AI_Cargo#AI_CARGO Cargo The Cargo created.
|
||||
function EVENT:CreateEventNewCargo( Cargo )
|
||||
self:I( { Cargo } )
|
||||
self:F( { Cargo } )
|
||||
|
||||
local Event = {
|
||||
id = EVENTS.NewCargo,
|
||||
@@ -899,11 +922,27 @@ do -- Event Creation
|
||||
|
||||
world.onEvent( Event )
|
||||
end
|
||||
|
||||
--- Creation of a S_EVENT_PLAYER_ENTER_AIRCRAFT event.
|
||||
-- @param #EVENT self
|
||||
-- @param Wrapper.Unit#UNIT PlayerUnit The aircraft unit the player entered.
|
||||
function EVENT:CreateEventPlayerEnterAircraft( PlayerUnit )
|
||||
self:F( { PlayerUnit } )
|
||||
|
||||
local Event = {
|
||||
id = EVENTS.PlayerEnterAircraft,
|
||||
time = timer.getTime(),
|
||||
initiator = PlayerUnit:GetDCSObject()
|
||||
}
|
||||
|
||||
world.onEvent( Event )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- @param #EVENT self
|
||||
-- @param #EVENTDATA Event
|
||||
--- Main event function.
|
||||
-- @param #EVENT self
|
||||
-- @param #EVENTDATA Event Event data table.
|
||||
function EVENT:onEvent( Event )
|
||||
|
||||
local ErrorHandler = function( errmsg )
|
||||
@@ -1001,6 +1040,16 @@ function EVENT:onEvent( Event )
|
||||
Event.IniCategory = Event.IniDCSUnit:getDesc().category
|
||||
Event.IniTypeName = Event.initiator:isExist() and Event.IniDCSUnit:getTypeName() or "SCENERY" -- TODO: Bug fix for 2.1!
|
||||
end
|
||||
|
||||
if Event.IniObjectCategory == Object.Category.BASE then
|
||||
Event.IniDCSUnit = Event.initiator
|
||||
Event.IniDCSUnitName = Event.IniDCSUnit:getName()
|
||||
Event.IniUnitName = Event.IniDCSUnitName
|
||||
Event.IniUnit = AIRBASE:FindByName(Event.IniDCSUnitName)
|
||||
Event.IniCoalition = Event.IniDCSUnit:getCoalition()
|
||||
Event.IniCategory = Event.IniDCSUnit:getDesc().category
|
||||
Event.IniTypeName = Event.IniDCSUnit:getTypeName()
|
||||
end
|
||||
end
|
||||
|
||||
if Event.target then
|
||||
@@ -1277,7 +1326,7 @@ function EVENT:onEvent( Event )
|
||||
Event = nil
|
||||
end
|
||||
|
||||
--- The EVENTHANDLER structure
|
||||
--- The EVENTHANDLER structure.
|
||||
-- @type EVENTHANDLER
|
||||
-- @extends Core.Base#BASE
|
||||
EVENTHANDLER = {
|
||||
@@ -1285,7 +1334,7 @@ EVENTHANDLER = {
|
||||
ClassID = 0,
|
||||
}
|
||||
|
||||
--- The EVENTHANDLER constructor
|
||||
--- The EVENTHANDLER constructor.
|
||||
-- @param #EVENTHANDLER self
|
||||
-- @return #EVENTHANDLER self
|
||||
function EVENTHANDLER:New()
|
||||
|
||||
@@ -71,7 +71,7 @@
|
||||
--
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
-- ### Contributions:
|
||||
-- ### Contributions: **funkyfranky**
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
@@ -81,6 +81,12 @@
|
||||
do -- FSM
|
||||
|
||||
--- @type FSM
|
||||
-- @field #string ClassName Name of the class.
|
||||
-- @field Core.Scheduler#SCHEDULER CallScheduler Call scheduler.
|
||||
-- @field #table options Options.
|
||||
-- @field #table subs Subs.
|
||||
-- @field #table Scores Scores.
|
||||
-- @field #string current Current state name.
|
||||
-- @extends Core.Base#BASE
|
||||
|
||||
|
||||
@@ -369,8 +375,7 @@ do -- FSM
|
||||
self._EventSchedules = {}
|
||||
|
||||
self.CallScheduler = SCHEDULER:New( self )
|
||||
|
||||
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -379,7 +384,6 @@ do -- FSM
|
||||
-- @param #FSM self
|
||||
-- @param #string State A string defining the start state.
|
||||
function FSM:SetStartState( State )
|
||||
|
||||
self._StartState = State
|
||||
self.current = State
|
||||
end
|
||||
@@ -389,7 +393,6 @@ do -- FSM
|
||||
-- @param #FSM self
|
||||
-- @return #string A string containing the start state.
|
||||
function FSM:GetStartState()
|
||||
|
||||
return self._StartState or {}
|
||||
end
|
||||
|
||||
@@ -406,6 +409,7 @@ do -- FSM
|
||||
Transition.Event = Event
|
||||
Transition.To = To
|
||||
|
||||
-- Debug message.
|
||||
self:T2( Transition )
|
||||
|
||||
self._Transitions[Transition] = Transition
|
||||
@@ -414,9 +418,9 @@ do -- FSM
|
||||
|
||||
|
||||
--- Returns a table of the transition rules defined within the FSM.
|
||||
-- @return #table
|
||||
function FSM:GetTransitions()
|
||||
|
||||
-- @param #FSM self
|
||||
-- @return #table Transitions.
|
||||
function FSM:GetTransitions()
|
||||
return self._Transitions or {}
|
||||
end
|
||||
|
||||
@@ -448,7 +452,8 @@ do -- FSM
|
||||
|
||||
|
||||
--- Returns a table of the SubFSM rules defined within the FSM.
|
||||
-- @return #table
|
||||
-- @param #FSM self
|
||||
-- @return #table Sub processes.
|
||||
function FSM:GetProcesses()
|
||||
|
||||
self:F( { Processes = self._Processes } )
|
||||
@@ -480,15 +485,17 @@ do -- FSM
|
||||
end
|
||||
|
||||
--- Adds an End state.
|
||||
function FSM:AddEndState( State )
|
||||
|
||||
-- @param #FSM self
|
||||
-- @param #string State The FSM state.
|
||||
function FSM:AddEndState( State )
|
||||
self._EndStates[State] = State
|
||||
self.endstates[State] = State
|
||||
end
|
||||
|
||||
--- Returns the End states.
|
||||
function FSM:GetEndStates()
|
||||
|
||||
-- @param #FSM self
|
||||
-- @return #table End states.
|
||||
function FSM:GetEndStates()
|
||||
return self._EndStates or {}
|
||||
end
|
||||
|
||||
@@ -532,18 +539,22 @@ do -- FSM
|
||||
end
|
||||
|
||||
--- Returns a table with the scores defined.
|
||||
function FSM:GetScores()
|
||||
|
||||
-- @param #FSM self
|
||||
-- @param #table Scores.
|
||||
function FSM:GetScores()
|
||||
return self._Scores or {}
|
||||
end
|
||||
|
||||
--- Returns a table with the Subs defined.
|
||||
function FSM:GetSubs()
|
||||
|
||||
-- @param #FSM self
|
||||
-- @return #table Sub processes.
|
||||
function FSM:GetSubs()
|
||||
return self.options.subs
|
||||
end
|
||||
|
||||
|
||||
--- Load call backs.
|
||||
-- @param #FSM self
|
||||
-- @param #table CallBackTable Table of call backs.
|
||||
function FSM:LoadCallBacks( CallBackTable )
|
||||
|
||||
for name, callback in pairs( CallBackTable or {} ) do
|
||||
@@ -551,21 +562,34 @@ do -- FSM
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
--- Event map.
|
||||
-- @param #FSM self
|
||||
-- @param #table Events Events.
|
||||
-- @param #table EventStructure Event structure.
|
||||
function FSM:_eventmap( Events, EventStructure )
|
||||
|
||||
local Event = EventStructure.Event
|
||||
local __Event = "__" .. EventStructure.Event
|
||||
|
||||
self[Event] = self[Event] or self:_create_transition(Event)
|
||||
self[__Event] = self[__Event] or self:_delayed_transition(Event)
|
||||
|
||||
-- Debug message.
|
||||
self:T2( "Added methods: " .. Event .. ", " .. __Event )
|
||||
|
||||
Events[Event] = self.Events[Event] or { map = {} }
|
||||
self:_add_to_map( Events[Event].map, EventStructure )
|
||||
|
||||
end
|
||||
|
||||
|
||||
--- Sub maps.
|
||||
-- @param #FSM self
|
||||
-- @param #table subs Subs.
|
||||
-- @param #table sub Sub.
|
||||
-- @param #string name Name.
|
||||
function FSM:_submap( subs, sub, name )
|
||||
--self:F( { sub = sub, name = name } )
|
||||
|
||||
subs[sub.From] = subs[sub.From] or {}
|
||||
subs[sub.From][sub.Event] = subs[sub.From][sub.Event] or {}
|
||||
|
||||
@@ -578,22 +602,24 @@ do -- FSM
|
||||
subs[sub.From][sub.Event][sub].ReturnEvents = sub.ReturnEvents or {} -- these events need to be given to find the correct continue event ... if none given, the processing will stop.
|
||||
subs[sub.From][sub.Event][sub].name = name
|
||||
subs[sub.From][sub.Event][sub].fsmparent = self
|
||||
|
||||
end
|
||||
|
||||
|
||||
--- Call handler.
|
||||
-- @param #FSM self
|
||||
-- @param #string step Step "onafter", "onbefore", "onenter", "onleave".
|
||||
-- @param #string trigger Trigger.
|
||||
-- @param #table params Parameters.
|
||||
-- @param #string EventName Event name.
|
||||
-- @return Value.
|
||||
function FSM:_call_handler( step, trigger, params, EventName )
|
||||
--env.info(string.format("FF T=%.3f _call_handler step=%s, trigger=%s, event=%s", timer.getTime(), step, trigger, EventName))
|
||||
|
||||
local handler = step .. trigger
|
||||
local ErrorHandler = function( errmsg )
|
||||
|
||||
env.info( "Error in SCHEDULER function:" .. errmsg )
|
||||
if BASE.Debug ~= nil then
|
||||
env.info( BASE.Debug.traceback() )
|
||||
end
|
||||
|
||||
return errmsg
|
||||
end
|
||||
|
||||
if self[handler] then
|
||||
|
||||
--[[
|
||||
if step == "onafter" or step == "OnAfter" then
|
||||
self:T( ":::>" .. step .. params[2] .. " : " .. params[1] .. " >> " .. params[2] .. ">" .. step .. params[2] .. "()" .. " >> " .. params[3] )
|
||||
elseif step == "onbefore" or step == "OnBefore" then
|
||||
@@ -604,14 +630,33 @@ do -- FSM
|
||||
self:T( ":::>" .. step .. params[1] .. " : " .. params[1] .. ">" .. step .. params[1] .. "()" .. " >> " .. params[2] .. " >> " .. params[3] )
|
||||
else
|
||||
self:T( ":::>" .. step .. " : " .. params[1] .. " >> " .. params[2] .. " >> " .. params[3] )
|
||||
end
|
||||
end
|
||||
]]
|
||||
|
||||
self._EventSchedules[EventName] = nil
|
||||
local Result, Value = xpcall( function() return self[handler]( self, unpack( params ) ) end, ErrorHandler )
|
||||
return Value
|
||||
|
||||
-- Error handler.
|
||||
local ErrorHandler = function( errmsg )
|
||||
env.info( "Error in SCHEDULER function:" .. errmsg )
|
||||
if BASE.Debug ~= nil then
|
||||
env.info( BASE.Debug.traceback() )
|
||||
end
|
||||
return errmsg
|
||||
end
|
||||
|
||||
--return self[handler](self, unpack( params ))
|
||||
|
||||
-- Protected call.
|
||||
local Result, Value = xpcall( function() return self[handler]( self, unpack( params ) ) end, ErrorHandler )
|
||||
return Value
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- @param #FSM self
|
||||
--- Handler.
|
||||
-- @param #FSM self
|
||||
-- @param #string EventName Event name.
|
||||
-- @param ... Arguments.
|
||||
function FSM._handler( self, EventName, ... )
|
||||
|
||||
local Can, To = self:can( EventName )
|
||||
@@ -621,7 +666,11 @@ do -- FSM
|
||||
end
|
||||
|
||||
if Can then
|
||||
|
||||
-- From state.
|
||||
local From = self.current
|
||||
|
||||
-- Parameters.
|
||||
local Params = { From, EventName, To, ... }
|
||||
|
||||
|
||||
@@ -632,8 +681,8 @@ do -- FSM
|
||||
self["onafter".. EventName] or
|
||||
self["OnAfter".. EventName] or
|
||||
self["onenter".. To] or
|
||||
self["OnEnter".. To]
|
||||
then
|
||||
self["OnEnter".. To] then
|
||||
|
||||
if self:_call_handler( "onbefore", EventName, Params, EventName ) == false then
|
||||
self:T( "*** FSM *** Cancel" .. " *** " .. self.current .. " --> " .. EventName .. " --> " .. To .. " *** onbefore" .. EventName )
|
||||
return false
|
||||
@@ -653,8 +702,11 @@ do -- FSM
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
else
|
||||
|
||||
local ClassName = self:GetClassName()
|
||||
|
||||
if ClassName == "FSM" then
|
||||
self:T( "*** FSM *** Transit *** " .. self.current .. " --> " .. EventName .. " --> " .. To )
|
||||
end
|
||||
@@ -672,46 +724,56 @@ do -- FSM
|
||||
end
|
||||
end
|
||||
|
||||
-- New current state.
|
||||
self.current = To
|
||||
|
||||
local execute = true
|
||||
|
||||
local subtable = self:_gosub( From, EventName )
|
||||
|
||||
for _, sub in pairs( subtable ) do
|
||||
|
||||
--if sub.nextevent then
|
||||
-- self:F2( "nextevent = " .. sub.nextevent )
|
||||
-- self[sub.nextevent]( self )
|
||||
--end
|
||||
|
||||
self:T( "*** FSM *** Sub *** " .. sub.StartEvent )
|
||||
|
||||
sub.fsm.fsmparent = self
|
||||
sub.fsm.ReturnEvents = sub.ReturnEvents
|
||||
sub.fsm[sub.StartEvent]( sub.fsm )
|
||||
|
||||
execute = false
|
||||
end
|
||||
|
||||
local fsmparent, Event = self:_isendstate( To )
|
||||
|
||||
if fsmparent and Event then
|
||||
|
||||
self:T( "*** FSM *** End *** " .. Event )
|
||||
|
||||
self:_call_handler("onenter", To, Params, EventName )
|
||||
self:_call_handler("OnEnter", To, Params, EventName )
|
||||
self:_call_handler("onafter", EventName, Params, EventName )
|
||||
self:_call_handler("OnAfter", EventName, Params, EventName )
|
||||
self:_call_handler("onstate", "change", Params, EventName )
|
||||
|
||||
fsmparent[Event]( fsmparent )
|
||||
|
||||
execute = false
|
||||
end
|
||||
|
||||
if execute then
|
||||
self:_call_handler("onafter", EventName, Params, EventName )
|
||||
self:_call_handler("OnAfter", EventName, Params, EventName )
|
||||
|
||||
-- only execute the call if the From state is not equal to the To state! Otherwise this function should never execute!
|
||||
--if from ~= to then
|
||||
self:_call_handler("onenter", To, Params, EventName )
|
||||
self:_call_handler("OnEnter", To, Params, EventName )
|
||||
--end
|
||||
self:_call_handler("onafter", EventName, Params, EventName )
|
||||
self:_call_handler("OnAfter", EventName, Params, EventName )
|
||||
|
||||
self:_call_handler("onenter", To, Params, EventName )
|
||||
self:_call_handler("OnEnter", To, Params, EventName )
|
||||
|
||||
self:_call_handler("onstate", "change", Params, EventName )
|
||||
|
||||
end
|
||||
else
|
||||
self:T( "*** FSM *** NO Transition *** " .. self.current .. " --> " .. EventName .. " --> ? " )
|
||||
@@ -719,37 +781,68 @@ do -- FSM
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
--- Delayed transition.
|
||||
-- @param #FSM self
|
||||
-- @param #string EventName Event name.
|
||||
-- @return #function Function.
|
||||
function FSM:_delayed_transition( EventName )
|
||||
|
||||
return function( self, DelaySeconds, ... )
|
||||
|
||||
-- Debug.
|
||||
self:T2( "Delayed Event: " .. EventName )
|
||||
|
||||
local CallID = 0
|
||||
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
|
||||
|
||||
-- Call _handler.
|
||||
CallID = self.CallScheduler:Schedule( self, self._handler, { EventName, ... }, DelaySeconds or 1, nil, nil, nil, 4, true )
|
||||
|
||||
-- Set call ID.
|
||||
self._EventSchedules[EventName] = CallID
|
||||
|
||||
-- Debug output.
|
||||
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." )
|
||||
end
|
||||
|
||||
-- Debug.
|
||||
self:T2( { CallID = CallID } )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
--- Create transition.
|
||||
-- @param #FSM self
|
||||
-- @param #string EventName Event name.
|
||||
-- @return #function Function.
|
||||
function FSM:_create_transition( EventName )
|
||||
return function( self, ... ) return self._handler( self, EventName , ... ) end
|
||||
end
|
||||
|
||||
|
||||
--- Go sub.
|
||||
-- @param #FSM self
|
||||
-- @param #string ParentFrom Parent from state.
|
||||
-- @param #string ParentEvent Parent event name.
|
||||
-- @return #table Subs.
|
||||
function FSM:_gosub( ParentFrom, ParentEvent )
|
||||
local fsmtable = {}
|
||||
if self.subs[ParentFrom] and self.subs[ParentFrom][ParentEvent] then
|
||||
@@ -759,9 +852,15 @@ do -- FSM
|
||||
return {}
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--- Is end state.
|
||||
-- @param #FSM self
|
||||
-- @param #string Current Current state name.
|
||||
-- @return #table FSM parent.
|
||||
-- @return #string Event name.
|
||||
function FSM:_isendstate( Current )
|
||||
local FSMParent = self.fsmparent
|
||||
|
||||
if FSMParent and self.endstates[Current] then
|
||||
--self:T( { state = Current, endstates = self.endstates, endstate = self.endstates[Current] } )
|
||||
FSMParent.current = Current
|
||||
@@ -778,9 +877,14 @@ do -- FSM
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
--- Add to map.
|
||||
-- @param #FSM self
|
||||
-- @param #table Map Map.
|
||||
-- @param #table Event Event table.
|
||||
function FSM:_add_to_map( Map, Event )
|
||||
self:F3( { Map, Event } )
|
||||
|
||||
if type(Event.From) == 'string' then
|
||||
Map[Event.From] = Event.To
|
||||
else
|
||||
@@ -788,33 +892,60 @@ do -- FSM
|
||||
Map[From] = Event.To
|
||||
end
|
||||
end
|
||||
|
||||
self:T3( { Map, Event } )
|
||||
end
|
||||
|
||||
|
||||
--- Get current state.
|
||||
-- @param #FSM self
|
||||
-- @return #string Current FSM state.
|
||||
function FSM:GetState()
|
||||
return self.current
|
||||
end
|
||||
|
||||
|
||||
--- Get current state.
|
||||
-- @param #FSM self
|
||||
-- @return #string Current FSM state.
|
||||
function FSM:GetCurrentState()
|
||||
return self.current
|
||||
end
|
||||
|
||||
|
||||
--- Check if FSM is in state.
|
||||
-- @param #FSM self
|
||||
-- @param #string State State name.
|
||||
-- @param #boolean If true, FSM is in this state.
|
||||
function FSM:Is( State )
|
||||
return self.current == State
|
||||
end
|
||||
|
||||
|
||||
--- Check if FSM is in state.
|
||||
-- @param #FSM self
|
||||
-- @param #string State State name.
|
||||
-- @param #boolean If true, FSM is in this state.
|
||||
function FSM:is(state)
|
||||
return self.current == state
|
||||
end
|
||||
|
||||
|
||||
--- Check if can do an event.
|
||||
-- @param #FSM self
|
||||
-- @param #string e Event name.
|
||||
-- @return #boolean If true, FSM can do the event.
|
||||
-- @return #string To state.
|
||||
function FSM:can(e)
|
||||
|
||||
local Event = self.Events[e]
|
||||
self:F3( { self.current, Event } )
|
||||
|
||||
--self:F3( { self.current, Event } )
|
||||
|
||||
local To = Event and Event.map[self.current] or Event.map['*']
|
||||
|
||||
return To ~= nil, To
|
||||
end
|
||||
|
||||
|
||||
--- Check if cannot do an event.
|
||||
-- @param #FSM self
|
||||
-- @param #string e Event name.
|
||||
-- @return #boolean If true, FSM cannot do the event.
|
||||
function FSM:cannot(e)
|
||||
return not self:can(e)
|
||||
end
|
||||
|
||||
@@ -175,10 +175,6 @@ do -- COORDINATE
|
||||
-- In order to use the most optimal road system to transport vehicles, the method @{#COORDINATE.GetPathOnRoad}() will calculate
|
||||
-- the most optimal path following the road between two coordinates.
|
||||
--
|
||||
--
|
||||
--
|
||||
--
|
||||
--
|
||||
-- ## 8) Metric or imperial system
|
||||
--
|
||||
-- * @{#COORDINATE.IsMetric}(): Returns if the 3D point is Metric or Nautical Miles.
|
||||
@@ -204,23 +200,23 @@ do -- COORDINATE
|
||||
|
||||
--- @field COORDINATE.WaypointAction
|
||||
COORDINATE.WaypointAction = {
|
||||
TurningPoint = "Turning Point",
|
||||
FlyoverPoint = "Fly Over Point",
|
||||
FromParkingArea = "From Parking Area",
|
||||
TurningPoint = "Turning Point",
|
||||
FlyoverPoint = "Fly Over Point",
|
||||
FromParkingArea = "From Parking Area",
|
||||
FromParkingAreaHot = "From Parking Area Hot",
|
||||
FromRunway = "From Runway",
|
||||
Landing = "Landing",
|
||||
LandingReFuAr = "LandingReFuAr",
|
||||
FromRunway = "From Runway",
|
||||
Landing = "Landing",
|
||||
LandingReFuAr = "LandingReFuAr",
|
||||
}
|
||||
|
||||
--- @field COORDINATE.WaypointType
|
||||
COORDINATE.WaypointType = {
|
||||
TakeOffParking = "TakeOffParking",
|
||||
TakeOffParking = "TakeOffParking",
|
||||
TakeOffParkingHot = "TakeOffParkingHot",
|
||||
TakeOff = "TakeOffParkingHot",
|
||||
TurningPoint = "Turning Point",
|
||||
Land = "Land",
|
||||
LandingReFuAr = "LandingReFuAr",
|
||||
TakeOff = "TakeOffParkingHot",
|
||||
TurningPoint = "Turning Point",
|
||||
Land = "Land",
|
||||
LandingReFuAr = "LandingReFuAr",
|
||||
}
|
||||
|
||||
|
||||
@@ -232,6 +228,7 @@ do -- COORDINATE
|
||||
-- @return #COORDINATE
|
||||
function COORDINATE:New( x, y, z )
|
||||
|
||||
--env.info("FF COORDINATE New")
|
||||
local self = BASE:Inherit( self, BASE:New() ) -- #COORDINATE
|
||||
self.x = x
|
||||
self.y = y
|
||||
@@ -303,6 +300,45 @@ do -- COORDINATE
|
||||
return { x = self.x, y = self.z }
|
||||
end
|
||||
|
||||
--- Update x,y,z coordinates from a given 3D vector.
|
||||
-- @param #COORDINATE self
|
||||
-- @param DCS#Vec3 Vec3 The 3D vector with x,y,z components.
|
||||
-- @return #COORDINATE The modified COORDINATE itself.
|
||||
function COORDINATE:UpdateFromVec3(Vec3)
|
||||
|
||||
self.x=Vec3.x
|
||||
self.y=Vec3.y
|
||||
self.z=Vec3.z
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Update x,y,z coordinates from another given COORDINATE.
|
||||
-- @param #COORDINATE self
|
||||
-- @param #COORDINATE Coordinate The coordinate with the new x,y,z positions.
|
||||
-- @return #COORDINATE The modified COORDINATE itself.
|
||||
function COORDINATE:UpdateFromCoordinate(Coordinate)
|
||||
|
||||
self.x=Coordinate.x
|
||||
self.y=Coordinate.y
|
||||
self.z=Coordinate.z
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Update x and z coordinates from a given 2D vector.
|
||||
-- @param #COORDINATE self
|
||||
-- @param DCS#Vec2 Vec2 The 2D vector with x,y components. x is overwriting COORDINATE.x while y is overwriting COORDINATE.z.
|
||||
-- @return #COORDINATE The modified COORDINATE itself.
|
||||
function COORDINATE:UpdateFromVec2(Vec2)
|
||||
|
||||
self.x=Vec2.x
|
||||
self.z=Vec2.y
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Returns the coordinate from the latitude and longitude given in decimal degrees.
|
||||
-- @param #COORDINATE self
|
||||
-- @param #number latitude Latitude in decimal degrees.
|
||||
@@ -506,19 +542,29 @@ do -- COORDINATE
|
||||
-- @param DCS#Distance Distance The Distance to be added in meters.
|
||||
-- @param DCS#Angle Angle The Angle in degrees. Defaults to 0 if not specified (nil).
|
||||
-- @param #boolean Keepalt If true, keep altitude of original coordinate. Default is that the new coordinate is created at the translated land height.
|
||||
-- @return Core.Point#COORDINATE The new calculated COORDINATE.
|
||||
function COORDINATE:Translate( Distance, Angle, Keepalt )
|
||||
local SX = self.x
|
||||
local SY = self.z
|
||||
local Radians = (Angle or 0) / 180 * math.pi
|
||||
local TX = Distance * math.cos( Radians ) + SX
|
||||
local TY = Distance * math.sin( Radians ) + SY
|
||||
|
||||
if Keepalt then
|
||||
return COORDINATE:NewFromVec3( { x = TX, y=self.y, z = TY } )
|
||||
-- @param #boolean Overwrite If true, overwrite the original COORDINATE with the translated one. Otherwise, create a new COODINATE.
|
||||
-- @return #COORDINATE The new calculated COORDINATE.
|
||||
function COORDINATE:Translate( Distance, Angle, Keepalt, Overwrite )
|
||||
|
||||
-- Angle in rad.
|
||||
local alpha = math.rad((Angle or 0))
|
||||
|
||||
local x = Distance * math.cos(alpha) + self.x -- New x
|
||||
local z = Distance * math.sin(alpha) + self.z -- New z
|
||||
|
||||
local y=Keepalt and self.y or land.getHeight({x=x, y=z})
|
||||
|
||||
if Overwrite then
|
||||
self.x=x
|
||||
self.y=y
|
||||
self.z=z
|
||||
return self
|
||||
else
|
||||
return COORDINATE:NewFromVec2( { x = TX, y = TY } )
|
||||
--env.info("FF translate with NEW coordinate T="..timer.getTime())
|
||||
local coord=COORDINATE:New(x, y, z)
|
||||
return coord
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- Rotate coordinate in 2D (x,z) space.
|
||||
@@ -716,17 +762,22 @@ do -- COORDINATE
|
||||
-- Move the vector to start at the end of A.
|
||||
vec=UTILS.VecAdd(self, vec)
|
||||
|
||||
return self:New(vec.x,vec.y,vec.z)
|
||||
local coord=COORDINATE:New(vec.x,vec.y,vec.z)
|
||||
return coord
|
||||
end
|
||||
|
||||
--- Return the 2D distance in meters between the target COORDINATE and the COORDINATE.
|
||||
-- @param #COORDINATE self
|
||||
-- @param #COORDINATE TargetCoordinate The target COORDINATE.
|
||||
-- @param #COORDINATE TargetCoordinate The target COORDINATE. Can also be a DCS#Vec3.
|
||||
-- @return DCS#Distance Distance The distance in meters.
|
||||
function COORDINATE:Get2DDistance( TargetCoordinate )
|
||||
local TargetVec3 = TargetCoordinate:GetVec3()
|
||||
local SourceVec3 = self:GetVec3()
|
||||
return ( ( TargetVec3.x - SourceVec3.x ) ^ 2 + ( TargetVec3.z - SourceVec3.z ) ^ 2 ) ^ 0.5
|
||||
function COORDINATE:Get2DDistance(TargetCoordinate)
|
||||
|
||||
local a={x=TargetCoordinate.x-self.x, y=0, z=TargetCoordinate.z-self.z}
|
||||
|
||||
local d=UTILS.VecNorm(a)
|
||||
|
||||
return d
|
||||
|
||||
end
|
||||
|
||||
--- Returns the temperature in Degrees Celsius.
|
||||
@@ -1086,23 +1137,6 @@ do -- COORDINATE
|
||||
return self
|
||||
end
|
||||
|
||||
--- Add a Distance in meters from the COORDINATE horizontal plane, with the given angle, and calculate the new COORDINATE.
|
||||
-- @param #COORDINATE self
|
||||
-- @param DCS#Distance Distance The Distance to be added in meters.
|
||||
-- @param DCS#Angle Angle The Angle in degrees.
|
||||
-- @return #COORDINATE The new calculated COORDINATE.
|
||||
function COORDINATE:Translate( Distance, Angle )
|
||||
local SX = self.x
|
||||
local SZ = self.z
|
||||
local Radians = Angle / 180 * math.pi
|
||||
local TX = Distance * math.cos( Radians ) + SX
|
||||
local TZ = Distance * math.sin( Radians ) + SZ
|
||||
|
||||
return COORDINATE:New( TX, self.y, TZ )
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- Build an air type route point.
|
||||
-- @param #COORDINATE self
|
||||
-- @param #COORDINATE.WaypointAltType AltType The altitude type.
|
||||
@@ -1290,8 +1324,10 @@ do -- COORDINATE
|
||||
RoutePoint.x = self.x
|
||||
RoutePoint.y = self.z
|
||||
|
||||
RoutePoint.alt = self:GetLandHeight()+1 -- self.y
|
||||
RoutePoint.alt = self:GetLandHeight()+1
|
||||
RoutePoint.alt_type = COORDINATE.WaypointAltType.BARO
|
||||
|
||||
RoutePoint.type = "Turning Point"
|
||||
|
||||
RoutePoint.action = Formation or "Off Road"
|
||||
RoutePoint.formation_template=""
|
||||
@@ -1351,7 +1387,7 @@ do -- COORDINATE
|
||||
-- @param #number Coalition (Optional) Coalition of the airbase.
|
||||
-- @return Wrapper.Airbase#AIRBASE Closest Airbase to the given coordinate.
|
||||
-- @return #number Distance to the closest airbase in meters.
|
||||
function COORDINATE:GetClosestAirbase(Category, Coalition)
|
||||
function COORDINATE:GetClosestAirbase2(Category, Coalition)
|
||||
|
||||
-- Get all airbases of the map.
|
||||
local airbases=AIRBASE.GetAllAirbases(Coalition)
|
||||
@@ -1384,6 +1420,36 @@ do -- COORDINATE
|
||||
|
||||
return closest,distmin
|
||||
end
|
||||
|
||||
--- Gets the nearest airbase with respect to the current coordinates.
|
||||
-- @param #COORDINATE self
|
||||
-- @param #number Category (Optional) Category of the airbase. Enumerator of @{Wrapper.Airbase#AIRBASE.Category}.
|
||||
-- @param #number Coalition (Optional) Coalition of the airbase.
|
||||
-- @return Wrapper.Airbase#AIRBASE Closest Airbase to the given coordinate.
|
||||
-- @return #number Distance to the closest airbase in meters.
|
||||
function COORDINATE:GetClosestAirbase(Category, Coalition)
|
||||
|
||||
local a=self:GetVec3()
|
||||
|
||||
local distmin=math.huge
|
||||
local airbase=nil
|
||||
for DCSairbaseID, DCSairbase in pairs(world.getAirbases(Coalition)) do
|
||||
local b=DCSairbase:getPoint()
|
||||
|
||||
local c=UTILS.VecSubstract(a,b)
|
||||
local dist=UTILS.VecNorm(c)
|
||||
|
||||
--env.info(string.format("Airbase %s dist=%d category=%d", DCSairbase:getName(), dist, DCSairbase:getCategory()))
|
||||
|
||||
if dist<distmin and (Category==nil or Category==DCSairbase:getDesc().category) then
|
||||
distmin=dist
|
||||
airbase=DCSairbase
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return AIRBASE:Find(airbase)
|
||||
end
|
||||
|
||||
--- Gets the nearest parking spot.
|
||||
-- @param #COORDINATE self
|
||||
@@ -1526,27 +1592,8 @@ do -- COORDINATE
|
||||
local coord=COORDINATE:NewFromVec2(_vec2)
|
||||
|
||||
Path[#Path+1]=coord
|
||||
|
||||
if MarkPath then
|
||||
coord:MarkToAll(string.format("Path segment %d.", _i))
|
||||
end
|
||||
if SmokePath then
|
||||
coord:SmokeGreen()
|
||||
end
|
||||
end
|
||||
|
||||
-- Mark/smoke endpoints
|
||||
if IncludeEndpoints then
|
||||
if MarkPath then
|
||||
COORDINATE:NewFromVec2(path[1]):MarkToAll("Path Initinal Point")
|
||||
COORDINATE:NewFromVec2(path[1]):MarkToAll("Path Final Point")
|
||||
end
|
||||
if SmokePath then
|
||||
COORDINATE:NewFromVec2(path[1]):SmokeBlue()
|
||||
COORDINATE:NewFromVec2(path[#path]):SmokeBlue()
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
else
|
||||
self:E("Path is nil. No valid path on road could be found.")
|
||||
GotPath=false
|
||||
@@ -1557,6 +1604,23 @@ do -- COORDINATE
|
||||
Path[#Path+1]=ToCoord
|
||||
end
|
||||
|
||||
-- Mark or smoke.
|
||||
if MarkPath or SmokePath then
|
||||
for i,c in pairs(Path) do
|
||||
local coord=c --#COORDINATE
|
||||
if MarkPath then
|
||||
coord:MarkToAll(string.format("Path segment %d", i))
|
||||
end
|
||||
if SmokePath then
|
||||
if i==1 or i==#Path then
|
||||
coord:SmokeBlue()
|
||||
else
|
||||
coord:SmokeGreen()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Sum up distances.
|
||||
if #Path>=2 then
|
||||
for i=1,#Path-1 do
|
||||
@@ -1564,7 +1628,7 @@ do -- COORDINATE
|
||||
end
|
||||
else
|
||||
-- There are cases where no path on road can be found.
|
||||
return nil,nil
|
||||
return nil,nil,false
|
||||
end
|
||||
|
||||
return Path, Way, GotPath
|
||||
@@ -1921,15 +1985,18 @@ do -- COORDINATE
|
||||
--- Returns if a Coordinate has Line of Sight (LOS) with the ToCoordinate.
|
||||
-- @param #COORDINATE self
|
||||
-- @param #COORDINATE ToCoordinate
|
||||
-- @param #number Offset Height offset in meters. Default 2 m.
|
||||
-- @return #boolean true If the ToCoordinate has LOS with the Coordinate, otherwise false.
|
||||
function COORDINATE:IsLOS( ToCoordinate )
|
||||
function COORDINATE:IsLOS( ToCoordinate, Offset )
|
||||
|
||||
Offset=Offset or 2
|
||||
|
||||
-- Measurement of visibility should not be from the ground, so Adding a hypotethical 2 meters to each Coordinate.
|
||||
local FromVec3 = self:GetVec3()
|
||||
FromVec3.y = FromVec3.y + 2
|
||||
FromVec3.y = FromVec3.y + Offset
|
||||
|
||||
local ToVec3 = ToCoordinate:GetVec3()
|
||||
ToVec3.y = ToVec3.y + 2
|
||||
ToVec3.y = ToVec3.y + Offset
|
||||
|
||||
local IsLOS = land.isVisible( FromVec3, ToVec3 )
|
||||
|
||||
@@ -1969,6 +2036,296 @@ do -- COORDINATE
|
||||
return InSphere
|
||||
end
|
||||
|
||||
--- Get sun rise time for a specific date at the coordinate.
|
||||
-- @param #COORDINATE self
|
||||
-- @param #number Day The day.
|
||||
-- @param #number Month The month.
|
||||
-- @param #number Year The year.
|
||||
-- @param #boolean InSeconds If true, return the sun rise time in seconds.
|
||||
-- @return #string Sunrise time, e.g. "05:41".
|
||||
function COORDINATE:GetSunriseAtDate(Day, Month, Year, InSeconds)
|
||||
|
||||
-- Day of the year.
|
||||
local DayOfYear=UTILS.GetDayOfYear(Year, Month, Day)
|
||||
|
||||
local Latitude, Longitude=self:GetLLDDM()
|
||||
|
||||
local Tdiff=UTILS.GMTToLocalTimeDifference()
|
||||
|
||||
local sunrise=UTILS.GetSunRiseAndSet(DayOfYear, Latitude, Longitude, true, Tdiff)
|
||||
|
||||
if InSeconds then
|
||||
return sunrise
|
||||
else
|
||||
return UTILS.SecondsToClock(sunrise, true)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- Get sun rise time for a specific day of the year at the coordinate.
|
||||
-- @param #COORDINATE self
|
||||
-- @param #number DayOfYear The day of the year.
|
||||
-- @param #boolean InSeconds If true, return the sun rise time in seconds.
|
||||
-- @return #string Sunrise time, e.g. "05:41".
|
||||
function COORDINATE:GetSunriseAtDayOfYear(DayOfYear, InSeconds)
|
||||
|
||||
local Latitude, Longitude=self:GetLLDDM()
|
||||
|
||||
local Tdiff=UTILS.GMTToLocalTimeDifference()
|
||||
|
||||
local sunrise=UTILS.GetSunRiseAndSet(DayOfYear, Latitude, Longitude, true, Tdiff)
|
||||
|
||||
if InSeconds then
|
||||
return sunrise
|
||||
else
|
||||
return UTILS.SecondsToClock(sunrise, true)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- Get todays sun rise time.
|
||||
-- @param #COORDINATE self
|
||||
-- @param #boolean InSeconds If true, return the sun rise time in seconds.
|
||||
-- @return #string Sunrise time, e.g. "05:41".
|
||||
function COORDINATE:GetSunrise(InSeconds)
|
||||
|
||||
-- Get current day of the year.
|
||||
local DayOfYear=UTILS.GetMissionDayOfYear()
|
||||
|
||||
-- Lat and long at this point.
|
||||
local Latitude, Longitude=self:GetLLDDM()
|
||||
|
||||
-- GMT time diff.
|
||||
local Tdiff=UTILS.GMTToLocalTimeDifference()
|
||||
|
||||
-- Sunrise in seconds of the day.
|
||||
local sunrise=UTILS.GetSunRiseAndSet(DayOfYear, Latitude, Longitude, true, Tdiff)
|
||||
|
||||
local date=UTILS.GetDCSMissionDate()
|
||||
|
||||
-- Debug output.
|
||||
--self:I(string.format("Sun rise at lat=%.3f long=%.3f on %s (DayOfYear=%d): %s (%d sec of the day) (GMT %d)", Latitude, Longitude, date, DayOfYear, tostring(UTILS.SecondsToClock(sunrise)), sunrise, Tdiff))
|
||||
|
||||
if InSeconds then
|
||||
return sunrise
|
||||
else
|
||||
return UTILS.SecondsToClock(sunrise, true)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- Get minutes until the next sun rise at this coordinate.
|
||||
-- @param #COORDINATE self
|
||||
-- @param OnlyToday If true, only calculate the sun rise of today. If sun has already risen, the time in negative minutes since sunrise is reported.
|
||||
-- @return #number Minutes to the next sunrise.
|
||||
function COORDINATE:GetMinutesToSunrise(OnlyToday)
|
||||
|
||||
-- Seconds of today
|
||||
local time=UTILS.SecondsOfToday()
|
||||
|
||||
-- Next Sunrise in seconds.
|
||||
local sunrise=nil
|
||||
|
||||
-- Time to sunrise.
|
||||
local delta=nil
|
||||
|
||||
if OnlyToday then
|
||||
|
||||
---
|
||||
-- Sunrise of today
|
||||
---
|
||||
|
||||
sunrise=self:GetSunrise(true)
|
||||
|
||||
delta=sunrise-time
|
||||
|
||||
else
|
||||
|
||||
---
|
||||
-- Sunrise of tomorrow
|
||||
---
|
||||
|
||||
-- Tomorrows day of the year.
|
||||
local DayOfYear=UTILS.GetMissionDayOfYear()+1
|
||||
|
||||
local Latitude, Longitude=self:GetLLDDM()
|
||||
|
||||
local Tdiff=UTILS.GMTToLocalTimeDifference()
|
||||
|
||||
sunrise=UTILS.GetSunRiseAndSet(DayOfYear, Latitude, Longitude, true, Tdiff)
|
||||
|
||||
delta=sunrise+UTILS.SecondsToMidnight()
|
||||
|
||||
end
|
||||
|
||||
return delta/60
|
||||
end
|
||||
|
||||
--- Check if it is day, i.e. if the sun has risen about the horizon at this coordinate.
|
||||
-- @param #COORDINATE self
|
||||
-- @param #string Clock (Optional) Time in format "HH:MM:SS+D", e.g. "05:40:00+3" to check if is day at 5:40 at third day after mission start. Default is to check right now.
|
||||
-- @return #boolean If true, it is day. If false, it is night time.
|
||||
function COORDINATE:IsDay(Clock)
|
||||
|
||||
if Clock then
|
||||
|
||||
local Time=UTILS.ClockToSeconds(Clock)
|
||||
|
||||
local clock=UTILS.Split(Clock, "+")[1]
|
||||
|
||||
-- Tomorrows day of the year.
|
||||
local DayOfYear=UTILS.GetMissionDayOfYear(Time)
|
||||
|
||||
local Latitude, Longitude=self:GetLLDDM()
|
||||
|
||||
local Tdiff=UTILS.GMTToLocalTimeDifference()
|
||||
|
||||
local sunrise=UTILS.GetSunRiseAndSet(DayOfYear, Latitude, Longitude, true, Tdiff)
|
||||
local sunset=UTILS.GetSunRiseAndSet(DayOfYear, Latitude, Longitude, false, Tdiff)
|
||||
|
||||
local time=UTILS.ClockToSeconds(clock)
|
||||
|
||||
-- Check if time is between sunrise and sunset.
|
||||
if time>sunrise and time<=sunset then
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
|
||||
else
|
||||
|
||||
-- Todays sun rise in sec.
|
||||
local sunrise=self:GetSunrise(true)
|
||||
|
||||
-- Todays sun set in sec.
|
||||
local sunset=self:GetSunset(true)
|
||||
|
||||
-- Seconds passed since midnight.
|
||||
local time=UTILS.SecondsOfToday()
|
||||
|
||||
-- Check if time is between sunrise and sunset.
|
||||
if time>sunrise and time<=sunset then
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- Check if it is night, i.e. if the sun has set below the horizon at this coordinate.
|
||||
-- @param #COORDINATE self
|
||||
-- @param #string Clock (Optional) Time in format "HH:MM:SS+D", e.g. "05:40:00+3" to check if is night at 5:40 at third day after mission start. Default is to check right now.
|
||||
-- @return #boolean If true, it is night. If false, it is day time.
|
||||
function COORDINATE:IsNight(Clock)
|
||||
return not self:IsDay(Clock)
|
||||
end
|
||||
|
||||
--- Get sun set time for a specific date at the coordinate.
|
||||
-- @param #COORDINATE self
|
||||
-- @param #number Day The day.
|
||||
-- @param #number Month The month.
|
||||
-- @param #number Year The year.
|
||||
-- @param #boolean InSeconds If true, return the sun rise time in seconds.
|
||||
-- @return #string Sunset time, e.g. "20:41".
|
||||
function COORDINATE:GetSunsetAtDate(Day, Month, Year, InSeconds)
|
||||
|
||||
-- Day of the year.
|
||||
local DayOfYear=UTILS.GetDayOfYear(Year, Month, Day)
|
||||
|
||||
local Latitude, Longitude=self:GetLLDDM()
|
||||
|
||||
local Tdiff=UTILS.GMTToLocalTimeDifference()
|
||||
|
||||
local sunset=UTILS.GetSunRiseAndSet(DayOfYear, Latitude, Longitude, false, Tdiff)
|
||||
|
||||
if InSeconds then
|
||||
return sunset
|
||||
else
|
||||
return UTILS.SecondsToClock(sunset, true)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- Get todays sun set time.
|
||||
-- @param #COORDINATE self
|
||||
-- @param #boolean InSeconds If true, return the sun set time in seconds.
|
||||
-- @return #string Sunrise time, e.g. "20:41".
|
||||
function COORDINATE:GetSunset(InSeconds)
|
||||
|
||||
-- Get current day of the year.
|
||||
local DayOfYear=UTILS.GetMissionDayOfYear()
|
||||
|
||||
-- Lat and long at this point.
|
||||
local Latitude, Longitude=self:GetLLDDM()
|
||||
|
||||
-- GMT time diff.
|
||||
local Tdiff=UTILS.GMTToLocalTimeDifference()
|
||||
|
||||
-- Sunrise in seconds of the day.
|
||||
local sunrise=UTILS.GetSunRiseAndSet(DayOfYear, Latitude, Longitude, false, Tdiff)
|
||||
|
||||
local date=UTILS.GetDCSMissionDate()
|
||||
|
||||
-- Debug output.
|
||||
--self:I(string.format("Sun set at lat=%.3f long=%.3f on %s (DayOfYear=%d): %s (%d sec of the day) (GMT %d)", Latitude, Longitude, date, DayOfYear, tostring(UTILS.SecondsToClock(sunrise)), sunrise, Tdiff))
|
||||
|
||||
if InSeconds then
|
||||
return sunrise
|
||||
else
|
||||
return UTILS.SecondsToClock(sunrise, true)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- Get minutes until the next sun set at this coordinate.
|
||||
-- @param #COORDINATE self
|
||||
-- @param OnlyToday If true, only calculate the sun set of today. If sun has already set, the time in negative minutes since sunset is reported.
|
||||
-- @return #number Minutes to the next sunrise.
|
||||
function COORDINATE:GetMinutesToSunset(OnlyToday)
|
||||
|
||||
-- Seconds of today
|
||||
local time=UTILS.SecondsOfToday()
|
||||
|
||||
-- Next Sunset in seconds.
|
||||
local sunset=nil
|
||||
|
||||
-- Time to sunrise.
|
||||
local delta=nil
|
||||
|
||||
if OnlyToday then
|
||||
|
||||
---
|
||||
-- Sunset of today
|
||||
---
|
||||
|
||||
sunset=self:GetSunset(true)
|
||||
|
||||
delta=sunset-time
|
||||
|
||||
else
|
||||
|
||||
---
|
||||
-- Sunset of tomorrow
|
||||
---
|
||||
|
||||
-- Tomorrows day of the year.
|
||||
local DayOfYear=UTILS.GetMissionDayOfYear()+1
|
||||
|
||||
local Latitude, Longitude=self:GetLLDDM()
|
||||
|
||||
local Tdiff=UTILS.GMTToLocalTimeDifference()
|
||||
|
||||
sunset=UTILS.GetSunRiseAndSet(DayOfYear, Latitude, Longitude, false, Tdiff)
|
||||
|
||||
delta=sunset+UTILS.SecondsToMidnight()
|
||||
|
||||
end
|
||||
|
||||
return delta/60
|
||||
end
|
||||
|
||||
|
||||
--- Return a BR string from a COORDINATE to the COORDINATE.
|
||||
-- @param #COORDINATE self
|
||||
@@ -2036,6 +2393,14 @@ do -- COORDINATE
|
||||
return ""
|
||||
end
|
||||
|
||||
--- Get Latitude and Longitude in Degrees Decimal Minutes (DDM).
|
||||
-- @param #COORDINATE self
|
||||
-- @return #number Latitude in DDM.
|
||||
-- @return #number Lontitude in DDM.
|
||||
function COORDINATE:GetLLDDM()
|
||||
return coord.LOtoLL( self:GetVec3() )
|
||||
end
|
||||
|
||||
--- Provides a Lat Lon string in Degree Minute Second format.
|
||||
-- @param #COORDINATE self
|
||||
-- @param Core.Settings#SETTINGS Settings (optional) The settings. Can be nil, and in this case the default settings are used. If you want to specify your own settings, use the _SETTINGS object.
|
||||
|
||||
@@ -429,7 +429,7 @@ BEACON = {
|
||||
-- @field #number VOR
|
||||
-- @field #number DME
|
||||
-- @field #number VOR_DME
|
||||
-- @field #number TACAN
|
||||
-- @field #number TACAN TACtical Air Navigation system.
|
||||
-- @field #number VORTAC
|
||||
-- @field #number RSBN
|
||||
-- @field #number BROADCAST_STATION
|
||||
@@ -440,45 +440,74 @@ BEACON = {
|
||||
-- @field #number ILS_NEAR_HOMER
|
||||
-- @field #number ILS_LOCALIZER
|
||||
-- @field #number ILS_GLIDESLOPE
|
||||
-- @field #number PRMG_LOCALIZER
|
||||
-- @field #number PRMG_GLIDESLOPE
|
||||
-- @field #number ICLS Same as ICLS glideslope.
|
||||
-- @field #number ICLS_LOCALIZER
|
||||
-- @field #number ICLS_GLIDESLOPE
|
||||
-- @field #number NAUTICAL_HOMER
|
||||
-- @field #number ICLS
|
||||
BEACON.Type={
|
||||
NULL = 0,
|
||||
VOR = 1,
|
||||
DME = 2,
|
||||
VOR_DME = 3,
|
||||
TACAN = 4,
|
||||
VORTAC = 5,
|
||||
RSBN = 32,
|
||||
BROADCAST_STATION = 1024,
|
||||
HOMER = 8,
|
||||
AIRPORT_HOMER = 4104,
|
||||
NULL = 0,
|
||||
VOR = 1,
|
||||
DME = 2,
|
||||
VOR_DME = 3,
|
||||
TACAN = 4,
|
||||
VORTAC = 5,
|
||||
RSBN = 128,
|
||||
BROADCAST_STATION = 1024,
|
||||
HOMER = 8,
|
||||
AIRPORT_HOMER = 4104,
|
||||
AIRPORT_HOMER_WITH_MARKER = 4136,
|
||||
ILS_FAR_HOMER = 16408,
|
||||
ILS_NEAR_HOMER = 16456,
|
||||
ILS_LOCALIZER = 16640,
|
||||
ILS_GLIDESLOPE = 16896,
|
||||
NAUTICAL_HOMER = 32776,
|
||||
ICLS = 131584,
|
||||
ILS_FAR_HOMER = 16408,
|
||||
ILS_NEAR_HOMER = 16424,
|
||||
ILS_LOCALIZER = 16640,
|
||||
ILS_GLIDESLOPE = 16896,
|
||||
PRMG_LOCALIZER = 33024,
|
||||
PRMG_GLIDESLOPE = 33280,
|
||||
ICLS = 131584, --leaving this in here but it is the same as ICLS_GLIDESLOPE
|
||||
ICLS_LOCALIZER = 131328,
|
||||
ICLS_GLIDESLOPE = 131584,
|
||||
NAUTICAL_HOMER = 65536,
|
||||
|
||||
}
|
||||
|
||||
--- Beacon systems supported by DCS. https://wiki.hoggitworld.com/view/DCS_command_activateBeacon
|
||||
-- @type BEACON.System
|
||||
-- @field #number PAR_10
|
||||
-- @field #number RSBN_5
|
||||
-- @field #number TACAN
|
||||
-- @field #number TACAN_TANKER
|
||||
-- @field #number ILS_LOCALIZER (This is the one to be used for AA TACAN Tanker!)
|
||||
-- @field #number ILS_GLIDESLOPE
|
||||
-- @field #number BROADCAST_STATION
|
||||
-- @field #number PAR_10 ?
|
||||
-- @field #number RSBN_5 Russian VOR/DME system.
|
||||
-- @field #number TACAN TACtical Air Navigation system on ground.
|
||||
-- @field #number TACAN_TANKER_X TACtical Air Navigation system for tankers on X band.
|
||||
-- @field #number TACAN_TANKER_Y TACtical Air Navigation system for tankers on Y band.
|
||||
-- @field #number VOR Very High Frequency Omni-Directional Range
|
||||
-- @field #number ILS_LOCALIZER ILS localizer
|
||||
-- @field #number ILS_GLIDESLOPE ILS glideslope.
|
||||
-- @field #number PRGM_LOCALIZER PRGM localizer.
|
||||
-- @field #number PRGM_GLIDESLOPE PRGM glideslope.
|
||||
-- @field #number BROADCAST_STATION Broadcast station.
|
||||
-- @field #number VORTAC Radio-based navigational aid for aircraft pilots consisting of a co-located VHF omnidirectional range (VOR) beacon and a tactical air navigation system (TACAN) beacon.
|
||||
-- @field #number TACAN_AA_MODE_X TACtical Air Navigation for aircraft on X band.
|
||||
-- @field #number TACAN_AA_MODE_Y TACtical Air Navigation for aircraft on Y band.
|
||||
-- @field #number VORDME Radio beacon that combines a VHF omnidirectional range (VOR) with a distance measuring equipment (DME).
|
||||
-- @field #number ICLS_LOCALIZER Carrier landing system.
|
||||
-- @field #number ICLS_GLIDESLOPE Carrier landing system.
|
||||
BEACON.System={
|
||||
PAR_10 = 1,
|
||||
RSBN_5 = 2,
|
||||
TACAN = 3,
|
||||
TACAN_TANKER = 4,
|
||||
ILS_LOCALIZER = 5,
|
||||
ILS_GLIDESLOPE = 6,
|
||||
BROADCAST_STATION = 7,
|
||||
PAR_10 = 1,
|
||||
RSBN_5 = 2,
|
||||
TACAN = 3,
|
||||
TACAN_TANKER_X = 4,
|
||||
TACAN_TANKER_Y = 5,
|
||||
VOR = 6,
|
||||
ILS_LOCALIZER = 7,
|
||||
ILS_GLIDESLOPE = 8,
|
||||
PRMG_LOCALIZER = 9,
|
||||
PRMG_GLIDESLOPE = 10,
|
||||
BROADCAST_STATION = 11,
|
||||
VORTAC = 12,
|
||||
TACAN_AA_MODE_X = 13,
|
||||
TACAN_AA_MODE_Y = 14,
|
||||
VORDME = 15,
|
||||
ICLS_LOCALIZER = 16,
|
||||
ICLS_GLIDESLOPE = 17,
|
||||
}
|
||||
|
||||
--- Create a new BEACON Object. This doesn't activate the beacon, though, use @{#BEACON.ActivateTACAN} etc.
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
--
|
||||
-- @type RADIOQUEUE
|
||||
-- @field #string ClassName Name of the class "RADIOQUEUE".
|
||||
-- @field #boolean Debug Debug mode. More info.
|
||||
-- @field #boolean Debugmode Debug mode. More info.
|
||||
-- @field #string lid ID for dcs.log.
|
||||
-- @field #number frequency The radio frequency in Hz.
|
||||
-- @field #number modulation The radio modulation. Either radio.modulation.AM or radio.modulation.FM.
|
||||
@@ -38,7 +38,7 @@
|
||||
-- @extends Core.Base#BASE
|
||||
RADIOQUEUE = {
|
||||
ClassName = "RADIOQUEUE",
|
||||
Debug = nil,
|
||||
Debugmode = nil,
|
||||
lid = nil,
|
||||
frequency = nil,
|
||||
modulation = nil,
|
||||
@@ -55,7 +55,7 @@ RADIOQUEUE = {
|
||||
power = nil,
|
||||
numbers = {},
|
||||
checking = nil,
|
||||
schedonce = nil,
|
||||
schedonce = false,
|
||||
}
|
||||
|
||||
--- Radio queue transmission data.
|
||||
@@ -375,8 +375,10 @@ function RADIOQUEUE:Broadcast(transmission)
|
||||
sender:SetCommand(commandTransmit)
|
||||
|
||||
-- Debug message.
|
||||
local text=string.format("file=%s, freq=%.2f MHz, duration=%.2f sec, subtitle=%s", filename, self.frequency/1000000, transmission.duration, transmission.subtitle or "")
|
||||
MESSAGE:New(text, 2, "RADIOQUEUE "..self.alias):ToAllIf(self.Debug)
|
||||
if self.Debugmode then
|
||||
local text=string.format("file=%s, freq=%.2f MHz, duration=%.2f sec, subtitle=%s", filename, self.frequency/1000000, transmission.duration, transmission.subtitle or "")
|
||||
MESSAGE:New(text, 2, "RADIOQUEUE "..self.alias):ToAll()
|
||||
end
|
||||
|
||||
else
|
||||
|
||||
@@ -388,10 +390,7 @@ function RADIOQUEUE:Broadcast(transmission)
|
||||
|
||||
-- Try to get positon from sender unit/static.
|
||||
if self.sendername then
|
||||
local coord=self:_GetRadioSenderCoord()
|
||||
if coord then
|
||||
vec3=coord:GetVec3()
|
||||
end
|
||||
vec3=self:_GetRadioSenderCoord()
|
||||
end
|
||||
|
||||
-- Try to get fixed positon.
|
||||
@@ -408,8 +407,10 @@ function RADIOQUEUE:Broadcast(transmission)
|
||||
trigger.action.radioTransmission(filename, vec3, self.modulation, false, self.frequency, self.power)
|
||||
|
||||
-- Debug message.
|
||||
local text=string.format("file=%s, freq=%.2f MHz, duration=%.2f sec, subtitle=%s", filename, self.frequency/1000000, transmission.duration, transmission.subtitle or "")
|
||||
MESSAGE:New(string.format(text, filename, transmission.duration, transmission.subtitle or ""), 5, "RADIOQUEUE "..self.alias):ToAllIf(self.Debug)
|
||||
if self.Debugmode then
|
||||
local text=string.format("file=%s, freq=%.2f MHz, duration=%.2f sec, subtitle=%s", filename, self.frequency/1000000, transmission.duration, transmission.subtitle or "")
|
||||
MESSAGE:New(string.format(text, filename, transmission.duration, transmission.subtitle or ""), 5, "RADIOQUEUE "..self.alias):ToAll()
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
@@ -524,7 +525,7 @@ end
|
||||
|
||||
--- Get unit from which we want to transmit a radio message. This has to be an aircraft for subtitles to work.
|
||||
-- @param #RADIOQUEUE self
|
||||
-- @return Wrapper.Unit#UNIT Sending aircraft unit or nil if was not setup, is not an aircraft or is not alive.
|
||||
-- @return Wrapper.Unit#UNIT Sending unit or nil if was not setup, is not an aircraft or ground unit or is not alive.
|
||||
function RADIOQUEUE:_GetRadioSender()
|
||||
|
||||
-- Check if we have a sending aircraft.
|
||||
@@ -532,11 +533,12 @@ function RADIOQUEUE:_GetRadioSender()
|
||||
|
||||
-- Try the general default.
|
||||
if self.sendername then
|
||||
|
||||
-- First try to find a unit
|
||||
sender=UNIT:FindByName(self.sendername)
|
||||
|
||||
-- Check that sender is alive and an aircraft.
|
||||
if sender and sender:IsAlive() and sender:IsAir() then
|
||||
if sender and sender:IsAlive() and (sender:IsAir() or sender:IsGround()) then
|
||||
return sender
|
||||
end
|
||||
|
||||
@@ -547,7 +549,7 @@ end
|
||||
|
||||
--- Get unit from which we want to transmit a radio message. This has to be an aircraft for subtitles to work.
|
||||
-- @param #RADIOQUEUE self
|
||||
-- @return Core.Point#COORDINATE Coordinate of the sender unit.
|
||||
-- @return DCS#Vec3 Vector 3D.
|
||||
function RADIOQUEUE:_GetRadioSenderCoord()
|
||||
|
||||
local vec3=nil
|
||||
@@ -560,7 +562,7 @@ function RADIOQUEUE:_GetRadioSenderCoord()
|
||||
|
||||
-- Check that sender is alive and an aircraft.
|
||||
if sender and sender:IsAlive() then
|
||||
return sender:GetCoordinate()
|
||||
return sender:GetVec3()
|
||||
end
|
||||
|
||||
-- Now try a static.
|
||||
@@ -568,7 +570,7 @@ function RADIOQUEUE:_GetRadioSenderCoord()
|
||||
|
||||
-- Check that sender is alive and an aircraft.
|
||||
if sender then
|
||||
return sender:GetCoordinate()
|
||||
return sender:GetVec3()
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1718,27 +1718,29 @@ function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalT
|
||||
self:T(string.format("Group %s is spawned on farp/ship/runway %s.", self.SpawnTemplatePrefix, SpawnAirbase:GetName()))
|
||||
nfree=SpawnAirbase:GetFreeParkingSpotsNumber(termtype, true)
|
||||
spots=SpawnAirbase:GetFreeParkingSpotsTable(termtype, true)
|
||||
--[[
|
||||
elseif Parkingdata~=nil then
|
||||
-- Parking data explicitly set by user as input parameter.
|
||||
nfree=#Parkingdata
|
||||
spots=Parkingdata
|
||||
]]
|
||||
else
|
||||
if ishelo then
|
||||
if termtype==nil then
|
||||
-- Helo is spawned. Try exclusive helo spots first.
|
||||
self:T(string.format("Helo group %s is at %s using terminal type %d.", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), AIRBASE.TerminalType.HelicopterOnly))
|
||||
spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup, AIRBASE.TerminalType.HelicopterOnly, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits)
|
||||
spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup, AIRBASE.TerminalType.HelicopterOnly, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits, Parkingdata)
|
||||
nfree=#spots
|
||||
if nfree<nunits then
|
||||
-- Not enough helo ports. Let's try also other terminal types.
|
||||
self:T(string.format("Helo group %s is at %s using terminal type %d.", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), AIRBASE.TerminalType.HelicopterUsable))
|
||||
spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup, AIRBASE.TerminalType.HelicopterUsable, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits)
|
||||
spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup, AIRBASE.TerminalType.HelicopterUsable, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits, Parkingdata)
|
||||
nfree=#spots
|
||||
end
|
||||
else
|
||||
-- No terminal type specified. We try all spots except shelters.
|
||||
self:T(string.format("Helo group %s is at %s using terminal type %d.", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), termtype))
|
||||
spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup, termtype, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits)
|
||||
spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup, termtype, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits, Parkingdata)
|
||||
nfree=#spots
|
||||
end
|
||||
else
|
||||
@@ -1747,29 +1749,30 @@ function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalT
|
||||
if isbomber or istransport or istanker or isawacs then
|
||||
-- First we fill the potentially bigger spots.
|
||||
self:T(string.format("Transport/bomber group %s is at %s using terminal type %d.", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), AIRBASE.TerminalType.OpenBig))
|
||||
spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup, AIRBASE.TerminalType.OpenBig, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits)
|
||||
spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup, AIRBASE.TerminalType.OpenBig, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits, Parkingdata)
|
||||
nfree=#spots
|
||||
if nfree<nunits then
|
||||
-- Now we try the smaller ones.
|
||||
self:T(string.format("Transport/bomber group %s is at %s using terminal type %d.", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), AIRBASE.TerminalType.OpenMedOrBig))
|
||||
spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup, AIRBASE.TerminalType.OpenMedOrBig, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits)
|
||||
spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup, AIRBASE.TerminalType.OpenMedOrBig, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits, Parkingdata)
|
||||
nfree=#spots
|
||||
end
|
||||
else
|
||||
self:T(string.format("Fighter group %s is at %s using terminal type %d.", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), AIRBASE.TerminalType.FighterAircraft))
|
||||
spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup, AIRBASE.TerminalType.FighterAircraft, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits)
|
||||
spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup, AIRBASE.TerminalType.FighterAircraft, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits, Parkingdata)
|
||||
nfree=#spots
|
||||
end
|
||||
else
|
||||
-- Terminal type explicitly given.
|
||||
self:T(string.format("Plane group %s is at %s using terminal type %s.", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), tostring(termtype)))
|
||||
spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup, termtype, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits)
|
||||
spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup, termtype, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits, Parkingdata)
|
||||
nfree=#spots
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Get parking data.
|
||||
-- Debug: Get parking data.
|
||||
--[[
|
||||
local parkingdata=SpawnAirbase:GetParkingSpotsTable(termtype)
|
||||
self:T(string.format("Parking at %s, terminal type %s:", SpawnAirbase:GetName(), tostring(termtype)))
|
||||
for _,_spot in pairs(parkingdata) do
|
||||
@@ -1777,6 +1780,7 @@ function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalT
|
||||
SpawnAirbase:GetName(), _spot.TerminalID, _spot.TerminalType,tostring(_spot.Free),tostring(_spot.TOAC),_spot.TerminalID0,_spot.DistToRwy))
|
||||
end
|
||||
self:T(string.format("%s at %s: free parking spots = %d - number of units = %d", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), nfree, nunits))
|
||||
]]
|
||||
|
||||
-- Set this to true if not enough spots are available for emergency air start.
|
||||
local _notenough=false
|
||||
@@ -2144,27 +2148,29 @@ function SPAWN:ParkAircraft( SpawnAirbase, TerminalType, Parkingdata, SpawnIndex
|
||||
self:T(string.format("Group %s is spawned on farp/ship/runway %s.", self.SpawnTemplatePrefix, SpawnAirbase:GetName()))
|
||||
nfree=SpawnAirbase:GetFreeParkingSpotsNumber(termtype, true)
|
||||
spots=SpawnAirbase:GetFreeParkingSpotsTable(termtype, true)
|
||||
--[[
|
||||
elseif Parkingdata~=nil then
|
||||
-- Parking data explicitly set by user as input parameter.
|
||||
nfree=#Parkingdata
|
||||
spots=Parkingdata
|
||||
]]
|
||||
else
|
||||
if ishelo then
|
||||
if termtype==nil then
|
||||
-- Helo is spawned. Try exclusive helo spots first.
|
||||
self:T(string.format("Helo group %s is at %s using terminal type %d.", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), AIRBASE.TerminalType.HelicopterOnly))
|
||||
spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup, AIRBASE.TerminalType.HelicopterOnly, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits)
|
||||
spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup, AIRBASE.TerminalType.HelicopterOnly, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits, Parkingdata)
|
||||
nfree=#spots
|
||||
if nfree<nunits then
|
||||
-- Not enough helo ports. Let's try also other terminal types.
|
||||
self:T(string.format("Helo group %s is at %s using terminal type %d.", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), AIRBASE.TerminalType.HelicopterUsable))
|
||||
spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup, AIRBASE.TerminalType.HelicopterUsable, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits)
|
||||
spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup, AIRBASE.TerminalType.HelicopterUsable, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits, Parkingdata)
|
||||
nfree=#spots
|
||||
end
|
||||
else
|
||||
-- No terminal type specified. We try all spots except shelters.
|
||||
self:T(string.format("Helo group %s is at %s using terminal type %d.", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), termtype))
|
||||
spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup, termtype, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits)
|
||||
spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup, termtype, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits, Parkingdata)
|
||||
nfree=#spots
|
||||
end
|
||||
else
|
||||
@@ -2176,29 +2182,30 @@ function SPAWN:ParkAircraft( SpawnAirbase, TerminalType, Parkingdata, SpawnIndex
|
||||
if isbomber or istransport then
|
||||
-- First we fill the potentially bigger spots.
|
||||
self:T(string.format("Transport/bomber group %s is at %s using terminal type %d.", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), AIRBASE.TerminalType.OpenBig))
|
||||
spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup, AIRBASE.TerminalType.OpenBig, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits)
|
||||
spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup, AIRBASE.TerminalType.OpenBig, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits, Parkingdata)
|
||||
nfree=#spots
|
||||
if nfree<nunits then
|
||||
-- Now we try the smaller ones.
|
||||
self:T(string.format("Transport/bomber group %s is at %s using terminal type %d.", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), AIRBASE.TerminalType.OpenMedOrBig))
|
||||
spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup, AIRBASE.TerminalType.OpenMedOrBig, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits)
|
||||
spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup, AIRBASE.TerminalType.OpenMedOrBig, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits, Parkingdata)
|
||||
nfree=#spots
|
||||
end
|
||||
else
|
||||
self:T(string.format("Fighter group %s is at %s using terminal type %d.", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), AIRBASE.TerminalType.FighterAircraft))
|
||||
spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup, AIRBASE.TerminalType.FighterAircraft, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits)
|
||||
spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup, AIRBASE.TerminalType.FighterAircraft, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits, Parkingdata)
|
||||
nfree=#spots
|
||||
end
|
||||
else
|
||||
-- Terminal type explicitly given.
|
||||
self:T(string.format("Plane group %s is at %s using terminal type %s.", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), tostring(termtype)))
|
||||
spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup, termtype, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits)
|
||||
spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup, termtype, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits, Parkingdata)
|
||||
nfree=#spots
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Get parking data.
|
||||
-- Debug: Get parking data.
|
||||
--[[
|
||||
local parkingdata=SpawnAirbase:GetParkingSpotsTable(termtype)
|
||||
self:T2(string.format("Parking at %s, terminal type %s:", SpawnAirbase:GetName(), tostring(termtype)))
|
||||
for _,_spot in pairs(parkingdata) do
|
||||
@@ -2206,6 +2213,7 @@ function SPAWN:ParkAircraft( SpawnAirbase, TerminalType, Parkingdata, SpawnIndex
|
||||
SpawnAirbase:GetName(), _spot.TerminalID, _spot.TerminalType,tostring(_spot.Free),tostring(_spot.TOAC),_spot.TerminalID0,_spot.DistToRwy))
|
||||
end
|
||||
self:T(string.format("%s at %s: free parking spots = %d - number of units = %d", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), nfree, nunits))
|
||||
]]
|
||||
|
||||
-- Set this to true if not enough spots are available for emergency air start.
|
||||
local _notenough=false
|
||||
|
||||
@@ -1,104 +1,156 @@
|
||||
--- **Core** - Spawn new statics in your running missions.
|
||||
--- **Core** - Spawn statics.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ## Features:
|
||||
--
|
||||
-- * Spawn new statics from a static already defined using the mission editor.
|
||||
-- * Spawn new statics from a static already defined in the mission editor.
|
||||
-- * Spawn new statics from a given template.
|
||||
-- * Spawn new statics from a given type.
|
||||
-- * Spawn with a custom heading and location.
|
||||
-- * Spawn within a zone.
|
||||
-- * Spawn statics linked to units, .e.g on aircraft carriers.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- # Demo Missions
|
||||
--
|
||||
-- ### [SPAWNSTATIC Demo Missions source code](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release/SPS - Spawning Statics)
|
||||
--
|
||||
-- ### [SPAWNSTATIC Demo Missions, only for beta testers](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/SPS%20-%20Spawning%20Statics)
|
||||
-- ## [SPAWNSTATIC Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/SPS%20-%20Spawning%20Statics)
|
||||
--
|
||||
-- ### [ALL Demo Missions pack of the last release](https://github.com/FlightControl-Master/MOOSE_MISSIONS/releases)
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- # YouTube Channel
|
||||
--
|
||||
-- ### [SPAWNSTATIC YouTube Channel]()
|
||||
-- ## [SPAWNSTATIC YouTube Channel]() [No videos yet!]
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
-- ### Contributions:
|
||||
-- ### Contributions: **funkyfranky**
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @module Core.SpawnStatic
|
||||
-- @image Core_Spawnstatic.JPG
|
||||
|
||||
|
||||
|
||||
--- @type SPAWNSTATIC
|
||||
-- @field #string SpawnTemplatePrefix Name of the template group.
|
||||
-- @field #number CountryID Country ID.
|
||||
-- @field #number CoalitionID Coalition ID.
|
||||
-- @field #number CategoryID Category ID.
|
||||
-- @field #number SpawnIndex Running number increased with each new Spawn.
|
||||
-- @field Wrapper.Unit#UNIT InitLinkUnit The unit the static is linked to.
|
||||
-- @field #number InitOffsetX Link offset X coordinate.
|
||||
-- @field #number InitOffsetY Link offset Y coordinate.
|
||||
-- @field #number InitOffsetAngle Link offset angle in degrees.
|
||||
-- @field #number InitStaticHeading Heading of the static.
|
||||
-- @field #string InitStaticLivery Livery for aircraft.
|
||||
-- @field #string InitStaticShape Shape of teh static.
|
||||
-- @field #string InitStaticType Type of the static.
|
||||
-- @field #string InitStaticCategory Categrory of the static.
|
||||
-- @field #string InitStaticName Name of the static.
|
||||
-- @field Core.Point#COORDINATE InitStaticCoordinate Coordinate where to spawn the static.
|
||||
-- @field #boolean InitDead Set static to be dead if true.
|
||||
-- @field #boolean InitCargo If true, static can act as cargo.
|
||||
-- @field #number InitCargoMass Mass of cargo in kg.
|
||||
-- @extends Core.Base#BASE
|
||||
|
||||
|
||||
--- Allows to spawn dynamically new @{Static}s.
|
||||
-- Through creating a copy of an existing static object template as defined in the Mission Editor (ME),
|
||||
-- SPAWNSTATIC can retireve the properties of the defined static object template (like type, category etc), and "copy"
|
||||
-- these properties to create a new static object and place it at the desired coordinate.
|
||||
--- Allows to spawn dynamically new @{Static}s into your mission.
|
||||
--
|
||||
-- New spawned @{Static}s get **the same name** as the name of the template Static,
|
||||
-- or gets the given name when a new name is provided at the Spawn method.
|
||||
-- Through creating a copy of an existing static object template as defined in the Mission Editor (ME), SPAWNSTATIC can retireve the properties of the defined static object template (like type, category etc),
|
||||
-- and "copy" these properties to create a new static object and place it at the desired coordinate.
|
||||
--
|
||||
-- New spawned @{Static}s get **the same name** as the name of the template Static, or gets the given name when a new name is provided at the Spawn method.
|
||||
-- By default, spawned @{Static}s will follow a naming convention at run-time:
|
||||
--
|
||||
-- * Spawned @{Static}s will have the name _StaticName_#_nnn_, where _StaticName_ is the name of the **Template Static**,
|
||||
-- and _nnn_ is a **counter from 0 to 99999**.
|
||||
-- * Spawned @{Static}s will have the name _StaticName_#_nnn_, where _StaticName_ is the name of the **Template Static**, and _nnn_ is a **counter from 0 to 99999**.
|
||||
--
|
||||
--
|
||||
-- ## SPAWNSTATIC construction methods
|
||||
-- # SPAWNSTATIC Constructors
|
||||
--
|
||||
-- Create a new SPAWNSTATIC object with the @{#SPAWNSTATIC.NewFromStatic}():
|
||||
-- Firstly, we need to create a SPAWNSTATIC object that will be used to spawn new statics into the mission. There are three ways to do this.
|
||||
--
|
||||
-- * @{#SPAWNSTATIC.NewFromStatic}(): Creates a new SPAWNSTATIC object given a name that is used as the base of the naming of each spawned Static.
|
||||
-- ## Use another Static
|
||||
--
|
||||
-- A new SPAWNSTATIC object can be created using another static by the @{#SPAWNSTATIC.NewFromStatic}() function. All parameters such as position, heading, country will be initialized
|
||||
-- from the static.
|
||||
--
|
||||
-- ## From a Template
|
||||
--
|
||||
-- A SPAWNSTATIC object can also be created from a template table using the @{#SPAWNSTATIC.NewFromTemplate}(SpawnTemplate, CountryID) function. All parameters are taken from the template.
|
||||
--
|
||||
-- ## From a Type
|
||||
--
|
||||
-- A very basic method is to create a SPAWNSTATIC object by just giving the type of the static. All parameters must be initialized from the InitXYZ functions described below. Otherwise default values
|
||||
-- are used. For example, if no spawn coordinate is given, the static will be created at the origin of the map.
|
||||
--
|
||||
-- # Setting Parameters
|
||||
--
|
||||
-- Parameters such as the spawn position, heading, country etc. can be set via :Init*XYZ* functions. Note that these functions must be given before the actual spawn command!
|
||||
--
|
||||
-- * @{#SPAWNSTATIC.InitCoordinate}(Coordinate) Sets the coordinate where the static is spawned. Statics are always spawnd on the ground.
|
||||
-- * @{#SPAWNSTATIC.InitHeading}(Heading) sets the orientation of the static.
|
||||
-- * @{#SPAWNSTATIC.InitLivery}(LiveryName) sets the livery of the static. Not all statics support this.
|
||||
-- * @{#SPAWNSTATIC.InitType}(StaticType) sets the type of the static.
|
||||
-- * @{#SPAWNSTATIC.InitShape}(StaticType) sets the shape of the static. Not all statics have this parameter.
|
||||
-- * @{#SPAWNSTATIC.InitNamePrefix}(NamePrefix) sets the name prefix of the spawned statics.
|
||||
-- * @{#SPAWNSTATIC.InitCountry}(CountryID) sets the country and therefore the coalition of the spawned statics.
|
||||
-- * @{#SPAWNSTATIC.InitLinkToUnit}(Unit, OffsetX, OffsetY, OffsetAngle) links the static to a unit, e.g. to an aircraft carrier.
|
||||
--
|
||||
-- ## **Spawn** methods
|
||||
-- # Spawning the Statics
|
||||
--
|
||||
-- Groups can be spawned at different times and methods:
|
||||
-- Once the SPAWNSTATIC object is created and parameters are initialized, the spawn command can be given. There are different methods where some can be used to directly set parameters
|
||||
-- such as position and heading.
|
||||
--
|
||||
-- * @{#SPAWNSTATIC.SpawnFromPointVec2}(): Spawn a new group from a POINT_VEC2 coordinate.
|
||||
-- (The group will be spawned at land height ).
|
||||
-- * @{#SPAWNSTATIC.SpawnFromZone}(): Spawn a new group in a @{Zone}.
|
||||
-- * @{#SPAWNSTATIC.Spawn}(Heading, NewName) spawns the static with the set parameters. Optionally, heading and name can be given. The name **must be unique**!
|
||||
-- * @{#SPAWNSTATIC.SpawnFromCoordinate}(Coordinate, Heading, NewName) spawn the static at the given coordinate. Optionally, heading and name can be given. The name **must be unique**!
|
||||
-- * @{#SPAWNSTATIC.SpawnFromPointVec2}(PointVec2, Heading, NewName) spawns the static at a POINT_VEC2 coordinate. Optionally, heading and name can be given. The name **must be unique**!
|
||||
-- * @{#SPAWNSTATIC.SpawnFromZone}(Zone, Heading, NewName) spawns the static at the center of a @{Zone}. Optionally, heading and name can be given. The name **must be unique**!
|
||||
--
|
||||
-- @field #SPAWNSTATIC SPAWNSTATIC
|
||||
--
|
||||
SPAWNSTATIC = {
|
||||
ClassName = "SPAWNSTATIC",
|
||||
ClassName = "SPAWNSTATIC",
|
||||
SpawnIndex = 0,
|
||||
}
|
||||
|
||||
--- Static template table data.
|
||||
-- @type SPAWNSTATIC.TemplateData
|
||||
-- @field #string name Name of the static.
|
||||
-- @field #string type Type of the static.
|
||||
-- @field #string category Category of the static.
|
||||
-- @field #number x X-coordinate of the static.
|
||||
-- @field #number y Y-coordinate of teh static.
|
||||
-- @field #number heading Heading in rad.
|
||||
-- @field #boolean dead Static is dead if true.
|
||||
-- @field #string livery_id Livery name.
|
||||
-- @field #number unitId Unit ID.
|
||||
-- @field #number groupId Group ID.
|
||||
-- @field #table offsets Offset parameters when linked to a unit.
|
||||
-- @field #number mass Cargo mass in kg.
|
||||
-- @field #boolean canCargo Static can be a cargo.
|
||||
|
||||
--- @type SPAWNSTATIC.SpawnZoneTable
|
||||
-- @list <Core.Zone#ZONE_BASE> SpawnZone
|
||||
|
||||
|
||||
--- Creates the main object to spawn a @{Static} defined in the ME.
|
||||
--- Creates the main object to spawn a @{Static} defined in the mission editor (ME).
|
||||
-- @param #SPAWNSTATIC self
|
||||
-- @param #string SpawnTemplatePrefix is the name of the Group in the ME that defines the Template. Each new group will have the name starting with SpawnTemplatePrefix.
|
||||
-- @param DCS#country.id SpawnCountryID The ID of the country.
|
||||
-- @param DCS#coalition.side SpawnCoalitionID The ID of the coalition.
|
||||
-- @return #SPAWNSTATIC
|
||||
function SPAWNSTATIC:NewFromStatic( SpawnTemplatePrefix, SpawnCountryID, SpawnCoalitionID )
|
||||
-- @param #string SpawnTemplateName Name of the static object in the ME. Each new static will have the name starting with this prefix.
|
||||
-- @param DCS#country.id SpawnCountryID (Optional) The ID of the country.
|
||||
-- @return #SPAWNSTATIC self
|
||||
function SPAWNSTATIC:NewFromStatic(SpawnTemplateName, SpawnCountryID)
|
||||
|
||||
local self = BASE:Inherit( self, BASE:New() ) -- #SPAWNSTATIC
|
||||
self:F( { SpawnTemplatePrefix } )
|
||||
|
||||
local TemplateStatic, CoalitionID, CategoryID, CountryID = _DATABASE:GetStaticGroupTemplate( SpawnTemplatePrefix )
|
||||
local TemplateStatic, CoalitionID, CategoryID, CountryID = _DATABASE:GetStaticGroupTemplate(SpawnTemplateName)
|
||||
|
||||
if TemplateStatic then
|
||||
self.SpawnTemplatePrefix = SpawnTemplatePrefix
|
||||
self.CountryID = SpawnCountryID or CountryID
|
||||
self.CategoryID = CategoryID
|
||||
self.CoalitionID = SpawnCoalitionID or CoalitionID
|
||||
self.SpawnIndex = 0
|
||||
self.SpawnTemplatePrefix = SpawnTemplateName
|
||||
self.TemplateStaticUnit = UTILS.DeepCopy(TemplateStatic.units[1])
|
||||
self.CountryID = SpawnCountryID or CountryID
|
||||
self.CategoryID = CategoryID
|
||||
self.CoalitionID = CoalitionID
|
||||
self.SpawnIndex = 0
|
||||
else
|
||||
error( "SPAWNSTATIC:New: There is no static declared in the mission editor with SpawnTemplatePrefix = '" .. SpawnTemplatePrefix .. "'" )
|
||||
error( "SPAWNSTATIC:New: There is no static declared in the mission editor with SpawnTemplatePrefix = '" .. tostring(SpawnTemplateName) .. "'" )
|
||||
end
|
||||
|
||||
self:SetEventPriority( 5 )
|
||||
@@ -106,206 +158,325 @@ function SPAWNSTATIC:NewFromStatic( SpawnTemplatePrefix, SpawnCountryID, SpawnCo
|
||||
return self
|
||||
end
|
||||
|
||||
--- Creates the main object to spawn a @{Static} based on a type name.
|
||||
--- Creates the main object to spawn a @{Static} given a template table.
|
||||
-- @param #SPAWNSTATIC self
|
||||
-- @param #string SpawnTypeName is the name of the type.
|
||||
-- @return #SPAWNSTATIC
|
||||
function SPAWNSTATIC:NewFromType( SpawnTypeName, SpawnShapeName, SpawnCategory, SpawnCountryID, SpawnCoalitionID )
|
||||
local self = BASE:Inherit( self, BASE:New() ) -- #SPAWNSTATIC
|
||||
self:F( { SpawnTypeName } )
|
||||
|
||||
self.SpawnTypeName = SpawnTypeName
|
||||
self.CountryID = SpawnCountryID
|
||||
self.CoalitionID = SpawnCoalitionID
|
||||
self.SpawnIndex = 0
|
||||
-- @param #table SpawnTemplate Template used for spawning.
|
||||
-- @param DCS#country.id CountryID The ID of the country. Default `country.id.USA`.
|
||||
-- @return #SPAWNSTATIC self
|
||||
function SPAWNSTATIC:NewFromTemplate(SpawnTemplate, CountryID)
|
||||
|
||||
self:SetEventPriority( 5 )
|
||||
local self = BASE:Inherit( self, BASE:New() ) -- #SPAWNSTATIC
|
||||
|
||||
self.TemplateStaticUnit = UTILS.DeepCopy(SpawnTemplate)
|
||||
self.SpawnTemplatePrefix = SpawnTemplate.name
|
||||
self.CountryID = CountryID or country.id.USA
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Creates the main object to spawn a @{Static} from a given type.
|
||||
-- NOTE that you have to init many other parameters as spawn coordinate etc.
|
||||
-- @param #SPAWNSTATIC self
|
||||
-- @param #string StaticType Type of the static.
|
||||
-- @param #string StaticCategory Category of the static, e.g. "Planes".
|
||||
-- @param DCS#country.id CountryID The ID of the country. Default `country.id.USA`.
|
||||
-- @return #SPAWNSTATIC self
|
||||
function SPAWNSTATIC:NewFromType(StaticType, StaticCategory, CountryID)
|
||||
|
||||
local self = BASE:Inherit( self, BASE:New() ) -- #SPAWNSTATIC
|
||||
|
||||
self.InitStaticType=StaticType
|
||||
self.InitStaticCategory=StaticCategory
|
||||
self.CountryID=CountryID or country.id.USA
|
||||
self.SpawnTemplatePrefix=self.InitStaticType
|
||||
|
||||
self.InitStaticCoordinate=COORDINATE:New(0, 0, 0)
|
||||
self.InitStaticHeading=0
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Creates a new @{Static} at the original position.
|
||||
--- Initialize heading of the spawned static.
|
||||
-- @param #SPAWNSTATIC self
|
||||
-- @param #number Heading The heading of the static, which is a number in degrees from 0 to 360.
|
||||
-- @param #string (optional) The name of the new static.
|
||||
-- @return #SPAWNSTATIC
|
||||
function SPAWNSTATIC:Spawn( Heading, NewName ) --R2.3
|
||||
self:F( { Heading, NewName } )
|
||||
|
||||
local StaticTemplate, CoalitionID, CategoryID, CountryID = _DATABASE:GetStaticGroupTemplate( self.SpawnTemplatePrefix )
|
||||
|
||||
if StaticTemplate then
|
||||
|
||||
local StaticUnitTemplate = StaticTemplate.units[1]
|
||||
|
||||
StaticTemplate.name = NewName or string.format("%s#%05d", self.SpawnTemplatePrefix, self.SpawnIndex )
|
||||
StaticTemplate.heading = ( Heading / 180 ) * math.pi
|
||||
|
||||
_DATABASE:_RegisterStaticTemplate( StaticTemplate, CoalitionID, CategoryID, CountryID )
|
||||
|
||||
local Static = coalition.addStaticObject( self.CountryID or CountryID, StaticTemplate.units[1] )
|
||||
|
||||
self.SpawnIndex = self.SpawnIndex + 1
|
||||
|
||||
return _DATABASE:FindStatic(Static:getName())
|
||||
end
|
||||
|
||||
return nil
|
||||
-- @param Core.Point#COORDINATE Coordinate Position where the static is spawned.
|
||||
-- @return #SPAWNSTATIC self
|
||||
function SPAWNSTATIC:InitCoordinate(Coordinate)
|
||||
self.InitStaticCoordinate=Coordinate
|
||||
return self
|
||||
end
|
||||
|
||||
--- Initialize heading of the spawned static.
|
||||
-- @param #SPAWNSTATIC self
|
||||
-- @param #number Heading The heading in degrees.
|
||||
-- @return #SPAWNSTATIC self
|
||||
function SPAWNSTATIC:InitHeading(Heading)
|
||||
self.InitStaticHeading=Heading
|
||||
return self
|
||||
end
|
||||
|
||||
--- Initialize livery of the spawned static.
|
||||
-- @param #SPAWNSTATIC self
|
||||
-- @param #string LiveryName Name of the livery to use.
|
||||
-- @return #SPAWNSTATIC self
|
||||
function SPAWNSTATIC:InitLivery(LiveryName)
|
||||
self.InitStaticLivery=LiveryName
|
||||
return self
|
||||
end
|
||||
|
||||
--- Initialize type of the spawned static.
|
||||
-- @param #SPAWNSTATIC self
|
||||
-- @param #string StaticType Type of the static, e.g. "FA-18C_hornet".
|
||||
-- @return #SPAWNSTATIC self
|
||||
function SPAWNSTATIC:InitType(StaticType)
|
||||
self.InitStaticType=StaticType
|
||||
return self
|
||||
end
|
||||
|
||||
--- Initialize shape of the spawned static. Required by some but not all statics.
|
||||
-- @param #SPAWNSTATIC self
|
||||
-- @param #string StaticShape Shape of the static, e.g. "carrier_tech_USA".
|
||||
-- @return #SPAWNSTATIC self
|
||||
function SPAWNSTATIC:InitShape(StaticShape)
|
||||
self.InitStaticShape=StaticShape
|
||||
return self
|
||||
end
|
||||
|
||||
--- Initialize parameters for spawning FARPs.
|
||||
-- @param #SPAWNSTATIC self
|
||||
-- @param #number CallsignID Callsign ID. Default 1 (="London").
|
||||
-- @param #number Frequency Frequency in MHz. Default 127.5 MHz.
|
||||
-- @param #number Modulation Modulation 0=AM, 1=FM.
|
||||
-- @return #SPAWNSTATIC self
|
||||
function SPAWNSTATIC:InitFARP(CallsignID, Frequency, Modulation)
|
||||
self.InitFarp=true
|
||||
self.InitFarpCallsignID=CallsignID or 1
|
||||
self.InitFarpFreq=Frequency or 127.5
|
||||
self.InitFarpModu=Modulation or 0
|
||||
return self
|
||||
end
|
||||
|
||||
--- Initialize cargo mass.
|
||||
-- @param #SPAWNSTATIC self
|
||||
-- @param #number Mass Mass of the cargo in kg.
|
||||
-- @return #SPAWNSTATIC self
|
||||
function SPAWNSTATIC:InitCargoMass(Mass)
|
||||
self.InitCargoMass=Mass
|
||||
return self
|
||||
end
|
||||
|
||||
--- Initialize as cargo.
|
||||
-- @param #SPAWNSTATIC self
|
||||
-- @param #boolean IsCargo If true, this static can act as cargo.
|
||||
-- @return #SPAWNSTATIC self
|
||||
function SPAWNSTATIC:InitCargo(IsCargo)
|
||||
self.InitCargo=IsCargo
|
||||
return self
|
||||
end
|
||||
|
||||
--- Initialize country of the spawned static. This determines the category.
|
||||
-- @param #SPAWNSTATIC self
|
||||
-- @param #string CountryID The country ID, e.g. country.id.USA.
|
||||
-- @return #SPAWNSTATIC self
|
||||
function SPAWNSTATIC:InitCountry(CountryID)
|
||||
self.CountryID=CountryID
|
||||
return self
|
||||
end
|
||||
|
||||
--- Initialize name prefix statics get. This will be appended by "#0001", "#0002" etc.
|
||||
-- @param #SPAWNSTATIC self
|
||||
-- @param #string NamePrefix Name prefix of statics spawned. Will append #0001, etc to the name.
|
||||
-- @return #SPAWNSTATIC self
|
||||
function SPAWNSTATIC:InitNamePrefix(NamePrefix)
|
||||
self.SpawnTemplatePrefix=NamePrefix
|
||||
return self
|
||||
end
|
||||
|
||||
--- Init link to a unit.
|
||||
-- @param #SPAWNSTATIC self
|
||||
-- @param Wrapper.Unit#UNIT Unit The unit to which the static is linked.
|
||||
-- @param #number OffsetX Offset in X.
|
||||
-- @param #number OffsetY Offset in Y.
|
||||
-- @param #number OffsetAngle Offset angle in degrees.
|
||||
-- @return #SPAWNSTATIC self
|
||||
function SPAWNSTATIC:InitLinkToUnit(Unit, OffsetX, OffsetY, OffsetAngle)
|
||||
|
||||
self.InitLinkUnit=Unit
|
||||
self.InitOffsetX=OffsetX or 0
|
||||
self.InitOffsetY=OffsetY or 0
|
||||
self.InitOffsetAngle=OffsetAngle or 0
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Spawn a new STATIC object.
|
||||
-- @param #SPAWNSTATIC self
|
||||
-- @param #number Heading (Optional) The heading of the static, which is a number in degrees from 0 to 360. Default is the heading of the template.
|
||||
-- @param #string NewName (Optional) The name of the new static.
|
||||
-- @return Wrapper.Static#STATIC The static spawned.
|
||||
function SPAWNSTATIC:Spawn(Heading, NewName)
|
||||
|
||||
if Heading then
|
||||
self.InitStaticHeading=Heading
|
||||
end
|
||||
|
||||
if NewName then
|
||||
self.InitStaticName=NewName
|
||||
end
|
||||
|
||||
return self:_SpawnStatic(self.TemplateStaticUnit, self.CountryID)
|
||||
|
||||
end
|
||||
|
||||
--- Creates a new @{Static} from a POINT_VEC2.
|
||||
-- @param #SPAWNSTATIC self
|
||||
-- @param Core.Point#POINT_VEC2 PointVec2 The 2D coordinate where to spawn the static.
|
||||
-- @param #number Heading The heading of the static, which is a number in degrees from 0 to 360.
|
||||
-- @param #string (optional) The name of the new static.
|
||||
-- @return #SPAWNSTATIC
|
||||
function SPAWNSTATIC:SpawnFromPointVec2( PointVec2, Heading, NewName ) --R2.1
|
||||
self:F( { PointVec2, Heading, NewName } )
|
||||
|
||||
local StaticTemplate, CoalitionID, CategoryID, CountryID = _DATABASE:GetStaticGroupTemplate( self.SpawnTemplatePrefix )
|
||||
|
||||
if StaticTemplate then
|
||||
|
||||
local StaticUnitTemplate = StaticTemplate.units[1]
|
||||
|
||||
StaticUnitTemplate.x = PointVec2.x
|
||||
StaticUnitTemplate.y = PointVec2.z
|
||||
|
||||
StaticTemplate.route = nil
|
||||
StaticTemplate.groupId = nil
|
||||
|
||||
StaticTemplate.name = NewName or string.format("%s#%05d", self.SpawnTemplatePrefix, self.SpawnIndex )
|
||||
StaticUnitTemplate.name = StaticTemplate.name
|
||||
StaticUnitTemplate.heading = ( Heading / 180 ) * math.pi
|
||||
|
||||
_DATABASE:_RegisterStaticTemplate( StaticTemplate, CoalitionID, CategoryID, CountryID)
|
||||
|
||||
self:F({StaticTemplate = StaticTemplate})
|
||||
-- @param #string NewName (Optional) The name of the new static.
|
||||
-- @return Wrapper.Static#STATIC The static spawned.
|
||||
function SPAWNSTATIC:SpawnFromPointVec2(PointVec2, Heading, NewName)
|
||||
|
||||
local Static = coalition.addStaticObject( self.CountryID or CountryID, StaticTemplate.units[1] )
|
||||
|
||||
self.SpawnIndex = self.SpawnIndex + 1
|
||||
|
||||
return _DATABASE:FindStatic(Static:getName())
|
||||
end
|
||||
local vec2={x=PointVec2:GetX(), y=PointVec2:GetY()}
|
||||
|
||||
local Coordinate=COORDINATE:NewFromVec2(vec2)
|
||||
|
||||
return nil
|
||||
return self:SpawnFromCoordinate(Coordinate, Heading, NewName)
|
||||
end
|
||||
|
||||
|
||||
--- Creates a new @{Static} from a COORDINATE.
|
||||
-- @param #SPAWNSTATIC self
|
||||
-- @param Core.Point#COORDINATE Coordinate The 3D coordinate where to spawn the static.
|
||||
-- @param #number Heading (Optional) Heading The heading of the static, which is a number in degrees from 0 to 360. Default is 0 degrees.
|
||||
-- @param #number Heading (Optional) Heading The heading of the static in degrees. Default is 0 degrees.
|
||||
-- @param #string NewName (Optional) The name of the new static.
|
||||
-- @return #SPAWNSTATIC
|
||||
function SPAWNSTATIC:SpawnFromCoordinate(Coordinate, Heading, NewName) --R2.4
|
||||
self:F( { PointVec2, Heading, NewName } )
|
||||
|
||||
local StaticTemplate, CoalitionID, CategoryID, CountryID = _DATABASE:GetStaticGroupTemplate( self.SpawnTemplatePrefix )
|
||||
|
||||
if StaticTemplate then
|
||||
|
||||
Heading=Heading or 0
|
||||
|
||||
local StaticUnitTemplate = StaticTemplate.units[1]
|
||||
|
||||
StaticUnitTemplate.x = Coordinate.x
|
||||
StaticUnitTemplate.y = Coordinate.z
|
||||
StaticUnitTemplate.alt = Coordinate.y
|
||||
|
||||
StaticTemplate.route = nil
|
||||
StaticTemplate.groupId = nil
|
||||
|
||||
StaticTemplate.name = NewName or string.format("%s#%05d", self.SpawnTemplatePrefix, self.SpawnIndex )
|
||||
StaticUnitTemplate.name = StaticTemplate.name
|
||||
StaticUnitTemplate.heading = ( Heading / 180 ) * math.pi
|
||||
|
||||
_DATABASE:_RegisterStaticTemplate( StaticTemplate, CoalitionID, CategoryID, CountryID)
|
||||
|
||||
self:F({StaticTemplate = StaticTemplate})
|
||||
-- @return Wrapper.Static#STATIC The spawned STATIC object.
|
||||
function SPAWNSTATIC:SpawnFromCoordinate(Coordinate, Heading, NewName)
|
||||
|
||||
local Static = coalition.addStaticObject( self.CountryID or CountryID, StaticTemplate.units[1] )
|
||||
|
||||
self.SpawnIndex = self.SpawnIndex + 1
|
||||
|
||||
return _DATABASE:FindStatic(Static:getName())
|
||||
-- Set up coordinate.
|
||||
self.InitStaticCoordinate=Coordinate
|
||||
|
||||
if Heading then
|
||||
self.InitStaticHeading=Heading
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
--- Respawns the original @{Static}.
|
||||
-- @param #SPAWNSTATIC self
|
||||
-- @param #number delay Delay before respawn in seconds.
|
||||
-- @return #SPAWNSTATIC
|
||||
function SPAWNSTATIC:ReSpawn(delay)
|
||||
|
||||
if delay and delay>0 then
|
||||
self:ScheduleOnce(delay, SPAWNSTATIC.ReSpawn, self)
|
||||
else
|
||||
|
||||
local StaticTemplate, CoalitionID, CategoryID, CountryID = _DATABASE:GetStaticGroupTemplate( self.SpawnTemplatePrefix )
|
||||
|
||||
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 nil
|
||||
if NewName then
|
||||
self.InitStaticName=NewName
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Creates the original @{Static} at a POINT_VEC2.
|
||||
-- @param #SPAWNSTATIC self
|
||||
-- @param Core.Point#COORDINATE Coordinate The 2D coordinate where to spawn the static.
|
||||
-- @param #number Heading The heading of the static, which is a number in degrees from 0 to 360.
|
||||
-- @return #SPAWNSTATIC
|
||||
function SPAWNSTATIC:ReSpawnAt( Coordinate, Heading )
|
||||
|
||||
local StaticTemplate, CoalitionID, CategoryID, CountryID = _DATABASE:GetStaticGroupTemplate( self.SpawnTemplatePrefix )
|
||||
|
||||
if StaticTemplate then
|
||||
|
||||
local StaticUnitTemplate = StaticTemplate.units[1]
|
||||
|
||||
StaticUnitTemplate.x = Coordinate.x
|
||||
StaticUnitTemplate.y = Coordinate.z
|
||||
|
||||
StaticUnitTemplate.heading = Heading and ( ( Heading / 180 ) * math.pi ) or StaticTemplate.heading
|
||||
|
||||
local Static = coalition.addStaticObject( self.CountryID or CountryID, StaticTemplate.units[1] )
|
||||
|
||||
return _DATABASE:FindStatic(Static:getName())
|
||||
end
|
||||
|
||||
return nil
|
||||
return self:_SpawnStatic(self.TemplateStaticUnit, self.CountryID)
|
||||
end
|
||||
|
||||
|
||||
--- Creates a new @{Static} from a @{Zone}.
|
||||
-- @param #SPAWNSTATIC self
|
||||
-- @param Core.Zone#ZONE_BASE Zone The Zone where to spawn the static.
|
||||
-- @param #number Heading The heading of the static, which is a number in degrees from 0 to 360.
|
||||
-- @param #string NewName (optional) The name of the new static.
|
||||
-- @return #SPAWNSTATIC
|
||||
function SPAWNSTATIC:SpawnFromZone( Zone, Heading, NewName ) --R2.1
|
||||
self:F( { Zone, Heading, NewName } )
|
||||
-- @param #number Heading (Optional)The heading of the static in degrees. Default is the heading of the template.
|
||||
-- @param #string NewName (Optional) The name of the new static.
|
||||
-- @return Wrapper.Static#STATIC The static spawned.
|
||||
function SPAWNSTATIC:SpawnFromZone(Zone, Heading, NewName)
|
||||
|
||||
-- Spawn the new static at the center of the zone.
|
||||
local Static = self:SpawnFromPointVec2( Zone:GetPointVec2(), Heading, NewName )
|
||||
|
||||
return Static
|
||||
end
|
||||
|
||||
--- Spawns a new static using a given template. Additionally, the country ID needs to be specified, which also determines the coalition of the spawned static.
|
||||
-- @param #SPAWNSTATIC self
|
||||
-- @param #SPAWNSTATIC.TemplateData Template Spawn unit template.
|
||||
-- @param #number CountryID The country ID.
|
||||
-- @return Wrapper.Static#STATIC The static spawned.
|
||||
function SPAWNSTATIC:_SpawnStatic(Template, CountryID)
|
||||
|
||||
Template=Template or {}
|
||||
|
||||
local CountryID=CountryID or self.CountryID
|
||||
|
||||
if self.InitStaticType then
|
||||
Template.type=self.InitStaticType
|
||||
end
|
||||
|
||||
if self.InitStaticCategory then
|
||||
Template.category=self.InitStaticCategory
|
||||
end
|
||||
|
||||
if self.InitStaticCoordinate then
|
||||
Template.x = self.InitStaticCoordinate.x
|
||||
Template.y = self.InitStaticCoordinate.z
|
||||
Template.alt = self.InitStaticCoordinate.y
|
||||
end
|
||||
|
||||
if self.InitStaticHeading then
|
||||
Template.heading = math.rad(self.InitStaticHeading)
|
||||
end
|
||||
|
||||
if self.InitStaticShape then
|
||||
Template.shape_name=self.InitStaticShape
|
||||
end
|
||||
|
||||
if self.InitStaticLivery then
|
||||
Template.livery_id=self.InitStaticLivery
|
||||
end
|
||||
|
||||
if self.InitDead~=nil then
|
||||
Template.dead=self.InitDead
|
||||
end
|
||||
|
||||
if self.InitCargo~=nil then
|
||||
Template.isCargo=self.InitCargo
|
||||
end
|
||||
|
||||
if self.InitLinkUnit then
|
||||
Template.linkUnit=self.InitLinkUnit:GetID()
|
||||
Template.linkOffset=true
|
||||
Template.offsets={}
|
||||
Template.offsets.y=self.InitOffsetY
|
||||
Template.offsets.x=self.InitOffsetX
|
||||
Template.offsets.angle=self.InitOffsetAngle and math.rad(self.InitOffsetAngle) or 0
|
||||
end
|
||||
|
||||
if self.InitFarp then
|
||||
Template.heliport_callsign_id = self.InitFarpCallsignID
|
||||
Template.heliport_frequency = self.InitFarpFreq
|
||||
Template.heliport_modulation = self.InitFarpModu
|
||||
Template.unitId=nil
|
||||
end
|
||||
|
||||
-- Increase spawn index counter.
|
||||
self.SpawnIndex = self.SpawnIndex + 1
|
||||
|
||||
-- Name of the spawned static.
|
||||
Template.name = self.InitStaticName or string.format("%s#%05d", self.SpawnTemplatePrefix, self.SpawnIndex)
|
||||
|
||||
-- Add and register the new static.
|
||||
local mystatic=_DATABASE:AddStatic(Template.name)
|
||||
|
||||
-- Debug output.
|
||||
self:T(Template)
|
||||
|
||||
-- Add static to the game.
|
||||
local Static=nil
|
||||
|
||||
if self.InitFarp then
|
||||
|
||||
local TemplateGroup={}
|
||||
TemplateGroup.units={}
|
||||
TemplateGroup.units[1]=Template
|
||||
|
||||
TemplateGroup.visible=true
|
||||
TemplateGroup.hidden=false
|
||||
TemplateGroup.x=Template.x
|
||||
TemplateGroup.y=Template.y
|
||||
TemplateGroup.name=Template.name
|
||||
|
||||
self:T("Spawning FARP")
|
||||
self:T({Template=Template})
|
||||
self:T({TemplateGroup=TemplateGroup})
|
||||
|
||||
-- ED's dirty way to spawn FARPS.
|
||||
Static=coalition.addGroup(CountryID, -1, TemplateGroup)
|
||||
else
|
||||
Static=coalition.addStaticObject(CountryID, Template)
|
||||
end
|
||||
|
||||
return mystatic
|
||||
end
|
||||
|
||||
@@ -341,7 +341,7 @@ do
|
||||
-- @return #SPOT
|
||||
function SPOT:onafterLaseOff( From, Event, To )
|
||||
|
||||
self:F( {"Stopped lasing for ", self.Target:GetName() , SpotIR = self.SportIR, SpotLaser = self.SpotLaser } )
|
||||
self:F( {"Stopped lasing for ", self.Target and self.Target:GetName() or "coord", SpotIR = self.SportIR, SpotLaser = self.SpotLaser } )
|
||||
|
||||
self.Lasing = false
|
||||
|
||||
|
||||
292
Moose Development/Moose/Core/Timer.lua
Normal file
292
Moose Development/Moose/Core/Timer.lua
Normal file
@@ -0,0 +1,292 @@
|
||||
--- **Core** - Timer scheduler.
|
||||
--
|
||||
-- **Main Features:**
|
||||
--
|
||||
-- * Delay function calls
|
||||
-- * Easy set up and little overhead
|
||||
-- * Set start, stop and time interval
|
||||
-- * Define max function calls
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **funkyfranky**
|
||||
-- @module Core.Timer
|
||||
-- @image Core_Scheduler.JPG
|
||||
|
||||
|
||||
--- TIMER class.
|
||||
-- @type TIMER
|
||||
-- @field #string ClassName Name of the class.
|
||||
-- @field #string lid Class id string for output to DCS log file.
|
||||
-- @field #number tid Timer ID returned by the DCS API function.
|
||||
-- @field #number uid Unique ID of the timer.
|
||||
-- @field #function func Timer function.
|
||||
-- @field #table para Parameters passed to the timer function.
|
||||
-- @field #number Tstart Relative start time in seconds.
|
||||
-- @field #number Tstop Relative stop time in seconds.
|
||||
-- @field #number dT Time interval between function calls in seconds.
|
||||
-- @field #number ncalls Counter of function calls.
|
||||
-- @field #number ncallsMax Max number of function calls. If reached, timer is stopped.
|
||||
-- @field #boolean isrunning If `true`, timer is running. Else it was not started yet or was stopped.
|
||||
-- @extends Core.Base#BASE
|
||||
|
||||
--- *Better three hours too soon than a minute too late.* - William Shakespeare
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- # The TIMER Concept
|
||||
--
|
||||
-- The TIMER class is the little sister of the @{Core.Scheduler#SCHEDULER} class. It does the same thing but is a bit easier to use and has less overhead. It should be sufficient in many cases.
|
||||
--
|
||||
-- It provides an easy interface to the DCS [timer.scheduleFunction](https://wiki.hoggitworld.com/view/DCS_func_scheduleFunction).
|
||||
--
|
||||
-- # Construction
|
||||
--
|
||||
-- A new TIMER is created by the @{#TIMER.New}(*func*, *...*) function
|
||||
--
|
||||
-- local mytimer=TIMER:New(myfunction, a, b)
|
||||
--
|
||||
-- The first parameter *func* is the function that is called followed by the necessary comma separeted parameters that are passed to that function.
|
||||
--
|
||||
-- ## Starting the Timer
|
||||
--
|
||||
-- The timer is started by the @{#TIMER.Start}(*Tstart*, *dT*, *Duration*) function
|
||||
--
|
||||
-- mytimer:Start(5, 1, 20)
|
||||
--
|
||||
-- where
|
||||
--
|
||||
-- * *Tstart* is the relative start time in seconds. In the example, the first function call happens after 5 sec.
|
||||
-- * *dT* is the time interval between function calls in seconds. Above, the function is called once per second.
|
||||
-- * *Duration* is the duration in seconds after which the timer is stopped. This is relative to the start time. Here, the timer will run for 20 seconds.
|
||||
--
|
||||
-- Note that
|
||||
--
|
||||
-- * if *Tstart* is not specified (*nil*), the first function call happens immediately, i.e. after one millisecond.
|
||||
-- * if *dT* is not specified (*nil*), the function is called only once.
|
||||
-- * if *Duration* is not specified (*nil*), the timer runs forever or until stopped manually or until the max function calls are reached (see below).
|
||||
--
|
||||
-- For example,
|
||||
--
|
||||
-- mytimer:Start(3) -- Will call the function once after 3 seconds.
|
||||
-- mytimer:Start(nil, 0.5) -- Will call right now and then every 0.5 sec until all eternity.
|
||||
-- mytimer:Start(nil, 2.0, 20) -- Will call right now and then every 2.0 sec for 20 sec.
|
||||
-- mytimer:Start(1.0, nil, 10) -- Does not make sense as the function is only called once anyway.
|
||||
--
|
||||
-- ## Stopping the Timer
|
||||
--
|
||||
-- The timer can be stopped manually by the @{#TIMER.Stop}(*Delay*) function
|
||||
--
|
||||
-- mytimer:Stop()
|
||||
--
|
||||
-- If the optional paramter *Delay* is specified, the timer is stopped after *delay* seconds.
|
||||
--
|
||||
-- ## Limit Function Calls
|
||||
--
|
||||
-- The timer can be stopped after a certain amount of function calles with the @{#TIMER.SetMaxFunctionCalls}(*Nmax*) function
|
||||
--
|
||||
-- mytimer:SetMaxFunctionCalls(20)
|
||||
--
|
||||
-- where *Nmax* is the number of calls after which the timer is stopped, here 20.
|
||||
--
|
||||
-- For example,
|
||||
--
|
||||
-- mytimer:SetMaxFunctionCalls(66):Start(1.0, 0.1)
|
||||
--
|
||||
-- will start the timer after one second and call the function every 0.1 seconds. Once the function has been called 66 times, the timer is stopped.
|
||||
--
|
||||
--
|
||||
-- @field #TIMER
|
||||
TIMER = {
|
||||
ClassName = "TIMER",
|
||||
lid = nil,
|
||||
}
|
||||
|
||||
--- Timer ID.
|
||||
_TIMERID=0
|
||||
|
||||
--- Timer data base.
|
||||
--_TIMERDB={}
|
||||
|
||||
--- TIMER class version.
|
||||
-- @field #string version
|
||||
TIMER.version="0.1.1"
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- TODO list
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
-- TODO: A lot.
|
||||
-- TODO: Write docs.
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- Constructor
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
--- Create a new TIMER object.
|
||||
-- @param #TIMER self
|
||||
-- @param #function Function The function to call.
|
||||
-- @param ... Parameters passed to the function if any.
|
||||
-- @return #TIMER self
|
||||
function TIMER:New(Function, ...)
|
||||
|
||||
-- Inherit BASE.
|
||||
local self=BASE:Inherit(self, BASE:New()) --#TIMER
|
||||
|
||||
-- Function to call.
|
||||
self.func=Function
|
||||
|
||||
-- Function arguments.
|
||||
self.para=arg or {}
|
||||
|
||||
-- Number of function calls.
|
||||
self.ncalls=0
|
||||
|
||||
-- Not running yet.
|
||||
self.isrunning=false
|
||||
|
||||
-- Increase counter
|
||||
_TIMERID=_TIMERID+1
|
||||
|
||||
-- Set UID.
|
||||
self.uid=_TIMERID
|
||||
|
||||
-- Log id.
|
||||
self.lid=string.format("TIMER UID=%d | ", self.uid)
|
||||
|
||||
-- Add to DB.
|
||||
--_TIMERDB[self.uid]=self
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Create a new TIMER object.
|
||||
-- @param #TIMER self
|
||||
-- @param #number Tstart Relative start time in seconds.
|
||||
-- @param #number dT Interval between function calls in seconds. If not specified `nil`, the function is called only once.
|
||||
-- @param #number Duration Time in seconds for how long the timer is running. If not specified `nil`, the timer runs forever or until stopped manually by the `TIMER:Stop()` function.
|
||||
-- @return #TIMER self
|
||||
function TIMER:Start(Tstart, dT, Duration)
|
||||
|
||||
-- Current time.
|
||||
local Tnow=timer.getTime()
|
||||
|
||||
-- Start time in sec.
|
||||
self.Tstart=Tstart and Tnow+Tstart or Tnow+0.001 -- one millisecond delay if Tstart=nil
|
||||
|
||||
-- Set time interval.
|
||||
self.dT=dT
|
||||
|
||||
-- Stop time.
|
||||
if Duration then
|
||||
self.Tstop=self.Tstart+Duration
|
||||
end
|
||||
|
||||
-- Call DCS timer function.
|
||||
self.tid=timer.scheduleFunction(self._Function, self, self.Tstart)
|
||||
|
||||
-- Set log id.
|
||||
self.lid=string.format("TIMER UID=%d/%d | ", self.uid, self.tid)
|
||||
|
||||
-- Is now running.
|
||||
self.isrunning=true
|
||||
|
||||
-- Debug info.
|
||||
self:T(self.lid..string.format("Starting Timer in %.3f sec, dT=%s, Tstop=%s", self.Tstart-Tnow, tostring(self.dT), tostring(self.Tstop)))
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Stop the timer by removing the timer function.
|
||||
-- @param #TIMER self
|
||||
-- @param #number Delay (Optional) Delay in seconds, before the timer is stopped.
|
||||
-- @return #TIMER self
|
||||
function TIMER:Stop(Delay)
|
||||
|
||||
if Delay and Delay>0 then
|
||||
|
||||
self.Tstop=timer.getTime()+Delay
|
||||
|
||||
else
|
||||
|
||||
if self.tid then
|
||||
|
||||
-- Remove timer function.
|
||||
self:T(self.lid..string.format("Stopping timer by removing timer function after %d calls!", self.ncalls))
|
||||
timer.removeFunction(self.tid)
|
||||
|
||||
-- Not running any more.
|
||||
self.isrunning=false
|
||||
|
||||
-- Remove DB entry.
|
||||
--_TIMERDB[self.uid]=nil
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set max number of function calls. When the function has been called this many times, the TIMER is stopped.
|
||||
-- @param #TIMER self
|
||||
-- @param #number Nmax Set number of max function calls.
|
||||
-- @return #TIMER self
|
||||
function TIMER:SetMaxFunctionCalls(Nmax)
|
||||
self.ncallsMax=Nmax
|
||||
return self
|
||||
end
|
||||
|
||||
--- Check if the timer has been started and was not stopped.
|
||||
-- @param #TIMER self
|
||||
-- @return #boolean If `true`, the timer is running.
|
||||
function TIMER:IsRunning()
|
||||
return self.isrunning
|
||||
end
|
||||
|
||||
--- Call timer function.
|
||||
-- @param #TIMER self
|
||||
-- @param #number time DCS model time in seconds.
|
||||
-- @return #number Time when the function is called again or `nil` if the timer is stopped.
|
||||
function TIMER:_Function(time)
|
||||
|
||||
-- Call function.
|
||||
self.func(unpack(self.para))
|
||||
|
||||
-- Increase number of calls.
|
||||
self.ncalls=self.ncalls+1
|
||||
|
||||
-- Next time.
|
||||
local Tnext=self.dT and time+self.dT or nil
|
||||
|
||||
-- Check if we stop the timer.
|
||||
local stopme=false
|
||||
if Tnext==nil then
|
||||
-- No next time.
|
||||
self:T(self.lid..string.format("No next time as dT=nil ==> Stopping timer after %d function calls", self.ncalls))
|
||||
stopme=true
|
||||
elseif self.Tstop and Tnext>self.Tstop then
|
||||
-- Stop time passed.
|
||||
self:T(self.lid..string.format("Stop time passed %.3f > %.3f ==> Stopping timer after %d function calls", Tnext, self.Tstop, self.ncalls))
|
||||
stopme=true
|
||||
elseif self.ncallsMax and self.ncalls>=self.ncallsMax then
|
||||
-- Number of max function calls reached.
|
||||
self:T(self.lid..string.format("Max function calls Nmax=%d reached ==> Stopping timer after %d function calls", self.ncallsMax, self.ncalls))
|
||||
stopme=true
|
||||
end
|
||||
|
||||
if stopme then
|
||||
-- Remove timer function.
|
||||
self:Stop()
|
||||
return nil
|
||||
else
|
||||
-- Call again in Tnext seconds.
|
||||
return Tnext
|
||||
end
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
@@ -69,7 +69,8 @@ do -- UserFlag
|
||||
|
||||
if Delay and Delay>0 then
|
||||
self:ScheduleOnce(Delay, USERFLAG.Set, self, Number)
|
||||
else
|
||||
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
|
||||
|
||||
|
||||
@@ -95,6 +95,11 @@ do -- world
|
||||
--- Returns a table of mark panels indexed numerically that are present within the mission. See [hoggit](https://wiki.hoggitworld.com/view/DCS_func_getMarkPanels)
|
||||
-- @function [parent=#world] getMarkPanels
|
||||
-- @return #table Table of marks.
|
||||
|
||||
--- Returns a table of DCS airbase objects.
|
||||
-- @function [parent=#world] getAirbases
|
||||
-- @param #number coalitionId The coalition side number ID. Default is all airbases are returned.
|
||||
-- @return #table Table of DCS airbase objects.
|
||||
|
||||
end -- world
|
||||
|
||||
@@ -360,7 +365,7 @@ do -- Types
|
||||
|
||||
--- Time is given in seconds.
|
||||
-- @type Time
|
||||
-- @extends #number
|
||||
-- @extends #number Time in seconds.
|
||||
|
||||
--- Model time is the time that drives the simulation. Model time may be stopped, accelerated and decelerated relative real time.
|
||||
-- @type ModelTime
|
||||
@@ -368,20 +373,20 @@ do -- Types
|
||||
|
||||
--- Mission time is a model time plus time of the mission start.
|
||||
-- @type MissionTime
|
||||
-- @extends #number
|
||||
-- @extends #number Time in seconds.
|
||||
|
||||
|
||||
--- Distance is given in meters.
|
||||
-- @type Distance
|
||||
-- @extends #number
|
||||
-- @extends #number Distance in meters.
|
||||
|
||||
--- Angle is given in radians.
|
||||
-- @type Angle
|
||||
-- @extends #number
|
||||
-- @extends #number Angle in radians.
|
||||
|
||||
--- Azimuth is an angle of rotation around world axis y counter-clockwise.
|
||||
-- @type Azimuth
|
||||
-- @extends #number
|
||||
-- @extends #number Angle in radians.
|
||||
|
||||
--- Mass is given in kilograms.
|
||||
-- @type Mass
|
||||
@@ -401,15 +406,15 @@ do -- Types
|
||||
|
||||
--- Position is a composite structure. It consists of both coordinate vector and orientation matrix. Position3 (also known as "Pos3" for short) is a table that has following format:
|
||||
-- @type Position3
|
||||
-- @field #Vec3 p
|
||||
-- @field #Vec3 x
|
||||
-- @field #Vec3 y
|
||||
-- @field #Vec3 z
|
||||
-- @field #Vec3 p 3D position vector.
|
||||
-- @field #Vec3 x Orientation component of vector pointing East.
|
||||
-- @field #Vec3 y Orientation component of vector pointing up.
|
||||
-- @field #Vec3 z Orientation component of vector pointing North.
|
||||
|
||||
--- 3-dimensional box.
|
||||
-- @type Box3
|
||||
-- @field #Vec3 min
|
||||
-- @field #Vec3 max
|
||||
-- @field #Vec3 min Min.
|
||||
-- @field #Vec3 max Max
|
||||
|
||||
--- Each object belongs to a type. Object type is a named couple of properties those independent of mission and common for all units of the same type. Name of unit type is a string. Samples of unit type: "Su-27", "KAMAZ" and "M2 Bradley".
|
||||
-- @type TypeName
|
||||
@@ -514,7 +519,7 @@ do -- Object
|
||||
--- Returns object coordinates for current time.
|
||||
-- @function [parent=#Object] getPoint
|
||||
-- @param #Object self
|
||||
-- @return #Vec3
|
||||
-- @return #Vec3 3D position vector with x,y,z components.
|
||||
|
||||
--- Returns object position for current time.
|
||||
-- @function [parent=#Object] getPosition
|
||||
@@ -524,7 +529,7 @@ do -- Object
|
||||
--- Returns the unit's velocity vector.
|
||||
-- @function [parent=#Object] getVelocity
|
||||
-- @param #Object self
|
||||
-- @return #Vec3
|
||||
-- @return #Vec3 3D velocity vector.
|
||||
|
||||
--- Returns true if the unit is in air.
|
||||
-- @function [parent=#Object] inAir
|
||||
|
||||
@@ -693,7 +693,7 @@ ARTY.db={
|
||||
|
||||
--- Arty script version.
|
||||
-- @field #string version
|
||||
ARTY.version="1.1.7"
|
||||
ARTY.version="1.2.0"
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
@@ -1146,12 +1146,11 @@ end
|
||||
-- @param alias (Optional) Alias name the group will be calling itself when sending messages. Default is the group name.
|
||||
-- @return #ARTY ARTY object or nil if group does not exist or is not a ground or naval group.
|
||||
function ARTY:NewFromCargoGroup(cargogroup, alias)
|
||||
BASE:F2({cargogroup=cargogroup, alias=alias})
|
||||
|
||||
if cargogroup then
|
||||
BASE:T(self.lid..string.format("ARTY script version %s. Added CARGO group %s.", ARTY.version, cargogroup:GetName()))
|
||||
BASE:T(string.format("ARTY script version %s. Added CARGO group %s.", ARTY.version, cargogroup:GetName()))
|
||||
else
|
||||
BASE:E(self.lid.."ERROR: Requested ARTY CARGO GROUP does not exist! (Has to be a MOOSE CARGO(!) group.)")
|
||||
BASE:E("ERROR: Requested ARTY CARGO GROUP does not exist! (Has to be a MOOSE CARGO(!) group.)")
|
||||
return nil
|
||||
end
|
||||
|
||||
@@ -2766,38 +2765,32 @@ function ARTY:onafterStatus(Controllable, From, Event, To)
|
||||
self:_EventFromTo("onafterStatus", Event, From, To)
|
||||
|
||||
-- Get ammo.
|
||||
local ntot, nshells, nrockets, nmissiles=self:GetAmmo()
|
||||
local nammo, nshells, nrockets, nmissiles=self:GetAmmo()
|
||||
|
||||
-- We have a cargo group ==> check if group was loaded into a carrier.
|
||||
if self.iscargo and self.cargogroup then
|
||||
if self.cargogroup:IsLoaded() and not self:is("InTransit") then
|
||||
-- Group is now InTransit state. Current target is canceled.
|
||||
self:T(self.lid..string.format("Group %s has been loaded into a carrier and is now transported.", self.alias))
|
||||
self:Loaded()
|
||||
elseif self.cargogroup:IsUnLoaded() then
|
||||
-- Group has been unloaded and is combat ready again.
|
||||
self:T(self.lid..string.format("Group %s has been unloaded from the carrier.", self.alias))
|
||||
self:UnLoaded()
|
||||
end
|
||||
end
|
||||
|
||||
-- FSM state.
|
||||
local fsmstate=self:GetState()
|
||||
self:I(self.lid..string.format("Status %s, Ammo total=%d: shells=%d [smoke=%d, illu=%d, nukes=%d*%.3f kT], rockets=%d, missiles=%d", fsmstate, ntot, nshells, self.Nsmoke, self.Nillu, self.Nukes, self.nukewarhead/1000000, nrockets, nmissiles))
|
||||
self:T(self.lid..string.format("Status %s, Ammo total=%d: shells=%d [smoke=%d, illu=%d, nukes=%d*%.3f kT], rockets=%d, missiles=%d", fsmstate, nammo, nshells, self.Nsmoke, self.Nillu, self.Nukes, self.nukewarhead/1000000, nrockets, nmissiles))
|
||||
|
||||
if self.Controllable and self.Controllable:IsAlive() then
|
||||
|
||||
-- We have a cargo group ==> check if group was loaded into a carrier.
|
||||
if self.cargogroup then
|
||||
if self.cargogroup:IsLoaded() and not self:is("InTransit") then
|
||||
-- Group is now InTransit state. Current target is canceled.
|
||||
self:T(self.lid..string.format("Group %s has been loaded into a carrier and is now transported.", self.alias))
|
||||
self:Loaded()
|
||||
elseif self.cargogroup:IsUnLoaded() then
|
||||
-- Group has been unloaded and is combat ready again.
|
||||
self:T(self.lid..string.format("Group %s has been unloaded from the carrier.", self.alias))
|
||||
self:UnLoaded()
|
||||
end
|
||||
end
|
||||
|
||||
-- Debug current status info.
|
||||
if self.Debug then
|
||||
self:_StatusReport()
|
||||
end
|
||||
|
||||
-- Group is being transported as cargo ==> skip everything and check again in 5 seconds.
|
||||
if self:is("InTransit") then
|
||||
self:__Status(-5)
|
||||
return
|
||||
end
|
||||
|
||||
-- Group on the move.
|
||||
if self:is("Moving") then
|
||||
self:T2(self.lid..string.format("%s: Moving", Controllable:GetName()))
|
||||
@@ -2884,7 +2877,7 @@ function ARTY:onafterStatus(Controllable, From, Event, To)
|
||||
end
|
||||
|
||||
-- Get ammo.
|
||||
local nammo, nshells, nrockets, nmissiles=self:GetAmmo()
|
||||
--local nammo, nshells, nrockets, nmissiles=self:GetAmmo()
|
||||
|
||||
-- Check if we have a target in the queue for which weapons are still available.
|
||||
local gotsome=false
|
||||
@@ -2914,9 +2907,23 @@ function ARTY:onafterStatus(Controllable, From, Event, To)
|
||||
-- Call status again in ~10 sec.
|
||||
self:__Status(self.StatusInterval)
|
||||
|
||||
elseif self.iscargo then
|
||||
|
||||
-- We have a cargo group ==> check if group was loaded into a carrier.
|
||||
if self.cargogroup and self.cargogroup:IsAlive() then
|
||||
|
||||
-- Group is being transported as cargo ==> skip everything and check again in 5 seconds.
|
||||
if self:is("InTransit") then
|
||||
self:__Status(-5)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
else
|
||||
self:E(self.lid..string.format("Arty group %s is not alive!", self.groupname))
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
@@ -4814,7 +4821,8 @@ function ARTY:_CheckShootingStarted()
|
||||
end
|
||||
|
||||
-- Check if we waited long enough and no shot was fired.
|
||||
if dt > self.WaitForShotTime and self.Nshots==0 then
|
||||
--if dt > self.WaitForShotTime and self.Nshots==0 then
|
||||
if dt > self.WaitForShotTime and (self.Nshots==0 or self.currentTarget.nshells >= self.Nshots) then --https://github.com/FlightControl-Master/MOOSE/issues/1356
|
||||
|
||||
-- Debug info.
|
||||
self:T(self.lid..string.format("%s, no shot event after %d seconds. Removing current target %s from list.", self.groupname, self.WaitForShotTime, name))
|
||||
|
||||
841
Moose Development/Moose/Functional/Mantis.lua
Normal file
841
Moose Development/Moose/Functional/Mantis.lua
Normal file
@@ -0,0 +1,841 @@
|
||||
--- **Functional** -- Modular, Automatic and Network capable Targeting and Interception System for Air Defenses
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- **MANTIS** - Moose derived Modular, Automatic and Network capable Targeting and Interception System
|
||||
-- Controls a network of SAM sites. Use detection to switch on the AA site closest to the enemy
|
||||
-- Leverage evasiveness from SEAD
|
||||
-- Leverage attack range setup added by DCS in 11/20
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ## Missions:
|
||||
--
|
||||
-- ### [MANTIS - Modular, Automatic and Network capable Targeting and Interception System](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/MTS%20-%20Mantis/MTS-010%20-%20Basic%20Mantis%20Demo)
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author : **applevangelist **
|
||||
--
|
||||
-- @module Functional.Mantis
|
||||
-- @image Functional.Mantis.jpg
|
||||
|
||||
-- Date: Jan 2021
|
||||
|
||||
-------------------------------------------------------------------------
|
||||
--- **MANTIS** class, extends @{#Core.Base#BASE}
|
||||
-- @type MANTIS #MANTIS
|
||||
-- @field #string Classname
|
||||
-- @field #string name Name of this Mantis
|
||||
-- @field #string SAM_Templates_Prefix Prefix to build the #GROUP_SET for SAM sites
|
||||
-- @field @{#Core.Set#GROUP_SET} SAM_Group The SAM #GROUP_SET
|
||||
-- @field #string EWR_Templates_Prefix Prefix to build the #GROUP_SET for EWR group
|
||||
-- @field @{#Core.Set#GROUP_SET} EWR_Group The EWR #GROUP_SET
|
||||
-- @field @{#Core.Set#GROUP_SET} Adv_EWR_Group The EWR #GROUP_SET used for advanced mode
|
||||
-- @field #string HQ_Template_CC The ME name of the HQ object
|
||||
-- @field @{#Wrapper.Group#GROUP} HQ_CC The #GROUP object of the HQ
|
||||
-- @field #table SAM_Table Table of SAM sites
|
||||
-- @field #string lid Prefix for logging
|
||||
-- @field @{#Functional.Detection#DETECTION_AREAS} Detection The #DETECTION_AREAS object for EWR
|
||||
-- @field @{Functional.Detection#DETECTION_AREAS} AWACS_Detection The #DETECTION_AREAS object for AWACS
|
||||
-- @field #boolean debug Switch on extra messages
|
||||
-- @field #boolean verbose Switch on extra logging
|
||||
-- @field #number checkradius Radius of the SAM sites
|
||||
-- @field #number grouping Radius to group detected objects
|
||||
-- @field #number acceptrange Radius of the EWR detection
|
||||
-- @field #number detectinterval Interval in seconds for the target detection
|
||||
-- @field #number engagerange Firing engage range of the SAMs, see [https://wiki.hoggitworld.com/view/DCS_option_engagementRange]
|
||||
-- @field #boolean autorelocate Relocate HQ and EWR groups in random intervals. Note: You need to select units for this which are *actually mobile*
|
||||
-- @field #boolean advanced Use advanced mode, will decrease reactivity of MANTIS, if HQ and/or EWR network dies. Set SAMs to RED state if both are dead. Requires usage of an HQ object
|
||||
-- @field #number adv_ratio Percentage to use for advanced mode, defaults to 100%
|
||||
-- @field #number adv_state Advanced mode state tracker
|
||||
-- @field #boolean advAwacs Boolean switch to use Awacs as a separate detection stream
|
||||
-- @field #number awacsrange Detection range of an optional Awacs unit
|
||||
-- @extends @{#Core.Base#BASE}
|
||||
|
||||
|
||||
--- *The worst thing that can happen to a good cause is, not to be skillfully attacked, but to be ineptly defended.* - Frédéric Bastiat
|
||||
--
|
||||
-- Simple Class for a more intelligent Air Defense System
|
||||
--
|
||||
-- #MANTIS
|
||||
-- Moose derived Modular, Automatic and Network capable Targeting and Interception System.
|
||||
-- Controls a network of SAM sites. Use detection to switch on the AA site closest to the enemy.
|
||||
-- Leverage evasiveness from @{Functional.Sead#SEAD}.
|
||||
-- Leverage attack range setup added by DCS in 11/20.
|
||||
--
|
||||
-- Set up your SAM sites in the mission editor. Name the groups with common prefix like "Red SAM".
|
||||
-- Set up your EWR system in the mission editor. Name the groups with common prefix like "Red EWR". Can be e.g. AWACS or a combination of AWACS and Search Radars like e.g. EWR 1L13 etc.
|
||||
-- [optional] Set up your HQ. Can be any group, e.g. a command vehicle.
|
||||
--
|
||||
-- # 1. Basic tactical considerations when setting up your SAM sites
|
||||
--
|
||||
-- ## 1.1 Radar systems and AWACS
|
||||
--
|
||||
-- Typically, your setup should consist of EWR (early warning) radars to detect and track targets, accompanied by AWACS if your scenario forsees that. Ensure that your EWR radars have a good coverage of the area you want to track.
|
||||
-- **Location** is of highest importantance here. Whilst AWACS in DCS has almost the "all seeing eye", EWR don't have that. Choose your location wisely, against a mountain backdrop or inside a valley even the best EWR system
|
||||
-- doesn't work well. Prefer higher-up locations with a good view; use F7 in-game to check where you actually placed your EWR and have a look around. Apart from the obvious choice, do also consider other radar units
|
||||
-- for this role, most have "SR" (search radar) or "STR" (search and track radar) in their names, use the encyclopedia to see what they actually do.
|
||||
--
|
||||
-- ## 1.2 SAM sites
|
||||
--
|
||||
-- Typically your SAM should cover all attack ranges. The closer the enemy gets, the more systems you will need to deploy to defend your location. Use a combination of long-range systems like the SA-10/11, midrange like SA-6 and short-range like
|
||||
-- SA-2 for defense (Patriot, Hawk, Gepard, Blindfire for the blue side). For close-up defense and defense against HARMs or low-flying aircraft, helicopters it is also advisable to deploy SA-15 TOR systems, Shilka, Strela and Tunguska units, as well as manpads (Think Gepard, Avenger, Chaparral,
|
||||
-- Linebacker, Roland systems for the blue side). If possible, overlap ranges for mutual coverage.
|
||||
--
|
||||
-- ## 1.3 Typical problems
|
||||
--
|
||||
-- Often times, people complain because the detection cannot "see" oncoming targets and/or Mantis switches on too late. Three typial problems here are
|
||||
--
|
||||
-- * bad placement of radar units,
|
||||
-- * overestimation how far units can "see" and
|
||||
-- * not taking into account that a SAM site will take (e.g for a SA-6) 30-40 seconds between switching to RED, acquiring the target and firing.
|
||||
--
|
||||
-- An attacker doing 350knots will cover ca 180meters/second or thus more than 6km until the SA-6 fires. Use triggers zones and the ruler in the missione editor to understand distances and zones. Take into account that the ranges given by the circles
|
||||
-- in the mission editor are absolute maximum ranges; in-game this is rather 50-75% of that depending on the system. Fiddle with placement and options to see what works best for your scenario, and remember **everything in here is in meters**.
|
||||
--
|
||||
-- # 2. Start up your MANTIS with a basic setting
|
||||
--
|
||||
-- `myredmantis = MANTIS:New("myredmantis","Red SAM","Red EWR",nil,"red",false)`
|
||||
-- `myredmantis:Start()`
|
||||
--
|
||||
-- [optional] Use
|
||||
--
|
||||
-- * `MANTIS:SetEWRGrouping(radius)`
|
||||
-- * `MANTIS:SetEWRRange(radius)`
|
||||
-- * `MANTIS:SetSAMRadius(radius)`
|
||||
-- * `MANTIS:SetDetectInterval(interval)`
|
||||
-- * `MANTIS:SetAutoRelocate(hq, ewr)`
|
||||
--
|
||||
-- before starting #MANTIS to fine-tune your setup.
|
||||
--
|
||||
-- If you want to use a separate AWACS unit (default detection range: 250km) to support your EWR system, use e.g. the following setup:
|
||||
--
|
||||
-- `mybluemantis = MANTIS:New("bluemantis","Blue SAM","Blue EWR",nil,"blue",false,"Blue Awacs")`
|
||||
-- `mybluemantis:Start()`
|
||||
--
|
||||
-- # 3. Default settings
|
||||
--
|
||||
-- By default, the following settings are active:
|
||||
--
|
||||
-- * SAM_Templates_Prefix = "Red SAM" - SAM site group names in the mission editor begin with "Red SAM"
|
||||
-- * EWR_Templates_Prefix = "Red EWR" - EWR group names in the mission editor begin with "Red EWR" - can also be combined with an AWACS unit
|
||||
-- * checkradius = 25000 (meters) - SAMs will engage enemy flights, if they are within a 25km around each SAM site - `MANTIS:SetSAMRadius(radius)`
|
||||
-- * grouping = 5000 (meters) - Detection (EWR) will group enemy flights to areas of 5km for tracking - `MANTIS:SetEWRGrouping(radius)`
|
||||
-- * acceptrange = 80000 (meters) - Detection (EWR) will on consider flights inside a 80km radius - `MANTIS:SetEWRRange(radius)`
|
||||
-- * detectinterval = 30 (seconds) - MANTIS will decide every 30 seconds which SAM to activate - `MANTIS:SetDetectInterval(interval)`
|
||||
-- * engagerange = 75 (percent) - SAMs will only fire if flights are inside of a 75% radius of their max firerange - `MANTIS:SetSAMRange(range)`
|
||||
-- * dynamic = false - Group filtering is set to once, i.e. newly added groups will not be part of the setup by default - `MANTIS:New(name,samprefix,ewrprefix,hq,coaltion,dynamic)`
|
||||
-- * autorelocate = false - HQ and (mobile) EWR system will not relocate in random intervals between 30mins and 1 hour - `MANTIS:SetAutoRelocate(hq, ewr)`
|
||||
-- * debug = false - Debugging reports on screen are set to off - `MANTIS:Debug(onoff)`
|
||||
--
|
||||
-- # 4. Advanced Mode
|
||||
--
|
||||
-- Advanced mode will *decrease* reactivity of MANTIS, if HQ and/or EWR network dies. Awacs is counted as one EWR unit. It will set SAMs to RED state if both are dead. Requires usage of an **HQ** object and the **dynamic** option.
|
||||
-- E.g. `mymantis:SetAdvancedMode( true, 90 )`
|
||||
-- Use this option if you want to make use of or allow advanced SEAD tactics.
|
||||
--
|
||||
-- @field #MANTIS
|
||||
MANTIS = {
|
||||
ClassName = "MANTIS",
|
||||
name = "mymantis",
|
||||
SAM_Templates_Prefix = "",
|
||||
SAM_Group = nil,
|
||||
EWR_Templates_Prefix = "",
|
||||
EWR_Group = nil,
|
||||
Adv_EWR_Group = nil,
|
||||
HQ_Template_CC = "",
|
||||
HQ_CC = nil,
|
||||
SAM_Table = {},
|
||||
lid = "",
|
||||
Detection = nil,
|
||||
AWACS_Detection = nil,
|
||||
debug = false,
|
||||
checkradius = 25000,
|
||||
grouping = 5000,
|
||||
acceptrange = 80000,
|
||||
detectinterval = 30,
|
||||
engagerange = 75,
|
||||
autorelocate = false,
|
||||
advanced = false,
|
||||
adv_ratio = 100,
|
||||
adv_state = 0,
|
||||
AWACS_Prefix = "",
|
||||
advAwacs = false,
|
||||
verbose = false,
|
||||
awacsrange = 250000
|
||||
}
|
||||
|
||||
-----------------------------------------------------------------------
|
||||
-- MANTIS System
|
||||
-----------------------------------------------------------------------
|
||||
|
||||
do
|
||||
--- Function to instantiate a new object of class MANTIS
|
||||
--@param #MANTIS self
|
||||
--@param #string name Name of this MANTIS for reporting
|
||||
--@param #string samprefix Prefixes for the SAM groups from the ME, e.g. all groups starting with "Red Sam..."
|
||||
--@param #string ewrprefix Prefixes for the EWR groups from the ME, e.g. all groups starting with "Red EWR..."
|
||||
--@param #string hq Group name of your HQ (optional)
|
||||
--@param #string coaltion Coalition side of your setup, e.g. "blue", "red" or "neutral"
|
||||
--@param #boolean dynamic Use constant (true) filtering or just filter once (false, default) (optional)
|
||||
--@param #string awacs Group name of your Awacs (optional)
|
||||
--@return #MANTIS self
|
||||
function MANTIS:New(name,samprefix,ewrprefix,hq,coaltion,dynamic,awacs)
|
||||
|
||||
-- DONE: Create some user functions for these
|
||||
-- DONE: Make HQ useful
|
||||
-- DONE: Set SAMs to auto if EWR dies
|
||||
-- DONE: Refresh SAM table in dynamic mode
|
||||
-- DONE: Treat Awacs separately, since they might be >80km off site
|
||||
|
||||
self.name = name or "mymantis"
|
||||
self.SAM_Templates_Prefix = samprefix or "Red SAM"
|
||||
self.EWR_Templates_Prefix = ewrprefix or "Red EWR"
|
||||
self.HQ_Template_CC = hq or nil
|
||||
self.Coalition = coaltion or "red"
|
||||
self.SAM_Table = {}
|
||||
self.dynamic = dynamic or false
|
||||
self.checkradius = 25000
|
||||
self.grouping = 5000
|
||||
self.acceptrange = 80000
|
||||
self.detectinterval = 30
|
||||
self.engagerange = 75
|
||||
self.autorelocate = false
|
||||
self.autorelocateunits = { HQ = false, EWR = false}
|
||||
self.advanced = false
|
||||
self.adv_ratio = 100
|
||||
self.adv_state = 0
|
||||
self.verbose = false
|
||||
self.Adv_EWR_Group = nil
|
||||
self.AWACS_Prefix = awacs or nil
|
||||
self.awacsrange = 250000 --TODO: 250km, User Function to change
|
||||
if type(awacs) == "string" then
|
||||
self.advAwacs = true
|
||||
else
|
||||
self.advAwacs = false
|
||||
end
|
||||
|
||||
-- @field #string version
|
||||
self.version="0.3.6"
|
||||
env.info(string.format("***** Starting MANTIS Version %s *****", self.version))
|
||||
|
||||
-- Set the string id for output to DCS.log file.
|
||||
self.lid=string.format("MANTIS %s | ", self.name)
|
||||
|
||||
-- Debug trace.
|
||||
if self.debug then
|
||||
BASE:TraceOnOff(true)
|
||||
BASE:TraceClass(self.ClassName)
|
||||
--BASE:TraceClass("SEAD")
|
||||
BASE:TraceLevel(1)
|
||||
end
|
||||
|
||||
if self.dynamic then
|
||||
-- Set SAM SET_GROUP
|
||||
self.SAM_Group = SET_GROUP:New():FilterPrefixes(self.SAM_Templates_Prefix):FilterCoalitions(self.Coalition):FilterStart()
|
||||
-- Set EWR SET_GROUP
|
||||
self.EWR_Group = SET_GROUP:New():FilterPrefixes({self.SAM_Templates_Prefix,self.EWR_Templates_Prefix}):FilterCoalitions(self.Coalition):FilterStart()
|
||||
else
|
||||
-- Set SAM SET_GROUP
|
||||
self.SAM_Group = SET_GROUP:New():FilterPrefixes(self.SAM_Templates_Prefix):FilterCoalitions(self.Coalition):FilterOnce()
|
||||
-- Set EWR SET_GROUP
|
||||
self.EWR_Group = SET_GROUP:New():FilterPrefixes({self.SAM_Templates_Prefix,self.EWR_Templates_Prefix}):FilterCoalitions(self.Coalition):FilterOnce()
|
||||
end
|
||||
|
||||
-- set up CC
|
||||
if self.HQ_Template_CC then
|
||||
self.HQ_CC = GROUP:FindByName(self.HQ_Template_CC)
|
||||
end
|
||||
-- Inherit everything from BASE class.
|
||||
local self = BASE:Inherit(self, BASE:New()) -- #MANTIS
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
-----------------------------------------------------------------------
|
||||
-- MANTIS helper functions
|
||||
-----------------------------------------------------------------------
|
||||
|
||||
--- [internal] Function to get the self.SAM_Table
|
||||
-- @param #MANTIS self
|
||||
-- @return #table table
|
||||
function MANTIS:_GetSAMTable()
|
||||
return self.SAM_Table
|
||||
end
|
||||
|
||||
--- [internal] Function to set the self.SAM_Table
|
||||
-- @param #MANTIS self
|
||||
-- @return #MANTIS self
|
||||
function MANTIS:_SetSAMTable(table)
|
||||
self.SAM_Table = table
|
||||
return self
|
||||
end
|
||||
|
||||
--- Function to set the grouping radius of the detection in meters
|
||||
-- @param #MANTIS self
|
||||
-- @param #number radius Radius upon which detected objects will be grouped
|
||||
function MANTIS:SetEWRGrouping(radius)
|
||||
local radius = radius or 5000
|
||||
self.grouping = radius
|
||||
end
|
||||
|
||||
--- Function to set the detection radius of the EWR in meters
|
||||
-- @param #MANTIS self
|
||||
-- @param #number radius Radius of the EWR detection zone
|
||||
function MANTIS:SetEWRRange(radius)
|
||||
local radius = radius or 80000
|
||||
self.acceptrange = radius
|
||||
end
|
||||
|
||||
--- Function to set switch-on/off zone for the SAM sites in meters
|
||||
-- @param #MANTIS self
|
||||
-- @param #number radius Radius of the firing zone
|
||||
function MANTIS:SetSAMRadius(radius)
|
||||
local radius = radius or 25000
|
||||
self.checkradius = radius
|
||||
end
|
||||
|
||||
--- Function to set SAM firing engage range, 0-100 percent, e.g. 75
|
||||
-- @param #MANTIS self
|
||||
-- @param #number range Percent of the max fire range
|
||||
function MANTIS:SetSAMRange(range)
|
||||
local range = range or 75
|
||||
if range < 0 or range > 100 then
|
||||
range = 75
|
||||
end
|
||||
self.engagerange = range
|
||||
end
|
||||
|
||||
--- Function to set a new SAM firing engage range, use this method to adjust range while running MANTIS, e.g. for different setups day and night
|
||||
-- @param #MANTIS self
|
||||
-- @param #number range Percent of the max fire range
|
||||
function MANTIS:SetNewSAMRangeWhileRunning(range)
|
||||
local range = range or 75
|
||||
if range < 0 or range > 100 then
|
||||
range = 75
|
||||
end
|
||||
self.engagerange = range
|
||||
self:_RefreshSAMTable()
|
||||
self.mysead.EngagementRange = range
|
||||
end
|
||||
|
||||
--- Function to set switch-on/off the debug state
|
||||
-- @param #MANTIS self
|
||||
-- @param #boolean onoff Set true to switch on
|
||||
function MANTIS:Debug(onoff)
|
||||
local onoff = onoff or false
|
||||
self.debug = onoff
|
||||
end
|
||||
|
||||
--- Function to get the HQ object for further use
|
||||
-- @param #MANTIS self
|
||||
-- @return Wrapper.GROUP#GROUP The HQ #GROUP object or *nil* if it doesn't exist
|
||||
function MANTIS:GetCommandCenter()
|
||||
if self.HQ_CC then
|
||||
return self.HQ_CC
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
--- Function to set separate AWACS detection instance
|
||||
-- @param #MANTIS self
|
||||
-- @param #string prefix Name of the AWACS group in the mission editor
|
||||
function MANTIS:SetAwacs(prefix)
|
||||
if prefix ~= nil then
|
||||
if type(prefix) == "string" then
|
||||
self.AWACS_Prefix = prefix
|
||||
self.advAwacs = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Function to set the HQ object for further use
|
||||
-- @param #MANTIS self
|
||||
-- @param Wrapper.GROUP#GROUP The HQ #GROUP object to be set as HQ
|
||||
function MANTIS:SetCommandCenter(group)
|
||||
local group = group or nil
|
||||
if group ~= nil then
|
||||
if type(group) == "string" then
|
||||
self.HQ_CC = GROUP:FindByName(group)
|
||||
self.HQ_Template_CC = group
|
||||
else
|
||||
self.HQ_CC = group
|
||||
self.HQ_Template_CC = group:GetName()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Function to set the detection interval
|
||||
-- @param #MANTIS self
|
||||
-- @param #number interval The interval in seconds
|
||||
function MANTIS:SetDetectInterval(interval)
|
||||
local interval = interval or 30
|
||||
self.detectinterval = interval
|
||||
end
|
||||
|
||||
--- Function to set Advanded Mode
|
||||
-- @param #MANTIS self
|
||||
-- @param #boolean onoff If true, will activate Advanced Mode
|
||||
-- @param #number ratio [optional] Percentage to use for advanced mode, defaults to 100%
|
||||
-- @usage Advanced mode will *decrease* reactivity of MANTIS, if HQ and/or EWR network dies. Set SAMs to RED state if both are dead. Requires usage of an **HQ** object and the **dynamic** option.
|
||||
-- E.g. `mymantis:SetAdvancedMode(true, 90)`
|
||||
function MANTIS:SetAdvancedMode(onoff, ratio)
|
||||
self:F({onoff, ratio})
|
||||
local onoff = onoff or false
|
||||
local ratio = ratio or 100
|
||||
if (type(self.HQ_Template_CC) == "string") and onoff and self.dynamic then
|
||||
self.adv_ratio = ratio
|
||||
self.advanced = true
|
||||
self.adv_state = 0
|
||||
self.Adv_EWR_Group = SET_GROUP:New():FilterPrefixes(self.EWR_Templates_Prefix):FilterCoalitions(self.Coalition):FilterStart()
|
||||
env.info(string.format("***** Starting Advanced Mode MANTIS Version %s *****", self.version))
|
||||
else
|
||||
local text = self.lid.." Advanced Mode requires a HQ and dynamic to be set. Revisit your MANTIS:New() statement to add both."
|
||||
local m= MESSAGE:New(text,10,"MANTIS",true):ToAll()
|
||||
BASE:E(text)
|
||||
end
|
||||
end
|
||||
|
||||
--- [Internal] Function to check if HQ is alive
|
||||
-- @param #MANTIS self
|
||||
-- @return #boolean True if HQ is alive, else false
|
||||
function MANTIS:_CheckHQState()
|
||||
local text = self.lid.." Checking HQ State"
|
||||
self:T(text)
|
||||
local m= MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug)
|
||||
if self.verbose then env.info(text) end
|
||||
-- start check
|
||||
if self.advanced then
|
||||
local hq = self.HQ_Template_CC
|
||||
local hqgrp = GROUP:FindByName(hq)
|
||||
if hqgrp then
|
||||
if hqgrp:IsAlive() then -- ok we're on, hq exists and as alive
|
||||
env.info(self.lid.." HQ is alive!")
|
||||
return true
|
||||
else
|
||||
env.info(self.lid.." HQ is dead!")
|
||||
return false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- [Internal] Function to check if EWR is (at least partially) alive
|
||||
-- @param #MANTIS self
|
||||
-- @return #boolean True if EWR is alive, else false
|
||||
function MANTIS:_CheckEWRState()
|
||||
local text = self.lid.." Checking EWR State"
|
||||
self:F(text)
|
||||
local m= MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug)
|
||||
if self.verbose then env.info(text) end
|
||||
-- start check
|
||||
if self.advanced then
|
||||
local EWR_Group = self.Adv_EWR_Group
|
||||
--local EWR_Set = EWR_Group.Set
|
||||
local nalive = EWR_Group:CountAlive()
|
||||
if self.advAwacs then
|
||||
local awacs = GROUP:FindByName(self.AWACS_Prefix)
|
||||
if awacs ~= nil then
|
||||
if awacs:IsAlive() then
|
||||
nalive = nalive+1
|
||||
end
|
||||
end
|
||||
end
|
||||
env.info(self.lid..string.format(" No of EWR alive is %d", nalive))
|
||||
if nalive > 0 then
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- [Internal] Function to determine state of the advanced mode
|
||||
-- @param #MANTIS self
|
||||
-- @return #number Newly calculated interval
|
||||
-- @return #number Previous state for tracking 0, 1, or 2
|
||||
function MANTIS:_CheckAdvState()
|
||||
local text = self.lid.." Checking Advanced State"
|
||||
self:F(text)
|
||||
local m=MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug)
|
||||
if self.verbose then env.info(text) end
|
||||
-- start check
|
||||
local currstate = self.adv_state -- save curr state for comparison later
|
||||
local EWR_State = self:_CheckEWRState()
|
||||
local HQ_State = self:_CheckHQState()
|
||||
-- set state
|
||||
if EWR_State and HQ_State then -- both alive
|
||||
self.adv_state = 0 --everything is fine
|
||||
elseif EWR_State or HQ_State then -- one alive
|
||||
self.adv_state = 1 --slow down level 1
|
||||
else -- none alive
|
||||
self.adv_state = 2 --slow down level 2
|
||||
end
|
||||
-- calculate new detectioninterval
|
||||
local interval = self.detectinterval -- e.g. 30
|
||||
local ratio = self.adv_ratio / 100 -- e.g. 80/100 = 0.8
|
||||
ratio = ratio * self.adv_state -- e.g 0.8*2 = 1.6
|
||||
local newinterval = interval + (interval * ratio) -- e.g. 30+(30*1.6) = 78
|
||||
local text = self.lid..string.format(" Calculated OldState/NewState/Interval: %d / %d / %d", currstate, self.adv_state, newinterval)
|
||||
self:F(text)
|
||||
local m=MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug)
|
||||
if self.verbose then env.info(text) end
|
||||
return newinterval, currstate
|
||||
end
|
||||
|
||||
--- Function to set autorelocation for HQ and EWR objects. Note: Units must be actually mobile in DCS!
|
||||
-- @param #MANTIS self
|
||||
-- @param #boolean hq If true, will relocate HQ object
|
||||
-- @param #boolean ewr If true, will relocate EWR objects
|
||||
function MANTIS:SetAutoRelocate(hq, ewr)
|
||||
self:F({hq, ewr})
|
||||
local hqrel = hq or false
|
||||
local ewrel = ewr or false
|
||||
if hqrel or ewrel then
|
||||
self.autorelocate = true
|
||||
self.autorelocateunits = { HQ = hqrel, EWR = ewrel }
|
||||
self:T({self.autorelocate, self.autorelocateunits})
|
||||
end
|
||||
end
|
||||
|
||||
--- [Internal] Function to execute the relocation
|
||||
-- @param #MANTIS self
|
||||
function MANTIS:_RelocateGroups()
|
||||
self:T(self.lid.." Relocating Groups")
|
||||
local text = self.lid.." Relocating Groups"
|
||||
local m= MESSAGE:New(text,10,"MANTIS",true):ToAllIf(self.debug)
|
||||
if self.verbose then env.info(text) end
|
||||
if self.autorelocate then
|
||||
-- relocate HQ
|
||||
if self.autorelocateunits.HQ and self.HQ_CC then --only relocate if HQ exists
|
||||
local _hqgrp = self.HQ_CC
|
||||
self:T(self.lid.." Relocating HQ")
|
||||
local text = self.lid.." Relocating HQ"
|
||||
local m= MESSAGE:New(text,10,"MANTIS"):ToAll()
|
||||
_hqgrp:RelocateGroundRandomInRadius(20,500,true,true)
|
||||
end
|
||||
--relocate EWR
|
||||
-- TODO: maybe dependent on AlarmState? Observed: SA11 SR only relocates if no objects in reach
|
||||
if self.autorelocateunits.EWR then
|
||||
-- get EWR Group
|
||||
local EWR_GRP = SET_GROUP:New():FilterPrefixes(self.EWR_Templates_Prefix):FilterCoalitions(self.Coalition):FilterOnce()
|
||||
local EWR_Grps = EWR_GRP.Set --table of objects in SET_GROUP
|
||||
for _,_grp in pairs (EWR_Grps) do
|
||||
if _grp:IsGround() then
|
||||
self:T(self.lid.." Relocating EWR ".._grp:GetName())
|
||||
local text = self.lid.." Relocating EWR ".._grp:GetName()
|
||||
local m= MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug)
|
||||
if self.verbose then env.info(text) end
|
||||
_grp:RelocateGroundRandomInRadius(20,500,true,true)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Function to check if any object is in the given SAM zone
|
||||
-- @param #MANTIS self
|
||||
-- @param #table dectset Table of coordinates of detected items
|
||||
-- @param samcoordinate Core.Point#COORDINATE Coordinate object.
|
||||
-- @return #boolean True if in any zone, else false
|
||||
function MANTIS:CheckObjectInZone(dectset, samcoordinate)
|
||||
self:F(self.lid.."CheckObjectInZone Called")
|
||||
-- check if non of the coordinate is in the given defense zone
|
||||
local radius = self.checkradius
|
||||
local set = dectset
|
||||
for _,_coord in pairs (set) do
|
||||
local coord = _coord -- get current coord to check
|
||||
-- output for cross-check
|
||||
local dectstring = coord:ToStringLLDMS()
|
||||
local samstring = samcoordinate:ToStringLLDMS()
|
||||
local targetdistance = samcoordinate:DistanceFromPointVec2(coord)
|
||||
local text = string.format("Checking SAM at % s - Distance %d m - Target %s", samstring, targetdistance, dectstring)
|
||||
local m = MESSAGE:New(text,10,"Check"):ToAllIf(self.debug)
|
||||
if self.verbose then env.info(self.lid..text) end
|
||||
-- end output to cross-check
|
||||
if targetdistance <= radius then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
--- Function to start the detection via EWR groups
|
||||
-- @param #MANTIS self
|
||||
-- @return Functional.Detection #DETECTION_AREAS The running detection set
|
||||
function MANTIS:StartDetection()
|
||||
self:F(self.lid.."Starting Detection")
|
||||
|
||||
-- start detection
|
||||
local groupset = self.EWR_Group
|
||||
local grouping = self.grouping or 5000
|
||||
local acceptrange = self.acceptrange or 80000
|
||||
local interval = self.detectinterval or 60
|
||||
|
||||
--@param Functional.Detection #DETECTION_AREAS _MANTISdetection [internal] The MANTIS detection object
|
||||
_MANTISdetection = DETECTION_AREAS:New( groupset, grouping ) --[internal] Grouping detected objects to 5000m zones
|
||||
_MANTISdetection:FilterCategories({ Unit.Category.AIRPLANE, Unit.Category.HELICOPTER })
|
||||
_MANTISdetection:SetAcceptRange(acceptrange)
|
||||
_MANTISdetection:SetRefreshTimeInterval(interval)
|
||||
_MANTISdetection:Start()
|
||||
|
||||
function _MANTISdetection:OnAfterDetectedItem(From,Event,To,DetectedItem)
|
||||
--BASE:I( { From, Event, To, DetectedItem })
|
||||
local debug = false
|
||||
if DetectedItem.IsDetected and debug then
|
||||
local Coordinate = DetectedItem.Coordinate -- Core.Point#COORDINATE
|
||||
local text = "MANTIS: Detection at "..Coordinate:ToStringLLDMS()
|
||||
local m = MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug)
|
||||
end
|
||||
end
|
||||
return _MANTISdetection
|
||||
end
|
||||
|
||||
--- Function to start the detection via AWACS if defined as separate
|
||||
-- @param #MANTIS self
|
||||
-- @return Functional.Detection #DETECTION_AREAS The running detection set
|
||||
function MANTIS:StartAwacsDetection()
|
||||
self:F(self.lid.."Starting Awacs Detection")
|
||||
|
||||
-- start detection
|
||||
local group = self.AWACS_Prefix
|
||||
local groupset = SET_GROUP:New():FilterPrefixes(group):FilterCoalitions(self.Coalition):FilterStart()
|
||||
local grouping = self.grouping or 5000
|
||||
--local acceptrange = self.acceptrange or 80000
|
||||
local interval = self.detectinterval or 60
|
||||
|
||||
--@param Functional.Detection #DETECTION_AREAS _MANTISdetection [internal] The MANTIS detection object
|
||||
_MANTISAwacs = DETECTION_AREAS:New( groupset, grouping ) --[internal] Grouping detected objects to 5000m zones
|
||||
_MANTISAwacs:FilterCategories({ Unit.Category.AIRPLANE, Unit.Category.HELICOPTER })
|
||||
_MANTISAwacs:SetAcceptRange(self.awacsrange) --250km
|
||||
_MANTISAwacs:SetRefreshTimeInterval(interval)
|
||||
_MANTISAwacs:Start()
|
||||
|
||||
function _MANTISAwacs:OnAfterDetectedItem(From,Event,To,DetectedItem)
|
||||
--BASE:I( { From, Event, To, DetectedItem })
|
||||
local debug = false
|
||||
if DetectedItem.IsDetected and debug then
|
||||
local Coordinate = DetectedItem.Coordinate -- Core.Point#COORDINATE
|
||||
local text = "Awacs Detection at "..Coordinate:ToStringLLDMS()
|
||||
local m = MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug)
|
||||
end
|
||||
end
|
||||
return _MANTISAwacs
|
||||
end
|
||||
|
||||
--- Function to set the SAM start state
|
||||
-- @param #MANTIS self
|
||||
-- @return #MANTIS self
|
||||
function MANTIS:SetSAMStartState()
|
||||
-- TODO: if using dynamic filtering, update SAM_Table and the (active) SEAD groups, pull req #1405/#1406
|
||||
self:F(self.lid.."Setting SAM Start States")
|
||||
-- get SAM Group
|
||||
local SAM_SET = self.SAM_Group
|
||||
local SAM_Grps = SAM_SET.Set --table of objects
|
||||
local SAM_Tbl = {} -- table of SAM defense zones
|
||||
local SEAD_Grps = {} -- table of SAM names to make evasive
|
||||
local engagerange = self.engagerange -- firing range in % of max
|
||||
--cycle through groups and set alarm state etc
|
||||
for _i,_group in pairs (SAM_Grps) do
|
||||
local group = _group
|
||||
group:OptionAlarmStateGreen() -- AI off
|
||||
group:SetOption(AI.Option.Ground.id.AC_ENGAGEMENT_RANGE_RESTRICTION,engagerange) --default engagement will be 75% of firing range
|
||||
if group:IsGround() then
|
||||
local grpname = group:GetName()
|
||||
local grpcoord = group:GetCoordinate()
|
||||
table.insert( SAM_Tbl, {grpname, grpcoord})
|
||||
table.insert( SEAD_Grps, grpname )
|
||||
end
|
||||
end
|
||||
self.SAM_Table = SAM_Tbl
|
||||
-- make SAMs evasive
|
||||
local mysead = SEAD:New( SEAD_Grps )
|
||||
mysead:SetEngagementRange(engagerange)
|
||||
self.mysead = mysead
|
||||
return self
|
||||
end
|
||||
|
||||
--- (Internal) Function to update SAM table and SEAD state
|
||||
-- @param #MANTIS self
|
||||
-- @return #MANTIS self
|
||||
function MANTIS:_RefreshSAMTable()
|
||||
self:F(self.lid.."Setting SAM Start States")
|
||||
-- Requires SEAD 0.2.2 or better
|
||||
-- get SAM Group
|
||||
local SAM_SET = self.SAM_Group
|
||||
local SAM_Grps = SAM_SET.Set --table of objects
|
||||
local SAM_Tbl = {} -- table of SAM defense zones
|
||||
local SEAD_Grps = {} -- table of SAM names to make evasive
|
||||
local engagerange = self.engagerange -- firing range in % of max
|
||||
--cycle through groups and set alarm state etc
|
||||
for _i,_group in pairs (SAM_Grps) do
|
||||
local group = _group
|
||||
group:SetOption(AI.Option.Ground.id.AC_ENGAGEMENT_RANGE_RESTRICTION,engagerange) --engagement will be 75% of firing range
|
||||
if group:IsGround() then
|
||||
local grpname = group:GetName()
|
||||
local grpcoord = group:GetCoordinate()
|
||||
table.insert( SAM_Tbl, {grpname, grpcoord}) -- make the table lighter, as I don't really use the zone here
|
||||
table.insert( SEAD_Grps, grpname )
|
||||
end
|
||||
end
|
||||
self.SAM_Table = SAM_Tbl
|
||||
-- make SAMs evasive
|
||||
if self.mysead ~= nil then
|
||||
local mysead = self.mysead
|
||||
mysead:UpdateSet( SEAD_Grps )
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
-----------------------------------------------------------------------
|
||||
-- MANTIS main functions
|
||||
-----------------------------------------------------------------------
|
||||
|
||||
--- Function to set the SAM start state
|
||||
-- @param #MANTIS self
|
||||
-- @return #MANTIS self
|
||||
function MANTIS:Start()
|
||||
self:F(self.lid.."Starting MANTIS")
|
||||
self:SetSAMStartState()
|
||||
self.Detection = self:StartDetection()
|
||||
if self.advAwacs then
|
||||
self.AWACS_Detection = self:StartAwacsDetection()
|
||||
end
|
||||
-- detection function
|
||||
local function check(detection)
|
||||
--get detected set
|
||||
local detset = detection:GetDetectedItemCoordinates()
|
||||
self:F("Check:", {detset})
|
||||
-- randomly update SAM Table
|
||||
local rand = math.random(1,100)
|
||||
if rand > 65 then -- 1/3 of cases
|
||||
self:_RefreshSAMTable()
|
||||
end
|
||||
-- switch SAMs on/off if (n)one of the detected groups is inside their reach
|
||||
local samset = self:_GetSAMTable() -- table of i.1=names, i.2=coordinates
|
||||
for _,_data in pairs (samset) do
|
||||
local samcoordinate = _data[2]
|
||||
local name = _data[1]
|
||||
local samgroup = GROUP:FindByName(name)
|
||||
if self:CheckObjectInZone(detset, samcoordinate) then --check any target in zone
|
||||
if samgroup:IsAlive() then
|
||||
-- switch off SAM
|
||||
samgroup:OptionAlarmStateRed()
|
||||
--samgroup:OptionROEWeaponFree()
|
||||
--samgroup:SetAIOn()
|
||||
local text = string.format("SAM %s switched to alarm state RED!", name)
|
||||
local m=MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug)
|
||||
if self.verbose then env.info(self.lid..text) end
|
||||
end --end alive
|
||||
else
|
||||
if samgroup:IsAlive() then
|
||||
-- switch off SAM
|
||||
samgroup:OptionAlarmStateGreen()
|
||||
--samgroup:OptionROEWeaponFree()
|
||||
--samgroup:SetAIOn()
|
||||
local text = string.format("SAM %s switched to alarm state GREEN!", name)
|
||||
local m=MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug)
|
||||
if self.verbose then env.info(self.lid..text) end
|
||||
end --end alive
|
||||
end --end check
|
||||
end --for for loop
|
||||
end --end function
|
||||
-- relocation relay function
|
||||
local function relocate()
|
||||
self:_RelocateGroups()
|
||||
end
|
||||
-- check advanced state
|
||||
local function checkadvstate()
|
||||
local interval, oldstate = self:_CheckAdvState()
|
||||
local newstate = self.adv_state
|
||||
if newstate ~= oldstate then
|
||||
-- deal with new state
|
||||
if newstate == 2 then
|
||||
-- switch alarm state RED
|
||||
if self.MantisTimer.isrunning then
|
||||
self.MantisTimer:Stop()
|
||||
self.MantisTimer.isrunning = false
|
||||
end -- stop Awacs timer
|
||||
if self.MantisATimer.isrunning then
|
||||
self.MantisATimer:Stop()
|
||||
self.MantisATimer.isrunning = false
|
||||
end -- stop timer
|
||||
local samset = self:_GetSAMTable() -- table of i.1=names, i.2=coordinates
|
||||
for _,_data in pairs (samset) do
|
||||
local name = _data[1]
|
||||
local samgroup = GROUP:FindByName(name)
|
||||
if samgroup:IsAlive() then
|
||||
samgroup:OptionAlarmStateRed()
|
||||
end -- end alive
|
||||
end -- end for loop
|
||||
elseif newstate <= 1 then
|
||||
-- change MantisTimer to slow down or speed up
|
||||
if self.MantisTimer.isrunning then
|
||||
self.MantisTimer:Stop()
|
||||
self.MantisTimer.isrunning = false
|
||||
end
|
||||
if self.MantisATimer.isrunning then
|
||||
self.MantisATimer:Stop()
|
||||
self.MantisATimer.isrunning = false
|
||||
end
|
||||
self.MantisTimer = TIMER:New(check,self.Detection)
|
||||
self.MantisTimer:Start(5,interval,nil)
|
||||
self.MantisTimer.isrunning = true
|
||||
if self.advAwacs then
|
||||
self.MantisATimer = TIMER:New(check,self.AWACS_Detection)
|
||||
self.MantisATimer:Start(15,interval,nil)
|
||||
self.MantisATimer.isrunning = true
|
||||
end
|
||||
end
|
||||
end -- end newstate vs oldstate
|
||||
end
|
||||
-- timers to run the system
|
||||
local interval = self.detectinterval
|
||||
self.MantisTimer = TIMER:New(check,self.Detection)
|
||||
self.MantisTimer:Start(5,interval,nil)
|
||||
self.MantisTimer.isrunning = true
|
||||
-- Awacs timer
|
||||
if self.advAwacs then
|
||||
self.MantisATimer = TIMER:New(check,self.AWACS_Detection)
|
||||
self.MantisATimer:Start(15,interval,nil)
|
||||
self.MantisATimer.isrunning = true
|
||||
end
|
||||
-- timer to relocate HQ and EWR
|
||||
if self.autorelocate then
|
||||
local relointerval = math.random(1800,3600) -- random between 30 and 60 mins
|
||||
self.MantisReloTimer = TIMER:New(relocate)
|
||||
self.MantisReloTimer:Start(relointerval,relointerval,nil)
|
||||
end
|
||||
-- timer for advanced state check
|
||||
if self.advanced then
|
||||
self.MantisAdvTimer = TIMER:New(checkadvstate)
|
||||
self.MantisAdvTimer:Start(30,interval*5,nil)
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- Function to stop MANTIS
|
||||
-- @param #MANTIS self
|
||||
-- @return #MANTIS self
|
||||
function MANTIS:Stop()
|
||||
if self.MantisTimer.isrunning then
|
||||
self.MantisTimer:Stop()
|
||||
end
|
||||
if self.MantisATimer.isrunning then
|
||||
self.MantisATimer:Stop()
|
||||
end
|
||||
if self.autorelocate then
|
||||
self.MantisReloTimer:Stop()
|
||||
end
|
||||
if self.advanced then
|
||||
self.MantisAdvTimer:Stop()
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
end
|
||||
-----------------------------------------------------------------------
|
||||
-- MANTIS end
|
||||
-----------------------------------------------------------------------
|
||||
@@ -53,6 +53,7 @@
|
||||
-- @type RANGE
|
||||
-- @field #string ClassName Name of the Class.
|
||||
-- @field #boolean Debug If true, debug info is send as messages on the screen.
|
||||
-- @field #boolean verbose Verbosity level. Higher means more output to DCS log file.
|
||||
-- @field #string id String id of range for output in DCS log.
|
||||
-- @field #string rangename Name of the range.
|
||||
-- @field Core.Point#COORDINATE location Coordinate of the range location.
|
||||
@@ -93,6 +94,8 @@
|
||||
-- @field Core.RadioQueue#RADIOQUEUE instructor Instructor radio queue.
|
||||
-- @field #number rangecontrolfreq Frequency on which the range control transmitts.
|
||||
-- @field Core.RadioQueue#RADIOQUEUE rangecontrol Range control radio queue.
|
||||
-- @field #string rangecontrolrelayname Name of relay unit.
|
||||
-- @field #string instructorrelayname Name of relay unit.
|
||||
-- @field #string soundpath Path inside miz file where the sound files are located. Default is "Range Soundfiles/".
|
||||
-- @extends Core.Fsm#FSM
|
||||
|
||||
@@ -287,6 +290,7 @@
|
||||
RANGE={
|
||||
ClassName = "RANGE",
|
||||
Debug = false,
|
||||
verbose = 0,
|
||||
id = nil,
|
||||
rangename = nil,
|
||||
location = nil,
|
||||
@@ -516,7 +520,7 @@ RANGE.MenuF10Root=nil
|
||||
|
||||
--- Range script version.
|
||||
-- @field #string version
|
||||
RANGE.version="2.2.2"
|
||||
RANGE.version="2.3.0"
|
||||
|
||||
--TODO list:
|
||||
--TODO: Verbosity level for messages.
|
||||
@@ -554,7 +558,6 @@ function RANGE:New(rangename)
|
||||
-- Debug info.
|
||||
local text=string.format("Script version %s - creating new RANGE object %s.", RANGE.version, self.rangename)
|
||||
self:I(self.id..text)
|
||||
MESSAGE:New(text, 10):ToAllIf(self.Debug)
|
||||
|
||||
-- Defaults
|
||||
self:SetDefaultPlayerSmokeBomb()
|
||||
@@ -721,7 +724,6 @@ function RANGE:onafterStart()
|
||||
-- Starting range.
|
||||
local text=string.format("Starting RANGE %s. Number of strafe targets = %d. Number of bomb targets = %d.", self.rangename, self.nstrafetargets, self.nbombtargets)
|
||||
self:I(self.id..text)
|
||||
MESSAGE:New(text,10):ToAllIf(self.Debug)
|
||||
|
||||
-- Event handling.
|
||||
if self.eventmoose then
|
||||
@@ -755,6 +757,7 @@ function RANGE:onafterStart()
|
||||
|
||||
-- Radio queue.
|
||||
self.rangecontrol=RADIOQUEUE:New(self.rangecontrolfreq, nil, self.rangename)
|
||||
self.rangecontrol.schedonce=true
|
||||
|
||||
-- Init numbers.
|
||||
self.rangecontrol:SetDigit(0, RANGE.Sound.RC0.filename, RANGE.Sound.RC0.duration, self.soundpath)
|
||||
@@ -770,6 +773,7 @@ function RANGE:onafterStart()
|
||||
|
||||
-- Set location where the messages are transmitted from.
|
||||
self.rangecontrol:SetSenderCoordinate(self.location)
|
||||
self.rangecontrol:SetSenderUnitName(self.rangecontrolrelayname)
|
||||
|
||||
-- Start range control radio queue.
|
||||
self.rangecontrol:Start(1, 0.1)
|
||||
@@ -778,7 +782,8 @@ function RANGE:onafterStart()
|
||||
if self.instructorfreq then
|
||||
|
||||
-- Radio queue.
|
||||
self.instructor=RADIOQUEUE:New(self.instructorfreq, nil, self.rangename)
|
||||
self.instructor=RADIOQUEUE:New(self.instructorfreq, nil, self.rangename)
|
||||
self.instructor.schedonce=true
|
||||
|
||||
-- Init numbers.
|
||||
self.instructor:SetDigit(0, RANGE.Sound.IR0.filename, RANGE.Sound.IR0.duration, self.soundpath)
|
||||
@@ -794,6 +799,7 @@ function RANGE:onafterStart()
|
||||
|
||||
-- Set location where the messages are transmitted from.
|
||||
self.instructor:SetSenderCoordinate(self.location)
|
||||
self.instructor:SetSenderUnitName(self.instructorrelayname)
|
||||
|
||||
-- Start instructor radio queue.
|
||||
self.instructor:Start(1, 0.1)
|
||||
@@ -1067,18 +1073,22 @@ end
|
||||
--- Enable range control and set frequency.
|
||||
-- @param #RANGE self
|
||||
-- @param #number frequency Frequency in MHz. Default 256 MHz.
|
||||
-- @param #string relayunitname Name of the unit used for transmission.
|
||||
-- @return #RANGE self
|
||||
function RANGE:SetRangeControl(frequency)
|
||||
function RANGE:SetRangeControl(frequency, relayunitname)
|
||||
self.rangecontrolfreq=frequency or 256
|
||||
self.rangecontrolrelayname=relayunitname
|
||||
return self
|
||||
end
|
||||
|
||||
--- Enable instructor radio and set frequency.
|
||||
-- @param #RANGE self
|
||||
-- @param #number frequency Frequency in MHz. Default 305 MHz.
|
||||
-- @param #string relayunitname Name of the unit used for transmission.
|
||||
-- @return #RANGE self
|
||||
function RANGE:SetInstructorRadio(frequency)
|
||||
function RANGE:SetInstructorRadio(frequency, relayunitname)
|
||||
self.instructorfreq=frequency or 305
|
||||
self.instructorrelayname=relayunitname
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -1140,7 +1150,6 @@ function RANGE:AddStrafePit(targetnames, boxlength, boxwidth, heading, inversehe
|
||||
-- Neither unit nor static object with this name could be found.
|
||||
local text=string.format("ERROR! Could not find ANY strafe target object with name %s.", _name)
|
||||
self:E(self.id..text)
|
||||
MESSAGE:New(text, 10):ToAllIf(self.Debug)
|
||||
|
||||
end
|
||||
|
||||
@@ -1160,7 +1169,6 @@ function RANGE:AddStrafePit(targetnames, boxlength, boxwidth, heading, inversehe
|
||||
if ntargets==0 then
|
||||
local text=string.format("ERROR! No strafe target could be found when calling RANGE:AddStrafePit() for range %s", self.rangename)
|
||||
self:E(self.id..text)
|
||||
MESSAGE:New(text, 10):ToAllIf(self.Debug)
|
||||
return
|
||||
end
|
||||
|
||||
@@ -1230,7 +1238,6 @@ function RANGE:AddStrafePit(targetnames, boxlength, boxwidth, heading, inversehe
|
||||
-- Debug info
|
||||
local text=string.format("Adding new strafe target %s with %d targets: heading = %03d, box_L = %.1f, box_W = %.1f, goodpass = %d, foul line = %.1f", _name, ntargets, heading, l, w, goodpass, foulline)
|
||||
self:T(self.id..text)
|
||||
MESSAGE:New(text, 5):ToAllIf(self.Debug)
|
||||
|
||||
return self
|
||||
end
|
||||
@@ -1559,13 +1566,13 @@ function RANGE:OnEventBirth(EventData)
|
||||
-- Debug output.
|
||||
local text=string.format("Player %s, callsign %s entered unit %s (UID %d) of group %s (GID %d)", _playername, _callsign, _unitName, _uid, _group:GetName(), _gid)
|
||||
self:T(self.id..text)
|
||||
MESSAGE:New(text, 5):ToAllIf(self.Debug)
|
||||
|
||||
-- Reset current strafe status.
|
||||
self.strafeStatus[_uid] = nil
|
||||
|
||||
-- Add Menu commands after a delay of 0.1 seconds.
|
||||
SCHEDULER:New(nil, self._AddF10Commands, {self,_unitName}, 0.1)
|
||||
--SCHEDULER:New(nil, self._AddF10Commands, {self,_unitName}, 0.1)
|
||||
self:ScheduleOnce(0.1, self._AddF10Commands, self, _unitName)
|
||||
|
||||
-- By default, some bomb impact points and do not flare each hit on target.
|
||||
self.PlayerSettings[_playername]={} --#RANGE.PlayerData
|
||||
@@ -1583,7 +1590,8 @@ function RANGE:OnEventBirth(EventData)
|
||||
|
||||
-- Start check in zone timer.
|
||||
if self.planes[_uid] ~= true then
|
||||
SCHEDULER:New(nil, self._CheckInZone, {self, EventData.IniUnitName}, 1, 1)
|
||||
--SCHEDULER:New(nil, self._CheckInZone, {self, EventData.IniUnitName}, 1, 1)
|
||||
self.timerCheckZone=TIMER:New(self._CheckInZone, self, EventData.IniUnitName):Start(1, 1)
|
||||
self.planes[_uid] = true
|
||||
end
|
||||
|
||||
@@ -1666,15 +1674,12 @@ function RANGE:OnEventHit(EventData)
|
||||
|
||||
if _unit and _playername then
|
||||
|
||||
-- Position of target.
|
||||
local targetPos = _target:GetCoordinate()
|
||||
|
||||
-- Message to player.
|
||||
--local text=string.format("%s, direct hit on target %s.", self:_myname(_unitName), targetname)
|
||||
--self:DisplayMessageToGroup(_unit, text, 10, true)
|
||||
|
||||
-- Flare target.
|
||||
if self.PlayerSettings[_playername].flaredirecthits then
|
||||
|
||||
-- Position of target.
|
||||
local targetPos = _target:GetCoordinate()
|
||||
|
||||
targetPos:Flare(self.PlayerSettings[_playername].flarecolor)
|
||||
end
|
||||
|
||||
@@ -1716,9 +1721,6 @@ function RANGE:OnEventShot(EventData)
|
||||
self:T(self.id.."EVENT SHOT: Weapon name = ".._weaponName)
|
||||
self:T(self.id.."EVENT SHOT: Weapon cate = "..weaponcategory)
|
||||
|
||||
-- Special cases:
|
||||
--local _viggen=string.match(_weapon, "ROBOT") or string.match(_weapon, "RB75") or string.match(_weapon, "BK90") or string.match(_weapon, "RB15") or string.match(_weapon, "RB04")
|
||||
|
||||
-- Tracking conditions for bombs, rockets and missiles.
|
||||
local _bombs = weaponcategory==Weapon.Category.BOMB --string.match(_weapon, "weapons.bombs")
|
||||
local _rockets = weaponcategory==Weapon.Category.ROCKET --string.match(_weapon, "weapons.nurs")
|
||||
@@ -1752,7 +1754,7 @@ function RANGE:OnEventShot(EventData)
|
||||
self:T(self.id..string.format("RANGE %s: Tracking %s - %s.", self.rangename, _weapon, EventData.weapon:getName()))
|
||||
|
||||
-- Init bomb position.
|
||||
local _lastBombPos = {x=0,y=0,z=0}
|
||||
local _lastBombPos = {x=0,y=0,z=0} --DCS#Vec3
|
||||
|
||||
-- Function monitoring the position of a bomb until impact.
|
||||
local function trackBomb(_ordnance)
|
||||
@@ -1908,8 +1910,39 @@ end
|
||||
-- @param #string To To state.
|
||||
function RANGE:onafterStatus(From, Event, To)
|
||||
|
||||
-- Check range status.
|
||||
self:I(self.id..string.format("Range status: %s", self:GetState()))
|
||||
if self.verbose>0 then
|
||||
|
||||
local fsmstate=self:GetState()
|
||||
|
||||
local text=string.format("Range status: %s", fsmstate)
|
||||
|
||||
if self.instructor then
|
||||
local alive="N/A"
|
||||
if self.instructorrelayname then
|
||||
local relay=UNIT:FindByName(self.instructorrelayname)
|
||||
if relay then
|
||||
alive=tostring(relay:IsAlive())
|
||||
end
|
||||
end
|
||||
text=text..string.format(", Instructor %.3f MHz (Relay=%s alive=%s)", self.instructorfreq, tostring(self.instructorrelayname), alive)
|
||||
end
|
||||
|
||||
if self.rangecontrol then
|
||||
local alive="N/A"
|
||||
if self.rangecontrolrelayname then
|
||||
local relay=UNIT:FindByName(self.rangecontrolrelayname)
|
||||
if relay then
|
||||
alive=tostring(relay:IsAlive())
|
||||
end
|
||||
end
|
||||
text=text..string.format(", Control %.3f MHz (Relay=%s alive=%s)", self.rangecontrolfreq, tostring(self.rangecontrolrelayname), alive)
|
||||
end
|
||||
|
||||
|
||||
-- Check range status.
|
||||
self:I(self.id..text)
|
||||
|
||||
end
|
||||
|
||||
-- Check player status.
|
||||
self:_CheckPlayers()
|
||||
@@ -2701,6 +2734,32 @@ function RANGE:_CheckInZone(_unitName)
|
||||
|
||||
if _unit and _playername then
|
||||
|
||||
--- Function to check if unit is in zone and facing in the right direction and is below the max alt.
|
||||
local function checkme(targetheading, _zone)
|
||||
local zone=_zone --Core.Zone#ZONE
|
||||
|
||||
-- Heading check.
|
||||
local unitheading = _unit:GetHeading()
|
||||
local pitheading = targetheading-180
|
||||
local deltaheading = unitheading-pitheading
|
||||
local towardspit = math.abs(deltaheading)<=90 or math.abs(deltaheading-360)<=90
|
||||
|
||||
if towardspit then
|
||||
|
||||
local vec3=_unit:GetVec3()
|
||||
local vec2={x=vec3.x, y=vec3.z} --DCS#Vec2
|
||||
local landheight=land.getHeight(vec2)
|
||||
local unitalt=vec3.y-landheight
|
||||
|
||||
if unitalt<=self.strafemaxalt then
|
||||
local unitinzone=zone:IsVec2InZone(vec2)
|
||||
return unitinzone
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
-- Current position of player unit.
|
||||
local _unitID = _unit:GetID()
|
||||
|
||||
@@ -2712,18 +2771,8 @@ function RANGE:_CheckInZone(_unitName)
|
||||
-- Get the current approach zone and check if player is inside.
|
||||
local zone=_currentStrafeRun.zone.polygon --Core.Zone#ZONE_POLYGON_BASE
|
||||
|
||||
local unitheading = _unit:GetHeading()
|
||||
local pitheading = _currentStrafeRun.zone.heading - 180
|
||||
local deltaheading = unitheading-pitheading
|
||||
local towardspit = math.abs(deltaheading)<=90 or math.abs(deltaheading-360)<=90
|
||||
local unitalt=_unit:GetHeight()-_unit:GetCoordinate():GetLandHeight()
|
||||
|
||||
-- Check if unit is inside zone and below max height AGL.
|
||||
local unitinzone=_unit:IsInZone(zone) and unitalt <= self.strafemaxalt and towardspit
|
||||
|
||||
-- Debug output
|
||||
local text=string.format("Checking still in zone. Unit = %s, player = %s in zone = %s. alt = %d, delta heading = %d", _unitName, _playername, tostring(unitinzone), unitalt, deltaheading)
|
||||
self:T2(self.id..text)
|
||||
-- Check if unit in zone and facing the right direction.
|
||||
local unitinzone=checkme(_currentStrafeRun.zone.heading, zone)
|
||||
|
||||
-- Check if player is in strafe zone and below max alt.
|
||||
if unitinzone then
|
||||
@@ -2823,22 +2872,10 @@ function RANGE:_CheckInZone(_unitName)
|
||||
for _,_targetZone in pairs(self.strafeTargets) do
|
||||
|
||||
-- Get the current approach zone and check if player is inside.
|
||||
local zonenname=_targetZone.name
|
||||
local zone=_targetZone.polygon --Core.Zone#ZONE_POLYGON_BASE
|
||||
|
||||
-- Check if player is in zone and below max alt and flying towards the target.
|
||||
local unitheading = _unit:GetHeading()
|
||||
local pitheading = _targetZone.heading - 180
|
||||
local deltaheading = unitheading-pitheading
|
||||
local towardspit = math.abs(deltaheading)<=90 or math.abs(deltaheading-360)<=90
|
||||
local unitalt =_unit:GetHeight()-_unit:GetCoordinate():GetLandHeight()
|
||||
|
||||
-- Check if unit is inside zone and below max height AGL.
|
||||
local unitinzone=_unit:IsInZone(zone) and unitalt <= self.strafemaxalt and towardspit
|
||||
|
||||
-- Debug info.
|
||||
local text=string.format("Checking zone %s. Unit = %s, player = %s in zone = %s. alt = %d, delta heading = %d", _targetZone.name, _unitName, _playername, tostring(unitinzone), unitalt, deltaheading)
|
||||
self:T2(self.id..text)
|
||||
-- Check if unit in zone and facing the right direction.
|
||||
local unitinzone=checkme(_targetZone.heading, zone)
|
||||
|
||||
-- Player is inside zone.
|
||||
if unitinzone then
|
||||
@@ -2847,7 +2884,7 @@ function RANGE:_CheckInZone(_unitName)
|
||||
local _ammo=self:_GetAmmo(_unitName)
|
||||
|
||||
-- Init strafe status for this player.
|
||||
self.strafeStatus[_unitID] = {hits = 0, zone = _targetZone, time = 1, ammo=_ammo, pastfoulline=false }
|
||||
self.strafeStatus[_unitID] = {hits = 0, zone = _targetZone, time = 1, ammo=_ammo, pastfoulline=false}
|
||||
|
||||
-- Rolling in!
|
||||
local _msg=string.format("%s, rolling in on strafe pit %s.", self:_myname(_unitName), _targetZone.name)
|
||||
@@ -3054,11 +3091,9 @@ function RANGE:_GetAmmo(unitname)
|
||||
|
||||
local text=string.format("Player %s has %d rounds ammo of type %s", playername, Nammo, Tammo)
|
||||
self:T(self.id..text)
|
||||
MESSAGE:New(text, 10):ToAllIf(self.Debug)
|
||||
else
|
||||
local text=string.format("Player %s has %d ammo of type %s", playername, Nammo, Tammo)
|
||||
self:T(self.id..text)
|
||||
MESSAGE:New(text, 10):ToAllIf(self.Debug)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -15,7 +15,9 @@
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Authors: **FlightControl**
|
||||
-- ### Authors: **FlightControl**, **applevangelist**
|
||||
--
|
||||
-- Last Update: Dec 2020
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
@@ -39,18 +41,20 @@
|
||||
SEAD = {
|
||||
ClassName = "SEAD",
|
||||
TargetSkill = {
|
||||
Average = { Evade = 50, DelayOff = { 10, 25 }, DelayOn = { 10, 30 } } ,
|
||||
Good = { Evade = 30, DelayOff = { 8, 20 }, DelayOn = { 20, 40 } } ,
|
||||
High = { Evade = 15, DelayOff = { 5, 17 }, DelayOn = { 30, 50 } } ,
|
||||
Excellent = { Evade = 10, DelayOff = { 3, 10 }, DelayOn = { 30, 60 } }
|
||||
Average = { Evade = 30, DelayOn = { 40, 60 } } ,
|
||||
Good = { Evade = 20, DelayOn = { 30, 50 } } ,
|
||||
High = { Evade = 15, DelayOn = { 20, 40 } } ,
|
||||
Excellent = { Evade = 10, DelayOn = { 10, 30 } }
|
||||
},
|
||||
SEADGroupPrefixes = {}
|
||||
SEADGroupPrefixes = {},
|
||||
SuppressedGroups = {},
|
||||
EngagementRange = 75 -- default 75% engagement range Feature Request #1355
|
||||
}
|
||||
|
||||
--- Creates the main object which is handling defensive actions for SA sites or moving SA vehicles.
|
||||
-- When an anti radiation missile is fired (KH-58, KH-31P, KH-31A, KH-25MPU, HARM missiles), the SA will shut down their radars and will take evasive actions...
|
||||
-- Chances are big that the missile will miss.
|
||||
-- @param table{string,...}|string SEADGroupPrefixes which is a table of Prefixes of the SA Groups in the DCSRTE on which evasive actions need to be taken.
|
||||
-- @param table{string,...}|string SEADGroupPrefixes which is a table of Prefixes of the SA Groups in the DCS mission editor on which evasive actions need to be taken.
|
||||
-- @return SEAD
|
||||
-- @usage
|
||||
-- -- CCCP SEAD Defenses
|
||||
@@ -66,29 +70,64 @@ function SEAD:New( SEADGroupPrefixes )
|
||||
self.SEADGroupPrefixes[SEADGroupPrefix] = SEADGroupPrefix
|
||||
end
|
||||
else
|
||||
self.SEADGroupNames[SEADGroupPrefixes] = SEADGroupPrefixes
|
||||
self.SEADGroupPrefixes[SEADGroupPrefixes] = SEADGroupPrefixes
|
||||
end
|
||||
|
||||
self:HandleEvent( EVENTS.Shot )
|
||||
|
||||
self:I("*** SEAD - Started Version 0.2.2")
|
||||
return self
|
||||
end
|
||||
|
||||
--- Update the active SEAD Set
|
||||
-- @param #SEAD self
|
||||
-- @param #table SEADGroupPrefixes The prefixes to add, note: can also be a single #string
|
||||
-- @return #SEAD self
|
||||
function SEAD:UpdateSet( SEADGroupPrefixes )
|
||||
|
||||
self:F( SEADGroupPrefixes )
|
||||
|
||||
if type( SEADGroupPrefixes ) == 'table' then
|
||||
for SEADGroupPrefixID, SEADGroupPrefix in pairs( SEADGroupPrefixes ) do
|
||||
self.SEADGroupPrefixes[SEADGroupPrefix] = SEADGroupPrefix
|
||||
end
|
||||
else
|
||||
self.SEADGroupPrefixes[SEADGroupPrefixes] = SEADGroupPrefixes
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Sets the engagement range of the SAMs. Defaults to 75% to make it more deadly. Feature Request #1355
|
||||
-- @param #SEAD self
|
||||
-- @param #number range Set the engagement range in percent, e.g. 50
|
||||
-- @return self
|
||||
function SEAD:SetEngagementRange(range)
|
||||
self:F( { range } )
|
||||
range = range or 75
|
||||
if range < 0 or range > 100 then
|
||||
range = 75
|
||||
end
|
||||
self.EngagementRange = range
|
||||
self:T(string.format("*** SEAD - Engagement range set to %s",range))
|
||||
return self
|
||||
end
|
||||
|
||||
--- Detects if an SA site was shot with an anti radiation missile. In this case, take evasive actions based on the skill level set within the ME.
|
||||
-- @see SEAD
|
||||
-- @param #SEAD
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
function SEAD:OnEventShot( EventData )
|
||||
self:F( { EventData } )
|
||||
self:T( { EventData } )
|
||||
|
||||
local SEADUnit = EventData.IniDCSUnit
|
||||
local SEADUnitName = EventData.IniDCSUnitName
|
||||
local SEADWeapon = EventData.Weapon -- Identify the weapon fired
|
||||
local SEADWeapon = EventData.Weapon -- Identify the weapon fired
|
||||
local SEADWeaponName = EventData.WeaponName -- return weapon type
|
||||
-- Start of the 2nd loop
|
||||
self:T( "Missile Launched = " .. SEADWeaponName )
|
||||
|
||||
self:T( "*** SEAD - Missile Launched = " .. SEADWeaponName)
|
||||
self:T({ SEADWeapon })
|
||||
|
||||
--if SEADWeaponName == "KH-58" or SEADWeaponName == "KH-25MPU" or SEADWeaponName == "AGM-88" or SEADWeaponName == "KH-31A" or SEADWeaponName == "KH-31P" then -- Check if the missile is a SEAD
|
||||
--check for SEAD missiles
|
||||
if SEADWeaponName == "weapons.missiles.X_58" --Kh-58U anti-radiation missiles fired
|
||||
or
|
||||
SEADWeaponName == "weapons.missiles.Kh25MP_PRGS1VP" --Kh-25MP anti-radiation missiles fired
|
||||
@@ -107,27 +146,35 @@ function SEAD:OnEventShot( EventData )
|
||||
or
|
||||
SEADWeaponName == "weapons.missiles.AGM_122" --AGM-122 Sidearm anti-radiation missiles fired
|
||||
or
|
||||
SEADWeaponName == "weapons.missiles.LD-10" --LD-10 anti-radiation missiles fired
|
||||
or
|
||||
SEADWeaponName == "weapons.missiles.ALARM" --ALARM anti-radiation missiles fired
|
||||
or
|
||||
SEADWeaponName == "weapons.missiles.AGM_84E" --AGM84 anti-radiation missiles fired
|
||||
or
|
||||
SEADWeaponName == "weapons.missiles.AGM_84A" --AGM84 anti-radiation missiles fired
|
||||
or
|
||||
SEADWeaponName == "weapons.missiles.AGM_84H" --AGM84 anti-radiation missiles fired
|
||||
then
|
||||
|
||||
local _evade = math.random (1,100) -- random number for chance of evading action
|
||||
local _targetMim = EventData.Weapon:getTarget() -- Identify target
|
||||
local _targetMimname = Unit.getName(_targetMim)
|
||||
local _targetMimgroup = Unit.getGroup(Weapon.getTarget(SEADWeapon))
|
||||
local _targetMimgroupName = _targetMimgroup:getName()
|
||||
local _targetMimcont= _targetMimgroup:getController()
|
||||
local _targetMimname = Unit.getName(_targetMim) -- Unit name
|
||||
local _targetMimgroup = Unit.getGroup(Weapon.getTarget(SEADWeapon)) --targeted grouo
|
||||
local _targetMimgroupName = _targetMimgroup:getName() -- group name
|
||||
local _targetskill = _DATABASE.Templates.Units[_targetMimname].Template.skill
|
||||
self:T( self.SEADGroupPrefixes )
|
||||
self:T( _targetMimgroupName )
|
||||
-- see if we are shot at
|
||||
local SEADGroupFound = false
|
||||
for SEADGroupPrefixID, SEADGroupPrefix in pairs( self.SEADGroupPrefixes ) do
|
||||
if string.find( _targetMimgroupName, SEADGroupPrefix, 1, true ) then
|
||||
SEADGroupFound = true
|
||||
self:T( 'Group Found' )
|
||||
self:T( '*** SEAD - Group Found' )
|
||||
break
|
||||
end
|
||||
end
|
||||
if SEADGroupFound == true then
|
||||
if SEADGroupFound == true then -- yes we are being attacked
|
||||
if _targetskill == "Random" then -- when skill is random, choose a skill
|
||||
local Skills = { "Average", "Good", "High", "Excellent" }
|
||||
_targetskill = Skills[ math.random(1,4) ]
|
||||
@@ -136,63 +183,36 @@ function SEAD:OnEventShot( EventData )
|
||||
if self.TargetSkill[_targetskill] then
|
||||
if (_evade > self.TargetSkill[_targetskill].Evade) then
|
||||
|
||||
self:T( string.format("Evading, target skill " ..string.format(_targetskill)) )
|
||||
|
||||
local _targetMim = Weapon.getTarget(SEADWeapon)
|
||||
local _targetMimname = Unit.getName(_targetMim)
|
||||
self:T( string.format("*** SEAD - Evading, target skill " ..string.format(_targetskill)) )
|
||||
|
||||
local _targetMimgroup = Unit.getGroup(Weapon.getTarget(SEADWeapon))
|
||||
local _targetMimcont= _targetMimgroup:getController()
|
||||
|
||||
routines.groupRandomDistSelf(_targetMimgroup,300,'Diamond',250,20) -- move randomly
|
||||
|
||||
local SuppressedGroups1 = {} -- unit suppressed radar off for a random time
|
||||
|
||||
local function SuppressionEnd1(id)
|
||||
id.ctrl:setOption(AI.Option.Ground.id.ALARM_STATE,AI.Option.Ground.val.ALARM_STATE.GREEN)
|
||||
SuppressedGroups1[id.groupName] = nil
|
||||
end
|
||||
|
||||
local id = {
|
||||
--tracker ID table to switch groups off and on again
|
||||
local id = {
|
||||
groupName = _targetMimgroup,
|
||||
ctrl = _targetMimcont
|
||||
}
|
||||
|
||||
local delay1 = math.random(self.TargetSkill[_targetskill].DelayOff[1], self.TargetSkill[_targetskill].DelayOff[2])
|
||||
|
||||
if SuppressedGroups1[id.groupName] == nil then
|
||||
|
||||
SuppressedGroups1[id.groupName] = {
|
||||
SuppressionEndTime1 = timer.getTime() + delay1,
|
||||
SuppressionEndN1 = SuppressionEndCounter1 --Store instance of SuppressionEnd() scheduled function
|
||||
}
|
||||
|
||||
Controller.setOption(_targetMimcont, AI.Option.Ground.id.ALARM_STATE,AI.Option.Ground.val.ALARM_STATE.GREEN)
|
||||
timer.scheduleFunction(SuppressionEnd1, id, SuppressedGroups1[id.groupName].SuppressionEndTime1) --Schedule the SuppressionEnd() function
|
||||
--trigger.action.outText( string.format("Radar Off " ..string.format(delay1)), 20)
|
||||
end
|
||||
|
||||
local SuppressedGroups = {}
|
||||
|
||||
local function SuppressionEnd(id)
|
||||
|
||||
local function SuppressionEnd(id) --switch group back on
|
||||
local range = self.EngagementRange -- Feature Request #1355
|
||||
self:T(string.format("*** SEAD - Engagement Range is %d", range))
|
||||
id.ctrl:setOption(AI.Option.Ground.id.ALARM_STATE,AI.Option.Ground.val.ALARM_STATE.RED)
|
||||
SuppressedGroups[id.groupName] = nil
|
||||
id.ctrl:setOption(AI.Option.Ground.id.AC_ENGAGEMENT_RANGE_RESTRICTION,range) --Feature Request #1355
|
||||
self.SuppressedGroups[id.groupName] = nil --delete group id from table when done
|
||||
end
|
||||
|
||||
local id = {
|
||||
groupName = _targetMimgroup,
|
||||
ctrl = _targetMimcont
|
||||
}
|
||||
|
||||
-- randomize switch-on time
|
||||
local delay = math.random(self.TargetSkill[_targetskill].DelayOn[1], self.TargetSkill[_targetskill].DelayOn[2])
|
||||
|
||||
if SuppressedGroups[id.groupName] == nil then
|
||||
SuppressedGroups[id.groupName] = {
|
||||
SuppressionEndTime = timer.getTime() + delay,
|
||||
SuppressionEndN = SuppressionEndCounter --Store instance of SuppressionEnd() scheduled function
|
||||
}
|
||||
|
||||
timer.scheduleFunction(SuppressionEnd, id, SuppressedGroups[id.groupName].SuppressionEndTime) --Schedule the SuppressionEnd() function
|
||||
--trigger.action.outText( string.format("Radar On " ..string.format(delay)), 20)
|
||||
local SuppressionEndTime = timer.getTime() + delay
|
||||
--create entry
|
||||
if self.SuppressedGroups[id.groupName] == nil then --no timer entry for this group yet
|
||||
self.SuppressedGroups[id.groupName] = {
|
||||
SuppressionEndTime = delay
|
||||
}
|
||||
Controller.setOption(_targetMimcont, AI.Option.Ground.id.ALARM_STATE,AI.Option.Ground.val.ALARM_STATE.GREEN)
|
||||
timer.scheduleFunction(SuppressionEnd, id, SuppressionEndTime) --Schedule the SuppressionEnd() function
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -193,7 +193,13 @@
|
||||
-- warehouseBatumi:AddAsset("Huey", 5, WAREHOUSE.Attribute.AIR_TRANSPORTHELO)
|
||||
--
|
||||
-- This becomes important when assets are requested from other warehouses as described below. In this case, the five Hueys are now marked as transport helicopters and
|
||||
-- not attack helicopters.
|
||||
-- not attack helicopters. This is also particularly useful when adding assets to a warehouse with the intention of using them to transport other units that are part of
|
||||
-- a subsequent request (see below). Setting the attribute will help to ensure that warehouse module can find the correct unit when attempting to service a request in its
|
||||
-- queue. For example, if we want to add an Amphibious Landing Ship, even though most are indeed armed, it's recommended to do the following:
|
||||
--
|
||||
-- warehouseBatumi:AddAsset("Landing Ship", 1, WAREHOUSE.Attribute.NAVAL_UNARMEDSHIP)
|
||||
--
|
||||
-- Then when adding the request, you can simply specify WAREHOUSE.TransportType.SHIP (which corresponds to NAVAL_UNARMEDSHIP) as the TransportType.
|
||||
--
|
||||
-- ### Setting the Cargo Bay Weight Limit
|
||||
-- You can ajust the cargo bay weight limit, in case it is not calculated correctly automatically. For example, the cargo bay of a C-17A is much smaller in DCS than that of a C-130, which is
|
||||
@@ -1569,6 +1575,7 @@ WAREHOUSE = {
|
||||
delivered = {},
|
||||
defending = {},
|
||||
portzone = nil,
|
||||
harborzone = nil,
|
||||
shippinglanes = {},
|
||||
offroadpaths = {},
|
||||
autodefence = false,
|
||||
@@ -1726,12 +1733,15 @@ WAREHOUSE.Attribute = {
|
||||
-- @field #string TRAIN Transports are conducted by trains. Not implemented yet. Also trains are buggy in DCS.
|
||||
-- @field #string SELFPROPELLED Assets go to their destination by themselves. No transport carrier needed.
|
||||
WAREHOUSE.TransportType = {
|
||||
AIRPLANE = "Air_TransportPlane",
|
||||
HELICOPTER = "Air_TransportHelo",
|
||||
APC = "Ground_APC",
|
||||
TRAIN = "Ground_Train",
|
||||
SHIP = "Naval_UnarmedShip",
|
||||
SELFPROPELLED = "Selfpropelled",
|
||||
AIRPLANE = "Air_TransportPlane",
|
||||
HELICOPTER = "Air_TransportHelo",
|
||||
APC = "Ground_APC",
|
||||
TRAIN = "Ground_Train",
|
||||
SHIP = "Naval_UnarmedShip",
|
||||
AIRCRAFTCARRIER = "Naval_AircraftCarrier",
|
||||
WARSHIP = "Naval_WarShip",
|
||||
ARMEDSHIP = "Naval_ArmedShip",
|
||||
SELFPROPELLED = "Selfpropelled",
|
||||
}
|
||||
|
||||
--- Warehouse quantity enumerator for selecting number of assets, e.g. all, half etc. of what is in stock rather than an absolute number.
|
||||
@@ -1776,7 +1786,7 @@ WAREHOUSE.version="1.0.2"
|
||||
-- TODO: Make more examples: ARTY, CAP, ...
|
||||
-- TODO: Check also general requests like all ground. Is this a problem for self propelled if immobile units are among the assets? Check if transport.
|
||||
-- TODO: Handle the case when units of a group die during the transfer.
|
||||
-- TODO: Added habours as interface for transport to from warehouses? Could make a rudimentary shipping dispatcher.
|
||||
-- DONE: Added harbours as interface for transport to/from warehouses. Simplifies process of spawning units near the ship, especially if cargo not self-propelled.
|
||||
-- DONE: Test capturing a neutral warehouse.
|
||||
-- DONE: Add save/load capability of warehouse <==> persistance after mission restart. Difficult in lua!
|
||||
-- DONE: Get cargo bay and weight from CARGO_GROUP and GROUP. No necessary any more!
|
||||
@@ -1829,7 +1839,6 @@ WAREHOUSE.version="1.0.2"
|
||||
-- @param #string alias (Optional) Alias of the warehouse, i.e. the name it will be called when sending messages etc. Default is the name of the static
|
||||
-- @return #WAREHOUSE self
|
||||
function WAREHOUSE:New(warehouse, alias)
|
||||
BASE:T({warehouse=warehouse})
|
||||
|
||||
-- Check if just a string was given and convert to static.
|
||||
if type(warehouse)=="string" then
|
||||
@@ -2602,8 +2611,10 @@ end
|
||||
--- Check parking ID.
|
||||
-- @param #WAREHOUSE self
|
||||
-- @param Wrapper.Airbase#AIRBASE.ParkingSpot spot Parking spot.
|
||||
-- @param Wrapper.Airbase#AIRBASE airbase The airbase.
|
||||
-- @return #boolean If true, parking is valid.
|
||||
function WAREHOUSE:_CheckParkingValid(spot)
|
||||
function WAREHOUSE:_CheckParkingValid(spot, airbase)
|
||||
|
||||
if self.parkingIDs==nil then
|
||||
return true
|
||||
end
|
||||
@@ -2709,6 +2720,18 @@ function WAREHOUSE:SetPortZone(zone)
|
||||
return self
|
||||
end
|
||||
|
||||
--- Add a Harbor Zone for this warehouse where naval cargo units will spawn and be received.
|
||||
-- Both warehouses must have the harbor zone defined for units to properly spawn on both the
|
||||
-- sending and receiving side. The harbor zone should be within 3km of the port zone used for
|
||||
-- warehouse in order to facilitate the boarding process.
|
||||
-- @param #WAREHOUSE self
|
||||
-- @param Core.Zone#ZONE zone The zone defining the naval embarcation/debarcation point for cargo units
|
||||
-- @return #WAREHOUSE self
|
||||
function WAREHOUSE:SetHarborZone(zone)
|
||||
self.harborzone=zone
|
||||
return self
|
||||
end
|
||||
|
||||
--- Add a shipping lane from this warehouse to another remote warehouse.
|
||||
-- Note that both warehouses must have a port zone defined before a shipping lane can be added!
|
||||
-- Shipping lane is taken from the waypoints of a (late activated) template group. So set up a group, e.g. a ship or a helicopter, and place its
|
||||
@@ -4340,10 +4363,10 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request)
|
||||
self:_ErrorMessage("ERROR: Cargo transport by train not supported yet!")
|
||||
return
|
||||
|
||||
elseif Request.transporttype==WAREHOUSE.TransportType.SHIP then
|
||||
elseif Request.transporttype==WAREHOUSE.TransportType.SHIP or Request.transporttype==WAREHOUSE.TransportType.NAVALCARRIER then
|
||||
|
||||
self:_ErrorMessage("ERROR: Cargo transport by ship not supported yet!")
|
||||
return
|
||||
-- Spawn Ship in port zone
|
||||
spawngroup=self:_SpawnAssetGroundNaval(_alias, _assetitem, Request, self.portzone)
|
||||
|
||||
elseif Request.transporttype==WAREHOUSE.TransportType.SELFPROPELLED then
|
||||
|
||||
@@ -4471,6 +4494,9 @@ function WAREHOUSE:onafterRequestSpawned(From, Event, To, Request, CargoGroupSet
|
||||
--_boardradius=nil
|
||||
elseif Request.transporttype==WAREHOUSE.TransportType.APC then
|
||||
--_boardradius=nil
|
||||
elseif Request.transporttype==WAREHOUSE.TransportType.SHIP or Request.transporttype==WAREHOUSE.TransportType.AIRCRAFTCARRIER
|
||||
or Request.transporttype==WAREHOUSE.TransportType.ARMEDSHIP or Request.transporttype==WAREHOUSE.TransportType.WARSHIP then
|
||||
_boardradius=6000
|
||||
end
|
||||
|
||||
-- Empty cargo group set.
|
||||
@@ -4481,7 +4507,6 @@ function WAREHOUSE:onafterRequestSpawned(From, Event, To, Request, CargoGroupSet
|
||||
|
||||
-- Find asset belonging to this group.
|
||||
local asset=self:FindAssetInDB(_group)
|
||||
|
||||
-- New cargo group object.
|
||||
local cargogroup=CARGO_GROUP:New(_group, _cargotype,_group:GetName(),_boardradius, asset.loadradius)
|
||||
|
||||
@@ -4490,6 +4515,7 @@ function WAREHOUSE:onafterRequestSpawned(From, Event, To, Request, CargoGroupSet
|
||||
|
||||
-- Add group to group set.
|
||||
CargoGroups:AddCargo(cargogroup)
|
||||
|
||||
end
|
||||
|
||||
------------------------
|
||||
@@ -4535,23 +4561,54 @@ function WAREHOUSE:onafterRequestSpawned(From, Event, To, Request, CargoGroupSet
|
||||
-- Set home zone.
|
||||
CargoTransport:SetHomeZone(self.spawnzone)
|
||||
|
||||
elseif Request.transporttype==WAREHOUSE.TransportType.SHIP or Request.transporttype==WAREHOUSE.TransportType.AIRCRAFTCARRIER
|
||||
or Request.transporttype==WAREHOUSE.TransportType.ARMEDSHIP or Request.transporttype==WAREHOUSE.TransportType.WARSHIP then
|
||||
|
||||
-- Pickup and deploy zones.
|
||||
local PickupZoneSet = SET_ZONE:New():AddZone(self.portzone)
|
||||
PickupZoneSet:AddZone(self.harborzone)
|
||||
local DeployZoneSet = SET_ZONE:New():AddZone(Request.warehouse.harborzone)
|
||||
|
||||
|
||||
-- Get the shipping lane to use and pass it to the Dispatcher
|
||||
local remotename = Request.warehouse.warehouse:GetName()
|
||||
local ShippingLane = self.shippinglanes[remotename][math.random(#self.shippinglanes[remotename])]
|
||||
|
||||
-- Define dispatcher for this task.
|
||||
CargoTransport = AI_CARGO_DISPATCHER_SHIP:New(TransportGroupSet, CargoGroups, PickupZoneSet, DeployZoneSet, ShippingLane)
|
||||
|
||||
-- Set home zone
|
||||
CargoTransport:SetHomeZone(self.portzone)
|
||||
|
||||
else
|
||||
self:E(self.lid.."ERROR: Unknown transporttype!")
|
||||
end
|
||||
|
||||
-- Set pickup and deploy radii.
|
||||
-- The 20 m inner radius are to ensure that the helo does not land on the warehouse itself in the middle of the default spawn zone.
|
||||
local pickupouter=200
|
||||
local pickupinner=0
|
||||
if self.spawnzone.Radius~=nil then
|
||||
pickupouter=self.spawnzone.Radius
|
||||
local pickupouter = 200
|
||||
local pickupinner = 0
|
||||
local deployouter = 200
|
||||
local deployinner = 0
|
||||
if Request.transporttype==WAREHOUSE.TransportType.SHIP or Request.transporttype==WAREHOUSE.TransportType.AIRCRAFTCARRIER
|
||||
or Request.transporttype==WAREHOUSE.TransportType.ARMEDSHIP or Request.transporttype==WAREHOUSE.TransportType.WARSHIP then
|
||||
pickupouter=1000
|
||||
pickupinner=20
|
||||
end
|
||||
local deployouter=200
|
||||
local deployinner=0
|
||||
if self.spawnzone.Radius~=nil then
|
||||
deployouter=Request.warehouse.spawnzone.Radius
|
||||
deployinner=20
|
||||
deployouter=1000
|
||||
deployinner=0
|
||||
else
|
||||
pickupouter=200
|
||||
pickupinner=0
|
||||
if self.spawnzone.Radius~=nil then
|
||||
pickupouter=self.spawnzone.Radius
|
||||
pickupinner=20
|
||||
end
|
||||
deployouter=200
|
||||
deployinner=0
|
||||
if self.spawnzone.Radius~=nil then
|
||||
deployouter=Request.warehouse.spawnzone.Radius
|
||||
deployinner=20
|
||||
end
|
||||
end
|
||||
CargoTransport:SetPickupRadius(pickupouter, pickupinner)
|
||||
CargoTransport:SetDeployRadius(deployouter, deployinner)
|
||||
@@ -4630,7 +4687,7 @@ function WAREHOUSE:onafterRequestSpawned(From, Event, To, Request, CargoGroupSet
|
||||
-- Get cargo group object.
|
||||
local group=Cargo:GetObject() --Wrapper.Group#GROUP
|
||||
|
||||
-- Get request.
|
||||
-- Get request.
|
||||
local request=warehouse:_GetRequestOfGroup(group, warehouse.pending)
|
||||
|
||||
-- Add cargo group to this carrier.
|
||||
@@ -6925,11 +6982,16 @@ function WAREHOUSE:_CheckRequestValid(request)
|
||||
valid=false
|
||||
end
|
||||
|
||||
elseif request.transporttype==WAREHOUSE.TransportType.SHIP then
|
||||
elseif request.transporttype==WAREHOUSE.TransportType.SHIP or request.transporttype==WAREHOUSE.TransportType.AIRCRAFTCARRIER
|
||||
or request.transporttype==WAREHOUSE.TransportType.ARMEDSHIP or request.transporttype==WAREHOUSE.TransportType.WARSHIP then
|
||||
|
||||
-- Transport by ship.
|
||||
self:E("ERROR: Incorrect request. Transport by SHIP not implemented yet!")
|
||||
valid=false
|
||||
local shippinglane=self:HasConnectionNaval(request.warehouse)
|
||||
|
||||
if not shippinglane then
|
||||
self:E("ERROR: Incorrect request. No shipping lane has been defined between warehouses!")
|
||||
valid=false
|
||||
end
|
||||
|
||||
elseif request.transporttype==WAREHOUSE.TransportType.TRAIN then
|
||||
|
||||
@@ -7025,7 +7087,7 @@ function WAREHOUSE:_CheckRequestNow(request)
|
||||
|
||||
-- If no transport is requested, assets need to be mobile unless it is a self request.
|
||||
local onlymobile=false
|
||||
if type(request.transport)=="number" and request.ntransport==0 and not request.toself then
|
||||
if type(request.ntransport)=="number" and request.ntransport==0 and not request.toself then
|
||||
onlymobile=true
|
||||
end
|
||||
|
||||
@@ -7607,7 +7669,7 @@ function WAREHOUSE:_FindParkingForAssets(airbase, assets)
|
||||
local parkingspot=_parkingspot --Wrapper.Airbase#AIRBASE.ParkingSpot
|
||||
|
||||
-- Check correct terminal type for asset. We don't want helos in shelters etc.
|
||||
if AIRBASE._CheckTerminalType(parkingspot.TerminalType, terminaltype) and self:_CheckParkingValid(parkingspot) then
|
||||
if AIRBASE._CheckTerminalType(parkingspot.TerminalType, terminaltype) and self:_CheckParkingValid(parkingspot, airbase) and airbase:_CheckParkingLists(parkingspot.TerminalID) then
|
||||
|
||||
-- Coordinate of the parking spot.
|
||||
local _spot=parkingspot.Coordinate -- Core.Point#COORDINATE
|
||||
@@ -8052,10 +8114,9 @@ function WAREHOUSE:_GetAttribute(group)
|
||||
-- Ships
|
||||
local aircraftcarrier=group:HasAttribute("Aircraft Carriers")
|
||||
local warship=group:HasAttribute("Heavy armed ships")
|
||||
local armedship=group:HasAttribute("Armed ships")
|
||||
local armedship=group:HasAttribute("Armed ships") or group:HasAttribute("Armed Ship")
|
||||
local unarmedship=group:HasAttribute("Unarmed ships")
|
||||
|
||||
|
||||
-- Define attribute. Order is important.
|
||||
if transportplane then
|
||||
attribute=WAREHOUSE.Attribute.AIR_TRANSPORTPLANE
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
__Moose.Include( 'Scripts/Moose/Utilities/Enums.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Utilities/Routines.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Utilities/Utils.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Utilities/Profiler.lua' )
|
||||
|
||||
__Moose.Include( 'Scripts/Moose/Core/Base.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Core/UserFlag.lua' )
|
||||
@@ -24,6 +25,7 @@ __Moose.Include( 'Scripts/Moose/Core/RadioQueue.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Core/RadioSpeech.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Core/Spawn.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Core/SpawnStatic.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Core/Timer.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Core/Goal.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Core/Spot.lua' )
|
||||
|
||||
@@ -37,6 +39,7 @@ __Moose.Include( 'Scripts/Moose/Wrapper/Client.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Wrapper/Static.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Wrapper/Airbase.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Wrapper/Scenery.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Wrapper/Marker.lua' )
|
||||
|
||||
__Moose.Include( 'Scripts/Moose/Cargo/Cargo.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Cargo/CargoUnit.lua' )
|
||||
@@ -64,6 +67,7 @@ __Moose.Include( 'Scripts/Moose/Functional/Suppression.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Functional/PseudoATC.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Functional/Warehouse.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Functional/Fox.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Functional/Mantis.lua' )
|
||||
|
||||
__Moose.Include( 'Scripts/Moose/Ops/Airboss.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Ops/RecoveryTanker.lua' )
|
||||
@@ -95,10 +99,12 @@ __Moose.Include( 'Scripts/Moose/AI/AI_Cargo.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/AI/AI_Cargo_APC.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/AI/AI_Cargo_Helicopter.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/AI/AI_Cargo_Airplane.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/AI/AI_Cargo_Ship.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/AI/AI_Cargo_Dispatcher.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/AI/AI_Cargo_Dispatcher_APC.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/AI/AI_Cargo_Dispatcher_Airplane.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/AI/AI_Cargo_Dispatcher_Ship.lua' )
|
||||
|
||||
__Moose.Include( 'Scripts/Moose/Actions/Act_Assign.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Actions/Act_Route.lua' )
|
||||
|
||||
@@ -67,8 +67,10 @@
|
||||
-- @field #number subduration Duration how long subtitles are displayed in seconds.
|
||||
-- @field #boolean metric If true, use metric units. If false, use imperial (default).
|
||||
-- @field #boolean PmmHg If true, give pressure in millimeters of Mercury. Default is inHg for imperial and hecto Pascal (=mili Bars) for metric units.
|
||||
-- @field #boolean qnhonly If true, suppresses reporting QFE. Default is to report both QNH and QFE.
|
||||
-- @field #boolean TDegF If true, give temperature in degrees Fahrenheit. Default is in degrees Celsius independent of chosen unit system.
|
||||
-- @field #number zuludiff Time difference local vs. zulu in hours.
|
||||
-- @field #boolean zulutimeonly If true, suppresses report of local time, sunrise, and sunset.
|
||||
-- @field #number magvar Magnetic declination/variation at the airport in degrees.
|
||||
-- @field #table ils Table of ILS frequencies (can be runway specific).
|
||||
-- @field #table ndbinner Table of inner NDB frequencies (can be runway specific).
|
||||
@@ -302,8 +304,10 @@ ATIS = {
|
||||
subduration = nil,
|
||||
metric = nil,
|
||||
PmmHg = nil,
|
||||
qnhonly = false,
|
||||
TDegF = nil,
|
||||
zuludiff = nil,
|
||||
zulutimeonly = false,
|
||||
magvar = nil,
|
||||
ils = {},
|
||||
ndbinner = {},
|
||||
@@ -362,11 +366,32 @@ ATIS.Alphabet = {
|
||||
-- @field #number Nevada +12° (East).
|
||||
-- @field #number Normandy -10° (West).
|
||||
-- @field #number PersianGulf +2° (East).
|
||||
-- @field #number TheChannel -10° (West).
|
||||
-- @field #number Syria +5° (East).
|
||||
ATIS.RunwayM2T={
|
||||
Caucasus=0,
|
||||
Nevada=12,
|
||||
Normany=-10,
|
||||
Normandy=-10,
|
||||
PersianGulf=2,
|
||||
TheChannel=-10,
|
||||
Syria=5,
|
||||
}
|
||||
|
||||
--- Whether ICAO phraseology is used for ATIS broadcasts.
|
||||
-- @type ATIS.ICAOPhraseology
|
||||
-- @field #boolean Caucasus true.
|
||||
-- @field #boolean Nevada false.
|
||||
-- @field #boolean Normandy true.
|
||||
-- @field #boolean PersianGulf true.
|
||||
-- @field #boolean TheChannel true.
|
||||
-- @field #boolean Syria true.
|
||||
ATIS.ICAOPhraseology={
|
||||
Caucasus=true,
|
||||
Nevada=false,
|
||||
Normandy=true,
|
||||
PersianGulf=true,
|
||||
TheChannel=true,
|
||||
Syria=true,
|
||||
}
|
||||
|
||||
--- Nav point data.
|
||||
@@ -415,6 +440,7 @@ ATIS.RunwayM2T={
|
||||
-- @field #ATIS.Soundfile MegaHertz
|
||||
-- @field #ATIS.Soundfile Meters
|
||||
-- @field #ATIS.Soundfile MetersPerSecond
|
||||
-- @field #ATIS.Soundfile Miles
|
||||
-- @field #ATIS.Soundfile MillimetersOfMercury
|
||||
-- @field #ATIS.Soundfile N0
|
||||
-- @field #ATIS.Soundfile N1
|
||||
@@ -434,6 +460,8 @@ ATIS.RunwayM2T={
|
||||
-- @field #ATIS.Soundfile Right
|
||||
-- @field #ATIS.Soundfile Snow
|
||||
-- @field #ATIS.Soundfile SnowStorm
|
||||
-- @field #ATIS.Soundfile SunriseAt
|
||||
-- @field #ATIS.Soundfile SunsetAt
|
||||
-- @field #ATIS.Soundfile Temperature
|
||||
-- @field #ATIS.Soundfile Thousand
|
||||
-- @field #ATIS.Soundfile ThunderStorm
|
||||
@@ -485,6 +513,7 @@ ATIS.Sound = {
|
||||
MegaHertz={filename="MegaHertz.ogg", duration=0.87},
|
||||
Meters={filename="Meters.ogg", duration=0.59},
|
||||
MetersPerSecond={filename="MetersPerSecond.ogg", duration=1.14},
|
||||
Miles={filename="Miles.ogg", duration=0.60},
|
||||
MillimetersOfMercury={filename="MillimetersOfMercury.ogg", duration=1.53},
|
||||
Minus={filename="Minus.ogg", duration=0.64},
|
||||
N0={filename="N-0.ogg", duration=0.55},
|
||||
@@ -505,6 +534,9 @@ ATIS.Sound = {
|
||||
Right={filename="Right.ogg", duration=0.44},
|
||||
Snow={filename="Snow.ogg", duration=0.48},
|
||||
SnowStorm={filename="SnowStorm.ogg", duration=0.82},
|
||||
StatuteMiles={filename="StatuteMiles.ogg", duration=1.15},
|
||||
SunriseAt={filename="SunriseAt.ogg", duration=0.92},
|
||||
SunsetAt={filename="SunsetAt.ogg", duration=0.95},
|
||||
Temperature={filename="Temperature.ogg", duration=0.64},
|
||||
Thousand={filename="Thousand.ogg", duration=0.55},
|
||||
ThunderStorm={filename="ThunderStorm.ogg", duration=0.81},
|
||||
@@ -522,6 +554,7 @@ ATIS.Sound = {
|
||||
TACANChannel={filename="TACANChannel.ogg", duration=0.88},
|
||||
PRMGChannel={filename="PRMGChannel.ogg", duration=1.18},
|
||||
RSBNChannel={filename="RSBNChannel.ogg", duration=1.14},
|
||||
Zulu={filename="Zulu.ogg", duration=0.62},
|
||||
}
|
||||
|
||||
|
||||
@@ -531,7 +564,7 @@ _ATIS={}
|
||||
|
||||
--- ATIS class version.
|
||||
-- @field #string version
|
||||
ATIS.version="0.7.1"
|
||||
ATIS.version="0.9.0"
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- TODO list
|
||||
@@ -599,12 +632,12 @@ 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("*", "Report", "*") -- Report ATIS text.
|
||||
self:AddTransition("*", "Stop", "Stopped") -- Stop.
|
||||
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("*", "Report", "*") -- Report ATIS text.
|
||||
self:AddTransition("*", "Stop", "Stopped") -- Stop.
|
||||
|
||||
------------------------
|
||||
--- Pseudo Functions ---
|
||||
@@ -894,6 +927,14 @@ function ATIS:SetAltimeterQNH(switch)
|
||||
return self
|
||||
end
|
||||
|
||||
--- Suppresses QFE readout. Default is to report both QNH and QFE.
|
||||
-- @param #ATIS self
|
||||
-- @return #ATIS self
|
||||
function ATIS:ReportQNHOnly()
|
||||
self.qnhonly=true
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set magnetic declination/variation at the airport.
|
||||
--
|
||||
-- Default is per map:
|
||||
@@ -943,9 +984,10 @@ end
|
||||
-- Default is per map:
|
||||
--
|
||||
-- * Caucasus +4
|
||||
-- * Nevada -7
|
||||
-- * Normandy +1
|
||||
-- * Nevada -8
|
||||
-- * Normandy 0
|
||||
-- * Persian Gulf +4
|
||||
-- * The Channel +2 (should be 0)
|
||||
--
|
||||
-- @param #ATIS self
|
||||
-- @param #number delta Time difference in hours.
|
||||
@@ -955,6 +997,14 @@ function ATIS:SetZuluTimeDifference(delta)
|
||||
return self
|
||||
end
|
||||
|
||||
--- Suppresses local time, sunrise, and sunset. Default is to report all these times.
|
||||
-- @param #ATIS self
|
||||
-- @return #ATIS self
|
||||
function ATIS:ReportZuluTimeOnly()
|
||||
self.zulutimeonly=true
|
||||
return self
|
||||
end
|
||||
|
||||
--- Add ILS station. Note that this can be runway specific.
|
||||
-- @param #ATIS self
|
||||
-- @param #number frequency ILS frequency in MHz.
|
||||
@@ -1244,8 +1294,6 @@ function ATIS:onafterBroadcast(From, Event, To)
|
||||
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)
|
||||
@@ -1267,17 +1315,13 @@ function ATIS:onafterBroadcast(From, Event, To)
|
||||
-- User specified.
|
||||
time=time-self.zuludiff*60*60
|
||||
else
|
||||
if self.theatre==DCSMAP.Caucasus then
|
||||
time=time-4*60*60 -- Caucasus UTC+4 hours
|
||||
elseif self.theatre==DCSMAP.PersianGulf then
|
||||
time=time-4*60*60 -- Abu Dhabi UTC+4 hours
|
||||
elseif self.theatre==DCSMAP.NTTR then
|
||||
time=time+7*60*60 -- Las Vegas UTC-7 hours
|
||||
elseif self.theatre==DCSMAP.Normandy then
|
||||
time=time-1*60*60 -- Calais UTC+1 hour
|
||||
end
|
||||
time=time-UTILS.GMTToLocalTimeDifference()*60*60
|
||||
end
|
||||
|
||||
if time < 0 then
|
||||
time = 24*60*60 + time --avoid negative time around midnight
|
||||
end
|
||||
|
||||
local clock=UTILS.SecondsToClock(time)
|
||||
local zulu=UTILS.Split(clock, ":")
|
||||
local ZULU=string.format("%s%s", zulu[1], zulu[2])
|
||||
@@ -1293,6 +1337,18 @@ function ATIS:onafterBroadcast(From, Event, To)
|
||||
self:T3(string.format("ZULU =%s", tostring(ZULU)))
|
||||
self:T3(string.format("NATO =%s", tostring(NATO)))
|
||||
|
||||
--------------------------
|
||||
--- Sunrise and Sunset ---
|
||||
--------------------------
|
||||
|
||||
local sunrise=coord:GetSunrise()
|
||||
sunrise=UTILS.Split(sunrise, ":")
|
||||
local SUNRISE=string.format("%s%s", sunrise[1], sunrise[2])
|
||||
|
||||
local sunset=coord:GetSunset()
|
||||
sunset=UTILS.Split(sunset, ":")
|
||||
local SUNSET=string.format("%s%s", sunset[1], sunset[2])
|
||||
|
||||
---------------------------------
|
||||
--- Temperature and Dew Point ---
|
||||
---------------------------------
|
||||
@@ -1347,13 +1403,24 @@ function ATIS:onafterBroadcast(From, Event, To)
|
||||
visibilitymin=dust
|
||||
end
|
||||
end
|
||||
|
||||
local VISIBILITY=""
|
||||
|
||||
-- Visibility in NM.
|
||||
local VISIBILITY=string.format("%d", UTILS.Round(UTILS.MetersToNM(visibilitymin)))
|
||||
|
||||
-- Visibility in km.
|
||||
if self.metric then
|
||||
VISIBILITY=string.format("%d", UTILS.Round(visibilitymin/1000))
|
||||
-- Visibility in km.
|
||||
local reportedviz=UTILS.Round(visibilitymin/1000)
|
||||
-- max reported visibility 9999 m
|
||||
if reportedviz > 10 then
|
||||
reportedviz=10
|
||||
end
|
||||
VISIBILITY=string.format("%d", reportedviz)
|
||||
else
|
||||
-- max reported visibility 10 NM
|
||||
local reportedviz=UTILS.Round(UTILS.MetersToSM(visibilitymin))
|
||||
if reportedviz > 10 then
|
||||
reportedviz=10
|
||||
end
|
||||
VISIBILITY=string.format("%d", reportedviz)
|
||||
end
|
||||
|
||||
--------------
|
||||
@@ -1441,60 +1508,65 @@ function ATIS:onafterBroadcast(From, Event, To)
|
||||
-- Zulu Time
|
||||
subtitle=string.format("%s Zulu", ZULU)
|
||||
self.radioqueue:Number2Transmission(ZULU, nil, 0.5)
|
||||
self:Transmission(ATIS.Sound.TimeZulu, 0.2, subtitle)
|
||||
self:Transmission(ATIS.Sound.Zulu, 0.2, subtitle)
|
||||
alltext=alltext..";\n"..subtitle
|
||||
|
||||
if not self.zulutimeonly then
|
||||
|
||||
-- Sunrise Time
|
||||
subtitle=string.format("Sunrise at %s local time", SUNRISE)
|
||||
self:Transmission(ATIS.Sound.SunriseAt, 0.5, subtitle)
|
||||
self.radioqueue:Number2Transmission(SUNRISE, nil, 0.2)
|
||||
self:Transmission(ATIS.Sound.TimeLocal, 0.2)
|
||||
alltext=alltext..";\n"..subtitle
|
||||
|
||||
-- Sunset Time
|
||||
subtitle=string.format("Sunset at %s local time", SUNSET)
|
||||
self:Transmission(ATIS.Sound.SunsetAt, 0.5, subtitle)
|
||||
self.radioqueue:Number2Transmission(SUNSET, nil, 0.5)
|
||||
self:Transmission(ATIS.Sound.TimeLocal, 0.2)
|
||||
alltext=alltext..";\n"..subtitle
|
||||
end
|
||||
|
||||
-- Wind
|
||||
if self.metric then
|
||||
subtitle=string.format("Wind from %s at %s m/s", WINDFROM, WINDSPEED)
|
||||
else
|
||||
subtitle=string.format("Wind from %s at %s knots", WINDFROM, WINDSPEED)
|
||||
end
|
||||
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)
|
||||
self.radioqueue:Number2Transmission(WINDSPEED)
|
||||
if self.metric then
|
||||
self:Transmission(ATIS.Sound.MetersPerSecond, 0.2)
|
||||
else
|
||||
self:Transmission(ATIS.Sound.Knots, 0.2)
|
||||
end
|
||||
if turbulence>0 then
|
||||
self:Transmission(ATIS.Sound.Gusting, 0.2)
|
||||
end
|
||||
alltext=alltext..";\n"..subtitle
|
||||
|
||||
-- Visibility
|
||||
if self.metric then
|
||||
subtitle=string.format("Visibility %s km", VISIBILITY)
|
||||
else
|
||||
subtitle=string.format("Visibility %s NM", VISIBILITY)
|
||||
subtitle=string.format("Visibility %s SM", VISIBILITY)
|
||||
end
|
||||
self:Transmission(ATIS.Sound.Visibilty, 1.0, subtitle)
|
||||
self.radioqueue:Number2Transmission(VISIBILITY)
|
||||
if self.metric then
|
||||
self:Transmission(ATIS.Sound.Kilometers, 0.2)
|
||||
else
|
||||
self:Transmission(ATIS.Sound.NauticalMiles, 0.2)
|
||||
self:Transmission(ATIS.Sound.StatuteMiles, 0.2)
|
||||
end
|
||||
alltext=alltext..";\n"..subtitle
|
||||
|
||||
-- Cloud base
|
||||
self:Transmission(CloudCover, 1.0, CLOUDSsub)
|
||||
if CLOUDBASE and static then
|
||||
-- Base
|
||||
if self.metric then
|
||||
subtitle=string.format("Cloudbase %s, ceiling %s meters", CLOUDBASE, CLOUDCEIL)
|
||||
else
|
||||
subtitle=string.format("Cloudbase %s, ceiling %s ft", CLOUDBASE, CLOUDCEIL)
|
||||
end
|
||||
self:Transmission(ATIS.Sound.CloudBase, 1.0, subtitle)
|
||||
if tonumber(CLOUDBASE1000)>0 then
|
||||
self.radioqueue:Number2Transmission(CLOUDBASE1000)
|
||||
self:Transmission(ATIS.Sound.Thousand, 0.1)
|
||||
end
|
||||
if tonumber(CLOUDBASE0100)>0 then
|
||||
self.radioqueue:Number2Transmission(CLOUDBASE0100)
|
||||
self:Transmission(ATIS.Sound.Hundred, 0.1)
|
||||
end
|
||||
-- Ceiling
|
||||
self:Transmission(ATIS.Sound.CloudCeiling, 0.5)
|
||||
if tonumber(CLOUDCEIL1000)>0 then
|
||||
self.radioqueue:Number2Transmission(CLOUDCEIL1000)
|
||||
self:Transmission(ATIS.Sound.Thousand, 0.1)
|
||||
end
|
||||
if tonumber(CLOUDCEIL0100)>0 then
|
||||
self.radioqueue:Number2Transmission(CLOUDCEIL0100)
|
||||
self:Transmission(ATIS.Sound.Hundred, 0.1)
|
||||
end
|
||||
if self.metric then
|
||||
self:Transmission(ATIS.Sound.Meters, 0.1)
|
||||
else
|
||||
self:Transmission(ATIS.Sound.Feet, 0.1)
|
||||
end
|
||||
end
|
||||
alltext=alltext..";\n"..subtitle
|
||||
|
||||
|
||||
-- Weather phenomena
|
||||
local wp=false
|
||||
local wpsub=""
|
||||
@@ -1550,37 +1622,42 @@ function ATIS:onafterBroadcast(From, Event, To)
|
||||
alltext=alltext..";\n"..subtitle
|
||||
end
|
||||
|
||||
-- Altimeter QNH/QFE.
|
||||
if self.PmmHg then
|
||||
subtitle=string.format("Altimeter QNH %s.%s, QFE %s.%s mmHg", QNH[1], QNH[2], QFE[1], QFE[2])
|
||||
else
|
||||
-- Cloud base
|
||||
self:Transmission(CloudCover, 1.0, CLOUDSsub)
|
||||
if CLOUDBASE and static then
|
||||
-- Base
|
||||
if self.metric then
|
||||
subtitle=string.format("Altimeter QNH %s.%s, QFE %s.%s hPa", QNH[1], QNH[2], QFE[1], QFE[2])
|
||||
subtitle=string.format("Cloudbase %s, ceiling %s meters", CLOUDBASE, CLOUDCEIL)
|
||||
else
|
||||
subtitle=string.format("Altimeter QNH %s.%s, QFE %s.%s inHg", QNH[1], QNH[2], QFE[1], QFE[2])
|
||||
subtitle=string.format("Cloudbase %s, ceiling %s ft", CLOUDBASE, CLOUDCEIL)
|
||||
end
|
||||
self:Transmission(ATIS.Sound.CloudBase, 1.0, subtitle)
|
||||
if tonumber(CLOUDBASE1000)>0 then
|
||||
self.radioqueue:Number2Transmission(CLOUDBASE1000)
|
||||
self:Transmission(ATIS.Sound.Thousand, 0.1)
|
||||
end
|
||||
if tonumber(CLOUDBASE0100)>0 then
|
||||
self.radioqueue:Number2Transmission(CLOUDBASE0100)
|
||||
self:Transmission(ATIS.Sound.Hundred, 0.1)
|
||||
end
|
||||
-- Ceiling
|
||||
self:Transmission(ATIS.Sound.CloudCeiling, 0.5)
|
||||
if tonumber(CLOUDCEIL1000)>0 then
|
||||
self.radioqueue:Number2Transmission(CLOUDCEIL1000)
|
||||
self:Transmission(ATIS.Sound.Thousand, 0.1)
|
||||
end
|
||||
if tonumber(CLOUDCEIL0100)>0 then
|
||||
self.radioqueue:Number2Transmission(CLOUDCEIL0100)
|
||||
self:Transmission(ATIS.Sound.Hundred, 0.1)
|
||||
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])
|
||||
self:Transmission(ATIS.Sound.Decimal, 0.2)
|
||||
self.radioqueue:Number2Transmission(QNH[2])
|
||||
self:Transmission(ATIS.Sound.QFE, 0.75)
|
||||
self.radioqueue:Number2Transmission(QFE[1])
|
||||
self:Transmission(ATIS.Sound.Decimal, 0.2)
|
||||
self.radioqueue:Number2Transmission(QFE[2])
|
||||
if self.PmmHg then
|
||||
self:Transmission(ATIS.Sound.MillimetersOfMercury, 0.1)
|
||||
else
|
||||
if self.metric then
|
||||
self:Transmission(ATIS.Sound.HectoPascal, 0.1)
|
||||
self:Transmission(ATIS.Sound.Meters, 0.1)
|
||||
else
|
||||
self:Transmission(ATIS.Sound.InchesOfMercury, 0.1)
|
||||
self:Transmission(ATIS.Sound.Feet, 0.1)
|
||||
end
|
||||
end
|
||||
alltext=alltext..";\n"..subtitle
|
||||
|
||||
|
||||
-- Temperature
|
||||
if self.TDegF then
|
||||
if temperature<0 then
|
||||
@@ -1607,7 +1684,7 @@ function ATIS:onafterBroadcast(From, Event, To)
|
||||
self:Transmission(ATIS.Sound.DegreesCelsius, 0.2)
|
||||
end
|
||||
alltext=alltext..";\n"..subtitle
|
||||
|
||||
|
||||
-- Dew point
|
||||
if self.TDegF then
|
||||
if dewpoint<0 then
|
||||
@@ -1635,27 +1712,57 @@ function ATIS:onafterBroadcast(From, Event, To)
|
||||
end
|
||||
alltext=alltext..";\n"..subtitle
|
||||
|
||||
-- Wind
|
||||
if self.metric then
|
||||
subtitle=string.format("Wind from %s at %s m/s", WINDFROM, WINDSPEED)
|
||||
-- Altimeter QNH/QFE.
|
||||
if self.PmmHg then
|
||||
if self.qnhonly then
|
||||
subtitle=string.format("Altimeter %s.%s mmHg", QNH[1], QNH[2])
|
||||
else
|
||||
subtitle=string.format("Altimeter QNH %s.%s, QFE %s.%s mmHg", QNH[1], QNH[2], QFE[1], QFE[2])
|
||||
end
|
||||
else
|
||||
subtitle=string.format("Wind from %s at %s knots", WINDFROM, WINDSPEED)
|
||||
if self.metric then
|
||||
if self.qnhonly then
|
||||
subtitle=string.format("Altimeter %s.%s hPa", QNH[1], QNH[2])
|
||||
else
|
||||
subtitle=string.format("Altimeter QNH %s.%s, QFE %s.%s hPa", QNH[1], QNH[2], QFE[1], QFE[2])
|
||||
end
|
||||
else
|
||||
if self.qnhonly then
|
||||
subtitle=string.format("Altimeter %s.%s inHg", QNH[1], QNH[2])
|
||||
else
|
||||
subtitle=string.format("Altimeter QNH %s.%s, QFE %s.%s inHg", QNH[1], QNH[2], QFE[1], QFE[2])
|
||||
end
|
||||
end
|
||||
end
|
||||
if turbulence>0 then
|
||||
subtitle=subtitle..", gusting"
|
||||
local _ALTIMETER=subtitle
|
||||
self:Transmission(ATIS.Sound.Altimeter, 1.0, subtitle)
|
||||
if not self.qnhonly then
|
||||
self:Transmission(ATIS.Sound.QNH, 0.5)
|
||||
end
|
||||
local _WIND=subtitle
|
||||
self:Transmission(ATIS.Sound.WindFrom, 1.0, subtitle)
|
||||
self.radioqueue:Number2Transmission(WINDFROM)
|
||||
self:Transmission(ATIS.Sound.At, 0.2)
|
||||
self.radioqueue:Number2Transmission(WINDSPEED)
|
||||
if self.metric then
|
||||
self:Transmission(ATIS.Sound.MetersPerSecond, 0.2)
|
||||
self.radioqueue:Number2Transmission(QNH[1])
|
||||
|
||||
if ATIS.ICAOPhraseology[UTILS.GetDCSMap()] then
|
||||
self:Transmission(ATIS.Sound.Decimal, 0.2)
|
||||
end
|
||||
self.radioqueue:Number2Transmission(QNH[2])
|
||||
|
||||
if not self.qnhonly then
|
||||
self:Transmission(ATIS.Sound.QFE, 0.75)
|
||||
self.radioqueue:Number2Transmission(QFE[1])
|
||||
if ATIS.ICAOPhraseology[UTILS.GetDCSMap()] then
|
||||
self:Transmission(ATIS.Sound.Decimal, 0.2)
|
||||
end
|
||||
self.radioqueue:Number2Transmission(QFE[2])
|
||||
end
|
||||
|
||||
if self.PmmHg then
|
||||
self:Transmission(ATIS.Sound.MillimetersOfMercury, 0.1)
|
||||
else
|
||||
self:Transmission(ATIS.Sound.Knots, 0.2)
|
||||
end
|
||||
if turbulence>0 then
|
||||
self:Transmission(ATIS.Sound.Gusting, 0.2)
|
||||
if self.metric then
|
||||
self:Transmission(ATIS.Sound.HectoPascal, 0.1)
|
||||
else
|
||||
self:Transmission(ATIS.Sound.InchesOfMercury, 0.1)
|
||||
end
|
||||
end
|
||||
alltext=alltext..";\n"..subtitle
|
||||
|
||||
@@ -1873,13 +1980,6 @@ function ATIS:onafterBroadcast(From, Event, To)
|
||||
|
||||
alltext=alltext..";\n"..subtitle
|
||||
end
|
||||
|
||||
--[[
|
||||
-- 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)
|
||||
|
||||
@@ -27,12 +27,16 @@
|
||||
-- **Supported Carriers:**
|
||||
--
|
||||
-- * [USS John C. Stennis](https://en.wikipedia.org/wiki/USS_John_C._Stennis) (CVN-74)
|
||||
-- * [USS Theodore Roosevelt](https://en.wikipedia.org/wiki/USS_Theodore_Roosevelt_(CVN-71)) (CVN-71) [Super Carrier Module]
|
||||
-- * [USS Abraham Lincoln](https://en.wikipedia.org/wiki/USS_Abraham_Lincoln_(CVN-72)) (CVN-72) [Super Carrier Module]
|
||||
-- * [USS George Washington](https://en.wikipedia.org/wiki/USS_George_Washington_(CVN-73)) (CVN-73) [Super Carrier Module]
|
||||
-- * [USS Harry S. Truman](https://en.wikipedia.org/wiki/USS_Harry_S._Truman) (CVN-75) [Super Carrier Module]
|
||||
-- * [USS Tarawa](https://en.wikipedia.org/wiki/USS_Tarawa_(LHA-1)) (LHA-1) [**WIP**]
|
||||
--
|
||||
-- **Supported Aircraft:**
|
||||
--
|
||||
-- * [F/A-18C Hornet Lot 20](https://forums.eagle.ru/forumdisplay.php?f=557) (Player & AI)
|
||||
-- * [F-14B Tomcat](https://forums.eagle.ru/forumdisplay.php?f=395) (Player & AI)
|
||||
-- * [F-14A/B Tomcat](https://forums.eagle.ru/forumdisplay.php?f=395) (Player & AI)
|
||||
-- * [A-4E Skyhawk Community Mod](https://forums.eagle.ru/showthread.php?t=224989) (Player & AI)
|
||||
-- * [AV-8B N/A Harrier](https://forums.eagle.ru/forumdisplay.php?f=555) (Player & AI) [**WIP**]
|
||||
-- * F/A-18C Hornet (AI)
|
||||
@@ -47,7 +51,9 @@
|
||||
-- the no other fixed wing aircraft (human or AI controlled) are supposed to land on the Tarawa. Currently only Case I is supported. Case II/III take slightly steps from the CVN carrier.
|
||||
-- However, the two Case II/III pattern are very similar so this is not a big drawback.
|
||||
--
|
||||
-- Heatblur's mighty F-14B Tomcat has been added (March 13th 2019) as well.
|
||||
-- Heatblur's mighty F-14B Tomcat has been added (March 13th 2019) as well. Same goes for the A version.
|
||||
--
|
||||
-- The [DCS Supercarriers](https://forums.eagle.ru/forum/151-dcs-supercarrier/) are also supported.
|
||||
--
|
||||
-- ## Discussion
|
||||
--
|
||||
@@ -1261,7 +1267,7 @@ AIRBOSS.AircraftCarrier={
|
||||
AV8B="AV8BNA",
|
||||
HORNET="FA-18C_hornet",
|
||||
A4EC="A-4E-C",
|
||||
F14A="F-14A_tomcat",
|
||||
F14A="F-14A-135-GR",
|
||||
F14B="F-14B",
|
||||
F14A_AI="F-14A",
|
||||
FA18C="F/A-18C",
|
||||
@@ -1273,17 +1279,19 @@ AIRBOSS.AircraftCarrier={
|
||||
|
||||
--- 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 ROOSEVELT USS Theodore Roosevelt (CVN-71) [Super Carrier Module]
|
||||
-- @field #string LINCOLN USS Abraham Lincoln (CVN-72) [Super Carrier Module]
|
||||
-- @field #string WASHINGTON USS George Washington (CVN-73) [Super Carrier Module]
|
||||
-- @field #string STENNIS USS John C. Stennis (CVN-74)
|
||||
-- @field #string VINSON USS Carl Vinson (CVN-70)
|
||||
-- @field #string TRUMAN USS Harry S. Truman (CVN-75) [Super Carrier Module]
|
||||
-- @field #string VINSON USS Carl Vinson (CVN-70) [Obsolete]
|
||||
-- @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",
|
||||
TRUMAN="CVN_75",
|
||||
STENNIS="Stennis",
|
||||
VINSON="VINSON",
|
||||
TARAWA="LHA_Tarawa",
|
||||
@@ -1697,7 +1705,7 @@ AIRBOSS.MenuF10Root=nil
|
||||
|
||||
--- Airboss class version.
|
||||
-- @field #string version
|
||||
AIRBOSS.version="1.1.3"
|
||||
AIRBOSS.version="1.1.5"
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- TODO list
|
||||
@@ -1936,11 +1944,13 @@ function AIRBOSS:New(carriername, alias)
|
||||
if self.carriertype==AIRBOSS.CarrierType.STENNIS then
|
||||
self:_InitStennis()
|
||||
elseif self.carriertype==AIRBOSS.CarrierType.ROOSEVELT then
|
||||
self:_InitStennis()
|
||||
self:_InitNimitz()
|
||||
elseif self.carriertype==AIRBOSS.CarrierType.LINCOLN then
|
||||
self:_InitStennis()
|
||||
elseif self.carriertype==AIRBOSS.CarrierType.WASHINGTON then
|
||||
self:_InitStennis()
|
||||
self:_InitNimitz()
|
||||
elseif self.carriertype==AIRBOSS.CarrierType.WASHINGTON then
|
||||
self:_InitNimitz()
|
||||
elseif self.carriertype==AIRBOSS.CarrierType.TRUMAN then
|
||||
self:_InitNimitz()
|
||||
elseif self.carriertype==AIRBOSS.CarrierType.VINSON then
|
||||
-- TODO: Carl Vinson parameters.
|
||||
self:_InitStennis()
|
||||
@@ -2018,7 +2028,7 @@ function AIRBOSS:New(carriername, alias)
|
||||
|
||||
-- Bow
|
||||
bow:FlareYellow()
|
||||
|
||||
|
||||
-- Runway half width = 10 m.
|
||||
local r1=stern:Translate(self.carrierparam.rwywidth*0.5, FB+90)
|
||||
local r2=stern:Translate(self.carrierparam.rwywidth*0.5, FB-90)
|
||||
@@ -2464,7 +2474,7 @@ function AIRBOSS:AddRecoveryWindow(starttime, stoptime, case, holdingoffset, tur
|
||||
local Tstart=UTILS.ClockToSeconds(starttime)
|
||||
|
||||
-- Set stop time.
|
||||
local Tstop=UTILS.ClockToSeconds(stoptime or Tstart+90*60)
|
||||
local Tstop=stoptime and UTILS.ClockToSeconds(stoptime) or Tstart+90*60
|
||||
|
||||
-- Consistancy check for timing.
|
||||
if Tstart>Tstop then
|
||||
@@ -2624,10 +2634,10 @@ end
|
||||
|
||||
--- Set time before carrier turns and recovery window opens.
|
||||
-- @param #AIRBOSS self
|
||||
-- @param #number interval Time interval in seconds. Default 600 sec.
|
||||
-- @param #number interval Time interval in seconds. Default 300 sec.
|
||||
-- @return #AIRBOSS self
|
||||
function AIRBOSS:SetRecoveryTurnTime(interval)
|
||||
self.dTturn=interval or 600
|
||||
self.dTturn=interval or 300
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -4094,7 +4104,7 @@ function AIRBOSS:onafterRecoveryUnpause(From, Event, To)
|
||||
self:T(self.lid..string.format("Unpausing aircraft recovery."))
|
||||
|
||||
-- Resume recovery.
|
||||
self:_MarshalCallRecoveryResume()
|
||||
self:_MarshalCallResumeRecovery()
|
||||
|
||||
end
|
||||
|
||||
@@ -4292,7 +4302,36 @@ function AIRBOSS:_InitStennis()
|
||||
|
||||
end
|
||||
|
||||
--- Init parameters for USS Stennis carrier.
|
||||
--- Init parameters for Nimitz class super carriers.
|
||||
-- @param #AIRBOSS self
|
||||
function AIRBOSS:_InitNimitz()
|
||||
|
||||
-- Init Stennis as default.
|
||||
self:_InitStennis()
|
||||
|
||||
-- Carrier Parameters.
|
||||
self.carrierparam.sterndist =-164
|
||||
self.carrierparam.deckheight = 20
|
||||
|
||||
-- Total size of the carrier (approx as rectangle).
|
||||
self.carrierparam.totlength=332.8 -- Wiki says 332.8 meters overall length.
|
||||
self.carrierparam.totwidthport=45 -- Wiki says 76.8 meters overall beam.
|
||||
self.carrierparam.totwidthstarboard=35
|
||||
|
||||
-- Landing runway.
|
||||
self.carrierparam.rwyangle = -9
|
||||
self.carrierparam.rwylength = 250
|
||||
self.carrierparam.rwywidth = 25
|
||||
|
||||
-- Wires.
|
||||
self.carrierparam.wire1 = 55 -- Distance from stern to first wire.
|
||||
self.carrierparam.wire2 = 67
|
||||
self.carrierparam.wire3 = 79
|
||||
self.carrierparam.wire4 = 92
|
||||
|
||||
end
|
||||
|
||||
--- Init parameters for LHA-1 Tarawa carrier.
|
||||
-- @param #AIRBOSS self
|
||||
function AIRBOSS:_InitTarawa()
|
||||
|
||||
@@ -7866,7 +7905,7 @@ end
|
||||
function AIRBOSS:_RemoveUnitFromFlight(unit)
|
||||
|
||||
-- Check if unit exists.
|
||||
if unit then
|
||||
if unit and unit:IsInstanceOf("UNIT") then
|
||||
|
||||
-- Get group.
|
||||
local group=unit:GetGroup()
|
||||
@@ -10297,9 +10336,12 @@ function AIRBOSS:_GetSternCoord()
|
||||
if self.carriertype==AIRBOSS.CarrierType.TARAWA then
|
||||
-- Tarawa: Translate 8 meters port.
|
||||
stern=stern:Translate(self.carrierparam.sterndist, hdg):Translate(8, FB-90)
|
||||
else
|
||||
elseif self.carriertype==AIRBOSS.CarrierType.STENNIS then
|
||||
-- Stennis: translate 7 meters starboard wrt Final bearing.
|
||||
stern=stern:Translate(self.carrierparam.sterndist, hdg):Translate(7, FB+90)
|
||||
else
|
||||
-- Nimitz SC: translate 8 meters starboard wrt Final bearing.
|
||||
stern=stern:Translate(self.carrierparam.sterndist, hdg):Translate(8.5, FB+90)
|
||||
end
|
||||
|
||||
-- Set altitude.
|
||||
@@ -14016,10 +14058,10 @@ function AIRBOSS:_GetOnboardNumbers(group, playeronly)
|
||||
-- Onboard number and unit name.
|
||||
local n=tostring(unit.onboard_num)
|
||||
local name=unit.name
|
||||
local skill=unit.skill
|
||||
local skill=unit.skill or "Unknown"
|
||||
|
||||
-- Debug text.
|
||||
text=text..string.format("\n- unit %s: onboard #=%s skill=%s", name, n, skill)
|
||||
text=text..string.format("\n- unit %s: onboard #=%s skill=%s", name, n, tostring(skill))
|
||||
|
||||
if playeronly and skill=="Client" or skill=="Player" then
|
||||
-- There can be only one player in the group, so we skip everything else.
|
||||
@@ -17609,7 +17651,8 @@ function AIRBOSS:_SaveTrapSheet(playerData, grade)
|
||||
if self.trapprefix then
|
||||
filename=string.format("%s_%s-%04d.csv", self.trapprefix, playerData.actype, i)
|
||||
else
|
||||
filename=string.format("AIRBOSS-%s_Trapsheet-%s_%s-%04d.csv", self.alias, playerData.name, playerData.actype, i)
|
||||
local name=UTILS.ReplaceIllegalCharacters(playerData.name, "_")
|
||||
filename=string.format("AIRBOSS-%s_Trapsheet-%s_%s-%04d.csv", self.alias, name, playerData.actype, i)
|
||||
end
|
||||
|
||||
-- Set path.
|
||||
|
||||
@@ -1345,7 +1345,6 @@ function RECOVERYTANKER:OnEventEngineShutdown(EventData)
|
||||
group:InitModex(self.modex)
|
||||
|
||||
-- Respawn tanker. Delaying respawn due to DCS bug https://github.com/FlightControl-Master/MOOSE/issues/1076
|
||||
--SCHEDULER:New(nil , group.RespawnAtCurrentAirbase, {group}, 1)
|
||||
self:ScheduleOnce(1, GROUP.RespawnAtCurrentAirbase, group)
|
||||
|
||||
-- Create tanker beacon and activate TACAN.
|
||||
@@ -1364,7 +1363,6 @@ function RECOVERYTANKER:OnEventEngineShutdown(EventData)
|
||||
end
|
||||
|
||||
-- Initial route.
|
||||
--SCHEDULER:New(nil, self._InitRoute, {self, -self.distStern+UTILS.NMToMeters(3)}, 2)
|
||||
self:ScheduleOnce(2, RECOVERYTANKER._InitRoute, self, -self.distStern+UTILS.NMToMeters(3))
|
||||
end
|
||||
|
||||
|
||||
@@ -876,8 +876,8 @@ function RESCUEHELO:onafterStart(From, Event, To)
|
||||
local Spawn=SPAWN:NewWithAlias(self.helogroupname, self.alias)
|
||||
|
||||
-- Set modex for spawn.
|
||||
Spawn:InitModex(self.modex)
|
||||
|
||||
Spawn:InitModex(self.modex)
|
||||
|
||||
-- Spawn in air or at airbase.
|
||||
if self.takeoff==SPAWN.Takeoff.Air then
|
||||
|
||||
|
||||
@@ -881,6 +881,9 @@ do -- Group Assignment
|
||||
local Mission = self:GetMission()
|
||||
local CommandCenter = Mission:GetCommandCenter()
|
||||
CommandCenter:SetMenu()
|
||||
|
||||
self:MenuFlashTaskStatus( TaskGroup, false ) -- stop message flashing, if any #1383 & #1312
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1252,7 +1255,7 @@ function TASK:MenuFlashTaskStatus( TaskGroup, Flash )
|
||||
self.FlashTaskStatus = Flash
|
||||
|
||||
if self.FlashTaskStatus then
|
||||
self.FlashTaskScheduler, self.FlashTaskScheduleID = SCHEDULER:New( self, self.MenuTaskStatus, { TaskGroup }, 0, 60 )
|
||||
self.FlashTaskScheduler, self.FlashTaskScheduleID = SCHEDULER:New( self, self.MenuTaskStatus, { TaskGroup }, 0, 60) --Issue #1383 never ending flash messages
|
||||
else
|
||||
if self.FlashTaskScheduler then
|
||||
self.FlashTaskScheduler:Stop( self.FlashTaskScheduleID )
|
||||
|
||||
@@ -344,7 +344,9 @@ function TASKINFO:Report( Report, Detail, ReportGroup, Task )
|
||||
Text = DataText
|
||||
else
|
||||
local DataText = Data.Data -- #string
|
||||
Text = DataText
|
||||
if type(DataText) == "string" then --Issue #1388 - don't just assume this is a string
|
||||
Text = DataText
|
||||
end
|
||||
end
|
||||
|
||||
if Line < math.floor( Data.Order / 10 ) then
|
||||
|
||||
@@ -199,7 +199,7 @@ do -- TASK_A2A_DISPATCHER
|
||||
|
||||
self.Detection = Detection
|
||||
self.Mission = Mission
|
||||
|
||||
self.FlashNewTask = false
|
||||
|
||||
-- TODO: Check detection through radar.
|
||||
self.Detection:FilterCategories( Unit.Category.AIRPLANE, Unit.Category.HELICOPTER )
|
||||
@@ -253,7 +253,12 @@ do -- TASK_A2A_DISPATCHER
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Set flashing player messages on or off
|
||||
-- @param #TASK_A2A_DISPATCHER self
|
||||
-- @param #boolean onoff Set messages on (true) or off (false)
|
||||
function TASK_A2A_DISPATCHER:SetSendMessages( onoff )
|
||||
self.FlashNewTask = onoff
|
||||
end
|
||||
|
||||
--- Creates an INTERCEPT task when there are targets for it.
|
||||
-- @param #TASK_A2A_DISPATCHER self
|
||||
@@ -610,7 +615,7 @@ do -- TASK_A2A_DISPATCHER
|
||||
local TaskText = TaskReport:Text(", ")
|
||||
|
||||
for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do
|
||||
if ( not Mission:IsGroupAssigned(TaskGroup) ) and TaskText ~= "" then
|
||||
if ( not Mission:IsGroupAssigned(TaskGroup) ) and TaskText ~= "" and (self.FlashNewTask) then
|
||||
Mission:GetCommandCenter():MessageToGroup( string.format( "%s has tasks %s. Subscribe to a task using the radio menu.", Mission:GetShortText(), TaskText ), TaskGroup )
|
||||
end
|
||||
end
|
||||
|
||||
@@ -451,6 +451,7 @@ do -- TASK_A2G_DISPATCHER
|
||||
|
||||
self.Detection = Detection
|
||||
self.Mission = Mission
|
||||
self.FlashNewTask = true --set to false to suppress flash messages
|
||||
|
||||
self.Detection:FilterCategories( { Unit.Category.GROUND_UNIT } )
|
||||
|
||||
@@ -471,6 +472,12 @@ do -- TASK_A2G_DISPATCHER
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set flashing player messages on or off
|
||||
-- @param #TASK_A2G_DISPATCHER self
|
||||
-- @param #boolean onoff Set messages on (true) or off (false)
|
||||
function TASK_A2G_DISPATCHER:SetSendMessages( onoff )
|
||||
self.FlashNewTask = onoff
|
||||
end
|
||||
|
||||
--- Creates a SEAD task when there are targets for it.
|
||||
-- @param #TASK_A2G_DISPATCHER self
|
||||
@@ -616,7 +623,9 @@ do -- TASK_A2G_DISPATCHER
|
||||
if not DetectedItem then
|
||||
local TaskText = Task:GetName()
|
||||
for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do
|
||||
Mission:GetCommandCenter():MessageToGroup( string.format( "Obsolete A2G task %s for %s removed.", TaskText, Mission:GetShortText() ), TaskGroup )
|
||||
if self.FlashNewTask then
|
||||
Mission:GetCommandCenter():MessageToGroup( string.format( "Obsolete A2G task %s for %s removed.", TaskText, Mission:GetShortText() ), TaskGroup )
|
||||
end
|
||||
end
|
||||
Task = self:RemoveTask( TaskIndex )
|
||||
end
|
||||
@@ -686,7 +695,7 @@ do -- TASK_A2G_DISPATCHER
|
||||
-- Now we send to each group the changes, if any.
|
||||
for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do
|
||||
local TargetsText = TargetsReport:Text(", ")
|
||||
if ( Mission:IsGroupAssigned(TaskGroup) ) and TargetsText ~= "" then
|
||||
if ( Mission:IsGroupAssigned(TaskGroup) ) and TargetsText ~= "" and self.FlashNewTask then
|
||||
Mission:GetCommandCenter():MessageToGroup( string.format( "Task %s has change of targets:\n %s", Task:GetName(), TargetsText ), TaskGroup )
|
||||
end
|
||||
end
|
||||
@@ -805,7 +814,7 @@ do -- TASK_A2G_DISPATCHER
|
||||
|
||||
local TaskText = TaskReport:Text(", ")
|
||||
for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do
|
||||
if ( not Mission:IsGroupAssigned(TaskGroup) ) and TaskText ~= "" then
|
||||
if ( not Mission:IsGroupAssigned(TaskGroup) ) and TaskText ~= "" and self.FlashNewTask then
|
||||
Mission:GetCommandCenter():MessageToGroup( string.format( "%s has tasks %s. Subscribe to a task using the radio menu.", Mission:GetShortText(), TaskText ), TaskGroup )
|
||||
end
|
||||
end
|
||||
@@ -815,4 +824,4 @@ do -- TASK_A2G_DISPATCHER
|
||||
return true
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
@@ -83,7 +83,7 @@ do -- TASK_CAPTURE_DISPATCHER
|
||||
-- ## 1.1. Create a command center.
|
||||
--
|
||||
-- First you need to create a command center using the @{Tasking.CommandCenter#COMMANDCENTER.New}() constructor.
|
||||
-- The command assumes that you´ve setup a group in the mission editor with the name HQ.
|
||||
-- The command assumes that you´ve setup a group in the mission editor with the name HQ.
|
||||
-- This group will act as the command center object.
|
||||
-- It is a good practice to mark this group as invisible and invulnerable.
|
||||
--
|
||||
@@ -184,6 +184,7 @@ do -- TASK_CAPTURE_DISPATCHER
|
||||
local self = BASE:Inherit( self, TASK_MANAGER:New( SetGroup ) ) -- #TASK_CAPTURE_DISPATCHER
|
||||
|
||||
self.Mission = Mission
|
||||
self.FlashNewTask = false
|
||||
|
||||
self:AddTransition( "Started", "Assign", "Started" )
|
||||
self:AddTransition( "Started", "ZoneCaptured", "Started" )
|
||||
@@ -383,7 +384,7 @@ do -- TASK_CAPTURE_DISPATCHER
|
||||
local TaskText = TaskReport:Text(", ")
|
||||
|
||||
for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do
|
||||
if ( not Mission:IsGroupAssigned(TaskGroup) ) and TaskText ~= "" then
|
||||
if ( not Mission:IsGroupAssigned(TaskGroup) ) and TaskText ~= "" and ( not self.FlashNewTask ) then
|
||||
Mission:GetCommandCenter():MessageToGroup( string.format( "%s has tasks %s. Subscribe to a task using the radio menu.", Mission:GetShortText(), TaskText ), TaskGroup )
|
||||
end
|
||||
end
|
||||
|
||||
@@ -56,6 +56,17 @@ ENUMS.ROT = {
|
||||
AllowAbortMission=4,
|
||||
}
|
||||
|
||||
--- Alarm state.
|
||||
-- @type ENUMS.AlarmState
|
||||
-- @field #number Auto AI will automatically switch alarm states based on the presence of threats. The AI kind of cheats in this regard.
|
||||
-- @field #number Green Group is not combat ready. Sensors are stowed if possible.
|
||||
-- @field #number Red Group is combat ready and actively searching for targets. Some groups like infantry will not move in this state.
|
||||
ENUMS.AlarmState = {
|
||||
Auto=0,
|
||||
Green=1,
|
||||
Red=2,
|
||||
}
|
||||
|
||||
--- Weapon types. See the [Weapon Flag](https://wiki.hoggitworld.com/view/DCS_enum_weapon_flag) enumerotor on hoggit wiki.
|
||||
-- @type ENUMS.WeaponFlag
|
||||
ENUMS.WeaponFlag={
|
||||
@@ -222,6 +233,15 @@ ENUMS.Formation.RotaryWing.EchelonLeft={}
|
||||
ENUMS.Formation.RotaryWing.EchelonLeft.D70 =590081
|
||||
ENUMS.Formation.RotaryWing.EchelonLeft.D300=590082
|
||||
ENUMS.Formation.RotaryWing.EchelonLeft.D600=590083
|
||||
ENUMS.Formation.Vehicle={}
|
||||
ENUMS.Formation.Vehicle.Vee="Vee"
|
||||
ENUMS.Formation.Vehicle.EchelonRight="EchelonR"
|
||||
ENUMS.Formation.Vehicle.OffRoad="Off Road"
|
||||
ENUMS.Formation.Vehicle.Rank="Rank"
|
||||
ENUMS.Formation.Vehicle.EchelonLeft="EchelonL"
|
||||
ENUMS.Formation.Vehicle.OnRoad="On Road"
|
||||
ENUMS.Formation.Vehicle.Cone="Cone"
|
||||
ENUMS.Formation.Vehicle.Diamond="Diamond"
|
||||
|
||||
--- 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.
|
||||
@@ -242,4 +262,52 @@ ENUMS.FormationOld.RotaryWing={}
|
||||
ENUMS.FormationOld.RotaryWing.Wedge=8
|
||||
ENUMS.FormationOld.RotaryWing.Echelon=9
|
||||
ENUMS.FormationOld.RotaryWing.Front=10
|
||||
ENUMS.FormationOld.RotaryWing.Column=11
|
||||
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[" "]=" "
|
||||
539
Moose Development/Moose/Utilities/Profiler.lua
Normal file
539
Moose Development/Moose/Utilities/Profiler.lua
Normal file
@@ -0,0 +1,539 @@
|
||||
--- **Utils** - Lua Profiler.
|
||||
--
|
||||
-- Find out how many times functions are called and how much real time it costs.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **TAW CougarNL**, *funkyfranky*
|
||||
--
|
||||
-- @module Utilities.PROFILER
|
||||
-- @image MOOSE.JPG
|
||||
|
||||
|
||||
--- PROFILER class.
|
||||
-- @type PROFILER
|
||||
-- @field #string ClassName Name of the class.
|
||||
-- @field #table Counters Function counters.
|
||||
-- @field #table dInfo Info.
|
||||
-- @field #table fTime Function time.
|
||||
-- @field #table fTimeTotal Total function time.
|
||||
-- @field #table eventhandler Event handler to get mission end event.
|
||||
-- @field #number TstartGame Game start time timer.getTime().
|
||||
-- @field #number TstartOS OS real start time os.clock.
|
||||
-- @field #boolean logUnknown Log unknown functions. Default is off.
|
||||
-- @field #number ThreshCPS Low calls per second threshold. Only write output if function has more calls per second than this value.
|
||||
-- @field #number ThreshTtot Total time threshold. Only write output if total function CPU time is more than this value.
|
||||
-- @field #string fileNamePrefix Output file name prefix, e.g. "MooseProfiler".
|
||||
-- @field #string fileNameSuffix Output file name prefix, e.g. "txt"
|
||||
|
||||
--- *The emperor counsels simplicity. First principles. Of each particular thing, ask: What is it in itself, in its own constitution? What is its causal nature? *
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- # The PROFILER Concept
|
||||
--
|
||||
-- Profile your lua code. This tells you, which functions are called very often and which consume most real time.
|
||||
-- With this information you can optimize the perfomance of your code.
|
||||
--
|
||||
-- # Prerequisites
|
||||
--
|
||||
-- The modules **os** and **lfs** need to be desanizied.
|
||||
--
|
||||
--
|
||||
-- # Start
|
||||
--
|
||||
-- The profiler can simply be started with the @{#PROFILER.Start}(*Delay, Duration*) function
|
||||
--
|
||||
-- PROFILER.Start()
|
||||
--
|
||||
-- The optional parameter *Delay* can be used to delay the start by a certain amount of seconds and the optional parameter *Duration* can be used to
|
||||
-- stop the profiler after a certain amount of seconds.
|
||||
--
|
||||
-- # Stop
|
||||
--
|
||||
-- The profiler automatically stops when the mission ends. But it can be stopped any time with the @{#PROFILER.Stop}(*Delay*) function
|
||||
--
|
||||
-- PROFILER.Stop()
|
||||
--
|
||||
-- The optional parameter *Delay* can be used to specify a delay after which the profiler is stopped.
|
||||
--
|
||||
-- When the profiler is stopped, the output is written to a file.
|
||||
--
|
||||
-- # Output
|
||||
--
|
||||
-- The profiler output is written to a file in your DCS home folder
|
||||
--
|
||||
-- X:\User\<Your User Name>\Saved Games\DCS OpenBeta\Logs
|
||||
--
|
||||
-- The default file name is "MooseProfiler.txt". If that file exists, the file name is "MooseProfiler-001.txt" etc.
|
||||
--
|
||||
-- ## Data
|
||||
--
|
||||
-- The data in the output file provides information on the functions that were called in the mission.
|
||||
--
|
||||
-- It will tell you how many times a function was called in total, how many times per second, how much time in total and the percentage of time.
|
||||
--
|
||||
-- If you only want output for functions that are called more than *X* times per second, you can set
|
||||
--
|
||||
-- PROFILER.ThreshCPS=1.5
|
||||
--
|
||||
-- With this setting, only functions which are called more than 1.5 times per second are displayed. The default setting is PROFILER.ThreshCPS=0.0 (no threshold).
|
||||
--
|
||||
-- Furthermore, you can limit the output for functions that consumed a certain amount of CPU time in total by
|
||||
--
|
||||
-- PROFILER.ThreshTtot=0.005
|
||||
--
|
||||
-- With this setting, which is also the default, only functions which in total used more than 5 milliseconds CPU time.
|
||||
--
|
||||
-- @field #PROFILER
|
||||
PROFILER = {
|
||||
ClassName = "PROFILER",
|
||||
Counters = {},
|
||||
dInfo = {},
|
||||
fTime = {},
|
||||
fTimeTotal = {},
|
||||
eventHandler = {},
|
||||
logUnknown = false,
|
||||
ThreshCPS = 0.0,
|
||||
ThreshTtot = 0.005,
|
||||
fileNamePrefix = "MooseProfiler",
|
||||
fileNameSuffix = "txt"
|
||||
}
|
||||
|
||||
--- Waypoint data.
|
||||
-- @type PROFILER.Data
|
||||
-- @field #string func The function name.
|
||||
-- @field #string src The source file.
|
||||
-- @field #number line The line number
|
||||
-- @field #number count Number of function calls.
|
||||
-- @field #number tm Total time in seconds.
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- Start/Stop Profiler
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
--- Start profiler.
|
||||
-- @param #number Delay Delay in seconds before profiler is stated. Default is immediately.
|
||||
-- @param #number Duration Duration in (game) seconds before the profiler is stopped. Default is when mission ends.
|
||||
function PROFILER.Start(Delay, Duration)
|
||||
|
||||
-- Check if os, io and lfs are available.
|
||||
local go=true
|
||||
if not os then
|
||||
env.error("ERROR: Profiler needs os to be desanitized!")
|
||||
go=false
|
||||
end
|
||||
if not io then
|
||||
env.error("ERROR: Profiler needs io to be desanitized!")
|
||||
go=false
|
||||
end
|
||||
if not lfs then
|
||||
env.error("ERROR: Profiler needs lfs to be desanitized!")
|
||||
go=false
|
||||
end
|
||||
if not go then
|
||||
return
|
||||
end
|
||||
|
||||
if Delay and Delay>0 then
|
||||
BASE:ScheduleOnce(Delay, PROFILER.Start, 0, Duration)
|
||||
else
|
||||
|
||||
-- Set start time.
|
||||
PROFILER.TstartGame=timer.getTime()
|
||||
PROFILER.TstartOS=os.clock()
|
||||
|
||||
-- Add event handler.
|
||||
world.addEventHandler(PROFILER.eventHandler)
|
||||
|
||||
-- Info in log.
|
||||
env.info('############################ Profiler Started ############################')
|
||||
if Duration then
|
||||
env.info(string.format("- Will be running for %d seconds", Duration))
|
||||
else
|
||||
env.info(string.format("- Will be stopped when mission ends"))
|
||||
end
|
||||
env.info(string.format("- Calls per second threshold %.3f/sec", PROFILER.ThreshCPS))
|
||||
env.info(string.format("- Total function time threshold %.3f sec", PROFILER.ThreshTtot))
|
||||
env.info(string.format("- Output file \"%s\" in your DCS log file folder", PROFILER.getfilename(PROFILER.fileNameSuffix)))
|
||||
env.info(string.format("- Output file \"%s\" in CSV format", PROFILER.getfilename("csv")))
|
||||
env.info('###############################################################################')
|
||||
|
||||
|
||||
-- Message on screen
|
||||
local duration=Duration or 600
|
||||
trigger.action.outText("### Profiler running ###", duration)
|
||||
|
||||
-- Set hook.
|
||||
debug.sethook(PROFILER.hook, "cr")
|
||||
|
||||
-- Auto stop profiler.
|
||||
if Duration then
|
||||
PROFILER.Stop(Duration)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- Stop profiler.
|
||||
-- @param #number Delay Delay before stop in seconds.
|
||||
function PROFILER.Stop(Delay)
|
||||
|
||||
if Delay and Delay>0 then
|
||||
|
||||
BASE:ScheduleOnce(Delay, PROFILER.Stop)
|
||||
|
||||
else
|
||||
|
||||
-- Remove hook.
|
||||
debug.sethook()
|
||||
|
||||
|
||||
-- Run time game.
|
||||
local runTimeGame=timer.getTime()-PROFILER.TstartGame
|
||||
|
||||
-- Run time real OS.
|
||||
local runTimeOS=os.clock()-PROFILER.TstartOS
|
||||
|
||||
-- Show info.
|
||||
PROFILER.showInfo(runTimeGame, runTimeOS)
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- Event handler.
|
||||
function PROFILER.eventHandler:onEvent(event)
|
||||
if event.id==world.event.S_EVENT_MISSION_END then
|
||||
PROFILER.Stop()
|
||||
end
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- Hook
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
--- Debug hook.
|
||||
-- @param #table event Event.
|
||||
function PROFILER.hook(event)
|
||||
|
||||
local f=debug.getinfo(2, "f").func
|
||||
|
||||
if event=='call' then
|
||||
|
||||
if PROFILER.Counters[f]==nil then
|
||||
|
||||
PROFILER.Counters[f]=1
|
||||
PROFILER.dInfo[f]=debug.getinfo(2,"Sn")
|
||||
|
||||
if PROFILER.fTimeTotal[f]==nil then
|
||||
PROFILER.fTimeTotal[f]=0
|
||||
end
|
||||
|
||||
else
|
||||
PROFILER.Counters[f]=PROFILER.Counters[f]+1
|
||||
end
|
||||
|
||||
if PROFILER.fTime[f]==nil then
|
||||
PROFILER.fTime[f]=os.clock()
|
||||
end
|
||||
|
||||
elseif (event=='return') then
|
||||
|
||||
if PROFILER.fTime[f]~=nil then
|
||||
PROFILER.fTimeTotal[f]=PROFILER.fTimeTotal[f]+(os.clock()-PROFILER.fTime[f])
|
||||
PROFILER.fTime[f]=nil
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- Data
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
--- Get data.
|
||||
-- @param #function func Function.
|
||||
-- @return #string Function name.
|
||||
-- @return #string Source file name.
|
||||
-- @return #string Line number.
|
||||
-- @return #number Function time in seconds.
|
||||
function PROFILER.getData(func)
|
||||
|
||||
local n=PROFILER.dInfo[func]
|
||||
|
||||
if n.what=="C" then
|
||||
return n.name, "?", "?", PROFILER.fTimeTotal[func]
|
||||
end
|
||||
|
||||
return n.name, n.short_src, n.linedefined, PROFILER.fTimeTotal[func]
|
||||
end
|
||||
|
||||
--- Write text to log file.
|
||||
-- @param #function f The file.
|
||||
-- @param #string txt The text.
|
||||
function PROFILER._flog(f, txt)
|
||||
f:write(txt.."\r\n")
|
||||
end
|
||||
|
||||
--- Show table.
|
||||
-- @param #table data Data table.
|
||||
-- @param #function f The file.
|
||||
-- @param #number runTimeGame Game run time in seconds.
|
||||
function PROFILER.showTable(data, f, runTimeGame)
|
||||
|
||||
-- Loop over data.
|
||||
for i=1, #data do
|
||||
local t=data[i] --#PROFILER.Data
|
||||
|
||||
-- Calls per second.
|
||||
local cps=t.count/runTimeGame
|
||||
|
||||
local threshCPS=cps>=PROFILER.ThreshCPS
|
||||
local threshTot=t.tm>=PROFILER.ThreshTtot
|
||||
|
||||
if threshCPS and threshTot then
|
||||
|
||||
-- Output
|
||||
local text=string.format("%30s: %8d calls %8.1f/sec - Time Total %8.3f sec (%.3f %%) %5.3f sec/call %s line %s", t.func, t.count, cps, t.tm, t.tm/runTimeGame*100, t.tm/t.count, tostring(t.src), tostring(t.line))
|
||||
PROFILER._flog(f, text)
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- Print csv file.
|
||||
-- @param #table data Data table.
|
||||
-- @param #number runTimeGame Game run time in seconds.
|
||||
function PROFILER.printCSV(data, runTimeGame)
|
||||
|
||||
-- Output file.
|
||||
local file=PROFILER.getfilename("csv")
|
||||
local g=io.open(file, 'w')
|
||||
|
||||
-- Header.
|
||||
local text="Function,Total Calls,Calls per Sec,Total Time,Total in %,Sec per Call,Source File;Line Number,"
|
||||
g:write(text.."\r\n")
|
||||
|
||||
-- Loop over data.
|
||||
for i=1, #data do
|
||||
local t=data[i] --#PROFILER.Data
|
||||
|
||||
-- Calls per second.
|
||||
local cps=t.count/runTimeGame
|
||||
|
||||
-- Output
|
||||
local txt=string.format("%s,%d,%.1f,%.3f,%.3f,%.3f,%s,%s,", t.func, t.count, cps, t.tm, t.tm/runTimeGame*100, t.tm/t.count, tostring(t.src), tostring(t.line))
|
||||
g:write(txt.."\r\n")
|
||||
|
||||
end
|
||||
|
||||
-- Close file.
|
||||
g:close()
|
||||
end
|
||||
|
||||
|
||||
--- Write info to output file.
|
||||
-- @param #string ext Extension.
|
||||
-- @return #string File name.
|
||||
function PROFILER.getfilename(ext)
|
||||
|
||||
local dir=lfs.writedir()..[[Logs\]]
|
||||
|
||||
ext=ext or PROFILER.fileNameSuffix
|
||||
|
||||
local file=dir..PROFILER.fileNamePrefix.."."..ext
|
||||
|
||||
if not UTILS.FileExists(file) then
|
||||
return file
|
||||
end
|
||||
|
||||
for i=1,999 do
|
||||
|
||||
local file=string.format("%s%s-%03d.%s", dir,PROFILER.fileNamePrefix, i, ext)
|
||||
|
||||
if not UTILS.FileExists(file) then
|
||||
return file
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- Write info to output file.
|
||||
-- @param #number runTimeGame Game time in seconds.
|
||||
-- @param #number runTimeOS OS time in seconds.
|
||||
function PROFILER.showInfo(runTimeGame, runTimeOS)
|
||||
|
||||
-- Output file.
|
||||
local file=PROFILER.getfilename(PROFILER.fileNameSuffix)
|
||||
local f=io.open(file, 'w')
|
||||
|
||||
-- Gather data.
|
||||
local Ttot=0
|
||||
local Calls=0
|
||||
|
||||
local t={}
|
||||
|
||||
local tcopy=nil --#PROFILER.Data
|
||||
local tserialize=nil --#PROFILER.Data
|
||||
local tforgen=nil --#PROFILER.Data
|
||||
local tpairs=nil --#PROFILER.Data
|
||||
|
||||
|
||||
for func, count in pairs(PROFILER.Counters) do
|
||||
|
||||
local s,src,line,tm=PROFILER.getData(func)
|
||||
|
||||
if PROFILER.logUnknown==true then
|
||||
if s==nil then s="<Unknown>" end
|
||||
end
|
||||
|
||||
if s~=nil then
|
||||
|
||||
-- Profile data.
|
||||
local T=
|
||||
{ func=s,
|
||||
src=src,
|
||||
line=line,
|
||||
count=count,
|
||||
tm=tm,
|
||||
} --#PROFILER.Data
|
||||
|
||||
-- Collect special cases. Somehow, e.g. "_copy" appears multiple times so we try to gather all data.
|
||||
if s=="_copy" then
|
||||
if tcopy==nil then
|
||||
tcopy=T
|
||||
else
|
||||
tcopy.count=tcopy.count+T.count
|
||||
tcopy.tm=tcopy.tm+T.tm
|
||||
end
|
||||
elseif s=="_Serialize" then
|
||||
if tserialize==nil then
|
||||
tserialize=T
|
||||
else
|
||||
tserialize.count=tserialize.count+T.count
|
||||
tserialize.tm=tserialize.tm+T.tm
|
||||
end
|
||||
elseif s=="(for generator)" then
|
||||
if tforgen==nil then
|
||||
tforgen=T
|
||||
else
|
||||
tforgen.count=tforgen.count+T.count
|
||||
tforgen.tm=tforgen.tm+T.tm
|
||||
end
|
||||
elseif s=="pairs" then
|
||||
if tpairs==nil then
|
||||
tpairs=T
|
||||
else
|
||||
tpairs.count=tpairs.count+T.count
|
||||
tpairs.tm=tpairs.tm+T.tm
|
||||
end
|
||||
else
|
||||
table.insert(t, T)
|
||||
end
|
||||
|
||||
-- Total function time.
|
||||
Ttot=Ttot+tm
|
||||
|
||||
-- Total number of calls.
|
||||
Calls=Calls+count
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
-- Add special cases.
|
||||
if tcopy then
|
||||
table.insert(t, tcopy)
|
||||
end
|
||||
if tserialize then
|
||||
table.insert(t, tserialize)
|
||||
end
|
||||
if tforgen then
|
||||
table.insert(t, tforgen)
|
||||
end
|
||||
if tpairs then
|
||||
table.insert(t, tpairs)
|
||||
end
|
||||
|
||||
env.info('############################ Profiler Stopped ############################')
|
||||
env.info(string.format("* Runtime Game : %s = %d sec", UTILS.SecondsToClock(runTimeGame, true), runTimeGame))
|
||||
env.info(string.format("* Runtime Real : %s = %d sec", UTILS.SecondsToClock(runTimeOS, true), runTimeOS))
|
||||
env.info(string.format("* Function time : %s = %.1f sec (%.1f percent of runtime game)", UTILS.SecondsToClock(Ttot, true), Ttot, Ttot/runTimeGame*100))
|
||||
env.info(string.format("* Total functions : %d", #t))
|
||||
env.info(string.format("* Total func calls : %d", Calls))
|
||||
env.info(string.format("* Writing to file : \"%s\"", file))
|
||||
env.info(string.format("* Writing to file : \"%s\"", PROFILER.getfilename("csv")))
|
||||
env.info("##############################################################################")
|
||||
|
||||
-- Sort by total time.
|
||||
table.sort(t, function(a,b) return a.tm>b.tm end)
|
||||
|
||||
-- Write data.
|
||||
PROFILER._flog(f,"")
|
||||
PROFILER._flog(f,"************************************************************************************************************************")
|
||||
PROFILER._flog(f,"************************************************************************************************************************")
|
||||
PROFILER._flog(f,"************************************************************************************************************************")
|
||||
PROFILER._flog(f,"")
|
||||
PROFILER._flog(f,"-------------------------")
|
||||
PROFILER._flog(f,"---- Profiler Report ----")
|
||||
PROFILER._flog(f,"-------------------------")
|
||||
PROFILER._flog(f,"")
|
||||
PROFILER._flog(f,string.format("* Runtime Game : %s = %.1f sec", UTILS.SecondsToClock(runTimeGame, true), runTimeGame))
|
||||
PROFILER._flog(f,string.format("* Runtime Real : %s = %.1f sec", UTILS.SecondsToClock(runTimeOS, true), runTimeOS))
|
||||
PROFILER._flog(f,string.format("* Function time : %s = %.1f sec (%.1f %% of runtime game)", UTILS.SecondsToClock(Ttot, true), Ttot, Ttot/runTimeGame*100))
|
||||
PROFILER._flog(f,"")
|
||||
PROFILER._flog(f,string.format("* Total functions = %d", #t))
|
||||
PROFILER._flog(f,string.format("* Total func calls = %d", Calls))
|
||||
PROFILER._flog(f,"")
|
||||
PROFILER._flog(f,string.format("* Calls per second threshold = %.3f/sec", PROFILER.ThreshCPS))
|
||||
PROFILER._flog(f,string.format("* Total func time threshold = %.3f sec", PROFILER.ThreshTtot))
|
||||
PROFILER._flog(f,"")
|
||||
PROFILER._flog(f,"************************************************************************************************************************")
|
||||
PROFILER._flog(f,"")
|
||||
PROFILER.showTable(t, f, runTimeGame)
|
||||
|
||||
-- Sort by number of calls.
|
||||
table.sort(t, function(a,b) return a.tm/a.count>b.tm/b.count end)
|
||||
|
||||
-- Detailed data.
|
||||
PROFILER._flog(f,"")
|
||||
PROFILER._flog(f,"************************************************************************************************************************")
|
||||
PROFILER._flog(f,"")
|
||||
PROFILER._flog(f,"--------------------------------------")
|
||||
PROFILER._flog(f,"---- Data Sorted by Time per Call ----")
|
||||
PROFILER._flog(f,"--------------------------------------")
|
||||
PROFILER._flog(f,"")
|
||||
PROFILER.showTable(t, f, runTimeGame)
|
||||
|
||||
-- Sort by number of calls.
|
||||
table.sort(t, function(a,b) return a.count>b.count end)
|
||||
|
||||
-- Detailed data.
|
||||
PROFILER._flog(f,"")
|
||||
PROFILER._flog(f,"************************************************************************************************************************")
|
||||
PROFILER._flog(f,"")
|
||||
PROFILER._flog(f,"------------------------------------")
|
||||
PROFILER._flog(f,"---- Data Sorted by Total Calls ----")
|
||||
PROFILER._flog(f,"------------------------------------")
|
||||
PROFILER._flog(f,"")
|
||||
PROFILER.showTable(t, f, runTimeGame)
|
||||
|
||||
-- Closing.
|
||||
PROFILER._flog(f,"")
|
||||
PROFILER._flog(f,"************************************************************************************************************************")
|
||||
PROFILER._flog(f,"************************************************************************************************************************")
|
||||
PROFILER._flog(f,"************************************************************************************************************************")
|
||||
-- Close file.
|
||||
f:close()
|
||||
|
||||
-- Print csv file.
|
||||
PROFILER.printCSV(t, runTimeGame)
|
||||
end
|
||||
|
||||
@@ -48,11 +48,15 @@ BIGSMOKEPRESET = {
|
||||
-- @field #string Normandy Normandy map.
|
||||
-- @field #string NTTR Nevada Test and Training Range map.
|
||||
-- @field #string PersianGulf Persian Gulf map.
|
||||
-- @field #string TheChannel The Channel map.
|
||||
-- @field #string Syria Syria map.
|
||||
DCSMAP = {
|
||||
Caucasus="Caucasus",
|
||||
NTTR="Nevada",
|
||||
Normandy="Normandy",
|
||||
PersianGulf="PersianGulf"
|
||||
PersianGulf="PersianGulf",
|
||||
TheChannel="TheChannel",
|
||||
Syria="Syria",
|
||||
}
|
||||
|
||||
|
||||
@@ -111,6 +115,19 @@ CALLSIGN={
|
||||
Mantis=18,
|
||||
Badger=19,
|
||||
},
|
||||
-- FARP
|
||||
FARP={
|
||||
London=1,
|
||||
Dallas=2,
|
||||
Paris=3,
|
||||
Moscow=4,
|
||||
Berlin=5,
|
||||
Rome=6,
|
||||
Madrid=7,
|
||||
Warsaw=8,
|
||||
Dublin=9,
|
||||
Perth=10,
|
||||
},
|
||||
} --#CALLSIGN
|
||||
|
||||
--- Utilities static class.
|
||||
@@ -187,21 +204,30 @@ end
|
||||
-- @param #table object The input table.
|
||||
-- @return #table Copy of the input table.
|
||||
UTILS.DeepCopy = function(object)
|
||||
|
||||
local lookup_table = {}
|
||||
|
||||
-- Copy function.
|
||||
local function _copy(object)
|
||||
if type(object) ~= "table" then
|
||||
return object
|
||||
elseif lookup_table[object] then
|
||||
return lookup_table[object]
|
||||
end
|
||||
|
||||
local new_table = {}
|
||||
|
||||
lookup_table[object] = new_table
|
||||
|
||||
for index, value in pairs(object) do
|
||||
new_table[_copy(index)] = _copy(value)
|
||||
end
|
||||
|
||||
return setmetatable(new_table, getmetatable(object))
|
||||
end
|
||||
|
||||
local objectreturn = _copy(object)
|
||||
|
||||
return objectreturn
|
||||
end
|
||||
|
||||
@@ -311,6 +337,10 @@ UTILS.MetersToNM = function(meters)
|
||||
return meters/1852
|
||||
end
|
||||
|
||||
UTILS.MetersToSM = function(meters)
|
||||
return meters/1609.34
|
||||
end
|
||||
|
||||
UTILS.MetersToFeet = function(meters)
|
||||
return meters/0.3048
|
||||
end
|
||||
@@ -351,7 +381,7 @@ UTILS.MpsToMiph = function( mps )
|
||||
end
|
||||
|
||||
--- Convert meters per second to knots.
|
||||
-- @param #number knots Speed in m/s.
|
||||
-- @param #number mps Speed in m/s.
|
||||
-- @return #number Speed in knots.
|
||||
UTILS.MpsToKnots = function( mps )
|
||||
return mps * 1.94384 --3600 / 1852
|
||||
@@ -718,7 +748,8 @@ function UTILS.SecondsToClock(seconds, short)
|
||||
local clock=hours..":"..mins..":"..secs.."+"..days
|
||||
if short then
|
||||
if hours=="00" then
|
||||
clock=mins..":"..secs
|
||||
--clock=mins..":"..secs
|
||||
clock=hours..":"..mins..":"..secs
|
||||
else
|
||||
clock=hours..":"..mins..":"..secs
|
||||
end
|
||||
@@ -727,6 +758,26 @@ function UTILS.SecondsToClock(seconds, short)
|
||||
end
|
||||
end
|
||||
|
||||
--- Seconds of today.
|
||||
-- @return #number Seconds passed since last midnight.
|
||||
function UTILS.SecondsOfToday()
|
||||
|
||||
-- Time in seconds.
|
||||
local time=timer.getAbsTime()
|
||||
|
||||
-- Short format without days since mission start.
|
||||
local clock=UTILS.SecondsToClock(time, true)
|
||||
|
||||
-- Time is now the seconds passed since last midnight.
|
||||
return UTILS.ClockToSeconds(clock)
|
||||
end
|
||||
|
||||
--- Cound seconds until next midnight.
|
||||
-- @return #number Seconds to midnight.
|
||||
function UTILS.SecondsToMidnight()
|
||||
return 24*60*60-UTILS.SecondsOfToday()
|
||||
end
|
||||
|
||||
--- Convert clock time from hours, minutes and seconds to seconds.
|
||||
-- @param #string clock String of clock time. E.g., "06:12:35" or "5:1:30+1". Format is (H)H:(M)M:((S)S)(+D) H=Hours, M=Minutes, S=Seconds, D=Days.
|
||||
-- @return #number Seconds. Corresponds to what you cet from timer.getAbsTime() function.
|
||||
@@ -783,6 +834,15 @@ function UTILS.DisplayMissionTime(duration)
|
||||
MESSAGE:New(text, duration):ToAll()
|
||||
end
|
||||
|
||||
--- Replace illegal characters [<>|/?*:\\] in a string.
|
||||
-- @param #string Text Input text.
|
||||
-- @param #string ReplaceBy Replace illegal characters by this character or string. Default underscore "_".
|
||||
-- @return #string The input text with illegal chars replaced.
|
||||
function UTILS.ReplaceIllegalCharacters(Text, ReplaceBy)
|
||||
ReplaceBy=ReplaceBy or "_"
|
||||
local text=Text:gsub("[<>|/?*:\\]", ReplaceBy)
|
||||
return text
|
||||
end
|
||||
|
||||
--- Generate a Gaussian pseudo-random number.
|
||||
-- @param #number x0 Expectation value of distribution.
|
||||
@@ -937,6 +997,22 @@ function UTILS.HdgDiff(h1, h2)
|
||||
end
|
||||
|
||||
|
||||
--- Translate 3D vector in the 2D (x,z) plane. y-component (usually altitude) unchanged.
|
||||
-- @param DCS#Vec3 a Vector in 3D with x, y, z components.
|
||||
-- @param #number distance The distance to translate.
|
||||
-- @param #number angle Rotation angle in degrees.
|
||||
-- @return DCS#Vec3 Vector rotated in the (x,z) plane.
|
||||
function UTILS.VecTranslate(a, distance, angle)
|
||||
|
||||
local SX = a.x
|
||||
local SY = a.z
|
||||
local Radians=math.rad(angle or 0)
|
||||
local TX=distance*math.cos(Radians)+SX
|
||||
local TY=distance*math.sin(Radians)+SY
|
||||
|
||||
return {x=TX, y=a.y, z=TY}
|
||||
end
|
||||
|
||||
--- Rotate 3D vector in the 2D (x,z) plane. y-component (usually altitude) unchanged.
|
||||
-- @param DCS#Vec3 a Vector in 3D with x, y, z components.
|
||||
-- @param #number angle Rotation angle in degrees.
|
||||
@@ -958,7 +1034,6 @@ function UTILS.Rotate2D(a, angle)
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- Converts a TACAN Channel/Mode couple into a frequency in Hz.
|
||||
-- @param #number TACANChannel The TACAN channel, i.e. the 10 in "10X".
|
||||
-- @param #string TACANMode The TACAN mode, i.e. the "X" in "10X".
|
||||
@@ -1002,15 +1077,64 @@ function UTILS.GetDCSMap()
|
||||
return env.mission.theatre
|
||||
end
|
||||
|
||||
--- Returns the mission date. This is the date the mission started.
|
||||
--- Returns the mission date. This is the date the mission **started**.
|
||||
-- @return #string Mission date in yyyy/mm/dd format.
|
||||
-- @return #number The year anno domini.
|
||||
-- @return #number The month.
|
||||
-- @return #number The day.
|
||||
function UTILS.GetDCSMissionDate()
|
||||
local year=tostring(env.mission.date.Year)
|
||||
local month=tostring(env.mission.date.Month)
|
||||
local day=tostring(env.mission.date.Day)
|
||||
return string.format("%s/%s/%s", year, month, day)
|
||||
return string.format("%s/%s/%s", year, month, day), tonumber(year), tonumber(month), tonumber(day)
|
||||
end
|
||||
|
||||
--- Returns the day of the mission.
|
||||
-- @param #number Time (Optional) Abs. time in seconds. Default now, i.e. the value return from timer.getAbsTime().
|
||||
-- @return #number Day of the mission. Mission starts on day 0.
|
||||
function UTILS.GetMissionDay(Time)
|
||||
|
||||
Time=Time or timer.getAbsTime()
|
||||
|
||||
local clock=UTILS.SecondsToClock(Time, false)
|
||||
|
||||
local x=tonumber(UTILS.Split(clock, "+")[2])
|
||||
|
||||
return x
|
||||
end
|
||||
|
||||
--- Returns the current day of the year of the mission.
|
||||
-- @param #number Time (Optional) Abs. time in seconds. Default now, i.e. the value return from timer.getAbsTime().
|
||||
-- @return #number Current day of year of the mission. For example, January 1st returns 0, January 2nd returns 1 etc.
|
||||
function UTILS.GetMissionDayOfYear(Time)
|
||||
|
||||
local Date, Year, Month, Day=UTILS.GetDCSMissionDate()
|
||||
|
||||
local d=UTILS.GetMissionDay(Time)
|
||||
|
||||
return UTILS.GetDayOfYear(Year, Month, Day)+d
|
||||
|
||||
end
|
||||
|
||||
--- Returns the current date.
|
||||
-- @return #string Mission date in yyyy/mm/dd format.
|
||||
-- @return #number The year anno domini.
|
||||
-- @return #number The month.
|
||||
-- @return #number The day.
|
||||
function UTILS.GetDate()
|
||||
|
||||
-- Mission start date
|
||||
local date, year, month, day=UTILS.GetDCSMissionDate()
|
||||
|
||||
local time=timer.getAbsTime()
|
||||
|
||||
local clock=UTILS.SecondsToClock(time, false)
|
||||
|
||||
local x=tonumber(UTILS.Split(clock, "+")[2])
|
||||
|
||||
local day=day+x
|
||||
|
||||
end
|
||||
|
||||
--- Returns the magnetic declination of the map.
|
||||
-- Returned values for the current maps are:
|
||||
@@ -1035,6 +1159,10 @@ function UTILS.GetMagneticDeclination(map)
|
||||
declination=-10
|
||||
elseif map==DCSMAP.PersianGulf then
|
||||
declination=2
|
||||
elseif map==DCSMAP.TheChannel then
|
||||
declination=-10
|
||||
elseif map==DCSMAP.Syria then
|
||||
declination=5
|
||||
else
|
||||
declination=0
|
||||
end
|
||||
@@ -1144,3 +1272,189 @@ function UTILS.GetCallsignName(Callsign)
|
||||
|
||||
return "Ghostrider"
|
||||
end
|
||||
|
||||
--- Get the time difference between GMT and local time.
|
||||
-- @return #number Local time difference in hours compared to GMT. E.g. Dubai is GMT+4 ==> +4 is returned.
|
||||
function UTILS.GMTToLocalTimeDifference()
|
||||
|
||||
local theatre=UTILS.GetDCSMap()
|
||||
|
||||
if theatre==DCSMAP.Caucasus then
|
||||
return 4 -- Caucasus UTC+4 hours
|
||||
elseif theatre==DCSMAP.PersianGulf then
|
||||
return 4 -- Abu Dhabi UTC+4 hours
|
||||
elseif theatre==DCSMAP.NTTR then
|
||||
return -8 -- Las Vegas UTC-8 hours
|
||||
elseif theatre==DCSMAP.Normandy then
|
||||
return 0 -- Calais UTC+1 hour
|
||||
elseif theatre==DCSMAP.TheChannel then
|
||||
return 2 -- This map currently needs +2
|
||||
elseif theatre==DCSMAP.Syria then
|
||||
return 3 -- Damascus is UTC+3 hours
|
||||
else
|
||||
BASE:E(string.format("ERROR: Unknown Map %s in UTILS.GMTToLocal function. Returning 0", tostring(theatre)))
|
||||
return 0
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
--- Get the day of the year. Counting starts on 1st of January.
|
||||
-- @param #number Year The year.
|
||||
-- @param #number Month The month.
|
||||
-- @param #number Day The day.
|
||||
-- @return #number The day of the year.
|
||||
function UTILS.GetDayOfYear(Year, Month, Day)
|
||||
|
||||
local floor = math.floor
|
||||
|
||||
local n1 = floor(275 * Month / 9)
|
||||
local n2 = floor((Month + 9) / 12)
|
||||
local n3 = (1 + floor((Year - 4 * floor(Year / 4) + 2) / 3))
|
||||
|
||||
return n1 - (n2 * n3) + Day - 30
|
||||
end
|
||||
|
||||
--- Get sunrise or sun set of a specific day of the year at a specific location.
|
||||
-- @param #number DayOfYear The day of the year.
|
||||
-- @param #number Latitude Latitude.
|
||||
-- @param #number Longitude Longitude.
|
||||
-- @param #boolean Rising If true, calc sun rise, or sun set otherwise.
|
||||
-- @param #number Tlocal Local time offset in hours. E.g. +4 for a location which has GMT+4.
|
||||
-- @return #number Sun rise/set in seconds of the day.
|
||||
function UTILS.GetSunRiseAndSet(DayOfYear, Latitude, Longitude, Rising, Tlocal)
|
||||
|
||||
-- Defaults
|
||||
local zenith=90.83
|
||||
local latitude=Latitude
|
||||
local longitude=Longitude
|
||||
local rising=Rising
|
||||
local n=DayOfYear
|
||||
Tlocal=Tlocal or 0
|
||||
|
||||
|
||||
-- Short cuts.
|
||||
local rad = math.rad
|
||||
local deg = math.deg
|
||||
local floor = math.floor
|
||||
local frac = function(n) return n - floor(n) end
|
||||
local cos = function(d) return math.cos(rad(d)) end
|
||||
local acos = function(d) return deg(math.acos(d)) end
|
||||
local sin = function(d) return math.sin(rad(d)) end
|
||||
local asin = function(d) return deg(math.asin(d)) end
|
||||
local tan = function(d) return math.tan(rad(d)) end
|
||||
local atan = function(d) return deg(math.atan(d)) end
|
||||
|
||||
local function fit_into_range(val, min, max)
|
||||
local range = max - min
|
||||
local count
|
||||
if val < min then
|
||||
count = floor((min - val) / range) + 1
|
||||
return val + count * range
|
||||
elseif val >= max then
|
||||
count = floor((val - max) / range) + 1
|
||||
return val - count * range
|
||||
else
|
||||
return val
|
||||
end
|
||||
end
|
||||
|
||||
-- Convert the longitude to hour value and calculate an approximate time
|
||||
local lng_hour = longitude / 15
|
||||
|
||||
local t
|
||||
if rising then -- Rising time is desired
|
||||
t = n + ((6 - lng_hour) / 24)
|
||||
else -- Setting time is desired
|
||||
t = n + ((18 - lng_hour) / 24)
|
||||
end
|
||||
|
||||
-- Calculate the Sun's mean anomaly
|
||||
local M = (0.9856 * t) - 3.289
|
||||
|
||||
-- Calculate the Sun's true longitude
|
||||
local L = fit_into_range(M + (1.916 * sin(M)) + (0.020 * sin(2 * M)) + 282.634, 0, 360)
|
||||
|
||||
-- Calculate the Sun's right ascension
|
||||
local RA = fit_into_range(atan(0.91764 * tan(L)), 0, 360)
|
||||
|
||||
-- Right ascension value needs to be in the same quadrant as L
|
||||
local Lquadrant = floor(L / 90) * 90
|
||||
local RAquadrant = floor(RA / 90) * 90
|
||||
RA = RA + Lquadrant - RAquadrant
|
||||
|
||||
-- Right ascension value needs to be converted into hours
|
||||
RA = RA / 15
|
||||
|
||||
-- Calculate the Sun's declination
|
||||
local sinDec = 0.39782 * sin(L)
|
||||
local cosDec = cos(asin(sinDec))
|
||||
|
||||
-- Calculate the Sun's local hour angle
|
||||
local cosH = (cos(zenith) - (sinDec * sin(latitude))) / (cosDec * cos(latitude))
|
||||
|
||||
if rising and cosH > 1 then
|
||||
return "N/R" -- The sun never rises on this location on the specified date
|
||||
elseif cosH < -1 then
|
||||
return "N/S" -- The sun never sets on this location on the specified date
|
||||
end
|
||||
|
||||
-- Finish calculating H and convert into hours
|
||||
local H
|
||||
if rising then
|
||||
H = 360 - acos(cosH)
|
||||
else
|
||||
H = acos(cosH)
|
||||
end
|
||||
H = H / 15
|
||||
|
||||
-- Calculate local mean time of rising/setting
|
||||
local T = H + RA - (0.06571 * t) - 6.622
|
||||
|
||||
-- Adjust back to UTC
|
||||
local UT = fit_into_range(T - lng_hour +Tlocal, 0, 24)
|
||||
|
||||
return floor(UT)*60*60+frac(UT)*60*60--+Tlocal*60*60
|
||||
end
|
||||
|
||||
--- Get sun rise of a specific day of the year at a specific location.
|
||||
-- @param #number Day Day of the year.
|
||||
-- @param #number Month Month of the year.
|
||||
-- @param #number Year Year.
|
||||
-- @param #number Latitude Latitude.
|
||||
-- @param #number Longitude Longitude.
|
||||
-- @param #boolean Rising If true, calc sun rise, or sun set otherwise.
|
||||
-- @param #number Tlocal Local time offset in hours. E.g. +4 for a location which has GMT+4. Default 0.
|
||||
-- @return #number Sun rise in seconds of the day.
|
||||
function UTILS.GetSunrise(Day, Month, Year, Latitude, Longitude, Tlocal)
|
||||
|
||||
local DayOfYear=UTILS.GetDayOfYear(Year, Month, Day)
|
||||
|
||||
return UTILS.GetSunRiseAndSet(DayOfYear, Latitude, Longitude, true, Tlocal)
|
||||
end
|
||||
|
||||
--- Get sun set of a specific day of the year at a specific location.
|
||||
-- @param #number Day Day of the year.
|
||||
-- @param #number Month Month of the year.
|
||||
-- @param #number Year Year.
|
||||
-- @param #number Latitude Latitude.
|
||||
-- @param #number Longitude Longitude.
|
||||
-- @param #boolean Rising If true, calc sun rise, or sun set otherwise.
|
||||
-- @param #number Tlocal Local time offset in hours. E.g. +4 for a location which has GMT+4. Default 0.
|
||||
-- @return #number Sun rise in seconds of the day.
|
||||
function UTILS.GetSunset(Day, Month, Year, Latitude, Longitude, Tlocal)
|
||||
|
||||
local DayOfYear=UTILS.GetDayOfYear(Year, Month, Day)
|
||||
|
||||
return UTILS.GetSunRiseAndSet(DayOfYear, Latitude, Longitude, false, Tlocal)
|
||||
end
|
||||
|
||||
--- Get OS time. Needs os to be desanitized!
|
||||
-- @return #number Os time in seconds.
|
||||
function UTILS.GetOSTime()
|
||||
if os then
|
||||
return os.clock()
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
File diff suppressed because it is too large
Load Diff
@@ -3,8 +3,7 @@
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
--
|
||||
-- ### Contributions:
|
||||
-- ### Contributions: **funkyfranky**
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
@@ -52,13 +51,6 @@
|
||||
--
|
||||
-- @field #CLIENT
|
||||
CLIENT = {
|
||||
ONBOARDSIDE = {
|
||||
NONE = 0,
|
||||
LEFT = 1,
|
||||
RIGHT = 2,
|
||||
BACK = 3,
|
||||
FRONT = 4
|
||||
},
|
||||
ClassName = "CLIENT",
|
||||
ClientName = nil,
|
||||
ClientAlive = false,
|
||||
@@ -66,27 +58,20 @@ CLIENT = {
|
||||
ClientBriefingShown = false,
|
||||
_Menus = {},
|
||||
_Tasks = {},
|
||||
Messages = {
|
||||
}
|
||||
Messages = {},
|
||||
Players = {},
|
||||
}
|
||||
|
||||
|
||||
--- Finds a CLIENT from the _DATABASE using the relevant DCS Unit.
|
||||
-- @param #CLIENT self
|
||||
-- @param #string ClientName Name of the DCS **Unit** as defined within the Mission Editor.
|
||||
-- @param #string ClientBriefing Text that describes the briefing of the mission when a Player logs into the Client.
|
||||
-- @return #CLIENT
|
||||
-- @usage
|
||||
-- -- Create new Clients.
|
||||
-- local Mission = MISSIONSCHEDULER.AddMission( 'Russia Transport Troops SA-6', 'Operational', 'Transport troops from the control center to one of the SA-6 SAM sites to activate their operation.', 'Russia' )
|
||||
-- Mission:AddGoal( DeploySA6TroopsGoal )
|
||||
--
|
||||
-- Mission:AddClient( CLIENT:FindByName( 'RU MI-8MTV2*HOT-Deploy Troops 1' ):Transport() )
|
||||
-- Mission:AddClient( CLIENT:FindByName( 'RU MI-8MTV2*RAMP-Deploy Troops 3' ):Transport() )
|
||||
-- Mission:AddClient( CLIENT:FindByName( 'RU MI-8MTV2*HOT-Deploy Troops 2' ):Transport() )
|
||||
-- Mission:AddClient( CLIENT:FindByName( 'RU MI-8MTV2*RAMP-Deploy Troops 4' ):Transport() )
|
||||
function CLIENT:Find( DCSUnit, Error )
|
||||
-- @param DCS#Unit DCSUnit The DCS unit of the client.
|
||||
-- @param #boolean Error Throw an error message.
|
||||
-- @return #CLIENT The CLIENT found in the _DATABASE.
|
||||
function CLIENT:Find(DCSUnit, Error)
|
||||
|
||||
local ClientName = DCSUnit:getName()
|
||||
|
||||
local ClientFound = _DATABASE:FindClient( ClientName )
|
||||
|
||||
if ClientFound then
|
||||
@@ -117,11 +102,15 @@ end
|
||||
-- Mission:AddClient( CLIENT:FindByName( 'RU MI-8MTV2*HOT-Deploy Troops 2' ):Transport() )
|
||||
-- Mission:AddClient( CLIENT:FindByName( 'RU MI-8MTV2*RAMP-Deploy Troops 4' ):Transport() )
|
||||
function CLIENT:FindByName( ClientName, ClientBriefing, Error )
|
||||
|
||||
-- Client
|
||||
local ClientFound = _DATABASE:FindClient( ClientName )
|
||||
|
||||
if ClientFound then
|
||||
ClientFound:F( { ClientName, ClientBriefing } )
|
||||
ClientFound:AddBriefing( ClientBriefing )
|
||||
|
||||
ClientFound:AddBriefing(ClientBriefing)
|
||||
|
||||
ClientFound.MessageSwitch = true
|
||||
|
||||
return ClientFound
|
||||
@@ -132,51 +121,105 @@ function CLIENT:FindByName( ClientName, ClientBriefing, Error )
|
||||
end
|
||||
end
|
||||
|
||||
function CLIENT:Register( ClientName )
|
||||
--- Transport defines that the Client is a Transport. Transports show cargo.
|
||||
-- @param #CLIENT self
|
||||
-- @param #string ClientName Name of the client unit.
|
||||
-- @return #CLIENT self
|
||||
function CLIENT:Register(ClientName)
|
||||
|
||||
local self = BASE:Inherit( self, UNIT:Register( ClientName ) ) -- #CLIENT
|
||||
|
||||
self:F( ClientName )
|
||||
self.ClientName = ClientName
|
||||
self.MessageSwitch = true
|
||||
self.ClientAlive2 = false
|
||||
-- Inherit unit.
|
||||
local self = BASE:Inherit( self, UNIT:Register(ClientName )) -- #CLIENT
|
||||
|
||||
--self.AliveCheckScheduler = routines.scheduleFunction( self._AliveCheckScheduler, { self }, timer.getTime() + 1, 5 )
|
||||
self.AliveCheckScheduler = SCHEDULER:New( self, self._AliveCheckScheduler, { "Client Alive " .. ClientName }, 1, 5, 0.5 )
|
||||
self.AliveCheckScheduler:NoTrace()
|
||||
-- Set client name.
|
||||
self.ClientName = ClientName
|
||||
|
||||
-- Message switch.
|
||||
self.MessageSwitch = true
|
||||
|
||||
-- Alive2.
|
||||
self.ClientAlive2 = false
|
||||
|
||||
self:F( self )
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Transport defines that the Client is a Transport. Transports show cargo.
|
||||
-- @param #CLIENT self
|
||||
-- @return #CLIENT
|
||||
-- @return #CLIENT self
|
||||
function CLIENT:Transport()
|
||||
self:F()
|
||||
|
||||
self.ClientTransport = true
|
||||
return self
|
||||
end
|
||||
|
||||
--- AddBriefing adds a briefing to a CLIENT when a player joins a mission.
|
||||
--- Adds a briefing to a CLIENT when a player joins a mission.
|
||||
-- @param #CLIENT self
|
||||
-- @param #string ClientBriefing is the text defining the Mission briefing.
|
||||
-- @return #CLIENT self
|
||||
function CLIENT:AddBriefing( ClientBriefing )
|
||||
self:F( ClientBriefing )
|
||||
|
||||
self.ClientBriefing = ClientBriefing
|
||||
self.ClientBriefingShown = false
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Add player name.
|
||||
-- @param #CLIENT self
|
||||
-- @param #string PlayerName Name of the player.
|
||||
-- @return #CLIENT self
|
||||
function CLIENT:AddPlayer(PlayerName)
|
||||
|
||||
table.insert(self.Players, PlayerName)
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Get player name(s).
|
||||
-- @param #CLIENT self
|
||||
-- @return #table List of player names or an empty table `{}`.
|
||||
function CLIENT:GetPlayers()
|
||||
return self.Players
|
||||
end
|
||||
|
||||
--- Get name of player.
|
||||
-- @param #CLIENT self
|
||||
-- @return #string Player name or `nil`.
|
||||
function CLIENT:GetPlayer()
|
||||
if #self.Players>0 then
|
||||
return self.Players[1]
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Remove player.
|
||||
-- @param #CLIENT self
|
||||
-- @param #string PlayerName Name of the player.
|
||||
-- @return #CLIENT self
|
||||
function CLIENT:RemovePlayer(PlayerName)
|
||||
|
||||
for i,playername in pairs(self.Players) do
|
||||
if PlayerName==playername then
|
||||
table.remove(self.Players, i)
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Remove all players.
|
||||
-- @param #CLIENT self
|
||||
-- @return #CLIENT self
|
||||
function CLIENT:RemovePlayers()
|
||||
self.Players={}
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Show the briefing of a CLIENT.
|
||||
-- @param #CLIENT self
|
||||
-- @return #CLIENT self
|
||||
function CLIENT:ShowBriefing()
|
||||
self:F( { self.ClientName, self.ClientBriefingShown } )
|
||||
|
||||
if not self.ClientBriefingShown then
|
||||
self.ClientBriefingShown = true
|
||||
@@ -204,8 +247,6 @@ function CLIENT:ShowMissionBriefing( MissionBriefing )
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- Resets a CLIENT.
|
||||
-- @param #CLIENT self
|
||||
-- @param #string ClientName Name of the Group as defined within the Mission Editor. The Group must have a Unit with the type Client.
|
||||
@@ -241,6 +282,7 @@ end
|
||||
--- Checks for a client alive event and calls a function on a continuous basis.
|
||||
-- @param #CLIENT self
|
||||
-- @param #function CallBackFunction Create a function that will be called when a player joins the slot.
|
||||
-- @param ... (Optional) Arguments for callback function as comma separated list.
|
||||
-- @return #CLIENT
|
||||
function CLIENT:Alive( CallBackFunction, ... )
|
||||
self:F()
|
||||
@@ -248,6 +290,9 @@ function CLIENT:Alive( CallBackFunction, ... )
|
||||
self.ClientCallBack = CallBackFunction
|
||||
self.ClientParameters = arg
|
||||
|
||||
self.AliveCheckScheduler = SCHEDULER:New( self, self._AliveCheckScheduler, { "Client Alive " .. self.ClientName }, 0.1, 5, 0.5 )
|
||||
self.AliveCheckScheduler:NoTrace()
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -255,19 +300,29 @@ end
|
||||
function CLIENT:_AliveCheckScheduler( SchedulerName )
|
||||
self:F3( { SchedulerName, self.ClientName, self.ClientAlive2, self.ClientBriefingShown, self.ClientCallBack } )
|
||||
|
||||
if self:IsAlive() then
|
||||
if self:IsAlive() then
|
||||
|
||||
if self.ClientAlive2 == false then
|
||||
|
||||
-- Show briefing.
|
||||
self:ShowBriefing()
|
||||
|
||||
-- Callback function.
|
||||
if self.ClientCallBack then
|
||||
self:T("Calling Callback function")
|
||||
self.ClientCallBack( self, unpack( self.ClientParameters ) )
|
||||
end
|
||||
|
||||
-- Alive.
|
||||
self.ClientAlive2 = true
|
||||
end
|
||||
|
||||
else
|
||||
|
||||
if self.ClientAlive2 == true then
|
||||
self.ClientAlive2 = false
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return true
|
||||
@@ -291,6 +346,7 @@ function CLIENT:GetDCSGroup()
|
||||
local ClientUnit = Unit.getByName( self.ClientName )
|
||||
|
||||
local CoalitionsData = { AlivePlayersRed = coalition.getPlayers( coalition.side.RED ), AlivePlayersBlue = coalition.getPlayers( coalition.side.BLUE ) }
|
||||
|
||||
for CoalitionId, CoalitionData in pairs( CoalitionsData ) do
|
||||
self:T3( { "CoalitionData:", CoalitionData } )
|
||||
for UnitId, UnitData in pairs( CoalitionData ) do
|
||||
@@ -299,10 +355,14 @@ function CLIENT:GetDCSGroup()
|
||||
|
||||
--self:F(self.ClientName)
|
||||
if ClientUnit then
|
||||
|
||||
local ClientGroup = ClientUnit:getGroup()
|
||||
|
||||
if ClientGroup then
|
||||
self:T3( "ClientGroup = " .. self.ClientName )
|
||||
if ClientGroup:isExist() and UnitData:getGroup():isExist() then
|
||||
|
||||
if ClientGroup:isExist() and UnitData:getGroup():isExist() then
|
||||
|
||||
if ClientGroup:getID() == UnitData:getGroup():getID() then
|
||||
self:T3( "Normal logic" )
|
||||
self:T3( self.ClientName .. " : group found!" )
|
||||
@@ -310,15 +370,22 @@ function CLIENT:GetDCSGroup()
|
||||
self.ClientGroupName = ClientGroup:getName()
|
||||
return ClientGroup
|
||||
end
|
||||
|
||||
else
|
||||
|
||||
-- Now we need to resolve the bugs in DCS 1.5 ...
|
||||
-- Consult the database for the units of the Client Group. (ClientGroup:getUnits() returns nil)
|
||||
self:T3( "Bug 1.5 logic" )
|
||||
|
||||
local ClientGroupTemplate = _DATABASE.Templates.Units[self.ClientName].GroupTemplate
|
||||
|
||||
self.ClientGroupID = ClientGroupTemplate.groupId
|
||||
|
||||
self.ClientGroupName = _DATABASE.Templates.Units[self.ClientName].GroupName
|
||||
|
||||
self:T3( self.ClientName .. " : group found in bug 1.5 resolvement logic!" )
|
||||
return ClientGroup
|
||||
|
||||
end
|
||||
-- else
|
||||
-- error( "Client " .. self.ClientName .. " not found!" )
|
||||
@@ -343,22 +410,22 @@ function CLIENT:GetDCSGroup()
|
||||
end
|
||||
end
|
||||
|
||||
-- Nothing could be found :(
|
||||
self.ClientGroupID = nil
|
||||
self.ClientGroupUnit = nil
|
||||
self.ClientGroupName = nil
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
-- TODO: Check DCS#Group.ID
|
||||
--- Get the group ID of the client.
|
||||
-- @param #CLIENT self
|
||||
-- @return DCS#Group.ID
|
||||
-- @return #number DCS#Group ID.
|
||||
function CLIENT:GetClientGroupID()
|
||||
|
||||
local ClientGroup = self:GetDCSGroup()
|
||||
-- This updates the ID.
|
||||
self:GetDCSGroup()
|
||||
|
||||
--self:F( self.ClientGroupID ) -- Determined in GetDCSGroup()
|
||||
return self.ClientGroupID
|
||||
end
|
||||
|
||||
@@ -368,26 +435,28 @@ end
|
||||
-- @return #string
|
||||
function CLIENT:GetClientGroupName()
|
||||
|
||||
local ClientGroup = self:GetDCSGroup()
|
||||
|
||||
self:T( self.ClientGroupName ) -- Determined in GetDCSGroup()
|
||||
-- This updates the group name.
|
||||
self:GetDCSGroup()
|
||||
|
||||
return self.ClientGroupName
|
||||
end
|
||||
|
||||
--- Returns the UNIT of the CLIENT.
|
||||
-- @param #CLIENT self
|
||||
-- @return Wrapper.Unit#UNIT
|
||||
-- @return Wrapper.Unit#UNIT The client UNIT or `nil`.
|
||||
function CLIENT:GetClientGroupUnit()
|
||||
self:F2()
|
||||
|
||||
local ClientDCSUnit = Unit.getByName( self.ClientName )
|
||||
|
||||
self:T( self.ClientDCSUnit )
|
||||
|
||||
if ClientDCSUnit and ClientDCSUnit:isExist() then
|
||||
local ClientUnit = _DATABASE:FindUnit( self.ClientName )
|
||||
self:T2( ClientUnit )
|
||||
local ClientUnit=_DATABASE:FindUnit( self.ClientName )
|
||||
return ClientUnit
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Returns the DCSUnit of the CLIENT.
|
||||
|
||||
@@ -129,6 +129,7 @@
|
||||
-- * @{#CONTROLLABLE.Route}(): Make the Controllable to follow a given route.
|
||||
-- * @{#CONTROLLABLE.RouteGroundTo}(): Make the GROUND Controllable to drive towards a specific coordinate.
|
||||
-- * @{#CONTROLLABLE.RouteAirTo}(): Make the AIR Controllable to fly towards a specific coordinate.
|
||||
-- * @{#CONTROLLABLE.RelocateGroundRandomInRadius}(): Relocate the GROUND controllable to a random point in a given radius.
|
||||
--
|
||||
-- # 5) Option methods
|
||||
--
|
||||
@@ -173,6 +174,12 @@
|
||||
-- * @{#CONTROLLABLE.OptionAllowJettisonWeaponsOnThreat}
|
||||
-- * @{#CONTROLLABLE.OptionKeepWeaponsOnThreat}
|
||||
--
|
||||
-- ## 5.5) Air-2-Air missile attack range:
|
||||
-- * @{#CONTROLLABLE.OptionAAAttackRange}(): Defines the usage of A2A missiles against possible targets.
|
||||
--
|
||||
-- ## 5.6) GROUND units attack range:
|
||||
-- * @{#CONTROLLABLE.OptionEngageRange}(): Engage range limit in percent (a number between 0 and 100). Default 100. Defines the range at which a GROUND unit/group (e.g. a SAM site) is allowed to use its weapons automatically.
|
||||
--
|
||||
-- @field #CONTROLLABLE
|
||||
CONTROLLABLE = {
|
||||
ClassName = "CONTROLLABLE",
|
||||
@@ -243,8 +250,7 @@ end
|
||||
|
||||
--- Returns the initial health.
|
||||
-- @param #CONTROLLABLE self
|
||||
-- @return #number The controllable health value (unit or group average).
|
||||
-- @return #nil The controllable is not existing or alive.
|
||||
-- @return #number The controllable health value (unit or group average) or `nil` if the controllable does not exist.
|
||||
function CONTROLLABLE:GetLife0()
|
||||
self:F2( self.ControllableName )
|
||||
|
||||
@@ -296,7 +302,6 @@ end
|
||||
-- @return #nil The CONTROLLABLE is not existing or alive.
|
||||
function CONTROLLABLE:GetFuel()
|
||||
self:F( self.ControllableName )
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
@@ -803,7 +808,7 @@ function CONTROLLABLE:CommandSetFrequency(Frequency, Modulation, Delay)
|
||||
local CommandSetFrequency = {
|
||||
id = 'SetFrequency',
|
||||
params = {
|
||||
frequency = Frequency,
|
||||
frequency = Frequency*1000000,
|
||||
modulation = Modulation or radio.modulation.AM,
|
||||
}
|
||||
}
|
||||
@@ -1429,16 +1434,6 @@ end
|
||||
-- @return DCS#Task The DCS task structure.
|
||||
function CONTROLLABLE:TaskFireAtPoint( Vec2, Radius, AmmoCount, WeaponType )
|
||||
|
||||
-- FireAtPoint = {
|
||||
-- id = 'FireAtPoint',
|
||||
-- params = {
|
||||
-- point = Vec2,
|
||||
-- radius = Distance,
|
||||
-- expendQty = number,
|
||||
-- expendQtyEnabled = boolean,
|
||||
-- }
|
||||
-- }
|
||||
|
||||
local DCSTask = {
|
||||
id = 'FireAtPoint',
|
||||
params = {
|
||||
@@ -1458,7 +1453,6 @@ function CONTROLLABLE:TaskFireAtPoint( Vec2, Radius, AmmoCount, WeaponType )
|
||||
DCSTask.params.weaponType=WeaponType
|
||||
end
|
||||
|
||||
self:T3( { DCSTask } )
|
||||
return DCSTask
|
||||
end
|
||||
|
||||
@@ -1481,8 +1475,8 @@ end
|
||||
-- @param #number WeaponType Bitmask of weapon types, which are allowed to use.
|
||||
-- @param DCS#AI.Task.Designation Designation (Optional) Designation type.
|
||||
-- @param #boolean Datalink (Optional) Allows to use datalink to send the target information to attack aircraft. Enabled by default.
|
||||
-- @param #number Frequency Frequency used to communicate with the FAC.
|
||||
-- @param #number Modulation Modulation of radio for communication.
|
||||
-- @param #number Frequency Frequency in MHz used to communicate with the FAC. Default 133 MHz.
|
||||
-- @param #number Modulation Modulation of radio for communication. Default 0=AM.
|
||||
-- @param #number CallsignName Callsign enumerator name of the FAC.
|
||||
-- @param #number CallsignNumber Callsign number, e.g. Axeman-**1**.
|
||||
-- @return DCS#Task The DCS task structure.
|
||||
@@ -1492,11 +1486,11 @@ function CONTROLLABLE:TaskFAC_AttackGroup( AttackGroup, WeaponType, Designation,
|
||||
id = 'FAC_AttackGroup',
|
||||
params = {
|
||||
groupId = AttackGroup:GetID(),
|
||||
weaponType = WeaponType,
|
||||
designation = Designation,
|
||||
datalink = Datalink,
|
||||
frequency = Frequency,
|
||||
modulation = Modulation,
|
||||
weaponType = WeaponType or ENUMS.WeaponFlag.AutoDCS,
|
||||
designation = Designation or "Auto",
|
||||
datalink = Datalink and Datalink or true,
|
||||
frequency = (Frequency or 133)*1000000,
|
||||
modulation = Modulation or radio.modulation.AM,
|
||||
callname = CallsignName,
|
||||
number = CallsignNumber,
|
||||
}
|
||||
@@ -1903,7 +1897,7 @@ do -- Patrol methods
|
||||
local ToCoord = COORDINATE:NewFromVec2( { x = Waypoint.x, y = Waypoint.y } )
|
||||
-- Create a "ground route point", which is a "point" structure that can be given as a parameter to a Task
|
||||
local Route = {}
|
||||
Route[#Route+1] = FromCoord:WaypointGround( 0 )
|
||||
Route[#Route+1] = FromCoord:WaypointGround( Speed, Formation )
|
||||
Route[#Route+1] = ToCoord:WaypointGround( Speed, Formation )
|
||||
|
||||
|
||||
@@ -1956,7 +1950,7 @@ do -- Patrol methods
|
||||
|
||||
-- Create a "ground route point", which is a "point" structure that can be given as a parameter to a Task
|
||||
local Route = {}
|
||||
Route[#Route+1] = FromCoord:WaypointGround( 20 )
|
||||
Route[#Route+1] = FromCoord:WaypointGround( Speed, Formation )
|
||||
Route[#Route+1] = ToCoord:WaypointGround( Speed, Formation )
|
||||
|
||||
|
||||
@@ -2841,7 +2835,7 @@ function CONTROLLABLE:GetDetectedUnitSet(DetectVisual, DetectOptical, DetectRada
|
||||
return unitset
|
||||
end
|
||||
|
||||
--- Return the detected target groups of the controllable as a @{SET_GROUP}.
|
||||
--- Return the detected target groups of the controllable as a @{Core.Set#SET_GROUP}.
|
||||
-- The optional parametes specify the detection methods that can be applied.
|
||||
-- If no detection method is given, the detection will use all the available methods by default.
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE self
|
||||
@@ -3672,3 +3666,81 @@ function CONTROLLABLE:OptionRestrictBurner(RestrictBurner)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- Sets Controllable Option for A2A attack range for AIR FIGHTER units.
|
||||
-- @param #CONTROLLABLE self
|
||||
-- @param #number range Defines the range
|
||||
-- @usage Range can be one of MAX_RANGE = 0, NEZ_RANGE = 1, HALF_WAY_RMAX_NEZ = 2, TARGET_THREAT_EST = 3, RANDOM_RANGE = 4. Defaults to 3. See: https://wiki.hoggitworld.com/view/DCS_option_missileAttack
|
||||
function CONTROLLABLE:OptionAAAttackRange(range)
|
||||
self:F2( { self.ControllableName } )
|
||||
-- defaults to 3
|
||||
local range = range or 3
|
||||
if range < 0 or range > 4 then
|
||||
range = 3
|
||||
end
|
||||
local DCSControllable = self:GetDCSObject()
|
||||
if DCSControllable then
|
||||
local Controller = self:_GetController()
|
||||
if Controller then
|
||||
if self:IsAir() then
|
||||
self:SetOption(AI.Option.Air.val.MISSILE_ATTACK, range)
|
||||
end
|
||||
end
|
||||
return self
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Defines the range at which a GROUND unit/group is allowed to use its weapons automatically.
|
||||
-- @param #CONTROLLABLE self
|
||||
-- @param #number EngageRange Engage range limit in percent (a number between 0 and 100). Default 100.
|
||||
-- @return #CONTROLLABLE self
|
||||
function CONTROLLABLE:OptionEngageRange(EngageRange)
|
||||
self:F2( { self.ControllableName } )
|
||||
-- Set default if not specified.
|
||||
EngageRange=EngageRange or 100
|
||||
if EngageRange < 0 or EngageRange > 100 then
|
||||
EngageRange = 100
|
||||
end
|
||||
local DCSControllable = self:GetDCSObject()
|
||||
if DCSControllable then
|
||||
local Controller = self:_GetController()
|
||||
if Controller then
|
||||
if self:IsGround() then
|
||||
self:SetOption(AI.Option.Ground.id.AC_ENGAGEMENT_RANGE_RESTRICTION, EngageRange)
|
||||
end
|
||||
end
|
||||
return self
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
--- (GROUND) Relocate controllable to a random point within a given radius; use e.g.for evasive actions; Note that not all ground controllables can actually drive, also the alarm state of the controllable might stop it from moving.
|
||||
-- @param #CONTROLLABLE self
|
||||
-- @param #number speed Speed of the controllable, default 20
|
||||
-- @param #number radius Radius of the relocation zone, default 500
|
||||
-- @param #boolean onroad If true, route on road (less problems with AI way finding), default true
|
||||
-- @param #boolean shortcut If true and onroad is set, take a shorter route - if available - off road, default false
|
||||
function CONTROLLABLE:RelocateGroundRandomInRadius(speed, radius, onroad, shortcut)
|
||||
self:F2( { self.ControllableName } )
|
||||
|
||||
local _coord = self:GetCoordinate()
|
||||
local _radius = radius or 500
|
||||
local _speed = speed or 20
|
||||
local _tocoord = _coord:GetRandomCoordinateInRadius(_radius,100)
|
||||
local _onroad = onroad or true
|
||||
local _grptsk = {}
|
||||
local _candoroad = false
|
||||
local _shortcut = shortcut or false
|
||||
|
||||
-- create a DCS Task an push it on the group
|
||||
-- TaskGroundOnRoad(ToCoordinate,Speed,OffRoadFormation,Shortcut,FromCoordinate,WaypointFunction,WaypointFunctionArguments)
|
||||
if onroad then
|
||||
_grptsk, _candoroad = self:TaskGroundOnRoad(_tocoord,_speed,"Off Road",_shortcut)
|
||||
self:Route(_grptsk,5)
|
||||
else
|
||||
self:TaskRouteToVec2(_tocoord:GetVec2(),_speed,"Off Road")
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -261,7 +261,9 @@ end
|
||||
-- @param #string GroupName The Group name
|
||||
-- @return #GROUP self
|
||||
function GROUP:Register( GroupName )
|
||||
|
||||
local self = BASE:Inherit( self, CONTROLLABLE:New( GroupName ) ) -- #GROUP
|
||||
|
||||
self.GroupName = GroupName
|
||||
|
||||
self:SetEventPriority( 4 )
|
||||
@@ -668,14 +670,15 @@ end
|
||||
-- @param #number UnitNumber The number of the UNIT wrapper class to be returned.
|
||||
-- @return Wrapper.Unit#UNIT The UNIT wrapper class.
|
||||
function GROUP:GetUnit( UnitNumber )
|
||||
self:F3( { self.GroupName, UnitNumber } )
|
||||
|
||||
local DCSGroup = self:GetDCSObject()
|
||||
|
||||
if DCSGroup then
|
||||
|
||||
local DCSUnit = DCSGroup:getUnit( UnitNumber )
|
||||
local UnitFound = UNIT:Find( DCSGroup:getUnit( UnitNumber ) )
|
||||
self:T2( UnitFound )
|
||||
|
||||
local UnitFound = UNIT:Find(DCSUnit)
|
||||
|
||||
return UnitFound
|
||||
end
|
||||
|
||||
@@ -688,13 +691,11 @@ end
|
||||
-- @param #number UnitNumber The number of the DCS Unit to be returned.
|
||||
-- @return DCS#Unit The DCS Unit.
|
||||
function GROUP:GetDCSUnit( UnitNumber )
|
||||
self:F3( { self.GroupName, UnitNumber } )
|
||||
|
||||
local DCSGroup = self:GetDCSObject()
|
||||
local DCSGroup=self:GetDCSObject()
|
||||
|
||||
if DCSGroup then
|
||||
local DCSUnitFound = DCSGroup:getUnit( UnitNumber )
|
||||
self:T3( DCSUnitFound )
|
||||
local DCSUnitFound=DCSGroup:getUnit( UnitNumber )
|
||||
return DCSUnitFound
|
||||
end
|
||||
|
||||
@@ -706,14 +707,14 @@ end
|
||||
-- @param #GROUP self
|
||||
-- @return #number The DCS Group size.
|
||||
function GROUP:GetSize()
|
||||
self:F3( { self.GroupName } )
|
||||
|
||||
local DCSGroup = self:GetDCSObject()
|
||||
|
||||
if DCSGroup then
|
||||
|
||||
local GroupSize = DCSGroup:getSize()
|
||||
|
||||
if GroupSize then
|
||||
self:T3( GroupSize )
|
||||
return GroupSize
|
||||
else
|
||||
return 0
|
||||
@@ -946,24 +947,31 @@ end
|
||||
-- @param #GROUP self
|
||||
-- @return DCS#Vec2 Current Vec2 point of the first DCS Unit of the DCS Group.
|
||||
function GROUP:GetVec2()
|
||||
self:F2( self.GroupName )
|
||||
|
||||
local UnitPoint = self:GetUnit(1)
|
||||
UnitPoint:GetVec2()
|
||||
local GroupPointVec2 = UnitPoint:GetVec2()
|
||||
self:T3( GroupPointVec2 )
|
||||
return GroupPointVec2
|
||||
local Unit=self:GetUnit(1)
|
||||
|
||||
if Unit then
|
||||
local vec2=Unit:GetVec2()
|
||||
return vec2
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- Returns the current Vec3 vector of the first DCS Unit in the GROUP.
|
||||
-- @param #GROUP self
|
||||
-- @return DCS#Vec3 Current Vec3 of the first DCS Unit of the GROUP.
|
||||
function GROUP:GetVec3()
|
||||
self:F2( self.GroupName )
|
||||
|
||||
local GroupVec3 = self:GetUnit(1):GetVec3()
|
||||
self:T3( GroupVec3 )
|
||||
return GroupVec3
|
||||
-- Get first unit.
|
||||
local unit=self:GetUnit(1)
|
||||
|
||||
if unit then
|
||||
local vec3=unit:GetVec3()
|
||||
return vec3
|
||||
end
|
||||
|
||||
self:E("ERROR: Cannot get Vec3 of group "..tostring(self.GroupName))
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Returns a POINT_VEC2 object indicating the point in 2D of the first UNIT of the GROUP within the mission.
|
||||
@@ -990,13 +998,11 @@ end
|
||||
-- @param Wrapper.Group#GROUP self
|
||||
-- @return Core.Point#COORDINATE The COORDINATE of the GROUP.
|
||||
function GROUP:GetCoordinate()
|
||||
self:F2( self.PositionableName )
|
||||
|
||||
local FirstUnit = self:GetUnit(1)
|
||||
|
||||
if FirstUnit then
|
||||
local FirstUnitCoordinate = FirstUnit:GetCoordinate()
|
||||
self:T3(FirstUnitCoordinate)
|
||||
return FirstUnitCoordinate
|
||||
end
|
||||
|
||||
@@ -1039,11 +1045,16 @@ function GROUP:GetHeading()
|
||||
local GroupSize = self:GetSize()
|
||||
local HeadingAccumulator = 0
|
||||
|
||||
local n=0
|
||||
if GroupSize then
|
||||
for i = 1, GroupSize do
|
||||
HeadingAccumulator = HeadingAccumulator + self:GetUnit(i):GetHeading()
|
||||
local unit=self:GetUnit(i)
|
||||
if unit and unit:IsAlive() then
|
||||
HeadingAccumulator = HeadingAccumulator + unit:GetHeading()
|
||||
n=n+1
|
||||
end
|
||||
end
|
||||
return math.floor(HeadingAccumulator / GroupSize)
|
||||
return math.floor(HeadingAccumulator / n)
|
||||
end
|
||||
|
||||
BASE:E( { "Cannot GetHeading", Group = self, Alive = self:IsAlive() } )
|
||||
@@ -1161,6 +1172,35 @@ end
|
||||
|
||||
do -- Is Zone methods
|
||||
|
||||
|
||||
--- Check if any unit of a group is inside a @{Zone}.
|
||||
-- @param #GROUP self
|
||||
-- @param Core.Zone#ZONE_BASE Zone The zone to test.
|
||||
-- @return #boolean Returns `true` if *at least one unit* is inside the zone or `false` if *no* unit is inside.
|
||||
function GROUP:IsInZone( Zone )
|
||||
|
||||
if self:IsAlive() then
|
||||
|
||||
for UnitID, UnitData in pairs(self:GetUnits()) do
|
||||
local Unit = UnitData -- Wrapper.Unit#UNIT
|
||||
|
||||
-- Get 2D vector. That's all we need for the zone check.
|
||||
local vec2=Unit:GetVec2()
|
||||
|
||||
if Zone:IsVec2InZone(vec2) then
|
||||
return true -- At least one unit is in the zone. That is enough.
|
||||
else
|
||||
-- This one is not but another could be.
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Returns true if all units of the group are within a @{Zone}.
|
||||
-- @param #GROUP self
|
||||
-- @param Core.Zone#ZONE_BASE Zone The zone to test.
|
||||
@@ -2095,6 +2135,7 @@ end
|
||||
|
||||
--- Calculate the maxium A2G threat level of the Group.
|
||||
-- @param #GROUP self
|
||||
-- @return #number Number between 0 and 10.
|
||||
function GROUP:CalculateThreatLevelA2G()
|
||||
|
||||
local MaxThreatLevelA2G = 0
|
||||
@@ -2110,6 +2151,25 @@ function GROUP:CalculateThreatLevelA2G()
|
||||
return MaxThreatLevelA2G
|
||||
end
|
||||
|
||||
--- Get threat level of the group.
|
||||
-- @param #GROUP self
|
||||
-- @return #number Max threat level (a number between 0 and 10).
|
||||
function GROUP:GetThreatLevel()
|
||||
|
||||
local threatlevelMax = 0
|
||||
for UnitName, UnitData in pairs(self:GetUnits()) do
|
||||
local ThreatUnit = UnitData -- Wrapper.Unit#UNIT
|
||||
|
||||
local threatlevel = ThreatUnit:GetThreatLevel()
|
||||
if threatlevel > threatlevelMax then
|
||||
threatlevelMax=threatlevel
|
||||
end
|
||||
end
|
||||
|
||||
return threatlevelMax
|
||||
end
|
||||
|
||||
|
||||
--- Returns true if the first unit of the GROUP is in the air.
|
||||
-- @param Wrapper.Group#GROUP self
|
||||
-- @return #boolean true if in the first unit of the group is in the air or #nil if the GROUP is not existing or not alive.
|
||||
@@ -2142,27 +2202,52 @@ function GROUP:IsAirborne(AllUnits)
|
||||
|
||||
if units then
|
||||
|
||||
for _,_unit in pairs(units) do
|
||||
local unit=_unit --Wrapper.Unit#UNIT
|
||||
|
||||
if unit then
|
||||
|
||||
-- Unit in air or not.
|
||||
local inair=unit:InAir()
|
||||
if AllUnits then
|
||||
|
||||
--- We want to know if ALL units are airborne.
|
||||
|
||||
for _,_unit in pairs(units) do
|
||||
local unit=_unit --Wrapper.Unit#UNIT
|
||||
|
||||
-- Unit is not in air and we wanted to know whether ALL units are ==> return false
|
||||
if inair==false and AllUnits==true then
|
||||
return false
|
||||
end
|
||||
if unit then
|
||||
|
||||
-- At least one unit is in are and we did not care which one.
|
||||
if inair==true and not AllUnits then
|
||||
return true
|
||||
-- Unit in air or not.
|
||||
local inair=unit:InAir()
|
||||
|
||||
-- At least one unit is not in air.
|
||||
if not inair then
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
-- At least one unit is in the air.
|
||||
return true
|
||||
|
||||
-- All units are in air.
|
||||
return true
|
||||
|
||||
else
|
||||
|
||||
--- We want to know if ANY unit is airborne.
|
||||
|
||||
for _,_unit in pairs(units) do
|
||||
local unit=_unit --Wrapper.Unit#UNIT
|
||||
|
||||
if unit then
|
||||
|
||||
-- Unit in air or not.
|
||||
local inair=unit:InAir()
|
||||
|
||||
if inair then
|
||||
-- At least one unit is in air.
|
||||
return true
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
-- No unit is in air.
|
||||
return false
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
776
Moose Development/Moose/Wrapper/Marker.lua
Normal file
776
Moose Development/Moose/Wrapper/Marker.lua
Normal file
@@ -0,0 +1,776 @@
|
||||
--- **Wrapper** - Markers On the F10 map.
|
||||
--
|
||||
-- **Main Features:**
|
||||
--
|
||||
-- * Convenient handling of markers via multiple user API functions.
|
||||
-- * Update text and position of marker easily via scripting.
|
||||
-- * Delay creation and removal of markers via (optional) parameters.
|
||||
-- * Retrieve data such as text and coordinate.
|
||||
-- * Marker specific FSM events when a marker is added, removed or changed.
|
||||
-- * Additional FSM events when marker text or position is changed.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **funkyfranky**
|
||||
-- @module Wrapper.Marker
|
||||
-- @image Wrapper_Marker.png
|
||||
|
||||
|
||||
--- Marker class.
|
||||
-- @type MARKER
|
||||
-- @field #string ClassName Name of the class.
|
||||
-- @field #boolean Debug Debug mode. Messages to all about status.
|
||||
-- @field #string lid Class id string for output to DCS log file.
|
||||
-- @field #number mid Marker ID.
|
||||
-- @field Core.Point#COORDINATE coordinate Coordinate of the mark.
|
||||
-- @field #string text Text displayed in the mark panel.
|
||||
-- @field #string message Message dispayed when the mark is added.
|
||||
-- @field #boolean readonly Marker is read-only.
|
||||
-- @field #number coalition Coalition to which the marker is displayed.
|
||||
-- @extends Core.Fsm#FSM
|
||||
|
||||
--- Just because...
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- # The MARKER Class Idea
|
||||
--
|
||||
-- The MARKER class simplifies creating, updating and removing of markers on the F10 map.
|
||||
--
|
||||
-- # Create a Marker
|
||||
--
|
||||
-- -- Create a MARKER object at Batumi with a trivial text.
|
||||
-- local Coordinate=AIRBASE:FindByName("Batumi"):GetCoordinate()
|
||||
-- mymarker=MARKER:New(Coordinate, "I am Batumi Airfield")
|
||||
--
|
||||
-- Now this does **not** show the marker yet. We still need to specifiy to whom it is shown. There are several options, i.e.
|
||||
-- show the marker to everyone, to a speficic coaliton only, or only to a specific group.
|
||||
--
|
||||
-- ## For Everyone
|
||||
--
|
||||
-- If the marker should be visible to everyone, you can use the :ToAll() function.
|
||||
--
|
||||
-- mymarker=MARKER:New(Coordinate, "I am Batumi Airfield"):ToAll()
|
||||
--
|
||||
-- ## For a Coaliton
|
||||
--
|
||||
-- If the maker should be visible to a specific coalition, you can use the :ToCoalition() function.
|
||||
--
|
||||
-- mymarker=MARKER:New(Coordinate, "I am Batumi Airfield"):ToCoaliton(coaliton.side.BLUE)
|
||||
--
|
||||
-- ### To Blue Coaliton
|
||||
--
|
||||
-- ### To Red Coalition
|
||||
--
|
||||
-- This would show the marker only to the Blue coaliton.
|
||||
--
|
||||
-- ## For a Group
|
||||
--
|
||||
--
|
||||
-- # Removing a Marker
|
||||
--
|
||||
--
|
||||
-- # Updating a Marker
|
||||
--
|
||||
-- The marker text and coordinate can be updated easily as shown below.
|
||||
--
|
||||
-- However, note that **updateing involves to remove and recreate the marker if either text or its coordinate is changed**.
|
||||
-- *This is a DCS scripting engine limitation.*
|
||||
--
|
||||
-- ## Update Text
|
||||
--
|
||||
-- If you created a marker "mymarker" as shown above, you can update the dispayed test by
|
||||
--
|
||||
-- mymarker:UpdateText("I am the new text at Batumi")
|
||||
--
|
||||
-- The update can also be delayed by, e.g. 90 seconds, using
|
||||
--
|
||||
-- mymarker:UpdateText("I am the new text at Batumi", 90)
|
||||
--
|
||||
-- ## Update Coordinate
|
||||
--
|
||||
-- If you created a marker "mymarker" as shown above, you can update its coordinate on the F10 map by
|
||||
--
|
||||
-- mymarker:UpdateCoordinate(NewCoordinate)
|
||||
--
|
||||
-- The update can also be delayed by, e.g. 60 seconds, using
|
||||
--
|
||||
-- mymarker:UpdateCoordinate(NewCoordinate, 60)
|
||||
--
|
||||
-- # Retrieve Data
|
||||
--
|
||||
-- The important data as the displayed text and the coordinate of the marker can be retrieved easily.
|
||||
--
|
||||
-- ## Text
|
||||
--
|
||||
-- local text=mymarker:GetText()
|
||||
-- env.info("Marker Text = " .. text)
|
||||
--
|
||||
-- ## Coordinate
|
||||
--
|
||||
-- local Coordinate=mymarker:GetCoordinate()
|
||||
-- env.info("Marker Coordinate LL DSM = " .. Coordinate:ToStringLLDMS())
|
||||
--
|
||||
--
|
||||
-- # FSM Events
|
||||
--
|
||||
-- Moose creates addditonal events, so called FSM event, when markers are added, changed, removed, and text or the coordianteis updated.
|
||||
--
|
||||
-- These events can be captured and used for processing via OnAfter functions as shown below.
|
||||
--
|
||||
-- ## Added
|
||||
--
|
||||
-- ## Changed
|
||||
--
|
||||
-- ## Removed
|
||||
--
|
||||
-- ## TextUpdate
|
||||
--
|
||||
-- ## CoordUpdate
|
||||
--
|
||||
--
|
||||
-- # Examples
|
||||
--
|
||||
--
|
||||
-- @field #MARKER
|
||||
MARKER = {
|
||||
ClassName = "MARKER",
|
||||
Debug = false,
|
||||
lid = nil,
|
||||
mid = nil,
|
||||
coordinate = nil,
|
||||
text = nil,
|
||||
message = nil,
|
||||
readonly = nil,
|
||||
coalition = nil,
|
||||
}
|
||||
|
||||
--- Marker ID. Running number.
|
||||
_MARKERID=0
|
||||
|
||||
--- Marker class version.
|
||||
-- @field #string version
|
||||
MARKER.version="0.1.0"
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- TODO list
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
-- TODO: User "Get" functions. E.g., :GetCoordinate()
|
||||
-- DONE: Add delay to user functions.
|
||||
-- DONE: Handle events.
|
||||
-- DONE: Create FSM events.
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- Constructor
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
--- Create a new MARKER class object.
|
||||
-- @param #MARKER self
|
||||
-- @param Core.Point#COORDINATE Coordinate Coordinate where to place the marker.
|
||||
-- @param #string Text Text displayed on the mark panel.
|
||||
-- @return #MARKER self
|
||||
function MARKER:New(Coordinate, Text)
|
||||
|
||||
-- Inherit everything from FSM class.
|
||||
local self=BASE:Inherit(self, FSM:New()) -- #MARKER
|
||||
|
||||
self.coordinate=Coordinate
|
||||
|
||||
self.text=Text
|
||||
|
||||
-- Defaults
|
||||
self.readonly=false
|
||||
self.message=""
|
||||
|
||||
-- New marker ID. This is not the one of the actual marker.
|
||||
_MARKERID=_MARKERID+1
|
||||
|
||||
self.myid=_MARKERID
|
||||
|
||||
-- Log ID.
|
||||
self.lid=string.format("Marker #%d | ", self.myid)
|
||||
|
||||
-- Start State.
|
||||
self:SetStartState("Invisible")
|
||||
|
||||
-- Add FSM transitions.
|
||||
-- From State --> Event --> To State
|
||||
self:AddTransition("Invisible", "Added", "Visible") -- Marker was added.
|
||||
self:AddTransition("Visible", "Removed", "Invisible") -- Marker was removed.
|
||||
self:AddTransition("*", "Changed", "*") -- Marker was changed.
|
||||
|
||||
self:AddTransition("*", "TextUpdate", "*") -- Text updated.
|
||||
self:AddTransition("*", "CoordUpdate", "*") -- Coordinates updated.
|
||||
|
||||
--- Triggers the FSM event "Added".
|
||||
-- @function [parent=#MARKER] Added
|
||||
-- @param #MARKER self
|
||||
-- @param Core.Event#EVENTDATA EventData Event data table.
|
||||
|
||||
--- Triggers the delayed FSM event "Added".
|
||||
-- @function [parent=#MARKER] __Added
|
||||
-- @param #MARKER self
|
||||
-- @param Core.Event#EVENTDATA EventData Event data table.
|
||||
|
||||
--- On after "Added" event user function.
|
||||
-- @function [parent=#MARKER] OnAfterAdded
|
||||
-- @param #MARKER self
|
||||
-- @param #string From From state.
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
-- @param Core.Event#EVENTDATA EventData Event data table.
|
||||
|
||||
|
||||
--- Triggers the FSM event "Removed".
|
||||
-- @function [parent=#MARKER] Removed
|
||||
-- @param #MARKER self
|
||||
-- @param Core.Event#EVENTDATA EventData Event data table.
|
||||
|
||||
--- Triggers the delayed FSM event "Removed".
|
||||
-- @function [parent=#MARKER] __Removed
|
||||
-- @param #MARKER self
|
||||
-- @param Core.Event#EVENTDATA EventData Event data table.
|
||||
|
||||
--- On after "Removed" event user function.
|
||||
-- @function [parent=#MARKER] OnAfterRemoved
|
||||
-- @param #MARKER self
|
||||
-- @param #string From From state.
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
-- @param Core.Event#EVENTDATA EventData Event data table.
|
||||
|
||||
|
||||
--- Triggers the FSM event "Changed".
|
||||
-- @function [parent=#MARKER] Changed
|
||||
-- @param #MARKER self
|
||||
-- @param Core.Event#EVENTDATA EventData Event data table.
|
||||
|
||||
--- Triggers the delayed FSM event "Changed".
|
||||
-- @function [parent=#MARKER] __Changed
|
||||
-- @param #MARKER self
|
||||
-- @param Core.Event#EVENTDATA EventData Event data table.
|
||||
|
||||
--- On after "Changed" event user function.
|
||||
-- @function [parent=#MARKER] OnAfterChanged
|
||||
-- @param #MARKER self
|
||||
-- @param #string From From state.
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
-- @param Core.Event#EVENTDATA EventData Event data table.
|
||||
|
||||
|
||||
--- Triggers the FSM event "TextUpdate".
|
||||
-- @function [parent=#MARKER] TextUpdate
|
||||
-- @param #MARKER self
|
||||
-- @param #string Text The new text.
|
||||
|
||||
--- Triggers the delayed FSM event "TextUpdate".
|
||||
-- @function [parent=#MARKER] __TextUpdate
|
||||
-- @param #MARKER self
|
||||
-- @param #string Text The new text.
|
||||
|
||||
--- On after "TextUpdate" event user function.
|
||||
-- @function [parent=#MARKER] OnAfterTextUpdate
|
||||
-- @param #MARKER self
|
||||
-- @param #string From From state.
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
-- @param #string Text The new text.
|
||||
|
||||
|
||||
--- Triggers the FSM event "CoordUpdate".
|
||||
-- @function [parent=#MARKER] CoordUpdate
|
||||
-- @param #MARKER self
|
||||
-- @param Core.Point#COORDINATE Coordinate The new Coordinate.
|
||||
|
||||
--- Triggers the delayed FSM event "CoordUpdate".
|
||||
-- @function [parent=#MARKER] __CoordUpdate
|
||||
-- @param #MARKER self
|
||||
-- @param Core.Point#COORDINATE Coordinate The updated Coordinate.
|
||||
|
||||
--- On after "CoordUpdate" event user function.
|
||||
-- @function [parent=#MARKER] OnAfterCoordUpdate
|
||||
-- @param #MARKER self
|
||||
-- @param #string From From state.
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
-- @param Core.Point#COORDINATE Coordinate The updated Coordinate.
|
||||
|
||||
|
||||
-- Handle events.
|
||||
self:HandleEvent(EVENTS.MarkAdded)
|
||||
self:HandleEvent(EVENTS.MarkRemoved)
|
||||
self:HandleEvent(EVENTS.MarkChange)
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- User API Functions
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
--- Marker is readonly. Text cannot be changed and marker cannot be removed.
|
||||
-- @param #MARKER self
|
||||
-- @return #MARKER self
|
||||
function MARKER:ReadOnly()
|
||||
|
||||
self.readonly=true
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set message that is displayed on screen if the marker is added.
|
||||
-- @param #MARKER self
|
||||
-- @param #string Text Message displayed when the marker is added.
|
||||
-- @return #MARKER self
|
||||
function MARKER:Message(Text)
|
||||
|
||||
self.message=Text or ""
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Place marker visible for everyone.
|
||||
-- @param #MARKER self
|
||||
-- @param #number Delay (Optional) Delay in seconds, before the marker is created.
|
||||
-- @return #MARKER self
|
||||
function MARKER:ToAll(Delay)
|
||||
|
||||
if Delay and Delay>0 then
|
||||
self:ScheduleOnce(Delay, MARKER.ToAll, self)
|
||||
else
|
||||
|
||||
self.toall=true
|
||||
self.tocoaliton=nil
|
||||
self.coalition=nil
|
||||
self.togroup=nil
|
||||
self.groupname=nil
|
||||
self.groupid=nil
|
||||
|
||||
-- First remove an existing mark.
|
||||
if self.shown then
|
||||
self:Remove()
|
||||
end
|
||||
|
||||
self.mid=UTILS.GetMarkID()
|
||||
|
||||
-- Call DCS function.
|
||||
trigger.action.markToAll(self.mid, self.text, self.coordinate:GetVec3(), self.readonly, self.message)
|
||||
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Place marker visible for a specific coalition only.
|
||||
-- @param #MARKER self
|
||||
-- @param #number Coalition Coalition 1=Red, 2=Blue, 0=Neutral. See `coaliton.side.RED`.
|
||||
-- @param #number Delay (Optional) Delay in seconds, before the marker is created.
|
||||
-- @return #MARKER self
|
||||
function MARKER:ToCoalition(Coalition, Delay)
|
||||
|
||||
if Delay and Delay>0 then
|
||||
self:ScheduleOnce(Delay, MARKER.ToCoalition, self, Coalition)
|
||||
else
|
||||
|
||||
self.coalition=Coalition
|
||||
|
||||
self.tocoaliton=true
|
||||
self.toall=false
|
||||
self.togroup=false
|
||||
self.groupname=nil
|
||||
self.groupid=nil
|
||||
|
||||
-- First remove an existing mark.
|
||||
if self.shown then
|
||||
self:Remove()
|
||||
end
|
||||
|
||||
self.mid=UTILS.GetMarkID()
|
||||
|
||||
-- Call DCS function.
|
||||
trigger.action.markToCoalition(self.mid, self.text, self.coordinate:GetVec3(), self.coalition, self.readonly, self.message)
|
||||
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Place marker visible for the blue coalition only.
|
||||
-- @param #MARKER self
|
||||
-- @param #number Delay (Optional) Delay in seconds, before the marker is created.
|
||||
-- @return #MARKER self
|
||||
function MARKER:ToBlue(Delay)
|
||||
self:ToCoalition(coalition.side.BLUE, Delay)
|
||||
return self
|
||||
end
|
||||
|
||||
--- Place marker visible for the blue coalition only.
|
||||
-- @param #MARKER self
|
||||
-- @param #number Delay (Optional) Delay in seconds, before the marker is created.
|
||||
-- @return #MARKER self
|
||||
function MARKER:ToRed(Delay)
|
||||
self:ToCoalition(coalition.side.RED, Delay)
|
||||
return self
|
||||
end
|
||||
|
||||
--- Place marker visible for the neutral coalition only.
|
||||
-- @param #MARKER self
|
||||
-- @param #number Delay (Optional) Delay in seconds, before the marker is created.
|
||||
-- @return #MARKER self
|
||||
function MARKER:ToNeutral(Delay)
|
||||
self:ToCoalition(coalition.side.NEUTRAL, Delay)
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Place marker visible for a specific group only.
|
||||
-- @param #MARKER self
|
||||
-- @param Wrapper.Group#GROUP Group The group to which the marker is displayed.
|
||||
-- @param #number Delay (Optional) Delay in seconds, before the marker is created.
|
||||
-- @return #MARKER self
|
||||
function MARKER:ToGroup(Group, Delay)
|
||||
|
||||
if Delay and Delay>0 then
|
||||
self:ScheduleOnce(Delay, MARKER.ToGroup, self, Group)
|
||||
else
|
||||
|
||||
-- Check if group exists.
|
||||
if Group and Group:IsAlive()~=nil then
|
||||
|
||||
self.groupid=Group:GetID()
|
||||
|
||||
if self.groupid then
|
||||
|
||||
self.groupname=Group:GetName()
|
||||
|
||||
self.togroup=true
|
||||
self.tocoaliton=nil
|
||||
self.coalition=nil
|
||||
self.toall=nil
|
||||
|
||||
-- First remove an existing mark.
|
||||
if self.shown then
|
||||
self:Remove()
|
||||
end
|
||||
|
||||
self.mid=UTILS.GetMarkID()
|
||||
|
||||
-- Call DCS function.
|
||||
trigger.action.markToGroup(self.mid, self.text, self.coordinate:GetVec3(), self.groupid, self.readonly, self.message)
|
||||
|
||||
end
|
||||
|
||||
else
|
||||
--TODO: Warning!
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Update the text displayed on the mark panel.
|
||||
-- @param #MARKER self
|
||||
-- @param #string Text Updated text.
|
||||
-- @param #number Delay (Optional) Delay in seconds, before the marker is created.
|
||||
-- @return #MARKER self
|
||||
function MARKER:UpdateText(Text, Delay)
|
||||
|
||||
if Delay and Delay>0 then
|
||||
self:ScheduleOnce(Delay, MARKER.UpdateText, self, Text)
|
||||
else
|
||||
|
||||
self.text=tostring(Text)
|
||||
|
||||
self:Refresh()
|
||||
|
||||
self:TextUpdate(tostring(Text))
|
||||
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Update the coordinate where the marker is displayed.
|
||||
-- @param #MARKER self
|
||||
-- @param Core.Point#COORDINATE Coordinate The new coordinate.
|
||||
-- @param #number Delay (Optional) Delay in seconds, before the marker is created.
|
||||
-- @return #MARKER self
|
||||
function MARKER:UpdateCoordinate(Coordinate, Delay)
|
||||
|
||||
if Delay and Delay>0 then
|
||||
self:ScheduleOnce(Delay, MARKER.UpdateCoordinate, self, Coordinate)
|
||||
else
|
||||
|
||||
self.coordinate=Coordinate
|
||||
|
||||
self:Refresh()
|
||||
|
||||
self:CoordUpdate(Coordinate)
|
||||
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Refresh the marker.
|
||||
-- @param #MARKER self
|
||||
-- @param #number Delay (Optional) Delay in seconds, before the marker is created.
|
||||
-- @return #MARKER self
|
||||
function MARKER:Refresh(Delay)
|
||||
|
||||
if Delay and Delay>0 then
|
||||
self:ScheduleOnce(Delay, MARKER.Refresh, self)
|
||||
else
|
||||
|
||||
if self.toall then
|
||||
|
||||
self:ToAll()
|
||||
|
||||
elseif self.tocoaliton then
|
||||
|
||||
self:ToCoalition(self.coalition)
|
||||
|
||||
elseif self.togroup then
|
||||
|
||||
local group=GROUP:FindByName(self.groupname)
|
||||
|
||||
self:ToGroup(group)
|
||||
|
||||
else
|
||||
self:E(self.lid.."ERROR: unknown To in :Refresh()!")
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Remove a marker.
|
||||
-- @param #MARKER self
|
||||
-- @param #number Delay (Optional) Delay in seconds, before the marker is removed.
|
||||
-- @return #MARKER self
|
||||
function MARKER:Remove(Delay)
|
||||
|
||||
if Delay and Delay>0 then
|
||||
self:ScheduleOnce(Delay, MARKER.Remove, self)
|
||||
else
|
||||
|
||||
if self.shown then
|
||||
|
||||
-- Call DCS function.
|
||||
trigger.action.removeMark(self.mid)
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Get position of the marker.
|
||||
-- @param #MARKER self
|
||||
-- @return Core.Point#COORDINATE The coordinate of the marker.
|
||||
function MARKER:GetCoordinate()
|
||||
return self.coordinate
|
||||
end
|
||||
|
||||
--- Get text that is displayed in the marker panel.
|
||||
-- @param #MARKER self
|
||||
-- @return #string Marker text.
|
||||
function MARKER:GetText()
|
||||
return self.text
|
||||
end
|
||||
|
||||
--- Set text that is displayed in the marker panel. Note this does not show the marker.
|
||||
-- @param #MARKER self
|
||||
-- @param #string Text Marker text. Default is an empty sting "".
|
||||
-- @return #MARKER self
|
||||
function MARKER:SetText(Text)
|
||||
self.text=Text and tostring(Text) or ""
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Check if marker is currently visible on the F10 map.
|
||||
-- @param #MARKER self
|
||||
-- @return #boolean True if the marker is currently visible.
|
||||
function MARKER:IsVisible()
|
||||
return self:Is("Visible")
|
||||
end
|
||||
|
||||
--- Check if marker is currently invisible on the F10 map.
|
||||
-- @param #MARKER self
|
||||
-- @return
|
||||
function MARKER:IsInvisible()
|
||||
return self:Is("Invisible")
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- Event Functions
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
--- Event function when a MARKER is added.
|
||||
-- @param #MARKER self
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
function MARKER:OnEventMarkAdded(EventData)
|
||||
|
||||
if EventData and EventData.MarkID then
|
||||
|
||||
local MarkID=EventData.MarkID
|
||||
|
||||
self:T3(self.lid..string.format("Captured event MarkAdded for Mark ID=%s", tostring(MarkID)))
|
||||
|
||||
if MarkID==self.mid then
|
||||
|
||||
self.shown=true
|
||||
|
||||
self:Added(EventData)
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- Event function when a MARKER is removed.
|
||||
-- @param #MARKER self
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
function MARKER:OnEventMarkRemoved(EventData)
|
||||
|
||||
if EventData and EventData.MarkID then
|
||||
|
||||
local MarkID=EventData.MarkID
|
||||
|
||||
self:T3(self.lid..string.format("Captured event MarkAdded for Mark ID=%s", tostring(MarkID)))
|
||||
|
||||
if MarkID==self.mid then
|
||||
|
||||
self.shown=false
|
||||
|
||||
self:Removed(EventData)
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- Event function when a MARKER changed.
|
||||
-- @param #MARKER self
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
function MARKER:OnEventMarkChange(EventData)
|
||||
|
||||
if EventData and EventData.MarkID then
|
||||
|
||||
local MarkID=EventData.MarkID
|
||||
|
||||
self:T3(self.lid..string.format("Captured event MarkChange for Mark ID=%s", tostring(MarkID)))
|
||||
|
||||
if MarkID==self.mid then
|
||||
|
||||
self:Changed(EventData)
|
||||
|
||||
self:TextChanged(tostring(EventData.MarkText))
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- FSM Event Functions
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
--- On after "Added" event.
|
||||
-- @param #MARKER self
|
||||
-- @param #string From From state.
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
-- @param Core.Event#EVENTDATA EventData Event data table.
|
||||
function MARKER:onafterAdded(From, Event, To, EventData)
|
||||
|
||||
-- Debug info.
|
||||
local text=string.format("Captured event MarkAdded for myself:\n")
|
||||
text=text..string.format("Marker ID = %s\n", tostring(EventData.MarkID))
|
||||
text=text..string.format("Coalition = %s\n", tostring(EventData.MarkCoalition))
|
||||
text=text..string.format("Group ID = %s\n", tostring(EventData.MarkGroupID))
|
||||
text=text..string.format("Initiator = %s\n", EventData.IniUnit and EventData.IniUnit:GetName() or "Nobody")
|
||||
text=text..string.format("Coordinate = %s\n", EventData.MarkCoordinate and EventData.MarkCoordinate:ToStringLLDMS() or "Nowhere")
|
||||
text=text..string.format("Text: \n%s", tostring(EventData.MarkText))
|
||||
self:T2(self.lid..text)
|
||||
|
||||
end
|
||||
|
||||
--- On after "Removed" event.
|
||||
-- @param #MARKER self
|
||||
-- @param #string From From state.
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
-- @param Core.Event#EVENTDATA EventData Event data table.
|
||||
function MARKER:onafterRemoved(From, Event, To, EventData)
|
||||
|
||||
-- Debug info.
|
||||
local text=string.format("Captured event MarkRemoved for myself:\n")
|
||||
text=text..string.format("Marker ID = %s\n", tostring(EventData.MarkID))
|
||||
text=text..string.format("Coalition = %s\n", tostring(EventData.MarkCoalition))
|
||||
text=text..string.format("Group ID = %s\n", tostring(EventData.MarkGroupID))
|
||||
text=text..string.format("Initiator = %s\n", EventData.IniUnit and EventData.IniUnit:GetName() or "Nobody")
|
||||
text=text..string.format("Coordinate = %s\n", EventData.MarkCoordinate and EventData.MarkCoordinate:ToStringLLDMS() or "Nowhere")
|
||||
text=text..string.format("Text: \n%s", tostring(EventData.MarkText))
|
||||
self:T2(self.lid..text)
|
||||
|
||||
end
|
||||
|
||||
--- On after "Changed" event.
|
||||
-- @param #MARKER self
|
||||
-- @param #string From From state.
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
-- @param Core.Event#EVENTDATA EventData Event data table.
|
||||
function MARKER:onafterChanged(From, Event, To, EventData)
|
||||
|
||||
-- Debug info.
|
||||
local text=string.format("Captured event MarkChange for myself:\n")
|
||||
text=text..string.format("Marker ID = %s\n", tostring(EventData.MarkID))
|
||||
text=text..string.format("Coalition = %s\n", tostring(EventData.MarkCoalition))
|
||||
text=text..string.format("Group ID = %s\n", tostring(EventData.MarkGroupID))
|
||||
text=text..string.format("Initiator = %s\n", EventData.IniUnit and EventData.IniUnit:GetName() or "Nobody")
|
||||
text=text..string.format("Coordinate = %s\n", EventData.MarkCoordinate and EventData.MarkCoordinate:ToStringLLDMS() or "Nowhere")
|
||||
text=text..string.format("Text: \n%s", tostring(EventData.MarkText))
|
||||
self:T2(self.lid..text)
|
||||
|
||||
end
|
||||
|
||||
--- On after "TextUpdate" event.
|
||||
-- @param #MARKER self
|
||||
-- @param #string From From state.
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
-- @param #string Text The updated text, displayed in the mark panel.
|
||||
function MARKER:onafterTextUpdate(From, Event, To, Text)
|
||||
|
||||
self:T(self.lid..string.format("New Marker Text:\n%s", Text))
|
||||
|
||||
end
|
||||
|
||||
--- On after "CoordUpdate" event.
|
||||
-- @param #MARKER self
|
||||
-- @param #string From From state.
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
-- @param Core.Point#COORDINATE Coordinate The updated coordinates.
|
||||
function MARKER:onafterCoordUpdate(From, Event, To, Coordinate)
|
||||
|
||||
self:T(self.lid..string.format("New Marker Coordinate in LL DMS: %s", Coordinate:ToStringLLDMS()))
|
||||
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
File diff suppressed because it is too large
Load Diff
@@ -194,7 +194,7 @@ end
|
||||
-- @param Core.Point#COORDINATE Coordinate The coordinate where to spawn the new Static.
|
||||
-- @param #number Heading The heading of the static respawn in degrees. Default is 0 deg.
|
||||
-- @param #number Delay Delay in seconds before the static is spawned.
|
||||
function STATIC:SpawnAt( Coordinate, Heading, Delay )
|
||||
function STATIC:SpawnAt(Coordinate, Heading, Delay)
|
||||
|
||||
Heading=Heading or 0
|
||||
|
||||
@@ -202,51 +202,58 @@ function STATIC:SpawnAt( Coordinate, Heading, Delay )
|
||||
SCHEDULER:New(nil, self.SpawnAt, {self, Coordinate, Heading}, Delay)
|
||||
else
|
||||
|
||||
local SpawnStatic = SPAWNSTATIC:NewFromStatic( self.StaticName )
|
||||
local SpawnStatic=SPAWNSTATIC:NewFromStatic(self.StaticName)
|
||||
|
||||
SpawnStatic:SpawnFromPointVec2( Coordinate, Heading, self.StaticName )
|
||||
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Respawn the @{Wrapper.Unit} at the same location with the same properties.
|
||||
-- This is useful to respawn a cargo after it has been destroyed.
|
||||
-- @param #STATIC self
|
||||
-- @param DCS#country.id countryid The country ID used for spawning the new static. Default is same as currently.
|
||||
-- @param #number Delay Delay in seconds before static is respawned.
|
||||
function STATIC:ReSpawn(countryid, Delay)
|
||||
|
||||
countryid=countryid or self:GetCountry()
|
||||
-- @param DCS#country.id CountryID (Optional) The country ID used for spawning the new static. Default is same as currently.
|
||||
-- @param #number Delay (Optional) Delay in seconds before static is respawned. Default now.
|
||||
function STATIC:ReSpawn(CountryID, Delay)
|
||||
|
||||
if Delay and Delay>0 then
|
||||
SCHEDULER:New(nil, self.ReSpawn, {self, countryid}, Delay)
|
||||
SCHEDULER:New(nil, self.ReSpawn, {self, CountryID}, Delay)
|
||||
else
|
||||
|
||||
local SpawnStatic = SPAWNSTATIC:NewFromStatic( self.StaticName, countryid )
|
||||
CountryID=CountryID or self:GetCountry()
|
||||
|
||||
local SpawnStatic=SPAWNSTATIC:NewFromStatic(self.StaticName, CountryID)
|
||||
|
||||
SpawnStatic:ReSpawn()
|
||||
SpawnStatic:Spawn(nil, self.StaticName)
|
||||
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Respawn the @{Wrapper.Unit} at a defined Coordinate with an optional heading.
|
||||
-- @param #STATIC self
|
||||
-- @param Core.Point#COORDINATE Coordinate The coordinate where to spawn the new Static.
|
||||
-- @param #number Heading The heading of the static respawn in degrees. Default is 0 deg.
|
||||
-- @param #number Delay Delay in seconds before static is respawned.
|
||||
function STATIC:ReSpawnAt( Coordinate, Heading, Delay )
|
||||
-- @param #number Heading (Optional) The heading of the static respawn in degrees. Default the current heading.
|
||||
-- @param #number Delay (Optional) Delay in seconds before static is respawned. Default now.
|
||||
function STATIC:ReSpawnAt(Coordinate, Heading, Delay)
|
||||
|
||||
Heading=Heading or 0
|
||||
--Heading=Heading or 0
|
||||
|
||||
if Delay and Delay>0 then
|
||||
SCHEDULER:New(nil, self.ReSpawnAt, {self, Coordinate, Heading}, Delay)
|
||||
else
|
||||
|
||||
local SpawnStatic = SPAWNSTATIC:NewFromStatic( self.StaticName )
|
||||
local SpawnStatic=SPAWNSTATIC:NewFromStatic(self.StaticName, self:GetCountry())
|
||||
|
||||
SpawnStatic:SpawnFromCoordinate(Coordinate, Heading, self.StaticName)
|
||||
|
||||
SpawnStatic:ReSpawnAt( Coordinate, Heading )
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
--
|
||||
-- ### Contributions:
|
||||
-- ### Contributions: **funkyfranky**
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
@@ -22,6 +22,8 @@
|
||||
|
||||
|
||||
--- @type UNIT
|
||||
-- @field #string ClassName Name of the class.
|
||||
-- @field #string UnitName Name of the unit.
|
||||
-- @extends Wrapper.Controllable#CONTROLLABLE
|
||||
|
||||
--- For each DCS Unit object alive within a running mission, a UNIT wrapper object (instance) will be created within the _@{DATABASE} object.
|
||||
@@ -87,6 +89,7 @@
|
||||
-- @field #UNIT UNIT
|
||||
UNIT = {
|
||||
ClassName="UNIT",
|
||||
UnitName=nil,
|
||||
}
|
||||
|
||||
|
||||
@@ -103,12 +106,18 @@ UNIT = {
|
||||
--- Create a new UNIT from DCSUnit.
|
||||
-- @param #UNIT self
|
||||
-- @param #string UnitName The name of the DCS unit.
|
||||
-- @return #UNIT
|
||||
-- @return #UNIT self
|
||||
function UNIT:Register( UnitName )
|
||||
|
||||
-- Inherit CONTROLLABLE.
|
||||
local self = BASE:Inherit( self, CONTROLLABLE:New( UnitName ) )
|
||||
|
||||
-- Set unit name.
|
||||
self.UnitName = UnitName
|
||||
|
||||
-- Set event prio.
|
||||
self:SetEventPriority( 3 )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -258,8 +267,7 @@ end
|
||||
|
||||
--- Returns if the unit is activated.
|
||||
-- @param #UNIT self
|
||||
-- @return #boolean true if Unit is activated.
|
||||
-- @return #nil The DCS Unit is not existing or alive.
|
||||
-- @return #boolean `true` if Unit is activated. `nil` The DCS Unit is not existing or alive.
|
||||
function UNIT:IsActive()
|
||||
self:F2( self.UnitName )
|
||||
|
||||
@@ -279,9 +287,7 @@ end
|
||||
-- If the Unit is alive and active, true is returned.
|
||||
-- If the Unit is alive but not active, false is returned.
|
||||
-- @param #UNIT self
|
||||
-- @return #boolean true if Unit is alive and active.
|
||||
-- @return #boolean false if Unit is alive but not active.
|
||||
-- @return #nil if the Unit is not existing or is not alive.
|
||||
-- @return #boolean `true` if Unit is alive and active. `false` if Unit is alive but not active. `nil` if the Unit is not existing or is not alive.
|
||||
function UNIT:IsAlive()
|
||||
self:F3( self.UnitName )
|
||||
|
||||
@@ -300,7 +306,6 @@ end
|
||||
--- Returns the Unit's callsign - the localized string.
|
||||
-- @param #UNIT self
|
||||
-- @return #string The Callsign of the Unit.
|
||||
-- @return #nil The DCS Unit is not existing or alive.
|
||||
function UNIT:GetCallsign()
|
||||
self:F2( self.UnitName )
|
||||
|
||||
@@ -377,6 +382,32 @@ function UNIT:GetPlayerName()
|
||||
|
||||
end
|
||||
|
||||
--- Checks is the unit is a *Player* or *Client* slot.
|
||||
-- @param #UNIT self
|
||||
-- @return #boolean If true, unit is a player or client aircraft
|
||||
function UNIT:IsClient()
|
||||
|
||||
if _DATABASE.CLIENTS[self.UnitName] then
|
||||
return true
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
--- Get the CLIENT of the unit
|
||||
-- @param #UNIT self
|
||||
-- @return Wrapper.Client#CLIENT
|
||||
function UNIT:GetClient()
|
||||
|
||||
local client=_DATABASE.CLIENTS[self.UnitName]
|
||||
|
||||
if client then
|
||||
return client
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Returns the unit's number in the group.
|
||||
-- The number is the same number the unit has in ME.
|
||||
-- It may not be changed during the mission.
|
||||
@@ -640,8 +671,7 @@ end
|
||||
|
||||
--- Returns the unit sensors.
|
||||
-- @param #UNIT self
|
||||
-- @return DCS#Unit.Sensors
|
||||
-- @return #nil The DCS Unit is not existing or alive.
|
||||
-- @return DCS#Unit.Sensors Table of sensors.
|
||||
function UNIT:GetSensors()
|
||||
self:F2( self.UnitName )
|
||||
|
||||
@@ -661,7 +691,6 @@ end
|
||||
--- Returns if the unit has sensors of a certain type.
|
||||
-- @param #UNIT self
|
||||
-- @return #boolean returns true if the unit has specified types of sensors. This function is more preferable than Unit.getSensors() if you don't want to get information about all the unit's sensors, and just want to check if the unit has specified types of sensors.
|
||||
-- @return #nil The DCS Unit is not existing or alive.
|
||||
function UNIT:HasSensors( ... )
|
||||
self:F2( arg )
|
||||
|
||||
@@ -678,7 +707,6 @@ end
|
||||
--- Returns if the unit is SEADable.
|
||||
-- @param #UNIT self
|
||||
-- @return #boolean returns true if the unit is SEADable.
|
||||
-- @return #nil The DCS Unit is not existing or alive.
|
||||
function UNIT:HasSEAD()
|
||||
self:F2()
|
||||
|
||||
@@ -705,7 +733,6 @@ end
|
||||
-- @param #UNIT self
|
||||
-- @return #boolean Indicates if at least one of the unit's radar(s) is on.
|
||||
-- @return DCS#Object The object of the radar's interest. Not nil only if at least one radar of the unit is tracking a target.
|
||||
-- @return #nil The DCS Unit is not existing or alive.
|
||||
function UNIT:GetRadar()
|
||||
self:F2( self.UnitName )
|
||||
|
||||
@@ -1188,23 +1215,26 @@ do -- Event Handling
|
||||
|
||||
--- Subscribe to a DCS Event.
|
||||
-- @param #UNIT self
|
||||
-- @param Core.Event#EVENTS Event
|
||||
-- @param #function EventFunction (optional) The function to be called when the event occurs for the unit.
|
||||
-- @return #UNIT
|
||||
function UNIT:HandleEvent( Event, EventFunction )
|
||||
-- @param Core.Event#EVENTS EventID Event ID.
|
||||
-- @param #function EventFunction (Optional) The function to be called when the event occurs for the unit.
|
||||
-- @return #UNIT self
|
||||
function UNIT:HandleEvent(EventID, EventFunction)
|
||||
|
||||
self:EventDispatcher():OnEventForUnit( self:GetName(), EventFunction, self, Event )
|
||||
self:EventDispatcher():OnEventForUnit(self:GetName(), EventFunction, self, EventID)
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- UnSubscribe to a DCS event.
|
||||
-- @param #UNIT self
|
||||
-- @param Core.Event#EVENTS Event
|
||||
-- @return #UNIT
|
||||
function UNIT:UnHandleEvent( Event )
|
||||
-- @param Core.Event#EVENTS EventID Event ID.
|
||||
-- @return #UNIT self
|
||||
function UNIT:UnHandleEvent(EventID)
|
||||
|
||||
self:EventDispatcher():RemoveForUnit( self:GetName(), self, Event )
|
||||
--self:EventDispatcher():RemoveForUnit( self:GetName(), self, EventID )
|
||||
|
||||
-- Fixes issue #1365 https://github.com/FlightControl-Master/MOOSE/issues/1365
|
||||
self:EventDispatcher():RemoveEvent(self, EventID)
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
Utilities/Enums.lua
|
||||
Utilities/Routines.lua
|
||||
Utilities/Utils.lua
|
||||
Utilities/Enums.lua
|
||||
Utilities/Profiler.lua
|
||||
|
||||
Core/Base.lua
|
||||
Core/UserFlag.lua
|
||||
@@ -11,6 +14,7 @@ Core/Event.lua
|
||||
Core/Settings.lua
|
||||
Core/Menu.lua
|
||||
Core/Zone.lua
|
||||
Core/Zone_Detection.lua
|
||||
Core/Database.lua
|
||||
Core/Set.lua
|
||||
Core/Point.lua
|
||||
@@ -18,8 +22,11 @@ Core/Velocity.lua
|
||||
Core/Message.lua
|
||||
Core/Fsm.lua
|
||||
Core/Radio.lua
|
||||
Core/RadioQueue.lua
|
||||
Core/RadioSpeech.lua
|
||||
Core/Spawn.lua
|
||||
Core/SpawnStatic.lua
|
||||
Core/Timer.lua
|
||||
Core/Goal.lua
|
||||
Core/Spot.lua
|
||||
|
||||
@@ -33,6 +40,7 @@ Wrapper/Client.lua
|
||||
Wrapper/Static.lua
|
||||
Wrapper/Airbase.lua
|
||||
Wrapper/Scenery.lua
|
||||
Wrapper/Marker.lua
|
||||
|
||||
Cargo/Cargo.lua
|
||||
Cargo/CargoUnit.lua
|
||||
@@ -48,6 +56,7 @@ Functional/Escort.lua
|
||||
Functional/MissileTrainer.lua
|
||||
Functional/ATC_Ground.lua
|
||||
Functional/Detection.lua
|
||||
Functional/DetectionZones.lua
|
||||
Functional/Designate.lua
|
||||
Functional/RAT.lua
|
||||
Functional/Range.lua
|
||||
@@ -67,31 +76,35 @@ Ops/ATIS.lua
|
||||
|
||||
AI/AI_Balancer.lua
|
||||
AI/AI_Air.lua
|
||||
AI/AI_A2A.lua
|
||||
AI/AI_Air_Patrol.lua
|
||||
AI/AI_Air_Engage.lua
|
||||
AI/AI_A2A_Patrol.lua
|
||||
AI/AI_A2A_Cap.lua
|
||||
AI/AI_A2A_Gci.lua
|
||||
AI/AI_A2A_Dispatcher.lua
|
||||
AI/AI_A2G.lua
|
||||
AI/AI_A2G_Engage.lua
|
||||
AI/AI_A2G_BAI.lua
|
||||
AI/AI_A2G_CAS.lua
|
||||
AI/AI_A2G_SEAD.lua
|
||||
AI/AI_A2G_Patrol.lua
|
||||
AI/AI_A2G_Dispatcher.lua
|
||||
AI/AI_Patrol.lua
|
||||
AI/AI_Cap.lua
|
||||
AI/AI_Cas.lua
|
||||
AI/AI_Bai.lua
|
||||
AI/AI_CAP.lua
|
||||
AI/AI_CAS.lua
|
||||
AI/AI_BAI.lua
|
||||
AI/AI_Formation.lua
|
||||
AI/AI_Escort.lua
|
||||
AI/AI_Escort_Request.lua
|
||||
AI/AI_Escort_Dispatcher.lua
|
||||
AI/AI_Escort_Dispatcher_Request.lua
|
||||
AI/AI_Cargo.lua
|
||||
AI/AI_Cargo_APC.lua
|
||||
AI/AI_Cargo_Helicopter.lua
|
||||
AI/AI_Cargo_Airplane.lua
|
||||
AI/AI_Cargo_Ship.lua
|
||||
AI/AI_Cargo_Dispatcher.lua
|
||||
AI/AI_Cargo_Dispatcher_APC.lua
|
||||
AI/AI_Cargo_Dispatcher_Helicopter.lua
|
||||
AI/AI_Cargo_Dispatcher_Airplane.lua
|
||||
AI/AI_Cargo_Dispatcher_Ship.lua
|
||||
|
||||
Actions/Act_Assign.lua
|
||||
Actions/Act_Route.lua
|
||||
@@ -108,10 +121,11 @@ Tasking/Task_A2G_Dispatcher.lua
|
||||
Tasking/Task_A2G.lua
|
||||
Tasking/Task_A2A_Dispatcher.lua
|
||||
Tasking/Task_A2A.lua
|
||||
Tasking/Task_Cargo.lua
|
||||
Tasking/Task_CARGO.lua
|
||||
Tasking/Task_Cargo_Transport.lua
|
||||
Tasking/Task_Cargo_CSAR.lua
|
||||
Tasking/Task_Cargo_Dispatcher.lua
|
||||
Tasking/TaskZoneCapture.lua
|
||||
Tasking/Task_Capture_Zone.lua
|
||||
Tasking/Task_Capture_Dispatcher.lua
|
||||
|
||||
Globals.lua
|
||||
|
||||
14
README.md
14
README.md
@@ -73,19 +73,7 @@ MOOSE has a living (chat and video) community of users, beta testers and contrib
|
||||
|
||||
|
||||
|
||||
|
||||
# [Please DONATE ...](https://donorbox.org/fund-github-subscriptionfor-moose)
|
||||
|
||||
If you appreciate this development, please support to extend the framework. The development of this framework takes a lot of time.
|
||||
A small gift would help me to buy a new small laptop that I can use to extend this framework while commuting to and from work ...
|
||||
Also, your donations will be saved and spent wisely to the advantage of the community!
|
||||
|
||||
If everyone helps with a small amount, it would be really great!
|
||||
|
||||
<a class="dbox-donation-button" href="https://donorbox.org/fund-github-subscriptionfor-moose" style="background:#2d81c5 url(https://raw.githubusercontent.com/FlightControl-Master/MOOSE_DOCS/master/Configuration/Donate.png) no-repeat 37px center; color: #fff;text-decoration: none;font-family: Verdana,sans-serif;display: inline-block;font-size: 16px;padding: 15px 38px 15px 75px; -webkit-border-radius: 2px; -moz-border-radius: 2px; border-radius: 2px; box-shadow: 0 1px 0 0 #1f5a89; text-shadow: 0 1px rgba(0, 0, 0, 0.3);" >Donate</a>
|
||||
|
||||
|
||||
Kind regards,
|
||||
FlightControl (FC)
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user