Compare commits

...

32 Commits

Author SHA1 Message Date
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
15 changed files with 8233 additions and 6676 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

@@ -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
@@ -2667,7 +2755,7 @@ function RANGE:_DisplayRangeWeather( _unitname )
local tW = string.format( "%.1f m/s", Ws ) local tW = string.format( "%.1f m/s", Ws )
local tP = string.format( "%.1f mmHg", P * hPa2mmHg ) local tP = string.format( "%.1f mmHg", P * hPa2mmHg )
if settings:IsImperial() then if settings:IsImperial() then
-- tT=string.format("%d°F", UTILS.CelsiusToFahrenheit(T)) -- tT=string.format("%d°F", UTILS.CelciusToFarenheit(T))
tW = string.format( "%.1f knots", UTILS.MpsToKnots( Ws ) ) tW = string.format( "%.1f knots", UTILS.MpsToKnots( Ws ) )
tP = string.format( "%.2f inHg", P * hPa2inHg ) tP = string.format( "%.2f inHg", P * hPa2inHg )
end end
@@ -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,11 @@ 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 class version. --- CSAR class version.
-- @field #string version -- @field #string version
CSAR.version = "1.0.1r1" CSAR.version="1.0.4c"
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- ToDo list -- ToDo list
@@ -344,36 +355,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 +401,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 +461,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 +470,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 +479,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 +516,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 +534,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 +584,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 +628,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 +638,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 +666,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 +716,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 +725,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 +779,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 +801,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 +835,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 +964,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 +1156,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 +1180,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 +1194,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 +1243,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 +1276,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 +1303,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 +1332,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 +1369,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 +1527,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 +1657,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 +1711,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 +1927,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 +2038,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 +2089,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 +2121,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 +2160,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 +2173,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 +2186,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,6 +7,7 @@
-- ### 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
@@ -128,6 +129,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.
@@ -1695,6 +1752,16 @@ 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 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

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
-- --
@@ -442,8 +441,6 @@ AIRBASE.Syria={
["Abu_al_Duhur"] = "Abu al-Duhur", ["Abu_al_Duhur"] = "Abu al-Duhur",
} }
--- Airbases of the Mariana Islands map: --- Airbases of the Mariana Islands map:
-- --
-- * AIRBASE.MarianaIslands.Rota_Intl -- * AIRBASE.MarianaIslands.Rota_Intl
@@ -463,7 +460,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 +768,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 +1107,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 +1115,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 +1190,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 +1336,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 +1438,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 +1525,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

@@ -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.