From 9ec29f607fed2f5e00d3a84a733335892558de96 Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Tue, 1 Aug 2017 07:19:29 +0200 Subject: [PATCH 1/7] Fixed an issue with the overhead parameter Now the GCI will spawn the actual overhead needed, independent of the grouping parameter. For example, when grouping was 1 and overhead 1.5, only one plane was spawned. Now two planes are spawned of 2 groups of 1 unit. --- .../Moose/AI/AI_A2A_Dispatcher.lua | 108 ++++++++++-------- 1 file changed, 58 insertions(+), 50 deletions(-) diff --git a/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua b/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua index 05a641400..72a3b3542 100644 --- a/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua @@ -772,6 +772,8 @@ do -- AI_A2A_DISPATCHER self:SetEngageRadius() self:SetGciRadius() + self:SetIntercept( 300 ) -- A default intercept delay time of 300 seconds. + self:SetDisengageRadius( 100000 ) -- The default disengage radius is 100 km. self:SetDefaultTakeoff( AI_A2A_DISPATCHER.Takeoff.Air ) self:SetDefaultLanding( AI_A2A_DISPATCHER.Landing.NearAirbase ) @@ -781,8 +783,6 @@ do -- AI_A2A_DISPATCHER self:SetDefaultDamageThreshold( 0.4 ) -- When 40% of damage, go RTB. self:SetDefaultCapTimeInterval( 180, 600 ) -- Between 180 and 600 seconds. self:SetDefaultCapLimit( 1 ) -- Maximum one CAP per squadron. - self:SetIntercept( 300 ) -- A default intercept delay time of 300 seconds. - self:SetDisengageRadius( 100000 ) -- The default disengage radius is 100 km. self:AddTransition( "Started", "Assign", "Started" ) @@ -2445,58 +2445,66 @@ do -- AI_A2A_DISPATCHER local DefenderOverhead = DefenderSquadron.Overhead or self.DefenderDefault.Overhead local DefenderGrouping = DefenderSquadron.Grouping or self.DefenderDefault.Grouping local DefendersNeeded = math.ceil( DefendersCount * DefenderOverhead ) + + self:F( { DefaultOverhead = self.DefenderDefault.Overhead, Overhead = DefenderOverhead } ) + self:F( { DefaultGrouping = self.DefenderDefault.Grouping, Grouping = DefenderGrouping } ) + self:F( { DefendersCount = DefendersCount, DefendersNeeded = DefendersNeeded } ) + + while ( DefendersNeeded > 0 ) do - local Spawn = DefenderSquadron.Spawn[ math.random( 1, #DefenderSquadron.Spawn ) ] - if DefenderGrouping then - Spawn:InitGrouping( ( DefenderGrouping < DefendersNeeded ) and DefenderGrouping or DefendersNeeded ) - else - Spawn:InitGrouping() - end - - local TakeoffMethod = self:GetSquadronTakeoff( ClosestDefenderSquadronName ) - local DefenderGCI = Spawn:SpawnAtAirbase( DefenderSquadron.Airbase, TakeoffMethod ) - self:F( { GCIDefender = DefenderGCI:GetName() } ) - - self:AddDefenderToSquadron( DefenderSquadron, DefenderGCI ) - - - if DefenderGCI then - - DefendersCount = DefendersCount - DefenderGCI:GetSize() - - local Fsm = AI_A2A_GCI:New( DefenderGCI, Gci.EngageMinSpeed, Gci.EngageMaxSpeed ) - Fsm:SetDispatcher( self ) - Fsm:SetHomeAirbase( DefenderSquadron.Airbase ) - Fsm:SetFuelThreshold( self.DefenderDefault.FuelThreshold, 60 ) - Fsm:SetDamageThreshold( self.DefenderDefault.DamageThreshold ) - Fsm:SetDisengageRadius( self.DisengageRadius ) - Fsm:Start() - Fsm:__Engage( 2, DetectedItem.Set ) -- Engage on the TargetSetUnit - - - self:SetDefenderTask( DefenderGCI, "GCI", Fsm, DetectedItem ) - - - function Fsm:onafterRTB( Defender, From, Event, To ) - self:F({"GCI RTB"}) - self:GetParent(self).onafterRTB( self, Defender, From, Event, To ) - - local Dispatcher = self:GetDispatcher() -- #AI_A2A_DISPATCHER - local AIGroup = self:GetControllable() - Dispatcher:ClearDefenderTaskTarget( AIGroup ) + local Spawn = DefenderSquadron.Spawn[ math.random( 1, #DefenderSquadron.Spawn ) ] -- Functional.Spawn#SPAWN + if DefenderGrouping then + Spawn:InitGrouping( ( DefenderGrouping < DefendersNeeded ) and DefenderGrouping or DefendersNeeded ) + else + Spawn:InitGrouping() end - --- @param #AI_A2A_DISPATCHER self - function Fsm:onafterHome( Defender, From, Event, To ) - self:F({"GCI Home"}) - self:GetParent(self).onafterHome( self, Defender, From, Event, To ) + local TakeoffMethod = self:GetSquadronTakeoff( ClosestDefenderSquadronName ) + local DefenderGCI = Spawn:SpawnAtAirbase( DefenderSquadron.Airbase, TakeoffMethod ) + self:F( { GCIDefender = DefenderGCI:GetName() } ) + + DefendersNeeded = DefendersNeeded - DefenderGCI:GetSize() + + self:AddDefenderToSquadron( DefenderSquadron, DefenderGCI ) + + if DefenderGCI then + + DefendersCount = DefendersCount - DefenderGCI:GetSize() - local Dispatcher = self:GetDispatcher() -- #AI_A2A_DISPATCHER - local AIGroup = self:GetControllable() - local Squadron = Dispatcher:GetSquadronFromDefender( AIGroup ) - if Dispatcher:GetSquadronLanding( Squadron.Name ) == AI_A2A_DISPATCHER.Landing.NearAirbase then - Dispatcher:RemoveDefenderFromSquadron( Squadron, AIGroup ) - AIGroup:Destroy() + local Fsm = AI_A2A_GCI:New( DefenderGCI, Gci.EngageMinSpeed, Gci.EngageMaxSpeed ) + Fsm:SetDispatcher( self ) + Fsm:SetHomeAirbase( DefenderSquadron.Airbase ) + Fsm:SetFuelThreshold( self.DefenderDefault.FuelThreshold, 60 ) + Fsm:SetDamageThreshold( self.DefenderDefault.DamageThreshold ) + Fsm:SetDisengageRadius( self.DisengageRadius ) + Fsm:Start() + Fsm:__Engage( 2, DetectedItem.Set ) -- Engage on the TargetSetUnit + + + self:SetDefenderTask( DefenderGCI, "GCI", Fsm, DetectedItem ) + + + function Fsm:onafterRTB( Defender, From, Event, To ) + self:F({"GCI RTB"}) + self:GetParent(self).onafterRTB( self, Defender, From, Event, To ) + + local Dispatcher = self:GetDispatcher() -- #AI_A2A_DISPATCHER + local AIGroup = self:GetControllable() + Dispatcher:ClearDefenderTaskTarget( AIGroup ) + end + + --- @param #AI_A2A_DISPATCHER self + function Fsm:onafterHome( Defender, From, Event, To ) + self:F({"GCI Home"}) + self:GetParent(self).onafterHome( self, Defender, From, Event, To ) + + local Dispatcher = self:GetDispatcher() -- #AI_A2A_DISPATCHER + local AIGroup = self:GetControllable() + local Squadron = Dispatcher:GetSquadronFromDefender( AIGroup ) + if Dispatcher:GetSquadronLanding( Squadron.Name ) == AI_A2A_DISPATCHER.Landing.NearAirbase then + Dispatcher:RemoveDefenderFromSquadron( Squadron, AIGroup ) + AIGroup:Destroy() + end end end end From 9054a493f9f1dd98df7c975ecb36bb1e572ff8a7 Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Tue, 1 Aug 2017 17:35:53 +0200 Subject: [PATCH 2/7] Updated defects in dispatcher --- Moose Development/Moose/AI/AI_A2A.lua | 97 +++++++++++++++---- Moose Development/Moose/AI/AI_A2A_Cap.lua | 17 +++- .../Moose/AI/AI_A2A_Dispatcher.lua | 64 +++++++++--- Moose Development/Moose/AI/AI_A2A_Gci.lua | 21 ++-- .../Moose/Core/ScheduleDispatcher.lua | 2 +- .../Moose/Wrapper/Controllable.lua | 2 +- 6 files changed, 160 insertions(+), 43 deletions(-) diff --git a/Moose Development/Moose/AI/AI_A2A.lua b/Moose Development/Moose/AI/AI_A2A.lua index 8241c5f19..02755c943 100644 --- a/Moose Development/Moose/AI/AI_A2A.lua +++ b/Moose Development/Moose/AI/AI_A2A.lua @@ -222,6 +222,7 @@ function AI_A2A:New( AIGroup ) self:AddTransition( "*", "Return", "Returning" ) + self:AddTransition( "*", "Hold", "Holding" ) self:AddTransition( "*", "Home", "Home" ) self:AddTransition( "*", "LostControl", "LostControl" ) self:AddTransition( "*", "Fuel", "Fuel" ) @@ -382,21 +383,34 @@ end --- @param #AI_A2A self function AI_A2A:onafterStatus() - self:F() + + self:F( " Checking Status" ) if self.Controllable and self.Controllable:IsAlive() then local RTB = false 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:Home() - RTB = true + 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 + if self:Is( "Damaged" ) or self:Is( "LostControl" ) then + if DistanceFromHomeBase < 5000 then + self:E( self.Controllable:GetName() .. " is too far from home base, RTB!" ) + self:Home( "Destroy" ) + end + end + + local Fuel = self.Controllable:GetUnit(1):GetFuel() self:F({Fuel=Fuel}) @@ -427,10 +441,15 @@ function AI_A2A:onafterStatus() -- Check if planes went RTB and are out of control. if self.Controllable:HasTask() == false then if not self:Is( "Started" ) and - not self:Is( "Stopped" ) then + not self:Is( "Stopped" ) and + not self:Is( "Home" ) then if self.IdleCount >= 2 then - self:E( self.Controllable:GetName() .. " control lost! " ) - self:LostControl() + if Damage ~= InitialLife then + self:Damaged() + else + self:E( self.Controllable:GetName() .. " control lost! " ) + self:LostControl() + end else self.IdleCount = self.IdleCount + 1 end @@ -438,7 +457,7 @@ function AI_A2A:onafterStatus() else self.IdleCount = 0 end - + if RTB == true then self:__RTB( 0.5 ) end @@ -451,11 +470,30 @@ end --- @param Wrapper.Group#GROUP AIGroup function AI_A2A.RTBRoute( AIGroup ) - AIGroup:E( { "RTBRoute:", AIGroup:GetName() } ) - local _AI_A2A = AIGroup:GetState( AIGroup, "AI_A2A" ) -- #AI_A2A - _AI_A2A:__RTB( 0.5 ) + AIGroup:E( { "AI_A2A.RTBRoute:", AIGroup:GetName() } ) + + if AIGroup:IsAlive() then + local _AI_A2A = AIGroup:GetState( AIGroup, "AI_A2A" ) -- #AI_A2A + _AI_A2A:__RTB( 0.5 ) + local Task = AIGroup:TaskOrbitCircle( 4000, 400 ) + AIGroup:SetTask( Task ) + end + end +--- @param Wrapper.Group#GROUP AIGroup +function AI_A2A.RTBHold( AIGroup ) + + AIGroup:E( { "AI_A2A.RTBHold:", AIGroup:GetName() } ) + if AIGroup:IsAlive() then + local _AI_A2A = AIGroup:GetState( AIGroup, "AI_A2A" ) -- #AI_A2A + _AI_A2A:__RTB( 0.5 ) + _AI_A2A:Return() + local Task = AIGroup:TaskOrbitCircle( 4000, 400 ) + AIGroup:SetTask( Task ) + end + +end --- @param #AI_A2A self @@ -468,8 +506,6 @@ function AI_A2A:onafterRTB( AIGroup, From, Event, To ) self:E( "Group " .. AIGroup:GetName() .. " ... RTB! ( " .. self:GetState() .. " )" ) - self.CheckStatus = false - self:ClearTargetDistance() AIGroup:ClearTasks() @@ -486,6 +522,7 @@ function AI_A2A:onafterRTB( AIGroup, From, Event, To ) local ToAirbaseCoord = CurrentCoord:Translate( 5000, ToAirbaseAngle ) if Distance < 5000 then + self:E( "RTB and near the airbase!" ) self:Home() return end @@ -501,6 +538,7 @@ function AI_A2A:onafterRTB( AIGroup, From, Event, To ) self:F( { Angle = ToAirbaseAngle, ToTargetSpeed = ToTargetSpeed } ) self:T2( { self.MinSpeed, self.MaxSpeed, ToTargetSpeed } ) + EngageRoute[#EngageRoute+1] = ToPatrolRoutePoint EngageRoute[#EngageRoute+1] = ToPatrolRoutePoint AIGroup:OptionROEHoldFire() @@ -511,12 +549,13 @@ function AI_A2A:onafterRTB( AIGroup, From, Event, To ) local Tasks = {} Tasks[#Tasks+1] = AIGroup:TaskFunction( 1, 1, "AI_A2A.RTBRoute" ) - EngageRoute[1].task = AIGroup:TaskCombo( Tasks ) + Tasks[#Tasks+1] = AIGroup:TaskOrbitCircle( 4000, 350 ) + EngageRoute[#EngageRoute].task = AIGroup:TaskCombo( Tasks ) AIGroup:SetState( AIGroup, "AI_A2A", self ) --- NOW ROUTE THE GROUP! - AIGroup:WayPointExecute( 1, 0 ) + AIGroup:SetTask( AIGroup:TaskRoute( EngageRoute ), 1 ) end @@ -533,6 +572,30 @@ function AI_A2A:onafterHome( AIGroup, From, Event, To ) 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( 1, 1, "AI_A2A.RTBHold" ) + + local OrbitHoldTask = AIGroup:TaskOrbitCircle( 4000, self.PatrolMinSpeed ) + + AIGroup:SetState( AIGroup, "AI_A2A", self ) + + AIGroup:SetTask( AIGroup:TaskCombo( { TimedOrbitTask, RTBTask, OrbitHoldTask } ), 0 ) + end + +end + + diff --git a/Moose Development/Moose/AI/AI_A2A_Cap.lua b/Moose Development/Moose/AI/AI_A2A_Cap.lua index 88e7c9932..1b365bc17 100644 --- a/Moose Development/Moose/AI/AI_A2A_Cap.lua +++ b/Moose Development/Moose/AI/AI_A2A_Cap.lua @@ -350,8 +350,15 @@ end --- @param Wrapper.Group#GROUP AIGroup function AI_A2A_CAP.AttackRoute( AIGroup ) - local EngageZone = AIGroup:GetState( AIGroup, "AI_A2A_CAP" ) -- AI.AI_Cap#AI_A2A_CAP - EngageZone:__Engage( 0.5 ) + AIGroup:E( { "AI_A2A_CAP.AttackRoute:", AIGroup:GetName() } ) + + if AIGroup:IsAlive() then + local _AI_A2A_CAP = AIGroup:GetState( AIGroup, "AI_A2A_CAP" ) -- AI.AI_Cap#AI_A2A_CAP + _AI_A2A_CAP:__Engage( 0.5 ) + + local Task = AIGroup:TaskOrbitCircle( 4000, 400 ) + AIGroup:SetTask( Task ) + end end --- @param #AI_A2A_CAP self @@ -438,17 +445,17 @@ function AI_A2A_CAP:onafterEngage( AIGroup, From, Event, To, AttackSetUnit ) AIGroup:OptionROEOpenFire() AIGroup:OptionROTPassiveDefense() - AttackTasks[#AttackTasks+1] = AIGroup:TaskFunction( 1, #AttackTasks, "AI_A2A_CAP.AttackRoute" ) + AttackTasks[#AttackTasks+1] = AIGroup:TaskFunction( 1, 1, "AI_A2A_CAP.AttackRoute" ) AttackTasks[#AttackTasks+1] = AIGroup:TaskOrbitCircle( 4000, self.PatrolMinSpeed ) - EngageRoute[#EngageRoute].task = AIGroup:TaskCombo( AttackTasks ) + EngageRoute[1].task = AIGroup:TaskCombo( AttackTasks ) --- Do a trick, link the NewEngageRoute function of the object to the AIControllable in a temporary variable ... AIGroup:SetState( AIGroup, "AI_A2A_CAP", self ) end --- NOW ROUTE THE GROUP! - AIGroup:WayPointExecute( 1, 0 ) + AIGroup:SetTask( AIGroup:TaskRoute( EngageRoute ), 1 ) end else self:E("No targets found -> Going back to Patrolling") diff --git a/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua b/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua index 72a3b3542..82ac7edfd 100644 --- a/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua @@ -460,6 +460,8 @@ do -- AI_A2A_DISPATCHER -- For example with a group setting of 2, if 3 targets are detected and cannot be engaged by CAP or any airborne flight, -- a GCI needs to be started, the GCI flights will be grouped as follows: Group 1 of 2 flights and Group 2 of one flight! -- + -- Even more ... If one target has been detected, and the overhead is 1.5, grouping is 1, then two groups of planes will be spawned, with one unit each! + -- -- The **grouping value is set for a Squadron**, and can be **dynamically adjusted** during mission execution, so to adjust the defense flights grouping when the tactical situation changes. -- -- ### 6.4. Overhead and Balance the effectiveness of the air defenses in case of GCI @@ -484,6 +486,8 @@ do -- AI_A2A_DISPATCHER -- The amount of defending units is calculated by multiplying the amount of detected attacking planes as part of the detected group -- multiplied by the Overhead and rounded up to the smallest integer. -- + -- For example ... If one target has been detected, and the overhead is 1.5, grouping is 1, then two groups of planes will be spawned, with one unit each! + -- -- The **overhead value is set for a Squadron**, and can be **dynamically adjusted** during mission execution, so to adjust the defense overhead when the tactical situation changes. -- -- ## 7. Setup a squadron for CAP @@ -2163,11 +2167,11 @@ do -- AI_A2A_DISPATCHER --- @param #AI_A2A_DISPATCHER self - function AI_A2A_DISPATCHER:AddDefenderToSquadron( Squadron, Defender ) + function AI_A2A_DISPATCHER:AddDefenderToSquadron( Squadron, Defender, Size ) self.Defenders = self.Defenders or {} local DefenderName = Defender:GetName() self.Defenders[ DefenderName ] = Squadron - Squadron.Resources = Squadron.Resources - Defender:GetSize() + Squadron.Resources = Squadron.Resources - Size self:F( { DefenderName = DefenderName, SquadronResources = Squadron.Resources } ) end @@ -2324,7 +2328,7 @@ do -- AI_A2A_DISPATCHER local TakeoffMethod = self:GetSquadronTakeoff( SquadronName ) local DefenderCAP = Spawn:SpawnAtAirbase( DefenderSquadron.Airbase, TakeoffMethod ) - self:AddDefenderToSquadron( DefenderSquadron, DefenderCAP ) + self:AddDefenderToSquadron( DefenderSquadron, DefenderCAP, DefenderGrouping ) if DefenderCAP then @@ -2367,13 +2371,19 @@ do -- AI_A2A_DISPATCHER end --- @param #AI_A2A_DISPATCHER self - function Fsm:onafterHome( Defender, From, Event, To ) + function Fsm:onafterHome( Defender, From, Event, To, Action ) self:F({"CAP Home"}) self:GetParent(self).onafterHome( self, Defender, From, Event, To ) local Dispatcher = self:GetDispatcher() -- #AI_A2A_DISPATCHER local AIGroup = self:GetControllable() local Squadron = Dispatcher:GetSquadronFromDefender( AIGroup ) + + if Action and Action == "Destroy" then + Dispatcher:RemoveDefenderFromSquadron( Squadron, AIGroup ) + AIGroup:Destroy() + end + if Dispatcher:GetSquadronLanding( Squadron.Name ) == AI_A2A_DISPATCHER.Landing.NearAirbase then Dispatcher:RemoveDefenderFromSquadron( Squadron, AIGroup ) AIGroup:Destroy() @@ -2412,7 +2422,7 @@ do -- AI_A2A_DISPATCHER for SquadronName, DefenderSquadron in pairs( self.DefenderSquadrons or {} ) do for InterceptID, Intercept in pairs( DefenderSquadron.Gci or {} ) do - self:E( { DefenderSquadron } ) + --self:E( { DefenderSquadron } ) local SpawnCoord = DefenderSquadron.Airbase:GetCoordinate() -- Core.Point#COORDINATE --local TargetCoord = AttackerSet:GetFirst():GetCoordinate() local TargetCoord = DetectedItem.InterceptCoord @@ -2453,23 +2463,24 @@ do -- AI_A2A_DISPATCHER while ( DefendersNeeded > 0 ) do local Spawn = DefenderSquadron.Spawn[ math.random( 1, #DefenderSquadron.Spawn ) ] -- Functional.Spawn#SPAWN + local DefenderGrouping = ( DefenderGrouping < DefendersNeeded ) and DefenderGrouping or DefendersNeeded if DefenderGrouping then - Spawn:InitGrouping( ( DefenderGrouping < DefendersNeeded ) and DefenderGrouping or DefendersNeeded ) + Spawn:InitGrouping( DefenderGrouping ) else Spawn:InitGrouping() end local TakeoffMethod = self:GetSquadronTakeoff( ClosestDefenderSquadronName ) - local DefenderGCI = Spawn:SpawnAtAirbase( DefenderSquadron.Airbase, TakeoffMethod ) + local DefenderGCI = Spawn:SpawnAtAirbase( DefenderSquadron.Airbase, TakeoffMethod ) -- Wrapper.Group#GROUP self:F( { GCIDefender = DefenderGCI:GetName() } ) - DefendersNeeded = DefendersNeeded - DefenderGCI:GetSize() + DefendersNeeded = DefendersNeeded - DefenderGrouping - self:AddDefenderToSquadron( DefenderSquadron, DefenderGCI ) + self:AddDefenderToSquadron( DefenderSquadron, DefenderGCI, DefenderGrouping ) if DefenderGCI then - DefendersCount = DefendersCount - DefenderGCI:GetSize() + DefendersCount = DefendersCount - DefenderGrouping local Fsm = AI_A2A_GCI:New( DefenderGCI, Gci.EngageMinSpeed, Gci.EngageMaxSpeed ) Fsm:SetDispatcher( self ) @@ -2492,15 +2503,35 @@ do -- AI_A2A_DISPATCHER local AIGroup = self:GetControllable() Dispatcher:ClearDefenderTaskTarget( AIGroup ) end + + --- @param #AI_A2A_DISPATCHER self + function Fsm:onafterLostControl( Defender, From, Event, To ) + self:F({"GCI Home"}) + self:GetParent(self).onafterHome( self, Defender, From, Event, To ) + + local Dispatcher = self:GetDispatcher() -- #AI_A2A_DISPATCHER + local AIGroup = self:GetControllable() -- Wrapper.Group#GROUP + local Squadron = Dispatcher:GetSquadronFromDefender( AIGroup ) + if AIGroup:IsAboveRunway() then + Dispatcher:RemoveDefenderFromSquadron( Squadron, AIGroup ) + AIGroup:Destroy() + end + end --- @param #AI_A2A_DISPATCHER self - function Fsm:onafterHome( Defender, From, Event, To ) + function Fsm:onafterHome( Defender, From, Event, To, Action ) self:F({"GCI Home"}) self:GetParent(self).onafterHome( self, Defender, From, Event, To ) local Dispatcher = self:GetDispatcher() -- #AI_A2A_DISPATCHER local AIGroup = self:GetControllable() local Squadron = Dispatcher:GetSquadronFromDefender( AIGroup ) + + if Action and Action == "Destroy" then + Dispatcher:RemoveDefenderFromSquadron( Squadron, AIGroup ) + AIGroup:Destroy() + end + if Dispatcher:GetSquadronLanding( Squadron.Name ) == AI_A2A_DISPATCHER.Landing.NearAirbase then Dispatcher:RemoveDefenderFromSquadron( Squadron, AIGroup ) AIGroup:Destroy() @@ -2662,7 +2693,16 @@ do -- AI_A2A_DISPATCHER local Defender = Defender -- Wrapper.Group#GROUP if not DefenderTask.Target then local DefenderHasTask = Defender:HasTask() - Report:Add( string.format( " - %s ( %s - %s ): ( #%d ) %s", Defender:GetName(), DefenderTask.Type, DefenderTask.Fsm:GetState(), Defender:GetSize(), Defender:HasTask() == true and "Executing" or "Idle" ) ) + local Fuel = Defender:GetUnit(1):GetFuel() * 100 + local Damage = Defender:GetLife() / Defender:GetLife0() * 100 + Report:Add( string.format( " - %s ( %s - %s ): ( #%d ) F: %3d, D:%3d - %s", + Defender:GetName(), + DefenderTask.Type, + DefenderTask.Fsm:GetState(), + Defender:GetSize(), + Fuel, + Damage, + Defender:HasTask() == true and "Executing" or "Idle" ) ) end end Report:Add( string.format( "\n - %d Tasks", TaskCount ) ) diff --git a/Moose Development/Moose/AI/AI_A2A_Gci.lua b/Moose Development/Moose/AI/AI_A2A_Gci.lua index 24c3446d1..2986be4fd 100644 --- a/Moose Development/Moose/AI/AI_A2A_Gci.lua +++ b/Moose Development/Moose/AI/AI_A2A_Gci.lua @@ -311,11 +311,17 @@ end -- todo: need to fix this global function --- @param Wrapper.Group#GROUP AIControllable -function AI_A2A_GCI.InterceptRoute( AIControllable ) +function AI_A2A_GCI.InterceptRoute( AIGroup ) - local EngageZone = AIControllable:GetState( AIControllable, "EngageZone" ) -- AI.AI_Cap#AI_A2A_GCI - EngageZone:E( "NewEngageRoute" ) - EngageZone:__Engage( 0.5 ) + AIGroup:E( { "AI_A2A_GCI.InterceptRoute:", AIGroup:GetName() } ) + + if AIGroup:IsAlive() then + local _AI_A2A_GCI = AIGroup:GetState( AIGroup, "AI_A2A_GCI" ) -- AI.AI_Cap#AI_A2A_GCI + _AI_A2A_GCI:__Engage( 0.5 ) + + local Task = AIGroup:TaskOrbitCircle( 4000, 400 ) + AIGroup:SetTask( Task ) + end end --- @param #AI_A2A_GCI self @@ -411,16 +417,17 @@ function AI_A2A_GCI:onafterEngage( AIGroup, From, Event, To, AttackSetUnit ) AIGroup:OptionROEOpenFire() AIGroup:OptionROTPassiveDefense() - AttackTasks[#AttackTasks+1] = AIGroup:TaskFunction( 1, #AttackTasks, "AI_A2A_GCI.InterceptRoute" ) + AttackTasks[#AttackTasks+1] = AIGroup:TaskFunction( 1, 1, "AI_A2A_GCI.InterceptRoute" ) AttackTasks[#AttackTasks+1] = AIGroup:TaskOrbitCircle( 4000, self.EngageMinSpeed ) EngageRoute[#EngageRoute].task = AIGroup:TaskCombo( AttackTasks ) --- Do a trick, link the NewEngageRoute function of the object to the AIControllable in a temporary variable ... - AIGroup:SetState( AIGroup, "EngageZone", self ) + AIGroup:SetState( AIGroup, "AI_A2A_GCI", self ) end --- NOW ROUTE THE GROUP! - AIGroup:WayPointExecute( 1, 0 ) + --AIGroup:ClearTasks() + AIGroup:SetTask( AIGroup:TaskRoute( EngageRoute ), 1 ) end else diff --git a/Moose Development/Moose/Core/ScheduleDispatcher.lua b/Moose Development/Moose/Core/ScheduleDispatcher.lua index 51358dee4..7ba478111 100644 --- a/Moose Development/Moose/Core/ScheduleDispatcher.lua +++ b/Moose Development/Moose/Core/ScheduleDispatcher.lua @@ -62,7 +62,7 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr -- Initialize the ObjectSchedulers array, which is a weakly coupled table. -- If the object used as the key is nil, then the garbage collector will remove the item from the Functions array. - self.ObjectSchedulers = self.ObjectSchedulers or setmetatable( {}, { __mode = "v" } ) + self.ObjectSchedulers = self.ObjectSchedulers or {} -- setmetatable( {}, { __mode = "v" } ) if Scheduler.MasterObject then self.ObjectSchedulers[self.CallID] = Scheduler diff --git a/Moose Development/Moose/Wrapper/Controllable.lua b/Moose Development/Moose/Wrapper/Controllable.lua index 33981f5d6..3baff32e2 100644 --- a/Moose Development/Moose/Wrapper/Controllable.lua +++ b/Moose Development/Moose/Wrapper/Controllable.lua @@ -340,7 +340,7 @@ function CONTROLLABLE:SetTask( DCSTask, WaitTime ) -- Therefore we schedule the functions to set the mission and options for the Controllable. -- Controller.setTask( Controller, DCSTask ) - if not WaitTime then + if not WaitTime or WaitTime == 0 then Controller:setTask( DCSTask ) else self.TaskScheduler:Schedule( Controller, Controller.setTask, { DCSTask }, WaitTime ) From d8d06a18ce06bc94dc647975d6f1ea9f82480214 Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Wed, 2 Aug 2017 09:43:08 +0200 Subject: [PATCH 3/7] Updates and bug fixes --- Moose Development/Moose/AI/AI_A2A_Dispatcher.lua | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua b/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua index 82ac7edfd..e164e3cd1 100644 --- a/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua @@ -2679,7 +2679,16 @@ do -- AI_A2A_DISPATCHER for Defender, DefenderTask in pairs( self:GetDefenderTasks() ) do local Defender = Defender -- Wrapper.Group#GROUP if DefenderTask.Target and DefenderTask.Target.Index == DetectedItem.Index then - Report:Add( string.format( " - %s ( %s - %s ): ( #%d ) %s", Defender:GetName(), DefenderTask.Type, DefenderTask.Fsm:GetState(), Defender:GetSize(), Defender:HasTask() == true and "Executing" or "Idle" ) ) + local Fuel = Defender:GetUnit(1):GetFuel() * 100 + local Damage = Defender:GetLife() / Defender:GetLife0() * 100 + Report:Add( string.format( " - %s ( %s - %s ): ( #%d ) F: %3d, D:%3d - %s", + Defender:GetName(), + DefenderTask.Type, + DefenderTask.Fsm:GetState(), + Defender:GetSize(), + Fuel, + Damage, + Defender:HasTask() == true and "Executing" or "Idle" ) ) end end end From 4b60f776ce434757bfc0ca08809dec8759d55891 Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Wed, 2 Aug 2017 12:41:35 +0200 Subject: [PATCH 4/7] Apply randomization at start for schedules. Apply randomization at start for schedules. --- Moose Development/Moose/AI/AI_A2A_Cap.lua | 10 +++------- .../Moose/AI/AI_A2A_Dispatcher.lua | 10 +++++++++- Moose Development/Moose/AI/AI_A2A_Gci.lua | 6 +++--- Moose Development/Moose/AI/AI_A2A_Patrol.lua | 20 ++++++++++++------- .../Moose/Core/ScheduleDispatcher.lua | 6 +++--- 5 files changed, 31 insertions(+), 21 deletions(-) diff --git a/Moose Development/Moose/AI/AI_A2A_Cap.lua b/Moose Development/Moose/AI/AI_A2A_Cap.lua index 1b365bc17..bd38bd29a 100644 --- a/Moose Development/Moose/AI/AI_A2A_Cap.lua +++ b/Moose Development/Moose/AI/AI_A2A_Cap.lua @@ -356,8 +356,8 @@ function AI_A2A_CAP.AttackRoute( AIGroup ) local _AI_A2A_CAP = AIGroup:GetState( AIGroup, "AI_A2A_CAP" ) -- AI.AI_Cap#AI_A2A_CAP _AI_A2A_CAP:__Engage( 0.5 ) - local Task = AIGroup:TaskOrbitCircle( 4000, 400 ) - AIGroup:SetTask( Task ) + --local Task = AIGroup:TaskOrbitCircle( 4000, 400 ) + --AIGroup:SetTask( Task ) end end @@ -434,10 +434,6 @@ function AI_A2A_CAP:onafterEngage( AIGroup, From, Event, To, AttackSetUnit ) end end - --- Now we're going to do something special, we're going to call a function from a waypoint action at the AIControllable... - self.Controllable:WayPointInitialize( EngageRoute ) - - if #AttackTasks == 0 then self:E("No targets found -> Going back to Patrolling") self:__Abort( 0.5 ) @@ -446,7 +442,7 @@ function AI_A2A_CAP:onafterEngage( AIGroup, From, Event, To, AttackSetUnit ) AIGroup:OptionROTPassiveDefense() AttackTasks[#AttackTasks+1] = AIGroup:TaskFunction( 1, 1, "AI_A2A_CAP.AttackRoute" ) - AttackTasks[#AttackTasks+1] = AIGroup:TaskOrbitCircle( 4000, self.PatrolMinSpeed ) + --AttackTasks[#AttackTasks+1] = AIGroup:TaskOrbitCircle( AIGroup:GetHeight(), self.PatrolMinSpeed ) EngageRoute[1].task = AIGroup:TaskCombo( AttackTasks ) diff --git a/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua b/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua index e164e3cd1..3d2d67dd1 100644 --- a/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua @@ -1482,10 +1482,18 @@ do -- AI_A2A_DISPATCHER Cap.CapLimit = CapLimit Cap.Scheduler = Cap.Scheduler or SCHEDULER:New( self ) local Scheduler = Cap.Scheduler -- Core.Scheduler#SCHEDULER + local ScheduleID = Cap.ScheduleID local Variance = ( Cap.HighInterval - Cap.LowInterval ) / 2 local Median = Cap.LowInterval + Variance local Randomization = Variance / Median - Scheduler:Schedule(self, self.SchedulerCAP, { SquadronName }, Median, Median, Randomization ) + + self:E({Randomization = Randomization}) + + if ScheduleID then + Scheduler:Stop( ScheduleID ) + end + + Cap.ScheduleID = Scheduler:Schedule( self, self.SchedulerCAP, { SquadronName }, Median, Median, Randomization ) else error( "This squadron does not exist:" .. SquadronName ) end diff --git a/Moose Development/Moose/AI/AI_A2A_Gci.lua b/Moose Development/Moose/AI/AI_A2A_Gci.lua index 2986be4fd..17107c514 100644 --- a/Moose Development/Moose/AI/AI_A2A_Gci.lua +++ b/Moose Development/Moose/AI/AI_A2A_Gci.lua @@ -319,8 +319,8 @@ function AI_A2A_GCI.InterceptRoute( AIGroup ) local _AI_A2A_GCI = AIGroup:GetState( AIGroup, "AI_A2A_GCI" ) -- AI.AI_Cap#AI_A2A_GCI _AI_A2A_GCI:__Engage( 0.5 ) - local Task = AIGroup:TaskOrbitCircle( 4000, 400 ) - AIGroup:SetTask( Task ) + --local Task = AIGroup:TaskOrbitCircle( 4000, 400 ) + --AIGroup:SetTask( Task ) end end @@ -418,7 +418,7 @@ function AI_A2A_GCI:onafterEngage( AIGroup, From, Event, To, AttackSetUnit ) AIGroup:OptionROTPassiveDefense() AttackTasks[#AttackTasks+1] = AIGroup:TaskFunction( 1, 1, "AI_A2A_GCI.InterceptRoute" ) - AttackTasks[#AttackTasks+1] = AIGroup:TaskOrbitCircle( 4000, self.EngageMinSpeed ) + --AttackTasks[#AttackTasks+1] = AIGroup:TaskOrbitCircle( AIGroup:GetHeight(), self.EngageMinSpeed ) EngageRoute[#EngageRoute].task = AIGroup:TaskCombo( AttackTasks ) --- Do a trick, link the NewEngageRoute function of the object to the AIControllable in a temporary variable ... diff --git a/Moose Development/Moose/AI/AI_A2A_Patrol.lua b/Moose Development/Moose/AI/AI_A2A_Patrol.lua index 12b59918f..47ca407a6 100644 --- a/Moose Development/Moose/AI/AI_A2A_Patrol.lua +++ b/Moose Development/Moose/AI/AI_A2A_Patrol.lua @@ -317,8 +317,13 @@ end -- Note that this method is required, as triggers the next route when patrolling for the Controllable. function AI_A2A_PATROL.PatrolRoute( AIGroup ) - local _AI_A2A_Patrol = AIGroup:GetState( AIGroup, "AI_A2A_PATROL" ) -- #AI_A2A_PATROL - _AI_A2A_Patrol:Route() + AIGroup:E( { "AI_A2A_PATROL.PatrolRoute:", AIGroup:GetName() } ) + + if AIGroup:IsAlive() then + local _AI_A2A_Patrol = AIGroup:GetState( AIGroup, "AI_A2A_PATROL" ) -- #AI_A2A_PATROL + _AI_A2A_Patrol:Route() + end + end @@ -347,7 +352,8 @@ function AI_A2A_PATROL:onafterRoute( AIGroup, From, Event, To ) local CurrentCoord = AIGroup:GetCoordinate() local ToTargetCoord = self.PatrolZone:GetRandomPointVec2() - ToTargetCoord:SetAlt(math.random( self.PatrolFloorAltitude,self.PatrolCeilingAltitude ) ) + self:E({self.PatrolFloorAltitude, self.PatrolCeilingAltitude}) + ToTargetCoord:SetAlt( math.random( self.PatrolFloorAltitude, self.PatrolCeilingAltitude ) ) self:SetTargetDistance( ToTargetCoord ) -- For RTB status check local ToTargetSpeed = math.random( self.PatrolMinSpeed, self.PatrolMaxSpeed ) @@ -364,9 +370,6 @@ function AI_A2A_PATROL:onafterRoute( AIGroup, From, Event, To ) PatrolRoute[#PatrolRoute+1] = ToPatrolRoutePoint PatrolRoute[#PatrolRoute+1] = ToPatrolRoutePoint - --- Now we're going to do something special, we're going to call a function from a waypoint action at the AIControllable... - AIGroup:WayPointInitialize( PatrolRoute ) - local Tasks = {} Tasks[#Tasks+1] = AIGroup:TaskFunction( 1, 1, "AI_A2A_PATROL.PatrolRoute" ) @@ -375,8 +378,11 @@ function AI_A2A_PATROL:onafterRoute( AIGroup, From, Event, To ) --- Do a trick, link the NewPatrolRoute function of the PATROLGROUP object to the AIControllable in a temporary variable ... AIGroup:SetState( AIGroup, "AI_A2A_PATROL", self ) + AIGroup:OptionROEReturnFire() + AIGroup:OptionROTPassiveDefense() + --- NOW ROUTE THE GROUP! - AIGroup:WayPointExecute( 1, 2 ) + AIGroup:SetTask( AIGroup:TaskRoute( PatrolRoute ), 0.5 ) end end diff --git a/Moose Development/Moose/Core/ScheduleDispatcher.lua b/Moose Development/Moose/Core/ScheduleDispatcher.lua index 7ba478111..ca8f77150 100644 --- a/Moose Development/Moose/Core/ScheduleDispatcher.lua +++ b/Moose Development/Moose/Core/ScheduleDispatcher.lua @@ -79,8 +79,8 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr self.Schedule[Scheduler][self.CallID].Arguments = ScheduleArguments self.Schedule[Scheduler][self.CallID].StartTime = timer.getTime() + ( Start or 0 ) self.Schedule[Scheduler][self.CallID].Start = Start + .1 - self.Schedule[Scheduler][self.CallID].Repeat = Repeat - self.Schedule[Scheduler][self.CallID].Randomize = Randomize + self.Schedule[Scheduler][self.CallID].Repeat = Repeat or 0 + self.Schedule[Scheduler][self.CallID].Randomize = Randomize or 0 self.Schedule[Scheduler][self.CallID].Stop = Stop self:T3( self.Schedule[Scheduler][self.CallID] ) @@ -185,7 +185,7 @@ function SCHEDULEDISPATCHER:Start( Scheduler, CallID ) Schedule[CallID].ScheduleID = timer.scheduleFunction( Schedule[CallID].CallHandler, CallID, - timer.getTime() + Schedule[CallID].Start + timer.getTime() + Schedule[CallID].Start + math.random( - ( Schedule[CallID].Randomize * Schedule[CallID].Repeat / 2 ), ( Schedule[CallID].Randomize * Schedule[CallID].Repeat / 2 ) ) ) end else From 2501db53b8312e128537156afff6e28ec0a6c77e Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Wed, 2 Aug 2017 12:46:13 +0200 Subject: [PATCH 5/7] Removed traces --- Moose Development/Moose/AI/AI_A2A_Dispatcher.lua | 2 -- Moose Development/Moose/AI/AI_A2A_Patrol.lua | 1 - 2 files changed, 3 deletions(-) diff --git a/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua b/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua index 3d2d67dd1..886451dd2 100644 --- a/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua @@ -1487,8 +1487,6 @@ do -- AI_A2A_DISPATCHER local Median = Cap.LowInterval + Variance local Randomization = Variance / Median - self:E({Randomization = Randomization}) - if ScheduleID then Scheduler:Stop( ScheduleID ) end diff --git a/Moose Development/Moose/AI/AI_A2A_Patrol.lua b/Moose Development/Moose/AI/AI_A2A_Patrol.lua index 47ca407a6..60d0aee64 100644 --- a/Moose Development/Moose/AI/AI_A2A_Patrol.lua +++ b/Moose Development/Moose/AI/AI_A2A_Patrol.lua @@ -352,7 +352,6 @@ function AI_A2A_PATROL:onafterRoute( AIGroup, From, Event, To ) local CurrentCoord = AIGroup:GetCoordinate() local ToTargetCoord = self.PatrolZone:GetRandomPointVec2() - self:E({self.PatrolFloorAltitude, self.PatrolCeilingAltitude}) ToTargetCoord:SetAlt( math.random( self.PatrolFloorAltitude, self.PatrolCeilingAltitude ) ) self:SetTargetDistance( ToTargetCoord ) -- For RTB status check From 2cf1801f1d344e9b5ba3e259d1ac5894d711e610 Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Wed, 2 Aug 2017 20:54:26 +0200 Subject: [PATCH 6/7] Fixed endless loop when out of resources upon receiving a GCI request. --- .../Moose/AI/AI_A2A_Dispatcher.lua | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua b/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua index 886451dd2..0be824d76 100644 --- a/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua @@ -2423,7 +2423,9 @@ do -- AI_A2A_DISPATCHER local ClosestDistance = 0 local ClosestDefenderSquadronName = nil - while( DefendersCount > 0 ) do + local BreakLoop = false + + while( DefendersCount > 0 and not BreakLoop ) do for SquadronName, DefenderSquadron in pairs( self.DefenderSquadrons or {} ) do for InterceptID, Intercept in pairs( DefenderSquadron.Gci or {} ) do @@ -2434,7 +2436,7 @@ do -- AI_A2A_DISPATCHER local TargetCoord = DetectedItem.InterceptCoord if TargetCoord then local Distance = SpawnCoord:Get2DDistance( TargetCoord ) - self:F( { Distance = Distance, TargetCoord = TargetCoord } ) + self:F( { Distance = Distance, TargetCoord = TargetCoord } ) if ClosestDistance == 0 or Distance < ClosestDistance then @@ -2543,15 +2545,20 @@ do -- AI_A2A_DISPATCHER AIGroup:Destroy() end end - end - end + end -- if DefenderGCI then + end -- while ( DefendersNeeded > 0 ) do end + else + -- No more resources, try something else. + -- Subject for a later enhancement to try to depart from another squadron and disable this one. + BreakLoop = true + break end else -- There isn't any closest airbase anymore, break the loop. break end - end + end -- if DefenderSquadron then end From 2611ba0fe8cd258df4f32c6767c4057f6b971f54 Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Wed, 2 Aug 2017 21:18:16 +0200 Subject: [PATCH 7/7] Fix the schedule dispatcher -- Implemented a working Stop time. --- Moose Development/Moose/Core/ScheduleDispatcher.lua | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Moose Development/Moose/Core/ScheduleDispatcher.lua b/Moose Development/Moose/Core/ScheduleDispatcher.lua index ca8f77150..ab5785ff3 100644 --- a/Moose Development/Moose/Core/ScheduleDispatcher.lua +++ b/Moose Development/Moose/Core/ScheduleDispatcher.lua @@ -62,7 +62,7 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr -- Initialize the ObjectSchedulers array, which is a weakly coupled table. -- If the object used as the key is nil, then the garbage collector will remove the item from the Functions array. - self.ObjectSchedulers = self.ObjectSchedulers or {} -- setmetatable( {}, { __mode = "v" } ) + self.ObjectSchedulers = self.ObjectSchedulers or setmetatable( {}, { __mode = "v" } ) if Scheduler.MasterObject then self.ObjectSchedulers[self.CallID] = Scheduler @@ -133,10 +133,10 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr end local CurrentTime = timer.getTime() - local StartTime = CurrentTime + Start + local StartTime = Schedule.StartTime if Status and (( Result == nil ) or ( Result and Result ~= false ) ) then - if Repeat ~= 0 and ( Stop == 0 ) or ( Stop ~= 0 and CurrentTime <= StartTime + Stop ) then + if Repeat ~= 0 and ( ( Stop == 0 ) or ( Stop ~= 0 and CurrentTime <= StartTime + Stop ) ) then local ScheduleTime = CurrentTime + Repeat + @@ -182,6 +182,7 @@ function SCHEDULEDISPATCHER:Start( Scheduler, CallID ) -- Only start when there is no ScheduleID defined! -- This prevents to "Start" the scheduler twice with the same CallID... if not Schedule[CallID].ScheduleID then + Schedule[CallID].StartTime = timer.getTime() -- Set the StartTime field to indicate when the scheduler started. Schedule[CallID].ScheduleID = timer.scheduleFunction( Schedule[CallID].CallHandler, CallID,