Merge branch 'master-353-task-cargo-transport' into enhancement-353-task-cargo-transport

This commit is contained in:
Sven Van de Velde
2017-04-12 17:35:10 +02:00
committed by GitHub
328 changed files with 18685 additions and 4164 deletions

View File

@@ -3,7 +3,6 @@
<listAttribute key="org.eclipse.debug.ui.favoriteGroups">
<listEntry value="org.eclipse.ui.externaltools.launchGroup"/>
</listAttribute>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_LOCATION" value="${workspace_loc:/Moose_Framework/Utils/lua/5.1/bin/luadocumentor.bat}"/>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_TOOL_ARGUMENTS" value="&quot;Moose\Core\*.lua&quot; &#13;&#10;&quot;Moose\Wrapper\*.lua&quot; &#13;&#10;&quot;Moose\Actions\*.lua&quot; &#13;&#10;&quot;Moose\Functional\*.lua&quot; &#13;&#10;&quot;Moose\Tasking\*.lua&quot; &#13;&#10;&quot;Moose\Utilities\*.lua&quot; &#13;&#10;&quot;Moose\AI\*.lua&quot; &#13;&#10;--dir &quot;${workspace_loc:/Moose_Framework/docs/Documentation}&#13;&#10;--style ${workspace_loc:/Moose_Framework/docs/Stylesheet/stylesheet.css}"/>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_WORKING_DIRECTORY" value="${workspace_loc:/Moose_Framework/Moose Development}"/>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_LOCATION" value="${workspace_loc:/Moose_Framework/Utils/luadocumentor.bat}"/>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_WORKING_DIRECTORY" value="${workspace_loc:/Moose_Framework/Utils}"/>
</launchConfiguration>

View File

@@ -3,7 +3,7 @@
<listAttribute key="org.eclipse.debug.ui.favoriteGroups">
<listEntry value="org.eclipse.ui.externaltools.launchGroup"/>
</listAttribute>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_LOCATION" value="${workspace_loc:/Moose_Framework/Utils/lua/5.1/bin/lua.exe}"/>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_LOCATION" value="${workspace_loc:/Moose_Framework/Utils/luarocks/lua5.1.exe}"/>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_TOOL_ARGUMENTS" value="&quot;Moose_Create.lua&quot; &#13;&#10;&quot;D&quot;&#13;&#10;&quot;${current_date}&quot; &#13;&#10;&quot;${workspace_loc:/Moose_Framework//Moose Development/Moose}&quot; &#13;&#10;&quot;${workspace_loc:/Moose_Framework/Moose Mission Setup}&quot;"/>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_WORKING_DIRECTORY" value="${workspace_loc:/Moose_Framework/Moose Mission Setup}"/>
</launchConfiguration>

View File

@@ -3,7 +3,7 @@
<listAttribute key="org.eclipse.debug.ui.favoriteGroups">
<listEntry value="org.eclipse.ui.externaltools.launchGroup"/>
</listAttribute>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_LOCATION" value="${workspace_loc:/Moose_Framework/Utils/lua/5.1/bin/lua.exe}"/>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_LOCATION" value="${workspace_loc:/Moose_Framework/Utils/luarocks/lua5.1.exe}"/>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_TOOL_ARGUMENTS" value="&quot;Moose_Create.lua&quot; &#13;&#10;&quot;S&quot;&#13;&#10;&quot;${current_date}&quot; &#13;&#10;&quot;${workspace_loc:/Moose_Framework//Moose Development/Moose}&quot; &#13;&#10;&quot;${workspace_loc:/Moose_Framework/Moose Mission Setup}&quot;"/>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_WORKING_DIRECTORY" value="${workspace_loc:/Moose_Framework/Moose Mission Setup}"/>
</launchConfiguration>

View File

@@ -4,6 +4,5 @@
<listEntry value="org.eclipse.ui.externaltools.launchGroup"/>
</listAttribute>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_LOCATION" value="${workspace_loc:/Moose_Framework/Moose Mission Setup/Moose Mission Update/Moose_Update_Missions.bat}"/>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_TOOL_ARGUMENTS" value="&quot;${folder_prompt}&quot;"/>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_WORKING_DIRECTORY" value="${workspace_loc:/Moose_Framework/Moose Mission Setup/Moose Mission Update}"/>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_WORKING_DIRECTORY" value="${workspace_loc:/Moose_Missions}"/>
</launchConfiguration>

View File

@@ -219,9 +219,9 @@ local _ClassID = 0
BASE = {
ClassName = "BASE",
ClassID = 0,
_Private = {},
Events = {},
States = {}
States = {},
_ = {},
}
--- The Formation Class
@@ -360,7 +360,7 @@ do -- Event Handling
-- @param #BASE self
-- @return #number The @{Event} processing Priority.
function BASE:GetEventPriority()
return self._Private.EventPriority or 5
return self._.EventPriority or 5
end
--- Set the Class @{Event} processing Priority.
@@ -370,7 +370,7 @@ do -- Event Handling
-- @param #number EventPriority The @{Event} processing Priority.
-- @return self
function BASE:SetEventPriority( EventPriority )
self._Private.EventPriority = EventPriority
self._.EventPriority = EventPriority
end
--- Remove all subscribed events

View File

@@ -46,6 +46,7 @@ DATABASE = {
Templates = {
Units = {},
Groups = {},
Statics = {},
ClientsByName = {},
ClientsByID = {},
},
@@ -318,7 +319,7 @@ function DATABASE:Spawn( SpawnTemplate )
SpawnTemplate.CountryID = nil
SpawnTemplate.CategoryID = nil
self:_RegisterTemplate( SpawnTemplate, SpawnCoalitionID, SpawnCategoryID, SpawnCountryID )
self:_RegisterGroupTemplate( SpawnTemplate, SpawnCoalitionID, SpawnCategoryID, SpawnCountryID )
self:T3( SpawnTemplate )
coalition.addGroup( SpawnCountryID, SpawnCategoryID, SpawnTemplate )
@@ -354,7 +355,7 @@ end
-- @param #DATABASE self
-- @param #table GroupTemplate
-- @return #DATABASE self
function DATABASE:_RegisterTemplate( GroupTemplate, CoalitionID, CategoryID, CountryID )
function DATABASE:_RegisterGroupTemplate( GroupTemplate, CoalitionID, CategoryID, CountryID )
local GroupTemplateName = env.getValueDictByKey(GroupTemplate.name)
@@ -432,6 +433,54 @@ function DATABASE:GetGroupTemplate( GroupName )
return GroupTemplate
end
--- Private method that registers new Static Templates within the DATABASE Object.
-- @param #DATABASE self
-- @param #table GroupTemplate
-- @return #DATABASE self
function DATABASE:_RegisterStaticTemplate( StaticTemplate, CoalitionID, CategoryID, CountryID )
local TraceTable = {}
local StaticTemplateName = env.getValueDictByKey(StaticTemplate.name)
self.Templates.Statics[StaticTemplateName] = self.Templates.Statics[StaticTemplateName] or {}
StaticTemplate.CategoryID = CategoryID
StaticTemplate.CoalitionID = CoalitionID
StaticTemplate.CountryID = CountryID
self.Templates.Statics[StaticTemplateName].StaticName = StaticTemplateName
self.Templates.Statics[StaticTemplateName].GroupTemplate = StaticTemplate
self.Templates.Statics[StaticTemplateName].UnitTemplate = StaticTemplate.units[1]
self.Templates.Statics[StaticTemplateName].CategoryID = CategoryID
self.Templates.Statics[StaticTemplateName].CoalitionID = CoalitionID
self.Templates.Statics[StaticTemplateName].CountryID = CountryID
TraceTable[#TraceTable+1] = "Static"
TraceTable[#TraceTable+1] = self.Templates.Statics[StaticTemplateName].GroupName
TraceTable[#TraceTable+1] = "Coalition"
TraceTable[#TraceTable+1] = self.Templates.Statics[StaticTemplateName].CoalitionID
TraceTable[#TraceTable+1] = "Category"
TraceTable[#TraceTable+1] = self.Templates.Statics[StaticTemplateName].CategoryID
TraceTable[#TraceTable+1] = "Country"
TraceTable[#TraceTable+1] = self.Templates.Statics[StaticTemplateName].CountryID
self:E( TraceTable )
end
--- @param #DATABASE self
function DATABASE:GetStaticUnitTemplate( StaticName )
local StaticTemplate = self.Templates.Statics[StaticName].UnitTemplate
StaticTemplate.SpawnCoalitionID = self.Templates.Statics[StaticName].CoalitionID
StaticTemplate.SpawnCategoryID = self.Templates.Statics[StaticName].CategoryID
StaticTemplate.SpawnCountryID = self.Templates.Statics[StaticName].CountryID
return StaticTemplate
end
function DATABASE:GetGroupNameFromUnitName( UnitName )
return self.Templates.Units[UnitName].GroupName
end
@@ -798,6 +847,7 @@ function DATABASE:OnEventDeleteCargo( EventData )
end
--- @param #DATABASE self
function DATABASE:_RegisterTemplates()
self:F2()
@@ -853,11 +903,18 @@ function DATABASE:_RegisterTemplates()
--self.Units[coa_name][countryName][category] = {}
for group_num, GroupTemplate in pairs(obj_type_data.group) do
for group_num, Template in pairs(obj_type_data.group) do
if GroupTemplate and GroupTemplate.units and type(GroupTemplate.units) == 'table' then --making sure again- this is a valid group
self:_RegisterTemplate(
GroupTemplate,
if obj_type_name ~= "static" and Template and Template.units and type(Template.units) == 'table' then --making sure again- this is a valid group
self:_RegisterGroupTemplate(
Template,
CoalitionSide,
_DATABASECategory[string.lower(CategoryName)],
CountryID
)
else
self:_RegisterStaticTemplate(
Template,
CoalitionSide,
_DATABASECategory[string.lower(CategoryName)],
CountryID

View File

@@ -235,7 +235,7 @@ EVENTS = {
-- 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.µ
-- * A (Object.Category.)STATIC : A STATIC object type is involved in the Event.µ
--
-- @type EVENTDATA
-- @field #number id The identifier of the event.
@@ -280,116 +280,139 @@ EVENTS = {
local _EVENTMETA = {
[world.event.S_EVENT_SHOT] = {
Order = 1,
Side = "I",
Event = "OnEventShot",
Text = "S_EVENT_SHOT"
},
[world.event.S_EVENT_HIT] = {
Order = 1,
Side = "T",
Event = "OnEventHit",
Text = "S_EVENT_HIT"
},
[world.event.S_EVENT_TAKEOFF] = {
Order = 1,
Side = "I",
Event = "OnEventTakeoff",
Text = "S_EVENT_TAKEOFF"
},
[world.event.S_EVENT_LAND] = {
Order = 1,
Side = "I",
Event = "OnEventLand",
Text = "S_EVENT_LAND"
},
[world.event.S_EVENT_CRASH] = {
Order = -1,
Side = "I",
Event = "OnEventCrash",
Text = "S_EVENT_CRASH"
},
[world.event.S_EVENT_EJECTION] = {
Order = 1,
Side = "I",
Event = "OnEventEjection",
Text = "S_EVENT_EJECTION"
},
[world.event.S_EVENT_REFUELING] = {
Order = 1,
Side = "I",
Event = "OnEventRefueling",
Text = "S_EVENT_REFUELING"
},
[world.event.S_EVENT_DEAD] = {
Order = -1,
Side = "I",
Event = "OnEventDead",
Text = "S_EVENT_DEAD"
},
[world.event.S_EVENT_PILOT_DEAD] = {
Order = 1,
Side = "I",
Event = "OnEventPilotDead",
Text = "S_EVENT_PILOT_DEAD"
},
[world.event.S_EVENT_BASE_CAPTURED] = {
Order = 1,
Side = "I",
Event = "OnEventBaseCaptured",
Text = "S_EVENT_BASE_CAPTURED"
},
[world.event.S_EVENT_MISSION_START] = {
Order = 1,
Side = "N",
Event = "OnEventMissionStart",
Text = "S_EVENT_MISSION_START"
},
[world.event.S_EVENT_MISSION_END] = {
Order = 1,
Side = "N",
Event = "OnEventMissionEnd",
Text = "S_EVENT_MISSION_END"
},
[world.event.S_EVENT_TOOK_CONTROL] = {
Order = 1,
Side = "N",
Event = "OnEventTookControl",
Text = "S_EVENT_TOOK_CONTROL"
},
[world.event.S_EVENT_REFUELING_STOP] = {
Order = 1,
Side = "I",
Event = "OnEventRefuelingStop",
Text = "S_EVENT_REFUELING_STOP"
},
[world.event.S_EVENT_BIRTH] = {
Order = 1,
Side = "I",
Event = "OnEventBirth",
Text = "S_EVENT_BIRTH"
},
[world.event.S_EVENT_HUMAN_FAILURE] = {
Order = 1,
Side = "I",
Event = "OnEventHumanFailure",
Text = "S_EVENT_HUMAN_FAILURE"
},
[world.event.S_EVENT_ENGINE_STARTUP] = {
Order = 1,
Side = "I",
Event = "OnEventEngineStartup",
Text = "S_EVENT_ENGINE_STARTUP"
},
[world.event.S_EVENT_ENGINE_SHUTDOWN] = {
Order = 1,
Side = "I",
Event = "OnEventEngineShutdown",
Text = "S_EVENT_ENGINE_SHUTDOWN"
},
[world.event.S_EVENT_PLAYER_ENTER_UNIT] = {
Order = 1,
Side = "I",
Event = "OnEventPlayerEnterUnit",
Text = "S_EVENT_PLAYER_ENTER_UNIT"
},
[world.event.S_EVENT_PLAYER_LEAVE_UNIT] = {
Order = -1,
Side = "I",
Event = "OnEventPlayerLeaveUnit",
Text = "S_EVENT_PLAYER_LEAVE_UNIT"
},
[world.event.S_EVENT_PLAYER_COMMENT] = {
Order = 1,
Side = "I",
Event = "OnEventPlayerComment",
Text = "S_EVENT_PLAYER_COMMENT"
},
[world.event.S_EVENT_SHOOTING_START] = {
Order = 1,
Side = "I",
Event = "OnEventShootingStart",
Text = "S_EVENT_SHOOTING_START"
},
[world.event.S_EVENT_SHOOTING_END] = {
Order = 1,
Side = "I",
Event = "OnEventShootingEnd",
Text = "S_EVENT_SHOOTING_END"
},
@@ -417,13 +440,6 @@ function EVENT:New()
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
@@ -435,7 +451,7 @@ function EVENT:Init( EventID, 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" } )
self.Events[EventID] = {}
end
-- Each event has a subtable of EventClasses, ordered by EventPriority.
@@ -445,53 +461,56 @@ function EVENT:Init( EventID, EventClass )
end
if not self.Events[EventID][EventPriority][EventClass] then
self.Events[EventID][EventPriority][EventClass] = setmetatable( {}, { __mode = "v" } )
self.Events[EventID][EventPriority][EventClass] = {}
end
return self.Events[EventID][EventPriority][EventClass]
end
--- Removes an Events entry
--- Removes a subscription
-- @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
self:E( { "Removing subscription for class: ", EventClass:GetClassNameAndID() } )
local EventPriority = EventClass:GetEventPriority()
self.EventsDead = self.EventsDead or {}
self.EventsDead[EventID] = self.EventsDead[EventID] or {}
self.EventsDead[EventID][EventPriority] = self.EventsDead[EventID][EventPriority] or {}
self.EventsDead[EventID][EventPriority][EventClass] = self.Events[EventID][EventPriority][EventClass]
self.Events[EventID][EventPriority][EventClass] = nil
end
--- Removes an Events entry for a UNIT.
--- Resets subscriptions
-- @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 } )
function EVENT:Reset( EventObject )
local EventClass = EventClass
local EventPriority = EventClass:GetEventPriority()
local Event = self.Events[EventID][EventPriority][EventClass]
Event.EventUnit[UnitName] = nil
self:E( { "Resetting subscriptions for class: ", EventObject:GetClassNameAndID() } )
local EventPriority = EventObject:GetEventPriority()
for EventID, EventData in pairs( self.Events ) do
if self.EventsDead then
if self.EventsDead[EventID] then
if self.EventsDead[EventID][EventPriority] then
if self.EventsDead[EventID][EventPriority][EventObject] then
self.Events[EventID][EventPriority][EventObject] = self.EventsDead[EventID][EventPriority][EventObject]
end
end
end
end
end
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
@@ -535,7 +554,6 @@ function EVENT:OnEventGeneric( EventFunction, EventClass, EventID )
local EventData = self:Init( EventID, EventClass )
EventData.EventFunction = EventFunction
EventData.EventClass = EventClass
return self
end
@@ -552,12 +570,8 @@ 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
EventData.EventUnit = true
EventData.EventFunction = EventFunction
return self
end
@@ -572,12 +586,8 @@ 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
Event.EventGroup = true
Event.EventFunction = EventFunction
return self
end
@@ -736,10 +746,10 @@ function EVENT:onEvent( Event )
return errmsg
end
self:E( _EVENTMETA[Event.id].Text, Event )
local EventMeta = _EVENTMETA[Event.id]
if self and self.Events and self.Events[Event.id] then
if Event.initiator then
@@ -849,12 +859,12 @@ function EVENT:onEvent( Event )
Event.CargoName = Event.cargo.Name
end
local PriorityOrder = _EVENTMETA[Event.id].Order
local PriorityOrder = EventMeta.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 } )
self:E( { EventMeta.Text, Event, Event.IniDCSUnitName, Event.TgtDCSUnitName, PriorityOrder } )
end
for EventPriority = PriorityBegin, PriorityEnd, PriorityOrder do
@@ -864,187 +874,144 @@ function EVENT:onEvent( Event )
-- Okay, we got the event from DCS. Now loop the SORTED self.EventSorted[] table for the received Event.id, and for each EventData registered, check if a function needs to be called.
for EventClass, EventData in pairs( self.Events[Event.id][EventPriority] ) do
self:E( { "Evaluating: ", EventClass:GetClassNameAndID() } )
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 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
-- So now the EventClass must be a UNIT class!!! We check if it is still "Alive".
if EventClass:IsAlive() or
Event.id == EVENTS.Crash or
Event.id == EVENTS.Dead 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.
local UnitName = EventClass:GetName()
if ( EventMeta.Side == "I" and UnitName == Event.IniDCSUnitName ) or
( EventMeta.Side == "T" and UnitName == Event.TgtDCSUnitName ) then
-- First test if a EventFunction is Set, otherwise search for the default function
if EventData.EventFunction then
if Event.IniObjectCategory ~= 3 then
self:E( { "Calling " .. _EVENTMETA[Event.id].Event .. " for Class ", EventClass:GetClassNameAndID(), EventPriority } )
self:E( { "Calling EventFunction for UNIT ", EventClass:GetClassNameAndID(), ", Unit ", Event.IniUnitName, EventPriority } )
end
local Result, Value = xpcall(
function()
return EventFunction( EventClass, Event )
return EventData.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 } )
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 ]
if EventFunction and type( EventFunction ) == "function" then
-- Now call the default event function.
if Event.IniObjectCategory ~= 3 then
self:E( { "Calling " .. EventMeta.Event .. " for Class ", EventClass:GetClassNameAndID(), EventPriority } )
end
local Result, Value = xpcall(
function()
return EventFunction( EventClass, Event )
end, ErrorHandler )
end
local Result, Value = xpcall(
function()
return EventFunction( EventClass, Event )
end, ErrorHandler )
end
end
end
else
-- The EventClass is not alive anymore, we remove it from the EventHandlers...
self:Remove( EventClass, Event.id )
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 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
-- So now the EventClass must be a GROUP class!!! We check if it is still "Alive".
if EventClass:IsAlive() or
Event.id == EVENTS.Crash or
Event.id == EVENTS.Dead 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
-- We can get the name of the EventClass, which is now always a GROUP object.
local GroupName = EventClass:GetName()
if ( EventMeta.Side == "I" and GroupName == Event.IniDCSGroupName ) or
( EventMeta.Side == "T" and GroupName == Event.TgtDCSGroupName ) then
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 or Event.WeaponUNIT) and not EventData.EventUnit ) or
Event.Cargo 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
self:E( { "Calling EventFunction for GROUP ", EventClass:GetClassNameAndID(), ", Unit ", Event.IniUnitName, 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 ]
local EventFunction = EventClass[ EventMeta.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 } )
self:E( { "Calling " .. EventMeta.Event .. " for GROUP ", EventClass:GetClassNameAndID(), EventPriority } )
end
local Result, Value = xpcall(
function()
local Result, Value = EventFunction( EventClass, Event )
return Result, Value
return EventFunction( EventClass, Event )
end, ErrorHandler )
end
end
end
else
-- The EventClass is not alive anymore, we remove it from the EventHandlers...
self:Remove( EventClass, Event.id )
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 not EventData.EventUnit 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 ]
if EventFunction and type( EventFunction ) == "function" then
-- Now call the default event function.
if Event.IniObjectCategory ~= 3 then
self:E( { "Calling " .. EventMeta.Event .. " for Class ", EventClass:GetClassNameAndID(), EventPriority } )
end
local Result, Value = xpcall(
function()
local Result, Value = EventFunction( EventClass, Event )
return Result, Value
end, ErrorHandler )
end
end
end
end
end
@@ -1052,7 +1019,7 @@ function EVENT:onEvent( Event )
end
end
else
self:E( { _EVENTMETA[Event.id].Text, Event } )
self:E( { EventMeta.Text, Event } )
end
Event = nil

View File

@@ -1,6 +1,6 @@
--- **Core** - The RADIO class is responsible for **transmitting radio communications**.
--
-- --- bitmap
-- ![Banner Image](..\Presentations\RADIO\Dia1.JPG)
--
-- ===
--
@@ -27,19 +27,20 @@
--
-- ===
--
-- ### Authors: Hugues "Grey_Echo" Bousquet
-- ### Author: Hugues "Grey_Echo" Bousquet
--
-- @module Radio
--- # 1) RADIO class, extends @{Base#BASE}
--
-- ## 1.1) RADIO usage
--
-- There are 3 steps to a successful radio transmission.
--
-- * First, you need to **"add" a @{#RADIO} object** to your @{Positionable#POSITIONABLE}. This is done using the @{Positionable#POSITIONABLE.GetRadio}() function,
-- * First, you need to **"add a @{#RADIO} object** to your @{Positionable#POSITIONABLE}. This is done using the @{Positionable#POSITIONABLE.GetRadio}() function,
-- * Then, you will **set the relevant parameters** to the transmission (see below),
-- * When done, you can actually **broadcast the transmission** (i.e. play the sound) with the @{Positionable#POSITIONABLE.Broadcast}() function.
-- * When done, you can actually **broadcast the transmission** (i.e. play the sound) with the @{RADIO.Broadcast}() function.
--
-- Methods to set relevant parameters for both a @{Unit#UNIT} or a @{Group#GROUP} or any other @{Positionable#POSITIONABLE}
--
@@ -53,7 +54,7 @@
-- * @{#RADIO.SetSubtitle}() : Set both the subtitle and its duration,
-- * @{#RADIO.NewUnitTransmission}() : Shortcut to set all the relevant parameters in one method call
--
-- Additional Methods to set relevant parameters if the transmiter is any other @{Wrapper.Positionable#POSITIONABLE}
-- Additional Methods to set relevant parameters if the transmiter is any other @{Positionable#POSITIONABLE}
--
-- * @{#RADIO.SetPower}() : Sets the power of the antenna in Watts
-- * @{#RADIO.NewGenericTransmission}() : Shortcut to set all the relevant parameters in one method call
@@ -68,7 +69,7 @@
-- * Note that if the transmission has a subtitle, it will be readable, regardless of the quality of the transmission.
--
-- @type RADIO
-- @field Wrapper.Positionable#POSITIONABLE Positionable The transmiter
-- @field Positionable#POSITIONABLE Positionable The transmiter
-- @field #string FileName Name of the sound file
-- @field #number Frequency Frequency of the transmission in Hz
-- @field #number Modulation Modulation of the transmission (either radio.modulation.AM or radio.modulation.FM)

View File

@@ -1374,7 +1374,7 @@ function SET_UNIT:ForEachUnitCompletelyInZone( ZoneObject, IteratorFunction, ...
--- @param Core.Zone#ZONE_BASE ZoneObject
-- @param Wrapper.Unit#UNIT UnitObject
function( ZoneObject, UnitObject )
if UnitObject:IsCompletelyInZone( ZoneObject ) then
if UnitObject:IsInZone( ZoneObject ) then
return true
else
return false

View File

@@ -0,0 +1,177 @@
--- **Core** -- Spawn dynamically new STATICs in your missions.
--
-- ![Banner Image](..\Presentations\SPAWNSTATIC\Dia1.JPG)
--
-- ====
--
-- SPAWNSTATIC spawns static structures in your missions dynamically. See below the SPAWNSTATIC class documentation.
--
-- ====
--
-- # Demo Missions
--
-- ### [SPAWNSTATIC Demo Missions source code](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release/SPS - Spawning Statics)
--
-- ### [SPAWNSTATIC Demo Missions, only for beta testers](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/SPS%20-%20Spawning%20Statics)
--
-- ### [ALL Demo Missions pack of the last release](https://github.com/FlightControl-Master/MOOSE_MISSIONS/releases)
--
-- ====
--
-- # YouTube Channel
--
-- ### [SPAWNSTATIC YouTube Channel]()
--
-- ====
--
-- # **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.
--
-- Hereby the change log:
--
-- ===
--
-- # **AUTHORS and CONTRIBUTIONS**
--
-- ### Contributions:
--
-- ### Authors:
--
-- * **FlightControl**: Design & Programming
--
-- @module SpawnStatic
--- @type SPAWNSTATIC
-- @extends Core.Base#BASE
--- # SPAWNSTATIC class, extends @{Base#BASE}
--
-- The SPAWNSTATIC class allows to spawn dynamically new @{Static}s.
-- Through creating a copy of an existing static object template as defined in the Mission Editor (ME),
-- SPAWNSTATIC can retireve the properties of the defined static object template (like type, category etc), and "copy"
-- these properties to create a new static object and place it at the desired coordinate.
--
-- New spawned @{Static}s get **the same name** as the name of the template Static,
-- or gets the given name when a new name is provided at the Spawn method.
-- By default, spawned @{Static}s will follow a naming convention at run-time:
--
-- * Spawned @{Static}s will have the name _StaticName_#_nnn_, where _StaticName_ is the name of the **Template Static**,
-- and _nnn_ is a **counter from 0 to 99999**.
--
--
-- ## SPAWNSTATIC construction methods
--
-- Create a new SPAWNSTATIC object with the @{#SPAWNSTATIC.NewFromStatic}():
--
-- * @{#SPAWNSTATIC.NewFromStatic}(): Creates a new SPAWNSTATIC object given a name that is used as the base of the naming of each spawned Static.
--
-- ## **Spawn** methods
--
-- Groups can be spawned at different times and methods:
--
-- * @{#SPAWNSTATIC.SpawnFromPointVec2}(): Spawn a new group from a POINT_VEC2 coordinate.
-- (The group will be spawned at land height ).
-- * @{#SPAWNSTATIC.SpawnFromZone}(): Spawn a new group in a @{Zone}.
--
-- @field #SPAWNSTATIC SPAWNSTATIC
--
SPAWNSTATIC = {
ClassName = "SPAWNSTATIC",
}
--- @type SPAWNSTATIC.SpawnZoneTable
-- @list <Core.Zone#ZONE_BASE> SpawnZone
--- Creates the main object to spawn a @{Static} defined in the ME.
-- @param #SPAWNSTATIC self
-- @param #string SpawnTemplatePrefix is the name of the Group in the ME that defines the Template. Each new group will have the name starting with SpawnTemplatePrefix.
-- @return #SPAWNSTATIC
function SPAWNSTATIC:NewFromStatic( SpawnTemplatePrefix, CountryID )
local self = BASE:Inherit( self, BASE:New() ) -- #SPAWNSTATIC
self:F( { SpawnTemplatePrefix } )
local TemplateStatic = StaticObject.getByName( SpawnTemplatePrefix )
if TemplateStatic then
self.SpawnTemplatePrefix = SpawnTemplatePrefix
self.CountryID = CountryID
self.SpawnIndex = 0
else
error( "SPAWNSTATIC:New: There is no group declared in the mission editor with SpawnTemplatePrefix = '" .. SpawnTemplatePrefix .. "'" )
end
self:SetEventPriority( 5 )
return self
end
--- Creates the main object to spawn a @{Static} based on a type name.
-- @param #SPAWNSTATIC self
-- @param #string SpawnTypeName is the name of the type.
-- @return #SPAWNSTATIC
function SPAWNSTATIC:NewFromType( SpawnTypeName, SpawnShapeName, SpawnCategory, CountryID )
local self = BASE:Inherit( self, BASE:New() ) -- #SPAWNSTATIC
self:F( { SpawnTypeName } )
self.SpawnTypeName = SpawnTypeName
self.CountryID = CountryID
self.SpawnIndex = 0
self:SetEventPriority( 5 )
return self
end
--- Creates a new @{Static} from a POINT_VEC2.
-- @param #SPAWNSTATIC self
-- @param Core.Point#POINT_VEC2 PointVec2 The 2D coordinate where to spawn the static.
-- @param #number Heading The heading of the static, which is a number in degrees from 0 to 360.
-- @param #string (optional) The name of the new static.
-- @return #SPAWNSTATIC
function SPAWNSTATIC:SpawnFromPointVec2( PointVec2, Heading, NewName )
self:F( { PointVec2, Heading, NewName } )
local CountryName = _DATABASE.COUNTRY_NAME[self.CountryID]
local StaticTemplate = _DATABASE:GetStaticUnitTemplate( self.SpawnTemplatePrefix )
StaticTemplate.x = PointVec2:GetLat()
StaticTemplate.y = PointVec2:GetLon()
StaticTemplate.name = NewName or string.format("%s#%05d", self.SpawnTemplatePrefix, self.SpawnIndex )
StaticTemplate.heading = ( Heading / 180 ) * math.pi
StaticTemplate.CountryID = nil
StaticTemplate.CoalitionID = nil
StaticTemplate.CategoryID = nil
local Static = coalition.addStaticObject( self.CountryID, StaticTemplate )
self.SpawnIndex = self.SpawnIndex + 1
return Static
end
--- Creates a new @{Static} from a @{Zone}.
-- @param #SPAWNSTATIC self
-- @param Core.Zone#ZONE_BASE Zone The Zone where to spawn the static.
-- @param #number Heading The heading of the static, which is a number in degrees from 0 to 360.
-- @param #string (optional) The name of the new static.
-- @return #SPAWNSTATIC
function SPAWNSTATIC:SpawnFromZone( Zone, Heading, NewName )
self:F( { Zone, Heading, NewName } )
local Static = self:SpawnFromPointVec2( Zone:GetPointVec2(), Heading, NewName )
return Static
end

View File

@@ -7,8 +7,25 @@
-- DETECTION classes facilitate the detection of enemy units within the battle zone executed by FACs (Forward Air Controllers) or RECCEs (Reconnassance Units).
-- DETECTION uses the in-built detection capabilities of DCS World, but adds new functionalities.
--
-- Please watch this [youtube video](https://youtu.be/C7p81dUwP-E) that explains the detection concepts.
-- Find the DETECTION classes documentation further in this document in the globals section.
--
-- ====
--
-- # Demo Missions
--
-- ### [DETECTION Demo Missions and Source Code](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release/DET%20-%20Detection)
--
-- ### [DETECTION Demo Missions, only for Beta Testers](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/DET%20-%20Detection)
--
-- ### [ALL Demo Missions pack of the Latest Release](https://github.com/FlightControl-Master/MOOSE_MISSIONS/releases)
--
-- ====
--
-- # YouTube Channel
--
-- ### [DETECTION YouTube Channel](https://www.youtube.com/playlist?list=PL7ZUrU4zZUl3Cf5jpI6BS0sBOVWK__tji)
--
-- ====
--
-- ### Contributions:
--
@@ -23,16 +40,24 @@
do -- DETECTION_BASE
--- # 1) DETECTION_BASE class, extends @{Fsm#FSM}
--- @type DETECTION_BASE
-- @field Core.Set#SET_GROUP DetectionSetGroup The @{Set} of GROUPs in the Forward Air Controller role.
-- @field Dcs.DCSTypes#Distance DetectionRange The range till which targets are accepted to be detected.
-- @field #DETECTION_BASE.DetectedObjects DetectedObjects The list of detected objects.
-- @field #table DetectedObjectsIdentified Map of the DetectedObjects identified.
-- @field #number DetectionRun
-- @extends Core.Fsm#FSM
--- DETECTION_BASE class, extends @{Fsm#FSM}
--
-- The DETECTION_BASE class defines the core functions to administer detected objects.
-- The DETECTION_BASE class will detect objects within the battle zone for a list of @{Group}s detecting targets following (a) detection method(s).
--
-- ## 1.1) DETECTION_BASE constructor
-- ## DETECTION_BASE constructor
--
-- Construct a new DETECTION_BASE instance using the @{#DETECTION_BASE.New}() method.
--
-- ## 1.2) DETECTION_BASE initialization
-- ## Initialization
--
-- By default, detection will return detected objects with all the detection sensors available.
-- However, you can ask how the objects were found with specific detection methods.
@@ -48,7 +73,29 @@ do -- DETECTION_BASE
-- * @{#DETECTION_BASE.InitDetectRWR}(): Detected using RWR.
-- * @{#DETECTION_BASE.InitDetectDLINK}(): Detected using DLINK.
--
-- ## 1.3) DETECTION_BASE derived classes group the detected units into a DetectedItems[] list
-- ## **Filter** detected units based on **category of the unit**
--
-- Filter the detected units based on Unit.Category using the method @{#DETECTION_BASE.FilterCategories}().
-- The different values of Unit.Category can be:
--
-- * Unit.Category.AIRPLANE
-- * Unit.Category.GROUND_UNIT
-- * Unit.Category.HELICOPTER
-- * Unit.Category.SHIP
-- * Unit.Category.STRUCTURE
--
-- Multiple Unit.Category entries can be given as a table and then these will be evaluated as an OR expression.
--
-- Example to filter a single category (Unit.Category.AIRPLANE).
--
-- DetectionObject:FilterCategories( Unit.Category.AIRPLANE )
--
-- Example to filter multiple categories (Unit.Category.AIRPLANE, Unit.Category.HELICOPTER). Note the {}.
--
-- DetectionObject:FilterCategories( { Unit.Category.AIRPLANE, Unit.Category.HELICOPTER } )
--
--
-- ## **DETECTION_ derived classes** group the detected units into a **DetectedItems[]** list
--
-- DETECTION_BASE derived classes build a list called DetectedItems[], which is essentially a first later
-- of grouping of detected units. Each DetectedItem within the DetectedItems[] list contains
@@ -67,7 +114,7 @@ do -- DETECTION_BASE
-- * A DetectedSet from the DetectedItems[] list can be retrieved using the method @{Detection#DETECTION_BASE.GetDetectedSet}( DetectedItemIndex ).
-- This method retrieves the Set from a DetectedItem element from the DetectedItem list (DetectedItems[ DetectedItemIndex ].Set ).
--
-- ## 1.4) Apply additional Filters to fine-tune the detected objects
-- ## **Visual filters** to fine-tune the probability of the detected objects
--
-- By default, DCS World will return any object that is in LOS and within "visual reach", or detectable through one of the electronic detection means.
-- That being said, the DCS World detection algorithm can sometimes be unrealistic.
@@ -88,7 +135,8 @@ do -- DETECTION_BASE
-- I advise however, that, when you first use the DETECTION derived classes, that you don't use these filters.
-- Only when you experience unrealistic behaviour in your missions, these filters could be applied.
--
-- ### 1.4.1 ) Distance visual detection probability
--
-- ### Distance visual detection probability
--
-- Upon a **visual** detection, the further away a detected object is, the less likely it is to be detected properly.
-- Also, the speed of accurate detection plays a role.
@@ -102,7 +150,7 @@ do -- DETECTION_BASE
--
-- Use the method @{Detection#DETECTION_BASE.SetDistanceProbability}() to set the probability factor upon a 10 km distance.
--
-- ### 1.4.2 ) Alpha Angle visual detection probability
-- ### Alpha Angle visual detection probability
--
-- Upon a **visual** detection, the higher the unit is during the detecting process, the more likely the detected unit is to be detected properly.
-- A detection at a 90% alpha angle is the most optimal, a detection at 10% is less and a detection at 0% is less likely to be correct.
@@ -114,7 +162,7 @@ do -- DETECTION_BASE
--
-- Use the method @{Detection#DETECTION_BASE.SetAlphaAngleProbability}() to set the probability factor if 0°.
--
-- ### 1.4.3 ) Cloudy Zones detection probability
-- ### Cloudy Zones detection probability
--
-- Upon a **visual** detection, the more a detected unit is within a cloudy zone, the less likely the detected unit is to be detected successfully.
-- The Cloudy Zones work with the ZONE_BASE derived classes. The mission designer can define within the mission
@@ -129,12 +177,12 @@ do -- DETECTION_BASE
-- Typically, this kind of filter would be applied for very specific areas were a detection needs to be very realisting for
-- AI not to detect so easily targets within a forrest or village rich area.
--
-- ## 1.5 ) Accept / Reject detected units
-- ## Accept / Reject detected units
--
-- DETECTION_BASE can accept or reject successful detections based on the location of the detected object,
-- if it is located in range or located inside or outside of specific zones.
--
-- ### 1.5.1 ) Detection acceptance of within range limit
-- ### Detection acceptance of within range limit
--
-- A range can be set that will limit a successful detection for a unit.
-- Use the method @{Detection#DETECTION_BASE.SetAcceptRange}() to apply a range in meters till where detected units will be accepted.
@@ -151,7 +199,7 @@ do -- DETECTION_BASE
-- Detection:Start()
--
--
-- ### 1.5.2 ) Detection acceptance if within zone(s).
-- ### Detection acceptance if within zone(s).
--
-- Specific ZONE_BASE object(s) can be given as a parameter, which will only accept a detection if the unit is within the specified ZONE_BASE object(s).
-- Use the method @{Detection#DETECTION_BASE.SetAcceptZones}() will accept detected units if they are within the specified zones.
@@ -171,7 +219,7 @@ do -- DETECTION_BASE
-- -- Start the Detection.
-- Detection:Start()
--
-- ### 1.5.3 ) Detection rejectance if within zone(s).
-- ### Detection rejectance if within zone(s).
--
-- Specific ZONE_BASE object(s) can be given as a parameter, which will reject detection if the unit is within the specified ZONE_BASE object(s).
-- Use the method @{Detection#DETECTION_BASE.SetRejectZones}() will reject detected units if they are within the specified zones.
@@ -192,29 +240,24 @@ do -- DETECTION_BASE
-- -- Start the Detection.
-- Detection:Start()
--
-- ## 1.6) DETECTION_BASE is a Finite State Machine
-- ## DETECTION_BASE is a Finite State Machine
--
-- Various Events and State Transitions can be tailored using DETECTION_BASE.
--
-- ### 1.6.1) DETECTION_BASE States
-- ### DETECTION_BASE States
--
-- * **Detecting**: The detection is running.
-- * **Stopped**: The detection is stopped.
--
-- ### 1.6.2) DETECTION_BASE Events
-- ### DETECTION_BASE Events
--
-- * **Start**: Start the detection process.
-- * **Detect**: Detect new units.
-- * **Detected**: New units have been detected.
-- * **Stop**: Stop the detection process.
--
-- @field #DETECTION_BASE DETECTION_BASE
--
-- @type DETECTION_BASE
-- @field Core.Set#SET_GROUP DetectionSetGroup The @{Set} of GROUPs in the Forward Air Controller role.
-- @field Dcs.DCSTypes#Distance DetectionRange The range till which targets are accepted to be detected.
-- @field #DETECTION_BASE.DetectedObjects DetectedObjects The list of detected objects.
-- @field #table DetectedObjectsIdentified Map of the DetectedObjects identified.
-- @field #number DetectionRun
-- @extends Core.Fsm#FSM
DETECTION_BASE = {
ClassName = "DETECTION_BASE",
DetectionSetGroup = nil,
@@ -267,11 +310,19 @@ do -- DETECTION_BASE
self.DetectionInterval = 30
self:InitDetectVisual( true )
self:InitDetectOptical( false )
self:InitDetectRadar( false )
self:InitDetectRWR( false )
self:InitDetectIRST( false )
self:InitDetectDLINK( false )
self:InitDetectOptical( true )
self:InitDetectRadar( true )
self:InitDetectRWR( true )
self:InitDetectIRST( true )
self:InitDetectDLINK( true )
self:FilterCategories( {
Unit.Category.AIRPLANE,
Unit.Category.GROUND_UNIT,
Unit.Category.HELICOPTER,
Unit.Category.SHIP,
Unit.Category.STRUCTURE
} )
-- Create FSM transitions.
@@ -498,9 +549,8 @@ do -- DETECTION_BASE
for DetectionObjectID, Detection in pairs( DetectedTargets ) do
local DetectedObject = Detection.object -- Dcs.DCSWrapper.Object#Object
self:T2( DetectedObject )
if DetectedObject and DetectedObject:isExist() and DetectedObject.id_ < 50000000 then
if DetectedObject and DetectedObject:isExist() and DetectedObject.id_ < 50000000 then -- and ( DetectedObject:getCategory() == Object.Category.UNIT or DetectedObject:getCategory() == Object.Category.STATIC ) then
local DetectionAccepted = true
@@ -515,10 +565,14 @@ do -- DETECTION_BASE
( DetectedObjectVec3.y - DetectionGroupVec3.y )^2 +
( DetectedObjectVec3.z - DetectionGroupVec3.z )^2
) ^ 0.5 / 1000
local DetectedUnitCategory = DetectedObject:getDesc().category
self:T( { "Detected Target", DetectionGroupName, DetectedObjectName, Distance } )
self:T( { "Detected Target:", DetectionGroupName, DetectedObjectName, Distance, DetectedUnitCategory, DetectedCategory } )
-- Calculate Acceptance
DetectionAccepted = self._.FilterCategories[DetectedUnitCategory] ~= nil and DetectionAccepted or false
if self.AcceptRange and Distance > self.AcceptRange then
DetectionAccepted = false
@@ -625,10 +679,10 @@ do -- DETECTION_BASE
end
if self.DetectionCount > 0 and self.DetectionRun == self.DetectionCount then
self:__Detect( self.DetectionInterval )
self:T( "--> Create Detection Sets" )
self:CreateDetectionSets()
self:__Detect( self.DetectionInterval )
end
end
@@ -645,6 +699,8 @@ do -- DETECTION_BASE
function DETECTION_BASE:InitDetectVisual( DetectVisual )
self.DetectVisual = DetectVisual
return self
end
--- Detect Optical.
@@ -655,6 +711,8 @@ do -- DETECTION_BASE
self:F2()
self.DetectOptical = DetectOptical
return self
end
--- Detect Radar.
@@ -665,6 +723,8 @@ do -- DETECTION_BASE
self:F2()
self.DetectRadar = DetectRadar
return self
end
--- Detect IRST.
@@ -675,6 +735,8 @@ do -- DETECTION_BASE
self:F2()
self.DetectIRST = DetectIRST
return self
end
--- Detect RWR.
@@ -685,6 +747,8 @@ do -- DETECTION_BASE
self:F2()
self.DetectRWR = DetectRWR
return self
end
--- Detect DLINK.
@@ -695,9 +759,52 @@ do -- DETECTION_BASE
self:F2()
self.DetectDLINK = DetectDLINK
return self
end
end
do -- Filter methods
--- Filter the detected units based on Unit.Category
-- The different values of Unit.Category can be:
--
-- * Unit.Category.AIRPLANE
-- * Unit.Category.GROUND_UNIT
-- * Unit.Category.HELICOPTER
-- * Unit.Category.SHIP
-- * Unit.Category.STRUCTURE
--
-- Multiple Unit.Category entries can be given as a table and then these will be evaluated as an OR expression.
--
-- Example to filter a single category (Unit.Category.AIRPLANE).
--
-- DetectionObject:FilterCategories( Unit.Category.AIRPLANE )
--
-- Example to filter multiple categories (Unit.Category.AIRPLANE, Unit.Category.HELICOPTER). Note the {}.
--
-- DetectionObject:FilterCategories( { Unit.Category.AIRPLANE, Unit.Category.HELICOPTER } )
--
-- @param #DETECTION_BASE self
-- @param #list<Dcs.DCSUnit#Unit> FilterCategories The Categories entries
-- @return #DETECTION_BASE self
function DETECTION_BASE:FilterCategories( FilterCategories )
self:F2()
self._.FilterCategories = {}
if type( FilterCategories ) == "table" then
for CategoryID, Category in pairs( FilterCategories ) do
self._.FilterCategories[Category] = Category
end
else
self._.FilterCategories[FilterCategories] = FilterCategories
end
return self
end
end
do

View File

@@ -1,112 +1,26 @@
--- Single-Player:**Yes** / Multi-Player:**Yes** / AI:**Yes** / Human:**No** / Types:**All** --
-- **Spawn groups of units dynamically in your missions.**
--- **Functional** -- Spawn dynamically new GROUPs in your missions.
--
-- ![Banner Image](..\Presentations\SPAWN\SPAWN.JPG)
--
-- ===
-- ====
--
-- # 1) @{#SPAWN} class, extends @{Base#BASE}
-- The documentation of the SPAWN class can be found further in this document.
--
-- The @{#SPAWN} class allows to spawn dynamically new groups, based on pre-defined initialization settings, modifying the behaviour when groups are spawned.
-- For each group to be spawned, within the mission editor, a group has to be created with the "late activation flag" set. We call this group the *"Spawn Template"* of the SPAWN object.
-- A reference to this Spawn Template needs to be provided when constructing the SPAWN object, by indicating the name of the group within the mission editor in the constructor methods.
-- ====
--
-- Within the SPAWN object, there is an internal index that keeps track of which group from the internal group list was spawned.
-- When new groups get spawned by using the SPAWN methods (see below), it will be validated whether the Limits (@{#SPAWN.Limit}) of the SPAWN object are not reached.
-- When all is valid, a new group will be created by the spawning methods, and the internal index will be increased with 1.
-- # Demo Missions
--
-- Regarding the name of new spawned groups, a _SpawnPrefix_ will be assigned for each new group created.
-- If you want to have the Spawn Template name to be used as the _SpawnPrefix_ name, use the @{#SPAWN.New} constructor.
-- However, when the @{#SPAWN.NewWithAlias} constructor was used, the Alias name will define the _SpawnPrefix_ name.
-- Groups will follow the following naming structure when spawned at run-time:
-- ### [SPAWN Demo Missions source code](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release/SPA%20-%20Spawning)
--
-- 1. Spawned groups will have the name _SpawnPrefix_#ggg, where ggg is a counter from 0 to 999.
-- 2. Spawned units will have the name _SpawnPrefix_#ggg-uu, where uu is a counter from 0 to 99 for each new spawned unit belonging to the group.
--
-- Some additional notes that need to be remembered:
--
-- * Templates are actually groups defined within the mission editor, with the flag "Late Activation" set. As such, these groups are never used within the mission, but are used by the @{#SPAWN} module.
-- * It is important to defined BEFORE you spawn new groups, a proper initialization of the SPAWN instance is done with the options you want to use.
-- * When designing a mission, NEVER name groups using a "#" within the name of the group Spawn Template(s), or the SPAWN module logic won't work anymore.
--
-- ## 1.1) SPAWN construction methods
--
-- Create a new SPAWN object with the @{#SPAWN.New}() or the @{#SPAWN.NewWithAlias}() methods:
--
-- * @{#SPAWN.New}(): Creates a new SPAWN object taking the name of the group that represents the GROUP Template (definition).
-- * @{#SPAWN.NewWithAlias}(): Creates a new SPAWN object taking the name of the group that represents the GROUP Template (definition), and gives each spawned @{Group} an different name.
-- ### [SPAWN Demo Missions, only for beta testers](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/SPA%20-%20Spawning)
--
-- It is important to understand how the SPAWN class works internally. The SPAWN object created will contain internally a list of groups that will be spawned and that are already spawned.
-- The initialization methods will modify this list of groups so that when a group gets spawned, ALL information is already prepared when spawning. This is done for performance reasons.
-- So in principle, the group list will contain all parameters and configurations after initialization, and when groups get actually spawned, this spawning can be done quickly and efficient.
--
-- ## 1.2) SPAWN initialization methods
-- ### [ALL Demo Missions pack of the last release](https://github.com/FlightControl-Master/MOOSE_MISSIONS/releases)
--
-- A spawn object will behave differently based on the usage of **initialization** methods, which all start with the **Init** prefix:
-- ====
--
-- * @{#SPAWN.InitKeepUnitNames}(): Keeps the unit names as defined within the mission editor, but note that anything after a # mark is ignored, and any spaces before and after the resulting name are removed. IMPORTANT! This method MUST be the first used after :New !!!
-- * @{#SPAWN.InitLimit}(): Limits the amount of groups that can be alive at the same time and that can be dynamically spawned.
-- * @{#SPAWN.InitRandomizeRoute}(): Randomize the routes of spawned groups, and for air groups also optionally the height.
-- * @{#SPAWN.InitRandomizeTemplate}(): Randomize the group templates so that when a new group is spawned, a random group template is selected from one of the templates defined.
-- * @{#SPAWN.InitUnControlled}(): Spawn plane groups uncontrolled.
-- * @{#SPAWN.InitArray}(): Make groups visible before they are actually activated, and order these groups like a batallion in an array.
-- * @{#SPAWN.InitRepeat}(): Re-spawn groups when they land at the home base. Similar methods are @{#SPAWN.InitRepeatOnLanding} and @{#SPAWN.InitRepeatOnEngineShutDown}.
-- * @{#SPAWN.InitRandomizePosition}(): Randomizes the position of @{Group}s that are spawned within a **radius band**, given an Outer and Inner radius, from the point that the spawn happens.
-- * @{#SPAWN.InitRandomizeUnits}(): Randomizes the @{Unit}s in the @{Group} that is spawned within a **radius band**, given an Outer and Inner radius.
-- * @{#SPAWN.InitRandomizeZones}(): Randomizes the spawning between a predefined list of @{Zone}s that are declared using this function. Each zone can be given a probability factor.
-- * @{#SPAWN.InitAIOn}(): Turns the AI On when spawning the new @{Group} object.
-- * @{#SPAWN.InitAIOff}(): Turns the AI Off when spawning the new @{Group} object.
-- * @{#SPAWN.InitAIOnOff}(): Turns the AI On or Off when spawning the new @{Group} object.
-- # YouTube Channel
--
-- ## 1.3) SPAWN spawning methods
--
-- Groups can be spawned at different times and methods:
--
-- * @{#SPAWN.Spawn}(): Spawn one new group based on the last spawned index.
-- * @{#SPAWN.ReSpawn}(): Re-spawn a group based on a given index.
-- * @{#SPAWN.SpawnScheduled}(): Spawn groups at scheduled but randomized intervals. You can use @{#SPAWN.SpawnScheduleStart}() and @{#SPAWN.SpawnScheduleStop}() to start and stop the schedule respectively.
-- * @{#SPAWN.SpawnFromVec3}(): Spawn a new group from a Vec3 coordinate. (The group will can be spawned at a point in the air).
-- * @{#SPAWN.SpawnFromVec2}(): Spawn a new group from a Vec2 coordinate. (The group will be spawned at land height ).
-- * @{#SPAWN.SpawnFromStatic}(): Spawn a new group from a structure, taking the position of a @{Static}.
-- * @{#SPAWN.SpawnFromUnit}(): Spawn a new group taking the position of a @{Unit}.
-- * @{#SPAWN.SpawnInZone}(): Spawn a new group in a @{Zone}.
--
-- Note that @{#SPAWN.Spawn} and @{#SPAWN.ReSpawn} return a @{GROUP#GROUP.New} object, that contains a reference to the DCSGroup object.
-- You can use the @{GROUP} object to do further actions with the DCSGroup.
--
-- ## 1.4) Retrieve alive GROUPs spawned by the SPAWN object
--
-- The SPAWN class administers which GROUPS it has reserved (in stock) or has created during mission execution.
-- Every time a SPAWN object spawns a new GROUP object, a reference to the GROUP object is added to an internal table of GROUPS.
-- SPAWN provides methods to iterate through that internal GROUP object reference table:
--
-- * @{#SPAWN.GetFirstAliveGroup}(): Will find the first alive GROUP it has spawned, and return the alive GROUP object and the first Index where the first alive GROUP object has been found.
-- * @{#SPAWN.GetNextAliveGroup}(): Will find the next alive GROUP object from a given Index, and return a reference to the alive GROUP object and the next Index where the alive GROUP has been found.
-- * @{#SPAWN.GetLastAliveGroup}(): Will find the last alive GROUP object, and will return a reference to the last live GROUP object and the last Index where the last alive GROUP object has been found.
--
-- You can use the methods @{#SPAWN.GetFirstAliveGroup}() and sequently @{#SPAWN.GetNextAliveGroup}() to iterate through the alive GROUPS within the SPAWN object, and to actions... See the respective methods for an example.
-- The method @{#SPAWN.GetGroupFromIndex}() will return the GROUP object reference from the given Index, dead or alive...
--
-- ## 1.5) SPAWN object cleaning
--
-- Sometimes, it will occur during a mission run-time, that ground or especially air objects get damaged, and will while being damged stop their activities, while remaining alive.
-- In such cases, the SPAWN object will just sit there and wait until that group gets destroyed, but most of the time it won't,
-- and it may occur that no new groups are or can be spawned as limits are reached.
-- To prevent this, a @{#SPAWN.InitCleanUp}() initialization method has been defined that will silently monitor the status of each spawned group.
-- Once a group has a velocity = 0, and has been waiting for a defined interval, that group will be cleaned or removed from run-time.
-- There is a catch however :-) If a damaged group has returned to an airbase within the coalition, that group will not be considered as "lost"...
-- In such a case, when the inactive group is cleaned, a new group will Re-spawned automatically.
-- This models AI that has succesfully returned to their airbase, to restart their combat activities.
-- Check the @{#SPAWN.InitCleanUp}() for further info.
--
-- ## 1.6) Catch the @{Group} spawn event in a callback function!
--
-- When using the SpawnScheduled method, new @{Group}s are created following the schedule timing parameters.
-- When a new @{Group} is spawned, you maybe want to execute actions with that group spawned at the spawn event.
-- To SPAWN class supports this functionality through the @{#SPAWN.OnSpawnGroup}( **function( SpawnedGroup ) end ** ) method, which takes a function as a parameter that you can define locally.
-- Whenever a new @{Group} is spawned, the given function is called, and the @{Group} that was just spawned, is given as a parameter.
-- As a result, your spawn event handling function requires one parameter to be declared, which will contain the spawned @{Group} object.
-- A coding example is provided at the description of the @{#SPAWN.OnSpawnGroup}( **function( SpawnedGroup ) end ** ) method.
-- ### [SPAWN YouTube Channel](https://www.youtube.com/playlist?list=PL7ZUrU4zZUl1jirWIo4t4YxqN-HxjqRkL)
--
-- ====
--
@@ -119,44 +33,34 @@
--
-- Hereby the change log:
--
-- 2017-04-08: SPAWN:**InitDelayOnOff( DelayOnOff )** added.
-- 2017-04-08: SPAWN:**InitDelayOn()** added.
-- 2017-04-08: SPAWN:**InitDelayOff()** added.
--
-- 2017-03-14: SPAWN:**InitKeepUnitNames()** added.
-- 2017-03-14: SPAWN:**InitRandomizePosition( RandomizePosition, OuterRadious, InnerRadius )** added.
-- 2017-03-14: SPAWN:**InitRandomizePosition( RandomizePosition, OuterRadious, InnerRadius )** added.
--
-- 2017-02-04: SPAWN:InitUnControlled( **UnControlled** ) replaces SPAWN:InitUnControlled().
-- 2017-02-04: SPAWN:InitUnControlled( **UnControlled** ) replaces SPAWN:InitUnControlled().
--
-- 2017-01-24: SPAWN:**InitAIOnOff( AIOnOff )** added.
--
-- 2017-01-24: SPAWN:**InitAIOn()** added.
--
-- 2017-01-24: SPAWN:**InitAIOff()** added.
--
-- 2016-08-15: SPAWN:**InitCleanUp**( SpawnCleanUpInterval ) replaces SPAWN:_CleanUp_( SpawnCleanUpInterval ).
-- 2017-01-24: SPAWN:**InitAIOnOff( AIOnOff )** added.
-- 2017-01-24: SPAWN:**InitAIOn()** added.
-- 2017-01-24: SPAWN:**InitAIOff()** added.
--
-- 2016-08-15: SPAWN:**InitCleanUp**( SpawnCleanUpInterval ) replaces SPAWN:_CleanUp_( SpawnCleanUpInterval ).
-- 2016-08-15: SPAWN:**InitRandomizeZones( SpawnZones )** added.
--
-- 2016-08-14: SPAWN:**OnSpawnGroup**( SpawnCallBackFunction, ... ) replaces SPAWN:_SpawnFunction_( SpawnCallBackFunction, ... ).
--
-- 2016-08-14: SPAWN.SpawnInZone( Zone, __RandomizeGroup__, SpawnIndex ) replaces SpawnInZone( Zone, _RandomizeUnits, OuterRadius, InnerRadius,_ SpawnIndex ).
--
-- 2016-08-14: SPAWN.SpawnFromVec3( Vec3, SpawnIndex ) replaces SpawnFromVec3( Vec3, _RandomizeUnits, OuterRadius, InnerRadius,_ SpawnIndex ):
--
-- 2016-08-14: SPAWN.SpawnFromVec2( Vec2, SpawnIndex ) replaces SpawnFromVec2( Vec2, _RandomizeUnits, OuterRadius, InnerRadius,_ SpawnIndex ):
--
-- 2016-08-14: SPAWN.SpawnFromUnit( SpawnUnit, SpawnIndex ) replaces SpawnFromUnit( SpawnUnit, _RandomizeUnits, OuterRadius, InnerRadius,_ SpawnIndex ):
--
-- 2016-08-14: SPAWN.SpawnFromUnit( SpawnUnit, SpawnIndex ) replaces SpawnFromStatic( SpawnStatic, _RandomizeUnits, OuterRadius, InnerRadius,_ SpawnIndex ):
--
-- 2016-08-14: SPAWN.**InitRandomizeUnits( RandomizeUnits, OuterRadius, InnerRadius )** added:
--
-- 2016-08-14: SPAWN.**Init**Limit( SpawnMaxUnitsAlive, SpawnMaxGroups ) replaces SPAWN._Limit_( SpawnMaxUnitsAlive, SpawnMaxGroups ):
--
-- 2016-08-14: SPAWN.**Init**Array( SpawnAngle, SpawnWidth, SpawnDeltaX, SpawnDeltaY ) replaces SPAWN._Array_( SpawnAngle, SpawnWidth, SpawnDeltaX, SpawnDeltaY ).
--
-- 2016-08-14: SPAWN.**Init**RandomizeRoute( SpawnStartPoint, SpawnEndPoint, SpawnRadius, SpawnHeight ) replaces SPAWN._RandomizeRoute_( SpawnStartPoint, SpawnEndPoint, SpawnRadius, SpawnHeight ).
--
-- 2016-08-14: SPAWN.**Init**RandomizeTemplate( SpawnTemplatePrefixTable ) replaces SPAWN._RandomizeTemplate_( SpawnTemplatePrefixTable ).
--
-- 2016-08-14: SPAWN.**Init**UnControlled() replaces SPAWN._UnControlled_().
-- 2016-08-14: SPAWN:**OnSpawnGroup**( SpawnCallBackFunction, ... ) replaces SPAWN:_SpawnFunction_( SpawnCallBackFunction, ... ).
-- 2016-08-14: SPAWN.SpawnInZone( Zone, __RandomizeGroup__, SpawnIndex ) replaces SpawnInZone( Zone, _RandomizeUnits, OuterRadius, InnerRadius,_ SpawnIndex ).
-- 2016-08-14: SPAWN.SpawnFromVec3( Vec3, SpawnIndex ) replaces SpawnFromVec3( Vec3, _RandomizeUnits, OuterRadius, InnerRadius,_ SpawnIndex ).
-- 2016-08-14: SPAWN.SpawnFromVec2( Vec2, SpawnIndex ) replaces SpawnFromVec2( Vec2, _RandomizeUnits, OuterRadius, InnerRadius,_ SpawnIndex ).
-- 2016-08-14: SPAWN.SpawnFromUnit( SpawnUnit, SpawnIndex ) replaces SpawnFromUnit( SpawnUnit, _RandomizeUnits, OuterRadius, InnerRadius,_ SpawnIndex ).
-- 2016-08-14: SPAWN.SpawnFromUnit( SpawnUnit, SpawnIndex ) replaces SpawnFromStatic( SpawnStatic, _RandomizeUnits, OuterRadius, InnerRadius,_ SpawnIndex ).
-- 2016-08-14: SPAWN.**InitRandomizeUnits( RandomizeUnits, OuterRadius, InnerRadius )** added.
-- 2016-08-14: SPAWN.**Init**Limit( SpawnMaxUnitsAlive, SpawnMaxGroups ) replaces SPAWN._Limit_( SpawnMaxUnitsAlive, SpawnMaxGroups ).
-- 2016-08-14: SPAWN.**Init**Array( SpawnAngle, SpawnWidth, SpawnDeltaX, SpawnDeltaY ) replaces SPAWN._Array_( SpawnAngle, SpawnWidth, SpawnDeltaX, SpawnDeltaY ).
-- 2016-08-14: SPAWN.**Init**RandomizeRoute( SpawnStartPoint, SpawnEndPoint, SpawnRadius, SpawnHeight ) replaces SPAWN._RandomizeRoute_( SpawnStartPoint, SpawnEndPoint, SpawnRadius, SpawnHeight ).
-- 2016-08-14: SPAWN.**Init**RandomizeTemplate( SpawnTemplatePrefixTable ) replaces SPAWN._RandomizeTemplate_( SpawnTemplatePrefixTable ).
-- 2016-08-14: SPAWN.**Init**UnControlled() replaces SPAWN._UnControlled_().
--
-- ===
--
@@ -177,7 +81,6 @@
--- SPAWN Class
-- @type SPAWN
-- @extends Core.Base#BASE
-- @field ClassName
-- @field #string SpawnTemplatePrefix
-- @field #string SpawnAliasPrefix
@@ -186,6 +89,214 @@
-- @field #number SpawnIndex
-- @field #number MaxAliveGroups
-- @field #SPAWN.SpawnZoneTable SpawnZoneTable
-- @extends Core.Base#BASE
--- # SPAWN class, extends @{Base#BASE}
--
-- The SPAWN class allows to spawn dynamically new groups.
-- Each SPAWN object needs to be have a related **template group** setup in the Mission Editor (ME),
-- which is a normal group with the **Late Activation** flag set.
-- This template group will never be activated in your mission.
-- SPAWN uses that **template group** to reference to all the characteristics
-- (air, ground, livery, unit composition, formation, skill level etc) of each new group to be spawned.
--
-- Therefore, when creating a SPAWN object, the @{#SPAWN.New} and @{#SPAWN.NewWithAlias} require
-- **the name of the template group** to be given as a string to those constructor methods.
--
-- Initialization settings can be applied on the SPAWN object,
-- which modify the behaviour or the way groups are spawned.
-- These initialization methods have the prefix **Init**.
-- There are also spawn methods with the prefix **Spawn** and will spawn new groups in various ways.
--
-- ### IMPORTANT! The methods with prefix **Init** must be used before any methods with prefix **Spawn** method are used, or unexpected results may appear!!!
--
-- Because SPAWN can spawn multiple groups of a template group,
-- SPAWN has an **internal index** that keeps track
-- which was the latest group that was spawned.
--
-- **Limits** can be set on how many groups can be spawn in each SPAWN object,
-- using the method @{#SPAWN.InitLimit}. SPAWN has 2 kind of limits:
--
-- * The maximum amount of @{Unit}s that can be **alive** at the same time...
-- * The maximum amount of @{Group}s that can be **spawned**... This is more of a **resource**-type of limit.
--
-- When new groups get spawned using the **Spawn** methods,
-- it will be evaluated whether any limits have been reached.
-- When no spawn limit is reached, a new group will be created by the spawning methods,
-- and the internal index will be increased with 1.
--
-- These limits ensure that your mission does not accidentally get flooded with spawned groups.
-- Additionally, it also guarantees that independent of the group composition,
-- at any time, the most optimal amount of groups are alive in your mission.
-- For example, if your template group has a group composition of 10 units, and you specify a limit of 100 units alive at the same time,
-- with unlimited resources = :InitLimit( 100, 0 ) and 10 groups are alive, but two groups have only one unit alive in the group,
-- then a sequent Spawn(Scheduled) will allow a new group to be spawned!!!
--
-- ### IMPORTANT!! If a limit has been reached, it is possible that a **Spawn** method returns **nil**, meaning, no @{Group} had been spawned!!!
--
-- Spawned groups get **the same name** as the name of the template group.
-- Spawned units in those groups keep _by default_ **the same name** as the name of the template group.
-- However, because multiple groups and units are created from the template group,
-- a suffix is added to each spawned group and unit.
--
-- Newly spawned groups will get the following naming structure at run-time:
--
-- 1. Spawned groups will have the name _GroupName_#_nnn_, where _GroupName_ is the name of the **template group**,
-- and _nnn_ is a **counter from 0 to 999**.
-- 2. Spawned units will have the name _GroupName_#_nnn_-_uu_,
-- where _uu_ is a **counter from 0 to 99** for each new spawned unit belonging to the group.
--
-- That being said, there is a way to keep the same unit names!
-- The method @{#SPAWN.InitKeepUnitNames}() will keep the same unit names as defined within the template group, thus:
--
-- 3. Spawned units will have the name _UnitName_#_nnn_-_uu_,
-- where _UnitName_ is the **unit name as defined in the template group*,
-- and _uu_ is a **counter from 0 to 99** for each new spawned unit belonging to the group.
--
-- Some **additional notes that need to be considered!!**:
--
-- * templates are actually groups defined within the mission editor, with the flag "Late Activation" set.
-- As such, these groups are never used within the mission, but are used by the @{#SPAWN} module.
-- * It is important to defined BEFORE you spawn new groups,
-- a proper initialization of the SPAWN instance is done with the options you want to use.
-- * When designing a mission, NEVER name groups using a "#" within the name of the group Spawn template(s),
-- or the SPAWN module logic won't work anymore.
--
-- ## SPAWN construction methods
--
-- Create a new SPAWN object with the @{#SPAWN.New}() or the @{#SPAWN.NewWithAlias}() methods:
--
-- * @{#SPAWN.New}(): Creates a new SPAWN object taking the name of the group that represents the GROUP template (definition).
-- * @{#SPAWN.NewWithAlias}(): Creates a new SPAWN object taking the name of the group that represents the GROUP template (definition), and gives each spawned @{Group} an different name.
--
-- It is important to understand how the SPAWN class works internally. The SPAWN object created will contain internally a list of groups that will be spawned and that are already spawned.
-- The initialization methods will modify this list of groups so that when a group gets spawned, ALL information is already prepared when spawning. This is done for performance reasons.
-- So in principle, the group list will contain all parameters and configurations after initialization, and when groups get actually spawned, this spawning can be done quickly and efficient.
--
-- ## SPAWN **Init**ialization methods
--
-- A spawn object will behave differently based on the usage of **initialization** methods, which all start with the **Init** prefix:
--
-- ### Unit Names
--
-- * @{#SPAWN.InitKeepUnitNames}(): Keeps the unit names as defined within the mission editor, but note that anything after a # mark is ignored, and any spaces before and after the resulting name are removed. IMPORTANT! This method MUST be the first used after :New !!!
--
-- ### Route randomization
--
-- * @{#SPAWN.InitRandomizeRoute}(): Randomize the routes of spawned groups, and for air groups also optionally the height.
--
-- ### Group composition randomization
--
-- * @{#SPAWN.InitRandomizeTemplate}(): Randomize the group templates so that when a new group is spawned, a random group template is selected from one of the templates defined.
--
-- ### Uncontrolled
--
-- * @{#SPAWN.InitUnControlled}(): Spawn plane groups uncontrolled.
--
-- ### Array formation
--
-- * @{#SPAWN.InitArray}(): Make groups visible before they are actually activated, and order these groups like a batallion in an array.
--
-- ### Position randomization
--
-- * @{#SPAWN.InitRandomizePosition}(): Randomizes the position of @{Group}s that are spawned within a **radius band**, given an Outer and Inner radius, from the point that the spawn happens.
-- * @{#SPAWN.InitRandomizeUnits}(): Randomizes the @{Unit}s in the @{Group} that is spawned within a **radius band**, given an Outer and Inner radius.
-- * @{#SPAWN.InitRandomizeZones}(): Randomizes the spawning between a predefined list of @{Zone}s that are declared using this function. Each zone can be given a probability factor.
--
-- ### Enable / Disable AI when spawning a new @{Group}
--
-- * @{#SPAWN.InitAIOn}(): Turns the AI On when spawning the new @{Group} object.
-- * @{#SPAWN.InitAIOff}(): Turns the AI Off when spawning the new @{Group} object.
-- * @{#SPAWN.InitAIOnOff}(): Turns the AI On or Off when spawning the new @{Group} object.
--
-- ### Limit scheduled spawning
--
-- * @{#SPAWN.InitLimit}(): Limits the amount of groups that can be alive at the same time and that can be dynamically spawned.
--
-- ### Delay initial scheduled spawn
--
-- * @{#SPAWN.InitDelayOnOff}(): Turns the inital delay On/Off when scheduled spawning the first @{Group} object.
-- * @{#SPAWN.InitDelayOn}(): Turns the inital delay On when scheduled spawning the first @{Group} object.
-- * @{#SPAWN.InitDelayOff}(): Turns the inital delay Off when scheduled spawning the first @{Group} object.
--
-- ### Repeat spawned @{Group}s upon landing
--
-- * @{#SPAWN.InitRepeat}() or @{#SPAWN.InitRepeatOnLanding}(): This method is used to re-spawn automatically the same group after it has landed.
-- * @{#SPAWN.InitRepeatOnEngineShutDown}(): This method is used to re-spawn automatically the same group after it has landed and it shuts down the engines at the ramp.
--
--
-- ## SPAWN **Spawn** methods
--
-- Groups can be spawned at different times and methods:
--
-- ### **Single** spawning methods
--
-- * @{#SPAWN.Spawn}(): Spawn one new group based on the last spawned index.
-- * @{#SPAWN.ReSpawn}(): Re-spawn a group based on a given index.
-- * @{#SPAWN.SpawnFromVec3}(): Spawn a new group from a Vec3 coordinate. (The group will can be spawned at a point in the air).
-- * @{#SPAWN.SpawnFromVec2}(): Spawn a new group from a Vec2 coordinate. (The group will be spawned at land height ).
-- * @{#SPAWN.SpawnFromStatic}(): Spawn a new group from a structure, taking the position of a @{Static}.
-- * @{#SPAWN.SpawnFromUnit}(): Spawn a new group taking the position of a @{Unit}.
-- * @{#SPAWN.SpawnInZone}(): Spawn a new group in a @{Zone}.
--
-- Note that @{#SPAWN.Spawn} and @{#SPAWN.ReSpawn} return a @{GROUP#GROUP.New} object, that contains a reference to the DCSGroup object.
-- You can use the @{GROUP} object to do further actions with the DCSGroup.
--
-- ### **Scheduled** spawning methods
--
-- * @{#SPAWN.SpawnScheduled}(): Spawn groups at scheduled but randomized intervals.
-- * @{#SPAWN.SpawnScheduledStart}(): Start or continue to spawn groups at scheduled time intervals.
-- * @{#SPAWN.SpawnScheduledStop}(): Stop the spawning of groups at scheduled time intervals.
--
--
--
-- ## Retrieve alive GROUPs spawned by the SPAWN object
--
-- The SPAWN class administers which GROUPS it has reserved (in stock) or has created during mission execution.
-- Every time a SPAWN object spawns a new GROUP object, a reference to the GROUP object is added to an internal table of GROUPS.
-- SPAWN provides methods to iterate through that internal GROUP object reference table:
--
-- * @{#SPAWN.GetFirstAliveGroup}(): Will find the first alive GROUP it has spawned, and return the alive GROUP object and the first Index where the first alive GROUP object has been found.
-- * @{#SPAWN.GetNextAliveGroup}(): Will find the next alive GROUP object from a given Index, and return a reference to the alive GROUP object and the next Index where the alive GROUP has been found.
-- * @{#SPAWN.GetLastAliveGroup}(): Will find the last alive GROUP object, and will return a reference to the last live GROUP object and the last Index where the last alive GROUP object has been found.
--
-- You can use the methods @{#SPAWN.GetFirstAliveGroup}() and sequently @{#SPAWN.GetNextAliveGroup}() to iterate through the alive GROUPS within the SPAWN object, and to actions... See the respective methods for an example.
-- The method @{#SPAWN.GetGroupFromIndex}() will return the GROUP object reference from the given Index, dead or alive...
--
-- ## Spawned cleaning of inactive groups
--
-- Sometimes, it will occur during a mission run-time, that ground or especially air objects get damaged, and will while being damged stop their activities, while remaining alive.
-- In such cases, the SPAWN object will just sit there and wait until that group gets destroyed, but most of the time it won't,
-- and it may occur that no new groups are or can be spawned as limits are reached.
-- To prevent this, a @{#SPAWN.InitCleanUp}() initialization method has been defined that will silently monitor the status of each spawned group.
-- Once a group has a velocity = 0, and has been waiting for a defined interval, that group will be cleaned or removed from run-time.
-- There is a catch however :-) If a damaged group has returned to an airbase within the coalition, that group will not be considered as "lost"...
-- In such a case, when the inactive group is cleaned, a new group will Re-spawned automatically.
-- This models AI that has succesfully returned to their airbase, to restart their combat activities.
-- Check the @{#SPAWN.InitCleanUp}() for further info.
--
-- ## Catch the @{Group} Spawn Event in a callback function!
--
-- When using the @{#SPAWN.SpawnScheduled)() method, new @{Group}s are created following the spawn time interval parameters.
-- When a new @{Group} is spawned, you maybe want to execute actions with that group spawned at the spawn event.
-- The SPAWN class supports this functionality through the method @{#SPAWN.OnSpawnGroup}( **function( SpawnedGroup ) end ** ),
-- which takes a function as a parameter that you can define locally.
-- Whenever a new @{Group} is spawned, the given function is called, and the @{Group} that was just spawned, is given as a parameter.
-- As a result, your spawn event handling function requires one parameter to be declared, which will contain the spawned @{Group} object.
-- A coding example is provided at the description of the @{#SPAWN.OnSpawnGroup}( **function( SpawnedGroup ) end ** ) method.
--
-- ## Delay the initial spawning
--
-- When using the @{#SPAWN.SpawnScheduled)() method, the default behaviour of this method will be that it will spawn the initial (first) @{Group}
-- immediately when :SpawnScheduled() is initiated. The methods @{#SPAWN.InitDelayOnOff}() and @{#SPAWN.InitDelayOn}() can be used to
-- activate a delay before the first @{Group} is spawned. For completeness, a method @{#SPAWN.InitDelayOff}() is also available, that
-- can be used to switch off the initial delay. Because there is no delay by default, this method would only be used when a
-- @{#SPAWN.SpawnScheduledStop}() ; @{#SPAWN.SpawnScheduledStart}() sequence would have been used.
--
--
-- @field #SPAWN SPAWN
--
SPAWN = {
ClassName = "SPAWN",
SpawnTemplatePrefix = nil,
@@ -227,6 +338,7 @@ function SPAWN:New( SpawnTemplatePrefix )
self.AIOnOff = true -- The AI is on by default when spawning a group.
self.SpawnUnControlled = false
self.SpawnInitKeepUnitNames = false -- Overwrite unit names by default with group name.
self.DelayOnOff = false -- No intial delay when spawning the first group.
self.SpawnGroups = {} -- Array containing the descriptions of each Group to be Spawned.
else
@@ -270,6 +382,7 @@ function SPAWN:NewWithAlias( SpawnTemplatePrefix, SpawnAliasPrefix )
self.AIOnOff = true -- The AI is on by default when spawning a group.
self.SpawnUnControlled = false
self.SpawnInitKeepUnitNames = false -- Overwrite unit names by default with group name.
self.DelayOnOff = false -- No intial delay when spawning the first group.
self.SpawnGroups = {} -- Array containing the descriptions of each Group to be Spawned.
else
@@ -626,6 +739,36 @@ do -- AI methods
end -- AI methods
do -- Delay methods
--- Turns the Delay On or Off for the first @{Group} scheduled spawning.
-- The default value is that for scheduled spawning, there is an initial delay when spawning the first @{Group}.
-- @param #SPAWN self
-- @param #boolean DelayOnOff A value of true sets the Delay On, a value of false sets the Delay Off.
-- @return #SPAWN The SPAWN object
function SPAWN:InitDelayOnOff( DelayOnOff )
self.DelayOnOff = DelayOnOff
return self
end
--- Turns the Delay On for the @{Group} when spawning.
-- @param #SPAWN self
-- @return #SPAWN The SPAWN object
function SPAWN:InitDelayOn()
return self:InitDelayOnOff( true )
end
--- Turns the Delay Off for the @{Group} when spawning.
-- @param #SPAWN self
-- @return #SPAWN The SPAWN object
function SPAWN:InitDelayOff()
return self:InitDelayOnOff( false )
end
end -- Delay methods
--- Will spawn a group based on the internal index.
-- Note: Uses @{DATABASE} module defined in MOOSE.
-- @param #SPAWN self
@@ -669,6 +812,8 @@ function SPAWN:ReSpawn( SpawnIndex )
SpawnGroup:ReSpawnFunction()
end
SpawnGroup:ResetEvents()
return SpawnGroup
end
@@ -787,7 +932,11 @@ function SPAWN:SpawnScheduled( SpawnTime, SpawnTimeVariation )
self:F( { SpawnTime, SpawnTimeVariation } )
if SpawnTime ~= nil and SpawnTimeVariation ~= nil then
self.SpawnScheduler = SCHEDULER:New( self, self._Scheduler, {}, 1, SpawnTime, SpawnTimeVariation )
local InitialDelay = 0
if self.DelayOnOff == true then
InitialDelay = math.random( SpawnTime - SpawnTime * SpawnTimeVariation, SpawnTime + SpawnTime * SpawnTimeVariation )
end
self.SpawnScheduler = SCHEDULER:New( self, self._Scheduler, {}, InitialDelay, SpawnTime, SpawnTimeVariation )
end
return self
@@ -795,17 +944,23 @@ end
--- Will re-start the spawning scheduler.
-- Note: This method is only required to be called when the schedule was stopped.
-- @param #SPAWN self
-- @return #SPAWN
function SPAWN:SpawnScheduleStart()
self:F( { self.SpawnTemplatePrefix } )
self.SpawnScheduler:Start()
return self
end
--- Will stop the scheduled spawning scheduler.
-- @param #SPAWN self
-- @return #SPAWN
function SPAWN:SpawnScheduleStop()
self:F( { self.SpawnTemplatePrefix } )
self.SpawnScheduler:Stop()
return self
end
@@ -926,7 +1081,7 @@ end
function SPAWN:SpawnFromUnit( HostUnit, SpawnIndex )
self:F( { self.SpawnTemplatePrefix, HostUnit, SpawnIndex } )
if HostUnit and HostUnit:IsAlive() then -- and HostUnit:getUnit(1):inAir() == false then
if HostUnit and HostUnit:IsAlive() ~= nil then -- and HostUnit:getUnit(1):inAir() == false then
return self:SpawnFromVec3( HostUnit:GetVec3(), SpawnIndex )
end

View File

@@ -834,6 +834,9 @@ function GROUP:Respawn( Template )
self:Destroy()
_DATABASE:Spawn( Template )
self:ResetEvents()
end
--- Returns the group template from the @{DATABASE} (_DATABASE object).
@@ -1077,7 +1080,21 @@ do -- Event Handling
-- @return #GROUP
function GROUP:UnHandleEvent( Event )
self:EventDispatcher():RemoveForGroup( self:GetName(), self, Event )
self:EventDispatcher():Remove( self, Event )
return self
end
--- Reset the subscriptions.
-- @param #GROUP self
-- @return #GROUP
function GROUP:ResetEvents()
self:EventDispatcher():Reset( self )
for UnitID, UnitData in pairs( self:GetUnits() ) do
UnitData:ResetEvents()
end
return self
end

View File

@@ -990,5 +990,16 @@ do -- Event Handling
return self
end
--- Reset the subscriptions.
-- @param #UNIT self
-- @return #UNIT
function UNIT:ResetEvents()
self:EventDispatcher():Reset( self )
return self
end
end