Compare commits

...

40 Commits

Author SHA1 Message Date
Applevangelist
5260b2b430 And don't forget Fahrenheit 2022-03-26 14:46:52 +01:00
Applevangelist
0213bc7aef Correcting Celcius to Celsius 2022-03-26 14:43:06 +01:00
Applevangelist
ca8b0899d0 docu changes 2022-03-23 07:56:52 +01:00
Applevangelist
a1f5c0ab9b CSAR/CTLD - added type to script 2022-03-22 10:38:20 +01:00
Applevangelist
b0e3f82d27 AIRBASE - added 10 new AB names in Syria 2022-03-18 09:48:50 +01:00
Applevangelist
327ab4766b changed descriptions 2022-03-18 07:59:58 +01:00
Applevangelist
3aee8a49c1 Added CONTROLLABLE SetSpeed() and SetAltitude() 2022-03-18 07:39:48 +01:00
Applevangelist
57de0b7351 docu fixes 2022-03-16 08:45:27 +01:00
Applevangelist
4df1e310a3 CSAR - remove timer check for "open the door" message to make behaviour more realistic 2022-03-14 09:12:24 +01:00
TommyC81
802a77238a Range re-formatting and documentation re-fixing (#1691)
* Update Range.lua

Code formatting.

* RANGE - Documentation fixes.
2022-03-12 09:47:14 +01:00
Applevangelist
85a7e18fae SCORING: Corrected calc error in summary scoring functions 2022-03-11 10:18:59 +01:00
Applevangelist
26b1fd3487 Update CTLD.lua (#1692)
minor nil check
2022-03-09 10:26:16 +01:00
Applevangelist
ae7a363012 CTLD - small extra nil check in _GetUnitCargoMass(Unit) 2022-03-03 12:34:43 +01:00
Applevangelist
473362af45 CTLD - small fix for finding crates when using engineers 2022-03-03 11:02:25 +01:00
Applevangelist
cde0d09f0a CSAR - remove double class 2022-02-21 19:36:22 +01:00
Applevangelist
94f093826b SEAD - adding workaround for AGM_154 which lost target data 2022-02-21 08:36:37 +01:00
Applevangelist
84f231ea08 CSAR - added wet feet check if also using csarUsePara (no landing event triggered) 2022-02-18 08:22:47 +01:00
Applevangelist
3d9bb14713 CSAR - added "wet feet" option for a 2nd template to be used over water 2022-02-17 17:41:32 +01:00
Applevangelist
6c6cdcf763 CTLD - fix list/build side effect from adding weight limits to helos 2022-02-16 10:06:04 +01:00
Applevangelist
00c8690e61 CTLD - corrected default weight limits when using CTLD:UnitCapabilities() - was setting loadable weight to zero 2022-02-15 18:07:26 +01:00
Applevangelist
a0d492cd2d added back GROUP:GetHighestThreat() 2022-02-15 14:41:31 +01:00
Applevangelist
ba5ccc1021 CTLD:SetTroopDropZoneRadius(Radius) 2022-02-13 12:08:23 +01:00
Applevangelist
a4163017d5 CSAR - CSAR:SpawnCSARAtZone(Zone ...) - Zone can now be a ZONE object as well as a string 2022-02-08 07:49:04 +01:00
Applevangelist
7f4a5c48ec CTLD - add subcategory option, added CTLD:AddCTLDZoneFromAirbase(AirbaseName, Type, Color, Active, HasBeacon) 2022-02-08 07:47:48 +01:00
Applevangelist
9f7588b245 DETECTION - added 3 missing functions 2022-02-04 08:54:02 +01:00
Applevangelist
63cbc0c55b RANGE - added option to save target sheet 2022-02-03 10:01:48 +01:00
Applevangelist
28eb7a678c CTLD - Added Hercules support for crates, troops & vehicles loaded with the help of the ground crew and dropped from the plane. Added weight checks for loaded crates. 2022-02-03 10:00:19 +01:00
Applevangelist
a95c49915a SET - correct error in intersection 2022-02-01 08:02:51 +01:00
Applevangelist
b7adc6add6 POINT - added function to name/stop fires and smoke 2022-01-30 09:47:11 +01:00
Applevangelist
2aeebf280b AI Dispatchers - add ability to add/remove resources to/from a squad 2022-01-24 09:54:16 +01:00
Applevangelist
8ac06979f0 CTLD added color options for smoke drops, droppable beacons w/ timer 2022-01-23 11:42:16 +01:00
Applevangelist
2d4f90d5eb Added new Callsigns as per 2.7.9 2022-01-23 11:37:07 +01:00
TommyC81
d7a44a639d Update Detection.lua (#1685)
Code formatting. Fix minor typos, errors, and references in documentation.
2022-01-23 11:21:59 +01:00
Applevangelist
7bfa05f47d DETECTION - corrected call for Vec2 in zone 2022-01-19 07:52:59 +01:00
Applevangelist
c7bbb09195 Added doors check for UH-60L 2022-01-16 17:07:44 +01:00
Applevangelist
41c9c15ae5 CTLD, CSAR - added support for UH-60L 2022-01-16 11:39:19 +01:00
Applevangelist
964831becf CTLD - make container shape configureable 2022-01-15 11:34:23 +01:00
Applevangelist
e847b92cce RAT - Docu corrections 2022-01-11 15:14:40 +01:00
TommyC81
c2ecd86bb4 Minor fixes (#1684)
* Update AI_A2A_Dispatcher.lua

Minor code formatting.

* Update Airbase.lua

Code formatting.
2022-01-10 15:10:30 +01:00
Applevangelist
70d922fad6 SHort name mina AP 2022-01-04 15:10:25 +01:00
18 changed files with 9566 additions and 7836 deletions

View File

@@ -1510,7 +1510,7 @@ do -- AI_A2A_DISPATCHER
local Message = "Clearing (" .. DefenderTask.Type .. ") " local Message = "Clearing (" .. DefenderTask.Type .. ") "
Message = Message .. Defender:GetName() Message = Message .. Defender:GetName()
if Target then if Target then
Message = Message .. ( Target and ( " from " .. Target.Index .. " [" .. Target.Set:Count() .. "]" ) ) or "" Message = Message .. ((Target and (" from " .. Target.Index .. " [" .. Target.Set:Count() .. "]")) or "")
end end
self:F( { Target = Message } ) self:F( { Target = Message } )
end end
@@ -1559,7 +1559,7 @@ do -- AI_A2A_DISPATCHER
local Message = "(" .. self.DefenderTasks[Defender].Type .. ") " local Message = "(" .. self.DefenderTasks[Defender].Type .. ") "
Message = Message .. Defender:GetName() Message = Message .. Defender:GetName()
Message = Message .. ( AttackerDetection and ( " target " .. AttackerDetection.Index .. " [" .. AttackerDetection.Set:Count() .. "]" ) ) or "" Message = Message .. ((AttackerDetection and (" target " .. AttackerDetection.Index .. " [" .. AttackerDetection.Set:Count() .. "]")) or "")
self:F( { AttackerDetection = Message } ) self:F( { AttackerDetection = Message } )
if AttackerDetection then if AttackerDetection then
self.DefenderTasks[Defender].Target = AttackerDetection self.DefenderTasks[Defender].Target = AttackerDetection
@@ -3877,6 +3877,30 @@ do
self:CAP( SquadronName ) self:CAP( SquadronName )
end end
--- Add resources to a Squadron
-- @param #AI_A2A_DISPATCHER self
-- @param #string Squadron The squadron name.
-- @param #number Amount Number of resources to add.
function AI_A2A_DISPATCHER:AddToSquadron(Squadron,Amount)
local Squadron = self:GetSquadron(Squadron)
if Squadron.ResourceCount then
Squadron.ResourceCount = Squadron.ResourceCount + Amount
end
self:T({Squadron = Squadron.Name,SquadronResourceCount = Squadron.ResourceCount})
end
--- Remove resources from a Squadron
-- @param #AI_A2A_DISPATCHER self
-- @param #string Squadron The squadron name.
-- @param #number Amount Number of resources to remove.
function AI_A2A_DISPATCHER:RemoveFromSquadron(Squadron,Amount)
local Squadron = self:GetSquadron(Squadron)
if Squadron.ResourceCount then
Squadron.ResourceCount = Squadron.ResourceCount - Amount
end
self:T({Squadron = Squadron.Name,SquadronResourceCount = Squadron.ResourceCount})
end
end end
do do

View File

@@ -4729,5 +4729,29 @@ do
self:Patrol( SquadronName, PatrolTaskType ) self:Patrol( SquadronName, PatrolTaskType )
end end
--- Add resources to a Squadron
-- @param #AI_A2G_DISPATCHER self
-- @param #string Squadron The squadron name.
-- @param #number Amount Number of resources to add.
function AI_A2G_DISPATCHER:AddToSquadron(Squadron,Amount)
local Squadron = self:GetSquadron(Squadron)
if Squadron.ResourceCount then
Squadron.ResourceCount = Squadron.ResourceCount + Amount
end
self:T({Squadron = Squadron.Name,SquadronResourceCount = Squadron.ResourceCount})
end
--- Remove resources from a Squadron
-- @param #AI_A2G_DISPATCHER self
-- @param #string Squadron The squadron name.
-- @param #number Amount Number of resources to remove.
function AI_A2G_DISPATCHER:RemoveFromSquadron(Squadron,Amount)
local Squadron = self:GetSquadron(Squadron)
if Squadron.ResourceCount then
Squadron.ResourceCount = Squadron.ResourceCount - Amount
end
self:T({Squadron = Squadron.Name,SquadronResourceCount = Squadron.ResourceCount})
end
end end

File diff suppressed because it is too large Load Diff

View File

@@ -317,7 +317,7 @@ do -- SET_BASE
for _, Object in pairs( union.Set ) do for _, Object in pairs( union.Set ) do
if self:IsIncludeObject( Object ) and SetB:IsIncludeObject( Object ) then if self:IsIncludeObject( Object ) and SetB:IsIncludeObject( Object ) then
intersection:AddObject( intersection ) intersection:AddObject( Object )
end end
end end

View File

@@ -53,6 +53,7 @@
-- @module Core.Zone -- @module Core.Zone
-- @image Core_Zones.JPG -- @image Core_Zones.JPG
--- @type ZONE_BASE --- @type ZONE_BASE
-- @field #string ZoneName Name of the zone. -- @field #string ZoneName Name of the zone.
-- @field #number ZoneProbability A value between 0 and 1. 0 = 0% and 1 = 100% probability. -- @field #number ZoneProbability A value between 0 and 1. 0 = 0% and 1 = 100% probability.
@@ -60,6 +61,7 @@
-- @field #table Color Table with four entries, e.g. {1, 0, 0, 0.15}. First three are RGB color code. Fourth is the transparency Alpha value. -- @field #table Color Table with four entries, e.g. {1, 0, 0, 0.15}. First three are RGB color code. Fourth is the transparency Alpha value.
-- @extends Core.Fsm#FSM -- @extends Core.Fsm#FSM
--- This class is an abstract BASE class for derived classes, and is not meant to be instantiated. --- This class is an abstract BASE class for derived classes, and is not meant to be instantiated.
-- --
-- ## Each zone has a name: -- ## Each zone has a name:
@@ -106,9 +108,10 @@ ZONE_BASE = {
ZoneName = "", ZoneName = "",
ZoneProbability = 1, ZoneProbability = 1,
DrawID=nil, DrawID=nil,
Color = {}, Color={}
} }
--- The ZONE_BASE.BoundingSquare --- The ZONE_BASE.BoundingSquare
-- @type ZONE_BASE.BoundingSquare -- @type ZONE_BASE.BoundingSquare
-- @field DCS#Distance x1 The lower x coordinate (left down) -- @field DCS#Distance x1 The lower x coordinate (left down)
@@ -116,6 +119,7 @@ ZONE_BASE = {
-- @field DCS#Distance x2 The higher x coordinate (right up) -- @field DCS#Distance x2 The higher x coordinate (right up)
-- @field DCS#Distance y2 The higher y coordinate (right up) -- @field DCS#Distance y2 The higher y coordinate (right up)
--- ZONE_BASE constructor --- ZONE_BASE constructor
-- @param #ZONE_BASE self -- @param #ZONE_BASE self
-- @param #string ZoneName Name of the zone. -- @param #string ZoneName Name of the zone.
@@ -126,9 +130,13 @@ function ZONE_BASE:New( ZoneName )
self.ZoneName = ZoneName self.ZoneName = ZoneName
--_DATABASE:AddZone(ZoneName,self)
return self return self
end end
--- Returns the name of the zone. --- Returns the name of the zone.
-- @param #ZONE_BASE self -- @param #ZONE_BASE self
-- @return #string The name of the zone. -- @return #string The name of the zone.
@@ -138,6 +146,7 @@ function ZONE_BASE:GetName()
return self.ZoneName return self.ZoneName
end end
--- Sets the name of the zone. --- Sets the name of the zone.
-- @param #ZONE_BASE self -- @param #ZONE_BASE self
-- @param #string ZoneName The name of the zone. -- @param #string ZoneName The name of the zone.
@@ -194,6 +203,7 @@ function ZONE_BASE:IsPointVec3InZone( PointVec3 )
return InZone return InZone
end end
--- Returns the @{DCS#Vec2} coordinate of the zone. --- Returns the @{DCS#Vec2} coordinate of the zone.
-- @param #ZONE_BASE self -- @param #ZONE_BASE self
-- @return #nil. -- @return #nil.
@@ -217,6 +227,7 @@ function ZONE_BASE:GetPointVec2()
return PointVec2 return PointVec2
end end
--- Returns the @{DCS#Vec3} of the zone. --- Returns the @{DCS#Vec3} of the zone.
-- @param #ZONE_BASE self -- @param #ZONE_BASE self
-- @param DCS#Distance Height The height to add to the land height where the center of the zone is located. -- @param DCS#Distance Height The height to add to the land height where the center of the zone is located.
@@ -279,6 +290,24 @@ function ZONE_BASE:GetCoordinate( Height ) -- R2.1
return self.Coordinate return self.Coordinate
end end
--- Get 2D distance to a coordinate.
-- @param #ZONE_BASE self
-- @param Core.Point#COORDINATE Coordinate Reference coordinate. Can also be a DCS#Vec2 or DCS#Vec3 object.
-- @return #number Distance to the reference coordinate in meters.
function ZONE_BASE:Get2DDistance(Coordinate)
local a=self:GetVec2()
local b={}
if Coordinate.z then
b.x=Coordinate.x
b.y=Coordinate.z
else
b.x=Coordinate.x
b.y=Coordinate.y
end
local dist=UTILS.VecDist2D(a,b)
return dist
end
--- Define a random @{DCS#Vec2} within the zone. --- Define a random @{DCS#Vec2} within the zone.
-- @param #ZONE_BASE self -- @param #ZONE_BASE self
-- @return DCS#Vec2 The Vec2 coordinates. -- @return DCS#Vec2 The Vec2 coordinates.
@@ -315,6 +344,7 @@ function ZONE_BASE:BoundZone()
end end
--- Set color of zone. --- Set color of zone.
-- @param #ZONE_BASE self -- @param #ZONE_BASE self
-- @param #table RGBcolor RGB color table. Default `{1, 0, 0}`. -- @param #table RGBcolor RGB color table. Default `{1, 0, 0}`.
@@ -383,6 +413,7 @@ function ZONE_BASE:GetDrawID()
return self.DrawID return self.DrawID
end end
--- Smokes the zone boundaries in a color. --- Smokes the zone boundaries in a color.
-- @param #ZONE_BASE self -- @param #ZONE_BASE self
-- @param Utilities.Utils#SMOKECOLOR SmokeColor The smoke color. -- @param Utilities.Utils#SMOKECOLOR SmokeColor The smoke color.
@@ -447,6 +478,7 @@ function ZONE_BASE:GetZoneMaybe()
end end
end end
--- The ZONE_RADIUS class, defined by a zone name, a location and a radius. --- The ZONE_RADIUS class, defined by a zone name, a location and a radius.
-- @type ZONE_RADIUS -- @type ZONE_RADIUS
-- @field DCS#Vec2 Vec2 The current location of the zone. -- @field DCS#Vec2 Vec2 The current location of the zone.
@@ -642,6 +674,7 @@ function ZONE_RADIUS:BoundZone( Points, CountryID, UnBound )
return self return self
end end
--- Smokes the zone boundaries in a color. --- Smokes the zone boundaries in a color.
-- @param #ZONE_RADIUS self -- @param #ZONE_RADIUS self
-- @param Utilities.Utils#SMOKECOLOR SmokeColor The smoke color. -- @param Utilities.Utils#SMOKECOLOR SmokeColor The smoke color.
@@ -673,6 +706,7 @@ function ZONE_RADIUS:SmokeZone( SmokeColor, Points, AddHeight, AngleOffset )
return self return self
end end
--- Flares the zone boundaries in a color. --- Flares the zone boundaries in a color.
-- @param #ZONE_RADIUS self -- @param #ZONE_RADIUS self
-- @param Utilities.Utils#FLARECOLOR FlareColor The flare color. -- @param Utilities.Utils#FLARECOLOR FlareColor The flare color.
@@ -769,6 +803,10 @@ function ZONE_RADIUS:GetVec3( Height )
return Vec3 return Vec3
end end
--- Scan the zone for the presence of units of the given ObjectCategories. --- Scan the zone for the presence of units of the given ObjectCategories.
-- Note that after a zone has been scanned, the zone can be evaluated by: -- Note that after a zone has been scanned, the zone can be evaluated by:
-- --
@@ -801,7 +839,7 @@ function ZONE_RADIUS:Scan( ObjectCategories, UnitCategories )
params = { params = {
point = ZoneCoord:GetVec3(), point = ZoneCoord:GetVec3(),
radius = ZoneRadius, radius = ZoneRadius,
}, }
} }
local function EvaluateZone( ZoneObject ) local function EvaluateZone( ZoneObject )
@@ -874,6 +912,7 @@ function ZONE_RADIUS:GetScannedUnits()
return self.ScanData.Units return self.ScanData.Units
end end
--- Get a set of scanned units. --- Get a set of scanned units.
-- @param #ZONE_RADIUS self -- @param #ZONE_RADIUS self
-- @return Core.Set#SET_UNIT Set of units and statics inside the zone. -- @return Core.Set#SET_UNIT Set of units and statics inside the zone.
@@ -927,6 +966,7 @@ function ZONE_RADIUS:GetScannedSetGroup()
return self.ScanSetGroup return self.ScanSetGroup
end end
--- Count the number of different coalitions inside the zone. --- Count the number of different coalitions inside the zone.
-- @param #ZONE_RADIUS self -- @param #ZONE_RADIUS self
-- @return #number Counted coalitions. -- @return #number Counted coalitions.
@@ -979,6 +1019,7 @@ function ZONE_RADIUS:GetScannedCoalition( Coalition )
end end
end end
--- Get scanned scenery type --- Get scanned scenery type
-- @param #ZONE_RADIUS self -- @param #ZONE_RADIUS self
-- @return #table Table of DCS scenery type objects. -- @return #table Table of DCS scenery type objects.
@@ -986,6 +1027,7 @@ function ZONE_RADIUS:GetScannedSceneryType( SceneryType )
return self.ScanData.Scenery[SceneryType] return self.ScanData.Scenery[SceneryType]
end end
--- Get scanned scenery table --- Get scanned scenery table
-- @param #ZONE_RADIUS self -- @param #ZONE_RADIUS self
-- @return #table Table of DCS scenery objects. -- @return #table Table of DCS scenery objects.
@@ -993,8 +1035,9 @@ function ZONE_RADIUS:GetScannedScenery()
return self.ScanData.Scenery return self.ScanData.Scenery
end end
--- Is All in Zone of Coalition? --- Is All in Zone of Coalition?
-- Check if only the specified coalition is inside the zone and noone else. -- Check if only the specifed coalition is inside the zone and noone else.
-- @param #ZONE_RADIUS self -- @param #ZONE_RADIUS self
-- @param #number Coalition Coalition ID of the coalition which is checked to be the only one in the zone. -- @param #number Coalition Coalition ID of the coalition which is checked to be the only one in the zone.
-- @return #boolean True, if **only** that coalition is inside the zone and no one else. -- @return #boolean True, if **only** that coalition is inside the zone and no one else.
@@ -1007,6 +1050,7 @@ function ZONE_RADIUS:IsAllInZoneOfCoalition( Coalition )
return self:CountScannedCoalitions() == 1 and self:GetScannedCoalition( Coalition ) == true return self:CountScannedCoalitions() == 1 and self:GetScannedCoalition( Coalition ) == true
end end
--- Is All in Zone of Other Coalition? --- Is All in Zone of Other Coalition?
-- Check if only one coalition is inside the zone and the specified coalition is not the one. -- Check if only one coalition is inside the zone and the specified coalition is not the one.
-- You first need to use the @{#ZONE_RADIUS.Scan} method to scan the zone before it can be evaluated! -- You first need to use the @{#ZONE_RADIUS.Scan} method to scan the zone before it can be evaluated!
@@ -1023,23 +1067,23 @@ function ZONE_RADIUS:IsAllInZoneOfOtherCoalition( Coalition )
return self:CountScannedCoalitions() == 1 and self:GetScannedCoalition( Coalition ) == nil return self:CountScannedCoalitions() == 1 and self:GetScannedCoalition( Coalition ) == nil
end end
--- Is Some in Zone of Coalition? --- Is Some in Zone of Coalition?
-- Check if more than one coalition is inside the zone and the specified coalition is one of them. -- Check if more than one coaltion is inside the zone and the specifed coalition is one of them.
-- You first need to use the @{#ZONE_RADIUS.Scan} method to scan the zone before it can be evaluated! -- You first need to use the @{#ZONE_RADIUS.Scan} method to scan the zone before it can be evaluated!
-- Note that once a zone has been scanned, multiple evaluations can be done on the scan result set. -- Note that once a zone has been scanned, multiple evaluations can be done on the scan result set.
-- @param #ZONE_RADIUS self -- @param #ZONE_RADIUS self
-- @param #number Coalition ID of the coalition which is checked to be inside the zone. -- @param #number Coalition ID of the coaliton which is checked to be inside the zone.
-- @return #boolean True if more than one coalition is inside the zone and the specified coalition is one of them. -- @return #boolean True if more than one coalition is inside the zone and the specified coalition is one of them.
-- @usage -- @usage
--
-- self.Zone:Scan() -- self.Zone:Scan()
-- local IsAttacked = self.Zone:IsSomeInZoneOfCoalition( self.Coalition ) -- local IsAttacked = self.Zone:IsSomeInZoneOfCoalition( self.Coalition )
--
function ZONE_RADIUS:IsSomeInZoneOfCoalition( Coalition ) function ZONE_RADIUS:IsSomeInZoneOfCoalition( Coalition )
return self:CountScannedCoalitions() > 1 and self:GetScannedCoalition( Coalition ) == true return self:CountScannedCoalitions() > 1 and self:GetScannedCoalition( Coalition ) == true
end end
--- Is None in Zone of Coalition? --- Is None in Zone of Coalition?
-- You first need to use the @{#ZONE_RADIUS.Scan} method to scan the zone before it can be evaluated! -- You first need to use the @{#ZONE_RADIUS.Scan} method to scan the zone before it can be evaluated!
-- Note that once a zone has been scanned, multiple evaluations can be done on the scan result set. -- Note that once a zone has been scanned, multiple evaluations can be done on the scan result set.
@@ -1047,30 +1091,30 @@ end
-- @param Coalition -- @param Coalition
-- @return #boolean -- @return #boolean
-- @usage -- @usage
--
-- self.Zone:Scan() -- self.Zone:Scan()
-- local IsOccupied = self.Zone:IsNoneInZoneOfCoalition( self.Coalition ) -- local IsOccupied = self.Zone:IsNoneInZoneOfCoalition( self.Coalition )
--
function ZONE_RADIUS:IsNoneInZoneOfCoalition( Coalition ) function ZONE_RADIUS:IsNoneInZoneOfCoalition( Coalition )
return self:GetScannedCoalition( Coalition ) == nil return self:GetScannedCoalition( Coalition ) == nil
end end
--- Is None in Zone? --- Is None in Zone?
-- You first need to use the @{#ZONE_RADIUS.Scan} method to scan the zone before it can be evaluated! -- You first need to use the @{#ZONE_RADIUS.Scan} method to scan the zone before it can be evaluated!
-- Note that once a zone has been scanned, multiple evaluations can be done on the scan result set. -- Note that once a zone has been scanned, multiple evaluations can be done on the scan result set.
-- @param #ZONE_RADIUS self -- @param #ZONE_RADIUS self
-- @return #boolean -- @return #boolean
-- @usage -- @usage
--
-- self.Zone:Scan() -- self.Zone:Scan()
-- local IsEmpty = self.Zone:IsNoneInZone() -- local IsEmpty = self.Zone:IsNoneInZone()
--
function ZONE_RADIUS:IsNoneInZone() function ZONE_RADIUS:IsNoneInZone()
return self:CountScannedCoalitions() == 0 return self:CountScannedCoalitions() == 0
end end
--- Searches the zone --- Searches the zone
-- @param #ZONE_RADIUS self -- @param #ZONE_RADIUS self
-- @param ObjectCategories A list of categories, which are members of Object.Category -- @param ObjectCategories A list of categories, which are members of Object.Category
@@ -1089,11 +1133,12 @@ function ZONE_RADIUS:SearchZone( EvaluateFunction, ObjectCategories )
params = { params = {
point = ZoneCoord:GetVec3(), point = ZoneCoord:GetVec3(),
radius = ZoneRadius / 2, radius = ZoneRadius / 2,
}, }
} }
local function EvaluateZone( ZoneDCSUnit ) local function EvaluateZone( ZoneDCSUnit )
local ZoneUnit = UNIT:Find( ZoneDCSUnit ) local ZoneUnit = UNIT:Find( ZoneDCSUnit )
return EvaluateFunction( ZoneUnit ) return EvaluateFunction( ZoneUnit )
@@ -1135,24 +1180,54 @@ end
--- Returns a random Vec2 location within the zone. --- Returns a random Vec2 location within the zone.
-- @param #ZONE_RADIUS self -- @param #ZONE_RADIUS self
-- @param #number inner (optional) Minimal distance from the center of the zone. Default is 0. -- @param #number inner (Optional) Minimal distance from the center of the zone. Default is 0.
-- @param #number outer (optional) Maximal distance from the outer edge of the zone. Default is the radius of the zone. -- @param #number outer (Optional) Maximal distance from the outer edge of the zone. Default is the radius of the zone.
-- @param #table surfacetypes (Optional) Table of surface types. Can also be a single surface type. We will try max 100 times to find the right type!
-- @return DCS#Vec2 The random location within the zone. -- @return DCS#Vec2 The random location within the zone.
function ZONE_RADIUS:GetRandomVec2( inner, outer ) function ZONE_RADIUS:GetRandomVec2(inner, outer, surfacetypes)
self:F( self.ZoneName, inner, outer )
local Point = {}
local Vec2 = self:GetVec2() local Vec2 = self:GetVec2()
local _inner = inner or 0 local _inner = inner or 0
local _outer = outer or self:GetRadius() local _outer = outer or self:GetRadius()
local angle = math.random() * math.pi * 2; if surfacetypes and type(surfacetypes)~="table" then
Point.x = Vec2.x + math.cos( angle ) * math.random( _inner, _outer ); surfacetypes={surfacetypes}
Point.y = Vec2.y + math.sin( angle ) * math.random( _inner, _outer ); end
self:T( { Point } ) local function _getpoint()
local point = {}
local angle = math.random() * math.pi * 2
point.x = Vec2.x + math.cos(angle) * math.random(_inner, _outer)
point.y = Vec2.y + math.sin(angle) * math.random(_inner, _outer)
return point
end
return Point local function _checkSurface(point)
local stype=land.getSurfaceType(point)
for _,sf in pairs(surfacetypes) do
if sf==stype then
return true
end
end
return false
end
local point=_getpoint()
if surfacetypes then
local N=1 ; local Nmax=100 ; local gotit=false
while gotit==false and N<=Nmax do
gotit=_checkSurface(point)
if gotit then
--env.info(string.format("Got random coordinate with surface type %d after N=%d/%d iterations", land.getSurfaceType(point), N, Nmax))
else
point=_getpoint()
N=N+1
end
end
end
return point
end end
--- Returns a @{Core.Point#POINT_VEC2} object reflecting a random 2D location within the zone. --- Returns a @{Core.Point#POINT_VEC2} object reflecting a random 2D location within the zone.
@@ -1185,6 +1260,7 @@ function ZONE_RADIUS:GetRandomVec3( inner, outer )
return { x = Vec2.x, y = self.y, z = Vec2.y } return { x = Vec2.x, y = self.y, z = Vec2.y }
end end
--- Returns a @{Core.Point#POINT_VEC3} object reflecting a random 3D location within the zone. --- Returns a @{Core.Point#POINT_VEC3} object reflecting a random 3D location within the zone.
-- @param #ZONE_RADIUS self -- @param #ZONE_RADIUS self
-- @param #number inner (optional) Minimal distance from the center of the zone. Default is 0. -- @param #number inner (optional) Minimal distance from the center of the zone. Default is 0.
@@ -1200,24 +1276,28 @@ function ZONE_RADIUS:GetRandomPointVec3( inner, outer )
return PointVec3 return PointVec3
end end
--- Returns a @{Core.Point#COORDINATE} object reflecting a random 3D location within the zone. --- Returns a @{Core.Point#COORDINATE} object reflecting a random 3D location within the zone.
-- @param #ZONE_RADIUS self -- @param #ZONE_RADIUS self
-- @param #number inner (optional) Minimal distance from the center of the zone. Default is 0. -- @param #number inner (Optional) Minimal distance from the center of the zone. Default is 0.
-- @param #number outer (optional) Maximal distance from the outer edge of the zone. Default is the radius of the zone. -- @param #number outer (Optional) Maximal distance from the outer edge of the zone. Default is the radius of the zone.
-- @return Core.Point#COORDINATE -- @param #table surfacetypes (Optional) Table of surface types. Can also be a single surface type. We will try max 1000 times to find the right type!
function ZONE_RADIUS:GetRandomCoordinate( inner, outer ) -- @return Core.Point#COORDINATE The random coordinate.
self:F( self.ZoneName, inner, outer ) function ZONE_RADIUS:GetRandomCoordinate(inner, outer, surfacetypes)
local Coordinate = COORDINATE:NewFromVec2( self:GetRandomVec2( inner, outer ) ) local vec2=self:GetRandomVec2(inner, outer, surfacetypes)
self:T3( { Coordinate = Coordinate } ) local Coordinate = COORDINATE:NewFromVec2(vec2)
return Coordinate return Coordinate
end end
--- @type ZONE --- @type ZONE
-- @extends #ZONE_RADIUS -- @extends #ZONE_RADIUS
--- The ZONE class, defined by the zone name as defined within the Mission Editor. --- The ZONE class, defined by the zone name as defined within the Mission Editor.
-- This class implements the inherited functions from @{#ZONE_RADIUS} taking into account the own zone format and properties. -- This class implements the inherited functions from @{#ZONE_RADIUS} taking into account the own zone format and properties.
-- --
@@ -1250,6 +1330,7 @@ ZONE = {
ClassName="ZONE", ClassName="ZONE",
} }
--- Constructor of ZONE taking the zone name. --- Constructor of ZONE taking the zone name.
-- @param #ZONE self -- @param #ZONE self
-- @param #string ZoneName The name of the zone as defined within the mission editor. -- @param #string ZoneName The name of the zone as defined within the mission editor.
@@ -1269,7 +1350,7 @@ function ZONE:New( ZoneName )
-- Error! -- Error!
if not Zone then if not Zone then
error( "Zone " .. ZoneName .. " does not exist." ) env.error( "ERROR: Zone " .. ZoneName .. " does not exist!" )
return nil return nil
end end
@@ -1296,10 +1377,13 @@ function ZONE:FindByName( ZoneName )
return ZoneFound return ZoneFound
end end
--- @type ZONE_UNIT --- @type ZONE_UNIT
-- @field Wrapper.Unit#UNIT ZoneUNIT -- @field Wrapper.Unit#UNIT ZoneUNIT
-- @extends Core.Zone#ZONE_RADIUS -- @extends Core.Zone#ZONE_RADIUS
--- # ZONE_UNIT class, extends @{Zone#ZONE_RADIUS} --- # ZONE_UNIT class, extends @{Zone#ZONE_RADIUS}
-- --
-- The ZONE_UNIT class defined by a zone attached to a @{Wrapper.Unit#UNIT} with a radius and optional offsets. -- The ZONE_UNIT class defined by a zone attached to a @{Wrapper.Unit#UNIT} with a radius and optional offsets.
@@ -1351,6 +1435,7 @@ function ZONE_UNIT:New( ZoneName, ZoneUNIT, Radius, Offset )
return self return self
end end
--- Returns the current location of the @{Wrapper.Unit#UNIT}. --- Returns the current location of the @{Wrapper.Unit#UNIT}.
-- @param #ZONE_UNIT self -- @param #ZONE_UNIT self
-- @return DCS#Vec2 The location of the zone based on the @{Wrapper.Unit#UNIT}location and the offset, if any. -- @return DCS#Vec2 The location of the zone based on the @{Wrapper.Unit#UNIT}location and the offset, if any.
@@ -1437,6 +1522,7 @@ end
--- @type ZONE_GROUP --- @type ZONE_GROUP
-- @extends #ZONE_RADIUS -- @extends #ZONE_RADIUS
--- The ZONE_GROUP class defines by a zone around a @{Wrapper.Group#GROUP} with a radius. The current leader of the group defines the center of the zone. --- The ZONE_GROUP class defines by a zone around a @{Wrapper.Group#GROUP} with a radius. The current leader of the group defines the center of the zone.
-- This class implements the inherited functions from @{Core.Zone#ZONE_RADIUS} taking into account the own zone format and properties. -- This class implements the inherited functions from @{Core.Zone#ZONE_RADIUS} taking into account the own zone format and properties.
-- --
@@ -1464,6 +1550,7 @@ function ZONE_GROUP:New( ZoneName, ZoneGROUP, Radius )
return self return self
end end
--- Returns the current location of the @{Wrapper.Group}. --- Returns the current location of the @{Wrapper.Group}.
-- @param #ZONE_GROUP self -- @param #ZONE_GROUP self
-- @return DCS#Vec2 The location of the zone based on the @{Wrapper.Group} location. -- @return DCS#Vec2 The location of the zone based on the @{Wrapper.Group} location.
@@ -1517,10 +1604,12 @@ function ZONE_GROUP:GetRandomPointVec2( inner, outer )
return PointVec2 return PointVec2
end end
--- @type ZONE_POLYGON_BASE --- @type ZONE_POLYGON_BASE
-- --@field #ZONE_POLYGON_BASE.ListVec2 Polygon The polygon defined by an array of @{DCS#Vec2}. -- --@field #ZONE_POLYGON_BASE.ListVec2 Polygon The polygon defined by an array of @{DCS#Vec2}.
-- @extends #ZONE_BASE -- @extends #ZONE_BASE
--- The ZONE_POLYGON_BASE class defined by a sequence of @{Wrapper.Group#GROUP} waypoints within the Mission Editor, forming a polygon. --- The ZONE_POLYGON_BASE class defined by a sequence of @{Wrapper.Group#GROUP} waypoints within the Mission Editor, forming a polygon.
-- This class implements the inherited functions from @{Core.Zone#ZONE_RADIUS} taking into account the own zone format and properties. -- This class implements the inherited functions from @{Core.Zone#ZONE_RADIUS} taking into account the own zone format and properties.
-- This class is an abstract BASE class for derived classes, and is not meant to be instantiated. -- This class is an abstract BASE class for derived classes, and is not meant to be instantiated.
@@ -1658,6 +1747,7 @@ function ZONE_POLYGON_BASE:GetVertexCoordinate( Index )
return nil return nil
end end
--- Get a list of verticies of the polygon. --- Get a list of verticies of the polygon.
-- @param #ZONE_POLYGON_BASE self -- @param #ZONE_POLYGON_BASE self
-- @return <DCS#Vec2> List of DCS#Vec2 verticies defining the edges of the polygon. -- @return <DCS#Vec2> List of DCS#Vec2 verticies defining the edges of the polygon.
@@ -1753,6 +1843,7 @@ function ZONE_POLYGON_BASE:BoundZone( UnBound )
return self return self
end end
--- Draw the zone on the F10 map. **NOTE** Currently, only polygons with **exactly four points** are supported! --- Draw the zone on the F10 map. **NOTE** Currently, only polygons with **exactly four points** are supported!
-- @param #ZONE_POLYGON_BASE self -- @param #ZONE_POLYGON_BASE self
-- @param #number Coalition Coalition: All=-1, Neutral=0, Red=1, Blue=2. Default -1=All. -- @param #number Coalition Coalition: All=-1, Neutral=0, Red=1, Blue=2. Default -1=All.
@@ -1772,6 +1863,7 @@ function ZONE_POLYGON_BASE:DrawZone( Coalition, Color, Alpha, FillColor, FillAlp
FillColor=FillColor or Color FillColor=FillColor or Color
FillAlpha=FillAlpha or self:GetColorAlpha() FillAlpha=FillAlpha or self:GetColorAlpha()
if #self._.Polygon==4 then if #self._.Polygon==4 then
local Coord2=COORDINATE:NewFromVec2(self._.Polygon[2]) local Coord2=COORDINATE:NewFromVec2(self._.Polygon[2])
@@ -1789,6 +1881,7 @@ function ZONE_POLYGON_BASE:DrawZone( Coalition, Color, Alpha, FillColor, FillAlp
end end
return self return self
end end
@@ -1823,6 +1916,7 @@ function ZONE_POLYGON_BASE:SmokeZone( SmokeColor, Segments )
return self return self
end end
--- Flare the zone boundaries in a color. --- Flare the zone boundaries in a color.
-- @param #ZONE_POLYGON_BASE self -- @param #ZONE_POLYGON_BASE self
-- @param Utilities.Utils#FLARECOLOR FlareColor The flare color. -- @param Utilities.Utils#FLARECOLOR FlareColor The flare color.
@@ -1858,6 +1952,9 @@ function ZONE_POLYGON_BASE:FlareZone( FlareColor, Segments, Azimuth, AddHeight )
return self return self
end end
--- Returns if a location is within the zone. --- Returns if a location is within the zone.
-- Source learned and taken from: https://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html -- Source learned and taken from: https://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html
-- @param #ZONE_POLYGON_BASE self -- @param #ZONE_POLYGON_BASE self
@@ -1876,7 +1973,8 @@ function ZONE_POLYGON_BASE:IsVec2InZone( Vec2 )
while Next <= #self._.Polygon do while Next <= #self._.Polygon do
self:T( { Next, Prev, self._.Polygon[Next], self._.Polygon[Prev] } ) self:T( { Next, Prev, self._.Polygon[Next], self._.Polygon[Prev] } )
if ( ( ( self._.Polygon[Next].y > Vec2.y ) ~= ( self._.Polygon[Prev].y > Vec2.y ) ) and if ( ( ( self._.Polygon[Next].y > Vec2.y ) ~= ( self._.Polygon[Prev].y > Vec2.y ) ) and
(Vec2.x < (self._.Polygon[Prev].x - self._.Polygon[Next].x) * (Vec2.y - self._.Polygon[Next].y) / (self._.Polygon[Prev].y - self._.Polygon[Next].y) + self._.Polygon[Next].x)) then ( Vec2.x < ( self._.Polygon[Prev].x - self._.Polygon[Next].x ) * ( Vec2.y - self._.Polygon[Next].y ) / ( self._.Polygon[Prev].y - self._.Polygon[Next].y ) + self._.Polygon[Next].x )
) then
InPolygon = not InPolygon InPolygon = not InPolygon
end end
self:T2( { InPolygon = InPolygon } ) self:T2( { InPolygon = InPolygon } )
@@ -1892,28 +1990,30 @@ end
-- @param #ZONE_POLYGON_BASE self -- @param #ZONE_POLYGON_BASE self
-- @return DCS#Vec2 The Vec2 coordinate. -- @return DCS#Vec2 The Vec2 coordinate.
function ZONE_POLYGON_BASE:GetRandomVec2() function ZONE_POLYGON_BASE:GetRandomVec2()
self:F2()
--- It is a bit tricky to find a random point within a polygon. Right now i am doing it the dirty and inefficient way... -- It is a bit tricky to find a random point within a polygon. Right now i am doing it the dirty and inefficient way...
local Vec2Found = false
local Vec2 -- Get the bounding square.
local BS = self:GetBoundingSquare() local BS = self:GetBoundingSquare()
self:T2( BS ) local Nmax=1000 ; local n=0
while n<Nmax do
while Vec2Found == false do -- Random point in the bounding square.
Vec2 = { x = math.random( BS.x1, BS.x2 ), y = math.random( BS.y1, BS.y2 ) } local Vec2={x=math.random(BS.x1, BS.x2), y=math.random(BS.y1, BS.y2)}
self:T2( Vec2 )
-- Check if this is in the polygon.
if self:IsVec2InZone(Vec2) then if self:IsVec2InZone(Vec2) then
Vec2Found = true
end
end
self:T2( Vec2 )
return Vec2 return Vec2
end end
n=n+1
end
self:E("Could not find a random point in the polygon zone!")
return nil
end
--- Return a @{Core.Point#POINT_VEC2} object representing a random 2D point at landheight within the zone. --- Return a @{Core.Point#POINT_VEC2} object representing a random 2D point at landheight within the zone.
-- @param #ZONE_POLYGON_BASE self -- @param #ZONE_POLYGON_BASE self
-- @return @{Core.Point#POINT_VEC2} -- @return @{Core.Point#POINT_VEC2}
@@ -1940,6 +2040,7 @@ function ZONE_POLYGON_BASE:GetRandomPointVec3()
return PointVec3 return PointVec3
end end
--- Return a @{Core.Point#COORDINATE} object representing a random 3D point at landheight within the zone. --- Return a @{Core.Point#COORDINATE} object representing a random 3D point at landheight within the zone.
-- @param #ZONE_POLYGON_BASE self -- @param #ZONE_POLYGON_BASE self
-- @return Core.Point#COORDINATE -- @return Core.Point#COORDINATE
@@ -1953,6 +2054,7 @@ function ZONE_POLYGON_BASE:GetRandomCoordinate()
return Coordinate return Coordinate
end end
--- Get the bounding square the zone. --- Get the bounding square the zone.
-- @param #ZONE_POLYGON_BASE self -- @param #ZONE_POLYGON_BASE self
-- @return #ZONE_POLYGON_BASE.BoundingSquare The bounding square. -- @return #ZONE_POLYGON_BASE.BoundingSquare The bounding square.
@@ -2018,17 +2120,18 @@ end
--- @type ZONE_POLYGON --- @type ZONE_POLYGON
-- @extends #ZONE_POLYGON_BASE -- @extends #ZONE_POLYGON_BASE
--- The ZONE_POLYGON class defined by a sequence of @{Wrapper.Group#GROUP} waypoints within the Mission Editor, forming a polygon. --- The ZONE_POLYGON class defined by a sequence of @{Wrapper.Group#GROUP} waypoints within the Mission Editor, forming a polygon.
-- This class implements the inherited functions from @{Core.Zone#ZONE_RADIUS} taking into account the own zone format and properties. -- This class implements the inherited functions from @{Core.Zone#ZONE_RADIUS} taking into account the own zone format and properties.
-- --
-- ## Declare a ZONE_POLYGON directly in the DCS mission editor! -- ## Declare a ZONE_POLYGON directly in the DCS mission editor!
-- --
-- You can declare a ZONE_POLYGON using the DCS mission editor by adding the ~ZONE_POLYGON tag in the group name. -- You can declare a ZONE_POLYGON using the DCS mission editor by adding the #ZONE_POLYGON tag in the group name.
-- --
-- So, imagine you have a group declared in the mission editor, with group name `DefenseZone~ZONE_POLYGON`. -- So, imagine you have a group declared in the mission editor, with group name `DefenseZone#ZONE_POLYGON`.
-- Then during mission startup, when loading Moose.lua, this group will be detected as a ZONE_POLYGON declaration. -- Then during mission startup, when loading Moose.lua, this group will be detected as a ZONE_POLYGON declaration.
-- Within the background, a ZONE_POLYGON object will be created within the @{Core.Database} using the properties of the group. -- Within the background, a ZONE_POLYGON object will be created within the @{Core.Database} using the properties of the group.
-- The ZONE_POLYGON name will be the group name without the ~ZONE_POLYGON tag. -- The ZONE_POLYGON name will be the group name without the #ZONE_POLYGON tag.
-- --
-- So, you can search yourself for the ZONE_POLYGON by using the @{#ZONE_POLYGON.FindByName}() method. -- So, you can search yourself for the ZONE_POLYGON by using the @{#ZONE_POLYGON.FindByName}() method.
-- In this example, `local PolygonZone = ZONE_POLYGON:FindByName( "DefenseZone" )` would return the ZONE_POLYGON object -- In this example, `local PolygonZone = ZONE_POLYGON:FindByName( "DefenseZone" )` would return the ZONE_POLYGON object
@@ -2065,6 +2168,7 @@ function ZONE_POLYGON:New( ZoneName, ZoneGroup )
return self return self
end end
--- Constructor to create a ZONE_POLYGON instance, taking the zone name and the **name** of the @{Wrapper.Group#GROUP} defined within the Mission Editor. --- Constructor to create a ZONE_POLYGON instance, taking the zone name and the **name** of the @{Wrapper.Group#GROUP} defined within the Mission Editor.
-- The @{Wrapper.Group#GROUP} waypoints define the polygon corners. The first and the last point are automatically connected by ZONE_POLYGON. -- The @{Wrapper.Group#GROUP} waypoints define the polygon corners. The first and the last point are automatically connected by ZONE_POLYGON.
-- @param #ZONE_POLYGON self -- @param #ZONE_POLYGON self
@@ -2085,6 +2189,7 @@ function ZONE_POLYGON:NewFromGroupName( GroupName )
return self return self
end end
--- Find a polygon zone in the _DATABASE using the name of the polygon zone. --- Find a polygon zone in the _DATABASE using the name of the polygon zone.
-- @param #ZONE_POLYGON self -- @param #ZONE_POLYGON self
-- @param #string ZoneName The name of the polygon zone. -- @param #string ZoneName The name of the polygon zone.
@@ -2098,8 +2203,12 @@ end
do -- ZONE_AIRBASE do -- ZONE_AIRBASE
--- @type ZONE_AIRBASE --- @type ZONE_AIRBASE
-- @field #boolean isShip If `true`, airbase is a ship.
-- @field #boolean isHelipad If `true`, airbase is a helipad.
-- @field #boolean isAirdrome If `true`, airbase is an airdrome.
-- @extends #ZONE_RADIUS -- @extends #ZONE_RADIUS
--- The ZONE_AIRBASE class defines by a zone around a @{Wrapper.Airbase#AIRBASE} with a radius. --- The ZONE_AIRBASE class defines by a zone around a @{Wrapper.Airbase#AIRBASE} with a radius.
-- This class implements the inherited functions from @{Core.Zone#ZONE_RADIUS} taking into account the own zone format and properties. -- This class implements the inherited functions from @{Core.Zone#ZONE_RADIUS} taking into account the own zone format and properties.
-- --
@@ -2108,6 +2217,8 @@ do -- ZONE_AIRBASE
ClassName="ZONE_AIRBASE", ClassName="ZONE_AIRBASE",
} }
--- Constructor to create a ZONE_AIRBASE instance, taking the zone name, a zone @{Wrapper.Airbase#AIRBASE} and a radius. --- Constructor to create a ZONE_AIRBASE instance, taking the zone name, a zone @{Wrapper.Airbase#AIRBASE} and a radius.
-- @param #ZONE_AIRBASE self -- @param #ZONE_AIRBASE self
-- @param #string AirbaseName Name of the airbase. -- @param #string AirbaseName Name of the airbase.
@@ -2124,6 +2235,20 @@ do -- ZONE_AIRBASE
self._.ZoneAirbase = Airbase self._.ZoneAirbase = Airbase
self._.ZoneVec2Cache = self._.ZoneAirbase:GetVec2() self._.ZoneVec2Cache = self._.ZoneAirbase:GetVec2()
if Airbase:IsShip() then
self.isShip=true
self.isHelipad=false
self.isAirdrome=false
elseif Airbase:IsHelipad() then
self.isShip=false
self.isHelipad=true
self.isAirdrome=false
elseif Airbase:IsAirdrome() then
self.isShip=false
self.isHelipad=false
self.isAirdrome=true
end
-- Zone objects are added to the _DATABASE and SET_ZONE objects. -- Zone objects are added to the _DATABASE and SET_ZONE objects.
_EVENTDISPATCHER:CreateEventNewZone( self ) _EVENTDISPATCHER:CreateEventNewZone( self )
@@ -2137,9 +2262,9 @@ do -- ZONE_AIRBASE
return self._.ZoneAirbase return self._.ZoneAirbase
end end
--- Returns the current location of the @{Wrapper.Group}. --- Returns the current location of the AIRBASE.
-- @param #ZONE_AIRBASE self -- @param #ZONE_AIRBASE self
-- @return DCS#Vec2 The location of the zone based on the @{Wrapper.Group} location. -- @return DCS#Vec2 The location of the zone based on the AIRBASE location.
function ZONE_AIRBASE:GetVec2() function ZONE_AIRBASE:GetVec2()
self:F( self.ZoneName ) self:F( self.ZoneName )
@@ -2157,24 +2282,6 @@ do -- ZONE_AIRBASE
return ZoneVec2 return ZoneVec2
end end
--- Returns a random location within the zone of the @{Wrapper.Group}.
-- @param #ZONE_AIRBASE self
-- @return DCS#Vec2 The random location of the zone based on the @{Wrapper.Group} location.
function ZONE_AIRBASE:GetRandomVec2()
self:F( self.ZoneName )
local Point = {}
local Vec2 = self._.ZoneAirbase:GetVec2()
local angle = math.random() * math.pi * 2;
Point.x = Vec2.x + math.cos( angle ) * math.random() * self:GetRadius();
Point.y = Vec2.y + math.sin( angle ) * math.random() * self:GetRadius();
self:T( { Point } )
return Point
end
--- Returns a @{Core.Point#POINT_VEC2} object reflecting a random 2D location within the zone. --- Returns a @{Core.Point#POINT_VEC2} object reflecting a random 2D location within the zone.
-- @param #ZONE_AIRBASE self -- @param #ZONE_AIRBASE self
-- @param #number inner (optional) Minimal distance from the center of the zone. Default is 0. -- @param #number inner (optional) Minimal distance from the center of the zone. Default is 0.
@@ -2190,4 +2297,5 @@ do -- ZONE_AIRBASE
return PointVec2 return PointVec2
end end
end end

View File

@@ -1053,7 +1053,7 @@ end
-- * `AIRBASE.Nevada.Lincoln_County` -- * `AIRBASE.Nevada.Lincoln_County`
-- * `AIRBASE.Nevada.McCarran_International_Airport` -- * `AIRBASE.Nevada.McCarran_International_Airport`
-- * `AIRBASE.Nevada.Mesquite` -- * `AIRBASE.Nevada.Mesquite`
-- * `AIRBASE.Nevada.Mina_Airport_3Q0` -- * `AIRBASE.Nevada.Mina_Airport`
-- * `AIRBASE.Nevada.Nellis_AFB` -- * `AIRBASE.Nevada.Nellis_AFB`
-- * `AIRBASE.Nevada.North_Las_Vegas` -- * `AIRBASE.Nevada.North_Las_Vegas`
-- * `AIRBASE.Nevada.Pahute_Mesa_Airstrip` -- * `AIRBASE.Nevada.Pahute_Mesa_Airstrip`

View File

@@ -19,7 +19,7 @@
-- --
-- === -- ===
-- --
-- Facilitate the detection of enemy units within the battle zone executed by FACs (Forward Air Controllers) or RECCEs (Reconnassance Units). -- Facilitate the detection of enemy units within the battle zone executed by FACs (Forward Air Controllers) or RECCEs (Reconnaissance Units).
-- It uses the in-built detection capabilities of DCS World, but adds new functionalities. -- It uses the in-built detection capabilities of DCS World, but adds new functionalities.
-- --
-- === -- ===
@@ -37,7 +37,6 @@
-- @module Functional.Detection -- @module Functional.Detection
-- @image Detection.JPG -- @image Detection.JPG
do -- DETECTION_BASE do -- DETECTION_BASE
--- @type DETECTION_BASE --- @type DETECTION_BASE
@@ -92,7 +91,6 @@ do -- DETECTION_BASE
-- --
-- DetectionObject:FilterCategories( { Unit.Category.AIRPLANE, Unit.Category.HELICOPTER } ) -- DetectionObject:FilterCategories( { Unit.Category.AIRPLANE, Unit.Category.HELICOPTER } )
-- --
--
-- ## **DETECTION_ derived classes** group the detected units into a **DetectedItems[]** list -- ## **DETECTION_ derived classes** group the detected units into a **DetectedItems[]** list
-- --
-- DETECTION_BASE derived classes build a list called DetectedItems[], which is essentially a first later -- DETECTION_BASE derived classes build a list called DetectedItems[], which is essentially a first later
@@ -128,11 +126,10 @@ do -- DETECTION_BASE
-- * A probability factor based on the alpha angle between the detected object and the unit detecting. -- * A probability factor based on the alpha angle between the detected object and the unit detecting.
-- A detection from a higher altitude allows for better detection than when on the ground. -- A detection from a higher altitude allows for better detection than when on the ground.
-- * Define a probability factor for "cloudy zones", which are zones where forests or villages are located. In these zones, detection will be much more difficult. -- * Define a probability factor for "cloudy zones", which are zones where forests or villages are located. In these zones, detection will be much more difficult.
-- The mission designer needs to define these cloudy zones within the mission, and needs to register these zones in the DETECTION_ objects additing a probability factor per zone. -- The mission designer needs to define these cloudy zones within the mission, and needs to register these zones in the DETECTION_ objects adding a probability factor per zone.
-- --
-- I advise however, that, when you first use the DETECTION derived classes, that you don't use these filters. -- I advise however, that, when you first use the DETECTION derived classes, that you don't use these filters.
-- Only when you experience unrealistic behaviour in your missions, these filters could be applied. -- Only when you experience unrealistic behavior in your missions, these filters could be applied.
--
-- --
-- ### Distance visual detection probability -- ### Distance visual detection probability
-- --
@@ -170,9 +167,9 @@ do -- DETECTION_BASE
-- --
-- Note however, that the more zones are defined to be "cloudy" within a detection, the more performance it will take -- Note however, that the more zones are defined to be "cloudy" within a detection, the more performance it will take
-- from the DETECTION_BASE to calculate the presence of the detected unit within each zone. -- from the DETECTION_BASE to calculate the presence of the detected unit within each zone.
-- Expecially for ZONE_POLYGON, try to limit the amount of nodes of the polygon! -- Especially for ZONE_POLYGON, try to limit the amount of nodes of the polygon!
-- --
-- Typically, this kind of filter would be applied for very specific areas were a detection needs to be very realisting for -- Typically, this kind of filter would be applied for very specific areas where a detection needs to be very realistic for
-- AI not to detect so easily targets within a forrest or village rich area. -- AI not to detect so easily targets within a forrest or village rich area.
-- --
-- ## Accept / Reject detected units -- ## Accept / Reject detected units
@@ -217,7 +214,7 @@ do -- DETECTION_BASE
-- -- Start the Detection. -- -- Start the Detection.
-- Detection:Start() -- Detection:Start()
-- --
-- ### Detection rejectance if within zone(s). -- ### Detection rejection if within zone(s).
-- --
-- Specific ZONE_BASE object(s) can be given as a parameter, which will reject detection if the unit is within the specified ZONE_BASE object(s). -- Specific ZONE_BASE object(s) can be given as a parameter, which will reject detection if the unit is within the specified ZONE_BASE object(s).
-- Use the method @{Functional.Detection#DETECTION_BASE.SetRejectZones}() will reject detected units if they are within the specified zones. -- Use the method @{Functional.Detection#DETECTION_BASE.SetRejectZones}() will reject detected units if they are within the specified zones.
@@ -287,11 +284,10 @@ do -- DETECTION_BASE
-- @field #boolean LastPos -- @field #boolean LastPos
-- @field #number LastVelocity -- @field #number LastVelocity
--- @type DETECTION_BASE.DetectedItems --- @type DETECTION_BASE.DetectedItems
-- @list <#DETECTION_BASE.DetectedItem> -- @list <#DETECTION_BASE.DetectedItem>
--- Detected item data structrue. --- Detected item data structure.
-- @type DETECTION_BASE.DetectedItem -- @type DETECTION_BASE.DetectedItem
-- @field #boolean IsDetected Indicates if the DetectedItem has been detected or not. -- @field #boolean IsDetected Indicates if the DetectedItem has been detected or not.
-- @field Core.Set#SET_UNIT Set The Set of Units in the detected area. -- @field Core.Set#SET_UNIT Set The Set of Units in the detected area.
@@ -302,7 +298,7 @@ do -- DETECTION_BASE
-- @field #boolean FriendliesNearBy Indicates if there are friendlies within the detected area. -- @field #boolean FriendliesNearBy Indicates if there are friendlies within the detected area.
-- @field Wrapper.Unit#UNIT NearestFAC The nearest FAC near the Area. -- @field Wrapper.Unit#UNIT NearestFAC The nearest FAC near the Area.
-- @field Core.Point#COORDINATE Coordinate The last known coordinate of the DetectedItem. -- @field Core.Point#COORDINATE Coordinate The last known coordinate of the DetectedItem.
-- @field Core.Point#COORDINATE InterceptCoord Intercept coordiante. -- @field Core.Point#COORDINATE InterceptCoord Intercept coordinate.
-- @field #number DistanceRecce Distance in meters of the Recce. -- @field #number DistanceRecce Distance in meters of the Recce.
-- @field #number Index Detected item key. Could also be a string. -- @field #number Index Detected item key. Could also be a string.
-- @field #string ItemID ItemPrefix .. "." .. self.DetectedItemMax. -- @field #string ItemID ItemPrefix .. "." .. self.DetectedItemMax.
@@ -310,7 +306,7 @@ do -- DETECTION_BASE
-- @field #table PlayersNearBy Table of nearby players. -- @field #table PlayersNearBy Table of nearby players.
-- @field #table FriendliesDistance Table of distances to friendly units. -- @field #table FriendliesDistance Table of distances to friendly units.
-- @field #string TypeName Type name of the detected unit. -- @field #string TypeName Type name of the detected unit.
-- @field #string CategoryName Catetory name of the detected unit. -- @field #string CategoryName Category name of the detected unit.
-- @field #string Name Name of the detected object. -- @field #string Name Name of the detected object.
-- @field #boolean IsVisible If true, detected object is visible. -- @field #boolean IsVisible If true, detected object is visible.
-- @field #number LastTime Last time the detected item was seen. -- @field #number LastTime Last time the detected item was seen.
@@ -441,7 +437,6 @@ do -- DETECTION_BASE
-- @param #DETECTION_BASE self -- @param #DETECTION_BASE self
-- @param #number Delay The delay in seconds. -- @param #number Delay The delay in seconds.
self:AddTransition( "Detecting", "Detected", "Detecting" ) self:AddTransition( "Detecting", "Detected", "Detecting" )
--- OnBefore Transition Handler for Event Detected. --- OnBefore Transition Handler for Event Detected.
@@ -566,12 +561,10 @@ do -- DETECTION_BASE
local DetectionInterval = self.DetectionCount / (self.RefreshTimeInterval - 1) local DetectionInterval = self.DetectionCount / (self.RefreshTimeInterval - 1)
self:ForEachAliveRecce( self:ForEachAliveRecce( function( DetectionGroup )
function( DetectionGroup )
self:__Detection( DetectDelay, DetectionGroup, DetectionTimeStamp ) -- Process each detection asynchronously. self:__Detection( DetectDelay, DetectionGroup, DetectionTimeStamp ) -- Process each detection asynchronously.
DetectDelay = DetectDelay + DetectionInterval DetectDelay = DetectDelay + DetectionInterval
end end )
)
self:__Detect( -self.RefreshTimeInterval ) self:__Detect( -self.RefreshTimeInterval )
@@ -594,7 +587,6 @@ do -- DETECTION_BASE
return self return self
end end
--- @param #DETECTION_BASE self --- @param #DETECTION_BASE self
-- @param #string From The From State string. -- @param #string From The From State string.
-- @param #string Event The Event string. -- @param #string Event The Event string.
@@ -675,8 +667,7 @@ do -- DETECTION_BASE
local Distance = ((DetectedObjectVec3.x - DetectionGroupVec3.x) ^ 2 + local Distance = ((DetectedObjectVec3.x - DetectionGroupVec3.x) ^ 2 +
(DetectedObjectVec3.y - DetectionGroupVec3.y) ^ 2 + (DetectedObjectVec3.y - DetectionGroupVec3.y) ^ 2 +
( DetectedObjectVec3.z - DetectionGroupVec3.z )^2 (DetectedObjectVec3.z - DetectionGroupVec3.z) ^ 2) ^ 0.5 / 1000
) ^ 0.5 / 1000
local DetectedUnitCategory = DetectedObject:getDesc().category local DetectedUnitCategory = DetectedObject:getDesc().category
@@ -714,7 +705,7 @@ do -- DETECTION_BASE
if self.RejectZones then if self.RejectZones then
for RejectZoneID, RejectZone in pairs( self.RejectZones ) do for RejectZoneID, RejectZone in pairs( self.RejectZones ) do
local RejectZone = RejectZone -- Core.Zone#ZONE_BASE local RejectZone = RejectZone -- Core.Zone#ZONE_BASE
if RejectZone:IsPointVec2InZone( DetectedObjectVec2 ) == true then if RejectZone:IsVec2InZone( DetectedObjectVec2 ) == true then
DetectionAccepted = false DetectionAccepted = false
end end
end end
@@ -759,7 +750,7 @@ do -- DETECTION_BASE
local ZoneProbability = ZoneData[2] -- #number local ZoneProbability = ZoneData[2] -- #number
ZoneProbability = ZoneProbability * 30 / 300 ZoneProbability = ZoneProbability * 30 / 300
if ZoneObject:IsPointVec2InZone( DetectedObjectVec2 ) == true then if ZoneObject:IsVec2InZone( DetectedObjectVec2 ) == true then
local Probability = math.random() -- Selects a number between 0 and 1 local Probability = math.random() -- Selects a number between 0 and 1
-- self:T( { Probability, ZoneProbability } ) -- self:T( { Probability, ZoneProbability } )
if Probability > ZoneProbability then if Probability > ZoneProbability then
@@ -854,10 +845,8 @@ do -- DETECTION_BASE
end end
end end
end end
end end
do -- DetectionItems Creation do -- DetectionItems Creation
@@ -906,7 +895,6 @@ do -- DETECTION_BASE
return self return self
end end
end end
do -- Initialization methods do -- Initialization methods
@@ -1151,7 +1139,6 @@ do -- DETECTION_BASE
return self return self
end end
--- Upon a **visual** detection, the higher the unit is during the detecting process, the more likely the detected unit is to be detected properly. --- Upon a **visual** detection, the higher the unit is during the detecting process, the more likely the detected unit is to be detected properly.
-- A detection at a 90% alpha angle is the most optimal, a detection at 10% is less and a detection at 0% is less likely to be correct. -- A detection at a 90% alpha angle is the most optimal, a detection at 10% is less and a detection at 0% is less likely to be correct.
-- --
@@ -1184,7 +1171,6 @@ do -- DETECTION_BASE
return self return self
end end
end end
do -- Change processing do -- Change processing
@@ -1221,7 +1207,6 @@ do -- DETECTION_BASE
return self return self
end end
--- Add a change to the detected zone. --- Add a change to the detected zone.
-- @param #DETECTION_BASE self -- @param #DETECTION_BASE self
-- @param #DETECTION_BASE.DetectedItem DetectedItem -- @param #DETECTION_BASE.DetectedItem DetectedItem
@@ -1315,7 +1300,7 @@ do -- DETECTION_BASE
return DetectedItem.FriendliesNearIntercept return DetectedItem.FriendliesNearIntercept
end end
--- Returns the distance used to identify friendlies near the deteted item ... --- Returns the distance used to identify friendlies near the detected item ...
-- @param #DETECTION_BASE self -- @param #DETECTION_BASE self
-- @param #DETECTION_BASE.DetectedItem DetectedItem The detected item. -- @param #DETECTION_BASE.DetectedItem DetectedItem The detected item.
-- @return #table A table of distances to friendlies. -- @return #table A table of distances to friendlies.
@@ -1327,7 +1312,7 @@ do -- DETECTION_BASE
--- Returns if there are friendlies nearby the FAC units ... --- Returns if there are friendlies nearby the FAC units ...
-- @param #DETECTION_BASE self -- @param #DETECTION_BASE self
-- @param #DETECTION_BASE.DetectedItem DetectedItem -- @param #DETECTION_BASE.DetectedItem DetectedItem
-- @return #boolean trhe if there are friendlies nearby -- @return #boolean true if there are friendlies nearby
function DETECTION_BASE:IsPlayersNearBy( DetectedItem ) function DETECTION_BASE:IsPlayersNearBy( DetectedItem )
return DetectedItem.PlayersNearBy ~= nil return DetectedItem.PlayersNearBy ~= nil
@@ -1366,7 +1351,6 @@ do -- DETECTION_BASE
point = InterceptCoord:GetVec3(), point = InterceptCoord:GetVec3(),
radius = self.FriendliesRange, radius = self.FriendliesRange,
} }
} }
--- @param DCS#Unit FoundDCSUnit --- @param DCS#Unit FoundDCSUnit
@@ -1465,8 +1449,7 @@ do -- DETECTION_BASE
end end
end end
end end
end end )
)
end end
self:F( { Friendlies = DetectedItem.FriendliesNearBy, Players = DetectedItem.PlayersNearBy } ) self:F( { Friendlies = DetectedItem.FriendliesNearBy, Players = DetectedItem.PlayersNearBy } )
@@ -1542,7 +1525,6 @@ do -- DETECTION_BASE
return nil return nil
end end
--- Gets a detected unit type name, taking into account the detection results. --- Gets a detected unit type name, taking into account the detection results.
-- @param #DETECTION_BASE self -- @param #DETECTION_BASE self
-- @param Wrapper.Unit#UNIT DetectedUnit -- @param Wrapper.Unit#UNIT DetectedUnit
@@ -1570,7 +1552,6 @@ do -- DETECTION_BASE
return "Undetected:" .. DetectedUnit:GetName() return "Undetected:" .. DetectedUnit:GetName()
end end
--- Adds a new DetectedItem to the DetectedItems list. --- Adds a new DetectedItem to the DetectedItems list.
-- The DetectedItem is a table and contains a SET_UNIT in the field Set. -- The DetectedItem is a table and contains a SET_UNIT in the field Set.
-- @param #DETECTION_BASE self -- @param #DETECTION_BASE self
@@ -1584,7 +1565,6 @@ do -- DETECTION_BASE
self.DetectedItemCount = self.DetectedItemCount + 1 self.DetectedItemCount = self.DetectedItemCount + 1
self.DetectedItemMax = self.DetectedItemMax + 1 self.DetectedItemMax = self.DetectedItemMax + 1
DetectedItemKey = DetectedItemKey or self.DetectedItemMax DetectedItemKey = DetectedItemKey or self.DetectedItemMax
self.DetectedItems[DetectedItemKey] = DetectedItem self.DetectedItems[DetectedItemKey] = DetectedItem
self.DetectedItemsByIndex[DetectedItemKey] = DetectedItem self.DetectedItemsByIndex[DetectedItemKey] = DetectedItem
@@ -1636,7 +1616,6 @@ do -- DETECTION_BASE
end end
end end
--- Get the DetectedItems by Key. --- Get the DetectedItems by Key.
-- This will return the DetectedItems collection, indexed by the Key, which can be any object that acts as the key of the detection. -- This will return the DetectedItems collection, indexed by the Key, which can be any object that acts as the key of the detection.
-- @param #DETECTION_BASE self -- @param #DETECTION_BASE self
@@ -1657,7 +1636,7 @@ do -- DETECTION_BASE
--- Get the amount of SETs with detected objects. --- Get the amount of SETs with detected objects.
-- @param #DETECTION_BASE self -- @param #DETECTION_BASE self
-- @return #number The amount of detected items. Note that the amount of detected items can differ with the reality, because detections are not real-time but doen in intervals! -- @return #number The amount of detected items. Note that the amount of detected items can differ with the reality, because detections are not real-time but done in intervals!
function DETECTION_BASE:GetDetectedItemsCount() function DETECTION_BASE:GetDetectedItemsCount()
local DetectedCount = self.DetectedItemCount local DetectedCount = self.DetectedItemCount
@@ -1719,7 +1698,7 @@ do -- DETECTION_BASE
return "" return ""
end end
--- Get the @{Core.Set#SET_UNIT} of a detecttion area using a given numeric index. --- Get the @{Core.Set#SET_UNIT} of a detection area using a given numeric index.
-- @param #DETECTION_BASE self -- @param #DETECTION_BASE self
-- @param #DETECTION_BASE.DetectedItem DetectedItem -- @param #DETECTION_BASE.DetectedItem DetectedItem
-- @return Core.Set#SET_UNIT DetectedSet -- @return Core.Set#SET_UNIT DetectedSet
@@ -1766,7 +1745,6 @@ do -- DETECTION_BASE
return DetectedItem.IsDetected return DetectedItem.IsDetected
end end
do -- Zones do -- Zones
--- Get the @{Core.Zone#ZONE_UNIT} of a detection area using a given numeric index. --- Get the @{Core.Zone#ZONE_UNIT} of a detection area using a given numeric index.
@@ -1800,7 +1778,6 @@ do -- DETECTION_BASE
return self return self
end end
--- Unlock the detected items when created and unlock all existing detected items. --- Unlock the detected items when created and unlock all existing detected items.
-- @param #DETECTION_BASE self -- @param #DETECTION_BASE self
-- @return #DETECTION_BASE -- @return #DETECTION_BASE
@@ -1824,7 +1801,6 @@ do -- DETECTION_BASE
end end
--- Lock a detected item. --- Lock a detected item.
-- @param #DETECTION_BASE self -- @param #DETECTION_BASE self
-- @param #DETECTION_BASE.DetectedItem DetectedItem The DetectedItem. -- @param #DETECTION_BASE.DetectedItem DetectedItem The DetectedItem.
@@ -1847,9 +1823,6 @@ do -- DETECTION_BASE
return self return self
end end
--- Set the detected item coordinate. --- Set the detected item coordinate.
-- @param #DETECTION_BASE self -- @param #DETECTION_BASE self
-- @param #DETECTION_BASE.DetectedItem DetectedItem The DetectedItem to set the coordinate at. -- @param #DETECTION_BASE.DetectedItem DetectedItem The DetectedItem to set the coordinate at.
@@ -1869,7 +1842,6 @@ do -- DETECTION_BASE
end end
end end
--- Get the detected item coordinate. --- Get the detected item coordinate.
-- @param #DETECTION_BASE self -- @param #DETECTION_BASE self
-- @param #DETECTION_BASE.DetectedItem DetectedItem The DetectedItem to set the coordinate at. -- @param #DETECTION_BASE.DetectedItem DetectedItem The DetectedItem to set the coordinate at.
@@ -1911,8 +1883,6 @@ do -- DETECTION_BASE
end end
end end
--- Get the detected item coordinate. --- Get the detected item coordinate.
-- @param #DETECTION_BASE self -- @param #DETECTION_BASE self
-- @param #DETECTION_BASE.DetectedItem DetectedItem The DetectedItem. -- @param #DETECTION_BASE.DetectedItem DetectedItem The DetectedItem.
@@ -1928,7 +1898,6 @@ do -- DETECTION_BASE
return nil, "" return nil, ""
end end
--- Report summary of a detected item using a given numeric index. --- Report summary of a detected item using a given numeric index.
-- @param #DETECTION_BASE self -- @param #DETECTION_BASE self
-- @param #DETECTION_BASE.DetectedItem DetectedItem The DetectedItem. -- @param #DETECTION_BASE.DetectedItem DetectedItem The DetectedItem.
@@ -1940,7 +1909,7 @@ do -- DETECTION_BASE
return nil return nil
end end
--- Report detailed of a detectedion result. --- Report detailed of a detection result.
-- @param #DETECTION_BASE self -- @param #DETECTION_BASE self
-- @param Wrapper.Group#GROUP AttackGroup The group to generate the report for. -- @param Wrapper.Group#GROUP AttackGroup The group to generate the report for.
-- @return #string -- @return #string
@@ -1987,8 +1956,6 @@ do -- DETECTION_BASE
end end
--- Schedule the DETECTION construction. --- Schedule the DETECTION construction.
-- @param #DETECTION_BASE self -- @param #DETECTION_BASE self
-- @param #number DelayTime The delay in seconds to wait the reporting. -- @param #number DelayTime The delay in seconds to wait the reporting.
@@ -2078,7 +2045,6 @@ do -- DETECTION_UNITS
end end
--- Create the DetectedItems list from the DetectedObjects table. --- Create the DetectedItems list from the DetectedObjects table.
-- For each DetectedItem, a one field array is created containing the Unit detected. -- For each DetectedItem, a one field array is created containing the Unit detected.
-- @param #DETECTION_UNITS self -- @param #DETECTION_UNITS self
@@ -2130,7 +2096,6 @@ do -- DETECTION_UNITS
end end
end end
-- Now we need to loop through the unidentified detected units and add these... These are all new items. -- Now we need to loop through the unidentified detected units and add these... These are all new items.
for DetectedUnitName, DetectedObjectData in pairs( self.DetectedObjects ) do for DetectedUnitName, DetectedObjectData in pairs( self.DetectedObjects ) do
@@ -2181,7 +2146,6 @@ do -- DETECTION_UNITS
end end
--- Report summary of a DetectedItem using a given numeric index. --- Report summary of a DetectedItem using a given numeric index.
-- @param #DETECTION_UNITS self -- @param #DETECTION_UNITS self
-- @param #DETECTION_BASE.DetectedItem DetectedItem The DetectedItem. -- @param #DETECTION_BASE.DetectedItem DetectedItem The DetectedItem.
@@ -2238,7 +2202,6 @@ do -- DETECTION_UNITS
return nil return nil
end end
--- Report detailed of a detection result. --- Report detailed of a detection result.
-- @param #DETECTION_UNITS self -- @param #DETECTION_UNITS self
-- @param Wrapper.Group#GROUP AttackGroup The group to generate the report for. -- @param Wrapper.Group#GROUP AttackGroup The group to generate the report for.
@@ -2332,7 +2295,6 @@ do -- DETECTION_TYPES
end end
--- Create the DetectedItems list from the DetectedObjects table. --- Create the DetectedItems list from the DetectedObjects table.
-- For each DetectedItem, a one field array is created containing the Unit detected. -- For each DetectedItem, a one field array is created containing the Unit detected.
-- @param #DETECTION_TYPES self -- @param #DETECTION_TYPES self
@@ -2371,7 +2333,6 @@ do -- DETECTION_TYPES
end end
end end
-- Now we need to loop through the unidentified detected units and add these... These are all new items. -- Now we need to loop through the unidentified detected units and add these... These are all new items.
for DetectedUnitName, DetectedObjectData in pairs( self.DetectedObjects ) do for DetectedUnitName, DetectedObjectData in pairs( self.DetectedObjects ) do
@@ -2395,8 +2356,6 @@ do -- DETECTION_TYPES
end end
end end
-- Check if there are any friendlies nearby. -- Check if there are any friendlies nearby.
for DetectedItemID, DetectedItemData in pairs( self.DetectedItems ) do for DetectedItemID, DetectedItemData in pairs( self.DetectedItems ) do
@@ -2413,8 +2372,6 @@ do -- DETECTION_TYPES
self:NearestRecce( DetectedItem ) self:NearestRecce( DetectedItem )
end end
end end
--- Report summary of a DetectedItem using a given numeric index. --- Report summary of a DetectedItem using a given numeric index.
@@ -2469,7 +2426,6 @@ do -- DETECTION_TYPES
end end
do -- DETECTION_AREAS do -- DETECTION_AREAS
--- @type DETECTION_AREAS --- @type DETECTION_AREAS
@@ -2484,14 +2440,14 @@ do -- DETECTION_AREAS
-- --
-- ## 4.1) Retrieve the Detected Unit Sets and Detected Zones -- ## 4.1) Retrieve the Detected Unit Sets and Detected Zones
-- --
-- The methods to manage the DetectedItems[].Set(s) are implemented in @{Functional.Detection#DECTECTION_BASE} and -- The methods to manage the DetectedItems[].Set(s) are implemented in @{Functional.Detection#DETECTION_BASE} and
-- the methods to manage the DetectedItems[].Zone(s) is implemented in @{Functional.Detection#DETECTION_AREAS}. -- the methods to manage the DetectedItems[].Zone(s) are implemented in @{Functional.Detection#DETECTION_AREAS}.
-- --
-- Retrieve the DetectedItems[].Set with the method @{Functional.Detection#DETECTION_BASE.GetDetectedSet}(). A @{Core.Set#SET_UNIT} object will be returned. -- Retrieve the DetectedItems[].Set with the method @{Functional.Detection#DETECTION_BASE.GetDetectedSet}(). A @{Core.Set#SET_UNIT} object will be returned.
-- --
-- Retrieve the formed @{Zone@ZONE_UNIT}s as a result of the grouping the detected units within the DetectionZoneRange, use the method @{Functional.Detection#DETECTION_BASE.GetDetectionZones}(). -- Retrieve the formed @{Zone@ZONE_UNIT}s as a result of the grouping the detected units within the DetectionZoneRange, use the method @{Functional.Detection#DETECTION_AREAS.GetDetectionZones}().
-- To understand the amount of zones created, use the method @{Functional.Detection#DETECTION_BASE.GetDetectionZoneCount}(). -- To understand the amount of zones created, use the method @{Functional.Detection#DETECTION_AREAS.GetDetectionZoneCount}().
-- If you want to obtain a specific zone from the DetectedZones, use the method @{Functional.Detection#DETECTION_BASE.GetDetectionZone}() with a given index. -- If you want to obtain a specific zone from the DetectedZones, use the method @{Functional.Detection#DETECTION_AREAS.GetDetectionZoneByID}() with a given index.
-- --
-- ## 4.4) Flare or Smoke detected units -- ## 4.4) Flare or Smoke detected units
-- --
@@ -2513,7 +2469,6 @@ do -- DETECTION_AREAS
DetectionZoneRange = nil, DetectionZoneRange = nil,
} }
--- DETECTION_AREAS constructor. --- DETECTION_AREAS constructor.
-- @param #DETECTION_AREAS self -- @param #DETECTION_AREAS self
-- @param Core.Set#SET_GROUP DetectionSetGroup The @{Set} of GROUPs in the Forward Air Controller role. -- @param Core.Set#SET_GROUP DetectionSetGroup The @{Set} of GROUPs in the Forward Air Controller role.
@@ -2535,6 +2490,48 @@ do -- DETECTION_AREAS
return self return self
end end
--- Retrieve set of detected zones.
-- @param #DETECTION_AREAS self
-- @return Core.Set#SET_ZONE The @{Set} of ZONE_UNIT objects detected.
function DETECTION_AREAS:GetDetectionZones()
local zoneset = SET_ZONE:New()
for _ID,_Item in pairs (self.DetectedItems) do
local item = _Item -- #DETECTION_BASE.DetectedItem
if item.Zone then
zoneset:AddZone(item.Zone)
end
end
return zoneset
end
--- Retrieve a specific zone by its ID (number)
-- @param #DETECTION_AREAS self
-- @param #number ID
-- @return Core.Zone#ZONE_UNIT The zone or nil if it does not exist
function DETECTION_AREAS:GetDetectionZoneByID(ID)
local zone = nil
for _ID,_Item in pairs (self.DetectedItems) do
local item = _Item -- #DETECTION_BASE.DetectedItem
if item.ID == ID then
zone = item.Zone
break
end
end
return zone
end
--- Retrieve number of detected zones.
-- @param #DETECTION_AREAS self
-- @return #number The number of zones.
function DETECTION_AREAS:GetDetectionZoneCount()
local zoneset = 0
for _ID,_Item in pairs (self.DetectedItems) do
if _Item.Zone then
zoneset = zoneset + 1
end
end
return zoneset
end
--- Report summary of a detected item using a given numeric index. --- Report summary of a detected item using a given numeric index.
-- @param #DETECTION_AREAS self -- @param #DETECTION_AREAS self
@@ -2593,7 +2590,6 @@ do -- DETECTION_AREAS
DetectedItemCoordText = DetectedItemCoordinate:ToStringA2G( AttackGroup, Settings ) DetectedItemCoordText = DetectedItemCoordinate:ToStringA2G( AttackGroup, Settings )
end end
local ThreatLevelA2G = self:GetDetectedItemThreatLevel( DetectedItem ) local ThreatLevelA2G = self:GetDetectedItemThreatLevel( DetectedItem )
local DetectedItemsCount = DetectedSet:Count() local DetectedItemsCount = DetectedSet:Count()
local DetectedItemsTypes = DetectedSet:GetTypeNames() local DetectedItemsTypes = DetectedSet:GetTypeNames()
@@ -2630,7 +2626,6 @@ do -- DETECTION_AREAS
return ReportText return ReportText
end end
--- Calculate the optimal intercept point of the DetectedItem. --- Calculate the optimal intercept point of the DetectedItem.
-- @param #DETECTION_AREAS self -- @param #DETECTION_AREAS self
-- @param #DETECTION_BASE.DetectedItem DetectedItem -- @param #DETECTION_BASE.DetectedItem DetectedItem
@@ -2655,8 +2650,6 @@ do -- DETECTION_AREAS
end end
--- Smoke the detected units --- Smoke the detected units
-- @param #DETECTION_AREAS self -- @param #DETECTION_AREAS self
-- @return #DETECTION_AREAS self -- @return #DETECTION_AREAS self
@@ -2760,13 +2753,11 @@ do -- DETECTION_AREAS
end end
--- Make a DetectionSet table. This function will be overridden in the derived classes.
--- Make a DetectionSet table. This function will be overridden in the derived clsses.
-- @param #DETECTION_AREAS self -- @param #DETECTION_AREAS self
-- @return #DETECTION_AREAS self -- @return #DETECTION_AREAS self
function DETECTION_AREAS:CreateDetectionItems() function DETECTION_AREAS:CreateDetectionItems()
self:F( "Checking Detected Items for new Detected Units ..." ) self:F( "Checking Detected Items for new Detected Units ..." )
-- self:F( { DetectedObjects = self.DetectedObjects } ) -- self:F( { DetectedObjects = self.DetectedObjects } )
@@ -2794,8 +2785,6 @@ do -- DETECTION_AREAS
-- self:IdentifyDetectedObject( DetectedZoneObject ) -- self:IdentifyDetectedObject( DetectedZoneObject )
AreaExists = true AreaExists = true
else else
-- The center object of the detected area has not been detected. Find an other unit of the set to become the center of the area. -- The center object of the detected area has not been detected. Find an other unit of the set to become the center of the area.
-- First remove the center unit from the set. -- First remove the center unit from the set.
@@ -2882,11 +2871,9 @@ do -- DETECTION_AREAS
end end
end end
-- We iterated through the existing detection areas and: -- We iterated through the existing detection areas and:
-- - We checked which units are still detected in each detection area. Those units were flagged as Identified. -- - We checked which units are still detected in each detection area. Those units were flagged as Identified.
-- - We recentered the detection area to new center units where it was needed. -- - We re-centered the detection area to new center units where it was needed.
-- --
-- Now we need to loop through the unidentified detected units and see where they belong: -- Now we need to loop through the unidentified detected units and see where they belong:
-- - They can be added to a new detection area and become the new center unit. -- - They can be added to a new detection area and become the new center unit.
@@ -2961,15 +2948,13 @@ do -- DETECTION_AREAS
self:SetDetectedItemThreatLevel( DetectedItem ) -- Calculate A2G threat level self:SetDetectedItemThreatLevel( DetectedItem ) -- Calculate A2G threat level
self:NearestRecce( DetectedItem ) self:NearestRecce( DetectedItem )
if DETECTION_AREAS._SmokeDetectedUnits or self._SmokeDetectedUnits then if DETECTION_AREAS._SmokeDetectedUnits or self._SmokeDetectedUnits then
DetectedZone.ZoneUNIT:SmokeRed() DetectedZone.ZoneUNIT:SmokeRed()
end end
-- DetectedSet:Flush( self ) -- DetectedSet:Flush( self )
DetectedSet:ForEachUnit( DetectedSet:ForEachUnit( --- @param Wrapper.Unit#UNIT DetectedUnit
--- @param Wrapper.Unit#UNIT DetectedUnit
function( DetectedUnit ) function( DetectedUnit )
if DetectedUnit:IsAlive() then if DetectedUnit:IsAlive() then
-- self:T( "Detected Set #" .. DetectedItem.ID .. ":" .. DetectedUnit:GetName() ) -- self:T( "Detected Set #" .. DetectedItem.ID .. ":" .. DetectedUnit:GetName() )
@@ -2980,8 +2965,7 @@ do -- DETECTION_AREAS
DetectedUnit:SmokeGreen() DetectedUnit:SmokeGreen()
end end
end end
end end )
)
if DETECTION_AREAS._FlareDetectedZones or self._FlareDetectedZones then if DETECTION_AREAS._FlareDetectedZones or self._FlareDetectedZones then
DetectedZone:FlareZone( SMOKECOLOR.White, 30, math.random( 0, 90 ) ) DetectedZone:FlareZone( SMOKECOLOR.White, 30, math.random( 0, 90 ) )
end end
@@ -2999,4 +2983,3 @@ do -- DETECTION_AREAS
end end

View File

@@ -1289,7 +1289,7 @@ end
--- Include all airports which lie in a zone as possible destinations. --- Include all airports which lie in a zone as possible destinations.
-- @param #RAT self -- @param #RAT self
-- @param Core.Zone#ZONE zone Zone in which the departure airports lie. Has to be a MOOSE zone. -- @param Core.Zone#ZONE zone Zone in which the destination airports lie. Has to be a MOOSE zone.
-- @return #RAT RAT self object. -- @return #RAT RAT self object.
function RAT:SetDestinationsFromZone(zone) function RAT:SetDestinationsFromZone(zone)
self:F2(zone) self:F2(zone)
@@ -1305,7 +1305,7 @@ end
--- Include all airports which lie in a zone as possible destinations. --- Include all airports which lie in a zone as possible destinations.
-- @param #RAT self -- @param #RAT self
-- @param Core.Zone#ZONE zone Zone in which the destination airports lie. Has to be a MOOSE zone. -- @param Core.Zone#ZONE zone Zone in which the departure airports lie. Has to be a MOOSE zone.
-- @return #RAT RAT self object. -- @return #RAT RAT self object.
function RAT:SetDeparturesFromZone(zone) function RAT:SetDeparturesFromZone(zone)
self:F2(zone) self:F2(zone)

View File

@@ -91,13 +91,16 @@
-- @field #boolean trackmissiles If true (default), all missile types are tracked and impact point to closest bombing target is evaluated. -- @field #boolean trackmissiles If true (default), all missile types are tracked and impact point to closest bombing target is evaluated.
-- @field #boolean defaultsmokebomb If true, initialize player settings to smoke bomb. -- @field #boolean defaultsmokebomb If true, initialize player settings to smoke bomb.
-- @field #boolean autosave If true, automatically save results every X seconds. -- @field #boolean autosave If true, automatically save results every X seconds.
-- @field #number instructorfreq Frequency on which the range control transmits. -- @field #number instructorfreq Frequency on which the range control transmitts.
-- @field Sound.RadioQueue#RADIOQUEUE instructor Instructor radio queue. -- @field Sound.RadioQueue#RADIOQUEUE instructor Instructor radio queue.
-- @field #number rangecontrolfreq Frequency on which the range control transmits. -- @field #number rangecontrolfreq Frequency on which the range control transmitts.
-- @field Sound.RadioQueue#RADIOQUEUE rangecontrol Range control radio queue. -- @field Sound.RadioQueue#RADIOQUEUE rangecontrol Range control radio queue.
-- @field #string rangecontrolrelayname Name of relay unit. -- @field #string rangecontrolrelayname Name of relay unit.
-- @field #string instructorrelayname 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/". -- @field #string soundpath Path inside miz file where the sound files are located. Default is "Range Soundfiles/".
-- @field #boolean targetsheet If true, players can save their target sheets. Rangeboss will not work if targetsheets do not save.
-- @field #string targetpath Path where to save the target sheets.
-- @field #string targetprefix File prefix for target sheet files.
-- @extends Core.Fsm#FSM -- @extends Core.Fsm#FSM
--- *Don't only practice your art, but force your way into its secrets; art deserves that, for it and knowledge can raise man to the Divine.* - Ludwig van Beethoven --- *Don't only practice your art, but force your way into its secrets; art deserves that, for it and knowledge can raise man to the Divine.* - Ludwig van Beethoven
@@ -121,7 +124,7 @@
-- --
-- Due to a DCS bug, it is not possible to directly monitor when a player enters a plane. So in a mission with client slots, it is vital that -- Due to a DCS bug, it is not possible to directly monitor when a player enters a plane. So in a mission with client slots, it is vital that
-- a player first enters as spectator or hits ESC twice and **after that** jumps into the slot of his aircraft! -- a player first enters as spectator or hits ESC twice and **after that** jumps into the slot of his aircraft!
-- If that is not done, the script is not started correctly. This can be checked by looking at the radio menus. If the mission was entered correctly, -- If that is not done, the script is not started correctly. This can be checked by looking at the radio menues. If the mission was entered correctly,
-- there should be an "On the Range" menu items in the "F10. Other..." menu. -- there should be an "On the Range" menu items in the "F10. Other..." menu.
-- --
-- # Strafe Pits -- # Strafe Pits
@@ -151,7 +154,7 @@
-- --
-- * The first parameter *targetnames* defines the target or targets. This can be a single item or a Table with the name(s) of @{Wrapper.Unit} or @{Static} objects defined in the mission editor. -- * The first parameter *targetnames* defines the target or targets. This can be a single item or a Table with the name(s) of @{Wrapper.Unit} or @{Static} objects defined in the mission editor.
-- * The (optional) parameter *goodhitrange* specifies the radius in metres around the target within which a bomb/rocket hit is considered to be "good". -- * The (optional) parameter *goodhitrange* specifies the radius in metres around the target within which a bomb/rocket hit is considered to be "good".
-- * If final (optional) parameter *randommove* can be enabled to create moving targets. If this parameter is set to true, the units of this bombing target will randomly move within the range zone. -- * If final (optional) parameter "*randommove*" can be enabled to create moving targets. If this parameter is set to true, the units of this bombing target will randomly move within the range zone.
-- Note that there might be quirks since DCS units can get stuck in buildings etc. So it might be safer to manually define a route for the units in the mission editor if moving targets are desired. -- Note that there might be quirks since DCS units can get stuck in buildings etc. So it might be safer to manually define a route for the units in the mission editor if moving targets are desired.
-- --
-- ## Adding Groups -- ## Adding Groups
@@ -260,11 +263,12 @@
-- -- Add bombing targets. A good hit is if the bomb falls less then 50 m from the target. -- -- Add bombing targets. A good hit is if the bomb falls less then 50 m from the target.
-- GoldwaterRange:AddBombingTargets(bombtargets, 50) -- GoldwaterRange:AddBombingTargets(bombtargets, 50)
-- --
-- -- Start Range. -- -- Start range.
-- GoldwaterRange:Start() -- GoldwaterRange:Start()
-- --
-- The [476th - Air Weapons Range Objects mod](http://www.476vfightergroup.com/downloads.php?do=file&id=287) is (implicitly) used in this example. -- The [476th - Air Weapons Range Objects mod](http://www.476vfightergroup.com/downloads.php?do=file&id=287) is (implicitly) used in this example.
-- --
--
-- # Debugging -- # Debugging
-- --
-- In case you have problems, it is always a good idea to have a look at your DCS log file. You find it in your "Saved Games" folder, so for example in -- In case you have problems, it is always a good idea to have a look at your DCS log file. You find it in your "Saved Games" folder, so for example in
@@ -330,22 +334,25 @@ RANGE = {
rangecontrolfreq = nil, rangecontrolfreq = nil,
rangecontrol = nil, rangecontrol = nil,
soundpath = "Range Soundfiles/", soundpath = "Range Soundfiles/",
targetsheet = nil,
targetpath = nil,
targetprefix = nil,
} }
--- Default range parameters. --- Default range parameters.
-- @list Defaults -- @list Defaults
RANGE.Defaults = { RANGE.Defaults = {
goodhitrange = 25, -- meters goodhitrange = 25,
strafemaxalt = 914, -- meters AGL strafemaxalt = 914,
dtBombtrack = 0.005, -- seconds dtBombtrack = 0.005,
Tmsg = 30, -- seconds Tmsg = 30,
ndisplayresult = 10, ndisplayresult = 10,
rangeradius = 5000, -- meters rangeradius = 5000,
TdelaySmoke = 3.0, -- seconds TdelaySmoke = 3.0,
boxlength = 3000, -- meters boxlength = 3000,
boxwidth = 300, -- meters boxwidth = 300,
goodpass = 20, -- targethits per pass goodpass = 20,
foulline = 610, -- meters foulline = 610
} }
--- Target type, i.e. unit, static, or coordinate. --- Target type, i.e. unit, static, or coordinate.
@@ -356,9 +363,17 @@ RANGE.Defaults = {
RANGE.TargetType = { RANGE.TargetType = {
UNIT = "Unit", UNIT = "Unit",
STATIC = "Static", STATIC = "Static",
COORD = "Coordinate", COORD = "Coordinate"
} }
--- Default range variables for RangeBoss/Hypeman tie in.
hypemanStrafeRollIn = "nil"
StrafeAircraftType = "strafeAircraftTypeNotSet"
Straferesult = {}
clientRollingIn = false
clientStrafed = false
invalidStrafe = false
--- Player settings. --- Player settings.
-- @type RANGE.PlayerData -- @type RANGE.PlayerData
-- @field #boolean smokebombimpact Smoke bomb impact points. -- @field #boolean smokebombimpact Smoke bomb impact points.
@@ -869,6 +884,22 @@ function RANGE:SetAutosaveOff()
return self return self
end end
--- Enable saving of player's target sheets and specify an optional directory path.
-- @param #RANGE self
-- @param #string path (Optional) Path where to save the target sheets.
-- @param #string prefix (Optional) Prefix for target sheet files. File name will be saved as *prefix_aircrafttype-0001.csv*, *prefix_aircrafttype-0002.csv*, etc.
-- @return #RANGE self
function RANGE:SetTargetSheet( path, prefix )
if io then
self.targetsheet = true
self.targetpath = path
self.targetprefix = prefix
else
self:E( self.lid .. "ERROR: io is not desanitized. Cannot save target sheet." )
end
return self
end
--- Set messages to examiner. The examiner will receive messages from all clients. --- Set messages to examiner. The examiner will receive messages from all clients.
-- @param #RANGE self -- @param #RANGE self
-- @param #string examinergroupname Name of the group of the examiner. -- @param #string examinergroupname Name of the group of the examiner.
@@ -900,10 +931,10 @@ end
--- Set player setting whether bomb impact points are smoked or not. --- Set player setting whether bomb impact points are smoked or not.
-- @param #RANGE self -- @param #RANGE self
-- @param #boolean switch (Optional) If true, impact points of bombs will be smoked. Default is true. -- @param #boolean switch If true nor nil default is to smoke impact points of bombs.
-- @return #RANGE self -- @return #RANGE self
function RANGE:SetDefaultPlayerSmokeBomb( switch ) function RANGE:SetDefaultPlayerSmokeBomb( switch )
if switch == nil or switch == true then if switch == true or switch == nil then
self.defaultsmokebomb = true self.defaultsmokebomb = true
else else
self.defaultsmokebomb = false self.defaultsmokebomb = false
@@ -1183,7 +1214,7 @@ function RANGE:AddStrafePit( targetnames, boxlength, boxwidth, heading, inverseh
if heading < 0 then if heading < 0 then
heading = heading + 360 heading = heading + 360
end end
if heading >= 360 then if heading > 360 then
heading = heading - 360 heading = heading - 360
end end
@@ -1246,7 +1277,7 @@ end
-- @param #number boxlength (Optional) Length of the approach box in meters. Default is 3000 m. -- @param #number boxlength (Optional) Length of the approach box in meters. Default is 3000 m.
-- @param #number boxwidth (Optional) Width of the approach box in meters. Default is 300 m. -- @param #number boxwidth (Optional) Width of the approach box in meters. Default is 300 m.
-- @param #number heading (Optional) Approach heading in Degrees. Default is heading of the unit as defined in the mission editor. -- @param #number heading (Optional) Approach heading in Degrees. Default is heading of the unit as defined in the mission editor.
-- @param #boolean inverseheading (Optional) Use inverse heading (heading --> heading - 180 Degrees). Default is false. -- @param #boolean inverseheading (Optional) Take inverse heading (heading --> heading - 180 Degrees). Default is false.
-- @param #number goodpass (Optional) Number of hits for a "good" strafing pass. Default is 20. -- @param #number goodpass (Optional) Number of hits for a "good" strafing pass. Default is 20.
-- @param #number foulline (Optional) Foul line distance. Hits from closer than this distance are not counted. Default 610 m = 2000 ft. Set to 0 for no foul line. -- @param #number foulline (Optional) Foul line distance. Hits from closer than this distance are not counted. Default 610 m = 2000 ft. Set to 0 for no foul line.
-- @return #RANGE self -- @return #RANGE self
@@ -1281,8 +1312,8 @@ end
--- Add bombing target(s) to range. --- Add bombing target(s) to range.
-- @param #RANGE self -- @param #RANGE self
-- @param #table targetnames Single or multiple (Table) names of unit or static objects serving as bomb targets. -- @param #table targetnames Single or multiple (Table) names of unit or static objects serving as bomb targets.
-- @param #number goodhitrange (Optional) Max hit distance from target unit in meters which is considered as a good hit. Default is 25 m. -- @param #number goodhitrange (Optional) Max distance from target unit (in meters) which is considered as a good hit. Default is 25 m.
-- @param #boolean randommove (Optional) If true, unit will move randomly within the range. Default is false. -- @param #boolean randommove If true, unit will move randomly within the range. Default is false.
-- @return #RANGE self -- @return #RANGE self
function RANGE:AddBombingTargets( targetnames, goodhitrange, randommove ) function RANGE:AddBombingTargets( targetnames, goodhitrange, randommove )
self:F( { targetnames = targetnames, goodhitrange = goodhitrange, randommove = randommove } ) self:F( { targetnames = targetnames, goodhitrange = goodhitrange, randommove = randommove } )
@@ -1320,8 +1351,8 @@ end
--- Add a unit or static object as bombing target. --- Add a unit or static object as bombing target.
-- @param #RANGE self -- @param #RANGE self
-- @param Wrapper.Positionable#POSITIONABLE unit Positionable (unit or static) of the strafe target. -- @param Wrapper.Positionable#POSITIONABLE unit Positionable (unit or static) of the strafe target.
-- @param #number goodhitrange (Optional) Max hit distance from target unit in meters which is considered as a good hit. Default is 25 m. -- @param #number goodhitrange Max distance from unit which is considered as a good hit.
-- @param #boolean randommove (Optional) If true, unit will move randomly within the range. Default is false. -- @param #boolean randommove If true, unit will move randomly within the range. Default is false.
-- @return #RANGE self -- @return #RANGE self
function RANGE:AddBombingTargetUnit( unit, goodhitrange, randommove ) function RANGE:AddBombingTargetUnit( unit, goodhitrange, randommove )
self:F( { unit = unit, goodhitrange = goodhitrange, randommove = randommove } ) self:F( { unit = unit, goodhitrange = goodhitrange, randommove = randommove } )
@@ -1374,25 +1405,12 @@ function RANGE:AddBombingTargetUnit( unit, goodhitrange, randommove )
return self return self
end end
--- Add a coordinate of a bombing target. --- Add a coordinate of a bombing target. This
-- @param #RANGE self -- @param #RANGE self
-- @param Core.Point#COORDINATE coord The coordinate. -- @param Core.Point#COORDINATE coord The coordinate.
-- @param #string name (Optional) Name of target. Default is "Bomb Target". -- @param #string name Name of target.
-- @param #number goodhitrange (Optional) Max hit distance from target unit in meters which is considered as a good hit. Default is 25 m. -- @param #number goodhitrange Max distance from unit which is considered as a good hit.
-- @return #RANGE self -- @return #RANGE self
-- @usage
--
-- -- Setup a Range
-- RangeOne = RANGE:New( "Range One" )
-- -- Find the STATIC target object as setup in the ME.
-- RangeOneBombTarget = STATIC:FindByName( "RangeOneBombTarget" )
-- -- Add the coordinate of the STATIC target object as a bomb target (thus keeping the bomb function active, even if the STATIC target is destroyed).
-- RangeOne:AddBombingTargetCoordinate( RangeOneBombTarget:GetCoordinate(), "RangeOneBombTarget", 50)
-- -- Or, add the coordinate of the STATIC target object as a bomb target using default values (name will be "Bomb Target", goodhitrange will be 25 m).
-- RangeOne:AddBombingTargetCoordinate( RangeOneBombTarget:GetCoordinate() )
-- -- Start Range.
-- RangeOne:Start()
--
function RANGE:AddBombingTargetCoordinate( coord, name, goodhitrange ) function RANGE:AddBombingTargetCoordinate( coord, name, goodhitrange )
local target = {} -- #RANGE.BombTarget local target = {} -- #RANGE.BombTarget
@@ -1413,8 +1431,8 @@ end
--- Add all units of a group as bombing targets. --- Add all units of a group as bombing targets.
-- @param #RANGE self -- @param #RANGE self
-- @param Wrapper.Group#GROUP group Group of bombing targets. -- @param Wrapper.Group#GROUP group Group of bombing targets.
-- @param #number goodhitrange (Optional) Max hit distance from target unit in meters which is considered as a good hit. Default is 25 m. -- @param #number goodhitrange Max distance from unit which is considered as a good hit.
-- @param #boolean randommove (Optional) If true, unit will move randomly within the range. Default is false. -- @param #boolean randommove If true, unit will move randomly within the range. Default is false.
-- @return #RANGE self -- @return #RANGE self
function RANGE:AddBombingTargetGroup( group, goodhitrange, randommove ) function RANGE:AddBombingTargetGroup( group, goodhitrange, randommove )
self:F( { group = group, goodhitrange = goodhitrange, randommove = randommove } ) self:F( { group = group, goodhitrange = goodhitrange, randommove = randommove } )
@@ -1433,22 +1451,11 @@ function RANGE:AddBombingTargetGroup( group, goodhitrange, randommove )
return self return self
end end
--- Returns the foul line distance between strafe pit target and a foul line distance marker object. --- Measures the foule line distance between two unit or static objects.
-- @param #RANGE self -- @param #RANGE self
-- @param #string namepit Name of the strafe pit target object. -- @param #string namepit Name of the strafe pit target object.
-- @param #string namefoulline Name of the foul line distance marker object. -- @param #string namefoulline Name of the fould line distance marker object.
-- @return #number Foul line distance in meters. -- @return #number Foul line distance in meters.
-- @usage
--
-- -- Setup a Range
-- RangeOne = RANGE:New( "Range One" )
-- -- Get distance between strafe target objext and foul line distance marker object.
-- RangeOneFoulDistance = RangeOne:GetFoullineDistance( "RangeOneStrafeTarget" , "RangeOneFoulLineObject" )
-- -- Add a strafe pit using the measured foul line distance. Where nil is used, strafe pit default values will be used - adjust as required.
-- RangeOne:AddStrafePit( "RangeOneStrafeTarget", nil, nil, nil, nil, nil, RangeOneFoulDistance )
-- -- Start Range.
-- RangeOne:Start()
--
function RANGE:GetFoullineDistance( namepit, namefoulline ) function RANGE:GetFoullineDistance( namepit, namefoulline )
self:F( { namepit = namepit, namefoulline = namefoulline } ) self:F( { namepit = namepit, namefoulline = namefoulline } )
@@ -1573,6 +1580,7 @@ function RANGE:OnEventBirth( EventData )
self:T3( self.id .. "BIRTH: player = " .. tostring( _playername ) ) self:T3( self.id .. "BIRTH: player = " .. tostring( _playername ) )
if _unit and _playername then if _unit and _playername then
local _uid = _unit:GetID() local _uid = _unit:GetID()
local _group = _unit:GetGroup() local _group = _unit:GetGroup()
local _gid = _group:GetID() local _gid = _group:GetID()
@@ -1609,9 +1617,9 @@ function RANGE:OnEventBirth( EventData )
self.timerCheckZone = TIMER:New( self._CheckInZone, self, EventData.IniUnitName ):Start( 1, 1 ) self.timerCheckZone = TIMER:New( self._CheckInZone, self, EventData.IniUnitName ):Start( 1, 1 )
self.planes[_uid] = true self.planes[_uid] = true
end end
end
end end
end
--- Range event handler for event hit. --- Range event handler for event hit.
-- @param #RANGE self -- @param #RANGE self
@@ -1672,6 +1680,7 @@ function RANGE:OnEventHit( EventData )
self:_DisplayMessageToGroup( _unit, text ) self:_DisplayMessageToGroup( _unit, text )
self:T2( self.id .. text ) self:T2( self.id .. text )
_currentTarget.pastfoulline = true _currentTarget.pastfoulline = true
invalidStrafe = true -- Rangeboss Edit
end end
end end
@@ -1701,7 +1710,6 @@ function RANGE:OnEventHit( EventData )
end end
end end
end end
end end
--- Range event handler for event shot (when a unit releases a rocket or bomb (but not a fast firing gun). --- Range event handler for event shot (when a unit releases a rocket or bomb (but not a fast firing gun).
@@ -1762,6 +1770,7 @@ function RANGE:OnEventShot( EventData )
-- Only track if distance player to range is < 25 km. Also check that a player shot. No need to track AI weapons. -- Only track if distance player to range is < 25 km. Also check that a player shot. No need to track AI weapons.
if _track and dPR <= self.BombtrackThreshold and _unit and _playername then if _track and dPR <= self.BombtrackThreshold and _unit and _playername then
-- Player data. -- Player data.
local playerData = self.PlayerSettings[_playername] -- #RANGE.PlayerData local playerData = self.PlayerSettings[_playername] -- #RANGE.PlayerData
@@ -1843,7 +1852,9 @@ function RANGE:OnEventShot( EventData )
_distance = _temp _distance = _temp
_closetTarget = _bombtarget _closetTarget = _bombtarget
_closeCoord = targetcoord _closeCoord = targetcoord
if _distance <= 0.5 * _bombtarget.goodhitrange then if _distance <= 1.53 then -- Rangeboss Edit
_hitquality = "SHACK" -- Rangeboss Edit
elseif _distance <= 0.5 * _bombtarget.goodhitrange then -- Rangeboss Edit
_hitquality = "EXCELLENT" _hitquality = "EXCELLENT"
elseif _distance <= _bombtarget.goodhitrange then elseif _distance <= _bombtarget.goodhitrange then
_hitquality = "GOOD" _hitquality = "GOOD"
@@ -1876,6 +1887,10 @@ function RANGE:OnEventShot( EventData )
result.player = playerData.playername result.player = playerData.playername
result.time = timer.getAbsTime() result.time = timer.getAbsTime()
result.airframe = playerData.airframe result.airframe = playerData.airframe
result.roundsFired = 0 -- Rangeboss Edit
result.roundsHit = 0 -- Rangeboss Edit
result.roundsQuality = "N/A" -- Rangeboss Edit
result.rangename = self.rangename
-- Add to table. -- Add to table.
table.insert( _results, result ) table.insert( _results, result )
@@ -1916,6 +1931,74 @@ end
----------------------------------------------------------------------------------------------------------------------------------------------------------------------- -----------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- FSM Functions -- FSM Functions
----------------------------------------------------------------------------------------------------------------------------------------------------------------------- -----------------------------------------------------------------------------------------------------------------------------------------------------------------------
function RANGE:_SaveTargetSheet( _playername, result ) -- RangeBoss Specific Function
--- Function that saves data to file
local function _savefile( filename, data )
local f = io.open( filename, "wb" )
if f then
f:write( data )
f:close()
else
env.info( "RANGEBOSS EDIT - could not save target sheet to file" )
-- self:E(self.lid..string.format("ERROR: could not save target sheet to file %s.\nFile may contain invalid characters.", tostring(filename)))
end
end
-- Set path or default.
local path = self.targetpath
if lfs then
path = path or lfs.writedir() .. [[Logs\]]
end
-- Create unused file name.
local filename = nil
for i = 1, 9999 do
-- Create file name
if self.targetprefix then
filename = string.format( "%s_%s-%04d.csv", self.targetprefix, playerData.actype, i )
else
local name = UTILS.ReplaceIllegalCharacters( _playername, "_" )
filename = string.format( "RANGERESULTS-%s_Targetsheet-%s-%04d.csv", self.rangename, name, i )
end
-- Set path.
if path ~= nil then
filename = path .. "\\" .. filename
end
-- Check if file exists.
local _exists = UTILS.FileExists( filename )
if not _exists then
break
end
end
-- Header line
local data = "Name,Target,Distance,Radial,Quality,Rounds Fired,Rounds Hit,Rounds Quality,Attack Heading,Weapon,Airframe,Mission Time,OS Time\n"
-- local result=_result --#RANGE.BombResult
local distance = result.distance
local weapon = result.weapon
local target = result.name
local radial = result.radial
local quality = result.quality
local time = UTILS.SecondsToClock( result.time )
local airframe = result.airframe
local date = "n/a"
local roundsFired = result.roundsFired
local roundsHit = result.roundsHit
local strafeResult = result.roundsQuality
local attackHeading = result.heading
if os then
date = os.date()
end
data = data .. string.format( "%s,%s,%.2f,%03d,%s,%03d,%03d,%s,%03d,%s,%s,%s,%s", _playername, target, distance, radial, quality, roundsFired, roundsHit, strafeResult, attackHeading, weapon, airframe, time, date )
-- Save file.
_savefile( filename, data )
end
--- Check spawn queue and spawn aircraft if necessary. --- Check spawn queue and spawn aircraft if necessary.
-- @param #RANGE self -- @param #RANGE self
@@ -2012,11 +2095,16 @@ end
-- @param #RANGE.PlayerData player Player data table. -- @param #RANGE.PlayerData player Player data table.
function RANGE:onafterImpact( From, Event, To, result, player ) function RANGE:onafterImpact( From, Event, To, result, player )
-- Send message to player.
local text = string.format( "%s, impact %03d° for %d m (%d ft)", player.playername, result.radial, result.distance, UTILS.MetersToFeet( result.distance ) )
-- Only display target name if there is more than one bomb target. -- Only display target name if there is more than one bomb target.
local targetname = nil
if #self.bombingTargets > 1 then if #self.bombingTargets > 1 then
text = text .. string.format( " from bulls of target %s.", result.name ) local targetname = result.name
end
-- Send message to player.
local text = string.format( "%s, impact %03d° for %d ft", player.playername, result.radial, UTILS.MetersToFeet( result.distance ) )
if targetname then
text = text .. string.format( " from bulls of target %s." )
else else
text = text .. "." text = text .. "."
end end
@@ -2120,7 +2208,7 @@ function RANGE:onafterSave( From, Event, To )
_savefile( filename, scores ) _savefile( filename, scores )
end end
--- Function called before load event. Checks that io and lfs are desanitized. --- Function called before save event. Checks that io and lfs are desanitized.
-- @param #RANGE self -- @param #RANGE self
-- @param #string From From state. -- @param #string From From state.
-- @param #string Event Event. -- @param #string Event Event.
@@ -2489,7 +2577,7 @@ function RANGE:_DisplayRangeInfo( _unitname )
local range = coord:Get2DDistance( position ) local range = coord:Get2DDistance( position )
-- Bearing string. -- Bearing string.
local Bs = string.format( "%03d°", angle ) local Bs = string.format( '%03d°', angle )
local texthit local texthit
if self.PlayerSettings[playername].flaredirecthits then if self.PlayerSettings[playername].flaredirecthits then
@@ -2583,7 +2671,7 @@ function RANGE:_DisplayBombTargets( _unitname )
end end
end end
self:_DisplayMessageToGroup( _unit, _text, 60, true, true ) self:_DisplayMessageToGroup( _unit, _text, 120, true, true )
end end
end end
@@ -2656,7 +2744,7 @@ function RANGE:_DisplayRangeWeather( _unitname )
-- Get Beaufort wind scale. -- Get Beaufort wind scale.
local Bn, Bd = UTILS.BeaufortScale( Ws ) local Bn, Bd = UTILS.BeaufortScale( Ws )
local WD = string.format( "%03d°", Wd ) local WD = string.format( '%03d°', Wd )
local Ts = string.format( "%d°C", T ) local Ts = string.format( "%d°C", T )
local hPa2inHg = 0.0295299830714 local hPa2inHg = 0.0295299830714
@@ -2744,6 +2832,7 @@ function RANGE:_CheckInZone( _unitName )
-- Get player unit and name. -- Get player unit and name.
local _unit, _playername = self:_GetPlayerUnitAndName( _unitName ) local _unit, _playername = self:_GetPlayerUnitAndName( _unitName )
local unitheading = 0 -- RangeBoss
if _unit and _playername then if _unit and _playername then
@@ -2753,6 +2842,7 @@ function RANGE:_CheckInZone( _unitName )
-- Heading check. -- Heading check.
local unitheading = _unit:GetHeading() local unitheading = _unit:GetHeading()
unitheadingStrafe = _unit:GetHeading() -- RangeBoss
local pitheading = targetheading - 180 local pitheading = targetheading - 180
local deltaheading = unitheading - pitheading local deltaheading = unitheading - pitheading
local towardspit = math.abs( deltaheading ) <= 90 or math.abs( deltaheading - 360 ) <= 90 local towardspit = math.abs( deltaheading ) <= 90 or math.abs( deltaheading - 360 ) <= 90
@@ -2789,7 +2879,7 @@ function RANGE:_CheckInZone( _unitName )
-- Check if player is in strafe zone and below max alt. -- Check if player is in strafe zone and below max alt.
if unitinzone then if unitinzone then
StrafeAircraftType = _unit:GetTypeName() -- RangeBoss
-- Still in zone, keep counting hits. Increase counter. -- Still in zone, keep counting hits. Increase counter.
_currentStrafeRun.time = _currentStrafeRun.time + 1 _currentStrafeRun.time = _currentStrafeRun.time + 1
@@ -2821,7 +2911,7 @@ function RANGE:_CheckInZone( _unitName )
-- Result. -- Result.
local _result = self.strafeStatus[_unitID] local _result = self.strafeStatus[_unitID]
local _sound = nil -- #RANGE.Soundfile local _sound = nil -- #RANGE.Soundfile
--[[ --RangeBoss commented out in order to implement strafe quality based on accuracy percentage, not the number of rounds on target
-- Judge this pass. Text is displayed on summary. -- Judge this pass. Text is displayed on summary.
if _result.hits >= _result.zone.goodPass*2 then if _result.hits >= _result.zone.goodPass*2 then
_result.text = "EXCELLENT PASS" _result.text = "EXCELLENT PASS"
@@ -2836,7 +2926,7 @@ function RANGE:_CheckInZone( _unitName )
_result.text = "POOR PASS" _result.text = "POOR PASS"
_sound=RANGE.Sound.RCPoorPass _sound=RANGE.Sound.RCPoorPass
end end
]]
-- Calculate accuracy of run. Number of hits wrt number of rounds fired. -- Calculate accuracy of run. Number of hits wrt number of rounds fired.
local shots = _result.ammo - _ammo local shots = _result.ammo - _ammo
local accur = 0 local accur = 0
@@ -2847,6 +2937,29 @@ function RANGE:_CheckInZone( _unitName )
end end
end end
if invalidStrafe == true then --
_result.text = "* INVALID - PASSED FOUL LINE *"
_sound = RANGE.Sound.RCPoorPass --
else
if accur >= 90 then
_result.text = "DEADEYE PASS"
_sound = RANGE.Sound.RCExcellentPass
elseif accur >= 75 then
_result.text = "EXCELLENT PASS"
_sound = RANGE.Sound.RCExcellentPass
elseif accur >= 50 then
_result.text = "GOOD PASS"
_sound = RANGE.Sound.RCGoodPass
elseif accur >= 25 then
_result.text = "INEFFECTIVE PASS"
_sound = RANGE.Sound.RCIneffectivePass
else
_result.text = "POOR PASS"
_sound = RANGE.Sound.RCPoorPass
end
end
clientStrafed = true -- RANGEBOSS
-- Message text. -- Message text.
local _text = string.format( "%s, hits on target %s: %d", self:_myname( _unitName ), _result.zone.name, _result.hits ) local _text = string.format( "%s, hits on target %s: %d", self:_myname( _unitName ), _result.zone.name, _result.hits )
if shots and accur then if shots and accur then
@@ -2857,6 +2970,45 @@ function RANGE:_CheckInZone( _unitName )
-- Send message. -- Send message.
self:_DisplayMessageToGroup( _unit, _text ) self:_DisplayMessageToGroup( _unit, _text )
-- RangeBoss Edit for strafe table insert
-- Local results.
local result = {} -- #RANGE.BombResult
result.name = _result.zone.name or "unknown"
result.distance = 0
result.radial = 0
result.weapon = "N/A"
result.quality = "N/A"
result.player = _playernamee
result.time = timer.getAbsTime()
result.airframe = StrafeAircraftType
result.roundsFired = shots -- RANGEBOSS
result.roundsHit = _result.hits -- RANGEBOSS
result.roundsQuality = _result.text -- RANGEBOSS
result.strafeAccuracy = accur
result.heading = unitheadingStrafe -- RANGEBOSS
Straferesult.name = _result.zone.name or "unknown"
Straferesult.distance = 0
Straferesult.radial = 0
Straferesult.weapon = "N/A"
Straferesult.quality = "N/A"
Straferesult.player = _playername
Straferesult.time = timer.getAbsTime()
Straferesult.airframe = StrafeAircraftType
Straferesult.roundsFired = shots
Straferesult.roundsHit = _result.hits
Straferesult.roundsQuality = _result.text
Straferesult.strafeAccuracy = accur
Straferesult.rangename = self.rangename
-- Save trap sheet.
if playerData.targeton and self.targetsheet then
self:_SaveTargetSheet( _playername, result )
end
-- RangeBoss edit for strafe data saved to file
-- Voice over. -- Voice over.
if self.rangecontrol then if self.rangecontrol then
self.rangecontrol:NewTransmission( RANGE.Sound.RCHitsOnTarget.filename, RANGE.Sound.RCHitsOnTarget.duration, self.soundpath ) self.rangecontrol:NewTransmission( RANGE.Sound.RCHitsOnTarget.filename, RANGE.Sound.RCHitsOnTarget.duration, self.soundpath )
@@ -2908,9 +3060,11 @@ function RANGE:_CheckInZone( _unitName )
if self.rangecontrol then if self.rangecontrol then
self.rangecontrol:NewTransmission( RANGE.Sound.RCRollingInOnStrafeTarget.filename, RANGE.Sound.RCRollingInOnStrafeTarget.duration, self.soundpath ) self.rangecontrol:NewTransmission( RANGE.Sound.RCRollingInOnStrafeTarget.filename, RANGE.Sound.RCRollingInOnStrafeTarget.duration, self.soundpath )
end end
clientRollingIn = true -- RANGEBOSS
-- Send message. -- Send message.
self:_DisplayMessageToGroup( _unit, _msg, 10, true ) self:_DisplayMessageToGroup( _unit, _msg, 10, true )
hypemanStrafeRollIn = _msg -- RANGEBOSS
-- We found our player. Skip remaining checks. -- We found our player. Skip remaining checks.
break break
@@ -2959,7 +3113,8 @@ function RANGE:_AddF10Commands( _unitName )
-- MISSION LEVEL -- -- MISSION LEVEL --
------------------- -------------------
_rangePath = missionCommands.addSubMenuForGroup( _gid, self.rangename, RANGE.MenuF10Root ) -- _rangePath = missionCommands.addSubMenuForGroup(_gid, self.rangename, RANGE.MenuF10Root)
_rangePath = MENU_GROUP:New( group, "On the Range" )
else else
@@ -2969,54 +3124,57 @@ function RANGE:_AddF10Commands( _unitName )
-- Main F10 menu: F10/On the Range/<Range Name>/ -- Main F10 menu: F10/On the Range/<Range Name>/
if RANGE.MenuF10[_gid] == nil then if RANGE.MenuF10[_gid] == nil then
RANGE.MenuF10[_gid] = missionCommands.addSubMenuForGroup( _gid, "On the Range" ) -- RANGE.MenuF10[_gid]=missionCommands.addSubMenuForGroup(_gid, "On the Range")
RANGE.MenuF10[_gid] = MENU_GROUP:New( group, "On the Range" )
end end
_rangePath = missionCommands.addSubMenuForGroup( _gid, self.rangename, RANGE.MenuF10[_gid] ) -- _rangePath = missionCommands.addSubMenuForGroup(_gid, self.rangename, RANGE.MenuF10[_gid])
_rangePath = MENU_GROUP:New( group, self.rangename, RANGE.MenuF10[_gid] )
end end
local _statsPath = missionCommands.addSubMenuForGroup( _gid, "Statistics", _rangePath ) local _statsPath = MENU_GROUP:New( group, "Statistics", _rangePath )
local _markPath = missionCommands.addSubMenuForGroup( _gid, "Mark Targets", _rangePath ) local _markPath = MENU_GROUP:New( group, "Mark Targets", _rangePath )
local _settingsPath = missionCommands.addSubMenuForGroup( _gid, "My Settings", _rangePath ) local _settingsPath = MENU_GROUP:New( group, "My Settings", _rangePath )
local _infoPath = missionCommands.addSubMenuForGroup( _gid, "Range Info", _rangePath ) local _infoPath = MENU_GROUP:New( group, "Range Info", _rangePath )
-- F10/On the Range/<Range Name>/My Settings/ -- F10/On the Range/<Range Name>/My Settings/
local _mysmokePath = missionCommands.addSubMenuForGroup( _gid, "Smoke Color", _settingsPath ) local _mysmokePath = MENU_GROUP:New( group, "Smoke Color", _settingsPath )
local _myflarePath = missionCommands.addSubMenuForGroup( _gid, "Flare Color", _settingsPath ) local _myflarePath = MENU_GROUP:New( group, "Flare Color", _settingsPath )
-- F10/On the Range/<Range Name>/Mark Targets/ -- F10/On the Range/<Range Name>/Mark Targets/
missionCommands.addCommandForGroup( _gid, "Mark On Map", _markPath, self._MarkTargetsOnMap, self, _unitName ) local _MoMap = MENU_GROUP_COMMAND:New( group, "Mark On Map", _markPath, self._MarkTargetsOnMap, self, _unitName )
missionCommands.addCommandForGroup( _gid, "Illuminate Range", _markPath, self._IlluminateBombTargets, self, _unitName ) local _IllRng = MENU_GROUP_COMMAND:New( group, "Illuminate Range", _markPath, self._IlluminateBombTargets, self, _unitName )
missionCommands.addCommandForGroup( _gid, "Smoke Strafe Pits", _markPath, self._SmokeStrafeTargetBoxes, self, _unitName ) local _SSpit = MENU_GROUP_COMMAND:New( group, "Smoke Strafe Pits", _markPath, self._SmokeStrafeTargetBoxes, self, _unitName )
missionCommands.addCommandForGroup( _gid, "Smoke Strafe Tgts", _markPath, self._SmokeStrafeTargets, self, _unitName ) local _SStgts = MENU_GROUP_COMMAND:New( group, "Smoke Strafe Tgts", _markPath, self._SmokeStrafeTargets, self, _unitName )
missionCommands.addCommandForGroup( _gid, "Smoke Bomb Tgts", _markPath, self._SmokeBombTargets, self, _unitName ) local _SBtgts = MENU_GROUP_COMMAND:New( group, "Smoke Bomb Tgts", _markPath, self._SmokeBombTargets, self, _unitName )
-- F10/On the Range/<Range Name>/Stats/ -- F10/On the Range/<Range Name>/Stats/
missionCommands.addCommandForGroup( _gid, "All Strafe Results", _statsPath, self._DisplayStrafePitResults, self, _unitName ) local _AllSR = MENU_GROUP_COMMAND:New( group, "All Strafe Results", _statsPath, self._DisplayStrafePitResults, self, _unitName )
missionCommands.addCommandForGroup( _gid, "All Bombing Results", _statsPath, self._DisplayBombingResults, self, _unitName ) local _AllBR = MENU_GROUP_COMMAND:New( group, "All Bombing Results", _statsPath, self._DisplayBombingResults, self, _unitName )
missionCommands.addCommandForGroup( _gid, "My Strafe Results", _statsPath, self._DisplayMyStrafePitResults, self, _unitName ) local _MySR = MENU_GROUP_COMMAND:New( group, "My Strafe Results", _statsPath, self._DisplayMyStrafePitResults, self, _unitName )
missionCommands.addCommandForGroup( _gid, "My Bomb Results", _statsPath, self._DisplayMyBombingResults, self, _unitName ) local _MyBR = MENU_GROUP_COMMAND:New( group, "My Bomb Results", _statsPath, self._DisplayMyBombingResults, self, _unitName )
missionCommands.addCommandForGroup( _gid, "Reset All Stats", _statsPath, self._ResetRangeStats, self, _unitName ) local _ResetST = MENU_GROUP_COMMAND:New( group, "Reset All Stats", _statsPath, self._ResetRangeStats, self, _unitName )
-- F10/On the Range/<Range Name>/My Settings/Smoke Color/ -- F10/On the Range/<Range Name>/My Settings/Smoke Color/
missionCommands.addCommandForGroup( _gid, "Blue Smoke", _mysmokePath, self._playersmokecolor, self, _unitName, SMOKECOLOR.Blue ) local _BlueSM = MENU_GROUP_COMMAND:New( group, "Blue Smoke", _mysmokePath, self._playersmokecolor, self, _unitName, SMOKECOLOR.Blue )
missionCommands.addCommandForGroup( _gid, "Green Smoke", _mysmokePath, self._playersmokecolor, self, _unitName, SMOKECOLOR.Green ) local _GrSM = MENU_GROUP_COMMAND:New( group, "Green Smoke", _mysmokePath, self._playersmokecolor, self, _unitName, SMOKECOLOR.Green )
missionCommands.addCommandForGroup( _gid, "Orange Smoke", _mysmokePath, self._playersmokecolor, self, _unitName, SMOKECOLOR.Orange ) local _OrSM = MENU_GROUP_COMMAND:New( group, "Orange Smoke", _mysmokePath, self._playersmokecolor, self, _unitName, SMOKECOLOR.Orange )
missionCommands.addCommandForGroup( _gid, "Red Smoke", _mysmokePath, self._playersmokecolor, self, _unitName, SMOKECOLOR.Red ) local _ReSM = MENU_GROUP_COMMAND:New( group, "Red Smoke", _mysmokePath, self._playersmokecolor, self, _unitName, SMOKECOLOR.Red )
missionCommands.addCommandForGroup( _gid, "White Smoke", _mysmokePath, self._playersmokecolor, self, _unitName, SMOKECOLOR.White ) local _WhSm = MENU_GROUP_COMMAND:New( group, "White Smoke", _mysmokePath, self._playersmokecolor, self, _unitName, SMOKECOLOR.White )
-- F10/On the Range/<Range Name>/My Settings/Flare Color/ -- F10/On the Range/<Range Name>/My Settings/Flare Color/
missionCommands.addCommandForGroup( _gid, "Green Flares", _myflarePath, self._playerflarecolor, self, _unitName, FLARECOLOR.Green ) local _GrFl = MENU_GROUP_COMMAND:New( group, "Green Flares", _myflarePath, self._playerflarecolor, self, _unitName, FLARECOLOR.Green )
missionCommands.addCommandForGroup( _gid, "Red Flares", _myflarePath, self._playerflarecolor, self, _unitName, FLARECOLOR.Red ) local _ReFl = MENU_GROUP_COMMAND:New( group, "Red Flares", _myflarePath, self._playerflarecolor, self, _unitName, FLARECOLOR.Red )
missionCommands.addCommandForGroup( _gid, "White Flares", _myflarePath, self._playerflarecolor, self, _unitName, FLARECOLOR.White ) local _WhFl = MENU_GROUP_COMMAND:New( group, "White Flares", _myflarePath, self._playerflarecolor, self, _unitName, FLARECOLOR.White )
missionCommands.addCommandForGroup( _gid, "Yellow Flares", _myflarePath, self._playerflarecolor, self, _unitName, FLARECOLOR.Yellow ) local _YeFl = MENU_GROUP_COMMAND:New( group, "Yellow Flares", _myflarePath, self._playerflarecolor, self, _unitName, FLARECOLOR.Yellow )
-- F10/On the Range/<Range Name>/My Settings/ -- F10/On the Range/<Range Name>/My Settings/
missionCommands.addCommandForGroup( _gid, "Smoke Delay On/Off", _settingsPath, self._SmokeBombDelayOnOff, self, _unitName ) local _SmDe = MENU_GROUP_COMMAND:New( group, "Smoke Delay On/Off", _settingsPath, self._SmokeBombDelayOnOff, self, _unitName )
missionCommands.addCommandForGroup( _gid, "Smoke Impact On/Off", _settingsPath, self._SmokeBombImpactOnOff, self, _unitName ) local _SmIm = MENU_GROUP_COMMAND:New( group, "Smoke Impact On/Off", _settingsPath, self._SmokeBombImpactOnOff, self, _unitName )
missionCommands.addCommandForGroup( _gid, "Flare Hits On/Off", _settingsPath, self._FlareDirectHitsOnOff, self, _unitName ) local _FlHi = MENU_GROUP_COMMAND:New( group, "Flare Hits On/Off", _settingsPath, self._FlareDirectHitsOnOff, self, _unitName )
missionCommands.addCommandForGroup( _gid, "All Messages On/Off", _settingsPath, self._MessagesToPlayerOnOff, self, _unitName ) local _AlMeA = MENU_GROUP_COMMAND:New( group, "All Messages On/Off", _settingsPath, self._MessagesToPlayerOnOff, self, _unitName )
local _TrpSh = MENU_GROUP_COMMAND:New( group, "Targetsheet On/Off", _settingsPath, self._TargetsheetOnOff, self, _unitName )
-- F10/On the Range/<Range Name>/Range Information -- F10/On the Range/<Range Name>/Range Information
missionCommands.addCommandForGroup( _gid, "General Info", _infoPath, self._DisplayRangeInfo, self, _unitName ) local _WeIn = MENU_GROUP_COMMAND:New( group, "General Info", _infoPath, self._DisplayRangeInfo, self, _unitName )
missionCommands.addCommandForGroup( _gid, "Weather Report", _infoPath, self._DisplayRangeWeather, self, _unitName ) local _WeRe = MENU_GROUP_COMMAND:New( group, "Weather Report", _infoPath, self._DisplayRangeWeather, self, _unitName )
missionCommands.addCommandForGroup( _gid, "Bombing Targets", _infoPath, self._DisplayBombTargets, self, _unitName ) local _BoTgtgs = MENU_GROUP_COMMAND:New( group, "Bombing Targets", _infoPath, self._DisplayBombTargets, self, _unitName )
missionCommands.addCommandForGroup( _gid, "Strafe Pits", _infoPath, self._DisplayStrafePits, self, _unitName ) local _StrPits = MENU_GROUP_COMMAND:New( group, "Strafe Pits", _infoPath, self._DisplayStrafePits, self, _unitName ):Refresh()
end end
else else
self:E( self.id .. "Could not find group or group ID in AddF10Menu() function. Unit name: " .. _unitName ) self:E( self.id .. "Could not find group or group ID in AddF10Menu() function. Unit name: " .. _unitName )
@@ -3031,7 +3189,7 @@ end
-- Helper Functions -- Helper Functions
----------------------------------------------------------------------------------------------------------------------------------------------------------------------- -----------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Get the coordinate of a Bomb target. --- Get the number of shells a unit currently has.
-- @param #RANGE self -- @param #RANGE self
-- @param #RANGE.BombTarget target Bomb target data. -- @param #RANGE.BombTarget target Bomb target data.
-- @return Core.Point#COORDINATE Target coordinate. -- @return Core.Point#COORDINATE Target coordinate.
@@ -3341,6 +3499,49 @@ function RANGE:_MessagesToPlayerOnOff( unitname )
end end
--- Targetsheet saves if player on or off.
-- @param #RANGE self
-- @param #string _unitname Name of the player unit.
function RANGE:_TargetsheetOnOff( _unitname )
self:F2( _unitname )
-- Get player unit and player name.
local unit, playername = self:_GetPlayerUnitAndName( _unitname )
-- Check if we have a player.
if unit and playername then
-- Player data.
local playerData = self.PlayerSettings[playername] -- #RANGE.PlayerData
if playerData then
-- Check if option is enabled at all.
local text = ""
if self.targetsheet then
-- Invert current setting.
playerData.targeton = not playerData.targeton
-- Inform player.
if playerData.targeton == true then
text = string.format( "roger, your targetsheets are now SAVED." )
else
text = string.format( "affirm, your targetsheets are NOT SAVED." )
end
else
text = "negative, target sheet data recorder is broken on this range."
end
-- Message to player.
-- self:MessageToPlayer(playerData, text, nil, playerData.name, 5)
self:_DisplayMessageToGroup( unit, text, 5, false, false )
end
end
end
--- Toggle status of flaring direct hits of range targets. --- Toggle status of flaring direct hits of range targets.
-- @param #RANGE self -- @param #RANGE self
-- @param #string unitname Name of the player unit. -- @param #string unitname Name of the player unit.
@@ -3507,7 +3708,7 @@ end
--- Checks if a static object with a certain name exists. It also added it to the MOOSE data base, if it is not already in there. --- Checks if a static object with a certain name exists. It also added it to the MOOSE data base, if it is not already in there.
-- @param #RANGE self -- @param #RANGE self
-- @param #string name Name of the potential static object. -- @param #string name Name of the potential static object.
-- @return #boolean Returns true if a static with this name exists. Returns false if a unit with this name exists. Returns nil if neither unit or static exist. -- @return #boolean Returns true if a static with this name exists. Retruns false if a unit with this name exists. Returns nil if neither unit or static exist.
function RANGE:_CheckStatic( name ) function RANGE:_CheckStatic( name )
self:F2( name ) self:F2( name )
@@ -3601,11 +3802,9 @@ function RANGE:_myname( unitname )
local unit = UNIT:FindByName( unitname ) local unit = UNIT:FindByName( unitname )
local pname = unit:GetPlayerName() local pname = unit:GetPlayerName()
-- TODO: Either remove these leftovers, or implement them.
-- local csign = unit:GetCallsign() -- local csign = unit:GetCallsign()
-- return string.format("%s (%s)", csign, pname)
-- return string.format("%s (%s)", csign, pname)
return string.format( "%s", pname ) return string.format( "%s", pname )
end end

View File

@@ -1650,7 +1650,7 @@ function SCORING:ReportScoreGroupDetailed( PlayerGroup )
self:F( { ReportMissions, ScoreMissions, PenaltyMissions } ) self:F( { ReportMissions, ScoreMissions, PenaltyMissions } )
local PlayerScore = ScoreHits + ScoreDestroys + ScoreCoalitionChanges + ScoreGoals + ScoreMissions local PlayerScore = ScoreHits + ScoreDestroys + ScoreCoalitionChanges + ScoreGoals + ScoreMissions
local PlayerPenalty = PenaltyHits + PenaltyDestroys + PenaltyCoalitionChanges + ScoreGoals + PenaltyMissions local PlayerPenalty = PenaltyHits + PenaltyDestroys + PenaltyCoalitionChanges + PenaltyGoals + PenaltyMissions
PlayerMessage = string.format( "Player '%s' Score = %d ( %d Score, -%d Penalties )%s%s%s%s%s", PlayerMessage = string.format( "Player '%s' Score = %d ( %d Score, -%d Penalties )%s%s%s%s%s",
PlayerName, PlayerName,
@@ -1705,7 +1705,7 @@ function SCORING:ReportScoreAllSummary( PlayerGroup )
self:F( { ReportMissions, ScoreMissions, PenaltyMissions } ) self:F( { ReportMissions, ScoreMissions, PenaltyMissions } )
local PlayerScore = ScoreHits + ScoreDestroys + ScoreCoalitionChanges + ScoreGoals + ScoreMissions local PlayerScore = ScoreHits + ScoreDestroys + ScoreCoalitionChanges + ScoreGoals + ScoreMissions
local PlayerPenalty = PenaltyHits + PenaltyDestroys + PenaltyCoalitionChanges + ScoreGoals + PenaltyMissions local PlayerPenalty = PenaltyHits + PenaltyDestroys + PenaltyCoalitionChanges + PenaltyGoals + PenaltyMissions
PlayerMessage = string.format( "Player '%s' Score = %d ( %d Score, -%d Penalties )", PlayerMessage = string.format( "Player '%s' Score = %d ( %d Score, -%d Penalties )",
PlayerName, PlayerName,

View File

@@ -19,7 +19,7 @@
-- --
-- ### Authors: **FlightControl**, **applevangelist** -- ### Authors: **FlightControl**, **applevangelist**
-- --
-- Last Update: Nov 2021 -- Last Update: Feb 2022
-- --
-- === -- ===
-- --
@@ -59,6 +59,7 @@ SEAD = {
Padding = 10, Padding = 10,
CallBack = nil, CallBack = nil,
UseCallBack = false, UseCallBack = false,
debug = false,
} }
--- Missile enumerators --- Missile enumerators
@@ -76,6 +77,8 @@ SEAD = {
["X_25"] = "X_25", ["X_25"] = "X_25",
["X_31"] = "X_31", ["X_31"] = "X_31",
["Kh25"] = "Kh25", ["Kh25"] = "Kh25",
["BGM_109"] = "BGM_109",
["AGM_154"] = "AGM_154",
} }
--- Missile enumerators - from DCS ME and Wikipedia --- Missile enumerators - from DCS ME and Wikipedia
@@ -85,7 +88,7 @@ SEAD = {
["AGM_88"] = { 150, 3}, ["AGM_88"] = { 150, 3},
["AGM_45"] = { 12, 2}, ["AGM_45"] = { 12, 2},
["AGM_122"] = { 16.5, 2.3}, ["AGM_122"] = { 16.5, 2.3},
["AGM_84"] = { 280, 0.85}, ["AGM_84"] = { 280, 0.8},
["ALARM"] = { 45, 2}, ["ALARM"] = { 45, 2},
["LD-10"] = { 60, 4}, ["LD-10"] = { 60, 4},
["X_58"] = { 70, 4}, ["X_58"] = { 70, 4},
@@ -93,6 +96,8 @@ SEAD = {
["X_25"] = { 25, 0.76}, ["X_25"] = { 25, 0.76},
["X_31"] = {150, 3}, ["X_31"] = {150, 3},
["Kh25"] = {25, 0.8}, ["Kh25"] = {25, 0.8},
["BGM_109"] = {460, 0.705}, --in-game ~465kn
["AGM_154"] = {130, 0.61},
} }
--- Creates the main object which is handling defensive actions for SA sites or moving SA vehicles. --- Creates the main object which is handling defensive actions for SA sites or moving SA vehicles.
@@ -108,8 +113,8 @@ SEAD = {
-- SEAD_RU_SAM_Defenses = SEAD:New( { 'RU SA-6 Kub', 'RU SA-6 Defenses', 'RU MI-26 Troops', 'RU Attack Gori' } ) -- SEAD_RU_SAM_Defenses = SEAD:New( { 'RU SA-6 Kub', 'RU SA-6 Defenses', 'RU MI-26 Troops', 'RU Attack Gori' } )
function SEAD:New( SEADGroupPrefixes, Padding ) function SEAD:New( SEADGroupPrefixes, Padding )
local self = BASE:Inherit( self, BASE:New() ) local self = BASE:Inherit( self, FSM:New() )
self:F( SEADGroupPrefixes ) self:T( SEADGroupPrefixes )
if type( SEADGroupPrefixes ) == 'table' then if type( SEADGroupPrefixes ) == 'table' then
for SEADGroupPrefixID, SEADGroupPrefix in pairs( SEADGroupPrefixes ) do for SEADGroupPrefixID, SEADGroupPrefix in pairs( SEADGroupPrefixes ) do
@@ -122,14 +127,21 @@ function SEAD:New( SEADGroupPrefixes, Padding )
local padding = Padding or 10 local padding = Padding or 10
if padding < 10 then padding = 10 end if padding < 10 then padding = 10 end
self.Padding = padding self.Padding = padding
self.UseEmissionsOnOff = false self.UseEmissionsOnOff = true
self.debug = false
self.CallBack = nil self.CallBack = nil
self.UseCallBack = false self.UseCallBack = false
self:HandleEvent( EVENTS.Shot, self.HandleEventShot ) self:HandleEvent( EVENTS.Shot, self.HandleEventShot )
self:I("*** SEAD - Started Version 0.3.3") -- Start State.
self:SetStartState("Running")
self:AddTransition("*", "ManageEvasion", "*")
self:AddTransition("*", "CalculateHitZone", "*")
self:I("*** SEAD - Started Version 0.4.3")
return self return self
end end
@@ -213,7 +225,7 @@ function SEAD:_CheckHarms(WeaponName)
local hit = false local hit = false
local name = "" local name = ""
for _,_name in pairs (SEAD.Harms) do for _,_name in pairs (SEAD.Harms) do
if string.find(WeaponName,_name,1) then if string.find(WeaponName,_name,1,true) then
hit = true hit = true
name = _name name = _name
break break
@@ -249,47 +261,94 @@ function SEAD:_GetDistance(_point1, _point2)
end end
end end
--- (Internal) Detects if an SAM site was shot with an anti radiation missile. In this case, take evasive actions based on the skill level set within the ME. --- (Internal) Calculate hit zone of an AGM-88
-- @param #SEAD self -- @param #SEAD self
-- @param Core.Event#EVENTDATA EventData -- @param #table SEADWeapon DCS.Weapon object
-- @param Core.Point#COORDINATE pos0 Position of the plane when it fired
-- @param #number height Height when the missile was fired
-- @param Wrapper.Group#GROUP SEADGroup Attacker group
-- @param #string SEADWeaponName Weapon Name
-- @return #SEAD self -- @return #SEAD self
function SEAD:HandleEventShot( EventData ) function SEAD:onafterCalculateHitZone(From,Event,To,SEADWeapon,pos0,height,SEADGroup,SEADWeaponName)
self:T( { EventData.id } ) self:T("**** Calculating hit zone for " .. (SEADWeaponName or "None"))
local SEADPlane = EventData.IniUnit -- Wrapper.Unit#UNIT if SEADWeapon and SEADWeapon:isExist() then
local SEADPlanePos = SEADPlane:GetCoordinate() -- Core.Point#COORDINATE --local pos = SEADWeapon:getPoint()
local SEADUnit = EventData.IniDCSUnit
local SEADUnitName = EventData.IniDCSUnitName
local SEADWeapon = EventData.Weapon -- Identify the weapon fired
local SEADWeaponName = EventData.WeaponName -- return weapon type
self:T( "*** SEAD - Missile Launched = " .. SEADWeaponName) -- postion and height
--self:T({ SEADWeapon }) local position = SEADWeapon:getPosition()
local mheight = height
-- heading
local wph = math.atan2(position.x.z, position.x.x)
if wph < 0 then
wph=wph+2*math.pi
end
wph=math.deg(wph)
if self:_CheckHarms(SEADWeaponName) then -- velocity
self:T( '*** SEAD - Weapon Match' ) local wpndata = SEAD.HarmData["AGM_88"]
local _targetskill = "Random" if string.find(SEADWeaponName,"154",1) then
wpndata = SEAD.HarmData["AGM_154"]
end
local mveloc = math.floor(wpndata[2] * 340.29)
local c1 = (2*mheight*9.81)/(mveloc^2)
local c2 = (mveloc^2) / 9.81
local Ropt = c2 * math.sqrt(c1+1)
if height <= 5000 then
Ropt = Ropt * 0.72
elseif height <= 7500 then
Ropt = Ropt * 0.82
elseif height <= 10000 then
Ropt = Ropt * 0.87
elseif height <= 12500 then
Ropt = Ropt * 0.98
end
-- look at a couple of zones across the trajectory
for n=1,3 do
local dist = Ropt - ((n-1)*20000)
local predpos= pos0:Translate(dist,wph)
if predpos then
local targetzone = ZONE_RADIUS:New("Target Zone",predpos:GetVec2(),20000)
if self.debug then
predpos:MarkToAll(string.format("height=%dm | heading=%d | velocity=%ddeg | Ropt=%dm",mheight,wph,mveloc,Ropt),false)
targetzone:DrawZone(coalition.side.BLUE,{0,0,1},0.2,nil,nil,3,true)
end
local seadset = SET_GROUP:New():FilterPrefixes(self.SEADGroupPrefixes):FilterZones({targetzone}):FilterOnce()
local tgtcoord = targetzone:GetRandomPointVec2()
--if tgtcoord and tgtcoord.ClassName == "COORDINATE" then
--local tgtgrp = seadset:FindNearestGroupFromPointVec2(tgtcoord)
local tgtgrp = seadset:GetRandom()
local _targetgroup = nil
local _targetgroupname = "none" local _targetgroupname = "none"
local _target = EventData.Weapon:getTarget() -- Identify target local _targetskill = "Random"
local _targetUnit = UNIT:Find(_target) -- Wrapper.Unit#UNIT if tgtgrp and tgtgrp:IsAlive() then
local _targetgroup = nil -- Wrapper.Group#GROUP _targetgroup = tgtgrp
if _targetUnit and _targetUnit:IsAlive() then _targetgroupname = tgtgrp:GetName() -- group name
_targetgroup = _targetUnit:GetGroup() _targetskill = tgtgrp:GetUnit(1):GetSkill()
_targetgroupname = _targetgroup:GetName() -- group name self:T("*** Found Target = ".. _targetgroupname)
local _targetUnitName = _targetUnit:GetName() self:ManageEvasion(_targetskill,_targetgroup,pos0,"AGM_88",SEADGroup, 20)
_targetUnit:GetSkill()
_targetskill = _targetUnit:GetSkill()
end end
-- see if we are shot at --end
local SEADGroupFound = false
for SEADGroupPrefixID, SEADGroupPrefix in pairs( self.SEADGroupPrefixes ) do
self:T( _targetgroupname, SEADGroupPrefix )
if string.find( _targetgroupname, SEADGroupPrefix, 1, true ) then
SEADGroupFound = true
self:T( '*** SEAD - Group Match Found' )
break
end end
end end
if SEADGroupFound == true then -- yes we are being attacked end
return self
end
--- (Internal) Handle Evasion
-- @param #SEAD self
-- @param #string _targetskill
-- @param Wrapper.Group#GROUP _targetgroup
-- @param Core.Point#COORDINATE SEADPlanePos
-- @param #string SEADWeaponName
-- @param Wrapper.Group#GROUP SEADGroup Attacker Group
-- @param #number timeoffset Offset for tti calc
-- @return #SEAD self
function SEAD:onafterManageEvasion(From,Event,To,_targetskill,_targetgroup,SEADPlanePos,SEADWeaponName,SEADGroup,timeoffset)
local timeoffset = timeoffset or 0
if _targetskill == "Random" then -- when skill is random, choose a skill if _targetskill == "Random" then -- when skill is random, choose a skill
local Skills = { "Average", "Good", "High", "Excellent" } local Skills = { "Average", "Good", "High", "Excellent" }
_targetskill = Skills[ math.random(1,4) ] _targetskill = Skills[ math.random(1,4) ]
@@ -313,7 +372,7 @@ function SEAD:HandleEventShot( EventData )
wpnspeed = math.floor(mach * 340.29) wpnspeed = math.floor(mach * 340.29)
end end
-- time to impact -- time to impact
local _tti = math.floor(_distance / wpnspeed) -- estimated impact time local _tti = math.floor(_distance / wpnspeed) - timeoffset -- estimated impact time
if _distance > 0 then if _distance > 0 then
_distance = math.floor(_distance / 1000) -- km _distance = math.floor(_distance / 1000) -- km
else else
@@ -329,6 +388,7 @@ function SEAD:HandleEventShot( EventData )
self:T(string.format("*** SEAD - %s Radar Off & Relocating",args[2])) self:T(string.format("*** SEAD - %s Radar Off & Relocating",args[2]))
local grp = args[1] -- Wrapper.Group#GROUP local grp = args[1] -- Wrapper.Group#GROUP
local name = args[2] -- #string Group Name local name = args[2] -- #string Group Name
local attacker = args[3] -- Wrapper.Group#GROUP
if self.UseEmissionsOnOff then if self.UseEmissionsOnOff then
grp:EnableEmission(false) grp:EnableEmission(false)
end end
@@ -336,7 +396,7 @@ function SEAD:HandleEventShot( EventData )
grp:RelocateGroundRandomInRadius(20,300,false,false,"Diamond") grp:RelocateGroundRandomInRadius(20,300,false,false,"Diamond")
if self.UseCallBack then if self.UseCallBack then
local object = self.CallBack local object = self.CallBack
object:SeadSuppressionStart(grp,name) object:SeadSuppressionStart(grp,name,attacker)
end end
end end
@@ -347,7 +407,7 @@ function SEAD:HandleEventShot( EventData )
if self.UseEmissionsOnOff then if self.UseEmissionsOnOff then
grp:EnableEmission(true) grp:EnableEmission(true)
end end
grp:OptionAlarmStateAuto() grp:OptionAlarmStateRed()
grp:OptionEngageRange(self.EngagementRange) grp:OptionEngageRange(self.EngagementRange)
self.SuppressedGroups[name] = false self.SuppressedGroups[name] = false
if self.UseCallBack then if self.UseCallBack then
@@ -359,26 +419,100 @@ function SEAD:HandleEventShot( EventData )
-- randomize switch-on time -- randomize switch-on time
local delay = math.random(self.TargetSkill[_targetskill].DelayOn[1], self.TargetSkill[_targetskill].DelayOn[2]) local delay = math.random(self.TargetSkill[_targetskill].DelayOn[1], self.TargetSkill[_targetskill].DelayOn[2])
if delay > _tti then delay = delay / 2 end -- speed up if delay > _tti then delay = delay / 2 end -- speed up
if _tti > (3*delay) then delay = (_tti / 2) * 0.9 end -- shot from afar if _tti > 600 then delay = _tti - 90 end -- shot from afar, 600 is default shorad ontime
local SuppressionStartTime = timer.getTime() + delay local SuppressionStartTime = timer.getTime() + delay
local SuppressionEndTime = timer.getTime() + _tti + self.Padding local SuppressionEndTime = timer.getTime() + _tti + self.Padding
local _targetgroupname = _targetgroup:GetName()
if not self.SuppressedGroups[_targetgroupname] then if not self.SuppressedGroups[_targetgroupname] then
self:T(string.format("*** SEAD - %s | Parameters TTI %ds | Switch-Off in %ds",_targetgroupname,_tti,delay)) self:T(string.format("*** SEAD - %s | Parameters TTI %ds | Switch-Off in %ds",_targetgroupname,_tti,delay))
timer.scheduleFunction(SuppressionStart,{_targetgroup,_targetgroupname},SuppressionStartTime) timer.scheduleFunction(SuppressionStart,{_targetgroup,_targetgroupname, SEADGroup},SuppressionStartTime)
timer.scheduleFunction(SuppressionStop,{_targetgroup,_targetgroupname},SuppressionEndTime) timer.scheduleFunction(SuppressionStop,{_targetgroup,_targetgroupname},SuppressionEndTime)
self.SuppressedGroups[_targetgroupname] = true self.SuppressedGroups[_targetgroupname] = true
if self.UseCallBack then if self.UseCallBack then
local object = self.CallBack local object = self.CallBack
object:SeadSuppressionPlanned(_targetgroup,_targetgroupname,SuppressionStartTime,SuppressionEndTime) object:SeadSuppressionPlanned(_targetgroup,_targetgroupname,SuppressionStartTime,SuppressionEndTime, SEADGroup)
end end
end end
end end
end end
end end
end return self
end end
--- (Internal) Detects if an SAM site was shot with an anti radiation missile. In this case, take evasive actions based on the skill level set within the ME.
-- @param #SEAD self
-- @param Core.Event#EVENTDATA EventData
-- @return #SEAD self
function SEAD:HandleEventShot( EventData )
self:T( { EventData.id } )
local SEADPlane = EventData.IniUnit -- Wrapper.Unit#UNIT
local SEADGroup = EventData.IniGroup -- Wrapper.Group#GROUP
local SEADPlanePos = SEADPlane:GetCoordinate() -- Core.Point#COORDINATE
local SEADUnit = EventData.IniDCSUnit
local SEADUnitName = EventData.IniDCSUnitName
local SEADWeapon = EventData.Weapon -- Identify the weapon fired
local SEADWeaponName = EventData.WeaponName -- return weapon type
self:T( "*** SEAD - Missile Launched = " .. SEADWeaponName)
--self:T({ SEADWeapon })
if self:_CheckHarms(SEADWeaponName) then
self:T( '*** SEAD - Weapon Match' )
local _targetskill = "Random"
local _targetgroupname = "none"
local _target = EventData.Weapon:getTarget() -- Identify target
if not _target or self.debug then -- AGM-88 or 154 w/o target data
self:E("***** SEAD - No target data for " .. (SEADWeaponName or "None"))
if string.find(SEADWeaponName,"AGM_88",1,true) or string.find(SEADWeaponName,"AGM_154",1,true) then
self:I("**** Tracking AGM-88/154 with no target data.")
local pos0 = SEADPlane:GetCoordinate()
local fheight = SEADPlane:GetHeight()
self:__CalculateHitZone(20,SEADWeapon,pos0,fheight,SEADGroup,SEADWeaponName)
end
return self
end
local targetcat = _target:getCategory() -- Identify category
local _targetUnit = nil -- Wrapper.Unit#UNIT
local _targetgroup = nil -- Wrapper.Group#GROUP
self:T(string.format("*** Targetcat = %d",targetcat))
if targetcat == Object.Category.UNIT then -- UNIT
self:T("*** Target Category UNIT")
_targetUnit = UNIT:Find(_target) -- Wrapper.Unit#UNIT
if _targetUnit and _targetUnit:IsAlive() then
_targetgroup = _targetUnit:GetGroup()
_targetgroupname = _targetgroup:GetName() -- group name
local _targetUnitName = _targetUnit:GetName()
_targetUnit:GetSkill()
_targetskill = _targetUnit:GetSkill()
end
elseif targetcat == Object.Category.STATIC then
self:T("*** Target Category STATIC")
local seadset = SET_GROUP:New():FilterPrefixes(self.SEADGroupPrefixes):FilterOnce()
local targetpoint = _target:getPoint() or {x=0,y=0,z=0}
local tgtcoord = COORDINATE:NewFromVec3(targetpoint)
local tgtgrp = seadset:FindNearestGroupFromPointVec2(tgtcoord)
if tgtgrp and tgtgrp:IsAlive() then
_targetgroup = tgtgrp
_targetgroupname = tgtgrp:GetName() -- group name
_targetskill = tgtgrp:GetUnit(1):GetSkill()
self:T("*** Found Target = ".. _targetgroupname)
end
end
-- see if we are shot at
local SEADGroupFound = false
for SEADGroupPrefixID, SEADGroupPrefix in pairs( self.SEADGroupPrefixes ) do
self:T("Target = ".. _targetgroupname .. " | Prefix = " .. SEADGroupPrefix )
if string.find( _targetgroupname, SEADGroupPrefix,1,true ) then
SEADGroupFound = true
self:T( '*** SEAD - Group Match Found' )
break
end
end
if SEADGroupFound == true then -- yes we are being attacked
self:ManageEvasion(_targetskill,_targetgroup,SEADPlanePos,SEADWeaponName,SEADGroup)
end
end
return self return self
end end

View File

@@ -6,6 +6,14 @@
-- --
-- === -- ===
-- --
-- ## Missions:--- **Ops** -- Combat Search and Rescue.
--
-- ===
--
-- **CSAR** - MOOSE based Helicopter CSAR Operations.
--
-- ===
--
-- ## Missions: -- ## Missions:
-- --
-- ### [CSAR - Combat Search & Rescue](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/OPS%20-%20CSAR) -- ### [CSAR - Combat Search & Rescue](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/OPS%20-%20CSAR)
@@ -22,7 +30,7 @@
-- @module Ops.CSAR -- @module Ops.CSAR
-- @image OPS_CSAR.jpg -- @image OPS_CSAR.jpg
-- Date: Dec 2021 -- Date: Feb 2022
------------------------------------------------------------------------- -------------------------------------------------------------------------
--- **CSAR** class, extends Core.Base#BASE, Core.Fsm#FSM --- **CSAR** class, extends Core.Base#BASE, Core.Fsm#FSM
@@ -42,14 +50,14 @@
-- # CSAR Concept -- # CSAR Concept
-- --
-- * MOOSE-based Helicopter CSAR Operations for Players. -- * MOOSE-based Helicopter CSAR Operations for Players.
-- * Object oriented refactoring of Ciribob's fantastic CSAR script. -- * Object oriented refactoring of Ciribob\'s fantastic CSAR script.
-- * No need for extra MIST loading. -- * No need for extra MIST loading.
-- * Additional events to tailor your mission. -- * Additional events to tailor your mission.
-- * Optional SpawnCASEVAC to create casualties without beacon (e.g. handling dead ground vehicles and create CASEVAC requests). -- * Optional SpawnCASEVAC to create casualties without beacon (e.g. handling dead ground vehicles and create CASVAC requests).
-- --
-- ## 0. Prerequisites -- ## 0. Prerequisites
-- --
-- You need to load an .ogg sound file for the pilot's beacons into the mission, e.g. "beacon.ogg", use a once trigger, "sound to country" for that. -- You need to load an .ogg soundfile for the pilot\'s beacons into the mission, e.g. "beacon.ogg", use a once trigger, "sound to country" for that.
-- Create a late-activated single infantry unit as template in the mission editor and name it e.g. "Downed Pilot". -- Create a late-activated single infantry unit as template in the mission editor and name it e.g. "Downed Pilot".
-- --
-- ## 1. Basic Setup -- ## 1. Basic Setup
@@ -68,23 +76,23 @@
-- --
-- The following options are available (with their defaults). Only set the ones you want changed: -- The following options are available (with their defaults). Only set the ones you want changed:
-- --
-- self.allowDownedPilotCAcontrol = false -- Set to false if you don't want to allow control by Combined Arms. -- self.allowDownedPilotCAcontrol = false -- Set to false if you don\'t want to allow control by Combined Arms.
-- self.allowFARPRescue = true -- allows pilots to be rescued by landing at a FARP or Airbase. Else MASH only! -- self.allowFARPRescue = true -- allows pilots to be rescued by landing at a FARP or Airbase. Else MASH only!
-- self.FARPRescueDistance = 1000 -- you need to be this close to a FARP or Airport for the pilot to be rescued. -- self.FARPRescueDistance = 1000 -- you need to be this close to a FARP or Airport for the pilot to be rescued.
-- self.autosmoke = false -- automatically smoke a downed pilot's location when a helicopter is near. -- self.autosmoke = false -- automatically smoke a downed pilot\'s location when a heli is near.
-- self.autosmokedistance = 1000 -- distance in meters for automatic smoke deployment -- self.autosmokedistance = 1000 -- distance for autosmoke
-- self.coordtype = 1 -- Use Lat/Long DDM (0), Lat/Long DMS (1), MGRS (2), Bullseye imperial (3) or Bullseye metric (4) for coordinates. -- self.coordtype = 1 -- Use Lat/Long DDM (0), Lat/Long DMS (1), MGRS (2), Bullseye imperial (3) or Bullseye metric (4) for coordinates.
-- self.csarOncrash = false -- (WIP) If set to true, will generate a downed pilot when a plane crashes as well. -- self.csarOncrash = false -- (WIP) If set to true, will generate a downed pilot when a plane crashes as well.
-- self.enableForAI = false -- set to false to disable AI pilots from being rescued. -- self.enableForAI = false -- set to false to disable AI pilots from being rescued.
-- self.pilotRuntoExtractPoint = true -- Downed pilot will run to the rescue helicopter up to self.extractDistance in meters. -- self.pilotRuntoExtractPoint = true -- Downed pilot will run to the rescue helicopter up to self.extractDistance in meters.
-- self.extractDistance = 500 -- Distance the downed pilot will start to run to the rescue helicopter. -- self.extractDistance = 500 -- Distance the downed pilot will start to run to the rescue helicopter.
-- self.immortalcrew = true -- Set to true to make wounded crew immortal. -- self.immortalcrew = true -- Set to true to make wounded crew immortal.
-- self.invisiblecrew = false -- Set to true to make wounded crew invisible. -- self.invisiblecrew = false -- Set to true to make wounded crew insvisible.
-- self.loadDistance = 75 -- configure distance for pilots to get into helicopter in meters. -- self.loadDistance = 75 -- configure distance for pilots to get into helicopter in meters.
-- self.mashprefix = {"MASH"} -- prefixes of #GROUP objects used as MASHes. -- self.mashprefix = {"MASH"} -- prefixes of #GROUP objects used as MASHes.
-- self.max_units = 6 -- max number of pilots that can be carried if #CSAR.AircraftType is undefined. -- self.max_units = 6 -- max number of pilots that can be carried if #CSAR.AircraftType is undefined.
-- self.messageTime = 15 -- Time to show messages for in seconds. Doubled for long messages. -- self.messageTime = 15 -- Time to show messages for in seconds. Doubled for long messages.
-- self.radioSound = "beacon.ogg" -- the name of the sound file to use for the pilots' radio beacons. -- self.radioSound = "beacon.ogg" -- the name of the sound file to use for the pilots\' radio beacons.
-- self.smokecolor = 4 -- Color of smokemarker, 0 is green, 1 is red, 2 is white, 3 is orange and 4 is blue. -- self.smokecolor = 4 -- Color of smokemarker, 0 is green, 1 is red, 2 is white, 3 is orange and 4 is blue.
-- self.useprefix = true -- Requires CSAR helicopter #GROUP names to have the prefix(es) defined below. -- self.useprefix = true -- Requires CSAR helicopter #GROUP names to have the prefix(es) defined below.
-- self.csarPrefix = { "helicargo", "MEDEVAC"} -- #GROUP name prefixes used for useprefix=true - DO NOT use # in helicopter names in the Mission Editor! -- self.csarPrefix = { "helicargo", "MEDEVAC"} -- #GROUP name prefixes used for useprefix=true - DO NOT use # in helicopter names in the Mission Editor!
@@ -109,15 +117,16 @@
-- --
-- ## 2.1 Experimental Features -- ## 2.1 Experimental Features
-- --
-- WARNING - Here'll be dragons! -- WARNING - Here\'ll be dragons!
-- DANGER - For this to work you need to de-sanitize your mission environment (all three entries) in <DCS root>\Scripts\MissionScripting.lua -- DANGER - For this to work you need to de-sanitize your mission environment (all three entries) in <DCS root>\Scripts\MissionScripting.lua
-- Needs SRS => 1.9.6 to work (works on the **server** side of SRS) -- Needs SRS => 1.9.6 to work (works on the **server** side of SRS)
-- self.useSRS = false -- Set true to use FF's SRS integration -- self.useSRS = false -- Set true to use FF\'s SRS integration
-- self.SRSPath = "E:\\Progra~1\\DCS-SimpleRadio-Standalone\\" -- adjust your own path in your SRS installation -- server(!) -- self.SRSPath = "E:\\Progra~1\\DCS-SimpleRadio-Standalone\\" -- adjust your own path in your SRS installation -- server(!)
-- self.SRSchannel = 300 -- radio channel -- self.SRSchannel = 300 -- radio channel
-- self.SRSModulation = radio.modulation.AM -- modulation -- self.SRSModulation = radio.modulation.AM -- modulation
-- -- -- --
-- self.csarUsePara = false -- If set to true, will use the LandingAfterEjection Event instead of Ejection --shagrat -- self.csarUsePara = false -- If set to true, will use the LandingAfterEjection Event instead of Ejection --shagrat
-- self.wetfeettemplate = "man in floating thingy" -- if you use a mod to have a pilot in a rescue float, put the template name in here for wet feet spawns. Note: in conjunction with csarUsePara this might create dual ejected pilots in edge cases.
-- --
-- ## 3. Results -- ## 3. Results
-- --
@@ -141,7 +150,7 @@
-- --
-- ### 4.2. Approach. -- ### 4.2. Approach.
-- --
-- A CSAR helicopter is closing in on a downed pilot. Use e.g. `function my_csar:OnAfterApproach(...)` to link into this event: -- A CSAR helicpoter is closing in on a downed pilot. Use e.g. `function my_csar:OnAfterApproach(...)` to link into this event:
-- --
-- function my_csar:OnAfterApproach(from, event, to, heliname, groupname) -- function my_csar:OnAfterApproach(from, event, to, heliname, groupname)
-- ... your code here ... -- ... your code here ...
@@ -205,7 +214,7 @@ CSAR = {
hoverStatus = {}, -- tracks status of a helis hover above a downed pilot hoverStatus = {}, -- tracks status of a helis hover above a downed pilot
pilotDisabled = {}, -- tracks what aircraft a pilot is disabled for pilotDisabled = {}, -- tracks what aircraft a pilot is disabled for
pilotLives = {}, -- tracks how many lives a pilot has pilotLives = {}, -- tracks how many lives a pilot has
useprefix = true, -- Use the Prefix defined below, requires Unit to have the Prefix defined useprefix = true, -- Use the Prefixed defined below, Requires Unit have the Prefix defined below
csarPrefix = {}, csarPrefix = {},
template = nil, template = nil,
mash = {}, mash = {},
@@ -227,8 +236,9 @@ CSAR = {
-- @field #number frequency Frequency of the NDB. -- @field #number frequency Frequency of the NDB.
-- @field #string player Player name if applicable. -- @field #string player Player name if applicable.
-- @field Wrapper.Group#GROUP group Spawned group object. -- @field Wrapper.Group#GROUP group Spawned group object.
-- @field #number timestamp Timestamp for approach process -- @field #number timestamp Timestamp for approach process.
-- @field #boolean alive Group is alive or dead/rescued -- @field #boolean alive Group is alive or dead/rescued.
-- @field #boolean wetfeet Group is spawned over (deep) water.
--- All slot / Limit settings --- All slot / Limit settings
-- @type CSAR.AircraftType -- @type CSAR.AircraftType
@@ -244,10 +254,12 @@ CSAR.AircraftType["Mi-8MT"] = 12
CSAR.AircraftType["Mi-24P"] = 8 CSAR.AircraftType["Mi-24P"] = 8
CSAR.AircraftType["Mi-24V"] = 8 CSAR.AircraftType["Mi-24V"] = 8
CSAR.AircraftType["Bell-47"] = 2 CSAR.AircraftType["Bell-47"] = 2
CSAR.AircraftType["UH-60L"] = 10
CSAR.AircraftType["AH-64D_BLK_II"] = 2
--- CSAR class version. --- CSAR class version.
-- @field #string version -- @field #string version
CSAR.version = "1.0.1r1" CSAR.version="1.0.4d"
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- ToDo list -- ToDo list
@@ -344,36 +356,36 @@ function CSAR:New( Coalition, Template, Alias )
self.rescues = 0 -- counter for successful rescue landings at FARP/AFB/MASH self.rescues = 0 -- counter for successful rescue landings at FARP/AFB/MASH
self.rescuedpilots = 0 -- counter for saved pilots self.rescuedpilots = 0 -- counter for saved pilots
self.csarOncrash = false -- If set to true, will generate a csar when a plane crashes as well. self.csarOncrash = false -- If set to true, will generate a csar when a plane crashes as well.
self.allowDownedPilotCAcontrol = false -- Set to false if you don't want to allow control by Combined arms. self.allowDownedPilotCAcontrol = false -- Set to false if you don\'t want to allow control by Combined arms.
self.enableForAI = false -- set to false to disable AI units from being rescued. self.enableForAI = false -- set to false to disable AI units from being rescued.
self.smokecolor = 4 -- Color of smokemarker for blue side, 0 is green, 1 is red, 2 is white, 3 is orange and 4 is blue self.smokecolor = 4 -- Color of smokemarker for blue side, 0 is green, 1 is red, 2 is white, 3 is orange and 4 is blue
self.coordtype = 2 -- Use Lat/Long DDM (0), Lat/Long DMS (1), MGRS (2), Bullseye imperial (3) or Bullseye metric (4) for coordinates. self.coordtype = 2 -- Use Lat/Long DDM (0), Lat/Long DMS (1), MGRS (2), Bullseye imperial (3) or Bullseye metric (4) for coordinates.
self.immortalcrew = true -- Set to true to make wounded crew immortal self.immortalcrew = true -- Set to true to make wounded crew immortal
self.invisiblecrew = false -- Set to true to make wounded crew invisible self.invisiblecrew = false -- Set to true to make wounded crew insvisible
self.messageTime = 15 -- Time to show longer messages for in seconds self.messageTime = 15 -- Time to show longer messages for in seconds
self.pilotRuntoExtractPoint = true -- Downed Pilot will run to the rescue helicopter up to self.extractDistance METERS. self.pilotRuntoExtractPoint = true -- Downed Pilot will run to the rescue helicopter up to self.extractDistance METERS
self.loadDistance = 75 -- configure distance for pilot to get in helicopter in meters. self.loadDistance = 75 -- configure distance for pilot to get in helicopter in meters.
self.extractDistance = 500 -- Distance the Downed pilot will run to the rescue helicopter. self.extractDistance = 500 -- Distance the Downed pilot will run to the rescue helicopter
self.loadtimemax = 135 -- seconds self.loadtimemax = 135 -- seconds
self.radioSound = "beacon.ogg" -- the name of the sound file to use for the Pilot radio beacons. If this isn't added to the mission BEACONS WONT WORK! self.radioSound = "beacon.ogg" -- the name of the sound file to use for the Pilot radio beacons. If this isnt added to the mission BEACONS WONT WORK!
self.beaconRefresher = 29 -- seconds self.beaconRefresher = 29 -- seconds
self.allowFARPRescue = true --allows pilot to be rescued by landing at a FARP or Airbase self.allowFARPRescue = true --allows pilot to be rescued by landing at a FARP or Airbase
self.FARPRescueDistance = 1000 -- you need to be this close to a FARP or Airport for the pilot to be rescued. self.FARPRescueDistance = 1000 -- you need to be this close to a FARP or Airport for the pilot to be rescued.
self.max_units = 6 --max number of pilots that can be carried self.max_units = 6 --max number of pilots that can be carried
self.useprefix = true -- Use the Prefixed defined below, Requires Unit have the Prefix defined below. self.useprefix = true -- Use the Prefixed defined below, Requires Unit have the Prefix defined below
self.csarPrefix = { "helicargo", "MEDEVAC" } -- prefixes used for useprefix=true - DON'T use # in names! self.csarPrefix = { "helicargo", "MEDEVAC"} -- prefixes used for useprefix=true - DON\'T use # in names!
self.template = Template or "generic" -- template for downed pilot self.template = Template or "generic" -- template for downed pilot
self.mashprefix = {"MASH"} -- prefixes used to find MASHes self.mashprefix = {"MASH"} -- prefixes used to find MASHes
self.autosmoke = false -- automatically smoke location when heli is near self.autosmoke = false -- automatically smoke location when heli is near
self.autosmokedistance = 2000 -- distance in meters for automatic smoke deployment self.autosmokedistance = 2000 -- distance for autosmoke
-- added 0.1.4 -- added 0.1.4
self.limitmaxdownedpilots = true self.limitmaxdownedpilots = true
self.maxdownedpilots = 25 self.maxdownedpilots = 25
-- generate Frequencies -- generate Frequencies
self:_GenerateVHFrequencies() self:_GenerateVHFrequencies()
-- added 0.1.8 -- added 0.1.8
self.approachdist_far = 5000 -- switch to 10 sec interval approach mode, meters self.approachdist_far = 5000 -- switch do 10 sec interval approach mode, meters
self.approachdist_near = 3000 -- switch to 5 sec interval approach mode, meters self.approachdist_near = 3000 -- switch to 5 sec interval approach mode, meters
self.pilotmustopendoors = false -- switch to true to enable check on open doors self.pilotmustopendoors = false -- switch to true to enable check on open doors
self.suppressmessages = false self.suppressmessages = false
@@ -390,10 +402,14 @@ function CSAR:New( Coalition, Template, Alias )
-- added 0.1.3 -- added 0.1.3
self.csarUsePara = false -- shagrat set to true, will use the LandingAfterEjection Event instead of Ejection self.csarUsePara = false -- shagrat set to true, will use the LandingAfterEjection Event instead of Ejection
-- WARNING - here'll be dragons -- added 0.1.4
self.wetfeettemplate = nil
self.usewetfeet = false
-- WARNING - here\'ll be dragons
-- for this to work you need to de-sanitize your mission environment in <DCS root>\Scripts\MissionScripting.lua -- for this to work you need to de-sanitize your mission environment in <DCS root>\Scripts\MissionScripting.lua
-- needs SRS => 1.9.6 to work (works on the *server* side) -- needs SRS => 1.9.6 to work (works on the *server* side)
self.useSRS = false -- Use FF's SRS integration self.useSRS = false -- Use FF\'s SRS integration
self.SRSPath = "E:\\Progra~1\\DCS-SimpleRadio-Standalone\\" -- adjust your own path in your server(!) self.SRSPath = "E:\\Progra~1\\DCS-SimpleRadio-Standalone\\" -- adjust your own path in your server(!)
self.SRSchannel = 300 -- radio channel self.SRSchannel = 300 -- radio channel
self.SRSModulation = radio.modulation.AM -- modulation self.SRSModulation = radio.modulation.AM -- modulation
@@ -446,7 +462,7 @@ function CSAR:New( Coalition, Template, Alias )
-- @param #string Event Event. -- @param #string Event Event.
-- @param #string To To state. -- @param #string To To state.
-- @param #string Heliname Name of the helicopter group. -- @param #string Heliname Name of the helicopter group.
-- @param #string Woundedgroupname Name of the downed pilot's group. -- @param #string Woundedgroupname Name of the downed pilot\'s group.
--- On After "Boarded" event. Downed pilot boarded heli. --- On After "Boarded" event. Downed pilot boarded heli.
-- @function [parent=#CSAR] OnAfterBoarded -- @function [parent=#CSAR] OnAfterBoarded
@@ -455,7 +471,7 @@ function CSAR:New( Coalition, Template, Alias )
-- @param #string Event Event. -- @param #string Event Event.
-- @param #string To To state. -- @param #string To To state.
-- @param #string Heliname Name of the helicopter group. -- @param #string Heliname Name of the helicopter group.
-- @param #string Woundedgroupname Name of the downed pilot's group. -- @param #string Woundedgroupname Name of the downed pilot\'s group.
--- On After "Returning" event. Heli can return home with downed pilot(s). --- On After "Returning" event. Heli can return home with downed pilot(s).
-- @function [parent=#CSAR] OnAfterReturning -- @function [parent=#CSAR] OnAfterReturning
@@ -464,7 +480,7 @@ function CSAR:New( Coalition, Template, Alias )
-- @param #string Event Event. -- @param #string Event Event.
-- @param #string To To state. -- @param #string To To state.
-- @param #string Heliname Name of the helicopter group. -- @param #string Heliname Name of the helicopter group.
-- @param #string Woundedgroupname Name of the downed pilot's group. -- @param #string Woundedgroupname Name of the downed pilot\'s group.
--- On After "Rescued" event. Pilot(s) have been brought to the MASH/FARP/AFB. --- On After "Rescued" event. Pilot(s) have been brought to the MASH/FARP/AFB.
-- @function [parent=#CSAR] OnAfterRescued -- @function [parent=#CSAR] OnAfterRescued
@@ -501,8 +517,9 @@ end
-- @param #string Typename Typename of unit. -- @param #string Typename Typename of unit.
-- @param #number Frequency Frequency of the NDB in Hz -- @param #number Frequency Frequency of the NDB in Hz
-- @param #string Playername Name of Player (if applicable) -- @param #string Playername Name of Player (if applicable)
-- @param #boolean Wetfeet Ejected over water
-- @return #CSAR self. -- @return #CSAR self.
function CSAR:_CreateDownedPilotTrack( Group, Groupname, Side, OriginalUnit, Description, Typename, Frequency, Playername ) function CSAR:_CreateDownedPilotTrack(Group,Groupname,Side,OriginalUnit,Description,Typename,Frequency,Playername,Wetfeet)
self:T({"_CreateDownedPilotTrack",Groupname,Side,OriginalUnit,Description,Typename,Frequency,Playername}) self:T({"_CreateDownedPilotTrack",Groupname,Side,OriginalUnit,Description,Typename,Frequency,Playername})
-- create new entry -- create new entry
@@ -518,6 +535,7 @@ function CSAR:_CreateDownedPilotTrack( Group, Groupname, Side, OriginalUnit, Des
DownedPilot.group = Group DownedPilot.group = Group
DownedPilot.timestamp = 0 DownedPilot.timestamp = 0
DownedPilot.alive = true DownedPilot.alive = true
DownedPilot.wetfeet = Wetfeet or false
-- Add Pilot -- Add Pilot
local PilotTable = self.downedPilots local PilotTable = self.downedPilots
@@ -567,19 +585,23 @@ end
-- @param #number country Country for template. -- @param #number country Country for template.
-- @param Core.Point#COORDINATE point Coordinate to spawn at. -- @param Core.Point#COORDINATE point Coordinate to spawn at.
-- @param #number frequency Frequency of the pilot's beacon -- @param #number frequency Frequency of the pilot's beacon
-- @param #boolean wetfeet Spawn is over water
-- @return Wrapper.Group#GROUP group The #GROUP object. -- @return Wrapper.Group#GROUP group The #GROUP object.
-- @return #string alias The alias name. -- @return #string alias The alias name.
function CSAR:_SpawnPilotInField( country, point, frequency ) function CSAR:_SpawnPilotInField(country,point,frequency,wetfeet)
self:T( { country, point, frequency } ) self:T({country,point,frequency,tostring(wetfeet)})
local freq = frequency or 1000 local freq = frequency or 1000
local freq = freq / 1000 -- kHz local freq = freq / 1000 -- kHz
for i=1,10 do for i=1,10 do
math.random(i,10000) math.random(i,10000)
end end
if point:IsSurfaceTypeWater() then if point:IsSurfaceTypeWater() or wetfeet then
point.y = 0 point.y = 0
end end
local template = self.template local template = self.template
if self.usewetfeet and wetfeet then
template = self.wetfeettemplate
end
local alias = string.format("Pilot %.2fkHz-%d", freq, math.random(1,99)) local alias = string.format("Pilot %.2fkHz-%d", freq, math.random(1,99))
local coalition = self.coalition local coalition = self.coalition
local pilotcacontrol = self.allowDownedPilotCAcontrol -- Switch AI on/oof - is this really correct for CA? local pilotcacontrol = self.allowDownedPilotCAcontrol -- Switch AI on/oof - is this really correct for CA?
@@ -607,8 +629,8 @@ function CSAR:_AddSpecialOptions( group )
local _setImmortal = { local _setImmortal = {
id = 'SetImmortal', id = 'SetImmortal',
params = { params = {
value = true, value = true
}, }
} }
group:SetCommand(_setImmortal) group:SetCommand(_setImmortal)
end end
@@ -617,8 +639,8 @@ function CSAR:_AddSpecialOptions( group )
local _setInvisible = { local _setInvisible = {
id = 'SetInvisible', id = 'SetInvisible',
params = { params = {
value = true, value = true
}, }
} }
group:SetCommand(_setInvisible) group:SetCommand(_setInvisible)
end end
@@ -645,15 +667,19 @@ function CSAR:_AddCsar( _coalition, _country, _point, _typeName, _unitName, _pla
self:T({_coalition , _country, _point, _typeName, _unitName, _playerName, _freq, noMessage, _description}) self:T({_coalition , _country, _point, _typeName, _unitName, _playerName, _freq, noMessage, _description})
local template = self.template local template = self.template
local wetfeet = false
local surface = _point:GetSurfaceType()
if surface == land.SurfaceType.WATER then
wetfeet = true
end
if not _freq then if not _freq then
_freq = self:_GenerateADFFrequency() _freq = self:_GenerateADFFrequency()
if not _freq then if not _freq then _freq = 333000 end --noob catch
_freq = 333000
end -- noob catch
end end
local _spawnedGroup, _alias = self:_SpawnPilotInField( _country, _point, _freq ) local _spawnedGroup, _alias = self:_SpawnPilotInField(_country,_point,_freq,wetfeet)
local _typeName = _typeName or "Pilot" local _typeName = _typeName or "Pilot"
@@ -691,7 +717,7 @@ function CSAR:_AddCsar( _coalition, _country, _point, _typeName, _unitName, _pla
local _GroupName = _spawnedGroup:GetName() or _alias local _GroupName = _spawnedGroup:GetName() or _alias
self:_CreateDownedPilotTrack( _spawnedGroup, _GroupName, _coalition, _unitName, _text, _typeName, _freq, _playerName ) self:_CreateDownedPilotTrack(_spawnedGroup,_GroupName,_coalition,_unitName,_text,_typeName,_freq,_playerName,wetfeet)
self:_InitSARForPilot(_spawnedGroup, _unitName, _freq, noMessage) --shagrat use unitName to have the aircraft callsign / descriptive "name" etc. self:_InitSARForPilot(_spawnedGroup, _unitName, _freq, noMessage) --shagrat use unitName to have the aircraft callsign / descriptive "name" etc.
@@ -700,18 +726,27 @@ end
--- (Internal) Function to add a CSAR object into the scene at a zone coordinate. For mission designers wanting to add e.g. PoWs to the scene. --- (Internal) Function to add a CSAR object into the scene at a zone coordinate. For mission designers wanting to add e.g. PoWs to the scene.
-- @param #CSAR self -- @param #CSAR self
-- @param #string _zone Name of the zone. -- @param #string _zone Name of the zone. Can also be passed as a (normal, round) ZONE object.
-- @param #number _coalition Coalition. -- @param #number _coalition Coalition.
-- @param #string _description (optional) Description. -- @param #string _description (optional) Description.
-- @param #boolean _randomPoint (optional) Random yes or no. -- @param #boolean _randomPoint (optional) Random yes or no.
-- @param #boolean _nomessage (optional) If true, don't send a message to SAR. -- @param #boolean _nomessage (optional) If true, don\'t send a message to SAR.
-- @param #string unitname (optional) Name of the lost unit. -- @param #string unitname (optional) Name of the lost unit.
-- @param #string typename (optional) Type of plane. -- @param #string typename (optional) Type of plane.
-- @param #boolean forcedesc (optional) Force to use the description passed only for the pilot track entry. Use to have fully custom names. -- @param #boolean forcedesc (optional) Force to use the description passed only for the pilot track entry. Use to have fully custom names.
function CSAR:_SpawnCsarAtZone( _zone, _coalition, _description, _randomPoint, _nomessage, unitname, typename, forcedesc) function CSAR:_SpawnCsarAtZone( _zone, _coalition, _description, _randomPoint, _nomessage, unitname, typename, forcedesc)
self:T(self.lid .. " _SpawnCsarAtZone") self:T(self.lid .. " _SpawnCsarAtZone")
local freq = self:_GenerateADFFrequency() local freq = self:_GenerateADFFrequency()
local _triggerZone = ZONE:New( _zone ) -- trigger to use as reference position
local _triggerZone = nil
if type(_zone) == "string" then
_triggerZone = ZONE:New(_zone) -- trigger to use as reference position
elseif type(_zone) == "table" and _zone.ClassName then
if string.find(_zone.ClassName, "ZONE",1) then
_triggerZone = _zone -- is already a zone
end
end
if _triggerZone == nil then if _triggerZone == nil then
self:E(self.lid.."ERROR: Can\'t find zone called " .. _zone, 10) self:E(self.lid.."ERROR: Can\'t find zone called " .. _zone, 10)
return return
@@ -745,11 +780,11 @@ end
--- Function to add a CSAR object into the scene at a zone coordinate. For mission designers wanting to add e.g. PoWs to the scene. --- Function to add a CSAR object into the scene at a zone coordinate. For mission designers wanting to add e.g. PoWs to the scene.
-- @param #CSAR self -- @param #CSAR self
-- @param #string Zone Name of the zone. -- @param #string Zone Name of the zone. Can also be passed as a (normal, round) ZONE object.
-- @param #number Coalition Coalition. -- @param #number Coalition Coalition.
-- @param #string Description (optional) Description. -- @param #string Description (optional) Description.
-- @param #boolean RandomPoint (optional) Random yes or no. -- @param #boolean RandomPoint (optional) Random yes or no.
-- @param #boolean Nomessage (optional) If true, don't send a message to SAR. -- @param #boolean Nomessage (optional) If true, don\'t send a message to SAR.
-- @param #string Unitname (optional) Name of the lost unit. -- @param #string Unitname (optional) Name of the lost unit.
-- @param #string Typename (optional) Type of plane. -- @param #string Typename (optional) Type of plane.
-- @param #boolean Forcedesc (optional) Force to use the **description passed only** for the pilot track entry. Use to have fully custom names. -- @param #boolean Forcedesc (optional) Force to use the **description passed only** for the pilot track entry. Use to have fully custom names.
@@ -767,7 +802,7 @@ end
-- @param #string _Point a POINT_VEC2. -- @param #string _Point a POINT_VEC2.
-- @param #number _coalition Coalition. -- @param #number _coalition Coalition.
-- @param #string _description (optional) Description. -- @param #string _description (optional) Description.
-- @param #boolean _nomessage (optional) If true, don't send a message to SAR. -- @param #boolean _nomessage (optional) If true, don\'t send a message to SAR.
-- @param #string unitname (optional) Name of the lost unit. -- @param #string unitname (optional) Name of the lost unit.
-- @param #string typename (optional) Type of plane. -- @param #string typename (optional) Type of plane.
-- @param #boolean forcedesc (optional) Force to use the description passed only for the pilot track entry. Use to have fully custom names. -- @param #boolean forcedesc (optional) Force to use the description passed only for the pilot track entry. Use to have fully custom names.
@@ -801,7 +836,7 @@ end
-- @param #number Coalition Coalition. -- @param #number Coalition Coalition.
-- @param #string Description (optional) Description. -- @param #string Description (optional) Description.
-- @param #boolean addBeacon (optional) yes or no. -- @param #boolean addBeacon (optional) yes or no.
-- @param #boolean Nomessage (optional) If true, don't send a message to SAR. -- @param #boolean Nomessage (optional) If true, don\'t send a message to SAR.
-- @param #string Unitname (optional) Name of the lost unit. -- @param #string Unitname (optional) Name of the lost unit.
-- @param #string Typename (optional) Type of plane. -- @param #string Typename (optional) Type of plane.
-- @param #boolean Forcedesc (optional) Force to use the **description passed only** for the pilot track entry. Use to have fully custom names. -- @param #boolean Forcedesc (optional) Force to use the **description passed only** for the pilot track entry. Use to have fully custom names.
@@ -930,8 +965,18 @@ function CSAR:_EventHandler( EventData )
return return
end end
-- TODO: Over water check --- EVENTS.LandingAfterEjection NOT triggered by DCS, so handle csarUsePara = true case
-- might create dual pilots in edge cases
local wetfeet = false
local surface = _unit:GetCoordinate():GetSurfaceType()
if surface == land.SurfaceType.WATER then
wetfeet = true
end
-- all checks passed, get going. -- all checks passed, get going.
if self.csarUsePara == false then -- shagrat check parameter LandingAfterEjection, if true don't spawn a Pilot from EJECTION event, wait for the Chute to land if self.csarUsePara == false or (self.csarUsePara and wetfeet ) then --shagrat check parameter LandingAfterEjection, if true don't spawn a Pilot from EJECTION event, wait for the Chute to land
local _freq = self:_GenerateADFFrequency() local _freq = self:_GenerateADFFrequency()
self:_AddCsar(_coalition, _unit:GetCountry(), _unit:GetCoordinate() , _unit:GetTypeName(), _unit:GetName(), _event.IniPlayerName, _freq, false, "none") self:_AddCsar(_coalition, _unit:GetCountry(), _unit:GetCoordinate() , _unit:GetTypeName(), _unit:GetName(), _event.IniPlayerName, _freq, false, "none")
return true return true
@@ -1112,7 +1157,7 @@ function CSAR:_CheckWoundedGroupStatus( heliname, woundedgroupname )
if _distance < self.approachdist_near and _distance > 0 then if _distance < self.approachdist_near and _distance > 0 then
if self:_CheckCloseWoundedGroup(_distance, _heliUnit, _heliName, _woundedGroup, _woundedGroupName) == true then if self:_CheckCloseWoundedGroup(_distance, _heliUnit, _heliName, _woundedGroup, _woundedGroupName) == true then
-- we're close, reschedule -- we\'re close, reschedule
_downedpilot.timestamp = timer.getAbsTime() _downedpilot.timestamp = timer.getAbsTime()
self:__Approach(-5,heliname,woundedgroupname) self:__Approach(-5,heliname,woundedgroupname)
end end
@@ -1136,7 +1181,7 @@ function CSAR:_CheckWoundedGroupStatus( heliname, woundedgroupname )
end end
self.heliCloseMessage[_lookupKeyHeli] = nil self.heliCloseMessage[_lookupKeyHeli] = nil
self.landedStatus[_lookupKeyHeli] = nil self.landedStatus[_lookupKeyHeli] = nil
-- reschedule as units aren't dead yet , schedule for a bit slower though as we're far away --reschedule as units aren\'t dead yet , schedule for a bit slower though as we\'re far away
_downedpilot.timestamp = timer.getAbsTime() _downedpilot.timestamp = timer.getAbsTime()
self:__Approach(-10,heliname,woundedgroupname) self:__Approach(-10,heliname,woundedgroupname)
end end
@@ -1150,7 +1195,7 @@ function CSAR:_CheckWoundedGroupStatus( heliname, woundedgroupname )
return self return self
end end
--- (Internal) Function to pop a smoke at a wounded pilot's positions. --- (Internal) Function to pop a smoke at a wounded pilot\'s positions.
-- @param #CSAR self -- @param #CSAR self
-- @param #string _woundedGroupName Name of the group. -- @param #string _woundedGroupName Name of the group.
-- @param Wrapper.Group#GROUP _woundedLeader Object of the group. -- @param Wrapper.Group#GROUP _woundedLeader Object of the group.
@@ -1199,7 +1244,14 @@ function CSAR:_PickupUnit( _heliUnit, _pilotName, _woundedGroup, _woundedGroupNa
local found,downedgrouptable = self:_CheckNameInDownedPilots(_woundedGroupName) local found,downedgrouptable = self:_CheckNameInDownedPilots(_woundedGroupName)
local grouptable = downedgrouptable --#CSAR.DownedPilot local grouptable = downedgrouptable --#CSAR.DownedPilot
self.inTransitGroups[_heliName][_woundedGroupName] = { originalUnit = grouptable.originalUnit, woundedGroup = _woundedGroupName, side = self.coalition, desc = grouptable.desc, player = grouptable.player } self.inTransitGroups[_heliName][_woundedGroupName] =
{
originalUnit = grouptable.originalUnit,
woundedGroup = _woundedGroupName,
side = self.coalition,
desc = grouptable.desc,
player = grouptable.player,
}
_woundedGroup:Destroy(false) _woundedGroup:Destroy(false)
self:_RemoveNameFromDownedPilots(_woundedGroupName,true) self:_RemoveNameFromDownedPilots(_woundedGroupName,true)
@@ -1225,6 +1277,7 @@ function CSAR:_OrderGroupToMoveToPoint( _leader, _destination )
return self return self
end end
--- (internal) Function to check if the heli door(s) are open. Thanks to Shadowze. --- (internal) Function to check if the heli door(s) are open. Thanks to Shadowze.
-- @param #CSAR self -- @param #CSAR self
-- @param #string unit_name Name of unit. -- @param #string unit_name Name of unit.
@@ -1251,6 +1304,7 @@ function CSAR:_CheckCloseWoundedGroup( _distance, _heliUnit, _heliName, _wounded
local _found, _pilotable = self:_CheckNameInDownedPilots(_woundedGroupName) -- #boolean, #CSAR.DownedPilot local _found, _pilotable = self:_CheckNameInDownedPilots(_woundedGroupName) -- #boolean, #CSAR.DownedPilot
local _pilotName = _pilotable.desc local _pilotName = _pilotable.desc
local _reset = true local _reset = true
if (_distance < 500) then if (_distance < 500) then
@@ -1279,7 +1333,8 @@ function CSAR:_CheckCloseWoundedGroup( _distance, _heliUnit, _heliName, _wounded
_time = self.landedStatus[_lookupKeyHeli] - 10 _time = self.landedStatus[_lookupKeyHeli] - 10
self.landedStatus[_lookupKeyHeli] = _time self.landedStatus[_lookupKeyHeli] = _time
end end
if _time <= 0 or _distance < self.loadDistance then --if _time <= 0 or _distance < self.loadDistance then
if _distance < self.loadDistance + 5 or _distance <= 13 then
if self.pilotmustopendoors and not self:_IsLoadingDoorOpen(_heliName) then if self.pilotmustopendoors and not self:_IsLoadingDoorOpen(_heliName) then
self:_DisplayMessageToSAR(_heliUnit, "Open the door to let me in!", self.messageTime, true) self:_DisplayMessageToSAR(_heliUnit, "Open the door to let me in!", self.messageTime, true)
return true return true
@@ -1315,9 +1370,7 @@ function CSAR:_CheckCloseWoundedGroup( _distance, _heliUnit, _heliName, _wounded
--check height! --check height!
local leaderheight = _woundedLeader:GetHeight() local leaderheight = _woundedLeader:GetHeight()
if leaderheight < 0 then if leaderheight < 0 then leaderheight = 0 end
leaderheight = 0
end
local _height = _heliUnit:GetHeight() - leaderheight local _height = _heliUnit:GetHeight() - leaderheight
-- TODO - make variable -- TODO - make variable
@@ -1475,7 +1528,7 @@ function CSAR:_DisplayMessageToSAR( _unit, _text, _time, _clear, _speak, _overri
return self return self
end end
--- (Internal) Function to get string of a group's position. --- (Internal) Function to get string of a group\'s position.
-- @param #CSAR self -- @param #CSAR self
-- @param Wrapper.Controllable#CONTROLLABLE _woundedGroup Group or Unit object. -- @param Wrapper.Controllable#CONTROLLABLE _woundedGroup Group or Unit object.
-- @return #string Coordinates as Text -- @return #string Coordinates as Text
@@ -1605,9 +1658,7 @@ function CSAR:_SignalFlare( _unitName )
local _closest = self:_GetClosestDownedPilot(_heli) local _closest = self:_GetClosestDownedPilot(_heli)
local smokedist = 8000 local smokedist = 8000
if self.approachdist_far > smokedist then if self.approachdist_far > smokedist then smokedist = self.approachdist_far end
smokedist = self.approachdist_far
end
if _closest ~= nil and _closest.pilot ~= nil and _closest.distance > 0 and _closest.distance < smokedist then if _closest ~= nil and _closest.pilot ~= nil and _closest.distance > 0 and _closest.distance < smokedist then
local _clockDir = self:_GetClockDirection(_heli, _closest.pilot) local _clockDir = self:_GetClockDirection(_heli, _closest.pilot)
@@ -1661,9 +1712,7 @@ function CSAR:_Reqsmoke( _unitName )
return return
end end
local smokedist = 8000 local smokedist = 8000
if smokedist < self.approachdist_far then if smokedist < self.approachdist_far then smokedist = self.approachdist_far end
smokedist = self.approachdist_far
end
local _closest = self:_GetClosestDownedPilot(_heli) local _closest = self:_GetClosestDownedPilot(_heli)
if _closest ~= nil and _closest.pilot ~= nil and _closest.distance > 0 and _closest.distance < smokedist then if _closest ~= nil and _closest.pilot ~= nil and _closest.distance > 0 and _closest.distance < smokedist then
local _clockDir = self:_GetClockDirection(_heli, _closest.pilot) local _clockDir = self:_GetClockDirection(_heli, _closest.pilot)
@@ -1879,13 +1928,9 @@ function CSAR:_GetClockDirection( _heli, _group )
local clock = 12 local clock = 12
if _heading then if _heading then
local Aspect = Angle - _heading local Aspect = Angle - _heading
if Aspect == 0 then if Aspect == 0 then Aspect = 360 end
Aspect = 360
end
clock = math.abs(UTILS.Round((Aspect / 30),0)) clock = math.abs(UTILS.Round((Aspect / 30),0))
if clock == 0 then if clock == 0 then clock = 12 end
clock = 12
end
end end
return clock return clock
end end
@@ -1994,6 +2039,9 @@ function CSAR:onafterStart( From, Event, To )
self.allheligroupset = SET_GROUP:New():FilterCoalitions(self.coalitiontxt):FilterCategoryHelicopter():FilterStart() self.allheligroupset = SET_GROUP:New():FilterCoalitions(self.coalitiontxt):FilterCategoryHelicopter():FilterStart()
end end
self.mash = SET_GROUP:New():FilterCoalitions(self.coalitiontxt):FilterPrefixes(self.mashprefix):FilterStart() -- currently only GROUP objects, maybe support STATICs also? self.mash = SET_GROUP:New():FilterCoalitions(self.coalitiontxt):FilterPrefixes(self.mashprefix):FilterStart() -- currently only GROUP objects, maybe support STATICs also?
if self.wetfeettemplate then
self.usewetfeet = true
end
self:__Status(-10) self:__Status(-10)
return self return self
end end
@@ -2042,7 +2090,7 @@ function CSAR:onbeforeStatus( From, Event, To )
local name = entry.name local name = entry.name
local timestamp = entry.timestamp or 0 local timestamp = entry.timestamp or 0
local now = timer.getAbsTime() local now = timer.getAbsTime()
if now - timestamp > 17 then -- only check if we're not in approach mode, which is iterations of 5 and 10. if now - timestamp > 17 then -- only check if we\'re not in approach mode, which is iterations of 5 and 10.
self:_CheckWoundedGroupStatus(_sar,name) self:_CheckWoundedGroupStatus(_sar,name)
end end
end end
@@ -2074,7 +2122,8 @@ function CSAR:onafterStatus( From, Event, To )
end end
if self.verbose > 0 then if self.verbose > 0 then
local text = string.format( "%s Active SAR: %d | Downed Pilots in field: %d (max %d) | Pilots boarded: %d | Landings: %d | Pilots rescued: %d", self.lid, NumberOfSARPilots, PilotsInFieldN, self.maxdownedpilots, PilotsBoarded, self.rescues, self.rescuedpilots ) local text = string.format("%s Active SAR: %d | Downed Pilots in field: %d (max %d) | Pilots boarded: %d | Landings: %d | Pilots rescued: %d",
self.lid,NumberOfSARPilots,PilotsInFieldN,self.maxdownedpilots,PilotsBoarded,self.rescues,self.rescuedpilots)
self:T(text) self:T(text)
if self.verbose < 2 then if self.verbose < 2 then
self:I(text) self:I(text)
@@ -2112,7 +2161,7 @@ end
-- @param #string Event Event triggered. -- @param #string Event Event triggered.
-- @param #string To To state. -- @param #string To To state.
-- @param #string Heliname Name of the helicopter group. -- @param #string Heliname Name of the helicopter group.
-- @param #string Woundedgroupname Name of the downed pilot's group. -- @param #string Woundedgroupname Name of the downed pilot\'s group.
function CSAR:onbeforeApproach(From, Event, To, Heliname, Woundedgroupname) function CSAR:onbeforeApproach(From, Event, To, Heliname, Woundedgroupname)
self:T({From, Event, To, Heliname, Woundedgroupname}) self:T({From, Event, To, Heliname, Woundedgroupname})
self:_CheckWoundedGroupStatus(Heliname,Woundedgroupname) self:_CheckWoundedGroupStatus(Heliname,Woundedgroupname)
@@ -2125,7 +2174,7 @@ end
-- @param #string Event Event triggered. -- @param #string Event Event triggered.
-- @param #string To To state. -- @param #string To To state.
-- @param #string Heliname Name of the helicopter group. -- @param #string Heliname Name of the helicopter group.
-- @param #string Woundedgroupname Name of the downed pilot's group. -- @param #string Woundedgroupname Name of the downed pilot\'s group.
function CSAR:onbeforeBoarded(From, Event, To, Heliname, Woundedgroupname) function CSAR:onbeforeBoarded(From, Event, To, Heliname, Woundedgroupname)
self:T({From, Event, To, Heliname, Woundedgroupname}) self:T({From, Event, To, Heliname, Woundedgroupname})
self:_ScheduledSARFlight(Heliname,Woundedgroupname) self:_ScheduledSARFlight(Heliname,Woundedgroupname)
@@ -2138,7 +2187,7 @@ end
-- @param #string Event Event triggered. -- @param #string Event Event triggered.
-- @param #string To To state. -- @param #string To To state.
-- @param #string Heliname Name of the helicopter group. -- @param #string Heliname Name of the helicopter group.
-- @param #string Woundedgroupname Name of the downed pilot's group. -- @param #string Woundedgroupname Name of the downed pilot\'s group.
-- @param #boolean IsAirport True if heli has landed on an AFB (from event land). -- @param #boolean IsAirport True if heli has landed on an AFB (from event land).
function CSAR:onbeforeReturning(From, Event, To, Heliname, Woundedgroupname, IsAirPort) function CSAR:onbeforeReturning(From, Event, To, Heliname, Woundedgroupname, IsAirPort)
self:T({From, Event, To, Heliname, Woundedgroupname}) self:T({From, Event, To, Heliname, Woundedgroupname})

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,4 @@
--- This module contains derived utilities taken from the MIST framework, which are excellent tools to be reused in an OO environment. --- This module contains derived utilities taken from the MIST framework, as well as a lot of added helpers from the MOOSE community.
-- --
-- ### Authors: -- ### Authors:
-- --
@@ -7,10 +7,12 @@
-- ### Contributions: -- ### Contributions:
-- --
-- * FlightControl : Rework to OO framework. -- * FlightControl : Rework to OO framework.
-- * And many more
-- --
-- @module Utils -- @module Utils
-- @image MOOSE.JPG -- @image MOOSE.JPG
--- @type SMOKECOLOR --- @type SMOKECOLOR
-- @field Green -- @field Green
-- @field Red -- @field Red
@@ -57,9 +59,10 @@ DCSMAP = {
PersianGulf="PersianGulf", PersianGulf="PersianGulf",
TheChannel="TheChannel", TheChannel="TheChannel",
Syria="Syria", Syria="Syria",
MarianaIslands = "MarianaIslands", MarianaIslands="MarianaIslands"
} }
--- See [DCS_enum_callsigns](https://wiki.hoggitworld.com/view/DCS_enum_callsigns) --- See [DCS_enum_callsigns](https://wiki.hoggitworld.com/view/DCS_enum_callsigns)
-- @type CALLSIGN -- @type CALLSIGN
CALLSIGN={ CALLSIGN={
@@ -128,6 +131,62 @@ CALLSIGN = {
Dublin=9, Dublin=9,
Perth=10, Perth=10,
}, },
F16={
Viper=9,
Venom=10,
Lobo=11,
Cowboy=12,
Python=13,
Rattler=14,
Panther=15,
Wolf=16,
Weasel=17,
Wild=18,
Ninja=19,
Jedi=20,
},
F18={
Hornet=9,
Squid=10,
Ragin=11,
Roman=12,
Sting=13,
Jury=14,
Jokey=15,
Ram=16,
Hawk=17,
Devil=18,
Check=19,
Snake=20,
},
F15E={
Dude=9,
Thud=10,
Gunny=11,
Trek=12,
Sniper=13,
Sled=14,
Best=15,
Jazz=16,
Rage=17,
Tahoe=18,
},
B1B={
Bone=9,
Dark=10,
Vader=11
},
B52={
Buff=9,
Dump=10,
Kenworth=11,
},
TransportAircraft={
Heavy=9,
Trash=10,
Cargo=11,
Ascot=12,
},
} --#CALLSIGN } --#CALLSIGN
--- Utilities static class. --- Utilities static class.
@@ -199,6 +258,7 @@ UTILS.IsInstanceOf = function( object, className )
return false return false
end end
--- Deep copy a table. See http://lua-users.org/wiki/CopyTable --- Deep copy a table. See http://lua-users.org/wiki/CopyTable
-- @param #table object The input table. -- @param #table object The input table.
-- @return #table Copy of the input table. -- @return #table Copy of the input table.
@@ -230,6 +290,7 @@ UTILS.DeepCopy = function( object )
return objectreturn return objectreturn
end end
--- Porting in Slmod's serialize_slmod2. --- Porting in Slmod's serialize_slmod2.
-- @param #table tbl Input table. -- @param #table tbl Input table.
UTILS.OneLineSerialize = function( tbl ) -- serialization of a table all on a single line, no comments, made to replace old get_table_string function UTILS.OneLineSerialize = function( tbl ) -- serialization of a table all on a single line, no comments, made to replace old get_table_string function
@@ -322,6 +383,7 @@ UTILS.BasicSerialize = function( s )
end end
end end
UTILS.ToDegree = function(angle) UTILS.ToDegree = function(angle)
return angle*180/math.pi return angle*180/math.pi
end end
@@ -412,20 +474,20 @@ UTILS.KnotsToMps = function( knots )
end end
--- Convert temperature from Celsius to Fahrenheit. --- Convert temperature from Celsius to Fahrenheit.
-- @param #number Celsius Temperature in degrees Celsius. -- @param #number Celcius Temperature in degrees Celsius.
-- @return #number Temperature in degrees Fahrenheit. -- @return #number Temperature in degrees Fahrenheit.
UTILS.CelsiusToFahrenheit = function( Celsius ) UTILS.CelsiusToFahrenheit = function( Celcius )
return Celsius * 9 / 5 + 32 return Celcius * 9/5 + 32
end end
--- Convert pressure from hectopascal (hPa) to inches of mercury (inHg). --- Convert pressure from hecto Pascal (hPa) to inches of mercury (inHg).
-- @param #number hPa Pressure in hPa. -- @param #number hPa Pressure in hPa.
-- @return #number Pressure in inHg. -- @return #number Pressure in inHg.
UTILS.hPa2inHg = function( hPa ) UTILS.hPa2inHg = function( hPa )
return hPa * 0.0295299830714 return hPa * 0.0295299830714
end end
--- Convert knots to altitude corrected KIAS, e.g. for tankers. --- Convert knots to alitude corrected KIAS, e.g. for tankers.
-- @param #number knots Speed in knots. -- @param #number knots Speed in knots.
-- @param #number altitude Altitude in feet -- @param #number altitude Altitude in feet
-- @return #number Corrected KIAS -- @return #number Corrected KIAS
@@ -433,14 +495,14 @@ UTILS.KnotsToAltKIAS = function( knots, altitude )
return (knots * 0.018 * (altitude / 1000)) + knots return (knots * 0.018 * (altitude / 1000)) + knots
end end
--- Convert pressure from hectopascal (hPa) to millimeters of mercury (mmHg). --- Convert pressure from hecto Pascal (hPa) to millimeters of mercury (mmHg).
-- @param #number hPa Pressure in hPa. -- @param #number hPa Pressure in hPa.
-- @return #number Pressure in mmHg. -- @return #number Pressure in mmHg.
UTILS.hPa2mmHg = function( hPa ) UTILS.hPa2mmHg = function( hPa )
return hPa * 0.7500615613030 return hPa * 0.7500615613030
end end
--- Convert kilograms (kg) to pounds (lbs). --- Convert kilo gramms (kg) to pounds (lbs).
-- @param #number kg Mass in kg. -- @param #number kg Mass in kg.
-- @return #number Mass in lbs. -- @return #number Mass in lbs.
UTILS.kg2lbs = function( kg ) UTILS.kg2lbs = function( kg )
@@ -507,7 +569,8 @@ UTILS.tostringLL = function( lat, lon, acc, DMS )
end end
-- 024° 23' 12"N or 024° 23' 12.03"N -- 024° 23' 12"N or 024° 23' 12.03"N
return string.format( '%03d°', latDeg ) .. string.format( '%02d', latMin ) .. '\'' .. string.format( secFrmtStr, latSec ) .. '"' .. latHemi .. ' ' .. string.format( '%03d°', lonDeg ) .. string.format( '%02d', lonMin ) .. '\'' .. string.format( secFrmtStr, lonSec ) .. '"' .. lonHemi return string.format('%03d°', latDeg)..string.format('%02d', latMin)..'\''..string.format(secFrmtStr, latSec)..'"'..latHemi..' '
.. string.format('%03d°', lonDeg)..string.format('%02d', lonMin)..'\''..string.format(secFrmtStr, lonSec)..'"'..lonHemi
else -- degrees, decimal minutes. else -- degrees, decimal minutes.
latMin = UTILS.Round(latMin, acc) latMin = UTILS.Round(latMin, acc)
@@ -532,7 +595,8 @@ UTILS.tostringLL = function( lat, lon, acc, DMS )
end end
-- 024 23'N or 024 23.123'N -- 024 23'N or 024 23.123'N
return string.format( '%03d°', latDeg ) .. ' ' .. string.format( minFrmtStr, latMin ) .. '\'' .. latHemi .. ' ' .. string.format( '%03d°', lonDeg ) .. ' ' .. string.format( minFrmtStr, lonMin ) .. '\'' .. lonHemi return string.format('%03d°', latDeg) .. ' ' .. string.format(minFrmtStr, latMin) .. '\'' .. latHemi .. ' '
.. string.format('%03d°', lonDeg) .. ' ' .. string.format(minFrmtStr, lonMin) .. '\'' .. lonHemi
end end
end end
@@ -557,12 +621,8 @@ UTILS.tostringMGRS = function( MGRS, acc ) -- R2.1
local nN=5-string.len(Northing) local nN=5-string.len(Northing)
-- Get leading zeros (if any). -- Get leading zeros (if any).
for i = 1, nE do for i=1,nE do Easting="0"..Easting end
Easting = "0" .. Easting for i=1,nN do Northing="0"..Northing end
end
for i = 1, nN do
Northing = "0" .. Northing
end
-- Return MGRS string. -- Return MGRS string.
return string.format("%s %s %s %s", MGRS.UTMZone, MGRS.MGRSDigraph, string.sub(Easting, 1, acc), string.sub(Northing, 1, acc)) return string.format("%s %s %s %s", MGRS.UTMZone, MGRS.MGRSDigraph, string.sub(Easting, 1, acc), string.sub(Northing, 1, acc))
@@ -570,6 +630,7 @@ UTILS.tostringMGRS = function( MGRS, acc ) -- R2.1
end end
--- From http://lua-users.org/wiki/SimpleRound --- From http://lua-users.org/wiki/SimpleRound
-- use negative idp for rounding ahead of decimal place, positive for rounding after decimal place -- use negative idp for rounding ahead of decimal place, positive for rounding after decimal place
function UTILS.Round( num, idp ) function UTILS.Round( num, idp )
@@ -591,16 +652,12 @@ end
function UTILS.spairs( t, order ) function UTILS.spairs( t, order )
-- collect the keys -- collect the keys
local keys = {} local keys = {}
for k in pairs( t ) do for k in pairs(t) do keys[#keys+1] = k end
keys[#keys + 1] = k
end
-- if order function given, sort by it by passing the table and keys a, b, -- if order function given, sort by it by passing the table and keys a, b,
-- otherwise just sort the keys -- otherwise just sort the keys
if order then if order then
table.sort( keys, function( a, b ) table.sort(keys, function(a,b) return order(t, a, b) end)
return order( t, a, b )
end )
else else
table.sort(keys) table.sort(keys)
end end
@@ -615,22 +672,18 @@ function UTILS.spairs( t, order )
end end
end end
-- Here is a customized version of pairs, which I called kpairs because it iterates over the table in a sorted order, based on a function that will determine the keys as reference first. -- Here is a customized version of pairs, which I called kpairs because it iterates over the table in a sorted order, based on a function that will determine the keys as reference first.
function UTILS.kpairs( t, getkey, order ) function UTILS.kpairs( t, getkey, order )
-- collect the keys -- collect the keys
local keys = {} local keys = {}
local keyso = {} local keyso = {}
for k, o in pairs( t ) do for k, o in pairs(t) do keys[#keys+1] = k keyso[#keyso+1] = getkey( o ) end
keys[#keys + 1] = k
keyso[#keyso + 1] = getkey( o )
end
-- if order function given, sort by it by passing the table and keys a, b, -- if order function given, sort by it by passing the table and keys a, b,
-- otherwise just sort the keys -- otherwise just sort the keys
if order then if order then
table.sort( keys, function( a, b ) table.sort(keys, function(a,b) return order(t, a, b) end)
return order( t, a, b )
end )
else else
table.sort(keys) table.sort(keys)
end end
@@ -650,9 +703,7 @@ function UTILS.rpairs( t )
-- collect the keys -- collect the keys
local keys = {} local keys = {}
for k in pairs( t ) do for k in pairs(t) do keys[#keys+1] = k end
keys[#keys + 1] = k
end
local random = {} local random = {}
local j = #keys local j = #keys
@@ -691,6 +742,7 @@ function UTILS.RemoveMark( MarkID, Delay )
end end
end end
-- Test if a Vec2 is in a radius of another Vec2 -- Test if a Vec2 is in a radius of another Vec2
function UTILS.IsInRadius( InVec2, Vec2, Radius ) function UTILS.IsInRadius( InVec2, Vec2, Radius )
@@ -757,9 +809,9 @@ function UTILS.BeaufortScale( speed )
return bn,bd return bn,bd
end end
--- Split string at separators. C.f. http://stackoverflow.com/questions/1426954/split-string-in-lua --- Split string at seperators. C.f. http://stackoverflow.com/questions/1426954/split-string-in-lua
-- @param #string str Sting to split. -- @param #string str Sting to split.
-- @param #string sep Separator for split. -- @param #string sep Speparator for split.
-- @return #table Split text. -- @return #table Split text.
function UTILS.Split(str, sep) function UTILS.Split(str, sep)
local result = {} local result = {}
@@ -836,7 +888,7 @@ function UTILS.SecondsOfToday()
return UTILS.ClockToSeconds(clock) return UTILS.ClockToSeconds(clock)
end end
--- Count seconds until next midnight. --- Cound seconds until next midnight.
-- @return #number Seconds to midnight. -- @return #number Seconds to midnight.
function UTILS.SecondsToMidnight() function UTILS.SecondsToMidnight()
return 24*60*60-UTILS.SecondsOfToday() return 24*60*60-UTILS.SecondsOfToday()
@@ -987,6 +1039,7 @@ function UTILS.Vec2Dot( a, b )
return a.x*b.x + a.y*b.y return a.x*b.x + a.y*b.y
end end
--- Calculate the [euclidean norm](https://en.wikipedia.org/wiki/Euclidean_distance) (length) of a 3D vector. --- Calculate the [euclidean norm](https://en.wikipedia.org/wiki/Euclidean_distance) (length) of a 3D vector.
-- @param DCS#Vec3 a Vector in 3D with x, y, z components. -- @param DCS#Vec3 a Vector in 3D with x, y, z components.
-- @return #number Norm of the vector. -- @return #number Norm of the vector.
@@ -1014,6 +1067,7 @@ function UTILS.VecDist2D( a, b )
return d return d
end end
--- Calculate the distance between two 3D vectors. --- Calculate the distance between two 3D vectors.
-- @param DCS#Vec3 a Vector in 3D with x, y, z components. -- @param DCS#Vec3 a Vector in 3D with x, y, z components.
-- @param DCS#Vec3 b Vector in 3D with x, y, z components. -- @param DCS#Vec3 b Vector in 3D with x, y, z components.
@@ -1043,6 +1097,14 @@ function UTILS.VecSubstract( a, b )
return {x=a.x-b.x, y=a.y-b.y, z=a.z-b.z} return {x=a.x-b.x, y=a.y-b.y, z=a.z-b.z}
end end
--- Calculate the difference between two 2D vectors by substracting the x,y components from each other.
-- @param DCS#Vec2 a Vector in 2D with x, y components.
-- @param DCS#Vec2 b Vector in 2D with x, y components.
-- @return DCS#Vec2 Vector c=a-b with c(i)=a(i)-b(i), i=x,y.
function UTILS.Vec2Substract(a, b)
return {x=a.x-b.x, y=a.y-b.y}
end
--- Calculate the total vector of two 3D vectors by adding the x,y,z components of each other. --- Calculate the total vector of two 3D vectors by adding the x,y,z components of each other.
-- @param DCS#Vec3 a Vector in 3D with x, y, z components. -- @param DCS#Vec3 a Vector in 3D with x, y, z components.
-- @param DCS#Vec3 b Vector in 3D with x, y, z components. -- @param DCS#Vec3 b Vector in 3D with x, y, z components.
@@ -1051,6 +1113,14 @@ function UTILS.VecAdd( a, b )
return {x=a.x+b.x, y=a.y+b.y, z=a.z+b.z} return {x=a.x+b.x, y=a.y+b.y, z=a.z+b.z}
end end
--- Calculate the total vector of two 2D vectors by adding the x,y components of each other.
-- @param DCS#Vec2 a Vector in 2D with x, y components.
-- @param DCS#Vec2 b Vector in 2D with x, y components.
-- @return DCS#Vec2 Vector c=a+b with c(i)=a(i)+b(i), i=x,y.
function UTILS.Vec2Add(a, b)
return {x=a.x+b.x, y=a.y+b.y}
end
--- Calculate the angle between two 3D vectors. --- Calculate the angle between two 3D vectors.
-- @param DCS#Vec3 a Vector in 3D with x, y, z components. -- @param DCS#Vec3 a Vector in 3D with x, y, z components.
-- @param DCS#Vec3 b Vector in 3D with x, y, z components. -- @param DCS#Vec3 b Vector in 3D with x, y, z components.
@@ -1112,6 +1182,7 @@ function UTILS.HdgDiff( h1, h2 )
return math.abs(delta) return math.abs(delta)
end end
--- Translate 3D vector in the 2D (x,z) plane. y-component (usually altitude) unchanged. --- Translate 3D vector in the 2D (x,z) plane. y-component (usually altitude) unchanged.
-- @param DCS#Vec3 a Vector in 3D with x, y, z components. -- @param DCS#Vec3 a Vector in 3D with x, y, z components.
-- @param #number distance The distance to translate. -- @param #number distance The distance to translate.
@@ -1183,6 +1254,7 @@ function UTILS.Vec2Rotate2D( a, angle )
return A return A
end end
--- Converts a TACAN Channel/Mode couple into a frequency in Hz. --- Converts a TACAN Channel/Mode couple into a frequency in Hz.
-- @param #number TACANChannel The TACAN channel, i.e. the 10 in "10X". -- @param #number TACANChannel The TACAN channel, i.e. the 10 in "10X".
-- @param #string TACANMode The TACAN mode, i.e. the "X" in "10X". -- @param #string TACANMode The TACAN mode, i.e. the "X" in "10X".
@@ -1219,6 +1291,7 @@ function UTILS.TACANToFrequency( TACANChannel, TACANMode )
return (A + TACANChannel - B) * 1000000 return (A + TACANChannel - B) * 1000000
end end
--- Returns the DCS map/theatre as optained by env.mission.theatre --- Returns the DCS map/theatre as optained by env.mission.theatre
-- @return #string DCS map name. -- @return #string DCS map name.
function UTILS.GetDCSMap() function UTILS.GetDCSMap()
@@ -1264,26 +1337,6 @@ function UTILS.GetMissionDayOfYear( Time )
end 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. --- Returns the magnetic declination of the map.
-- Returned values for the current maps are: -- Returned values for the current maps are:
-- --
@@ -1353,7 +1406,8 @@ function UTILS.CheckMemory( output )
return mem return mem
end end
--- Get the coalition name from its numerical ID, e.g. coalition.side.RED.
--- Get the coalition name from its numerical ID, e.g. coaliton.side.RED.
-- @param #number Coalition The coalition ID. -- @param #number Coalition The coalition ID.
-- @return #string The coalition name, i.e. "Neutral", "Red" or "Blue" (or "Unknown"). -- @return #string The coalition name, i.e. "Neutral", "Red" or "Blue" (or "Unknown").
function UTILS.GetCoalitionName(Coalition) function UTILS.GetCoalitionName(Coalition)
@@ -1452,6 +1506,7 @@ function UTILS.GMTToLocalTimeDifference()
end end
--- Get the day of the year. Counting starts on 1st of January. --- Get the day of the year. Counting starts on 1st of January.
-- @param #number Year The year. -- @param #number Year The year.
-- @param #number Month The month. -- @param #number Month The month.
@@ -1485,31 +1540,18 @@ function UTILS.GetSunRiseAndSet( DayOfYear, Latitude, Longitude, Rising, Tlocal
local n=DayOfYear local n=DayOfYear
Tlocal=Tlocal or 0 Tlocal=Tlocal or 0
-- Short cuts. -- Short cuts.
local rad = math.rad local rad = math.rad
local deg = math.deg local deg = math.deg
local floor = math.floor local floor = math.floor
local frac = function( n ) local frac = function(n) return n - floor(n) end
return n - floor( n ) local cos = function(d) return math.cos(rad(d)) end
end local acos = function(d) return deg(math.acos(d)) end
local cos = function( d ) local sin = function(d) return math.sin(rad(d)) end
return math.cos( rad( d ) ) local asin = function(d) return deg(math.asin(d)) end
end local tan = function(d) return math.tan(rad(d)) end
local acos = function( d ) local atan = function(d) return deg(math.atan(d)) end
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 function fit_into_range(val, min, max)
local range = max - min local range = max - min
@@ -1695,9 +1737,25 @@ function UTILS.IsLoadingDoorOpen( unit_name )
ret_val = true ret_val = true
end end
if string.find(type_name, "UH-60L") and (unit:getDrawArgumentValue(401) == 1) or (unit:getDrawArgumentValue(402) == 1) then
BASE:T(unit_name .. " cargo door is open")
ret_val = true
end
if string.find(type_name, "UH-60L" ) and unit:getDrawArgumentValue(38) == 1 or unit:getDrawArgumentValue(400) == 1 then
BASE:T(unit_name .. " front door(s) are open")
ret_val = true
end
if string.find(type_name, "AH-64D") then
BASE:T(unit_name .. " front door(s) are open")
ret_val = true -- no doors on this one ;)
end
if ret_val == false then if ret_val == false then
BASE:T(unit_name .. " all doors are closed") BASE:T(unit_name .. " all doors are closed")
end end
return ret_val return ret_val
end -- nil end -- nil
@@ -1838,7 +1896,9 @@ function UTILS.GenerateLaserCodes()
while _code < 1777 and _count < 30 do while _code < 1777 and _count < 30 do
while true do while true do
_code = _code + 1 _code = _code + 1
if not ContainsDigit( _code, 8 ) and not ContainsDigit( _code, 9 ) and not ContainsDigit( _code, 0 ) then if not ContainsDigit(_code, 8)
and not ContainsDigit(_code, 9)
and not ContainsDigit(_code, 0) then
table.insert(jtacGeneratedLaserCodes, _code) table.insert(jtacGeneratedLaserCodes, _code)
break break
end end
@@ -2105,9 +2165,7 @@ end
-- @return #table Table of data objects (tables) containing groupname, coordinate and group object. Returns nil when file cannot be read. -- @return #table Table of data objects (tables) containing groupname, coordinate and group object. Returns nil when file cannot be read.
function UTILS.LoadStationaryListOfGroups(Path,Filename,Reduce) function UTILS.LoadStationaryListOfGroups(Path,Filename,Reduce)
local reduce = true local reduce = true
if Reduce == false then if Reduce == false then reduce = false end
reduce = false
end
local filename = Filename or "StateListofGroups" local filename = Filename or "StateListofGroups"
local datatable = {} local datatable = {}
if UTILS.CheckFileExists(Path,filename) then if UTILS.CheckFileExists(Path,filename) then
@@ -2126,9 +2184,8 @@ function UTILS.LoadStationaryListOfGroups( Path, Filename, Reduce )
local data = { groupname=groupname, size=size, coordinate=coordinate, group=GROUP:FindByName(groupname) } local data = { groupname=groupname, size=size, coordinate=coordinate, group=GROUP:FindByName(groupname) }
if reduce then if reduce then
local actualgroup = GROUP:FindByName(groupname) local actualgroup = GROUP:FindByName(groupname)
local actualsize = actualgroup:CountAliveUnits() if actualgroup and actualgroup:IsAlive() and actualgroup:CountAliveUnits() > size then
if actualsize > size then local reduction = actualgroup:CountAliveUnits() - size
local reduction = actualsize - size
BASE:I("Reducing groupsize by ".. reduction .. " units!") BASE:I("Reducing groupsize by ".. reduction .. " units!")
-- reduce existing group -- reduce existing group
local units = actualgroup:GetUnits() local units = actualgroup:GetUnits()
@@ -2154,9 +2211,7 @@ end
-- Returns nil when file cannot be read. Returns a table of data entries if Spawn is false: `{ groupname=groupname, size=size, coordinate=coordinate }` -- Returns nil when file cannot be read. Returns a table of data entries if Spawn is false: `{ groupname=groupname, size=size, coordinate=coordinate }`
function UTILS.LoadSetOfGroups(Path,Filename,Spawn) function UTILS.LoadSetOfGroups(Path,Filename,Spawn)
local spawn = true local spawn = true
if Spawn == false then if Spawn == false then spawn = false end
spawn = false
end
BASE:I("Spawn = "..tostring(spawn)) BASE:I("Spawn = "..tostring(spawn))
local filename = Filename or "SetOfGroups" local filename = Filename or "SetOfGroups"
local setdata = SET_GROUP:New() local setdata = SET_GROUP:New()
@@ -2179,7 +2234,10 @@ function UTILS.LoadSetOfGroups( Path, Filename, Spawn )
local data = { groupname=groupname, size=size, coordinate=coordinate } local data = { groupname=groupname, size=size, coordinate=coordinate }
table.insert(datatable,data) table.insert(datatable,data)
if spawn then if spawn then
local group = SPAWN:New( groupname ):InitDelayOff():OnSpawnGroup( function( spwndgrp ) local group = SPAWN:New(groupname)
:InitDelayOff()
:OnSpawnGroup(
function(spwndgrp)
setdata:AddObject(spwndgrp) setdata:AddObject(spwndgrp)
local actualsize = spwndgrp:CountAliveUnits() local actualsize = spwndgrp:CountAliveUnits()
if actualsize > size then if actualsize > size then
@@ -2191,7 +2249,9 @@ function UTILS.LoadSetOfGroups( Path, Filename, Spawn )
units2[i]:Destroy(false) units2[i]:Destroy(false)
end end
end end
end ):SpawnFromCoordinate( coordinate ) end
)
:SpawnFromCoordinate(coordinate)
end end
end end
else else
@@ -2239,9 +2299,7 @@ end
-- Returns nil when file cannot be read. -- Returns nil when file cannot be read.
function UTILS.LoadStationaryListOfStatics(Path,Filename,Reduce) function UTILS.LoadStationaryListOfStatics(Path,Filename,Reduce)
local reduce = true local reduce = true
if Reduce == false then if Reduce == false then reduce = false end
reduce = false
end
local filename = Filename or "StateListofStatics" local filename = Filename or "StateListofStatics"
local datatable = {} local datatable = {}
if UTILS.CheckFileExists(Path,filename) then if UTILS.CheckFileExists(Path,filename) then

View File

@@ -11,7 +11,6 @@
-- @module Wrapper.Airbase -- @module Wrapper.Airbase
-- @image Wrapper_Airbase.JPG -- @image Wrapper_Airbase.JPG
--- @type AIRBASE --- @type AIRBASE
-- @field #string ClassName Name of the class, i.e. "AIRBASE". -- @field #string ClassName Name of the class, i.e. "AIRBASE".
-- @field #table CategoryName Names of airbase categories. -- @field #table CategoryName Names of airbase categories.
@@ -52,7 +51,7 @@
-- * @{#AIRBASE.Find}(): Find a AIRBASE instance from the _DATABASE object using a DCS Airbase object. -- * @{#AIRBASE.Find}(): Find a AIRBASE instance from the _DATABASE object using a DCS Airbase object.
-- * @{#AIRBASE.FindByName}(): Find a AIRBASE instance from the _DATABASE object using a DCS Airbase name. -- * @{#AIRBASE.FindByName}(): Find a AIRBASE instance from the _DATABASE object using a DCS Airbase name.
-- --
-- IMPORTANT: ONE SHOULD NEVER SANATIZE these AIRBASE OBJECT REFERENCES! (make the AIRBASE object references nil). -- IMPORTANT: ONE SHOULD NEVER SANITIZE these AIRBASE OBJECT REFERENCES! (make the AIRBASE object references nil).
-- --
-- ## DCS Airbase APIs -- ## DCS Airbase APIs
-- --
@@ -385,6 +384,16 @@ AIRBASE.TheChannel = {
-- * AIRBASE.Syria.Beirut_Rafic_Hariri -- * AIRBASE.Syria.Beirut_Rafic_Hariri
-- * AIRBASE.Syria.An_Nasiriyah -- * AIRBASE.Syria.An_Nasiriyah
-- * AIRBASE.Syria.Abu_al_Duhur -- * AIRBASE.Syria.Abu_al_Duhur
-- * AIRBASE.Syria.At_Tanf
-- * AIRBASE.Syria.H3
-- * AIRBASE.Syria.H3_Northwest
-- * AIRBASE.Syria.H3_Southwest
-- * AIRBASE.Syria.Kharab_Ishk
-- * AIRBASE.Syria.Raj_al_Issa_East
-- * AIRBASE.Syria.Raj_al_Issa_West
-- * AIRBASE.Syria.Ruwayshid
-- * AIRBASE.Syria.Sanliurfa
-- * AIRBASE.Syria.Tal_Siman
-- --
--@field Syria --@field Syria
AIRBASE.Syria={ AIRBASE.Syria={
@@ -440,10 +449,18 @@ AIRBASE.Syria={
["Beirut_Rafic_Hariri"]="Beirut-Rafic Hariri", ["Beirut_Rafic_Hariri"]="Beirut-Rafic Hariri",
["An_Nasiriyah"]="An Nasiriyah", ["An_Nasiriyah"]="An Nasiriyah",
["Abu_al_Duhur"]="Abu al-Duhur", ["Abu_al_Duhur"]="Abu al-Duhur",
["At_Tanf"]="At Tanf",
["H3"]="H3",
["H3_Northwest"]="H3 Northwest",
["H3_Southwest"]="H3 Southwest",
["Kharab_Ishk"]="Kharab Ishk",
["Raj_al_Issa_East"]="Raj al Issa East",
["Raj_al_Issa_West"]="Raj al Issa West",
["Ruwayshid"]="Ruwayshid",
["Sanliurfa"]="Sanliurfa",
["Tal_Siman"]="Tal Siman",
} }
--- Airbases of the Mariana Islands map: --- Airbases of the Mariana Islands map:
-- --
-- * AIRBASE.MarianaIslands.Rota_Intl -- * AIRBASE.MarianaIslands.Rota_Intl
@@ -463,7 +480,6 @@ AIRBASE.MarianaIslands={
["Olf_Orote"] = "Olf Orote", ["Olf_Orote"] = "Olf Orote",
} }
--- AIRBASE.ParkingSpot ".Coordinate, ".TerminalID", ".TerminalType", ".TOAC", ".Free", ".TerminalID0", ".DistToRwy". --- AIRBASE.ParkingSpot ".Coordinate, ".TerminalID", ".TerminalType", ".TOAC", ".Free", ".TerminalID0", ".DistToRwy".
-- @type AIRBASE.ParkingSpot -- @type AIRBASE.ParkingSpot
-- @field Core.Point#COORDINATE Coordinate Coordinate of the parking spot. -- @field Core.Point#COORDINATE Coordinate Coordinate of the parking spot.
@@ -772,7 +788,6 @@ function AIRBASE:SetParkingSpotBlacklist(TerminalIdBlacklist)
return self return self
end end
--- Get category of airbase. --- Get category of airbase.
-- @param #AIRBASE self -- @param #AIRBASE self
-- @return #number Category of airbase from GetDesc().category. -- @return #number Category of airbase from GetDesc().category.
@@ -1112,8 +1127,7 @@ function AIRBASE:MarkParkingSpots(termtype, mark)
for _, _spot in pairs( parkingdata ) do for _, _spot in pairs( parkingdata ) do
-- Mark text. -- Mark text.
local _text=string.format("Term Index=%d, Term Type=%d, Free=%s, TOAC=%s, Term ID0=%d, Dist2Rwy=%.1f m", local _text = string.format( "Term Index=%d, Term Type=%d, Free=%s, TOAC=%s, Term ID0=%d, Dist2Rwy=%.1f m", _spot.TerminalID, _spot.TerminalType, tostring( _spot.Free ), tostring( _spot.TOAC ), _spot.TerminalID0, _spot.DistToRwy )
_spot.TerminalID, _spot.TerminalType,tostring(_spot.Free),tostring(_spot.TOAC),_spot.TerminalID0,_spot.DistToRwy)
-- Create mark on the F10 map. -- Create mark on the F10 map.
if mark then if mark then
@@ -1121,8 +1135,7 @@ function AIRBASE:MarkParkingSpots(termtype, mark)
end end
-- Info to DCS.log file. -- Info to DCS.log file.
local _text=string.format("%s, Term Index=%3d, Term Type=%03d, Free=%5s, TOAC=%5s, Term ID0=%3d, Dist2Rwy=%.1f m", local _text = string.format( "%s, Term Index=%3d, Term Type=%03d, Free=%5s, TOAC=%5s, Term ID0=%3d, Dist2Rwy=%.1f m", airbasename, _spot.TerminalID, _spot.TerminalType, tostring( _spot.Free ), tostring( _spot.TOAC ), _spot.TerminalID0, _spot.DistToRwy )
airbasename, _spot.TerminalID, _spot.TerminalType,tostring(_spot.Free),tostring(_spot.TOAC),_spot.TerminalID0,_spot.DistToRwy)
self:E( _text ) self:E( _text )
end end
end end
@@ -1197,7 +1210,6 @@ function AIRBASE:FindFreeParkingSpotForAircraft(group, terminaltype, scanradius,
az = 17 -- width az = 17 -- width
end end
-- Number of spots we are looking for. Note that, e.g. grouping can require a number different from the group size! -- Number of spots we are looking for. Note that, e.g. grouping can require a number different from the group size!
local _nspots = nspots or group:GetSize() local _nspots = nspots or group:GetSize()
@@ -1344,7 +1356,6 @@ function AIRBASE:_CheckParkingLists(TerminalID)
end end
end end
-- Check if a whitelist was defined. -- Check if a whitelist was defined.
if self.parkingWhitelist and #self.parkingWhitelist > 0 then if self.parkingWhitelist and #self.parkingWhitelist > 0 then
for _, terminalID in pairs( self.parkingWhitelist or {} ) do for _, terminalID in pairs( self.parkingWhitelist or {} ) do
@@ -1447,7 +1458,6 @@ function AIRBASE:GetRunwayData(magvar, mark)
-- Airbase name. -- Airbase name.
local name = self:GetName() local name = self:GetName()
-- Exceptions -- Exceptions
if name == AIRBASE.Nevada.Jean_Airport or if name == AIRBASE.Nevada.Jean_Airport or
name == AIRBASE.Nevada.Creech_AFB or name == AIRBASE.Nevada.Creech_AFB or
@@ -1535,7 +1545,6 @@ function AIRBASE:GetRunwayData(magvar, mark)
return j return j
end end
for i = 1, N do for i = 1, N do
-- Get the other spawn point coordinate. -- Get the other spawn point coordinate.

View File

@@ -3770,3 +3770,44 @@ function POSITIONABLE:IsSubmarine()
return nil return nil
end end
--- Sets the controlled group to go at the specified speed in meters per second.
-- @param #CONTROLLABLE self
-- @param #number Speed Speed in meters per second.
-- @param #boolean Keep (Optional) When set to true, will maintain the speed on passing waypoints. If not present or false, the controlled group will return to the speed as defined by their route.
-- @return #CONTROLLABLE self
function CONTROLLABLE:SetSpeed(Speed, Keep)
self:F2( { self.ControllableName } )
-- Set default if not specified.
local speed = Speed or 5
local DCSControllable = self:GetDCSObject()
if DCSControllable then
local Controller = self:_GetController()
if Controller then
Controller:setSpeed(speed, Keep)
end
end
return self
end
--- [AIR] Sets the controlled aircraft group to fly at the specified altitude in meters.
-- @param #CONTROLLABLE self
-- @param #number Altitude Altitude in meters.
-- @param #boolean Keep (Optional) When set to true, will maintain the altitude on passing waypoints. If not present or false, the controlled group will return to the altitude as defined by their route.
-- @param #string AltType (Optional) Specifies the altitude type used. If nil, the altitude type of the current waypoint will be used. Accepted values are "BARO" and "RADIO".
-- @return #CONTROLLABLE self
function CONTROLLABLE:SetAltitude(Altitude, Keep, AltType)
self:F2( { self.ControllableName } )
-- Set default if not specified.
local altitude = Altitude or 1000
local DCSControllable = self:GetDCSObject()
if DCSControllable then
local Controller = self:_GetController()
if Controller then
if self:IsAir() then
Controller:setAltitude(altitude, Keep, AltType)
end
end
end
return self
end

View File

@@ -2609,6 +2609,40 @@ function GROUP:GetSkill()
return skill return skill
end end
--- Get the unit in the group with the highest threat level, which is still alive.
-- @param #GROUP self
-- @return Wrapper.Unit#UNIT The most dangerous unit in the group.
-- @return #number Threat level of the unit.
function GROUP:GetHighestThreat()
-- Get units of the group.
local units=self:GetUnits()
if units then
local threat=nil ; local maxtl=0
for _,_unit in pairs(units or {}) do
local unit=_unit --Wrapper.Unit#UNIT
if unit and unit:IsAlive() then
-- Threat level of group.
local tl=unit:GetThreatLevel()
-- Check if greater the current threat.
if tl>maxtl then
maxtl=tl
threat=unit
end
end
end
return threat, maxtl
end
return nil, nil
end
--do -- Smoke --do -- Smoke
-- --
----- Signal a flare at the position of the GROUP. ----- Signal a flare at the position of the GROUP.

View File

@@ -10,8 +10,11 @@
-- --
-- @module Wrapper.Static -- @module Wrapper.Static
-- @image Wrapper_Static.JPG -- @image Wrapper_Static.JPG
--- @type STATIC --- @type STATIC
-- @extends Wrapper.Positionable#POSITIONABLE -- @extends Wrapper.Positionable#POSITIONABLE
--- Wrapper class to handle Static objects. --- Wrapper class to handle Static objects.
-- --
-- Note that Statics are almost the same as Units, but they don't have a controller. -- Note that Statics are almost the same as Units, but they don't have a controller.
@@ -37,10 +40,13 @@
-- --
-- * @{#STATIC.FindByName}(): Find a STATIC instance from the _DATABASE object using a DCS Static name. -- * @{#STATIC.FindByName}(): Find a STATIC instance from the _DATABASE object using a DCS Static name.
-- --
-- IMPORTANT: ONE SHOULD NEVER SANITIZE these STATIC OBJECT REFERENCES! (make the STATIC object references nil). -- IMPORTANT: ONE SHOULD NEVER SANATIZE these STATIC OBJECT REFERENCES! (make the STATIC object references nil).
-- --
-- @field #STATIC -- @field #STATIC
STATIC = { ClassName = "STATIC" } STATIC = {
ClassName = "STATIC",
}
--- Register a static object. --- Register a static object.
-- @param #STATIC self -- @param #STATIC self
@@ -52,6 +58,7 @@ function STATIC:Register( StaticName )
return self return self
end end
--- Finds a STATIC from the _DATABASE using a DCSStatic object. --- Finds a STATIC from the _DATABASE using a DCSStatic object.
-- @param #STATIC self -- @param #STATIC self
-- @param DCS#StaticObject DCSStatic An existing DCS Static object reference. -- @param DCS#StaticObject DCSStatic An existing DCS Static object reference.
@@ -90,9 +97,8 @@ end
--- Destroys the STATIC. --- Destroys the STATIC.
-- @param #STATIC self -- @param #STATIC self
-- @param #boolean GenerateEvent (Optional) true to generate a crash or dead event, false to not generate any event. `nil` (default) creates a remove event. -- @param #boolean GenerateEvent (Optional) true if you want to generate a crash or dead event for the static.
-- @return #nil The DCS StaticObject is not existing or alive. -- @return #nil The DCS StaticObject is not existing or alive.
--
-- @usage -- @usage
-- -- Air static example: destroy the static Helicopter and generate a S_EVENT_CRASH. -- -- Air static example: destroy the static Helicopter and generate a S_EVENT_CRASH.
-- Helicopter = STATIC:FindByName( "Helicopter" ) -- Helicopter = STATIC:FindByName( "Helicopter" )
@@ -111,7 +117,7 @@ end
-- @usage -- @usage
-- -- Destroy without event generation example. -- -- Destroy without event generation example.
-- Ship = STATIC:FindByName( "Boat" ) -- Ship = STATIC:FindByName( "Boat" )
-- Ship:Destroy( false ) -- Don't generate any event upon destruction. -- Ship:Destroy( false ) -- Don't generate an event upon destruction.
-- --
function STATIC:Destroy( GenerateEvent ) function STATIC:Destroy( GenerateEvent )
self:F2( self.ObjectName ) self:F2( self.ObjectName )
@@ -142,6 +148,7 @@ function STATIC:Destroy( GenerateEvent )
return nil return nil
end end
--- Get DCS object of static of static. --- Get DCS object of static of static.
-- @param #STATIC self -- @param #STATIC self
-- @return DCS static object -- @return DCS static object
@@ -173,6 +180,7 @@ function STATIC:GetUnits()
return nil return nil
end end
--- Get threat level of static. --- Get threat level of static.
-- @param #STATIC self -- @param #STATIC self
-- @return #number Threat level 1. -- @return #number Threat level 1.
@@ -203,6 +211,7 @@ function STATIC:SpawnAt( Coordinate, Heading, Delay )
return self return self
end end
--- Respawn the @{Wrapper.Unit} at the same location with the same properties. --- Respawn the @{Wrapper.Unit} at the same location with the same properties.
-- This is useful to respawn a cargo after it has been destroyed. -- This is useful to respawn a cargo after it has been destroyed.
-- @param #STATIC self -- @param #STATIC self
@@ -225,11 +234,12 @@ function STATIC:ReSpawn( CountryID, Delay )
return self return self
end end
--- Respawn the @{Wrapper.Unit} at a defined Coordinate with an optional heading. --- Respawn the @{Wrapper.Unit} at a defined Coordinate with an optional heading.
-- @param #STATIC self -- @param #STATIC self
-- @param Core.Point#COORDINATE Coordinate The coordinate where to spawn the new Static. -- @param Core.Point#COORDINATE Coordinate The coordinate where to spawn the new Static.
-- @param #number Heading (Optional) The heading of the static respawn in degrees. Default is the current heading. -- @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 is now. -- @param #number Delay (Optional) Delay in seconds before static is respawned. Default now.
function STATIC:ReSpawnAt(Coordinate, Heading, Delay) function STATIC:ReSpawnAt(Coordinate, Heading, Delay)
--Heading=Heading or 0 --Heading=Heading or 0
@@ -237,11 +247,12 @@ function STATIC:ReSpawnAt( Coordinate, Heading, Delay )
if Delay and Delay>0 then if Delay and Delay>0 then
SCHEDULER:New(nil, self.ReSpawnAt, {self, Coordinate, Heading}, Delay) SCHEDULER:New(nil, self.ReSpawnAt, {self, Coordinate, Heading}, Delay)
else else
local SpawnStatic=SPAWNSTATIC:NewFromStatic(self.StaticName, self:GetCountry()) local SpawnStatic=SPAWNSTATIC:NewFromStatic(self.StaticName, self:GetCountry())
SpawnStatic:SpawnFromCoordinate(Coordinate, Heading, self.StaticName) SpawnStatic:SpawnFromCoordinate(Coordinate, Heading, self.StaticName)
end end
return self return self
end end