Merge branch 'develop' into tt/misc-bug-fixes

This commit is contained in:
ttrebuchon 2023-08-13 15:34:40 -04:00
commit a2772c97d5
16 changed files with 1405 additions and 294 deletions

View File

@ -147,8 +147,8 @@
-- @image Escorting.JPG
--- @type AI_ESCORT
---
-- @type AI_ESCORT
-- @extends AI.AI_Formation#AI_FORMATION
@ -168,11 +168,14 @@
--
-- -- First find the GROUP object and the CLIENT object.
-- local EscortUnit = CLIENT:FindByName( "Unit Name" ) -- The Unit Name is the name of the unit flagged with the skill Client in the mission editor.
-- local EscortGroup = GROUP:FindByName( "Group Name" ) -- The Group Name is the name of the group that will escort the Escort Client.
-- local EscortGroup = SET_GROUP:New():FilterPrefixes("Escort"):FilterOnce() -- The the group name of the escorts contains "Escort".
--
-- -- Now use these 2 objects to construct the new EscortPlanes object.
-- EscortPlanes = AI_ESCORT:New( EscortUnit, EscortGroup, "Desert", "Welcome to the mission. You are escorted by a plane with code name 'Desert', which can be instructed through the F10 radio menu." )
--
-- EscortPlanes:MenusAirplanes() -- create menus for airplanes
-- EscortPlanes:__Start(2)
--
--
-- @field #AI_ESCORT
AI_ESCORT = {
ClassName = "AI_ESCORT",
@ -189,7 +192,7 @@ AI_ESCORT = {
TaskPoints = {}
}
--- @field Functional.Detection#DETECTION_AREAS
-- @field Functional.Detection#DETECTION_AREAS
AI_ESCORT.Detection = nil
--- MENUPARAM type
@ -211,10 +214,14 @@ AI_ESCORT.Detection = nil
--
-- -- First find the GROUP object and the CLIENT object.
-- local EscortUnit = CLIENT:FindByName( "Unit Name" ) -- The Unit Name is the name of the unit flagged with the skill Client in the mission editor.
-- local EscortGroup = GROUP:FindByName( "Group Name" ) -- The Group Name is the name of the group that will escort the Escort Client.
-- local EscortGroup = SET_GROUP:New():FilterPrefixes("Escort"):FilterOnce() -- The the group name of the escorts contains "Escort".
--
-- -- Now use these 2 objects to construct the new EscortPlanes object.
-- EscortPlanes = AI_ESCORT:New( EscortUnit, EscortGroup, "Desert", "Welcome to the mission. You are escorted by a plane with code name 'Desert', which can be instructed through the F10 radio menu." )
-- EscortPlanes:MenusAirplanes() -- create menus for airplanes
-- EscortPlanes:__Start(2)
--
--
function AI_ESCORT:New( EscortUnit, EscortGroupSet, EscortName, EscortBriefing )
local self = BASE:Inherit( self, AI_FORMATION:New( EscortUnit, EscortGroupSet, EscortName, EscortBriefing ) ) -- #AI_ESCORT
@ -227,10 +234,17 @@ function AI_ESCORT:New( EscortUnit, EscortGroupSet, EscortName, EscortBriefing )
self.EscortGroupSet = EscortGroupSet
self.EscortGroupSet:SetSomeIteratorLimit( 8 )
self.EscortBriefing = EscortBriefing
self.Menu = {}
self.Menu.HoldAtEscortPosition = self.Menu.HoldAtEscortPosition or {}
self.Menu.HoldAtLeaderPosition = self.Menu.HoldAtLeaderPosition or {}
self.Menu.Flare = self.Menu.Flare or {}
self.Menu.Smoke = self.Menu.Smoke or {}
self.Menu.Targets = self.Menu.Targets or {}
self.Menu.ROE = self.Menu.ROE or {}
self.Menu.ROT = self.Menu.ROT or {}
-- if not EscortBriefing then
-- EscortGroup:MessageToClient( EscortGroup:GetCategoryName() .. " '" .. EscortName .. "' (" .. EscortGroup:GetCallsign() .. ") reporting! " ..
@ -250,7 +264,7 @@ function AI_ESCORT:New( EscortUnit, EscortGroupSet, EscortName, EscortBriefing )
EscortGroupSet:ForEachGroup(
--- @param Core.Group#GROUP EscortGroup
-- @param Core.Group#GROUP EscortGroup
function( EscortGroup )
-- Set EscortGroup known at EscortUnit.
if not self.PlayerUnit._EscortGroups then
@ -325,14 +339,14 @@ function AI_ESCORT:_InitEscortRoute( EscortGroup )
end
--- @param #AI_ESCORT self
-- @param #AI_ESCORT self
-- @param Core.Set#SET_GROUP EscortGroupSet
function AI_ESCORT:onafterStart( EscortGroupSet )
self:F()
EscortGroupSet:ForEachGroup(
--- @param Core.Group#GROUP EscortGroup
-- @param Core.Group#GROUP EscortGroup
function( EscortGroup )
EscortGroup:WayPointInitialize()
@ -370,7 +384,7 @@ function AI_ESCORT:onafterStart( EscortGroupSet )
self:_InitFlightMenus()
self.EscortGroupSet:ForSomeGroupAlive(
--- @param Core.Group#GROUP EscortGroup
-- @param Core.Group#GROUP EscortGroup
function( EscortGroup )
self:_InitEscortMenus( EscortGroup )
@ -378,7 +392,7 @@ function AI_ESCORT:onafterStart( EscortGroupSet )
self:SetFlightModeFormation( EscortGroup )
--- @param #AI_ESCORT self
-- @param #AI_ESCORT self
-- @param Core.Event#EVENTDATA EventData
function EscortGroup:OnEventDeadOrCrash( EventData )
self:F( { "EventDead", EventData } )
@ -394,14 +408,14 @@ function AI_ESCORT:onafterStart( EscortGroupSet )
end
--- @param #AI_ESCORT self
-- @param #AI_ESCORT self
-- @param Core.Set#SET_GROUP EscortGroupSet
function AI_ESCORT:onafterStop( EscortGroupSet )
self:F()
EscortGroupSet:ForEachGroup(
--- @param Core.Group#GROUP EscortGroup
-- @param Core.Group#GROUP EscortGroup
function( EscortGroup )
EscortGroup:WayPointInitialize()
@ -550,7 +564,7 @@ function AI_ESCORT:SetFlightMenuFormation( Formation )
local MenuFlightFormationID = MENU_GROUP_COMMAND:New( self.PlayerGroup, Formation, FlightMenuFormation,
function ( self, Formation, ... )
self.EscortGroupSet:ForSomeGroupAlive(
--- @param Core.Group#GROUP EscortGroup
-- @param Core.Group#GROUP EscortGroup
function( EscortGroup, self, Formation, Arguments )
if EscortGroup:IsAir() then
self:E({FormationID=FormationID})
@ -764,7 +778,7 @@ end
function AI_ESCORT:SetFlightMenuHoldAtEscortPosition()
for _, MenuHoldAtEscortPosition in pairs( self.Menu.HoldAtEscortPosition ) do
for _, MenuHoldAtEscortPosition in pairs( self.Menu.HoldAtEscortPosition or {} ) do
local FlightMenuReportNavigation = MENU_GROUP:New( self.PlayerGroup, "Navigation", self.FlightMenu )
local FlightMenuHoldPosition = MENU_GROUP_COMMAND
@ -785,7 +799,7 @@ end
function AI_ESCORT:SetEscortMenuHoldAtEscortPosition( EscortGroup )
for _, HoldAtEscortPosition in pairs( self.Menu.HoldAtEscortPosition ) do
for _, HoldAtEscortPosition in pairs( self.Menu.HoldAtEscortPosition or {}) do
if EscortGroup:IsAir() then
local EscortGroupName = EscortGroup:GetName()
local EscortMenuReportNavigation = MENU_GROUP:New( self.PlayerGroup, "Navigation", EscortGroup.EscortMenu )
@ -853,7 +867,7 @@ end
function AI_ESCORT:SetFlightMenuHoldAtLeaderPosition()
for _, MenuHoldAtLeaderPosition in pairs( self.Menu.HoldAtLeaderPosition ) do
for _, MenuHoldAtLeaderPosition in pairs( self.Menu.HoldAtLeaderPosition or {}) do
local FlightMenuReportNavigation = MENU_GROUP:New( self.PlayerGroup, "Navigation", self.FlightMenu )
local FlightMenuHoldAtLeaderPosition = MENU_GROUP_COMMAND
@ -874,7 +888,7 @@ end
function AI_ESCORT:SetEscortMenuHoldAtLeaderPosition( EscortGroup )
for _, HoldAtLeaderPosition in pairs( self.Menu.HoldAtLeaderPosition ) do
for _, HoldAtLeaderPosition in pairs( self.Menu.HoldAtLeaderPosition or {}) do
if EscortGroup:IsAir() then
local EscortGroupName = EscortGroup:GetName()
@ -999,7 +1013,7 @@ end
function AI_ESCORT:SetFlightMenuFlare()
for _, MenuFlare in pairs( self.Menu.Flare) do
for _, MenuFlare in pairs( self.Menu.Flare or {}) do
local FlightMenuReportNavigation = MENU_GROUP:New( self.PlayerGroup, "Navigation", self.FlightMenu )
local FlightMenuFlare = MENU_GROUP:New( self.PlayerGroup, MenuFlare.MenuText, FlightMenuReportNavigation )
@ -1014,7 +1028,7 @@ end
function AI_ESCORT:SetEscortMenuFlare( EscortGroup )
for _, MenuFlare in pairs( self.Menu.Flare) do
for _, MenuFlare in pairs( self.Menu.Flare or {}) do
if EscortGroup:IsAir() then
local EscortGroupName = EscortGroup:GetName()
@ -1059,7 +1073,7 @@ end
function AI_ESCORT:SetFlightMenuSmoke()
for _, MenuSmoke in pairs( self.Menu.Smoke) do
for _, MenuSmoke in pairs( self.Menu.Smoke or {}) do
local FlightMenuReportNavigation = MENU_GROUP:New( self.PlayerGroup, "Navigation", self.FlightMenu )
local FlightMenuSmoke = MENU_GROUP:New( self.PlayerGroup, MenuSmoke.MenuText, FlightMenuReportNavigation )
@ -1076,7 +1090,7 @@ end
function AI_ESCORT:SetEscortMenuSmoke( EscortGroup )
for _, MenuSmoke in pairs( self.Menu.Smoke) do
for _, MenuSmoke in pairs( self.Menu.Smoke or {}) do
if EscortGroup:IsAir() then
local EscortGroupName = EscortGroup:GetName()
@ -1169,7 +1183,7 @@ function AI_ESCORT:SetFlightMenuTargets()
local FlightMenuAttackNearbyAir = MENU_GROUP_COMMAND:New( self.PlayerGroup, "Attack nearest airborne targets", self.FlightMenuAttack, AI_ESCORT._FlightAttackNearestTarget, self, self.__Enum.ReportType.Air ):SetTag( "Attack" )
local FlightMenuAttackNearbyGround = MENU_GROUP_COMMAND:New( self.PlayerGroup, "Attack nearest ground targets", self.FlightMenuAttack, AI_ESCORT._FlightAttackNearestTarget, self, self.__Enum.ReportType.Ground ):SetTag( "Attack" )
for _, MenuTargets in pairs( self.Menu.Targets) do
for _, MenuTargets in pairs( self.Menu.Targets or {}) do
MenuTargets.FlightReportTargetsScheduler = SCHEDULER:New( self, self._FlightReportTargetsScheduler, {}, MenuTargets.Interval, MenuTargets.Interval )
end
@ -1179,7 +1193,7 @@ end
function AI_ESCORT:SetEscortMenuTargets( EscortGroup )
for _, MenuTargets in pairs( self.Menu.Targets) do
for _, MenuTargets in pairs( self.Menu.Targets or {} or {}) do
if EscortGroup:IsAir() then
local EscortGroupName = EscortGroup:GetName()
--local EscortMenuReportTargets = MENU_GROUP:New( self.PlayerGroup, "Report targets", EscortGroup.EscortMenu )
@ -1231,7 +1245,7 @@ function AI_ESCORT:MenuAssistedAttack()
self:F()
self.EscortGroupSet:ForSomeGroupAlive(
--- @param Core.Group#GROUP EscortGroup
-- @param Core.Group#GROUP EscortGroup
function( EscortGroup )
if not EscortGroup:IsAir() then
-- Request assistance from other escorts.
@ -1246,7 +1260,7 @@ end
function AI_ESCORT:SetFlightMenuROE()
for _, MenuROE in pairs( self.Menu.ROE) do
for _, MenuROE in pairs( self.Menu.ROE or {}) do
local FlightMenuROE = MENU_GROUP:New( self.PlayerGroup, "Rule Of Engagement", self.FlightMenu )
local FlightMenuROEHoldFire = MENU_GROUP_COMMAND:New( self.PlayerGroup, "Hold fire", FlightMenuROE, AI_ESCORT._FlightROEHoldFire, self, "Holding weapons!" )
@ -1261,7 +1275,7 @@ end
function AI_ESCORT:SetEscortMenuROE( EscortGroup )
for _, MenuROE in pairs( self.Menu.ROE) do
for _, MenuROE in pairs( self.Menu.ROE or {}) do
if EscortGroup:IsAir() then
local EscortGroupName = EscortGroup:GetName()
@ -1302,7 +1316,7 @@ end
function AI_ESCORT:SetFlightMenuROT()
for _, MenuROT in pairs( self.Menu.ROT) do
for _, MenuROT in pairs( self.Menu.ROT or {}) do
local FlightMenuROT = MENU_GROUP:New( self.PlayerGroup, "Reaction On Threat", self.FlightMenu )
local FlightMenuROTNoReaction = MENU_GROUP_COMMAND:New( self.PlayerGroup, "Fight until death", FlightMenuROT, AI_ESCORT._FlightROTNoReaction, self, "Fighting until death!" )
@ -1317,7 +1331,7 @@ end
function AI_ESCORT:SetEscortMenuROT( EscortGroup )
for _, MenuROT in pairs( self.Menu.ROT) do
for _, MenuROT in pairs( self.Menu.ROT or {}) do
if EscortGroup:IsAir() then
local EscortGroupName = EscortGroup:GetName()
@ -1375,7 +1389,7 @@ function AI_ESCORT:SetEscortMenuResumeMission( EscortGroup )
end
--- @param #AI_ESCORT self
-- @param #AI_ESCORT self
-- @param Wrapper.Group#GROUP OrbitGroup
-- @param Wrapper.Group#GROUP EscortGroup
-- @param #number OrbitHeight
@ -1419,7 +1433,7 @@ function AI_ESCORT:_HoldPosition( OrbitGroup, EscortGroup, OrbitHeight, OrbitSec
end
--- @param #AI_ESCORT self
-- @param #AI_ESCORT self
-- @param Wrapper.Group#GROUP OrbitGroup
-- @param #number OrbitHeight
-- @param #number OrbitSeconds
@ -1428,7 +1442,7 @@ function AI_ESCORT:_FlightHoldPosition( OrbitGroup, OrbitHeight, OrbitSeconds )
local EscortUnit = self.PlayerUnit
self.EscortGroupSet:ForEachGroupAlive(
--- @param Core.Group#GROUP EscortGroup
-- @param Core.Group#GROUP EscortGroup
function( EscortGroup, OrbitGroup )
if EscortGroup:IsAir() then
if OrbitGroup == nil then
@ -1456,7 +1470,7 @@ end
function AI_ESCORT:_FlightJoinUp()
self.EscortGroupSet:ForEachGroupAlive(
--- @param Core.Group#GROUP EscortGroup
-- @param Core.Group#GROUP EscortGroup
function( EscortGroup )
if EscortGroup:IsAir() then
self:_JoinUp( EscortGroup )
@ -1483,7 +1497,7 @@ end
function AI_ESCORT:_FlightFormationTrail( XStart, XSpace, YStart )
self.EscortGroupSet:ForEachGroupAlive(
--- @param Core.Group#GROUP EscortGroup
-- @param Core.Group#GROUP EscortGroup
function( EscortGroup )
if EscortGroup:IsAir() then
self:_EscortFormationTrail( EscortGroup, XStart, XSpace, YStart )
@ -1510,7 +1524,7 @@ end
function AI_ESCORT:_FlightFormationStack( XStart, XSpace, YStart, YSpace )
self.EscortGroupSet:ForEachGroupAlive(
--- @param Core.Group#GROUP EscortGroup
-- @param Core.Group#GROUP EscortGroup
function( EscortGroup )
if EscortGroup:IsAir() then
self:_EscortFormationStack( EscortGroup, XStart, XSpace, YStart, YSpace )
@ -1533,7 +1547,7 @@ end
function AI_ESCORT:_FlightFlare( Color, Message )
self.EscortGroupSet:ForEachGroupAlive(
--- @param Core.Group#GROUP EscortGroup
-- @param Core.Group#GROUP EscortGroup
function( EscortGroup )
if EscortGroup:IsAir() then
self:_Flare( EscortGroup, Color, Message )
@ -1556,7 +1570,7 @@ end
function AI_ESCORT:_FlightSmoke( Color, Message )
self.EscortGroupSet:ForEachGroupAlive(
--- @param Core.Group#GROUP EscortGroup
-- @param Core.Group#GROUP EscortGroup
function( EscortGroup )
if EscortGroup:IsAir() then
self:_Smoke( EscortGroup, Color, Message )
@ -1587,7 +1601,7 @@ end
function AI_ESCORT:_FlightSwitchReportNearbyTargets( ReportTargets )
self.EscortGroupSet:ForEachGroupAlive(
--- @param Core.Group#GROUP EscortGroup
-- @param Core.Group#GROUP EscortGroup
function( EscortGroup )
if EscortGroup:IsAir() then
self:_EscortSwitchReportNearbyTargets( EscortGroup, ReportTargets )
@ -1679,7 +1693,7 @@ function AI_ESCORT:_ScanTargets( ScanDuration )
end
--- @param Wrapper.Group#GROUP EscortGroup
-- @param Wrapper.Group#GROUP EscortGroup
-- @param #AI_ESCORT self
function AI_ESCORT.___Resume( EscortGroup, self )
@ -1701,7 +1715,7 @@ function AI_ESCORT.___Resume( EscortGroup, self )
end
--- @param #AI_ESCORT self
-- @param #AI_ESCORT self
-- @param Wrapper.Group#GROUP EscortGroup
-- @param #number WayPoint
function AI_ESCORT:_ResumeMission( EscortGroup, WayPoint )
@ -1723,7 +1737,7 @@ function AI_ESCORT:_ResumeMission( EscortGroup, WayPoint )
end
--- @param #AI_ESCORT self
-- @param #AI_ESCORT self
-- @param Wrapper.Group#GROUP EscortGroup The escort group that will attack the detected item.
-- @param Functional.Detection#DETECTION_BASE.DetectedItem DetectedItem
function AI_ESCORT:_AttackTarget( EscortGroup, DetectedItem )
@ -1743,7 +1757,7 @@ function AI_ESCORT:_AttackTarget( EscortGroup, DetectedItem )
local AttackUnitTasks = {}
DetectedSet:ForEachUnit(
--- @param Wrapper.Unit#UNIT DetectedUnit
-- @param Wrapper.Unit#UNIT DetectedUnit
function( DetectedUnit, Tasks )
if DetectedUnit:IsAlive() then
AttackUnitTasks[#AttackUnitTasks+1] = EscortGroup:TaskAttackUnit( DetectedUnit )
@ -1767,7 +1781,7 @@ function AI_ESCORT:_AttackTarget( EscortGroup, DetectedItem )
local Tasks = {}
DetectedSet:ForEachUnit(
--- @param Wrapper.Unit#UNIT DetectedUnit
-- @param Wrapper.Unit#UNIT DetectedUnit
function( DetectedUnit, Tasks )
if DetectedUnit:IsAlive() then
Tasks[#Tasks+1] = EscortGroup:TaskFireAtPoint( DetectedUnit:GetVec2(), 50 )
@ -1795,7 +1809,7 @@ end
function AI_ESCORT:_FlightAttackTarget( DetectedItem )
self.EscortGroupSet:ForEachGroupAlive(
--- @param Core.Group#GROUP EscortGroup
-- @param Core.Group#GROUP EscortGroup
function( EscortGroup, DetectedItem )
if EscortGroup:IsAir() then
self:_AttackTarget( EscortGroup, DetectedItem )
@ -1842,7 +1856,7 @@ end
---
--- @param #AI_ESCORT self
-- @param #AI_ESCORT self
-- @param Wrapper.Group#GROUP EscortGroup The escort group that will attack the detected item.
-- @param Functional.Detection#DETECTION_BASE.DetectedItem DetectedItem
function AI_ESCORT:_AssistTarget( EscortGroup, DetectedItem )
@ -1854,7 +1868,7 @@ function AI_ESCORT:_AssistTarget( EscortGroup, DetectedItem )
local Tasks = {}
DetectedSet:ForEachUnit(
--- @param Wrapper.Unit#UNIT DetectedUnit
-- @param Wrapper.Unit#UNIT DetectedUnit
function( DetectedUnit, Tasks )
if DetectedUnit:IsAlive() then
Tasks[#Tasks+1] = EscortGroup:TaskFireAtPoint( DetectedUnit:GetVec2(), 50 )
@ -1881,7 +1895,7 @@ end
function AI_ESCORT:_FlightROEHoldFire( EscortROEMessage )
self.EscortGroupSet:ForEachGroupAlive(
--- @param Wrapper.Group#GROUP EscortGroup
-- @param Wrapper.Group#GROUP EscortGroup
function( EscortGroup )
self:_ROE( EscortGroup, EscortGroup.OptionROEHoldFire, EscortROEMessage )
end
@ -1890,7 +1904,7 @@ end
function AI_ESCORT:_FlightROEOpenFire( EscortROEMessage )
self.EscortGroupSet:ForEachGroupAlive(
--- @param Wrapper.Group#GROUP EscortGroup
-- @param Wrapper.Group#GROUP EscortGroup
function( EscortGroup )
self:_ROE( EscortGroup, EscortGroup.OptionROEOpenFire, EscortROEMessage )
end
@ -1899,7 +1913,7 @@ end
function AI_ESCORT:_FlightROEReturnFire( EscortROEMessage )
self.EscortGroupSet:ForEachGroupAlive(
--- @param Wrapper.Group#GROUP EscortGroup
-- @param Wrapper.Group#GROUP EscortGroup
function( EscortGroup )
self:_ROE( EscortGroup, EscortGroup.OptionROEReturnFire, EscortROEMessage )
end
@ -1908,7 +1922,7 @@ end
function AI_ESCORT:_FlightROEWeaponFree( EscortROEMessage )
self.EscortGroupSet:ForEachGroupAlive(
--- @param Wrapper.Group#GROUP EscortGroup
-- @param Wrapper.Group#GROUP EscortGroup
function( EscortGroup )
self:_ROE( EscortGroup, EscortGroup.OptionROEWeaponFree, EscortROEMessage )
end
@ -1924,7 +1938,7 @@ end
function AI_ESCORT:_FlightROTNoReaction( EscortROTMessage )
self.EscortGroupSet:ForEachGroupAlive(
--- @param Wrapper.Group#GROUP EscortGroup
-- @param Wrapper.Group#GROUP EscortGroup
function( EscortGroup )
self:_ROT( EscortGroup, EscortGroup.OptionROTNoReaction, EscortROTMessage )
end
@ -1933,7 +1947,7 @@ end
function AI_ESCORT:_FlightROTPassiveDefense( EscortROTMessage )
self.EscortGroupSet:ForEachGroupAlive(
--- @param Wrapper.Group#GROUP EscortGroup
-- @param Wrapper.Group#GROUP EscortGroup
function( EscortGroup )
self:_ROT( EscortGroup, EscortGroup.OptionROTPassiveDefense, EscortROTMessage )
end
@ -1942,7 +1956,7 @@ end
function AI_ESCORT:_FlightROTEvadeFire( EscortROTMessage )
self.EscortGroupSet:ForEachGroupAlive(
--- @param Wrapper.Group#GROUP EscortGroup
-- @param Wrapper.Group#GROUP EscortGroup
function( EscortGroup )
self:_ROT( EscortGroup, EscortGroup.OptionROTEvadeFire, EscortROTMessage )
end
@ -1951,7 +1965,7 @@ end
function AI_ESCORT:_FlightROTVertical( EscortROTMessage )
self.EscortGroupSet:ForEachGroupAlive(
--- @param Wrapper.Group#GROUP EscortGroup
-- @param Wrapper.Group#GROUP EscortGroup
function( EscortGroup )
self:_ROT( EscortGroup, EscortGroup.OptionROTVertical, EscortROTMessage )
end
@ -2178,5 +2192,3 @@ function AI_ESCORT:_FlightReportTargetsScheduler()
return false
end

View File

@ -381,7 +381,7 @@ end
CLIENTMENUMANAGER = {
ClassName = "CLIENTMENUMANAGER",
lid = "",
version = "0.1.0",
version = "0.1.1",
name = nil,
clientset = nil,
menutree = {},
@ -670,7 +670,7 @@ end
--- Remove the entry and all entries below the given entry from the client's F10 menus.
-- @param #CLIENTMENUMANAGER self
-- @param #CLIENTMENU Entry The entry to remove
-- @param Wrapper.Client#CLIENT Client (optional) If given, make this change only for this client. In this case the generic structure will not be touched.
-- @param Wrapper.Client#CLIENT Client (optional) If given, make this change only for this client.
-- @return #CLIENTMENUMANAGER self
function CLIENTMENUMANAGER:DeleteF10Entry(Entry,Client)
self:T(self.lid.."DeleteF10Entry")
@ -783,3 +783,4 @@ end
-- End ClientMenu
--
----------------------------------------------------------------------------------------------------------------

View File

@ -2389,7 +2389,6 @@ do -- COORDINATE
end
--- Creates a free form shape on the F10 map. The first point is the current COORDINATE. The remaining points need to be specified.
-- **NOTE**: A free form polygon must have **at least three points** in total and currently only **up to 15 points** in total are supported.
-- @param #COORDINATE self
-- @param #table Coordinates Table of coordinates of the remaining points of the shape.
-- @param #number Coalition Coalition: All=-1, Neutral=0, Red=1, Blue=2. Default -1=All.
@ -2463,9 +2462,30 @@ do -- COORDINATE
vecs[11], vecs[12], vecs[13], vecs[14], vecs[15],
Color, FillColor, LineType, ReadOnly, Text or "")
else
self:E("ERROR: Currently a free form polygon can only have 15 points in total!")
-- Unfortunately, unpack(vecs) does not work! So no idea how to generalize this :(
trigger.action.markupToAll(7, Coalition, MarkID, unpack(vecs), Color, FillColor, LineType, ReadOnly, Text or "")
--trigger.action.markupToAll(7, Coalition, MarkID, unpack(vecs), Color, FillColor, LineType, ReadOnly, Text or "")
-- Write command as string and execute that. Idea by Grimes https://forum.dcs.world/topic/324201-mark-to-all-function/#comment-5273793
local s=string.format("trigger.action.markupToAll(7, %d, %d,", Coalition, MarkID)
for _,vec in pairs(vecs) do
s=s..string.format("%s,", UTILS._OneLineSerialize(vec))
end
s=s..string.format("%s, %s, %s, %s", UTILS._OneLineSerialize(Color), UTILS._OneLineSerialize(FillColor), tostring(LineType), tostring(ReadOnly))
if Text and Text~="" then
s=s..string.format(", \"%s\"", Text)
end
s=s..")"
-- Execute string command
local success=UTILS.DoString(s)
if not success then
self:E("ERROR: Could not draw polygon")
env.info(s)
end
end
return MarkID

View File

@ -46,8 +46,9 @@
-- @image Core_Sets.JPG
do -- SET_BASE
--- @type SET_BASE
---
-- @type SET_BASE
-- @field #table Filter Table of filters.
-- @field #table Set Table of objects.
-- @field #table Index Table of indices.
@ -242,7 +243,7 @@ do -- SET_BASE
function SET_BASE:Add( ObjectName, Object )
-- Debug info.
self:T( { ObjectName = ObjectName, Object = Object } )
self:T2( { ObjectName = ObjectName, Object = Object } )
-- Ensure that the existing element is removed from the Set before a new one is inserted to the Set
if self.Set[ObjectName] then
@ -257,6 +258,8 @@ do -- SET_BASE
-- Trigger Added event.
self:Added( ObjectName, Object )
return self
end
--- Adds a @{Core.Base#BASE} object in the @{Core.Set#SET_BASE}, using the Object Name as the index.
@ -578,8 +581,8 @@ do -- SET_BASE
end
----- Private method that registers all alive players in the mission.
---- @param #SET_BASE self
---- @return #SET_BASE self
-- @param #SET_BASE self
-- @return #SET_BASE self
-- function SET_BASE:_RegisterPlayers()
--
-- local CoalitionsData = { AlivePlayersRed = coalition.getPlayers( coalition.side.RED ), AlivePlayersBlue = coalition.getPlayers( coalition.side.BLUE ) }
@ -796,8 +799,8 @@ do -- SET_BASE
----- Iterate the SET_BASE and call an iterator function for each **alive** unit, providing the Unit and optional parameters.
---- @param #SET_BASE self
---- @param #function IteratorFunction The function that will be called when there is an alive unit in the SET_BASE. The function needs to accept a UNIT parameter.
-- @param #SET_BASE self
-- @param #function IteratorFunction The function that will be called when there is an alive unit in the SET_BASE. The function needs to accept a UNIT parameter.
---- @return #SET_BASE self
-- function SET_BASE:ForEachDCSUnitAlive( IteratorFunction, ... )
-- self:F3( arg )
@ -808,8 +811,8 @@ do -- SET_BASE
-- end
--
----- Iterate the SET_BASE and call an iterator function for each **alive** player, providing the Unit of the player and optional parameters.
---- @param #SET_BASE self
---- @param #function IteratorFunction The function that will be called when there is an alive player in the SET_BASE. The function needs to accept a UNIT parameter.
-- @param #SET_BASE self
-- @param #function IteratorFunction The function that will be called when there is an alive player in the SET_BASE. The function needs to accept a UNIT parameter.
---- @return #SET_BASE self
-- function SET_BASE:ForEachPlayer( IteratorFunction, ... )
-- self:F3( arg )
@ -821,8 +824,8 @@ do -- SET_BASE
--
--
----- Iterate the SET_BASE and call an iterator function for each client, providing the Client to the function and optional parameters.
---- @param #SET_BASE self
---- @param #function IteratorFunction The function that will be called when there is an alive player in the SET_BASE. The function needs to accept a CLIENT parameter.
-- @param #SET_BASE self
-- @param #function IteratorFunction The function that will be called when there is an alive player in the SET_BASE. The function needs to accept a CLIENT parameter.
---- @return #SET_BASE self
-- function SET_BASE:ForEachClient( IteratorFunction, ... )
-- self:F3( arg )
@ -901,9 +904,12 @@ do -- SET_BASE
end
do -- SET_GROUP
do
--- @type SET_GROUP #SET_GROUP
-- SET_GROUP
---
-- @type SET_GROUP #SET_GROUP
-- @field Core.Timer#TIMER ZoneTimer
-- @field #number ZoneTimerInterval
-- @extends Core.Set#SET_BASE
@ -1568,7 +1574,7 @@ do -- SET_GROUP
self:F2( arg )
self:ForEach( IteratorFunction, arg, self:GetSet(),
--- @param Core.Zone#ZONE_BASE ZoneObject
-- @param Core.Zone#ZONE_BASE ZoneObject
-- @param Wrapper.Group#GROUP GroupObject
function( ZoneObject, GroupObject )
if GroupObject:IsCompletelyInZone( ZoneObject ) then
@ -1590,7 +1596,7 @@ do -- SET_GROUP
self:F2( arg )
self:ForEach( IteratorFunction, arg, self:GetSet(),
--- @param Core.Zone#ZONE_BASE ZoneObject
-- @param Core.Zone#ZONE_BASE ZoneObject
-- @param Wrapper.Group#GROUP GroupObject
function( ZoneObject, GroupObject )
if GroupObject:IsPartlyInZone( ZoneObject ) then
@ -1612,7 +1618,7 @@ do -- SET_GROUP
self:F2( arg )
self:ForEach( IteratorFunction, arg, self:GetSet(),
--- @param Core.Zone#ZONE_BASE ZoneObject
-- @param Core.Zone#ZONE_BASE ZoneObject
-- @param Wrapper.Group#GROUP GroupObject
function( ZoneObject, GroupObject )
if GroupObject:IsNotInZone( ZoneObject ) then
@ -1659,7 +1665,7 @@ do -- SET_GROUP
self:F2( arg )
self:ForEach( IteratorFunction, arg, self:GetSet(),
--- @param Core.Zone#ZONE_BASE ZoneObject
-- @param Core.Zone#ZONE_BASE ZoneObject
-- @param Wrapper.Group#GROUP GroupObject
function( ZoneObject, GroupObject )
if GroupObject:IsAnyInZone( ZoneObject ) then
@ -1857,8 +1863,8 @@ do -- SET_GROUP
end
----- Iterate the SET_GROUP and call an iterator function for each **alive** player, providing the Group of the player and optional parameters.
---- @param #SET_GROUP self
---- @param #function IteratorFunction The function that will be called when there is an alive player in the SET_GROUP. The function needs to accept a GROUP parameter.
-- @param #SET_GROUP self
-- @param #function IteratorFunction The function that will be called when there is an alive player in the SET_GROUP. The function needs to accept a GROUP parameter.
---- @return #SET_GROUP self
-- function SET_GROUP:ForEachPlayer( IteratorFunction, ... )
-- self:F2( arg )
@ -1870,8 +1876,8 @@ do -- SET_GROUP
--
--
----- Iterate the SET_GROUP and call an iterator function for each client, providing the Client to the function and optional parameters.
---- @param #SET_GROUP self
---- @param #function IteratorFunction The function that will be called when there is an alive player in the SET_GROUP. The function needs to accept a CLIENT parameter.
-- @param #SET_GROUP self
-- @param #function IteratorFunction The function that will be called when there is an alive player in the SET_GROUP. The function needs to accept a CLIENT parameter.
---- @return #SET_GROUP self
-- function SET_GROUP:ForEachClient( IteratorFunction, ... )
-- self:F2( arg )
@ -1945,7 +1951,7 @@ do -- SET_GROUP
if self.Filter.Zones then
local MGroupZone = false
for ZoneName, Zone in pairs( self.Filter.Zones ) do
--self:I( "Zone:", ZoneName )
--self:T( "Zone:", ZoneName )
if MGroup:IsInZone(Zone) then
MGroupZone = true
end
@ -2011,8 +2017,9 @@ do -- SET_GROUP
end
do -- SET_UNIT
--- @type SET_UNIT
---
-- @type SET_UNIT
-- @field Core.Timer#TIMER ZoneTimer
-- @field #number ZoneTimerInterval
-- @extends Core.Set#SET_BASE
@ -2672,7 +2679,7 @@ do -- SET_UNIT
self:F2( arg )
self:ForEach( IteratorFunction, arg, self:GetSet(),
--- @param Core.Zone#ZONE_BASE ZoneObject
-- @param Core.Zone#ZONE_BASE ZoneObject
-- @param Wrapper.Unit#UNIT UnitObject
function( ZoneObject, UnitObject )
if UnitObject:IsInZone( ZoneObject ) then
@ -2694,7 +2701,7 @@ do -- SET_UNIT
self:F2( arg )
self:ForEach( IteratorFunction, arg, self:GetSet(),
--- @param Core.Zone#ZONE_BASE ZoneObject
-- @param Core.Zone#ZONE_BASE ZoneObject
-- @param Wrapper.Unit#UNIT UnitObject
function( ZoneObject, UnitObject )
if UnitObject:IsNotInZone( ZoneObject ) then
@ -3011,8 +3018,8 @@ do -- SET_UNIT
----- Iterate the SET_UNIT and call an iterator function for each **alive** player, providing the Unit of the player and optional parameters.
---- @param #SET_UNIT self
---- @param #function IteratorFunction The function that will be called when there is an alive player in the SET_UNIT. The function needs to accept a UNIT parameter.
-- @param #SET_UNIT self
-- @param #function IteratorFunction The function that will be called when there is an alive player in the SET_UNIT. The function needs to accept a UNIT parameter.
---- @return #SET_UNIT self
-- function SET_UNIT:ForEachPlayer( IteratorFunction, ... )
-- self:F2( arg )
@ -3024,8 +3031,8 @@ do -- SET_UNIT
--
--
----- Iterate the SET_UNIT and call an iterator function for each client, providing the Client to the function and optional parameters.
---- @param #SET_UNIT self
---- @param #function IteratorFunction The function that will be called when there is an alive player in the SET_UNIT. The function needs to accept a CLIENT parameter.
-- @param #SET_UNIT self
-- @param #function IteratorFunction The function that will be called when there is an alive player in the SET_UNIT. The function needs to accept a CLIENT parameter.
---- @return #SET_UNIT self
-- function SET_UNIT:ForEachClient( IteratorFunction, ... )
-- self:F2( arg )
@ -3040,7 +3047,7 @@ do -- SET_UNIT
-- @param Wrapper.Unit#UNIT MUnit
-- @return #SET_UNIT self
function SET_UNIT:IsIncludeObject( MUnit )
self:F2( MUnit )
self:F2( {MUnit} )
local MUnitInclude = false
@ -3191,8 +3198,9 @@ do -- SET_UNIT
end
do -- SET_STATIC
--- @type SET_STATIC
---
-- @type SET_STATIC
-- @extends Core.Set#SET_BASE
--- Mission designers can use the SET_STATIC class to build sets of Statics belonging to certain:
@ -3612,7 +3620,7 @@ do -- SET_STATIC
self:F2( arg )
self:ForEach( IteratorFunction, arg, self:GetSet(),
--- @param Core.Zone#ZONE_BASE ZoneObject
-- @param Core.Zone#ZONE_BASE ZoneObject
-- @param Wrapper.Static#STATIC StaticObject
function( ZoneObject, StaticObject )
if StaticObject:IsInZone( ZoneObject ) then
@ -3634,7 +3642,7 @@ do -- SET_STATIC
self:F2( arg )
self:ForEach( IteratorFunction, arg, self:GetSet(),
--- @param Core.Zone#ZONE_BASE ZoneObject
-- @param Core.Zone#ZONE_BASE ZoneObject
-- @param Wrapper.Static#STATIC StaticObject
function( ZoneObject, StaticObject )
if StaticObject:IsNotInZone( ZoneObject ) then
@ -3945,8 +3953,9 @@ do -- SET_STATIC
end
do -- SET_CLIENT
--- @type SET_CLIENT
---
-- @type SET_CLIENT
-- @field Core.Timer#TIMER ZoneTimer
-- @field #number ZoneTimerInterval
-- @extends Core.Set#SET_BASE
@ -4008,8 +4017,8 @@ do -- SET_CLIENT
Countries = nil,
ClientPrefixes = nil,
Zones = nil,
Playernames = nil,
Callsigns = nil,
Playernames = nil,
Callsigns = nil,
},
FilterMeta = {
Coalitions = {
@ -4059,7 +4068,7 @@ do -- SET_CLIENT
--- Remove CLIENT(s) from SET_CLIENT.
-- @param Core.Set#SET_CLIENT self
-- @param Wrapper.Client#CLIENT RemoveClientNames A single name or an array of CLIENT names.
-- @param Wrapper.Client#CLIENT RemoveClientNames A single object or an array of CLIENT objects.
-- @return self
function SET_CLIENT:RemoveClientsByName( RemoveClientNames )
@ -4369,7 +4378,7 @@ do -- SET_CLIENT
self:F2( arg )
self:ForEach( IteratorFunction, arg, self:GetSet(),
--- @param Core.Zone#ZONE_BASE ZoneObject
-- @param Core.Zone#ZONE_BASE ZoneObject
-- @param Wrapper.Client#CLIENT ClientObject
function( ZoneObject, ClientObject )
if ClientObject:IsInZone( ZoneObject ) then
@ -4391,7 +4400,7 @@ do -- SET_CLIENT
self:F2( arg )
self:ForEach( IteratorFunction, arg, self:GetSet(),
--- @param Core.Zone#ZONE_BASE ZoneObject
-- @param Core.Zone#ZONE_BASE ZoneObject
-- @param Wrapper.Client#CLIENT ClientObject
function( ZoneObject, ClientObject )
if ClientObject:IsNotInZone( ZoneObject ) then
@ -4459,7 +4468,7 @@ do -- SET_CLIENT
if self.Filter.Active == false or (self.Filter.Active == true and MClient:IsActive() == true and MClient:IsAlive() == true) then
MClientActive = true
end
--self:I( { "Evaluated Active", MClientActive } )
--self:T( { "Evaluated Active", MClientActive } )
MClientInclude = MClientInclude and MClientActive
end
@ -4526,45 +4535,45 @@ do -- SET_CLIENT
MClientInclude = MClientInclude and MClientPrefix
end
if self.Filter.Zones then
local MClientZone = false
for ZoneName, Zone in pairs( self.Filter.Zones ) do
self:T3( "Zone:", ZoneName )
local unit = MClient:GetClientGroupUnit()
if unit and unit:IsInZone(Zone) then
MClientZone = true
end
end
MClientInclude = MClientInclude and MClientZone
end
if self.Filter.Playernames then
local MClientPlayername = false
local playername = MClient:GetPlayerName() or "Unknown"
--self:I(playername)
for _,_Playername in pairs(self.Filter.Playernames) do
if playername and string.find(playername,_Playername) then
MClientPlayername = true
end
end
self:T( { "Evaluated Playername", MClientPlayername } )
MClientInclude = MClientInclude and MClientPlayername
end
if self.Filter.Callsigns then
local MClientCallsigns = false
local callsign = MClient:GetCallsign()
--self:I(callsign)
for _,_Callsign in pairs(self.Filter.Callsigns) do
if callsign and string.find(callsign,_Callsign) then
MClientCallsigns = true
end
end
self:T( { "Evaluated Callsign", MClientCallsigns } )
MClientInclude = MClientInclude and MClientCallsigns
end
end
if self.Filter.Zones then
local MClientZone = false
for ZoneName, Zone in pairs( self.Filter.Zones ) do
self:T3( "Zone:", ZoneName )
local unit = MClient:GetClientGroupUnit()
if unit and unit:IsInZone(Zone) then
MClientZone = true
end
end
MClientInclude = MClientInclude and MClientZone
end
if self.Filter.Playernames then
local MClientPlayername = false
local playername = MClient:GetPlayerName() or "Unknown"
--self:T(playername)
for _,_Playername in pairs(self.Filter.Playernames) do
if playername and string.find(playername,_Playername) then
MClientPlayername = true
end
end
self:T( { "Evaluated Playername", MClientPlayername } )
MClientInclude = MClientInclude and MClientPlayername
end
if self.Filter.Callsigns then
local MClientCallsigns = false
local callsign = MClient:GetCallsign()
--self:T(callsign)
for _,_Callsign in pairs(self.Filter.Callsigns) do
if callsign and string.find(callsign,_Callsign) then
MClientCallsigns = true
end
end
self:T( { "Evaluated Callsign", MClientCallsigns } )
MClientInclude = MClientInclude and MClientCallsigns
end
end
self:T2( MClientInclude )
return MClientInclude
end
@ -4573,7 +4582,8 @@ end
do -- SET_PLAYER
--- @type SET_PLAYER
---
-- @type SET_PLAYER
-- @extends Core.Set#SET_BASE
--- Mission designers can use the @{Core.Set#SET_PLAYER} class to build sets of units belonging to alive players:
@ -4868,7 +4878,7 @@ do -- SET_PLAYER
self:F2( arg )
self:ForEach( IteratorFunction, arg, self:GetSet(),
--- @param Core.Zone#ZONE_BASE ZoneObject
-- @param Core.Zone#ZONE_BASE ZoneObject
-- @param Wrapper.Client#CLIENT ClientObject
function( ZoneObject, ClientObject )
if ClientObject:IsInZone( ZoneObject ) then
@ -4890,7 +4900,7 @@ do -- SET_PLAYER
self:F2( arg )
self:ForEach( IteratorFunction, arg, self:GetSet(),
--- @param Core.Zone#ZONE_BASE ZoneObject
-- @param Core.Zone#ZONE_BASE ZoneObject
-- @param Wrapper.Client#CLIENT ClientObject
function( ZoneObject, ClientObject )
if ClientObject:IsNotInZone( ZoneObject ) then
@ -4998,8 +5008,9 @@ do -- SET_PLAYER
end
do -- SET_AIRBASE
--- @type SET_AIRBASE
---
-- @type SET_AIRBASE
-- @extends Core.Set#SET_BASE
--- Mission designers can use the @{Core.Set#SET_AIRBASE} class to build sets of airbases optionally belonging to certain:
@ -5341,8 +5352,9 @@ do -- SET_AIRBASE
end
do -- SET_CARGO
--- @type SET_CARGO
---
-- @type SET_CARGO
-- @extends Core.Set#SET_BASE
--- Mission designers can use the @{Core.Set#SET_CARGO} class to build sets of cargos optionally belonging to certain:
@ -5767,8 +5779,9 @@ do -- SET_CARGO
end
do -- SET_ZONE
--- @type SET_ZONE
---
-- @type SET_ZONE
-- @extends Core.Set#SET_BASE
--- Mission designers can use the @{Core.Set#SET_ZONE} class to build sets of zones of various types.
@ -6135,8 +6148,9 @@ do -- SET_ZONE
end
do -- SET_ZONE_GOAL
--- @type SET_ZONE_GOAL
---
-- @type SET_ZONE_GOAL
-- @extends Core.Set#SET_BASE
--- Mission designers can use the @{Core.Set#SET_ZONE_GOAL} class to build sets of zones of various types.
@ -6446,8 +6460,9 @@ do -- SET_ZONE_GOAL
end
do -- SET_OPSZONE
--- @type SET_OPSZONE
---
-- @type SET_OPSZONE
-- @extends Core.Set#SET_BASE
--- Mission designers can use the @{Core.Set#SET_OPSZONE} class to build sets of zones of various types.
@ -6903,8 +6918,9 @@ end
do -- SET_OPSGROUP
--- @type SET_OPSGROUP
---
-- @type SET_OPSGROUP
-- @extends Core.Set#SET_BASE
--- Mission designers can use the @{Core.Set#SET_OPSGROUP} class to build sets of OPS groups belonging to certain:
@ -7571,6 +7587,7 @@ do -- SET_SCENERY
Scenerys = {},
Filter = {
SceneryPrefixes = nil,
SceneryRoles = nil,
Zones = nil,
},
}
@ -7593,7 +7610,7 @@ do -- SET_SCENERY
if ZoneSet then
for _,_zone in pairs(ZoneSet.Set) do
self:T("Zone type handed: "..tostring(_zone.ClassName))
self:T("Zone type handed: "..tostring(_zone.ClassName))
table.insert(zonenames,_zone:GetName())
end
self:AddSceneryByName(zonenames)
@ -7617,7 +7634,7 @@ do -- SET_SCENERY
--- Add SCENERY(s) to SET_SCENERY.
-- @param #SET_SCENERY self
-- @param #string AddScenery A single SCENERY.
-- @param Wrapper.Scenery#SCENERY AddScenery A single SCENERY object.
-- @return #SET_SCENERY self
function SET_SCENERY:AddScenery( AddScenery )
self:F2( AddScenery:GetName() )
@ -7687,6 +7704,7 @@ do -- SET_SCENERY
end
for _,Zone in pairs( zones ) do
local zonename = Zone:GetName()
self:T(zonename)
self.Filter.Zones[zonename] = Zone
end
return self
@ -7705,11 +7723,30 @@ do -- SET_SCENERY
Prefixes = { Prefixes }
end
for PrefixID, Prefix in pairs( Prefixes ) do
--self:T(Prefix)
self.Filter.SceneryPrefixes[Prefix] = Prefix
end
return self
end
--- Builds a set of SCENERYs that **contain** an exact match of the "ROLE" property.
-- @param #SET_SCENERY self
-- @param #string Role The string pattern(s) that needs to exactly match the scenery "ROLE" property from the ME quad-zone properties. Can also be passed as a `#table` of strings.
-- @return #SET_SCENERY self
function SET_SCENERY:FilterRoles( Role )
if not self.Filter.SceneryRoles then
self.Filter.SceneryRoles = {}
end
if type( Role ) ~= "table" then
Role = { Role }
end
for PrefixID, Prefix in pairs( Role ) do
--self:T(Prefix)
self.Filter.SceneryRoles[Prefix] = Prefix
end
return self
end
--- Iterate the SET_SCENERY and count how many SCENERYSs are alive.
-- @param #SET_SCENERY self
-- @return #number The number of SCENERYSs alive.
@ -7802,10 +7839,76 @@ do -- SET_SCENERY
-- @param Wrapper.Scenery#SCENERY MScenery
-- @return #SET_SCENERY self
function SET_SCENERY:IsIncludeObject( MScenery )
self:F2( MScenery )
return true
self:T( MScenery.SceneryName )
local MSceneryInclude = true
if MScenery then
local MSceneryName = MScenery:GetName()
-- Filter Prefixes
if self.Filter.Prefixes then
local MSceneryPrefix = false
for ZonePrefixId, ZonePrefix in pairs( self.Filter.Prefixes ) do
self:T( { "Prefix:", string.find( MSceneryName, ZonePrefix, 1 ), ZonePrefix } )
if string.find( MSceneryName, ZonePrefix, 1 ) then
MSceneryPrefix = true
end
end
self:T( { "Evaluated Prefix", MSceneryPrefix } )
MSceneryInclude = MSceneryInclude and MSceneryPrefix
end
if self.Filter.Zones then
local MSceneryZone = false
for ZoneName, Zone in pairs( self.Filter.Zones ) do
--self:T( "Zone:", ZoneName )
local coord = MScenery:GetCoordinate()
if coord and Zone:IsCoordinateInZone(coord) then
MSceneryZone = true
end
self:T( { "Evaluated Zone", MSceneryZone } )
end
MSceneryInclude = MSceneryInclude and MSceneryZone
end
-- Filter Roles
if self.Filter.SceneryRoles then
local MSceneryRole = false
local Role = MScenery:GetProperty("ROLE") or "none"
for ZoneRoleId, ZoneRole in pairs( self.Filter.SceneryRoles ) do
self:T( { "Role:", ZoneRole, Role } )
if ZoneRole == Role then
MSceneryRole = true
end
end
self:T( { "Evaluated Role ", MSceneryRole } )
MSceneryInclude = MSceneryInclude and MSceneryRole
end
end
self:T2( MSceneryInclude )
return MSceneryInclude
end
--- Filters for the defined collection.
-- @param #SET_SCENERY self
-- @return #SET_SCENERY self
function SET_SCENERY:FilterOnce()
for ObjectName, Object in pairs( self:GetSet() ) do
self:T(ObjectName)
if self:IsIncludeObject( Object ) then
self:Add( ObjectName, Object )
else
self:Remove(ObjectName, true)
end
end
return self --FilteredSet
end
--- Count overall initial (Life0) lifepoints of the SET objects.
-- @param #SET_SCENERY self
-- @return #number LIfe0Points
@ -7843,7 +7946,7 @@ do -- SET_SCENERY
function SET_SCENERY:GetRelativeLife()
local life = self:GetLife()
local life0 = self:GetLife0()
self:T3(string.format("Set Lifepoints: %d life0 | %d life",life0,life))
self:T2(string.format("Set Lifepoints: %d life0 | %d life",life0,life))
local rlife = math.floor((life / life0) * 100)
return rlife
end

View File

@ -1420,7 +1420,7 @@ function SCORING:ReportDetailedPlayerHits( PlayerName )
Penalty = Penalty + UnitData.Penalty
PenaltyHit = UnitData.PenaltyHit
end
local ScoreMessageHit = string.format( "%s:%d ", CategoryName, Score - Penalty )
local ScoreMessageHit = string.format( "%s: %d ", CategoryName, Score - Penalty )
self:T( ScoreMessageHit )
ScoreMessageHits = ScoreMessageHits .. ScoreMessageHit
PlayerScore = PlayerScore + Score
@ -1476,7 +1476,7 @@ function SCORING:ReportDetailedPlayerDestroys( PlayerName )
end
end
local ScoreMessageDestroy = string.format( " %s:%d ", CategoryName, Score - Penalty )
local ScoreMessageDestroy = string.format( " %s: %d ", CategoryName, Score - Penalty )
self:T( ScoreMessageDestroy )
ScoreMessageDestroys = ScoreMessageDestroys .. ScoreMessageDestroy

View File

@ -419,6 +419,8 @@ ATIS.RunwayM2T = {
TheChannel = -10,
Syria = 5,
MarianaIslands = 2,
Falklands = 12,
Sinai = 5,
}
--- Whether ICAO phraseology is used for ATIS broadcasts.
@ -430,6 +432,8 @@ ATIS.RunwayM2T = {
-- @field #boolean TheChannel true.
-- @field #boolean Syria true.
-- @field #boolean MarianaIslands true.
-- @field #boolean Falklands true.
-- @field #boolean Sinai true.
ATIS.ICAOPhraseology = {
Caucasus = true,
Nevada = false,
@ -437,7 +441,9 @@ ATIS.ICAOPhraseology = {
PersianGulf = true,
TheChannel = true,
Syria = true,
MarianaIslands = true
MarianaIslands = true,
Falklands = true,
Sinai = true,
}
--- Nav point data.
@ -1272,7 +1278,8 @@ end
-- @param #string Event Event.
-- @param #string To To state.
function ATIS:onafterStart( From, Event, To )
self:I("Airbase category is "..self.airbase:GetAirbaseCategory())
-- Check that this is an airdrome.
if self.airbase:GetAirbaseCategory() == Airbase.Category.SHIP then
self:E( self.lid .. string.format( "ERROR: Cannot start ATIS for airbase %s! Only AIRDROMES are supported but NOT SHIPS.", self.airbasename ) )
@ -1823,7 +1830,10 @@ function ATIS:onafterBroadcast( From, Event, To )
-- Airbase name
subtitle = string.format( "%s", self.airbasename )
if (not self.ATISforFARPs) and self.airbasename:find( "AFB" ) == nil and self.airbasename:find( "Airport" ) == nil and self.airbasename:find( "Airstrip" ) == nil and self.airbasename:find( "airfield" ) == nil and self.airbasename:find( "AB" ) == nil then
if (not self.ATISforFARPs) and self.airbasename:find( "AFB" ) == nil and self.airbasename:find( "Airport" ) == nil
and self.airbasename:find( "Airstrip" ) == nil and self.airbasename:find( "airfield" ) == nil and self.airbasename:find( "AB" ) == nil
and self.airbasename:find( "Field" ) == nil
then
subtitle = subtitle .. " Airport"
end
if not self.useSRS then

View File

@ -1735,10 +1735,10 @@ AIRBOSS.Difficulty = {
-- @field #table trapsheet Groove data table recorded every 0.5 seconds.
-- @field #boolean trapon If true, save trap sheets.
-- @field #string debriefschedulerID Debrief scheduler ID.
--
--
-- @field Sound.SRS#MSRS SRS
-- @field Sound.SRS#MSRSQUEUE SRSQ
--
--
-- @extends #AIRBOSS.FlightGroup
--- Main group level radio menu: F10 Other/Airboss.
@ -3058,7 +3058,7 @@ end
-- @param #number Port Port of the SRS server, defaults to 5002.
-- @param #string Culture (Optional, Airboss Culture) Culture, defaults to "en-US".
-- @param #string Gender (Optional, Airboss Gender) Gender, e.g. "male" or "female". Defaults to "male".
-- @param #string Voice (Optional, Airboss Voice) Set to use a specific voice. Will **override gender and culture** settings.
-- @param #string Voice (Optional, Airboss Voice) Set to use a specific voice. Will **override gender and culture** settings.
-- @param #string GoogleCreds (Optional) Path to Google credentials, e.g. "C:\\Program Files\\DCS-SimpleRadio-Standalone\\yourgooglekey.json".
-- @param #number Volume (Optional) E.g. 0.75. Defaults to 1.0 (loudest).
-- @param #table AltBackend (Optional) See MSRS for details.
@ -3087,10 +3087,10 @@ function AIRBOSS:EnableSRS(PathToSRS,Port,Culture,Gender,Voice,GoogleCreds,Volum
-- SRSQUEUE
self.SRSQ = MSRSQUEUE:New("AIRBOSS")
self.SRSQ:SetTransmitOnlyWithPlayers(true)
if not self.PilotRadio then
if not self.PilotRadio then
self:SetSRSPilotVoice()
end
return self
return self
end
--- Set LSO radio frequency and modulation. Default frequency is 264 MHz AM.
@ -5924,9 +5924,11 @@ function AIRBOSS:_ScanCarrierZone()
-- Get aircraft type name.
local actype = group:GetTypeName()
-- Create a new flight group
if knownflight then
-- Debug output.
self:T2(self.lid..string.format("Known flight group %s of type %s in CCA.", groupname, actype))
-- Check if flight is AI and if we want to handle it at all.
if knownflight.ai and knownflight.flag == -100 and self.handleai then
@ -5943,9 +5945,10 @@ function AIRBOSS:_ScanCarrierZone()
flight.airboss = self
end
-- Send AI flight to marshal stack.
-- Send AI flight to marshal stack if group closes in more than 5 and has initial flag value.
if putintomarshal then
-- Get the next free stack for current recovery case.
local stack = self:_GetFreeStack( knownflight.ai )
@ -11553,7 +11556,7 @@ function AIRBOSS:GetHeadingIntoWind( magnetic, coord )
local function adjustDegreesForWindSpeed(windSpeed)
local degreesAdjustment = 0
-- the windspeeds are in m/s
-- +0 degrees at 15m/s = 37kts
-- +0 degrees at 14m/s = 35kts
-- +0 degrees at 13m/s = 33kts
@ -11568,7 +11571,7 @@ function AIRBOSS:GetHeadingIntoWind( magnetic, coord )
-- +20 degrees at 4m/s = 26kts
-- +20 degrees at 3m/s = 26kts
-- +30 degrees at 2m/s = 26kts 1s
if windSpeed > 0 and windSpeed < 3 then
degreesAdjustment = 30
elseif windSpeed >= 3 and windSpeed < 5 then
@ -11580,17 +11583,17 @@ function AIRBOSS:GetHeadingIntoWind( magnetic, coord )
elseif windSpeed >= 13 then
degreesAdjustment = 0
end
return degreesAdjustment
end
-- Get direction the wind is blowing from. This is where we want to go.
local windfrom, vwind = self:GetWind( nil, nil, coord )
--self:I("windfrom="..windfrom.." vwind="..vwind)
vwind = vwind + adjustDegreesForWindSpeed(vwind)
--self:I("windfrom="..windfrom.." (c)vwind="..vwind)
-- Actually, we want the runway in the wind.
@ -14730,12 +14733,12 @@ function AIRBOSS:RadioTransmission( radio, call, loud, delay, interval, click, p
if radio == nil or call == nil then
return
end
if not self.SRS then
-- Create a new radio transmission item.
local transmission = {} -- #AIRBOSS.Radioitem
transmission.radio = radio
transmission.call = call
transmission.Tplay = timer.getAbsTime() + (delay or 0)
@ -14743,58 +14746,58 @@ function AIRBOSS:RadioTransmission( radio, call, loud, delay, interval, click, p
transmission.isplaying = false
transmission.Tstarted = nil
transmission.loud = loud and call.loud
-- Player onboard number if sender has one.
if self:_IsOnboard( call.modexsender ) then
self:_Number2Radio( radio, call.modexsender, delay, 0.3, pilotcall )
end
-- Play onboard number if receiver has one.
if self:_IsOnboard( call.modexreceiver ) then
self:_Number2Radio( radio, call.modexreceiver, delay, 0.3, pilotcall )
end
-- Add transmission to the right queue.
local caller = ""
if radio.alias == "LSO" then
table.insert( self.RQLSO, transmission )
caller = "LSOCall"
-- Schedule radio queue checks.
if not self.RQLid then
self:T( self.lid .. string.format( "Starting LSO radio queue." ) )
self.RQLid = self.radiotimer:Schedule( nil, AIRBOSS._CheckRadioQueue, { self, self.RQLSO, "LSO" }, 0.02, 0.05 )
end
elseif radio.alias == "MARSHAL" then
table.insert( self.RQMarshal, transmission )
caller = "MarshalCall"
if not self.RQMid then
self:T( self.lid .. string.format( "Starting Marhal radio queue." ) )
self.RQMid = self.radiotimer:Schedule( nil, AIRBOSS._CheckRadioQueue, { self, self.RQMarshal, "MARSHAL" }, 0.02, 0.05 )
end
end
-- Append radio click sound at the end of the transmission.
if click then
self:RadioTransmission( radio, self[caller].CLICK, false, delay )
end
else
-- SRS transmission
local frequency = self.MarshalRadio.frequency
local modulation = self.MarshalRadio.modulation
local voice = nil
local gender = nil
local culture = nil
if radio.alias == "AIRBOSS" then
frequency = self.AirbossRadio.frequency
modulation = self.AirbossRadio.modulation
@ -14802,13 +14805,13 @@ function AIRBOSS:RadioTransmission( radio, call, loud, delay, interval, click, p
gender = self.AirbossRadio.gender
culture = self.AirbossRadio.culture
end
if radio.alias == "MARSHAL" then
voice = self.MarshalRadio.voice
gender = self.MarshalRadio.gender
culture = self.MarshalRadio.culture
end
if radio.alias == "LSO" then
frequency = self.LSORadio.frequency
modulation = self.LSORadio.modulation
@ -14816,7 +14819,7 @@ function AIRBOSS:RadioTransmission( radio, call, loud, delay, interval, click, p
gender = self.LSORadio.gender
culture = self.LSORadio.culture
end
if pilotcall then
voice = self.PilotRadio.voice
gender = self.PilotRadio.gender
@ -14830,16 +14833,16 @@ function AIRBOSS:RadioTransmission( radio, call, loud, delay, interval, click, p
modulation = self.AirbossRadio.modulation
radio.alias = "AIRBOSS"
end
local volume = nil
if loud then
volume = 1.0
end
--local text = tostring(call.modexreceiver).."; "..radio.alias.."; "..call.subtitle
local text = call.subtitle
self:I(self.lid..text)
self:I(self.lid..text)
local srstext = self:_GetNiceSRSText(text)
self.SRSQ:NewTransmission(srstext, call.duration, self.SRS, tstart, 0.1, subgroups, call.subtitle, call.subduration, frequency, modulation, gender, culture, voice, volume, radio.alias)
end
@ -14858,11 +14861,11 @@ function AIRBOSS:SetSRSPilotVoice( Voice, Gender, Culture )
self.PilotRadio.voice = Voice or MSRS.Voices.Microsoft.David
self.PilotRadio.gender = Gender or "male"
self.PilotRadio.culture = Culture or "en-US"
if (not Voice) and self.SRS and self.SRS.google then
self.PilotRadio.voice = MSRS.Voices.Google.Standard.en_US_Standard_J
end
return self
end
@ -15176,44 +15179,44 @@ function AIRBOSS:MessageToPlayer( playerData, message, sender, receiver, duratio
-- SCHEDULER:New(nil, self.MessageToPlayer, {self, playerData, message, sender, receiver, duration, clear}, delay)
self:ScheduleOnce( delay, self.MessageToPlayer, self, playerData, message, sender, receiver, duration, clear )
else
if not self.SRS then
-- Wait until previous sound finished.
local wait = 0
-- Onboard number to get the attention.
if receiver == playerData.onboard then
-- Which voice over number to use.
if sender and (sender == "LSO" or sender == "MARSHAL" or sender == "AIRBOSS") then
-- User sound of board number.
wait = wait + self:_Number2Sound( playerData, sender, receiver )
end
end
-- Negative.
if string.find( text:lower(), "negative" ) then
local filename = self:_RadioFilename( self.MarshalCall.NEGATIVE, false, "MARSHAL" )
USERSOUND:New( filename ):ToGroup( playerData.group, wait )
wait = wait + self.MarshalCall.NEGATIVE.duration
end
-- Affirm.
if string.find( text:lower(), "affirm" ) then
local filename = self:_RadioFilename( self.MarshalCall.AFFIRMATIVE, false, "MARSHAL" )
USERSOUND:New( filename ):ToGroup( playerData.group, wait )
wait = wait + self.MarshalCall.AFFIRMATIVE.duration
end
-- Roger.
if string.find( text:lower(), "roger" ) then
local filename = self:_RadioFilename( self.MarshalCall.ROGER, false, "MARSHAL" )
USERSOUND:New( filename ):ToGroup( playerData.group, wait )
wait = wait + self.MarshalCall.ROGER.duration
end
-- Play click sound to end message.
if wait > 0 then
local filename = self:_RadioFilename( self.MarshalCall.CLICK )
@ -15226,7 +15229,7 @@ function AIRBOSS:MessageToPlayer( playerData, message, sender, receiver, duratio
local voice = self.MarshalRadio.voice
local gender = self.MarshalRadio.gender
local culture = self.MarshalRadio.culture
if not sender then sender = "AIRBOSS" end
if string.find(sender,"AIRBOSS" ) then

View File

@ -17,7 +17,7 @@
-- ===
--
-- ### Author: **applevangelist**
-- @date Last Update December 2022
-- @date Last Update July 2023
-- @module Ops.AWACS
-- @image OPS_AWACS.jpg
@ -63,6 +63,7 @@ do
-- @field Utilities.FiFo#FIFO RadioQueue
-- @field Utilities.FiFo#FIFO PrioRadioQueue
-- @field Utilities.FiFo#FIFO CAPAirwings
-- @field Utilities.FiFo#FIFO TacticalQueue
-- @field #number AwacsTimeOnStation
-- @field #number AwacsTimeStamp
-- @field #number EscortsTimeOnStation
@ -113,6 +114,13 @@ do
-- @field Wrapper.Group#GROUP GCIGroup EWR group object for GCI ops
-- @field #string locale Localization
-- @field #boolean IncludeHelicopters
-- @field #boolean TacticalMenu
-- @field #table TacticalFrequencies
-- @field #table TacticalSubscribers
-- @field #number TacticalBaseFreq
-- @field #number TacticalIncrFreq
-- @field #number TacticalModulation
-- @field #number TacticalInterval
-- @extends Core.Fsm#FSM
@ -499,7 +507,7 @@ do
-- @field #AWACS
AWACS = {
ClassName = "AWACS", -- #string
version = "0.2.55", -- #string
version = "0.2.57", -- #string
lid = "", -- #string
coalition = coalition.side.BLUE, -- #number
coalitiontxt = "blue", -- #string
@ -532,6 +540,7 @@ AWACS = {
ContactsAO = {}, -- Utilities.FiFo#FIFO
RadioQueue = {}, -- Utilities.FiFo#FIFO
PrioRadioQueue = {}, -- Utilities.FiFo#FIFO
TacticalQueue = {}, -- Utilities.FiFo#FIFO
AwacsTimeOnStation = 4,
AwacsTimeStamp = 0,
EscortsTimeOnStation = 4,
@ -587,6 +596,13 @@ AWACS = {
GCIGroup = nil,
locale = "en",
IncludeHelicopters = false,
TacticalMenu = false,
TacticalFrequencies = {},
TacticalSubscribers = {},
TacticalBaseFreq = 130,
TacticalIncrFreq = 0.5,
TacticalModulation = radio.modulation.AM,
TacticalInterval = 120,
}
---
@ -917,7 +933,7 @@ AWACS.TaskStatus = {
--@field #boolean FromAI
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO-List 0.2.52
-- TODO-List 0.2.53
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--
-- DONE - WIP - Player tasking, VID
@ -1110,6 +1126,7 @@ function AWACS:New(Name,AirWing,Coalition,AirbaseName,AwacsOrbit,OpsZone,Station
self.Volume = 1.0
self.RadioQueue = FIFO:New() -- Utilities.FiFo#FIFO
self.PrioRadioQueue = FIFO:New() -- Utilities.FiFo#FIFO
self.TacticalQueue = FIFO:New() -- Utilities.FiFo#FIFO
self.maxspeakentries = 3
self.GoogleTTSPadding = 1
self.WindowsTTSPadding = 2.5
@ -1180,6 +1197,15 @@ function AWACS:New(Name,AirWing,Coalition,AirbaseName,AwacsOrbit,OpsZone,Station
self.clientmenus = FIFO:New() -- Utilities.FiFo#FIFO
-- Tactical Menu
self.TacticalMenu = false
self.TacticalBaseFreq = 130
self.TacticalIncrFreq = 0.5
self.TacticalModulation = radio.modulation.AM
self.acticalFrequencies = {}
self.TacticalSubscribers = {}
self.TacticalInterval = 120
-- SET for Intel Detection
self.DetectionSet=SET_GROUP:New()
@ -1204,6 +1230,7 @@ function AWACS:New(Name,AirWing,Coalition,AirbaseName,AwacsOrbit,OpsZone,Station
self:AddTransition("*", "LostCluster", "*")
self:AddTransition("*", "LostContact", "*")
self:AddTransition("*", "CheckRadioQueue", "*")
self:AddTransition("*", "CheckTacticalQueue", "*")
self:AddTransition("*", "EscortShiftChange", "*")
self:AddTransition("*", "AwacsShiftChange", "*")
self:AddTransition("*", "FlightOnMission", "*")
@ -1354,6 +1381,159 @@ end
-- Functions
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- [User] Set the tactical information option, create 10 radio channels groups can subscribe and get Bogey Dope on a specific frequency automatically.
-- @param #AWACS self
-- @param #number BaseFreq Base Frequency to use, defaults to 130.
-- @param #number Increase Increase to use, defaults to 0.5, thus channels created are 130, 130.5, 131 .. etc.
-- @param #number Modulation Modulation to use, defaults to radio.modulation.AM.
-- @param #number Interval Seconds between each update call.
-- @param #number Number Number of Frequencies to create, can be 1..10.
-- @return #AWACS self
function AWACS:SetTacticalRadios(BaseFreq,Increase,Modulation,Interval,Number)
self:T(self.lid.."SetTacticalRadios")
self.TacticalMenu = true
self.TacticalBaseFreq = BaseFreq or 130
self.TacticalIncrFreq = Increase or 0.5
self.TacticalModulation = Modulation or radio.modulation.AM
self.TacticalInterval = Interval or 120
local number = Number or 10
if number < 1 then number = 1 end
if number > 10 then number = 10 end
for i=1,number do
local freq = self.TacticalBaseFreq + ((i-1)*self.TacticalIncrFreq)
self.TacticalFrequencies[freq] = freq
end
if self.AwacsSRS then
self.TacticalSRS = MSRS:New(self.PathToSRS,self.TacticalBaseFreq,self.TacticalModulation,self.Volume)
self.TacticalSRS:SetCoalition(self.coalition)
self.TacticalSRS:SetGender(self.Gender)
self.TacticalSRS:SetCulture(self.Culture)
self.TacticalSRS:SetVoice(self.Voice)
self.TacticalSRS:SetPort(self.Port)
self.TacticalSRS:SetLabel("AWACS")
if self.PathToGoogleKey then
self.TacticalSRS:SetGoogle(self.PathToGoogleKey)
end
self.TacticalSRSQ = MSRSQUEUE:New("Tactical AWACS")
end
return self
end
--- TODO
-- [Internal] _RefreshMenuNonSubscribed
-- @param #AWACS self
-- @return #AWACS self
function AWACS:_RefreshMenuNonSubscribed()
self:T(self.lid.."_RefreshMenuNonSubscribed")
local aliveset = self.clientset:GetAliveSet()
for _,_group in pairs(aliveset) do
-- go through set and re-build the sub-menu
local grp = _group -- Wrapper.Client#CLIENT
local Group = grp:GetGroup()
local gname = nil
if Group and Group:IsAlive() then
gname = Group:GetName()
self:T(gname)
end
local menustr = self.clientmenus:ReadByID(gname)
local menu = menustr.tactical -- Core.Menu#MENU_GROUP
if not self.TacticalSubscribers[gname] and menu then
menu:RemoveSubMenus()
for _,_freq in UTILS.spairs(self.TacticalFrequencies) do
local modu = UTILS.GetModulationName(self.TacticalModulation)
local text = string.format("Subscribe to %.3f %s",_freq,modu)
local entry = MENU_GROUP_COMMAND:New(Group,text,menu,self._SubScribeTactRadio,self,Group,_freq)
end
end
end
return self
end
--- [Internal] _UnsubScribeTactRadio
-- @param #AWACS self
-- @param Wrapper.Group#GROUP Group
-- @return #AWACS self
function AWACS:_UnsubScribeTactRadio(Group)
self:T(self.lid.."_UnsubScribeTactRadio")
local text = ""
local textScreen = ""
local GID, Outcome = self:_GetManagedGrpID(Group)
local gcallsign = self:_GetCallSign(Group,GID) or "Ghost 1"
local gname = Group:GetName() or "unknown"
if Outcome and self.TacticalSubscribers[gname] then
-- Pilot is checked in
local Freq = self.TacticalSubscribers[gname]
self.TacticalFrequencies[Freq] = Freq
self.TacticalSubscribers[gname] = nil
local modu = self.TacticalModulation == 0 and "AM" or "FM"
text = string.format("%s, %s, switch back to AWACS main frequency!",gcallsign,self.callsigntxt)
self:_NewRadioEntry(text,text,GID,true,true,true,false,true)
self:_RefreshMenuNonSubscribed()
elseif self.AwacsFG then
-- no, unknown
local nocheckin = self.gettext:GetEntry("NOTCHECKEDIN",self.locale)
text = string.format(nocheckin,self:_GetCallSign(Group,GID) or "Ghost 1", self.callsigntxt)
self:_NewRadioEntry(text,text,GID,Outcome,true,true,false)
end
return self
end
--- [Internal] _SubScribeTactRadio
-- @param #AWACS self
-- @param Wrapper.Group#GROUP Group
-- @param #number Frequency
-- @return #AWACS self
function AWACS:_SubScribeTactRadio(Group,Frequency)
self:T(self.lid.."_SubScribeTactRadio")
local text = ""
local textScreen = ""
local GID, Outcome = self:_GetManagedGrpID(Group)
local gcallsign = self:_GetCallSign(Group,GID) or "Ghost 1"
local gname = Group:GetName() or "unknown"
if Outcome then
-- Pilot is checked in
self.TacticalSubscribers[gname] = Frequency
self.TacticalFrequencies[Frequency] = nil
local modu = self.TacticalModulation == 0 and "AM" or "FM"
text = string.format("%s, %s, switch to %.3f %s for tactical information!",gcallsign,self.callsigntxt,Frequency,modu)
self:_NewRadioEntry(text,text,GID,true,true,true,false,true)
local menustr = self.clientmenus:ReadByID(gname)
local menu = menustr.tactical -- Core.Menu#MENU_GROUP
if menu then
menu:RemoveSubMenus()
local text = string.format("Unsubscribe %.3f %s",Frequency,modu)
local entry = MENU_GROUP_COMMAND:New(Group,text,menu,self._UnsubScribeTactRadio,self,Group)
end
elseif self.AwacsFG then
-- no, unknown
local nocheckin = self.gettext:GetEntry("NOTCHECKEDIN",self.locale)
text = string.format(nocheckin,self:_GetCallSign(Group,GID) or "Ghost 1", self.callsigntxt)
self:_NewRadioEntry(text,text,GID,Outcome,true,true,false)
end
return self
end
--- [Internal] _CheckSubscribers
-- @param #AWACS self
-- @return #AWACS self
function AWACS:_CheckSubscribers()
self:T(self.lid.."_InitLocalization")
for _name,_freq in pairs(self.TacticalSubscribers or {}) do
local grp = GROUP:FindByName(_name)
if (not grp) or (not grp:IsAlive()) then
self.TacticalFrequencies[_freq] = _freq
self.TacticalSubscribers[_name] = nil
end
end
return self
end
--- [Internal] Init localization
-- @param #AWACS self
-- @return #AWACS self
@ -1426,19 +1606,22 @@ end
-- @param #boolean IsNew New
-- @param #boolean FromAI From AI
-- @param #boolean IsPrio Priority entry
-- @param #boolean Tactical Is for tactical info
-- @return #AWACS self
function AWACS:_NewRadioEntry(TextTTS, TextScreen,GID,IsGroup,ToScreen,IsNew,FromAI,IsPrio)
function AWACS:_NewRadioEntry(TextTTS, TextScreen,GID,IsGroup,ToScreen,IsNew,FromAI,IsPrio,Tactical)
self:T(self.lid.."_NewRadioEntry")
local RadioEntry = {} -- #AWACS.RadioEntry
RadioEntry.IsNew = IsNew
RadioEntry.TextTTS = TextTTS
RadioEntry.TextScreen = TextScreen
RadioEntry.TextScreen = TextScreen or TextTTS
RadioEntry.GroupID = GID
RadioEntry.ToScreen = ToScreen
RadioEntry.Duration = STTS.getSpeechTime(TextTTS,0.95,false) or 8
RadioEntry.FromAI = FromAI
RadioEntry.IsGroup = IsGroup
if IsPrio then
if Tactical then
self.TacticalQueue:Push(RadioEntry)
elseif IsPrio then
self.PrioRadioQueue:Push(RadioEntry)
else
self.RadioQueue:Push(RadioEntry)
@ -1618,7 +1801,7 @@ function AWACS:_EventHandler(EventData)
if WeaponDesc.category == 1 then
Type = "Missile"
-- AAM
local guidance = WeaponDesc.guidance -- IR=2, Radar Active=3, Radar Semi Active=4, Radar Passive = 5
local guidance = WeaponDesc.guidance or 4 -- IR=2, Radar Active=3, Radar Semi Active=4, Radar Passive = 5
if guidance == 2 then
warndist = 10
elseif guidance == 3 then
@ -2689,8 +2872,9 @@ end
-- @param #AWACS self
-- @param #string Callsign Callsign to address
-- @param #number GID GroupID for comms
-- @param #boolean Tactical Is for tactical info
-- @return #AWACS self
function AWACS:_CreateBogeyDope(Callsign,GID)
function AWACS:_CreateBogeyDope(Callsign,GID,Tactical)
self:T(self.lid.."_CreateBogeyDope for "..Callsign.." GID "..GID)
local managedgroup = self.ManagedGrps[GID] -- #AWACS.ManagedGroup
@ -2716,7 +2900,7 @@ function AWACS:_CreateBogeyDope(Callsign,GID)
local tag = contact.TargetGroupNaming
local reportingname = contact.ReportingName
-- DONE - add tag
self:_AnnounceContact(contact,false,group,true,tag,false,reportingname)
self:_AnnounceContact(contact,false,group,true,tag,false,reportingname,Tactical)
end
end
@ -2841,8 +3025,9 @@ end
--- [Internal] AWACS Menu for Bogey Dope
-- @param #AWACS self
-- @param Wrapper.Group#GROUP Group Group to use
-- @param #boolean Tactical Check for tactical info
-- @return #AWACS self
function AWACS:_BogeyDope(Group)
function AWACS:_BogeyDope(Group,Tactical)
self:T(self.lid.."_BogeyDope")
local text = ""
local textScreen = ""
@ -2853,8 +3038,7 @@ function AWACS:_BogeyDope(Group)
-- no intel yet!
local clean = self.gettext:GetEntry("CLEAN",self.locale)
text = string.format(clean,self:_GetCallSign(Group,GID) or "Ghost 1", self.callsigntxt)
self:_NewRadioEntry(text,text,0,false,true,true,false,true)
self:_NewRadioEntry(text,text,0,false,true,true,false,true,Tactical)
return self
end
@ -2896,7 +3080,7 @@ function AWACS:_BogeyDope(Group)
local clean = self.gettext:GetEntry("CLEAN",self.locale)
text = string.format(clean,self:_GetCallSign(Group,GID) or "Ghost 1", self.callsigntxt)
self:_NewRadioEntry(text,textScreen,GID,Outcome,Outcome,true,false,true)
self:_NewRadioEntry(text,textScreen,GID,Outcome,Outcome,true,false,true,Tactical)
else
@ -2915,9 +3099,9 @@ function AWACS:_BogeyDope(Group)
textScreen = string.format("%s%d %s.\n",textScreen,contactsAO,groupstxt)
end
self:_NewRadioEntry(text,textScreen,GID,Outcome,true,true,false,true)
self:_NewRadioEntry(text,textScreen,GID,Outcome,true,true,false,true,Tactical)
self:_CreateBogeyDope(self:_GetCallSign(Group,GID) or "Ghost 1",GID)
self:_CreateBogeyDope(self:_GetCallSign(Group,GID) or "Ghost 1",GID,Tactical)
end
end
@ -2925,7 +3109,7 @@ function AWACS:_BogeyDope(Group)
-- no, unknown
local nocheckin = self.gettext:GetEntry("NOTCHECKEDIN",self.locale)
text = string.format(nocheckin,self:_GetCallSign(Group,GID) or "Ghost 1", self.callsigntxt)
self:_NewRadioEntry(text,text,GID,Outcome,true,true,false)
self:_NewRadioEntry(text,text,GID,Outcome,true,true,false,Tactical)
end
return self
@ -3528,11 +3712,17 @@ function AWACS:_CheckOut(Group,GID,dead)
local managedgroup = self.ManagedGrps[GID] -- #AWACS.ManagedGroup
local Stack = managedgroup.AnchorStackNo
local Angels = managedgroup.AnchorStackAngels
local GroupName = managedgroup.GroupName
-- remove menus
if managedgroup.IsPlayer then
if self.clientmenus:HasUniqueID(managedgroup.GroupName) then
local menus = self.clientmenus:PullByID(managedgroup.GroupName) --#AWACS.MenuStructure
if self.clientmenus:HasUniqueID(GroupName) then
local menus = self.clientmenus:PullByID(GroupName) --#AWACS.MenuStructure
menus.basemenu:Remove()
if self.TacticalSubscribers[GroupName] then
local Freq = self.TacticalSubscribers[GroupName]
self.TacticalFrequencies[Freq] = Freq
self.TacticalSubscribers[GroupName] = nil
end
end
end
-- delete open tasks
@ -3614,6 +3804,22 @@ function AWACS:_SetClientMenus()
local friendly = MENU_GROUP_COMMAND:New(cgrp,"Friendly",vid,self._VID,self,cgrp,AWACS.IFF.FRIENDLY)
end
local tactical
if self.TacticalMenu then
tactical = MENU_GROUP:New(cgrp,"Tactical Radio",basemenu)
if self.TacticalSubscribers[cgrpname] then
-- unsubscribe
local entry = MENU_GROUP_COMMAND:New(cgrp,"Unsubscribe",tactical,self._UnsubScribeTactRadio,self,cgrp)
else
-- subscribe
for _,_freq in UTILS.spairs(self.TacticalFrequencies) do
local modu = UTILS.GetModulationName(self.TacticalModulation)
local text = string.format("Subscribe to %.3f %s",_freq,modu)
local entry = MENU_GROUP_COMMAND:New(cgrp,text,tactical,self._SubScribeTactRadio,self,cgrp,_freq)
end
end
end
local ainfo = MENU_GROUP_COMMAND:New(cgrp,"Awacs Info",basemenu,self._ShowAwacsInfo,self,cgrp)
local checkout = MENU_GROUP_COMMAND:New(cgrp,"Check Out",basemenu,self._CheckOut,self,cgrp)
@ -3631,6 +3837,7 @@ function AWACS:_SetClientMenus()
unable = unable,
abort = abort,
commit=commit,
tactical=tactical,
}
self.clientmenus:PullByID(cgrpname)
self.clientmenus:Push(menus,cgrpname)
@ -4812,8 +5019,9 @@ end
-- @param #string Tag Tag name for this contact. Alpha, Brave, Charlie ...
-- @param #boolean IsPopup This is a pop-up group
-- @param #string ReportingName The NATO code reporting name for the contact, e.g. "Foxbat". "Bogey" if unknown.
-- @param #boolean Tactical
-- @return #AWACS self
function AWACS:_AnnounceContact(Contact,IsNew,Group,IsBogeyDope,Tag,IsPopup,ReportingName)
function AWACS:_AnnounceContact(Contact,IsNew,Group,IsBogeyDope,Tag,IsPopup,ReportingName,Tactical)
self:T(self.lid.."_AnnounceContact")
-- do we have a group to talk to?
local tag = ""
@ -4940,7 +5148,7 @@ function AWACS:_AnnounceContact(Contact,IsNew,Group,IsBogeyDope,Tag,IsPopup,Repo
BRAText = string.gsub(BRAText,"BRA","brah")
local prio = IsNew or IsBogeyDope
self:_NewRadioEntry(BRAText,TextScreen,GID,isGroup,true,IsNew,false,prio)
self:_NewRadioEntry(BRAText,TextScreen,GID,isGroup,true,IsNew,false,prio,Tactical)
return self
end
@ -5328,6 +5536,15 @@ function AWACS:_MergedCall(GID)
local merge = self.gettext:GetEntry("MERGED",self.locale)
local text = string.format("%s. %s. %s.",self.callsigntxt,pilotcallsign,merge)
self:_NewRadioEntry(text,text,GID,true,self.debug,true,false,true)
if GID and GID ~= 0 then
local managedgroup = self.ManagedGrps[GID] -- #AWACS.ManagedGroup
if managedgroup and managedgroup.Group and managedgroup.Group:IsAlive() then
local name = managedgroup.GroupName
if self.TacticalSubscribers[name] then
self:_NewRadioEntry(text,text,GID,true,self.debug,true,false,true,true)
end
end
end
return self
end
@ -5703,6 +5920,10 @@ function AWACS:onafterStart(From, Event, To)
self:__Started(-5)
end
if self.TacticalMenu then
self:__CheckTacticalQueue(55)
end
self:__Status(-30)
return self
end
@ -5976,6 +6197,8 @@ function AWACS:onafterStatus(From, Event, To)
self:_CheckMerges()
self:_CheckSubscribers()
local outcome, targets = self:_TargetSelectionProcess(true)
self:_CheckTaskQueue()
@ -6316,6 +6539,77 @@ function AWACS:onafterLostCluster(From,Event,To,Cluster,Mission)
return self
end
--- [Internal] onafterCheckTacticalQueue
-- @param #AWACS self
-- @param #string From
-- @param #string Event
-- @param #string To
-- @return #AWACS self
function AWACS:onafterCheckTacticalQueue(From,Event,To)
self:T({From, Event, To})
-- do we have messages queued?
if self.clientset:CountAlive() == 0 then
self:T(self.lid.."No player connected.")
self:__CheckTacticalQueue(-5)
return self
end
for _name,_freq in pairs(self.TacticalSubscribers) do
local Group = nil
if _name then
Group = GROUP:FindByName(_name)
end
if Group and Group:IsAlive() then
self:_BogeyDope(Group,true)
end
end
if (self.TacticalQueue:IsNotEmpty()) then
while self.TacticalQueue:Count() > 0 do
local RadioEntry = self.TacticalQueue:Pull() -- #AWACS.RadioEntry
self:T({RadioEntry})
local frequency = self.TacticalBaseFreq
if RadioEntry.GroupID and RadioEntry.GroupID ~= 0 then
local managedgroup = self.ManagedGrps[RadioEntry.GroupID] -- #AWACS.ManagedGroup
if managedgroup and managedgroup.Group and managedgroup.Group:IsAlive() then
local name = managedgroup.GroupName
frequency = self.TacticalSubscribers[name]
end
end
-- AI AWACS Speaking
local gtext = RadioEntry.TextTTS
if self.PathToGoogleKey then
gtext = string.format("<speak><prosody rate='medium'>%s</prosody></speak>",gtext)
end
self.TacticalSRSQ:NewTransmission(gtext,nil,self.TacticalSRS,nil,0.5,nil,nil,nil,frequency,self.TacticalModulation,nil,nil,nil,nil,nil)
self:T(RadioEntry.TextTTS)
if RadioEntry.ToScreen and RadioEntry.TextScreen and (not self.SuppressScreenOutput) then
if RadioEntry.GroupID and RadioEntry.GroupID ~= 0 then
local managedgroup = self.ManagedGrps[RadioEntry.GroupID] -- #AWACS.ManagedGroup
if managedgroup and managedgroup.Group and managedgroup.Group:IsAlive() then
MESSAGE:New(RadioEntry.TextScreen,20,"AWACS"):ToGroup(managedgroup.Group)
self:T(RadioEntry.TextScreen)
end
else
MESSAGE:New(RadioEntry.TextScreen,20,"AWACS"):ToCoalition(self.coalition)
end
end
end
end -- end while
if self:Is("Running") then
self:__CheckTacticalQueue(-self.TacticalInterval)
end
return self
end
--- [Internal] onafterCheckRadioQueue
-- @param #AWACS self
-- @param #string From

View File

@ -1485,6 +1485,17 @@ function CHIEF:AddGciCapZone(Zone, Altitude, Speed, Heading, Leg)
return zone
end
--- Remove a GCI CAP
-- @param #CHIEF self
-- @param Core.Zone#ZONE Zone Zone, where the flight orbits.
function CHIEF:RemoveGciCapZone(Zone)
-- Hand over to commander.
local zone=self.commander:RemoveGciCapZone(Zone)
return zone
end
--- Add an AWACS zone.
-- @param #CHIEF self
-- @param Core.Zone#ZONE Zone Zone.
@ -1501,6 +1512,17 @@ function CHIEF:AddAwacsZone(Zone, Altitude, Speed, Heading, Leg)
return zone
end
--- Remove a AWACS zone.
-- @param #CHIEF self
-- @param Core.Zone#ZONE Zone Zone, where the flight orbits.
function CHIEF:RemoveAwacsZone(Zone)
-- Hand over to commander.
local zone=self.commander:RemoveAwacsZone(Zone)
return zone
end
--- Add a refuelling tanker zone.
-- @param #CHIEF self
-- @param Core.Zone#ZONE Zone Zone.
@ -1518,6 +1540,16 @@ function CHIEF:AddTankerZone(Zone, Altitude, Speed, Heading, Leg, RefuelSystem)
return zone
end
--- Remove a refuelling tanker zone.
-- @param #CHIEF self
-- @param Core.Zone#ZONE Zone Zone, where the flight orbits.
function CHIEF:RemoveTankerZone(Zone)
-- Hand over to commander.
local zone=self.commander:RemoveTankerZone(Zone)
return zone
end
--- Set border zone set, defining your territory.
--

View File

@ -710,6 +710,26 @@ function COMMANDER:AddGciCapZone(Zone, Altitude, Speed, Heading, Leg)
return patrolzone
end
--- Remove a GCI CAP.
-- @param #COMMANDER self
-- @param Core.Zone#ZONE Zone Zone, where the flight orbits.
function COMMANDER:RemoveGciCapZone(Zone)
local patrolzone={} --Ops.AirWing#AIRWING.PatrolZone
patrolzone.zone=Zone
for i,_patrolzone in pairs(self.gcicapZones) do
if _patrolzone.zone == patrolzone.zone then
if _patrolzone.mission and _patrolzone.mission:IsNotOver() then
_patrolzone.mission:Cancel()
end
table.remove(self.gcicapZones, i)
break
end
end
return patrolzone
end
--- Add an AWACS zone.
-- @param #COMMANDER self
-- @param Core.Zone#ZONE Zone Zone.
@ -735,6 +755,26 @@ function COMMANDER:AddAwacsZone(Zone, Altitude, Speed, Heading, Leg)
return awacszone
end
--- Remove a AWACS zone.
-- @param #COMMANDER self
-- @param Core.Zone#ZONE Zone Zone, where the flight orbits.
function COMMANDER:RemoveAwacsZone(Zone)
local awacszone={} --Ops.AirWing#AIRWING.PatrolZone
awacszone.zone=Zone
for i,_awacszone in pairs(self.awacsZones) do
if _awacszone.zone == awacszone.zone then
if _awacszone.mission and _awacszone.mission:IsNotOver() then
_awacszone.mission:Cancel()
end
table.remove(self.awacsZones, i)
break
end
end
return awacszone
end
--- Add a refuelling tanker zone.
-- @param #COMMANDER self
-- @param Core.Zone#ZONE Zone Zone.
@ -762,6 +802,26 @@ function COMMANDER:AddTankerZone(Zone, Altitude, Speed, Heading, Leg, RefuelSyst
return tankerzone
end
--- Remove a refuelling tanker zone.
-- @param #COMMANDER self
-- @param Core.Zone#ZONE Zone Zone, where the flight orbits.
function COMMANDER:RemoveTankerZone(Zone)
local tankerzone={} --Ops.AirWing#AIRWING.PatrolZone
tankerzone.zone=Zone
for i,_tankerzone in pairs(self.tankerZones) do
if _tankerzone.zone == tankerzone.zone then
if _tankerzone.mission and _tankerzone.mission:IsNotOver() then
_tankerzone.mission:Cancel()
end
table.remove(self.tankerZones, i)
break
end
end
return tankerzone
end
--- Check if this mission is already in the queue.
-- @param #COMMANDER self
-- @param Ops.Auftrag#AUFTRAG Mission The mission.

View File

@ -325,7 +325,322 @@ function FLIGHTGROUP:New(group)
-- @param #FLIGHTGROUP self
-- @param #number delay Delay in seconds.
-- TODO: Add pseudo functions.
--- FSM Function OnAfterElementSpawned.
-- @function [parent=#FLIGHTGROUP] OnAfterElementSpawned
-- @param #FLIGHTGROUP self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param Ops.OpsGroup#OPSGROUP.Element Element The flight group element.
--- FSM Function OnAfterElementParking.
-- @function [parent=#FLIGHTGROUP] OnAfterElementParking
-- @param #FLIGHTGROUP self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param Ops.OpsGroup#OPSGROUP.Element Element The flight group element.
-- @param Wrapper.Airbase#AIRBASE.ParkingSpot Spot Parking Spot.
--- FSM Function OnAfterElementEngineOn.
-- @function [parent=#FLIGHTGROUP] OnAfterElementEngineOn
-- @param #FLIGHTGROUP self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param Ops.OpsGroup#OPSGROUP.Element Element The flight group element.
--- FSM Function OnAfterElementTaxiing.
-- @function [parent=#FLIGHTGROUP] OnAfterElementTaxiing
-- @param #FLIGHTGROUP self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param Ops.OpsGroup#OPSGROUP.Element Element The flight group element.
--- FSM Function OnAfterElementTakeoff.
-- @function [parent=#FLIGHTGROUP] OnAfterElementTakeoff
-- @param #FLIGHTGROUP self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param Ops.OpsGroup#OPSGROUP.Element Element The flight group element.
-- @param Wrapper.Airbase#AIRBASE airbase The airbase if applicable or nil.
--- FSM Function OnAfterElementAirborne.
-- @function [parent=#FLIGHTGROUP] OnAfterElementAirborne
-- @param #FLIGHTGROUP self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param Ops.OpsGroup#OPSGROUP.Element Element The flight group element.
--- FSM Function OnAfterElementLanded.
-- @function [parent=#FLIGHTGROUP] OnAfterElementLanded
-- @param #FLIGHTGROUP self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param Ops.OpsGroup#OPSGROUP.Element Element The flight group element.
-- @param Wrapper.Airbase#AIRBASE airbase The airbase if applicable or nil.
--- FSM Function OnAfterElementArrived.
-- @function [parent=#FLIGHTGROUP] OnAfterElementArrived
-- @param #FLIGHTGROUP self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param Ops.OpsGroup#OPSGROUP.Element Element The flight group element.
-- @param Wrapper.Airbase#AIRBASE airbase The airbase, where the element arrived.
-- @param Wrapper.Airbase#AIRBASE.ParkingSpot Parking The Parking spot the element has.
--- FSM Function OnAfterElementDestroyed.
-- @function [parent=#FLIGHTGROUP] OnAfterElementDestroyed
-- @param #FLIGHTGROUP self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param Ops.OpsGroup#OPSGROUP.Element Element The flight group element.
--- FSM Function OnAfterElementDead.
-- @function [parent=#FLIGHTGROUP] OnAfterElementDead
-- @param #FLIGHTGROUP self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param Ops.OpsGroup#OPSGROUP.Element Element The flight group element.
--- FSM Function OnAfterSpawned.
-- @function [parent=#FLIGHTGROUP] OnAfterSpawned
-- @param #FLIGHTGROUP self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
--- FSM Function OnAfterParking.
-- @function [parent=#FLIGHTGROUP] OnAfterParking
-- @param #FLIGHTGROUP self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
--- FSM Function OnAfterTaxiing.
-- @function [parent=#FLIGHTGROUP] OnAfterTaxiing
-- @param #FLIGHTGROUP self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
--- FSM Function OnAfterTakeoff.
-- @function [parent=#FLIGHTGROUP] OnAfterTakeoff
-- @param #FLIGHTGROUP self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
--- FSM Function OnAfterAirborne.
-- @function [parent=#FLIGHTGROUP] OnAfterAirborne
-- @param #FLIGHTGROUP self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
--- FSM Function OnAfterCruise.
-- @function [parent=#FLIGHTGROUP] OnAfterCruise
-- @param #FLIGHTGROUP self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
--- FSM Function OnAfterLanding.
-- @function [parent=#FLIGHTGROUP] OnAfterLanding
-- @param #FLIGHTGROUP self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
--- FSM Function OnAfterLanded.
-- @function [parent=#FLIGHTGROUP] OnAfterLanded
-- @param #FLIGHTGROUP self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param Wrapper.Airbase#AIRBASE airbase The airbase the flight landed.
--- FSM Function OnAfterLandedAt.
-- @function [parent=#FLIGHTGROUP] OnAfterLandedAt
-- @param #FLIGHTGROUP self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
--- FSM Function OnAfterArrived.
-- @function [parent=#FLIGHTGROUP] OnAfterArrived
-- @param #FLIGHTGROUP self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
--- FSM Function OnAfterDead.
-- @function [parent=#FLIGHTGROUP] OnAfterDead
-- @param #FLIGHTGROUP self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
--- FSM Function OnAfterUpdateRoute.
-- @function [parent=#FLIGHTGROUP] OnAfterUpdateRoute
-- @param #FLIGHTGROUP self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param #number n Next waypoint index. Default is the one coming after that one that has been passed last.
-- @param #number N Waypoint Max waypoint index to be included in the route. Default is the final waypoint.
--- FSM Function OnAfterOutOfMissilesAA.
-- @function [parent=#FLIGHTGROUP] OnAfterOutOfMissilesAA
-- @param #FLIGHTGROUP self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
--- FSM Function OnAfterOutOfMissilesAG.
-- @function [parent=#FLIGHTGROUP] OnAfterOutOfMissilesAG
-- @param #FLIGHTGROUP self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
--- FSM Function OnAfterRTB.
-- @function [parent=#FLIGHTGROUP] OnAfterRTB
-- @param #FLIGHTGROUP self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param Wrapper.Airbase#AIRBASE airbase The airbase to hold at.
-- @param #number SpeedTo Speed used for traveling from current position to holding point in knots. Default 75% of max speed.
-- @param #number SpeedHold Holding speed in knots. Default 250 kts.
-- @param #number SpeedLand Landing speed in knots. Default 170 kts.
--- FSM Function OnAfterLandAtAirbase.
-- @function [parent=#FLIGHTGROUP] OnAfterLandAtAirbase
-- @param #FLIGHTGROUP self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param Wrapper.Airbase#AIRBASE airbase The airbase to hold at.
--- FSM Function OnAfterWait.
-- @function [parent=#FLIGHTGROUP] OnAfterWait
-- @param #FLIGHTGROUP self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param #number Duration Duration how long the group will be waiting in seconds. Default `nil` (=forever).
-- @param #number Altitude Altitude in feet. Default 10,000 ft for airplanes and 1,000 feet for helos.
-- @param #number Speed Speed in knots. Default 250 kts for airplanes and 20 kts for helos.
--- FSM Function OnAfterRefuel.
-- @function [parent=#FLIGHTGROUP] OnAfterRefuel
-- @param #FLIGHTGROUP self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param Core.Point#COORDINATE Coordinate The coordinate.
--- FSM Function OnAfterRefueled.
-- @function [parent=#FLIGHTGROUP] OnAfterRefueled
-- @param #FLIGHTGROUP self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
--- FSM Function OnAfterDisengage.
-- @function [parent=#FLIGHTGROUP] OnAfterDisengage
-- @param #FLIGHTGROUP self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param Core.Set#SET_UNIT TargetUnitSet
--- FSM Function OnAfterEngageTarget.
-- @function [parent=#FLIGHTGROUP] OnAfterEngageTarget
-- @param #FLIGHTGROUP self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param #table Target Target object. Can be a UNIT, STATIC, GROUP, SET_UNIT or SET_GROUP object.
--- FSM Function OnAfterLandAt.
-- @function [parent=#FLIGHTGROUP] OnAfterLandAt
-- @param #FLIGHTGROUP self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param Core.Set#SET_UNIT TargetUnitSet
--- FSM Function OnAfterFuelLow.
-- @function [parent=#FLIGHTGROUP] OnAfterFuelLow
-- @param #FLIGHTGROUP self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
--- FSM Function OnAfterFuelCritical.
-- @function [parent=#FLIGHTGROUP] OnAfterFuelCritical
-- @param #FLIGHTGROUP self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
--- FSM Function OnBeforeUpdateRoute.
-- @function [parent=#FLIGHTGROUP] OnBeforeUpdateRoute
-- @param #FLIGHTGROUP self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param #number n Next waypoint index. Default is the one coming after that one that has been passed last.
-- @param #number N Waypoint Max waypoint index to be included in the route. Default is the final waypoint.
-- @return #boolean Transision allowed?
--- FSM Function OnBeforeRTB.
-- @function [parent=#FLIGHTGROUP] OnBeforeRTB
-- @param #FLIGHTGROUP self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param Wrapper.Airbase#AIRBASE airbase The airbase to hold at.
-- @param #number SpeedTo Speed used for travelling from current position to holding point in knots.
-- @param #number SpeedHold Holding speed in knots.
--- FSM Function OnBeforeLandAtAirbase.
-- @function [parent=#FLIGHTGROUP] OnBeforeLandAtAirbase
-- @param #FLIGHTGROUP self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param Wrapper.Airbase#AIRBASE airbase The airbase to hold at.
--- FSM Function OnBeforeWait.
-- @function [parent=#FLIGHTGROUP] OnBeforeWait
-- @param #FLIGHTGROUP self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param #number Duration Duration how long the group will be waiting in seconds. Default `nil` (=forever).
-- @param #number Altitude Altitude in feet. Default 10,000 ft for airplanes and 1,000 feet for helos.
-- @param #number Speed Speed in knots. Default 250 kts for airplanes and 20 kts for helos.
--- FSM Function OnBeforeLandAt.
-- @function [parent=#FLIGHTGROUP] OnBeforeLandAt
-- @param #FLIGHTGROUP self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param Core.Point#COORDINATE Coordinate The coordinate where to land. Default is current position.
-- @param #number Duration The duration in seconds to remain on ground. Default 600 sec (10 min).
-- TODO: Add pseudo functions ? Normally done, but should be double check
-- Handle events:
self:HandleEvent(EVENTS.Birth, self.OnEventBirth)
@ -4176,11 +4491,13 @@ function FLIGHTGROUP:GetClosestAirbase()
local coord=group:GetCoordinate()
local coalition=self:GetCoalition()
local airbase=coord:GetClosestAirbase() --(nil, coalition)
return airbase
if coord then
local airbase=coord:GetClosestAirbase() --(nil, coalition)
return airbase
else
return nil
end
else
return nil

View File

@ -21,7 +21,7 @@
-- ===
-- @module Ops.PlayerTask
-- @image OPS_PlayerTask.jpg
-- @date Last Update June 2023
-- @date Last Update July 2023
do
@ -98,7 +98,7 @@ PLAYERTASK = {
--- PLAYERTASK class version.
-- @field #string version
PLAYERTASK.version="0.1.17"
PLAYERTASK.version="0.1.19"
--- Generic task condition.
-- @type PLAYERTASK.Condition
@ -1008,6 +1008,7 @@ do
-- @field Core.ClientMenu#CLIENTMENUMANAGER ActiveTaskMenuTemplate
-- @field Core.ClientMenu#CLIENTMENU ActiveTopMenu
-- @field Core.ClientMenu#CLIENTMENU ActiveInfoMenu
-- @field Core.ClientMenu#CLIENTMENU MenuNoTask
-- @extends Core.Fsm#FSM
---
@ -1336,6 +1337,7 @@ PLAYERTASKCONTROLLER = {
InfoHasCoordinate = false,
UseTypeNames = false,
Scoring = nil,
MenuNoTask = nil,
}
---
@ -1654,13 +1656,6 @@ function PLAYERTASKCONTROLLER:New(Name, Coalition, Type, ClientFilter)
local starttime = math.random(5,10)
self:__Status(starttime)
-- Player leaves
self:HandleEvent(EVENTS.PlayerLeaveUnit, self._EventHandler)
self:HandleEvent(EVENTS.Ejection, self._EventHandler)
self:HandleEvent(EVENTS.Crash, self._EventHandler)
self:HandleEvent(EVENTS.PilotDead, self._EventHandler)
self:HandleEvent(EVENTS.PlayerEnterAircraft, self._EventHandler)
self:I(self.lid..self.version.." Started.")
return self
@ -2175,7 +2170,7 @@ end
-- @return #PLAYERTASKCONTROLLER self
function PLAYERTASKCONTROLLER:_EventHandler(EventData)
self:T(self.lid.."_EventHandler: "..EventData.id)
self:T(self.lid.."_EventHandler: "..EventData.IniPlayerName)
--self:T(self.lid.."_EventHandler: "..EventData.IniPlayerName)
if EventData.id == EVENTS.PlayerLeaveUnit or EventData.id == EVENTS.Ejection or EventData.id == EVENTS.Crash or EventData.id == EVENTS.PilotDead then
if EventData.IniPlayerName then
self:T(self.lid.."Event for player: "..EventData.IniPlayerName)
@ -2481,6 +2476,15 @@ function PLAYERTASKCONTROLLER:_CheckTaskQueue()
self:T("*****Removing player " .. _id)
self.TasksPerPlayer:PullByID(_id)
end
local clients=task:GetClientObjects()
for _,client in pairs(clients) do
self:_RemoveMenuEntriesForTask(task,client)
--self:_SwitchMenuForClient(client,"Info")
end
for _,client in pairs(clients) do
-- self:_RemoveMenuEntriesForTask(Task,client)
self:_SwitchMenuForClient(client,"Info",5)
end
-- Follow-up tasks?
local nexttasks = {}
if task.FinalState == "Success" then
@ -3469,6 +3473,19 @@ function PLAYERTASKCONTROLLER:_UpdateJoinMenuTemplate()
local actinfomenu = self.ActiveInfoMenu
--local entrynumbers = {}
--local existingentries = {}
if self.TaskQueue:Count() == 0 and self.MenuNoTask == nil then
local menunotasks = self.gettext:GetEntry("MENUNOTASKS",self.locale)
self.MenuNoTask = controller:NewEntry(menunotasks,self.JoinMenu)
controller:AddEntry(self.MenuNoTask)
end
if self.TaskQueue:Count() > 0 and self.MenuNoTask ~= nil then
controller:DeleteGenericEntry(self.MenuNoTask)
controller:DeleteF10Entry(self.MenuNoTask)
self.MenuNoTask = nil
end
local maxn = self.menuitemlimit
-- Generate task type menu items
for _type,_ in pairs(taskpertype) do
@ -3579,28 +3596,28 @@ function PLAYERTASKCONTROLLER:_RemoveMenuEntriesForTask(Task,Client)
if Task then
if Task.UUIDS and self.JoinTaskMenuTemplate then
--self:I("***** JoinTaskMenuTemplate")
UTILS.PrintTableToLog(Task.UUIDS)
--UTILS.PrintTableToLog(Task.UUIDS)
local controller = self.JoinTaskMenuTemplate
for _,_uuid in pairs(Task.UUIDS) do
local Entry = controller:FindEntryByUUID(_uuid)
if Entry then
controller:DeleteF10Entry(Entry,Client)
controller:DeleteGenericEntry(Entry)
UTILS.PrintTableToLog(controller.menutree)
--UTILS.PrintTableToLog(controller.menutree)
end
end
end
if Task.AUUIDS and self.ActiveTaskMenuTemplate then
--self:I("***** ActiveTaskMenuTemplate")
UTILS.PrintTableToLog(Task.AUUIDS)
--UTILS.PrintTableToLog(Task.AUUIDS)
for _,_uuid in pairs(Task.AUUIDS) do
local controller = self.ActiveTaskMenuTemplate
local Entry = controller:FindEntryByUUID(_uuid)
if Entry then
controller:DeleteF10Entry(Entry,Client)
controller:DeleteGenericEntry(Entry)
UTILS.PrintTableToLog(controller.menutree)
--UTILS.PrintTableToLog(controller.menutree)
end
end
end
@ -3641,8 +3658,13 @@ function PLAYERTASKCONTROLLER:_CreateJoinMenuTemplate()
self.JoinInfoMenu = JoinTaskMenuTemplate:NewEntry(menutaskinfo,self.JoinTopMenu)
end
if self.TaskQueue:Count() == 0 then
JoinTaskMenuTemplate:NewEntry(menunotasks,self.JoinMenu)
if self.TaskQueue:Count() == 0 and self.MenuNoTask == nil then
self.MenuNoTask = JoinTaskMenuTemplate:NewEntry(menunotasks,self.JoinMenu)
end
if self.TaskQueue:Count() > 0 and self.MenuNoTask ~= nil then
JoinTaskMenuTemplate:DeleteGenericEntry(self.MenuNoTask)
self.MenuNoTask = nil
end
self.JoinTaskMenuTemplate = JoinTaskMenuTemplate
@ -4043,6 +4065,12 @@ function PLAYERTASKCONTROLLER:onafterStart(From, Event, To)
self:T(self.lid.."onafterStart")
self:_CreateJoinMenuTemplate()
self:_CreateActiveTaskMenuTemplate()
-- Player Events
self:HandleEvent(EVENTS.PlayerLeaveUnit, self._EventHandler)
self:HandleEvent(EVENTS.Ejection, self._EventHandler)
self:HandleEvent(EVENTS.Crash, self._EventHandler)
self:HandleEvent(EVENTS.PilotDead, self._EventHandler)
self:HandleEvent(EVENTS.PlayerEnterAircraft, self._EventHandler)
return self
end
@ -4190,6 +4218,15 @@ function PLAYERTASKCONTROLLER:onafterTaskFailed(From, Event, To, Task)
taskname = string.format(failtxttts, self.MenuName or self.Name, Task.PlayerTaskNr, tostring(Task.TTSType))
self.SRSQueue:NewTransmission(taskname,nil,self.SRS,nil,2)
end
local clients=Task:GetClientObjects()
for _,client in pairs(clients) do
self:_RemoveMenuEntriesForTask(Task,client)
--self:_SwitchMenuForClient(client,"Info")
end
for _,client in pairs(clients) do
-- self:_RemoveMenuEntriesForTask(Task,client)
self:_SwitchMenuForClient(client,"Info",5)
end
return self
end

View File

@ -295,8 +295,9 @@ UTILS.DeepCopy = function(object)
end
--- Porting in Slmod's serialize_slmod2.
--- Serialize a given table.
-- @param #table tbl Input table.
-- @return #string Table as a string.
UTILS.OneLineSerialize = function( tbl ) -- serialization of a table all on a single line, no comments, made to replace old get_table_string function
local lookup_table = {}
@ -373,7 +374,54 @@ local lookup_table = {}
return objectreturn
end
--porting in Slmod's "safestring" basic serialize
--- Serialize a table to a single line string.
-- @param #table tbl table to serialize.
-- @return #string string containing serialized table.
function UTILS._OneLineSerialize(tbl)
if type(tbl) == 'table' then --function only works for tables!
local tbl_str = {}
tbl_str[#tbl_str + 1] = '{ '
for ind,val in pairs(tbl) do -- serialize its fields
if type(ind) == "number" then
tbl_str[#tbl_str + 1] = '['
tbl_str[#tbl_str + 1] = tostring(ind)
tbl_str[#tbl_str + 1] = '] = '
else --must be a string
tbl_str[#tbl_str + 1] = '['
tbl_str[#tbl_str + 1] = UTILS.BasicSerialize(ind)
tbl_str[#tbl_str + 1] = '] = '
end
if ((type(val) == 'number') or (type(val) == 'boolean')) then
tbl_str[#tbl_str + 1] = tostring(val)
tbl_str[#tbl_str + 1] = ', '
elseif type(val) == 'string' then
tbl_str[#tbl_str + 1] = UTILS.BasicSerialize(val)
tbl_str[#tbl_str + 1] = ', '
elseif type(val) == 'nil' then -- won't ever happen, right?
tbl_str[#tbl_str + 1] = 'nil, '
elseif type(val) == 'table' then
tbl_str[#tbl_str + 1] = UTILS._OneLineSerialize(val)
tbl_str[#tbl_str + 1] = ', ' --I think this is right, I just added it
else
--log:warn('Unable to serialize value type $1 at index $2', mist.utils.basicSerialize(type(val)), tostring(ind))
end
end
tbl_str[#tbl_str + 1] = '}'
return table.concat(tbl_str)
else
return UTILS.BasicSerialize(tbl)
end
end
--- Basic serialize (porting in Slmod's "safestring" basic serialize).
-- @param #string s Table to serialize.
UTILS.BasicSerialize = function(s)
if s == nil then
return "\"\""
@ -404,6 +452,114 @@ function UTILS.PrintTableToLog(table, indent)
end
end
--- Returns table in a easy readable string representation.
-- @param tbl table to show
-- @param loc
-- @param indent
-- @param tableshow_tbls
-- @return Human readable string representation of given table.
function UTILS.TableShow(tbl, loc, indent, tableshow_tbls)
tableshow_tbls = tableshow_tbls or {} --create table of tables
loc = loc or ""
indent = indent or ""
if type(tbl) == 'table' then --function only works for tables!
tableshow_tbls[tbl] = loc
local tbl_str = {}
tbl_str[#tbl_str + 1] = indent .. '{\n'
for ind,val in pairs(tbl) do -- serialize its fields
if type(ind) == "number" then
tbl_str[#tbl_str + 1] = indent
tbl_str[#tbl_str + 1] = loc .. '['
tbl_str[#tbl_str + 1] = tostring(ind)
tbl_str[#tbl_str + 1] = '] = '
else
tbl_str[#tbl_str + 1] = indent
tbl_str[#tbl_str + 1] = loc .. '['
tbl_str[#tbl_str + 1] = UTILS.BasicSerialize(ind)
tbl_str[#tbl_str + 1] = '] = '
end
if ((type(val) == 'number') or (type(val) == 'boolean')) then
tbl_str[#tbl_str + 1] = tostring(val)
tbl_str[#tbl_str + 1] = ',\n'
elseif type(val) == 'string' then
tbl_str[#tbl_str + 1] = UTILS.BasicSerialize(val)
tbl_str[#tbl_str + 1] = ',\n'
elseif type(val) == 'nil' then -- won't ever happen, right?
tbl_str[#tbl_str + 1] = 'nil,\n'
elseif type(val) == 'table' then
if tableshow_tbls[val] then
tbl_str[#tbl_str + 1] = tostring(val) .. ' already defined: ' .. tableshow_tbls[val] .. ',\n'
else
tableshow_tbls[val] = loc .. '[' .. UTILS.BasicSerialize(ind) .. ']'
tbl_str[#tbl_str + 1] = tostring(val) .. ' '
tbl_str[#tbl_str + 1] = UTILS.TableShow(val, loc .. '[' .. UTILS.BasicSerialize(ind).. ']', indent .. ' ', tableshow_tbls)
tbl_str[#tbl_str + 1] = ',\n'
end
elseif type(val) == 'function' then
if debug and debug.getinfo then
local fcnname = tostring(val)
local info = debug.getinfo(val, "S")
if info.what == "C" then
tbl_str[#tbl_str + 1] = string.format('%q', fcnname .. ', C function') .. ',\n'
else
if (string.sub(info.source, 1, 2) == [[./]]) then
tbl_str[#tbl_str + 1] = string.format('%q', fcnname .. ', defined in (' .. info.linedefined .. '-' .. info.lastlinedefined .. ')' .. info.source) ..',\n'
else
tbl_str[#tbl_str + 1] = string.format('%q', fcnname .. ', defined in (' .. info.linedefined .. '-' .. info.lastlinedefined .. ')') ..',\n'
end
end
else
tbl_str[#tbl_str + 1] = 'a function,\n'
end
else
tbl_str[#tbl_str + 1] = 'unable to serialize value type ' .. UTILS.BasicSerialize(type(val)) .. ' at index ' .. tostring(ind)
end
end
tbl_str[#tbl_str + 1] = indent .. '}'
return table.concat(tbl_str)
end
end
--- Dumps the global table _G.
-- This dumps the global table _G to a file in the DCS\Logs directory.
-- This function requires you to disable script sanitization in $DCS_ROOT\Scripts\MissionScripting.lua to access lfs and io libraries.
-- @param #string fname File name.
function UTILS.Gdump(fname)
if lfs and io then
local fdir = lfs.writedir() .. [[Logs\]] .. fname
local f = io.open(fdir, 'w')
f:write(UTILS.TableShow(_G))
f:close()
env.info(string.format('Wrote debug data to $1', fdir))
else
env.error("WARNING: lfs and/or io not de-sanitized - cannot dump _G!")
end
end
--- Executes the given string.
-- borrowed from Slmod
-- @param #string s string containing LUA code.
-- @return #boolean `true` if successfully executed, `false` otherwise.
function UTILS.DoString(s)
local f, err = loadstring(s)
if f then
return true, f()
else
return false, err
end
end
UTILS.ToDegree = function(angle)
return angle*180/math.pi

View File

@ -11,7 +11,7 @@
-- @module Wrapper.Airbase
-- @image Wrapper_Airbase.JPG
---
-- @type AIRBASE
-- @field #string ClassName Name of the class, i.e. "AIRBASE".
-- @field #table CategoryName Names of airbase categories.
@ -548,6 +548,8 @@ AIRBASE.Syria={
-- * AIRBASE.MarianaIslands.Saipan_Intl
-- * AIRBASE.MarianaIslands.Tinian_Intl
-- * AIRBASE.MarianaIslands.Olf_Orote
-- * AIRBASE.MarianaIslands.Pagan_Airstrip
-- * AIRBASE.MarianaIslands.North_West_Field
--
-- @field MarianaIslands
AIRBASE.MarianaIslands = {
@ -557,6 +559,8 @@ AIRBASE.MarianaIslands = {
["Saipan_Intl"] = "Saipan Intl",
["Tinian_Intl"] = "Tinian Intl",
["Olf_Orote"] = "Olf Orote",
["Pagan_Airstrip"] = "Pagan Airstrip",
["North_West_Field"] = "North West Field",
}
--- Airbases of the South Atlantic map:
@ -787,7 +791,13 @@ function AIRBASE:Register(AirbaseName)
-- Category.
self.category=self.descriptors and self.descriptors.category or Airbase.Category.AIRDROME
-- H2 is bugged
--if self.AirbaseName == "H4" and self.descriptors == nil then
--self:E("***** H4 on Syria map is currently bugged!")
--return nil
--end
-- Set category.
if self.category==Airbase.Category.AIRDROME then
self.isAirdrome=true

View File

@ -1734,6 +1734,27 @@ function CONTROLLABLE:EnRouteTaskSEAD(TargetTypes, Priority)
return DCSTask
end
--- (AIR) Enroute CAP task.
-- @param #CONTROLLABLE self
-- @param DCS#AttributeNameArray TargetTypes Array of target categories allowed to engage. Default `{"Air"}`.
-- @param #number Priority (Optional) All en-route tasks have the priority parameter. This is a number (less value - higher priority) that determines actions related to what task will be performed first. Default 0.
-- @return DCS#Task The DCS task structure.
function CONTROLLABLE:EnRouteTaskCAP(TargetTypes, Priority)
local DCSTask = {
id = 'EngageTargets',
key = "CAP",
--auto = true,
enabled = true,
params = {
targetTypes = TargetTypes or {"Air"},
priority = Priority or 0
}
}
return DCSTask
end
--- (AIR) Engaging a controllable. The task does not assign the target controllable to the unit/controllable to attack now;
-- it just allows the unit/controllable to engage the target controllable as well as other assigned targets.
-- See [hoggit](https://wiki.hoggitworld.com/view/DCS_task_engageGroup).

View File

@ -18,6 +18,7 @@
-- @field #string SceneryName Name of the scenery object.
-- @field DCS#Object SceneryObject DCS scenery object.
-- @field #number Life0 Initial life points.
-- @field #table Properties
-- @extends Wrapper.Positionable#POSITIONABLE
@ -43,7 +44,7 @@ function SCENERY:Register( SceneryName, SceneryObject )
local self = BASE:Inherit( self, POSITIONABLE:New( SceneryName ) )
self.SceneryName = SceneryName
self.SceneryName = tostring(SceneryName)
self.SceneryObject = SceneryObject
@ -52,9 +53,43 @@ function SCENERY:Register( SceneryName, SceneryObject )
else
self.Life0 = 0
end
self.Properties = {}
return self
end
--- Returns the Value of the zone with the given PropertyName, or nil if no matching property exists.
-- @param #SCENERY self
-- @param #string PropertyName The name of a the QuadZone Property from the scenery assignment to be retrieved.
-- @return #string The Value of the QuadZone Property from the scenery assignment with the given PropertyName, or nil if absent.
function SCENERY:GetProperty(PropertyName)
return self.Properties[PropertyName]
end
--- Returns the scenery Properties table.
-- @param #SCENERY self
-- @return #table The Key:Value table of QuadZone properties of the zone from the scenery assignment .
function SCENERY:GetAllProperties()
return self.Properties
end
--- Set a scenery property
-- @param #SCENERY self
-- @param #string PropertyName
-- @param #string PropertyValue
-- @return #SCENERY self
function SCENERY:SetProperty(PropertyName, PropertyValue)
self.Properties[PropertyName] = PropertyValue
return self
end
--- Obtain object name.
--@param #SCENERY self
--@return #string Name
function SCENERY:GetName()
return self.SceneryName
end
--- Obtain DCS Object from the SCENERY Object.
--@param #SCENERY self
--@return DCS#Object DCS scenery object.
@ -115,26 +150,28 @@ end
--@param Core.Point#COORDINATE Coordinate Where to find the scenery object
--@param #number Radius (optional) Search radius around coordinate, defaults to 100
--@return #SCENERY Scenery Object or `nil` if it cannot be found
function SCENERY:FindByName(Name, Coordinate, Radius)
function SCENERY:FindByName(Name, Coordinate, Radius, Role)
local radius = Radius or 100
local name = Name or "unknown"
local scenery = nil
BASE:T({name, radius, Coordinate:GetVec2()})
---
-- @param Core.Point#COORDINATE coordinate
-- @param #number radius
-- @param #string name
local function SceneryScan(coordinate, radius, name)
if coordinate ~= nil then
local scenerylist = coordinate:ScanScenery(radius)
local rscenery = nil
for _,_scenery in pairs(scenerylist) do
local function SceneryScan(scoordinate, sradius, sname)
if scoordinate ~= nil then
local Vec2 = scoordinate:GetVec2()
local scanzone = ZONE_RADIUS:New("Zone-"..sname,Vec2,sradius,true)
scanzone:Scan({Object.Category.SCENERY})
local scanned = scanzone:GetScannedSceneryObjects()
local rscenery = nil -- Wrapper.Scenery#SCENERY
for _,_scenery in pairs(scanned) do
local scenery = _scenery -- Wrapper.Scenery#SCENERY
if tostring(scenery.SceneryName) == tostring(name) then
if tostring(scenery.SceneryName) == tostring(sname) then
rscenery = scenery
if Role then rscenery:SetProperty("ROLE",Role) end
break
end
end
@ -144,6 +181,7 @@ function SCENERY:FindByName(Name, Coordinate, Radius)
end
if Coordinate then
--BASE:I("Coordinate Scenery Scan")
scenery = SceneryScan(Coordinate, radius, name)
end
@ -154,7 +192,7 @@ end
-- to find the correct object.
--@param #SCENERY self
--@param #string Name The name or id of the scenery object as taken from the ME. Ex. '595785449'
--@param Core.Zone#ZONE Zone Where to find the scenery object. Can be handed as zone name.
--@param Core.Zone#ZONE_BASE Zone Where to find the scenery object. Can be handed as zone name.
--@param #number Radius (optional) Search radius around coordinate, defaults to 100
--@return #SCENERY Scenery Object or `nil` if it cannot be found
function SCENERY:FindByNameInZone(Name, Zone, Radius)
@ -164,7 +202,7 @@ function SCENERY:FindByNameInZone(Name, Zone, Radius)
Zone = ZONE:FindByName(Zone)
end
local coordinate = Zone:GetCoordinate()
return self:FindByName(Name,coordinate,Radius)
return self:FindByName(Name,coordinate,Radius,Zone:GetProperty("ROLE"))
end
--- Find a SCENERY object from its zone name. Since SCENERY isn't registered in the Moose database (just too many objects per map), we need to do a scan first
@ -173,38 +211,34 @@ end
--@param #string ZoneName The name of the scenery zone as created with a right-click on the map in the mission editor and select "assigned to...". Can be handed over as ZONE object.
--@return #SCENERY First found Scenery Object or `nil` if it cannot be found
function SCENERY:FindByZoneName( ZoneName )
local zone = ZoneName -- Core.Zone#ZONE
local zone = ZoneName -- Core.Zone#ZONE_BASE
if type(ZoneName) == "string" then
zone = ZONE:FindByName(ZoneName)
zone = ZONE:FindByName(ZoneName) -- Core.Zone#ZONE_POLYGON
end
local _id = zone:GetProperty('OBJECT ID')
BASE:T("Object ID ".._id)
--local properties = zone:GetAllProperties() or {}
--BASE:I(string.format("Object ID is: %s",_id or "none"))
--BASE:T("Object ID ".._id)
if not _id then
-- this zone has no object ID
BASE:E("**** Zone without object ID: "..ZoneName.." | Type: "..tostring(zone.ClassName))
if string.find(zone.ClassName,"POLYGON") then
zone:Scan({Object.Category.SCENERY})
local scanned = zone:GetScannedScenery()
local scanned = zone:GetScannedSceneryObjects()
for _,_scenery in (scanned) do
local scenery = _scenery -- Wrapper.Scenery#SCENERY
if scenery:IsAlive() then
local role = zone:GetProperty("ROLE")
if role then scenery:SetProperty("ROLE",role) end
return scenery
end
end
return nil
else
local coordinate = zone:GetCoordinate()
local scanned = coordinate:ScanScenery()
for _,_scenery in (scanned) do
local scenery = _scenery -- Wrapper.Scenery#SCENERY
if scenery:IsAlive() then
return scenery
end
end
return nil
return self:FindByName(_id, zone:GetCoordinate(),nil,zone:GetProperty("ROLE"))
end
else
return self:FindByName(_id, zone:GetCoordinate())
return self:FindByName(_id, zone:GetCoordinate(),nil,zone:GetProperty("ROLE"))
end
end
@ -219,6 +253,7 @@ function SCENERY:FindAllByZoneName( ZoneName )
zone = ZONE:FindByName(ZoneName)
end
local _id = zone:GetProperty('OBJECT ID')
--local properties = zone:GetAllProperties() or {}
if not _id then
-- this zone has no object ID
--BASE:E("**** Zone without object ID: "..ZoneName.." | Type: "..tostring(zone.ClassName))
@ -230,7 +265,7 @@ function SCENERY:FindAllByZoneName( ZoneName )
return nil
end
else
local obj = self:FindByName(_id, zone:GetCoordinate())
local obj = self:FindByName(_id, zone:GetCoordinate(),nil,zone:GetProperty("ROLE"))
if obj then
return {obj}
else