mirror of
https://github.com/FlightControl-Master/MOOSE.git
synced 2025-08-15 10:47:21 +00:00
Compare commits
32 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4df1e310a3 | ||
|
|
802a77238a | ||
|
|
85a7e18fae | ||
|
|
26b1fd3487 | ||
|
|
ae7a363012 | ||
|
|
473362af45 | ||
|
|
cde0d09f0a | ||
|
|
94f093826b | ||
|
|
84f231ea08 | ||
|
|
3d9bb14713 | ||
|
|
6c6cdcf763 | ||
|
|
00c8690e61 | ||
|
|
a0d492cd2d | ||
|
|
ba5ccc1021 | ||
|
|
a4163017d5 | ||
|
|
7f4a5c48ec | ||
|
|
9f7588b245 | ||
|
|
63cbc0c55b | ||
|
|
28eb7a678c | ||
|
|
a95c49915a | ||
|
|
b7adc6add6 | ||
|
|
2aeebf280b | ||
|
|
8ac06979f0 | ||
|
|
2d4f90d5eb | ||
|
|
d7a44a639d | ||
|
|
7bfa05f47d | ||
|
|
c7bbb09195 | ||
|
|
41c9c15ae5 | ||
|
|
964831becf | ||
|
|
e847b92cce | ||
|
|
c2ecd86bb4 | ||
|
|
70d922fad6 |
@@ -1510,7 +1510,7 @@ do -- AI_A2A_DISPATCHER
|
||||
local Message = "Clearing (" .. DefenderTask.Type .. ") "
|
||||
Message = Message .. Defender:GetName()
|
||||
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
|
||||
self:F( { Target = Message } )
|
||||
end
|
||||
@@ -1559,7 +1559,7 @@ do -- AI_A2A_DISPATCHER
|
||||
|
||||
local Message = "(" .. self.DefenderTasks[Defender].Type .. ") "
|
||||
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 } )
|
||||
if AttackerDetection then
|
||||
self.DefenderTasks[Defender].Target = AttackerDetection
|
||||
@@ -2653,7 +2653,7 @@ do -- AI_A2A_DISPATCHER
|
||||
-- -- Let flights by default land and despawn at engine shutdown.
|
||||
-- A2ADispatcher:SetDefaultLandingAtEngineShutdown()
|
||||
--
|
||||
function AI_A2A_DISPATCHER:SetDefaultLandingAtEngineShutdown()
|
||||
function AI_A2A_DISPATCHER:SetDefaultLandingAtEngineShutdown()
|
||||
|
||||
self:SetDefaultLanding( AI_A2A_DISPATCHER.Landing.AtEngineShutdown )
|
||||
|
||||
@@ -3877,6 +3877,30 @@ do
|
||||
self:CAP( SquadronName )
|
||||
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
|
||||
|
||||
do
|
||||
|
||||
@@ -4729,5 +4729,29 @@ do
|
||||
self:Patrol( SquadronName, PatrolTaskType )
|
||||
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
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -317,7 +317,7 @@ do -- SET_BASE
|
||||
|
||||
for _, Object in pairs( union.Set ) do
|
||||
if self:IsIncludeObject( Object ) and SetB:IsIncludeObject( Object ) then
|
||||
intersection:AddObject( intersection )
|
||||
intersection:AddObject( Object )
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -1053,7 +1053,7 @@ end
|
||||
-- * `AIRBASE.Nevada.Lincoln_County`
|
||||
-- * `AIRBASE.Nevada.McCarran_International_Airport`
|
||||
-- * `AIRBASE.Nevada.Mesquite`
|
||||
-- * `AIRBASE.Nevada.Mina_Airport_3Q0`
|
||||
-- * `AIRBASE.Nevada.Mina_Airport`
|
||||
-- * `AIRBASE.Nevada.Nellis_AFB`
|
||||
-- * `AIRBASE.Nevada.North_Las_Vegas`
|
||||
-- * `AIRBASE.Nevada.Pahute_Mesa_Airstrip`
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1289,7 +1289,7 @@ end
|
||||
|
||||
--- Include all airports which lie in a zone as possible destinations.
|
||||
-- @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.
|
||||
function RAT:SetDestinationsFromZone(zone)
|
||||
self:F2(zone)
|
||||
@@ -1305,7 +1305,7 @@ end
|
||||
|
||||
--- Include all airports which lie in a zone as possible destinations.
|
||||
-- @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.
|
||||
function RAT:SetDeparturesFromZone(zone)
|
||||
self:F2(zone)
|
||||
|
||||
@@ -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 defaultsmokebomb If true, initialize player settings to smoke bomb.
|
||||
-- @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 #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 #string rangecontrolrelayname Name of relay unit.
|
||||
-- @field #string instructorrelayname Name of relay unit.
|
||||
-- @field #string soundpath Path inside miz file where the sound files are located. Default is "Range Soundfiles/".
|
||||
-- @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
|
||||
|
||||
--- *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
|
||||
-- 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.
|
||||
--
|
||||
-- # 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 (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.
|
||||
--
|
||||
-- ## Adding Groups
|
||||
@@ -260,11 +263,12 @@
|
||||
-- -- Add bombing targets. A good hit is if the bomb falls less then 50 m from the target.
|
||||
-- GoldwaterRange:AddBombingTargets(bombtargets, 50)
|
||||
--
|
||||
-- -- Start Range.
|
||||
-- -- Start range.
|
||||
-- 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.
|
||||
--
|
||||
--
|
||||
-- # 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
|
||||
@@ -286,66 +290,69 @@
|
||||
--
|
||||
-- @field #RANGE
|
||||
RANGE = {
|
||||
ClassName = "RANGE",
|
||||
Debug = false,
|
||||
verbose = 0,
|
||||
id = nil,
|
||||
rangename = nil,
|
||||
location = nil,
|
||||
messages = true,
|
||||
rangeradius = 5000,
|
||||
rangezone = nil,
|
||||
strafeTargets = {},
|
||||
bombingTargets = {},
|
||||
nbombtargets = 0,
|
||||
nstrafetargets = 0,
|
||||
MenuAddedTo = {},
|
||||
planes = {},
|
||||
strafeStatus = {},
|
||||
ClassName = "RANGE",
|
||||
Debug = false,
|
||||
verbose = 0,
|
||||
id = nil,
|
||||
rangename = nil,
|
||||
location = nil,
|
||||
messages = true,
|
||||
rangeradius = 5000,
|
||||
rangezone = nil,
|
||||
strafeTargets = {},
|
||||
bombingTargets = {},
|
||||
nbombtargets = 0,
|
||||
nstrafetargets = 0,
|
||||
MenuAddedTo = {},
|
||||
planes = {},
|
||||
strafeStatus = {},
|
||||
strafePlayerResults = {},
|
||||
bombPlayerResults = {},
|
||||
PlayerSettings = {},
|
||||
dtBombtrack = 0.005,
|
||||
BombtrackThreshold = 25000,
|
||||
Tmsg = 30,
|
||||
examinergroupname = nil,
|
||||
examinerexclusive = nil,
|
||||
strafemaxalt = 914,
|
||||
ndisplayresult = 10,
|
||||
BombSmokeColor = SMOKECOLOR.Red,
|
||||
StrafeSmokeColor = SMOKECOLOR.Green,
|
||||
bombPlayerResults = {},
|
||||
PlayerSettings = {},
|
||||
dtBombtrack = 0.005,
|
||||
BombtrackThreshold = 25000,
|
||||
Tmsg = 30,
|
||||
examinergroupname = nil,
|
||||
examinerexclusive = nil,
|
||||
strafemaxalt = 914,
|
||||
ndisplayresult = 10,
|
||||
BombSmokeColor = SMOKECOLOR.Red,
|
||||
StrafeSmokeColor = SMOKECOLOR.Green,
|
||||
StrafePitSmokeColor = SMOKECOLOR.White,
|
||||
illuminationminalt = 500,
|
||||
illuminationmaxalt = 1000,
|
||||
scorebombdistance = 1000,
|
||||
TdelaySmoke = 3.0,
|
||||
eventmoose = true,
|
||||
trackbombs = true,
|
||||
trackrockets = true,
|
||||
trackmissiles = true,
|
||||
defaultsmokebomb = true,
|
||||
autosave = false,
|
||||
instructorfreq = nil,
|
||||
instructor = nil,
|
||||
rangecontrolfreq = nil,
|
||||
rangecontrol = nil,
|
||||
soundpath = "Range Soundfiles/",
|
||||
illuminationminalt = 500,
|
||||
illuminationmaxalt = 1000,
|
||||
scorebombdistance = 1000,
|
||||
TdelaySmoke = 3.0,
|
||||
eventmoose = true,
|
||||
trackbombs = true,
|
||||
trackrockets = true,
|
||||
trackmissiles = true,
|
||||
defaultsmokebomb = true,
|
||||
autosave = false,
|
||||
instructorfreq = nil,
|
||||
instructor = nil,
|
||||
rangecontrolfreq = nil,
|
||||
rangecontrol = nil,
|
||||
soundpath = "Range Soundfiles/",
|
||||
targetsheet = nil,
|
||||
targetpath = nil,
|
||||
targetprefix = nil,
|
||||
}
|
||||
|
||||
--- Default range parameters.
|
||||
-- @list Defaults
|
||||
RANGE.Defaults = {
|
||||
goodhitrange = 25, -- meters
|
||||
strafemaxalt = 914, -- meters AGL
|
||||
dtBombtrack = 0.005, -- seconds
|
||||
Tmsg = 30, -- seconds
|
||||
goodhitrange = 25,
|
||||
strafemaxalt = 914,
|
||||
dtBombtrack = 0.005,
|
||||
Tmsg = 30,
|
||||
ndisplayresult = 10,
|
||||
rangeradius = 5000, -- meters
|
||||
TdelaySmoke = 3.0, -- seconds
|
||||
boxlength = 3000, -- meters
|
||||
boxwidth = 300, -- meters
|
||||
goodpass = 20, -- targethits per pass
|
||||
foulline = 610, -- meters
|
||||
rangeradius = 5000,
|
||||
TdelaySmoke = 3.0,
|
||||
boxlength = 3000,
|
||||
boxwidth = 300,
|
||||
goodpass = 20,
|
||||
foulline = 610
|
||||
}
|
||||
|
||||
--- Target type, i.e. unit, static, or coordinate.
|
||||
@@ -354,11 +361,19 @@ RANGE.Defaults = {
|
||||
-- @field #string STATIC Target is a static.
|
||||
-- @field #string COORD Target is a coordinate.
|
||||
RANGE.TargetType = {
|
||||
UNIT = "Unit",
|
||||
UNIT = "Unit",
|
||||
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.
|
||||
-- @type RANGE.PlayerData
|
||||
-- @field #boolean smokebombimpact Smoke bomb impact points.
|
||||
@@ -869,6 +884,22 @@ function RANGE:SetAutosaveOff()
|
||||
return self
|
||||
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.
|
||||
-- @param #RANGE self
|
||||
-- @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.
|
||||
-- @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
|
||||
function RANGE:SetDefaultPlayerSmokeBomb( switch )
|
||||
if switch == nil or switch == true then
|
||||
if switch == true or switch == nil then
|
||||
self.defaultsmokebomb = true
|
||||
else
|
||||
self.defaultsmokebomb = false
|
||||
@@ -1183,7 +1214,7 @@ function RANGE:AddStrafePit( targetnames, boxlength, boxwidth, heading, inverseh
|
||||
if heading < 0 then
|
||||
heading = heading + 360
|
||||
end
|
||||
if heading >= 360 then
|
||||
if heading > 360 then
|
||||
heading = heading - 360
|
||||
end
|
||||
|
||||
@@ -1246,7 +1277,7 @@ end
|
||||
-- @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 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 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
|
||||
@@ -1281,8 +1312,8 @@ end
|
||||
--- Add bombing target(s) to range.
|
||||
-- @param #RANGE self
|
||||
-- @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 #boolean randommove (Optional) If true, unit will move randomly within the range. Default is false.
|
||||
-- @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 If true, unit will move randomly within the range. Default is false.
|
||||
-- @return #RANGE self
|
||||
function RANGE:AddBombingTargets( targetnames, goodhitrange, randommove )
|
||||
self:F( { targetnames = targetnames, goodhitrange = goodhitrange, randommove = randommove } )
|
||||
@@ -1320,8 +1351,8 @@ end
|
||||
--- Add a unit or static object as bombing target.
|
||||
-- @param #RANGE self
|
||||
-- @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 #boolean randommove (Optional) If true, unit will move randomly within the range. Default is false.
|
||||
-- @param #number goodhitrange Max distance from unit which is considered as a good hit.
|
||||
-- @param #boolean randommove If true, unit will move randomly within the range. Default is false.
|
||||
-- @return #RANGE self
|
||||
function RANGE:AddBombingTargetUnit( unit, goodhitrange, randommove )
|
||||
self:F( { unit = unit, goodhitrange = goodhitrange, randommove = randommove } )
|
||||
@@ -1374,25 +1405,12 @@ function RANGE:AddBombingTargetUnit( unit, goodhitrange, randommove )
|
||||
return self
|
||||
end
|
||||
|
||||
--- Add a coordinate of a bombing target.
|
||||
--- Add a coordinate of a bombing target. This
|
||||
-- @param #RANGE self
|
||||
-- @param Core.Point#COORDINATE coord The coordinate.
|
||||
-- @param #string name (Optional) Name of target. Default is "Bomb 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 #string name Name of target.
|
||||
-- @param #number goodhitrange Max distance from unit which is considered as a good hit.
|
||||
-- @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 )
|
||||
|
||||
local target = {} -- #RANGE.BombTarget
|
||||
@@ -1413,8 +1431,8 @@ end
|
||||
--- Add all units of a group as bombing targets.
|
||||
-- @param #RANGE self
|
||||
-- @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 #boolean randommove (Optional) If true, unit will move randomly within the range. Default is false.
|
||||
-- @param #number goodhitrange Max distance from unit which is considered as a good hit.
|
||||
-- @param #boolean randommove If true, unit will move randomly within the range. Default is false.
|
||||
-- @return #RANGE self
|
||||
function RANGE:AddBombingTargetGroup( group, goodhitrange, randommove )
|
||||
self:F( { group = group, goodhitrange = goodhitrange, randommove = randommove } )
|
||||
@@ -1433,22 +1451,11 @@ function RANGE:AddBombingTargetGroup( group, goodhitrange, randommove )
|
||||
return self
|
||||
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 #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.
|
||||
-- @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 )
|
||||
self:F( { namepit = namepit, namefoulline = namefoulline } )
|
||||
|
||||
@@ -1573,6 +1580,7 @@ function RANGE:OnEventBirth( EventData )
|
||||
self:T3( self.id .. "BIRTH: player = " .. tostring( _playername ) )
|
||||
|
||||
if _unit and _playername then
|
||||
|
||||
local _uid = _unit:GetID()
|
||||
local _group = _unit:GetGroup()
|
||||
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.planes[_uid] = true
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
--- Range event handler for event hit.
|
||||
@@ -1672,6 +1680,7 @@ function RANGE:OnEventHit( EventData )
|
||||
self:_DisplayMessageToGroup( _unit, text )
|
||||
self:T2( self.id .. text )
|
||||
_currentTarget.pastfoulline = true
|
||||
invalidStrafe = true -- Rangeboss Edit
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1701,7 +1710,6 @@ function RANGE:OnEventHit( EventData )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- 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.
|
||||
if _track and dPR <= self.BombtrackThreshold and _unit and _playername then
|
||||
|
||||
-- Player data.
|
||||
local playerData = self.PlayerSettings[_playername] -- #RANGE.PlayerData
|
||||
|
||||
@@ -1843,7 +1852,9 @@ function RANGE:OnEventShot( EventData )
|
||||
_distance = _temp
|
||||
_closetTarget = _bombtarget
|
||||
_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"
|
||||
elseif _distance <= _bombtarget.goodhitrange then
|
||||
_hitquality = "GOOD"
|
||||
@@ -1876,6 +1887,10 @@ function RANGE:OnEventShot( EventData )
|
||||
result.player = playerData.playername
|
||||
result.time = timer.getAbsTime()
|
||||
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.
|
||||
table.insert( _results, result )
|
||||
@@ -1916,6 +1931,74 @@ end
|
||||
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- 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.
|
||||
-- @param #RANGE self
|
||||
@@ -2012,11 +2095,16 @@ end
|
||||
-- @param #RANGE.PlayerData player Player data table.
|
||||
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.
|
||||
local targetname = nil
|
||||
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
|
||||
text = text .. "."
|
||||
end
|
||||
@@ -2120,7 +2208,7 @@ function RANGE:onafterSave( From, Event, To )
|
||||
_savefile( filename, scores )
|
||||
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 #string From From state.
|
||||
-- @param #string Event Event.
|
||||
@@ -2489,7 +2577,7 @@ function RANGE:_DisplayRangeInfo( _unitname )
|
||||
local range = coord:Get2DDistance( position )
|
||||
|
||||
-- Bearing string.
|
||||
local Bs = string.format( "%03d°", angle )
|
||||
local Bs = string.format( '%03d°', angle )
|
||||
|
||||
local texthit
|
||||
if self.PlayerSettings[playername].flaredirecthits then
|
||||
@@ -2583,7 +2671,7 @@ function RANGE:_DisplayBombTargets( _unitname )
|
||||
end
|
||||
end
|
||||
|
||||
self:_DisplayMessageToGroup( _unit, _text, 60, true, true )
|
||||
self:_DisplayMessageToGroup( _unit, _text, 120, true, true )
|
||||
end
|
||||
end
|
||||
|
||||
@@ -2656,7 +2744,7 @@ function RANGE:_DisplayRangeWeather( _unitname )
|
||||
-- Get Beaufort wind scale.
|
||||
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 hPa2inHg = 0.0295299830714
|
||||
@@ -2667,7 +2755,7 @@ function RANGE:_DisplayRangeWeather( _unitname )
|
||||
local tW = string.format( "%.1f m/s", Ws )
|
||||
local tP = string.format( "%.1f mmHg", P * hPa2mmHg )
|
||||
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 ) )
|
||||
tP = string.format( "%.2f inHg", P * hPa2inHg )
|
||||
end
|
||||
@@ -2744,6 +2832,7 @@ function RANGE:_CheckInZone( _unitName )
|
||||
|
||||
-- Get player unit and name.
|
||||
local _unit, _playername = self:_GetPlayerUnitAndName( _unitName )
|
||||
local unitheading = 0 -- RangeBoss
|
||||
|
||||
if _unit and _playername then
|
||||
|
||||
@@ -2753,6 +2842,7 @@ function RANGE:_CheckInZone( _unitName )
|
||||
|
||||
-- Heading check.
|
||||
local unitheading = _unit:GetHeading()
|
||||
unitheadingStrafe = _unit:GetHeading() -- RangeBoss
|
||||
local pitheading = targetheading - 180
|
||||
local deltaheading = unitheading - pitheading
|
||||
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.
|
||||
if unitinzone then
|
||||
|
||||
StrafeAircraftType = _unit:GetTypeName() -- RangeBoss
|
||||
-- Still in zone, keep counting hits. Increase counter.
|
||||
_currentStrafeRun.time = _currentStrafeRun.time + 1
|
||||
|
||||
@@ -2821,22 +2911,22 @@ function RANGE:_CheckInZone( _unitName )
|
||||
-- Result.
|
||||
local _result = self.strafeStatus[_unitID]
|
||||
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.
|
||||
if _result.hits >= _result.zone.goodPass * 2 then
|
||||
if _result.hits >= _result.zone.goodPass*2 then
|
||||
_result.text = "EXCELLENT PASS"
|
||||
_sound = RANGE.Sound.RCExcellentPass
|
||||
_sound=RANGE.Sound.RCExcellentPass
|
||||
elseif _result.hits >= _result.zone.goodPass then
|
||||
_result.text = "GOOD PASS"
|
||||
_sound = RANGE.Sound.RCGoodPass
|
||||
elseif _result.hits >= _result.zone.goodPass / 2 then
|
||||
_sound=RANGE.Sound.RCGoodPass
|
||||
elseif _result.hits >= _result.zone.goodPass/2 then
|
||||
_result.text = "INEFFECTIVE PASS"
|
||||
_sound = RANGE.Sound.RCIneffectivePass
|
||||
_sound=RANGE.Sound.RCIneffectivePass
|
||||
else
|
||||
_result.text = "POOR PASS"
|
||||
_sound = RANGE.Sound.RCPoorPass
|
||||
_sound=RANGE.Sound.RCPoorPass
|
||||
end
|
||||
|
||||
]]
|
||||
-- Calculate accuracy of run. Number of hits wrt number of rounds fired.
|
||||
local shots = _result.ammo - _ammo
|
||||
local accur = 0
|
||||
@@ -2847,6 +2937,29 @@ function RANGE:_CheckInZone( _unitName )
|
||||
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.
|
||||
local _text = string.format( "%s, hits on target %s: %d", self:_myname( _unitName ), _result.zone.name, _result.hits )
|
||||
if shots and accur then
|
||||
@@ -2857,6 +2970,45 @@ function RANGE:_CheckInZone( _unitName )
|
||||
-- Send message.
|
||||
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.
|
||||
if self.rangecontrol then
|
||||
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
|
||||
self.rangecontrol:NewTransmission( RANGE.Sound.RCRollingInOnStrafeTarget.filename, RANGE.Sound.RCRollingInOnStrafeTarget.duration, self.soundpath )
|
||||
end
|
||||
clientRollingIn = true -- RANGEBOSS
|
||||
|
||||
-- Send message.
|
||||
self:_DisplayMessageToGroup( _unit, _msg, 10, true )
|
||||
hypemanStrafeRollIn = _msg -- RANGEBOSS
|
||||
|
||||
-- We found our player. Skip remaining checks.
|
||||
break
|
||||
@@ -2959,7 +3113,8 @@ function RANGE:_AddF10Commands( _unitName )
|
||||
-- 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
|
||||
|
||||
@@ -2969,54 +3124,57 @@ function RANGE:_AddF10Commands( _unitName )
|
||||
|
||||
-- Main F10 menu: F10/On the Range/<Range Name>/
|
||||
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
|
||||
_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
|
||||
|
||||
local _statsPath = missionCommands.addSubMenuForGroup( _gid, "Statistics", _rangePath )
|
||||
local _markPath = missionCommands.addSubMenuForGroup( _gid, "Mark Targets", _rangePath )
|
||||
local _settingsPath = missionCommands.addSubMenuForGroup( _gid, "My Settings", _rangePath )
|
||||
local _infoPath = missionCommands.addSubMenuForGroup( _gid, "Range Info", _rangePath )
|
||||
local _statsPath = MENU_GROUP:New( group, "Statistics", _rangePath )
|
||||
local _markPath = MENU_GROUP:New( group, "Mark Targets", _rangePath )
|
||||
local _settingsPath = MENU_GROUP:New( group, "My Settings", _rangePath )
|
||||
local _infoPath = MENU_GROUP:New( group, "Range Info", _rangePath )
|
||||
|
||||
-- F10/On the Range/<Range Name>/My Settings/
|
||||
local _mysmokePath = missionCommands.addSubMenuForGroup( _gid, "Smoke Color", _settingsPath )
|
||||
local _myflarePath = missionCommands.addSubMenuForGroup( _gid, "Flare Color", _settingsPath )
|
||||
local _mysmokePath = MENU_GROUP:New( group, "Smoke Color", _settingsPath )
|
||||
local _myflarePath = MENU_GROUP:New( group, "Flare Color", _settingsPath )
|
||||
|
||||
-- F10/On the Range/<Range Name>/Mark Targets/
|
||||
missionCommands.addCommandForGroup( _gid, "Mark On Map", _markPath, self._MarkTargetsOnMap, self, _unitName )
|
||||
missionCommands.addCommandForGroup( _gid, "Illuminate Range", _markPath, self._IlluminateBombTargets, self, _unitName )
|
||||
missionCommands.addCommandForGroup( _gid, "Smoke Strafe Pits", _markPath, self._SmokeStrafeTargetBoxes, self, _unitName )
|
||||
missionCommands.addCommandForGroup( _gid, "Smoke Strafe Tgts", _markPath, self._SmokeStrafeTargets, self, _unitName )
|
||||
missionCommands.addCommandForGroup( _gid, "Smoke Bomb Tgts", _markPath, self._SmokeBombTargets, self, _unitName )
|
||||
local _MoMap = MENU_GROUP_COMMAND:New( group, "Mark On Map", _markPath, self._MarkTargetsOnMap, self, _unitName )
|
||||
local _IllRng = MENU_GROUP_COMMAND:New( group, "Illuminate Range", _markPath, self._IlluminateBombTargets, self, _unitName )
|
||||
local _SSpit = MENU_GROUP_COMMAND:New( group, "Smoke Strafe Pits", _markPath, self._SmokeStrafeTargetBoxes, self, _unitName )
|
||||
local _SStgts = MENU_GROUP_COMMAND:New( group, "Smoke Strafe Tgts", _markPath, self._SmokeStrafeTargets, self, _unitName )
|
||||
local _SBtgts = MENU_GROUP_COMMAND:New( group, "Smoke Bomb Tgts", _markPath, self._SmokeBombTargets, self, _unitName )
|
||||
-- F10/On the Range/<Range Name>/Stats/
|
||||
missionCommands.addCommandForGroup( _gid, "All Strafe Results", _statsPath, self._DisplayStrafePitResults, self, _unitName )
|
||||
missionCommands.addCommandForGroup( _gid, "All Bombing Results", _statsPath, self._DisplayBombingResults, self, _unitName )
|
||||
missionCommands.addCommandForGroup( _gid, "My Strafe Results", _statsPath, self._DisplayMyStrafePitResults, self, _unitName )
|
||||
missionCommands.addCommandForGroup( _gid, "My Bomb Results", _statsPath, self._DisplayMyBombingResults, self, _unitName )
|
||||
missionCommands.addCommandForGroup( _gid, "Reset All Stats", _statsPath, self._ResetRangeStats, self, _unitName )
|
||||
local _AllSR = MENU_GROUP_COMMAND:New( group, "All Strafe Results", _statsPath, self._DisplayStrafePitResults, self, _unitName )
|
||||
local _AllBR = MENU_GROUP_COMMAND:New( group, "All Bombing Results", _statsPath, self._DisplayBombingResults, self, _unitName )
|
||||
local _MySR = MENU_GROUP_COMMAND:New( group, "My Strafe Results", _statsPath, self._DisplayMyStrafePitResults, self, _unitName )
|
||||
local _MyBR = MENU_GROUP_COMMAND:New( group, "My Bomb Results", _statsPath, self._DisplayMyBombingResults, 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/
|
||||
missionCommands.addCommandForGroup( _gid, "Blue Smoke", _mysmokePath, self._playersmokecolor, self, _unitName, SMOKECOLOR.Blue )
|
||||
missionCommands.addCommandForGroup( _gid, "Green Smoke", _mysmokePath, self._playersmokecolor, self, _unitName, SMOKECOLOR.Green )
|
||||
missionCommands.addCommandForGroup( _gid, "Orange Smoke", _mysmokePath, self._playersmokecolor, self, _unitName, SMOKECOLOR.Orange )
|
||||
missionCommands.addCommandForGroup( _gid, "Red Smoke", _mysmokePath, self._playersmokecolor, self, _unitName, SMOKECOLOR.Red )
|
||||
missionCommands.addCommandForGroup( _gid, "White Smoke", _mysmokePath, self._playersmokecolor, self, _unitName, SMOKECOLOR.White )
|
||||
local _BlueSM = MENU_GROUP_COMMAND:New( group, "Blue Smoke", _mysmokePath, self._playersmokecolor, self, _unitName, SMOKECOLOR.Blue )
|
||||
local _GrSM = MENU_GROUP_COMMAND:New( group, "Green Smoke", _mysmokePath, self._playersmokecolor, self, _unitName, SMOKECOLOR.Green )
|
||||
local _OrSM = MENU_GROUP_COMMAND:New( group, "Orange Smoke", _mysmokePath, self._playersmokecolor, self, _unitName, SMOKECOLOR.Orange )
|
||||
local _ReSM = MENU_GROUP_COMMAND:New( group, "Red Smoke", _mysmokePath, self._playersmokecolor, self, _unitName, SMOKECOLOR.Red )
|
||||
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/
|
||||
missionCommands.addCommandForGroup( _gid, "Green Flares", _myflarePath, self._playerflarecolor, self, _unitName, FLARECOLOR.Green )
|
||||
missionCommands.addCommandForGroup( _gid, "Red Flares", _myflarePath, self._playerflarecolor, self, _unitName, FLARECOLOR.Red )
|
||||
missionCommands.addCommandForGroup( _gid, "White Flares", _myflarePath, self._playerflarecolor, self, _unitName, FLARECOLOR.White )
|
||||
missionCommands.addCommandForGroup( _gid, "Yellow Flares", _myflarePath, self._playerflarecolor, self, _unitName, FLARECOLOR.Yellow )
|
||||
local _GrFl = MENU_GROUP_COMMAND:New( group, "Green Flares", _myflarePath, self._playerflarecolor, self, _unitName, FLARECOLOR.Green )
|
||||
local _ReFl = MENU_GROUP_COMMAND:New( group, "Red Flares", _myflarePath, self._playerflarecolor, self, _unitName, FLARECOLOR.Red )
|
||||
local _WhFl = MENU_GROUP_COMMAND:New( group, "White Flares", _myflarePath, self._playerflarecolor, self, _unitName, FLARECOLOR.White )
|
||||
local _YeFl = MENU_GROUP_COMMAND:New( group, "Yellow Flares", _myflarePath, self._playerflarecolor, self, _unitName, FLARECOLOR.Yellow )
|
||||
-- F10/On the Range/<Range Name>/My Settings/
|
||||
missionCommands.addCommandForGroup( _gid, "Smoke Delay On/Off", _settingsPath, self._SmokeBombDelayOnOff, self, _unitName )
|
||||
missionCommands.addCommandForGroup( _gid, "Smoke Impact On/Off", _settingsPath, self._SmokeBombImpactOnOff, self, _unitName )
|
||||
missionCommands.addCommandForGroup( _gid, "Flare Hits On/Off", _settingsPath, self._FlareDirectHitsOnOff, self, _unitName )
|
||||
missionCommands.addCommandForGroup( _gid, "All Messages On/Off", _settingsPath, self._MessagesToPlayerOnOff, self, _unitName )
|
||||
local _SmDe = MENU_GROUP_COMMAND:New( group, "Smoke Delay On/Off", _settingsPath, self._SmokeBombDelayOnOff, self, _unitName )
|
||||
local _SmIm = MENU_GROUP_COMMAND:New( group, "Smoke Impact On/Off", _settingsPath, self._SmokeBombImpactOnOff, self, _unitName )
|
||||
local _FlHi = MENU_GROUP_COMMAND:New( group, "Flare Hits On/Off", _settingsPath, self._FlareDirectHitsOnOff, 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
|
||||
missionCommands.addCommandForGroup( _gid, "General Info", _infoPath, self._DisplayRangeInfo, self, _unitName )
|
||||
missionCommands.addCommandForGroup( _gid, "Weather Report", _infoPath, self._DisplayRangeWeather, self, _unitName )
|
||||
missionCommands.addCommandForGroup( _gid, "Bombing Targets", _infoPath, self._DisplayBombTargets, self, _unitName )
|
||||
missionCommands.addCommandForGroup( _gid, "Strafe Pits", _infoPath, self._DisplayStrafePits, self, _unitName )
|
||||
local _WeIn = MENU_GROUP_COMMAND:New( group, "General Info", _infoPath, self._DisplayRangeInfo, self, _unitName )
|
||||
local _WeRe = MENU_GROUP_COMMAND:New( group, "Weather Report", _infoPath, self._DisplayRangeWeather, self, _unitName )
|
||||
local _BoTgtgs = MENU_GROUP_COMMAND:New( group, "Bombing Targets", _infoPath, self._DisplayBombTargets, self, _unitName )
|
||||
local _StrPits = MENU_GROUP_COMMAND:New( group, "Strafe Pits", _infoPath, self._DisplayStrafePits, self, _unitName ):Refresh()
|
||||
end
|
||||
else
|
||||
self:E( self.id .. "Could not find group or group ID in AddF10Menu() function. Unit name: " .. _unitName )
|
||||
@@ -3031,7 +3189,7 @@ end
|
||||
-- Helper Functions
|
||||
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
--- Get the coordinate of a Bomb target.
|
||||
--- Get the number of shells a unit currently has.
|
||||
-- @param #RANGE self
|
||||
-- @param #RANGE.BombTarget target Bomb target data.
|
||||
-- @return Core.Point#COORDINATE Target coordinate.
|
||||
@@ -3341,6 +3499,49 @@ function RANGE:_MessagesToPlayerOnOff( unitname )
|
||||
|
||||
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.
|
||||
-- @param #RANGE self
|
||||
-- @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.
|
||||
-- @param #RANGE self
|
||||
-- @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 )
|
||||
self:F2( name )
|
||||
|
||||
@@ -3601,11 +3802,9 @@ function RANGE:_myname( unitname )
|
||||
|
||||
local unit = UNIT:FindByName( unitname )
|
||||
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", pname )
|
||||
end
|
||||
|
||||
|
||||
@@ -1650,7 +1650,7 @@ function SCORING:ReportScoreGroupDetailed( PlayerGroup )
|
||||
self:F( { ReportMissions, ScoreMissions, PenaltyMissions } )
|
||||
|
||||
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",
|
||||
PlayerName,
|
||||
@@ -1705,7 +1705,7 @@ function SCORING:ReportScoreAllSummary( PlayerGroup )
|
||||
self:F( { ReportMissions, ScoreMissions, PenaltyMissions } )
|
||||
|
||||
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 )",
|
||||
PlayerName,
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
--
|
||||
-- ### Authors: **FlightControl**, **applevangelist**
|
||||
--
|
||||
-- Last Update: Nov 2021
|
||||
-- Last Update: Feb 2022
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
@@ -59,6 +59,7 @@ SEAD = {
|
||||
Padding = 10,
|
||||
CallBack = nil,
|
||||
UseCallBack = false,
|
||||
debug = false,
|
||||
}
|
||||
|
||||
--- Missile enumerators
|
||||
@@ -76,6 +77,8 @@ SEAD = {
|
||||
["X_25"] = "X_25",
|
||||
["X_31"] = "X_31",
|
||||
["Kh25"] = "Kh25",
|
||||
["BGM_109"] = "BGM_109",
|
||||
["AGM_154"] = "AGM_154",
|
||||
}
|
||||
|
||||
--- Missile enumerators - from DCS ME and Wikipedia
|
||||
@@ -85,7 +88,7 @@ SEAD = {
|
||||
["AGM_88"] = { 150, 3},
|
||||
["AGM_45"] = { 12, 2},
|
||||
["AGM_122"] = { 16.5, 2.3},
|
||||
["AGM_84"] = { 280, 0.85},
|
||||
["AGM_84"] = { 280, 0.8},
|
||||
["ALARM"] = { 45, 2},
|
||||
["LD-10"] = { 60, 4},
|
||||
["X_58"] = { 70, 4},
|
||||
@@ -93,6 +96,8 @@ SEAD = {
|
||||
["X_25"] = { 25, 0.76},
|
||||
["X_31"] = {150, 3},
|
||||
["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.
|
||||
@@ -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' } )
|
||||
function SEAD:New( SEADGroupPrefixes, Padding )
|
||||
|
||||
local self = BASE:Inherit( self, BASE:New() )
|
||||
self:F( SEADGroupPrefixes )
|
||||
local self = BASE:Inherit( self, FSM:New() )
|
||||
self:T( SEADGroupPrefixes )
|
||||
|
||||
if type( SEADGroupPrefixes ) == 'table' then
|
||||
for SEADGroupPrefixID, SEADGroupPrefix in pairs( SEADGroupPrefixes ) do
|
||||
@@ -122,14 +127,21 @@ function SEAD:New( SEADGroupPrefixes, Padding )
|
||||
local padding = Padding or 10
|
||||
if padding < 10 then padding = 10 end
|
||||
self.Padding = padding
|
||||
self.UseEmissionsOnOff = false
|
||||
self.UseEmissionsOnOff = true
|
||||
|
||||
self.debug = false
|
||||
|
||||
self.CallBack = nil
|
||||
self.UseCallBack = false
|
||||
|
||||
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
|
||||
end
|
||||
|
||||
@@ -213,7 +225,7 @@ function SEAD:_CheckHarms(WeaponName)
|
||||
local hit = false
|
||||
local name = ""
|
||||
for _,_name in pairs (SEAD.Harms) do
|
||||
if string.find(WeaponName,_name,1) then
|
||||
if string.find(WeaponName,_name,1,true) then
|
||||
hit = true
|
||||
name = _name
|
||||
break
|
||||
@@ -249,6 +261,186 @@ function SEAD:_GetDistance(_point1, _point2)
|
||||
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.
|
||||
-- @param #SEAD self
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
@@ -256,6 +448,7 @@ end
|
||||
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
|
||||
@@ -270,114 +463,55 @@ function SEAD:HandleEventShot( EventData )
|
||||
local _targetskill = "Random"
|
||||
local _targetgroupname = "none"
|
||||
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
|
||||
if _targetUnit and _targetUnit:IsAlive() then
|
||||
_targetgroup = _targetUnit:GetGroup()
|
||||
_targetgroupname = _targetgroup:GetName() -- group name
|
||||
local _targetUnitName = _targetUnit:GetName()
|
||||
_targetUnit:GetSkill()
|
||||
_targetskill = _targetUnit:GetSkill()
|
||||
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( _targetgroupname, SEADGroupPrefix )
|
||||
if string.find( _targetgroupname, SEADGroupPrefix, 1, true ) then
|
||||
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
|
||||
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) -- 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
|
||||
self:ManageEvasion(_targetskill,_targetgroup,SEADPlanePos,SEADWeaponName,SEADGroup)
|
||||
end
|
||||
end
|
||||
return self
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -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:
|
||||
--
|
||||
@@ -7,6 +7,7 @@
|
||||
-- ### Contributions:
|
||||
--
|
||||
-- * FlightControl : Rework to OO framework.
|
||||
-- * And many more
|
||||
--
|
||||
-- @module Utils
|
||||
-- @image MOOSE.JPG
|
||||
@@ -62,73 +63,129 @@ DCSMAP = {
|
||||
|
||||
--- See [DCS_enum_callsigns](https://wiki.hoggitworld.com/view/DCS_enum_callsigns)
|
||||
-- @type CALLSIGN
|
||||
CALLSIGN = {
|
||||
CALLSIGN={
|
||||
-- Aircraft
|
||||
Aircraft = {
|
||||
Enfield = 1,
|
||||
Springfield = 2,
|
||||
Uzi = 3,
|
||||
Colt = 4,
|
||||
Dodge = 5,
|
||||
Ford = 6,
|
||||
Chevy = 7,
|
||||
Pontiac = 8,
|
||||
Aircraft={
|
||||
Enfield=1,
|
||||
Springfield=2,
|
||||
Uzi=3,
|
||||
Colt=4,
|
||||
Dodge=5,
|
||||
Ford=6,
|
||||
Chevy=7,
|
||||
Pontiac=8,
|
||||
-- A-10A or A-10C
|
||||
Hawg = 9,
|
||||
Boar = 10,
|
||||
Pig = 11,
|
||||
Tusk = 12,
|
||||
Hawg=9,
|
||||
Boar=10,
|
||||
Pig=11,
|
||||
Tusk=12,
|
||||
},
|
||||
-- AWACS
|
||||
AWACS = {
|
||||
Overlord = 1,
|
||||
Magic = 2,
|
||||
Wizard = 3,
|
||||
Focus = 4,
|
||||
Darkstar = 5,
|
||||
AWACS={
|
||||
Overlord=1,
|
||||
Magic=2,
|
||||
Wizard=3,
|
||||
Focus=4,
|
||||
Darkstar=5,
|
||||
},
|
||||
-- Tanker
|
||||
Tanker = {
|
||||
Texaco = 1,
|
||||
Arco = 2,
|
||||
Shell = 3,
|
||||
Tanker={
|
||||
Texaco=1,
|
||||
Arco=2,
|
||||
Shell=3,
|
||||
},
|
||||
-- JTAC
|
||||
JTAC = {
|
||||
Axeman = 1,
|
||||
Darknight = 2,
|
||||
Warrior = 3,
|
||||
Pointer = 4,
|
||||
Eyeball = 5,
|
||||
Moonbeam = 6,
|
||||
Whiplash = 7,
|
||||
Finger = 8,
|
||||
Pinpoint = 9,
|
||||
Ferret = 10,
|
||||
Shaba = 11,
|
||||
Playboy = 12,
|
||||
Hammer = 13,
|
||||
Jaguar = 14,
|
||||
Deathstar = 15,
|
||||
Anvil = 16,
|
||||
Firefly = 17,
|
||||
Mantis = 18,
|
||||
Badger = 19,
|
||||
JTAC={
|
||||
Axeman=1,
|
||||
Darknight=2,
|
||||
Warrior=3,
|
||||
Pointer=4,
|
||||
Eyeball=5,
|
||||
Moonbeam=6,
|
||||
Whiplash=7,
|
||||
Finger=8,
|
||||
Pinpoint=9,
|
||||
Ferret=10,
|
||||
Shaba=11,
|
||||
Playboy=12,
|
||||
Hammer=13,
|
||||
Jaguar=14,
|
||||
Deathstar=15,
|
||||
Anvil=16,
|
||||
Firefly=17,
|
||||
Mantis=18,
|
||||
Badger=19,
|
||||
},
|
||||
-- FARP
|
||||
FARP = {
|
||||
London = 1,
|
||||
Dallas = 2,
|
||||
Paris = 3,
|
||||
Moscow = 4,
|
||||
Berlin = 5,
|
||||
Rome = 6,
|
||||
Madrid = 7,
|
||||
Warsaw = 8,
|
||||
Dublin = 9,
|
||||
Perth = 10,
|
||||
FARP={
|
||||
London=1,
|
||||
Dallas=2,
|
||||
Paris=3,
|
||||
Moscow=4,
|
||||
Berlin=5,
|
||||
Rome=6,
|
||||
Madrid=7,
|
||||
Warsaw=8,
|
||||
Dublin=9,
|
||||
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.
|
||||
-- @type UTILS
|
||||
@@ -1695,6 +1752,16 @@ function UTILS.IsLoadingDoorOpen( unit_name )
|
||||
ret_val = true
|
||||
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
|
||||
BASE:T( unit_name .. " all doors are closed" )
|
||||
end
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2609,6 +2609,40 @@ function GROUP:GetSkill()
|
||||
return skill
|
||||
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
|
||||
--
|
||||
----- Signal a flare at the position of the GROUP.
|
||||
|
||||
Reference in New Issue
Block a user