mirror of
https://github.com/FlightControl-Master/MOOSE.git
synced 2025-10-29 16:58:06 +00:00
Finish 2.4.8
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
--- **AI** -- Models the process of air to air operations for airplanes.
|
--- **AI** -- (R2.2) - Models the process of air operations for airplanes.
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
@@ -9,49 +9,40 @@
|
|||||||
-- @module AI.AI_A2A
|
-- @module AI.AI_A2A
|
||||||
-- @image AI_Air_To_Air_Dispatching.JPG
|
-- @image AI_Air_To_Air_Dispatching.JPG
|
||||||
|
|
||||||
|
--BASE:TraceClass("AI_A2A")
|
||||||
|
|
||||||
|
|
||||||
--- @type AI_A2A
|
--- @type AI_A2A
|
||||||
-- @extends Core.Fsm#FSM_CONTROLLABLE
|
-- @extends Core.Fsm#FSM_CONTROLLABLE
|
||||||
|
|
||||||
--- The AI_A2A class implements the core functions to operate an AI @{Wrapper.Group} A2A tasking.
|
--- The AI_A2A class implements the core functions to operate an AI @{Wrapper.Group} A2A tasking.
|
||||||
--
|
--
|
||||||
|
--
|
||||||
-- ## AI_A2A constructor
|
-- ## AI_A2A constructor
|
||||||
--
|
--
|
||||||
-- * @{#AI_A2A.New}(): Creates a new AI_A2A object.
|
-- * @{#AI_A2A.New}(): Creates a new AI_A2A object.
|
||||||
--
|
--
|
||||||
-- # 2) AI_A2A is a Finite State Machine.
|
-- ## 2. AI_A2A is a FSM
|
||||||
--
|
--
|
||||||
-- This section must be read as follows. Each of the rows indicate a state transition, triggered through an event, and with an ending state of the event was executed.
|
-- 
|
||||||
-- The first column is the **From** state, the second column the **Event**, and the third column the **To** state.
|
|
||||||
--
|
--
|
||||||
-- So, each of the rows have the following structure.
|
-- ### 2.1. AI_A2A States
|
||||||
--
|
--
|
||||||
-- * **From** => **Event** => **To**
|
-- * **None** ( Group ): The process is not started yet.
|
||||||
|
-- * **Patrolling** ( Group ): The AI is patrolling the Patrol Zone.
|
||||||
|
-- * **Returning** ( Group ): The AI is returning to Base.
|
||||||
|
-- * **Stopped** ( Group ): The process is stopped.
|
||||||
|
-- * **Crashed** ( Group ): The AI has crashed or is dead.
|
||||||
--
|
--
|
||||||
-- Important to know is that an event can only be executed if the **current state** is the **From** state.
|
-- ### 2.2. AI_A2A Events
|
||||||
-- This, when an **Event** that is being triggered has a **From** state that is equal to the **Current** state of the state machine, the event will be executed,
|
|
||||||
-- and the resulting state will be the **To** state.
|
|
||||||
--
|
--
|
||||||
-- These are the different possible state transitions of this state machine implementation:
|
-- * **Start** ( Group ): Start the process.
|
||||||
--
|
-- * **Stop** ( Group ): Stop the process.
|
||||||
-- * Idle => Start => Monitoring
|
-- * **Route** ( Group ): Route the AI to a new random 3D point within the Patrol Zone.
|
||||||
--
|
-- * **RTB** ( Group ): Route the AI to the home base.
|
||||||
-- ## 2.1) AI_A2A States.
|
-- * **Detect** ( Group ): The AI is detecting targets.
|
||||||
--
|
-- * **Detected** ( Group ): The AI has detected new targets.
|
||||||
-- * **None**: The process is not started yet.
|
-- * **Status** ( Group ): The AI is checking status (fuel and damage). When the tresholds have been reached, the AI will RTB.
|
||||||
-- * **Patrolling**: The AI is patrolling the Patrol Zone.
|
|
||||||
-- * **Returning**: The AI is returning to Base.
|
|
||||||
-- * **Stopped**: The process is stopped.
|
|
||||||
-- * **Crashed**: The AI has crashed or is dead.
|
|
||||||
--
|
|
||||||
-- ## 2.2) AI_A2A Events.
|
|
||||||
--
|
|
||||||
-- * **Start**: Start the process.
|
|
||||||
-- * **Stop**: Stop the process.
|
|
||||||
-- * **Route**: Route the AI to a new random 3D point within the Patrol Zone.
|
|
||||||
-- * **RTB**: Route the AI to the home base.
|
|
||||||
-- * **Detect**: The AI is detecting targets.
|
|
||||||
-- * **Detected**: The AI has detected new targets.
|
|
||||||
-- * **Status**: The AI is checking status (fuel and damage). When the tresholds have been reached, the AI will RTB.
|
|
||||||
--
|
--
|
||||||
-- ## 3. Set or Get the AI controllable
|
-- ## 3. Set or Get the AI controllable
|
||||||
--
|
--
|
||||||
@@ -70,12 +61,677 @@ AI_A2A = {
|
|||||||
function AI_A2A:New( AIGroup )
|
function AI_A2A:New( AIGroup )
|
||||||
|
|
||||||
-- Inherits from BASE
|
-- Inherits from BASE
|
||||||
local self = BASE:Inherit( self, AI_AIR:New( AIGroup ) ) -- #AI_A2A
|
local self = BASE:Inherit( self, FSM_CONTROLLABLE:New() ) -- #AI_A2A
|
||||||
|
|
||||||
|
self:SetControllable( AIGroup )
|
||||||
|
|
||||||
self:SetFuelThreshold( .2, 60 )
|
self:SetFuelThreshold( .2, 60 )
|
||||||
self:SetDamageThreshold( 0.4 )
|
self:SetDamageThreshold( 0.4 )
|
||||||
self:SetDisengageRadius( 70000 )
|
self:SetDisengageRadius( 70000 )
|
||||||
|
|
||||||
|
self:SetStartState( "Stopped" )
|
||||||
|
|
||||||
|
self:AddTransition( "*", "Start", "Started" )
|
||||||
|
|
||||||
|
--- Start Handler OnBefore for AI_A2A
|
||||||
|
-- @function [parent=#AI_A2A] OnBeforeStart
|
||||||
|
-- @param #AI_A2A self
|
||||||
|
-- @param #string From
|
||||||
|
-- @param #string Event
|
||||||
|
-- @param #string To
|
||||||
|
-- @return #boolean
|
||||||
|
|
||||||
|
--- Start Handler OnAfter for AI_A2A
|
||||||
|
-- @function [parent=#AI_A2A] OnAfterStart
|
||||||
|
-- @param #AI_A2A self
|
||||||
|
-- @param #string From
|
||||||
|
-- @param #string Event
|
||||||
|
-- @param #string To
|
||||||
|
|
||||||
|
--- Start Trigger for AI_A2A
|
||||||
|
-- @function [parent=#AI_A2A] Start
|
||||||
|
-- @param #AI_A2A self
|
||||||
|
|
||||||
|
--- Start Asynchronous Trigger for AI_A2A
|
||||||
|
-- @function [parent=#AI_A2A] __Start
|
||||||
|
-- @param #AI_A2A self
|
||||||
|
-- @param #number Delay
|
||||||
|
|
||||||
|
self:AddTransition( "*", "Stop", "Stopped" )
|
||||||
|
|
||||||
|
--- OnLeave Transition Handler for State Stopped.
|
||||||
|
-- @function [parent=#AI_A2A] OnLeaveStopped
|
||||||
|
-- @param #AI_A2A self
|
||||||
|
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||||
|
-- @param #string From The From State string.
|
||||||
|
-- @param #string Event The Event string.
|
||||||
|
-- @param #string To The To State string.
|
||||||
|
-- @return #boolean Return false to cancel Transition.
|
||||||
|
|
||||||
|
--- OnEnter Transition Handler for State Stopped.
|
||||||
|
-- @function [parent=#AI_A2A] OnEnterStopped
|
||||||
|
-- @param #AI_A2A self
|
||||||
|
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||||
|
-- @param #string From The From State string.
|
||||||
|
-- @param #string Event The Event string.
|
||||||
|
-- @param #string To The To State string.
|
||||||
|
|
||||||
|
--- OnBefore Transition Handler for Event Stop.
|
||||||
|
-- @function [parent=#AI_A2A] OnBeforeStop
|
||||||
|
-- @param #AI_A2A self
|
||||||
|
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||||
|
-- @param #string From The From State string.
|
||||||
|
-- @param #string Event The Event string.
|
||||||
|
-- @param #string To The To State string.
|
||||||
|
-- @return #boolean Return false to cancel Transition.
|
||||||
|
|
||||||
|
--- OnAfter Transition Handler for Event Stop.
|
||||||
|
-- @function [parent=#AI_A2A] OnAfterStop
|
||||||
|
-- @param #AI_A2A self
|
||||||
|
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||||
|
-- @param #string From The From State string.
|
||||||
|
-- @param #string Event The Event string.
|
||||||
|
-- @param #string To The To State string.
|
||||||
|
|
||||||
|
--- Synchronous Event Trigger for Event Stop.
|
||||||
|
-- @function [parent=#AI_A2A] Stop
|
||||||
|
-- @param #AI_A2A self
|
||||||
|
|
||||||
|
--- Asynchronous Event Trigger for Event Stop.
|
||||||
|
-- @function [parent=#AI_A2A] __Stop
|
||||||
|
-- @param #AI_A2A self
|
||||||
|
-- @param #number Delay The delay in seconds.
|
||||||
|
|
||||||
|
self:AddTransition( "*", "Status", "*" ) -- FSM_CONTROLLABLE Transition for type #AI_A2A.
|
||||||
|
|
||||||
|
--- OnBefore Transition Handler for Event Status.
|
||||||
|
-- @function [parent=#AI_A2A] OnBeforeStatus
|
||||||
|
-- @param #AI_A2A self
|
||||||
|
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||||
|
-- @param #string From The From State string.
|
||||||
|
-- @param #string Event The Event string.
|
||||||
|
-- @param #string To The To State string.
|
||||||
|
-- @return #boolean Return false to cancel Transition.
|
||||||
|
|
||||||
|
--- OnAfter Transition Handler for Event Status.
|
||||||
|
-- @function [parent=#AI_A2A] OnAfterStatus
|
||||||
|
-- @param #AI_A2A self
|
||||||
|
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||||
|
-- @param #string From The From State string.
|
||||||
|
-- @param #string Event The Event string.
|
||||||
|
-- @param #string To The To State string.
|
||||||
|
|
||||||
|
--- Synchronous Event Trigger for Event Status.
|
||||||
|
-- @function [parent=#AI_A2A] Status
|
||||||
|
-- @param #AI_A2A self
|
||||||
|
|
||||||
|
--- Asynchronous Event Trigger for Event Status.
|
||||||
|
-- @function [parent=#AI_A2A] __Status
|
||||||
|
-- @param #AI_A2A self
|
||||||
|
-- @param #number Delay The delay in seconds.
|
||||||
|
|
||||||
|
self:AddTransition( "*", "RTB", "*" ) -- FSM_CONTROLLABLE Transition for type #AI_A2A.
|
||||||
|
|
||||||
|
--- OnBefore Transition Handler for Event RTB.
|
||||||
|
-- @function [parent=#AI_A2A] OnBeforeRTB
|
||||||
|
-- @param #AI_A2A self
|
||||||
|
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||||
|
-- @param #string From The From State string.
|
||||||
|
-- @param #string Event The Event string.
|
||||||
|
-- @param #string To The To State string.
|
||||||
|
-- @return #boolean Return false to cancel Transition.
|
||||||
|
|
||||||
|
--- OnAfter Transition Handler for Event RTB.
|
||||||
|
-- @function [parent=#AI_A2A] OnAfterRTB
|
||||||
|
-- @param #AI_A2A self
|
||||||
|
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||||
|
-- @param #string From The From State string.
|
||||||
|
-- @param #string Event The Event string.
|
||||||
|
-- @param #string To The To State string.
|
||||||
|
|
||||||
|
--- Synchronous Event Trigger for Event RTB.
|
||||||
|
-- @function [parent=#AI_A2A] RTB
|
||||||
|
-- @param #AI_A2A self
|
||||||
|
|
||||||
|
--- Asynchronous Event Trigger for Event RTB.
|
||||||
|
-- @function [parent=#AI_A2A] __RTB
|
||||||
|
-- @param #AI_A2A self
|
||||||
|
-- @param #number Delay The delay in seconds.
|
||||||
|
|
||||||
|
--- OnLeave Transition Handler for State Returning.
|
||||||
|
-- @function [parent=#AI_A2A] OnLeaveReturning
|
||||||
|
-- @param #AI_A2A self
|
||||||
|
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||||
|
-- @param #string From The From State string.
|
||||||
|
-- @param #string Event The Event string.
|
||||||
|
-- @param #string To The To State string.
|
||||||
|
-- @return #boolean Return false to cancel Transition.
|
||||||
|
|
||||||
|
--- OnEnter Transition Handler for State Returning.
|
||||||
|
-- @function [parent=#AI_A2A] OnEnterReturning
|
||||||
|
-- @param #AI_A2A self
|
||||||
|
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||||
|
-- @param #string From The From State string.
|
||||||
|
-- @param #string Event The Event string.
|
||||||
|
-- @param #string To The To State string.
|
||||||
|
|
||||||
|
self:AddTransition( "Patrolling", "Refuel", "Refuelling" )
|
||||||
|
|
||||||
|
--- Refuel Handler OnBefore for AI_A2A
|
||||||
|
-- @function [parent=#AI_A2A] OnBeforeRefuel
|
||||||
|
-- @param #AI_A2A self
|
||||||
|
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||||
|
-- @param #string From
|
||||||
|
-- @param #string Event
|
||||||
|
-- @param #string To
|
||||||
|
-- @return #boolean
|
||||||
|
|
||||||
|
--- Refuel Handler OnAfter for AI_A2A
|
||||||
|
-- @function [parent=#AI_A2A] OnAfterRefuel
|
||||||
|
-- @param #AI_A2A self
|
||||||
|
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||||
|
-- @param #string From
|
||||||
|
-- @param #string Event
|
||||||
|
-- @param #string To
|
||||||
|
|
||||||
|
--- Refuel Trigger for AI_A2A
|
||||||
|
-- @function [parent=#AI_A2A] Refuel
|
||||||
|
-- @param #AI_A2A self
|
||||||
|
|
||||||
|
--- Refuel Asynchronous Trigger for AI_A2A
|
||||||
|
-- @function [parent=#AI_A2A] __Refuel
|
||||||
|
-- @param #AI_A2A self
|
||||||
|
-- @param #number Delay
|
||||||
|
|
||||||
|
self:AddTransition( "*", "Takeoff", "Airborne" )
|
||||||
|
self:AddTransition( "*", "Return", "Returning" )
|
||||||
|
self:AddTransition( "*", "Hold", "Holding" )
|
||||||
|
self:AddTransition( "*", "Home", "Home" )
|
||||||
|
self:AddTransition( "*", "LostControl", "LostControl" )
|
||||||
|
self:AddTransition( "*", "Fuel", "Fuel" )
|
||||||
|
self:AddTransition( "*", "Damaged", "Damaged" )
|
||||||
|
self:AddTransition( "*", "Eject", "*" )
|
||||||
|
self:AddTransition( "*", "Crash", "Crashed" )
|
||||||
|
self:AddTransition( "*", "PilotDead", "*" )
|
||||||
|
|
||||||
|
self.IdleCount = 0
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- @param Wrapper.Group#GROUP self
|
||||||
|
-- @param Core.Event#EVENTDATA EventData
|
||||||
|
function GROUP:OnEventTakeoff( EventData, Fsm )
|
||||||
|
Fsm:Takeoff()
|
||||||
|
self:UnHandleEvent( EVENTS.Takeoff )
|
||||||
|
end
|
||||||
|
|
||||||
|
function AI_A2A:SetDispatcher( Dispatcher )
|
||||||
|
self.Dispatcher = Dispatcher
|
||||||
|
end
|
||||||
|
|
||||||
|
function AI_A2A:GetDispatcher()
|
||||||
|
return self.Dispatcher
|
||||||
|
end
|
||||||
|
|
||||||
|
function AI_A2A:SetTargetDistance( Coordinate )
|
||||||
|
|
||||||
|
local CurrentCoord = self.Controllable:GetCoordinate()
|
||||||
|
self.TargetDistance = CurrentCoord:Get2DDistance( Coordinate )
|
||||||
|
|
||||||
|
self.ClosestTargetDistance = ( not self.ClosestTargetDistance or self.ClosestTargetDistance > self.TargetDistance ) and self.TargetDistance or self.ClosestTargetDistance
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function AI_A2A:ClearTargetDistance()
|
||||||
|
|
||||||
|
self.TargetDistance = nil
|
||||||
|
self.ClosestTargetDistance = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Sets (modifies) the minimum and maximum speed of the patrol.
|
||||||
|
-- @param #AI_A2A self
|
||||||
|
-- @param DCS#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Controllable} in km/h.
|
||||||
|
-- @param DCS#Speed PatrolMaxSpeed The maximum speed of the @{Wrapper.Controllable} in km/h.
|
||||||
|
-- @return #AI_A2A self
|
||||||
|
function AI_A2A:SetSpeed( PatrolMinSpeed, PatrolMaxSpeed )
|
||||||
|
self:F2( { PatrolMinSpeed, PatrolMaxSpeed } )
|
||||||
|
|
||||||
|
self.PatrolMinSpeed = PatrolMinSpeed
|
||||||
|
self.PatrolMaxSpeed = PatrolMaxSpeed
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Sets the floor and ceiling altitude of the patrol.
|
||||||
|
-- @param #AI_A2A self
|
||||||
|
-- @param DCS#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol.
|
||||||
|
-- @param DCS#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol.
|
||||||
|
-- @return #AI_A2A self
|
||||||
|
function AI_A2A:SetAltitude( PatrolFloorAltitude, PatrolCeilingAltitude )
|
||||||
|
self:F2( { PatrolFloorAltitude, PatrolCeilingAltitude } )
|
||||||
|
|
||||||
|
self.PatrolFloorAltitude = PatrolFloorAltitude
|
||||||
|
self.PatrolCeilingAltitude = PatrolCeilingAltitude
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Sets the home airbase.
|
||||||
|
-- @param #AI_A2A self
|
||||||
|
-- @param Wrapper.Airbase#AIRBASE HomeAirbase
|
||||||
|
-- @return #AI_A2A self
|
||||||
|
function AI_A2A:SetHomeAirbase( HomeAirbase )
|
||||||
|
self:F2( { HomeAirbase } )
|
||||||
|
|
||||||
|
self.HomeAirbase = HomeAirbase
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Sets to refuel at the given tanker.
|
||||||
|
-- @param #AI_A2A self
|
||||||
|
-- @param Wrapper.Group#GROUP TankerName The group name of the tanker as defined within the Mission Editor or spawned.
|
||||||
|
-- @return #AI_A2A self
|
||||||
|
function AI_A2A:SetTanker( TankerName )
|
||||||
|
self:F2( { TankerName } )
|
||||||
|
|
||||||
|
self.TankerName = TankerName
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Sets the disengage range, that when engaging a target beyond the specified range, the engagement will be cancelled and the plane will RTB.
|
||||||
|
-- @param #AI_A2A self
|
||||||
|
-- @param #number DisengageRadius The disengage range.
|
||||||
|
-- @return #AI_A2A self
|
||||||
|
function AI_A2A:SetDisengageRadius( DisengageRadius )
|
||||||
|
self:F2( { DisengageRadius } )
|
||||||
|
|
||||||
|
self.DisengageRadius = DisengageRadius
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set the status checking off.
|
||||||
|
-- @param #AI_A2A self
|
||||||
|
-- @return #AI_A2A self
|
||||||
|
function AI_A2A:SetStatusOff()
|
||||||
|
self:F2()
|
||||||
|
|
||||||
|
self.CheckStatus = false
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- When the AI is out of fuel, it is required that a new AI is started, before the old AI can return to the home base.
|
||||||
|
-- Therefore, with a parameter and a calculation of the distance to the home base, the fuel treshold is calculated.
|
||||||
|
-- When the fuel treshold is reached, the AI will continue for a given time its patrol task in orbit, while a new AIControllable is targetted to the AI_A2A.
|
||||||
|
-- Once the time is finished, the old AI will return to the base.
|
||||||
|
-- @param #AI_A2A self
|
||||||
|
-- @param #number PatrolFuelThresholdPercentage The treshold in percentage (between 0 and 1) when the AIControllable is considered to get out of fuel.
|
||||||
|
-- @param #number PatrolOutOfFuelOrbitTime The amount of seconds the out of fuel AIControllable will orbit before returning to the base.
|
||||||
|
-- @return #AI_A2A self
|
||||||
|
function AI_A2A:SetFuelThreshold( PatrolFuelThresholdPercentage, PatrolOutOfFuelOrbitTime )
|
||||||
|
|
||||||
|
self.PatrolFuelThresholdPercentage = PatrolFuelThresholdPercentage
|
||||||
|
self.PatrolOutOfFuelOrbitTime = PatrolOutOfFuelOrbitTime
|
||||||
|
|
||||||
|
self.Controllable:OptionRTBBingoFuel( false )
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- When the AI is damaged beyond a certain treshold, it is required that the AI returns to the home base.
|
||||||
|
-- However, damage cannot be foreseen early on.
|
||||||
|
-- Therefore, when the damage treshold is reached,
|
||||||
|
-- the AI will return immediately to the home base (RTB).
|
||||||
|
-- Note that for groups, the average damage of the complete group will be calculated.
|
||||||
|
-- So, in a group of 4 airplanes, 2 lost and 2 with damage 0.2, the damage treshold will be 0.25.
|
||||||
|
-- @param #AI_A2A self
|
||||||
|
-- @param #number PatrolDamageThreshold The treshold in percentage (between 0 and 1) when the AI is considered to be damaged.
|
||||||
|
-- @return #AI_A2A self
|
||||||
|
function AI_A2A:SetDamageThreshold( PatrolDamageThreshold )
|
||||||
|
|
||||||
|
self.PatrolManageDamage = true
|
||||||
|
self.PatrolDamageThreshold = PatrolDamageThreshold
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Defines a new patrol route using the @{Process_PatrolZone} parameters and settings.
|
||||||
|
-- @param #AI_A2A self
|
||||||
|
-- @return #AI_A2A self
|
||||||
|
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||||
|
-- @param #string From The From State string.
|
||||||
|
-- @param #string Event The Event string.
|
||||||
|
-- @param #string To The To State string.
|
||||||
|
function AI_A2A:onafterStart( Controllable, From, Event, To )
|
||||||
|
|
||||||
|
self:__Status( 10 ) -- Check status status every 30 seconds.
|
||||||
|
|
||||||
|
self:HandleEvent( EVENTS.PilotDead, self.OnPilotDead )
|
||||||
|
self:HandleEvent( EVENTS.Crash, self.OnCrash )
|
||||||
|
self:HandleEvent( EVENTS.Ejection, self.OnEjection )
|
||||||
|
|
||||||
|
Controllable:OptionROEHoldFire()
|
||||||
|
Controllable:OptionROTVertical()
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
--- @param #AI_A2A self
|
||||||
|
function AI_A2A:onbeforeStatus()
|
||||||
|
|
||||||
|
return self.CheckStatus
|
||||||
|
end
|
||||||
|
|
||||||
|
--- @param #AI_A2A self
|
||||||
|
function AI_A2A:onafterStatus()
|
||||||
|
|
||||||
|
if self.Controllable and self.Controllable:IsAlive() then
|
||||||
|
|
||||||
|
local RTB = false
|
||||||
|
|
||||||
|
local DistanceFromHomeBase = self.HomeAirbase:GetCoordinate():Get2DDistance( self.Controllable:GetCoordinate() )
|
||||||
|
|
||||||
|
if not self:Is( "Holding" ) and not self:Is( "Returning" ) then
|
||||||
|
local DistanceFromHomeBase = self.HomeAirbase:GetCoordinate():Get2DDistance( self.Controllable:GetCoordinate() )
|
||||||
|
self:F({DistanceFromHomeBase=DistanceFromHomeBase})
|
||||||
|
|
||||||
|
if DistanceFromHomeBase > self.DisengageRadius then
|
||||||
|
self:E( self.Controllable:GetName() .. " is too far from home base, RTB!" )
|
||||||
|
self:Hold( 300 )
|
||||||
|
RTB = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- I think this code is not requirement anymore after release 2.5.
|
||||||
|
-- if self:Is( "Fuel" ) or self:Is( "Damaged" ) or self:Is( "LostControl" ) then
|
||||||
|
-- if DistanceFromHomeBase < 5000 then
|
||||||
|
-- self:E( self.Controllable:GetName() .. " is near the home base, RTB!" )
|
||||||
|
-- self:Home( "Destroy" )
|
||||||
|
-- end
|
||||||
|
-- end
|
||||||
|
|
||||||
|
|
||||||
|
if not self:Is( "Fuel" ) and not self:Is( "Home" ) then
|
||||||
|
local Fuel = self.Controllable:GetFuelMin()
|
||||||
|
self:F({Fuel=Fuel, PatrolFuelThresholdPercentage=self.PatrolFuelThresholdPercentage})
|
||||||
|
if Fuel < self.PatrolFuelThresholdPercentage then
|
||||||
|
if self.TankerName then
|
||||||
|
self:E( self.Controllable:GetName() .. " is out of fuel: " .. Fuel .. " ... Refuelling at Tanker!" )
|
||||||
|
self:Refuel()
|
||||||
|
else
|
||||||
|
self:E( self.Controllable:GetName() .. " is out of fuel: " .. Fuel .. " ... RTB!" )
|
||||||
|
local OldAIControllable = self.Controllable
|
||||||
|
|
||||||
|
local OrbitTask = OldAIControllable:TaskOrbitCircle( math.random( self.PatrolFloorAltitude, self.PatrolCeilingAltitude ), self.PatrolMinSpeed )
|
||||||
|
local TimedOrbitTask = OldAIControllable:TaskControlled( OrbitTask, OldAIControllable:TaskCondition(nil,nil,nil,nil,self.PatrolOutOfFuelOrbitTime,nil ) )
|
||||||
|
OldAIControllable:SetTask( TimedOrbitTask, 10 )
|
||||||
|
|
||||||
|
self:Fuel()
|
||||||
|
RTB = true
|
||||||
|
end
|
||||||
|
else
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- TODO: Check GROUP damage function.
|
||||||
|
local Damage = self.Controllable:GetLife()
|
||||||
|
local InitialLife = self.Controllable:GetLife0()
|
||||||
|
self:F( { Damage = Damage, InitialLife = InitialLife, DamageThreshold = self.PatrolDamageThreshold } )
|
||||||
|
if ( Damage / InitialLife ) < self.PatrolDamageThreshold then
|
||||||
|
self:E( self.Controllable:GetName() .. " is damaged: " .. Damage .. " ... RTB!" )
|
||||||
|
self:Damaged()
|
||||||
|
RTB = true
|
||||||
|
self:SetStatusOff()
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Check if planes went RTB and are out of control.
|
||||||
|
-- We only check if planes are out of control, when they are in duty.
|
||||||
|
if self.Controllable:HasTask() == false then
|
||||||
|
if not self:Is( "Started" ) and
|
||||||
|
not self:Is( "Stopped" ) and
|
||||||
|
not self:Is( "Fuel" ) and
|
||||||
|
not self:Is( "Damaged" ) and
|
||||||
|
not self:Is( "Home" ) then
|
||||||
|
if self.IdleCount >= 2 then
|
||||||
|
if Damage ~= InitialLife then
|
||||||
|
self:Damaged()
|
||||||
|
else
|
||||||
|
self:E( self.Controllable:GetName() .. " control lost! " )
|
||||||
|
self:LostControl()
|
||||||
|
end
|
||||||
|
else
|
||||||
|
self.IdleCount = self.IdleCount + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
self.IdleCount = 0
|
||||||
|
end
|
||||||
|
|
||||||
|
if RTB == true then
|
||||||
|
self:__RTB( 0.5 )
|
||||||
|
end
|
||||||
|
|
||||||
|
if not self:Is("Home") then
|
||||||
|
self:__Status( 10 )
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- @param Wrapper.Group#GROUP AIGroup
|
||||||
|
function AI_A2A.RTBRoute( AIGroup, Fsm )
|
||||||
|
|
||||||
|
AIGroup:F( { "AI_A2A.RTBRoute:", AIGroup:GetName() } )
|
||||||
|
|
||||||
|
if AIGroup:IsAlive() then
|
||||||
|
Fsm:__RTB( 0.5 )
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
--- @param Wrapper.Group#GROUP AIGroup
|
||||||
|
function AI_A2A.RTBHold( AIGroup, Fsm )
|
||||||
|
|
||||||
|
AIGroup:F( { "AI_A2A.RTBHold:", AIGroup:GetName() } )
|
||||||
|
if AIGroup:IsAlive() then
|
||||||
|
Fsm:__RTB( 0.5 )
|
||||||
|
Fsm:Return()
|
||||||
|
local Task = AIGroup:TaskOrbitCircle( 4000, 400 )
|
||||||
|
AIGroup:SetTask( Task )
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- @param #AI_A2A self
|
||||||
|
-- @param Wrapper.Group#GROUP AIGroup
|
||||||
|
function AI_A2A:onafterRTB( AIGroup, From, Event, To )
|
||||||
|
self:F( { AIGroup, From, Event, To } )
|
||||||
|
|
||||||
|
|
||||||
|
if AIGroup and AIGroup:IsAlive() then
|
||||||
|
|
||||||
|
self:E( "Group " .. AIGroup:GetName() .. " ... RTB! ( " .. self:GetState() .. " )" )
|
||||||
|
|
||||||
|
self:ClearTargetDistance()
|
||||||
|
AIGroup:ClearTasks()
|
||||||
|
|
||||||
|
local EngageRoute = {}
|
||||||
|
|
||||||
|
--- Calculate the target route point.
|
||||||
|
|
||||||
|
local CurrentCoord = AIGroup:GetCoordinate()
|
||||||
|
local ToTargetCoord = self.HomeAirbase:GetCoordinate()
|
||||||
|
local ToTargetSpeed = math.random( self.PatrolMinSpeed, self.PatrolMaxSpeed )
|
||||||
|
local ToAirbaseAngle = CurrentCoord:GetAngleDegrees( CurrentCoord:GetDirectionVec3( ToTargetCoord ) )
|
||||||
|
|
||||||
|
local Distance = CurrentCoord:Get2DDistance( ToTargetCoord )
|
||||||
|
|
||||||
|
local ToAirbaseCoord = CurrentCoord:Translate( 5000, ToAirbaseAngle )
|
||||||
|
if Distance < 5000 then
|
||||||
|
self:E( "RTB and near the airbase!" )
|
||||||
|
self:Home()
|
||||||
|
return
|
||||||
|
end
|
||||||
|
--- Create a route point of type air.
|
||||||
|
local ToRTBRoutePoint = ToAirbaseCoord:WaypointAir(
|
||||||
|
self.PatrolAltType,
|
||||||
|
POINT_VEC3.RoutePointType.TurningPoint,
|
||||||
|
POINT_VEC3.RoutePointAction.TurningPoint,
|
||||||
|
ToTargetSpeed,
|
||||||
|
true
|
||||||
|
)
|
||||||
|
|
||||||
|
self:F( { Angle = ToAirbaseAngle, ToTargetSpeed = ToTargetSpeed } )
|
||||||
|
self:T2( { self.MinSpeed, self.MaxSpeed, ToTargetSpeed } )
|
||||||
|
|
||||||
|
EngageRoute[#EngageRoute+1] = ToRTBRoutePoint
|
||||||
|
EngageRoute[#EngageRoute+1] = ToRTBRoutePoint
|
||||||
|
|
||||||
|
AIGroup:OptionROEHoldFire()
|
||||||
|
AIGroup:OptionROTEvadeFire()
|
||||||
|
|
||||||
|
--- Now we're going to do something special, we're going to call a function from a waypoint action at the AIControllable...
|
||||||
|
AIGroup:WayPointInitialize( EngageRoute )
|
||||||
|
|
||||||
|
local Tasks = {}
|
||||||
|
Tasks[#Tasks+1] = AIGroup:TaskFunction( "AI_A2A.RTBRoute", self )
|
||||||
|
EngageRoute[#EngageRoute].task = AIGroup:TaskCombo( Tasks )
|
||||||
|
|
||||||
|
--- NOW ROUTE THE GROUP!
|
||||||
|
AIGroup:Route( EngageRoute, 0.5 )
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
--- @param #AI_A2A self
|
||||||
|
-- @param Wrapper.Group#GROUP AIGroup
|
||||||
|
function AI_A2A:onafterHome( AIGroup, From, Event, To )
|
||||||
|
self:F( { AIGroup, From, Event, To } )
|
||||||
|
|
||||||
|
self:E( "Group " .. self.Controllable:GetName() .. " ... Home! ( " .. self:GetState() .. " )" )
|
||||||
|
|
||||||
|
if AIGroup and AIGroup:IsAlive() then
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
--- @param #AI_A2A self
|
||||||
|
-- @param Wrapper.Group#GROUP AIGroup
|
||||||
|
function AI_A2A:onafterHold( AIGroup, From, Event, To, HoldTime )
|
||||||
|
self:F( { AIGroup, From, Event, To } )
|
||||||
|
|
||||||
|
self:E( "Group " .. self.Controllable:GetName() .. " ... Holding! ( " .. self:GetState() .. " )" )
|
||||||
|
|
||||||
|
if AIGroup and AIGroup:IsAlive() then
|
||||||
|
local OrbitTask = AIGroup:TaskOrbitCircle( math.random( self.PatrolFloorAltitude, self.PatrolCeilingAltitude ), self.PatrolMinSpeed )
|
||||||
|
local TimedOrbitTask = AIGroup:TaskControlled( OrbitTask, AIGroup:TaskCondition( nil, nil, nil, nil, HoldTime , nil ) )
|
||||||
|
|
||||||
|
local RTBTask = AIGroup:TaskFunction( "AI_A2A.RTBHold", self )
|
||||||
|
|
||||||
|
local OrbitHoldTask = AIGroup:TaskOrbitCircle( 4000, self.PatrolMinSpeed )
|
||||||
|
|
||||||
|
--AIGroup:SetState( AIGroup, "AI_A2A", self )
|
||||||
|
|
||||||
|
AIGroup:SetTask( AIGroup:TaskCombo( { TimedOrbitTask, RTBTask, OrbitHoldTask } ), 1 )
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
--- @param Wrapper.Group#GROUP AIGroup
|
||||||
|
function AI_A2A.Resume( AIGroup, Fsm )
|
||||||
|
|
||||||
|
AIGroup:I( { "AI_A2A.Resume:", AIGroup:GetName() } )
|
||||||
|
if AIGroup:IsAlive() then
|
||||||
|
Fsm:__RTB( 0.5 )
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
--- @param #AI_A2A self
|
||||||
|
-- @param Wrapper.Group#GROUP AIGroup
|
||||||
|
function AI_A2A:onafterRefuel( AIGroup, From, Event, To )
|
||||||
|
self:F( { AIGroup, From, Event, To } )
|
||||||
|
|
||||||
|
self:E( "Group " .. self.Controllable:GetName() .. " ... Refuelling! ( " .. self:GetState() .. " )" )
|
||||||
|
|
||||||
|
if AIGroup and AIGroup:IsAlive() then
|
||||||
|
local Tanker = GROUP:FindByName( self.TankerName )
|
||||||
|
if Tanker:IsAlive() and Tanker:IsAirPlane() then
|
||||||
|
|
||||||
|
local RefuelRoute = {}
|
||||||
|
|
||||||
|
--- Calculate the target route point.
|
||||||
|
|
||||||
|
local CurrentCoord = AIGroup:GetCoordinate()
|
||||||
|
local ToRefuelCoord = Tanker:GetCoordinate()
|
||||||
|
local ToRefuelSpeed = math.random( self.PatrolMinSpeed, self.PatrolMaxSpeed )
|
||||||
|
|
||||||
|
--- Create a route point of type air.
|
||||||
|
local ToRefuelRoutePoint = ToRefuelCoord:WaypointAir(
|
||||||
|
self.PatrolAltType,
|
||||||
|
POINT_VEC3.RoutePointType.TurningPoint,
|
||||||
|
POINT_VEC3.RoutePointAction.TurningPoint,
|
||||||
|
ToRefuelSpeed,
|
||||||
|
true
|
||||||
|
)
|
||||||
|
|
||||||
|
self:F( { ToRefuelSpeed = ToRefuelSpeed } )
|
||||||
|
|
||||||
|
RefuelRoute[#RefuelRoute+1] = ToRefuelRoutePoint
|
||||||
|
RefuelRoute[#RefuelRoute+1] = ToRefuelRoutePoint
|
||||||
|
|
||||||
|
AIGroup:OptionROEHoldFire()
|
||||||
|
AIGroup:OptionROTEvadeFire()
|
||||||
|
|
||||||
|
local Tasks = {}
|
||||||
|
Tasks[#Tasks+1] = AIGroup:TaskRefueling()
|
||||||
|
Tasks[#Tasks+1] = AIGroup:TaskFunction( self:GetClassName() .. ".Resume", self )
|
||||||
|
RefuelRoute[#RefuelRoute].task = AIGroup:TaskCombo( Tasks )
|
||||||
|
|
||||||
|
AIGroup:Route( RefuelRoute, 0.5 )
|
||||||
|
else
|
||||||
|
self:RTB()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
--- @param #AI_A2A self
|
||||||
|
function AI_A2A:onafterDead()
|
||||||
|
self:SetStatusOff()
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- @param #AI_A2A self
|
||||||
|
-- @param Core.Event#EVENTDATA EventData
|
||||||
|
function AI_A2A:OnCrash( EventData )
|
||||||
|
|
||||||
|
if self.Controllable:IsAlive() and EventData.IniDCSGroupName == self.Controllable:GetName() then
|
||||||
|
self:E( self.Controllable:GetUnits() )
|
||||||
|
if #self.Controllable:GetUnits() == 1 then
|
||||||
|
self:__Crash( 1, EventData )
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- @param #AI_A2A self
|
||||||
|
-- @param Core.Event#EVENTDATA EventData
|
||||||
|
function AI_A2A:OnEjection( EventData )
|
||||||
|
|
||||||
|
if self.Controllable:IsAlive() and EventData.IniDCSGroupName == self.Controllable:GetName() then
|
||||||
|
self:__Eject( 1, EventData )
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- @param #AI_A2A self
|
||||||
|
-- @param Core.Event#EVENTDATA EventData
|
||||||
|
function AI_A2A:OnPilotDead( EventData )
|
||||||
|
|
||||||
|
if self.Controllable:IsAlive() and EventData.IniDCSGroupName == self.Controllable:GetName() then
|
||||||
|
self:__PilotDead( 1, EventData )
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|||||||
@@ -281,7 +281,6 @@ function AI_A2A_CAP:New( AICap, PatrolZone, PatrolFloorAltitude, PatrolCeilingAl
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- onafter State Transition for Event Patrol.
|
--- onafter State Transition for Event Patrol.
|
||||||
-- @param #AI_A2A_CAP self
|
-- @param #AI_A2A_CAP self
|
||||||
-- @param Wrapper.Group#GROUP AICap The AI Group managed by the FSM.
|
-- @param Wrapper.Group#GROUP AICap The AI Group managed by the FSM.
|
||||||
|
|||||||
@@ -1,69 +0,0 @@
|
|||||||
--- **AI** -- Models the process of air to ground operations for airplanes and helicopters.
|
|
||||||
--
|
|
||||||
-- ===
|
|
||||||
--
|
|
||||||
-- ### Author: **FlightControl**
|
|
||||||
--
|
|
||||||
-- ===
|
|
||||||
--
|
|
||||||
-- @module AI.AI_A2G
|
|
||||||
-- @image AI_Air_To_Ground_Dispatching.JPG
|
|
||||||
|
|
||||||
--- @type AI_A2G
|
|
||||||
-- @extends AI.AI_Air#AI_AIR
|
|
||||||
|
|
||||||
--- The AI_A2G class implements the core functions to operate an AI @{Wrapper.Group} A2G tasking.
|
|
||||||
--
|
|
||||||
--
|
|
||||||
-- # 1) AI_A2G constructor
|
|
||||||
--
|
|
||||||
-- * @{#AI_A2G.New}(): Creates a new AI_A2G object.
|
|
||||||
--
|
|
||||||
-- # 2) AI_A2G is a Finite State Machine.
|
|
||||||
--
|
|
||||||
-- This section must be read as follows. Each of the rows indicate a state transition, triggered through an event, and with an ending state of the event was executed.
|
|
||||||
-- The first column is the **From** state, the second column the **Event**, and the third column the **To** state.
|
|
||||||
--
|
|
||||||
-- So, each of the rows have the following structure.
|
|
||||||
--
|
|
||||||
-- * **From** => **Event** => **To**
|
|
||||||
--
|
|
||||||
-- Important to know is that an event can only be executed if the **current state** is the **From** state.
|
|
||||||
-- This, when an **Event** that is being triggered has a **From** state that is equal to the **Current** state of the state machine, the event will be executed,
|
|
||||||
-- and the resulting state will be the **To** state.
|
|
||||||
--
|
|
||||||
-- These are the different possible state transitions of this state machine implementation:
|
|
||||||
--
|
|
||||||
-- * Idle => Start => Monitoring
|
|
||||||
--
|
|
||||||
-- ## 2.1) AI_A2G States.
|
|
||||||
--
|
|
||||||
-- * **Idle**: The process is idle.
|
|
||||||
--
|
|
||||||
-- ## 2.2) AI_A2G Events.
|
|
||||||
--
|
|
||||||
-- * **Start**: Start the transport process.
|
|
||||||
-- * **Stop**: Stop the transport process.
|
|
||||||
-- * **Monitor**: Monitor and take action.
|
|
||||||
--
|
|
||||||
-- @field #AI_A2G
|
|
||||||
AI_A2G = {
|
|
||||||
ClassName = "AI_A2G",
|
|
||||||
}
|
|
||||||
|
|
||||||
--- Creates a new AI_A2G process.
|
|
||||||
-- @param #AI_A2G self
|
|
||||||
-- @param Wrapper.Group#GROUP AIGroup The group object to receive the A2G Process.
|
|
||||||
-- @return #AI_A2G
|
|
||||||
function AI_A2G:New( AIGroup )
|
|
||||||
|
|
||||||
-- Inherits from BASE
|
|
||||||
local self = BASE:Inherit( self, AI_AIR:New( AIGroup ) ) -- #AI_A2G
|
|
||||||
|
|
||||||
self:SetFuelThreshold( .2, 60 )
|
|
||||||
self:SetDamageThreshold( 0.4 )
|
|
||||||
self:SetDisengageRadius( 70000 )
|
|
||||||
|
|
||||||
return self
|
|
||||||
end
|
|
||||||
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,440 +0,0 @@
|
|||||||
--- **AI** -- Models the process of air to ground engagement for airplanes and helicopters.
|
|
||||||
--
|
|
||||||
-- This is a class used in the @{AI_A2G_Dispatcher}.
|
|
||||||
--
|
|
||||||
-- ===
|
|
||||||
--
|
|
||||||
-- ### Author: **FlightControl**
|
|
||||||
--
|
|
||||||
-- ===
|
|
||||||
--
|
|
||||||
-- @module AI.AI_A2G_ENGAGE
|
|
||||||
-- @image AI_Ground_Control_Engage.JPG
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
--- @type AI_A2G_ENGAGE
|
|
||||||
-- @extends AI.AI_A2A#AI_A2A
|
|
||||||
|
|
||||||
|
|
||||||
--- Implements the core functions to intercept intruders. Use the Engage trigger to intercept intruders.
|
|
||||||
--
|
|
||||||
-- 
|
|
||||||
--
|
|
||||||
-- The AI_A2G_ENGAGE is assigned a @{Wrapper.Group} and this must be done before the AI_A2G_ENGAGE process can be started using the **Start** event.
|
|
||||||
--
|
|
||||||
-- 
|
|
||||||
--
|
|
||||||
-- The AI will fly towards the random 3D point within the patrol zone, using a random speed within the given altitude and speed limits.
|
|
||||||
-- Upon arrival at the 3D point, a new random 3D point will be selected within the patrol zone using the given limits.
|
|
||||||
--
|
|
||||||
-- 
|
|
||||||
--
|
|
||||||
-- This cycle will continue.
|
|
||||||
--
|
|
||||||
-- 
|
|
||||||
--
|
|
||||||
-- During the patrol, the AI will detect enemy targets, which are reported through the **Detected** event.
|
|
||||||
--
|
|
||||||
-- 
|
|
||||||
--
|
|
||||||
-- When enemies are detected, the AI will automatically engage the enemy.
|
|
||||||
--
|
|
||||||
-- 
|
|
||||||
--
|
|
||||||
-- Until a fuel or damage treshold has been reached by the AI, or when the AI is commanded to RTB.
|
|
||||||
-- When the fuel treshold has been reached, the airplane will fly towards the nearest friendly airbase and will land.
|
|
||||||
--
|
|
||||||
-- 
|
|
||||||
--
|
|
||||||
-- ## 1. AI_A2G_ENGAGE constructor
|
|
||||||
--
|
|
||||||
-- * @{#AI_A2G_ENGAGE.New}(): Creates a new AI_A2G_ENGAGE object.
|
|
||||||
--
|
|
||||||
-- ## 3. Set the Range of Engagement
|
|
||||||
--
|
|
||||||
-- 
|
|
||||||
--
|
|
||||||
-- An optional range can be set in meters,
|
|
||||||
-- that will define when the AI will engage with the detected airborne enemy targets.
|
|
||||||
-- The range can be beyond or smaller than the range of the Patrol Zone.
|
|
||||||
-- The range is applied at the position of the AI.
|
|
||||||
-- Use the method @{AI.AI_GCI#AI_A2G_ENGAGE.SetEngageRange}() to define that range.
|
|
||||||
--
|
|
||||||
-- ## 4. Set the Zone of Engagement
|
|
||||||
--
|
|
||||||
-- 
|
|
||||||
--
|
|
||||||
-- An optional @{Zone} can be set,
|
|
||||||
-- that will define when the AI will engage with the detected airborne enemy targets.
|
|
||||||
-- Use the method @{AI.AI_Cap#AI_A2G_ENGAGE.SetEngageZone}() to define that Zone.
|
|
||||||
--
|
|
||||||
-- ===
|
|
||||||
--
|
|
||||||
-- @field #AI_A2G_ENGAGE
|
|
||||||
AI_A2G_ENGAGE = {
|
|
||||||
ClassName = "AI_A2G_ENGAGE",
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
--- Creates a new AI_A2G_ENGAGE object
|
|
||||||
-- @param #AI_A2G_ENGAGE self
|
|
||||||
-- @param Wrapper.Group#GROUP AIGroup
|
|
||||||
-- @return #AI_A2G_ENGAGE
|
|
||||||
function AI_A2G_ENGAGE:New( AIGroup, EngageMinSpeed, EngageMaxSpeed )
|
|
||||||
|
|
||||||
-- Inherits from BASE
|
|
||||||
local self = BASE:Inherit( self, AI_A2G:New( AIGroup ) ) -- #AI_A2G_ENGAGE
|
|
||||||
|
|
||||||
self.Accomplished = false
|
|
||||||
self.Engaging = false
|
|
||||||
|
|
||||||
self.EngageMinSpeed = EngageMinSpeed
|
|
||||||
self.EngageMaxSpeed = EngageMaxSpeed
|
|
||||||
self.PatrolMinSpeed = EngageMinSpeed
|
|
||||||
self.PatrolMaxSpeed = EngageMaxSpeed
|
|
||||||
|
|
||||||
self.PatrolAltType = "RADIO"
|
|
||||||
|
|
||||||
self:AddTransition( { "Started", "Engaging", "Returning", "Airborne" }, "Engage", "Engaging" ) -- FSM_CONTROLLABLE Transition for type #AI_A2G_ENGAGE.
|
|
||||||
|
|
||||||
--- OnBefore Transition Handler for Event Engage.
|
|
||||||
-- @function [parent=#AI_A2G_ENGAGE] OnBeforeEngage
|
|
||||||
-- @param #AI_A2G_ENGAGE self
|
|
||||||
-- @param Wrapper.Group#GROUP AIGroup The Group Object managed by the FSM.
|
|
||||||
-- @param #string From The From State string.
|
|
||||||
-- @param #string Event The Event string.
|
|
||||||
-- @param #string To The To State string.
|
|
||||||
-- @return #boolean Return false to cancel Transition.
|
|
||||||
|
|
||||||
--- OnAfter Transition Handler for Event Engage.
|
|
||||||
-- @function [parent=#AI_A2G_ENGAGE] OnAfterEngage
|
|
||||||
-- @param #AI_A2G_ENGAGE self
|
|
||||||
-- @param Wrapper.Group#GROUP AIGroup The Group Object managed by the FSM.
|
|
||||||
-- @param #string From The From State string.
|
|
||||||
-- @param #string Event The Event string.
|
|
||||||
-- @param #string To The To State string.
|
|
||||||
|
|
||||||
--- Synchronous Event Trigger for Event Engage.
|
|
||||||
-- @function [parent=#AI_A2G_ENGAGE] Engage
|
|
||||||
-- @param #AI_A2G_ENGAGE self
|
|
||||||
|
|
||||||
--- Asynchronous Event Trigger for Event Engage.
|
|
||||||
-- @function [parent=#AI_A2G_ENGAGE] __Engage
|
|
||||||
-- @param #AI_A2G_ENGAGE self
|
|
||||||
-- @param #number Delay The delay in seconds.
|
|
||||||
|
|
||||||
--- OnLeave Transition Handler for State Engaging.
|
|
||||||
-- @function [parent=#AI_A2G_ENGAGE] OnLeaveEngaging
|
|
||||||
-- @param #AI_A2G_ENGAGE self
|
|
||||||
-- @param Wrapper.Group#GROUP AIGroup The Group Object managed by the FSM.
|
|
||||||
-- @param #string From The From State string.
|
|
||||||
-- @param #string Event The Event string.
|
|
||||||
-- @param #string To The To State string.
|
|
||||||
-- @return #boolean Return false to cancel Transition.
|
|
||||||
|
|
||||||
--- OnEnter Transition Handler for State Engaging.
|
|
||||||
-- @function [parent=#AI_A2G_ENGAGE] OnEnterEngaging
|
|
||||||
-- @param #AI_A2G_ENGAGE self
|
|
||||||
-- @param Wrapper.Group#GROUP AIGroup The Group Object managed by the FSM.
|
|
||||||
-- @param #string From The From State string.
|
|
||||||
-- @param #string Event The Event string.
|
|
||||||
-- @param #string To The To State string.
|
|
||||||
|
|
||||||
self:AddTransition( "Engaging", "Fired", "Engaging" ) -- FSM_CONTROLLABLE Transition for type #AI_A2G_ENGAGE.
|
|
||||||
|
|
||||||
--- OnBefore Transition Handler for Event Fired.
|
|
||||||
-- @function [parent=#AI_A2G_ENGAGE] OnBeforeFired
|
|
||||||
-- @param #AI_A2G_ENGAGE self
|
|
||||||
-- @param Wrapper.Group#GROUP AIGroup The Group Object managed by the FSM.
|
|
||||||
-- @param #string From The From State string.
|
|
||||||
-- @param #string Event The Event string.
|
|
||||||
-- @param #string To The To State string.
|
|
||||||
-- @return #boolean Return false to cancel Transition.
|
|
||||||
|
|
||||||
--- OnAfter Transition Handler for Event Fired.
|
|
||||||
-- @function [parent=#AI_A2G_ENGAGE] OnAfterFired
|
|
||||||
-- @param #AI_A2G_ENGAGE self
|
|
||||||
-- @param Wrapper.Group#GROUP AIGroup The Group Object managed by the FSM.
|
|
||||||
-- @param #string From The From State string.
|
|
||||||
-- @param #string Event The Event string.
|
|
||||||
-- @param #string To The To State string.
|
|
||||||
|
|
||||||
--- Synchronous Event Trigger for Event Fired.
|
|
||||||
-- @function [parent=#AI_A2G_ENGAGE] Fired
|
|
||||||
-- @param #AI_A2G_ENGAGE self
|
|
||||||
|
|
||||||
--- Asynchronous Event Trigger for Event Fired.
|
|
||||||
-- @function [parent=#AI_A2G_ENGAGE] __Fired
|
|
||||||
-- @param #AI_A2G_ENGAGE self
|
|
||||||
-- @param #number Delay The delay in seconds.
|
|
||||||
|
|
||||||
self:AddTransition( "*", "Destroy", "*" ) -- FSM_CONTROLLABLE Transition for type #AI_A2G_ENGAGE.
|
|
||||||
|
|
||||||
--- OnBefore Transition Handler for Event Destroy.
|
|
||||||
-- @function [parent=#AI_A2G_ENGAGE] OnBeforeDestroy
|
|
||||||
-- @param #AI_A2G_ENGAGE self
|
|
||||||
-- @param Wrapper.Group#GROUP AIGroup The Group Object managed by the FSM.
|
|
||||||
-- @param #string From The From State string.
|
|
||||||
-- @param #string Event The Event string.
|
|
||||||
-- @param #string To The To State string.
|
|
||||||
-- @return #boolean Return false to cancel Transition.
|
|
||||||
|
|
||||||
--- OnAfter Transition Handler for Event Destroy.
|
|
||||||
-- @function [parent=#AI_A2G_ENGAGE] OnAfterDestroy
|
|
||||||
-- @param #AI_A2G_ENGAGE self
|
|
||||||
-- @param Wrapper.Group#GROUP AIGroup The Group Object managed by the FSM.
|
|
||||||
-- @param #string From The From State string.
|
|
||||||
-- @param #string Event The Event string.
|
|
||||||
-- @param #string To The To State string.
|
|
||||||
|
|
||||||
--- Synchronous Event Trigger for Event Destroy.
|
|
||||||
-- @function [parent=#AI_A2G_ENGAGE] Destroy
|
|
||||||
-- @param #AI_A2G_ENGAGE self
|
|
||||||
|
|
||||||
--- Asynchronous Event Trigger for Event Destroy.
|
|
||||||
-- @function [parent=#AI_A2G_ENGAGE] __Destroy
|
|
||||||
-- @param #AI_A2G_ENGAGE self
|
|
||||||
-- @param #number Delay The delay in seconds.
|
|
||||||
|
|
||||||
|
|
||||||
self:AddTransition( "Engaging", "Abort", "Patrolling" ) -- FSM_CONTROLLABLE Transition for type #AI_A2G_ENGAGE.
|
|
||||||
|
|
||||||
--- OnBefore Transition Handler for Event Abort.
|
|
||||||
-- @function [parent=#AI_A2G_ENGAGE] OnBeforeAbort
|
|
||||||
-- @param #AI_A2G_ENGAGE self
|
|
||||||
-- @param Wrapper.Group#GROUP AIGroup The Group Object managed by the FSM.
|
|
||||||
-- @param #string From The From State string.
|
|
||||||
-- @param #string Event The Event string.
|
|
||||||
-- @param #string To The To State string.
|
|
||||||
-- @return #boolean Return false to cancel Transition.
|
|
||||||
|
|
||||||
--- OnAfter Transition Handler for Event Abort.
|
|
||||||
-- @function [parent=#AI_A2G_ENGAGE] OnAfterAbort
|
|
||||||
-- @param #AI_A2G_ENGAGE self
|
|
||||||
-- @param Wrapper.Group#GROUP AIGroup The Group Object managed by the FSM.
|
|
||||||
-- @param #string From The From State string.
|
|
||||||
-- @param #string Event The Event string.
|
|
||||||
-- @param #string To The To State string.
|
|
||||||
|
|
||||||
--- Synchronous Event Trigger for Event Abort.
|
|
||||||
-- @function [parent=#AI_A2G_ENGAGE] Abort
|
|
||||||
-- @param #AI_A2G_ENGAGE self
|
|
||||||
|
|
||||||
--- Asynchronous Event Trigger for Event Abort.
|
|
||||||
-- @function [parent=#AI_A2G_ENGAGE] __Abort
|
|
||||||
-- @param #AI_A2G_ENGAGE self
|
|
||||||
-- @param #number Delay The delay in seconds.
|
|
||||||
|
|
||||||
self:AddTransition( "Engaging", "Accomplish", "Patrolling" ) -- FSM_CONTROLLABLE Transition for type #AI_A2G_ENGAGE.
|
|
||||||
|
|
||||||
--- OnBefore Transition Handler for Event Accomplish.
|
|
||||||
-- @function [parent=#AI_A2G_ENGAGE] OnBeforeAccomplish
|
|
||||||
-- @param #AI_A2G_ENGAGE self
|
|
||||||
-- @param Wrapper.Group#GROUP AIGroup The Group Object managed by the FSM.
|
|
||||||
-- @param #string From The From State string.
|
|
||||||
-- @param #string Event The Event string.
|
|
||||||
-- @param #string To The To State string.
|
|
||||||
-- @return #boolean Return false to cancel Transition.
|
|
||||||
|
|
||||||
--- OnAfter Transition Handler for Event Accomplish.
|
|
||||||
-- @function [parent=#AI_A2G_ENGAGE] OnAfterAccomplish
|
|
||||||
-- @param #AI_A2G_ENGAGE self
|
|
||||||
-- @param Wrapper.Group#GROUP AIGroup The Group Object managed by the FSM.
|
|
||||||
-- @param #string From The From State string.
|
|
||||||
-- @param #string Event The Event string.
|
|
||||||
-- @param #string To The To State string.
|
|
||||||
|
|
||||||
--- Synchronous Event Trigger for Event Accomplish.
|
|
||||||
-- @function [parent=#AI_A2G_ENGAGE] Accomplish
|
|
||||||
-- @param #AI_A2G_ENGAGE self
|
|
||||||
|
|
||||||
--- Asynchronous Event Trigger for Event Accomplish.
|
|
||||||
-- @function [parent=#AI_A2G_ENGAGE] __Accomplish
|
|
||||||
-- @param #AI_A2G_ENGAGE self
|
|
||||||
-- @param #number Delay The delay in seconds.
|
|
||||||
|
|
||||||
return self
|
|
||||||
end
|
|
||||||
|
|
||||||
--- onafter event handler for Start event.
|
|
||||||
-- @param #AI_A2G_ENGAGE self
|
|
||||||
-- @param Wrapper.Group#GROUP AIGroup The AI group managed by the FSM.
|
|
||||||
-- @param #string From The From State string.
|
|
||||||
-- @param #string Event The Event string.
|
|
||||||
-- @param #string To The To State string.
|
|
||||||
function AI_A2G_ENGAGE:onafterStart( AIGroup, From, Event, To )
|
|
||||||
|
|
||||||
self:GetParent( self ).onafterStart( self, AIGroup, From, Event, To )
|
|
||||||
AIGroup:HandleEvent( EVENTS.Takeoff, nil, self )
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
--- onafter event handler for Engage event.
|
|
||||||
-- @param #AI_A2G_ENGAGE self
|
|
||||||
-- @param Wrapper.Group#GROUP AIGroup The AI Group managed by the FSM.
|
|
||||||
-- @param #string From The From State string.
|
|
||||||
-- @param #string Event The Event string.
|
|
||||||
-- @param #string To The To State string.
|
|
||||||
function AI_A2G_ENGAGE:onafterEngage( AIGroup, From, Event, To )
|
|
||||||
|
|
||||||
self:HandleEvent( EVENTS.Dead )
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
-- todo: need to fix this global function
|
|
||||||
|
|
||||||
--- @param Wrapper.Group#GROUP AIControllable
|
|
||||||
function AI_A2G_ENGAGE.EngageRoute( AIGroup, Fsm )
|
|
||||||
|
|
||||||
AIGroup:F( { "AI_A2G_ENGAGE.EngageRoute:", AIGroup:GetName() } )
|
|
||||||
|
|
||||||
if AIGroup:IsAlive() then
|
|
||||||
Fsm:__Engage( 0.5 )
|
|
||||||
|
|
||||||
--local Task = AIGroup:TaskOrbitCircle( 4000, 400 )
|
|
||||||
--AIGroup:SetTask( Task )
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
--- onbefore event handler for Engage event.
|
|
||||||
-- @param #AI_A2G_ENGAGE self
|
|
||||||
-- @param Wrapper.Group#GROUP AIGroup The group Object managed by the FSM.
|
|
||||||
-- @param #string From The From State string.
|
|
||||||
-- @param #string Event The Event string.
|
|
||||||
-- @param #string To The To State string.
|
|
||||||
function AI_A2G_ENGAGE:onbeforeEngage( AIGroup, From, Event, To )
|
|
||||||
|
|
||||||
if self.Accomplished == true then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
--- onafter event handler for Abort event.
|
|
||||||
-- @param #AI_A2G_ENGAGE self
|
|
||||||
-- @param Wrapper.Group#GROUP AIGroup The AI Group managed by the FSM.
|
|
||||||
-- @param #string From The From State string.
|
|
||||||
-- @param #string Event The Event string.
|
|
||||||
-- @param #string To The To State string.
|
|
||||||
function AI_A2G_ENGAGE:onafterAbort( AIGroup, From, Event, To )
|
|
||||||
AIGroup:ClearTasks()
|
|
||||||
self:Return()
|
|
||||||
self:__RTB( 0.5 )
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
--- @param #AI_A2G_ENGAGE self
|
|
||||||
-- @param Wrapper.Group#GROUP AIGroup The GroupGroup managed by the FSM.
|
|
||||||
-- @param #string From The From State string.
|
|
||||||
-- @param #string Event The Event string.
|
|
||||||
-- @param #string To The To State string.
|
|
||||||
function AI_A2G_ENGAGE:onafterEngage( AIGroup, From, Event, To, AttackSetUnit )
|
|
||||||
|
|
||||||
self:F( { AIGroup, From, Event, To, AttackSetUnit} )
|
|
||||||
|
|
||||||
self.AttackSetUnit = AttackSetUnit or self.AttackSetUnit -- Core.Set#SET_UNIT
|
|
||||||
|
|
||||||
local FirstAttackUnit = self.AttackSetUnit:GetFirst()
|
|
||||||
|
|
||||||
if FirstAttackUnit and FirstAttackUnit:IsAlive() then
|
|
||||||
|
|
||||||
if AIGroup:IsAlive() then
|
|
||||||
|
|
||||||
local EngageRoute = {}
|
|
||||||
|
|
||||||
local CurrentCoord = AIGroup:GetCoordinate()
|
|
||||||
|
|
||||||
--- Calculate the target route point.
|
|
||||||
|
|
||||||
local CurrentCoord = AIGroup:GetCoordinate()
|
|
||||||
|
|
||||||
local ToTargetCoord = self.AttackSetUnit:GetFirst():GetCoordinate()
|
|
||||||
self:SetTargetDistance( ToTargetCoord ) -- For RTB status check
|
|
||||||
|
|
||||||
local ToTargetSpeed = math.random( self.EngageMinSpeed, self.EngageMaxSpeed )
|
|
||||||
local ToEngageAngle = CurrentCoord:GetAngleDegrees( CurrentCoord:GetDirectionVec3( ToTargetCoord ) )
|
|
||||||
|
|
||||||
--- Create a route point of type air.
|
|
||||||
local ToPatrolRoutePoint = CurrentCoord:Translate( 15000, ToEngageAngle ):WaypointAir(
|
|
||||||
self.PatrolAltType,
|
|
||||||
POINT_VEC3.RoutePointType.TurningPoint,
|
|
||||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
|
||||||
ToTargetSpeed,
|
|
||||||
true
|
|
||||||
)
|
|
||||||
|
|
||||||
self:F( { Angle = ToEngageAngle, ToTargetSpeed = ToTargetSpeed } )
|
|
||||||
self:F( { self.EngageMinSpeed, self.EngageMaxSpeed, ToTargetSpeed } )
|
|
||||||
|
|
||||||
EngageRoute[#EngageRoute+1] = ToPatrolRoutePoint
|
|
||||||
EngageRoute[#EngageRoute+1] = ToPatrolRoutePoint
|
|
||||||
|
|
||||||
local AttackTasks = {}
|
|
||||||
|
|
||||||
for AttackUnitID, AttackUnit in pairs( self.AttackSetUnit:GetSet() ) do
|
|
||||||
local AttackUnit = AttackUnit -- Wrapper.Unit#UNIT
|
|
||||||
if AttackUnit:IsAlive() and AttackUnit:IsGround() then
|
|
||||||
self:T( { "Eliminating Unit:", AttackUnit:GetName(), AttackUnit:IsAlive(), AttackUnit:IsGround() } )
|
|
||||||
AttackTasks[#AttackTasks+1] = AIGroup:TaskAttackUnit( AttackUnit )
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if #AttackTasks == 0 then
|
|
||||||
self:E("No targets found -> Going RTB")
|
|
||||||
self:Return()
|
|
||||||
self:__RTB( 0.5 )
|
|
||||||
else
|
|
||||||
AIGroup:OptionROEOpenFire()
|
|
||||||
AIGroup:OptionROTEvadeFire()
|
|
||||||
|
|
||||||
AttackTasks[#AttackTasks+1] = AIGroup:TaskFunction( "AI_A2G_ENGAGE.EngageRoute", self )
|
|
||||||
EngageRoute[#EngageRoute].task = AIGroup:TaskCombo( AttackTasks )
|
|
||||||
end
|
|
||||||
|
|
||||||
AIGroup:Route( EngageRoute, 0.5 )
|
|
||||||
|
|
||||||
end
|
|
||||||
else
|
|
||||||
self:E("No targets found -> Going RTB")
|
|
||||||
self:Return()
|
|
||||||
self:__RTB( 0.5 )
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
--- @param #AI_A2G_ENGAGE self
|
|
||||||
-- @param Wrapper.Group#GROUP AIGroup The Group Object managed by the FSM.
|
|
||||||
-- @param #string From The From State string.
|
|
||||||
-- @param #string Event The Event string.
|
|
||||||
-- @param #string To The To State string.
|
|
||||||
function AI_A2G_ENGAGE:onafterAccomplish( AIGroup, From, Event, To )
|
|
||||||
self.Accomplished = true
|
|
||||||
self:SetDetectionOff()
|
|
||||||
end
|
|
||||||
|
|
||||||
--- @param #AI_A2G_ENGAGE self
|
|
||||||
-- @param Wrapper.Group#GROUP AIGroup The Group Object managed by the FSM.
|
|
||||||
-- @param #string From The From State string.
|
|
||||||
-- @param #string Event The Event string.
|
|
||||||
-- @param #string To The To State string.
|
|
||||||
-- @param Core.Event#EVENTDATA EventData
|
|
||||||
function AI_A2G_ENGAGE:onafterDestroy( AIGroup, From, Event, To, EventData )
|
|
||||||
|
|
||||||
if EventData.IniUnit then
|
|
||||||
self.AttackUnits[EventData.IniUnit] = nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
--- @param #AI_A2G_ENGAGE self
|
|
||||||
-- @param Core.Event#EVENTDATA EventData
|
|
||||||
function AI_A2G_ENGAGE:OnEventDead( EventData )
|
|
||||||
self:F( { "EventDead", EventData } )
|
|
||||||
|
|
||||||
if EventData.IniDCSUnit then
|
|
||||||
if self.AttackUnits and self.AttackUnits[EventData.IniUnit] then
|
|
||||||
self:__Destroy( 1, EventData )
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@@ -1,732 +0,0 @@
|
|||||||
--- **AI** -- Models the process of AI air operations.
|
|
||||||
--
|
|
||||||
-- ===
|
|
||||||
--
|
|
||||||
-- ### Author: **FlightControl**
|
|
||||||
--
|
|
||||||
-- ===
|
|
||||||
--
|
|
||||||
-- @module AI.AI_Air
|
|
||||||
-- @image AI_Air_Operations.JPG
|
|
||||||
|
|
||||||
--- @type AI_AIR
|
|
||||||
-- @extends Core.Fsm#FSM_CONTROLLABLE
|
|
||||||
|
|
||||||
--- The AI_AIR class implements the core functions to operate an AI @{Wrapper.Group}.
|
|
||||||
--
|
|
||||||
--
|
|
||||||
-- # 1) AI_AIR constructor
|
|
||||||
--
|
|
||||||
-- * @{#AI_AIR.New}(): Creates a new AI_AIR object.
|
|
||||||
--
|
|
||||||
-- # 2) AI_AIR is a Finite State Machine.
|
|
||||||
--
|
|
||||||
-- This section must be read as follows. Each of the rows indicate a state transition, triggered through an event, and with an ending state of the event was executed.
|
|
||||||
-- The first column is the **From** state, the second column the **Event**, and the third column the **To** state.
|
|
||||||
--
|
|
||||||
-- So, each of the rows have the following structure.
|
|
||||||
--
|
|
||||||
-- * **From** => **Event** => **To**
|
|
||||||
--
|
|
||||||
-- Important to know is that an event can only be executed if the **current state** is the **From** state.
|
|
||||||
-- This, when an **Event** that is being triggered has a **From** state that is equal to the **Current** state of the state machine, the event will be executed,
|
|
||||||
-- and the resulting state will be the **To** state.
|
|
||||||
--
|
|
||||||
-- These are the different possible state transitions of this state machine implementation:
|
|
||||||
--
|
|
||||||
-- * Idle => Start => Monitoring
|
|
||||||
--
|
|
||||||
-- ## 2.1) AI_AIR States.
|
|
||||||
--
|
|
||||||
-- * **Idle**: The process is idle.
|
|
||||||
--
|
|
||||||
-- ## 2.2) AI_AIR Events.
|
|
||||||
--
|
|
||||||
-- * **Start**: Start the transport process.
|
|
||||||
-- * **Stop**: Stop the transport process.
|
|
||||||
-- * **Monitor**: Monitor and take action.
|
|
||||||
--
|
|
||||||
-- @field #AI_AIR
|
|
||||||
AI_AIR = {
|
|
||||||
ClassName = "AI_AIR",
|
|
||||||
}
|
|
||||||
|
|
||||||
--- Creates a new AI_AIR process.
|
|
||||||
-- @param #AI_AIR self
|
|
||||||
-- @param Wrapper.Group#GROUP AIGroup The group object to receive the A2G Process.
|
|
||||||
-- @return #AI_AIR
|
|
||||||
function AI_AIR:New( AIGroup )
|
|
||||||
|
|
||||||
-- Inherits from BASE
|
|
||||||
local self = BASE:Inherit( self, FSM_CONTROLLABLE:New() ) -- #AI_AIR
|
|
||||||
|
|
||||||
self:SetControllable( AIGroup )
|
|
||||||
|
|
||||||
self:SetStartState( "Stopped" )
|
|
||||||
|
|
||||||
self:AddTransition( "*", "Start", "Started" )
|
|
||||||
|
|
||||||
--- Start Handler OnBefore for AI_AIR
|
|
||||||
-- @function [parent=#AI_AIR] OnBeforeStart
|
|
||||||
-- @param #AI_AIR self
|
|
||||||
-- @param #string From
|
|
||||||
-- @param #string Event
|
|
||||||
-- @param #string To
|
|
||||||
-- @return #boolean
|
|
||||||
|
|
||||||
--- Start Handler OnAfter for AI_AIR
|
|
||||||
-- @function [parent=#AI_AIR] OnAfterStart
|
|
||||||
-- @param #AI_AIR self
|
|
||||||
-- @param #string From
|
|
||||||
-- @param #string Event
|
|
||||||
-- @param #string To
|
|
||||||
|
|
||||||
--- Start Trigger for AI_AIR
|
|
||||||
-- @function [parent=#AI_AIR] Start
|
|
||||||
-- @param #AI_AIR self
|
|
||||||
|
|
||||||
--- Start Asynchronous Trigger for AI_AIR
|
|
||||||
-- @function [parent=#AI_AIR] __Start
|
|
||||||
-- @param #AI_AIR self
|
|
||||||
-- @param #number Delay
|
|
||||||
|
|
||||||
self:AddTransition( "*", "Stop", "Stopped" )
|
|
||||||
|
|
||||||
--- OnLeave Transition Handler for State Stopped.
|
|
||||||
-- @function [parent=#AI_AIR] OnLeaveStopped
|
|
||||||
-- @param #AI_AIR self
|
|
||||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
|
||||||
-- @param #string From The From State string.
|
|
||||||
-- @param #string Event The Event string.
|
|
||||||
-- @param #string To The To State string.
|
|
||||||
-- @return #boolean Return false to cancel Transition.
|
|
||||||
|
|
||||||
--- OnEnter Transition Handler for State Stopped.
|
|
||||||
-- @function [parent=#AI_AIR] OnEnterStopped
|
|
||||||
-- @param #AI_AIR self
|
|
||||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
|
||||||
-- @param #string From The From State string.
|
|
||||||
-- @param #string Event The Event string.
|
|
||||||
-- @param #string To The To State string.
|
|
||||||
|
|
||||||
--- OnBefore Transition Handler for Event Stop.
|
|
||||||
-- @function [parent=#AI_AIR] OnBeforeStop
|
|
||||||
-- @param #AI_AIR self
|
|
||||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
|
||||||
-- @param #string From The From State string.
|
|
||||||
-- @param #string Event The Event string.
|
|
||||||
-- @param #string To The To State string.
|
|
||||||
-- @return #boolean Return false to cancel Transition.
|
|
||||||
|
|
||||||
--- OnAfter Transition Handler for Event Stop.
|
|
||||||
-- @function [parent=#AI_AIR] OnAfterStop
|
|
||||||
-- @param #AI_AIR self
|
|
||||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
|
||||||
-- @param #string From The From State string.
|
|
||||||
-- @param #string Event The Event string.
|
|
||||||
-- @param #string To The To State string.
|
|
||||||
|
|
||||||
--- Synchronous Event Trigger for Event Stop.
|
|
||||||
-- @function [parent=#AI_AIR] Stop
|
|
||||||
-- @param #AI_AIR self
|
|
||||||
|
|
||||||
--- Asynchronous Event Trigger for Event Stop.
|
|
||||||
-- @function [parent=#AI_AIR] __Stop
|
|
||||||
-- @param #AI_AIR self
|
|
||||||
-- @param #number Delay The delay in seconds.
|
|
||||||
|
|
||||||
self:AddTransition( "*", "Status", "*" ) -- FSM_CONTROLLABLE Transition for type #AI_AIR.
|
|
||||||
|
|
||||||
--- OnBefore Transition Handler for Event Status.
|
|
||||||
-- @function [parent=#AI_AIR] OnBeforeStatus
|
|
||||||
-- @param #AI_AIR self
|
|
||||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
|
||||||
-- @param #string From The From State string.
|
|
||||||
-- @param #string Event The Event string.
|
|
||||||
-- @param #string To The To State string.
|
|
||||||
-- @return #boolean Return false to cancel Transition.
|
|
||||||
|
|
||||||
--- OnAfter Transition Handler for Event Status.
|
|
||||||
-- @function [parent=#AI_AIR] OnAfterStatus
|
|
||||||
-- @param #AI_AIR self
|
|
||||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
|
||||||
-- @param #string From The From State string.
|
|
||||||
-- @param #string Event The Event string.
|
|
||||||
-- @param #string To The To State string.
|
|
||||||
|
|
||||||
--- Synchronous Event Trigger for Event Status.
|
|
||||||
-- @function [parent=#AI_AIR] Status
|
|
||||||
-- @param #AI_AIR self
|
|
||||||
|
|
||||||
--- Asynchronous Event Trigger for Event Status.
|
|
||||||
-- @function [parent=#AI_AIR] __Status
|
|
||||||
-- @param #AI_AIR self
|
|
||||||
-- @param #number Delay The delay in seconds.
|
|
||||||
|
|
||||||
self:AddTransition( "*", "RTB", "*" ) -- FSM_CONTROLLABLE Transition for type #AI_AIR.
|
|
||||||
|
|
||||||
--- OnBefore Transition Handler for Event RTB.
|
|
||||||
-- @function [parent=#AI_AIR] OnBeforeRTB
|
|
||||||
-- @param #AI_AIR self
|
|
||||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
|
||||||
-- @param #string From The From State string.
|
|
||||||
-- @param #string Event The Event string.
|
|
||||||
-- @param #string To The To State string.
|
|
||||||
-- @return #boolean Return false to cancel Transition.
|
|
||||||
|
|
||||||
--- OnAfter Transition Handler for Event RTB.
|
|
||||||
-- @function [parent=#AI_AIR] OnAfterRTB
|
|
||||||
-- @param #AI_AIR self
|
|
||||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
|
||||||
-- @param #string From The From State string.
|
|
||||||
-- @param #string Event The Event string.
|
|
||||||
-- @param #string To The To State string.
|
|
||||||
|
|
||||||
--- Synchronous Event Trigger for Event RTB.
|
|
||||||
-- @function [parent=#AI_AIR] RTB
|
|
||||||
-- @param #AI_AIR self
|
|
||||||
|
|
||||||
--- Asynchronous Event Trigger for Event RTB.
|
|
||||||
-- @function [parent=#AI_AIR] __RTB
|
|
||||||
-- @param #AI_AIR self
|
|
||||||
-- @param #number Delay The delay in seconds.
|
|
||||||
|
|
||||||
--- OnLeave Transition Handler for State Returning.
|
|
||||||
-- @function [parent=#AI_AIR] OnLeaveReturning
|
|
||||||
-- @param #AI_AIR self
|
|
||||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
|
||||||
-- @param #string From The From State string.
|
|
||||||
-- @param #string Event The Event string.
|
|
||||||
-- @param #string To The To State string.
|
|
||||||
-- @return #boolean Return false to cancel Transition.
|
|
||||||
|
|
||||||
--- OnEnter Transition Handler for State Returning.
|
|
||||||
-- @function [parent=#AI_AIR] OnEnterReturning
|
|
||||||
-- @param #AI_AIR self
|
|
||||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
|
||||||
-- @param #string From The From State string.
|
|
||||||
-- @param #string Event The Event string.
|
|
||||||
-- @param #string To The To State string.
|
|
||||||
|
|
||||||
self:AddTransition( "Patrolling", "Refuel", "Refuelling" )
|
|
||||||
|
|
||||||
--- Refuel Handler OnBefore for AI_AIR
|
|
||||||
-- @function [parent=#AI_AIR] OnBeforeRefuel
|
|
||||||
-- @param #AI_AIR self
|
|
||||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
|
||||||
-- @param #string From
|
|
||||||
-- @param #string Event
|
|
||||||
-- @param #string To
|
|
||||||
-- @return #boolean
|
|
||||||
|
|
||||||
--- Refuel Handler OnAfter for AI_AIR
|
|
||||||
-- @function [parent=#AI_AIR] OnAfterRefuel
|
|
||||||
-- @param #AI_AIR self
|
|
||||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
|
||||||
-- @param #string From
|
|
||||||
-- @param #string Event
|
|
||||||
-- @param #string To
|
|
||||||
|
|
||||||
--- Refuel Trigger for AI_AIR
|
|
||||||
-- @function [parent=#AI_AIR] Refuel
|
|
||||||
-- @param #AI_AIR self
|
|
||||||
|
|
||||||
--- Refuel Asynchronous Trigger for AI_AIR
|
|
||||||
-- @function [parent=#AI_AIR] __Refuel
|
|
||||||
-- @param #AI_AIR self
|
|
||||||
-- @param #number Delay
|
|
||||||
|
|
||||||
self:AddTransition( "*", "Takeoff", "Airborne" )
|
|
||||||
self:AddTransition( "*", "Return", "Returning" )
|
|
||||||
self:AddTransition( "*", "Hold", "Holding" )
|
|
||||||
self:AddTransition( "*", "Home", "Home" )
|
|
||||||
self:AddTransition( "*", "LostControl", "LostControl" )
|
|
||||||
self:AddTransition( "*", "Fuel", "Fuel" )
|
|
||||||
self:AddTransition( "*", "Damaged", "Damaged" )
|
|
||||||
self:AddTransition( "*", "Eject", "*" )
|
|
||||||
self:AddTransition( "*", "Crash", "Crashed" )
|
|
||||||
self:AddTransition( "*", "PilotDead", "*" )
|
|
||||||
|
|
||||||
self.IdleCount = 0
|
|
||||||
|
|
||||||
return self
|
|
||||||
end
|
|
||||||
|
|
||||||
--- @param Wrapper.Group#GROUP self
|
|
||||||
-- @param Core.Event#EVENTDATA EventData
|
|
||||||
function GROUP:OnEventTakeoff( EventData, Fsm )
|
|
||||||
Fsm:Takeoff()
|
|
||||||
self:UnHandleEvent( EVENTS.Takeoff )
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function AI_AIR:SetDispatcher( Dispatcher )
|
|
||||||
self.Dispatcher = Dispatcher
|
|
||||||
end
|
|
||||||
|
|
||||||
function AI_AIR:GetDispatcher()
|
|
||||||
return self.Dispatcher
|
|
||||||
end
|
|
||||||
|
|
||||||
function AI_AIR:SetTargetDistance( Coordinate )
|
|
||||||
|
|
||||||
local CurrentCoord = self.Controllable:GetCoordinate()
|
|
||||||
self.TargetDistance = CurrentCoord:Get2DDistance( Coordinate )
|
|
||||||
|
|
||||||
self.ClosestTargetDistance = ( not self.ClosestTargetDistance or self.ClosestTargetDistance > self.TargetDistance ) and self.TargetDistance or self.ClosestTargetDistance
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function AI_AIR:ClearTargetDistance()
|
|
||||||
|
|
||||||
self.TargetDistance = nil
|
|
||||||
self.ClosestTargetDistance = nil
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
--- Sets (modifies) the minimum and maximum speed of the patrol.
|
|
||||||
-- @param #AI_AIR self
|
|
||||||
-- @param DCS#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Controllable} in km/h.
|
|
||||||
-- @param DCS#Speed PatrolMaxSpeed The maximum speed of the @{Wrapper.Controllable} in km/h.
|
|
||||||
-- @return #AI_AIR self
|
|
||||||
function AI_AIR:SetSpeed( PatrolMinSpeed, PatrolMaxSpeed )
|
|
||||||
self:F2( { PatrolMinSpeed, PatrolMaxSpeed } )
|
|
||||||
|
|
||||||
self.PatrolMinSpeed = PatrolMinSpeed
|
|
||||||
self.PatrolMaxSpeed = PatrolMaxSpeed
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
--- Sets the floor and ceiling altitude of the patrol.
|
|
||||||
-- @param #AI_AIR self
|
|
||||||
-- @param DCS#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol.
|
|
||||||
-- @param DCS#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol.
|
|
||||||
-- @return #AI_AIR self
|
|
||||||
function AI_AIR:SetAltitude( PatrolFloorAltitude, PatrolCeilingAltitude )
|
|
||||||
self:F2( { PatrolFloorAltitude, PatrolCeilingAltitude } )
|
|
||||||
|
|
||||||
self.PatrolFloorAltitude = PatrolFloorAltitude
|
|
||||||
self.PatrolCeilingAltitude = PatrolCeilingAltitude
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
--- Sets the home airbase.
|
|
||||||
-- @param #AI_AIR self
|
|
||||||
-- @param Wrapper.Airbase#AIRBASE HomeAirbase
|
|
||||||
-- @return #AI_AIR self
|
|
||||||
function AI_AIR:SetHomeAirbase( HomeAirbase )
|
|
||||||
self:F2( { HomeAirbase } )
|
|
||||||
|
|
||||||
self.HomeAirbase = HomeAirbase
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Sets to refuel at the given tanker.
|
|
||||||
-- @param #AI_AIR self
|
|
||||||
-- @param Wrapper.Group#GROUP TankerName The group name of the tanker as defined within the Mission Editor or spawned.
|
|
||||||
-- @return #AI_AIR self
|
|
||||||
function AI_AIR:SetTanker( TankerName )
|
|
||||||
self:F2( { TankerName } )
|
|
||||||
|
|
||||||
self.TankerName = TankerName
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
--- Sets the disengage range, that when engaging a target beyond the specified range, the engagement will be cancelled and the plane will RTB.
|
|
||||||
-- @param #AI_AIR self
|
|
||||||
-- @param #number DisengageRadius The disengage range.
|
|
||||||
-- @return #AI_AIR self
|
|
||||||
function AI_AIR:SetDisengageRadius( DisengageRadius )
|
|
||||||
self:F2( { DisengageRadius } )
|
|
||||||
|
|
||||||
self.DisengageRadius = DisengageRadius
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Set the status checking off.
|
|
||||||
-- @param #AI_AIR self
|
|
||||||
-- @return #AI_AIR self
|
|
||||||
function AI_AIR:SetStatusOff()
|
|
||||||
self:F2()
|
|
||||||
|
|
||||||
self.CheckStatus = false
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
--- When the AI is out of fuel, it is required that a new AI is started, before the old AI can return to the home base.
|
|
||||||
-- Therefore, with a parameter and a calculation of the distance to the home base, the fuel treshold is calculated.
|
|
||||||
-- When the fuel treshold is reached, the AI will continue for a given time its patrol task in orbit, while a new AIControllable is targetted to the AI_AIR.
|
|
||||||
-- Once the time is finished, the old AI will return to the base.
|
|
||||||
-- @param #AI_AIR self
|
|
||||||
-- @param #number FuelThresholdPercentage The treshold in percentage (between 0 and 1) when the AIControllable is considered to get out of fuel.
|
|
||||||
-- @param #number OutOfFuelOrbitTime The amount of seconds the out of fuel AIControllable will orbit before returning to the base.
|
|
||||||
-- @return #AI_AIR self
|
|
||||||
function AI_AIR:SetFuelThreshold( FuelThresholdPercentage, OutOfFuelOrbitTime )
|
|
||||||
|
|
||||||
self.FuelThresholdPercentage = FuelThresholdPercentage
|
|
||||||
self.OutOfFuelOrbitTime = OutOfFuelOrbitTime
|
|
||||||
|
|
||||||
self.Controllable:OptionRTBBingoFuel( false )
|
|
||||||
|
|
||||||
return self
|
|
||||||
end
|
|
||||||
|
|
||||||
--- When the AI is damaged beyond a certain treshold, it is required that the AI returns to the home base.
|
|
||||||
-- However, damage cannot be foreseen early on.
|
|
||||||
-- Therefore, when the damage treshold is reached,
|
|
||||||
-- the AI will return immediately to the home base (RTB).
|
|
||||||
-- Note that for groups, the average damage of the complete group will be calculated.
|
|
||||||
-- So, in a group of 4 airplanes, 2 lost and 2 with damage 0.2, the damage treshold will be 0.25.
|
|
||||||
-- @param #AI_AIR self
|
|
||||||
-- @param #number PatrolDamageThreshold The treshold in percentage (between 0 and 1) when the AI is considered to be damaged.
|
|
||||||
-- @return #AI_AIR self
|
|
||||||
function AI_AIR:SetDamageThreshold( PatrolDamageThreshold )
|
|
||||||
|
|
||||||
self.PatrolManageDamage = true
|
|
||||||
self.PatrolDamageThreshold = PatrolDamageThreshold
|
|
||||||
|
|
||||||
return self
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Defines a new patrol route using the @{Process_PatrolZone} parameters and settings.
|
|
||||||
-- @param #AI_AIR self
|
|
||||||
-- @return #AI_AIR self
|
|
||||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
|
||||||
-- @param #string From The From State string.
|
|
||||||
-- @param #string Event The Event string.
|
|
||||||
-- @param #string To The To State string.
|
|
||||||
function AI_AIR:onafterStart( Controllable, From, Event, To )
|
|
||||||
|
|
||||||
self:__Status( 10 ) -- Check status status every 30 seconds.
|
|
||||||
|
|
||||||
self:HandleEvent( EVENTS.PilotDead, self.OnPilotDead )
|
|
||||||
self:HandleEvent( EVENTS.Crash, self.OnCrash )
|
|
||||||
self:HandleEvent( EVENTS.Ejection, self.OnEjection )
|
|
||||||
|
|
||||||
Controllable:OptionROEHoldFire()
|
|
||||||
Controllable:OptionROTVertical()
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
--- @param #AI_AIR self
|
|
||||||
function AI_AIR:onbeforeStatus()
|
|
||||||
|
|
||||||
return self.CheckStatus
|
|
||||||
end
|
|
||||||
|
|
||||||
--- @param #AI_AIR self
|
|
||||||
function AI_AIR:onafterStatus()
|
|
||||||
|
|
||||||
if self.Controllable and self.Controllable:IsAlive() then
|
|
||||||
|
|
||||||
local RTB = false
|
|
||||||
|
|
||||||
local DistanceFromHomeBase = self.HomeAirbase:GetCoordinate():Get2DDistance( self.Controllable:GetCoordinate() )
|
|
||||||
|
|
||||||
if not self:Is( "Holding" ) and not self:Is( "Returning" ) then
|
|
||||||
local DistanceFromHomeBase = self.HomeAirbase:GetCoordinate():Get2DDistance( self.Controllable:GetCoordinate() )
|
|
||||||
self:F({DistanceFromHomeBase=DistanceFromHomeBase})
|
|
||||||
|
|
||||||
if DistanceFromHomeBase > self.DisengageRadius then
|
|
||||||
self:E( self.Controllable:GetName() .. " is too far from home base, RTB!" )
|
|
||||||
self:Hold( 300 )
|
|
||||||
RTB = false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- I think this code is not requirement anymore after release 2.5.
|
|
||||||
-- if self:Is( "Fuel" ) or self:Is( "Damaged" ) or self:Is( "LostControl" ) then
|
|
||||||
-- if DistanceFromHomeBase < 5000 then
|
|
||||||
-- self:E( self.Controllable:GetName() .. " is near the home base, RTB!" )
|
|
||||||
-- self:Home( "Destroy" )
|
|
||||||
-- end
|
|
||||||
-- end
|
|
||||||
|
|
||||||
|
|
||||||
if not self:Is( "Fuel" ) and not self:Is( "Home" ) then
|
|
||||||
local Fuel = self.Controllable:GetFuelMin()
|
|
||||||
self:F({Fuel=Fuel, FuelThresholdPercentage=self.FuelThresholdPercentage})
|
|
||||||
if Fuel < self.FuelThresholdPercentage then
|
|
||||||
if self.TankerName then
|
|
||||||
self:E( self.Controllable:GetName() .. " is out of fuel: " .. Fuel .. " ... Refuelling at Tanker!" )
|
|
||||||
self:Refuel()
|
|
||||||
else
|
|
||||||
self:E( self.Controllable:GetName() .. " is out of fuel: " .. Fuel .. " ... RTB!" )
|
|
||||||
local OldAIControllable = self.Controllable
|
|
||||||
|
|
||||||
local OrbitTask = OldAIControllable:TaskOrbitCircle( math.random( self.PatrolFloorAltitude, self.PatrolCeilingAltitude ), self.PatrolMinSpeed )
|
|
||||||
local TimedOrbitTask = OldAIControllable:TaskControlled( OrbitTask, OldAIControllable:TaskCondition(nil,nil,nil,nil,self.OutOfFuelOrbitTime,nil ) )
|
|
||||||
OldAIControllable:SetTask( TimedOrbitTask, 10 )
|
|
||||||
|
|
||||||
self:Fuel()
|
|
||||||
RTB = true
|
|
||||||
end
|
|
||||||
else
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- TODO: Check GROUP damage function.
|
|
||||||
local Damage = self.Controllable:GetLife()
|
|
||||||
local InitialLife = self.Controllable:GetLife0()
|
|
||||||
self:F( { Damage = Damage, InitialLife = InitialLife, DamageThreshold = self.PatrolDamageThreshold } )
|
|
||||||
if ( Damage / InitialLife ) < self.PatrolDamageThreshold then
|
|
||||||
self:E( self.Controllable:GetName() .. " is damaged: " .. Damage .. " ... RTB!" )
|
|
||||||
self:Damaged()
|
|
||||||
RTB = true
|
|
||||||
self:SetStatusOff()
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Check if planes went RTB and are out of control.
|
|
||||||
-- We only check if planes are out of control, when they are in duty.
|
|
||||||
if self.Controllable:HasTask() == false then
|
|
||||||
if not self:Is( "Started" ) and
|
|
||||||
not self:Is( "Stopped" ) and
|
|
||||||
not self:Is( "Fuel" ) and
|
|
||||||
not self:Is( "Damaged" ) and
|
|
||||||
not self:Is( "Home" ) then
|
|
||||||
if self.IdleCount >= 2 then
|
|
||||||
if Damage ~= InitialLife then
|
|
||||||
self:Damaged()
|
|
||||||
else
|
|
||||||
self:E( self.Controllable:GetName() .. " control lost! " )
|
|
||||||
self:LostControl()
|
|
||||||
end
|
|
||||||
else
|
|
||||||
self.IdleCount = self.IdleCount + 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
else
|
|
||||||
self.IdleCount = 0
|
|
||||||
end
|
|
||||||
|
|
||||||
if RTB == true then
|
|
||||||
self:__RTB( 0.5 )
|
|
||||||
end
|
|
||||||
|
|
||||||
if not self:Is("Home") then
|
|
||||||
self:__Status( 10 )
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
--- @param Wrapper.Group#GROUP AIGroup
|
|
||||||
function AI_AIR.RTBRoute( AIGroup, Fsm )
|
|
||||||
|
|
||||||
AIGroup:F( { "AI_AIR.RTBRoute:", AIGroup:GetName() } )
|
|
||||||
|
|
||||||
if AIGroup:IsAlive() then
|
|
||||||
Fsm:__RTB( 0.5 )
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
--- @param Wrapper.Group#GROUP AIGroup
|
|
||||||
function AI_AIR.RTBHold( AIGroup, Fsm )
|
|
||||||
|
|
||||||
AIGroup:F( { "AI_AIR.RTBHold:", AIGroup:GetName() } )
|
|
||||||
if AIGroup:IsAlive() then
|
|
||||||
Fsm:__RTB( 0.5 )
|
|
||||||
Fsm:Return()
|
|
||||||
local Task = AIGroup:TaskOrbitCircle( 4000, 400 )
|
|
||||||
AIGroup:SetTask( Task )
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
--- @param #AI_AIR self
|
|
||||||
-- @param Wrapper.Group#GROUP AIGroup
|
|
||||||
function AI_AIR:onafterRTB( AIGroup, From, Event, To )
|
|
||||||
self:F( { AIGroup, From, Event, To } )
|
|
||||||
|
|
||||||
|
|
||||||
if AIGroup and AIGroup:IsAlive() then
|
|
||||||
|
|
||||||
self:E( "Group " .. AIGroup:GetName() .. " ... RTB! ( " .. self:GetState() .. " )" )
|
|
||||||
|
|
||||||
self:ClearTargetDistance()
|
|
||||||
AIGroup:ClearTasks()
|
|
||||||
|
|
||||||
local EngageRoute = {}
|
|
||||||
|
|
||||||
--- Calculate the target route point.
|
|
||||||
|
|
||||||
local CurrentCoord = AIGroup:GetCoordinate()
|
|
||||||
local ToTargetCoord = self.HomeAirbase:GetCoordinate()
|
|
||||||
local ToTargetSpeed = math.random( self.PatrolMinSpeed, self.PatrolMaxSpeed )
|
|
||||||
local ToAirbaseAngle = CurrentCoord:GetAngleDegrees( CurrentCoord:GetDirectionVec3( ToTargetCoord ) )
|
|
||||||
|
|
||||||
local Distance = CurrentCoord:Get2DDistance( ToTargetCoord )
|
|
||||||
|
|
||||||
local ToAirbaseCoord = CurrentCoord:Translate( 5000, ToAirbaseAngle )
|
|
||||||
if Distance < 5000 then
|
|
||||||
self:E( "RTB and near the airbase!" )
|
|
||||||
self:Home()
|
|
||||||
return
|
|
||||||
end
|
|
||||||
--- Create a route point of type air.
|
|
||||||
local ToRTBRoutePoint = ToAirbaseCoord:WaypointAir(
|
|
||||||
self.PatrolAltType,
|
|
||||||
POINT_VEC3.RoutePointType.TurningPoint,
|
|
||||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
|
||||||
ToTargetSpeed,
|
|
||||||
true
|
|
||||||
)
|
|
||||||
|
|
||||||
self:F( { Angle = ToAirbaseAngle, ToTargetSpeed = ToTargetSpeed } )
|
|
||||||
self:T2( { self.MinSpeed, self.MaxSpeed, ToTargetSpeed } )
|
|
||||||
|
|
||||||
EngageRoute[#EngageRoute+1] = ToRTBRoutePoint
|
|
||||||
EngageRoute[#EngageRoute+1] = ToRTBRoutePoint
|
|
||||||
|
|
||||||
AIGroup:OptionROEHoldFire()
|
|
||||||
AIGroup:OptionROTEvadeFire()
|
|
||||||
|
|
||||||
--- Now we're going to do something special, we're going to call a function from a waypoint action at the AIControllable...
|
|
||||||
AIGroup:WayPointInitialize( EngageRoute )
|
|
||||||
|
|
||||||
local Tasks = {}
|
|
||||||
Tasks[#Tasks+1] = AIGroup:TaskFunction( "AI_AIR.RTBRoute", self )
|
|
||||||
EngageRoute[#EngageRoute].task = AIGroup:TaskCombo( Tasks )
|
|
||||||
|
|
||||||
--- NOW ROUTE THE GROUP!
|
|
||||||
AIGroup:Route( EngageRoute, 0.5 )
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
--- @param #AI_AIR self
|
|
||||||
-- @param Wrapper.Group#GROUP AIGroup
|
|
||||||
function AI_AIR:onafterHome( AIGroup, From, Event, To )
|
|
||||||
self:F( { AIGroup, From, Event, To } )
|
|
||||||
|
|
||||||
self:E( "Group " .. self.Controllable:GetName() .. " ... Home! ( " .. self:GetState() .. " )" )
|
|
||||||
|
|
||||||
if AIGroup and AIGroup:IsAlive() then
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
--- @param #AI_AIR self
|
|
||||||
-- @param Wrapper.Group#GROUP AIGroup
|
|
||||||
function AI_AIR:onafterHold( AIGroup, From, Event, To, HoldTime )
|
|
||||||
self:F( { AIGroup, From, Event, To } )
|
|
||||||
|
|
||||||
self:E( "Group " .. self.Controllable:GetName() .. " ... Holding! ( " .. self:GetState() .. " )" )
|
|
||||||
|
|
||||||
if AIGroup and AIGroup:IsAlive() then
|
|
||||||
local OrbitTask = AIGroup:TaskOrbitCircle( math.random( self.PatrolFloorAltitude, self.PatrolCeilingAltitude ), self.PatrolMinSpeed )
|
|
||||||
local TimedOrbitTask = AIGroup:TaskControlled( OrbitTask, AIGroup:TaskCondition( nil, nil, nil, nil, HoldTime , nil ) )
|
|
||||||
|
|
||||||
local RTBTask = AIGroup:TaskFunction( "AI_AIR.RTBHold", self )
|
|
||||||
|
|
||||||
local OrbitHoldTask = AIGroup:TaskOrbitCircle( 4000, self.PatrolMinSpeed )
|
|
||||||
|
|
||||||
--AIGroup:SetState( AIGroup, "AI_AIR", self )
|
|
||||||
|
|
||||||
AIGroup:SetTask( AIGroup:TaskCombo( { TimedOrbitTask, RTBTask, OrbitHoldTask } ), 1 )
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
--- @param Wrapper.Group#GROUP AIGroup
|
|
||||||
function AI_AIR.Resume( AIGroup, Fsm )
|
|
||||||
|
|
||||||
AIGroup:I( { "AI_AIR.Resume:", AIGroup:GetName() } )
|
|
||||||
if AIGroup:IsAlive() then
|
|
||||||
Fsm:__RTB( 0.5 )
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
--- @param #AI_AIR self
|
|
||||||
-- @param Wrapper.Group#GROUP AIGroup
|
|
||||||
function AI_AIR:onafterRefuel( AIGroup, From, Event, To )
|
|
||||||
self:F( { AIGroup, From, Event, To } )
|
|
||||||
|
|
||||||
self:E( "Group " .. self.Controllable:GetName() .. " ... Refuelling! ( " .. self:GetState() .. " )" )
|
|
||||||
|
|
||||||
if AIGroup and AIGroup:IsAlive() then
|
|
||||||
local Tanker = GROUP:FindByName( self.TankerName )
|
|
||||||
if Tanker:IsAlive() and Tanker:IsAirPlane() then
|
|
||||||
|
|
||||||
local RefuelRoute = {}
|
|
||||||
|
|
||||||
--- Calculate the target route point.
|
|
||||||
|
|
||||||
local CurrentCoord = AIGroup:GetCoordinate()
|
|
||||||
local ToRefuelCoord = Tanker:GetCoordinate()
|
|
||||||
local ToRefuelSpeed = math.random( self.PatrolMinSpeed, self.PatrolMaxSpeed )
|
|
||||||
|
|
||||||
--- Create a route point of type air.
|
|
||||||
local ToRefuelRoutePoint = ToRefuelCoord:WaypointAir(
|
|
||||||
self.PatrolAltType,
|
|
||||||
POINT_VEC3.RoutePointType.TurningPoint,
|
|
||||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
|
||||||
ToRefuelSpeed,
|
|
||||||
true
|
|
||||||
)
|
|
||||||
|
|
||||||
self:F( { ToRefuelSpeed = ToRefuelSpeed } )
|
|
||||||
|
|
||||||
RefuelRoute[#RefuelRoute+1] = ToRefuelRoutePoint
|
|
||||||
RefuelRoute[#RefuelRoute+1] = ToRefuelRoutePoint
|
|
||||||
|
|
||||||
AIGroup:OptionROEHoldFire()
|
|
||||||
AIGroup:OptionROTEvadeFire()
|
|
||||||
|
|
||||||
local Tasks = {}
|
|
||||||
Tasks[#Tasks+1] = AIGroup:TaskRefueling()
|
|
||||||
Tasks[#Tasks+1] = AIGroup:TaskFunction( self:GetClassName() .. ".Resume", self )
|
|
||||||
RefuelRoute[#RefuelRoute].task = AIGroup:TaskCombo( Tasks )
|
|
||||||
|
|
||||||
AIGroup:Route( RefuelRoute, 0.5 )
|
|
||||||
else
|
|
||||||
self:RTB()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
--- @param #AI_AIR self
|
|
||||||
function AI_AIR:onafterDead()
|
|
||||||
self:SetStatusOff()
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
--- @param #AI_AIR self
|
|
||||||
-- @param Core.Event#EVENTDATA EventData
|
|
||||||
function AI_AIR:OnCrash( EventData )
|
|
||||||
|
|
||||||
if self.Controllable:IsAlive() and EventData.IniDCSGroupName == self.Controllable:GetName() then
|
|
||||||
self:E( self.Controllable:GetUnits() )
|
|
||||||
if #self.Controllable:GetUnits() == 1 then
|
|
||||||
self:__Crash( 1, EventData )
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
--- @param #AI_AIR self
|
|
||||||
-- @param Core.Event#EVENTDATA EventData
|
|
||||||
function AI_AIR:OnEjection( EventData )
|
|
||||||
|
|
||||||
if self.Controllable:IsAlive() and EventData.IniDCSGroupName == self.Controllable:GetName() then
|
|
||||||
self:__Eject( 1, EventData )
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
--- @param #AI_AIR self
|
|
||||||
-- @param Core.Event#EVENTDATA EventData
|
|
||||||
function AI_AIR:OnPilotDead( EventData )
|
|
||||||
|
|
||||||
if self.Controllable:IsAlive() and EventData.IniDCSGroupName == self.Controllable:GetName() then
|
|
||||||
self:__PilotDead( 1, EventData )
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@@ -36,7 +36,6 @@
|
|||||||
-- @field #boolean ReportTargets If true, nearby targets are reported.
|
-- @field #boolean ReportTargets If true, nearby targets are reported.
|
||||||
-- @Field DCSTypes#AI.Option.Air.val.ROE OptionROE Which ROE is set to the FollowGroup.
|
-- @Field DCSTypes#AI.Option.Air.val.ROE OptionROE Which ROE is set to the FollowGroup.
|
||||||
-- @field DCSTypes#AI.Option.Air.val.REACTION_ON_THREAT OptionReactionOnThreat Which REACTION_ON_THREAT is set to the FollowGroup.
|
-- @field DCSTypes#AI.Option.Air.val.REACTION_ON_THREAT OptionReactionOnThreat Which REACTION_ON_THREAT is set to the FollowGroup.
|
||||||
-- @field #number dtFollow Time step between position updates.
|
|
||||||
|
|
||||||
|
|
||||||
--- Build large formations, make AI follow a @{Wrapper.Client#CLIENT} (player) leader or a @{Wrapper.Unit#UNIT} (AI) leader.
|
--- Build large formations, make AI follow a @{Wrapper.Client#CLIENT} (player) leader or a @{Wrapper.Unit#UNIT} (AI) leader.
|
||||||
@@ -107,7 +106,6 @@ AI_FORMATION = {
|
|||||||
FollowScheduler = nil,
|
FollowScheduler = nil,
|
||||||
OptionROE = AI.Option.Air.val.ROE.OPEN_FIRE,
|
OptionROE = AI.Option.Air.val.ROE.OPEN_FIRE,
|
||||||
OptionReactionOnThreat = AI.Option.Air.val.REACTION_ON_THREAT.ALLOW_ABORT_MISSION,
|
OptionReactionOnThreat = AI.Option.Air.val.REACTION_ON_THREAT.ALLOW_ABORT_MISSION,
|
||||||
dtFollow = 0.5,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
--- AI_FORMATION.Mode class
|
--- AI_FORMATION.Mode class
|
||||||
@@ -141,7 +139,7 @@ function AI_FORMATION:New( FollowUnit, FollowGroupSet, FollowName, FollowBriefin
|
|||||||
|
|
||||||
self:AddTransition( "*", "Stop", "Stopped" )
|
self:AddTransition( "*", "Stop", "Stopped" )
|
||||||
|
|
||||||
self:AddTransition( {"None", "Stopped"}, "Start", "Following" )
|
self:AddTransition( "None", "Start", "Following" )
|
||||||
|
|
||||||
self:AddTransition( "*", "FormationLine", "*" )
|
self:AddTransition( "*", "FormationLine", "*" )
|
||||||
--- FormationLine Handler OnBefore for AI_FORMATION
|
--- FormationLine Handler OnBefore for AI_FORMATION
|
||||||
@@ -622,16 +620,6 @@ function AI_FORMATION:New( FollowUnit, FollowGroupSet, FollowName, FollowBriefin
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Set time interval between updates of the formation.
|
|
||||||
-- @param #AI_FORMATION self
|
|
||||||
-- @param #number dt Time step in seconds between formation updates. Default is every 0.5 seconds.
|
|
||||||
-- @return #AI_FORMATION
|
|
||||||
function AI_FORMATION:SetFollowTimeInterval(dt) --R2.1
|
|
||||||
self.dtFollow=dt or 0.5
|
|
||||||
return self
|
|
||||||
end
|
|
||||||
|
|
||||||
--- This function is for test, it will put on the frequency of the FollowScheduler a red smoke at the direction vector calculated for the escort to fly to.
|
--- This function is for test, it will put on the frequency of the FollowScheduler a red smoke at the direction vector calculated for the escort to fly to.
|
||||||
-- This allows to visualize where the escort is flying to.
|
-- This allows to visualize where the escort is flying to.
|
||||||
-- @param #AI_FORMATION self
|
-- @param #AI_FORMATION self
|
||||||
@@ -905,30 +893,7 @@ function AI_FORMATION:SetFlightRandomization( FlightRandomization ) --R2.1
|
|||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Follow event fuction. Check if coming from state "stopped". If so the transition is rejected.
|
--- @param Follow#AI_FORMATION self
|
||||||
-- @param #AI_FORMATION self
|
|
||||||
-- @param Core.Set#SET_GROUP FollowGroupSet The following set of groups.
|
|
||||||
-- @param #string From From state.
|
|
||||||
-- @param #string Event Event.
|
|
||||||
-- @pram #string To The to state.
|
|
||||||
function AI_FORMATION:onafterStop(FollowGroupSet, From, Event, To) --R2.1
|
|
||||||
self:E("Stopping formation.")
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Follow event fuction. Check if coming from state "stopped". If so the transition is rejected.
|
|
||||||
-- @param #AI_FORMATION self
|
|
||||||
-- @param Core.Set#SET_GROUP FollowGroupSet The following set of groups.
|
|
||||||
-- @param #string From From state.
|
|
||||||
-- @param #string Event Event.
|
|
||||||
-- @pram #string To The to state.
|
|
||||||
function AI_FORMATION:onbeforeFollow( FollowGroupSet, From, Event, To ) --R2.1
|
|
||||||
if From=="Stopped" then
|
|
||||||
return false -- Deny transition.
|
|
||||||
end
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
--- @param #AI_FORMATION self
|
|
||||||
function AI_FORMATION:onenterFollowing( FollowGroupSet ) --R2.1
|
function AI_FORMATION:onenterFollowing( FollowGroupSet ) --R2.1
|
||||||
self:F( )
|
self:F( )
|
||||||
|
|
||||||
@@ -1068,7 +1033,7 @@ function AI_FORMATION:onenterFollowing( FollowGroupSet ) --R2.1
|
|||||||
self, ClientUnit, CT1, CV1, CT2, CV2
|
self, ClientUnit, CT1, CV1, CT2, CV2
|
||||||
)
|
)
|
||||||
|
|
||||||
self:__Follow( -self.dtFollow )
|
self:__Follow( -0.5 )
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1121,9 +1121,6 @@ do -- COORDINATE
|
|||||||
--- Build a Waypoint Air "Landing".
|
--- Build a Waypoint Air "Landing".
|
||||||
-- @param #COORDINATE self
|
-- @param #COORDINATE self
|
||||||
-- @param DCS#Speed Speed Airspeed in km/h.
|
-- @param DCS#Speed Speed Airspeed in km/h.
|
||||||
-- @param Wrapper.Airbase#AIRBASE airbase The airbase for takeoff and landing points.
|
|
||||||
-- @param #table DCSTasks A table of @{DCS#Task} items which are executed at the waypoint.
|
|
||||||
-- @param #string description A text description of the waypoint, which will be shown on the F10 map.
|
|
||||||
-- @return #table The route point.
|
-- @return #table The route point.
|
||||||
-- @usage
|
-- @usage
|
||||||
--
|
--
|
||||||
@@ -1132,8 +1129,8 @@ do -- COORDINATE
|
|||||||
-- LandingWaypoint = LandingCoord:WaypointAirLanding( 60 )
|
-- LandingWaypoint = LandingCoord:WaypointAirLanding( 60 )
|
||||||
-- HeliGroup:Route( { LandWaypoint }, 1 ) -- Start landing the helicopter in one second.
|
-- HeliGroup:Route( { LandWaypoint }, 1 ) -- Start landing the helicopter in one second.
|
||||||
--
|
--
|
||||||
function COORDINATE:WaypointAirLanding( Speed, airbase, DCSTasks, description )
|
function COORDINATE:WaypointAirLanding( Speed )
|
||||||
return self:WaypointAir( nil, COORDINATE.WaypointType.Land, COORDINATE.WaypointAction.Landing, Speed, airbase, DCSTasks, description )
|
return self:WaypointAir( nil, COORDINATE.WaypointType.Land, COORDINATE.WaypointAction.Landing, Speed )
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -2367,7 +2367,7 @@ do -- SET_UNIT
|
|||||||
local HasSEAD = UnitSEAD:HasSEAD()
|
local HasSEAD = UnitSEAD:HasSEAD()
|
||||||
|
|
||||||
self:T3(HasSEAD)
|
self:T3(HasSEAD)
|
||||||
if HasSEAD == true then
|
if HasSEAD then
|
||||||
SEADCount = SEADCount + 1
|
SEADCount = SEADCount + 1
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -216,7 +216,7 @@
|
|||||||
-- One way to determin which types of ammo the unit carries, one can use the debug mode of the arty class via @{#ARTY.SetDebugON}().
|
-- One way to determin which types of ammo the unit carries, one can use the debug mode of the arty class via @{#ARTY.SetDebugON}().
|
||||||
-- In debug mode, the all ammo types of the group are printed to the monitor as message and can be found in the DCS.log file.
|
-- In debug mode, the all ammo types of the group are printed to the monitor as message and can be found in the DCS.log file.
|
||||||
--
|
--
|
||||||
-- ## Employing Selected Weapons
|
-- ## Empoying Selected Weapons
|
||||||
--
|
--
|
||||||
-- If an ARTY group carries multiple weapons, which can be used for artillery task, a certain weapon type can be selected to attack the target.
|
-- If an ARTY group carries multiple weapons, which can be used for artillery task, a certain weapon type can be selected to attack the target.
|
||||||
-- This is done via the *weapontype* parameter of the @{#ARTY.AssignTargetCoord}(..., *weapontype*, ...) function.
|
-- This is done via the *weapontype* parameter of the @{#ARTY.AssignTargetCoord}(..., *weapontype*, ...) function.
|
||||||
@@ -674,13 +674,11 @@ ARTY.id="ARTY | "
|
|||||||
|
|
||||||
--- Arty script version.
|
--- Arty script version.
|
||||||
-- @field #string version
|
-- @field #string version
|
||||||
ARTY.version="1.0.7"
|
ARTY.version="1.0.6"
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
-- TODO list:
|
-- TODO list:
|
||||||
-- TODO: Add hit event and make the arty group relocate.
|
|
||||||
-- TODO: Handle rearming for ships. How?
|
|
||||||
-- DONE: Delete targets from queue user function.
|
-- DONE: Delete targets from queue user function.
|
||||||
-- DONE: Delete entire target queue user function.
|
-- DONE: Delete entire target queue user function.
|
||||||
-- DONE: Add weapon types. Done but needs improvements.
|
-- DONE: Add weapon types. Done but needs improvements.
|
||||||
@@ -699,9 +697,11 @@ ARTY.version="1.0.7"
|
|||||||
-- DONE: Add command move to make arty group move.
|
-- DONE: Add command move to make arty group move.
|
||||||
-- DONE: remove schedulers for status event.
|
-- DONE: remove schedulers for status event.
|
||||||
-- DONE: Improve handling of special weapons. When winchester if using selected weapons?
|
-- DONE: Improve handling of special weapons. When winchester if using selected weapons?
|
||||||
|
-- TODO: Handle rearming for ships. How?
|
||||||
-- DONE: Make coordinate after rearming general, i.e. also work after the group has moved to anonther location.
|
-- DONE: Make coordinate after rearming general, i.e. also work after the group has moved to anonther location.
|
||||||
-- DONE: Add set commands via markers. E.g. set rearming place.
|
-- DONE: Add set commands via markers. E.g. set rearming place.
|
||||||
-- DONE: Test stationary types like mortas ==> rearming etc.
|
-- DONE: Test stationary types like mortas ==> rearming etc.
|
||||||
|
-- TODO: Add hit event and make the arty group relocate.
|
||||||
-- DONE: Add illumination and smoke.
|
-- DONE: Add illumination and smoke.
|
||||||
|
|
||||||
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
@@ -4253,116 +4253,101 @@ end
|
|||||||
-- @param #ARTY self
|
-- @param #ARTY self
|
||||||
function ARTY:_CheckTargetsInRange()
|
function ARTY:_CheckTargetsInRange()
|
||||||
|
|
||||||
local targets2delete={}
|
|
||||||
|
|
||||||
for i=1,#self.targets do
|
for i=1,#self.targets do
|
||||||
local _target=self.targets[i]
|
local _target=self.targets[i]
|
||||||
|
|
||||||
self:T3(ARTY.id..string.format("Before: Target %s - in range = %s", _target.name, tostring(_target.inrange)))
|
self:T3(ARTY.id..string.format("Before: Target %s - in range = %s", _target.name, tostring(_target.inrange)))
|
||||||
|
|
||||||
-- Check if target is in range.
|
-- Check if target is in range.
|
||||||
local _inrange,_toofar,_tooclose,_remove=self:_TargetInRange(_target)
|
local _inrange,_toofar,_tooclose=self:_TargetInRange(_target)
|
||||||
self:T3(ARTY.id..string.format("Inbetw: Target %s - in range = %s, toofar = %s, tooclose = %s", _target.name, tostring(_target.inrange), tostring(_toofar), tostring(_tooclose)))
|
self:T3(ARTY.id..string.format("Inbetw: Target %s - in range = %s, toofar = %s, tooclose = %s", _target.name, tostring(_target.inrange), tostring(_toofar), tostring(_tooclose)))
|
||||||
|
|
||||||
if _remove then
|
-- Init default for assigning moves into range.
|
||||||
|
local _movetowards=false
|
||||||
|
local _moveaway=false
|
||||||
|
|
||||||
-- The ARTY group is immobile and not cargo but the target is not in range!
|
if _target.inrange==nil then
|
||||||
table.insert(targets2delete, _target.name)
|
|
||||||
|
|
||||||
else
|
-- First time the check is performed. We call the function again and send a message.
|
||||||
|
_target.inrange,_toofar,_tooclose=self:_TargetInRange(_target, self.report or self.Debug)
|
||||||
-- Init default for assigning moves into range.
|
|
||||||
local _movetowards=false
|
|
||||||
local _moveaway=false
|
|
||||||
|
|
||||||
if _target.inrange==nil then
|
|
||||||
|
|
||||||
-- First time the check is performed. We call the function again and send a message.
|
|
||||||
_target.inrange,_toofar,_tooclose=self:_TargetInRange(_target, self.report or self.Debug)
|
|
||||||
|
|
||||||
-- Send group towards/away from target.
|
|
||||||
if _toofar then
|
|
||||||
_movetowards=true
|
|
||||||
elseif _tooclose then
|
|
||||||
_moveaway=true
|
|
||||||
end
|
|
||||||
|
|
||||||
elseif _target.inrange==true then
|
|
||||||
|
|
||||||
-- Target was in range at previous check...
|
|
||||||
|
|
||||||
if _toofar then --...but is now too far away.
|
|
||||||
_movetowards=true
|
|
||||||
elseif _tooclose then --...but is now too close.
|
|
||||||
_moveaway=true
|
|
||||||
end
|
|
||||||
|
|
||||||
elseif _target.inrange==false then
|
|
||||||
|
|
||||||
-- Target was out of range at previous check.
|
|
||||||
|
|
||||||
if _inrange then
|
|
||||||
-- Inform coalition that target is now in range.
|
|
||||||
local text=string.format("%s, target %s is now in range.", self.alias, _target.name)
|
|
||||||
self:T(ARTY.id..text)
|
|
||||||
MESSAGE:New(text,10):ToCoalitionIf(self.Controllable:GetCoalition(), self.report or self.Debug)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
-- Send group towards/away from target.
|
||||||
|
if _toofar then
|
||||||
|
_movetowards=true
|
||||||
|
elseif _tooclose then
|
||||||
|
_moveaway=true
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Assign a relocation command so that the unit will be in range of the requested target.
|
elseif _target.inrange==true then
|
||||||
if self.autorelocate and (_movetowards or _moveaway) then
|
|
||||||
|
|
||||||
-- Get current position.
|
-- Target was in range at previous check...
|
||||||
local _from=self.Controllable:GetCoordinate()
|
|
||||||
local _dist=_from:Get2DDistance(_target.coord)
|
|
||||||
|
|
||||||
if _dist<=self.autorelocatemaxdist then
|
|
||||||
|
|
||||||
local _tocoord --Core.Point#COORDINATE
|
|
||||||
local _name=""
|
|
||||||
local _safetymargin=500
|
|
||||||
|
|
||||||
if _movetowards then
|
|
||||||
|
|
||||||
-- Target was in range on previous check but now we are too far away.
|
|
||||||
local _waytogo=_dist-self.maxrange+_safetymargin
|
|
||||||
local _heading=self:_GetHeading(_from,_target.coord)
|
|
||||||
_tocoord=_from:Translate(_waytogo, _heading)
|
|
||||||
_name=string.format("%s, relocation to within max firing range of target %s", self.alias, _target.name)
|
|
||||||
|
|
||||||
elseif _moveaway then
|
|
||||||
|
|
||||||
-- Target was in range on previous check but now we are too far away.
|
|
||||||
local _waytogo=_dist-self.minrange+_safetymargin
|
|
||||||
local _heading=self:_GetHeading(_target.coord,_from)
|
|
||||||
_tocoord=_from:Translate(_waytogo, _heading)
|
|
||||||
_name=string.format("%s, relocation to within min firing range of target %s", self.alias, _target.name)
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Send info message.
|
|
||||||
MESSAGE:New(_name.." assigned.", 10):ToCoalitionIf(self.Controllable:GetCoalition(), self.report or self.Debug)
|
|
||||||
|
|
||||||
-- Assign relocation move.
|
|
||||||
self:AssignMoveCoord(_tocoord, nil, nil, self.autorelocateonroad, false, _name, true)
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
|
if _toofar then --...but is now too far away.
|
||||||
|
_movetowards=true
|
||||||
|
elseif _tooclose then --...but is now too close.
|
||||||
|
_moveaway=true
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Update value.
|
elseif _target.inrange==false then
|
||||||
_target.inrange=_inrange
|
|
||||||
|
-- Target was out of range at previous check.
|
||||||
|
|
||||||
|
if _inrange then
|
||||||
|
-- Inform coalition that target is now in range.
|
||||||
|
local text=string.format("%s, target %s is now in range.", self.alias, _target.name)
|
||||||
|
self:T(ARTY.id..text)
|
||||||
|
MESSAGE:New(text,10):ToCoalitionIf(self.Controllable:GetCoalition(), self.report or self.Debug)
|
||||||
|
end
|
||||||
|
|
||||||
self:T3(ARTY.id..string.format("After: Target %s - in range = %s", _target.name, tostring(_target.inrange)))
|
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
-- Remove targets not in range.
|
-- Assign a relocation command so that the unit will be in range of the requested target.
|
||||||
for _,targetname in pairs(targets2delete) do
|
if self.autorelocate and (_movetowards or _moveaway) then
|
||||||
self:RemoveTarget(targetname)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
-- Get current position.
|
||||||
|
local _from=self.Controllable:GetCoordinate()
|
||||||
|
local _dist=_from:Get2DDistance(_target.coord)
|
||||||
|
|
||||||
|
if _dist<=self.autorelocatemaxdist then
|
||||||
|
|
||||||
|
local _tocoord --Core.Point#COORDINATE
|
||||||
|
local _name=""
|
||||||
|
local _safetymargin=500
|
||||||
|
|
||||||
|
if _movetowards then
|
||||||
|
|
||||||
|
-- Target was in range on previous check but now we are too far away.
|
||||||
|
local _waytogo=_dist-self.maxrange+_safetymargin
|
||||||
|
local _heading=self:_GetHeading(_from,_target.coord)
|
||||||
|
_tocoord=_from:Translate(_waytogo, _heading)
|
||||||
|
_name=string.format("%s, relocation to within max firing range of target %s", self.alias, _target.name)
|
||||||
|
|
||||||
|
elseif _moveaway then
|
||||||
|
|
||||||
|
-- Target was in range on previous check but now we are too far away.
|
||||||
|
local _waytogo=_dist-self.minrange+_safetymargin
|
||||||
|
local _heading=self:_GetHeading(_target.coord,_from)
|
||||||
|
_tocoord=_from:Translate(_waytogo, _heading)
|
||||||
|
_name=string.format("%s, relocation to within min firing range of target %s", self.alias, _target.name)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Send info message.
|
||||||
|
MESSAGE:New(_name.." assigned.", 10):ToCoalitionIf(self.Controllable:GetCoalition(), self.report or self.Debug)
|
||||||
|
|
||||||
|
-- Assign relocation move.
|
||||||
|
self:AssignMoveCoord(_tocoord, nil, nil, self.autorelocateonroad, false, _name, true)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Update value.
|
||||||
|
_target.inrange=_inrange
|
||||||
|
|
||||||
|
self:T3(ARTY.id..string.format("After: Target %s - in range = %s", _target.name, tostring(_target.inrange)))
|
||||||
|
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Check all normal (untimed) targets and return the target with the highest priority which has been engaged the fewest times.
|
--- Check all normal (untimed) targets and return the target with the highest priority which has been engaged the fewest times.
|
||||||
@@ -4743,7 +4728,6 @@ end
|
|||||||
-- @return #boolean True if target is in range, false otherwise.
|
-- @return #boolean True if target is in range, false otherwise.
|
||||||
-- @return #boolean True if ARTY group is too far away from the target, i.e. distance > max firing range.
|
-- @return #boolean True if ARTY group is too far away from the target, i.e. distance > max firing range.
|
||||||
-- @return #boolean True if ARTY group is too close to the target, i.e. distance < min finring range.
|
-- @return #boolean True if ARTY group is too close to the target, i.e. distance < min finring range.
|
||||||
-- @return #boolean True if target should be removed since ARTY group is immobile and not cargo.
|
|
||||||
function ARTY:_TargetInRange(target, message)
|
function ARTY:_TargetInRange(target, message)
|
||||||
self:F3(target)
|
self:F3(target)
|
||||||
|
|
||||||
@@ -4779,13 +4763,11 @@ function ARTY:_TargetInRange(target, message)
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- Remove target if ARTY group cannot move, e.g. Mortas. No chance to be ever in range - unless they are cargo.
|
-- Remove target if ARTY group cannot move, e.g. Mortas. No chance to be ever in range - unless they are cargo.
|
||||||
local _remove=false
|
|
||||||
if not (self.ismobile or self.iscargo) and _inrange==false then
|
if not (self.ismobile or self.iscargo) and _inrange==false then
|
||||||
--self:RemoveTarget(target.name)
|
self:RemoveTarget(target.name)
|
||||||
_remove=true
|
|
||||||
end
|
end
|
||||||
|
|
||||||
return _inrange,_toofar,_tooclose,_remove
|
return _inrange,_toofar,_tooclose
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Get the weapon type name, which should be used to attack the target.
|
--- Get the weapon type name, which should be used to attack the target.
|
||||||
|
|||||||
@@ -327,8 +327,6 @@ do -- DETECTION_BASE
|
|||||||
self:InitDetectIRST( nil )
|
self:InitDetectIRST( nil )
|
||||||
self:InitDetectDLINK( nil )
|
self:InitDetectDLINK( nil )
|
||||||
|
|
||||||
self:SetFriendliesRange( 6000 )
|
|
||||||
|
|
||||||
self:FilterCategories( {
|
self:FilterCategories( {
|
||||||
Unit.Category.AIRPLANE,
|
Unit.Category.AIRPLANE,
|
||||||
Unit.Category.GROUND_UNIT,
|
Unit.Category.GROUND_UNIT,
|
||||||
@@ -1358,6 +1356,7 @@ do -- DETECTION_BASE
|
|||||||
if FoundUnitCoalition ~= EnemyCoalition and FoundUnitInReportSetGroup == false then
|
if FoundUnitCoalition ~= EnemyCoalition and FoundUnitInReportSetGroup == false then
|
||||||
local FriendlyUnit = UNIT:Find( FoundDCSUnit )
|
local FriendlyUnit = UNIT:Find( FoundDCSUnit )
|
||||||
local FriendlyUnitName = FriendlyUnit:GetName()
|
local FriendlyUnitName = FriendlyUnit:GetName()
|
||||||
|
local FriendlyUnitCategory = FriendlyUnit:GetDesc().category
|
||||||
|
|
||||||
-- Friendlies are sorted per unit category.
|
-- Friendlies are sorted per unit category.
|
||||||
DetectedItem.FriendliesNearBy = DetectedItem.FriendliesNearBy or {}
|
DetectedItem.FriendliesNearBy = DetectedItem.FriendliesNearBy or {}
|
||||||
@@ -1386,7 +1385,7 @@ do -- DETECTION_BASE
|
|||||||
|
|
||||||
if PlayerUnit and PlayerUnit:IsInZone(DetectionZone) then
|
if PlayerUnit and PlayerUnit:IsInZone(DetectionZone) then
|
||||||
|
|
||||||
local PlayerUnitCategory = PlayerUnit:GetUnitCategory()
|
local PlayerUnitCategory = PlayerUnit:GetDesc().category
|
||||||
|
|
||||||
if ( not self.FriendliesCategory ) or ( self.FriendliesCategory and ( self.FriendliesCategory == PlayerUnitCategory ) ) then
|
if ( not self.FriendliesCategory ) or ( self.FriendliesCategory and ( self.FriendliesCategory == PlayerUnitCategory ) ) then
|
||||||
|
|
||||||
@@ -1473,7 +1472,7 @@ do -- DETECTION_BASE
|
|||||||
local DetectedUnit = UNIT:FindByName( ObjectName )
|
local DetectedUnit = UNIT:FindByName( ObjectName )
|
||||||
if DetectedUnit and DetectedUnit:IsAlive() then
|
if DetectedUnit and DetectedUnit:IsAlive() then
|
||||||
if self:IsDetectedObjectIdentified( DetectedObject ) == false then
|
if self:IsDetectedObjectIdentified( DetectedObject ) == false then
|
||||||
self:F( { DetectedObject = DetectedObject } )
|
--self:F( { DetectedObject = DetectedObject } )
|
||||||
return DetectedObject
|
return DetectedObject
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -5435,7 +5435,7 @@ function RAT:_ATCInit(airports_map)
|
|||||||
if not RAT.ATC.init then
|
if not RAT.ATC.init then
|
||||||
local text
|
local text
|
||||||
text="Starting RAT ATC.\nSimultanious = "..RAT.ATC.Nclearance.."\n".."Delay = "..RAT.ATC.delay
|
text="Starting RAT ATC.\nSimultanious = "..RAT.ATC.Nclearance.."\n".."Delay = "..RAT.ATC.delay
|
||||||
BASE:T(RAT.id..text)
|
self:T(RAT.id..text)
|
||||||
RAT.ATC.init=true
|
RAT.ATC.init=true
|
||||||
for _,ap in pairs(airports_map) do
|
for _,ap in pairs(airports_map) do
|
||||||
local name=ap:GetName()
|
local name=ap:GetName()
|
||||||
@@ -5458,7 +5458,7 @@ end
|
|||||||
-- @param #string name Group name of the flight.
|
-- @param #string name Group name of the flight.
|
||||||
-- @param #string dest Name of the destination airport.
|
-- @param #string dest Name of the destination airport.
|
||||||
function RAT:_ATCAddFlight(name, dest)
|
function RAT:_ATCAddFlight(name, dest)
|
||||||
BASE:T(string.format("%sATC %s: Adding flight %s with destination %s.", RAT.id, dest, name, dest))
|
self:T(string.format("%sATC %s: Adding flight %s with destination %s.", RAT.id, dest, name, dest))
|
||||||
RAT.ATC.flight[name]={}
|
RAT.ATC.flight[name]={}
|
||||||
RAT.ATC.flight[name].destination=dest
|
RAT.ATC.flight[name].destination=dest
|
||||||
RAT.ATC.flight[name].Tarrive=-1
|
RAT.ATC.flight[name].Tarrive=-1
|
||||||
@@ -5483,7 +5483,7 @@ end
|
|||||||
-- @param #string name Group name of the flight.
|
-- @param #string name Group name of the flight.
|
||||||
-- @param #number time Time the fight first registered.
|
-- @param #number time Time the fight first registered.
|
||||||
function RAT:_ATCRegisterFlight(name, time)
|
function RAT:_ATCRegisterFlight(name, time)
|
||||||
BASE:T(RAT.id.."Flight ".. name.." registered at ATC for landing clearance.")
|
self:T(RAT.id.."Flight ".. name.." registered at ATC for landing clearance.")
|
||||||
RAT.ATC.flight[name].Tarrive=time
|
RAT.ATC.flight[name].Tarrive=time
|
||||||
RAT.ATC.flight[name].holding=0
|
RAT.ATC.flight[name].holding=0
|
||||||
end
|
end
|
||||||
@@ -5514,7 +5514,7 @@ function RAT:_ATCStatus()
|
|||||||
|
|
||||||
-- Aircraft is holding.
|
-- Aircraft is holding.
|
||||||
local text=string.format("ATC %s: Flight %s is holding for %i:%02d. %s.", dest, name, hold/60, hold%60, busy)
|
local text=string.format("ATC %s: Flight %s is holding for %i:%02d. %s.", dest, name, hold/60, hold%60, busy)
|
||||||
BASE:T(RAT.id..text)
|
self:T(RAT.id..text)
|
||||||
|
|
||||||
elseif hold==RAT.ATC.onfinal then
|
elseif hold==RAT.ATC.onfinal then
|
||||||
|
|
||||||
@@ -5522,7 +5522,7 @@ function RAT:_ATCStatus()
|
|||||||
local Tfinal=Tnow-RAT.ATC.flight[name].Tonfinal
|
local Tfinal=Tnow-RAT.ATC.flight[name].Tonfinal
|
||||||
|
|
||||||
local text=string.format("ATC %s: Flight %s is on final. Waiting %i:%02d for landing event.", dest, name, Tfinal/60, Tfinal%60)
|
local text=string.format("ATC %s: Flight %s is on final. Waiting %i:%02d for landing event.", dest, name, Tfinal/60, Tfinal%60)
|
||||||
BASE:T(RAT.id..text)
|
self:T(RAT.id..text)
|
||||||
|
|
||||||
elseif hold==RAT.ATC.unregistered then
|
elseif hold==RAT.ATC.unregistered then
|
||||||
|
|
||||||
@@ -5530,7 +5530,7 @@ function RAT:_ATCStatus()
|
|||||||
--self:T(string.format("ATC %s: Flight %s is not registered yet (hold %d).", dest, name, hold))
|
--self:T(string.format("ATC %s: Flight %s is not registered yet (hold %d).", dest, name, hold))
|
||||||
|
|
||||||
else
|
else
|
||||||
BASE:E(RAT.id.."ERROR: Unknown holding time in RAT:_ATCStatus().")
|
self:E(RAT.id.."ERROR: Unknown holding time in RAT:_ATCStatus().")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -5572,12 +5572,12 @@ function RAT:_ATCCheck()
|
|||||||
|
|
||||||
-- Debug message.
|
-- Debug message.
|
||||||
local text=string.format("ATC %s: Flight %s runway is busy. You are #%d of %d in landing queue. Your holding time is %i:%02d.", name, flight,qID, nqueue, RAT.ATC.flight[flight].holding/60, RAT.ATC.flight[flight].holding%60)
|
local text=string.format("ATC %s: Flight %s runway is busy. You are #%d of %d in landing queue. Your holding time is %i:%02d.", name, flight,qID, nqueue, RAT.ATC.flight[flight].holding/60, RAT.ATC.flight[flight].holding%60)
|
||||||
BASE:T(RAT.id..text)
|
self:T(RAT.id..text)
|
||||||
|
|
||||||
else
|
else
|
||||||
|
|
||||||
local text=string.format("ATC %s: Flight %s was cleared for landing. Your holding time was %i:%02d.", name, flight, RAT.ATC.flight[flight].holding/60, RAT.ATC.flight[flight].holding%60)
|
local text=string.format("ATC %s: Flight %s was cleared for landing. Your holding time was %i:%02d.", name, flight, RAT.ATC.flight[flight].holding/60, RAT.ATC.flight[flight].holding%60)
|
||||||
BASE:T(RAT.id..text)
|
self:T(RAT.id..text)
|
||||||
|
|
||||||
-- Clear flight for landing.
|
-- Clear flight for landing.
|
||||||
RAT:_ATCClearForLanding(name, flight)
|
RAT:_ATCClearForLanding(name, flight)
|
||||||
@@ -5706,6 +5706,11 @@ function RAT:_ATCQueue()
|
|||||||
table.insert(RAT.ATC.airport[airport].queue, v[1])
|
table.insert(RAT.ATC.airport[airport].queue, v[1])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--fvh
|
||||||
|
--for k,v in ipairs(RAT.ATC.airport[airport].queue) do
|
||||||
|
--print(string.format("queue #%02i flight \"%s\" holding %d seconds",k, v, RAT.ATC.flight[v].holding))
|
||||||
|
--end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -276,7 +276,7 @@ RANGE.id="RANGE | "
|
|||||||
|
|
||||||
--- Range script version.
|
--- Range script version.
|
||||||
-- @field #string version
|
-- @field #string version
|
||||||
RANGE.version="1.2.2"
|
RANGE.version="1.2.1"
|
||||||
|
|
||||||
--TODO list:
|
--TODO list:
|
||||||
--TODO: Add custom weapons, which can be specified by the user.
|
--TODO: Add custom weapons, which can be specified by the user.
|
||||||
@@ -1166,8 +1166,6 @@ function RANGE:OnEventShot(EventData)
|
|||||||
-- Distance from range. We dont want to smoke targets outside of the range.
|
-- Distance from range. We dont want to smoke targets outside of the range.
|
||||||
local impactdist=impactcoord:Get2DDistance(self.location)
|
local impactdist=impactcoord:Get2DDistance(self.location)
|
||||||
|
|
||||||
--impactcoord:MarkToAll("Bomb impact point")
|
|
||||||
|
|
||||||
-- Smoke impact point of bomb.
|
-- Smoke impact point of bomb.
|
||||||
if self.PlayerSettings[_playername].smokebombimpact and impactdist<self.rangeradius then
|
if self.PlayerSettings[_playername].smokebombimpact and impactdist<self.rangeradius then
|
||||||
if self.PlayerSettings[_playername].delaysmoke then
|
if self.PlayerSettings[_playername].delaysmoke then
|
||||||
@@ -1187,8 +1185,6 @@ function RANGE:OnEventShot(EventData)
|
|||||||
-- Distance between bomb and target.
|
-- Distance between bomb and target.
|
||||||
local _temp = impactcoord:Get2DDistance(_target:GetCoordinate())
|
local _temp = impactcoord:Get2DDistance(_target:GetCoordinate())
|
||||||
|
|
||||||
--env.info(string.format("FF target = %s dist = %d m", _target:GetName(), _temp))
|
|
||||||
|
|
||||||
-- Find closest target to last known position of the bomb.
|
-- Find closest target to last known position of the bomb.
|
||||||
if _distance == nil or _temp < _distance then
|
if _distance == nil or _temp < _distance then
|
||||||
_distance = _temp
|
_distance = _temp
|
||||||
@@ -1229,7 +1225,7 @@ function RANGE:OnEventShot(EventData)
|
|||||||
elseif _distance <= self.rangeradius then
|
elseif _distance <= self.rangeradius then
|
||||||
-- Send message
|
-- Send message
|
||||||
local _message=string.format("%s, weapon fell more than %.1f km away from nearest range target. No score!", _callsign, self.scorebombdistance/1000)
|
local _message=string.format("%s, weapon fell more than %.1f km away from nearest range target. No score!", _callsign, self.scorebombdistance/1000)
|
||||||
self:_DisplayMessageToGroup(_unit, _message, nil, false)
|
self:_DisplayMessageToGroup(_unit, _message, nil, true)
|
||||||
end
|
end
|
||||||
|
|
||||||
--Terminate the timer
|
--Terminate the timer
|
||||||
|
|||||||
@@ -69,7 +69,6 @@
|
|||||||
-- @field #boolean autosave Automatically save assets to file when mission ends.
|
-- @field #boolean autosave Automatically save assets to file when mission ends.
|
||||||
-- @field #string autosavepath Path where the asset file is saved on auto save.
|
-- @field #string autosavepath Path where the asset file is saved on auto save.
|
||||||
-- @field #string autosavefilename File name of the auto asset save file. Default is auto generated from warehouse id and name.
|
-- @field #string autosavefilename File name of the auto asset save file. Default is auto generated from warehouse id and name.
|
||||||
-- @field #boolean safeparking If true, parking spots for aircraft are considered as occupied if e.g. a client aircraft is parked there. Default false.
|
|
||||||
-- @extends Core.Fsm#FSM
|
-- @extends Core.Fsm#FSM
|
||||||
|
|
||||||
--- Have your assets at the right place at the right time - or not!
|
--- Have your assets at the right place at the right time - or not!
|
||||||
@@ -625,8 +624,7 @@
|
|||||||
-- The @{#WAREHOUSE.OnAfterAttacked} function can be used by the mission designer to react to the enemy attack. For example by deploying some or all ground troops
|
-- The @{#WAREHOUSE.OnAfterAttacked} function can be used by the mission designer to react to the enemy attack. For example by deploying some or all ground troops
|
||||||
-- currently in stock to defend the warehouse. Note that the warehouse also has a self defence option which can be enabled by the @{#WAREHOUSE.SetAutoDefenceOn}()
|
-- currently in stock to defend the warehouse. Note that the warehouse also has a self defence option which can be enabled by the @{#WAREHOUSE.SetAutoDefenceOn}()
|
||||||
-- function. In this case, the warehouse will automatically spawn all ground troops. If the spawn zone is further away from the warehouse zone, all mobile troops
|
-- function. In this case, the warehouse will automatically spawn all ground troops. If the spawn zone is further away from the warehouse zone, all mobile troops
|
||||||
-- are routed to the warehouse zone. The self request which is triggered on an automatic defence has the assignment "AutoDefence". So you can use this to
|
-- are routed to the warehouse zone.
|
||||||
-- give orders to the groups that were spawned using the @{#WAREHOUSE.OnAfterSelfRequest} function.
|
|
||||||
--
|
--
|
||||||
-- If only ground troops of the enemy coalition are present in the warehouse zone, the warehouse and all its assets falls into the hands of the enemy.
|
-- If only ground troops of the enemy coalition are present in the warehouse zone, the warehouse and all its assets falls into the hands of the enemy.
|
||||||
-- In this case the event **Captured** is triggered which can be captured by the @{#WAREHOUSE.OnAfterCaptured} function.
|
-- In this case the event **Captured** is triggered which can be captured by the @{#WAREHOUSE.OnAfterCaptured} function.
|
||||||
@@ -1557,7 +1555,6 @@ WAREHOUSE = {
|
|||||||
autosave = false,
|
autosave = false,
|
||||||
autosavepath = nil,
|
autosavepath = nil,
|
||||||
autosavefile = nil,
|
autosavefile = nil,
|
||||||
saveparking = false,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
--- Item of the warehouse stock table.
|
--- Item of the warehouse stock table.
|
||||||
@@ -1729,7 +1726,7 @@ WAREHOUSE.db = {
|
|||||||
|
|
||||||
--- Warehouse class version.
|
--- Warehouse class version.
|
||||||
-- @field #string version
|
-- @field #string version
|
||||||
WAREHOUSE.version="0.6.6"
|
WAREHOUSE.version="0.6.4"
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
-- TODO: Warehouse todo list.
|
-- TODO: Warehouse todo list.
|
||||||
@@ -1738,12 +1735,12 @@ WAREHOUSE.version="0.6.6"
|
|||||||
-- TODO: Add check if assets "on the move" are stationary. Can happen if ground units get stuck in buildings. If stationary auto complete transport by adding assets to request warehouse? Time?
|
-- TODO: Add check if assets "on the move" are stationary. Can happen if ground units get stuck in buildings. If stationary auto complete transport by adding assets to request warehouse? Time?
|
||||||
-- TODO: Optimize findpathonroad. Do it only once (first time) and safe paths between warehouses similar to off-road paths.
|
-- TODO: Optimize findpathonroad. Do it only once (first time) and safe paths between warehouses similar to off-road paths.
|
||||||
-- TODO: Spawn assets only virtually, i.e. remove requested assets from stock but do NOT spawn them ==> Interface to A2A dispatcher! Maybe do a negative sign on asset number?
|
-- TODO: Spawn assets only virtually, i.e. remove requested assets from stock but do NOT spawn them ==> Interface to A2A dispatcher! Maybe do a negative sign on asset number?
|
||||||
|
-- TODO: Test capturing a neutral warehouse.
|
||||||
-- TODO: Make more examples: ARTY, CAP, ...
|
-- TODO: Make more examples: ARTY, CAP, ...
|
||||||
-- TODO: Check also general requests like all ground. Is this a problem for self propelled if immobile units are among the assets? Check if transport.
|
-- TODO: Check also general requests like all ground. Is this a problem for self propelled if immobile units are among the assets? Check if transport.
|
||||||
-- TODO: Handle the case when units of a group die during the transfer.
|
-- TODO: Handle the case when units of a group die during the transfer.
|
||||||
-- TODO: Added habours as interface for transport to from warehouses? Could make a rudimentary shipping dispatcher.
|
-- TODO: Added habours as interface for transport to from warehouses? Could make a rudimentary shipping dispatcher.
|
||||||
-- DONE: Test capturing a neutral warehouse.
|
-- TODO: Add save/load capability of warehouse <==> percistance after mission restart. Difficult in lua!
|
||||||
-- DONE: Add save/load capability of warehouse <==> percistance after mission restart. Difficult in lua!
|
|
||||||
-- DONE: Get cargo bay and weight from CARGO_GROUP and GROUP. No necessary any more!
|
-- DONE: Get cargo bay and weight from CARGO_GROUP and GROUP. No necessary any more!
|
||||||
-- DONE: Add possibility to set weight and cargo bay manually in AddAsset function as optional parameters.
|
-- DONE: Add possibility to set weight and cargo bay manually in AddAsset function as optional parameters.
|
||||||
-- DONE: Check overlapping aircraft sometimes.
|
-- DONE: Check overlapping aircraft sometimes.
|
||||||
@@ -1865,7 +1862,7 @@ function WAREHOUSE:New(warehouse, alias)
|
|||||||
self:AddTransition("*", "Stop", "Stopped") -- Stop the warehouse.
|
self:AddTransition("*", "Stop", "Stopped") -- Stop the warehouse.
|
||||||
self:AddTransition("Stopped", "Restart", "Running") -- Restart the warehouse when it was stopped before.
|
self:AddTransition("Stopped", "Restart", "Running") -- Restart the warehouse when it was stopped before.
|
||||||
self:AddTransition("Loaded", "Restart", "Running") -- Restart the warehouse when assets were loaded from file before.
|
self:AddTransition("Loaded", "Restart", "Running") -- Restart the warehouse when assets were loaded from file before.
|
||||||
self:AddTransition("*", "Save", "*") -- Save the warehouse state to disk.
|
self:AddTransition("*", "Save", "*") -- TODO Save the warehouse state to disk.
|
||||||
self:AddTransition("*", "Attacked", "Attacked") -- Warehouse is under attack by enemy coalition.
|
self:AddTransition("*", "Attacked", "Attacked") -- Warehouse is under attack by enemy coalition.
|
||||||
self:AddTransition("Attacked", "Defeated", "Running") -- Attack by other coalition was defeated!
|
self:AddTransition("Attacked", "Defeated", "Running") -- Attack by other coalition was defeated!
|
||||||
self:AddTransition("*", "ChangeCountry", "*") -- Change country (and coalition) of the warehouse. Warehouse is respawned!
|
self:AddTransition("*", "ChangeCountry", "*") -- Change country (and coalition) of the warehouse. Warehouse is respawned!
|
||||||
@@ -2366,24 +2363,6 @@ function WAREHOUSE:SetReportOff()
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Enable safe parking option, i.e. parking spots at an airbase will be considered as occupied when a client aircraft is parked there (even if the client slot is not taken by a player yet).
|
|
||||||
-- Note that also incoming aircraft can reserve/occupie parking spaces.
|
|
||||||
-- @param #WAREHOUSE self
|
|
||||||
-- @return #WAREHOUSE self
|
|
||||||
function WAREHOUSE:SetSafeParkingOn()
|
|
||||||
self.safeparking=true
|
|
||||||
return self
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Disable safe parking option. Note that is the default setting.
|
|
||||||
-- @param #WAREHOUSE self
|
|
||||||
-- @return #WAREHOUSE self
|
|
||||||
function WAREHOUSE:SetSafeParkingOff()
|
|
||||||
self.safeparking=false
|
|
||||||
return self
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
--- Set interval of status updates. Note that normally only one request can be processed per time interval.
|
--- Set interval of status updates. Note that normally only one request can be processed per time interval.
|
||||||
-- @param #WAREHOUSE self
|
-- @param #WAREHOUSE self
|
||||||
-- @param #number timeinterval Time interval in seconds.
|
-- @param #number timeinterval Time interval in seconds.
|
||||||
@@ -3552,11 +3531,11 @@ function WAREHOUSE:onafterAddAsset(From, Event, To, group, ngroups, forceattribu
|
|||||||
self:T(warehouse.wid..string.format("WARNING: Group %s is neither cargo nor transport!", group:GetName()))
|
self:T(warehouse.wid..string.format("WARNING: Group %s is neither cargo nor transport!", group:GetName()))
|
||||||
end
|
end
|
||||||
|
|
||||||
-- If no assignment was given we take the assignment of the request if there is any.
|
end
|
||||||
if assignment==nil and request.assignment~=nil then
|
|
||||||
assignment=request.assignment
|
|
||||||
end
|
|
||||||
|
|
||||||
|
-- If no assignment was given we take the assignment of the request if there is any.
|
||||||
|
if assignment==nil and request.assignment~=nil then
|
||||||
|
assignment=request.assignment
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -3609,7 +3588,6 @@ function WAREHOUSE:onafterAddAsset(From, Event, To, group, ngroups, forceattribu
|
|||||||
|
|
||||||
else
|
else
|
||||||
self:E(self.wid.."ERROR: Unknown group added as asset!")
|
self:E(self.wid.."ERROR: Unknown group added as asset!")
|
||||||
self:E({unknowngroup=group})
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Update status.
|
-- Update status.
|
||||||
@@ -4642,7 +4620,7 @@ function WAREHOUSE:onafterAttacked(From, Event, To, Coalition, Country)
|
|||||||
text=text..string.format("Deploying all %d ground assets.", nground)
|
text=text..string.format("Deploying all %d ground assets.", nground)
|
||||||
|
|
||||||
-- Add self request.
|
-- Add self request.
|
||||||
self:AddRequest(self, WAREHOUSE.Descriptor.CATEGORY, Group.Category.GROUND, WAREHOUSE.Quantity.ALL, nil, nil , 0, "AutoDefence")
|
self:AddRequest(self, WAREHOUSE.Descriptor.CATEGORY, Group.Category.GROUND, WAREHOUSE.Quantity.ALL, nil, nil , 0)
|
||||||
else
|
else
|
||||||
text=text..string.format("No ground assets currently available.")
|
text=text..string.format("No ground assets currently available.")
|
||||||
end
|
end
|
||||||
@@ -6318,26 +6296,25 @@ function WAREHOUSE:_CheckRequestValid(request)
|
|||||||
-- TODO: maybe only check if spots > 0 for the necessary terminal type? At least for FARPS.
|
-- TODO: maybe only check if spots > 0 for the necessary terminal type? At least for FARPS.
|
||||||
|
|
||||||
-- Get necessary terminal type.
|
-- Get necessary terminal type.
|
||||||
local termtype_dep=self:_GetTerminal(asset.attribute, self:GetAirbaseCategory())
|
local termtype=self:_GetTerminal(asset.attribute)
|
||||||
local termtype_des=self:_GetTerminal(asset.attribute, request.warehouse:GetAirbaseCategory())
|
|
||||||
|
|
||||||
-- Get number of parking spots.
|
-- Get number of parking spots.
|
||||||
local np_departure=self.airbase:GetParkingSpotsNumber(termtype_dep)
|
local np_departure=self.airbase:GetParkingSpotsNumber(termtype)
|
||||||
local np_destination=request.airbase:GetParkingSpotsNumber(termtype_des)
|
local np_destination=request.airbase:GetParkingSpotsNumber(termtype)
|
||||||
|
|
||||||
-- Debug info.
|
-- Debug info.
|
||||||
self:T(string.format("Asset attribute = %s, DEPARTURE: terminal type = %d, spots = %d, DESTINATION: terminal type = %d, spots = %d", asset.attribute, termtype_dep, np_departure, termtype_des, np_destination))
|
self:T(string.format("Asset attribute = %s, terminal type = %d, spots at departure = %d, destination = %d", asset.attribute, termtype, np_departure, np_destination))
|
||||||
|
|
||||||
-- Not enough parking at sending warehouse.
|
-- Not enough parking at sending warehouse.
|
||||||
--if (np_departure < request.nasset) and not (self.category==Airbase.Category.SHIP or self.category==Airbase.Category.HELIPAD) then
|
--if (np_departure < request.nasset) and not (self.category==Airbase.Category.SHIP or self.category==Airbase.Category.HELIPAD) then
|
||||||
if np_departure < nasset then
|
if np_departure < nasset then
|
||||||
self:E(string.format("ERROR: Incorrect request. Not enough parking spots of terminal type %d at warehouse. Available spots %d < %d necessary.", termtype_dep, np_departure, nasset))
|
self:E(string.format("ERROR: Incorrect request. Not enough parking spots of terminal type %d at warehouse. Available spots %d < %d necessary.", termtype, np_departure, nasset))
|
||||||
valid=false
|
valid=false
|
||||||
end
|
end
|
||||||
|
|
||||||
-- No parking at requesting warehouse.
|
-- No parking at requesting warehouse.
|
||||||
if np_destination == 0 then
|
if np_destination == 0 then
|
||||||
self:E(string.format("ERROR: Incorrect request. No parking spots of terminal type %d at requesting warehouse. Available spots = %d!", termtype_des, np_destination))
|
self:E(string.format("ERROR: Incorrect request. No parking spots of terminal type %d at requesting warehouse. Available spots = %d!", termtype, np_destination))
|
||||||
valid=false
|
valid=false
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -6475,7 +6452,7 @@ function WAREHOUSE:_CheckRequestValid(request)
|
|||||||
self:T(text)
|
self:T(text)
|
||||||
|
|
||||||
-- Get necessary terminal type for helos or transport aircraft.
|
-- Get necessary terminal type for helos or transport aircraft.
|
||||||
local termtype=self:_GetTerminal(request.transporttype, self:GetAirbaseCategory())
|
local termtype=self:_GetTerminal(request.transporttype)
|
||||||
|
|
||||||
-- Get number of parking spots.
|
-- Get number of parking spots.
|
||||||
local np_departure=self.airbase:GetParkingSpotsNumber(termtype)
|
local np_departure=self.airbase:GetParkingSpotsNumber(termtype)
|
||||||
@@ -6494,7 +6471,6 @@ function WAREHOUSE:_CheckRequestValid(request)
|
|||||||
if request.transporttype==WAREHOUSE.TransportType.AIRPLANE then
|
if request.transporttype==WAREHOUSE.TransportType.AIRPLANE then
|
||||||
|
|
||||||
-- Total number of parking spots for transport planes at destination.
|
-- Total number of parking spots for transport planes at destination.
|
||||||
termtype=self:_GetTerminal(request.transporttype, request.warehouse:GetAirbaseCategory())
|
|
||||||
local np_destination=request.airbase:GetParkingSpotsNumber(termtype)
|
local np_destination=request.airbase:GetParkingSpotsNumber(termtype)
|
||||||
|
|
||||||
-- Debug info.
|
-- Debug info.
|
||||||
@@ -6936,13 +6912,13 @@ end
|
|||||||
--- Get the proper terminal type based on generalized attribute of the group.
|
--- Get the proper terminal type based on generalized attribute of the group.
|
||||||
--@param #WAREHOUSE self
|
--@param #WAREHOUSE self
|
||||||
--@param #WAREHOUSE.Attribute _attribute Generlized attibute of unit.
|
--@param #WAREHOUSE.Attribute _attribute Generlized attibute of unit.
|
||||||
--@param #number _category Airbase category.
|
|
||||||
--@return Wrapper.Airbase#AIRBASE.TerminalType Terminal type for this group.
|
--@return Wrapper.Airbase#AIRBASE.TerminalType Terminal type for this group.
|
||||||
function WAREHOUSE:_GetTerminal(_attribute, _category)
|
function WAREHOUSE:_GetTerminal(_attribute)
|
||||||
|
|
||||||
-- Default terminal is "large".
|
-- Default terminal is "large".
|
||||||
local _terminal=AIRBASE.TerminalType.OpenBig
|
local _terminal=AIRBASE.TerminalType.OpenBig
|
||||||
|
|
||||||
|
|
||||||
if _attribute==WAREHOUSE.Attribute.AIR_FIGHTER then
|
if _attribute==WAREHOUSE.Attribute.AIR_FIGHTER then
|
||||||
-- Fighter ==> small.
|
-- Fighter ==> small.
|
||||||
_terminal=AIRBASE.TerminalType.FighterAircraft
|
_terminal=AIRBASE.TerminalType.FighterAircraft
|
||||||
@@ -6952,15 +6928,6 @@ function WAREHOUSE:_GetTerminal(_attribute, _category)
|
|||||||
elseif _attribute==WAREHOUSE.Attribute.AIR_TRANSPORTHELO or _attribute==WAREHOUSE.Attribute.AIR_ATTACKHELO then
|
elseif _attribute==WAREHOUSE.Attribute.AIR_TRANSPORTHELO or _attribute==WAREHOUSE.Attribute.AIR_ATTACKHELO then
|
||||||
-- Helicopter.
|
-- Helicopter.
|
||||||
_terminal=AIRBASE.TerminalType.HelicopterUsable
|
_terminal=AIRBASE.TerminalType.HelicopterUsable
|
||||||
else
|
|
||||||
--_terminal=AIRBASE.TerminalType.OpenMedOrBig
|
|
||||||
end
|
|
||||||
|
|
||||||
-- For ships, we allow medium spots for all fixed wing aircraft. There are smaller tankers and AWACS aircraft that can use a carrier.
|
|
||||||
if _category==Airbase.Category.SHIP then
|
|
||||||
if not (_attribute==WAREHOUSE.Attribute.AIR_TRANSPORTHELO or _attribute==WAREHOUSE.Attribute.AIR_ATTACKHELO) then
|
|
||||||
_terminal=AIRBASE.TerminalType.OpenMedOrBig
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
return _terminal
|
return _terminal
|
||||||
@@ -7035,6 +7002,20 @@ function WAREHOUSE:_FindParkingForAssets(airbase, assets)
|
|||||||
table.insert(obstacles,{coord=_coord, size=_size, name=_name, type="scenery"})
|
table.insert(obstacles,{coord=_coord, size=_size, name=_name, type="scenery"})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--[[
|
||||||
|
-- TODO Clients? Unoccupied client aircraft are also important! Are they already included in scanned units maybe?
|
||||||
|
local clients=_DATABASE.CLIENTS
|
||||||
|
for _,_client in pairs(clients) do
|
||||||
|
local client=_client --Wrapper.Client#CLIENT
|
||||||
|
env.info(string.format("FF Client name %s", client:GetName()))
|
||||||
|
local unit=UNIT:FindByName(client:GetName())
|
||||||
|
--local unit=client:GetClientGroupUnit()
|
||||||
|
local _coord=unit:GetCoordinate()
|
||||||
|
local _name=unit:GetName()
|
||||||
|
local _size=self:_GetObjectSize(client:GetClientGroupDCSUnit())
|
||||||
|
table.insert(obstacles,{coord=_coord, size=_size, name=_name, type="client"})
|
||||||
|
end
|
||||||
|
]]
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Parking data for all assets.
|
-- Parking data for all assets.
|
||||||
@@ -7045,7 +7026,7 @@ function WAREHOUSE:_FindParkingForAssets(airbase, assets)
|
|||||||
local _asset=asset --#WAREHOUSE.Assetitem
|
local _asset=asset --#WAREHOUSE.Assetitem
|
||||||
|
|
||||||
-- Get terminal type of this asset
|
-- Get terminal type of this asset
|
||||||
local terminaltype=self:_GetTerminal(asset.attribute, self:GetAirbaseCategory())
|
local terminaltype=self:_GetTerminal(asset.attribute)
|
||||||
|
|
||||||
-- Asset specific parking.
|
-- Asset specific parking.
|
||||||
parking[_asset.uid]={}
|
parking[_asset.uid]={}
|
||||||
@@ -7068,16 +7049,9 @@ function WAREHOUSE:_FindParkingForAssets(airbase, assets)
|
|||||||
|
|
||||||
--env.info(string.format("FF asset=%s (id=%d): needs terminal type=%d, id=%d, #obstacles=%d", _asset.templatename, _asset.uid, terminaltype, _termid, #obstacles))
|
--env.info(string.format("FF asset=%s (id=%d): needs terminal type=%d, id=%d, #obstacles=%d", _asset.templatename, _asset.uid, terminaltype, _termid, #obstacles))
|
||||||
|
|
||||||
|
-- Loop over all obstacles.
|
||||||
local free=true
|
local free=true
|
||||||
local problem=nil
|
local problem=nil
|
||||||
|
|
||||||
-- Safe parking using TO_AC from DCS result.
|
|
||||||
if self.safeparking and _toac then
|
|
||||||
free=false
|
|
||||||
self:T("Parking spot %d is occupied by other aircraft taking off or landing.", _termid)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Loop over all obstacles.
|
|
||||||
for _,obstacle in pairs(obstacles) do
|
for _,obstacle in pairs(obstacles) do
|
||||||
|
|
||||||
-- Check if aircraft overlaps with any obstacle.
|
-- Check if aircraft overlaps with any obstacle.
|
||||||
|
|||||||
@@ -712,7 +712,7 @@ function CONTROLLABLE:TaskAttackUnit( AttackUnit, GroupAttack, WeaponExpend, Att
|
|||||||
groupAttack = GroupAttack or false,
|
groupAttack = GroupAttack or false,
|
||||||
visible = Visible or false,
|
visible = Visible or false,
|
||||||
expend = WeaponExpend or "Auto",
|
expend = WeaponExpend or "Auto",
|
||||||
directionEnabled = Direction and true or nil,
|
directionEnabled = Direction and true or false,
|
||||||
direction = Direction,
|
direction = Direction,
|
||||||
altitudeEnabled = Altitude and true or false,
|
altitudeEnabled = Altitude and true or false,
|
||||||
altitude = Altitude or 30,
|
altitude = Altitude or 30,
|
||||||
@@ -870,38 +870,6 @@ function CONTROLLABLE:TaskOrbitCircleAtVec2( Point, Altitude, Speed )
|
|||||||
return DCSTask
|
return DCSTask
|
||||||
end
|
end
|
||||||
|
|
||||||
--- (AIR) Orbit at a position with at a given altitude and speed. Optionally, a race track pattern can be specified.
|
|
||||||
-- @param #CONTROLLABLE self
|
|
||||||
-- @param Core.Point#COORDINATE Coord Coordinate at which the CONTROLLABLE orbits.
|
|
||||||
-- @param #number Altitude Altitude in meters of the orbit pattern.
|
|
||||||
-- @param #number Speed Speed [m/s] flying the orbit pattern
|
|
||||||
-- @param Core.Point#COORDINATE CoordRaceTrack (Optional) If this coordinate is specified, the CONTROLLABLE will fly a race-track pattern using this and the initial coordinate.
|
|
||||||
-- @return #CONTROLLABLE self
|
|
||||||
function CONTROLLABLE:TaskOrbit(Coord, Altitude, Speed, CoordRaceTrack)
|
|
||||||
|
|
||||||
local Pattern=AI.Task.OrbitPattern.CIRCLE
|
|
||||||
|
|
||||||
local P1=Coord:GetVec2()
|
|
||||||
local P2=nil
|
|
||||||
if CoordRaceTrack then
|
|
||||||
Pattern=AI.Task.OrbitPattern.RACE_TRACK
|
|
||||||
P2=CoordRaceTrack:GetVec2()
|
|
||||||
end
|
|
||||||
|
|
||||||
local Task = {
|
|
||||||
id = 'Orbit',
|
|
||||||
params = {
|
|
||||||
pattern = Pattern,
|
|
||||||
point = P1,
|
|
||||||
point2 = P2,
|
|
||||||
speed = Speed,
|
|
||||||
altitude = Altitude,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Task
|
|
||||||
end
|
|
||||||
|
|
||||||
--- (AIR) Orbit at the current position of the first unit of the controllable at a specified alititude.
|
--- (AIR) Orbit at the current position of the first unit of the controllable at a specified alititude.
|
||||||
-- @param #CONTROLLABLE self
|
-- @param #CONTROLLABLE self
|
||||||
-- @param #number Altitude The altitude [m] to hold the position.
|
-- @param #number Altitude The altitude [m] to hold the position.
|
||||||
@@ -990,7 +958,11 @@ function CONTROLLABLE:TaskRefueling()
|
|||||||
-- params = {}
|
-- params = {}
|
||||||
-- }
|
-- }
|
||||||
|
|
||||||
local DCSTask={id='Refueling', params={}}
|
local DCSTask
|
||||||
|
DCSTask = { id = 'Refueling',
|
||||||
|
params = {
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
self:T3( { DCSTask } )
|
self:T3( { DCSTask } )
|
||||||
return DCSTask
|
return DCSTask
|
||||||
@@ -2129,7 +2101,7 @@ do -- Route methods
|
|||||||
FromCoordinate = FromCoordinate or self:GetCoordinate()
|
FromCoordinate = FromCoordinate or self:GetCoordinate()
|
||||||
|
|
||||||
-- Get path and path length on road including the end points (From and To).
|
-- Get path and path length on road including the end points (From and To).
|
||||||
local PathOnRoad, LengthOnRoad, GotPath =FromCoordinate:GetPathOnRoad(ToCoordinate, true)
|
local PathOnRoad, LengthOnRoad=FromCoordinate:GetPathOnRoad(ToCoordinate, true)
|
||||||
|
|
||||||
-- Get the length only(!) on the road.
|
-- Get the length only(!) on the road.
|
||||||
local _,LengthRoad=FromCoordinate:GetPathOnRoad(ToCoordinate, false)
|
local _,LengthRoad=FromCoordinate:GetPathOnRoad(ToCoordinate, false)
|
||||||
@@ -2141,7 +2113,7 @@ do -- Route methods
|
|||||||
-- Calculate the direct distance between the initial and final points.
|
-- Calculate the direct distance between the initial and final points.
|
||||||
local LengthDirect=FromCoordinate:Get2DDistance(ToCoordinate)
|
local LengthDirect=FromCoordinate:Get2DDistance(ToCoordinate)
|
||||||
|
|
||||||
if GotPath then
|
if PathOnRoad then
|
||||||
|
|
||||||
-- Off road part of the rout: Total=OffRoad+OnRoad.
|
-- Off road part of the rout: Total=OffRoad+OnRoad.
|
||||||
LengthOffRoad=LengthOnRoad-LengthRoad
|
LengthOffRoad=LengthOnRoad-LengthRoad
|
||||||
@@ -2164,7 +2136,7 @@ do -- Route methods
|
|||||||
local canroad=false
|
local canroad=false
|
||||||
|
|
||||||
-- Check if a valid path on road could be found.
|
-- Check if a valid path on road could be found.
|
||||||
if GotPath and LengthDirect > 2000 then -- if the length of the movement is less than 1 km, drive directly.
|
if PathOnRoad and LengthDirect > 2000 then -- if the length of the movement is less than 1 km, drive directly.
|
||||||
-- Check whether the road is very long compared to direct path.
|
-- Check whether the road is very long compared to direct path.
|
||||||
if LongRoad and Shortcut then
|
if LongRoad and Shortcut then
|
||||||
|
|
||||||
@@ -3052,3 +3024,6 @@ function CONTROLLABLE:IsAirPlane()
|
|||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- Message APIs
|
||||||
@@ -325,7 +325,7 @@ end
|
|||||||
-- So all event listeners will catch the destroy event of this group for each unit in the group.
|
-- So all event listeners will catch the destroy event of this group for each unit in the group.
|
||||||
-- To raise these events, provide the `GenerateEvent` parameter.
|
-- To raise these events, provide the `GenerateEvent` parameter.
|
||||||
-- @param #GROUP self
|
-- @param #GROUP self
|
||||||
-- @param #boolean GenerateEvent If true, a crash or dead event for each unit is generated. If false, if no event is triggered. If nil, a RemoveUnit event is triggered.
|
-- @param #boolean GenerateEvent true if you want to generate a crash or dead event for each unit.
|
||||||
-- @usage
|
-- @usage
|
||||||
-- -- Air unit example: destroy the Helicopter and generate a S_EVENT_CRASH for each unit in the Helicopter group.
|
-- -- Air unit example: destroy the Helicopter and generate a S_EVENT_CRASH for each unit in the Helicopter group.
|
||||||
-- Helicopter = GROUP:FindByName( "Helicopter" )
|
-- Helicopter = GROUP:FindByName( "Helicopter" )
|
||||||
|
|||||||
@@ -620,24 +620,6 @@ function UNIT:GetLife0()
|
|||||||
return 0
|
return 0
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Returns the category of the unit, which is different than the generic category types of objects.
|
|
||||||
-- @param #UNIT self
|
|
||||||
-- @return DCS#Unit.Category
|
|
||||||
function UNIT:GetUnitCategory()
|
|
||||||
self:F3( self.UnitName )
|
|
||||||
|
|
||||||
local DCSUnit = self:GetDCSObject()
|
|
||||||
if DCSUnit then
|
|
||||||
local UnitCategory = DCSUnit:getDesc().category
|
|
||||||
self:T3( UnitCategory )
|
|
||||||
|
|
||||||
return UnitCategory
|
|
||||||
end
|
|
||||||
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
--- Returns the category name of the #UNIT.
|
--- Returns the category name of the #UNIT.
|
||||||
-- @param #UNIT self
|
-- @param #UNIT self
|
||||||
-- @return #string Category name = Helicopter, Airplane, Ground Unit, Ship
|
-- @return #string Category name = Helicopter, Airplane, Ground Unit, Ship
|
||||||
@@ -663,8 +645,6 @@ function UNIT:GetCategoryName()
|
|||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
--- Returns the Unit's A2G threat level on a scale from 1 to 10 ...
|
--- Returns the Unit's A2G threat level on a scale from 1 to 10 ...
|
||||||
-- The following threat levels are foreseen:
|
-- The following threat levels are foreseen:
|
||||||
--
|
--
|
||||||
@@ -922,31 +902,29 @@ end
|
|||||||
function UNIT:InAir()
|
function UNIT:InAir()
|
||||||
self:F2( self.UnitName )
|
self:F2( self.UnitName )
|
||||||
|
|
||||||
-- Get DCS unit object.
|
|
||||||
local DCSUnit = self:GetDCSObject() --DCS#Unit
|
local DCSUnit = self:GetDCSObject() --DCS#Unit
|
||||||
|
|
||||||
if DCSUnit then
|
if DCSUnit then
|
||||||
|
-- Implementation of workaround. The original code is below.
|
||||||
|
-- This to simulate the landing on buildings.
|
||||||
|
|
||||||
-- Get DCS result of whether unit is in air or not.
|
local UnitInAir = true
|
||||||
local UnitInAir = DCSUnit:inAir()
|
|
||||||
|
|
||||||
-- Get unit category.
|
|
||||||
local UnitCategory = DCSUnit:getDesc().category
|
local UnitCategory = DCSUnit:getDesc().category
|
||||||
|
if UnitCategory == Unit.Category.HELICOPTER then
|
||||||
-- If DCS says that it is in air, check if this is really the case, since we might have landed on a building where inAir()=true but actually is not.
|
|
||||||
-- This is a workaround since DCS currently does not acknoledge that helos land on buildings.
|
|
||||||
-- Note however, that the velocity check will fail if the ground is moving, e.g. on an aircraft carrier!
|
|
||||||
if UnitInAir==true and UnitCategory == Unit.Category.HELICOPTER then
|
|
||||||
local VelocityVec3 = DCSUnit:getVelocity()
|
local VelocityVec3 = DCSUnit:getVelocity()
|
||||||
local Velocity = UTILS.VecNorm(VelocityVec3)
|
local Velocity = ( VelocityVec3.x ^ 2 + VelocityVec3.y ^ 2 + VelocityVec3.z ^ 2 ) ^ 0.5 -- in meters / sec
|
||||||
local Coordinate = DCSUnit:getPoint()
|
local Coordinate = DCSUnit:getPoint()
|
||||||
local LandHeight = land.getHeight( { x = Coordinate.x, y = Coordinate.z } )
|
local LandHeight = land.getHeight( { x = Coordinate.x, y = Coordinate.z } )
|
||||||
local Height = Coordinate.y - LandHeight
|
local Height = Coordinate.y - LandHeight
|
||||||
if Velocity < 1 and Height <= 60 then
|
if Velocity < 1 and Height <= 60 then
|
||||||
UnitInAir = false
|
UnitInAir = false
|
||||||
end
|
end
|
||||||
|
else
|
||||||
|
UnitInAir = DCSUnit:inAir()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
self:T3( UnitInAir )
|
self:T3( UnitInAir )
|
||||||
return UnitInAir
|
return UnitInAir
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -60,15 +60,11 @@ Functional/PseudoATC.lua
|
|||||||
Functional/Warehouse.lua
|
Functional/Warehouse.lua
|
||||||
|
|
||||||
AI/AI_Balancer.lua
|
AI/AI_Balancer.lua
|
||||||
AI/AI_Air.lua
|
|
||||||
AI/AI_A2A.lua
|
AI/AI_A2A.lua
|
||||||
AI/AI_A2A_Patrol.lua
|
AI/AI_A2A_Patrol.lua
|
||||||
AI/AI_A2A_Cap.lua
|
AI/AI_A2A_Cap.lua
|
||||||
AI/AI_A2A_Gci.lua
|
AI/AI_A2A_Gci.lua
|
||||||
AI/AI_A2A_Dispatcher.lua
|
AI/AI_A2A_Dispatcher.lua
|
||||||
AI/AI_A2G.lua
|
|
||||||
AI/AI_A2G_Engage.lua
|
|
||||||
AI/AI_A2G_Dispatcher.lua
|
|
||||||
AI/AI_Patrol.lua
|
AI/AI_Patrol.lua
|
||||||
AI/AI_Cap.lua
|
AI/AI_Cap.lua
|
||||||
AI/AI_Cas.lua
|
AI/AI_Cas.lua
|
||||||
|
|||||||
Reference in New Issue
Block a user