SET_CLIENT made + AIBALANCER

This commit is contained in:
FlightControl
2016-06-10 14:33:08 +02:00
parent bff0c0bf4e
commit cb4d8bbde3
27 changed files with 1556 additions and 208 deletions

View File

@@ -0,0 +1,74 @@
--- 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.
--
-- 1.1) AIBALANCER construction method:
-- ------------------------------------
-- Create a new AIBALANCER object with the @{#AIBALANCER.New} method:
--
-- * @{#AIBALANCER.New}: Creates a new AIBALANCER object.
--
--
-- ===
-- @module AIBalancer
-- @author FlightControl
--- AIBALANCER class
-- @type AIBALANCER
-- @field Set#SET_CLIENT SetClient
-- @field Spawn#SPAWN SpawnAI
-- @extends Base#BASE
AIBALANCER = {
ClassName = "AIBALANCER",
}
--- 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
self.SpawnAI = SpawnAI
self.AIMonitorSchedule = SCHEDULER:New( self, self._ClientAliveMonitorScheduler, {}, 1, 10, 0 )
return self
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 = Client:GetState( self, 'AIGroup' ) -- Group#GROUP
AIGroup:Destroy()
end
else
if not ClientAIAliveState or ClientAIAliveState == false then
Client:SetState( self, 'AIAlive', true )
Client:SetState( self, 'AIGroup', self.SpawnAI:Spawn() )
end
end
end
)
return true
end

View File

@@ -68,7 +68,8 @@ local _TraceClassMethod = {}
BASE = {
ClassName = "BASE",
ClassID = 0,
Events = {}
Events = {},
States = {}
}
--- The Formation Class
@@ -323,6 +324,36 @@ function BASE:onEvent(event)
end
end
function BASE:SetState( Object, StateName, State )
local ClassNameAndID = Object:GetClassNameAndID()
if not self.States[ClassNameAndID] then
self.States[ClassNameAndID] = {}
end
self.States[ClassNameAndID][StateName] = State
return self.States[ClassNameAndID][StateName]
end
function BASE:GetState( Object, StateName )
local ClassNameAndID = Object:GetClassNameAndID()
if self.States[ClassNameAndID] then
return self.States[ClassNameAndID][StateName]
end
return nil
end
function BASE:ClearState( Object, StateName )
local ClassNameAndID = Object:GetClassNameAndID()
if self.States[ClassNameAndID] then
self.States[ClassNameAndID][StateName] = nil
end
end
-- Trace section
-- Log a trace (only shown when trace is on)
@@ -426,10 +457,10 @@ function BASE:F3( Arguments )
end
--- Trace a function logic. Can be anywhere within the function logic.
--- Trace a function logic.
-- @param #BASE self
-- @param Arguments A #table or any field.
function BASE:T( Arguments, DebugInfoCurrentParam, DebugInfoFromParam )
function BASE:_T( Arguments, DebugInfoCurrentParam, DebugInfoFromParam )
if _TraceOn and ( ( _TraceAll == true ) or ( _TraceClass[self.ClassName] or _TraceClassMethod[self.ClassName] ) ) then
@@ -452,6 +483,21 @@ function BASE:T( Arguments, DebugInfoCurrentParam, DebugInfoFromParam )
end
end
--- Trace a function logic level 1. Can be anywhere within the function logic.
-- @param #BASE self
-- @param Arguments A #table or any field.
function BASE:T( Arguments )
local DebugInfoCurrent = debug.getinfo( 2, "nl" )
local DebugInfoFrom = debug.getinfo( 3, "l" )
if _TraceLevel >= 1 then
self:_T( Arguments, DebugInfoCurrent, DebugInfoFrom )
end
end
--- Trace a function logic level 2. Can be anywhere within the function logic.
-- @param #BASE self
-- @param Arguments A #table or any field.
@@ -461,7 +507,7 @@ function BASE:T2( Arguments )
local DebugInfoFrom = debug.getinfo( 3, "l" )
if _TraceLevel >= 2 then
self:T( Arguments, DebugInfoCurrent, DebugInfoFrom )
self:_T( Arguments, DebugInfoCurrent, DebugInfoFrom )
end
end
@@ -475,7 +521,7 @@ function BASE:T3( Arguments )
local DebugInfoFrom = debug.getinfo( 3, "l" )
if _TraceLevel >= 3 then
self:T( Arguments, DebugInfoCurrent, DebugInfoFrom )
self:_T( Arguments, DebugInfoCurrent, DebugInfoFrom )
end
end

View File

@@ -288,7 +288,7 @@ end
-- @param #DATABASE self
-- @param #table GroupTemplate
-- @return #DATABASE self
function DATABASE:_RegisterTemplate( GroupTemplate )
function DATABASE:_RegisterTemplate( GroupTemplate, CoalitionName, CategoryName, CountryName )
local GroupTemplateName = env.getValueDictByKey(GroupTemplate.name)
@@ -307,6 +307,9 @@ function DATABASE:_RegisterTemplate( GroupTemplate )
self.Templates.Groups[GroupTemplateName].groupId = GroupTemplate.groupId
self.Templates.Groups[GroupTemplateName].UnitCount = #GroupTemplate.units
self.Templates.Groups[GroupTemplateName].Units = GroupTemplate.units
self.Templates.Groups[GroupTemplateName].CategoryName = CategoryName
self.Templates.Groups[GroupTemplateName].CoalitionName = CoalitionName
self.Templates.Groups[GroupTemplateName].CountryName = CountryName
self:T2( { "Group", self.Templates.Groups[GroupTemplateName].GroupName, self.Templates.Groups[GroupTemplateName].UnitCount } )
@@ -319,9 +322,15 @@ function DATABASE:_RegisterTemplate( GroupTemplate )
self.Templates.Units[UnitTemplateName].GroupName = GroupTemplateName
self.Templates.Units[UnitTemplateName].GroupTemplate = GroupTemplate
self.Templates.Units[UnitTemplateName].GroupId = GroupTemplate.groupId
self.Templates.Units[UnitTemplateName].CategoryName = CategoryName
self.Templates.Units[UnitTemplateName].CoalitionName = CoalitionName
self.Templates.Units[UnitTemplateName].CountryName = CountryName
self:E( {"skill",UnitTemplate.skill})
if UnitTemplate.skill and (UnitTemplate.skill == "Client" or UnitTemplate.skill == "Player") then
self.Templates.ClientsByName[UnitTemplateName] = UnitTemplate
self.Templates.ClientsByName[UnitTemplateName].CategoryName = CategoryName
self.Templates.ClientsByName[UnitTemplateName].CoalitionName = CoalitionName
self.Templates.ClientsByName[UnitTemplateName].CountryName = CountryName
self.Templates.ClientsByID[UnitTemplate.unitId] = UnitTemplate
end
self:E( { "Unit", self.Templates.Units[UnitTemplateName].UnitName } )
@@ -617,25 +626,25 @@ function DATABASE:_RegisterTemplates()
self.Navpoints = {}
self.UNITS = {}
--Build routines.db.units and self.Navpoints
for coa_name, coa_data in pairs(env.mission.coalition) do
for CoalitionName, coa_data in pairs(env.mission.coalition) do
if (coa_name == 'red' or coa_name == 'blue') and type(coa_data) == 'table' then
if (CoalitionName == 'red' or CoalitionName == 'blue') and type(coa_data) == 'table' then
--self.Units[coa_name] = {}
----------------------------------------------
-- build nav points DB
self.Navpoints[coa_name] = {}
self.Navpoints[CoalitionName] = {}
if coa_data.nav_points then --navpoints
for nav_ind, nav_data in pairs(coa_data.nav_points) do
if type(nav_data) == 'table' then
self.Navpoints[coa_name][nav_ind] = routines.utils.deepCopy(nav_data)
self.Navpoints[CoalitionName][nav_ind] = routines.utils.deepCopy(nav_data)
self.Navpoints[coa_name][nav_ind]['name'] = nav_data.callsignStr -- name is a little bit more self-explanatory.
self.Navpoints[coa_name][nav_ind]['point'] = {} -- point is used by SSE, support it.
self.Navpoints[coa_name][nav_ind]['point']['x'] = nav_data.x
self.Navpoints[coa_name][nav_ind]['point']['y'] = 0
self.Navpoints[coa_name][nav_ind]['point']['z'] = nav_data.y
self.Navpoints[CoalitionName][nav_ind]['name'] = nav_data.callsignStr -- name is a little bit more self-explanatory.
self.Navpoints[CoalitionName][nav_ind]['point'] = {} -- point is used by SSE, support it.
self.Navpoints[CoalitionName][nav_ind]['point']['x'] = nav_data.x
self.Navpoints[CoalitionName][nav_ind]['point']['y'] = 0
self.Navpoints[CoalitionName][nav_ind]['point']['z'] = nav_data.y
end
end
end
@@ -643,7 +652,7 @@ function DATABASE:_RegisterTemplates()
if coa_data.country then --there is a country table
for cntry_id, cntry_data in pairs(coa_data.country) do
local countryName = string.lower(cntry_data.name)
local CountryName = string.upper(cntry_data.name)
--self.Units[coa_name][countryName] = {}
--self.Units[coa_name][countryName]["countryId"] = cntry_data.id
@@ -653,7 +662,7 @@ function DATABASE:_RegisterTemplates()
if obj_type_name == "helicopter" or obj_type_name == "ship" or obj_type_name == "plane" or obj_type_name == "vehicle" or obj_type_name == "static" then --should be an unncessary check
local category = obj_type_name
local CategoryName = obj_type_name
if ((type(obj_type_data) == 'table') and obj_type_data.group and (type(obj_type_data.group) == 'table') and (#obj_type_data.group > 0)) then --there's a group!
@@ -662,7 +671,7 @@ function DATABASE:_RegisterTemplates()
for group_num, GroupTemplate in pairs(obj_type_data.group) do
if GroupTemplate and GroupTemplate.units and type(GroupTemplate.units) == 'table' then --making sure again- this is a valid group
self:_RegisterTemplate( GroupTemplate )
self:_RegisterTemplate( GroupTemplate, CoalitionName, CategoryName, CountryName )
end --if GroupTemplate and GroupTemplate.units then
end --for group_num, GroupTemplate in pairs(obj_type_data.group) do
end --if ((type(obj_type_data) == 'table') and obj_type_data.group and (type(obj_type_data.group) == 'table') and (#obj_type_data.group > 0)) then

View File

@@ -35,6 +35,7 @@ Include.File( "Movement" )
Include.File( "Sead" )
Include.File( "Escort" )
Include.File( "MissileTrainer" )
Include.File( "AIBalancer" )

View File

@@ -1247,11 +1247,11 @@ SET_CLIENT = {
neutral = coalition.side.NEUTRAL,
},
Categories = {
plane = Client.Category.AIRPLANE,
helicopter = Client.Category.HELICOPTER,
ground = Client.Category.GROUND_CLIENT,
ship = Client.Category.SHIP,
structure = Client.Category.STRUCTURE,
plane = "plane",
helicopter = "helicopter",
ground = "vehicle",
ship = "ship",
structure = "static",
},
},
}
@@ -1432,12 +1432,7 @@ end
function SET_CLIENT:AddInDatabase( Event )
self:F3( { Event } )
if not self.Database[Event.IniDCSClientName] then
self.Database[Event.IniDCSClientName] = CLIENT:Register( Event.IniDCSClientName )
self:T3( self.Database[Event.IniDCSClientName] )
end
return Event.IniDCSClientName, self.Database[Event.IniDCSClientName]
return Event.IniDCSUnitName, self.Database[Event.IniDCSUnitName]
end
--- Handles the Database to check on any event that Object exists in the Database.
@@ -1449,7 +1444,7 @@ end
function SET_CLIENT:FindInDatabase( Event )
self:F3( { Event } )
return Event.IniDCSClientName, self.Database[Event.IniDCSClientName]
return Event.IniDCSUnitName, self.Database[Event.IniDCSUnitName]
end
--- Interate the SET_CLIENT and call an interator function for each **alive** CLIENT, providing the CLIENT and optional parameters.
@@ -1508,97 +1503,82 @@ function SET_CLIENT:ForEachClientNotInZone( ZoneObject, IteratorFunction, ... )
return self
end
----- Interate the SET_CLIENT and call an interator function for each **alive** player, providing the Client of the player and optional parameters.
---- @param #SET_CLIENT self
---- @param #function IteratorFunction The function that will be called when there is an alive player in the SET_CLIENT. The function needs to accept a CLIENT parameter.
---- @return #SET_CLIENT self
--function SET_CLIENT:ForEachPlayer( IteratorFunction, ... )
-- self:F2( arg )
--
-- self:ForEach( IteratorFunction, arg, self.PlayersAlive )
--
-- return self
--end
--
--
----- Interate the SET_CLIENT and call an interator function for each client, providing the Client to the function and optional parameters.
---- @param #SET_CLIENT self
---- @param #function IteratorFunction The function that will be called when there is an alive player in the SET_CLIENT. The function needs to accept a CLIENT parameter.
---- @return #SET_CLIENT self
--function SET_CLIENT:ForEachClient( IteratorFunction, ... )
-- self:F2( arg )
--
-- self:ForEach( IteratorFunction, arg, self.Clients )
--
-- return self
--end
---
-- @param #SET_CLIENT self
-- @param Client#CLIENT MClient
-- @return #SET_CLIENT self
function SET_CLIENT:IsIncludeObject( MClient )
self:F2( MClient )
local MClientInclude = true
if self.Filter.Coalitions then
local MClientCoalition = false
for CoalitionID, CoalitionName in pairs( self.Filter.Coalitions ) do
self:T3( { "Coalition:", MClient:GetCoalition(), self.FilterMeta.Coalitions[CoalitionName], CoalitionName } )
if self.FilterMeta.Coalitions[CoalitionName] and self.FilterMeta.Coalitions[CoalitionName] == MClient:GetCoalition() then
MClientCoalition = true
if MClient then
local MClientName = MClient.UnitName
if self.Filter.Coalitions then
local MClientCoalition = false
for CoalitionID, CoalitionName in pairs( self.Filter.Coalitions ) do
local ClientCoalitionName = _DATABASE.Templates.ClientsByName[MClientName].CoalitionName
self:T3( { "Coalition:", ClientCoalitionName, self.FilterMeta.Coalitions[CoalitionName], CoalitionName } )
if self.FilterMeta.Coalitions[CoalitionName] and self.FilterMeta.Coalitions[CoalitionName] == ClientCoalitionName then
MClientCoalition = true
end
end
self:T( { "Evaluated Coalition", MClientCoalition } )
MClientInclude = MClientInclude and MClientCoalition
end
if self.Filter.Categories then
local MClientCategory = false
for CategoryID, CategoryName in pairs( self.Filter.Categories ) do
local ClientCategoryName = _DATABASE.Templates.ClientsByName[MClientName].CategoryName
self:T3( { "Category:", ClientCategoryName, self.FilterMeta.Categories[CategoryName], CategoryName } )
if self.FilterMeta.Categories[CategoryName] and self.FilterMeta.Categories[CategoryName] == ClientCategoryName then
MClientCategory = true
end
end
self:T( { "Evaluated Category", MClientCategory } )
MClientInclude = MClientInclude and MClientCategory
end
if self.Filter.Types then
local MClientType = false
for TypeID, TypeName in pairs( self.Filter.Types ) do
self:T3( { "Type:", MClient:GetTypeName(), TypeName } )
if TypeName == MClient:GetTypeName() then
MClientType = true
end
end
self:T( { "Evaluated Type", MClientType } )
MClientInclude = MClientInclude and MClientType
end
if self.Filter.Countries then
local MClientCountry = false
for CountryID, CountryName in pairs( self.Filter.Countries ) do
local ClientCountryName = _DATABASE.Templates.ClientsByName[MClientName].CountryName
self:T3( { "Country:", ClientCountryName, country.id[CountryName], CountryName } )
if country.id[CountryName] and country.id[ClientCountryName] and country.id[CountryName] == country.id[ClientCountryName] then
MClientCountry = true
end
end
self:T( { "Evaluated Country", MClientCountry } )
MClientInclude = MClientInclude and MClientCountry
end
if self.Filter.ClientPrefixes then
local MClientPrefix = false
for ClientPrefixId, ClientPrefix in pairs( self.Filter.ClientPrefixes ) do
self:T3( { "Prefix:", string.find( MClient.UnitName, ClientPrefix, 1 ), ClientPrefix } )
if string.find( MClient.UnitName, ClientPrefix, 1 ) then
MClientPrefix = true
end
end
self:T( { "Evaluated Prefix", MClientPrefix } )
MClientInclude = MClientInclude and MClientPrefix
end
MClientInclude = MClientInclude and MClientCoalition
end
if self.Filter.Categories then
local MClientCategory = false
for CategoryID, CategoryName in pairs( self.Filter.Categories ) do
self:T3( { "Category:", MClient:GetDesc().category, self.FilterMeta.Categories[CategoryName], CategoryName } )
if self.FilterMeta.Categories[CategoryName] and self.FilterMeta.Categories[CategoryName] == MClient:GetDesc().category then
MClientCategory = true
end
end
MClientInclude = MClientInclude and MClientCategory
end
if self.Filter.Types then
local MClientType = false
for TypeID, TypeName in pairs( self.Filter.Types ) do
self:T3( { "Type:", MClient:GetTypeName(), TypeName } )
if TypeName == MClient:GetTypeName() then
MClientType = true
end
end
MClientInclude = MClientInclude and MClientType
end
if self.Filter.Countries then
local MClientCountry = false
for CountryID, CountryName in pairs( self.Filter.Countries ) do
self:T3( { "Country:", MClient:GetCountry(), CountryName } )
if country.id[CountryName] == MClient:GetCountry() then
MClientCountry = true
end
end
MClientInclude = MClientInclude and MClientCountry
end
if self.Filter.ClientPrefixes then
local MClientPrefix = false
for ClientPrefixId, ClientPrefix in pairs( self.Filter.ClientPrefixes ) do
self:T3( { "Prefix:", string.find( MClient:GetName(), ClientPrefix, 1 ), ClientPrefix } )
if string.find( MClient:GetName(), ClientPrefix, 1 ) then
MClientPrefix = true
end
end
MClientInclude = MClientInclude and MClientPrefix
end
self:T2( MClientInclude )
return MClientInclude
end

View File

@@ -160,7 +160,8 @@ function UNIT:GetDCSUnit()
if DCSUnit then
return DCSUnit
end
self:E( "Unit " .. self.UnitName .. " not found!" )
return nil
end
@@ -470,9 +471,11 @@ function UNIT:GetDesc()
if DCSUnit then
local UnitDesc = DCSUnit:getDesc()
self:T2( UnitDesc )
return UnitDesc
end
self:E( "Unit " .. self.UnitName .. "not found!" )
return nil
end