This commit is contained in:
FlightControl 2017-06-01 13:18:50 +02:00
parent 531f8a9106
commit 17838d7099
5 changed files with 248 additions and 102 deletions

View File

@ -168,26 +168,27 @@ do -- AI_A2A_DISPATCHER
function AI_A2A_DISPATCHER:onafterCAP( SquadronName, Repeat )
local A2AType = "CAP"
self.DefenderSquadrons["CAP"] = self.DefenderSquadrons["CAP"] or {}
self.DefenderSquadrons["CAP"][SquadronName] = self.DefenderSquadrons["CAP"][SquadronName] or {}
self.DefenderSquadrons[SquadronName] = self.DefenderSquadrons[SquadronName] or {}
self.DefenderSquadrons[SquadronName].Cap = self.DefenderSquadrons[SquadronName].Cap or {}
local CAP = self.DefenderSquadrons["CAP"][SquadronName]
if CAP then
local DefenderSquadron = self.DefenderSquadrons[SquadronName]
local Cap = DefenderSquadron.Cap
if Cap then
if self:CanCAP( SquadronName ) then
local AIGroup = CAP.Spawn:Spawn()
local Spawn = DefenderSquadron.Spawn[ math.random( 1, #DefenderSquadron.Spawn ) ]
local AIGroup = Spawn:SpawnAtAirbase( DefenderSquadron.Airbase )
self:F( { AIGroup = AIGroup:GetName() } )
if AIGroup then
local Fsm = AI_A2A_CAP:New( AIGroup, CAP.Zone, CAP.FloorAltitude, CAP.CeilingAltitude, CAP.MinSpeed, CAP.MaxSpeed, CAP.AltType )
local Fsm = AI_A2A_CAP:New( AIGroup, Cap.Zone, Cap.FloorAltitude, Cap.CeilingAltitude, Cap.MinSpeed, Cap.MaxSpeed, Cap.AltType )
Fsm:__Patrol( 1 )
self.DefenderTasks = self.DefenderTasks or {}
self.DefenderTasks[AIGroup] = self.DefenderTasks[AIGroup] or {}
self.DefenderTasks[AIGroup].Type = A2AType
self.DefenderTasks[AIGroup].Type = "CAP"
self.DefenderTasks[AIGroup].Fsm = Fsm
end
end
@ -202,8 +203,6 @@ do -- AI_A2A_DISPATCHER
function AI_A2A_DISPATCHER:onafterENGAGE( From, Event, To, TargetSetUnit, TargetReference, AIGroups )
local A2AType = "CAP"
self:F( { AIGroups = AIGroups } )
if AIGroups then
@ -229,33 +228,35 @@ do -- AI_A2A_DISPATCHER
function AI_A2A_DISPATCHER:onafterINTERCEPT( From, Event, To, TargetSetUnit, TargetReference, DefendersMissing )
local A2AType = "INTERCEPT"
local ClosestDistance = 0
local ClosestINTERCEPTName = nil
local ClosestDefenderSquadronName = nil
local AttackerCount = TargetSetUnit:Count()
local DefendersCount = 0
while( DefendersCount < DefendersMissing ) do
for INTERCEPTName, INTERCEPT in pairs( self.DefenderSquadrons[A2AType] or {} ) do
local SpawnCoord = INTERCEPT.Spawn:GetCoordinate() -- Core.Point#COORDINATE
local TargetCoord = TargetSetUnit:GetFirst():GetCoordinate()
local Distance = SpawnCoord:Get2DDistance( TargetCoord )
if ClosestDistance == 0 or Distance < ClosestDistance then
ClosestDistance = Distance
ClosestINTERCEPTName = INTERCEPTName
for SquadronName, DefenderSquadron in pairs( self.DefenderSquadrons or {} ) do
for InterceptID, Intercept in pairs( DefenderSquadron.Intercept or {} ) do
local SpawnCoord = DefenderSquadron.Airbase:GetCoordinate() -- Core.Point#COORDINATE
local TargetCoord = TargetSetUnit:GetFirst():GetCoordinate()
local Distance = SpawnCoord:Get2DDistance( TargetCoord )
if ClosestDistance == 0 or Distance < ClosestDistance then
ClosestDistance = Distance
ClosestDefenderSquadronName = SquadronName
end
end
end
if ClosestINTERCEPTName then
if ClosestDefenderSquadronName then
local INTERCEPT = self.DefenderSquadrons[A2AType][ClosestINTERCEPTName]
local DefenderSquadron = self.DefenderSquadrons[ClosestDefenderSquadronName]
local Intercept = self.DefenderSquadrons[ClosestDefenderSquadronName].Intercept
local AIGroup = INTERCEPT.Spawn:Spawn()
local Spawn = DefenderSquadron.Spawn[ math.random( 1, #DefenderSquadron.Spawn ) ]
local AIGroup = Spawn:SpawnAtAirbase( DefenderSquadron.Airbase )
self:F( { AIGroup = AIGroup:GetName() } )
if AIGroup then
@ -267,7 +268,7 @@ do -- AI_A2A_DISPATCHER
self.DefenderTasks = self.DefenderTasks or {}
self.DefenderTasks[AIGroup] = self.DefenderTasks[AIGroup] or {}
self.DefenderTasks[AIGroup].Type = A2AType
self.DefenderTasks[AIGroup].Type = "INTERCEPT"
self.DefenderTasks[AIGroup].Fsm = Fsm
self.DefenderTasks[AIGroup].Target = TargetReference
@ -284,53 +285,80 @@ do -- AI_A2A_DISPATCHER
end
function AI_A2A_DISPATCHER:SetSquadron( SquadronName, AirbaseName, SpawnTemplates, Resources )
function AI_A2A_DISPATCHER:SetCAP( SquadronName, Spawn, Zone, FloorAltitude, CeilingAltitude, MinSpeed, MaxSpeed, AltType )
self.DefenderSquadrons["CAP"] = self.DefenderSquadrons["CAP"] or {}
self.DefenderSquadrons["CAP"][SquadronName] = self.DefenderSquadrons["CAP"][SquadronName] or {}
local CAP = self.DefenderSquadrons["CAP"][SquadronName]
CAP.Name = SquadronName
CAP.Spawn = Spawn -- Funtional.Spawn#SPAWN
CAP.Zone = Zone
CAP.FloorAltitude = FloorAltitude
CAP.CeilingAltitude = CeilingAltitude
CAP.MinSpeed = MinSpeed
CAP.MaxSpeed = MaxSpeed
CAP.AltType = AltType
self.DefenderSquadrons[SquadronName] = self.DefenderSquadrons[SquadronName] or {}
self:SetCAPInterval( SquadronName, 60, 300, 1 )
local DefenderSquadron = self.DefenderSquadrons[SquadronName]
self:E( { AirbaseName = AirbaseName } )
DefenderSquadron.Airbase = AIRBASE:FindByName( AirbaseName )
self:E( { Airbase = DefenderSquadron.Airbase } )
self:E( { AirbaseObject = DefenderSquadron.Airbase:GetDCSObject() } )
DefenderSquadron.Spawn = {}
if type( SpawnTemplates ) == "string" then
local SpawnTemplate = SpawnTemplates
DefenderSquadron.Spawn[1] = SPAWN:New( SpawnTemplate )
else
for TemplateID, SpawnTemplate in pairs( SpawnTemplates ) do
DefenderSquadron.Spawn[#DefenderSquadron.Spawn+1] = SPAWN:New( SpawnTemplate )
end
end
DefenderSquadron.Resources = Resources
end
function AI_A2A_DISPATCHER:SetCAP( SquadronName, Zone, FloorAltitude, CeilingAltitude, MinSpeed, MaxSpeed, AltType )
self.DefenderSquadrons[SquadronName] = self.DefenderSquadrons[SquadronName] or {}
self.DefenderSquadrons[SquadronName].Cap = self.DefenderSquadrons[SquadronName].Cap or {}
local DefenderSquadron = self.DefenderSquadrons[SquadronName]
local Cap = self.DefenderSquadrons[SquadronName].Cap
Cap.Name = SquadronName
Cap.Zone = Zone
Cap.FloorAltitude = FloorAltitude
Cap.CeilingAltitude = CeilingAltitude
Cap.MinSpeed = MinSpeed
Cap.MaxSpeed = MaxSpeed
Cap.AltType = AltType
self:SetCAPInterval( SquadronName, 2, 180, 600, 1 )
end
function AI_A2A_DISPATCHER:SetCAPInterval( SquadronName, LowInterval, HighInterval, Probability )
function AI_A2A_DISPATCHER:SetCAPInterval( SquadronName, Limit, LowInterval, HighInterval, Probability )
self.DefenderSquadrons["CAP"] = self.DefenderSquadrons["CAP"] or {}
self.DefenderSquadrons["CAP"][SquadronName] = self.DefenderSquadrons["CAP"][SquadronName] or {}
self.DefenderSquadrons[SquadronName] = self.DefenderSquadrons[SquadronName] or {}
self.DefenderSquadrons[SquadronName].Cap = self.DefenderSquadrons[SquadronName].Cap or {}
local CAP = self.DefenderSquadrons["CAP"][SquadronName]
if CAP then
CAP.LowInterval = LowInterval
CAP.HighInterval = HighInterval
CAP.Probability = Probability
local DefenderSquadron = self.DefenderSquadrons[SquadronName]
local Cap = self.DefenderSquadrons[SquadronName].Cap
if Cap then
Cap.LowInterval = LowInterval
Cap.HighInterval = HighInterval
Cap.Probability = Probability
Cap.Limit = Limit
else
error( "This squadron does not exist:" .. SquadronName )
end
self:__CAP( self:GetCAPDelay( SquadronName ), SquadronName, true )
self:__CAP( -self:GetCAPDelay( SquadronName ), SquadronName, true )
end
function AI_A2A_DISPATCHER:GetCAPDelay( SquadronName )
self.DefenderSquadrons["CAP"] = self.DefenderSquadrons["CAP"] or {}
self.DefenderSquadrons["CAP"][SquadronName] = self.DefenderSquadrons["CAP"][SquadronName] or {}
self.DefenderSquadrons[SquadronName] = self.DefenderSquadrons[SquadronName] or {}
self.DefenderSquadrons[SquadronName].Cap = self.DefenderSquadrons[SquadronName].Cap or {}
local CAP = self.DefenderSquadrons["CAP"][SquadronName]
local DefenderSquadron = self.DefenderSquadrons[SquadronName]
if CAP then
return math.random( CAP.LowInterval, CAP.HighInterval )
local Cap = self.DefenderSquadrons[SquadronName].Cap
if Cap then
return math.random( Cap.LowInterval, Cap.HighInterval )
else
error( "This squadron does not exist:" .. SquadronName )
end
@ -338,33 +366,34 @@ do -- AI_A2A_DISPATCHER
function AI_A2A_DISPATCHER:CanCAP( SquadronName )
self.DefenderSquadrons["CAP"] = self.DefenderSquadrons["CAP"] or {}
self.DefenderSquadrons["CAP"][SquadronName] = self.DefenderSquadrons["CAP"][SquadronName] or {}
self.DefenderSquadrons[SquadronName] = self.DefenderSquadrons[SquadronName] or {}
self.DefenderSquadrons[SquadronName].Cap = self.DefenderSquadrons[SquadronName].Cap or {}
local CAP = self.DefenderSquadrons["CAP"][SquadronName]
local DefenderSquadron = self.DefenderSquadrons[SquadronName]
if CAP then
local Probability = math.random()
if Probability < CAP.Probability then
return true
else
return false
local Cap = self.DefenderSquadrons[SquadronName].Cap
if Cap then
local CapCount = self:CountCapAirborne( SquadronName )
if CapCount < Cap.Limit then
local Probability = math.random()
if Probability <= Cap.Probability then
return true
end
end
return false
else
error( "This squadron does not exist:" .. SquadronName )
end
end
function AI_A2A_DISPATCHER:SetINTERCEPT( Name, Spawn, MinSpeed, MaxSpeed )
function AI_A2A_DISPATCHER:SetINTERCEPT( SquadronName, MinSpeed, MaxSpeed )
self.DefenderSquadrons["INTERCEPT"] = self.DefenderSquadrons["INTERCEPT"] or {}
self.DefenderSquadrons["INTERCEPT"][Name] = self.DefenderSquadrons["INTERCEPT"][Name] or {}
self.DefenderSquadrons[SquadronName] = self.DefenderSquadrons[SquadronName] or {}
self.DefenderSquadrons[SquadronName].Intercept = self.DefenderSquadrons[SquadronName].Intercept or {}
local INTERCEPT = self.DefenderSquadrons["INTERCEPT"][Name]
INTERCEPT.Name = Name
INTERCEPT.Spawn = Spawn
INTERCEPT.MinSpeed = MinSpeed
INTERCEPT.MaxSpeed = MaxSpeed
local Intercept = self.DefenderSquadrons[SquadronName].Intercept
Intercept.Name = SquadronName
Intercept.MaxSpeed = MaxSpeed
end
@ -393,6 +422,22 @@ do -- AI_A2A_DISPATCHER
return nil
end
function AI_A2A_DISPATCHER:CountCapAirborne( SquadronName )
-- First, count the active AIGroups Units, targetting the DetectedSet
local CapCount = 0
local DefenderSquadron = self.DefenderSquadrons[SquadronName]
if DefenderSquadron then
for InterceptID, Intercept in pairs( DefenderSquadron.Intercept or {} ) do
CapCount = CapCount + 1
end
end
return CapCount
end
function AI_A2A_DISPATCHER:CountDefendersEngaged( DetectedItem )
@ -613,27 +658,27 @@ do -- AI_A2A_DISPATCHER
-- @param #boolean DetectedItemID
-- @param #boolean DetectedItemChange
-- @return Tasking.Task#TASK
function AI_A2A_DISPATCHER:EvaluateRemoveTask( DetectedItem, A2A_Index )
function AI_A2A_DISPATCHER:EvaluateRemoveTask( DetectedItem, TargetIndex )
local A2A_Target = self.DefenderTargets[A2A_Index]
local DefenderTarget = self.DefenderTargets[TargetIndex]
if A2A_Target then
if DefenderTarget then
for AIGroupName, AIGroup in pairs( A2A_Target.Groups ) do
for AIGroupName, AIGroup in pairs( DefenderTarget.Groups ) do
local AIGroup = AIGroup -- Wrapper.Group#GROUP
if not AIGroup:IsAlive() then
self.DefenderTasks[AIGroup] = nil
self.DefenderTargets[A2A_Index].Groups[AIGroupName] = nil
self.DefenderTargets[TargetIndex].Groups[AIGroupName] = nil
end
end
local DetectedSet = DetectedItem.Set -- Core.Set#SET_UNIT
if DetectedSet:Count() == 0 then
self.DefenderTargets[A2A_Index] = nil
self.DefenderTargets[TargetIndex] = nil
end
end
return self.DefenderTargets[A2A_Index]
return self.DefenderTargets[TargetIndex]
end

View File

@ -21,6 +21,9 @@
-- @module AI_A2A_Intercept
BASE:TraceClass("AI_A2A_INTERCEPT")
--- @type AI_A2A_INTERCEPT
-- @extends AI.AI_A2A#AI_A2A
@ -352,32 +355,40 @@ function AI_A2A_INTERCEPT:onafterEngage( AIGroup, From, Event, To, AttackSetUnit
local EngageRoute = {}
--- Calculate the current route point.
local CurrentVec2 = AIGroup:GetVec2()
--TODO: Create GetAltitude function for GROUP, and delete GetUnit(1).
local CurrentAltitude = AIGroup:GetUnit(1):GetAltitude()
local CurrentSpeed = AIGroup:GetUnit(1):GetVelocityKMH()
local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y )
local ToEngageZoneSpeed = self.PatrolMaxSpeed
local CurrentRoutePoint = CurrentPointVec3:RoutePointAir(
self.PatrolAltType,
POINT_VEC3.RoutePointType.TurningPoint,
POINT_VEC3.RoutePointAction.TurningPoint,
CurrentSpeed,
true
)
EngageRoute[#EngageRoute+1] = CurrentRoutePoint
local CurrentCoord = AIGroup:GetCoordinate()
local ToTargetSpeed = math.random( self.MinSpeed, self.MaxSpeed )
if not AIGroup:GetUnit(1):IsAboveRunway() then
--TODO: Create GetAltitude function for GROUP, and delete GetUnit(1).
local CurrentSpeed = AIGroup:GetUnit(1):GetVelocityKMH()
local ToEngageZoneSpeed = self.PatrolMaxSpeed
local CurrentRoutePoint = CurrentCoord:RoutePointAir(
self.PatrolAltType,
POINT_VEC3.RoutePointType.TurningPoint,
POINT_VEC3.RoutePointAction.TurningPoint,
ToTargetSpeed,
true
)
EngageRoute[#EngageRoute+1] = CurrentRoutePoint
end
--- Find the target point.
local ToTargetCoord = self.AttackSetUnit:GetFirst():GetCoordinate()
local ToTargetSpeed = math.random( self.MinSpeed, self.MaxSpeed )
local ToInterceptAngle = CurrentCoord:GetAngleDegrees( CurrentCoord:GetDirectionVec3( ToTargetCoord ) )
local ToInterceptCoord = CurrentCoord:Translate( 5000, ToInterceptAngle )
self:T2( { self.MinSpeed, self.MaxSpeed, ToTargetSpeed } )
--- Create a route point of type air.
local ToPatrolRoutePoint = ToTargetCoord:RoutePointAir(
local ToPatrolRoutePoint = ToInterceptCoord:RoutePointAir(
self.PatrolAltType,
POINT_VEC3.RoutePointType.TurningPoint,
POINT_VEC3.RoutePointAction.TurningPoint,
@ -386,6 +397,8 @@ function AI_A2A_INTERCEPT:onafterEngage( AIGroup, From, Event, To, AttackSetUnit
)
EngageRoute[#EngageRoute+1] = ToPatrolRoutePoint
AIGroup:OptionROEOpenFire()
AIGroup:OptionROTPassiveDefense()
@ -394,7 +407,7 @@ function AI_A2A_INTERCEPT:onafterEngage( AIGroup, From, Event, To, AttackSetUnit
for AttackUnitID, AttackUnit in pairs( self.AttackSetUnit:GetSet() ) do
local AttackUnit = AttackUnit -- Wrapper.Unit#UNIT
self:T( { AttackUnit, AttackUnit:IsAlive(), AttackUnit:IsAir() } )
self:T( { "Intercepting Unit:", AttackUnit:GetName(), AttackUnit:IsAlive(), AttackUnit:IsAir() } )
if AttackUnit:IsAlive() and AttackUnit:IsAir() then
AttackTasks[#AttackTasks+1] = AIGroup:TaskAttackUnit( AttackUnit )
end

View File

@ -138,12 +138,13 @@ do -- COORDINATE
ClassName = "COORDINATE",
}
--- COORDINATE constructor.
-- @param #COORDINATE self
-- @param Dcs.DCSTypes#Distance x The x coordinate of the Vec3 point, pointing to the North.
-- @param Dcs.DCSTypes#Distance y The y coordinate of the Vec3 point, pointing to the Right.
-- @param Dcs.DCSTypes#Distance z The z coordinate of the Vec3 point, pointing to the Right.
-- @return Core.Point#COORDINATE
-- @return #COORDINATE
function COORDINATE:New( x, y, z )
local self = BASE:Inherit( self, BASE:New() ) -- #COORDINATE
@ -177,7 +178,7 @@ do -- COORDINATE
--- Create a new COORDINATE object from Vec3 coordinates.
-- @param #COORDINATE self
-- @param Dcs.DCSTypes#Vec3 Vec3 The Vec3 point.
-- @return Core.Point#COORDINATE
-- @return #COORDINATE
function COORDINATE:NewFromVec3( Vec3 )
local self = self:New( Vec3.x, Vec3.y, Vec3.z ) -- #COORDINATE

View File

@ -957,6 +957,68 @@ function SPAWN:OnSpawnGroup( SpawnCallBackFunction, ... )
return self
end
--- Will spawn a group at an airbase.
-- This method is mostly advisable to be used if you want to simulate spawning units at an airbase.
-- Note that each point in the route assigned to the spawning group is reset to the point of the spawn.
-- You can use the returned group to further define the route to be followed.
-- @param #SPAWN self
-- @param Wrapper.Airbase#AIRBASE Airbase The @{Airbase} where to spawn the group.
-- @param #number SpawnIndex (optional) The index which group to spawn within the given zone.
-- @return Wrapper.Group#GROUP that was spawned.
-- @return #nil Nothing was spawned.
function SPAWN:SpawnAtAirbase( Airbase, SpawnIndex )
self:F( { self.SpawnTemplatePrefix, Airbase, SpawnIndex } )
local PointVec3 = Airbase:GetPointVec3()
self:T2(PointVec3)
if SpawnIndex then
else
SpawnIndex = self.SpawnIndex + 1
end
if self:_GetSpawnIndex( SpawnIndex ) then
local SpawnTemplate = self.SpawnGroups[self.SpawnIndex].SpawnTemplate
if SpawnTemplate then
self:T( { "Current point of ", self.SpawnTemplatePrefix, Airbase } )
-- Translate the position of the Group Template to the Vec3.
for UnitID = 1, #SpawnTemplate.units do
self:T( 'Before Translation SpawnTemplate.units['..UnitID..'].x = ' .. SpawnTemplate.units[UnitID].x .. ', SpawnTemplate.units['..UnitID..'].y = ' .. SpawnTemplate.units[UnitID].y )
local UnitTemplate = SpawnTemplate.units[UnitID]
local SX = UnitTemplate.x
local SY = UnitTemplate.y
local BX = SpawnTemplate.route.points[1].x
local BY = SpawnTemplate.route.points[1].y
local TX = PointVec3.x + ( SX - BX )
local TY = PointVec3.z + ( SY - BY )
SpawnTemplate.units[UnitID].x = TX
SpawnTemplate.units[UnitID].y = TY
SpawnTemplate.units[UnitID].alt = PointVec3.y
self:T( 'After Translation SpawnTemplate.units['..UnitID..'].x = ' .. SpawnTemplate.units[UnitID].x .. ', SpawnTemplate.units['..UnitID..'].y = ' .. SpawnTemplate.units[UnitID].y )
end
SpawnTemplate.route.points[1].x = PointVec3.x
SpawnTemplate.route.points[1].y = PointVec3.z
SpawnTemplate.route.points[1].alt = Airbase.y
SpawnTemplate.route.points[1].action = "From Parking Area Hot"
SpawnTemplate.route.points[1].type = "TakeOffParkingHot"
SpawnTemplate.route.points[1].airdromeId = Airbase:GetID()
SpawnTemplate.x = PointVec3.x
SpawnTemplate.y = PointVec3.z
return self:SpawnWithIndex( self.SpawnIndex )
end
end
return nil
end
--- Will spawn a group from a Vec3 in 3D space.
-- This method is mostly advisable to be used if you want to simulate spawning units in the air, like helicopters or airplanes.

View File

@ -47,7 +47,7 @@
-- the first letter of the method is also capitalized. So, by example, the DCS Airbase method @{DCSWrapper.Airbase#Airbase.getName}()
-- is implemented in the AIRBASE class as @{#AIRBASE.GetName}().
--
-- @field #AIRBASE
-- @field #AIRBASE AIRBASE
AIRBASE = {
ClassName="AIRBASE",
CategoryName = {
@ -57,6 +57,31 @@ AIRBASE = {
},
}
--- @field Caucasus
AIRBASE.Caucasus = {
["Gelendzhik"] = "Gelendzhik",
["Krasnodar_Pashkovsky"] = "Krasnodar-Pashkovsky",
["Sukhumi_Babushara"] = "Sukhumi-Babushara",
["Gudauta"] = "Gudauta",
["Batumi"] = "Batumi",
["Senaki_Kolkhi"] = "Senaki-Kolkhi",
["Kobuleti"] = "Kobuleti",
["Kutaisi"] = "Kutaisi",
["Tbilisi_Lochini"] = "Tbilisi-Lochini",
["Soganlug"] = "Soganlug",
["Vaziani"] = "Vaziani",
["Anapa_Vityazevo"] = "Anapa-Vityazevo",
["Krasnodar_Center"] = "Krasnodar-Center",
["Novorossiysk"] = "Novorossiysk",
["Krymsk"] = "Krymsk",
["Maykop_Khanskaya"] = "Maykop-Khanskaya",
["Sochi_Adler"] = "Sochi-Adler",
["Mineralnye_Vody"] = "Mineralnye Vody",
["Nalchik"] = "Nalchik",
["Mozdok"] = "Mozdok",
["Beslan"] = "Beslan",
}
-- Registration.
--- Create a new AIRBASE from DCSAirbase.