Compare commits

...

9 Commits

Author SHA1 Message Date
Frank 92bc0389d3 Asset count 2026-08-04 18:21:53 +02:00
Frank 7a5f6ad3c6 Merge branch 'master-ng' into FF/Ops 2026-08-01 16:30:05 +02:00
Frank 2a73d8d34a Add TERRITORY class and various fixes
Add a new passive TERRITORY functional module (Functional/Territory.lua) and register it in Modules.lua. TERRITORY provides zone-backed territory objects and DB helpers (Find/Add/Delete).

Update AUFTRAG: bump version to 1.5.0, introduce AUFTRAG.Summary, add an "Evaluated" event/transition, populate and emit mission summary from Evaluate(), set a dTevaluate for BAI, replace Stop() with __Stop(-120) on terminal states, and clear summary on repeats/stops. Minor ARTY debug prints and make Speed param optional.

INTEL: add _UpdateAgents and SetAgentAuto to optionally auto-refresh the detection set and invoke it during status updates.

OPSGROUP: require StayInZoneTime>0 before treating task as stayed.

AIRBASE: determine DCS object category and name, handle ship/oil-rig/helipad edge cases (add static helipad to DB), keep Zell detection, and create parkingCircle with explicit parameter. Coherent small refactors and safety checks across modules.
2026-08-01 16:29:24 +02:00
Frank e8f5214e0b Merge branch 'master-ng' into FF/Ops 2026-06-21 18:21:33 +02:00
Frank e4c7e809cb TARGET v0.8.0
- Added Element functions
- Improved handling of delayed dead events
2026-06-18 22:47:11 +02:00
Frank aab16dd966 Legion/Cohort Database
- moved to individual classes
2026-06-17 09:00:17 +02:00
Frank 8b356f0913 Merge branch 'master-ng' into FF/Ops 2026-06-15 20:48:57 +02:00
Frank c3086b0265 Update Database.lua
- fixed bug
2026-06-05 22:54:42 +02:00
Frank 599a2ba6fb Database
- Added COHORTs and LEGIONs to database
2026-06-05 22:50:52 +02:00
17 changed files with 667 additions and 105 deletions
+36
View File
@@ -92,6 +92,8 @@ DATABASE = {
ZONES_GOAL = {}, ZONES_GOAL = {},
WAREHOUSES = {}, WAREHOUSES = {},
FLIGHTGROUPS = {}, FLIGHTGROUPS = {},
COHORTS={},
LEGIONS={},
FLIGHTCONTROLS = {}, FLIGHTCONTROLS = {},
OPSZONES = {}, OPSZONES = {},
PATHLINES = {}, PATHLINES = {},
@@ -2087,6 +2089,40 @@ function DATABASE:FindOpsGroupFromUnit(unitname)
end end
end end
--
--- Add an OPS COHORT (SQUADRON, PLATOON, FLOTILLA) to the data base.
-- @param #DATABASE self
-- @param Ops.Cohort#COHORT cohort The cohort added to the DB.
function DATABASE:AddCohort(cohort)
self.COHORTS[cohort.name]=cohort
end
--- Find an OPS COHORT (SQUADRON, PLATOON, FLOTILLA) in the data base.
-- @param #DATABASE self
-- @param #string cohortname Name of the cohort.
-- @return Ops.Cohort#COHORT Cohort object.
function DATABASE:FindCohort(cohortname)
return self.COHORTS[cohortname]
end
--- Add an OPS LEGION (AIRWING, BRIGADE, FLEET) to the data base.
-- @param #DATABASE self
-- @param Ops.Legion#LEGION legion The legion added to the DB.
function DATABASE:AddLegion(legion)
self.LEGIONS[legion.alias]=legion
end
--- Find an OPS LEGION (AIRWING, BRIGADE, FLEET) in the data base.
-- @param #DATABASE self
-- @param #string legionname Name of the legion.
-- @return Ops.Legion#LEGION Legion object.
function DATABASE:FindLegion(legionname)
return self.LEGIONS[legionname]
end
--
--- Add a flight control to the data base. --- Add a flight control to the data base.
-- @param #DATABASE self -- @param #DATABASE self
-- @param OPS.FlightControl#FLIGHTCONTROL flightcontrol -- @param OPS.FlightControl#FLIGHTCONTROL flightcontrol
@@ -0,0 +1,292 @@
--- **Ops** - Passive strategic territory.
--
-- **Main Features:**
--
-- * Associate a static MOOSE zone with a coalition
-- * Keep strategic territory geometry separate from OPSZONE scanning
-- * Register territories in the MOOSE database
-- * Delegate coordinate checks and F10 drawing to the underlying zone
--
-- A TERRITORY is deliberately passive. It does not scan DCS objects, evaluate
-- ownership, run a scheduler, or make tactical or strategic decisions.
--
-- ===
--
-- @module Ops.Territory
--- TERRITORY class.
-- @type TERRITORY
-- @field #string ClassName Name of the class.
-- @field #string version Class version.
-- @field #number verbose Verbosity level.
-- @field #string lid Log ID string.
-- @field #string name Unique territory name.
-- @field #string zoneName Name of the underlying MOOSE zone.
-- @field Core.Zone#ZONE_BASE zone Underlying MOOSE zone.
-- @field #number coalition Coalition associated with the territory.
-- @extends Core.Base#BASE
--- A passive strategic area defined by an existing MOOSE zone.
--
-- TERRITORY contains geometry and declarative ownership only. Unlike
-- @{Ops.OpsZone#OPSZONE}, it performs no periodic object scans and has no FSM.
--
-- @field #TERRITORY
TERRITORY = {
ClassName = "TERRITORY",
verbose = 0,
}
--- TERRITORY class version.
-- @field #string version
TERRITORY.version = "0.1.0"
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- DATABASE extension
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Finds a TERRITORY based on its name.
-- @param Core.Database#DATABASE self
-- @param #string TerritoryName Name of the territory.
-- @return #TERRITORY The found territory or `nil`.
function DATABASE:FindTerritory(TerritoryName)
local territories = self.TERRITORIES or {}
return territories[TerritoryName]
end
--- Adds a TERRITORY to the database.
-- @param Core.Database#DATABASE self
-- @param #TERRITORY Territory Territory to add.
-- @return #TERRITORY The registered territory or `nil`.
function DATABASE:AddTerritory(Territory)
if not Territory then
return nil
end
self.TERRITORIES = self.TERRITORIES or {}
local territoryName = Territory:GetName()
if not self.TERRITORIES[territoryName] then
self.TERRITORIES[territoryName] = Territory
end
return self.TERRITORIES[territoryName]
end
--- Deletes a TERRITORY from the database.
-- @param Core.Database#DATABASE self
-- @param #string TerritoryName Name of the territory.
-- @return Core.Database#DATABASE self
function DATABASE:DeleteTerritory(TerritoryName)
if self.TERRITORIES then
self.TERRITORIES[TerritoryName] = nil
end
return self
end
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Constructor
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Create a new TERRITORY class object.
-- @param #TERRITORY self
-- @param Core.Zone#ZONE_BASE Zone The underlying MOOSE zone or its Mission Editor name.
-- @param #number Coalition (Optional) Associated coalition. Default `coalition.side.NEUTRAL`.
-- @param #string Name (Optional) Unique territory name. Default is the zone name.
-- @return #TERRITORY self
-- @usage
-- local north = TERRITORY:New("Territory North", coalition.side.BLUE)
-- local southZone = ZONE:FindByName("Territory South")
-- local south = TERRITORY:New(southZone, coalition.side.RED, "Southern Territory")
function TERRITORY:New(Zone, Coalition, Name)
-- Inherit everything from BASE class.
local self = BASE:Inherit(self, BASE:New()) -- #TERRITORY
-- Resolve a Mission Editor zone name.
if type(Zone) == "string" then
local zoneName = Zone
Zone = ZONE:FindByName(zoneName)
if not Zone then
self:E(string.format("ERROR: No ZONE found for name: %s", tostring(zoneName)))
return nil
end
elseif not Zone then
self:E("ERROR: First parameter Zone is nil in TERRITORY:New(Zone) call!")
return nil
end
-- A territory relies only on the common ZONE_BASE interface.
if type(Zone.GetName) ~= "function"
or type(Zone.GetCoordinate) ~= "function"
or type(Zone.IsCoordinateInZone) ~= "function" then
self:E("ERROR: TERRITORY requires a ZONE_BASE derived object!")
return nil
end
local zoneName = Zone:GetName()
local territoryName = Name or zoneName
if type(territoryName) ~= "string" or territoryName == "" then
self:E("ERROR: TERRITORY requires a non-empty name!")
return nil
end
self.zone = Zone
self.zoneName = zoneName
self.name = territoryName
self.lid = string.format("TERRITORY %s | ", territoryName)
if not self:SetCoalition(Coalition or coalition.side.NEUTRAL) then
return nil
end
-- Register in the MOOSE database.
_DATABASE:AddTerritory(self)
return self
end
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Set functions
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Set the coalition associated with this territory.
-- This method only changes declarative ownership. It does not evaluate DCS
-- units or trigger any capture logic.
-- @param #TERRITORY self
-- @param #number Coalition Coalition side number.
-- @return #TERRITORY self or `nil` if the coalition is invalid.
function TERRITORY:SetCoalition(Coalition)
if Coalition ~= coalition.side.NEUTRAL
and Coalition ~= coalition.side.RED
and Coalition ~= coalition.side.BLUE then
self:E(self.lid .. string.format("ERROR: Invalid coalition: %s", tostring(Coalition)))
return nil
end
self.coalition = Coalition
return self
end
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Get functions
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Find a TERRITORY by name.
-- @param #TERRITORY self
-- @param #string Name Name of the territory.
-- @return #TERRITORY The found territory or `nil`.
function TERRITORY:FindByName(Name)
return _DATABASE:FindTerritory(Name)
end
--- Get the territory name.
-- @param #TERRITORY self
-- @return #string Territory name.
function TERRITORY:GetName()
return self.name
end
--- Get the underlying zone name.
-- @param #TERRITORY self
-- @return #string Zone name.
function TERRITORY:GetZoneName()
return self.zoneName
end
--- Get the underlying MOOSE zone.
-- @param #TERRITORY self
-- @return Core.Zone#ZONE_BASE The underlying zone.
function TERRITORY:GetZone()
return self.zone
end
--- Get the territory center coordinate.
-- @param #TERRITORY self
-- @return Core.Point#COORDINATE Territory center coordinate.
function TERRITORY:GetCoordinate()
return self.zone:GetCoordinate()
end
--- Get the coalition associated with the territory.
-- @param #TERRITORY self
-- @return #number Coalition side number.
function TERRITORY:GetCoalition()
return self.coalition
end
--- Get the name of the coalition associated with the territory.
-- @param #TERRITORY self
-- @return #string Coalition name.
function TERRITORY:GetCoalitionName()
return UTILS.GetCoalitionName(self.coalition)
end
--- Get the owner of the territory.
-- This is an alias for @{#TERRITORY.GetCoalition}.
-- @param #TERRITORY self
-- @return #number Coalition side number.
function TERRITORY:GetOwner()
return self:GetCoalition()
end
--- Get the owner coalition name.
-- This is an alias for @{#TERRITORY.GetCoalitionName}.
-- @param #TERRITORY self
-- @return #string Coalition name.
function TERRITORY:GetOwnerName()
return self:GetCoalitionName()
end
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Zone functions
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Test whether a coordinate lies inside the territory.
-- @param #TERRITORY self
-- @param Core.Point#COORDINATE Coordinate Coordinate to test.
-- @return #boolean `true` if the coordinate lies inside the territory.
function TERRITORY:ContainsCoordinate(Coordinate)
return self.zone:IsCoordinateInZone(Coordinate)
end
--- Test whether a Vec2 lies inside the territory.
-- @param #TERRITORY self
-- @param DCS#Vec2 Vec2 Vec2 to test.
-- @return #boolean `true` if the Vec2 lies inside the territory.
function TERRITORY:ContainsVec2(Vec2)
return self.zone:IsVec2InZone(Vec2)
end
--- Draw the territory on the F10 map.
-- Drawing is delegated to the underlying MOOSE zone.
-- @param #TERRITORY self
-- @param #number Coalition (Optional) Coalition visibility. Default `-1` for all.
-- @param #table Color (Optional) RGB line color.
-- @param #number Alpha (Optional) Line alpha.
-- @param #table FillColor (Optional) RGB fill color.
-- @param #number FillAlpha (Optional) Fill alpha.
-- @param #number LineType (Optional) DCS line type.
-- @return #TERRITORY self
function TERRITORY:Draw(Coalition, Color, Alpha, FillColor, FillAlpha, LineType)
self.zone:DrawZone(Coalition, Color, Alpha, FillColor, FillAlpha, LineType)
return self
end
--- Remove the territory drawing from the F10 map.
-- @param #TERRITORY self
-- @return #TERRITORY self
function TERRITORY:Undraw()
self.zone:UndrawZone()
return self
end
--- Remove the territory from the MOOSE database.
-- The underlying zone is not deleted.
-- @param #TERRITORY self
-- @return #TERRITORY self
function TERRITORY:Remove()
_DATABASE:DeleteTerritory(self.name)
return self
end
+1
View File
@@ -79,6 +79,7 @@ __Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Functional/Tiresias.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Functional/Stratego.lua' ) __Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Functional/Stratego.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Functional/ClientWatch.lua') __Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Functional/ClientWatch.lua')
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Functional/Formation.lua') __Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Functional/Formation.lua')
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Functional/Territory.lua')
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Ops/Airboss.lua' ) __Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Ops/Airboss.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Ops/RecoveryTanker.lua' ) __Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Ops/RecoveryTanker.lua' )
+45 -28
View File
@@ -290,6 +290,9 @@ function AIRWING:New(warehousename, airwingname)
-- @param Ops.FlightGroup#FLIGHTGROUP FlightGroup The FLIGHTGROUP on mission. -- @param Ops.FlightGroup#FLIGHTGROUP FlightGroup The FLIGHTGROUP on mission.
-- @param Ops.Auftrag#AUFTRAG Mission The mission. -- @param Ops.Auftrag#AUFTRAG Mission The mission.
-- Add legion to DB
_DATABASE:AddLegion(self)
return self return self
end end
@@ -541,6 +544,47 @@ function AIRWING:AddPayloadCapability(Payload, MissionTypes, Performance)
return self return self
end end
--- Filter available payloads for a given aircraft type and mission type.
-- @param #AIRWING self
-- @param #string UnitType The type of the unit.
-- @param #string MissionType The mission type.
-- @param #table Payloads Specific payloads only to be considered.
-- @return #AIRWING.Payload Payload table or *nil*.
function AIRWING:_FilterPlayloads(UnitType, MissionType, Payloads)
local function _checkPayloads(payload)
if Payloads then
for _,Payload in pairs(Payloads) do
if Payload.uid==payload.uid then
return true
end
end
else
-- Payload was not specified.
return nil
end
return false
end
-- Pre-selection: filter out only those payloads that are valid for the airframe and mission type and are available.
local payloads={}
for _,_payload in pairs(self.payloads) do
local payload=_payload --#AIRWING.Payload
local specialpayload=_checkPayloads(payload)
local compatible=AUFTRAG.CheckMissionCapability(MissionType, payload.capabilities)
local goforit = specialpayload or (specialpayload==nil and compatible)
if payload.aircrafttype==UnitType and payload.navail>0 and goforit then
table.insert(payloads, payload)
end
end
return payloads
end
--- Fetch a payload from the airwing resources for a given unit and mission type. --- Fetch a payload from the airwing resources for a given unit and mission type.
-- The payload with the highest priority is preferred. -- The payload with the highest priority is preferred.
-- @param #AIRWING self -- @param #AIRWING self
@@ -589,34 +633,7 @@ function AIRWING:FetchPayloadFromStock(UnitType, MissionType, Payloads)
end end
end end
local function _checkPayloads(payload) local payloads=self:_FilterPlayloads(UnitType, MissionType, Payloads)
if Payloads then
for _,Payload in pairs(Payloads) do
if Payload.uid==payload.uid then
return true
end
end
else
-- Payload was not specified.
return nil
end
return false
end
-- Pre-selection: filter out only those payloads that are valid for the airframe and mission type and are available.
local payloads={}
for _,_payload in pairs(self.payloads) do
local payload=_payload --#AIRWING.Payload
local specialpayload=_checkPayloads(payload)
local compatible=AUFTRAG.CheckMissionCapability(MissionType, payload.capabilities)
local goforit = specialpayload or (specialpayload==nil and compatible)
if payload.aircrafttype==UnitType and payload.navail>0 and goforit then
table.insert(payloads, payload)
end
end
-- Debug. -- Debug.
if self.verbose>=4 then if self.verbose>=4 then
+62 -5
View File
@@ -192,6 +192,8 @@
-- @field #boolean optionInvisible Invisible is on/off. -- @field #boolean optionInvisible Invisible is on/off.
-- @field #boolean optionImmortal Immortal is on/off. -- @field #boolean optionImmortal Immortal is on/off.
-- --
-- @field #AUFTRAG.Summary summary Auftrag summary.
--
-- @extends Core.Fsm#FSM -- @extends Core.Fsm#FSM
--- *A warrior's mission is to foster the success of others.* -- Morihei Ueshiba --- *A warrior's mission is to foster the success of others.* -- Morihei Ueshiba
@@ -656,6 +658,19 @@ AUFTRAG.Category={
-- @field #string DAMAGED Target was damaged. -- @field #string DAMAGED Target was damaged.
-- @field #string DESTROYED Target was destroyed. -- @field #string DESTROYED Target was destroyed.
--- Mission summary.
-- @type AUFTRAG.Summary
-- @field #boolean success If true, mission was successful.
-- @field #number Ntargets0 Number of initial targets.
-- @field #number Ntargets Number of final targets after mission is done.
-- @field #number damage Target damage in per cent.
-- @field #number Ndestroyed Number of destroyed targets.
-- @field #number Nkills Number of kills from assigned groups.
-- @field #number Nelements Number of elements assigned to mission.
-- @field #number targetLife Target life points after mission is over.
-- @field #number category Target category.
-- @field #number Ncasualties Number of own casualties.
--- Generic mission condition. --- Generic mission condition.
-- @type AUFTRAG.Condition -- @type AUFTRAG.Condition
-- @field #function func Callback function to check for a condition. Should return a #boolean. -- @field #function func Callback function to check for a condition. Should return a #boolean.
@@ -676,7 +691,7 @@ AUFTRAG.Category={
--- AUFTRAG class version. --- AUFTRAG class version.
-- @field #string version -- @field #string version
AUFTRAG.version="1.4.2" AUFTRAG.version="1.5.0"
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list -- TODO list
@@ -776,6 +791,7 @@ function AUFTRAG:New(Type)
self:AddTransition("*", "Cancel", AUFTRAG.Status.CANCELLED) -- Command to cancel the mission. self:AddTransition("*", "Cancel", AUFTRAG.Status.CANCELLED) -- Command to cancel the mission.
self:AddTransition("*", "Evaluated", "*")
self:AddTransition("*", "Success", AUFTRAG.Status.SUCCESS) self:AddTransition("*", "Success", AUFTRAG.Status.SUCCESS)
self:AddTransition("*", "Failed", AUFTRAG.Status.FAILED) self:AddTransition("*", "Failed", AUFTRAG.Status.FAILED)
@@ -946,6 +962,24 @@ function AUFTRAG:New(Type)
-- @param #string Event Event. -- @param #string Event Event.
-- @param #string To To state. -- @param #string To To state.
--- Triggers the FSM event "Evaluated".
-- @function [parent=#AUFTRAG] Evaluated
-- @param #AUFTRAG self
-- @param #AUFTRAG.Summary
--- Triggers the FSM event "Evaluated" after a delay.
-- @function [parent=#AUFTRAG] __Evaluated
-- @param #AUFTRAG self
-- @param #number delay Delay in seconds.
-- @param #AUFTRAG.Summary Summary Mission summary.
--- On after "Evaluated" event.
-- @function [parent=#AUFTRAG] OnAfterEvaluated
-- @param #AUFTRAG self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param #AUFTRAG.Summary Summary Mission summary.
--- Triggers the FSM event "Success". --- Triggers the FSM event "Success".
-- @function [parent=#AUFTRAG] Success -- @function [parent=#AUFTRAG] Success
@@ -1751,6 +1785,9 @@ function AUFTRAG:NewBAI(Target, Altitude)
mission.optionROE=ENUMS.ROE.OpenFire mission.optionROE=ENUMS.ROE.OpenFire
mission.optionROT=ENUMS.ROT.PassiveDefense mission.optionROT=ENUMS.ROT.PassiveDefense
-- Evaluate result after 5 min. We might need time until the bombs have dropped and targets have been detroyed.
mission.dTevaluate=5*60
mission.categories={AUFTRAG.Category.AIRCRAFT} mission.categories={AUFTRAG.Category.AIRCRAFT}
mission.DCStask=mission:GetDCSMissionTask() mission.DCStask=mission:GetDCSMissionTask()
@@ -2304,6 +2341,9 @@ function AUFTRAG:NewARTY(Target, Nshots, Radius, Altitude)
local mission=AUFTRAG:New(AUFTRAG.Type.ARTY) local mission=AUFTRAG:New(AUFTRAG.Type.ARTY)
printf("FF nshots=%s", tostring(Nshots))
printf("FF radius=%s", tostring(Radius))
mission:_TargetFromObject(Target) mission:_TargetFromObject(Target)
mission.artyShots=Nshots or nil mission.artyShots=Nshots or nil
@@ -2407,7 +2447,7 @@ end
-- @param #AUFTRAG self -- @param #AUFTRAG self
-- @param Ops.OpsZone#OPSZONE OpsZone The OPS zone to capture. -- @param Ops.OpsZone#OPSZONE OpsZone The OPS zone to capture.
-- @param #number Coalition The coalition which should capture the zone for the mission to be successful. -- @param #number Coalition The coalition which should capture the zone for the mission to be successful.
-- @param #number Speed Speed in knots. -- @param #number Speed (Optional) Speed in knots.
-- @param #number Altitude (Optional) Altitude in feet. Only for airborne units. Default 2000 feet ASL. -- @param #number Altitude (Optional) Altitude in feet. Only for airborne units. Default 2000 feet ASL.
-- @param #string Formation (Optional) Formation used by ground units during patrol. Default "Off Road". -- @param #string Formation (Optional) Formation used by ground units during patrol. Default "Off Road".
-- @param #number StayInZoneTime Stay this many seconds in the zone when done, only then drive back. -- @param #number StayInZoneTime Stay this many seconds in the zone when done, only then drive back.
@@ -4690,6 +4730,18 @@ function AUFTRAG:Evaluate()
failed=false failed=false
end end
self.summary={} --#AUFTRAG.Summary
self.summary.success=not failed
self.summary.damage=targetdamage
self.summary.Ntargets=Ntargets
self.summary.Ntargets0=Ntargets0
self.summary.Ncasualties=self.Ncasualties
self.summary.Ndestroyed=self.engageTarget.Ndestroyed
self.summary.Nelements=self.Nelements
self.summary.Nkills=self.Nkills
self.summary.category=self.engageTarget:GetCategory()
self.summary.targetLife=Life
-- Debug text. -- Debug text.
if self.verbose > 0 then if self.verbose > 0 then
local text=string.format("Evaluating mission:\n") local text=string.format("Evaluating mission:\n")
@@ -4709,6 +4761,9 @@ function AUFTRAG:Evaluate()
self:I(self.lid..text) self:I(self.lid..text)
end end
-- Trigger evaluated result and pass summary.
self:Evaluated(self.summary)
-- Trigger events. -- Trigger events.
if failed then if failed then
self:I(self.lid..string.format("Mission %d [%s] failed!", self.auftragsnummer, self.type)) self:I(self.lid..string.format("Mission %d [%s] failed!", self.auftragsnummer, self.type))
@@ -5120,7 +5175,6 @@ end
-- FSM Functions -- FSM Functions
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- On after "Planned" event. --- On after "Planned" event.
-- @param #AUFTRAG self -- @param #AUFTRAG self
-- @param #string From From state. -- @param #string From From state.
@@ -5430,7 +5484,7 @@ function AUFTRAG:onafterSuccess(From, Event, To)
-- Stop mission. -- Stop mission.
self:T(self.lid..string.format("Mission SUCCESS! Number of max repeats %d reached ==> Stopping mission!", self.repeated+1)) self:T(self.lid..string.format("Mission SUCCESS! Number of max repeats %d reached ==> Stopping mission!", self.repeated+1))
self:Stop() self:__Stop(-120)
end end
@@ -5472,7 +5526,7 @@ function AUFTRAG:onafterFailed(From, Event, To)
-- Stop mission. -- Stop mission.
self:T(self.lid..string.format("Mission FAILED! Number of max repeats %d reached ==> Stopping mission!", self.repeated+1)) self:T(self.lid..string.format("Mission FAILED! Number of max repeats %d reached ==> Stopping mission!", self.repeated+1))
self:Stop() self:__Stop(-120)
end end
@@ -5585,6 +5639,7 @@ function AUFTRAG:onafterRepeat(From, Event, To)
self.Ngroups=0 self.Ngroups=0
self.Nassigned=nil self.Nassigned=nil
self.Ndead=0 self.Ndead=0
self.summary=nil
-- Update DCS mission task. Could be that the initial task (e.g. for bombing) was destroyed. Then we need to update the coordinate. -- Update DCS mission task. Could be that the initial task (e.g. for bombing) was destroyed. Then we need to update the coordinate.
self.DCStask=self:GetDCSMissionTask() self.DCStask=self:GetDCSMissionTask()
@@ -5636,6 +5691,8 @@ function AUFTRAG:onafterStop(From, Event, To)
-- No group data. -- No group data.
self.groupdata={} self.groupdata={}
self.summary=nil
-- Clear pending scheduler calls. -- Clear pending scheduler calls.
self.CallScheduler:Clear() self.CallScheduler:Clear()
+3
View File
@@ -150,6 +150,9 @@ function BRIGADE:New(WarehouseName, BrigadeName)
-- @param Ops.ArmyGroup#ARMYGROUP ArmyGroup The ARMYGROUP on mission. -- @param Ops.ArmyGroup#ARMYGROUP ArmyGroup The ARMYGROUP on mission.
-- @param Ops.Auftrag#AUFTRAG Mission The mission. -- @param Ops.Auftrag#AUFTRAG Mission The mission.
-- Add legion to DB
_DATABASE:AddLegion(self)
return self return self
end end
+25
View File
@@ -1102,6 +1102,31 @@ function COHORT:CountAssets(InStock, MissionTypes, Attributes)
return N return N
end end
--- Count available assets in legion warehouse stock. Spawned, requested and reserved assets are not counted.
-- @param #COHORT self
-- @param #table MissionTypes (Optional) Count only assest that can perform certain mission type(s). Default is all types.
-- @param #table Attributes (Optional) Count only assest that have a certain attribute(s), e.g. `WAREHOUSE.Attribute.AIR_BOMBER`.
-- @return #number Number of assets.
function COHORT:CountAvailableAssets(MissionTypes, Attributes)
local N=0
for _,_asset in pairs(self.assets) do
local asset=_asset --Functional.Warehouse#WAREHOUSE.Assetitem
if not (asset.spawned or asset.requested or asset.isReserved) then
if MissionTypes==nil or AUFTRAG.CheckMissionCapability(MissionTypes, self.missiontypes) then
if Attributes==nil or self:CheckAttribute(Attributes) then
N=N+1 --This is in stock.
end
end
end
end
return N
end
--- Get OPSGROUPs. --- Get OPSGROUPs.
-- @param #COHORT self -- @param #COHORT self
-- @param #table MissionTypes (Optional) Count only assest that can perform certain mission type(s). Default is all types. -- @param #table MissionTypes (Optional) Count only assest that can perform certain mission type(s). Default is all types.
+16
View File
@@ -2112,6 +2112,22 @@ function COMMANDER:CountAssets(InStock, MissionTypes, Attributes)
return N return N
end end
--- Count available assets of all assigned legions, which are in stock. Spawned and reserved assets are not counted.
-- @param #COMMANDER self
-- @param #table MissionTypes (Optional) Count only assest that can perform certain mission type(s). Default is all types.
-- @param #table Attributes (Optional) Count only assest that have a certain attribute(s), e.g. `WAREHOUSE.Attribute.AIR_BOMBER`.
-- @return #number Amount of asset groups.
function COMMANDER:CountAvailableAssets(MissionTypes, Attributes)
local N=0
for _,_legion in pairs(self.legions) do
local legion=_legion --Ops.Legion#LEGION
N=N+legion:CountAvailableAssets(MissionTypes, Attributes)
end
return N
end
--- Count assets of all assigned legions. --- Count assets of all assigned legions.
-- @param #COMMANDER self -- @param #COMMANDER self
-- @param #table MissionTypes (Optional) Count only missions of these types. Default is all types. -- @param #table MissionTypes (Optional) Count only missions of these types. Default is all types.
+3
View File
@@ -167,6 +167,9 @@ function FLEET:New(WarehouseName, FleetName)
-- @param Ops.NavyGroup#NAVYGROUP NavyGroup The NAVYGROUP on mission. -- @param Ops.NavyGroup#NAVYGROUP NavyGroup The NAVYGROUP on mission.
-- @param Ops.Auftrag#AUFTRAG Mission The mission. -- @param Ops.Auftrag#AUFTRAG Mission The mission.
-- Add legion to DB
_DATABASE:AddLegion(self)
return self return self
end end
+3
View File
@@ -73,6 +73,9 @@ function FLOTILLA:New(TemplateGroupName, Ngroups, FlotillaName)
-- Get initial ammo. -- Get initial ammo.
self.ammo=self:_CheckAmmo() self.ammo=self:_CheckAmmo()
-- Add cohort to DB
_DATABASE:AddCohort(self)
return self return self
end end
@@ -350,6 +350,18 @@ INTEL.RCS_NoseOnFraction = 0.15
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Constructor -- Constructor
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Create a new INTEL object and start the FSM.
-- @param #INTEL self
-- @return #INTEL self
function INTEL:_UpdateAgents()
-- Filter coalition.
if self.coalition then
local coalitionname=UTILS.GetCoalitionName(self.coalition):lower()
self.detectionset:FilterCoalitions(coalitionname):FilterAlive():FilterOnce()
end
end
--- Create a new INTEL object and start the FSM. --- Create a new INTEL object and start the FSM.
-- @param #INTEL self -- @param #INTEL self
@@ -837,6 +849,18 @@ function INTEL:AddAgent(AgentGroup)
return self return self
end end
--- Set whether the detection set, aka agents, are updated periodically. With this all alive groups of the INTEL coalition are included.
-- @param #INTEL self
-- @param #boolean switch If `true` or nil, all groups of the coalition will be added automatically to the Agent set.
-- @return #INTEL self
function INTEL:SetAgentAuto(switch)
if switch==nil then
switch=true
end
self.update_detectionset=switch
return self
end
--- Enable or disable cluster analysis of detected targets. --- Enable or disable cluster analysis of detected targets.
-- Targets will be grouped in coupled clusters. -- Targets will be grouped in coupled clusters.
-- @param #INTEL self -- @param #INTEL self
@@ -1026,6 +1050,10 @@ function INTEL:onafterStatus(From, Event, To)
-- FSM state. -- FSM state.
local fsmstate=self:GetState() local fsmstate=self:GetState()
if self.update_detectionset then
self:_UpdateAgents()
end
-- Fresh arrays. -- Fresh arrays.
self.ContactsLost={} self.ContactsLost={}
self.ContactsUnknown={} self.ContactsUnknown={}
+17 -1
View File
@@ -306,7 +306,6 @@ function LEGION:New(WarehouseName, LegionName)
-- @param Ops.Cohort#COHORT Cohort The cohort the asset belongs to. -- @param Ops.Cohort#COHORT Cohort The cohort the asset belongs to.
-- @param Functional.Warehouse#WAREHOUSE.Assetitem Asset The asset that returned. -- @param Functional.Warehouse#WAREHOUSE.Assetitem Asset The asset that returned.
return self return self
end end
@@ -2099,6 +2098,23 @@ function LEGION:CountAssets(InStock, MissionTypes, Attributes)
return N return N
end end
--- Count total number of available assets in the legion stock.
-- @param #LEGION self
-- @param #table MissionTypes (Optional) Count only assest that can perform certain mission type(s). Default is all types.
-- @param #table Attributes (Optional) Count only assest that have a certain attribute(s), e.g. `GROUP.Attribute.AIR_BOMBER`.
-- @return #number Amount of asset groups in stock.
function LEGION:CountAvailableAssets(MissionTypes, Attributes)
local N=0
for _,_cohort in pairs(self.cohorts) do
local cohort=_cohort --Ops.Cohort#COHORT
N=N+cohort:CountAvailableAssets(MissionTypes,Attributes)
end
return N
end
--- Get OPSGROUPs that are spawned and alive. --- Get OPSGROUPs that are spawned and alive.
-- @param #LEGION self -- @param #LEGION self
-- @param #table MissionTypes (Optional) Get only assest that can perform certain mission type(s). Default is all types. -- @param #table MissionTypes (Optional) Get only assest that can perform certain mission type(s). Default is all types.
+1 -1
View File
@@ -4691,7 +4691,7 @@ function OPSGROUP:_UpdateTask(Task, Mission)
self:T(self.lid..string.format("Zone %s captured ==> Task DONE!", zoneCurr:GetName())) self:T(self.lid..string.format("Zone %s captured ==> Task DONE!", zoneCurr:GetName()))
-- Task done. -- Task done.
if Task.StayInZoneTime then if Task.StayInZoneTime and Task.StayInZoneTime>0 then
local stay = Task.StayInZoneTime local stay = Task.StayInZoneTime
self:__TaskDone(stay,Task) self:__TaskDone(stay,Task)
else else
+3
View File
@@ -72,6 +72,9 @@ function PLATOON:New(TemplateGroupName, Ngroups, PlatoonName)
-- Get ammo. -- Get ammo.
self.ammo=self:_CheckAmmo() self.ammo=self:_CheckAmmo()
-- Add cohort to DB
_DATABASE:AddCohort(self)
return self return self
end end
+3
View File
@@ -116,6 +116,9 @@ function SQUADRON:New(TemplateGroupName, Ngroups, SquadronName)
-- See COHORT class -- See COHORT class
-- Add cohort to DB
_DATABASE:AddCohort(self)
return self return self
end end
+91 -51
View File
@@ -153,7 +153,7 @@ _TARGETID=0
--- TARGET class version. --- TARGET class version.
-- @field #string version -- @field #string version
TARGET.version="0.7.1" TARGET.version="0.8.0"
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list -- TODO list
@@ -206,6 +206,9 @@ function TARGET:New(TargetObject)
self:AddTransition("*", "Status", "*") -- Status update. self:AddTransition("*", "Status", "*") -- Status update.
self:AddTransition("*", "Stop", "Stopped") -- Stop FSM. self:AddTransition("*", "Stop", "Stopped") -- Stop FSM.
self:AddTransition("*", "ElementDestroyed", "*") -- A target element was destroyed.
self:AddTransition("*", "ElementDead", "*") -- A target element is dead (destroyed or despawned).
self:AddTransition("*", "ObjectDamaged", "*") -- A target object was damaged. self:AddTransition("*", "ObjectDamaged", "*") -- A target object was damaged.
self:AddTransition("*", "ObjectDestroyed", "*") -- A target object was destroyed. self:AddTransition("*", "ObjectDestroyed", "*") -- A target object was destroyed.
self:AddTransition("*", "ObjectDead", "*") -- A target object is dead (destroyed or despawned). self:AddTransition("*", "ObjectDead", "*") -- A target object is dead (destroyed or despawned).
@@ -245,6 +248,19 @@ function TARGET:New(TargetObject)
-- @param #number delay Delay in seconds. -- @param #number delay Delay in seconds.
--- Triggers the FSM event "ElementDestroyed".
-- @function [parent=#TARGET] ElementDestroyed
-- @param #TARGET self
-- @param #string ElementName Name of the element.
-- @param #TARGET.Object Target Target object.
--- Triggers the FSM event "ElementDead".
-- @function [parent=#TARGET] ElementDead
-- @param #TARGET self
-- @param #string ElementName Name of the element.
-- @param #TARGET.Object Target Target object.
--- Triggers the FSM event "ObjectDamaged". --- Triggers the FSM event "ObjectDamaged".
-- @function [parent=#TARGET] ObjectDamaged -- @function [parent=#TARGET] ObjectDamaged
-- @param #TARGET self -- @param #TARGET self
@@ -643,6 +659,19 @@ function TARGET:onafterStatus(From, Event, To)
-- FSM state. -- FSM state.
local fsmstate=self:GetState() local fsmstate=self:GetState()
-- First we check any target has been destroyed and the dead/unitlost event was not fired
for i,_target in pairs(self.targets) do
local target=_target --#TARGET.Object
local life=self:GetTargetLife(target)
if life<1 and target.Status~=TARGET.ObjectStatus.DEAD then
self:E(self.lid..string.format("FF life is zero but no object dead event fired ==> waiting for target object %s events!", tostring(target.Name)))
-- We wait 60 seconds for the events to occur
self:__Status(-60)
return self
end
end
-- Update damage. -- Update damage.
local damaged=false local damaged=false
for i,_target in pairs(self.targets) do for i,_target in pairs(self.targets) do
@@ -736,6 +765,61 @@ end
-- FSM Events -- FSM Events
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- On after "ElementDestroyed" event.
-- @param #TARGET self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param #string Name Name of the element.
-- @param #TARGET.Object Target Target object.
function TARGET:onafterElementDestroyed(From, Event, To, Name, Target)
-- Debug message.
self:T(self.lid..string.format("Element %s of target object %s destroyed", Name, Target.Name))
-- Increase destroyed counter.
Target.Ndestroyed=Target.Ndestroyed+1
-- Increase dead counter.
self.Ndestroyed=self.Ndestroyed+1
self:ElementDead(Name, Target)
return self
end
--- On after "ElementDead" event.
-- @param #TARGET self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param #string Name Name of the element.
-- @param #TARGET.Object Target Target object.
function TARGET:onafterElementDead(From, Event, To, Name, Target)
-- Debug message.
self:T(self.lid..string.format("Element %s of target object %s dead", Name, Target.Name))
-- Increase dead counter.
Target.Ndead=Target.Ndead+1
-- Increase dead counter.
self.Ndead=self.Ndead+1
-- All dead ==> Trigger event.
if Target.Ndestroyed==Target.N0 then
self:ObjectDestroyed(Target)
elseif Target.Ndead==Target.N0 then
self:ObjectDead(Target)
end
return self
end
--- On after "ObjectDamaged" event. --- On after "ObjectDamaged" event.
-- @param #TARGET self -- @param #TARGET self
-- @param #string From From state. -- @param #string From From state.
@@ -761,11 +845,6 @@ function TARGET:onafterObjectDestroyed(From, Event, To, Target)
-- Debug message. -- Debug message.
self:T(self.lid..string.format("Object %s destroyed", Target.Name)) self:T(self.lid..string.format("Object %s destroyed", Target.Name))
-- Increase destroyed counter.
self.Ndestroyed=self.Ndestroyed+1
Target.Ndestroyed=Target.Ndestroyed+1
Target.Life=0 Target.Life=0
-- Call object dead event. -- Call object dead event.
@@ -788,15 +867,9 @@ function TARGET:onafterObjectDead(From, Event, To, Target)
-- Set target status. -- Set target status.
Target.Status=TARGET.ObjectStatus.DEAD Target.Status=TARGET.ObjectStatus.DEAD
-- Increase dead object counter
Target.Ndead=Target.Ndead+1
-- Set target object life to 0. -- Set target object life to 0.
Target.Life=0 Target.Life=0
-- Increase dead counter.
self.Ndead=self.Ndead+1
-- Check if anyone is alive? -- Check if anyone is alive?
local dead=true local dead=true
for _,_target in pairs(self.targets) do for _,_target in pairs(self.targets) do
@@ -889,54 +962,21 @@ function TARGET:OnEventUnitDeadOrLost(EventData)
-- Add to the list of casualties. -- Add to the list of casualties.
table.insert(self.casualties, Name) table.insert(self.casualties, Name)
-- Try to get target Group. -- Get target from Group or Unit.
local target=self:GetTargetByName(EventData.IniGroupName) local target=self:GetTargetByName(EventData.IniGroupName) or self:GetTargetByName(EventData.IniUnitName)
-- Try unit target.
if not target then
target=self:GetTargetByName(EventData.IniUnitName)
end
-- Check if we could find a target object. -- Check if we could find a target object.
if target then if target then
local Ndead=target.Ndead -- Increase dead/destroyed counter
local Ndestroyed=target.Ndestroyed
if EventData.id==EVENTS.RemoveUnit then if EventData.id==EVENTS.RemoveUnit then
Ndead=Ndead+1 self:ElementDead(Name, target)
else else
Ndestroyed=Ndestroyed+1 self:ElementDestroyed(Name, target)
Ndead=Ndead+1
end end
-- Check if ALL objects are dead
if Ndead==target.N0 then
if Ndestroyed>=target.N0 then
-- Debug message.
self:T2(self.lid..string.format("EVENT ID=%d: target %s dead/lost ==> destroyed", EventData.id, tostring(target.Name)))
target.Life = 0
-- Trigger object destroyed event. This sets the Life to zero and increases Ndestroyed
self:ObjectDestroyed(target)
else else
self:E(self.lid..string.format("ERROR: Could not get target from IniGroup or IniUnit name when event Dead or UnitLost occured! Stats are not correctly updated :("))
-- Debug message.
self:T2(self.lid..string.format("EVENT ID=%d: target %s removed ==> dead", EventData.id, tostring(target.Name)))
target.Life = 0
-- Trigger object dead event. This sets the Life to zero and increases Ndead counter
self:ObjectDead(target)
end
end
end -- Event belongs to this TARGET end -- Event belongs to this TARGET
end end
+25 -6
View File
@@ -1964,6 +1964,25 @@ function AIRBASE:Register(AirbaseName)
-- Category. -- Category.
self.category=self.descriptors and self.descriptors.category or Airbase.Category.AIRDROME self.category=self.descriptors and self.descriptors.category or Airbase.Category.AIRDROME
-- Get DCS object.
local airbase=self:GetDCSObject()
if airbase then
self.objectcategory=Object.getCategory(airbase)
else
self.objectcategory=Object.Category.BASE
end
if self.objectcategory==Object.Category.BASE then
self.objectcategoryName="BASE"
elseif self.objectcategory==Object.Category.STATIC then
self.objectcategoryName="STATIC"
elseif self.objectcategory==Object.Category.UNIT then
self.objectcategoryName="UNIT"
else
self.objectcategoryName="OTHER"
end
-- H2 is bugged -- H2 is bugged
--if self.AirbaseName == "H4" and self.descriptors == nil then --if self.AirbaseName == "H4" and self.descriptors == nil then
--self:E("***** H4 on Syria map is currently bugged!") --self:E("***** H4 on Syria map is currently bugged!")
@@ -1971,12 +1990,12 @@ function AIRBASE:Register(AirbaseName)
--end --end
-- Set category. -- Set category.
if self.category==Airbase.Category.AIRDROME then if self.category==Airbase.Category.AIRDROME then
self.isAirdrome=true self.isAirdrome=true
elseif self.category==Airbase.Category.HELIPAD or self.descriptors.typeName=="FARP_SINGLE_01" then elseif self.category==Airbase.Category.HELIPAD or self.descriptors.typeName=="FARP_SINGLE_01" then
self.isHelipad=true self.isHelipad=true
self.category=Airbase.Category.HELIPAD self.category=Airbase.Category.HELIPAD
elseif self.category==Airbase.Category.SHIP then elseif self.category==Airbase.Category.SHIP then
self.isShip=true self.isShip=true
-- DCS bug: Oil rigs and gas platforms have category=2 (ship). Also they cannot be retrieved by coalition.getStaticObjects() -- DCS bug: Oil rigs and gas platforms have category=2 (ship). Also they cannot be retrieved by coalition.getStaticObjects()
if self.descriptors.typeName=="Oil rig" or self.descriptors.typeName=="Ga" then if self.descriptors.typeName=="Oil rig" or self.descriptors.typeName=="Ga" then
@@ -1986,9 +2005,9 @@ elseif self.category==Airbase.Category.SHIP then
_DATABASE:AddStatic(AirbaseName) _DATABASE:AddStatic(AirbaseName)
end end
if self:GetTypeName() == "Zell" then self.isZell = true end if self:GetTypeName() == "Zell" then self.isZell = true end
else else
self:E("ERROR: Unknown airbase category!") self:E("ERROR: Unknown airbase category!")
end end
-- Init Runways. -- Init Runways.
self:_InitRunways() self:_InitRunways()
@@ -2614,7 +2633,7 @@ function AIRBASE:GetMinimumBoundingCircleFromParkingSpots(mark)
local spots = self:GetParkingSpotsVec2s() local spots = self:GetParkingSpotsVec2s()
if #spots == 0 then return self.AirbaseZone end if #spots == 0 then return self.AirbaseZone end
local center, radius = UTILS.GetMinimumBoundingCircle(spots) local center, radius = UTILS.GetMinimumBoundingCircle(spots)
self.parkingCircle = ZONE_RADIUS:New(self.AirbaseName.." ParkingCircle",center,radius+50) self.parkingCircle = ZONE_RADIUS:New(self.AirbaseName.." ParkingCircle", center, radius+50, true)
if mark == true then if mark == true then
self.parkingCircle:DrawZone(-1,{1,0,0},1,{0,1,0},0.2,3) self.parkingCircle:DrawZone(-1,{1,0,0},1,{0,1,0},0.2,3)
end end