- Fixed bug in WAREHOUSE isShip
- FLIGHTGROUP added damage and ammo checks that trigger FSM events
- Improved engage detected targets
- Improved OPSZONE
This commit is contained in:
Frank 2021-09-24 14:47:36 +02:00
parent d7dae1366d
commit 972fa9f674
11 changed files with 759 additions and 290 deletions

View File

@ -80,7 +80,8 @@
-- @field #string autosavepath Path where the asset file is saved on auto save. -- @field #string autosavepath Path where the asset file is saved on auto save.
-- @field #string autosavefile File name of the auto asset save file. Default is auto generated from warehouse id and name. -- @field #string autosavefile File name of the auto asset save file. Default is auto generated from warehouse id and name.
-- @field #boolean safeparking If true, parking spots for aircraft are considered as occupied if e.g. a client aircraft is parked there. Default false. -- @field #boolean safeparking If true, parking spots for aircraft are considered as occupied if e.g. a client aircraft is parked there. Default false.
-- @field #boolean isunit If true, warehouse is represented by a unit instead of a static. -- @field #boolean isUnit If `true`, warehouse is represented by a unit instead of a static.
-- @field #boolean isShip If `true`, warehouse is represented by a ship unit.
-- @field #number lowfuelthresh Low fuel threshold. Triggers the event AssetLowFuel if for any unit fuel goes below this number. -- @field #number lowfuelthresh Low fuel threshold. Triggers the event AssetLowFuel if for any unit fuel goes below this number.
-- @field #boolean respawnafterdestroyed If true, warehouse is respawned after it was destroyed. Assets are kept. -- @field #boolean respawnafterdestroyed If true, warehouse is respawned after it was destroyed. Assets are kept.
-- @field #number respawndelay Delay before respawn in seconds. -- @field #number respawndelay Delay before respawn in seconds.
@ -1590,7 +1591,8 @@ WAREHOUSE = {
autosavepath = nil, autosavepath = nil,
autosavefile = nil, autosavefile = nil,
saveparking = false, saveparking = false,
isunit = false, isUnit = false,
isShip = false,
lowfuelthresh = 0.15, lowfuelthresh = 0.15,
respawnafterdestroyed=false, respawnafterdestroyed=false,
respawndelay = nil, respawndelay = nil,
@ -1655,6 +1657,7 @@ WAREHOUSE = {
-- @field #table transportassets Table of transport carrier assets. Each element of the table is a @{#WAREHOUSE.Assetitem}. -- @field #table transportassets Table of transport carrier assets. Each element of the table is a @{#WAREHOUSE.Assetitem}.
-- @field #number transportattribute Attribute of transport assets of type @{#WAREHOUSE.Attribute}. -- @field #number transportattribute Attribute of transport assets of type @{#WAREHOUSE.Attribute}.
-- @field #number transportcategory Category of transport assets of type @{#WAREHOUSE.Category}. -- @field #number transportcategory Category of transport assets of type @{#WAREHOUSE.Category}.
-- @field #boolean lateActivation Assets are spawned in late activated state.
--- Item of the warehouse pending queue table. --- Item of the warehouse pending queue table.
-- @type WAREHOUSE.Pendingitem -- @type WAREHOUSE.Pendingitem
@ -1857,39 +1860,45 @@ WAREHOUSE.version="1.0.2"
-- @return #WAREHOUSE self -- @return #WAREHOUSE self
function WAREHOUSE:New(warehouse, alias) function WAREHOUSE:New(warehouse, alias)
-- Inherit everthing from FSM class.
local self=BASE:Inherit(self, FSM:New()) -- #WAREHOUSE
-- Check if just a string was given and convert to static. -- Check if just a string was given and convert to static.
if type(warehouse)=="string" then if type(warehouse)=="string" then
local warehousename=warehouse local warehousename=warehouse
warehouse=UNIT:FindByName(warehousename) warehouse=UNIT:FindByName(warehousename)
if warehouse==nil then if warehouse==nil then
warehouse=STATIC:FindByName(warehousename, true) warehouse=STATIC:FindByName(warehousename, true)
self.isunit=false
else
self.isunit=true
if warehouse:IsShip() then
env.info("FF warehouse is ship!")
self.isShip=true
end
end end
end end
-- Nil check. -- Nil check.
if warehouse==nil then if warehouse==nil then
BASE:E("ERROR: Warehouse does not exist!") env.error("ERROR: Warehouse does not exist!")
return nil return nil
end end
-- Check if we have a STATIC or UNIT object.
if warehouse:IsInstanceOf("STATIC") then
self.isUnit=false
elseif warehouse:IsInstanceOf("UNIT") then
self.isUnit=true
if warehouse:IsShip() then
self.isShip=true
end
else
env.error("ERROR: Warehouse is neither STATIC nor UNIT object!")
return nil
end
-- Set alias. -- Set alias.
self.alias=alias or warehouse:GetName() self.alias=alias or warehouse:GetName()
-- Print version.
env.info(string.format("Adding warehouse v%s for structure %s with alias %s", WAREHOUSE.version, warehouse:GetName(), self.alias))
-- Inherit everthing from FSM class.
local self=BASE:Inherit(self, FSM:New()) -- #WAREHOUSE
-- Set some string id for output to DCS.log file. -- Set some string id for output to DCS.log file.
self.lid=string.format("WAREHOUSE %s | ", self.alias) self.lid=string.format("WAREHOUSE %s | ", self.alias)
-- Print version.
self:I(self.lid..string.format("Adding warehouse v%s for structure %s [isUnit=%s, isShip=%s]", WAREHOUSE.version, warehouse:GetName(), tostring(self:IsUnit()), tostring(self:IsShip())))
-- Set some variables. -- Set some variables.
self.warehouse=warehouse self.warehouse=warehouse
@ -3324,7 +3333,7 @@ end
--- Check if runway is operational. --- Check if runway is operational.
-- @param #WAREHOUSE self -- @param #WAREHOUSE self
-- @return #boolean If true, runway is operational. -- @return #boolean If `true`, runway is operational.
function WAREHOUSE:IsRunwayOperational() function WAREHOUSE:IsRunwayOperational()
if self.airbase then if self.airbase then
if self.runwaydestroyed then if self.runwaydestroyed then
@ -3360,6 +3369,27 @@ function WAREHOUSE:GetRunwayRepairtime()
return 0 return 0
end end
--- Check if warehouse physical representation is a unit (not a static) object.
-- @param #WAREHOUSE self
-- @return #boolean If `true`, warehouse object is a unit.
function WAREHOUSE:IsUnit()
return self.isUnit
end
--- Check if warehouse physical representation is a static (not a unit) object.
-- @param #WAREHOUSE self
-- @return #boolean If `true`, warehouse object is a static.
function WAREHOUSE:IsStatic()
return not self.isUnit
end
--- Check if warehouse physical representation is a ship.
-- @param #WAREHOUSE self
-- @return #boolean If `true`, warehouse object is a ship.
function WAREHOUSE:IsShip()
return self.isShip
end
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- FSM states -- FSM states
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@ -4399,7 +4429,7 @@ function WAREHOUSE:onbeforeRequest(From, Event, To, Request)
-- Delete request from queue because it will never be possible. -- Delete request from queue because it will never be possible.
-- Unless(!) at least one is a moving warehouse, which could, e.g., be an aircraft carrier. -- Unless(!) at least one is a moving warehouse, which could, e.g., be an aircraft carrier.
if not (self.isunit or Request.warehouse.isunit) then if not (self.isUnit or Request.warehouse.isUnit) then
self:_DeleteQueueItem(Request, self.queue) self:_DeleteQueueItem(Request, self.queue)
end end
@ -5828,8 +5858,6 @@ function WAREHOUSE:_SpawnAssetGroundNaval(alias, asset, request, spawnzone, late
-- Late activation. -- Late activation.
template.lateActivation=lateactivated template.lateActivation=lateactivated
--env.info("FF lateActivation="..tostring(template.lateActivation))
template.route.points[1].x = coord.x template.route.points[1].x = coord.x
template.route.points[1].y = coord.z template.route.points[1].y = coord.z
@ -6108,18 +6136,10 @@ function WAREHOUSE:_RouteGround(group, request)
end end
for n,wp in ipairs(Waypoints) do for n,wp in ipairs(Waypoints) do
--env.info(n)
local tf=self:_SimpleTaskFunctionWP("warehouse:_PassingWaypoint",group, n, #Waypoints) local tf=self:_SimpleTaskFunctionWP("warehouse:_PassingWaypoint",group, n, #Waypoints)
group:SetTaskWaypoint(wp, tf) group:SetTaskWaypoint(wp, tf)
end end
-- Task function triggering the arrived event at the last waypoint.
--local TaskFunction = self:_SimpleTaskFunction("warehouse:_Arrived", group)
-- Put task function on last waypoint.
--local Waypoint = Waypoints[#Waypoints]
--group:SetTaskWaypoint(Waypoint, TaskFunction)
-- Route group to destination. -- Route group to destination.
group:Route(Waypoints, 1) group:Route(Waypoints, 1)
@ -7676,7 +7696,7 @@ function WAREHOUSE:_SimpleTaskFunction(Function, group)
local DCSScript = {} local DCSScript = {}
DCSScript[#DCSScript+1] = string.format('local mygroup = GROUP:FindByName(\"%s\") ', groupname) -- The group that executes the task function. Very handy with the "...". DCSScript[#DCSScript+1] = string.format('local mygroup = GROUP:FindByName(\"%s\") ', groupname) -- The group that executes the task function. Very handy with the "...".
if self.isunit then if self.isUnit then
DCSScript[#DCSScript+1] = string.format("local mywarehouse = UNIT:FindByName(\"%s\") ", warehouse) -- The unit that holds the warehouse self object. DCSScript[#DCSScript+1] = string.format("local mywarehouse = UNIT:FindByName(\"%s\") ", warehouse) -- The unit that holds the warehouse self object.
else else
DCSScript[#DCSScript+1] = string.format("local mywarehouse = STATIC:FindByName(\"%s\") ", warehouse) -- The static that holds the warehouse self object. DCSScript[#DCSScript+1] = string.format("local mywarehouse = STATIC:FindByName(\"%s\") ", warehouse) -- The static that holds the warehouse self object.
@ -7707,7 +7727,7 @@ function WAREHOUSE:_SimpleTaskFunctionWP(Function, group, n, N)
local DCSScript = {} local DCSScript = {}
DCSScript[#DCSScript+1] = string.format('local mygroup = GROUP:FindByName(\"%s\") ', groupname) -- The group that executes the task function. Very handy with the "...". DCSScript[#DCSScript+1] = string.format('local mygroup = GROUP:FindByName(\"%s\") ', groupname) -- The group that executes the task function. Very handy with the "...".
if self.isunit then if self.isUnit then
DCSScript[#DCSScript+1] = string.format("local mywarehouse = UNIT:FindByName(\"%s\") ", warehouse) -- The unit that holds the warehouse self object. DCSScript[#DCSScript+1] = string.format("local mywarehouse = UNIT:FindByName(\"%s\") ", warehouse) -- The unit that holds the warehouse self object.
else else
DCSScript[#DCSScript+1] = string.format("local mywarehouse = STATIC:FindByName(\"%s\") ", warehouse) -- The static that holds the warehouse self object. DCSScript[#DCSScript+1] = string.format("local mywarehouse = STATIC:FindByName(\"%s\") ", warehouse) -- The static that holds the warehouse self object.

View File

@ -147,9 +147,10 @@ AIRWING.version="0.9.0"
-- ToDo list -- ToDo list
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO: Spawn in air or hot ==> Needs WAREHOUSE update. -- TODO: Spawn in air ==> Needs WAREHOUSE update.
-- DONE: Spawn in air.
-- TODO: Make special request to transfer squadrons to anther airwing (or warehouse). -- TODO: Make special request to transfer squadrons to anther airwing (or warehouse).
-- TODO: Check that airbase has enough parking spots if a request is BIG. Alternatively, split requests. -- TODO: Check that airbase has enough parking spots if a request is BIG.
-- DONE: Add squadrons to warehouse. -- DONE: Add squadrons to warehouse.
-- DONE: Build mission queue. -- DONE: Build mission queue.
-- DONE: Find way to start missions. -- DONE: Find way to start missions.

View File

@ -55,10 +55,13 @@ ARMYGROUP = {
engage = {}, engage = {},
} }
--- Target --- Engage Target.
-- @type ARMYGROUP.Target -- @type ARMYGROUP.Target
-- @field Ops.Target#TARGET Target The target. -- @field Ops.Target#TARGET Target The target.
-- @field Core.Point#COORDINATE Coordinate Last known coordinate of the target. -- @field Core.Point#COORDINATE Coordinate Last known coordinate of the target.
-- @field Ops.OpsGroup#OPSGROUP.Waypoint Waypoint the waypoint created to go to the target.
-- @field #number roe ROE backup.
-- @field #number alarmstate Alarm state backup.
--- Army Group version. --- Army Group version.
-- @field #string version -- @field #string version
@ -332,7 +335,11 @@ end
function ARMYGROUP:IsCombatReady() function ARMYGROUP:IsCombatReady()
local combatready=true local combatready=true
if self:IsRearming() or self:IsRetreating() or self.outofAmmo or self:IsEngaging() or self:is("Retreated") or self:IsDead() or self:IsStopped() or self:IsInUtero() then if self:IsRearming() or self:IsRetreating() or self:IsOutOfAmmo() or self:IsEngaging() or self:IsDead() or self:IsStopped() or self:IsInUtero() then
combatready=false
end
if self:IsPickingup() or self:IsLoading() or self:IsTransporting() or self:IsLoaded() or self:IsCargo() or self:IsCarrier() then
combatready=false combatready=false
end end
@ -356,26 +363,20 @@ function ARMYGROUP:Status()
if alive then if alive then
--- -- Update position etc.
-- Detection self:_UpdatePosition()
---
-- Check if group has detected any units. -- Check if group has detected any units.
if self.detectionOn then self:_CheckDetectedUnits()
self:_CheckDetectedUnits()
end
-- Check ammo status. -- Check ammo status.
self:_CheckAmmoStatus() self:_CheckAmmoStatus()
-- Update position etc.
self:_UpdatePosition()
-- Check if group got stuck.
self:_CheckStuck()
-- Check damage of elements and group. -- Check damage of elements and group.
self:_CheckDamage() self:_CheckDamage()
-- Check if group got stuck.
self:_CheckStuck()
-- Update engagement. -- Update engagement.
if self:IsEngaging() then if self:IsEngaging() then
@ -462,6 +463,21 @@ function ARMYGROUP:Status()
end end
self:I(self.lid..text) self:I(self.lid..text)
end end
---
-- Engage Detected Targets
---
if self:IsCruising() and self.detectionOn and self.engagedetectedOn then
local targetgroup, targetdist=self:_GetDetectedTarget()
-- If we found a group, we engage it.
if targetgroup then
self:I(self.lid..string.format("Engaging target group %s at distance %d meters", targetgroup:GetName(), targetdist))
self:EngageTarget(targetgroup)
end
end
--- ---
@ -580,7 +596,7 @@ function ARMYGROUP:onafterSpawned(From, Event, To)
-- Update route. -- Update route.
if #self.waypoints>1 then if #self.waypoints>1 then
self:Cruise(nil, self.option.Formation) self:__Cruise(-0.1, nil, self.option.Formation)
else else
self:FullStop() self:FullStop()
end end
@ -1003,11 +1019,13 @@ function ARMYGROUP:onafterEngageTarget(From, Event, To, Target)
-- Target coordinate. -- Target coordinate.
self.engage.Coordinate=UTILS.DeepCopy(self.engage.Target:GetCoordinate()) self.engage.Coordinate=UTILS.DeepCopy(self.engage.Target:GetCoordinate())
-- TODO: Backup current ROE and alarm state and reset after disengage. -- Backup ROE and alarm state.
self.engage.roe=self:GetROE()
self.engage.alarmstate=self:GetAlarmstate()
-- Switch ROE and alarm state. -- Switch ROE and alarm state.
self:SwitchAlarmstate(ENUMS.AlarmState.Auto) self:SwitchAlarmstate(ENUMS.AlarmState.Auto)
self:SwitchROE(ENUMS.ROE.WeaponFree) self:SwitchROE(ENUMS.ROE.OpenFire)
-- ID of current waypoint. -- ID of current waypoint.
local uid=self:GetWaypointCurrent().uid local uid=self:GetWaypointCurrent().uid
@ -1025,17 +1043,19 @@ end
function ARMYGROUP:_UpdateEngageTarget() function ARMYGROUP:_UpdateEngageTarget()
if self.engage.Target and self.engage.Target:IsAlive() then if self.engage.Target and self.engage.Target:IsAlive() then
--env.info("FF Update Engage Target "..self.engage.Target:GetName())
local vec3=self.engage.Target:GetCoordinate():GetVec3() -- Get current position vector.
local vec3=self.engage.Target:GetVec3()
local dist=UTILS.VecDist2D(vec3, self.engage.Coordinate:GetVec3()) -- Distance to last known position of target.
local dist=UTILS.VecDist3D(vec3, self.engage.Coordinate:GetVec3())
-- Check if target moved more than 100 meters.
if dist>100 then if dist>100 then
--env.info("FF Update Engage Target Moved "..self.engage.Target:GetName()) --env.info("FF Update Engage Target Moved "..self.engage.Target:GetName())
-- Update new position.
self.engage.Coordinate:UpdateFromVec3(vec3) self.engage.Coordinate:UpdateFromVec3(vec3)
-- ID of current waypoint. -- ID of current waypoint.
@ -1053,7 +1073,10 @@ function ARMYGROUP:_UpdateEngageTarget()
end end
else else
-- Target not alive any more == Disengage.
self:Disengage() self:Disengage()
end end
end end
@ -1066,8 +1089,17 @@ end
function ARMYGROUP:onafterDisengage(From, Event, To) function ARMYGROUP:onafterDisengage(From, Event, To)
self:T(self.lid.."Disengage Target") self:T(self.lid.."Disengage Target")
-- TODO: Reset ROE and alarm state. -- Restore previous ROE and alarm state.
self:_CheckGroupDone(1) self:SwitchROE(self.engage.roe)
self:SwitchAlarmstate(self.engage.alarmstate)
-- Remove current waypoint
if self.engage.Waypoint then
self:RemoveWaypointByID(self.engage.Waypoint.uid)
end
-- Check group is done
self:_CheckGroupDone(1)
end end
--- On after "Rearmed" event. --- On after "Rearmed" event.

View File

@ -152,16 +152,21 @@
-- --
-- === -- ===
-- --
-- ![Banner Image](..\Presentations\OPS\Auftrag\_Main.png)
--
-- # The AUFTRAG Concept -- # The AUFTRAG Concept
-- --
-- The AUFTRAG class significantly simplifies the workflow of using DCS tasks. -- The AUFTRAG class significantly simplifies the workflow of using DCS tasks.
-- --
-- You can think of an AUFTRAG as document, which contains the mission briefing, i.e. information about the target location, mission altitude, speed and various other parameters. -- You can think of an AUFTRAG as document, which contains the mission briefing, i.e. information about the target location, mission altitude, speed and various other parameters.
-- This document can be handed over directly to a pilot (or multiple pilots) via the @{Ops.FlightGroup#FLIGHTGROUP} class. The pilots will then execute the mission. -- This document can be handed over directly to a pilot (or multiple pilots) via the @{Ops.FlightGroup#FLIGHTGROUP} class. The pilots will then execute the mission.
-- The AUFTRAG document can also be given to an AIRWING. The airwing will then determine the best assets (pilots and payloads) available for the job. --
-- One more up the food chain, an AUFTRAG can be passed to a WINGCOMMANDER. The wing commander will find the best AIRWING and pass the job over to it. -- The AUFTRAG document can also be given to an AIRWING. The airwing will then determine the best assets (pilots and payloads) available for the job.
--
-- Similarly, an AUFTRAG can be given to ground or navel groups via the @{Ops.ArmyGroup#ARMYGROUP} or @{Ops.NavyGroup#NAVYGROUP} classes, respectively. These classes have also
-- AIRWING analouges, which are called BRIGADE and FLEET. Brigades and fleets will likewise select the best assets they have available and pass on the AUFTRAG to them.
--
--
-- One more up the food chain, an AUFTRAG can be passed to a COMMANDER. The commander will recruit the best assets of AIRWINGs, BRIGADEs and/or FLEETs and pass the job over to it.
--
-- --
-- # Airborne Missions -- # Airborne Missions
-- --
@ -169,7 +174,7 @@
-- --
-- ## Anti-Ship -- ## Anti-Ship
-- --
-- An anti-ship mission can be created with the @{#AUFTRAG.NewANTISHIP}(*Target, Altitude*) function. -- An anti-ship mission can be created with the @{#AUFTRAG.NewANTISHIP}() function.
-- --
-- ## AWACS -- ## AWACS
-- --
@ -225,7 +230,7 @@
-- --
-- ## RECON -- ## RECON
-- --
-- Not implemented yet. -- An reconnaissance mission can be created with the @{#AUFTRAG.NewRECON}() function.
-- --
-- ## RESCUE HELO -- ## RESCUE HELO
-- --
@ -260,40 +265,43 @@
-- --
-- # Assigning Missions -- # Assigning Missions
-- --
-- An AUFTRAG can be assigned to groups, airwings or wingcommanders -- An AUFTRAG can be assigned to groups (FLIGHTGROUP, ARMYGROUP, NAVYGROUP), legions (AIRWING, BRIGADE, FLEET) or to a COMMANDER.
-- --
-- ## Group Level -- ## Group Level
-- --
-- ### Flight Group -- ### Flight Group
-- --
-- Assigning an AUFTRAG to a flight groups is done via the @{Ops.FlightGroup#FLIGHTGROUP.AddMission} function. See FLIGHTGROUP docs for details. -- Assigning an AUFTRAG to a flight group is done via the @{Ops.FlightGroup#FLIGHTGROUP.AddMission} function. See FLIGHTGROUP docs for details.
--
-- ### Army Group
--
-- Assigning an AUFTRAG to an army group is done via the @{Ops.ArmyGroup#ARMYGROUP.AddMission} function. See ARMYGROUP docs for details.
-- --
-- ### Navy Group -- ### Navy Group
-- --
-- Assigning an AUFTRAG to a navy groups is done via the @{Ops.NavyGroup#NAVYGROUP.AddMission} function. See NAVYGROUP docs for details. -- Assigning an AUFTRAG to a navy group is done via the @{Ops.NavyGroup#NAVYGROUP.AddMission} function. See NAVYGROUP docs for details.
-- --
-- ## Legion Level -- ## Legion Level
-- --
-- Adding an AUFTRAG to an airwing is done via the @{Ops.AirWing#AIRWING.AddMission} function. See AIRWING docs for further details. -- Adding an AUFTRAG to an airwing is done via the @{Ops.AirWing#AIRWING.AddMission} function. See AIRWING docs for further details.
-- Similarly, an AUFTRAG can be added to a brigade via the @{Ops.Brigade#BRIGADE.AddMission} function -- Similarly, an AUFTRAG can be added to a brigade via the @{Ops.Brigade#BRIGADE.AddMission} function.
-- --
-- ## Commander Level -- ## Commander Level
-- --
-- Assigning an AUFTRAG to acommander is done via the @{Ops.Commander#COMMANDER.AddMission} function. See COMMADER docs for details. -- Assigning an AUFTRAG to acommander is done via the @{Ops.Commander#COMMANDER.AddMission} function. See COMMANDER docs for details.
-- --
-- ## Chief Level
--
-- Assigning an AUFTRAG to a wing commander is done via the @{Ops.Chief#CHIEF.AddMission} function. See CHIEF docs for details.
-- --
--
--
-- # Events -- # Events
-- --
-- The AUFTRAG class creates many useful (FSM) events, which can be used in the mission designers script. -- The AUFTRAG class creates many useful (FSM) events, which can be used in the mission designers script.
--
-- TODO
-- --
-- --
-- # Examples -- # Examples
-- --
-- TODO
--
-- --
-- @field #AUFTRAG -- @field #AUFTRAG
AUFTRAG = { AUFTRAG = {
@ -599,8 +607,8 @@ function AUFTRAG:New(Type)
self:SetStartState(self.status) self:SetStartState(self.status)
-- PLANNED --> (QUEUED) --> (REQUESTED) --> SCHEDULED --> STARTED --> EXECUTING --> DONE -- PLANNED --> (QUEUED) --> (REQUESTED) --> SCHEDULED --> STARTED --> EXECUTING --> DONE
self:AddTransition("*", "Planned", AUFTRAG.Status.PLANNED) -- Mission is in planning stage. self:AddTransition("*", "Planned", AUFTRAG.Status.PLANNED) -- Mission is in planning stage. Could be in the queue of a COMMANDER or CHIEF.
self:AddTransition(AUFTRAG.Status.PLANNED, "Queued", AUFTRAG.Status.QUEUED) -- Mission is in queue of an AIRWING. self:AddTransition(AUFTRAG.Status.PLANNED, "Queued", AUFTRAG.Status.QUEUED) -- Mission is in queue of a LEGION.
self:AddTransition(AUFTRAG.Status.QUEUED, "Requested", AUFTRAG.Status.REQUESTED) -- Mission assets have been requested from the warehouse. self:AddTransition(AUFTRAG.Status.QUEUED, "Requested", AUFTRAG.Status.REQUESTED) -- Mission assets have been requested from the warehouse.
self:AddTransition(AUFTRAG.Status.REQUESTED, "Scheduled", AUFTRAG.Status.SCHEDULED) -- Mission added to the first ops group queue. self:AddTransition(AUFTRAG.Status.REQUESTED, "Scheduled", AUFTRAG.Status.SCHEDULED) -- Mission added to the first ops group queue.
@ -624,6 +632,171 @@ function AUFTRAG:New(Type)
self:AddTransition("*", "ElementDestroyed", "*") self:AddTransition("*", "ElementDestroyed", "*")
self:AddTransition("*", "GroupDead", "*") self:AddTransition("*", "GroupDead", "*")
self:AddTransition("*", "AssetDead", "*") self:AddTransition("*", "AssetDead", "*")
------------------------
--- Pseudo Functions ---
------------------------
--- Triggers the FSM event "Status".
-- @function [parent=#AUFTRAG] Status
-- @param #AUFTRAG self
--- Triggers the FSM event "Status" after a delay.
-- @function [parent=#AUFTRAG] __Status
-- @param #AUFTRAG self
-- @param #number delay Delay in seconds.
--- Triggers the FSM event "Stop".
-- @function [parent=#AUFTRAG] Stop
-- @param #AUFTRAG self
--- Triggers the FSM event "Stop" after a delay.
-- @function [parent=#AUFTRAG] __Stop
-- @param #AUFTRAG self
-- @param #number delay Delay in seconds.
--- Triggers the FSM event "Planned".
-- @function [parent=#AUFTRAG] Planned
-- @param #AUFTRAG self
--- Triggers the FSM event "Planned" after a delay.
-- @function [parent=#AUFTRAG] __Planned
-- @param #AUFTRAG self
-- @param #number delay Delay in seconds.
--- Triggers the FSM event "Queued".
-- @function [parent=#AUFTRAG] Queued
-- @param #AUFTRAG self
--- Triggers the FSM event "Queued" after a delay.
-- @function [parent=#AUFTRAG] __Queued
-- @param #AUFTRAG self
-- @param #number delay Delay in seconds.
--- Triggers the FSM event "Requested".
-- @function [parent=#AUFTRAG] Requested
-- @param #AUFTRAG self
--- Triggers the FSM event "Requested" after a delay.
-- @function [parent=#AUFTRAG] __Requested
-- @param #AUFTRAG self
-- @param #number delay Delay in seconds.
--- Triggers the FSM event "Scheduled".
-- @function [parent=#AUFTRAG] Scheduled
-- @param #AUFTRAG self
--- Triggers the FSM event "Scheduled" after a delay.
-- @function [parent=#AUFTRAG] __Scheduled
-- @param #AUFTRAG self
-- @param #number delay Delay in seconds.
--- Triggers the FSM event "Started".
-- @function [parent=#AUFTRAG] Started
-- @param #AUFTRAG self
--- Triggers the FSM event "Started" after a delay.
-- @function [parent=#AUFTRAG] __Started
-- @param #AUFTRAG self
-- @param #number delay Delay in seconds.
--- Triggers the FSM event "Executing".
-- @function [parent=#AUFTRAG] Executing
-- @param #AUFTRAG self
--- Triggers the FSM event "Executing" after a delay.
-- @function [parent=#AUFTRAG] __Executing
-- @param #AUFTRAG self
-- @param #number delay Delay in seconds.
--- Triggers the FSM event "Cancel".
-- @function [parent=#AUFTRAG] Cancel
-- @param #AUFTRAG self
--- Triggers the FSM event "Cancel" after a delay.
-- @function [parent=#AUFTRAG] __Cancel
-- @param #AUFTRAG self
-- @param #number delay Delay in seconds.
--- On after "Cancel" event.
-- @function [parent=#AUFTRAG] OnAfterCancel
-- @param #AUFTRAG self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
--- Triggers the FSM event "Done".
-- @function [parent=#AUFTRAG] Done
-- @param #AUFTRAG self
--- Triggers the FSM event "Done" after a delay.
-- @function [parent=#AUFTRAG] __Done
-- @param #AUFTRAG self
-- @param #number delay Delay in seconds.
--- On after "Done" event.
-- @function [parent=#AUFTRAG] OnAfterDone
-- @param #AUFTRAG self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
--- Triggers the FSM event "Success".
-- @function [parent=#AUFTRAG] Success
-- @param #AUFTRAG self
--- Triggers the FSM event "Success" after a delay.
-- @function [parent=#AUFTRAG] __Success
-- @param #AUFTRAG self
-- @param #number delay Delay in seconds.
--- On after "Success" event.
-- @function [parent=#AUFTRAG] OnAfterSuccess
-- @param #AUFTRAG self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
--- Triggers the FSM event "Failure".
-- @function [parent=#AUFTRAG] Failure
-- @param #AUFTRAG self
--- Triggers the FSM event "Failure" after a delay.
-- @function [parent=#AUFTRAG] __Failure
-- @param #AUFTRAG self
-- @param #number delay Delay in seconds.
--- On after "Failure" event.
-- @function [parent=#AUFTRAG] OnAfterFailure
-- @param #AUFTRAG self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
--- Triggers the FSM event "Repeat".
-- @function [parent=#AUFTRAG] Repeat
-- @param #AUFTRAG self
--- Triggers the FSM event "Repeat" after a delay.
-- @function [parent=#AUFTRAG] __Repeat
-- @param #AUFTRAG self
-- @param #number delay Delay in seconds.
--- On after "Repeat" event.
-- @function [parent=#AUFTRAG] OnAfterRepeat
-- @param #AUFTRAG self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- Init status update. -- Init status update.
self:__Status(-1) self:__Status(-1)
@ -1921,6 +2094,44 @@ function AUFTRAG:SetEngageAltitude(Altitude)
return self return self
end end
--- Enable to automatically engage detected targets.
-- @param #AUFTRAG self
-- @param #number RangeMax Max range in NM. Only detected targets within this radius from the group will be engaged. Default is 25 NM.
-- @param #table TargetTypes Types of target attributes that will be engaged. See [DCS enum attributes](https://wiki.hoggitworld.com/view/DCS_enum_attributes). Default "All".
-- @param Core.Set#SET_ZONE EngageZoneSet Set of zones in which targets are engaged. Default is anywhere.
-- @param Core.Set#SET_ZONE NoEngageZoneSet Set of zones in which targets are *not* engaged. Default is nowhere.
-- @return #AUFTRAG self
function AUFTRAG:SetEngageDetected(RangeMax, TargetTypes, EngageZoneSet, NoEngageZoneSet)
-- Ensure table.
if TargetTypes then
if type(TargetTypes)~="table" then
TargetTypes={TargetTypes}
end
else
TargetTypes={"All"}
end
-- Ensure SET_ZONE if ZONE is provided.
if EngageZoneSet and EngageZoneSet:IsInstanceOf("ZONE_BASE") then
local zoneset=SET_ZONE:New():AddZone(EngageZoneSet)
EngageZoneSet=zoneset
end
if NoEngageZoneSet and NoEngageZoneSet:IsInstanceOf("ZONE_BASE") then
local zoneset=SET_ZONE:New():AddZone(NoEngageZoneSet)
NoEngageZoneSet=zoneset
end
-- Set parameters.
self.engagedetectedOn=true
self.engagedetectedRmax=UTILS.NMToMeters(RangeMax or 25)
self.engagedetectedTypes=TargetTypes
self.engagedetectedEngageZones=EngageZoneSet
self.engagedetectedNoEngageZones=NoEngageZoneSet
return self
end
--- Set mission altitude. This is the altitude of the waypoint create where the DCS task is executed. --- Set mission altitude. This is the altitude of the waypoint create where the DCS task is executed.
-- @param #AUFTRAG self -- @param #AUFTRAG self
-- @param #string Altitude Altitude in feet. -- @param #string Altitude Altitude in feet.
@ -4039,8 +4250,9 @@ end
-- @param #AUFTRAG self -- @param #AUFTRAG self
-- @param Wrapper.Group#GROUP group Group. -- @param Wrapper.Group#GROUP group Group.
-- @param #number randomradius Random radius in meters. -- @param #number randomradius Random radius in meters.
-- @param #table surfacetypes Surface types of random zone.
-- @return Core.Point#COORDINATE Coordinate where the mission is executed. -- @return Core.Point#COORDINATE Coordinate where the mission is executed.
function AUFTRAG:GetMissionWaypointCoord(group, randomradius) function AUFTRAG:GetMissionWaypointCoord(group, randomradius, surfacetypes)
-- Check if a coord has been explicitly set. -- Check if a coord has been explicitly set.
if self.missionWaypointCoord then if self.missionWaypointCoord then
@ -4052,12 +4264,12 @@ function AUFTRAG:GetMissionWaypointCoord(group, randomradius)
end end
-- Create waypoint coordinate half way between us and the target. -- Create waypoint coordinate half way between us and the target.
local waypointcoord=group:GetCoordinate():GetIntermediateCoordinate(self:GetTargetCoordinate(), self.missionFraction) local waypointcoord=group:GetCoordinate():GetIntermediateCoordinate(self:GetTargetCoordinate(), self.missionFraction)
local alt=waypointcoord.y local alt=waypointcoord.y
-- Add some randomization. -- Add some randomization.
if randomradius then if randomradius then
waypointcoord=ZONE_RADIUS:New("Temp", waypointcoord:GetVec2(), randomradius):GetRandomCoordinate():SetAltitude(alt, false) waypointcoord=ZONE_RADIUS:New("Temp", waypointcoord:GetVec2(), randomradius):GetRandomCoordinate(nil, nil, surfacetypes):SetAltitude(alt, false)
end end
-- Set altitude of mission waypoint. -- Set altitude of mission waypoint.

View File

@ -413,6 +413,47 @@ function CHIEF:GetCommander()
return self.commander return self.commander
end end
--- Add an AIRWING to the chief's commander.
-- @param #CHIEF self
-- @param Ops.AirWing#AIRWING Airwing The airwing to add.
-- @return #CHIEF self
function CHIEF:AddAirwing(Airwing)
-- Add airwing to the commander.
self:AddLegion(Airwing)
return self
end
--- Add a BRIGADE to the chief's commander.
-- @param #CHIEF self
-- @param Ops.Brigade#BRIGADE Brigade The brigade to add.
-- @return #CHIEF self
function CHIEF:AddBrigade(Brigade)
-- Add brigade to the commander
self:AddLegion(Brigade)
return self
end
--- Add a LEGION to the chief's commander.
-- @param #CHIEF self
-- @param Ops.Legion#LEGION Legion The legion to add.
-- @return #CHIEF self
function CHIEF:AddLegion(Legion)
-- Set chief of the legion.
Legion.chief=self
-- Add legion to the commander.
self.commander:AddLegion(Legion)
return self
end
--- Add mission to mission queue of the COMMANDER. --- Add mission to mission queue of the COMMANDER.
-- @param #CHIEF self -- @param #CHIEF self
-- @param Ops.Auftrag#AUFTRAG Mission Mission to be added. -- @param Ops.Auftrag#AUFTRAG Mission Mission to be added.
@ -1181,10 +1222,8 @@ function CHIEF:CheckOpsZoneQueue()
env.info(string.format("Zone %s is owned by coalition %d", opszone.zone:GetName(), ownercoalition)) env.info(string.format("Zone %s is owned by coalition %d", opszone.zone:GetName(), ownercoalition))
-- Recruit ground assets that -- Recruit ground assets that
local recruited, assets, legions=self:RecruitAssetsForZone(opszone, AUFTRAG.Type.PATROLZONE, 1, 3, {Group.Category.GROUND}, {GROUP.Attribute.GROUND_INFANTRY}) local recruited=self:RecruitAssetsForZone(opszone, AUFTRAG.Type.PATROLZONE, 1, 3, {Group.Category.GROUND}, {GROUP.Attribute.GROUND_INFANTRY})
end end
end end
@ -1494,8 +1533,6 @@ end
-- @param #table Categories Group categories of the assets. -- @param #table Categories Group categories of the assets.
-- @param #table Attributes Generalized group attributes. -- @param #table Attributes Generalized group attributes.
-- @return #boolean If `true` enough assets could be recruited. -- @return #boolean If `true` enough assets could be recruited.
-- @return #table Assets that have been recruited from all legions.
-- @return #table Legions that have recruited assets.
function CHIEF:RecruitAssetsForZone(OpsZone, MissionType, NassetsMin, NassetsMax, Categories, Attributes) function CHIEF:RecruitAssetsForZone(OpsZone, MissionType, NassetsMin, NassetsMax, Categories, Attributes)
-- Cohorts. -- Cohorts.
@ -1520,7 +1557,7 @@ function CHIEF:RecruitAssetsForZone(OpsZone, MissionType, NassetsMin, NassetsMax
-- Target position. -- Target position.
local TargetVec2=OpsZone.zone:GetVec2() local TargetVec2=OpsZone.zone:GetVec2()
-- Recruite assets. -- Recruite infantry assets.
local recruitedInf, assetsInf, legionsInf=LEGION.RecruitCohortAssets(Cohorts, MissionType, nil, NassetsMin, NassetsMax, TargetVec2, nil, nil, nil, nil, Categories, Attributes) local recruitedInf, assetsInf, legionsInf=LEGION.RecruitCohortAssets(Cohorts, MissionType, nil, NassetsMin, NassetsMax, TargetVec2, nil, nil, nil, nil, Categories, Attributes)
if recruitedInf then if recruitedInf then
@ -1536,9 +1573,9 @@ function CHIEF:RecruitAssetsForZone(OpsZone, MissionType, NassetsMin, NassetsMax
end end
end end
-- Recruite assets. -- Recruite carrier assets. This need to be ground or helicopters to deploy at a zone.
local recruitedTrans, assetsTrans, legionsTrans= local recruitedTrans, assetsTrans, legionsTrans=
LEGION.RecruitCohortAssets(Cohorts, AUFTRAG.Type.OPSTRANSPORT, nil, NassetsMin, NassetsMax, TargetVec2, nil, nil, nil, weightMax, {Group.Category.HELICOPTER, Group.Category.GROUND}) LEGION.RecruitCohortAssets(Cohorts, AUFTRAG.Type.OPSTRANSPORT, nil, 1, 1, TargetVec2, nil, nil, nil, weightMax, {Group.Category.HELICOPTER, Group.Category.GROUND})
local transport=nil --Ops.OpsTransport#OPSTRANSPORT local transport=nil --Ops.OpsTransport#OPSTRANSPORT
if recruitedTrans then if recruitedTrans then
@ -1550,7 +1587,15 @@ function CHIEF:RecruitAssetsForZone(OpsZone, MissionType, NassetsMin, NassetsMax
-- Add cargo assets to transport. -- Add cargo assets to transport.
for _,_legion in pairs(legionsInf) do for _,_legion in pairs(legionsInf) do
local legion=_legion --Ops.Legion#LEGION local legion=_legion --Ops.Legion#LEGION
local tpz=transport:AddTransportZoneCombo(legion.spawnzone, OpsZone.zone)
-- Pickup at spawnzone or at airbase if the legion warehouse has one.
local pickupzone=legion.spawnzone
if legion.airbase and legion:IsRunwayOperational() then
pickupzone=ZONE_AIRBASE:New(legion.airbasename, 4000)
end
local tpz=transport:AddTransportZoneCombo(pickupzone, OpsZone.zone)
for _,_asset in pairs(assetsInf) do for _,_asset in pairs(assetsInf) do
local asset=_asset --Functional.Warehouse#WAREHOUSE.Assetitem local asset=_asset --Functional.Warehouse#WAREHOUSE.Assetitem
if asset.legion.alias==legion.alias then if asset.legion.alias==legion.alias then
@ -1579,6 +1624,7 @@ function CHIEF:RecruitAssetsForZone(OpsZone, MissionType, NassetsMin, NassetsMax
-- Create Patrol zone mission. -- Create Patrol zone mission.
local mission=AUFTRAG:NewPATROLZONE(OpsZone.zone) local mission=AUFTRAG:NewPATROLZONE(OpsZone.zone)
mission:SetEngageDetected()
for _,asset in pairs(assetsInf) do for _,asset in pairs(assetsInf) do
mission:AddAsset(asset) mission:AddAsset(asset)
@ -1593,11 +1639,13 @@ function CHIEF:RecruitAssetsForZone(OpsZone, MissionType, NassetsMin, NassetsMax
OpsZone.missionPatrol=mission OpsZone.missionPatrol=mission
return true
else else
LEGION.UnRecruitAssets(assetsInf) LEGION.UnRecruitAssets(assetsInf)
return false
end end
return recruited, assets, legions return nil
end end
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

View File

@ -20,7 +20,7 @@
-- @field #table legions Table of legions which are commanded. -- @field #table legions Table of legions which are commanded.
-- @field #table missionqueue Mission queue. -- @field #table missionqueue Mission queue.
-- @field #table transportqueue Transport queue. -- @field #table transportqueue Transport queue.
-- @field Ops.ChiefOfStaff#CHIEF chief Chief of staff. -- @field Ops.Chief#CHIEF chief Chief of staff.
-- @extends Core.Fsm#FSM -- @extends Core.Fsm#FSM
--- Be surprised! --- Be surprised!
@ -736,6 +736,7 @@ function COMMANDER:CheckMissionQueue()
for _,_legion in pairs(legions) do for _,_legion in pairs(legions) do
local legion=_legion --Ops.Legion#LEGION local legion=_legion --Ops.Legion#LEGION
-- Set pickup zone to spawn zone or airbase if the legion has one that is operational.
local pickupzone=legion.spawnzone local pickupzone=legion.spawnzone
if legion.airbase and legion:IsRunwayOperational() then if legion.airbase and legion:IsRunwayOperational() then
pickupzone=ZONE_AIRBASE:New(legion.airbasename, 4000) pickupzone=ZONE_AIRBASE:New(legion.airbasename, 4000)

View File

@ -501,55 +501,6 @@ function FLIGHTGROUP:SetFuelCriticalRTB(switch)
return self return self
end end
--- Enable to automatically engage detected targets.
-- @param #FLIGHTGROUP self
-- @param #number RangeMax Max range in NM. Only detected targets within this radius from the group will be engaged. Default is 25 NM.
-- @param #table TargetTypes Types of target attributes that will be engaged. See [DCS enum attributes](https://wiki.hoggitworld.com/view/DCS_enum_attributes). Default "All".
-- @param Core.Set#SET_ZONE EngageZoneSet Set of zones in which targets are engaged. Default is anywhere.
-- @param Core.Set#SET_ZONE NoEngageZoneSet Set of zones in which targets are *not* engaged. Default is nowhere.
-- @return #FLIGHTGROUP self
function FLIGHTGROUP:SetEngageDetectedOn(RangeMax, TargetTypes, EngageZoneSet, NoEngageZoneSet)
-- Ensure table.
if TargetTypes then
if type(TargetTypes)~="table" then
TargetTypes={TargetTypes}
end
else
TargetTypes={"All"}
end
-- Ensure SET_ZONE if ZONE is provided.
if EngageZoneSet and EngageZoneSet:IsInstanceOf("ZONE_BASE") then
local zoneset=SET_ZONE:New():AddZone(EngageZoneSet)
EngageZoneSet=zoneset
end
if NoEngageZoneSet and NoEngageZoneSet:IsInstanceOf("ZONE_BASE") then
local zoneset=SET_ZONE:New():AddZone(NoEngageZoneSet)
NoEngageZoneSet=zoneset
end
-- Set parameters.
self.engagedetectedOn=true
self.engagedetectedRmax=UTILS.NMToMeters(RangeMax or 25)
self.engagedetectedTypes=TargetTypes
self.engagedetectedEngageZones=EngageZoneSet
self.engagedetectedNoEngageZones=NoEngageZoneSet
-- Ensure detection is ON or it does not make any sense.
self:SetDetection(true)
return self
end
--- Disable to automatically engage detected targets.
-- @param #FLIGHTGROUP self
-- @return #FLIGHTGROUP self
function FLIGHTGROUP:SetEngageDetectedOff()
self.engagedetectedOn=false
return self
end
--- Enable that the group is despawned after landing. This can be useful to avoid DCS taxi issues with other AI or players or jamming taxiways. --- Enable that the group is despawned after landing. This can be useful to avoid DCS taxi issues with other AI or players or jamming taxiways.
-- @param #FLIGHTGROUP self -- @param #FLIGHTGROUP self
@ -841,23 +792,28 @@ function FLIGHTGROUP:Status()
-- FSM state. -- FSM state.
local fsmstate=self:GetState() local fsmstate=self:GetState()
-- Is group alive?
local alive=self:IsAlive()
-- Update position. -- Update position.
self:_UpdatePosition() self:_UpdatePosition()
---
-- Detection
---
-- Check if group has detected any units. -- Check if group has detected any units.
if self.detectionOn then self:_CheckDetectedUnits()
self:_CheckDetectedUnits()
end -- Check ammo status.
self:_CheckAmmoStatus()
-- Check damage.
self:_CheckDamage()
--- ---
-- Parking -- Parking
--- ---
-- TODO: _CheckParking() function
-- Check if flight began to taxi (if it was parking). -- Check if flight began to taxi (if it was parking).
if self:IsParking() then if self:IsParking() then
for _,_element in pairs(self.elements) do for _,_element in pairs(self.elements) do
@ -930,11 +886,6 @@ function FLIGHTGROUP:Status()
local lp0=unit:GetLife0() local lp0=unit:GetLife0()
local parking=element.parking and tostring(element.parking.TerminalID) or "X" local parking=element.parking and tostring(element.parking.TerminalID) or "X"
-- Check if element is not dead and we missed an event.
--if life<=0 and element.status~=OPSGROUP.ElementStatus.DEAD and element.status~=OPSGROUP.ElementStatus.INUTERO then
-- self:ElementDead(element)
--end
-- Get ammo. -- Get ammo.
local ammo=self:GetAmmoElement(element) local ammo=self:GetAmmoElement(element)
@ -952,7 +903,9 @@ function FLIGHTGROUP:Status()
-- Distance travelled -- Distance travelled
--- ---
if self.verbose>=4 and self:IsAlive() then if self.verbose>=4 and alive then
-- TODO: _Check distance travelled.
-- Travelled distance since last check. -- Travelled distance since last check.
local ds=self.travelds local ds=self.travelds
@ -998,18 +951,14 @@ function FLIGHTGROUP:Status()
end end
---
-- Tasks & Missions
---
self:_PrintTaskAndMissionStatus()
--- ---
-- Fuel State -- Fuel State
--- ---
-- TODO: _CheckFuelState() function.
-- Only if group is in air. -- Only if group is in air.
if self:IsAlive() and self.group:IsAirborne(true) then if alive and self.group:IsAirborne(true) then
local fuelmin=self:GetFuelMin() local fuelmin=self:GetFuelMin()
@ -1051,77 +1000,7 @@ function FLIGHTGROUP:Status()
--- ---
if self:IsAirborne() and self:IsFuelGood() and self.detectionOn and self.engagedetectedOn then if self:IsAirborne() and self:IsFuelGood() and self.detectionOn and self.engagedetectedOn then
-- Target. local targetgroup, targetdist=self:_GetDetectedTarget()
local targetgroup=nil --Wrapper.Group#GROUP
local targetdist=math.huge
-- Loop over detected groups.
for _,_group in pairs(self.detectedgroups:GetSet()) do
local group=_group --Wrapper.Group#GROUP
if group and group:IsAlive() then
-- Get 3D vector of target.
local targetVec3=group:GetVec3()
-- Distance to target.
local distance=UTILS.VecDist3D(self.position, targetVec3)
if distance<=self.engagedetectedRmax and distance<targetdist then
-- Check type attribute.
local righttype=false
for _,attribute in pairs(self.engagedetectedTypes) do
local gotit=group:HasAttribute(attribute, false)
self:I(self.lid..string.format("Group %s has attribute %s = %s", group:GetName(), attribute, tostring(gotit)))
if gotit then
righttype=true
break
end
end
-- We got the right type.
if righttype then
local insideEngage=true
local insideNoEngage=false
-- Check engage zones.
if self.engagedetectedEngageZones then
insideEngage=false
for _,_zone in pairs(self.engagedetectedEngageZones.Set) do
local zone=_zone --Core.Zone#ZONE
local inzone=zone:IsVec3InZone(targetVec3)
if inzone then
insideEngage=true
break
end
end
end
-- Check no engage zones.
if self.engagedetectedNoEngageZones then
for _,_zone in pairs(self.engagedetectedNoEngageZones.Set) do
local zone=_zone --Core.Zone#ZONE
local inzone=zone:IsVec3InZone(targetVec3)
if inzone then
insideNoEngage=true
break
end
end
end
-- If inside engage but not inside no engage zones.
if insideEngage and not insideNoEngage then
targetdist=distance
targetgroup=group
end
end
end
end
end
-- If we found a group, we engage it. -- If we found a group, we engage it.
if targetgroup then if targetgroup then
@ -1137,6 +1016,12 @@ function FLIGHTGROUP:Status()
self:_CheckCargoTransport() self:_CheckCargoTransport()
---
-- Tasks & Missions
---
self:_PrintTaskAndMissionStatus()
end end
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

View File

@ -18,6 +18,7 @@
-- @field #table transportqueue Transport queue. -- @field #table transportqueue Transport queue.
-- @field #table cohorts Cohorts of this legion. -- @field #table cohorts Cohorts of this legion.
-- @field Ops.Commander#COMMANDER commander Commander of this legion. -- @field Ops.Commander#COMMANDER commander Commander of this legion.
-- @field Ops.Chief#CHIEF chief Chief of this legion.
-- @extends Functional.Warehouse#WAREHOUSE -- @extends Functional.Warehouse#WAREHOUSE
--- Be surprised! --- Be surprised!
@ -672,9 +673,9 @@ function LEGION:onafterMissionRequest(From, Event, To, Mission)
local request=self:GetRequestByID(self.queueid) local request=self:GetRequestByID(self.queueid)
if request then if request then
if self.isShip then if self:IsShip() then
--self:T(self.lid.."FF request late activated") self:T(self.lid.."Warehouse phyiscal structure is SHIP. Requestes assets will be late activated!")
--request.lateActivation=true request.lateActivation=true
end end
end end
@ -985,6 +986,8 @@ function LEGION:onafterAssetSpawned(From, Event, To, group, asset, request)
-- Check if we have a cohort or if this was some other request. -- Check if we have a cohort or if this was some other request.
if cohort then if cohort then
self:I(self.lid..string.format("Cohort asset spawned %s", asset.spawngroupname))
-- Create a flight group. -- Create a flight group.
local flightgroup=self:_CreateFlightGroup(asset) local flightgroup=self:_CreateFlightGroup(asset)
@ -1064,8 +1067,10 @@ function LEGION:onafterAssetSpawned(From, Event, To, group, asset, request)
end end
-- Add group to the detection set of the CHIEF (INTEL). -- Add group to the detection set of the CHIEF (INTEL).
if self.commander and self.commander.chief then local chief=self.chief or (self.commander and self.commander.chief or nil) --Ops.Chief#CHIEF
self.commander.chief.detectionset:AddGroup(asset.flightgroup.group) if chief then
self:I(self.lid..string.format("Adding group %s to agents of CHIEF", group:GetName()))
chief.detectionset:AddGroup(asset.flightgroup.group)
end end
elseif string.find(assignment, "Transport-") then elseif string.find(assignment, "Transport-") then

View File

@ -465,26 +465,22 @@ function NAVYGROUP:Status(From, Event, To)
-- Is group alive? -- Is group alive?
local alive=self:IsAlive() local alive=self:IsAlive()
-- -- Free path.
local freepath=0 local freepath=0
-- Check if group is exists and is active.
if alive then if alive then
---
-- Detection
---
-- Check if group has detected any units.
if self.detectionOn then
self:_CheckDetectedUnits()
end
-- Update last known position, orientation, velocity. -- Update last known position, orientation, velocity.
self:_UpdatePosition() self:_UpdatePosition()
-- Check if group has detected any units.
self:_CheckDetectedUnits()
-- Check if group started or stopped turning. -- Check if group started or stopped turning.
self:_CheckTurning() self:_CheckTurning()
-- Distance to next Waypoint.
local disttoWP=math.min(self:GetDistanceToWaypoint(), UTILS.NMToMeters(10)) local disttoWP=math.min(self:GetDistanceToWaypoint(), UTILS.NMToMeters(10))
freepath=disttoWP freepath=disttoWP
@ -527,7 +523,8 @@ function NAVYGROUP:Status(From, Event, To)
end end
end end
-- Group exists but can also be inactive.
if alive~=nil then if alive~=nil then
if self.verbose>=1 then if self.verbose>=1 then
@ -702,7 +699,7 @@ function NAVYGROUP:onafterSpawned(From, Event, To)
-- Update route. -- Update route.
if #self.waypoints>1 then if #self.waypoints>1 then
self:Cruise() self:__Cruise(-0.1)
else else
self:FullStop() self:FullStop()
end end

View File

@ -85,6 +85,12 @@
-- --
-- @field Core.Astar#ASTAR Astar path finding. -- @field Core.Astar#ASTAR Astar path finding.
-- @field #boolean ispathfinding If true, group is on pathfinding route. -- @field #boolean ispathfinding If true, group is on pathfinding route.
--
-- @field #boolean engagedetectedOn If `true`, auto engage detected targets.
-- @field #number engagedetectedRmax Max range in NM. Only detected targets within this radius from the group will be engaged. Default is 25 NM.
-- @field #table engagedetectedTypes Types of target attributes that will be engaged. See [DCS enum attributes](https://wiki.hoggitworld.com/view/DCS_enum_attributes). Default "All".
-- @field Core.Set#SET_ZONE engagedetectedEngageZones Set of zones in which targets are engaged. Default is anywhere.
-- @field Core.Set#SET_ZONE engagedetectedNoEngageZones Set of zones in which targets are *not* engaged. Default is nowhere.
-- --
-- @field #OPSGROUP.Radio radio Current radio settings. -- @field #OPSGROUP.Radio radio Current radio settings.
-- @field #OPSGROUP.Radio radioDefault Default radio settings. -- @field #OPSGROUP.Radio radioDefault Default radio settings.
@ -607,8 +613,6 @@ function OPSGROUP:New(group)
self:AddTransition("*", "InUtero", "InUtero") -- Deactivated group goes back to mummy. self:AddTransition("*", "InUtero", "InUtero") -- Deactivated group goes back to mummy.
self:AddTransition("*", "Stop", "Stopped") -- Stop FSM. self:AddTransition("*", "Stop", "Stopped") -- Stop FSM.
--self:AddTransition("*", "Status", "*") -- Status update.
self:AddTransition("*", "Destroyed", "*") -- The whole group is dead. self:AddTransition("*", "Destroyed", "*") -- The whole group is dead.
self:AddTransition("*", "Damaged", "*") -- Someone in the group took damage. self:AddTransition("*", "Damaged", "*") -- Someone in the group took damage.
@ -929,6 +933,7 @@ end
-- @param #boolean Switch If `true`, detection is on. If `false` or `nil`, detection is off. Default is off. -- @param #boolean Switch If `true`, detection is on. If `false` or `nil`, detection is off. Default is off.
-- @return #OPSGROUP self -- @return #OPSGROUP self
function OPSGROUP:SetDetection(Switch) function OPSGROUP:SetDetection(Switch)
self:T(self.lid..string.format("Detection is %s", tostring(Switch)))
self.detectionOn=Switch self.detectionOn=Switch
return self return self
end end
@ -1112,6 +1117,59 @@ function OPSGROUP:GetHighestThreat()
return threat, levelmax return threat, levelmax
end end
--- Enable to automatically engage detected targets.
-- @param #OPSGROUP self
-- @param #number RangeMax Max range in NM. Only detected targets within this radius from the group will be engaged. Default is 25 NM.
-- @param #table TargetTypes Types of target attributes that will be engaged. See [DCS enum attributes](https://wiki.hoggitworld.com/view/DCS_enum_attributes). Default "All".
-- @param Core.Set#SET_ZONE EngageZoneSet Set of zones in which targets are engaged. Default is anywhere.
-- @param Core.Set#SET_ZONE NoEngageZoneSet Set of zones in which targets are *not* engaged. Default is nowhere.
-- @return #OPSGROUP self
function OPSGROUP:SetEngageDetectedOn(RangeMax, TargetTypes, EngageZoneSet, NoEngageZoneSet)
-- Ensure table.
if TargetTypes then
if type(TargetTypes)~="table" then
TargetTypes={TargetTypes}
end
else
TargetTypes={"All"}
end
-- Ensure SET_ZONE if ZONE is provided.
if EngageZoneSet and EngageZoneSet:IsInstanceOf("ZONE_BASE") then
local zoneset=SET_ZONE:New():AddZone(EngageZoneSet)
EngageZoneSet=zoneset
end
if NoEngageZoneSet and NoEngageZoneSet:IsInstanceOf("ZONE_BASE") then
local zoneset=SET_ZONE:New():AddZone(NoEngageZoneSet)
NoEngageZoneSet=zoneset
end
-- Set parameters.
self.engagedetectedOn=true
self.engagedetectedRmax=UTILS.NMToMeters(RangeMax or 25)
self.engagedetectedTypes=TargetTypes
self.engagedetectedEngageZones=EngageZoneSet
self.engagedetectedNoEngageZones=NoEngageZoneSet
-- Debug info.
self:T(self.lid..string.format("Engage detected ON: Rmax=%d NM", UTILS.MetersToNM(self.engagedetectedRmax)))
-- Ensure detection is ON or it does not make any sense.
self:SetDetection(true)
return self
end
--- Disable to automatically engage detected targets.
-- @param #OPSGROUP self
-- @return #OPSGROUP self
function OPSGROUP:SetEngageDetectedOff()
self:T(self.lid..string.format("Engage detected OFF"))
self.engagedetectedOn=false
return self
end
--- Check if an element of the group has line of sight to a coordinate. --- Check if an element of the group has line of sight to a coordinate.
-- @param #OPSGROUP self -- @param #OPSGROUP self
-- @param Core.Point#COORDINATE Coordinate The position to which we check the LoS. -- @param Core.Point#COORDINATE Coordinate The position to which we check the LoS.
@ -2042,7 +2100,7 @@ function OPSGROUP:HasPassedFinalWaypoint()
return self.passedfinalwp return self.passedfinalwp
end end
--- Check if the group is currently rearming. --- Check if the group is currently rearming or on its way to the rearming place.
-- @param #OPSGROUP self -- @param #OPSGROUP self
-- @return #boolean If true, group is rearming. -- @return #boolean If true, group is rearming.
function OPSGROUP:IsRearming() function OPSGROUP:IsRearming()
@ -2050,6 +2108,42 @@ function OPSGROUP:IsRearming()
return rearming return rearming
end end
--- Check if the group is completely out of ammo.
-- @param #OPSGROUP self
-- @return #boolean If `true`, group is out-of-ammo.
function OPSGROUP:IsOutOfAmmo()
return self.outofAmmo
end
--- Check if the group is out of bombs.
-- @param #OPSGROUP self
-- @return #boolean If `true`, group is out of bombs.
function OPSGROUP:IsOutOfBombs()
return self.outofBombs
end
--- Check if the group is out of guns.
-- @param #OPSGROUP self
-- @return #boolean If `true`, group is out of guns.
function OPSGROUP:IsOutOfGuns()
return self.outofGuns
end
--- Check if the group is out of missiles.
-- @param #OPSGROUP self
-- @return #boolean If `true`, group is out of missiles.
function OPSGROUP:IsOutOfMissiles()
return self.outofMissiles
end
--- Check if the group is out of torpedos.
-- @param #OPSGROUP self
-- @return #boolean If `true`, group is out of torpedos.
function OPSGROUP:IsOutOfTorpedos()
return self.outofTorpedos
end
--- Check if the group has currently switched a LASER on. --- Check if the group has currently switched a LASER on.
-- @param #OPSGROUP self -- @param #OPSGROUP self
-- @return #boolean If true, LASER of the group is on. -- @return #boolean If true, LASER of the group is on.
@ -2057,14 +2151,23 @@ function OPSGROUP:IsLasing()
return self.spot.On return self.spot.On
end end
--- Check if the group is currently retreating. --- Check if the group is currently retreating or retreated.
-- @param #OPSGROUP self -- @param #OPSGROUP self
-- @return #boolean If true, group is retreating. -- @return #boolean If true, group is retreating or retreated.
function OPSGROUP:IsRetreating() function OPSGROUP:IsRetreating()
local is=self:is("Retreating") local is=self:is("Retreating") or self:is("Retreated")
return is return is
end end
--- Check if the group is retreated (has reached its retreat zone).
-- @param #OPSGROUP self
-- @return #boolean If true, group is retreated.
function OPSGROUP:IsRetreated()
local is=self:is("Retreated")
return is
end
--- Check if the group is currently returning to a zone. --- Check if the group is currently returning to a zone.
-- @param #OPSGROUP self -- @param #OPSGROUP self
-- @return #boolean If true, group is returning. -- @return #boolean If true, group is returning.
@ -2812,15 +2915,6 @@ function OPSGROUP:SetTask(DCSTask)
if self:IsAlive() then if self:IsAlive() then
if self.taskcurrent>0 then
-- TODO: Why the hell did I do this? It breaks scheduled tasks. I comment it out for now to see where it fails.
--local task=self:GetTaskCurrent()
--self:RemoveTask(task)
--self.taskcurrent=0
end
-- Inject enroute tasks. -- Inject enroute tasks.
if self.taskenroute and #self.taskenroute>0 then if self.taskenroute and #self.taskenroute>0 then
if tostring(DCSTask.id)=="ComboTask" then if tostring(DCSTask.id)=="ComboTask" then
@ -2836,7 +2930,6 @@ function OPSGROUP:SetTask(DCSTask)
end end
-- Set task. -- Set task.
--self.group:SetTask(DCSTask)
self.controller:setTask(DCSTask) self.controller:setTask(DCSTask)
-- Debug info. -- Debug info.
@ -2861,7 +2954,6 @@ function OPSGROUP:PushTask(DCSTask)
if self:IsAlive() then if self:IsAlive() then
-- Push task. -- Push task.
--self.group:PushTask(DCSTask)
self.controller:pushTask(DCSTask) self.controller:pushTask(DCSTask)
-- Debug info. -- Debug info.
@ -3315,7 +3407,6 @@ function OPSGROUP:onafterTaskExecute(From, Event, To, Task)
-- Insert into task queue. Not sure any more, why I added this. But probably if a task is just executed without having been put into the queue. -- Insert into task queue. Not sure any more, why I added this. But probably if a task is just executed without having been put into the queue.
if self:GetTaskCurrent()==nil then if self:GetTaskCurrent()==nil then
--env.info("FF adding current task to queue")
table.insert(self.taskqueue, Task) table.insert(self.taskqueue, Task)
end end
@ -3355,8 +3446,15 @@ function OPSGROUP:onafterTaskExecute(From, Event, To, Task)
-- Parameters. -- Parameters.
local zone=Task.dcstask.params.zone --Core.Zone#ZONE local zone=Task.dcstask.params.zone --Core.Zone#ZONE
local surfacetypes=nil
if self:IsArmygroup() then
surfacetypes={land.SurfaceType.LAND, land.SurfaceType.ROAD}
elseif self:IsNavygroup() then
surfacetypes={land.SurfaceType.WATER, land.SurfaceType.SHALLOW_WATER}
end
-- Random coordinate in zone. -- Random coordinate in zone.
local Coordinate=zone:GetRandomCoordinate() local Coordinate=zone:GetRandomCoordinate(nil, nil, surfacetypes)
--Coordinate:MarkToAll("Random Patrol Zone Coordinate") --Coordinate:MarkToAll("Random Patrol Zone Coordinate")
@ -3968,6 +4066,11 @@ function OPSGROUP:onafterMissionExecute(From, Event, To, Mission)
-- Set mission status to EXECUTING. -- Set mission status to EXECUTING.
Mission:Executing() Mission:Executing()
-- Set auto engage detected targets.
if Mission.engagedetectedOn then
self:SetEngageDetectedOn(UTILS.MetersToNM(Mission.engagedetectedRmax), Mission.engagedetectedTypes, Mission.engagedetectedEngageZones, Mission.engagedetectedNoEngageZones)
end
end end
@ -4124,6 +4227,11 @@ function OPSGROUP:onafterMissionDone(From, Event, To, Mission)
Mission.patroldata.noccupied=Mission.patroldata.noccupied-1 Mission.patroldata.noccupied=Mission.patroldata.noccupied-1
AIRWING.UpdatePatrolPointMarker(Mission.patroldata) AIRWING.UpdatePatrolPointMarker(Mission.patroldata)
end end
-- Switch auto engage detected off. This IGNORES that engage detected had been activated for the group!
if Mission.engagedetectedOn then
self:SetEngageDetectedOff()
end
-- ROE to default. -- ROE to default.
if Mission.optionROE then if Mission.optionROE then
@ -4201,17 +4309,20 @@ function OPSGROUP:RouteToMission(mission, delay)
-- Catch dead or stopped groups. -- Catch dead or stopped groups.
if self:IsDead() or self:IsStopped() then if self:IsDead() or self:IsStopped() then
self:T(self.lid..string.format("Route To Mission: I am DEAD or STOPPED! Ooops..."))
return return
end end
-- OPSTRANSPORT: Just add the ops transport to the queue. -- OPSTRANSPORT: Just add the ops transport to the queue.
if mission.type==AUFTRAG.Type.OPSTRANSPORT then if mission.type==AUFTRAG.Type.OPSTRANSPORT then
self:T(self.lid..string.format("Route To Mission: I am OPSTRANSPORT! Add transport and return..."))
self:AddOpsTransport(mission.opstransport) self:AddOpsTransport(mission.opstransport)
return return
end end
-- ALERT 5: Just set the mission to executing. -- ALERT5: Just set the mission to executing.
if mission.type==AUFTRAG.Type.ALERT5 then if mission.type==AUFTRAG.Type.ALERT5 then
self:T(self.lid..string.format("Route To Mission: I am ALERT5! Go right to MissionExecute()..."))
self:MissionExecute(mission) self:MissionExecute(mission)
return return
end end
@ -4219,15 +4330,28 @@ function OPSGROUP:RouteToMission(mission, delay)
-- ID of current waypoint. -- ID of current waypoint.
local uid=self:GetWaypointCurrent().uid local uid=self:GetWaypointCurrent().uid
-- Random radius. -- Ingress waypoint coordinate where the mission is executed.
local waypointcoord=nil --Core.Point#COORDINATE
-- Random radius of 1000 meters.
local randomradius=1000 local randomradius=1000
-- Surface types.
local surfacetypes=nil
if self:IsArmygroup() then
surfacetypes={land.SurfaceType.LAND, land.SurfaceType.ROAD}
elseif self:IsNavygroup() then
surfacetypes={land.SurfaceType.WATER, land.SurfaceType.SHALLOW_WATER}
end
-- Get ingress waypoint.
if mission.type==AUFTRAG.Type.PATROLZONE then if mission.type==AUFTRAG.Type.PATROLZONE then
randomradius=nil local zone=mission.engageTarget:GetObject() --Core.Zone#ZONE
waypointcoord=zone:GetRandomCoordinate(nil , nil, surfacetypes)
else
waypointcoord=mission:GetMissionWaypointCoord(self.group, randomradius, surfacetypes)
end end
-- Get coordinate where the mission is executed.
local waypointcoord=mission:GetMissionWaypointCoord(self.group, randomradius)
-- Add enroute tasks. -- Add enroute tasks.
for _,task in pairs(mission.enrouteTasks) do for _,task in pairs(mission.enrouteTasks) do
self:AddTaskEnroute(task) self:AddTaskEnroute(task)
@ -4532,9 +4656,17 @@ function OPSGROUP:onafterPassingWaypoint(From, Event, To, Waypoint)
-- Zone object. -- Zone object.
local zone=task.dcstask.params.zone --Core.Zone#ZONE local zone=task.dcstask.params.zone --Core.Zone#ZONE
-- Random coordinate in zone. -- Surface types.
local Coordinate=zone:GetRandomCoordinate() local surfacetypes=nil
if self:IsArmygroup() then
surfacetypes={land.SurfaceType.LAND, land.SurfaceType.ROAD}
elseif self:IsNavygroup() then
surfacetypes={land.SurfaceType.WATER, land.SurfaceType.SHALLOW_WATER}
end
-- Random coordinate in zone.
local Coordinate=zone:GetRandomCoordinate(nil, nil, surfacetypes)
-- Speed and altitude. -- Speed and altitude.
local Speed=UTILS.KmphToKnots(task.dcstask.params.speed or self.speedCruise) local Speed=UTILS.KmphToKnots(task.dcstask.params.speed or self.speedCruise)
local Altitude=task.dcstask.params.altitude and UTILS.MetersToFeet(task.dcstask.params.altitude) or nil local Altitude=task.dcstask.params.altitude and UTILS.MetersToFeet(task.dcstask.params.altitude) or nil
@ -7820,7 +7952,7 @@ end
-- @param #OPSGROUP self -- @param #OPSGROUP self
function OPSGROUP:_CheckDetectedUnits() function OPSGROUP:_CheckDetectedUnits()
if self.group and not self:IsDead() then if self.detectionOn and self.group and not self:IsDead() then
-- Get detected DCS units. -- Get detected DCS units.
local detectedtargets=self.group:GetDetectedTargets() local detectedtargets=self.group:GetDetectedTargets()
@ -10636,6 +10768,87 @@ function OPSGROUP:ClearWaypoints(IndexMin, IndexMax)
--self.waypoints={} --self.waypoints={}
end end
--- Get target group.
-- @param #OPSGROUP self
-- @return Wrapper.Group#GROUP Detected target group.
-- @return #number Distance to target.
function OPSGROUP:_GetDetectedTarget()
-- Target.
local targetgroup=nil --Wrapper.Group#GROUP
local targetdist=math.huge
-- Loop over detected groups.
for _,_group in pairs(self.detectedgroups:GetSet()) do
local group=_group --Wrapper.Group#GROUP
if group and group:IsAlive() then
-- Get 3D vector of target.
local targetVec3=group:GetVec3()
-- Distance to target.
local distance=UTILS.VecDist3D(self.position, targetVec3)
if distance<=self.engagedetectedRmax and distance<targetdist then
-- Check type attribute.
local righttype=false
for _,attribute in pairs(self.engagedetectedTypes) do
local gotit=group:HasAttribute(attribute, false)
self:I(self.lid..string.format("Group %s has attribute %s = %s", group:GetName(), attribute, tostring(gotit)))
if gotit then
righttype=true
break
end
end
-- We got the right type.
if righttype then
local insideEngage=true
local insideNoEngage=false
-- Check engage zones.
if self.engagedetectedEngageZones then
insideEngage=false
for _,_zone in pairs(self.engagedetectedEngageZones.Set) do
local zone=_zone --Core.Zone#ZONE
local inzone=zone:IsVec3InZone(targetVec3)
if inzone then
insideEngage=true
break
end
end
end
-- Check no engage zones.
if self.engagedetectedNoEngageZones then
for _,_zone in pairs(self.engagedetectedNoEngageZones.Set) do
local zone=_zone --Core.Zone#ZONE
local inzone=zone:IsVec3InZone(targetVec3)
if inzone then
insideNoEngage=true
break
end
end
end
-- If inside engage but not inside no engage zones.
if insideEngage and not insideNoEngage then
targetdist=distance
targetgroup=group
end
end
end
end
end
return targetgroup, targetdist
end
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

View File

@ -16,6 +16,7 @@
--- OPSZONE class. --- OPSZONE class.
-- @type OPSZONE -- @type OPSZONE
-- @field #string ClassName Name of the class. -- @field #string ClassName Name of the class.
-- @field #string lid DCS log ID string.
-- @field #number verbose Verbosity of output. -- @field #number verbose Verbosity of output.
-- @field Core.Zone#ZONE zone The zone. -- @field Core.Zone#ZONE zone The zone.
-- @field Wrapper.Airbase#AIRBASE airbase The airbase that is monitored. -- @field Wrapper.Airbase#AIRBASE airbase The airbase that is monitored.
@ -34,6 +35,7 @@
-- @field #number dTCapture Time interval in seconds until a zone is captured. -- @field #number dTCapture Time interval in seconds until a zone is captured.
-- @field #boolean neutralCanCapture Neutral units can capture. Default `false`. -- @field #boolean neutralCanCapture Neutral units can capture. Default `false`.
-- @field #boolean drawZone If `true`, draw the zone on the F10 map. -- @field #boolean drawZone If `true`, draw the zone on the F10 map.
-- @field #boolean markZone If `true`, mark the zone on the F10 map.
-- @extends Core.Fsm#FSM -- @extends Core.Fsm#FSM
--- Be surprised! --- Be surprised!
@ -132,8 +134,12 @@ function OPSZONE:New(Zone, CoalitionOwner)
self:AddTransition("*", "Stop", "Stopped") -- Stop FSM. self:AddTransition("*", "Stop", "Stopped") -- Stop FSM.
self:AddTransition("*", "Captured", "Guarded") -- Zone was captured. self:AddTransition("*", "Captured", "Guarded") -- Zone was captured.
self:AddTransition("Empty", "Guarded", "Guarded") -- Owning coalition left the zone and returned.
self:AddTransition("*", "Empty", "Empty") -- No red or blue units inside the zone. self:AddTransition("*", "Empty", "Empty") -- No red or blue units inside the zone.
self:AddTransition("*", "Attacked", "Attacked") -- A guarded zone is under attack. self:AddTransition("*", "Attacked", "Attacked") -- A guarded zone is under attack.
self:AddTransition("*", "Defeated", "Guarded") -- The owning coalition defeated an attack. self:AddTransition("*", "Defeated", "Guarded") -- The owning coalition defeated an attack.
@ -180,6 +186,23 @@ function OPSZONE:New(Zone, CoalitionOwner)
-- @param #number Coalition Coalition side that captured the zone. -- @param #number Coalition Coalition side that captured the zone.
--- Triggers the FSM event "Guarded".
-- @function [parent=#OPSZONE] Guarded
-- @param #OPSZONE self
--- Triggers the FSM event "Guarded" after a delay.
-- @function [parent=#OPSZONE] __Guarded
-- @param #OPSZONE self
-- @param #number delay Delay in seconds.
--- On after "Guarded" event.
-- @function [parent=#OPSZONE] OnAfterGuarded
-- @param #OPSZONE self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
--- Triggers the FSM event "Empty". --- Triggers the FSM event "Empty".
-- @function [parent=#OPSZONE] Empty -- @function [parent=#OPSZONE] Empty
-- @param #OPSZONE self -- @param #OPSZONE self
@ -403,6 +426,18 @@ function OPSZONE:onafterStart(From, Event, To)
-- Reinit the timer. -- Reinit the timer.
self.timerStatus=self.timerStatus or TIMER:New(OPSZONE.Status, self) self.timerStatus=self.timerStatus or TIMER:New(OPSZONE.Status, self)
-- Perform initial scan.
self:Scan()
if self.Nblu==0 and self.Nred==0 then
elseif self.Nblu>0 and self.Nred>0 then
elseif self.Nblu>0 then
elseif self.Nred>0 then
end
-- Status update. -- Status update.
self.timerStatus:Start(1, 60) self.timerStatus:Start(1, 60)
@ -528,12 +563,12 @@ function OPSZONE:onenterGuarded(From, Event, To)
local color=self:_GetZoneColor() local color=self:_GetZoneColor()
self.zone:DrawZone(nil, color, 1.0, color, 0.7) self.zone:DrawZone(nil, color, 1.0, color, 0.5)
end end
end end
--- On enter "Guarded" state. --- On enter "Attacked" state.
-- @param #OPSZONE self -- @param #OPSZONE self
-- @param #string From From state. -- @param #string From From state.
-- @param #string Event Event. -- @param #string Event Event.
@ -549,9 +584,10 @@ function OPSZONE:onenterAttacked(From, Event, To)
if self.drawZone then if self.drawZone then
self.zone:UndrawZone() self.zone:UndrawZone()
local color={1,1,1}
self.zone:DrawZone(nil, color, 1.0, color, 0.9) local color={1,204/255,204/255}
self.zone:DrawZone(nil, color, 1.0, color, 0.5)
end end
end end
@ -580,7 +616,7 @@ end
-- Scan Functions -- Scan Functions
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Add a platoon to the brigade. --- Scan zone.
-- @param #OPSZONE self -- @param #OPSZONE self
-- @return #OPSZONE self -- @return #OPSZONE self
function OPSZONE:Scan() function OPSZONE:Scan()
@ -720,7 +756,20 @@ function OPSZONE:Scan()
self.Nred=Nred self.Nred=Nred
self.Nblu=Nblu self.Nblu=Nblu
self.Nnut=Nnut self.Nnut=Nnut
return self
end
--- Evaluate zone.
-- @param #OPSZONE self
-- @return #OPSZONE self
function OPSZONE:EvaluateZone()
-- Set values.
local Nred=self.Nred
local Nblu=self.Nblu
local Nnut=self.Nnut
if self:IsRed() then if self:IsRed() then
--- ---
@ -746,7 +795,7 @@ function OPSZONE:Scan()
else else
-- Still red units in red zone. -- Red units in red zone.
if Nblu>0 then if Nblu>0 then
@ -758,6 +807,9 @@ function OPSZONE:Scan()
if self:IsAttacked() and self:IsContested() then if self:IsAttacked() and self:IsContested() then
self:Defeated(coalition.side.BLUE) self:Defeated(coalition.side.BLUE)
elseif self:IsEmpty() then
-- Red units left zone and returned (or from initial Empty state).
self:Guarded()
end end
end end
@ -810,6 +862,9 @@ function OPSZONE:Scan()
if self:IsAttacked() and self:IsContested() then if self:IsAttacked() and self:IsContested() then
-- Blue defeated read attack. -- Blue defeated read attack.
self:Defeated(coalition.side.RED) self:Defeated(coalition.side.RED)
elseif self:IsEmpty() then
-- Blue units left zone and returned (or from initial Empty state).
self:Guarded()
end end
end end
@ -858,7 +913,7 @@ function OPSZONE:Scan()
self:E(self.lid.."ERROR!") self:E(self.lid.."ERROR!")
end end
return self
end end
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@ -923,11 +978,11 @@ function OPSZONE:_GetZoneColor()
local color={0,0,0} local color={0,0,0}
if self.ownerCurrent==coalition.side.NEUTRAL then if self.ownerCurrent==coalition.side.NEUTRAL then
color={0, 1, 0} color={1, 1, 1}
elseif self.ownerCurrent==coalition.side.BLUE then elseif self.ownerCurrent==coalition.side.BLUE then
color={1, 0, 0}
elseif self.ownerCurrent==coalition.side.RED then
color={0, 0, 1} color={0, 0, 1}
elseif self.ownerCurrent==coalition.side.RED then
color={1, 0, 0}
else else
end end