mirror of
https://github.com/FlightControl-Master/MOOSE.git
synced 2025-08-15 10:47:21 +00:00
Compare commits
40 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5260b2b430 | ||
|
|
0213bc7aef | ||
|
|
ca8b0899d0 | ||
|
|
a1f5c0ab9b | ||
|
|
b0e3f82d27 | ||
|
|
327ab4766b | ||
|
|
3aee8a49c1 | ||
|
|
57de0b7351 | ||
|
|
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
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -3770,3 +3770,44 @@ function POSITIONABLE:IsSubmarine()
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Sets the controlled group to go at the specified speed in meters per second.
|
||||
-- @param #CONTROLLABLE self
|
||||
-- @param #number Speed Speed in meters per second.
|
||||
-- @param #boolean Keep (Optional) When set to true, will maintain the speed on passing waypoints. If not present or false, the controlled group will return to the speed as defined by their route.
|
||||
-- @return #CONTROLLABLE self
|
||||
function CONTROLLABLE:SetSpeed(Speed, Keep)
|
||||
self:F2( { self.ControllableName } )
|
||||
-- Set default if not specified.
|
||||
local speed = Speed or 5
|
||||
local DCSControllable = self:GetDCSObject()
|
||||
if DCSControllable then
|
||||
local Controller = self:_GetController()
|
||||
if Controller then
|
||||
Controller:setSpeed(speed, Keep)
|
||||
end
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- [AIR] Sets the controlled aircraft group to fly at the specified altitude in meters.
|
||||
-- @param #CONTROLLABLE self
|
||||
-- @param #number Altitude Altitude in meters.
|
||||
-- @param #boolean Keep (Optional) When set to true, will maintain the altitude on passing waypoints. If not present or false, the controlled group will return to the altitude as defined by their route.
|
||||
-- @param #string AltType (Optional) Specifies the altitude type used. If nil, the altitude type of the current waypoint will be used. Accepted values are "BARO" and "RADIO".
|
||||
-- @return #CONTROLLABLE self
|
||||
function CONTROLLABLE:SetAltitude(Altitude, Keep, AltType)
|
||||
self:F2( { self.ControllableName } )
|
||||
-- Set default if not specified.
|
||||
local altitude = Altitude or 1000
|
||||
local DCSControllable = self:GetDCSObject()
|
||||
if DCSControllable then
|
||||
local Controller = self:_GetController()
|
||||
if Controller then
|
||||
if self:IsAir() then
|
||||
Controller:setAltitude(altitude, Keep, AltType)
|
||||
end
|
||||
end
|
||||
end
|
||||
return self
|
||||
end
|
||||
@@ -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.
|
||||
|
||||
@@ -10,8 +10,11 @@
|
||||
--
|
||||
-- @module Wrapper.Static
|
||||
-- @image Wrapper_Static.JPG
|
||||
|
||||
|
||||
--- @type STATIC
|
||||
-- @extends Wrapper.Positionable#POSITIONABLE
|
||||
|
||||
--- Wrapper class to handle Static objects.
|
||||
--
|
||||
-- Note that Statics are almost the same as Units, but they don't have a controller.
|
||||
@@ -37,10 +40,13 @@
|
||||
--
|
||||
-- * @{#STATIC.FindByName}(): Find a STATIC instance from the _DATABASE object using a DCS Static name.
|
||||
--
|
||||
-- IMPORTANT: ONE SHOULD NEVER SANITIZE these STATIC OBJECT REFERENCES! (make the STATIC object references nil).
|
||||
-- IMPORTANT: ONE SHOULD NEVER SANATIZE these STATIC OBJECT REFERENCES! (make the STATIC object references nil).
|
||||
--
|
||||
-- @field #STATIC
|
||||
STATIC = { ClassName = "STATIC" }
|
||||
STATIC = {
|
||||
ClassName = "STATIC",
|
||||
}
|
||||
|
||||
|
||||
--- Register a static object.
|
||||
-- @param #STATIC self
|
||||
@@ -52,6 +58,7 @@ function STATIC:Register( StaticName )
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Finds a STATIC from the _DATABASE using a DCSStatic object.
|
||||
-- @param #STATIC self
|
||||
-- @param DCS#StaticObject DCSStatic An existing DCS Static object reference.
|
||||
@@ -90,9 +97,8 @@ end
|
||||
|
||||
--- Destroys the STATIC.
|
||||
-- @param #STATIC self
|
||||
-- @param #boolean GenerateEvent (Optional) true to generate a crash or dead event, false to not generate any event. `nil` (default) creates a remove event.
|
||||
-- @param #boolean GenerateEvent (Optional) true if you want to generate a crash or dead event for the static.
|
||||
-- @return #nil The DCS StaticObject is not existing or alive.
|
||||
--
|
||||
-- @usage
|
||||
-- -- Air static example: destroy the static Helicopter and generate a S_EVENT_CRASH.
|
||||
-- Helicopter = STATIC:FindByName( "Helicopter" )
|
||||
@@ -111,7 +117,7 @@ end
|
||||
-- @usage
|
||||
-- -- Destroy without event generation example.
|
||||
-- Ship = STATIC:FindByName( "Boat" )
|
||||
-- Ship:Destroy( false ) -- Don't generate any event upon destruction.
|
||||
-- Ship:Destroy( false ) -- Don't generate an event upon destruction.
|
||||
--
|
||||
function STATIC:Destroy( GenerateEvent )
|
||||
self:F2( self.ObjectName )
|
||||
@@ -142,6 +148,7 @@ function STATIC:Destroy( GenerateEvent )
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
--- Get DCS object of static of static.
|
||||
-- @param #STATIC self
|
||||
-- @return DCS static object
|
||||
@@ -173,6 +180,7 @@ function STATIC:GetUnits()
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
--- Get threat level of static.
|
||||
-- @param #STATIC self
|
||||
-- @return #number Threat level 1.
|
||||
@@ -186,15 +194,15 @@ end
|
||||
-- @param Core.Point#COORDINATE Coordinate The coordinate where to spawn the new Static.
|
||||
-- @param #number Heading The heading of the static respawn in degrees. Default is 0 deg.
|
||||
-- @param #number Delay Delay in seconds before the static is spawned.
|
||||
function STATIC:SpawnAt( Coordinate, Heading, Delay )
|
||||
function STATIC:SpawnAt(Coordinate, Heading, Delay)
|
||||
|
||||
Heading = Heading or 0
|
||||
Heading=Heading or 0
|
||||
|
||||
if Delay and Delay > 0 then
|
||||
SCHEDULER:New( nil, self.SpawnAt, { self, Coordinate, Heading }, Delay )
|
||||
if Delay and Delay>0 then
|
||||
SCHEDULER:New(nil, self.SpawnAt, {self, Coordinate, Heading}, Delay)
|
||||
else
|
||||
|
||||
local SpawnStatic = SPAWNSTATIC:NewFromStatic( self.StaticName )
|
||||
local SpawnStatic=SPAWNSTATIC:NewFromStatic(self.StaticName)
|
||||
|
||||
SpawnStatic:SpawnFromPointVec2( Coordinate, Heading, self.StaticName )
|
||||
|
||||
@@ -203,45 +211,48 @@ function STATIC:SpawnAt( Coordinate, Heading, Delay )
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Respawn the @{Wrapper.Unit} at the same location with the same properties.
|
||||
-- This is useful to respawn a cargo after it has been destroyed.
|
||||
-- @param #STATIC self
|
||||
-- @param DCS#country.id CountryID (Optional) The country ID used for spawning the new static. Default is same as currently.
|
||||
-- @param #number Delay (Optional) Delay in seconds before static is respawned. Default now.
|
||||
function STATIC:ReSpawn( CountryID, Delay )
|
||||
function STATIC:ReSpawn(CountryID, Delay)
|
||||
|
||||
if Delay and Delay > 0 then
|
||||
SCHEDULER:New( nil, self.ReSpawn, { self, CountryID }, Delay )
|
||||
if Delay and Delay>0 then
|
||||
SCHEDULER:New(nil, self.ReSpawn, {self, CountryID}, Delay)
|
||||
else
|
||||
|
||||
CountryID = CountryID or self:GetCountry()
|
||||
CountryID=CountryID or self:GetCountry()
|
||||
|
||||
local SpawnStatic = SPAWNSTATIC:NewFromStatic( self.StaticName, CountryID )
|
||||
local SpawnStatic=SPAWNSTATIC:NewFromStatic(self.StaticName, CountryID)
|
||||
|
||||
SpawnStatic:Spawn( nil, self.StaticName )
|
||||
SpawnStatic:Spawn(nil, self.StaticName)
|
||||
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Respawn the @{Wrapper.Unit} at a defined Coordinate with an optional heading.
|
||||
-- @param #STATIC self
|
||||
-- @param Core.Point#COORDINATE Coordinate The coordinate where to spawn the new Static.
|
||||
-- @param #number Heading (Optional) The heading of the static respawn in degrees. Default is the current heading.
|
||||
-- @param #number Delay (Optional) Delay in seconds before static is respawned. Default is now.
|
||||
function STATIC:ReSpawnAt( Coordinate, Heading, Delay )
|
||||
-- @param #number Heading (Optional) The heading of the static respawn in degrees. Default the current heading.
|
||||
-- @param #number Delay (Optional) Delay in seconds before static is respawned. Default now.
|
||||
function STATIC:ReSpawnAt(Coordinate, Heading, Delay)
|
||||
|
||||
-- Heading=Heading or 0
|
||||
--Heading=Heading or 0
|
||||
|
||||
if Delay and Delay > 0 then
|
||||
SCHEDULER:New( nil, self.ReSpawnAt, { self, Coordinate, Heading }, Delay )
|
||||
if Delay and Delay>0 then
|
||||
SCHEDULER:New(nil, self.ReSpawnAt, {self, Coordinate, Heading}, Delay)
|
||||
else
|
||||
local SpawnStatic = SPAWNSTATIC:NewFromStatic( self.StaticName, self:GetCountry() )
|
||||
|
||||
SpawnStatic:SpawnFromCoordinate( Coordinate, Heading, self.StaticName )
|
||||
local SpawnStatic=SPAWNSTATIC:NewFromStatic(self.StaticName, self:GetCountry())
|
||||
|
||||
SpawnStatic:SpawnFromCoordinate(Coordinate, Heading, self.StaticName)
|
||||
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
Reference in New Issue
Block a user