Progress on the AI_PATROLZONE!!! Working test mission!!!

This commit is contained in:
Sven Van de Velde
2016-08-21 10:56:56 +02:00
parent 3861362ed9
commit a800531ea0
89 changed files with 2115 additions and 55817 deletions

View File

@@ -3,9 +3,10 @@
-- ===
--
-- 1) @{AIBalancer#AIBALANCER} class, extends @{Base#BASE}
-- ================================================
-- =======================================================
-- The @{AIBalancer#AIBALANCER} class controls the dynamic spawning of AI GROUPS depending on a SET_CLIENT.
-- There will be as many AI GROUPS spawned as there at CLIENTS in SET_CLIENT not spawned.
-- The AIBalancer uses the @{PatrolZone#PATROLZONE} class to make AI patrol an zone until the fuel treshold is reached.
--
-- 1.1) AIBALANCER construction method:
-- ------------------------------------
@@ -26,22 +27,44 @@
--
-- ===
--
-- **API CHANGE HISTORY**
-- ======================
--
-- The underlying change log documents the API changes. Please read this carefully. The following notation is used:
--
-- * **Added** parts are expressed in bold type face.
-- * _Removed_ parts are expressed in italic type face.
--
-- Hereby the change log:
--
-- 2016-08-17: SPAWN:**InitCleanUp**( SpawnCleanUpInterval ) replaces SPAWN:_CleanUp_( SpawnCleanUpInterval )
--
-- * Want to ensure that the methods starting with **Init** are the first called methods before any _Spawn_ method is called!
-- * This notation makes it now more clear which methods are initialization methods and which methods are Spawn enablement methods.
--
-- ===
--
-- AUTHORS and CONTRIBUTIONS
-- =========================
--
-- ### Contributions:
--
-- * **Dutch_Baron (James)** Who you can search on the Eagle Dynamics Forums.
-- * **Dutch_Baron (James)**: Who you can search on the Eagle Dynamics Forums.
-- Working together with James has resulted in the creation of the AIBALANCER class.
-- James has shared his ideas on balancing AI with air units, and together we made a first design which you can use now :-)
--
-- * **SNAFU**
-- * **SNAFU**:
-- Had a couple of mails with the guys to validate, if the same concept in the GCI/CAP script could be reworked within MOOSE.
-- None of the script code has been used however within the new AIBALANCER moose class.
--
-- ### Authors:
--
-- * FlightControl - Framework Design & Programming
-- * FlightControl: Framework Design & Programming
--
-- @module AIBalancer
--- AIBALANCER class
-- @type AIBALANCER
-- @field Set#SET_CLIENT SetClient
@@ -122,11 +145,28 @@ end
-- @param #AIBALANCER self
-- @param PatrolZone#PATROLZONE PatrolZone The @{PatrolZone} where the AI needs to patrol.
-- @return PatrolZone#PATROLZONE self
function AIBALANCER:SetPatrolZone( PatrolZone )
function AIBALANCER:SetPatrolZone( PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed )
self.PatrolZone = PatrolZone
self.PatrolZone = PATROLZONE:New(
self.SpawnAI,
PatrolZone,
PatrolFloorAltitude,
PatrolCeilingAltitude,
PatrolMinSpeed,
PatrolMaxSpeed
)
end
--- Get the @{PatrolZone} object assigned by the @{AIBalancer} object.
-- @param #AIBALANCER self
-- @return PatrolZone#PATROLZONE PatrolZone The @{PatrolZone} where the AI needs to patrol.
function AIBALANCER:GetPatrolZone()
return self.PatrolZone
end
--- @param #AIBALANCER self
function AIBALANCER:_ClientAliveMonitorScheduler()

View File

@@ -0,0 +1,388 @@
--- This module contains the AI\_PATROLZONE class.
--
-- ===
--
-- 1) @{#AI_PATROLZONE} class, extends @{StateMachine#STATEMACHINE}
-- ================================================================
-- The @{#AI_PATROLZONE} class implements the core functions to patrol a @{Zone} by an AIR @{Group}.
-- The patrol algorithm works that for each airplane patrolling, upon arrival at the patrol zone,
-- a random point is selected as the route point within the 3D space, within the given boundary limits.
-- The airplane 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 random 3D point, a new 3D random point will be selected within the patrol zone using the given limits.
-- This cycle will continue until a fuel treshold has been reached by the airplane.
-- When the fuel treshold has been reached, the airplane will fly towards the nearest friendly airbase and will land.
--
-- 1.1) AI\_PATROLZONE constructor:
-- ----------------------------
--
-- * @{#AI_PATROLZONE.New}(): Creates a new AI\_PATROLZONE object.
--
-- 1.2) AI\_PATROLZONE state machine:
-- ----------------------------------
-- The AI\_PATROLZONE is a state machine: it manages the different events and states of the AIGroup it is controlling.
--
-- ### 1.2.1) AI\_PATROLZONE Events:
--
-- * @{#AI_PATROLZONE.Route}( AIGroup ): A new 3D route point is selected and the AIGroup will fly towards that point with the given speed.
-- * @{#AI_PATROLZONE.Patrol}( AIGroup ): The AIGroup reports it is patrolling. This event is called every 30 seconds.
-- * @{#AI_PATROLZONE.RTB}( AIGroup ): The AIGroup will report return to base.
-- * @{#AI_PATROLZONE.End}( AIGroup ): The end of the AI\_PATROLZONE process.
-- * @{#AI_PATROLZONE.Dead}( AIGroup ): The AIGroup is dead. The AI\_PATROLZONE process will be ended.
--
-- ### 1.2.2) AI\_PATROLZONE States:
--
-- * **Route**: A new 3D route point is selected and the AIGroup will fly towards that point with the given speed.
-- * **Patrol**: The AIGroup is patrolling. This state is set every 30 seconds, so every 30 seconds, a state transition function can be used.
-- * **RTB**: The AIGroup reports it wants to return to the base.
-- * **Dead**: The AIGroup is dead ...
-- * **End**: The process has come to an end.
--
-- ### 1.2.3) AI\_PATROLZONE state transition functions:
--
-- State transition functions can be set **by the mission designer** customizing or improving the behaviour of the state.
-- There are 2 moments when state transition functions will be called by the state machine:
--
-- * **Before** the state transition.
-- The state transition function needs to start with the name **OnBefore + the name of the state**.
-- If the state transition function returns false, then the processing of the state transition will not be done!
-- If you want to change the behaviour of the AIGroup at this event, return false,
-- but then you'll need to specify your own logic using the AIGroup!
--
-- * **After** the state transition.
-- The state transition function needs to start with the name **OnAfter + the name of the state**.
-- These state transition functions need to provide a return value, which is specified at the function description.
--
-- An example how to manage a state transition for an AI\_PATROLZONE object **Patrol** for the state **RTB**:
--
-- local PatrolZoneGroup = GROUP:FindByName( "Patrol Zone" )
-- local PatrolZone = ZONE_POLYGON:New( "PatrolZone", PatrolZoneGroup )
--
-- local PatrolSpawn = SPAWN:New( "Patrol Group" )
-- local PatrolGroup = PatrolSpawn:Spawn()
--
-- local Patrol = AI_PATROLZONE:New( PatrolZone, 3000, 6000, 300, 600 )
-- Patrol:AddGroup( PatrolGroup )
-- Patrol:ManageFuel( 0.2, 60 )
--
-- **OnBefore**RTB( AIGroup ) will be called by the AI\_PATROLZONE object when the AIGroup reports RTB, but **before** the RTB default action is processed by the AI_PATROLZONE object.
--
-- --- State transition function for the AI\_PATROLZONE **Patrol** object
-- -- @param #AI_PATROLZONE self
-- -- @param Group#GROUP AIGroup
-- -- @return #boolean If false is returned, then the OnAfter state transition function will not be called.
-- function Patrol:OnBeforeRTB( AIGroup )
-- AIGroup:MessageToRed( "Returning to base", 20 )
-- end
--
-- **OnAfter**RTB( AIGroup ) will be called by the AI\_PATROLZONE object when the AIGroup reports RTB, but **after** the RTB default action was processed by the AI_PATROLZONE object.
--
-- --- State transition function for the AI\_PATROLZONE **Patrol** object
-- -- @param #AI_PATROLZONE self
-- -- @param Group#GROUP AIGroup
-- -- @return #Group#GROUP The new AIGroup object that is set to be patrolling the zone.
-- function Patrol:OnAfterRTB( AIGroup )
-- return PatrolSpawn:Spawn()
-- end
--
-- 1.3) Modify the AI\_PATROLZONE parameters:
-- --------------------------------------
-- The following methods are available to modify the parameters of a AI\_PATROLZONE object:
--
-- * @{#AI_PATROLZONE.SetGroup}(): Set the AI Patrol Group.
-- * @{#AI_PATROLZONE.SetSpeed}(): Set the patrol speed of the AI, for the next patrol.
-- * @{#AI_PATROLZONE.SetAltitude}(): Set altitude of the AI, for the next patrol.
--
-- 1.3) Manage the out of fuel in the AI\_PATROLZONE:
-- ----------------------------------------------
-- When the PatrolGroup is out of fuel, it is required that a new PatrolGroup is started, before the old PatrolGroup 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 PatrolGroup will continue for a given time its patrol task in orbit, while a new PatrolGroup is targetted to the AI\_PATROLZONE.
-- Once the time is finished, the old PatrolGroup will return to the base.
-- Use the method @{#AI_PATROLZONE.ManageFuel}() to have this proces in place.
--
-- ====
--
-- **API CHANGE HISTORY**
-- ======================
--
-- The underlying change log documents the API changes. Please read this carefully. The following notation is used:
--
-- * **Added** parts are expressed in bold type face.
-- * _Removed_ parts are expressed in italic type face.
--
-- Hereby the change log:
--
-- 2016-08-17: AI\_PATROLZONE:New( **PatrolSpawn,** PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed ) replaces AI\_PATROLZONE:New( PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed )
--
-- 2016-07-01: Initial class and API.
--
-- ===
--
-- AUTHORS and CONTRIBUTIONS
-- =========================
--
-- ### Contributions:
--
-- * **DutchBaron**: Testing.
--
-- ### Authors:
--
-- * **FlightControl**: Design & Programming
--
--
-- @module AI_PatrolZone
--- AI\_PATROLZONE class
-- @type AI_PATROLZONE
-- @field Group#GROUP PatrolGroup The @{Group} patrolling.
-- @field Zone#ZONE_BASE PatrolZone The @{Zone} where the patrol needs to be executed.
-- @field DCSTypes#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol.
-- @field DCSTypes#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol.
-- @field DCSTypes#Speed PatrolMinSpeed The minimum speed of the @{Group} in km/h.
-- @field DCSTypes#Speed PatrolMaxSpeed The maximum speed of the @{Group} in km/h.
-- @extends StateMachine#StateMachine
AI_PATROLZONE = {
ClassName = "AI_PATROLZONE",
}
--- Creates a new AI\_PATROLZONE object
-- @param #AI_PATROLZONE self
-- @param Zone#ZONE_BASE PatrolZone The @{Zone} where the patrol needs to be executed.
-- @param DCSTypes#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol.
-- @param DCSTypes#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol.
-- @param DCSTypes#Speed PatrolMinSpeed The minimum speed of the @{Group} in km/h.
-- @param DCSTypes#Speed PatrolMaxSpeed The maximum speed of the @{Group} in km/h.
-- @return #AI_PATROLZONE self
-- @usage
-- -- Define a new AI_PATROLZONE Object. This PatrolArea will patrol a group within PatrolZone between 3000 and 6000 meters, with a variying speed between 600 and 900 km/h.
-- PatrolZone = ZONE:New( 'PatrolZone' )
-- PatrolSpawn = SPAWN:New( 'Patrol Group' )
-- PatrolArea = AI_PATROLZONE:New( PatrolZone, 3000, 6000, 600, 900 )
function AI_PATROLZONE:New( PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed )
local FSMT = {
initial = 'None',
events = {
{ name = 'Start', from = '*', to = 'Route' },
{ name = 'Route', from = '*', to = 'Route' },
{ name = 'Patrol', from = { 'Patrol', 'Route' }, to = 'Patrol' },
{ name = 'RTB', from = 'Patrol', to = 'RTB' },
{ name = 'End', from = '*', to = 'End' },
{ name = 'Dead', from = '*', to = 'End' },
},
callbacks = {
onenterRoute = self._EnterRoute,
onenterPatrol = self._EnterPatrol,
onenterRTB = self._EnterRTB,
onenterEnd = self._EnterEnd,
},
}
-- Inherits from BASE
local self = BASE:Inherit( self, STATEMACHINE:New( FSMT ) )
self.PatrolZone = PatrolZone
self.PatrolFloorAltitude = PatrolFloorAltitude
self.PatrolCeilingAltitude = PatrolCeilingAltitude
self.PatrolMinSpeed = PatrolMinSpeed
self.PatrolMaxSpeed = PatrolMaxSpeed
return self
end
--- Set the @{Group} to act as the Patroller.
-- @param #AI_PATROLZONE self
-- @param Group#GROUP PatrolGroup The @{Group} patrolling.
-- @return #AI_PATROLZONE self
function AI_PATROLZONE:SetGroup( PatrolGroup )
self.PatrolGroup = PatrolGroup
self.PatrolGroupTemplateName = PatrolGroup:GetName()
return self
end
--- Sets (modifies) the minimum and maximum speed of the patrol.
-- @param #AI_PATROLZONE self
-- @param DCSTypes#Speed PatrolMinSpeed The minimum speed of the @{Group} in km/h.
-- @param DCSTypes#Speed PatrolMaxSpeed The maximum speed of the @{Group} in km/h.
-- @return #AI_PATROLZONE self
function AI_PATROLZONE: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_PATROLZONE self
-- @param DCSTypes#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol.
-- @param DCSTypes#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol.
-- @return #AI_PATROLZONE self
function AI_PATROLZONE:SetAltitude( PatrolFloorAltitude, PatrolCeilingAltitude )
self:F2( { PatrolFloorAltitude, PatrolCeilingAltitude } )
self.PatrolFloorAltitude = PatrolFloorAltitude
self.PatrolCeilingAltitude = PatrolCeilingAltitude
end
--- @param Group#GROUP PatrolGroup
function _NewPatrolRoute( PatrolGroup )
PatrolGroup:T( "NewPatrolRoute" )
local PatrolZone = PatrolGroup:GetState( PatrolGroup, "PatrolZone" ) -- PatrolZone#AI_PATROLZONE
PatrolZone:__Route( 1, PatrolGroup )
end
--- When the PatrolGroup is out of fuel, it is required that a new PatrolGroup is started, before the old PatrolGroup 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 PatrolGroup will continue for a given time its patrol task in orbit, while a new PatrolGroup is targetted to the AI\_PATROLZONE.
-- Once the time is finished, the old PatrolGroup will return to the base.
-- @param #AI_PATROLZONE self
-- @param #number PatrolFuelTresholdPercentage The treshold in percentage (between 0 and 1) when the PatrolGroup is considered to get out of fuel.
-- @param #number PatrolOutOfFuelOrbitTime The amount of seconds the out of fuel PatrolGroup will orbit before returning to the base.
-- @return #AI_PATROLZONE self
function AI_PATROLZONE:ManageFuel( PatrolFuelTresholdPercentage, PatrolOutOfFuelOrbitTime )
self.PatrolManageFuel = true
self.PatrolFuelTresholdPercentage = PatrolFuelTresholdPercentage
self.PatrolOutOfFuelOrbitTime = PatrolOutOfFuelOrbitTime
return self
end
--- Defines a new patrol route using the @{PatrolZone} parameters and settings.
-- @param #AI_PATROLZONE self
-- @return #AI_PATROLZONE self
function AI_PATROLZONE:_EnterRoute( AIGroup )
self:F2()
local PatrolRoute = {}
if self.PatrolGroup:IsAlive() then
--- Determine if the PatrolGroup is within the PatrolZone.
-- If not, make a waypoint within the to that the PatrolGroup will fly at maximum speed to that point.
-- --- Calculate the current route point.
-- local CurrentVec2 = self.PatrolGroup:GetVec2()
-- local CurrentAltitude = self.PatrolGroup:GetUnit(1):GetAltitude()
-- local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y )
-- local CurrentRoutePoint = CurrentPointVec3:RoutePointAir(
-- POINT_VEC3.RoutePointAltType.BARO,
-- POINT_VEC3.RoutePointType.TurningPoint,
-- POINT_VEC3.RoutePointAction.TurningPoint,
-- ToPatrolZoneSpeed,
-- true
-- )
--
-- PatrolRoute[#PatrolRoute+1] = CurrentRoutePoint
self:T2( PatrolRoute )
if self.PatrolGroup:IsNotInZone( self.PatrolZone ) then
--- Find a random 2D point in PatrolZone.
local ToPatrolZoneVec2 = self.PatrolZone:GetRandomVec2()
self:T2( ToPatrolZoneVec2 )
--- Define Speed and Altitude.
local ToPatrolZoneAltitude = math.random( self.PatrolFloorAltitude, self.PatrolCeilingAltitude )
local ToPatrolZoneSpeed = self.PatrolMaxSpeed
self:T2( ToPatrolZoneSpeed )
--- Obtain a 3D @{Point} from the 2D point + altitude.
local ToPatrolZonePointVec3 = POINT_VEC3:New( ToPatrolZoneVec2.x, ToPatrolZoneAltitude, ToPatrolZoneVec2.y )
--- Create a route point of type air.
local ToPatrolZoneRoutePoint = ToPatrolZonePointVec3:RoutePointAir(
POINT_VEC3.RoutePointAltType.BARO,
POINT_VEC3.RoutePointType.TurningPoint,
POINT_VEC3.RoutePointAction.TurningPoint,
ToPatrolZoneSpeed,
true
)
PatrolRoute[#PatrolRoute+1] = ToPatrolZoneRoutePoint
end
--- Define a random point in the @{Zone}. The AI will fly to that point within the zone.
--- Find a random 2D point in PatrolZone.
local ToTargetVec2 = self.PatrolZone:GetRandomVec2()
self:T2( ToTargetVec2 )
--- Define Speed and Altitude.
local ToTargetAltitude = math.random( self.PatrolFloorAltitude, self.PatrolCeilingAltitude )
local ToTargetSpeed = math.random( self.PatrolMinSpeed, self.PatrolMaxSpeed )
self:T2( { self.PatrolMinSpeed, self.PatrolMaxSpeed, ToTargetSpeed } )
--- Obtain a 3D @{Point} from the 2D point + altitude.
local ToTargetPointVec3 = POINT_VEC3:New( ToTargetVec2.x, ToTargetAltitude, ToTargetVec2.y )
--- Create a route point of type air.
local ToTargetRoutePoint = ToTargetPointVec3:RoutePointAir(
POINT_VEC3.RoutePointAltType.BARO,
POINT_VEC3.RoutePointType.TurningPoint,
POINT_VEC3.RoutePointAction.TurningPoint,
ToTargetSpeed,
true
)
--ToTargetPointVec3:SmokeRed()
PatrolRoute[#PatrolRoute+1] = ToTargetRoutePoint
--- Now we're going to do something special, we're going to call a function from a waypoint action at the PatrolGroup...
self.PatrolGroup:WayPointInitialize( PatrolRoute )
--- Do a trick, link the NewPatrolRoute function of the PATROLGROUP object to the PatrolGroup in a temporary variable ...
self.PatrolGroup:SetState( self.PatrolGroup, "PatrolZone", self )
self.PatrolGroup:WayPointFunction( #PatrolRoute, 1, "_NewPatrolRoute" )
--- NOW ROUTE THE GROUP!
self.PatrolGroup:WayPointExecute( 1 )
self:__Patrol( 30, self.PatrolGroup )
end
end
--- @param #AI_PATROLZONE self
function AI_PATROLZONE:_EnterPatrol( AIGroup )
self:F2()
if self.PatrolGroup and self.PatrolGroup:IsAlive() then
local Fuel = self.PatrolGroup:GetUnit(1):GetFuel()
if Fuel < self.PatrolFuelTresholdPercentage then
local OldPatrolGroup = self.PatrolGroup
local PatrolGroupTemplate = self.PatrolGroup:GetTemplate()
local OrbitTask = OldPatrolGroup:TaskOrbitCircle( math.random( self.PatrolFloorAltitude, self.PatrolCeilingAltitude ), self.PatrolMinSpeed )
local TimedOrbitTask = OldPatrolGroup:TaskControlled( OrbitTask, OldPatrolGroup:TaskCondition(nil,nil,nil,nil,self.PatrolOutOfFuelOrbitTime,nil ) )
OldPatrolGroup:SetTask( TimedOrbitTask, 10 )
self:RTB( self.PatrolGroup )
else
self:__Patrol( 30, self.PatrolGroup ) -- Execute the Patrol event after 30 seconds.
end
end
end

View File

@@ -230,9 +230,10 @@ function CONTROLLABLE:SetTask( DCSTask, WaitTime )
-- Controller.setTask( Controller, DCSTask )
if not WaitTime then
WaitTime = 1
Controller:setTask( DCSTask )
else
SCHEDULER:New( Controller, Controller.setTask, { DCSTask }, WaitTime )
end
SCHEDULER:New( Controller, Controller.setTask, { DCSTask }, WaitTime )
return self
end
@@ -2152,7 +2153,7 @@ function CONTROLLABLE:TaskFunction( WayPoint, WayPointIndex, FunctionString, Fun
local DCSTask
local DCSScript = {}
DCSScript[#DCSScript+1] = "local MissionControllable = CONTROLLABLE:Find( ... ) "
DCSScript[#DCSScript+1] = "local MissionControllable = GROUP:Find( ... ) "
if FunctionArguments and #FunctionArguments > 0 then
DCSScript[#DCSScript+1] = FunctionString .. "( MissionControllable, " .. table.concat( FunctionArguments, "," ) .. ")"

View File

@@ -673,19 +673,19 @@ function GROUP:GetMaxVelocity()
local DCSGroup = self:GetDCSObject()
if DCSGroup then
local MaxVelocity = 0
local GroupVelocityMax = 0
for Index, UnitData in pairs( DCSGroup:getUnits() ) do
local Velocity = UnitData:getVelocity()
local VelocityTotal = math.abs( Velocity.x ) + math.abs( Velocity.y ) + math.abs( Velocity.z )
local UnitVelocityVec3 = UnitData:getVelocity()
local UnitVelocity = math.abs( UnitVelocityVec3.x ) + math.abs( UnitVelocityVec3.y ) + math.abs( UnitVelocityVec3.z )
if VelocityTotal < MaxVelocity then
MaxVelocity = VelocityTotal
if UnitVelocity > GroupVelocityMax then
GroupVelocityMax = UnitVelocity
end
end
return MaxVelocity
return GroupVelocityMax
end
return nil

View File

@@ -34,8 +34,8 @@ Include.File( "Movement" )
Include.File( "Sead" )
Include.File( "Escort" )
Include.File( "MissileTrainer" )
Include.File( "PatrolZone" )
Include.File( "AIBalancer" )
Include.File( "AI_PatrolZone" )
--Include.File( "AIBalancer" )
Include.File( "AirbasePolice" )
Include.File( "Detection" )

View File

@@ -45,6 +45,8 @@
--
-- Hereby the change log:
--
-- 2016-08-17: PATROLZONE:New( **PatrolSpawn,** PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed ) replaces PATROLZONE:New( PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed )
--
-- 2016-07-01: Initial class and API.
--
-- ===
@@ -54,16 +56,17 @@
--
-- ### Contributions:
--
-- * DutchBaron: Testing.
-- * **DutchBaron**: Testing.
--
-- ### Authors:
--
-- * FlightControl: Design & Programming
-- * **FlightControl**: Design & Programming
--
--
-- @module PatrolZone
--- PATROLZONE class
-- @type PATROLZONE
-- @field Group#GROUP PatrolGroup The @{Group} patrolling.
@@ -77,8 +80,11 @@ PATROLZONE = {
ClassName = "PATROLZONE",
}
--- Creates a new PATROLZONE object, taking a @{Group} object as a parameter. The GROUP needs to be alive.
-- @param #PATROLZONE self
-- @param Spawn#SPAWN PatrolSpawn The @{SPAWN} object to spawn new group objects when required due to the fuel treshold.
-- @param Zone#ZONE_BASE PatrolZone The @{Zone} where the patrol needs to be executed.
-- @param DCSTypes#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol.
-- @param DCSTypes#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol.
@@ -88,13 +94,14 @@ PATROLZONE = {
-- @usage
-- -- Define a new PATROLZONE Object. This PatrolArea will patrol a group within PatrolZone between 3000 and 6000 meters, with a variying speed between 600 and 900 km/h.
-- PatrolZone = ZONE:New( 'PatrolZone' )
-- PatrolGroup = GROUP:FindByName( "Patrol Group" )
-- PatrolArea = PATROLZONE:New( PatrolGroup, PatrolZone, 3000, 6000, 600, 900 )
function PATROLZONE:New( PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed )
-- PatrolSpawn = SPAWN:New( "Patrol Group" )
-- PatrolArea = PATROLZONE:New( PatrolSpawn, PatrolZone, 3000, 6000, 600, 900 )
function PATROLZONE:New( PatrolSpawn, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed )
-- Inherits from BASE
local self = BASE:Inherit( self, BASE:New() )
self.PatrolSpawn = PatrolSpawn
self.PatrolZone = PatrolZone
self.PatrolFloorAltitude = PatrolFloorAltitude
self.PatrolCeilingAltitude = PatrolCeilingAltitude
@@ -104,6 +111,8 @@ function PATROLZONE:New( PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude,
return self
end
--- Set the @{Group} to act as the Patroller.
-- @param #PATROLZONE self
-- @param Group#GROUP PatrolGroup The @{Group} patrolling.
@@ -116,12 +125,13 @@ function PATROLZONE:SetGroup( PatrolGroup )
if not self.PatrolOutOfFuelMonitor then
self.PatrolOutOfFuelMonitor = SCHEDULER:New( nil, _MonitorOutOfFuelScheduled, { self }, 1, 120, 0 )
self.SpawnPatrolGroup = SPAWN:New( self.PatrolGroupTemplateName )
end
return self
end
--- Sets (modifies) the minimum and maximum speed of the patrol.
-- @param #PATROLZONE self
-- @param DCSTypes#Speed PatrolMinSpeed The minimum speed of the @{Group} in km/h.
@@ -134,6 +144,8 @@ function PATROLZONE:SetSpeed( PatrolMinSpeed, PatrolMaxSpeed )
self.PatrolMaxSpeed = PatrolMaxSpeed
end
--- Sets the floor and ceiling altitude of the patrol.
-- @param #PATROLZONE self
-- @param DCSTypes#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol.
@@ -156,6 +168,8 @@ function _NewPatrolRoute( PatrolGroup )
PatrolZone:NewPatrolRoute()
end
--- Defines a new patrol route using the @{PatrolZone} parameters and settings.
-- @param #PATROLZONE self
-- @return #PATROLZONE self
@@ -267,7 +281,6 @@ function PATROLZONE:ManageFuel( PatrolFuelTresholdPercentage, PatrolOutOfFuelOrb
if self.PatrolGroup then
self.PatrolOutOfFuelMonitor = SCHEDULER:New( self, self._MonitorOutOfFuelScheduled, {}, 1, 120, 0 )
self.SpawnPatrolGroup = SPAWN:New( self.PatrolGroupTemplateName )
end
return self
end
@@ -287,7 +300,7 @@ function _MonitorOutOfFuelScheduled( self )
local TimedOrbitTask = OldPatrolGroup:TaskControlled( OrbitTask, OldPatrolGroup:TaskCondition(nil,nil,nil,nil,self.PatrolOutOfFuelOrbitTime,nil ) )
OldPatrolGroup:SetTask( TimedOrbitTask, 10 )
local NewPatrolGroup = self.SpawnPatrolGroup:Spawn()
local NewPatrolGroup = self.PatrolSpawn:Spawn()
self.PatrolGroup = NewPatrolGroup
self:NewPatrolRoute()
end

View File

@@ -236,6 +236,7 @@ function POSITIONABLE:InAir()
return nil
end
--- Returns the POSITIONABLE velocity vector.
-- @param Positionable#POSITIONABLE self

View File

@@ -56,7 +56,7 @@ SCHEDULER = {
-- @return #SCHEDULER self
function SCHEDULER:New( TimeEventObject, TimeEventFunction, TimeEventFunctionArguments, StartSeconds, RepeatSecondsInterval, RandomizationFactor, StopSeconds )
local self = BASE:Inherit( self, BASE:New() )
self:F2( { TimeEventObject, TimeEventFunction, TimeEventFunctionArguments, StartSeconds, RepeatSecondsInterval, RandomizationFactor, StopSeconds } )
self:F2( { StartSeconds, RepeatSecondsInterval, RandomizationFactor, StopSeconds } )
self:Schedule( TimeEventObject, TimeEventFunction, TimeEventFunctionArguments, StartSeconds, RepeatSecondsInterval, RandomizationFactor, StopSeconds )
@@ -75,7 +75,8 @@ end
-- @param #number StopSeconds Specifies the amount of seconds when the scheduler will be stopped.
-- @return #SCHEDULER self
function SCHEDULER:Schedule( TimeEventObject, TimeEventFunction, TimeEventFunctionArguments, StartSeconds, RepeatSecondsInterval, RandomizationFactor, StopSeconds )
self:F2( { TimeEventFunctionArguments, StartSeconds, RepeatSecondsInterval, RandomizationFactor, StopSeconds } )
self:F2( { StartSeconds, RepeatSecondsInterval, RandomizationFactor, StopSeconds } )
self:T3( { TimeEventFunctionArguments } )
self.TimeEventObject = TimeEventObject
self.TimeEventFunction = TimeEventFunction
@@ -97,7 +98,7 @@ end
-- @param #SCHEDULER self
-- @return #SCHEDULER self
function SCHEDULER:Start()
self:F2( self.TimeEventObject )
self:F2()
if self.RepeatSecondsInterval ~= 0 then
self.Repeat = true
@@ -107,7 +108,8 @@ function SCHEDULER:Start()
if self.ScheduleID then
timer.removeFunction( self.ScheduleID )
end
self.ScheduleID = timer.scheduleFunction( self._Scheduler, self, timer.getTime() + self.StartSeconds + .01 )
self:T( { self.StartSeconds } )
self.ScheduleID = timer.scheduleFunction( self._Scheduler, self, timer.getTime() + self.StartSeconds + .001 )
end
return self

View File

@@ -182,11 +182,11 @@
--
-- ### Contributions:
--
-- * Aaron: Posed the idea for Group position randomization at SpawnInZone and make the Unit randomization separate from the Group randomization.
-- * **Aaron**: Posed the idea for Group position randomization at SpawnInZone and make the Unit randomization separate from the Group randomization.
--
-- ### Authors:
--
-- * FlightControl : Design & Programming
-- * **FlightControl**: Design & Programming
--
--
-- @module Spawn
@@ -490,6 +490,10 @@ function SPAWN:InitCleanUp( SpawnCleanUpInterval )
self.SpawnCleanUpInterval = SpawnCleanUpInterval
self.SpawnCleanUpTimeStamps = {}
local SpawnGroup, SpawnCursor = self:GetFirstAliveGroup()
self:T( { "CleanUp Scheduler:", SpawnGroup } )
--self.CleanUpFunction = routines.scheduleFunction( self._SpawnCleanUpScheduler, { self }, timer.getTime() + 1, SpawnCleanUpInterval )
self.CleanUpScheduler = SCHEDULER:New( self, self._SpawnCleanUpScheduler, {}, 1, SpawnCleanUpInterval, 0.2 )
return self
@@ -614,6 +618,7 @@ function SPAWN:SpawnWithIndex( SpawnIndex )
else
local SpawnTemplate = self.SpawnGroups[self.SpawnIndex].SpawnTemplate
self:T( SpawnTemplate.name )
if SpawnTemplate then
@@ -642,7 +647,7 @@ function SPAWN:SpawnWithIndex( SpawnIndex )
if self.RepeatOnEngineShutDown then
_EVENTDISPATCHER:OnEngineShutDownForTemplate( SpawnTemplate, self._OnEngineShutDown, self )
end
self:T3( SpawnTemplate )
self:T3( SpawnTemplate.name )
self.SpawnGroups[self.SpawnIndex].Group = _DATABASE:Spawn( SpawnTemplate )
@@ -1506,28 +1511,72 @@ function SPAWN:_Scheduler()
return true
end
--- Schedules the CleanUp of Groups
-- @param #SPAWN self
-- @return #boolean True = Continue Scheduler
function SPAWN:_SpawnCleanUpScheduler()
self:F( { "CleanUp Scheduler:", self.SpawnTemplatePrefix } )
local SpawnCursor
local SpawnGroup, SpawnCursor = self:GetFirstAliveGroup( SpawnCursor )
local SpawnGroup, SpawnCursor = self:GetFirstAliveGroup()
self:T( { "CleanUp Scheduler:", SpawnGroup } )
while SpawnGroup do
if SpawnGroup:AllOnGround() and SpawnGroup:GetMaxVelocity() < 1 then
if not self.SpawnCleanUpTimeStamps[SpawnGroup:GetName()] then
self.SpawnCleanUpTimeStamps[SpawnGroup:GetName()] = timer.getTime()
else
if self.SpawnCleanUpTimeStamps[SpawnGroup:GetName()] + self.SpawnCleanUpInterval < timer.getTime() then
self:T( { "CleanUp Scheduler:", "Cleaning:", SpawnGroup } )
SpawnGroup:Destroy()
end
end
else
self.SpawnCleanUpTimeStamps[SpawnGroup:GetName()] = nil
end
local SpawnUnits = SpawnGroup:GetUnits()
for UnitID, UnitData in pairs( SpawnUnits ) do
local SpawnUnit = UnitData -- Unit#UNIT
local SpawnUnitName = SpawnUnit:GetName()
self.SpawnCleanUpTimeStamps[SpawnUnitName] = self.SpawnCleanUpTimeStamps[SpawnUnitName] or {}
local Stamp = self.SpawnCleanUpTimeStamps[SpawnUnitName]
self:T( { SpawnUnitName, Stamp } )
if Stamp.Moved then
if SpawnUnit:InAir() == false then
if SpawnUnit:GetVelocityKMH() < 1 then
-- If the plane is not moving, and is on the ground, assign it with a timestamp...
if not Stamp.Time then
Stamp.Time = timer.getTime()
else
if Stamp.Time + self.SpawnCleanUpInterval < timer.getTime() then
self:T( { "CleanUp Scheduler:", "ReSpawning:", SpawnGroup:GetName() } )
self:ReSpawn( SpawnCursor )
Stamp.Moved = nil
Stamp.Time = nil
end
end
else
Stamp.Time = nil
end
else
Stamp.Moved = nil
Stamp.Time = nil
end
else
if SpawnUnit:InAir() == false and SpawnUnit:GetVelocityKMH() > 1 then
Stamp.Moved = true
else
-- If the plane did not move and on the runway for about 3 minutes, clean it.
if SpawnUnit:IsAboveRunway() and SpawnUnit:GetVelocityKMH() < 1 then
if not Stamp.Time then
Stamp.Time = timer.getTime()
end
if Stamp.Time + 180 < timer.getTime() then
self:T( { "CleanUp Scheduler:", "ReSpawning inactive group:", SpawnGroup:GetName() } )
self:ReSpawn( SpawnCursor )
Stamp.Moved = nil
Stamp.Time = nil
end
else
Stamp.Moved = nil
Stamp.Time = nil
end
end
end
end
SpawnGroup, SpawnCursor = self:GetNextAliveGroup( SpawnCursor )

View File

@@ -18,6 +18,7 @@
--- STATEMACHINE class
-- @type STATEMACHINE
-- @extends Base#BASE
STATEMACHINE = {
ClassName = "STATEMACHINE",
}
@@ -47,7 +48,10 @@ function STATEMACHINE:New( options )
for _, event in ipairs(options.events or {}) do
local name = event.name
local __name = "__" .. event.name
self[name] = self[name] or self:_create_transition(name)
self[__name] = self[__name] or self:_delayed_transition(name)
self:T( "Added methods: " .. name .. ", " .. __name )
self.events[name] = self.events[name] or { map = {} }
self:_add_to_map(self.events[name].map, event)
end
@@ -95,56 +99,81 @@ function STATEMACHINE:_call_handler(handler, params)
end
end
function STATEMACHINE:_create_transition(name)
self:E( { name = name } )
return function(self, ...)
local can, to = self:can(name)
self:T( { name, can, to } )
function STATEMACHINE._handler( self, EventName, ... )
if can then
local from = self.current
local params = { self, name, from, to, ... }
self:F( EventName )
if self:_call_handler(self["onbefore" .. name], params) == false
or self:_call_handler(self["onleave" .. from], params) == false then
return false
end
local can, to = self:can(EventName)
self:T( { EventName, can, to } )
local ReturnValues = nil
self.current = to
local execute = true
local subtable = self:_gosub( to, name )
for _, sub in pairs( subtable ) do
self:F( "calling sub: " .. sub.event )
sub.fsm.fsmparent = self
sub.fsm.returnevents = sub.returnevents
sub.fsm[sub.event]( sub.fsm )
execute = true
end
local fsmparent, event = self:_isendstate( to )
if fsmparent and event then
self:F( { "end state: ", fsmparent, event } )
self:_call_handler(self["onenter" .. to] or self["on" .. to], params)
self:_call_handler(self["onafter" .. name] or self["on" .. name], params)
self:_call_handler(self["onstatechange"], params)
fsmparent[event]( fsmparent )
execute = false
end
if can then
local from = self.current
local params = { self, ..., EventName, from, to }
if execute then
self:F( { "execute: " .. to, name } )
self:_call_handler(self["onenter" .. to] or self["on" .. to], params)
self:_call_handler(self["onafter" .. name] or self["on" .. name], params)
self:_call_handler(self["onstatechange"], params)
end
return true
if self:_call_handler(self["onbefore" .. EventName], params) == false
or self:_call_handler(self["onleave" .. from], params) == false then
return false
end
return false
self.current = to
local execute = true
local subtable = self:_gosub( to, EventName )
for _, sub in pairs( subtable ) do
self:F2( "calling sub: " .. sub.event )
sub.fsm.fsmparent = self
sub.fsm.returnevents = sub.returnevents
sub.fsm[sub.event]( sub.fsm )
execute = true
end
local fsmparent, event = self:_isendstate( to )
if fsmparent and event then
self:F2( { "end state: ", fsmparent, event } )
self:_call_handler(self["onenter" .. to] or self["on" .. to], params)
self:_call_handler(self["onafter" .. EventName] or self["on" .. EventName], params)
self:_call_handler(self["onstatechange"], params)
fsmparent[event]( fsmparent )
execute = false
end
if execute then
self:T3( { onenter = "onenter" .. to, callback = self["onenter" .. to] } )
self:_call_handler(self["onenter" .. to] or self["on" .. to], params)
self:T3( { On = "OnBefore" .. to, callback = self["OnBefore" .. to] } )
if ( self:_call_handler(self["OnBefore" .. to], params ) ~= false ) then
self:T3( { onafter = "onafter" .. EventName, callback = self["onafter" .. EventName] } )
self:_call_handler(self["onafter" .. EventName] or self["on" .. EventName], params)
self:T3( { On = "OnAfter" .. to, callback = self["OnAfter" .. to] } )
ReturnValues = self:_call_handler(self["OnAfter" .. to], params )
end
self:_call_handler(self["onstatechange"], params)
end
return ReturnValues
end
return nil
end
function STATEMACHINE:_delayed_transition( EventName )
self:E( { EventName = EventName } )
return function( self, DelaySeconds, ... )
self:T( "Delayed Event: " .. EventName )
SCHEDULER:New( self, self._handler, { EventName, ... }, DelaySeconds )
end
end
function STATEMACHINE:_create_transition( EventName )
self:E( { Event = EventName } )
return function( self, ... ) return self._handler( self, EventName , ... ) end
end
function STATEMACHINE:_gosub( parentstate, parentevent )

View File

@@ -0,0 +1,283 @@
--- This module contains the AIBALANCER class.
--
-- ===
--
-- 1) @{AIBalancer#AIBALANCER} class, extends @{Base#BASE}
-- =======================================================
-- The @{AIBalancer#AIBALANCER} class controls the dynamic spawning of AI GROUPS depending on a SET_CLIENT.
-- There will be as many AI GROUPS spawned as there at CLIENTS in SET_CLIENT not spawned.
-- The AIBalancer uses the @{PatrolZone#PATROLZONE} class to make AI patrol an zone until the fuel treshold is reached.
--
-- 1.1) AIBALANCER construction method:
-- ------------------------------------
-- Create a new AIBALANCER object with the @{#AIBALANCER.New} method:
--
-- * @{#AIBALANCER.New}: Creates a new AIBALANCER object.
--
-- 1.2) AIBALANCER returns AI to Airbases:
-- ---------------------------------------
-- You can configure to have the AI to return to:
--
-- * @{#AIBALANCER.ReturnToHomeAirbase}: Returns the AI to the home @{Airbase#AIRBASE}.
-- * @{#AIBALANCER.ReturnToNearestAirbases}: Returns the AI to the nearest friendly @{Airbase#AIRBASE}.
--
-- 1.3) AIBALANCER allows AI to patrol specific zones:
-- ---------------------------------------------------
-- Use @{AIBalancer#AIBALANCER.SetPatrolZone}() to specify a zone where the AI needs to patrol.
--
-- ===
--
-- **API CHANGE HISTORY**
-- ======================
--
-- The underlying change log documents the API changes. Please read this carefully. The following notation is used:
--
-- * **Added** parts are expressed in bold type face.
-- * _Removed_ parts are expressed in italic type face.
--
-- Hereby the change log:
--
-- 2016-08-17: SPAWN:**InitCleanUp**( SpawnCleanUpInterval ) replaces SPAWN:_CleanUp_( SpawnCleanUpInterval )
--
-- * Want to ensure that the methods starting with **Init** are the first called methods before any _Spawn_ method is called!
-- * This notation makes it now more clear which methods are initialization methods and which methods are Spawn enablement methods.
--
-- ===
--
-- AUTHORS and CONTRIBUTIONS
-- =========================
--
-- ### Contributions:
--
-- * **Dutch_Baron (James)**: Who you can search on the Eagle Dynamics Forums.
-- Working together with James has resulted in the creation of the AIBALANCER class.
-- James has shared his ideas on balancing AI with air units, and together we made a first design which you can use now :-)
--
-- * **SNAFU**:
-- Had a couple of mails with the guys to validate, if the same concept in the GCI/CAP script could be reworked within MOOSE.
-- None of the script code has been used however within the new AIBALANCER moose class.
--
-- ### Authors:
--
-- * FlightControl: Framework Design & Programming
--
-- @module AIBalancer
--- AIBALANCER class
-- @type AIBALANCER
-- @field Set#SET_CLIENT SetClient
-- @field Spawn#SPAWN SpawnAI
-- @field #boolean ToNearestAirbase
-- @field Set#SET_AIRBASE ReturnAirbaseSet
-- @field DCSTypes#Distance ReturnTresholdRange
-- @field #boolean ToHomeAirbase
-- @field PatrolZone#PATROLZONE PatrolZone
-- @extends Base#BASE
AIBALANCER = {
ClassName = "AIBALANCER",
PatrolZones = {},
AIGroups = {},
}
--- Creates a new AIBALANCER object, building a set of units belonging to a coalitions, categories, countries, types or with defined prefix names.
-- @param #AIBALANCER self
-- @param SetClient A SET_CLIENT object that will contain the CLIENT objects to be monitored if they are alive or not (joined by a player).
-- @param SpawnAI A SPAWN object that will spawn the AI units required, balancing the SetClient.
-- @return #AIBALANCER self
function AIBALANCER:New( SetClient, SpawnAI )
-- Inherits from BASE
local self = BASE:Inherit( self, BASE:New() )
self.SetClient = SetClient
if type( SpawnAI ) == "table" then
if SpawnAI.ClassName and SpawnAI.ClassName == "SPAWN" then
self.SpawnAI = { SpawnAI }
else
local SpawnObjects = true
for SpawnObjectID, SpawnObject in pairs( SpawnAI ) do
if SpawnObject.ClassName and SpawnObject.ClassName == "SPAWN" then
self:E( SpawnObject.ClassName )
else
self:E( "other object" )
SpawnObjects = false
end
end
if SpawnObjects == true then
self.SpawnAI = SpawnAI
else
error( "No SPAWN object given in parameter SpawnAI, either as a single object or as a table of objects!" )
end
end
end
self.ToNearestAirbase = false
self.ReturnHomeAirbase = false
self.AIMonitorSchedule = SCHEDULER:New( self, self._ClientAliveMonitorScheduler, {}, 1, 10, 0 )
return self
end
--- Returns the AI to the nearest friendly @{Airbase#AIRBASE}.
-- @param #AIBALANCER self
-- @param DCSTypes#Distance ReturnTresholdRange If there is an enemy @{Client#CLIENT} within the ReturnTresholdRange given in meters, the AI will not return to the nearest @{Airbase#AIRBASE}.
-- @param Set#SET_AIRBASE ReturnAirbaseSet The SET of @{Set#SET_AIRBASE}s to evaluate where to return to.
function AIBALANCER:ReturnToNearestAirbases( ReturnTresholdRange, ReturnAirbaseSet )
self.ToNearestAirbase = true
self.ReturnTresholdRange = ReturnTresholdRange
self.ReturnAirbaseSet = ReturnAirbaseSet
end
--- Returns the AI to the home @{Airbase#AIRBASE}.
-- @param #AIBALANCER self
-- @param DCSTypes#Distance ReturnTresholdRange If there is an enemy @{Client#CLIENT} within the ReturnTresholdRange given in meters, the AI will not return to the nearest @{Airbase#AIRBASE}.
function AIBALANCER:ReturnToHomeAirbase( ReturnTresholdRange )
self.ToHomeAirbase = true
self.ReturnTresholdRange = ReturnTresholdRange
end
--- Let the AI patrol a @{Zone} with a given Speed range and Altitude range.
-- @param #AIBALANCER self
-- @param PatrolZone#PATROLZONE PatrolZone The @{PatrolZone} where the AI needs to patrol.
-- @return PatrolZone#PATROLZONE self
function AIBALANCER:SetPatrolZone( PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed )
self.PatrolZone = PATROLZONE:New(
self.SpawnAI,
PatrolZone,
PatrolFloorAltitude,
PatrolCeilingAltitude,
PatrolMinSpeed,
PatrolMaxSpeed
)
end
--- Get the @{PatrolZone} object assigned by the @{AIBalancer} object.
-- @param #AIBALANCER self
-- @return PatrolZone#PATROLZONE PatrolZone The @{PatrolZone} where the AI needs to patrol.
function AIBALANCER:GetPatrolZone()
return self.PatrolZone
end
--- @param #AIBALANCER self
function AIBALANCER:_ClientAliveMonitorScheduler()
self.SetClient:ForEachClient(
--- @param Client#CLIENT Client
function( Client )
local ClientAIAliveState = Client:GetState( self, 'AIAlive' )
self:T( ClientAIAliveState )
if Client:IsAlive() then
if ClientAIAliveState == true then
Client:SetState( self, 'AIAlive', false )
local AIGroup = self.AIGroups[Client.UnitName] -- Group#GROUP
-- local PatrolZone = Client:GetState( self, "PatrolZone" )
-- if PatrolZone then
-- PatrolZone = nil
-- Client:ClearState( self, "PatrolZone" )
-- end
if self.ToNearestAirbase == false and self.ToHomeAirbase == false then
AIGroup:Destroy()
else
-- We test if there is no other CLIENT within the self.ReturnTresholdRange of the first unit of the AI group.
-- If there is a CLIENT, the AI stays engaged and will not return.
-- If there is no CLIENT within the self.ReturnTresholdRange, then the unit will return to the Airbase return method selected.
local PlayerInRange = { Value = false }
local RangeZone = ZONE_RADIUS:New( 'RangeZone', AIGroup:GetVec2(), self.ReturnTresholdRange )
self:E( RangeZone )
_DATABASE:ForEachPlayer(
--- @param Unit#UNIT RangeTestUnit
function( RangeTestUnit, RangeZone, AIGroup, PlayerInRange )
self:E( { PlayerInRange, RangeTestUnit.UnitName, RangeZone.ZoneName } )
if RangeTestUnit:IsInZone( RangeZone ) == true then
self:E( "in zone" )
if RangeTestUnit:GetCoalition() ~= AIGroup:GetCoalition() then
self:E( "in range" )
PlayerInRange.Value = true
end
end
end,
--- @param Zone#ZONE_RADIUS RangeZone
-- @param Group#GROUP AIGroup
function( RangeZone, AIGroup, PlayerInRange )
local AIGroupTemplate = AIGroup:GetTemplate()
if PlayerInRange.Value == false then
if self.ToHomeAirbase == true then
local WayPointCount = #AIGroupTemplate.route.points
local SwitchWayPointCommand = AIGroup:CommandSwitchWayPoint( 1, WayPointCount, 1 )
AIGroup:SetCommand( SwitchWayPointCommand )
AIGroup:MessageToRed( "Returning to home base ...", 30 )
else
-- Okay, we need to send this Group back to the nearest base of the Coalition of the AI.
--TODO: i need to rework the POINT_VEC2 thing.
local PointVec2 = POINT_VEC2:New( AIGroup:GetVec2().x, AIGroup:GetVec2().y )
local ClosestAirbase = self.ReturnAirbaseSet:FindNearestAirbaseFromPointVec2( PointVec2 )
self:T( ClosestAirbase.AirbaseName )
AIGroup:MessageToRed( "Returning to " .. ClosestAirbase:GetName().. " ...", 30 )
local RTBRoute = AIGroup:RouteReturnToAirbase( ClosestAirbase )
AIGroupTemplate.route = RTBRoute
AIGroup:Respawn( AIGroupTemplate )
end
end
end
, RangeZone, AIGroup, PlayerInRange
)
end
end
else
if not ClientAIAliveState or ClientAIAliveState == false then
Client:SetState( self, 'AIAlive', true )
-- OK, spawn a new group from the SpawnAI objects provided.
local SpawnAICount = #self.SpawnAI
local SpawnAIIndex = math.random( 1, SpawnAICount )
local AIGroup = self.SpawnAI[SpawnAIIndex]:Spawn()
AIGroup:E( "spawning new AIGroup" )
--TODO: need to rework UnitName thing ...
self.AIGroups[Client.UnitName] = AIGroup
--- Now test if the AIGroup needs to patrol a zone, otherwise let it follow its route...
if self.PatrolZone then
self.PatrolZones[#self.PatrolZones+1] = PATROLZONE:New(
self.PatrolZone.PatrolZone,
self.PatrolZone.PatrolFloorAltitude,
self.PatrolZone.PatrolCeilingAltitude,
self.PatrolZone.PatrolMinSpeed,
self.PatrolZone.PatrolMaxSpeed
)
if self.PatrolZone.PatrolManageFuel == true then
self.PatrolZones[#self.PatrolZones]:ManageFuel( self.PatrolZone.PatrolFuelTresholdPercentage, self.PatrolZone.PatrolOutOfFuelOrbitTime )
end
self.PatrolZones[#self.PatrolZones]:SetGroup( AIGroup )
--self.PatrolZones[#self.PatrolZones+1] = PatrolZone
--Client:SetState( self, "PatrolZone", PatrolZone )
end
end
end
end
)
return true
end

View File

@@ -1,3 +1,12 @@
2016-08-17
- Making the PATROLZONE class work with a SPAWN object, instead of a GROUP object.
-- The SPAWN object is needed to make the FuelManagement logic work correctly. New planes need to be spawned when old planes are out of fuel.
-- Changed the PATROLZONE:New() method to work with a SPAWN object instead of a GROUP object.
- Making the AIBALANCER class work with the modified PATROLZONE class SPAWN object.
-- Added AIBALANCER:GetPatrolZone() method.
2016-08-15
- Removed the old classes and moved into an "Old" folder in the Moose/Development folder.