mirror of
https://github.com/FlightControl-Master/MOOSE.git
synced 2025-08-15 10:47:21 +00:00
1011 lines
40 KiB
Lua
1011 lines
40 KiB
Lua
--- **Core** - EVENT models DCS **event dispatching** using a **publish-subscribe** model.
|
||
--
|
||
-- 
|
||
--
|
||
-- ===
|
||
--
|
||
-- # 1) Event Handling Overview
|
||
--
|
||
-- 
|
||
--
|
||
-- Within a running mission, various DCS events occur. Units are dynamically created, crash, die, shoot stuff, get hit etc.
|
||
-- This module provides a mechanism to dispatch those events occuring within your running mission, to the different objects orchestrating your mission.
|
||
--
|
||
-- 
|
||
--
|
||
-- Objects can subscribe to different events. The Event dispatcher will publish the received DCS events to the subscribed MOOSE objects, in a specified order.
|
||
-- In this way, the subscribed MOOSE objects are kept in sync with your evolving running mission.
|
||
--
|
||
-- ## 1.1) Event Dispatching
|
||
--
|
||
-- 
|
||
--
|
||
-- The _EVENTDISPATCHER object is automatically created within MOOSE,
|
||
-- and handles the dispatching of DCS Events occurring
|
||
-- in the simulator to the subscribed objects
|
||
-- in the correct processing order.
|
||
--
|
||
-- 
|
||
--
|
||
-- There are 5 levels of kind of objects that the _EVENTDISPATCHER services:
|
||
--
|
||
-- * _DATABASE object: The core of the MOOSE objects. Any object that is created, deleted or updated, is done in this database.
|
||
-- * SET_ derived classes: Subsets of the _DATABASE object. These subsets are updated by the _EVENTDISPATCHER as the second priority.
|
||
-- * UNIT objects: UNIT objects can subscribe to DCS events. Each DCS event will be directly published to teh subscribed UNIT object.
|
||
-- * GROUP objects: GROUP objects can subscribe to DCS events. Each DCS event will be directly published to the subscribed GROUP object.
|
||
-- * Any other object: Various other objects can subscribe to DCS events. Each DCS event triggered will be published to each subscribed object.
|
||
--
|
||
-- 
|
||
--
|
||
-- For most DCS events, the above order of updating will be followed.
|
||
--
|
||
-- 
|
||
--
|
||
-- But for some DCS events, the publishing order is reversed. This is due to the fact that objects need to be **erased** instead of added.
|
||
--
|
||
-- ## 1.2) Event Handling
|
||
--
|
||
-- 
|
||
--
|
||
-- The actual event subscribing and handling is not facilitated through the _EVENTDISPATCHER, but it is done through the @{BASE} class, @{UNIT} class and @{GROUP} class.
|
||
-- The _EVENTDISPATCHER is a component that is quietly working in the background of MOOSE.
|
||
--
|
||
-- 
|
||
--
|
||
-- The BASE class provides methods to catch DCS Events. These are events that are triggered from within the DCS simulator,
|
||
-- and handled through lua scripting. MOOSE provides an encapsulation to handle these events more efficiently.
|
||
--
|
||
-- ### 1.2.1 Subscribe / Unsubscribe to DCS Events
|
||
--
|
||
-- At first, the mission designer will need to **Subscribe** to a specific DCS event for the class.
|
||
-- So, when the DCS event occurs, the class will be notified of that event.
|
||
-- There are two functions which you use to subscribe to or unsubscribe from an event.
|
||
--
|
||
-- * @{Base#BASE.HandleEvent}(): Subscribe to a DCS Event.
|
||
-- * @{Base#BASE.UnHandleEvent}(): Unsubscribe from a DCS Event.
|
||
--
|
||
-- Note that for a UNIT, the event will be handled **for that UNIT only**!
|
||
-- Note that for a GROUP, the event will be handled **for all the UNITs in that GROUP only**!
|
||
--
|
||
-- For all objects of other classes, the subscribed events will be handled for **all UNITs within the Mission**!
|
||
-- So if a UNIT within the mission has the subscribed event for that object,
|
||
-- then the object event handler will receive the event for that UNIT!
|
||
--
|
||
-- ### 1.3.2 Event Handling of DCS Events
|
||
--
|
||
-- Once the class is subscribed to the event, an **Event Handling** method on the object or class needs to be written that will be called
|
||
-- when the DCS event occurs. The Event Handling method receives an @{Event#EVENTDATA} structure, which contains a lot of information
|
||
-- about the event that occurred.
|
||
--
|
||
-- Find below an example of the prototype how to write an event handling function for two units:
|
||
--
|
||
-- local Tank1 = UNIT:FindByName( "Tank A" )
|
||
-- local Tank2 = UNIT:FindByName( "Tank B" )
|
||
--
|
||
-- -- Here we subscribe to the Dead events. So, if one of these tanks dies, the Tank1 or Tank2 objects will be notified.
|
||
-- Tank1:HandleEvent( EVENTS.Dead )
|
||
-- Tank2:HandleEvent( EVENTS.Dead )
|
||
--
|
||
-- --- This function is an Event Handling function that will be called when Tank1 is Dead.
|
||
-- -- @param Wrapper.Unit#UNIT self
|
||
-- -- @param Core.Event#EVENTDATA EventData
|
||
-- function Tank1:OnEventDead( EventData )
|
||
--
|
||
-- self:SmokeGreen()
|
||
-- end
|
||
--
|
||
-- --- This function is an Event Handling function that will be called when Tank2 is Dead.
|
||
-- -- @param Wrapper.Unit#UNIT self
|
||
-- -- @param Core.Event#EVENTDATA EventData
|
||
-- function Tank2:OnEventDead( EventData )
|
||
--
|
||
-- self:SmokeBlue()
|
||
-- end
|
||
--
|
||
-- ### 1.3.3 Event Handling methods that are automatically called upon subscribed DCS events
|
||
--
|
||
-- 
|
||
--
|
||
-- The following list outlines which EVENTS item in the structure corresponds to which Event Handling method.
|
||
-- Always ensure that your event handling methods align with the events being subscribed to, or nothing will be executed.
|
||
--
|
||
-- # 2) EVENTS type
|
||
--
|
||
-- The EVENTS structure contains names for all the different DCS events that objects can subscribe to using the
|
||
-- @{Base#BASE.HandleEvent}() method.
|
||
--
|
||
-- # 3) EVENTDATA type
|
||
--
|
||
-- The @{Event#EVENTDATA} structure contains all the fields that are populated with event information before
|
||
-- an Event Handler method is being called by the event dispatcher.
|
||
-- The Event Handler received the EVENTDATA object as a parameter, and can be used to investigate further the different events.
|
||
-- There are basically 4 main categories of information stored in the EVENTDATA structure:
|
||
--
|
||
-- * Initiator Unit data: Several fields documenting the initiator unit related to the event.
|
||
-- * Target Unit data: Several fields documenting the target unit related to the event.
|
||
-- * Weapon data: Certain events populate weapon information.
|
||
-- * Place data: Certain events populate place information.
|
||
--
|
||
-- --- This function is an Event Handling function that will be called when Tank1 is Dead.
|
||
-- -- EventData is an EVENTDATA structure.
|
||
-- -- We use the EventData.IniUnit to smoke the tank Green.
|
||
-- -- @param Wrapper.Unit#UNIT self
|
||
-- -- @param Core.Event#EVENTDATA EventData
|
||
-- function Tank1:OnEventDead( EventData )
|
||
--
|
||
-- EventData.IniUnit:SmokeGreen()
|
||
-- end
|
||
--
|
||
--
|
||
-- Find below an overview which events populate which information categories:
|
||
--
|
||
-- 
|
||
--
|
||
-- **IMPORTANT NOTE:** Some events can involve not just UNIT objects, but also STATIC objects!!!
|
||
-- In that case the initiator or target unit fields will refer to a STATIC object!
|
||
-- In case a STATIC object is involved, the documentation indicates which fields will and won't not be populated.
|
||
-- The fields **IniObjectCategory** and **TgtObjectCategory** contain the indicator which **kind of object is involved** in the event.
|
||
-- You can use the enumerator **Object.Category.UNIT** and **Object.Category.STATIC** to check on IniObjectCategory and TgtObjectCategory.
|
||
-- Example code snippet:
|
||
--
|
||
-- if Event.IniObjectCategory == Object.Category.UNIT then
|
||
-- ...
|
||
-- end
|
||
-- if Event.IniObjectCategory == Object.Category.STATIC then
|
||
-- ...
|
||
-- end
|
||
--
|
||
-- When a static object is involved in the event, the Group and Player fields won't be populated.
|
||
--
|
||
-- ====
|
||
--
|
||
-- # **API CHANGE HISTORY**
|
||
--
|
||
-- The underlying change log documents the API changes. Please read this carefully. The following notation is used:
|
||
--
|
||
-- * **Added** parts are expressed in bold type face.
|
||
-- * _Removed_ parts are expressed in italic type face.
|
||
--
|
||
-- YYYY-MM-DD: CLASS:**NewFunction**( Params ) replaces CLASS:_OldFunction_( Params )
|
||
-- YYYY-MM-DD: CLASS:**NewFunction( Params )** added
|
||
--
|
||
-- Hereby the change log:
|
||
--
|
||
-- * 2017-03-07: Added the correct event dispatching in case the event is subscribed by a GROUP.
|
||
--
|
||
-- * 2017-02-07: Did a complete revision of the Event Handing API and underlying mechanisms.
|
||
--
|
||
-- ===
|
||
--
|
||
-- # **AUTHORS and CONTRIBUTIONS**
|
||
--
|
||
-- ### Contributions:
|
||
--
|
||
-- ### Authors:
|
||
--
|
||
-- * [**FlightControl**](https://forums.eagle.ru/member.php?u=89536): Design & Programming & documentation.
|
||
--
|
||
-- @module Event
|
||
|
||
|
||
--- The EVENT structure
|
||
-- @type EVENT
|
||
-- @field #EVENT.Events Events
|
||
-- @extends Core.Base#BASE
|
||
EVENT = {
|
||
ClassName = "EVENT",
|
||
ClassID = 0,
|
||
}
|
||
|
||
--- The different types of events supported by MOOSE.
|
||
-- Use this structure to subscribe to events using the @{Base#BASE.HandleEvent}() method.
|
||
-- @type EVENTS
|
||
EVENTS = {
|
||
Shot = world.event.S_EVENT_SHOT,
|
||
Hit = world.event.S_EVENT_HIT,
|
||
Takeoff = world.event.S_EVENT_TAKEOFF,
|
||
Land = world.event.S_EVENT_LAND,
|
||
Crash = world.event.S_EVENT_CRASH,
|
||
Ejection = world.event.S_EVENT_EJECTION,
|
||
Refueling = world.event.S_EVENT_REFUELING,
|
||
Dead = world.event.S_EVENT_DEAD,
|
||
PilotDead = world.event.S_EVENT_PILOT_DEAD,
|
||
BaseCaptured = world.event.S_EVENT_BASE_CAPTURED,
|
||
MissionStart = world.event.S_EVENT_MISSION_START,
|
||
MissionEnd = world.event.S_EVENT_MISSION_END,
|
||
TookControl = world.event.S_EVENT_TOOK_CONTROL,
|
||
RefuelingStop = world.event.S_EVENT_REFUELING_STOP,
|
||
Birth = world.event.S_EVENT_BIRTH,
|
||
HumanFailure = world.event.S_EVENT_HUMAN_FAILURE,
|
||
EngineStartup = world.event.S_EVENT_ENGINE_STARTUP,
|
||
EngineShutdown = world.event.S_EVENT_ENGINE_SHUTDOWN,
|
||
PlayerEnterUnit = world.event.S_EVENT_PLAYER_ENTER_UNIT,
|
||
PlayerLeaveUnit = world.event.S_EVENT_PLAYER_LEAVE_UNIT,
|
||
PlayerComment = world.event.S_EVENT_PLAYER_COMMENT,
|
||
ShootingStart = world.event.S_EVENT_SHOOTING_START,
|
||
ShootingEnd = world.event.S_EVENT_SHOOTING_END,
|
||
}
|
||
|
||
--- The Event structure
|
||
-- Note that at the beginning of each field description, there is an indication which field will be populated depending on the object type involved in the Event:
|
||
--
|
||
-- * A (Object.Category.)UNIT : A UNIT object type is involved in the Event.
|
||
-- * A (Object.Category.)STATIC : A STATIC object type is involved in the Event.<2E>
|
||
--
|
||
-- @type EVENTDATA
|
||
-- @field #number id The identifier of the event.
|
||
--
|
||
-- @field Dcs.DCSUnit#Unit initiator (UNIT/STATIC/SCENERY) The initiating @{Dcs.DCSUnit#Unit} or @{Dcs.DCSStaticObject#StaticObject}.
|
||
-- @field Dcs.DCSObject#Object.Category IniObjectCategory (UNIT/STATIC/SCENERY) The initiator object category ( Object.Category.UNIT or Object.Category.STATIC ).
|
||
-- @field Dcs.DCSUnit#Unit IniDCSUnit (UNIT/STATIC) The initiating @{DCSUnit#Unit} or @{DCSStaticObject#StaticObject}.
|
||
-- @field #string IniDCSUnitName (UNIT/STATIC) The initiating Unit name.
|
||
-- @field Wrapper.Unit#UNIT IniUnit (UNIT/STATIC) The initiating MOOSE wrapper @{Unit#UNIT} of the initiator Unit object.
|
||
-- @field #string IniUnitName (UNIT/STATIC) The initiating UNIT name (same as IniDCSUnitName).
|
||
-- @field Dcs.DCSGroup#Group IniDCSGroup (UNIT) The initiating {DCSGroup#Group}.
|
||
-- @field #string IniDCSGroupName (UNIT) The initiating Group name.
|
||
-- @field Wrapper.Group#GROUP IniGroup (UNIT) The initiating MOOSE wrapper @{Group#GROUP} of the initiator Group object.
|
||
-- @field #string IniGroupName UNIT) The initiating GROUP name (same as IniDCSGroupName).
|
||
-- @field #string IniPlayerName (UNIT) The name of the initiating player in case the Unit is a client or player slot.
|
||
-- @field Dcs.DCScoalition#coalition.side IniCoalition (UNIT) The coalition of the initiator.
|
||
-- @field Dcs.DCSUnit#Unit.Category IniCategory (UNIT) The category of the initiator.
|
||
-- @field #string IniTypeName (UNIT) The type name of the initiator.
|
||
--
|
||
-- @field Dcs.DCSUnit#Unit target (UNIT/STATIC) The target @{Dcs.DCSUnit#Unit} or @{DCSStaticObject#StaticObject}.
|
||
-- @field Dcs.DCSObject#Object.Category TgtObjectCategory (UNIT/STATIC) The target object category ( Object.Category.UNIT or Object.Category.STATIC ).
|
||
-- @field Dcs.DCSUnit#Unit TgtDCSUnit (UNIT/STATIC) The target @{DCSUnit#Unit} or @{DCSStaticObject#StaticObject}.
|
||
-- @field #string TgtDCSUnitName (UNIT/STATIC) The target Unit name.
|
||
-- @field Wrapper.Unit#UNIT TgtUnit (UNIT/STATIC) The target MOOSE wrapper @{Unit#UNIT} of the target Unit object.
|
||
-- @field #string TgtUnitName (UNIT/STATIC) The target UNIT name (same as TgtDCSUnitName).
|
||
-- @field Dcs.DCSGroup#Group TgtDCSGroup (UNIT) The target {DCSGroup#Group}.
|
||
-- @field #string TgtDCSGroupName (UNIT) The target Group name.
|
||
-- @field Wrapper.Group#GROUP TgtGroup (UNIT) The target MOOSE wrapper @{Group#GROUP} of the target Group object.
|
||
-- @field #string TgtGroupName (UNIT) The target GROUP name (same as TgtDCSGroupName).
|
||
-- @field #string TgtPlayerName (UNIT) The name of the target player in case the Unit is a client or player slot.
|
||
-- @field Dcs.DCScoalition#coalition.side TgtCoalition (UNIT) The coalition of the target.
|
||
-- @field Dcs.DCSUnit#Unit.Category TgtCategory (UNIT) The category of the target.
|
||
-- @field #string TgtTypeName (UNIT) The type name of the target.
|
||
--
|
||
-- @field weapon The weapon used during the event.
|
||
-- @field Weapon
|
||
-- @field WeaponName
|
||
-- @field WeaponTgtDCSUnit
|
||
|
||
|
||
local _EVENTMETA = {
|
||
[world.event.S_EVENT_SHOT] = {
|
||
Order = 1,
|
||
Event = "OnEventShot",
|
||
Text = "S_EVENT_SHOT"
|
||
},
|
||
[world.event.S_EVENT_HIT] = {
|
||
Order = 1,
|
||
Event = "OnEventHit",
|
||
Text = "S_EVENT_HIT"
|
||
},
|
||
[world.event.S_EVENT_TAKEOFF] = {
|
||
Order = 1,
|
||
Event = "OnEventTakeoff",
|
||
Text = "S_EVENT_TAKEOFF"
|
||
},
|
||
[world.event.S_EVENT_LAND] = {
|
||
Order = 1,
|
||
Event = "OnEventLand",
|
||
Text = "S_EVENT_LAND"
|
||
},
|
||
[world.event.S_EVENT_CRASH] = {
|
||
Order = -1,
|
||
Event = "OnEventCrash",
|
||
Text = "S_EVENT_CRASH"
|
||
},
|
||
[world.event.S_EVENT_EJECTION] = {
|
||
Order = 1,
|
||
Event = "OnEventEjection",
|
||
Text = "S_EVENT_EJECTION"
|
||
},
|
||
[world.event.S_EVENT_REFUELING] = {
|
||
Order = 1,
|
||
Event = "OnEventRefueling",
|
||
Text = "S_EVENT_REFUELING"
|
||
},
|
||
[world.event.S_EVENT_DEAD] = {
|
||
Order = -1,
|
||
Event = "OnEventDead",
|
||
Text = "S_EVENT_DEAD"
|
||
},
|
||
[world.event.S_EVENT_PILOT_DEAD] = {
|
||
Order = 1,
|
||
Event = "OnEventPilotDead",
|
||
Text = "S_EVENT_PILOT_DEAD"
|
||
},
|
||
[world.event.S_EVENT_BASE_CAPTURED] = {
|
||
Order = 1,
|
||
Event = "OnEventBaseCaptured",
|
||
Text = "S_EVENT_BASE_CAPTURED"
|
||
},
|
||
[world.event.S_EVENT_MISSION_START] = {
|
||
Order = 1,
|
||
Event = "OnEventMissionStart",
|
||
Text = "S_EVENT_MISSION_START"
|
||
},
|
||
[world.event.S_EVENT_MISSION_END] = {
|
||
Order = 1,
|
||
Event = "OnEventMissionEnd",
|
||
Text = "S_EVENT_MISSION_END"
|
||
},
|
||
[world.event.S_EVENT_TOOK_CONTROL] = {
|
||
Order = 1,
|
||
Event = "OnEventTookControl",
|
||
Text = "S_EVENT_TOOK_CONTROL"
|
||
},
|
||
[world.event.S_EVENT_REFUELING_STOP] = {
|
||
Order = 1,
|
||
Event = "OnEventRefuelingStop",
|
||
Text = "S_EVENT_REFUELING_STOP"
|
||
},
|
||
[world.event.S_EVENT_BIRTH] = {
|
||
Order = 1,
|
||
Event = "OnEventBirth",
|
||
Text = "S_EVENT_BIRTH"
|
||
},
|
||
[world.event.S_EVENT_HUMAN_FAILURE] = {
|
||
Order = 1,
|
||
Event = "OnEventHumanFailure",
|
||
Text = "S_EVENT_HUMAN_FAILURE"
|
||
},
|
||
[world.event.S_EVENT_ENGINE_STARTUP] = {
|
||
Order = 1,
|
||
Event = "OnEventEngineStartup",
|
||
Text = "S_EVENT_ENGINE_STARTUP"
|
||
},
|
||
[world.event.S_EVENT_ENGINE_SHUTDOWN] = {
|
||
Order = 1,
|
||
Event = "OnEventEngineShutdown",
|
||
Text = "S_EVENT_ENGINE_SHUTDOWN"
|
||
},
|
||
[world.event.S_EVENT_PLAYER_ENTER_UNIT] = {
|
||
Order = 1,
|
||
Event = "OnEventPlayerEnterUnit",
|
||
Text = "S_EVENT_PLAYER_ENTER_UNIT"
|
||
},
|
||
[world.event.S_EVENT_PLAYER_LEAVE_UNIT] = {
|
||
Order = -1,
|
||
Event = "OnEventPlayerLeaveUnit",
|
||
Text = "S_EVENT_PLAYER_LEAVE_UNIT"
|
||
},
|
||
[world.event.S_EVENT_PLAYER_COMMENT] = {
|
||
Order = 1,
|
||
Event = "OnEventPlayerComment",
|
||
Text = "S_EVENT_PLAYER_COMMENT"
|
||
},
|
||
[world.event.S_EVENT_SHOOTING_START] = {
|
||
Order = 1,
|
||
Event = "OnEventShootingStart",
|
||
Text = "S_EVENT_SHOOTING_START"
|
||
},
|
||
[world.event.S_EVENT_SHOOTING_END] = {
|
||
Order = 1,
|
||
Event = "OnEventShootingEnd",
|
||
Text = "S_EVENT_SHOOTING_END"
|
||
},
|
||
}
|
||
|
||
|
||
--- The Events structure
|
||
-- @type EVENT.Events
|
||
-- @field #number IniUnit
|
||
|
||
function EVENT:New()
|
||
local self = BASE:Inherit( self, BASE:New() )
|
||
self:F2()
|
||
self.EventHandler = world.addEventHandler( self )
|
||
return self
|
||
end
|
||
|
||
function EVENT:EventText( EventID )
|
||
|
||
local EventText = _EVENTMETA[EventID].Text
|
||
|
||
return EventText
|
||
end
|
||
|
||
|
||
--- Initializes the Events structure for the event
|
||
-- @param #EVENT self
|
||
-- @param Dcs.DCSWorld#world.event EventID
|
||
-- @param Core.Base#BASE EventClass
|
||
-- @return #EVENT.Events
|
||
function EVENT:Init( EventID, EventClass )
|
||
self:F3( { _EVENTMETA[EventID].Text, EventClass } )
|
||
|
||
if not self.Events[EventID] then
|
||
-- Create a WEAK table to ensure that the garbage collector is cleaning the event links when the object usage is cleaned.
|
||
self.Events[EventID] = setmetatable( {}, { __mode = "k" } )
|
||
end
|
||
|
||
-- Each event has a subtable of EventClasses, ordered by EventPriority.
|
||
local EventPriority = EventClass:GetEventPriority()
|
||
if not self.Events[EventID][EventPriority] then
|
||
self.Events[EventID][EventPriority] = setmetatable( {}, { __mode = "kv" } )
|
||
end
|
||
|
||
if not self.Events[EventID][EventPriority][EventClass] then
|
||
self.Events[EventID][EventPriority][EventClass] = {}
|
||
end
|
||
return self.Events[EventID][EventPriority][EventClass]
|
||
end
|
||
|
||
--- Removes an Events entry
|
||
-- @param #EVENT self
|
||
-- @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 )
|
||
self:F3( { EventClass, _EVENTMETA[EventID].Text } )
|
||
|
||
local EventClass = EventClass
|
||
local EventPriority = EventClass:GetEventPriority()
|
||
self.Events[EventID][EventPriority][EventClass] = nil
|
||
end
|
||
|
||
--- Removes an Events entry for a UNIT.
|
||
-- @param #EVENT self
|
||
-- @param #string UnitName The name of the UNIT.
|
||
-- @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:RemoveForUnit( UnitName, EventClass, EventID )
|
||
self:F3( { EventClass, _EVENTMETA[EventID].Text } )
|
||
|
||
local EventClass = EventClass
|
||
local EventPriority = EventClass:GetEventPriority()
|
||
local Event = self.Events[EventID][EventPriority][EventClass]
|
||
Event.EventUnit[UnitName] = nil
|
||
end
|
||
|
||
--- Removes an Events entry for a GROUP.
|
||
-- @param #EVENT self
|
||
-- @param #string GroupName The name of the GROUP.
|
||
-- @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:RemoveForGroup( GroupName, EventClass, EventID )
|
||
self:F3( { EventClass, _EVENTMETA[EventID].Text } )
|
||
|
||
local EventClass = EventClass
|
||
local EventPriority = EventClass:GetEventPriority()
|
||
local Event = self.Events[EventID][EventPriority][EventClass]
|
||
Event.EventGroup[GroupName] = nil
|
||
end
|
||
|
||
--- Clears all event subscriptions for a @{Base#BASE} derived object.
|
||
-- @param #EVENT self
|
||
-- @param Core.Base#BASE EventObject
|
||
function EVENT:RemoveAll( EventObject )
|
||
self:F3( { EventObject:GetClassNameAndID() } )
|
||
|
||
local EventClass = EventObject:GetClassNameAndID()
|
||
local EventPriority = EventClass:GetEventPriority()
|
||
for EventID, EventData in pairs( self.Events ) do
|
||
self.Events[EventID][EventPriority][EventClass] = nil
|
||
end
|
||
end
|
||
|
||
|
||
|
||
--- Create an OnDead event handler for a group
|
||
-- @param #EVENT self
|
||
-- @param #table EventTemplate
|
||
-- @param #function EventFunction The function to be called when the event occurs for the unit.
|
||
-- @param EventClass The instance of the class for which the event is.
|
||
-- @param #function OnEventFunction
|
||
-- @return #EVENT
|
||
function EVENT:OnEventForTemplate( EventTemplate, EventFunction, EventClass, EventID )
|
||
self:F2( EventTemplate.name )
|
||
|
||
for EventUnitID, EventUnit in pairs( EventTemplate.units ) do
|
||
self:OnEventForUnit( EventUnit.name, EventFunction, EventClass, EventID )
|
||
end
|
||
return self
|
||
end
|
||
|
||
--- Set a new listener for an S_EVENT_X event independent from a unit or a weapon.
|
||
-- @param #EVENT self
|
||
-- @param #function EventFunction The function to be called when the event occurs for the unit.
|
||
-- @param Core.Base#BASE EventClass The self instance of the class for which the event is captured. When the event happens, the event process will be called in this class provided.
|
||
-- @param EventID
|
||
-- @return #EVENT
|
||
function EVENT:OnEventGeneric( EventFunction, EventClass, EventID )
|
||
self:F2( { EventID } )
|
||
|
||
local EventData = self:Init( EventID, EventClass )
|
||
EventData.EventFunction = EventFunction
|
||
EventData.EventClass = EventClass
|
||
|
||
return self
|
||
end
|
||
|
||
|
||
--- Set a new listener for an S_EVENT_X event for a UNIT.
|
||
-- @param #EVENT self
|
||
-- @param #string UnitName The name of the UNIT.
|
||
-- @param #function EventFunction The function to be called when the event occurs for the GROUP.
|
||
-- @param Core.Base#BASE EventClass The self instance of the class for which the event is.
|
||
-- @param EventID
|
||
-- @return #EVENT
|
||
function EVENT:OnEventForUnit( UnitName, EventFunction, EventClass, EventID )
|
||
self:F2( UnitName )
|
||
|
||
local EventData = self:Init( EventID, EventClass )
|
||
if not EventData.EventUnit then
|
||
EventData.EventUnit = {}
|
||
end
|
||
EventData.EventUnit[UnitName] = {}
|
||
EventData.EventUnit[UnitName].EventFunction = EventFunction
|
||
EventData.EventUnit[UnitName].EventClass = EventClass
|
||
return self
|
||
end
|
||
|
||
--- Set a new listener for an S_EVENT_X event for a GROUP.
|
||
-- @param #EVENT self
|
||
-- @param #string GroupName The name of the GROUP.
|
||
-- @param #function EventFunction The function to be called when the event occurs for the GROUP.
|
||
-- @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 )
|
||
|
||
local Event = self:Init( EventID, EventClass )
|
||
if not Event.EventGroup then
|
||
Event.EventGroup = {}
|
||
end
|
||
Event.EventGroup[GroupName] = {}
|
||
Event.EventGroup[GroupName].EventFunction = EventFunction
|
||
Event.EventGroup[GroupName].EventClass = EventClass
|
||
return self
|
||
end
|
||
|
||
do -- OnBirth
|
||
|
||
--- Create an OnBirth event handler for a group
|
||
-- @param #EVENT self
|
||
-- @param Wrapper.Group#GROUP EventGroup
|
||
-- @param #function EventFunction The function to be called when the event occurs for the unit.
|
||
-- @param EventClass The self instance of the class for which the event is.
|
||
-- @return #EVENT
|
||
function EVENT:OnBirthForTemplate( EventTemplate, EventFunction, EventClass )
|
||
self:F2( EventTemplate.name )
|
||
|
||
self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, EVENTS.Birth )
|
||
|
||
return self
|
||
end
|
||
|
||
end
|
||
|
||
do -- OnCrash
|
||
|
||
--- Create an OnCrash event handler for a group
|
||
-- @param #EVENT self
|
||
-- @param Wrapper.Group#GROUP EventGroup
|
||
-- @param #function EventFunction The function to be called when the event occurs for the unit.
|
||
-- @param EventClass The self instance of the class for which the event is.
|
||
-- @return #EVENT
|
||
function EVENT:OnCrashForTemplate( EventTemplate, EventFunction, EventClass )
|
||
self:F2( EventTemplate.name )
|
||
|
||
self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, EVENTS.Crash )
|
||
|
||
return self
|
||
end
|
||
|
||
end
|
||
|
||
do -- OnDead
|
||
|
||
--- Create an OnDead event handler for a group
|
||
-- @param #EVENT self
|
||
-- @param Wrapper.Group#GROUP EventGroup
|
||
-- @param #function EventFunction The function to be called when the event occurs for the unit.
|
||
-- @param EventClass The self instance of the class for which the event is.
|
||
-- @return #EVENT
|
||
function EVENT:OnDeadForTemplate( EventTemplate, EventFunction, EventClass )
|
||
self:F2( EventTemplate.name )
|
||
|
||
self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, EVENTS.Dead )
|
||
|
||
return self
|
||
end
|
||
|
||
end
|
||
|
||
|
||
do -- OnLand
|
||
--- Create an OnLand event handler for a group
|
||
-- @param #EVENT self
|
||
-- @param #table EventTemplate
|
||
-- @param #function EventFunction The function to be called when the event occurs for the unit.
|
||
-- @param EventClass The self instance of the class for which the event is.
|
||
-- @return #EVENT
|
||
function EVENT:OnLandForTemplate( EventTemplate, EventFunction, EventClass )
|
||
self:F2( EventTemplate.name )
|
||
|
||
self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, EVENTS.Land )
|
||
|
||
return self
|
||
end
|
||
|
||
end
|
||
|
||
do -- OnTakeOff
|
||
--- Create an OnTakeOff event handler for a group
|
||
-- @param #EVENT self
|
||
-- @param #table EventTemplate
|
||
-- @param #function EventFunction The function to be called when the event occurs for the unit.
|
||
-- @param EventClass The self instance of the class for which the event is.
|
||
-- @return #EVENT
|
||
function EVENT:OnTakeOffForTemplate( EventTemplate, EventFunction, EventClass )
|
||
self:F2( EventTemplate.name )
|
||
|
||
self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, EVENTS.Takeoff )
|
||
|
||
return self
|
||
end
|
||
|
||
end
|
||
|
||
do -- OnEngineShutDown
|
||
|
||
--- Create an OnDead event handler for a group
|
||
-- @param #EVENT self
|
||
-- @param #table EventTemplate
|
||
-- @param #function EventFunction The function to be called when the event occurs for the unit.
|
||
-- @param EventClass The self instance of the class for which the event is.
|
||
-- @return #EVENT
|
||
function EVENT:OnEngineShutDownForTemplate( EventTemplate, EventFunction, EventClass )
|
||
self:F2( EventTemplate.name )
|
||
|
||
self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, EVENTS.EngineShutdown )
|
||
|
||
return self
|
||
end
|
||
|
||
end
|
||
|
||
|
||
--- @param #EVENT self
|
||
-- @param #EVENTDATA Event
|
||
function EVENT:onEvent( Event )
|
||
|
||
local ErrorHandler = function( errmsg )
|
||
|
||
env.info( "Error in SCHEDULER function:" .. errmsg )
|
||
if debug ~= nil then
|
||
env.info( debug.traceback() )
|
||
end
|
||
|
||
return errmsg
|
||
end
|
||
|
||
if self and self.Events and self.Events[Event.id] then
|
||
|
||
|
||
if Event.initiator then
|
||
|
||
Event.IniObjectCategory = Event.initiator:getCategory()
|
||
|
||
if Event.IniObjectCategory == Object.Category.UNIT then
|
||
Event.IniDCSUnit = Event.initiator
|
||
Event.IniDCSUnitName = Event.IniDCSUnit:getName()
|
||
Event.IniUnitName = Event.IniDCSUnitName
|
||
Event.IniDCSGroup = Event.IniDCSUnit:getGroup()
|
||
Event.IniUnit = UNIT:FindByName( Event.IniDCSUnitName )
|
||
if not Event.IniUnit then
|
||
-- Unit can be a CLIENT. Most likely this will be the case ...
|
||
Event.IniUnit = CLIENT:FindByName( Event.IniDCSUnitName, '', true )
|
||
end
|
||
Event.IniDCSGroupName = ""
|
||
if Event.IniDCSGroup and Event.IniDCSGroup:isExist() then
|
||
Event.IniDCSGroupName = Event.IniDCSGroup:getName()
|
||
Event.IniGroup = GROUP:FindByName( Event.IniDCSGroupName )
|
||
if Event.IniGroup then
|
||
Event.IniGroupName = Event.IniDCSGroupName
|
||
end
|
||
end
|
||
Event.IniPlayerName = Event.IniDCSUnit:getPlayerName()
|
||
Event.IniCoalition = Event.IniDCSUnit:getCoalition()
|
||
Event.IniTypeName = Event.IniDCSUnit:getTypeName()
|
||
Event.IniCategory = Event.IniDCSUnit:getDesc().category
|
||
end
|
||
|
||
if Event.IniObjectCategory == Object.Category.STATIC then
|
||
Event.IniDCSUnit = Event.initiator
|
||
Event.IniDCSUnitName = Event.IniDCSUnit:getName()
|
||
Event.IniUnitName = Event.IniDCSUnitName
|
||
Event.IniUnit = STATIC:FindByName( Event.IniDCSUnitName, false )
|
||
Event.IniCoalition = Event.IniDCSUnit:getCoalition()
|
||
Event.IniCategory = Event.IniDCSUnit:getDesc().category
|
||
Event.IniTypeName = Event.IniDCSUnit:getTypeName()
|
||
end
|
||
|
||
if Event.IniObjectCategory == Object.Category.SCENERY then
|
||
Event.IniDCSUnit = Event.initiator
|
||
Event.IniDCSUnitName = Event.IniDCSUnit:getName()
|
||
Event.IniUnitName = Event.IniDCSUnitName
|
||
Event.IniUnit = SCENERY:Register( Event.IniDCSUnitName, Event.initiator )
|
||
Event.IniCategory = Event.IniDCSUnit:getDesc().category
|
||
Event.IniTypeName = Event.IniDCSUnit:getTypeName()
|
||
end
|
||
end
|
||
|
||
if Event.target then
|
||
|
||
Event.TgtObjectCategory = Event.target:getCategory()
|
||
|
||
if Event.TgtObjectCategory == Object.Category.UNIT then
|
||
Event.TgtDCSUnit = Event.target
|
||
Event.TgtDCSGroup = Event.TgtDCSUnit:getGroup()
|
||
Event.TgtDCSUnitName = Event.TgtDCSUnit:getName()
|
||
Event.TgtUnitName = Event.TgtDCSUnitName
|
||
Event.TgtUnit = UNIT:FindByName( Event.TgtDCSUnitName )
|
||
Event.TgtDCSGroupName = ""
|
||
if Event.TgtDCSGroup and Event.TgtDCSGroup:isExist() then
|
||
Event.TgtDCSGroupName = Event.TgtDCSGroup:getName()
|
||
Event.TgtGroup = GROUP:FindByName( Event.TgtDCSGroupName )
|
||
if Event.TgtGroup then
|
||
Event.TgtGroupName = Event.TgtDCSGroupName
|
||
end
|
||
end
|
||
Event.TgtPlayerName = Event.TgtDCSUnit:getPlayerName()
|
||
Event.TgtCoalition = Event.TgtDCSUnit:getCoalition()
|
||
Event.TgtCategory = Event.TgtDCSUnit:getDesc().category
|
||
Event.TgtTypeName = Event.TgtDCSUnit:getTypeName()
|
||
end
|
||
|
||
if Event.TgtObjectCategory == Object.Category.STATIC then
|
||
Event.TgtDCSUnit = Event.target
|
||
Event.TgtDCSUnitName = Event.TgtDCSUnit:getName()
|
||
Event.TgtUnitName = Event.TgtDCSUnitName
|
||
Event.TgtUnit = STATIC:FindByName( Event.TgtDCSUnitName )
|
||
Event.TgtCoalition = Event.TgtDCSUnit:getCoalition()
|
||
Event.TgtCategory = Event.TgtDCSUnit:getDesc().category
|
||
Event.TgtTypeName = Event.TgtDCSUnit:getTypeName()
|
||
end
|
||
|
||
if Event.TgtObjectCategory == Object.Category.SCENERY then
|
||
Event.TgtDCSUnit = Event.target
|
||
Event.TgtDCSUnitName = Event.TgtDCSUnit:getName()
|
||
Event.TgtUnitName = Event.TgtDCSUnitName
|
||
Event.TgtUnit = SCENERY:Register( Event.TgtDCSUnitName, Event.target )
|
||
Event.TgtCategory = Event.TgtDCSUnit:getDesc().category
|
||
Event.TgtTypeName = Event.TgtDCSUnit:getTypeName()
|
||
end
|
||
end
|
||
|
||
if Event.weapon then
|
||
Event.Weapon = Event.weapon
|
||
Event.WeaponName = Event.Weapon:getTypeName()
|
||
--Event.WeaponTgtDCSUnit = Event.Weapon:getTarget()
|
||
end
|
||
|
||
local PriorityOrder = _EVENTMETA[Event.id].Order
|
||
local PriorityBegin = PriorityOrder == -1 and 5 or 1
|
||
local PriorityEnd = PriorityOrder == -1 and 1 or 5
|
||
|
||
if Event.IniObjectCategory ~= 3 then
|
||
self:E( { _EVENTMETA[Event.id].Text, Event, Event.IniDCSUnitName, Event.TgtDCSUnitName, PriorityOrder } )
|
||
end
|
||
|
||
for EventPriority = PriorityBegin, PriorityEnd, PriorityOrder do
|
||
|
||
if self.Events[Event.id][EventPriority] then
|
||
|
||
-- 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
|
||
|
||
Event.IniGroup = GROUP:FindByName( Event.IniDCSGroupName )
|
||
Event.TgtGroup = GROUP:FindByName( Event.TgtDCSGroupName )
|
||
|
||
-- If the EventData is for a UNIT, the call directly the EventClass EventFunction for that UNIT.
|
||
if ( Event.IniDCSUnitName and EventData.EventUnit and EventData.EventUnit[Event.IniDCSUnitName] ) or
|
||
( Event.TgtDCSUnitName and EventData.EventUnit and EventData.EventUnit[Event.TgtDCSUnitName] ) then
|
||
|
||
if EventData.EventUnit[Event.IniDCSUnitName] then
|
||
|
||
-- First test if a EventFunction is Set, otherwise search for the default function
|
||
if EventData.EventUnit[Event.IniDCSUnitName].EventFunction then
|
||
|
||
if Event.IniObjectCategory ~= 3 then
|
||
self:E( { "Calling EventFunction for UNIT ", EventClass:GetClassNameAndID(), ", Unit ", Event.IniUnitName, EventPriority } )
|
||
end
|
||
|
||
local Result, Value = xpcall(
|
||
function()
|
||
return EventData.EventUnit[Event.IniDCSUnitName].EventFunction( EventClass, Event )
|
||
end, ErrorHandler )
|
||
|
||
else
|
||
|
||
-- There is no EventFunction defined, so try to find if a default OnEvent function is defined on the object.
|
||
local EventFunction = EventClass[ _EVENTMETA[Event.id].Event ]
|
||
if EventFunction and type( EventFunction ) == "function" then
|
||
|
||
-- Now call the default event function.
|
||
if Event.IniObjectCategory ~= 3 then
|
||
self:E( { "Calling " .. _EVENTMETA[Event.id].Event .. " for Class ", EventClass:GetClassNameAndID(), EventPriority } )
|
||
end
|
||
|
||
local Result, Value = xpcall(
|
||
function()
|
||
return EventFunction( EventClass, Event )
|
||
end, ErrorHandler )
|
||
end
|
||
end
|
||
end
|
||
|
||
if EventData.EventUnit[Event.TgtDCSUnitName] then
|
||
|
||
-- First test if a EventFunction is Set, otherwise search for the default function
|
||
if EventData.EventUnit[Event.TgtDCSUnitName].EventFunction then
|
||
|
||
if Event.IniObjectCategory ~= 3 then
|
||
self:E( { "Calling EventFunction for UNIT ", EventClass:GetClassNameAndID(), ", Unit ", Event.TgtUnitName, EventPriority } )
|
||
end
|
||
|
||
local Result, Value = xpcall(
|
||
function()
|
||
return EventData.EventUnit[Event.TgtDCSUnitName].EventFunction( EventClass, Event )
|
||
end, ErrorHandler )
|
||
|
||
else
|
||
|
||
-- There is no EventFunction defined, so try to find if a default OnEvent function is defined on the object.
|
||
local EventFunction = EventClass[ _EVENTMETA[Event.id].Event ]
|
||
if EventFunction and type( EventFunction ) == "function" then
|
||
|
||
-- Now call the default event function.
|
||
if Event.IniObjectCategory ~= 3 then
|
||
self:E( { "Calling " .. _EVENTMETA[Event.id].Event .. " for Class ", EventClass:GetClassNameAndID(), EventPriority } )
|
||
end
|
||
|
||
local Result, Value = xpcall(
|
||
function()
|
||
return EventFunction( EventClass, Event )
|
||
end, ErrorHandler )
|
||
end
|
||
end
|
||
end
|
||
|
||
else
|
||
|
||
-- If the EventData is for a GROUP, the call directly the EventClass EventFunction for the UNIT in that GROUP.
|
||
if ( Event.IniDCSUnitName and Event.IniDCSGroupName and Event.IniGroupName and EventData.EventGroup and EventData.EventGroup[Event.IniGroupName] ) or
|
||
( Event.TgtDCSUnitName and Event.TgtDCSGroupName and Event.TgtGroupName and EventData.EventGroup and EventData.EventGroup[Event.TgtGroupName] ) then
|
||
|
||
if EventData.EventGroup[Event.IniGroupName] then
|
||
-- First test if a EventFunction is Set, otherwise search for the default function
|
||
if EventData.EventGroup[Event.IniGroupName].EventFunction then
|
||
|
||
if Event.IniObjectCategory ~= 3 then
|
||
self:E( { "Calling EventFunction for GROUP ", EventClass:GetClassNameAndID(), ", Unit ", Event.IniUnitName, EventPriority } )
|
||
end
|
||
|
||
local Result, Value = xpcall(
|
||
function()
|
||
return EventData.EventGroup[Event.IniGroupName].EventFunction( EventClass, Event )
|
||
end, ErrorHandler )
|
||
|
||
else
|
||
|
||
-- There is no EventFunction defined, so try to find if a default OnEvent function is defined on the object.
|
||
local EventFunction = EventClass[ _EVENTMETA[Event.id].Event ]
|
||
if EventFunction and type( EventFunction ) == "function" then
|
||
|
||
-- Now call the default event function.
|
||
if Event.IniObjectCategory ~= 3 then
|
||
self:E( { "Calling " .. _EVENTMETA[Event.id].Event .. " for GROUP ", EventClass:GetClassNameAndID(), EventPriority } )
|
||
end
|
||
|
||
local Result, Value = xpcall(
|
||
function()
|
||
return EventFunction( EventClass, Event )
|
||
end, ErrorHandler )
|
||
end
|
||
end
|
||
end
|
||
|
||
if EventData.EventGroup[Event.TgtGroupName] then
|
||
if EventData.EventGroup[Event.TgtGroupName].EventFunction then
|
||
|
||
if Event.IniObjectCategory ~= 3 then
|
||
self:E( { "Calling EventFunction for GROUP ", EventClass:GetClassNameAndID(), ", Unit ", Event.TgtUnitName, EventPriority } )
|
||
end
|
||
|
||
local Result, Value = xpcall(
|
||
function()
|
||
return EventData.EventGroup[Event.TgtGroupName].EventFunction( EventClass, Event )
|
||
end, ErrorHandler )
|
||
|
||
else
|
||
|
||
-- There is no EventFunction defined, so try to find if a default OnEvent function is defined on the object.
|
||
local EventFunction = EventClass[ _EVENTMETA[Event.id].Event ]
|
||
if EventFunction and type( EventFunction ) == "function" then
|
||
|
||
-- Now call the default event function.
|
||
if Event.IniObjectCategory ~= 3 then
|
||
self:E( { "Calling " .. _EVENTMETA[Event.id].Event .. " for GROUP ", EventClass:GetClassNameAndID(), EventPriority } )
|
||
end
|
||
|
||
local Result, Value = xpcall(
|
||
function()
|
||
return EventFunction( EventClass, Event )
|
||
end, ErrorHandler )
|
||
end
|
||
end
|
||
end
|
||
|
||
else
|
||
|
||
-- If the EventData is not bound to a specific unit, then call the EventClass EventFunction.
|
||
-- Note that here the EventFunction will need to implement and determine the logic for the relevant source- or target unit, or weapon.
|
||
if Event.IniDCSUnit and not EventData.EventUnit then
|
||
|
||
if EventClass == EventData.EventClass then
|
||
|
||
-- First test if a EventFunction is Set, otherwise search for the default function
|
||
if EventData.EventFunction then
|
||
|
||
-- There is an EventFunction defined, so call the EventFunction.
|
||
if Event.IniObjectCategory ~= 3 then
|
||
self:E( { "Calling EventFunction for Class ", EventClass:GetClassNameAndID(), EventPriority } )
|
||
end
|
||
local Result, Value = xpcall(
|
||
function()
|
||
return EventData.EventFunction( EventClass, Event )
|
||
end, ErrorHandler )
|
||
else
|
||
|
||
-- There is no EventFunction defined, so try to find if a default OnEvent function is defined on the object.
|
||
local EventFunction = EventClass[ _EVENTMETA[Event.id].Event ]
|
||
if EventFunction and type( EventFunction ) == "function" then
|
||
|
||
-- Now call the default event function.
|
||
if Event.IniObjectCategory ~= 3 then
|
||
self:E( { "Calling " .. _EVENTMETA[Event.id].Event .. " for Class ", EventClass:GetClassNameAndID(), EventPriority } )
|
||
end
|
||
|
||
local Result, Value = xpcall(
|
||
function()
|
||
return EventFunction( EventClass, Event )
|
||
end, ErrorHandler )
|
||
end
|
||
end
|
||
end
|
||
end
|
||
end
|
||
end
|
||
end
|
||
end
|
||
end
|
||
else
|
||
self:E( { _EVENTMETA[Event.id].Text, Event } )
|
||
end
|
||
end
|
||
|
||
--- The EVENTHANDLER structure
|
||
-- @type EVENTHANDLER
|
||
-- @extends Core.Base#BASE
|
||
EVENTHANDLER = {
|
||
ClassName = "EVENTHANDLER",
|
||
ClassID = 0,
|
||
}
|
||
|
||
--- The EVENTHANDLER constructor
|
||
-- @param #EVENTHANDLER self
|
||
-- @return #EVENTHANDLER
|
||
function EVENTHANDLER:New()
|
||
self = BASE:Inherit( self, BASE:New() ) -- #EVENTHANDLER
|
||
return self
|
||
end
|