mirror of
https://github.com/FlightControl-Master/MOOSE.git
synced 2025-08-15 10:47:21 +00:00
Compare commits
76 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8af3f89c14 | ||
|
|
fe3079caad | ||
|
|
61ac6b4131 | ||
|
|
36cb189512 | ||
|
|
15f9843878 | ||
|
|
67f847dd16 | ||
|
|
8b9143d3f1 | ||
|
|
0388d47f23 | ||
|
|
de9b173d9b | ||
|
|
2cecc526fb | ||
|
|
968d178317 | ||
|
|
3c477b872a | ||
|
|
77e6088114 | ||
|
|
edd6594953 | ||
|
|
f8c05c99d0 | ||
|
|
50f6d98b49 | ||
|
|
147eeb05f6 | ||
|
|
d8cb15a577 | ||
|
|
0daac876ea | ||
|
|
1832125022 | ||
|
|
c311c40b72 | ||
|
|
db516a2077 | ||
|
|
ff8766669c | ||
|
|
06dc9a732e | ||
|
|
50c74d0852 | ||
|
|
1c97eb6f3c | ||
|
|
69449430d1 | ||
|
|
663cd34aa3 | ||
|
|
cfed6f5153 | ||
|
|
2b22d5288c | ||
|
|
a64424ecc8 | ||
|
|
fd1b2ecb86 | ||
|
|
6cae3e62cf | ||
|
|
05ce7e4513 | ||
|
|
136bd19f19 | ||
|
|
8873504daf | ||
|
|
a844a5d697 | ||
|
|
a49f4eaa21 | ||
|
|
e6e2651f8c | ||
|
|
b93ba13644 | ||
|
|
e4a51951b0 | ||
|
|
ad56e39942 | ||
|
|
ea09dc5a6e | ||
|
|
8ecfd913a3 | ||
|
|
53367c786e | ||
|
|
db5797bb4e | ||
|
|
5e8fe97752 | ||
|
|
393fa0bfbb | ||
|
|
4f51884b9d | ||
|
|
4c5c320073 | ||
|
|
9098590568 | ||
|
|
555bb7e68b | ||
|
|
c0a18957f0 | ||
|
|
2cf939560e | ||
|
|
9d3a7aae78 | ||
|
|
f6ed592f92 | ||
|
|
c98757d13c | ||
|
|
17378f509e | ||
|
|
7f18ea0e7a | ||
|
|
5172619cb1 | ||
|
|
3962529698 | ||
|
|
6481d5d41e | ||
|
|
6cc3d73c04 | ||
|
|
e541e39403 | ||
|
|
c7ea45e5fd | ||
|
|
20f28b3d2c | ||
|
|
f3f63ab8aa | ||
|
|
e91090cfff | ||
|
|
1a7fb3c13e | ||
|
|
59857ed79d | ||
|
|
4797665939 | ||
|
|
b89749036d | ||
|
|
c6268488de | ||
|
|
de04369703 | ||
|
|
05b6f19a87 | ||
|
|
2753df8216 |
@@ -47,19 +47,21 @@ function AI_CARGO:New( Carrier, CargoSet )
|
||||
|
||||
self:SetStartState( "Unloaded" )
|
||||
|
||||
self:AddTransition( "Unloaded", "Pickup", "*" )
|
||||
self:AddTransition( "Loaded", "Deploy", "*" )
|
||||
-- Board
|
||||
self:AddTransition( "Unloaded", "Pickup", "Unloaded" )
|
||||
self:AddTransition( "*", "Load", "*" )
|
||||
self:AddTransition( "*", "Reload", "*" )
|
||||
self:AddTransition( "*", "Board", "*" )
|
||||
self:AddTransition( "*", "Loaded", "Loaded" )
|
||||
self:AddTransition( "Loaded", "PickedUp", "Loaded" )
|
||||
|
||||
self:AddTransition( "*", "Load", "Boarding" )
|
||||
self:AddTransition( "Boarding", "Board", "Boarding" )
|
||||
self:AddTransition( "Loaded", "Board", "Loaded" )
|
||||
self:AddTransition( "Boarding", "Loaded", "Boarding" )
|
||||
self:AddTransition( "Boarding", "PickedUp", "Loaded" )
|
||||
-- Unload
|
||||
self:AddTransition( "Loaded", "Deploy", "*" )
|
||||
self:AddTransition( "*", "Unload", "*" )
|
||||
self:AddTransition( "*", "Unboard", "*" )
|
||||
self:AddTransition( "*", "Unloaded", "Unloaded" )
|
||||
self:AddTransition( "Unloaded", "Deployed", "Unloaded" )
|
||||
|
||||
self:AddTransition( "Loaded", "Unload", "Unboarding" )
|
||||
self:AddTransition( "Unboarding", "Unboard", "Unboarding" )
|
||||
self:AddTransition( "Unboarding", "Unloaded", "Unboarding" )
|
||||
self:AddTransition( "Unboarding", "Deployed", "Unloaded" )
|
||||
|
||||
--- Pickup Handler OnBefore for AI_CARGO
|
||||
-- @function [parent=#AI_CARGO] OnBeforePickup
|
||||
@@ -393,7 +395,7 @@ end
|
||||
function AI_CARGO:onafterBoard( Carrier, From, Event, To, Cargo, CarrierUnit, PickupZone )
|
||||
self:F( { Carrier, From, Event, To, Cargo, CarrierUnit:GetName() } )
|
||||
|
||||
if Carrier and Carrier:IsAlive() and From == "Boarding" then
|
||||
if Carrier and Carrier:IsAlive() then
|
||||
self:F({ IsLoaded = Cargo:IsLoaded(), Cargo:GetName(), Carrier:GetName() } )
|
||||
if not Cargo:IsLoaded() and not Cargo:IsDestroyed() then
|
||||
self:__Board( -10, Cargo, CarrierUnit, PickupZone )
|
||||
@@ -509,7 +511,7 @@ end
|
||||
function AI_CARGO:onafterUnboard( Carrier, From, Event, To, Cargo, CarrierUnit, DeployZone, Defend )
|
||||
self:F( { Carrier, From, Event, To, Cargo:GetName(), DeployZone = DeployZone, Defend = Defend } )
|
||||
|
||||
if Carrier and Carrier:IsAlive() and From == "Unboarding" then
|
||||
if Carrier and Carrier:IsAlive() then
|
||||
if not Cargo:IsUnLoaded() then
|
||||
self:__Unboard( 10, Cargo, CarrierUnit, DeployZone, Defend )
|
||||
return
|
||||
@@ -580,4 +582,3 @@ function AI_CARGO:onafterDeployed( Carrier, From, Event, To, DeployZone, Defend
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
@@ -98,7 +98,8 @@ function AI_CARGO_APC:New( APC, CargoSet, CombatRadius )
|
||||
self:AddTransition( "*", "Guard", "Unloaded" )
|
||||
self:AddTransition( "*", "Home", "*" )
|
||||
self:AddTransition( "*", "Reload", "Boarding" )
|
||||
|
||||
self:AddTransition( "*", "Deployed", "*" )
|
||||
self:AddTransition( "*", "PickedUp", "*" )
|
||||
self:AddTransition( "*", "Destroyed", "Destroyed" )
|
||||
|
||||
self:SetCombatRadius( CombatRadius )
|
||||
|
||||
@@ -64,21 +64,25 @@ function AI_CARGO_HELICOPTER:New( Helicopter, CargoSet )
|
||||
self.Zone = ZONE_GROUP:New( Helicopter:GetName(), Helicopter, 300 )
|
||||
|
||||
self:SetStartState( "Unloaded" )
|
||||
-- Boarding
|
||||
self:AddTransition( "Unloaded", "Pickup", "Unloaded" )
|
||||
self:AddTransition( "*", "Landed", "*" )
|
||||
self:AddTransition( "*", "Load", "*" )
|
||||
self:AddTransition( "*", "Loaded", "Loaded" )
|
||||
self:AddTransition( "Loaded", "PickedUp", "Loaded" )
|
||||
|
||||
self:AddTransition( "Unloaded", "Pickup", "*" )
|
||||
self:AddTransition( "Loaded", "Deploy", "*" )
|
||||
self:AddTransition( "*", "Loaded", "Loaded" )
|
||||
self:AddTransition( "Unboarding", "Pickup", "Unloaded" )
|
||||
self:AddTransition( "Unloaded", "Unboard", "Unloaded" )
|
||||
self:AddTransition( "Unloaded", "Unloaded", "Unloaded" )
|
||||
self:AddTransition( "*", "PickedUp", "*" )
|
||||
self:AddTransition( "*", "Landed", "*" )
|
||||
self:AddTransition( "*", "Queue", "*" )
|
||||
self:AddTransition( "*", "Orbit" , "*" )
|
||||
-- Unboarding
|
||||
self:AddTransition( "Loaded", "Deploy", "*" )
|
||||
self:AddTransition( "*", "Queue", "*" )
|
||||
self:AddTransition( "*", "Orbit" , "*" )
|
||||
self:AddTransition( "*", "Destroyed", "*" )
|
||||
self:AddTransition( "*", "Unload", "*" )
|
||||
self:AddTransition( "*", "Unloaded", "Unloaded" )
|
||||
self:AddTransition( "Unloaded", "Deployed", "Unloaded" )
|
||||
|
||||
-- RTB
|
||||
self:AddTransition( "*", "Home" , "*" )
|
||||
|
||||
self:AddTransition( "*", "Destroyed", "Destroyed" )
|
||||
|
||||
--- Pickup Handler OnBefore for AI_CARGO_HELICOPTER
|
||||
-- @function [parent=#AI_CARGO_HELICOPTER] OnBeforePickup
|
||||
-- @param #AI_CARGO_HELICOPTER self
|
||||
@@ -207,6 +211,9 @@ function AI_CARGO_HELICOPTER:New( Helicopter, CargoSet )
|
||||
|
||||
self:SetCarrier( Helicopter )
|
||||
|
||||
self.landingspeed = 15 -- kph
|
||||
self.landingheight = 5.5 -- meter
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -255,6 +262,25 @@ function AI_CARGO_HELICOPTER:SetCarrier( Helicopter )
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set landingspeed and -height for helicopter landings. Adjust after tracing if your helis get stuck after landing.
|
||||
-- @param #AI_CARGO_HELICOPTER self
|
||||
-- @param #number speed Landing speed in kph(!), e.g. 15
|
||||
-- @param #number height Landing height in meters(!), e.g. 5.5
|
||||
-- @return #AI_CARGO_HELICOPTER self
|
||||
-- @usage If your choppers get stuck, add tracing to your script to determine if they hit the right parameters like so:
|
||||
--
|
||||
-- BASE:TraceOn()
|
||||
-- BASE:TraceClass("AI_CARGO_HELICOPTER")
|
||||
--
|
||||
-- Watch the DCS.log for entries stating `Helicopter:<name>, Height = Helicopter:<number>, Velocity = Helicopter:<number>`
|
||||
-- Adjust if necessary.
|
||||
function AI_CARGO_HELICOPTER:SetLandingSpeedAndHeight(speed, height)
|
||||
local _speed = speed or 15
|
||||
local _height = height or 5.5
|
||||
self.landingheight = _height
|
||||
self.landingspeed = _speed
|
||||
return self
|
||||
end
|
||||
|
||||
--- @param #AI_CARGO_HELICOPTER self
|
||||
-- @param Wrapper.Group#GROUP Helicopter
|
||||
@@ -271,13 +297,13 @@ function AI_CARGO_HELICOPTER:onafterLanded( Helicopter, From, Event, To )
|
||||
-- 1 - When the helo lands normally on the ground.
|
||||
-- 2 - when the helo is hit and goes RTB or even when it is destroyed.
|
||||
-- For point 2, this is an issue, the infantry may not unload in this case!
|
||||
-- So we check if the helo is on the ground, and velocity< 5.
|
||||
-- So we check if the helo is on the ground, and velocity< 15.
|
||||
-- Only then the infantry can unload (and load too, for consistency)!
|
||||
|
||||
self:F( { Helicopter:GetName(), Height = Helicopter:GetHeight( true ), Velocity = Helicopter:GetVelocityKMH() } )
|
||||
self:T( { Helicopter:GetName(), Height = Helicopter:GetHeight( true ), Velocity = Helicopter:GetVelocityKMH() } )
|
||||
|
||||
if self.RoutePickup == true then
|
||||
if Helicopter:GetHeight( true ) <= 5.5 and Helicopter:GetVelocityKMH() < 15 then
|
||||
if Helicopter:GetHeight( true ) <= self.landingheight then --and Helicopter:GetVelocityKMH() < self.landingspeed then
|
||||
--self:Load( Helicopter:GetPointVec2() )
|
||||
self:Load( self.PickupZone )
|
||||
self.RoutePickup = false
|
||||
@@ -285,7 +311,7 @@ function AI_CARGO_HELICOPTER:onafterLanded( Helicopter, From, Event, To )
|
||||
end
|
||||
|
||||
if self.RouteDeploy == true then
|
||||
if Helicopter:GetHeight( true ) <= 5.5 and Helicopter:GetVelocityKMH() < 15 then
|
||||
if Helicopter:GetHeight( true ) <= self.landingheight then --and Helicopter:GetVelocityKMH() < self.landingspeed then
|
||||
self:Unload( self.DeployZone )
|
||||
self.RouteDeploy = false
|
||||
end
|
||||
|
||||
@@ -136,7 +136,7 @@ function DATABASE:New()
|
||||
self:_RegisterGroupsAndUnits()
|
||||
self:_RegisterClients()
|
||||
self:_RegisterStatics()
|
||||
self:_RegisterAirbases()
|
||||
--self:_RegisterAirbases()
|
||||
--self:_RegisterPlayers()
|
||||
|
||||
self.UNITS_Position = 0
|
||||
|
||||
@@ -3999,6 +3999,24 @@ do -- SET_CLIENT
|
||||
return self
|
||||
end
|
||||
|
||||
--- Iterate the SET_CLIENT and count alive units.
|
||||
-- @param #SET_CLIENT self
|
||||
-- @return #number count
|
||||
function SET_CLIENT:CountAlive()
|
||||
|
||||
local Set = self:GetSet()
|
||||
|
||||
local CountU = 0
|
||||
for UnitID, UnitData in pairs(Set) do -- For each GROUP in SET_GROUP
|
||||
if UnitData and UnitData:IsAlive() then
|
||||
CountU = CountU + 1
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return CountU
|
||||
end
|
||||
|
||||
---
|
||||
-- @param #SET_CLIENT self
|
||||
-- @param Wrapper.Client#CLIENT MClient
|
||||
@@ -4746,7 +4764,7 @@ do -- SET_AIRBASE
|
||||
|
||||
local airbaseName, airbase=self:FindInDatabase(EventData)
|
||||
|
||||
if airbase and airbase:IsShip() or airbase:IsHelipad() then
|
||||
if airbase and (airbase:IsShip() or airbase:IsHelipad()) then
|
||||
self:RemoveAirbasesByName(airbaseName)
|
||||
end
|
||||
|
||||
|
||||
@@ -138,24 +138,24 @@ SPAWNSTATIC = {
|
||||
-- @return #SPAWNSTATIC self
|
||||
function SPAWNSTATIC:NewFromStatic(SpawnTemplateName, SpawnCountryID)
|
||||
|
||||
local self = BASE:Inherit( self, BASE:New() ) -- #SPAWNSTATIC
|
||||
local self = BASE:Inherit( self, BASE:New() ) -- #SPAWNSTATIC
|
||||
|
||||
local TemplateStatic, CoalitionID, CategoryID, CountryID = _DATABASE:GetStaticGroupTemplate(SpawnTemplateName)
|
||||
local TemplateStatic, CoalitionID, CategoryID, CountryID = _DATABASE:GetStaticGroupTemplate(SpawnTemplateName)
|
||||
|
||||
if TemplateStatic then
|
||||
self.SpawnTemplatePrefix = SpawnTemplateName
|
||||
self.TemplateStaticUnit = UTILS.DeepCopy(TemplateStatic.units[1])
|
||||
self.CountryID = SpawnCountryID or CountryID
|
||||
self.CategoryID = CategoryID
|
||||
self.CoalitionID = CoalitionID
|
||||
self.SpawnIndex = 0
|
||||
else
|
||||
error( "SPAWNSTATIC:New: There is no static declared in the mission editor with SpawnTemplatePrefix = '" .. tostring(SpawnTemplateName) .. "'" )
|
||||
end
|
||||
if TemplateStatic then
|
||||
self.SpawnTemplatePrefix = SpawnTemplateName
|
||||
self.TemplateStaticUnit = UTILS.DeepCopy(TemplateStatic.units[1])
|
||||
self.CountryID = SpawnCountryID or CountryID
|
||||
self.CategoryID = CategoryID
|
||||
self.CoalitionID = CoalitionID
|
||||
self.SpawnIndex = 0
|
||||
else
|
||||
error( "SPAWNSTATIC:New: There is no static declared in the mission editor with SpawnTemplatePrefix = '" .. tostring(SpawnTemplateName) .. "'" )
|
||||
end
|
||||
|
||||
self:SetEventPriority( 5 )
|
||||
|
||||
return self
|
||||
return self
|
||||
end
|
||||
|
||||
--- Creates the main object to spawn a @{Static} given a template table.
|
||||
@@ -422,7 +422,11 @@ function SPAWNSTATIC:_SpawnStatic(Template, CountryID)
|
||||
end
|
||||
|
||||
if self.InitCargo~=nil then
|
||||
Template.isCargo=self.InitCargo
|
||||
Template.canCargo=self.InitCargo
|
||||
end
|
||||
|
||||
if self.InitCargoMass~=nil then
|
||||
Template.mass=self.InitCargoMass
|
||||
end
|
||||
|
||||
if self.InitLinkUnit then
|
||||
|
||||
@@ -183,12 +183,12 @@ function ZONE_BASE:IsCoordinateInZone( Coordinate )
|
||||
return InZone
|
||||
end
|
||||
|
||||
--- Returns if a PointVec2 is within the zone.
|
||||
--- Returns if a PointVec2 is within the zone. (Name is misleading, actually takes a #COORDINATE)
|
||||
-- @param #ZONE_BASE self
|
||||
-- @param Core.Point#POINT_VEC2 PointVec2 The PointVec2 to test.
|
||||
-- @param Core.Point#COORDINATE PointVec2 The coordinate to test.
|
||||
-- @return #boolean true if the PointVec2 is within the zone.
|
||||
function ZONE_BASE:IsPointVec2InZone( PointVec2 )
|
||||
local InZone = self:IsVec2InZone( PointVec2:GetVec2() )
|
||||
function ZONE_BASE:IsPointVec2InZone( Coordinate )
|
||||
local InZone = self:IsVec2InZone( Coordinate:GetVec2() )
|
||||
return InZone
|
||||
end
|
||||
|
||||
@@ -498,8 +498,8 @@ end
|
||||
--
|
||||
-- @field #ZONE_RADIUS
|
||||
ZONE_RADIUS = {
|
||||
ClassName="ZONE_RADIUS",
|
||||
}
|
||||
ClassName="ZONE_RADIUS",
|
||||
}
|
||||
|
||||
--- Constructor of @{#ZONE_RADIUS}, taking the zone name, the zone location and a radius.
|
||||
-- @param #ZONE_RADIUS self
|
||||
@@ -510,15 +510,15 @@ ZONE_RADIUS = {
|
||||
function ZONE_RADIUS:New( ZoneName, Vec2, Radius )
|
||||
|
||||
-- Inherit ZONE_BASE.
|
||||
local self = BASE:Inherit( self, ZONE_BASE:New( ZoneName ) ) -- #ZONE_RADIUS
|
||||
self:F( { ZoneName, Vec2, Radius } )
|
||||
local self = BASE:Inherit( self, ZONE_BASE:New( ZoneName ) ) -- #ZONE_RADIUS
|
||||
self:F( { ZoneName, Vec2, Radius } )
|
||||
|
||||
self.Radius = Radius
|
||||
self.Vec2 = Vec2
|
||||
self.Radius = Radius
|
||||
self.Vec2 = Vec2
|
||||
|
||||
--self.Coordinate=COORDINATE:NewFromVec2(Vec2)
|
||||
--self.Coordinate=COORDINATE:NewFromVec2(Vec2)
|
||||
|
||||
return self
|
||||
return self
|
||||
end
|
||||
|
||||
--- Update zone from a 2D vector.
|
||||
@@ -746,11 +746,11 @@ end
|
||||
-- @param #ZONE_RADIUS self
|
||||
-- @return DCS#Vec2 The location of the zone.
|
||||
function ZONE_RADIUS:GetVec2()
|
||||
self:F2( self.ZoneName )
|
||||
self:F2( self.ZoneName )
|
||||
|
||||
self:T2( { self.Vec2 } )
|
||||
self:T2( { self.Vec2 } )
|
||||
|
||||
return self.Vec2
|
||||
return self.Vec2
|
||||
end
|
||||
|
||||
--- Sets the @{DCS#Vec2} of the zone.
|
||||
@@ -1165,20 +1165,20 @@ end
|
||||
-- @param #number outer (optional) Maximal distance from the outer edge of the zone. Default is the radius of the zone.
|
||||
-- @return DCS#Vec2 The random location within the zone.
|
||||
function ZONE_RADIUS:GetRandomVec2( inner, outer )
|
||||
self:F( self.ZoneName, inner, outer )
|
||||
self:F( self.ZoneName, inner, outer )
|
||||
|
||||
local Point = {}
|
||||
local Vec2 = self:GetVec2()
|
||||
local _inner = inner or 0
|
||||
local _outer = outer or self:GetRadius()
|
||||
local Point = {}
|
||||
local Vec2 = self:GetVec2()
|
||||
local _inner = inner or 0
|
||||
local _outer = outer or self:GetRadius()
|
||||
|
||||
local angle = math.random() * math.pi * 2;
|
||||
Point.x = Vec2.x + math.cos( angle ) * math.random(_inner, _outer);
|
||||
Point.y = Vec2.y + math.sin( angle ) * math.random(_inner, _outer);
|
||||
local angle = math.random() * math.pi * 2;
|
||||
Point.x = Vec2.x + math.cos( angle ) * math.random(_inner, _outer);
|
||||
Point.y = Vec2.y + math.sin( angle ) * math.random(_inner, _outer);
|
||||
|
||||
self:T( { Point } )
|
||||
self:T( { Point } )
|
||||
|
||||
return Point
|
||||
return Point
|
||||
end
|
||||
|
||||
--- Returns a @{Core.Point#POINT_VEC2} object reflecting a random 2D location within the zone.
|
||||
|
||||
@@ -1707,8 +1707,8 @@ end
|
||||
--- Returns the unit of a player and the player name. If the unit does not belong to a player, nil is returned.
|
||||
-- @param #FOX self
|
||||
-- @param DCS#Weapon weapon The weapon.
|
||||
-- @return #number Notching heading right, i.e. missile heading +90<EFBFBD>
|
||||
-- @return #number Notching heading left, i.e. missile heading -90<EFBFBD>.
|
||||
-- @return #number Notching heading right, i.e. missile heading +90°.
|
||||
-- @return #number Notching heading left, i.e. missile heading -90°.
|
||||
function FOX:_GetNotchingHeadings(weapon)
|
||||
|
||||
if weapon then
|
||||
|
||||
@@ -23,9 +23,9 @@
|
||||
-- Date: July 2021
|
||||
|
||||
-------------------------------------------------------------------------
|
||||
--- **MANTIS** class, extends #Core.Base#BASE
|
||||
--- **MANTIS** class, extends Core.Base#BASE
|
||||
-- @type MANTIS
|
||||
-- @field #string Classname
|
||||
-- @field #string ClassName
|
||||
-- @field #string name Name of this Mantis
|
||||
-- @field #string SAM_Templates_Prefix Prefix to build the #SET_GROUP for SAM sites
|
||||
-- @field Core.Set#SET_GROUP SAM_Group The SAM #SET_GROUP
|
||||
@@ -195,6 +195,9 @@ MANTIS = {
|
||||
TimeStamp = 0,
|
||||
state2flag = false,
|
||||
SamStateTracker = {},
|
||||
DLink = false,
|
||||
DLTimeStamp = 0,
|
||||
Padding = 10,
|
||||
}
|
||||
|
||||
--- Advanced state enumerator
|
||||
@@ -219,7 +222,8 @@ do
|
||||
--@param #string coaltion Coalition side of your setup, e.g. "blue", "red" or "neutral"
|
||||
--@param #boolean dynamic Use constant (true) filtering or just filter once (false, default) (optional)
|
||||
--@param #string awacs Group name of your Awacs (optional)
|
||||
--@param #boolean EmOnOff Make MANTIS switch Emissions on and off instead of changing the alarm state between RED and GREEN
|
||||
--@param #boolean EmOnOff Make MANTIS switch Emissions on and off instead of changing the alarm state between RED and GREEN (optional)
|
||||
--@param #number Padding For #SEAD - Extra number of seconds to add to radar switch-back-on time (optional)
|
||||
--@return #MANTIS self
|
||||
--@usage Start up your MANTIS with a basic setting
|
||||
--
|
||||
@@ -241,7 +245,7 @@ do
|
||||
-- `mybluemantis = MANTIS:New("bluemantis","Blue SAM","Blue EWR",nil,"blue",false,"Blue Awacs")`
|
||||
-- `mybluemantis:Start()`
|
||||
--
|
||||
function MANTIS:New(name,samprefix,ewrprefix,hq,coaltion,dynamic,awacs, EmOnOff)
|
||||
function MANTIS:New(name,samprefix,ewrprefix,hq,coaltion,dynamic,awacs, EmOnOff, Padding)
|
||||
|
||||
-- DONE: Create some user functions for these
|
||||
-- DONE: Make HQ useful
|
||||
@@ -278,6 +282,8 @@ do
|
||||
self.relointerval = math.random(1800,3600) -- random between 30 and 60 mins
|
||||
self.state2flag = false
|
||||
self.SamStateTracker = {} -- table to hold alert states, so we don't trigger state changes twice in adv mode
|
||||
self.DLink = false
|
||||
self.Padding = Padding or 10
|
||||
|
||||
if EmOnOff then
|
||||
if EmOnOff == false then
|
||||
@@ -325,7 +331,7 @@ do
|
||||
end
|
||||
|
||||
-- @field #string version
|
||||
self.version="0.5.2"
|
||||
self.version="0.6.2"
|
||||
self:I(string.format("***** Starting MANTIS Version %s *****", self.version))
|
||||
|
||||
--- FSM Functions ---
|
||||
@@ -593,7 +599,7 @@ do
|
||||
-- E.g. `mymantis:SetAdvancedMode(true, 90)`
|
||||
function MANTIS:SetAdvancedMode(onoff, ratio)
|
||||
self:T(self.lid .. "SetAdvancedMode")
|
||||
self:T({onoff, ratio})
|
||||
--self:T({onoff, ratio})
|
||||
local onoff = onoff or false
|
||||
local ratio = ratio or 100
|
||||
if (type(self.HQ_Template_CC) == "string") and onoff and self.dynamic then
|
||||
@@ -619,6 +625,17 @@ do
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set using an #INTEL_DLINK object instead of #DETECTION. Requires Develop branch of Moose.lua.
|
||||
-- @param #MANTIS self
|
||||
-- @param Ops.Intelligence#INTEL_DLINK DLink The data link object to be used.
|
||||
function MANTIS:SetUsingDLink(DLink)
|
||||
self:T(self.lid .. "SetUsingDLink")
|
||||
self.DLink = true
|
||||
self.Detection = DLink
|
||||
self.DLTimeStamp = timer.getAbsTime()
|
||||
return self
|
||||
end
|
||||
|
||||
--- [Internal] Function to check if HQ is alive
|
||||
-- @param #MANTIS self
|
||||
-- @return #boolean True if HQ is alive, else false
|
||||
@@ -633,10 +650,10 @@ do
|
||||
local hqgrp = GROUP:FindByName(hq)
|
||||
if hqgrp then
|
||||
if hqgrp:IsAlive() then -- ok we're on, hq exists and as alive
|
||||
self:T(self.lid.." HQ is alive!")
|
||||
--self:T(self.lid.." HQ is alive!")
|
||||
return true
|
||||
else
|
||||
self:T(self.lid.." HQ is dead!")
|
||||
--self:T(self.lid.." HQ is dead!")
|
||||
return false
|
||||
end
|
||||
end
|
||||
@@ -650,7 +667,7 @@ do
|
||||
function MANTIS:_CheckEWRState()
|
||||
self:T(self.lid .. "CheckEWRState")
|
||||
local text = self.lid.." Checking EWR State"
|
||||
self:T(text)
|
||||
--self:T(text)
|
||||
local m= MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug)
|
||||
if self.verbose then self:I(text) end
|
||||
-- start check
|
||||
@@ -666,7 +683,7 @@ do
|
||||
end
|
||||
end
|
||||
end
|
||||
self:T(self.lid..string.format(" No of EWR alive is %d", nalive))
|
||||
--self:T(self.lid..string.format(" No of EWR alive is %d", nalive))
|
||||
if nalive > 0 then
|
||||
return true
|
||||
else
|
||||
@@ -682,10 +699,8 @@ do
|
||||
-- @return #number Previous state for tracking 0, 1, or 2
|
||||
function MANTIS:_CalcAdvState()
|
||||
self:T(self.lid .. "CalcAdvState")
|
||||
local text = self.lid.." Calculating Advanced State"
|
||||
self:T(text)
|
||||
local m=MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug)
|
||||
if self.verbose then self:I(text) end
|
||||
local m=MESSAGE:New(self.lid.." Calculating Advanced State",10,"MANTIS"):ToAllIf(self.debug)
|
||||
if self.verbose then self:I(self.lid.." Calculating Advanced State") end
|
||||
-- start check
|
||||
local currstate = self.adv_state -- save curr state for comparison later
|
||||
local EWR_State = self:_CheckEWRState()
|
||||
@@ -703,10 +718,12 @@ do
|
||||
local ratio = self.adv_ratio / 100 -- e.g. 80/100 = 0.8
|
||||
ratio = ratio * self.adv_state -- e.g 0.8*2 = 1.6
|
||||
local newinterval = interval + (interval * ratio) -- e.g. 30+(30*1.6) = 78
|
||||
local text = self.lid..string.format(" Calculated OldState/NewState/Interval: %d / %d / %d", currstate, self.adv_state, newinterval)
|
||||
self:T(text)
|
||||
local m=MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug)
|
||||
if self.verbose then self:I(text) end
|
||||
if self.debug or self.verbose then
|
||||
local text = self.lid..string.format(" Calculated OldState/NewState/Interval: %d / %d / %d", currstate, self.adv_state, newinterval)
|
||||
--self:T(text)
|
||||
local m=MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug)
|
||||
if self.verbose then self:I(text) end
|
||||
end
|
||||
return newinterval, currstate
|
||||
end
|
||||
|
||||
@@ -716,13 +733,13 @@ do
|
||||
-- @param #boolean ewr If true, will relocate EWR objects
|
||||
function MANTIS:SetAutoRelocate(hq, ewr)
|
||||
self:T(self.lid .. "SetAutoRelocate")
|
||||
self:T({hq, ewr})
|
||||
--self:T({hq, ewr})
|
||||
local hqrel = hq or false
|
||||
local ewrel = ewr or false
|
||||
if hqrel or ewrel then
|
||||
self.autorelocate = true
|
||||
self.autorelocateunits = { HQ = hqrel, EWR = ewrel }
|
||||
self:T({self.autorelocate, self.autorelocateunits})
|
||||
--self:T({self.autorelocate, self.autorelocateunits})
|
||||
end
|
||||
return self
|
||||
end
|
||||
@@ -739,7 +756,7 @@ do
|
||||
local HQGroup = self.HQ_CC
|
||||
if self.autorelocateunits.HQ and self.HQ_CC and HQGroup:IsAlive() then --only relocate if HQ exists
|
||||
local _hqgrp = self.HQ_CC
|
||||
self:T(self.lid.." Relocating HQ")
|
||||
--self:T(self.lid.." Relocating HQ")
|
||||
local text = self.lid.." Relocating HQ"
|
||||
--local m= MESSAGE:New(text,10,"MANTIS"):ToAll()
|
||||
_hqgrp:RelocateGroundRandomInRadius(20,500,true,true)
|
||||
@@ -752,7 +769,7 @@ do
|
||||
local EWR_Grps = EWR_GRP.Set --table of objects in SET_GROUP
|
||||
for _,_grp in pairs (EWR_Grps) do
|
||||
if _grp:IsAlive() and _grp:IsGround() then
|
||||
self:T(self.lid.." Relocating EWR ".._grp:GetName())
|
||||
--self:T(self.lid.." Relocating EWR ".._grp:GetName())
|
||||
local text = self.lid.." Relocating EWR ".._grp:GetName()
|
||||
local m= MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug)
|
||||
if self.verbose then self:I(text) end
|
||||
@@ -778,12 +795,14 @@ do
|
||||
for _,_coord in pairs (set) do
|
||||
local coord = _coord -- get current coord to check
|
||||
-- output for cross-check
|
||||
local dectstring = coord:ToStringLLDMS()
|
||||
local samstring = samcoordinate:ToStringLLDMS()
|
||||
local targetdistance = samcoordinate:DistanceFromPointVec2(coord)
|
||||
local text = string.format("Checking SAM at % s - Distance %d m - Target %s", samstring, targetdistance, dectstring)
|
||||
local m = MESSAGE:New(text,10,"Check"):ToAllIf(self.debug)
|
||||
if self.verbose then self:I(self.lid..text) end
|
||||
if self.verbose or self.debug then
|
||||
local dectstring = coord:ToStringLLDMS()
|
||||
local samstring = samcoordinate:ToStringLLDMS()
|
||||
local text = string.format("Checking SAM at % s - Distance %d m - Target %s", samstring, targetdistance, dectstring)
|
||||
local m = MESSAGE:New(text,10,"Check"):ToAllIf(self.debug)
|
||||
self:I(self.lid..text)
|
||||
end
|
||||
-- end output to cross-check
|
||||
if targetdistance <= radius then
|
||||
return true, targetdistance
|
||||
@@ -888,7 +907,7 @@ do
|
||||
end
|
||||
self.SAM_Table = SAM_Tbl
|
||||
-- make SAMs evasive
|
||||
local mysead = SEAD:New( SEAD_Grps )
|
||||
local mysead = SEAD:New( SEAD_Grps, self.Padding ) -- Functional.Sead#SEAD
|
||||
mysead:SetEngagementRange(engagerange)
|
||||
self.mysead = mysead
|
||||
return self
|
||||
@@ -999,9 +1018,11 @@ do
|
||||
self:__ShoradActivated(1,name, radius, ontime)
|
||||
end
|
||||
-- debug output
|
||||
local text = string.format("SAM %s switched to alarm state RED!", name)
|
||||
local m=MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug)
|
||||
if self.verbose then self:I(self.lid..text) end
|
||||
if self.debug or self.verbose then
|
||||
local text = string.format("SAM %s switched to alarm state RED!", name)
|
||||
local m=MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug)
|
||||
if self.verbose then self:I(self.lid..text) end
|
||||
end
|
||||
end --end alive
|
||||
else
|
||||
if samgroup:IsAlive() then
|
||||
@@ -1014,9 +1035,11 @@ do
|
||||
self:__GreenState(1,samgroup)
|
||||
self.SamStateTracker[name] = "GREEN"
|
||||
end
|
||||
local text = string.format("SAM %s switched to alarm state GREEN!", name)
|
||||
local m=MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug)
|
||||
if self.verbose then self:I(self.lid..text) end
|
||||
if self.debug or self.verbose then
|
||||
local text = string.format("SAM %s switched to alarm state GREEN!", name)
|
||||
local m=MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug)
|
||||
if self.verbose then self:I(self.lid..text) end
|
||||
end
|
||||
end --end alive
|
||||
end --end check
|
||||
end --for for loop
|
||||
@@ -1067,6 +1090,20 @@ do
|
||||
return self
|
||||
end
|
||||
|
||||
--- [Internal] Check DLink state
|
||||
-- @param #MANTIS self
|
||||
-- @return #MANTIS self
|
||||
function MANTIS:_CheckDLinkState()
|
||||
self:T(self.lid .. "_CheckDLinkState")
|
||||
local dlink = self.Detection -- Ops.Intelligence#INTEL_DLINK
|
||||
local TS = timer.getAbsTime()
|
||||
if not dlink:Is("Running") and (TS - self.DLTimeStamp > 29) then
|
||||
self.DLink = false
|
||||
self.Detection = self:StartDetection() -- fall back
|
||||
self:I(self.lid .. "Intel DLink not running - switching back to single detection!")
|
||||
end
|
||||
end
|
||||
|
||||
--- [Internal] Function to set start state
|
||||
-- @param #MANTIS self
|
||||
-- @param #string From The From State
|
||||
@@ -1077,11 +1114,13 @@ do
|
||||
self:T({From, Event, To})
|
||||
self:T(self.lid.."Starting MANTIS")
|
||||
self:SetSAMStartState()
|
||||
self.Detection = self:StartDetection()
|
||||
if not self.DLink then
|
||||
self.Detection = self:StartDetection()
|
||||
end
|
||||
if self.advAwacs then
|
||||
self.AWACS_Detection = self:StartAwacsDetection()
|
||||
end
|
||||
self:__Status(self.detectinterval)
|
||||
self:__Status(-math.random(1,10))
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -1120,11 +1159,16 @@ do
|
||||
end
|
||||
end
|
||||
|
||||
-- timer for advanced state check
|
||||
-- advanced state check
|
||||
if self.advanced then
|
||||
self:_CheckAdvState()
|
||||
end
|
||||
|
||||
-- check DLink state
|
||||
if self.DLink then
|
||||
self:_CheckDLinkState()
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -1136,6 +1180,13 @@ do
|
||||
-- @return #MANTIS self
|
||||
function MANTIS:onafterStatus(From,Event,To)
|
||||
self:T({From, Event, To})
|
||||
-- Display some states
|
||||
if self.debug then
|
||||
self:I(self.lid .. "Status Report")
|
||||
for _name,_state in pairs(self.SamStateTracker) do
|
||||
self:I(string.format("Site %s\tStatus %s",_name,_state))
|
||||
end
|
||||
end
|
||||
local interval = self.detectinterval * -1
|
||||
self:__Status(interval)
|
||||
return self
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
-- ## Features:
|
||||
--
|
||||
-- * Track the missiles fired at you and other players, providing bearing and range information of the missiles towards the airplanes.
|
||||
-- * Provide alerts of missile launches, including detailed information of the units launching, including bearing, range <EFBFBD>
|
||||
-- * Provide alerts of missile launches, including detailed information of the units launching, including bearing, range °
|
||||
-- * Provide alerts when a missile would have killed your aircraft.
|
||||
-- * Provide alerts when the missile self destructs.
|
||||
-- * Enable / Disable and Configure the Missile Trainer using the various menu options.
|
||||
|
||||
@@ -5371,7 +5371,7 @@ function RAT:_ModifySpawnTemplate(waypoints, livery, spawnplace, departure, take
|
||||
|
||||
if spawnonground then
|
||||
|
||||
-- Sh<EFBFBD>ps and FARPS seem to have a build in queue.
|
||||
-- Sh°ps and FARPS seem to have a build in queue.
|
||||
if spawnonship or spawnonfarp or spawnonrunway or automatic then
|
||||
self:T(RAT.id..string.format("RAT group %s spawning at farp, ship or runway %s.", self.alias, departure:GetName()))
|
||||
|
||||
|
||||
@@ -32,7 +32,9 @@
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ## Missions: Example missions will be added later.
|
||||
-- ## Missions:
|
||||
--
|
||||
-- * [MAR - On the Range - MOOSE - SC](https://www.digitalcombatsimulator.com/en/files/3317765/) by shagrat
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
@@ -2558,7 +2560,7 @@ function RANGE:_DisplayBombTargets(_unitname)
|
||||
-- Get elevation
|
||||
local elevation=coord:GetLandHeight()
|
||||
local eltxt=string.format("%d m", elevation)
|
||||
if _settings:IsImperial() then
|
||||
if not _settings:IsMetric() then
|
||||
elevation=UTILS.MetersToFeet(elevation)
|
||||
eltxt=string.format("%d ft", elevation)
|
||||
end
|
||||
@@ -2829,6 +2831,7 @@ function RANGE:_CheckInZone(_unitName)
|
||||
local accur=0
|
||||
if shots>0 then
|
||||
accur=_result.hits/shots*100
|
||||
if accur > 100 then accur = 100 end
|
||||
end
|
||||
|
||||
-- Message text.
|
||||
|
||||
@@ -17,14 +17,15 @@
|
||||
--
|
||||
-- ### Authors: **FlightControl**, **applevangelist**
|
||||
--
|
||||
-- Last Update: July 2021
|
||||
-- Last Update: Aug 2021
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @module Functional.Sead
|
||||
-- @image SEAD.JPG
|
||||
|
||||
--- @type SEAD
|
||||
---
|
||||
-- @type SEAD
|
||||
-- @extends Core.Base#BASE
|
||||
|
||||
--- Make SAM sites execute evasive and defensive behaviour when being fired upon.
|
||||
@@ -48,7 +49,8 @@ SEAD = {
|
||||
},
|
||||
SEADGroupPrefixes = {},
|
||||
SuppressedGroups = {},
|
||||
EngagementRange = 75 -- default 75% engagement range Feature Request #1355
|
||||
EngagementRange = 75, -- default 75% engagement range Feature Request #1355
|
||||
Padding = 10,
|
||||
}
|
||||
|
||||
--- Missile enumerators
|
||||
@@ -59,7 +61,7 @@ SEAD = {
|
||||
["AGM_122"] = "AGM_122",
|
||||
["AGM_84"] = "AGM_84",
|
||||
["AGM_45"] = "AGM_45",
|
||||
["ALARN"] = "ALARM",
|
||||
["ALARM"] = "ALARM",
|
||||
["LD-10"] = "LD-10",
|
||||
["X_58"] = "X_58",
|
||||
["X_28"] = "X_28",
|
||||
@@ -68,17 +70,35 @@ SEAD = {
|
||||
["Kh25"] = "Kh25",
|
||||
}
|
||||
|
||||
--- Missile enumerators - from DCS ME and Wikipedia
|
||||
-- @field HarmData
|
||||
SEAD.HarmData = {
|
||||
-- km and mach
|
||||
["AGM_88"] = { 150, 3},
|
||||
["AGM_45"] = { 12, 2},
|
||||
["AGM_122"] = { 16.5, 2.3},
|
||||
["AGM_84"] = { 280, 0.85},
|
||||
["ALARM"] = { 45, 2},
|
||||
["LD-10"] = { 60, 4},
|
||||
["X_58"] = { 70, 4},
|
||||
["X_28"] = { 80, 2.5},
|
||||
["X_25"] = { 25, 0.76},
|
||||
["X_31"] = {150, 3},
|
||||
["Kh25"] = {25, 0.8},
|
||||
}
|
||||
|
||||
--- Creates the main object which is handling defensive actions for SA sites or moving SA vehicles.
|
||||
-- When an anti radiation missile is fired (KH-58, KH-31P, KH-31A, KH-25MPU, HARM missiles), the SA will shut down their radars and will take evasive actions...
|
||||
-- Chances are big that the missile will miss.
|
||||
-- @param #SEAD self
|
||||
-- @param table{string,...}|string SEADGroupPrefixes which is a table of Prefixes of the SA Groups in the DCS mission editor on which evasive actions need to be taken.
|
||||
-- @param #table SEADGroupPrefixes Table of #string entries or single #string, which is a table of Prefixes of the SA Groups in the DCS mission editor on which evasive actions need to be taken.
|
||||
-- @param #number Padding (Optional) Extra number of seconds to add to radar switch-back-on time
|
||||
-- @return SEAD
|
||||
-- @usage
|
||||
-- -- CCCP SEAD Defenses
|
||||
-- -- Defends the Russian SA installations from SEAD attacks.
|
||||
-- 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 )
|
||||
function SEAD:New( SEADGroupPrefixes, Padding )
|
||||
|
||||
local self = BASE:Inherit( self, BASE:New() )
|
||||
self:F( SEADGroupPrefixes )
|
||||
@@ -91,8 +111,13 @@ function SEAD:New( SEADGroupPrefixes )
|
||||
self.SEADGroupPrefixes[SEADGroupPrefixes] = SEADGroupPrefixes
|
||||
end
|
||||
|
||||
local padding = Padding or 10
|
||||
if padding < 10 then padding = 10 end
|
||||
self.Padding = padding
|
||||
|
||||
self:HandleEvent( EVENTS.Shot, self.HandleEventShot )
|
||||
self:I("*** SEAD - Started Version 0.2.8")
|
||||
|
||||
self:I("*** SEAD - Started Version 0.3.1")
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -102,7 +127,7 @@ end
|
||||
-- @return #SEAD self
|
||||
function SEAD:UpdateSet( SEADGroupPrefixes )
|
||||
|
||||
self:F( SEADGroupPrefixes )
|
||||
self:T( SEADGroupPrefixes )
|
||||
|
||||
if type( SEADGroupPrefixes ) == 'table' then
|
||||
for SEADGroupPrefixID, SEADGroupPrefix in pairs( SEADGroupPrefixes ) do
|
||||
@@ -120,7 +145,7 @@ end
|
||||
-- @param #number range Set the engagement range in percent, e.g. 50
|
||||
-- @return self
|
||||
function SEAD:SetEngagementRange(range)
|
||||
self:F( { range } )
|
||||
self:T( { range } )
|
||||
range = range or 75
|
||||
if range < 0 or range > 100 then
|
||||
range = 75
|
||||
@@ -130,17 +155,61 @@ function SEAD:SetEngagementRange(range)
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set the padding in seconds, which extends the radar off time calculated by SEAD
|
||||
-- @param #SEAD self
|
||||
-- @param #number Padding Extra number of seconds to add for the switch-on
|
||||
function SEAD:SetPadding(Padding)
|
||||
self:T( { Padding } )
|
||||
local padding = Padding or 10
|
||||
if padding < 10 then padding = 10 end
|
||||
self.Padding = padding
|
||||
return self
|
||||
end
|
||||
|
||||
--- Check if a known HARM was fired
|
||||
-- @param #SEAD self
|
||||
-- @param #string WeaponName
|
||||
-- @return #boolean Returns true for a match
|
||||
-- @return #string name Name of hit in table
|
||||
function SEAD:_CheckHarms(WeaponName)
|
||||
self:F( { WeaponName } )
|
||||
self:T( { WeaponName } )
|
||||
local hit = false
|
||||
local name = ""
|
||||
for _,_name in pairs (SEAD.Harms) do
|
||||
if string.find(WeaponName,_name,1) then hit = true end
|
||||
if string.find(WeaponName,_name,1) then
|
||||
hit = true
|
||||
name = _name
|
||||
break
|
||||
end
|
||||
end
|
||||
return hit
|
||||
return hit, name
|
||||
end
|
||||
|
||||
--- (Internal) Return distance in meters between two coordinates or -1 on error.
|
||||
-- @param #SEAD self
|
||||
-- @param Core.Point#COORDINATE _point1 Coordinate one
|
||||
-- @param Core.Point#COORDINATE _point2 Coordinate two
|
||||
-- @return #number Distance in meters
|
||||
function SEAD:_GetDistance(_point1, _point2)
|
||||
self:T("_GetDistance")
|
||||
if _point1 and _point2 then
|
||||
local distance1 = _point1:Get2DDistance(_point2)
|
||||
local distance2 = _point1:DistanceFromPointVec2(_point2)
|
||||
--self:T({dist1=distance1, dist2=distance2})
|
||||
if distance1 and type(distance1) == "number" then
|
||||
return distance1
|
||||
elseif distance2 and type(distance2) == "number" then
|
||||
return distance2
|
||||
else
|
||||
self:E("*****Cannot calculate distance!")
|
||||
self:E({_point1,_point2})
|
||||
return -1
|
||||
end
|
||||
else
|
||||
self:E("******Cannot calculate distance!")
|
||||
self:E({_point1,_point2})
|
||||
return -1
|
||||
end
|
||||
end
|
||||
|
||||
--- 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.
|
||||
@@ -148,35 +217,38 @@ end
|
||||
-- @param #SEAD
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
function SEAD:HandleEventShot( EventData )
|
||||
self:T( { EventData } )
|
||||
|
||||
self:T( { EventData.id } )
|
||||
local SEADPlane = EventData.IniUnit -- Wrapper.Unit#UNIT
|
||||
local SEADPlanePos = SEADPlane:GetCoordinate() -- Core.Point#COORDINATE
|
||||
local SEADUnit = EventData.IniDCSUnit
|
||||
local SEADUnitName = EventData.IniDCSUnitName
|
||||
local SEADWeapon = EventData.Weapon -- Identify the weapon fired
|
||||
local SEADWeaponName = EventData.WeaponName -- return weapon type
|
||||
|
||||
self:T( "*** SEAD - Missile Launched = " .. SEADWeaponName)
|
||||
self:T({ SEADWeapon })
|
||||
--self:T({ SEADWeapon })
|
||||
|
||||
if self:_CheckHarms(SEADWeaponName) then
|
||||
self:T( '*** SEAD - Weapon Match' )
|
||||
local _targetskill = "Random"
|
||||
local _targetMimgroupName = "none"
|
||||
local _evade = math.random (1,100) -- random number for chance of evading action
|
||||
local _targetMim = EventData.Weapon:getTarget() -- Identify target
|
||||
local _targetUnit = UNIT:Find(_targetMim) -- Unit name by DCS Object
|
||||
local _targetgroupname = "none"
|
||||
local _target = EventData.Weapon:getTarget() -- Identify target
|
||||
local _targetUnit = UNIT:Find(_target) -- Wrapper.Unit#UNIT
|
||||
local _targetgroup = nil -- Wrapper.Group#GROUP
|
||||
if _targetUnit and _targetUnit:IsAlive() then
|
||||
local _targetMimgroup = _targetUnit:GetGroup()
|
||||
local _targetMimgroupName = _targetMimgroup:GetName() -- group name
|
||||
--local _targetskill = _DATABASE.Templates.Units[_targetUnit].Template.skill
|
||||
self:T( self.SEADGroupPrefixes )
|
||||
self:T( _targetMimgroupName )
|
||||
_targetgroup = _targetUnit:GetGroup()
|
||||
_targetgroupname = _targetgroup:GetName() -- group name
|
||||
local _targetUnitName = _targetUnit:GetName()
|
||||
_targetUnit:GetSkill()
|
||||
_targetskill = _targetUnit:GetSkill()
|
||||
end
|
||||
-- see if we are shot at
|
||||
local SEADGroupFound = false
|
||||
for SEADGroupPrefixID, SEADGroupPrefix in pairs( self.SEADGroupPrefixes ) do
|
||||
if string.find( _targetMimgroupName, SEADGroupPrefix, 1, true ) then
|
||||
self:T( SEADGroupPrefix )
|
||||
if string.find( _targetgroupname, SEADGroupPrefix, 1, true ) then
|
||||
SEADGroupFound = true
|
||||
self:T( '*** SEAD - Group Found' )
|
||||
self:T( '*** SEAD - Group Match Found' )
|
||||
break
|
||||
end
|
||||
end
|
||||
@@ -185,42 +257,67 @@ function SEAD:HandleEventShot( EventData )
|
||||
local Skills = { "Average", "Good", "High", "Excellent" }
|
||||
_targetskill = Skills[ math.random(1,4) ]
|
||||
end
|
||||
self:T( _targetskill )
|
||||
--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( string.format("*** SEAD - Evading, target skill " ..string.format(_targetskill)) )
|
||||
|
||||
local _targetMimgroup = Unit.getGroup(Weapon.getTarget(SEADWeapon))
|
||||
local _targetMimcont= _targetMimgroup:getController()
|
||||
|
||||
routines.groupRandomDistSelf(_targetMimgroup,300,'Diamond',250,20) -- move randomly
|
||||
|
||||
--tracker ID table to switch groups off and on again
|
||||
local id = {
|
||||
groupName = _targetMimgroup,
|
||||
ctrl = _targetMimcont
|
||||
}
|
||||
|
||||
local function SuppressionEnd(id) --switch group back on
|
||||
local range = self.EngagementRange -- Feature Request #1355
|
||||
self:T(string.format("*** SEAD - Engagement Range is %d", range))
|
||||
id.ctrl:setOption(AI.Option.Ground.id.ALARM_STATE,AI.Option.Ground.val.ALARM_STATE.RED)
|
||||
--id.groupName:enableEmission(true)
|
||||
id.ctrl:setOption(AI.Option.Ground.id.AC_ENGAGEMENT_RANGE_RESTRICTION,range) --Feature Request #1355
|
||||
self.SuppressedGroups[id.groupName] = nil --delete group id from table when done
|
||||
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
|
||||
-- randomize switch-on time
|
||||
local delay = math.random(self.TargetSkill[_targetskill].DelayOn[1], self.TargetSkill[_targetskill].DelayOn[2])
|
||||
local SuppressionEndTime = timer.getTime() + delay
|
||||
--create entry
|
||||
if self.SuppressedGroups[id.groupName] == nil then --no timer entry for this group yet
|
||||
self.SuppressedGroups[id.groupName] = {
|
||||
SuppressionEndTime = delay
|
||||
}
|
||||
Controller.setOption(_targetMimcont, AI.Option.Ground.id.ALARM_STATE,AI.Option.Ground.val.ALARM_STATE.GREEN)
|
||||
--_targetMimgroup:enableEmission(false)
|
||||
timer.scheduleFunction(SuppressionEnd, id, SuppressionEndTime) --Schedule the SuppressionEnd() function
|
||||
-- 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
|
||||
grp:OptionAlarmStateGreen()
|
||||
grp:RelocateGroundRandomInRadius(20,300,false,false,"Diamond")
|
||||
end
|
||||
|
||||
local function SuppressionStop(args)
|
||||
self:T(string.format("*** SEAD - %s Radar On",args[2]))
|
||||
local grp = args[1] -- Wrapper.Group#GROUP
|
||||
grp:OptionAlarmStateRed()
|
||||
grp:OptionEngageRange(self.EngagementRange)
|
||||
self.SuppressedGroups[args[2]] = false
|
||||
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
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -113,7 +113,7 @@ do
|
||||
["AGM_122"] = "AGM_122",
|
||||
["AGM_84"] = "AGM_84",
|
||||
["AGM_45"] = "AGM_45",
|
||||
["ALARN"] = "ALARM",
|
||||
["ALARM"] = "ALARM",
|
||||
["LD-10"] = "LD-10",
|
||||
["X_58"] = "X_58",
|
||||
["X_28"] = "X_28",
|
||||
|
||||
@@ -715,20 +715,21 @@ do -- ZONE_CAPTURE_COALITION
|
||||
|
||||
local UnitHit = EventData.TgtUnit
|
||||
|
||||
if UnitHit.ClassName ~= "SCENERY" then
|
||||
-- Check if unit is inside the capture zone and that it is of the defending coalition.
|
||||
if UnitHit and UnitHit:IsInZone(self) and UnitHit:GetCoalition()==self.Coalition then
|
||||
if UnitHit and UnitHit:IsInZone(self) and UnitHit:GetCoalition()==self.Coalition then
|
||||
|
||||
-- Update last hit time.
|
||||
self.HitTimeLast=timer.getTime()
|
||||
-- Update last hit time.
|
||||
self.HitTimeLast=timer.getTime()
|
||||
|
||||
-- Only trigger attacked event if not already in state "Attacked".
|
||||
if self:GetState()~="Attacked" then
|
||||
self:F2("Hit ==> Attack")
|
||||
self:Attack()
|
||||
end
|
||||
-- Only trigger attacked event if not already in state "Attacked".
|
||||
if self:GetState()~="Attacked" then
|
||||
self:F2("Hit ==> Attack")
|
||||
self:Attack()
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
@@ -890,12 +891,14 @@ do -- ZONE_CAPTURE_COALITION
|
||||
end
|
||||
|
||||
-- Status text.
|
||||
local text=string.format("CAPTURE ZONE %s: Owner=%s (Previous=%s): #blue=%d, #red=%d, Status %s", self:GetZoneName(), self:GetCoalitionName(), UTILS.GetCoalitionName(self:GetPreviousCoalition()), nBlue, nRed, State)
|
||||
local NewState = self:GetState()
|
||||
if NewState~=State then
|
||||
text=text..string.format(" --> %s", NewState)
|
||||
if false then
|
||||
local text=string.format("CAPTURE ZONE %s: Owner=%s (Previous=%s): #blue=%d, #red=%d, Status %s", self:GetZoneName(), self:GetCoalitionName(), UTILS.GetCoalitionName(self:GetPreviousCoalition()), nBlue, nRed, State)
|
||||
local NewState = self:GetState()
|
||||
if NewState~=State then
|
||||
text=text..string.format(" --> %s", NewState)
|
||||
end
|
||||
self:I(text)
|
||||
end
|
||||
self:I(text)
|
||||
|
||||
end
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ _DATABASE:_RegisterCargos()
|
||||
|
||||
--- Register zones.
|
||||
_DATABASE:_RegisterZones()
|
||||
_DATABASE:_RegisterAirbases()
|
||||
|
||||
--- Check if os etc is available.
|
||||
BASE:I("Checking de-sanitization of os, io and lfs:")
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,3 @@
|
||||
|
||||
--- **Ops** -- Combat Search and Rescue.
|
||||
--
|
||||
-- ===
|
||||
@@ -23,7 +22,7 @@
|
||||
-- @module Ops.CSAR
|
||||
-- @image OPS_CSAR.jpg
|
||||
|
||||
-- Date: July 2021
|
||||
-- Date: Oct 2021
|
||||
|
||||
-------------------------------------------------------------------------
|
||||
--- **CSAR** class, extends Core.Base#BASE, Core.Fsm#FSM
|
||||
@@ -70,6 +69,7 @@
|
||||
--
|
||||
-- self.allowDownedPilotCAcontrol = false -- Set to false if you don\'t want to allow control by Combined Arms.
|
||||
-- self.allowFARPRescue = true -- allows pilots to be rescued by landing at a FARP or Airbase. Else MASH only!
|
||||
-- self.FARPRescueDistance = 1000 -- you need to be this close to a FARP or Airport for the pilot to be rescued.
|
||||
-- self.autosmoke = false -- automatically smoke a downed pilot\'s location when a heli is near.
|
||||
-- self.autosmokedistance = 1000 -- distance for autosmoke
|
||||
-- self.coordtype = 1 -- Use Lat/Long DDM (0), Lat/Long DMS (1), MGRS (2), Bullseye imperial (3) or Bullseye metric (4) for coordinates.
|
||||
@@ -95,6 +95,16 @@
|
||||
-- self.approachdist_far = 5000 -- switch do 10 sec interval approach mode, meters
|
||||
-- self.approachdist_near = 3000 -- switch to 5 sec interval approach mode, meters
|
||||
-- self.pilotmustopendoors = false -- switch to true to enable check of open doors
|
||||
-- -- (added 0.1.9)
|
||||
-- self.suppressmessages = false -- switch off all messaging if you want to do your own
|
||||
-- -- (added 0.1.11)
|
||||
-- self.rescuehoverheight = 20 -- max height for a hovering rescue in meters
|
||||
-- self.rescuehoverdistance = 10 -- max distance for a hovering rescue in meters
|
||||
-- -- (added 0.1.12)
|
||||
-- -- Country codes for spawned pilots
|
||||
-- self.countryblue= country.id.USA
|
||||
-- self.countryred = country.id.RUSSIA
|
||||
-- self.countryneutral = country.id.UN_PEACEKEEPERS
|
||||
--
|
||||
-- ## 2.1 Experimental Features
|
||||
--
|
||||
@@ -214,26 +224,6 @@ CSAR = {
|
||||
-- @field Wrapper.Group#GROUP group Spawned group object.
|
||||
-- @field #number timestamp Timestamp for approach process
|
||||
-- @field #boolean alive Group is alive or dead/rescued
|
||||
--
|
||||
--- Updated and sorted list of known NDB beacons (in kHz!) from the available maps.
|
||||
|
||||
--[[ Moved to Utils
|
||||
-- @field #CSAR.SkipFrequencies
|
||||
CSAR.SkipFrequencies = {
|
||||
214,274,291.5,295,297.5,
|
||||
300.5,304,307,309.5,311,312,312.5,316,
|
||||
320,324,328,329,330,336,337,
|
||||
342,343,348,351,352,353,358,
|
||||
363,365,368,372.5,374,
|
||||
380,381,384,389,395,396,
|
||||
414,420,430,432,435,440,450,455,462,470,485,
|
||||
507,515,520,525,528,540,550,560,570,577,580,602,625,641,662,670,680,682,690,
|
||||
705,720,722,730,735,740,745,750,770,795,
|
||||
822,830,862,866,
|
||||
905,907,920,935,942,950,995,
|
||||
1000,1025,1030,1050,1065,1116,1175,1182,1210
|
||||
}
|
||||
--]]
|
||||
|
||||
--- All slot / Limit settings
|
||||
-- @type CSAR.AircraftType
|
||||
@@ -248,10 +238,11 @@ CSAR.AircraftType["Mi-8MTV2"] = 12
|
||||
CSAR.AircraftType["Mi-8MT"] = 12
|
||||
CSAR.AircraftType["Mi-24P"] = 8
|
||||
CSAR.AircraftType["Mi-24V"] = 8
|
||||
CSAR.AircraftType["Bell-47"] = 2
|
||||
|
||||
--- CSAR class version.
|
||||
-- @field #string version
|
||||
CSAR.version="0.1.8r3"
|
||||
CSAR.version="0.1.11r2"
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- ToDo list
|
||||
@@ -361,12 +352,13 @@ function CSAR:New(Coalition, Template, Alias)
|
||||
self.loadtimemax = 135 -- seconds
|
||||
self.radioSound = "beacon.ogg" -- the name of the sound file to use for the Pilot radio beacons. If this isnt added to the mission BEACONS WONT WORK!
|
||||
self.allowFARPRescue = true --allows pilot to be rescued by landing at a FARP or Airbase
|
||||
self.FARPRescueDistance = 1000 -- you need to be this close to a FARP or Airport for the pilot to be rescued.
|
||||
self.max_units = 6 --max number of pilots that can be carried
|
||||
self.useprefix = true -- Use the Prefixed defined below, Requires Unit have the Prefix defined below
|
||||
self.csarPrefix = { "helicargo", "MEDEVAC"} -- prefixes used for useprefix=true - DON\'T use # in names!
|
||||
self.template = Template or "generic" -- template for downed pilot
|
||||
self.mashprefix = {"MASH"} -- prefixes used to find MASHes
|
||||
self.mash = SET_GROUP:New():FilterCoalitions(self.coalition):FilterPrefixes(self.mashprefix):FilterOnce() -- currently only GROUP objects, maybe support STATICs also?
|
||||
|
||||
self.autosmoke = false -- automatically smoke location when heli is near
|
||||
self.autosmokedistance = 2000 -- distance for autosmoke
|
||||
-- added 0.1.4
|
||||
@@ -378,6 +370,16 @@ function CSAR:New(Coalition, Template, Alias)
|
||||
self.approachdist_far = 5000 -- switch do 10 sec interval approach mode, meters
|
||||
self.approachdist_near = 3000 -- switch to 5 sec interval approach mode, meters
|
||||
self.pilotmustopendoors = false -- switch to true to enable check on open doors
|
||||
self.suppressmessages = false
|
||||
|
||||
-- added 0.1.11r1
|
||||
self.rescuehoverheight = 20
|
||||
self.rescuehoverdistance = 10
|
||||
|
||||
-- added 0.1.12
|
||||
self.countryblue= country.id.USA
|
||||
self.countryred = country.id.RUSSIA
|
||||
self.countryneutral = country.id.UN_PEACEKEEPERS
|
||||
|
||||
-- WARNING - here\'ll be dragons
|
||||
-- for this to work you need to de-sanitize your mission environment in <DCS root>\Scripts\MissionScripting.lua
|
||||
@@ -565,6 +567,7 @@ function CSAR:_SpawnPilotInField(country,point,frequency)
|
||||
for i=1,10 do
|
||||
math.random(i,10000)
|
||||
end
|
||||
if point:IsSurfaceTypeWater() then point.y = 0 end
|
||||
local template = self.template
|
||||
local alias = string.format("Pilot %.2fkHz-%d", freq, math.random(1,99))
|
||||
local coalition = self.coalition
|
||||
@@ -642,7 +645,7 @@ function CSAR:_AddCsar(_coalition , _country, _point, _typeName, _unitName, _pla
|
||||
local _typeName = _typeName or "Pilot"
|
||||
|
||||
if not noMessage then
|
||||
self:_DisplayToAllSAR("MAYDAY MAYDAY! " .. _typeName .. " is down. ", self.coalition, 10)
|
||||
self:_DisplayToAllSAR("MAYDAY MAYDAY! " .. _typeName .. " is down. ", self.coalition, self.messageTime)
|
||||
end
|
||||
|
||||
if _freq then
|
||||
@@ -703,11 +706,11 @@ function CSAR:_SpawnCsarAtZone( _zone, _coalition, _description, _randomPoint, _
|
||||
|
||||
local _country = 0
|
||||
if _coalition == coalition.side.BLUE then
|
||||
_country = country.id.USA
|
||||
_country = self.countryblue
|
||||
elseif _coalition == coalition.side.RED then
|
||||
_country = country.id.RUSSIA
|
||||
_country = self.countryred
|
||||
else
|
||||
_country = country.id.UN_PEACEKEEPERS
|
||||
_country = self.countryneutral
|
||||
end
|
||||
|
||||
self:_AddCsar(_coalition, _country, pos, typename, unitname, _description, freq, _nomessage, _description, forcedesc)
|
||||
@@ -734,7 +737,6 @@ function CSAR:SpawnCSARAtZone(Zone, Coalition, Description, RandomPoint, Nomessa
|
||||
return self
|
||||
end
|
||||
|
||||
-- TODO: Split in functions per Event type
|
||||
--- (Internal) Event handler.
|
||||
-- @param #CSAR self
|
||||
function CSAR:_EventHandler(EventData)
|
||||
@@ -806,8 +808,7 @@ function CSAR:_EventHandler(EventData)
|
||||
if self:_DoubleEjection(_unitname) then
|
||||
return
|
||||
end
|
||||
self:_DisplayToAllSAR("MAYDAY MAYDAY! " .. _unit:GetTypeName() .. " shot down. No Chute!", self.coalition, 10)
|
||||
--local m = MESSAGE:New("MAYDAY MAYDAY! " .. _unit:GetTypeName() .. " shot down. No Chute!",10,"Info"):ToCoalition(self.coalition)
|
||||
|
||||
else
|
||||
self:T(self.lid .. " Pilot has not taken off, ignore")
|
||||
end
|
||||
@@ -894,11 +895,7 @@ function CSAR:_EventHandler(EventData)
|
||||
end
|
||||
|
||||
if _place:GetCoalition() == self.coalition or _place:GetCoalition() == coalition.side.NEUTRAL then
|
||||
if self.pilotmustopendoors and not self:_IsLoadingDoorOpen(_event.IniUnitName) then
|
||||
self:_DisplayMessageToSAR(_unit, "Open the door to let me out!", self.messageTime, true)
|
||||
else
|
||||
self:_RescuePilots(_unit)
|
||||
end
|
||||
self:_ScheduledSARFlight(_event.IniUnitName,_event.IniGroupName,true)
|
||||
else
|
||||
self:T(string.format("Airfield %d, Unit %d", _place:GetCoalition(), _unit:GetCoalition()))
|
||||
end
|
||||
@@ -918,7 +915,6 @@ end
|
||||
function CSAR:_InitSARForPilot(_downedGroup, _GroupName, _freq, _nomessage)
|
||||
self:T(self.lid .. " _InitSARForPilot")
|
||||
local _leader = _downedGroup:GetUnit(1)
|
||||
--local _groupName = _downedGroup:GetName()
|
||||
local _groupName = _GroupName
|
||||
local _freqk = _freq / 1000
|
||||
local _coordinatesText = self:_GetPositionOfWounded(_downedGroup)
|
||||
@@ -934,7 +930,7 @@ function CSAR:_InitSARForPilot(_downedGroup, _GroupName, _freq, _nomessage)
|
||||
end
|
||||
|
||||
-- trigger FSM event
|
||||
self:__PilotDown(2,_downedGroup, _freqk, _leadername, _coordinatesText)
|
||||
self:__PilotDown(2,_downedGroup, _freqk, _groupName, _coordinatesText)
|
||||
|
||||
return self
|
||||
end
|
||||
@@ -1104,7 +1100,6 @@ function CSAR:_PickupUnit(_heliUnit, _pilotName, _woundedGroup, _woundedGroupNam
|
||||
local grouptable = downedgrouptable --#CSAR.DownedPilot
|
||||
self.inTransitGroups[_heliName][_woundedGroupName] =
|
||||
{
|
||||
-- DONE: Fix with #CSAR.DownedPilot
|
||||
originalUnit = grouptable.originalUnit,
|
||||
woundedGroup = _woundedGroupName,
|
||||
side = self.coalition,
|
||||
@@ -1112,7 +1107,7 @@ function CSAR:_PickupUnit(_heliUnit, _pilotName, _woundedGroup, _woundedGroupNam
|
||||
player = grouptable.player,
|
||||
}
|
||||
|
||||
_woundedGroup:Destroy()
|
||||
_woundedGroup:Destroy(false)
|
||||
self:_RemoveNameFromDownedPilots(_woundedGroupName,true)
|
||||
|
||||
self:_DisplayMessageToSAR(_heliUnit, string.format("%s: %s I\'m in! Get to the MASH ASAP! ", _heliName, _pilotName), self.messageTime,true,true)
|
||||
@@ -1143,39 +1138,7 @@ end
|
||||
-- @return #boolean outcome The outcome.
|
||||
function CSAR:_IsLoadingDoorOpen( unit_name )
|
||||
self:T(self.lid .. " _IsLoadingDoorOpen")
|
||||
local ret_val = false
|
||||
local unit = Unit.getByName(unit_name)
|
||||
if unit ~= nil then
|
||||
local type_name = unit:getTypeName()
|
||||
|
||||
if type_name == "Mi-8MT" and unit:getDrawArgumentValue(86) == 1 or unit:getDrawArgumentValue(250) == 1 then
|
||||
self:T(unit_name .. " Cargo doors are open or cargo door not present")
|
||||
ret_val = true
|
||||
end
|
||||
|
||||
if type_name == "Mi-24P" and unit:getDrawArgumentValue(38) == 1 or unit:getDrawArgumentValue(86) == 1 then
|
||||
self:T(unit_name .. " a side door is open")
|
||||
ret_val = true
|
||||
end
|
||||
|
||||
if type_name == "UH-1H" and unit:getDrawArgumentValue(43) == 1 or unit:getDrawArgumentValue(44) == 1 then
|
||||
self:T(unit_name .. " a side door is open ")
|
||||
ret_val = true
|
||||
end
|
||||
|
||||
if string.find(type_name, "SA342" ) and unit:getDrawArgumentValue(34) == 1 or unit:getDrawArgumentValue(38) == 1 then
|
||||
self:T(unit_name .. " front door(s) are open")
|
||||
ret_val = true
|
||||
end
|
||||
|
||||
if ret_val == false then
|
||||
self:T(unit_name .. " all doors are closed")
|
||||
end
|
||||
return ret_val
|
||||
|
||||
end -- nil
|
||||
|
||||
return false
|
||||
return UTILS.IsLoadingDoorOpen(unit_name)
|
||||
end
|
||||
|
||||
--- (Internal) Function to check if heli is close to group.
|
||||
@@ -1206,14 +1169,12 @@ function CSAR:_CheckCloseWoundedGroup(_distance, _heliUnit, _heliName, _woundedG
|
||||
else
|
||||
self:_DisplayMessageToSAR(_heliUnit, string.format("%s: %s. You\'re close now! Land in a safe place, I will go there ", _heliName, _pilotName), self.messageTime,false,true)
|
||||
end
|
||||
--mark as shown for THIS heli and THIS group
|
||||
self.heliCloseMessage[_lookupKeyHeli] = true
|
||||
end
|
||||
|
||||
-- have we landed close enough?
|
||||
if not _heliUnit:InAir() then
|
||||
|
||||
-- if you land on them, doesnt matter if they were heading to someone else as you\'re closer, you win! :)
|
||||
if self.pilotRuntoExtractPoint == true then
|
||||
if (_distance < self.extractDistance) then
|
||||
local _time = self.landedStatus[_lookupKeyHeli]
|
||||
@@ -1257,15 +1218,16 @@ function CSAR:_CheckCloseWoundedGroup(_distance, _heliUnit, _heliName, _woundedG
|
||||
end
|
||||
|
||||
if _heliUnit:InAir() and _unitsInHelicopter + 1 <= _maxUnits then
|
||||
|
||||
if _distance < 8.0 then
|
||||
-- TODO - make variable
|
||||
if _distance < self.rescuehoverdistance then
|
||||
|
||||
--check height!
|
||||
local leaderheight = _woundedLeader:GetHeight()
|
||||
if leaderheight < 0 then leaderheight = 0 end
|
||||
local _height = _heliUnit:GetHeight() - leaderheight
|
||||
|
||||
if _height <= 20.0 then
|
||||
-- TODO - make variable
|
||||
if _height <= self.rescuehoverheight then
|
||||
|
||||
local _time = self.hoverStatus[_lookupKeyHeli]
|
||||
|
||||
@@ -1314,7 +1276,8 @@ end
|
||||
-- @param #CSAR self
|
||||
-- @param #string heliname Heli name
|
||||
-- @param #string groupname Group name
|
||||
function CSAR:_ScheduledSARFlight(heliname,groupname)
|
||||
-- @param #boolean isairport If true, EVENT.Landing took place at an airport or FARP
|
||||
function CSAR:_ScheduledSARFlight(heliname,groupname, isairport)
|
||||
self:T(self.lid .. " _ScheduledSARFlight")
|
||||
self:T({heliname,groupname})
|
||||
local _heliUnit = self:_GetSARHeli(heliname)
|
||||
@@ -1337,8 +1300,8 @@ function CSAR:_ScheduledSARFlight(heliname,groupname)
|
||||
return
|
||||
end
|
||||
|
||||
if _dist < 200 and _heliUnit:InAir() == false then
|
||||
if self.pilotmustopendoors and not self:_IsLoadingDoorOpen(heliname) then
|
||||
if ( _dist < self.FARPRescueDistance or isairport ) and _heliUnit:InAir() == false then
|
||||
if self.pilotmustopendoors and self:_IsLoadingDoorOpen(heliname) == false then
|
||||
self:_DisplayMessageToSAR(_heliUnit, "Open the door to let me out!", self.messageTime, true)
|
||||
else
|
||||
self:_RescuePilots(_heliUnit)
|
||||
@@ -1347,7 +1310,7 @@ function CSAR:_ScheduledSARFlight(heliname,groupname)
|
||||
end
|
||||
|
||||
--queue up
|
||||
self:__Returning(-5,heliname,_woundedGroupName)
|
||||
self:__Returning(-5,heliname,_woundedGroupName, isairport)
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -1364,7 +1327,6 @@ function CSAR:_RescuePilots(_heliUnit)
|
||||
return
|
||||
end
|
||||
|
||||
-- DONE: count saved units?
|
||||
local PilotsSaved = self:_PilotsOnboard(_heliName)
|
||||
|
||||
self.inTransitGroups[_heliName] = nil
|
||||
@@ -1403,7 +1365,9 @@ function CSAR:_DisplayMessageToSAR(_unit, _text, _time, _clear, _speak)
|
||||
local group = _unit:GetGroup()
|
||||
local _clear = _clear or nil
|
||||
local _time = _time or self.messageTime
|
||||
local m = MESSAGE:New(_text,_time,"Info",_clear):ToGroup(group)
|
||||
if not self.suppressmessages then
|
||||
local m = MESSAGE:New(_text,_time,"Info",_clear):ToGroup(group)
|
||||
end
|
||||
-- integrate SRS
|
||||
if _speak and self.useSRS then
|
||||
local srstext = SOUNDTEXT:New(_text)
|
||||
@@ -1458,7 +1422,6 @@ function CSAR:_DisplayActiveSAR(_unitName)
|
||||
local _groupName = _value.name
|
||||
self:T(string.format("Display Active Pilot: %s", tostring(_groupName)))
|
||||
self:T({Table=_value})
|
||||
--local _woundedGroup = GROUP:FindByName(_groupName)
|
||||
local _woundedGroup = _value.group
|
||||
if _woundedGroup and _value.alive then
|
||||
local _coordinatesText = self:_GetPositionOfWounded(_woundedGroup)
|
||||
@@ -1501,10 +1464,17 @@ function CSAR:_GetClosestDownedPilot(_heli)
|
||||
local _shortestDistance = -1
|
||||
local _distance = 0
|
||||
local _closestGroupInfo = nil
|
||||
local _heliCoord = _heli:GetCoordinate()
|
||||
local _heliCoord = _heli:GetCoordinate() or _heli:GetCoordinate()
|
||||
|
||||
if _heliCoord == nil then
|
||||
self:E("****Error obtaining coordinate!")
|
||||
return nil
|
||||
end
|
||||
|
||||
local DownedPilotsTable = self.downedPilots
|
||||
for _, _groupInfo in pairs(DownedPilotsTable) do
|
||||
|
||||
for _, _groupInfo in UTILS.spairs(DownedPilotsTable) do
|
||||
--for _, _groupInfo in pairs(DownedPilotsTable) do
|
||||
local _woundedName = _groupInfo.name
|
||||
local _tempWounded = _groupInfo.group
|
||||
|
||||
@@ -1570,12 +1540,11 @@ end
|
||||
-- @param #number _messagetime How long to show.
|
||||
function CSAR:_DisplayToAllSAR(_message, _side, _messagetime)
|
||||
self:T(self.lid .. " _DisplayToAllSAR")
|
||||
local messagetime = _messagetime or self.messageTime
|
||||
for _, _unitName in pairs(self.csarUnits) do
|
||||
local _unit = self:_GetSARHeli(_unitName)
|
||||
if _unit then
|
||||
if not _messagetime then
|
||||
self:_DisplayMessageToSAR(_unit, _message, _messagetime)
|
||||
end
|
||||
if _unit and not self.suppressmessages then
|
||||
self:_DisplayMessageToSAR(_unit, _message, _messagetime)
|
||||
end
|
||||
end
|
||||
return self
|
||||
@@ -1747,9 +1716,20 @@ end
|
||||
function CSAR:_GetDistance(_point1, _point2)
|
||||
self:T(self.lid .. " _GetDistance")
|
||||
if _point1 and _point2 then
|
||||
local distance = _point1:DistanceFromPointVec2(_point2)
|
||||
return distance
|
||||
local distance1 = _point1:Get2DDistance(_point2)
|
||||
local distance2 = _point1:DistanceFromPointVec2(_point2)
|
||||
if distance1 and type(distance1) == "number" then
|
||||
return distance1
|
||||
elseif distance2 and type(distance2) == "number" then
|
||||
return distance2
|
||||
else
|
||||
self:E("*****Cannot calculate distance!")
|
||||
self:E({_point1,_point2})
|
||||
return -1
|
||||
end
|
||||
else
|
||||
self:E("******Cannot calculate distance!")
|
||||
self:E({_point1,_point2})
|
||||
return -1
|
||||
end
|
||||
end
|
||||
@@ -1758,7 +1738,6 @@ end
|
||||
-- @param #CSAR self
|
||||
function CSAR:_GenerateVHFrequencies()
|
||||
self:T(self.lid .. " _GenerateVHFrequencies")
|
||||
--local _skipFrequencies = self.SkipFrequencies
|
||||
|
||||
local FreeVHFFrequencies = {}
|
||||
FreeVHFFrequencies = UTILS.GenerateVHFrequencies()
|
||||
@@ -1798,7 +1777,6 @@ function CSAR:_GetClockDirection(_heli, _group)
|
||||
if _heading then
|
||||
local Aspect = Angle - _heading
|
||||
if Aspect == 0 then Aspect = 360 end
|
||||
--clock = math.floor(Aspect / 30)
|
||||
clock = math.abs(UTILS.Round((Aspect / 30),0))
|
||||
if clock == 0 then clock = 12 end
|
||||
end
|
||||
@@ -1907,6 +1885,7 @@ function CSAR:onafterStart(From, Event, To)
|
||||
else
|
||||
self.allheligroupset = SET_GROUP:New():FilterCoalitions(self.coalitiontxt):FilterCategoryHelicopter():FilterStart()
|
||||
end
|
||||
self.mash = SET_GROUP:New():FilterCoalitions(self.coalitiontxt):FilterPrefixes(self.mashprefix):FilterStart() -- currently only GROUP objects, maybe support STATICs also?
|
||||
self:__Status(-10)
|
||||
return self
|
||||
end
|
||||
@@ -1915,19 +1894,19 @@ end
|
||||
-- @param #CSAR self
|
||||
function CSAR:_CheckDownedPilotTable()
|
||||
local pilots = self.downedPilots
|
||||
for _,_entry in pairs (pilots) do
|
||||
self:T("Checking for " .. _entry.name)
|
||||
self:T({entry=_entry})
|
||||
local group = _entry.group
|
||||
if not group:IsAlive() then
|
||||
self:T("Group is dead")
|
||||
if _entry.alive == true then
|
||||
self:T("Switching .alive to false")
|
||||
local npilots = {}
|
||||
|
||||
for _ind,_entry in pairs(pilots) do
|
||||
local _group = _entry.group
|
||||
if _group:IsAlive() then
|
||||
npilots[_ind] = _entry
|
||||
else
|
||||
if _entry.alive then
|
||||
self:__KIA(1,_entry.desc)
|
||||
self:_RemoveNameFromDownedPilots(_entry.name,true)
|
||||
end
|
||||
end
|
||||
end
|
||||
self.downedPilots = npilots
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -2047,9 +2026,10 @@ end
|
||||
-- @param #string To To state.
|
||||
-- @param #string Heliname Name of the helicopter group.
|
||||
-- @param #string Woundedgroupname Name of the downed pilot\'s group.
|
||||
function CSAR:onbeforeReturning(From, Event, To, Heliname, Woundedgroupname)
|
||||
-- @param #boolean IsAirport True if heli has landed on an AFB (from event land).
|
||||
function CSAR:onbeforeReturning(From, Event, To, Heliname, Woundedgroupname, IsAirPort)
|
||||
self:T({From, Event, To, Heliname, Woundedgroupname})
|
||||
self:_ScheduledSARFlight(Heliname,Woundedgroupname)
|
||||
self:_ScheduledSARFlight(Heliname,Woundedgroupname, IsAirPort)
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -157,7 +157,7 @@ end
|
||||
--- Set the frequency for the radio transmission.
|
||||
-- If the transmitting positionable is a unit or group, this also set the command "SetFrequency" with the defined frequency and modulation.
|
||||
-- @param #RADIO self
|
||||
-- @param #number Frequency Frequency in MHz. Ranges allowed for radio transmissions in DCS : 30-87.995 / 108-173.995 / 225-399.975MHz.
|
||||
-- @param #number Frequency Frequency in MHz.
|
||||
-- @return #RADIO self
|
||||
function RADIO:SetFrequency(Frequency)
|
||||
self:F2(Frequency)
|
||||
@@ -165,7 +165,7 @@ function RADIO:SetFrequency(Frequency)
|
||||
if type(Frequency) == "number" then
|
||||
|
||||
-- If frequency is in range
|
||||
if (Frequency >= 30 and Frequency <= 87.995) or (Frequency >= 108 and Frequency <= 173.995) or (Frequency >= 225 and Frequency <= 399.975) then
|
||||
-- if (Frequency >= 30 and Frequency <= 87.995) or (Frequency >= 108 and Frequency <= 173.995) or (Frequency >= 225 and Frequency <= 399.975) then
|
||||
|
||||
-- Convert frequency from MHz to Hz
|
||||
self.Frequency = Frequency * 1000000
|
||||
@@ -186,10 +186,10 @@ function RADIO:SetFrequency(Frequency)
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
-- end
|
||||
end
|
||||
|
||||
self:E({"Frequency is outside of DCS Frequency ranges (30-80, 108-152, 225-400). Frequency unchanged.", Frequency})
|
||||
self:E({"Frequency is not a number. Frequency unchanged.", Frequency})
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
@@ -162,33 +162,32 @@ RADIOSPEECH.Vocabulary.RU = {
|
||||
["8000"] = { "8000", 0.92 },
|
||||
["9000"] = { "9000", 0.87 },
|
||||
|
||||
["Ñ<EFBFBD>тепени"] = { "degrees", 0.5 },
|
||||
["километров"] = { "kilometers", 0.65 },
|
||||
["градусы"] = { "degrees", 0.5 },
|
||||
["километры"] = { "kilometers", 0.65 },
|
||||
["km"] = { "kilometers", 0.65 },
|
||||
["миль"] = { "miles", 0.45 },
|
||||
["мили"] = { "miles", 0.45 },
|
||||
["mi"] = { "miles", 0.45 },
|
||||
["метры"] = { "meters", 0.41 },
|
||||
["метров"] = { "meters", 0.41 },
|
||||
["m"] = { "meters", 0.41 },
|
||||
["ноги"] = { "feet", 0.37 },
|
||||
["ноги"] = { "feet", 0.37 },
|
||||
|
||||
["br"] = { "br", 1.1 },
|
||||
["bra"] = { "bra", 0.3 },
|
||||
|
||||
["возвращение на базу"] = { "returning_to_base", 1.40 },
|
||||
["на пути к наземной цели"] = { "on_route_to_ground_target", 1.45 },
|
||||
["перехват боги"] = { "intercepting_bogeys", 1.22 },
|
||||
["поражение наземной цели"] = { "engaging_ground_target", 1.53 },
|
||||
["привлечение болотных птиц"] = { "engaging_bogeys", 1.68 },
|
||||
["колёса вверх..."] = { "wheels_up", 0.92 },
|
||||
["посадка на базу"] = { "landing at base", 1.04 },
|
||||
["патрулирование"] = { "patrolling", 0.96 },
|
||||
|
||||
["возвращаÑ<EFBFBD>Ñ<EFBFBD>ÑŒ на базу"] = { "returning_to_base", 1.40 },
|
||||
["на пути к наземной цели"] = { "on_route_to_ground_target", 1.45 },
|
||||
["перехват Ñ<>амолетов"] = { "intercepting_bogeys", 1.22 },
|
||||
["поражение наземной цели"] = { "engaging_ground_target", 1.53 },
|
||||
["захватывающие Ñ<>амолеты"] = { "engaging_bogeys", 1.68 },
|
||||
["колеÑ<EFBFBD>а вверх"] = { "wheels_up", 0.92 },
|
||||
["поÑ<EFBFBD>адка на базу"] = { "landing at base", 1.04 },
|
||||
["патрулирующий"] = { "patrolling", 0.96 },
|
||||
|
||||
["за"] = { "for", 0.27 },
|
||||
["и"] = { "and", 0.17 },
|
||||
["в"] = { "at", 0.19 },
|
||||
["dot"] = { "dot", 0.51 },
|
||||
["defender"] = { "defender", 0.45 },
|
||||
["для"] = { "for", 0.27 },
|
||||
["и"] = { "and", 0.17 },
|
||||
["на сайте"] = { "at", 0.19 },
|
||||
["точка"] = { "dot", 0.51 },
|
||||
["защитник"] = { "defender", 0.45 },
|
||||
}
|
||||
|
||||
--- Create a new RADIOSPEECH object for a given radio frequency/modulation.
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
--
|
||||
-- ### Contributions:
|
||||
--
|
||||
-- * FlightControl : Rework to OO framework
|
||||
-- * FlightControl : Rework to OO framework.
|
||||
--
|
||||
-- @module Utils
|
||||
-- @image MOOSE.JPG
|
||||
@@ -339,18 +339,34 @@ UTILS.MetersToNM = function(meters)
|
||||
return meters/1852
|
||||
end
|
||||
|
||||
UTILS.KiloMetersToNM = function(kilometers)
|
||||
return kilometers/1852*1000
|
||||
end
|
||||
|
||||
UTILS.MetersToSM = function(meters)
|
||||
return meters/1609.34
|
||||
end
|
||||
|
||||
UTILS.KiloMetersToSM = function(kilometers)
|
||||
return kilometers/1609.34*1000
|
||||
end
|
||||
|
||||
UTILS.MetersToFeet = function(meters)
|
||||
return meters/0.3048
|
||||
end
|
||||
|
||||
UTILS.KiloMetersToFeet = function(kilometers)
|
||||
return kilometers/0.3048*1000
|
||||
end
|
||||
|
||||
UTILS.NMToMeters = function(NM)
|
||||
return NM*1852
|
||||
end
|
||||
|
||||
UTILS.NMToKiloMeters = function(NM)
|
||||
return NM*1852/1000
|
||||
end
|
||||
|
||||
UTILS.FeetToMeters = function(feet)
|
||||
return feet*0.3048
|
||||
end
|
||||
@@ -491,7 +507,7 @@ UTILS.tostringLL = function( lat, lon, acc, DMS)
|
||||
secFrmtStr = '%0' .. width .. '.' .. acc .. 'f'
|
||||
end
|
||||
|
||||
-- 024<EFBFBD> 23' 12"N or 024<EFBFBD> 23' 12.03"N
|
||||
-- 024° 23' 12"N or 024° 23' 12.03"N
|
||||
return string.format('%03d°', latDeg)..string.format('%02d', latMin)..'\''..string.format(secFrmtStr, latSec)..'"'..latHemi..' '
|
||||
.. string.format('%03d°', lonDeg)..string.format('%02d', lonMin)..'\''..string.format(secFrmtStr, lonSec)..'"'..lonHemi
|
||||
|
||||
@@ -907,7 +923,7 @@ function UTILS.RandomGaussian(x0, sigma, xmin, xmax, imax)
|
||||
local x1=math.random()
|
||||
local x2=math.random()
|
||||
|
||||
-- Transform to Gaussian exp(-(x-x0)²/(2*sigma²).
|
||||
-- Transform to Gaussian exp(-(x-x0)°/(2*sigma°).
|
||||
r = math.sqrt(-2*sigma*sigma * math.log(x1)) * math.cos(2*math.pi * x2) + x0
|
||||
|
||||
i=i+1
|
||||
@@ -961,6 +977,33 @@ function UTILS.VecNorm(a)
|
||||
return math.sqrt(UTILS.VecDot(a, a))
|
||||
end
|
||||
|
||||
--- Calculate the distance between two 2D vectors.
|
||||
-- @param DCS#Vec2 a Vector in 3D with x, y components.
|
||||
-- @param DCS#Vec2 b Vector in 3D with x, y components.
|
||||
-- @return #number Distance between the vectors.
|
||||
function UTILS.VecDist2D(a, b)
|
||||
|
||||
local c={x=b.x-a.x, y=b.y-a.y}
|
||||
|
||||
local d=math.sqrt(c.x*c.x+c.y*c.y)
|
||||
|
||||
return d
|
||||
end
|
||||
|
||||
|
||||
--- Calculate the distance between two 3D vectors.
|
||||
-- @param DCS#Vec3 a Vector in 3D with x, y, z components.
|
||||
-- @param DCS#Vec3 b Vector in 3D with x, y, z components.
|
||||
-- @return #number Distance between the vectors.
|
||||
function UTILS.VecDist3D(a, b)
|
||||
|
||||
local c={x=b.x-a.x, y=b.y-a.y, z=b.z-a.z}
|
||||
|
||||
local d=math.sqrt(UTILS.VecDot(c, c))
|
||||
|
||||
return d
|
||||
end
|
||||
|
||||
--- Calculate the [cross product](https://en.wikipedia.org/wiki/Cross_product) of two 3D vectors. The result is a 3D vector.
|
||||
-- @param DCS#Vec3 a Vector in 3D with x, y, z components.
|
||||
-- @param DCS#Vec3 b Vector in 3D with x, y, z components.
|
||||
@@ -1510,7 +1553,7 @@ end
|
||||
--@return #table
|
||||
function UTILS.ShuffleTable(t)
|
||||
if t == nil or type(t) ~= "table" then
|
||||
BASE:I("Error in ShuffleTable: Missing or wrong tyåe of Argument")
|
||||
BASE:I("Error in ShuffleTable: Missing or wrong type of Argument")
|
||||
return
|
||||
end
|
||||
math.random()
|
||||
@@ -1535,7 +1578,7 @@ function UTILS.IsLoadingDoorOpen( unit_name )
|
||||
if unit ~= nil then
|
||||
local type_name = unit:getTypeName()
|
||||
|
||||
if type_name == "Mi-8MT" and unit:getDrawArgumentValue(86) == 1 or unit:getDrawArgumentValue(250) == 1 then
|
||||
if type_name == "Mi-8MT" and unit:getDrawArgumentValue(38) == 1 or unit:getDrawArgumentValue(86) == 1 or unit:getDrawArgumentValue(250) < 0 then
|
||||
BASE:T(unit_name .. " Cargo doors are open or cargo door not present")
|
||||
ret_val = true
|
||||
end
|
||||
@@ -1555,6 +1598,26 @@ function UTILS.IsLoadingDoorOpen( unit_name )
|
||||
ret_val = true
|
||||
end
|
||||
|
||||
if string.find(type_name, "Hercules") and unit:getDrawArgumentValue(1215) == 1 and unit:getDrawArgumentValue(1216) == 1 then
|
||||
BASE:T(unit_name .. " rear doors are open")
|
||||
ret_val = true
|
||||
end
|
||||
|
||||
if string.find(type_name, "Hercules") and (unit:getDrawArgumentValue(1220) == 1 or unit:getDrawArgumentValue(1221) == 1) then
|
||||
BASE:T(unit_name .. " para doors are open")
|
||||
ret_val = true
|
||||
end
|
||||
|
||||
if string.find(type_name, "Hercules") and unit:getDrawArgumentValue(1217) == 1 then
|
||||
BASE:T(unit_name .. " side door is open")
|
||||
ret_val = true
|
||||
end
|
||||
|
||||
if string.find(type_name, "Bell-47") then -- bell aint got no doors so always ready to load injured soldiers
|
||||
BASE:T(unit_name .. " door is open")
|
||||
ret_val = true
|
||||
end
|
||||
|
||||
if ret_val == false then
|
||||
BASE:T(unit_name .. " all doors are closed")
|
||||
end
|
||||
@@ -1588,10 +1651,10 @@ function UTILS.GenerateVHFrequencies()
|
||||
local _skipFrequencies = {
|
||||
214,274,291.5,295,297.5,
|
||||
300.5,304,307,309.5,311,312,312.5,316,
|
||||
320,324,328,329,330,336,337,
|
||||
320,324,328,329,330,332,336,337,
|
||||
342,343,348,351,352,353,358,
|
||||
363,365,368,372.5,374,
|
||||
380,381,384,389,395,396,
|
||||
380,381,384,385,389,395,396,
|
||||
414,420,430,432,435,440,450,455,462,470,485,
|
||||
507,515,520,525,528,540,550,560,570,577,580,
|
||||
602,625,641,662,670,680,682,690,
|
||||
@@ -1698,7 +1761,7 @@ function UTILS.GenerateLaserCodes()
|
||||
while _code < 1777 and _count < 30 do
|
||||
while true do
|
||||
_code = _code + 1
|
||||
if not self:_ContainsDigit(_code, 8)
|
||||
if not ContainsDigit(_code, 8)
|
||||
and not ContainsDigit(_code, 9)
|
||||
and not ContainsDigit(_code, 0) then
|
||||
table.insert(jtacGeneratedLaserCodes, _code)
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
-- @field #table CategoryName Names of airbase categories.
|
||||
-- @field #string AirbaseName Name of the airbase.
|
||||
-- @field #number AirbaseID Airbase ID.
|
||||
-- @field Core.Zone#ZONE AirbaseZone Circular zone around the airbase with a radius of 2500 meters. For ships this is a ZONE_UNIT object.
|
||||
-- @field #number category Airbase category.
|
||||
-- @field #table descriptors DCS descriptors.
|
||||
-- @field #boolean isAirdrome Airbase is an airdrome.
|
||||
@@ -546,6 +547,13 @@ function AIRBASE:Register(AirbaseName)
|
||||
self.isHelipad=true
|
||||
elseif self.category==Airbase.Category.SHIP then
|
||||
self.isShip=true
|
||||
-- DCS bug: Oil rigs and gas platforms have category=2 (ship). Also they cannot be retrieved by coalition.getStaticObjects()
|
||||
if self.descriptors.typeName=="Oil rig" or self.descriptors.typeName=="Ga" then
|
||||
self.isHelipad=true
|
||||
self.isShip=false
|
||||
self.category=Airbase.Category.HELIPAD
|
||||
_DATABASE:AddStatic(AirbaseName)
|
||||
end
|
||||
else
|
||||
self:E("ERROR: Unknown airbase category!")
|
||||
end
|
||||
@@ -558,8 +566,14 @@ function AIRBASE:Register(AirbaseName)
|
||||
self:GetCoordinate()
|
||||
|
||||
if vec2 then
|
||||
-- TODO: For ships we need a moving zone.
|
||||
self.AirbaseZone=ZONE_RADIUS:New( AirbaseName, vec2, 2500 )
|
||||
if self.isShip then
|
||||
local unit=UNIT:FindByName(AirbaseName)
|
||||
if unit then
|
||||
self.AirbaseZone=ZONE_UNIT:New(AirbaseName, unit, 2500)
|
||||
end
|
||||
else
|
||||
self.AirbaseZone=ZONE_RADIUS:New(AirbaseName, vec2, 2500)
|
||||
end
|
||||
else
|
||||
self:E(string.format("ERROR: Cound not get position Vec2 of airbase %s", AirbaseName))
|
||||
end
|
||||
@@ -1006,10 +1020,19 @@ function AIRBASE:GetParkingSpotsTable(termtype)
|
||||
|
||||
local spot=self:_GetParkingSpotByID(_spot.Term_Index)
|
||||
|
||||
spot.Free=_isfree(_spot) -- updated
|
||||
spot.TOAC=_spot.TO_AC -- updated
|
||||
if spot then
|
||||
|
||||
spot.Free=_isfree(_spot) -- updated
|
||||
spot.TOAC=_spot.TO_AC -- updated
|
||||
|
||||
table.insert(spots, spot)
|
||||
|
||||
else
|
||||
|
||||
self:E(string.format("ERROR: Parking spot %s is nil!", tostring(_spot.Term_Index)))
|
||||
|
||||
end
|
||||
|
||||
table.insert(spots, spot)
|
||||
end
|
||||
|
||||
end
|
||||
@@ -1084,7 +1107,7 @@ function AIRBASE:MarkParkingSpots(termtype, mark)
|
||||
|
||||
-- Get airbase name.
|
||||
local airbasename=self:GetName()
|
||||
self:E(string.format("Parking spots at %s for termial type %s:", airbasename, tostring(termtype)))
|
||||
self:E(string.format("Parking spots at %s for terminal type %s:", airbasename, tostring(termtype)))
|
||||
|
||||
for _,_spot in pairs(parkingdata) do
|
||||
|
||||
@@ -1161,14 +1184,25 @@ function AIRBASE:FindFreeParkingSpotForAircraft(group, terminaltype, scanradius,
|
||||
parkingdata=parkingdata or self:GetParkingSpotsTable(terminaltype)
|
||||
|
||||
-- Get the aircraft size, i.e. it's longest side of x,z.
|
||||
local aircraft=group:GetUnit(1)
|
||||
local _aircraftsize, ax,ay,az=aircraft:GetObjectSize()
|
||||
local aircraft = nil -- fix local problem below
|
||||
local _aircraftsize, ax,ay,az
|
||||
if group and group.ClassName == "GROUP" then
|
||||
aircraft=group:GetUnit(1)
|
||||
_aircraftsize, ax,ay,az=aircraft:GetObjectSize()
|
||||
else
|
||||
-- SU27 dimensions
|
||||
_aircraftsize = 23
|
||||
ax = 23 -- length
|
||||
ay = 7 -- height
|
||||
az = 17 -- width
|
||||
end
|
||||
|
||||
|
||||
-- Number of spots we are looking for. Note that, e.g. grouping can require a number different from the group size!
|
||||
local _nspots=nspots or group:GetSize()
|
||||
|
||||
-- Debug info.
|
||||
self:E(string.format("%s: Looking for %d parking spot(s) for aircraft of size %.1f m (x=%.1f,y=%.1f,z=%.1f) at termial type %s.", airport, _nspots, _aircraftsize, ax, ay, az, tostring(terminaltype)))
|
||||
self:E(string.format("%s: Looking for %d parking spot(s) for aircraft of size %.1f m (x=%.1f,y=%.1f,z=%.1f) at terminal type %s.", airport, _nspots, _aircraftsize, ax, ay, az, tostring(terminaltype)))
|
||||
|
||||
-- Table of valid spots.
|
||||
local validspots={}
|
||||
@@ -1291,6 +1325,7 @@ function AIRBASE:FindFreeParkingSpotForAircraft(group, terminaltype, scanradius,
|
||||
|
||||
-- Retrun spots we found, even if there were not enough.
|
||||
return validspots
|
||||
|
||||
end
|
||||
|
||||
--- Check black and white lists.
|
||||
|
||||
@@ -3779,8 +3779,9 @@ end
|
||||
-- @param #number radius Radius of the relocation zone, default 500
|
||||
-- @param #boolean onroad If true, route on road (less problems with AI way finding), default true
|
||||
-- @param #boolean shortcut If true and onroad is set, take a shorter route - if available - off road, default false
|
||||
-- @param #string formation Formation string as in the mission editor, e.g. "Vee", "Diamond", "Line abreast", etc. Defaults to "Off Road"
|
||||
-- @return #CONTROLLABLE self
|
||||
function CONTROLLABLE:RelocateGroundRandomInRadius(speed, radius, onroad, shortcut)
|
||||
function CONTROLLABLE:RelocateGroundRandomInRadius(speed, radius, onroad, shortcut, formation)
|
||||
self:F2( { self.ControllableName } )
|
||||
|
||||
local _coord = self:GetCoordinate()
|
||||
@@ -3791,14 +3792,14 @@ function CONTROLLABLE:RelocateGroundRandomInRadius(speed, radius, onroad, shortc
|
||||
local _grptsk = {}
|
||||
local _candoroad = false
|
||||
local _shortcut = shortcut or false
|
||||
local _formation = formation or "Off Road"
|
||||
|
||||
-- create a DCS Task an push it on the group
|
||||
-- TaskGroundOnRoad(ToCoordinate,Speed,OffRoadFormation,Shortcut,FromCoordinate,WaypointFunction,WaypointFunctionArguments)
|
||||
if onroad then
|
||||
_grptsk, _candoroad = self:TaskGroundOnRoad(_tocoord,_speed,"Off Road",_shortcut)
|
||||
_grptsk, _candoroad = self:TaskGroundOnRoad(_tocoord,_speed,_formation,_shortcut)
|
||||
self:Route(_grptsk,5)
|
||||
else
|
||||
self:TaskRouteToVec2(_tocoord:GetVec2(),_speed,"Off Road")
|
||||
self:TaskRouteToVec2(_tocoord:GetVec2(),_speed,_formation)
|
||||
end
|
||||
|
||||
return self
|
||||
|
||||
@@ -1147,6 +1147,7 @@ function GROUP:GetAmmunition()
|
||||
local Nshells=0
|
||||
local Nrockets=0
|
||||
local Nmissiles=0
|
||||
local Nbombs=0
|
||||
|
||||
if DCSControllable then
|
||||
|
||||
@@ -1155,18 +1156,19 @@ function GROUP:GetAmmunition()
|
||||
local Unit = UnitData -- Wrapper.Unit#UNIT
|
||||
|
||||
-- Get ammo of the unit
|
||||
local ntot, nshells, nrockets, nmissiles = Unit:GetAmmunition()
|
||||
local ntot, nshells, nrockets, nbombs, nmissiles = Unit:GetAmmunition()
|
||||
|
||||
Ntot=Ntot+ntot
|
||||
Nshells=Nshells+nshells
|
||||
Nrockets=Nrockets+nrockets
|
||||
Nmissiles=Nmissiles+nmissiles
|
||||
Nbombs=Nbombs+nbombs
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return Ntot, Nshells, Nrockets, Nmissiles
|
||||
return Ntot, Nshells, Nrockets, Nbombs, Nmissiles
|
||||
end
|
||||
|
||||
|
||||
@@ -2576,8 +2578,10 @@ end
|
||||
-- @return #GROUP self
|
||||
function GROUP:SetCommandInvisible(switch)
|
||||
self:F2( self.GroupName )
|
||||
local switch = switch or false
|
||||
local SetInvisible = {id = 'SetInvisible', params = {value = true}}
|
||||
if switch==nil then
|
||||
switch=false
|
||||
end
|
||||
local SetInvisible = {id = 'SetInvisible', params = {value = switch}}
|
||||
self:SetCommand(SetInvisible)
|
||||
return self
|
||||
end
|
||||
@@ -2588,12 +2592,25 @@ end
|
||||
-- @return #GROUP self
|
||||
function GROUP:SetCommandImmortal(switch)
|
||||
self:F2( self.GroupName )
|
||||
local switch = switch or false
|
||||
local SetInvisible = {id = 'SetImmortal', params = {value = true}}
|
||||
self:SetCommand(SetInvisible)
|
||||
if switch==nil then
|
||||
switch=false
|
||||
end
|
||||
local SetImmortal = {id = 'SetImmortal', params = {value = switch}}
|
||||
self:SetCommand(SetImmortal)
|
||||
return self
|
||||
end
|
||||
|
||||
--- Get skill from Group. Effectively gets the skill from Unit 1 as the group holds no skill value.
|
||||
-- @param #GROUP self
|
||||
-- @return #string Skill String of skill name.
|
||||
function GROUP:GetSkill()
|
||||
self:F2( self.GroupName )
|
||||
local unit = self:GetUnit(1)
|
||||
local name = unit:GetName()
|
||||
local skill = _DATABASE.Templates.Units[name].Template.skill or "Random"
|
||||
return skill
|
||||
end
|
||||
|
||||
--do -- Smoke
|
||||
--
|
||||
----- Signal a flare at the position of the GROUP.
|
||||
|
||||
@@ -684,6 +684,27 @@ function POSITIONABLE:IsShip()
|
||||
end
|
||||
|
||||
|
||||
--- Returns if the unit is a submarine.
|
||||
-- @param #POSITIONABLE self
|
||||
-- @return #boolean Submarines attributes result.
|
||||
function POSITIONABLE:IsSubmarine()
|
||||
self:F2()
|
||||
|
||||
local DCSUnit = self:GetDCSObject()
|
||||
|
||||
if DCSUnit then
|
||||
local UnitDescriptor = DCSUnit:getDesc()
|
||||
if UnitDescriptor.attributes["Submarines"] == true then
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
--- Returns true if the POSITIONABLE is in the air.
|
||||
-- Polymorphic, is overridden in GROUP and UNIT.
|
||||
-- @param Wrapper.Positionable#POSITIONABLE self
|
||||
@@ -1513,6 +1534,7 @@ do -- Cargo
|
||||
["Ural-4320 APA-5D"] = 10,
|
||||
["Ural-4320T"] = 14,
|
||||
["ZBD04A"] = 7, -- new by kappa
|
||||
["VAB_Mephisto"] = 8, -- new by Apple
|
||||
}
|
||||
|
||||
local CargoBayWeightLimit = ( Weights[Desc.typeName] or 0 ) * 95
|
||||
|
||||
@@ -1426,3 +1426,13 @@ function UNIT:EnableEmission(switch)
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Get skill from Unit.
|
||||
-- @param #UNIT self
|
||||
-- @return #string Skill String of skill name.
|
||||
function UNIT:GetSkill()
|
||||
self:F2( self.UnitName )
|
||||
local name = self.UnitName
|
||||
local skill = _DATABASE.Templates.Units[name].Template.skill or "Random"
|
||||
return skill
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user