Merge remote-tracking branch 'origin/master' into develop

# Conflicts:
#	Moose Development/Moose/Ops/EasyA2G.lua
#	Moose Development/Moose/Ops/EasyGCICAP.lua
This commit is contained in:
Applevangelist
2025-12-21 15:40:46 +01:00
6 changed files with 632 additions and 37 deletions
+113 -7
View File
@@ -1067,13 +1067,111 @@ do
return self
end
--- Add a single reject zone to MANTIS.
-- @param #MANTIS self
-- @param Core.Zone#ZONE Zone The zone to be added
-- @return #MANTIS self
function MANTIS:AddRejectZone(Zone)
if Zone and Zone:IsInstanceOf("ZONE_BASE") then
table.insert(self.RejectZones,Zone)
self.usezones = true
end
return self
end
--- Add a single accept zone to MANTIS.
-- @param #MANTIS self
-- @param Core.Zone#ZONE Zone The zone to be added
-- @return #MANTIS self
function MANTIS:AddAcceptZone(Zone)
if Zone and Zone:IsInstanceOf("ZONE_BASE") then
table.insert(self.AcceptZones,Zone)
self.usezones = true
end
return self
end
--- Add a single conflict zone to MANTIS.
-- @param #MANTIS self
-- @param Core.Zone#ZONE Zone The zone to be added
-- @return #MANTIS self
function MANTIS:AddConflictZone(Zone)
if Zone and Zone:IsInstanceOf("ZONE_BASE") then
table.insert(self.ConflictZones,Zone)
self.usezones = true
end
return self
end
--- Function to set corridor zones.
-- @param #MANTIS self
-- @param Core.Set#SET_ZONE CorridorZones Can be handed in as SET\_ZONE or single ZONE object.
-- @return #MANTIS self
function MANTIS:SetCorridorZones(CorridorZones)
self:T(self.lid .. "SetCorridorZones")
if CorridorZones and CorridorZones:IsInstanceOf("SET_ZONE") then
self.corridorzones = CorridorZones
self.usecorridors = true
elseif CorridorZones and CorridorZones:IsInstanceOf("ZONE_BASE") then
if not self.corridorzones then self.corridorzones = SET_ZONE:New() end
self.corridorzones:AddZone(CorridorZones)
self.usecorridors = true
end
if self.intelset then
for _,_intel in pairs(self.intelset) do
_intel:SetCorridorZones(self.corridorzones)
end
end
return self
end
--- Function to add one corridor zone.
-- @param #MANTIS self
-- @param Core.Zone#ZONE CorridorZone The ZONE object to be added.
-- @return #MANTIS self
function MANTIS:AddCorridorZone(CorridorZone)
self:T(self.lid .. "AddCorridorZone")
self:SetCorridorZones(CorridorZone)
return self
end
--- Function to set corridor zone floor and ceiling in FEET.
-- @param #MANTIS self
-- @param #number Floor Floor altitude ASL in feet.
-- @param #number Ceiling Ceiling altitude ASL in feet.
-- @return #MANTIS self
function MANTIS:SetCorridorZoneFloorAndCeiling(Floor,Ceiling)
self.corridorfloor = UTILS.FeetToMeters(Floor)
self.corridorceiling = UTILS.FeetToMeters(Ceiling)
if self.intelset then
for _,_intel in pairs(self.intelset) do
_intel:SetCorridorLimits(self.corridorfloor,self.corridorceiling)
end
end
return self
end
--- Function to set corridor zone floor and ceiling in METERS.
-- @param #MANTIS self
-- @param #number Floor Floor altitude ASL in meters.
-- @param #number Ceiling Ceiling altitude ASL in meters.
-- @return #MANTIS self
function MANTIS:SetCorridorZoneFloorAndCeilingMeters(Floor,Ceiling)
self.corridorfloor = Floor
self.corridorceiling = Ceiling
if self.intelset then
for _,_intel in pairs(self.intelset) do
_intel:SetCorridorLimits(self.corridorfloor,self.corridorceiling)
end
end
return self
end
--- Function to set the detection radius of the EWR in meters. (Deprecated, SAM range is used)
-- @param #MANTIS self
-- @param #number radius Radius of the EWR detection zone
function MANTIS:SetEWRRange(radius)
self:T(self.lid .. "SetEWRRange")
--local radius = radius or 80000
-- self.acceptrange = radius
return self
end
@@ -1628,6 +1726,12 @@ do
IntelOne.DetectAccousticRadius = self.DetectAccousticRadius or 2000
IntelOne.DetectAccousticUnitTypes = self.DetectAccousticCategories or {Unit.Category.HELICOPTER}
--IntelOne:SetClusterAnalysis(true,true,true)
if self.usecorridors == true then
IntelOne:SetCorrdidorZones(self.corridorzones)
if self.corridorfloor or self.corridorceiling then
IntelOne:SetCooridorLimits(self.corridorfloor,self.corridorceiling)
end
end
IntelOne:Start()
local IntelTwo = INTEL:New(samset,self.coalition,self.name.." IntelTwo")
@@ -1635,6 +1739,12 @@ do
IntelTwo.DetectAccousticRadius = self.DetectAccousticRadius or 2000
IntelTwo.DetectAccousticUnitTypes = self.DetectAccousticCategories or {Unit.Category.HELICOPTER}
--IntelTwo:SetClusterAnalysis(true,true,true)
if self.usecorridors == true then
IntelTwo:SetCorrdidorZones(self.corridorzones)
if self.corridorfloor or self.corridorceiling then
IntelTwo:SetCooridorLimits(self.corridorfloor,self.corridorceiling)
end
end
IntelTwo:Start()
local CacheTime = self.DLinkCacheTime or 120
@@ -2227,11 +2337,7 @@ do
else
self.Detection = self:StartIntelDetection()
end
--[[
if self.advAwacs and not self.automode then
self.AWACS_Detection = self:StartAwacsDetection()
end
--]]
if self.autoshorad then
self.Shorad = SHORAD:New(self.name.."-SHORAD","SHORAD",self.SAM_Group,self.ShoradActDistance,self.ShoradTime,self.Coalition,self.UseEmOnOff,self.SmokeDecoy,self.SmokeDecoyColor)
self.Shorad:SetDefenseLimits(80,95)
+58 -1
View File
@@ -509,7 +509,7 @@ do
-- @field #AWACS
AWACS = {
ClassName = "AWACS", -- #string
version = "0.2.73", -- #string
version = "0.2.74", -- #string
lid = "", -- #string
coalition = coalition.side.BLUE, -- #number
coalitiontxt = "blue", -- #string
@@ -2060,6 +2060,55 @@ function AWACS:SetRejectionZone(Zone,Draw)
return self
end
--- Function to set corridor zones.
-- @param #AWACS self
-- @param Core.Set#SET_ZONE CorridorZones Can be handed in as SET\_ZONE or single ZONE object.
-- @return #AWACS self
function AWACS:SetCorridorZones(CorridorZones)
self:T(self.lid .. "SetCorridorZones")
if CorridorZones and CorridorZones:IsInstanceOf("SET_ZONE") then
self.corridorzones = CorridorZones
self.usecorridors = true
elseif CorridorZones and CorridorZones:IsInstanceOf("ZONE_BASE") then
if not self.corridorzones then self.corridorzones = SET_ZONE:New() end
self.corridorzones:AddZone(CorridorZones)
self.usecorridors = true
end
return self
end
--- Function to add one corridor zone.
-- @param #AWACS self
-- @param Core.Zone#ZONE CorridorZone The ZONE object to be added.
-- @return #AWACS self
function AWACS:AddCorridorZone(CorridorZone)
self:T(self.lid .. "AddCorridorZone")
self:SetCorridorZones(CorridorZone)
return self
end
--- Function to set corridor zone floor and ceiling in FEET.
-- @param #AWACS self
-- @param #number Floor Floor altitude ASL in feet.
-- @param #number Ceiling Ceiling altitude ASL in feet.
-- @return #AWACS self
function AWACS:SetCorridorZoneFloorAndCeiling(Floor,Ceiling)
self.corridorfloor = UTILS.FeetToMeters(Floor)
self.corridorceiling = UTILS.FeetToMeters(Ceiling)
return self
end
--- Function to set corridor zone floor and ceiling in METERS.
-- @param #AWACS self
-- @param #number Floor Floor altitude ASL in meters.
-- @param #number Ceiling Ceiling altitude ASL in meters.
-- @return #AWACS self
function AWACS:SetCorridorZoneFloorAndCeilingMeters(Floor,Ceiling)
self.corridorfloor = Floor
self.corridorceiling = Ceiling
return self
end
--- [User] Draw a line around the FEZ on the F10 map.
-- @param #AWACS self
-- @return #AWACS self
@@ -4336,6 +4385,14 @@ function AWACS:_StartIntel(awacs)
intel:SetFilterCategory({Unit.Category.AIRPLANE,Unit.Category.HELICOPTER})
end
-- Corridors
if self.usecorridors == true then
intel:SetCorridorZones(self.corridorzones)
if self.corridorceiling or self.corridorfloor then
intel:SetCorridorLimits(self.corridorfloor,self.corridorceiling)
end
end
-- Callbacks
local function NewCluster(Cluster)
self:__NewCluster(5,Cluster)
+9 -1
View File
@@ -293,7 +293,7 @@ EASYA2G = {
--- EASYA2G class version.
-- @field #string version
EASYA2G.version="0.0.2"
EASYA2G.version="0.1.3"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list
@@ -806,6 +806,14 @@ function EASYA2G:_StartIntel()
BlueIntel:SetRejectZones(self.NoGoZoneSet)
BlueIntel:SetConflictZones(self.ConflictZoneSet)
BlueIntel:SetVerbosity(0)
if self.usecorridors == true then
BlueIntel:SetCorridorZones(self.corridorzones)
if self.corridorfloor or self.corridorceiling then
BlueIntel:SetCorridorLimitsFeet(self.corridorfloor,self.corridorceiling)
end
end
BlueIntel:Start()
if self.debug then
+57 -1
View File
@@ -284,7 +284,7 @@ EASYGCICAP = {
--- EASYGCICAP class version.
-- @field #string version
EASYGCICAP.version="0.1.32"
EASYGCICAP.version="0.1.33"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--
@@ -1364,6 +1364,54 @@ function EASYGCICAP:AddConflictZone(Zone)
return self
end
--- Function to set corridor zones.
-- @param #EASYGCICAP self
-- @param Core.Set#SET_ZONE CorridorZones Can be handed in as SET\_ZONE or single ZONE object.
-- @return #EASYGCICAP self
function EASYGCICAP:SetCorridorZones(CorridorZones)
self:T(self.lid .. "SetCorridorZones")
if CorridorZones and CorridorZones:IsInstanceOf("SET_ZONE") then
self.corridorzones = CorridorZones
self.usecorridors = true
elseif CorridorZones and CorridorZones:IsInstanceOf("ZONE_BASE") then
if not self.corridorzones then self.corridorzones = SET_ZONE:New() end
self.corridorzones:AddZone(CorridorZones)
self.usecorridors = true
end
return self
end
--- Function to add one corridor zone.
-- @param #EASYGCICAP self
-- @param Core.Zone#ZONE CorridorZone The ZONE object to be added.
-- @return #EASYGCICAP self
function EASYGCICAP:AddCorridorZone(CorridorZone)
self:T(self.lid .. "AddCorridorZone")
self:SetCorridorZones(CorridorZone)
return self
end
--- Function to set corridor zone floor and ceiling in FEET.
-- @param #EASYGCICAP self
-- @param #number Floor Floor altitude ASL in feet.
-- @param #number Ceiling Ceiling altitude ASL in feet.
-- @return #EASYGCICAP self
function EASYGCICAP:SetCorridorZoneFloorAndCeiling(Floor,Ceiling)
self.corridorfloor = UTILS.FeetToMeters(Floor)
self.corridorceiling = UTILS.FeetToMeters(Ceiling)
return self
end
--- Function to set corridor zone floor and ceiling in METERS.
-- @param #EASYGCICAP self
-- @param #number Floor Floor altitude ASL in meters.
-- @param #number Ceiling Ceiling altitude ASL in meters.
-- @return #EASYGCICAP self
function EASYGCICAP:SetCorridorZoneFloorAndCeilingMeters(Floor,Ceiling)
self.corridorfloor = Floor
self.corridorceiling = Ceiling
return self
end
--- (Internal) Try to assign the intercept to a FlightGroup already in air and ready.
-- @param #EASYGCICAP self
@@ -1574,6 +1622,14 @@ function EASYGCICAP:_StartIntel()
BlueIntel:SetRejectZones(self.NoGoZoneSet)
BlueIntel:SetConflictZones(self.ConflictZoneSet)
BlueIntel:SetVerbosity(0)
if self.usecorridors == true then
BlueIntel:SetCorridorZones(self.corridorzones)
if self.corridorfloor or self.corridorceiling then
BlueIntel:SetCorridorLimitsFeet(self.corridorfloor,self.corridorceiling)
end
end
BlueIntel:Start()
if self.debug then
+87 -3
View File
@@ -27,6 +27,9 @@
-- @field Core.Set#SET_ZONE acceptzoneset Set of accept zones. If defined, only contacts in these zones are considered.
-- @field Core.Set#SET_ZONE rejectzoneset Set of reject zones. Contacts in these zones are not considered, even if they are in accept zones.
-- @field Core.Set#SET_ZONE conflictzoneset Set of conflict zones. Contacts in these zones are considered, even if they are not in accept zones or if they are in reject zones.
-- @field Core.Set#SET_ZONE corridorzoneset Set of corridor zones. Contacts in these zones are never considered. Also see corridorfloorheight and corridorfloorceiling.
-- @field #number corridorfloor [Air] Contacts below this height (ASL!) are considered, even if they are in a corridor zone.
-- @field #number corridorceiling [Air] Contacts above this height (ASL!) are considered, even if they are in a corridor zone.
-- @field #table Contacts Table of detected items.
-- @field #table ContactsLost Table of lost detected items.
-- @field #table ContactsUnknown Table of new detected items.
@@ -166,7 +169,7 @@ INTEL.Ctype={
--- INTEL class version.
-- @field #string version
INTEL.version="0.3.7"
INTEL.version="0.3.9"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- ToDo list
@@ -181,6 +184,7 @@ INTEL.version="0.3.7"
-- NOGO: SetAttributeZone --> return groups of generalized attributes in a zone.
-- DONE: Loose units only if they remain undetected for a given time interval. We want to avoid fast oscillation between detected/lost states. Maybe 1-5 min would be a good time interval?!
-- DONE: Combine units to groups for all, new and lost.
-- DONE: Add corridor zones.
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Constructor
@@ -272,6 +276,7 @@ function INTEL:New(DetectionSet, Coalition, Alias)
self:SetForgetTime()
self:SetAcceptZones()
self:SetRejectZones()
self:SetCorridorZones()
self:SetConflictZones()
------------------------
@@ -501,7 +506,63 @@ function INTEL:RemoveConflictZone(ConflictZone)
return self
end
--- **OBSOLETE, will be removed in next version!** Set forget contacts time interval.
--- Set corrdidor zones. Contacts detected in this/these zone(s) are never reported by the detection.
-- Note that corrdidor zones overrule all other zones, for exceptions see corridor floor and corridor ceiling heights.
-- @param #INTEL self
-- @param Core.Set#SET_ZONE CorridorZoneSet Set of corrdidor zone(s).
-- @return #INTEL self
function INTEL:SetCorridorZones(CorridorZoneSet)
self.corridorzoneset=CorridorZoneSet or SET_ZONE:New()
return self
end
--- Add a corrdidor zone. Contacts detected in this zone are corrdidored and not reported by the detection.
-- Note that corrdidor zones overrule all other zones, for exceptions see corridor floor and corridor ceiling heights.
-- @param #INTEL self
-- @param Core.Zone#ZONE CorridorZone Add a zone to the corrdidor zone set.
-- @return #INTEL self
function INTEL:AddCorridorZone(CorridorZone)
self.corridorzoneset:AddZone(CorridorZone)
return self
end
--- Remove a corrdidor zone from the corrdidor zone set.
-- Note that corrdidor zones overrule all other zones, for exceptions see corridor floor and corridor ceiling heights.
-- @param #INTEL self
-- @param Core.Zone#ZONE CorridorZone Remove a zone from the corrdidor zone set.
-- @return #INTEL self
function INTEL:RemoveCorridorZone(CorridorZone)
self.corridorzoneset:Remove(CorridorZone:GetName(), true)
return self
end
--- [Air] Add corrdidor zone floor and height. Are considered as ASL (above sea level or barometric) values.
-- Overrides corridor exception for objects flying outside this limitations.
-- @param #INTEL self
-- @param #number Floor Floor altitude in meters.
-- @param #number Ceiling Ceiling altitude in meters.
-- @return #INTEL self
function INTEL:SetCorridorLimits(Floor,Ceiling)
self.corridorceiling = Ceiling or 10000
self.corridorfloor = Floor or 1
return self
end
--- [Air] Add corrdidor zone floor and height. Are considered as ASL (above sea level or barometric) values.
-- Overrides corridor exception for objects flying outside this limitations.
-- @param #INTEL self
-- @param #number Floor Floor altitude in feet.
-- @param #number Ceiling Ceiling altitude in feet.
-- @return #INTEL self
function INTEL:SetCorridorLimitsFeet(Floor,Ceiling)
local Ceiling = Ceiling or 25000
local Floor = Floor or 15000
self.corridorceiling = UTILS.FeetToMeters(Ceiling)
self.corridorfloor = UTILS.FeetToMeters(Floor)
return self
end
--- **OBSOLETE, not functional!** Set forget contacts time interval.
-- Previously known contacts that are not detected any more, are "lost" after this time.
-- This avoids fast oscillations between a contact being detected and undetected.
-- @param #INTEL self
@@ -922,6 +983,29 @@ function INTEL:UpdateIntel()
table.insert(remove, unitname)
end
end
-- Check if unit is in any of the corridor zones.
if self.corridorzoneset:Count()>0 then
local inzone = false
for _,_zone in pairs(self.corridorzoneset.Set) do
local zone=_zone --Core.Zone#ZONE
if unit:IsInZone(zone) then
if unit:IsAir() and (self.corridorfloor ~= nil or self.corridorceiling ~= nil) then
local alt = unit:GetAltitude()
if self.corridorfloor and alt > self.corridorfloor then inzone = true end
if self.corridorceiling and alt < self.corridorceiling then inzone = true end
if inzone == true then break end
else
inzone=true
break
end
end
end
-- Unit is inside a corridor zone ==> remove!
if inzone then
table.insert(remove, unitname)
end
end
-- Filter unit categories. Added check that we have a UNIT and not a STATIC object because :GetUnitCategory() is only available for units.
if #self.filterCategory>0 and unit:IsInstanceOf("UNIT") then
@@ -2209,7 +2293,7 @@ function INTEL:GetClusterCoordinate(Cluster, Update)
return Cluster.coordinate
end
--- Check if the coorindate of the cluster changed.
--- Check if the coordindate of the cluster changed.
-- @param #INTEL self
-- @param #INTEL.Cluster Cluster The cluster.
-- @param #number Threshold in meters. Default 100 m.
+308 -24
View File
@@ -61,6 +61,7 @@ do
-- @field #number PreviousCount
-- @field #boolean CanSmoke
-- @field #boolean ShowThreatDetails
-- @field #boolean PersistMe
-- @extends Core.Fsm#FSM
@@ -98,11 +99,12 @@ PLAYERTASK = {
PreviousCount = 0,
CanSmoke = true,
ShowThreatDetails = true,
PersistMe = false,
}
--- PLAYERTASK class version.
-- @field #string version
PLAYERTASK.version="0.1.29"
PLAYERTASK.version="0.1.31"
--- Generic task condition.
-- @type PLAYERTASK.Condition
@@ -401,8 +403,17 @@ function PLAYERTASK:CanJoinTask(Group, Client)
return true
end
--- [User] Set this task for persistance, if persistance is enabled on the PLAYERTASKCONTROLLER instance.
-- @param #PLAYERTASK self
-- @return #PLAYERTASK self
function PLAYERTASK:EnablePersistance()
self.PersistMe = true
return self
end
--- [Internal] Add a PLAYERTASKCONTROLLER for this task
-- @param #PLAYERTASK self
--
-- @param Ops.PlayerTask#PLAYERTASKCONTROLLER Controller
-- @return #PLAYERTASK self
function PLAYERTASK:_SetController(Controller)
@@ -1088,7 +1099,6 @@ function PLAYERTASK:onafterStatus(From, Event, To)
return self
end
--- [Internal] On after progress call
-- @param #PLAYERTASK self
-- @param #string From
@@ -1363,6 +1373,12 @@ do
-- @field Core.ClientMenu#CLIENTMENU MenuNoTask
-- @field #boolean InformationMenu Show Radio Info Menu
-- @field #number TaskInfoDuration How long to show the briefing info on the screen
-- @field #table TaskPersistance Table for persistance data
-- @field #boolean TaskPersistanceSwitch Switch for persisting tasks
-- @field #string TaskPersistancePath File path for persisting tasks
-- @field #string TaskPersistanceFilename File name for persisting tasks
-- @field #table TasksPersistable List of persistable tasks
-- @field #number SceneryExplosivesAmount Kgs of TNT to explode scenery on task persistance loading
-- @extends Core.Fsm#FSM
---
@@ -1669,8 +1685,28 @@ do
--
-- Set a marker on the map and add the following text to create targets from it: "TARGET". This is effectively the same as adding a COORDINATE object as target.
-- The marker can be deleted any time.
--
-- ## 9 Discussion
--
-- ## 9 Single Task Persistence for mission designer added tasks
--
-- The class can persist the state of single tasks of type BOMBING, PRECISIONBOMBING, ARTY and SEAD, i.e. tasks which have a GROUND(!) GROUP, UNIT, STATIC or SCENERY as target.
-- This requires the task to have a unique(!) menu name set, a TARGET which already exists on the map at mission start(!), and a flag that this task is actually to be persisted.
-- Also, you need to desanitize the mission scripting environment, i.e. "lfs" and "io" must be available so we can write to disk.
--
-- -- First, we need to enable on the PLAYERTASKCONTROLLER itself
-- taskmanager:EnableTaskPersistance([[C:\Users\myname\Saved Games\DCS\Missions\MyMisionFolder\]],"Mission Tasks.csv") -- Path and Filename
--
-- -- Then, we can design a task marking mission progress that we want to persist
-- local RussianRadios = SET_STATIC:New():FilterPrefixes("Comms Tower Russia"):FilterOnce()
--
-- local RadioTask = PLAYERTASK:New(AUFTRAG.Type.BOMBING,RussianRadios,true,5,"Bombing")
-- RadioTask:SetMenuName("Neutralize Comms Towers") -- UNIQUE menu name so we can find the task later!
-- RadioTask:AddFreetext("Find and neutralize the two communication towers near NB70 East of Fulda on Streufelsberg!")
-- RadioTask:AddFreetextTTS("Find and neutralize the two communication towers naer N;B;7;zero; East of Fulda on Streufelsberg!")
-- RadioTask:EnablePersistance() -- Enable persistence for this task
--
-- taskmanager:AddPlayerTaskToQueue(RadioTask,true,false)
--
-- ## 10 Discussion
--
-- If you have questions or suggestions, please visit the [MOOSE Discord](https://discord.gg/AeYAkHP) #ops-playertask channel.
--
@@ -1720,6 +1756,12 @@ PLAYERTASKCONTROLLER = {
MenuNoTask = nil,
InformationMenu = false,
TaskInfoDuration = 30,
TaskPersistance = {},
TaskPersistanceSwitch = false,
TaskPersistancePath = nil,
TaskPersistanceFilename = nil,
TasksPersistable = {},
SceneryExplosivesAmount = 300,
}
---
@@ -1740,6 +1782,7 @@ AUFTRAG.Type.PRECISIONBOMBING = "Precision Bombing"
AUFTRAG.Type.CTLD = "Combat Transport"
AUFTRAG.Type.CSAR = "Combat Rescue"
AUFTRAG.Type.CONQUER = "Conquer"
---
-- @type Scores
PLAYERTASKCONTROLLER.Scores = {
@@ -1759,6 +1802,24 @@ PLAYERTASKCONTROLLER.Scores = {
[AUFTRAG.Type.CAP] = 100,
[AUFTRAG.Type.CAPTUREZONE] = 100,
}
---
-- @type TasksPersistable
PLAYERTASKCONTROLLER.TasksPersistable = {
[AUFTRAG.Type.PRECISIONBOMBING] = true,
[AUFTRAG.Type.BOMBING] = true,
[AUFTRAG.Type.ARTY] = true,
[AUFTRAG.Type.SEAD] = true,
}
---
-- @type PersistenceData
-- @field #number ID
-- @field #string Name
-- @field #string Type
-- @field #number InitialTargets
-- @field #number Targetsleft
-- @field #boolean updated
---
-- @type SeadAttributes
@@ -2174,6 +2235,30 @@ function PLAYERTASKCONTROLLER:New(Name, Coalition, Type, ClientFilter)
end
--- [User] Enable Task persistance (for specific gound target tasks)
-- @param #PLAYERTASKCONTROLLER self
-- @param #string Path Path where to save the task data
-- @param #string Filename File name under which to save the task data
-- @param #number KgsOfTNT (Optional) Explosives kgs used to remove scenery for persistence, defaults to 300
-- @return #PLAYERTASKCONTROLLER self
function PLAYERTASKCONTROLLER:EnableTaskPersistance(Path,Filename,KgsOfTNT)
self.TaskPersistanceSwitch = true
self.TaskPersistancePath = Path
self.TaskPersistanceFilename = Filename
self.SceneryExplosivesAmount = KgsOfTNT or 300
return self
end
--- [User] Disable Task persistance (for specific gound target tasks)
-- @param #PLAYERTASKCONTROLLER self
-- @return #PLAYERTASKCONTROLLER self
function PLAYERTASKCONTROLLER:DisableTaskPersistance()
self.TaskPersistanceSwitch = false
self.TaskPersistancePath = nil
self.TaskPersistanceFilename = nil
return self
end
--- [User] Set or create a SCORING object for this taskcontroller
-- @param #PLAYERTASKCONTROLLER self
-- @param Functional.Scoring#SCORING Scoring (optional) the Scoring object
@@ -2861,15 +2946,6 @@ function PLAYERTASKCONTROLLER:_GetTasksPerType()
end
end
--[[
for _type,_data in pairs(tasktypes) do
self:I("Task Type: ".._type)
for _id,_task in pairs(_data) do
self:I("Task Name: ".._task.Target:GetName())
end
end
--]]
return tasktypes
end
@@ -3566,9 +3642,9 @@ function PLAYERTASKCONTROLLER:AddPlayerTaskToQueue(PlayerTask,Silent,TaskFilter)
PlayerTask:_SetController(self)
PlayerTask:SetCoalition(self.Coalition)
self.TaskQueue:Push(PlayerTask)
if not Silent then
self:__TaskAdded(10,PlayerTask)
end
--if not Silent then
self:__TaskAdded(10,PlayerTask,Silent)
--end
else
self:E(self.lid.."***** NO valid PAYERTASK object sent!")
end
@@ -4584,6 +4660,76 @@ function PLAYERTASKCONTROLLER:RemoveConflictZone(ConflictZone)
return self
end
--- [User] Add an corridor zone to INTEL detection. You need to set up detection with @{#PLAYERTASKCONTROLLER.SetupIntel}() **before** using this.
-- @param #PLAYERTASKCONTROLLER self
-- @param Core.Zone#ZONE CorridorZone Add a zone to the corridor zone set.
-- @return #PLAYERTASKCONTROLLER self
function PLAYERTASKCONTROLLER:AddCorridorZone(CorridorZone)
self:T(self.lid.."AddCorridorZone")
if self.Intel then
self.Intel:AddCorridorZone(CorridorZone)
else
self:E(self.lid.."*****NO detection has been set up (yet)!")
end
return self
end
--- [User] Add an corridor SET_ZONE to INTEL detection. You need to set up detection with @{#PLAYERTASKCONTROLLER.SetupIntel}() **before** using this.
-- @param #PLAYERTASKCONTROLLER self
-- @param Core.Set#SET_ZONE CorridorZoneSet Add a SET_ZONE to the corridor zone set.
-- @return #PLAYERTASKCONTROLLER self
function PLAYERTASKCONTROLLER:AddCorridorZoneSet(CorridorZoneSet)
self:T(self.lid.."AddCorridorZoneSet")
if self.Intel then
self.Intel.corridorzoneset:AddSet(CorridorZoneSet)
else
self:E(self.lid.."*****NO detection has been set up (yet)!")
end
return self
end
--- [User] Remove an corridor zone from INTEL detection. You need to set up detection with @{#PLAYERTASKCONTROLLER.SetupIntel}() **before** using this.
-- @param #PLAYERTASKCONTROLLER self
-- @param Core.Zone#ZONE CorridorZone Remove this zone from the corridor zone set.
-- @return #PLAYERTASKCONTROLLER self
function PLAYERTASKCONTROLLER:RemoveCorridorZone(CorridorZone)
self:T(self.lid.."RemoveCorridorZone")
if self.Intel then
self.Intel:RemoveCorridorZone(CorridorZone)
else
self:E(self.lid.."*****NO detection has been set up (yet)!")
end
return self
end
--- Function to set corridor zone floor and ceiling in FEET.
-- @param #PLAYERTASKCONTROLLER self
-- @param #number Floor Floor altitude ASL in feet.
-- @param #number Ceiling Ceiling altitude ASL in feet.
-- @return #PLAYERTASKCONTROLLER self
function PLAYERTASKCONTROLLER:SetCorridorZoneFloorAndCeiling(Floor,Ceiling)
if self.Intel then
self.Intel:SetCorridorLimitsFeet(Floor,Ceiling)
else
self:E(self.lid.."*****NO detection has been set up (yet)!")
end
return self
end
--- Function to set corridor zone floor and ceiling in METERS.
-- @param #PLAYERTASKCONTROLLER self
-- @param #number Floor Floor altitude ASL in meters.
-- @param #number Ceiling Ceiling altitude ASL in meters.
-- @return #PLAYERTASKCONTROLLER self
function PLAYERTASKCONTROLLER:SetCorridorZoneFloorAndCeilingMeters(Floor,Ceiling)
if self.Intel then
self.Intel:SetCorridorLimits(Floor,Ceiling)
else
self:E(self.lid.."*****NO detection has been set up (yet)!")
end
return self
end
--- [User] Set the top menu name to a custom string.
-- @param #PLAYERTASKCONTROLLER self
-- @param #string Name The name to use as the top menu designation.
@@ -4765,6 +4911,128 @@ function PLAYERTASKCONTROLLER:SetSRSBroadcast(Frequency,Modulation)
return self
end
---
-- @param #PLAYERTASKCONTROLLER self
-- @param Ops.PlayerTask#PLASERTASK Task
-- @param #number TargetsLeft
function PLAYERTASKCONTROLLER:_UpdateTargetsAlive(Task,TargetsLeft)
self:T(self.lid.."_UpdateTargetsAlive")
local delta = Task.Target:CountTargets() - TargetsLeft
if delta > 0 then
self:T("Delta targets to be removed: "..delta)
local count = 0
local targets = Task.Target:GetObjects()
for _,_object in pairs(targets or {}) do
if _object and _object.ClassName and (_object:IsInstanceOf("GROUP") or _object:IsInstanceOf("UNIT") or _object:IsInstanceOf("STATIC") or _object:IsInstanceOf("SCENERY")) then
if count < delta then
count = count + 1
if not _object:IsInstanceOf("SCENERY") then
_object:Destroy(true)
else
_object:Explode(self.SceneryExplosivesAmount)
end
end
end
end
end
return self
end
---
-- @param #PLAYERTASKCONTROLLER self
function PLAYERTASKCONTROLLER:_LoadTasksPersisted()
self:T(self.lid.."_LoadTasksPersisted")
local function MatchTask(Type,Name)
local foundtask
self.TaskQueue:ForEach(
function(_task)
local task = _task -- #PLAYERTASK
if task.Type == Type and task.Target.name and task.Target.name == Name then
foundtask = task
end
end
)
return foundtask
end
if lfs and io then
local ok,data = UTILS.LoadFromFile(self.TaskPersistancePath,self.TaskPersistanceFilename)
if ok == true then
table.remove(data, 1)
for _,_entry in pairs(data) do
-- "--ID;;Name;;InitialTargets;;Targetsleft;;Type\n"
local dataset = UTILS.Split(_entry,";;")
local Taskdata = {} -- #PersistenceData
Taskdata.ID = tonumber(dataset[1])
Taskdata.Name = tostring(dataset[2])
Taskdata.InitialTargets = tonumber(dataset[3])
Taskdata.Targetsleft = tonumber(dataset[4])
Taskdata.Type = tostring(dataset[5])
Taskdata.Task = MatchTask(Taskdata.Type,Taskdata.Name)
if Taskdata.Task == nil then
self:E(self.lid.."No actual task found for "..Taskdata.Name)
else
self:T(self.lid.."Task loaded and match found for "..Taskdata.Name)
end
Taskdata.updated = Taskdata.InitialTargets == Taskdata.Targetsleft and true or false
if Taskdata.Task and Taskdata.updated == false then
self:_UpdateTargetsAlive(Taskdata.Task,Taskdata.Targetsleft)
Taskdata.updated = true
end
self.TaskPersistance[Taskdata.ID] = Taskdata
end
end
end
return self
end
---
-- @param #PLAYERTASKCONTROLLER self
function PLAYERTASKCONTROLLER:ClearPersistedData()
if lfs and io then
local text = "-- Data Cleared\n"
UTILS.SaveToFile(self.TaskPersistancePath,self.TaskPersistanceFilename,text)
end
return self
end
---
-- @param #PLAYERTASKCONTROLLER self
function PLAYERTASKCONTROLLER:_SaveTasksPersisted()
if lfs and io then
local text = "--ID;;Name;;InitialTargets;;Targetsleft;;Type\n"
for _,_data in pairs(self.TaskPersistance) do
local data = _data -- #PersistenceData
data.Targetsleft = data.Task.Target:CountTargets() -- recount
if data.Task and data.Task:IsDone() then data.Targetsleft = 0 end
local tasktext = string.format("%d;;%s;;%d;;%d;;%s\n",data.ID,data.Name,data.InitialTargets,data.Targetsleft,data.Type)
text = text..tasktext
end
UTILS.SaveToFile(self.TaskPersistancePath,self.TaskPersistanceFilename,text)
end
return self
end
---
-- @param #PLAYERTASKCONTROLLER self
-- @param #PLAYERTASK Task
function PLAYERTASKCONTROLLER:_AddPersistenceData(Task)
local Taskdata = {} -- #PersistenceData
if not self.TaskPersistance[Task.PlayerTaskNr] then
Taskdata.ID = Task.PlayerTaskNr
Taskdata.Name = Task.Target.name or "none"
Taskdata.InitialTargets = Task.Target:CountTargets()
Taskdata.Targetsleft = Taskdata.InitialTargets
Taskdata.Type = Task.Type
Taskdata.updated = true
Taskdata.Task = Task
self.TaskPersistance[Task.PlayerTaskNr] = Taskdata
end
return self
end
-------------------------------------------------------------------------------------------------------------------
-- FSM Functions PLAYERTASKCONTROLLER
-- TODO: FSM Functions PLAYERTASKCONTROLLER
@@ -4788,7 +5056,11 @@ function PLAYERTASKCONTROLLER:onafterStart(From, Event, To)
self:HandleEvent(EVENTS.PilotDead, self._EventHandler)
self:HandleEvent(EVENTS.PlayerEnterAircraft, self._EventHandler)
self:HandleEvent(EVENTS.UnitLost, self._EventHandler)
self:SetEventPriority(5)
self:SetEventPriority(5)
-- Persistence
if self.TaskPersistanceSwitch == true then
self:_LoadTasksPersisted()
end
return self
end
@@ -4828,6 +5100,11 @@ function PLAYERTASKCONTROLLER:onafterStatus(From, Event, To)
self:I(text)
end
-- Persistence
if self.TaskPersistanceSwitch == true then
self:_SaveTasksPersisted()
end
if self:GetState() ~= "Stopped" then
self:__Status(-30)
end
@@ -4983,19 +5260,26 @@ end
-- @param #string Event
-- @param #string To
-- @param Ops.PlayerTask#PLAYERTASK Task
-- @param #boolean Silent
-- @return #PLAYERTASKCONTROLLER self
function PLAYERTASKCONTROLLER:onafterTaskAdded(From, Event, To, Task)
function PLAYERTASKCONTROLLER:onafterTaskAdded(From, Event, To, Task, Silent)
self:T({From, Event, To})
self:T(self.lid.."TaskAdded")
local addtxt = self.gettext:GetEntry("TASKADDED",self.locale)
local taskname = string.format(addtxt, self.MenuName or self.Name, tostring(Task.Type))
if not self.NoScreenOutput then
self:_SendMessageToClients(taskname,15)
--local m = MESSAGE:New(taskname,15,"Tasking"):ToCoalition(self.Coalition)
if not Silent then
if not self.NoScreenOutput then
self:_SendMessageToClients(taskname,15)
--local m = MESSAGE:New(taskname,15,"Tasking"):ToCoalition(self.Coalition)
end
if self.UseSRS then
taskname = string.format(addtxt, self.MenuName or self.Name, tostring(Task.TTSType))
self.SRSQueue:NewTransmission(taskname,nil,self.SRS,nil,2)
end
end
if self.UseSRS then
taskname = string.format(addtxt, self.MenuName or self.Name, tostring(Task.TTSType))
self.SRSQueue:NewTransmission(taskname,nil,self.SRS,nil,2)
self:T(self.lid..string.format("Pers = %s | Type = %s | TypePers = %s | TaskFlag = %s",tostring(self.TaskPersistanceSwitch),tostring(Task.Type),tostring(self.TasksPersistable[Task.Type]),tostring(Task.PersistMe)))
if self.TaskPersistanceSwitch == true and self.TasksPersistable[Task.Type] == true and Task.PersistMe == true then
self:_AddPersistenceData(Task)
end
return self
end