Merge branch 'master' into funkyfranky

This commit is contained in:
funkyfranky
2017-09-22 19:03:04 +02:00
30 changed files with 817 additions and 139 deletions

View File

@@ -248,6 +248,7 @@ function AI_A2A:New( AIGroup )
-- @param #AI_A2A self -- @param #AI_A2A self
-- @param #number Delay -- @param #number Delay
self:AddTransition( "*", "Takeoff", "Airborne" )
self:AddTransition( "*", "Return", "Returning" ) self:AddTransition( "*", "Return", "Returning" )
self:AddTransition( "*", "Hold", "Holding" ) self:AddTransition( "*", "Hold", "Holding" )
self:AddTransition( "*", "Home", "Home" ) self:AddTransition( "*", "Home", "Home" )
@@ -263,6 +264,13 @@ function AI_A2A:New( AIGroup )
return self return self
end 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 ) function AI_A2A:SetDispatcher( Dispatcher )
self.Dispatcher = Dispatcher self.Dispatcher = Dispatcher
end end

View File

@@ -141,7 +141,7 @@ function AI_A2A_CAP:New( AIGroup, PatrolZone, PatrolFloorAltitude, PatrolCeiling
self.EngageMinSpeed = EngageMinSpeed self.EngageMinSpeed = EngageMinSpeed
self.EngageMaxSpeed = EngageMaxSpeed 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. --- OnBefore Transition Handler for Event Engage.
-- @function [parent=#AI_A2A_CAP] OnBeforeEngage -- @function [parent=#AI_A2A_CAP] OnBeforeEngage
@@ -302,6 +302,17 @@ function AI_A2A_CAP:New( AIGroup, PatrolZone, PatrolFloorAltitude, PatrolCeiling
return self return self
end 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 #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
function AI_A2A_CAP:onafterStart( AIGroup, From, Event, To )
AIGroup:HandleEvent( EVENTS.Takeoff, nil, self )
end
--- Set the Engage Zone which defines where the AI will engage bogies. --- Set the Engage Zone which defines where the AI will engage bogies.
-- @param #AI_A2A_CAP self -- @param #AI_A2A_CAP self

View File

@@ -978,6 +978,7 @@ do -- AI_A2A_DISPATCHER
-- This will avoid the detection to still "know" the shot unit until the next detection. -- This will avoid the detection to still "know" the shot unit until the next detection.
-- Otherwise, a new intercept or engage may happen for an already shot plane! -- Otherwise, a new intercept or engage may happen for an already shot plane!
self:HandleEvent( EVENTS.Crash, self.OnEventCrashOrDead ) self:HandleEvent( EVENTS.Crash, self.OnEventCrashOrDead )
self:HandleEvent( EVENTS.Dead, self.OnEventCrashOrDead ) self:HandleEvent( EVENTS.Dead, self.OnEventCrashOrDead )
@@ -1360,6 +1361,8 @@ do -- AI_A2A_DISPATCHER
-- @param #AI_A2A_DISPATCHER self -- @param #AI_A2A_DISPATCHER self
function AI_A2A_DISPATCHER:SetDefenderTask( SquadronName, Defender, Type, Fsm, Target ) function AI_A2A_DISPATCHER:SetDefenderTask( SquadronName, Defender, Type, Fsm, Target )
self:F( { SquadronName = SquadronName, Defender = Defender:GetName() } )
self.DefenderTasks[Defender] = self.DefenderTasks[Defender] or {} self.DefenderTasks[Defender] = self.DefenderTasks[Defender] or {}
self.DefenderTasks[Defender].Type = Type self.DefenderTasks[Defender].Type = Type
self.DefenderTasks[Defender].Fsm = Fsm self.DefenderTasks[Defender].Fsm = Fsm
@@ -2526,15 +2529,18 @@ do -- AI_A2A_DISPATCHER
DetectedSet:Flush() DetectedSet:Flush()
local DefenderTasks = self:GetDefenderTasks() local DefenderTasks = self:GetDefenderTasks()
for Defender, DefenderTask in pairs( DefenderTasks ) do for DefenderGroup, DefenderTask in pairs( DefenderTasks ) do
local Defender = Defender -- Wrapper.Group#GROUP local Defender = DefenderGroup -- Wrapper.Group#GROUP
local DefenderTaskTarget = DefenderTask.Target local DefenderTaskTarget = DefenderTask.Target
local DefenderSquadronName = DefenderTask.SquadronName local DefenderSquadronName = DefenderTask.SquadronName
if DefenderTaskTarget and DefenderTaskTarget.Index == AttackerDetection.Index then if DefenderTaskTarget and DefenderTaskTarget.Index == AttackerDetection.Index then
local Squadron = self:GetSquadron( DefenderSquadronName ) local Squadron = self:GetSquadron( DefenderSquadronName )
local SquadronOverhead = Squadron.Overhead or self.DefenderDefault.Overhead local SquadronOverhead = Squadron.Overhead or self.DefenderDefault.Overhead
DefenderCount = DefenderCount + Defender:GetSize() / SquadronOverhead
self:E( "Defender Group Name: " .. Defender:GetName() .. ", Size: " .. Defender:GetSize() ) local DefenderSize = Defender:GetInitialSize()
DefenderCount = DefenderCount + DefenderSize / SquadronOverhead
self:F( "Defender Group Name: " .. Defender:GetName() .. ", Size: " .. DefenderSize )
end end
end end
@@ -2622,10 +2628,21 @@ do -- AI_A2A_DISPATCHER
Fsm:SetDisengageRadius( self.DisengageRadius ) Fsm:SetDisengageRadius( self.DisengageRadius )
Fsm:SetTanker( DefenderSquadron.TankerName or self.DefenderDefault.TankerName ) Fsm:SetTanker( DefenderSquadron.TankerName or self.DefenderDefault.TankerName )
Fsm:Start() Fsm:Start()
Fsm:__Patrol( 2 )
self:SetDefenderTask( SquadronName, DefenderCAP, "CAP", Fsm ) self:SetDefenderTask( SquadronName, DefenderCAP, "CAP", Fsm )
function Fsm:onafterTakeoff( Defender, From, Event, To )
self:F({"GCI Birth", Defender:GetName()})
--self:GetParent(self).onafterBirth( self, Defender, From, Event, To )
local Dispatcher = Fsm:GetDispatcher() -- #AI_A2A_DISPATCHER
local Squadron = Dispatcher:GetSquadronFromDefender( Defender )
if Squadron then
Fsm:__Patrol( 2 ) -- Start Patrolling
end
end
function Fsm:onafterRTB( Defender, From, Event, To ) function Fsm:onafterRTB( Defender, From, Event, To )
self:F({"CAP RTB", Defender:GetName()}) self:F({"CAP RTB", Defender:GetName()})
self:GetParent(self).onafterRTB( self, Defender, From, Event, To ) self:GetParent(self).onafterRTB( self, Defender, From, Event, To )
@@ -2721,7 +2738,7 @@ do -- AI_A2A_DISPATCHER
local SpawnCoord = DefenderSquadron.Airbase:GetCoordinate() -- Core.Point#COORDINATE local SpawnCoord = DefenderSquadron.Airbase:GetCoordinate() -- Core.Point#COORDINATE
local AttackerCoord = AttackerUnit:GetCoordinate() local AttackerCoord = AttackerUnit:GetCoordinate()
local InterceptCoord = AttackerDetection.InterceptCoord local InterceptCoord = AttackerDetection.InterceptCoord
self:F({InterceptCoord = InterceptCoord}) self:F( { InterceptCoord = InterceptCoord } )
if InterceptCoord then if InterceptCoord then
local InterceptDistance = SpawnCoord:Get2DDistance( InterceptCoord ) local InterceptDistance = SpawnCoord:Get2DDistance( InterceptCoord )
local AirbaseDistance = SpawnCoord:Get2DDistance( AttackerCoord ) local AirbaseDistance = SpawnCoord:Get2DDistance( AttackerCoord )
@@ -2757,6 +2774,11 @@ do -- AI_A2A_DISPATCHER
self:F( { Grouping = DefenderGrouping, SquadronGrouping = DefenderSquadron.Grouping, DefaultGrouping = self.DefenderDefault.Grouping } ) self:F( { Grouping = DefenderGrouping, SquadronGrouping = DefenderSquadron.Grouping, DefaultGrouping = self.DefenderDefault.Grouping } )
self:F( { DefendersCount = DefenderCount, DefendersNeeded = DefendersNeeded } ) self:F( { DefendersCount = DefenderCount, DefendersNeeded = DefendersNeeded } )
if DefendersNeeded > DefenderSquadron.Resources then
DefendersNeeded = DefenderSquadron.Resources
BreakLoop = true
end
while ( DefendersNeeded > 0 ) do while ( DefendersNeeded > 0 ) do
local Spawn = DefenderSquadron.Spawn[ math.random( 1, #DefenderSquadron.Spawn ) ] -- Functional.Spawn#SPAWN local Spawn = DefenderSquadron.Spawn[ math.random( 1, #DefenderSquadron.Spawn ) ] -- Functional.Spawn#SPAWN
@@ -2769,7 +2791,7 @@ do -- AI_A2A_DISPATCHER
local TakeoffMethod = self:GetSquadronTakeoff( ClosestDefenderSquadronName ) local TakeoffMethod = self:GetSquadronTakeoff( ClosestDefenderSquadronName )
local DefenderGCI = Spawn:SpawnAtAirbase( DefenderSquadron.Airbase, TakeoffMethod, DefenderSquadron.TakeoffAltitude or self.DefenderDefault.TakeoffAltitude ) -- Wrapper.Group#GROUP local DefenderGCI = Spawn:SpawnAtAirbase( DefenderSquadron.Airbase, TakeoffMethod, DefenderSquadron.TakeoffAltitude or self.DefenderDefault.TakeoffAltitude ) -- Wrapper.Group#GROUP
self:F( { GCIDefender = DefenderGCI:GetName() } ) self:E( { GCIDefender = DefenderGCI:GetName() } )
DefendersNeeded = DefendersNeeded - DefenderGrouping DefendersNeeded = DefendersNeeded - DefenderGrouping
@@ -2786,12 +2808,24 @@ do -- AI_A2A_DISPATCHER
Fsm:SetDamageThreshold( self.DefenderDefault.DamageThreshold ) Fsm:SetDamageThreshold( self.DefenderDefault.DamageThreshold )
Fsm:SetDisengageRadius( self.DisengageRadius ) Fsm:SetDisengageRadius( self.DisengageRadius )
Fsm:Start() Fsm:Start()
Fsm:__Engage( 2, AttackerDetection.Set ) -- Engage on the TargetSetUnit
self:SetDefenderTask( ClosestDefenderSquadronName, DefenderGCI, "GCI", Fsm, AttackerDetection ) self:SetDefenderTask( ClosestDefenderSquadronName, DefenderGCI, "GCI", Fsm, AttackerDetection )
function Fsm:onafterTakeoff( Defender, From, Event, To )
self:F({"GCI Birth", Defender:GetName()})
--self:GetParent(self).onafterBirth( self, Defender, From, Event, To )
local Dispatcher = Fsm:GetDispatcher() -- #AI_A2A_DISPATCHER
local Squadron = Dispatcher:GetSquadronFromDefender( Defender )
local DefenderTarget = Dispatcher:GetDefenderTaskTarget( Defender )
if DefenderTarget then
Fsm:__Engage( 2, DefenderTarget.Set ) -- Engage on the TargetSetUnit
end
end
function Fsm:onafterRTB( Defender, From, Event, To ) function Fsm:onafterRTB( Defender, From, Event, To )
self:F({"GCI RTB", Defender:GetName()}) self:F({"GCI RTB", Defender:GetName()})
self:GetParent(self).onafterRTB( self, Defender, From, Event, To ) self:GetParent(self).onafterRTB( self, Defender, From, Event, To )
@@ -2920,7 +2954,11 @@ do -- AI_A2A_DISPATCHER
for AIGroup, DefenderTask in pairs( self:GetDefenderTasks() ) do for AIGroup, DefenderTask in pairs( self:GetDefenderTasks() ) do
local AIGroup = AIGroup -- Wrapper.Group#GROUP local AIGroup = AIGroup -- Wrapper.Group#GROUP
if not AIGroup:IsAlive() then if not AIGroup:IsAlive() then
self:ClearDefenderTask( AIGroup ) local DefenderTaskFsm = self:GetDefenderTaskFsm( AIGroup )
self:E( { Defender = AIGroup:GetName(), DefenderState = DefenderTaskFsm:GetState() } )
if not DefenderTaskFsm:Is( "Started" ) then
self:ClearDefenderTask( AIGroup )
end
else else
if DefenderTask.Target then if DefenderTask.Target then
local AttackerItem = Detection:GetDetectedItem( DefenderTask.Target.Index ) local AttackerItem = Detection:GetDetectedItem( DefenderTask.Target.Index )

View File

@@ -134,7 +134,7 @@ function AI_A2A_GCI:New( AIGroup, EngageMinSpeed, EngageMaxSpeed )
self.PatrolAltType = "RADIO" 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. --- OnBefore Transition Handler for Event Engage.
-- @function [parent=#AI_A2A_GCI] OnBeforeEngage -- @function [parent=#AI_A2A_GCI] OnBeforeEngage
@@ -295,6 +295,19 @@ function AI_A2A_GCI:New( AIGroup, EngageMinSpeed, EngageMaxSpeed )
return self return self
end 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 #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
function AI_A2A_GCI:onafterStart( AIGroup, From, Event, To )
AIGroup:HandleEvent( EVENTS.Takeoff, nil, self )
end
--- onafter State Transition for Event Patrol. --- onafter State Transition for Event Patrol.
-- @param #AI_A2A_GCI self -- @param #AI_A2A_GCI self

View File

@@ -179,7 +179,7 @@ function AI_A2A_PATROL:New( AIGroup, PatrolZone, PatrolFloorAltitude, PatrolCeil
-- defafult PatrolAltType to "RADIO" if not specified -- defafult PatrolAltType to "RADIO" if not specified
self.PatrolAltType = PatrolAltType or "RADIO" self.PatrolAltType = PatrolAltType or "RADIO"
self:AddTransition( { "Started", "Refuelling" }, "Patrol", "Patrolling" ) self:AddTransition( { "Started", "Airborne", "Refuelling" }, "Patrol", "Patrolling" )
--- OnBefore Transition Handler for Event Patrol. --- OnBefore Transition Handler for Event Patrol.
-- @function [parent=#AI_A2A_PATROL] OnBeforePatrol -- @function [parent=#AI_A2A_PATROL] OnBeforePatrol

View File

@@ -358,16 +358,20 @@ function AI_CAP_ZONE:onafterStart( Controllable, From, Event, To )
end end
-- todo: need to fix this global function
--- @param Wrapper.Controllable#CONTROLLABLE AIControllable --- @param AI.AI_CAP#AI_CAP_ZONE
function _NewEngageCapRoute( AIControllable ) -- @param Wrapper.Group#GROUP EngageGroup
function AI_CAP_ZONE.EngageRoute( EngageGroup, Fsm )
AIControllable:T( "NewEngageRoute" ) EngageGroup:F( { "AI_CAP_ZONE.EngageRoute:", EngageGroup:GetName() } )
local EngageZone = AIControllable:GetState( AIControllable, "EngageZone" ) -- AI.AI_Cap#AI_CAP_ZONE
EngageZone:__Engage( 1 ) if EngageGroup:IsAlive() then
Fsm:__Engage( 1 )
end
end end
--- @param #AI_CAP_ZONE self --- @param #AI_CAP_ZONE self
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM. -- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
-- @param #string From The From State string. -- @param #string From The From State string.
@@ -503,28 +507,20 @@ function AI_CAP_ZONE:onafterEngage( Controllable, From, Event, To )
end end
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 if #AttackTasks == 0 then
self:F("No targets found -> Going back to Patrolling") self:F("No targets found -> Going back to Patrolling")
self:__Abort( 1 ) self:__Abort( 1 )
self:__Route( 1 ) self:__Route( 1 )
self:SetDetectionActivated() self:SetDetectionActivated()
else else
AttackTasks[#AttackTasks+1] = Controllable:TaskFunction( "AI_CAP_ZONE.EngageRoute", self )
EngageRoute[1].task = Controllable:TaskCombo( AttackTasks ) 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() self:SetDetectionDeactivated()
end end
--- NOW ROUTE THE GROUP! Controllable:Route( EngageRoute, 0.5 )
self.Controllable:WayPointExecute( 1, 2 )
end end
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. self:SetDetectionDeactivated() -- When not engaging, set the detection off.
end end
--- @param Wrapper.Controllable#CONTROLLABLE AIControllable --- @param AI.AI_CAS#AI_CAS_ZONE
function _NewEngageRoute( AIControllable ) -- @param Wrapper.Group#GROUP EngageGroup
function AI_CAS_ZONE.EngageRoute( EngageGroup, Fsm )
AIControllable:T( "NewEngageRoute" ) EngageGroup:F( { "AI_CAS_ZONE.EngageRoute:", EngageGroup:GetName() } )
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 ) if EngageGroup:IsAlive() then
Fsm:__Engage( 1, Fsm.EngageSpeed, Fsm.EngageAltitude, Fsm.EngageWeaponExpend, Fsm.EngageAttackQty, Fsm.EngageDirection )
end
end end
@@ -464,6 +467,9 @@ function AI_CAS_ZONE:onafterEngage( Controllable, From, Event, To,
if Controllable:IsAlive() then if Controllable:IsAlive() then
Controllable:OptionROEOpenFire()
Controllable:OptionROTVertical()
local EngageRoute = {} local EngageRoute = {}
--- Calculate the current route point. --- Calculate the current route point.
@@ -485,7 +491,7 @@ function AI_CAS_ZONE:onafterEngage( Controllable, From, Event, To,
local AttackTasks = {} local AttackTasks = {}
for DetectedUnitID, DetectedUnit in pairs( self.DetectedUnits ) do for DetectedUnit, Detected in pairs( self.DetectedUnits ) do
local DetectedUnit = DetectedUnit -- Wrapper.Unit#UNIT local DetectedUnit = DetectedUnit -- Wrapper.Unit#UNIT
self:T( DetectedUnit ) self:T( DetectedUnit )
if DetectedUnit:IsAlive() then if DetectedUnit:IsAlive() then
@@ -503,7 +509,8 @@ function AI_CAS_ZONE:onafterEngage( Controllable, From, Event, To,
end end
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. --- Define a random point in the @{Zone}. The AI will fly to that point within the zone.
@@ -525,19 +532,7 @@ function AI_CAS_ZONE:onafterEngage( Controllable, From, Event, To,
EngageRoute[#EngageRoute+1] = ToTargetRoutePoint 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:Route( EngageRoute, 0.5 )
Controllable:WayPointInitialize( EngageRoute )
--- 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:SetRefreshTimeInterval( 2 ) self:SetRefreshTimeInterval( 2 )
self:SetDetectionActivated() self:SetDetectionActivated()

View File

@@ -608,6 +608,22 @@ function BASE:CreateEventCrash( EventTime, Initiator )
world.onEvent( Event ) world.onEvent( Event )
end 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. -- TODO: Complete Dcs.DCSTypes#Event structure.
--- The main event handling function... This function captures all events generated for the class. --- The main event handling function... This function captures all events generated for the class.
-- @param #BASE self -- @param #BASE self

View File

@@ -561,12 +561,13 @@ end
-- @param Core.Base#BASE EventClass The self instance of the class for which the event is. -- @param Core.Base#BASE EventClass The self instance of the class for which the event is.
-- @param EventID -- @param EventID
-- @return #EVENT -- @return #EVENT
function EVENT:OnEventForGroup( GroupName, EventFunction, EventClass, EventID ) function EVENT:OnEventForGroup( GroupName, EventFunction, EventClass, EventID, ... )
self:F2( GroupName ) self:E( GroupName )
local Event = self:Init( EventID, EventClass ) local Event = self:Init( EventID, EventClass )
Event.EventGroup = true Event.EventGroup = true
Event.EventFunction = EventFunction Event.EventFunction = EventFunction
Event.Params = arg
return self return self
end end
@@ -950,7 +951,7 @@ function EVENT:onEvent( Event )
local Result, Value = xpcall( local Result, Value = xpcall(
function() function()
return EventData.EventFunction( EventClass, Event ) return EventData.EventFunction( EventClass, Event, unpack( EventData.Params ) )
end, ErrorHandler ) end, ErrorHandler )
else else
@@ -966,14 +967,14 @@ function EVENT:onEvent( Event )
local Result, Value = xpcall( local Result, Value = xpcall(
function() function()
return EventFunction( EventClass, Event ) return EventFunction( EventClass, Event, unpack( EventData.Params ) )
end, ErrorHandler ) end, ErrorHandler )
end end
end end
end end
else else
-- The EventClass is not alive anymore, we remove it from the EventHandlers... -- The EventClass is not alive anymore, we remove it from the EventHandlers...
self:RemoveEvent( EventClass, Event.id ) --self:RemoveEvent( EventClass, Event.id )
end end
else else

View File

@@ -140,10 +140,23 @@ do -- MENU_COMMAND_BASE
local self = BASE:Inherit( self, MENU_BASE:New( MenuText, ParentMenu ) ) -- #MENU_COMMAND_BASE 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:SetCommandMenuFunction( CommandMenuFunction ) self:SetCommandMenuFunction( CommandMenuFunction )
self:SetCommandMenuArguments( CommandMenuArguments ) self:SetCommandMenuArguments( CommandMenuArguments )
self.MenuCallHandler = function() self.MenuCallHandler = function()
self.CommandMenuFunction( unpack( self.CommandMenuArguments ) ) local function MenuFunction()
return self.CommandMenuFunction( unpack( self.CommandMenuArguments ) )
end
local Status, Result = xpcall( MenuFunction, ErrorHandler )
end end
return self return self

View File

@@ -488,9 +488,9 @@ do -- COORDINATE
function COORDINATE:GetHeadingText( Settings ) function COORDINATE:GetHeadingText( Settings )
local Heading = self:GetHeading() local Heading = self:GetHeading()
if Heading then if Heading then
return string.format( " heading %3.2f °", Heading ) return string.format( " bearing %3d°", Heading )
else else
return " heading cannot be determined" return " bearing unknown"
end end
end end
@@ -1072,7 +1072,7 @@ do -- COORDINATE
end end
if ModeA2A then if ModeA2A == true then
return self:ToStringA2A( Controllable, Settings ) return self:ToStringA2A( Controllable, Settings )
else else
return self:ToStringA2G( Controllable, Settings ) return self:ToStringA2G( Controllable, Settings )

View File

@@ -1883,7 +1883,7 @@ end
-- @return #number Heading Heading in degrees and speed in mps in case of moving units. -- @return #number Heading Heading in degrees and speed in mps in case of moving units.
function SET_UNIT:GetHeading() function SET_UNIT:GetHeading()
local AvgHeading = nil local HeadingSet = nil
local MovingCount = 0 local MovingCount = 0
for UnitName, UnitData in pairs( self:GetSet() ) do for UnitName, UnitData in pairs( self:GetSet() ) do
@@ -1894,15 +1894,20 @@ function SET_UNIT:GetHeading()
local Velocity = Coordinate:GetVelocity() local Velocity = Coordinate:GetVelocity()
if Velocity ~= 0 then if Velocity ~= 0 then
local Heading = Coordinate:GetHeading() local Heading = Coordinate:GetHeading()
AvgHeading = AvgHeading and ( AvgHeading + Heading ) or Heading if HeadingSet == nil then
MovingCount = MovingCount + 1 HeadingSet = Heading
else
local HeadingDiff = ( HeadingSet - Heading + 180 + 360 ) % 360 - 180
HeadingDiff = math.abs( HeadingDiff )
if HeadingDiff > 5 then
HeadingSet = nil
break
end
end
end end
end end
AvgHeading = AvgHeading and ( AvgHeading / MovingCount ) return HeadingSet
self:F( { AvgHeading = AvgHeading } )
return AvgHeading
end end

View File

@@ -608,9 +608,9 @@ function SCORING:_AddPlayerFromUnit( UnitData )
if self.Players[PlayerName].UnitCoalition ~= UnitCoalition then if self.Players[PlayerName].UnitCoalition ~= UnitCoalition then
self.Players[PlayerName].Penalty = self.Players[PlayerName].Penalty + 50 self.Players[PlayerName].Penalty = self.Players[PlayerName].Penalty + 50
self.Players[PlayerName].PenaltyCoalition = self.Players[PlayerName].PenaltyCoalition + 1 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.", "(changed " .. self.Players[PlayerName].PenaltyCoalition .. " times the coalition). 50 Penalty points added.",
2 MESSAGE.Type.Information
):ToAll() ):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, 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() ) UnitName, _SCORINGCoalition[UnitCoalition], _SCORINGCategory[UnitCategory], UnitData:GetTypeName() )
@@ -626,16 +626,16 @@ function SCORING:_AddPlayerFromUnit( UnitData )
if self.Players[PlayerName].Penalty > self.Fratricide * 0.50 then if self.Players[PlayerName].Penalty > self.Fratricide * 0.50 then
if self.Players[PlayerName].PenaltyWarning < 1 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, 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,
30 MESSAGE.Type.Information
):ToAll() ):ToAll()
self.Players[PlayerName].PenaltyWarning = self.Players[PlayerName].PenaltyWarning + 1 self.Players[PlayerName].PenaltyWarning = self.Players[PlayerName].PenaltyWarning + 1
end end
end end
if self.Players[PlayerName].Penalty > self.Fratricide then if self.Players[PlayerName].Penalty > self.Fratricide then
MESSAGE:New( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' committed FRATRICIDE, he will be COURT MARTIALED and is DISMISSED from this mission!", MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' committed FRATRICIDE, he will be COURT MARTIALED and is DISMISSED from this mission!",
10 MESSAGE.Type.Information
):ToAll() ):ToAll()
UnitData:GetGroup():Destroy() UnitData:GetGroup():Destroy()
end end
@@ -668,7 +668,7 @@ function SCORING:AddGoalScore( PlayerUnit, GoalTag, Text, Score )
PlayerData.Goals[GoalTag].Score = PlayerData.Goals[GoalTag].Score + Score PlayerData.Goals[GoalTag].Score = PlayerData.Goals[GoalTag].Score + Score
PlayerData.Score = PlayerData.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() ) self:ScoreCSV( PlayerName, "", "GOAL_" .. string.upper( GoalTag ), 1, Score, PlayerUnit:GetName() )
end end
@@ -704,7 +704,7 @@ function SCORING:_AddMissionTaskScore( Mission, PlayerUnit, Text, Score )
PlayerData.Score = self.Players[PlayerName].Score + Score PlayerData.Score = self.Players[PlayerName].Score + Score
PlayerData.Mission[MissionName].ScoreTask = self.Players[PlayerName].Mission[MissionName].ScoreTask + 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() ) self:ScoreCSV( PlayerName, "", "TASK_" .. MissionName:gsub( ' ', '_' ), 1, Score, PlayerUnit:GetName() )
end end
@@ -732,9 +732,9 @@ function SCORING:_AddMissionScore( Mission, Text, Score )
PlayerData.Score = PlayerData.Score + Score PlayerData.Score = PlayerData.Score + Score
PlayerData.Mission[MissionName].ScoreMission = PlayerData.Mission[MissionName].ScoreMission + 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!", Score .. " mission score!",
60 ):ToAll() MESSAGE.Type.Information ):ToAll()
self:ScoreCSV( PlayerName, "", "MISSION_" .. MissionName:gsub( ' ', '_' ), 1, Score ) self:ScoreCSV( PlayerName, "", "MISSION_" .. MissionName:gsub( ' ', '_' ), 1, Score )
end end
@@ -902,19 +902,19 @@ function SCORING:_EventOnHit( Event )
if TargetPlayerName ~= nil then -- It is a player hitting another player ... if TargetPlayerName ~= nil then -- It is a player hitting another player ...
MESSAGE MESSAGE
:New( self.DisplayMessagePrefix .. "Player '" .. InitPlayerName .. "' hit friendly player '" .. TargetPlayerName .. "' " .. :NewType( self.DisplayMessagePrefix .. "Player '" .. InitPlayerName .. "' hit friendly player '" .. TargetPlayerName .. "' " ..
TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.PenaltyHit .. " times. " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.PenaltyHit .. " times. " ..
"Penalty: -" .. PlayerHit.Penalty .. ". Score Total:" .. Player.Score - Player.Penalty, "Penalty: -" .. PlayerHit.Penalty .. ". Score Total:" .. Player.Score - Player.Penalty,
2 MESSAGE.Type.Update
) )
:ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() ) :ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() )
:ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() ) :ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() )
else else
MESSAGE MESSAGE
:New( self.DisplayMessagePrefix .. "Player '" .. InitPlayerName .. "' hit friendly target " .. :NewType( self.DisplayMessagePrefix .. "Player '" .. InitPlayerName .. "' hit friendly target " ..
TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.PenaltyHit .. " times. " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.PenaltyHit .. " times. " ..
"Penalty: -" .. PlayerHit.Penalty .. ". Score Total:" .. Player.Score - Player.Penalty, "Penalty: -" .. PlayerHit.Penalty .. ". Score Total:" .. Player.Score - Player.Penalty,
2 MESSAGE.Type.Update
) )
:ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() ) :ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() )
:ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() ) :ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() )
@@ -926,19 +926,19 @@ function SCORING:_EventOnHit( Event )
PlayerHit.ScoreHit = PlayerHit.ScoreHit + 1 PlayerHit.ScoreHit = PlayerHit.ScoreHit + 1
if TargetPlayerName ~= nil then -- It is a player hitting another player ... if TargetPlayerName ~= nil then -- It is a player hitting another player ...
MESSAGE MESSAGE
:New( self.DisplayMessagePrefix .. "Player '" .. InitPlayerName .. "' hit enemy player '" .. TargetPlayerName .. "' " .. :NewType( self.DisplayMessagePrefix .. "Player '" .. InitPlayerName .. "' hit enemy player '" .. TargetPlayerName .. "' " ..
TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.ScoreHit .. " times. " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.ScoreHit .. " times. " ..
"Score: " .. PlayerHit.Score .. ". Score Total:" .. Player.Score - Player.Penalty, "Score: " .. PlayerHit.Score .. ". Score Total:" .. Player.Score - Player.Penalty,
2 MESSAGE.Type.Update
) )
:ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() ) :ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() )
:ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() ) :ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() )
else else
MESSAGE MESSAGE
:New( self.DisplayMessagePrefix .. "Player '" .. InitPlayerName .. "' hit enemy target " .. :NewType( self.DisplayMessagePrefix .. "Player '" .. InitPlayerName .. "' hit enemy target " ..
TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.ScoreHit .. " times. " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.ScoreHit .. " times. " ..
"Score: " .. PlayerHit.Score .. ". Score Total:" .. Player.Score - Player.Penalty, "Score: " .. PlayerHit.Score .. ". Score Total:" .. Player.Score - Player.Penalty,
2 MESSAGE.Type.Update
) )
:ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() ) :ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() )
:ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() ) :ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() )
@@ -947,8 +947,8 @@ function SCORING:_EventOnHit( Event )
end end
else -- A scenery object was hit. else -- A scenery object was hit.
MESSAGE MESSAGE
:New( self.DisplayMessagePrefix .. "Player '" .. InitPlayerName .. "' hit scenery object.", :NewType( self.DisplayMessagePrefix .. "Player '" .. InitPlayerName .. "' hit scenery object.",
2 MESSAGE.Type.Update
) )
:ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() ) :ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() )
:ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() ) :ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() )
@@ -1008,10 +1008,10 @@ function SCORING:_EventOnHit( Event )
PlayerHit.PenaltyHit = PlayerHit.PenaltyHit + 1 PlayerHit.PenaltyHit = PlayerHit.PenaltyHit + 1
MESSAGE MESSAGE
:New( self.DisplayMessagePrefix .. "Player '" .. Event.WeaponPlayerName .. "' hit friendly target " .. :NewType( self.DisplayMessagePrefix .. "Player '" .. Event.WeaponPlayerName .. "' hit friendly target " ..
TargetUnitCategory .. " ( " .. TargetType .. " ) " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " ..
"Penalty: -" .. PlayerHit.Penalty .. " = " .. Player.Score - Player.Penalty, "Penalty: -" .. PlayerHit.Penalty .. " = " .. Player.Score - Player.Penalty,
2 MESSAGE.Type.Update
) )
:ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() ) :ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() )
:ToCoalitionIf( Event.WeaponCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() ) :ToCoalitionIf( Event.WeaponCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() )
@@ -1021,10 +1021,10 @@ function SCORING:_EventOnHit( Event )
PlayerHit.Score = PlayerHit.Score + 1 PlayerHit.Score = PlayerHit.Score + 1
PlayerHit.ScoreHit = PlayerHit.ScoreHit + 1 PlayerHit.ScoreHit = PlayerHit.ScoreHit + 1
MESSAGE MESSAGE
:New( self.DisplayMessagePrefix .. "Player '" .. Event.WeaponPlayerName .. "' hit enemy target " .. :NewType( self.DisplayMessagePrefix .. "Player '" .. Event.WeaponPlayerName .. "' hit enemy target " ..
TargetUnitCategory .. " ( " .. TargetType .. " ) " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " ..
"Score: +" .. PlayerHit.Score .. " = " .. Player.Score - Player.Penalty, "Score: +" .. PlayerHit.Score .. " = " .. Player.Score - Player.Penalty,
2 MESSAGE.Type.Update
) )
:ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() ) :ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() )
:ToCoalitionIf( Event.WeaponCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() ) :ToCoalitionIf( Event.WeaponCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() )
@@ -1032,8 +1032,8 @@ function SCORING:_EventOnHit( Event )
end end
else -- A scenery object was hit. else -- A scenery object was hit.
MESSAGE MESSAGE
:New( self.DisplayMessagePrefix .. "Player '" .. Event.WeaponPlayerName .. "' hit scenery object.", :NewType( self.DisplayMessagePrefix .. "Player '" .. Event.WeaponPlayerName .. "' hit scenery object.",
2 MESSAGE.Type.Update
) )
:ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() ) :ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() )
:ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() ) :ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() )
@@ -1131,19 +1131,19 @@ function SCORING:_EventOnDeadOrCrash( Event )
if Player.HitPlayers[TargetPlayerName] then -- A player destroyed another player if Player.HitPlayers[TargetPlayerName] then -- A player destroyed another player
MESSAGE MESSAGE
:New( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' destroyed friendly player '" .. TargetPlayerName .. "' " .. :NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' destroyed friendly player '" .. TargetPlayerName .. "' " ..
TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " .. TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " ..
"Penalty: -" .. TargetDestroy.Penalty .. " = " .. Player.Score - Player.Penalty, "Penalty: -" .. TargetDestroy.Penalty .. " = " .. Player.Score - Player.Penalty,
15 MESSAGE.Type.Information
) )
:ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() ) :ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() )
:ToCoalitionIf( InitCoalition, self:IfMessagesDestroy() and self:IfMessagesToCoalition() ) :ToCoalitionIf( InitCoalition, self:IfMessagesDestroy() and self:IfMessagesToCoalition() )
else else
MESSAGE MESSAGE
:New( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' destroyed friendly target " .. :NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' destroyed friendly target " ..
TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " .. TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " ..
"Penalty: -" .. TargetDestroy.Penalty .. " = " .. Player.Score - Player.Penalty, "Penalty: -" .. TargetDestroy.Penalty .. " = " .. Player.Score - Player.Penalty,
15 MESSAGE.Type.Information
) )
:ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() ) :ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() )
:ToCoalitionIf( InitCoalition, self:IfMessagesDestroy() and self:IfMessagesToCoalition() ) :ToCoalitionIf( InitCoalition, self:IfMessagesDestroy() and self:IfMessagesToCoalition() )
@@ -1165,19 +1165,19 @@ function SCORING:_EventOnDeadOrCrash( Event )
TargetDestroy.ScoreDestroy = TargetDestroy.ScoreDestroy + 1 TargetDestroy.ScoreDestroy = TargetDestroy.ScoreDestroy + 1
if Player.HitPlayers[TargetPlayerName] then -- A player destroyed another player if Player.HitPlayers[TargetPlayerName] then -- A player destroyed another player
MESSAGE MESSAGE
:New( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' destroyed enemy player '" .. TargetPlayerName .. "' " .. :NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' destroyed enemy player '" .. TargetPlayerName .. "' " ..
TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " .. TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " ..
"Score: +" .. TargetDestroy.Score .. " = " .. Player.Score - Player.Penalty, "Score: +" .. TargetDestroy.Score .. " = " .. Player.Score - Player.Penalty,
15 MESSAGE.Type.Information
) )
:ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() ) :ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() )
:ToCoalitionIf( InitCoalition, self:IfMessagesDestroy() and self:IfMessagesToCoalition() ) :ToCoalitionIf( InitCoalition, self:IfMessagesDestroy() and self:IfMessagesToCoalition() )
else else
MESSAGE MESSAGE
:New( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' destroyed enemy " .. :NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' destroyed enemy " ..
TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " .. TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " ..
"Score: +" .. TargetDestroy.Score .. " = " .. Player.Score - Player.Penalty, "Score: +" .. TargetDestroy.Score .. " = " .. Player.Score - Player.Penalty,
15 MESSAGE.Type.Information
) )
:ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() ) :ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() )
:ToCoalitionIf( InitCoalition, self:IfMessagesDestroy() and self:IfMessagesToCoalition() ) :ToCoalitionIf( InitCoalition, self:IfMessagesDestroy() and self:IfMessagesToCoalition() )
@@ -1191,9 +1191,9 @@ function SCORING:_EventOnDeadOrCrash( Event )
Player.Score = Player.Score + Score Player.Score = Player.Score + Score
TargetDestroy.Score = TargetDestroy.Score + Score TargetDestroy.Score = TargetDestroy.Score + Score
MESSAGE 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, "Player '" .. PlayerName .. "' receives an extra " .. Score .. " points! Total: " .. Player.Score - Player.Penalty,
15 MESSAGE.Type.Information
) )
:ToAllIf( self:IfMessagesScore() and self:IfMessagesToAll() ) :ToAllIf( self:IfMessagesScore() and self:IfMessagesToAll() )
:ToCoalitionIf( InitCoalition, self:IfMessagesScore() and self:IfMessagesToCoalition() ) :ToCoalitionIf( InitCoalition, self:IfMessagesScore() and self:IfMessagesToCoalition() )
@@ -1210,10 +1210,10 @@ function SCORING:_EventOnDeadOrCrash( Event )
Player.Score = Player.Score + Score Player.Score = Player.Score + Score
TargetDestroy.Score = TargetDestroy.Score + Score TargetDestroy.Score = TargetDestroy.Score + Score
MESSAGE 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! " .. "Player '" .. PlayerName .. "' receives an extra " .. Score .. " points! " ..
"Total: " .. Player.Score - Player.Penalty, "Total: " .. Player.Score - Player.Penalty,
15 ) MESSAGE.Type.Information )
:ToAllIf( self:IfMessagesZone() and self:IfMessagesToAll() ) :ToAllIf( self:IfMessagesZone() and self:IfMessagesToAll() )
:ToCoalitionIf( InitCoalition, self:IfMessagesZone() and self:IfMessagesToCoalition() ) :ToCoalitionIf( InitCoalition, self:IfMessagesZone() and self:IfMessagesToCoalition() )
self:ScoreCSV( PlayerName, TargetPlayerName, "DESTROY_SCORE", 1, Score, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType ) self:ScoreCSV( PlayerName, TargetPlayerName, "DESTROY_SCORE", 1, Score, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType )
@@ -1232,10 +1232,10 @@ function SCORING:_EventOnDeadOrCrash( Event )
Player.Score = Player.Score + Score Player.Score = Player.Score + Score
TargetDestroy.Score = TargetDestroy.Score + Score TargetDestroy.Score = TargetDestroy.Score + Score
MESSAGE 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! " .. "Player '" .. PlayerName .. "' receives an extra " .. Score .. " points! " ..
"Total: " .. Player.Score - Player.Penalty, "Total: " .. Player.Score - Player.Penalty,
15 MESSAGE.Type.Information
) )
:ToAllIf( self:IfMessagesZone() and self:IfMessagesToAll() ) :ToAllIf( self:IfMessagesZone() and self:IfMessagesToAll() )
:ToCoalitionIf( InitCoalition, self:IfMessagesZone() and self:IfMessagesToCoalition() ) :ToCoalitionIf( InitCoalition, self:IfMessagesZone() and self:IfMessagesToCoalition() )
@@ -1522,7 +1522,7 @@ function SCORING:ReportScoreGroupSummary( PlayerGroup )
PlayerScore, PlayerScore,
PlayerPenalty PlayerPenalty
) )
MESSAGE:New( PlayerMessage, 30, "Player '" .. PlayerName .. "'" ):ToGroup( PlayerGroup ) MESSAGE:NewType( PlayerMessage, MESSAGE.Type.Detailed ):ToGroup( PlayerGroup )
end end
end end
@@ -1579,7 +1579,7 @@ function SCORING:ReportScoreGroupDetailed( PlayerGroup )
ReportGoals, ReportGoals,
ReportMissions ReportMissions
) )
MESSAGE:New( PlayerMessage, 30, "Player '" .. PlayerName .. "'" ):ToGroup( PlayerGroup ) MESSAGE:NewType( PlayerMessage, MESSAGE.Type.Detailed ):ToGroup( PlayerGroup )
end end
end end
@@ -1628,7 +1628,7 @@ function SCORING:ReportScoreAllSummary( PlayerGroup )
PlayerScore, PlayerScore,
PlayerPenalty PlayerPenalty
) )
MESSAGE:New( PlayerMessage, 30, "Player '" .. PlayerName .. "'" ):ToGroup( PlayerGroup ) MESSAGE:NewType( PlayerMessage, MESSAGE.Type.Overview ):ToGroup( PlayerGroup )
end end
end end

View File

@@ -1110,7 +1110,17 @@ function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude ) -- R2.2
SpawnTemplate.x = PointVec3.x SpawnTemplate.x = PointVec3.x
SpawnTemplate.y = PointVec3.z 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, { GroupSpawned, timer.getTime(), UnitSpawned:GetDCSObject() } , 1 )
end
end
return GroupSpawned
end end
end end

View File

@@ -888,6 +888,8 @@ end
-- @return #string -- @return #string
function MISSION:ReportOverview( ReportGroup, TaskStatus ) function MISSION:ReportOverview( ReportGroup, TaskStatus )
self:F( { TaskStatus = TaskStatus } )
local Report = REPORT:New() local Report = REPORT:New()
-- List the name of the mission. -- List the name of the mission.

View File

@@ -1589,8 +1589,9 @@ function TASK:ReportDetails( ReportGroup )
Report:Add( TaskInfoIDText .. TaskInfo.TaskInfoText ) Report:Add( TaskInfoIDText .. TaskInfo.TaskInfoText )
elseif type(TaskInfo) == "table" then elseif type(TaskInfo) == "table" then
if TaskInfoID == "Coordinates" then if TaskInfoID == "Coordinates" then
local FromCoordinate = ReportGroup:GetUnit(1):GetCoordinate()
local ToCoordinate = TaskInfo.TaskInfoText -- Core.Point#COORDINATE local ToCoordinate = TaskInfo.TaskInfoText -- Core.Point#COORDINATE
Report:Add( TaskInfoIDText .. ToCoordinate:ToString() ) Report:Add( TaskInfoIDText .. ToCoordinate:ToString( ReportGroup:GetUnit(1), nil, self ) )
else else
end end
end end

View File

@@ -366,7 +366,8 @@ do -- TASK_A2G_SEAD
end end
function TASK_A2G_SEAD:ReportOrder( ReportGroup ) function TASK_A2G_SEAD:ReportOrder( ReportGroup )
local Coordinate = self.TaskInfo.Coordinates.TaskInfoText local Coordinate = self:GetInfo( "Coordinates" )
--local Coordinate = self.TaskInfo.Coordinates.TaskInfoText
local Distance = ReportGroup:GetCoordinate():Get2DDistance( Coordinate ) local Distance = ReportGroup:GetCoordinate():Get2DDistance( Coordinate )
return Distance return Distance
@@ -515,7 +516,8 @@ do -- TASK_A2G_BAI
function TASK_A2G_BAI:ReportOrder( ReportGroup ) function TASK_A2G_BAI:ReportOrder( ReportGroup )
local Coordinate = self.TaskInfo.Coordinates.TaskInfoText local Coordinate = self:GetInfo( "Coordinates" )
--local Coordinate = self.TaskInfo.Coordinates.TaskInfoText
local Distance = ReportGroup:GetCoordinate():Get2DDistance( Coordinate ) local Distance = ReportGroup:GetCoordinate():Get2DDistance( Coordinate )
return Distance return Distance
@@ -630,7 +632,7 @@ do -- TASK_A2G_CAS
function TASK_A2G_CAS:UpdateTaskInfo() function TASK_A2G_CAS:UpdateTaskInfo()
local TargetCoordinate = self.Detection and self.Detection:GetDetectedItemCoordinate( self.DetectedItemIndex ) or self.TargetSetUnit:GetFirst():GetCoordinate() local TargetCoordinate = ( self.Detection and self.Detection:GetDetectedItemCoordinate( self.DetectedItemIndex ) ) or self.TargetSetUnit:GetFirst():GetCoordinate()
self:SetInfo( "Coordinates", TargetCoordinate, 0 ) self:SetInfo( "Coordinates", TargetCoordinate, 0 )
local ThreatLevel, ThreatText local ThreatLevel, ThreatText
@@ -661,8 +663,10 @@ do -- TASK_A2G_CAS
end end
--- @param #TASK_A2G_CAS self
function TASK_A2G_CAS:ReportOrder( ReportGroup ) function TASK_A2G_CAS:ReportOrder( ReportGroup )
local Coordinate = self.TaskInfo.Coordinates.TaskInfoText
local Coordinate = self:GetInfo( "Coordinates" )
local Distance = ReportGroup:GetCoordinate():Get2DDistance( Coordinate ) local Distance = ReportGroup:GetCoordinate():Get2DDistance( Coordinate )
return Distance return Distance

View File

@@ -372,6 +372,7 @@ do -- TASK_A2G_DISPATCHER
self.Tasks[TaskIndex] = Task self.Tasks[TaskIndex] = Task
Task:SetTargetZone( DetectedZone ) Task:SetTargetZone( DetectedZone )
Task:SetDispatcher( self ) Task:SetDispatcher( self )
Task:UpdateTaskInfo()
Mission:AddTask( Task ) Mission:AddTask( Task )
TaskReport:Add( Task:GetName() ) TaskReport:Add( Task:GetName() )

View File

@@ -1161,9 +1161,9 @@ do -- Event Handling
-- @param Core.Event#EVENTS Event -- @param Core.Event#EVENTS Event
-- @param #function EventFunction (optional) The function to be called when the event occurs for the GROUP. -- @param #function EventFunction (optional) The function to be called when the event occurs for the GROUP.
-- @return #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 return self
end end

View File

@@ -635,7 +635,7 @@ function POSITIONABLE:MessageToSetGroup( Message, Duration, MessageSetGroup, Nam
if DCSObject:isExist() then if DCSObject:isExist() then
MessageSetGroup:ForEachGroup( MessageSetGroup:ForEachGroup(
function( MessageGroup ) function( MessageGroup )
self:GetMessageType( Message, Duration, Name ):ToGroup( MessageGroup ) self:GetMessage( Message, Duration, Name ):ToGroup( MessageGroup )
end end
) )
end end

565
Utils/DCS_ControlAPI.txt Normal file
View File

@@ -0,0 +1,565 @@
DCS Simulation Control User Scripts
====================================
The behaviour of the DCS can be altered using the *GameGUI.lua scripts.
You define the hooks to the DCS events, and then do what you want using the provided API.
===================================================================================================
When loading, DCS searches for Saved Games\DCS\Scripts\*GameGUI.lua files,
sorts them by name and then loads into the GUI Lua-state.
Each user script is loaded into an isolated environment, so the only
thing they share is the state of the simulator.
Each script defines a set of callbacks to the DCS events and sets them with the call
DCS.setUserCallbacks(cb_table)
For each callback type the hooks of all user scripts will be called in order of loading.
For callbacks which are supposed to returning a value, currently there are 3 of them:
onPlayerTryConnect
onPlayerTrySendChat
onPlayerTryChangeSlot
returning a value means breaking the hook call chain.
Returning nothing (or nil) means continuing the hook chain, which ends with the default allow-all handlers.
The example user script 'testGameGUI.lua':
----------------------------------------------------------------------------------------------
local test = {}
function test.onPlayerTryConnect(ipaddr, name, ucid, playerID)
print('onPlayerTryConnect(%s, %s, %s, %d)', ipaddr, name, ucid, playerID)
-- if you want to gently intercept the call, allowing other user scripts to get it,
-- you better return nothing here
return true -- allow the player to connect
end
function test.onSimulationStart()
print('Current mission is '..DCS.getMissionName())
end
DCS.setUserCallbacks(test) -- here we set our callbacks
----------------------------------------------------------------------------------------------
The available API are documented below.
The full list of the callbacks is at the end of this document.
In addition, all standard lua 5.1 libraries are available as well, namely:
base api, like print, etc,
math.*
table.*
string.*
io.*
os.*
debug.*
===================================================================================================
Lua File System (lfs) API
-------------------------------
lfs.currentdir() -> string
Returns the path of the DCS install folder
lfs.writedir() -> string
Returns the path of the current 'Saved Games\DCS' folder.
lfs.tempdir() -> string
Returns the pat of the DCS Temp folder (AppData\Local\Temp\DCS).
lfs.mkdir()
lfs.rmdir()
lfs.attributes()
lfs.dir()
lfs.normpath()
lfs.realpath()
DCS Control API, table 'DCS.*'
-------------------------------
DCS.setPause(bool)
Pauses/resumes the simulation. Server-side only.
DCS.getPause() -> bool
true if simulation is paused
DCS.stopMission()
stops current mission
DCS.exitProcess()
Exits the DCS process.
DCS.isMultiplayer() -> bool
True when running in the multiplayer mode.
DCS.isServer() -> bool
True when running as a server or in the single-player mode.
DCS.getModelTime() -> number
returns current DCS simulation time in seconds.
DCS.getRealTime() -> number
returns current DCS real time in seconds relative to the DCS start time.
DCS.getMissionOptions() -> table
Returns the value of 'mission.options'
DCS.getMissionDescription() -> string
translated mission.descriptionText string
DCS.getAvailableCoalitions() -> table {
[coalition_id] = { name = "coalition name", }
...
}
Returns a list of coalitions which have available slots.
DCS.getAvailableSlots(coalitionID) -> array of {unitId, type, role, callsign, groupName, country}
Returns the list of available slots.
NOTE: the returned unitID is actually a slotID, which for multi-seat units is 'unitID_seatID'
DCS.getCurrentMission() -> table with the currently loaded mission
NOTE: to get valid mission.options use DCS.getMissionOptions()
DCS.getMissionName() -> string
Returns the name of the current mission
DCS.getMissionFilename() -> string
Returns the file name of the current mission (returns nil when acting as a multiplayer client).
DCS.getMissionResult(string side) -> integer [0, 100]
Gets missin result for either 'red' or 'blue'
DCS.getUnitProperty(missionId, propertyId) -> string
propertyId:
DCS.UNIT_RUNTIME_ID, // unique within runtime mission. int
DCS.UNIT_MISSION_ID, // unique within mission file. int>0
DCS.UNIT_NAME, // unit name, as assigned by mission designer.
DCS.UNIT_TYPE, // unit type (Ural, ZU-23, etc)
DCS.UNIT_CATEGORY,
DCS.UNIT_GROUP_MISSION_ID, // group ID, unique within mission file. int>0
DCS.UNIT_GROUPNAME, // group name, as assigned by mission designer.
DCS.UNIT_GROUPCATEGORY,
DCS.UNIT_CALLSIGN,
DCS.UNIT_HIDDEN,// ME hiding
DCS.UNIT_COALITION,// "blue", "red" or "unknown"
DCS.UNIT_COUNTRY_ID,
DCS.UNIT_TASK, //"unit.group.task"
DCS.UNIT_PLAYER_NAME, // valid for network "humanable" units
DCS.UNIT_ROLE,//"artillery_commander", "instructor", etc
DCS.UNIT_INVISIBLE_MAP_ICON,//ME invisible map icon
DCS.getUnitType(missionId) -> typeId
a shortcut for DCS.getUnitProperty(missionId, DCS.UNIT_TYPE)
DCS.getUnitTypeAttribute(typeId, attr) -> string
Returns a value from Database: Objects[typeId][attr],
for example DCS.getUnitTypeAttribute("Ural", "DisplayName")
DCS.writeDebriefing(str)
Writes a custom string to the debriefing file
DCS.setUserCallbacks(cb_table)
Hooks the callbacks using the handlers from the provided table.
See: "GameGUI scripts" section.
Logging API 'log.*'
------------------------
Logging works as follows:
a) each log message is accompanied with 2 attributes: a subsystem, and level.
b) after each messages gets into a logger it passes (asynchronously) through
a series of output filters which decide where the message will be written to.
Writing to log is done by:
log.write(SUBSYSTEM_NAME, LOG_LEVEL, message, ...)
if there are any arguments after 'message',
the actual string is formed as string.format(message, ...)
SUBSYSTEM_NAME is a string
LOG_LEVEL is one of the values, listed below
see log.set_output()
log.set_output(log_file_name_wo_ext, rule_subsystem_name, rule_level_mask, rule_output_mode)
the args:
log_file_name_wo_ext: resulting log will be written to $WRITE_DIR/Logs/<log_file_name_wo_ext>.log
rule_subsytem_name: the name of the subsystem whose messages to write or empty string to match all subsystems
rule_level_mask: a sum of log-level bit flags to match messages
valid flags are:
log.ALERT
log.ERROR
log.WARNING
log.INFO
log.DEBUG
log.ALL - includes all of the above
log.TRACE - a special level which is excluded from dcs.log file
rule_output_mode: a sum of output flags:
log.MESSAGE
log.TIME
log.MODULE - this is a 'subsystem', not a dlc
log.LEVEL
log.FULL - all of the above
So, in order to save net.trace(msg) messages to a file, you should issue a call:
log.set_output('lua-net', 'LuaNET', log.TRACE, log.MESSAGE + log.TIME)
This will write to a Logs/lua-net.log file
Or, to save everything lua-network-related:
log.set_output('lua-net', 'LuaNET', log.TRACE + log.ALL, log.MESSAGE + log.TIME + log.LEVEL)
To close the log file, you must use
log.set_output('lua-net', '', 0, 0)
log.* API is available in the 'Saved Games\DCS\Config\autoexec.cfg' file as well so you can control log output in you local machine.
Network specific API, available through the table 'net.'
----------------------------------------------------------------
net.log(msg) -- equivalent to log.write('LuaNET', log.INFO, msg)
net.trace(msg) -- equivalent to log.write('LuaNET', log.TRACE, msg)
What is the difference: log() always writes to dcs.log, but may lose messages if the output rate is too high.
trace() output never appears in the dcs.log file, it must be explicitly directed to a log file.
It never loses messages when there's an active output, but it may block if output rate is higher than writing to the log file.
To control logger output you can use $WRITE_DIR/Config/autoexec.cfg file, or call this from your network script
(log.* API, see above)
net.dostring_in(state, string) -> string
Executes a lua-string in a given internal lua-state and returns a string result
Valid state names are:
'config': the state in which $INSTALL_DIR/Config/main.cfg is executed, as well as $WRITE_DIR/Config/autoexec.cfg
used for configuration settings
'mission': holds current mission
'export': runs $WRITE_DIR/Scripts/Export.lua and the relevant export API
net.send_chat(string message, bool all)
Send chat message. If not all, then send to my coalition (side) only.
net.send_chat_to(string message, playerID to)
Send direct chat message to a player
Server-side only:
net.send_chat_to(string message, playerID to[, playerID from])
net.recv_chat(message[, int from=0])
Receive chat message locally[, pretending it was sent by another player].
from = 0 means from the system
net.load_mission(miz_filename)
Loads a specified mission, temporarily overriding the server mission list.
SERVER ONLY
net.load_next_mission() -> bool
Load the next mission from the server mission list. Returns false if list end is reached
SERVER ONLY
net.get_player_list() -> array of playerID
Returns the list of currently connected players
net.get_my_player_id() -> playerID
Returns the playerID of the local player. Currently always 1 for the server.
net.get_server_id() -> playerID
Returns playerID of the server. Currently, always 1.
net.get_player_info(playerID) -> table
Returns a table of all player attributes or nil if playerID is invalid
net.get_player_info(playerID, attrName) -> value
Returns a value of a given attribute for the playerID.
Currently defined attributes are:
'id': playerID
'name': player name
'side': 0 - spectators, 1 - red, 2 - blue
'slot': slotID of the player or ''
'ping': ping of the player in ms
'ipaddr': IP address of the player, SERVER ONLY
'ucid': Unique Client Identifier, SERVER ONLY
net.kick(id, message)
Kick a player.
net.get_stat(playerID, statID) -> integer
Get statistics for player. statIDs are:
net.PS_PING (0) - ping (in ms)
net.PS_CRASH (1) - number of crashes
net.PS_CAR (2) - number of destroyed vehicles
net.PS_PLANE (3) - ... planes/helicopters
net.PS_SHIP (4) - ... ships
net.PS_SCORE (5) - total score
net.PS_LAND (6) - number of landings
net.PS_EJECT (7) - of ejects
net.get_name(playerID) -> string
The same as net.get_player_info(playerID, 'name')
FIXME: implement in ServMan_compat.lua ?
net.get_slot(playerID) -> sideID, slotID
The same as:
net.get_player_info(playerID, 'side'), net.get_player_info(playerID, 'slot')
FIXME: implement in ServMan_compat.lua ?
net.set_slot(sideID, slotID)
Try to set the local player's slot. Empty slotID ('') puts the player into spectators.
net.force_player_slot(playerID, sideID, slotID) -> boolean
Forces a player to occupy a set slot. Slot '' means no slot (moves player to spectators)
SideID: 0 - spectators, 1 - red, 2 - blue
net.set_name(playerID, name) -- OBSOLETE, works only locally
net.lua2json(value) -> string
Convert a Lua value to JSON string
net.json2lua(json_string) -> value
Convert JSON string to a Lua value
LuaExport API 'Export.Lo*'
----------------------------------------------------------------
See Scripts/Export.lua for the documentation. Note that all export
API functions are available here in the Export. namespace, not the global one.
In multiplayer the availability of the API on clients depends on the server setting.
The calls to check export capabilities:
Export.LoIsObjectExportAllowed() -- returns the value of server.advanced.allow_object_export
Export.LoIsSensorExportAllowed() -- returns the value of server.advanced.allow_sensor_export
Export.LoIsOwnshipExportAllowed() -- returns the value of server.advanced.allow_ownship_export
These calls are only available on clients when LoIsObjectExportAllowed() is true:
Export.LoGetObjectById
Export.LoGetWorldObjects
These calls are only available on clients when LoIsSensorExportAllowed() is true:
Export.LoGetTWSInfo
Export.LoGetTargetInformation
Export.LoGetLockedTargetInformation
Export.LoGetF15_TWS_Contacts
Export.LoGetSightingSystemInfo
Export.LoGetWingTargets
These calls are only available on clients when LoIsOwnshipExportAllowed() is true:
Export.LoGetPlayerPlaneId
Export.LoGetIndicatedAirSpeed
Export.LoGetAngleOfAttack
Export.LoGetAngleOfSideSlip
Export.LoGetAccelerationUnits
Export.LoGetVerticalVelocity
Export.LoGetADIPitchBankYaw
Export.LoGetTrueAirSpeed
Export.LoGetAltitudeAboveSeaLevel
Export.LoGetAltitudeAboveGroundLevel
Export.LoGetMachNumber
Export.LoGetRadarAltimeter
Export.LoGetMagneticYaw
Export.LoGetGlideDeviation
Export.LoGetSideDeviation
Export.LoGetSlipBallPosition
Export.LoGetBasicAtmospherePressure
Export.LoGetControlPanel_HSI
Export.LoGetEngineInfo
Export.LoGetSelfData
Export.LoGetCameraPosition
Export.LoSetCameraPosition
Export.LoSetCommand
Export.LoGetMCPState
Export.LoGetRoute
Export.LoGetNavigationInfo
Export.LoGetPayloadInfo
Export.LoGetWingInfo
Export.LoGetMechInfo
Export.LoGetRadioBeaconsStatus
Export.LoGetVectorVelocity
Export.LoGetVectorWindVelocity
Export.LoGetSnares
Export.LoGetAngularVelocity
Export.LoGetHeightWithObjects
Export.LoGetFMData
These functions are always available:
Export.LoGetPilotName
Export.LoGetAltitude
Export.LoGetNameByType
Export.LoGeoCoordinatesToLoCoordinates
Export.LoCoordinatesToGeoCoordinates
Export.LoGetVersionInfo
Export.LoGetWindAtPoint
Export.LoGetModelTime
Export.LoGetMissionStartTime
These are not available in the *GameGUI state:
-- Export.LoSetSharedTexture
-- Export.LoRemoveSharedTexture
-- Export.LoUpdateSharedTexture
-------------------------------------------------------------------------------------------
--- The Callbacks.
-------------------------------------------------------------------------------------------
function onMissionLoadBegin()
end
function onMissionLoadProgress(progress, message)
end
function onMissionLoadEnd()
end
function onSimulationStart()
end
function onSimulationStop()
end
function onSimulationFrame()
end
function onSimulationPause()
end
function onSimulationResume()
end
function onGameEvent(eventName,arg1,arg2,arg3,arg4)
--"friendly_fire", playerID, weaponName, victimPlayerID
--"mission_end", winner, msg
--"kill", killerPlayerID, killerUnitType, killerSide, victimPlayerID, victimUnitType, victimSide, weaponName
--"self_kill", playerID
--"change_slot", playerID, slotID, prevSide
--"connect", playerID, name
--"disconnect", playerID, name, playerSide, reason_code
--"crash", playerID, unit_missionID
--"eject", playerID, unit_missionID
--"takeoff", playerID, unit_missionID, airdromeName
--"landing", playerID, unit_missionID, airdromeName
--"pilot_death", playerID, unit_missionID
end
function onNetConnect(localPlayerID)
end
function onNetMissionChanged(newMissionName)
end
function onNetDisconnect(reason_msg, err_code)
end
-- disconnect reason codes:
net.ERR_INVALID_ADDRESS
net.ERR_CONNECT_FAILED
net.ERR_WRONG_VERSION
net.ERR_PROTOCOL_ERROR
net.ERR_TAINTED_CLIENT
net.ERR_INVALID_PASSWORD
net.ERR_BANNED
net.ERR_BAD_CALLSIGN
net.ERR_TIMEOUT
net.ERR_KICKED
function onPlayerConnect(id)
end
function onPlayerDisconnect(id, err_code)
-- this is never called for local playerID
end
function onPlayerStart(id)
-- a player entered the simulation
-- this is never called for local playerID
end
function onPlayerStop(id)
-- a player left the simulation (happens right before a disconnect, if player exited by desire)
-- this is never called for local playerID
end
function onPlayerChangeSlot(id)
-- a player successfully changed the slot
-- this will also come as onGameEvent('change_slot', playerID, slotID),
-- if allowed by server.advanced.event_Connect setting
end
--- These 3 functions are different from the rest:
--- 1. they are called directly from the network code, so try to make them as fast as possible
--- 2. they return a result
-- The code shows the default implementations.
function onPlayerTryConnect(addr, name, ucid, playerID) --> true | false, "disconnect reason"
return true
end
function onPlayerTrySendChat(playerID, msg, all) -- -> filteredMessage | "" - empty string drops the message
return msg
end
function onPlayerTryChangeSlot(playerID, side, slotID) -- -> true | false
return true
end
-- GUI callbacks
function onChatMessage(message, from)
-- this one may be useful for chat archiving
end
function onShowRadioMenu(a_h)
end
function onShowPool()
end
function onShowGameMenu()
end
function onShowBriefing()
end
function onShowChatAll()
end
function onShowChatTeam()
end
function onShowChatRead()
end
function onShowMessage(a_text, a_duration)
end
function onTriggerMessage(message, duration, clearView)
end
function onRadioMessage(message, duration)
end
function onRadioCommand(command_message)
end
===================================================================================================
Happy hacking!
Sincerely,
dsb at eagle dot ru

View File

@@ -1151,6 +1151,7 @@ function below will use the range 1-7 just in case</p>
<dl class="function"> <dl class="function">
<dt> <dt>
<em></em>
<a id="#(DESIGNATE).LaserCodes" > <a id="#(DESIGNATE).LaserCodes" >
<strong>DESIGNATE.LaserCodes</strong> <strong>DESIGNATE.LaserCodes</strong>
</a> </a>

View File

@@ -2460,7 +2460,6 @@ The index of the DetectedItem.</p>
<dl class="function"> <dl class="function">
<dt> <dt>
<em>#number</em>
<a id="#(DETECTION_BASE).DetectedItemCount" > <a id="#(DETECTION_BASE).DetectedItemCount" >
<strong>DETECTION_BASE.DetectedItemCount</strong> <strong>DETECTION_BASE.DetectedItemCount</strong>
</a> </a>
@@ -2474,7 +2473,6 @@ The index of the DetectedItem.</p>
<dl class="function"> <dl class="function">
<dt> <dt>
<em>#number</em>
<a id="#(DETECTION_BASE).DetectedItemMax" > <a id="#(DETECTION_BASE).DetectedItemMax" >
<strong>DETECTION_BASE.DetectedItemMax</strong> <strong>DETECTION_BASE.DetectedItemMax</strong>
</a> </a>
@@ -4052,7 +4050,7 @@ Return false to cancel Transition.</p>
<dl class="function"> <dl class="function">
<dt> <dt>
<em>#number</em> <em></em>
<a id="#(DETECTION_BASE).RefreshTimeInterval" > <a id="#(DETECTION_BASE).RefreshTimeInterval" >
<strong>DETECTION_BASE.RefreshTimeInterval</strong> <strong>DETECTION_BASE.RefreshTimeInterval</strong>
</a> </a>

View File

@@ -1599,7 +1599,7 @@ A string defining the start state.</p>
<dl class="function"> <dl class="function">
<dt> <dt>
<em></em> <em>#string</em>
<a id="#(FSM)._StartState" > <a id="#(FSM)._StartState" >
<strong>FSM._StartState</strong> <strong>FSM._StartState</strong>
</a> </a>
@@ -1898,7 +1898,6 @@ A string defining the start state.</p>
<dl class="function"> <dl class="function">
<dt> <dt>
<em></em>
<a id="#(FSM).current" > <a id="#(FSM).current" >
<strong>FSM.current</strong> <strong>FSM.current</strong>
</a> </a>

View File

@@ -228,7 +228,6 @@ on defined intervals (currently every minute).</p>
<dl class="function"> <dl class="function">
<dt> <dt>
<em>#number</em>
<a id="#(MOVEMENT).AliveUnits" > <a id="#(MOVEMENT).AliveUnits" >
<strong>MOVEMENT.AliveUnits</strong> <strong>MOVEMENT.AliveUnits</strong>
</a> </a>
@@ -237,9 +236,6 @@ on defined intervals (currently every minute).</p>
<p> Contains the counter how many units are currently alive</p>
</dd> </dd>
</dl> </dl>
<dl class="function"> <dl class="function">

View File

@@ -1974,7 +1974,6 @@ The height in meters to add to the altitude of the positionable.</p>
<dl class="function"> <dl class="function">
<dt> <dt>
<em><a href="Core.Spot.html##(SPOT)">Core.Spot#SPOT</a></em>
<a id="#(POSITIONABLE).Spot" > <a id="#(POSITIONABLE).Spot" >
<strong>POSITIONABLE.Spot</strong> <strong>POSITIONABLE.Spot</strong>
</a> </a>

View File

@@ -1241,7 +1241,7 @@ true if metric.</p>
<dl class="function"> <dl class="function">
<dt> <dt>
<em>#boolean</em> <em></em>
<a id="#(SETTINGS).Metric" > <a id="#(SETTINGS).Metric" >
<strong>SETTINGS.Metric</strong> <strong>SETTINGS.Metric</strong>
</a> </a>

View File

@@ -2192,6 +2192,9 @@ The group that was spawned. You can use this group for further actions.</p>
<p> Don't repeat the group from Take-Off till Landing and back Take-Off by ReSpawning.</p>
</dd> </dd>
</dl> </dl>
<dl class="function"> <dl class="function">
@@ -2780,6 +2783,9 @@ when nothing was spawned.</p>
<p> By default, no InitLimit</p>
</dd> </dd>
</dl> </dl>
<dl class="function"> <dl class="function">
@@ -2815,7 +2821,7 @@ when nothing was spawned.</p>
<dl class="function"> <dl class="function">
<dt> <dt>
<em></em> <em>#number</em>
<a id="#(SPAWN).SpawnMaxGroups" > <a id="#(SPAWN).SpawnMaxGroups" >
<strong>SPAWN.SpawnMaxGroups</strong> <strong>SPAWN.SpawnMaxGroups</strong>
</a> </a>
@@ -2832,7 +2838,7 @@ when nothing was spawned.</p>
<dl class="function"> <dl class="function">
<dt> <dt>
<em></em> <em>#number</em>
<a id="#(SPAWN).SpawnMaxUnitsAlive" > <a id="#(SPAWN).SpawnMaxUnitsAlive" >
<strong>SPAWN.SpawnMaxUnitsAlive</strong> <strong>SPAWN.SpawnMaxUnitsAlive</strong>
</a> </a>
@@ -3160,7 +3166,7 @@ Spawn_BE_KA50 = SPAWN:New( 'BE KA-50@RAMP-Ground Defense' ):Schedule( 600, 0.5 )
<dl class="function"> <dl class="function">
<dt> <dt>
<em></em> <em>#boolean</em>
<a id="#(SPAWN).SpawnUnControlled" > <a id="#(SPAWN).SpawnUnControlled" >
<strong>SPAWN.SpawnUnControlled</strong> <strong>SPAWN.SpawnUnControlled</strong>
</a> </a>
@@ -3184,7 +3190,7 @@ Spawn_BE_KA50 = SPAWN:New( 'BE KA-50@RAMP-Ground Defense' ):Schedule( 600, 0.5 )
<p> When the first Spawn executes, all the Groups need to be made visible before start.</p> <p> Flag that indicates if all the Groups of the SpawnGroup need to be visible when Spawned.</p>
</dd> </dd>
</dl> </dl>

View File

@@ -437,6 +437,7 @@ ptional) The name of the new static.</p>
<dl class="function"> <dl class="function">
<dt> <dt>
<em>#number</em>
<a id="#(SPAWNSTATIC).SpawnIndex" > <a id="#(SPAWNSTATIC).SpawnIndex" >
<strong>SPAWNSTATIC.SpawnIndex</strong> <strong>SPAWNSTATIC.SpawnIndex</strong>
</a> </a>

View File

@@ -553,7 +553,7 @@ based on the tasking capabilities defined in <a href="Task.html##(TASK)">Task#TA
<dl class="function"> <dl class="function">
<dt> <dt>
<em><a href="Core.Cargo.html##(CARGO_GROUP)">Core.Cargo#CARGO_GROUP</a></em> <em></em>
<a id="#(FSM_PROCESS).Cargo" > <a id="#(FSM_PROCESS).Cargo" >
<strong>FSM_PROCESS.Cargo</strong> <strong>FSM_PROCESS.Cargo</strong>
</a> </a>
@@ -567,7 +567,6 @@ based on the tasking capabilities defined in <a href="Task.html##(TASK)">Task#TA
<dl class="function"> <dl class="function">
<dt> <dt>
<em></em>
<a id="#(FSM_PROCESS).DeployZone" > <a id="#(FSM_PROCESS).DeployZone" >
<strong>FSM_PROCESS.DeployZone</strong> <strong>FSM_PROCESS.DeployZone</strong>
</a> </a>