Merge branch 'master' into LDT-Debug

# Conflicts:
#	Moose Development/Moose/Moose.lua
#	Moose Mission Setup/Moose.lua
This commit is contained in:
FlightControl_Master
2017-11-05 06:09:41 +01:00
370 changed files with 85751 additions and 9019 deletions

View File

@@ -70,8 +70,9 @@ function AI_A2A:New( AIGroup )
self:SetControllable( AIGroup )
self:ManageFuel( .2, 60 )
self:ManageDamage( 0.4 )
self:SetFuelThreshold( .2, 60 )
self:SetDamageThreshold( 0.4 )
self:SetDisengageRadius( 70000 )
self:SetStartState( "Stopped" )
@@ -219,8 +220,37 @@ function AI_A2A:New( AIGroup )
-- @param #string Event The Event string.
-- @param #string To The To State string.
self:AddTransition( "Patrolling", "Refuel", "Refuelling" )
--- Refuel Handler OnBefore for AI_A2A
-- @function [parent=#AI_A2A] OnBeforeRefuel
-- @param #AI_A2A self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From
-- @param #string Event
-- @param #string To
-- @return #boolean
--- Refuel Handler OnAfter for AI_A2A
-- @function [parent=#AI_A2A] OnAfterRefuel
-- @param #AI_A2A self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From
-- @param #string Event
-- @param #string To
--- Refuel Trigger for AI_A2A
-- @function [parent=#AI_A2A] Refuel
-- @param #AI_A2A self
--- Refuel Asynchronous Trigger for AI_A2A
-- @function [parent=#AI_A2A] __Refuel
-- @param #AI_A2A self
-- @param #number Delay
self:AddTransition( "*", "Takeoff", "Airborne" )
self:AddTransition( "*", "Return", "Returning" )
self:AddTransition( "*", "Hold", "Holding" )
self:AddTransition( "*", "Home", "Home" )
self:AddTransition( "*", "LostControl", "LostControl" )
self:AddTransition( "*", "Fuel", "Fuel" )
@@ -234,6 +264,13 @@ function AI_A2A:New( AIGroup )
return self
end
--- @param Wrapper.Group#GROUP self
-- @param Core.Event#EVENTDATA EventData
function GROUP:OnEventTakeoff( EventData, Fsm )
Fsm:Takeoff()
self:UnHandleEvent( EVENTS.Takeoff )
end
function AI_A2A:SetDispatcher( Dispatcher )
self.Dispatcher = Dispatcher
end
@@ -294,8 +331,27 @@ function AI_A2A:SetHomeAirbase( HomeAirbase )
self.HomeAirbase = HomeAirbase
end
--- Sets to refuel at the given tanker.
-- @param #AI_A2A self
-- @param Wrapper.Group#GROUP TankerName The group name of the tanker as defined within the Mission Editor or spawned.
-- @return #AI_A2A self
function AI_A2A:SetTanker( TankerName )
self:F2( { TankerName } )
self.TankerName = TankerName
end
--- Sets the disengage range, that when engaging a target beyond the specified range, the engagement will be cancelled and the plane will RTB.
-- @param #AI_A2A self
-- @param #number DisengageRadius The disengage range.
-- @return #AI_A2A self
function AI_A2A:SetDisengageRadius( DisengageRadius )
self:F2( { DisengageRadius } )
self.DisengageRadius = DisengageRadius
end
--- Set the status checking off.
-- @param #AI_A2A self
-- @return #AI_A2A self
@@ -311,13 +367,13 @@ end
-- When the fuel treshold is reached, the AI will continue for a given time its patrol task in orbit, while a new AIControllable is targetted to the AI_A2A.
-- Once the time is finished, the old AI will return to the base.
-- @param #AI_A2A self
-- @param #number PatrolFuelTresholdPercentage The treshold in percentage (between 0 and 1) when the AIControllable is considered to get out of fuel.
-- @param #number PatrolFuelThresholdPercentage The treshold in percentage (between 0 and 1) when the AIControllable is considered to get out of fuel.
-- @param #number PatrolOutOfFuelOrbitTime The amount of seconds the out of fuel AIControllable will orbit before returning to the base.
-- @return #AI_A2A self
function AI_A2A:ManageFuel( PatrolFuelTresholdPercentage, PatrolOutOfFuelOrbitTime )
function AI_A2A:SetFuelThreshold( PatrolFuelThresholdPercentage, PatrolOutOfFuelOrbitTime )
self.PatrolManageFuel = true
self.PatrolFuelTresholdPercentage = PatrolFuelTresholdPercentage
self.PatrolFuelThresholdPercentage = PatrolFuelThresholdPercentage
self.PatrolOutOfFuelOrbitTime = PatrolOutOfFuelOrbitTime
self.Controllable:OptionRTBBingoFuel( false )
@@ -332,12 +388,12 @@ end
-- Note that for groups, the average damage of the complete group will be calculated.
-- So, in a group of 4 airplanes, 2 lost and 2 with damage 0.2, the damage treshold will be 0.25.
-- @param #AI_A2A self
-- @param #number PatrolDamageTreshold The treshold in percentage (between 0 and 1) when the AI is considered to be damaged.
-- @param #number PatrolDamageThreshold The treshold in percentage (between 0 and 1) when the AI is considered to be damaged.
-- @return #AI_A2A self
function AI_A2A:ManageDamage( PatrolDamageTreshold )
function AI_A2A:SetDamageThreshold( PatrolDamageThreshold )
self.PatrolManageDamage = true
self.PatrolDamageTreshold = PatrolDamageTreshold
self.PatrolDamageThreshold = PatrolDamageThreshold
return self
end
@@ -372,45 +428,79 @@ end
--- @param #AI_A2A self
function AI_A2A:onafterStatus()
self:F()
self:F( " Checking Status" )
if self.Controllable and self.Controllable:IsAlive() then
local RTB = false
local Fuel = self.Controllable:GetUnit(1):GetFuel()
self:F({Fuel=Fuel})
if Fuel < self.PatrolFuelTresholdPercentage then
self:E( self.Controllable:GetName() .. " is out of fuel: " .. Fuel .. " ... RTB!" )
local OldAIControllable = self.Controllable
local AIControllableTemplate = self.Controllable:GetTemplate()
local DistanceFromHomeBase = self.HomeAirbase:GetCoordinate():Get2DDistance( self.Controllable:GetCoordinate() )
if not self:Is( "Holding" ) and not self:Is( "Returning" ) then
local DistanceFromHomeBase = self.HomeAirbase:GetCoordinate():Get2DDistance( self.Controllable:GetCoordinate() )
self:F({DistanceFromHomeBase=DistanceFromHomeBase})
local OrbitTask = OldAIControllable:TaskOrbitCircle( math.random( self.PatrolFloorAltitude, self.PatrolCeilingAltitude ), self.PatrolMinSpeed )
local TimedOrbitTask = OldAIControllable:TaskControlled( OrbitTask, OldAIControllable:TaskCondition(nil,nil,nil,nil,self.PatrolOutOfFuelOrbitTime,nil ) )
OldAIControllable:SetTask( TimedOrbitTask, 10 )
if DistanceFromHomeBase > self.DisengageRadius then
self:E( self.Controllable:GetName() .. " is too far from home base, RTB!" )
self:Hold( 300 )
RTB = false
end
end
if self:Is( "Fuel" ) or self:Is( "Damaged" ) or self:Is( "LostControl" ) then
if DistanceFromHomeBase < 5000 then
self:E( self.Controllable:GetName() .. " is too far from home base, RTB!" )
self:Home( "Destroy" )
end
end
self:Fuel()
RTB = true
else
if not self:Is( "Fuel" ) and not self:Is( "Home" ) then
local Fuel = self.Controllable:GetFuel()
self:F({Fuel=Fuel})
if Fuel < self.PatrolFuelThresholdPercentage then
if self.TankerName then
self:E( 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!" )
local OldAIControllable = self.Controllable
local OrbitTask = OldAIControllable:TaskOrbitCircle( math.random( self.PatrolFloorAltitude, self.PatrolCeilingAltitude ), self.PatrolMinSpeed )
local TimedOrbitTask = OldAIControllable:TaskControlled( OrbitTask, OldAIControllable:TaskCondition(nil,nil,nil,nil,self.PatrolOutOfFuelOrbitTime,nil ) )
OldAIControllable:SetTask( TimedOrbitTask, 10 )
self:Fuel()
RTB = true
end
else
end
end
-- TODO: Check GROUP damage function.
local Damage = self.Controllable:GetLife()
local InitialLife = self.Controllable:GetLife0()
self:F( { Damage = Damage, InitialLife = InitialLife, DamageTreshold = self.PatrolDamageTreshold } )
if ( Damage / InitialLife ) < self.PatrolDamageTreshold then
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:Damaged()
RTB = true
self:SetStatusOff()
end
-- Check if planes went RTB and are out of control.
if self.Controllable:HasTask() == false then
if not self:Is( "Started" ) and
not self:Is( "Stopped" ) then
not self:Is( "Stopped" ) and
not self:Is( "Home" ) then
if self.IdleCount >= 2 then
self:E( self.Controllable:GetName() .. " control lost! " )
self:LostControl()
if Damage ~= InitialLife then
self:Damaged()
else
self:E( self.Controllable:GetName() .. " control lost! " )
self:LostControl()
end
else
self.IdleCount = self.IdleCount + 1
end
@@ -418,7 +508,7 @@ function AI_A2A:onafterStatus()
else
self.IdleCount = 0
end
if RTB == true then
self:__RTB( 0.5 )
end
@@ -429,13 +519,28 @@ end
--- @param Wrapper.Group#GROUP AIGroup
function AI_A2A.RTBRoute( AIGroup )
function AI_A2A.RTBRoute( AIGroup, Fsm )
AIGroup:E( { "RTBRoute:", AIGroup:GetName() } )
local _AI_A2A = AIGroup:GetState( AIGroup, "AI_A2A" ) -- #AI_A2A
_AI_A2A:__RTB( 0.5 )
AIGroup:F( { "AI_A2A.RTBRoute:", AIGroup:GetName() } )
if AIGroup:IsAlive() then
Fsm:__RTB( 0.5 )
end
end
--- @param Wrapper.Group#GROUP AIGroup
function AI_A2A.RTBHold( AIGroup, Fsm )
AIGroup:F( { "AI_A2A.RTBHold:", AIGroup:GetName() } )
if AIGroup:IsAlive() then
Fsm:__RTB( 0.5 )
Fsm:Return()
local Task = AIGroup:TaskOrbitCircle( 4000, 400 )
AIGroup:SetTask( Task )
end
end
--- @param #AI_A2A self
@@ -448,8 +553,6 @@ function AI_A2A:onafterRTB( AIGroup, From, Event, To )
self:E( "Group " .. AIGroup:GetName() .. " ... RTB! ( " .. self:GetState() .. " )" )
self.CheckStatus = false
self:ClearTargetDistance()
AIGroup:ClearTasks()
@@ -466,11 +569,12 @@ function AI_A2A:onafterRTB( AIGroup, From, Event, To )
local ToAirbaseCoord = CurrentCoord:Translate( 5000, ToAirbaseAngle )
if Distance < 5000 then
self:E( "RTB and near the airbase!" )
self:Home()
return
end
--- Create a route point of type air.
local ToPatrolRoutePoint = ToAirbaseCoord:RoutePointAir(
local ToRTBRoutePoint = ToAirbaseCoord:WaypointAir(
self.PatrolAltType,
POINT_VEC3.RoutePointType.TurningPoint,
POINT_VEC3.RoutePointAction.TurningPoint,
@@ -481,7 +585,8 @@ function AI_A2A:onafterRTB( AIGroup, From, Event, To )
self:F( { Angle = ToAirbaseAngle, ToTargetSpeed = ToTargetSpeed } )
self:T2( { self.MinSpeed, self.MaxSpeed, ToTargetSpeed } )
EngageRoute[#EngageRoute+1] = ToPatrolRoutePoint
EngageRoute[#EngageRoute+1] = ToRTBRoutePoint
EngageRoute[#EngageRoute+1] = ToRTBRoutePoint
AIGroup:OptionROEHoldFire()
AIGroup:OptionROTEvadeFire()
@@ -490,13 +595,11 @@ function AI_A2A:onafterRTB( AIGroup, From, Event, To )
AIGroup:WayPointInitialize( EngageRoute )
local Tasks = {}
Tasks[#Tasks+1] = AIGroup:TaskFunction( 1, 1, "AI_A2A.RTBRoute" )
EngageRoute[1].task = AIGroup:TaskCombo( Tasks )
AIGroup:SetState( AIGroup, "AI_A2A", self )
Tasks[#Tasks+1] = AIGroup:TaskFunction( "AI_A2A.RTBRoute", self )
EngageRoute[#EngageRoute].task = AIGroup:TaskCombo( Tasks )
--- NOW ROUTE THE GROUP!
AIGroup:WayPointExecute( 1, 0 )
AIGroup:Route( EngageRoute, 0.5 )
end
@@ -513,6 +616,89 @@ function AI_A2A:onafterHome( AIGroup, From, Event, To )
end
end
--- @param #AI_A2A self
-- @param Wrapper.Group#GROUP AIGroup
function AI_A2A:onafterHold( AIGroup, From, Event, To, HoldTime )
self:F( { AIGroup, From, Event, To } )
self:E( "Group " .. self.Controllable:GetName() .. " ... Holding! ( " .. self:GetState() .. " )" )
if AIGroup and AIGroup:IsAlive() then
local OrbitTask = AIGroup:TaskOrbitCircle( math.random( self.PatrolFloorAltitude, self.PatrolCeilingAltitude ), self.PatrolMinSpeed )
local TimedOrbitTask = AIGroup:TaskControlled( OrbitTask, AIGroup:TaskCondition( nil, nil, nil, nil, HoldTime , nil ) )
local RTBTask = AIGroup:TaskFunction( "AI_A2A.RTBHold", self )
local OrbitHoldTask = AIGroup:TaskOrbitCircle( 4000, self.PatrolMinSpeed )
--AIGroup:SetState( AIGroup, "AI_A2A", self )
AIGroup:SetTask( AIGroup:TaskCombo( { TimedOrbitTask, RTBTask, OrbitHoldTask } ), 1 )
end
end
--- @param Wrapper.Group#GROUP AIGroup
function AI_A2A.Resume( AIGroup, Fsm )
AIGroup:F( { "AI_A2A.Resume:", AIGroup:GetName() } )
if AIGroup:IsAlive() then
Fsm:__RTB( 0.5 )
end
end
--- @param #AI_A2A self
-- @param Wrapper.Group#GROUP AIGroup
function AI_A2A: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
local RefuelRoute = {}
--- Calculate the target route point.
local CurrentCoord = AIGroup:GetCoordinate()
local ToRefuelCoord = Tanker:GetCoordinate()
local ToRefuelSpeed = math.random( self.PatrolMinSpeed, self.PatrolMaxSpeed )
--- Create a route point of type air.
local ToRefuelRoutePoint = ToRefuelCoord:WaypointAir(
self.PatrolAltType,
POINT_VEC3.RoutePointType.TurningPoint,
POINT_VEC3.RoutePointAction.TurningPoint,
ToRefuelSpeed,
true
)
self:F( { ToRefuelSpeed = ToRefuelSpeed } )
RefuelRoute[#RefuelRoute+1] = ToRefuelRoutePoint
RefuelRoute[#RefuelRoute+1] = ToRefuelRoutePoint
AIGroup:OptionROEHoldFire()
AIGroup:OptionROTEvadeFire()
local Tasks = {}
Tasks[#Tasks+1] = AIGroup:TaskRefueling()
Tasks[#Tasks+1] = AIGroup:TaskFunction( self:GetClassName() .. ".Resume", self )
RefuelRoute[#RefuelRoute].task = AIGroup:TaskCombo( Tasks )
AIGroup:Route( RefuelRoute, 0.5 )
else
self:RTB()
end
end
end

View File

@@ -4,7 +4,7 @@
--
-- ===
--
-- AI CAP classes makes AI Controllables execute a Combat Air Patrol.
-- AI CAP classes makes AI Groups execute a Combat Air Patrol.
--
-- There are the following types of CAP classes defined:
--
@@ -34,7 +34,7 @@
--- # AI_A2A_CAP class, extends @{AI_CAP#AI_PATROL_ZONE}
--
-- The AI_A2A_CAP class implements the core functions to patrol a @{Zone} by an AI @{Controllable} or @{Group}
-- The AI_A2A_CAP class implements the core functions to patrol a @{Zone} by an AI @{Group} or @{Group}
-- and automatically engage any airborne enemies that are within a certain range or within a certain zone.
--
-- ![Process](..\Presentations\AI_CAP\Dia3.JPG)
@@ -120,20 +120,20 @@ AI_A2A_CAP = {
--- Creates a new AI_A2A_CAP object
-- @param #AI_A2A_CAP self
-- @param Wrapper.Group#GROUP AIGroup
-- @param Wrapper.Group#GROUP AICap
-- @param Core.Zone#ZONE_BASE PatrolZone The @{Zone} where the patrol needs to be executed.
-- @param Dcs.DCSTypes#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol.
-- @param Dcs.DCSTypes#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol.
-- @param Dcs.DCSTypes#Speed PatrolMinSpeed The minimum speed of the @{Controllable} in km/h.
-- @param Dcs.DCSTypes#Speed PatrolMaxSpeed The maximum speed of the @{Controllable} in km/h.
-- @param Dcs.DCSTypes#Speed EngageMinSpeed The minimum speed of the @{Controllable} in km/h when engaging a target.
-- @param Dcs.DCSTypes#Speed EngageMaxSpeed The maximum speed of the @{Controllable} in km/h when engaging a target.
-- @param Dcs.DCSTypes#Speed PatrolMinSpeed The minimum speed of the @{Group} in km/h.
-- @param Dcs.DCSTypes#Speed PatrolMaxSpeed The maximum speed of the @{Group} in km/h.
-- @param Dcs.DCSTypes#Speed EngageMinSpeed The minimum speed of the @{Group} in km/h when engaging a target.
-- @param Dcs.DCSTypes#Speed EngageMaxSpeed The maximum speed of the @{Group} in km/h when engaging a target.
-- @param Dcs.DCSTypes#AltitudeType PatrolAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to RADIO
-- @return #AI_A2A_CAP
function AI_A2A_CAP:New( AIGroup, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, EngageMinSpeed, EngageMaxSpeed, PatrolAltType )
function AI_A2A_CAP:New( AICap, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, EngageMinSpeed, EngageMaxSpeed, PatrolAltType )
-- Inherits from BASE
local self = BASE:Inherit( self, AI_A2A_PATROL:New( AIGroup, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType ) ) -- #AI_A2A_CAP
local self = BASE:Inherit( self, AI_A2A_PATROL:New( AICap, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType ) ) -- #AI_A2A_CAP
self.Accomplished = false
self.Engaging = false
@@ -141,12 +141,12 @@ function AI_A2A_CAP:New( AIGroup, PatrolZone, PatrolFloorAltitude, PatrolCeiling
self.EngageMinSpeed = EngageMinSpeed
self.EngageMaxSpeed = EngageMaxSpeed
self:AddTransition( { "Patrolling", "Engaging", "Returning" }, "Engage", "Engaging" ) -- FSM_CONTROLLABLE Transition for type #AI_A2A_CAP.
self:AddTransition( { "Patrolling", "Engaging", "Returning", "Airborne" }, "Engage", "Engaging" ) -- FSM_CONTROLLABLE Transition for type #AI_A2A_CAP.
--- OnBefore Transition Handler for Event Engage.
-- @function [parent=#AI_A2A_CAP] OnBeforeEngage
-- @param #AI_A2A_CAP self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param Wrapper.Group#GROUP AICap The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
@@ -155,7 +155,7 @@ function AI_A2A_CAP:New( AIGroup, PatrolZone, PatrolFloorAltitude, PatrolCeiling
--- OnAfter Transition Handler for Event Engage.
-- @function [parent=#AI_A2A_CAP] OnAfterEngage
-- @param #AI_A2A_CAP self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param Wrapper.Group#GROUP AICap The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
@@ -172,7 +172,7 @@ function AI_A2A_CAP:New( AIGroup, PatrolZone, PatrolFloorAltitude, PatrolCeiling
--- OnLeave Transition Handler for State Engaging.
-- @function [parent=#AI_A2A_CAP] OnLeaveEngaging
-- @param #AI_A2A_CAP self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param Wrapper.Group#GROUP AICap The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
@@ -181,7 +181,7 @@ function AI_A2A_CAP:New( AIGroup, PatrolZone, PatrolFloorAltitude, PatrolCeiling
--- OnEnter Transition Handler for State Engaging.
-- @function [parent=#AI_A2A_CAP] OnEnterEngaging
-- @param #AI_A2A_CAP self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param Wrapper.Group#GROUP AICap The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
@@ -191,7 +191,7 @@ function AI_A2A_CAP:New( AIGroup, PatrolZone, PatrolFloorAltitude, PatrolCeiling
--- OnBefore Transition Handler for Event Fired.
-- @function [parent=#AI_A2A_CAP] OnBeforeFired
-- @param #AI_A2A_CAP self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param Wrapper.Group#GROUP AICap The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
@@ -200,7 +200,7 @@ function AI_A2A_CAP:New( AIGroup, PatrolZone, PatrolFloorAltitude, PatrolCeiling
--- OnAfter Transition Handler for Event Fired.
-- @function [parent=#AI_A2A_CAP] OnAfterFired
-- @param #AI_A2A_CAP self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param Wrapper.Group#GROUP AICap The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
@@ -219,7 +219,7 @@ function AI_A2A_CAP:New( AIGroup, PatrolZone, PatrolFloorAltitude, PatrolCeiling
--- OnBefore Transition Handler for Event Destroy.
-- @function [parent=#AI_A2A_CAP] OnBeforeDestroy
-- @param #AI_A2A_CAP self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param Wrapper.Group#GROUP AICap The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
@@ -228,7 +228,7 @@ function AI_A2A_CAP:New( AIGroup, PatrolZone, PatrolFloorAltitude, PatrolCeiling
--- OnAfter Transition Handler for Event Destroy.
-- @function [parent=#AI_A2A_CAP] OnAfterDestroy
-- @param #AI_A2A_CAP self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param Wrapper.Group#GROUP AICap The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
@@ -248,7 +248,7 @@ function AI_A2A_CAP:New( AIGroup, PatrolZone, PatrolFloorAltitude, PatrolCeiling
--- OnBefore Transition Handler for Event Abort.
-- @function [parent=#AI_A2A_CAP] OnBeforeAbort
-- @param #AI_A2A_CAP self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param Wrapper.Group#GROUP AICap The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
@@ -257,7 +257,7 @@ function AI_A2A_CAP:New( AIGroup, PatrolZone, PatrolFloorAltitude, PatrolCeiling
--- OnAfter Transition Handler for Event Abort.
-- @function [parent=#AI_A2A_CAP] OnAfterAbort
-- @param #AI_A2A_CAP self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param Wrapper.Group#GROUP AICap The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
@@ -276,7 +276,7 @@ function AI_A2A_CAP:New( AIGroup, PatrolZone, PatrolFloorAltitude, PatrolCeiling
--- OnBefore Transition Handler for Event Accomplish.
-- @function [parent=#AI_A2A_CAP] OnBeforeAccomplish
-- @param #AI_A2A_CAP self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param Wrapper.Group#GROUP AICap The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
@@ -285,7 +285,7 @@ function AI_A2A_CAP:New( AIGroup, PatrolZone, PatrolFloorAltitude, PatrolCeiling
--- OnAfter Transition Handler for Event Accomplish.
-- @function [parent=#AI_A2A_CAP] OnAfterAccomplish
-- @param #AI_A2A_CAP self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param Wrapper.Group#GROUP AICap The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
@@ -302,6 +302,17 @@ function AI_A2A_CAP:New( AIGroup, PatrolZone, PatrolFloorAltitude, PatrolCeiling
return self
end
--- onafter State Transition for Event Patrol.
-- @param #AI_A2A_GCI self
-- @param Wrapper.Group#GROUP AICap The AI Group 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_A2A_CAP:onafterStart( AICap, From, Event, To )
AICap:HandleEvent( EVENTS.Takeoff, nil, self )
end
--- Set the Engage Zone which defines where the AI will engage bogies.
-- @param #AI_A2A_CAP self
@@ -333,33 +344,36 @@ end
--- onafter State Transition for Event Patrol.
-- @param #AI_A2A_CAP self
-- @param Wrapper.Controllable#CONTROLLABLE AIGroup The AI Group managed by the FSM.
-- @param Wrapper.Group#GROUP AICap The AI Group 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_A2A_CAP:onafterPatrol( AIGroup, From, Event, To )
function AI_A2A_CAP:onafterPatrol( AICap, From, Event, To )
-- Call the parent Start event handler
self:GetParent(self).onafterPatrol( self, AIGroup, From, Event, To )
self:GetParent(self).onafterPatrol( self, AICap, From, Event, To )
self:HandleEvent( EVENTS.Dead )
end
-- todo: need to fix this global function
--- @param Wrapper.Group#GROUP AIGroup
function AI_A2A_CAP.AttackRoute( AIGroup )
--- @param Wrapper.Group#GROUP AICap
function AI_A2A_CAP.AttackRoute( AICap, Fsm )
local EngageZone = AIGroup:GetState( AIGroup, "AI_A2A_CAP" ) -- AI.AI_Cap#AI_A2A_CAP
EngageZone:__Engage( 0.5 )
AICap:F( { "AI_A2A_CAP.AttackRoute:", AICap:GetName() } )
if AICap:IsAlive() then
Fsm:__Engage( 0.5 )
end
end
--- @param #AI_A2A_CAP self
-- @param Wrapper.Controllable#CONTROLLABLE AIGroup The Controllable Object managed by the FSM.
-- @param Wrapper.Group#GROUP AICap The Group Object 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_A2A_CAP:onbeforeEngage( AIGroup, From, Event, To )
function AI_A2A_CAP:onbeforeEngage( AICap, From, Event, To )
if self.Accomplished == true then
return false
@@ -367,43 +381,43 @@ function AI_A2A_CAP:onbeforeEngage( AIGroup, From, Event, To )
end
--- @param #AI_A2A_CAP self
-- @param Wrapper.Controllable#CONTROLLABLE AIGroup The AI Group managed by the FSM.
-- @param Wrapper.Group#GROUP AICap The AI Group 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_A2A_CAP:onafterAbort( AIGroup, From, Event, To )
AIGroup:ClearTasks()
function AI_A2A_CAP:onafterAbort( AICap, From, Event, To )
AICap:ClearTasks()
self:__Route( 0.5 )
end
--- @param #AI_A2A_CAP self
-- @param Wrapper.Controllable#CONTROLLABLE AIGroup The Controllable Object managed by the FSM.
-- @param Wrapper.Group#GROUP AICap The AICap Object 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_A2A_CAP:onafterEngage( AIGroup, From, Event, To, AttackSetUnit )
function AI_A2A_CAP:onafterEngage( AICap, From, Event, To, AttackSetUnit )
self:F( { AIGroup, From, Event, To, AttackSetUnit} )
self:F( { AICap, From, Event, To, AttackSetUnit} )
self.AttackSetUnit = AttackSetUnit or self.AttackSetUnit -- Core.Set#SET_UNIT
local FirstAttackUnit = self.AttackSetUnit:GetFirst()
local FirstAttackUnit = self.AttackSetUnit:GetFirst() -- Wrapper.Unit#UNIT
if FirstAttackUnit then
if FirstAttackUnit and FirstAttackUnit:IsAlive() then -- If there is no attacker anymore, stop the engagement.
if AIGroup:IsAlive() then
if AICap:IsAlive() then
local EngageRoute = {}
--- Calculate the target route point.
local CurrentCoord = AIGroup:GetCoordinate()
local CurrentCoord = AICap:GetCoordinate()
local ToTargetCoord = self.AttackSetUnit:GetFirst():GetCoordinate()
local ToTargetSpeed = math.random( self.EngageMinSpeed, self.EngageMaxSpeed )
local ToInterceptAngle = CurrentCoord:GetAngleDegrees( CurrentCoord:GetDirectionVec3( ToTargetCoord ) )
--- Create a route point of type air.
local ToPatrolRoutePoint = CurrentCoord:Translate( 5000, ToInterceptAngle ):RoutePointAir(
local ToPatrolRoutePoint = CurrentCoord:Translate( 5000, ToInterceptAngle ):WaypointAir(
self.PatrolAltType,
POINT_VEC3.RoutePointType.TurningPoint,
POINT_VEC3.RoutePointAction.TurningPoint,
@@ -414,40 +428,31 @@ function AI_A2A_CAP:onafterEngage( AIGroup, From, Event, To, AttackSetUnit )
self:F( { Angle = ToInterceptAngle, ToTargetSpeed = ToTargetSpeed } )
self:T2( { self.MinSpeed, self.MaxSpeed, ToTargetSpeed } )
EngageRoute[#EngageRoute+1] = ToPatrolRoutePoint
EngageRoute[#EngageRoute+1] = ToPatrolRoutePoint
AIGroup:OptionROEOpenFire()
AIGroup:OptionROTPassiveDefense()
local AttackTasks = {}
for AttackUnitID, AttackUnit in pairs( self.AttackSetUnit:GetSet() ) do
local AttackUnit = AttackUnit -- Wrapper.Unit#UNIT
self:T( { "Attacking Unit:", AttackUnit:GetName(), AttackUnit:IsAlive(), AttackUnit:IsAir() } )
if AttackUnit:IsAlive() and AttackUnit:IsAir() then
AttackTasks[#AttackTasks+1] = AIGroup:TaskAttackUnit( AttackUnit )
AttackTasks[#AttackTasks+1] = AICap:TaskAttackUnit( AttackUnit )
end
end
--- Now we're going to do something special, we're going to call a function from a waypoint action at the AIControllable...
self.Controllable:WayPointInitialize( EngageRoute )
if #AttackTasks == 0 then
self:E("No targets found -> Going back to Patrolling")
self:__Abort( 0.5 )
else
AttackTasks[#AttackTasks+1] = AIGroup:TaskFunction( 1, #AttackTasks, "AI_A2A_CAP.AttackRoute" )
AttackTasks[#AttackTasks+1] = AIGroup:TaskOrbitCircle( 4000, self.PatrolMinSpeed )
EngageRoute[1].task = AIGroup:TaskCombo( AttackTasks )
--- Do a trick, link the NewEngageRoute function of the object to the AIControllable in a temporary variable ...
AIGroup:SetState( AIGroup, "AI_A2A_CAP", self )
AICap:OptionROEOpenFire()
AICap:OptionROTEvadeFire()
AttackTasks[#AttackTasks+1] = AICap:TaskFunction( "AI_A2A_CAP.AttackRoute", self )
EngageRoute[#EngageRoute].task = AICap:TaskCombo( AttackTasks )
end
--- NOW ROUTE THE GROUP!
AIGroup:WayPointExecute( 1, 0 )
AICap:Route( EngageRoute, 0.5 )
end
else
self:E("No targets found -> Going back to Patrolling")
@@ -456,22 +461,22 @@ function AI_A2A_CAP:onafterEngage( AIGroup, From, Event, To, AttackSetUnit )
end
--- @param #AI_A2A_CAP self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param Wrapper.Group#GROUP AICap The Group Object 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_A2A_CAP:onafterAccomplish( Controllable, From, Event, To )
function AI_A2A_CAP:onafterAccomplish( AICap, From, Event, To )
self.Accomplished = true
self:SetDetectionOff()
end
--- @param #AI_A2A_CAP self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param Wrapper.Group#GROUP AICap The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @param Core.Event#EVENTDATA EventData
function AI_A2A_CAP:onafterDestroy( Controllable, From, Event, To, EventData )
function AI_A2A_CAP:onafterDestroy( AICap, From, Event, To, EventData )
if EventData.IniUnit then
self.AttackUnits[EventData.IniUnit] = nil
@@ -489,3 +494,15 @@ function AI_A2A_CAP:OnEventDead( EventData )
end
end
end
--- @param Wrapper.Group#GROUP AICap
function AI_A2A_CAP.Resume( AICap )
AICap:F( { "AI_A2A_CAP.Resume:", AICap:GetName() } )
if AICap:IsAlive() then
local _AI_A2A = AICap:GetState( AICap, "AI_A2A" ) -- #AI_A2A
_AI_A2A:__Reset( 1 )
_AI_A2A:__Route( 5 )
end
end

File diff suppressed because it is too large Load Diff

View File

@@ -117,12 +117,12 @@ AI_A2A_GCI = {
--- Creates a new AI_A2A_GCI object
-- @param #AI_A2A_GCI self
-- @param Wrapper.Group#GROUP AIGroup
-- @param Wrapper.Group#GROUP AIIntercept
-- @return #AI_A2A_GCI
function AI_A2A_GCI:New( AIGroup, EngageMinSpeed, EngageMaxSpeed )
function AI_A2A_GCI:New( AIIntercept, EngageMinSpeed, EngageMaxSpeed )
-- Inherits from BASE
local self = BASE:Inherit( self, AI_A2A:New( AIGroup ) ) -- #AI_A2A_GCI
local self = BASE:Inherit( self, AI_A2A:New( AIIntercept ) ) -- #AI_A2A_GCI
self.Accomplished = false
self.Engaging = false
@@ -134,12 +134,12 @@ function AI_A2A_GCI:New( AIGroup, EngageMinSpeed, EngageMaxSpeed )
self.PatrolAltType = "RADIO"
self:AddTransition( { "Started", "Engaging", "Returning" }, "Engage", "Engaging" ) -- FSM_CONTROLLABLE Transition for type #AI_A2A_GCI.
self:AddTransition( { "Started", "Engaging", "Returning", "Airborne" }, "Engage", "Engaging" ) -- FSM_CONTROLLABLE Transition for type #AI_A2A_GCI.
--- OnBefore Transition Handler for Event Engage.
-- @function [parent=#AI_A2A_GCI] OnBeforeEngage
-- @param #AI_A2A_GCI self
-- @param Wrapper.Group#GROUP AIGroup The AIGroup Object managed by the FSM.
-- @param Wrapper.Group#GROUP AIIntercept The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
@@ -148,7 +148,7 @@ function AI_A2A_GCI:New( AIGroup, EngageMinSpeed, EngageMaxSpeed )
--- OnAfter Transition Handler for Event Engage.
-- @function [parent=#AI_A2A_GCI] OnAfterEngage
-- @param #AI_A2A_GCI self
-- @param Wrapper.Group#GROUP AIGroup The AIGroup Object managed by the FSM.
-- @param Wrapper.Group#GROUP AIIntercept The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
@@ -165,7 +165,7 @@ function AI_A2A_GCI:New( AIGroup, EngageMinSpeed, EngageMaxSpeed )
--- OnLeave Transition Handler for State Engaging.
-- @function [parent=#AI_A2A_GCI] OnLeaveEngaging
-- @param #AI_A2A_GCI self
-- @param Wrapper.Group#GROUP AIGroup The AIGroup Object managed by the FSM.
-- @param Wrapper.Group#GROUP AIIntercept The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
@@ -174,7 +174,7 @@ function AI_A2A_GCI:New( AIGroup, EngageMinSpeed, EngageMaxSpeed )
--- OnEnter Transition Handler for State Engaging.
-- @function [parent=#AI_A2A_GCI] OnEnterEngaging
-- @param #AI_A2A_GCI self
-- @param Wrapper.Group#GROUP AIGroup The AIGroup Object managed by the FSM.
-- @param Wrapper.Group#GROUP AIIntercept The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
@@ -184,7 +184,7 @@ function AI_A2A_GCI:New( AIGroup, EngageMinSpeed, EngageMaxSpeed )
--- OnBefore Transition Handler for Event Fired.
-- @function [parent=#AI_A2A_GCI] OnBeforeFired
-- @param #AI_A2A_GCI self
-- @param Wrapper.Group#GROUP AIGroup The AIGroup Object managed by the FSM.
-- @param Wrapper.Group#GROUP AIIntercept The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
@@ -193,7 +193,7 @@ function AI_A2A_GCI:New( AIGroup, EngageMinSpeed, EngageMaxSpeed )
--- OnAfter Transition Handler for Event Fired.
-- @function [parent=#AI_A2A_GCI] OnAfterFired
-- @param #AI_A2A_GCI self
-- @param Wrapper.Group#GROUP AIGroup The AIGroup Object managed by the FSM.
-- @param Wrapper.Group#GROUP AIIntercept The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
@@ -212,7 +212,7 @@ function AI_A2A_GCI:New( AIGroup, EngageMinSpeed, EngageMaxSpeed )
--- OnBefore Transition Handler for Event Destroy.
-- @function [parent=#AI_A2A_GCI] OnBeforeDestroy
-- @param #AI_A2A_GCI self
-- @param Wrapper.Group#GROUP AIGroup The AIGroup Object managed by the FSM.
-- @param Wrapper.Group#GROUP AIIntercept The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
@@ -221,7 +221,7 @@ function AI_A2A_GCI:New( AIGroup, EngageMinSpeed, EngageMaxSpeed )
--- OnAfter Transition Handler for Event Destroy.
-- @function [parent=#AI_A2A_GCI] OnAfterDestroy
-- @param #AI_A2A_GCI self
-- @param Wrapper.Group#GROUP AIGroup The AIGroup Object managed by the FSM.
-- @param Wrapper.Group#GROUP AIIntercept The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
@@ -241,7 +241,7 @@ function AI_A2A_GCI:New( AIGroup, EngageMinSpeed, EngageMaxSpeed )
--- OnBefore Transition Handler for Event Abort.
-- @function [parent=#AI_A2A_GCI] OnBeforeAbort
-- @param #AI_A2A_GCI self
-- @param Wrapper.Group#GROUP AIGroup The AIGroup Object managed by the FSM.
-- @param Wrapper.Group#GROUP AIIntercept The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
@@ -250,7 +250,7 @@ function AI_A2A_GCI:New( AIGroup, EngageMinSpeed, EngageMaxSpeed )
--- OnAfter Transition Handler for Event Abort.
-- @function [parent=#AI_A2A_GCI] OnAfterAbort
-- @param #AI_A2A_GCI self
-- @param Wrapper.Group#GROUP AIGroup The AIGroup Object managed by the FSM.
-- @param Wrapper.Group#GROUP AIIntercept The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
@@ -269,7 +269,7 @@ function AI_A2A_GCI:New( AIGroup, EngageMinSpeed, EngageMaxSpeed )
--- OnBefore Transition Handler for Event Accomplish.
-- @function [parent=#AI_A2A_GCI] OnBeforeAccomplish
-- @param #AI_A2A_GCI self
-- @param Wrapper.Group#GROUP AIGroup The AIGroup Object managed by the FSM.
-- @param Wrapper.Group#GROUP AIIntercept The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
@@ -278,7 +278,7 @@ function AI_A2A_GCI:New( AIGroup, EngageMinSpeed, EngageMaxSpeed )
--- OnAfter Transition Handler for Event Accomplish.
-- @function [parent=#AI_A2A_GCI] OnAfterAccomplish
-- @param #AI_A2A_GCI self
-- @param Wrapper.Group#GROUP AIGroup The AIGroup Object managed by the FSM.
-- @param Wrapper.Group#GROUP AIIntercept The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
@@ -295,14 +295,27 @@ function AI_A2A_GCI:New( AIGroup, EngageMinSpeed, EngageMaxSpeed )
return self
end
--- onafter State Transition for Event Patrol.
-- @param #AI_A2A_GCI self
-- @param Wrapper.Group#GROUP AIGroup The AI Group managed by the FSM.
-- @param Wrapper.Group#GROUP AIIntercept The AI Group 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_A2A_GCI:onafterEngage( AIGroup, From, Event, To )
function AI_A2A_GCI:onafterStart( AIIntercept, From, Event, To )
AIIntercept:HandleEvent( EVENTS.Takeoff, nil, self )
end
--- onafter State Transition for Event Patrol.
-- @param #AI_A2A_GCI self
-- @param Wrapper.Group#GROUP AIIntercept The AI Group 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_A2A_GCI:onafterEngage( AIIntercept, From, Event, To )
self:HandleEvent( EVENTS.Dead )
@@ -311,19 +324,24 @@ end
-- todo: need to fix this global function
--- @param Wrapper.Group#GROUP AIControllable
function AI_A2A_GCI.InterceptRoute( AIControllable )
function AI_A2A_GCI.InterceptRoute( AIIntercept, Fsm )
AIControllable:T( "NewEngageRoute" )
local EngageZone = AIControllable:GetState( AIControllable, "EngageZone" ) -- AI.AI_Cap#AI_A2A_GCI
EngageZone:__Engage( 0.5 )
AIIntercept:F( { "AI_A2A_GCI.InterceptRoute:", AIIntercept:GetName() } )
if AIIntercept:IsAlive() then
Fsm:__Engage( 0.5 )
--local Task = AIIntercept:TaskOrbitCircle( 4000, 400 )
--AIIntercept:SetTask( Task )
end
end
--- @param #AI_A2A_GCI self
-- @param Wrapper.Group#GROUP AIGroup The AIGroup Object managed by the FSM.
-- @param Wrapper.Group#GROUP AIIntercept The Group Object 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_A2A_GCI:onbeforeEngage( AIGroup, From, Event, To )
function AI_A2A_GCI:onbeforeEngage( AIIntercept, From, Event, To )
if self.Accomplished == true then
return false
@@ -331,39 +349,41 @@ function AI_A2A_GCI:onbeforeEngage( AIGroup, From, Event, To )
end
--- @param #AI_A2A_GCI self
-- @param Wrapper.Group#GROUP AIGroup The AI Group managed by the FSM.
-- @param Wrapper.Group#GROUP AIIntercept The AI Group 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_A2A_GCI:onafterAbort( AIGroup, From, Event, To )
AIGroup:ClearTasks()
function AI_A2A_GCI:onafterAbort( AIIntercept, From, Event, To )
AIIntercept:ClearTasks()
self:Return()
self:__RTB( 0.5 )
end
--- @param #AI_A2A_GCI self
-- @param Wrapper.Group#GROUP AIGroup The AIGroup Object managed by the FSM.
-- @param Wrapper.Group#GROUP AIIntercept 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_A2A_GCI:onafterEngage( AIGroup, From, Event, To, AttackSetUnit )
function AI_A2A_GCI:onafterEngage( AIIntercept, From, Event, To, AttackSetUnit )
self:F( { AIGroup, From, Event, To, AttackSetUnit} )
self:F( { AIIntercept, From, Event, To, AttackSetUnit} )
self.AttackSetUnit = AttackSetUnit or self.AttackSetUnit -- Core.Set#SET_UNIT
local FirstAttackUnit = self.AttackSetUnit:GetFirst()
if FirstAttackUnit then
if FirstAttackUnit and FirstAttackUnit:IsAlive() then
if AIGroup:IsAlive() then
if AIIntercept:IsAlive() then
local EngageRoute = {}
local CurrentCoord = AIIntercept:GetCoordinate()
--- Calculate the target route point.
local CurrentCoord = AIGroup:GetCoordinate()
local CurrentCoord = AIIntercept:GetCoordinate()
local ToTargetCoord = self.AttackSetUnit:GetFirst():GetCoordinate()
self:SetTargetDistance( ToTargetCoord ) -- For RTB status check
@@ -372,7 +392,7 @@ function AI_A2A_GCI:onafterEngage( AIGroup, From, Event, To, AttackSetUnit )
local ToInterceptAngle = CurrentCoord:GetAngleDegrees( CurrentCoord:GetDirectionVec3( ToTargetCoord ) )
--- Create a route point of type air.
local ToPatrolRoutePoint = CurrentCoord:Translate( 5000, ToInterceptAngle ):RoutePointAir(
local ToPatrolRoutePoint = CurrentCoord:Translate( 15000, ToInterceptAngle ):WaypointAir(
self.PatrolAltType,
POINT_VEC3.RoutePointType.TurningPoint,
POINT_VEC3.RoutePointAction.TurningPoint,
@@ -381,42 +401,34 @@ function AI_A2A_GCI:onafterEngage( AIGroup, From, Event, To, AttackSetUnit )
)
self:F( { Angle = ToInterceptAngle, ToTargetSpeed = ToTargetSpeed } )
self:T2( { self.EngageMinSpeed, self.EngageMaxSpeed, ToTargetSpeed } )
self:F( { self.EngageMinSpeed, self.EngageMaxSpeed, ToTargetSpeed } )
EngageRoute[#EngageRoute+1] = ToPatrolRoutePoint
EngageRoute[#EngageRoute+1] = ToPatrolRoutePoint
AIGroup:OptionROEOpenFire()
AIGroup:OptionROTPassiveDefense()
local AttackTasks = {}
for AttackUnitID, AttackUnit in pairs( self.AttackSetUnit:GetSet() ) do
local AttackUnit = AttackUnit -- Wrapper.Unit#UNIT
self:T( { "Intercepting Unit:", AttackUnit:GetName(), AttackUnit:IsAlive(), AttackUnit:IsAir() } )
if AttackUnit:IsAlive() and AttackUnit:IsAir() then
AttackTasks[#AttackTasks+1] = AIGroup:TaskAttackUnit( AttackUnit )
self:T( { "Intercepting Unit:", AttackUnit:GetName(), AttackUnit:IsAlive(), AttackUnit:IsAir() } )
AttackTasks[#AttackTasks+1] = AIIntercept:TaskAttackUnit( AttackUnit )
end
end
--- Now we're going to do something special, we're going to call a function from a waypoint action at the AIControllable...
AIGroup:WayPointInitialize( EngageRoute )
if #AttackTasks == 0 then
self:E("No targets found -> Going RTB")
self:Return()
self:__RTB( 0.5 )
else
AttackTasks[#AttackTasks+1] = AIGroup:TaskFunction( 1, #AttackTasks, "AI_A2A_GCI.InterceptRoute" )
AttackTasks[#AttackTasks+1] = AIGroup:TaskOrbitCircle( 4000, self.EngageMinSpeed )
EngageRoute[1].task = AIGroup:TaskCombo( AttackTasks )
--- Do a trick, link the NewEngageRoute function of the object to the AIControllable in a temporary variable ...
AIGroup:SetState( AIGroup, "EngageZone", self )
AIIntercept:OptionROEOpenFire()
AIIntercept:OptionROTEvadeFire()
AttackTasks[#AttackTasks+1] = AIIntercept:TaskFunction( "AI_A2A_GCI.InterceptRoute", self )
EngageRoute[#EngageRoute].task = AIIntercept:TaskCombo( AttackTasks )
end
--- NOW ROUTE THE GROUP!
AIGroup:WayPointExecute( 1, 0 )
AIIntercept:Route( EngageRoute, 0.5 )
end
else
@@ -427,22 +439,22 @@ function AI_A2A_GCI:onafterEngage( AIGroup, From, Event, To, AttackSetUnit )
end
--- @param #AI_A2A_GCI self
-- @param Wrapper.Group#GROUP AIGroup The AIGroup Object managed by the FSM.
-- @param Wrapper.Group#GROUP AIIntercept The Group Object 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_A2A_GCI:onafterAccomplish( AIGroup, From, Event, To )
function AI_A2A_GCI:onafterAccomplish( AIIntercept, From, Event, To )
self.Accomplished = true
self:SetDetectionOff()
end
--- @param #AI_A2A_GCI self
-- @param Wrapper.Group#GROUP AIGroup The AIGroup Object managed by the FSM.
-- @param Wrapper.Group#GROUP AIIntercept The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
-- @param Core.Event#EVENTDATA EventData
function AI_A2A_GCI:onafterDestroy( AIGroup, From, Event, To, EventData )
function AI_A2A_GCI:onafterDestroy( AIIntercept, From, Event, To, EventData )
if EventData.IniUnit then
self.AttackUnits[EventData.IniUnit] = nil

View File

@@ -4,7 +4,7 @@
--
-- ===
--
-- AI PATROL classes makes AI Controllables execute an Patrol.
-- AI PATROL classes makes AI Groups execute an Patrol.
--
-- There are the following types of PATROL classes defined:
--
@@ -44,7 +44,7 @@
--- # AI_A2A_PATROL class, extends @{Fsm#FSM_CONTROLLABLE}
--
-- The AI_A2A_PATROL class implements the core functions to patrol a @{Zone} by an AI @{Controllable} or @{Group}.
-- The AI_A2A_PATROL class implements the core functions to patrol a @{Zone} by an AI @{Group} or @{Group}.
--
-- ![Process](..\Presentations\AI_PATROL\Dia3.JPG)
--
@@ -120,7 +120,7 @@
-- * @{#AI_A2A_PATROL.SetDetectionOn}(): Set the detection on. The AI will detect for targets.
-- * @{#AI_A2A_PATROL.SetDetectionOff}(): Set the detection off, the AI will not detect for targets. The existing target list will NOT be erased.
--
-- The detection frequency can be set with @{#AI_A2A_PATROL.SetDetectionInterval}( seconds ), where the amount of seconds specify how much seconds will be waited before the next detection.
-- The detection frequency can be set with @{#AI_A2A_PATROL.SetRefreshTimeInterval}( seconds ), where the amount of seconds specify how much seconds will be waited before the next detection.
-- Use the method @{#AI_A2A_PATROL.GetDetectedUnits}() to obtain a list of the @{Unit}s detected by the AI.
--
-- The detection can be filtered to potential targets in a specific zone.
@@ -139,7 +139,7 @@
--
-- ## 7. Manage "damage" behaviour of the AI in the AI_A2A_PATROL
--
-- When the AI is damaged, it is required that a new AIControllable is started. However, damage cannon be foreseen early on.
-- When the AI is damaged, it is required that a new Patrol is started. However, damage cannon be foreseen early on.
-- Therefore, when the damage treshold is reached, the AI will return immediately to the home base (RTB).
-- Use the method @{#AI_A2A_PATROL.ManageDamage}() to have this proces in place.
--
@@ -152,23 +152,23 @@ AI_A2A_PATROL = {
--- Creates a new AI_A2A_PATROL object
-- @param #AI_A2A_PATROL self
-- @param Wrapper.Group#GROUP AIGroup
-- @param Wrapper.Group#GROUP AIPatrol
-- @param Core.Zone#ZONE_BASE PatrolZone The @{Zone} where the patrol needs to be executed.
-- @param Dcs.DCSTypes#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol.
-- @param Dcs.DCSTypes#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol.
-- @param Dcs.DCSTypes#Speed PatrolMinSpeed The minimum speed of the @{Controllable} in km/h.
-- @param Dcs.DCSTypes#Speed PatrolMaxSpeed The maximum speed of the @{Controllable} in km/h.
-- @param Dcs.DCSTypes#Speed PatrolMinSpeed The minimum speed of the @{Group} in km/h.
-- @param Dcs.DCSTypes#Speed PatrolMaxSpeed The maximum speed of the @{Group} in km/h.
-- @param Dcs.DCSTypes#AltitudeType PatrolAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to RADIO
-- @return #AI_A2A_PATROL self
-- @usage
-- -- Define a new AI_A2A_PATROL Object. This PatrolArea will patrol an AIControllable within PatrolZone between 3000 and 6000 meters, with a variying speed between 600 and 900 km/h.
-- -- Define a new AI_A2A_PATROL Object. This PatrolArea will patrol a Group within PatrolZone between 3000 and 6000 meters, with a variying speed between 600 and 900 km/h.
-- PatrolZone = ZONE:New( 'PatrolZone' )
-- PatrolSpawn = SPAWN:New( 'Patrol Group' )
-- PatrolArea = AI_A2A_PATROL:New( PatrolZone, 3000, 6000, 600, 900 )
function AI_A2A_PATROL:New( AIGroup, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType )
function AI_A2A_PATROL:New( AIPatrol, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType )
-- Inherits from BASE
local self = BASE:Inherit( self, AI_A2A:New( AIGroup ) ) -- #AI_A2A_PATROL
local self = BASE:Inherit( self, AI_A2A:New( AIPatrol ) ) -- #AI_A2A_PATROL
self.PatrolZone = PatrolZone
self.PatrolFloorAltitude = PatrolFloorAltitude
@@ -179,12 +179,12 @@ function AI_A2A_PATROL:New( AIGroup, PatrolZone, PatrolFloorAltitude, PatrolCeil
-- defafult PatrolAltType to "RADIO" if not specified
self.PatrolAltType = PatrolAltType or "RADIO"
self:AddTransition( "Started", "Patrol", "Patrolling" )
self:AddTransition( { "Started", "Airborne", "Refuelling" }, "Patrol", "Patrolling" )
--- OnBefore Transition Handler for Event Patrol.
-- @function [parent=#AI_A2A_PATROL] OnBeforePatrol
-- @param #AI_A2A_PATROL self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param Wrapper.Group#GROUP AIPatrol The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
@@ -193,7 +193,7 @@ function AI_A2A_PATROL:New( AIGroup, PatrolZone, PatrolFloorAltitude, PatrolCeil
--- OnAfter Transition Handler for Event Patrol.
-- @function [parent=#AI_A2A_PATROL] OnAfterPatrol
-- @param #AI_A2A_PATROL self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param Wrapper.Group#GROUP AIPatrol The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
@@ -210,7 +210,7 @@ function AI_A2A_PATROL:New( AIGroup, PatrolZone, PatrolFloorAltitude, PatrolCeil
--- OnLeave Transition Handler for State Patrolling.
-- @function [parent=#AI_A2A_PATROL] OnLeavePatrolling
-- @param #AI_A2A_PATROL self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param Wrapper.Group#GROUP AIPatrol The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
@@ -219,7 +219,7 @@ function AI_A2A_PATROL:New( AIGroup, PatrolZone, PatrolFloorAltitude, PatrolCeil
--- OnEnter Transition Handler for State Patrolling.
-- @function [parent=#AI_A2A_PATROL] OnEnterPatrolling
-- @param #AI_A2A_PATROL self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param Wrapper.Group#GROUP AIPatrol The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
@@ -229,7 +229,7 @@ function AI_A2A_PATROL:New( AIGroup, PatrolZone, PatrolFloorAltitude, PatrolCeil
--- OnBefore Transition Handler for Event Route.
-- @function [parent=#AI_A2A_PATROL] OnBeforeRoute
-- @param #AI_A2A_PATROL self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param Wrapper.Group#GROUP AIPatrol The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
@@ -238,7 +238,7 @@ function AI_A2A_PATROL:New( AIGroup, PatrolZone, PatrolFloorAltitude, PatrolCeil
--- OnAfter Transition Handler for Event Route.
-- @function [parent=#AI_A2A_PATROL] OnAfterRoute
-- @param #AI_A2A_PATROL self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param Wrapper.Group#GROUP AIPatrol The Group Object managed by the FSM.
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
@@ -252,6 +252,8 @@ function AI_A2A_PATROL:New( AIGroup, PatrolZone, PatrolFloorAltitude, PatrolCeil
-- @param #AI_A2A_PATROL self
-- @param #number Delay The delay in seconds.
self:AddTransition( "*", "Reset", "Patrolling" ) -- FSM_CONTROLLABLE Transition for type #AI_A2A_PATROL.
return self
@@ -262,8 +264,8 @@ end
--- Sets (modifies) the minimum and maximum speed of the patrol.
-- @param #AI_A2A_PATROL self
-- @param Dcs.DCSTypes#Speed PatrolMinSpeed The minimum speed of the @{Controllable} in km/h.
-- @param Dcs.DCSTypes#Speed PatrolMaxSpeed The maximum speed of the @{Controllable} in km/h.
-- @param Dcs.DCSTypes#Speed PatrolMinSpeed The minimum speed of the @{Group} in km/h.
-- @param Dcs.DCSTypes#Speed PatrolMaxSpeed The maximum speed of the @{Group} in km/h.
-- @return #AI_A2A_PATROL self
function AI_A2A_PATROL:SetSpeed( PatrolMinSpeed, PatrolMaxSpeed )
self:F2( { PatrolMinSpeed, PatrolMaxSpeed } )
@@ -290,18 +292,18 @@ end
--- Defines a new patrol route using the @{Process_PatrolZone} parameters and settings.
-- @param #AI_A2A_PATROL self
-- @return #AI_A2A_PATROL self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param Wrapper.Group#GROUP AIPatrol The Group Object 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_A2A_PATROL:onafterPatrol( Controllable, From, Event, To )
function AI_A2A_PATROL:onafterPatrol( AIPatrol, From, Event, To )
self:F2()
self:ClearTargetDistance()
self:__Route( 1 )
self.Controllable:OnReSpawn(
AIPatrol:OnReSpawn(
function( PatrolGroup )
self:E( "ReSpawn" )
self:__Reset( 1 )
@@ -312,23 +314,27 @@ end
--- @param Wrapper.Group#GROUP AIGroup
-- This statis method is called from the route path within the last task at the last waaypoint of the Controllable.
-- Note that this method is required, as triggers the next route when patrolling for the Controllable.
function AI_A2A_PATROL.PatrolRoute( AIGroup )
--- @param Wrapper.Group#GROUP AIPatrol
-- This statis method is called from the route path within the last task at the last waaypoint of the AIPatrol.
-- Note that this method is required, as triggers the next route when patrolling for the AIPatrol.
function AI_A2A_PATROL.PatrolRoute( AIPatrol, Fsm )
local _AI_A2A_Patrol = AIGroup:GetState( AIGroup, "AI_A2A_PATROL" ) -- #AI_A2A_PATROL
_AI_A2A_Patrol:Route()
AIPatrol:F( { "AI_A2A_PATROL.PatrolRoute:", AIPatrol:GetName() } )
if AIPatrol:IsAlive() then
Fsm:Route()
end
end
--- Defines a new patrol route using the @{Process_PatrolZone} parameters and settings.
-- @param #AI_A2A_PATROL self
-- @param Wrapper.Group#GROUP AIGroup The AIGroup managed by the FSM.
-- @param Wrapper.Group#GROUP AIPatrol The Group 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_A2A_PATROL:onafterRoute( AIGroup, From, Event, To )
function AI_A2A_PATROL:onafterRoute( AIPatrol, From, Event, To )
self:F2()
@@ -338,22 +344,22 @@ function AI_A2A_PATROL:onafterRoute( AIGroup, From, Event, To )
end
if AIGroup:IsAlive() then
if AIPatrol:IsAlive() then
local PatrolRoute = {}
--- Calculate the target route point.
local CurrentCoord = AIGroup:GetCoordinate()
local CurrentCoord = AIPatrol:GetCoordinate()
local ToTargetCoord = self.PatrolZone:GetRandomPointVec2()
ToTargetCoord:SetAlt(math.random( self.PatrolFloorAltitude,self.PatrolCeilingAltitude ) )
ToTargetCoord:SetAlt( math.random( self.PatrolFloorAltitude, self.PatrolCeilingAltitude ) )
self:SetTargetDistance( ToTargetCoord ) -- For RTB status check
local ToTargetSpeed = math.random( self.PatrolMinSpeed, self.PatrolMaxSpeed )
--- Create a route point of type air.
local ToPatrolRoutePoint = ToTargetCoord:RoutePointAir(
local ToPatrolRoutePoint = ToTargetCoord:WaypointAir(
self.PatrolAltType,
POINT_VEC3.RoutePointType.TurningPoint,
POINT_VEC3.RoutePointAction.TurningPoint,
@@ -361,22 +367,29 @@ function AI_A2A_PATROL:onafterRoute( AIGroup, From, Event, To )
true
)
PatrolRoute[#PatrolRoute+1] = ToPatrolRoutePoint
PatrolRoute[#PatrolRoute+1] = ToPatrolRoutePoint
--- Now we're going to do something special, we're going to call a function from a waypoint action at the AIControllable...
AIGroup:WayPointInitialize( PatrolRoute )
local Tasks = {}
Tasks[#Tasks+1] = AIGroup:TaskFunction( 1, 1, "AI_A2A_PATROL.PatrolRoute" )
Tasks[#Tasks+1] = AIPatrol:TaskFunction( "AI_A2A_PATROL.PatrolRoute", self )
PatrolRoute[#PatrolRoute].task = AIPatrol:TaskCombo( Tasks )
PatrolRoute[1].task = AIGroup:TaskCombo( Tasks )
--- Do a trick, link the NewPatrolRoute function of the PATROLGROUP object to the AIControllable in a temporary variable ...
AIGroup:SetState( AIGroup, "AI_A2A_PATROL", self )
AIPatrol:OptionROEReturnFire()
AIPatrol:OptionROTEvadeFire()
--- NOW ROUTE THE GROUP!
AIGroup:WayPointExecute( 1, 2 )
AIPatrol:Route( PatrolRoute, 0.5 )
end
end
--- @param Wrapper.Group#GROUP AIPatrol
function AI_A2A_PATROL.Resume( AIPatrol )
AIPatrol:F( { "AI_A2A_PATROL.Resume:", AIPatrol:GetName() } )
if AIPatrol:IsAlive() then
local _AI_A2A = AIPatrol:GetState( AIPatrol, "AI_A2A" ) -- #AI_A2A
_AI_A2A:__Reset( 1 )
_AI_A2A:__Route( 5 )
end
end

View File

@@ -531,7 +531,7 @@ function AI_BAI_ZONE:onafterEngage( Controllable, From, Event, To,
local CurrentAltitude = self.Controllable:GetUnit(1):GetAltitude()
local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y )
local ToEngageZoneSpeed = self.PatrolMaxSpeed
local CurrentRoutePoint = CurrentPointVec3:RoutePointAir(
local CurrentRoutePoint = CurrentPointVec3:WaypointAir(
self.PatrolAltType,
POINT_VEC3.RoutePointType.TurningPoint,
POINT_VEC3.RoutePointAction.TurningPoint,
@@ -588,7 +588,7 @@ function AI_BAI_ZONE:onafterEngage( Controllable, From, Event, To,
local ToTargetPointVec3 = POINT_VEC3:New( ToTargetVec2.x, self.EngageAltitude, ToTargetVec2.y )
--- Create a route point of type air.
local ToTargetRoutePoint = ToTargetPointVec3:RoutePointAir(
local ToTargetRoutePoint = ToTargetPointVec3:WaypointAir(
self.PatrolAltType,
POINT_VEC3.RoutePointType.TurningPoint,
POINT_VEC3.RoutePointAction.TurningPoint,
@@ -612,7 +612,7 @@ function AI_BAI_ZONE:onafterEngage( Controllable, From, Event, To,
--- NOW ROUTE THE GROUP!
Controllable:WayPointExecute( 1 )
self:SetDetectionInterval( 2 )
self:SetRefreshTimeInterval( 2 )
self:SetDetectionActivated()
self:__Target( -2 ) -- Start Targetting
end

View File

@@ -33,7 +33,7 @@
--- @type AI_BALANCER
-- @field Core.Set#SET_CLIENT SetClient
-- @field Functional.Spawn#SPAWN SpawnAI
-- @field Core.Spawn#SPAWN SpawnAI
-- @field Wrapper.Group#GROUP Test
-- @extends Core.Fsm#FSM_SET
@@ -106,7 +106,7 @@ AI_BALANCER = {
--- Creates a new AI_BALANCER object
-- @param #AI_BALANCER self
-- @param Core.Set#SET_CLIENT SetClient A SET\_CLIENT object that will contain the CLIENT objects to be monitored if they are alive or not (joined by a player).
-- @param Functional.Spawn#SPAWN SpawnAI The default Spawn object to spawn new AI Groups when needed.
-- @param Core.Spawn#SPAWN SpawnAI The default Spawn object to spawn new AI Groups when needed.
-- @return #AI_BALANCER
function AI_BALANCER:New( SetClient, SpawnAI )
@@ -151,22 +151,22 @@ end
--- Returns the AI to the nearest friendly @{Airbase#AIRBASE}.
-- @param #AI_BALANCER self
-- @param Dcs.DCSTypes#Distance ReturnTresholdRange If there is an enemy @{Client#CLIENT} within the ReturnTresholdRange given in meters, the AI will not return to the nearest @{Airbase#AIRBASE}.
-- @param Dcs.DCSTypes#Distance ReturnThresholdRange If there is an enemy @{Client#CLIENT} within the ReturnThresholdRange given in meters, the AI will not return to the nearest @{Airbase#AIRBASE}.
-- @param Core.Set#SET_AIRBASE ReturnAirbaseSet The SET of @{Set#SET_AIRBASE}s to evaluate where to return to.
function AI_BALANCER:ReturnToNearestAirbases( ReturnTresholdRange, ReturnAirbaseSet )
function AI_BALANCER:ReturnToNearestAirbases( ReturnThresholdRange, ReturnAirbaseSet )
self.ToNearestAirbase = true
self.ReturnTresholdRange = ReturnTresholdRange
self.ReturnThresholdRange = ReturnThresholdRange
self.ReturnAirbaseSet = ReturnAirbaseSet
end
--- Returns the AI to the home @{Airbase#AIRBASE}.
-- @param #AI_BALANCER self
-- @param Dcs.DCSTypes#Distance ReturnTresholdRange If there is an enemy @{Client#CLIENT} within the ReturnTresholdRange given in meters, the AI will not return to the nearest @{Airbase#AIRBASE}.
function AI_BALANCER:ReturnToHomeAirbase( ReturnTresholdRange )
-- @param Dcs.DCSTypes#Distance ReturnThresholdRange If there is an enemy @{Client#CLIENT} within the ReturnThresholdRange given in meters, the AI will not return to the nearest @{Airbase#AIRBASE}.
function AI_BALANCER:ReturnToHomeAirbase( ReturnThresholdRange )
self.ToHomeAirbase = true
self.ReturnTresholdRange = ReturnTresholdRange
self.ReturnThresholdRange = ReturnThresholdRange
end
--- @param #AI_BALANCER self
@@ -246,12 +246,12 @@ function AI_BALANCER:onenterMonitoring( SetGroup )
if self.ToNearestAirbase == false and self.ToHomeAirbase == false then
self:Destroy( Client.UnitName, AIGroup )
else
-- We test if there is no other CLIENT within the self.ReturnTresholdRange of the first unit of the AI group.
-- We test if there is no other CLIENT within the self.ReturnThresholdRange of the first unit of the AI group.
-- If there is a CLIENT, the AI stays engaged and will not return.
-- If there is no CLIENT within the self.ReturnTresholdRange, then the unit will return to the Airbase return method selected.
-- If there is no CLIENT within the self.ReturnThresholdRange, then the unit will return to the Airbase return method selected.
local PlayerInRange = { Value = false }
local RangeZone = ZONE_RADIUS:New( 'RangeZone', AIGroup:GetVec2(), self.ReturnTresholdRange )
local RangeZone = ZONE_RADIUS:New( 'RangeZone', AIGroup:GetVec2(), self.ReturnThresholdRange )
self:T2( RangeZone )

View File

@@ -358,16 +358,20 @@ function AI_CAP_ZONE:onafterStart( Controllable, From, Event, To )
end
-- todo: need to fix this global function
--- @param Wrapper.Controllable#CONTROLLABLE AIControllable
function _NewEngageCapRoute( AIControllable )
--- @param AI.AI_CAP#AI_CAP_ZONE
-- @param Wrapper.Group#GROUP EngageGroup
function AI_CAP_ZONE.EngageRoute( EngageGroup, Fsm )
AIControllable:T( "NewEngageRoute" )
local EngageZone = AIControllable:GetState( AIControllable, "EngageZone" ) -- AI.AI_Cap#AI_CAP_ZONE
EngageZone:__Engage( 1 )
EngageGroup:F( { "AI_CAP_ZONE.EngageRoute:", EngageGroup:GetName() } )
if EngageGroup:IsAlive() then
Fsm:__Engage( 1 )
end
end
--- @param #AI_CAP_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string.
@@ -402,7 +406,7 @@ function AI_CAP_ZONE:onafterDetected( Controllable, From, Event, To )
end
if Engage == true then
self:E( 'Detected -> Engaging' )
self:F( 'Detected -> Engaging' )
self:__Engage( 1 )
end
end
@@ -440,7 +444,7 @@ function AI_CAP_ZONE:onafterEngage( Controllable, From, Event, To )
local CurrentAltitude = self.Controllable:GetUnit(1):GetAltitude()
local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y )
local ToEngageZoneSpeed = self.PatrolMaxSpeed
local CurrentRoutePoint = CurrentPointVec3:RoutePointAir(
local CurrentRoutePoint = CurrentPointVec3:WaypointAir(
self.PatrolAltType,
POINT_VEC3.RoutePointType.TurningPoint,
POINT_VEC3.RoutePointAction.TurningPoint,
@@ -464,7 +468,7 @@ function AI_CAP_ZONE:onafterEngage( Controllable, From, Event, To )
local ToTargetPointVec3 = POINT_VEC3:New( ToTargetVec2.x, ToTargetAltitude, ToTargetVec2.y )
--- Create a route point of type air.
local ToPatrolRoutePoint = ToTargetPointVec3:RoutePointAir(
local ToPatrolRoutePoint = ToTargetPointVec3:WaypointAir(
self.PatrolAltType,
POINT_VEC3.RoutePointType.TurningPoint,
POINT_VEC3.RoutePointAction.TurningPoint,
@@ -475,7 +479,7 @@ function AI_CAP_ZONE:onafterEngage( Controllable, From, Event, To )
EngageRoute[#EngageRoute+1] = ToPatrolRoutePoint
Controllable:OptionROEOpenFire()
Controllable:OptionROTPassiveDefense()
Controllable:OptionROTEvadeFire()
local AttackTasks = {}
@@ -485,13 +489,13 @@ function AI_CAP_ZONE:onafterEngage( Controllable, From, Event, To )
if DetectedUnit:IsAlive() and DetectedUnit:IsAir() then
if self.EngageZone then
if DetectedUnit:IsInZone( self.EngageZone ) then
self:E( {"Within Zone and Engaging ", DetectedUnit } )
self:F( {"Within Zone and Engaging ", DetectedUnit } )
AttackTasks[#AttackTasks+1] = Controllable:TaskAttackUnit( DetectedUnit )
end
else
if self.EngageRange then
if DetectedUnit:GetPointVec3():Get2DDistance(Controllable:GetPointVec3() ) <= self.EngageRange then
self:E( {"Within Range and Engaging", DetectedUnit } )
self:F( {"Within Range and Engaging", DetectedUnit } )
AttackTasks[#AttackTasks+1] = Controllable:TaskAttackUnit( DetectedUnit )
end
else
@@ -503,28 +507,20 @@ function AI_CAP_ZONE:onafterEngage( Controllable, From, Event, To )
end
end
--- Now we're going to do something special, we're going to call a function from a waypoint action at the AIControllable...
self.Controllable:WayPointInitialize( EngageRoute )
if #AttackTasks == 0 then
self:E("No targets found -> Going back to Patrolling")
self:F("No targets found -> Going back to Patrolling")
self:__Abort( 1 )
self:__Route( 1 )
self:SetDetectionActivated()
else
AttackTasks[#AttackTasks+1] = Controllable:TaskFunction( "AI_CAP_ZONE.EngageRoute", self )
EngageRoute[1].task = Controllable:TaskCombo( AttackTasks )
--- Do a trick, link the NewEngageRoute function of the object to the AIControllable in a temporary variable ...
self.Controllable:SetState( self.Controllable, "EngageZone", self )
self.Controllable:WayPointFunction( #EngageRoute, 1, "_NewEngageCapRoute" )
self:SetDetectionDeactivated()
end
--- NOW ROUTE THE GROUP!
self.Controllable:WayPointExecute( 1, 2 )
Controllable:Route( EngageRoute, 0.5 )
end
end

View File

@@ -373,12 +373,15 @@ function AI_CAS_ZONE:onafterStart( Controllable, From, Event, To )
self:SetDetectionDeactivated() -- When not engaging, set the detection off.
end
--- @param Wrapper.Controllable#CONTROLLABLE AIControllable
function _NewEngageRoute( AIControllable )
--- @param AI.AI_CAS#AI_CAS_ZONE
-- @param Wrapper.Group#GROUP EngageGroup
function AI_CAS_ZONE.EngageRoute( EngageGroup, Fsm )
AIControllable:T( "NewEngageRoute" )
local EngageZone = AIControllable:GetState( AIControllable, "EngageZone" ) -- AI.AI_Cas#AI_CAS_ZONE
EngageZone:__Engage( 1, EngageZone.EngageSpeed, EngageZone.EngageAltitude, EngageZone.EngageWeaponExpend, EngageZone.EngageAttackQty, EngageZone.EngageDirection )
EngageGroup:F( { "AI_CAS_ZONE.EngageRoute:", EngageGroup:GetName() } )
if EngageGroup:IsAlive() then
Fsm:__Engage( 1, Fsm.EngageSpeed, Fsm.EngageAltitude, Fsm.EngageWeaponExpend, Fsm.EngageAttackQty, Fsm.EngageDirection )
end
end
@@ -464,6 +467,9 @@ function AI_CAS_ZONE:onafterEngage( Controllable, From, Event, To,
if Controllable:IsAlive() then
Controllable:OptionROEOpenFire()
Controllable:OptionROTVertical()
local EngageRoute = {}
--- Calculate the current route point.
@@ -473,7 +479,7 @@ function AI_CAS_ZONE:onafterEngage( Controllable, From, Event, To,
local CurrentAltitude = self.Controllable:GetUnit(1):GetAltitude()
local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y )
local ToEngageZoneSpeed = self.PatrolMaxSpeed
local CurrentRoutePoint = CurrentPointVec3:RoutePointAir(
local CurrentRoutePoint = CurrentPointVec3:WaypointAir(
self.PatrolAltType,
POINT_VEC3.RoutePointType.TurningPoint,
POINT_VEC3.RoutePointAction.TurningPoint,
@@ -485,7 +491,7 @@ function AI_CAS_ZONE:onafterEngage( Controllable, From, Event, To,
local AttackTasks = {}
for DetectedUnitID, DetectedUnit in pairs( self.DetectedUnits ) do
for DetectedUnit, Detected in pairs( self.DetectedUnits ) do
local DetectedUnit = DetectedUnit -- Wrapper.Unit#UNIT
self:T( DetectedUnit )
if DetectedUnit:IsAlive() then
@@ -503,7 +509,8 @@ function AI_CAS_ZONE:onafterEngage( Controllable, From, Event, To,
end
end
EngageRoute[1].task = Controllable:TaskCombo( AttackTasks )
AttackTasks[#AttackTasks+1] = Controllable:TaskFunction( "AI_CAS_ZONE.EngageRoute", self )
EngageRoute[#EngageRoute].task = Controllable:TaskCombo( AttackTasks )
--- Define a random point in the @{Zone}. The AI will fly to that point within the zone.
@@ -515,7 +522,7 @@ function AI_CAS_ZONE:onafterEngage( Controllable, From, Event, To,
local ToTargetPointVec3 = POINT_VEC3:New( ToTargetVec2.x, self.EngageAltitude, ToTargetVec2.y )
--- Create a route point of type air.
local ToTargetRoutePoint = ToTargetPointVec3:RoutePointAir(
local ToTargetRoutePoint = ToTargetPointVec3:WaypointAir(
self.PatrolAltType,
POINT_VEC3.RoutePointType.TurningPoint,
POINT_VEC3.RoutePointAction.TurningPoint,
@@ -524,22 +531,10 @@ function AI_CAS_ZONE:onafterEngage( Controllable, From, Event, To,
)
EngageRoute[#EngageRoute+1] = ToTargetRoutePoint
--- Now we're going to do something special, we're going to call a function from a waypoint action at the AIControllable...
Controllable:WayPointInitialize( EngageRoute )
Controllable:Route( EngageRoute, 0.5 )
--- Do a trick, link the NewEngageRoute function of the object to the AIControllable in a temporary variable ...
Controllable:SetState( Controllable, "EngageZone", self )
Controllable:WayPointFunction( #EngageRoute, 1, "_NewEngageRoute" )
--- NOW ROUTE THE GROUP!
Controllable:WayPointExecute( 1 )
Controllable:OptionROEOpenFire()
Controllable:OptionROTVertical()
self:SetDetectionInterval( 2 )
self:SetRefreshTimeInterval( 2 )
self:SetDetectionActivated()
self:__Target( -2 ) -- Start Targetting
end

View File

@@ -958,7 +958,7 @@ function AI_FORMATION:onenterFollowing( FollowGroupSet ) --R2.1
-- @param Wrapper.Unit#UNIT ClientUnit
function( FollowGroup, Formation, ClientUnit, CT1, CV1, CT2, CV2 )
FollowGroup:OptionROTPassiveDefense()
FollowGroup:OptionROTEvadeFire()
FollowGroup:OptionROEReturnFire()
local GroupUnit = FollowGroup:GetUnit( 1 )

View File

@@ -47,7 +47,7 @@
-- @field Dcs.DCSTypes#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol.
-- @field Dcs.DCSTypes#Speed PatrolMinSpeed The minimum speed of the @{Controllable} in km/h.
-- @field Dcs.DCSTypes#Speed PatrolMaxSpeed The maximum speed of the @{Controllable} in km/h.
-- @field Functional.Spawn#SPAWN CoordTest
-- @field Core.Spawn#SPAWN CoordTest
-- @extends Core.Fsm#FSM_CONTROLLABLE
--- # AI_PATROL_ZONE class, extends @{Fsm#FSM_CONTROLLABLE}
@@ -128,7 +128,7 @@
-- * @{#AI_PATROL_ZONE.SetDetectionOn}(): Set the detection on. The AI will detect for targets.
-- * @{#AI_PATROL_ZONE.SetDetectionOff}(): Set the detection off, the AI will not detect for targets. The existing target list will NOT be erased.
--
-- The detection frequency can be set with @{#AI_PATROL_ZONE.SetDetectionInterval}( seconds ), where the amount of seconds specify how much seconds will be waited before the next detection.
-- The detection frequency can be set with @{#AI_PATROL_ZONE.SetRefreshTimeInterval}( seconds ), where the amount of seconds specify how much seconds will be waited before the next detection.
-- Use the method @{#AI_PATROL_ZONE.GetDetectedUnits}() to obtain a list of the @{Unit}s detected by the AI.
--
-- The detection can be filtered to potential targets in a specific zone.
@@ -187,7 +187,7 @@ function AI_PATROL_ZONE:New( PatrolZone, PatrolFloorAltitude, PatrolCeilingAltit
-- defafult PatrolAltType to "RADIO" if not specified
self.PatrolAltType = PatrolAltType or "RADIO"
self:SetDetectionInterval( 30 )
self:SetRefreshTimeInterval( 30 )
self.CheckStatus = true
@@ -544,7 +544,7 @@ end
-- @param #AI_PATROL_ZONE self
-- @param #number Seconds The interval in seconds.
-- @return #AI_PATROL_ZONE self
function AI_PATROL_ZONE:SetDetectionInterval( Seconds )
function AI_PATROL_ZONE:SetRefreshTimeInterval( Seconds )
self:F2()
if Seconds then
@@ -591,13 +591,13 @@ end
-- When the fuel treshold is reached, the AI will continue for a given time its patrol task in orbit, while a new AIControllable is targetted to the AI_PATROL_ZONE.
-- Once the time is finished, the old AI will return to the base.
-- @param #AI_PATROL_ZONE self
-- @param #number PatrolFuelTresholdPercentage The treshold in percentage (between 0 and 1) when the AIControllable is considered to get out of fuel.
-- @param #number PatrolFuelThresholdPercentage The treshold in percentage (between 0 and 1) when the AIControllable is considered to get out of fuel.
-- @param #number PatrolOutOfFuelOrbitTime The amount of seconds the out of fuel AIControllable will orbit before returning to the base.
-- @return #AI_PATROL_ZONE self
function AI_PATROL_ZONE:ManageFuel( PatrolFuelTresholdPercentage, PatrolOutOfFuelOrbitTime )
function AI_PATROL_ZONE:ManageFuel( PatrolFuelThresholdPercentage, PatrolOutOfFuelOrbitTime )
self.PatrolManageFuel = true
self.PatrolFuelTresholdPercentage = PatrolFuelTresholdPercentage
self.PatrolFuelThresholdPercentage = PatrolFuelThresholdPercentage
self.PatrolOutOfFuelOrbitTime = PatrolOutOfFuelOrbitTime
return self
@@ -610,12 +610,12 @@ end
-- Note that for groups, the average damage of the complete group will be calculated.
-- So, in a group of 4 airplanes, 2 lost and 2 with damage 0.2, the damage treshold will be 0.25.
-- @param #AI_PATROL_ZONE self
-- @param #number PatrolDamageTreshold The treshold in percentage (between 0 and 1) when the AI is considered to be damaged.
-- @param #number PatrolDamageThreshold The treshold in percentage (between 0 and 1) when the AI is considered to be damaged.
-- @return #AI_PATROL_ZONE self
function AI_PATROL_ZONE:ManageDamage( PatrolDamageTreshold )
function AI_PATROL_ZONE:ManageDamage( PatrolDamageThreshold )
self.PatrolManageDamage = true
self.PatrolDamageTreshold = PatrolDamageTreshold
self.PatrolDamageThreshold = PatrolDamageThreshold
return self
end
@@ -748,7 +748,7 @@ function AI_PATROL_ZONE:onafterRoute( Controllable, From, Event, To )
local CurrentAltitude = self.Controllable:GetUnit(1):GetAltitude()
local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y )
local ToPatrolZoneSpeed = self.PatrolMaxSpeed
local CurrentRoutePoint = CurrentPointVec3:RoutePointAir(
local CurrentRoutePoint = CurrentPointVec3:WaypointAir(
self.PatrolAltType,
POINT_VEC3.RoutePointType.TakeOffParking,
POINT_VEC3.RoutePointAction.FromParkingArea,
@@ -763,7 +763,7 @@ function AI_PATROL_ZONE:onafterRoute( Controllable, From, Event, To )
local CurrentAltitude = self.Controllable:GetUnit(1):GetAltitude()
local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y )
local ToPatrolZoneSpeed = self.PatrolMaxSpeed
local CurrentRoutePoint = CurrentPointVec3:RoutePointAir(
local CurrentRoutePoint = CurrentPointVec3:WaypointAir(
self.PatrolAltType,
POINT_VEC3.RoutePointType.TurningPoint,
POINT_VEC3.RoutePointAction.TurningPoint,
@@ -789,7 +789,7 @@ function AI_PATROL_ZONE:onafterRoute( Controllable, From, Event, To )
local ToTargetPointVec3 = POINT_VEC3:New( ToTargetVec2.x, ToTargetAltitude, ToTargetVec2.y )
--- Create a route point of type air.
local ToTargetRoutePoint = ToTargetPointVec3:RoutePointAir(
local ToTargetRoutePoint = ToTargetPointVec3:WaypointAir(
self.PatrolAltType,
POINT_VEC3.RoutePointType.TurningPoint,
POINT_VEC3.RoutePointAction.TurningPoint,
@@ -831,10 +831,9 @@ function AI_PATROL_ZONE:onafterStatus()
local RTB = false
local Fuel = self.Controllable:GetUnit(1):GetFuel()
if Fuel < self.PatrolFuelTresholdPercentage then
if Fuel < self.PatrolFuelThresholdPercentage then
self:E( self.Controllable:GetName() .. " is out of fuel:" .. Fuel .. ", RTB!" )
local OldAIControllable = self.Controllable
local AIControllableTemplate = self.Controllable:GetTemplate()
local OrbitTask = OldAIControllable:TaskOrbitCircle( math.random( self.PatrolFloorAltitude, self.PatrolCeilingAltitude ), self.PatrolMinSpeed )
local TimedOrbitTask = OldAIControllable:TaskControlled( OrbitTask, OldAIControllable:TaskCondition(nil,nil,nil,nil,self.PatrolOutOfFuelOrbitTime,nil ) )
@@ -846,7 +845,7 @@ function AI_PATROL_ZONE:onafterStatus()
-- TODO: Check GROUP damage function.
local Damage = self.Controllable:GetLife()
if Damage <= self.PatrolDamageTreshold then
if Damage <= self.PatrolDamageThreshold then
self:E( self.Controllable:GetName() .. " is damaged:" .. Damage .. ", RTB!" )
RTB = true
end
@@ -877,7 +876,7 @@ function AI_PATROL_ZONE:onafterRTB()
local CurrentAltitude = self.Controllable:GetUnit(1):GetAltitude()
local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y )
local ToPatrolZoneSpeed = self.PatrolMaxSpeed
local CurrentRoutePoint = CurrentPointVec3:RoutePointAir(
local CurrentRoutePoint = CurrentPointVec3:WaypointAir(
self.PatrolAltType,
POINT_VEC3.RoutePointType.TurningPoint,
POINT_VEC3.RoutePointAction.TurningPoint,

View File

@@ -91,7 +91,7 @@ do -- ACT_ACCOUNT
--- StateMachine callback function
-- @param #ACT_ACCOUNT self
-- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit
-- @param Wrapper.Unit#UNIT ProcessUnit
-- @param #string Event
-- @param #string From
-- @param #string To
@@ -107,7 +107,7 @@ do -- ACT_ACCOUNT
--- StateMachine callback function
-- @param #ACT_ACCOUNT self
-- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit
-- @param Wrapper.Unit#UNIT ProcessUnit
-- @param #string Event
-- @param #string From
-- @param #string To
@@ -125,7 +125,7 @@ do -- ACT_ACCOUNT
--- StateMachine callback function
-- @param #ACT_ACCOUNT self
-- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit
-- @param Wrapper.Unit#UNIT ProcessUnit
-- @param #string Event
-- @param #string From
-- @param #string To
@@ -155,7 +155,6 @@ do -- ACT_ACCOUNT_DEADS
-- @extends #ACT_ACCOUNT
ACT_ACCOUNT_DEADS = {
ClassName = "ACT_ACCOUNT_DEADS",
TargetSetUnit = nil,
}
@@ -163,13 +162,10 @@ do -- ACT_ACCOUNT_DEADS
-- @param #ACT_ACCOUNT_DEADS self
-- @param Set#SET_UNIT TargetSetUnit
-- @param #string TaskName
function ACT_ACCOUNT_DEADS:New( TargetSetUnit, TaskName )
function ACT_ACCOUNT_DEADS:New()
-- Inherits from BASE
local self = BASE:Inherit( self, ACT_ACCOUNT:New() ) -- #ACT_ACCOUNT_DEADS
self.TargetSetUnit = TargetSetUnit
self.TaskName = TaskName
self.DisplayInterval = 30
self.DisplayCount = 30
self.DisplayMessage = true
@@ -181,38 +177,39 @@ do -- ACT_ACCOUNT_DEADS
function ACT_ACCOUNT_DEADS:Init( FsmAccount )
self.TargetSetUnit = FsmAccount.TargetSetUnit
self.TaskName = FsmAccount.TaskName
self.Task = self:GetTask()
self.TaskName = self.Task:GetName()
end
--- Process Events
--- StateMachine callback function
-- @param #ACT_ACCOUNT_DEADS self
-- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit
-- @param Wrapper.Unit#UNIT ProcessUnit
-- @param #string Event
-- @param #string From
-- @param #string To
function ACT_ACCOUNT_DEADS:onenterReport( ProcessUnit, Task, From, Event, To )
self:E( { ProcessUnit, From, Event, To } )
self:Message( "Your group with assigned " .. self.TaskName .. " task has " .. self.TargetSetUnit:GetUnitTypesText() .. " targets left to be destroyed." )
local MessageText = "Your group with assigned " .. self.TaskName .. " task has " .. Task.TargetSetUnit:GetUnitTypesText() .. " targets left to be destroyed."
self:GetCommandCenter():MessageTypeToGroup( MessageText, ProcessUnit:GetGroup(), MESSAGE.Type.Information )
end
--- StateMachine callback function
-- @param #ACT_ACCOUNT_DEADS self
-- @param Wrapper.Client#CLIENT ProcessClient
-- @param Wrapper.Unit#UNIT ProcessUnit
-- @param Tasking.Task#TASK Task
-- @param #string From
-- @param #string Event
-- @param #string To
-- @param Core.Event#EVENTDATA EventData
function ACT_ACCOUNT_DEADS:onafterEvent( ProcessClient, Task, From, Event, To, EventData )
self:T( { ProcessClient:GetName(), Task:GetName(), From, Event, To, EventData } )
function ACT_ACCOUNT_DEADS:onafterEvent( ProcessUnit, Task, From, Event, To, EventData )
self:T( { ProcessUnit:GetName(), Task:GetName(), From, Event, To, EventData } )
if self.TargetSetUnit:FindUnit( EventData.IniUnitName ) then
local PlayerName = ProcessClient:GetPlayerName()
if Task.TargetSetUnit:FindUnit( EventData.IniUnitName ) then
local PlayerName = ProcessUnit:GetPlayerName()
local PlayerHit = self.PlayerHits and self.PlayerHits[EventData.IniUnitName]
if PlayerHit == PlayerName then
self:Player( EventData )
@@ -224,24 +221,26 @@ do -- ACT_ACCOUNT_DEADS
--- StateMachine callback function
-- @param #ACT_ACCOUNT_DEADS self
-- @param Wrapper.Client#CLIENT ProcessClient
-- @param Wrapper.Unit#UNIT ProcessUnit
-- @param Tasking.Task#TASK Task
-- @param #string From
-- @param #string Event
-- @param #string To
-- @param Core.Event#EVENTDATA EventData
function ACT_ACCOUNT_DEADS:onenterAccountForPlayer( ProcessClient, Task, From, Event, To, EventData )
self:T( { ProcessClient:GetName(), Task:GetName(), From, Event, To, EventData } )
function ACT_ACCOUNT_DEADS:onenterAccountForPlayer( ProcessUnit, Task, From, Event, To, EventData )
self:T( { ProcessUnit:GetName(), Task:GetName(), From, Event, To, EventData } )
local TaskGroup = ProcessClient:GetGroup()
local TaskGroup = ProcessUnit:GetGroup()
self.TargetSetUnit:Remove( EventData.IniUnitName )
self:Message( "You have destroyed a target. Your group assigned with task " .. self.TaskName .. " has " .. self.TargetSetUnit:Count() .. " targets ( " .. self.TargetSetUnit:GetUnitTypesText() .. " ) left to be destroyed." )
local PlayerName = ProcessClient:GetPlayerName()
Task.TargetSetUnit:Remove( EventData.IniUnitName )
local MessageText = "You have destroyed a target.\nYour group assigned with task " .. self.TaskName .. " has\n" .. Task.TargetSetUnit:Count() .. " targets ( " .. Task.TargetSetUnit:GetUnitTypesText() .. " ) left to be destroyed."
self:GetCommandCenter():MessageTypeToGroup( MessageText, ProcessUnit:GetGroup(), MESSAGE.Type.Information )
local PlayerName = ProcessUnit:GetPlayerName()
Task:AddProgress( PlayerName, "Destroyed " .. EventData.IniTypeName, timer.getTime(), 1 )
if self.TargetSetUnit:Count() > 0 then
if Task.TargetSetUnit:Count() > 0 then
self:__More( 1 )
else
self:__NoMore( 1 )
@@ -250,20 +249,22 @@ do -- ACT_ACCOUNT_DEADS
--- StateMachine callback function
-- @param #ACT_ACCOUNT_DEADS self
-- @param Wrapper.Client#CLIENT ProcessClient
-- @param Wrapper.Unit#UNIT ProcessUnit
-- @param Tasking.Task#TASK Task
-- @param #string From
-- @param #string Event
-- @param #string To
-- @param Core.Event#EVENTDATA EventData
function ACT_ACCOUNT_DEADS:onenterAccountForOther( ProcessClient, Task, From, Event, To, EventData )
self:T( { ProcessClient:GetName(), Task:GetName(), From, Event, To, EventData } )
function ACT_ACCOUNT_DEADS:onenterAccountForOther( ProcessUnit, Task, From, Event, To, EventData )
self:T( { ProcessUnit:GetName(), Task:GetName(), From, Event, To, EventData } )
local TaskGroup = ProcessClient:GetGroup()
self.TargetSetUnit:Remove( EventData.IniUnitName )
self:Message( "One of the task targets has been destroyed. Your group assigned with task " .. self.TaskName .. " has " .. self.TargetSetUnit:Count() .. " targets ( " .. self.TargetSetUnit:GetUnitTypesText() .. " ) left to be destroyed." )
local TaskGroup = ProcessUnit:GetGroup()
Task.TargetSetUnit:Remove( EventData.IniUnitName )
local MessageText = "One of the task targets has been destroyed.\nYour group assigned with task " .. self.TaskName .. " has\n" .. Task.TargetSetUnit:Count() .. " targets ( " .. Task.TargetSetUnit:GetUnitTypesText() .. " ) left to be destroyed."
self:GetCommandCenter():MessageTypeToGroup( MessageText, ProcessUnit:GetGroup(), MESSAGE.Type.Information )
if self.TargetSetUnit:Count() > 0 then
if Task.TargetSetUnit:Count() > 0 then
self:__More( 1 )
else
self:__NoMore( 1 )

View File

@@ -229,14 +229,14 @@ do -- ACT_ASSIGN_MENU_ACCEPT
--- StateMachine callback function
-- @param #ACT_ASSIGN_MENU_ACCEPT self
-- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit
-- @param Wrapper.Unit#UNIT ProcessUnit
-- @param #string Event
-- @param #string From
-- @param #string To
function ACT_ASSIGN_MENU_ACCEPT:onafterStart( ProcessUnit, From, Event, To )
self:E( { ProcessUnit, From, Event, To } )
self:Message( "Access the radio menu to accept the task. You have 30 seconds or the assignment will be cancelled." )
self:GetCommandCenter():MessageTypeToGroup( "Access the radio menu to accept the task. You have 30 seconds or the assignment will be cancelled.", ProcessUnit:GetGroup(), MESSAGE.Type.Information )
local ProcessGroup = ProcessUnit:GetGroup()
@@ -263,7 +263,7 @@ do -- ACT_ASSIGN_MENU_ACCEPT
--- StateMachine callback function
-- @param #ACT_ASSIGN_MENU_ACCEPT self
-- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit
-- @param Wrapper.Unit#UNIT ProcessUnit
-- @param #string Event
-- @param #string From
-- @param #string To
@@ -275,7 +275,7 @@ do -- ACT_ASSIGN_MENU_ACCEPT
--- StateMachine callback function
-- @param #ACT_ASSIGN_MENU_ACCEPT self
-- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit
-- @param Wrapper.Unit#UNIT ProcessUnit
-- @param #string Event
-- @param #string From
-- @param #string To

View File

@@ -154,7 +154,7 @@ do -- ACT_ROUTE
--- Get the routing text to be displayed.
-- The route mode determines the text displayed.
-- @param #ACT_ROUTE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable
-- @param Wrapper.Unit#UNIT Controllable
-- @return #string
function ACT_ROUTE:GetRouteText( Controllable )
@@ -174,6 +174,7 @@ do -- ACT_ROUTE
end
local Task = self:GetTask() -- This is to dermine that the coordinates are for a specific task mode (A2A or A2G).
local CC = self:GetTask():GetMission():GetCommandCenter()
if CC then
if CC:IsModeWWII() then
@@ -198,7 +199,7 @@ do -- ACT_ROUTE
RouteText = Coordinate:ToStringFromRP( ShortestReferencePoint, ShortestReferenceName, Controllable )
end
else
RouteText = Coordinate:ToString( Controllable )
RouteText = Coordinate:ToString( Controllable, nil, Task )
end
end
@@ -214,7 +215,7 @@ do -- ACT_ROUTE
--- StateMachine callback function
-- @param #ACT_ROUTE self
-- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit
-- @param Wrapper.Unit#UNIT ProcessUnit
-- @param #string Event
-- @param #string From
-- @param #string To
@@ -226,7 +227,7 @@ do -- ACT_ROUTE
--- Check if the controllable has arrived.
-- @param #ACT_ROUTE self
-- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit
-- @param Wrapper.Unit#UNIT ProcessUnit
-- @return #boolean
function ACT_ROUTE:onfuncHasArrived( ProcessUnit )
return false
@@ -234,7 +235,7 @@ do -- ACT_ROUTE
--- StateMachine callback function
-- @param #ACT_ROUTE self
-- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit
-- @param Wrapper.Unit#UNIT ProcessUnit
-- @param #string Event
-- @param #string From
-- @param #string To
@@ -351,7 +352,7 @@ do -- ACT_ROUTE_POINT
--- Method override to check if the controllable has arrived.
-- @param #ACT_ROUTE_POINT self
-- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit
-- @param Wrapper.Unit#UNIT ProcessUnit
-- @return #boolean
function ACT_ROUTE_POINT:onfuncHasArrived( ProcessUnit )
@@ -360,7 +361,7 @@ do -- ACT_ROUTE_POINT
if Distance <= self.Range then
local RouteText = "You have arrived."
self:Message( RouteText )
self:GetCommandCenter():MessageTypeToGroup( RouteText, ProcessUnit:GetGroup(), MESSAGE.Type.Information )
return true
end
end
@@ -372,14 +373,15 @@ do -- ACT_ROUTE_POINT
--- StateMachine callback function
-- @param #ACT_ROUTE_POINT self
-- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit
-- @param Wrapper.Unit#UNIT ProcessUnit
-- @param #string Event
-- @param #string From
-- @param #string To
function ACT_ROUTE_POINT:onafterReport( ProcessUnit, From, Event, To )
local RouteText = self:GetRouteText( ProcessUnit )
self:Message( RouteText )
self:GetCommandCenter():MessageTypeToGroup( RouteText, ProcessUnit:GetGroup(), MESSAGE.Type.Update )
end
end -- ACT_ROUTE_POINT
@@ -444,13 +446,13 @@ do -- ACT_ROUTE_ZONE
--- Method override to check if the controllable has arrived.
-- @param #ACT_ROUTE self
-- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit
-- @param Wrapper.Unit#UNIT ProcessUnit
-- @return #boolean
function ACT_ROUTE_ZONE:onfuncHasArrived( ProcessUnit )
if ProcessUnit:IsInZone( self.Zone ) then
local RouteText = "You have arrived within the zone."
self:Message( RouteText )
self:GetCommandCenter():MessageTypeToGroup( RouteText, ProcessUnit:GetGroup(), MESSAGE.Type.Information )
end
return ProcessUnit:IsInZone( self.Zone )
@@ -460,7 +462,7 @@ do -- ACT_ROUTE_ZONE
--- StateMachine callback function
-- @param #ACT_ROUTE_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit
-- @param Wrapper.Unit#UNIT ProcessUnit
-- @param #string Event
-- @param #string From
-- @param #string To
@@ -468,7 +470,7 @@ do -- ACT_ROUTE_ZONE
self:E( { ProcessUnit = ProcessUnit } )
local RouteText = self:GetRouteText( ProcessUnit )
self:Message( RouteText )
self:GetCommandCenter():MessageTypeToGroup( RouteText, ProcessUnit:GetGroup(), MESSAGE.Type.Update )
end
end -- ACT_ROUTE_ZONE

View File

@@ -4,10 +4,6 @@
--
-- ===
--
-- The @{#BASE} class is the core root class from where every other class in moose is derived.
--
-- ===
--
-- ### Author: **Sven Van de Velde (FlightControl)**
-- ### Contributions:
--
@@ -202,7 +198,15 @@ BASE = {
ClassID = 0,
Events = {},
States = {},
_ = {},
}
--- @field #BASE.__
BASE.__ = {}
--- @field #BASE._
BASE._ = {
Schedules = {} --- Contains the Schedulers Active
}
--- The Formation Class
@@ -228,47 +232,19 @@ FORMATION = {
-- @return #BASE
function BASE:New()
local self = routines.utils.deepCopy( self ) -- Create a new self instance
local MetaTable = {}
setmetatable( self, MetaTable )
self.__index = self
_ClassID = _ClassID + 1
self.ClassID = _ClassID
-- This is for "private" methods...
-- When a __ is passed to a method as "self", the __index will search for the method on the public method list too!
-- if rawget( self, "__" ) then
--setmetatable( self, { __index = self.__ } )
-- end
return self
end
function BASE:_Destructor()
--self:E("_Destructor")
--self:EventRemoveAll()
end
-- THIS IS WHY WE NEED LUA 5.2 ...
function BASE:_SetDestructor()
-- TODO: Okay, this is really technical...
-- When you set a proxy to a table to catch __gc, weak tables don't behave like weak...
-- Therefore, I am parking this logic until I've properly discussed all this with the community.
local proxy = newproxy(true)
local proxyMeta = getmetatable(proxy)
proxyMeta.__gc = function ()
env.info("In __gc for " .. self:GetClassNameAndID() )
if self._Destructor then
self:_Destructor()
end
end
-- keep the userdata from newproxy reachable until the object
-- table is about to be garbage-collected - then the __gc hook
-- will be invoked and the destructor called
rawset( self, '__proxy', proxy )
end
--- This is the worker method to inherit from a parent class.
-- @param #BASE self
-- @param Child is the Child class that inherits.
@@ -276,18 +252,40 @@ end
-- @return #BASE Child
function BASE:Inherit( Child, Parent )
local Child = routines.utils.deepCopy( Child )
--local Parent = routines.utils.deepCopy( Parent )
--local Parent = Parent
if Child ~= nil then
setmetatable( Child, Parent )
Child.__index = Child
-- This is for "private" methods...
-- When a __ is passed to a method as "self", the __index will search for the method on the public method list of the same object too!
if rawget( Child, "__" ) then
setmetatable( Child, { __index = Child.__ } )
setmetatable( Child.__, { __index = Parent } )
else
setmetatable( Child, { __index = Parent } )
end
--Child:_SetDestructor()
end
--self:T( 'Inherited from ' .. Parent.ClassName )
return Child
end
local function getParent( Child )
local Parent = nil
if Child.ClassName == 'BASE' then
Parent = nil
else
if rawget( Child, "__" ) then
Parent = getmetatable( Child.__ ).__index
else
Parent = getmetatable( Child ).__index
end
end
return Parent
end
--- This is the worker method to retrieve the Parent class.
-- Note that the Parent class must be passed to call the parent class method.
--
@@ -297,12 +295,91 @@ end
-- @param #BASE self
-- @param #BASE Child is the Child class from which the Parent class needs to be retrieved.
-- @return #BASE
function BASE:GetParent( Child )
local Parent = getmetatable( Child )
-- env.info('Inherited class of ' .. Child.ClassName .. ' is ' .. Parent.ClassName )
return Parent
function BASE:GetParent( Child, FromClass )
local Parent
-- BASE class has no parent
if Child.ClassName == 'BASE' then
Parent = nil
else
self:E({FromClass = FromClass})
self:E({Child = Child.ClassName})
if FromClass then
while( Child.ClassName ~= "BASE" and Child.ClassName ~= FromClass.ClassName ) do
Child = getParent( Child )
self:E({Child.ClassName})
end
end
if Child.ClassName == 'BASE' then
Parent = nil
else
Parent = getParent( Child )
end
end
self:E({Parent.ClassName})
return Parent
end
--- This is the worker method to check if an object is an (sub)instance of a class.
--
-- ### Examples:
--
-- * ZONE:New( 'some zone' ):IsInstanceOf( ZONE ) will return true
-- * ZONE:New( 'some zone' ):IsInstanceOf( 'ZONE' ) will return true
-- * ZONE:New( 'some zone' ):IsInstanceOf( 'zone' ) will return true
-- * ZONE:New( 'some zone' ):IsInstanceOf( 'BASE' ) will return true
--
-- * ZONE:New( 'some zone' ):IsInstanceOf( 'GROUP' ) will return false
--
-- @param #BASE self
-- @param ClassName is the name of the class or the class itself to run the check against
-- @return #boolean
function BASE:IsInstanceOf( ClassName )
-- Is className NOT a string ?
if type( ClassName ) ~= 'string' then
-- Is className a Moose class ?
if type( ClassName ) == 'table' and ClassName.ClassName ~= nil then
-- Get the name of the Moose class as a string
ClassName = ClassName.ClassName
-- className is neither a string nor a Moose class, throw an error
else
-- I'm not sure if this should take advantage of MOOSE logging function, or throw an error for pcall
local err_str = 'className parameter should be a string; parameter received: '..type( ClassName )
self:E( err_str )
-- error( err_str )
return false
end
end
ClassName = string.upper( ClassName )
if string.upper( self.ClassName ) == ClassName then
return true
end
local Parent = getParent(self)
while Parent do
if string.upper( Parent.ClassName ) == ClassName then
return true
end
Parent = getParent( Parent )
end
return false
end
--- Get the ClassName + ClassID of the class instance.
-- The ClassName + ClassID is formatted as '%s#%09d'.
-- @param #BASE self
@@ -383,7 +460,7 @@ do -- Event Handling
-- @return #BASE
function BASE:UnHandleEvent( Event )
self:EventDispatcher():Remove( self, Event )
self:EventDispatcher():RemoveEvent( self, Event )
return self
end
@@ -566,6 +643,22 @@ function BASE:CreateEventCrash( EventTime, Initiator )
world.onEvent( Event )
end
--- Creation of a Takeoff Event.
-- @param #BASE self
-- @param Dcs.DCSTypes#Time EventTime The time stamp of the event.
-- @param Dcs.DCSWrapper.Object#Object Initiator The initiating object of the event.
function BASE:CreateEventTakeoff( EventTime, Initiator )
self:F( { EventTime, Initiator } )
local Event = {
id = world.event.S_EVENT_TAKEOFF,
time = EventTime,
initiator = Initiator,
}
world.onEvent( Event )
end
-- TODO: Complete Dcs.DCSTypes#Event structure.
--- The main event handling function... This function captures all events generated for the class.
-- @param #BASE self
@@ -596,6 +689,86 @@ function BASE:onEvent(event)
end
end
do -- Scheduling
--- Schedule a new time event. Note that the schedule will only take place if the scheduler is *started*. Even for a single schedule event, the scheduler needs to be started also.
-- @param #BASE self
-- @param #number Start Specifies the amount of seconds that will be waited before the scheduling is started, and the event function is called.
-- @param #function SchedulerFunction The event function to be called when a timer event occurs. The event function needs to accept the parameters specified in SchedulerArguments.
-- @param #table ... Optional arguments that can be given as part of scheduler. The arguments need to be given as a table { param1, param 2, ... }.
-- @return #number The ScheduleID of the planned schedule.
function BASE:ScheduleOnce( Start, SchedulerFunction, ... )
self:F2( { Start } )
self:T3( { ... } )
local ObjectName = "-"
ObjectName = self.ClassName .. self.ClassID
self:F3( { "ScheduleOnce: ", ObjectName, Start } )
self.SchedulerObject = self
local ScheduleID = _SCHEDULEDISPATCHER:AddSchedule(
self,
SchedulerFunction,
{ ... },
Start,
nil,
nil,
nil
)
self._.Schedules[#self.Schedules+1] = ScheduleID
return self._.Schedules
end
--- Schedule a new time event. Note that the schedule will only take place if the scheduler is *started*. Even for a single schedule event, the scheduler needs to be started also.
-- @param #BASE self
-- @param #number Start Specifies the amount of seconds that will be waited before the scheduling is started, and the event function is called.
-- @param #number Repeat Specifies the interval in seconds when the scheduler will call the event function.
-- @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.
-- @param #function SchedulerFunction The event function to be called when a timer event occurs. The event function needs to accept the parameters specified in SchedulerArguments.
-- @param #table ... Optional arguments that can be given as part of scheduler. The arguments need to be given as a table { param1, param 2, ... }.
-- @return #number The ScheduleID of the planned schedule.
function BASE:ScheduleRepeat( Start, Repeat, RandomizeFactor, Stop, SchedulerFunction, ... )
self:F2( { Start } )
self:T3( { ... } )
local ObjectName = "-"
ObjectName = self.ClassName .. self.ClassID
self:F3( { "ScheduleRepeat: ", ObjectName, Start, Repeat, RandomizeFactor, Stop } )
self.SchedulerObject = self
local ScheduleID = _SCHEDULEDISPATCHER:AddSchedule(
self,
SchedulerFunction,
{ ... },
Start,
Repeat,
RandomizeFactor,
Stop
)
self._.Schedules[SchedulerFunction] = ScheduleID
return self._.Schedules
end
--- Stops the Schedule.
-- @param #BASE self
-- @param #function SchedulerFunction The event function to be called when a timer event occurs. The event function needs to accept the parameters specified in SchedulerArguments.
function BASE:ScheduleStop( SchedulerFunction )
self:F3( { "ScheduleStop:" } )
_SCHEDULEDISPATCHER:Stop( self, self._.Schedules[SchedulerFunction] )
end
end
--- Set a state or property of the Object given a Key and a Value.
-- Note that if the Object is destroyed, nillified or garbage collected, then the Values and Keys will also be gone.
-- @param #BASE self
@@ -610,7 +783,6 @@ function BASE:SetState( Object, Key, Value )
self.States[ClassNameAndID] = self.States[ClassNameAndID] or {}
self.States[ClassNameAndID][Key] = Value
self:T2( { ClassNameAndID, Key, Value } )
return self.States[ClassNameAndID][Key]
end
@@ -628,7 +800,6 @@ function BASE:GetState( Object, Key )
if self.States[ClassNameAndID] then
local Value = self.States[ClassNameAndID][Key] or false
self:T2( { ClassNameAndID, Key, Value } )
return Value
end
@@ -899,3 +1070,35 @@ end
--- old stuff
--function BASE:_Destructor()
-- --self:E("_Destructor")
--
-- --self:EventRemoveAll()
--end
-- THIS IS WHY WE NEED LUA 5.2 ...
--function BASE:_SetDestructor()
--
-- -- TODO: Okay, this is really technical...
-- -- When you set a proxy to a table to catch __gc, weak tables don't behave like weak...
-- -- Therefore, I am parking this logic until I've properly discussed all this with the community.
--
-- local proxy = newproxy(true)
-- local proxyMeta = getmetatable(proxy)
--
-- proxyMeta.__gc = function ()
-- env.info("In __gc for " .. self:GetClassNameAndID() )
-- if self._Destructor then
-- self:_Destructor()
-- end
-- end
--
-- -- keep the userdata from newproxy reachable until the object
-- -- table is about to be garbage-collected - then the __gc hook
-- -- will be invoked and the destructor called
-- rawset( self, '__proxy', proxy )
--
--end

File diff suppressed because it is too large Load Diff

View File

@@ -2,8 +2,19 @@
--
-- ====
--
-- 1) @{#DATABASE} class, extends @{Base#BASE}
-- ===================================================
-- ### Author: **Sven Van de Velde (FlightControl)**
-- ### Contributions:
--
-- ====
--
-- @module Database
--- @type DATABASE
-- @extends Core.Base#BASE
--- # DATABASE class, extends @{Base#BASE}
--
-- Mission designers can use the DATABASE class to refer to:
--
-- * STATICS
@@ -17,35 +28,10 @@
--
-- On top, for internal MOOSE administration purposes, the DATBASE administers the Unit and Group TEMPLATES as defined within the Mission Editor.
--
-- Moose will automatically create one instance of the DATABASE class into the **global** object _DATABASE.
-- Moose refers to _DATABASE within the framework extensively, but you can also refer to the _DATABASE object within your missions if required.
-- 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.
--
-- 1.1) DATABASE iterators
-- -----------------------
-- You can iterate the database with the available iterator methods.
-- The iterator methods will walk the DATABASE set, and call for each element within the set a function that you provide.
-- The following iterator methods are currently available within the DATABASE:
--
-- * @{#DATABASE.ForEachUnit}: Calls a function for each @{UNIT} it finds within the DATABASE.
-- * @{#DATABASE.ForEachGroup}: Calls a function for each @{GROUP} it finds within the DATABASE.
-- * @{#DATABASE.ForEachPlayer}: Calls a function for each alive player it finds within the DATABASE.
-- * @{#DATABASE.ForEachPlayerJoined}: Calls a function for each joined player it finds within the DATABASE.
-- * @{#DATABASE.ForEachClient}: Calls a function for each @{CLIENT} it finds within the DATABASE.
-- * @{#DATABASE.ForEachClientAlive}: Calls a function for each alive @{CLIENT} it finds within the DATABASE.
--
-- ===
--
--
-- ### Author: **Sven Van de Velde (FlightControl)**
-- ### Contributions:
--
-- ====
-- @module Database
--- DATABASE class
-- @type DATABASE
-- @extends Core.Base#BASE
-- @field #DATABASE
DATABASE = {
ClassName = "DATABASE",
Templates = {
@@ -70,6 +56,8 @@ DATABASE = {
NavPoints = {},
PLAYERSETTINGS = {},
ZONENAMES = {},
HITS = {},
DESTROYS = {},
}
local _DATABASECoalition =
@@ -104,6 +92,7 @@ function DATABASE:New()
self:HandleEvent( EVENTS.Birth, self._EventOnBirth )
self:HandleEvent( EVENTS.Dead, self._EventOnDeadOrCrash )
self:HandleEvent( EVENTS.Crash, self._EventOnDeadOrCrash )
self:HandleEvent( EVENTS.Hit, self.AccountHits )
self:HandleEvent( EVENTS.NewCargo )
self:HandleEvent( EVENTS.DeleteCargo )
@@ -214,6 +203,16 @@ function DATABASE:FindStatic( StaticName )
return StaticFound
end
--- Finds a AIRBASE based on the AirbaseName.
-- @param #DATABASE self
-- @param #string AirbaseName
-- @return Wrapper.Airbase#AIRBASE The found AIRBASE.
function DATABASE:FindAirbase( AirbaseName )
local AirbaseFound = self.AIRBASES[AirbaseName]
return AirbaseFound
end
--- Adds a Airbase based on the Airbase Name in the DATABASE.
-- @param #DATABASE self
-- @param #string AirbaseName The name of the airbase
@@ -375,7 +374,12 @@ function DATABASE:Spawn( SpawnTemplate )
SpawnTemplate.CountryID = SpawnCountryID
SpawnTemplate.CategoryID = SpawnCategoryID
-- Ensure that for the spawned group and its units, there are GROUP and UNIT objects created in the DATABASE.
local SpawnGroup = self:AddGroup( SpawnTemplate.name )
for UnitID, UnitData in pairs( SpawnTemplate.units ) do
self:AddUnit( UnitData.name )
end
return SpawnGroup
end
@@ -400,10 +404,13 @@ end
--- Private method that registers new Group Templates within the DATABASE Object.
-- @param #DATABASE self
-- @param #table GroupTemplate
-- @param Dcs.DCScoalition#coalition.side CoalitionSide The coalition.side of the object.
-- @param Dcs.DCSObject#Object.Category CategoryID The Object.category of the object.
-- @param Dcs.DCScountry#country.id CountryID the country.id of the object
-- @return #DATABASE self
function DATABASE:_RegisterGroupTemplate( GroupTemplate, CoalitionID, CategoryID, CountryID )
function DATABASE:_RegisterGroupTemplate( GroupTemplate, CoalitionSide, CategoryID, CountryID, GroupName )
local GroupTemplateName = env.getValueDictByKey(GroupTemplate.name)
local GroupTemplateName = GroupName or env.getValueDictByKey( GroupTemplate.name )
local TraceTable = {}
@@ -418,7 +425,7 @@ function DATABASE:_RegisterGroupTemplate( GroupTemplate, CoalitionID, CategoryID
end
GroupTemplate.CategoryID = CategoryID
GroupTemplate.CoalitionID = CoalitionID
GroupTemplate.CoalitionID = CoalitionSide
GroupTemplate.CountryID = CountryID
self.Templates.Groups[GroupTemplateName].GroupName = GroupTemplateName
@@ -427,7 +434,7 @@ function DATABASE:_RegisterGroupTemplate( GroupTemplate, CoalitionID, CategoryID
self.Templates.Groups[GroupTemplateName].UnitCount = #GroupTemplate.units
self.Templates.Groups[GroupTemplateName].Units = GroupTemplate.units
self.Templates.Groups[GroupTemplateName].CategoryID = CategoryID
self.Templates.Groups[GroupTemplateName].CoalitionID = CoalitionID
self.Templates.Groups[GroupTemplateName].CoalitionID = CoalitionSide
self.Templates.Groups[GroupTemplateName].CountryID = CountryID
@@ -454,13 +461,13 @@ function DATABASE:_RegisterGroupTemplate( GroupTemplate, CoalitionID, CategoryID
self.Templates.Units[UnitTemplate.name].GroupTemplate = GroupTemplate
self.Templates.Units[UnitTemplate.name].GroupId = GroupTemplate.groupId
self.Templates.Units[UnitTemplate.name].CategoryID = CategoryID
self.Templates.Units[UnitTemplate.name].CoalitionID = CoalitionID
self.Templates.Units[UnitTemplate.name].CoalitionID = CoalitionSide
self.Templates.Units[UnitTemplate.name].CountryID = CountryID
if UnitTemplate.skill and (UnitTemplate.skill == "Client" or UnitTemplate.skill == "Player") then
self.Templates.ClientsByName[UnitTemplate.name] = UnitTemplate
self.Templates.ClientsByName[UnitTemplate.name].CategoryID = CategoryID
self.Templates.ClientsByName[UnitTemplate.name].CoalitionID = CoalitionID
self.Templates.ClientsByName[UnitTemplate.name].CoalitionID = CoalitionSide
self.Templates.ClientsByName[UnitTemplate.name].CountryID = CountryID
self.Templates.ClientsByID[UnitTemplate.unitId] = UnitTemplate
end
@@ -504,7 +511,7 @@ function DATABASE:_RegisterStaticTemplate( StaticTemplate, CoalitionID, Category
TraceTable[#TraceTable+1] = "Static"
TraceTable[#TraceTable+1] = self.Templates.Statics[StaticTemplateName].GroupName
TraceTable[#TraceTable+1] = self.Templates.Statics[StaticTemplateName].StaticName
TraceTable[#TraceTable+1] = "Coalition"
TraceTable[#TraceTable+1] = self.Templates.Statics[StaticTemplateName].CoalitionID
@@ -631,6 +638,7 @@ end
function DATABASE:_RegisterStatics()
local CoalitionsData = { GroupsRed = coalition.getStaticObjects( coalition.side.RED ), GroupsBlue = coalition.getStaticObjects( coalition.side.BLUE ) }
self:E( { Statics = CoalitionsData } )
for CoalitionId, CoalitionData in pairs( CoalitionsData ) do
for DCSStaticId, DCSStatic in pairs( CoalitionData ) do
@@ -707,6 +715,8 @@ function DATABASE:_EventOnDeadOrCrash( Event )
end
end
end
self:AccountDestroys( Event )
end
@@ -929,7 +939,7 @@ end
-- @param #string PlayerName
-- @return Core.Settings#SETTINGS
function DATABASE:GetPlayerSettings( PlayerName )
self:E({PlayerName})
self:F2( { PlayerName } )
return self.PLAYERSETTINGS[PlayerName]
end
@@ -940,7 +950,7 @@ end
-- @param Core.Settings#SETTINGS Settings
-- @return Core.Settings#SETTINGS
function DATABASE:SetPlayerSettings( PlayerName, Settings )
self:E({PlayerName, Settings})
self:F2( { PlayerName, Settings } )
self.PLAYERSETTINGS[PlayerName] = Settings
end
@@ -1038,6 +1048,101 @@ function DATABASE:_RegisterTemplates()
return self
end
--- Account the Hits of the Players.
-- @param #DATABASE self
-- @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
self:T( "Hitting Scenery" )
-- What is he hitting?
if Event.TgtCategory then
if Event.IniCoalition 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.
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 = ""
local TargetGroupName = ""
local TargetPlayerName = ""
local TargetCoalition = nil
local TargetCategory = nil
local TargetType = nil
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
self:T( "Something got destroyed" )
local Destroyed = false
-- What is the player destroying?
if self.HITS[Event.IniUnitName] then -- Was there a hit for this unit for this player before registered???
self.DESTROYS[Event.IniUnitName] = self.DESTROYS[Event.IniUnitName] or {}
self.DESTROYS[Event.IniUnitName] = true
end
end

View File

@@ -450,16 +450,16 @@ end
-- @param Core.Base#BASE EventClass The self instance of the class for which the event is.
-- @param Dcs.DCSWorld#world.event EventID
-- @return #EVENT.Events
function EVENT:Remove( EventClass, EventID )
function EVENT:RemoveEvent( EventClass, EventID )
self:E( { "Removing subscription for class: ", EventClass:GetClassNameAndID() } )
self:F2( { "Removing subscription for class: ", EventClass:GetClassNameAndID() } )
local EventPriority = EventClass:GetEventPriority()
self.EventsDead = self.EventsDead or {}
self.EventsDead[EventID] = self.EventsDead[EventID] or {}
self.EventsDead[EventID][EventPriority] = self.EventsDead[EventID][EventPriority] or {}
self.EventsDead[EventID][EventPriority][EventClass] = self.Events[EventID][EventPriority][EventClass]
self.Events = self.Events or {}
self.Events[EventID] = self.Events[EventID] or {}
self.Events[EventID][EventPriority] = self.Events[EventID][EventPriority] or {}
self.Events[EventID][EventPriority][EventClass] = self.Events[EventID][EventPriority][EventClass]
self.Events[EventID][EventPriority][EventClass] = nil
@@ -561,12 +561,13 @@ end
-- @param Core.Base#BASE EventClass The self instance of the class for which the event is.
-- @param EventID
-- @return #EVENT
function EVENT:OnEventForGroup( GroupName, EventFunction, EventClass, EventID )
self:F2( GroupName )
function EVENT:OnEventForGroup( GroupName, EventFunction, EventClass, EventID, ... )
self:E( GroupName )
local Event = self:Init( EventID, EventClass )
Event.EventGroup = true
Event.EventFunction = EventFunction
Event.Params = arg
return self
end
@@ -743,10 +744,15 @@ function EVENT:onEvent( Event )
local EventMeta = _EVENTMETA[Event.id]
if self and self.Events and self.Events[Event.id] then
--self:E( { EventMeta.Text, Event } ) -- Activate the see all incoming events ...
if self and
self.Events and
self.Events[Event.id] and
( Event.initiator ~= nil or ( Event.initiator == nil and Event.id ~= EVENTS.PlayerLeaveUnit ) ) then
if Event.initiator then
Event.IniObjectCategory = Event.initiator:getCategory()
if Event.IniObjectCategory == Object.Category.UNIT then
@@ -868,9 +874,9 @@ function EVENT:onEvent( Event )
-- Okay, we got the event from DCS. Now loop the SORTED self.EventSorted[] table for the received Event.id, and for each EventData registered, check if a function needs to be called.
for EventClass, EventData in pairs( self.Events[Event.id][EventPriority] ) do
if Event.IniObjectCategory ~= Object.Category.STATIC then
--self:E( { "Evaluating: ", EventClass:GetClassNameAndID() } )
end
--if Event.IniObjectCategory ~= Object.Category.STATIC then
-- self:E( { "Evaluating: ", EventClass:GetClassNameAndID() } )
--end
Event.IniGroup = GROUP:FindByName( Event.IniDCSGroupName )
Event.TgtGroup = GROUP:FindByName( Event.TgtDCSGroupName )
@@ -920,7 +926,7 @@ function EVENT:onEvent( Event )
end
else
-- The EventClass is not alive anymore, we remove it from the EventHandlers...
self:Remove( EventClass, Event.id )
self:RemoveEvent( EventClass, Event.id )
end
else
@@ -947,7 +953,7 @@ function EVENT:onEvent( Event )
local Result, Value = xpcall(
function()
return EventData.EventFunction( EventClass, Event )
return EventData.EventFunction( EventClass, Event, unpack( EventData.Params ) )
end, ErrorHandler )
else
@@ -963,14 +969,14 @@ function EVENT:onEvent( Event )
local Result, Value = xpcall(
function()
return EventFunction( EventClass, Event )
return EventFunction( EventClass, Event, unpack( EventData.Params ) )
end, ErrorHandler )
end
end
end
else
-- The EventClass is not alive anymore, we remove it from the EventHandlers...
self:Remove( EventClass, Event.id )
--self:RemoveEvent( EventClass, Event.id )
end
else
@@ -1015,7 +1021,7 @@ function EVENT:onEvent( Event )
end
end
else
self:E( { EventMeta.Text, Event } )
self:T( { EventMeta.Text, Event } )
end
Event = nil

View File

@@ -418,7 +418,7 @@ do -- FSM
-- @param #table ReturnEvents A table indicating for which returned events of the SubFSM which Event must be triggered in the FSM.
-- @return Core.Fsm#FSM_PROCESS The SubFSM.
function FSM:AddProcess( From, Event, Process, ReturnEvents )
self:T( { From, Event, Process, ReturnEvents } )
self:T( { From, Event } )
local Sub = {}
Sub.From = From
@@ -541,7 +541,7 @@ do -- FSM
end
function FSM:_submap( subs, sub, name )
self:F( { sub = sub, name = name } )
--self:F( { sub = sub, name = name } )
subs[sub.From] = subs[sub.From] or {}
subs[sub.From][sub.Event] = subs[sub.From][sub.Event] or {}
@@ -844,7 +844,7 @@ do -- FSM_CONTROLLABLE
-- @param Wrapper.Controllable#CONTROLLABLE FSMControllable
-- @return #FSM_CONTROLLABLE
function FSM_CONTROLLABLE:SetControllable( FSMControllable )
self:F( FSMControllable )
--self:F( FSMControllable:GetName() )
self.Controllable = FSMControllable
end
@@ -904,7 +904,7 @@ do -- FSM_PROCESS
local self = BASE:Inherit( self, FSM_CONTROLLABLE:New() ) -- Core.Fsm#FSM_PROCESS
self:F( Controllable, Task )
--self:F( Controllable )
self:Assign( Controllable, Task )
@@ -960,7 +960,7 @@ do -- FSM_PROCESS
-- Copy Processes
for ProcessID, Process in pairs( self:GetProcesses() ) do
self:E( { Process} )
--self:E( { Process:GetName() } )
local FsmProcess = NewFsm:AddProcess( Process.From, Process.Event, Process.fsm:Copy( Controllable, Task ), Process.ReturnEvents )
end
@@ -990,7 +990,6 @@ do -- FSM_PROCESS
-- Copy Processes
for ProcessID, Process in pairs( self:GetProcesses() ) do
self:E( { Process} )
if Process.fsm then
Process.fsm:Remove()
Process.fsm = nil
@@ -1063,7 +1062,7 @@ end
-- @param Wrapper.Unit#UNIT ProcessUnit
-- @return #FSM_PROCESS self
function FSM_PROCESS:Assign( ProcessUnit, Task )
self:T( { Task, ProcessUnit } )
--self:T( { Task:GetName(), ProcessUnit:GetName() } )
self:SetControllable( ProcessUnit )
self:SetTask( Task )
@@ -1093,7 +1092,7 @@ end
-- @param #string From
-- @param #string To
function FSM_PROCESS:onstatechange( ProcessUnit, Task, From, Event, To, Dummy )
self:T( { ProcessUnit, From, Event, To, Dummy, self:IsTrace() } )
self:T( { ProcessUnit:GetName(), From, Event, To, Dummy, self:IsTrace() } )
if self:IsTrace() then
--MESSAGE:New( "@ Process " .. self:GetClassNameAndID() .. " : " .. Event .. " changed to state " .. To, 2 ):ToAll()

View File

@@ -0,0 +1,146 @@
--- **Core (WIP)** -- Base class to allow the modeling of processes to achieve Goals.
--
-- ====
--
-- GOAL models processes that have an objective with a defined achievement. Derived classes implement the ways how the achievements can be realized.
--
-- ====
--
-- ### Author: **Sven Van de Velde (FlightControl)**
--
-- ====
--
-- @module Goal
do -- Goal
--- @type GOAL
-- @extends Core.Fsm#FSM
--- # GOAL class, extends @{Fsm#FSM}
--
-- GOAL models processes that have an objective with a defined achievement. Derived classes implement the ways how the achievements can be realized.
--
-- ## 1. GOAL constructor
--
-- * @{#GOAL.New}(): Creates a new GOAL object.
--
-- ## 2. GOAL is a finite state machine (FSM).
--
-- ### 2.1 GOAL States
--
-- * **Pending**: The goal object is in progress.
-- * **Achieved**: The goal objective is Achieved.
--
-- ### 2.2 GOAL Events
--
-- * **Achieved**: Set the goal objective to Achieved.
--
-- @field #GOAL
GOAL = {
ClassName = "GOAL",
}
--- @field #table GOAL.Players
GOAL.Players = {}
--- @field #number GOAL.TotalContributions
GOAL.TotalContributions = 0
--- GOAL Constructor.
-- @param #GOAL self
-- @return #GOAL
function GOAL:New()
local self = BASE:Inherit( self, FSM:New() ) -- #GOAL
self:F( {} )
--- Achieved State for GOAL
-- @field GOAL.Achieved
--- Achieved State Handler OnLeave for GOAL
-- @function [parent=#GOAL] OnLeaveAchieved
-- @param #GOAL self
-- @param #string From
-- @param #string Event
-- @param #string To
-- @return #boolean
--- Achieved State Handler OnEnter for GOAL
-- @function [parent=#GOAL] OnEnterAchieved
-- @param #GOAL self
-- @param #string From
-- @param #string Event
-- @param #string To
self:SetStartState( "Pending" )
self:AddTransition( "*", "Achieved", "Achieved" )
--- Achieved Handler OnBefore for GOAL
-- @function [parent=#GOAL] OnBeforeAchieved
-- @param #GOAL self
-- @param #string From
-- @param #string Event
-- @param #string To
-- @return #boolean
--- Achieved Handler OnAfter for GOAL
-- @function [parent=#GOAL] OnAfterAchieved
-- @param #GOAL self
-- @param #string From
-- @param #string Event
-- @param #string To
--- Achieved Trigger for GOAL
-- @function [parent=#GOAL] Achieved
-- @param #GOAL self
--- Achieved Asynchronous Trigger for GOAL
-- @function [parent=#GOAL] __Achieved
-- @param #GOAL self
-- @param #number Delay
self:SetEventPriority( 5 )
return self
end
--- @param #GOAL self
-- @param #string PlayerName
function GOAL:AddPlayerContribution( PlayerName )
self.Players[PlayerName] = self.Players[PlayerName] or 0
self.Players[PlayerName] = self.Players[PlayerName] + 1
self.TotalContributions = self.TotalContributions + 1
end
--- @param #GOAL self
-- @param #number Player contribution.
function GOAL:GetPlayerContribution( PlayerName )
return self.Players[PlayerName] or 0
end
--- @param #GOAL self
function GOAL:GetPlayerContributions()
return self.Players or {}
end
--- @param #GOAL self
function GOAL:GetTotalContributions()
return self.TotalContributions or 0
end
--- @param #GOAL self
-- @return #boolean true if the goal is Achieved
function GOAL:IsAchieved()
return self:Is( "Achieved" )
end
end

View File

@@ -103,6 +103,15 @@ do -- MENU_BASE
return self
end
--- Sets a tag for later selection of menu refresh.
-- @param #MENU_BASE self
-- @param #string MenuTag A Tag or Key that will filter only menu items set with this key.
-- @return #MENU_BASE
function MENU_BASE:SetTag( MenuTag )
self.MenuTag = MenuTag
return self
end
end
do -- MENU_COMMAND_BASE
@@ -115,6 +124,7 @@ do -- MENU_COMMAND_BASE
-- ----------------------------------------------------------
-- The MENU_COMMAND_BASE class defines the main MENU class where other MENU COMMAND_
-- classes are derived from, in order to set commands.
--
-- @field #MENU_COMMAND_BASE
MENU_COMMAND_BASE = {
ClassName = "MENU_COMMAND_BASE",
@@ -128,15 +138,51 @@ do -- MENU_COMMAND_BASE
-- @return #MENU_COMMAND_BASE
function MENU_COMMAND_BASE:New( MenuText, ParentMenu, CommandMenuFunction, CommandMenuArguments )
local self = BASE:Inherit( self, MENU_BASE:New( MenuText, ParentMenu ) )
local self = BASE:Inherit( self, MENU_BASE:New( MenuText, ParentMenu ) ) -- #MENU_COMMAND_BASE
-- When a menu function goes into error, DCS displays an obscure menu message.
-- This error handler catches the menu error and displays the full call stack.
local ErrorHandler = function( errmsg )
env.info( "MOOSE error in MENU COMMAND function: " .. errmsg )
if debug ~= nil then
env.info( debug.traceback() )
end
return errmsg
end
self.CommandMenuFunction = CommandMenuFunction
self.MenuCallHandler = function( CommandMenuArguments )
self.CommandMenuFunction( unpack( CommandMenuArguments ) )
self:SetCommandMenuFunction( CommandMenuFunction )
self:SetCommandMenuArguments( CommandMenuArguments )
self.MenuCallHandler = function()
local function MenuFunction()
return self.CommandMenuFunction( unpack( self.CommandMenuArguments ) )
end
local Status, Result = xpcall( MenuFunction, ErrorHandler )
end
return self
end
--- This sets the new command function of a menu,
-- so that if a menu is regenerated, or if command function changes,
-- that the function set for the menu is loosely coupled with the menu itself!!!
-- If the function changes, no new menu needs to be generated if the menu text is the same!!!
-- @param #MENU_COMMAND_BASE
-- @return #MENU_COMMAND_BASE
function MENU_COMMAND_BASE:SetCommandMenuFunction( CommandMenuFunction )
self.CommandMenuFunction = CommandMenuFunction
return self
end
--- This sets the new command arguments of a menu,
-- so that if a menu is regenerated, or if command arguments change,
-- that the arguments set for the menu are loosely coupled with the menu itself!!!
-- If the arguments change, no new menu needs to be generated if the menu text is the same!!!
-- @param #MENU_COMMAND_BASE
-- @return #MENU_COMMAND_BASE
function MENU_COMMAND_BASE:SetCommandMenuArguments( CommandMenuArguments )
self.CommandMenuArguments = CommandMenuArguments
return self
end
end
@@ -247,7 +293,7 @@ do -- MENU_MISSION_COMMAND
self:T( { MenuText, CommandMenuFunction, arg } )
self.MenuPath = missionCommands.addCommand( MenuText, self.MenuParentPath, self.MenuCallHandler, arg )
self.MenuPath = missionCommands.addCommand( MenuText, self.MenuParentPath, self.MenuCallHandler )
ParentMenu.Menus[self.MenuPath] = self
@@ -420,7 +466,7 @@ do -- MENU_COALITION_COMMAND
self:T( { MenuText, CommandMenuFunction, arg } )
self.MenuPath = missionCommands.addCommandForCoalition( self.MenuCoalition, MenuText, self.MenuParentPath, self.MenuCallHandler, arg )
self.MenuPath = missionCommands.addCommandForCoalition( self.MenuCoalition, MenuText, self.MenuParentPath, self.MenuCallHandler )
ParentMenu.Menus[self.MenuPath] = self
@@ -653,7 +699,7 @@ do -- MENU_CLIENT
missionCommands.removeItemForGroup( self.MenuClient:GetClientGroupID(), MenuPath[MenuPathID] )
end
self.MenuPath = missionCommands.addCommandForGroup( self.MenuClient:GetClientGroupID(), MenuText, MenuParentPath, self.MenuCallHandler, arg )
self.MenuPath = missionCommands.addCommandForGroup( self.MenuClient:GetClientGroupID(), MenuText, MenuParentPath, self.MenuCallHandler )
MenuPath[MenuPathID] = self.MenuPath
if ParentMenu and ParentMenu.Menus then
@@ -805,13 +851,14 @@ do
--- Removes the sub menus recursively of this MENU_GROUP.
-- @param #MENU_GROUP self
-- @param MenuTime
-- @param MenuTag A Tag or Key to filter the menus to be refreshed with the Tag set.
-- @return #MENU_GROUP self
function MENU_GROUP:RemoveSubMenus( MenuTime )
function MENU_GROUP:RemoveSubMenus( MenuTime, MenuTag )
--self:F2( { self.MenuPath, MenuTime, self.MenuTime } )
--self:T( { "Removing Group SubMenus:", self.MenuGroup:GetName(), self.MenuPath } )
self:T( { "Removing Group SubMenus:", MenuTime, MenuTag, self.MenuGroup:GetName(), self.MenuPath } )
for MenuText, Menu in pairs( self.Menus ) do
Menu:Remove( MenuTime )
Menu:Remove( MenuTime, MenuTag )
end
end
@@ -820,28 +867,31 @@ do
--- Removes the main menu and sub menus recursively of this MENU_GROUP.
-- @param #MENU_GROUP self
-- @param MenuTime
-- @param MenuTag A Tag or Key to filter the menus to be refreshed with the Tag set.
-- @return #nil
function MENU_GROUP:Remove( MenuTime )
function MENU_GROUP:Remove( MenuTime, MenuTag )
--self:F2( { self.MenuGroupID, self.MenuPath, MenuTime, self.MenuTime } )
self:RemoveSubMenus( MenuTime )
self:RemoveSubMenus( MenuTime, MenuTag )
if not MenuTime or self.MenuTime ~= MenuTime then
if self.MenuGroup._Menus[self.Path] then
self = self.MenuGroup._Menus[self.Path]
missionCommands.removeItemForGroup( self.MenuGroupID, self.MenuPath )
if self.ParentMenu then
self.ParentMenu.Menus[self.MenuText] = nil
self.ParentMenu.MenuCount = self.ParentMenu.MenuCount - 1
if self.ParentMenu.MenuCount == 0 then
if self.MenuRemoveParent == true then
self:T2( "Removing Parent Menu " )
self.ParentMenu:Remove()
if ( not MenuTag ) or ( MenuTag and self.MenuTag and MenuTag == self.MenuTag ) then
if self.MenuGroup._Menus[self.Path] then
self = self.MenuGroup._Menus[self.Path]
missionCommands.removeItemForGroup( self.MenuGroupID, self.MenuPath )
if self.ParentMenu then
self.ParentMenu.Menus[self.MenuText] = nil
self.ParentMenu.MenuCount = self.ParentMenu.MenuCount - 1
if self.ParentMenu.MenuCount == 0 then
if self.MenuRemoveParent == true then
self:T2( "Removing Parent Menu " )
self.ParentMenu:Remove()
end
end
end
end
self:T( { "Removing Group Menu:", MenuGroup = self.MenuGroup:GetName(), MenuPath = self.MenuGroup._Menus[self.Path].Path } )
self:T( { "Removing Group Menu:", MenuGroup = self.MenuGroup:GetName() } )
self.MenuGroup._Menus[self.Path] = nil
self = nil
end
@@ -852,7 +902,7 @@ do
--- @type MENU_GROUP_COMMAND
-- @extends Core.Menu#MENU_BASE
-- @extends Core.Menu#MENU_COMMAND_BASE
--- # MENU_GROUP_COMMAND class, extends @{Menu#MENU_COMMAND_BASE}
--
@@ -876,32 +926,37 @@ do
function MENU_GROUP_COMMAND:New( MenuGroup, MenuText, ParentMenu, CommandMenuFunction, ... )
MenuGroup._Menus = MenuGroup._Menus or {}
local Path = ( ParentMenu and ( table.concat( ParentMenu.MenuPath or {}, "@" ) .. "@" .. MenuText ) ) or MenuText
local Path = ( ParentMenu and ( table.concat( ParentMenu.MenuPath or {}, "@" ) .. "@" .. MenuText ) ) or MenuText
if MenuGroup._Menus[Path] then
self = MenuGroup._Menus[Path]
self:F2( { "Re-using Group Command Menu:", MenuGroup:GetName(), MenuText } )
else
self = BASE:Inherit( self, MENU_COMMAND_BASE:New( MenuText, ParentMenu, CommandMenuFunction, arg ) )
--if MenuGroup:IsAlive() then
MenuGroup._Menus[Path] = self
--end
self.Path = Path
self.MenuGroup = MenuGroup
self.MenuGroupID = MenuGroup:GetID()
self.MenuText = MenuText
self.ParentMenu = ParentMenu
self:F( { "Adding Group Command Menu:", MenuGroup = MenuGroup:GetName(), MenuText = MenuText, MenuPath = self.MenuParentPath } )
self.MenuPath = missionCommands.addCommandForGroup( self.MenuGroupID, MenuText, self.MenuParentPath, self.MenuCallHandler, arg )
if self.ParentMenu and self.ParentMenu.Menus then
self.ParentMenu.Menus[MenuText] = self
self.ParentMenu.MenuCount = self.ParentMenu.MenuCount + 1
self:F2( { ParentMenu.Menus, MenuText } )
end
--self:E( { Path=Path } )
--self:E( { self.MenuTag, self.MenuTime, "Re-using Group Command Menu:", MenuGroup:GetName(), MenuText } )
self:SetCommandMenuFunction( CommandMenuFunction )
self:SetCommandMenuArguments( arg )
return self
end
self = BASE:Inherit( self, MENU_COMMAND_BASE:New( MenuText, ParentMenu, CommandMenuFunction, arg ) )
--if MenuGroup:IsAlive() then
MenuGroup._Menus[Path] = self
--end
--self:E({Path=Path})
self.Path = Path
self.MenuGroup = MenuGroup
self.MenuGroupID = MenuGroup:GetID()
self.MenuText = MenuText
self.ParentMenu = ParentMenu
self:F( { "Adding Group Command Menu:", MenuGroup = MenuGroup:GetName(), MenuText = MenuText, MenuPath = self.MenuParentPath } )
self.MenuPath = missionCommands.addCommandForGroup( self.MenuGroupID, MenuText, self.MenuParentPath, self.MenuCallHandler )
if self.ParentMenu and self.ParentMenu.Menus then
self.ParentMenu.Menus[MenuText] = self
self.ParentMenu.MenuCount = self.ParentMenu.MenuCount + 1
self:F2( { ParentMenu.Menus, MenuText } )
end
-- end
return self
end
@@ -909,28 +964,32 @@ do
--- Removes a menu structure for a group.
-- @param #MENU_GROUP_COMMAND self
-- @param MenuTime
-- @param MenuTag A Tag or Key to filter the menus to be refreshed with the Tag set.
-- @return #nil
function MENU_GROUP_COMMAND:Remove( MenuTime )
function MENU_GROUP_COMMAND:Remove( MenuTime, MenuTag )
--self:F2( { self.MenuGroupID, self.MenuPath, MenuTime, self.MenuTime } )
--self:E( { MenuTag = MenuTag, MenuTime = self.MenuTime, Path = self.Path } )
if not MenuTime or self.MenuTime ~= MenuTime then
if self.MenuGroup._Menus[self.Path] then
self = self.MenuGroup._Menus[self.Path]
missionCommands.removeItemForGroup( self.MenuGroupID, self.MenuPath )
self:T( { "Removing Group Command Menu:", MenuGroup = self.MenuGroup:GetName(), MenuText = self.MenuText, MenuPath = self.Path } )
self.ParentMenu.Menus[self.MenuText] = nil
self.ParentMenu.MenuCount = self.ParentMenu.MenuCount - 1
if self.ParentMenu.MenuCount == 0 then
if self.MenuRemoveParent == true then
self:T2( "Removing Parent Menu " )
self.ParentMenu:Remove()
if ( not MenuTag ) or ( MenuTag and self.MenuTag and MenuTag == self.MenuTag ) then
if self.MenuGroup._Menus[self.Path] then
self = self.MenuGroup._Menus[self.Path]
missionCommands.removeItemForGroup( self.MenuGroupID, self.MenuPath )
--self:E( { "Removing Group Command Menu:", MenuGroup = self.MenuGroup:GetName(), MenuText = self.MenuText, MenuPath = self.Path } )
self.ParentMenu.Menus[self.MenuText] = nil
self.ParentMenu.MenuCount = self.ParentMenu.MenuCount - 1
if self.ParentMenu.MenuCount == 0 then
if self.MenuRemoveParent == true then
self:T2( "Removing Parent Menu " )
self.ParentMenu:Remove()
end
end
self.MenuGroup._Menus[self.Path] = nil
self = nil
end
self.MenuGroup._Menus[self.Path] = nil
self = nil
end
end

View File

@@ -53,6 +53,16 @@ MESSAGE = {
MessageID = 0,
}
--- Message Types
-- @type MESSAGE.Type
MESSAGE.Type = {
Update = "Update",
Information = "Information",
Briefing = "Briefing Report",
Overview = "Overview Report",
Detailed = "Detailed Report"
}
--- Creates a new MESSAGE object. Note that these MESSAGE objects are not yet displayed on the display panel. You must use the functions @{ToClient} or @{ToCoalition} or @{ToAll} to send these Messages to the respective recipients.
-- @param self
@@ -74,6 +84,9 @@ function MESSAGE:New( MessageText, MessageDuration, MessageCategory )
local self = BASE:Inherit( self, BASE:New() )
self:F( { MessageText, MessageDuration, MessageCategory } )
self.MessageType = nil
-- When no MessageCategory is given, we don't show it as a title...
if MessageCategory and MessageCategory ~= "" then
if MessageCategory:sub(-1) ~= "\n" then
@@ -96,6 +109,37 @@ function MESSAGE:New( MessageText, MessageDuration, MessageCategory )
return self
end
--- Creates a new MESSAGE object of a certain type.
-- Note that these MESSAGE objects are not yet displayed on the display panel.
-- You must use the functions @{ToClient} or @{ToCoalition} or @{ToAll} to send these Messages to the respective recipients.
-- The message display times are automatically defined based on the timing settings in the @{Settings} menu.
-- @param self
-- @param #string MessageText is the text of the Message.
-- @param #MESSAGE.Type MessageType The type of the message.
-- @return #MESSAGE
-- @usage
-- MessageAll = MESSAGE:NewType( "To all Players: BLUE has won! Each player of BLUE wins 50 points!", MESSAGE.Type.Information )
-- MessageRED = MESSAGE:NewType( "To the RED Players: You receive a penalty because you've killed one of your own units", MESSAGE.Type.Information )
-- MessageClient1 = MESSAGE:NewType( "Congratulations, you've just hit a target", MESSAGE.Type.Update )
-- MessageClient2 = MESSAGE:NewType( "Congratulations, you've just killed a target", MESSAGE.Type.Update )
function MESSAGE:NewType( MessageText, MessageType )
local self = BASE:Inherit( self, BASE:New() )
self:F( { MessageText } )
self.MessageType = MessageType
self.MessageTime = timer.getTime()
self.MessageText = MessageText:gsub("^\n","",1):gsub("\n$","",1)
return self
end
--- Sends a MESSAGE to a Client Group. Note that the Group needs to be defined within the ME with the skillset "Client" or "Player".
-- @param #MESSAGE self
-- @param Wrapper.Client#CLIENT Client is the Group of the Client.
@@ -115,14 +159,22 @@ end
-- MessageClient2 = MESSAGE:New( "Congratulations, you've just killed a target", "Score", 25, "Score" )
-- MessageClient1:ToClient( ClientGroup )
-- MessageClient2:ToClient( ClientGroup )
function MESSAGE:ToClient( Client )
function MESSAGE:ToClient( Client, Settings )
self:F( Client )
if Client and Client:GetClientGroupID() then
local ClientGroupID = Client:GetClientGroupID()
self:T( self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$","") .. " / " .. self.MessageDuration )
trigger.action.outTextForGroup( ClientGroupID, self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$",""), self.MessageDuration )
if self.MessageType then
local Settings = Settings or ( Client and _DATABASE:GetPlayerSettings( Client:GetPlayerName() ) ) or _SETTINGS -- Core.Settings#SETTINGS
self.MessageDuration = Settings:GetMessageTime( self.MessageType )
self.MessageCategory = "" -- self.MessageType .. ": "
end
if self.MessageDuration ~= 0 then
local ClientGroupID = Client:GetClientGroupID()
self:T( self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$","") .. " / " .. self.MessageDuration )
trigger.action.outTextForGroup( ClientGroupID, self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$",""), self.MessageDuration )
end
end
return self
@@ -132,13 +184,21 @@ end
-- @param #MESSAGE self
-- @param Wrapper.Group#GROUP Group is the Group.
-- @return #MESSAGE
function MESSAGE:ToGroup( Group )
function MESSAGE:ToGroup( Group, Settings )
self:F( Group.GroupName )
if Group then
if self.MessageType then
local Settings = Settings or ( Group and _DATABASE:GetPlayerSettings( Group:GetPlayerName() ) ) or _SETTINGS -- Core.Settings#SETTINGS
self.MessageDuration = Settings:GetMessageTime( self.MessageType )
self.MessageCategory = "" -- self.MessageType .. ": "
end
self:T( self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$","") .. " / " .. self.MessageDuration )
trigger.action.outTextForGroup( Group:GetID(), self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$",""), self.MessageDuration )
if self.MessageDuration ~= 0 then
self:T( self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$","") .. " / " .. self.MessageDuration )
trigger.action.outTextForGroup( Group:GetID(), self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$",""), self.MessageDuration )
end
end
return self
@@ -193,12 +253,20 @@ end
-- or
-- MessageRED = MESSAGE:New( "To the RED Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" )
-- MessageRED:ToCoalition( coalition.side.RED )
function MESSAGE:ToCoalition( CoalitionSide )
function MESSAGE:ToCoalition( CoalitionSide, Settings )
self:F( CoalitionSide )
if self.MessageType then
local Settings = Settings or _SETTINGS -- Core.Settings#SETTINGS
self.MessageDuration = Settings:GetMessageTime( self.MessageType )
self.MessageCategory = "" -- self.MessageType .. ": "
end
if CoalitionSide then
self:T( self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$","") .. " / " .. self.MessageDuration )
trigger.action.outTextForCoalition( CoalitionSide, self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$",""), self.MessageDuration )
if self.MessageDuration ~= 0 then
self:T( self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$","") .. " / " .. self.MessageDuration )
trigger.action.outTextForCoalition( CoalitionSide, self.MessageText:gsub("\n$",""):gsub("\n$",""), self.MessageDuration )
end
end
return self
@@ -232,8 +300,16 @@ end
function MESSAGE:ToAll()
self:F()
self:ToCoalition( coalition.side.RED )
self:ToCoalition( coalition.side.BLUE )
if self.MessageType then
local Settings = Settings or _SETTINGS -- Core.Settings#SETTINGS
self.MessageDuration = Settings:GetMessageTime( self.MessageType )
self.MessageCategory = "" -- self.MessageType .. ": "
end
if self.MessageDuration ~= 0 then
self:T( self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$","") .. " / " .. self.MessageDuration )
trigger.action.outText( self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$",""), self.MessageDuration )
end
return self
end
@@ -245,8 +321,7 @@ end
function MESSAGE:ToAllIf( Condition )
if Condition and Condition == true then
self:ToCoalition( coalition.side.RED )
self:ToCoalition( coalition.side.BLUE )
self:ToAll()
end
return self

View File

@@ -54,8 +54,8 @@ do -- COORDINATE
--
-- A COORDINATE can prepare waypoints for Ground and Air groups to be embedded into a Route.
--
-- * @{#COORDINATE.RoutePointAir}(): Build an air route point.
-- * @{#COORDINATE.RoutePointGround}(): Build a ground route point.
-- * @{#COORDINATE.WaypointAir}(): Build an air route point.
-- * @{#COORDINATE.WaypointGround}(): Build a ground route point.
--
-- Route points can be used in the Route methods of the @{Group#GROUP} class.
--
@@ -90,6 +90,18 @@ do -- COORDINATE
-- * @{#COORDINATE.IlluminationBomb}(): To illuminate the point.
--
--
-- ## Markings
--
-- Place markers (text boxes with clarifications for briefings, target locations or any other reference point) on the map for all players, coalitions or specific groups:
--
-- * @{#COORDINATE.MarkToAll}(): Place a mark to all players.
-- * @{#COORDINATE.MarkToCoalition}(): Place a mark to a coalition.
-- * @{#COORDINATE.MarkToCoalitionRed}(): Place a mark to the red coalition.
-- * @{#COORDINATE.MarkToCoalitionBlue}(): Place a mark to the blue coalition.
-- * @{#COORDINATE.MarkToGroup}(): Place a mark to a group (needs to have a client in it or a CA group (CA group is bugged)).
-- * @{#COORDINATE.RemoveMark}(): Removes a mark from the map.
--
--
-- ## 3D calculation methods
--
-- Various calculation methods exist to use or manipulate 3D space. Find below a short description of each method:
@@ -138,6 +150,31 @@ do -- COORDINATE
ClassName = "COORDINATE",
}
--- @field COORDINATE.WaypointAltType
COORDINATE.WaypointAltType = {
BARO = "BARO",
RADIO = "RADIO",
}
--- @field COORDINATE.WaypointAction
COORDINATE.WaypointAction = {
TurningPoint = "Turning Point",
FlyoverPoint = "Fly Over Point",
FromParkingArea = "From Parking Area",
FromParkingAreaHot = "From Parking Area Hot",
FromRunway = "From Runway",
Landing = "Landing",
}
--- @field COORDINATE.WaypointType
COORDINATE.WaypointType = {
TakeOffParking = "TakeOffParking",
TakeOffParkingHot = "TakeOffParkingHot",
TakeOff = "TakeOffParkingHot",
TurningPoint = "Turning Point",
Land = "Land",
}
--- COORDINATE constructor.
-- @param #COORDINATE self
@@ -279,11 +316,55 @@ do -- COORDINATE
return RandomVec3
end
--- Return the height of the land at the coordinate.
-- @param #COORDINATE self
-- @return #number
function COORDINATE:GetLandHeight()
local Vec2 = { x = self.x, y = self.z }
return land.getHeight( Vec2 )
end
--- Set the heading of the coordinate, if applicable.
-- @param #COORDINATE self
function COORDINATE:SetHeading( Heading )
self.Heading = Heading
end
--- Get the heading of the coordinate, if applicable.
-- @param #COORDINATE self
-- @return #number or nil
function COORDINATE:GetHeading()
return self.Heading
end
--- Set the velocity of the COORDINATE.
-- @param #COORDINATE self
-- @param #string Velocity Velocity in meters per second.
function COORDINATE:SetVelocity( Velocity )
self.Velocity = Velocity
end
--- Return the velocity of the COORDINATE.
-- @param #COORDINATE self
-- @return #number Velocity in meters per second.
function COORDINATE:GetVelocity()
local Velocity = self.Velocity
return Velocity or 0
end
--- Return velocity text of the COORDINATE.
-- @param #COORDINATE self
-- @return #string
function COORDINATE:GetMovingText( Settings )
return self:GetVelocityText( Settings ) .. ", " .. self:GetHeadingText( Settings )
end
--- Return a direction vector Vec3 from COORDINATE to the COORDINATE.
@@ -294,6 +375,7 @@ do -- COORDINATE
return { x = TargetCoordinate.x - self.x, y = TargetCoordinate.y - self.y, z = TargetCoordinate.z - self.z }
end
--- Get a correction in radians of the real magnetic north of the COORDINATE.
-- @param #COORDINATE self
-- @return #number CorrectionRadians The correction in radians.
@@ -339,6 +421,7 @@ do -- COORDINATE
return ( ( TargetVec3.x - SourceVec3.x ) ^ 2 + ( TargetVec3.z - SourceVec3.z ) ^ 2 ) ^ 0.5
end
--- Return the 3D distance in meters between the target COORDINATE and the COORDINATE.
-- @param #COORDINATE self
-- @param #COORDINATE TargetCoordinate The target COORDINATE.
@@ -405,6 +488,38 @@ do -- COORDINATE
end
--- Return the velocity text of the COORDINATE.
-- @param #COORDINATE self
-- @return #string Velocity text.
function COORDINATE:GetVelocityText( Settings )
local Velocity = self:GetVelocity()
local Settings = Settings or _SETTINGS
if Velocity then
if Settings:IsMetric() then
return string.format( " moving at %d km/h", UTILS.MpsToKmph( Velocity ) )
else
return string.format( " moving at %d mi/h", UTILS.MpsToKmph( Velocity ) / 1.852 )
end
else
return " stationary"
end
end
--- Return the heading text of the COORDINATE.
-- @param #COORDINATE self
-- @return #string Heading text.
function COORDINATE:GetHeadingText( Settings )
local Heading = self:GetHeading()
if Heading then
return string.format( " bearing %3d°", Heading )
else
return " bearing unknown"
end
end
--- Provides a Bearing / Range string
-- @param #COORDINATE self
-- @param #number AngleRadians The angle in randians
@@ -463,25 +578,25 @@ do -- COORDINATE
--- Build an air type route point.
-- @param #COORDINATE self
-- @param #COORDINATE.RoutePointAltType AltType The altitude type.
-- @param #COORDINATE.RoutePointType Type The route point type.
-- @param #COORDINATE.RoutePointAction Action The route point action.
-- @param #COORDINATE.WaypointAltType AltType The altitude type.
-- @param #COORDINATE.WaypointType Type The route point type.
-- @param #COORDINATE.WaypointAction Action The route point action.
-- @param Dcs.DCSTypes#Speed Speed Airspeed in km/h.
-- @param #boolean SpeedLocked true means the speed is locked.
-- @return #table The route point.
function COORDINATE:RoutePointAir( AltType, Type, Action, Speed, SpeedLocked )
function COORDINATE:WaypointAir( AltType, Type, Action, Speed, SpeedLocked )
self:F2( { AltType, Type, Action, Speed, SpeedLocked } )
local RoutePoint = {}
RoutePoint.x = self.x
RoutePoint.y = self.z
RoutePoint.alt = self.y
RoutePoint.alt_type = AltType
RoutePoint.alt_type = AltType or "RADIO"
RoutePoint.type = Type
RoutePoint.action = Action
RoutePoint.type = Type or nil
RoutePoint.action = Action or nil
RoutePoint.speed = Speed / 3.6
RoutePoint.speed = ( Speed and Speed / 3.6 ) or ( 500 / 3.6 )
RoutePoint.speed_locked = true
-- ["task"] =
@@ -505,12 +620,81 @@ do -- COORDINATE
return RoutePoint
end
--- Build a Waypoint Air "Turning Point".
-- @param #COORDINATE self
-- @param #COORDINATE.WaypointAltType AltType The altitude type.
-- @param Dcs.DCSTypes#Speed Speed Airspeed in km/h.
-- @return #table The route point.
function COORDINATE:WaypointAirTurningPoint( AltType, Speed )
return self:WaypointAir( AltType, COORDINATE.WaypointType.TurningPoint, COORDINATE.WaypointAction.TurningPoint, Speed )
end
--- Build a Waypoint Air "Fly Over Point".
-- @param #COORDINATE self
-- @param #COORDINATE.WaypointAltType AltType The altitude type.
-- @param Dcs.DCSTypes#Speed Speed Airspeed in km/h.
-- @return #table The route point.
function COORDINATE:WaypointAirFlyOverPoint( AltType, Speed )
return self:WaypointAir( AltType, COORDINATE.WaypointType.TurningPoint, COORDINATE.WaypointAction.FlyoverPoint, Speed )
end
--- Build a Waypoint Air "Take Off Parking Hot".
-- @param #COORDINATE self
-- @param #COORDINATE.WaypointAltType AltType The altitude type.
-- @param Dcs.DCSTypes#Speed Speed Airspeed in km/h.
-- @return #table The route point.
function COORDINATE:WaypointAirTakeOffParkingHot( AltType, Speed )
return self:WaypointAir( AltType, COORDINATE.WaypointType.TakeOffParkingHot, COORDINATE.WaypointAction.FromParkingAreaHot, Speed )
end
--- Build a Waypoint Air "Take Off Parking".
-- @param #COORDINATE self
-- @param #COORDINATE.WaypointAltType AltType The altitude type.
-- @param Dcs.DCSTypes#Speed Speed Airspeed in km/h.
-- @return #table The route point.
function COORDINATE:WaypointAirTakeOffParking( AltType, Speed )
return self:WaypointAir( AltType, COORDINATE.WaypointType.TakeOffParking, COORDINATE.WaypointAction.FromParkingArea, Speed )
end
--- Build a Waypoint Air "Take Off Runway".
-- @param #COORDINATE self
-- @param #COORDINATE.WaypointAltType AltType The altitude type.
-- @param Dcs.DCSTypes#Speed Speed Airspeed in km/h.
-- @return #table The route point.
function COORDINATE:WaypointAirTakeOffRunway( AltType, Speed )
return self:WaypointAir( AltType, COORDINATE.WaypointType.TakeOff, COORDINATE.WaypointAction.FromRunway, Speed )
end
--- Build a Waypoint Air "Landing".
-- @param #COORDINATE self
-- @param Dcs.DCSTypes#Speed Speed Airspeed in km/h.
-- @return #table The route point.
-- @usage
--
-- LandingZone = ZONE:New( "LandingZone" )
-- LandingCoord = LandingZone:GetCoordinate()
-- LandingWaypoint = LandingCoord:WaypointAirLanding( 60 )
-- HeliGroup:Route( { LandWaypoint }, 1 ) -- Start landing the helicopter in one second.
--
function COORDINATE:WaypointAirLanding( Speed )
return self:WaypointAir( nil, COORDINATE.WaypointType.Land, COORDINATE.WaypointAction.Landing, Speed )
end
--- Build an ground type route point.
-- @param #COORDINATE self
-- @param Dcs.DCSTypes#Speed Speed Speed in km/h.
-- @param #COORDINATE.RoutePointAction Formation The route point Formation.
-- @param #number Speed (optional) Speed in km/h. The default speed is 999 km/h.
-- @param #string Formation (optional) The route point Formation, which is a text string that specifies exactly the Text in the Type of the route point, like "Vee", "Echelon Right".
-- @return #table The route point.
function COORDINATE:RoutePointGround( Speed, Formation )
function COORDINATE:WaypointGround( Speed, Formation )
self:F2( { Formation, Speed } )
local RoutePoint = {}
@@ -520,7 +704,7 @@ do -- COORDINATE
RoutePoint.action = Formation or ""
RoutePoint.speed = Speed / 3.6
RoutePoint.speed = ( Speed or 999 ) / 3.6
RoutePoint.speed_locked = true
-- ["task"] =
@@ -642,6 +826,88 @@ do -- COORDINATE
self:F2( Azimuth )
self:Flare( FLARECOLOR.Red, Azimuth )
end
do -- Markings
--- Mark to All
-- @param #COORDINATE self
-- @param #string MarkText Free format text that shows the marking clarification.
-- @return #number The resulting Mark ID which is a number.
-- @usage
-- local TargetCoord = TargetGroup:GetCoordinate()
-- local MarkID = TargetCoord:MarkToAll( "This is a target for all players" )
function COORDINATE:MarkToAll( MarkText )
local MarkID = UTILS.GetMarkID()
trigger.action.markToAll( MarkID, MarkText, self:GetVec3() )
return MarkID
end
--- Mark to Coalition
-- @param #COORDINATE self
-- @param #string MarkText Free format text that shows the marking clarification.
-- @param Coalition
-- @return #number The resulting Mark ID which is a number.
-- @usage
-- local TargetCoord = TargetGroup:GetCoordinate()
-- local MarkID = TargetCoord:MarkToCoalition( "This is a target for the red coalition", coalition.side.RED )
function COORDINATE:MarkToCoalition( MarkText, Coalition )
local MarkID = UTILS.GetMarkID()
trigger.action.markToCoalition( MarkID, MarkText, self:GetVec3(), Coalition )
return MarkID
end
--- Mark to Red Coalition
-- @param #COORDINATE self
-- @param #string MarkText Free format text that shows the marking clarification.
-- @return #number The resulting Mark ID which is a number.
-- @usage
-- local TargetCoord = TargetGroup:GetCoordinate()
-- local MarkID = TargetCoord:MarkToCoalitionRed( "This is a target for the red coalition" )
function COORDINATE:MarkToCoalitionRed( MarkText )
return self:MarkToCoalition( MarkText, coalition.side.RED )
end
--- Mark to Blue Coalition
-- @param #COORDINATE self
-- @param #string MarkText Free format text that shows the marking clarification.
-- @return #number The resulting Mark ID which is a number.
-- @usage
-- local TargetCoord = TargetGroup:GetCoordinate()
-- local MarkID = TargetCoord:MarkToCoalitionBlue( "This is a target for the blue coalition" )
function COORDINATE:MarkToCoalitionBlue( MarkText )
return self:MarkToCoalition( MarkText, coalition.side.BLUE )
end
--- Mark to Group
-- @param #COORDINATE self
-- @param #string MarkText Free format text that shows the marking clarification.
-- @param Wrapper.Group#GROUP MarkGroup The @{Group} that receives the mark.
-- @return #number The resulting Mark ID which is a number.
-- @usage
-- local TargetCoord = TargetGroup:GetCoordinate()
-- local MarkGroup = GROUP:FindByName( "AttackGroup" )
-- local MarkID = TargetCoord:MarkToGroup( "This is a target for the attack group", AttackGroup )
function COORDINATE:MarkToGroup( MarkText, MarkGroup )
local MarkID = UTILS.GetMarkID()
trigger.action.markToGroup( MarkID, MarkText, self:GetVec3(), MarkGroup:GetID() )
return MarkID
end
--- Remove a mark
-- @param #COORDINATE self
-- @param #number MarkID The ID of the mark to be removed.
-- @usage
-- local TargetCoord = TargetGroup:GetCoordinate()
-- local MarkGroup = GROUP:FindByName( "AttackGroup" )
-- local MarkID = TargetCoord:MarkToGroup( "This is a target for the attack group", AttackGroup )
-- <<< logic >>>
-- RemoveMark( MarkID ) -- The mark is now removed
function COORDINATE:RemoveMark( MarkID )
trigger.action.removeMark( MarkID )
end
end -- Markings
--- Returns if a Coordinate has Line of Sight (LOS) with the ToCoordinate.
-- @param #COORDINATE self
@@ -662,6 +928,39 @@ do -- COORDINATE
end
--- Returns if a Coordinate is in a certain Radius of this Coordinate in 2D plane using the X and Z axis.
-- @param #COORDINATE self
-- @param #COORDINATE ToCoordinate The coordinate that will be tested if it is in the radius of this coordinate.
-- @param #number Radius The radius of the circle on the 2D plane around this coordinate.
-- @return #boolean true if in the Radius.
function COORDINATE:IsInRadius( Coordinate, Radius )
local InVec2 = self:GetVec2()
local Vec2 = Coordinate:GetVec2()
local InRadius = UTILS.IsInRadius( InVec2, Vec2, Radius)
return InRadius
end
--- Returns if a Coordinate is in a certain radius of this Coordinate in 3D space using the X, Y and Z axis.
-- So Radius defines the radius of the a Sphere in 3D space around this coordinate.
-- @param #COORDINATE self
-- @param #COORDINATE ToCoordinate The coordinate that will be tested if it is in the radius of this coordinate.
-- @param #number Radius The radius of the sphere in the 3D space around this coordinate.
-- @return #boolean true if in the Sphere.
function COORDINATE:IsInSphere( Coordinate, Radius )
local InVec3 = self:GetVec3()
local Vec3 = Coordinate:GetVec3()
local InSphere = UTILS.IsInSphere( InVec3, Vec3, Radius)
return InSphere
end
--- Return a BR string from a COORDINATE to the COORDINATE.
-- @param #COORDINATE self
-- @param #COORDINATE TargetCoordinate The target COORDINATE.
@@ -725,16 +1024,26 @@ do -- COORDINATE
return ""
end
--- Provides a Lat Lon string
--- Provides a Lat Lon string in Degree Minute Second format.
-- @param #COORDINATE self
-- @param Core.Settings#SETTINGS Settings (optional) Settings
-- @return #string The LL Text
function COORDINATE:ToStringLL( Settings ) --R2.1 Fixes issue #424.
-- @return #string The LL DMS Text
function COORDINATE:ToStringLLDMS( Settings )
local LL_Accuracy = Settings and Settings.LL_Accuracy or _SETTINGS.LL_Accuracy
local LL_DMS = Settings and Settings.LL_DMS or _SETTINGS.LL_DMS
local lat, lon = coord.LOtoLL( self:GetVec3() )
return "LL, " .. UTILS.tostringLL( lat, lon, LL_Accuracy, LL_DMS )
return "LL DMS, " .. UTILS.tostringLL( lat, lon, LL_Accuracy, true )
end
--- Provides a Lat Lon string in Degree Decimal Minute format.
-- @param #COORDINATE self
-- @param Core.Settings#SETTINGS Settings (optional) Settings
-- @return #string The LL DDM Text
function COORDINATE:ToStringLLDDM( Settings )
local LL_Accuracy = Settings and Settings.LL_Accuracy or _SETTINGS.LL_Accuracy
local lat, lon = coord.LOtoLL( self:GetVec3() )
return "LL DDM, " .. UTILS.tostringLL( lat, lon, LL_Accuracy, false )
end
--- Provides a MGRS string
@@ -780,44 +1089,124 @@ do -- COORDINATE
end
--- Provides a coordinate string of the point, based on the A2G coordinate format system.
-- @param #COORDINATE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable
-- @param Core.Settings#SETTINGS Settings
-- @return #string The coordinate Text in the configured coordinate system.
function COORDINATE:ToStringA2G( Controllable, Settings ) -- R2.2
self:F( { Controllable = Controllable and Controllable:GetName() } )
local Settings = Settings or ( Controllable and _DATABASE:GetPlayerSettings( Controllable:GetPlayerName() ) ) or _SETTINGS
if Settings:IsA2G_BR() then
-- If no Controllable is given to calculate the BR from, then MGRS will be used!!!
if Controllable then
local Coordinate = Controllable:GetCoordinate()
return Controllable and self:ToStringBR( Coordinate, Settings ) or self:ToStringMGRS( Settings )
else
return self:ToStringMGRS( Settings )
end
end
if Settings:IsA2G_LL_DMS() then
return self:ToStringLLDMS( Settings )
end
if Settings:IsA2G_LL_DDM() then
return self:ToStringLLDDM( Settings )
end
if Settings:IsA2G_MGRS() then
return self:ToStringMGRS( Settings )
end
return nil
end
--- Provides a coordinate string of the point, based on the A2A coordinate format system.
-- @param #COORDINATE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable
-- @param Core.Settings#SETTINGS Settings
-- @return #string The coordinate Text in the configured coordinate system.
function COORDINATE:ToStringA2A( Controllable, Settings ) -- R2.2
self:F( { Controllable = Controllable and Controllable:GetName() } )
local Settings = Settings or ( Controllable and _DATABASE:GetPlayerSettings( Controllable:GetPlayerName() ) ) or _SETTINGS
if Settings:IsA2A_BRAA() then
if Controllable then
local Coordinate = Controllable:GetCoordinate()
return self:ToStringBRA( Coordinate, Settings )
else
return self:ToStringMGRS( Settings )
end
end
if Settings:IsA2A_BULLS() then
local Coalition = Controllable:GetCoalition()
return self:ToStringBULLS( Coalition, Settings )
end
if Settings:IsA2A_LL_DMS() then
return self:ToStringLLDMS( Settings )
end
if Settings:IsA2A_LL_DDM() then
return self:ToStringLLDDM( Settings )
end
if Settings:IsA2A_MGRS() then
return self:ToStringMGRS( Settings )
end
return nil
end
--- Provides a coordinate string of the point, based on a coordinate format system:
-- * Uses default settings in COORDINATE.
-- * Can be overridden if for a GROUP containing x clients, a menu was selected to override the default.
-- @param #COORDINATE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable
-- @param Core.Settings#SETTINGS Settings
-- @param Tasking.Task#TASK Task The task for which coordinates need to be calculated.
-- @return #string The coordinate Text in the configured coordinate system.
function COORDINATE:ToString( Controllable, Settings ) -- R2.2
function COORDINATE:ToString( Controllable, Settings, Task ) -- R2.2
self:E( { Controllable = Controllable } )
self:F( { Controllable = Controllable and Controllable:GetName() } )
local Settings = Settings or ( Controllable and _DATABASE:GetPlayerSettings( Controllable:GetPlayerName() ) ) or _SETTINGS
local IsAir = Controllable and Controllable:IsAirPlane() or false
if IsAir then
if Settings:IsA2A_BRA() then
local Coordinate = Controllable:GetCoordinate()
return self:ToStringBRA( Coordinate, Settings )
end
if Settings:IsA2A_BULLS() then
local Coalition = Controllable:GetCoalition()
return self:ToStringBULLS( Coalition, Settings )
local ModeA2A = false
if Task then
if Task:IsInstanceOf( TASK_A2A ) then
ModeA2A = true
else
if Task:IsInstanceOf( TASK_A2G ) then
ModeA2A = false
else
if Task:IsInstanceOf( TASK_CARGO ) then
ModeA2A = false
else
ModeA2A = false
end
end
end
else
if Settings:IsA2G_BRA() then
local Coordinate = Controllable:GetCoordinate()
return Controllable and self:ToStringBR( Coordinate, Settings ) or self:ToStringMGRS( Settings )
end
if Settings:IsA2G_LL() then
return self:ToStringLL( Settings )
end
if Settings:IsA2G_MGRS() then
return self:ToStringMGRS( Settings )
local IsAir = Controllable and Controllable:IsAirPlane() or false
if IsAir then
ModeA2A = true
else
ModeA2A = false
end
end
if ModeA2A == true then
return self:ToStringA2A( Controllable, Settings )
else
return self:ToStringA2G( Controllable, Settings )
end
return nil
end

View File

@@ -0,0 +1,86 @@
--- The REPORT class
-- @type REPORT
-- @extends Core.Base#BASE
REPORT = {
ClassName = "REPORT",
Title = "",
}
--- Create a new REPORT.
-- @param #REPORT self
-- @param #string Title
-- @return #REPORT
function REPORT:New( Title )
local self = BASE:Inherit( self, BASE:New() ) -- #REPORT
self.Report = {}
self:SetTitle( Title or "" )
self:SetIndent( 3 )
return self
end
--- Has the REPORT Text?
-- @param #REPORT self
-- @return #boolean
function REPORT:HasText() --R2.1
return #self.Report > 0
end
--- Set indent of a REPORT.
-- @param #REPORT self
-- @param #number Indent
-- @return #REPORT
function REPORT:SetIndent( Indent ) --R2.1
self.Indent = Indent
return self
end
--- Add a new line to a REPORT.
-- @param #REPORT self
-- @param #string Text
-- @return #REPORT
function REPORT:Add( Text )
self.Report[#self.Report+1] = Text
return self
end
--- Add a new line to a REPORT.
-- @param #REPORT self
-- @param #string Text
-- @return #REPORT
function REPORT:AddIndent( Text ) --R2.1
self.Report[#self.Report+1] = string.rep(" ", self.Indent ) .. Text:gsub("\n","\n"..string.rep( " ", self.Indent ) )
return self
end
--- Produces the text of the report, taking into account an optional delimeter, which is \n by default.
-- @param #REPORT self
-- @param #string Delimiter (optional) A delimiter text.
-- @return #string The report text.
function REPORT:Text( Delimiter )
Delimiter = Delimiter or "\n"
local ReportText = ( self.Title ~= "" and self.Title .. Delimiter or self.Title ) .. table.concat( self.Report, Delimiter ) or ""
return ReportText
end
--- Sets the title of the report.
-- @param #REPORT self
-- @param #string Title The title of the report.
-- @return #REPORT
function REPORT:SetTitle( Title )
self.Title = Title
return self
end
--- Gets the amount of report items contained in the report.
-- @param #REPORT self
-- @return #number Returns the number of report items contained in the report. 0 is returned if no report items are contained in the report. The title is not counted for.
function REPORT:GetCount()
return #self.Report
end

View File

@@ -55,6 +55,7 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr
self:F2( { Scheduler, ScheduleFunction, ScheduleArguments, Start, Repeat, Randomize, Stop } )
self.CallID = self.CallID + 1
local CallID = self.CallID .. "#" .. ( Scheduler.MasterObject and Scheduler.MasterObject.GetClassNameAndID and Scheduler.MasterObject:GetClassNameAndID() or "" ) or ""
-- Initialize the ObjectSchedulers array, which is a weakly coupled table.
-- If the object used as the key is nil, then the garbage collector will remove the item from the Functions array.
@@ -65,27 +66,27 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr
self.ObjectSchedulers = self.ObjectSchedulers or setmetatable( {}, { __mode = "v" } )
if Scheduler.MasterObject then
self.ObjectSchedulers[self.CallID] = Scheduler
self:F3( { CallID = self.CallID, ObjectScheduler = tostring(self.ObjectSchedulers[self.CallID]), MasterObject = tostring(Scheduler.MasterObject) } )
self.ObjectSchedulers[CallID] = Scheduler
self:F3( { CallID = CallID, ObjectScheduler = tostring(self.ObjectSchedulers[CallID]), MasterObject = tostring(Scheduler.MasterObject) } )
else
self.PersistentSchedulers[self.CallID] = Scheduler
self:F3( { CallID = self.CallID, PersistentScheduler = self.PersistentSchedulers[self.CallID] } )
self.PersistentSchedulers[CallID] = Scheduler
self:F3( { CallID = CallID, PersistentScheduler = self.PersistentSchedulers[CallID] } )
end
self.Schedule = self.Schedule or setmetatable( {}, { __mode = "k" } )
self.Schedule[Scheduler] = self.Schedule[Scheduler] or {}
self.Schedule[Scheduler][self.CallID] = {}
self.Schedule[Scheduler][self.CallID].Function = ScheduleFunction
self.Schedule[Scheduler][self.CallID].Arguments = ScheduleArguments
self.Schedule[Scheduler][self.CallID].StartTime = timer.getTime() + ( Start or 0 )
self.Schedule[Scheduler][self.CallID].Start = Start + .1
self.Schedule[Scheduler][self.CallID].Repeat = Repeat
self.Schedule[Scheduler][self.CallID].Randomize = Randomize
self.Schedule[Scheduler][self.CallID].Stop = Stop
self.Schedule[Scheduler][CallID] = {}
self.Schedule[Scheduler][CallID].Function = ScheduleFunction
self.Schedule[Scheduler][CallID].Arguments = ScheduleArguments
self.Schedule[Scheduler][CallID].StartTime = timer.getTime() + ( Start or 0 )
self.Schedule[Scheduler][CallID].Start = Start + .1
self.Schedule[Scheduler][CallID].Repeat = Repeat or 0
self.Schedule[Scheduler][CallID].Randomize = Randomize or 0
self.Schedule[Scheduler][CallID].Stop = Stop
self:T3( self.Schedule[Scheduler][self.CallID] )
self:T3( self.Schedule[Scheduler][CallID] )
self.Schedule[Scheduler][self.CallID].CallHandler = function( CallID )
self.Schedule[Scheduler][CallID].CallHandler = function( CallID )
self:F2( CallID )
local ErrorHandler = function( errmsg )
@@ -100,11 +101,12 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr
if not Scheduler then
Scheduler = self.PersistentSchedulers[CallID]
end
--self:T3( { Scheduler = Scheduler } )
if Scheduler then
local MasterObject = tostring(Scheduler.MasterObject)
local Schedule = self.Schedule[Scheduler][CallID]
--self:T3( { Schedule = Schedule } )
@@ -133,10 +135,13 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr
end
local CurrentTime = timer.getTime()
local StartTime = CurrentTime + Start
local StartTime = Schedule.StartTime
self:F3( { Master = MasterObject, CurrentTime = CurrentTime, StartTime = StartTime, Start = Start, Repeat = Repeat, Randomize = Randomize, Stop = Stop } )
if Status and (( Result == nil ) or ( Result and Result ~= false ) ) then
if Repeat ~= 0 and ( Stop == 0 ) or ( Stop ~= 0 and CurrentTime <= StartTime + Stop ) then
if Repeat ~= 0 and ( ( Stop == 0 ) or ( Stop ~= 0 and CurrentTime <= StartTime + Stop ) ) then
local ScheduleTime =
CurrentTime +
Repeat +
@@ -160,9 +165,9 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr
return nil
end
self:Start( Scheduler, self.CallID )
self:Start( Scheduler, CallID )
return self.CallID
return CallID
end
function SCHEDULEDISPATCHER:RemoveSchedule( Scheduler, CallID )
@@ -182,10 +187,11 @@ function SCHEDULEDISPATCHER:Start( Scheduler, CallID )
-- Only start when there is no ScheduleID defined!
-- This prevents to "Start" the scheduler twice with the same CallID...
if not Schedule[CallID].ScheduleID then
Schedule[CallID].StartTime = timer.getTime() -- Set the StartTime field to indicate when the scheduler started.
Schedule[CallID].ScheduleID = timer.scheduleFunction(
Schedule[CallID].CallHandler,
CallID,
timer.getTime() + Schedule[CallID].Start
timer.getTime() + Schedule[CallID].Start
)
end
else

View File

@@ -210,7 +210,8 @@ SCHEDULER = {
-- @return #SCHEDULER self.
-- @return #number The ScheduleID of the planned schedule.
function SCHEDULER:New( SchedulerObject, SchedulerFunction, SchedulerArguments, Start, Repeat, RandomizeFactor, Stop )
local self = BASE:Inherit( self, BASE:New() )
local self = BASE:Inherit( self, BASE:New() ) -- #SCHEDULER
self:F2( { Start, Repeat, RandomizeFactor, Stop } )
local ScheduleID = nil

File diff suppressed because it is too large Load Diff

View File

@@ -55,11 +55,15 @@ do -- SETTINGS
if PlayerName == nil then
local self = BASE:Inherit( self, BASE:New() ) -- #SETTINGS
self:SetMetric() -- Defaults
self:SetA2G_MGRS() -- Defaults
self:SetA2A_BRA() -- Defaults
self:SetLL_Accuracy( 2 ) -- Defaults
self:SetLL_DMS( true ) -- Defaults
self:SetA2G_BR() -- Defaults
self:SetA2A_BRAA() -- Defaults
self:SetLL_Accuracy( 3 ) -- Defaults
self:SetMGRS_Accuracy( 5 ) -- Defaults
self:SetMessageTime( MESSAGE.Type.Briefing, 180 )
self:SetMessageTime( MESSAGE.Type.Detailed, 60 )
self:SetMessageTime( MESSAGE.Type.Information, 30 )
self:SetMessageTime( MESSAGE.Type.Overview, 60 )
self:SetMessageTime( MESSAGE.Type.Update, 15 )
return self
else
local Settings = _DATABASE:GetPlayerSettings( PlayerName )
@@ -82,7 +86,6 @@ do -- SETTINGS
-- @param #SETTINGS self
-- @return #boolean true if metric.
function SETTINGS:IsMetric()
self:E( {Metric = ( self.Metric ~= nil and self.Metric == true ) or ( self.Metric == nil and _SETTINGS:IsMetric() ) } )
return ( self.Metric ~= nil and self.Metric == true ) or ( self.Metric == nil and _SETTINGS:IsMetric() )
end
@@ -96,7 +99,6 @@ do -- SETTINGS
-- @param #SETTINGS self
-- @return #boolean true if imperial.
function SETTINGS:IsImperial()
self:E( {Metric = ( self.Metric ~= nil and self.Metric == false ) or ( self.Metric == nil and _SETTINGS:IsMetric() ) } )
return ( self.Metric ~= nil and self.Metric == false ) or ( self.Metric == nil and _SETTINGS:IsMetric() )
end
@@ -111,25 +113,10 @@ do -- SETTINGS
--- Gets the SETTINGS LL accuracy.
-- @param #SETTINGS self
-- @return #number
function SETTINGS:GetLL_Accuracy()
return self.LL_Accuracy or _SETTINGS:GetLL_Accuracy()
function SETTINGS:GetLL_DDM_Accuracy()
return self.LL_DDM_Accuracy or _SETTINGS:GetLL_DDM_Accuracy()
end
--- Sets the SETTINGS LL DMS.
-- @param #SETTINGS self
-- @param #number LL_DMS
-- @return #SETTINGS
function SETTINGS:SetLL_DMS( LL_DMS )
self.LL_DMS = LL_DMS
end
--- Gets the SETTINGS LL DMS.
-- @param #SETTINGS self
-- @return #number
function SETTINGS:GetLL_DMS()
return self.LL_DMS or _SETTINGS:GetLL_DMS()
end
--- Sets the SETTINGS MGRS accuracy.
-- @param #SETTINGS self
-- @param #number MGRS_Accuracy
@@ -145,21 +132,50 @@ do -- SETTINGS
return self.MGRS_Accuracy or _SETTINGS:GetMGRS_Accuracy()
end
--- Sets A2G LL
--- Sets the SETTINGS Message Display Timing of a MessageType
-- @param #SETTINGS self
-- @return #SETTINGS
function SETTINGS:SetA2G_LL()
self.A2GSystem = "LL"
-- @param Core.Message#MESSAGE MessageType The type of the message.
-- @param #number MessageTime The display time duration in seconds of the MessageType.
function SETTINGS:SetMessageTime( MessageType, MessageTime )
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.
-- @return #number
function SETTINGS:GetMessageTime( MessageType )
return ( self.MessageTypeTimings and self.MessageTypeTimings[MessageType] ) or _SETTINGS:GetMessageTime( MessageType )
end
--- Is LL
--- Sets A2G LL DMS
-- @param #SETTINGS self
-- @return #boolean true if LL
function SETTINGS:IsA2G_LL()
return ( self.A2GSystem and self.A2GSystem == "LL" ) or ( not self.A2GSystem and _SETTINGS:IsA2G_LL() )
-- @return #SETTINGS
function SETTINGS:SetA2G_LL_DMS()
self.A2GSystem = "LL DMS"
end
--- Sets A2G LL DDM
-- @param #SETTINGS self
-- @return #SETTINGS
function SETTINGS:SetA2G_LL_DDM()
self.A2GSystem = "LL DDM"
end
--- Is LL DMS
-- @param #SETTINGS self
-- @return #boolean true if LL DMS
function SETTINGS:IsA2G_LL_DMS()
return ( self.A2GSystem and self.A2GSystem == "LL DMS" ) or ( not self.A2GSystem and _SETTINGS:IsA2G_LL_DMS() )
end
--- Is LL DDM
-- @param #SETTINGS self
-- @return #boolean true if LL DDM
function SETTINGS:IsA2G_LL_DDM()
return ( self.A2GSystem and self.A2GSystem == "LL DDM" ) or ( not self.A2GSystem and _SETTINGS:IsA2G_LL_DDM() )
end
--- Sets A2G MGRS
@@ -179,31 +195,30 @@ do -- SETTINGS
--- Sets A2G BRA
-- @param #SETTINGS self
-- @return #SETTINGS
function SETTINGS:SetA2G_BRA()
self.A2GSystem = "BRA"
function SETTINGS:SetA2G_BR()
self.A2GSystem = "BR"
end
--- Is BRA
-- @param #SETTINGS self
-- @return #boolean true if BRA
function SETTINGS:IsA2G_BRA()
self:E( { BRA = ( self.A2GSystem and self.A2GSystem == "BRA" ) or ( not self.A2GSystem and _SETTINGS:IsA2G_BRA() ) } )
return ( self.A2GSystem and self.A2GSystem == "BRA" ) or ( not self.A2GSystem and _SETTINGS:IsA2G_BRA() )
function SETTINGS:IsA2G_BR()
return ( self.A2GSystem and self.A2GSystem == "BR" ) or ( not self.A2GSystem and _SETTINGS:IsA2G_BR() )
end
--- Sets A2A BRA
-- @param #SETTINGS self
-- @return #SETTINGS
function SETTINGS:SetA2A_BRA()
self.A2ASystem = "BRA"
function SETTINGS:SetA2A_BRAA()
self.A2ASystem = "BRAA"
end
--- Is BRA
-- @param #SETTINGS self
-- @return #boolean true if BRA
function SETTINGS:IsA2A_BRA()
self:E( { BRA = ( self.A2ASystem and self.A2ASystem == "BRA" ) or ( not self.A2ASystem and _SETTINGS:IsA2A_BRA() ) } )
return ( self.A2ASystem and self.A2ASystem == "BRA" ) or ( not self.A2ASystem and _SETTINGS:IsA2A_BRA() )
function SETTINGS:IsA2A_BRAA()
self:E( { BRA = ( self.A2ASystem and self.A2ASystem == "BRAA" ) or ( not self.A2ASystem and _SETTINGS:IsA2A_BRAA() ) } )
return ( self.A2ASystem and self.A2ASystem == "BRAA" ) or ( not self.A2ASystem and _SETTINGS:IsA2A_BRAA() )
end
--- Sets A2A BULLS
@@ -220,69 +235,179 @@ do -- SETTINGS
return ( self.A2ASystem and self.A2ASystem == "BULLS" ) or ( not self.A2ASystem and _SETTINGS:IsA2A_BULLS() )
end
--- Sets A2A LL DMS
-- @param #SETTINGS self
-- @return #SETTINGS
function SETTINGS:SetA2A_LL_DMS()
self.A2ASystem = "LL DMS"
end
--- Sets A2A LL DDM
-- @param #SETTINGS self
-- @return #SETTINGS
function SETTINGS:SetA2A_LL_DDM()
self.A2ASystem = "LL DDM"
end
--- Is LL DMS
-- @param #SETTINGS self
-- @return #boolean true if LL DMS
function SETTINGS:IsA2A_LL_DMS()
return ( self.A2ASystem and self.A2ASystem == "LL DMS" ) or ( not self.A2ASystem and _SETTINGS:IsA2A_LL_DMS() )
end
--- Is LL DDM
-- @param #SETTINGS self
-- @return #boolean true if LL DDM
function SETTINGS:IsA2A_LL_DDM()
return ( self.A2ASystem and self.A2ASystem == "LL DDM" ) or ( not self.A2ASystem and _SETTINGS:IsA2A_LL_DDM() )
end
--- Sets A2A MGRS
-- @param #SETTINGS self
-- @return #SETTINGS
function SETTINGS:SetA2A_MGRS()
self.A2ASystem = "MGRS"
end
--- Is MGRS
-- @param #SETTINGS self
-- @return #boolean true if MGRS
function SETTINGS:IsA2A_MGRS()
return ( self.A2ASystem and self.A2ASystem == "MGRS" ) or ( not self.A2ASystem and _SETTINGS:IsA2A_MGRS() )
end
--- @param #SETTINGS self
-- @return #SETTINGS
function SETTINGS:SetSystemMenu( RootMenu, MenuText )
function SETTINGS:SetSystemMenu( MenuGroup, RootMenu )
MenuText = MenuText or "System Settings"
if not self.SettingsMenu then
self.SettingsMenu = MENU_MISSION:New( MenuText, RootMenu )
end
if self.DefaultMenu then
self.DefaultMenu:Remove()
self.DefaultMenu = nil
end
self.DefaultMenu = MENU_MISSION:New( "Default Settings", self.SettingsMenu )
local MenuText = "System Settings"
local A2GCoordinateMenu = MENU_MISSION:New( "A2G Coordinate System", self.DefaultMenu )
local MenuTime = timer.getTime()
local SettingsMenu = MENU_GROUP:New( MenuGroup, MenuText, RootMenu ):SetTime( MenuTime )
local A2GCoordinateMenu = MENU_GROUP:New( MenuGroup, "A2G Coordinate System", SettingsMenu ):SetTime( MenuTime )
if self:IsA2G_LL() then
MENU_MISSION_COMMAND:New( "Activate BRA", A2GCoordinateMenu, self.A2GMenuSystem, self, "BRA" )
MENU_MISSION_COMMAND:New( "Activate MGRS", A2GCoordinateMenu, self.A2GMenuSystem, self, "MGRS" )
MENU_MISSION_COMMAND:New( "LL Accuracy 1", A2GCoordinateMenu, self.MenuLL_Accuracy, self, 1 )
MENU_MISSION_COMMAND:New( "LL Accuracy 2", A2GCoordinateMenu, self.MenuLL_Accuracy, self, 2 )
MENU_MISSION_COMMAND:New( "LL Accuracy 3", A2GCoordinateMenu, self.MenuLL_Accuracy, self, 3 )
MENU_MISSION_COMMAND:New( "LL Decimal On", A2GCoordinateMenu, self.MenuLL_DMS, self, true )
MENU_MISSION_COMMAND:New( "LL Decimal Off", A2GCoordinateMenu, self.MenuLL_DMS, self, false )
if not self:IsA2G_LL_DMS() then
MENU_GROUP_COMMAND:New( MenuGroup, "Lat/Lon Degree Min Sec (LL DMS)", A2GCoordinateMenu, self.A2GMenuSystem, self, MenuGroup, RootMenu, "LL DMS" ):SetTime( MenuTime )
end
if not self:IsA2G_LL_DDM() then
MENU_GROUP_COMMAND:New( MenuGroup, "Lat/Lon Degree Dec Min (LL DDM)", A2GCoordinateMenu, self.A2GMenuSystem, self, MenuGroup, RootMenu, "LL DDM" ):SetTime( MenuTime )
end
if self:IsA2G_LL_DDM() then
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
if not self:IsA2G_BR() then
MENU_GROUP_COMMAND:New( MenuGroup, "Bearing, Range (BR)", A2GCoordinateMenu, self.A2GMenuSystem, self, MenuGroup, RootMenu, "BR" ):SetTime( MenuTime )
end
if not self:IsA2G_MGRS() then
MENU_GROUP_COMMAND:New( MenuGroup, "Military Grid (MGRS)", A2GCoordinateMenu, self.A2GMenuSystem, self, MenuGroup, RootMenu, "MGRS" ):SetTime( MenuTime )
end
if self:IsA2G_MGRS() then
MENU_MISSION_COMMAND:New( "Activate BRA", A2GCoordinateMenu, self.A2GMenuSystem, self, "BRA" )
MENU_MISSION_COMMAND:New( "Activate LL", A2GCoordinateMenu, self.A2GMenuSystem, self, "LL" )
MENU_MISSION_COMMAND:New( "MGRS Accuracy 1", A2GCoordinateMenu, self.MenuMGRS_Accuracy, self, 1 )
MENU_MISSION_COMMAND:New( "MGRS Accuracy 2", A2GCoordinateMenu, self.MenuMGRS_Accuracy, self, 2 )
MENU_MISSION_COMMAND:New( "MGRS Accuracy 3", A2GCoordinateMenu, self.MenuMGRS_Accuracy, self, 3 )
MENU_MISSION_COMMAND:New( "MGRS Accuracy 4", A2GCoordinateMenu, self.MenuMGRS_Accuracy, self, 4 )
MENU_MISSION_COMMAND:New( "MGRS Accuracy 5", A2GCoordinateMenu, self.MenuMGRS_Accuracy, self, 5 )
MENU_GROUP_COMMAND:New( MenuGroup, "MGRS Accuracy 1", A2GCoordinateMenu, self.MenuMGRS_Accuracy, self, MenuGroup, RootMenu, 1 ):SetTime( MenuTime )
MENU_GROUP_COMMAND:New( MenuGroup, "MGRS Accuracy 2", A2GCoordinateMenu, self.MenuMGRS_Accuracy, self, MenuGroup, RootMenu, 2 ):SetTime( MenuTime )
MENU_GROUP_COMMAND:New( MenuGroup, "MGRS Accuracy 3", A2GCoordinateMenu, self.MenuMGRS_Accuracy, self, MenuGroup, RootMenu, 3 ):SetTime( MenuTime )
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
if self:IsA2G_BRA() then
MENU_MISSION_COMMAND:New( "Activate MGRS", A2GCoordinateMenu, self.A2GMenuSystem, self, "MGRS" )
MENU_MISSION_COMMAND:New( "Activate LL", A2GCoordinateMenu, self.A2GMenuSystem, self, "LL" )
local A2ACoordinateMenu = MENU_GROUP:New( MenuGroup, "A2A Coordinate System", SettingsMenu ):SetTime( MenuTime )
if not self:IsA2A_LL_DMS() then
MENU_GROUP_COMMAND:New( MenuGroup, "Lat/Lon Degree Min Sec (LL DMS)", A2ACoordinateMenu, self.A2AMenuSystem, self, MenuGroup, RootMenu, "LL DMS" ):SetTime( MenuTime )
end
local A2ACoordinateMenu = MENU_MISSION:New( "A2A Coordinate System", self.DefaultMenu )
if self:IsA2A_BULLS() then
MENU_MISSION_COMMAND:New( "Activate BRA", A2ACoordinateMenu, self.A2AMenuSystem, self, "BRA" )
if not self:IsA2A_LL_DDM() then
MENU_GROUP_COMMAND:New( MenuGroup, "Lat/Lon Degree Dec Min (LL DDM)", A2ACoordinateMenu, self.A2AMenuSystem, self, MenuGroup, RootMenu, "LL DDM" ):SetTime( MenuTime )
end
if self:IsA2A_BRA() then
MENU_MISSION_COMMAND:New( "Activate BULLS", A2ACoordinateMenu, self.A2AMenuSystem, self, "BULLS" )
if self:IsA2A_LL_DDM() then
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
if not self:IsA2A_BULLS() then
MENU_GROUP_COMMAND:New( MenuGroup, "Bullseye (BULLS)", A2ACoordinateMenu, self.A2AMenuSystem, self, MenuGroup, RootMenu, "BULLS" ):SetTime( MenuTime )
end
local MetricsMenu = MENU_MISSION:New( "Measures and Weights System", self.DefaultMenu )
if not self:IsA2A_BRAA() then
MENU_GROUP_COMMAND:New( MenuGroup, "Bearing Range Altitude Aspect (BRAA)", A2ACoordinateMenu, self.A2AMenuSystem, self, MenuGroup, RootMenu, "BRAA" ):SetTime( MenuTime )
end
if not self:IsA2A_MGRS() then
MENU_GROUP_COMMAND:New( MenuGroup, "Military Grid (MGRS)", A2ACoordinateMenu, self.A2AMenuSystem, self, MenuGroup, RootMenu, "MGRS" ):SetTime( MenuTime )
end
if self:IsA2A_MGRS() then
MENU_GROUP_COMMAND:New( MenuGroup, "MGRS Accuracy 1", A2ACoordinateMenu, self.MenuMGRS_Accuracy, self, MenuGroup, RootMenu, 1 ):SetTime( MenuTime )
MENU_GROUP_COMMAND:New( MenuGroup, "MGRS Accuracy 2", A2ACoordinateMenu, self.MenuMGRS_Accuracy, self, MenuGroup, RootMenu, 2 ):SetTime( MenuTime )
MENU_GROUP_COMMAND:New( MenuGroup, "MGRS Accuracy 3", A2ACoordinateMenu, self.MenuMGRS_Accuracy, self, MenuGroup, RootMenu, 3 ):SetTime( MenuTime )
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 MetricsMenu = MENU_GROUP:New( MenuGroup, "Measures and Weights System", SettingsMenu ):SetTime( MenuTime )
if self:IsMetric() then
MENU_MISSION_COMMAND:New( "Activate Imperial", MetricsMenu, self.MenuMWSystem, self, false )
MENU_GROUP_COMMAND:New( MenuGroup, "Imperial (Miles,Feet)", MetricsMenu, self.MenuMWSystem, self, MenuGroup, RootMenu, false ):SetTime( MenuTime )
end
if self:IsImperial() then
MENU_MISSION_COMMAND:New( "Activate Metric", MetricsMenu, self.MenuMWSystem, self, true )
MENU_GROUP_COMMAND:New( MenuGroup, "Metric (Kilometers,Meters)", MetricsMenu, self.MenuMWSystem, self, MenuGroup, RootMenu, true ):SetTime( MenuTime )
end
local MessagesMenu = MENU_GROUP:New( MenuGroup, "Messages and Reports", SettingsMenu ):SetTime( MenuTime )
local UpdateMessagesMenu = MENU_GROUP:New( MenuGroup, "Update Messages", MessagesMenu ):SetTime( MenuTime )
MENU_GROUP_COMMAND:New( MenuGroup, "Off", UpdateMessagesMenu, self.MenuMessageTimingsSystem, self, MenuGroup, RootMenu, MESSAGE.Type.Update, 0 ):SetTime( MenuTime )
MENU_GROUP_COMMAND:New( MenuGroup, "5 seconds", UpdateMessagesMenu, self.MenuMessageTimingsSystem, self, MenuGroup, RootMenu, MESSAGE.Type.Update, 5 ):SetTime( MenuTime )
MENU_GROUP_COMMAND:New( MenuGroup, "10 seconds", UpdateMessagesMenu, self.MenuMessageTimingsSystem, self, MenuGroup, RootMenu, MESSAGE.Type.Update, 10 ):SetTime( MenuTime )
MENU_GROUP_COMMAND:New( MenuGroup, "15 seconds", UpdateMessagesMenu, self.MenuMessageTimingsSystem, self, MenuGroup, RootMenu, MESSAGE.Type.Update, 15 ):SetTime( MenuTime )
MENU_GROUP_COMMAND:New( MenuGroup, "30 seconds", UpdateMessagesMenu, self.MenuMessageTimingsSystem, self, MenuGroup, RootMenu, MESSAGE.Type.Update, 30 ):SetTime( MenuTime )
MENU_GROUP_COMMAND:New( MenuGroup, "1 minute", UpdateMessagesMenu, self.MenuMessageTimingsSystem, self, MenuGroup, RootMenu, MESSAGE.Type.Update, 60 ):SetTime( MenuTime )
local InformationMessagesMenu = MENU_GROUP:New( MenuGroup, "Information Messages", MessagesMenu ):SetTime( MenuTime )
MENU_GROUP_COMMAND:New( MenuGroup, "5 seconds", InformationMessagesMenu, self.MenuMessageTimingsSystem, self, MenuGroup, RootMenu, MESSAGE.Type.Information, 5 ):SetTime( MenuTime )
MENU_GROUP_COMMAND:New( MenuGroup, "10 seconds", InformationMessagesMenu, self.MenuMessageTimingsSystem, self, MenuGroup, RootMenu, MESSAGE.Type.Information, 10 ):SetTime( MenuTime )
MENU_GROUP_COMMAND:New( MenuGroup, "15 seconds", InformationMessagesMenu, self.MenuMessageTimingsSystem, self, MenuGroup, RootMenu, MESSAGE.Type.Information, 15 ):SetTime( MenuTime )
MENU_GROUP_COMMAND:New( MenuGroup, "30 seconds", InformationMessagesMenu, self.MenuMessageTimingsSystem, self, MenuGroup, RootMenu, MESSAGE.Type.Information, 30 ):SetTime( MenuTime )
MENU_GROUP_COMMAND:New( MenuGroup, "1 minute", InformationMessagesMenu, self.MenuMessageTimingsSystem, self, MenuGroup, RootMenu, MESSAGE.Type.Information, 60 ):SetTime( MenuTime )
MENU_GROUP_COMMAND:New( MenuGroup, "2 minutes", InformationMessagesMenu, self.MenuMessageTimingsSystem, self, MenuGroup, RootMenu, MESSAGE.Type.Information, 120 ):SetTime( MenuTime )
local BriefingReportsMenu = MENU_GROUP:New( MenuGroup, "Briefing Reports", MessagesMenu ):SetTime( MenuTime )
MENU_GROUP_COMMAND:New( MenuGroup, "15 seconds", BriefingReportsMenu, self.MenuMessageTimingsSystem, self, MenuGroup, RootMenu, MESSAGE.Type.Briefing, 15 ):SetTime( MenuTime )
MENU_GROUP_COMMAND:New( MenuGroup, "30 seconds", BriefingReportsMenu, self.MenuMessageTimingsSystem, self, MenuGroup, RootMenu, MESSAGE.Type.Briefing, 30 ):SetTime( MenuTime )
MENU_GROUP_COMMAND:New( MenuGroup, "1 minute", BriefingReportsMenu, self.MenuMessageTimingsSystem, self, MenuGroup, RootMenu, MESSAGE.Type.Briefing, 60 ):SetTime( MenuTime )
MENU_GROUP_COMMAND:New( MenuGroup, "2 minutes", BriefingReportsMenu, self.MenuMessageTimingsSystem, self, MenuGroup, RootMenu, MESSAGE.Type.Briefing, 120 ):SetTime( MenuTime )
MENU_GROUP_COMMAND:New( MenuGroup, "3 minutes", BriefingReportsMenu, self.MenuMessageTimingsSystem, self, MenuGroup, RootMenu, MESSAGE.Type.Briefing, 180 ):SetTime( MenuTime )
local OverviewReportsMenu = MENU_GROUP:New( MenuGroup, "Overview Reports", MessagesMenu ):SetTime( MenuTime )
MENU_GROUP_COMMAND:New( MenuGroup, "15 seconds", OverviewReportsMenu, self.MenuMessageTimingsSystem, self, MenuGroup, RootMenu, MESSAGE.Type.Overview, 15 ):SetTime( MenuTime )
MENU_GROUP_COMMAND:New( MenuGroup, "30 seconds", OverviewReportsMenu, self.MenuMessageTimingsSystem, self, MenuGroup, RootMenu, MESSAGE.Type.Overview, 30 ):SetTime( MenuTime )
MENU_GROUP_COMMAND:New( MenuGroup, "1 minute", OverviewReportsMenu, self.MenuMessageTimingsSystem, self, MenuGroup, RootMenu, MESSAGE.Type.Overview, 60 ):SetTime( MenuTime )
MENU_GROUP_COMMAND:New( MenuGroup, "2 minutes", OverviewReportsMenu, self.MenuMessageTimingsSystem, self, MenuGroup, RootMenu, MESSAGE.Type.Overview, 120 ):SetTime( MenuTime )
MENU_GROUP_COMMAND:New( MenuGroup, "3 minutes", OverviewReportsMenu, self.MenuMessageTimingsSystem, self, MenuGroup, RootMenu, MESSAGE.Type.Overview, 180 ):SetTime( MenuTime )
local DetailedReportsMenu = MENU_GROUP:New( MenuGroup, "Detailed Reports", MessagesMenu ):SetTime( MenuTime )
MENU_GROUP_COMMAND:New( MenuGroup, "15 seconds", DetailedReportsMenu, self.MenuMessageTimingsSystem, self, MenuGroup, RootMenu, MESSAGE.Type.DetailedReportsMenu, 15 ):SetTime( MenuTime )
MENU_GROUP_COMMAND:New( MenuGroup, "30 seconds", DetailedReportsMenu, self.MenuMessageTimingsSystem, self, MenuGroup, RootMenu, MESSAGE.Type.DetailedReportsMenu, 30 ):SetTime( MenuTime )
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
@@ -293,35 +418,39 @@ do -- SETTINGS
-- @return #SETTINGS
function SETTINGS:SetPlayerMenu( PlayerUnit )
local MenuText = "Player Settings"
self.MenuText = MenuText
local SettingsMenu = _SETTINGS.SettingsMenu
local PlayerGroup = PlayerUnit:GetGroup()
local PlayerName = PlayerUnit:GetPlayerName()
local PlayerNames = PlayerGroup:GetPlayerNames()
local GroupMenu = MENU_GROUP:New( PlayerGroup, MenuText, SettingsMenu )
local PlayerMenu = MENU_GROUP:New( PlayerGroup, 'Settings "' .. PlayerName .. '"', GroupMenu )
local PlayerMenu = MENU_GROUP:New( PlayerGroup, 'Settings "' .. PlayerName .. '"' )
self.PlayerMenu = PlayerMenu
local A2GCoordinateMenu = MENU_GROUP:New( PlayerGroup, "A2G Coordinate System", PlayerMenu )
if self:IsA2G_LL() then
MENU_GROUP_COMMAND:New( PlayerGroup, "Activate BRA", A2GCoordinateMenu, self.MenuGroupA2GSystem, self, PlayerUnit, PlayerGroup, PlayerName, "BRA" )
MENU_GROUP_COMMAND:New( PlayerGroup, "Activate MGRS", A2GCoordinateMenu, self.MenuGroupA2GSystem, self, PlayerUnit, PlayerGroup, PlayerName, "MGRS" )
MENU_GROUP_COMMAND:New( PlayerGroup, "LL Accuracy 1", A2GCoordinateMenu, self.MenuGroupLL_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 1 )
MENU_GROUP_COMMAND:New( PlayerGroup, "LL Accuracy 2", A2GCoordinateMenu, self.MenuGroupLL_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 2 )
MENU_GROUP_COMMAND:New( PlayerGroup, "LL Accuracy 3", A2GCoordinateMenu, self.MenuGroupLL_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 3 )
MENU_GROUP_COMMAND:New( PlayerGroup, "LL Decimal On", A2GCoordinateMenu, self.MenuGroupLL_DMSSystem, self, PlayerUnit, PlayerGroup, PlayerName, true )
MENU_GROUP_COMMAND:New( PlayerGroup, "LL Decimal Off", A2GCoordinateMenu, self.MenuGroupLL_DMSSystem, self, PlayerUnit, PlayerGroup, PlayerName, false )
if not self:IsA2G_LL_DMS() then
MENU_GROUP_COMMAND:New( PlayerGroup, "Lat/Lon Degree Min Sec (LL DMS)", A2GCoordinateMenu, self.MenuGroupA2GSystem, self, PlayerUnit, PlayerGroup, PlayerName, "LL DMS" )
end
if not self:IsA2G_LL_DDM() then
MENU_GROUP_COMMAND:New( PlayerGroup, "Lat/Lon Degree Dec Min (LL DDM)", A2GCoordinateMenu, self.MenuGroupA2GSystem, self, PlayerUnit, PlayerGroup, PlayerName, "LL DDM" )
end
if self:IsA2G_LL_DDM() then
MENU_GROUP_COMMAND:New( PlayerGroup, "LL DDM Accuracy 1", A2GCoordinateMenu, self.MenuGroupLL_DDM_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 1 )
MENU_GROUP_COMMAND:New( PlayerGroup, "LL DDM Accuracy 2", A2GCoordinateMenu, self.MenuGroupLL_DDM_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 2 )
MENU_GROUP_COMMAND:New( PlayerGroup, "LL DDM Accuracy 3", A2GCoordinateMenu, self.MenuGroupLL_DDM_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 3 )
end
if not self:IsA2G_BR() then
MENU_GROUP_COMMAND:New( PlayerGroup, "Bearing, Range (BR)", A2GCoordinateMenu, self.MenuGroupA2GSystem, self, PlayerUnit, PlayerGroup, PlayerName, "BR" )
end
if not self:IsA2G_MGRS() then
MENU_GROUP_COMMAND:New( PlayerGroup, "Military Grid (MGRS)", A2GCoordinateMenu, self.MenuGroupA2GSystem, self, PlayerUnit, PlayerGroup, PlayerName, "MGRS" )
end
if self:IsA2G_MGRS() then
MENU_GROUP_COMMAND:New( PlayerGroup, "Activate BRA", A2GCoordinateMenu, self.MenuGroupA2GSystem, self, PlayerUnit, PlayerGroup, PlayerName, "BRA" )
MENU_GROUP_COMMAND:New( PlayerGroup, "Activate LL", A2GCoordinateMenu, self.MenuGroupA2GSystem, self, PlayerUnit, PlayerGroup, PlayerName, "LL" )
MENU_GROUP_COMMAND:New( PlayerGroup, "MGRS Accuracy 1", A2GCoordinateMenu, self.MenuGroupMGRS_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 1 )
MENU_GROUP_COMMAND:New( PlayerGroup, "MGRS Accuracy 2", A2GCoordinateMenu, self.MenuGroupMGRS_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 2 )
MENU_GROUP_COMMAND:New( PlayerGroup, "MGRS Accuracy 3", A2GCoordinateMenu, self.MenuGroupMGRS_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 3 )
@@ -329,30 +458,93 @@ do -- SETTINGS
MENU_GROUP_COMMAND:New( PlayerGroup, "MGRS Accuracy 5", A2GCoordinateMenu, self.MenuGroupMGRS_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 5 )
end
if self:IsA2G_BRA() then
MENU_GROUP_COMMAND:New( PlayerGroup, "Activate MGRS", A2GCoordinateMenu, self.MenuGroupA2GSystem, self, PlayerUnit, PlayerGroup, PlayerName, "MGRS" )
MENU_GROUP_COMMAND:New( PlayerGroup, "Activate LL", A2GCoordinateMenu, self.MenuGroupA2GSystem, self, PlayerUnit, PlayerGroup, PlayerName, "LL" )
end
local A2ACoordinateMenu = MENU_GROUP:New( PlayerGroup, "A2A Coordinate System", PlayerMenu )
if self:IsA2A_BULLS() then
MENU_GROUP_COMMAND:New( PlayerGroup, "Activate BRA", A2ACoordinateMenu, self.MenuGroupA2ASystem, self, PlayerUnit, PlayerGroup, PlayerName, "BRA" )
if not self:IsA2A_LL_DMS() then
MENU_GROUP_COMMAND:New( PlayerGroup, "Lat/Lon Degree Min Sec (LL DMS)", A2GCoordinateMenu, self.MenuGroupA2GSystem, self, PlayerUnit, PlayerGroup, PlayerName, "LL DMS" )
end
if self:IsA2A_BRA() then
MENU_GROUP_COMMAND:New( PlayerGroup, "Activate BULLS", A2ACoordinateMenu, self.MenuGroupA2ASystem, self, PlayerUnit, PlayerGroup, PlayerName, "BULLS" )
if not self:IsA2A_LL_DDM() then
MENU_GROUP_COMMAND:New( PlayerGroup, "Lat/Lon Degree Dec Min (LL DDM)", A2GCoordinateMenu, self.MenuGroupA2GSystem, self, PlayerUnit, PlayerGroup, PlayerName, "LL DDM" )
end
if self:IsA2A_LL_DDM() then
MENU_GROUP_COMMAND:New( PlayerGroup, "LL DDM Accuracy 1", A2GCoordinateMenu, self.MenuGroupLL_DDM_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 1 )
MENU_GROUP_COMMAND:New( PlayerGroup, "LL DDM Accuracy 2", A2GCoordinateMenu, self.MenuGroupLL_DDM_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 2 )
MENU_GROUP_COMMAND:New( PlayerGroup, "LL DDM Accuracy 3", A2GCoordinateMenu, self.MenuGroupLL_DDM_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 3 )
end
if not self:IsA2A_BULLS() then
MENU_GROUP_COMMAND:New( PlayerGroup, "Bullseye (BULLS)", A2ACoordinateMenu, self.MenuGroupA2ASystem, self, PlayerUnit, PlayerGroup, PlayerName, "BULLS" )
end
if not self:IsA2A_BRAA() then
MENU_GROUP_COMMAND:New( PlayerGroup, "Bearing Range Altitude Aspect (BRAA)", A2ACoordinateMenu, self.MenuGroupA2ASystem, self, PlayerUnit, PlayerGroup, PlayerName, "BRAA" )
end
if not self:IsA2A_MGRS() then
MENU_GROUP_COMMAND:New( PlayerGroup, "Military Grid (MGRS)", A2ACoordinateMenu, self.MenuGroupA2ASystem, self, PlayerUnit, PlayerGroup, PlayerName, "MGRS" )
end
if self:IsA2A_MGRS() then
MENU_GROUP_COMMAND:New( PlayerGroup, "Military Grid (MGRS) Accuracy 1", A2ACoordinateMenu, self.MenuGroupMGRS_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 1 )
MENU_GROUP_COMMAND:New( PlayerGroup, "Military Grid (MGRS) Accuracy 2", A2ACoordinateMenu, self.MenuGroupMGRS_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 2 )
MENU_GROUP_COMMAND:New( PlayerGroup, "Military Grid (MGRS) Accuracy 3", A2ACoordinateMenu, self.MenuGroupMGRS_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 3 )
MENU_GROUP_COMMAND:New( PlayerGroup, "Military Grid (MGRS) Accuracy 4", A2ACoordinateMenu, self.MenuGroupMGRS_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 4 )
MENU_GROUP_COMMAND:New( PlayerGroup, "Military Grid (MGRS) Accuracy 5", A2ACoordinateMenu, self.MenuGroupMGRS_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 5 )
end
local MetricsMenu = MENU_GROUP:New( PlayerGroup, "Measures and Weights System", PlayerMenu )
if self:IsMetric() then
MENU_GROUP_COMMAND:New( PlayerGroup, "Activate Imperial", MetricsMenu, self.MenuGroupMWSystem, self, PlayerUnit, PlayerGroup, PlayerName, false )
MENU_GROUP_COMMAND:New( PlayerGroup, "Imperial (Miles,Feet)", MetricsMenu, self.MenuGroupMWSystem, self, PlayerUnit, PlayerGroup, PlayerName, false )
end
if self:IsImperial() then
MENU_GROUP_COMMAND:New( PlayerGroup, "Activate Metric", MetricsMenu, self.MenuGroupMWSystem, self, PlayerUnit, PlayerGroup, PlayerName, true )
MENU_GROUP_COMMAND:New( PlayerGroup, "Metric (Kilometers,Meters)", MetricsMenu, self.MenuGroupMWSystem, self, PlayerUnit, PlayerGroup, PlayerName, true )
end
local MessagesMenu = MENU_GROUP:New( PlayerGroup, "Messages and Reports", 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 )
MENU_GROUP_COMMAND:New( PlayerGroup, "10 seconds", UpdateMessagesMenu, self.MenuGroupMessageTimingsSystem, self, PlayerUnit, PlayerGroup, PlayerName, MESSAGE.Type.Update, 10 )
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 )
MENU_GROUP_COMMAND:New( PlayerGroup, "15 seconds", InformationMessagesMenu, self.MenuGroupMessageTimingsSystem, self, PlayerUnit, PlayerGroup, PlayerName, MESSAGE.Type.Information, 15 )
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 )
return self
end
@@ -372,40 +564,44 @@ do -- SETTINGS
--- @param #SETTINGS self
function SETTINGS:A2GMenuSystem( A2GSystem )
function SETTINGS:A2GMenuSystem( MenuGroup, RootMenu, A2GSystem )
self.A2GSystem = A2GSystem
self:SetSystemMenu()
MESSAGE:New( string.format("Settings: Default A2G coordinate system set to %s for all players!", A2GSystem ), 5 ):ToAll()
self:SetSystemMenu( MenuGroup, RootMenu )
end
--- @param #SETTINGS self
function SETTINGS:A2AMenuSystem( A2ASystem )
function SETTINGS:A2AMenuSystem( MenuGroup, RootMenu, A2ASystem )
self.A2ASystem = A2ASystem
self:SetSystemMenu()
MESSAGE:New( string.format("Settings: Default A2A coordinate system set to %s for all players!", A2ASystem ), 5 ):ToAll()
self:SetSystemMenu( MenuGroup, RootMenu )
end
--- @param #SETTINGS self
function SETTINGS:MenuLL_Accuracy( LL_Accuracy )
function SETTINGS:MenuLL_DDM_Accuracy( MenuGroup, RootMenu, LL_Accuracy )
self.LL_Accuracy = LL_Accuracy
self:SetSystemMenu()
MESSAGE:New( string.format("Settings: Default LL accuracy set to %s for all players!", LL_Accuracy ), 5 ):ToAll()
self:SetSystemMenu( MenuGroup, RootMenu )
end
--- @param #SETTINGS self
function SETTINGS:MenuLL_DMS( LL_DMS )
self.LL_DMS = LL_DMS
self:SetSystemMenu()
end
--- @param #SETTINGS self
function SETTINGS:MenuMGRS_Accuracy( MGRS_Accuracy )
function SETTINGS:MenuMGRS_Accuracy( MenuGroup, RootMenu, MGRS_Accuracy )
self.MGRS_Accuracy = MGRS_Accuracy
self:SetSystemMenu()
MESSAGE:New( string.format("Settings: Default MGRS accuracy set to %s for all players!", MGRS_Accuracy ), 5 ):ToAll()
self:SetSystemMenu( MenuGroup, RootMenu )
end
--- @param #SETTINGS self
function SETTINGS:MenuMWSystem( MW )
function SETTINGS:MenuMWSystem( MenuGroup, RootMenu, MW )
self.Metric = MW
MESSAGE:New( string.format("Settings: Default measurement format set to %s for all players!.", MW and "Metric" or "Imperial" ), 5 ):ToAll()
self:SetSystemMenu()
MESSAGE:New( string.format("Settings: Default measurement format set to %s for all players!", MW and "Metric" or "Imperial" ), 5 ):ToAll()
self:SetSystemMenu( MenuGroup, RootMenu )
end
--- @param #SETTINGS self
function SETTINGS:MenuMessageTimingsSystem( MenuGroup, RootMenu, MessageType, MessageTime )
self:SetMessageTime( MessageType, MessageTime )
MESSAGE:New( string.format( "Settings: Default message time set for %s to %d.", MessageType, MessageTime ), 5 ):ToAll()
end
do
@@ -413,7 +609,7 @@ do -- SETTINGS
function SETTINGS:MenuGroupA2GSystem( PlayerUnit, PlayerGroup, PlayerName, A2GSystem )
BASE:E( {self, PlayerUnit:GetName(), A2GSystem} )
self.A2GSystem = A2GSystem
MESSAGE:New( string.format("Settings: A2G format set to %s for player %s.", A2GSystem, PlayerName ), 5 ):ToGroup( PlayerGroup )
MESSAGE:New( string.format( "Settings: A2G format set to %s for player %s.", A2GSystem, PlayerName ), 5 ):ToGroup( PlayerGroup )
self:RemovePlayerMenu(PlayerUnit)
self:SetPlayerMenu(PlayerUnit)
end
@@ -421,42 +617,40 @@ do -- SETTINGS
--- @param #SETTINGS self
function SETTINGS:MenuGroupA2ASystem( PlayerUnit, PlayerGroup, PlayerName, A2ASystem )
self.A2ASystem = A2ASystem
MESSAGE:New( string.format("Settings: A2A format set to %s for player %s.", A2ASystem, PlayerName ), 5 ):ToGroup( PlayerGroup )
MESSAGE:New( string.format( "Settings: A2A format set to %s for player %s.", A2ASystem, PlayerName ), 5 ):ToGroup( PlayerGroup )
self:RemovePlayerMenu(PlayerUnit)
self:SetPlayerMenu(PlayerUnit)
end
--- @param #SETTINGS self
function SETTINGS:MenuGroupLL_AccuracySystem( PlayerUnit, PlayerGroup, PlayerName, LL_Accuracy )
function SETTINGS:MenuGroupLL_DDM_AccuracySystem( PlayerUnit, PlayerGroup, PlayerName, LL_Accuracy )
self.LL_Accuracy = LL_Accuracy
MESSAGE:New( string.format("Settings: A2G LL format accuracy set to %d for player %s.", LL_Accuracy, PlayerName ), 5 ):ToGroup( PlayerGroup )
MESSAGE:New( string.format( "Settings: A2G LL format accuracy set to %d for player %s.", LL_Accuracy, PlayerName ), 5 ):ToGroup( PlayerGroup )
self:RemovePlayerMenu(PlayerUnit)
self:SetPlayerMenu(PlayerUnit)
end
--- @param #SETTINGS self
function SETTINGS:MenuGroupLL_DMSSystem( PlayerUnit, PlayerGroup, PlayerName, LL_DMS )
self.LL_DMS = LL_DMS
MESSAGE:New( string.format("Settings: A2G LL format mode set to %s for player %s.", LL_DMS and "DMS" or "HMS", PlayerName ), 5 ):ToGroup( PlayerGroup )
self:RemovePlayerMenu(PlayerUnit)
self:SetPlayerMenu(PlayerUnit)
end
--- @param #SETTINGS self
function SETTINGS:MenuGroupMGRS_AccuracySystem( PlayerUnit, PlayerGroup, PlayerName, MGRS_Accuracy )
self.MGRS_Accuracy = MGRS_Accuracy
MESSAGE:New( string.format("Settings: A2G MGRS format accuracy set to %d for player %s.", MGRS_Accuracy, PlayerName ), 5 ):ToGroup( PlayerGroup )
MESSAGE:New( string.format( "Settings: A2G MGRS format accuracy set to %d for player %s.", MGRS_Accuracy, PlayerName ), 5 ):ToGroup( PlayerGroup )
self:RemovePlayerMenu(PlayerUnit)
self:SetPlayerMenu(PlayerUnit)
end
--- @param #SETTINGS self
function SETTINGS:MenuGroupMWSystem( PlayerUnit, PlayerGroup, PlayerName, MW )
self.Metrics = MW
MESSAGE:New( string.format("Settings: Measurement format set to %s for player %s.", MW and "Metric" or "Imperial", PlayerName ), 5 ):ToGroup( PlayerGroup )
self.Metric = MW
MESSAGE:New( string.format( "Settings: Measurement format set to %s for player %s.", MW and "Metric" or "Imperial", PlayerName ), 5 ):ToGroup( PlayerGroup )
self:RemovePlayerMenu(PlayerUnit)
self:SetPlayerMenu(PlayerUnit)
end
--- @param #SETTINGS self
function SETTINGS:MenuGroupMessageTimingsSystem( PlayerUnit, PlayerGroup, PlayerName, MessageType, MessageTime )
self:SetMessageTime( MessageType, MessageTime )
MESSAGE:New( string.format( "Settings: Default message time set for %s to %d.", MessageType, MessageTime ), 5 ):ToGroup( PlayerGroup )
end
end

View File

@@ -1,4 +1,4 @@
--- **Functional** -- Spawn dynamically new GROUPs in your missions.
--- **Core** -- SPAWN class dynamically spawns new groups of units in your missions.
--
-- ![Banner Image](..\Presentations\SPAWN\SPAWN.JPG)
--
@@ -55,8 +55,12 @@
--- # SPAWN class, extends @{Base#BASE}
--
-- -- ![Banner Image](..\Presentations\SPAWN\SPAWN.JPG)
--
-- ===
--
-- The SPAWN class allows to spawn dynamically new groups.
-- Each SPAWN object needs to be have a related **template group** setup in the Mission Editor (ME),
-- Each SPAWN object needs to be have related **template groups** setup in the Mission Editor (ME),
-- which is a normal group with the **Late Activation** flag set.
-- This template group will never be activated in your mission.
-- SPAWN uses that **template group** to reference to all the characteristics
@@ -200,6 +204,7 @@
-- * @{#SPAWN.SpawnFromStatic}(): Spawn a new group from a structure, taking the position of a @{Static}.
-- * @{#SPAWN.SpawnFromUnit}(): Spawn a new group taking the position of a @{Unit}.
-- * @{#SPAWN.SpawnInZone}(): Spawn a new group in a @{Zone}.
-- * @{#SPAWN.SpawnAtAirbase}(): Spawn a new group at an @{Airbase}, which can be an airdrome, ship or helipad.
--
-- Note that @{#SPAWN.Spawn} and @{#SPAWN.ReSpawn} return a @{GROUP#GROUP.New} object, that contains a reference to the DCSGroup object.
-- You can use the @{GROUP} object to do further actions with the DCSGroup.
@@ -270,8 +275,12 @@ SPAWN = {
-- @extends Wrapper.Group#GROUP.Takeoff
--- @field #SPAWN.Takeoff Takeoff
SPAWN.Takeoff = GROUP.Takeoff
SPAWN.Takeoff = {
Air = 1,
Runway = 2,
Hot = 3,
Cold = 4,
}
--- @type SPAWN.SpawnZoneTable
-- @list <Core.Zone#ZONE_BASE> SpawnZone
@@ -289,7 +298,7 @@ function SPAWN:New( SpawnTemplatePrefix )
local self = BASE:Inherit( self, BASE:New() ) -- #SPAWN
self:F( { SpawnTemplatePrefix } )
local TemplateGroup = Group.getByName( SpawnTemplatePrefix )
local TemplateGroup = GROUP:FindByName( SpawnTemplatePrefix )
if TemplateGroup then
self.SpawnTemplatePrefix = SpawnTemplatePrefix
self.SpawnIndex = 0
@@ -333,7 +342,7 @@ function SPAWN:NewWithAlias( SpawnTemplatePrefix, SpawnAliasPrefix )
local self = BASE:Inherit( self, BASE:New() )
self:F( { SpawnTemplatePrefix, SpawnAliasPrefix } )
local TemplateGroup = Group.getByName( SpawnTemplatePrefix )
local TemplateGroup = GROUP:FindByName( SpawnTemplatePrefix )
if TemplateGroup then
self.SpawnTemplatePrefix = SpawnTemplatePrefix
self.SpawnAliasPrefix = SpawnAliasPrefix
@@ -520,6 +529,73 @@ function SPAWN:InitRandomizeTemplate( SpawnTemplatePrefixTable )
return self
end
--- Randomize templates to be used as the unit representatives for the Spawned group, defined using a SET_GROUP object.
-- This method becomes useful when you need to spawn groups with random templates of groups defined within the mission editor,
-- but they will all follow the same Template route and have the same prefix name.
-- In other words, this method randomizes between a defined set of groups the template to be used for each new spawn of a group.
-- @param #SPAWN self
-- @param Core.Set#SET_GROUP SpawnTemplateSet A SET_GROUP object set, that contains the groups that are possible unit representatives of the group to be spawned.
-- @return #SPAWN
-- @usage
-- -- NATO Tank Platoons invading Gori.
--
-- -- Choose between different 'US Tank Platoon Template' configurations to be spawned for the
-- -- 'US Tank Platoon Left', 'US Tank Platoon Middle' and 'US Tank Platoon Right' SPAWN objects.
--
-- -- Each new SPAWN will randomize the route, with a defined time interval of 200 seconds with 40% time variation (randomization) and
-- -- with a limit set of maximum 12 Units alive simulteneously and 150 Groups to be spawned during the whole mission.
--
-- Spawn_US_PlatoonSet = SET_GROUP:New():FilterPrefixes( "US Tank Platoon Templates" ):FilterOnce()
--
-- --- Now use the Spawn_US_PlatoonSet to define the templates using InitRandomizeTemplateSet.
-- Spawn_US_Platoon_Left = SPAWN:New( 'US Tank Platoon Left' ):InitLimit( 12, 150 ):Schedule( 200, 0.4 ):InitRandomizeTemplateSet( Spawn_US_PlatoonSet ):InitRandomizeRoute( 3, 3, 2000 )
-- Spawn_US_Platoon_Middle = SPAWN:New( 'US Tank Platoon Middle' ):InitLimit( 12, 150 ):Schedule( 200, 0.4 ):InitRandomizeTemplateSet( Spawn_US_PlatoonSet ):InitRandomizeRoute( 3, 3, 2000 )
-- Spawn_US_Platoon_Right = SPAWN:New( 'US Tank Platoon Right' ):InitLimit( 12, 150 ):Schedule( 200, 0.4 ):InitRandomizeTemplateSet( Spawn_US_PlatoonSet ):InitRandomizeRoute( 3, 3, 2000 )
function SPAWN:InitRandomizeTemplateSet( SpawnTemplateSet ) -- R2.3
self:F( { self.SpawnTemplatePrefix } )
self.SpawnTemplatePrefixTable = SpawnTemplateSet:GetSetNames()
self.SpawnRandomizeTemplate = true
for SpawnGroupID = 1, self.SpawnMaxGroups do
self:_RandomizeTemplate( SpawnGroupID )
end
return self
end
--- Randomize templates to be used as the unit representatives for the Spawned group, defined by specifying the prefix names.
-- This method becomes useful when you need to spawn groups with random templates of groups defined within the mission editor,
-- but they will all follow the same Template route and have the same prefix name.
-- In other words, this method randomizes between a defined set of groups the template to be used for each new spawn of a group.
-- @param #SPAWN self
-- @param #string SpawnTemplatePrefixes A string or a list of string that contains the prefixes of the groups that are possible unit representatives of the group to be spawned.
-- @return #SPAWN
-- @usage
-- -- NATO Tank Platoons invading Gori.
--
-- -- Choose between different 'US Tank Platoon Templates' configurations to be spawned for the
-- -- 'US Tank Platoon Left', 'US Tank Platoon Middle' and 'US Tank Platoon Right' SPAWN objects.
--
-- -- Each new SPAWN will randomize the route, with a defined time interval of 200 seconds with 40% time variation (randomization) and
-- -- with a limit set of maximum 12 Units alive simulteneously and 150 Groups to be spawned during the whole mission.
--
-- Spawn_US_Platoon_Left = SPAWN:New( 'US Tank Platoon Left' ):InitLimit( 12, 150 ):Schedule( 200, 0.4 ):InitRandomizeTemplatePrefixes( "US Tank Platoon Templates" ):InitRandomizeRoute( 3, 3, 2000 )
-- Spawn_US_Platoon_Middle = SPAWN:New( 'US Tank Platoon Middle' ):InitLimit( 12, 150 ):Schedule( 200, 0.4 ):InitRandomizeTemplatePrefixes( "US Tank Platoon Templates" ):InitRandomizeRoute( 3, 3, 2000 )
-- Spawn_US_Platoon_Right = SPAWN:New( 'US Tank Platoon Right' ):InitLimit( 12, 150 ):Schedule( 200, 0.4 ):InitRandomizeTemplatePrefixes( "US Tank Platoon Templates" ):InitRandomizeRoute( 3, 3, 2000 )
function SPAWN:InitRandomizeTemplatePrefixes( SpawnTemplatePrefixes ) --R2.3
self:F( { self.SpawnTemplatePrefix } )
local SpawnTemplateSet = SET_GROUP:New():FilterPrefixes( SpawnTemplatePrefixes ):FilterOnce()
self:InitRandomizeTemplateSet( SpawnTemplateSet )
return self
end
--- When spawning a new group, make the grouping of the units according the InitGrouping setting.
-- @param #SPAWN self
-- @param #number Grouping Indicates the maximum amount of units in the group.
@@ -982,19 +1058,53 @@ function SPAWN:OnSpawnGroup( SpawnCallBackFunction, ... )
return self
end
--- Will spawn a group at an airbase.
--- Will spawn a group at an @{Airbase}.
-- This method is mostly advisable to be used if you want to simulate spawning units at an airbase.
-- Note that each point in the route assigned to the spawning group is reset to the point of the spawn.
-- You can use the returned group to further define the route to be followed.
--
-- The @{Airbase#AIRBASE} object must refer to a valid airbase known in the sim.
-- You can use the following enumerations to search for the pre-defined airbases on the current known maps of DCS:
--
-- * @{Airbase#AIRBASE.Caucasus}: The airbases on the Caucasus map.
-- * @{Airbase#AIRBASE.Nevada}: The airbases on the Nevada (NTTR) map.
-- * @{Airbase#AIRBASE.Normandy}: The airbases on the Normandy map.
--
-- Use the method @{Airbase#AIRBASE.FindByName}() to retrieve the airbase object.
-- The known AIRBASE objects are automatically imported at mission start by MOOSE.
-- Therefore, there isn't any New() constructor defined for AIRBASE objects.
--
-- Ships and Farps are added within the mission, and are therefore not known.
-- For these AIRBASE objects, there isn't an @{Airbase#AIRBASE} enumeration defined.
-- You need to provide the **exact name** of the airbase as the parameter to the @{Airbase#AIRBASE.FindByName}() method!
--
-- @param #SPAWN self
-- @param Wrapper.Airbase#AIRBASE Airbase The @{Airbase} where to spawn the group.
-- @param Wrapper.Airbase#AIRBASE SpawnAirbase The @{Airbase} where to spawn the group.
-- @param #SPAWN.Takeoff Takeoff (optional) The location and takeoff method. Default is Hot.
-- @param #number TakeoffAltitude (optional) The altitude above the ground.
-- @return Wrapper.Group#GROUP that was spawned.
-- @return #nil Nothing was spawned.
function SPAWN:SpawnAtAirbase( Airbase, Takeoff ) -- R2.2
self:F( { self.SpawnTemplatePrefix, Airbase } )
-- @usage
-- Spawn_Plane = SPAWN:New( "Plane" )
-- Spawn_Plane:SpawnAtAirbase( AIRBASE:FindByName( AIRBASE.Caucasus.Krymsk ), SPAWN.Takeoff.Cold )
-- Spawn_Plane:SpawnAtAirbase( AIRBASE:FindByName( AIRBASE.Caucasus.Krymsk ), SPAWN.Takeoff.Hot )
-- Spawn_Plane:SpawnAtAirbase( AIRBASE:FindByName( AIRBASE.Caucasus.Krymsk ), SPAWN.Takeoff.Runway )
--
-- Spawn_Plane:SpawnAtAirbase( AIRBASE:FindByName( "Carrier" ), SPAWN.Takeoff.Cold )
--
-- Spawn_Heli = SPAWN:New( "Heli")
--
-- Spawn_Heli:SpawnAtAirbase( AIRBASE:FindByName( "FARP Cold" ), SPAWN.Takeoff.Cold )
-- Spawn_Heli:SpawnAtAirbase( AIRBASE:FindByName( "FARP Hot" ), SPAWN.Takeoff.Hot )
-- Spawn_Heli:SpawnAtAirbase( AIRBASE:FindByName( "FARP Runway" ), SPAWN.Takeoff.Runway )
-- Spawn_Heli:SpawnAtAirbase( AIRBASE:FindByName( "FARP Air" ), SPAWN.Takeoff.Air )
--
-- Spawn_Heli:SpawnAtAirbase( AIRBASE:FindByName( "Carrier" ), SPAWN.Takeoff.Cold )
--
function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude ) -- R2.2
self:E( { self.SpawnTemplatePrefix, SpawnAirbase, Takeoff, TakeoffAltitude } )
local PointVec3 = Airbase:GetPointVec3()
local PointVec3 = SpawnAirbase:GetPointVec3()
self:T2(PointVec3)
Takeoff = Takeoff or SPAWN.Takeoff.Hot
@@ -1005,34 +1115,87 @@ function SPAWN:SpawnAtAirbase( Airbase, Takeoff ) -- R2.2
if SpawnTemplate then
self:T( { "Current point of ", self.SpawnTemplatePrefix, Airbase } )
self:T( { "Current point of ", self.SpawnTemplatePrefix, SpawnAirbase } )
local SpawnPoint = SpawnTemplate.route.points[1]
-- These are only for ships.
SpawnPoint.linkUnit = nil
SpawnPoint.helipadId = nil
SpawnPoint.airdromeId = nil
local AirbaseID = SpawnAirbase:GetID()
local AirbaseCategory = SpawnAirbase:GetDesc().category
self:F( { AirbaseCategory = AirbaseCategory } )
if AirbaseCategory == Airbase.Category.SHIP then
SpawnPoint.linkUnit = AirbaseID
SpawnPoint.helipadId = AirbaseID
elseif AirbaseCategory == Airbase.Category.HELIPAD then
SpawnPoint.linkUnit = AirbaseID
SpawnPoint.helipadId = AirbaseID
elseif AirbaseCategory == Airbase.Category.AIRDROME then
SpawnPoint.airdromeId = AirbaseID
end
SpawnPoint.alt = 0
SpawnPoint.type = GROUPTEMPLATE.Takeoff[Takeoff][1] -- type
SpawnPoint.action = GROUPTEMPLATE.Takeoff[Takeoff][2] -- action
-- Translate the position of the Group Template to the Vec3.
for UnitID = 1, #SpawnTemplate.units do
self:T( 'Before Translation SpawnTemplate.units['..UnitID..'].x = ' .. SpawnTemplate.units[UnitID].x .. ', SpawnTemplate.units['..UnitID..'].y = ' .. SpawnTemplate.units[UnitID].y )
-- These cause a lot of confusion.
local UnitTemplate = SpawnTemplate.units[UnitID]
UnitTemplate.parking = nil
UnitTemplate.parking_id = nil
UnitTemplate.alt = 0
local SX = UnitTemplate.x
local SY = UnitTemplate.y
local BX = SpawnTemplate.route.points[1].x
local BY = SpawnTemplate.route.points[1].y
local BX = SpawnPoint.x
local BY = SpawnPoint.y
local TX = PointVec3.x + ( SX - BX )
local TY = PointVec3.z + ( SY - BY )
SpawnTemplate.units[UnitID].x = TX
SpawnTemplate.units[UnitID].y = TY
SpawnTemplate.units[UnitID].alt = PointVec3.y
self:T( 'After Translation SpawnTemplate.units['..UnitID..'].x = ' .. SpawnTemplate.units[UnitID].x .. ', SpawnTemplate.units['..UnitID..'].y = ' .. SpawnTemplate.units[UnitID].y )
UnitTemplate.x = TX
UnitTemplate.y = TY
if Takeoff == GROUP.Takeoff.Air then
UnitTemplate.alt = PointVec3.y + ( TakeoffAltitude or 200 )
--else
-- UnitTemplate.alt = PointVec3.y + 10
end
self:T( 'After Translation SpawnTemplate.units['..UnitID..'].x = ' .. UnitTemplate.x .. ', SpawnTemplate.units['..UnitID..'].y = ' .. UnitTemplate.y )
end
SpawnTemplate.route.points[1].x = PointVec3.x
SpawnTemplate.route.points[1].y = PointVec3.z
SpawnTemplate.route.points[1].alt = Airbase.y
SpawnTemplate.route.points[1].type = GROUPTEMPLATE.Takeoff[Takeoff]
SpawnTemplate.route.points[1].airdromeId = Airbase:GetID()
SpawnPoint.x = PointVec3.x
SpawnPoint.y = PointVec3.z
if Takeoff == GROUP.Takeoff.Air then
SpawnPoint.alt = PointVec3.y + ( TakeoffAltitude or 200 )
--else
-- SpawnPoint.alt = PointVec3.y + 10
end
SpawnTemplate.x = PointVec3.x
SpawnTemplate.y = PointVec3.z
return self:SpawnWithIndex( self.SpawnIndex )
local GroupSpawned = self:SpawnWithIndex( self.SpawnIndex )
-- When spawned in the air, we need to generate a Takeoff Event
if Takeoff == GROUP.Takeoff.Air then
for UnitID, UnitSpawned in pairs( GroupSpawned:GetUnits() ) do
SCHEDULER:New( nil, BASE.CreateEventTakeoff, { timer.getTime(), UnitSpawned:GetDCSObject() } , 1 )
end
end
return GroupSpawned
end
end
@@ -1068,6 +1231,8 @@ function SPAWN:SpawnFromVec3( Vec3, SpawnIndex )
if SpawnTemplate then
self:T( { "Current point of ", self.SpawnTemplatePrefix, Vec3 } )
local TemplateHeight = SpawnTemplate.route.points[1].alt
-- Translate the position of the Group Template to the Vec3.
for UnitID = 1, #SpawnTemplate.units do
@@ -1081,16 +1246,20 @@ function SPAWN:SpawnFromVec3( Vec3, SpawnIndex )
local TY = Vec3.z + ( SY - BY )
SpawnTemplate.units[UnitID].x = TX
SpawnTemplate.units[UnitID].y = TY
SpawnTemplate.units[UnitID].alt = Vec3.y
if SpawnTemplate.CategoryID ~= Group.Category.SHIP then
SpawnTemplate.units[UnitID].alt = Vec3.y or TemplateHeight
end
self:T( 'After Translation SpawnTemplate.units['..UnitID..'].x = ' .. SpawnTemplate.units[UnitID].x .. ', SpawnTemplate.units['..UnitID..'].y = ' .. SpawnTemplate.units[UnitID].y )
end
SpawnTemplate.route.points[1].x = Vec3.x
SpawnTemplate.route.points[1].y = Vec3.z
SpawnTemplate.route.points[1].alt = Vec3.y
if SpawnTemplate.CategoryID ~= Group.Category.SHIP then
SpawnTemplate.route.points[1].alt = Vec3.y or TemplateHeight
end
SpawnTemplate.x = Vec3.x
SpawnTemplate.y = Vec3.z
SpawnTemplate.alt = Vec3.y or TemplateHeight
return self:SpawnWithIndex( self.SpawnIndex )
end
@@ -1105,14 +1274,31 @@ end
-- You can use the returned group to further define the route to be followed.
-- @param #SPAWN self
-- @param Dcs.DCSTypes#Vec2 Vec2 The Vec2 coordinates where to spawn the group.
-- @param #number MinHeight (optional) The minimum height to spawn an airborne group into the zone.
-- @param #number MaxHeight (optional) The maximum height to spawn an airborne group into the zone.
-- @param #number SpawnIndex (optional) The index which group to spawn within the given zone.
-- @return Wrapper.Group#GROUP that was spawned.
-- @return #nil Nothing was spawned.
function SPAWN:SpawnFromVec2( Vec2, SpawnIndex )
self:F( { self.SpawnTemplatePrefix, Vec2, SpawnIndex } )
-- @usage
--
-- local SpawnVec2 = ZONE:New( ZoneName ):GetVec2()
--
-- -- Spawn at the zone center position at the height specified in the ME of the group template!
-- SpawnAirplanes:SpawnFromVec2( SpawnVec2 )
--
-- -- Spawn from the static position at the height randomized between 2000 and 4000 meters.
-- SpawnAirplanes:SpawnFromVec2( SpawnVec2, 2000, 4000 )
--
function SPAWN:SpawnFromVec2( Vec2, MinHeight, MaxHeight, SpawnIndex )
self:F( { self.SpawnTemplatePrefix, self.SpawnIndex, Vec2, MinHeight, MaxHeight, SpawnIndex } )
local PointVec2 = POINT_VEC2:NewFromVec2( Vec2 )
return self:SpawnFromVec3( PointVec2:GetVec3(), SpawnIndex )
local Height = nil
if MinHeight and MaxHeight then
Height = math.random( MinHeight, MaxHeight)
end
return self:SpawnFromVec3( { x = Vec2.x, y = Height, z = Vec2.y }, SpawnIndex ) -- y can be nil. In this case, spawn on the ground for vehicles, and in the template altitude for air.
end
@@ -1121,14 +1307,26 @@ end
-- You can use the returned group to further define the route to be followed.
-- @param #SPAWN self
-- @param Wrapper.Unit#UNIT HostUnit The air or ground unit dropping or unloading the group.
-- @param #number MinHeight (optional) The minimum height to spawn an airborne group into the zone.
-- @param #number MaxHeight (optional) The maximum height to spawn an airborne group into the zone.
-- @param #number SpawnIndex (optional) The index which group to spawn within the given zone.
-- @return Wrapper.Group#GROUP that was spawned.
-- @return #nil Nothing was spawned.
function SPAWN:SpawnFromUnit( HostUnit, SpawnIndex )
self:F( { self.SpawnTemplatePrefix, HostUnit, SpawnIndex } )
-- @usage
--
-- local SpawnStatic = STATIC:FindByName( StaticName )
--
-- -- Spawn from the static position at the height specified in the ME of the group template!
-- SpawnAirplanes:SpawnFromUnit( SpawnStatic )
--
-- -- Spawn from the static position at the height randomized between 2000 and 4000 meters.
-- SpawnAirplanes:SpawnFromUnit( SpawnStatic, 2000, 4000 )
--
function SPAWN:SpawnFromUnit( HostUnit, MinHeight, MaxHeight, SpawnIndex )
self:F( { self.SpawnTemplatePrefix, HostUnit, MinHeight, MaxHeight, SpawnIndex } )
if HostUnit and HostUnit:IsAlive() ~= nil then -- and HostUnit:getUnit(1):inAir() == false then
return self:SpawnFromVec3( HostUnit:GetVec3(), SpawnIndex )
return self:SpawnFromVec2( HostUnit:GetVec2(), MinHeight, MaxHeight, SpawnIndex )
end
return nil
@@ -1138,14 +1336,26 @@ end
-- You can use the returned group to further define the route to be followed.
-- @param #SPAWN self
-- @param Wrapper.Static#STATIC HostStatic The static dropping or unloading the group.
-- @param #number MinHeight (optional) The minimum height to spawn an airborne group into the zone.
-- @param #number MaxHeight (optional) The maximum height to spawn an airborne group into the zone.
-- @param #number SpawnIndex (optional) The index which group to spawn within the given zone.
-- @return Wrapper.Group#GROUP that was spawned.
-- @return #nil Nothing was spawned.
function SPAWN:SpawnFromStatic( HostStatic, SpawnIndex )
self:F( { self.SpawnTemplatePrefix, HostStatic, SpawnIndex } )
-- @usage
--
-- local SpawnStatic = STATIC:FindByName( StaticName )
--
-- -- Spawn from the static position at the height specified in the ME of the group template!
-- SpawnAirplanes:SpawnFromStatic( SpawnStatic )
--
-- -- Spawn from the static position at the height randomized between 2000 and 4000 meters.
-- SpawnAirplanes:SpawnFromStatic( SpawnStatic, 2000, 4000 )
--
function SPAWN:SpawnFromStatic( HostStatic, MinHeight, MaxHeight, SpawnIndex )
self:F( { self.SpawnTemplatePrefix, HostStatic, MinHeight, MaxHeight, SpawnIndex } )
if HostStatic and HostStatic:IsAlive() then
return self:SpawnFromVec3( HostStatic:GetVec3(), SpawnIndex )
return self:SpawnFromVec2( HostStatic:GetVec2(), MinHeight, MaxHeight, SpawnIndex )
end
return nil
@@ -1158,17 +1368,38 @@ end
-- @param #SPAWN self
-- @param Core.Zone#ZONE Zone The zone where the group is to be spawned.
-- @param #boolean RandomizeGroup (optional) Randomization of the @{Group} position in the zone.
-- @param #number MinHeight (optional) The minimum height to spawn an airborne group into the zone.
-- @param #number MaxHeight (optional) The maximum height to spawn an airborne group into the zone.
-- @param #number SpawnIndex (optional) The index which group to spawn within the given zone.
-- @return Wrapper.Group#GROUP that was spawned.
-- @return #nil when nothing was spawned.
function SPAWN:SpawnInZone( Zone, RandomizeGroup, SpawnIndex )
self:F( { self.SpawnTemplatePrefix, Zone, RandomizeGroup, SpawnIndex } )
-- @usage
--
-- local SpawnZone = ZONE:New( ZoneName )
--
-- -- Spawn at the zone center position at the height specified in the ME of the group template!
-- SpawnAirplanes:SpawnInZone( SpawnZone )
--
-- -- Spawn in the zone at a random position at the height specified in the Me of the group template.
-- SpawnAirplanes:SpawnInZone( SpawnZone, true )
--
-- -- Spawn in the zone at a random position at the height randomized between 2000 and 4000 meters.
-- SpawnAirplanes:SpawnInZone( SpawnZone, true, 2000, 4000 )
--
-- -- Spawn at the zone center position at the height randomized between 2000 and 4000 meters.
-- SpawnAirplanes:SpawnInZone( SpawnZone, false, 2000, 4000 )
--
-- -- Spawn at the zone center position at the height randomized between 2000 and 4000 meters.
-- SpawnAirplanes:SpawnInZone( SpawnZone, nil, 2000, 4000 )
--
function SPAWN:SpawnInZone( Zone, RandomizeGroup, MinHeight, MaxHeight, SpawnIndex )
self:F( { self.SpawnTemplatePrefix, Zone, RandomizeGroup, MinHeight, MaxHeight, SpawnIndex } )
if Zone then
if RandomizeGroup then
return self:SpawnFromVec2( Zone:GetRandomVec2(), SpawnIndex )
return self:SpawnFromVec2( Zone:GetRandomVec2(), MinHeight, MaxHeight, SpawnIndex )
else
return self:SpawnFromVec2( Zone:GetVec2(), SpawnIndex )
return self:SpawnFromVec2( Zone:GetVec2(), MinHeight, MaxHeight, SpawnIndex )
end
end

View File

@@ -116,6 +116,33 @@ function SPAWNSTATIC:NewFromType( SpawnTypeName, SpawnShapeName, SpawnCategory,
return self
end
--- Creates a new @{Static} at the original position.
-- @param #SPAWNSTATIC self
-- @param #number Heading The heading of the static, which is a number in degrees from 0 to 360.
-- @param #string (optional) The name of the new static.
-- @return #SPAWNSTATIC
function SPAWNSTATIC:Spawn( Heading, NewName ) --R2.3
self:F( { Heading, NewName } )
local CountryName = _DATABASE.COUNTRY_NAME[self.CountryID]
local StaticTemplate = _DATABASE:GetStaticUnitTemplate( self.SpawnTemplatePrefix )
StaticTemplate.name = NewName or string.format("%s#%05d", self.SpawnTemplatePrefix, self.SpawnIndex )
StaticTemplate.heading = ( Heading / 180 ) * math.pi
StaticTemplate.CountryID = nil
StaticTemplate.CoalitionID = nil
StaticTemplate.CategoryID = nil
local Static = coalition.addStaticObject( self.CountryID, StaticTemplate )
self.SpawnIndex = self.SpawnIndex + 1
return Static
end
--- Creates a new @{Static} from a POINT_VEC2.
-- @param #SPAWNSTATIC self
@@ -130,8 +157,13 @@ function SPAWNSTATIC:SpawnFromPointVec2( PointVec2, Heading, NewName ) --R2.1
local StaticTemplate = _DATABASE:GetStaticUnitTemplate( self.SpawnTemplatePrefix )
StaticTemplate.x = PointVec2:GetLat()
StaticTemplate.y = PointVec2:GetLon()
StaticTemplate.x = PointVec2.x
StaticTemplate.y = PointVec2.z
StaticTemplate.units = nil
StaticTemplate.route = nil
StaticTemplate.groupId = nil
StaticTemplate.name = NewName or string.format("%s#%05d", self.SpawnTemplatePrefix, self.SpawnIndex )
StaticTemplate.heading = ( Heading / 180 ) * math.pi

View File

@@ -222,7 +222,7 @@ do
self:HandleEvent( EVENTS.Dead )
self:__Lasing( -0.2 )
self:__Lasing( -1 )
end
--- @param #SPOT self

View File

@@ -0,0 +1,95 @@
--- **Core (WIP)** -- Manage user flags.
--
-- ====
--
-- Management of DCS User Flags.
--
-- ====
--
-- ### Author: **Sven Van de Velde (FlightControl)**
--
-- ====
--
-- @module UserFlag
do -- UserFlag
--- @type USERFLAG
-- @extends Core.Base#BASE
--- # USERFLAG class, extends @{Base#BASE}
--
-- Management of DCS User Flags.
--
-- ## 1. USERFLAG constructor
--
-- * @{#USERFLAG.New}(): Creates a new USERFLAG object.
--
-- @field #USERFLAG
USERFLAG = {
ClassName = "USERFLAG",
}
--- USERFLAG Constructor.
-- @param #USERFLAG self
-- @param #string UserFlagName The name of the userflag, which is a free text string.
-- @return #USERFLAG
function USERFLAG:New( UserFlagName ) --R2.3
local self = BASE:Inherit( self, BASE:New() ) -- #USERFLAG
self.UserFlagName = UserFlagName
return self
end
--- Set the userflag to a given Number.
-- @param #USERFLAG self
-- @param #number Number The number value to be checked if it is the same as the userflag.
-- @return #USERFLAG The userflag instance.
-- @usage
-- local BlueVictory = USERFLAG:New( "VictoryBlue" )
-- BlueVictory:Set( 100 ) -- Set the UserFlag VictoryBlue to 100.
--
function USERFLAG:Set( Number ) --R2.3
self:F( { Number = Number } )
trigger.action.setUserFlag( self.UserFlagName, Number )
return self
end
--- Get the userflag Number.
-- @param #USERFLAG self
-- @return #number Number The number value to be checked if it is the same as the userflag.
-- @usage
-- local BlueVictory = USERFLAG:New( "VictoryBlue" )
-- local BlueVictoryValue = BlueVictory:Get() -- Get the UserFlag VictoryBlue value.
--
function USERFLAG:Get( Number ) --R2.3
return trigger.misc.getUserFlag( self.UserFlagName )
end
--- Check if the userflag has a value of Number.
-- @param #USERFLAG self
-- @param #number Number The number value to be checked if it is the same as the userflag.
-- @return #boolean true if the Number is the value of the userflag.
-- @usage
-- local BlueVictory = USERFLAG:New( "VictoryBlue" )
-- if BlueVictory:Is( 1 ) then
-- return "Blue has won"
-- end
function USERFLAG:Is( Number ) --R2.3
return trigger.misc.getUserFlag( self.UserFlagName ) == Number
end
end

View File

@@ -0,0 +1,129 @@
--- **Core (WIP)** -- Manage user sound.
--
-- ====
--
-- Management of DCS User Sound.
--
-- ====
--
-- ### Author: **Sven Van de Velde (FlightControl)**
--
-- ====
--
-- @module UserSound
do -- UserSound
--- @type USERSOUND
-- @extends Core.Base#BASE
--- # USERSOUND class, extends @{Base#BASE}
--
-- Management of DCS User Sound.
--
-- ## 1. USERSOUND constructor
--
-- * @{#USERSOUND.New}(): Creates a new USERSOUND object.
--
-- @field #USERSOUND
USERSOUND = {
ClassName = "USERSOUND",
}
--- USERSOUND Constructor.
-- @param #USERSOUND self
-- @param #string UserSoundFileName The filename of the usersound.
-- @return #USERSOUND
function USERSOUND:New( UserSoundFileName ) --R2.3
local self = BASE:Inherit( self, BASE:New() ) -- #USERSOUND
self.UserSoundFileName = UserSoundFileName
return self
end
--- Set usersound filename.
-- @param #USERSOUND self
-- @param #string UserSoundFileName The filename of the usersound.
-- @return #USERSOUND The usersound instance.
-- @usage
-- local BlueVictory = USERSOUND:New( "BlueVictory.ogg" )
-- BlueVictory:SetFileName( "BlueVictoryLoud.ogg" ) -- Set the BlueVictory to change the file name to play a louder sound.
--
function USERSOUND:SetFileName( UserSoundFileName ) --R2.3
self.UserSoundFileName = UserSoundFileName
return self
end
--- Play the usersound to all players.
-- @param #USERSOUND self
-- @return #USERSOUND The usersound instance.
-- @usage
-- local BlueVictory = USERSOUND:New( "BlueVictory.ogg" )
-- BlueVictory:ToAll() -- Play the sound that Blue has won.
--
function USERSOUND:ToAll() --R2.3
trigger.action.outSound( self.UserSoundFileName )
return self
end
--- Play the usersound to the given coalition.
-- @param #USERSOUND self
-- @param Dcs.DCScoalition#coalition Coalition The coalition to play the usersound to.
-- @return #USERSOUND The usersound instance.
-- @usage
-- local BlueVictory = USERSOUND:New( "BlueVictory.ogg" )
-- BlueVictory:ToCoalition( coalition.side.BLUE ) -- Play the sound that Blue has won to the blue coalition.
--
function USERSOUND:ToCoalition( Coalition ) --R2.3
trigger.action.outSoundForCoalition(Coalition, self.UserSoundFileName )
return self
end
--- Play the usersound to the given country.
-- @param #USERSOUND self
-- @param Dcs.DCScountry#country Country The country to play the usersound to.
-- @return #USERSOUND The usersound instance.
-- @usage
-- local BlueVictory = USERSOUND:New( "BlueVictory.ogg" )
-- BlueVictory:ToCountry( country.id.USA ) -- Play the sound that Blue has won to the USA country.
--
function USERSOUND:ToCountry( Country ) --R2.3
trigger.action.outSoundForCountry( Country, self.UserSoundFileName )
return self
end
--- Play the usersound to the given @{Group}.
-- @param #USERSOUND self
-- @param Wrapper.Group#GROUP Group The @{Group} to play the usersound to.
-- @return #USERSOUND The usersound instance.
-- @usage
-- local BlueVictory = USERSOUND:New( "BlueVictory.ogg" )
-- local PlayerGroup = GROUP:FindByName( "PlayerGroup" ) -- Search for the active group named "PlayerGroup", that contains a human player.
-- BlueVictory:ToGroup( PlayerGroup ) -- Play the sound that Blue has won to the player group.
--
function USERSOUND:ToGroup( Group ) --R2.3
trigger.action.outSoundForGroup( Group:GetID(), self.UserSoundFileName )
return self
end
end

View File

@@ -0,0 +1,184 @@
--- **Core** -- VELOCITY models a speed, which can be expressed in various formats according the Settings.
--
-- ===
--
-- ### Author: **Sven Van de Velde (FlightControl)**
-- ### Contributions:
--
-- ====
--
-- @module Velocity
do -- Velocity
--- @type VELOCITY
-- @extends Core.Base#BASE
--- # VELOCITY class, extends @{Base#BASE}
--
-- VELOCITY models a speed, which can be expressed in various formats according the Settings.
--
-- ## 1. VELOCITY constructor
--
-- * @{#VELOCITY.New}(): Creates a new VELOCITY object.
--
-- @field #VELOCITY
VELOCITY = {
ClassName = "VELOCITY",
}
--- VELOCITY Constructor.
-- @param #VELOCITY self
-- @param #number VelocityMps The velocity in meters per second.
-- @return #VELOCITY
function VELOCITY:New( VelocityMps )
local self = BASE:Inherit( self, BASE:New() ) -- #VELOCITY
self:F( {} )
self.Velocity = VelocityMps
return self
end
--- Set the velocity in Mps (meters per second).
-- @param #VELOCITY self
-- @param #number VelocityMps The velocity in meters per second.
-- @return #VELOCITY
function VELOCITY:Set( VelocityMps )
self.Velocity = VelocityMps
return self
end
--- Get the velocity in Mps (meters per second).
-- @param #VELOCITY self
-- @return #number The velocity in meters per second.
function VELOCITY:Get()
return self.Velocity
end
--- Set the velocity in Kmph (kilometers per hour).
-- @param #VELOCITY self
-- @param #number VelocityKmph The velocity in kilometers per hour.
-- @return #VELOCITY
function VELOCITY:SetKmph( VelocityKmph )
self.Velocity = UTILS.KmphToMps( VelocityKmph )
return self
end
--- Get the velocity in Kmph (kilometers per hour).
-- @param #VELOCITY self
-- @return #number The velocity in kilometers per hour.
function VELOCITY:GetKmph()
return UTILS.MpsToKmph( self.Velocity )
end
--- Set the velocity in Miph (miles per hour).
-- @param #VELOCITY self
-- @param #number VelocityMiph The velocity in miles per hour.
-- @return #VELOCITY
function VELOCITY:SetMiph( VelocityMiph )
self.Velocity = UTILS.MiphToMps( VelocityMiph )
return self
end
--- Get the velocity in Miph (miles per hour).
-- @param #VELOCITY self
-- @return #number The velocity in miles per hour.
function VELOCITY:GetMiph()
return UTILS.MpsToMiph( self.Velocity )
end
--- Get the velocity in text, according the player @{Settings}.
-- @param #VELOCITY self
-- @param Core.Settings#SETTINGS Settings
-- @return #string The velocity in text.
function VELOCITY:GetText( Settings )
local Settings = Settings or _SETTINGS
if self.Velocity ~= 0 then
if Settings:IsMetric() then
return string.format( "%d km/h", UTILS.MpsToKmph( self.Velocity ) )
else
return string.format( "%d mi/h", UTILS.MpsToMiph( self.Velocity ) )
end
else
return "stationary"
end
end
--- Get the velocity in text, according the player or default @{Settings}.
-- @param #VELOCITY self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable
-- @param Core.Settings#SETTINGS Settings
-- @return #string The velocity in text according the player or default @{Settings}
function VELOCITY:ToString( VelocityGroup, Settings ) -- R2.3
self:F( { Group = VelocityGroup and VelocityGroup:GetName() } )
local Settings = Settings or ( VelocityGroup and _DATABASE:GetPlayerSettings( VelocityGroup:GetPlayerName() ) ) or _SETTINGS
return self:GetText( Settings )
end
end
do -- VELOCITY_POSITIONABLE
--- @type VELOCITY_POSITIONABLE
-- @extends Core.Base#BASE
--- # VELOCITY_POSITIONABLE class, extends @{Base#BASE}
--
-- VELOCITY_POSITIONABLE monitors the speed of an @{Positionable} in the simulation, which can be expressed in various formats according the Settings.
--
-- ## 1. VELOCITY_POSITIONABLE constructor
--
-- * @{#VELOCITY_POSITIONABLE.New}(): Creates a new VELOCITY_POSITIONABLE object.
--
-- @field #VELOCITY_POSITIONABLE
VELOCITY_POSITIONABLE = {
ClassName = "VELOCITY_POSITIONABLE",
}
--- VELOCITY_POSITIONABLE Constructor.
-- @param #VELOCITY_POSITIONABLE self
-- @param Wrapper.Positionable#POSITIONABLE Positionable The Positionable to monitor the speed.
-- @return #VELOCITY_POSITIONABLE
function VELOCITY_POSITIONABLE:New( Positionable )
local self = BASE:Inherit( self, VELOCITY:New() ) -- #VELOCITY_POSITIONABLE
self:F( {} )
self.Positionable = Positionable
return self
end
--- Get the velocity in Mps (meters per second).
-- @param #VELOCITY_POSITIONABLE self
-- @return #number The velocity in meters per second.
function VELOCITY_POSITIONABLE:Get()
return self.Positionable:GetVelocityMPS() or 0
end
--- Get the velocity in Kmph (kilometers per hour).
-- @param #VELOCITY_POSITIONABLE self
-- @return #number The velocity in kilometers per hour.
function VELOCITY_POSITIONABLE:GetKmph()
return UTILS.MpsToKmph( self.Positionable:GetVelocityMPS() or 0)
end
--- Get the velocity in Miph (miles per hour).
-- @param #VELOCITY_POSITIONABLE self
-- @return #number The velocity in miles per hour.
function VELOCITY_POSITIONABLE:GetMiph()
return UTILS.MpsToMiph( self.Positionable:GetVelocityMPS() or 0 )
end
--- Get the velocity in text, according the player or default @{Settings}.
-- @param #VELOCITY_POSITIONABLE self
-- @return #string The velocity in text according the player or default @{Settings}
function VELOCITY_POSITIONABLE:ToString() -- R2.3
self:F( { Group = self.Positionable and self.Positionable:GetName() } )
local Settings = Settings or ( self.Positionable and _DATABASE:GetPlayerSettings( self.Positionable:GetPlayerName() ) ) or _SETTINGS
self.Velocity = self.Positionable:GetVelocityMPS()
return self:GetText( Settings )
end
end

View File

@@ -50,6 +50,8 @@
-- ## Each zone has a name:
--
-- * @{#ZONE_BASE.GetName}(): Returns the name of the zone.
-- * @{#ZONE_BASE.SetName}(): Sets the name of the zone.
--
--
-- ## Each zone implements two polymorphic functions defined in @{Zone#ZONE_BASE}:
--
@@ -121,6 +123,17 @@ function ZONE_BASE:GetName()
return self.ZoneName
end
--- Sets the name of the zone.
-- @param #ZONE_BASE self
-- @param #string ZoneName The name of the zone.
-- @return #ZONE_BASE
function ZONE_BASE:SetName( ZoneName )
self:F2()
self.ZoneName = ZoneName
end
--- Returns if a Vec2 is within the zone.
-- @param #ZONE_BASE self
-- @param Dcs.DCSTypes#Vec2 Vec2 The Vec2 to test.
@@ -445,12 +458,17 @@ end
-- @param #ZONE_RADIUS self
-- @param Utilities.Utils#SMOKECOLOR SmokeColor The smoke color.
-- @param #number Points (optional) The amount of points in the circle.
-- @param #number AddHeight (optional) The height to be added for the smoke.
-- @param #number AddOffSet (optional) The angle to be added for the smoking start position.
-- @return #ZONE_RADIUS self
function ZONE_RADIUS:SmokeZone( SmokeColor, Points )
function ZONE_RADIUS:SmokeZone( SmokeColor, Points, AddHeight, AngleOffset )
self:F2( SmokeColor )
local Point = {}
local Vec2 = self:GetVec2()
AddHeight = AddHeight or 0
AngleOffset = AngleOffset or 0
Points = Points and Points or 360
@@ -458,10 +476,10 @@ function ZONE_RADIUS:SmokeZone( SmokeColor, Points )
local RadialBase = math.pi*2
for Angle = 0, 360, 360 / Points do
local Radial = Angle * RadialBase / 360
local Radial = ( Angle + AngleOffset ) * RadialBase / 360
Point.x = Vec2.x + math.cos( Radial ) * self:GetRadius()
Point.y = Vec2.y + math.sin( Radial ) * self:GetRadius()
POINT_VEC2:New( Point.x, Point.y ):Smoke( SmokeColor )
POINT_VEC2:New( Point.x, Point.y, AddHeight ):Smoke( SmokeColor )
end
return self
@@ -473,13 +491,16 @@ end
-- @param Utilities.Utils#FLARECOLOR FlareColor The flare color.
-- @param #number Points (optional) The amount of points in the circle.
-- @param Dcs.DCSTypes#Azimuth Azimuth (optional) Azimuth The azimuth of the flare.
-- @param #number AddHeight (optional) The height to be added for the smoke.
-- @return #ZONE_RADIUS self
function ZONE_RADIUS:FlareZone( FlareColor, Points, Azimuth )
function ZONE_RADIUS:FlareZone( FlareColor, Points, Azimuth, AddHeight )
self:F2( { FlareColor, Azimuth } )
local Point = {}
local Vec2 = self:GetVec2()
AddHeight = AddHeight or 0
Points = Points and Points or 360
local Angle
@@ -489,7 +510,7 @@ function ZONE_RADIUS:FlareZone( FlareColor, Points, Azimuth )
local Radial = Angle * RadialBase / 360
Point.x = Vec2.x + math.cos( Radial ) * self:GetRadius()
Point.y = Vec2.y + math.sin( Radial ) * self:GetRadius()
POINT_VEC2:New( Point.x, Point.y ):Flare( FlareColor, Azimuth )
POINT_VEC2:New( Point.x, Point.y, AddHeight ):Flare( FlareColor, Azimuth )
end
return self
@@ -562,6 +583,189 @@ function ZONE_RADIUS:GetVec3( Height )
end
--- Scan the zone
-- @param #ZONE_RADIUS self
-- @param ObjectCategories
-- @param Coalition
function ZONE_RADIUS:Scan( ObjectCategories )
self.ScanData = {}
self.ScanData.Coalitions = {}
self.ScanData.Scenery = {}
local ZoneCoord = self:GetCoordinate()
local ZoneRadius = self:GetRadius()
self:E({ZoneCoord = ZoneCoord, ZoneRadius = ZoneRadius, ZoneCoordLL = ZoneCoord:ToStringLLDMS()})
local SphereSearch = {
id = world.VolumeType.SPHERE,
params = {
point = ZoneCoord:GetVec3(),
radius = ZoneRadius,
}
}
local function EvaluateZone( ZoneObject )
if ZoneObject:isExist() then
local ObjectCategory = ZoneObject:getCategory()
if ( ObjectCategory == Object.Category.UNIT and ZoneObject:isActive() ) or
ObjectCategory == Object.Category.STATIC then
local CoalitionDCSUnit = ZoneObject:getCoalition()
self.ScanData.Coalitions[CoalitionDCSUnit] = true
self:E( { Name = ZoneObject:getName(), Coalition = CoalitionDCSUnit } )
end
if ObjectCategory == Object.Category.SCENERY then
local SceneryType = ZoneObject:getTypeName()
local SceneryName = ZoneObject:getName()
self.ScanData.Scenery[SceneryType] = self.ScanData.Scenery[SceneryType] or {}
self.ScanData.Scenery[SceneryType][SceneryName] = SCENERY:Register( SceneryName, ZoneObject )
self:E( { SCENERY = self.ScanData.Scenery[SceneryType][SceneryName] } )
end
end
return true
end
world.searchObjects( ObjectCategories, SphereSearch, EvaluateZone )
end
function ZONE_RADIUS:CountScannedCoalitions()
local Count = 0
for CoalitionID, Coalition in pairs( self.ScanData.Coalitions ) do
Count = Count + 1
end
return Count
end
--- Get Coalitions of the units in the Zone, or Check if there are units of the given Coalition in the Zone.
-- Returns nil if there are none ot two Coalitions in the zone!
-- Returns one Coalition if there are only Units of one Coalition in the Zone.
-- Returns the Coalition for the given Coalition if there are units of the Coalition in the Zone
-- @param #ZONE_RADIUS self
-- @return #table
function ZONE_RADIUS:GetScannedCoalition( Coalition )
if Coalition then
return self.ScanData.Coalitions[Coalition]
else
local Count = 0
local ReturnCoalition = nil
for CoalitionID, Coalition in pairs( self.ScanData.Coalitions ) do
Count = Count + 1
ReturnCoalition = CoalitionID
end
if Count ~= 1 then
ReturnCoalition = nil
end
return ReturnCoalition
end
end
function ZONE_RADIUS:GetScannedSceneryType( SceneryType )
return self.ScanData.Scenery[SceneryType]
end
function ZONE_RADIUS:GetScannedScenery()
return self.ScanData.Scenery
end
--- Is All in Zone of Coalition?
-- @param #ZONE_RADIUS self
-- @param Coalition
-- @return #boolean
function ZONE_RADIUS:IsAllInZoneOfCoalition( Coalition )
return self:CountScannedCoalitions() == 1 and self:GetScannedCoalition( Coalition ) == true
end
--- Is All in Zone of Other Coalition?
-- @param #ZONE_RADIUS self
-- @param Coalition
-- @return #boolean
function ZONE_RADIUS:IsAllInZoneOfOtherCoalition( Coalition )
self:E( { Coalitions = self.Coalitions, Count = self:CountScannedCoalitions() } )
return self:CountScannedCoalitions() == 1 and self:GetScannedCoalition( Coalition ) == nil
end
--- Is Some in Zone of Coalition?
-- @param #ZONE_RADIUS self
-- @param Coalition
-- @return #boolean
function ZONE_RADIUS:IsSomeInZoneOfCoalition( Coalition )
return self:CountScannedCoalitions() > 1 and self:GetScannedCoalition( Coalition ) == true
end
--- Is None in Zone of Coalition?
-- @param #ZONE_RADIUS self
-- @param Coalition
-- @return #boolean
function ZONE_RADIUS:IsNoneInZoneOfCoalition( Coalition )
return self:GetScannedCoalition( Coalition ) == nil
end
--- Is None in Zone?
-- @param #ZONE_RADIUS self
-- @return #boolean
function ZONE_RADIUS:IsNoneInZone()
return self:CountScannedCoalitions() == 0
end
--- Searches the zone
-- @param #ZONE_RADIUS self
-- @param ObjectCategories A list of categories, which are members of Object.Category
-- @param EvaluateFunction
function ZONE_RADIUS:SearchZone( EvaluateFunction, ObjectCategories )
local SearchZoneResult = true
local ZoneCoord = self:GetCoordinate()
local ZoneRadius = self:GetRadius()
self:E({ZoneCoord = ZoneCoord, ZoneRadius = ZoneRadius, ZoneCoordLL = ZoneCoord:ToStringLLDMS()})
local SphereSearch = {
id = world.VolumeType.SPHERE,
params = {
point = ZoneCoord:GetVec3(),
radius = ZoneRadius / 2,
}
}
local function EvaluateZone( ZoneDCSUnit )
env.info( ZoneDCSUnit:getName() )
local ZoneUnit = UNIT:Find( ZoneDCSUnit )
return EvaluateFunction( ZoneUnit )
end
world.searchObjects( Object.Category.UNIT, SphereSearch, EvaluateZone )
end
--- Returns if a location is within the zone.
-- @param #ZONE_RADIUS self
-- @param Dcs.DCSTypes#Vec2 Vec2 The location to test.
@@ -645,6 +849,22 @@ function ZONE_RADIUS:GetRandomPointVec3( inner, outer )
end
--- Returns a @{Point#COORDINATE} object reflecting a random 3D location within the zone.
-- @param #ZONE_RADIUS self
-- @param #number inner (optional) Minimal distance from the center of the zone. Default is 0.
-- @param #number outer (optional) Maximal distance from the outer edge of the zone. Default is the radius of the zone.
-- @return Core.Point#COORDINATE
function ZONE_RADIUS:GetRandomCoordinate( inner, outer )
self:F( self.ZoneName, inner, outer )
local Coordinate = COORDINATE:NewFromVec2( self:GetRandomVec2() )
self:T3( { Coordinate = Coordinate } )
return Coordinate
end
--- @type ZONE
-- @extends #ZONE_RADIUS
@@ -834,6 +1054,20 @@ function ZONE_GROUP:GetRandomVec2()
return Point
end
--- Returns a @{Point#POINT_VEC2} object reflecting a random 2D location within the zone.
-- @param #ZONE_GROUP self
-- @param #number inner (optional) Minimal distance from the center of the zone. Default is 0.
-- @param #number outer (optional) Maximal distance from the outer edge of the zone. Default is the radius of the zone.
-- @return Core.Point#POINT_VEC2 The @{Point#POINT_VEC2} object reflecting the random 3D location within the zone.
function ZONE_GROUP:GetRandomPointVec2( inner, outer )
self:F( self.ZoneName, inner, outer )
local PointVec2 = POINT_VEC2:NewFromVec2( self:GetRandomVec2() )
self:T3( { PointVec2 } )
return PointVec2
end
--- @type ZONE_POLYGON_BASE
@@ -1077,6 +1311,20 @@ function ZONE_POLYGON_BASE:GetRandomPointVec3()
end
--- Return a @{Point#COORDINATE} object representing a random 3D point at landheight within the zone.
-- @param #ZONE_POLYGON_BASE self
-- @return Core.Point#COORDINATE
function ZONE_POLYGON_BASE:GetRandomCoordinate()
self:F2()
local Coordinate = COORDINATE:NewFromVec2( self:GetRandomVec2() )
self:T2( Coordinate )
return Coordinate
end
--- Get the bounding square the zone.
-- @param #ZONE_POLYGON_BASE self
-- @return #ZONE_POLYGON_BASE.BoundingSquare The bounding square.
@@ -1114,7 +1362,7 @@ ZONE_POLYGON = {
ClassName="ZONE_POLYGON",
}
--- Constructor to create a ZONE_POLYGON instance, taking the zone name and the name of the @{Group#GROUP} defined within the Mission Editor.
--- Constructor to create a ZONE_POLYGON instance, taking the zone name and the @{Group#GROUP} defined within the Mission Editor.
-- The @{Group#GROUP} waypoints define the polygon corners. The first and the last point are automatically connected by ZONE_POLYGON.
-- @param #ZONE_POLYGON self
-- @param #string ZoneName Name of the zone.
@@ -1130,3 +1378,22 @@ function ZONE_POLYGON:New( ZoneName, ZoneGroup )
return self
end
--- Constructor to create a ZONE_POLYGON instance, taking the zone name and the **name** of the @{Group#GROUP} defined within the Mission Editor.
-- The @{Group#GROUP} waypoints define the polygon corners. The first and the last point are automatically connected by ZONE_POLYGON.
-- @param #ZONE_POLYGON self
-- @param #string ZoneName Name of the zone.
-- @param #string GroupName The group name of the GROUP defining the waypoints within the Mission Editor to define the polygon shape.
-- @return #ZONE_POLYGON self
function ZONE_POLYGON:NewFromGroupName( ZoneName, GroupName )
local ZoneGroup = GROUP:FindByName( GroupName )
local GroupPoints = ZoneGroup:GetTaskRoute()
local self = BASE:Inherit( self, ZONE_POLYGON_BASE:New( ZoneName, GroupPoints ) )
self:F( { ZoneName, ZoneGroup, self._.Polygon } )
return self
end

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,136 +1,234 @@
--- **Functional** -- The CLEANUP class keeps an area clean of crashing or colliding airplanes. It also prevents airplanes from firing within this area.
--- **Functional** -- The CLEANUP_AIRBASE class keeps an area clean of crashing or colliding airplanes. It also prevents airplanes from firing within this area.
--
-- ===
--
-- ### Author: **Sven Van de Velde (FlightControl)**
-- ### Contributions:
--
-- ====
--
-- @module CleanUp
--- The CLEANUP class.
-- @type CLEANUP
--- @type CLEANUP_AIRBASE.__ Methods which are not intended for mission designers, but which are used interally by the moose designer :-)
-- @field #map<#string,Wrapper.Airbase#AIRBASE> Airbases Map of Airbases.
-- @extends Core.Base#BASE
CLEANUP = {
ClassName = "CLEANUP",
ZoneNames = {},
TimeInterval = 300,
--- @type CLEANUP_AIRBASE
-- @extends #CLEANUP_AIRBASE.__
--- # CLEANUP_AIRBASE, extends @{Base#BASE}
--
-- ![Banner Image](..\Presentations\CLEANUP_AIRBASE\Dia1.JPG)
--
-- The CLEANUP_AIRBASE class keeps airbases clean, and tries to guarantee continuous airbase operations, even under combat.
-- Specific airbases need to be provided that need to be guarded. Each airbase registered, will be guarded within a zone of 8 km around the airbase.
-- Any unit that fires a missile, or shoots within the zone of an airbase, will be monitored by CLEANUP_AIRBASE.
-- Within the 8km zone, units cannot fire any missile, which prevents the airbase runway to receive missile or bomb hits.
-- Any airborne or ground unit that is on the runway below 30 meters (default value) will be automatically removed if it is damaged.
--
-- This is not a full 100% secure implementation. It is still possible that CLEANUP_AIRBASE cannot prevent (in-time) to keep the airbase clean.
-- The following situations may happen that will still stop the runway of an airbase:
--
-- * A damaged unit is not removed on time when above the runway, and crashes on the runway.
-- * A bomb or missile is still able to dropped on the runway.
-- * Units collide on the airbase, and could not be removed on time.
--
-- When a unit is within the airbase zone and needs to be monitored,
-- its status will be checked every 0.25 seconds! This is required to ensure that the airbase is kept clean.
-- But as a result, there is more CPU overload.
--
-- So as an advise, I suggest you use the CLEANUP_AIRBASE class with care:
--
-- * Only monitor airbases that really need to be monitored!
-- * Try not to monitor airbases that are likely to be invaded by enemy troops.
-- For these airbases, there is little use to keep them clean, as they will be invaded anyway...
--
-- By following the above guidelines, you can add airbase cleanup with acceptable CPU overhead.
--
-- ## 1. CLEANUP_AIRBASE Constructor
--
-- Creates the main object which is preventing the airbase to get polluted with debris on the runway, which halts the airbase.
--
-- -- Clean these Zones.
-- CleanUpAirports = CLEANUP_AIRBASE:New( { AIRBASE.Caucasus.Tbilisi, AIRBASE.Caucasus.Kutaisi )
--
-- -- or
-- CleanUpTbilisi = CLEANUP_AIRBASE:New( AIRBASE.Caucasus.Tbilisi )
-- CleanUpKutaisi = CLEANUP_AIRBASE:New( AIRBASE.Caucasus.Kutaisi )
--
-- ## 2. Add or Remove airbases
--
-- The method @{#CLEANUP_AIRBASE.AddAirbase}() to add an airbase to the cleanup validation process.
-- The method @{#CLEANUP_AIRBASE.RemoveAirbase}() removes an airbase from the cleanup validation process.
--
-- ## 3. Clean missiles and bombs within the airbase zone.
--
-- When missiles or bombs hit the runway, the airbase operations stop.
-- Use the method @{#CLEANUP_AIRBASE.SetCleanMissiles}() to control the cleaning of missiles, which will prevent airbases to stop.
-- Note that this method will not allow anymore airbases to be attacked, so there is a trade-off here to do.
--
-- @field #CLEANUP_AIRBASE
CLEANUP_AIRBASE = {
ClassName = "CLEANUP_AIRBASE",
TimeInterval = 0.2,
CleanUpList = {},
}
-- @field #CLEANUP_AIRBASE.__
CLEANUP_AIRBASE.__ = {}
--- @field #CLEANUP_AIRBASE.__.Airbases
CLEANUP_AIRBASE.__.Airbases = {}
--- Creates the main object which is handling the cleaning of the debris within the given Zone Names.
-- @param #CLEANUP self
-- @param #table ZoneNames Is a table of zone names where the debris should be cleaned. Also a single string can be passed with one zone name.
-- @param #number TimeInterval The interval in seconds when the clean activity takes place. The default is 300 seconds, thus every 5 minutes.
-- @return #CLEANUP
-- @param #CLEANUP_AIRBASE self
-- @param #list<#string> AirbaseNames Is a table of airbase names where the debris should be cleaned. Also a single string can be passed with one airbase name.
-- @return #CLEANUP_AIRBASE
-- @usage
-- -- Clean these Zones.
-- CleanUpAirports = CLEANUP:New( { 'CLEAN Tbilisi', 'CLEAN Kutaisi' }, 150 )
-- CleanUpAirports = CLEANUP_AIRBASE:New( { AIRBASE.Caucasus.Tbilisi, AIRBASE.Caucasus.Kutaisi )
-- or
-- CleanUpTbilisi = CLEANUP:New( 'CLEAN Tbilisi', 150 )
-- CleanUpKutaisi = CLEANUP:New( 'CLEAN Kutaisi', 600 )
function CLEANUP:New( ZoneNames, TimeInterval )
-- CleanUpTbilisi = CLEANUP_AIRBASE:New( AIRBASE.Caucasus.Tbilisi )
-- CleanUpKutaisi = CLEANUP_AIRBASE:New( AIRBASE.Caucasus.Kutaisi )
function CLEANUP_AIRBASE:New( AirbaseNames )
local self = BASE:Inherit( self, BASE:New() ) -- #CLEANUP
self:F( { ZoneNames, TimeInterval } )
local self = BASE:Inherit( self, BASE:New() ) -- #CLEANUP_AIRBASE
self:F( { AirbaseNames } )
if type( ZoneNames ) == 'table' then
self.ZoneNames = ZoneNames
if type( AirbaseNames ) == 'table' then
for AirbaseID, AirbaseName in pairs( AirbaseNames ) do
self:AddAirbase( AirbaseName )
end
else
self.ZoneNames = { ZoneNames }
end
if TimeInterval then
self.TimeInterval = TimeInterval
local AirbaseName = AirbaseNames
self:AddAirbase( AirbaseName )
end
self:HandleEvent( EVENTS.Birth )
self:HandleEvent( EVENTS.Birth, self.__.OnEventBirth )
self.CleanUpScheduler = SCHEDULER:New( self, self._CleanUpScheduler, {}, 1, TimeInterval )
self.__.CleanUpScheduler = SCHEDULER:New( self, self.__.CleanUpSchedule, {}, 1, self.TimeInterval )
self:HandleEvent( EVENTS.EngineShutdown , self.__.EventAddForCleanUp )
self:HandleEvent( EVENTS.EngineStartup, self.__.EventAddForCleanUp )
self:HandleEvent( EVENTS.Hit, self.__.EventAddForCleanUp )
self:HandleEvent( EVENTS.PilotDead, self.__.OnEventCrash )
self:HandleEvent( EVENTS.Dead, self.__.OnEventCrash )
self:HandleEvent( EVENTS.Crash, self.__.OnEventCrash )
return self
end
--- Destroys a group from the simulator, but checks first if it is still existing!
-- @param #CLEANUP self
-- @param Dcs.DCSWrapper.Group#Group GroupObject The object to be destroyed.
-- @param #string CleanUpGroupName The groupname...
function CLEANUP:_DestroyGroup( GroupObject, CleanUpGroupName )
self:F( { GroupObject, CleanUpGroupName } )
if GroupObject then -- and GroupObject:isExist() then
trigger.action.deactivateGroup(GroupObject)
self:T( { "GroupObject Destroyed", GroupObject } )
end
--- Adds an airbase to the airbase validation list.
-- @param #CLEANUP_AIRBASE self
-- @param #string AirbaseName
-- @return #CLEANUP_AIRBASE
function CLEANUP_AIRBASE:AddAirbase( AirbaseName )
self.__.Airbases[AirbaseName] = AIRBASE:FindByName( AirbaseName )
self:F({"Airbase:", AirbaseName, self.__.Airbases[AirbaseName]:GetDesc()})
return self
end
--- Destroys a @{DCSWrapper.Unit#Unit} from the simulator, but checks first if it is still existing!
-- @param #CLEANUP self
-- @param Dcs.DCSWrapper.Unit#Unit CleanUpUnit The object to be destroyed.
-- @param #string CleanUpUnitName The Unit name ...
function CLEANUP:_DestroyUnit( CleanUpUnit, CleanUpUnitName )
self:F( { CleanUpUnit, CleanUpUnitName } )
--- Removes an airbase from the airbase validation list.
-- @param #CLEANUP_AIRBASE self
-- @param #string AirbaseName
-- @return #CLEANUP_AIRBASE
function CLEANUP_AIRBASE:RemoveAirbase( AirbaseName )
self.__.Airbases[AirbaseName] = nil
return self
end
--- Enables or disables the cleaning of missiles within the airbase zones.
-- Airbase operations stop when a missile or bomb is dropped at a runway.
-- Note that when this method is used, the airbase operations won't stop if
-- the missile or bomb was cleaned within the airbase zone, which is 8km from the center of the airbase.
-- However, there is a trade-off to make. Attacks on airbases won't be possible anymore if this method is used.
-- Note, one can also use the method @{#CLEANUP_AIRBASE.RemoveAirbase}() to remove the airbase from the control process as a whole,
-- when an enemy unit is near. That is also an option...
-- @param #CLEANUP_AIRBASE self
-- @param #string CleanMissiles (Default=true) If true, missiles fired are immediately destroyed. If false missiles are not controlled.
-- @return #CLEANUP_AIRBASE
function CLEANUP_AIRBASE:SetCleanMissiles( CleanMissiles )
if CleanMissiles then
self:HandleEvent( EVENTS.Shot, self.__.OnEventShot )
else
self:UnHandleEvent( EVENTS.Shot )
end
end
function CLEANUP_AIRBASE.__:IsInAirbase( Vec2 )
local InAirbase = false
for AirbaseName, Airbase in pairs( self.__.Airbases ) do
local Airbase = Airbase -- Wrapper.Airbase#AIRBASE
if Airbase:GetZone():IsVec2InZone( Vec2 ) then
InAirbase = true
break;
end
end
return InAirbase
end
--- Destroys a @{Unit} from the simulator, but checks first if it is still existing!
-- @param #CLEANUP_AIRBASE self
-- @param Wrapper.Unit#UNIT CleanUpUnit The object to be destroyed.
function CLEANUP_AIRBASE.__:DestroyUnit( CleanUpUnit )
self:F( { CleanUpUnit } )
if CleanUpUnit then
local CleanUpGroup = Unit.getGroup(CleanUpUnit)
local CleanUpUnitName = CleanUpUnit:GetName()
local CleanUpGroup = CleanUpUnit:GetGroup()
-- TODO Client bug in 1.5.3
if CleanUpGroup and CleanUpGroup:isExist() then
local CleanUpGroupUnits = CleanUpGroup:getUnits()
if CleanUpGroup:IsAlive() then
local CleanUpGroupUnits = CleanUpGroup:GetUnits()
if #CleanUpGroupUnits == 1 then
local CleanUpGroupName = CleanUpGroup:getName()
--self:CreateEventCrash( timer.getTime(), CleanUpUnit )
CleanUpGroup:destroy()
self:T( { "Destroyed Group:", CleanUpGroupName } )
local CleanUpGroupName = CleanUpGroup:GetName()
CleanUpGroup:Destroy()
else
CleanUpUnit:destroy()
self:T( { "Destroyed Unit:", CleanUpUnitName } )
CleanUpUnit:Destroy()
end
self.CleanUpList[CleanUpUnitName] = nil -- Cleaning from the list
CleanUpUnit = nil
self.CleanUpList[CleanUpUnitName] = nil
end
end
end
-- TODO check Dcs.DCSTypes#Weapon
--- Destroys a missile from the simulator, but checks first if it is still existing!
-- @param #CLEANUP self
-- @param Dcs.DCSTypes#Weapon MissileObject
function CLEANUP:_DestroyMissile( MissileObject )
self:F( { MissileObject } )
--- Destroys a missile from the simulator, but checks first if it is still existing!
-- @param #CLEANUP_AIRBASE self
-- @param Dcs.DCSTypes#Weapon MissileObject
function CLEANUP_AIRBASE.__:DestroyMissile( MissileObject )
self:F( { MissileObject } )
if MissileObject and MissileObject:isExist() then
MissileObject:destroy()
self:T( "MissileObject Destroyed")
end
end
--- @param #CLEANUP self
--- @param #CLEANUP_AIRBASE self
-- @param Core.Event#EVENTDATA EventData
function CLEANUP:_OnEventBirth( EventData )
function CLEANUP_AIRBASE.__:OnEventBirth( EventData )
self:F( { EventData } )
self.CleanUpList[EventData.IniDCSUnitName] = {}
self.CleanUpList[EventData.IniDCSUnitName].CleanUpUnit = EventData.IniDCSUnit
self.CleanUpList[EventData.IniDCSUnitName].CleanUpGroup = EventData.IniDCSGroup
self.CleanUpList[EventData.IniDCSUnitName].CleanUpUnit = EventData.IniUnit
self.CleanUpList[EventData.IniDCSUnitName].CleanUpGroup = EventData.IniGroup
self.CleanUpList[EventData.IniDCSUnitName].CleanUpGroupName = EventData.IniDCSGroupName
self.CleanUpList[EventData.IniDCSUnitName].CleanUpUnitName = EventData.IniDCSUnitName
EventData.IniUnit:HandleEvent( EVENTS.EngineShutdown , self._EventAddForCleanUp )
EventData.IniUnit:HandleEvent( EVENTS.EngineStartup, self._EventAddForCleanUp )
EventData.IniUnit:HandleEvent( EVENTS.Hit, self._EventAddForCleanUp )
EventData.IniUnit:HandleEvent( EVENTS.PilotDead, self._EventCrash )
EventData.IniUnit:HandleEvent( EVENTS.Dead, self._EventCrash )
EventData.IniUnit:HandleEvent( EVENTS.Crash, self._EventCrash )
EventData.IniUnit:HandleEvent( EVENTS.Shot, self._EventShot )
end
--- Detects if a crash event occurs.
-- Crashed units go into a CleanUpList for removal.
-- @param #CLEANUP self
-- @param Dcs.DCSTypes#Event event
function CLEANUP:_EventCrash( Event )
-- @param #CLEANUP_AIRBASE self
-- @param Core.Event#EVENTDATA Event
function CLEANUP_AIRBASE.__:OnEventCrash( Event )
self:F( { Event } )
--TODO: This stuff is not working due to a DCS bug. Burning units cannot be destroyed.
@@ -141,171 +239,164 @@ function CLEANUP:_EventCrash( Event )
-- self:T("after deactivateGroup")
-- event.initiator:destroy()
self.CleanUpList[Event.IniDCSUnitName] = {}
self.CleanUpList[Event.IniDCSUnitName].CleanUpUnit = Event.IniDCSUnit
self.CleanUpList[Event.IniDCSUnitName].CleanUpGroup = Event.IniDCSGroup
self.CleanUpList[Event.IniDCSUnitName].CleanUpGroupName = Event.IniDCSGroupName
self.CleanUpList[Event.IniDCSUnitName].CleanUpUnitName = Event.IniDCSUnitName
if Event.IniDCSUnitName and Event.IniCategory == Object.Category.UNIT then
self.CleanUpList[Event.IniDCSUnitName] = {}
self.CleanUpList[Event.IniDCSUnitName].CleanUpUnit = Event.IniUnit
self.CleanUpList[Event.IniDCSUnitName].CleanUpGroup = Event.IniGroup
self.CleanUpList[Event.IniDCSUnitName].CleanUpGroupName = Event.IniDCSGroupName
self.CleanUpList[Event.IniDCSUnitName].CleanUpUnitName = Event.IniDCSUnitName
end
end
--- Detects if a unit shoots a missile.
-- If this occurs within one of the zones, then the weapon used must be destroyed.
-- @param #CLEANUP self
-- @param Dcs.DCSTypes#Event event
function CLEANUP:_EventShot( Event )
-- If this occurs within one of the airbases, then the weapon used must be destroyed.
-- @param #CLEANUP_AIRBASE self
-- @param Core.Event#EVENTDATA Event
function CLEANUP_AIRBASE.__:OnEventShot( Event )
self:F( { Event } )
-- Test if the missile was fired within one of the CLEANUP.ZoneNames.
local CurrentLandingZoneID = 0
CurrentLandingZoneID = routines.IsUnitInZones( Event.IniDCSUnit, self.ZoneNames )
if ( CurrentLandingZoneID ) then
-- Okay, the missile was fired within the CLEANUP.ZoneNames, destroy the fired weapon.
--_SEADmissile:destroy()
SCHEDULER:New( self, CLEANUP._DestroyMissile, { Event.Weapon }, 0.1 )
-- Test if the missile was fired within one of the CLEANUP_AIRBASE.AirbaseNames.
if self:IsInAirbase( Event.IniUnit:GetVec2() ) then
-- Okay, the missile was fired within the CLEANUP_AIRBASE.AirbaseNames, destroy the fired weapon.
self:DestroyMissile( Event.Weapon )
end
end
--- Detects if the Unit has an S_EVENT_HIT within the given ZoneNames. If this is the case, destroy the unit.
-- @param #CLEANUP self
-- @param Dcs.DCSTypes#Event event
function CLEANUP:_EventHitCleanUp( Event )
--- Detects if the Unit has an S_EVENT_HIT within the given AirbaseNames. If this is the case, destroy the unit.
-- @param #CLEANUP_AIRBASE self
-- @param Core.Event#EVENTDATA Event
function CLEANUP_AIRBASE.__:OnEventHit( Event )
self:F( { Event } )
if Event.IniDCSUnit then
if routines.IsUnitInZones( Event.IniDCSUnit, self.ZoneNames ) ~= nil then
self:T( { "Life: ", Event.IniDCSUnitName, ' = ', Event.IniDCSUnit:getLife(), "/", Event.IniDCSUnit:getLife0() } )
if Event.IniDCSUnit:getLife() < Event.IniDCSUnit:getLife0() then
if Event.IniUnit then
if self:IsInAirbase( Event.IniUnit:GetVec2() ) then
self:T( { "Life: ", Event.IniDCSUnitName, ' = ', Event.IniUnit:GetLife(), "/", Event.IniUnit:GetLife0() } )
if Event.IniUnit:GetLife() < Event.IniUnit:GetLife0() then
self:T( "CleanUp: Destroy: " .. Event.IniDCSUnitName )
SCHEDULER:New( self, CLEANUP._DestroyUnit, { Event.IniDCSUnit }, 0.1 )
CLEANUP_AIRBASE.__:DestroyUnit( Event.IniUnit )
end
end
end
if Event.TgtDCSUnit then
if routines.IsUnitInZones( Event.TgtDCSUnit, self.ZoneNames ) ~= nil then
self:T( { "Life: ", Event.TgtDCSUnitName, ' = ', Event.TgtDCSUnit:getLife(), "/", Event.TgtDCSUnit:getLife0() } )
if Event.TgtDCSUnit:getLife() < Event.TgtDCSUnit:getLife0() then
if Event.TgtUnit then
if self:IsInAirbase( Event.TgtUnit:GetVec2() ) then
self:T( { "Life: ", Event.TgtDCSUnitName, ' = ', Event.TgtUnit:GetLife(), "/", Event.TgtUnit:GetLife0() } )
if Event.TgtUnit:GetLife() < Event.TgtUnit:GetLife0() then
self:T( "CleanUp: Destroy: " .. Event.TgtDCSUnitName )
SCHEDULER:New( self, CLEANUP._DestroyUnit, { Event.TgtDCSUnit }, 0.1 )
CLEANUP_AIRBASE.__:DestroyUnit( Event.TgtUnit )
end
end
end
end
--- Add the @{DCSWrapper.Unit#Unit} to the CleanUpList for CleanUp.
function CLEANUP:_AddForCleanUp( CleanUpUnit, CleanUpUnitName )
-- @param #CLEANUP_AIRBASE self
-- @param Wrapper.Unit#UNIT CleanUpUnit
-- @oaram #string CleanUpUnitName
function CLEANUP_AIRBASE.__:AddForCleanUp( CleanUpUnit, CleanUpUnitName )
self:F( { CleanUpUnit, CleanUpUnitName } )
self.CleanUpList[CleanUpUnitName] = {}
self.CleanUpList[CleanUpUnitName].CleanUpUnit = CleanUpUnit
self.CleanUpList[CleanUpUnitName].CleanUpUnitName = CleanUpUnitName
self.CleanUpList[CleanUpUnitName].CleanUpGroup = Unit.getGroup(CleanUpUnit)
self.CleanUpList[CleanUpUnitName].CleanUpGroupName = Unit.getGroup(CleanUpUnit):getName()
local CleanUpGroup = CleanUpUnit:GetGroup()
self.CleanUpList[CleanUpUnitName].CleanUpGroup = CleanUpGroup
self.CleanUpList[CleanUpUnitName].CleanUpGroupName = CleanUpGroup:GetName()
self.CleanUpList[CleanUpUnitName].CleanUpTime = timer.getTime()
self.CleanUpList[CleanUpUnitName].CleanUpMoved = false
self:T( { "CleanUp: Add to CleanUpList: ", Unit.getGroup(CleanUpUnit):getName(), CleanUpUnitName } )
self:T( { "CleanUp: Add to CleanUpList: ", CleanUpGroup:GetName(), CleanUpUnitName } )
end
--- Detects if the Unit has an S_EVENT_ENGINE_SHUTDOWN or an S_EVENT_HIT within the given ZoneNames. If this is the case, add the Group to the CLEANUP List.
-- @param #CLEANUP self
-- @param Dcs.DCSTypes#Event event
function CLEANUP:_EventAddForCleanUp( Event )
--- Detects if the Unit has an S_EVENT_ENGINE_SHUTDOWN or an S_EVENT_HIT within the given AirbaseNames. If this is the case, add the Group to the CLEANUP_AIRBASE List.
-- @param #CLEANUP_AIRBASE.__ self
-- @param Core.Event#EVENTDATA Event
function CLEANUP_AIRBASE.__:EventAddForCleanUp( Event )
if Event.IniDCSUnit then
self:F({Event})
if Event.IniDCSUnit and Event.IniCategory == Object.Category.UNIT then
if self.CleanUpList[Event.IniDCSUnitName] == nil then
if routines.IsUnitInZones( Event.IniDCSUnit, self.ZoneNames ) ~= nil then
self:_AddForCleanUp( Event.IniDCSUnit, Event.IniDCSUnitName )
if self:IsInAirbase( Event.IniUnit:GetVec2() ) then
self:AddForCleanUp( Event.IniUnit, Event.IniDCSUnitName )
end
end
end
if Event.TgtDCSUnit then
if Event.TgtDCSUnit and Event.TgtCategory == Object.Category.UNIT then
if self.CleanUpList[Event.TgtDCSUnitName] == nil then
if routines.IsUnitInZones( Event.TgtDCSUnit, self.ZoneNames ) ~= nil then
self:_AddForCleanUp( Event.TgtDCSUnit, Event.TgtDCSUnitName )
if self:IsInAirbase( Event.TgtUnit:GetVec2() ) then
self:AddForCleanUp( Event.TgtUnit, Event.TgtDCSUnitName )
end
end
end
end
local CleanUpSurfaceTypeText = {
"LAND",
"SHALLOW_WATER",
"WATER",
"ROAD",
"RUNWAY"
}
--- At the defined time interval, CleanUp the Groups within the CleanUpList.
-- @param #CLEANUP self
function CLEANUP:_CleanUpScheduler()
self:F( { "CleanUp Scheduler" } )
-- @param #CLEANUP_AIRBASE self
function CLEANUP_AIRBASE.__:CleanUpSchedule()
local CleanUpCount = 0
for CleanUpUnitName, UnitData in pairs( self.CleanUpList ) do
for CleanUpUnitName, CleanUpListData in pairs( self.CleanUpList ) do
CleanUpCount = CleanUpCount + 1
self:T( { CleanUpUnitName, UnitData } )
local CleanUpUnit = Unit.getByName(UnitData.CleanUpUnitName)
local CleanUpGroupName = UnitData.CleanUpGroupName
local CleanUpUnitName = UnitData.CleanUpUnitName
if CleanUpUnit then
self:T( { "CleanUp Scheduler", "Checking:", CleanUpUnitName } )
local CleanUpUnit = CleanUpListData.CleanUpUnit -- Wrapper.Unit#UNIT
local CleanUpGroupName = CleanUpListData.CleanUpGroupName
if CleanUpUnit:IsAlive() ~= nil then
if _DATABASE:GetStatusGroup( CleanUpGroupName ) ~= "ReSpawn" then
local CleanUpUnitVec3 = CleanUpUnit:getPoint()
--self:T( CleanUpUnitVec3 )
local CleanUpUnitVec2 = {}
CleanUpUnitVec2.x = CleanUpUnitVec3.x
CleanUpUnitVec2.y = CleanUpUnitVec3.z
--self:T( CleanUpUnitVec2 )
local CleanUpSurfaceType = land.getSurfaceType(CleanUpUnitVec2)
--self:T( CleanUpSurfaceType )
if CleanUpUnit and CleanUpUnit:getLife() <= CleanUpUnit:getLife0() * 0.95 then
if CleanUpSurfaceType == land.SurfaceType.RUNWAY then
if CleanUpUnit:inAir() then
local CleanUpLandHeight = land.getHeight(CleanUpUnitVec2)
local CleanUpUnitHeight = CleanUpUnitVec3.y - CleanUpLandHeight
self:T( { "CleanUp Scheduler", "Height = " .. CleanUpUnitHeight } )
if CleanUpUnitHeight < 30 then
local CleanUpCoordinate = CleanUpUnit:GetCoordinate()
self:T( { "CleanUp Scheduler", CleanUpUnitName } )
if CleanUpUnit:GetLife() <= CleanUpUnit:GetLife0() * 0.95 then
if CleanUpUnit:IsAboveRunway() then
if CleanUpUnit:InAir() then
local CleanUpLandHeight = CleanUpCoordinate:GetLandHeight()
local CleanUpUnitHeight = CleanUpCoordinate.y - CleanUpLandHeight
if CleanUpUnitHeight < 100 then
self:T( { "CleanUp Scheduler", "Destroy " .. CleanUpUnitName .. " because below safe height and damaged." } )
self:_DestroyUnit(CleanUpUnit, CleanUpUnitName)
self:DestroyUnit( CleanUpUnit )
end
else
self:T( { "CleanUp Scheduler", "Destroy " .. CleanUpUnitName .. " because on runway and damaged." } )
self:_DestroyUnit(CleanUpUnit, CleanUpUnitName)
self:DestroyUnit( CleanUpUnit )
end
end
end
-- Clean Units which are waiting for a very long time in the CleanUpZone.
if CleanUpUnit then
local CleanUpUnitVelocity = CleanUpUnit:getVelocity()
local CleanUpUnitVelocityTotal = math.abs(CleanUpUnitVelocity.x) + math.abs(CleanUpUnitVelocity.y) + math.abs(CleanUpUnitVelocity.z)
if CleanUpUnitVelocityTotal < 1 then
if UnitData.CleanUpMoved then
if UnitData.CleanUpTime + 180 <= timer.getTime() then
local CleanUpUnitVelocity = CleanUpUnit:GetVelocityKMH()
if CleanUpUnitVelocity < 1 then
if CleanUpListData.CleanUpMoved then
if CleanUpListData.CleanUpTime + 180 <= timer.getTime() then
self:T( { "CleanUp Scheduler", "Destroy due to not moving anymore " .. CleanUpUnitName } )
self:_DestroyUnit(CleanUpUnit, CleanUpUnitName)
self:DestroyUnit( CleanUpUnit )
end
end
else
UnitData.CleanUpTime = timer.getTime()
UnitData.CleanUpMoved = true
CleanUpListData.CleanUpTime = timer.getTime()
CleanUpListData.CleanUpMoved = true
end
end
else
-- Do nothing ...
self.CleanUpList[CleanUpUnitName] = nil -- Not anymore in the DCSRTE
self.CleanUpList[CleanUpUnitName] = nil
end
else
self:T( "CleanUp: Group " .. CleanUpUnitName .. " cannot be found in DCS RTE, removing ..." )
self.CleanUpList[CleanUpUnitName] = nil -- Not anymore in the DCSRTE
self.CleanUpList[CleanUpUnitName] = nil
end
end
self:T(CleanUpCount)

View File

@@ -1,4 +1,4 @@
--- **Functional** -- Management of target **Designation**.
--- **Functional** -- Management of target **Designation**. Lase, smoke and illuminate targets.
--
-- --![Banner Image](..\Presentations\DESIGNATE\Dia1.JPG)
--
@@ -70,10 +70,14 @@ do -- DESIGNATE
-- The RecceSet is continuously detecting for potential Targets, executing its task as part of the DetectionObject.
-- Once Targets have been detected, the DesignateObject will trigger the **Detect Event**.
--
-- In order to prevent an overflow in the DesignateObject of detected targets, there is a maximum
-- amount of DetectionItems that can be put in **scope** of the DesignateObject.
-- We call this the **MaximumDesignations** term.
--
-- As part of the Detect Event, the DetectionItems list is used by the DesignateObject to provide the Players with:
--
-- * The RecceGroups are reporting to each AttackGroup, sending **Messages** containing the Threat Level and the TargetSet composition.
-- * **Menu options** are created and updated for each AttackGroup, containing the Threat Level and the TargetSet composition.
-- * **Menu options** are created and updated for each AttackGroup, containing the Detection ID and the Coordinates.
--
-- A Player can then select an action from the Designate Menu.
--
@@ -109,7 +113,7 @@ do -- DESIGNATE
--
-- ### 2.1 DESIGNATE States
--
-- * **Designating** ( Group ): The process is not started yet.
-- * **Designating** ( Group ): The designation process.
--
-- ### 2.2 DESIGNATE Events
--
@@ -119,9 +123,17 @@ do -- DESIGNATE
-- * **@{#DESIGNATE.Smoke}**: Smoke the targets with the specified Index.
-- * **@{#DESIGNATE.Status}**: Report designation status.
--
-- ## 3. Laser codes
-- ## 3. Maximum Designations
--
-- ### 3.1 Set possible laser codes
-- In order to prevent an overflow of designations due to many Detected Targets, there is a
-- Maximum Designations scope that is set in the DesignationObject.
--
-- The method @{#DESIGNATE.SetMaximumDesignations}() will put a limit on the amount of designations put in scope of the DesignationObject.
-- Using the menu system, the player can "forget" a designation, so that gradually a new designation can be put in scope when detected.
--
-- ## 4. Laser codes
--
-- ### 4.1. Set possible laser codes
--
-- An array of laser codes can be provided, that will be used by the DESIGNATE when lasing.
-- The laser code is communicated by the Recce when it is lasing a larget.
@@ -139,11 +151,20 @@ do -- DESIGNATE
--
-- The above sets a collection of possible laser codes that can be assigned. **Note the { } notation!**
--
-- ### 3.2 Auto generate laser codes
-- ### 4.2. Auto generate laser codes
--
-- Use the method @{#DESIGNATE.GenerateLaserCodes}() to generate all possible laser codes. Logic implemented and advised by Ciribob!
--
-- ## 4. Autolase to automatically lase detected targets.
-- ### 4.3. Add specific lase codes to the lase menu
--
-- Certain plane types can only drop laser guided ordonnance when targets are lased with specific laser codes.
-- The SU-25T needs targets to be lased using laser code 1113.
-- The A-10A needs targets to be lased using laser code 1680.
--
-- The method @{#DESIGNATE.AddMenuLaserCode}() to allow a player to lase a target using a specific laser code.
-- Remove such a lase menu option using @{#DESIGNATE.RemoveMenuLaserCode}().
--
-- ## 5. Autolase to automatically lase detected targets.
--
-- DetectionItems can be auto lased once detected by Recces. As such, there is almost no action required from the Players using the Designate Menu.
-- The **auto lase** function can be activated through the Designation Menu.
@@ -154,7 +175,7 @@ do -- DESIGNATE
--
-- Activate the auto lasing.
--
-- ## 5. Target prioritization on threat level
-- ## 6. Target prioritization on threat level
--
-- Targets can be detected of different types in one DetectionItem. Depending on the type of the Target, a different threat level applies in an Air to Ground combat context.
-- SAMs are of a higher threat than normal tanks. So, if the Target type was recognized, the Recces will select those targets that form the biggest threat first,
@@ -167,7 +188,12 @@ do -- DESIGNATE
--
-- The example will activate the threat level prioritization for this the Designate object. Threats will be marked based on the threat level of the Target.
--
-- ## 6. Status Report
-- ## 6. Designate Menu Location for a Mission
--
-- You can make DESIGNATE work for a @{Mission#MISSION} object. In this way, the designate menu will not appear in the root of the radio menu, but in the menu of the Mission.
-- Use the method @{#DESIGNATE.SetMission}() to set the @{Mission} object for the designate function.
--
-- ## 7. Status Report
--
-- A status report is available that displays the current Targets detected, grouped per DetectionItem, and a list of which Targets are currently being marked.
--
@@ -182,7 +208,6 @@ do -- DESIGNATE
-- The example will activate the flashing of the status menu for this Designate object.
--
-- @field #DESIGNATE
--
DESIGNATE = {
ClassName = "DESIGNATE",
}
@@ -192,8 +217,9 @@ do -- DESIGNATE
-- @param Tasking.CommandCenter#COMMANDCENTER CC
-- @param Functional.Detection#DETECTION_BASE Detection
-- @param Core.Set#SET_GROUP AttackSet The Attack collection of GROUP objects to designate and report for.
-- @param Tasking.Mission#MISSION Mission (Optional) The Mission where the menu needs to be attached.
-- @return #DESIGNATE
function DESIGNATE:New( CC, Detection, AttackSet )
function DESIGNATE:New( CC, Detection, AttackSet, Mission )
local self = BASE:Inherit( self, FSM:New() ) -- #DESIGNATE
self:F( { Detection } )
@@ -360,22 +386,33 @@ do -- DESIGNATE
self.RecceSet = Detection:GetDetectionSetGroup()
self.Recces = {}
self.Designating = {}
self:SetDesignateName()
self.LaseDuration = 60
self:SetFlashStatusMenu( false )
self:SetDesignateMenu()
self:SetMission( Mission )
self:SetLaserCodes( 1688 ) -- set self.LaserCodes
self:SetAutoLase( false ) -- set self.Autolase
self:SetLaserCodes( { 1688, 1130, 4785, 6547, 1465, 4578 } ) -- set self.LaserCodes
self:SetAutoLase( false, false ) -- set self.Autolase and don't send message.
self:SetThreatLevelPrioritization( false ) -- self.ThreatLevelPrioritization, default is threat level priorization off
self:SetMaximumDesignations( 5 ) -- Sets the maximum designations. The default is 5 designations.
self:SetMaximumDistanceDesignations( 12000 ) -- Sets the maximum distance on which designations can be accepted. The default is 8000 meters.
self:SetMaximumMarkings( 2 ) -- Per target group, a maximum of 2 markings will be made by default.
self:SetDesignateMenu()
self.LaserCodesUsed = {}
self.MenuLaserCodes = {} -- This map contains the laser codes that will be shown in the designate menu to lase with specific laser codes.
self.Detection:__Start( 2 )
self:__Detect( -15 )
self.MarkScheduler = SCHEDULER:New( self )
return self
end
@@ -399,6 +436,56 @@ do -- DESIGNATE
end
--- Set the maximum amount of designations.
-- @param #DESIGNATE self
-- @param #number MaximumDesignations
-- @return #DESIGNATE
function DESIGNATE:SetMaximumDesignations( MaximumDesignations )
self.MaximumDesignations = MaximumDesignations
return self
end
--- Set the maximum ground designation distance.
-- @param #DESIGNATE self
-- @param #number MaximumDistanceGroundDesignation Maximum ground designation distance in meters.
-- @return #DESIGNATE
function DESIGNATE:SetMaximumDistanceGroundDesignation( MaximumDistanceGroundDesignation )
self.MaximumDistanceGroundDesignation = MaximumDistanceGroundDesignation
return self
end
--- Set the maximum air designation distance.
-- @param #DESIGNATE self
-- @param #number MaximumDistanceAirDesignation Maximum air designation distance in meters.
-- @return #DESIGNATE
function DESIGNATE:SetMaximumDistanceAirDesignation( MaximumDistanceAirDesignation )
self.MaximumDistanceAirDesignation = MaximumDistanceAirDesignation
return self
end
--- Set the overall maximum distance when designations can be accepted.
-- @param #DESIGNATE self
-- @param #number MaximumDistanceDesignations Maximum distance in meters to accept designations.
-- @return #DESIGNATE
function DESIGNATE:SetMaximumDistanceDesignations( MaximumDistanceDesignations )
self.MaximumDistanceDesignations = MaximumDistanceDesignations
return self
end
--- Set the maximum amount of markings FACs will do, per designated target group.
-- @param #DESIGNATE self
-- @param #number MaximumMarkings Maximum markings FACs will do, per designated target group.
-- @return #DESIGNATE
function DESIGNATE:SetMaximumMarkings( MaximumMarkings )
self.MaximumMarkings = MaximumMarkings
return self
end
--- Set an array of possible laser codes.
-- Each new lase will select a code from this table.
-- @param #DESIGNATE self
@@ -407,13 +494,64 @@ do -- DESIGNATE
function DESIGNATE:SetLaserCodes( LaserCodes ) --R2.1
self.LaserCodes = ( type( LaserCodes ) == "table" ) and LaserCodes or { LaserCodes }
self:E(self.LaserCodes)
self:E( { LaserCodes = self.LaserCodes } )
self.LaserCodesUsed = {}
return self
end
--- Add a specific lase code to the designate lase menu to lase targets with a specific laser code.
-- The MenuText will appear in the lase menu.
-- @param #DESIGNATE self
-- @param #number LaserCode The specific laser code to be added to the lase menu.
-- @param #string MenuText The text to be shown to the player. If you specify a %d in the MenuText, the %d will be replaced with the LaserCode specified.
-- @return #DESIGNATE
-- @usage
-- RecceDesignation:AddMenuLaserCode( 1113, "Lase with %d for Su-25T" )
-- RecceDesignation:AddMenuLaserCode( 1680, "Lase with %d for A-10A" )
--
function DESIGNATE:AddMenuLaserCode( LaserCode, MenuText )
self.MenuLaserCodes[LaserCode] = MenuText
self:SetDesignateMenu()
return self
end
--- Removes a specific lase code from the designate lase menu.
-- @param #DESIGNATE self
-- @param #number LaserCode The specific laser code that was set to be added to the lase menu.
-- @return #DESIGNATE
-- @usage
-- RecceDesignation:RemoveMenuLaserCode( 1113 )
--
function DESIGNATE:RemoveMenuLaserCode( LaserCode )
self.MenuLaserCodes[LaserCode] = nil
self:SetDesignateMenu()
return self
end
--- Set the name of the designation. The name will appear in the menu.
-- This method can be used to control different designations for different plane types.
-- @param #DESIGNATE self
-- @param #string DesignateName
-- @return #DESIGNATE
function DESIGNATE:SetDesignateName( DesignateName )
self.DesignateName = "Designation" .. ( DesignateName and ( " for " .. DesignateName ) or "" )
return self
end
--- Generate an array of possible laser codes.
-- Each new lase will select a code from this table.
-- The entered value can range from 1111 - 1788,
@@ -470,21 +608,22 @@ do -- DESIGNATE
--- Set auto lase.
-- Auto lase will start lasing targets immediately when these are in range.
-- @param #DESIGNATE self
-- @param #boolean AutoLase
-- @param #boolean AutoLase (optional) true sets autolase on, false off. Default is off.
-- @param #boolean Message (optional) true is send message, false or nil won't send a message. Default is no message sent.
-- @return #DESIGNATE
function DESIGNATE:SetAutoLase( AutoLase ) --R2.1
function DESIGNATE:SetAutoLase( AutoLase, Message )
self.AutoLase = AutoLase
self.AutoLase = AutoLase or false
local AutoLaseOnOff = ( AutoLase == true ) and "On" or "Off"
local CC = self.CC:GetPositionable()
if CC then
CC:MessageToSetGroup( "Auto Lase " .. AutoLaseOnOff .. ".", 15, self.AttackSet )
if Message then
local AutoLaseOnOff = ( self.AutoLase == true ) and "On" or "Off"
local CC = self.CC:GetPositionable()
if CC then
CC:MessageToSetGroup( self.DesignateName .. ": Auto Lase " .. AutoLaseOnOff .. ".", 15, self.AttackSet )
end
end
self:ActivateAutoLase()
self:CoordinateLase()
self:SetDesignateMenu()
return self
@@ -501,6 +640,17 @@ do -- DESIGNATE
return self
end
--- Set the MISSION object for which designate will function.
-- When a MISSION object is assigned, the menu for the designation will be located at the Mission Menu.
-- @param #DESIGNATE self
-- @param Tasking.Mission#MISSION Mission The MISSION object.
-- @return #DESIGNATE
function DESIGNATE:SetMission( Mission ) --R2.2
self.Mission = Mission
return self
end
---
@@ -508,15 +658,96 @@ do -- DESIGNATE
-- @return #DESIGNATE
function DESIGNATE:onafterDetect()
self:__Detect( -60 )
self:__Detect( -math.random( 60 ) )
self:ActivateAutoLase()
self:DesignationScope()
self:CoordinateLase()
self:SendStatus()
self:SetDesignateMenu()
return self
end
--- Adapt the designation scope according the detected items.
-- @param #DESIGNATE self
-- @return #DESIGNATE
function DESIGNATE:DesignationScope()
local DetectedItems = self.Detection:GetDetectedItems()
local DetectedItemCount = 0
for DesignateIndex, Designating in pairs( self.Designating ) do
local DetectedItem = DetectedItems[DesignateIndex]
if DetectedItem then
-- Check LOS...
local IsDetected = self.Detection:IsDetectedItemDetected( DetectedItem )
self:F({IsDetected = IsDetected, DetectedItem })
if IsDetected == false then
self:F("Removing")
-- This Detection is obsolete, remove from the designate scope
self.Designating[DesignateIndex] = nil
self.AttackSet:ForEachGroup(
function( AttackGroup )
local DetectionText = self.Detection:DetectedItemReportSummary( DesignateIndex, AttackGroup ):Text( ", " )
self.CC:GetPositionable():MessageToGroup( "Targets out of LOS\n" .. DetectionText, 10, AttackGroup, self.DesignateName )
end
)
else
DetectedItemCount = DetectedItemCount + 1
end
else
-- This Detection is obsolete, remove from the designate scope
self.Designating[DesignateIndex] = nil
end
end
if DetectedItemCount < 5 then
for DesignateIndex, DetectedItem in pairs( DetectedItems ) do
local IsDetected = self.Detection:IsDetectedItemDetected( DetectedItem )
if IsDetected == true then
self:F( { DistanceRecce = DetectedItem.DistanceRecce } )
if DetectedItem.DistanceRecce <= self.MaximumDistanceDesignations then
if self.Designating[DesignateIndex] == nil then
-- ok, we added one item to the designate scope.
self.AttackSet:ForEachGroup(
function( AttackGroup )
local DetectionText = self.Detection:DetectedItemReportSummary( DesignateIndex, AttackGroup ):Text( ", " )
self.CC:GetPositionable():MessageToGroup( "Targets detected at \n" .. DetectionText, 10, AttackGroup, self.DesignateName )
end
)
self.Designating[DesignateIndex] = ""
break
end
end
end
end
end
return self
end
--- Coordinates the Auto Lase.
-- @param #DESIGNATE self
-- @return #DESIGNATE
function DESIGNATE:CoordinateLase()
local DetectedItems = self.Detection:GetDetectedItems()
for DesignateIndex, Designating in pairs( self.Designating ) do
local DetectedItem = DetectedItems[DesignateIndex]
if DetectedItem then
if self.AutoLase then
self:LaseOn( DesignateIndex, self.LaseDuration )
end
end
end
return self
end
--- Sends the status to the Attack Groups.
-- @param #DESIGNATE self
-- @param Wrapper.Group#GROUP AttackGroup
@@ -533,20 +764,23 @@ do -- DESIGNATE
if self.FlashStatusMenu[AttackGroup] or ( MenuAttackGroup and ( AttackGroup:GetName() == MenuAttackGroup:GetName() ) ) then
local DetectedReport = REPORT:New( "Targets designated:\n" )
local DetectedReport = REPORT:New( "Targets ready for Designation:" )
local DetectedItems = self.Detection:GetDetectedItems()
for Index, DetectedItemData in pairs( DetectedItems ) do
local Report = self.Detection:DetectedItemReportSummary( Index, AttackGroup )
DetectedReport:Add(" - " .. Report)
for DesignateIndex, Designating in pairs( self.Designating ) do
local DetectedItem = DetectedItems[DesignateIndex]
if DetectedItem then
local Report = self.Detection:DetectedItemReportSummary( DesignateIndex, AttackGroup ):Text( ", " )
DetectedReport:Add( string.rep( "-", 140 ) )
DetectedReport:Add( " - " .. Report )
end
end
local CC = self.CC:GetPositionable()
CC:MessageToGroup( DetectedReport:Text( "\n" ), Duration, AttackGroup )
CC:MessageToGroup( DetectedReport:Text( "\n" ), Duration, AttackGroup, self.DesignateName )
local DesignationReport = REPORT:New( "Targets marked:\n" )
local DesignationReport = REPORT:New( "Marking Targets:\n" )
self.RecceSet:ForEachGroup(
function( RecceGroup )
@@ -560,34 +794,7 @@ do -- DESIGNATE
end
)
CC:MessageToGroup( DesignationReport:Text(), Duration, AttackGroup )
end
end
)
return self
end
--- Coordinates the Auto Lase.
-- @param #DESIGNATE self
-- @return #DESIGNATE
function DESIGNATE:ActivateAutoLase()
self.AttackSet:Flush()
self.AttackSet:ForEachGroup(
--- @param Wrapper.Group#GROUP GroupReport
function( AttackGroup )
local DetectedItems = self.Detection:GetDetectedItems()
for Index, DetectedItemData in pairs( DetectedItems ) do
if self.AutoLase then
if not self.Designating[Index] then
self:LaseOn( Index, self.LaseDuration )
end
end
CC:MessageToGroup( DesignationReport:Text(), Duration, AttackGroup, self.DesignateName )
end
end
)
@@ -606,66 +813,79 @@ do -- DESIGNATE
--- @param Wrapper.Group#GROUP GroupReport
function( AttackGroup )
local DesignateMenu = AttackGroup:GetState( AttackGroup, "DesignateMenu" ) -- Core.Menu#MENU_GROUP
if DesignateMenu then
DesignateMenu:Remove()
DesignateMenu = nil
self:E("Remove Menu")
end
DesignateMenu = MENU_GROUP:New( AttackGroup, "Designate" )
self:E(DesignateMenu)
AttackGroup:SetState( AttackGroup, "DesignateMenu", DesignateMenu )
self.MenuDesignate = self.MenuDesignate or {}
local MissionMenu = nil
if self.Mission then
MissionMenu = self.Mission:GetRootMenu( AttackGroup )
end
local MenuTime = timer.getTime()
self.MenuDesignate[AttackGroup] = MENU_GROUP:New( AttackGroup, self.DesignateName, MissionMenu ):SetTime( MenuTime ):SetTag( self.DesignateName )
local MenuDesignate = self.MenuDesignate[AttackGroup] -- Core.Menu#MENU_GROUP
-- Set Menu option for auto lase
if self.AutoLase then
MENU_GROUP_COMMAND:New( AttackGroup, "Auto Lase Off", DesignateMenu, self.MenuAutoLase, self, false )
MENU_GROUP_COMMAND:New( AttackGroup, "Auto Lase Off", MenuDesignate, self.MenuAutoLase, self, false ):SetTime( MenuTime ):SetTag( self.DesignateName )
else
MENU_GROUP_COMMAND:New( AttackGroup, "Auto Lase On", DesignateMenu, self.MenuAutoLase, self, true )
MENU_GROUP_COMMAND:New( AttackGroup, "Auto Lase On", MenuDesignate, self.MenuAutoLase, self, true ):SetTime( MenuTime ):SetTag( self.DesignateName )
end
local StatusMenu = MENU_GROUP:New( AttackGroup, "Status", DesignateMenu )
MENU_GROUP_COMMAND:New( AttackGroup, "Report Status 15s", StatusMenu, self.MenuStatus, self, AttackGroup, 15 )
MENU_GROUP_COMMAND:New( AttackGroup, "Report Status 30s", StatusMenu, self.MenuStatus, self, AttackGroup, 30 )
MENU_GROUP_COMMAND:New( AttackGroup, "Report Status 60s", StatusMenu, self.MenuStatus, self, AttackGroup, 60 )
local StatusMenu = MENU_GROUP:New( AttackGroup, "Status", MenuDesignate ):SetTime( MenuTime ):SetTag( self.DesignateName )
MENU_GROUP_COMMAND:New( AttackGroup, "Report Status 15s", StatusMenu, self.MenuStatus, self, AttackGroup, 15 ):SetTime( MenuTime ):SetTag( self.DesignateName )
MENU_GROUP_COMMAND:New( AttackGroup, "Report Status 30s", StatusMenu, self.MenuStatus, self, AttackGroup, 30 ):SetTime( MenuTime ):SetTag( self.DesignateName )
MENU_GROUP_COMMAND:New( AttackGroup, "Report Status 60s", StatusMenu, self.MenuStatus, self, AttackGroup, 60 ):SetTime( MenuTime ):SetTag( self.DesignateName )
if self.FlashStatusMenu[AttackGroup] then
MENU_GROUP_COMMAND:New( AttackGroup, "Flash Status Report Off", StatusMenu, self.MenuFlashStatus, self, AttackGroup, false )
MENU_GROUP_COMMAND:New( AttackGroup, "Flash Status Report Off", StatusMenu, self.MenuFlashStatus, self, AttackGroup, false ):SetTime( MenuTime ):SetTag( self.DesignateName )
else
MENU_GROUP_COMMAND:New( AttackGroup, "Flash Status Report On", StatusMenu, self.MenuFlashStatus, self, AttackGroup, true )
MENU_GROUP_COMMAND:New( AttackGroup, "Flash Status Report On", StatusMenu, self.MenuFlashStatus, self, AttackGroup, true ):SetTime( MenuTime ):SetTag( self.DesignateName )
end
local DetectedItems = self.Detection:GetDetectedItems()
for Index, DetectedItemData in pairs( DetectedItems ) do
for DesignateIndex, Designating in pairs( self.Designating ) do
local DetectedItem = self.Detection:GetDetectedItem( DesignateIndex )
if DetectedItem then
local Report = self.Detection:DetectedItemMenu( Index, AttackGroup )
if not self.Designating[Index] then
local DetectedMenu = MENU_GROUP:New( AttackGroup, Report, DesignateMenu )
MENU_GROUP_COMMAND:New( AttackGroup, "Lase target 60 secs", DetectedMenu, self.MenuLaseOn, self, Index, 60 )
MENU_GROUP_COMMAND:New( AttackGroup, "Lase target 120 secs", DetectedMenu, self.MenuLaseOn, self, Index, 120 )
MENU_GROUP_COMMAND:New( AttackGroup, "Smoke red", DetectedMenu, self.MenuSmoke, self, Index, SMOKECOLOR.Red )
MENU_GROUP_COMMAND:New( AttackGroup, "Smoke blue", DetectedMenu, self.MenuSmoke, self, Index, SMOKECOLOR.Blue )
MENU_GROUP_COMMAND:New( AttackGroup, "Smoke green", DetectedMenu, self.MenuSmoke, self, Index, SMOKECOLOR.Green )
MENU_GROUP_COMMAND:New( AttackGroup, "Smoke white", DetectedMenu, self.MenuSmoke, self, Index, SMOKECOLOR.White )
MENU_GROUP_COMMAND:New( AttackGroup, "Smoke orange", DetectedMenu, self.MenuSmoke, self, Index, SMOKECOLOR.Orange )
MENU_GROUP_COMMAND:New( AttackGroup, "Illuminate", DetectedMenu, self.MenuIlluminate, self, Index )
else
if self.Designating[Index] == "Laser" then
Report = "Lasing " .. Report
elseif self.Designating[Index] == "Smoke" then
Report = "Smoking " .. Report
elseif self.Designating[Index] == "Illuminate" then
Report = "Illuminating " .. Report
end
local DetectedMenu = MENU_GROUP:New( AttackGroup, Report, DesignateMenu )
if self.Designating[Index] == "Laser" then
MENU_GROUP_COMMAND:New( AttackGroup, "Stop lasing", DetectedMenu, self.MenuLaseOff, self, Index )
local Coord = self.Detection:GetDetectedItemCoordinate( DesignateIndex )
local ID = self.Detection:GetDetectedItemID( DesignateIndex )
local MenuText = ID .. ", " .. Coord:ToStringA2G( AttackGroup )
if Designating == "" then
MenuText = "(-) " .. MenuText
local DetectedMenu = MENU_GROUP:New( AttackGroup, MenuText, MenuDesignate ):SetTime( MenuTime ):SetTag( self.DesignateName )
MENU_GROUP_COMMAND:New( AttackGroup, "Search other target", DetectedMenu, self.MenuForget, self, DesignateIndex ):SetTime( MenuTime ):SetTag( self.DesignateName )
for LaserCode, MenuText in pairs( self.MenuLaserCodes ) do
MENU_GROUP_COMMAND:New( AttackGroup, string.format( MenuText, LaserCode ), DetectedMenu, self.MenuLaseCode, self, DesignateIndex, 60, LaserCode ):SetTime( MenuTime ):SetTag( self.DesignateName )
end
MENU_GROUP_COMMAND:New( AttackGroup, "Lase with random laser code(s)", DetectedMenu, self.MenuLaseOn, self, DesignateIndex, 60 ):SetTime( MenuTime ):SetTag( self.DesignateName )
MENU_GROUP_COMMAND:New( AttackGroup, "Smoke red", DetectedMenu, self.MenuSmoke, self, DesignateIndex, SMOKECOLOR.Red ):SetTime( MenuTime ):SetTag( self.DesignateName )
MENU_GROUP_COMMAND:New( AttackGroup, "Smoke blue", DetectedMenu, self.MenuSmoke, self, DesignateIndex, SMOKECOLOR.Blue ):SetTime( MenuTime ):SetTag( self.DesignateName )
MENU_GROUP_COMMAND:New( AttackGroup, "Smoke green", DetectedMenu, self.MenuSmoke, self, DesignateIndex, SMOKECOLOR.Green ):SetTime( MenuTime ):SetTag( self.DesignateName )
MENU_GROUP_COMMAND:New( AttackGroup, "Smoke white", DetectedMenu, self.MenuSmoke, self, DesignateIndex, SMOKECOLOR.White ):SetTime( MenuTime ):SetTag( self.DesignateName )
MENU_GROUP_COMMAND:New( AttackGroup, "Smoke orange", DetectedMenu, self.MenuSmoke, self, DesignateIndex, SMOKECOLOR.Orange ):SetTime( MenuTime ):SetTag( self.DesignateName )
MENU_GROUP_COMMAND:New( AttackGroup, "Illuminate", DetectedMenu, self.MenuIlluminate, self, DesignateIndex ):SetTime( MenuTime ):SetTag( self.DesignateName )
else
if Designating == "Laser" then
MenuText = "(L) " .. MenuText
elseif Designating == "Smoke" then
MenuText = "(S) " .. MenuText
elseif Designating == "Illuminate" then
MenuText = "(I) " .. MenuText
end
local DetectedMenu = MENU_GROUP:New( AttackGroup, MenuText, MenuDesignate ):SetTime( MenuTime ):SetTag( self.DesignateName )
if Designating == "Laser" then
MENU_GROUP_COMMAND:New( AttackGroup, "Stop lasing", DetectedMenu, self.MenuLaseOff, self, DesignateIndex ):SetTime( MenuTime ):SetTag( self.DesignateName )
else
end
end
end
end
MenuDesignate:Remove( MenuTime, self.DesignateName )
end
)
@@ -692,13 +912,23 @@ do -- DESIGNATE
end
---
-- @param #DESIGNATE self
function DESIGNATE:MenuForget( Index )
self:E("Forget")
self.Designating[Index] = nil
self:SetDesignateMenu()
end
---
-- @param #DESIGNATE self
function DESIGNATE:MenuAutoLase( AutoLase )
self:E("AutoLase")
self:SetAutoLase( AutoLase )
self:SetAutoLase( AutoLase, true )
end
---
@@ -708,7 +938,7 @@ do -- DESIGNATE
self:E("Designate through Smoke")
self.Designating[Index] = "Smoke"
self:__Smoke( 1, Index, Color )
self:Smoke( Index, Color )
end
---
@@ -729,96 +959,204 @@ do -- DESIGNATE
self:E("Designate through Lase")
self:__LaseOn( 1, Index, Duration )
self:SetDesignateMenu()
end
---
-- @param #DESIGNATE self
function DESIGNATE:MenuLaseCode( Index, Duration, LaserCode )
self:E( "Designate through Lase using " .. LaserCode )
self:__LaseOn( 1, Index, Duration, LaserCode )
self:SetDesignateMenu()
end
---
-- @param #DESIGNATE self
function DESIGNATE:MenuLaseOff( Index, Duration )
self:E("Lasing off")
self.Designating[Index] = nil
self.Designating[Index] = ""
self:__LaseOff( 1, Index )
self:SetDesignateMenu()
end
---
-- @param #DESIGNATE self
function DESIGNATE:onafterLaseOn( From, Event, To, Index, Duration )
function DESIGNATE:onafterLaseOn( From, Event, To, Index, Duration, LaserCode )
self.Designating[Index] = "Laser"
self:Lasing( Index, Duration )
self.LaseStart = timer.getTime()
self.LaseDuration = Duration
self:__Lasing( -1, Index, Duration, LaserCode )
end
---
-- @param #DESIGNATE self
-- @return #DESIGNATE
function DESIGNATE:onafterLasing( From, Event, To, Index, Duration )
function DESIGNATE:onafterLasing( From, Event, To, Index, Duration, LaserCodeRequested )
local TargetSetUnit = self.Detection:GetDetectedSet( Index )
local MarkingCount = 0
local MarkedTypes = {}
local ReportTypes = REPORT:New()
local ReportLaserCodes = REPORT:New()
TargetSetUnit:Flush()
--self:F( { Recces = self.Recces } )
for TargetUnit, RecceData in pairs( self.Recces ) do
local Recce = RecceData -- Wrapper.Unit#UNIT
self:F( { TargetUnit = TargetUnit, Recce = Recce:GetName() } )
if not Recce:IsLasing() then
local LaserCode = Recce:GetLaserCode() --(Not deleted when stopping with lasing).
local LaserCode = Recce:GetLaserCode() -- (Not deleted when stopping with lasing).
self:F( { ClearingLaserCode = LaserCode } )
self.LaserCodesUsed[LaserCode] = nil
self.Recces[TargetUnit] = nil
end
end
TargetSetUnit:ForEachUnitPerThreatLevel( 10, 0,
--- @param Wrapper.Unit#UNIT SmokeUnit
function( TargetUnit )
self:E("In procedure")
if TargetUnit:IsAlive() then
local Recce = self.Recces[TargetUnit]
if not Recce then
for RecceGroupID, RecceGroup in pairs( self.RecceSet:GetSet() ) do
for UnitID, UnitData in pairs( RecceGroup:GetUnits() or {} ) do
local RecceUnit = UnitData -- Wrapper.Unit#UNIT
if RecceUnit:IsLasing() == false then
if RecceUnit:IsDetected( TargetUnit ) and RecceUnit:IsLOS( TargetUnit ) then
local LaserCodeIndex = math.random( 1, #self.LaserCodes )
local LaserCode = self.LaserCodes[LaserCodeIndex]
if not self.LaserCodesUsed[LaserCode] then
self.LaserCodesUsed[LaserCode] = LaserCodeIndex
local Spot = RecceUnit:LaseUnit( TargetUnit, LaserCode, Duration )
local AttackSet = self.AttackSet
function Spot:OnAfterDestroyed( From, Event, To )
self:E( "Destroyed Message" )
self.Recce:MessageToSetGroup( "Target " .. TargetUnit:GetTypeName() .. " destroyed. " .. TargetSetUnit:Count() .. " targets left.", 5, AttackSet )
end
self.Recces[TargetUnit] = RecceUnit
RecceUnit:MessageToSetGroup( "Marking " .. TargetUnit:GetTypeName() .. " with laser " .. RecceUnit:GetSpot().LaserCode .. " for " .. Duration .. "s.", 5, self.AttackSet )
break
end
else
RecceUnit:MessageToSetGroup( "Can't mark " .. TargetUnit:GetTypeName(), 5, self.AttackSet )
end
else
-- The Recce is lasing, but the Target is not detected or within LOS. So stop lasing and send a report.
if not RecceUnit:IsDetected( TargetUnit ) or not RecceUnit:IsLOS( TargetUnit ) then
local Recce = self.Recces[TargetUnit] -- Wrapper.Unit#UNIT
if Recce then
Recce:LaseOff()
Recce:MessageToGroup( "Target " .. TargetUnit:GetTypeName() "out of LOS. Cancelling lase!", 5, self.AttackSet )
end
end
end
end
end
else
Recce:MessageToSetGroup( "Marking " .. TargetUnit:GetTypeName() .. " with laser " .. Recce.LaserCode .. ".", 5, self.AttackSet )
end
-- If a specific lasercode is requested, we disable one active lase!
if LaserCodeRequested then
for TargetUnit, RecceData in pairs( self.Recces ) do -- We break after the first has been processed.
local Recce = RecceData -- Wrapper.Unit#UNIT
self:F( { TargetUnit = TargetUnit, Recce = Recce:GetName() } )
if Recce:IsLasing() then
-- When a Recce is lasing, we switch the lasing off, and clear the references to the lasing in the DESIGNATE class.
Recce:LaseOff() -- Switch off the lasing.
local LaserCode = Recce:GetLaserCode() -- (Not deleted when stopping with lasing).
self:F( { ClearingLaserCode = LaserCode } )
self.LaserCodesUsed[LaserCode] = nil
self.Recces[TargetUnit] = nil
break
end
end
)
self:__Lasing( 15, Index, Duration )
end
self:SetDesignateMenu()
if self.AutoLase or ( not self.AutoLase and ( self.LaseStart + Duration >= timer.getTime() ) ) then
TargetSetUnit:ForEachUnitPerThreatLevel( 10, 0,
--- @param Wrapper.Unit#UNIT SmokeUnit
function( TargetUnit )
self:F( { TargetUnit = TargetUnit:GetName() } )
if MarkingCount < self.MaximumMarkings then
if TargetUnit:IsAlive() then
local Recce = self.Recces[TargetUnit]
if not Recce then
self:E( "Lasing..." )
self.RecceSet:Flush()
for RecceGroupID, RecceGroup in pairs( self.RecceSet:GetSet() ) do
for UnitID, UnitData in pairs( RecceGroup:GetUnits() or {} ) do
local RecceUnit = UnitData -- Wrapper.Unit#UNIT
local RecceUnitDesc = RecceUnit:GetDesc()
--self:F( { RecceUnit = RecceUnit:GetName(), RecceDescription = RecceUnitDesc } )
if RecceUnit:IsLasing() == false then
--self:F( { IsDetected = RecceUnit:IsDetected( TargetUnit ), IsLOS = RecceUnit:IsLOS( TargetUnit ) } )
if RecceUnit:IsDetected( TargetUnit ) and RecceUnit:IsLOS( TargetUnit ) then
local LaserCodeIndex = math.random( 1, #self.LaserCodes )
local LaserCode = self.LaserCodes[LaserCodeIndex]
--self:F( { LaserCode = LaserCode, LaserCodeUsed = self.LaserCodesUsed[LaserCode] } )
if LaserCodeRequested and LaserCodeRequested ~= LaserCode then
LaserCode = LaserCodeRequested
LaserCodeRequested = nil
end
if not self.LaserCodesUsed[LaserCode] then
self.LaserCodesUsed[LaserCode] = LaserCodeIndex
local Spot = RecceUnit:LaseUnit( TargetUnit, LaserCode, Duration )
local AttackSet = self.AttackSet
function Spot:OnAfterDestroyed( From, Event, To )
self:E( "Destroyed Message" )
self.Recce:ToSetGroup( "Target " .. TargetUnit:GetTypeName() .. " destroyed. " .. TargetSetUnit:Count() .. " targets left.", 5, AttackSet, self.DesignateName )
end
self.Recces[TargetUnit] = RecceUnit
RecceUnit:MessageToSetGroup( "Marking " .. TargetUnit:GetTypeName() .. " with laser " .. RecceUnit:GetSpot().LaserCode .. " for " .. Duration .. "s.", 5, self.AttackSet, self.DesignateName )
-- OK. We have assigned for the Recce a TargetUnit. We can exit the function.
MarkingCount = MarkingCount + 1
local TargetUnitType = TargetUnit:GetTypeName()
if not MarkedTypes[TargetUnitType] then
MarkedTypes[TargetUnitType] = true
ReportTypes:Add(TargetUnitType)
end
ReportLaserCodes:Add(RecceUnit.LaserCode)
return
end
else
--RecceUnit:MessageToSetGroup( "Can't mark " .. TargetUnit:GetTypeName(), 5, self.AttackSet )
end
else
-- The Recce is lasing, but the Target is not detected or within LOS. So stop lasing and send a report.
if not RecceUnit:IsDetected( TargetUnit ) or not RecceUnit:IsLOS( TargetUnit ) then
local Recce = self.Recces[TargetUnit] -- Wrapper.Unit#UNIT
if Recce then
Recce:LaseOff()
Recce:MessageToSetGroup( "Target " .. TargetUnit:GetTypeName() "out of LOS. Cancelling lase!", 5, self.AttackSet, self.DesignateName )
end
else
MarkingCount = MarkingCount + 1
local TargetUnitType = TargetUnit:GetTypeName()
if not MarkedTypes[TargetUnitType] then
MarkedTypes[TargetUnitType] = true
ReportTypes:Add(TargetUnitType)
end
ReportLaserCodes:Add(RecceUnit.LaserCode)
end
end
end
end
else
MarkingCount = MarkingCount + 1
local TargetUnitType = TargetUnit:GetTypeName()
if not MarkedTypes[TargetUnitType] then
MarkedTypes[TargetUnitType] = true
ReportTypes:Add(TargetUnitType)
end
ReportLaserCodes:Add(Recce.LaserCode)
--Recce:MessageToSetGroup( self.DesignateName .. ": Marking " .. TargetUnit:GetTypeName() .. " with laser " .. Recce.LaserCode .. ".", 5, self.AttackSet )
end
end
end
end
)
local MarkedTypesText = ReportTypes:Text(', ')
local MarkedLaserCodesText = ReportLaserCodes:Text(', ')
for MarkedType, MarketCount in pairs( MarkedTypes ) do
self.CC:GetPositionable():MessageToSetGroup( "Marking " .. MarkingCount .. " x " .. MarkedTypesText .. " with lasers " .. MarkedLaserCodesText .. ".", 5, self.AttackSet, self.DesignateName )
end
self:__Lasing( -30, Index, Duration, LaserCodeRequested )
self:SetDesignateMenu()
else
self:__LaseOff( 1 )
end
end
@@ -830,7 +1168,7 @@ do -- DESIGNATE
local CC = self.CC:GetPositionable()
if CC then
CC:MessageToSetGroup( "Stopped lasing.", 5, self.AttackSet )
CC:MessageToSetGroup( "Stopped lasing.", 5, self.AttackSet, self.DesignateName )
end
local TargetSetUnit = self.Detection:GetDetectedSet( Index )
@@ -839,7 +1177,7 @@ do -- DESIGNATE
for TargetID, RecceData in pairs( Recces ) do
local Recce = RecceData -- Wrapper.Unit#UNIT
Recce:MessageToSetGroup( "Stopped lasing " .. Recce:GetSpot().Target:GetTypeName() .. ".", 5, self.AttackSet )
Recce:MessageToSetGroup( "Stopped lasing " .. Recce:GetSpot().Target:GetTypeName() .. ".", 5, self.AttackSet, self.DesignateName )
Recce:LaseOff()
end
@@ -859,19 +1197,29 @@ do -- DESIGNATE
local TargetSetUnit = self.Detection:GetDetectedSet( Index )
local TargetSetUnitCount = TargetSetUnit:Count()
TargetSetUnit:ForEachUnit(
local MarkedCount = 0
TargetSetUnit:ForEachUnitPerThreatLevel( 10, 0,
--- @param Wrapper.Unit#UNIT SmokeUnit
function( SmokeUnit )
self:E("In procedure")
if math.random( 1, TargetSetUnitCount ) == math.random( 1, TargetSetUnitCount ) then
if MarkedCount < self.MaximumMarkings then
MarkedCount = MarkedCount + 1
self:E( "Smoking ..." )
local RecceGroup = self.RecceSet:FindNearestGroupFromPointVec2(SmokeUnit:GetPointVec2())
local RecceUnit = RecceGroup:GetUnit( 1 )
if RecceUnit then
RecceUnit:MessageToSetGroup( "Smoking " .. SmokeUnit:GetTypeName() .. ".", 5, self.AttackSet )
SCHEDULER:New( self,
RecceUnit:MessageToSetGroup( "Smoking " .. SmokeUnit:GetTypeName() .. ".", 5, self.AttackSet, self.DesignateName )
self.MarkScheduler:Schedule( self,
function()
if SmokeUnit:IsAlive() then
SmokeUnit:Smoke( Color, 150 )
SmokeUnit:Smoke( Color, 50, 2 )
end
self:Done( Index )
end, {}, math.random( 5, 20 )
@@ -896,8 +1244,8 @@ do -- DESIGNATE
local RecceGroup = self.RecceSet:FindNearestGroupFromPointVec2(TargetUnit:GetPointVec2())
local RecceUnit = RecceGroup:GetUnit( 1 )
if RecceUnit then
RecceUnit:MessageToSetGroup( "Illuminating " .. TargetUnit:GetTypeName() .. ".", 5, self.AttackSet )
SCHEDULER:New( self,
RecceUnit:MessageToSetGroup( "Illuminating " .. TargetUnit:GetTypeName() .. ".", 5, self.AttackSet, self.DesignateName )
self.MarkScheduler:Schedule( self,
function()
if TargetUnit:IsAlive() then
TargetUnit:GetPointVec3():AddY(300):IlluminationBomb()

File diff suppressed because it is too large Load Diff

View File

@@ -875,7 +875,7 @@ function ESCORT:_AttackTarget( DetectedItemID )
end, Tasks
)
Tasks[#Tasks+1] = EscortGroup:TaskFunction( 1, 2, "_Resume", { "''" } )
Tasks[#Tasks+1] = EscortGroup:TaskFunction( "_Resume", { "''" } )
EscortGroup:SetTask(
EscortGroup:TaskCombo(
@@ -1161,19 +1161,23 @@ function ESCORT:_ReportTargetsScheduler()
for ClientEscortGroupName, EscortGroupData in pairs( self.EscortClient._EscortGroups ) do
local ClientEscortTargets = EscortGroupData.Detection
--local EscortUnit = EscortGroupData:GetUnit( 1 )
for DetectedItemID, DetectedItem in ipairs( DetectedItems ) do
for DetectedItemID, DetectedItem in pairs( DetectedItems ) do
self:E( { DetectedItemID, DetectedItem } )
-- Remove the sub menus of the Attack menu of the Escort for the EscortGroup.
local DetectedItemReportSummary = self.Detection:DetectedItemReportSummary( DetectedItemID, EscortGroupData )
local DetectedItemReportSummary = self.Detection:DetectedItemReportSummary( DetectedItemID, EscortGroupData.EscortGroup, _DATABASE:GetPlayerSettings( self.EscortClient:GetPlayerName() ) )
if ClientEscortGroupName == EscortGroupName then
DetectedMsgs[#DetectedMsgs+1] = DetectedItemReportSummary
local DetectedMsg = DetectedItemReportSummary:Text("\n")
DetectedMsgs[#DetectedMsgs+1] = DetectedMsg
self:T( DetectedMsg )
MENU_CLIENT_COMMAND:New( self.EscortClient,
DetectedItemReportSummary,
DetectedMsg,
self.EscortMenuAttackNearbyTargets,
ESCORT._AttackTarget,
self,
@@ -1182,10 +1186,12 @@ function ESCORT:_ReportTargetsScheduler()
else
if self.EscortMenuTargetAssistance then
self:T( DetectedItemReportSummary )
local DetectedMsg = DetectedItemReportSummary:Text("\n")
self:T( DetectedMsg )
local MenuTargetAssistance = MENU_CLIENT:New( self.EscortClient, EscortGroupData.EscortName, self.EscortMenuTargetAssistance )
MENU_CLIENT_COMMAND:New( self.EscortClient,
DetectedItemReportSummary,
DetectedMsg,
MenuTargetAssistance,
ESCORT._AssistTarget,
self,
@@ -1201,7 +1207,7 @@ function ESCORT:_ReportTargetsScheduler()
end
self:E( DetectedMsgs )
if DetectedTargets then
self.EscortGroup:MessageToClient( "Detected targets:\n" .. table.concat( DetectedMsgs, "\n" ), 20, self.EscortClient )
self.EscortGroup:MessageToClient( "Reporting detected targets:\n" .. table.concat( DetectedMsgs, "\n" ), 20, self.EscortClient )
else
self.EscortGroup:MessageToClient( "No targets detected.", 10, self.EscortClient )
end

View File

@@ -442,7 +442,7 @@ function MISSILETRAINER._MenuMessages( MenuParameters )
if MenuParameters.Distance ~= nil then
self.Distance = MenuParameters.Distance
MESSAGE:New( "Hit detection distance set to " .. self.Distance * 1000 .. " meters", 15, "Menu" ):ToAll()
MESSAGE:New( "Hit detection distance set to " .. ( self.Distance * 1000 ) .. " meters", 15, "Menu" ):ToAll()
end
end
@@ -570,72 +570,76 @@ function MISSILETRAINER:_TrackMissiles()
for ClientDataID, ClientData in pairs( self.TrackingMissiles ) do
local Client = ClientData.Client
self:T2( { Client:GetName() } )
if Client and Client:IsAlive() then
for MissileDataID, MissileData in pairs( ClientData.MissileData ) do
self:T3( MissileDataID )
local TrainerSourceUnit = MissileData.TrainerSourceUnit
local TrainerWeapon = MissileData.TrainerWeapon
local TrainerTargetUnit = MissileData.TrainerTargetUnit
local TrainerWeaponTypeName = MissileData.TrainerWeaponTypeName
local TrainerWeaponLaunched = MissileData.TrainerWeaponLaunched
for MissileDataID, MissileData in pairs( ClientData.MissileData ) do
self:T3( MissileDataID )
if Client and Client:IsAlive() and TrainerSourceUnit and TrainerSourceUnit:IsAlive() and TrainerWeapon and TrainerWeapon:isExist() and TrainerTargetUnit and TrainerTargetUnit:IsAlive() then
local PositionMissile = TrainerWeapon:getPosition().p
local TargetVec3 = Client:GetVec3()
local Distance = ( ( PositionMissile.x - TargetVec3.x )^2 +
( PositionMissile.y - TargetVec3.y )^2 +
( PositionMissile.z - TargetVec3.z )^2
) ^ 0.5 / 1000
if Distance <= self.Distance then
-- Hit alert
TrainerWeapon:destroy()
if self.MessagesOnOff == true and self.AlertsHitsOnOff == true then
self:T( "killed" )
local Message = MESSAGE:New(
string.format( "%s launched by %s killed %s",
TrainerWeapon:getTypeName(),
TrainerSourceUnit:GetTypeName(),
TrainerTargetUnit:GetPlayerName()
), 15, "Hit Alert" )
if self.AlertsToAll == true then
Message:ToAll()
else
Message:ToClient( Client )
local TrainerSourceUnit = MissileData.TrainerSourceUnit
local TrainerWeapon = MissileData.TrainerWeapon
local TrainerTargetUnit = MissileData.TrainerTargetUnit
local TrainerWeaponTypeName = MissileData.TrainerWeaponTypeName
local TrainerWeaponLaunched = MissileData.TrainerWeaponLaunched
if Client and Client:IsAlive() and TrainerSourceUnit and TrainerSourceUnit:IsAlive() and TrainerWeapon and TrainerWeapon:isExist() and TrainerTargetUnit and TrainerTargetUnit:IsAlive() then
local PositionMissile = TrainerWeapon:getPosition().p
local TargetVec3 = Client:GetVec3()
local Distance = ( ( PositionMissile.x - TargetVec3.x )^2 +
( PositionMissile.y - TargetVec3.y )^2 +
( PositionMissile.z - TargetVec3.z )^2
) ^ 0.5 / 1000
if Distance <= self.Distance then
-- Hit alert
TrainerWeapon:destroy()
if self.MessagesOnOff == true and self.AlertsHitsOnOff == true then
self:T( "killed" )
local Message = MESSAGE:New(
string.format( "%s launched by %s killed %s",
TrainerWeapon:getTypeName(),
TrainerSourceUnit:GetTypeName(),
TrainerTargetUnit:GetPlayerName()
), 15, "Hit Alert" )
if self.AlertsToAll == true then
Message:ToAll()
else
Message:ToClient( Client )
end
MissileData = nil
table.remove( ClientData.MissileData, MissileDataID )
self:T(ClientData.MissileData)
end
end
else
if not ( TrainerWeapon and TrainerWeapon:isExist() ) then
if self.MessagesOnOff == true and self.AlertsLaunchesOnOff == true then
-- Weapon does not exist anymore. Delete from Table
local Message = MESSAGE:New(
string.format( "%s launched by %s self destructed!",
TrainerWeaponTypeName,
TrainerSourceUnit:GetTypeName()
), 5, "Tracking" )
if self.AlertsToAll == true then
Message:ToAll()
else
Message:ToClient( Client )
end
end
MissileData = nil
table.remove( ClientData.MissileData, MissileDataID )
self:T(ClientData.MissileData)
self:T( ClientData.MissileData )
end
end
else
if not ( TrainerWeapon and TrainerWeapon:isExist() ) then
if self.MessagesOnOff == true and self.AlertsLaunchesOnOff == true then
-- Weapon does not exist anymore. Delete from Table
local Message = MESSAGE:New(
string.format( "%s launched by %s self destructed!",
TrainerWeaponTypeName,
TrainerSourceUnit:GetTypeName()
), 5, "Tracking" )
if self.AlertsToAll == true then
Message:ToAll()
else
Message:ToClient( Client )
end
end
MissileData = nil
table.remove( ClientData.MissileData, MissileDataID )
self:T( ClientData.MissileData )
end
end
else
self.TrackingMissiles[ClientDataID] = nil
end
end
@@ -651,7 +655,7 @@ function MISSILETRAINER:_TrackMissiles()
for ClientDataID, ClientData in pairs( self.TrackingMissiles ) do
local Client = ClientData.Client
self:T2( { Client:GetName() } )
--self:T2( { Client:GetName() } )
ClientData.MessageToClient = ""
@@ -661,7 +665,7 @@ function MISSILETRAINER:_TrackMissiles()
for TrackingDataID, TrackingData in pairs( self.TrackingMissiles ) do
for MissileDataID, MissileData in pairs( TrackingData.MissileData ) do
self:T3( MissileDataID )
--self:T3( MissileDataID )
local TrainerSourceUnit = MissileData.TrainerSourceUnit
local TrainerWeapon = MissileData.TrainerWeapon

View File

@@ -0,0 +1,305 @@
--- **Functional** -- The PROTECT class handles the protection of objects, which can be zones, units, scenery.
--
-- ===
--
-- ### Author: **Sven Van de Velde (FlightControl)**
-- ### Contributions: **MillerTime**
--
-- ====
--
-- @module Protect
--- @type PROTECT.__ Methods which are not intended for mission designers, but which are used interally by the moose designer :-)
-- @extends Core.Fsm#FSM
--- @type PROTECT
-- @extends #PROTECT.__
--- # PROTECT, extends @{Base#BASE}
--
-- @field #PROTECT
PROTECT = {
ClassName = "PROTECT",
}
--- Get the ProtectZone
-- @param #PROTECT self
-- @return Core.Zone#ZONE_BASE
function PROTECT:GetProtectZone()
return self.ProtectZone
end
--- Get the name of the ProtectZone
-- @param #PROTECT self
-- @return #string
function PROTECT:GetProtectZoneName()
return self.ProtectZone:GetName()
end
--- Set the owning coalition of the zone.
-- @param #PROTECT self
-- @param DCSCoalition.DCSCoalition#coalition Coalition
function PROTECT:SetCoalition( Coalition )
self.Coalition = Coalition
end
--- Get the owning coalition of the zone.
-- @param #PROTECT self
-- @return DCSCoalition.DCSCoalition#coalition Coalition.
function PROTECT:GetCoalition()
return self.Coalition
end
--- Get the owning coalition name of the zone.
-- @param #PROTECT self
-- @return #string Coalition name.
function PROTECT:GetCoalitionName()
if self.Coalition == coalition.side.BLUE then
return "Blue"
end
if self.Coalition == coalition.side.RED then
return "Red"
end
if self.Coalition == coalition.side.NEUTRAL then
return "Neutral"
end
return ""
end
function PROTECT:IsGuarded()
local IsGuarded = self.ProtectZone:IsAllInZoneOfCoalition( self.Coalition )
self:E( { IsGuarded = IsGuarded } )
return IsGuarded
end
function PROTECT:IsCaptured()
local IsCaptured = self.ProtectZone:IsAllInZoneOfOtherCoalition( self.Coalition )
self:E( { IsCaptured = IsCaptured } )
return IsCaptured
end
function PROTECT:IsAttacked()
local IsAttacked = self.ProtectZone:IsSomeInZoneOfCoalition( self.Coalition )
self:E( { IsAttacked = IsAttacked } )
return IsAttacked
end
function PROTECT:IsEmpty()
local IsEmpty = self.ProtectZone:IsNoneInZone()
self:E( { IsEmpty = IsEmpty } )
return IsEmpty
end
--- Check if the units are still alive.
-- @param #PROTECT self
function PROTECT:AreProtectUnitsAlive()
local IsAlive = false
local UnitSet = self.ProtectUnitSet
UnitSet:Flush()
local UnitList = UnitSet:GetSet()
for UnitID, ProtectUnit in pairs( UnitList ) do
local IsUnitAlive = ProtectUnit:IsAlive()
if IsUnitAlive == true then
IsAlive = true
break
end
end
return IsAlive
end
--- Check if the statics are still alive.
-- @param #PROTECT self
function PROTECT:AreProtectStaticsAlive()
local IsAlive = false
local StaticSet = self.ProtectStaticSet
StaticSet:Flush()
local StaticList = StaticSet:GetSet()
for UnitID, ProtectStatic in pairs( StaticList ) do
local IsStaticAlive = ProtectStatic:IsAlive()
if IsStaticAlive == true then
IsAlive = true
break
end
end
return IsAlive
end
--- Check if there is a capture unit in the zone.
-- @param #PROTECT self
function PROTECT:IsCaptureUnitInZone()
local CaptureUnitSet = self.CaptureUnitSet
CaptureUnitSet:Flush()
local IsInZone = self.CaptureUnitSet:IsPartiallyInZone( self.ProtectZone )
self:E({IsInZone = IsInZone})
return IsInZone
end
--- Smoke.
-- @param #PROTECT self
-- @param #SMOKECOLOR.Color SmokeColor
function PROTECT:Smoke( SmokeColor )
self.SmokeColor = SmokeColor
end
--- Flare.
-- @param #PROTECT self
-- @param #SMOKECOLOR.Color FlareColor
function PROTECT:Flare( FlareColor )
self.ProtectZone:FlareZone( FlareColor, math.random( 1, 360 ) )
end
--- Mark.
-- @param #PROTECT self
function PROTECT:Mark()
local Coord = self.ProtectZone:GetCoordinate()
local ZoneName = self:GetProtectZoneName()
local State = self:GetState()
if self.MarkRed and self.MarkBlue then
self:E( { MarkRed = self.MarkRed, MarkBlue = self.MarkBlue } )
Coord:RemoveMark( self.MarkRed )
Coord:RemoveMark( self.MarkBlue )
end
if self.Coalition == coalition.side.BLUE then
self.MarkBlue = Coord:MarkToCoalitionBlue( "Guard Zone: " .. ZoneName .. "\nStatus: " .. State )
self.MarkRed = Coord:MarkToCoalitionRed( "Capture Zone: " .. ZoneName .. "\nStatus: " .. State )
else
self.MarkRed = Coord:MarkToCoalitionRed( "Guard Zone: " .. ZoneName .. "\nStatus: " .. State )
self.MarkBlue = Coord:MarkToCoalitionBlue( "Capture Zone: " .. ZoneName .. "\nStatus: " .. State )
end
end
--- Bound.
-- @param #PROTECT self
function PROTECT:onafterStart()
self:ScheduleRepeat( 5, 15, 0.1, nil, self.StatusCoalition, self )
self:ScheduleRepeat( 5, 15, 0.1, nil, self.StatusZone, self )
self:ScheduleRepeat( 10, 15, 0, nil, self.StatusSmoke, self )
end
--- Bound.
-- @param #PROTECT self
function PROTECT:onenterGuarded()
if self.Coalition == coalition.side.BLUE then
--elf.ProtectZone:BoundZone( 12, country.id.USA )
else
--self.ProtectZone:BoundZone( 12, country.id.RUSSIA )
end
self:Mark()
end
function PROTECT:onenterCaptured()
local NewCoalition = self.ProtectZone:GetCoalition()
self:E( { NewCoalition = NewCoalition } )
self:SetCoalition( NewCoalition )
self:Mark()
end
function PROTECT:onenterEmpty()
self:Mark()
end
function PROTECT:onenterAttacked()
self:Mark()
end
--- Check status Coalition ownership.
-- @param #PROTECT self
function PROTECT:StatusCoalition()
self:E( { State = self:GetState() } )
self.ProtectZone:Scan()
if self:IsGuarded() then
self:Guard()
else
if self:IsCaptured() then
self:Capture()
end
end
end
--- Check status Zone.
-- @param #PROTECT self
function PROTECT:StatusZone()
self:E( { State = self:GetState() } )
self.ProtectZone:Scan()
if self:IsAttacked() then
self:Attack()
else
if self:IsEmpty() then
self:Empty()
end
end
end
--- Check status Smoke.
-- @param #PROTECT self
function PROTECT:StatusSmoke()
local CurrentTime = timer.getTime()
if self.SmokeTime == nil or self.SmokeTime + 300 <= CurrentTime then
if self.SmokeColor then
self.ProtectZone:GetCoordinate():Smoke( self.SmokeColor )
--self.SmokeColor = nil
self.SmokeTime = CurrentTime
end
end
end

File diff suppressed because it is too large Load Diff

View File

@@ -102,7 +102,9 @@
-- A mission has goals and achievements. The scoring system provides an API to set additional scores when a goal or achievement event happens.
-- Use the method @{#SCORING.AddGoalScore}() to add a score for a Player at any time in your mission.
--
-- ## 1.5) Configure fratricide level.
-- ## 1.5) (Decommissioned) Configure fratricide level.
--
-- **This functionality is decomissioned until the DCS bug concerning Unit:destroy() not being functional in multi player for player units has been fixed by ED**.
--
-- When a player commits too much damage to friendlies, his penalty score will reach a certain level.
-- Use the method @{#SCORING.SetFratricide}() to define the level when a player gets kicked.
@@ -258,7 +260,7 @@ function SCORING:New( GameName )
-- Configure Messages
self:SetMessagesToAll()
self:SetMessagesHit( true )
self:SetMessagesHit( false )
self:SetMessagesDestroy( true )
self:SetMessagesScore( true )
self:SetMessagesZone( true )
@@ -294,7 +296,7 @@ end
-- @param #string DisplayMessagePrefix (Default="Scoring: ") The scoring prefix string.
-- @return #SCORING
function SCORING:SetDisplayMessagePrefix( DisplayMessagePrefix )
self.DisplayMessagePrefix = DisplayMessagePrefix or "Scoring: "
self.DisplayMessagePrefix = DisplayMessagePrefix or ""
return self
end
@@ -608,14 +610,15 @@ function SCORING:_AddPlayerFromUnit( UnitData )
if self.Players[PlayerName].UnitCoalition ~= UnitCoalition then
self.Players[PlayerName].Penalty = self.Players[PlayerName].Penalty + 50
self.Players[PlayerName].PenaltyCoalition = self.Players[PlayerName].PenaltyCoalition + 1
MESSAGE:New( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' changed coalition from " .. _SCORINGCoalition[self.Players[PlayerName].UnitCoalition] .. " to " .. _SCORINGCoalition[UnitCoalition] ..
MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' changed coalition from " .. _SCORINGCoalition[self.Players[PlayerName].UnitCoalition] .. " to " .. _SCORINGCoalition[UnitCoalition] ..
"(changed " .. self.Players[PlayerName].PenaltyCoalition .. " times the coalition). 50 Penalty points added.",
2
MESSAGE.Type.Information
):ToAll()
self:ScoreCSV( PlayerName, "", "COALITION_PENALTY", 1, -50, self.Players[PlayerName].UnitName, _SCORINGCoalition[self.Players[PlayerName].UnitCoalition], _SCORINGCategory[self.Players[PlayerName].UnitCategory], self.Players[PlayerName].UnitType,
UnitName, _SCORINGCoalition[UnitCoalition], _SCORINGCategory[UnitCategory], UnitData:GetTypeName() )
end
end
self.Players[PlayerName].UnitName = UnitName
self.Players[PlayerName].UnitCoalition = UnitCoalition
self.Players[PlayerName].UnitCategory = UnitCategory
@@ -624,26 +627,59 @@ function SCORING:_AddPlayerFromUnit( UnitData )
self.Players[PlayerName].ThreatLevel = UnitThreatLevel
self.Players[PlayerName].ThreatType = UnitThreatType
-- TODO: DCS bug concerning Units with skill level client don't get destroyed in multi player. This logic is deactivated until this bug gets fixed.
--[[
if self.Players[PlayerName].Penalty > self.Fratricide * 0.50 then
if self.Players[PlayerName].PenaltyWarning < 1 then
MESSAGE:New( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "': WARNING! If you continue to commit FRATRICIDE and have a PENALTY score higher than " .. self.Fratricide .. ", you will be COURT MARTIALED and DISMISSED from this mission! \nYour total penalty is: " .. self.Players[PlayerName].Penalty,
30
MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "': WARNING! If you continue to commit FRATRICIDE and have a PENALTY score higher than " .. self.Fratricide .. ", you will be COURT MARTIALED and DISMISSED from this mission! \nYour total penalty is: " .. self.Players[PlayerName].Penalty,
MESSAGE.Type.Information
):ToAll()
self.Players[PlayerName].PenaltyWarning = self.Players[PlayerName].PenaltyWarning + 1
end
end
if self.Players[PlayerName].Penalty > self.Fratricide then
--UnitData:Destroy()
MESSAGE:New( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' committed FRATRICIDE, he will be COURT MARTIALED and is DISMISSED from this mission!",
10
MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' committed FRATRICIDE, he will be COURT MARTIALED and is DISMISSED from this mission!",
MESSAGE.Type.Information
):ToAll()
UnitData:GetGroup():Destroy()
end
--]]
end
end
--- Add a goal score for a player.
-- The method takes the Player name for which the Goal score needs to be set.
-- The GoalTag is a string or identifier that is taken into the CSV file scoring log to identify the goal.
-- A free text can be given that is shown to the players.
-- The Score can be both positive and negative.
-- @param #SCORING self
-- @param #string PlayerName The name of the Player.
-- @param #string GoalTag The string or identifier that is used in the CSV file to identify the goal (sort or group later in Excel).
-- @param #string Text A free text that is shown to the players.
-- @param #number Score The score can be both positive or negative ( Penalty ).
function SCORING:AddGoalScorePlayer( PlayerName, GoalTag, Text, Score )
self:E( { PlayerName, PlayerName, GoalTag, Text, Score } )
-- PlayerName can be nil, if the Unit with the player crashed or due to another reason.
if PlayerName then
local PlayerData = self.Players[PlayerName]
PlayerData.Goals[GoalTag] = PlayerData.Goals[GoalTag] or { Score = 0 }
PlayerData.Goals[GoalTag].Score = PlayerData.Goals[GoalTag].Score + Score
PlayerData.Score = PlayerData.Score + Score
MESSAGE:NewType( self.DisplayMessagePrefix .. Text, MESSAGE.Type.Information ):ToAll()
self:ScoreCSV( PlayerName, "", "GOAL_" .. string.upper( GoalTag ), 1, Score, nil )
end
end
--- Add a goal score for a player.
-- The method takes the PlayerUnit for which the Goal score needs to be set.
-- The GoalTag is a string or identifier that is taken into the CSV file scoring log to identify the goal.
@@ -668,7 +704,7 @@ function SCORING:AddGoalScore( PlayerUnit, GoalTag, Text, Score )
PlayerData.Goals[GoalTag].Score = PlayerData.Goals[GoalTag].Score + Score
PlayerData.Score = PlayerData.Score + Score
MESSAGE:New( self.DisplayMessagePrefix .. Text, 30 ):ToAll()
MESSAGE:NewType( self.DisplayMessagePrefix .. Text, MESSAGE.Type.Information ):ToAll()
self:ScoreCSV( PlayerName, "", "GOAL_" .. string.upper( GoalTag ), 1, Score, PlayerUnit:GetName() )
end
@@ -704,12 +740,45 @@ function SCORING:_AddMissionTaskScore( Mission, PlayerUnit, Text, Score )
PlayerData.Score = self.Players[PlayerName].Score + Score
PlayerData.Mission[MissionName].ScoreTask = self.Players[PlayerName].Mission[MissionName].ScoreTask + Score
MESSAGE:New( self.DisplayMessagePrefix .. MissionName .. " : " .. Text .. " Score: " .. Score, 30 ):ToAll()
MESSAGE:NewType( self.DisplayMessagePrefix .. MissionName .. " : " .. Text .. " Score: " .. Score, MESSAGE.Type.Information ):ToAll()
self:ScoreCSV( PlayerName, "", "TASK_" .. MissionName:gsub( ' ', '_' ), 1, Score, PlayerUnit:GetName() )
end
end
--- Registers Scores the players completing a Mission Task.
-- @param #SCORING self
-- @param Tasking.Mission#MISSION Mission
-- @param #string PlayerName
-- @param #string Text
-- @param #number Score
function SCORING:_AddMissionGoalScore( Mission, PlayerName, Text, Score )
local MissionName = Mission:GetName()
self:E( { Mission:GetName(), PlayerName, Text, Score } )
-- PlayerName can be nil, if the Unit with the player crashed or due to another reason.
if PlayerName then
local PlayerData = self.Players[PlayerName]
if not PlayerData.Mission[MissionName] then
PlayerData.Mission[MissionName] = {}
PlayerData.Mission[MissionName].ScoreTask = 0
PlayerData.Mission[MissionName].ScoreMission = 0
end
self:T( PlayerName )
self:T( PlayerData.Mission[MissionName] )
PlayerData.Score = self.Players[PlayerName].Score + Score
PlayerData.Mission[MissionName].ScoreTask = self.Players[PlayerName].Mission[MissionName].ScoreTask + Score
MESSAGE:NewType( string.format( "%s%s: %s! Player %s receives %d score!", self.DisplayMessagePrefix, MissionName, Text, PlayerName, Score ), MESSAGE.Type.Information ):ToAll()
self:ScoreCSV( PlayerName, "", "TASK_" .. MissionName:gsub( ' ', '_' ), 1, Score )
end
end
--- Registers Mission Scores for possible multiple players that contributed in the Mission.
-- @param #SCORING self
@@ -732,9 +801,9 @@ function SCORING:_AddMissionScore( Mission, Text, Score )
PlayerData.Score = PlayerData.Score + Score
PlayerData.Mission[MissionName].ScoreMission = PlayerData.Mission[MissionName].ScoreMission + Score
MESSAGE:New( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' has " .. Text .. " in Mission '" .. MissionName .. "'. " ..
MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' has " .. Text .. " in Mission '" .. MissionName .. "'. " ..
Score .. " mission score!",
60 ):ToAll()
MESSAGE.Type.Information ):ToAll()
self:ScoreCSV( PlayerName, "", "MISSION_" .. MissionName:gsub( ' ', '_' ), 1, Score )
end
@@ -902,19 +971,19 @@ function SCORING:_EventOnHit( Event )
if TargetPlayerName ~= nil then -- It is a player hitting another player ...
MESSAGE
:New( self.DisplayMessagePrefix .. "Player '" .. InitPlayerName .. "' hit friendly player '" .. TargetPlayerName .. "' " ..
:NewType( self.DisplayMessagePrefix .. "Player '" .. InitPlayerName .. "' hit friendly player '" .. TargetPlayerName .. "' " ..
TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.PenaltyHit .. " times. " ..
"Penalty: -" .. PlayerHit.Penalty .. ". Score Total:" .. Player.Score - Player.Penalty,
2
MESSAGE.Type.Update
)
:ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() )
:ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() )
else
MESSAGE
:New( self.DisplayMessagePrefix .. "Player '" .. InitPlayerName .. "' hit a friendly target " ..
:NewType( self.DisplayMessagePrefix .. "Player '" .. InitPlayerName .. "' hit friendly target " ..
TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.PenaltyHit .. " times. " ..
"Penalty: -" .. PlayerHit.Penalty .. ". Score Total:" .. Player.Score - Player.Penalty,
2
MESSAGE.Type.Update
)
:ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() )
:ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() )
@@ -926,19 +995,19 @@ function SCORING:_EventOnHit( Event )
PlayerHit.ScoreHit = PlayerHit.ScoreHit + 1
if TargetPlayerName ~= nil then -- It is a player hitting another player ...
MESSAGE
:New( self.DisplayMessagePrefix .. "Player '" .. InitPlayerName .. "' hit enemy player '" .. TargetPlayerName .. "' " ..
:NewType( self.DisplayMessagePrefix .. "Player '" .. InitPlayerName .. "' hit enemy player '" .. TargetPlayerName .. "' " ..
TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.ScoreHit .. " times. " ..
"Score: " .. PlayerHit.Score .. ". Score Total:" .. Player.Score - Player.Penalty,
2
MESSAGE.Type.Update
)
:ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() )
:ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() )
else
MESSAGE
:New( self.DisplayMessagePrefix .. "Player '" .. InitPlayerName .. "' hit an enemy target " ..
:NewType( self.DisplayMessagePrefix .. "Player '" .. InitPlayerName .. "' hit enemy target " ..
TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.ScoreHit .. " times. " ..
"Score: " .. PlayerHit.Score .. ". Score Total:" .. Player.Score - Player.Penalty,
2
MESSAGE.Type.Update
)
:ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() )
:ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() )
@@ -947,8 +1016,8 @@ function SCORING:_EventOnHit( Event )
end
else -- A scenery object was hit.
MESSAGE
:New( self.DisplayMessagePrefix .. "Player '" .. InitPlayerName .. "' hit a scenery object.",
2
:NewType( self.DisplayMessagePrefix .. "Player '" .. InitPlayerName .. "' hit scenery object.",
MESSAGE.Type.Update
)
:ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() )
:ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() )
@@ -1008,10 +1077,10 @@ function SCORING:_EventOnHit( Event )
PlayerHit.PenaltyHit = PlayerHit.PenaltyHit + 1
MESSAGE
:New( self.DisplayMessagePrefix .. "Player '" .. Event.WeaponPlayerName .. "' hit a friendly target " ..
TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.PenaltyHit .. " times. " ..
"Penalty: -" .. PlayerHit.Penalty .. ". Score Total:" .. Player.Score - Player.Penalty,
2
:NewType( self.DisplayMessagePrefix .. "Player '" .. Event.WeaponPlayerName .. "' hit friendly target " ..
TargetUnitCategory .. " ( " .. TargetType .. " ) " ..
"Penalty: -" .. PlayerHit.Penalty .. " = " .. Player.Score - Player.Penalty,
MESSAGE.Type.Update
)
:ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() )
:ToCoalitionIf( Event.WeaponCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() )
@@ -1021,10 +1090,10 @@ function SCORING:_EventOnHit( Event )
PlayerHit.Score = PlayerHit.Score + 1
PlayerHit.ScoreHit = PlayerHit.ScoreHit + 1
MESSAGE
:New( self.DisplayMessagePrefix .. "Player '" .. Event.WeaponPlayerName .. "' hit an enemy target " ..
TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.ScoreHit .. " times. " ..
"Score: " .. PlayerHit.Score .. ". Score Total:" .. Player.Score - Player.Penalty,
2
:NewType( self.DisplayMessagePrefix .. "Player '" .. Event.WeaponPlayerName .. "' hit enemy target " ..
TargetUnitCategory .. " ( " .. TargetType .. " ) " ..
"Score: +" .. PlayerHit.Score .. " = " .. Player.Score - Player.Penalty,
MESSAGE.Type.Update
)
:ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() )
:ToCoalitionIf( Event.WeaponCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() )
@@ -1032,8 +1101,8 @@ function SCORING:_EventOnHit( Event )
end
else -- A scenery object was hit.
MESSAGE
:New( self.DisplayMessagePrefix .. "Player '" .. Event.WeaponPlayerName .. "' hit a scenery object.",
2
:NewType( self.DisplayMessagePrefix .. "Player '" .. Event.WeaponPlayerName .. "' hit scenery object.",
MESSAGE.Type.Update
)
:ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() )
:ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() )
@@ -1131,19 +1200,19 @@ function SCORING:_EventOnDeadOrCrash( Event )
if Player.HitPlayers[TargetPlayerName] then -- A player destroyed another player
MESSAGE
:New( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' destroyed friendly player '" .. TargetPlayerName .. "' " ..
TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " .. TargetDestroy.PenaltyDestroy .. " times. " ..
"Penalty: -" .. TargetDestroy.Penalty .. ". Score Total:" .. Player.Score - Player.Penalty,
15
:NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' destroyed friendly player '" .. TargetPlayerName .. "' " ..
TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " ..
"Penalty: -" .. TargetDestroy.Penalty .. " = " .. Player.Score - Player.Penalty,
MESSAGE.Type.Information
)
:ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() )
:ToCoalitionIf( InitCoalition, self:IfMessagesDestroy() and self:IfMessagesToCoalition() )
else
MESSAGE
:New( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' destroyed a friendly target " ..
TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " .. TargetDestroy.PenaltyDestroy .. " times. " ..
"Penalty: -" .. TargetDestroy.Penalty .. ". Score Total:" .. Player.Score - Player.Penalty,
15
:NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' destroyed friendly target " ..
TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " ..
"Penalty: -" .. TargetDestroy.Penalty .. " = " .. Player.Score - Player.Penalty,
MESSAGE.Type.Information
)
:ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() )
:ToCoalitionIf( InitCoalition, self:IfMessagesDestroy() and self:IfMessagesToCoalition() )
@@ -1165,19 +1234,19 @@ function SCORING:_EventOnDeadOrCrash( Event )
TargetDestroy.ScoreDestroy = TargetDestroy.ScoreDestroy + 1
if Player.HitPlayers[TargetPlayerName] then -- A player destroyed another player
MESSAGE
:New( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' destroyed enemy player '" .. TargetPlayerName .. "' " ..
TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " .. TargetDestroy.ScoreDestroy .. " times. " ..
"Score: " .. TargetDestroy.Score .. ". Score Total:" .. Player.Score - Player.Penalty,
15
:NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' destroyed enemy player '" .. TargetPlayerName .. "' " ..
TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " ..
"Score: +" .. TargetDestroy.Score .. " = " .. Player.Score - Player.Penalty,
MESSAGE.Type.Information
)
:ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() )
:ToCoalitionIf( InitCoalition, self:IfMessagesDestroy() and self:IfMessagesToCoalition() )
else
MESSAGE
:New( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' destroyed an enemy " ..
TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " .. TargetDestroy.ScoreDestroy .. " times. " ..
"Score: " .. TargetDestroy.Score .. ". Total:" .. Player.Score - Player.Penalty,
15
:NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' destroyed enemy " ..
TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " ..
"Score: +" .. TargetDestroy.Score .. " = " .. Player.Score - Player.Penalty,
MESSAGE.Type.Information
)
:ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() )
:ToCoalitionIf( InitCoalition, self:IfMessagesDestroy() and self:IfMessagesToCoalition() )
@@ -1191,9 +1260,9 @@ function SCORING:_EventOnDeadOrCrash( Event )
Player.Score = Player.Score + Score
TargetDestroy.Score = TargetDestroy.Score + Score
MESSAGE
:New( self.DisplayMessagePrefix .. "Special target '" .. TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " .. " destroyed! " ..
:NewType( self.DisplayMessagePrefix .. "Special target '" .. TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " .. " destroyed! " ..
"Player '" .. PlayerName .. "' receives an extra " .. Score .. " points! Total: " .. Player.Score - Player.Penalty,
15
MESSAGE.Type.Information
)
:ToAllIf( self:IfMessagesScore() and self:IfMessagesToAll() )
:ToCoalitionIf( InitCoalition, self:IfMessagesScore() and self:IfMessagesToCoalition() )
@@ -1210,10 +1279,10 @@ function SCORING:_EventOnDeadOrCrash( Event )
Player.Score = Player.Score + Score
TargetDestroy.Score = TargetDestroy.Score + Score
MESSAGE
:New( self.DisplayMessagePrefix .. "Target destroyed in zone '" .. ScoreZone:GetName() .. "'." ..
:NewType( self.DisplayMessagePrefix .. "Target destroyed in zone '" .. ScoreZone:GetName() .. "'." ..
"Player '" .. PlayerName .. "' receives an extra " .. Score .. " points! " ..
"Total: " .. Player.Score - Player.Penalty,
15 )
MESSAGE.Type.Information )
:ToAllIf( self:IfMessagesZone() and self:IfMessagesToAll() )
:ToCoalitionIf( InitCoalition, self:IfMessagesZone() and self:IfMessagesToCoalition() )
self:ScoreCSV( PlayerName, TargetPlayerName, "DESTROY_SCORE", 1, Score, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType )
@@ -1232,10 +1301,10 @@ function SCORING:_EventOnDeadOrCrash( Event )
Player.Score = Player.Score + Score
TargetDestroy.Score = TargetDestroy.Score + Score
MESSAGE
:New( self.DisplayMessagePrefix .. "Scenery destroyed in zone '" .. ScoreZone:GetName() .. "'." ..
:NewType( self.DisplayMessagePrefix .. "Scenery destroyed in zone '" .. ScoreZone:GetName() .. "'." ..
"Player '" .. PlayerName .. "' receives an extra " .. Score .. " points! " ..
"Total: " .. Player.Score - Player.Penalty,
15
MESSAGE.Type.Information
)
:ToAllIf( self:IfMessagesZone() and self:IfMessagesToAll() )
:ToCoalitionIf( InitCoalition, self:IfMessagesZone() and self:IfMessagesToCoalition() )
@@ -1522,7 +1591,7 @@ function SCORING:ReportScoreGroupSummary( PlayerGroup )
PlayerScore,
PlayerPenalty
)
MESSAGE:New( PlayerMessage, 30, "Player '" .. PlayerName .. "'" ):ToGroup( PlayerGroup )
MESSAGE:NewType( PlayerMessage, MESSAGE.Type.Detailed ):ToGroup( PlayerGroup )
end
end
@@ -1579,7 +1648,7 @@ function SCORING:ReportScoreGroupDetailed( PlayerGroup )
ReportGoals,
ReportMissions
)
MESSAGE:New( PlayerMessage, 30, "Player '" .. PlayerName .. "'" ):ToGroup( PlayerGroup )
MESSAGE:NewType( PlayerMessage, MESSAGE.Type.Detailed ):ToGroup( PlayerGroup )
end
end
@@ -1628,7 +1697,7 @@ function SCORING:ReportScoreAllSummary( PlayerGroup )
PlayerScore,
PlayerPenalty
)
MESSAGE:New( PlayerMessage, 30, "Player '" .. PlayerName .. "'" ):ToGroup( PlayerGroup )
MESSAGE:NewType( PlayerMessage, MESSAGE.Type.Overview ):ToGroup( PlayerGroup )
end
end

View File

@@ -0,0 +1,442 @@
--- **Functional** -- (WIP R2.3) Models the process to capture a Zone for a Coalition, which is guarded by another Coalition.
--
-- ====
--
-- ![Banner Image](..\Presentations\ZONE_CAPTURE_COALITION\Dia1.JPG)
--
-- ===
--
-- ### Contributions: **Millertime**: Concept
-- ### Author: **Sven Van de Velde (FlightControl)**
--
-- ====
--
-- @module ZoneCaptureCoalition
do -- ZONE_CAPTURE_COALITION
--- @type ZONE_CAPTURE_COALITION
-- @extends Functional.ZoneGoalCoalition#ZONE_GOAL_COALITION
--- # ZONE\_CAPTURE\_COALITION class, extends @{ZoneGoalCoalition#ZONE_GOAL_COALITION}
--
-- Models the process to capture a Zone for a Coalition, which is guarded by another Coalition.
--
-- ---
--
-- ![Banner Image](..\Presentations\ZONE_CAPTURE_COALITION\Dia1.JPG)
--
-- ---
--
-- The Zone is initially **Guarded** by the __owning coalition__, which is the coalition that initially occupies the zone with units of its coalition.
-- Once units of an other coalition are entering the Zone, the state will change to **Attacked**. As long as these units remain in the zone, the state keeps set to Attacked.
-- When all units are destroyed in the Zone, the state will change to **Empty**, which expresses that the Zone is empty, and can be captured.
-- When units of the other coalition are in the Zone, and no other units of the owning coalition is in the Zone, the Zone is captured, and its state will change to **Captured**.
--
-- Event handlers can be defined by the mission designer to action on the state transitions.
--
-- ## 1. ZONE\_CAPTURE\_COALITION constructor
--
-- * @{#ZONE_CAPTURE_COALITION.New}(): Creates a new ZONE\_CAPTURE\_COALITION object.
--
-- ## 2. ZONE\_CAPTURE\_COALITION is a finite state machine (FSM).
--
-- ### 2.1 ZONE\_CAPTURE\_COALITION States
--
-- * **Captured**: The Zone has been captured by an other coalition.
-- * **Attacked**: The Zone is currently intruded by an other coalition. There are units of the owning coalition and an other coalition in the Zone.
-- * **Guarded**: The Zone is guarded by the owning coalition. There is no other unit of an other coalition in the Zone.
-- * **Empty**: The Zone is empty. There is not valid unit in the Zone.
--
-- ### 2.2 ZONE\_CAPTURE\_COALITION Events
--
-- * **Capture**: The Zone has been captured by an other coalition.
-- * **Attack**: The Zone is currently intruded by an other coalition. There are units of the owning coalition and an other coalition in the Zone.
-- * **Guard**: The Zone is guarded by the owning coalition. There is no other unit of an other coalition in the Zone.
-- * **Empty**: The Zone is empty. There is not valid unit in the Zone.
--
-- @field #ZONE_CAPTURE_COALITION
ZONE_CAPTURE_COALITION = {
ClassName = "ZONE_CAPTURE_COALITION",
}
--- @field #table ZONE_CAPTURE_COALITION.States
ZONE_CAPTURE_COALITION.States = {}
--- ZONE_CAPTURE_COALITION Constructor.
-- @param #ZONE_CAPTURE_COALITION self
-- @param Core.Zone#ZONE Zone A @{Zone} object with the goal to be achieved.
-- @param DCSCoalition.DCSCoalition#coalition Coalition The initial coalition owning the zone.
-- @return #ZONE_CAPTURE_COALITION
-- @usage
--
-- AttackZone = ZONE:New( "AttackZone" )
--
-- ZoneCaptureCoalition = ZONE_CAPTURE_COALITION:New( AttackZone, coalition.side.RED ) -- Create a new ZONE_CAPTURE_COALITION object of zone AttackZone with ownership RED coalition.
-- ZoneCaptureCoalition:__Guard( 1 ) -- Start the Guarding of the AttackZone.
--
function ZONE_CAPTURE_COALITION:New( Zone, Coalition )
local self = BASE:Inherit( self, ZONE_GOAL_COALITION:New( Zone, Coalition ) ) -- #ZONE_CAPTURE_COALITION
self:F( { Zone = Zone, Coalition = Coalition } )
do
--- Captured State Handler OnLeave for ZONE\_CAPTURE\_COALITION
-- @function [parent=#ZONE_CAPTURE_COALITION] OnLeaveCaptured
-- @param #ZONE_CAPTURE_COALITION self
-- @param #string From
-- @param #string Event
-- @param #string To
-- @return #boolean
--- Captured State Handler OnEnter for ZONE\_CAPTURE\_COALITION
-- @function [parent=#ZONE_CAPTURE_COALITION] OnEnterCaptured
-- @param #ZONE_CAPTURE_COALITION self
-- @param #string From
-- @param #string Event
-- @param #string To
end
do
--- Attacked State Handler OnLeave for ZONE\_CAPTURE\_COALITION
-- @function [parent=#ZONE_CAPTURE_COALITION] OnLeaveAttacked
-- @param #ZONE_CAPTURE_COALITION self
-- @param #string From
-- @param #string Event
-- @param #string To
-- @return #boolean
--- Attacked State Handler OnEnter for ZONE\_CAPTURE\_COALITION
-- @function [parent=#ZONE_CAPTURE_COALITION] OnEnterAttacked
-- @param #ZONE_CAPTURE_COALITION self
-- @param #string From
-- @param #string Event
-- @param #string To
end
do
--- Guarded State Handler OnLeave for ZONE\_CAPTURE\_COALITION
-- @function [parent=#ZONE_CAPTURE_COALITION] OnLeaveGuarded
-- @param #ZONE_CAPTURE_COALITION self
-- @param #string From
-- @param #string Event
-- @param #string To
-- @return #boolean
--- Guarded State Handler OnEnter for ZONE\_CAPTURE\_COALITION
-- @function [parent=#ZONE_CAPTURE_COALITION] OnEnterGuarded
-- @param #ZONE_CAPTURE_COALITION self
-- @param #string From
-- @param #string Event
-- @param #string To
end
do
--- Empty State Handler OnLeave for ZONE\_CAPTURE\_COALITION
-- @function [parent=#ZONE_CAPTURE_COALITION] OnLeaveEmpty
-- @param #ZONE_CAPTURE_COALITION self
-- @param #string From
-- @param #string Event
-- @param #string To
-- @return #boolean
--- Empty State Handler OnEnter for ZONE\_CAPTURE\_COALITION
-- @function [parent=#ZONE_CAPTURE_COALITION] OnEnterEmpty
-- @param #ZONE_CAPTURE_COALITION self
-- @param #string From
-- @param #string Event
-- @param #string To
end
self:AddTransition( "*", "Guard", "Guarded" )
--- Guard Handler OnBefore for ZONE\_CAPTURE\_COALITION
-- @function [parent=#ZONE_CAPTURE_COALITION] OnBeforeGuard
-- @param #ZONE_CAPTURE_COALITION self
-- @param #string From
-- @param #string Event
-- @param #string To
-- @return #boolean
--- Guard Handler OnAfter for ZONE\_CAPTURE\_COALITION
-- @function [parent=#ZONE_CAPTURE_COALITION] OnAfterGuard
-- @param #ZONE_CAPTURE_COALITION self
-- @param #string From
-- @param #string Event
-- @param #string To
--- Guard Trigger for ZONE\_CAPTURE\_COALITION
-- @function [parent=#ZONE_CAPTURE_COALITION] Guard
-- @param #ZONE_CAPTURE_COALITION self
--- Guard Asynchronous Trigger for ZONE\_CAPTURE\_COALITION
-- @function [parent=#ZONE_CAPTURE_COALITION] __Guard
-- @param #ZONE_CAPTURE_COALITION self
-- @param #number Delay
self:AddTransition( "*", "Empty", "Empty" )
--- Empty Handler OnBefore for ZONE\_CAPTURE\_COALITION
-- @function [parent=#ZONE_CAPTURE_COALITION] OnBeforeEmpty
-- @param #ZONE_CAPTURE_COALITION self
-- @param #string From
-- @param #string Event
-- @param #string To
-- @return #boolean
--- Empty Handler OnAfter for ZONE\_CAPTURE\_COALITION
-- @function [parent=#ZONE_CAPTURE_COALITION] OnAfterEmpty
-- @param #ZONE_CAPTURE_COALITION self
-- @param #string From
-- @param #string Event
-- @param #string To
--- Empty Trigger for ZONE\_CAPTURE\_COALITION
-- @function [parent=#ZONE_CAPTURE_COALITION] Empty
-- @param #ZONE_CAPTURE_COALITION self
--- Empty Asynchronous Trigger for ZONE\_CAPTURE\_COALITION
-- @function [parent=#ZONE_CAPTURE_COALITION] __Empty
-- @param #ZONE_CAPTURE_COALITION self
-- @param #number Delay
self:AddTransition( { "Guarded", "Empty" }, "Attack", "Attacked" )
--- Attack Handler OnBefore for ZONE\_CAPTURE\_COALITION
-- @function [parent=#ZONE_CAPTURE_COALITION] OnBeforeAttack
-- @param #ZONE_CAPTURE_COALITION self
-- @param #string From
-- @param #string Event
-- @param #string To
-- @return #boolean
--- Attack Handler OnAfter for ZONE\_CAPTURE\_COALITION
-- @function [parent=#ZONE_CAPTURE_COALITION] OnAfterAttack
-- @param #ZONE_CAPTURE_COALITION self
-- @param #string From
-- @param #string Event
-- @param #string To
--- Attack Trigger for ZONE\_CAPTURE\_COALITION
-- @function [parent=#ZONE_CAPTURE_COALITION] Attack
-- @param #ZONE_CAPTURE_COALITION self
--- Attack Asynchronous Trigger for ZONE\_CAPTURE\_COALITION
-- @function [parent=#ZONE_CAPTURE_COALITION] __Attack
-- @param #ZONE_CAPTURE_COALITION self
-- @param #number Delay
self:AddTransition( { "Guarded", "Attacked", "Empty" }, "Capture", "Captured" )
--- Capture Handler OnBefore for ZONE\_CAPTURE\_COALITION
-- @function [parent=#ZONE_CAPTURE_COALITION] OnBeforeCapture
-- @param #ZONE_CAPTURE_COALITION self
-- @param #string From
-- @param #string Event
-- @param #string To
-- @return #boolean
--- Capture Handler OnAfter for ZONE\_CAPTURE\_COALITION
-- @function [parent=#ZONE_CAPTURE_COALITION] OnAfterCapture
-- @param #ZONE_CAPTURE_COALITION self
-- @param #string From
-- @param #string Event
-- @param #string To
--- Capture Trigger for ZONE\_CAPTURE\_COALITION
-- @function [parent=#ZONE_CAPTURE_COALITION] Capture
-- @param #ZONE_CAPTURE_COALITION self
--- Capture Asynchronous Trigger for ZONE\_CAPTURE\_COALITION
-- @function [parent=#ZONE_CAPTURE_COALITION] __Capture
-- @param #ZONE_CAPTURE_COALITION self
-- @param #number Delay
if not self.ScheduleStatusZone then
self.ScheduleStatusZone = self:ScheduleRepeat( 15, 15, 0.1, nil, self.StatusZone, self )
end
return self
end
--- @param #ZONE_CAPTURE_COALITION self
function ZONE_CAPTURE_COALITION:onenterCaptured()
self:GetParent( self, ZONE_CAPTURE_COALITION ).onenterCaptured( self )
self.Goal:Achieved()
end
function ZONE_CAPTURE_COALITION:IsGuarded()
local IsGuarded = self.Zone:IsAllInZoneOfCoalition( self.Coalition )
self:E( { IsGuarded = IsGuarded } )
return IsGuarded
end
function ZONE_CAPTURE_COALITION:IsEmpty()
local IsEmpty = self.Zone:IsNoneInZone()
self:E( { IsEmpty = IsEmpty } )
return IsEmpty
end
function ZONE_CAPTURE_COALITION:IsCaptured()
local IsCaptured = self.Zone:IsAllInZoneOfOtherCoalition( self.Coalition )
self:E( { IsCaptured = IsCaptured } )
return IsCaptured
end
function ZONE_CAPTURE_COALITION:IsAttacked()
local IsAttacked = self.Zone:IsSomeInZoneOfCoalition( self.Coalition )
self:E( { IsAttacked = IsAttacked } )
return IsAttacked
end
--- Mark.
-- @param #ZONE_CAPTURE_COALITION self
function ZONE_CAPTURE_COALITION:Mark()
local Coord = self.Zone:GetCoordinate()
local ZoneName = self:GetZoneName()
local State = self:GetState()
if self.MarkRed and self.MarkBlue then
self:E( { MarkRed = self.MarkRed, MarkBlue = self.MarkBlue } )
Coord:RemoveMark( self.MarkRed )
Coord:RemoveMark( self.MarkBlue )
end
if self.Coalition == coalition.side.BLUE then
self.MarkBlue = Coord:MarkToCoalitionBlue( "Coalition: Blue\nGuard Zone: " .. ZoneName .. "\nStatus: " .. State )
self.MarkRed = Coord:MarkToCoalitionRed( "Coalition: Blue\nCapture Zone: " .. ZoneName .. "\nStatus: " .. State )
else
self.MarkRed = Coord:MarkToCoalitionRed( "Coalition: Red\nGuard Zone: " .. ZoneName .. "\nStatus: " .. State )
self.MarkBlue = Coord:MarkToCoalitionBlue( "Coalition: Red\nCapture Zone: " .. ZoneName .. "\nStatus: " .. State )
end
end
--- Bound.
-- @param #ZONE_CAPTURE_COALITION self
function ZONE_CAPTURE_COALITION:onenterGuarded()
--self:GetParent( self ):onenterGuarded()
if self.Coalition == coalition.side.BLUE then
--elf.ProtectZone:BoundZone( 12, country.id.USA )
else
--self.ProtectZone:BoundZone( 12, country.id.RUSSIA )
end
self:Mark()
end
function ZONE_CAPTURE_COALITION:onenterCaptured()
--self:GetParent( self ):onenterCaptured()
local NewCoalition = self.Zone:GetScannedCoalition()
self:E( { NewCoalition = NewCoalition } )
self:SetCoalition( NewCoalition )
self:Mark()
end
function ZONE_CAPTURE_COALITION:onenterEmpty()
--self:GetParent( self ):onenterEmpty()
self:Mark()
end
function ZONE_CAPTURE_COALITION:onenterAttacked()
--self:GetParent( self ):onenterAttacked()
self:Mark()
end
--- When started, check the Coalition status.
-- @param #ZONE_CAPTURE_COALITION self
function ZONE_CAPTURE_COALITION:onafterGuard()
--self:E({BASE:GetParent( self )})
--BASE:GetParent( self ).onafterGuard( self )
if not self.SmokeScheduler then
self.SmokeScheduler = self:ScheduleRepeat( 1, 1, 0.1, nil, self.StatusSmoke, self )
end
end
function ZONE_CAPTURE_COALITION:IsCaptured()
local IsCaptured = self.Zone:IsAllInZoneOfOtherCoalition( self.Coalition )
self:E( { IsCaptured = IsCaptured } )
return IsCaptured
end
function ZONE_CAPTURE_COALITION:IsAttacked()
local IsAttacked = self.Zone:IsSomeInZoneOfCoalition( self.Coalition )
self:E( { IsAttacked = IsAttacked } )
return IsAttacked
end
--- Check status Coalition ownership.
-- @param #ZONE_CAPTURE_COALITION self
function ZONE_CAPTURE_COALITION:StatusZone()
local State = self:GetState()
self:E( { State = self:GetState() } )
self:GetParent( self, ZONE_CAPTURE_COALITION ).StatusZone( self )
if State ~= "Guarded" and self:IsGuarded() then
self:Guard()
end
if State ~= "Empty" and self:IsEmpty() then
self:Empty()
end
if State ~= "Attacked" and self:IsAttacked() then
self:Attack()
end
if State ~= "Captured" and self:IsCaptured() then
self:Capture()
end
end
end

View File

@@ -0,0 +1,177 @@
--- **Functional (WIP)** -- Base class that models processes to achieve goals involving a Zone.
--
-- ====
--
-- ZONE_GOAL models processes that have a Goal with a defined achievement involving a Zone.
-- Derived classes implement the ways how the achievements can be realized.
--
-- ====
--
-- ### Author: **Sven Van de Velde (FlightControl)**
--
-- ====
--
-- @module ZoneGoal
do -- Zone
--- @type ZONE_GOAL
-- @extends Core.Fsm#FSM
--- # ZONE_GOAL class, extends @{Fsm#FSM}
--
-- ZONE_GOAL models processes that have a Goal with a defined achievement involving a Zone.
-- Derived classes implement the ways how the achievements can be realized.
--
-- ## 1. ZONE_GOAL constructor
--
-- * @{#ZONE_GOAL.New}(): Creates a new ZONE_GOAL object.
--
-- ## 2. ZONE_GOAL is a finite state machine (FSM).
--
-- ### 2.1 ZONE_GOAL States
--
-- * None: Initial State
--
-- ### 2.2 ZONE_GOAL Events
--
-- * DestroyedUnit: A @{Unit} is destroyed in the Zone. The event will only get triggered if the method @{#ZONE_GOAL.MonitorDestroyedUnits}() is used.
--
-- @field #ZONE_GOAL
ZONE_GOAL = {
ClassName = "ZONE_GOAL",
}
--- ZONE_GOAL Constructor.
-- @param #ZONE_GOAL self
-- @param Core.Zone#ZONE_BASE 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
self:F( { Zone = Zone } )
self.Zone = Zone -- Core.Zone#ZONE_BASE
self.Goal = GOAL:New()
self.SmokeTime = nil
self:AddTransition( "*", "DestroyedUnit", "*" )
--- DestroyedUnit Handler OnAfter for ZONE_GOAL
-- @function [parent=#ZONE_GOAL] OnAfterDestroyedUnit
-- @param #ZONE_GOAL self
-- @param #string From
-- @param #string Event
-- @param #string To
-- @param Wrapper.Unit#UNIT DestroyedUnit The destroyed unit.
-- @param #string PlayerName The name of the player.
return self
end
--- Get the Zone
-- @param #ZONE_GOAL self
-- @return Core.Zone#ZONE_BASE
function ZONE_GOAL:GetZone()
return self.Zone
end
--- Get the name of the ProtectZone
-- @param #ZONE_GOAL self
-- @return #string
function ZONE_GOAL:GetZoneName()
return self.Zone:GetName()
end
--- Smoke the center of theh zone.
-- @param #ZONE_GOAL self
-- @param #SMOKECOLOR.Color SmokeColor
function ZONE_GOAL:Smoke( SmokeColor )
self:F( { SmokeColor = SmokeColor} )
self.SmokeColor = SmokeColor
end
--- Flare the center of the zone.
-- @param #ZONE_GOAL self
-- @param #SMOKECOLOR.Color FlareColor
function ZONE_GOAL:Flare( FlareColor )
self.Zone:FlareZone( FlareColor, math.random( 1, 360 ) )
end
--- When started, check the Smoke and the Zone status.
-- @param #ZONE_GOAL self
function ZONE_GOAL:onafterGuard()
--self:GetParent( self ):onafterStart()
self:E("Guard")
--self:ScheduleRepeat( 15, 15, 0.1, nil, self.StatusZone, self )
if not self.SmokeScheduler then
self.SmokeScheduler = self:ScheduleRepeat( 1, 1, 0.1, nil, self.StatusSmoke, self )
end
end
--- Check status Smoke.
-- @param #ZONE_GOAL self
function ZONE_GOAL:StatusSmoke()
self:F({self.SmokeTime, self.SmokeColor})
local CurrentTime = timer.getTime()
if self.SmokeTime == nil or self.SmokeTime + 300 <= CurrentTime then
if self.SmokeColor then
self.Zone:GetCoordinate():Smoke( self.SmokeColor )
--self.SmokeColor = nil
self.SmokeTime = CurrentTime
end
end
end
--- @param #ZONE_GOAL self
-- @param Core.Event#EVENTDATA EventData
function ZONE_GOAL:__Destroyed( EventData )
self:E( { "EventDead", EventData } )
self:E( { EventData.IniUnit } )
local Vec3 = EventData.IniDCSUnit:getPosition().p
self:E( { Vec3 = Vec3 } )
local ZoneGoal = self:GetZone()
self:E({ZoneGoal})
if EventData.IniDCSUnit then
if ZoneGoal:IsVec3InZone(Vec3) then
local PlayerHits = _DATABASE.HITS[EventData.IniUnitName]
if PlayerHits then
for PlayerName, PlayerHit in pairs( PlayerHits.Players or {} ) do
self.Goal:AddPlayerContribution( PlayerName )
self:DestroyedUnit( EventData.IniUnitName, PlayerName )
end
end
end
end
end
--- Activate the event UnitDestroyed to be fired when a unit is destroyed in the zone.
-- @param #ZONE_GOAL self
function ZONE_GOAL:MonitorDestroyedUnits()
self:HandleEvent( EVENTS.Dead, self.__Destroyed )
self:HandleEvent( EVENTS.Crash, self.__Destroyed )
end
end

View File

@@ -0,0 +1,452 @@
--- **Functional (WIP)** -- Base class that models processes to achieve goals involving a Zone and Cargo.
--
-- ====
--
-- ZONE_GOAL_CARGO models processes that have a Goal with a defined achievement involving a Zone and Cargo.
-- Derived classes implement the ways how the achievements can be realized.
--
-- ====
--
-- ### Author: **Sven Van de Velde (FlightControl)**
--
-- ====
--
-- @module ZoneGoalCargo
do -- ZoneGoal
--- @type ZONE_GOAL_CARGO
-- @extends Functional.ZoneGoal#ZONE_GOAL
--- # ZONE_GOAL_CARGO class, extends @{ZoneGoal#ZONE_GOAL}
--
-- ZONE_GOAL_CARGO models processes that have a Goal with a defined achievement involving a Zone and Cargo.
-- Derived classes implement the ways how the achievements can be realized.
--
-- ## 1. ZONE_GOAL_CARGO constructor
--
-- * @{#ZONE_GOAL_CARGO.New}(): Creates a new ZONE_GOAL_CARGO object.
--
-- ## 2. ZONE_GOAL_CARGO is a finite state machine (FSM).
--
-- ### 2.1 ZONE_GOAL_CARGO States
--
-- * **Deployed**: The Zone has been captured by an other coalition.
-- * **Airborne**: The Zone is currently intruded by an other coalition. There are units of the owning coalition and an other coalition in the Zone.
-- * **Loaded**: The Zone is guarded by the owning coalition. There is no other unit of an other coalition in the Zone.
-- * **Empty**: The Zone is empty. There is not valid unit in the Zone.
--
-- ### 2.2 ZONE_GOAL_CARGO Events
--
-- * **Capture**: The Zone has been captured by an other coalition.
-- * **Attack**: The Zone is currently intruded by an other coalition. There are units of the owning coalition and an other coalition in the Zone.
-- * **Guard**: The Zone is guarded by the owning coalition. There is no other unit of an other coalition in the Zone.
-- * **Empty**: The Zone is empty. There is not valid unit in the Zone.
--
-- ### 2.3 ZONE_GOAL_CARGO State Machine
--
-- @field #ZONE_GOAL_CARGO
ZONE_GOAL_CARGO = {
ClassName = "ZONE_GOAL_CARGO",
}
--- @field #table ZONE_GOAL_CARGO.States
ZONE_GOAL_CARGO.States = {}
--- ZONE_GOAL_CARGO Constructor.
-- @param #ZONE_GOAL_CARGO self
-- @param Core.Zone#ZONE Zone A @{Zone} object with the goal to be achieved.
-- @param DCSCoalition.DCSCoalition#coalition Coalition The initial coalition owning the zone.
-- @return #ZONE_GOAL_CARGO
function ZONE_GOAL_CARGO:New( Zone, Coalition )
local self = BASE:Inherit( self, ZONE_GOAL:New( Zone ) ) -- #ZONE_GOAL_CARGO
self:F( { Zone = Zone, Coalition = Coalition } )
self:SetCoalition( Coalition )
do
--- Captured State Handler OnLeave for ZONE_GOAL_CARGO
-- @function [parent=#ZONE_GOAL_CARGO] OnLeaveCaptured
-- @param #ZONE_GOAL_CARGO self
-- @param #string From
-- @param #string Event
-- @param #string To
-- @return #boolean
--- Captured State Handler OnEnter for ZONE_GOAL_CARGO
-- @function [parent=#ZONE_GOAL_CARGO] OnEnterCaptured
-- @param #ZONE_GOAL_CARGO self
-- @param #string From
-- @param #string Event
-- @param #string To
end
do
--- Attacked State Handler OnLeave for ZONE_GOAL_CARGO
-- @function [parent=#ZONE_GOAL_CARGO] OnLeaveAttacked
-- @param #ZONE_GOAL_CARGO self
-- @param #string From
-- @param #string Event
-- @param #string To
-- @return #boolean
--- Attacked State Handler OnEnter for ZONE_GOAL_CARGO
-- @function [parent=#ZONE_GOAL_CARGO] OnEnterAttacked
-- @param #ZONE_GOAL_CARGO self
-- @param #string From
-- @param #string Event
-- @param #string To
end
do
--- Guarded State Handler OnLeave for ZONE_GOAL_CARGO
-- @function [parent=#ZONE_GOAL_CARGO] OnLeaveGuarded
-- @param #ZONE_GOAL_CARGO self
-- @param #string From
-- @param #string Event
-- @param #string To
-- @return #boolean
--- Guarded State Handler OnEnter for ZONE_GOAL_CARGO
-- @function [parent=#ZONE_GOAL_CARGO] OnEnterGuarded
-- @param #ZONE_GOAL_CARGO self
-- @param #string From
-- @param #string Event
-- @param #string To
end
do
--- Empty State Handler OnLeave for ZONE_GOAL_CARGO
-- @function [parent=#ZONE_GOAL_CARGO] OnLeaveEmpty
-- @param #ZONE_GOAL_CARGO self
-- @param #string From
-- @param #string Event
-- @param #string To
-- @return #boolean
--- Empty State Handler OnEnter for ZONE_GOAL_CARGO
-- @function [parent=#ZONE_GOAL_CARGO] OnEnterEmpty
-- @param #ZONE_GOAL_CARGO self
-- @param #string From
-- @param #string Event
-- @param #string To
end
self:AddTransition( "*", "Guard", "Guarded" )
--- Guard Handler OnBefore for ZONE_GOAL_CARGO
-- @function [parent=#ZONE_GOAL_CARGO] OnBeforeGuard
-- @param #ZONE_GOAL_CARGO self
-- @param #string From
-- @param #string Event
-- @param #string To
-- @return #boolean
--- Guard Handler OnAfter for ZONE_GOAL_CARGO
-- @function [parent=#ZONE_GOAL_CARGO] OnAfterGuard
-- @param #ZONE_GOAL_CARGO self
-- @param #string From
-- @param #string Event
-- @param #string To
--- Guard Trigger for ZONE_GOAL_CARGO
-- @function [parent=#ZONE_GOAL_CARGO] Guard
-- @param #ZONE_GOAL_CARGO self
--- Guard Asynchronous Trigger for ZONE_GOAL_CARGO
-- @function [parent=#ZONE_GOAL_CARGO] __Guard
-- @param #ZONE_GOAL_CARGO self
-- @param #number Delay
self:AddTransition( "*", "Empty", "Empty" )
--- Empty Handler OnBefore for ZONE_GOAL_CARGO
-- @function [parent=#ZONE_GOAL_CARGO] OnBeforeEmpty
-- @param #ZONE_GOAL_CARGO self
-- @param #string From
-- @param #string Event
-- @param #string To
-- @return #boolean
--- Empty Handler OnAfter for ZONE_GOAL_CARGO
-- @function [parent=#ZONE_GOAL_CARGO] OnAfterEmpty
-- @param #ZONE_GOAL_CARGO self
-- @param #string From
-- @param #string Event
-- @param #string To
--- Empty Trigger for ZONE_GOAL_CARGO
-- @function [parent=#ZONE_GOAL_CARGO] Empty
-- @param #ZONE_GOAL_CARGO self
--- Empty Asynchronous Trigger for ZONE_GOAL_CARGO
-- @function [parent=#ZONE_GOAL_CARGO] __Empty
-- @param #ZONE_GOAL_CARGO self
-- @param #number Delay
self:AddTransition( { "Guarded", "Empty" }, "Attack", "Attacked" )
--- Attack Handler OnBefore for ZONE_GOAL_CARGO
-- @function [parent=#ZONE_GOAL_CARGO] OnBeforeAttack
-- @param #ZONE_GOAL_CARGO self
-- @param #string From
-- @param #string Event
-- @param #string To
-- @return #boolean
--- Attack Handler OnAfter for ZONE_GOAL_CARGO
-- @function [parent=#ZONE_GOAL_CARGO] OnAfterAttack
-- @param #ZONE_GOAL_CARGO self
-- @param #string From
-- @param #string Event
-- @param #string To
--- Attack Trigger for ZONE_GOAL_CARGO
-- @function [parent=#ZONE_GOAL_CARGO] Attack
-- @param #ZONE_GOAL_CARGO self
--- Attack Asynchronous Trigger for ZONE_GOAL_CARGO
-- @function [parent=#ZONE_GOAL_CARGO] __Attack
-- @param #ZONE_GOAL_CARGO self
-- @param #number Delay
self:AddTransition( { "Guarded", "Attacked", "Empty" }, "Capture", "Captured" )
--- Capture Handler OnBefore for ZONE_GOAL_CARGO
-- @function [parent=#ZONE_GOAL_CARGO] OnBeforeCapture
-- @param #ZONE_GOAL_CARGO self
-- @param #string From
-- @param #string Event
-- @param #string To
-- @return #boolean
--- Capture Handler OnAfter for ZONE_GOAL_CARGO
-- @function [parent=#ZONE_GOAL_CARGO] OnAfterCapture
-- @param #ZONE_GOAL_CARGO self
-- @param #string From
-- @param #string Event
-- @param #string To
--- Capture Trigger for ZONE_GOAL_CARGO
-- @function [parent=#ZONE_GOAL_CARGO] Capture
-- @param #ZONE_GOAL_CARGO self
--- Capture Asynchronous Trigger for ZONE_GOAL_CARGO
-- @function [parent=#ZONE_GOAL_CARGO] __Capture
-- @param #ZONE_GOAL_CARGO self
-- @param #number Delay
return self
end
--- Set the owning coalition of the zone.
-- @param #ZONE_GOAL_CARGO self
-- @param DCSCoalition.DCSCoalition#coalition Coalition
function ZONE_GOAL_CARGO:SetCoalition( Coalition )
self.Coalition = Coalition
end
--- Get the owning coalition of the zone.
-- @param #ZONE_GOAL_CARGO self
-- @return DCSCoalition.DCSCoalition#coalition Coalition.
function ZONE_GOAL_CARGO:GetCoalition()
return self.Coalition
end
--- Get the owning coalition name of the zone.
-- @param #ZONE_GOAL_CARGO self
-- @return #string Coalition name.
function ZONE_GOAL_CARGO:GetCoalitionName()
if self.Coalition == coalition.side.BLUE then
return "Blue"
end
if self.Coalition == coalition.side.RED then
return "Red"
end
if self.Coalition == coalition.side.NEUTRAL then
return "Neutral"
end
return ""
end
function ZONE_GOAL_CARGO:IsGuarded()
local IsGuarded = self.Zone:IsAllInZoneOfCoalition( self.Coalition )
self:E( { IsGuarded = IsGuarded } )
return IsGuarded
end
function ZONE_GOAL_CARGO:IsEmpty()
local IsEmpty = self.Zone:IsNoneInZone()
self:E( { IsEmpty = IsEmpty } )
return IsEmpty
end
function ZONE_GOAL_CARGO:IsCaptured()
local IsCaptured = self.Zone:IsAllInZoneOfOtherCoalition( self.Coalition )
self:E( { IsCaptured = IsCaptured } )
return IsCaptured
end
function ZONE_GOAL_CARGO:IsAttacked()
local IsAttacked = self.Zone:IsSomeInZoneOfCoalition( self.Coalition )
self:E( { IsAttacked = IsAttacked } )
return IsAttacked
end
--- Mark.
-- @param #ZONE_GOAL_CARGO self
function ZONE_GOAL_CARGO:Mark()
local Coord = self.Zone:GetCoordinate()
local ZoneName = self:GetZoneName()
local State = self:GetState()
if self.MarkRed and self.MarkBlue then
self:E( { MarkRed = self.MarkRed, MarkBlue = self.MarkBlue } )
Coord:RemoveMark( self.MarkRed )
Coord:RemoveMark( self.MarkBlue )
end
if self.Coalition == coalition.side.BLUE then
self.MarkBlue = Coord:MarkToCoalitionBlue( "Guard Zone: " .. ZoneName .. "\nStatus: " .. State )
self.MarkRed = Coord:MarkToCoalitionRed( "Capture Zone: " .. ZoneName .. "\nStatus: " .. State )
else
self.MarkRed = Coord:MarkToCoalitionRed( "Guard Zone: " .. ZoneName .. "\nStatus: " .. State )
self.MarkBlue = Coord:MarkToCoalitionBlue( "Capture Zone: " .. ZoneName .. "\nStatus: " .. State )
end
end
--- Bound.
-- @param #ZONE_GOAL_CARGO self
function ZONE_GOAL_CARGO:onenterGuarded()
--self:GetParent( self ):onenterGuarded()
if self.Coalition == coalition.side.BLUE then
--elf.ProtectZone:BoundZone( 12, country.id.USA )
else
--self.ProtectZone:BoundZone( 12, country.id.RUSSIA )
end
self:Mark()
end
function ZONE_GOAL_CARGO:onenterCaptured()
--self:GetParent( self ):onenterCaptured()
local NewCoalition = self.Zone:GetCoalition()
self:E( { NewCoalition = NewCoalition } )
self:SetCoalition( NewCoalition )
self:Mark()
end
function ZONE_GOAL_CARGO:onenterEmpty()
--self:GetParent( self ):onenterEmpty()
self:Mark()
end
function ZONE_GOAL_CARGO:onenterAttacked()
--self:GetParent( self ):onenterAttacked()
self:Mark()
end
--- When started, check the Coalition status.
-- @param #ZONE_GOAL_CARGO self
function ZONE_GOAL_CARGO:onafterGuard()
--self:E({BASE:GetParent( self )})
--BASE:GetParent( self ).onafterGuard( self )
if not self.SmokeScheduler then
self.SmokeScheduler = self:ScheduleRepeat( 1, 1, 0.1, nil, self.StatusSmoke, self )
end
if not self.ScheduleStatusZone then
self.ScheduleStatusZone = self:ScheduleRepeat( 15, 15, 0.1, nil, self.StatusZone, self )
end
end
function ZONE_GOAL_CARGO:IsCaptured()
local IsCaptured = self.Zone:IsAllInZoneOfOtherCoalition( self.Coalition )
self:E( { IsCaptured = IsCaptured } )
return IsCaptured
end
function ZONE_GOAL_CARGO:IsAttacked()
local IsAttacked = self.Zone:IsSomeInZoneOfCoalition( self.Coalition )
self:E( { IsAttacked = IsAttacked } )
return IsAttacked
end
--- Check status Coalition ownership.
-- @param #ZONE_GOAL_CARGO self
function ZONE_GOAL_CARGO:StatusZone()
local State = self:GetState()
self:E( { State = self:GetState() } )
self.Zone:Scan()
if State ~= "Guarded" and self:IsGuarded() then
self:Guard()
end
if State ~= "Empty" and self:IsEmpty() then
self:Empty()
end
if State ~= "Attacked" and self:IsAttacked() then
self:Attack()
end
if State ~= "Captured" and self:IsCaptured() then
self:Capture()
end
end
end

View File

@@ -0,0 +1,113 @@
--- **Functional (WIP)** -- Base class that models processes to achieve goals involving a Zone for a Coalition.
--
-- ====
--
-- ZONE_GOAL_COALITION models processes that have a Goal with a defined achievement involving a Zone for a Coalition.
-- Derived classes implement the ways how the achievements can be realized.
--
-- ====
--
-- ### Author: **Sven Van de Velde (FlightControl)**
--
-- ====
--
-- @module ZoneGoalCoalition
do -- ZoneGoal
--- @type ZONE_GOAL_COALITION
-- @extends Functional.ZoneGoal#ZONE_GOAL
--- # ZONE_GOAL_COALITION class, extends @{ZoneGoal#ZONE_GOAL}
--
-- ZONE_GOAL_COALITION models processes that have a Goal with a defined achievement involving a Zone for a Coalition.
-- Derived classes implement the ways how the achievements can be realized.
--
-- ## 1. ZONE_GOAL_COALITION constructor
--
-- * @{#ZONE_GOAL_COALITION.New}(): Creates a new ZONE_GOAL_COALITION object.
--
-- ## 2. ZONE_GOAL_COALITION is a finite state machine (FSM).
--
-- ### 2.1 ZONE_GOAL_COALITION States
--
-- ### 2.2 ZONE_GOAL_COALITION Events
--
-- ### 2.3 ZONE_GOAL_COALITION State Machine
--
-- @field #ZONE_GOAL_COALITION
ZONE_GOAL_COALITION = {
ClassName = "ZONE_GOAL_COALITION",
}
--- @field #table ZONE_GOAL_COALITION.States
ZONE_GOAL_COALITION.States = {}
--- ZONE_GOAL_COALITION Constructor.
-- @param #ZONE_GOAL_COALITION self
-- @param Core.Zone#ZONE Zone A @{Zone} object with the goal to be achieved.
-- @param DCSCoalition.DCSCoalition#coalition Coalition The initial coalition owning the zone.
-- @return #ZONE_GOAL_COALITION
function ZONE_GOAL_COALITION:New( Zone, Coalition )
local self = BASE:Inherit( self, ZONE_GOAL:New( Zone ) ) -- #ZONE_GOAL_COALITION
self:F( { Zone = Zone, Coalition = Coalition } )
self:SetCoalition( Coalition )
return self
end
--- Set the owning coalition of the zone.
-- @param #ZONE_GOAL_COALITION self
-- @param DCSCoalition.DCSCoalition#coalition Coalition
function ZONE_GOAL_COALITION:SetCoalition( Coalition )
self.Coalition = Coalition
end
--- Get the owning coalition of the zone.
-- @param #ZONE_GOAL_COALITION self
-- @return DCSCoalition.DCSCoalition#coalition Coalition.
function ZONE_GOAL_COALITION:GetCoalition()
return self.Coalition
end
--- Get the owning coalition name of the zone.
-- @param #ZONE_GOAL_COALITION self
-- @return #string Coalition name.
function ZONE_GOAL_COALITION:GetCoalitionName()
if self.Coalition == coalition.side.BLUE then
return "Blue"
end
if self.Coalition == coalition.side.RED then
return "Red"
end
if self.Coalition == coalition.side.NEUTRAL then
return "Neutral"
end
return ""
end
--- Check status Coalition ownership.
-- @param #ZONE_GOAL_COALITION self
function ZONE_GOAL_COALITION:StatusZone()
local State = self:GetState()
self:E( { State = self:GetState() } )
self.Zone:Scan( { Object.Category.UNIT, Object.Category.STATIC } )
end
end

View File

@@ -13,80 +13,7 @@
--- The REPORT class
-- @type REPORT
-- @extends Core.Base#BASE
REPORT = {
ClassName = "REPORT",
Title = "",
}
--- Create a new REPORT.
-- @param #REPORT self
-- @param #string Title
-- @return #REPORT
function REPORT:New( Title )
local self = BASE:Inherit( self, BASE:New() ) -- #REPORT
self.Report = {}
Title = Title or ""
if Title then
self.Title = Title
end
self:SetIndent( 3 )
return self
end
--- Has the REPORT Text?
-- @param #REPORT self
-- @return #boolean
function REPORT:HasText() --R2.1
return #self.Report > 0
end
--- Set indent of a REPORT.
-- @param #REPORT self
-- @param #number Indent
-- @return #REPORT
function REPORT:SetIndent( Indent ) --R2.1
self.Indent = Indent
return self
end
--- Add a new line to a REPORT.
-- @param #REPORT self
-- @param #string Text
-- @return #REPORT
function REPORT:Add( Text )
self.Report[#self.Report+1] = Text
return self
end
--- Add a new line to a REPORT.
-- @param #REPORT self
-- @param #string Text
-- @return #REPORT
function REPORT:AddIndent( Text ) --R2.1
self.Report[#self.Report+1] = string.rep(" ", self.Indent ) .. Text:gsub("\n","\n"..string.rep( " ", self.Indent ) )
return self
end
--- Produces the text of the report, taking into account an optional delimeter, which is \n by default.
-- @param #REPORT self
-- @param #string Delimiter (optional) A delimiter text.
-- @return #string The report text.
function REPORT:Text( Delimiter )
Delimiter = Delimiter or "\n"
local ReportText = ( self.Title ~= "" and self.Title .. Delimiter or self.Title ) .. table.concat( self.Report, Delimiter ) or ""
return ReportText
end
--- The COMMANDCENTER class
-- @type COMMANDCENTER
@@ -207,6 +134,7 @@ function COMMANDCENTER:New( CommandCenterPositionable, CommandCenterName )
local PlayerGroup = EventData.IniGroup -- The GROUP object should be filled!
Mission:JoinUnit( PlayerUnit, PlayerGroup )
end
self:SetMenu()
end
)
@@ -260,6 +188,8 @@ function COMMANDCENTER:New( CommandCenterPositionable, CommandCenterName )
)
self:SetMenu()
_SETTINGS:SetSystemMenu( CommandCenterPositionable )
return self
end
@@ -428,10 +358,20 @@ end
-- @param #COMMANDCENTER self
-- @param #string Message
-- @param Wrapper.Group#GROUP TaskGroup
-- @param #sring Name (optional) The name of the Group used as a prefix for the message to the Group. If not provided, there will be nothing shown.
function COMMANDCENTER:MessageToGroup( Message, TaskGroup )
self:GetPositionable():MessageToGroup( Message , 15, TaskGroup, self:GetName() )
self:GetPositionable():MessageToGroup( Message, 15, TaskGroup, self:GetName() )
end
--- Send a CC message of a specified type to a GROUP.
-- @param #COMMANDCENTER self
-- @param #string Message
-- @param Wrapper.Group#GROUP TaskGroup
-- @param Core.Message#MESSAGE.MessageType MessageType The type of the message, resulting in automatic time duration and prefix of the message.
function COMMANDCENTER:MessageTypeToGroup( Message, TaskGroup, MessageType )
self:GetPositionable():MessageTypeToGroup( Message, MessageType, TaskGroup, self:GetName() )
end
@@ -447,6 +387,20 @@ function COMMANDCENTER:MessageToCoalition( Message )
end
--- Send a CC message of a specified type to the coalition of the CC.
-- @param #COMMANDCENTER self
-- @param #string Message The message.
-- @param Core.Message#MESSAGE.MessageType MessageType The type of the message, resulting in automatic time duration and prefix of the message.
function COMMANDCENTER:MessageTypeToCoalition( Message, MessageType )
local CCCoalition = self:GetPositionable():GetCoalition()
--TODO: Fix coalition bug!
self:GetPositionable():MessageTypeToCoalition( Message, MessageType, CCCoalition )
end
--- Report the status of all MISSIONs to a GROUP.
-- Each Mission is listed, with an indication how many Tasks are still to be completed.
-- @param #COMMANDCENTER self

View File

@@ -15,7 +15,7 @@
-- ---------------------------------
-- Derived DETECTION_MANAGER classes will reports detected units using the method @{DetectionManager#DETECTION_MANAGER.ReportDetected}(). This method implements polymorphic behaviour.
--
-- The time interval in seconds of the reporting can be changed using the methods @{DetectionManager#DETECTION_MANAGER.SetReportInterval}().
-- The time interval in seconds of the reporting can be changed using the methods @{DetectionManager#DETECTION_MANAGER.SetRefreshTimeInterval}().
-- To control how long a reporting message is displayed, use @{DetectionManager#DETECTION_MANAGER.SetReportDisplayTime}().
-- Derived classes need to implement the method @{DetectionManager#DETECTION_MANAGER.GetReportDisplayTime}() to use the correct display time for displayed messages during a report.
--
@@ -126,7 +126,7 @@ do -- DETECTION MANAGER
self:AddTransition( "Started", "Report", "Started" )
self:SetReportInterval( 30 )
self:SetRefreshTimeInterval( 30 )
self:SetReportDisplayTime( 25 )
self:E( { Detection = Detection } )
@@ -143,19 +143,19 @@ do -- DETECTION MANAGER
self:E( "onafterReport" )
self:__Report( -self._ReportInterval )
self:__Report( -self._RefreshTimeInterval )
self:ProcessDetected( self.Detection )
end
--- Set the reporting time interval.
-- @param #DETECTION_MANAGER self
-- @param #number ReportInterval The interval in seconds when a report needs to be done.
-- @param #number RefreshTimeInterval The interval in seconds when a report needs to be done.
-- @return #DETECTION_MANAGER self
function DETECTION_MANAGER:SetReportInterval( ReportInterval )
function DETECTION_MANAGER:SetRefreshTimeInterval( RefreshTimeInterval )
self:F2()
self._ReportInterval = ReportInterval
self._RefreshTimeInterval = RefreshTimeInterval
end

View File

@@ -264,14 +264,14 @@ function MISSION:New( CommandCenter, MissionName, MissionPriority, MissionBriefi
end
-- FSM function for a MISSION
--- FSM function for a MISSION
-- @param #MISSION self
-- @param #string From
-- @param #string Event
-- @param #string To
function MISSION:onenterCOMPLETED( From, Event, To )
self:GetCommandCenter():MessageToCoalition( self:GetName() .. " has been completed! Good job guys!" )
self:GetCommandCenter():MessageTypeToCoalition( self:GetName() .. " has been completed! Good job guys!", MESSAGE.Type.Information )
end
--- Gets the mission name.
@@ -450,7 +450,7 @@ do -- Group Assignment
local MissionGroupName = MissionGroup:GetName()
self.AssignedGroups[MissionGroupName] = nil
self:E( string.format( "Mission %s is unassigned to %s", MissionName, MissionGroupName ) )
--self:E( string.format( "Mission %s is unassigned to %s", MissionName, MissionGroupName ) )
return self
end
@@ -475,7 +475,23 @@ function MISSION:RemoveTaskMenu( Task )
end
--- Gets the mission menu for the coalition.
--- Gets the root mission menu for the TaskGroup.
-- @param #MISSION self
-- @return Core.Menu#MENU_COALITION self
function MISSION:GetRootMenu( TaskGroup ) -- R2.2
local CommandCenter = self:GetCommandCenter()
local CommandCenterMenu = CommandCenter:GetMenu()
local MissionName = self:GetName()
--local MissionMenu = CommandCenterMenu:GetMenu( MissionName )
self.MissionMenu = self.MissionMenu or MENU_COALITION:New( self.MissionCoalition, self:GetName(), CommandCenterMenu )
return self.MissionMenu
end
--- Gets the mission menu for the TaskGroup.
-- @param #MISSION self
-- @return Core.Menu#MENU_COALITION self
function MISSION:GetMenu( TaskGroup ) -- R2.1 -- Changed Menu Structure
@@ -486,27 +502,28 @@ function MISSION:GetMenu( TaskGroup ) -- R2.1 -- Changed Menu Structure
local MissionName = self:GetName()
--local MissionMenu = CommandCenterMenu:GetMenu( MissionName )
self.MissionMenu = self.MissionMenu or {}
self.MissionMenu[TaskGroup] = self.MissionMenu[TaskGroup] or {}
self.MissionGroupMenu = self.MissionGroupMenu or {}
self.MissionGroupMenu[TaskGroup] = self.MissionGroupMenu[TaskGroup] or {}
local Menu = self.MissionMenu[TaskGroup]
local GroupMenu = self.MissionGroupMenu[TaskGroup]
Menu.MainMenu = Menu.MainMenu or MENU_GROUP:New( TaskGroup, self:GetName(), CommandCenterMenu )
Menu.BriefingMenu = Menu.BriefingMenu or MENU_GROUP_COMMAND:New( TaskGroup, "Mission Briefing", Menu.MainMenu, self.MenuReportBriefing, self, TaskGroup )
self.MissionMenu = self.MissionMenu or MENU_COALITION:New( self.MissionCoalition, self:GetName(), CommandCenterMenu )
GroupMenu.BriefingMenu = GroupMenu.BriefingMenu or MENU_GROUP_COMMAND:New( TaskGroup, "Mission Briefing", self.MissionMenu, self.MenuReportBriefing, self, TaskGroup )
Menu.TaskReportsMenu = Menu.TaskReportsMenu or MENU_GROUP:New( TaskGroup, "Task Reports", Menu.MainMenu )
Menu.ReportTasksMenu = Menu.ReportTasksMenu or MENU_GROUP_COMMAND:New( TaskGroup, "Report Tasks", Menu.TaskReportsMenu, self.MenuReportTasksSummary, self, TaskGroup )
Menu.ReportPlannedTasksMenu = Menu.ReportPlannedTasksMenu or MENU_GROUP_COMMAND:New( TaskGroup, "Report Planned Tasks", Menu.TaskReportsMenu, self.MenuReportTasksPerStatus, self, TaskGroup, "Planned" )
Menu.ReportAssignedTasksMenu = Menu.ReportAssignedTasksMenu or MENU_GROUP_COMMAND:New( TaskGroup, "Report Assigned Tasks", Menu.TaskReportsMenu, self.MenuReportTasksPerStatus, self, TaskGroup, "Assigned" )
Menu.ReportSuccessTasksMenu = Menu.ReportSuccessTasksMenu or MENU_GROUP_COMMAND:New( TaskGroup, "Report Successful Tasks", Menu.TaskReportsMenu, self.MenuReportTasksPerStatus, self, TaskGroup, "Success" )
Menu.ReportFailedTasksMenu = Menu.ReportFailedTasksMenu or MENU_GROUP_COMMAND:New( TaskGroup, "Report Failed Tasks", Menu.TaskReportsMenu, self.MenuReportTasksPerStatus, self, TaskGroup, "Failed" )
Menu.ReportHeldTasksMenu = Menu.ReportHeldTasksMenu or MENU_GROUP_COMMAND:New( TaskGroup, "Report Held Tasks", Menu.TaskReportsMenu, self.MenuReportTasksPerStatus, self, TaskGroup, "Hold" )
GroupMenu.TaskReportsMenu = GroupMenu.TaskReportsMenu or MENU_GROUP:New( TaskGroup, "Task Reports", self.MissionMenu )
GroupMenu.ReportTasksMenu = GroupMenu.ReportTasksMenu or MENU_GROUP_COMMAND:New( TaskGroup, "Report Tasks", GroupMenu.TaskReportsMenu, self.MenuReportTasksSummary, self, TaskGroup )
GroupMenu.ReportPlannedTasksMenu = GroupMenu.ReportPlannedTasksMenu or MENU_GROUP_COMMAND:New( TaskGroup, "Report Planned Tasks", GroupMenu.TaskReportsMenu, self.MenuReportTasksPerStatus, self, TaskGroup, "Planned" )
GroupMenu.ReportAssignedTasksMenu = GroupMenu.ReportAssignedTasksMenu or MENU_GROUP_COMMAND:New( TaskGroup, "Report Assigned Tasks", GroupMenu.TaskReportsMenu, self.MenuReportTasksPerStatus, self, TaskGroup, "Assigned" )
GroupMenu.ReportSuccessTasksMenu = GroupMenu.ReportSuccessTasksMenu or MENU_GROUP_COMMAND:New( TaskGroup, "Report Successful Tasks", GroupMenu.TaskReportsMenu, self.MenuReportTasksPerStatus, self, TaskGroup, "Success" )
GroupMenu.ReportFailedTasksMenu = GroupMenu.ReportFailedTasksMenu or MENU_GROUP_COMMAND:New( TaskGroup, "Report Failed Tasks", GroupMenu.TaskReportsMenu, self.MenuReportTasksPerStatus, self, TaskGroup, "Failed" )
GroupMenu.ReportHeldTasksMenu = GroupMenu.ReportHeldTasksMenu or MENU_GROUP_COMMAND:New( TaskGroup, "Report Held Tasks", GroupMenu.TaskReportsMenu, self.MenuReportTasksPerStatus, self, TaskGroup, "Hold" )
Menu.PlayerReportsMenu = Menu.PlayerReportsMenu or MENU_GROUP:New( TaskGroup, "Statistics Reports", Menu.MainMenu )
Menu.ReportMissionHistory = Menu.ReportPlayersHistory or MENU_GROUP_COMMAND:New( TaskGroup, "Report Mission Progress", Menu.PlayerReportsMenu, self.MenuReportPlayersProgress, self, TaskGroup )
Menu.ReportPlayersPerTaskMenu = Menu.ReportPlayersPerTaskMenu or MENU_GROUP_COMMAND:New( TaskGroup, "Report Players per Task", Menu.PlayerReportsMenu, self.MenuReportPlayersPerTask, self, TaskGroup )
GroupMenu.PlayerReportsMenu = GroupMenu.PlayerReportsMenu or MENU_GROUP:New( TaskGroup, "Statistics Reports", self.MissionMenu )
GroupMenu.ReportMissionHistory = GroupMenu.ReportPlayersHistory or MENU_GROUP_COMMAND:New( TaskGroup, "Report Mission Progress", GroupMenu.PlayerReportsMenu, self.MenuReportPlayersProgress, self, TaskGroup )
GroupMenu.ReportPlayersPerTaskMenu = GroupMenu.ReportPlayersPerTaskMenu or MENU_GROUP_COMMAND:New( TaskGroup, "Report Players per Task", GroupMenu.PlayerReportsMenu, self.MenuReportPlayersPerTask, self, TaskGroup )
return Menu.MainMenu
return self.MissionMenu
end
@@ -843,8 +860,9 @@ end
--- Create a summary report of the Mission (one line).
-- @param #MISSION self
-- @param Wrapper.Group#GROUP ReportGroup
-- @return #string
function MISSION:ReportSummary()
function MISSION:ReportSummary( ReportGroup )
local Report = REPORT:New()
@@ -857,9 +875,9 @@ function MISSION:ReportSummary()
Report:Add( string.format( '%s - %s - Task Overview Report', Name, Status ) )
-- Determine how many tasks are remaining.
for TaskID, Task in pairs( self:GetTasks() ) do
for TaskID, Task in UTILS.spairs( self:GetTasks(), function( t, a, b ) return t[a]:ReportOrder( ReportGroup ) < t[b]:ReportOrder( ReportGroup ) end ) do
local Task = Task -- Tasking.Task#TASK
Report:Add( "- " .. Task:ReportSummary() )
Report:Add( "- " .. Task:ReportSummary( ReportGroup ) )
end
return Report:Text()
@@ -870,6 +888,8 @@ end
-- @return #string
function MISSION:ReportOverview( ReportGroup, TaskStatus )
self:F( { TaskStatus = TaskStatus } )
local Report = REPORT:New()
-- List the name of the mission.
@@ -881,12 +901,17 @@ function MISSION:ReportOverview( ReportGroup, TaskStatus )
Report:Add( string.format( '%s - %s - %s Tasks Report', Name, Status, TaskStatus ) )
-- Determine how many tasks are remaining.
local TasksRemaining = 0
for TaskID, Task in pairs( self:GetTasks() ) do
local Tasks = 0
for TaskID, Task in UTILS.spairs( self:GetTasks(), function( t, a, b ) return t[a]:ReportOrder( ReportGroup ) < t[b]:ReportOrder( ReportGroup ) end ) do
local Task = Task -- Tasking.Task#TASK
if Task:Is( TaskStatus ) then
Report:Add( string.rep( "-", 140 ) )
Report:Add( " - " .. Task:ReportOverview( ReportGroup ) )
end
Tasks = Tasks + 1
if Tasks >= 8 then
break
end
end
return Report:Text()
@@ -935,7 +960,7 @@ function MISSION:MenuReportBriefing( ReportGroup )
local Report = self:ReportBriefing()
self:GetCommandCenter():MessageToGroup( Report, ReportGroup )
self:GetCommandCenter():MessageTypeToGroup( Report, ReportGroup, MESSAGE.Type.Briefing )
end
@@ -945,9 +970,9 @@ end
-- @param Wrapper.Group#GROUP ReportGroup
function MISSION:MenuReportTasksSummary( ReportGroup )
local Report = self:ReportSummary()
local Report = self:ReportSummary( ReportGroup )
self:GetCommandCenter():MessageToGroup( Report, ReportGroup )
self:GetCommandCenter():MessageTypeToGroup( Report, ReportGroup, MESSAGE.Type.Overview )
end
@@ -960,7 +985,7 @@ function MISSION:MenuReportTasksPerStatus( ReportGroup, TaskStatus )
local Report = self:ReportOverview( ReportGroup, TaskStatus )
self:GetCommandCenter():MessageToGroup( Report, ReportGroup )
self:GetCommandCenter():MessageTypeToGroup( Report, ReportGroup, MESSAGE.Type.Overview )
end
@@ -970,7 +995,7 @@ function MISSION:MenuReportPlayersPerTask( ReportGroup )
local Report = self:ReportPlayersPerTask()
self:GetCommandCenter():MessageToGroup( Report, ReportGroup )
self:GetCommandCenter():MessageTypeToGroup( Report, ReportGroup, MESSAGE.Type.Overview )
end
--- @param #MISSION self
@@ -979,7 +1004,7 @@ function MISSION:MenuReportPlayersProgress( ReportGroup )
local Report = self:ReportPlayersProgress()
self:GetCommandCenter():MessageToGroup( Report, ReportGroup )
self:GetCommandCenter():MessageTypeToGroup( Report, ReportGroup, MESSAGE.Type.Overview )
end

View File

@@ -175,28 +175,34 @@ function TASK:New( Mission, SetGroupAssign, TaskName, TaskType, TaskBriefing )
--- Goal Handler OnBefore for TASK
-- @function [parent=#TASK] OnBeforeGoal
-- @param #TASK self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable
-- @param #string From
-- @param #string Event
-- @param #string To
-- @param Wrapper.Unit#UNIT PlayerUnit The @{Unit} of the player.
-- @param #string PlayerName The name of the player.
-- @return #boolean
--- Goal Handler OnAfter for TASK
-- @function [parent=#TASK] OnAfterGoal
-- @param #TASK self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable
-- @param #string From
-- @param #string Event
-- @param #string To
-- @param Wrapper.Unit#UNIT PlayerUnit The @{Unit} of the player.
-- @param #string PlayerName The name of the player.
--- Goal Trigger for TASK
-- @function [parent=#TASK] Goal
-- @param #TASK self
-- @param Wrapper.Unit#UNIT PlayerUnit The @{Unit} of the player.
-- @param #string PlayerName The name of the player.
--- Goal Asynchronous Trigger for TASK
-- @function [parent=#TASK] __Goal
-- @param #TASK self
-- @param #number Delay
-- @param Wrapper.Unit#UNIT PlayerUnit The @{Unit} of the player.
-- @param #string PlayerName The name of the player.
@@ -272,7 +278,7 @@ function TASK:JoinUnit( PlayerUnit, PlayerGroup )
-- Check if the PlayerGroup is already assigned to the Task. If yes, the PlayerGroup is added to the Task.
-- If the PlayerGroup is not assigned to the Task, the menu needs to be set. In that case, the PlayerUnit will become the GroupPlayer leader.
if self:IsStatePlanned() or self:IsStateReplanned() then
self:SetMenuForGroup( PlayerGroup )
--self:SetMenuForGroup( PlayerGroup )
--self:MessageToGroups( PlayerUnit:GetPlayerName() .. " is planning to join Task " .. self:GetName() )
end
if self:IsStateAssigned() then
@@ -309,7 +315,7 @@ function TASK:AbortGroup( PlayerGroup )
self:E( { IsGroupAssigned = IsGroupAssigned } )
if IsGroupAssigned then
local PlayerName = PlayerGroup:GetUnit(1):GetPlayerName()
self:MessageToGroups( PlayerName .. " aborted Task " .. self:GetName() )
--self:MessageToGroups( PlayerName .. " aborted Task " .. self:GetName() )
self:UnAssignFromGroup( PlayerGroup )
--self:Abort()
@@ -469,7 +475,7 @@ do -- Group Assignment
local TaskGroupName = TaskGroup:GetName()
self.AssignedGroups[TaskGroupName] = nil
self:E( string.format( "Task %s is unassigned to %s", TaskName, TaskGroupName ) )
--self:E( string.format( "Task %s is unassigned to %s", TaskName, TaskGroupName ) )
-- Set the group to be assigned at mission level. This allows to decide the menu options on mission level for this group.
self:GetMission():ClearGroupAssignment( TaskGroup )
@@ -479,9 +485,9 @@ do -- Group Assignment
SetAssignedGroups:ForEachGroup(
function( AssignedGroup )
if self:IsGroupAssigned(AssignedGroup) then
self:GetMission():GetCommandCenter():MessageToGroup( string.format( "Task %s is unassigned from group %s.", TaskName, TaskGroupName ), AssignedGroup )
--self:GetMission():GetCommandCenter():MessageToGroup( string.format( "Task %s is unassigned from group %s.", TaskName, TaskGroupName ), AssignedGroup )
else
self:GetMission():GetCommandCenter():MessageToGroup( string.format( "Task %s is unassigned from your group.", TaskName ), AssignedGroup )
--self:GetMission():GetCommandCenter():MessageToGroup( string.format( "Task %s is unassigned from your group.", TaskName ), AssignedGroup )
end
end
)
@@ -530,8 +536,9 @@ do -- Group Assignment
--- UnAssign the @{Task} from a @{Group}.
-- @param #TASK self
-- @param Wrapper.Group#GROUP TaskGroup
function TASK:UnAssignFromGroup( TaskGroup )
self:F2( { TaskGroup } )
self:F2( { TaskGroup = TaskGroup:GetName() } )
self:ClearGroupAssignment( TaskGroup )
@@ -539,7 +546,7 @@ do -- Group Assignment
for UnitID, UnitData in pairs( TaskUnits ) do
local TaskUnit = UnitData -- Wrapper.Unit#UNIT
local PlayerName = TaskUnit:GetPlayerName()
if PlayerName ~= nil or PlayerName ~= "" then
if PlayerName ~= nil and PlayerName ~= "" then -- Only remove units that have players!
self:UnAssignFromUnit( TaskUnit )
end
end
@@ -573,7 +580,6 @@ function TASK:AssignToUnit( TaskUnit )
-- Assign a new FsmUnit to TaskUnit.
local FsmUnit = self:SetStateMachine( TaskUnit, FsmTemplate:Copy( TaskUnit, self ) ) -- Core.Fsm#FSM_PROCESS
self:E({"Address FsmUnit", tostring( FsmUnit ) } )
FsmUnit:SetStartState( "Planned" )
@@ -738,13 +744,16 @@ function TASK:SetPlannedMenuForGroup( TaskGroup, MenuTime )
--local MissionMenu = Mission:GetMenu( TaskGroup )
local TaskPlannedMenu = MENU_GROUP:New( TaskGroup, "Planned Tasks", MissionMenu ):SetTime( MenuTime )
local TaskTypeMenu = MENU_GROUP:New( TaskGroup, TaskType, TaskPlannedMenu ):SetTime( MenuTime ):SetRemoveParent( true )
local TaskTypeMenu = MENU_GROUP:New( TaskGroup, TaskText, TaskTypeMenu ):SetTime( MenuTime ):SetRemoveParent( true )
local ReportTaskMenu = MENU_GROUP_COMMAND:New( TaskGroup, string.format( "Report Task Status" ), TaskTypeMenu, self.MenuTaskStatus, self, TaskGroup ):SetTime( MenuTime ):SetRemoveParent( true )
self.MenuPlanned = self.MenuPlanned or {}
self.MenuPlanned[TaskGroup] = MENU_GROUP:New( TaskGroup, "Join Planned Task", MissionMenu, Mission.MenuReportTasksPerStatus, Mission, TaskGroup, "Planned" ):SetTime( MenuTime ):SetTag( "Tasking" )
local TaskTypeMenu = MENU_GROUP:New( TaskGroup, TaskType, self.MenuPlanned[TaskGroup] ):SetTime( MenuTime ):SetTag( "Tasking" ):SetRemoveParent( true )
local TaskTypeMenu = MENU_GROUP:New( TaskGroup, TaskText, TaskTypeMenu ):SetTime( MenuTime ):SetTag( "Tasking" ):SetRemoveParent( true )
local ReportTaskMenu = MENU_GROUP_COMMAND:New( TaskGroup, string.format( "Report Task Status" ), TaskTypeMenu, self.MenuTaskStatus, self, TaskGroup ):SetTime( MenuTime ):SetTag( "Tasking" ):SetRemoveParent( true )
if not Mission:IsGroupAssigned( TaskGroup ) then
local JoinTaskMenu = MENU_GROUP_COMMAND:New( TaskGroup, string.format( "Join Task" ), TaskTypeMenu, self.MenuAssignToGroup, { self = self, TaskGroup = TaskGroup } ):SetTime( MenuTime ):SetRemoveParent( true )
self:F( { "Replacing Join Task menu" } )
local JoinTaskMenu = MENU_GROUP_COMMAND:New( TaskGroup, string.format( "Join Task" ), TaskTypeMenu, self.MenuAssignToGroup, self, TaskGroup ):SetTime( MenuTime ):SetTag( "Tasking" ):SetRemoveParent( true )
local MarkTaskMenu = MENU_GROUP_COMMAND:New( TaskGroup, string.format( "Mark Task on Map" ), TaskTypeMenu, self.MenuMarkToGroup, self, TaskGroup ):SetTime( MenuTime ):SetTag( "Tasking" ):SetRemoveParent( true )
end
return self
@@ -775,9 +784,10 @@ function TASK:SetAssignedMenuForGroup( TaskGroup, MenuTime )
-- local MissionMenu = MENU_GROUP:New( TaskGroup, MissionName, CommandCenterMenu ):SetTime( MenuTime )
-- local MissionMenu = Mission:GetMenu( TaskGroup )
local TaskAssignedMenu = MENU_GROUP:New( TaskGroup, string.format( "Assigned Task %s", TaskName ), MissionMenu ):SetTime( MenuTime )
local TaskTypeMenu = MENU_GROUP_COMMAND:New( TaskGroup, string.format( "Report Task Status" ), TaskAssignedMenu, self.MenuTaskStatus, self, TaskGroup ):SetTime( MenuTime ):SetRemoveParent( true )
local TaskMenu = MENU_GROUP_COMMAND:New( TaskGroup, string.format( "Abort Group from Task" ), TaskAssignedMenu, self.MenuTaskAbort, self, TaskGroup ):SetTime( MenuTime ):SetRemoveParent( true )
self.MenuAssigned = self.MenuAssigned or {}
self.MenuAssigned[TaskGroup] = MENU_GROUP:New( TaskGroup, string.format( "Assigned Task %s", TaskName ), MissionMenu ):SetTime( MenuTime ):SetTag( "Tasking" )
local TaskTypeMenu = MENU_GROUP_COMMAND:New( TaskGroup, string.format( "Report Task Status" ), self.MenuAssigned[TaskGroup], self.MenuTaskStatus, self, TaskGroup ):SetTime( MenuTime ):SetTag( "Tasking" ):SetRemoveParent( true )
local TaskMenu = MENU_GROUP_COMMAND:New( TaskGroup, string.format( "Abort Group from Task" ), self.MenuAssigned[TaskGroup], self.MenuTaskAbort, self, TaskGroup ):SetTime( MenuTime ):SetTag( "Tasking" ):SetRemoveParent( true )
return self
end
@@ -791,9 +801,7 @@ function TASK:RemoveMenu( MenuTime )
for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do
local TaskGroup = TaskGroup -- Wrapper.Group#GROUP
if TaskGroup:IsAlive() and TaskGroup:GetPlayerNames() then
self:RefreshMenus( TaskGroup, MenuTime )
end
self:RefreshMenus( TaskGroup, MenuTime )
end
end
@@ -814,15 +822,18 @@ function TASK:RefreshMenus( TaskGroup, MenuTime )
local MissionMenu = Mission:GetMenu( TaskGroup )
local TaskName = self:GetName()
local PlannedMenu = MissionMenu:GetMenu( "Planned Tasks" )
local AssignedMenu = MissionMenu:GetMenu( string.format( "Assigned Task %s", TaskName ) )
self.MenuPlanned = self.MenuPlanned or {}
local PlannedMenu = self.MenuPlanned[TaskGroup]
self.MenuAssigned = self.MenuAssigned or {}
local AssignedMenu = self.MenuAssigned[TaskGroup]
if PlannedMenu then
PlannedMenu:Remove( MenuTime )
PlannedMenu:Remove( MenuTime , "Tasking")
end
if AssignedMenu then
AssignedMenu:Remove( MenuTime )
AssignedMenu:Remove( MenuTime, "Tasking" )
end
end
@@ -846,16 +857,44 @@ function TASK:RemoveAssignedMenuForGroup( TaskGroup )
end
function TASK.MenuAssignToGroup( MenuParam )
--- @param #TASK self
-- @param Wrapper.Group#GROUP TaskGroup
function TASK:MenuAssignToGroup( TaskGroup )
local self = MenuParam.self
local TaskGroup = MenuParam.TaskGroup
self:E( "Assigned menu selected")
self:E( "Join Task menu selected")
self:AssignToGroup( TaskGroup )
end
--- @param #TASK self
-- @param Wrapper.Group#GROUP TaskGroup
function TASK:MenuMarkToGroup( TaskGroup )
self:E( "Mark Task menu selected")
self:UpdateTaskInfo()
local Report = REPORT:New():SetIndent( 0 )
-- List the name of the Task.
local Name = self:GetName()
Report:Add( "Task " .. Name .. ": " .. self:GetTaskBriefing() .. "\n" )
for TaskInfoID, TaskInfo in pairs( self.TaskInfo, function( t, a, b ) return t[a].TaskInfoOrder < t[b].TaskInfoOrder end ) do
local ReportText = self:GetMarkInfo( TaskInfoID, TaskInfo )
if ReportText then
Report:Add( ReportText )
end
end
local TargetCoordinate = self:GetInfo( "Coordinate" ) -- Core.Point#COORDINATE
local MarkText = Report:Text( ", " )
self:F( { Coordinate = TargetCoordinate, MarkText = MarkText } )
TargetCoordinate:MarkToGroup( MarkText, TaskGroup )
--Coordinate:MarkToAll( Briefing )
end
--- Report the task status.
-- @param #TASK self
function TASK:MenuTaskStatus( TaskGroup )
@@ -863,7 +902,7 @@ function TASK:MenuTaskStatus( TaskGroup )
local ReportText = self:ReportDetails( TaskGroup )
self:T( ReportText )
self:GetMission():GetCommandCenter():MessageToGroup( ReportText, TaskGroup )
self:GetMission():GetCommandCenter():MessageTypeToGroup( ReportText, TaskGroup, MESSAGE.Type.Detailed )
end
@@ -947,7 +986,7 @@ end
-- @param Wrapper.Unit#UNIT TaskUnit
-- @return #TASK self
function TASK:RemoveStateMachine( TaskUnit )
self:F( { TaskUnit, self.Fsm[TaskUnit] ~= nil } )
self:F( { TaskUnit = TaskUnit:GetName(), HasFsm = ( self.Fsm[TaskUnit] ~= nil ) } )
--self:E( self.Fsm )
--for TaskUnitT, Fsm in pairs( self.Fsm ) do
@@ -956,13 +995,16 @@ function TASK:RemoveStateMachine( TaskUnit )
--self.Fsm[TaskUnit] = nil
--end
self.Fsm[TaskUnit]:Remove()
self.Fsm[TaskUnit] = nil
if self.Fsm[TaskUnit] then
self.Fsm[TaskUnit]:Remove()
self.Fsm[TaskUnit] = nil
end
collectgarbage()
self:E( "Garbage Collected, Processes should be finalized now ...")
end
--- Checks if there is a FiniteStateMachine assigned to Task@{Unit} for @{Task}
-- @param #TASK self
-- @param Wrapper.Unit#UNIT TaskUnit
@@ -1016,10 +1058,26 @@ end
--- Sets the Information on the Task
-- @param #TASK self
-- @param #string TaskInfo
function TASK:SetInfo( TaskInfo, TaskInfoText )
-- @param #string TaskInfo The key and title of the task information.
-- @param #string TaskInfoText The Task info text.
-- @param #number TaskInfoOrder The ordering, a number between 0 and 99.
function TASK:SetInfo( TaskInfo, TaskInfoText, TaskInfoOrder )
self.TaskInfo[TaskInfo] = TaskInfoText
self.TaskInfo = self.TaskInfo or {}
self.TaskInfo[TaskInfo] = self.TaskInfo[TaskInfo] or {}
self.TaskInfo[TaskInfo].TaskInfoText = TaskInfoText
self.TaskInfo[TaskInfo].TaskInfoOrder = TaskInfoOrder
end
--- Gets the Information of the Task
-- @param #TASK self
-- @param #string TaskInfo The key and title of the task information.
-- @return #string TaskInfoText The Task info text.
function TASK:GetInfo( TaskInfo )
self.TaskInfo = self.TaskInfo or {}
self.TaskInfo[TaskInfo] = self.TaskInfo[TaskInfo] or {}
return self.TaskInfo[TaskInfo].TaskInfoText
end
--- Gets the Type of the Task
@@ -1181,9 +1239,11 @@ end
-- @param #string To
function TASK:onenterAssigned( From, Event, To, PlayerUnit, PlayerName )
self:E( { "Task Assigned", self.Dispatcher } )
--- This test is required, because the state transition will be fired also when the state does not change in case of an event.
if From ~= "Assigned" then
self:E( { From, Event, To, PlayerUnit:GetName(), PlayerName } )
self:GetMission():GetCommandCenter():MessageToCoalition( "Task " .. self:GetName() .. " is assigned." )
-- Set the total Progress to be achieved.
@@ -1198,7 +1258,7 @@ function TASK:onenterAssigned( From, Event, To, PlayerUnit, PlayerName )
self:GetMission():__Start( 1 )
-- When the task is assigned, the task goal needs to be checked of the derived classes.
self:__Goal( -10 ) -- Polymorphic
self:__Goal( -10, PlayerUnit, PlayerName ) -- Polymorphic
self:SetMenu()
end
@@ -1239,6 +1299,23 @@ function TASK:onenterAborted( From, Event, To )
end
--- FSM function for a TASK
-- @param #TASK self
-- @param #string From
-- @param #string Event
-- @param #string To
function TASK:onenterCancelled( From, Event, To )
self:E( "Task Cancelled" )
if From ~= "Cancelled" then
self:GetMission():GetCommandCenter():MessageToCoalition( "Task " .. self:GetName() .. " has been cancelled! The tactical situation has changed." )
self:UnAssignFromGroups()
self:SetMenu()
end
end
--- FSM function for a TASK
-- @param #TASK self
-- @param #string From
@@ -1313,7 +1390,7 @@ function TASK:onbeforeTimeOut( From, Event, To )
return false
end
do -- Dispatcher
do -- Links
--- Set dispatcher of a task
-- @param #TASK self
@@ -1323,6 +1400,19 @@ do -- Dispatcher
self.Dispatcher = Dispatcher
end
--- Set detection of a task
-- @param #TASK self
-- @param Function.Detection#DETECTION_BASE Detection
-- @param #number DetectedItemIndex
-- @return #TASK
function TASK:SetDetection( Detection, DetectedItemIndex )
self:E({DetectedItemIndex,Detection})
self.Detection = Detection
self.DetectedItemIndex = DetectedItemIndex
end
end
do -- Reporting
@@ -1330,55 +1420,74 @@ do -- Reporting
--- Create a summary report of the Task.
-- List the Task Name and Status
-- @param #TASK self
-- @param Wrapper.Group#GROUP ReportGroup
-- @return #string
function TASK:ReportSummary() --R2.1 fixed report. Now nicely formatted and contains the info required.
function TASK:ReportSummary( ReportGroup )
local Report = REPORT:New()
-- List the name of the Task.
local Name = self:GetName()
Report:Add( "Task " .. self:GetName() )
-- Determine the status of the Task.
local Status = "<" .. self:GetState() .. ">"
Report:Add( "State: <" .. self:GetState() .. ">" )
Report:Add( 'Task ' .. Name .. ' - State ' .. Status )
return Report:Text()
if self.TaskInfo["Coordinate"] then
local TaskInfoIDText = string.format( "%s: ", "Coordinate" )
local TaskCoord = self.TaskInfo["Coordinate"].TaskInfoText -- Core.Point#COORDINATE
Report:Add( TaskInfoIDText .. TaskCoord:ToString( ReportGroup, nil, self ) )
end
return Report:Text( ', ' )
end
--- Create an overiew report of the Task.
-- List the Task Name and Status
-- @param #TASK self
-- @return #string
function TASK:ReportOverview( ReportGroup ) --R2.1 fixed report. Now nicely formatted and contains the info required.
function TASK:ReportOverview( ReportGroup )
self:UpdateTaskInfo()
-- List the name of the Task.
local Name = self:GetName()
local Report = REPORT:New( Name )
local TaskName = self:GetName()
local Report = REPORT:New()
local Line = 0
local LineReport = REPORT:New()
-- Determine the status of the Task.
local Status = "<" .. self:GetState() .. ">"
for TaskInfoID, TaskInfo in pairs( self.TaskInfo ) do
for TaskInfoID, TaskInfo in UTILS.spairs( self.TaskInfo, function( t, a, b ) return t[a].TaskInfoOrder < t[b].TaskInfoOrder end ) do
self:F( { TaskInfo = TaskInfo } )
if Line < math.floor( TaskInfo.TaskInfoOrder / 10 ) then
if Line ~= 0 then
Report:AddIndent( LineReport:Text( ", " ) )
else
Report:Add( "Task " .. TaskName .. ", " .. LineReport:Text( ", " ) )
end
LineReport = REPORT:New()
Line = math.floor( TaskInfo.TaskInfoOrder / 10 )
end
local TaskInfoIDText = string.format( "%s: ", TaskInfoID )
if type(TaskInfo) == "string" then
Report:Add( TaskInfoIDText .. TaskInfo )
if type( TaskInfo.TaskInfoText ) == "string" then
LineReport:Add( TaskInfoIDText .. TaskInfo.TaskInfoText )
elseif type(TaskInfo) == "table" then
if TaskInfoID == "Coordinates" then
local FromCoordinate = ReportGroup:GetUnit(1):GetCoordinate()
local ToCoordinate = TaskInfo -- Core.Point#COORDINATE
if TaskInfoID == "Coordinate" then
local ToCoordinate = TaskInfo.TaskInfoText -- Core.Point#COORDINATE
--Report:Add( TaskInfoIDText )
Report:Add( ToCoordinate:ToString( ReportGroup ) )
LineReport:Add( TaskInfoIDText .. ToCoordinate:ToString( ReportGroup, nil, self ) )
--Report:AddIndent( ToCoordinate:ToStringBULLS( ReportGroup:GetCoalition() ) )
else
end
end
end
Report:AddIndent( LineReport:Text( ", " ) )
return Report:Text( ", ")
return Report:Text()
end
--- Create a count of the players in the Task.
@@ -1430,6 +1539,8 @@ end
-- @return #string
function TASK:ReportDetails( ReportGroup )
self:UpdateTaskInfo()
local Report = REPORT:New():SetIndent( 3 )
-- List the name of the Task.
@@ -1438,6 +1549,8 @@ function TASK:ReportDetails( ReportGroup )
-- Determine the status of the Task.
local Status = "<" .. self:GetState() .. ">"
Report:Add( "Task " .. Name .. " - " .. Status .. " - Detailed Report" )
-- Loop each Unit active in the Task, and find Player Names.
local PlayerNames = self:GetPlayerNames()
@@ -1446,30 +1559,21 @@ function TASK:ReportDetails( ReportGroup )
PlayerReport:Add( "Group " .. PlayerGroup:GetCallsign() .. ": " .. PlayerName )
end
local Players = PlayerReport:Text()
Report:Add( "Task: " .. Name .. " - " .. Status .. " - Detailed Report" )
Report:Add( " - Players:" )
Report:AddIndent( Players )
for TaskInfoID, TaskInfo in pairs( self.TaskInfo ) do
if Players ~= "" then
Report:Add( " - Players assigned:" )
Report:AddIndent( Players )
end
for TaskInfoID, TaskInfo in pairs( self.TaskInfo, function( t, a, b ) return t[a].TaskInfoOrder < t[b].TaskInfoOrder end ) do
local TaskInfoIDText = string.format( " - %s: ", TaskInfoID )
if type(TaskInfo) == "string" then
Report:Add( TaskInfoIDText .. TaskInfo )
elseif type(TaskInfo) == "table" then
if TaskInfoID == "Coordinates" then
local FromCoordinate = ReportGroup:GetUnit(1):GetCoordinate()
local ToCoordinate = TaskInfo -- Core.Point#COORDINATE
Report:Add( TaskInfoIDText )
Report:AddIndent( ToCoordinate:ToStringBRA( FromCoordinate ) .. ", " .. TaskInfo:ToStringAspect( FromCoordinate ) )
Report:AddIndent( ToCoordinate:ToStringBULLS( ReportGroup:GetCoalition() ) )
else
end
local ReportText = self:GetReportDetail( ReportGroup, TaskInfoID, TaskInfo )
if ReportText then
Report:Add( ReportText )
end
end
return Report:Text()
end

View File

@@ -0,0 +1,287 @@
--- **Tasking** - The TASK_Protect models tasks for players to protect or capture specific zones.
--
-- ====
--
-- ### Author: **Sven Van de Velde (FlightControl)**
--
-- ### Contributions: MillerTime
--
-- ====
--
-- @module TaskZoneCapture
do -- TASK_ZONE_GOAL
--- The TASK_ZONE_GOAL class
-- @type TASK_ZONE_GOAL
-- @field Core.ZoneGoal#ZONE_GOAL ZoneGoal
-- @extends Tasking.Task#TASK
--- # TASK_ZONE_GOAL class, extends @{Task#TASK}
--
-- The TASK_ZONE_GOAL class defines the task to protect or capture a protection zone.
-- The TASK_ZONE_GOAL is implemented using a @{Fsm#FSM_TASK}, and has the following statuses:
--
-- * **None**: Start of the process
-- * **Planned**: The A2G task is planned.
-- * **Assigned**: The A2G task is assigned to a @{Group#GROUP}.
-- * **Success**: The A2G task is successfully completed.
-- * **Failed**: The A2G task has failed. This will happen if the player exists the task early, without communicating a possible cancellation to HQ.
--
-- ## Set the scoring of achievements in an A2G attack.
--
-- Scoring or penalties can be given in the following circumstances:
--
-- * @{#TASK_ZONE_GOAL.SetScoreOnDestroy}(): Set a score when a target in scope of the A2G attack, has been destroyed.
-- * @{#TASK_ZONE_GOAL.SetScoreOnSuccess}(): Set a score when all the targets in scope of the A2G attack, have been destroyed.
-- * @{#TASK_ZONE_GOAL.SetPenaltyOnFailed}(): Set a penalty when the A2G attack has failed.
--
-- @field #TASK_ZONE_GOAL
TASK_ZONE_GOAL = {
ClassName = "TASK_ZONE_GOAL",
}
--- Instantiates a new TASK_ZONE_GOAL.
-- @param #TASK_ZONE_GOAL self
-- @param Tasking.Mission#MISSION Mission
-- @param Set#SET_GROUP SetGroup The set of groups for which the Task can be assigned.
-- @param #string TaskName The name of the Task.
-- @param Core.ZoneGoal#ZONE_GOAL ZoneGoal
-- @return #TASK_ZONE_GOAL self
function TASK_ZONE_GOAL:New( Mission, SetGroup, TaskName, ZoneGoal, TaskType, TaskBriefing )
local self = BASE:Inherit( self, TASK:New( Mission, SetGroup, TaskName, TaskType, TaskBriefing ) ) -- #TASK_ZONE_GOAL
self:F()
self.ZoneGoal = ZoneGoal
self.TaskType = TaskType
local Fsm = self:GetUnitProcess()
Fsm:AddProcess ( "Planned", "Accept", ACT_ASSIGN_ACCEPT:New( self.TaskBriefing ), { Assigned = "StartMonitoring", Rejected = "Reject" } )
Fsm:AddTransition( "Assigned", "StartMonitoring", "Monitoring" )
Fsm:AddTransition( "Monitoring", "Monitor", "Monitoring", {} )
Fsm:AddTransition( "Monitoring", "RouteTo", "Monitoring" )
Fsm:AddProcess( "Monitoring", "RouteToZone", ACT_ROUTE_ZONE:New(), {} )
--Fsm:AddTransition( "Accounted", "DestroyedAll", "Accounted" )
--Fsm:AddTransition( "Accounted", "Success", "Success" )
Fsm:AddTransition( "Rejected", "Reject", "Aborted" )
Fsm:AddTransition( "Failed", "Fail", "Failed" )
self:SetTargetZone( self.ZoneGoal:GetZone() )
--- Test
-- @param #FSM_PROCESS self
-- @param Wrapper.Unit#UNIT TaskUnit
-- @param Tasking.Task#TASK_ZONE_GOAL Task
function Fsm:onafterStartMonitoring( TaskUnit, Task )
self:E( { self } )
self:__Monitor( 0.1 )
self:__RouteTo( 0.1 )
end
--- Monitor Loop
-- @param #FSM_PROCESS self
-- @param Wrapper.Unit#UNIT TaskUnit
-- @param Tasking.Task#TASK_ZONE_GOAL Task
function Fsm:onafterMonitor( TaskUnit, Task )
self:E( { self } )
self:__Monitor( 15 )
end
--- Test
-- @param #FSM_PROCESS self
-- @param Wrapper.Unit#UNIT TaskUnit
-- @param Tasking.Task_A2G#TASK_ZONE_GOAL Task
function Fsm:onafterRouteTo( TaskUnit, Task )
self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } )
-- Determine the first Unit from the self.TargetSetUnit
if Task:GetTargetZone( TaskUnit ) then
self:__RouteTo( 0.1 )
end
end
return self
end
--- @param #TASK_ZONE_GOAL self
-- @param Core.ZoneGoal#ZONE_GOAL ZoneGoal The ZoneGoal Engine.
function TASK_ZONE_GOAL:SetProtect( ZoneGoal )
self.ZoneGoal = ZoneGoal -- Core.ZoneGoal#ZONE_GOAL
end
--- @param #TASK_ZONE_GOAL self
function TASK_ZONE_GOAL:GetPlannedMenuText()
return self:GetStateString() .. " - " .. self:GetTaskName() .. " ( " .. self.ZoneGoal:GetZoneName() .. " )"
end
--- @param #TASK_ZONE_GOAL self
-- @param Core.Zone#ZONE_BASE TargetZone The Zone object where the Target is located on the map.
-- @param Wrapper.Unit#UNIT TaskUnit
function TASK_ZONE_GOAL:SetTargetZone( TargetZone, TaskUnit )
local ProcessUnit = self:GetUnitProcess( TaskUnit )
local ActRouteZone = ProcessUnit:GetProcess( "Monitoring", "RouteToZone" ) -- Actions.Act_Route#ACT_ROUTE_ZONE
ActRouteZone:SetZone( TargetZone )
end
--- @param #TASK_ZONE_GOAL self
-- @param Wrapper.Unit#UNIT TaskUnit
-- @return Core.Zone#ZONE_BASE The Zone object where the Target is located on the map.
function TASK_ZONE_GOAL:GetTargetZone( TaskUnit )
local ProcessUnit = self:GetUnitProcess( TaskUnit )
local ActRouteZone = ProcessUnit:GetProcess( "Monitoring", "RouteToZone" ) -- Actions.Act_Route#ACT_ROUTE_ZONE
return ActRouteZone:GetZone()
end
function TASK_ZONE_GOAL:SetGoalTotal( GoalTotal )
self.GoalTotal = GoalTotal
end
function TASK_ZONE_GOAL:GetGoalTotal()
return self.GoalTotal
end
function TASK_ZONE_GOAL:GetMarkInfo( TaskInfoID, TaskInfo )
if type( TaskInfo.TaskInfoText ) == "string" then
return string.format( "%s: %s", TaskInfoID, TaskInfo.TaskInfoText )
elseif type( TaskInfo ) == "table" then
if TaskInfoID == "Coordinate" then
end
end
return nil
end
function TASK_ZONE_GOAL:GetReportDetail( ReportGroup, TaskInfoID, TaskInfo )
if type( TaskInfo.TaskInfoText ) == "string" then
return string.format( " - %s: %s", TaskInfoID, TaskInfo.TaskInfoText )
elseif type(TaskInfo) == "table" then
if TaskInfoID == "Coordinate" then
local FromCoordinate = ReportGroup:GetUnit(1):GetCoordinate()
local ToCoordinate = TaskInfo.TaskInfoText -- Core.Point#COORDINATE
return string.format( " - %s: %s", TaskInfoID, ToCoordinate:ToString( ReportGroup:GetUnit( 1 ), nil, self ) )
else
end
end
end
end
do -- TASK_ZONE_CAPTURE
--- The TASK_ZONE_CAPTURE class
-- @type TASK_ZONE_CAPTURE
-- @field Core.ZoneGoalCoalition#ZONE_GOAL_COALITION ZoneGoal
-- @extends #TASK_ZONE_GOAL
--- # TASK_ZONE_CAPTURE class, extends @{TaskZoneGoal#TASK_ZONE_GOAL}
--
-- The TASK_ZONE_CAPTURE class defines an Suppression or Extermination of Air Defenses task for a human player to be executed.
-- These tasks are important to be executed as they will help to achieve air superiority at the vicinity.
--
-- The TASK_ZONE_CAPTURE is used by the @{Task_A2G_Dispatcher#TASK_A2G_DISPATCHER} to automatically create SEAD tasks
-- based on detected enemy ground targets.
--
-- @field #TASK_ZONE_CAPTURE
TASK_ZONE_CAPTURE = {
ClassName = "TASK_ZONE_CAPTURE",
}
--- Instantiates a new TASK_ZONE_CAPTURE.
-- @param #TASK_ZONE_CAPTURE self
-- @param Tasking.Mission#MISSION Mission
-- @param Core.Set#SET_GROUP SetGroup The set of groups for which the Task can be assigned.
-- @param #string TaskName The name of the Task.
-- @param Core.ZoneGoalCoalition#ZONE_GOAL_COALITION ZoneGoalCoalition
-- @param #string TaskBriefing The briefing of the task.
-- @return #TASK_ZONE_CAPTURE self
function TASK_ZONE_CAPTURE:New( Mission, SetGroup, TaskName, ZoneGoalCoalition, TaskBriefing)
local self = BASE:Inherit( self, TASK_ZONE_GOAL:New( Mission, SetGroup, TaskName, ZoneGoalCoalition, "CAPTURE", TaskBriefing ) ) -- #TASK_ZONE_CAPTURE
self:F()
Mission:AddTask( self )
self.TaskCoalition = ZoneGoalCoalition:GetCoalition()
self.TaskCoalitionName = ZoneGoalCoalition:GetCoalitionName()
self.TaskZoneName = ZoneGoalCoalition:GetZoneName()
ZoneGoalCoalition:MonitorDestroyedUnits()
self:SetBriefing(
TaskBriefing or
"Capture Zone " .. self.TaskZoneName
)
self:UpdateTaskInfo()
return self
end
--- Instantiates a new TASK_ZONE_CAPTURE.
-- @param #TASK_ZONE_CAPTURE self
function TASK_ZONE_CAPTURE:UpdateTaskInfo()
local ZoneCoordinate = self.ZoneGoal:GetZone():GetCoordinate()
self:SetInfo( "Coordinate", ZoneCoordinate, 0 )
self:SetInfo( "Zone Name", self.ZoneGoal:GetZoneName(), 10 )
self:SetInfo( "Zone Coalition", self.ZoneGoal:GetCoalitionName(), 11 )
end
function TASK_ZONE_CAPTURE:ReportOrder( ReportGroup )
local Coordinate = self:GetInfo( "Coordinate" )
--local Coordinate = self.TaskInfo.Coordinates.TaskInfoText
local Distance = ReportGroup:GetCoordinate():Get2DDistance( Coordinate )
return Distance
end
--- @param #TASK_ZONE_CAPTURE self
-- @param Wrapper.Unit#UNIT TaskUnit
function TASK_ZONE_CAPTURE:OnAfterGoal( From, Event, To, PlayerUnit, PlayerName )
self:E( { PlayerUnit = PlayerUnit } )
if self.ZoneGoal then
if self.ZoneGoal.Goal:IsAchieved() then
self:Success()
local TotalContributions = self.ZoneGoal.Goal:GetTotalContributions()
local PlayerContributions = self.ZoneGoal.Goal:GetPlayerContributions()
self:E( { TotalContributions = TotalContributions, PlayerContributions = PlayerContributions } )
for PlayerName, PlayerContribution in pairs( PlayerContributions ) do
local Scoring = self:GetScoring()
if Scoring then
Scoring:_AddMissionGoalScore( self.Mission, PlayerName, "Zone " .. self.ZoneGoal:GetZoneName() .." captured", PlayerContribution * 200 / TotalContributions )
end
end
end
end
self:__Goal( -10, PlayerUnit, PlayerName )
end
end

View File

@@ -75,7 +75,7 @@ do -- TASK_A2A
Fsm:AddTransition( { "ArrivedAtRendezVous", "HoldingAtRendezVous" }, "Engage", "Engaging" )
Fsm:AddTransition( { "ArrivedAtRendezVous", "HoldingAtRendezVous" }, "HoldAtRendezVous", "HoldingAtRendezVous" )
Fsm:AddProcess ( "Engaging", "Account", ACT_ACCOUNT_DEADS:New( self.TargetSetUnit, self.TaskType ), {} )
Fsm:AddProcess ( "Engaging", "Account", ACT_ACCOUNT_DEADS:New(), {} )
Fsm:AddTransition( "Engaging", "RouteToTarget", "Engaging" )
Fsm:AddProcess( "Engaging", "RouteToTargetZone", ACT_ROUTE_ZONE:New(), {} )
Fsm:AddProcess( "Engaging", "RouteToTargetPoint", ACT_ROUTE_POINT:New(), {} )
@@ -277,6 +277,35 @@ do -- TASK_A2A
return self.GoalTotal
end
function TASK_A2A:GetMarkInfo( TaskInfoID, TaskInfo )
if type( TaskInfo.TaskInfoText ) == "string" then
if TaskInfoID == "Targets" then
else
return string.format( "%s: %s", TaskInfoID, TaskInfo.TaskInfoText )
end
elseif type( TaskInfo ) == "table" then
if TaskInfoID == "Coordinate" then
end
end
return nil
end
function TASK_A2A:GetReportDetail( ReportGroup, TaskInfoID, TaskInfo )
if type( TaskInfo.TaskInfoText ) == "string" then
return string.format( "%s: %s", TaskInfoID, TaskInfo.TaskInfoText )
elseif type(TaskInfo) == "table" then
if TaskInfoID == "Coordinate" then
local FromCoordinate = ReportGroup:GetUnit(1):GetCoordinate()
local ToCoordinate = TaskInfo.TaskInfoText -- Core.Point#COORDINATE
return string.format( " - %s: %s", TaskInfoID, ToCoordinate:ToString( ReportGroup:GetUnit(1), nil, self ) )
else
end
end
end
end
@@ -328,16 +357,48 @@ do -- TASK_A2A_INTERCEPT
"Intercept incoming intruders.\n"
)
local TargetCoordinate = TargetSetUnit:GetFirst():GetCoordinate()
self:SetInfo( "Coordinates", TargetCoordinate )
self:SetInfo( "Threat", "[" .. string.rep( "", TargetSetUnit:CalculateThreatLevelA2G() ) .. "]" )
local DetectedItemsCount = TargetSetUnit:Count()
local DetectedItemsTypes = TargetSetUnit:GetTypeNames()
self:SetInfo( "Targets", string.format( "%d of %s", DetectedItemsCount, DetectedItemsTypes ) )
self:UpdateTaskInfo()
return self
end
end
function TASK_A2A_INTERCEPT:UpdateTaskInfo()
local TargetCoordinate = self.Detection and self.Detection:GetDetectedItemCoordinate( self.DetectedItemIndex ) or self.TargetSetUnit:GetFirst():GetCoordinate()
self:SetInfo( "Coordinate", TargetCoordinate, 0 )
self:SetInfo( "Threat", "[" .. string.rep( "", self.Detection and self.Detection:GetDetectedItemThreatLevel( self.DetectedItemIndex ) or self.TargetSetUnit:CalculateThreatLevelA2G() ) .. "]", 11 )
if self.Detection then
local DetectedItemsCount = self.TargetSetUnit:Count()
local ReportTypes = REPORT:New()
local TargetTypes = {}
for TargetUnitName, TargetUnit in pairs( self.TargetSetUnit:GetSet() ) do
local TargetType = self.Detection:GetDetectedUnitTypeName( TargetUnit )
if not TargetTypes[TargetType] then
TargetTypes[TargetType] = TargetType
ReportTypes:Add( TargetType )
end
end
self:SetInfo( "Targets", string.format( "%d of %s", DetectedItemsCount, ReportTypes:Text( ", " ) ), 10 )
else
local DetectedItemsCount = self.TargetSetUnit:Count()
local DetectedItemsTypes = self.TargetSetUnit:GetTypeNames()
self:SetInfo( "Targets", string.format( "%d of %s", DetectedItemsCount, DetectedItemsTypes ), 10 )
end
end
--- @param #TASK_A2A_INTERCEPT self
-- @param Wrapper.Group#GROUP ReportGroup
function TASK_A2A_INTERCEPT:ReportOrder( ReportGroup )
self:F( { TaskInfo = self.TaskInfo } )
local Coordinate = self.TaskInfo.Coordinates.TaskInfoText
local Distance = ReportGroup:GetCoordinate():Get2DDistance( Coordinate )
return Distance
end
--- @param #TASK_A2A_INTERCEPT self
@@ -439,7 +500,7 @@ do -- TASK_A2A_SWEEP
-- @param #string TaskBriefing The briefing of the task.
-- @return #TASK_A2A_SWEEP self
function TASK_A2A_SWEEP:New( Mission, SetGroup, TaskName, TargetSetUnit, TaskBriefing )
local self = BASE:Inherit( self, TASK_A2A:New( Mission, SetGroup, TaskName, TargetSetUnit, "INTERCEPT", TaskBriefing ) ) -- #TASK_A2A_SWEEP
local self = BASE:Inherit( self, TASK_A2A:New( Mission, SetGroup, TaskName, TargetSetUnit, "SWEEP", TaskBriefing ) ) -- #TASK_A2A_SWEEP
self:F()
Mission:AddTask( self )
@@ -451,17 +512,47 @@ do -- TASK_A2A_SWEEP
"Perform a fighter sweep. Incoming intruders were detected and could be hiding at the location.\n"
)
local TargetCoordinate = TargetSetUnit:GetFirst():GetCoordinate()
self:SetInfo( "Coordinates", TargetCoordinate )
self:SetInfo( "Assumed Threat", "[" .. string.rep( "", TargetSetUnit:CalculateThreatLevelA2G() ) .. "]" )
local DetectedItemsCount = TargetSetUnit:Count()
local DetectedItemsTypes = TargetSetUnit:GetTypeNames()
self:SetInfo( "Lost Targets", string.format( "%d of %s", DetectedItemsCount, DetectedItemsTypes ) )
self:UpdateTaskInfo()
return self
end
function TASK_A2A_SWEEP:UpdateTaskInfo()
local TargetCoordinate = self.Detection and self.Detection:GetDetectedItemCoordinate( self.DetectedItemIndex ) or self.TargetSetUnit:GetFirst():GetCoordinate()
self:SetInfo( "Coordinate", TargetCoordinate, 0 )
self:SetInfo( "Assumed Threat", "[" .. string.rep( "", self.Detection and self.Detection:GetDetectedItemThreatLevel( self.DetectedItemIndex ) or self.TargetSetUnit:CalculateThreatLevelA2G() ) .. "]", 11 )
if self.Detection then
local DetectedItemsCount = self.TargetSetUnit:Count()
local ReportTypes = REPORT:New()
local TargetTypes = {}
for TargetUnitName, TargetUnit in pairs( self.TargetSetUnit:GetSet() ) do
local TargetType = self.Detection:GetDetectedUnitTypeName( TargetUnit )
if not TargetTypes[TargetType] then
TargetTypes[TargetType] = TargetType
ReportTypes:Add( TargetType )
end
end
self:SetInfo( "Lost Targets", string.format( "%d of %s", DetectedItemsCount, ReportTypes:Text( ", " ) ), 10 )
else
local DetectedItemsCount = self.TargetSetUnit:Count()
local DetectedItemsTypes = self.TargetSetUnit:GetTypeNames()
self:SetInfo( "Lost Targets", string.format( "%d of %s", DetectedItemsCount, DetectedItemsTypes ), 10 )
end
end
function TASK_A2A_SWEEP:ReportOrder( ReportGroup )
local Coordinate = self.TaskInfo.Coordinates.TaskInfoText
local Distance = ReportGroup:GetCoordinate():Get2DDistance( Coordinate )
return Distance
end
--- @param #TASK_A2A_SWEEP self
function TASK_A2A_SWEEP:onafterGoal( TaskUnit, From, Event, To )
local TargetSetUnit = self.TargetSetUnit -- Core.Set#SET_UNIT
@@ -570,17 +661,46 @@ do -- TASK_A2A_ENGAGE
"Bogeys are nearby! Players close by are ordered to ENGAGE the intruders!\n"
)
local TargetCoordinate = TargetSetUnit:GetFirst():GetCoordinate()
self:SetInfo( "Coordinates", TargetCoordinate )
self:SetInfo( "Threat", "[" .. string.rep( "", TargetSetUnit:CalculateThreatLevelA2G() ) .. "]" )
local DetectedItemsCount = TargetSetUnit:Count()
local DetectedItemsTypes = TargetSetUnit:GetTypeNames()
self:SetInfo( "Targets", string.format( "%d of %s", DetectedItemsCount, DetectedItemsTypes ) )
self:UpdateTaskInfo()
return self
end
function TASK_A2A_ENGAGE:UpdateTaskInfo()
local TargetCoordinate = self.Detection and self.Detection:GetDetectedItemCoordinate( self.DetectedItemIndex ) or self.TargetSetUnit:GetFirst():GetCoordinate()
self:SetInfo( "Coordinate", TargetCoordinate, 0 )
self:SetInfo( "Threat", "[" .. string.rep( "", self.Detection and self.Detection:GetDetectedItemThreatLevel( self.DetectedItemIndex ) or self.TargetSetUnit:CalculateThreatLevelA2G() ) .. "]", 11 )
if self.Detection then
local DetectedItemsCount = self.TargetSetUnit:Count()
local ReportTypes = REPORT:New()
local TargetTypes = {}
for TargetUnitName, TargetUnit in pairs( self.TargetSetUnit:GetSet() ) do
local TargetType = self.Detection:GetDetectedUnitTypeName( TargetUnit )
if not TargetTypes[TargetType] then
TargetTypes[TargetType] = TargetType
ReportTypes:Add( TargetType )
end
end
self:SetInfo( "Targets", string.format( "%d of %s", DetectedItemsCount, ReportTypes:Text( ", " ) ), 10 )
else
local DetectedItemsCount = self.TargetSetUnit:Count()
local DetectedItemsTypes = self.TargetSetUnit:GetTypeNames()
self:SetInfo( "Targets", string.format( "%d of %s", DetectedItemsCount, DetectedItemsTypes ), 10 )
end
end
function TASK_A2A_ENGAGE:ReportOrder( ReportGroup )
local Coordinate = self.TaskInfo.Coordinates.TaskInfoText
local Distance = ReportGroup:GetCoordinate():Get2DDistance( Coordinate )
return Distance
end
--- @param #TASK_A2A_ENGAGE self
function TASK_A2A_ENGAGE:onafterGoal( TaskUnit, From, Event, To )
local TargetSetUnit = self.TargetSetUnit -- Core.Set#SET_UNIT

View File

@@ -93,7 +93,7 @@ do -- TASK_A2A_DISPATCHER
--
-- local EWRDetection = DETECTION_AREAS:New( EWRSet, 6000 )
-- EWRDetection:SetFriendliesRange( 10000 )
-- EWRDetection:SetDetectionInterval(30)
-- EWRDetection:SetRefreshTimeInterval(30)
--
-- -- Setup the A2A dispatcher, and initialize it.
-- A2ADispatcher = TASK_A2A_DISPATCHER:New( Mission, AttackGroups, EWRDetection )
@@ -197,7 +197,7 @@ do -- TASK_A2A_DISPATCHER
-- TODO: Check detection through radar.
self.Detection:FilterCategories( Unit.Category.AIRPLANE, Unit.Category.HELICOPTER )
self.Detection:InitDetectRadar( true )
self.Detection:SetDetectionInterval( 30 )
self.Detection:SetRefreshTimeInterval( 30 )
self:AddTransition( "Started", "Assign", "Started" )
@@ -382,9 +382,7 @@ do -- TASK_A2A_DISPATCHER
end
if DetectedItemChanged == true or Remove then
--self:E( "Removing Tasking: " .. Task:GetTaskName() )
Mission:RemoveTask( Task )
self.Tasks[DetectedItemIndex] = nil
Task = self:RemoveTask( DetectedItemIndex )
end
end
end
@@ -482,6 +480,11 @@ do -- TASK_A2A_DISPATCHER
return PlayersCount, PlayerTypesReport
end
function TASK_A2A_DISPATCHER:RemoveTask( TaskIndex )
self.Mission:RemoveTask( self.Tasks[TaskIndex] )
self.Tasks[TaskIndex] = nil
end
--- Assigns tasks in relation to the detected items to the @{Set#SET_GROUP}.
-- @param #TASK_A2A_DISPATCHER self
@@ -510,8 +513,7 @@ do -- TASK_A2A_DISPATCHER
for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do
Mission:GetCommandCenter():MessageToGroup( string.format( "Obsolete A2A task %s for %s removed.", TaskText, Mission:GetName() ), TaskGroup )
end
Mission:RemoveTask( Task )
self.Tasks[TaskIndex] = nil
Task = self:RemoveTask( TaskIndex )
end
end
end
@@ -538,21 +540,24 @@ do -- TASK_A2A_DISPATCHER
local TargetSetUnit = self:EvaluateENGAGE( DetectedItem ) -- Returns a SetUnit if there are targets to be INTERCEPTed...
if TargetSetUnit then
Task = TASK_A2A_ENGAGE:New( Mission, self.SetGroup, string.format( "ENGAGE.%03d", DetectedID ), TargetSetUnit )
Task:SetDetection( Detection, TaskIndex )
else
local TargetSetUnit = self:EvaluateINTERCEPT( DetectedItem ) -- Returns a SetUnit if there are targets to be INTERCEPTed...
if TargetSetUnit then
Task = TASK_A2A_INTERCEPT:New( Mission, self.SetGroup, string.format( "INTERCEPT.%03d", DetectedID ), TargetSetUnit )
Task:SetDetection( Detection, TaskIndex )
else
local TargetSetUnit = self:EvaluateSWEEP( DetectedItem ) -- Returns a SetUnit
if TargetSetUnit then
Task = TASK_A2A_SWEEP:New( Mission, self.SetGroup, string.format( "SWEEP.%03d", DetectedID ), TargetSetUnit )
Task:SetDetection( Detection, TaskIndex )
end
end
end
if Task then
self.Tasks[TaskIndex] = Task
Task:SetTargetZone( DetectedZone, DetectedSet:GetFirst():GetAltitude(), DetectedSet:GetFirst():GetHeading() )
Task:SetTargetZone( DetectedZone, DetectedItem.Coordinate.y, DetectedItem.Coordinate.Heading )
Task:SetDispatcher( self )
Mission:AddTask( Task )
@@ -565,9 +570,9 @@ do -- TASK_A2A_DISPATCHER
if Task then
local FriendliesCount, FriendliesReport = self:GetFriendliesNearBy( DetectedItem )
Task:SetInfo( "Friendlies", string.format( "%d ( %s )", FriendliesCount, FriendliesReport:Text( "," ) ) )
Task:SetInfo( "Friendlies", string.format( "%d ( %s )", FriendliesCount, FriendliesReport:Text( "," ) ), 30 )
local PlayersCount, PlayersReport = self:GetPlayerFriendliesNearBy( DetectedItem )
Task:SetInfo( "Players", string.format( "%d ( %s )", PlayersCount, PlayersReport:Text( "," ) ) )
Task:SetInfo( "Players", string.format( "%d ( %s )", PlayersCount, PlayersReport:Text( "," ) ), 31 )
end
-- OK, so the tasking has been done, now delete the changes reported for the area.

View File

@@ -75,7 +75,7 @@ do -- TASK_A2G
Fsm:AddTransition( { "ArrivedAtRendezVous", "HoldingAtRendezVous" }, "Engage", "Engaging" )
Fsm:AddTransition( { "ArrivedAtRendezVous", "HoldingAtRendezVous" }, "HoldAtRendezVous", "HoldingAtRendezVous" )
Fsm:AddProcess ( "Engaging", "Account", ACT_ACCOUNT_DEADS:New( self.TargetSetUnit, self.TaskType ), {} )
Fsm:AddProcess ( "Engaging", "Account", ACT_ACCOUNT_DEADS:New(), {} )
Fsm:AddTransition( "Engaging", "RouteToTarget", "Engaging" )
Fsm:AddProcess( "Engaging", "RouteToTargetZone", ACT_ROUTE_ZONE:New(), {} )
Fsm:AddProcess( "Engaging", "RouteToTargetPoint", ACT_ROUTE_POINT:New(), {} )
@@ -141,9 +141,9 @@ do -- TASK_A2G
else
local TargetUnit = Task.TargetSetUnit:GetFirst() -- Wrapper.Unit#UNIT
if TargetUnit then
local Coordinate = TargetUnit:GetCoordinate()
local Coordinate = TargetUnit:GetPointVec3()
self:T( { TargetCoordinate = Coordinate, Coordinate:GetX(), Coordinate:GetY(), Coordinate:GetZ() } )
Task:SetTargetCoordinate( TargetUnit:GetCoordinate(), TaskUnit )
Task:SetTargetCoordinate( Coordinate, TaskUnit )
end
self:__RouteToTargetPoint( 0.1 )
end
@@ -165,6 +165,15 @@ do -- TASK_A2G
return self
end
--- @param #TASK_A2G self
-- @param Core.Set#SET_UNIT TargetSetUnit The set of targets.
function TASK_A2G:SetTargetSetUnit( TargetSetUnit )
self.TargetSetUnit = TargetSetUnit
end
--- @param #TASK_A2G self
function TASK_A2G:GetPlannedMenuText()
@@ -276,6 +285,36 @@ do -- TASK_A2G
return self.GoalTotal
end
function TASK_A2G:GetMarkInfo( TaskInfoID, TaskInfo )
if type( TaskInfo.TaskInfoText ) == "string" then
if TaskInfoID == "Targets" then
else
return string.format( "%s: %s", TaskInfoID, TaskInfo.TaskInfoText )
end
elseif type( TaskInfo ) == "table" then
if TaskInfoID == "Coordinate" then
end
end
return nil
end
function TASK_A2G:GetReportDetail( ReportGroup, TaskInfoID, TaskInfo )
if type( TaskInfo.TaskInfoText ) == "string" then
return string.format( "%s: %s", TaskInfoID, TaskInfo.TaskInfoText )
elseif type(TaskInfo) == "table" then
if TaskInfoID == "Coordinate" then
local FromCoordinate = ReportGroup:GetUnit(1):GetCoordinate()
local ToCoordinate = TaskInfo.TaskInfoText -- Core.Point#COORDINATE
return string.format( " - %s: %s", TaskInfoID, ToCoordinate:ToString( ReportGroup:GetUnit(1), nil, self ) )
else
end
end
end
end
@@ -308,7 +347,7 @@ do -- TASK_A2G_SEAD
-- @param Core.Set#SET_UNIT TargetSetUnit
-- @param #string TaskBriefing The briefing of the task.
-- @return #TASK_A2G_SEAD self
function TASK_A2G_SEAD:New( Mission, SetGroup, TaskName, TargetSetUnit, TaskBriefing )
function TASK_A2G_SEAD:New( Mission, SetGroup, TaskName, TargetSetUnit, TaskBriefing)
local self = BASE:Inherit( self, TASK_A2G:New( Mission, SetGroup, TaskName, TargetSetUnit, "SEAD", TaskBriefing ) ) -- #TASK_A2G_SEAD
self:F()
@@ -316,20 +355,55 @@ do -- TASK_A2G_SEAD
self:SetBriefing(
TaskBriefing or
"Execute a Suppression of Enemy Air Defenses.\n"
"Execute a Suppression of Enemy Air Defenses."
)
local TargetCoordinate = TargetSetUnit:GetFirst():GetCoordinate()
self:SetInfo( "Coordinates", TargetCoordinate )
self:SetInfo( "Threat", "[" .. string.rep( "", TargetSetUnit:CalculateThreatLevelA2G() ) .. "]" )
local DetectedItemsCount = TargetSetUnit:Count()
local DetectedItemsTypes = TargetSetUnit:GetTypeNames()
self:SetInfo( "Targets", string.format( "%d of %s", DetectedItemsCount, DetectedItemsTypes ) )
return self
end
function TASK_A2G_SEAD:UpdateTaskInfo()
local TargetCoordinate = self.Detection and self.Detection:GetDetectedItemCoordinate( self.DetectedItemIndex ) or self.TargetSetUnit:GetFirst():GetCoordinate()
self:SetInfo( "Coordinate", TargetCoordinate, 0 )
local ThreatLevel, ThreatText
if self.Detection then
ThreatLevel, ThreatText = self.Detection:GetDetectedItemThreatLevel( self.DetectedItemIndex )
else
ThreatLevel, ThreatText = self.TargetSetUnit:CalculateThreatLevelA2G()
end
self:SetInfo( "Threat", ThreatText .. " [" .. string.rep( "", ThreatLevel ) .. "]", 11 )
if self.Detection then
local DetectedItemsCount = self.TargetSetUnit:Count()
local ReportTypes = REPORT:New()
local TargetTypes = {}
for TargetUnitName, TargetUnit in pairs( self.TargetSetUnit:GetSet() ) do
local TargetType = self.Detection:GetDetectedUnitTypeName( TargetUnit )
if not TargetTypes[TargetType] then
TargetTypes[TargetType] = TargetType
ReportTypes:Add( TargetType )
end
end
self:SetInfo( "Targets", string.format( "%d of %s", DetectedItemsCount, ReportTypes:Text( ", " ) ), 10 )
else
local DetectedItemsCount = self.TargetSetUnit:Count()
local DetectedItemsTypes = self.TargetSetUnit:GetTypeNames()
self:SetInfo( "Targets", string.format( "%d of %s", DetectedItemsCount, DetectedItemsTypes ), 10 )
end
end
function TASK_A2G_SEAD:ReportOrder( ReportGroup )
local Coordinate = self:GetInfo( "Coordinate" )
--local Coordinate = self.TaskInfo.Coordinates.TaskInfoText
local Distance = ReportGroup:GetCoordinate():Get2DDistance( Coordinate )
return Distance
end
--- @param #TASK_A2G_SEAD self
function TASK_A2G_SEAD:onafterGoal( TaskUnit, From, Event, To )
local TargetSetUnit = self.TargetSetUnit -- Core.Set#SET_UNIT
@@ -341,7 +415,7 @@ do -- TASK_A2G_SEAD
self:__Goal( -10 )
end
--- Set a score when a target in scope of the A2A attack, has been destroyed .
--- Set a score when a target in scope of the A2G attack, has been destroyed .
-- @param #TASK_A2G_SEAD self
-- @param #string PlayerName The name of the player.
-- @param #number Score The score in points to be granted when task process has been achieved.
@@ -357,7 +431,7 @@ do -- TASK_A2G_SEAD
return self
end
--- Set a score when all the targets in scope of the A2A attack, have been destroyed.
--- Set a score when all the targets in scope of the A2G attack, have been destroyed.
-- @param #TASK_A2G_SEAD self
-- @param #string PlayerName The name of the player.
-- @param #number Score The score in points.
@@ -373,7 +447,7 @@ do -- TASK_A2G_SEAD
return self
end
--- Set a penalty when the A2A attack has failed.
--- Set a penalty when the A2G attack has failed.
-- @param #TASK_A2G_SEAD self
-- @param #string PlayerName The name of the player.
-- @param #number Penalty The penalty in points, must be a negative value!
@@ -429,19 +503,66 @@ do -- TASK_A2G_BAI
self:SetBriefing(
TaskBriefing or
"Execute a Battlefield Air Interdiction of a group of enemy targets.\n"
"Execute a Battlefield Air Interdiction of a group of enemy targets."
)
local TargetCoordinate = TargetSetUnit:GetFirst():GetCoordinate()
self:SetInfo( "Coordinates", TargetCoordinate )
self:SetInfo( "Threat", "[" .. string.rep( "", TargetSetUnit:CalculateThreatLevelA2G() ) .. "]" )
local DetectedItemsCount = TargetSetUnit:Count()
local DetectedItemsTypes = TargetSetUnit:GetTypeNames()
self:SetInfo( "Targets", string.format( "%d of %s", DetectedItemsCount, DetectedItemsTypes ) )
return self
end
end
function TASK_A2G_BAI:UpdateTaskInfo()
self:E({self.Detection, self.DetectedItemIndex})
local TargetCoordinate = self.Detection and self.Detection:GetDetectedItemCoordinate( self.DetectedItemIndex ) or self.TargetSetUnit:GetFirst():GetCoordinate()
self:SetInfo( "Coordinate", TargetCoordinate, 0 )
local ThreatLevel, ThreatText
if self.Detection then
ThreatLevel, ThreatText = self.Detection:GetDetectedItemThreatLevel( self.DetectedItemIndex )
else
ThreatLevel, ThreatText = self.TargetSetUnit:CalculateThreatLevelA2G()
end
self:SetInfo( "Threat", ThreatText .. " [" .. string.rep( "", ThreatLevel ) .. "]", 11 )
if self.Detection then
local DetectedItemsCount = self.TargetSetUnit:Count()
local ReportTypes = REPORT:New()
local TargetTypes = {}
for TargetUnitName, TargetUnit in pairs( self.TargetSetUnit:GetSet() ) do
local TargetType = self.Detection:GetDetectedUnitTypeName( TargetUnit )
if not TargetTypes[TargetType] then
TargetTypes[TargetType] = TargetType
ReportTypes:Add( TargetType )
end
end
self:SetInfo( "Targets", string.format( "%d of %s", DetectedItemsCount, ReportTypes:Text( ", " ) ), 10 )
else
local DetectedItemsCount = self.TargetSetUnit:Count()
local DetectedItemsTypes = self.TargetSetUnit:GetTypeNames()
self:SetInfo( "Targets", string.format( "%d of %s", DetectedItemsCount, DetectedItemsTypes ), 10 )
end
local TargetCoordinate = self:GetInfo( "Coordinate" ) -- Core.Point#COORDINATE
local Velocity = self.TargetSetUnit:GetVelocityVec3()
local Heading = self.TargetSetUnit:GetHeading()
TargetCoordinate:SetHeading( Heading )
TargetCoordinate:SetVelocity( Velocity )
self:SetInfo( "Position", "Targets are" .. TargetCoordinate:GetMovingText() .. ".", 12 )
end
function TASK_A2G_BAI:ReportOrder( ReportGroup )
local Coordinate = self:GetInfo( "Coordinate" )
--local Coordinate = self.TaskInfo.Coordinates.TaskInfoText
local Distance = ReportGroup:GetCoordinate():Get2DDistance( Coordinate )
return Distance
end
--- @param #TASK_A2G_BAI self
function TASK_A2G_BAI:onafterGoal( TaskUnit, From, Event, To )
@@ -454,7 +575,7 @@ do -- TASK_A2G_BAI
self:__Goal( -10 )
end
--- Set a score when a target in scope of the A2A attack, has been destroyed .
--- Set a score when a target in scope of the A2G attack, has been destroyed .
-- @param #TASK_A2G_BAI self
-- @param #string PlayerName The name of the player.
-- @param #number Score The score in points to be granted when task process has been achieved.
@@ -470,7 +591,7 @@ do -- TASK_A2G_BAI
return self
end
--- Set a score when all the targets in scope of the A2A attack, have been destroyed.
--- Set a score when all the targets in scope of the A2G attack, have been destroyed.
-- @param #TASK_A2G_BAI self
-- @param #string PlayerName The name of the player.
-- @param #number Score The score in points.
@@ -486,7 +607,7 @@ do -- TASK_A2G_BAI
return self
end
--- Set a penalty when the A2A attack has failed.
--- Set a penalty when the A2G attack has failed.
-- @param #TASK_A2G_BAI self
-- @param #string PlayerName The name of the player.
-- @param #number Penalty The penalty in points, must be a negative value!
@@ -541,20 +662,56 @@ do -- TASK_A2G_CAS
self:SetBriefing(
TaskBriefing or
"Execute a Close Air Support for a group of enemy targets.\n" ..
"Beware of friendlies at the vicinity!\n"
"Execute a Close Air Support for a group of enemy targets. " ..
"Beware of friendlies at the vicinity! "
)
local TargetCoordinate = TargetSetUnit:GetFirst():GetCoordinate()
self:SetInfo( "Coordinates", TargetCoordinate )
self:SetInfo( "Threat", "[" .. string.rep( "", TargetSetUnit:CalculateThreatLevelA2G() ) .. "]" )
local DetectedItemsCount = TargetSetUnit:Count()
local DetectedItemsTypes = TargetSetUnit:GetTypeNames()
self:SetInfo( "Targets", string.format( "%d of %s", DetectedItemsCount, DetectedItemsTypes ) )
return self
end
function TASK_A2G_CAS:UpdateTaskInfo()
local TargetCoordinate = ( self.Detection and self.Detection:GetDetectedItemCoordinate( self.DetectedItemIndex ) ) or self.TargetSetUnit:GetFirst():GetCoordinate()
self:SetInfo( "Coordinate", TargetCoordinate, 0 )
local ThreatLevel, ThreatText
if self.Detection then
ThreatLevel, ThreatText = self.Detection:GetDetectedItemThreatLevel( self.DetectedItemIndex )
else
ThreatLevel, ThreatText = self.TargetSetUnit:CalculateThreatLevelA2G()
end
self:SetInfo( "Threat", ThreatText .. " [" .. string.rep( "", ThreatLevel ) .. "]", 11 )
if self.Detection then
local DetectedItemsCount = self.TargetSetUnit:Count()
local ReportTypes = REPORT:New()
local TargetTypes = {}
for TargetUnitName, TargetUnit in pairs( self.TargetSetUnit:GetSet() ) do
local TargetType = self.Detection:GetDetectedUnitTypeName( TargetUnit )
if not TargetTypes[TargetType] then
TargetTypes[TargetType] = TargetType
ReportTypes:Add( TargetType )
end
end
self:SetInfo( "Targets", string.format( "%d of %s", DetectedItemsCount, ReportTypes:Text( ", " ) ), 10 )
else
local DetectedItemsCount = self.TargetSetUnit:Count()
local DetectedItemsTypes = self.TargetSetUnit:GetTypeNames()
self:SetInfo( "Targets", string.format( "%d of %s", DetectedItemsCount, DetectedItemsTypes ), 10 )
end
end
--- @param #TASK_A2G_CAS self
function TASK_A2G_CAS:ReportOrder( ReportGroup )
local Coordinate = self:GetInfo( "Coordinate" )
local Distance = ReportGroup:GetCoordinate():Get2DDistance( Coordinate )
return Distance
end
--- @param #TASK_A2G_CAS self
function TASK_A2G_CAS:onafterGoal( TaskUnit, From, Event, To )
@@ -567,7 +724,7 @@ do -- TASK_A2G_CAS
self:__Goal( -10 )
end
--- Set a score when a target in scope of the A2A attack, has been destroyed .
--- Set a score when a target in scope of the A2G attack, has been destroyed .
-- @param #TASK_A2G_CAS self
-- @param #string PlayerName The name of the player.
-- @param #number Score The score in points to be granted when task process has been achieved.
@@ -583,7 +740,7 @@ do -- TASK_A2G_CAS
return self
end
--- Set a score when all the targets in scope of the A2A attack, have been destroyed.
--- Set a score when all the targets in scope of the A2G attack, have been destroyed.
-- @param #TASK_A2G_CAS self
-- @param #string PlayerName The name of the player.
-- @param #number Score The score in points.
@@ -599,7 +756,7 @@ do -- TASK_A2G_CAS
return self
end
--- Set a penalty when the A2A attack has failed.
--- Set a penalty when the A2G attack has failed.
-- @param #TASK_A2G_CAS self
-- @param #string PlayerName The name of the player.
-- @param #number Penalty The penalty in points, must be a negative value!

View File

@@ -59,6 +59,7 @@ do -- TASK_A2G_DISPATCHER
self.Mission = Mission
self.Detection:FilterCategories( Unit.Category.GROUND_UNIT, Unit.Category.SHIP )
self.Detection:FilterFriendliesCategory( Unit.Category.GROUND_UNIT )
self:AddTransition( "Started", "Assign", "Started" )
@@ -81,7 +82,7 @@ do -- TASK_A2G_DISPATCHER
--- Creates a SEAD task when there are targets for it.
-- @param #TASK_A2G_DISPATCHER self
-- @param Functional.Detection#DETECTION_AREAS.DetectedItem DetectedItem
-- @return Set#SET_UNIT TargetSetUnit: The target set of units.
-- @return Core.Set#SET_UNIT TargetSetUnit: The target set of units.
-- @return #nil If there are no targets to be set.
function TASK_A2G_DISPATCHER:EvaluateSEAD( DetectedItem )
self:F( { DetectedItem.ItemID } )
@@ -109,7 +110,8 @@ do -- TASK_A2G_DISPATCHER
--- Creates a CAS task when there are targets for it.
-- @param #TASK_A2G_DISPATCHER self
-- @param Functional.Detection#DETECTION_AREAS.DetectedItem DetectedItem
-- @return Tasking.Task#TASK
-- @return Core.Set#SET_UNIT TargetSetUnit: The target set of units.
-- @return #nil If there are no targets to be set.
function TASK_A2G_DISPATCHER:EvaluateCAS( DetectedItem )
self:F( { DetectedItem.ItemID } )
@@ -120,8 +122,9 @@ do -- TASK_A2G_DISPATCHER
-- Determine if the set has radar targets. If it does, construct a SEAD task.
local GroundUnitCount = DetectedSet:HasGroundUnits()
local FriendliesNearBy = self.Detection:IsFriendliesNearBy( DetectedItem )
local RadarCount = DetectedSet:HasSEAD()
if GroundUnitCount > 0 and FriendliesNearBy == true then
if RadarCount == 0 and GroundUnitCount > 0 and FriendliesNearBy == true then
-- Copy the Set
local TargetSetUnit = SET_UNIT:New()
@@ -137,7 +140,8 @@ do -- TASK_A2G_DISPATCHER
--- Creates a BAI task when there are targets for it.
-- @param #TASK_A2G_DISPATCHER self
-- @param Functional.Detection#DETECTION_AREAS.DetectedItem DetectedItem
-- @return Tasking.Task#TASK
-- @return Core.Set#SET_UNIT TargetSetUnit: The target set of units.
-- @return #nil If there are no targets to be set.
function TASK_A2G_DISPATCHER:EvaluateBAI( DetectedItem, FriendlyCoalition )
self:F( { DetectedItem.ItemID } )
@@ -148,8 +152,9 @@ do -- TASK_A2G_DISPATCHER
-- Determine if the set has radar targets. If it does, construct a SEAD task.
local GroundUnitCount = DetectedSet:HasGroundUnits()
local FriendliesNearBy = self.Detection:IsFriendliesNearBy( DetectedItem )
local RadarCount = DetectedSet:HasSEAD()
if GroundUnitCount > 0 and FriendliesNearBy == false then
if RadarCount == 0 and GroundUnitCount > 0 and FriendliesNearBy == false then
-- Copy the Set
local TargetSetUnit = SET_UNIT:New()
@@ -162,6 +167,12 @@ do -- TASK_A2G_DISPATCHER
return nil
end
function TASK_A2G_DISPATCHER:RemoveTask( TaskIndex )
self.Mission:RemoveTask( self.Tasks[TaskIndex] )
self.Tasks[TaskIndex] = nil
end
--- Evaluates the removal of the Task from the Mission.
-- Can only occur when the DetectedItem is Changed AND the state of the Task is "Planned".
-- @param #TASK_A2G_DISPATCHER self
@@ -173,10 +184,9 @@ do -- TASK_A2G_DISPATCHER
function TASK_A2G_DISPATCHER:EvaluateRemoveTask( Mission, Task, TaskIndex, DetectedItemChanged )
if Task then
if Task:IsStatePlanned() and DetectedItemChanged == true then
if ( Task:IsStatePlanned() and DetectedItemChanged == true ) or Task:IsStateCancelled() then
--self:E( "Removing Tasking: " .. Task:GetTaskName() )
Mission:RemoveTask( Task )
self.Tasks[TaskIndex] = nil
self:RemoveTask( TaskIndex )
end
end
@@ -211,6 +221,7 @@ do -- TASK_A2G_DISPATCHER
for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do
Mission:GetCommandCenter():MessageToGroup( string.format( "Obsolete A2G task %s for %s removed.", TaskText, Mission:GetName() ), TaskGroup )
end
Task = self:RemoveTask( TaskIndex )
Mission:RemoveTask( Task )
self.Tasks[TaskIndex] = nil
end
@@ -224,20 +235,119 @@ do -- TASK_A2G_DISPATCHER
local DetectedSet = DetectedItem.Set -- Core.Set#SET_UNIT
local DetectedZone = DetectedItem.Zone
--self:E( { "Targets in DetectedItem", DetectedItem.ItemID, DetectedSet:Count(), tostring( DetectedItem ) } )
DetectedSet:Flush()
--DetectedSet:Flush()
local DetectedItemID = DetectedItem.ID
local TaskIndex = DetectedItem.Index
local DetectedItemChanged = DetectedItem.Changed
local Task = self.Tasks[TaskIndex]
Task = self:EvaluateRemoveTask( Mission, Task, TaskIndex, DetectedItemChanged ) -- Task will be removed if it is planned and changed.
self:E( { DetectedItemChanged = DetectedItemChanged, DetectedItemID = DetectedItemID, TaskIndex = TaskIndex } )
local Task = self.Tasks[TaskIndex] -- Tasking.Task_A2G#TASK_A2G
if Task then
-- If there is a Task and the task was assigned, then we check if the task was changed ... If it was, we need to reevaluate the targets.
if Task:IsStateAssigned() then
if DetectedItemChanged == true then -- The detection has changed, thus a new TargetSet is to be evaluated and set
local TargetsReport = REPORT:New()
local TargetSetUnit = self:EvaluateSEAD( DetectedItem ) -- Returns a SetUnit if there are targets to be SEADed...
if TargetSetUnit then
if Task:IsInstanceOf( TASK_A2G_SEAD ) then
Task:SetTargetSetUnit( TargetSetUnit )
Task:UpdateTaskInfo()
TargetsReport:Add( Detection:GetChangeText( DetectedItem ) )
else
Task:Cancel()
end
else
local TargetSetUnit = self:EvaluateCAS( DetectedItem ) -- Returns a SetUnit if there are targets to be CASed...
if TargetSetUnit then
if Task:IsInstanceOf( TASK_A2G_CAS ) then
Task:SetTargetSetUnit( TargetSetUnit )
Task:SetDetection( Detection, TaskIndex )
Task:UpdateTaskInfo()
TargetsReport:Add( Detection:GetChangeText( DetectedItem ) )
else
Task:Cancel()
Task = self:RemoveTask( TaskIndex )
end
else
local TargetSetUnit = self:EvaluateBAI( DetectedItem ) -- Returns a SetUnit if there are targets to be BAIed...
if TargetSetUnit then
if Task:IsInstanceOf( TASK_A2G_BAI ) then
Task:SetTargetSetUnit( TargetSetUnit )
Task:SetDetection( Detection, TaskIndex )
Task:UpdateTaskInfo()
TargetsReport:Add( Detection:GetChangeText( DetectedItem ) )
else
Task:Cancel()
Task = self:RemoveTask( TaskIndex )
end
end
end
end
-- Now we send to each group the changes, if any.
for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do
local TargetsText = TargetsReport:Text(", ")
if ( Mission:IsGroupAssigned(TaskGroup) ) and TargetsText ~= "" then
Mission:GetCommandCenter():MessageToGroup( string.format( "Task %s has change of targets:\n %s", Task:GetName(), TargetsText ), TaskGroup )
end
end
end
end
end
if Task then
if Task:IsStatePlanned() then
if DetectedItemChanged == true then -- The detection has changed, thus a new TargetSet is to be evaluated and set
if Task:IsInstanceOf( TASK_A2G_SEAD ) then
local TargetSetUnit = self:EvaluateSEAD( DetectedItem ) -- Returns a SetUnit if there are targets to be SEADed...
if TargetSetUnit then
Task:SetTargetSetUnit( TargetSetUnit )
Task:UpdateTaskInfo()
else
Task:Cancel()
Task = self:RemoveTask( TaskIndex )
end
else
if Task:IsInstanceOf( TASK_A2G_CAS ) then
local TargetSetUnit = self:EvaluateCAS( DetectedItem ) -- Returns a SetUnit if there are targets to be CASed...
if TargetSetUnit then
Task:SetTargetSetUnit( TargetSetUnit )
Task:SetDetection( Detection, TaskIndex )
Task:UpdateTaskInfo()
else
Task:Cancel()
Task = self:RemoveTask( TaskIndex )
end
else
if Task:IsInstanceOf( TASK_A2G_BAI ) then
local TargetSetUnit = self:EvaluateBAI( DetectedItem ) -- Returns a SetUnit if there are targets to be BAIed...
if TargetSetUnit then
Task:SetTargetSetUnit( TargetSetUnit )
Task:SetDetection( Detection, TaskIndex )
Task:UpdateTaskInfo()
else
Task:Cancel()
Task = self:RemoveTask( TaskIndex )
end
else
Task:Cancel()
Task = self:RemoveTask( TaskIndex )
end
end
end
end
end
end
-- Evaluate SEAD
if not Task then
local TargetSetUnit = self:EvaluateSEAD( DetectedItem ) -- Returns a SetUnit if there are targets to be SEADed...
if TargetSetUnit then
Task = TASK_A2G_SEAD:New( Mission, self.SetGroup, string.format( "SEAD.%03d", DetectedItemID ), TargetSetUnit )
Task:SetDetection( Detection, TaskIndex )
end
-- Evaluate CAS
@@ -245,6 +355,7 @@ do -- TASK_A2G_DISPATCHER
local TargetSetUnit = self:EvaluateCAS( DetectedItem ) -- Returns a SetUnit if there are targets to be CASed...
if TargetSetUnit then
Task = TASK_A2G_CAS:New( Mission, self.SetGroup, string.format( "CAS.%03d", DetectedItemID ), TargetSetUnit )
Task:SetDetection( Detection, TaskIndex )
end
-- Evaluate BAI
@@ -252,6 +363,7 @@ do -- TASK_A2G_DISPATCHER
local TargetSetUnit = self:EvaluateBAI( DetectedItem, self.Mission:GetCommandCenter():GetPositionable():GetCoalition() ) -- Returns a SetUnit if there are targets to be BAIed...
if TargetSetUnit then
Task = TASK_A2G_BAI:New( Mission, self.SetGroup, string.format( "BAI.%03d", DetectedItemID ), TargetSetUnit )
Task:SetDetection( Detection, TaskIndex )
end
end
end
@@ -260,13 +372,13 @@ do -- TASK_A2G_DISPATCHER
self.Tasks[TaskIndex] = Task
Task:SetTargetZone( DetectedZone )
Task:SetDispatcher( self )
Task:UpdateTaskInfo()
Mission:AddTask( Task )
TaskReport:Add( Task:GetName() )
else
self:E("This should not happen")
end
end
@@ -278,7 +390,6 @@ do -- TASK_A2G_DISPATCHER
Mission:GetCommandCenter():SetMenu()
local TaskText = TaskReport:Text(", ")
for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do
if ( not Mission:IsGroupAssigned(TaskGroup) ) and TaskText ~= "" then
Mission:GetCommandCenter():MessageToGroup( string.format( "%s has tasks %s. Subscribe to a task using the radio menu.", Mission:GetName(), TaskText ), TaskGroup )

View File

@@ -161,10 +161,15 @@ do -- TASK_CARGO
self.TaskType = TaskType
self.SmokeColor = SMOKECOLOR.Red
self.CargoItemCount = {} -- Map of Carriers having a cargo item count to check the cargo loading limits.
self.CargoLimit = 2
self.DeployZones = {} -- setmetatable( {}, { __mode = "v" } ) -- weak table on value
local Fsm = self:GetUnitProcess()
Fsm:SetStartState( "Planned" )
Fsm:AddProcess ( "Planned", "Accept", ACT_ASSIGN_ACCEPT:New( self.TaskBriefing ), { Assigned = "SelectAction", Rejected = "Reject" } )
@@ -202,12 +207,19 @@ do -- TASK_CARGO
-- @param Wrapper.Unit#UNIT TaskUnit
-- @param Tasking.Task_CARGO#TASK_CARGO Task
function Fsm:onafterSelectAction( TaskUnit, Task )
self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } )
local TaskUnitName = TaskUnit:GetName()
self:E( { TaskUnit = TaskUnitName, Task = Task and Task:GetClassNameAndID() } )
local MenuTime = timer.getTime()
TaskUnit.Menu = MENU_GROUP:New( TaskUnit:GetGroup(), Task:GetName() .. " @ " .. TaskUnit:GetName() ):SetTime( MenuTime )
local CargoItemCount = TaskUnit:CargoItemCount()
--Task:GetMission():GetCommandCenter():MessageToGroup( "Cargo in carrier: " .. CargoItemCount, TaskUnit:GetGroup() )
Task.SetCargo:ForEachCargo(
@@ -226,51 +238,37 @@ do -- TASK_CARGO
-- Cargo
-- ):SetTime(MenuTime)
-- end
if Cargo:IsUnLoaded() then
if Cargo:IsInRadius( TaskUnit:GetPointVec2() ) then
MENU_GROUP_COMMAND:New(
TaskUnit:GetGroup(),
"Board cargo " .. Cargo.Name,
TaskUnit.Menu,
self.MenuBoardCargo,
self,
Cargo
):SetTime(MenuTime)
else
MENU_GROUP_COMMAND:New(
TaskUnit:GetGroup(),
"Route to Pickup cargo " .. Cargo.Name,
TaskUnit.Menu,
self.MenuRouteToPickup,
self,
Cargo
):SetTime(MenuTime)
if CargoItemCount < Task.CargoLimit then
if Cargo:IsInRadius( TaskUnit:GetPointVec2() ) then
local NotInDeployZones = true
for DeployZoneName, DeployZone in pairs( Task.DeployZones ) do
if Cargo:IsInZone( DeployZone ) then
NotInDeployZones = false
end
end
if NotInDeployZones then
if not TaskUnit:InAir() then
MENU_GROUP_COMMAND:New( TaskUnit:GetGroup(), "Board cargo " .. Cargo.Name, TaskUnit.Menu, self.MenuBoardCargo, self, Cargo ):SetTime(MenuTime)
end
end
else
MENU_GROUP_COMMAND:New( TaskUnit:GetGroup(), "Route to Pickup cargo " .. Cargo.Name, TaskUnit.Menu, self.MenuRouteToPickup, self, Cargo ):SetTime(MenuTime)
end
end
end
if Cargo:IsLoaded() then
MENU_GROUP_COMMAND:New(
TaskUnit:GetGroup(),
"Unboard cargo " .. Cargo.Name,
TaskUnit.Menu,
self.MenuUnBoardCargo,
self,
Cargo
):SetTime(MenuTime)
if not TaskUnit:InAir() then
MENU_GROUP_COMMAND:New( TaskUnit:GetGroup(), "Unboard cargo " .. Cargo.Name, TaskUnit.Menu, self.MenuUnBoardCargo, self, Cargo ):SetTime(MenuTime)
end
-- Deployzones are optional zones that can be selected to request routing information.
for DeployZoneName, DeployZone in pairs( Task.DeployZones ) do
if not Cargo:IsInZone( DeployZone ) then
MENU_GROUP_COMMAND:New(
TaskUnit:GetGroup(),
"Route to Deploy cargo at " .. DeployZoneName,
TaskUnit.Menu,
self.MenuRouteToDeploy,
self,
DeployZone
):SetTime(MenuTime)
MENU_GROUP_COMMAND:New( TaskUnit:GetGroup(), "Route to Deploy cargo at " .. DeployZoneName, TaskUnit.Menu, self.MenuRouteToDeploy, self, DeployZone ):SetTime(MenuTime)
end
end
end
@@ -284,9 +282,9 @@ do -- TASK_CARGO
self:__SelectAction( -15 )
--Task:GetMission():GetCommandCenter():MessageToGroup("Cargo menu is ready ...", TaskUnit:GetGroup() )
end
---
-- @param #FSM_PROCESS self
-- @param Wrapper.Unit#UNIT TaskUnit
@@ -320,8 +318,7 @@ do -- TASK_CARGO
--#Wrapper.Unit#UNIT
--- Route to Cargo
-- @param #FSM_PROCESS self
--- @param #FSM_PROCESS self
-- @param Wrapper.Unit#UNIT TaskUnit
-- @param Tasking.Task_Cargo#TASK_CARGO Task
-- @param From
@@ -341,15 +338,15 @@ do -- TASK_CARGO
---
-- @param #FSM_PROCESS self
--- @param #FSM_PROCESS self
-- @param Wrapper.Unit#UNIT TaskUnit
-- @param Tasking.Task_Cargo#TASK_CARGO Task
function Fsm:onafterArriveAtPickup( TaskUnit, Task )
self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } )
if self.Cargo:IsAlive() then
TaskUnit:Smoke( Task:GetSmokeColor(), 15 )
self.Cargo:Smoke( Task:GetSmokeColor(), 15 )
if TaskUnit:IsAir() then
Task:GetMission():GetCommandCenter():MessageToGroup( "Land", TaskUnit:GetGroup() )
self:__Land( -0.1, "Pickup" )
else
self:__SelectAction( -0.1 )
@@ -358,8 +355,7 @@ do -- TASK_CARGO
end
---
-- @param #FSM_PROCESS self
--- @param #FSM_PROCESS self
-- @param Wrapper.Unit#UNIT TaskUnit
-- @param Tasking.Task_Cargo#TASK_CARGO Task
function Fsm:onafterCancelRouteToPickup( TaskUnit, Task )
@@ -369,8 +365,7 @@ do -- TASK_CARGO
end
--- Route to DeployZone
-- @param #FSM_PROCESS self
--- @param #FSM_PROCESS self
-- @param Wrapper.Unit#UNIT TaskUnit
function Fsm:onafterRouteToDeploy( TaskUnit, Task, From, Event, To, DeployZone )
self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } )
@@ -382,14 +377,14 @@ do -- TASK_CARGO
end
---
-- @param #FSM_PROCESS self
--- @param #FSM_PROCESS self
-- @param Wrapper.Unit#UNIT TaskUnit
-- @param Tasking.Task_Cargo#TASK_CARGO Task
function Fsm:onafterArriveAtDeploy( TaskUnit, Task )
self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } )
if TaskUnit:IsAir() then
Task:GetMission():GetCommandCenter():MessageToGroup( "Land", TaskUnit:GetGroup() )
self:__Land( -0.1, "Deploy" )
else
self:__SelectAction( -0.1 )
@@ -397,8 +392,7 @@ do -- TASK_CARGO
end
---
-- @param #FSM_PROCESS self
--- @param #FSM_PROCESS self
-- @param Wrapper.Unit#UNIT TaskUnit
-- @param Tasking.Task_Cargo#TASK_CARGO Task
function Fsm:onafterCancelRouteToDeploy( TaskUnit, Task )
@@ -409,7 +403,7 @@ do -- TASK_CARGO
-- @param #FSM_PROCESS self
--- @param #FSM_PROCESS self
-- @param Wrapper.Unit#UNIT TaskUnit
-- @param Tasking.Task_Cargo#TASK_CARGO Task
function Fsm:onafterLand( TaskUnit, Task, From, Event, To, Action )
@@ -418,7 +412,6 @@ do -- TASK_CARGO
if self.Cargo:IsAlive() then
if self.Cargo:IsInRadius( TaskUnit:GetPointVec2() ) then
if TaskUnit:InAir() then
Task:GetMission():GetCommandCenter():MessageToGroup( "Land", TaskUnit:GetGroup() )
self:__Land( -10, Action )
else
Task:GetMission():GetCommandCenter():MessageToGroup( "Landed ...", TaskUnit:GetGroup() )
@@ -434,8 +427,7 @@ do -- TASK_CARGO
end
end
---
-- @param #FSM_PROCESS self
--- @param #FSM_PROCESS self
-- @param Wrapper.Unit#UNIT TaskUnit
-- @param Tasking.Task_Cargo#TASK_CARGO Task
function Fsm:onafterLanded( TaskUnit, Task, From, Event, To, Action )
@@ -458,8 +450,7 @@ do -- TASK_CARGO
end
end
---
-- @param #FSM_PROCESS self
--- @param #FSM_PROCESS self
-- @param Wrapper.Unit#UNIT TaskUnit
-- @param Tasking.Task_Cargo#TASK_CARGO Task
function Fsm:onafterPrepareBoarding( TaskUnit, Task, From, Event, To, Cargo )
@@ -471,8 +462,7 @@ do -- TASK_CARGO
end
end
---
-- @param #FSM_PROCESS self
--- @param #FSM_PROCESS self
-- @param Wrapper.Unit#UNIT TaskUnit
-- @param Tasking.Task_Cargo#TASK_CARGO Task
function Fsm:onafterBoard( TaskUnit, Task )
@@ -498,14 +488,18 @@ do -- TASK_CARGO
end
---
-- @param #FSM_PROCESS self
--- @param #FSM_PROCESS self
-- @param Wrapper.Unit#UNIT TaskUnit
-- @param Tasking.Task_Cargo#TASK_CARGO Task
function Fsm:onafterBoarded( TaskUnit, Task )
self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } )
local TaskUnitName = TaskUnit:GetName()
self:E( { TaskUnit = TaskUnitName, Task = Task and Task:GetClassNameAndID() } )
self.Cargo:MessageToGroup( "Boarded ...", TaskUnit:GetGroup() )
TaskUnit:AddCargo( self.Cargo )
self:__SelectAction( 1 )
-- TODO:I need to find a more decent solution for this.
@@ -579,12 +573,28 @@ do -- TASK_CARGO
-- @param Wrapper.Unit#UNIT TaskUnit
-- @param Tasking.Task_Cargo#TASK_CARGO Task
function Fsm:onafterUnBoarded( TaskUnit, Task )
self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } )
local TaskUnitName = TaskUnit:GetName()
self:E( { TaskUnit = TaskUnitName, Task = Task and Task:GetClassNameAndID() } )
self.Cargo:MessageToGroup( "UnBoarded ...", TaskUnit:GetGroup() )
TaskUnit:RemoveCargo( self.Cargo )
local NotInDeployZones = true
for DeployZoneName, DeployZone in pairs( Task.DeployZones ) do
if self.Cargo:IsInZone( DeployZone ) then
NotInDeployZones = false
end
end
if NotInDeployZones == false then
self.Cargo:SetDeployed( true )
end
-- TODO:I need to find a more decent solution for this.
Task:E( { CargoDeployed = Task.CargoDeployed } )
Task:E( { CargoDeployed = Task.CargoDeployed and "true" or "false" } )
Task:E( { CargoIsAlive = self.Cargo:IsAlive() and "true" or "false" } )
if self.Cargo:IsAlive() then
if Task.CargoDeployed then
Task:CargoDeployed( TaskUnit, self.Cargo, self.DeployZone )
@@ -598,7 +608,17 @@ do -- TASK_CARGO
return self
end
--- Set a limit on the amount of cargo items that can be loaded into the Carriers.
-- @param #TASK_CARGO self
-- @param CargoLimit Specifies a number of cargo items that can be loaded in the helicopter.
-- @return #TASK_CARGO
function TASK_CARGO:SetCargoLimit( CargoLimit )
self.CargoLimit = CargoLimit
return self
end
---@param Color Might be SMOKECOLOR.Blue, SMOKECOLOR.Red SMOKECOLOR.Orange, SMOKECOLOR.White or SMOKECOLOR.Green
function TASK_CARGO:SetSmokeColor(SmokeColor)
@@ -770,6 +790,17 @@ do -- TASK_CARGO
return self
end
function TASK_CARGO:SetGoalTotal()
self.GoalTotal = self.SetCargo:Count()
end
function TASK_CARGO:GetGoalTotal()
return self.GoalTotal
end
end
@@ -802,6 +833,8 @@ do -- TASK_CARGO_TRANSPORT
self:AddTransition( "*", "CargoPickedUp", "*" )
self:AddTransition( "*", "CargoDeployed", "*" )
self:E( { CargoDeployed = self.CargoDeployed ~= nil and "true" or "false" } )
--- OnBefore Transition Handler for Event CargoPickedUp.
-- @function [parent=#TASK_CARGO_TRANSPORT] OnBeforeCargoPickedUp
-- @param #TASK_CARGO_TRANSPORT self
@@ -880,7 +913,7 @@ do -- TASK_CARGO_TRANSPORT
local CargoType = Cargo:GetType()
local CargoName = Cargo:GetName()
local CargoCoordinate = Cargo:GetCoordinate()
CargoReport:Add( string.format( '- "%s" (%s) at %s', CargoName, CargoType, CargoCoordinate:ToString() ) )
CargoReport:Add( string.format( '- "%s" (%s) at %s', CargoName, CargoType, CargoCoordinate:ToStringMGRS() ) )
end
)
@@ -892,6 +925,12 @@ do -- TASK_CARGO_TRANSPORT
return self
end
function TASK_CARGO_TRANSPORT:ReportOrder( ReportGroup )
return true
end
---
-- @param #TASK_CARGO_TRANSPORT self
@@ -908,27 +947,36 @@ do -- TASK_CARGO_TRANSPORT
-- Loop the CargoSet (so evaluate each Cargo in the SET_CARGO ).
for CargoID, CargoData in pairs( Set ) do
local Cargo = CargoData -- Core.Cargo#CARGO
if Cargo:IsDeployed() then
-- Loop the DeployZones set for the TASK_CARGO_TRANSPORT.
for DeployZoneID, DeployZone in pairs( DeployZones ) do
-- If there is a Cargo not in one of DeployZones, then not all Cargo is deployed.
self:T( { Cargo.CargoObject } )
if Cargo:IsInZone( DeployZone ) then
else
CargoDeployed = false
-- Loop the DeployZones set for the TASK_CARGO_TRANSPORT.
for DeployZoneID, DeployZone in pairs( DeployZones ) do
-- If there is a Cargo not in one of DeployZones, then not all Cargo is deployed.
self:T( { Cargo.CargoObject } )
if Cargo:IsInZone( DeployZone ) == false then
CargoDeployed = false
end
end
else
CargoDeployed = false
end
end
return CargoDeployed
end
---
--- @param #TASK_CARGO_TRANSPORT self
function TASK_CARGO_TRANSPORT:onafterGoal( TaskUnit, From, Event, To )
local CargoSet = self.CargoSet
if self:IsAllCargoTransported() then
self:Success()
end
self:__Goal( -10 )
end
end

View File

@@ -31,7 +31,72 @@ FLARECOLOR = trigger.flareColor -- #FLARECOLOR
--- Utilities static class.
-- @type UTILS
UTILS = {}
UTILS = {
_MarkID = 1
}
--- Function to infer instance of an object
--
-- ### Examples:
--
-- * UTILS.IsInstanceOf( 'some text', 'string' ) will return true
-- * UTILS.IsInstanceOf( some_function, 'function' ) will return true
-- * UTILS.IsInstanceOf( 10, 'number' ) will return true
-- * UTILS.IsInstanceOf( false, 'boolean' ) will return true
-- * UTILS.IsInstanceOf( nil, 'nil' ) will return true
--
-- * UTILS.IsInstanceOf( ZONE:New( 'some zone', ZONE ) will return true
-- * UTILS.IsInstanceOf( ZONE:New( 'some zone', 'ZONE' ) will return true
-- * UTILS.IsInstanceOf( ZONE:New( 'some zone', 'zone' ) will return true
-- * UTILS.IsInstanceOf( ZONE:New( 'some zone', 'BASE' ) will return true
--
-- * UTILS.IsInstanceOf( ZONE:New( 'some zone', 'GROUP' ) will return false
--
--
-- @param object is the object to be evaluated
-- @param className is the name of the class to evaluate (can be either a string or a Moose class)
-- @return #boolean
UTILS.IsInstanceOf = function( object, className )
-- Is className NOT a string ?
if not type( className ) == 'string' then
-- Is className a Moose class ?
if type( className ) == 'table' and className.IsInstanceOf ~= nil then
-- Get the name of the Moose class as a string
className = className.ClassName
-- className is neither a string nor a Moose class, throw an error
else
-- I'm not sure if this should take advantage of MOOSE logging function, or throw an error for pcall
local err_str = 'className parameter should be a string; parameter received: '..type( className )
self:E( err_str )
return false
-- error( err_str )
end
end
-- Is the object a Moose class instance ?
if type( object ) == 'table' and object.IsInstanceOf ~= nil then
-- Use the IsInstanceOf method of the BASE class
return object:IsInstanceOf( className )
else
-- If the object is not an instance of a Moose class, evaluate against lua basic data types
local basicDataTypes = { 'string', 'number', 'function', 'boolean', 'nil', 'table' }
for _, basicDataType in ipairs( basicDataTypes ) do
if className == basicDataType then
return type( object ) == basicDataType
end
end
end
-- Check failed
return false
end
--from http://lua-users.org/wiki/CopyTable
@@ -171,22 +236,36 @@ UTILS.FeetToMeters = function(feet)
return feet*0.3048
end
UTILS.MpsToKnots = function(mps)
return mps*3600/1852
UTILS.KnotsToKmph = function(knots)
return knots* 1.852
end
UTILS.MpsToKmph = function(mps)
return mps*3.6
UTILS.KmphToMps = function( kmph )
return kmph / 3.6
end
UTILS.KnotsToMps = function(knots)
return knots*1852/3600
UTILS.MpsToKmph = function( mps )
return mps * 3.6
end
UTILS.KmphToMps = function(kmph)
return kmph/3.6
UTILS.MiphToMps = function( miph )
return miph * 0.44704
end
UTILS.MpsToMiph = function( mps )
return mps / 0.44704
end
UTILS.MpsToKnots = function( mps )
return mps * 3600 / 1852
end
UTILS.KnotsToMps = function( knots )
return knots * 1852 / 3600
end
--[[acc:
in DM: decimal point of minutes.
In DMS: decimal point of seconds.
@@ -238,12 +317,13 @@ UTILS.tostringLL = function( lat, lon, acc, DMS)
end
local secFrmtStr -- create the formatting string for the seconds place
if acc <= 0 then -- no decimal place.
secFrmtStr = '%02d'
else
local width = 3 + acc -- 01.310 - that's a width of 6, for example.
secFrmtStr = '%0' .. width .. '.' .. acc .. 'f'
end
secFrmtStr = '%02d'
-- if acc <= 0 then -- no decimal place.
-- secFrmtStr = '%02d'
-- else
-- local width = 3 + acc -- 01.310 - that's a width of 6, for example.
-- secFrmtStr = '%0' .. width .. '.' .. acc .. 'f'
-- end
return string.format('%02d', latDeg) .. ' ' .. string.format('%02d', latMin) .. '\' ' .. string.format(secFrmtStr, latSec) .. '"' .. latHemi .. ' '
.. string.format('%02d', lonDeg) .. ' ' .. string.format('%02d', lonMin) .. '\' ' .. string.format(secFrmtStr, lonSec) .. '"' .. lonHemi
@@ -327,3 +407,28 @@ function UTILS.spairs( t, order )
end
end
end
-- get a new mark ID for markings
function UTILS.GetMarkID()
UTILS._MarkID = UTILS._MarkID + 1
return UTILS._MarkID
end
-- Test if a Vec2 is in a radius of another Vec2
function UTILS.IsInRadius( InVec2, Vec2, Radius )
local InRadius = ( ( InVec2.x - Vec2.x ) ^2 + ( InVec2.y - Vec2.y ) ^2 ) ^ 0.5 <= Radius
return InRadius
end
-- Test if a Vec3 is in the sphere of another Vec3
function UTILS.IsInSphere( InVec3, Vec3, Radius )
local InSphere = ( ( InVec3.x - Vec3.x ) ^2 + ( InVec3.y - Vec3.y ) ^2 + ( InVec3.z - Vec3.z ) ^2 ) ^ 0.5 <= Radius
return InSphere
end

View File

@@ -57,7 +57,33 @@ AIRBASE = {
},
}
--- @field Caucasus
--- Enumeration to identify the airbases in the Caucasus region.
--
-- These are all airbases of Caucasus:
--
-- * AIRBASE.Caucasus.Gelendzhik
-- * AIRBASE.Caucasus.Krasnodar_Pashkovsky
-- * AIRBASE.Caucasus.Sukhumi_Babushara
-- * AIRBASE.Caucasus.Gudauta
-- * AIRBASE.Caucasus.Batumi
-- * AIRBASE.Caucasus.Senaki_Kolkhi
-- * AIRBASE.Caucasus.Kobuleti
-- * AIRBASE.Caucasus.Kutaisi
-- * AIRBASE.Caucasus.Tbilisi_Lochini
-- * AIRBASE.Caucasus.Soganlug
-- * AIRBASE.Caucasus.Vaziani
-- * AIRBASE.Caucasus.Anapa_Vityazevo
-- * AIRBASE.Caucasus.Krasnodar_Center
-- * AIRBASE.Caucasus.Novorossiysk
-- * AIRBASE.Caucasus.Krymsk
-- * AIRBASE.Caucasus.Maykop_Khanskaya
-- * AIRBASE.Caucasus.Sochi_Adler
-- * AIRBASE.Caucasus.Mineralnye_Vody
-- * AIRBASE.Caucasus.Nalchik
-- * AIRBASE.Caucasus.Mozdok
-- * AIRBASE.Caucasus.Beslan
--
-- @field Caucasus
AIRBASE.Caucasus = {
["Gelendzhik"] = "Gelendzhik",
["Krasnodar_Pashkovsky"] = "Krasnodar-Pashkovsky",
@@ -83,6 +109,28 @@ AIRBASE.Caucasus = {
}
--- @field Nevada
--
-- These are all airbases of Nevada:
--
-- * AIRBASE.Nevada.Creech_AFB
-- * AIRBASE.Nevada.Groom_Lake_AFB
-- * AIRBASE.Nevada.McCarran_International_Airport
-- * AIRBASE.Nevada.Nellis_AFB
-- * AIRBASE.Nevada.Beatty_Airport
-- * AIRBASE.Nevada.Boulder_City_Airport
-- * AIRBASE.Nevada.Echo_Bay
-- * AIRBASE.Nevada.Henderson_Executive_Airport
-- * AIRBASE.Nevada.Jean_Airport
-- * AIRBASE.Nevada.Laughlin_Airport
-- * AIRBASE.Nevada.Lincoln_County
-- * AIRBASE.Nevada.Mellan_Airstrip
-- * AIRBASE.Nevada.Mesquite
-- * AIRBASE.Nevada.Mina_Airport_3Q0
-- * AIRBASE.Nevada.North_Las_Vegas
-- * AIRBASE.Nevada.Pahute_Mesa_Airstrip
-- * AIRBASE.Nevada.Tonopah_Airport
-- * AIRBASE.Nevada.Tonopah_Test_Range_Airfield
--
AIRBASE.Nevada = {
["Creech_AFB"] = "Creech AFB",
["Groom_Lake_AFB"] = "Groom Lake AFB",
@@ -105,6 +153,40 @@ AIRBASE.Nevada = {
}
--- @field Normandy
--
-- These are all airbases of Normandy:
--
-- * AIRBASE.Normandy.Saint_Pierre_du_Mont
-- * AIRBASE.Normandy.Lignerolles
-- * AIRBASE.Normandy.Cretteville
-- * AIRBASE.Normandy.Maupertus
-- * AIRBASE.Normandy.Brucheville
-- * AIRBASE.Normandy.Meautis
-- * AIRBASE.Normandy.Cricqueville_en_Bessin
-- * AIRBASE.Normandy.Lessay
-- * AIRBASE.Normandy.Sainte_Laurent_sur_Mer
-- * AIRBASE.Normandy.Biniville
-- * AIRBASE.Normandy.Cardonville
-- * AIRBASE.Normandy.Deux_Jumeaux
-- * AIRBASE.Normandy.Chippelle
-- * AIRBASE.Normandy.Beuzeville
-- * AIRBASE.Normandy.Azeville
-- * AIRBASE.Normandy.Picauville
-- * AIRBASE.Normandy.Le_Molay
-- * AIRBASE.Normandy.Longues_sur_Mer
-- * AIRBASE.Normandy.Carpiquet
-- * AIRBASE.Normandy.Bazenville
-- * AIRBASE.Normandy.Sainte_Croix_sur_Mer
-- * AIRBASE.Normandy.Beny_sur_Mer
-- * AIRBASE.Normandy.Rucqueville
-- * AIRBASE.Normandy.Sommervieu
-- * AIRBASE.Normandy.Lantheuil
-- * AIRBASE.Normandy.Evreux
-- * AIRBASE.Normandy.Chailey
-- * AIRBASE.Normandy.Needs_Oar_Point
-- * AIRBASE.Normandy.Funtington
-- * AIRBASE.Normandy.Tangmere
-- * AIRBASE.Normandy.Ford
AIRBASE.Normandy = {
["Saint_Pierre_du_Mont"] = "Saint Pierre du Mont",
["Lignerolles"] = "Lignerolles",
@@ -149,6 +231,7 @@ function AIRBASE:Register( AirbaseName )
local self = BASE:Inherit( self, POSITIONABLE:New( AirbaseName ) )
self.AirbaseName = AirbaseName
self.AirbaseZone = ZONE_RADIUS:New( AirbaseName, self:GetVec2(), 2500 )
return self
end
@@ -185,5 +268,12 @@ function AIRBASE:GetDCSObject()
return nil
end
--- Get the airbase zone.
-- @param #AIRBASE self
-- @return Core.Zone#ZONE_RADIUS The zone radius of the airbase.
function AIRBASE:GetZone()
return self.AirbaseZone
end

View File

@@ -95,6 +95,22 @@
-- * @{#CONTROLLABLE.TaskCondition}: Return a condition section for a controlled task.
-- * @{#CONTROLLABLE.TaskControlled}: Return a Controlled Task taking a Task and a TaskCondition.
--
-- ### Call a function as a Task
--
-- A function can be called which is part of a Task. The method @{#CONTROLLABLE.TaskFunction}() prepares
-- a Task that can call a GLOBAL function from within the Controller execution.
-- This method can also be used to **embed a function call when a certain waypoint has been reached**.
-- See below the **Tasks at Waypoints** section.
--
-- Demonstration Mission: [GRP-502 - Route at waypoint to random point](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/release-2-2-pre/GRP - Group Commands/GRP-502 - Route at waypoint to random point)
--
-- ### Tasks at Waypoints
--
-- Special Task methods are available to set tasks at certain waypoints.
-- The method @{#CONTROLLABLE.SetTaskWaypoint}() helps preparing a Route, embedding a Task at the Waypoint of the Route.
--
-- This creates a Task element, with an action to call a function as part of a Wrapped Task.
--
-- ### Obtain the mission from controllable templates
--
-- Controllable templates contain complete mission descriptions. Sometimes you want to copy a complete mission from a controllable and assign it to another:
@@ -108,7 +124,15 @@
-- * @{#CONTROLLABLE.CommandDoScript}: Do Script command.
-- * @{#CONTROLLABLE.CommandSwitchWayPoint}: Perform a switch waypoint command.
--
-- ## CONTROLLABLE Option methods
-- ## Routing of Controllables
--
-- Different routing methods exist to route GROUPs and UNITs to different locations:
--
-- * @{#CONTROLLABLE.Route}(): Make the Controllable to follow a given route.
-- * @{#CONTROLLABLE.RouteGroundTo}(): Make the GROUND Controllable to drive towards a specific coordinate.
-- * @{#CONTROLLABLE.RouteAirTo}(): Make the AIR Controllable to fly towards a specific coordinate.
--
-- ## Option methods
--
-- Controllable **Option methods** change the behaviour of the Controllable while being alive.
--
@@ -257,6 +281,16 @@ function CONTROLLABLE:GetLife0()
return nil
end
--- Returns relative amount of fuel (from 0.0 to 1.0) the unit has in its internal tanks.
-- This method returns nil to ensure polymorphic behaviour! This method needs to be overridden by GROUP or UNIT.
-- @param #CONTROLLABLE self
-- @return #nil The CONTROLLABLE is not existing or alive.
function CONTROLLABLE:GetFuel()
self:F( self.ControllableName )
return nil
end
@@ -334,16 +368,25 @@ function CONTROLLABLE:SetTask( DCSTask, WaitTime )
if DCSControllable then
local Controller = self:_GetController()
local DCSControllableName = self:GetName()
-- When a controllable SPAWNs, it takes about a second to get the controllable in the simulator. Setting tasks to unspawned controllables provides unexpected results.
-- Therefore we schedule the functions to set the mission and options for the Controllable.
-- Controller.setTask( Controller, DCSTask )
if not WaitTime then
Controller:setTask( DCSTask )
local function SetTask( Controller, DCSTask )
if self and self:IsAlive() then
local Controller = self:_GetController()
Controller:setTask( DCSTask )
else
BASE:E( DCSControllableName .. " is not alive anymore. Cannot set DCSTask " .. DCSTask )
end
end
if not WaitTime or WaitTime == 0 then
SetTask( self, DCSTask )
else
self.TaskScheduler:Schedule( Controller, Controller.setTask, { DCSTask }, WaitTime )
self.TaskScheduler:Schedule( self, SetTask, { DCSTask }, WaitTime )
end
return self
@@ -449,11 +492,11 @@ function CONTROLLABLE:TaskWrappedAction( DCSCommand, Index )
self:F2( { DCSCommand } )
local DCSTaskWrappedAction
DCSTaskWrappedAction = {
id = "WrappedAction",
enabled = true,
number = Index,
number = Index or 1,
auto = false,
params = {
action = DCSCommand,
@@ -464,6 +507,22 @@ function CONTROLLABLE:TaskWrappedAction( DCSCommand, Index )
return DCSTaskWrappedAction
end
--- Set a Task at a Waypoint using a Route list.
-- @param #CONTROLLABLE self
-- @param #table Waypoint The Waypoint!
-- @param Dcs.DCSTasking.Task#Task Task The Task structure to be executed!
-- @return Dcs.DCSTasking.Task#Task
function CONTROLLABLE:SetTaskWaypoint( Waypoint, Task )
Waypoint.task = self:TaskCombo( { Task } )
self:T3( { Waypoint.task } )
return Waypoint.task
end
--- Executes a command action
-- @param #CONTROLLABLE self
-- @param Dcs.DCSCommand#Command DCSCommand
@@ -1480,6 +1539,84 @@ function CONTROLLABLE:TaskEmbarkToTransport( Point, Radius )
return DCSTask
end
--- This creates a Task element, with an action to call a function as part of a Wrapped Task.
-- This Task can then be embedded at a Waypoint by calling the method @{#CONTROLLABLE.SetTaskWaypoint}.
-- @param #CONTROLLABLE self
-- @param #string FunctionString The function name embedded as a string that will be called.
-- @param ... The variable arguments passed to the function when called! These arguments can be of any type!
-- @return #CONTROLLABLE
-- @usage
--
-- local ZoneList = {
-- ZONE:New( "ZONE1" ),
-- ZONE:New( "ZONE2" ),
-- ZONE:New( "ZONE3" ),
-- ZONE:New( "ZONE4" ),
-- ZONE:New( "ZONE5" )
-- }
--
-- GroundGroup = GROUP:FindByName( "Vehicle" )
--
-- --- @param Wrapper.Group#GROUP GroundGroup
-- function RouteToZone( Vehicle, ZoneRoute )
--
-- local Route = {}
--
-- Vehicle:E( { ZoneRoute = ZoneRoute } )
--
-- Vehicle:MessageToAll( "Moving to zone " .. ZoneRoute:GetName(), 10 )
--
-- -- Get the current coordinate of the Vehicle
-- local FromCoord = Vehicle:GetCoordinate()
--
-- -- Select a random Zone and get the Coordinate of the new Zone.
-- local RandomZone = ZoneList[ math.random( 1, #ZoneList ) ] -- Core.Zone#ZONE
-- local ToCoord = RandomZone:GetCoordinate()
--
-- -- Create a "ground route point", which is a "point" structure that can be given as a parameter to a Task
-- Route[#Route+1] = FromCoord:WaypointGround( 72 )
-- Route[#Route+1] = ToCoord:WaypointGround( 60, "Vee" )
--
-- local TaskRouteToZone = Vehicle:TaskFunction( "RouteToZone", RandomZone )
--
-- Vehicle:SetTaskWaypoint( Route, #Route, TaskRouteToZone ) -- Set for the given Route at Waypoint 2 the TaskRouteToZone.
--
-- Vehicle:Route( Route, math.random( 10, 20 ) ) -- Move after a random seconds to the Route. See the Route method for details.
--
-- end
--
-- RouteToZone( GroundGroup, ZoneList[1] )
--
function CONTROLLABLE:TaskFunction( FunctionString, ... )
self:F2( { FunctionString, arg } )
local DCSTask
local DCSScript = {}
DCSScript[#DCSScript+1] = "local MissionControllable = GROUP:Find( ... ) "
if arg and arg.n > 0 then
local ArgumentKey = '_' .. tostring( arg ):match("table: (.*)")
self:SetState( self, ArgumentKey, arg )
DCSScript[#DCSScript+1] = "local Arguments = MissionControllable:GetState( MissionControllable, '" .. ArgumentKey .. "' ) "
--DCSScript[#DCSScript+1] = "MissionControllable:ClearState( MissionControllable, '" .. ArgumentKey .. "' ) "
DCSScript[#DCSScript+1] = FunctionString .. "( MissionControllable, unpack( Arguments ) )"
else
DCSScript[#DCSScript+1] = FunctionString .. "( MissionControllable )"
end
DCSTask = self:TaskWrappedAction(
self:CommandDoScript(
table.concat( DCSScript )
)
)
self:T( DCSTask )
return DCSTask
end
--- (AIR + GROUND) Return a mission task from a mission template.
@@ -1496,6 +1633,140 @@ function CONTROLLABLE:TaskMission( TaskMission )
return DCSTask
end
do -- Patrol methods
--- (GROUND) Patrol iteratively using the waypoints the for the (parent) group.
-- @param #CONTROLLABLE self
-- @return #CONTROLLABLE
function CONTROLLABLE:PatrolRoute()
local PatrolGroup = self -- Wrapper.Group#GROUP
if not self:IsInstanceOf( "GROUP" ) then
PatrolGroup = self:GetGroup() -- Wrapper.Group#GROUP
end
self:E( { PatrolGroup = PatrolGroup:GetName() } )
if PatrolGroup:IsGround() or PatrolGroup:IsShip() then
local Waypoints = PatrolGroup:GetTemplateRoutePoints()
-- Calculate the new Route.
local FromCoord = PatrolGroup:GetCoordinate()
local From = FromCoord:WaypointGround( 120 )
table.insert( Waypoints, 1, From )
local TaskRoute = PatrolGroup:TaskFunction( "CONTROLLABLE.PatrolRoute" )
self:E({Waypoints = Waypoints})
local Waypoint = Waypoints[#Waypoints]
PatrolGroup:SetTaskWaypoint( Waypoint, TaskRoute ) -- Set for the given Route at Waypoint 2 the TaskRouteToZone.
PatrolGroup:Route( Waypoints ) -- Move after a random seconds to the Route. See the Route method for details.
end
end
--- (GROUND) Patrol randomly to the waypoints the for the (parent) group.
-- A random waypoint will be picked and the group will move towards that point.
-- @param #CONTROLLABLE self
-- @return #CONTROLLABLE
function CONTROLLABLE:PatrolRouteRandom( Speed, Formation, ToWaypoint )
local PatrolGroup = self -- Wrapper.Group#GROUP
if not self:IsInstanceOf( "GROUP" ) then
PatrolGroup = self:GetGroup() -- Wrapper.Group#GROUP
end
self:E( { PatrolGroup = PatrolGroup:GetName() } )
if PatrolGroup:IsGround() or PatrolGroup:IsShip() then
local Waypoints = PatrolGroup:GetTemplateRoutePoints()
-- Calculate the new Route.
local FromCoord = PatrolGroup:GetCoordinate()
local FromWaypoint = 1
if ToWaypoint then
FromWaypoint = ToWaypoint
end
-- Loop until a waypoint has been found that is not the same as the current waypoint.
-- Otherwise the object zon't move or drive in circles and the algorithm would not do exactly
-- what it is supposed to do, which is making groups drive around.
local ToWaypoint
repeat
-- Select a random waypoint and check if it is not the same waypoint as where the object is about.
ToWaypoint = math.random( 1, #Waypoints )
until( ToWaypoint ~= FromWaypoint )
self:E( { FromWaypoint = FromWaypoint, ToWaypoint = ToWaypoint } )
local Waypoint = Waypoints[ToWaypoint] -- Select random waypoint.
local ToCoord = COORDINATE:NewFromVec2( { x = Waypoint.x, y = Waypoint.y } )
-- Create a "ground route point", which is a "point" structure that can be given as a parameter to a Task
local Route = {}
Route[#Route+1] = FromCoord:WaypointGround( 0 )
Route[#Route+1] = ToCoord:WaypointGround( Speed, Formation )
local TaskRouteToZone = PatrolGroup:TaskFunction( "CONTROLLABLE.PatrolRouteRandom", Speed, Formation, ToWaypoint )
PatrolGroup:SetTaskWaypoint( Route[#Route], TaskRouteToZone ) -- Set for the given Route at Waypoint 2 the TaskRouteToZone.
PatrolGroup:Route( Route, 1 ) -- Move after a random seconds to the Route. See the Route method for details.
end
end
--- (GROUND) Patrol randomly to the waypoints the for the (parent) group.
-- A random waypoint will be picked and the group will move towards that point.
-- @param #CONTROLLABLE self
-- @return #CONTROLLABLE
function CONTROLLABLE:PatrolZones( ZoneList, Speed, Formation )
if not type( ZoneList ) == "table" then
ZoneList = { ZoneList }
end
local PatrolGroup = self -- Wrapper.Group#GROUP
if not self:IsInstanceOf( "GROUP" ) then
PatrolGroup = self:GetGroup() -- Wrapper.Group#GROUP
end
self:E( { PatrolGroup = PatrolGroup:GetName() } )
if PatrolGroup:IsGround() or PatrolGroup:IsShip() then
local Waypoints = PatrolGroup:GetTemplateRoutePoints()
local Waypoint = Waypoints[math.random( 1, #Waypoints )] -- Select random waypoint.
-- Calculate the new Route.
local FromCoord = PatrolGroup:GetCoordinate()
-- Select a random Zone and get the Coordinate of the new Zone.
local RandomZone = ZoneList[ math.random( 1, #ZoneList ) ] -- Core.Zone#ZONE
local ToCoord = RandomZone:GetRandomCoordinate( 10 )
-- Create a "ground route point", which is a "point" structure that can be given as a parameter to a Task
local Route = {}
Route[#Route+1] = FromCoord:WaypointGround( 120 )
Route[#Route+1] = ToCoord:WaypointGround( Speed, Formation )
local TaskRouteToZone = PatrolGroup:TaskFunction( "CONTROLLABLE.PatrolZones", ZoneList, Speed, Formation )
PatrolGroup:SetTaskWaypoint( Route[#Route], TaskRouteToZone ) -- Set for the given Route at Waypoint 2 the TaskRouteToZone.
PatrolGroup:Route( Route, 1 ) -- Move after a random seconds to the Route. See the Route method for details.
end
end
end
--- Return a Misson task to follow a given route defined by Points.
-- @param #CONTROLLABLE self
-- @param #table Points A table of route points.
@@ -1620,19 +1891,16 @@ end
--- Make the controllable to follow a given route.
-- @param #CONTROLLABLE self
-- @param #table GoPoints A table of Route Points.
-- @return #CONTROLLABLE self
function CONTROLLABLE:Route( GoPoints )
self:F2( GoPoints )
-- @param #table Route A table of Route Points.
-- @param #number DelaySeconds Wait for the specified seconds before executing the Route.
-- @return #CONTROLLABLE The CONTROLLABLE.
function CONTROLLABLE:Route( Route, DelaySeconds )
self:F2( Route )
local DCSControllable = self:GetDCSObject()
if DCSControllable then
local Points = routines.utils.deepCopy( GoPoints )
local MissionTask = { id = 'Mission', params = { route = { points = Points, }, }, }
local Controller = self:_GetController()
--Controller.setTask( Controller, MissionTask )
self.TaskScheduler:Schedule( Controller, Controller.setTask, { MissionTask }, 1 )
local RouteTask = self:TaskRoute( Route ) -- Create a RouteTask, that will route the CONTROLLABLE to the Route.
self:SetTask( RouteTask, DelaySeconds or 1 ) -- Execute the RouteTask after the specified seconds (default is 1).
return self
end
@@ -1640,6 +1908,47 @@ function CONTROLLABLE:Route( GoPoints )
end
--- Make the GROUND Controllable to drive towards a specific point.
-- @param #CONTROLLABLE self
-- @param Core.Point#COORDINATE ToCoordinate A Coordinate to drive to.
-- @param #number Speed (optional) Speed in km/h. The default speed is 999 km/h.
-- @param #string Formation (optional) The route point Formation, which is a text string that specifies exactly the Text in the Type of the route point, like "Vee", "Echelon Right".
-- @param #number DelaySeconds Wait for the specified seconds before executing the Route.
-- @return #CONTROLLABLE The CONTROLLABLE.
function CONTROLLABLE:RouteGroundTo( ToCoordinate, Speed, Formation, DelaySeconds )
local FromCoordinate = self:GetCoordinate()
local FromWP = FromCoordinate:WaypointGround()
local ToWP = ToCoordinate:WaypointGround( Speed, Formation )
self:Route( { FromWP, ToWP }, DelaySeconds )
return self
end
--- Make the AIR Controllable fly towards a specific point.
-- @param #CONTROLLABLE self
-- @param Core.Point#COORDINATE ToCoordinate A Coordinate to drive to.
-- @param Core.Point#COORDINATE.RoutePointAltType AltType The altitude type.
-- @param Core.Point#COORDINATE.RoutePointType Type The route point type.
-- @param Core.Point#COORDINATE.RoutePointAction Action The route point action.
-- @param #number Speed (optional) Speed in km/h. The default speed is 999 km/h.
-- @param #number DelaySeconds Wait for the specified seconds before executing the Route.
-- @return #CONTROLLABLE The CONTROLLABLE.
function CONTROLLABLE:RouteAirTo( ToCoordinate, AltType, Type, Action, Speed, DelaySeconds )
local FromCoordinate = self:GetCoordinate()
local FromWP = FromCoordinate:WaypointAir()
local ToWP = ToCoordinate:WaypointAir( AltType, Type, Action, Speed )
self:Route( { FromWP, ToWP }, DelaySeconds )
return self
end
--- (AIR + GROUND) Route the controllable to a given zone.
-- The controllable final destination point can be randomized.
@@ -2228,6 +2537,72 @@ function CONTROLLABLE:OptionROTVertical()
return nil
end
--- Alarm state to Auto: AI will automatically switch alarm states based on the presence of threats. The AI kind of cheats in this regard.
-- @param #CONTROLLABLE self
-- @return #CONTROLLABLE self
function CONTROLLABLE:OptionAlarmStateAuto()
self:F2( { self.ControllableName } )
local DCSControllable = self:GetDCSObject()
if DCSControllable then
local Controller = self:_GetController()
if self:IsGround() then
Controller:setOption(AI.Option.Ground.id.ALARM_STATE, AI.Option.Ground.val.ALARM_STATE.AUTO)
elseif self:IsShip() then
Controller:setOption(AI.Option.Naval.id.ALARM_STATE, AI.Option.Naval.val.ALARM_STATE.AUTO)
end
return self
end
return nil
end
--- Alarm state to Green: Group is not combat ready. Sensors are stowed if possible.
-- @param #CONTROLLABLE self
-- @return #CONTROLLABLE self
function CONTROLLABLE:OptionAlarmStateGreen()
self:F2( { self.ControllableName } )
local DCSControllable = self:GetDCSObject()
if DCSControllable then
local Controller = self:_GetController()
if self:IsGround() then
Controller:setOption(AI.Option.Ground.id.ALARM_STATE, AI.Option.Ground.val.ALARM_STATE.GREEN)
elseif self:IsShip() then
Controller:setOption(AI.Option.Naval.id.ALARM_STATE, AI.Option.Naval.val.ALARM_STATE.GREEN)
end
return self
end
return nil
end
--- Alarm state to Red: Group is combat ready and actively searching for targets.
-- @param #CONTROLLABLE self
-- @return #CONTROLLABLE self
function CONTROLLABLE:OptionAlarmStateRed()
self:F2( { self.ControllableName } )
local DCSControllable = self:GetDCSObject()
if DCSControllable then
local Controller = self:_GetController()
if self:IsGround() then
Controller:setOption(AI.Option.Ground.id.ALARM_STATE, AI.Option.Ground.val.ALARM_STATE.RED)
elseif self:IsShip() then
Controller:setOption(AI.Option.Naval.id.ALARM_STATE, AI.Option.Naval.val.ALARM_STATE.RED)
end
return self
end
return nil
end
--- Set RTB on bingo fuel.
-- @param #CONTROLLABLE self
@@ -2321,37 +2696,11 @@ function CONTROLLABLE:WayPointFunction( WayPoint, WayPointIndex, WayPointFunctio
self:F2( { WayPoint, WayPointIndex, WayPointFunction } )
table.insert( self.WayPoints[WayPoint].task.params.tasks, WayPointIndex )
self.WayPoints[WayPoint].task.params.tasks[WayPointIndex] = self:TaskFunction( WayPoint, WayPointIndex, WayPointFunction, arg )
self.WayPoints[WayPoint].task.params.tasks[WayPointIndex] = self:TaskFunction( WayPointFunction, arg )
return self
end
function CONTROLLABLE:TaskFunction( WayPoint, WayPointIndex, FunctionString, FunctionArguments )
self:F2( { WayPoint, WayPointIndex, FunctionString, FunctionArguments } )
local DCSTask
local DCSScript = {}
DCSScript[#DCSScript+1] = "local MissionControllable = GROUP:Find( ... ) "
if FunctionArguments and #FunctionArguments > 0 then
DCSScript[#DCSScript+1] = FunctionString .. "( MissionControllable, " .. table.concat( FunctionArguments, "," ) .. ")"
else
DCSScript[#DCSScript+1] = FunctionString .. "( MissionControllable )"
end
DCSTask = self:TaskWrappedAction(
self:CommandDoScript(
table.concat( DCSScript )
), WayPointIndex
)
self:T( DCSTask )
return DCSTask
end
--- Executes the WayPoint plan.
-- The function gets a WayPoint parameter, that you can use to restart the mission at a specific WayPoint.
-- Note that when the WayPoint parameter is used, the new start mission waypoint of the controllable will be 1!
@@ -2388,12 +2737,22 @@ function CONTROLLABLE:IsAirPlane()
if DCSObject then
local Category = DCSObject:getDesc().category
self:T( Category )
return Category == Unit.Category.AIRPLANE
end
return nil
end
function CONTROLLABLE:GetSize()
local DCSObject = self:GetDCSObject()
if DCSObject then
return 1
else
return 0
end
end
-- Message APIs

View File

@@ -104,15 +104,39 @@ GROUP.Takeoff = {
GROUPTEMPLATE = {}
GROUPTEMPLATE.Takeoff = {
[GROUP.Takeoff.Air] = "Turning Point",
[GROUP.Takeoff.Runway] = "TakeOff",
[GROUP.Takeoff.Hot] = "TakeOffParkingHot",
[GROUP.Takeoff.Cold] = "TakeOffParking",
[GROUP.Takeoff.Air] = { "Turning Point", "Turning Point" },
[GROUP.Takeoff.Runway] = { "TakeOff", "From Runway" },
[GROUP.Takeoff.Hot] = { "TakeOffParkingHot", "From Parking Area Hot" },
[GROUP.Takeoff.Cold] = { "TakeOffParking", "From Parking Area" }
}
--- Create a new GROUP from a DCSGroup
--- Create a new GROUP from a given GroupTemplate as a parameter.
-- Note that the GroupTemplate is NOT spawned into the mission.
-- It is merely added to the @{Database}.
-- @param #GROUP self
-- @param Dcs.DCSWrapper.Group#Group GroupName The DCS Group name
-- @param #table GroupTemplate The GroupTemplate Structure exactly as defined within the mission editor.
-- @param Dcs.DCScoalition#coalition.side CoalitionSide The coalition.side of the group.
-- @param Dcs.DCSGroup#Group.Category CategoryID The Group.Category of the group.
-- @param Dcs.DCScountry#country.id CountryID the country.id of the group.
-- @return #GROUP self
function GROUP:NewTemplate( GroupTemplate, CoalitionSide, CategoryID, CountryID )
local GroupName = GroupTemplate.name
_DATABASE:_RegisterGroupTemplate( GroupTemplate, CategoryID, CountryID, CoalitionSide, GroupName )
self = BASE:Inherit( self, CONTROLLABLE:New( GroupName ) )
self:F2( GroupName )
self.GroupName = GroupName
_DATABASE:AddGroup( GroupName )
self:SetEventPriority( 4 )
return self
end
--- Create a new GROUP from an existing Group in the Mission.
-- @param #GROUP self
-- @param #string GroupName The Group name
-- @return #GROUP self
function GROUP:Register( GroupName )
self = BASE:Inherit( self, CONTROLLABLE:New( GroupName ) )
@@ -224,6 +248,7 @@ function GROUP:Destroy()
for Index, UnitData in pairs( DCSGroup:getUnits() ) do
self:CreateEventCrash( timer.getTime(), UnitData )
end
USERFLAG:New( self:GetName() ):Set( 100 )
DCSGroup:destroy()
DCSGroup = nil
end
@@ -231,6 +256,7 @@ function GROUP:Destroy()
return nil
end
--- Returns category of the DCS Group.
-- @param #GROUP self
-- @return Dcs.DCSWrapper.Group#Group.Category The category ID
@@ -353,8 +379,13 @@ function GROUP:GetSize()
if DCSGroup then
local GroupSize = DCSGroup:getSize()
self:T3( GroupSize )
return GroupSize
if GroupSize then
self:T3( GroupSize )
return GroupSize
else
return 0
end
end
return nil
@@ -502,7 +533,6 @@ end
--- Returns a COORDINATE object indicating the point of the first UNIT of the GROUP within the mission.
-- @param Wrapper.Group#GROUP self
-- @return Core.Point#COORDINATE The COORDINATE of the GROUP.
-- @return #nil The POSITIONABLE is not existing or alive.
function GROUP:GetCoordinate()
self:F2( self.PositionableName )
@@ -560,6 +590,32 @@ function GROUP:GetHeading()
end
--- Returns relative amount of fuel (from 0.0 to 1.0) the group has in its internal tanks. If there are additional fuel tanks the value may be greater than 1.0.
-- @param #GROUP self
-- @return #number The relative amount of fuel (from 0.0 to 1.0).
-- @return #nil The GROUP is not existing or alive.
function GROUP:GetFuel()
self:F( self.ControllableName )
local DCSControllable = self:GetDCSObject()
if DCSControllable then
local GroupSize = self:GetSize()
local TotalFuel = 0
for UnitID, UnitData in pairs( self:GetUnits() ) do
local Unit = UnitData -- Wrapper.Unit#UNIT
local UnitFuel = Unit:GetFuel()
self:F( { Fuel = UnitFuel } )
TotalFuel = TotalFuel + UnitFuel
end
local GroupFuel = TotalFuel / GroupSize
return GroupFuel
end
return 0
end
do -- Is Zone methods
--- Returns true if all units of the group are within a @{Zone}.
@@ -569,6 +625,8 @@ do -- Is Zone methods
function GROUP:IsCompletelyInZone( Zone )
self:F2( { self.GroupName, Zone } )
if not self:IsAlive() then return false end
for UnitID, UnitData in pairs( self:GetUnits() ) do
local Unit = UnitData -- Wrapper.Unit#UNIT
if Zone:IsVec3InZone( Unit:GetVec3() ) then
@@ -590,6 +648,8 @@ function GROUP:IsPartlyInZone( Zone )
local IsOneUnitInZone = false
local IsOneUnitOutsideZone = false
if not self:IsAlive() then return false end
for UnitID, UnitData in pairs( self:GetUnits() ) do
local Unit = UnitData -- Wrapper.Unit#UNIT
if Zone:IsVec3InZone( Unit:GetVec3() ) then
@@ -613,6 +673,8 @@ end
function GROUP:IsNotInZone( Zone )
self:F2( { self.GroupName, Zone } )
if not self:IsAlive() then return true end
for UnitID, UnitData in pairs( self:GetUnits() ) do
local Unit = UnitData -- Wrapper.Unit#UNIT
if Zone:IsVec3InZone( Unit:GetVec3() ) then
@@ -631,6 +693,8 @@ function GROUP:CountInZone( Zone )
self:F2( {self.GroupName, Zone} )
local Count = 0
if not self:IsAlive() then return Count end
for UnitID, UnitData in pairs( self:GetUnits() ) do
local Unit = UnitData -- Wrapper.Unit#UNIT
if Zone:IsVec3InZone( Unit:GetVec3() ) then
@@ -864,25 +928,28 @@ end
-- @param #table Template The template of the Group retrieved with GROUP:GetTemplate()
function GROUP:Respawn( Template )
local Vec3 = self:GetVec3()
Template.x = Vec3.x
Template.y = Vec3.z
--Template.x = nil
--Template.y = nil
self:E( #Template.units )
for UnitID, UnitData in pairs( self:GetUnits() ) do
local GroupUnit = UnitData -- Wrapper.Unit#UNIT
self:E( GroupUnit:GetName() )
if GroupUnit:IsAlive() then
local GroupUnitVec3 = GroupUnit:GetVec3()
local GroupUnitHeading = GroupUnit:GetHeading()
Template.units[UnitID].alt = GroupUnitVec3.y
Template.units[UnitID].x = GroupUnitVec3.x
Template.units[UnitID].y = GroupUnitVec3.z
Template.units[UnitID].heading = GroupUnitHeading
self:E( { UnitID, Template.units[UnitID], Template.units[UnitID] } )
if self:IsAlive() then
local Vec3 = self:GetVec3()
Template.x = Vec3.x
Template.y = Vec3.z
--Template.x = nil
--Template.y = nil
self:E( #Template.units )
for UnitID, UnitData in pairs( self:GetUnits() ) do
local GroupUnit = UnitData -- Wrapper.Unit#UNIT
self:E( GroupUnit:GetName() )
if GroupUnit:IsAlive() then
local GroupUnitVec3 = GroupUnit:GetVec3()
local GroupUnitHeading = GroupUnit:GetHeading()
Template.units[UnitID].alt = GroupUnitVec3.y
Template.units[UnitID].x = GroupUnitVec3.x
Template.units[UnitID].y = GroupUnitVec3.z
Template.units[UnitID].heading = GroupUnitHeading
self:E( { UnitID, Template.units[UnitID], Template.units[UnitID] } )
end
end
end
self:Destroy()
@@ -897,10 +964,19 @@ end
-- @return #table
function GROUP:GetTemplate()
local GroupName = self:GetName()
self:E( GroupName )
return _DATABASE:GetGroupTemplate( GroupName )
return UTILS.DeepCopy( _DATABASE:GetGroupTemplate( GroupName ) )
end
--- Returns the group template route.points[] (the waypoints) from the @{DATABASE} (_DATABASE object).
-- @param #GROUP self
-- @return #table
function GROUP:GetTemplateRoutePoints()
local GroupName = self:GetName()
return UTILS.DeepCopy( _DATABASE:GetGroupTemplate( GroupName ).route.points )
end
--- Sets the controlled status in a Template.
-- @param #GROUP self
-- @param #boolean Controlled true is controlled, false is uncontrolled.
@@ -1075,7 +1151,7 @@ do -- Route methods
local PointTo = {}
local AirbasePointVec2 = RTBAirbase:GetPointVec2()
local AirbaseAirPoint = AirbasePointVec2:RoutePointAir(
local AirbaseAirPoint = AirbasePointVec2:WaypointAir(
POINT_VEC3.RoutePointAltType.BARO,
"Land",
"Landing",
@@ -1120,9 +1196,9 @@ do -- Event Handling
-- @param Core.Event#EVENTS Event
-- @param #function EventFunction (optional) The function to be called when the event occurs for the GROUP.
-- @return #GROUP
function GROUP:HandleEvent( Event, EventFunction )
function GROUP:HandleEvent( Event, EventFunction, ... )
self:EventDispatcher():OnEventForGroup( self:GetName(), EventFunction, self, Event )
self:EventDispatcher():OnEventForGroup( self:GetName(), EventFunction, self, Event, ... )
return self
end
@@ -1133,7 +1209,7 @@ do -- Event Handling
-- @return #GROUP
function GROUP:UnHandleEvent( Event )
self:EventDispatcher():Remove( self, Event )
self:EventDispatcher():RemoveEvent( self, Event )
return self
end
@@ -1162,7 +1238,7 @@ do -- Players
-- @return #nil The group has no players
function GROUP:GetPlayerNames()
local PlayerNames = nil
local PlayerNames = {}
local Units = self:GetUnits()
for UnitID, UnitData in pairs( Units ) do
@@ -1178,4 +1254,96 @@ do -- Players
return PlayerNames
end
end
end
--do -- Smoke
--
----- Signal a flare at the position of the GROUP.
---- @param #GROUP self
---- @param Utilities.Utils#FLARECOLOR FlareColor
--function GROUP:Flare( FlareColor )
-- self:F2()
-- trigger.action.signalFlare( self:GetVec3(), FlareColor , 0 )
--end
--
----- Signal a white flare at the position of the GROUP.
---- @param #GROUP self
--function GROUP:FlareWhite()
-- self:F2()
-- trigger.action.signalFlare( self:GetVec3(), trigger.flareColor.White , 0 )
--end
--
----- Signal a yellow flare at the position of the GROUP.
---- @param #GROUP self
--function GROUP:FlareYellow()
-- self:F2()
-- trigger.action.signalFlare( self:GetVec3(), trigger.flareColor.Yellow , 0 )
--end
--
----- Signal a green flare at the position of the GROUP.
---- @param #GROUP self
--function GROUP:FlareGreen()
-- self:F2()
-- trigger.action.signalFlare( self:GetVec3(), trigger.flareColor.Green , 0 )
--end
--
----- Signal a red flare at the position of the GROUP.
---- @param #GROUP self
--function GROUP:FlareRed()
-- self:F2()
-- local Vec3 = self:GetVec3()
-- if Vec3 then
-- trigger.action.signalFlare( Vec3, trigger.flareColor.Red, 0 )
-- end
--end
--
----- Smoke the GROUP.
---- @param #GROUP self
--function GROUP:Smoke( SmokeColor, Range )
-- self:F2()
-- if Range then
-- trigger.action.smoke( self:GetRandomVec3( Range ), SmokeColor )
-- else
-- trigger.action.smoke( self:GetVec3(), SmokeColor )
-- end
--
--end
--
----- Smoke the GROUP Green.
---- @param #GROUP self
--function GROUP:SmokeGreen()
-- self:F2()
-- trigger.action.smoke( self:GetVec3(), trigger.smokeColor.Green )
--end
--
----- Smoke the GROUP Red.
---- @param #GROUP self
--function GROUP:SmokeRed()
-- self:F2()
-- trigger.action.smoke( self:GetVec3(), trigger.smokeColor.Red )
--end
--
----- Smoke the GROUP White.
---- @param #GROUP self
--function GROUP:SmokeWhite()
-- self:F2()
-- trigger.action.smoke( self:GetVec3(), trigger.smokeColor.White )
--end
--
----- Smoke the GROUP Orange.
---- @param #GROUP self
--function GROUP:SmokeOrange()
-- self:F2()
-- trigger.action.smoke( self:GetVec3(), trigger.smokeColor.Orange )
--end
--
----- Smoke the GROUP Blue.
---- @param #GROUP self
--function GROUP:SmokeBlue()
-- self:F2()
-- trigger.action.smoke( self:GetVec3(), trigger.smokeColor.Blue )
--end
--
--
--
--end

View File

@@ -83,15 +83,8 @@ end
function IDENTIFIABLE:GetName()
self:F2( self.IdentifiableName )
local DCSIdentifiable = self:GetDCSObject()
if DCSIdentifiable then
local IdentifiableName = self.IdentifiableName
return IdentifiableName
end
self:E( self.ClassName .. " " .. self.IdentifiableName .. " not found!" )
return nil
local IdentifiableName = self.IdentifiableName
return IdentifiableName
end
@@ -166,6 +159,36 @@ function IDENTIFIABLE:GetCoalition()
return nil
end
--- Returns the name of the coalition of the Identifiable.
-- @param #IDENTIFIABLE self
-- @return #string The name of the coalition.
-- @return #nil The DCS Identifiable is not existing or alive.
function IDENTIFIABLE:GetCoalitionName()
self:F2( self.IdentifiableName )
local DCSIdentifiable = self:GetDCSObject()
if DCSIdentifiable then
local IdentifiableCoalition = DCSIdentifiable:getCoalition()
self:T3( IdentifiableCoalition )
if IdentifiableCoalition == coalition.side.BLUE then
return "Blue"
end
if IdentifiableCoalition == coalition.side.RED then
return "Red"
end
if IdentifiableCoalition == coalition.side.NEUTRAL then
return "Neutral"
end
end
self:E( self.ClassName .. " " .. self.IdentifiableName .. " not found!" )
return nil
end
--- Returns country of the Identifiable.
-- @param #IDENTIFIABLE self
-- @return Dcs.DCScountry#country.id The country identifier.

View File

@@ -79,7 +79,7 @@ function OBJECT:Destroy()
local DCSObject = self:GetDCSObject()
if DCSObject then
--BASE:CreateEventCrash( timer.getTime(), DCSObject )
DCSObject:destroy()
end

View File

@@ -10,13 +10,11 @@
--
-- @module Positionable
--- The POSITIONABLE class
-- @type POSITIONABLE
--- @type POSITIONABLE.__ Methods which are not intended for mission designers, but which are used interally by the moose designer :-)
-- @extends Wrapper.Identifiable#IDENTIFIABLE
--- @type POSITIONABLE
-- @extends Wrapper.Identifiable#IDENTIFIABLE
-- @field #string PositionableName The name of the measurable.
-- @field Core.Spot#SPOT Spot The laser Spot.
-- @field #number LaserCode The last assigned laser code.
--- # POSITIONABLE class, extends @{Identifiable#IDENTIFIABLE}
@@ -49,6 +47,14 @@ POSITIONABLE = {
ClassName = "POSITIONABLE",
PositionableName = "",
}
--- @field #POSITIONABLE.__
POSITIONABLE.__ = {}
--- @field #POSITIONABLE.__.Cargo
POSITIONABLE.__.Cargo = {}
--- A DCSPositionable
-- @type DCSPositionable
-- @field id_ The ID of the controllable in DCS
@@ -150,7 +156,6 @@ end
--- Returns a COORDINATE object indicating the point in 3D of the POSITIONABLE within the mission.
-- @param Wrapper.Positionable#POSITIONABLE self
-- @return Core.Point#COORDINATE The COORDINATE of the POSITIONABLE.
-- @return #nil The POSITIONABLE is not existing or alive.
function POSITIONABLE:GetCoordinate()
self:F2( self.PositionableName )
@@ -159,8 +164,9 @@ function POSITIONABLE:GetCoordinate()
if DCSPositionable then
local PositionableVec3 = self:GetPositionVec3()
local PositionableCoordinate = POINT_VEC3:NewFromVec3( PositionableVec3 )
local PositionableCoordinate = COORDINATE:NewFromVec3( PositionableVec3 )
PositionableCoordinate:SetHeading( self:GetHeading() )
PositionableCoordinate:SetVelocity( self:GetVelocityMPS() )
self:T2( PositionableCoordinate )
return PositionableCoordinate
@@ -320,16 +326,35 @@ function POSITIONABLE:InAir()
return nil
end
--- Returns the POSITIONABLE velocity vector.
--- Returns the a @{Velocity} object from the positionable.
-- @param Wrapper.Positionable#POSITIONABLE self
-- @return Dcs.DCSTypes#Vec3 The velocity vector
-- @return Core.Velocity#VELOCITY Velocity The Velocity object.
-- @return #nil The POSITIONABLE is not existing or alive.
function POSITIONABLE:GetVelocity()
self:F2( self.PositionableName )
local DCSPositionable = self:GetDCSObject()
if DCSPositionable then
local Velocity = VELOCITY:New( self )
return Velocity
end
return nil
end
--- Returns the POSITIONABLE velocity Vec3 vector.
-- @param Wrapper.Positionable#POSITIONABLE self
-- @return Dcs.DCSTypes#Vec3 The velocity Vec3 vector
-- @return #nil The POSITIONABLE is not existing or alive.
function POSITIONABLE:GetVelocityVec3()
self:F2( self.PositionableName )
local DCSPositionable = self:GetDCSObject()
if DCSPositionable then
local PositionableVelocityVec3 = DCSPositionable:getVelocity()
self:T3( PositionableVelocityVec3 )
@@ -365,40 +390,38 @@ end
--- Returns the POSITIONABLE velocity in km/h.
-- @param Wrapper.Positionable#POSITIONABLE self
-- @return #number The velocity in km/h
-- @return #nil The POSITIONABLE is not existing or alive.
function POSITIONABLE:GetVelocityKMH()
self:F2( self.PositionableName )
local DCSPositionable = self:GetDCSObject()
if DCSPositionable then
local VelocityVec3 = self:GetVelocity()
local VelocityVec3 = self:GetVelocityVec3()
local Velocity = ( VelocityVec3.x ^ 2 + VelocityVec3.y ^ 2 + VelocityVec3.z ^ 2 ) ^ 0.5 -- in meters / sec
local Velocity = Velocity * 3.6 -- now it is in km/h.
self:T3( Velocity )
return Velocity
end
return nil
return 0
end
--- Returns the POSITIONABLE velocity in meters per second.
-- @param Wrapper.Positionable#POSITIONABLE self
-- @return #number The velocity in meters per second.
-- @return #nil The POSITIONABLE is not existing or alive.
function POSITIONABLE:GetVelocityMPS()
self:F2( self.PositionableName )
local DCSPositionable = self:GetDCSObject()
if DCSPositionable then
local VelocityVec3 = self:GetVelocity()
local VelocityVec3 = self:GetVelocityVec3()
local Velocity = ( VelocityVec3.x ^ 2 + VelocityVec3.y ^ 2 + VelocityVec3.z ^ 2 ) ^ 0.5 -- in meters / sec
self:T3( Velocity )
return Velocity
end
return nil
return 0
end
@@ -412,8 +435,8 @@ function POSITIONABLE:GetMessageText( Message, Name ) --R2.1 added
local DCSObject = self:GetDCSObject()
if DCSObject then
Name = Name and ( " (" .. Name .. ")" ) or ""
local Callsign = string.format( "[%s]", self:GetCallsign() ~= "" and self:GetCallsign() or self:GetName() )
local MessageText = Callsign .. Name .. ": " .. Message
local Callsign = string.format( "%s", self:GetCallsign() ~= "" and self:GetCallsign() or self:GetName() )
local MessageText = string.format("[%s%s]: %s", Callsign, Name, Message )
return MessageText
end
@@ -438,6 +461,23 @@ function POSITIONABLE:GetMessage( Message, Duration, Name ) --R2.1 changed calls
return nil
end
--- Returns a message of a specified type with the callsign embedded (if there is one).
-- @param #POSITIONABLE self
-- @param #string Message The message text
-- @param Core.Message#MESSAGE MessageType MessageType The message type.
-- @param #string Name (optional) The Name of the sender. If not provided, the Name is the type of the Positionable.
-- @return Core.Message#MESSAGE
function POSITIONABLE:GetMessageType( Message, MessageType, Name ) -- R2.2 changed callsign and name and using GetMessageText
local DCSObject = self:GetDCSObject()
if DCSObject then
local MessageText = self:GetMessageText( Message, Name )
return MESSAGE:NewType( MessageText, MessageType )
end
return nil
end
--- Send a message to all coalitions.
-- The message will appear in the message area. The message will begin with the callsign of the group and the type of the first unit sending the message.
-- @param #POSITIONABLE self
@@ -468,12 +508,6 @@ function POSITIONABLE:MessageToCoalition( Message, Duration, MessageCoalition )
local DCSObject = self:GetDCSObject()
if DCSObject then
if MessageCoalition == coalition.side.BLUE then
Name = "Blue coalition"
end
if MessageCoalition == coalition.side.RED then
Name = "Red coalition"
end
self:GetMessage( Message, Duration, Name ):ToCoalition( MessageCoalition )
end
@@ -481,6 +515,26 @@ function POSITIONABLE:MessageToCoalition( Message, Duration, MessageCoalition )
end
--- Send a message to a coalition.
-- The message will appear in the message area. The message will begin with the callsign of the group and the type of the first unit sending the message.
-- @param #POSITIONABLE self
-- @param #string Message The message text
-- @param Core.Message#MESSAGE.Type MessageType The message type that determines the duration.
-- @param Dcs.DCScoalition#coalition MessageCoalition The Coalition receiving the message.
function POSITIONABLE:MessageTypeToCoalition( Message, MessageType, MessageCoalition )
self:F2( { Message, MessageType } )
local Name = ""
local DCSObject = self:GetDCSObject()
if DCSObject then
self:GetMessageType( Message, MessageType, Name ):ToCoalition( MessageCoalition )
end
return nil
end
--- Send a message to the red coalition.
-- The message will appear in the message area. The message will begin with the callsign of the group and the type of the first unit sending the message.
-- @param #POSITIONABLE self
@@ -553,6 +607,26 @@ function POSITIONABLE:MessageToGroup( Message, Duration, MessageGroup, Name )
return nil
end
--- Send a message of a message type to a @{Group}.
-- The message will appear in the message area. The message will begin with the callsign of the group and the type of the first unit sending the message.
-- @param #POSITIONABLE self
-- @param #string Message The message text
-- @param Core.Message#MESSAGE.Type MessageType The message type that determines the duration.
-- @param Wrapper.Group#GROUP MessageGroup The GROUP object receiving the message.
-- @param #string Name (optional) The Name of the sender. If not provided, the Name is the type of the Positionable.
function POSITIONABLE:MessageTypeToGroup( Message, MessageType, MessageGroup, Name )
self:F2( { Message, MessageType } )
local DCSObject = self:GetDCSObject()
if DCSObject then
if DCSObject:isExist() then
self:GetMessageType( Message, MessageType, Name ):ToGroup( MessageGroup )
end
end
return nil
end
--- Send a message to a @{Set#SET_GROUP}.
-- The message will appear in the message area. The message will begin with the callsign of the group and the type of the first unit sending the message.
-- @param #POSITIONABLE self
@@ -679,5 +753,139 @@ function POSITIONABLE:GetLaserCode() --R2.1
return self.LaserCode
end
--- Add cargo.
-- @param #POSITIONABLE self
-- @param Core.Cargo#CARGO Cargo
-- @return #POSITIONABLE
function POSITIONABLE:AddCargo( Cargo )
self.__.Cargo[Cargo] = Cargo
return self
end
--- Remove cargo.
-- @param #POSITIONABLE self
-- @param Core.Cargo#CARGO Cargo
-- @return #POSITIONABLE
function POSITIONABLE:RemoveCargo( Cargo )
self.__.Cargo[Cargo] = nil
return self
end
--- Returns if carrier has given cargo.
-- @param #POSITIONABLE self
-- @return Core.Cargo#CARGO Cargo
function POSITIONABLE:HasCargo( Cargo )
return self.__.Cargo[Cargo]
end
--- Clear all cargo.
-- @param #POSITIONABLE self
function POSITIONABLE:ClearCargo()
self.__.Cargo = {}
end
--- Get cargo item count.
-- @param #POSITIONABLE self
-- @return Core.Cargo#CARGO Cargo
function POSITIONABLE:CargoItemCount()
local ItemCount = 0
for CargoName, Cargo in pairs( self.__.Cargo ) do
ItemCount = ItemCount + Cargo:GetCount()
end
return ItemCount
end
--- Signal a flare at the position of the POSITIONABLE.
-- @param #POSITIONABLE self
-- @param Utilities.Utils#FLARECOLOR FlareColor
function POSITIONABLE:Flare( FlareColor )
self:F2()
trigger.action.signalFlare( self:GetVec3(), FlareColor , 0 )
end
--- Signal a white flare at the position of the POSITIONABLE.
-- @param #POSITIONABLE self
function POSITIONABLE:FlareWhite()
self:F2()
trigger.action.signalFlare( self:GetVec3(), trigger.flareColor.White , 0 )
end
--- Signal a yellow flare at the position of the POSITIONABLE.
-- @param #POSITIONABLE self
function POSITIONABLE:FlareYellow()
self:F2()
trigger.action.signalFlare( self:GetVec3(), trigger.flareColor.Yellow , 0 )
end
--- Signal a green flare at the position of the POSITIONABLE.
-- @param #POSITIONABLE self
function POSITIONABLE:FlareGreen()
self:F2()
trigger.action.signalFlare( self:GetVec3(), trigger.flareColor.Green , 0 )
end
--- Signal a red flare at the position of the POSITIONABLE.
-- @param #POSITIONABLE self
function POSITIONABLE:FlareRed()
self:F2()
local Vec3 = self:GetVec3()
if Vec3 then
trigger.action.signalFlare( Vec3, trigger.flareColor.Red, 0 )
end
end
--- Smoke the POSITIONABLE.
-- @param #POSITIONABLE self
-- @param Utilities.Utils#SMOKECOLOR SmokeColor The color to smoke to positionable.
-- @param #number Range The range in meters to randomize the smoking around the positionable.
-- @param #number AddHeight The height in meters to add to the altitude of the positionable.
function POSITIONABLE:Smoke( SmokeColor, Range, AddHeight )
self:F2()
if Range then
local Vec3 = self:GetRandomVec3( Range )
Vec3.y = Vec3.y + AddHeight or 0
trigger.action.smoke( Vec3, SmokeColor )
else
local Vec3 = self:GetVec3()
Vec3.y = Vec3.y + AddHeight or 0
trigger.action.smoke( self:GetVec3(), SmokeColor )
end
end
--- Smoke the POSITIONABLE Green.
-- @param #POSITIONABLE self
function POSITIONABLE:SmokeGreen()
self:F2()
trigger.action.smoke( self:GetVec3(), trigger.smokeColor.Green )
end
--- Smoke the POSITIONABLE Red.
-- @param #POSITIONABLE self
function POSITIONABLE:SmokeRed()
self:F2()
trigger.action.smoke( self:GetVec3(), trigger.smokeColor.Red )
end
--- Smoke the POSITIONABLE White.
-- @param #POSITIONABLE self
function POSITIONABLE:SmokeWhite()
self:F2()
trigger.action.smoke( self:GetVec3(), trigger.smokeColor.White )
end
--- Smoke the POSITIONABLE Orange.
-- @param #POSITIONABLE self
function POSITIONABLE:SmokeOrange()
self:F2()
trigger.action.smoke( self:GetVec3(), trigger.smokeColor.Orange )
end
--- Smoke the POSITIONABLE Blue.
-- @param #POSITIONABLE self
function POSITIONABLE:SmokeBlue()
self:F2()
trigger.action.smoke( self:GetVec3(), trigger.smokeColor.Blue )
end

View File

@@ -61,7 +61,6 @@ function STATIC:FindByName( StaticName, RaiseError )
if StaticFound then
StaticFound:F3( { StaticName } )
return StaticFound
end
@@ -92,4 +91,18 @@ end
function STATIC:GetThreatLevel()
return 1, "Static"
end
end
--- Respawn the @{Unit} using a (tweaked) template of the parent Group.
-- @param #UNIT self
-- @param Core.Point#COORDINATE Coordinate The coordinate where to spawn the new Static.
-- @param #number Heading The heading of the unit respawn.
function STATIC:ReSpawn( Coordinate, Heading )
-- todo: need to fix country
local SpawnStatic = SPAWNSTATIC:NewFromStatic( self.StaticName, country.id.USA )
SpawnStatic:SpawnFromPointVec2( Coordinate, Heading, self.StaticName )
end

View File

@@ -159,6 +159,27 @@ function UNIT:GetDCSObject()
return nil
end
--- Destroys the UNIT.
-- @param #UNIT self
-- @return #nil The DCS Unit is not existing or alive.
function UNIT:Destroy()
self:F2( self.ObjectName )
local DCSObject = self:GetDCSObject()
if DCSObject then
local UnitGroup = self:GetGroup()
local UnitGroupName = UnitGroup:GetName()
self:F( { UnitGroupName = UnitGroupName } )
USERFLAG:New( UnitGroupName ):Set( 100 )
--BASE:CreateEventCrash( timer.getTime(), DCSObject )
DCSObject:destroy()
end
return nil
end
--- Respawn the @{Unit} using a (tweaked) template of the parent Group.
--
-- This function will:
@@ -486,12 +507,12 @@ function UNIT:GetRadar()
return nil, nil
end
--- Returns relative amount of fuel (from 0.0 to 1.0) the unit has in its internal tanks. If there are additional fuel tanks the value may be greater than 1.0.
--- Returns relative amount of fuel (from 0.0 to 1.0) the UNIT has in its internal tanks. If there are additional fuel tanks the value may be greater than 1.0.
-- @param #UNIT self
-- @return #number The relative amount of fuel (from 0.0 to 1.0).
-- @return #nil The DCS Unit is not existing or alive.
function UNIT:GetFuel()
self:F2( self.UnitName )
self:F( self.UnitName )
local DCSUnit = self:GetDCSObject()
@@ -536,7 +557,7 @@ function UNIT:GetLife()
return UnitLife
end
return nil
return -1
end
--- Returns the Unit's initial health.
@@ -553,7 +574,7 @@ function UNIT:GetLife0()
return UnitLife0
end
return nil
return 0
end
--- Returns the category name of the #UNIT.
@@ -598,122 +619,128 @@ end
-- @param #UNIT self
function UNIT:GetThreatLevel()
local Attributes = self:GetDesc().attributes
self:T( Attributes )
local ThreatLevel = 0
local ThreatText = ""
if self:IsGround() then
local Descriptor = self:GetDesc()
self:T( "Ground" )
if Descriptor then
local ThreatLevels = {
"Unarmed",
"Infantry",
"Old Tanks & APCs",
"Tanks & IFVs without ATGM",
"Tanks & IFV with ATGM",
"Modern Tanks",
"AAA",
"IR Guided SAMs",
"SR SAMs",
"MR SAMs",
"LR SAMs"
}
local Attributes = Descriptor.attributes
self:T( Attributes )
if self:IsGround() then
self:T( "Ground" )
if Attributes["LR SAM"] then ThreatLevel = 10
elseif Attributes["MR SAM"] then ThreatLevel = 9
elseif Attributes["SR SAM"] and
not Attributes["IR Guided SAM"] then ThreatLevel = 8
elseif ( Attributes["SR SAM"] or Attributes["MANPADS"] ) and
Attributes["IR Guided SAM"] then ThreatLevel = 7
elseif Attributes["AAA"] then ThreatLevel = 6
elseif Attributes["Modern Tanks"] then ThreatLevel = 5
elseif ( Attributes["Tanks"] or Attributes["IFV"] ) and
Attributes["ATGM"] then ThreatLevel = 4
elseif ( Attributes["Tanks"] or Attributes["IFV"] ) and
not Attributes["ATGM"] then ThreatLevel = 3
elseif Attributes["Old Tanks"] or Attributes["APC"] or Attributes["Artillery"] then ThreatLevel = 2
elseif Attributes["Infantry"] then ThreatLevel = 1
local ThreatLevels = {
"Unarmed",
"Infantry",
"Old Tanks & APCs",
"Tanks & IFVs without ATGM",
"Tanks & IFV with ATGM",
"Modern Tanks",
"AAA",
"IR Guided SAMs",
"SR SAMs",
"MR SAMs",
"LR SAMs"
}
if Attributes["LR SAM"] then ThreatLevel = 10
elseif Attributes["MR SAM"] then ThreatLevel = 9
elseif Attributes["SR SAM"] and
not Attributes["IR Guided SAM"] then ThreatLevel = 8
elseif ( Attributes["SR SAM"] or Attributes["MANPADS"] ) and
Attributes["IR Guided SAM"] then ThreatLevel = 7
elseif Attributes["AAA"] then ThreatLevel = 6
elseif Attributes["Modern Tanks"] then ThreatLevel = 5
elseif ( Attributes["Tanks"] or Attributes["IFV"] ) and
Attributes["ATGM"] then ThreatLevel = 4
elseif ( Attributes["Tanks"] or Attributes["IFV"] ) and
not Attributes["ATGM"] then ThreatLevel = 3
elseif Attributes["Old Tanks"] or Attributes["APC"] or Attributes["Artillery"] then ThreatLevel = 2
elseif Attributes["Infantry"] then ThreatLevel = 1
end
ThreatText = ThreatLevels[ThreatLevel+1]
end
ThreatText = ThreatLevels[ThreatLevel+1]
end
if self:IsAir() then
self:T( "Air" )
local ThreatLevels = {
"Unarmed",
"Tanker",
"AWACS",
"Transport Helicopter",
"UAV",
"Bomber",
"Strategic Bomber",
"Attack Helicopter",
"Battleplane",
"Multirole Fighter",
"Fighter"
}
if self:IsAir() then
if Attributes["Fighters"] then ThreatLevel = 10
elseif Attributes["Multirole fighters"] then ThreatLevel = 9
elseif Attributes["Battleplanes"] then ThreatLevel = 8
elseif Attributes["Attack helicopters"] then ThreatLevel = 7
elseif Attributes["Strategic bombers"] then ThreatLevel = 6
elseif Attributes["Bombers"] then ThreatLevel = 5
elseif Attributes["UAVs"] then ThreatLevel = 4
elseif Attributes["Transport helicopters"] then ThreatLevel = 3
elseif Attributes["AWACS"] then ThreatLevel = 2
elseif Attributes["Tankers"] then ThreatLevel = 1
self:T( "Air" )
local ThreatLevels = {
"Unarmed",
"Tanker",
"AWACS",
"Transport Helicopter",
"UAV",
"Bomber",
"Strategic Bomber",
"Attack Helicopter",
"Battleplane",
"Multirole Fighter",
"Fighter"
}
if Attributes["Fighters"] then ThreatLevel = 10
elseif Attributes["Multirole fighters"] then ThreatLevel = 9
elseif Attributes["Battleplanes"] then ThreatLevel = 8
elseif Attributes["Attack helicopters"] then ThreatLevel = 7
elseif Attributes["Strategic bombers"] then ThreatLevel = 6
elseif Attributes["Bombers"] then ThreatLevel = 5
elseif Attributes["UAVs"] then ThreatLevel = 4
elseif Attributes["Transport helicopters"] then ThreatLevel = 3
elseif Attributes["AWACS"] then ThreatLevel = 2
elseif Attributes["Tankers"] then ThreatLevel = 1
end
ThreatText = ThreatLevels[ThreatLevel+1]
end
ThreatText = ThreatLevels[ThreatLevel+1]
end
if self:IsShip() then
self:T( "Ship" )
--["Aircraft Carriers"] = {"Heavy armed ships",},
--["Cruisers"] = {"Heavy armed ships",},
--["Destroyers"] = {"Heavy armed ships",},
--["Frigates"] = {"Heavy armed ships",},
--["Corvettes"] = {"Heavy armed ships",},
--["Heavy armed ships"] = {"Armed ships", "Armed Air Defence", "HeavyArmoredUnits",},
--["Light armed ships"] = {"Armed ships","NonArmoredUnits"},
--["Armed ships"] = {"Ships"},
--["Unarmed ships"] = {"Ships","HeavyArmoredUnits",},
local ThreatLevels = {
"Unarmed ship",
"Light armed ships",
"Corvettes",
"",
"Frigates",
"",
"Cruiser",
"",
"Destroyer",
"",
"Aircraft Carrier"
}
if self:IsShip() then
self:T( "Ship" )
--["Aircraft Carriers"] = {"Heavy armed ships",},
--["Cruisers"] = {"Heavy armed ships",},
--["Destroyers"] = {"Heavy armed ships",},
--["Frigates"] = {"Heavy armed ships",},
--["Corvettes"] = {"Heavy armed ships",},
--["Heavy armed ships"] = {"Armed ships", "Armed Air Defence", "HeavyArmoredUnits",},
--["Light armed ships"] = {"Armed ships","NonArmoredUnits"},
--["Armed ships"] = {"Ships"},
--["Unarmed ships"] = {"Ships","HeavyArmoredUnits",},
if Attributes["Aircraft Carriers"] then ThreatLevel = 10
elseif Attributes["Destroyers"] then ThreatLevel = 8
elseif Attributes["Cruisers"] then ThreatLevel = 6
elseif Attributes["Frigates"] then ThreatLevel = 4
elseif Attributes["Corvettes"] then ThreatLevel = 2
elseif Attributes["Light armed ships"] then ThreatLevel = 1
local ThreatLevels = {
"Unarmed ship",
"Light armed ships",
"Corvettes",
"",
"Frigates",
"",
"Cruiser",
"",
"Destroyer",
"",
"Aircraft Carrier"
}
if Attributes["Aircraft Carriers"] then ThreatLevel = 10
elseif Attributes["Destroyers"] then ThreatLevel = 8
elseif Attributes["Cruisers"] then ThreatLevel = 6
elseif Attributes["Frigates"] then ThreatLevel = 4
elseif Attributes["Corvettes"] then ThreatLevel = 2
elseif Attributes["Light armed ships"] then ThreatLevel = 1
end
ThreatText = ThreatLevels[ThreatLevel+1]
end
ThreatText = ThreatLevels[ThreatLevel+1]
end
self:T2( ThreatLevel )
@@ -734,10 +761,9 @@ function UNIT:IsInZone( Zone )
if self:IsAlive() then
local IsInZone = Zone:IsVec3InZone( self:GetVec3() )
self:T2( { IsInZone } )
self:E( { Unit = self.UnitName, IsInZone = IsInZone } )
return IsInZone
end
return false
end
@@ -788,92 +814,6 @@ end
--- Signal a flare at the position of the UNIT.
-- @param #UNIT self
-- @param Utilities.Utils#FLARECOLOR FlareColor
function UNIT:Flare( FlareColor )
self:F2()
trigger.action.signalFlare( self:GetVec3(), FlareColor , 0 )
end
--- Signal a white flare at the position of the UNIT.
-- @param #UNIT self
function UNIT:FlareWhite()
self:F2()
trigger.action.signalFlare( self:GetVec3(), trigger.flareColor.White , 0 )
end
--- Signal a yellow flare at the position of the UNIT.
-- @param #UNIT self
function UNIT:FlareYellow()
self:F2()
trigger.action.signalFlare( self:GetVec3(), trigger.flareColor.Yellow , 0 )
end
--- Signal a green flare at the position of the UNIT.
-- @param #UNIT self
function UNIT:FlareGreen()
self:F2()
trigger.action.signalFlare( self:GetVec3(), trigger.flareColor.Green , 0 )
end
--- Signal a red flare at the position of the UNIT.
-- @param #UNIT self
function UNIT:FlareRed()
self:F2()
local Vec3 = self:GetVec3()
if Vec3 then
trigger.action.signalFlare( Vec3, trigger.flareColor.Red, 0 )
end
end
--- Smoke the UNIT.
-- @param #UNIT self
function UNIT:Smoke( SmokeColor, Range )
self:F2()
if Range then
trigger.action.smoke( self:GetRandomVec3( Range ), SmokeColor )
else
trigger.action.smoke( self:GetVec3(), SmokeColor )
end
end
--- Smoke the UNIT Green.
-- @param #UNIT self
function UNIT:SmokeGreen()
self:F2()
trigger.action.smoke( self:GetVec3(), trigger.smokeColor.Green )
end
--- Smoke the UNIT Red.
-- @param #UNIT self
function UNIT:SmokeRed()
self:F2()
trigger.action.smoke( self:GetVec3(), trigger.smokeColor.Red )
end
--- Smoke the UNIT White.
-- @param #UNIT self
function UNIT:SmokeWhite()
self:F2()
trigger.action.smoke( self:GetVec3(), trigger.smokeColor.White )
end
--- Smoke the UNIT Orange.
-- @param #UNIT self
function UNIT:SmokeOrange()
self:F2()
trigger.action.smoke( self:GetVec3(), trigger.smokeColor.Orange )
end
--- Smoke the UNIT Blue.
-- @param #UNIT self
function UNIT:SmokeBlue()
self:F2()
trigger.action.smoke( self:GetVec3(), trigger.smokeColor.Blue )
end
-- Is methods