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
@@ -2653,7 +2653,7 @@ do -- AI_A2A_DISPATCHER
-- -- Let flights by default land and despawn at engine shutdown. -- -- Let flights by default land and despawn at engine shutdown.
-- A2ADispatcher:SetDefaultLandingAtEngineShutdown() -- A2ADispatcher:SetDefaultLandingAtEngineShutdown()
-- --
function AI_A2A_DISPATCHER:SetDefaultLandingAtEngineShutdown() function AI_A2A_DISPATCHER:SetDefaultLandingAtEngineShutdown()
self:SetDefaultLanding( AI_A2A_DISPATCHER.Landing.AtEngineShutdown ) self:SetDefaultLanding( AI_A2A_DISPATCHER.Landing.AtEngineShutdown )
@@ -3876,7 +3876,31 @@ do
function AI_A2A_DISPATCHER:SchedulerCAP( SquadronName ) function AI_A2A_DISPATCHER:SchedulerCAP( SquadronName )
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

@@ -4728,6 +4728,30 @@ do
local PatrolTaskType = PatrolTaskTypes[math.random(1,3)] local PatrolTaskType = PatrolTaskTypes[math.random(1,3)]
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`

File diff suppressed because it is too large Load Diff

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
@@ -286,66 +290,69 @@
-- --
-- @field #RANGE -- @field #RANGE
RANGE = { RANGE = {
ClassName = "RANGE", ClassName = "RANGE",
Debug = false, Debug = false,
verbose = 0, verbose = 0,
id = nil, id = nil,
rangename = nil, rangename = nil,
location = nil, location = nil,
messages = true, messages = true,
rangeradius = 5000, rangeradius = 5000,
rangezone = nil, rangezone = nil,
strafeTargets = {}, strafeTargets = {},
bombingTargets = {}, bombingTargets = {},
nbombtargets = 0, nbombtargets = 0,
nstrafetargets = 0, nstrafetargets = 0,
MenuAddedTo = {}, MenuAddedTo = {},
planes = {}, planes = {},
strafeStatus = {}, strafeStatus = {},
strafePlayerResults = {}, strafePlayerResults = {},
bombPlayerResults = {}, bombPlayerResults = {},
PlayerSettings = {}, PlayerSettings = {},
dtBombtrack = 0.005, dtBombtrack = 0.005,
BombtrackThreshold = 25000, BombtrackThreshold = 25000,
Tmsg = 30, Tmsg = 30,
examinergroupname = nil, examinergroupname = nil,
examinerexclusive = nil, examinerexclusive = nil,
strafemaxalt = 914, strafemaxalt = 914,
ndisplayresult = 10, ndisplayresult = 10,
BombSmokeColor = SMOKECOLOR.Red, BombSmokeColor = SMOKECOLOR.Red,
StrafeSmokeColor = SMOKECOLOR.Green, StrafeSmokeColor = SMOKECOLOR.Green,
StrafePitSmokeColor = SMOKECOLOR.White, StrafePitSmokeColor = SMOKECOLOR.White,
illuminationminalt = 500, illuminationminalt = 500,
illuminationmaxalt = 1000, illuminationmaxalt = 1000,
scorebombdistance = 1000, scorebombdistance = 1000,
TdelaySmoke = 3.0, TdelaySmoke = 3.0,
eventmoose = true, eventmoose = true,
trackbombs = true, trackbombs = true,
trackrockets = true, trackrockets = true,
trackmissiles = true, trackmissiles = true,
defaultsmokebomb = true, defaultsmokebomb = true,
autosave = false, autosave = false,
instructorfreq = nil, instructorfreq = nil,
instructor = nil, instructor = nil,
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.
@@ -354,11 +361,19 @@ RANGE.Defaults = {
-- @field #string STATIC Target is a static. -- @field #string STATIC Target is a static.
-- @field #string COORD Target is a coordinate. -- @field #string COORD Target is a coordinate.
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,8 +1617,8 @@ 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.
@@ -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
@@ -2760,7 +2850,7 @@ function RANGE:_CheckInZone( _unitName )
if towardspit then if towardspit then
local vec3 = _unit:GetVec3() local vec3 = _unit:GetVec3()
local vec2 = { x = vec3.x, y = vec3.z } -- DCS#Vec2 local vec2 = { x = vec3.x, y = vec3.z } -- DCS#Vec2
local landheight = land.getHeight( vec2 ) local landheight = land.getHeight( vec2 )
local unitalt = vec3.y - landheight local unitalt = vec3.y - landheight
@@ -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,22 +2911,22 @@ 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"
_sound = RANGE.Sound.RCExcellentPass _sound=RANGE.Sound.RCExcellentPass
elseif _result.hits >= _result.zone.goodPass then elseif _result.hits >= _result.zone.goodPass then
_result.text = "GOOD PASS" _result.text = "GOOD PASS"
_sound = RANGE.Sound.RCGoodPass _sound=RANGE.Sound.RCGoodPass
elseif _result.hits >= _result.zone.goodPass / 2 then elseif _result.hits >= _result.zone.goodPass/2 then
_result.text = "INEFFECTIVE PASS" _result.text = "INEFFECTIVE PASS"
_sound = RANGE.Sound.RCIneffectivePass _sound=RANGE.Sound.RCIneffectivePass
else else
_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()
-- local csign = unit:GetCallsign()
-- TODO: Either remove these leftovers, or implement them.
-- 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,6 +261,186 @@ function SEAD:_GetDistance(_point1, _point2)
end end
end end
--- (Internal) Calculate hit zone of an AGM-88
-- @param #SEAD self
-- @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
function SEAD:onafterCalculateHitZone(From,Event,To,SEADWeapon,pos0,height,SEADGroup,SEADWeaponName)
self:T("**** Calculating hit zone for " .. (SEADWeaponName or "None"))
if SEADWeapon and SEADWeapon:isExist() then
--local pos = SEADWeapon:getPoint()
-- postion and height
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)
-- velocity
local wpndata = SEAD.HarmData["AGM_88"]
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 _targetskill = "Random"
if tgtgrp and tgtgrp:IsAlive() then
_targetgroup = tgtgrp
_targetgroupname = tgtgrp:GetName() -- group name
_targetskill = tgtgrp:GetUnit(1):GetSkill()
self:T("*** Found Target = ".. _targetgroupname)
self:ManageEvasion(_targetskill,_targetgroup,pos0,"AGM_88",SEADGroup, 20)
end
--end
end
end
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
local Skills = { "Average", "Good", "High", "Excellent" }
_targetskill = Skills[ math.random(1,4) ]
end
--self:T( _targetskill )
if self.TargetSkill[_targetskill] then
local _evade = math.random (1,100) -- random number for chance of evading action
if (_evade > self.TargetSkill[_targetskill].Evade) then
self:T("*** SEAD - Evading")
-- calculate distance of attacker
local _targetpos = _targetgroup:GetCoordinate()
local _distance = self:_GetDistance(SEADPlanePos, _targetpos)
-- weapon speed
local hit, data = self:_CheckHarms(SEADWeaponName)
local wpnspeed = 666 -- ;)
local reach = 10
if hit then
local wpndata = SEAD.HarmData[data]
reach = wpndata[1] * 1,1
local mach = wpndata[2]
wpnspeed = math.floor(mach * 340.29)
end
-- time to impact
local _tti = math.floor(_distance / wpnspeed) - timeoffset -- estimated impact time
if _distance > 0 then
_distance = math.floor(_distance / 1000) -- km
else
_distance = 0
end
self:T( string.format("*** SEAD - target skill %s, distance %dkm, reach %dkm, tti %dsec", _targetskill, _distance,reach,_tti ))
if reach >= _distance then
self:T("*** SEAD - Shot in Reach")
local function SuppressionStart(args)
self:T(string.format("*** SEAD - %s Radar Off & Relocating",args[2]))
local grp = args[1] -- Wrapper.Group#GROUP
local name = args[2] -- #string Group Name
local attacker = args[3] -- Wrapper.Group#GROUP
if self.UseEmissionsOnOff then
grp:EnableEmission(false)
end
grp:OptionAlarmStateGreen() -- needed else we cannot move around
grp:RelocateGroundRandomInRadius(20,300,false,false,"Diamond")
if self.UseCallBack then
local object = self.CallBack
object:SeadSuppressionStart(grp,name,attacker)
end
end
local function SuppressionStop(args)
self:T(string.format("*** SEAD - %s Radar On",args[2]))
local grp = args[1] -- Wrapper.Group#GROUP
local name = args[2] -- #string Group Nam
if self.UseEmissionsOnOff then
grp:EnableEmission(true)
end
grp:OptionAlarmStateRed()
grp:OptionEngageRange(self.EngagementRange)
self.SuppressedGroups[name] = false
if self.UseCallBack then
local object = self.CallBack
object:SeadSuppressionEnd(grp,name)
end
end
-- randomize switch-on time
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 _tti > 600 then delay = _tti - 90 end -- shot from afar, 600 is default shorad ontime
local SuppressionStartTime = timer.getTime() + delay
local SuppressionEndTime = timer.getTime() + _tti + self.Padding
local _targetgroupname = _targetgroup:GetName()
if not self.SuppressedGroups[_targetgroupname] then
self:T(string.format("*** SEAD - %s | Parameters TTI %ds | Switch-Off in %ds",_targetgroupname,_tti,delay))
timer.scheduleFunction(SuppressionStart,{_targetgroup,_targetgroupname, SEADGroup},SuppressionStartTime)
timer.scheduleFunction(SuppressionStop,{_targetgroup,_targetgroupname},SuppressionEndTime)
self.SuppressedGroups[_targetgroupname] = true
if self.UseCallBack then
local object = self.CallBack
object:SeadSuppressionPlanned(_targetgroup,_targetgroupname,SuppressionStartTime,SuppressionEndTime, SEADGroup)
end
end
end
end
end
return self
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) 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 #SEAD self
-- @param Core.Event#EVENTDATA EventData -- @param Core.Event#EVENTDATA EventData
@@ -256,6 +448,7 @@ end
function SEAD:HandleEventShot( EventData ) function SEAD:HandleEventShot( EventData )
self:T( { EventData.id } ) self:T( { EventData.id } )
local SEADPlane = EventData.IniUnit -- Wrapper.Unit#UNIT local SEADPlane = EventData.IniUnit -- Wrapper.Unit#UNIT
local SEADGroup = EventData.IniGroup -- Wrapper.Group#GROUP
local SEADPlanePos = SEADPlane:GetCoordinate() -- Core.Point#COORDINATE local SEADPlanePos = SEADPlane:GetCoordinate() -- Core.Point#COORDINATE
local SEADUnit = EventData.IniDCSUnit local SEADUnit = EventData.IniDCSUnit
local SEADUnitName = EventData.IniDCSUnitName local SEADUnitName = EventData.IniDCSUnitName
@@ -270,114 +463,55 @@ function SEAD:HandleEventShot( EventData )
local _targetskill = "Random" local _targetskill = "Random"
local _targetgroupname = "none" local _targetgroupname = "none"
local _target = EventData.Weapon:getTarget() -- Identify target local _target = EventData.Weapon:getTarget() -- Identify target
local _targetUnit = UNIT:Find(_target) -- Wrapper.Unit#UNIT 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 local _targetgroup = nil -- Wrapper.Group#GROUP
if _targetUnit and _targetUnit:IsAlive() then self:T(string.format("*** Targetcat = %d",targetcat))
_targetgroup = _targetUnit:GetGroup() if targetcat == Object.Category.UNIT then -- UNIT
_targetgroupname = _targetgroup:GetName() -- group name self:T("*** Target Category UNIT")
local _targetUnitName = _targetUnit:GetName() _targetUnit = UNIT:Find(_target) -- Wrapper.Unit#UNIT
_targetUnit:GetSkill() if _targetUnit and _targetUnit:IsAlive() then
_targetskill = _targetUnit:GetSkill() _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 end
-- see if we are shot at -- see if we are shot at
local SEADGroupFound = false local SEADGroupFound = false
for SEADGroupPrefixID, SEADGroupPrefix in pairs( self.SEADGroupPrefixes ) do for SEADGroupPrefixID, SEADGroupPrefix in pairs( self.SEADGroupPrefixes ) do
self:T( _targetgroupname, SEADGroupPrefix ) self:T("Target = ".. _targetgroupname .. " | Prefix = " .. SEADGroupPrefix )
if string.find( _targetgroupname, SEADGroupPrefix, 1, true ) then if string.find( _targetgroupname, SEADGroupPrefix,1,true ) then
SEADGroupFound = true SEADGroupFound = true
self:T( '*** SEAD - Group Match Found' ) self:T( '*** SEAD - Group Match Found' )
break break
end end
end end
if SEADGroupFound == true then -- yes we are being attacked if SEADGroupFound == true then -- yes we are being attacked
if _targetskill == "Random" then -- when skill is random, choose a skill self:ManageEvasion(_targetskill,_targetgroup,SEADPlanePos,SEADWeaponName,SEADGroup)
local Skills = { "Average", "Good", "High", "Excellent" }
_targetskill = Skills[ math.random(1,4) ]
end
--self:T( _targetskill )
if self.TargetSkill[_targetskill] then
local _evade = math.random (1,100) -- random number for chance of evading action
if (_evade > self.TargetSkill[_targetskill].Evade) then
self:T("*** SEAD - Evading")
-- calculate distance of attacker
local _targetpos = _targetgroup:GetCoordinate()
local _distance = self:_GetDistance(SEADPlanePos, _targetpos)
-- weapon speed
local hit, data = self:_CheckHarms(SEADWeaponName)
local wpnspeed = 666 -- ;)
local reach = 10
if hit then
local wpndata = SEAD.HarmData[data]
reach = wpndata[1] * 1,1
local mach = wpndata[2]
wpnspeed = math.floor(mach * 340.29)
end
-- time to impact
local _tti = math.floor(_distance / wpnspeed) -- estimated impact time
if _distance > 0 then
_distance = math.floor(_distance / 1000) -- km
else
_distance = 0
end
self:T( string.format("*** SEAD - target skill %s, distance %dkm, reach %dkm, tti %dsec", _targetskill, _distance,reach,_tti ))
if reach >= _distance then
self:T("*** SEAD - Shot in Reach")
local function SuppressionStart(args)
self:T(string.format("*** SEAD - %s Radar Off & Relocating",args[2]))
local grp = args[1] -- Wrapper.Group#GROUP
local name = args[2] -- #string Group Name
if self.UseEmissionsOnOff then
grp:EnableEmission(false)
end
grp:OptionAlarmStateGreen() -- needed else we cannot move around
grp:RelocateGroundRandomInRadius(20,300,false,false,"Diamond")
if self.UseCallBack then
local object = self.CallBack
object:SeadSuppressionStart(grp,name)
end
end
local function SuppressionStop(args)
self:T(string.format("*** SEAD - %s Radar On",args[2]))
local grp = args[1] -- Wrapper.Group#GROUP
local name = args[2] -- #string Group Nam
if self.UseEmissionsOnOff then
grp:EnableEmission(true)
end
grp:OptionAlarmStateAuto()
grp:OptionEngageRange(self.EngagementRange)
self.SuppressedGroups[name] = false
if self.UseCallBack then
local object = self.CallBack
object:SeadSuppressionEnd(grp,name)
end
end
-- randomize switch-on time
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 _tti > (3*delay) then delay = (_tti / 2) * 0.9 end -- shot from afar
local SuppressionStartTime = timer.getTime() + delay
local SuppressionEndTime = timer.getTime() + _tti + self.Padding
if not self.SuppressedGroups[_targetgroupname] then
self:T(string.format("*** SEAD - %s | Parameters TTI %ds | Switch-Off in %ds",_targetgroupname,_tti,delay))
timer.scheduleFunction(SuppressionStart,{_targetgroup,_targetgroupname},SuppressionStartTime)
timer.scheduleFunction(SuppressionStop,{_targetgroup,_targetgroupname},SuppressionEndTime)
self.SuppressedGroups[_targetgroupname] = true
if self.UseCallBack then
local object = self.CallBack
object:SeadSuppressionPlanned(_targetgroup,_targetgroupname,SuppressionStartTime,SuppressionEndTime)
end
end
end
end
end
end end
end end
return self return self

File diff suppressed because it is too large Load Diff

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
@@ -62,73 +63,129 @@ DCSMAP = {
--- See [DCS_enum_callsigns](https://wiki.hoggitworld.com/view/DCS_enum_callsigns) --- See [DCS_enum_callsigns](https://wiki.hoggitworld.com/view/DCS_enum_callsigns)
-- @type CALLSIGN -- @type CALLSIGN
CALLSIGN = { CALLSIGN={
-- Aircraft -- Aircraft
Aircraft = { Aircraft={
Enfield = 1, Enfield=1,
Springfield = 2, Springfield=2,
Uzi = 3, Uzi=3,
Colt = 4, Colt=4,
Dodge = 5, Dodge=5,
Ford = 6, Ford=6,
Chevy = 7, Chevy=7,
Pontiac = 8, Pontiac=8,
-- A-10A or A-10C -- A-10A or A-10C
Hawg = 9, Hawg=9,
Boar = 10, Boar=10,
Pig = 11, Pig=11,
Tusk = 12, Tusk=12,
}, },
-- AWACS -- AWACS
AWACS = { AWACS={
Overlord = 1, Overlord=1,
Magic = 2, Magic=2,
Wizard = 3, Wizard=3,
Focus = 4, Focus=4,
Darkstar = 5, Darkstar=5,
}, },
-- Tanker -- Tanker
Tanker = { Tanker={
Texaco = 1, Texaco=1,
Arco = 2, Arco=2,
Shell = 3, Shell=3,
}, },
-- JTAC -- JTAC
JTAC = { JTAC={
Axeman = 1, Axeman=1,
Darknight = 2, Darknight=2,
Warrior = 3, Warrior=3,
Pointer = 4, Pointer=4,
Eyeball = 5, Eyeball=5,
Moonbeam = 6, Moonbeam=6,
Whiplash = 7, Whiplash=7,
Finger = 8, Finger=8,
Pinpoint = 9, Pinpoint=9,
Ferret = 10, Ferret=10,
Shaba = 11, Shaba=11,
Playboy = 12, Playboy=12,
Hammer = 13, Hammer=13,
Jaguar = 14, Jaguar=14,
Deathstar = 15, Deathstar=15,
Anvil = 16, Anvil=16,
Firefly = 17, Firefly=17,
Mantis = 18, Mantis=18,
Badger = 19, Badger=19,
}, },
-- FARP -- FARP
FARP = { FARP={
London = 1, London=1,
Dallas = 2, Dallas=2,
Paris = 3, Paris=3,
Moscow = 4, Moscow=4,
Berlin = 5, Berlin=5,
Rome = 6, Rome=6,
Madrid = 7, Madrid=7,
Warsaw = 8, Warsaw=8,
Dublin = 9, Dublin=9,
Perth = 10, Perth=10,
}, },
} -- #CALLSIGN 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
--- Utilities static class. --- Utilities static class.
-- @type UTILS -- @type UTILS
@@ -1694,7 +1751,17 @@ function UTILS.IsLoadingDoorOpen( unit_name )
BASE:T( unit_name .. " door is open" ) BASE:T( unit_name .. " door is open" )
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

File diff suppressed because it is too large Load Diff

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.