mirror of
https://github.com/FlightControl-Master/MOOSE.git
synced 2026-07-21 06:04:49 +00:00
Merge branch 'FF/Develop' into FF/Fox2
This commit is contained in:
@@ -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 AICap:IsAlive() then
|
||||
if AICap and AICap:IsAlive() then
|
||||
|
||||
local EngageRoute = {}
|
||||
|
||||
@@ -417,6 +417,8 @@ function AI_A2A_CAP:onafterEngage( AICap, From, Event, To, AttackSetUnit )
|
||||
local AttackUnit = AttackUnit -- Wrapper.Unit#UNIT
|
||||
self:T( { "Attacking Unit:", AttackUnit:GetName(), AttackUnit:IsAlive(), AttackUnit:IsAir() } )
|
||||
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 )
|
||||
end
|
||||
end
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -257,6 +257,35 @@ function AI_A2A_PATROL:SetAltitude( PatrolFloorAltitude, PatrolCeilingAltitude )
|
||||
self.PatrolCeilingAltitude = PatrolCeilingAltitude
|
||||
end
|
||||
|
||||
--- Set race track parameters. CAP flights will perform race track patterns rather than randomly patrolling the zone.
|
||||
-- @param #AI_A2A_PATROL self
|
||||
-- @param #number LegMin Min Length of the race track leg in meters. Default 10,000 m.
|
||||
-- @param #number LegMax Max length of the race track leg in meters. Default 15,000 m.
|
||||
-- @param #number HeadingMin Min heading of the race track in degrees. Default 0 deg, i.e. from South to North.
|
||||
-- @param #number HeadingMax Max heading of the race track in degrees. Default 180 deg, i.e. from South to North.
|
||||
-- @param #number DurationMin (Optional) Min duration before switching the orbit position. Default is keep same orbit until RTB or engage.
|
||||
-- @param #number DurationMax (Optional) Max duration before switching the orbit position. Default is keep same orbit until RTB or engage.
|
||||
-- @param #table CapCoordinates Table of coordinates of first race track point. Second point is determined by leg length and heading.
|
||||
-- @return #AI_A2A_PATROL self
|
||||
function AI_A2A_PATROL:SetRaceTrackPattern(LegMin, LegMax, HeadingMin, HeadingMax, DurationMin, DurationMax, CapCoordinates)
|
||||
self:F2({leglength, duration})
|
||||
|
||||
self.racetrack=true
|
||||
self.racetracklegmin=LegMin or 10000
|
||||
self.racetracklegmax=LegMax or 15000
|
||||
self.racetrackheadingmin=HeadingMin or 0
|
||||
self.racetrackheadingmax=HeadingMax or 180
|
||||
self.racetrackdurationmin=DurationMin
|
||||
self.racetrackdurationmax=DurationMax
|
||||
|
||||
if self.racetrackdurationmax and not self.racetrackdurationmin then
|
||||
self.racetrackdurationmin=self.racetrackdurationmax
|
||||
end
|
||||
|
||||
self.racetrackcapcoordinates=CapCoordinates
|
||||
|
||||
end
|
||||
|
||||
|
||||
--- Defines a new patrol route using the @{Process_PatrolZone} parameters and settings.
|
||||
-- @param #AI_A2A_PATROL self
|
||||
@@ -312,7 +341,7 @@ function AI_A2A_PATROL:onafterRoute( AIPatrol, From, Event, To )
|
||||
end
|
||||
|
||||
|
||||
if AIPatrol:IsAlive() then
|
||||
if AIPatrol and AIPatrol:IsAlive() then
|
||||
|
||||
local PatrolRoute = {}
|
||||
|
||||
@@ -320,31 +349,78 @@ function AI_A2A_PATROL:onafterRoute( AIPatrol, From, Event, To )
|
||||
|
||||
local CurrentCoord = AIPatrol:GetCoordinate()
|
||||
|
||||
local ToTargetCoord = self.PatrolZone:GetRandomPointVec2()
|
||||
ToTargetCoord:SetAlt( math.random( self.PatrolFloorAltitude, self.PatrolCeilingAltitude ) )
|
||||
self:SetTargetDistance( ToTargetCoord ) -- For RTB status check
|
||||
-- Random altitude.
|
||||
local altitude=math.random(self.PatrolFloorAltitude, self.PatrolCeilingAltitude)
|
||||
|
||||
-- Random speed in km/h.
|
||||
local speedkmh = math.random(self.PatrolMinSpeed, self.PatrolMaxSpeed)
|
||||
|
||||
local ToTargetSpeed = math.random( self.PatrolMinSpeed, self.PatrolMaxSpeed )
|
||||
-- First waypoint is current position.
|
||||
PatrolRoute[1]=CurrentCoord:WaypointAirTurningPoint(nil, speedkmh, {}, "Current")
|
||||
|
||||
--- Create a route point of type air.
|
||||
local ToPatrolRoutePoint = ToTargetCoord:WaypointAir(
|
||||
self.PatrolAltType,
|
||||
POINT_VEC3.RoutePointType.TurningPoint,
|
||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
||||
ToTargetSpeed,
|
||||
true
|
||||
)
|
||||
if self.racetrack then
|
||||
|
||||
-- Random heading.
|
||||
local heading = math.random(self.racetrackheadingmin, self.racetrackheadingmax)
|
||||
|
||||
-- Random leg length.
|
||||
local leg=math.random(self.racetracklegmin, self.racetracklegmax)
|
||||
|
||||
-- Random duration if any.
|
||||
local duration = self.racetrackdurationmin
|
||||
if self.racetrackdurationmax then
|
||||
duration=math.random(self.racetrackdurationmin, self.racetrackdurationmax)
|
||||
end
|
||||
|
||||
-- CAP coordinate.
|
||||
local c0=self.PatrolZone:GetRandomCoordinate()
|
||||
if self.racetrackcapcoordinates and #self.racetrackcapcoordinates>0 then
|
||||
c0=self.racetrackcapcoordinates[math.random(#self.racetrackcapcoordinates)]
|
||||
end
|
||||
|
||||
-- Race track points.
|
||||
local c1=c0:SetAltitude(altitude) --Core.Point#COORDINATE
|
||||
local c2=c1:Translate(leg, heading):SetAltitude(altitude)
|
||||
|
||||
self:SetTargetDistance(c0) -- For RTB status check
|
||||
|
||||
-- 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)))
|
||||
--c1:MarkToAll("Race track c1")
|
||||
--c2:MarkToAll("Race track c2")
|
||||
|
||||
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 )
|
||||
|
||||
-- Task to orbit.
|
||||
local taskOrbit=AIPatrol:TaskOrbit(c1, altitude, UTILS.KmphToMps(speedkmh), c2)
|
||||
|
||||
-- Task function to redo the patrol at other random position.
|
||||
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 taskCont=AIPatrol:TaskControlled(taskOrbit, taskCond)
|
||||
|
||||
-- Second waypoint
|
||||
PatrolRoute[2]=c1:WaypointAirTurningPoint(self.PatrolAltType, speedkmh, {taskCont, taskPatrol}, "CAP Orbit")
|
||||
|
||||
else
|
||||
|
||||
-- Target coordinate.
|
||||
local ToTargetCoord=self.PatrolZone:GetRandomCoordinate() --Core.Point#COORDINATE
|
||||
ToTargetCoord:SetAltitude(altitude)
|
||||
|
||||
self:SetTargetDistance( ToTargetCoord ) -- For RTB status check
|
||||
|
||||
local taskReRoute=AIPatrol:TaskFunction( "AI_A2A_PATROL.PatrolRoute", self )
|
||||
|
||||
PatrolRoute[2]=ToTargetCoord:WaypointAirTurningPoint(self.PatrolAltType, speedkmh, {taskReRoute}, "Patrol Point")
|
||||
|
||||
end
|
||||
|
||||
-- ROE
|
||||
AIPatrol:OptionROEReturnFire()
|
||||
AIPatrol:OptionROTEvadeFire()
|
||||
|
||||
|
||||
-- Patrol.
|
||||
AIPatrol:Route( PatrolRoute, 0.5)
|
||||
end
|
||||
|
||||
|
||||
@@ -0,0 +1,155 @@
|
||||
--- **AI** -- (R2.4) - Models the assignment of AI escorts to player flights.
|
||||
--
|
||||
-- ## Features:
|
||||
-- --
|
||||
-- * Provides the facilities to trigger escorts when players join flight units.
|
||||
-- * Provide different means how escorts can be triggered:
|
||||
-- * Directly when a player joins a plane.
|
||||
-- * Through the menu.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ## Test Missions:
|
||||
--
|
||||
-- Test missions can be located on the main GITHUB site.
|
||||
--
|
||||
-- [FlightControl-Master/MOOSE_MISSIONS]
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @module AI.AI_Escort_Dispatcher
|
||||
-- @image AI_Escort_Dispatcher.JPG
|
||||
|
||||
|
||||
--- @type AI_ESCORT_DISPATCHER
|
||||
-- @field Core.Set#SET_GROUP CarrierSet The set of @{Wrapper.Group#GROUP} objects of carriers that will transport the cargo.
|
||||
-- @field Core.Set#SET_GROUP EscortGroupSet The set of group AI escorting the EscortUnit.
|
||||
-- @field #string EscortName Name of the escort.
|
||||
-- @field #string EscortBriefing A text showing the AI_ESCORT briefing to the player. Note that if no EscortBriefing is provided, the default briefing will be shown.
|
||||
-- @extends Core.Fsm#FSM
|
||||
|
||||
|
||||
--- A dynamic cargo handling capability for AI groups.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @field #AI_ESCORT_DISPATCHER
|
||||
AI_ESCORT_DISPATCHER = {
|
||||
ClassName = "AI_ESCORT_DISPATCHER",
|
||||
}
|
||||
|
||||
--- @field #list
|
||||
AI_ESCORT_DISPATCHER.AI_Escorts = {}
|
||||
|
||||
|
||||
--- Creates a new AI_ESCORT_DISPATCHER object.
|
||||
-- @param #AI_ESCORT_DISPATCHER self
|
||||
-- @param Core.Set#SET_GROUP CarrierSet The set of @{Wrapper.Group#GROUP} objects of carriers that will transport the cargo.
|
||||
-- @param Core.Spawn#SPAWN EscortSpawn The spawn object that will spawn in the Escorts.
|
||||
-- @param Wrapper.Airbase#AIRBASE EscortAirbase The airbase where the escorts are spawned.
|
||||
-- @param #string EscortName Name of the escort.
|
||||
-- @param #string EscortBriefing A text showing the AI_ESCORT briefing to the player. Note that if no EscortBriefing is provided, the default briefing will be shown.
|
||||
-- @return #AI_ESCORT_DISPATCHER
|
||||
function AI_ESCORT_DISPATCHER:New( CarrierSet, EscortSpawn, EscortAirbase, EscortName, EscortBriefing )
|
||||
|
||||
local self = BASE:Inherit( self, FSM:New() ) -- #AI_ESCORT_DISPATCHER
|
||||
|
||||
self.CarrierSet = CarrierSet
|
||||
self.EscortSpawn = EscortSpawn
|
||||
self.EscortAirbase = EscortAirbase
|
||||
self.EscortName = EscortName
|
||||
self.EscortBriefing = EscortBriefing
|
||||
|
||||
self:SetStartState( "Idle" )
|
||||
|
||||
self:AddTransition( "Monitoring", "Monitor", "Monitoring" )
|
||||
|
||||
self:AddTransition( "Idle", "Start", "Monitoring" )
|
||||
self:AddTransition( "Monitoring", "Stop", "Idle" )
|
||||
|
||||
-- Put a Dead event handler on CarrierSet, to ensure that when a carrier is destroyed, that all internal parameters are reset.
|
||||
function self.CarrierSet.OnAfterRemoved( CarrierSet, From, Event, To, CarrierName, Carrier )
|
||||
self:F( { Carrier = Carrier:GetName() } )
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
function AI_ESCORT_DISPATCHER:onafterStart( From, Event, To )
|
||||
|
||||
self:HandleEvent( EVENTS.Birth )
|
||||
|
||||
self:HandleEvent( EVENTS.PlayerLeaveUnit )
|
||||
|
||||
end
|
||||
|
||||
|
||||
--- @param #AI_ESCORT_DISPATCHER self
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
function AI_ESCORT_DISPATCHER:OnEventBirth( EventData )
|
||||
|
||||
local PlayerGroupName = EventData.IniGroupName
|
||||
local PlayerGroup = EventData.IniGroup
|
||||
local PlayerUnit = EventData.IniUnit
|
||||
|
||||
self:I({EscortAirbase= self.EscortAirbase } )
|
||||
self:I({PlayerGroupName = PlayerGroupName } )
|
||||
self:I({PlayerGroup = PlayerGroup})
|
||||
self:I({FirstGroup = self.CarrierSet:GetFirst()})
|
||||
self:I({FindGroup = self.CarrierSet:FindGroup( PlayerGroupName )})
|
||||
|
||||
if self.CarrierSet:FindGroup( PlayerGroupName ) then
|
||||
if not self.AI_Escorts[PlayerGroupName] then
|
||||
local LeaderUnit = PlayerUnit
|
||||
local EscortGroup = self.EscortSpawn:SpawnAtAirbase( self.EscortAirbase, SPAWN.Takeoff.Hot )
|
||||
self:I({EscortGroup = EscortGroup})
|
||||
|
||||
self:ScheduleOnce( 1,
|
||||
function( EscortGroup )
|
||||
local EscortSet = SET_GROUP:New()
|
||||
EscortSet:AddGroup( EscortGroup )
|
||||
self.AI_Escorts[PlayerGroupName] = AI_ESCORT:New( LeaderUnit, EscortSet, self.EscortName, self.EscortBriefing )
|
||||
self.AI_Escorts[PlayerGroupName]:FormationTrail( 0, 100, 0 )
|
||||
if EscortGroup:IsHelicopter() then
|
||||
self.AI_Escorts[PlayerGroupName]:MenusHelicopters()
|
||||
else
|
||||
self.AI_Escorts[PlayerGroupName]:MenusAirplanes()
|
||||
end
|
||||
self.AI_Escorts[PlayerGroupName]:__Start( 0.1 )
|
||||
end, EscortGroup
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
--- Start Trigger for AI_ESCORT_DISPATCHER
|
||||
-- @function [parent=#AI_ESCORT_DISPATCHER] Start
|
||||
-- @param #AI_ESCORT_DISPATCHER self
|
||||
|
||||
--- Start Asynchronous Trigger for AI_ESCORT_DISPATCHER
|
||||
-- @function [parent=#AI_ESCORT_DISPATCHER] __Start
|
||||
-- @param #AI_ESCORT_DISPATCHER self
|
||||
-- @param #number Delay
|
||||
|
||||
--- Stop Trigger for AI_ESCORT_DISPATCHER
|
||||
-- @function [parent=#AI_ESCORT_DISPATCHER] Stop
|
||||
-- @param #AI_ESCORT_DISPATCHER self
|
||||
|
||||
--- Stop Asynchronous Trigger for AI_ESCORT_DISPATCHER
|
||||
-- @function [parent=#AI_ESCORT_DISPATCHER] __Stop
|
||||
-- @param #AI_ESCORT_DISPATCHER self
|
||||
-- @param #number Delay
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
--- **AI** -- (R2.4) - Models the assignment of AI escorts to player flights upon request using the radio menu.
|
||||
--
|
||||
-- ## Features:
|
||||
-- --
|
||||
-- * Provides the facilities to trigger escorts when players join flight units.
|
||||
-- * Provide different means how escorts can be triggered:
|
||||
-- * Directly when a player joins a plane.
|
||||
-- * Through the menu.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ## Test Missions:
|
||||
--
|
||||
-- Test missions can be located on the main GITHUB site.
|
||||
--
|
||||
-- [FlightControl-Master/MOOSE_MISSIONS]
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @module AI.AI_ESCORT_DISPATCHER_REQUEST
|
||||
-- @image AI_ESCORT_DISPATCHER_REQUEST.JPG
|
||||
|
||||
|
||||
--- @type AI_ESCORT_DISPATCHER_REQUEST
|
||||
-- @field Core.Set#SET_GROUP CarrierSet The set of @{Wrapper.Group#GROUP} objects of carriers that will transport the cargo.
|
||||
-- @field Core.Set#SET_GROUP EscortGroupSet The set of group AI escorting the EscortUnit.
|
||||
-- @field #string EscortName Name of the escort.
|
||||
-- @field #string EscortBriefing A text showing the AI_ESCORT briefing to the player. Note that if no EscortBriefing is provided, the default briefing will be shown.
|
||||
-- @extends Core.Fsm#FSM
|
||||
|
||||
|
||||
--- A dynamic cargo handling capability for AI groups.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @field #AI_ESCORT_DISPATCHER_REQUEST
|
||||
AI_ESCORT_DISPATCHER_REQUEST = {
|
||||
ClassName = "AI_ESCORT_DISPATCHER_REQUEST",
|
||||
}
|
||||
|
||||
--- @field #list
|
||||
AI_ESCORT_DISPATCHER_REQUEST.AI_Escorts = {}
|
||||
|
||||
|
||||
--- Creates a new AI_ESCORT_DISPATCHER_REQUEST object.
|
||||
-- @param #AI_ESCORT_DISPATCHER_REQUEST self
|
||||
-- @param Core.Set#SET_GROUP CarrierSet The set of @{Wrapper.Group#GROUP} objects of carriers that will transport the cargo.
|
||||
-- @param Core.Spawn#SPAWN EscortSpawn The spawn object that will spawn in the Escorts.
|
||||
-- @param Wrapper.Airbase#AIRBASE EscortAirbase The airbase where the escorts are spawned.
|
||||
-- @param #string EscortName Name of the escort.
|
||||
-- @param #string EscortBriefing A text showing the AI_ESCORT briefing to the player. Note that if no EscortBriefing is provided, the default briefing will be shown.
|
||||
-- @return #AI_ESCORT_DISPATCHER_REQUEST
|
||||
function AI_ESCORT_DISPATCHER_REQUEST:New( CarrierSet, EscortSpawn, EscortAirbase, EscortName, EscortBriefing )
|
||||
|
||||
local self = BASE:Inherit( self, FSM:New() ) -- #AI_ESCORT_DISPATCHER_REQUEST
|
||||
|
||||
self.CarrierSet = CarrierSet
|
||||
self.EscortSpawn = EscortSpawn
|
||||
self.EscortAirbase = EscortAirbase
|
||||
self.EscortName = EscortName
|
||||
self.EscortBriefing = EscortBriefing
|
||||
|
||||
self:SetStartState( "Idle" )
|
||||
|
||||
self:AddTransition( "Monitoring", "Monitor", "Monitoring" )
|
||||
|
||||
self:AddTransition( "Idle", "Start", "Monitoring" )
|
||||
self:AddTransition( "Monitoring", "Stop", "Idle" )
|
||||
|
||||
-- Put a Dead event handler on CarrierSet, to ensure that when a carrier is destroyed, that all internal parameters are reset.
|
||||
function self.CarrierSet.OnAfterRemoved( CarrierSet, From, Event, To, CarrierName, Carrier )
|
||||
self:F( { Carrier = Carrier:GetName() } )
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
function AI_ESCORT_DISPATCHER_REQUEST:onafterStart( From, Event, To )
|
||||
|
||||
self:HandleEvent( EVENTS.Birth )
|
||||
|
||||
self:HandleEvent( EVENTS.PlayerLeaveUnit )
|
||||
|
||||
end
|
||||
|
||||
|
||||
--- @param #AI_ESCORT_DISPATCHER_REQUEST self
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
function AI_ESCORT_DISPATCHER_REQUEST:OnEventBirth( EventData )
|
||||
|
||||
local PlayerGroupName = EventData.IniGroupName
|
||||
local PlayerGroup = EventData.IniGroup
|
||||
local PlayerUnit = EventData.IniUnit
|
||||
|
||||
self:I({PlayerGroupName = PlayerGroupName } )
|
||||
self:I({PlayerGroup = PlayerGroup})
|
||||
self:I({FirstGroup = self.CarrierSet:GetFirst()})
|
||||
self:I({FindGroup = self.CarrierSet:FindGroup( PlayerGroupName )})
|
||||
|
||||
if self.CarrierSet:FindGroup( PlayerGroupName ) then
|
||||
if not self.AI_Escorts[PlayerGroupName] then
|
||||
local LeaderUnit = PlayerUnit
|
||||
self:ScheduleOnce( 0.1,
|
||||
function()
|
||||
self.AI_Escorts[PlayerGroupName] = AI_ESCORT_REQUEST:New( LeaderUnit, self.EscortSpawn, self.EscortAirbase, self.EscortName, self.EscortBriefing )
|
||||
self.AI_Escorts[PlayerGroupName]:FormationTrail( 0, 100, 0 )
|
||||
if PlayerGroup:IsHelicopter() then
|
||||
self.AI_Escorts[PlayerGroupName]:MenusHelicopters()
|
||||
else
|
||||
self.AI_Escorts[PlayerGroupName]:MenusAirplanes()
|
||||
end
|
||||
self.AI_Escorts[PlayerGroupName]:__Start( 0.1 )
|
||||
end
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
--- Start Trigger for AI_ESCORT_DISPATCHER_REQUEST
|
||||
-- @function [parent=#AI_ESCORT_DISPATCHER_REQUEST] Start
|
||||
-- @param #AI_ESCORT_DISPATCHER_REQUEST self
|
||||
|
||||
--- Start Asynchronous Trigger for AI_ESCORT_DISPATCHER_REQUEST
|
||||
-- @function [parent=#AI_ESCORT_DISPATCHER_REQUEST] __Start
|
||||
-- @param #AI_ESCORT_DISPATCHER_REQUEST self
|
||||
-- @param #number Delay
|
||||
|
||||
--- Stop Trigger for AI_ESCORT_DISPATCHER_REQUEST
|
||||
-- @function [parent=#AI_ESCORT_DISPATCHER_REQUEST] Stop
|
||||
-- @param #AI_ESCORT_DISPATCHER_REQUEST self
|
||||
|
||||
--- Stop Asynchronous Trigger for AI_ESCORT_DISPATCHER_REQUEST
|
||||
-- @function [parent=#AI_ESCORT_DISPATCHER_REQUEST] __Stop
|
||||
-- @param #AI_ESCORT_DISPATCHER_REQUEST self
|
||||
-- @param #number Delay
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -206,7 +206,7 @@ AI_ESCORT_REQUEST = {
|
||||
-- Escort:__Start( 5 )
|
||||
function AI_ESCORT_REQUEST:New( EscortUnit, EscortSpawn, EscortAirbase, EscortName, EscortBriefing )
|
||||
|
||||
self.EscortGroupSet = SET_GROUP:New()
|
||||
self.EscortGroupSet = SET_GROUP:New():FilterDeads():FilterCrashes()
|
||||
self.EscortSpawn = EscortSpawn
|
||||
self.EscortAirbase = EscortAirbase
|
||||
|
||||
@@ -227,12 +227,12 @@ function AI_ESCORT_REQUEST:SpawnEscort()
|
||||
|
||||
local EscortGroup = self.EscortSpawn:SpawnAtAirbase( self.EscortAirbase, SPAWN.Takeoff.Hot )
|
||||
|
||||
EscortGroup:OptionROTVertical()
|
||||
EscortGroup:OptionROEHoldFire()
|
||||
|
||||
self:ScheduleOnce( 0.1,
|
||||
function( EscortGroup )
|
||||
|
||||
EscortGroup:OptionROTVertical()
|
||||
EscortGroup:OptionROEHoldFire()
|
||||
|
||||
self.EscortGroupSet:AddGroup( EscortGroup )
|
||||
|
||||
local LeaderEscort = self.EscortGroupSet:GetFirst() -- Wrapper.Group#GROUP
|
||||
@@ -269,6 +269,7 @@ function AI_ESCORT_REQUEST:onafterStart( EscortGroupSet )
|
||||
self:F()
|
||||
|
||||
if not self.MenuRequestEscort then
|
||||
self.MainMenu = MENU_GROUP:New( self.PlayerGroup, self.EscortName )
|
||||
self.MenuRequestEscort = MENU_GROUP_COMMAND:New( self.LeaderGroup, "Request new escort ", self.MainMenu,
|
||||
function()
|
||||
self:SpawnEscort()
|
||||
|
||||
@@ -1102,7 +1102,7 @@ function AI_FORMATION:onenterFollowing( FollowGroupSet ) --R2.1
|
||||
|
||||
if FollowGroup:GetState( FollowGroup, "Mode" ) == self.__Enum.Mode.Formation then
|
||||
|
||||
self:I({Mode=FollowGroup:GetState( FollowGroup, "Mode" )})
|
||||
self:T({Mode=FollowGroup:GetState( FollowGroup, "Mode" )})
|
||||
|
||||
FollowGroup:OptionROTEvadeFire()
|
||||
FollowGroup:OptionROEReturnFire()
|
||||
|
||||
@@ -864,9 +864,9 @@ function EVENT:onEvent( Event )
|
||||
if Event.IniDCSGroup and Event.IniDCSGroup:isExist() then
|
||||
Event.IniDCSGroupName = Event.IniDCSGroup:getName()
|
||||
Event.IniGroup = GROUP:FindByName( Event.IniDCSGroupName )
|
||||
if Event.IniGroup then
|
||||
--if Event.IniGroup then
|
||||
Event.IniGroupName = Event.IniDCSGroupName
|
||||
end
|
||||
--end
|
||||
end
|
||||
Event.IniPlayerName = Event.IniDCSUnit:getPlayerName()
|
||||
Event.IniCoalition = Event.IniDCSUnit:getCoalition()
|
||||
@@ -918,9 +918,9 @@ function EVENT:onEvent( Event )
|
||||
if Event.TgtDCSGroup and Event.TgtDCSGroup:isExist() then
|
||||
Event.TgtDCSGroupName = Event.TgtDCSGroup:getName()
|
||||
Event.TgtGroup = GROUP:FindByName( Event.TgtDCSGroupName )
|
||||
if Event.TgtGroup then
|
||||
--if Event.TgtGroup then
|
||||
Event.TgtGroupName = Event.TgtDCSGroupName
|
||||
end
|
||||
--end
|
||||
end
|
||||
Event.TgtPlayerName = Event.TgtDCSUnit:getPlayerName()
|
||||
Event.TgtCoalition = Event.TgtDCSUnit:getCoalition()
|
||||
|
||||
@@ -517,7 +517,7 @@ end
|
||||
-- local myUnit = UNIT:FindByName("MyUnit")
|
||||
-- local myBeacon = myUnit:GetBeacon() -- Creates the beacon
|
||||
--
|
||||
-- myBeacon:TACAN(20, "Y", "TEXACO", true) -- Activate the beacon
|
||||
-- myBeacon:ActivateTACAN(20, "Y", "TEXACO", true) -- Activate the beacon
|
||||
function BEACON:ActivateTACAN(Channel, Mode, Message, Bearing, Duration)
|
||||
self:T({channel=Channel, mode=Mode, callsign=Message, bearing=Bearing, duration=Duration})
|
||||
|
||||
|
||||
@@ -558,6 +558,10 @@ do -- SET_BASE
|
||||
--- Iterate the SET_BASE and derived classes and call an iterator function for the given SET_BASE, providing the Object for each element within the set and optional parameters.
|
||||
-- @param #SET_BASE self
|
||||
-- @param #function IteratorFunction The function that will be called.
|
||||
-- @param #table arg Arguments of the IteratorFunction.
|
||||
-- @param #SET_BASE Set (Optional) The set to use. Default self:GetSet().
|
||||
-- @param #function Function (Optional) A function returning a #boolean true/false. Only if true, the IteratorFunction is called.
|
||||
-- @param #table FunctionArguments (Optional) Function arguments.
|
||||
-- @return #SET_BASE self
|
||||
function SET_BASE:ForEach( IteratorFunction, arg, Set, Function, FunctionArguments )
|
||||
self:F3( arg )
|
||||
@@ -571,7 +575,7 @@ do -- SET_BASE
|
||||
local Object = ObjectData
|
||||
self:T3( Object )
|
||||
if Function then
|
||||
if Function( unpack( FunctionArguments ), Object ) == true then
|
||||
if Function( unpack( FunctionArguments or {} ), Object ) == true then
|
||||
IteratorFunction( Object, unpack( arg ) )
|
||||
end
|
||||
else
|
||||
|
||||
@@ -319,7 +319,7 @@ function SPAWN:New( SpawnTemplatePrefix )
|
||||
self.SpawnUnControlled = false
|
||||
self.SpawnInitKeepUnitNames = false -- Overwrite unit names by default with group name.
|
||||
self.DelayOnOff = false -- No intial delay when spawning the first group.
|
||||
self.Grouping = nil -- No grouping.
|
||||
self.SpawnGrouping = nil -- No grouping.
|
||||
self.SpawnInitLivery = nil -- No special livery.
|
||||
self.SpawnInitSkill = nil -- No special skill.
|
||||
self.SpawnInitFreq = nil -- No special frequency.
|
||||
@@ -371,7 +371,7 @@ function SPAWN:NewWithAlias( SpawnTemplatePrefix, SpawnAliasPrefix )
|
||||
self.SpawnUnControlled = false
|
||||
self.SpawnInitKeepUnitNames = false -- Overwrite unit names by default with group name.
|
||||
self.DelayOnOff = false -- No intial delay when spawning the first group.
|
||||
self.Grouping = nil -- No grouping.
|
||||
self.SpawnGrouping = nil -- No grouping.
|
||||
self.SpawnInitLivery = nil -- No special livery.
|
||||
self.SpawnInitSkill = nil -- No special skill.
|
||||
self.SpawnInitFreq = nil -- No special frequency.
|
||||
@@ -549,7 +549,7 @@ end
|
||||
|
||||
--- Sets the country of the spawn group. Note that the country determins the coalition of the group depending on which country is defined to be on which side for each specific mission!
|
||||
-- @param #SPAWN self
|
||||
-- @param #DCS.country Country Country id as number or enumerator:
|
||||
-- @param #number Country Country id as number or enumerator:
|
||||
--
|
||||
-- * @{DCS#country.id.RUSSIA}
|
||||
-- * @{DCS#county.id.USA}
|
||||
@@ -1438,7 +1438,7 @@ end
|
||||
-- @param Wrapper.Airbase#AIRBASE.TerminalType TerminalType (optional) The terminal type the aircraft should be spawned at. See @{Wrapper.Airbase#AIRBASE.TerminalType}.
|
||||
-- @param #boolean EmergencyAirSpawn (optional) If true (default), groups are spawned in air if there is no parking spot at the airbase. If false, nothing is spawned if no parking spot is available.
|
||||
-- @param #table Parkingdata (optional) Table holding the coordinates and terminal ids for all units of the group. Spawning will be forced to happen at exactily these spots!
|
||||
-- @return Wrapper.Group#GROUP that was spawned or nil when nothing was spawned.
|
||||
-- @return Wrapper.Group#GROUP The group that was spawned or nil when nothing was spawned.
|
||||
-- @usage
|
||||
-- Spawn_Plane = SPAWN:New( "Plane" )
|
||||
-- Spawn_Plane:SpawnAtAirbase( AIRBASE:FindByName( AIRBASE.Caucasus.Krymsk ), SPAWN.Takeoff.Cold )
|
||||
@@ -1498,11 +1498,7 @@ function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalT
|
||||
local TemplateGroup = GROUP:FindByName(self.SpawnTemplatePrefix)
|
||||
local TemplateUnit=TemplateGroup:GetUnit(1)
|
||||
|
||||
--local ishelo=TemplateUnit:HasAttribute("Helicopters")
|
||||
--local isbomber=TemplateUnit:HasAttribute("Bombers")
|
||||
--local istransport=TemplateUnit:HasAttribute("Transports")
|
||||
--local isfighter=TemplateUnit:HasAttribute("Battleplanes")
|
||||
|
||||
-- General category of spawned group.
|
||||
local group=TemplateGroup
|
||||
local istransport=group:HasAttribute("Transports") and group:HasAttribute("Planes")
|
||||
local isawacs=group:HasAttribute("AWACS")
|
||||
@@ -1577,8 +1573,17 @@ function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalT
|
||||
|
||||
-- Set terminal type.
|
||||
local termtype=TerminalType
|
||||
if spawnonrunway then
|
||||
termtype=AIRBASE.TerminalType.Runway
|
||||
if spawnonrunway then
|
||||
if spawnonship then
|
||||
-- Looks like there are no runway spawn spots on the stennis!
|
||||
if ishelo then
|
||||
termtype=AIRBASE.TerminalType.HelicopterUsable
|
||||
else
|
||||
termtype=AIRBASE.TerminalType.OpenMedOrBig
|
||||
end
|
||||
else
|
||||
termtype=AIRBASE.TerminalType.Runway
|
||||
end
|
||||
end
|
||||
|
||||
-- Scan options. Might make that input somehow.
|
||||
@@ -1647,9 +1652,9 @@ function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalT
|
||||
|
||||
-- Get parking data.
|
||||
local parkingdata=SpawnAirbase:GetParkingSpotsTable(termtype)
|
||||
self:T2(string.format("Parking at %s, terminal type %s:", SpawnAirbase:GetName(), tostring(termtype)))
|
||||
self:T(string.format("Parking at %s, terminal type %s:", SpawnAirbase:GetName(), tostring(termtype)))
|
||||
for _,_spot in pairs(parkingdata) do
|
||||
self:T2(string.format("%s, Termin Index = %3d, Term Type = %03d, Free = %5s, TOAC = %5s, Term ID0 = %3d, Dist2Rwy = %4d",
|
||||
self:T(string.format("%s, Termin Index = %3d, Term Type = %03d, Free = %5s, TOAC = %5s, Term ID0 = %3d, Dist2Rwy = %4d",
|
||||
SpawnAirbase:GetName(), _spot.TerminalID, _spot.TerminalType,tostring(_spot.Free),tostring(_spot.TOAC),_spot.TerminalID0,_spot.DistToRwy))
|
||||
end
|
||||
self:T(string.format("%s at %s: free parking spots = %d - number of units = %d", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), nfree, nunits))
|
||||
@@ -1802,8 +1807,8 @@ function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalT
|
||||
end
|
||||
|
||||
-- Debug output.
|
||||
self:T2(string.format("Group %s unit number %d: Parking = %s",self.SpawnTemplatePrefix, UnitID, tostring(UnitTemplate.parking)))
|
||||
self:T2(string.format("Group %s unit number %d: Parking ID = %s",self.SpawnTemplatePrefix, UnitID, tostring(UnitTemplate.parking_id)))
|
||||
self:T(string.format("Group %s unit number %d: Parking = %s",self.SpawnTemplatePrefix, UnitID, tostring(UnitTemplate.parking)))
|
||||
self:T(string.format("Group %s unit number %d: Parking ID = %s",self.SpawnTemplatePrefix, UnitID, tostring(UnitTemplate.parking_id)))
|
||||
self:T2('After Translation SpawnTemplate.units['..UnitID..'].x = '..SpawnTemplate.units[UnitID].x..', SpawnTemplate.units['..UnitID..'].y = '..SpawnTemplate.units[UnitID].y)
|
||||
end
|
||||
end
|
||||
@@ -1816,7 +1821,7 @@ function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalT
|
||||
SpawnTemplate.x = PointVec3.x
|
||||
SpawnTemplate.y = PointVec3.z
|
||||
|
||||
SpawnTemplate.uncontrolled = nil
|
||||
SpawnTemplate.uncontrolled = self.SpawnUnControlled
|
||||
|
||||
-- Spawn group.
|
||||
local GroupSpawned = self:SpawnWithIndex( self.SpawnIndex )
|
||||
@@ -1840,6 +1845,66 @@ function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalT
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Spawn a group on an @{Wrapper.Airbase} at a specific parking spot.
|
||||
-- @param #SPAWN self
|
||||
-- @param Wrapper.Airbase#AIRBASE Airbase The @{Wrapper.Airbase} where to spawn the group.
|
||||
-- @param #table Spots Table of parking spot IDs. Note that these in general are different from the numbering in the mission editor!
|
||||
-- @param #SPAWN.Takeoff Takeoff (Optional) Takeoff type, i.e. either SPAWN.Takeoff.Cold or SPAWN.Takeoff.Hot. Default is Hot.
|
||||
-- @return Wrapper.Group#GROUP The group that was spawned or nil when nothing was spawned.
|
||||
function SPAWN:SpawnAtParkingSpot(Airbase, Spots, Takeoff) -- R2.5
|
||||
self:F({Airbase=Airbase, Spots=Spots, Takeoff=Takeoff})
|
||||
|
||||
-- Ensure that Spots parameter is a table.
|
||||
if type(Spots)~="table" then
|
||||
Spots={Spots}
|
||||
end
|
||||
|
||||
-- Get template group.
|
||||
local group=GROUP:FindByName(self.SpawnTemplatePrefix)
|
||||
|
||||
-- Get number of units in group.
|
||||
local nunits=self.SpawnGrouping or #group:GetUnits()
|
||||
|
||||
-- Quick check.
|
||||
if nunits then
|
||||
|
||||
-- Check that number of provided parking spots is large enough.
|
||||
if #Spots<nunits then
|
||||
self:E("ERROR: Number of provided parking spots is less than number of units in group!")
|
||||
return nil
|
||||
end
|
||||
|
||||
-- Table of parking data.
|
||||
local Parkingdata={}
|
||||
|
||||
-- Loop over provided Terminal IDs.
|
||||
for _,TerminalID in pairs(Spots) do
|
||||
|
||||
-- Get parking spot data.
|
||||
local spot=Airbase:GetParkingSpotData(TerminalID)
|
||||
|
||||
self:T2({spot=spot})
|
||||
|
||||
if spot and spot.Free then
|
||||
self:T(string.format("Adding parking spot ID=%d TermType=%d", spot.TerminalID, spot.TerminalType))
|
||||
table.insert(Parkingdata, spot)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
if #Parkingdata>=nunits then
|
||||
return self:SpawnAtAirbase(Airbase, Takeoff, nil, nil, nil, Parkingdata)
|
||||
else
|
||||
self:E("ERROR: Could not find enough free parking spots!")
|
||||
end
|
||||
|
||||
|
||||
else
|
||||
self:E("ERROR: Could not get number of units in group!")
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Will park a group at an @{Wrapper.Airbase}.
|
||||
--
|
||||
@@ -2215,10 +2280,11 @@ function SPAWN:ParkAtAirbase( SpawnAirbase, TerminalType, Parkingdata ) -- R2.2,
|
||||
self:ParkAircraft( SpawnAirbase, TerminalType, Parkingdata, 1 )
|
||||
|
||||
for SpawnIndex = 2, self.SpawnMaxGroups do
|
||||
self:ScheduleOnce( SpawnIndex * 0.1, SPAWN.ParkAircraft, self, SpawnAirbase, TerminalType, Parkingdata, SpawnIndex )
|
||||
self:ParkAircraft( SpawnAirbase, TerminalType, Parkingdata, SpawnIndex )
|
||||
--self:ScheduleOnce( SpawnIndex * 0.1, SPAWN.ParkAircraft, self, SpawnAirbase, TerminalType, Parkingdata, SpawnIndex )
|
||||
end
|
||||
|
||||
self:SetSpawnIndex()
|
||||
self:SetSpawnIndex( 0 )
|
||||
|
||||
return nil
|
||||
end
|
||||
@@ -3023,6 +3089,9 @@ function SPAWN:_TranslateRotate( SpawnIndex, SpawnRootX, SpawnRootY, SpawnX, Spa
|
||||
end
|
||||
|
||||
--- Get the next index of the groups to be spawned. This method is complicated, as it is used at several spaces.
|
||||
-- @param #SPAWN self
|
||||
-- @param #number SpawnIndex Spawn index.
|
||||
-- @return #number self.SpawnIndex
|
||||
function SPAWN:_GetSpawnIndex( SpawnIndex )
|
||||
self:F2( { self.SpawnTemplatePrefix, SpawnIndex, self.SpawnMaxGroups, self.SpawnMaxUnitsAlive, self.AliveUnits, #self.SpawnTemplate.units } )
|
||||
|
||||
|
||||
@@ -285,11 +285,8 @@ do
|
||||
|
||||
local RecceDcsUnit = self.Recce:GetDCSObject()
|
||||
|
||||
local aimat=Coordinate
|
||||
aimat.y=aimat.y+1
|
||||
|
||||
self.SpotIR = Spot.createInfraRed( RecceDcsUnit, { x = 0, y = 2, z = 0 }, aimat:GetVec3() )
|
||||
self.SpotLaser = Spot.createLaser( RecceDcsUnit, { x = 0, y = 2, z = 0 }, aimat:GetVec3(), LaserCode )
|
||||
self.SpotIR = Spot.createInfraRed( RecceDcsUnit, { x = 0, y = 1, z = 0 }, Coordinate:GetVec3() )
|
||||
self.SpotLaser = Spot.createLaser( RecceDcsUnit, { x = 0, y = 1, z = 0 }, Coordinate:GetVec3(), LaserCode )
|
||||
|
||||
if Duration then
|
||||
self.ScheduleID = self.LaseScheduler:Schedule( self, StopLase, {self}, Duration )
|
||||
@@ -323,14 +320,14 @@ do
|
||||
self:__Lasing( -0.2 )
|
||||
elseif self.TargetCoord then
|
||||
|
||||
local aimat=self.TargetCoord --Core.Point#COORDINATE
|
||||
aimat.y=aimat.y+1+math.random(-100,100)/100
|
||||
aimat.x=aimat.x+math.random(-100,100)/100
|
||||
-- Wiggle the IR spot a bit.
|
||||
local irvec3={x=self.TargetCoord.x+math.random(-100,100)/100, y=self.TargetCoord.y+math.random(-100,100)/100, z=self.TargetCoord.z} --#DCS.Vec3
|
||||
local lsvec3={x=self.TargetCoord.x, y=self.TargetCoord.y, z=self.TargetCoord.z} --#DCS.Vec3
|
||||
|
||||
self.SpotIR:setPoint(aimat:GetVec3())
|
||||
self.SpotLaser:setPoint(self.TargetCoord:GetVec3())
|
||||
self.SpotIR:setPoint(irvec3)
|
||||
self.SpotLaser:setPoint(lsvec3)
|
||||
|
||||
self:__Lasing( -0.2 )
|
||||
self:__Lasing(-0.25)
|
||||
else
|
||||
self:F( { "Target is not alive", self.Target:IsAlive() } )
|
||||
end
|
||||
|
||||
@@ -442,6 +442,33 @@ function ZONE_RADIUS:New( ZoneName, Vec2, Radius )
|
||||
return self
|
||||
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.
|
||||
-- @param #ZONE_RADIUS self
|
||||
-- @param #number Points (optional) The amount of points in the circle. Default 360.
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -291,17 +291,34 @@ do -- DETECTION_BASE
|
||||
--- @type DETECTION_BASE.DetectedItems
|
||||
-- @list <#DETECTION_BASE.DetectedItem>
|
||||
|
||||
--- @type DETECTION_BASE.DetectedItem
|
||||
--- Detected item data structrue.
|
||||
-- @type DETECTION_BASE.DetectedItem
|
||||
-- @field #boolean IsDetected Indicates if the DetectedItem has been detected or not.
|
||||
-- @field Core.Set#SET_UNIT Set
|
||||
-- @field Core.Set#SET_UNIT Set -- The Set of Units in the detected area.
|
||||
-- @field Core.Zone#ZONE_UNIT Zone -- The Zone of the detected area.
|
||||
-- @field #boolean Changed Documents if the detected area has changes.
|
||||
-- @field Core.Set#SET_UNIT Set The Set of Units in the detected area.
|
||||
-- @field Core.Zone#ZONE_UNIT Zone The Zone of the detected area.
|
||||
-- @field #boolean Changed Documents if the detected area has changed.
|
||||
-- @field #table Changes A list of the changes reported on the detected area. (It is up to the user of the detected area to consume those changes).
|
||||
-- @field #number ID -- The identifier of the detected area.
|
||||
-- @field #number ID The identifier of the detected area.
|
||||
-- @field #boolean FriendliesNearBy Indicates if there are friendlies within the detected area.
|
||||
-- @field Wrapper.Unit#UNIT NearestFAC The nearest FAC near the Area.
|
||||
-- @field Core.Point#COORDINATE Coordinate The last known coordinate of the DetectedItem.
|
||||
-- @field Core.Point#COORDINATE InterceptCoord Intercept coordiante.
|
||||
-- @field #number DistanceRecce Distance in meters of the Recce.
|
||||
-- @field #number Index Detected item key. Could also be a string.
|
||||
-- @field #string ItemID ItemPrefix .. "." .. self.DetectedItemMax.
|
||||
-- @field #boolean Locked Lock detected item.
|
||||
-- @field #table PlayersNearBy Table of nearby players.
|
||||
-- @field #table FriendliesDistance Table of distances to friendly units.
|
||||
-- @field #string TypeName Type name of the detected unit.
|
||||
-- @field #string CategoryName Catetory name of the detected unit.
|
||||
-- @field #string Name Name of the detected object.
|
||||
-- @field #boolean IsVisible If true, detected object is visible.
|
||||
-- @field #number LastTime Last time the detected item was seen.
|
||||
-- @field DCS#Vec3 LastPos Last known position of the detected item.
|
||||
-- @field DCS#Vec3 LastVelocity Last recorded 3D velocity vector of the detected item.
|
||||
-- @field #boolean KnowType Type of detected item is known.
|
||||
-- @field #boolean KnowDistance Distance to the detected item is known.
|
||||
-- @field #number Distance Distance to the detected item.
|
||||
|
||||
--- DETECTION constructor.
|
||||
-- @param #DETECTION_BASE self
|
||||
@@ -441,15 +458,18 @@ do -- DETECTION_BASE
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
-- @param #table Units Table of detected units.
|
||||
|
||||
--- Synchronous Event Trigger for Event Detected.
|
||||
-- @function [parent=#DETECTION_BASE] Detected
|
||||
-- @param #DETECTION_BASE self
|
||||
-- @param #table Units Table of detected units.
|
||||
|
||||
--- Asynchronous Event Trigger for Event Detected.
|
||||
-- @function [parent=#DETECTION_BASE] __Detected
|
||||
-- @param #DETECTION_BASE self
|
||||
-- @param #number Delay The delay in seconds.
|
||||
-- @param #table Units Table of detected units.
|
||||
|
||||
self:AddTransition( "Detecting", "DetectedItem", "Detecting" )
|
||||
|
||||
@@ -569,7 +589,7 @@ do -- DETECTION_BASE
|
||||
|
||||
local HasDetectedObjects = false
|
||||
|
||||
if Detection:IsAlive() then
|
||||
if Detection and Detection:IsAlive() then
|
||||
|
||||
--self:T( { "DetectionGroup is Alive", DetectionGroup:GetName() } )
|
||||
|
||||
@@ -800,12 +820,17 @@ do -- DETECTION_BASE
|
||||
end
|
||||
|
||||
self:CreateDetectionItems() -- Polymorphic call to Create/Update the DetectionItems list for the DETECTION_ class grouping method.
|
||||
|
||||
for DetectedItemID, DetectedItem in pairs( self.DetectedItems ) do
|
||||
|
||||
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.
|
||||
|
||||
if DetectedItem then
|
||||
self:__DetectedItem( 0.1, DetectedItem )
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
@@ -817,7 +842,7 @@ do -- DETECTION_BASE
|
||||
|
||||
do -- DetectionItems Creation
|
||||
|
||||
-- Clean the DetectedItem table.
|
||||
--- Clean the DetectedItem table.
|
||||
-- @param #DETECTION_BASE self
|
||||
-- @return #DETECTION_BASE
|
||||
function DETECTION_BASE:CleanDetectionItem( DetectedItem, DetectedItemID )
|
||||
@@ -1234,7 +1259,7 @@ do -- DETECTION_BASE
|
||||
|
||||
--- Returns if there are friendlies nearby the FAC units ...
|
||||
-- @param #DETECTION_BASE self
|
||||
-- @param DetectedItem
|
||||
-- @param #DETECTION_BASE.DetectedItem DetectedItem
|
||||
-- @param DCS#Unit.Category Category The category of the unit.
|
||||
-- @return #boolean true if there are friendlies nearby
|
||||
function DETECTION_BASE:IsFriendliesNearBy( DetectedItem, Category )
|
||||
@@ -1244,7 +1269,7 @@ do -- DETECTION_BASE
|
||||
|
||||
--- Returns friendly units nearby the FAC units ...
|
||||
-- @param #DETECTION_BASE self
|
||||
-- @param DetectedItem
|
||||
-- @param #DETECTION_BASE.DetectedItem DetectedItem
|
||||
-- @param DCS#Unit.Category Category The category of the unit.
|
||||
-- @return #map<#string,Wrapper.Unit#UNIT> The map of Friendly UNITs.
|
||||
function DETECTION_BASE:GetFriendliesNearBy( DetectedItem, Category )
|
||||
@@ -1254,6 +1279,7 @@ do -- DETECTION_BASE
|
||||
|
||||
--- Returns if there are friendlies nearby the intercept ...
|
||||
-- @param #DETECTION_BASE self
|
||||
-- @param #DETECTION_BASE.DetectedItem DetectedItem
|
||||
-- @return #boolean trhe if there are friendlies near the intercept.
|
||||
function DETECTION_BASE:IsFriendliesNearIntercept( DetectedItem )
|
||||
|
||||
@@ -1262,6 +1288,7 @@ do -- DETECTION_BASE
|
||||
|
||||
--- Returns friendly units nearby the intercept point ...
|
||||
-- @param #DETECTION_BASE self
|
||||
-- @param #DETECTION_BASE.DetectedItem DetectedItem The detected item.
|
||||
-- @return #map<#string,Wrapper.Unit#UNIT> The map of Friendly UNITs.
|
||||
function DETECTION_BASE:GetFriendliesNearIntercept( DetectedItem )
|
||||
|
||||
@@ -1270,7 +1297,8 @@ do -- DETECTION_BASE
|
||||
|
||||
--- Returns the distance used to identify friendlies near the deteted item ...
|
||||
-- @param #DETECTION_BASE self
|
||||
-- @return #number The distance.
|
||||
-- @param #DETECTION_BASE.DetectedItem DetectedItem The detected item.
|
||||
-- @return #table A table of distances to friendlies.
|
||||
function DETECTION_BASE:GetFriendliesDistance( DetectedItem )
|
||||
|
||||
return DetectedItem.FriendliesDistance
|
||||
@@ -1278,6 +1306,7 @@ do -- DETECTION_BASE
|
||||
|
||||
--- Returns if there are friendlies nearby the FAC units ...
|
||||
-- @param #DETECTION_BASE self
|
||||
-- @param #DETECTION_BASE.DetectedItem DetectedItem
|
||||
-- @return #boolean trhe if there are friendlies nearby
|
||||
function DETECTION_BASE:IsPlayersNearBy( DetectedItem )
|
||||
|
||||
@@ -1286,6 +1315,7 @@ do -- DETECTION_BASE
|
||||
|
||||
--- Returns friendly units nearby the FAC units ...
|
||||
-- @param #DETECTION_BASE self
|
||||
-- @param #DETECTION_BASE.DetectedItem DetectedItem The detected item.
|
||||
-- @return #map<#string,Wrapper.Unit#UNIT> The map of Friendly UNITs.
|
||||
function DETECTION_BASE:GetPlayersNearBy( DetectedItem )
|
||||
|
||||
@@ -1294,10 +1324,11 @@ do -- DETECTION_BASE
|
||||
|
||||
--- Background worker function to determine if there are friendlies nearby ...
|
||||
-- @param #DETECTION_BASE self
|
||||
-- @param #table TargetData
|
||||
function DETECTION_BASE:ReportFriendliesNearBy( TargetData )
|
||||
--self:F( { "Search Friendlies", DetectedItem = TargetData.DetectedItem } )
|
||||
|
||||
local DetectedItem = TargetData.DetectedItem -- Functional.Detection#DETECTION_BASE.DetectedItem
|
||||
local DetectedItem = TargetData.DetectedItem --#DETECTION_BASE.DetectedItem
|
||||
local DetectedSet = TargetData.DetectedItem.Set
|
||||
local DetectedUnit = DetectedSet:GetFirst() -- Wrapper.Unit#UNIT
|
||||
|
||||
@@ -1519,13 +1550,13 @@ do -- DETECTION_BASE
|
||||
--- Adds a new DetectedItem to the DetectedItems list.
|
||||
-- The DetectedItem is a table and contains a SET_UNIT in the field Set.
|
||||
-- @param #DETECTION_BASE self
|
||||
-- @param ItemPrefix
|
||||
-- @param DetectedItemKey The key of the DetectedItem.
|
||||
-- @param #string ItemPrefix Prefix of detected item.
|
||||
-- @param #number DetectedItemKey The key of the DetectedItem. Default self.DetectedItemMax. Could also be a string in principle.
|
||||
-- @param Core.Set#SET_UNIT Set (optional) The Set of Units to be added.
|
||||
-- @return #DETECTION_BASE.DetectedItem
|
||||
function DETECTION_BASE:AddDetectedItem( ItemPrefix, DetectedItemKey, Set )
|
||||
|
||||
local DetectedItem = {}
|
||||
local DetectedItem = {} --#DETECTION_BASE.DetectedItem
|
||||
self.DetectedItemCount = self.DetectedItemCount + 1
|
||||
self.DetectedItemMax = self.DetectedItemMax + 1
|
||||
|
||||
@@ -1706,6 +1737,7 @@ do -- DETECTION_BASE
|
||||
|
||||
--- Checks if there is at least one UNIT detected in the Set of the the DetectedItem.
|
||||
-- @param #DETECTION_BASE self
|
||||
-- @param #DETECTION_BASE.DetectedItem DetectedItem
|
||||
-- @return #boolean true if at least one UNIT is detected from the DetectedSet, false if no UNIT was detected from the DetectedSet.
|
||||
function DETECTION_BASE:IsDetectedItemDetected( DetectedItem )
|
||||
|
||||
@@ -1832,8 +1864,7 @@ do -- DETECTION_BASE
|
||||
|
||||
--- Get a list of the detected item coordinates.
|
||||
-- @param #DETECTION_BASE self
|
||||
-- @param #DETECTION_BASE.DetectedItem DetectedItem The DetectedItem to set the coordinate at.
|
||||
-- @return Core.Point#COORDINATE
|
||||
-- @return #table A table of Core.Point#COORDINATE
|
||||
function DETECTION_BASE:GetDetectedItemCoordinates()
|
||||
|
||||
local Coordinates = {}
|
||||
@@ -1898,7 +1929,7 @@ do -- DETECTION_BASE
|
||||
|
||||
--- Get the Detection Set.
|
||||
-- @param #DETECTION_BASE self
|
||||
-- @return Core.Set#SET_BASE
|
||||
-- @return #DETECTION_BASE self
|
||||
function DETECTION_BASE:GetDetectionSet()
|
||||
|
||||
local DetectionSet = self.DetectionSet
|
||||
@@ -2033,7 +2064,8 @@ do -- DETECTION_UNITS
|
||||
function DETECTION_UNITS:CreateDetectionItems()
|
||||
-- Loop the current detected items, and check if each object still exists and is detected.
|
||||
|
||||
for DetectedItemKey, DetectedItem in pairs( self.DetectedItems ) do
|
||||
for DetectedItemKey, _DetectedItem in pairs( self.DetectedItems ) do
|
||||
local DetectedItem=_DetectedItem --#DETECTION_BASE.DetectedItem
|
||||
|
||||
local DetectedItemSet = DetectedItem.Set -- Core.Set#SET_UNIT
|
||||
|
||||
|
||||
@@ -40,7 +40,9 @@
|
||||
-- @field #table launchzones Table of launch zones.
|
||||
-- @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 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 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.
|
||||
@@ -136,8 +138,10 @@ FOX = {
|
||||
safezones = {},
|
||||
launchzones = {},
|
||||
protectedset = nil,
|
||||
explosionpower = 5,
|
||||
explosiondist = 100,
|
||||
explosionpower = 0.1,
|
||||
explosiondist = 200,
|
||||
explosiondist2 = 500,
|
||||
bigmissilemass = 50,
|
||||
destroy = nil,
|
||||
dt50 = 5,
|
||||
dt10 = 1,
|
||||
@@ -169,14 +173,19 @@ FOX = {
|
||||
-- @field Wrapper.Unit#UNIT weapon Missile weapon unit.
|
||||
-- @field #boolean active If true the missile is active.
|
||||
-- @field #string missileType Type of missile.
|
||||
-- @field #string missileName Name of missile.
|
||||
-- @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.Group#GROUP shooterGroup Group that shot the missile.
|
||||
-- @field #number shooterCoalition Coalition side of the shooter.
|
||||
-- @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 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.
|
||||
|
||||
--- Main radio menu on group level.
|
||||
@@ -189,7 +198,7 @@ FOX.MenuF10Root=nil
|
||||
|
||||
--- FOX class version.
|
||||
-- @field #string version
|
||||
FOX.version="0.5.1"
|
||||
FOX.version="0.6.0"
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- ToDo list
|
||||
@@ -217,6 +226,10 @@ function FOX:New()
|
||||
self:SetDefaultMissileDestruction(true)
|
||||
self:SetDefaultLaunchAlerts(true)
|
||||
self:SetDefaultLaunchMarks(true)
|
||||
|
||||
-- Explosion/destruction defaults.
|
||||
self:SetExplosionDistance()
|
||||
self:SetExplosionDistanceBigMissiles()
|
||||
self:SetExplosionPower()
|
||||
|
||||
-- Start State.
|
||||
@@ -358,12 +371,16 @@ function FOX:onafterStart(From, Event, To)
|
||||
self:HandleEvent(EVENTS.Birth)
|
||||
self:HandleEvent(EVENTS.Shot)
|
||||
|
||||
if self.Debug then
|
||||
self:HandleEvent(EVENTS.Hit)
|
||||
end
|
||||
|
||||
if self.Debug then
|
||||
self:TraceClass(self.ClassName)
|
||||
self:TraceLevel(2)
|
||||
end
|
||||
|
||||
self:__Status(-10)
|
||||
self:__Status(-20)
|
||||
end
|
||||
|
||||
--- On after Stop event. Stops the missile trainer and unhandles events.
|
||||
@@ -378,8 +395,12 @@ function FOX:onafterStop(From, Event, To)
|
||||
env.info(text)
|
||||
|
||||
-- Handle events:
|
||||
self:UnhandleEvent(EVENTS.Birth)
|
||||
self:UnhandleEvent(EVENTS.Shot)
|
||||
self:UnHandleEvent(EVENTS.Birth)
|
||||
self:UnHandleEvent(EVENTS.Shot)
|
||||
|
||||
if self.Debug then
|
||||
self:UnhandleEvent(EVENTS.Hit)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -433,28 +454,42 @@ function FOX:AddProtectedGroup(group)
|
||||
return self
|
||||
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 #number power Explosion power in kg TNT. Default 5.
|
||||
-- @param #number power Explosion power in kg TNT. Default 0.1 kg.
|
||||
-- @return #FOX self
|
||||
function FOX:SetExplosionPower(power)
|
||||
|
||||
self.explosionpower=power or 5
|
||||
self.explosionpower=power or 0.1
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set missile-player distance when missile is destroyed.
|
||||
-- @param #FOX self
|
||||
-- @param #number distance Distance in meters. Default 100 m.
|
||||
-- @param #number distance Distance in meters. Default 200 m.
|
||||
-- @return #FOX self
|
||||
function FOX:SetExplosionDistance(distance)
|
||||
|
||||
self.explosiondist=distance or 100
|
||||
self.explosiondist=distance or 200
|
||||
|
||||
return self
|
||||
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.
|
||||
-- @param #FOX self
|
||||
@@ -558,8 +593,11 @@ function FOX:onafterStatus(From, Event, To)
|
||||
-- Get FSM state.
|
||||
local fsmstate=self:GetState()
|
||||
|
||||
local time=timer.getAbsTime()
|
||||
local clock=UTILS.SecondsToClock(time)
|
||||
|
||||
-- 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.
|
||||
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)
|
||||
|
||||
end
|
||||
if #self.missiles==0 then
|
||||
text=text.." none"
|
||||
end
|
||||
self:I(self.lid..text)
|
||||
|
||||
-- Remove inactive missiles.
|
||||
@@ -722,7 +763,9 @@ end
|
||||
function FOX:onafterMissileLaunch(From, Event, To, missile)
|
||||
|
||||
-- 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.
|
||||
for _,_player in pairs(self.players) do
|
||||
@@ -803,6 +846,9 @@ function FOX:onafterMissileLaunch(From, Event, To, missile)
|
||||
-- Missile velocity in m/s.
|
||||
local missileVelocity=UTILS.VecNorm(_ordnance:getVelocity())
|
||||
|
||||
-- Update missile target if necessary.
|
||||
self:GetMissileTarget(missile)
|
||||
|
||||
if missile.targetUnit then
|
||||
|
||||
-----------------------------------
|
||||
@@ -825,7 +871,30 @@ function FOX:onafterMissileLaunch(From, Event, To, missile)
|
||||
|
||||
------------------------------------
|
||||
-- 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.
|
||||
local mindist=nil
|
||||
@@ -843,17 +912,57 @@ function FOX:onafterMissileLaunch(From, Event, To, missile)
|
||||
-- Distance.
|
||||
local dist=missileCoord:Get3DDistance(playerCoord)
|
||||
|
||||
-- Maxrange from launch point to player.
|
||||
local maxrange=playerCoord:Get3DDistance(missile.shotCoord)
|
||||
-- Distance from shooter to player.
|
||||
local Dshooter2player=playerCoord:Get3DDistance(missile.shotCoord)
|
||||
|
||||
-- Update mindist if necessary. Only include players in range of missile.
|
||||
if (mindist==nil or dist<mindist) and dist<=maxrange then
|
||||
-- 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=player.unit
|
||||
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
|
||||
|
||||
-- Check if missile has a valid target.
|
||||
@@ -865,39 +974,69 @@ function FOX:onafterMissileLaunch(From, Event, To, missile)
|
||||
-- Distance from missile to target.
|
||||
local distance=missileCoord:Get3DDistance(targetCoord)
|
||||
|
||||
local bearing=targetCoord:HeadingTo(missileCoord)
|
||||
local eta=distance/missileVelocity
|
||||
-- Distance missile to shooter.
|
||||
local distShooter=nil
|
||||
if missile.shooterUnit and missile.shooterUnit:IsAlive() then
|
||||
distShooter=missileCoord:Get3DDistance(missile.shooterUnit:GetCoordinate())
|
||||
end
|
||||
|
||||
self:T2(self.lid..string.format("Distance = %.1f m, v=%.1f m/s, bearing=%03d°, eta=%.1f sec", distance, missileVelocity, bearing, eta))
|
||||
|
||||
-- Debug output.
|
||||
if self.Debug then
|
||||
local bearing=targetCoord:HeadingTo(missileCoord)
|
||||
local eta=distance/missileVelocity
|
||||
|
||||
-- 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
|
||||
|
||||
-- Distroy missile if it's getting too close.
|
||||
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 100 m from target ==> destroy missile if in safe zone.
|
||||
if distance<=self.explosiondist and self:_CheckCoordSafe(targetCoord)then
|
||||
-- If missile is 150 m from target ==> destroy missile if in safe zone.
|
||||
if destroymissile and self:_CheckCoordSafe(targetCoord) then
|
||||
|
||||
-- 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()
|
||||
|
||||
-- Missile is not active any more.
|
||||
missile.active=false
|
||||
|
||||
-- Debug smoke.
|
||||
if self.Debug then
|
||||
missileCoord:SmokeRed()
|
||||
targetCoord:SmokeGreen()
|
||||
end
|
||||
|
||||
-- Create event.
|
||||
self:MissileDestroyed(missile)
|
||||
|
||||
-- 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)
|
||||
end
|
||||
|
||||
local text=string.format("Destroying missile. %s", self:_DeadText())
|
||||
MESSAGE:New(text, 10):ToGroup(target:GetGroup())
|
||||
|
||||
-- Increase dead counter.
|
||||
|
||||
-- Target was a player.
|
||||
if missile.targetPlayer then
|
||||
|
||||
-- Message to target.
|
||||
local text=string.format("Destroying missile. %s", self:_DeadText())
|
||||
MESSAGE:New(text, 10):ToGroup(target:GetGroup())
|
||||
|
||||
-- Increase dead counter.
|
||||
missile.targetPlayer.dead=missile.targetPlayer.dead+1
|
||||
end
|
||||
|
||||
-- Terminate timer.
|
||||
return nil
|
||||
|
||||
else
|
||||
|
||||
-- Time step.
|
||||
@@ -922,10 +1061,15 @@ function FOX:onafterMissileLaunch(From, Event, To, missile)
|
||||
-- Check again in dt seconds.
|
||||
return timer.getTime()+dt
|
||||
end
|
||||
|
||||
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.
|
||||
return nil
|
||||
--return nil
|
||||
end
|
||||
|
||||
else
|
||||
@@ -945,7 +1089,7 @@ function FOX:onafterMissileLaunch(From, Event, To, missile)
|
||||
MESSAGE:New(text, 10):ToClient(player.client)
|
||||
|
||||
-- Increase defeated counter.
|
||||
player.defeated=player.defeated+1
|
||||
player.defeated=player.defeated+1
|
||||
end
|
||||
|
||||
end
|
||||
@@ -1041,11 +1185,52 @@ function FOX:OnEventBirth(EventData)
|
||||
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).
|
||||
-- @param #FOX self
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
function FOX:OnEventShot(EventData)
|
||||
self:I({eventshot = EventData})
|
||||
self:T2({eventshot=EventData})
|
||||
|
||||
if EventData.Weapon==nil then
|
||||
return
|
||||
@@ -1062,7 +1247,7 @@ function FOX:OnEventShot(EventData)
|
||||
|
||||
-- Weapon descriptor.
|
||||
local desc=EventData.Weapon:getDesc()
|
||||
self:E({desc=desc})
|
||||
self:T2({desc=desc})
|
||||
|
||||
-- Weapon category: 0=Shell, 1=Missile, 2=Rocket, 3=BOMB
|
||||
local weaponcategory=desc.category
|
||||
@@ -1091,15 +1276,6 @@ function FOX:OnEventShot(EventData)
|
||||
return
|
||||
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
|
||||
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.shotTime=timer.getAbsTime()
|
||||
missile.shotCoord=EventData.IniUnit:GetCoordinate()
|
||||
missile.targetUnit=_targetUnit
|
||||
missile.targetPlayer=self:_GetPlayerFromUnit(missile.targetUnit)
|
||||
missile.fuseDist=desc.fuseDist
|
||||
missile.explosive=desc.warhead.explosiveMass or desc.warhead.shapedExplosiveMass
|
||||
missile.targetOrig=missile.targetName
|
||||
|
||||
-- Only track if target was a player or target is protected.
|
||||
if missile.targetPlayer or self:_IsProtected(missile.targetUnit) then
|
||||
-- Set missile target name, unit and player.
|
||||
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.
|
||||
table.insert(self.missiles, missile)
|
||||
@@ -1137,6 +1320,36 @@ function FOX:OnEventShot(EventData)
|
||||
|
||||
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
|
||||
|
||||
@@ -57,8 +57,10 @@ SEAD = {
|
||||
-- -- 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 )
|
||||
|
||||
local self = BASE:Inherit( self, BASE:New() )
|
||||
self:F( SEADGroupPrefixes )
|
||||
self:F( SEADGroupPrefixes )
|
||||
|
||||
if type( SEADGroupPrefixes ) == 'table' then
|
||||
for SEADGroupPrefixID, SEADGroupPrefix in pairs( SEADGroupPrefixes ) do
|
||||
self.SEADGroupPrefixes[SEADGroupPrefix] = SEADGroupPrefix
|
||||
@@ -85,7 +87,29 @@ function SEAD:OnEventShot( EventData )
|
||||
local SEADWeaponName = EventData.WeaponName -- return weapon type
|
||||
-- Start of the 2nd loop
|
||||
self:T( "Missile Launched = " .. SEADWeaponName )
|
||||
if SEADWeaponName == "KH-58" or SEADWeaponName == "KH-25MPU" or SEADWeaponName == "AGM-88" or SEADWeaponName == "KH-31A" or SEADWeaponName == "KH-31P" then -- Check if the missile is a SEAD
|
||||
|
||||
--if SEADWeaponName == "KH-58" or SEADWeaponName == "KH-25MPU" or SEADWeaponName == "AGM-88" or SEADWeaponName == "KH-31A" or SEADWeaponName == "KH-31P" then -- Check if the missile is a SEAD
|
||||
if SEADWeaponName == "weapons.missiles.X_58" --Kh-58U anti-radiation missiles fired
|
||||
or
|
||||
SEADWeaponName == "weapons.missiles.Kh25MP_PRGS1VP" --Kh-25MP anti-radiation missiles fired
|
||||
or
|
||||
SEADWeaponName == "weapons.missiles.X_25MP" --Kh-25MPU anti-radiation missiles fired
|
||||
or
|
||||
SEADWeaponName == "weapons.missiles.X_28" --Kh-28 anti-radiation missiles fired
|
||||
or
|
||||
SEADWeaponName == "weapons.missiles.X_31P" --Kh-31P anti-radiation missiles fired
|
||||
or
|
||||
SEADWeaponName == "weapons.missiles.AGM_45A" --AGM-45A anti-radiation missiles fired
|
||||
or
|
||||
SEADWeaponName == "weapons.missiles.AGM_45" --AGM-45B anti-radiation missiles fired
|
||||
or
|
||||
SEADWeaponName == "weapons.missiles.AGM_88" --AGM-88C anti-radiation missiles fired
|
||||
or
|
||||
SEADWeaponName == "weapons.missiles.AGM_122" --AGM-122 Sidearm anti-radiation missiles fired
|
||||
or
|
||||
SEADWeaponName == "weapons.missiles.ALARM" --ALARM anti-radiation missiles fired
|
||||
then
|
||||
|
||||
local _evade = math.random (1,100) -- random number for chance of evading action
|
||||
local _targetMim = EventData.Weapon:getTarget() -- Identify target
|
||||
local _targetMimname = Unit.getName(_targetMim)
|
||||
@@ -111,47 +135,62 @@ function SEAD:OnEventShot( EventData )
|
||||
self:T( _targetskill )
|
||||
if self.TargetSkill[_targetskill] then
|
||||
if (_evade > self.TargetSkill[_targetskill].Evade) then
|
||||
|
||||
self:T( string.format("Evading, target skill " ..string.format(_targetskill)) )
|
||||
|
||||
local _targetMim = Weapon.getTarget(SEADWeapon)
|
||||
local _targetMimname = Unit.getName(_targetMim)
|
||||
local _targetMimgroup = Unit.getGroup(Weapon.getTarget(SEADWeapon))
|
||||
local _targetMimcont= _targetMimgroup:getController()
|
||||
|
||||
routines.groupRandomDistSelf(_targetMimgroup,300,'Diamond',250,20) -- move randomly
|
||||
|
||||
local SuppressedGroups1 = {} -- unit suppressed radar off for a random time
|
||||
|
||||
local function SuppressionEnd1(id)
|
||||
id.ctrl:setOption(AI.Option.Ground.id.ALARM_STATE,AI.Option.Ground.val.ALARM_STATE.GREEN)
|
||||
SuppressedGroups1[id.groupName] = nil
|
||||
end
|
||||
|
||||
local id = {
|
||||
groupName = _targetMimgroup,
|
||||
ctrl = _targetMimcont
|
||||
}
|
||||
|
||||
local delay1 = math.random(self.TargetSkill[_targetskill].DelayOff[1], self.TargetSkill[_targetskill].DelayOff[2])
|
||||
|
||||
if SuppressedGroups1[id.groupName] == nil then
|
||||
|
||||
SuppressedGroups1[id.groupName] = {
|
||||
SuppressionEndTime1 = timer.getTime() + delay1,
|
||||
SuppressionEndN1 = SuppressionEndCounter1 --Store instance of SuppressionEnd() scheduled function
|
||||
}
|
||||
}
|
||||
|
||||
Controller.setOption(_targetMimcont, AI.Option.Ground.id.ALARM_STATE,AI.Option.Ground.val.ALARM_STATE.GREEN)
|
||||
timer.scheduleFunction(SuppressionEnd1, id, SuppressedGroups1[id.groupName].SuppressionEndTime1) --Schedule the SuppressionEnd() function
|
||||
--trigger.action.outText( string.format("Radar Off " ..string.format(delay1)), 20)
|
||||
end
|
||||
|
||||
local SuppressedGroups = {}
|
||||
|
||||
local function SuppressionEnd(id)
|
||||
id.ctrl:setOption(AI.Option.Ground.id.ALARM_STATE,AI.Option.Ground.val.ALARM_STATE.RED)
|
||||
SuppressedGroups[id.groupName] = nil
|
||||
end
|
||||
|
||||
local id = {
|
||||
groupName = _targetMimgroup,
|
||||
ctrl = _targetMimcont
|
||||
}
|
||||
|
||||
local delay = math.random(self.TargetSkill[_targetskill].DelayOn[1], self.TargetSkill[_targetskill].DelayOn[2])
|
||||
|
||||
if SuppressedGroups[id.groupName] == nil then
|
||||
SuppressedGroups[id.groupName] = {
|
||||
SuppressionEndTime = timer.getTime() + delay,
|
||||
SuppressionEndN = SuppressionEndCounter --Store instance of SuppressionEnd() scheduled function
|
||||
}
|
||||
|
||||
timer.scheduleFunction(SuppressionEnd, id, SuppressedGroups[id.groupName].SuppressionEndTime) --Schedule the SuppressionEnd() function
|
||||
--trigger.action.outText( string.format("Radar On " ..string.format(delay)), 20)
|
||||
end
|
||||
|
||||
@@ -77,6 +77,8 @@
|
||||
-- @field #boolean safeparking If true, parking spots for aircraft are considered as occupied if e.g. a client aircraft is parked there. Default false.
|
||||
-- @field #boolean isunit If true, warehouse is represented by a unit instead of a static.
|
||||
-- @field #number lowfuelthresh Low fuel threshold. Triggers the event AssetLowFuel if for any unit fuel goes below this number.
|
||||
-- @field #boolean respawnafterdestroyed If true, warehouse is respawned after it was destroyed. Assets are kept.
|
||||
-- @field #number respawndelay Delay before respawn in seconds.
|
||||
-- @extends Core.Fsm#FSM
|
||||
|
||||
--- Have your assets at the right place at the right time - or not!
|
||||
@@ -1569,6 +1571,8 @@ WAREHOUSE = {
|
||||
saveparking = false,
|
||||
isunit = false,
|
||||
lowfuelthresh = 0.15,
|
||||
respawnafterdestroyed=false,
|
||||
respawndelay = nil,
|
||||
}
|
||||
|
||||
--- Item of the warehouse stock table.
|
||||
@@ -1747,7 +1751,7 @@ _WAREHOUSEDB = {
|
||||
|
||||
--- Warehouse class version.
|
||||
-- @field #string version
|
||||
WAREHOUSE.version="0.9.4"
|
||||
WAREHOUSE.version="0.9.5"
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- TODO: Warehouse todo list.
|
||||
@@ -1910,6 +1914,7 @@ function WAREHOUSE:New(warehouse, alias)
|
||||
self:AddTransition("*", "AirbaseRecaptured", "*") -- Airbase was re-captured from other coalition.
|
||||
self:AddTransition("*", "AssetDead", "*") -- An asset group died.
|
||||
self:AddTransition("*", "Destroyed", "Destroyed") -- Warehouse was destroyed. All assets in stock are gone and warehouse is stopped.
|
||||
self:AddTransition("Destroyed", "Respawn", "Running") -- Respawn warehouse after it was destroyed.
|
||||
|
||||
------------------------
|
||||
--- Pseudo Functions ---
|
||||
@@ -1942,6 +1947,22 @@ function WAREHOUSE:New(warehouse, alias)
|
||||
-- @param #WAREHOUSE self
|
||||
-- @param #number delay Delay in seconds.
|
||||
|
||||
--- Triggers the FSM event "Respawn".
|
||||
-- @function [parent=#WAREHOUSE] Respawn
|
||||
-- @param #WAREHOUSE self
|
||||
|
||||
--- Triggers the FSM event "Respawn" after a delay.
|
||||
-- @function [parent=#WAREHOUSE] __Respawn
|
||||
-- @param #WAREHOUSE self
|
||||
-- @param #number delay Delay in seconds.
|
||||
|
||||
--- On after "Respawn" event user function.
|
||||
-- @function [parent=#WAREHOUSE] OnAfterRespawn
|
||||
-- @param #WAREHOUSE self
|
||||
-- @param #string From From state.
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
|
||||
--- Triggers the FSM event "Pause". Pauses the warehouse. Assets can still be added and requests be made. However, requests are not processed.
|
||||
-- @function [parent=#WAREHOUSE] Pause
|
||||
-- @param #WAREHOUSE self
|
||||
@@ -2535,6 +2556,15 @@ function WAREHOUSE:SetSaveOnMissionEnd(path, filename)
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set respawn after destroy.
|
||||
-- @param #WAREHOUSE self
|
||||
-- @return #WAREHOUSE self
|
||||
function WAREHOUSE:SetRespawnAfterDestroyed(delay)
|
||||
self.respawnafterdestroyed=true
|
||||
self.respawndelay=delay
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Set the airbase belonging to this warehouse.
|
||||
-- Note that it has to be of the same coalition as the warehouse.
|
||||
@@ -3964,7 +3994,7 @@ function WAREHOUSE:onbeforeAddRequest(From, Event, To, warehouse, AssetDescripto
|
||||
end
|
||||
|
||||
-- Warehouse is destroyed?
|
||||
if self:IsDestroyed() then
|
||||
if self:IsDestroyed() and not self.respawnafterdestroyed then
|
||||
self:_ErrorMessage("ERROR: Invalid request. Warehouse is destroyed!", 0)
|
||||
okay=false
|
||||
end
|
||||
@@ -4348,9 +4378,9 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request)
|
||||
table.insert(_transportassets,_assetitem)
|
||||
|
||||
-- Spawned into the world.
|
||||
_assetitem.spawned=true
|
||||
_assetitem.spawned=true
|
||||
_assetitem.spawngroupname=spawngroup:GetName()
|
||||
|
||||
|
||||
-- Asset spawned FSM function.
|
||||
self:__AssetSpawned(1, spawngroup, _assetitem, Request)
|
||||
end
|
||||
@@ -4835,6 +4865,21 @@ function WAREHOUSE:onbeforeChangeCountry(From, Event, To, Country)
|
||||
return false
|
||||
end
|
||||
|
||||
--- Respawn warehouse.
|
||||
-- @param #WAREHOUSE self
|
||||
-- @param #string From From state.
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
function WAREHOUSE:onafterRespawn(From, Event, To)
|
||||
|
||||
-- Info message.
|
||||
local text=string.format("Respawning warehouse %s.", self.alias)
|
||||
self:_InfoMessage(text)
|
||||
|
||||
-- Respawn warehouse.
|
||||
self.warehouse:ReSpawn()
|
||||
|
||||
end
|
||||
|
||||
--- On after "ChangeCountry" event. Warehouse is respawned with the specified country. All queued requests are deleted and the owned airbase is reset if the coalition is changed by changing the
|
||||
-- country.
|
||||
@@ -4949,7 +4994,7 @@ function WAREHOUSE:onafterAirbaseRecaptured(From, Event, To, Coalition)
|
||||
end
|
||||
|
||||
|
||||
--- On after "AssetDead" event triggerd when an asset group died.
|
||||
--- On after "AssetDead" event triggered when an asset group died.
|
||||
-- @param #WAREHOUSE self
|
||||
-- @param #string From From state.
|
||||
-- @param #string Event Event.
|
||||
@@ -4971,22 +5016,34 @@ end
|
||||
function WAREHOUSE:onafterDestroyed(From, Event, To)
|
||||
|
||||
-- Message.
|
||||
local text=string.format("Warehouse %s was destroyed! Assets lost %d.", self.alias, #self.stock)
|
||||
local text=string.format("Warehouse %s was destroyed! Assets lost %d. Respawn=%s", self.alias, #self.stock, tostring(self.respawnafterdestroyed))
|
||||
self:_InfoMessage(text)
|
||||
|
||||
-- Remove all table entries from waiting queue and stock.
|
||||
for k,_ in pairs(self.queue) do
|
||||
self.queue[k]=nil
|
||||
end
|
||||
for k,_ in pairs(self.stock) do
|
||||
self.stock[k]=nil
|
||||
end
|
||||
if self.respawnafterdestroyed then
|
||||
|
||||
--self.queue=nil
|
||||
--self.queue={}
|
||||
if self.respawndelay then
|
||||
self:Pause()
|
||||
self:__Respawn(self.respawndelay)
|
||||
else
|
||||
self:Respawn()
|
||||
end
|
||||
|
||||
--self.stock=nil
|
||||
--self.stock={}
|
||||
else
|
||||
|
||||
-- Remove all table entries from waiting queue and stock.
|
||||
for k,_ in pairs(self.queue) do
|
||||
self.queue[k]=nil
|
||||
end
|
||||
for k,_ in pairs(self.stock) do
|
||||
self.stock[k]=nil
|
||||
end
|
||||
|
||||
--self.queue=nil
|
||||
--self.queue={}
|
||||
|
||||
--self.stock=nil
|
||||
--self.stock={}
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -5289,7 +5346,7 @@ function WAREHOUSE:_SpawnAssetRequest(Request)
|
||||
if _group then
|
||||
_groupset:AddGroup(_group)
|
||||
table.insert(_assets, _assetitem)
|
||||
|
||||
|
||||
-- Spawned into the world.
|
||||
_assetitem.spawned=true
|
||||
_assetitem.spawngroupname=_group:GetName()
|
||||
@@ -6355,7 +6412,7 @@ function WAREHOUSE:_CheckRequestConsistancy(queue)
|
||||
end
|
||||
|
||||
-- Is receiving warehouse destroyed?
|
||||
if request.warehouse:IsDestroyed() then
|
||||
if request.warehouse:IsDestroyed() and not self.respawnafterdestroyed then
|
||||
self:E(self.wid..string.format("ERROR: INVALID request. Requesting warehouse is destroyed!"))
|
||||
valid=false
|
||||
end
|
||||
@@ -7779,7 +7836,7 @@ function WAREHOUSE:_CheckFuel()
|
||||
local group=_group --Wrapper.Group#GROUP
|
||||
|
||||
if group and group:IsAlive() then
|
||||
|
||||
|
||||
-- Get min fuel of group.
|
||||
local fuel=group:GetFuelMin()
|
||||
|
||||
@@ -7788,11 +7845,11 @@ function WAREHOUSE:_CheckFuel()
|
||||
|
||||
-- Check if fuel is below threshold for first time.
|
||||
if fuel<self.lowfuelthresh and not qitem.lowfuel then
|
||||
|
||||
|
||||
-- Set low fuel flag.
|
||||
self:I(self.wid..string.format("Transport group %s is low on fuel! Min fuel state = %.2f", group:GetName(), fuel))
|
||||
qitem.lowfuel=true
|
||||
|
||||
|
||||
-- Trigger low fuel event.
|
||||
local asset=self:FindAssetInDB(group)
|
||||
self:AssetLowFuel(asset, qitem)
|
||||
@@ -7817,11 +7874,11 @@ function WAREHOUSE:_CheckFuel()
|
||||
|
||||
-- Check if fuel is below threshold for first time.
|
||||
if fuel<self.lowfuelthresh and not qitem.lowfuel then
|
||||
|
||||
|
||||
-- Set low fuel flag.
|
||||
self:I(self.wid..string.format("Cargo group %s is low on fuel! Min fuel state = %.2f", group:GetName(), fuel))
|
||||
qitem.lowfuel=true
|
||||
|
||||
|
||||
-- Trigger low fuel event.
|
||||
local asset=self:FindAssetInDB(group)
|
||||
self:AssetLowFuel(asset, qitem)
|
||||
|
||||
@@ -93,6 +93,8 @@ __Moose.Include( 'Scripts/Moose/AI/AI_Bai.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/AI/AI_Formation.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/AI/AI_Escort.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/AI/AI_Escort_Request.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/AI/AI_Escort_Dispatcher.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/AI/AI_Escort_Dispatcher_Request.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/AI/AI_Cargo.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/AI/AI_Cargo_APC.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/AI/AI_Cargo_Helicopter.lua' )
|
||||
|
||||
+3277
-3169
File diff suppressed because it is too large
Load Diff
@@ -60,6 +60,8 @@
|
||||
-- @field #number callsignname Number for the callsign name.
|
||||
-- @field #number callsignnumber Number of the callsign name.
|
||||
-- @field #string modex Tail number of the tanker.
|
||||
-- @field #boolean eplrs If true, enable data link, e.g. if used as AWACS.
|
||||
-- @field #boolean recovery If true, tanker will recover using the AIRBOSS marshal pattern.
|
||||
-- @extends Core.Fsm#FSM
|
||||
|
||||
--- Recovery Tanker.
|
||||
@@ -295,15 +297,16 @@ RECOVERYTANKER = {
|
||||
callsignnumber = nil,
|
||||
modex = nil,
|
||||
eplrs = nil,
|
||||
recovery = nil,
|
||||
}
|
||||
|
||||
--- Unique ID (global).
|
||||
-- @field #number UID Unique ID (global).
|
||||
RECOVERYTANKER.UID=0
|
||||
_RECOVERYTANKERID=0
|
||||
|
||||
--- Class version.
|
||||
-- @field #string version
|
||||
RECOVERYTANKER.version="1.0.7"
|
||||
RECOVERYTANKER.version="1.0.8"
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- TODO list
|
||||
@@ -349,16 +352,16 @@ function RECOVERYTANKER:New(carrierunit, tankergroupname)
|
||||
self.tankergroupname=tankergroupname
|
||||
|
||||
-- Increase unique ID.
|
||||
RECOVERYTANKER.UID=RECOVERYTANKER.UID+1
|
||||
_RECOVERYTANKERID=_RECOVERYTANKERID+1
|
||||
|
||||
-- Unique ID of this tanker.
|
||||
self.uid=RECOVERYTANKER.UID
|
||||
self.uid=_RECOVERYTANKERID
|
||||
|
||||
-- Save self in static object. Easier to retrieve later.
|
||||
self.carrier:SetState(self.carrier, string.format("RECOVERYTANKER_%d", self.uid) , self)
|
||||
|
||||
-- Set unique spawn alias.
|
||||
self.alias=string.format("%s_%s_%02d", self.carrier:GetName(), self.tankergroupname, RECOVERYTANKER.UID)
|
||||
self.alias=string.format("%s_%s_%02d", self.carrier:GetName(), self.tankergroupname, _RECOVERYTANKERID)
|
||||
|
||||
-- Log ID.
|
||||
self.lid=string.format("RECOVERYTANKER %s | ", self.alias)
|
||||
@@ -377,6 +380,7 @@ function RECOVERYTANKER:New(carrierunit, tankergroupname)
|
||||
self:SetPatternUpdateHeading()
|
||||
self:SetPatternUpdateInterval()
|
||||
self:SetAWACS(false)
|
||||
self:SetRecoveryAirboss(false)
|
||||
|
||||
-- Debug trace.
|
||||
if false then
|
||||
@@ -399,6 +403,7 @@ function RECOVERYTANKER:New(carrierunit, tankergroupname)
|
||||
self:AddTransition("*", "RefuelStop", "Running") -- Tanker starts to refuel.
|
||||
self:AddTransition("*", "Run", "Running") -- Tanker starts normal operation again.
|
||||
self:AddTransition("Running", "RTB", "Returning") -- Tanker is returning to base (for fuel).
|
||||
self:AddTransition("Returning", "Returned", "Returned") -- Tanker has returned to its airbase (i.e. landed).
|
||||
self:AddTransition("*", "Status", "*") -- Status update.
|
||||
self:AddTransition("Running", "PatternUpdate", "*") -- Update pattern wrt to carrier.
|
||||
self:AddTransition("*", "Stop", "Stopped") -- Stop the FSM.
|
||||
@@ -472,6 +477,26 @@ function RECOVERYTANKER:New(carrierunit, tankergroupname)
|
||||
-- @param Wrapper.Airbase#AIRBASE airbase The airbase where the tanker should return to.
|
||||
|
||||
|
||||
--- Triggers the FSM event "Returned" after the tanker has landed.
|
||||
-- @function [parent=#RECOVERYTANKER] Returned
|
||||
-- @param #RECOVERYTANKER self
|
||||
-- @param Wrapper.Airbase#AIRBASE airbase The airbase the tanker has landed.
|
||||
|
||||
--- Triggers the delayed FSM event "Returned" after the tanker has landed.
|
||||
-- @function [parent=#RECOVERYTANKER] __Returned
|
||||
-- @param #RECOVERYTANKER self
|
||||
-- @param #number delay Delay in seconds.
|
||||
-- @param Wrapper.Airbase#AIRBASE airbase The airbase the tanker has landed.
|
||||
|
||||
--- On after "Returned" event user function. Called when a the the tanker has landed at an airbase.
|
||||
-- @function [parent=#RECOVERYTANKER] OnAfterReturned
|
||||
-- @param #RECOVERYTANKER self
|
||||
-- @param #string From From state.
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
-- @param Wrapper.Airbase#AIRBASE airbase The airbase the tanker has landed.
|
||||
|
||||
|
||||
--- Triggers the FSM event "Status" that updates the tanker status.
|
||||
-- @function [parent=#RECOVERYTANKER] Status
|
||||
-- @param #RECOVERYTANKER self
|
||||
@@ -596,6 +621,19 @@ function RECOVERYTANKER:SetHomeBase(airbase)
|
||||
return self
|
||||
end
|
||||
|
||||
--- Activate recovery by the AIRBOSS class. Tanker will get a Marshal stack and perform a CASE I, II or III recovery when RTB.
|
||||
-- @param #RECOVERYTANKER self
|
||||
-- @param #boolean switch If true or nil, recovery is done by AIRBOSS.
|
||||
-- @return #RECOVERYTANKER self
|
||||
function RECOVERYTANKER:SetRecoveryAirboss(switch)
|
||||
if switch==true or switch==nil then
|
||||
self.recovery=true
|
||||
else
|
||||
self.recovery=false
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set that the group takes the roll of an AWACS instead of a refueling tanker.
|
||||
-- @param #RECOVERYTANKER self
|
||||
-- @param #boolean switch If true or nil, set roll AWACS.
|
||||
@@ -775,6 +813,13 @@ function RECOVERYTANKER:IsReturning()
|
||||
return self:is("Returning")
|
||||
end
|
||||
|
||||
--- Check if tanker has returned to base.
|
||||
-- @param #RECOVERYTANKER self
|
||||
-- @return #boolean If true, tanker has returned to base.
|
||||
function RECOVERYTANKER:IsReturned()
|
||||
return self:is("Returned")
|
||||
end
|
||||
|
||||
--- Check if tanker is currently operating.
|
||||
-- @param #RECOVERYTANKER self
|
||||
-- @return #boolean If true, tanker is operating.
|
||||
@@ -830,6 +875,7 @@ function RECOVERYTANKER:onafterStart(From, Event, To)
|
||||
|
||||
-- Handle events.
|
||||
self:HandleEvent(EVENTS.EngineShutdown)
|
||||
self:HandleEvent(EVENTS.Land)
|
||||
self:HandleEvent(EVENTS.Refueling, self._RefuelingStart) --Need explicit functions since OnEventRefueling and OnEventRefuelingStop did not hook!
|
||||
self:HandleEvent(EVENTS.RefuelingStop, self._RefuelingStop)
|
||||
self:HandleEvent(EVENTS.Crash, self._OnEventCrashOrDead)
|
||||
@@ -865,7 +911,7 @@ function RECOVERYTANKER:onafterStart(From, Event, To)
|
||||
else
|
||||
|
||||
-- Check if an uncontrolled tanker group was requested.
|
||||
if self.useuncontrolled then
|
||||
if self.uncontrolledac then
|
||||
|
||||
-- Use an uncontrolled aircraft group.
|
||||
self.tanker=GROUP:FindByName(self.tankergroupname)
|
||||
@@ -884,14 +930,14 @@ function RECOVERYTANKER:onafterStart(From, Event, To)
|
||||
else
|
||||
|
||||
-- Spawn tanker at airbase.
|
||||
self.tanker=Spawn:SpawnAtAirbase(self.airbase, self.takeoff)
|
||||
self.tanker=Spawn:SpawnAtAirbase(self.airbase, self.takeoff, nil, AIRBASE.TerminalType.OpenMedOrBig)
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
-- Initialize route. self.distStern<0!
|
||||
SCHEDULER:New(nil, self._InitRoute, {self, -self.distStern+UTILS.NMToMeters(3)}, 1)
|
||||
self:ScheduleOnce(1, self._InitRoute, self, -self.distStern+UTILS.NMToMeters(3))
|
||||
|
||||
-- Create tanker beacon.
|
||||
if self.TACANon then
|
||||
@@ -929,7 +975,7 @@ function RECOVERYTANKER:onafterStatus(From, Event, To)
|
||||
-- Get current time.
|
||||
local time=timer.getTime()
|
||||
|
||||
if self.tanker:IsAlive() then
|
||||
if self.tanker and self.tanker:IsAlive() then
|
||||
|
||||
---------------------
|
||||
-- TANKER is ALIVE --
|
||||
@@ -937,8 +983,14 @@ function RECOVERYTANKER:onafterStatus(From, Event, To)
|
||||
|
||||
-- Get fuel of tanker.
|
||||
local fuel=self.tanker:GetFuel()*100
|
||||
local text=string.format("Recovery tanker %s: state=%s fuel=%.1f", self.tanker:GetName(), self:GetState(), fuel)
|
||||
local life=self.tanker:GetUnit(1):GetLife()
|
||||
local life0=self.tanker:GetUnit(1):GetLife0()
|
||||
local lifeR=self.tanker:GetUnit(1):GetLifeRelative()
|
||||
|
||||
-- Report fuel and life.
|
||||
local text=string.format("Recovery tanker %s: state=%s fuel=%.1f, life=%.1f/%.1f=%d", self.tanker:GetName(), self:GetState(), fuel, life, life0, lifeR*100)
|
||||
self:T(self.lid..text)
|
||||
MESSAGE:New(text, 10):ToAllIf(self.Debug)
|
||||
|
||||
-- Check if tanker is running and not RTBing or refueling.
|
||||
if self:IsRunning() then
|
||||
@@ -947,7 +999,7 @@ function RECOVERYTANKER:onafterStatus(From, Event, To)
|
||||
if fuel<self.lowfuel then
|
||||
|
||||
-- Check if spawn in air is activated.
|
||||
if self.takeoff==SPAWN.Takeoff.Air or self.respawninair then
|
||||
if (self.takeoff==SPAWN.Takeoff.Air or self.respawninair) and not self.recovery then
|
||||
|
||||
-- Check that respawn should happen.
|
||||
if self.respawn then
|
||||
@@ -1013,6 +1065,16 @@ function RECOVERYTANKER:onafterStatus(From, Event, To)
|
||||
end
|
||||
end
|
||||
|
||||
elseif self:IsReturning() then
|
||||
|
||||
-- Tanker is returning to its base.
|
||||
self:T2(self.lid.."Tanker is returning.")
|
||||
|
||||
elseif self:IsReturned() then
|
||||
|
||||
-- Tanker landed. Waiting for engine shutdown...
|
||||
self:T2(self.lid.."Tanker returned. waiting for engine shutdown.")
|
||||
|
||||
end
|
||||
|
||||
-- Call status again in 30 seconds.
|
||||
@@ -1028,6 +1090,8 @@ function RECOVERYTANKER:onafterStatus(From, Event, To)
|
||||
|
||||
if not self:IsStopped() then
|
||||
|
||||
self:E(self.lid.."Recovery tanker is NOT alive (and not stopped)!")
|
||||
|
||||
-- Stop FSM.
|
||||
self:Stop()
|
||||
|
||||
@@ -1037,6 +1101,7 @@ function RECOVERYTANKER:onafterStatus(From, Event, To)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
@@ -1133,7 +1198,14 @@ function RECOVERYTANKER:onafterRTB(From, Event, To, airbase)
|
||||
|
||||
-- Set landing waypoint.
|
||||
wp[1]=self.tanker:GetCoordinate():WaypointAirTurningPoint(nil, speed, {}, "Current Position")
|
||||
wp[2]=airbase:GetCoordinate():SetAltitude(500):WaypointAirLanding(speed, airbase, nil, "Land at airbase")
|
||||
|
||||
|
||||
if self.recovery then
|
||||
-- Fly a bit until the airboss takes over.
|
||||
wp[2]=self.tanker:GetCoordinate():Translate(UTILS.NMToMeters(10), self.tanker:GetHeading(), true):WaypointAirTurningPoint(nil, speed, {}, "WP")
|
||||
else
|
||||
wp[2]=airbase:GetCoordinate():SetAltitude(500):WaypointAirLanding(speed, airbase, nil, "Land at airbase")
|
||||
end
|
||||
|
||||
-- Initialize WP and route tanker.
|
||||
self.tanker:WayPointInitialize(wp)
|
||||
@@ -1142,6 +1214,25 @@ function RECOVERYTANKER:onafterRTB(From, Event, To, airbase)
|
||||
self.tanker:Route(wp, 1)
|
||||
end
|
||||
|
||||
|
||||
--- On after Returned event. The tanker has landed.
|
||||
-- @param #RECOVERYTANKER self
|
||||
-- @param #string From From state.
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
-- @param Wrapper.Airbase#AIRBASE airbase The base at which the tanker landed.
|
||||
function RECOVERYTANKER:onafterReturned(From, Event, To, airbase)
|
||||
|
||||
if airbase then
|
||||
local airbasename=airbase:GetName()
|
||||
self:I(self.lid..string.format("Tanker returned to airbase %s", tostring(airbasename)))
|
||||
else
|
||||
self:E(self.lid..string.format("WARNING: Tanker landed but airbase (EventData.Place) is nil!"))
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
--- On after Stop event. Unhandle events and stop status updates.
|
||||
-- @param #RECOVERYTANKER self
|
||||
-- @param #string From From state.
|
||||
@@ -1150,14 +1241,18 @@ end
|
||||
function RECOVERYTANKER:onafterStop(From, Event, To)
|
||||
|
||||
-- Unhandle events.
|
||||
self:UnHandleEvent(EVENTS.Land)
|
||||
self:UnHandleEvent(EVENTS.EngineShutdown)
|
||||
self:UnHandleEvent(EVENTS.Refueling)
|
||||
self:UnHandleEvent(EVENTS.RefuelingStop)
|
||||
self:UnHandleEvent(EVENTS.Dead)
|
||||
self:UnHandleEvent(EVENTS.Crash)
|
||||
|
||||
-- Clear all pending FSM events.
|
||||
self.CallScheduler:Clear()
|
||||
|
||||
-- If tanker is alive, despawn it.
|
||||
if self.helo and self.helo:IsAlive() then
|
||||
if self.tanker and self.tanker:IsAlive() then
|
||||
self:I(self.lid.."Stopping FSM and despawning tanker.")
|
||||
self.tanker:Destroy()
|
||||
else
|
||||
@@ -1170,6 +1265,42 @@ end
|
||||
-- EVENT functions
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
--- Event handler for landing of recovery tanker.
|
||||
-- @param #RECOVERYTANKER self
|
||||
-- @param Core.Event#EVENTDATA EventData Event data.
|
||||
function RECOVERYTANKER:OnEventLand(EventData)
|
||||
|
||||
-- Group that shut down the engine.
|
||||
local group=EventData.IniGroup --Wrapper.Group#GROUP
|
||||
|
||||
-- Check if group is alive.
|
||||
if group and group:IsAlive() then
|
||||
|
||||
-- Group name. When spawning it will have #001 attached.
|
||||
local groupname=group:GetName()
|
||||
|
||||
-- Check that we have the right group and that it should be respawned.
|
||||
if groupname==self.tanker:GetName() then
|
||||
|
||||
local airbase=nil --Wrapper.Airbase#AIRBASE
|
||||
local airbasename="unknown"
|
||||
if EventData.Place then
|
||||
airbase=EventData.Place
|
||||
airbasename=airbase:GetName()
|
||||
end
|
||||
|
||||
-- Debug info.
|
||||
local text=string.format("Recovery tanker group %s landed at airbase %s.", group:GetName(), airbasename)
|
||||
MESSAGE:New(text, 10, "DEBUG"):ToAllIf(self.Debug)
|
||||
self:T(self.lid..text)
|
||||
|
||||
-- Trigger returned event.
|
||||
self:__Returned(1, airbase)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--- Event handler for engine shutdown of recovery tanker.
|
||||
-- Respawn tanker group once it landed because it was out of fuel.
|
||||
-- @param #RECOVERYTANKER self
|
||||
@@ -1180,7 +1311,7 @@ function RECOVERYTANKER:OnEventEngineShutdown(EventData)
|
||||
local group=EventData.IniGroup --Wrapper.Group#GROUP
|
||||
|
||||
-- Check if group is alive.
|
||||
if group:IsAlive() then
|
||||
if group and group:IsAlive() then
|
||||
|
||||
-- Group name. When spawning it will have #001 attached.
|
||||
local groupname=group:GetName()
|
||||
@@ -1199,9 +1330,9 @@ function RECOVERYTANKER:OnEventEngineShutdown(EventData)
|
||||
group:InitRadioModulation(self.RadioModu)
|
||||
group:InitModex(self.modex)
|
||||
|
||||
-- Respawn tanker.
|
||||
-- Delaying respawn due to DCS bug https://github.com/FlightControl-Master/MOOSE/issues/1076
|
||||
SCHEDULER:New(nil , group.RespawnAtCurrentAirbase, {group}, 1)
|
||||
-- Respawn tanker. Delaying respawn due to DCS bug https://github.com/FlightControl-Master/MOOSE/issues/1076
|
||||
--SCHEDULER:New(nil , group.RespawnAtCurrentAirbase, {group}, 1)
|
||||
self:ScheduleOnce(1, GROUP.RespawnAtCurrentAirbase, group)
|
||||
|
||||
-- Create tanker beacon and activate TACAN.
|
||||
if self.TACANon then
|
||||
@@ -1219,8 +1350,8 @@ function RECOVERYTANKER:OnEventEngineShutdown(EventData)
|
||||
end
|
||||
|
||||
-- Initial route.
|
||||
SCHEDULER:New(nil, self._InitRoute, {self, -self.distStern+UTILS.NMToMeters(3)}, 2)
|
||||
|
||||
--SCHEDULER:New(nil, self._InitRoute, {self, -self.distStern+UTILS.NMToMeters(3)}, 2)
|
||||
self:ScheduleOnce(2, RECOVERYTANKER._InitRoute, self, -self.distStern+UTILS.NMToMeters(3))
|
||||
end
|
||||
|
||||
end
|
||||
@@ -1250,8 +1381,7 @@ function RECOVERYTANKER:_RefuelingStart(EventData)
|
||||
self:T(self.lid..text)
|
||||
|
||||
-- FMS state "Refueling".
|
||||
self:RefuelStart(receiver)
|
||||
|
||||
self:RefuelStart(receiver)
|
||||
end
|
||||
|
||||
end
|
||||
@@ -1483,7 +1613,8 @@ function RECOVERYTANKER:_ActivateTACAN(delay)
|
||||
if delay and delay>0 then
|
||||
|
||||
-- Schedule TACAN activation.
|
||||
SCHEDULER:New(nil, self._ActivateTACAN, {self}, delay)
|
||||
--SCHEDULER:New(nil, self._ActivateTACAN, {self}, delay)
|
||||
self:ScheduleOnce(delay, RECOVERYTANKER._ActivateTACAN, self)
|
||||
|
||||
else
|
||||
|
||||
@@ -1494,7 +1625,7 @@ function RECOVERYTANKER:_ActivateTACAN(delay)
|
||||
if unit and unit:IsAlive() then
|
||||
|
||||
-- Debug message.
|
||||
local text=string.format("Activating recovery tanker TACAN beacon: channel=%d mode=%s, morse=%s.", self.TACANchannel, self.TACANmode, self.TACANmorse)
|
||||
local text=string.format("Activating TACAN beacon: channel=%d mode=%s, morse=%s.", self.TACANchannel, self.TACANmode, self.TACANmorse)
|
||||
MESSAGE:New(text, 10, "DEBUG"):ToAllIf(self.Debug)
|
||||
self:T(self.lid..text)
|
||||
|
||||
@@ -1503,7 +1634,7 @@ function RECOVERYTANKER:_ActivateTACAN(delay)
|
||||
self.beacon:ActivateTACAN(self.TACANchannel, self.TACANmode, self.TACANmorse, true)
|
||||
|
||||
else
|
||||
self:E("ERROR: Recovery tanker is not alive!")
|
||||
self:E(self.lid.."ERROR: Recovery tanker is not alive!")
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -231,11 +231,11 @@ RESCUEHELO = {
|
||||
|
||||
--- Unique ID (global).
|
||||
-- @field #number uid Unique ID (global).
|
||||
RESCUEHELO.UID=0
|
||||
_RESCUEHELOID=0
|
||||
|
||||
--- Class version.
|
||||
-- @field #string version
|
||||
RESCUEHELO.version="1.0.7"
|
||||
RESCUEHELO.version="1.0.8"
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- TODO list
|
||||
@@ -277,16 +277,16 @@ function RESCUEHELO:New(carrierunit, helogroupname)
|
||||
self.helogroupname=helogroupname
|
||||
|
||||
-- Increase ID.
|
||||
RESCUEHELO.UID=RESCUEHELO.UID+1
|
||||
_RESCUEHELOID=_RESCUEHELOID+1
|
||||
|
||||
-- Unique ID of this helo.
|
||||
self.uid=RESCUEHELO.UID
|
||||
self.uid=_RESCUEHELOID
|
||||
|
||||
-- Save self in static object. Easier to retrieve later.
|
||||
self.carrier:SetState(self.carrier, string.format("RESCUEHELO_%d", self.uid) , self)
|
||||
|
||||
-- Set unique spawn alias.
|
||||
self.alias=string.format("%s_%s_%02d", self.carrier:GetName(), self.helogroupname, RESCUEHELO.UID)
|
||||
self.alias=string.format("%s_%s_%02d", self.carrier:GetName(), self.helogroupname, _RESCUEHELOID)
|
||||
|
||||
-- Log ID.
|
||||
self.lid=string.format("RESCUEHELO %s | ", self.alias)
|
||||
@@ -386,6 +386,7 @@ function RESCUEHELO:New(carrierunit, helogroupname)
|
||||
-- @param #string To To state.
|
||||
-- @param Wrapper.Airbase#AIRBASE airbase The airbase to return to. Default is the home base.
|
||||
|
||||
|
||||
--- Triggers the FSM event "Returned" after the helo has landed.
|
||||
-- @function [parent=#RESCUEHELO] Returned
|
||||
-- @param #RESCUEHELO self
|
||||
@@ -719,7 +720,7 @@ end
|
||||
function RESCUEHELO:OnEventLand(EventData)
|
||||
local group=EventData.IniGroup --Wrapper.Group#GROUP
|
||||
|
||||
if group:IsAlive() then
|
||||
if group and group:IsAlive() then
|
||||
|
||||
-- Group name that landed.
|
||||
local groupname=group:GetName()
|
||||
@@ -727,8 +728,15 @@ function RESCUEHELO:OnEventLand(EventData)
|
||||
-- Check that it was our helo that landed.
|
||||
if groupname==self.helo:GetName() then
|
||||
|
||||
local airbase=nil --Wrapper.Airbase#AIRBASE
|
||||
local airbasename="unknown"
|
||||
if EventData.Place then
|
||||
airbase=EventData.Place
|
||||
airbasename=airbase:GetName()
|
||||
end
|
||||
|
||||
-- Respawn the Helo.
|
||||
local text=string.format("Respawning rescue helo group %s at home base.", groupname)
|
||||
local text=string.format("Rescue helo group %s landed at airbase %s.", groupname, airbasename)
|
||||
MESSAGE:New(text, 10, "DEBUG"):ToAllIf(self.Debug)
|
||||
self:T(self.lid..text)
|
||||
|
||||
@@ -749,7 +757,7 @@ function RESCUEHELO:OnEventLand(EventData)
|
||||
end
|
||||
|
||||
-- Trigger returned event. Respawn at current airbase.
|
||||
self:__Returned(3, EventData.Place)
|
||||
self:__Returned(3, airbase)
|
||||
|
||||
end
|
||||
end
|
||||
@@ -839,7 +847,6 @@ function RESCUEHELO:onafterStart(From, Event, To)
|
||||
self:I(self.lid..text)
|
||||
|
||||
-- Handle events.
|
||||
--self:HandleEvent(EVENTS.Birth)
|
||||
self:HandleEvent(EVENTS.Land)
|
||||
self:HandleEvent(EVENTS.Crash, self._OnEventCrashOrEject)
|
||||
self:HandleEvent(EVENTS.Ejection, self._OnEventCrashOrEject)
|
||||
@@ -899,7 +906,7 @@ function RESCUEHELO:onafterStart(From, Event, To)
|
||||
else
|
||||
|
||||
-- Spawn at airbase.
|
||||
self.helo=Spawn:SpawnAtAirbase(self.airbase, self.takeoff)
|
||||
self.helo=Spawn:SpawnAtAirbase(self.airbase, self.takeoff, nil, AIRBASE.TerminalType.HelicopterUsable)
|
||||
|
||||
-- Delay before formation is started.
|
||||
if self.takeoff==SPAWN.Takeoff.Runway then
|
||||
@@ -948,7 +955,7 @@ function RESCUEHELO:onafterStatus(From, Event, To)
|
||||
local time=timer.getTime()
|
||||
|
||||
-- Check if helo is running and not RTBing already or rescuing.
|
||||
if self.helo:IsAlive() then
|
||||
if self.helo and self.helo:IsAlive() then
|
||||
|
||||
-------------------
|
||||
-- HELO is ALIVE --
|
||||
@@ -959,9 +966,10 @@ function RESCUEHELO:onafterStatus(From, Event, To)
|
||||
local fuelrel=fuel/self.HeloFuel0
|
||||
local life=self.helo:GetUnit(1):GetLife()
|
||||
local life0=self.helo:GetUnit(1):GetLife0()
|
||||
local lifeR=self.helo:GetUnit(1):GetLifeRelative()
|
||||
|
||||
-- Report current fuel.
|
||||
local text=string.format("Rescue Helo %s: state=%s fuel=%.1f, rel.fuel=%.1f, life=%.1f/%.1f", self.helo:GetName(), self:GetState(), fuel, fuelrel, life, life0)
|
||||
local text=string.format("Rescue Helo %s: state=%s fuel=%.1f, rel.fuel=%.1f, life=%.1f/%.1f=%d", self.helo:GetName(), self:GetState(), fuel, fuelrel, life, life0, lifeR*100)
|
||||
MESSAGE:New(text, 10, "DEBUG"):ToAllIf(self.Debug)
|
||||
self:T(self.lid..text)
|
||||
|
||||
@@ -982,7 +990,7 @@ function RESCUEHELO:onafterStatus(From, Event, To)
|
||||
-- Respawn helo in air.
|
||||
self.helo=self.helo:Respawn(nil, true)
|
||||
|
||||
-- XXX: ATTENTION: if helo automatically RTBs on low fuel, it goes a bit cazy. The formation is not stopped and he partially dives into the water.
|
||||
-- XXX: ATTENTION: if helo automatically RTBs on low fuel, it goes a bit crazy. The formation is not stopped and he partially dives into the water.
|
||||
-- Also trying to find a ship to land on he flies right through it.
|
||||
--self.helo:OptionRTBBingoFuel(false)
|
||||
|
||||
@@ -1016,6 +1024,8 @@ function RESCUEHELO:onafterStatus(From, Event, To)
|
||||
|
||||
if not self:IsStopped() then
|
||||
|
||||
self:E(self.lid.."Rescue helo is NOT alive (and not stopped)!")
|
||||
|
||||
-- Stop FSM.
|
||||
self:Stop()
|
||||
|
||||
@@ -1047,18 +1057,6 @@ function RESCUEHELO:onafterRun(From, Event, To)
|
||||
self.formation:Start()
|
||||
end
|
||||
|
||||
-- Restart route of carrier if it was stopped.
|
||||
if self.carrierstop then
|
||||
-- Debug info.
|
||||
local text="Carrier resuming route after rescue operation."
|
||||
MESSAGE:New(text, 10, "DEBUG"):ToAllIf(self.Debug)
|
||||
self:T(self.lid..text)
|
||||
|
||||
-- Resume route of carrier.
|
||||
self.carrier:RouteResume()
|
||||
self.carrierstop=false
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
@@ -1143,15 +1141,6 @@ function RESCUEHELO:onafterRescue(From, Event, To, RescueCoord)
|
||||
-- Stop formation.
|
||||
self.formation:Stop()
|
||||
|
||||
-- Stop carrier.
|
||||
if self.rescuestopboat then
|
||||
local text="Stopping carrier for rescue operation."
|
||||
MESSAGE:New(text, 10, "DEBUG"):ToAllIf(self.Debug)
|
||||
self:T(self.lid..text)
|
||||
|
||||
self.carrier:RouteStop()
|
||||
self.carrierstop=true
|
||||
end
|
||||
end
|
||||
|
||||
--- On after RTB event. Send helo back to carrier.
|
||||
@@ -1170,21 +1159,6 @@ function RESCUEHELO:onafterRTB(From, Event, To, airbase)
|
||||
MESSAGE:New(text, 10, "DEBUG"):ToAllIf(self.Debug)
|
||||
self:T(self.lid..text)
|
||||
|
||||
--[[
|
||||
-- Waypoint array.
|
||||
local wp={}
|
||||
|
||||
-- Set landing waypoint at home base.
|
||||
wp[1]=self.helo:GetCoordinate():WaypointAirTurningPoint(nil, 300, {}, "Current Position")
|
||||
wp[2]=self.airbase:GetCoordinate():SetAltitude(70):WaypointAirLanding(300, self.airbase, {}, "Landing at Home Base")
|
||||
|
||||
-- Initialize WP and route helo.
|
||||
self.helo:WayPointInitialize(wp)
|
||||
|
||||
-- Set task.
|
||||
self.helo:Route(wp, 1)
|
||||
]]
|
||||
|
||||
-- Stop formation.
|
||||
if From=="Running" then
|
||||
self.formation:Stop()
|
||||
@@ -1204,7 +1178,7 @@ function RESCUEHELO:onafterReturned(From, Event, To, airbase)
|
||||
|
||||
if airbase then
|
||||
local airbasename=airbase:GetName()
|
||||
self:T(self.lid..string.format("Helo returned to airbase %s", tostring(airbasename)))
|
||||
self:I(self.lid..string.format("Helo returned to airbase %s", tostring(airbasename)))
|
||||
else
|
||||
self:E(self.lid..string.format("WARNING: Helo landed but airbase (EventData.Place) is nil!"))
|
||||
end
|
||||
@@ -1238,6 +1212,9 @@ function RESCUEHELO:onafterStop(From, Event, To)
|
||||
self:UnHandleEvent(EVENTS.Land)
|
||||
self:UnHandleEvent(EVENTS.Crash)
|
||||
self:UnHandleEvent(EVENTS.Ejection)
|
||||
|
||||
-- Clear all pending FSM events.
|
||||
self.CallScheduler:Clear()
|
||||
|
||||
-- If helo is alive, despawn it.
|
||||
if self.helo and self.helo:IsAlive() then
|
||||
@@ -1245,7 +1222,8 @@ function RESCUEHELO:onafterStop(From, Event, To)
|
||||
self.helo:Destroy()
|
||||
else
|
||||
self:I(self.lid.."Stopping FSM. Helo was not alive.")
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
@@ -1260,13 +1238,13 @@ function RESCUEHELO:RouteRTB(RTBAirbase, Speed)
|
||||
|
||||
-- Curent (from) waypoint.
|
||||
local coord=self.helo:GetCoordinate()
|
||||
local PointFrom=coord:WaypointAirTurningPoint(nil, Speed)
|
||||
local PointFrom=coord:WaypointAirTurningPoint(nil, Speed, {}, "Current")
|
||||
|
||||
-- Airbase coordinate.
|
||||
local PointAirbase=RTBAirbase:GetCoordinate():SetAltitude(100):WaypointAirTurningPoint(nil ,Speed)
|
||||
--local PointAirbase=RTBAirbase:GetCoordinate():SetAltitude(100):WaypointAirTurningPoint(nil ,Speed)
|
||||
|
||||
-- Landing waypoint. More general than prev version since it should also work with FAPRS and ships.
|
||||
local PointLanding=RTBAirbase:GetCoordinate():SetAltitude(20):WaypointAirLanding(Speed, RTBAirbase)
|
||||
local PointLanding=RTBAirbase:GetCoordinate():SetAltitude(20):WaypointAirLanding(Speed, RTBAirbase, {}, "Landing")
|
||||
|
||||
-- Waypoint table.
|
||||
local Points={PointFrom, PointLanding}
|
||||
@@ -1283,6 +1261,9 @@ function RESCUEHELO:RouteRTB(RTBAirbase, Speed)
|
||||
-- Respawn the group.
|
||||
self.helo=self.helo:Respawn(Template, true)
|
||||
|
||||
-- Route the group or this will not work.
|
||||
self.helo:Route(Points, 1)
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
@@ -21,6 +21,11 @@ routines.build = 22
|
||||
-- Utils- conversion, Lua utils, etc.
|
||||
routines.utils = {}
|
||||
|
||||
routines.utils.round = function(number, decimals)
|
||||
local power = 10^decimals
|
||||
return math.floor(number * power) / power
|
||||
end
|
||||
|
||||
--from http://lua-users.org/wiki/CopyTable
|
||||
routines.utils.deepCopy = function(object)
|
||||
local lookup_table = {}
|
||||
|
||||
@@ -429,12 +429,12 @@ UTILS.tostringLL = function( lat, lon, acc, DMS)
|
||||
|
||||
local secFrmtStr -- create the formatting string for the seconds place
|
||||
secFrmtStr = '%02d'
|
||||
-- if acc <= 0 then -- no decimal place.
|
||||
-- secFrmtStr = '%02d'
|
||||
-- else
|
||||
-- local width = 3 + acc -- 01.310 - that's a width of 6, for example.
|
||||
-- secFrmtStr = '%0' .. width .. '.' .. acc .. 'f'
|
||||
-- end
|
||||
if acc <= 0 then -- no decimal place.
|
||||
secFrmtStr = '%02d'
|
||||
else
|
||||
local width = 3 + acc -- 01.310 - that's a width of 6, for example.
|
||||
secFrmtStr = '%0' .. width .. '.' .. acc .. 'f'
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
@@ -311,7 +311,7 @@ AIRBASE.PersianGulf = {
|
||||
-- @field #number OpenMed 72: Open/Shelter air airplane only.
|
||||
-- @field #number OpenBig 104: Open air spawn points. Generally larger but does not guarantee large aircraft are capable of spawning there.
|
||||
-- @field #number OpenMedOrBig 176: Combines OpenMed and OpenBig spots.
|
||||
-- @field #number HelicopterUnsable 216: Combines HelicopterOnly, OpenMed and OpenBig.
|
||||
-- @field #number HelicopterUsable 216: Combines HelicopterOnly, OpenMed and OpenBig.
|
||||
-- @field #number FighterAircraft 244: Combines Shelter. OpenMed and OpenBig spots. So effectively all spots usable by fixed wing aircraft.
|
||||
AIRBASE.TerminalType = {
|
||||
Runway=16,
|
||||
@@ -553,14 +553,14 @@ function AIRBASE:GetParkingSpotsTable(termtype)
|
||||
local spots={}
|
||||
for _,_spot in pairs(parkingdata) do
|
||||
if AIRBASE._CheckTerminalType(_spot.Term_Type, termtype) then
|
||||
self:I({_spot=_spot})
|
||||
self:T2({_spot=_spot})
|
||||
local _free=_isfree(_spot)
|
||||
local _coord=COORDINATE:NewFromVec3(_spot.vTerminalPos)
|
||||
table.insert(spots, {Coordinate=_coord, TerminalID=_spot.Term_Index, TerminalType=_spot.Term_Type, TOAC=_spot.TO_AC, Free=_free, TerminalID0=_spot.Term_Index_0, DistToRwy=_spot.fDistToRW})
|
||||
end
|
||||
end
|
||||
|
||||
self:I({ spots = spots } )
|
||||
self:T2({ spots = spots } )
|
||||
|
||||
return spots
|
||||
end
|
||||
@@ -578,7 +578,7 @@ function AIRBASE:GetFreeParkingSpotsTable(termtype, allowTOAC)
|
||||
-- Put coordinates of free spots into table.
|
||||
local freespots={}
|
||||
for _,_spot in pairs(parkingfree) do
|
||||
if AIRBASE._CheckTerminalType(_spot.Term_Type, termtype) then
|
||||
if AIRBASE._CheckTerminalType(_spot.Term_Type, termtype) and _spot.Term_Index>0 then
|
||||
if (allowTOAC and allowTOAC==true) or _spot.TO_AC==false then
|
||||
local _coord=COORDINATE:NewFromVec3(_spot.vTerminalPos)
|
||||
table.insert(freespots, {Coordinate=_coord, TerminalID=_spot.Term_Index, TerminalType=_spot.Term_Type, TOAC=_spot.TO_AC, Free=true, TerminalID0=_spot.Term_Index_0, DistToRwy=_spot.fDistToRW})
|
||||
@@ -589,6 +589,31 @@ function AIRBASE:GetFreeParkingSpotsTable(termtype, allowTOAC)
|
||||
return freespots
|
||||
end
|
||||
|
||||
--- Get a table containing the coordinates, terminal index and terminal type of free parking spots at an airbase.
|
||||
-- @param #AIRBASE self
|
||||
-- @param #number TerminalID The terminal ID of the parking spot.
|
||||
-- @return #AIRBASE.ParkingSpot Table free parking spots. Table has the elements ".Coordinate, ".TerminalID", ".TerminalType", ".TOAC", ".Free", ".TerminalID0", ".DistToRwy".
|
||||
function AIRBASE:GetParkingSpotData(TerminalID)
|
||||
self:F({TerminalID=TerminalID})
|
||||
|
||||
-- Get parking data.
|
||||
local parkingdata=self:GetParkingSpotsTable()
|
||||
|
||||
-- Debug output.
|
||||
self:T2({parkingdata=parkingdata})
|
||||
|
||||
for _,_spot in pairs(parkingdata) do
|
||||
local spot=_spot --#AIRBASE.ParkingSpot
|
||||
self:E({TerminalID=spot.TerminalID,TerminalType=spot.TerminalType})
|
||||
if TerminalID==spot.TerminalID then
|
||||
return spot
|
||||
end
|
||||
end
|
||||
|
||||
self:E("ERROR: Could not find spot with Terminal ID="..tostring(TerminalID))
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Place markers of parking spots on the F10 map.
|
||||
-- @param #AIRBASE self
|
||||
-- @param #AIRBASE.TerminalType termtype Terminal type for which marks should be placed.
|
||||
@@ -711,7 +736,7 @@ function AIRBASE:FindFreeParkingSpotForAircraft(group, terminaltype, scanradius,
|
||||
local _spot=parkingspot.Coordinate -- Core.Point#COORDINATE
|
||||
local _termid=parkingspot.TerminalID
|
||||
|
||||
self:I({_termid=_termid})
|
||||
self:T2({_termid=_termid})
|
||||
|
||||
if AIRBASE._CheckTerminalType(parkingspot.TerminalType, terminaltype) then
|
||||
|
||||
@@ -893,7 +918,7 @@ function AIRBASE:CheckOnRunWay(group, radius, despawn)
|
||||
end
|
||||
|
||||
--- Get category of airbase.
|
||||
-- @param #WAREHOUSE self
|
||||
-- @param #AIRBASE self
|
||||
-- @return #number Category of airbase from GetDesc().category.
|
||||
function AIRBASE:GetAirbaseCategory()
|
||||
return self:GetDesc().category
|
||||
|
||||
@@ -2813,6 +2813,8 @@ function CONTROLLABLE:GetDetectedTargets( DetectVisual, DetectOptical, DetectRad
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Check if a target is detected.
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE self
|
||||
function CONTROLLABLE:IsTargetDetected( DCSObject, DetectVisual, DetectOptical, DetectRadar, DetectIRST, DetectRWR, DetectDLINK )
|
||||
self:F2( self.ControllableName )
|
||||
|
||||
@@ -2858,7 +2860,7 @@ function CONTROLLABLE:OptionROEHoldFirePossible()
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Holding weapons.
|
||||
--- Weapons Hold: AI will hold fire under all circumstances.
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE self
|
||||
-- @return Wrapper.Controllable#CONTROLLABLE self
|
||||
function CONTROLLABLE:OptionROEHoldFire()
|
||||
@@ -2900,7 +2902,7 @@ function CONTROLLABLE:OptionROEReturnFirePossible()
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Return fire.
|
||||
--- Return Fire: AI will only engage threats that shoot first.
|
||||
-- @param #CONTROLLABLE self
|
||||
-- @return #CONTROLLABLE self
|
||||
function CONTROLLABLE:OptionROEReturnFire()
|
||||
@@ -2942,7 +2944,7 @@ function CONTROLLABLE:OptionROEOpenFirePossible()
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Openfire.
|
||||
--- Open Fire (Only Designated): AI will engage only targets specified in its taskings.
|
||||
-- @param #CONTROLLABLE self
|
||||
-- @return #CONTROLLABLE self
|
||||
function CONTROLLABLE:OptionROEOpenFire()
|
||||
@@ -2966,6 +2968,45 @@ function CONTROLLABLE:OptionROEOpenFire()
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Can the CONTROLLABLE attack priority designated targets? Only for AIR!
|
||||
-- @param #CONTROLLABLE self
|
||||
-- @return #boolean
|
||||
function CONTROLLABLE:OptionROEOpenFireWeaponFreePossible()
|
||||
self:F2( { self.ControllableName } )
|
||||
|
||||
local DCSControllable = self:GetDCSObject()
|
||||
if DCSControllable then
|
||||
if self:IsAir() then
|
||||
return true
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Open Fire, Weapons Free (Priority Designated): AI will engage any enemy group it detects, but will prioritize targets specified in the groups tasking.
|
||||
-- **Only for AIR units!**
|
||||
-- @param #CONTROLLABLE self
|
||||
-- @return #CONTROLLABLE self
|
||||
function CONTROLLABLE:OptionROEOpenFireWeaponFree()
|
||||
self:F2( { self.ControllableName } )
|
||||
|
||||
local DCSControllable = self:GetDCSObject()
|
||||
if DCSControllable then
|
||||
local Controller = self:_GetController()
|
||||
|
||||
if self:IsAir() then
|
||||
Controller:setOption( AI.Option.Air.id.ROE, AI.Option.Air.val.ROE.OPEN_FIRE_WEAPON_FREE )
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Can the CONTROLLABLE attack targets of opportunity?
|
||||
-- @param #CONTROLLABLE self
|
||||
-- @return #boolean
|
||||
@@ -3238,7 +3279,10 @@ end
|
||||
function CONTROLLABLE:OptionRTBBingoFuel( RTB ) --R2.2
|
||||
self:F2( { self.ControllableName } )
|
||||
|
||||
RTB = RTB or true
|
||||
--RTB = RTB or true
|
||||
if RTB==nil then
|
||||
RTB=true
|
||||
end
|
||||
|
||||
local DCSControllable = self:GetDCSObject()
|
||||
if DCSControllable then
|
||||
|
||||
@@ -349,7 +349,8 @@ function GROUP:Destroy( GenerateEvent, delay )
|
||||
self:F2( self.GroupName )
|
||||
|
||||
if delay and delay>0 then
|
||||
SCHEDULER:New(nil, GROUP.Destroy, {self, GenerateEvent}, delay)
|
||||
--SCHEDULER:New(nil, GROUP.Destroy, {self, GenerateEvent}, delay)
|
||||
self:ScheduleOnce(delay, GROUP.Destroy, self, GenerateEvent)
|
||||
else
|
||||
|
||||
local DCSGroup = self:GetDCSObject()
|
||||
|
||||
@@ -612,8 +612,7 @@ end
|
||||
|
||||
--- Returns the unit's health. Dead units has health <= 1.0.
|
||||
-- @param #UNIT self
|
||||
-- @return #number The Unit's health value.
|
||||
-- @return #nil The DCS Unit is not existing or alive.
|
||||
-- @return #number The Unit's health value or -1 if unit does not exist any more.
|
||||
function UNIT:GetLife()
|
||||
self:F2( self.UnitName )
|
||||
|
||||
@@ -629,8 +628,7 @@ end
|
||||
|
||||
--- Returns the Unit's initial health.
|
||||
-- @param #UNIT self
|
||||
-- @return #number The Unit's initial health value.
|
||||
-- @return #nil The DCS Unit is not existing or alive.
|
||||
-- @return #number The Unit's initial health value or 0 if unit does not exist any more.
|
||||
function UNIT:GetLife0()
|
||||
self:F2( self.UnitName )
|
||||
|
||||
@@ -644,6 +642,34 @@ function UNIT:GetLife0()
|
||||
return 0
|
||||
end
|
||||
|
||||
--- Returns the unit's relative health.
|
||||
-- @param #UNIT self
|
||||
-- @return #number The Unit's relative health value, i.e. a number in [0,1] or -1 if unit does not exist any more.
|
||||
function UNIT:GetLifeRelative()
|
||||
self:F2(self.UnitName)
|
||||
|
||||
if self and self:IsAlive() then
|
||||
local life0=self:GetLife0()
|
||||
local lifeN=self:GetLife()
|
||||
return lifeN/life0
|
||||
end
|
||||
|
||||
return -1
|
||||
end
|
||||
|
||||
--- Returns the unit's relative damage, i.e. 1-life.
|
||||
-- @param #UNIT self
|
||||
-- @return #number The Unit's relative health value, i.e. a number in [0,1] or 1 if unit does not exist any more.
|
||||
function UNIT:GetDamageRelative()
|
||||
self:F2(self.UnitName)
|
||||
|
||||
if self and self:IsAlive() then
|
||||
return 1-self:GetLifeRelative()
|
||||
end
|
||||
|
||||
return 1
|
||||
end
|
||||
|
||||
--- Returns the category name of the #UNIT.
|
||||
-- @param #UNIT self
|
||||
-- @return #string Category name = Helicopter, Airplane, Ground Unit, Ship
|
||||
|
||||
Reference in New Issue
Block a user