- 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 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 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 #boolean respawnafterdestroyed If true, warehouse is respawned after it was destroyed. Assets are kept.
-- @field #number respawndelay Delay before respawn in seconds.
@ -1590,7 +1591,8 @@ WAREHOUSE = {
autosavepath = nil,
autosavefile = nil,
saveparking = false,
isunit = false,
isUnit = false,
isShip = false,
lowfuelthresh = 0.15,
respawnafterdestroyed=false,
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 #number transportattribute Attribute of transport assets of type @{#WAREHOUSE.Attribute}.
-- @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.
-- @type WAREHOUSE.Pendingitem
@ -1857,40 +1860,46 @@ WAREHOUSE.version="1.0.2"
-- @return #WAREHOUSE self
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.
if type(warehouse)=="string" then
local warehousename=warehouse
warehouse=UNIT:FindByName(warehousename)
if warehouse==nil then
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
-- Nil check.
if warehouse==nil then
BASE:E("ERROR: Warehouse does not exist!")
env.error("ERROR: Warehouse does not exist!")
return nil
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.
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.
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.
self.warehouse=warehouse
@ -3324,7 +3333,7 @@ end
--- Check if runway is operational.
-- @param #WAREHOUSE self
-- @return #boolean If true, runway is operational.
-- @return #boolean If `true`, runway is operational.
function WAREHOUSE:IsRunwayOperational()
if self.airbase then
if self.runwaydestroyed then
@ -3360,6 +3369,27 @@ function WAREHOUSE:GetRunwayRepairtime()
return 0
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
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@ -4399,7 +4429,7 @@ function WAREHOUSE:onbeforeRequest(From, Event, To, Request)
-- 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.
if not (self.isunit or Request.warehouse.isunit) then
if not (self.isUnit or Request.warehouse.isUnit) then
self:_DeleteQueueItem(Request, self.queue)
end
@ -5829,8 +5859,6 @@ function WAREHOUSE:_SpawnAssetGroundNaval(alias, asset, request, spawnzone, late
-- Late activation.
template.lateActivation=lateactivated
--env.info("FF lateActivation="..tostring(template.lateActivation))
template.route.points[1].x = coord.x
template.route.points[1].y = coord.z
@ -6108,18 +6136,10 @@ function WAREHOUSE:_RouteGround(group, request)
end
for n,wp in ipairs(Waypoints) do
--env.info(n)
local tf=self:_SimpleTaskFunctionWP("warehouse:_PassingWaypoint",group, n, #Waypoints)
group:SetTaskWaypoint(wp, tf)
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.
group:Route(Waypoints, 1)
@ -7676,7 +7696,7 @@ function WAREHOUSE:_SimpleTaskFunction(Function, group)
local DCSScript = {}
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.
else
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 = {}
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.
else
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: 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: 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: Build mission queue.
-- DONE: Find way to start missions.

View File

@ -55,10 +55,13 @@ ARMYGROUP = {
engage = {},
}
--- Target
--- Engage Target.
-- @type ARMYGROUP.Target
-- @field Ops.Target#TARGET Target 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.
-- @field #string version
@ -332,7 +335,11 @@ end
function ARMYGROUP:IsCombatReady()
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
end
@ -356,27 +363,21 @@ function ARMYGROUP:Status()
if alive then
---
-- Detection
---
-- Update position etc.
self:_UpdatePosition()
-- Check if group has detected any units.
if self.detectionOn then
self:_CheckDetectedUnits()
end
self:_CheckDetectedUnits()
-- Check ammo status.
self:_CheckAmmoStatus()
-- Update position etc.
self:_UpdatePosition()
-- Check damage of elements and group.
self:_CheckDamage()
-- Check if group got stuck.
self:_CheckStuck()
-- Check damage of elements and group.
self:_CheckDamage()
-- Update engagement.
if self:IsEngaging() then
self:_UpdateEngageTarget()
@ -463,6 +464,21 @@ function ARMYGROUP:Status()
self:I(self.lid..text)
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
---
-- Cargo
@ -580,7 +596,7 @@ function ARMYGROUP:onafterSpawned(From, Event, To)
-- Update route.
if #self.waypoints>1 then
self:Cruise(nil, self.option.Formation)
self:__Cruise(-0.1, nil, self.option.Formation)
else
self:FullStop()
end
@ -1003,11 +1019,13 @@ function ARMYGROUP:onafterEngageTarget(From, Event, To, Target)
-- Target coordinate.
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.
self:SwitchAlarmstate(ENUMS.AlarmState.Auto)
self:SwitchROE(ENUMS.ROE.WeaponFree)
self:SwitchROE(ENUMS.ROE.OpenFire)
-- ID of current waypoint.
local uid=self:GetWaypointCurrent().uid
@ -1026,16 +1044,18 @@ function ARMYGROUP:_UpdateEngageTarget()
if self.engage.Target and self.engage.Target:IsAlive() then
--env.info("FF Update Engage Target "..self.engage.Target:GetName())
-- Get current position vector.
local vec3=self.engage.Target:GetVec3()
local vec3=self.engage.Target:GetCoordinate():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
--env.info("FF Update Engage Target Moved "..self.engage.Target:GetName())
-- Update new position.
self.engage.Coordinate:UpdateFromVec3(vec3)
-- ID of current waypoint.
@ -1053,7 +1073,10 @@ function ARMYGROUP:_UpdateEngageTarget()
end
else
-- Target not alive any more == Disengage.
self:Disengage()
end
end
@ -1066,7 +1089,16 @@ end
function ARMYGROUP:onafterDisengage(From, Event, To)
self:T(self.lid.."Disengage Target")
-- TODO: Reset ROE and alarm state.
-- Restore previous ROE and alarm state.
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

View File

@ -152,16 +152,21 @@
--
-- ===
--
-- ![Banner Image](..\Presentations\OPS\Auftrag\_Main.png)
--
-- # The AUFTRAG Concept
--
-- 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.
-- 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.
--
-- 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
--
@ -169,7 +174,7 @@
--
-- ## 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
--
@ -225,7 +230,7 @@
--
-- ## RECON
--
-- Not implemented yet.
-- An reconnaissance mission can be created with the @{#AUFTRAG.NewRECON}() function.
--
-- ## RESCUE HELO
--
@ -260,40 +265,43 @@
--
-- # 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
--
-- ### 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
--
-- 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
--
-- 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
--
-- Assigning an AUFTRAG to acommander is done via the @{Ops.Commander#COMMANDER.AddMission} function. See COMMADER 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.
--
-- Assigning an AUFTRAG to acommander is done via the @{Ops.Commander#COMMANDER.AddMission} function. See COMMANDER docs for details.
--
--
-- # Events
--
-- The AUFTRAG class creates many useful (FSM) events, which can be used in the mission designers script.
--
-- TODO
--
--
-- # Examples
--
-- TODO
--
--
-- @field #AUFTRAG
AUFTRAG = {
@ -599,8 +607,8 @@ function AUFTRAG:New(Type)
self:SetStartState(self.status)
-- PLANNED --> (QUEUED) --> (REQUESTED) --> SCHEDULED --> STARTED --> EXECUTING --> DONE
self:AddTransition("*", "Planned", AUFTRAG.Status.PLANNED) -- Mission is in planning stage.
self:AddTransition(AUFTRAG.Status.PLANNED, "Queued", AUFTRAG.Status.QUEUED) -- Mission is in queue of an AIRWING.
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 a LEGION.
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.
@ -625,6 +633,171 @@ function AUFTRAG:New(Type)
self:AddTransition("*", "GroupDead", "*")
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.
self:__Status(-1)
@ -1921,6 +2094,44 @@ function AUFTRAG:SetEngageAltitude(Altitude)
return self
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.
-- @param #AUFTRAG self
-- @param #string Altitude Altitude in feet.
@ -4039,8 +4250,9 @@ end
-- @param #AUFTRAG self
-- @param Wrapper.Group#GROUP group Group.
-- @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.
function AUFTRAG:GetMissionWaypointCoord(group, randomradius)
function AUFTRAG:GetMissionWaypointCoord(group, randomradius, surfacetypes)
-- Check if a coord has been explicitly set.
if self.missionWaypointCoord then
@ -4057,7 +4269,7 @@ function AUFTRAG:GetMissionWaypointCoord(group, randomradius)
-- Add some randomization.
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
-- Set altitude of mission waypoint.

View File

@ -413,6 +413,47 @@ function CHIEF:GetCommander()
return self.commander
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.
-- @param #CHIEF self
-- @param Ops.Auftrag#AUFTRAG Mission Mission to be added.
@ -1181,9 +1222,7 @@ function CHIEF:CheckOpsZoneQueue()
env.info(string.format("Zone %s is owned by coalition %d", opszone.zone:GetName(), ownercoalition))
-- 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
@ -1494,8 +1533,6 @@ end
-- @param #table Categories Group categories of the assets.
-- @param #table Attributes Generalized group attributes.
-- @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)
-- Cohorts.
@ -1520,7 +1557,7 @@ function CHIEF:RecruitAssetsForZone(OpsZone, MissionType, NassetsMin, NassetsMax
-- Target position.
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)
if recruitedInf then
@ -1536,9 +1573,9 @@ function CHIEF:RecruitAssetsForZone(OpsZone, MissionType, NassetsMin, NassetsMax
end
end
-- Recruite assets.
-- Recruite carrier assets. This need to be ground or helicopters to deploy at a zone.
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
if recruitedTrans then
@ -1550,7 +1587,15 @@ function CHIEF:RecruitAssetsForZone(OpsZone, MissionType, NassetsMin, NassetsMax
-- Add cargo assets to transport.
for _,_legion in pairs(legionsInf) do
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
local asset=_asset --Functional.Warehouse#WAREHOUSE.Assetitem
if asset.legion.alias==legion.alias then
@ -1579,6 +1624,7 @@ function CHIEF:RecruitAssetsForZone(OpsZone, MissionType, NassetsMin, NassetsMax
-- Create Patrol zone mission.
local mission=AUFTRAG:NewPATROLZONE(OpsZone.zone)
mission:SetEngageDetected()
for _,asset in pairs(assetsInf) do
mission:AddAsset(asset)
@ -1593,11 +1639,13 @@ function CHIEF:RecruitAssetsForZone(OpsZone, MissionType, NassetsMin, NassetsMax
OpsZone.missionPatrol=mission
return true
else
LEGION.UnRecruitAssets(assetsInf)
return false
end
return recruited, assets, legions
return nil
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

View File

@ -20,7 +20,7 @@
-- @field #table legions Table of legions which are commanded.
-- @field #table missionqueue Mission 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
--- Be surprised!
@ -736,6 +736,7 @@ function COMMANDER:CheckMissionQueue()
for _,_legion in pairs(legions) do
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
if legion.airbase and legion:IsRunwayOperational() then
pickupzone=ZONE_AIRBASE:New(legion.airbasename, 4000)

View File

@ -501,55 +501,6 @@ function FLIGHTGROUP:SetFuelCriticalRTB(switch)
return self
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.
-- @param #FLIGHTGROUP self
@ -842,22 +793,27 @@ function FLIGHTGROUP:Status()
-- FSM state.
local fsmstate=self:GetState()
-- Is group alive?
local alive=self:IsAlive()
-- Update position.
self:_UpdatePosition()
---
-- Detection
---
-- Check if group has detected any units.
if self.detectionOn then
self:_CheckDetectedUnits()
end
self:_CheckDetectedUnits()
-- Check ammo status.
self:_CheckAmmoStatus()
-- Check damage.
self:_CheckDamage()
---
-- Parking
---
-- TODO: _CheckParking() function
-- Check if flight began to taxi (if it was parking).
if self:IsParking() then
for _,_element in pairs(self.elements) do
@ -930,11 +886,6 @@ function FLIGHTGROUP:Status()
local lp0=unit:GetLife0()
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.
local ammo=self:GetAmmoElement(element)
@ -952,7 +903,9 @@ function FLIGHTGROUP:Status()
-- 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.
local ds=self.travelds
@ -998,18 +951,14 @@ function FLIGHTGROUP:Status()
end
---
-- Tasks & Missions
---
self:_PrintTaskAndMissionStatus()
---
-- Fuel State
---
-- TODO: _CheckFuelState() function.
-- 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()
@ -1051,77 +1000,7 @@ function FLIGHTGROUP:Status()
---
if self:IsAirborne() and self:IsFuelGood() and self.detectionOn and self.engagedetectedOn then
-- 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
local targetgroup, targetdist=self:_GetDetectedTarget()
-- If we found a group, we engage it.
if targetgroup then
@ -1137,6 +1016,12 @@ function FLIGHTGROUP:Status()
self:_CheckCargoTransport()
---
-- Tasks & Missions
---
self:_PrintTaskAndMissionStatus()
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

View File

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

View File

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

View File

@ -86,6 +86,12 @@
-- @field Core.Astar#ASTAR Astar path finding.
-- @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 radioDefault Default radio settings.
-- @field Core.RadioQueue#RADIOQUEUE radioQueue Radio queue.
@ -607,8 +613,6 @@ function OPSGROUP:New(group)
self:AddTransition("*", "InUtero", "InUtero") -- Deactivated group goes back to mummy.
self:AddTransition("*", "Stop", "Stopped") -- Stop FSM.
--self:AddTransition("*", "Status", "*") -- Status update.
self:AddTransition("*", "Destroyed", "*") -- The whole group is dead.
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.
-- @return #OPSGROUP self
function OPSGROUP:SetDetection(Switch)
self:T(self.lid..string.format("Detection is %s", tostring(Switch)))
self.detectionOn=Switch
return self
end
@ -1112,6 +1117,59 @@ function OPSGROUP:GetHighestThreat()
return threat, levelmax
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.
-- @param #OPSGROUP self
-- @param Core.Point#COORDINATE Coordinate The position to which we check the LoS.
@ -2042,7 +2100,7 @@ function OPSGROUP:HasPassedFinalWaypoint()
return self.passedfinalwp
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
-- @return #boolean If true, group is rearming.
function OPSGROUP:IsRearming()
@ -2050,6 +2108,42 @@ function OPSGROUP:IsRearming()
return rearming
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.
-- @param #OPSGROUP self
-- @return #boolean If true, LASER of the group is on.
@ -2057,14 +2151,23 @@ function OPSGROUP:IsLasing()
return self.spot.On
end
--- Check if the group is currently retreating.
--- Check if the group is currently retreating or retreated.
-- @param #OPSGROUP self
-- @return #boolean If true, group is retreating.
-- @return #boolean If true, group is retreating or retreated.
function OPSGROUP:IsRetreating()
local is=self:is("Retreating")
local is=self:is("Retreating") or self:is("Retreated")
return is
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.
-- @param #OPSGROUP self
-- @return #boolean If true, group is returning.
@ -2812,15 +2915,6 @@ function OPSGROUP:SetTask(DCSTask)
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.
if self.taskenroute and #self.taskenroute>0 then
if tostring(DCSTask.id)=="ComboTask" then
@ -2836,7 +2930,6 @@ function OPSGROUP:SetTask(DCSTask)
end
-- Set task.
--self.group:SetTask(DCSTask)
self.controller:setTask(DCSTask)
-- Debug info.
@ -2861,7 +2954,6 @@ function OPSGROUP:PushTask(DCSTask)
if self:IsAlive() then
-- Push task.
--self.group:PushTask(DCSTask)
self.controller:pushTask(DCSTask)
-- 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.
if self:GetTaskCurrent()==nil then
--env.info("FF adding current task to queue")
table.insert(self.taskqueue, Task)
end
@ -3355,8 +3446,15 @@ function OPSGROUP:onafterTaskExecute(From, Event, To, Task)
-- Parameters.
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.
local Coordinate=zone:GetRandomCoordinate()
local Coordinate=zone:GetRandomCoordinate(nil, nil, surfacetypes)
--Coordinate:MarkToAll("Random Patrol Zone Coordinate")
@ -3969,6 +4067,11 @@ function OPSGROUP:onafterMissionExecute(From, Event, To, Mission)
-- Set mission status to 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
--- On after "PauseMission" event.
@ -4125,6 +4228,11 @@ function OPSGROUP:onafterMissionDone(From, Event, To, Mission)
AIRWING.UpdatePatrolPointMarker(Mission.patroldata)
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.
if Mission.optionROE then
self:SwitchROE()
@ -4201,17 +4309,20 @@ function OPSGROUP:RouteToMission(mission, delay)
-- Catch dead or stopped groups.
if self:IsDead() or self:IsStopped() then
self:T(self.lid..string.format("Route To Mission: I am DEAD or STOPPED! Ooops..."))
return
end
-- OPSTRANSPORT: Just add the ops transport to the queue.
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)
return
end
-- ALERT 5: Just set the mission to executing.
-- ALERT5: Just set the mission to executing.
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)
return
end
@ -4219,14 +4330,27 @@ function OPSGROUP:RouteToMission(mission, delay)
-- ID of current waypoint.
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
if mission.type==AUFTRAG.Type.PATROLZONE then
randomradius=nil
-- 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 coordinate where the mission is executed.
local waypointcoord=mission:GetMissionWaypointCoord(self.group, randomradius)
-- Get ingress waypoint.
if mission.type==AUFTRAG.Type.PATROLZONE then
local zone=mission.engageTarget:GetObject() --Core.Zone#ZONE
waypointcoord=zone:GetRandomCoordinate(nil , nil, surfacetypes)
else
waypointcoord=mission:GetMissionWaypointCoord(self.group, randomradius, surfacetypes)
end
-- Add enroute tasks.
for _,task in pairs(mission.enrouteTasks) do
@ -4532,8 +4656,16 @@ function OPSGROUP:onafterPassingWaypoint(From, Event, To, Waypoint)
-- Zone object.
local zone=task.dcstask.params.zone --Core.Zone#ZONE
-- 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
-- Random coordinate in zone.
local Coordinate=zone:GetRandomCoordinate()
local Coordinate=zone:GetRandomCoordinate(nil, nil, surfacetypes)
-- Speed and altitude.
local Speed=UTILS.KmphToKnots(task.dcstask.params.speed or self.speedCruise)
@ -7820,7 +7952,7 @@ end
-- @param #OPSGROUP self
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.
local detectedtargets=self.group:GetDetectedTargets()
@ -10636,6 +10768,87 @@ function OPSGROUP:ClearWaypoints(IndexMin, IndexMax)
--self.waypoints={}
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.
-- @type OPSZONE
-- @field #string ClassName Name of the class.
-- @field #string lid DCS log ID string.
-- @field #number verbose Verbosity of output.
-- @field Core.Zone#ZONE zone The zone.
-- @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 #boolean neutralCanCapture Neutral units can capture. Default `false`.
-- @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
--- Be surprised!
@ -132,8 +134,12 @@ function OPSZONE:New(Zone, CoalitionOwner)
self:AddTransition("*", "Stop", "Stopped") -- Stop FSM.
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("*", "Attacked", "Attacked") -- A guarded zone is under 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.
--- 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".
-- @function [parent=#OPSZONE] Empty
-- @param #OPSZONE self
@ -403,6 +426,18 @@ function OPSZONE:onafterStart(From, Event, To)
-- Reinit the timer.
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.
self.timerStatus:Start(1, 60)
@ -528,12 +563,12 @@ function OPSZONE:onenterGuarded(From, Event, To)
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
--- On enter "Guarded" state.
--- On enter "Attacked" state.
-- @param #OPSZONE self
-- @param #string From From state.
-- @param #string Event Event.
@ -549,9 +584,10 @@ function OPSZONE:onenterAttacked(From, Event, To)
if self.drawZone then
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
@ -580,7 +616,7 @@ end
-- Scan Functions
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Add a platoon to the brigade.
--- Scan zone.
-- @param #OPSZONE self
-- @return #OPSZONE self
function OPSZONE:Scan()
@ -721,6 +757,19 @@ function OPSZONE:Scan()
self.Nblu=Nblu
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
---
@ -746,7 +795,7 @@ function OPSZONE:Scan()
else
-- Still red units in red zone.
-- Red units in red zone.
if Nblu>0 then
@ -758,6 +807,9 @@ function OPSZONE:Scan()
if self:IsAttacked() and self:IsContested() then
self:Defeated(coalition.side.BLUE)
elseif self:IsEmpty() then
-- Red units left zone and returned (or from initial Empty state).
self:Guarded()
end
end
@ -810,6 +862,9 @@ function OPSZONE:Scan()
if self:IsAttacked() and self:IsContested() then
-- Blue defeated read attack.
self:Defeated(coalition.side.RED)
elseif self:IsEmpty() then
-- Blue units left zone and returned (or from initial Empty state).
self:Guarded()
end
end
@ -858,7 +913,7 @@ function OPSZONE:Scan()
self:E(self.lid.."ERROR!")
end
return self
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@ -923,11 +978,11 @@ function OPSZONE:_GetZoneColor()
local color={0,0,0}
if self.ownerCurrent==coalition.side.NEUTRAL then
color={0, 1, 0}
color={1, 1, 1}
elseif self.ownerCurrent==coalition.side.BLUE then
color={1, 0, 0}
elseif self.ownerCurrent==coalition.side.RED then
color={0, 0, 1}
elseif self.ownerCurrent==coalition.side.RED then
color={1, 0, 0}
else
end