diff --git a/Moose Development/Moose/AI/AI_Balancer.lua b/Moose Development/Moose/AI/AI_Balancer.lua index d4259d722..4d8c22909 100644 --- a/Moose Development/Moose/AI/AI_Balancer.lua +++ b/Moose Development/Moose/AI/AI_Balancer.lua @@ -74,30 +74,35 @@ --- AI_BALANCER class -- @type AI_BALANCER -- @field Core.Set#SET_CLIENT SetClient +-- @field Functional.Spawn#SPAWN SpawnAI +-- @field Wrapper.Group#GROUP Test -- @extends Core.Fsm#FSM_SET AI_BALANCER = { ClassName = "AI_BALANCER", PatrolZones = {}, AIGroups = {}, + Earliest = 5, -- Earliest a new AI can be spawned is in 5 seconds. + Latest = 60, -- Latest a new AI can be spawned is in 60 seconds. } + + --- Creates a new AI_BALANCER object -- @param #AI_BALANCER self -- @param Core.Set#SET_CLIENT 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 Functional.Spawn#SPAWN SpawnAI The default Spawn object to spawn new AI Groups when needed. -- @return #AI_BALANCER --- @usage --- -- Define a new AI_BALANCER Object. function AI_BALANCER:New( SetClient, SpawnAI ) -- Inherits from BASE - self = BASE:Inherit( self, FSM_SET:New( SET_GROUP:New() ) ) -- Core.Fsm#FSM_SET + local self = BASE:Inherit( self, FSM_SET:New( SET_GROUP:New() ) ) -- AI.AI_Balancer#AI_BALANCER self:SetStartState( "None" ) self:AddTransition( "*", "Start", "Monitoring" ) self:AddTransition( "*", "Monitor", "Monitoring" ) self:AddTransition( "*", "Spawn", "Spawning" ) self:AddTransition( "Spawning", "Spawned", "Spawned" ) + self:AddTransition( "*", "Destroyed", "Destroyed" ) self:AddTransition( "*", "Destroy", "Destroying" ) self:AddTransition( "*", "Return", "Returning" ) self:AddTransition( "*", "End", "End" ) @@ -105,6 +110,9 @@ function AI_BALANCER:New( SetClient, SpawnAI ) self.SetClient = SetClient self.SpawnAI = SpawnAI + + self.SpawnQueue = {} + self.ToNearestAirbase = false self.ToHomeAirbase = false @@ -113,6 +121,20 @@ function AI_BALANCER:New( SetClient, SpawnAI ) return self end +--- Sets the earliest to the latest interval in seconds how long AI_BALANCER will wait to spawn a new AI. +-- Provide 2 identical seconds if the interval should be a fixed amount of seconds. +-- @param #AI_BALANCER self +-- @param #number Earliest The earliest a new AI can be spawned in seconds. +-- @param #number Latest The latest a new AI can be spawned in seconds. +-- @return self +function AI_BALANCER:InitSpawnInterval( Earliest, Latest ) + + self.Earliest = Earliest + self.Latest = Latest + + return self +end + --- Returns the AI to the nearest friendly @{Wrapper.Airbase#AIRBASE}. -- @param #AI_BALANCER self -- @param Dcs.DCSTypes#Distance ReturnTresholdRange If there is an enemy @{Wrapper.Client#CLIENT} within the ReturnTresholdRange given in meters, the AI will not return to the nearest @{Wrapper.Airbase#AIRBASE}. @@ -140,15 +162,34 @@ end function AI_BALANCER:onenterSpawning( SetGroup, From, Event, To, ClientName ) -- OK, Spawn a new group from the default SpawnAI object provided. - local AIGroup = self.SpawnAI:Spawn() + local AIGroup = self.SpawnAI:Spawn() -- Wrapper.Group#GROUP AIGroup:E( "Spawning new AIGroup" ) --TODO: need to rework UnitName thing ... SetGroup:Add( ClientName, AIGroup ) + self.SpawnQueue[ClientName] = nil + + +-- --- @param Wrapper.Group#GROUP AIGroup +-- -- @param Core.Event#EVENTDATA EventData +-- local function Respawn( AIGroup, EventData ) +-- if EventData.IniUnit then +-- local CheckGroup = EventData.IniUnit:GetGroup() +-- if CheckGroup:GetName() == AIGroup:GetName() then +-- if CheckGroup:GetUnits() == nil then +-- AIGroup:Respawn( AIGroup:GetTemplate() ) +-- end +-- end +-- end +-- end +-- +-- +-- AIGroup:EventOnDead( Respawn ) +-- AIGroup:EventOnEjection( Respawn ) -- Fire the Spawned event. The first parameter is the AIGroup just Spawned. -- Mission designers can catch this event to bind further actions to the AIGroup. - self:Spawned( AIGroup ) + self:Spawned( AIGroup ) end --- @param #AI_BALANCER self @@ -159,6 +200,14 @@ function AI_BALANCER:onenterDestroying( SetGroup, From, Event, To, AIGroup ) AIGroup:Destroy() end +--- @param #AI_BALANCER self +-- @param Core.Set#SET_GROUP SetGroup +-- @param Wrapper.Group#GROUP AIGroup +function AI_BALANCER:onenterDestroyed( SetGroup, From, Event, To, AIGroup ) + + AIGroup:Destroy() +end + --- @param #AI_BALANCER self -- @param Core.Set#SET_GROUP SetGroup -- @param Wrapper.Group#GROUP AIGroup @@ -240,9 +289,13 @@ function AI_BALANCER:onenterMonitoring( SetGroup ) end else if not AIGroup or not AIGroup:IsAlive() == true then - self:E("client not alive") - self:Spawn( Client.UnitName ) - self:E("text after spawn") + self:E( "Client " .. Client.UnitName .. " not alive." ) + if not self.SpawnQueue[Client.UnitName] then + -- Spawn a new AI taking into account the spawn interval Earliest, Latest + self:__Spawn( math.random( self.Earliest, self.Latest ), Client.UnitName ) + self.SpawnQueue[Client.UnitName] = true + self:E( "New AI Spawned for Client " .. Client.UnitName ) + end end end return true diff --git a/Moose Development/Moose/Core/Fsm.lua b/Moose Development/Moose/Core/Fsm.lua index 25ac72ad7..6e6387b60 100644 --- a/Moose Development/Moose/Core/Fsm.lua +++ b/Moose Development/Moose/Core/Fsm.lua @@ -582,10 +582,10 @@ do -- FSM if execute then -- only execute the call if the From state is not equal to the To state! Otherwise this function should never execute! - if from ~= to then + --if from ~= to then self:_call_handler("onenter" .. to, params) self:_call_handler("OnEnter" .. to, params) - end + --end self:_call_handler("onafter" .. EventName, params) self:_call_handler("OnAfter" .. EventName, params) diff --git a/Moose Development/Moose/Core/ScheduleDispatcher.lua b/Moose Development/Moose/Core/ScheduleDispatcher.lua index d61e2cab7..18ac50a5a 100644 --- a/Moose Development/Moose/Core/ScheduleDispatcher.lua +++ b/Moose Development/Moose/Core/ScheduleDispatcher.lua @@ -75,7 +75,7 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr end self.Schedule = self.Schedule or setmetatable( {}, { __mode = "k" } ) - self.Schedule[Scheduler] = {} + self.Schedule[Scheduler] = self.Schedule[Scheduler] or {} self.Schedule[Scheduler][self.CallID] = {} self.Schedule[Scheduler][self.CallID].Function = ScheduleFunction self.Schedule[Scheduler][self.CallID].Arguments = ScheduleArguments diff --git a/Moose Development/Moose/Functional/Spawn.lua b/Moose Development/Moose/Functional/Spawn.lua index b5812fffa..843b12f0e 100644 --- a/Moose Development/Moose/Functional/Spawn.lua +++ b/Moose Development/Moose/Functional/Spawn.lua @@ -207,6 +207,7 @@ SPAWN = { SpawnAliasPrefix = nil, } + --- @type SPAWN.SpawnZoneTable -- @list SpawnZone diff --git a/Moose Development/Moose/Wrapper/Group.lua b/Moose Development/Moose/Wrapper/Group.lua index a5ba2121e..1115e8869 100644 --- a/Moose Development/Moose/Wrapper/Group.lua +++ b/Moose Development/Moose/Wrapper/Group.lua @@ -883,4 +883,3 @@ function GROUP:CalculateThreatLevelA2G() end - diff --git a/Moose Development/Moose/Wrapper/Object.lua b/Moose Development/Moose/Wrapper/Object.lua index 3ac441fbd..ed00a0cfe 100644 --- a/Moose Development/Moose/Wrapper/Object.lua +++ b/Moose Development/Moose/Wrapper/Object.lua @@ -34,7 +34,6 @@ OBJECT = { ObjectName = "", } - --- A DCSObject -- @type DCSObject -- @field id_ The ID of the controllable in DCS @@ -43,10 +42,11 @@ OBJECT = { -- @param #OBJECT self -- @param Dcs.DCSWrapper.Object#Object ObjectName The Object name -- @return #OBJECT self -function OBJECT:New( ObjectName ) +function OBJECT:New( ObjectName, Test ) local self = BASE:Inherit( self, BASE:New() ) self:F2( ObjectName ) self.ObjectName = ObjectName + return self end diff --git a/Moose Test Missions/AIB - AI Balancing/AIB-001 - Spawned AI/AIB-001 -Spawned AI.miz b/Moose Test Missions/AIB - AI Balancing/AIB-001 - Spawned AI/AIB-001 - Spawned AI.miz similarity index 100% rename from Moose Test Missions/AIB - AI Balancing/AIB-001 - Spawned AI/AIB-001 -Spawned AI.miz rename to Moose Test Missions/AIB - AI Balancing/AIB-001 - Spawned AI/AIB-001 - Spawned AI.miz diff --git a/Moose Test Missions/AIB - AI Balancing/AIB-004 - Respawn Test when Destroyed/AIB-004 - Respawn Test when Destroyed.lua b/Moose Test Missions/AIB - AI Balancing/AIB-004 - Respawn Test when Destroyed/AIB-004 - Respawn Test when Destroyed.lua new file mode 100644 index 000000000..a13f9ce66 --- /dev/null +++ b/Moose Test Missions/AIB - AI Balancing/AIB-004 - Respawn Test when Destroyed/AIB-004 - Respawn Test when Destroyed.lua @@ -0,0 +1,46 @@ +-- Name: Respawn Test when Destroyed +-- Author: FlightControl +-- Date Created: 7 January 2017 +-- +-- # Situation: +-- +-- For the red coalition, 2 client slots are foreseen. +-- For those players that have not joined the mission, red AI is spawned. +-- The red AI should start patrolling an area. +-- +-- The blue side has SAMs nearby. +-- Once the red AI takes off, the red AI is attacked by the blue SAMs. +-- Red AI should be killed and once that happens, a Respawn of the group should happen! +-- The Respawn happens through the InitCleanUp() API of SPAWN. +-- +-- # Test cases: +-- +-- 1. If no player is logging into the red slots, 2 red AI planes should be alive. +-- 2. If a player joins one red slot, one red AI plane should return to the nearest home base. +-- 3. If two players join the red slots, no AI plane should be spawned, and all airborne AI planes should return to the nearest home base. +-- 4. Monitor that once a red AI is destroyed, that it ReSpawns... +-- + +-- Define the SET of CLIENTs from the red coalition. This SET is filled during startup. +local RU_PlanesClientSet = SET_CLIENT:New():FilterCountries( "RUSSIA" ):FilterCategories( "plane" ):FilterStart() + +-- Define the SPAWN object for the red AI plane template. +-- We use InitCleanUp to check every 20 seconds, if there are no planes blocked at the airbase, waithing for take-off. +-- If a blocked plane exists, this red plane will be ReSpawned. +local RU_PlanesSpawn = SPAWN:New( "AI RU" ):InitCleanUp( 20 ) + +-- Start the AI_BALANCER, using the SET of red CLIENTs, and the SPAWN object as a parameter. +local RU_AI_Balancer = AI_BALANCER:New( RU_PlanesClientSet, RU_PlanesSpawn ) + +function RU_AI_Balancer:OnAfterSpawned( SetGroup, From, Event, To, AIGroup ) + + local PatrolZoneGroup = GROUP:FindByName( "PatrolZone" ) + local PatrolZone = ZONE_POLYGON:New( "PatrolZone", PatrolZoneGroup ) + + + local Patrol = AI_PATROLZONE:New( PatrolZone, 3000, 6000, 400, 600 ) + Patrol:ManageFuel( 0.2, 60 ) + Patrol:SetControllable( AIGroup ) + Patrol:__Start( 5 ) + +end diff --git a/Moose Test Missions/AIB - AI Balancing/AIB-004 - Respawn Test when Destroyed/AIB-004 - Respawn Test when Destroyed.miz b/Moose Test Missions/AIB - AI Balancing/AIB-004 - Respawn Test when Destroyed/AIB-004 - Respawn Test when Destroyed.miz new file mode 100644 index 000000000..d6792748a Binary files /dev/null and b/Moose Test Missions/AIB - AI Balancing/AIB-004 - Respawn Test when Destroyed/AIB-004 - Respawn Test when Destroyed.miz differ