mirror of
https://github.com/FlightControl-Master/MOOSE.git
synced 2025-08-15 10:47:21 +00:00
commit
d66672a29c
@ -386,7 +386,7 @@ function AI_A2A_CAP:onafterEngage( AICap, From, Event, To, AttackSetUnit )
|
|||||||
|
|
||||||
if FirstAttackUnit and FirstAttackUnit:IsAlive() then -- If there is no attacker anymore, stop the engagement.
|
if FirstAttackUnit and FirstAttackUnit:IsAlive() then -- If there is no attacker anymore, stop the engagement.
|
||||||
|
|
||||||
if AICap:IsAlive() then
|
if AICap and AICap:IsAlive() then
|
||||||
|
|
||||||
local EngageRoute = {}
|
local EngageRoute = {}
|
||||||
|
|
||||||
@ -417,6 +417,8 @@ function AI_A2A_CAP:onafterEngage( AICap, From, Event, To, AttackSetUnit )
|
|||||||
local AttackUnit = AttackUnit -- Wrapper.Unit#UNIT
|
local AttackUnit = AttackUnit -- Wrapper.Unit#UNIT
|
||||||
self:T( { "Attacking Unit:", AttackUnit:GetName(), AttackUnit:IsAlive(), AttackUnit:IsAir() } )
|
self:T( { "Attacking Unit:", AttackUnit:GetName(), AttackUnit:IsAlive(), AttackUnit:IsAir() } )
|
||||||
if AttackUnit:IsAlive() and AttackUnit:IsAir() then
|
if AttackUnit:IsAlive() and AttackUnit:IsAir() then
|
||||||
|
-- TODO: Add coalition check? Only attack units of if AttackUnit:GetCoalition()~=AICap:GetCoalition()
|
||||||
|
-- Maybe the detected set also contains
|
||||||
AttackTasks[#AttackTasks+1] = AICap:TaskAttackUnit( AttackUnit )
|
AttackTasks[#AttackTasks+1] = AICap:TaskAttackUnit( AttackUnit )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -1092,8 +1092,9 @@ do -- AI_A2A_DISPATCHER
|
|||||||
self:GetParent( self, AI_A2A_DISPATCHER ).onafterStart( self, From, Event, To )
|
self:GetParent( self, AI_A2A_DISPATCHER ).onafterStart( self, From, Event, To )
|
||||||
|
|
||||||
-- Spawn the resources.
|
-- Spawn the resources.
|
||||||
for SquadronName, DefenderSquadron in pairs( self.DefenderSquadrons ) do
|
for SquadronName,_DefenderSquadron in pairs( self.DefenderSquadrons ) do
|
||||||
DefenderSquadron.Resource = {}
|
local DefenderSquadron=_DefenderSquadron --#AI_A2A_DISPATCHER.Squadron
|
||||||
|
DefenderSquadron.Resources = {}
|
||||||
if DefenderSquadron.ResourceCount then
|
if DefenderSquadron.ResourceCount then
|
||||||
for Resource = 1, DefenderSquadron.ResourceCount do
|
for Resource = 1, DefenderSquadron.ResourceCount do
|
||||||
self:ParkDefender( DefenderSquadron )
|
self:ParkDefender( DefenderSquadron )
|
||||||
@ -1107,18 +1108,39 @@ do -- AI_A2A_DISPATCHER
|
|||||||
-- @param #AI_A2A_DISPATCHER self
|
-- @param #AI_A2A_DISPATCHER self
|
||||||
-- @param #AI_A2A_DISPATCHER.Squadron DefenderSquadron The squadron.
|
-- @param #AI_A2A_DISPATCHER.Squadron DefenderSquadron The squadron.
|
||||||
function AI_A2A_DISPATCHER:ParkDefender( DefenderSquadron )
|
function AI_A2A_DISPATCHER:ParkDefender( DefenderSquadron )
|
||||||
|
|
||||||
local TemplateID = math.random( 1, #DefenderSquadron.Spawn )
|
local TemplateID = math.random( 1, #DefenderSquadron.Spawn )
|
||||||
|
|
||||||
local Spawn = DefenderSquadron.Spawn[ TemplateID ] -- Core.Spawn#SPAWN
|
local Spawn = DefenderSquadron.Spawn[ TemplateID ] -- Core.Spawn#SPAWN
|
||||||
|
|
||||||
Spawn:InitGrouping( 1 )
|
Spawn:InitGrouping( 1 )
|
||||||
|
|
||||||
local SpawnGroup
|
local SpawnGroup
|
||||||
|
|
||||||
if self:IsSquadronVisible( DefenderSquadron.Name ) then
|
if self:IsSquadronVisible( DefenderSquadron.Name ) then
|
||||||
|
|
||||||
|
local Grouping=DefenderSquadron.Grouping or self.DefenderDefault.Grouping
|
||||||
|
|
||||||
|
Grouping=1
|
||||||
|
|
||||||
|
Spawn:InitGrouping(Grouping)
|
||||||
|
|
||||||
SpawnGroup = Spawn:SpawnAtAirbase( DefenderSquadron.Airbase, SPAWN.Takeoff.Cold )
|
SpawnGroup = Spawn:SpawnAtAirbase( DefenderSquadron.Airbase, SPAWN.Takeoff.Cold )
|
||||||
|
|
||||||
local GroupName = SpawnGroup:GetName()
|
local GroupName = SpawnGroup:GetName()
|
||||||
|
|
||||||
DefenderSquadron.Resources = DefenderSquadron.Resources or {}
|
DefenderSquadron.Resources = DefenderSquadron.Resources or {}
|
||||||
|
|
||||||
DefenderSquadron.Resources[TemplateID] = DefenderSquadron.Resources[TemplateID] or {}
|
DefenderSquadron.Resources[TemplateID] = DefenderSquadron.Resources[TemplateID] or {}
|
||||||
DefenderSquadron.Resources[TemplateID][GroupName] = {}
|
DefenderSquadron.Resources[TemplateID][GroupName] = {}
|
||||||
DefenderSquadron.Resources[TemplateID][GroupName] = SpawnGroup
|
DefenderSquadron.Resources[TemplateID][GroupName] = SpawnGroup
|
||||||
|
|
||||||
|
self.uncontrolled=self.uncontrolled or {}
|
||||||
|
self.uncontrolled[DefenderSquadron.Name]=self.uncontrolled[DefenderSquadron.Name] or {}
|
||||||
|
|
||||||
|
table.insert(self.uncontrolled[DefenderSquadron.Name], {group=SpawnGroup, name=GroupName, grouping=Grouping})
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
@ -1702,9 +1724,22 @@ do -- AI_A2A_DISPATCHER
|
|||||||
|
|
||||||
DefenderSquadron.Uncontrolled = true
|
DefenderSquadron.Uncontrolled = true
|
||||||
|
|
||||||
|
-- For now, grouping is forced to 1 due to other parts of the class which would not work well with grouping>1.
|
||||||
|
DefenderSquadron.Grouping=1
|
||||||
|
|
||||||
|
-- Get free parking for fighter aircraft.
|
||||||
|
local nfreeparking=DefenderSquadron.Airbase:GetFreeParkingSpotsNumber(AIRBASE.TerminalType.FighterAircraft, true)
|
||||||
|
|
||||||
|
-- Take number of free parking spots if no resource count was specifed.
|
||||||
|
DefenderSquadron.ResourceCount=DefenderSquadron.ResourceCount or nfreeparking
|
||||||
|
|
||||||
|
-- Check that resource count is not larger than free parking spots.
|
||||||
|
DefenderSquadron.ResourceCount=math.min(DefenderSquadron.ResourceCount, nfreeparking)
|
||||||
|
|
||||||
|
-- Set uncontrolled spawning option.
|
||||||
for SpawnTemplate,_DefenderSpawn in pairs( self.DefenderSpawns ) do
|
for SpawnTemplate,_DefenderSpawn in pairs( self.DefenderSpawns ) do
|
||||||
local DefenderSpawn=_DefenderSpawn --Core.Spawn#SPAWN
|
local DefenderSpawn=_DefenderSpawn --Core.Spawn#SPAWN
|
||||||
DefenderSpawn:InitUnControlled()
|
DefenderSpawn:InitUnControlled(true)
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
@ -2751,7 +2786,7 @@ do -- AI_A2A_DISPATCHER
|
|||||||
--- Get squadron from defender.
|
--- Get squadron from defender.
|
||||||
-- @param #AI_A2A_DISPATCHER self
|
-- @param #AI_A2A_DISPATCHER self
|
||||||
-- @param Wrapper.Group#GROUP Defender The defender group.
|
-- @param Wrapper.Group#GROUP Defender The defender group.
|
||||||
-- @return ?
|
-- @return #AI_A2A_DISPATCHER.Squadron Squadron The squadron.
|
||||||
function AI_A2A_DISPATCHER:GetSquadronFromDefender( Defender )
|
function AI_A2A_DISPATCHER:GetSquadronFromDefender( Defender )
|
||||||
self.Defenders = self.Defenders or {}
|
self.Defenders = self.Defenders or {}
|
||||||
local DefenderName = Defender:GetName()
|
local DefenderName = Defender:GetName()
|
||||||
@ -2800,8 +2835,9 @@ do -- AI_A2A_DISPATCHER
|
|||||||
if AIGroup and AIGroup:IsAlive() then
|
if AIGroup and AIGroup:IsAlive() then
|
||||||
-- Check if the CAP is patrolling or engaging. If not, this is not a valid CAP, even if it is alive!
|
-- Check if the CAP is patrolling or engaging. If not, this is not a valid CAP, even if it is alive!
|
||||||
-- The CAP could be damaged, lost control, or out of fuel!
|
-- The CAP could be damaged, lost control, or out of fuel!
|
||||||
if DefenderTask.Fsm:Is( "Patrolling" ) or DefenderTask.Fsm:Is( "Engaging" ) or DefenderTask.Fsm:Is( "Refuelling" )
|
--env.info("FF fsm state "..tostring(DefenderTask.Fsm:GetState()))
|
||||||
or DefenderTask.Fsm:Is( "Started" ) then
|
if DefenderTask.Fsm:Is( "Patrolling" ) or DefenderTask.Fsm:Is( "Engaging" ) or DefenderTask.Fsm:Is( "Refuelling" ) or DefenderTask.Fsm:Is( "Started" ) then
|
||||||
|
--env.info("FF capcount "..CapCount)
|
||||||
CapCount = CapCount + 1
|
CapCount = CapCount + 1
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -2908,16 +2944,48 @@ do -- AI_A2A_DISPATCHER
|
|||||||
function AI_A2A_DISPATCHER:ResourceActivate( DefenderSquadron, DefendersNeeded )
|
function AI_A2A_DISPATCHER:ResourceActivate( DefenderSquadron, DefendersNeeded )
|
||||||
|
|
||||||
local SquadronName = DefenderSquadron.Name
|
local SquadronName = DefenderSquadron.Name
|
||||||
|
|
||||||
DefendersNeeded = DefendersNeeded or 4
|
DefendersNeeded = DefendersNeeded or 4
|
||||||
|
|
||||||
local DefenderGrouping = DefenderSquadron.Grouping or self.DefenderDefault.Grouping
|
local DefenderGrouping = DefenderSquadron.Grouping or self.DefenderDefault.Grouping
|
||||||
|
|
||||||
DefenderGrouping = ( DefenderGrouping < DefendersNeeded ) and DefenderGrouping or DefendersNeeded
|
DefenderGrouping = ( DefenderGrouping < DefendersNeeded ) and DefenderGrouping or DefendersNeeded
|
||||||
|
|
||||||
|
--env.info(string.format("FF resource activate: Squadron=%s grouping=%d needed=%d visible=%s", SquadronName, DefenderGrouping, DefendersNeeded, tostring(self:IsSquadronVisible( SquadronName ))))
|
||||||
|
|
||||||
if self:IsSquadronVisible( SquadronName ) then
|
if self:IsSquadronVisible( SquadronName ) then
|
||||||
|
|
||||||
|
local n=#self.uncontrolled[SquadronName]
|
||||||
|
|
||||||
|
if n>0 then
|
||||||
|
-- Random number 1,...n
|
||||||
|
local id=math.random(n)
|
||||||
|
|
||||||
|
-- Pick a random defender group.
|
||||||
|
local Defender=self.uncontrolled[SquadronName][id].group --Wrapper.Group#GROUP
|
||||||
|
|
||||||
|
-- Start uncontrolled group.
|
||||||
|
Defender:StartUncontrolled()
|
||||||
|
|
||||||
|
-- Get grouping.
|
||||||
|
DefenderGrouping=self.uncontrolled[SquadronName][id].grouping
|
||||||
|
|
||||||
|
-- Add defender to squadron.
|
||||||
|
self:AddDefenderToSquadron( DefenderSquadron, Defender, DefenderGrouping )
|
||||||
|
|
||||||
|
-- Remove defender from uncontrolled table.
|
||||||
|
table.remove(self.uncontrolled[SquadronName], id)
|
||||||
|
|
||||||
|
return Defender, DefenderGrouping
|
||||||
|
else
|
||||||
|
return nil,0
|
||||||
|
end
|
||||||
|
|
||||||
-- Here we CAP the new planes.
|
-- Here we CAP the new planes.
|
||||||
-- The Resources table is filled in advance.
|
-- The Resources table is filled in advance.
|
||||||
local TemplateID = math.random( 1, #DefenderSquadron.Spawn ) -- Choose the template.
|
local TemplateID = math.random( 1, #DefenderSquadron.Spawn ) -- Choose the template.
|
||||||
|
|
||||||
|
--[[
|
||||||
-- We determine the grouping based on the parameters set.
|
-- We determine the grouping based on the parameters set.
|
||||||
self:F( { DefenderGrouping = DefenderGrouping } )
|
self:F( { DefenderGrouping = DefenderGrouping } )
|
||||||
|
|
||||||
@ -2960,8 +3028,16 @@ do -- AI_A2A_DISPATCHER
|
|||||||
self:AddDefenderToSquadron( DefenderSquadron, Defender, DefenderGrouping )
|
self:AddDefenderToSquadron( DefenderSquadron, Defender, DefenderGrouping )
|
||||||
return Defender, DefenderGrouping
|
return Defender, DefenderGrouping
|
||||||
end
|
end
|
||||||
|
]]
|
||||||
|
|
||||||
else
|
else
|
||||||
|
|
||||||
|
----------------------------
|
||||||
|
--- Squadron not visible ---
|
||||||
|
----------------------------
|
||||||
|
|
||||||
local Spawn = DefenderSquadron.Spawn[ math.random( 1, #DefenderSquadron.Spawn ) ] -- Core.Spawn#SPAWN
|
local Spawn = DefenderSquadron.Spawn[ math.random( 1, #DefenderSquadron.Spawn ) ] -- Core.Spawn#SPAWN
|
||||||
|
|
||||||
if DefenderGrouping then
|
if DefenderGrouping then
|
||||||
Spawn:InitGrouping( DefenderGrouping )
|
Spawn:InitGrouping( DefenderGrouping )
|
||||||
else
|
else
|
||||||
@ -2969,8 +3045,11 @@ do -- AI_A2A_DISPATCHER
|
|||||||
end
|
end
|
||||||
|
|
||||||
local TakeoffMethod = self:GetSquadronTakeoff( SquadronName )
|
local TakeoffMethod = self:GetSquadronTakeoff( SquadronName )
|
||||||
|
|
||||||
local Defender = Spawn:SpawnAtAirbase( DefenderSquadron.Airbase, TakeoffMethod, DefenderSquadron.TakeoffAltitude or self.DefenderDefault.TakeoffAltitude ) -- Wrapper.Group#GROUP
|
local Defender = Spawn:SpawnAtAirbase( DefenderSquadron.Airbase, TakeoffMethod, DefenderSquadron.TakeoffAltitude or self.DefenderDefault.TakeoffAltitude ) -- Wrapper.Group#GROUP
|
||||||
|
|
||||||
self:AddDefenderToSquadron( DefenderSquadron, Defender, DefenderGrouping )
|
self:AddDefenderToSquadron( DefenderSquadron, Defender, DefenderGrouping )
|
||||||
|
|
||||||
return Defender, DefenderGrouping
|
return Defender, DefenderGrouping
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -3261,6 +3340,7 @@ do -- AI_A2A_DISPATCHER
|
|||||||
Dispatcher:ParkDefender( Squadron )
|
Dispatcher:ParkDefender( Squadron )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end -- if DefenderGCI then
|
end -- if DefenderGCI then
|
||||||
end -- while ( DefendersNeeded > 0 ) do
|
end -- while ( DefendersNeeded > 0 ) do
|
||||||
end
|
end
|
||||||
|
|||||||
@ -349,14 +349,17 @@ function AI_A2A_PATROL:onafterRoute( AIPatrol, From, Event, To )
|
|||||||
|
|
||||||
local CurrentCoord = AIPatrol:GetCoordinate()
|
local CurrentCoord = AIPatrol:GetCoordinate()
|
||||||
|
|
||||||
if self.racetrack then
|
|
||||||
|
|
||||||
-- Random altitude.
|
-- Random altitude.
|
||||||
local altitude=math.random(self.PatrolFloorAltitude, self.PatrolCeilingAltitude)
|
local altitude=math.random(self.PatrolFloorAltitude, self.PatrolCeilingAltitude)
|
||||||
|
|
||||||
-- Random speed in km/h.
|
-- Random speed in km/h.
|
||||||
local speedkmh = math.random(self.PatrolMinSpeed, self.PatrolMaxSpeed)
|
local speedkmh = math.random(self.PatrolMinSpeed, self.PatrolMaxSpeed)
|
||||||
|
|
||||||
|
-- First waypoint is current position.
|
||||||
|
PatrolRoute[1]=CurrentCoord:WaypointAirTurningPoint(nil, speedkmh, {}, "Current")
|
||||||
|
|
||||||
|
if self.racetrack then
|
||||||
|
|
||||||
-- Random heading.
|
-- Random heading.
|
||||||
local heading = math.random(self.racetrackheadingmin, self.racetrackheadingmax)
|
local heading = math.random(self.racetrackheadingmin, self.racetrackheadingmax)
|
||||||
|
|
||||||
@ -379,6 +382,8 @@ function AI_A2A_PATROL:onafterRoute( AIPatrol, From, Event, To )
|
|||||||
local c1=c0:SetAltitude(altitude) --Core.Point#COORDINATE
|
local c1=c0:SetAltitude(altitude) --Core.Point#COORDINATE
|
||||||
local c2=c1:Translate(leg, heading):SetAltitude(altitude)
|
local c2=c1:Translate(leg, heading):SetAltitude(altitude)
|
||||||
|
|
||||||
|
self:SetTargetDistance(c0) -- For RTB status check
|
||||||
|
|
||||||
-- Debug:
|
-- Debug:
|
||||||
self:T(string.format("Patrol zone race track: v=%.1f knots, h=%.1f ft, heading=%03d, leg=%d m, t=%s sec", UTILS.KmphToKnots(speedkmh), UTILS.MetersToFeet(altitude), heading, leg, tostring(duration)))
|
self:T(string.format("Patrol zone race track: v=%.1f knots, h=%.1f ft, heading=%03d, leg=%d m, t=%s sec", UTILS.KmphToKnots(speedkmh), UTILS.MetersToFeet(altitude), heading, leg, tostring(duration)))
|
||||||
--c1:MarkToAll("Race track c1")
|
--c1:MarkToAll("Race track c1")
|
||||||
@ -390,36 +395,24 @@ function AI_A2A_PATROL:onafterRoute( AIPatrol, From, Event, To )
|
|||||||
-- Task function to redo the patrol at other random position.
|
-- Task function to redo the patrol at other random position.
|
||||||
local taskPatrol=AIPatrol:TaskFunction("AI_A2A_PATROL.PatrolRoute", self)
|
local taskPatrol=AIPatrol:TaskFunction("AI_A2A_PATROL.PatrolRoute", self)
|
||||||
|
|
||||||
|
-- Controlled task with task condition.
|
||||||
local taskCond=AIPatrol:TaskCondition(nil, nil, nil, nil, duration, nil)
|
local taskCond=AIPatrol:TaskCondition(nil, nil, nil, nil, duration, nil)
|
||||||
local taskCont=AIPatrol:TaskControlled(taskOrbit, taskCond)
|
local taskCont=AIPatrol:TaskControlled(taskOrbit, taskCond)
|
||||||
|
|
||||||
PatrolRoute[1]=CurrentCoord:WaypointAirTurningPoint(nil, speedkmh, {}, "Current")
|
-- Second waypoint
|
||||||
PatrolRoute[2]=c1:WaypointAirTurningPoint(self.PatrolAltType, speedkmh, {taskCont, taskPatrol}, "Orbit")
|
PatrolRoute[2]=c1:WaypointAirTurningPoint(self.PatrolAltType, speedkmh, {taskCont, taskPatrol}, "CAP Orbit")
|
||||||
|
|
||||||
else
|
else
|
||||||
|
|
||||||
local ToTargetCoord = self.PatrolZone:GetRandomPointVec2()
|
-- Target coordinate.
|
||||||
ToTargetCoord:SetAlt( math.random( self.PatrolFloorAltitude, self.PatrolCeilingAltitude ) )
|
local ToTargetCoord=self.PatrolZone:GetRandomCoordinate() --Core.Point#COORDINATE
|
||||||
|
ToTargetCoord:SetAltitude(altitude)
|
||||||
|
|
||||||
self:SetTargetDistance( ToTargetCoord ) -- For RTB status check
|
self:SetTargetDistance( ToTargetCoord ) -- For RTB status check
|
||||||
|
|
||||||
local ToTargetSpeed = math.random( self.PatrolMinSpeed, self.PatrolMaxSpeed )
|
local taskReRoute=AIPatrol:TaskFunction( "AI_A2A_PATROL.PatrolRoute", self )
|
||||||
|
|
||||||
--- Create a route point of type air.
|
PatrolRoute[2]=ToTargetCoord:WaypointAirTurningPoint(self.PatrolAltType, speedkmh, {taskReRoute}, "Patrol Point")
|
||||||
local ToPatrolRoutePoint = ToTargetCoord:WaypointAir(
|
|
||||||
self.PatrolAltType,
|
|
||||||
POINT_VEC3.RoutePointType.TurningPoint,
|
|
||||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
|
||||||
ToTargetSpeed,
|
|
||||||
true
|
|
||||||
)
|
|
||||||
|
|
||||||
PatrolRoute[#PatrolRoute+1] = ToPatrolRoutePoint
|
|
||||||
PatrolRoute[#PatrolRoute+1] = ToPatrolRoutePoint
|
|
||||||
|
|
||||||
local Tasks = {}
|
|
||||||
Tasks[#Tasks+1] = AIPatrol:TaskFunction( "AI_A2A_PATROL.PatrolRoute", self )
|
|
||||||
PatrolRoute[#PatrolRoute].task = AIPatrol:TaskCombo( Tasks )
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@ -1816,7 +1816,7 @@ function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalT
|
|||||||
SpawnTemplate.x = PointVec3.x
|
SpawnTemplate.x = PointVec3.x
|
||||||
SpawnTemplate.y = PointVec3.z
|
SpawnTemplate.y = PointVec3.z
|
||||||
|
|
||||||
SpawnTemplate.uncontrolled = nil
|
SpawnTemplate.uncontrolled = self.SpawnUnControlled
|
||||||
|
|
||||||
-- Spawn group.
|
-- Spawn group.
|
||||||
local GroupSpawned = self:SpawnWithIndex( self.SpawnIndex )
|
local GroupSpawned = self:SpawnWithIndex( self.SpawnIndex )
|
||||||
|
|||||||
@ -442,6 +442,33 @@ function ZONE_RADIUS:New( ZoneName, Vec2, Radius )
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Mark the zone with markers on the F10 map.
|
||||||
|
-- @param #ZONE_RADIUS self
|
||||||
|
-- @param #number Points (Optional) The amount of points in the circle. Default 360.
|
||||||
|
-- @return #ZONE_RADIUS self
|
||||||
|
function ZONE_RADIUS:MarkZone(Points)
|
||||||
|
|
||||||
|
local Point = {}
|
||||||
|
local Vec2 = self:GetVec2()
|
||||||
|
|
||||||
|
Points = Points and Points or 360
|
||||||
|
|
||||||
|
local Angle
|
||||||
|
local RadialBase = math.pi*2
|
||||||
|
|
||||||
|
for Angle = 0, 360, (360 / Points ) do
|
||||||
|
|
||||||
|
local Radial = Angle * RadialBase / 360
|
||||||
|
|
||||||
|
Point.x = Vec2.x + math.cos( Radial ) * self:GetRadius()
|
||||||
|
Point.y = Vec2.y + math.sin( Radial ) * self:GetRadius()
|
||||||
|
|
||||||
|
COORDINATE:NewFromVec2(Point):MarkToAll(self:GetName())
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
--- Bounds the zone with tires.
|
--- Bounds the zone with tires.
|
||||||
-- @param #ZONE_RADIUS self
|
-- @param #ZONE_RADIUS self
|
||||||
-- @param #number Points (optional) The amount of points in the circle. Default 360.
|
-- @param #number Points (optional) The amount of points in the circle. Default 360.
|
||||||
|
|||||||
@ -458,15 +458,18 @@ do -- DETECTION_BASE
|
|||||||
-- @param #string From The From State string.
|
-- @param #string From The From State string.
|
||||||
-- @param #string Event The Event string.
|
-- @param #string Event The Event string.
|
||||||
-- @param #string To The To State string.
|
-- @param #string To The To State string.
|
||||||
|
-- @param #table Units Table of detected units.
|
||||||
|
|
||||||
--- Synchronous Event Trigger for Event Detected.
|
--- Synchronous Event Trigger for Event Detected.
|
||||||
-- @function [parent=#DETECTION_BASE] Detected
|
-- @function [parent=#DETECTION_BASE] Detected
|
||||||
-- @param #DETECTION_BASE self
|
-- @param #DETECTION_BASE self
|
||||||
|
-- @param #table Units Table of detected units.
|
||||||
|
|
||||||
--- Asynchronous Event Trigger for Event Detected.
|
--- Asynchronous Event Trigger for Event Detected.
|
||||||
-- @function [parent=#DETECTION_BASE] __Detected
|
-- @function [parent=#DETECTION_BASE] __Detected
|
||||||
-- @param #DETECTION_BASE self
|
-- @param #DETECTION_BASE self
|
||||||
-- @param #number Delay The delay in seconds.
|
-- @param #number Delay The delay in seconds.
|
||||||
|
-- @param #table Units Table of detected units.
|
||||||
|
|
||||||
self:AddTransition( "Detecting", "DetectedItem", "Detecting" )
|
self:AddTransition( "Detecting", "DetectedItem", "Detecting" )
|
||||||
|
|
||||||
@ -586,7 +589,7 @@ do -- DETECTION_BASE
|
|||||||
|
|
||||||
local HasDetectedObjects = false
|
local HasDetectedObjects = false
|
||||||
|
|
||||||
if Detection:IsAlive() then
|
if Detection and Detection:IsAlive() then
|
||||||
|
|
||||||
--self:T( { "DetectionGroup is Alive", DetectionGroup:GetName() } )
|
--self:T( { "DetectionGroup is Alive", DetectionGroup:GetName() } )
|
||||||
|
|
||||||
@ -817,12 +820,17 @@ do -- DETECTION_BASE
|
|||||||
end
|
end
|
||||||
|
|
||||||
self:CreateDetectionItems() -- Polymorphic call to Create/Update the DetectionItems list for the DETECTION_ class grouping method.
|
self:CreateDetectionItems() -- Polymorphic call to Create/Update the DetectionItems list for the DETECTION_ class grouping method.
|
||||||
|
|
||||||
for DetectedItemID, DetectedItem in pairs( self.DetectedItems ) do
|
for DetectedItemID, DetectedItem in pairs( self.DetectedItems ) do
|
||||||
|
|
||||||
self:UpdateDetectedItemDetection( DetectedItem )
|
self:UpdateDetectedItemDetection( DetectedItem )
|
||||||
|
|
||||||
self:CleanDetectionItem( DetectedItem, DetectedItemID ) -- Any DetectionItem that has a Set with zero elements in it, must be removed from the DetectionItems list.
|
self:CleanDetectionItem( DetectedItem, DetectedItemID ) -- Any DetectionItem that has a Set with zero elements in it, must be removed from the DetectionItems list.
|
||||||
|
|
||||||
if DetectedItem then
|
if DetectedItem then
|
||||||
self:__DetectedItem( 0.1, DetectedItem )
|
self:__DetectedItem( 0.1, DetectedItem )
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -834,7 +842,7 @@ do -- DETECTION_BASE
|
|||||||
|
|
||||||
do -- DetectionItems Creation
|
do -- DetectionItems Creation
|
||||||
|
|
||||||
-- Clean the DetectedItem table.
|
--- Clean the DetectedItem table.
|
||||||
-- @param #DETECTION_BASE self
|
-- @param #DETECTION_BASE self
|
||||||
-- @return #DETECTION_BASE
|
-- @return #DETECTION_BASE
|
||||||
function DETECTION_BASE:CleanDetectionItem( DetectedItem, DetectedItemID )
|
function DETECTION_BASE:CleanDetectionItem( DetectedItem, DetectedItemID )
|
||||||
|
|||||||
@ -40,7 +40,9 @@
|
|||||||
-- @field #table launchzones Table of launch zones.
|
-- @field #table launchzones Table of launch zones.
|
||||||
-- @field Core.Set#SET_GROUP protectedset Set of protected groups.
|
-- @field Core.Set#SET_GROUP protectedset Set of protected groups.
|
||||||
-- @field #number explosionpower Power of explostion when destroying the missile in kg TNT. Default 5 kg TNT.
|
-- @field #number explosionpower Power of explostion when destroying the missile in kg TNT. Default 5 kg TNT.
|
||||||
-- @field #number explosiondist Missile player distance in meters for destroying the missile. Default 100 m.
|
-- @field #number explosiondist Missile player distance in meters for destroying smaller missiles. Default 200 m.
|
||||||
|
-- @field #number explosiondist2 Missile player distance in meters for destroying big missiles. Default 500 m.
|
||||||
|
-- @field #number bigmissilemass Explosion power of big missiles. Default 50 kg TNT. Big missiles will be destroyed earlier.
|
||||||
-- @field #number dt50 Time step [sec] for missile position updates if distance to target > 50 km. Default 5 sec.
|
-- @field #number dt50 Time step [sec] for missile position updates if distance to target > 50 km. Default 5 sec.
|
||||||
-- @field #number dt10 Time step [sec] for missile position updates if distance to target > 10 km and < 50 km. Default 1 sec.
|
-- @field #number dt10 Time step [sec] for missile position updates if distance to target > 10 km and < 50 km. Default 1 sec.
|
||||||
-- @field #number dt05 Time step [sec] for missile position updates if distance to target > 5 km and < 10 km. Default 0.5 sec.
|
-- @field #number dt05 Time step [sec] for missile position updates if distance to target > 5 km and < 10 km. Default 0.5 sec.
|
||||||
@ -136,8 +138,10 @@ FOX = {
|
|||||||
safezones = {},
|
safezones = {},
|
||||||
launchzones = {},
|
launchzones = {},
|
||||||
protectedset = nil,
|
protectedset = nil,
|
||||||
explosionpower = 5,
|
explosionpower = 0.1,
|
||||||
explosiondist = 100,
|
explosiondist = 200,
|
||||||
|
explosiondist2 = 500,
|
||||||
|
bigmissilemass = 50,
|
||||||
destroy = nil,
|
destroy = nil,
|
||||||
dt50 = 5,
|
dt50 = 5,
|
||||||
dt10 = 1,
|
dt10 = 1,
|
||||||
@ -169,14 +173,19 @@ FOX = {
|
|||||||
-- @field Wrapper.Unit#UNIT weapon Missile weapon unit.
|
-- @field Wrapper.Unit#UNIT weapon Missile weapon unit.
|
||||||
-- @field #boolean active If true the missile is active.
|
-- @field #boolean active If true the missile is active.
|
||||||
-- @field #string missileType Type of missile.
|
-- @field #string missileType Type of missile.
|
||||||
|
-- @field #string missileName Name of missile.
|
||||||
-- @field #number missileRange Range of missile in meters.
|
-- @field #number missileRange Range of missile in meters.
|
||||||
|
-- @field #number fuseDist Fuse distance in meters.
|
||||||
|
-- @field #number explosive Explosive mass in kg TNT.
|
||||||
-- @field Wrapper.Unit#UNIT shooterUnit Unit that shot the missile.
|
-- @field Wrapper.Unit#UNIT shooterUnit Unit that shot the missile.
|
||||||
-- @field Wrapper.Group#GROUP shooterGroup Group that shot the missile.
|
-- @field Wrapper.Group#GROUP shooterGroup Group that shot the missile.
|
||||||
-- @field #number shooterCoalition Coalition side of the shooter.
|
-- @field #number shooterCoalition Coalition side of the shooter.
|
||||||
-- @field #string shooterName Name of the shooter unit.
|
-- @field #string shooterName Name of the shooter unit.
|
||||||
-- @field #number shotTime Abs mission time in seconds the missile was fired.
|
-- @field #number shotTime Abs. mission time in seconds the missile was fired.
|
||||||
-- @field Core.Point#COORDINATE shotCoord Coordinate where the missile was fired.
|
-- @field Core.Point#COORDINATE shotCoord Coordinate where the missile was fired.
|
||||||
-- @field Wrapper.Unit#UNIT targetUnit Unit that was targeted.
|
-- @field Wrapper.Unit#UNIT targetUnit Unit that was targeted.
|
||||||
|
-- @field #string targetName Name of the target unit or "unknown".
|
||||||
|
-- @field #string targetOrig Name of the "original" target, i.e. the one right after launched.
|
||||||
-- @field #FOX.PlayerData targetPlayer Player that was targeted or nil.
|
-- @field #FOX.PlayerData targetPlayer Player that was targeted or nil.
|
||||||
|
|
||||||
--- Main radio menu on group level.
|
--- Main radio menu on group level.
|
||||||
@ -189,7 +198,7 @@ FOX.MenuF10Root=nil
|
|||||||
|
|
||||||
--- FOX class version.
|
--- FOX class version.
|
||||||
-- @field #string version
|
-- @field #string version
|
||||||
FOX.version="0.5.1"
|
FOX.version="0.6.0"
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
-- ToDo list
|
-- ToDo list
|
||||||
@ -217,6 +226,10 @@ function FOX:New()
|
|||||||
self:SetDefaultMissileDestruction(true)
|
self:SetDefaultMissileDestruction(true)
|
||||||
self:SetDefaultLaunchAlerts(true)
|
self:SetDefaultLaunchAlerts(true)
|
||||||
self:SetDefaultLaunchMarks(true)
|
self:SetDefaultLaunchMarks(true)
|
||||||
|
|
||||||
|
-- Explosion/destruction defaults.
|
||||||
|
self:SetExplosionDistance()
|
||||||
|
self:SetExplosionDistanceBigMissiles()
|
||||||
self:SetExplosionPower()
|
self:SetExplosionPower()
|
||||||
|
|
||||||
-- Start State.
|
-- Start State.
|
||||||
@ -358,12 +371,16 @@ function FOX:onafterStart(From, Event, To)
|
|||||||
self:HandleEvent(EVENTS.Birth)
|
self:HandleEvent(EVENTS.Birth)
|
||||||
self:HandleEvent(EVENTS.Shot)
|
self:HandleEvent(EVENTS.Shot)
|
||||||
|
|
||||||
|
if self.Debug then
|
||||||
|
self:HandleEvent(EVENTS.Hit)
|
||||||
|
end
|
||||||
|
|
||||||
if self.Debug then
|
if self.Debug then
|
||||||
self:TraceClass(self.ClassName)
|
self:TraceClass(self.ClassName)
|
||||||
self:TraceLevel(2)
|
self:TraceLevel(2)
|
||||||
end
|
end
|
||||||
|
|
||||||
self:__Status(-10)
|
self:__Status(-20)
|
||||||
end
|
end
|
||||||
|
|
||||||
--- On after Stop event. Stops the missile trainer and unhandles events.
|
--- On after Stop event. Stops the missile trainer and unhandles events.
|
||||||
@ -378,8 +395,12 @@ function FOX:onafterStop(From, Event, To)
|
|||||||
env.info(text)
|
env.info(text)
|
||||||
|
|
||||||
-- Handle events:
|
-- Handle events:
|
||||||
self:UnhandleEvent(EVENTS.Birth)
|
self:UnHandleEvent(EVENTS.Birth)
|
||||||
self:UnhandleEvent(EVENTS.Shot)
|
self:UnHandleEvent(EVENTS.Shot)
|
||||||
|
|
||||||
|
if self.Debug then
|
||||||
|
self:UnhandleEvent(EVENTS.Hit)
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -433,28 +454,42 @@ function FOX:AddProtectedGroup(group)
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Set explosion power.
|
--- Set explosion power. This is an "artificial" explosion generated when the missile is destroyed. Just for the visual effect.
|
||||||
|
-- Don't set the explosion power too big or it will harm the aircraft in the vicinity.
|
||||||
-- @param #FOX self
|
-- @param #FOX self
|
||||||
-- @param #number power Explosion power in kg TNT. Default 5.
|
-- @param #number power Explosion power in kg TNT. Default 0.1 kg.
|
||||||
-- @return #FOX self
|
-- @return #FOX self
|
||||||
function FOX:SetExplosionPower(power)
|
function FOX:SetExplosionPower(power)
|
||||||
|
|
||||||
self.explosionpower=power or 5
|
self.explosionpower=power or 0.1
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Set missile-player distance when missile is destroyed.
|
--- Set missile-player distance when missile is destroyed.
|
||||||
-- @param #FOX self
|
-- @param #FOX self
|
||||||
-- @param #number distance Distance in meters. Default 100 m.
|
-- @param #number distance Distance in meters. Default 200 m.
|
||||||
-- @return #FOX self
|
-- @return #FOX self
|
||||||
function FOX:SetExplosionDistance(distance)
|
function FOX:SetExplosionDistance(distance)
|
||||||
|
|
||||||
self.explosiondist=distance or 100
|
self.explosiondist=distance or 200
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Set missile-player distance when BIG missiles are destroyed.
|
||||||
|
-- @param #FOX self
|
||||||
|
-- @param #number distance Distance in meters. Default 500 m.
|
||||||
|
-- @param #number explosivemass Explosive mass of missile threshold in kg TNT. Default 50 kg.
|
||||||
|
-- @return #FOX self
|
||||||
|
function FOX:SetExplosionDistanceBigMissiles(distance, explosivemass)
|
||||||
|
|
||||||
|
self.explosiondist2=distance or 500
|
||||||
|
|
||||||
|
self.bigmissilemass=explosivemass or 50
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
--- Disable F10 menu for all players.
|
--- Disable F10 menu for all players.
|
||||||
-- @param #FOX self
|
-- @param #FOX self
|
||||||
@ -558,8 +593,11 @@ function FOX:onafterStatus(From, Event, To)
|
|||||||
-- Get FSM state.
|
-- Get FSM state.
|
||||||
local fsmstate=self:GetState()
|
local fsmstate=self:GetState()
|
||||||
|
|
||||||
|
local time=timer.getAbsTime()
|
||||||
|
local clock=UTILS.SecondsToClock(time)
|
||||||
|
|
||||||
-- Status.
|
-- Status.
|
||||||
self:I(self.lid..string.format("Missile trainer status: %s", fsmstate))
|
self:I(self.lid..string.format("Missile trainer status %s: %s", clock, fsmstate))
|
||||||
|
|
||||||
-- Check missile status.
|
-- Check missile status.
|
||||||
self:_CheckMissileStatus()
|
self:_CheckMissileStatus()
|
||||||
@ -664,6 +702,9 @@ function FOX:_CheckMissileStatus()
|
|||||||
text=text..string.format("\n[%d] %s: active=%s, range=%.1f NM, heading=%03d, target=%s, player=%s, missilename=%s", i, mtype, active, range, heading, targetname, playername, missile.missileName)
|
text=text..string.format("\n[%d] %s: active=%s, range=%.1f NM, heading=%03d, target=%s, player=%s, missilename=%s", i, mtype, active, range, heading, targetname, playername, missile.missileName)
|
||||||
|
|
||||||
end
|
end
|
||||||
|
if #self.missiles==0 then
|
||||||
|
text=text.." none"
|
||||||
|
end
|
||||||
self:I(self.lid..text)
|
self:I(self.lid..text)
|
||||||
|
|
||||||
-- Remove inactive missiles.
|
-- Remove inactive missiles.
|
||||||
@ -722,7 +763,9 @@ end
|
|||||||
function FOX:onafterMissileLaunch(From, Event, To, missile)
|
function FOX:onafterMissileLaunch(From, Event, To, missile)
|
||||||
|
|
||||||
-- Tracking info and init of last bomb position.
|
-- Tracking info and init of last bomb position.
|
||||||
self:I(FOX.lid..string.format("FOX: Tracking %s - %s.", missile.missileType, missile.missileName))
|
local text=string.format("FOX: Tracking missile %s(%s) - target %s - shooter %s", missile.missileType, missile.missileName, tostring(missile.targetName), missile.shooterName)
|
||||||
|
self:I(FOX.lid..text)
|
||||||
|
MESSAGE:New(text, 10):ToAllIf(self.Debug)
|
||||||
|
|
||||||
-- Loop over players.
|
-- Loop over players.
|
||||||
for _,_player in pairs(self.players) do
|
for _,_player in pairs(self.players) do
|
||||||
@ -803,6 +846,9 @@ function FOX:onafterMissileLaunch(From, Event, To, missile)
|
|||||||
-- Missile velocity in m/s.
|
-- Missile velocity in m/s.
|
||||||
local missileVelocity=UTILS.VecNorm(_ordnance:getVelocity())
|
local missileVelocity=UTILS.VecNorm(_ordnance:getVelocity())
|
||||||
|
|
||||||
|
-- Update missile target if necessary.
|
||||||
|
self:GetMissileTarget(missile)
|
||||||
|
|
||||||
if missile.targetUnit then
|
if missile.targetUnit then
|
||||||
|
|
||||||
-----------------------------------
|
-----------------------------------
|
||||||
@ -827,6 +873,29 @@ function FOX:onafterMissileLaunch(From, Event, To, missile)
|
|||||||
-- Missile has NO specific target --
|
-- Missile has NO specific target --
|
||||||
------------------------------------
|
------------------------------------
|
||||||
|
|
||||||
|
-- TODO: This might cause a problem with wingman. Even if the shooter itself is excluded from the check, it's wingmen are not.
|
||||||
|
-- That would trigger the distance check right after missile launch if things to wrong.
|
||||||
|
--
|
||||||
|
-- Possible solutions:
|
||||||
|
-- * Time check: enable this check after X seconds after missile was fired. What is X?
|
||||||
|
-- * Coalition check. But would not work in training situations where blue on blue is valid!
|
||||||
|
-- * At least enable it for surface-to-air missiles.
|
||||||
|
|
||||||
|
local function _GetTarget(_unit)
|
||||||
|
local unit=_unit --Wrapper.Unit#UNIT
|
||||||
|
|
||||||
|
-- Player position.
|
||||||
|
local playerCoord=unit:GetCoordinate()
|
||||||
|
|
||||||
|
-- Distance.
|
||||||
|
local dist=missileCoord:Get3DDistance(playerCoord)
|
||||||
|
|
||||||
|
-- Update mindist if necessary. Only include players in range of missile + 50% safety margin.
|
||||||
|
if dist<=self.explosiondist then
|
||||||
|
return unit
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
-- Distance to closest player.
|
-- Distance to closest player.
|
||||||
local mindist=nil
|
local mindist=nil
|
||||||
|
|
||||||
@ -843,17 +912,57 @@ function FOX:onafterMissileLaunch(From, Event, To, missile)
|
|||||||
-- Distance.
|
-- Distance.
|
||||||
local dist=missileCoord:Get3DDistance(playerCoord)
|
local dist=missileCoord:Get3DDistance(playerCoord)
|
||||||
|
|
||||||
-- Maxrange from launch point to player.
|
-- Distance from shooter to player.
|
||||||
local maxrange=playerCoord:Get3DDistance(missile.shotCoord)
|
local Dshooter2player=playerCoord:Get3DDistance(missile.shotCoord)
|
||||||
|
|
||||||
-- Update mindist if necessary. Only include players in range of missile.
|
-- Update mindist if necessary. Only include players in range of missile + 50% safety margin.
|
||||||
if (mindist==nil or dist<mindist) and dist<=maxrange then
|
if (mindist==nil or dist<mindist) and (Dshooter2player<=missile.missileRange*1.5 or dist<=self.explosiondist) then
|
||||||
mindist=dist
|
mindist=dist
|
||||||
target=player.unit
|
target=player.unit
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if self.protectedset then
|
||||||
|
|
||||||
|
-- Distance to closest protected unit.
|
||||||
|
mindist=nil
|
||||||
|
|
||||||
|
for _,_group in pairs(self.protectedset:GetSet()) do
|
||||||
|
local group=_group --Wrapper.Group#GROUP
|
||||||
|
for _,_unit in pairs(group:GetUnits()) do
|
||||||
|
local unit=_unit --Wrapper.Unit#UNIT
|
||||||
|
|
||||||
|
if unit and unit:IsAlive() then
|
||||||
|
|
||||||
|
-- Check that player was not the one who launched the missile.
|
||||||
|
if unit:GetName()~=missile.shooterName then
|
||||||
|
|
||||||
|
-- Player position.
|
||||||
|
local playerCoord=unit:GetCoordinate()
|
||||||
|
|
||||||
|
-- Distance.
|
||||||
|
local dist=missileCoord:Get3DDistance(playerCoord)
|
||||||
|
|
||||||
|
-- Distance from shooter to player.
|
||||||
|
local Dshooter2player=playerCoord:Get3DDistance(missile.shotCoord)
|
||||||
|
|
||||||
|
-- Update mindist if necessary. Only include players in range of missile + 50% safety margin.
|
||||||
|
if (mindist==nil or dist<mindist) and (Dshooter2player<=missile.missileRange*1.5 or dist<=self.explosiondist) then
|
||||||
|
mindist=dist
|
||||||
|
target=unit
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if target then
|
||||||
|
self:I(self.lid..string.format("Missile %s with NO explicit target got closest unit to missile as target %s. Dist=%s m", missile.missileType, target:GetName(), tostring(mindist)))
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Check if missile has a valid target.
|
-- Check if missile has a valid target.
|
||||||
@ -865,39 +974,69 @@ function FOX:onafterMissileLaunch(From, Event, To, missile)
|
|||||||
-- Distance from missile to target.
|
-- Distance from missile to target.
|
||||||
local distance=missileCoord:Get3DDistance(targetCoord)
|
local distance=missileCoord:Get3DDistance(targetCoord)
|
||||||
|
|
||||||
|
-- Distance missile to shooter.
|
||||||
|
local distShooter=nil
|
||||||
|
if missile.shooterUnit and missile.shooterUnit:IsAlive() then
|
||||||
|
distShooter=missileCoord:Get3DDistance(missile.shooterUnit:GetCoordinate())
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-- Debug output.
|
||||||
|
if self.Debug then
|
||||||
local bearing=targetCoord:HeadingTo(missileCoord)
|
local bearing=targetCoord:HeadingTo(missileCoord)
|
||||||
local eta=distance/missileVelocity
|
local eta=distance/missileVelocity
|
||||||
|
|
||||||
self:T2(self.lid..string.format("Distance = %.1f m, v=%.1f m/s, bearing=%03d°, eta=%.1f sec", distance, missileVelocity, bearing, eta))
|
-- Debug distance check.
|
||||||
|
self:I(self.lid..string.format("Missile %s Target %s: Distance = %.1f m, v=%.1f m/s, bearing=%03d°, ETA=%.1f sec", missile.missileType, target:GetName(), distance, missileVelocity, bearing, eta))
|
||||||
|
end
|
||||||
|
|
||||||
-- If missile is 100 m from target ==> destroy missile if in safe zone.
|
-- Distroy missile if it's getting too close.
|
||||||
if distance<=self.explosiondist and self:_CheckCoordSafe(targetCoord)then
|
local destroymissile=distance<=self.explosiondist
|
||||||
|
|
||||||
|
-- Check BIG missiles.
|
||||||
|
if self.explosiondist2 and distance<=self.explosiondist2 and not destroymissile then
|
||||||
|
destroymissile=missile.explosive>=self.bigmissilemass
|
||||||
|
end
|
||||||
|
|
||||||
|
-- If missile is 150 m from target ==> destroy missile if in safe zone.
|
||||||
|
if destroymissile and self:_CheckCoordSafe(targetCoord) then
|
||||||
|
|
||||||
-- Destroy missile.
|
-- Destroy missile.
|
||||||
self:T(self.lid..string.format("Destroying missile at distance %.1f m", distance))
|
self:I(self.lid..string.format("Destroying missile %s(%s) fired by %s aimed at %s [player=%s] at distance %.1f m",
|
||||||
|
missile.missileType, missile.missileName, missile.shooterName, target:GetName(), tostring(missile.targetPlayer~=nil), distance))
|
||||||
_ordnance:destroy()
|
_ordnance:destroy()
|
||||||
|
|
||||||
-- Missile is not active any more.
|
-- Missile is not active any more.
|
||||||
missile.active=false
|
missile.active=false
|
||||||
|
|
||||||
|
-- Debug smoke.
|
||||||
|
if self.Debug then
|
||||||
|
missileCoord:SmokeRed()
|
||||||
|
targetCoord:SmokeGreen()
|
||||||
|
end
|
||||||
|
|
||||||
-- Create event.
|
-- Create event.
|
||||||
self:MissileDestroyed(missile)
|
self:MissileDestroyed(missile)
|
||||||
|
|
||||||
-- Little explosion for the visual effect.
|
-- Little explosion for the visual effect.
|
||||||
if self.explosionpower>0 then
|
if self.explosionpower>0 and distance>50 and (distShooter==nil or (distShooter and distShooter>50)) then
|
||||||
missileCoord:Explosion(self.explosionpower)
|
missileCoord:Explosion(self.explosionpower)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Target was a player.
|
||||||
|
if missile.targetPlayer then
|
||||||
|
|
||||||
|
-- Message to target.
|
||||||
local text=string.format("Destroying missile. %s", self:_DeadText())
|
local text=string.format("Destroying missile. %s", self:_DeadText())
|
||||||
MESSAGE:New(text, 10):ToGroup(target:GetGroup())
|
MESSAGE:New(text, 10):ToGroup(target:GetGroup())
|
||||||
|
|
||||||
-- Increase dead counter.
|
-- Increase dead counter.
|
||||||
if missile.targetPlayer then
|
|
||||||
missile.targetPlayer.dead=missile.targetPlayer.dead+1
|
missile.targetPlayer.dead=missile.targetPlayer.dead+1
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Terminate timer.
|
-- Terminate timer.
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
else
|
else
|
||||||
|
|
||||||
-- Time step.
|
-- Time step.
|
||||||
@ -922,10 +1061,15 @@ function FOX:onafterMissileLaunch(From, Event, To, missile)
|
|||||||
-- Check again in dt seconds.
|
-- Check again in dt seconds.
|
||||||
return timer.getTime()+dt
|
return timer.getTime()+dt
|
||||||
end
|
end
|
||||||
|
|
||||||
else
|
else
|
||||||
|
|
||||||
|
-- Destroy missile.
|
||||||
|
self:I(self.lid..string.format("Missile %s(%s) fired by %s has no current target. Checking back in 0.1 sec.", missile.missileType, missile.missileName, missile.shooterName))
|
||||||
|
return timer.getTime()+0.1
|
||||||
|
|
||||||
-- No target ==> terminate timer.
|
-- No target ==> terminate timer.
|
||||||
return nil
|
--return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
else
|
else
|
||||||
@ -1041,11 +1185,52 @@ function FOX:OnEventBirth(EventData)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Get missile target.
|
||||||
|
-- @param #FOX self
|
||||||
|
-- @param #FOX.MissileData missile The missile data table.
|
||||||
|
function FOX:GetMissileTarget(missile)
|
||||||
|
|
||||||
|
local target=nil
|
||||||
|
local targetName="unknown"
|
||||||
|
local targetUnit=nil --Wrapper.Unit#UNIT
|
||||||
|
|
||||||
|
if missile.weapon and missile.weapon:isExist() then
|
||||||
|
|
||||||
|
-- Get target of missile.
|
||||||
|
target=missile.weapon:getTarget()
|
||||||
|
|
||||||
|
-- Get the target unit. Note if if _target is not nil, the unit can sometimes not be found!
|
||||||
|
if target then
|
||||||
|
self:T2({missiletarget=target})
|
||||||
|
|
||||||
|
-- Get target unit.
|
||||||
|
targetUnit=UNIT:Find(target)
|
||||||
|
|
||||||
|
if targetUnit then
|
||||||
|
targetName=targetUnit:GetName()
|
||||||
|
|
||||||
|
missile.targetUnit=targetUnit
|
||||||
|
missile.targetPlayer=self:_GetPlayerFromUnit(missile.targetUnit)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Missile got new target.
|
||||||
|
if missile.targetName and missile.targetName~=targetName then
|
||||||
|
self:I(self.lid..string.format("Missile %s(%s) changed target to %s. Previous target was %s.", missile.missileType, missile.missileName, targetName, missile.targetName))
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Set target name.
|
||||||
|
missile.targetName=targetName
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
--- FOX event handler for event shot (when a unit releases a rocket or bomb (but not a fast firing gun).
|
--- FOX event handler for event shot (when a unit releases a rocket or bomb (but not a fast firing gun).
|
||||||
-- @param #FOX self
|
-- @param #FOX self
|
||||||
-- @param Core.Event#EVENTDATA EventData
|
-- @param Core.Event#EVENTDATA EventData
|
||||||
function FOX:OnEventShot(EventData)
|
function FOX:OnEventShot(EventData)
|
||||||
self:I({eventshot = EventData})
|
self:T2({eventshot=EventData})
|
||||||
|
|
||||||
if EventData.Weapon==nil then
|
if EventData.Weapon==nil then
|
||||||
return
|
return
|
||||||
@ -1062,7 +1247,7 @@ function FOX:OnEventShot(EventData)
|
|||||||
|
|
||||||
-- Weapon descriptor.
|
-- Weapon descriptor.
|
||||||
local desc=EventData.Weapon:getDesc()
|
local desc=EventData.Weapon:getDesc()
|
||||||
self:E({desc=desc})
|
self:T2({desc=desc})
|
||||||
|
|
||||||
-- Weapon category: 0=Shell, 1=Missile, 2=Rocket, 3=BOMB
|
-- Weapon category: 0=Shell, 1=Missile, 2=Rocket, 3=BOMB
|
||||||
local weaponcategory=desc.category
|
local weaponcategory=desc.category
|
||||||
@ -1091,15 +1276,6 @@ function FOX:OnEventShot(EventData)
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Get the target unit. Note if if _target is not nil, the unit can sometimes not be found!
|
|
||||||
if _target then
|
|
||||||
self:E({target=_target})
|
|
||||||
--_targetName=Unit.getName(_target)
|
|
||||||
--_targetUnit=UNIT:FindByName(_targetName)
|
|
||||||
_targetUnit=UNIT:Find(_target)
|
|
||||||
end
|
|
||||||
self:E(FOX.lid..string.format("EVENT SHOT: Target name = %s", tostring(_targetName)))
|
|
||||||
|
|
||||||
-- Track missiles of type AAM=1, SAM=2 or OTHER=6
|
-- Track missiles of type AAM=1, SAM=2 or OTHER=6
|
||||||
local _track = weaponcategory==1 and missilecategory and (missilecategory==1 or missilecategory==2 or missilecategory==6)
|
local _track = weaponcategory==1 and missilecategory and (missilecategory==1 or missilecategory==2 or missilecategory==6)
|
||||||
|
|
||||||
@ -1119,11 +1295,18 @@ function FOX:OnEventShot(EventData)
|
|||||||
missile.shooterName=EventData.IniUnitName
|
missile.shooterName=EventData.IniUnitName
|
||||||
missile.shotTime=timer.getAbsTime()
|
missile.shotTime=timer.getAbsTime()
|
||||||
missile.shotCoord=EventData.IniUnit:GetCoordinate()
|
missile.shotCoord=EventData.IniUnit:GetCoordinate()
|
||||||
missile.targetUnit=_targetUnit
|
missile.fuseDist=desc.fuseDist
|
||||||
missile.targetPlayer=self:_GetPlayerFromUnit(missile.targetUnit)
|
missile.explosive=desc.warhead.explosiveMass or desc.warhead.shapedExplosiveMass
|
||||||
|
missile.targetOrig=missile.targetName
|
||||||
|
|
||||||
-- Only track if target was a player or target is protected.
|
-- Set missile target name, unit and player.
|
||||||
if missile.targetPlayer or self:_IsProtected(missile.targetUnit) then
|
self:GetMissileTarget(missile)
|
||||||
|
|
||||||
|
self:I(FOX.lid..string.format("EVENT SHOT: Shooter=%s %s(%s) ==> Target=%s, fuse dist=%s, explosive=%s",
|
||||||
|
tostring(missile.shooterName), tostring(missile.missileType), tostring(missile.missileName), tostring(missile.targetName), tostring(missile.fuseDist), tostring(missile.explosive)))
|
||||||
|
|
||||||
|
-- Only track if target was a player or target is protected. Saw the 9M311 missiles have no target!
|
||||||
|
if missile.targetPlayer or self:_IsProtected(missile.targetUnit) or missile.targetName=="unknown" then
|
||||||
|
|
||||||
-- Add missile table.
|
-- Add missile table.
|
||||||
table.insert(self.missiles, missile)
|
table.insert(self.missiles, missile)
|
||||||
@ -1137,6 +1320,36 @@ function FOX:OnEventShot(EventData)
|
|||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- FOX event handler for event hit.
|
||||||
|
-- @param #FOX self
|
||||||
|
-- @param Core.Event#EVENTDATA EventData
|
||||||
|
function FOX:OnEventHit(EventData)
|
||||||
|
self:T({eventhit = EventData})
|
||||||
|
|
||||||
|
-- Nil checks.
|
||||||
|
if EventData.Weapon==nil then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if EventData.IniUnit==nil then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if EventData.TgtUnit==nil then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local weapon=EventData.Weapon
|
||||||
|
local weaponname=weapon:getName()
|
||||||
|
|
||||||
|
for i,_missile in pairs(self.missiles) do
|
||||||
|
local missile=_missile --#FOX.MissileData
|
||||||
|
if missile.missileName==weaponname then
|
||||||
|
self:I(self.lid..string.format("WARNING: Missile %s (%s) hit target %s. Missile trainer target was %s.", missile.missileType, missile.missileName, EventData.TgtUnitName, missile.targetName))
|
||||||
|
self:I({missile=missile})
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
-- RADIO MENU Functions
|
-- RADIO MENU Functions
|
||||||
|
|||||||
@ -1681,12 +1681,13 @@ AIRBOSS.MenuF10Root=nil
|
|||||||
|
|
||||||
--- Airboss class version.
|
--- Airboss class version.
|
||||||
-- @field #string version
|
-- @field #string version
|
||||||
AIRBOSS.version="1.0.3"
|
AIRBOSS.version="1.0.4"
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
-- TODO list
|
-- TODO list
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-- TODO: Handle tanker and AWACS. Put them into pattern.
|
||||||
-- TODO: Handle cases where AI crashes on carrier deck ==> Clean up deck.
|
-- TODO: Handle cases where AI crashes on carrier deck ==> Clean up deck.
|
||||||
-- TODO: Player eject and crash debrief "gradings".
|
-- TODO: Player eject and crash debrief "gradings".
|
||||||
-- TODO: PWO during case 2/3.
|
-- TODO: PWO during case 2/3.
|
||||||
@ -6862,8 +6863,8 @@ function AIRBOSS:_GetFreeStack(ai, case, empty)
|
|||||||
local n=flight.flag
|
local n=flight.flag
|
||||||
|
|
||||||
if n>0 then
|
if n>0 then
|
||||||
if flight.ai then
|
if flight.ai or flight.case>1 then
|
||||||
stack[n]=0 -- AI get one stack on their own.
|
stack[n]=0 -- AI get one stack on their own. Also CASE II/III get one stack each.
|
||||||
else
|
else
|
||||||
stack[n]=stack[n]-1
|
stack[n]=stack[n]-1
|
||||||
end
|
end
|
||||||
@ -6878,7 +6879,7 @@ function AIRBOSS:_GetFreeStack(ai, case, empty)
|
|||||||
local nfree=nil
|
local nfree=nil
|
||||||
for i=1,nmaxstacks do
|
for i=1,nmaxstacks do
|
||||||
self:T2(self.lid..string.format("FF Stack[%d]=%d", i, stack[i]))
|
self:T2(self.lid..string.format("FF Stack[%d]=%d", i, stack[i]))
|
||||||
if ai or empty then
|
if ai or empty or case>1 then
|
||||||
-- AI need the whole stack.
|
-- AI need the whole stack.
|
||||||
if stack[i]==self.NmaxStack then
|
if stack[i]==self.NmaxStack then
|
||||||
nfree=i
|
nfree=i
|
||||||
|
|||||||
@ -429,12 +429,12 @@ UTILS.tostringLL = function( lat, lon, acc, DMS)
|
|||||||
|
|
||||||
local secFrmtStr -- create the formatting string for the seconds place
|
local secFrmtStr -- create the formatting string for the seconds place
|
||||||
secFrmtStr = '%02d'
|
secFrmtStr = '%02d'
|
||||||
-- if acc <= 0 then -- no decimal place.
|
if acc <= 0 then -- no decimal place.
|
||||||
-- secFrmtStr = '%02d'
|
secFrmtStr = '%02d'
|
||||||
-- else
|
else
|
||||||
-- local width = 3 + acc -- 01.310 - that's a width of 6, for example.
|
local width = 3 + acc -- 01.310 - that's a width of 6, for example.
|
||||||
-- secFrmtStr = '%0' .. width .. '.' .. acc .. 'f'
|
secFrmtStr = '%0' .. width .. '.' .. acc .. 'f'
|
||||||
-- end
|
end
|
||||||
|
|
||||||
return string.format('%03d', latDeg) .. ' ' .. string.format('%02d', latMin) .. '\' ' .. string.format(secFrmtStr, latSec) .. '"' .. latHemi .. ' '
|
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
|
.. string.format('%03d', lonDeg) .. ' ' .. string.format('%02d', lonMin) .. '\' ' .. string.format(secFrmtStr, lonSec) .. '"' .. lonHemi
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user