mirror of
https://github.com/FlightControl-Master/MOOSE.git
synced 2026-08-11 19:58:22 +00:00
Merge branch 'FF/Develop' into FF/Fox2
This commit is contained in:
@@ -433,7 +433,7 @@ function AI_A2A:onafterStatus()
|
||||
self:F({DistanceFromHomeBase=DistanceFromHomeBase})
|
||||
|
||||
if DistanceFromHomeBase > self.DisengageRadius then
|
||||
self:E( self.Controllable:GetName() .. " is too far from home base, RTB!" )
|
||||
self:I( self.Controllable:GetName() .. " is too far from home base, RTB!" )
|
||||
self:Hold( 300 )
|
||||
RTB = false
|
||||
end
|
||||
@@ -453,10 +453,10 @@ function AI_A2A:onafterStatus()
|
||||
self:F({Fuel=Fuel, PatrolFuelThresholdPercentage=self.PatrolFuelThresholdPercentage})
|
||||
if Fuel < self.PatrolFuelThresholdPercentage then
|
||||
if self.TankerName then
|
||||
self:E( self.Controllable:GetName() .. " is out of fuel: " .. Fuel .. " ... Refuelling at Tanker!" )
|
||||
self:I( self.Controllable:GetName() .. " is out of fuel: " .. Fuel .. " ... Refuelling at Tanker!" )
|
||||
self:Refuel()
|
||||
else
|
||||
self:E( self.Controllable:GetName() .. " is out of fuel: " .. Fuel .. " ... RTB!" )
|
||||
self:I( self.Controllable:GetName() .. " is out of fuel: " .. Fuel .. " ... RTB!" )
|
||||
local OldAIControllable = self.Controllable
|
||||
|
||||
local OrbitTask = OldAIControllable:TaskOrbitCircle( math.random( self.PatrolFloorAltitude, self.PatrolCeilingAltitude ), self.PatrolMinSpeed )
|
||||
@@ -475,7 +475,7 @@ function AI_A2A:onafterStatus()
|
||||
local InitialLife = self.Controllable:GetLife0()
|
||||
self:F( { Damage = Damage, InitialLife = InitialLife, DamageThreshold = self.PatrolDamageThreshold } )
|
||||
if ( Damage / InitialLife ) < self.PatrolDamageThreshold then
|
||||
self:E( self.Controllable:GetName() .. " is damaged: " .. Damage .. " ... RTB!" )
|
||||
self:I( self.Controllable:GetName() .. " is damaged: " .. Damage .. " ... RTB!" )
|
||||
self:Damaged()
|
||||
RTB = true
|
||||
self:SetStatusOff()
|
||||
@@ -493,7 +493,7 @@ function AI_A2A:onafterStatus()
|
||||
if Damage ~= InitialLife then
|
||||
self:Damaged()
|
||||
else
|
||||
self:E( self.Controllable:GetName() .. " control lost! " )
|
||||
self:I( self.Controllable:GetName() .. " control lost! " )
|
||||
self:LostControl()
|
||||
end
|
||||
else
|
||||
@@ -549,7 +549,7 @@ function AI_A2A:onafterRTB( AIGroup, From, Event, To )
|
||||
|
||||
if AIGroup and AIGroup:IsAlive() then
|
||||
|
||||
self:E( "Group " .. AIGroup:GetName() .. " ... RTB! ( " .. self:GetState() .. " )" )
|
||||
self:I( "Group " .. AIGroup:GetName() .. " ... RTB! ( " .. self:GetState() .. " )" )
|
||||
|
||||
self:ClearTargetDistance()
|
||||
AIGroup:ClearTasks()
|
||||
@@ -567,7 +567,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:I( "RTB and near the airbase!" )
|
||||
self:Home()
|
||||
return
|
||||
end
|
||||
@@ -608,7 +608,7 @@ end
|
||||
function AI_A2A:onafterHome( AIGroup, From, Event, To )
|
||||
self:F( { AIGroup, From, Event, To } )
|
||||
|
||||
self:E( "Group " .. self.Controllable:GetName() .. " ... Home! ( " .. self:GetState() .. " )" )
|
||||
self:I( "Group " .. self.Controllable:GetName() .. " ... Home! ( " .. self:GetState() .. " )" )
|
||||
|
||||
if AIGroup and AIGroup:IsAlive() then
|
||||
end
|
||||
@@ -622,7 +622,7 @@ end
|
||||
function AI_A2A:onafterHold( AIGroup, From, Event, To, HoldTime )
|
||||
self:F( { AIGroup, From, Event, To } )
|
||||
|
||||
self:E( "Group " .. self.Controllable:GetName() .. " ... Holding! ( " .. self:GetState() .. " )" )
|
||||
self:I( "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 )
|
||||
@@ -654,7 +654,7 @@ end
|
||||
function AI_A2A:onafterRefuel( AIGroup, From, Event, To )
|
||||
self:F( { AIGroup, From, Event, To } )
|
||||
|
||||
self:E( "Group " .. self.Controllable:GetName() .. " ... Refuelling! ( " .. self:GetState() .. " )" )
|
||||
self:I( "Group " .. self.Controllable:GetName() .. " ... Refuelling! ( " .. self:GetState() .. " )" )
|
||||
|
||||
if AIGroup and AIGroup:IsAlive() then
|
||||
local Tanker = GROUP:FindByName( self.TankerName )
|
||||
@@ -711,7 +711,7 @@ end
|
||||
function AI_A2A:OnCrash( EventData )
|
||||
|
||||
if self.Controllable:IsAlive() and EventData.IniDCSGroupName == self.Controllable:GetName() then
|
||||
self:E( self.Controllable:GetUnits() )
|
||||
self:I( self.Controllable:GetUnits() )
|
||||
if #self.Controllable:GetUnits() == 1 then
|
||||
self:__Crash( 1, EventData )
|
||||
end
|
||||
|
||||
@@ -278,6 +278,8 @@ function AI_A2A_CAP:New( AICap, PatrolZone, PatrolFloorAltitude, PatrolCeilingAl
|
||||
-- @param #AI_A2A_CAP self
|
||||
-- @param #number Delay The delay in seconds.
|
||||
|
||||
self:AddTransition( { "Patrolling", "Engaging" }, "Refuel", "Refuelling" )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
@@ -279,6 +279,8 @@ function AI_A2A_GCI:New( AIIntercept, EngageMinSpeed, EngageMaxSpeed )
|
||||
-- @param #AI_A2A_GCI self
|
||||
-- @param #number Delay The delay in seconds.
|
||||
|
||||
self:AddTransition( { "Patrolling", "Engaging" }, "Refuel", "Refuelling" )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
@@ -62,12 +62,14 @@ end
|
||||
-- @param #string To The To State string.
|
||||
function AI_A2G_BAI:onafterEngage( DefenderGroup, From, Event, To, AttackSetUnit )
|
||||
|
||||
self:F( { DefenderGroup, From, Event, To, AttackSetUnit} )
|
||||
self:I( { DefenderGroup, From, Event, To, AttackSetUnit} )
|
||||
|
||||
local DefenderGroupName = DefenderGroup:GetName()
|
||||
|
||||
local AttackCount = AttackSetUnit:Count()
|
||||
|
||||
self.AttackSetUnit = AttackSetUnit -- Kept in memory in case of resume from refuel in air!
|
||||
|
||||
if AttackCount > 0 then
|
||||
|
||||
if DefenderGroup:IsAlive() then
|
||||
@@ -129,7 +131,7 @@ function AI_A2G_BAI:onafterEngage( DefenderGroup, From, Event, To, AttackSetUnit
|
||||
end
|
||||
|
||||
if #AttackUnitTasks == 0 then
|
||||
self:E( DefenderGroupName .. ": No targets found -> Going RTB")
|
||||
self:I( DefenderGroupName .. ": No targets found -> Going RTB")
|
||||
self:Return()
|
||||
self:__RTB( self.TaskDelay )
|
||||
else
|
||||
@@ -146,8 +148,19 @@ function AI_A2G_BAI:onafterEngage( DefenderGroup, From, Event, To, AttackSetUnit
|
||||
DefenderGroup:Route( EngageRoute, self.TaskDelay )
|
||||
end
|
||||
else
|
||||
self:E( DefenderGroupName .. ": No targets found -> Going RTB")
|
||||
self:I( DefenderGroupName .. ": No targets found -> Going RTB")
|
||||
self:Return()
|
||||
self:__RTB( self.TaskDelay )
|
||||
end
|
||||
end
|
||||
|
||||
--- @param Wrapper.Group#GROUP AIEngage
|
||||
function AI_A2G_BAI.Resume( AIEngage, Fsm )
|
||||
|
||||
AIEngage:F( { "AI_A2G_BAI.Resume:", AIEngage:GetName() } )
|
||||
if AIEngage:IsAlive() then
|
||||
Fsm:__Reset( Fsm.TaskDelay )
|
||||
Fsm:__EngageRoute( Fsm.TaskDelay, Fsm.AttackSetUnit )
|
||||
end
|
||||
|
||||
end
|
||||
@@ -66,6 +66,8 @@ function AI_A2G_CAS:onafterEngage( DefenderGroup, From, Event, To, AttackSetUnit
|
||||
|
||||
local DefenderGroupName = DefenderGroup:GetName()
|
||||
|
||||
self.AttackSetUnit = AttackSetUnit -- Kept in memory in case of resume from refuel in air!
|
||||
|
||||
local AttackCount = AttackSetUnit:Count()
|
||||
|
||||
if AttackCount > 0 then
|
||||
@@ -124,7 +126,7 @@ function AI_A2G_CAS:onafterEngage( DefenderGroup, From, Event, To, AttackSetUnit
|
||||
|
||||
|
||||
if #AttackUnitTasks == 0 then
|
||||
self:E( DefenderGroupName .. ": No targets found -> Going RTB")
|
||||
self:I( DefenderGroupName .. ": No targets found -> Going RTB")
|
||||
self:Return()
|
||||
self:__RTB( self.TaskDelay )
|
||||
else
|
||||
@@ -142,9 +144,19 @@ function AI_A2G_CAS:onafterEngage( DefenderGroup, From, Event, To, AttackSetUnit
|
||||
DefenderGroup:Route( EngageRoute, self.TaskDelay )
|
||||
end
|
||||
else
|
||||
self:E( DefenderGroupName .. ": No targets found -> Going RTB")
|
||||
self:I( DefenderGroupName .. ": No targets found -> Going RTB")
|
||||
self:Return()
|
||||
self:__RTB( self.TaskDelay )
|
||||
end
|
||||
end
|
||||
|
||||
--- @param Wrapper.Group#GROUP AIEngage
|
||||
function AI_A2G_CAS.Resume( AIEngage, Fsm )
|
||||
|
||||
AIEngage:F( { "AI_A2G_CAS.Resume:", AIEngage:GetName() } )
|
||||
if AIEngage:IsAlive() then
|
||||
Fsm:__Reset( Fsm.TaskDelay )
|
||||
Fsm:__EngageRoute( Fsm.TaskDelay, Fsm.AttackSetUnit )
|
||||
end
|
||||
|
||||
end
|
||||
@@ -1133,7 +1133,7 @@ do -- AI_A2G_DISPATCHER
|
||||
|
||||
self.TakeoffScheduleID = self:ScheduleRepeat( 10, 10, 0, nil, self.ResourceTakeoff, self )
|
||||
|
||||
self:__Start( 1 )
|
||||
self:__Start( 1 )
|
||||
|
||||
return self
|
||||
end
|
||||
@@ -3496,91 +3496,123 @@ do -- AI_A2G_DISPATCHER
|
||||
|
||||
local AI_A2G_PATROL = { SEAD = AI_A2G_SEAD, BAI = AI_A2G_BAI, CAS = AI_A2G_CAS }
|
||||
|
||||
local Fsm = AI_A2G_PATROL[DefenseTaskType]:New( DefenderGroup, Patrol.EngageMinSpeed, Patrol.EngageMaxSpeed, Patrol.EngageFloorAltitude, Patrol.EngageCeilingAltitude, Patrol.Zone, Patrol.PatrolFloorAltitude, Patrol.PatrolCeilingAltitude, Patrol.PatrolMinSpeed, Patrol.PatrolMaxSpeed, Patrol.AltType )
|
||||
Fsm:SetDispatcher( self )
|
||||
Fsm:SetHomeAirbase( DefenderSquadron.Airbase )
|
||||
Fsm:SetFuelThreshold( DefenderSquadron.FuelThreshold or self.DefenderDefault.FuelThreshold, 60 )
|
||||
Fsm:SetDamageThreshold( self.DefenderDefault.DamageThreshold )
|
||||
Fsm:SetDisengageRadius( self.DisengageRadius )
|
||||
Fsm:SetTanker( DefenderSquadron.TankerName or self.DefenderDefault.TankerName )
|
||||
Fsm:Start()
|
||||
local AI_A2G_Fsm = AI_A2G_PATROL[DefenseTaskType]:New( DefenderGroup, Patrol.EngageMinSpeed, Patrol.EngageMaxSpeed, Patrol.EngageFloorAltitude, Patrol.EngageCeilingAltitude, Patrol.Zone, Patrol.PatrolFloorAltitude, Patrol.PatrolCeilingAltitude, Patrol.PatrolMinSpeed, Patrol.PatrolMaxSpeed, Patrol.AltType )
|
||||
AI_A2G_Fsm:SetDispatcher( self )
|
||||
AI_A2G_Fsm:SetHomeAirbase( DefenderSquadron.Airbase )
|
||||
AI_A2G_Fsm:SetFuelThreshold( DefenderSquadron.FuelThreshold or self.DefenderDefault.FuelThreshold, 60 )
|
||||
AI_A2G_Fsm:SetDamageThreshold( self.DefenderDefault.DamageThreshold )
|
||||
AI_A2G_Fsm:SetDisengageRadius( self.DisengageRadius )
|
||||
AI_A2G_Fsm:SetTanker( DefenderSquadron.TankerName or self.DefenderDefault.TankerName )
|
||||
AI_A2G_Fsm:Start()
|
||||
|
||||
self:SetDefenderTask( SquadronName, DefenderGroup, DefenseTaskType, Fsm, nil, DefenderGrouping )
|
||||
self:SetDefenderTask( SquadronName, DefenderGroup, DefenseTaskType, AI_A2G_Fsm, nil, DefenderGrouping )
|
||||
|
||||
function Fsm:onafterTakeoff( Defender, From, Event, To )
|
||||
self:F({"Defender Birth", Defender:GetName()})
|
||||
function AI_A2G_Fsm:onafterTakeoff( DefenderGroup, From, Event, To )
|
||||
self:F({"Defender Takeoff", DefenderGroup:GetName()})
|
||||
--self:GetParent(self).onafterBirth( self, Defender, From, Event, To )
|
||||
|
||||
local DefenderName = Defender:GetCallsign()
|
||||
local Dispatcher = Fsm:GetDispatcher() -- #AI_A2G_DISPATCHER
|
||||
local Squadron = Dispatcher:GetSquadronFromDefender( Defender )
|
||||
local DefenderName = DefenderGroup:GetCallsign()
|
||||
local Dispatcher = AI_A2G_Fsm:GetDispatcher() -- #AI_A2G_DISPATCHER
|
||||
local Squadron = Dispatcher:GetSquadronFromDefender( DefenderGroup )
|
||||
|
||||
if Squadron then
|
||||
Dispatcher:MessageToPlayers( "Squadron " .. Squadron.Name .. ", " .. DefenderName .. " airborne." )
|
||||
Fsm:Patrol() -- Engage on the TargetSetUnit
|
||||
Dispatcher:MessageToPlayers( "Squadron " .. Squadron.Name .. ", " .. DefenderName .. " airborne.", "Wheels_up.wav", 3, "A2G/", DefenderGroup )
|
||||
AI_A2G_Fsm:Patrol() -- Engage on the TargetSetUnit
|
||||
end
|
||||
end
|
||||
|
||||
function Fsm:onafterPatrolRoute( Defender, From, Event, To )
|
||||
self:F({"Defender PatrolRoute", Defender:GetName()})
|
||||
self:GetParent(self).onafterPatrolRoute( self, Defender, From, Event, To )
|
||||
function AI_A2G_Fsm:onafterPatrolRoute( DefenderGroup, From, Event, To )
|
||||
self:F({"Defender PatrolRoute", DefenderGroup:GetName()})
|
||||
self:GetParent(self).onafterPatrolRoute( self, DefenderGroup, From, Event, To )
|
||||
|
||||
local DefenderName = Defender:GetCallsign()
|
||||
local DefenderName = DefenderGroup:GetCallsign()
|
||||
local Dispatcher = self:GetDispatcher() -- #AI_A2G_DISPATCHER
|
||||
local Squadron = Dispatcher:GetSquadronFromDefender( Defender )
|
||||
local Squadron = Dispatcher:GetSquadronFromDefender( DefenderGroup )
|
||||
if Squadron then
|
||||
Dispatcher:MessageToPlayers( "Squadron " .. Squadron.Name .. ", " .. DefenderName .. " patrolling." )
|
||||
Dispatcher:MessageToPlayers( "Squadron " .. Squadron.Name .. ", " .. DefenderName .. " patrolling.", "Patrolling.wav", 3, "A2G/", DefenderGroup )
|
||||
end
|
||||
|
||||
Dispatcher:ClearDefenderTaskTarget( Defender )
|
||||
Dispatcher:ClearDefenderTaskTarget( DefenderGroup )
|
||||
end
|
||||
|
||||
function Fsm:onafterRTB( Defender, From, Event, To )
|
||||
self:F({"Defender RTB", Defender:GetName()})
|
||||
self:GetParent(self).onafterRTB( self, Defender, From, Event, To )
|
||||
function AI_A2G_Fsm:onafterEngageRoute( DefenderGroup, From, Event, To, AttackSetUnit )
|
||||
self:F({"Engage Route", DefenderGroup:GetName()})
|
||||
|
||||
local DefenderName = Defender:GetCallsign()
|
||||
local Dispatcher = self:GetDispatcher() -- #AI_A2G_DISPATCHER
|
||||
local Squadron = Dispatcher:GetSquadronFromDefender( Defender )
|
||||
Dispatcher:MessageToPlayers( "Squadron " .. Squadron.Name .. ", " .. DefenderName .. " returning." )
|
||||
self:GetParent(self).onafterEngageRoute( self, DefenderGroup, From, Event, To, AttackSetUnit )
|
||||
|
||||
local DefenderName = DefenderGroup:GetCallsign()
|
||||
local Dispatcher = AI_A2G_Fsm:GetDispatcher() -- #AI_A2G_DISPATCHER
|
||||
local Squadron = Dispatcher:GetSquadronFromDefender( DefenderGroup )
|
||||
|
||||
if Squadron then
|
||||
local FirstUnit = AttackSetUnit:GetFirst()
|
||||
local Coordinate = FirstUnit:GetCoordinate() -- Core.Point#COORDINATE
|
||||
|
||||
Dispatcher:MessageToPlayers( "Squadron " .. Squadron.Name .. ", " .. DefenderName .. " on route to ground target at " .. Coordinate:ToStringA2G( DefenderGroup ), "Moving_on_to_ground_target.wav", 3, "A2G/", DefenderGroup )
|
||||
end
|
||||
end
|
||||
|
||||
Dispatcher:ClearDefenderTaskTarget( Defender )
|
||||
function AI_A2G_Fsm:OnAfterEngage( DefenderGroup, From, Event, To, AttackSetUnit )
|
||||
self:F({"Engage Route", DefenderGroup:GetName()})
|
||||
--self:GetParent(self).onafterBirth( self, Defender, From, Event, To )
|
||||
|
||||
local DefenderName = DefenderGroup:GetCallsign()
|
||||
local Dispatcher = AI_A2G_Fsm:GetDispatcher() -- #AI_A2G_DISPATCHER
|
||||
local Squadron = Dispatcher:GetSquadronFromDefender( DefenderGroup )
|
||||
local FirstUnit = AttackSetUnit:GetFirst()
|
||||
if FirstUnit then
|
||||
local Coordinate = FirstUnit:GetCoordinate()
|
||||
|
||||
Dispatcher:MessageToPlayers( "Squadron " .. Squadron.Name .. ", " .. DefenderName .. " engaging ground target at " .. Coordinate:ToStringA2G( DefenderGroup ), "Engaging_ground_target.wav", 3, "A2G/", DefenderGroup )
|
||||
end
|
||||
end
|
||||
|
||||
function AI_A2G_Fsm:onafterRTB( DefenderGroup, From, Event, To )
|
||||
self:F({"Defender RTB", DefenderGroup:GetName()})
|
||||
self:GetParent(self).onafterRTB( self, DefenderGroup, From, Event, To )
|
||||
|
||||
local DefenderName = DefenderGroup:GetCallsign()
|
||||
local Dispatcher = self:GetDispatcher() -- #AI_A2G_DISPATCHER
|
||||
local Squadron = Dispatcher:GetSquadronFromDefender( DefenderGroup )
|
||||
Dispatcher:MessageToPlayers( "Squadron " .. Squadron.Name .. ", " .. DefenderName .. " returning.", "Returning_to_base.wav", 3, "A2G/", DefenderGroup )
|
||||
|
||||
Dispatcher:ClearDefenderTaskTarget( DefenderGroup )
|
||||
end
|
||||
|
||||
--- @param #AI_A2G_DISPATCHER self
|
||||
function Fsm:onafterLostControl( Defender, From, Event, To )
|
||||
self:F({"Defender LostControl", Defender:GetName()})
|
||||
self:GetParent(self).onafterHome( self, Defender, From, Event, To )
|
||||
function AI_A2G_Fsm:onafterLostControl( DefenderGroup, From, Event, To )
|
||||
self:F({"Defender LostControl", DefenderGroup:GetName()})
|
||||
self:GetParent(self).onafterHome( self, DefenderGroup, From, Event, To )
|
||||
|
||||
local DefenderName = Defender:GetCallsign()
|
||||
local Dispatcher = Fsm:GetDispatcher() -- #AI_A2G_DISPATCHER
|
||||
local Squadron = Dispatcher:GetSquadronFromDefender( Defender )
|
||||
local DefenderName = DefenderGroup:GetCallsign()
|
||||
local Dispatcher = AI_A2G_Fsm:GetDispatcher() -- #AI_A2G_DISPATCHER
|
||||
local Squadron = Dispatcher:GetSquadronFromDefender( DefenderGroup )
|
||||
Dispatcher:MessageToPlayers( "Squadron " .. Squadron.Name .. ", " .. DefenderName .. " lost control." )
|
||||
if Defender:IsAboveRunway() then
|
||||
Dispatcher:RemoveDefenderFromSquadron( Squadron, Defender )
|
||||
Defender:Destroy()
|
||||
if DefenderGroup:IsAboveRunway() then
|
||||
Dispatcher:RemoveDefenderFromSquadron( Squadron, DefenderGroup )
|
||||
DefenderGroup:Destroy()
|
||||
end
|
||||
end
|
||||
|
||||
--- @param #AI_A2G_DISPATCHER self
|
||||
function Fsm:onafterHome( Defender, From, Event, To, Action )
|
||||
self:F({"Defender Home", Defender:GetName()})
|
||||
self:GetParent(self).onafterHome( self, Defender, From, Event, To )
|
||||
function AI_A2G_Fsm:onafterHome( DefenderGroup, From, Event, To, Action )
|
||||
self:F({"Defender Home", DefenderGroup:GetName()})
|
||||
self:GetParent(self).onafterHome( self, DefenderGroup, From, Event, To )
|
||||
|
||||
local DefenderName = Defender:GetCallsign()
|
||||
local DefenderName = DefenderGroup:GetCallsign()
|
||||
local Dispatcher = self:GetDispatcher() -- #AI_A2G_DISPATCHER
|
||||
local Squadron = Dispatcher:GetSquadronFromDefender( Defender )
|
||||
Dispatcher:MessageToPlayers( "Squadron " .. Squadron.Name .. ", " .. DefenderName .. " landing." )
|
||||
local Squadron = Dispatcher:GetSquadronFromDefender( DefenderGroup )
|
||||
Dispatcher:MessageToPlayers( "Squadron " .. Squadron.Name .. ", " .. DefenderName .. " landing.", "Landing_at_base.wav", 3, "A2G/", DefenderGroup )
|
||||
|
||||
if Action and Action == "Destroy" then
|
||||
Dispatcher:RemoveDefenderFromSquadron( Squadron, Defender )
|
||||
Defender:Destroy()
|
||||
Dispatcher:RemoveDefenderFromSquadron( Squadron, DefenderGroup )
|
||||
DefenderGroup:Destroy()
|
||||
end
|
||||
|
||||
if Dispatcher:GetSquadronLanding( Squadron.Name ) == AI_A2G_DISPATCHER.Landing.NearAirbase then
|
||||
Dispatcher:RemoveDefenderFromSquadron( Squadron, Defender )
|
||||
Defender:Destroy()
|
||||
Dispatcher:ResourcePark( Squadron, Defender )
|
||||
Dispatcher:RemoveDefenderFromSquadron( Squadron, DefenderGroup )
|
||||
DefenderGroup:Destroy()
|
||||
Dispatcher:ResourcePark( Squadron, DefenderGroup )
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -3605,112 +3637,112 @@ do -- AI_A2G_DISPATCHER
|
||||
|
||||
local AI_A2G = { SEAD = AI_A2G_SEAD, BAI = AI_A2G_BAI, CAS = AI_A2G_CAS }
|
||||
|
||||
local Fsm = AI_A2G[DefenseTaskType]:New( DefenderGroup, Defense.EngageMinSpeed, Defense.EngageMaxSpeed, Defense.EngageFloorAltitude, Defense.EngageCeilingAltitude ) -- AI.AI_A2G_ENGAGE
|
||||
Fsm:SetDispatcher( self )
|
||||
Fsm:SetHomeAirbase( DefenderSquadron.Airbase )
|
||||
Fsm:SetFuelThreshold( DefenderSquadron.FuelThreshold or self.DefenderDefault.FuelThreshold, 60 )
|
||||
Fsm:SetDamageThreshold( self.DefenderDefault.DamageThreshold )
|
||||
Fsm:SetDisengageRadius( self.DisengageRadius )
|
||||
Fsm:Start()
|
||||
local AI_A2G_Fsm = AI_A2G[DefenseTaskType]:New( DefenderGroup, Defense.EngageMinSpeed, Defense.EngageMaxSpeed, Defense.EngageFloorAltitude, Defense.EngageCeilingAltitude ) -- AI.AI_A2G_ENGAGE
|
||||
AI_A2G_Fsm:SetDispatcher( self )
|
||||
AI_A2G_Fsm:SetHomeAirbase( DefenderSquadron.Airbase )
|
||||
AI_A2G_Fsm:SetFuelThreshold( DefenderSquadron.FuelThreshold or self.DefenderDefault.FuelThreshold, 60 )
|
||||
AI_A2G_Fsm:SetDamageThreshold( self.DefenderDefault.DamageThreshold )
|
||||
AI_A2G_Fsm:SetDisengageRadius( self.DisengageRadius )
|
||||
AI_A2G_Fsm:Start()
|
||||
|
||||
self:SetDefenderTask( SquadronName, DefenderGroup, DefenseTaskType, Fsm, AttackerDetection, DefenderGrouping )
|
||||
self:SetDefenderTask( SquadronName, DefenderGroup, DefenseTaskType, AI_A2G_Fsm, AttackerDetection, DefenderGrouping )
|
||||
|
||||
function Fsm:onafterTakeoff( Defender, From, Event, To )
|
||||
self:F({"Defender Birth", Defender:GetName()})
|
||||
function AI_A2G_Fsm:onafterTakeoff( DefenderGroup, From, Event, To )
|
||||
self:F({"Defender Birth", DefenderGroup:GetName()})
|
||||
--self:GetParent(self).onafterBirth( self, Defender, From, Event, To )
|
||||
|
||||
local DefenderName = Defender:GetCallsign()
|
||||
local Dispatcher = Fsm:GetDispatcher() -- #AI_A2G_DISPATCHER
|
||||
local Squadron = Dispatcher:GetSquadronFromDefender( Defender )
|
||||
local DefenderTarget = Dispatcher:GetDefenderTaskTarget( Defender )
|
||||
local DefenderName = DefenderGroup:GetCallsign()
|
||||
local Dispatcher = AI_A2G_Fsm:GetDispatcher() -- #AI_A2G_DISPATCHER
|
||||
local Squadron = Dispatcher:GetSquadronFromDefender( DefenderGroup )
|
||||
local DefenderTarget = Dispatcher:GetDefenderTaskTarget( DefenderGroup )
|
||||
|
||||
self:F( { DefenderTarget = DefenderTarget } )
|
||||
|
||||
if DefenderTarget then
|
||||
Dispatcher:MessageToPlayers( "Squadron " .. Squadron.Name .. ", " .. DefenderName .. " airborne. Engaging!" )
|
||||
Fsm:EngageRoute( DefenderTarget.Set ) -- Engage on the TargetSetUnit
|
||||
Dispatcher:MessageToPlayers( "Squadron " .. Squadron.Name .. ", " .. DefenderName .. " airborne. Engaging!", "Wheels_up.wav", 3, "A2G/", DefenderGroup )
|
||||
AI_A2G_Fsm:EngageRoute( DefenderTarget.Set ) -- Engage on the TargetSetUnit
|
||||
end
|
||||
end
|
||||
|
||||
function Fsm:onafterEngageRoute( Defender, From, Event, To, AttackSetUnit )
|
||||
self:F({"Engage Route", Defender:GetName()})
|
||||
self:GetParent(self).onafterEngageRoute( self, Defender, From, Event, To, AttackSetUnit )
|
||||
function AI_A2G_Fsm:onafterEngageRoute( DefenderGroup, From, Event, To, AttackSetUnit )
|
||||
self:F({"Engage Route", DefenderGroup:GetName()})
|
||||
|
||||
local DefenderName = Defender:GetCallsign()
|
||||
local Dispatcher = Fsm:GetDispatcher() -- #AI_A2G_DISPATCHER
|
||||
local Squadron = Dispatcher:GetSquadronFromDefender( Defender )
|
||||
local DefenderName = DefenderGroup:GetCallsign()
|
||||
local Dispatcher = AI_A2G_Fsm:GetDispatcher() -- #AI_A2G_DISPATCHER
|
||||
local Squadron = Dispatcher:GetSquadronFromDefender( DefenderGroup )
|
||||
|
||||
if Squadron then
|
||||
local FirstUnit = AttackSetUnit:GetFirst()
|
||||
local Coordinate = FirstUnit:GetCoordinate() -- Core.Point#COORDINATE
|
||||
|
||||
Dispatcher:MessageToPlayers( "Squadron " .. Squadron.Name .. ", " .. DefenderName .. " on route to ground target at " .. Coordinate:ToStringA2G( Defender ) )
|
||||
Dispatcher:MessageToPlayers( "Squadron " .. Squadron.Name .. ", " .. DefenderName .. " on route to ground target at " .. Coordinate:ToStringA2G( DefenderGroup ), "Moving_on_to_ground_target.wav", 3, "A2G/", DefenderGroup )
|
||||
end
|
||||
self:GetParent(self).onafterEngageRoute( self, DefenderGroup, From, Event, To, AttackSetUnit )
|
||||
end
|
||||
|
||||
function Fsm:OnAfterEngage( Defender, From, Event, To, AttackSetUnit )
|
||||
self:F({"Engage Route", Defender:GetName()})
|
||||
function AI_A2G_Fsm:OnAfterEngage( DefenderGroup, From, Event, To, AttackSetUnit )
|
||||
self:F({"Engage Route", DefenderGroup:GetName()})
|
||||
--self:GetParent(self).onafterBirth( self, Defender, From, Event, To )
|
||||
|
||||
local DefenderName = Defender:GetCallsign()
|
||||
local Dispatcher = Fsm:GetDispatcher() -- #AI_A2G_DISPATCHER
|
||||
local Squadron = Dispatcher:GetSquadronFromDefender( Defender )
|
||||
local DefenderName = DefenderGroup:GetCallsign()
|
||||
local Dispatcher = AI_A2G_Fsm:GetDispatcher() -- #AI_A2G_DISPATCHER
|
||||
local Squadron = Dispatcher:GetSquadronFromDefender( DefenderGroup )
|
||||
local FirstUnit = AttackSetUnit:GetFirst()
|
||||
if FirstUnit then
|
||||
local Coordinate = FirstUnit:GetCoordinate()
|
||||
|
||||
Dispatcher:MessageToPlayers( "Squadron " .. Squadron.Name .. ", " .. DefenderName .. " engaging ground target at " .. Coordinate:ToStringA2G( Defender ) )
|
||||
Dispatcher:MessageToPlayers( Squadron, "Squadron " .. Squadron.Name .. ", " .. DefenderName .. " engaging ground target at " .. Coordinate:ToStringA2G( DefenderGroup ), "Engaging_ground_target.wav", 3, "A2G/", DefenderGroup )
|
||||
end
|
||||
end
|
||||
|
||||
function Fsm:onafterRTB( Defender, From, Event, To )
|
||||
self:F({"Defender RTB", Defender:GetName()})
|
||||
function AI_A2G_Fsm:onafterRTB( DefenderGroup, From, Event, To )
|
||||
self:F({"Defender RTB", DefenderGroup:GetName()})
|
||||
|
||||
local DefenderName = Defender:GetCallsign()
|
||||
local DefenderName = DefenderGroup:GetCallsign()
|
||||
local Dispatcher = self:GetDispatcher() -- #AI_A2G_DISPATCHER
|
||||
local Squadron = Dispatcher:GetSquadronFromDefender( Defender )
|
||||
Dispatcher:MessageToPlayers( "Squadron " .. Squadron.Name .. ", " .. DefenderName .. " returning." )
|
||||
local Squadron = Dispatcher:GetSquadronFromDefender( DefenderGroup )
|
||||
Dispatcher:MessageToPlayers( "Squadron " .. Squadron.Name .. ", " .. DefenderName .. " returning.", "Returning_to_base.wav", 3, "A2G/", DefenderGroup )
|
||||
|
||||
self:GetParent(self).onafterRTB( self, Defender, From, Event, To )
|
||||
self:GetParent(self).onafterRTB( self, DefenderGroup, From, Event, To )
|
||||
|
||||
Dispatcher:ClearDefenderTaskTarget( Defender )
|
||||
Dispatcher:ClearDefenderTaskTarget( DefenderGroup )
|
||||
end
|
||||
|
||||
--- @param #AI_A2G_DISPATCHER self
|
||||
function Fsm:onafterLostControl( Defender, From, Event, To )
|
||||
self:F({"Defender LostControl", Defender:GetName()})
|
||||
self:GetParent(self).onafterHome( self, Defender, From, Event, To )
|
||||
function AI_A2G_Fsm:onafterLostControl( DefenderGroup, From, Event, To )
|
||||
self:F({"Defender LostControl", DefenderGroup:GetName()})
|
||||
self:GetParent(self).onafterHome( self, DefenderGroup, From, Event, To )
|
||||
|
||||
local DefenderName = Defender:GetCallsign()
|
||||
local Dispatcher = Fsm:GetDispatcher() -- #AI_A2G_DISPATCHER
|
||||
local Squadron = Dispatcher:GetSquadronFromDefender( Defender )
|
||||
local DefenderName = DefenderGroup:GetCallsign()
|
||||
local Dispatcher = AI_A2G_Fsm:GetDispatcher() -- #AI_A2G_DISPATCHER
|
||||
local Squadron = Dispatcher:GetSquadronFromDefender( DefenderGroup )
|
||||
--Dispatcher:MessageToPlayers( "Squadron " .. Squadron.Name .. ", " .. DefenderName .. " lost control." )
|
||||
|
||||
if Defender:IsAboveRunway() then
|
||||
Dispatcher:RemoveDefenderFromSquadron( Squadron, Defender )
|
||||
Defender:Destroy()
|
||||
if DefenderGroup:IsAboveRunway() then
|
||||
Dispatcher:RemoveDefenderFromSquadron( Squadron, DefenderGroup )
|
||||
DefenderGroup:Destroy()
|
||||
end
|
||||
end
|
||||
|
||||
--- @param #AI_A2G_DISPATCHER self
|
||||
function Fsm:onafterHome( Defender, From, Event, To, Action )
|
||||
self:F({"Defender Home", Defender:GetName()})
|
||||
self:GetParent(self).onafterHome( self, Defender, From, Event, To )
|
||||
function AI_A2G_Fsm:onafterHome( DefenderGroup, From, Event, To, Action )
|
||||
self:F({"Defender Home", DefenderGroup:GetName()})
|
||||
self:GetParent(self).onafterHome( self, DefenderGroup, From, Event, To )
|
||||
|
||||
local DefenderName = Defender:GetCallsign()
|
||||
local DefenderName = DefenderGroup:GetCallsign()
|
||||
local Dispatcher = self:GetDispatcher() -- #AI_A2G_DISPATCHER
|
||||
local Squadron = Dispatcher:GetSquadronFromDefender( Defender )
|
||||
Dispatcher:MessageToPlayers( "Squadron " .. Squadron.Name .. ", " .. DefenderName .. " landing." )
|
||||
local Squadron = Dispatcher:GetSquadronFromDefender( DefenderGroup )
|
||||
Dispatcher:MessageToPlayers( "Squadron " .. Squadron.Name .. ", " .. DefenderName .. " landing.", "Landing_at_base.wav", 3, "A2G/", DefenderGroup )
|
||||
|
||||
if Action and Action == "Destroy" then
|
||||
Dispatcher:RemoveDefenderFromSquadron( Squadron, Defender )
|
||||
Defender:Destroy()
|
||||
Dispatcher:RemoveDefenderFromSquadron( Squadron, DefenderGroup )
|
||||
DefenderGroup:Destroy()
|
||||
end
|
||||
|
||||
if Dispatcher:GetSquadronLanding( Squadron.Name ) == AI_A2G_DISPATCHER.Landing.NearAirbase then
|
||||
Dispatcher:RemoveDefenderFromSquadron( Squadron, Defender )
|
||||
Defender:Destroy()
|
||||
Dispatcher:ResourcePark( Squadron, Defender )
|
||||
Dispatcher:RemoveDefenderFromSquadron( Squadron, DefenderGroup )
|
||||
DefenderGroup:Destroy()
|
||||
Dispatcher:ResourcePark( Squadron, DefenderGroup )
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -4024,6 +4056,29 @@ do -- AI_A2G_DISPATCHER
|
||||
end
|
||||
|
||||
|
||||
--- Determine the distance as the keys of reference of the detected items.
|
||||
-- @param #AI_A2G_DISPATCHER self
|
||||
function AI_A2G_DISPATCHER:Keys( DetectedItem )
|
||||
|
||||
self:F( { DetectedItem = DetectedItem } )
|
||||
|
||||
local AttackCoordinate = self.Detection:GetDetectedItemCoordinate( DetectedItem )
|
||||
|
||||
local ShortestDistance = 999999999
|
||||
|
||||
for DefenseCoordinateName, DefenseCoordinate in pairs( self.DefenseCoordinates ) do
|
||||
local DefenseCoordinate = DefenseCoordinate -- Core.Point#COORDINATE
|
||||
|
||||
local EvaluateDistance = AttackCoordinate:Get2DDistance( DefenseCoordinate )
|
||||
|
||||
if EvaluateDistance <= ShortestDistance then
|
||||
ShortestDistance = EvaluateDistance
|
||||
end
|
||||
end
|
||||
|
||||
return ShortestDistance
|
||||
end
|
||||
|
||||
--- Assigns A2G AI Tasks in relation to the detected items.
|
||||
-- @param #AI_A2G_DISPATCHER self
|
||||
function AI_A2G_DISPATCHER:Order( DetectedItem )
|
||||
@@ -4043,6 +4098,107 @@ do -- AI_A2G_DISPATCHER
|
||||
|
||||
return ShortestDistance
|
||||
end
|
||||
|
||||
--- Shows the tactical display.
|
||||
-- @param #AI_A2G_DISPATCHER self
|
||||
function AI_A2G_DISPATCHER:ShowTacticalDisplay( Detection )
|
||||
|
||||
local AreaMsg = {}
|
||||
local TaskMsg = {}
|
||||
local ChangeMsg = {}
|
||||
|
||||
local TaskReport = REPORT:New()
|
||||
|
||||
local DefenseTotal = 0
|
||||
|
||||
local Report = REPORT:New( "\nTactical Overview" )
|
||||
|
||||
local DefenderGroupCount = 0
|
||||
local DefendersTotal = 0
|
||||
|
||||
-- Now that all obsolete tasks are removed, loop through the detected targets.
|
||||
--for DetectedItemID, DetectedItem in pairs( Detection:GetDetectedItems() ) do
|
||||
for DetectedItemID, DetectedItem in UTILS.spairs( Detection:GetDetectedItems(), function( t, a, b ) return self:Order(t[a]) < self:Order(t[b]) end ) do
|
||||
|
||||
if not self.Detection:IsDetectedItemLocked( DetectedItem ) == true then
|
||||
local DetectedItem = DetectedItem -- Functional.Detection#DETECTION_BASE.DetectedItem
|
||||
local DetectedSet = DetectedItem.Set -- Core.Set#SET_UNIT
|
||||
local DetectedCount = DetectedSet:Count()
|
||||
local DetectedZone = DetectedItem.Zone
|
||||
|
||||
self:F( { "Target ID", DetectedItem.ItemID } )
|
||||
|
||||
self:F( { DefenseLimit = self.DefenseLimit, DefenseTotal = DefenseTotal } )
|
||||
DetectedSet:Flush( self )
|
||||
|
||||
local DetectedID = DetectedItem.ID
|
||||
local DetectionIndex = DetectedItem.Index
|
||||
local DetectedItemChanged = DetectedItem.Changed
|
||||
|
||||
-- Show tactical situation
|
||||
local ThreatLevel = DetectedItem.Set:CalculateThreatLevelA2G()
|
||||
Report:Add( string.format( " - %1s%s ( %04s ): ( #%02d - %-4s ) %s" , ( DetectedItem.IsDetected == true ) and "!" or " ", DetectedItem.ItemID, DetectedItem.Index, DetectedItem.Set:Count(), DetectedItem.Type or " --- ", string.rep( "■", ThreatLevel ) ) )
|
||||
for Defender, DefenderTask in pairs( self:GetDefenderTasks() ) do
|
||||
local Defender = Defender -- Wrapper.Group#GROUP
|
||||
if DefenderTask.Target and DefenderTask.Target.Index == DetectedItem.Index then
|
||||
if Defender:IsAlive() then
|
||||
DefenderGroupCount = DefenderGroupCount + 1
|
||||
local Fuel = Defender:GetFuelMin() * 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
|
||||
end
|
||||
end
|
||||
|
||||
Report:Add( "\n - No Targets:")
|
||||
local TaskCount = 0
|
||||
for Defender, DefenderTask in pairs( self:GetDefenderTasks() ) do
|
||||
TaskCount = TaskCount + 1
|
||||
local Defender = Defender -- Wrapper.Group#GROUP
|
||||
if not DefenderTask.Target then
|
||||
if Defender:IsAlive() then
|
||||
local DefenderHasTask = Defender:HasTask()
|
||||
local Fuel = Defender:GetFuelMin() * 100
|
||||
local Damage = Defender:GetLife() / Defender:GetLife0() * 100
|
||||
DefenderGroupCount = DefenderGroupCount + 1
|
||||
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
|
||||
Report:Add( string.format( "\n - %d Tasks - %d Defender Groups", TaskCount, DefenderGroupCount ) )
|
||||
|
||||
Report:Add( string.format( "\n - %d Queued Aircraft Launches", #self.DefenseQueue ) )
|
||||
for DefenseQueueID, DefenseQueueItem in pairs( self.DefenseQueue ) do
|
||||
local DefenseQueueItem = DefenseQueueItem -- #AI_A2G_DISPATCHER.DefenseQueueItem
|
||||
Report:Add( string.format( " - %s - %s", DefenseQueueItem.SquadronName, DefenseQueueItem.DefenderSquadron.TakeoffTime, DefenseQueueItem.DefenderSquadron.TakeoffInterval) )
|
||||
|
||||
end
|
||||
|
||||
Report:Add( string.format( "\n - Squadron Resources: ", #self.DefenseQueue ) )
|
||||
for DefenderSquadronName, DefenderSquadron in pairs( self.DefenderSquadrons ) do
|
||||
Report:Add( string.format( " - %s - %d", DefenderSquadronName, DefenderSquadron.ResourceCount and DefenderSquadron.ResourceCount or "n/a" ) )
|
||||
end
|
||||
|
||||
self:F( Report:Text( "\n" ) )
|
||||
trigger.action.outText( Report:Text( "\n" ), 25 )
|
||||
|
||||
end
|
||||
|
||||
--- Assigns A2G AI Tasks in relation to the detected items.
|
||||
-- @param #AI_A2G_DISPATCHER self
|
||||
@@ -4102,7 +4258,7 @@ do -- AI_A2G_DISPATCHER
|
||||
|
||||
-- Now that all obsolete tasks are removed, loop through the detected targets.
|
||||
--for DetectedItemID, DetectedItem in pairs( Detection:GetDetectedItems() ) do
|
||||
for DetectedItemID, DetectedItem in UTILS.spairs( Detection:GetDetectedItems(), function( t, a, b ) return self:Order(t[a]) < self:Order(t[b]) end ) do
|
||||
for DetectedDistance, DetectedItem in UTILS.kpairs( Detection:GetDetectedItems(), function( t ) return self:Keys( t ) end, function( t, a, b ) return self:Order(t[a]) < self:Order(t[b]) end ) do
|
||||
|
||||
if not self.Detection:IsDetectedItemLocked( DetectedItem ) == true then
|
||||
local DetectedItem = DetectedItem -- Functional.Detection#DETECTION_BASE.DetectedItem
|
||||
@@ -4124,44 +4280,43 @@ do -- AI_A2G_DISPATCHER
|
||||
-- Calculate if for this DetectedItem if a defense needs to be initiated.
|
||||
-- This calculation is based on the distance between the defense point and the attackers, and the defensiveness parameter.
|
||||
-- The attackers closest to the defense coordinates will be handled first, or course!
|
||||
|
||||
local EngageCoordinate = nil
|
||||
|
||||
for DefenseCoordinateName, DefenseCoordinate in pairs( self.DefenseCoordinates ) do
|
||||
local DefenseCoordinate = DefenseCoordinate -- Core.Point#COORDINATE
|
||||
|
||||
local EvaluateDistance = AttackCoordinate:Get2DDistance( DefenseCoordinate )
|
||||
|
||||
local EngageDefenses = nil
|
||||
|
||||
if EvaluateDistance <= self.DefenseRadius then
|
||||
|
||||
local DistanceProbability = ( self.DefenseRadius / EvaluateDistance * self.DefenseReactivity )
|
||||
self:F( { DetectedDistance = DetectedDistance, DefenseRadius = self.DefenseRadius } )
|
||||
if DetectedDistance <= self.DefenseRadius then
|
||||
|
||||
self:F( { DetectedApproach = self._DefenseApproach } )
|
||||
if self._DefenseApproach == AI_A2G_DISPATCHER.DefenseApproach.Distance then
|
||||
EngageDefenses = true
|
||||
self:F( { EngageDefenses = EngageDefenses } )
|
||||
end
|
||||
|
||||
if self._DefenseApproach == AI_A2G_DISPATCHER.DefenseApproach.Random then
|
||||
local DistanceProbability = ( self.DefenseRadius / DetectedDistance * self.DefenseReactivity )
|
||||
local DefenseProbability = math.random()
|
||||
|
||||
|
||||
self:F( { DistanceProbability = DistanceProbability, DefenseProbability = DefenseProbability } )
|
||||
|
||||
if self._DefenseApproach == AI_A2G_DISPATCHER.DefenseApproach.Random then
|
||||
|
||||
if DefenseProbability <= DistanceProbability / ( 300 / 30 ) then
|
||||
EngageCoordinate = DefenseCoordinate
|
||||
break
|
||||
end
|
||||
end
|
||||
if self._DefenseApproach == AI_A2G_DISPATCHER.DefenseApproach.Distance then
|
||||
EngageCoordinate = DefenseCoordinate
|
||||
break
|
||||
|
||||
if DefenseProbability <= DistanceProbability / ( 300 / 30 ) then
|
||||
EngageDefenses = true
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
self:F( { EngageDefenses = EngageDefenses, DefenseLimit = self.DefenseLimit, DefenseTotal = DefenseTotal } )
|
||||
|
||||
-- There needs to be an EngageCoordinate.
|
||||
-- If self.DefenseLimit is set (thus limit the amount of defenses to one zone), then only start a new defense if the maximum has not been reached.
|
||||
-- If self.DefenseLimit has not been set, there is an unlimited amount of zones to be defended.
|
||||
if ( EngageCoordinate and ( self.DefenseLimit and DefenseTotal < self.DefenseLimit ) or not self.DefenseLimit ) then
|
||||
if ( EngageDefenses and ( self.DefenseLimit and DefenseTotal < self.DefenseLimit ) or not self.DefenseLimit ) then
|
||||
do
|
||||
local DefendersTotal, DefendersEngaged, DefendersMissing, Friendlies = self:Evaluate_SEAD( DetectedItem ) -- Returns a SET_UNIT with the SEAD targets to be engaged...
|
||||
if DefendersMissing > 0 then
|
||||
self:F( { DefendersTotal = DefendersTotal, DefendersEngaged = DefendersEngaged, DefendersMissing = DefendersMissing } )
|
||||
self:Defend( DetectedItem, DefendersTotal, DefendersEngaged, DefendersMissing, Friendlies, "SEAD", EngageCoordinate )
|
||||
self:Defend( DetectedItem, DefendersTotal, DefendersEngaged, DefendersMissing, Friendlies, "SEAD" )
|
||||
end
|
||||
end
|
||||
|
||||
@@ -4169,7 +4324,7 @@ do -- AI_A2G_DISPATCHER
|
||||
local DefendersTotal, DefendersEngaged, DefendersMissing, Friendlies = self:Evaluate_CAS( DetectedItem ) -- Returns a SET_UNIT with the CAS targets to be engaged...
|
||||
if DefendersMissing > 0 then
|
||||
self:F( { DefendersTotal = DefendersTotal, DefendersEngaged = DefendersEngaged, DefendersMissing = DefendersMissing } )
|
||||
self:Defend( DetectedItem, DefendersTotal, DefendersEngaged, DefendersMissing, Friendlies, "CAS", EngageCoordinate )
|
||||
self:Defend( DetectedItem, DefendersTotal, DefendersEngaged, DefendersMissing, Friendlies, "CAS" )
|
||||
end
|
||||
end
|
||||
|
||||
@@ -4177,7 +4332,7 @@ do -- AI_A2G_DISPATCHER
|
||||
local DefendersTotal, DefendersEngaged, DefendersMissing, Friendlies = self:Evaluate_BAI( DetectedItem ) -- Returns a SET_UNIT with the CAS targets to be engaged...
|
||||
if DefendersMissing > 0 then
|
||||
self:F( { DefendersTotal = DefendersTotal, DefendersEngaged = DefendersEngaged, DefendersMissing = DefendersMissing } )
|
||||
self:Defend( DetectedItem, DefendersTotal, DefendersEngaged, DefendersMissing, Friendlies, "BAI", EngageCoordinate )
|
||||
self:Defend( DetectedItem, DefendersTotal, DefendersEngaged, DefendersMissing, Friendlies, "BAI" )
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -4191,7 +4346,7 @@ do -- AI_A2G_DISPATCHER
|
||||
|
||||
for DefenseQueueID, DefenseQueueItem in pairs( self.DefenseQueue ) do
|
||||
local DefenseQueueItem = DefenseQueueItem -- #AI_A2G_DISPATCHER.DefenseQueueItem
|
||||
if DefenseQueueItem.AttackerDetection.Index == DetectedItem.Index then
|
||||
if DefenseQueueItem.AttackerDetection and DefenseQueueItem.AttackerDetection.Index and DefenseQueueItem.AttackerDetection.Index == DetectedItem.Index then
|
||||
DefenseTotal = DefenseTotal + 1
|
||||
end
|
||||
end
|
||||
|
||||
@@ -304,6 +304,8 @@ function AI_A2G_ENGAGE:New( AIGroup, EngageMinSpeed, EngageMaxSpeed, EngageFloor
|
||||
-- @param #AI_A2G_ENGAGE self
|
||||
-- @param #number Delay The delay in seconds.
|
||||
|
||||
self:AddTransition( { "Patrolling", "Engaging" }, "Refuel", "Refuelling" )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -389,17 +391,6 @@ function AI_A2G_ENGAGE:onafterAbort( AIGroup, From, Event, To )
|
||||
end
|
||||
|
||||
|
||||
--- @param #AI_A2G_ENGAGE self
|
||||
-- @param Wrapper.Group#GROUP DefenderGroup The GroupGroup managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
function AI_A2G_ENGAGE:onafterEngageRoute( DefenderGroup, From, Event, To, AttackSetUnit )
|
||||
|
||||
self:F( { DefenderGroup, From, Event, To, AttackSetUnit} )
|
||||
|
||||
end
|
||||
|
||||
--- @param #AI_A2G_ENGAGE self
|
||||
-- @param Wrapper.Group#GROUP AIGroup The Group Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
@@ -446,6 +437,8 @@ function AI_A2G_ENGAGE:onafterEngageRoute( DefenderGroup, From, Event, To, Attac
|
||||
|
||||
local DefenderGroupName = DefenderGroup:GetName()
|
||||
|
||||
self.AttackSetUnit = AttackSetUnit -- Kept in memory in case of resume from refuel in air!
|
||||
|
||||
local AttackCount = AttackSetUnit:Count()
|
||||
|
||||
if AttackCount > 0 then
|
||||
@@ -513,8 +506,9 @@ function AI_A2G_ENGAGE:onafterEngageRoute( DefenderGroup, From, Event, To, Attac
|
||||
|
||||
end
|
||||
else
|
||||
self:E( DefenderGroupName .. ": No targets found -> Going RTB")
|
||||
self:I( DefenderGroupName .. ": No targets found -> Going RTB")
|
||||
self:Return()
|
||||
self:__RTB( self.TaskDelay )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -286,9 +286,18 @@ function AI_A2G_PATROL:onafterPatrolRoute( AIPatrol, From, Event, To )
|
||||
self:SetTargetDistance( ToTargetCoord ) -- For RTB status check
|
||||
|
||||
local ToTargetSpeed = math.random( self.PatrolMinSpeed, self.PatrolMaxSpeed )
|
||||
|
||||
local FromWP = CurrentCoord:WaypointAir(
|
||||
self.PatrolAltType or "RADIO",
|
||||
POINT_VEC3.RoutePointType.TurningPoint,
|
||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
||||
ToTargetSpeed,
|
||||
true
|
||||
)
|
||||
PatrolRoute[#PatrolRoute+1] = FromWP
|
||||
|
||||
--- Create a route point of type air.
|
||||
local ToPatrolRoutePoint = ToTargetCoord:WaypointAir(
|
||||
local ToWP = ToTargetCoord:WaypointAir(
|
||||
self.PatrolAltType,
|
||||
POINT_VEC3.RoutePointType.TurningPoint,
|
||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
||||
@@ -296,8 +305,7 @@ function AI_A2G_PATROL:onafterPatrolRoute( AIPatrol, From, Event, To )
|
||||
true
|
||||
)
|
||||
|
||||
PatrolRoute[#PatrolRoute+1] = ToPatrolRoutePoint
|
||||
PatrolRoute[#PatrolRoute+1] = ToPatrolRoutePoint
|
||||
PatrolRoute[#PatrolRoute+1] = ToWP
|
||||
|
||||
local Tasks = {}
|
||||
Tasks[#Tasks+1] = AIPatrol:TaskFunction( "AI_A2G_PATROL.___PatrolRoute", self )
|
||||
@@ -316,8 +324,8 @@ function AI_A2G_PATROL.Resume( AIPatrol, Fsm )
|
||||
|
||||
AIPatrol:F( { "AI_A2G_PATROL.Resume:", AIPatrol:GetName() } )
|
||||
if AIPatrol:IsAlive() then
|
||||
Fsm:__Reset( self.TaskDelay )
|
||||
Fsm:__PatrolRoute( self.TaskDelay )
|
||||
Fsm:__Reset( Fsm.TaskDelay )
|
||||
Fsm:__PatrolRoute( Fsm.TaskDelay )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -117,6 +117,8 @@ function AI_A2G_SEAD:onafterEngage( DefenderGroup, From, Event, To, AttackSetUni
|
||||
|
||||
local DefenderGroupName = DefenderGroup:GetName()
|
||||
|
||||
self.AttackSetUnit = AttackSetUnit -- Kept in memory in case of resume from refuel in air!
|
||||
|
||||
local AttackCount = AttackSetUnit:Count()
|
||||
|
||||
if AttackCount > 0 then
|
||||
@@ -181,7 +183,7 @@ function AI_A2G_SEAD:onafterEngage( DefenderGroup, From, Event, To, AttackSetUni
|
||||
end
|
||||
|
||||
if #AttackUnitTasks == 0 then
|
||||
self:E( DefenderGroupName .. ": No targets found -> Going RTB")
|
||||
self:I( DefenderGroupName .. ": No targets found -> Going RTB")
|
||||
self:Return()
|
||||
self:__RTB( self.TaskDelay )
|
||||
else
|
||||
@@ -200,9 +202,19 @@ function AI_A2G_SEAD:onafterEngage( DefenderGroup, From, Event, To, AttackSetUni
|
||||
DefenderGroup:Route( EngageRoute, self.TaskDelay )
|
||||
end
|
||||
else
|
||||
self:E( DefenderGroupName .. ": No targets found -> Going RTB")
|
||||
self:I( DefenderGroupName .. ": No targets found -> Going RTB")
|
||||
self:Return()
|
||||
self:__RTB( self.TaskDelay )
|
||||
end
|
||||
end
|
||||
|
||||
--- @param Wrapper.Group#GROUP AIEngage
|
||||
function AI_A2G_SEAD.Resume( AIEngage, Fsm )
|
||||
|
||||
AIEngage:F( { "AI_A2G_SEAD.Resume:", AIEngage:GetName() } )
|
||||
if AIEngage:IsAlive() then
|
||||
Fsm:__Reset( Fsm.TaskDelay )
|
||||
Fsm:__EngageRoute( Fsm.TaskDelay, Fsm.AttackSetUnit )
|
||||
end
|
||||
|
||||
end
|
||||
@@ -446,7 +446,7 @@ function AI_AIR:onafterStatus()
|
||||
local DistanceFromHomeBase = self.HomeAirbase:GetCoordinate():Get2DDistance( self.Controllable:GetCoordinate() )
|
||||
|
||||
if DistanceFromHomeBase > self.DisengageRadius then
|
||||
self:E( self.Controllable:GetName() .. " is too far from home base, RTB!" )
|
||||
self:I( self.Controllable:GetName() .. " is too far from home base, RTB!" )
|
||||
self:Hold( 300 )
|
||||
RTB = false
|
||||
end
|
||||
@@ -461,7 +461,7 @@ function AI_AIR:onafterStatus()
|
||||
-- end
|
||||
|
||||
|
||||
if not self:Is( "Fuel" ) and not self:Is( "Home" ) then
|
||||
if not self:Is( "Fuel" ) and not self:Is( "Home" ) and not self:is( "Refuelling" )then
|
||||
|
||||
local Fuel = self.Controllable:GetFuelMin()
|
||||
|
||||
@@ -470,10 +470,10 @@ function AI_AIR:onafterStatus()
|
||||
if Fuel < self.FuelThresholdPercentage then
|
||||
|
||||
if self.TankerName then
|
||||
self:E( self.Controllable:GetName() .. " is out of fuel: " .. Fuel .. " ... Refuelling at Tanker!" )
|
||||
self:I( self.Controllable:GetName() .. " is out of fuel: " .. Fuel .. " ... Refuelling at Tanker!" )
|
||||
self:Refuel()
|
||||
else
|
||||
self:E( self.Controllable:GetName() .. " is out of fuel: " .. Fuel .. " ... RTB!" )
|
||||
self:I( self.Controllable:GetName() .. " is out of fuel: " .. Fuel .. " ... RTB!" )
|
||||
local OldAIControllable = self.Controllable
|
||||
|
||||
local OrbitTask = OldAIControllable:TaskOrbitCircle( math.random( self.PatrolFloorAltitude, self.PatrolCeilingAltitude ), self.PatrolMinSpeed )
|
||||
@@ -495,7 +495,7 @@ function AI_AIR:onafterStatus()
|
||||
-- Note that a group can consist of more units, so if one unit is damaged of a group, the mission may continue.
|
||||
-- The damaged unit will RTB due to DCS logic, and the others will continue to engage.
|
||||
if ( Damage / InitialLife ) < self.PatrolDamageThreshold then
|
||||
self:E( self.Controllable:GetName() .. " is damaged: " .. Damage .. " ... RTB!" )
|
||||
self:I( self.Controllable:GetName() .. " is damaged: " .. Damage .. " ... RTB!" )
|
||||
self:Damaged()
|
||||
RTB = true
|
||||
self:SetStatusOff()
|
||||
@@ -513,7 +513,7 @@ function AI_AIR:onafterStatus()
|
||||
if Damage ~= InitialLife then
|
||||
self:Damaged()
|
||||
else
|
||||
self:E( self.Controllable:GetName() .. " control lost! " )
|
||||
self:I( self.Controllable:GetName() .. " control lost! " )
|
||||
|
||||
self:LostControl()
|
||||
end
|
||||
@@ -570,7 +570,7 @@ function AI_AIR:onafterRTB( AIGroup, From, Event, To )
|
||||
|
||||
if AIGroup and AIGroup:IsAlive() then
|
||||
|
||||
self:E( "Group " .. AIGroup:GetName() .. " ... RTB! ( " .. self:GetState() .. " )" )
|
||||
self:I( "Group " .. AIGroup:GetName() .. " ... RTB! ( " .. self:GetState() .. " )" )
|
||||
|
||||
self:ClearTargetDistance()
|
||||
--AIGroup:ClearTasks()
|
||||
@@ -588,7 +588,7 @@ function AI_AIR:onafterRTB( AIGroup, From, Event, To )
|
||||
|
||||
local ToAirbaseCoord = FromCoord:Translate( 5000, ToAirbaseAngle )
|
||||
if Distance < 5000 then
|
||||
self:E( "RTB and near the airbase!" )
|
||||
self:I( "RTB and near the airbase!" )
|
||||
self:Home()
|
||||
return
|
||||
end
|
||||
@@ -634,7 +634,7 @@ end
|
||||
function AI_AIR:onafterHome( AIGroup, From, Event, To )
|
||||
self:F( { AIGroup, From, Event, To } )
|
||||
|
||||
self:E( "Group " .. self.Controllable:GetName() .. " ... Home! ( " .. self:GetState() .. " )" )
|
||||
self:I( "Group " .. self.Controllable:GetName() .. " ... Home! ( " .. self:GetState() .. " )" )
|
||||
|
||||
if AIGroup and AIGroup:IsAlive() then
|
||||
end
|
||||
@@ -648,7 +648,7 @@ end
|
||||
function AI_AIR:onafterHold( AIGroup, From, Event, To, HoldTime )
|
||||
self:F( { AIGroup, From, Event, To } )
|
||||
|
||||
self:E( "Group " .. self.Controllable:GetName() .. " ... Holding! ( " .. self:GetState() .. " )" )
|
||||
self:I( "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 )
|
||||
@@ -680,12 +680,14 @@ end
|
||||
function AI_AIR:onafterRefuel( AIGroup, From, Event, To )
|
||||
self:F( { AIGroup, From, Event, To } )
|
||||
|
||||
self:E( "Group " .. self.Controllable:GetName() .. " ... Refuelling! ( " .. self:GetState() .. " )" )
|
||||
|
||||
if AIGroup and AIGroup:IsAlive() then
|
||||
local Tanker = GROUP:FindByName( self.TankerName )
|
||||
|
||||
if Tanker:IsAlive() and Tanker:IsAirPlane() then
|
||||
|
||||
self:I( "Group " .. self.Controllable:GetName() .. " ... Refuelling! ( " .. self:GetState() .. "), at tanker " .. self.TankerName )
|
||||
|
||||
local RefuelRoute = {}
|
||||
|
||||
--- Calculate the target route point.
|
||||
|
||||
@@ -3289,6 +3289,9 @@ do
|
||||
self:Patrol( SquadronName, PatrolTaskType )
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
||||
|
||||
@@ -545,7 +545,7 @@ function AI_ESCORT:SetFlightMenuFormation( Formation )
|
||||
|
||||
if MenuFormation then
|
||||
local Arguments = MenuFormation.Arguments
|
||||
self:I({Arguments=unpack(Arguments)})
|
||||
--self:I({Arguments=unpack(Arguments)})
|
||||
local FlightMenuFormation = MENU_GROUP:New( self.PlayerGroup, "Formation", self.MainMenu )
|
||||
local MenuFlightFormationID = MENU_GROUP_COMMAND:New( self.PlayerGroup, Formation, FlightMenuFormation,
|
||||
function ( self, Formation, ... )
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
-- ## Features:
|
||||
-- --
|
||||
-- * Provides the facilities to trigger escorts when players join flight slots.
|
||||
-- *
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
|
||||
@@ -83,13 +83,6 @@ function AI_ESCORT_DISPATCHER_REQUEST:OnEventExit( EventData )
|
||||
local PlayerGroup = EventData.IniGroup
|
||||
local PlayerUnit = EventData.IniUnit
|
||||
|
||||
self.CarrierSet:Flush(self)
|
||||
self:I({EscortAirbase= self.EscortAirbase } )
|
||||
self:I({PlayerGroupName = PlayerGroupName } )
|
||||
self:I({PlayerGroup = PlayerGroup})
|
||||
self:I({FirstGroup = self.CarrierSet:GetFirst()})
|
||||
self:I({FindGroup = self.CarrierSet:FindGroup( PlayerGroupName )})
|
||||
|
||||
if self.CarrierSet:FindGroup( PlayerGroupName ) then
|
||||
if self.AI_Escorts[PlayerGroupName] then
|
||||
self.AI_Escorts[PlayerGroupName]:Stop()
|
||||
@@ -107,11 +100,6 @@ function AI_ESCORT_DISPATCHER_REQUEST:OnEventBirth( EventData )
|
||||
local PlayerGroup = EventData.IniGroup
|
||||
local PlayerUnit = EventData.IniUnit
|
||||
|
||||
self:I({PlayerGroupName = PlayerGroupName } )
|
||||
self:I({PlayerGroup = PlayerGroup})
|
||||
self:I({FirstGroup = self.CarrierSet:GetFirst()})
|
||||
self:I({FindGroup = self.CarrierSet:FindGroup( PlayerGroupName )})
|
||||
|
||||
if self.CarrierSet:FindGroup( PlayerGroupName ) then
|
||||
if not self.AI_Escorts[PlayerGroupName] then
|
||||
local LeaderUnit = PlayerUnit
|
||||
|
||||
@@ -825,7 +825,7 @@ function AI_PATROL_ZONE:onafterStatus()
|
||||
|
||||
local Fuel = self.Controllable:GetFuelMin()
|
||||
if Fuel < self.PatrolFuelThresholdPercentage then
|
||||
self:E( self.Controllable:GetName() .. " is out of fuel:" .. Fuel .. ", RTB!" )
|
||||
self:I( self.Controllable:GetName() .. " is out of fuel:" .. Fuel .. ", RTB!" )
|
||||
local OldAIControllable = self.Controllable
|
||||
|
||||
local OrbitTask = OldAIControllable:TaskOrbitCircle( math.random( self.PatrolFloorAltitude, self.PatrolCeilingAltitude ), self.PatrolMinSpeed )
|
||||
@@ -839,7 +839,7 @@ function AI_PATROL_ZONE:onafterStatus()
|
||||
-- TODO: Check GROUP damage function.
|
||||
local Damage = self.Controllable:GetLife()
|
||||
if Damage <= self.PatrolDamageThreshold then
|
||||
self:E( self.Controllable:GetName() .. " is damaged:" .. Damage .. ", RTB!" )
|
||||
self:I( self.Controllable:GetName() .. " is damaged:" .. Damage .. ", RTB!" )
|
||||
RTB = true
|
||||
end
|
||||
|
||||
@@ -900,7 +900,6 @@ end
|
||||
function AI_PATROL_ZONE:OnCrash( EventData )
|
||||
|
||||
if self.Controllable:IsAlive() and EventData.IniDCSGroupName == self.Controllable:GetName() then
|
||||
self:E( self.Controllable:GetUnits() )
|
||||
if #self.Controllable:GetUnits() == 1 then
|
||||
self:__Crash( 1, EventData )
|
||||
end
|
||||
|
||||
@@ -805,15 +805,17 @@ do -- Scheduling
|
||||
end
|
||||
|
||||
self.Scheduler.SchedulerObject = self.Scheduler
|
||||
--self.MasterObject = self
|
||||
|
||||
local ScheduleID = _SCHEDULEDISPATCHER:AddSchedule(
|
||||
local ScheduleID = self.Scheduler:Schedule(
|
||||
self,
|
||||
SchedulerFunction,
|
||||
{ ... },
|
||||
Start,
|
||||
Repeat,
|
||||
RandomizeFactor,
|
||||
Stop
|
||||
Stop,
|
||||
4
|
||||
)
|
||||
|
||||
self._.Schedules[#self._.Schedules+1] = ScheduleID
|
||||
@@ -889,6 +891,26 @@ end
|
||||
-- Log a trace (only shown when trace is on)
|
||||
-- TODO: Make trace function using variable parameters.
|
||||
|
||||
--- Set trace on.
|
||||
-- @param #BASE self
|
||||
-- @usage
|
||||
-- -- Switch the tracing On
|
||||
-- BASE:TraceOn()
|
||||
function BASE:TraceOn()
|
||||
self:TraceOnOff( true )
|
||||
end
|
||||
|
||||
--- Set trace off.
|
||||
-- @param #BASE self
|
||||
-- @usage
|
||||
-- -- Switch the tracing Off
|
||||
-- BASE:TraceOff()
|
||||
function BASE:TraceOff()
|
||||
self:TraceOnOff( false )
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- Set trace on or off
|
||||
-- Note that when trace is off, no BASE.Debug statement is performed, increasing performance!
|
||||
-- When Moose is loaded statically, (as one file), tracing is switched off by default.
|
||||
@@ -903,7 +925,7 @@ end
|
||||
-- -- Switch the tracing Off
|
||||
-- BASE:TraceOnOff( false )
|
||||
function BASE:TraceOnOff( TraceOnOff )
|
||||
_TraceOnOff = TraceOnOff
|
||||
_TraceOnOff = TraceOnOff or true
|
||||
end
|
||||
|
||||
|
||||
@@ -923,8 +945,8 @@ end
|
||||
-- @param #BASE self
|
||||
-- @param #number Level
|
||||
function BASE:TraceLevel( Level )
|
||||
_TraceLevel = Level
|
||||
self:E( "Tracing level " .. Level )
|
||||
_TraceLevel = Level or 1
|
||||
self:I( "Tracing level " .. _TraceLevel )
|
||||
end
|
||||
|
||||
--- Trace all methods in MOOSE
|
||||
@@ -932,12 +954,12 @@ end
|
||||
-- @param #boolean TraceAll true = trace all methods in MOOSE.
|
||||
function BASE:TraceAll( TraceAll )
|
||||
|
||||
_TraceAll = TraceAll
|
||||
_TraceAll = TraceAll or true
|
||||
|
||||
if _TraceAll then
|
||||
self:E( "Tracing all methods in MOOSE " )
|
||||
self:I( "Tracing all methods in MOOSE " )
|
||||
else
|
||||
self:E( "Switched off tracing all methods in MOOSE" )
|
||||
self:I( "Switched off tracing all methods in MOOSE" )
|
||||
end
|
||||
end
|
||||
|
||||
@@ -947,7 +969,7 @@ end
|
||||
function BASE:TraceClass( Class )
|
||||
_TraceClass[Class] = true
|
||||
_TraceClassMethod[Class] = {}
|
||||
self:E( "Tracing class " .. Class )
|
||||
self:I( "Tracing class " .. Class )
|
||||
end
|
||||
|
||||
--- Set tracing for a specific method of class
|
||||
@@ -960,7 +982,7 @@ function BASE:TraceClassMethod( Class, Method )
|
||||
_TraceClassMethod[Class].Method = {}
|
||||
end
|
||||
_TraceClassMethod[Class].Method[Method] = true
|
||||
self:E( "Tracing method " .. Method .. " of class " .. Class )
|
||||
self:I( "Tracing method " .. Method .. " of class " .. Class )
|
||||
end
|
||||
|
||||
--- Trace a function call. This function is private.
|
||||
@@ -987,7 +1009,7 @@ function BASE:_F( Arguments, DebugInfoCurrentParam, DebugInfoFromParam )
|
||||
if DebugInfoFrom then
|
||||
LineFrom = DebugInfoFrom.currentline
|
||||
end
|
||||
env.info( string.format( "%6d(%6d)/%1s:%20s%05d.%s(%s)" , LineCurrent, LineFrom, "F", self.ClassName, self.ClassID, Function, routines.utils.oneLineSerialize( Arguments ) ) )
|
||||
env.info( string.format( "%6d(%6d)/%1s:%30s%05d.%s(%s)" , LineCurrent, LineFrom, "F", self.ClassName, self.ClassID, Function, routines.utils.oneLineSerialize( Arguments ) ) )
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1062,7 +1084,7 @@ function BASE:_T( Arguments, DebugInfoCurrentParam, DebugInfoFromParam )
|
||||
if DebugInfoFrom then
|
||||
LineFrom = DebugInfoFrom.currentline
|
||||
end
|
||||
env.info( string.format( "%6d(%6d)/%1s:%20s%05d.%s" , LineCurrent, LineFrom, "T", self.ClassName, self.ClassID, routines.utils.oneLineSerialize( Arguments ) ) )
|
||||
env.info( string.format( "%6d(%6d)/%1s:%30s%05d.%s" , LineCurrent, LineFrom, "T", self.ClassName, self.ClassID, routines.utils.oneLineSerialize( Arguments ) ) )
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1133,9 +1155,9 @@ function BASE:E( Arguments )
|
||||
LineFrom = DebugInfoFrom.currentline
|
||||
end
|
||||
|
||||
env.info( string.format( "%6d(%6d)/%1s:%20s%05d.%s(%s)" , LineCurrent, LineFrom, "E", self.ClassName, self.ClassID, Function, routines.utils.oneLineSerialize( Arguments ) ) )
|
||||
env.info( string.format( "%6d(%6d)/%1s:%30s%05d.%s(%s)" , LineCurrent, LineFrom, "E", self.ClassName, self.ClassID, Function, routines.utils.oneLineSerialize( Arguments ) ) )
|
||||
else
|
||||
env.info( string.format( "%1s:%20s%05d(%s)" , "E", self.ClassName, self.ClassID, routines.utils.oneLineSerialize( Arguments ) ) )
|
||||
env.info( string.format( "%1s:%30s%05d(%s)" , "E", self.ClassName, self.ClassID, routines.utils.oneLineSerialize( Arguments ) ) )
|
||||
end
|
||||
|
||||
end
|
||||
@@ -1161,9 +1183,9 @@ function BASE:I( Arguments )
|
||||
LineFrom = DebugInfoFrom.currentline
|
||||
end
|
||||
|
||||
env.info( string.format( "%6d(%6d)/%1s:%20s%05d.%s(%s)" , LineCurrent, LineFrom, "I", self.ClassName, self.ClassID, Function, routines.utils.oneLineSerialize( Arguments ) ) )
|
||||
env.info( string.format( "%6d(%6d)/%1s:%30s%05d.%s(%s)" , LineCurrent, LineFrom, "I", self.ClassName, self.ClassID, Function, routines.utils.oneLineSerialize( Arguments ) ) )
|
||||
else
|
||||
env.info( string.format( "%1s:%20s%05d(%s)" , "I", self.ClassName, self.ClassID, routines.utils.oneLineSerialize( Arguments ) ) )
|
||||
env.info( string.format( "%1s:%30s%05d(%s)" , "I", self.ClassName, self.ClassID, routines.utils.oneLineSerialize( Arguments ) ) )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
--- **Core** - Manages several databases containing templates, mission objects, and mission information.
|
||||
--
|
||||
--- **Core** - Manages several databases containing templates, mission objects, and mission information.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
--
|
||||
-- ## Features:
|
||||
--
|
||||
--
|
||||
-- * During mission startup, scan the mission environment, and create / instantiate intelligently the different objects as defined within the mission.
|
||||
-- * Manage database of DCS Group templates (as modelled using the mission editor).
|
||||
-- - Group templates.
|
||||
@@ -20,14 +20,14 @@
|
||||
-- * Manage database of hits to units and statics.
|
||||
-- * Manage database of destroys of units and statics.
|
||||
-- * Manage database of @{Core.Zone#ZONE_BASE} objects.
|
||||
--
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
-- ### Contributions:
|
||||
--
|
||||
-- ### Contributions:
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
--
|
||||
-- @module Core.Database
|
||||
-- @image Core_Database.JPG
|
||||
|
||||
@@ -36,9 +36,9 @@
|
||||
-- @extends Core.Base#BASE
|
||||
|
||||
--- Contains collections of wrapper objects defined within MOOSE that reflect objects within the simulator.
|
||||
--
|
||||
--
|
||||
-- Mission designers can use the DATABASE class to refer to:
|
||||
--
|
||||
--
|
||||
-- * STATICS
|
||||
-- * UNITS
|
||||
-- * GROUPS
|
||||
@@ -47,12 +47,12 @@
|
||||
-- * PLAYERSJOINED
|
||||
-- * PLAYERS
|
||||
-- * CARGOS
|
||||
--
|
||||
--
|
||||
-- On top, for internal MOOSE administration purposes, the DATBASE administers the Unit and Group TEMPLATES as defined within the Mission Editor.
|
||||
--
|
||||
--
|
||||
-- The singleton object **_DATABASE** is automatically created by MOOSE, that administers all objects within the mission.
|
||||
-- Moose refers to **_DATABASE** within the framework extensively, but you can also refer to the _DATABASE object within your missions if required.
|
||||
--
|
||||
--
|
||||
-- @field #DATABASE
|
||||
DATABASE = {
|
||||
ClassName = "DATABASE",
|
||||
@@ -81,6 +81,7 @@ DATABASE = {
|
||||
HITS = {},
|
||||
DESTROYS = {},
|
||||
ZONES = {},
|
||||
ZONES_GOAL = {},
|
||||
WAREHOUSES = {},
|
||||
FLIGHTGROUPS = {},
|
||||
FLIGHTCONTROLS = {},
|
||||
@@ -115,7 +116,7 @@ function DATABASE:New()
|
||||
local self = BASE:Inherit( self, BASE:New() ) -- #DATABASE
|
||||
|
||||
self:SetEventPriority( 1 )
|
||||
|
||||
|
||||
self:HandleEvent( EVENTS.Birth, self._EventOnBirth )
|
||||
self:HandleEvent( EVENTS.Dead, self._EventOnDeadOrCrash )
|
||||
self:HandleEvent( EVENTS.Crash, self._EventOnDeadOrCrash )
|
||||
@@ -125,11 +126,11 @@ function DATABASE:New()
|
||||
self:HandleEvent( EVENTS.DeleteCargo )
|
||||
self:HandleEvent( EVENTS.NewZone )
|
||||
self:HandleEvent( EVENTS.DeleteZone )
|
||||
|
||||
|
||||
-- Follow alive players and clients
|
||||
--self:HandleEvent( EVENTS.PlayerEnterUnit, self._EventOnPlayerEnterUnit ) -- This is not working anymore!, handling this through the birth event.
|
||||
self:HandleEvent( EVENTS.PlayerLeaveUnit, self._EventOnPlayerLeaveUnit )
|
||||
|
||||
|
||||
self:_RegisterTemplates()
|
||||
self:_RegisterGroupsAndUnits()
|
||||
self:_RegisterClients()
|
||||
@@ -138,16 +139,16 @@ function DATABASE:New()
|
||||
self:_RegisterAirbases()
|
||||
|
||||
self.UNITS_Position = 0
|
||||
|
||||
|
||||
--- @param #DATABASE self
|
||||
local function CheckPlayers( self )
|
||||
|
||||
|
||||
local CoalitionsData = { AlivePlayersRed = coalition.getPlayers( coalition.side.RED ), AlivePlayersBlue = coalition.getPlayers( coalition.side.BLUE ), AlivePlayersNeutral = coalition.getPlayers( coalition.side.NEUTRAL )}
|
||||
for CoalitionId, CoalitionData in pairs( CoalitionsData ) do
|
||||
--self:E( { "CoalitionData:", CoalitionData } )
|
||||
for UnitId, UnitData in pairs( CoalitionData ) do
|
||||
if UnitData and UnitData:isExist() then
|
||||
|
||||
|
||||
local UnitName = UnitData:getName()
|
||||
local PlayerName = UnitData:getPlayerName()
|
||||
local PlayerUnit = UNIT:Find( UnitData )
|
||||
@@ -166,10 +167,10 @@ function DATABASE:New()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--self:E( "Scheduling" )
|
||||
--PlayerCheckSchedule = SCHEDULER:New( nil, CheckPlayers, { self }, 1, 1 )
|
||||
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -189,12 +190,13 @@ end
|
||||
function DATABASE:AddUnit( DCSUnitName )
|
||||
|
||||
if not self.UNITS[DCSUnitName] then
|
||||
self:I( { "Add UNIT:", DCSUnitName } )
|
||||
local UnitRegister = UNIT:Register( DCSUnitName )
|
||||
self.UNITS[DCSUnitName] = UNIT:Register( DCSUnitName )
|
||||
|
||||
|
||||
table.insert( self.UNITS_Index, DCSUnitName )
|
||||
end
|
||||
|
||||
|
||||
return self.UNITS[DCSUnitName]
|
||||
end
|
||||
|
||||
@@ -203,7 +205,7 @@ end
|
||||
-- @param #DATABASE self
|
||||
function DATABASE:DeleteUnit( DCSUnitName )
|
||||
|
||||
self.UNITS[DCSUnitName] = nil
|
||||
self.UNITS[DCSUnitName] = nil
|
||||
end
|
||||
|
||||
--- Adds a Static based on the Static Name in the DATABASE.
|
||||
@@ -214,7 +216,7 @@ function DATABASE:AddStatic( DCSStaticName )
|
||||
self.STATICS[DCSStaticName] = STATIC:Register( DCSStaticName )
|
||||
return self.STATICS[DCSStaticName]
|
||||
end
|
||||
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
@@ -223,7 +225,7 @@ end
|
||||
-- @param #DATABASE self
|
||||
function DATABASE:DeleteStatic( DCSStaticName )
|
||||
|
||||
--self.STATICS[DCSStaticName] = nil
|
||||
--self.STATICS[DCSStaticName] = nil
|
||||
end
|
||||
|
||||
--- Finds a STATIC based on the StaticName.
|
||||
@@ -262,7 +264,7 @@ end
|
||||
-- @param #string AirbaseName The name of the airbase
|
||||
function DATABASE:DeleteAirbase( AirbaseName )
|
||||
|
||||
self.AIRBASES[AirbaseName] = nil
|
||||
self.AIRBASES[AirbaseName] = nil
|
||||
end
|
||||
|
||||
--- Finds an AIRBASE based on the AirbaseName.
|
||||
@@ -283,29 +285,29 @@ do -- Zones
|
||||
-- @param #string ZoneName The name of the zone.
|
||||
-- @return Core.Zone#ZONE_BASE The found ZONE.
|
||||
function DATABASE:FindZone( ZoneName )
|
||||
|
||||
|
||||
local ZoneFound = self.ZONES[ZoneName]
|
||||
return ZoneFound
|
||||
end
|
||||
|
||||
|
||||
--- Adds a @{Zone} based on the zone name in the DATABASE.
|
||||
-- @param #DATABASE self
|
||||
-- @param #string ZoneName The name of the zone.
|
||||
-- @param Core.Zone#ZONE_BASE Zone The zone.
|
||||
function DATABASE:AddZone( ZoneName, Zone )
|
||||
|
||||
|
||||
if not self.ZONES[ZoneName] then
|
||||
self.ZONES[ZoneName] = Zone
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
--- Deletes a @{Zone} from the DATABASE based on the zone name.
|
||||
-- @param #DATABASE self
|
||||
-- @param #string ZoneName The name of the zone.
|
||||
function DATABASE:DeleteZone( ZoneName )
|
||||
|
||||
self.ZONES[ZoneName] = nil
|
||||
|
||||
self.ZONES[ZoneName] = nil
|
||||
end
|
||||
|
||||
|
||||
@@ -322,56 +324,89 @@ do -- Zones
|
||||
self.ZONENAMES[ZoneName] = ZoneName
|
||||
self:AddZone( ZoneName, Zone )
|
||||
end
|
||||
|
||||
|
||||
for ZoneGroupName, ZoneGroup in pairs( self.GROUPS ) do
|
||||
if ZoneGroupName:match("#ZONE_POLYGON") then
|
||||
local ZoneName1 = ZoneGroupName:match("(.*)#ZONE_POLYGON")
|
||||
local ZoneName2 = ZoneGroupName:match(".*#ZONE_POLYGON(.*)")
|
||||
local ZoneName = ZoneName1 .. ( ZoneName2 or "" )
|
||||
|
||||
|
||||
self:I( { "Register ZONE_POLYGON:", Name = ZoneName } )
|
||||
local Zone_Polygon = ZONE_POLYGON:New( ZoneName, ZoneGroup )
|
||||
self.ZONENAMES[ZoneName] = ZoneName
|
||||
self:AddZone( ZoneName, Zone_Polygon )
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
||||
end -- zone
|
||||
|
||||
do -- Zone_Goal
|
||||
|
||||
--- Finds a @{Zone} based on the zone name.
|
||||
-- @param #DATABASE self
|
||||
-- @param #string ZoneName The name of the zone.
|
||||
-- @return Core.Zone#ZONE_BASE The found ZONE.
|
||||
function DATABASE:FindZoneGoal( ZoneName )
|
||||
|
||||
local ZoneFound = self.ZONES_GOAL[ZoneName]
|
||||
return ZoneFound
|
||||
end
|
||||
|
||||
--- Adds a @{Zone} based on the zone name in the DATABASE.
|
||||
-- @param #DATABASE self
|
||||
-- @param #string ZoneName The name of the zone.
|
||||
-- @param Core.Zone#ZONE_BASE Zone The zone.
|
||||
function DATABASE:AddZoneGoal( ZoneName, Zone )
|
||||
|
||||
if not self.ZONES_GOAL[ZoneName] then
|
||||
self.ZONES_GOAL[ZoneName] = Zone
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--- Deletes a @{Zone} from the DATABASE based on the zone name.
|
||||
-- @param #DATABASE self
|
||||
-- @param #string ZoneName The name of the zone.
|
||||
function DATABASE:DeleteZoneGoal( ZoneName )
|
||||
|
||||
self.ZONES_GOAL[ZoneName] = nil
|
||||
end
|
||||
|
||||
end -- Zone_Goal
|
||||
do -- cargo
|
||||
|
||||
--- Adds a Cargo based on the Cargo Name in the DATABASE.
|
||||
-- @param #DATABASE self
|
||||
-- @param #string CargoName The name of the airbase
|
||||
function DATABASE:AddCargo( Cargo )
|
||||
|
||||
|
||||
if not self.CARGOS[Cargo.Name] then
|
||||
self.CARGOS[Cargo.Name] = Cargo
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
--- Deletes a Cargo from the DATABASE based on the Cargo Name.
|
||||
-- @param #DATABASE self
|
||||
-- @param #string CargoName The name of the airbase
|
||||
function DATABASE:DeleteCargo( CargoName )
|
||||
|
||||
self.CARGOS[CargoName] = nil
|
||||
|
||||
self.CARGOS[CargoName] = nil
|
||||
end
|
||||
|
||||
|
||||
--- Finds an CARGO based on the CargoName.
|
||||
-- @param #DATABASE self
|
||||
-- @param #string CargoName
|
||||
-- @return Wrapper.Cargo#CARGO The found CARGO.
|
||||
function DATABASE:FindCargo( CargoName )
|
||||
|
||||
|
||||
local CargoFound = self.CARGOS[CargoName]
|
||||
return CargoFound
|
||||
end
|
||||
|
||||
|
||||
--- Checks if the Template name has a #CARGO tag.
|
||||
-- If yes, the group is a cargo.
|
||||
-- @param #DATABASE self
|
||||
@@ -380,10 +415,10 @@ do -- cargo
|
||||
function DATABASE:IsCargo( TemplateName )
|
||||
|
||||
TemplateName = env.getValueDictByKey( TemplateName )
|
||||
|
||||
|
||||
local Cargo = TemplateName:match( "#(CARGO)" )
|
||||
|
||||
return Cargo and Cargo == "CARGO"
|
||||
return Cargo and Cargo == "CARGO"
|
||||
end
|
||||
|
||||
--- Private method that registers new Static Templates within the DATABASE Object.
|
||||
@@ -392,7 +427,7 @@ do -- cargo
|
||||
function DATABASE:_RegisterCargos()
|
||||
|
||||
local Groups = UTILS.DeepCopy( self.GROUPS ) -- This is a very important statement. CARGO_GROUP:New creates a new _DATABASE.GROUP entry, which will confuse the loop. I searched 4 hours on this to find the bug!
|
||||
|
||||
|
||||
for CargoGroupName, CargoGroup in pairs( Groups ) do
|
||||
self:I( { Cargo = CargoGroupName } )
|
||||
if self:IsCargo( CargoGroupName ) then
|
||||
@@ -405,12 +440,12 @@ do -- cargo
|
||||
local Name = CargoParam and CargoParam:match( "N=([%a%d]+),?") or CargoName
|
||||
local LoadRadius = CargoParam and tonumber( CargoParam:match( "RR=([%a%d]+),?") )
|
||||
local NearRadius = CargoParam and tonumber( CargoParam:match( "NR=([%a%d]+),?") )
|
||||
|
||||
|
||||
self:I({"Register CargoGroup:",Type=Type,Name=Name,LoadRadius=LoadRadius,NearRadius=NearRadius})
|
||||
CARGO_GROUP:New( CargoGroup, Type, Name, LoadRadius, NearRadius )
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
for CargoStaticName, CargoStatic in pairs( self.STATICS ) do
|
||||
if self:IsCargo( CargoStaticName ) then
|
||||
local CargoInfo = CargoStaticName:match("#CARGO(.*)")
|
||||
@@ -421,7 +456,7 @@ do -- cargo
|
||||
local Name = CargoParam and CargoParam:match( "N=([%a%d]+),?") or CargoName
|
||||
local LoadRadius = CargoParam and tonumber( CargoParam:match( "RR=([%a%d]+),?") )
|
||||
local NearRadius = CargoParam and tonumber( CargoParam:match( "NR=([%a%d]+),?") )
|
||||
|
||||
|
||||
if Category == "SLING" then
|
||||
self:I({"Register CargoSlingload:",Type=Type,Name=Name,LoadRadius=LoadRadius,NearRadius=NearRadius})
|
||||
CARGO_SLINGLOAD:New( CargoStatic, Type, Name, LoadRadius, NearRadius )
|
||||
@@ -433,7 +468,7 @@ do -- cargo
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
end -- cargo
|
||||
@@ -479,9 +514,9 @@ function DATABASE:AddGroup( GroupName )
|
||||
if not self.GROUPS[GroupName] then
|
||||
self:I( { "Add GROUP:", GroupName } )
|
||||
self.GROUPS[GroupName] = GROUP:Register( GroupName )
|
||||
end
|
||||
|
||||
return self.GROUPS[GroupName]
|
||||
end
|
||||
|
||||
return self.GROUPS[GroupName]
|
||||
end
|
||||
|
||||
--- Adds a player based on the Player Name in the DATABASE.
|
||||
@@ -584,7 +619,7 @@ function DATABASE:Spawn( SpawnTemplate )
|
||||
for UnitID, UnitData in pairs( SpawnTemplate.units ) do
|
||||
self:AddUnit( UnitData.name )
|
||||
end
|
||||
|
||||
|
||||
return SpawnGroup
|
||||
end
|
||||
|
||||
@@ -616,21 +651,21 @@ end
|
||||
function DATABASE:_RegisterGroupTemplate( GroupTemplate, CoalitionSide, CategoryID, CountryID, GroupName )
|
||||
|
||||
local GroupTemplateName = GroupName or env.getValueDictByKey( GroupTemplate.name )
|
||||
|
||||
|
||||
if not self.Templates.Groups[GroupTemplateName] then
|
||||
self.Templates.Groups[GroupTemplateName] = {}
|
||||
self.Templates.Groups[GroupTemplateName].Status = nil
|
||||
end
|
||||
|
||||
|
||||
-- Delete the spans from the route, it is not needed and takes memory.
|
||||
if GroupTemplate.route and GroupTemplate.route.spans then
|
||||
if GroupTemplate.route and GroupTemplate.route.spans then
|
||||
GroupTemplate.route.spans = nil
|
||||
end
|
||||
|
||||
|
||||
GroupTemplate.CategoryID = CategoryID
|
||||
GroupTemplate.CoalitionID = CoalitionSide
|
||||
GroupTemplate.CountryID = CountryID
|
||||
|
||||
|
||||
self.Templates.Groups[GroupTemplateName].GroupName = GroupTemplateName
|
||||
self.Templates.Groups[GroupTemplateName].Template = GroupTemplate
|
||||
self.Templates.Groups[GroupTemplateName].groupId = GroupTemplate.groupId
|
||||
@@ -645,7 +680,7 @@ function DATABASE:_RegisterGroupTemplate( GroupTemplate, CoalitionSide, Category
|
||||
for unit_num, UnitTemplate in pairs( GroupTemplate.units ) do
|
||||
|
||||
UnitTemplate.name = env.getValueDictByKey(UnitTemplate.name)
|
||||
|
||||
|
||||
self.Templates.Units[UnitTemplate.name] = {}
|
||||
self.Templates.Units[UnitTemplate.name].UnitName = UnitTemplate.name
|
||||
self.Templates.Units[UnitTemplate.name].Template = UnitTemplate
|
||||
@@ -663,8 +698,8 @@ function DATABASE:_RegisterGroupTemplate( GroupTemplate, CoalitionSide, Category
|
||||
self.Templates.ClientsByName[UnitTemplate.name].CountryID = CountryID
|
||||
self.Templates.ClientsByID[UnitTemplate.unitId] = UnitTemplate
|
||||
end
|
||||
|
||||
UnitNames[#UnitNames+1] = self.Templates.Units[UnitTemplate.name].UnitName
|
||||
|
||||
UnitNames[#UnitNames+1] = self.Templates.Units[UnitTemplate.name].UnitName
|
||||
end
|
||||
|
||||
self:I( { Group = self.Templates.Groups[GroupTemplateName].GroupName,
|
||||
@@ -693,13 +728,13 @@ function DATABASE:_RegisterStaticTemplate( StaticTemplate, CoalitionID, Category
|
||||
local StaticTemplate = UTILS.DeepCopy( StaticTemplate )
|
||||
|
||||
local StaticTemplateName = env.getValueDictByKey(StaticTemplate.name)
|
||||
|
||||
|
||||
self.Templates.Statics[StaticTemplateName] = self.Templates.Statics[StaticTemplateName] or {}
|
||||
|
||||
|
||||
StaticTemplate.CategoryID = CategoryID
|
||||
StaticTemplate.CoalitionID = CoalitionID
|
||||
StaticTemplate.CountryID = CountryID
|
||||
|
||||
|
||||
self.Templates.Statics[StaticTemplateName].StaticName = StaticTemplateName
|
||||
self.Templates.Statics[StaticTemplateName].GroupTemplate = StaticTemplate
|
||||
self.Templates.Statics[StaticTemplateName].UnitTemplate = StaticTemplate.units[1]
|
||||
@@ -710,12 +745,12 @@ function DATABASE:_RegisterStaticTemplate( StaticTemplate, CoalitionID, Category
|
||||
self:I( { Static = self.Templates.Statics[StaticTemplateName].StaticName,
|
||||
Coalition = self.Templates.Statics[StaticTemplateName].CoalitionID,
|
||||
Category = self.Templates.Statics[StaticTemplateName].CategoryID,
|
||||
Country = self.Templates.Statics[StaticTemplateName].CountryID
|
||||
Country = self.Templates.Statics[StaticTemplateName].CountryID
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
self:AddStatic( StaticTemplateName )
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
||||
@@ -783,7 +818,7 @@ function DATABASE:_RegisterPlayers()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -799,12 +834,12 @@ function DATABASE:_RegisterGroupsAndUnits()
|
||||
|
||||
if DCSGroup:isExist() then
|
||||
local DCSGroupName = DCSGroup:getName()
|
||||
|
||||
|
||||
self:I( { "Register Group:", DCSGroupName } )
|
||||
self:AddGroup( DCSGroupName )
|
||||
|
||||
for DCSUnitId, DCSUnit in pairs( DCSGroup:getUnits() ) do
|
||||
|
||||
|
||||
local DCSUnitName = DCSUnit:getName()
|
||||
self:I( { "Register Unit:", DCSUnitName } )
|
||||
self:AddUnit( DCSUnitName )
|
||||
@@ -812,10 +847,10 @@ function DATABASE:_RegisterGroupsAndUnits()
|
||||
else
|
||||
self:E( { "Group does not exist: ", DCSGroup } )
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
self:I("Groups:")
|
||||
for GroupName, Group in pairs( self.GROUPS ) do
|
||||
self:I( { "Group:", GroupName } )
|
||||
@@ -833,7 +868,7 @@ function DATABASE:_RegisterClients()
|
||||
self:I( { "Register Client:", ClientName } )
|
||||
self:AddClient( ClientName )
|
||||
end
|
||||
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -847,7 +882,7 @@ function DATABASE:_RegisterStatics()
|
||||
|
||||
if DCSStatic:isExist() then
|
||||
local DCSStaticName = DCSStatic:getName()
|
||||
|
||||
|
||||
self:I( { "Register Static:", DCSStaticName } )
|
||||
self:AddStatic( DCSStaticName )
|
||||
else
|
||||
@@ -887,7 +922,7 @@ function DATABASE:_EventOnBirth( Event )
|
||||
|
||||
if Event.IniDCSUnit then
|
||||
if Event.IniObjectCategory == 3 then
|
||||
self:AddStatic( Event.IniDCSUnitName )
|
||||
self:AddStatic( Event.IniDCSUnitName )
|
||||
else
|
||||
if Event.IniObjectCategory == 1 then
|
||||
self:AddUnit( Event.IniDCSUnitName )
|
||||
@@ -923,7 +958,7 @@ function DATABASE:_EventOnDeadOrCrash( Event )
|
||||
if Event.IniObjectCategory == 3 then
|
||||
if self.STATICS[Event.IniDCSUnitName] then
|
||||
self:DeleteStatic( Event.IniDCSUnitName )
|
||||
end
|
||||
end
|
||||
else
|
||||
if Event.IniObjectCategory == 1 then
|
||||
if self.UNITS[Event.IniDCSUnitName] then
|
||||
@@ -932,7 +967,7 @@ function DATABASE:_EventOnDeadOrCrash( Event )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
self:AccountDestroys( Event )
|
||||
end
|
||||
|
||||
@@ -986,7 +1021,7 @@ end
|
||||
-- @return #DATABASE self
|
||||
function DATABASE:ForEach( IteratorFunction, FinalizeFunction, arg, Set )
|
||||
self:F2( arg )
|
||||
|
||||
|
||||
local function CoRoutine()
|
||||
local Count = 0
|
||||
for ObjectID, Object in pairs( Set ) do
|
||||
@@ -995,20 +1030,20 @@ function DATABASE:ForEach( IteratorFunction, FinalizeFunction, arg, Set )
|
||||
Count = Count + 1
|
||||
-- if Count % 100 == 0 then
|
||||
-- coroutine.yield( false )
|
||||
-- end
|
||||
-- end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
-- local co = coroutine.create( CoRoutine )
|
||||
local co = CoRoutine
|
||||
|
||||
|
||||
local function Schedule()
|
||||
|
||||
|
||||
-- local status, res = coroutine.resume( co )
|
||||
local status, res = co()
|
||||
self:T3( { status, res } )
|
||||
|
||||
|
||||
if status == false then
|
||||
error( res )
|
||||
end
|
||||
@@ -1021,8 +1056,9 @@ function DATABASE:ForEach( IteratorFunction, FinalizeFunction, arg, Set )
|
||||
return false
|
||||
end
|
||||
|
||||
local Scheduler = SCHEDULER:New( self, Schedule, {}, 0.001, 0.001, 0 )
|
||||
|
||||
--local Scheduler = SCHEDULER:New( self, Schedule, {}, 0.001, 0.001, 0 )
|
||||
Schedule()
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -1033,7 +1069,7 @@ end
|
||||
-- @return #DATABASE self
|
||||
function DATABASE:ForEachStatic( IteratorFunction, FinalizeFunction, ... ) --R2.1
|
||||
self:F2( arg )
|
||||
|
||||
|
||||
self:ForEach( IteratorFunction, FinalizeFunction, arg, self.STATICS )
|
||||
|
||||
return self
|
||||
@@ -1046,7 +1082,7 @@ end
|
||||
-- @return #DATABASE self
|
||||
function DATABASE:ForEachUnit( IteratorFunction, FinalizeFunction, ... )
|
||||
self:F2( arg )
|
||||
|
||||
|
||||
self:ForEach( IteratorFunction, FinalizeFunction, arg, self.UNITS )
|
||||
|
||||
return self
|
||||
@@ -1059,7 +1095,7 @@ end
|
||||
-- @return #DATABASE self
|
||||
function DATABASE:ForEachGroup( IteratorFunction, FinalizeFunction, ... )
|
||||
self:F2( arg )
|
||||
|
||||
|
||||
self:ForEach( IteratorFunction, FinalizeFunction, arg, self.GROUPS )
|
||||
|
||||
return self
|
||||
@@ -1072,9 +1108,9 @@ end
|
||||
-- @return #DATABASE self
|
||||
function DATABASE:ForEachPlayer( IteratorFunction, FinalizeFunction, ... )
|
||||
self:F2( arg )
|
||||
|
||||
|
||||
self:ForEach( IteratorFunction, FinalizeFunction, arg, self.PLAYERS )
|
||||
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -1085,9 +1121,9 @@ end
|
||||
-- @return #DATABASE self
|
||||
function DATABASE:ForEachPlayerJoined( IteratorFunction, FinalizeFunction, ... )
|
||||
self:F2( arg )
|
||||
|
||||
|
||||
self:ForEach( IteratorFunction, FinalizeFunction, arg, self.PLAYERSJOINED )
|
||||
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -1097,9 +1133,9 @@ end
|
||||
-- @return #DATABASE self
|
||||
function DATABASE:ForEachPlayerUnit( IteratorFunction, FinalizeFunction, ... )
|
||||
self:F2( arg )
|
||||
|
||||
|
||||
self:ForEach( IteratorFunction, FinalizeFunction, arg, self.PLAYERUNITS )
|
||||
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -1110,7 +1146,7 @@ end
|
||||
-- @return #DATABASE self
|
||||
function DATABASE:ForEachClient( IteratorFunction, ... )
|
||||
self:F2( arg )
|
||||
|
||||
|
||||
self:ForEach( IteratorFunction, arg, self.CLIENTS )
|
||||
|
||||
return self
|
||||
@@ -1122,7 +1158,7 @@ end
|
||||
-- @return #DATABASE self
|
||||
function DATABASE:ForEachCargo( IteratorFunction, ... )
|
||||
self:F2( arg )
|
||||
|
||||
|
||||
self:ForEach( IteratorFunction, arg, self.CARGOS )
|
||||
|
||||
return self
|
||||
@@ -1244,10 +1280,10 @@ function DATABASE:_RegisterTemplates()
|
||||
|
||||
if (CoalitionName == 'red' or CoalitionName == 'blue' or CoalitionName == 'neutrals') and type(coa_data) == 'table' then
|
||||
--self.Units[coa_name] = {}
|
||||
|
||||
|
||||
local CoalitionSide = coalition.side[string.upper(CoalitionName)]
|
||||
if CoalitionName=="red" then
|
||||
CoalitionSide=coalition.side.NEUTRAL
|
||||
CoalitionSide=coalition.side.RED
|
||||
elseif CoalitionName=="blue" then
|
||||
CoalitionSide=coalition.side.BLUE
|
||||
else
|
||||
@@ -1277,10 +1313,10 @@ function DATABASE:_RegisterTemplates()
|
||||
|
||||
local CountryName = string.upper(cntry_data.name)
|
||||
local CountryID = cntry_data.id
|
||||
|
||||
|
||||
self.COUNTRY_ID[CountryName] = CountryID
|
||||
self.COUNTRY_NAME[CountryID] = CountryName
|
||||
|
||||
|
||||
--self.Units[coa_name][countryName] = {}
|
||||
--self.Units[coa_name][countryName]["countryId"] = cntry_data.id
|
||||
|
||||
@@ -1299,18 +1335,18 @@ function DATABASE:_RegisterTemplates()
|
||||
for group_num, Template in pairs(obj_type_data.group) do
|
||||
|
||||
if obj_type_name ~= "static" and Template and Template.units and type(Template.units) == 'table' then --making sure again- this is a valid group
|
||||
self:_RegisterGroupTemplate(
|
||||
Template,
|
||||
CoalitionSide,
|
||||
_DATABASECategory[string.lower(CategoryName)],
|
||||
CountryID
|
||||
self:_RegisterGroupTemplate(
|
||||
Template,
|
||||
CoalitionSide,
|
||||
_DATABASECategory[string.lower(CategoryName)],
|
||||
CountryID
|
||||
)
|
||||
else
|
||||
self:_RegisterStaticTemplate(
|
||||
Template,
|
||||
CoalitionSide,
|
||||
_DATABASECategory[string.lower(CategoryName)],
|
||||
CountryID
|
||||
self:_RegisterStaticTemplate(
|
||||
Template,
|
||||
CoalitionSide,
|
||||
_DATABASECategory[string.lower(CategoryName)],
|
||||
CountryID
|
||||
)
|
||||
end --if GroupTemplate and GroupTemplate.units then
|
||||
end --for group_num, GroupTemplate in pairs(obj_type_data.group) do
|
||||
@@ -1331,35 +1367,35 @@ end
|
||||
-- @param Core.Event#EVENTDATA Event
|
||||
function DATABASE:AccountHits( Event )
|
||||
self:F( { Event } )
|
||||
|
||||
|
||||
if Event.IniPlayerName ~= nil then -- It is a player that is hitting something
|
||||
self:T( "Hitting Something" )
|
||||
|
||||
|
||||
-- What is he hitting?
|
||||
if Event.TgtCategory then
|
||||
|
||||
|
||||
-- A target got hit
|
||||
self.HITS[Event.TgtUnitName] = self.HITS[Event.TgtUnitName] or {}
|
||||
local Hit = self.HITS[Event.TgtUnitName]
|
||||
|
||||
|
||||
Hit.Players = Hit.Players or {}
|
||||
Hit.Players[Event.IniPlayerName] = true
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- It is a weapon initiated by a player, that is hitting something
|
||||
-- This seems to occur only with scenery and static objects.
|
||||
if Event.WeaponPlayerName ~= nil then
|
||||
if Event.WeaponPlayerName ~= nil then
|
||||
self:T( "Hitting Scenery" )
|
||||
|
||||
|
||||
-- What is he hitting?
|
||||
if Event.TgtCategory then
|
||||
|
||||
|
||||
if Event.WeaponCoalition then -- A coalition object was hit, probably a static.
|
||||
-- A target got hit
|
||||
self.HITS[Event.TgtUnitName] = self.HITS[Event.TgtUnitName] or {}
|
||||
local Hit = self.HITS[Event.TgtUnitName]
|
||||
|
||||
|
||||
Hit.Players = Hit.Players or {}
|
||||
Hit.Players[Event.WeaponPlayerName] = true
|
||||
else -- A scenery object was hit.
|
||||
@@ -1367,13 +1403,13 @@ end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--- Account the destroys.
|
||||
-- @param #DATABASE self
|
||||
-- @param Core.Event#EVENTDATA Event
|
||||
function DATABASE:AccountDestroys( Event )
|
||||
self:F( { Event } )
|
||||
|
||||
|
||||
local TargetUnit = nil
|
||||
local TargetGroup = nil
|
||||
local TargetUnitName = ""
|
||||
@@ -1385,26 +1421,26 @@ end
|
||||
local TargetUnitCoalition = nil
|
||||
local TargetUnitCategory = nil
|
||||
local TargetUnitType = nil
|
||||
|
||||
|
||||
if Event.IniDCSUnit then
|
||||
|
||||
|
||||
TargetUnit = Event.IniUnit
|
||||
TargetUnitName = Event.IniDCSUnitName
|
||||
TargetGroup = Event.IniDCSGroup
|
||||
TargetGroupName = Event.IniDCSGroupName
|
||||
TargetPlayerName = Event.IniPlayerName
|
||||
|
||||
|
||||
TargetCoalition = Event.IniCoalition
|
||||
--TargetCategory = TargetUnit:getCategory()
|
||||
--TargetCategory = TargetUnit:getDesc().category -- Workaround
|
||||
TargetCategory = Event.IniCategory
|
||||
TargetType = Event.IniTypeName
|
||||
|
||||
|
||||
TargetUnitType = TargetType
|
||||
|
||||
|
||||
self:T( { TargetUnitName, TargetGroupName, TargetPlayerName, TargetCoalition, TargetCategory, TargetType } )
|
||||
end
|
||||
|
||||
|
||||
local Destroyed = false
|
||||
|
||||
-- What is the player destroying?
|
||||
@@ -1413,8 +1449,3 @@ end
|
||||
self.DESTROYS[Event.IniUnitName] = true
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -189,7 +189,9 @@ world.event.S_EVENT_NEW_CARGO = world.event.S_EVENT_MAX + 1000
|
||||
world.event.S_EVENT_DELETE_CARGO = world.event.S_EVENT_MAX + 1001
|
||||
world.event.S_EVENT_NEW_ZONE = world.event.S_EVENT_MAX + 1002
|
||||
world.event.S_EVENT_DELETE_ZONE = world.event.S_EVENT_MAX + 1003
|
||||
world.event.S_EVENT_REMOVE_UNIT = world.event.S_EVENT_MAX + 1004
|
||||
world.event.S_EVENT_NEW_ZONE_GOAL = world.event.S_EVENT_MAX + 1004
|
||||
world.event.S_EVENT_DELETE_ZONE_GOAL = world.event.S_EVENT_MAX + 1005
|
||||
world.event.S_EVENT_REMOVE_UNIT = world.event.S_EVENT_MAX + 1006
|
||||
|
||||
|
||||
--- The different types of events supported by MOOSE.
|
||||
@@ -226,6 +228,8 @@ EVENTS = {
|
||||
DeleteCargo = world.event.S_EVENT_DELETE_CARGO,
|
||||
NewZone = world.event.S_EVENT_NEW_ZONE,
|
||||
DeleteZone = world.event.S_EVENT_DELETE_ZONE,
|
||||
NewZoneGoal = world.event.S_EVENT_NEW_ZONE_GOAL,
|
||||
DeleteZoneGoal = world.event.S_EVENT_DELETE_ZONE_GOAL,
|
||||
RemoveUnit = world.event.S_EVENT_REMOVE_UNIT,
|
||||
}
|
||||
|
||||
@@ -462,6 +466,16 @@ local _EVENTMETA = {
|
||||
Event = "OnEventDeleteZone",
|
||||
Text = "S_EVENT_DELETE_ZONE"
|
||||
},
|
||||
[EVENTS.NewZoneGoal] = {
|
||||
Order = 1,
|
||||
Event = "OnEventNewZoneGoal",
|
||||
Text = "S_EVENT_NEW_ZONE_GOAL"
|
||||
},
|
||||
[EVENTS.DeleteZoneGoal] = {
|
||||
Order = 1,
|
||||
Event = "OnEventDeleteZoneGoal",
|
||||
Text = "S_EVENT_DELETE_ZONE_GOAL"
|
||||
},
|
||||
[EVENTS.RemoveUnit] = {
|
||||
Order = -1,
|
||||
Event = "OnEventRemoveUnit",
|
||||
@@ -800,6 +814,38 @@ do -- Event Creation
|
||||
world.onEvent( Event )
|
||||
end
|
||||
|
||||
--- Creation of a New ZoneGoal Event.
|
||||
-- @param #EVENT self
|
||||
-- @param Core.Functional#ZONE_GOAL ZoneGoal The ZoneGoal created.
|
||||
function EVENT:CreateEventNewZoneGoal( ZoneGoal )
|
||||
self:F( { ZoneGoal } )
|
||||
|
||||
local Event = {
|
||||
id = EVENTS.NewZoneGoal,
|
||||
time = timer.getTime(),
|
||||
ZoneGoal = ZoneGoal,
|
||||
}
|
||||
|
||||
world.onEvent( Event )
|
||||
end
|
||||
|
||||
|
||||
--- Creation of a ZoneGoal Deletion Event.
|
||||
-- @param #EVENT self
|
||||
-- @param Core.ZoneGoal#ZONE_GOAL ZoneGoal The ZoneGoal created.
|
||||
function EVENT:CreateEventDeleteZoneGoal( ZoneGoal )
|
||||
self:F( { ZoneGoal } )
|
||||
|
||||
local Event = {
|
||||
id = EVENTS.DeleteZoneGoal,
|
||||
time = timer.getTime(),
|
||||
ZoneGoal = ZoneGoal,
|
||||
}
|
||||
|
||||
world.onEvent( Event )
|
||||
end
|
||||
|
||||
|
||||
--- Creation of a S_EVENT_PLAYER_ENTER_UNIT Event.
|
||||
-- @param #EVENT self
|
||||
-- @param Wrapper.Unit#UNIT PlayerUnit.
|
||||
|
||||
@@ -594,7 +594,17 @@ do -- FSM
|
||||
return errmsg
|
||||
end
|
||||
if self[handler] then
|
||||
self:T( "*** FSM *** " .. step .. " *** " .. params[1] .. " --> " .. params[2] .. " --> " .. params[3] )
|
||||
if step == "onafter" or step == "OnAfter" then
|
||||
self:T( ":::>" .. step .. params[2] .. " : " .. params[1] .. " >> " .. params[2] .. ">" .. step .. params[2] .. "()" .. " >> " .. params[3] )
|
||||
elseif step == "onbefore" or step == "OnBefore" then
|
||||
self:T( ":::>" .. step .. params[2] .. " : " .. params[1] .. " >> " .. step .. params[2] .. "()" .. ">" .. params[2] .. " >> " .. params[3] )
|
||||
elseif step == "onenter" or step == "OnEnter" then
|
||||
self:T( ":::>" .. step .. params[3] .. " : " .. params[1] .. " >> " .. params[2] .. " >> " .. step .. params[3] .. "()" .. ">" .. params[3] )
|
||||
elseif step == "onleave" or step == "OnLeave" then
|
||||
self:T( ":::>" .. step .. params[1] .. " : " .. params[1] .. ">" .. step .. params[1] .. "()" .. " >> " .. params[2] .. " >> " .. params[3] )
|
||||
else
|
||||
self:T( ":::>" .. step .. " : " .. params[1] .. " >> " .. params[2] .. " >> " .. params[3] )
|
||||
end
|
||||
self._EventSchedules[EventName] = nil
|
||||
local Result, Value = xpcall( function() return self[handler]( self, unpack( params ) ) end, ErrorHandler )
|
||||
return Value
|
||||
@@ -718,13 +728,13 @@ do -- FSM
|
||||
if DelaySeconds < 0 then -- Only call the event ONCE!
|
||||
DelaySeconds = math.abs( DelaySeconds )
|
||||
if not self._EventSchedules[EventName] then
|
||||
CallID = self.CallScheduler:Schedule( self, self._handler, { EventName, ... }, DelaySeconds or 1 )
|
||||
CallID = self.CallScheduler:Schedule( self, self._handler, { EventName, ... }, DelaySeconds or 1, nil, nil, nil, 4, true )
|
||||
self._EventSchedules[EventName] = CallID
|
||||
else
|
||||
-- reschedule
|
||||
end
|
||||
else
|
||||
CallID = self.CallScheduler:Schedule( self, self._handler, { EventName, ... }, DelaySeconds or 1 )
|
||||
CallID = self.CallScheduler:Schedule( self, self._handler, { EventName, ... }, DelaySeconds or 1, nil, nil, nil, 4, true )
|
||||
end
|
||||
else
|
||||
error( "FSM: An asynchronous event trigger requires a DelaySeconds parameter!!! This can be positive or negative! Sorry, but will not process this." )
|
||||
|
||||
@@ -2075,7 +2075,7 @@ do -- COORDINATE
|
||||
-- @return #string The coordinate Text in the configured coordinate system.
|
||||
function COORDINATE:ToString( Controllable, Settings, Task )
|
||||
|
||||
self:E( { Controllable = Controllable and Controllable:GetName() } )
|
||||
-- self:E( { Controllable = Controllable and Controllable:GetName() } )
|
||||
|
||||
local Settings = Settings or ( Controllable and _DATABASE:GetPlayerSettings( Controllable:GetPlayerName() ) ) or _SETTINGS
|
||||
|
||||
|
||||
@@ -800,470 +800,4 @@ function BEACON:_TACANToFrequency(TACANChannel, TACANMode)
|
||||
return (A + TACANChannel - B) * 1000000
|
||||
end
|
||||
|
||||
--- Manages radio transmissions.
|
||||
--
|
||||
-- @type RADIOQUEUE
|
||||
-- @field #string ClassName Name of the class "RADIOQUEUE".
|
||||
-- @field #string lid ID for dcs.log.
|
||||
-- @field #number frequency The radio frequency in Hz.
|
||||
-- @field #number modulation The radio modulation. Either radio.modulation.AM or radio.modulation.FM.
|
||||
-- @field Core.Scheduler#SCHEDULER scheduler The scheduler.
|
||||
-- @field #string RQid The radio queue scheduler ID.
|
||||
-- @field #table queue The queue of transmissions.
|
||||
-- @field #number Tlast Time (abs) when the last transmission finished.
|
||||
-- @field Core.Point#COORDINATE sendercoord Coordinate from where transmissions are broadcasted.
|
||||
-- @field #number sendername Name of the sending unit or static.
|
||||
-- @field Wrapper.Positionable#POSITIONABLE positionable The positionable to broadcast the message.
|
||||
-- @field #number power Power of radio station in Watts. Default 100 W.
|
||||
-- @field #table numbers Table of number transmission parameters.
|
||||
-- @extends Core.Base#BASE
|
||||
RADIOQUEUE = {
|
||||
ClassName = "RADIOQUEUE",
|
||||
lid=nil,
|
||||
frequency=nil,
|
||||
modulation=nil,
|
||||
scheduler=nil,
|
||||
RQid=nil,
|
||||
queue={},
|
||||
Tlast=nil,
|
||||
sendercoord=nil,
|
||||
sendername=nil,
|
||||
positionable=nil,
|
||||
power=100,
|
||||
numbers={},
|
||||
}
|
||||
|
||||
--- Radio queue transmission data.
|
||||
-- @type RADIOQUEUE.Transmission
|
||||
-- @field #string filename Name of the file to be transmitted.
|
||||
-- @field #string path Path in miz file where the file is located.
|
||||
-- @field #number duration Duration in seconds.
|
||||
-- @field #number Tstarted Mission time (abs) in seconds when the transmission started.
|
||||
-- @field #boolean isplaying If true, transmission is currently playing.
|
||||
-- @field #number Tplay Mission time (abs) in seconds when the transmission should be played.
|
||||
|
||||
|
||||
--- Create a new RADIOQUEUE object for a given radio frequency/modulation.
|
||||
-- @param #RADIOQUEUE self
|
||||
-- @param #number frequency The radio frequency in MHz.
|
||||
-- @param #number modulation (Optional) The radio modulation. Default radio.modulation.AM.
|
||||
-- @return #RADIOQUEUE self The RADIOQUEUE object.
|
||||
function RADIOQUEUE:New(frequency, modulation)
|
||||
|
||||
-- Inherit base
|
||||
local self=BASE:Inherit(self, BASE:New()) -- Core.Radio#RADIOQUEUE
|
||||
|
||||
self.lid="RADIOQUEUE | "
|
||||
|
||||
if frequency==nil then
|
||||
self:E(self.lid.."ERROR: No frequency specified as first parameter!")
|
||||
return nil
|
||||
end
|
||||
|
||||
-- Frequency in Hz.
|
||||
self.frequency=frequency*1000000
|
||||
|
||||
-- Modulation.
|
||||
self.modulation=modulation or radio.modulation.AM
|
||||
|
||||
-- Scheduler
|
||||
self.scheduler=SCHEDULER:New()
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Start the radio queue.
|
||||
-- @param #RADIOQUEUE self
|
||||
-- @param #number delay (Optional) Delay in seconds, before the radio queue is started. Default 1 sec.
|
||||
-- @param #number dt (Optional) Time step in seconds for checking the queue. Default 0.01 sec.
|
||||
-- @return #RADIOQUEUE self The RADIOQUEUE object.
|
||||
function RADIOQUEUE:Start(delay, dt)
|
||||
|
||||
delay=delay or 1
|
||||
|
||||
dt=dt or 0.01
|
||||
|
||||
self:I(self.lid..string.format("Starting RADIOQUEUE in %.1f seconds with interval dt=%.3f seconds.", delay, dt))
|
||||
|
||||
self.RQid=self.scheduler:Schedule(self, self._CheckRadioQueue, {}, delay, dt)
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Stop the radio queue. Stop scheduler and delete queue.
|
||||
-- @param #RADIOQUEUE self
|
||||
-- @return #RADIOQUEUE self The RADIOQUEUE object.
|
||||
function RADIOQUEUE:Stop()
|
||||
self:I(self.lid.."Stopping RADIOQUEUE.")
|
||||
self.scheduler:Stop(self.RQid)
|
||||
self.queue={}
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set coordinate from where the transmission is broadcasted.
|
||||
-- @param #RADIOQUEUE self
|
||||
-- @param Core.Point#COORDINATE coordinate Coordinate of the sender.
|
||||
-- @return #RADIOQUEUE self The RADIOQUEUE object.
|
||||
function RADIOQUEUE:SetSenderCoordinate(coordinate)
|
||||
self.sendercoord=coordinate
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set name of unit or static from which transmissions are made.
|
||||
-- @param #RADIOQUEUE self
|
||||
-- @param #string name Name of the unit or static used for transmissions.
|
||||
-- @return #RADIOQUEUE self The RADIOQUEUE object.
|
||||
function RADIOQUEUE:SetSenderUnitName(name)
|
||||
self.sendername=name
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set parameters of a digit.
|
||||
-- @param #RADIOQUEUE self
|
||||
-- @param #number digit The digit 0-9.
|
||||
-- @param #string filename The name of the sound file.
|
||||
-- @param #number duration The duration of the sound file in seconds.
|
||||
-- @param #string path The directory within the miz file where the sound is located. Default "l10n/DEFAULT/".
|
||||
-- @return #RADIOQUEUE self The RADIOQUEUE object.
|
||||
function RADIOQUEUE:SetDigit(digit, filename, duration, path)
|
||||
|
||||
local transmission={} --#RADIOQUEUE.Transmission
|
||||
transmission.filename=filename
|
||||
transmission.duration=duration
|
||||
transmission.path=path or "l10n/DEFAULT/"
|
||||
|
||||
-- Convert digit to string in case it is given as a number.
|
||||
if type(digit)=="number" then
|
||||
digit=tostring(digit)
|
||||
end
|
||||
|
||||
-- Set transmission.
|
||||
self.numbers[digit]=transmission
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Add a transmission to the radio queue.
|
||||
-- @param #RADIOQUEUE self
|
||||
-- @param #RADIOQUEUE.Transmission transmission The transmission data table.
|
||||
-- @return #RADIOQUEUE self The RADIOQUEUE object.
|
||||
function RADIOQUEUE:AddTransmission(transmission)
|
||||
self:F({transmission=transmission})
|
||||
|
||||
-- Init.
|
||||
transmission.isplaying=false
|
||||
transmission.Tstarted=nil
|
||||
|
||||
-- Add to queue.
|
||||
table.insert(self.queue, transmission)
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Add a transmission to the radio queue.
|
||||
-- @param #RADIOQUEUE self
|
||||
-- @param #string filename Name of the sound file. Usually an ogg or wav file type.
|
||||
-- @param #number duration Duration in seconds the file lasts.
|
||||
-- @param #number path Directory path inside the miz file where the sound file is located. Default "l10n/DEFAULT/".
|
||||
-- @param #number tstart Start time (abs) seconds. Default now.
|
||||
-- @param #number interval Interval in seconds after the last transmission finished.
|
||||
-- @return #RADIOQUEUE self The RADIOQUEUE object.
|
||||
function RADIOQUEUE:NewTransmission(filename, duration, path, tstart, interval)
|
||||
|
||||
-- Sanity checks.
|
||||
if not filename then
|
||||
self:E(self.lid.."ERROR: No filename specified.")
|
||||
return nil
|
||||
end
|
||||
if type(filename)~="string" then
|
||||
self:E(self.lid.."ERROR: Filename specified is NOT a string.")
|
||||
return nil
|
||||
end
|
||||
|
||||
if not duration then
|
||||
self:E(self.lid.."ERROR: No duration of transmission specified.")
|
||||
return nil
|
||||
end
|
||||
if type(duration)~="number" then
|
||||
self:E(self.lid.."ERROR: Duration specified is NOT a number.")
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
local transmission={} --#RADIOQUEUE.Transmission
|
||||
transmission.filename=filename
|
||||
transmission.duration=duration
|
||||
transmission.path=path or "l10n/DEFAULT/"
|
||||
transmission.Tplay=tstart or timer.getAbsTime()
|
||||
|
||||
-- Add transmission to queue.
|
||||
self:AddTransmission(transmission)
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Convert a number (as string) into a radio transmission.
|
||||
-- E.g. for board number or headings.
|
||||
-- @param #RADIOQUEUE self
|
||||
-- @param #string number Number string, e.g. "032" or "183".
|
||||
-- @param #number delay Delay before transmission in seconds.
|
||||
-- @param #number interval Interval between the next call.
|
||||
-- @return #number Duration of the call in seconds.
|
||||
function RADIOQUEUE:Number2Transmission(number, delay, interval)
|
||||
|
||||
--- Split string into characters.
|
||||
local function _split(str)
|
||||
local chars={}
|
||||
for i=1,#str do
|
||||
local c=str:sub(i,i)
|
||||
table.insert(chars, c)
|
||||
end
|
||||
return chars
|
||||
end
|
||||
|
||||
-- Split string into characters.
|
||||
local numbers=_split(number)
|
||||
|
||||
local wait=0
|
||||
for i=1,#numbers do
|
||||
|
||||
-- Current number
|
||||
local n=numbers[i]
|
||||
|
||||
-- Radio call.
|
||||
local transmission=UTILS.DeepCopy(self.numbers[n]) --#RADIOQUEUE.Transmission
|
||||
|
||||
transmission.Tplay=timer.getAbsTime()+(delay or 0)
|
||||
|
||||
if interval and i==1 then
|
||||
transmission.interval=interval
|
||||
end
|
||||
|
||||
self:AddTransmission(transmission)
|
||||
|
||||
-- Add up duration of the number.
|
||||
wait=wait+transmission.duration
|
||||
end
|
||||
|
||||
-- Return the total duration of the call.
|
||||
return wait
|
||||
end
|
||||
|
||||
|
||||
--- Broadcast radio message.
|
||||
-- @param #RADIOQUEUE self
|
||||
-- @param #RADIOQUEUE.Transmission transmission The transmission.
|
||||
function RADIOQUEUE:Broadcast(transmission)
|
||||
|
||||
-- Get unit sending the transmission.
|
||||
local sender=self:_GetRadioSender()
|
||||
|
||||
-- Construct file name.
|
||||
local filename=string.format("%s%s", transmission.path, transmission.filename)
|
||||
|
||||
-- Create subtitle for transmission.
|
||||
--local subtitle=self:_RadioSubtitle(radio, call, loud)
|
||||
|
||||
if sender then
|
||||
|
||||
-- Broadcasting from aircraft. Only players tuned in to the right frequency will see the message.
|
||||
self:T(self.lid..string.format("Broadcasting from aircraft %s", sender:GetName()))
|
||||
|
||||
-- Command to set the Frequency for the transmission.
|
||||
local commandFrequency={
|
||||
id="SetFrequency",
|
||||
params={
|
||||
frequency=self.frequency, -- Frequency in Hz.
|
||||
modulation=self.modulation,
|
||||
}}
|
||||
|
||||
-- Command to tranmit the call.
|
||||
local commandTransmit={
|
||||
id = "TransmitMessage",
|
||||
params = {
|
||||
file=filename,
|
||||
duration=transmission.subduration or 5,
|
||||
subtitle=transmission.subtitle or "",
|
||||
loop=false,
|
||||
}}
|
||||
|
||||
-- Set commend for frequency
|
||||
sender:SetCommand(commandFrequency)
|
||||
|
||||
-- Set command for radio transmission.
|
||||
sender:SetCommand(commandTransmit)
|
||||
|
||||
else
|
||||
|
||||
-- Broadcasting from carrier. No subtitle possible. Need to send messages to players.
|
||||
self:T(self.lid..string.format("Broadcasting from carrier via trigger.action.radioTransmission()."))
|
||||
|
||||
-- Position from where to transmit.
|
||||
local vec3=nil
|
||||
|
||||
-- Try to get positon from sender unit/static.
|
||||
if self.sendername then
|
||||
local coord=self:_GetRadioSenderCoord()
|
||||
if coord then
|
||||
vec3=coord:GetVec3()
|
||||
end
|
||||
end
|
||||
|
||||
-- Try to get fixed positon.
|
||||
if self.sendercoord and not vec3 then
|
||||
vec3=self.sendercoord:GetVec3()
|
||||
end
|
||||
|
||||
-- Transmit via trigger.
|
||||
if vec3 then
|
||||
trigger.action.radioTransmission(filename, vec3, self.modulation, false, self.frequency, self.power)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
--- Check radio queue for transmissions to be broadcasted.
|
||||
-- @param #RADIOQUEUE self
|
||||
function RADIOQUEUE:_CheckRadioQueue()
|
||||
|
||||
-- Check if queue is empty.
|
||||
if #self.queue==0 then
|
||||
return
|
||||
end
|
||||
|
||||
-- Get current abs time.
|
||||
local time=timer.getAbsTime()
|
||||
|
||||
local playing=false
|
||||
local next=nil --#RADIOQUEUE.Transmission
|
||||
local remove=nil
|
||||
for i,_transmission in ipairs(self.queue) do
|
||||
local transmission=_transmission --#RADIOQUEUE.Transmission
|
||||
|
||||
-- Check if transmission time has passed.
|
||||
if time>=transmission.Tplay then
|
||||
|
||||
-- Check if transmission is currently playing.
|
||||
if transmission.isplaying then
|
||||
|
||||
-- Check if transmission is finished.
|
||||
if time>=transmission.Tstarted+transmission.duration then
|
||||
|
||||
-- Transmission over.
|
||||
transmission.isplaying=false
|
||||
|
||||
-- Remove ith element in queue.
|
||||
remove=i
|
||||
|
||||
-- Store time last transmission finished.
|
||||
self.Tlast=time
|
||||
|
||||
else -- still playing
|
||||
|
||||
-- Transmission is still playing.
|
||||
playing=true
|
||||
|
||||
end
|
||||
|
||||
else -- not playing yet
|
||||
|
||||
local Tlast=self.Tlast
|
||||
|
||||
if transmission.interval==nil then
|
||||
|
||||
-- Not playing ==> this will be next.
|
||||
if next==nil then
|
||||
next=transmission
|
||||
end
|
||||
|
||||
else
|
||||
|
||||
if Tlast==nil or time-Tlast>=transmission.interval then
|
||||
next=transmission
|
||||
else
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
-- We got a transmission or one with an interval that is not due yet. No need for anything else.
|
||||
if next or Tlast then
|
||||
break
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
else
|
||||
|
||||
-- Transmission not due yet.
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
-- Found a new transmission.
|
||||
if next~=nil and not playing then
|
||||
self:Broadcast(next)
|
||||
next.isplaying=true
|
||||
next.Tstarted=time
|
||||
end
|
||||
|
||||
-- Remove completed calls from queue.
|
||||
if remove then
|
||||
table.remove(self.queue, remove)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- Get unit from which we want to transmit a radio message. This has to be an aircraft for subtitles to work.
|
||||
-- @param #RADIOQUEUE self
|
||||
-- @return Wrapper.Unit#UNIT Sending aircraft unit or nil if was not setup, is not an aircraft or is not alive.
|
||||
function RADIOQUEUE:_GetRadioSender()
|
||||
|
||||
-- Check if we have a sending aircraft.
|
||||
local sender=nil --Wrapper.Unit#UNIT
|
||||
|
||||
-- Try the general default.
|
||||
if self.sendername then
|
||||
-- First try to find a unit
|
||||
sender=UNIT:FindByName(self.sendername)
|
||||
|
||||
-- Check that sender is alive and an aircraft.
|
||||
if sender and sender:IsAlive() and sender:IsAir() then
|
||||
return sender
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Get unit from which we want to transmit a radio message. This has to be an aircraft for subtitles to work.
|
||||
-- @param #RADIOQUEUE self
|
||||
-- @return Core.Point#COORDINATE Coordinate of the sender unit.
|
||||
function RADIOQUEUE:_GetRadioSenderCoord()
|
||||
|
||||
local vec3=nil
|
||||
|
||||
-- Try the general default.
|
||||
if self.sendername then
|
||||
|
||||
-- First try to find a unit
|
||||
local sender=UNIT:FindByName(self.sendername)
|
||||
|
||||
-- Check that sender is alive and an aircraft.
|
||||
if sender and sender:IsAlive() then
|
||||
return sender:GetCoodinate()
|
||||
end
|
||||
|
||||
-- Now try a static.
|
||||
local sender=STATIC:FindByName(self.sendername)
|
||||
|
||||
-- Check that sender is alive and an aircraft.
|
||||
if sender then
|
||||
return sender:GetCoodinate()
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
@@ -0,0 +1,483 @@
|
||||
--- **Core** - Queues Radio Transmissions.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ## Features:
|
||||
--
|
||||
-- * Managed Radio Transmissions.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Authors: funkyfranky
|
||||
--
|
||||
-- @module Core.RadioQueue
|
||||
-- @image Core_Radio.JPG
|
||||
|
||||
--- Manages radio transmissions.
|
||||
--
|
||||
-- @type RADIOQUEUE
|
||||
-- @field #string ClassName Name of the class "RADIOQUEUE".
|
||||
-- @field #string lid ID for dcs.log.
|
||||
-- @field #number frequency The radio frequency in Hz.
|
||||
-- @field #number modulation The radio modulation. Either radio.modulation.AM or radio.modulation.FM.
|
||||
-- @field Core.Scheduler#SCHEDULER scheduler The scheduler.
|
||||
-- @field #string RQid The radio queue scheduler ID.
|
||||
-- @field #table queue The queue of transmissions.
|
||||
-- @field #number Tlast Time (abs) when the last transmission finished.
|
||||
-- @field Core.Point#COORDINATE sendercoord Coordinate from where transmissions are broadcasted.
|
||||
-- @field #number sendername Name of the sending unit or static.
|
||||
-- @field Wrapper.Positionable#POSITIONABLE positionable The positionable to broadcast the message.
|
||||
-- @field #number power Power of radio station in Watts. Default 100 W.
|
||||
-- @field #table numbers Table of number transmission parameters.
|
||||
-- @extends Core.Base#BASE
|
||||
RADIOQUEUE = {
|
||||
ClassName = "RADIOQUEUE",
|
||||
lid=nil,
|
||||
frequency=nil,
|
||||
modulation=nil,
|
||||
scheduler=nil,
|
||||
RQid=nil,
|
||||
queue={},
|
||||
Tlast=nil,
|
||||
sendercoord=nil,
|
||||
sendername=nil,
|
||||
positionable=nil,
|
||||
power=100,
|
||||
numbers={},
|
||||
}
|
||||
|
||||
--- Radio queue transmission data.
|
||||
-- @type RADIOQUEUE.Transmission
|
||||
-- @field #string filename Name of the file to be transmitted.
|
||||
-- @field #string path Path in miz file where the file is located.
|
||||
-- @field #number duration Duration in seconds.
|
||||
-- @field #number Tstarted Mission time (abs) in seconds when the transmission started.
|
||||
-- @field #boolean isplaying If true, transmission is currently playing.
|
||||
-- @field #number Tplay Mission time (abs) in seconds when the transmission should be played.
|
||||
|
||||
|
||||
--- Create a new RADIOQUEUE object for a given radio frequency/modulation.
|
||||
-- @param #RADIOQUEUE self
|
||||
-- @param #number frequency The radio frequency in MHz.
|
||||
-- @param #number modulation (Optional) The radio modulation. Default radio.modulation.AM.
|
||||
-- @return #RADIOQUEUE self The RADIOQUEUE object.
|
||||
function RADIOQUEUE:New(frequency, modulation)
|
||||
|
||||
-- Inherit base
|
||||
local self=BASE:Inherit(self, BASE:New()) -- #RADIOQUEUE
|
||||
|
||||
self.lid="RADIOQUEUE | "
|
||||
|
||||
if frequency==nil then
|
||||
self:E(self.lid.."ERROR: No frequency specified as first parameter!")
|
||||
return nil
|
||||
end
|
||||
|
||||
-- Frequency in Hz.
|
||||
self.frequency=frequency*1000000
|
||||
|
||||
-- Modulation.
|
||||
self.modulation=modulation or radio.modulation.AM
|
||||
|
||||
-- Scheduler
|
||||
self.scheduler=SCHEDULER:New()
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Start the radio queue.
|
||||
-- @param #RADIOQUEUE self
|
||||
-- @param #number delay (Optional) Delay in seconds, before the radio queue is started. Default 1 sec.
|
||||
-- @param #number dt (Optional) Time step in seconds for checking the queue. Default 0.01 sec.
|
||||
-- @return #RADIOQUEUE self The RADIOQUEUE object.
|
||||
function RADIOQUEUE:Start(delay, dt)
|
||||
|
||||
delay=delay or 1
|
||||
|
||||
dt=dt or 0.01
|
||||
|
||||
self:I(self.lid..string.format("Starting RADIOQUEUE in %.1f seconds with interval dt=%.3f seconds.", delay, dt))
|
||||
|
||||
self.RQid=self.scheduler:Schedule(self, self._CheckRadioQueue, {}, delay, dt)
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Stop the radio queue. Stop scheduler and delete queue.
|
||||
-- @param #RADIOQUEUE self
|
||||
-- @return #RADIOQUEUE self The RADIOQUEUE object.
|
||||
function RADIOQUEUE:Stop()
|
||||
self:I(self.lid.."Stopping RADIOQUEUE.")
|
||||
self.scheduler:Stop(self.RQid)
|
||||
self.queue={}
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set coordinate from where the transmission is broadcasted.
|
||||
-- @param #RADIOQUEUE self
|
||||
-- @param Core.Point#COORDINATE coordinate Coordinate of the sender.
|
||||
-- @return #RADIOQUEUE self The RADIOQUEUE object.
|
||||
function RADIOQUEUE:SetSenderCoordinate(coordinate)
|
||||
self.sendercoord=coordinate
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set name of unit or static from which transmissions are made.
|
||||
-- @param #RADIOQUEUE self
|
||||
-- @param #string name Name of the unit or static used for transmissions.
|
||||
-- @return #RADIOQUEUE self The RADIOQUEUE object.
|
||||
function RADIOQUEUE:SetSenderUnitName(name)
|
||||
self.sendername=name
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set parameters of a digit.
|
||||
-- @param #RADIOQUEUE self
|
||||
-- @param #number digit The digit 0-9.
|
||||
-- @param #string filename The name of the sound file.
|
||||
-- @param #number duration The duration of the sound file in seconds.
|
||||
-- @param #string path The directory within the miz file where the sound is located. Default "l10n/DEFAULT/".
|
||||
-- @return #RADIOQUEUE self The RADIOQUEUE object.
|
||||
function RADIOQUEUE:SetDigit(digit, filename, duration, path)
|
||||
|
||||
local transmission={} --#RADIOQUEUE.Transmission
|
||||
transmission.filename=filename
|
||||
transmission.duration=duration
|
||||
transmission.path=path or "l10n/DEFAULT/"
|
||||
|
||||
-- Convert digit to string in case it is given as a number.
|
||||
if type(digit)=="number" then
|
||||
digit=tostring(digit)
|
||||
end
|
||||
|
||||
-- Set transmission.
|
||||
self.numbers[digit]=transmission
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Add a transmission to the radio queue.
|
||||
-- @param #RADIOQUEUE self
|
||||
-- @param #RADIOQUEUE.Transmission transmission The transmission data table.
|
||||
-- @return #RADIOQUEUE self The RADIOQUEUE object.
|
||||
function RADIOQUEUE:AddTransmission(transmission)
|
||||
self:F({transmission=transmission})
|
||||
|
||||
-- Init.
|
||||
transmission.isplaying=false
|
||||
transmission.Tstarted=nil
|
||||
|
||||
-- Add to queue.
|
||||
table.insert(self.queue, transmission)
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Add a transmission to the radio queue.
|
||||
-- @param #RADIOQUEUE self
|
||||
-- @param #string filename Name of the sound file. Usually an ogg or wav file type.
|
||||
-- @param #number duration Duration in seconds the file lasts.
|
||||
-- @param #number path Directory path inside the miz file where the sound file is located. Default "l10n/DEFAULT/".
|
||||
-- @param #number tstart Start time (abs) seconds. Default now.
|
||||
-- @param #number interval Interval in seconds after the last transmission finished.
|
||||
-- @return #RADIOQUEUE self The RADIOQUEUE object.
|
||||
function RADIOQUEUE:NewTransmission(filename, duration, path, tstart, interval)
|
||||
|
||||
-- Sanity checks.
|
||||
if not filename then
|
||||
self:E(self.lid.."ERROR: No filename specified.")
|
||||
return nil
|
||||
end
|
||||
if type(filename)~="string" then
|
||||
self:E(self.lid.."ERROR: Filename specified is NOT a string.")
|
||||
return nil
|
||||
end
|
||||
|
||||
if not duration then
|
||||
self:E(self.lid.."ERROR: No duration of transmission specified.")
|
||||
return nil
|
||||
end
|
||||
if type(duration)~="number" then
|
||||
self:E(self.lid.."ERROR: Duration specified is NOT a number.")
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
local transmission={} --#RADIOQUEUE.Transmission
|
||||
transmission.filename=filename
|
||||
transmission.duration=duration
|
||||
transmission.path=path or "l10n/DEFAULT/"
|
||||
transmission.Tplay=tstart or timer.getAbsTime()
|
||||
|
||||
-- Add transmission to queue.
|
||||
self:AddTransmission(transmission)
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Convert a number (as string) into a radio transmission.
|
||||
-- E.g. for board number or headings.
|
||||
-- @param #RADIOQUEUE self
|
||||
-- @param #string number Number string, e.g. "032" or "183".
|
||||
-- @param #number delay Delay before transmission in seconds.
|
||||
-- @param #number interval Interval between the next call.
|
||||
-- @return #number Duration of the call in seconds.
|
||||
function RADIOQUEUE:Number2Transmission(number, delay, interval)
|
||||
|
||||
--- Split string into characters.
|
||||
local function _split(str)
|
||||
local chars={}
|
||||
for i=1,#str do
|
||||
local c=str:sub(i,i)
|
||||
table.insert(chars, c)
|
||||
end
|
||||
return chars
|
||||
end
|
||||
|
||||
-- Split string into characters.
|
||||
local numbers=_split(number)
|
||||
|
||||
local wait=0
|
||||
for i=1,#numbers do
|
||||
|
||||
-- Current number
|
||||
local n=numbers[i]
|
||||
|
||||
-- Radio call.
|
||||
local transmission=UTILS.DeepCopy(self.numbers[n]) --#RADIOQUEUE.Transmission
|
||||
|
||||
transmission.Tplay=timer.getAbsTime()+(delay or 0)
|
||||
|
||||
if interval and i==1 then
|
||||
transmission.interval=interval
|
||||
end
|
||||
|
||||
self:AddTransmission(transmission)
|
||||
|
||||
-- Add up duration of the number.
|
||||
wait=wait+transmission.duration
|
||||
end
|
||||
|
||||
-- Return the total duration of the call.
|
||||
return wait
|
||||
end
|
||||
|
||||
|
||||
--- Broadcast radio message.
|
||||
-- @param #RADIOQUEUE self
|
||||
-- @param #RADIOQUEUE.Transmission transmission The transmission.
|
||||
function RADIOQUEUE:Broadcast(transmission)
|
||||
|
||||
-- Get unit sending the transmission.
|
||||
local sender=self:_GetRadioSender()
|
||||
|
||||
-- Construct file name.
|
||||
local filename=string.format("%s%s", transmission.path, transmission.filename)
|
||||
|
||||
-- Create subtitle for transmission.
|
||||
--local subtitle=self:_RadioSubtitle(radio, call, loud)
|
||||
|
||||
if sender then
|
||||
|
||||
-- Broadcasting from aircraft. Only players tuned in to the right frequency will see the message.
|
||||
self:T(self.lid..string.format("Broadcasting from aircraft %s", sender:GetName()))
|
||||
|
||||
-- Command to set the Frequency for the transmission.
|
||||
local commandFrequency={
|
||||
id="SetFrequency",
|
||||
params={
|
||||
frequency=self.frequency, -- Frequency in Hz.
|
||||
modulation=self.modulation,
|
||||
}}
|
||||
|
||||
-- Command to tranmit the call.
|
||||
local commandTransmit={
|
||||
id = "TransmitMessage",
|
||||
params = {
|
||||
file=filename,
|
||||
duration=transmission.subduration or 5,
|
||||
subtitle=transmission.subtitle or "",
|
||||
loop=false,
|
||||
}}
|
||||
|
||||
-- Set commend for frequency
|
||||
sender:SetCommand(commandFrequency)
|
||||
|
||||
-- Set command for radio transmission.
|
||||
sender:SetCommand(commandTransmit)
|
||||
|
||||
else
|
||||
|
||||
-- Broadcasting from carrier. No subtitle possible. Need to send messages to players.
|
||||
self:T(self.lid..string.format("Broadcasting from carrier via trigger.action.radioTransmission()."))
|
||||
|
||||
-- Position from where to transmit.
|
||||
local vec3=nil
|
||||
|
||||
-- Try to get positon from sender unit/static.
|
||||
if self.sendername then
|
||||
local coord=self:_GetRadioSenderCoord()
|
||||
if coord then
|
||||
vec3=coord:GetVec3()
|
||||
end
|
||||
end
|
||||
|
||||
-- Try to get fixed positon.
|
||||
if self.sendercoord and not vec3 then
|
||||
vec3=self.sendercoord:GetVec3()
|
||||
end
|
||||
|
||||
-- Transmit via trigger.
|
||||
if vec3 then
|
||||
self:E("Sending")
|
||||
self:E( { filename = filename, vec3 = vec3, modulation = self.modulation, frequency = self.frequency, power = self.power } )
|
||||
trigger.action.radioTransmission(filename, vec3, self.modulation, false, self.frequency, self.power)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
--- Check radio queue for transmissions to be broadcasted.
|
||||
-- @param #RADIOQUEUE self
|
||||
function RADIOQUEUE:_CheckRadioQueue()
|
||||
|
||||
-- Check if queue is empty.
|
||||
if #self.queue==0 then
|
||||
return
|
||||
end
|
||||
|
||||
-- Get current abs time.
|
||||
local time=timer.getAbsTime()
|
||||
|
||||
local playing=false
|
||||
local next=nil --#RADIOQUEUE.Transmission
|
||||
local remove=nil
|
||||
for i,_transmission in ipairs(self.queue) do
|
||||
local transmission=_transmission --#RADIOQUEUE.Transmission
|
||||
|
||||
-- Check if transmission time has passed.
|
||||
if time>=transmission.Tplay then
|
||||
|
||||
-- Check if transmission is currently playing.
|
||||
if transmission.isplaying then
|
||||
|
||||
-- Check if transmission is finished.
|
||||
if time>=transmission.Tstarted+transmission.duration then
|
||||
|
||||
-- Transmission over.
|
||||
transmission.isplaying=false
|
||||
|
||||
-- Remove ith element in queue.
|
||||
remove=i
|
||||
|
||||
-- Store time last transmission finished.
|
||||
self.Tlast=time
|
||||
|
||||
else -- still playing
|
||||
|
||||
-- Transmission is still playing.
|
||||
playing=true
|
||||
|
||||
end
|
||||
|
||||
else -- not playing yet
|
||||
|
||||
local Tlast=self.Tlast
|
||||
|
||||
if transmission.interval==nil then
|
||||
|
||||
-- Not playing ==> this will be next.
|
||||
if next==nil then
|
||||
next=transmission
|
||||
end
|
||||
|
||||
else
|
||||
|
||||
if Tlast==nil or time-Tlast>=transmission.interval then
|
||||
next=transmission
|
||||
else
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
-- We got a transmission or one with an interval that is not due yet. No need for anything else.
|
||||
if next or Tlast then
|
||||
break
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
else
|
||||
|
||||
-- Transmission not due yet.
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
-- Found a new transmission.
|
||||
if next~=nil and not playing then
|
||||
self:Broadcast(next)
|
||||
next.isplaying=true
|
||||
next.Tstarted=time
|
||||
end
|
||||
|
||||
-- Remove completed calls from queue.
|
||||
if remove then
|
||||
table.remove(self.queue, remove)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- Get unit from which we want to transmit a radio message. This has to be an aircraft for subtitles to work.
|
||||
-- @param #RADIOQUEUE self
|
||||
-- @return Wrapper.Unit#UNIT Sending aircraft unit or nil if was not setup, is not an aircraft or is not alive.
|
||||
function RADIOQUEUE:_GetRadioSender()
|
||||
|
||||
-- Check if we have a sending aircraft.
|
||||
local sender=nil --Wrapper.Unit#UNIT
|
||||
|
||||
-- Try the general default.
|
||||
if self.sendername then
|
||||
-- First try to find a unit
|
||||
sender=UNIT:FindByName(self.sendername)
|
||||
|
||||
-- Check that sender is alive and an aircraft.
|
||||
if sender and sender:IsAlive() and sender:IsAir() then
|
||||
return sender
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Get unit from which we want to transmit a radio message. This has to be an aircraft for subtitles to work.
|
||||
-- @param #RADIOQUEUE self
|
||||
-- @return Core.Point#COORDINATE Coordinate of the sender unit.
|
||||
function RADIOQUEUE:_GetRadioSenderCoord()
|
||||
|
||||
local vec3=nil
|
||||
|
||||
-- Try the general default.
|
||||
if self.sendername then
|
||||
|
||||
-- First try to find a unit
|
||||
local sender=UNIT:FindByName(self.sendername)
|
||||
|
||||
-- Check that sender is alive and an aircraft.
|
||||
if sender and sender:IsAlive() then
|
||||
return sender:GetCoordinate()
|
||||
end
|
||||
|
||||
-- Now try a static.
|
||||
local sender=STATIC:FindByName(self.sendername)
|
||||
|
||||
-- Check that sender is alive and an aircraft.
|
||||
if sender then
|
||||
return sender:GetCoordinate()
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
@@ -52,8 +52,8 @@ end
|
||||
-- Nothing of this code should be modified without testing it thoroughly.
|
||||
-- @param #SCHEDULEDISPATCHER self
|
||||
-- @param Core.Scheduler#SCHEDULER Scheduler
|
||||
function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleArguments, Start, Repeat, Randomize, Stop )
|
||||
self:F2( { Scheduler, ScheduleFunction, ScheduleArguments, Start, Repeat, Randomize, Stop } )
|
||||
function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleArguments, Start, Repeat, Randomize, Stop, TraceLevel, Fsm )
|
||||
self:F2( { Scheduler, ScheduleFunction, ScheduleArguments, Start, Repeat, Randomize, Stop, TraceLevel } )
|
||||
|
||||
self.CallID = self.CallID + 1
|
||||
local CallID = self.CallID .. "#" .. ( Scheduler.MasterObject and Scheduler.MasterObject.GetClassNameAndID and Scheduler.MasterObject:GetClassNameAndID() or "" ) or ""
|
||||
@@ -84,11 +84,52 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr
|
||||
self.Schedule[Scheduler][CallID].Repeat = Repeat or 0
|
||||
self.Schedule[Scheduler][CallID].Randomize = Randomize or 0
|
||||
self.Schedule[Scheduler][CallID].Stop = Stop
|
||||
|
||||
|
||||
-- This section handles the tracing of the scheduled calls.
|
||||
-- Because these calls will be executed with a delay, we inspect the place where these scheduled calls are initiated.
|
||||
-- The Info structure contains the output of the debug.getinfo() calls, which inspects the call stack for the function name, line number and source name.
|
||||
-- The call stack has many levels, and the correct semantical function call depends on where in the code AddSchedule was "used".
|
||||
-- - Using SCHEDULER:New()
|
||||
-- - Using Schedule:AddSchedule()
|
||||
-- - Using Fsm:__Func()
|
||||
-- - Using Class:ScheduleOnce()
|
||||
-- - Using Class:ScheduleRepeat()
|
||||
-- - ...
|
||||
-- So for each of these scheduled call variations, AddSchedule is the workhorse which will schedule the call.
|
||||
-- But the correct level with the correct semantical function location will differ depending on the above scheduled call invocation forms.
|
||||
-- That's where the field TraceLevel contains optionally the level in the call stack where the call information is obtained.
|
||||
-- The TraceLevel field indicates the correct level where the semantical scheduled call was invoked within the source, ensuring that function name, line number and source name are correct.
|
||||
-- There is one quick ...
|
||||
-- The FSM class models scheduled calls using the __Func syntax. However, these functions are "tailed".
|
||||
-- There aren't defined anywhere within the source code, but rather implemented as triggers within the FSM logic,
|
||||
-- and using the onbefore, onafter, onenter, onleave prefixes. (See the FSM for details).
|
||||
-- Therefore, in the call stack, at the TraceLevel these functions are mentioned as "tail calls", and the Info.name field will be nil as a result.
|
||||
-- To obtain the correct function name for FSM object calls, the function is mentioned in the call stack at a higher stack level.
|
||||
-- So when function name stored in Info.name is nil, then I inspect the function name within the call stack one level higher.
|
||||
-- So this little piece of code does its magic wonderfully, preformance overhead is neglectible, as scheduled calls don't happen that often.
|
||||
|
||||
local Info = {}
|
||||
|
||||
if debug then
|
||||
TraceLevel = TraceLevel or 2
|
||||
Info = debug.getinfo( TraceLevel, "nlS" )
|
||||
local name_fsm = debug.getinfo( TraceLevel - 1, "n" ).name -- #string
|
||||
if name_fsm then
|
||||
Info.name = name_fsm
|
||||
end
|
||||
--env.info( debug.traceback() )
|
||||
end
|
||||
|
||||
self:T3( self.Schedule[Scheduler][CallID] )
|
||||
|
||||
self.Schedule[Scheduler][CallID].CallHandler = function( CallID )
|
||||
--self:E( CallID )
|
||||
self.Schedule[Scheduler][CallID].CallHandler = function( Params )
|
||||
|
||||
local CallID = Params.CallID
|
||||
local Info = Params.Info
|
||||
local Source = Info.source or "?"
|
||||
local Line = Info.currentline or "?"
|
||||
local Name = Info.name or "?"
|
||||
|
||||
local ErrorHandler = function( errmsg )
|
||||
env.info( "Error in timer function: " .. errmsg )
|
||||
@@ -122,15 +163,19 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr
|
||||
local Stop = Schedule.Stop or 0
|
||||
local ScheduleID = Schedule.ScheduleID
|
||||
|
||||
local Prefix = ( Repeat == 0 ) and "--->" or "+++>"
|
||||
|
||||
local Status, Result
|
||||
--self:E( { SchedulerObject = SchedulerObject } )
|
||||
if SchedulerObject then
|
||||
local function Timer()
|
||||
SchedulerObject:T( Prefix .. Name .. ":" .. Line .. " (" .. Source .. ")" )
|
||||
return ScheduleFunction( SchedulerObject, unpack( ScheduleArguments ) )
|
||||
end
|
||||
Status, Result = xpcall( Timer, ErrorHandler )
|
||||
else
|
||||
local function Timer()
|
||||
self:T( Prefix .. Name .. ":" .. Line .. " (" .. Source .. ")" )
|
||||
return ScheduleFunction( unpack( ScheduleArguments ) )
|
||||
end
|
||||
Status, Result = xpcall( Timer, ErrorHandler )
|
||||
@@ -161,13 +206,13 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr
|
||||
self:Stop( Scheduler, CallID )
|
||||
end
|
||||
else
|
||||
self:E( "Scheduled obsolete call for CallID: " .. CallID )
|
||||
self:I( "<<<>" .. Name .. ":" .. Line .. " (" .. Source .. ")" )
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
self:Start( Scheduler, CallID )
|
||||
self:Start( Scheduler, CallID, Info )
|
||||
|
||||
return CallID
|
||||
end
|
||||
@@ -181,7 +226,7 @@ function SCHEDULEDISPATCHER:RemoveSchedule( Scheduler, CallID )
|
||||
end
|
||||
end
|
||||
|
||||
function SCHEDULEDISPATCHER:Start( Scheduler, CallID )
|
||||
function SCHEDULEDISPATCHER:Start( Scheduler, CallID, Info )
|
||||
self:F2( { Start = CallID, Scheduler = Scheduler } )
|
||||
|
||||
if CallID then
|
||||
@@ -192,13 +237,13 @@ function SCHEDULEDISPATCHER:Start( Scheduler, CallID )
|
||||
Schedule[CallID].StartTime = timer.getTime() -- Set the StartTime field to indicate when the scheduler started.
|
||||
Schedule[CallID].ScheduleID = timer.scheduleFunction(
|
||||
Schedule[CallID].CallHandler,
|
||||
CallID,
|
||||
{ CallID = CallID, Info = Info },
|
||||
timer.getTime() + Schedule[CallID].Start
|
||||
)
|
||||
end
|
||||
else
|
||||
for CallID, Schedule in pairs( self.Schedule[Scheduler] or {} ) do
|
||||
self:Start( Scheduler, CallID ) -- Recursive
|
||||
self:Start( Scheduler, CallID, Info ) -- Recursive
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -216,7 +216,7 @@ function SCHEDULER:New( SchedulerObject, SchedulerFunction, SchedulerArguments,
|
||||
self.MasterObject = SchedulerObject
|
||||
|
||||
if SchedulerFunction then
|
||||
ScheduleID = self:Schedule( SchedulerObject, SchedulerFunction, SchedulerArguments, Start, Repeat, RandomizeFactor, Stop )
|
||||
ScheduleID = self:Schedule( SchedulerObject, SchedulerFunction, SchedulerArguments, Start, Repeat, RandomizeFactor, Stop, 4 )
|
||||
end
|
||||
|
||||
return self, ScheduleID
|
||||
@@ -238,7 +238,7 @@ end
|
||||
-- @param #number RandomizeFactor Specifies a randomization factor between 0 and 1 to randomize the Repeat.
|
||||
-- @param #number Stop Specifies the amount of seconds when the scheduler will be stopped.
|
||||
-- @return #number The ScheduleID of the planned schedule.
|
||||
function SCHEDULER:Schedule( SchedulerObject, SchedulerFunction, SchedulerArguments, Start, Repeat, RandomizeFactor, Stop )
|
||||
function SCHEDULER:Schedule( SchedulerObject, SchedulerFunction, SchedulerArguments, Start, Repeat, RandomizeFactor, Stop, TraceLevel, Fsm )
|
||||
self:F2( { Start, Repeat, RandomizeFactor, Stop } )
|
||||
self:T3( { SchedulerArguments } )
|
||||
|
||||
@@ -256,7 +256,9 @@ function SCHEDULER:Schedule( SchedulerObject, SchedulerFunction, SchedulerArgume
|
||||
Start,
|
||||
Repeat,
|
||||
RandomizeFactor,
|
||||
Stop
|
||||
Stop,
|
||||
TraceLevel or 3,
|
||||
Fsm
|
||||
)
|
||||
|
||||
self.Schedules[#self.Schedules+1] = ScheduleID
|
||||
|
||||
@@ -384,7 +384,6 @@ do -- SET_BASE
|
||||
for ObjectName, Object in pairs( self.Database ) do
|
||||
|
||||
if self:IsIncludeObject( Object ) then
|
||||
self:E( { "Adding Object:", ObjectName } )
|
||||
self:Add( ObjectName, Object )
|
||||
end
|
||||
end
|
||||
@@ -5497,4 +5496,324 @@ do -- SET_ZONE
|
||||
return nil
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
do -- SET_ZONE_GOAL
|
||||
|
||||
--- @type SET_ZONE_GOAL
|
||||
-- @extends Core.Set#SET_BASE
|
||||
|
||||
--- Mission designers can use the @{Core.Set#SET_ZONE_GOAL} class to build sets of zones of various types.
|
||||
--
|
||||
-- ## SET_ZONE_GOAL constructor
|
||||
--
|
||||
-- Create a new SET_ZONE_GOAL object with the @{#SET_ZONE_GOAL.New} method:
|
||||
--
|
||||
-- * @{#SET_ZONE_GOAL.New}: Creates a new SET_ZONE_GOAL object.
|
||||
--
|
||||
-- ## Add or Remove ZONEs from SET_ZONE_GOAL
|
||||
--
|
||||
-- ZONEs can be added and removed using the @{Core.Set#SET_ZONE_GOAL.AddZonesByName} and @{Core.Set#SET_ZONE_GOAL.RemoveZonesByName} respectively.
|
||||
-- These methods take a single ZONE name or an array of ZONE names to be added or removed from SET_ZONE_GOAL.
|
||||
--
|
||||
-- ## SET_ZONE_GOAL filter criteria
|
||||
--
|
||||
-- You can set filter criteria to build the collection of zones in SET_ZONE_GOAL.
|
||||
-- Filter criteria are defined by:
|
||||
--
|
||||
-- * @{#SET_ZONE_GOAL.FilterPrefixes}: Builds the SET_ZONE_GOAL with the zones having a certain text pattern of prefix.
|
||||
--
|
||||
-- Once the filter criteria have been set for the SET_ZONE_GOAL, you can start filtering using:
|
||||
--
|
||||
-- * @{#SET_ZONE_GOAL.FilterStart}: Starts the filtering of the zones within the SET_ZONE_GOAL.
|
||||
--
|
||||
-- ## SET_ZONE_GOAL iterators
|
||||
--
|
||||
-- Once the filters have been defined and the SET_ZONE_GOAL has been built, you can iterate the SET_ZONE_GOAL with the available iterator methods.
|
||||
-- The iterator methods will walk the SET_ZONE_GOAL set, and call for each airbase within the set a function that you provide.
|
||||
-- The following iterator methods are currently available within the SET_ZONE_GOAL:
|
||||
--
|
||||
-- * @{#SET_ZONE_GOAL.ForEachZone}: Calls a function for each zone it finds within the SET_ZONE_GOAL.
|
||||
--
|
||||
-- ===
|
||||
-- @field #SET_ZONE_GOAL SET_ZONE_GOAL
|
||||
SET_ZONE_GOAL = {
|
||||
ClassName = "SET_ZONE_GOAL",
|
||||
Zones = {},
|
||||
Filter = {
|
||||
Prefixes = nil,
|
||||
},
|
||||
FilterMeta = {
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
--- Creates a new SET_ZONE_GOAL object, building a set of zones.
|
||||
-- @param #SET_ZONE_GOAL self
|
||||
-- @return #SET_ZONE_GOAL self
|
||||
-- @usage
|
||||
-- -- Define a new SET_ZONE_GOAL Object. The DatabaseSet will contain a reference to all Zones.
|
||||
-- DatabaseSet = SET_ZONE_GOAL:New()
|
||||
function SET_ZONE_GOAL:New()
|
||||
-- Inherits from BASE
|
||||
local self = BASE:Inherit( self, SET_BASE:New( _DATABASE.ZONES_GOAL ) )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Add ZONEs to SET_ZONE_GOAL.
|
||||
-- @param Core.Set#SET_ZONE_GOAL self
|
||||
-- @param Core.Zone#ZONE_BASE Zone A ZONE_BASE object.
|
||||
-- @return self
|
||||
function SET_ZONE_GOAL:AddZone( Zone )
|
||||
|
||||
self:Add( Zone:GetName(), Zone )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Remove ZONEs from SET_ZONE_GOAL.
|
||||
-- @param Core.Set#SET_ZONE_GOAL self
|
||||
-- @param Core.Zone#ZONE_BASE RemoveZoneNames A single name or an array of ZONE_BASE names.
|
||||
-- @return self
|
||||
function SET_ZONE_GOAL:RemoveZonesByName( RemoveZoneNames )
|
||||
|
||||
local RemoveZoneNamesArray = ( type( RemoveZoneNames ) == "table" ) and RemoveZoneNames or { RemoveZoneNames }
|
||||
|
||||
for RemoveZoneID, RemoveZoneName in pairs( RemoveZoneNamesArray ) do
|
||||
self:Remove( RemoveZoneName )
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Finds a Zone based on the Zone Name.
|
||||
-- @param #SET_ZONE_GOAL self
|
||||
-- @param #string ZoneName
|
||||
-- @return Core.Zone#ZONE_BASE The found Zone.
|
||||
function SET_ZONE_GOAL:FindZone( ZoneName )
|
||||
|
||||
local ZoneFound = self.Set[ZoneName]
|
||||
return ZoneFound
|
||||
end
|
||||
|
||||
|
||||
--- Get a random zone from the set.
|
||||
-- @param #SET_ZONE_GOAL self
|
||||
-- @return Core.Zone#ZONE_BASE The random Zone.
|
||||
-- @return #nil if no zone in the collection.
|
||||
function SET_ZONE_GOAL:GetRandomZone()
|
||||
|
||||
if self:Count() ~= 0 then
|
||||
|
||||
local Index = self.Index
|
||||
local ZoneFound = nil -- Core.Zone#ZONE_BASE
|
||||
|
||||
-- Loop until a zone has been found.
|
||||
-- The :GetZoneMaybe() call will evaluate the probability for the zone to be selected.
|
||||
-- If the zone is not selected, then nil is returned by :GetZoneMaybe() and the loop continues!
|
||||
while not ZoneFound do
|
||||
local ZoneRandom = math.random( 1, #Index )
|
||||
ZoneFound = self.Set[Index[ZoneRandom]]:GetZoneMaybe()
|
||||
end
|
||||
|
||||
return ZoneFound
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
--- Set a zone probability.
|
||||
-- @param #SET_ZONE_GOAL self
|
||||
-- @param #string ZoneName The name of the zone.
|
||||
function SET_ZONE_GOAL:SetZoneProbability( ZoneName, ZoneProbability )
|
||||
local Zone = self:FindZone( ZoneName )
|
||||
Zone:SetZoneProbability( ZoneProbability )
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
--- Builds a set of zones of defined zone prefixes.
|
||||
-- All the zones starting with the given prefixes will be included within the set.
|
||||
-- @param #SET_ZONE_GOAL self
|
||||
-- @param #string Prefixes The prefix of which the zone name starts with.
|
||||
-- @return #SET_ZONE_GOAL self
|
||||
function SET_ZONE_GOAL:FilterPrefixes( Prefixes )
|
||||
if not self.Filter.Prefixes then
|
||||
self.Filter.Prefixes = {}
|
||||
end
|
||||
if type( Prefixes ) ~= "table" then
|
||||
Prefixes = { Prefixes }
|
||||
end
|
||||
for PrefixID, Prefix in pairs( Prefixes ) do
|
||||
self.Filter.Prefixes[Prefix] = Prefix
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Starts the filtering.
|
||||
-- @param #SET_ZONE_GOAL self
|
||||
-- @return #SET_ZONE_GOAL self
|
||||
function SET_ZONE_GOAL:FilterStart()
|
||||
|
||||
if _DATABASE then
|
||||
|
||||
-- We initialize the first set.
|
||||
for ObjectName, Object in pairs( self.Database ) do
|
||||
if self:IsIncludeObject( Object ) then
|
||||
self:Add( ObjectName, Object )
|
||||
else
|
||||
self:RemoveZonesByName( ObjectName )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
self:HandleEvent( EVENTS.NewZoneGoal )
|
||||
self:HandleEvent( EVENTS.DeleteZoneGoal )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Stops the filtering for the defined collection.
|
||||
-- @param #SET_ZONE_GOAL self
|
||||
-- @return #SET_ZONE_GOAL self
|
||||
function SET_ZONE_GOAL:FilterStop()
|
||||
|
||||
self:UnHandleEvent( EVENTS.NewZoneGoal )
|
||||
self:UnHandleEvent( EVENTS.DeleteZoneGoal )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Handles the Database to check on an event (birth) that the Object was added in the Database.
|
||||
-- This is required, because sometimes the _DATABASE birth event gets called later than the SET_BASE birth event!
|
||||
-- @param #SET_ZONE_GOAL self
|
||||
-- @param Core.Event#EVENTDATA Event
|
||||
-- @return #string The name of the AIRBASE
|
||||
-- @return #table The AIRBASE
|
||||
function SET_ZONE_GOAL:AddInDatabase( Event )
|
||||
self:F3( { Event } )
|
||||
|
||||
return Event.IniDCSUnitName, self.Database[Event.IniDCSUnitName]
|
||||
end
|
||||
|
||||
--- Handles the Database to check on any event that Object exists in the Database.
|
||||
-- This is required, because sometimes the _DATABASE event gets called later than the SET_BASE event or vise versa!
|
||||
-- @param #SET_ZONE_GOAL self
|
||||
-- @param Core.Event#EVENTDATA Event
|
||||
-- @return #string The name of the AIRBASE
|
||||
-- @return #table The AIRBASE
|
||||
function SET_ZONE_GOAL:FindInDatabase( Event )
|
||||
self:F3( { Event } )
|
||||
|
||||
return Event.IniDCSUnitName, self.Database[Event.IniDCSUnitName]
|
||||
end
|
||||
|
||||
--- Iterate the SET_ZONE_GOAL and call an interator function for each ZONE, providing the ZONE and optional parameters.
|
||||
-- @param #SET_ZONE_GOAL self
|
||||
-- @param #function IteratorFunction The function that will be called when there is an alive ZONE in the SET_ZONE_GOAL. The function needs to accept a AIRBASE parameter.
|
||||
-- @return #SET_ZONE_GOAL self
|
||||
function SET_ZONE_GOAL:ForEachZone( IteratorFunction, ... )
|
||||
self:F2( arg )
|
||||
|
||||
self:ForEach( IteratorFunction, arg, self:GetSet() )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
---
|
||||
-- @param #SET_ZONE_GOAL self
|
||||
-- @param Core.Zone#ZONE_BASE MZone
|
||||
-- @return #SET_ZONE_GOAL self
|
||||
function SET_ZONE_GOAL:IsIncludeObject( MZone )
|
||||
self:F2( MZone )
|
||||
|
||||
local MZoneInclude = true
|
||||
|
||||
if MZone then
|
||||
local MZoneName = MZone:GetName()
|
||||
|
||||
if self.Filter.Prefixes then
|
||||
local MZonePrefix = false
|
||||
for ZonePrefixId, ZonePrefix in pairs( self.Filter.Prefixes ) do
|
||||
self:T3( { "Prefix:", string.find( MZoneName, ZonePrefix, 1 ), ZonePrefix } )
|
||||
if string.find( MZoneName, ZonePrefix, 1 ) then
|
||||
MZonePrefix = true
|
||||
end
|
||||
end
|
||||
self:T( { "Evaluated Prefix", MZonePrefix } )
|
||||
MZoneInclude = MZoneInclude and MZonePrefix
|
||||
end
|
||||
end
|
||||
|
||||
self:T2( MZoneInclude )
|
||||
return MZoneInclude
|
||||
end
|
||||
|
||||
--- Handles the OnEventNewZone event for the Set.
|
||||
-- @param #SET_ZONE_GOAL self
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
function SET_ZONE_GOAL:OnEventNewZoneGoal( EventData )
|
||||
|
||||
self:I( { "New Zone Capture Coalition", EventData } )
|
||||
self:I( { "Zone Capture Coalition", EventData.ZoneGoal } )
|
||||
|
||||
if EventData.ZoneGoal then
|
||||
if EventData.ZoneGoal and self:IsIncludeObject( EventData.ZoneGoal ) then
|
||||
self:I( { "Adding Zone Capture Coalition", EventData.ZoneGoal.ZoneName, EventData.ZoneGoal } )
|
||||
self:Add( EventData.ZoneGoal.ZoneName , EventData.ZoneGoal )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Handles the OnDead or OnCrash event for alive units set.
|
||||
-- @param #SET_ZONE_GOAL self
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
function SET_ZONE_GOAL:OnEventDeleteZoneGoal( EventData ) --R2.1
|
||||
self:F3( { EventData } )
|
||||
|
||||
if EventData.ZoneGoal then
|
||||
local Zone = _DATABASE:FindZone( EventData.ZoneGoal.ZoneName )
|
||||
if Zone and Zone.ZoneName then
|
||||
|
||||
-- When cargo was deleted, it may probably be because of an S_EVENT_DEAD.
|
||||
-- However, in the loading logic, an S_EVENT_DEAD is also generated after a Destroy() call.
|
||||
-- And this is a problem because it will remove all entries from the SET_ZONE_GOALs.
|
||||
-- To prevent this from happening, the Zone object has a flag NoDestroy.
|
||||
-- When true, the SET_ZONE_GOAL won't Remove the Zone object from the set.
|
||||
-- This flag is switched off after the event handlers have been called in the EVENT class.
|
||||
self:F( { ZoneNoDestroy=Zone.NoDestroy } )
|
||||
if Zone.NoDestroy then
|
||||
else
|
||||
self:Remove( Zone.ZoneName )
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Validate if a coordinate is in one of the zones in the set.
|
||||
-- Returns the ZONE object where the coordiante is located.
|
||||
-- If zones overlap, the first zone that validates the test is returned.
|
||||
-- @param #SET_ZONE_GOAL self
|
||||
-- @param Core.Point#COORDINATE Coordinate The coordinate to be searched.
|
||||
-- @return Core.Zone#ZONE_BASE The zone that validates the coordinate location.
|
||||
-- @return #nil No zone has been found.
|
||||
function SET_ZONE_GOAL:IsCoordinateInZone( Coordinate )
|
||||
|
||||
for _, Zone in pairs( self:GetSet() ) do
|
||||
local Zone = Zone -- Core.Zone#ZONE_BASE
|
||||
if Zone:IsCoordinateInZone( Coordinate ) then
|
||||
return Zone
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
--- **Core** - Manages various settings for running missions, consumed by moose classes and provides a menu system for players to tweak settings in running missions.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
--
|
||||
-- ## Features:
|
||||
--
|
||||
--
|
||||
-- * Provide a settings menu system to the players.
|
||||
-- * Provide a player settings menu and an overall mission settings menu.
|
||||
-- * Mission settings provide default settings, while player settings override mission settings.
|
||||
@@ -11,19 +11,19 @@
|
||||
-- * Provide a menu to select between different coordinate formats for A2A coordinates.
|
||||
-- * Provide a menu to select between different message time duration options.
|
||||
-- * Provide a menu to select between different metric systems.
|
||||
--
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
--
|
||||
-- The documentation of the SETTINGS class can be found further in this document.
|
||||
--
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
--
|
||||
-- # **AUTHORS and CONTRIBUTIONS**
|
||||
--
|
||||
-- ### Contributions:
|
||||
--
|
||||
-- ### Authors:
|
||||
--
|
||||
--
|
||||
-- ### Contributions:
|
||||
--
|
||||
-- ### Authors:
|
||||
--
|
||||
-- * **FlightControl**: Design & Programming
|
||||
--
|
||||
-- @module Core.Settings
|
||||
@@ -34,168 +34,181 @@
|
||||
-- @extends Core.Base#BASE
|
||||
|
||||
--- Takes care of various settings that influence the behaviour of certain functionalities and classes within the MOOSE framework.
|
||||
--
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
--
|
||||
-- The SETTINGS class takes care of various settings that influence the behaviour of certain functionalities and classes within the MOOSE framework.
|
||||
-- SETTINGS can work on 2 levels:
|
||||
--
|
||||
-- - **Default settings**: A running mission has **Default settings**.
|
||||
--
|
||||
-- - **Default settings**: A running mission has **Default settings**.
|
||||
-- - **Player settings**: For each player its own **Player settings** can be defined, overriding the **Default settings**.
|
||||
--
|
||||
--
|
||||
-- So, when there isn't any **Player setting** defined for a player for a specific setting, or, the player cannot be identified, the **Default setting** will be used instead.
|
||||
--
|
||||
--
|
||||
-- # 1) \_SETTINGS object
|
||||
--
|
||||
--
|
||||
-- MOOSE defines by default a singleton object called **\_SETTINGS**. Use this object to modify all the **Default settings** for a running mission.
|
||||
-- For each player, MOOSE will automatically allocate also a **player settings** object, and will expose a radio menu to allow the player to adapt the settings to his own preferences.
|
||||
--
|
||||
--
|
||||
-- # 2) SETTINGS Menu
|
||||
--
|
||||
--
|
||||
-- Settings can be adapted by the Players and by the Mission Administrator through **radio menus, which are automatically available in the mission**.
|
||||
-- These menus can be found **on level F10 under "Settings"**. There are two kinds of menus generated by the system.
|
||||
--
|
||||
--
|
||||
-- ## 2.1) Default settings menu
|
||||
--
|
||||
--
|
||||
-- A menu is created automatically per Command Center that allows to modify the **Default** settings.
|
||||
-- So, when joining a CC unit, a menu will be available that allows to change the settings parameters **FOR ALL THE PLAYERS**!
|
||||
-- Note that the **Default settings** will only be used when a player has not choosen its own settings.
|
||||
--
|
||||
--
|
||||
-- ## 2.2) Player settings menu
|
||||
--
|
||||
--
|
||||
-- A menu is created automatically per Player Slot (group) that allows to modify the **Player** settings.
|
||||
-- So, when joining a slot, a menu wil be available that allows to change the settings parameters **FOR THE PLAYER ONLY**!
|
||||
-- Note that when a player has not chosen a specific setting, the **Default settings** will be used.
|
||||
--
|
||||
--
|
||||
-- ## 2.3) Show or Hide the Player Setting menus
|
||||
--
|
||||
--
|
||||
-- Of course, it may be requried not to show any setting menus. In this case, a method is available on the **\_SETTINGS object**.
|
||||
-- Use @{#SETTINGS.SetPlayerMenuOff}() to hide the player menus, and use @{#SETTINGS.SetPlayerMenuOn}() show the player menus.
|
||||
-- Note that when this method is used, any player already in a slot will not have its menus visibility changed.
|
||||
-- The option will only have effect when a player enters a new slot or changes a slot.
|
||||
--
|
||||
-- The option will only have effect when a player enters a new slot or changes a slot.
|
||||
--
|
||||
-- Example:
|
||||
--
|
||||
--
|
||||
-- _SETTINGS:SetPlayerMenuOff() -- will disable the player menus.
|
||||
-- _SETTINGS:SetPlayerMenuOn() -- will enable the player menus.
|
||||
-- -- But only when a player exits and reenters the slot these settings will have effect!
|
||||
--
|
||||
--
|
||||
--
|
||||
--
|
||||
-- # 3) Settings
|
||||
--
|
||||
--
|
||||
-- There are different settings that are managed and applied within the MOOSE framework.
|
||||
-- See below a comprehensive description of each.
|
||||
--
|
||||
--
|
||||
-- ## 3.1) **A2G coordinates** display formatting
|
||||
--
|
||||
--
|
||||
-- ### 3.1.1) A2G coordinates setting **types**
|
||||
--
|
||||
--
|
||||
-- Will customize which display format is used to indicate A2G coordinates in text as part of the Command Center communications.
|
||||
--
|
||||
--
|
||||
-- - A2G BR: [Bearing Range](https://en.wikipedia.org/wiki/Bearing_(navigation)).
|
||||
-- - A2G MGRS: The [Military Grid Reference System](https://en.wikipedia.org/wiki/Military_Grid_Reference_System). The accuracy can also be adapted.
|
||||
-- - A2G LL DMS: Lattitude Longitude [Degrees Minutes Seconds](https://en.wikipedia.org/wiki/Geographic_coordinate_conversion). The accuracy can also be adapted.
|
||||
-- - A2G LL DDM: Lattitude Longitude [Decimal Degrees Minutes](https://en.wikipedia.org/wiki/Decimal_degrees). The accuracy can also be adapted.
|
||||
--
|
||||
--
|
||||
-- ### 3.1.2) A2G coordinates setting **menu**
|
||||
--
|
||||
--
|
||||
-- The settings can be changed by using the **Default settings menu** on the Command Center or the **Player settings menu** on the Player Slot.
|
||||
--
|
||||
--
|
||||
-- ### 3.1.3) A2G coordinates setting **methods**
|
||||
--
|
||||
--
|
||||
-- There are different methods that can be used to change the **System settings** using the \_SETTINGS object.
|
||||
--
|
||||
--
|
||||
-- - @{#SETTINGS.SetA2G_BR}(): Enable the BR display formatting by default.
|
||||
-- - @{#SETTINGS.SetA2G_MGRS}(): Enable the MGRS display formatting by default. Use @{SETTINGS.SetMGRS_Accuracy}() to adapt the accuracy of the MGRS formatting.
|
||||
-- - @{#SETTINGS.SetA2G_LL_DMS}(): Enable the LL DMS display formatting by default. Use @{SETTINGS.SetLL_Accuracy}() to adapt the accuracy of the Seconds formatting.
|
||||
-- - @{#SETTINGS.SetA2G_LL_DMS}(): Enable the LL DMS display formatting by default. Use @{SETTINGS.SetLL_Accuracy}() to adapt the accuracy of the Seconds formatting.
|
||||
-- - @{#SETTINGS.SetA2G_LL_DDM}(): Enable the LL DDM display formatting by default. Use @{SETTINGS.SetLL_Accuracy}() to adapt the accuracy of the Seconds formatting.
|
||||
--
|
||||
--
|
||||
-- ### 3.1.4) A2G coordinates setting - additional notes
|
||||
--
|
||||
-- One additional note on BR. In a situation when a BR coordinate should be given,
|
||||
--
|
||||
-- One additional note on BR. In a situation when a BR coordinate should be given,
|
||||
-- but there isn't any player context (no player unit to reference from), the MGRS formatting will be applied!
|
||||
--
|
||||
--
|
||||
-- ## 3.2) **A2A coordinates** formatting
|
||||
--
|
||||
--
|
||||
-- ### 3.2.1) A2A coordinates setting **types**
|
||||
--
|
||||
--
|
||||
-- Will customize which display format is used to indicate A2A coordinates in text as part of the Command Center communications.
|
||||
--
|
||||
--
|
||||
-- - A2A BRAA: [Bearing Range Altitude Aspect](https://en.wikipedia.org/wiki/Bearing_(navigation)).
|
||||
-- - A2A MGRS: The [Military Grid Reference System](https://en.wikipedia.org/wiki/Military_Grid_Reference_System). The accuracy can also be adapted.
|
||||
-- - A2A LL DMS: Lattitude Longitude [Degrees Minutes Seconds](https://en.wikipedia.org/wiki/Geographic_coordinate_conversion). The accuracy can also be adapted.
|
||||
-- - A2A LL DDM: Lattitude Longitude [Decimal Degrees and Minutes](https://en.wikipedia.org/wiki/Decimal_degrees). The accuracy can also be adapted.
|
||||
-- - A2A BULLS: [Bullseye](http://falcon4.wikidot.com/concepts:bullseye).
|
||||
--
|
||||
--
|
||||
-- ### 3.2.2) A2A coordinates setting **menu**
|
||||
--
|
||||
--
|
||||
-- The settings can be changed by using the **Default settings menu** on the Command Center or the **Player settings menu** on the Player Slot.
|
||||
--
|
||||
--
|
||||
-- ### 3.2.3) A2A coordinates setting **methods**
|
||||
--
|
||||
--
|
||||
-- There are different methods that can be used to change the **System settings** using the \_SETTINGS object.
|
||||
--
|
||||
--
|
||||
-- - @{#SETTINGS.SetA2A_BRAA}(): Enable the BR display formatting by default.
|
||||
-- - @{#SETTINGS.SetA2A_MGRS}(): Enable the MGRS display formatting by default. Use @{SETTINGS.SetMGRS_Accuracy}() to adapt the accuracy of the MGRS formatting.
|
||||
-- - @{#SETTINGS.SetA2A_LL_DMS}(): Enable the LL DMS display formatting by default. Use @{SETTINGS.SetLL_Accuracy}() to adapt the accuracy of the Seconds formatting.
|
||||
-- - @{#SETTINGS.SetA2A_LL_DMS}(): Enable the LL DMS display formatting by default. Use @{SETTINGS.SetLL_Accuracy}() to adapt the accuracy of the Seconds formatting.
|
||||
-- - @{#SETTINGS.SetA2A_LL_DDM}(): Enable the LL DDM display formatting by default. Use @{SETTINGS.SetLL_Accuracy}() to adapt the accuracy of the Seconds formatting.
|
||||
-- - @{#SETTINGS.SetA2A_BULLS}(): Enable the BULLSeye display formatting by default.
|
||||
--
|
||||
--
|
||||
-- ### 3.2.4) A2A coordinates settings - additional notes
|
||||
--
|
||||
-- One additional note on BRAA. In a situation when a BRAA coordinate should be given,
|
||||
--
|
||||
-- One additional note on BRAA. In a situation when a BRAA coordinate should be given,
|
||||
-- but there isn't any player context (no player unit to reference from), the MGRS formatting will be applied!
|
||||
--
|
||||
--
|
||||
-- ## 3.3) **Measurements** formatting
|
||||
--
|
||||
--
|
||||
-- ### 3.3.1) Measurements setting **types**
|
||||
--
|
||||
--
|
||||
-- Will customize the measurements system being used as part as part of the Command Center communications.
|
||||
--
|
||||
--
|
||||
-- - **Metrics** system: Applies the [Metrics system](https://en.wikipedia.org/wiki/Metric_system) ...
|
||||
-- - **Imperial** system: Applies the [Imperial system](https://en.wikipedia.org/wiki/Imperial_units) ...
|
||||
--
|
||||
-- - **Imperial** system: Applies the [Imperial system](https://en.wikipedia.org/wiki/Imperial_units) ...
|
||||
--
|
||||
-- ### 3.3.2) Measurements setting **menu**
|
||||
--
|
||||
--
|
||||
-- The settings can be changed by using the **Default settings menu** on the Command Center or the **Player settings menu** on the Player Slot.
|
||||
--
|
||||
--
|
||||
-- ### 3.3.3) Measurements setting **methods**
|
||||
--
|
||||
--
|
||||
-- There are different methods that can be used to change the **Default settings** using the \_SETTINGS object.
|
||||
--
|
||||
--
|
||||
-- - @{#SETTINGS.SetMetric}(): Enable the Metric system.
|
||||
-- - @{#SETTINGS.SetImperial}(): Enable the Imperial system.
|
||||
--
|
||||
--
|
||||
-- ## 3.4) **Message** display times
|
||||
--
|
||||
--
|
||||
-- ### 3.4.1) Message setting **types**
|
||||
--
|
||||
--
|
||||
-- There are various **Message Types** that will influence the duration how long a message will appear as part of the Command Center communications.
|
||||
--
|
||||
--
|
||||
-- - **Update** message: A short update message.
|
||||
-- - **Information** message: Provides new information **while** executing a mission.
|
||||
-- - **Briefing** message: Provides a complete briefing **before** executing a mission.
|
||||
-- - **Overview report**: Provides a short report overview, the summary of the report.
|
||||
-- - **Detailed report**: Provides a complete report.
|
||||
--
|
||||
--
|
||||
-- ### 3.4.2) Message setting **menu**
|
||||
--
|
||||
--
|
||||
-- The settings can be changed by using the **Default settings menu** on the Command Center or the **Player settings menu** on the Player Slot.
|
||||
--
|
||||
--
|
||||
-- Each Message Type has specific timings that will be applied when the message is displayed.
|
||||
-- The Settings Menu will provide for each Message Type a selection of proposed durations from which can be choosen.
|
||||
-- So the player can choose its own amount of seconds how long a message should be displayed of a certain type.
|
||||
-- Note that **Update** messages can be chosen not to be displayed at all!
|
||||
--
|
||||
--
|
||||
-- ### 3.4.3) Message setting **methods**
|
||||
--
|
||||
--
|
||||
-- There are different methods that can be used to change the **System settings** using the \_SETTINGS object.
|
||||
--
|
||||
-- - @{#SETTINGS.SetMessageTime}(): Define for a specific @{Message.MESSAGE.MessageType} the duration to be displayed in seconds.
|
||||
-- - @{#SETTINGS.GetMessageTime}(): Retrieves for a specific @{Message.MESSAGE.MessageType} the duration to be displayed in seconds.
|
||||
--
|
||||
--
|
||||
-- - @{#SETTINGS.SetMessageTime}(): Define for a specific @{Message.MESSAGE.MessageType} the duration to be displayed in seconds.
|
||||
-- - @{#SETTINGS.GetMessageTime}(): Retrieves for a specific @{Message.MESSAGE.MessageType} the duration to be displayed in seconds.
|
||||
--
|
||||
-- ## 3.5) **Era** of the battle
|
||||
--
|
||||
-- The threat level metric is scaled according the era of the battle. A target that is AAA, will pose a much greather threat in WWII than on modern warfare.
|
||||
-- Therefore, there are 4 era that are defined within the settings:
|
||||
--
|
||||
-- - **WWII** era: Use for warfare with equipment during the world war II time.
|
||||
-- - **Korea** era: Use for warfare with equipment during the Korea war time.
|
||||
-- - **Cold War** era: Use for warfare with equipment during the cold war time.
|
||||
-- - **Modern** era: Use for warfare with modern equipment in the 2000s.
|
||||
--
|
||||
-- There are different API defined that you can use with the _SETTINGS object to configure your mission script to work in one of the 4 era:
|
||||
-- @{#SETTINGS.SetEraWWII}(), @{#SETTINGS.SetEraKorea}(), @{#SETTINGS.SetEraCold}(), @{#SETTINGS.SetEraModern}()
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
--
|
||||
-- @field #SETTINGS
|
||||
SETTINGS = {
|
||||
ClassName = "SETTINGS",
|
||||
@@ -204,6 +217,19 @@ SETTINGS = {
|
||||
MenuStatic = true,
|
||||
}
|
||||
|
||||
SETTINGS.__Enum = {}
|
||||
|
||||
--- @type SETTINGS.__Enum.Era
|
||||
-- @field #number WWII
|
||||
-- @field #number Korea
|
||||
-- @field #number Cold
|
||||
-- @field #number Modern
|
||||
SETTINGS.__Enum.Era = {
|
||||
WWII = 1,
|
||||
Korea = 2,
|
||||
Cold = 3,
|
||||
Modern = 4,
|
||||
}
|
||||
|
||||
|
||||
do -- SETTINGS
|
||||
@@ -211,7 +237,7 @@ do -- SETTINGS
|
||||
--- SETTINGS constructor.
|
||||
-- @param #SETTINGS self
|
||||
-- @return #SETTINGS
|
||||
function SETTINGS:Set( PlayerName )
|
||||
function SETTINGS:Set( PlayerName )
|
||||
|
||||
if PlayerName == nil then
|
||||
local self = BASE:Inherit( self, BASE:New() ) -- #SETTINGS
|
||||
@@ -225,6 +251,7 @@ do -- SETTINGS
|
||||
self:SetMessageTime( MESSAGE.Type.Information, 30 )
|
||||
self:SetMessageTime( MESSAGE.Type.Overview, 60 )
|
||||
self:SetMessageTime( MESSAGE.Type.Update, 15 )
|
||||
self:SetEraModern()
|
||||
self:SetMenutextShort(true)
|
||||
self:SetMenuStatic(true)
|
||||
return self
|
||||
@@ -245,20 +272,20 @@ do -- SETTINGS
|
||||
function SETTINGS:SetMenutextShort(onoff)
|
||||
self.MenuShort = onoff
|
||||
end
|
||||
|
||||
|
||||
--- Set menu to be static.
|
||||
-- @param #SETTINGS self
|
||||
-- @param #boolean onoff If *true* menu is static. If *false* menu will be updated after changes (default).
|
||||
function SETTINGS:SetMenuStatic(onoff)
|
||||
self.MenuStatic = onoff
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- Sets the SETTINGS metric.
|
||||
-- @param #SETTINGS self
|
||||
function SETTINGS:SetMetric()
|
||||
self.Metric = true
|
||||
end
|
||||
|
||||
|
||||
--- Gets if the SETTINGS is metric.
|
||||
-- @param #SETTINGS self
|
||||
-- @return #boolean true if metric.
|
||||
@@ -271,7 +298,7 @@ do -- SETTINGS
|
||||
function SETTINGS:SetImperial()
|
||||
self.Metric = false
|
||||
end
|
||||
|
||||
|
||||
--- Gets if the SETTINGS is imperial.
|
||||
-- @param #SETTINGS self
|
||||
-- @return #boolean true if imperial.
|
||||
@@ -308,7 +335,7 @@ do -- SETTINGS
|
||||
function SETTINGS:GetMGRS_Accuracy()
|
||||
return self.MGRS_Accuracy or _SETTINGS:GetMGRS_Accuracy()
|
||||
end
|
||||
|
||||
|
||||
--- Sets the SETTINGS Message Display Timing of a MessageType
|
||||
-- @param #SETTINGS self
|
||||
-- @param Core.Message#MESSAGE MessageType The type of the message.
|
||||
@@ -317,8 +344,8 @@ do -- SETTINGS
|
||||
self.MessageTypeTimings = self.MessageTypeTimings or {}
|
||||
self.MessageTypeTimings[MessageType] = MessageTime
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
--- Gets the SETTINGS Message Display Timing of a MessageType
|
||||
-- @param #SETTINGS self
|
||||
-- @param Core.Message#MESSAGE MessageType The type of the message.
|
||||
@@ -460,9 +487,9 @@ do -- SETTINGS
|
||||
function SETTINGS:SetSystemMenu( MenuGroup, RootMenu )
|
||||
|
||||
local MenuText = "System Settings"
|
||||
|
||||
|
||||
local MenuTime = timer.getTime()
|
||||
|
||||
|
||||
local SettingsMenu = MENU_GROUP:New( MenuGroup, MenuText, RootMenu ):SetTime( MenuTime )
|
||||
|
||||
-------
|
||||
@@ -472,9 +499,9 @@ do -- SETTINGS
|
||||
local text="A2G Coordinate System XXX"
|
||||
if self.MenuShort then
|
||||
text="A2G Coordinates"
|
||||
end
|
||||
end
|
||||
local A2GCoordinateMenu = MENU_GROUP:New( MenuGroup, text, SettingsMenu ):SetTime( MenuTime )
|
||||
|
||||
|
||||
-- Set LL DMS
|
||||
if not self:IsA2G_LL_DMS() then
|
||||
local text="Lat/Lon Degree Min Sec (LL DMS)"
|
||||
@@ -483,16 +510,16 @@ do -- SETTINGS
|
||||
end
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, text, A2GCoordinateMenu, self.A2GMenuSystem, self, MenuGroup, RootMenu, "LL DMS" ):SetTime( MenuTime )
|
||||
end
|
||||
|
||||
|
||||
-- Set LL DDM
|
||||
if not self:IsA2G_LL_DDM() then
|
||||
local text="Lat/Lon Degree Dec Min (LL DDM)"
|
||||
if self.MenuShort then
|
||||
text="LL DDM"
|
||||
end
|
||||
end
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "Lat/Lon Degree Dec Min (LL DDM)", A2GCoordinateMenu, self.A2GMenuSystem, self, MenuGroup, RootMenu, "LL DDM" ):SetTime( MenuTime )
|
||||
end
|
||||
|
||||
|
||||
-- Set LL DMS accuracy.
|
||||
if self:IsA2G_LL_DDM() then
|
||||
local text1="LL DDM Accuracy 1"
|
||||
@@ -500,30 +527,30 @@ do -- SETTINGS
|
||||
local text3="LL DDM Accuracy 3"
|
||||
if self.MenuShort then
|
||||
text1="LL DDM"
|
||||
end
|
||||
end
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "LL DDM Accuracy 1", A2GCoordinateMenu, self.MenuLL_DDM_Accuracy, self, MenuGroup, RootMenu, 1 ):SetTime( MenuTime )
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "LL DDM Accuracy 2", A2GCoordinateMenu, self.MenuLL_DDM_Accuracy, self, MenuGroup, RootMenu, 2 ):SetTime( MenuTime )
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "LL DDM Accuracy 3", A2GCoordinateMenu, self.MenuLL_DDM_Accuracy, self, MenuGroup, RootMenu, 3 ):SetTime( MenuTime )
|
||||
end
|
||||
|
||||
|
||||
-- Set BR.
|
||||
if not self:IsA2G_BR() then
|
||||
local text="Bearing, Range (BR)"
|
||||
if self.MenuShort then
|
||||
text="BR"
|
||||
end
|
||||
end
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, text , A2GCoordinateMenu, self.A2GMenuSystem, self, MenuGroup, RootMenu, "BR" ):SetTime( MenuTime )
|
||||
end
|
||||
|
||||
|
||||
-- Set MGRS.
|
||||
if not self:IsA2G_MGRS() then
|
||||
local text="Military Grid (MGRS)"
|
||||
if self.MenuShort then
|
||||
text="MGRS"
|
||||
end
|
||||
end
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, text, A2GCoordinateMenu, self.A2GMenuSystem, self, MenuGroup, RootMenu, "MGRS" ):SetTime( MenuTime )
|
||||
end
|
||||
|
||||
|
||||
-- Set MGRS accuracy.
|
||||
if self:IsA2G_MGRS() then
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "MGRS Accuracy 1", A2GCoordinateMenu, self.MenuMGRS_Accuracy, self, MenuGroup, RootMenu, 1 ):SetTime( MenuTime )
|
||||
@@ -532,22 +559,22 @@ do -- SETTINGS
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "MGRS Accuracy 4", A2GCoordinateMenu, self.MenuMGRS_Accuracy, self, MenuGroup, RootMenu, 4 ):SetTime( MenuTime )
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "MGRS Accuracy 5", A2GCoordinateMenu, self.MenuMGRS_Accuracy, self, MenuGroup, RootMenu, 5 ):SetTime( MenuTime )
|
||||
end
|
||||
|
||||
|
||||
-------
|
||||
-- A2A Coordinate System
|
||||
-------
|
||||
|
||||
|
||||
local text="A2A Coordinate System"
|
||||
if self.MenuShort then
|
||||
text="A2A Coordinates"
|
||||
end
|
||||
end
|
||||
local A2ACoordinateMenu = MENU_GROUP:New( MenuGroup, text, SettingsMenu ):SetTime( MenuTime )
|
||||
|
||||
if not self:IsA2A_LL_DMS() then
|
||||
local text="Lat/Lon Degree Min Sec (LL DMS)"
|
||||
if self.MenuShort then
|
||||
text="LL DMS"
|
||||
end
|
||||
end
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, text, A2ACoordinateMenu, self.A2AMenuSystem, self, MenuGroup, RootMenu, "LL DMS" ):SetTime( MenuTime )
|
||||
end
|
||||
|
||||
@@ -555,7 +582,7 @@ do -- SETTINGS
|
||||
local text="Lat/Lon Degree Dec Min (LL DDM)"
|
||||
if self.MenuShort then
|
||||
text="LL DDM"
|
||||
end
|
||||
end
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, text, A2ACoordinateMenu, self.A2AMenuSystem, self, MenuGroup, RootMenu, "LL DDM" ):SetTime( MenuTime )
|
||||
end
|
||||
|
||||
@@ -563,7 +590,7 @@ do -- SETTINGS
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "LL DDM Accuracy 1", A2ACoordinateMenu, self.MenuLL_DDM_Accuracy, self, MenuGroup, RootMenu, 1 ):SetTime( MenuTime )
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "LL DDM Accuracy 2", A2ACoordinateMenu, self.MenuLL_DDM_Accuracy, self, MenuGroup, RootMenu, 2 ):SetTime( MenuTime )
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "LL DDM Accuracy 3", A2ACoordinateMenu, self.MenuLL_DDM_Accuracy, self, MenuGroup, RootMenu, 3 ):SetTime( MenuTime )
|
||||
end
|
||||
end
|
||||
|
||||
if not self:IsA2A_BULLS() then
|
||||
local text="Bullseye (BULLS)"
|
||||
@@ -572,20 +599,20 @@ do -- SETTINGS
|
||||
end
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, text, A2ACoordinateMenu, self.A2AMenuSystem, self, MenuGroup, RootMenu, "BULLS" ):SetTime( MenuTime )
|
||||
end
|
||||
|
||||
|
||||
if not self:IsA2A_BRAA() then
|
||||
local text="Bearing Range Altitude Aspect (BRAA)"
|
||||
if self.MenuShort then
|
||||
text="BRAA"
|
||||
end
|
||||
end
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, text, A2ACoordinateMenu, self.A2AMenuSystem, self, MenuGroup, RootMenu, "BRAA" ):SetTime( MenuTime )
|
||||
end
|
||||
|
||||
|
||||
if not self:IsA2A_MGRS() then
|
||||
local text="Military Grid (MGRS)"
|
||||
if self.MenuShort then
|
||||
text="MGRS"
|
||||
end
|
||||
end
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, text, A2ACoordinateMenu, self.A2AMenuSystem, self, MenuGroup, RootMenu, "MGRS" ):SetTime( MenuTime )
|
||||
end
|
||||
|
||||
@@ -596,33 +623,33 @@ do -- SETTINGS
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "MGRS Accuracy 4", A2ACoordinateMenu, self.MenuMGRS_Accuracy, self, MenuGroup, RootMenu, 4 ):SetTime( MenuTime )
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "MGRS Accuracy 5", A2ACoordinateMenu, self.MenuMGRS_Accuracy, self, MenuGroup, RootMenu, 5 ):SetTime( MenuTime )
|
||||
end
|
||||
|
||||
|
||||
local text="Measures and Weights System"
|
||||
if self.MenuShort then
|
||||
text="Unit System"
|
||||
end
|
||||
local MetricsMenu = MENU_GROUP:New( MenuGroup, text, SettingsMenu ):SetTime( MenuTime )
|
||||
|
||||
|
||||
if self:IsMetric() then
|
||||
local text="Imperial (Miles,Feet)"
|
||||
if self.MenuShort then
|
||||
text="Imperial"
|
||||
end
|
||||
end
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, text, MetricsMenu, self.MenuMWSystem, self, MenuGroup, RootMenu, false ):SetTime( MenuTime )
|
||||
end
|
||||
|
||||
|
||||
if self:IsImperial() then
|
||||
local text="Metric (Kilometers,Meters)"
|
||||
if self.MenuShort then
|
||||
text="Metric"
|
||||
end
|
||||
end
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, text, MetricsMenu, self.MenuMWSystem, self, MenuGroup, RootMenu, true ):SetTime( MenuTime )
|
||||
end
|
||||
end
|
||||
|
||||
local text="Messages and Reports"
|
||||
if self.MenuShort then
|
||||
text="Messages & Reports"
|
||||
end
|
||||
end
|
||||
local MessagesMenu = MENU_GROUP:New( MenuGroup, text, SettingsMenu ):SetTime( MenuTime )
|
||||
|
||||
local UpdateMessagesMenu = MENU_GROUP:New( MenuGroup, "Update Messages", MessagesMenu ):SetTime( MenuTime )
|
||||
@@ -661,10 +688,10 @@ do -- SETTINGS
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "1 minute", DetailedReportsMenu, self.MenuMessageTimingsSystem, self, MenuGroup, RootMenu, MESSAGE.Type.DetailedReportsMenu, 60 ):SetTime( MenuTime )
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "2 minutes", DetailedReportsMenu, self.MenuMessageTimingsSystem, self, MenuGroup, RootMenu, MESSAGE.Type.DetailedReportsMenu, 120 ):SetTime( MenuTime )
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "3 minutes", DetailedReportsMenu, self.MenuMessageTimingsSystem, self, MenuGroup, RootMenu, MESSAGE.Type.DetailedReportsMenu, 180 ):SetTime( MenuTime )
|
||||
|
||||
|
||||
|
||||
SettingsMenu:Remove( MenuTime )
|
||||
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -697,24 +724,24 @@ do -- SETTINGS
|
||||
function SETTINGS:SetPlayerMenu( PlayerUnit )
|
||||
|
||||
if _SETTINGS.ShowPlayerMenu == true then
|
||||
|
||||
|
||||
local PlayerGroup = PlayerUnit:GetGroup()
|
||||
local PlayerName = PlayerUnit:GetPlayerName()
|
||||
local PlayerNames = PlayerGroup:GetPlayerNames()
|
||||
|
||||
|
||||
local PlayerMenu = MENU_GROUP:New( PlayerGroup, 'Settings "' .. PlayerName .. '"' )
|
||||
|
||||
|
||||
self.PlayerMenu = PlayerMenu
|
||||
|
||||
|
||||
self:I(string.format("FF Setting menu for player %s", tostring(PlayerName)))
|
||||
|
||||
|
||||
local submenu = MENU_GROUP:New( PlayerGroup, "LL Accuracy", PlayerMenu )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "0 Decimal Places", submenu, self.MenuGroupLL_DDM_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 0 )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "1 Decimal Places", submenu, self.MenuGroupLL_DDM_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 1 )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "2 Decimal Places", submenu, self.MenuGroupLL_DDM_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 2 )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "3 Decimal Places", submenu, self.MenuGroupLL_DDM_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 3 )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "4 Decimal Places", submenu, self.MenuGroupLL_DDM_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 4 )
|
||||
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "4 Decimal Places", submenu, self.MenuGroupLL_DDM_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 4 )
|
||||
|
||||
local submenu = MENU_GROUP:New( PlayerGroup, "MGRS Accuracy", PlayerMenu )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "Accuracy 0", submenu, self.MenuGroupMGRS_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 0 )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "Accuracy 1", submenu, self.MenuGroupMGRS_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 1 )
|
||||
@@ -722,7 +749,7 @@ do -- SETTINGS
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "Accuracy 3", submenu, self.MenuGroupMGRS_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 3 )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "Accuracy 4", submenu, self.MenuGroupMGRS_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 4 )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "Accuracy 5", submenu, self.MenuGroupMGRS_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 5 )
|
||||
|
||||
|
||||
------
|
||||
-- A2G Coordinate System
|
||||
------
|
||||
@@ -730,118 +757,118 @@ do -- SETTINGS
|
||||
local text="A2G Coordinate System"
|
||||
if self.MenuShort then
|
||||
text="A2G Coordinates"
|
||||
end
|
||||
end
|
||||
local A2GCoordinateMenu = MENU_GROUP:New( PlayerGroup, text, PlayerMenu )
|
||||
|
||||
|
||||
if not self:IsA2G_LL_DMS() or self.MenuStatic then
|
||||
local text="Lat/Lon Degree Min Sec (LL DMS)"
|
||||
if self.MenuShort then
|
||||
text="LL DMS"
|
||||
end
|
||||
end
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, text, A2GCoordinateMenu, self.MenuGroupA2GSystem, self, PlayerUnit, PlayerGroup, PlayerName, "LL DMS" )
|
||||
end
|
||||
|
||||
|
||||
if not self:IsA2G_LL_DDM() or self.MenuStatic then
|
||||
local text="Lat/Lon Degree Dec Min (LL DDM)"
|
||||
if self.MenuShort then
|
||||
text="LL DDM"
|
||||
end
|
||||
end
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, text, A2GCoordinateMenu, self.MenuGroupA2GSystem, self, PlayerUnit, PlayerGroup, PlayerName, "LL DDM" )
|
||||
end
|
||||
|
||||
|
||||
if not self:IsA2G_BR() or self.MenuStatic then
|
||||
local text="Bearing, Range (BR)"
|
||||
if self.MenuShort then
|
||||
text="BR"
|
||||
end
|
||||
end
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, text, A2GCoordinateMenu, self.MenuGroupA2GSystem, self, PlayerUnit, PlayerGroup, PlayerName, "BR" )
|
||||
end
|
||||
|
||||
|
||||
if not self:IsA2G_MGRS() or self.MenuStatic then
|
||||
local text="Military Grid (MGRS)"
|
||||
if self.MenuShort then
|
||||
text="MGRS"
|
||||
end
|
||||
end
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, text, A2GCoordinateMenu, self.MenuGroupA2GSystem, self, PlayerUnit, PlayerGroup, PlayerName, "MGRS" )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
------
|
||||
-- A2A Coordinates Menu
|
||||
------
|
||||
|
||||
|
||||
local text="A2A Coordinate System"
|
||||
if self.MenuShort then
|
||||
text="A2A Coordinates"
|
||||
end
|
||||
end
|
||||
local A2ACoordinateMenu = MENU_GROUP:New( PlayerGroup, text, PlayerMenu )
|
||||
|
||||
|
||||
|
||||
|
||||
if not self:IsA2A_LL_DMS() or self.MenuStatic then
|
||||
local text="Lat/Lon Degree Min Sec (LL DMS)"
|
||||
if self.MenuShort then
|
||||
text="LL DMS"
|
||||
end
|
||||
end
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, text, A2ACoordinateMenu, self.MenuGroupA2GSystem, self, PlayerUnit, PlayerGroup, PlayerName, "LL DMS" )
|
||||
end
|
||||
|
||||
|
||||
if not self:IsA2A_LL_DDM() or self.MenuStatic then
|
||||
local text="Lat/Lon Degree Dec Min (LL DDM)"
|
||||
if self.MenuShort then
|
||||
text="LL DDM"
|
||||
end
|
||||
end
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, text, A2ACoordinateMenu, self.MenuGroupA2ASystem, self, PlayerUnit, PlayerGroup, PlayerName, "LL DDM" )
|
||||
end
|
||||
|
||||
|
||||
if not self:IsA2A_BULLS() or self.MenuStatic then
|
||||
local text="Bullseye (BULLS)"
|
||||
if self.MenuShort then
|
||||
text="BULLS"
|
||||
end
|
||||
end
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, text, A2ACoordinateMenu, self.MenuGroupA2ASystem, self, PlayerUnit, PlayerGroup, PlayerName, "BULLS" )
|
||||
end
|
||||
|
||||
|
||||
if not self:IsA2A_BRAA() or self.MenuStatic then
|
||||
local text="Bearing Range Altitude Aspect (BRAA)"
|
||||
if self.MenuShort then
|
||||
text="BRAA"
|
||||
end
|
||||
end
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, text, A2ACoordinateMenu, self.MenuGroupA2ASystem, self, PlayerUnit, PlayerGroup, PlayerName, "BRAA" )
|
||||
end
|
||||
|
||||
|
||||
if not self:IsA2A_MGRS() or self.MenuStatic then
|
||||
local text="Military Grid (MGRS)"
|
||||
if self.MenuShort then
|
||||
text="MGRS"
|
||||
end
|
||||
end
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, text, A2ACoordinateMenu, self.MenuGroupA2ASystem, self, PlayerUnit, PlayerGroup, PlayerName, "MGRS" )
|
||||
end
|
||||
|
||||
|
||||
---
|
||||
-- Unit system
|
||||
---
|
||||
|
||||
---
|
||||
|
||||
local text="Measures and Weights System"
|
||||
if self.MenuShort then
|
||||
text="Unit System"
|
||||
end
|
||||
end
|
||||
local MetricsMenu = MENU_GROUP:New( PlayerGroup, text, PlayerMenu )
|
||||
|
||||
|
||||
if self:IsMetric() or self.MenuStatic then
|
||||
local text="Imperial (Miles,Feet)"
|
||||
if self.MenuShort then
|
||||
text="Imperial"
|
||||
end
|
||||
end
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, text, MetricsMenu, self.MenuGroupMWSystem, self, PlayerUnit, PlayerGroup, PlayerName, false )
|
||||
end
|
||||
|
||||
|
||||
if self:IsImperial() or self.MenuStatic then
|
||||
local text="Metric (Kilometers,Meters)"
|
||||
if self.MenuShort then
|
||||
text="Metric"
|
||||
end
|
||||
end
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, text, MetricsMenu, self.MenuGroupMWSystem, self, PlayerUnit, PlayerGroup, PlayerName, true )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
---
|
||||
-- Messages and Reports
|
||||
---
|
||||
@@ -849,9 +876,9 @@ do -- SETTINGS
|
||||
local text="Messages and Reports"
|
||||
if self.MenuShort then
|
||||
text="Messages & Reports"
|
||||
end
|
||||
end
|
||||
local MessagesMenu = MENU_GROUP:New( PlayerGroup, text, PlayerMenu )
|
||||
|
||||
|
||||
local UpdateMessagesMenu = MENU_GROUP:New( PlayerGroup, "Update Messages", MessagesMenu )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "Off", UpdateMessagesMenu, self.MenuGroupMessageTimingsSystem, self, PlayerUnit, PlayerGroup, PlayerName, MESSAGE.Type.Update, 0 )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "5 seconds", UpdateMessagesMenu, self.MenuGroupMessageTimingsSystem, self, PlayerUnit, PlayerGroup, PlayerName, MESSAGE.Type.Update, 5 )
|
||||
@@ -859,7 +886,7 @@ do -- SETTINGS
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "15 seconds", UpdateMessagesMenu, self.MenuGroupMessageTimingsSystem, self, PlayerUnit, PlayerGroup, PlayerName, MESSAGE.Type.Update, 15 )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "30 seconds", UpdateMessagesMenu, self.MenuGroupMessageTimingsSystem, self, PlayerUnit, PlayerGroup, PlayerName, MESSAGE.Type.Update, 30 )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "1 minute", UpdateMessagesMenu, self.MenuGroupMessageTimingsSystem, self, PlayerUnit, PlayerGroup, PlayerName, MESSAGE.Type.Update, 60 )
|
||||
|
||||
|
||||
local InformationMessagesMenu = MENU_GROUP:New( PlayerGroup, "Information Messages", MessagesMenu )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "5 seconds", InformationMessagesMenu, self.MenuGroupMessageTimingsSystem, self, PlayerUnit, PlayerGroup, PlayerName, MESSAGE.Type.Information, 5 )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "10 seconds", InformationMessagesMenu, self.MenuGroupMessageTimingsSystem, self, PlayerUnit, PlayerGroup, PlayerName, MESSAGE.Type.Information, 10 )
|
||||
@@ -867,30 +894,30 @@ do -- SETTINGS
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "30 seconds", InformationMessagesMenu, self.MenuGroupMessageTimingsSystem, self, PlayerUnit, PlayerGroup, PlayerName, MESSAGE.Type.Information, 30 )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "1 minute", InformationMessagesMenu, self.MenuGroupMessageTimingsSystem, self, PlayerUnit, PlayerGroup, PlayerName, MESSAGE.Type.Information, 60 )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "2 minutes", InformationMessagesMenu, self.MenuGroupMessageTimingsSystem, self, PlayerUnit, PlayerGroup, PlayerName, MESSAGE.Type.Information, 120 )
|
||||
|
||||
|
||||
local BriefingReportsMenu = MENU_GROUP:New( PlayerGroup, "Briefing Reports", MessagesMenu )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "15 seconds", BriefingReportsMenu, self.MenuGroupMessageTimingsSystem, self, PlayerUnit, PlayerGroup, PlayerName, MESSAGE.Type.Briefing, 15 )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "30 seconds", BriefingReportsMenu, self.MenuGroupMessageTimingsSystem, self, PlayerUnit, PlayerGroup, PlayerName, MESSAGE.Type.Briefing, 30 )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "1 minute", BriefingReportsMenu, self.MenuGroupMessageTimingsSystem, self, PlayerUnit, PlayerGroup, PlayerName, MESSAGE.Type.Briefing, 60 )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "2 minutes", BriefingReportsMenu, self.MenuGroupMessageTimingsSystem, self, PlayerUnit, PlayerGroup, PlayerName, MESSAGE.Type.Briefing, 120 )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "3 minutes", BriefingReportsMenu, self.MenuGroupMessageTimingsSystem, self, PlayerUnit, PlayerGroup, PlayerName, MESSAGE.Type.Briefing, 180 )
|
||||
|
||||
|
||||
local OverviewReportsMenu = MENU_GROUP:New( PlayerGroup, "Overview Reports", MessagesMenu )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "15 seconds", OverviewReportsMenu, self.MenuGroupMessageTimingsSystem, self, PlayerUnit, PlayerGroup, PlayerName, MESSAGE.Type.Overview, 15 )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "30 seconds", OverviewReportsMenu, self.MenuGroupMessageTimingsSystem, self, PlayerUnit, PlayerGroup, PlayerName, MESSAGE.Type.Overview, 30 )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "1 minute", OverviewReportsMenu, self.MenuGroupMessageTimingsSystem, self, PlayerUnit, PlayerGroup, PlayerName, MESSAGE.Type.Overview, 60 )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "2 minutes", OverviewReportsMenu, self.MenuGroupMessageTimingsSystem, self, PlayerUnit, PlayerGroup, PlayerName, MESSAGE.Type.Overview, 120 )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "3 minutes", OverviewReportsMenu, self.MenuGroupMessageTimingsSystem, self, PlayerUnit, PlayerGroup, PlayerName, MESSAGE.Type.Overview, 180 )
|
||||
|
||||
|
||||
local DetailedReportsMenu = MENU_GROUP:New( PlayerGroup, "Detailed Reports", MessagesMenu )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "15 seconds", DetailedReportsMenu, self.MenuGroupMessageTimingsSystem, self, PlayerUnit, PlayerGroup, PlayerName, MESSAGE.Type.DetailedReportsMenu, 15 )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "30 seconds", DetailedReportsMenu, self.MenuGroupMessageTimingsSystem, self, PlayerUnit, PlayerGroup, PlayerName, MESSAGE.Type.DetailedReportsMenu, 30 )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "1 minute", DetailedReportsMenu, self.MenuGroupMessageTimingsSystem, self, PlayerUnit, PlayerGroup, PlayerName, MESSAGE.Type.DetailedReportsMenu, 60 )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "2 minutes", DetailedReportsMenu, self.MenuGroupMessageTimingsSystem, self, PlayerUnit, PlayerGroup, PlayerName, MESSAGE.Type.DetailedReportsMenu, 120 )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "3 minutes", DetailedReportsMenu, self.MenuGroupMessageTimingsSystem, self, PlayerUnit, PlayerGroup, PlayerName, MESSAGE.Type.DetailedReportsMenu, 180 )
|
||||
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -904,7 +931,7 @@ do -- SETTINGS
|
||||
self.PlayerMenu:Remove()
|
||||
self.PlayerMenu = nil
|
||||
end
|
||||
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -961,7 +988,7 @@ do -- SETTINGS
|
||||
self:SetPlayerMenu(PlayerUnit)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--- @param #SETTINGS self
|
||||
function SETTINGS:MenuGroupA2ASystem( PlayerUnit, PlayerGroup, PlayerName, A2ASystem )
|
||||
self.A2ASystem = A2ASystem
|
||||
@@ -971,7 +998,7 @@ do -- SETTINGS
|
||||
self:SetPlayerMenu(PlayerUnit)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--- @param #SETTINGS self
|
||||
function SETTINGS:MenuGroupLL_DDM_AccuracySystem( PlayerUnit, PlayerGroup, PlayerName, LL_Accuracy )
|
||||
self.LL_Accuracy = LL_Accuracy
|
||||
@@ -981,7 +1008,7 @@ do -- SETTINGS
|
||||
self:SetPlayerMenu(PlayerUnit)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--- @param #SETTINGS self
|
||||
function SETTINGS:MenuGroupMGRS_AccuracySystem( PlayerUnit, PlayerGroup, PlayerName, MGRS_Accuracy )
|
||||
self.MGRS_Accuracy = MGRS_Accuracy
|
||||
@@ -1007,9 +1034,48 @@ do -- SETTINGS
|
||||
self:SetMessageTime( MessageType, MessageTime )
|
||||
MESSAGE:New( string.format( "Settings: Default message time set for %s to %d.", MessageType, MessageTime ), 5 ):ToGroup( PlayerGroup )
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
--- Configures the era of the mission to be WWII.
|
||||
-- @param #SETTINGS self
|
||||
-- @return #SETTINGS self
|
||||
function SETTINGS:SetEraWWII()
|
||||
|
||||
self.Era = SETTINGS.__Enum.Era.WWII
|
||||
|
||||
end
|
||||
|
||||
--- Configures the era of the mission to be Korea.
|
||||
-- @param #SETTINGS self
|
||||
-- @return #SETTINGS self
|
||||
function SETTINGS:SetEraKorea()
|
||||
|
||||
self.Era = SETTINGS.__Enum.Era.Korea
|
||||
|
||||
end
|
||||
|
||||
|
||||
--- Configures the era of the mission to be Cold war.
|
||||
-- @param #SETTINGS self
|
||||
-- @return #SETTINGS self
|
||||
function SETTINGS:SetEraCold()
|
||||
|
||||
self.Era = SETTINGS.__Enum.Era.Cold
|
||||
|
||||
end
|
||||
|
||||
|
||||
--- Configures the era of the mission to be Modern war.
|
||||
-- @param #SETTINGS self
|
||||
-- @return #SETTINGS self
|
||||
function SETTINGS:SetEraModern()
|
||||
|
||||
self.Era = SETTINGS.__Enum.Era.Modern
|
||||
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
||||
|
||||
@@ -120,7 +120,7 @@ ZONE_BASE = {
|
||||
-- @param #string ZoneName Name of the zone.
|
||||
-- @return #ZONE_BASE self
|
||||
function ZONE_BASE:New( ZoneName )
|
||||
local self = BASE:Inherit( self, BASE:New() )
|
||||
local self = BASE:Inherit( self, FSM:New() )
|
||||
self:F( ZoneName )
|
||||
|
||||
self.ZoneName = ZoneName
|
||||
@@ -1393,7 +1393,7 @@ end
|
||||
function ZONE_POLYGON_BASE:Flush()
|
||||
self:F2()
|
||||
|
||||
self:E( { Polygon = self.ZoneName, Coordinates = self._.Polygon } )
|
||||
self:F( { Polygon = self.ZoneName, Coordinates = self._.Polygon } )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -1682,9 +1682,7 @@ do -- DETECTION_BASE
|
||||
-- @return #DETECTION_BASE.DetectedItem
|
||||
function DETECTION_BASE:GetDetectedItemByIndex( Index )
|
||||
|
||||
self:I( { DetectedItemsByIndex = self.DetectedItemsByIndex } )
|
||||
|
||||
self:I( { self.DetectedItemsByIndex } )
|
||||
self:F( { self.DetectedItemsByIndex } )
|
||||
|
||||
local DetectedItem = self.DetectedItemsByIndex[Index]
|
||||
if DetectedItem then
|
||||
|
||||
@@ -549,6 +549,9 @@ do -- ZONE_CAPTURE_COALITION
|
||||
-- If it is, then we must move the zone to attack state.
|
||||
self:HandleEvent( EVENTS.Hit, self.OnEventHit )
|
||||
|
||||
-- ZoneGoal objects are added to the _DATABASE.ZONES_GOAL and SET_ZONE_GOAL sets.
|
||||
_EVENTDISPATCHER:CreateEventNewZoneGoal( self )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -566,7 +569,7 @@ do -- ZONE_CAPTURE_COALITION
|
||||
|
||||
function ZONE_CAPTURE_COALITION:IsGuarded()
|
||||
|
||||
local IsGuarded = self.Zone:IsAllInZoneOfCoalition( self.Coalition )
|
||||
local IsGuarded = self:IsAllInZoneOfCoalition( self.Coalition )
|
||||
self:F( { IsGuarded = IsGuarded } )
|
||||
return IsGuarded
|
||||
end
|
||||
@@ -574,7 +577,7 @@ do -- ZONE_CAPTURE_COALITION
|
||||
|
||||
function ZONE_CAPTURE_COALITION:IsEmpty()
|
||||
|
||||
local IsEmpty = self.Zone:IsNoneInZone()
|
||||
local IsEmpty = self:IsNoneInZone()
|
||||
self:F( { IsEmpty = IsEmpty } )
|
||||
return IsEmpty
|
||||
end
|
||||
@@ -582,7 +585,7 @@ do -- ZONE_CAPTURE_COALITION
|
||||
|
||||
function ZONE_CAPTURE_COALITION:IsCaptured()
|
||||
|
||||
local IsCaptured = self.Zone:IsAllInZoneOfOtherCoalition( self.Coalition )
|
||||
local IsCaptured = self:IsAllInZoneOfOtherCoalition( self.Coalition )
|
||||
self:F( { IsCaptured = IsCaptured } )
|
||||
return IsCaptured
|
||||
end
|
||||
@@ -590,7 +593,7 @@ do -- ZONE_CAPTURE_COALITION
|
||||
|
||||
function ZONE_CAPTURE_COALITION:IsAttacked()
|
||||
|
||||
local IsAttacked = self.Zone:IsSomeInZoneOfCoalition( self.Coalition )
|
||||
local IsAttacked = self:IsSomeInZoneOfCoalition( self.Coalition )
|
||||
self:F( { IsAttacked = IsAttacked } )
|
||||
return IsAttacked
|
||||
end
|
||||
@@ -601,7 +604,7 @@ do -- ZONE_CAPTURE_COALITION
|
||||
-- @param #ZONE_CAPTURE_COALITION self
|
||||
function ZONE_CAPTURE_COALITION:Mark()
|
||||
|
||||
local Coord = self.Zone:GetCoordinate()
|
||||
local Coord = self:GetCoordinate()
|
||||
local ZoneName = self:GetZoneName()
|
||||
local State = self:GetState()
|
||||
|
||||
@@ -640,7 +643,7 @@ do -- ZONE_CAPTURE_COALITION
|
||||
|
||||
--self:GetParent( self ):onenterCaptured()
|
||||
|
||||
local NewCoalition = self.Zone:GetScannedCoalition()
|
||||
local NewCoalition = self:GetScannedCoalition()
|
||||
self:F( { NewCoalition = NewCoalition } )
|
||||
self:SetCoalition( NewCoalition )
|
||||
|
||||
@@ -673,14 +676,14 @@ do -- ZONE_CAPTURE_COALITION
|
||||
--BASE:GetParent( self ).onafterGuard( self )
|
||||
|
||||
if not self.SmokeScheduler then
|
||||
self.SmokeScheduler = self:ScheduleRepeat( 1, 1, 0.1, nil, self.StatusSmoke, self )
|
||||
self.SmokeScheduler = self:ScheduleRepeat( self.StartInterval, self.RepeatInterval, 0.1, nil, self.StatusSmoke, self )
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function ZONE_CAPTURE_COALITION:IsCaptured()
|
||||
|
||||
local IsCaptured = self.Zone:IsAllInZoneOfOtherCoalition( self.Coalition )
|
||||
local IsCaptured = self:IsAllInZoneOfOtherCoalition( self.Coalition )
|
||||
self:F( { IsCaptured = IsCaptured } )
|
||||
return IsCaptured
|
||||
end
|
||||
@@ -688,7 +691,7 @@ do -- ZONE_CAPTURE_COALITION
|
||||
|
||||
function ZONE_CAPTURE_COALITION:IsAttacked()
|
||||
|
||||
local IsAttacked = self.Zone:IsSomeInZoneOfCoalition( self.Coalition )
|
||||
local IsAttacked = self:IsSomeInZoneOfCoalition( self.Coalition )
|
||||
self:F( { IsAttacked = IsAttacked } )
|
||||
return IsAttacked
|
||||
end
|
||||
@@ -744,13 +747,13 @@ do -- ZONE_CAPTURE_COALITION
|
||||
--
|
||||
function ZONE_CAPTURE_COALITION:Start( StartInterval, RepeatInterval )
|
||||
|
||||
StartInterval = StartInterval or 15
|
||||
RepeatInterval = RepeatInterval or 15
|
||||
self.StartInterval = StartInterval or 15
|
||||
self.RepeatInterval = RepeatInterval or 15
|
||||
|
||||
if self.ScheduleStatusZone then
|
||||
self:ScheduleStop( self.ScheduleStatusZone )
|
||||
end
|
||||
self.ScheduleStatusZone = self:ScheduleRepeat( StartInterval, RepeatInterval, 0.1, nil, self.StatusZone, self )
|
||||
self.ScheduleStatusZone = self:ScheduleRepeat( self.StartInterval, self.RepeatInterval, 1.5, nil, self.StatusZone, self )
|
||||
end
|
||||
|
||||
|
||||
@@ -803,7 +806,7 @@ do -- ZONE_CAPTURE_COALITION
|
||||
local UnitHit = EventData.TgtUnit
|
||||
|
||||
if UnitHit then
|
||||
if UnitHit:IsInZone( self.Zone ) then
|
||||
if UnitHit:IsInZone( self ) then
|
||||
self:Attack()
|
||||
end
|
||||
end
|
||||
|
||||
@@ -44,14 +44,13 @@ do -- Zone
|
||||
|
||||
--- ZONE_GOAL Constructor.
|
||||
-- @param #ZONE_GOAL self
|
||||
-- @param Core.Zone#ZONE_BASE Zone A @{Zone} object with the goal to be achieved.
|
||||
-- @param Core.Zone#ZONE_RADIUS Zone A @{Zone} object with the goal to be achieved.
|
||||
-- @return #ZONE_GOAL
|
||||
function ZONE_GOAL:New( Zone )
|
||||
|
||||
local self = BASE:Inherit( self, FSM:New() ) -- #ZONE_GOAL
|
||||
local self = BASE:Inherit( self, ZONE_RADIUS:New( Zone:GetName(), Zone:GetVec2(), Zone:GetRadius() ) ) -- #ZONE_GOAL
|
||||
self:F( { Zone = Zone } )
|
||||
|
||||
self.Zone = Zone -- Core.Zone#ZONE_BASE
|
||||
self.Goal = GOAL:New()
|
||||
|
||||
self.SmokeTime = nil
|
||||
@@ -67,6 +66,7 @@ do -- Zone
|
||||
-- @param Wrapper.Unit#UNIT DestroyedUnit The destroyed unit.
|
||||
-- @param #string PlayerName The name of the player.
|
||||
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -74,7 +74,7 @@ do -- Zone
|
||||
-- @param #ZONE_GOAL self
|
||||
-- @return Core.Zone#ZONE_BASE
|
||||
function ZONE_GOAL:GetZone()
|
||||
return self.Zone
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
@@ -82,7 +82,7 @@ do -- Zone
|
||||
-- @param #ZONE_GOAL self
|
||||
-- @return #string
|
||||
function ZONE_GOAL:GetZoneName()
|
||||
return self.Zone:GetName()
|
||||
return self:GetName()
|
||||
end
|
||||
|
||||
|
||||
@@ -101,7 +101,7 @@ do -- Zone
|
||||
-- @param #ZONE_GOAL self
|
||||
-- @param #SMOKECOLOR.Color FlareColor
|
||||
function ZONE_GOAL:Flare( FlareColor )
|
||||
self.Zone:FlareZone( FlareColor, math.random( 1, 360 ) )
|
||||
self:FlareZone( FlareColor, math.random( 1, 360 ) )
|
||||
end
|
||||
|
||||
|
||||
@@ -130,7 +130,7 @@ do -- Zone
|
||||
|
||||
if self.SmokeTime == nil or self.SmokeTime + 300 <= CurrentTime then
|
||||
if self.SmokeColor then
|
||||
self.Zone:GetCoordinate():Smoke( self.SmokeColor )
|
||||
self:GetCoordinate():Smoke( self.SmokeColor )
|
||||
--self.SmokeColor = nil
|
||||
self.SmokeTime = CurrentTime
|
||||
end
|
||||
@@ -147,11 +147,9 @@ do -- Zone
|
||||
|
||||
local Vec3 = EventData.IniDCSUnit:getPosition().p
|
||||
self:F( { Vec3 = Vec3 } )
|
||||
local ZoneGoal = self:GetZone()
|
||||
self:F({ZoneGoal})
|
||||
|
||||
if EventData.IniDCSUnit then
|
||||
if ZoneGoal:IsVec3InZone(Vec3) then
|
||||
if self:IsVec3InZone(Vec3) then
|
||||
local PlayerHits = _DATABASE.HITS[EventData.IniUnitName]
|
||||
if PlayerHits then
|
||||
for PlayerName, PlayerHit in pairs( PlayerHits.Players or {} ) do
|
||||
|
||||
@@ -104,7 +104,7 @@ do -- ZoneGoal
|
||||
local State = self:GetState()
|
||||
self:F( { State = self:GetState() } )
|
||||
|
||||
self.Zone:Scan( { Object.Category.UNIT, Object.Category.STATIC } )
|
||||
self:Scan( { Object.Category.UNIT, Object.Category.STATIC } )
|
||||
|
||||
end
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ __Moose.Include( 'Scripts/Moose/Core/Velocity.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Core/Message.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Core/Fsm.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Core/Radio.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Core/RadioQueue.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Core/Spawn.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Core/SpawnStatic.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Core/Goal.lua' )
|
||||
|
||||
@@ -180,7 +180,7 @@ COMMANDCENTER = {
|
||||
COMMANDCENTER.AutoAssignMethods = {
|
||||
["Random"] = 1,
|
||||
["Distance"] = 2,
|
||||
["Priority"] = 3
|
||||
["Priority"] = 3,
|
||||
}
|
||||
|
||||
--- The constructor takes an IDENTIFIABLE as the HQ command center.
|
||||
@@ -200,7 +200,8 @@ function COMMANDCENTER:New( CommandCenterPositionable, CommandCenterName )
|
||||
|
||||
self:SetAutoAssignTasks( false )
|
||||
self:SetAutoAcceptTasks( true )
|
||||
self:SetAutoAssignMethod( COMMANDCENTER.AutoAssignMethods.Random )
|
||||
self:SetAutoAssignMethod( COMMANDCENTER.AutoAssignMethods.Distance )
|
||||
self:SetFlashStatus( false )
|
||||
|
||||
self:HandleEvent( EVENTS.Birth,
|
||||
--- @param #COMMANDCENTER self
|
||||
@@ -208,8 +209,8 @@ function COMMANDCENTER:New( CommandCenterPositionable, CommandCenterName )
|
||||
function( self, EventData )
|
||||
if EventData.IniObjectCategory == 1 then
|
||||
local EventGroup = GROUP:Find( EventData.IniDCSGroup )
|
||||
self:E( { CommandCenter = self:GetName(), EventGroup = EventGroup:GetName(), HasGroup = self:HasGroup( EventGroup ), EventData = EventData } )
|
||||
if EventGroup and self:HasGroup( EventGroup ) then
|
||||
--self:E( { CommandCenter = self:GetName(), EventGroup = EventGroup:GetName(), HasGroup = self:HasGroup( EventGroup ), EventData = EventData } )
|
||||
if EventGroup and EventGroup:IsAlive() and self:HasGroup( EventGroup ) then
|
||||
local CommandCenterMenu = MENU_GROUP:New( EventGroup, self:GetText() )
|
||||
local MenuReporting = MENU_GROUP:New( EventGroup, "Missions Reports", CommandCenterMenu )
|
||||
local MenuMissionsSummary = MENU_GROUP_COMMAND:New( EventGroup, "Missions Status Report", MenuReporting, self.ReportSummary, self, EventGroup )
|
||||
@@ -791,3 +792,12 @@ function COMMANDCENTER:ReportDetails( ReportGroup, Task )
|
||||
self:MessageToGroup( Report:Text(), ReportGroup )
|
||||
end
|
||||
|
||||
|
||||
--- Let the command center flash a report of the status of the subscribed task to a group.
|
||||
-- @param #COMMANDCENTER self
|
||||
function COMMANDCENTER:SetFlashStatus( Flash )
|
||||
self:F()
|
||||
|
||||
self.FlashStatus = Flash or true
|
||||
|
||||
end
|
||||
|
||||
@@ -254,11 +254,45 @@ do -- DETECTION MANAGER
|
||||
end
|
||||
|
||||
|
||||
--- Get the command center to communicate actions to the players.
|
||||
-- @param #DETECTION_MANAGER self
|
||||
-- @return Tasking.CommandCenter#COMMANDCENTER The command center.
|
||||
function DETECTION_MANAGER:GetCommandCenter()
|
||||
|
||||
return self.CC
|
||||
end
|
||||
|
||||
--- Set the frequency of communication and the mode of communication for voice overs.
|
||||
-- @param #DETECTION_MANAGER self
|
||||
-- @param #number RadioFrequency The frequency of communication.
|
||||
-- @param #number RadioModulation The modulation of communication.
|
||||
-- @param #number RadioPower The power in Watts of communication.
|
||||
function DETECTION_MANAGER:SetRadioFrequency( RadioFrequency, RadioModulation, RadioPower )
|
||||
|
||||
self.RadioFrequency = RadioFrequency
|
||||
self.RadioModulation = RadioModulation or radio.modulation.AM
|
||||
self.RadioPower = RadioPower or 100
|
||||
|
||||
if self.RadioQueue then
|
||||
self.RadioQueue:Stop()
|
||||
end
|
||||
|
||||
self.RadioQueue = nil
|
||||
|
||||
self.RadioQueue = RADIOQUEUE:New( self.RadioFrequency, self.RadioModulation )
|
||||
self.RadioQueue.power = self.RadioPower
|
||||
self.RadioQueue:Start( 0.5 )
|
||||
end
|
||||
|
||||
--- Send an information message to the players reporting to the command center.
|
||||
-- @param #DETECTION_MANAGER self
|
||||
-- @param #string Message The message to be sent.
|
||||
-- @param #string SoundFile The name of the sound file .wav or .ogg.
|
||||
-- @param #number SoundDuration The duration of the sound.
|
||||
-- @param #string SoundPath The path pointing to the folder in the mission file.
|
||||
-- @param Wrapper.Group#GROUP DefenderGroup The defender group sending the message.
|
||||
-- @return #DETECTION_MANGER self
|
||||
function DETECTION_MANAGER:MessageToPlayers( Message )
|
||||
function DETECTION_MANAGER:MessageToPlayers( Message, SoundFile, SoundDuration, SoundPath, DefenderGroup )
|
||||
|
||||
self:F( { Message = Message } )
|
||||
|
||||
@@ -269,6 +303,17 @@ do -- DETECTION MANAGER
|
||||
end
|
||||
end
|
||||
|
||||
-- Here we handle the transmission of the voice over.
|
||||
-- If for a certain reason the Defender does not exist, we use the coordinate of the airbase to send the message from.
|
||||
if SoundFile then
|
||||
local RadioQueue = self.RadioQueue -- Core.RadioQueue#RADIOQUEUE
|
||||
local DefenderUnit = DefenderGroup:GetUnit(1)
|
||||
if DefenderUnit and DefenderUnit:IsAlive() then
|
||||
RadioQueue:SetSenderUnitName( DefenderUnit:GetName() )
|
||||
end
|
||||
RadioQueue:NewTransmission( SoundFile, SoundDuration, SoundPath )
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -279,7 +324,6 @@ do -- DETECTION MANAGER
|
||||
-- @param Functional.Detection#DETECTION_BASE Detection
|
||||
-- @return #DETECTION_MANAGER self
|
||||
function DETECTION_MANAGER:ProcessDetected( Detection )
|
||||
self:E()
|
||||
|
||||
end
|
||||
|
||||
|
||||
@@ -856,6 +856,8 @@ do -- Group Assignment
|
||||
|
||||
CommandCenter:SetMenu()
|
||||
|
||||
self:MenuFlashTaskStatus( TaskGroup, self:GetMission():GetCommandCenter().FlashStatus )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -1111,6 +1113,11 @@ function TASK:SetAssignedMenuForGroup( TaskGroup, MenuTime )
|
||||
end
|
||||
local MarkMenu = MENU_GROUP_COMMAND:New( TaskGroup, string.format( "Mark Task Location on Map" ), TaskControl, self.MenuMarkToGroup, self, TaskGroup ):SetTime( MenuTime ):SetTag( "Tasking" )
|
||||
local TaskTypeMenu = MENU_GROUP_COMMAND:New( TaskGroup, string.format( "Report Task Details" ), TaskControl, self.MenuTaskStatus, self, TaskGroup ):SetTime( MenuTime ):SetTag( "Tasking" )
|
||||
if not self.FlashTaskStatus then
|
||||
local TaskFlashStatusMenu = MENU_GROUP_COMMAND:New( TaskGroup, string.format( "Flash Task Details" ), TaskControl, self.MenuFlashTaskStatus, self, TaskGroup, true ):SetTime( MenuTime ):SetTag( "Tasking" )
|
||||
else
|
||||
local TaskFlashStatusMenu = MENU_GROUP_COMMAND:New( TaskGroup, string.format( "Stop Flash Task Details" ), TaskControl, self.MenuFlashTaskStatus, self, TaskGroup, nil ):SetTime( MenuTime ):SetTag( "Tasking" )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1225,12 +1232,34 @@ end
|
||||
|
||||
--- Report the task status.
|
||||
-- @param #TASK self
|
||||
-- @param Wrapper.Group#GROUP TaskGroup
|
||||
function TASK:MenuTaskStatus( TaskGroup )
|
||||
|
||||
local ReportText = self:ReportDetails( TaskGroup )
|
||||
|
||||
self:T( ReportText )
|
||||
self:GetMission():GetCommandCenter():MessageTypeToGroup( ReportText, TaskGroup, MESSAGE.Type.Detailed )
|
||||
if TaskGroup:IsAlive() then
|
||||
|
||||
local ReportText = self:ReportDetails( TaskGroup )
|
||||
|
||||
self:T( ReportText )
|
||||
self:GetMission():GetCommandCenter():MessageTypeToGroup( ReportText, TaskGroup, MESSAGE.Type.Detailed )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- Report the task status.
|
||||
-- @param #TASK self
|
||||
function TASK:MenuFlashTaskStatus( TaskGroup, Flash )
|
||||
|
||||
self.FlashTaskStatus = Flash
|
||||
|
||||
if self.FlashTaskStatus then
|
||||
self.FlashTaskScheduler, self.FlashTaskScheduleID = SCHEDULER:New( self, self.MenuTaskStatus, { TaskGroup }, 0, 60 )
|
||||
else
|
||||
if self.FlashTaskScheduler then
|
||||
self.FlashTaskScheduler:Stop( self.FlashTaskScheduleID )
|
||||
self.FlashTaskScheduler = nil
|
||||
self.FlashTaskScheduleID = nil
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
@@ -52,16 +52,16 @@ end
|
||||
|
||||
--- Add taskinfo.
|
||||
-- @param #TASKINFO self
|
||||
-- @param #string The info key.
|
||||
-- @param #string Key The info key.
|
||||
-- @param Data The data of the info.
|
||||
-- @param #number Order The display order, which is a number from 0 to 100.
|
||||
-- @param #TASKINFO.Detail Detail The detail Level.
|
||||
-- @param #boolean Keep (optional) If true, this would indicate that the planned taskinfo would be persistent when the task is completed, so that the original planned task info is used at the completed reports.
|
||||
-- @return #TASKINFO self
|
||||
function TASKINFO:AddInfo( Key, Data, Order, Detail, Keep )
|
||||
self.VolatileInfo:Add( Key, { Data = Data, Order = Order, Detail = Detail } )
|
||||
function TASKINFO:AddInfo( Key, Data, Order, Detail, Keep, ShowKey, Type )
|
||||
self.VolatileInfo:Add( Key, { Data = Data, Order = Order, Detail = Detail, ShowKey = ShowKey, Type = Type } )
|
||||
if Keep == true then
|
||||
self.PersistentInfo:Add( Key, { Data = Data, Order = Order, Detail = Detail } )
|
||||
self.PersistentInfo:Add( Key, { Data = Data, Order = Order, Detail = Detail, ShowKey = ShowKey, Type = Type } )
|
||||
end
|
||||
return self
|
||||
end
|
||||
@@ -124,8 +124,8 @@ end
|
||||
-- @param #TASKINFO.Detail Detail The detail Level.
|
||||
-- @param #boolean Keep (optional) If true, this would indicate that the planned taskinfo would be persistent when the task is completed, so that the original planned task info is used at the completed reports.
|
||||
-- @return #TASKINFO self
|
||||
function TASKINFO:AddCoordinate( Coordinate, Order, Detail, Keep )
|
||||
self:AddInfo( "Coordinate", Coordinate, Order, Detail, Keep )
|
||||
function TASKINFO:AddCoordinate( Coordinate, Order, Detail, Keep, ShowKey, Name )
|
||||
self:AddInfo( Name or "Coordinate", Coordinate, Order, Detail, Keep, ShowKey, "Coordinate" )
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -133,8 +133,8 @@ end
|
||||
--- Get the Coordinate.
|
||||
-- @param #TASKINFO self
|
||||
-- @return Core.Point#COORDINATE Coordinate
|
||||
function TASKINFO:GetCoordinate()
|
||||
return self:GetData( "Coordinate" )
|
||||
function TASKINFO:GetCoordinate( Name )
|
||||
return self:GetData( Name or "Coordinate" )
|
||||
end
|
||||
|
||||
|
||||
@@ -308,10 +308,11 @@ function TASKINFO:Report( Report, Detail, ReportGroup, Task )
|
||||
|
||||
if Data.Detail:find( Detail ) then
|
||||
local Text = ""
|
||||
local ShowKey = ( Data.ShowKey == nil or Data.ShowKey == true )
|
||||
if Key == "TaskName" then
|
||||
Key = nil
|
||||
Text = Data.Data
|
||||
elseif Key == "Coordinate" then
|
||||
elseif Data.Type and Data.Type == "Coordinate" then
|
||||
local Coordinate = Data.Data -- Core.Point#COORDINATE
|
||||
Text = Coordinate:ToString( ReportGroup:GetUnit(1), nil, Task )
|
||||
elseif Key == "Threat" then
|
||||
@@ -357,7 +358,7 @@ function TASKINFO:Report( Report, Detail, ReportGroup, Task )
|
||||
end
|
||||
|
||||
if Text ~= "" then
|
||||
LineReport:Add( ( Key and ( Key .. ":" ) or "" ) .. Text )
|
||||
LineReport:Add( ( ( Key and ShowKey == true ) and ( Key .. ": " ) or "" ) .. Text )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -194,6 +194,45 @@ do -- TASK_CAPTURE_DISPATCHER
|
||||
end
|
||||
|
||||
|
||||
--- Link a task capture dispatcher from the other coalition to understand its plan for defenses.
|
||||
-- This is used for the tactical overview, so the players also know the zones attacked by the other coalition!
|
||||
-- @param #TASK_CAPTURE_DISPATCHER self
|
||||
-- @param #TASK_CAPTURE_DISPATCHER DefenseTaskCaptureDispatcher
|
||||
function TASK_CAPTURE_DISPATCHER:SetDefenseTaskCaptureDispatcher( DefenseTaskCaptureDispatcher )
|
||||
|
||||
self.DefenseTaskCaptureDispatcher = DefenseTaskCaptureDispatcher
|
||||
end
|
||||
|
||||
|
||||
--- Get the linked task capture dispatcher from the other coalition to understand its plan for defenses.
|
||||
-- This is used for the tactical overview, so the players also know the zones attacked by the other coalition!
|
||||
-- @param #TASK_CAPTURE_DISPATCHER self
|
||||
-- @return #TASK_CAPTURE_DISPATCHER
|
||||
function TASK_CAPTURE_DISPATCHER:GetDefenseTaskCaptureDispatcher()
|
||||
|
||||
return self.DefenseTaskCaptureDispatcher
|
||||
end
|
||||
|
||||
|
||||
--- Link an AI A2G dispatcher from the other coalition to understand its plan for defenses.
|
||||
-- This is used for the tactical overview, so the players also know the zones attacked by the other AI A2G dispatcher!
|
||||
-- @param #TASK_CAPTURE_DISPATCHER self
|
||||
-- @param AI.AI_A2G_Dispatcher#AI_A2G_DISPATCHER DefenseAIA2GDispatcher
|
||||
function TASK_CAPTURE_DISPATCHER:SetDefenseAIA2GDispatcher( DefenseAIA2GDispatcher )
|
||||
|
||||
self.DefenseAIA2GDispatcher = DefenseAIA2GDispatcher
|
||||
end
|
||||
|
||||
|
||||
--- Get the linked AI A2G dispatcher from the other coalition to understand its plan for defenses.
|
||||
-- This is used for the tactical overview, so the players also know the zones attacked by the AI A2G dispatcher!
|
||||
-- @param #TASK_CAPTURE_DISPATCHER self
|
||||
-- @return AI.AI_A2G_Dispatcher#AI_A2G_DISPATCHER
|
||||
function TASK_CAPTURE_DISPATCHER:GetDefenseAIA2GDispatcher()
|
||||
|
||||
return self.DefenseAIA2GDispatcher
|
||||
end
|
||||
|
||||
|
||||
--- Add a capture zone task.
|
||||
-- @param #TASK_CAPTURE_DISPATCHER self
|
||||
@@ -274,6 +313,9 @@ do -- TASK_CAPTURE_DISPATCHER
|
||||
CaptureZone.Task.TaskPrefix = CaptureZone.TaskPrefix -- We keep the TaskPrefix for further reference!
|
||||
Mission:AddTask( CaptureZone.Task )
|
||||
TaskReport:Add( TaskName )
|
||||
|
||||
-- Link the Task Dispatcher to the capture zone task, because it is used on the UpdateTaskInfo.
|
||||
CaptureZone.Task:SetDispatcher( self )
|
||||
CaptureZone.Task:UpdateTaskInfo()
|
||||
|
||||
function CaptureZone.Task.OnEnterAssigned( Task, From, Event, To )
|
||||
@@ -281,6 +323,7 @@ do -- TASK_CAPTURE_DISPATCHER
|
||||
self.AI_A2G_Dispatcher:Unlock( Task.TaskZoneName ) -- This will unlock the zone to be defended by AI.
|
||||
end
|
||||
CaptureZone.Task:UpdateTaskInfo()
|
||||
CaptureZone.Task.ZoneGoal.Attacked = true
|
||||
end
|
||||
|
||||
function CaptureZone.Task.OnEnterSuccess( Task, From, Event, To )
|
||||
@@ -289,6 +332,7 @@ do -- TASK_CAPTURE_DISPATCHER
|
||||
self.AI_A2G_Dispatcher:Lock( Task.TaskZoneName ) -- This will lock the zone from being defended by AI.
|
||||
end
|
||||
CaptureZone.Task:UpdateTaskInfo()
|
||||
CaptureZone.Task.ZoneGoal.Attacked = false
|
||||
end
|
||||
|
||||
function CaptureZone.Task.OnEnterCancelled( Task, From, Event, To )
|
||||
@@ -297,6 +341,7 @@ do -- TASK_CAPTURE_DISPATCHER
|
||||
self.AI_A2G_Dispatcher:Lock( Task.TaskZoneName ) -- This will lock the zone from being defended by AI.
|
||||
end
|
||||
CaptureZone.Task:UpdateTaskInfo()
|
||||
CaptureZone.Task.ZoneGoal.Attacked = false
|
||||
end
|
||||
|
||||
function CaptureZone.Task.OnEnterFailed( Task, From, Event, To )
|
||||
@@ -305,6 +350,7 @@ do -- TASK_CAPTURE_DISPATCHER
|
||||
self.AI_A2G_Dispatcher:Lock( Task.TaskZoneName ) -- This will lock the zone from being defended by AI.
|
||||
end
|
||||
CaptureZone.Task:UpdateTaskInfo()
|
||||
CaptureZone.Task.ZoneGoal.Attacked = false
|
||||
end
|
||||
|
||||
function CaptureZone.Task.OnEnterAborted( Task, From, Event, To )
|
||||
@@ -313,6 +359,7 @@ do -- TASK_CAPTURE_DISPATCHER
|
||||
self.AI_A2G_Dispatcher:Lock( Task.TaskZoneName ) -- This will lock the zone from being defended by AI.
|
||||
end
|
||||
CaptureZone.Task:UpdateTaskInfo()
|
||||
CaptureZone.Task.ZoneGoal.Attacked = false
|
||||
end
|
||||
|
||||
-- Now broadcast the onafterCargoPickedUp event to the Task Cargo Dispatcher.
|
||||
@@ -322,6 +369,7 @@ do -- TASK_CAPTURE_DISPATCHER
|
||||
self.AI_A2G_Dispatcher:Lock( Task.TaskZoneName ) -- This will lock the zone from being defended by AI.
|
||||
end
|
||||
CaptureZone.Task:UpdateTaskInfo()
|
||||
CaptureZone.Task.ZoneGoal.Attacked = false
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -228,11 +228,47 @@ do -- TASK_CAPTURE_ZONE
|
||||
local ZoneCoordinate = self.ZoneGoal:GetZone():GetCoordinate()
|
||||
self.TaskInfo:AddTaskName( 0, "MSOD", Persist )
|
||||
self.TaskInfo:AddCoordinate( ZoneCoordinate, 1, "SOD", Persist )
|
||||
self.TaskInfo:AddText( "Zone Name", self.ZoneGoal:GetZoneName(), 10, "MOD", Persist )
|
||||
self.TaskInfo:AddText( "Zone Coalition", self.ZoneGoal:GetCoalitionName(), 11, "MOD", Persist )
|
||||
local SetUnit = self.ZoneGoal.Zone:GetScannedSetUnit()
|
||||
-- self.TaskInfo:AddText( "Zone Name", self.ZoneGoal:GetZoneName(), 10, "MOD", Persist )
|
||||
-- self.TaskInfo:AddText( "Zone Coalition", self.ZoneGoal:GetCoalitionName(), 11, "MOD", Persist )
|
||||
local SetUnit = self.ZoneGoal:GetScannedSetUnit()
|
||||
local ThreatLevel, ThreatText = SetUnit:CalculateThreatLevelA2G()
|
||||
local ThreatCount = SetUnit:Count()
|
||||
self.TaskInfo:AddThreat( ThreatText, ThreatLevel, 20, "MOD", Persist )
|
||||
self.TaskInfo:AddInfo( "Remaining Units", ThreatCount, 21, "MOD", Persist, true)
|
||||
|
||||
if self.Dispatcher then
|
||||
local DefenseTaskCaptureDispatcher = self.Dispatcher:GetDefenseTaskCaptureDispatcher() -- Tasking.Task_Capture_Dispatcher#TASK_CAPTURE_DISPATCHER
|
||||
|
||||
if DefenseTaskCaptureDispatcher then
|
||||
-- Loop through all zones of the player Defenses, and check which zone has an assigned task!
|
||||
-- The Zones collection contains a Task. This Task is checked if it is assigned.
|
||||
-- If Assigned, then this task will be the task that is the closest to the defense zone.
|
||||
for TaskName, CaptureZone in pairs( DefenseTaskCaptureDispatcher.Zones or {} ) do
|
||||
local Task = CaptureZone.Task -- Tasking.Task_Capture_Zone#TASK_CAPTURE_ZONE
|
||||
if Task and Task:IsStateAssigned() then -- We also check assigned.
|
||||
-- Now we register the defense player zone information to the task report.
|
||||
self.TaskInfo:AddInfo( "Defense Player Zone", Task.ZoneGoal:GetName(), 30, "MOD", Persist )
|
||||
self.TaskInfo:AddCoordinate( Task.ZoneGoal:GetZone():GetCoordinate(), 31, "MOD", Persist, false, "Defense Player Coordinate" )
|
||||
end
|
||||
end
|
||||
end
|
||||
local DefenseAIA2GDispatcher = self.Dispatcher:GetDefenseAIA2GDispatcher() -- AI.AI_A2G_Dispatcher#AI_A2G_DISPATCHER
|
||||
|
||||
if DefenseAIA2GDispatcher then
|
||||
-- Loop through all the tasks of the AI Defenses, and check which zone is involved in the defenses and is active!
|
||||
for Defender, Task in pairs( DefenseAIA2GDispatcher:GetDefenderTasks() or {} ) do
|
||||
local DetectedItem = DefenseAIA2GDispatcher:GetDefenderTaskTarget( Defender )
|
||||
if DetectedItem then
|
||||
local DetectedZone = DefenseAIA2GDispatcher.Detection:GetDetectedItemZone( DetectedItem )
|
||||
if DetectedZone then
|
||||
self.TaskInfo:AddInfo( "Defense AI Zone", DetectedZone:GetName(), 40, "MOD", Persist )
|
||||
self.TaskInfo:AddCoordinate( DetectedZone:GetCoordinate(), 41, "MOD", Persist, false, "Defense AI Coordinate" )
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
@@ -274,7 +310,7 @@ do -- TASK_CAPTURE_ZONE
|
||||
-- @param #number AutoAssignMethod The method to be applied to the task.
|
||||
-- @param Tasking.CommandCenter#COMMANDCENTER CommandCenter The command center.
|
||||
-- @param Wrapper.Group#GROUP TaskGroup The player group.
|
||||
function TASK_CAPTURE_ZONE:GetAutoAssignPriority( AutoAssignMethod, CommandCenter, TaskGroup )
|
||||
function TASK_CAPTURE_ZONE:GetAutoAssignPriority( AutoAssignMethod, CommandCenter, TaskGroup, AutoAssignReference )
|
||||
|
||||
if AutoAssignMethod == COMMANDCENTER.AutoAssignMethods.Random then
|
||||
return math.random( 1, 9 )
|
||||
|
||||
@@ -185,7 +185,6 @@ do -- TASK_MANAGER
|
||||
-- @param #TASK_MANAGER self
|
||||
-- @return #TASK_MANAGER self
|
||||
function TASK_MANAGER:ManageTasks()
|
||||
self:E()
|
||||
|
||||
end
|
||||
|
||||
|
||||
@@ -521,6 +521,32 @@ function UTILS.spairs( t, order )
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- Here is a customized version of pairs, which I called kpairs because it iterates over the table in a sorted order, based on a function that will determine the keys as reference first.
|
||||
function UTILS.kpairs( t, getkey, order )
|
||||
-- collect the keys
|
||||
local keys = {}
|
||||
local keyso = {}
|
||||
for k, o in pairs(t) do keys[#keys+1] = k keyso[#keyso+1] = getkey( o ) end
|
||||
|
||||
-- if order function given, sort by it by passing the table and keys a, b,
|
||||
-- otherwise just sort the keys
|
||||
if order then
|
||||
table.sort(keys, function(a,b) return order(t, a, b) end)
|
||||
else
|
||||
table.sort(keys)
|
||||
end
|
||||
|
||||
-- return the iterator function
|
||||
local i = 0
|
||||
return function()
|
||||
i = i + 1
|
||||
if keys[i] then
|
||||
return keyso[i], t[keys[i]]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Here is a customized version of pairs, which I called rpairs because it iterates over the table in a random order.
|
||||
function UTILS.rpairs( t )
|
||||
-- collect the keys
|
||||
|
||||
@@ -133,7 +133,8 @@ function CLIENT:FindByName( ClientName, ClientBriefing, Error )
|
||||
end
|
||||
|
||||
function CLIENT:Register( ClientName )
|
||||
local self = BASE:Inherit( self, UNIT:Register( ClientName ) )
|
||||
|
||||
local self = BASE:Inherit( self, UNIT:Register( ClientName ) ) -- #CLIENT
|
||||
|
||||
self:F( ClientName )
|
||||
self.ClientName = ClientName
|
||||
@@ -141,7 +142,7 @@ function CLIENT:Register( ClientName )
|
||||
self.ClientAlive2 = false
|
||||
|
||||
--self.AliveCheckScheduler = routines.scheduleFunction( self._AliveCheckScheduler, { self }, timer.getTime() + 1, 5 )
|
||||
self.AliveCheckScheduler = SCHEDULER:New( self, self._AliveCheckScheduler, { "Client Alive " .. ClientName }, 1, 5 )
|
||||
self.AliveCheckScheduler = SCHEDULER:New( self, self._AliveCheckScheduler, { "Client Alive " .. ClientName }, 1, 5, 0.5 )
|
||||
|
||||
self:F( self )
|
||||
return self
|
||||
|
||||
@@ -307,7 +307,6 @@ end
|
||||
-- @param #CONTROLLABLE self
|
||||
-- @return #CONTROLLABLE
|
||||
function CONTROLLABLE:ClearTasks()
|
||||
self:E( "ClearTasks" )
|
||||
|
||||
local DCSControllable = self:GetDCSObject()
|
||||
|
||||
@@ -444,7 +443,7 @@ end
|
||||
-- @param #CONTROLLABLE self
|
||||
-- @param DCS#Time time DCS mission time.
|
||||
-- @param #string userFlag Name of the user flag.
|
||||
-- @param #boolean userFlagValue User flag value *true* or *false*. Could also be numeric, i.e. either 0=*false* or 1=*true*. Other numeric values don't work!
|
||||
-- @param #boolean userFlagValue User flag value *true* or *false*. Could also be numeric, i.e. either 0=*false* or 1=*true*. Other numeric values don't work!
|
||||
-- @param #string condition Lua string.
|
||||
-- @param DCS#Time duration Duration in seconds.
|
||||
-- @param #number lastWayPoint Last waypoint.
|
||||
@@ -944,7 +943,7 @@ end
|
||||
-- @param #boolean Divebomb (optional) Perform dive bombing. Default false.
|
||||
-- @return DCS#Task The DCS task structure.
|
||||
function CONTROLLABLE:TaskBombing( Vec2, GroupAttack, WeaponExpend, AttackQty, Direction, Altitude, WeaponType, Divebomb )
|
||||
self:E( { self.ControllableName, Vec2, GroupAttack, WeaponExpend, AttackQty, Direction, Altitude, WeaponType, Divebomb } )
|
||||
self:F( { self.ControllableName, Vec2, GroupAttack, WeaponExpend, AttackQty, Direction, Altitude, WeaponType, Divebomb } )
|
||||
|
||||
local _groupattack=false
|
||||
if GroupAttack then
|
||||
@@ -990,7 +989,7 @@ function CONTROLLABLE:TaskBombing( Vec2, GroupAttack, WeaponExpend, AttackQty, D
|
||||
},
|
||||
}
|
||||
|
||||
self:E( { TaskBombing=DCSTask } )
|
||||
self:F( { TaskBombing=DCSTask } )
|
||||
return DCSTask
|
||||
end
|
||||
|
||||
|
||||
@@ -696,7 +696,9 @@ end
|
||||
|
||||
|
||||
--- Returns the Unit's A2G threat level on a scale from 1 to 10 ...
|
||||
-- The following threat levels are foreseen:
|
||||
-- Depending on the era and the type of unit, the following threat levels are foreseen:
|
||||
--
|
||||
-- **Modern**:
|
||||
--
|
||||
-- * Threat level 0: Unit is unarmed.
|
||||
-- * Threat level 1: Unit is infantry.
|
||||
@@ -709,13 +711,49 @@ end
|
||||
-- * Threat level 8: Unit is a Short Range SAM, radar guided.
|
||||
-- * Threat level 9: Unit is a Medium Range SAM, radar guided.
|
||||
-- * Threat level 10: Unit is a Long Range SAM, radar guided.
|
||||
--
|
||||
-- **Cold**:
|
||||
--
|
||||
-- * Threat level 0: Unit is unarmed.
|
||||
-- * Threat level 1: Unit is infantry.
|
||||
-- * Threat level 2: Unit is an infantry vehicle.
|
||||
-- * Threat level 3: Unit is ground artillery.
|
||||
-- * Threat level 4: Unit is a tank.
|
||||
-- * Threat level 5: Unit is a modern tank or ifv with ATGM.
|
||||
-- * Threat level 6: Unit is a AAA.
|
||||
-- * Threat level 7: Unit is a SAM or manpad, IR guided.
|
||||
-- * Threat level 8: Unit is a Short Range SAM, radar guided.
|
||||
-- * Threat level 10: Unit is a Medium Range SAM, radar guided.
|
||||
--
|
||||
-- **Korea**:
|
||||
--
|
||||
-- * Threat level 0: Unit is unarmed.
|
||||
-- * Threat level 1: Unit is infantry.
|
||||
-- * Threat level 2: Unit is an infantry vehicle.
|
||||
-- * Threat level 3: Unit is ground artillery.
|
||||
-- * Threat level 5: Unit is a tank.
|
||||
-- * Threat level 6: Unit is a AAA.
|
||||
-- * Threat level 7: Unit is a SAM or manpad, IR guided.
|
||||
-- * Threat level 10: Unit is a Short Range SAM, radar guided.
|
||||
--
|
||||
-- **WWII**:
|
||||
--
|
||||
-- * Threat level 0: Unit is unarmed.
|
||||
-- * Threat level 1: Unit is infantry.
|
||||
-- * Threat level 2: Unit is an infantry vehicle.
|
||||
-- * Threat level 3: Unit is ground artillery.
|
||||
-- * Threat level 5: Unit is a tank.
|
||||
-- * Threat level 7: Unit is FLAK.
|
||||
-- * Threat level 10: Unit is AAA.
|
||||
--
|
||||
--
|
||||
-- @param #UNIT self
|
||||
function UNIT:GetThreatLevel()
|
||||
|
||||
|
||||
local ThreatLevel = 0
|
||||
local ThreatText = ""
|
||||
|
||||
|
||||
local Descriptor = self:GetDesc()
|
||||
|
||||
if Descriptor then
|
||||
|
||||
Reference in New Issue
Block a user