mirror of
https://github.com/FlightControl-Master/MOOSE.git
synced 2025-10-29 16:58:06 +00:00
Merge pull request #1858 from FlightControl-Master/FF/Ops
OPS Capture Zone
This commit is contained in:
commit
ff7ebb4f92
@ -88,6 +88,7 @@ DATABASE = {
|
|||||||
WAREHOUSES = {},
|
WAREHOUSES = {},
|
||||||
FLIGHTGROUPS = {},
|
FLIGHTGROUPS = {},
|
||||||
FLIGHTCONTROLS = {},
|
FLIGHTCONTROLS = {},
|
||||||
|
OPSZONES = {},
|
||||||
}
|
}
|
||||||
|
|
||||||
local _DATABASECoalition =
|
local _DATABASECoalition =
|
||||||
@ -410,6 +411,46 @@ do -- Zone_Goal
|
|||||||
end
|
end
|
||||||
|
|
||||||
end -- Zone_Goal
|
end -- Zone_Goal
|
||||||
|
|
||||||
|
do -- OpsZone
|
||||||
|
|
||||||
|
--- Finds a @{Ops.OpsZone#OPSZONE} based on the zone name.
|
||||||
|
-- @param #DATABASE self
|
||||||
|
-- @param #string ZoneName The name of the zone.
|
||||||
|
-- @return Ops.OpsZone#OPSZONE The found OPSZONE.
|
||||||
|
function DATABASE:FindOpsZone( ZoneName )
|
||||||
|
|
||||||
|
local ZoneFound = self.OPSZONES[ZoneName]
|
||||||
|
|
||||||
|
return ZoneFound
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Adds a @{Ops.OpsZone#OPSZONE} based on the zone name in the DATABASE.
|
||||||
|
-- @param #DATABASE self
|
||||||
|
-- @param Ops.OpsZone#OPSZONE OpsZone The zone.
|
||||||
|
function DATABASE:AddOpsZone( OpsZone )
|
||||||
|
|
||||||
|
if OpsZone then
|
||||||
|
|
||||||
|
local ZoneName=OpsZone:GetName()
|
||||||
|
|
||||||
|
if not self.OPSZONES[ZoneName] then
|
||||||
|
self.OPSZONES[ZoneName] = OpsZone
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Deletes a @{Ops.OpsZone#OPSZONE} from the DATABASE based on the zone name.
|
||||||
|
-- @param #DATABASE self
|
||||||
|
-- @param #string ZoneName The name of the zone.
|
||||||
|
function DATABASE:DeleteOpsZone( ZoneName )
|
||||||
|
self.OPSZONES[ZoneName] = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
end -- OpsZone
|
||||||
|
|
||||||
do -- cargo
|
do -- cargo
|
||||||
|
|
||||||
--- Adds a Cargo based on the Cargo Name in the DATABASE.
|
--- Adds a Cargo based on the Cargo Name in the DATABASE.
|
||||||
|
|||||||
@ -53,6 +53,7 @@ do -- SET_BASE
|
|||||||
-- @field #table Index Table of indices.
|
-- @field #table Index Table of indices.
|
||||||
-- @field #table List Unused table.
|
-- @field #table List Unused table.
|
||||||
-- @field Core.Scheduler#SCHEDULER CallScheduler
|
-- @field Core.Scheduler#SCHEDULER CallScheduler
|
||||||
|
-- @field #SET_BASE.Filters Filter Filters
|
||||||
-- @extends Core.Base#BASE
|
-- @extends Core.Base#BASE
|
||||||
|
|
||||||
--- The @{Core.Set#SET_BASE} class defines the core functions that define a collection of objects.
|
--- The @{Core.Set#SET_BASE} class defines the core functions that define a collection of objects.
|
||||||
@ -83,6 +84,11 @@ do -- SET_BASE
|
|||||||
YieldInterval = nil,
|
YieldInterval = nil,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
--- Filters
|
||||||
|
-- @type SET_BASE.Filters
|
||||||
|
-- @field #table Coalition Coalitions
|
||||||
|
-- @field #table Prefix Prefixes.
|
||||||
|
|
||||||
--- Creates a new SET_BASE object, building a set of units belonging to a coalitions, categories, countries, types or with defined prefix names.
|
--- Creates a new SET_BASE object, building a set of units belonging to a coalitions, categories, countries, types or with defined prefix names.
|
||||||
-- @param #SET_BASE self
|
-- @param #SET_BASE self
|
||||||
-- @return #SET_BASE
|
-- @return #SET_BASE
|
||||||
@ -135,11 +141,12 @@ do -- SET_BASE
|
|||||||
|
|
||||||
--- Clear the Objects in the Set.
|
--- Clear the Objects in the Set.
|
||||||
-- @param #SET_BASE self
|
-- @param #SET_BASE self
|
||||||
|
-- @param #boolean TriggerEvent If `true`, an event remove is triggered for each group that is removed from the set.
|
||||||
-- @return #SET_BASE self
|
-- @return #SET_BASE self
|
||||||
function SET_BASE:Clear()
|
function SET_BASE:Clear(TriggerEvent)
|
||||||
|
|
||||||
for Name, Object in pairs( self.Set ) do
|
for Name, Object in pairs( self.Set ) do
|
||||||
self:Remove( Name )
|
self:Remove( Name, not TriggerEvent )
|
||||||
end
|
end
|
||||||
|
|
||||||
return self
|
return self
|
||||||
@ -166,7 +173,7 @@ do -- SET_BASE
|
|||||||
|
|
||||||
--- Gets a list of the Names of the Objects in the Set.
|
--- Gets a list of the Names of the Objects in the Set.
|
||||||
-- @param #SET_BASE self
|
-- @param #SET_BASE self
|
||||||
-- @return #SET_BASE self
|
-- @return #table Table of names.
|
||||||
function SET_BASE:GetSetNames() -- R2.3
|
function SET_BASE:GetSetNames() -- R2.3
|
||||||
self:F2()
|
self:F2()
|
||||||
|
|
||||||
@ -181,7 +188,7 @@ do -- SET_BASE
|
|||||||
|
|
||||||
--- Returns a table of the Objects in the Set.
|
--- Returns a table of the Objects in the Set.
|
||||||
-- @param #SET_BASE self
|
-- @param #SET_BASE self
|
||||||
-- @return #SET_BASE self
|
-- @return #table Table of objects.
|
||||||
function SET_BASE:GetSetObjects() -- R2.3
|
function SET_BASE:GetSetObjects() -- R2.3
|
||||||
self:F2()
|
self:F2()
|
||||||
|
|
||||||
@ -197,16 +204,21 @@ do -- SET_BASE
|
|||||||
--- Removes a @{Core.Base#BASE} object from the @{Core.Set#SET_BASE} and derived classes, based on the Object Name.
|
--- Removes a @{Core.Base#BASE} object from the @{Core.Set#SET_BASE} and derived classes, based on the Object Name.
|
||||||
-- @param #SET_BASE self
|
-- @param #SET_BASE self
|
||||||
-- @param #string ObjectName
|
-- @param #string ObjectName
|
||||||
-- @param NoTriggerEvent (Optional) When `true`, the :Remove() method will not trigger a **Removed** event.
|
-- @param #boolean NoTriggerEvent (Optional) When `true`, the :Remove() method will not trigger a **Removed** event.
|
||||||
function SET_BASE:Remove( ObjectName, NoTriggerEvent )
|
function SET_BASE:Remove( ObjectName, NoTriggerEvent )
|
||||||
self:F2( { ObjectName = ObjectName } )
|
self:F2( { ObjectName = ObjectName } )
|
||||||
|
|
||||||
local TriggerEvent = true
|
local TriggerEvent = true
|
||||||
if NoTriggerEvent then TriggerEvent = false end
|
if NoTriggerEvent then
|
||||||
|
TriggerEvent = false
|
||||||
|
else
|
||||||
|
TriggerEvent = true
|
||||||
|
end
|
||||||
|
|
||||||
local Object = self.Set[ObjectName]
|
local Object = self.Set[ObjectName]
|
||||||
|
|
||||||
if Object then
|
if Object then
|
||||||
|
|
||||||
for Index, Key in ipairs( self.Index ) do
|
for Index, Key in ipairs( self.Index ) do
|
||||||
if Key == ObjectName then
|
if Key == ObjectName then
|
||||||
table.remove( self.Index, Index )
|
table.remove( self.Index, Index )
|
||||||
@ -214,6 +226,7 @@ do -- SET_BASE
|
|||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- When NoTriggerEvent is true, then no Removed event will be triggered.
|
-- When NoTriggerEvent is true, then no Removed event will be triggered.
|
||||||
if TriggerEvent then
|
if TriggerEvent then
|
||||||
self:Removed( ObjectName, Object )
|
self:Removed( ObjectName, Object )
|
||||||
@ -311,7 +324,6 @@ do -- SET_BASE
|
|||||||
-- @param #SET_BASE self
|
-- @param #SET_BASE self
|
||||||
-- @param Core.Set#SET_BASE SetB Set other set, called *B*.
|
-- @param Core.Set#SET_BASE SetB Set other set, called *B*.
|
||||||
-- @return Core.Set#SET_BASE A set of objects that is included in set *A* **and** in set *B*.
|
-- @return Core.Set#SET_BASE A set of objects that is included in set *A* **and** in set *B*.
|
||||||
|
|
||||||
function SET_BASE:GetSetIntersection(SetB)
|
function SET_BASE:GetSetIntersection(SetB)
|
||||||
|
|
||||||
local intersection=SET_BASE:New()
|
local intersection=SET_BASE:New()
|
||||||
@ -461,16 +473,32 @@ do -- SET_BASE
|
|||||||
-- @param #SET_BASE self
|
-- @param #SET_BASE self
|
||||||
-- @return #SET_BASE self
|
-- @return #SET_BASE self
|
||||||
function SET_BASE:FilterOnce()
|
function SET_BASE:FilterOnce()
|
||||||
|
|
||||||
|
--self:Clear()
|
||||||
|
|
||||||
for ObjectName, Object in pairs( self.Database ) do
|
for ObjectName, Object in pairs( self.Database ) do
|
||||||
|
|
||||||
if self:IsIncludeObject( Object ) then
|
if self:IsIncludeObject( Object ) then
|
||||||
self:Add( ObjectName, Object )
|
self:Add( ObjectName, Object )
|
||||||
|
else
|
||||||
|
self:Remove(ObjectName, true)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Clear all filters. You still need to apply :FilterOnce()
|
||||||
|
-- @param #SET_BASE self
|
||||||
|
-- @return #SET_BASE self
|
||||||
|
function SET_BASE:FilterClear()
|
||||||
|
|
||||||
|
for key,value in pairs(self.Filter) do
|
||||||
|
self.Filter[key]={}
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
--- Starts the filtering for the defined collection.
|
--- Starts the filtering for the defined collection.
|
||||||
-- @param #SET_BASE self
|
-- @param #SET_BASE self
|
||||||
@ -817,7 +845,7 @@ do -- SET_BASE
|
|||||||
--- Decides whether an object is in the SET
|
--- Decides whether an object is in the SET
|
||||||
-- @param #SET_BASE self
|
-- @param #SET_BASE self
|
||||||
-- @param #table Object
|
-- @param #table Object
|
||||||
-- @return #SET_BASE self
|
-- @return #boolean `true` if object is in set and `false` otherwise.
|
||||||
function SET_BASE:IsInSet( Object )
|
function SET_BASE:IsInSet( Object )
|
||||||
self:F3( Object )
|
self:F3( Object )
|
||||||
local outcome = false
|
local outcome = false
|
||||||
@ -1021,9 +1049,9 @@ do -- SET_GROUP
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Gets the Set.
|
--- Get a *new* set that only contains alive groups.
|
||||||
-- @param #SET_GROUP self
|
-- @param #SET_GROUP self
|
||||||
-- @return #table Table of objects
|
-- @return #SET_GROUP Set of alive groups.
|
||||||
function SET_GROUP:GetAliveSet()
|
function SET_GROUP:GetAliveSet()
|
||||||
self:F2()
|
self:F2()
|
||||||
|
|
||||||
@ -1169,11 +1197,14 @@ do -- SET_GROUP
|
|||||||
--- Builds a set of groups in zones.
|
--- Builds a set of groups in zones.
|
||||||
-- @param #SET_GROUP self
|
-- @param #SET_GROUP self
|
||||||
-- @param #table Zones Table of Core.Zone#ZONE Zone objects, or a Core.Set#SET_ZONE
|
-- @param #table Zones Table of Core.Zone#ZONE Zone objects, or a Core.Set#SET_ZONE
|
||||||
|
-- @param #boolean Clear If `true`, clear any previously defined filters.
|
||||||
-- @return #SET_GROUP self
|
-- @return #SET_GROUP self
|
||||||
function SET_GROUP:FilterZones( Zones )
|
function SET_GROUP:FilterZones( Zones, Clear )
|
||||||
if not self.Filter.Zones then
|
|
||||||
|
if Clear or not self.Filter.Zones then
|
||||||
self.Filter.Zones = {}
|
self.Filter.Zones = {}
|
||||||
end
|
end
|
||||||
|
|
||||||
local zones = {}
|
local zones = {}
|
||||||
if Zones.ClassName and Zones.ClassName == "SET_ZONE" then
|
if Zones.ClassName and Zones.ClassName == "SET_ZONE" then
|
||||||
zones = Zones.Set
|
zones = Zones.Set
|
||||||
@ -1183,34 +1214,12 @@ do -- SET_GROUP
|
|||||||
else
|
else
|
||||||
zones = Zones
|
zones = Zones
|
||||||
end
|
end
|
||||||
|
|
||||||
for _, Zone in pairs( zones ) do
|
for _, Zone in pairs( zones ) do
|
||||||
local zonename = Zone:GetName()
|
local zonename = Zone:GetName()
|
||||||
self.Filter.Zones[zonename] = Zone
|
self.Filter.Zones[zonename] = Zone
|
||||||
end
|
end
|
||||||
return self
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Builds a set of groups in zones.
|
|
||||||
-- @param #SET_GROUP self
|
|
||||||
-- @param #table Zones Table of Core.Zone#ZONE Zone objects, or a Core.Set#SET_ZONE
|
|
||||||
-- @return #SET_GROUP self
|
|
||||||
function SET_GROUP:FilterZones( Zones )
|
|
||||||
if not self.Filter.Zones then
|
|
||||||
self.Filter.Zones = {}
|
|
||||||
end
|
|
||||||
local zones = {}
|
|
||||||
if Zones.ClassName and Zones.ClassName == "SET_ZONE" then
|
|
||||||
zones = Zones.Set
|
|
||||||
elseif type( Zones ) ~= "table" or (type( Zones ) == "table" and Zones.ClassName ) then
|
|
||||||
self:E("***** FilterZones needs either a table of ZONE Objects or a SET_ZONE as parameter!")
|
|
||||||
return self
|
|
||||||
else
|
|
||||||
zones = Zones
|
|
||||||
end
|
|
||||||
for _,Zone in pairs( zones ) do
|
|
||||||
local zonename = Zone:GetName()
|
|
||||||
self.Filter.Zones[zonename] = Zone
|
|
||||||
end
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -1218,17 +1227,21 @@ do -- SET_GROUP
|
|||||||
-- Possible current coalitions are red, blue and neutral.
|
-- Possible current coalitions are red, blue and neutral.
|
||||||
-- @param #SET_GROUP self
|
-- @param #SET_GROUP self
|
||||||
-- @param #string Coalitions Can take the following values: "red", "blue", "neutral".
|
-- @param #string Coalitions Can take the following values: "red", "blue", "neutral".
|
||||||
|
-- @param #boolean Clear If `true`, clear any previously defined filters.
|
||||||
-- @return #SET_GROUP self
|
-- @return #SET_GROUP self
|
||||||
function SET_GROUP:FilterCoalitions( Coalitions )
|
function SET_GROUP:FilterCoalitions( Coalitions, Clear )
|
||||||
if not self.Filter.Coalitions then
|
|
||||||
|
if Clear or (not self.Filter.Coalitions) then
|
||||||
self.Filter.Coalitions = {}
|
self.Filter.Coalitions = {}
|
||||||
end
|
end
|
||||||
if type( Coalitions ) ~= "table" then
|
|
||||||
Coalitions = { Coalitions }
|
-- Ensure table.
|
||||||
end
|
Coalitions = UTILS.EnsureTable(Coalitions, false)
|
||||||
|
|
||||||
for CoalitionID, Coalition in pairs( Coalitions ) do
|
for CoalitionID, Coalition in pairs( Coalitions ) do
|
||||||
self.Filter.Coalitions[Coalition] = Coalition
|
self.Filter.Coalitions[Coalition] = Coalition
|
||||||
end
|
end
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -1236,17 +1249,22 @@ do -- SET_GROUP
|
|||||||
-- Possible current categories are plane, helicopter, ground, ship.
|
-- Possible current categories are plane, helicopter, ground, ship.
|
||||||
-- @param #SET_GROUP self
|
-- @param #SET_GROUP self
|
||||||
-- @param #string Categories Can take the following values: "plane", "helicopter", "ground", "ship".
|
-- @param #string Categories Can take the following values: "plane", "helicopter", "ground", "ship".
|
||||||
|
-- @param #boolean Clear If `true`, clear any previously defined filters.
|
||||||
-- @return #SET_GROUP self
|
-- @return #SET_GROUP self
|
||||||
function SET_GROUP:FilterCategories( Categories )
|
function SET_GROUP:FilterCategories( Categories, Clear )
|
||||||
if not self.Filter.Categories then
|
|
||||||
|
if Clear or not self.Filter.Categories then
|
||||||
self.Filter.Categories = {}
|
self.Filter.Categories = {}
|
||||||
end
|
end
|
||||||
|
|
||||||
if type( Categories ) ~= "table" then
|
if type( Categories ) ~= "table" then
|
||||||
Categories = { Categories }
|
Categories = { Categories }
|
||||||
end
|
end
|
||||||
|
|
||||||
for CategoryID, Category in pairs( Categories ) do
|
for CategoryID, Category in pairs( Categories ) do
|
||||||
self.Filter.Categories[Category] = Category
|
self.Filter.Categories[Category] = Category
|
||||||
end
|
end
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -1899,6 +1917,41 @@ do -- SET_GROUP
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Get the closest group of the set with respect to a given reference coordinate. Optionally, only groups of given coalitions are considered in the search.
|
||||||
|
-- @param #SET_GROUP self
|
||||||
|
-- @param Core.Point#COORDINATE Coordinate Reference Coordinate from which the closest group is determined.
|
||||||
|
-- @return Wrapper.Group#GROUP The closest group (if any).
|
||||||
|
-- @return #number Distance in meters to the closest group.
|
||||||
|
function SET_GROUP:GetClosestGroup(Coordinate, Coalitions)
|
||||||
|
|
||||||
|
local Set = self:GetSet()
|
||||||
|
|
||||||
|
local dmin=math.huge
|
||||||
|
local gmin=nil
|
||||||
|
|
||||||
|
for GroupID, GroupData in pairs( Set ) do -- For each GROUP in SET_GROUP
|
||||||
|
local group=GroupData --Wrapper.Group#GROUP
|
||||||
|
|
||||||
|
if group and group:IsAlive() and (Coalitions==nil or UTILS.IsAnyInTable(Coalitions, group:GetCoalition())) then
|
||||||
|
|
||||||
|
local coord=group:GetCoord()
|
||||||
|
|
||||||
|
-- Distance between ref. coordinate and group coordinate.
|
||||||
|
local d=UTILS.VecDist3D(Coordinate, coord)
|
||||||
|
|
||||||
|
if d<dmin then
|
||||||
|
dmin=d
|
||||||
|
gmin=group
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
return gmin, dmin
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
do -- SET_UNIT
|
do -- SET_UNIT
|
||||||
@ -2120,9 +2173,11 @@ do -- SET_UNIT
|
|||||||
if type( Coalitions ) ~= "table" then
|
if type( Coalitions ) ~= "table" then
|
||||||
Coalitions = { Coalitions }
|
Coalitions = { Coalitions }
|
||||||
end
|
end
|
||||||
|
|
||||||
for CoalitionID, Coalition in pairs( Coalitions ) do
|
for CoalitionID, Coalition in pairs( Coalitions ) do
|
||||||
self.Filter.Coalitions[Coalition] = Coalition
|
self.Filter.Coalitions[Coalition] = Coalition
|
||||||
end
|
end
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -4030,7 +4085,7 @@ do -- SET_CLIENT
|
|||||||
--- Builds a set of clients in zones.
|
--- Builds a set of clients in zones.
|
||||||
-- @param #SET_CLIENT self
|
-- @param #SET_CLIENT self
|
||||||
-- @param #table Zones Table of Core.Zone#ZONE Zone objects, or a Core.Set#SET_ZONE
|
-- @param #table Zones Table of Core.Zone#ZONE Zone objects, or a Core.Set#SET_ZONE
|
||||||
-- @return #SET_TABLE self
|
-- @return #SET_CLIENT self
|
||||||
function SET_CLIENT:FilterZones( Zones )
|
function SET_CLIENT:FilterZones( Zones )
|
||||||
if not self.Filter.Zones then
|
if not self.Filter.Zones then
|
||||||
self.Filter.Zones = {}
|
self.Filter.Zones = {}
|
||||||
@ -5840,8 +5895,7 @@ do -- SET_ZONE
|
|||||||
-- If zones overlap, the first zone that validates the test is returned.
|
-- If zones overlap, the first zone that validates the test is returned.
|
||||||
-- @param #SET_ZONE self
|
-- @param #SET_ZONE self
|
||||||
-- @param Core.Point#COORDINATE Coordinate The coordinate to be searched.
|
-- @param Core.Point#COORDINATE Coordinate The coordinate to be searched.
|
||||||
-- @return Core.Zone#ZONE_BASE The zone that validates the coordinate location.
|
-- @return Core.Zone#ZONE_BASE The zone (if any) that validates the coordinate location.
|
||||||
-- @return #nil No zone has been found.
|
|
||||||
function SET_ZONE:IsCoordinateInZone( Coordinate )
|
function SET_ZONE:IsCoordinateInZone( Coordinate )
|
||||||
|
|
||||||
for _, Zone in pairs( self:GetSet() ) do
|
for _, Zone in pairs( self:GetSet() ) do
|
||||||
@ -5853,6 +5907,27 @@ do -- SET_ZONE
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Get the closest zone to a given coordinate.
|
||||||
|
-- @param #SET_ZONE self
|
||||||
|
-- @param Core.Point#COORDINATE Coordinate The reference coordinate from which the closest zone is determined.
|
||||||
|
-- @return Core.Zone#ZONE_BASE The closest zone (if any).
|
||||||
|
-- @return #number Distance to ref coordinate in meters.
|
||||||
|
function SET_ZONE:GetClosestZone( Coordinate )
|
||||||
|
|
||||||
|
local dmin=math.huge
|
||||||
|
local zmin=nil
|
||||||
|
for _, Zone in pairs( self:GetSet() ) do
|
||||||
|
local Zone = Zone -- Core.Zone#ZONE_BASE
|
||||||
|
local d=Zone:Get2DDistance(Coordinate)
|
||||||
|
if d<dmin then
|
||||||
|
dmin=d
|
||||||
|
zmin=Zone
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return zmin, dmin
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -6167,6 +6242,461 @@ do -- SET_ZONE_GOAL
|
|||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
do -- SET_OPSZONE
|
||||||
|
|
||||||
|
--- @type SET_OPSZONE
|
||||||
|
-- @extends Core.Set#SET_BASE
|
||||||
|
|
||||||
|
--- Mission designers can use the @{Core.Set#SET_OPSZONE} class to build sets of zones of various types.
|
||||||
|
--
|
||||||
|
-- ## SET_OPSZONE constructor
|
||||||
|
--
|
||||||
|
-- Create a new SET_OPSZONE object with the @{#SET_OPSZONE.New} method:
|
||||||
|
--
|
||||||
|
-- * @{#SET_OPSZONE.New}: Creates a new SET_OPSZONE object.
|
||||||
|
--
|
||||||
|
-- ## Add or Remove ZONEs from SET_OPSZONE
|
||||||
|
--
|
||||||
|
-- ZONEs can be added and removed using the @{Core.Set#SET_OPSZONE.AddZonesByName} and @{Core.Set#SET_OPSZONE.RemoveZonesByName} respectively.
|
||||||
|
-- These methods take a single ZONE name or an array of ZONE names to be added or removed from SET_OPSZONE.
|
||||||
|
--
|
||||||
|
-- ## SET_OPSZONE filter criteria
|
||||||
|
--
|
||||||
|
-- You can set filter criteria to build the collection of zones in SET_OPSZONE.
|
||||||
|
-- Filter criteria are defined by:
|
||||||
|
--
|
||||||
|
-- * @{#SET_OPSZONE.FilterPrefixes}: Builds the SET_OPSZONE with the zones having a certain text pattern in their name. **ATTENTION!** Bad naming convention as this *does not* only filter *prefixes*.
|
||||||
|
--
|
||||||
|
-- Once the filter criteria have been set for the SET_OPSZONE, you can start filtering using:
|
||||||
|
--
|
||||||
|
-- * @{#SET_OPSZONE.FilterStart}: Starts the filtering of the zones within the SET_OPSZONE.
|
||||||
|
--
|
||||||
|
-- ## SET_OPSZONE iterators
|
||||||
|
--
|
||||||
|
-- Once the filters have been defined and the SET_OPSZONE has been built, you can iterate the SET_OPSZONE with the available iterator methods.
|
||||||
|
-- The iterator methods will walk the SET_OPSZONE set, and call for each airbase within the set a function that you provide.
|
||||||
|
-- The following iterator methods are currently available within the SET_OPSZONE:
|
||||||
|
--
|
||||||
|
-- * @{#SET_OPSZONE.ForEachZone}: Calls a function for each zone it finds within the SET_OPSZONE.
|
||||||
|
--
|
||||||
|
-- ===
|
||||||
|
-- @field #SET_OPSZONE SET_OPSZONE
|
||||||
|
SET_OPSZONE = {
|
||||||
|
ClassName = "SET_OPSZONE",
|
||||||
|
Zones = {},
|
||||||
|
Filter = {
|
||||||
|
Prefixes = nil,
|
||||||
|
Coalitions = nil,
|
||||||
|
},
|
||||||
|
FilterMeta = {
|
||||||
|
Coalitions = {
|
||||||
|
red = coalition.side.RED,
|
||||||
|
blue = coalition.side.BLUE,
|
||||||
|
neutral = coalition.side.NEUTRAL,
|
||||||
|
},
|
||||||
|
}, --FilterMeta
|
||||||
|
}
|
||||||
|
|
||||||
|
--- Creates a new SET_OPSZONE object, building a set of zones.
|
||||||
|
-- @param #SET_OPSZONE self
|
||||||
|
-- @return #SET_OPSZONE self
|
||||||
|
function SET_OPSZONE:New()
|
||||||
|
|
||||||
|
-- Inherits from BASE
|
||||||
|
local self = BASE:Inherit( self, SET_BASE:New( _DATABASE.OPSZONES ) )
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Add an OPSZONE to set.
|
||||||
|
-- @param Core.Set#SET_OPSZONE self
|
||||||
|
-- @param Ops.OpsZone#OPSZONE Zone The OPSZONE object.
|
||||||
|
-- @return #SET_OPSZONE self
|
||||||
|
function SET_OPSZONE:AddZone( Zone )
|
||||||
|
|
||||||
|
self:Add( Zone:GetName(), Zone )
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Remove ZONEs from SET_OPSZONE.
|
||||||
|
-- @param Core.Set#SET_OPSZONE self
|
||||||
|
-- @param #table RemoveZoneNames A single name or an array of OPSZONE names.
|
||||||
|
-- @return #SET_OPSZONE self
|
||||||
|
function SET_OPSZONE:RemoveZonesByName( RemoveZoneNames )
|
||||||
|
|
||||||
|
local RemoveZoneNamesArray = (type( RemoveZoneNames ) == "table") and RemoveZoneNames or { RemoveZoneNames }
|
||||||
|
|
||||||
|
--UTILS.EnsureTable(Object,ReturnNil)
|
||||||
|
|
||||||
|
for RemoveZoneID, RemoveZoneName in pairs( RemoveZoneNamesArray ) do
|
||||||
|
self:Remove( RemoveZoneName )
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Finds a Zone based on its name.
|
||||||
|
-- @param #SET_OPSZONE self
|
||||||
|
-- @param #string ZoneName
|
||||||
|
-- @return Ops.OpsZone#OPSZONE The found Zone.
|
||||||
|
function SET_OPSZONE:FindZone( ZoneName )
|
||||||
|
|
||||||
|
local ZoneFound = self.Set[ZoneName]
|
||||||
|
|
||||||
|
return ZoneFound
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get a random zone from the set.
|
||||||
|
-- @param #SET_OPSZONE self
|
||||||
|
-- @return Ops.OpsZone#OPSZONE The random Zone.
|
||||||
|
function SET_OPSZONE:GetRandomZone()
|
||||||
|
|
||||||
|
if self:Count() ~= 0 then
|
||||||
|
|
||||||
|
local Index = self.Index
|
||||||
|
local ZoneFound = nil -- Core.Zone#ZONE_BASE
|
||||||
|
|
||||||
|
-- Loop until a zone has been found.
|
||||||
|
-- The :GetZoneMaybe() call will evaluate the probability for the zone to be selected.
|
||||||
|
-- If the zone is not selected, then nil is returned by :GetZoneMaybe() and the loop continues!
|
||||||
|
while not ZoneFound do
|
||||||
|
local ZoneRandom = math.random( 1, #Index )
|
||||||
|
ZoneFound = self.Set[Index[ZoneRandom]]:GetZoneMaybe()
|
||||||
|
end
|
||||||
|
|
||||||
|
return ZoneFound
|
||||||
|
end
|
||||||
|
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set a zone probability.
|
||||||
|
-- @param #SET_OPSZONE self
|
||||||
|
-- @param #string ZoneName The name of the zone.
|
||||||
|
-- @param #number Probability The probability in percent.
|
||||||
|
function SET_OPSZONE:SetZoneProbability( ZoneName, Probability )
|
||||||
|
local Zone = self:FindZone( ZoneName )
|
||||||
|
Zone:SetZoneProbability( Probability )
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Builds a set of OPSZONEs that contain the given string in their name.
|
||||||
|
-- **ATTENTION!** Bad naming convention as this **does not** filter only **prefixes** but all zones that **contain** the string.
|
||||||
|
-- @param #SET_OPSZONE self
|
||||||
|
-- @param #string Prefixes The string pattern(s) that needs to be contained in the zone name. Can also be passed as a `#table` of strings.
|
||||||
|
-- @return #SET_OPSZONE self
|
||||||
|
function SET_OPSZONE:FilterPrefixes( Prefixes )
|
||||||
|
|
||||||
|
if not self.Filter.Prefixes then
|
||||||
|
self.Filter.Prefixes = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
Prefixes=UTILS.EnsureTable(Prefixes, false)
|
||||||
|
|
||||||
|
for PrefixID, Prefix in pairs( Prefixes ) do
|
||||||
|
self.Filter.Prefixes[Prefix] = Prefix
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Builds a set of groups of coalitions. Possible current coalitions are red, blue and neutral.
|
||||||
|
-- @param #SET_OPSZONE self
|
||||||
|
-- @param #string Coalitions Can take the following values: "red", "blue", "neutral" or combinations as a table, for example `{"red", "neutral"}`.
|
||||||
|
-- @return #SET_OPSZONE self
|
||||||
|
function SET_OPSZONE:FilterCoalitions(Coalitions)
|
||||||
|
|
||||||
|
-- Create an empty set.
|
||||||
|
if not self.Filter.Coalitions then
|
||||||
|
self.Filter.Coalitions={}
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Ensure we got a table.
|
||||||
|
Coalitions=UTILS.EnsureTable(Coalitions, false)
|
||||||
|
|
||||||
|
-- Set filter.
|
||||||
|
for CoalitionID, Coalition in pairs( Coalitions ) do
|
||||||
|
self.Filter.Coalitions[Coalition] = Coalition
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Filters for the defined collection.
|
||||||
|
-- @param #SET_OPSZONE self
|
||||||
|
-- @return #SET_OPSZONE self
|
||||||
|
function SET_OPSZONE:FilterOnce()
|
||||||
|
|
||||||
|
for ObjectName, Object in pairs( self.Database ) do
|
||||||
|
|
||||||
|
-- First remove the object (without creating an event).
|
||||||
|
self:Remove(ObjectName, true)
|
||||||
|
|
||||||
|
if self:IsIncludeObject( Object ) then
|
||||||
|
self:Add( ObjectName, Object )
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Clear all filters. You still need to apply `FilterOnce()` to have an effect on the set.
|
||||||
|
-- @param #SET_OPSZONE self
|
||||||
|
-- @return #SET_OPSZONE self
|
||||||
|
function SET_OPSZONE:FilterClear()
|
||||||
|
|
||||||
|
local parent=self:GetParent(self, SET_OPSZONE) --#SET_BASE
|
||||||
|
|
||||||
|
parent:FilterClear()
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Starts the filtering.
|
||||||
|
-- @param #SET_OPSZONE self
|
||||||
|
-- @return #SET_OPSZONE self
|
||||||
|
function SET_OPSZONE:FilterStart()
|
||||||
|
|
||||||
|
if _DATABASE then
|
||||||
|
|
||||||
|
-- We initialize the first set.
|
||||||
|
for ObjectName, Object in pairs( self.Database ) do
|
||||||
|
if self:IsIncludeObject( Object ) then
|
||||||
|
self:Add( ObjectName, Object )
|
||||||
|
else
|
||||||
|
self:RemoveZonesByName( ObjectName )
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
self:HandleEvent( EVENTS.NewZoneGoal )
|
||||||
|
self:HandleEvent( EVENTS.DeleteZoneGoal )
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Stops the filtering for the defined collection.
|
||||||
|
-- @param #SET_OPSZONE self
|
||||||
|
-- @return #SET_OPSZONE self
|
||||||
|
function SET_OPSZONE:FilterStop()
|
||||||
|
|
||||||
|
self:UnHandleEvent( EVENTS.NewZoneGoal )
|
||||||
|
self:UnHandleEvent( EVENTS.DeleteZoneGoal )
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Handles the Database to check on an event (birth) that the Object was added in the Database.
|
||||||
|
-- This is required, because sometimes the _DATABASE birth event gets called later than the SET_BASE birth event!
|
||||||
|
-- @param #SET_OPSZONE self
|
||||||
|
-- @param Core.Event#EVENTDATA Event
|
||||||
|
-- @return #string The name of the AIRBASE
|
||||||
|
-- @return #table The AIRBASE
|
||||||
|
function SET_OPSZONE:AddInDatabase( Event )
|
||||||
|
self:F3( { Event } )
|
||||||
|
|
||||||
|
return Event.IniDCSUnitName, self.Database[Event.IniDCSUnitName]
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Handles the Database to check on any event that Object exists in the Database.
|
||||||
|
-- This is required, because sometimes the _DATABASE event gets called later than the SET_BASE event or vise versa!
|
||||||
|
-- @param #SET_OPSZONE self
|
||||||
|
-- @param Core.Event#EVENTDATA Event
|
||||||
|
-- @return #string The name of the AIRBASE
|
||||||
|
-- @return #table The AIRBASE
|
||||||
|
function SET_OPSZONE:FindInDatabase( Event )
|
||||||
|
self:F3( { Event } )
|
||||||
|
|
||||||
|
return Event.IniDCSUnitName, self.Database[Event.IniDCSUnitName]
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Iterate the SET_OPSZONE and call an iterator function for each ZONE, providing the ZONE and optional parameters.
|
||||||
|
-- @param #SET_OPSZONE self
|
||||||
|
-- @param #function IteratorFunction The function that will be called when there is an alive ZONE in the SET_OPSZONE. The function needs to accept a AIRBASE parameter.
|
||||||
|
-- @return #SET_OPSZONE self
|
||||||
|
function SET_OPSZONE:ForEachZone( IteratorFunction, ... )
|
||||||
|
self:F2( arg )
|
||||||
|
|
||||||
|
self:ForEach( IteratorFunction, arg, self:GetSet() )
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Private function that checks if an object is contained in the set or filtered.
|
||||||
|
-- @param #SET_OPSZONE self
|
||||||
|
-- @param Ops.OpsZone#OPSZONE MZone The OPSZONE object.
|
||||||
|
-- @return #SET_OPSZONE self
|
||||||
|
function SET_OPSZONE:IsIncludeObject( MZone )
|
||||||
|
self:F2( MZone )
|
||||||
|
|
||||||
|
local MZoneInclude = true
|
||||||
|
|
||||||
|
if MZone then
|
||||||
|
|
||||||
|
local MZoneName = MZone:GetName()
|
||||||
|
|
||||||
|
if self.Filter.Prefixes then
|
||||||
|
|
||||||
|
local MZonePrefix = false
|
||||||
|
|
||||||
|
-- Loop over prefixes.
|
||||||
|
for ZonePrefixId, ZonePrefix in pairs( self.Filter.Prefixes ) do
|
||||||
|
|
||||||
|
-- Prifix
|
||||||
|
self:T3( { "Prefix:", string.find( MZoneName, ZonePrefix, 1 ), ZonePrefix } )
|
||||||
|
|
||||||
|
if string.find(MZoneName, ZonePrefix, 1) then
|
||||||
|
MZonePrefix = true
|
||||||
|
break --Break the loop as we found the prefix.
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
self:T( { "Evaluated Prefix", MZonePrefix } )
|
||||||
|
|
||||||
|
MZoneInclude = MZoneInclude and MZonePrefix
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Filter coalitions.
|
||||||
|
if self.Filter.Coalitions then
|
||||||
|
|
||||||
|
local MGroupCoalition = false
|
||||||
|
|
||||||
|
local coalition=MZone:GetOwner()
|
||||||
|
|
||||||
|
for _, CoalitionName in pairs( self.Filter.Coalitions ) do
|
||||||
|
|
||||||
|
if self.FilterMeta.Coalitions[CoalitionName] and self.FilterMeta.Coalitions[CoalitionName]==coalition then
|
||||||
|
MGroupCoalition = true
|
||||||
|
break -- Break the loop the coalition is contains.
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
MZoneInclude = MZoneInclude and MGroupCoalition
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
self:T2( MZoneInclude )
|
||||||
|
return MZoneInclude
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Handles the OnEventNewZone event for the Set.
|
||||||
|
-- @param #SET_OPSZONE self
|
||||||
|
-- @param Core.Event#EVENTDATA EventData
|
||||||
|
function SET_OPSZONE:OnEventNewZoneGoal( EventData )
|
||||||
|
|
||||||
|
-- Debug info.
|
||||||
|
self:T( { "New Zone Capture Coalition", EventData } )
|
||||||
|
self:T( { "Zone Capture Coalition", EventData.ZoneGoal } )
|
||||||
|
|
||||||
|
if EventData.ZoneGoal then
|
||||||
|
if EventData.ZoneGoal and self:IsIncludeObject( EventData.ZoneGoal ) then
|
||||||
|
self:T( { "Adding Zone Capture Coalition", EventData.ZoneGoal.ZoneName, EventData.ZoneGoal } )
|
||||||
|
self:Add( EventData.ZoneGoal.ZoneName, EventData.ZoneGoal )
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Handles the OnDead or OnCrash event for alive units set.
|
||||||
|
-- @param #SET_OPSZONE self
|
||||||
|
-- @param Core.Event#EVENTDATA EventData
|
||||||
|
function SET_OPSZONE:OnEventDeleteZoneGoal( EventData ) -- R2.1
|
||||||
|
self:F3( { EventData } )
|
||||||
|
|
||||||
|
if EventData.ZoneGoal then
|
||||||
|
local Zone = _DATABASE:FindZone( EventData.ZoneGoal.ZoneName )
|
||||||
|
if Zone and Zone.ZoneName then
|
||||||
|
|
||||||
|
-- When cargo was deleted, it may probably be because of an S_EVENT_DEAD.
|
||||||
|
-- However, in the loading logic, an S_EVENT_DEAD is also generated after a Destroy() call.
|
||||||
|
-- And this is a problem because it will remove all entries from the SET_OPSZONEs.
|
||||||
|
-- To prevent this from happening, the Zone object has a flag NoDestroy.
|
||||||
|
-- When true, the SET_OPSZONE won't Remove the Zone object from the set.
|
||||||
|
-- This flag is switched off after the event handlers have been called in the EVENT class.
|
||||||
|
self:F( { ZoneNoDestroy = Zone.NoDestroy } )
|
||||||
|
if Zone.NoDestroy then
|
||||||
|
else
|
||||||
|
self:Remove( Zone.ZoneName )
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Start all opszones of the set.
|
||||||
|
-- @param #SET_OPSZONE self
|
||||||
|
-- @return #SET_OPSZONE self
|
||||||
|
function SET_OPSZONE:Start()
|
||||||
|
|
||||||
|
for _,_Zone in pairs( self:GetSet() ) do
|
||||||
|
local Zone = _Zone --Ops.OpsZone#OPSZONE
|
||||||
|
if Zone:IsStopped() then
|
||||||
|
Zone:Start()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Validate if a coordinate is in one of the zones in the set.
|
||||||
|
-- Returns the ZONE object where the coordiante is located.
|
||||||
|
-- If zones overlap, the first zone that validates the test is returned.
|
||||||
|
-- @param #SET_OPSZONE self
|
||||||
|
-- @param Core.Point#COORDINATE Coordinate The coordinate to be searched.
|
||||||
|
-- @return Core.Zone#ZONE_BASE The zone that validates the coordinate location.
|
||||||
|
-- @return #nil No zone has been found.
|
||||||
|
function SET_OPSZONE:IsCoordinateInZone( Coordinate )
|
||||||
|
|
||||||
|
for _,_Zone in pairs( self:GetSet() ) do
|
||||||
|
local Zone = _Zone --Ops.OpsZone#OPSZONE
|
||||||
|
if Zone:GetZone():IsCoordinateInZone( Coordinate ) then
|
||||||
|
return Zone
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Get the closest OPSZONE from a given reference coordinate. Only started zones are considered.
|
||||||
|
-- @param #SET_OPSZONE self
|
||||||
|
-- @param Core.Point#COORDINATE Coordinate The reference coordinate from which the closest zone is determined.
|
||||||
|
-- @param #table Coalitions Only consider the given coalition(s), *e.g.* `{coaliton.side.RED}` to find the closest red zone.
|
||||||
|
-- @return Ops.OpsZone#OPSZONE The closest OPSZONE (if any).
|
||||||
|
-- @return #number Distance to ref coordinate in meters.
|
||||||
|
function SET_OPSZONE:GetClosestZone( Coordinate, Coalitions )
|
||||||
|
|
||||||
|
Coalitions=UTILS.EnsureTable(Coalitions, true)
|
||||||
|
|
||||||
|
local dmin=math.huge --#number
|
||||||
|
local zmin=nil --Ops.OpsZone#OPSZONE
|
||||||
|
|
||||||
|
for _,_opszone in pairs(self:GetSet()) do
|
||||||
|
local opszone=_opszone --Ops.OpsZone#OPSZONE
|
||||||
|
|
||||||
|
local coal=opszone:GetOwner()
|
||||||
|
|
||||||
|
if opszone:IsStarted() and (Coalitions==nil or (Coalitions and UTILS.IsInTable(Coalitions, coal))) then
|
||||||
|
|
||||||
|
-- Get 2D distance.
|
||||||
|
local d=opszone:GetZone():Get2DDistance(Coordinate)
|
||||||
|
|
||||||
|
if d<dmin then
|
||||||
|
dmin=d
|
||||||
|
zmin=opszone
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return zmin, dmin
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
do -- SET_OPSGROUP
|
do -- SET_OPSGROUP
|
||||||
@ -6289,7 +6819,7 @@ do -- SET_OPSGROUP
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Gets the Set.
|
--- Gets a **new** set that only contains alive groups.
|
||||||
-- @param #SET_OPSGROUP self
|
-- @param #SET_OPSGROUP self
|
||||||
-- @return #SET_OPSGROUP self
|
-- @return #SET_OPSGROUP self
|
||||||
function SET_OPSGROUP:GetAliveSet()
|
function SET_OPSGROUP:GetAliveSet()
|
||||||
@ -6454,11 +6984,12 @@ do -- SET_OPSGROUP
|
|||||||
-- Possible current coalitions are red, blue and neutral.
|
-- Possible current coalitions are red, blue and neutral.
|
||||||
-- @param #SET_OPSGROUP self
|
-- @param #SET_OPSGROUP self
|
||||||
-- @param #string Coalitions Can take the following values: "red", "blue", "neutral" or combinations as a table, for example `{"red", "neutral"}`.
|
-- @param #string Coalitions Can take the following values: "red", "blue", "neutral" or combinations as a table, for example `{"red", "neutral"}`.
|
||||||
|
-- @param #boolean Clear If `true`, clear any previously defined filters.
|
||||||
-- @return #SET_OPSGROUP self
|
-- @return #SET_OPSGROUP self
|
||||||
function SET_OPSGROUP:FilterCoalitions(Coalitions)
|
function SET_OPSGROUP:FilterCoalitions(Coalitions, Clear)
|
||||||
|
|
||||||
-- Create an empty set.
|
-- Create an empty set.
|
||||||
if not self.Filter.Coalitions then
|
if Clear or not self.Filter.Coalitions then
|
||||||
self.Filter.Coalitions={}
|
self.Filter.Coalitions={}
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -6487,10 +7018,11 @@ do -- SET_OPSGROUP
|
|||||||
--
|
--
|
||||||
-- @param #SET_OPSGROUP self
|
-- @param #SET_OPSGROUP self
|
||||||
-- @param #string Categories Can take the following values: "plane", "helicopter", "ground", "ship" or combinations as a table, for example `{"plane", "helicopter"}`.
|
-- @param #string Categories Can take the following values: "plane", "helicopter", "ground", "ship" or combinations as a table, for example `{"plane", "helicopter"}`.
|
||||||
|
-- @param #boolean Clear If `true`, clear any previously defined filters.
|
||||||
-- @return #SET_OPSGROUP self
|
-- @return #SET_OPSGROUP self
|
||||||
function SET_OPSGROUP:FilterCategories( Categories )
|
function SET_OPSGROUP:FilterCategories( Categories, Clear )
|
||||||
|
|
||||||
if not self.Filter.Categories then
|
if Clear or not self.Filter.Categories then
|
||||||
self.Filter.Categories={}
|
self.Filter.Categories={}
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -6548,11 +7080,12 @@ do -- SET_OPSGROUP
|
|||||||
--- Builds a set of groups of defined countries.
|
--- Builds a set of groups of defined countries.
|
||||||
-- @param #SET_OPSGROUP self
|
-- @param #SET_OPSGROUP self
|
||||||
-- @param #string Countries Can take those country strings known within DCS world.
|
-- @param #string Countries Can take those country strings known within DCS world.
|
||||||
|
-- @param #boolean Clear If `true`, clear any previously defined filters.
|
||||||
-- @return #SET_OPSGROUP self
|
-- @return #SET_OPSGROUP self
|
||||||
function SET_OPSGROUP:FilterCountries(Countries)
|
function SET_OPSGROUP:FilterCountries(Countries, Clear)
|
||||||
|
|
||||||
-- Create empty table if necessary.
|
-- Create empty table if necessary.
|
||||||
if not self.Filter.Countries then
|
if Clear or not self.Filter.Countries then
|
||||||
self.Filter.Countries = {}
|
self.Filter.Countries = {}
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -6574,11 +7107,12 @@ do -- SET_OPSGROUP
|
|||||||
-- **Attention!** Bad naming convention as this **does not** filter only **prefixes** but all groups that **contain** the string.
|
-- **Attention!** Bad naming convention as this **does not** filter only **prefixes** but all groups that **contain** the string.
|
||||||
-- @param #SET_OPSGROUP self
|
-- @param #SET_OPSGROUP self
|
||||||
-- @param #string Prefixes The string pattern(s) that needs to be contained in the group name. Can also be passed as a `#table` of strings.
|
-- @param #string Prefixes The string pattern(s) that needs to be contained in the group name. Can also be passed as a `#table` of strings.
|
||||||
|
-- @param #boolean Clear If `true`, clear any previously defined filters.
|
||||||
-- @return #SET_OPSGROUP self
|
-- @return #SET_OPSGROUP self
|
||||||
function SET_OPSGROUP:FilterPrefixes(Prefixes)
|
function SET_OPSGROUP:FilterPrefixes(Prefixes, Clear)
|
||||||
|
|
||||||
-- Create emtpy table if necessary.
|
-- Create emtpy table if necessary.
|
||||||
if not self.Filter.GroupPrefixes then
|
if Clear or not self.Filter.GroupPrefixes then
|
||||||
self.Filter.GroupPrefixes={}
|
self.Filter.GroupPrefixes={}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@ -68,7 +68,7 @@ ARMYGROUP = {
|
|||||||
|
|
||||||
--- Army Group version.
|
--- Army Group version.
|
||||||
-- @field #string version
|
-- @field #string version
|
||||||
ARMYGROUP.version="0.8.0"
|
ARMYGROUP.version="0.9.0"
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
-- TODO list
|
-- TODO list
|
||||||
@ -142,6 +142,7 @@ function ARMYGROUP:New(group)
|
|||||||
------------------------
|
------------------------
|
||||||
--- Pseudo Functions ---
|
--- Pseudo Functions ---
|
||||||
------------------------
|
------------------------
|
||||||
|
|
||||||
--- Triggers the FSM event "Cruise".
|
--- Triggers the FSM event "Cruise".
|
||||||
-- @function [parent=#ARMYGROUP] Cruise
|
-- @function [parent=#ARMYGROUP] Cruise
|
||||||
-- @param #ARMYGROUP self
|
-- @param #ARMYGROUP self
|
||||||
@ -253,10 +254,14 @@ function ARMYGROUP:New(group)
|
|||||||
--- Triggers the FSM event "Retreat".
|
--- Triggers the FSM event "Retreat".
|
||||||
-- @function [parent=#ARMYGROUP] Retreat
|
-- @function [parent=#ARMYGROUP] Retreat
|
||||||
-- @param #ARMYGROUP self
|
-- @param #ARMYGROUP self
|
||||||
|
-- @param Core.Zone#ZONE_BASE Zone (Optional) Zone where to retreat. Default is the closest retreat zone.
|
||||||
|
-- @param #number Formation (Optional) Formation of the group.
|
||||||
|
|
||||||
--- Triggers the FSM event "Retreat" after a delay.
|
--- Triggers the FSM event "Retreat" after a delay.
|
||||||
-- @function [parent=#ARMYGROUP] __Retreat
|
-- @function [parent=#ARMYGROUP] __Retreat
|
||||||
-- @param #ARMYGROUP self
|
-- @param #ARMYGROUP self
|
||||||
|
-- @param Core.Zone#ZONE_BASE Zone (Optional) Zone where to retreat. Default is the closest retreat zone.
|
||||||
|
-- @param #number Formation (Optional) Formation of the group.
|
||||||
-- @param #number delay Delay in seconds.
|
-- @param #number delay Delay in seconds.
|
||||||
|
|
||||||
--- On after "Retreat" event.
|
--- On after "Retreat" event.
|
||||||
@ -265,7 +270,8 @@ function ARMYGROUP:New(group)
|
|||||||
-- @param #string From From state.
|
-- @param #string From From state.
|
||||||
-- @param #string Event Event.
|
-- @param #string Event Event.
|
||||||
-- @param #string To To state.
|
-- @param #string To To state.
|
||||||
|
-- @param Core.Zone#ZONE_BASE Zone Zone where to retreat.
|
||||||
|
-- @param #number Formation Formation of the group. Can be #nil.
|
||||||
|
|
||||||
--- Triggers the FSM event "Retreated".
|
--- Triggers the FSM event "Retreated".
|
||||||
-- @function [parent=#ARMYGROUP] Retreated
|
-- @function [parent=#ARMYGROUP] Retreated
|
||||||
@ -287,7 +293,7 @@ function ARMYGROUP:New(group)
|
|||||||
--- Triggers the FSM event "EngageTarget".
|
--- Triggers the FSM event "EngageTarget".
|
||||||
-- @function [parent=#ARMYGROUP] EngageTarget
|
-- @function [parent=#ARMYGROUP] EngageTarget
|
||||||
-- @param #ARMYGROUP self
|
-- @param #ARMYGROUP self
|
||||||
-- @param Wrapper.Group#GROUP Group the group to be engaged.
|
-- @param Ops.Target#TARGET Target The target to be engaged. Can also be a GROUP or UNIT object.
|
||||||
-- @param #number Speed Speed in knots.
|
-- @param #number Speed Speed in knots.
|
||||||
-- @param #string Formation Formation used in the engagement.
|
-- @param #string Formation Formation used in the engagement.
|
||||||
|
|
||||||
@ -714,6 +720,25 @@ function ARMYGROUP:Status()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-- Get current mission (if any).
|
||||||
|
local mission=self:GetMissionCurrent()
|
||||||
|
|
||||||
|
-- If mission, check if DCS task needs to be updated.
|
||||||
|
if mission and mission.updateDCSTask and mission:GetGroupStatus(self)==AUFTRAG.GroupStatus.EXECUTING then
|
||||||
|
|
||||||
|
if mission.type==AUFTRAG.Type.CAPTUREZONE then
|
||||||
|
|
||||||
|
-- Get task.
|
||||||
|
local Task=mission:GetGroupWaypointTask(self)
|
||||||
|
|
||||||
|
-- Update task: Engage or get new zone.
|
||||||
|
self:_UpdateTask(Task, mission)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
else
|
else
|
||||||
-- Check damage of elements and group.
|
-- Check damage of elements and group.
|
||||||
@ -1010,6 +1035,9 @@ function ARMYGROUP:onbeforeUpdateRoute(From, Event, To, n, N, Speed, Formation)
|
|||||||
elseif task.dcstask.id==AUFTRAG.SpecialTask.RELOCATECOHORT then
|
elseif task.dcstask.id==AUFTRAG.SpecialTask.RELOCATECOHORT then
|
||||||
-- For relocate
|
-- For relocate
|
||||||
self:T2(self.lid.."Allowing update route for Task: Relocate Cohort")
|
self:T2(self.lid.."Allowing update route for Task: Relocate Cohort")
|
||||||
|
elseif task.dcstask.id==AUFTRAG.SpecialTask.REARMING then
|
||||||
|
-- For relocate
|
||||||
|
self:T2(self.lid.."Allowing update route for Task: Rearming")
|
||||||
else
|
else
|
||||||
local taskname=task and task.description or "No description"
|
local taskname=task and task.description or "No description"
|
||||||
self:T(self.lid..string.format("WARNING: Update route denied because taskcurrent=%d>0! Task description = %s", self.taskcurrent, tostring(taskname)))
|
self:T(self.lid..string.format("WARNING: Update route denied because taskcurrent=%d>0! Task description = %s", self.taskcurrent, tostring(taskname)))
|
||||||
@ -1339,7 +1367,7 @@ function ARMYGROUP:onafterDetour(From, Event, To, Coordinate, Speed, Formation,
|
|||||||
Speed=Speed or self:GetSpeedCruise()
|
Speed=Speed or self:GetSpeedCruise()
|
||||||
|
|
||||||
-- ID of current waypoint.
|
-- ID of current waypoint.
|
||||||
local uid=self:GetWaypointCurrent().uid
|
local uid=self:GetWaypointCurrentUID()
|
||||||
|
|
||||||
-- Add waypoint after current.
|
-- Add waypoint after current.
|
||||||
local wp=self:AddWaypoint(Coordinate, Speed, uid, Formation, true)
|
local wp=self:AddWaypoint(Coordinate, Speed, uid, Formation, true)
|
||||||
@ -1411,10 +1439,15 @@ function ARMYGROUP:onbeforeRearm(From, Event, To, Coordinate, Formation)
|
|||||||
|
|
||||||
-- Pause current mission.
|
-- Pause current mission.
|
||||||
if self:IsOnMission() then
|
if self:IsOnMission() then
|
||||||
self:T(self.lid.."Rearm command but have current mission ==> Pausing mission!")
|
local mission=self:GetMissionCurrent()
|
||||||
self:PauseMission()
|
if mission and mission.type~=AUFTRAG.Type.REARMING then
|
||||||
dt=-0.1
|
self:T(self.lid.."Rearm command but have current mission ==> Pausing mission!")
|
||||||
allowed=false
|
self:PauseMission()
|
||||||
|
dt=-0.1
|
||||||
|
allowed=false
|
||||||
|
else
|
||||||
|
self:T(self.lid.."Rearm command and current mission is REARMING ==> Transition ALLOWED!")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Disengage.
|
-- Disengage.
|
||||||
@ -1457,7 +1490,7 @@ function ARMYGROUP:onafterRearm(From, Event, To, Coordinate, Formation)
|
|||||||
self:T(self.lid..string.format("Group send to rearm"))
|
self:T(self.lid..string.format("Group send to rearm"))
|
||||||
|
|
||||||
-- ID of current waypoint.
|
-- ID of current waypoint.
|
||||||
local uid=self:GetWaypointCurrent().uid
|
local uid=self:GetWaypointCurrentUID()
|
||||||
|
|
||||||
-- Add waypoint after current.
|
-- Add waypoint after current.
|
||||||
local wp=self:AddWaypoint(Coordinate, nil, uid, Formation, true)
|
local wp=self:AddWaypoint(Coordinate, nil, uid, Formation, true)
|
||||||
@ -1480,6 +1513,7 @@ function ARMYGROUP:onafterRearmed(From, Event, To)
|
|||||||
|
|
||||||
-- Check if this is a rearming mission.
|
-- Check if this is a rearming mission.
|
||||||
if mission and mission.type==AUFTRAG.Type.REARMING then
|
if mission and mission.type==AUFTRAG.Type.REARMING then
|
||||||
|
|
||||||
-- Rearmed ==> Mission Done! This also checks if the group is done.
|
-- Rearmed ==> Mission Done! This also checks if the group is done.
|
||||||
self:MissionDone(mission)
|
self:MissionDone(mission)
|
||||||
|
|
||||||
@ -1649,10 +1683,14 @@ end
|
|||||||
function ARMYGROUP:onafterRetreat(From, Event, To, Zone, Formation)
|
function ARMYGROUP:onafterRetreat(From, Event, To, Zone, Formation)
|
||||||
|
|
||||||
-- ID of current waypoint.
|
-- ID of current waypoint.
|
||||||
local uid=self:GetWaypointCurrent().uid
|
local uid=self:GetWaypointCurrentUID()
|
||||||
|
|
||||||
|
-- Get random coordinate of the zone.
|
||||||
local Coordinate=Zone:GetRandomCoordinate()
|
local Coordinate=Zone:GetRandomCoordinate()
|
||||||
|
|
||||||
|
-- Debug info.
|
||||||
|
self:T(self.lid..string.format("Retreating to zone %s", Zone:GetName()))
|
||||||
|
|
||||||
-- Add waypoint after current.
|
-- Add waypoint after current.
|
||||||
local wp=self:AddWaypoint(Coordinate, nil, uid, Formation, true)
|
local wp=self:AddWaypoint(Coordinate, nil, uid, Formation, true)
|
||||||
|
|
||||||
@ -1702,10 +1740,11 @@ function ARMYGROUP:onbeforeEngageTarget(From, Event, To, Target, Speed, Formatio
|
|||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Pause current mission.
|
-- Get current mission.
|
||||||
local mission=self:GetMissionCurrent()
|
local mission=self:GetMissionCurrent()
|
||||||
|
|
||||||
if mission and mission.type~=AUFTRAG.Type.GROUNDATTACK then
|
-- Pause current mission unless it uses the EngageTarget command.
|
||||||
|
if mission and mission.type~=AUFTRAG.Type.GROUNDATTACK and mission.type~=AUFTRAG.Type.CAPTUREZONE then
|
||||||
self:T(self.lid.."Engage command but have current mission ==> Pausing mission!")
|
self:T(self.lid.."Engage command but have current mission ==> Pausing mission!")
|
||||||
self:PauseMission()
|
self:PauseMission()
|
||||||
dt=-0.1
|
dt=-0.1
|
||||||
@ -1727,7 +1766,7 @@ end
|
|||||||
-- @param #string From From state.
|
-- @param #string From From state.
|
||||||
-- @param #string Event Event.
|
-- @param #string Event Event.
|
||||||
-- @param #string To To state.
|
-- @param #string To To state.
|
||||||
-- @param Wrapper.Group#GROUP Group the group to be engaged.
|
-- @param Ops.Target#TARGET Target The target to be engaged. Can also be a group or unit.
|
||||||
-- @param #number Speed Attack speed in knots.
|
-- @param #number Speed Attack speed in knots.
|
||||||
-- @param #string Formation Formation used in the engagement. Default `ENUMS.Formation.Vehicle.Vee`.
|
-- @param #string Formation Formation used in the engagement. Default `ENUMS.Formation.Vehicle.Vee`.
|
||||||
function ARMYGROUP:onafterEngageTarget(From, Event, To, Target, Speed, Formation)
|
function ARMYGROUP:onafterEngageTarget(From, Event, To, Target, Speed, Formation)
|
||||||
@ -1785,8 +1824,8 @@ function ARMYGROUP:_UpdateEngageTarget()
|
|||||||
-- Distance to last known position of target.
|
-- Distance to last known position of target.
|
||||||
local dist=UTILS.VecDist3D(vec3, self.engage.Coordinate:GetVec3())
|
local dist=UTILS.VecDist3D(vec3, self.engage.Coordinate:GetVec3())
|
||||||
|
|
||||||
-- Check if target moved more than 100 meters.
|
-- Check if target moved more than 100 meters or we do not have line of sight.
|
||||||
if dist>100 then
|
if dist>100 or not self:HasLoS(self.engage.Target:GetCoordinate()) then
|
||||||
|
|
||||||
--env.info("FF Update Engage Target Moved "..self.engage.Target:GetName())
|
--env.info("FF Update Engage Target Moved "..self.engage.Target:GetName())
|
||||||
|
|
||||||
@ -1794,7 +1833,7 @@ function ARMYGROUP:_UpdateEngageTarget()
|
|||||||
self.engage.Coordinate:UpdateFromVec3(vec3)
|
self.engage.Coordinate:UpdateFromVec3(vec3)
|
||||||
|
|
||||||
-- ID of current waypoint.
|
-- ID of current waypoint.
|
||||||
local uid=self:GetWaypointCurrent().uid
|
local uid=self:GetWaypointCurrentUID()
|
||||||
|
|
||||||
-- Remove current waypoint
|
-- Remove current waypoint
|
||||||
self:RemoveWaypointByID(self.engage.Waypoint.uid)
|
self:RemoveWaypointByID(self.engage.Waypoint.uid)
|
||||||
@ -1805,7 +1844,7 @@ function ARMYGROUP:_UpdateEngageTarget()
|
|||||||
self.engage.Waypoint=self:AddWaypoint(intercoord, self.engage.Speed, uid, self.engage.Formation, true)
|
self.engage.Waypoint=self:AddWaypoint(intercoord, self.engage.Speed, uid, self.engage.Formation, true)
|
||||||
|
|
||||||
-- Set if we want to resume route after reaching the detour waypoint.
|
-- Set if we want to resume route after reaching the detour waypoint.
|
||||||
self.engage.Waypoint.detour=0
|
self.engage.Waypoint.detour=0
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -1952,8 +1991,10 @@ function ARMYGROUP:AddWaypoint(Coordinate, Speed, AfterWaypointWithID, Formation
|
|||||||
if not Formation then
|
if not Formation then
|
||||||
if self.formationPerma then
|
if self.formationPerma then
|
||||||
Formation = self.formationPerma
|
Formation = self.formationPerma
|
||||||
|
elseif self.optionDefault.Formation then
|
||||||
|
Formation = self.optionDefault.Formation
|
||||||
elseif self.option.Formation then
|
elseif self.option.Formation then
|
||||||
Formation = self.option.Formation
|
Formation = self.option.Formation
|
||||||
else
|
else
|
||||||
-- Default formation is on road.
|
-- Default formation is on road.
|
||||||
Formation = ENUMS.Formation.Vehicle.OnRoad
|
Formation = ENUMS.Formation.Vehicle.OnRoad
|
||||||
@ -2037,8 +2078,11 @@ function ARMYGROUP:_InitGroup(Template)
|
|||||||
-- Set default radio.
|
-- Set default radio.
|
||||||
self:SetDefaultRadio(self.radio.Freq, self.radio.Modu, self.radio.On)
|
self:SetDefaultRadio(self.radio.Freq, self.radio.Modu, self.radio.On)
|
||||||
|
|
||||||
-- Set default formation from first waypoint.
|
-- Get current formation from first waypoint.
|
||||||
self.optionDefault.Formation=template.route.points[1].action --self:GetWaypoint(1).action
|
self.option.Formation=template.route.points[1].action
|
||||||
|
|
||||||
|
-- Set default formation to "on road".
|
||||||
|
self.optionDefault.Formation=ENUMS.Formation.Vehicle.OnRoad
|
||||||
|
|
||||||
-- Default TACAN off.
|
-- Default TACAN off.
|
||||||
self:SetDefaultTACAN(nil, nil, nil, nil, true)
|
self:SetDefaultTACAN(nil, nil, nil, nil, true)
|
||||||
|
|||||||
@ -422,7 +422,8 @@ _AUFTRAGSNR=0
|
|||||||
-- @field #string AIRDEFENSE Air defense.
|
-- @field #string AIRDEFENSE Air defense.
|
||||||
-- @field #string EWR Early Warning Radar.
|
-- @field #string EWR Early Warning Radar.
|
||||||
-- @field #string RECOVERYTANKER Recovery tanker.
|
-- @field #string RECOVERYTANKER Recovery tanker.
|
||||||
-- @filed #string REARMING Rearming mission.
|
-- @field #string REARMING Rearming mission.
|
||||||
|
-- @field #string CAPTUREZONE Capture zone mission.
|
||||||
-- @field #string NOTHING Nothing.
|
-- @field #string NOTHING Nothing.
|
||||||
AUFTRAG.Type={
|
AUFTRAG.Type={
|
||||||
ANTISHIP="Anti Ship",
|
ANTISHIP="Anti Ship",
|
||||||
@ -465,6 +466,7 @@ AUFTRAG.Type={
|
|||||||
EWR="Early Warning Radar",
|
EWR="Early Warning Radar",
|
||||||
RECOVERYTANKER="Recovery Tanker",
|
RECOVERYTANKER="Recovery Tanker",
|
||||||
REARMING="Rearming",
|
REARMING="Rearming",
|
||||||
|
CAPTUREZONE="Capture Zone",
|
||||||
NOTHING="Nothing",
|
NOTHING="Nothing",
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -487,6 +489,7 @@ AUFTRAG.Type={
|
|||||||
-- @field #string EWR Early Warning Radar.
|
-- @field #string EWR Early Warning Radar.
|
||||||
-- @field #string RECOVERYTANKER Recovery tanker.
|
-- @field #string RECOVERYTANKER Recovery tanker.
|
||||||
-- @field #string REARMING Rearming.
|
-- @field #string REARMING Rearming.
|
||||||
|
-- @field #string CAPTUREZONE Capture OPS zone.
|
||||||
-- @field #string NOTHING Nothing.
|
-- @field #string NOTHING Nothing.
|
||||||
AUFTRAG.SpecialTask={
|
AUFTRAG.SpecialTask={
|
||||||
FORMATION="Formation",
|
FORMATION="Formation",
|
||||||
@ -507,6 +510,7 @@ AUFTRAG.SpecialTask={
|
|||||||
EWR="Early Warning Radar",
|
EWR="Early Warning Radar",
|
||||||
RECOVERYTANKER="Recovery Tanker",
|
RECOVERYTANKER="Recovery Tanker",
|
||||||
REARMING="Rearming",
|
REARMING="Rearming",
|
||||||
|
CAPTUREZONE="Capture Zone",
|
||||||
NOTHING="Nothing",
|
NOTHING="Nothing",
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -628,7 +632,7 @@ AUFTRAG.Category={
|
|||||||
|
|
||||||
--- AUFTRAG class version.
|
--- AUFTRAG class version.
|
||||||
-- @field #string version
|
-- @field #string version
|
||||||
AUFTRAG.version="0.9.7"
|
AUFTRAG.version="0.9.9"
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
-- TODO list
|
-- TODO list
|
||||||
@ -637,6 +641,7 @@ AUFTRAG.version="0.9.7"
|
|||||||
-- TODO: Replace engageRange by missionRange. Here and in other classes. CTRL+H is your friend!
|
-- TODO: Replace engageRange by missionRange. Here and in other classes. CTRL+H is your friend!
|
||||||
-- TODO: Mission success options damaged, destroyed.
|
-- TODO: Mission success options damaged, destroyed.
|
||||||
-- TODO: F10 marker to create new missions.
|
-- TODO: F10 marker to create new missions.
|
||||||
|
-- DONE: Add Capture zone task.
|
||||||
-- DONE: Add orbit mission for moving anker points.
|
-- DONE: Add orbit mission for moving anker points.
|
||||||
-- DONE: Add recovery tanker mission for boat ops.
|
-- DONE: Add recovery tanker mission for boat ops.
|
||||||
-- DONE: Added auftrag category.
|
-- DONE: Added auftrag category.
|
||||||
@ -2020,6 +2025,51 @@ function AUFTRAG:NewPATROLZONE(Zone, Speed, Altitude, Formation)
|
|||||||
return mission
|
return mission
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- **[AIR, GROUND, NAVAL]** Create a CAPTUREZONE mission. Group(s) will go to the zone and patrol it randomly.
|
||||||
|
-- @param #AUFTRAG self
|
||||||
|
-- @param Ops.OpsZone#OPSZONE OpsZone The OPS zone to capture.
|
||||||
|
-- @param #number Coalition The coalition which should capture the zone for the mission to be successful.
|
||||||
|
-- @param #number Speed Speed in knots.
|
||||||
|
-- @param #number Altitude Altitude in feet. Only for airborne units. Default 2000 feet ASL.
|
||||||
|
-- @param #string Formation Formation used by ground units during patrol. Default "Off Road".
|
||||||
|
-- @return #AUFTRAG self
|
||||||
|
function AUFTRAG:NewCAPTUREZONE(OpsZone, Coalition, Speed, Altitude, Formation)
|
||||||
|
|
||||||
|
local mission=AUFTRAG:New(AUFTRAG.Type.CAPTUREZONE)
|
||||||
|
|
||||||
|
|
||||||
|
mission:_TargetFromObject(OpsZone)
|
||||||
|
|
||||||
|
mission.coalition=Coalition
|
||||||
|
|
||||||
|
mission.missionTask=mission:GetMissionTaskforMissionType(AUFTRAG.Type.CAPTUREZONE)
|
||||||
|
|
||||||
|
mission.optionROE=ENUMS.ROE.ReturnFire
|
||||||
|
mission.optionROT=ENUMS.ROT.PassiveDefense
|
||||||
|
mission.optionAlarm=ENUMS.AlarmState.Auto
|
||||||
|
|
||||||
|
mission.missionFraction=0.1
|
||||||
|
mission.missionSpeed=Speed and UTILS.KnotsToKmph(Speed) or nil
|
||||||
|
mission.missionAltitude=Altitude and UTILS.FeetToMeters(Altitude) or nil
|
||||||
|
|
||||||
|
mission.categories={AUFTRAG.Category.ALL}
|
||||||
|
|
||||||
|
mission.DCStask=mission:GetDCSMissionTask()
|
||||||
|
|
||||||
|
mission.updateDCSTask=true
|
||||||
|
|
||||||
|
local params={}
|
||||||
|
|
||||||
|
params.formation=Formation or "Off Road"
|
||||||
|
params.zone=mission:GetObjective()
|
||||||
|
params.altitude=mission.missionAltitude
|
||||||
|
params.speed=mission.missionSpeed
|
||||||
|
|
||||||
|
mission.DCStask.params=params
|
||||||
|
|
||||||
|
return mission
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
--- **[OBSOLETE]** Create a ARMORATTACK mission.
|
--- **[OBSOLETE]** Create a ARMORATTACK mission.
|
||||||
-- ** Note that this is actually creating a GROUNDATTACK mission!**
|
-- ** Note that this is actually creating a GROUNDATTACK mission!**
|
||||||
@ -2663,6 +2713,15 @@ function AUFTRAG:SetRepeatOnSuccess(Nrepeat)
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- **[LEGION, COMMANDER, CHIEF]** Set that mission assets get reinforced if their number drops below Nmin.
|
||||||
|
-- @param #AUFTRAG self
|
||||||
|
-- @param #number Nreinforce Number of max asset groups used to reinforce.
|
||||||
|
-- @return #AUFTRAG self
|
||||||
|
function AUFTRAG:SetReinforce(Nreinforce)
|
||||||
|
self.reinforce=Nreinforce
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
--- **[LEGION, COMMANDER, CHIEF]** Define how many assets are required to do the job. Only used if the mission is handled by a **LEGION** (AIRWING, BRIGADE, ...) or higher level.
|
--- **[LEGION, COMMANDER, CHIEF]** Define how many assets are required to do the job. Only used if the mission is handled by a **LEGION** (AIRWING, BRIGADE, ...) or higher level.
|
||||||
-- @param #AUFTRAG self
|
-- @param #AUFTRAG self
|
||||||
-- @param #number NassetsMin Minimum number of asset groups. Default 1.
|
-- @param #number NassetsMin Minimum number of asset groups. Default 1.
|
||||||
@ -2684,24 +2743,32 @@ end
|
|||||||
|
|
||||||
--- **[LEGION, COMMANDER, CHIEF]** Get number of required assets.
|
--- **[LEGION, COMMANDER, CHIEF]** Get number of required assets.
|
||||||
-- @param #AUFTRAG self
|
-- @param #AUFTRAG self
|
||||||
-- @param Ops.Legion#Legion Legion (Optional) Only get the required assets for a specific legion. If required assets for this legion are not defined, the total number is returned.
|
|
||||||
-- @return #number Min. number of required assets.
|
-- @return #number Min. number of required assets.
|
||||||
-- @return #number Max. number of required assets.
|
-- @return #number Max. number of required assets.
|
||||||
function AUFTRAG:GetRequiredAssets(Legion)
|
function AUFTRAG:GetRequiredAssets()
|
||||||
|
|
||||||
--local N=self.nassets
|
|
||||||
|
|
||||||
--if Legion and self.Nassets[Legion.alias] then
|
|
||||||
-- N=self.Nassets[Legion.alias]
|
|
||||||
--end
|
|
||||||
|
|
||||||
local Nmin=self.NassetsMin
|
local Nmin=self.NassetsMin
|
||||||
local Nmax=self.NassetsMax
|
local Nmax=self.NassetsMax
|
||||||
|
|
||||||
if self.type==AUFTRAG.Type.RELOCATECOHORT then
|
if self.type==AUFTRAG.Type.RELOCATECOHORT then
|
||||||
|
|
||||||
|
-- Relocation gets all the assets.
|
||||||
local cohort=self.DCStask.params.cohort --Ops.Cohort#COHORT
|
local cohort=self.DCStask.params.cohort --Ops.Cohort#COHORT
|
||||||
Nmin=#cohort.assets
|
Nmin=#cohort.assets
|
||||||
Nmax=Nmin
|
Nmax=Nmin
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
-- Check if this is an reinforcement.
|
||||||
|
if self:IsExecuting() and self.reinforce and self.reinforce>0 then
|
||||||
|
local N=self:CountOpsGroups()
|
||||||
|
if N<Nmin then
|
||||||
|
Nmin=math.min(Nmin-N, self.reinforce)
|
||||||
|
Nmax=Nmin
|
||||||
|
self:T(self.lid..string.format("FF Executing Nmin=%d, N=%d, Nreinfoce=%d ==> Nmin=%d", self.NassetsMin, N, self.reinforce, Nmin))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
return Nmin, Nmax
|
return Nmin, Nmax
|
||||||
@ -2961,6 +3028,7 @@ function AUFTRAG:AddTransportCarriers(Carriers)
|
|||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- **[LEGION, COMMANDER, CHIEF]** Set required attribute(s) the assets must have.
|
--- **[LEGION, COMMANDER, CHIEF]** Set required attribute(s) the assets must have.
|
||||||
@ -2968,10 +3036,8 @@ end
|
|||||||
-- @param #table Attributes Generalized attribute(s).
|
-- @param #table Attributes Generalized attribute(s).
|
||||||
-- @return #AUFTRAG self
|
-- @return #AUFTRAG self
|
||||||
function AUFTRAG:SetRequiredAttribute(Attributes)
|
function AUFTRAG:SetRequiredAttribute(Attributes)
|
||||||
if Attributes and type(Attributes)~="table" then
|
self.attributes=UTILS.EnsureTable(Attributes, true)
|
||||||
Attributes={Attributes}
|
return self
|
||||||
end
|
|
||||||
self.attributes=Attributes
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--- **[LEGION, COMMANDER, CHIEF]** Set required property or properties the assets must have.
|
--- **[LEGION, COMMANDER, CHIEF]** Set required property or properties the assets must have.
|
||||||
@ -2980,10 +3046,8 @@ end
|
|||||||
-- @param #table Properties Property or table of properties.
|
-- @param #table Properties Property or table of properties.
|
||||||
-- @return #AUFTRAG self
|
-- @return #AUFTRAG self
|
||||||
function AUFTRAG:SetRequiredProperty(Properties)
|
function AUFTRAG:SetRequiredProperty(Properties)
|
||||||
if Properties and type(Properties)~="table" then
|
self.properties=UTILS.EnsureTable(Properties, true)
|
||||||
Properties={Properties}
|
return self
|
||||||
end
|
|
||||||
self.properties=Properties
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--- **[LEGION, COMMANDER, CHIEF]** Set number of required carrier groups if an OPSTRANSPORT assignment is required.
|
--- **[LEGION, COMMANDER, CHIEF]** Set number of required carrier groups if an OPSTRANSPORT assignment is required.
|
||||||
@ -3854,7 +3918,7 @@ function AUFTRAG:onafterStatus(From, Event, To)
|
|||||||
self:T(self.lid.."No targets left cancelling mission!")
|
self:T(self.lid.."No targets left cancelling mission!")
|
||||||
self:Cancel()
|
self:Cancel()
|
||||||
|
|
||||||
elseif self:IsExecuting() then
|
elseif self:IsExecuting() and ((not self.reinforce) or self.reinforce==0) then
|
||||||
|
|
||||||
-- Had the case that mission was in state Executing but all assigned groups were dead.
|
-- Had the case that mission was in state Executing but all assigned groups were dead.
|
||||||
-- TODO: might need to loop over all assigned groups
|
-- TODO: might need to loop over all assigned groups
|
||||||
@ -4383,6 +4447,12 @@ function AUFTRAG:CheckGroupsDone()
|
|||||||
self:T2(self.lid..string.format("CheckGroupsDone: Mission is still in state %s [FSM=%s] (PLANNED or QUEUED or REQUESTED). Mission NOT DONE!", self.status, self:GetState()))
|
self:T2(self.lid..string.format("CheckGroupsDone: Mission is still in state %s [FSM=%s] (PLANNED or QUEUED or REQUESTED). Mission NOT DONE!", self.status, self:GetState()))
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Check if there is still reinforcement to be expected.
|
||||||
|
if self:IsExecuting() and self.reinforce and self.reinforce>0 then
|
||||||
|
self:T2(self.lid..string.format("CheckGroupsDone: Mission is still in state %s [FSM=%s] and reinfoce=%d. Mission NOT DONE!", self.status, self:GetState(), self.reinforce))
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
-- It could be that all flights were destroyed on the way to the mission execution waypoint.
|
-- It could be that all flights were destroyed on the way to the mission execution waypoint.
|
||||||
-- TODO: would be better to check if everybody is dead by now.
|
-- TODO: would be better to check if everybody is dead by now.
|
||||||
@ -4542,7 +4612,7 @@ function AUFTRAG:onafterAssetDead(From, Event, To, Asset)
|
|||||||
self:T(self.lid..string.format("Asset %s dead! Number of ops groups remaining %d", tostring(Asset.spawngroupname), N))
|
self:T(self.lid..string.format("Asset %s dead! Number of ops groups remaining %d", tostring(Asset.spawngroupname), N))
|
||||||
|
|
||||||
-- All assets dead?
|
-- All assets dead?
|
||||||
if N==0 then
|
if N==0 and (self.reinforce==nil or self.reinforce==0) then
|
||||||
|
|
||||||
if self:IsNotOver() then
|
if self:IsNotOver() then
|
||||||
|
|
||||||
@ -4983,8 +5053,11 @@ function AUFTRAG:CountMissionTargets()
|
|||||||
|
|
||||||
local N=0
|
local N=0
|
||||||
|
|
||||||
|
-- Count specific coalitions.
|
||||||
|
local Coalitions=self.coalition and UTILS.GetCoalitionEnemy(self.coalition, true) or nil
|
||||||
|
|
||||||
if self.engageTarget then
|
if self.engageTarget then
|
||||||
N=self.engageTarget:CountTargets()
|
N=self.engageTarget:CountTargets(Coalitions)
|
||||||
end
|
end
|
||||||
|
|
||||||
return N
|
return N
|
||||||
@ -5049,9 +5122,13 @@ end
|
|||||||
|
|
||||||
--- Get mission objective object. Could be many things depending on the mission type.
|
--- Get mission objective object. Could be many things depending on the mission type.
|
||||||
-- @param #AUFTRAG self
|
-- @param #AUFTRAG self
|
||||||
|
-- @param Core.Point#COORDINATE RefCoordinate (Optional) Reference coordinate from which the closest target is determined.
|
||||||
|
-- @param #table Coalitions (Optional) Only consider targets of the given coalition(s).
|
||||||
-- @return Wrapper.Positionable#POSITIONABLE The target object. Could be many things.
|
-- @return Wrapper.Positionable#POSITIONABLE The target object. Could be many things.
|
||||||
function AUFTRAG:GetObjective()
|
function AUFTRAG:GetObjective(RefCoordinate, Coalitions)
|
||||||
local objective=self:GetTargetData():GetObject()
|
|
||||||
|
local objective=self:GetTargetData():GetObject(RefCoordinate, Coalitions)
|
||||||
|
|
||||||
return objective
|
return objective
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -5789,6 +5866,22 @@ function AUFTRAG:GetDCSMissionTask()
|
|||||||
DCStask.params=param
|
DCStask.params=param
|
||||||
|
|
||||||
table.insert(DCStasks, DCStask)
|
table.insert(DCStasks, DCStask)
|
||||||
|
|
||||||
|
elseif self.type==AUFTRAG.Type.CAPTUREZONE then
|
||||||
|
|
||||||
|
--------------------------
|
||||||
|
-- CAPTURE ZONE Mission --
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
local DCStask={}
|
||||||
|
|
||||||
|
DCStask.id=AUFTRAG.SpecialTask.CAPTUREZONE
|
||||||
|
|
||||||
|
-- We create a "fake" DCS task and pass the parameters to the FLIGHTGROUP.
|
||||||
|
local param={}
|
||||||
|
DCStask.params=param
|
||||||
|
|
||||||
|
table.insert(DCStasks, DCStask)
|
||||||
|
|
||||||
elseif self.type==AUFTRAG.Type.CASENHANCED then
|
elseif self.type==AUFTRAG.Type.CASENHANCED then
|
||||||
|
|
||||||
@ -5866,7 +5959,7 @@ function AUFTRAG:GetDCSMissionTask()
|
|||||||
|
|
||||||
table.insert(DCStasks, DCStask)
|
table.insert(DCStasks, DCStask)
|
||||||
|
|
||||||
elseif self.type==AUFTRAG.Type.AMMOSUPPLY then
|
elseif self.type==AUFTRAG.Type.REARMING then
|
||||||
|
|
||||||
----------------------
|
----------------------
|
||||||
-- REARMING Mission --
|
-- REARMING Mission --
|
||||||
|
|||||||
@ -176,7 +176,11 @@ function COHORT:New(TemplateGroupName, Ngroups, CohortName)
|
|||||||
for i,_unit in pairs(units) do
|
for i,_unit in pairs(units) do
|
||||||
local unit=_unit --Wrapper.Unit#UNIT
|
local unit=_unit --Wrapper.Unit#UNIT
|
||||||
local desc=unit:GetDesc()
|
local desc=unit:GetDesc()
|
||||||
self.weightAsset=self.weightAsset + (desc.massMax or 666)
|
local mass=666
|
||||||
|
if desc then
|
||||||
|
mass=desc.massMax or desc.massEmpty
|
||||||
|
end
|
||||||
|
self.weightAsset=self.weightAsset + (mass or 666)
|
||||||
if i==1 then
|
if i==1 then
|
||||||
self.cargobayLimit=unit:GetCargoBayFreeWeight()
|
self.cargobayLimit=unit:GetCargoBayFreeWeight()
|
||||||
end
|
end
|
||||||
|
|||||||
@ -1671,25 +1671,51 @@ function COMMANDER:RecruitAssetsForMission(Mission)
|
|||||||
|
|
||||||
-- Debug info.
|
-- Debug info.
|
||||||
self:T2(self.lid..string.format("Recruiting assets for mission \"%s\" [%s]", Mission:GetName(), Mission:GetType()))
|
self:T2(self.lid..string.format("Recruiting assets for mission \"%s\" [%s]", Mission:GetName(), Mission:GetType()))
|
||||||
|
|
||||||
-- Cohorts.
|
|
||||||
local Cohorts=self:_GetCohorts(Mission.specialLegions, Mission.specialCohorts, Mission.operation)
|
|
||||||
|
|
||||||
-- Debug info.
|
|
||||||
self:T(self.lid..string.format("Found %d cohort candidates for mission", #Cohorts))
|
|
||||||
|
|
||||||
-- Number of required assets.
|
-- Number of required assets.
|
||||||
local NreqMin, NreqMax=Mission:GetRequiredAssets()
|
local NreqMin, NreqMax=Mission:GetRequiredAssets()
|
||||||
|
|
||||||
-- Target position.
|
-- Target position.
|
||||||
local TargetVec2=Mission:GetTargetVec2()
|
local TargetVec2=Mission:GetTargetVec2()
|
||||||
|
|
||||||
-- Special payloads.
|
-- Special payloads.
|
||||||
local Payloads=Mission.payloads
|
local Payloads=Mission.payloads
|
||||||
|
|
||||||
|
-- Largest cargo bay available of available carrier assets if mission assets need to be transported.
|
||||||
|
local MaxWeight=nil
|
||||||
|
|
||||||
|
if Mission.NcarriersMin then
|
||||||
|
|
||||||
|
-- Get transport cohorts.
|
||||||
|
local Cohorts=LEGION._GetCohorts(Mission.transportLegions or self.legions, Mission.transportCohorts)
|
||||||
|
|
||||||
|
-- Filter cohorts that can actually perform transport missions.
|
||||||
|
local transportcohorts={}
|
||||||
|
for _,_cohort in pairs(Cohorts) do
|
||||||
|
local cohort=_cohort --Ops.Cohort#COHORT
|
||||||
|
|
||||||
|
-- Check if cohort can perform transport to target.
|
||||||
|
--TODO: Option to filter transport carrier asset categories, attributes and/or properties.
|
||||||
|
local can=LEGION._CohortCan(cohort, AUFTRAG.Type.OPSTRANSPORT, Categories, Attributes, Properties, nil, TargetVec2)
|
||||||
|
|
||||||
|
-- MaxWeight of cargo assets is limited by the largets available cargo bay. We don't want to select, e.g., tanks that cannot be transported by APCs or helos.
|
||||||
|
if can and (MaxWeight==nil or cohort.cargobayLimit>MaxWeight) then
|
||||||
|
MaxWeight=cohort.cargobayLimit
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
self:T(self.lid..string.format("Largest cargo bay available=%.1f", MaxWeight))
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Get cohorts.
|
||||||
|
local Cohorts=LEGION._GetCohorts(Mission.specialLegions or self.legions, Mission.specialCohorts, Mission.operation, self.opsqueue)
|
||||||
|
|
||||||
|
-- Debug info.
|
||||||
|
self:T(self.lid..string.format("Found %d cohort candidates for mission", #Cohorts))
|
||||||
|
|
||||||
-- Recruite assets.
|
-- Recruite assets.
|
||||||
local recruited, assets, legions=LEGION.RecruitCohortAssets(Cohorts, Mission.type, Mission.alert5MissionType, NreqMin, NreqMax, TargetVec2, Payloads,
|
local recruited, assets, legions=LEGION.RecruitCohortAssets(Cohorts, Mission.type, Mission.alert5MissionType, NreqMin, NreqMax, TargetVec2, Payloads,
|
||||||
Mission.engageRange, Mission.refuelSystem, nil, nil, nil, Mission.attributes, Mission.properties, {Mission.engageWeaponType})
|
Mission.engageRange, Mission.refuelSystem, nil, nil, MaxWeight, nil, Mission.attributes, Mission.properties, {Mission.engageWeaponType})
|
||||||
|
|
||||||
return recruited, assets, legions
|
return recruited, assets, legions
|
||||||
end
|
end
|
||||||
|
|||||||
@ -47,7 +47,7 @@ LEGION = {
|
|||||||
|
|
||||||
--- LEGION class version.
|
--- LEGION class version.
|
||||||
-- @field #string version
|
-- @field #string version
|
||||||
LEGION.version="0.3.4"
|
LEGION.version="0.4.0"
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
-- ToDo list
|
-- ToDo list
|
||||||
@ -665,9 +665,24 @@ function LEGION:CheckMissionQueue()
|
|||||||
-- Look for first task that is not accomplished.
|
-- Look for first task that is not accomplished.
|
||||||
for _,_mission in pairs(self.missionqueue) do
|
for _,_mission in pairs(self.missionqueue) do
|
||||||
local mission=_mission --Ops.Auftrag#AUFTRAG
|
local mission=_mission --Ops.Auftrag#AUFTRAG
|
||||||
|
|
||||||
|
-- Check if reinforcement is necessary.
|
||||||
|
local reinforce=false
|
||||||
|
if mission:IsExecuting() and mission.reinforce and mission.reinforce>0 then
|
||||||
|
|
||||||
|
local N=mission:CountOpsGroups()
|
||||||
|
|
||||||
|
local Nmin, Nmax=mission:GetRequiredAssets()
|
||||||
|
|
||||||
|
if N<Nmin then
|
||||||
|
reinforce=true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
mission:CountOpsGroups()
|
||||||
|
|
||||||
-- Firstly, check if mission is due?
|
-- Firstly, check if mission is due?
|
||||||
if mission:IsQueued(self) and mission:IsReadyToGo() and (mission.importance==nil or mission.importance<=vip) then
|
if (mission:IsQueued(self) or reinforce) and mission:IsReadyToGo() and (mission.importance==nil or mission.importance<=vip) then
|
||||||
|
|
||||||
-- Recruit best assets for the job.
|
-- Recruit best assets for the job.
|
||||||
local recruited, assets, legions=self:RecruitAssetsForMission(mission)
|
local recruited, assets, legions=self:RecruitAssetsForMission(mission)
|
||||||
@ -693,8 +708,11 @@ function LEGION:CheckMissionQueue()
|
|||||||
-- Recruit carrier assets for transport.
|
-- Recruit carrier assets for transport.
|
||||||
local Transport=nil
|
local Transport=nil
|
||||||
if mission.NcarriersMin then
|
if mission.NcarriersMin then
|
||||||
|
|
||||||
|
-- Transport legions.
|
||||||
local Legions=mission.transportLegions or {self}
|
local Legions=mission.transportLegions or {self}
|
||||||
|
|
||||||
|
-- Assign carrier assets for transport.
|
||||||
TransportAvail, Transport=self:AssignAssetsForTransport(Legions, assets, mission.NcarriersMin, mission.NcarriersMax, mission.transportDeployZone, mission.transportDisembarkZone)
|
TransportAvail, Transport=self:AssignAssetsForTransport(Legions, assets, mission.NcarriersMin, mission.NcarriersMax, mission.transportDeployZone, mission.transportDisembarkZone)
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -706,8 +724,10 @@ function LEGION:CheckMissionQueue()
|
|||||||
end
|
end
|
||||||
|
|
||||||
if EscortAvail and TransportAvail then
|
if EscortAvail and TransportAvail then
|
||||||
|
|
||||||
-- Got a mission.
|
-- Got a mission.
|
||||||
self:MissionRequest(mission)
|
self:MissionRequest(mission)
|
||||||
|
|
||||||
return true
|
return true
|
||||||
else
|
else
|
||||||
-- Recruited assets but no requested escort available. Unrecruit assets!
|
-- Recruited assets but no requested escort available. Unrecruit assets!
|
||||||
@ -2133,29 +2153,39 @@ function LEGION:RecruitAssetsForMission(Mission)
|
|||||||
|
|
||||||
-- Payloads.
|
-- Payloads.
|
||||||
local Payloads=Mission.payloads
|
local Payloads=Mission.payloads
|
||||||
|
|
||||||
-- Get special escort legions and/or cohorts.
|
-- Largest cargo bay available of available carrier assets if mission assets need to be transported.
|
||||||
local Cohorts={}
|
local MaxWeight=nil
|
||||||
for _,_legion in pairs(Mission.specialLegions or {}) do
|
|
||||||
local legion=_legion --Ops.Legion#LEGION
|
if Mission.NcarriersMin then
|
||||||
for _,_cohort in pairs(legion.cohorts) do
|
|
||||||
local cohort=_cohort --Ops.Cohort#COHORT
|
-- Get transport cohorts.
|
||||||
table.insert(Cohorts, cohort)
|
local Cohorts=LEGION._GetCohorts(Mission.transportLegions or {self}, Mission.transportCohorts or self.cohorts)
|
||||||
end
|
|
||||||
end
|
|
||||||
for _,_cohort in pairs(Mission.specialCohorts or {}) do
|
|
||||||
local cohort=_cohort --Ops.Cohort#COHORT
|
|
||||||
table.insert(Cohorts, cohort)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- No escort cohorts/legions given ==> take own cohorts.
|
|
||||||
if #Cohorts==0 then
|
|
||||||
Cohorts=self.cohorts
|
|
||||||
end
|
|
||||||
|
|
||||||
|
-- Filter cohorts that can actually perform transport missions.
|
||||||
|
local transportcohorts={}
|
||||||
|
for _,_cohort in pairs(Cohorts) do
|
||||||
|
local cohort=_cohort --Ops.Cohort#COHORT
|
||||||
|
|
||||||
|
-- Check if cohort can perform transport to target.
|
||||||
|
--TODO: Option to filter transport carrier asset categories, attributes and/or properties.
|
||||||
|
local can=LEGION._CohortCan(cohort, AUFTRAG.Type.OPSTRANSPORT, Categories, Attributes, Properties, nil, TargetVec2)
|
||||||
|
|
||||||
|
-- MaxWeight of cargo assets is limited by the largets available cargo bay. We don't want to select, e.g., tanks that cannot be transported by APCs or helos.
|
||||||
|
if can and (MaxWeight==nil or cohort.cargobayLimit>MaxWeight) then
|
||||||
|
MaxWeight=cohort.cargobayLimit
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
self:T(self.lid..string.format("Largest cargo bay available=%.1f", MaxWeight))
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Get cohorts.
|
||||||
|
local Cohorts=LEGION._GetCohorts(Mission.specialLegions or {self}, Mission.specialCohorts or self.cohorts, Operation, OpsQueue)
|
||||||
|
|
||||||
-- Recuit assets.
|
-- Recuit assets.
|
||||||
local recruited, assets, legions=LEGION.RecruitCohortAssets(Cohorts, Mission.type, Mission.alert5MissionType, NreqMin, NreqMax, TargetVec2, Payloads,
|
local recruited, assets, legions=LEGION.RecruitCohortAssets(Cohorts, Mission.type, Mission.alert5MissionType, NreqMin, NreqMax, TargetVec2, Payloads,
|
||||||
Mission.engageRange, Mission.refuelSystem, nil, nil, nil, Mission.attributes, Mission.properties, {Mission.engageWeaponType})
|
Mission.engageRange, Mission.refuelSystem, nil, nil, MaxWeight, nil, Mission.attributes, Mission.properties, {Mission.engageWeaponType})
|
||||||
|
|
||||||
return recruited, assets, legions
|
return recruited, assets, legions
|
||||||
end
|
end
|
||||||
@ -2249,42 +2279,118 @@ function LEGION:RecruitAssetsForEscort(Mission, Assets)
|
|||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Get cohorts.
|
||||||
|
-- @param #table Legions Special legions.
|
||||||
|
-- @param #table Cohorts Special cohorts.
|
||||||
|
-- @param Ops.Operation#OPERATION Operation Operation.
|
||||||
|
-- @param #table OpsQueue Queue of operations.
|
||||||
|
-- @return #table Cohorts.
|
||||||
|
function LEGION._GetCohorts(Legions, Cohorts, Operation, OpsQueue)
|
||||||
|
|
||||||
|
OpsQueue=OpsQueue or {}
|
||||||
|
|
||||||
|
--- Function that check if a legion or cohort is part of an operation.
|
||||||
|
local function CheckOperation(LegionOrCohort)
|
||||||
|
-- No operations ==> no problem!
|
||||||
|
if #OpsQueue==0 then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Cohort is not dedicated to a running(!) operation. We assume so.
|
||||||
|
local isAvail=true
|
||||||
|
|
||||||
|
-- Only available...
|
||||||
|
if Operation then
|
||||||
|
isAvail=false
|
||||||
|
end
|
||||||
|
|
||||||
|
for _,_operation in pairs(OpsQueue) do
|
||||||
|
local operation=_operation --Ops.Operation#OPERATION
|
||||||
|
|
||||||
|
-- Legion is assigned to this operation.
|
||||||
|
local isOps=operation:IsAssignedCohortOrLegion(LegionOrCohort)
|
||||||
|
|
||||||
|
if isOps and operation:IsRunning() then
|
||||||
|
|
||||||
|
-- Is dedicated.
|
||||||
|
isAvail=false
|
||||||
|
|
||||||
|
if Operation==nil then
|
||||||
|
-- No Operation given and this is dedicated to at least one operation.
|
||||||
|
return false
|
||||||
|
else
|
||||||
|
if Operation.uid==operation.uid then
|
||||||
|
-- Operation given and is part of it.
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return isAvail
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Chosen cohorts.
|
||||||
|
local cohorts={}
|
||||||
|
|
||||||
|
-- Check if there are any special legions and/or cohorts.
|
||||||
|
if (Legions and #Legions>0) or (Cohorts and #Cohorts>0) then
|
||||||
|
|
||||||
|
-- Add cohorts of special legions.
|
||||||
|
for _,_legion in pairs(Legions or {}) do
|
||||||
|
local legion=_legion --Ops.Legion#LEGION
|
||||||
|
|
||||||
|
-- Check that runway is operational.
|
||||||
|
local Runway=legion:IsAirwing() and legion:IsRunwayOperational() or true
|
||||||
|
|
||||||
|
-- Legion has to be running.
|
||||||
|
if legion:IsRunning() and Runway then
|
||||||
|
|
||||||
|
-- Add cohorts of legion.
|
||||||
|
for _,_cohort in pairs(legion.cohorts) do
|
||||||
|
local cohort=_cohort --Ops.Cohort#COHORT
|
||||||
|
|
||||||
|
if (CheckOperation(cohort.legion) or CheckOperation(cohort)) and not UTILS.IsInTable(cohorts, cohort, "name") then
|
||||||
|
table.insert(cohorts, cohort)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Add special cohorts.
|
||||||
|
for _,_cohort in pairs(Cohorts or {}) do
|
||||||
|
local cohort=_cohort --Ops.Cohort#COHORT
|
||||||
|
|
||||||
|
if CheckOperation(cohort) and not UTILS.IsInTable(cohorts, cohort, "name") then
|
||||||
|
table.insert(cohorts, cohort)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
return cohorts
|
||||||
|
end
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
-- Recruiting and Optimization Functions
|
-- Recruiting and Optimization Functions
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
--- Recruit assets from Cohorts for the given parameters. **NOTE** that we set the `asset.isReserved=true` flag so it cant be recruited by anyone else.
|
--- Recruit assets from Cohorts for the given parameters. **NOTE** that we set the `asset.isReserved=true` flag so it cant be recruited by anyone else.
|
||||||
-- @param #table Cohorts Cohorts included.
|
-- @param Ops.Cohort#COHORT Cohort The Cohort.
|
||||||
-- @param #string MissionTypeRecruit Mission type for recruiting the cohort assets.
|
-- @param #string MissionType Misson type(s).
|
||||||
-- @param #string MissionTypeOpt Mission type for which the assets are optimized. Default is the same as `MissionTypeRecruit`.
|
-- @param #table Categories Group categories.
|
||||||
-- @param #number NreqMin Minimum number of required assets.
|
|
||||||
-- @param #number NreqMax Maximum number of required assets.
|
|
||||||
-- @param DCS#Vec2 TargetVec2 Target position as 2D vector.
|
|
||||||
-- @param #table Payloads Special payloads.
|
|
||||||
-- @param #number RangeMax Max range in meters.
|
|
||||||
-- @param #number RefuelSystem Refuelsystem.
|
|
||||||
-- @param #number CargoWeight Cargo weight for recruiting transport carriers.
|
|
||||||
-- @param #number TotalWeight Total cargo weight in kg.
|
|
||||||
-- @param #table Categories Group categories.
|
|
||||||
-- @param #table Attributes Group attributes. See `GROUP.Attribute.`
|
-- @param #table Attributes Group attributes. See `GROUP.Attribute.`
|
||||||
-- @param #table Properties DCS attributes.
|
-- @param #table Properties DCS attributes.
|
||||||
-- @param #table WeaponTypes Bit of weapon types.
|
-- @param #table WeaponTypes Bit of weapon types.
|
||||||
-- @return #boolean If `true` enough assets could be recruited.
|
-- @param DCS#Vec2 TargetVec2 Target position.
|
||||||
-- @return #table Recruited assets. **NOTE** that we set the `asset.isReserved=true` flag so it cant be recruited by anyone else.
|
-- @param RangeMax Max range in meters.
|
||||||
-- @return #table Legions of recruited assets.
|
-- @param #number RefuelSystem Refueling system (boom or probe).
|
||||||
function LEGION.RecruitCohortAssets(Cohorts, MissionTypeRecruit, MissionTypeOpt, NreqMin, NreqMax, TargetVec2, Payloads, RangeMax, RefuelSystem, CargoWeight, TotalWeight, Categories, Attributes, Properties, WeaponTypes)
|
-- @param #number CargoWeight Cargo weight [kg]. This checks the cargo bay of the cohort assets and ensures that it is large enough to carry the given cargo weight.
|
||||||
|
-- @param #number MaxWeight Max weight [kg]. This checks whether the cohort asset group is not too heavy.
|
||||||
|
-- @return #boolean Returns `true` if given cohort can meet all requirements.
|
||||||
|
function LEGION._CohortCan(Cohort, MissionType, Categories, Attributes, Properties, WeaponTypes, TargetVec2, RangeMax, RefuelSystem, CargoWeight, MaxWeight)
|
||||||
|
|
||||||
-- The recruited assets.
|
|
||||||
local Assets={}
|
|
||||||
|
|
||||||
-- Legions of recruited assets.
|
|
||||||
local Legions={}
|
|
||||||
|
|
||||||
-- Set MissionTypeOpt to Recruit if nil.
|
|
||||||
if MissionTypeOpt==nil then
|
|
||||||
MissionTypeOpt=MissionTypeRecruit
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Function to check category.
|
--- Function to check category.
|
||||||
local function CheckCategory(_cohort)
|
local function CheckCategory(_cohort)
|
||||||
local cohort=_cohort --Ops.Cohort#COHORT
|
local cohort=_cohort --Ops.Cohort#COHORT
|
||||||
@ -2350,9 +2456,9 @@ function LEGION.RecruitCohortAssets(Cohorts, MissionTypeRecruit, MissionTypeOpt,
|
|||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Loops over cohorts.
|
--- Function to check range.
|
||||||
for _,_cohort in pairs(Cohorts) do
|
local function CheckRange(_cohort)
|
||||||
local cohort=_cohort --Ops.Cohort#COHORT
|
local cohort=_cohort --Ops.Cohort#COHORT
|
||||||
|
|
||||||
-- Distance to target.
|
-- Distance to target.
|
||||||
@ -2362,50 +2468,175 @@ function LEGION.RecruitCohortAssets(Cohorts, MissionTypeRecruit, MissionTypeOpt,
|
|||||||
local Rmax=cohort:GetMissionRange(WeaponTypes)
|
local Rmax=cohort:GetMissionRange(WeaponTypes)
|
||||||
local InRange=(RangeMax and math.max(RangeMax, Rmax) or Rmax) >= TargetDistance
|
local InRange=(RangeMax and math.max(RangeMax, Rmax) or Rmax) >= TargetDistance
|
||||||
|
|
||||||
|
return InRange
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Function to check weapon type.
|
||||||
|
local function CheckRefueling(_cohort)
|
||||||
|
local cohort=_cohort --Ops.Cohort#COHORT
|
||||||
|
|
||||||
-- Has the requested refuelsystem?
|
-- Has the requested refuelsystem?
|
||||||
local Refuel=RefuelSystem~=nil and (RefuelSystem==cohort.tankerSystem) or true
|
--local Refuel=RefuelSystem~=nil and (RefuelSystem==cohort.tankerSystem) or true
|
||||||
|
|
||||||
-- STRANGE: Why did the above line did not give the same result?! Above Refuel is always true!
|
-- STRANGE: Why did the above line did not give the same result?! Above Refuel is always true!
|
||||||
local Refuel=true
|
|
||||||
if RefuelSystem then
|
if RefuelSystem then
|
||||||
if cohort.tankerSystem then
|
if cohort.tankerSystem then
|
||||||
Refuel=RefuelSystem==cohort.tankerSystem
|
return RefuelSystem==cohort.tankerSystem
|
||||||
else
|
else
|
||||||
Refuel=false
|
return false
|
||||||
end
|
end
|
||||||
|
else
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Function to check cargo weight.
|
||||||
|
local function CheckCargoWeight(_cohort)
|
||||||
|
local cohort=_cohort --Ops.Cohort#COHORT
|
||||||
|
if CargoWeight~=nil then
|
||||||
|
return cohort.cargobayLimit>=CargoWeight
|
||||||
|
else
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Function to check cargo weight.
|
||||||
|
local function CheckMaxWeight(_cohort)
|
||||||
|
local cohort=_cohort --Ops.Cohort#COHORT
|
||||||
|
if MaxWeight~=nil then
|
||||||
|
cohort:I(string.format("Cohort weight=%.1f | max weight=%.1f", cohort.weightAsset, MaxWeight))
|
||||||
|
return cohort.weightAsset<=MaxWeight
|
||||||
|
else
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-- Is capable of the mission type?
|
||||||
|
local can=AUFTRAG.CheckMissionCapability(MissionType, Cohort.missiontypes)
|
||||||
|
|
||||||
|
if can then
|
||||||
|
can=CheckCategory(Cohort)
|
||||||
|
else
|
||||||
|
env.info(string.format("Cohort %s cannot because of mission types", Cohort.name))
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
if can then
|
||||||
|
if MissionType==AUFTRAG.Type.RELOCATECOHORT then
|
||||||
|
can=Cohort:IsRelocating()
|
||||||
|
else
|
||||||
|
can=Cohort:IsOnDuty()
|
||||||
end
|
end
|
||||||
|
else
|
||||||
-- Is capable of the mission type?
|
env.info(string.format("Cohort %s cannot because of category", Cohort.name))
|
||||||
local Capable=AUFTRAG.CheckMissionCapability({MissionTypeRecruit}, cohort.missiontypes)
|
BASE:I(Categories)
|
||||||
|
BASE:I(Cohort.category)
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
if can then
|
||||||
|
can=CheckAttribute(Cohort)
|
||||||
|
else
|
||||||
|
env.info(string.format("Cohort %s cannot because of readyiness", Cohort.name))
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
if can then
|
||||||
|
can=CheckProperty(Cohort)
|
||||||
|
else
|
||||||
|
env.info(string.format("Cohort %s cannot because of attribute", Cohort.name))
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
if can then
|
||||||
|
can=CheckWeapon(Cohort)
|
||||||
|
else
|
||||||
|
env.info(string.format("Cohort %s cannot because of property", Cohort.name))
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
if can then
|
||||||
|
can=CheckRange(Cohort)
|
||||||
|
else
|
||||||
|
env.info(string.format("Cohort %s cannot because of weapon type", Cohort.name))
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
if can then
|
||||||
|
can=CheckRefueling(Cohort)
|
||||||
|
else
|
||||||
|
env.info(string.format("Cohort %s cannot because of range", Cohort.name))
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
if can then
|
||||||
|
can=CheckCargoWeight(Cohort)
|
||||||
|
else
|
||||||
|
env.info(string.format("Cohort %s cannot because of refueling system", Cohort.name))
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
if can then
|
||||||
|
can=CheckMaxWeight(Cohort)
|
||||||
|
else
|
||||||
|
env.info(string.format("Cohort %s cannot because of cargo weight", Cohort.name))
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
if can then
|
||||||
|
return true
|
||||||
|
else
|
||||||
|
env.info(string.format("Cohort %s cannot because of max weight", Cohort.name))
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Recruit assets from Cohorts for the given parameters. **NOTE** that we set the `asset.isReserved=true` flag so it cant be recruited by anyone else.
|
||||||
|
-- @param #table Cohorts Cohorts included.
|
||||||
|
-- @param #string MissionTypeRecruit Mission type for recruiting the cohort assets.
|
||||||
|
-- @param #string MissionTypeOpt Mission type for which the assets are optimized. Default is the same as `MissionTypeRecruit`.
|
||||||
|
-- @param #number NreqMin Minimum number of required assets.
|
||||||
|
-- @param #number NreqMax Maximum number of required assets.
|
||||||
|
-- @param DCS#Vec2 TargetVec2 Target position as 2D vector.
|
||||||
|
-- @param #table Payloads Special payloads.
|
||||||
|
-- @param #number RangeMax Max range in meters.
|
||||||
|
-- @param #number RefuelSystem Refuelsystem.
|
||||||
|
-- @param #number CargoWeight Cargo weight for recruiting transport carriers.
|
||||||
|
-- @param #number TotalWeight Total cargo weight in kg.
|
||||||
|
-- @param #number MaxWeight Max weight [kg] of the asset group.
|
||||||
|
-- @param #table Categories Group categories.
|
||||||
|
-- @param #table Attributes Group attributes. See `GROUP.Attribute.`
|
||||||
|
-- @param #table Properties DCS attributes.
|
||||||
|
-- @param #table WeaponTypes Bit of weapon types.
|
||||||
|
-- @return #boolean If `true` enough assets could be recruited.
|
||||||
|
-- @return #table Recruited assets. **NOTE** that we set the `asset.isReserved=true` flag so it cant be recruited by anyone else.
|
||||||
|
-- @return #table Legions of recruited assets.
|
||||||
|
function LEGION.RecruitCohortAssets(Cohorts, MissionTypeRecruit, MissionTypeOpt, NreqMin, NreqMax, TargetVec2, Payloads, RangeMax, RefuelSystem, CargoWeight, TotalWeight, MaxWeight, Categories, Attributes, Properties, WeaponTypes)
|
||||||
|
|
||||||
|
-- The recruited assets.
|
||||||
|
local Assets={}
|
||||||
|
|
||||||
|
-- Legions of recruited assets.
|
||||||
|
local Legions={}
|
||||||
|
|
||||||
|
-- Set MissionTypeOpt to Recruit if nil.
|
||||||
|
if MissionTypeOpt==nil then
|
||||||
|
MissionTypeOpt=MissionTypeRecruit
|
||||||
|
end
|
||||||
|
|
||||||
-- Can carry the cargo?
|
-- Loops over cohorts.
|
||||||
local CanCarry=CargoWeight and cohort.cargobayLimit>=CargoWeight or true
|
for _,_cohort in pairs(Cohorts) do
|
||||||
|
local cohort=_cohort --Ops.Cohort#COHORT
|
||||||
|
|
||||||
-- Right category.
|
-- Check if cohort can do the mission.
|
||||||
local RightCategory=CheckCategory(cohort)
|
local can=LEGION._CohortCan(cohort, MissionTypeRecruit, Categories, Attributes, Properties, WeaponTypes, TargetVec2, RangeMax, RefuelSystem, CargoWeight, MaxWeight)
|
||||||
|
|
||||||
-- Right attribute.
|
|
||||||
local RightAttribute=CheckAttribute(cohort)
|
|
||||||
|
|
||||||
-- Right property (DCS attribute).
|
|
||||||
local RightProperty=CheckProperty(cohort)
|
|
||||||
|
|
||||||
-- Right weapon type.
|
|
||||||
local RightWeapon=CheckWeapon(cohort)
|
|
||||||
|
|
||||||
-- Cohort ready to execute mission.
|
|
||||||
local Ready=cohort:IsOnDuty()
|
|
||||||
if MissionTypeRecruit==AUFTRAG.Type.RELOCATECOHORT then
|
|
||||||
Ready=cohort:IsRelocating()
|
|
||||||
Capable=true
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Debug info.
|
|
||||||
cohort:T(cohort.lid..string.format("State=%s: Capable=%s, InRange=%s, Refuel=%s, CanCarry=%s, Category=%s, Attribute=%s, Property=%s, Weapon=%s",
|
|
||||||
cohort:GetState(), tostring(Capable), tostring(InRange), tostring(Refuel), tostring(CanCarry), tostring(RightCategory), tostring(RightAttribute), tostring(RightProperty), tostring(RightWeapon)))
|
|
||||||
|
|
||||||
-- Check OnDuty, capable, in range and refueling type (if TANKER).
|
-- Check OnDuty, capable, in range and refueling type (if TANKER).
|
||||||
if Ready and Capable and InRange and Refuel and CanCarry and RightCategory and RightAttribute and RightProperty and RightWeapon then
|
if can then
|
||||||
|
|
||||||
-- Recruit assets from cohort.
|
-- Recruit assets from cohort.
|
||||||
local assets, npayloads=cohort:RecruitAssets(MissionTypeRecruit, 999)
|
local assets, npayloads=cohort:RecruitAssets(MissionTypeRecruit, 999)
|
||||||
@ -2456,23 +2687,30 @@ function LEGION.RecruitCohortAssets(Cohorts, MissionTypeRecruit, MissionTypeOpt,
|
|||||||
-- Found enough assets
|
-- Found enough assets
|
||||||
---
|
---
|
||||||
|
|
||||||
-- Add assets to mission.
|
-- Total cargo bay of all carrier assets.
|
||||||
local cargobay=0
|
local cargobay=0
|
||||||
|
|
||||||
|
-- Add assets to mission.
|
||||||
for i=1,Nassets do
|
for i=1,Nassets do
|
||||||
local asset=Assets[i] --Functional.Warehouse#WAREHOUSE.Assetitem
|
local asset=Assets[i] --Functional.Warehouse#WAREHOUSE.Assetitem
|
||||||
|
|
||||||
|
-- Asset is reserved and will not be picked for other missions.
|
||||||
asset.isReserved=true
|
asset.isReserved=true
|
||||||
|
|
||||||
|
-- Add legion.
|
||||||
Legions[asset.legion.alias]=asset.legion
|
Legions[asset.legion.alias]=asset.legion
|
||||||
|
|
||||||
|
-- Check if total cargo weight was given.
|
||||||
if TotalWeight then
|
if TotalWeight then
|
||||||
|
|
||||||
-- Number of
|
-- Number of
|
||||||
local N=math.floor(asset.cargobaytot/asset.nunits / CargoWeight)*asset.nunits
|
local N=math.floor(asset.cargobaytot/asset.nunits / CargoWeight)*asset.nunits
|
||||||
--env.info(string.format("cargobaytot=%d, cargoweight=%d ==> N=%d", asset.cargobaytot, CargoWeight, N))
|
--env.info(string.format("cargobaytot=%d, cargoweight=%d ==> N=%d", asset.cargobaytot, CargoWeight, N))
|
||||||
|
|
||||||
|
-- Sum up total cargo bay of all carrier assets.
|
||||||
cargobay=cargobay + N*CargoWeight
|
cargobay=cargobay + N*CargoWeight
|
||||||
|
|
||||||
|
-- Check if enough carrier assets were found to transport all cargo.
|
||||||
if cargobay>=TotalWeight then
|
if cargobay>=TotalWeight then
|
||||||
--env.info(string.format("FF found enough assets to transport all cargo! N=%d [%d], cargobay=%.1f >= %.1f kg total weight", i, Nassets, cargobay, TotalWeight))
|
--env.info(string.format("FF found enough assets to transport all cargo! N=%d [%d], cargobay=%.1f >= %.1f kg total weight", i, Nassets, cargobay, TotalWeight))
|
||||||
Nassets=i
|
Nassets=i
|
||||||
@ -2580,7 +2818,7 @@ function LEGION:AssignAssetsForEscort(Cohorts, Assets, NescortMin, NescortMax, M
|
|||||||
TargetTypes=TargetTypes or targetTypes
|
TargetTypes=TargetTypes or targetTypes
|
||||||
|
|
||||||
-- Recruit escort asset for the mission asset.
|
-- Recruit escort asset for the mission asset.
|
||||||
local Erecruited, eassets, elegions=LEGION.RecruitCohortAssets(Cohorts, AUFTRAG.Type.ESCORT, MissionType, NescortMin, NescortMax, TargetVec2, nil, nil, nil, nil, nil, Categories)
|
local Erecruited, eassets, elegions=LEGION.RecruitCohortAssets(Cohorts, AUFTRAG.Type.ESCORT, MissionType, NescortMin, NescortMax, TargetVec2, nil, nil, nil, nil, nil, nil, Categories)
|
||||||
|
|
||||||
if Erecruited then
|
if Erecruited then
|
||||||
Escorts[asset.spawngroupname]={EscortLegions=elegions, EscortAssets=eassets, ecategory=asset.category}
|
Escorts[asset.spawngroupname]={EscortLegions=elegions, EscortAssets=eassets, ecategory=asset.category}
|
||||||
@ -2685,24 +2923,8 @@ function LEGION:AssignAssetsForTransport(Legions, CargoAssets, NcarriersMin, Nca
|
|||||||
-- Is an escort requested in the first place?
|
-- Is an escort requested in the first place?
|
||||||
if NcarriersMin and NcarriersMax and (NcarriersMin>0 or NcarriersMax>0) then
|
if NcarriersMin and NcarriersMax and (NcarriersMin>0 or NcarriersMax>0) then
|
||||||
|
|
||||||
-- Cohorts.
|
-- Get cohorts.
|
||||||
local Cohorts={}
|
local Cohorts=LEGION._GetCohorts(Legions)
|
||||||
for _,_legion in pairs(Legions) do
|
|
||||||
local legion=_legion --Ops.Legion#LEGION
|
|
||||||
|
|
||||||
-- Check that runway is operational.
|
|
||||||
local Runway=legion:IsAirwing() and legion:IsRunwayOperational() or true
|
|
||||||
|
|
||||||
if legion:IsRunning() and Runway then
|
|
||||||
|
|
||||||
-- Loops over cohorts.
|
|
||||||
for _,_cohort in pairs(legion.cohorts) do
|
|
||||||
local cohort=_cohort --Ops.Cohort#COHORT
|
|
||||||
table.insert(Cohorts, cohort)
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Get all legions and heaviest cargo group weight
|
-- Get all legions and heaviest cargo group weight
|
||||||
local CargoLegions={} ; local CargoWeight=nil ; local TotalWeight=0
|
local CargoLegions={} ; local CargoWeight=nil ; local TotalWeight=0
|
||||||
@ -2714,13 +2936,17 @@ function LEGION:AssignAssetsForTransport(Legions, CargoAssets, NcarriersMin, Nca
|
|||||||
end
|
end
|
||||||
TotalWeight=TotalWeight+asset.weight
|
TotalWeight=TotalWeight+asset.weight
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Debug info.
|
||||||
|
self:T(self.lid..string.format("Cargo weight=%.1f", CargoWeight))
|
||||||
|
self:T(self.lid..string.format("Total weight=%.1f", TotalWeight))
|
||||||
|
|
||||||
-- Target is the deploy zone.
|
-- Target is the deploy zone.
|
||||||
local TargetVec2=DeployZone:GetVec2()
|
local TargetVec2=DeployZone:GetVec2()
|
||||||
|
|
||||||
-- Recruit assets and legions.
|
-- Recruit assets and legions.
|
||||||
local TransportAvail, CarrierAssets, CarrierLegions=
|
local TransportAvail, CarrierAssets, CarrierLegions=
|
||||||
LEGION.RecruitCohortAssets(Cohorts, AUFTRAG.Type.OPSTRANSPORT, nil, NcarriersMin, NcarriersMax, TargetVec2, nil, nil, nil, CargoWeight, TotalWeight, Categories, Attributes, Properties)
|
LEGION.RecruitCohortAssets(Cohorts, AUFTRAG.Type.OPSTRANSPORT, nil, NcarriersMin, NcarriersMax, TargetVec2, nil, nil, nil, CargoWeight, TotalWeight, nil, Categories, Attributes, Properties)
|
||||||
|
|
||||||
if TransportAvail then
|
if TransportAvail then
|
||||||
|
|
||||||
@ -2922,7 +3148,7 @@ function LEGION._OptimizeAssetSelection(assets, MissionType, TargetVec2, Include
|
|||||||
local text=string.format("Optimized %d assets for %s mission/transport (payload=%s):", #assets, MissionType, tostring(IncludePayload))
|
local text=string.format("Optimized %d assets for %s mission/transport (payload=%s):", #assets, MissionType, tostring(IncludePayload))
|
||||||
for i,Asset in pairs(assets) do
|
for i,Asset in pairs(assets) do
|
||||||
local asset=Asset --Functional.Warehouse#WAREHOUSE.Assetitem
|
local asset=Asset --Functional.Warehouse#WAREHOUSE.Assetitem
|
||||||
text=text..string.format("\n%s %s: score=%d", asset.squadname, asset.spawngroupname, asset.score)
|
text=text..string.format("\n%s %s: score=%d", asset.squadname, asset.spawngroupname, asset.score or -1)
|
||||||
asset.score=nil
|
asset.score=nil
|
||||||
end
|
end
|
||||||
env.info(text)
|
env.info(text)
|
||||||
|
|||||||
@ -4,6 +4,7 @@
|
|||||||
--
|
--
|
||||||
-- * Define operation phases
|
-- * Define operation phases
|
||||||
-- * Define conditions when phases are over
|
-- * Define conditions when phases are over
|
||||||
|
-- * Option to have branches in the phase tree
|
||||||
-- * Dedicate resources to operations
|
-- * Dedicate resources to operations
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
@ -25,10 +26,14 @@
|
|||||||
-- @type OPERATION
|
-- @type OPERATION
|
||||||
-- @field #string ClassName Name of the class.
|
-- @field #string ClassName Name of the class.
|
||||||
-- @field #number verbose Verbosity level.
|
-- @field #number verbose Verbosity level.
|
||||||
|
-- @field #number uid Unique ID of the operation.
|
||||||
-- @field #string lid Class id string for output to DCS log file.
|
-- @field #string lid Class id string for output to DCS log file.
|
||||||
-- @field #string name Name of the operation.
|
-- @field #string name Name of the operation.
|
||||||
|
-- @field #number Tstart Start time in seconds of abs mission time.
|
||||||
|
-- @field #number Tstop Stop time in seconds of abs mission time.
|
||||||
|
-- @field #number duration Duration of the operation in seconds.
|
||||||
-- @field Core.Condition#CONDITION conditionStart Start condition.
|
-- @field Core.Condition#CONDITION conditionStart Start condition.
|
||||||
-- @field Core.Condition#CONDITION conditionStop Stop condition.
|
-- @field Core.Condition#CONDITION conditionOver Over condition.
|
||||||
-- @field #table branches Branches.
|
-- @field #table branches Branches.
|
||||||
-- @field #OPERATION.Branch branchMaster Master branch.
|
-- @field #OPERATION.Branch branchMaster Master branch.
|
||||||
-- @field #OPERATION.Branch branchActive Active branch.
|
-- @field #OPERATION.Branch branchActive Active branch.
|
||||||
@ -89,6 +94,9 @@ _OPERATIONID=0
|
|||||||
-- @field #string name Name of the phase.
|
-- @field #string name Name of the phase.
|
||||||
-- @field Core.Condition#CONDITION conditionOver Conditions when the phase is over.
|
-- @field Core.Condition#CONDITION conditionOver Conditions when the phase is over.
|
||||||
-- @field #string status Phase status.
|
-- @field #string status Phase status.
|
||||||
|
-- @field #number Tstart Abs. mission time when the phase was started.
|
||||||
|
-- @field #number nActive Number of times the phase was active.
|
||||||
|
-- @field #number duration Duration in seconds how long the phase should be active after it started.
|
||||||
-- @field #OPERATION.Branch branch The branch this phase belongs to.
|
-- @field #OPERATION.Branch branch The branch this phase belongs to.
|
||||||
|
|
||||||
--- Operation branch.
|
--- Operation branch.
|
||||||
@ -120,14 +128,15 @@ OPERATION.PhaseStatus={
|
|||||||
|
|
||||||
--- OPERATION class version.
|
--- OPERATION class version.
|
||||||
-- @field #string version
|
-- @field #string version
|
||||||
OPERATION.version="0.1.0"
|
OPERATION.version="0.2.0"
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
-- TODO list
|
-- TODO list
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
-- TODO: Branches?
|
|
||||||
-- TODO: "Over" conditions.
|
-- TODO: "Over" conditions.
|
||||||
|
-- TODO: Repeat phases: after over ==> planned (not over)
|
||||||
|
-- DONE: Branches.
|
||||||
-- DONE: Phases.
|
-- DONE: Phases.
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
@ -161,6 +170,15 @@ function OPERATION:New(Name)
|
|||||||
-- Master branch.
|
-- Master branch.
|
||||||
self.branchMaster=self:AddBranch("Master")
|
self.branchMaster=self:AddBranch("Master")
|
||||||
|
|
||||||
|
self.conditionStart=CONDITION:New("Operation %s start", self.name)
|
||||||
|
self.conditionStart:SetNoneResult(false) --If no condition function is specified, the ops will NOT be over.
|
||||||
|
self.conditionStart:SetDefaultPersistence(false)
|
||||||
|
|
||||||
|
self.conditionOver=CONDITION:New("Operation %s over", self.name)
|
||||||
|
self.conditionOver:SetNoneResult(false)
|
||||||
|
self.conditionOver:SetDefaultPersistence(false)
|
||||||
|
|
||||||
|
|
||||||
-- Set master as active branch.
|
-- Set master as active branch.
|
||||||
self.branchActive=self.branchMaster
|
self.branchActive=self.branchMaster
|
||||||
|
|
||||||
@ -197,6 +215,12 @@ function OPERATION:New(Name)
|
|||||||
-- @param #OPERATION self
|
-- @param #OPERATION self
|
||||||
-- @param #number delay Delay in seconds.
|
-- @param #number delay Delay in seconds.
|
||||||
|
|
||||||
|
--- On after "Start" event.
|
||||||
|
-- @function [parent=#OPERATION] OnAfterStart
|
||||||
|
-- @param #OPERATION self
|
||||||
|
-- @param #string From From state.
|
||||||
|
-- @param #string Event Event.
|
||||||
|
-- @param #string To To state.
|
||||||
|
|
||||||
--- Triggers the FSM event "Stop".
|
--- Triggers the FSM event "Stop".
|
||||||
-- @function [parent=#OPERATION] Stop
|
-- @function [parent=#OPERATION] Stop
|
||||||
@ -279,12 +303,14 @@ function OPERATION:New(Name)
|
|||||||
-- @function [parent=#OPERATION] BranchSwitch
|
-- @function [parent=#OPERATION] BranchSwitch
|
||||||
-- @param #OPERATION self
|
-- @param #OPERATION self
|
||||||
-- @param #OPERATION.Branch Branch The branch that is now active.
|
-- @param #OPERATION.Branch Branch The branch that is now active.
|
||||||
|
-- @param #OPERATION.Phase Phase The new phase.
|
||||||
|
|
||||||
--- Triggers the FSM event "BranchSwitch" after a delay.
|
--- Triggers the FSM event "BranchSwitch" after a delay.
|
||||||
-- @function [parent=#OPERATION] __BranchSwitch
|
-- @function [parent=#OPERATION] __BranchSwitch
|
||||||
-- @param #OPERATION self
|
-- @param #OPERATION self
|
||||||
-- @param #number delay Delay in seconds.
|
-- @param #number delay Delay in seconds.
|
||||||
-- @param #OPERATION.Branch Branch The branch that is now active.
|
-- @param #OPERATION.Branch Branch The branch that is now active.
|
||||||
|
-- @param #OPERATION.Phase Phase The new phase.
|
||||||
|
|
||||||
--- On after "BranchSwitch" event.
|
--- On after "BranchSwitch" event.
|
||||||
-- @function [parent=#OPERATION] OnAfterBranchSwitch
|
-- @function [parent=#OPERATION] OnAfterBranchSwitch
|
||||||
@ -293,7 +319,7 @@ function OPERATION:New(Name)
|
|||||||
-- @param #string Event Event.
|
-- @param #string Event Event.
|
||||||
-- @param #string To To state.
|
-- @param #string To To state.
|
||||||
-- @param #OPERATION.Branch Branch The branch that is now active.
|
-- @param #OPERATION.Branch Branch The branch that is now active.
|
||||||
|
-- @param #OPERATION.Phase Phase The new phase.
|
||||||
|
|
||||||
--- Triggers the FSM event "Over".
|
--- Triggers the FSM event "Over".
|
||||||
-- @function [parent=#OPERATION] Over
|
-- @function [parent=#OPERATION] Over
|
||||||
@ -366,12 +392,34 @@ function OPERATION:SetTime(ClockStart, ClockStop)
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Add (all) condition function when the whole operation is over. Must return a `#boolean`.
|
||||||
|
-- @param #OPERATION self
|
||||||
|
-- @param #function Function Function that needs to be `true` before the operation is over.
|
||||||
|
-- @param ... Condition function arguments if any.
|
||||||
|
-- @return Core.Condition#CONDITION.Function Condition function table.
|
||||||
|
function OPERATION:AddConditonOverAll(Function, ...)
|
||||||
|
local cf=self.conditionOver:AddFunctionAll(Function, ...)
|
||||||
|
return cf
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Add (any) condition function when the whole operation is over. Must return a `#boolean`.
|
||||||
|
-- @param #OPERATION self
|
||||||
|
-- @param #function Function Function that needs to be `true` before the operation is over.
|
||||||
|
-- @param ... Condition function arguments if any.
|
||||||
|
-- @return Core.Condition#CONDITION.Function Condition function table.
|
||||||
|
function OPERATION:AddConditonOverAny(Phase, Function, ...)
|
||||||
|
local cf=self.conditionOver:AddFunctionAny(Function, ...)
|
||||||
|
return cf
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Add a new phase to the operation. This is added add the end of all previously added phases (if any).
|
--- Add a new phase to the operation. This is added add the end of all previously added phases (if any).
|
||||||
-- @param #OPERATION self
|
-- @param #OPERATION self
|
||||||
-- @param #string Name Name of the phase. Default "Phase-01" where the last number is a running number.
|
-- @param #string Name Name of the phase. Default "Phase-01" where the last number is a running number.
|
||||||
-- @param #OPERATION.Branch Branch The branch to which this phase is added. Default is the master branch.
|
-- @param #OPERATION.Branch Branch The branch to which this phase is added. Default is the master branch.
|
||||||
|
-- @param #number Duration Duration in seconds how long the phase will last. Default `nil`=forever.
|
||||||
-- @return #OPERATION.Phase Phase table object.
|
-- @return #OPERATION.Phase Phase table object.
|
||||||
function OPERATION:AddPhase(Name, Branch)
|
function OPERATION:AddPhase(Name, Branch, Duration)
|
||||||
|
|
||||||
-- Branch.
|
-- Branch.
|
||||||
Branch=Branch or self.branchMaster
|
Branch=Branch or self.branchMaster
|
||||||
@ -382,6 +430,8 @@ function OPERATION:AddPhase(Name, Branch)
|
|||||||
-- Branch of phase
|
-- Branch of phase
|
||||||
phase.branch=Branch
|
phase.branch=Branch
|
||||||
|
|
||||||
|
-- Set duraction of pahse (if any).
|
||||||
|
phase.duration=Duration
|
||||||
|
|
||||||
-- Debug output.
|
-- Debug output.
|
||||||
self:T(self.lid..string.format("Adding phase %s to branch %s", phase.name, Branch.name))
|
self:T(self.lid..string.format("Adding phase %s to branch %s", phase.name, Branch.name))
|
||||||
@ -413,6 +463,12 @@ function OPERATION:InsertPhaseAfter(PhaseAfter, Name)
|
|||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Get a name of this operation.
|
||||||
|
-- @param #OPERATION self
|
||||||
|
-- @return #string Name of this operation or "Unknown".
|
||||||
|
function OPERATION:GetName()
|
||||||
|
return self.name or "Unknown"
|
||||||
|
end
|
||||||
|
|
||||||
--- Get a phase by its name.
|
--- Get a phase by its name.
|
||||||
-- @param #OPERATION self
|
-- @param #OPERATION self
|
||||||
@ -439,10 +495,26 @@ end
|
|||||||
-- @param #string Status New status, *e.g.* `OPERATION.PhaseStatus.OVER`.
|
-- @param #string Status New status, *e.g.* `OPERATION.PhaseStatus.OVER`.
|
||||||
-- @return #OPERATION self
|
-- @return #OPERATION self
|
||||||
function OPERATION:SetPhaseStatus(Phase, Status)
|
function OPERATION:SetPhaseStatus(Phase, Status)
|
||||||
|
|
||||||
if Phase then
|
if Phase then
|
||||||
self:T(self.lid..string.format("Phase %s status: %s-->%s"), Phase.status, Status)
|
|
||||||
|
-- Debug message.
|
||||||
|
self:T(self.lid..string.format("Phase %s status: %s-->%s", tostring(Phase.name), tostring(Phase.status), tostring(Status)))
|
||||||
|
|
||||||
|
-- Set status.
|
||||||
Phase.status=Status
|
Phase.status=Status
|
||||||
|
|
||||||
|
-- Set time stamp when phase becase active.
|
||||||
|
if Phase.status==OPERATION.PhaseStatus.ACTIVE then
|
||||||
|
Phase.Tstart=timer.getAbsTime()
|
||||||
|
Phase.nActive=Phase.nActive+1
|
||||||
|
elseif Phase.status==OPERATION.PhaseStatus.OVER then
|
||||||
|
-- Trigger PhaseOver event.
|
||||||
|
self:PhaseOver(Phase)
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -461,40 +533,66 @@ end
|
|||||||
-- @return #OPERATION self
|
-- @return #OPERATION self
|
||||||
function OPERATION:SetPhaseConditonOver(Phase, Condition)
|
function OPERATION:SetPhaseConditonOver(Phase, Condition)
|
||||||
if Phase then
|
if Phase then
|
||||||
self:T(self.lid..string.format("Setting phase %s conditon over %s"), Phase.name, Condition and Condition.name or "None")
|
self:T(self.lid..string.format("Setting phase %s conditon over %s", self:GetPhaseName(Phase), Condition and Condition.name or "None"))
|
||||||
Phase.conditionOver=Condition
|
Phase.conditionOver=Condition
|
||||||
end
|
end
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Add condition function when the given phase is over. Must return a `#boolean`.
|
|
||||||
-- @param #OPERATION self
|
|
||||||
-- @param #OPERATION.Phase Phase The phase.
|
|
||||||
-- @param #function Function Function that needs to be `true`before the phase is over.
|
|
||||||
-- @param ... Condition function arguments if any.
|
|
||||||
-- @return #OPERATION self
|
|
||||||
function OPERATION:AddPhaseConditonOverAll(Phase, Function, ...)
|
|
||||||
if Phase then
|
|
||||||
Phase.conditionOver:AddFunctionAll(Function, ...)
|
|
||||||
end
|
|
||||||
return self
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Add condition function when the given phase is over. Must return a `#boolean`.
|
--- Add condition function when the given phase is over. Must return a `#boolean`.
|
||||||
-- @param #OPERATION self
|
-- @param #OPERATION self
|
||||||
-- @param #OPERATION.Phase Phase The phase.
|
-- @param #OPERATION.Phase Phase The phase.
|
||||||
-- @param #function Function Function that needs to be `true` before the phase is over.
|
-- @param #function Function Function that needs to be `true` before the phase is over.
|
||||||
-- @param ... Condition function arguments if any.
|
-- @param ... Condition function arguments if any.
|
||||||
-- @return #OPERATION self
|
-- @return Core.Condition#CONDITION.Function Condition function table.
|
||||||
|
function OPERATION:AddPhaseConditonOverAll(Phase, Function, ...)
|
||||||
|
if Phase then
|
||||||
|
local cf=Phase.conditionOver:AddFunctionAll(Function, ...)
|
||||||
|
return cf
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Add condition function when the given phase is over. Must return a `#boolean`.
|
||||||
|
-- @param #OPERATION self
|
||||||
|
-- @param #OPERATION.Phase Phase The phase.
|
||||||
|
-- @param #function Function Function that needs to be `true` before the phase is over.
|
||||||
|
-- @param ... Condition function arguments if any.
|
||||||
|
-- @return Core.Condition#CONDITION.Function Condition function table.
|
||||||
function OPERATION:AddPhaseConditonOverAny(Phase, Function, ...)
|
function OPERATION:AddPhaseConditonOverAny(Phase, Function, ...)
|
||||||
if Phase then
|
if Phase then
|
||||||
Phase.conditionOver:AddFunctionAny(Function, ...)
|
local cf=Phase.conditionOver:AddFunctionAny(Function, ...)
|
||||||
|
return cf
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set persistence of condition function. By default, condition functions are removed after a phase is over.
|
||||||
|
-- @param #OPERATION self
|
||||||
|
-- @param Core.Condition#CONDITION.Function ConditionFunction Condition function table.
|
||||||
|
-- @param #boolean IsPersistent If `true` or `nil`, condition function is persistent.
|
||||||
|
-- @return #OPERATION self
|
||||||
|
function OPERATION:SetConditionFunctionPersistence(ConditionFunction, IsPersistent)
|
||||||
|
ConditionFunction.persistence=IsPersistent
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Add condition function when the given phase is to be repeated. The provided function must return a `#boolean`.
|
||||||
|
-- If the condition evaluation returns `true`, the phase is set to state `Planned` instead of `Over` and can be repeated.
|
||||||
|
-- @param #OPERATION self
|
||||||
|
-- @param #OPERATION.Phase Phase The phase.
|
||||||
|
-- @param #function Function Function that needs to be `true` before the phase is over.
|
||||||
|
-- @param ... Condition function arguments if any.
|
||||||
|
-- @return #OPERATION self
|
||||||
|
function OPERATION:AddPhaseConditonRepeatAll(Phase, Function, ...)
|
||||||
|
if Phase then
|
||||||
|
Phase.conditionRepeat:AddFunctionAll(Function, ...)
|
||||||
end
|
end
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Get codition when the given phase is over.
|
--- Get condition when the given phase is over.
|
||||||
-- @param #OPERATION self
|
-- @param #OPERATION self
|
||||||
-- @param #OPERATION.Phase Phase The phase.
|
-- @param #OPERATION.Phase Phase The phase.
|
||||||
-- @return Core.Condition#CONDITION Condition when the phase is over (if any).
|
-- @return Core.Condition#CONDITION Condition when the phase is over (if any).
|
||||||
@ -502,24 +600,12 @@ function OPERATION:GetPhaseConditonOver(Phase, Condition)
|
|||||||
return Phase.conditionOver
|
return Phase.conditionOver
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Get currrently active phase.
|
--- Get how many times a phase has been active.
|
||||||
-- @param #OPERATION self
|
-- @param #OPERATION self
|
||||||
-- @param #OPERATION.Phase Phase The phase.
|
-- @param #OPERATION.Phase Phase The phase.
|
||||||
-- @param #string Status New status, e.g. `OPERATION.PhaseStatus.OVER`.
|
-- @return #number Number of times the phase has been active.
|
||||||
-- @return #OPERATION self
|
function OPERATION:GetPhaseNactive(Phase)
|
||||||
function OPERATION:SetPhaseStatus(Phase, Status)
|
return Phase.nActive
|
||||||
if Phase then
|
|
||||||
self:T(self.lid..string.format("Phase \"%s\" status: %s-->%s", Phase.name, Phase.status, Status))
|
|
||||||
Phase.status=Status
|
|
||||||
end
|
|
||||||
return self
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Get currrently active phase.
|
|
||||||
-- @param #OPERATION self
|
|
||||||
-- @return #OPERATION.Phase Current phase or `nil` if no current phase is active.
|
|
||||||
function OPERATION:GetPhaseActive()
|
|
||||||
return self.phase
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Get name of a phase.
|
--- Get name of a phase.
|
||||||
@ -537,18 +623,11 @@ function OPERATION:GetPhaseName(Phase)
|
|||||||
return "None"
|
return "None"
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Check if a phase is the currently active one.
|
--- Get currrently active phase.
|
||||||
-- @param #OPERATION self
|
-- @param #OPERATION self
|
||||||
-- @param #OPERATION.Phase Phase The phase to check.
|
-- @return #OPERATION.Phase Current phase or `nil` if no current phase is active.
|
||||||
-- @return #boolean If `true`, this phase is currently active.
|
function OPERATION:GetPhaseActive()
|
||||||
function OPERATION:IsPhaseActive(Phase)
|
return self.phase
|
||||||
local phase=self:GetPhaseActive()
|
|
||||||
if phase and phase.uid==Phase.uid then
|
|
||||||
return true
|
|
||||||
else
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
return nil
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Get index of phase.
|
--- Get index of phase.
|
||||||
@ -657,6 +736,13 @@ function OPERATION:AddBranch(Name)
|
|||||||
return branch
|
return branch
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Get the master branch. This is the default branch and should always exist (if it was not explicitly deleted).
|
||||||
|
-- @param #OPERATION self
|
||||||
|
-- @return #OPERATION.Branch The master branch.
|
||||||
|
function OPERATION:GetBranchMaster()
|
||||||
|
return self.branchMaster
|
||||||
|
end
|
||||||
|
|
||||||
--- Get the currently active branch.
|
--- Get the currently active branch.
|
||||||
-- @param #OPERATION self
|
-- @param #OPERATION self
|
||||||
-- @return #OPERATION.Branch The active branch. If no branch is active, the master branch is returned.
|
-- @return #OPERATION.Branch The active branch. If no branch is active, the master branch is returned.
|
||||||
@ -678,20 +764,26 @@ end
|
|||||||
|
|
||||||
--- Add an edge between two branches.
|
--- Add an edge between two branches.
|
||||||
-- @param #OPERATION self
|
-- @param #OPERATION self
|
||||||
-- @param #OPERATION.Branch BranchTo The branch *to* which to switch.
|
-- @param #OPERATION.Phase PhaseFrom The phase of the *from* branch *after* which to switch.
|
||||||
-- @param #OPERATION.Phase PhaseAfter The phase of the *from* branch *after* which to switch.
|
-- @param #OPERATION.Phase PhaseTo The phase of the *to* branch *to* which to switch.
|
||||||
-- @param #OPERATION.Phase PhaseNext The phase of the *to* branch *to* which to switch.
|
|
||||||
-- @param Core.Condition#CONDITION ConditionSwitch (Optional) Condition(s) when to switch the branches.
|
-- @param Core.Condition#CONDITION ConditionSwitch (Optional) Condition(s) when to switch the branches.
|
||||||
-- @return #OPERATION.Branch Branch table object.
|
-- @return #OPERATION.Edge Edge table object.
|
||||||
function OPERATION:AddEdge(BranchTo, PhaseAfter, PhaseNext, ConditionSwitch)
|
function OPERATION:AddEdge(PhaseFrom, PhaseTo, ConditionSwitch)
|
||||||
|
|
||||||
local edge={} --#OPERATION.Edge
|
local edge={} --#OPERATION.Edge
|
||||||
|
|
||||||
edge.branchFrom=PhaseAfter and PhaseAfter.branch or self.branchMaster
|
edge.phaseFrom=PhaseFrom
|
||||||
edge.phaseFrom=PhaseAfter
|
edge.phaseTo=PhaseTo
|
||||||
edge.branchTo=BranchTo
|
|
||||||
edge.phaseTo=PhaseNext
|
edge.branchFrom=PhaseFrom.branch
|
||||||
edge.conditionSwitch=ConditionSwitch or CONDITION:New("Edge")
|
edge.branchTo=PhaseTo.branch
|
||||||
|
|
||||||
|
if ConditionSwitch then
|
||||||
|
edge.conditionSwitch=ConditionSwitch
|
||||||
|
else
|
||||||
|
edge.conditionSwitch=CONDITION:New("Edge")
|
||||||
|
edge.conditionSwitch:SetNoneResult(true)
|
||||||
|
end
|
||||||
|
|
||||||
table.insert(edge.branchFrom.edges, edge)
|
table.insert(edge.branchFrom.edges, edge)
|
||||||
|
|
||||||
@ -699,16 +791,18 @@ function OPERATION:AddEdge(BranchTo, PhaseAfter, PhaseNext, ConditionSwitch)
|
|||||||
end
|
end
|
||||||
|
|
||||||
--- Add condition function to an edge when branches are switched. The function must return a `#boolean`.
|
--- Add condition function to an edge when branches are switched. The function must return a `#boolean`.
|
||||||
|
-- If multiple condition functions are added, all of these must return true for the branch switch to occur.
|
||||||
-- @param #OPERATION self
|
-- @param #OPERATION self
|
||||||
-- @param #OPERATION.Edge Edge The edge connecting the two branches.
|
-- @param #OPERATION.Edge Edge The edge connecting the two branches.
|
||||||
-- @param #function Function Function that needs to be `true` for switching between the branches.
|
-- @param #function Function Function that needs to be `true` for switching between the branches.
|
||||||
-- @param ... Condition function arguments if any.
|
-- @param ... Condition function arguments if any.
|
||||||
-- @return #OPERATION self
|
-- @return Core.Condition#CONDITION.Function Condition function table.
|
||||||
function OPERATION:AddEdgeConditonSwitchAll(Edge, Function, ...)
|
function OPERATION:AddEdgeConditonSwitchAll(Edge, Function, ...)
|
||||||
if Edge then
|
if Edge then
|
||||||
Edge.conditionSwitch:AddFunctionAll(Function, ...)
|
local cf=Edge.conditionSwitch:AddFunctionAll(Function, ...)
|
||||||
|
return cf
|
||||||
end
|
end
|
||||||
return self
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Add mission to operation.
|
--- Add mission to operation.
|
||||||
@ -739,9 +833,9 @@ function OPERATION:AddTarget(Target, Phase)
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Add Targets from operation.
|
--- Get targets of operation.
|
||||||
-- @param #OPERATION self
|
-- @param #OPERATION self
|
||||||
-- @param #OPERATION.Phase Phase
|
-- @param #OPERATION.Phase Phase (Optional) Only return targets set for this phase. Default is targets of all phases.
|
||||||
-- @return #table Targets Table of #TARGET objects
|
-- @return #table Targets Table of #TARGET objects
|
||||||
function OPERATION:GetTargets(Phase)
|
function OPERATION:GetTargets(Phase)
|
||||||
local N = {}
|
local N = {}
|
||||||
@ -896,6 +990,61 @@ function OPERATION:IsStopped()
|
|||||||
return is
|
return is
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Check if operation is **not** "Over" or "Stopped".
|
||||||
|
-- @param #OPERATION self
|
||||||
|
-- @return #boolean If `true`, operation is not "Over" or "Stopped".
|
||||||
|
function OPERATION:IsNotOver()
|
||||||
|
local is=not (self:IsOver() or self:IsStopped())
|
||||||
|
return is
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Check if phase is in status "Active".
|
||||||
|
-- @param #OPERATION self
|
||||||
|
-- @param #OPERATION.Phase Phase The phase.
|
||||||
|
-- @return #boolean If `true`, phase is active.
|
||||||
|
function OPERATION:IsPhaseActive(Phase)
|
||||||
|
if Phase and Phase.status and Phase.status==OPERATION.PhaseStatus.ACTIVE then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Check if a phase is the currently active one.
|
||||||
|
-- @param #OPERATION self
|
||||||
|
-- @param #OPERATION.Phase Phase The phase to check.
|
||||||
|
-- @return #boolean If `true`, this phase is currently active.
|
||||||
|
function OPERATION:IsPhaseActive(Phase)
|
||||||
|
local phase=self:GetPhaseActive()
|
||||||
|
if phase and phase.uid==Phase.uid then
|
||||||
|
return true
|
||||||
|
else
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Check if phase is in status "Planned".
|
||||||
|
-- @param #OPERATION self
|
||||||
|
-- @param #OPERATION.Phase Phase The phase.
|
||||||
|
-- @return #boolean If `true`, phase is Planned.
|
||||||
|
function OPERATION:IsPhasePlanned(Phase)
|
||||||
|
if Phase and Phase.status and Phase.status==OPERATION.PhaseStatus.PLANNED then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Check if phase is in status "Over".
|
||||||
|
-- @param #OPERATION self
|
||||||
|
-- @param #OPERATION.Phase Phase The phase.
|
||||||
|
-- @return #boolean If `true`, phase is over.
|
||||||
|
function OPERATION:IsPhaseOver(Phase)
|
||||||
|
if Phase and Phase.status and Phase.status==OPERATION.PhaseStatus.OVER then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
-- Status Update
|
-- Status Update
|
||||||
@ -927,27 +1076,30 @@ function OPERATION:onafterStatusUpdate(From, Event, To)
|
|||||||
-- Current FSM state.
|
-- Current FSM state.
|
||||||
local fsmstate=self:GetState()
|
local fsmstate=self:GetState()
|
||||||
|
|
||||||
|
-- Start operation.
|
||||||
if self:IsPlanned() then
|
if self:IsPlanned() then
|
||||||
if self.Tstart and Tnow>self.Tstart then
|
|
||||||
|
-- Start operation if start time has passed (if any) and start condition(s) are met (if any).
|
||||||
|
if (self.Tstart and Tnow>self.Tstart or self.Tstart==nil) and (self.conditionStart==nil or self.conditionStart:Evaluate()) then
|
||||||
self:Start()
|
self:Start()
|
||||||
end
|
end
|
||||||
end
|
|
||||||
if (self.Tstop and Tnow>self.Tstop) and not (self:IsOver() or self:IsStopped()) then
|
elseif self:IsNotOver() then
|
||||||
self:Over()
|
|
||||||
|
-- Operation is over if stop time has passed (if any) and over condition(s) are met (if any).
|
||||||
|
if (self.Tstop and Tnow>self.Tstop or self.Tstop==nil) and (self.conditionOver==nil or self.conditionOver:Evaluate()) then
|
||||||
|
self:Over()
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if (not self:IsRunning()) and (self.conditionStart and self.conditionStart:Evaluate()) then
|
|
||||||
self:Start()
|
|
||||||
end
|
|
||||||
if self:IsRunning() and (self.conditionStop and self.conditionStop:Evaluate()) then
|
|
||||||
self:Over()
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Check phases.
|
-- Check phases.
|
||||||
if self:IsRunning() then
|
if self:IsRunning() then
|
||||||
self:_CheckPhases()
|
self:_CheckPhases()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
-- Debug output.
|
-- Debug output.
|
||||||
if self.verbose>=1 then
|
if self.verbose>=1 then
|
||||||
|
|
||||||
@ -972,7 +1124,7 @@ function OPERATION:onafterStatusUpdate(From, Event, To)
|
|||||||
local text="Phases:"
|
local text="Phases:"
|
||||||
for i,_phase in pairs(self.branchActive.phases) do
|
for i,_phase in pairs(self.branchActive.phases) do
|
||||||
local phase=_phase --#OPERATION.Phase
|
local phase=_phase --#OPERATION.Phase
|
||||||
text=text..string.format("\n[%d] %s: status=%s", i, phase.name, tostring(phase.status))
|
text=text..string.format("\n[%d] %s [uid=%d]: status=%s Nact=%d", i, phase.name, phase.uid, tostring(phase.status), phase.nActive)
|
||||||
end
|
end
|
||||||
if text=="Phases:" then text=text.." None" end
|
if text=="Phases:" then text=text.." None" end
|
||||||
self:I(self.lid..text)
|
self:I(self.lid..text)
|
||||||
@ -1026,7 +1178,9 @@ function OPERATION:onafterPhaseChange(From, Event, To, Phase)
|
|||||||
-- Previous phase (if any).
|
-- Previous phase (if any).
|
||||||
local oldphase="None"
|
local oldphase="None"
|
||||||
if self.phase then
|
if self.phase then
|
||||||
self:SetPhaseStatus(self.phase, OPERATION.PhaseStatus.OVER)
|
if self.phase.status~=OPERATION.PhaseStatus.OVER then
|
||||||
|
self:SetPhaseStatus(self.phase, OPERATION.PhaseStatus.OVER)
|
||||||
|
end
|
||||||
oldphase=self.phase.name
|
oldphase=self.phase.name
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -1042,13 +1196,27 @@ function OPERATION:onafterPhaseChange(From, Event, To, Phase)
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- On after "PhaseOver" event.
|
||||||
|
-- @param #OPERATION self
|
||||||
|
-- @param #string From From state.
|
||||||
|
-- @param #string Event Event.
|
||||||
|
-- @param #string To To state.
|
||||||
|
-- @param #OPERATION.Phase Phase The phase that is over.
|
||||||
|
function OPERATION:onafterPhaseOver(From, Event, To, Phase)
|
||||||
|
|
||||||
|
-- Remove all non-persistant condition functions.
|
||||||
|
Phase.conditionOver:RemoveNonPersistant()
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
--- On after "BranchSwitch" event.
|
--- On after "BranchSwitch" event.
|
||||||
-- @param #OPERATION self
|
-- @param #OPERATION self
|
||||||
-- @param #string From From state.
|
-- @param #string From From state.
|
||||||
-- @param #string Event Event.
|
-- @param #string Event Event.
|
||||||
-- @param #string To To state.
|
-- @param #string To To state.
|
||||||
-- @param #OPERATION.Branch Branch The new branch.
|
-- @param #OPERATION.Branch Branch The new branch.
|
||||||
function OPERATION:onafterBranchSwitch(From, Event, To, Branch)
|
-- @param #OPERATION.Phase Phase The phase.
|
||||||
|
function OPERATION:onafterBranchSwitch(From, Event, To, Branch, Phase)
|
||||||
|
|
||||||
-- Debug info.
|
-- Debug info.
|
||||||
self:T(self.lid..string.format("Switching to branch %s", Branch.name))
|
self:T(self.lid..string.format("Switching to branch %s", Branch.name))
|
||||||
@ -1056,6 +1224,9 @@ function OPERATION:onafterBranchSwitch(From, Event, To, Branch)
|
|||||||
-- Set active branch.
|
-- Set active branch.
|
||||||
self.branchActive=Branch
|
self.branchActive=Branch
|
||||||
|
|
||||||
|
-- Change phase.
|
||||||
|
self:PhaseChange(Phase)
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -1064,7 +1235,6 @@ end
|
|||||||
-- @param #string From From state.
|
-- @param #string From From state.
|
||||||
-- @param #string Event Event.
|
-- @param #string Event Event.
|
||||||
-- @param #string To To state.
|
-- @param #string To To state.
|
||||||
-- @param #OPERATION.Phase Phase The new phase.
|
|
||||||
function OPERATION:onafterOver(From, Event, To)
|
function OPERATION:onafterOver(From, Event, To)
|
||||||
|
|
||||||
-- Debug message.
|
-- Debug message.
|
||||||
@ -1078,7 +1248,9 @@ function OPERATION:onafterOver(From, Event, To)
|
|||||||
local branch=_branch --#OPERATION.Branch
|
local branch=_branch --#OPERATION.Branch
|
||||||
for _,_phase in pairs(branch.phases) do
|
for _,_phase in pairs(branch.phases) do
|
||||||
local phase=_phase --#OPERATION.Phase
|
local phase=_phase --#OPERATION.Phase
|
||||||
self:SetPhaseStatus(phase, OPERATION.PhaseStatus.OVER)
|
if not self:IsPhaseOver(phase) then
|
||||||
|
self:SetPhaseStatus(phase, OPERATION.PhaseStatus.OVER)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -1098,7 +1270,18 @@ function OPERATION:_CheckPhases()
|
|||||||
|
|
||||||
-- Check if active phase is over if conditon over is defined.
|
-- Check if active phase is over if conditon over is defined.
|
||||||
if phase and phase.conditionOver then
|
if phase and phase.conditionOver then
|
||||||
|
|
||||||
|
-- Evaluate if phase is over.
|
||||||
local isOver=phase.conditionOver:Evaluate()
|
local isOver=phase.conditionOver:Evaluate()
|
||||||
|
|
||||||
|
local Tnow=timer.getAbsTime()
|
||||||
|
|
||||||
|
-- Check if duration of phase if over.
|
||||||
|
if phase.duration and phase.Tstart and Tnow-phase.Tstart>phase.duration then
|
||||||
|
isOver=true
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Set phase status to over. This also triggers the PhaseOver() event.
|
||||||
if isOver then
|
if isOver then
|
||||||
self:SetPhaseStatus(phase, OPERATION.PhaseStatus.OVER)
|
self:SetPhaseStatus(phase, OPERATION.PhaseStatus.OVER)
|
||||||
end
|
end
|
||||||
@ -1110,6 +1293,11 @@ function OPERATION:_CheckPhases()
|
|||||||
for _,_edge in pairs(self.branchActive.edges) do
|
for _,_edge in pairs(self.branchActive.edges) do
|
||||||
local edge=_edge --#OPERATION.Edge
|
local edge=_edge --#OPERATION.Edge
|
||||||
|
|
||||||
|
if phase then
|
||||||
|
--env.info(string.format("phase active uid=%d", phase.uid))
|
||||||
|
--env.info(string.format("Phase from uid=%d", edge.phaseFrom.uid))
|
||||||
|
end
|
||||||
|
|
||||||
if (edge.phaseFrom==nil) or (phase and edge.phaseFrom.uid==phase.uid) then
|
if (edge.phaseFrom==nil) or (phase and edge.phaseFrom.uid==phase.uid) then
|
||||||
|
|
||||||
-- Evaluate switch condition.
|
-- Evaluate switch condition.
|
||||||
@ -1117,26 +1305,28 @@ function OPERATION:_CheckPhases()
|
|||||||
|
|
||||||
if switch then
|
if switch then
|
||||||
|
|
||||||
-- Switch to new branch.
|
-- Get next phase of the branch
|
||||||
self:BranchSwitch(edge.branchTo)
|
local phaseTo=edge.phaseTo or self:GetPhaseNext(edge.branchTo, nil)
|
||||||
|
|
||||||
-- If we want to switch to a specific phase of the branch.
|
if phaseTo then
|
||||||
if edge.phaseTo then
|
|
||||||
|
|
||||||
-- Change phase.
|
-- Switch to new branch.
|
||||||
self:PhaseChange(edge.phaseTo)
|
self:BranchSwitch(edge.branchTo, phaseTo)
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
-- No next phase ==> Ops is over!
|
||||||
|
self:Over()
|
||||||
|
|
||||||
-- Done here!
|
|
||||||
return
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Break the loop.
|
-- Done here!
|
||||||
break
|
return
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Next phase.
|
-- Next phase.
|
||||||
self:PhaseNext()
|
self:PhaseNext()
|
||||||
|
|
||||||
@ -1157,7 +1347,9 @@ function OPERATION:_CreatePhase(Name)
|
|||||||
phase.uid=self.counterPhase
|
phase.uid=self.counterPhase
|
||||||
phase.name=Name or string.format("Phase-%02d", self.counterPhase)
|
phase.name=Name or string.format("Phase-%02d", self.counterPhase)
|
||||||
phase.conditionOver=CONDITION:New(Name.." Over")
|
phase.conditionOver=CONDITION:New(Name.." Over")
|
||||||
|
phase.conditionOver:SetDefaultPersistence(false)
|
||||||
phase.status=OPERATION.PhaseStatus.PLANNED
|
phase.status=OPERATION.PhaseStatus.PLANNED
|
||||||
|
phase.nActive=0
|
||||||
|
|
||||||
return phase
|
return phase
|
||||||
end
|
end
|
||||||
|
|||||||
@ -335,6 +335,7 @@ OPSGROUP.TaskType={
|
|||||||
-- @field #number waypoint Waypoint index if task is a waypoint task.
|
-- @field #number waypoint Waypoint index if task is a waypoint task.
|
||||||
-- @field Core.UserFlag#USERFLAG stopflag If flag is set to 1 (=true), the task is stopped.
|
-- @field Core.UserFlag#USERFLAG stopflag If flag is set to 1 (=true), the task is stopped.
|
||||||
-- @field #number backupROE Rules of engagement that are restored once the task is over.
|
-- @field #number backupROE Rules of engagement that are restored once the task is over.
|
||||||
|
-- @field Ops.Target#TARGET target Target object.
|
||||||
|
|
||||||
--- Option data.
|
--- Option data.
|
||||||
-- @type OPSGROUP.Option
|
-- @type OPSGROUP.Option
|
||||||
@ -499,7 +500,7 @@ OPSGROUP.CargoStatus={
|
|||||||
|
|
||||||
--- OpsGroup version.
|
--- OpsGroup version.
|
||||||
-- @field #string version
|
-- @field #string version
|
||||||
OPSGROUP.version="0.8.0"
|
OPSGROUP.version="0.9.0"
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
-- TODO list
|
-- TODO list
|
||||||
@ -885,10 +886,10 @@ function OPSGROUP:GetCoalition()
|
|||||||
return self.group:GetCoalition()
|
return self.group:GetCoalition()
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Returns the absolute (average) life points of the group.
|
--- Returns the absolute total life points of the group.
|
||||||
-- @param #OPSGROUP self
|
-- @param #OPSGROUP self
|
||||||
-- @param #OPSGROUP.Element Element (Optional) Only get life points of this element.
|
-- @param #OPSGROUP.Element Element (Optional) Only get life points of this element.
|
||||||
-- @return #number Life points. If group contains more than one element, the average is given.
|
-- @return #number Life points, *i.e.* the sum of life points over all units in the group (unless a specific element was passed).
|
||||||
-- @return #number Initial life points.
|
-- @return #number Initial life points.
|
||||||
function OPSGROUP:GetLifePoints(Element)
|
function OPSGROUP:GetLifePoints(Element)
|
||||||
|
|
||||||
@ -3315,7 +3316,13 @@ function OPSGROUP:RemoveWaypoint(wpindex)
|
|||||||
else
|
else
|
||||||
self.currentwp=self.currentwp-1
|
self.currentwp=self.currentwp-1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Could be that the waypoint we are currently moving to was the LAST waypoint. Then we now passed the final waypoint.
|
||||||
|
if (self.adinfinitum or istemp) then
|
||||||
|
self:_PassedFinalWaypoint(false, "Removed PASSED temporary waypoint ")
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
@ -4043,11 +4050,16 @@ end
|
|||||||
-- @param #string To To state.
|
-- @param #string To To state.
|
||||||
-- @param Ops.OpsGroup#OPSGROUP.Task Task The task.
|
-- @param Ops.OpsGroup#OPSGROUP.Task Task The task.
|
||||||
function OPSGROUP:onafterTaskExecute(From, Event, To, Task)
|
function OPSGROUP:onafterTaskExecute(From, Event, To, Task)
|
||||||
self:T({Task})
|
|
||||||
-- Debug message.
|
-- Debug message.
|
||||||
local text=string.format("Task %s ID=%d execute", tostring(Task.description), Task.id)
|
local text=string.format("Task %s ID=%d execute", tostring(Task.description), Task.id)
|
||||||
|
|
||||||
|
-- Debug info.
|
||||||
self:T(self.lid..text)
|
self:T(self.lid..text)
|
||||||
self:T({Task})
|
|
||||||
|
-- Debug info.
|
||||||
|
self:T2({Task})
|
||||||
|
|
||||||
-- Cancel current task if there is any.
|
-- Cancel current task if there is any.
|
||||||
if self.taskcurrent>0 then
|
if self.taskcurrent>0 then
|
||||||
self:TaskCancel()
|
self:TaskCancel()
|
||||||
@ -4070,7 +4082,7 @@ function OPSGROUP:onafterTaskExecute(From, Event, To, Task)
|
|||||||
-- Get mission of this task (if any).
|
-- Get mission of this task (if any).
|
||||||
local Mission=self:GetMissionByTaskID(self.taskcurrent)
|
local Mission=self:GetMissionByTaskID(self.taskcurrent)
|
||||||
|
|
||||||
|
-- Update push DCS task.
|
||||||
self:_UpdateTask(Task, Mission)
|
self:_UpdateTask(Task, Mission)
|
||||||
|
|
||||||
-- Set AUFTRAG status.
|
-- Set AUFTRAG status.
|
||||||
@ -4080,12 +4092,13 @@ function OPSGROUP:onafterTaskExecute(From, Event, To, Task)
|
|||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Push task
|
--- Update (DCS) task.
|
||||||
-- @param #OPSGROUP self
|
-- @param #OPSGROUP self
|
||||||
-- @param Ops.OpsGroup#OPSGROUP.Task Task The task.
|
-- @param Ops.OpsGroup#OPSGROUP.Task Task The task.
|
||||||
|
-- @param Ops.Auftrag#AUFTRAG Mission The mission.
|
||||||
function OPSGROUP:_UpdateTask(Task, Mission)
|
function OPSGROUP:_UpdateTask(Task, Mission)
|
||||||
|
|
||||||
local Mission=Mission or self:GetMissionByTaskID(self.taskcurrent)
|
Mission=Mission or self:GetMissionByTaskID(self.taskcurrent)
|
||||||
|
|
||||||
if Task.dcstask.id==AUFTRAG.SpecialTask.FORMATION then
|
if Task.dcstask.id==AUFTRAG.SpecialTask.FORMATION then
|
||||||
|
|
||||||
@ -4218,6 +4231,14 @@ function OPSGROUP:_UpdateTask(Task, Mission)
|
|||||||
-- Check if ammo is full.
|
-- Check if ammo is full.
|
||||||
|
|
||||||
local rearmed=self:_CheckAmmoFull()
|
local rearmed=self:_CheckAmmoFull()
|
||||||
|
|
||||||
|
if rearmed then
|
||||||
|
self:T2(self.lid.."Ammo already full ==> reaming task done!")
|
||||||
|
self:TaskDone(Task)
|
||||||
|
else
|
||||||
|
self:T2(self.lid.."Ammo not full ==> Rearm()")
|
||||||
|
self:Rearm()
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
elseif Task.dcstask.id==AUFTRAG.SpecialTask.ALERT5 then
|
elseif Task.dcstask.id==AUFTRAG.SpecialTask.ALERT5 then
|
||||||
@ -4359,6 +4380,79 @@ function OPSGROUP:_UpdateTask(Task, Mission)
|
|||||||
end
|
end
|
||||||
|
|
||||||
wp.missionUID=Mission and Mission.auftragsnummer or nil
|
wp.missionUID=Mission and Mission.auftragsnummer or nil
|
||||||
|
|
||||||
|
elseif Task.dcstask.id==AUFTRAG.SpecialTask.CAPTUREZONE then
|
||||||
|
|
||||||
|
---
|
||||||
|
-- Task "CaptureZone" Mission.
|
||||||
|
-- Check if zone was captured or find new target to engage.
|
||||||
|
---
|
||||||
|
|
||||||
|
-- Not enganging already.
|
||||||
|
if self:IsEngaging() then
|
||||||
|
|
||||||
|
-- Group is currently engaging an enemy unit to capture the zone.
|
||||||
|
self:T2(self.lid..string.format("CaptureZone: Engaging currently!"))
|
||||||
|
else
|
||||||
|
|
||||||
|
-- Get enemy coalitions. We do not include neutrals.
|
||||||
|
local Coalitions=UTILS.GetCoalitionEnemy(self:GetCoalition(), false)
|
||||||
|
|
||||||
|
-- Current target object.
|
||||||
|
local zoneCurr=Task.target --Ops.OpsZone#OPSZONE
|
||||||
|
|
||||||
|
if zoneCurr then
|
||||||
|
|
||||||
|
self:T(self.lid..string.format("Current target zone=%s owner=%s", zoneCurr:GetName(), zoneCurr:GetOwnerName()))
|
||||||
|
|
||||||
|
if zoneCurr:GetOwner()==self:GetCoalition() then
|
||||||
|
-- Current zone captured ==> Find next zone or call it a day!
|
||||||
|
|
||||||
|
-- Debug info.
|
||||||
|
self:T(self.lid..string.format("Zone %s captured ==> Task DONE!", zoneCurr:GetName()))
|
||||||
|
|
||||||
|
-- Task done.
|
||||||
|
self:TaskDone(Task)
|
||||||
|
|
||||||
|
else
|
||||||
|
-- Current zone NOT captured yet ==> Find Target
|
||||||
|
|
||||||
|
-- Debug info.
|
||||||
|
self:T(self.lid..string.format("Zone %s NOT captured!", zoneCurr:GetName()))
|
||||||
|
|
||||||
|
if Mission:GetGroupStatus(self)==AUFTRAG.GroupStatus.EXECUTING then
|
||||||
|
|
||||||
|
-- Debug info.
|
||||||
|
self:T(self.lid..string.format("Zone %s NOT captured and EXECUTING ==> Find target", zoneCurr:GetName()))
|
||||||
|
|
||||||
|
|
||||||
|
-- Get closest target.
|
||||||
|
local targetgroup=zoneCurr:GetScannedGroupSet():GetClosestGroup(self.coordinate, Coalitions)
|
||||||
|
|
||||||
|
if targetgroup then
|
||||||
|
|
||||||
|
-- Debug info.
|
||||||
|
self:T(self.lid..string.format("Zone %s NOT captured: engaging target %s", zoneCurr:GetName(), targetgroup:GetName()))
|
||||||
|
|
||||||
|
-- Engage target group.
|
||||||
|
self:EngageTarget(targetgroup)
|
||||||
|
|
||||||
|
else
|
||||||
|
-- Error Message.
|
||||||
|
self:E(self.lid..string.format("ERROR: Current zone not captured but no target group could be found. This should NOT happen!"))
|
||||||
|
end
|
||||||
|
|
||||||
|
else
|
||||||
|
self:T(self.lid..string.format("Zone %s NOT captured and NOT EXECUTING", zoneCurr:GetName()))
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
else
|
||||||
|
self:T(self.lid..string.format("NO Current target zone=%s"))
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
else
|
else
|
||||||
|
|
||||||
@ -4643,9 +4737,21 @@ function OPSGROUP:onafterTaskDone(From, Event, To, Task)
|
|||||||
-- Check if mission is paused.
|
-- Check if mission is paused.
|
||||||
if status~=AUFTRAG.GroupStatus.PAUSED then
|
if status~=AUFTRAG.GroupStatus.PAUSED then
|
||||||
---
|
---
|
||||||
-- Mission is NOT over ==> trigger done
|
-- Mission is NOT over ==> trigger DONE
|
||||||
---
|
---
|
||||||
|
|
||||||
|
if Mission.type==AUFTRAG.Type.CAPTUREZONE and Mission:CountMissionTargets()>0 then
|
||||||
|
|
||||||
|
-- Remove mission waypoints.
|
||||||
|
self:T(self.lid.."Remove mission waypoints")
|
||||||
|
self:_RemoveMissionWaypoints(Mission, false)
|
||||||
|
|
||||||
|
self:T(self.lid.."Task done ==> Route to mission for next opszone")
|
||||||
|
self:MissionStart(Mission)
|
||||||
|
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
-- Get egress waypoint uid.
|
-- Get egress waypoint uid.
|
||||||
local EgressUID=Mission:GetGroupEgressWaypointUID(self)
|
local EgressUID=Mission:GetGroupEgressWaypointUID(self)
|
||||||
|
|
||||||
@ -5477,6 +5583,13 @@ function OPSGROUP:RouteToMission(mission, delay)
|
|||||||
surfacetypes={land.SurfaceType.WATER, land.SurfaceType.SHALLOW_WATER}
|
surfacetypes={land.SurfaceType.WATER, land.SurfaceType.SHALLOW_WATER}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Get target object.
|
||||||
|
local targetobject=mission:GetObjective(currentcoord, UTILS.GetCoalitionEnemy(self:GetCoalition(), true))
|
||||||
|
|
||||||
|
if targetobject then
|
||||||
|
self:T(self.lid..string.format("Route to mission target object %s", targetobject:GetName()))
|
||||||
|
end
|
||||||
|
|
||||||
-- Get ingress waypoint.
|
-- Get ingress waypoint.
|
||||||
if mission.opstransport and not mission.opstransport:IsCargoDelivered(self.groupname) then
|
if mission.opstransport and not mission.opstransport:IsCargoDelivered(self.groupname) then
|
||||||
|
|
||||||
@ -5507,7 +5620,7 @@ function OPSGROUP:RouteToMission(mission, delay)
|
|||||||
---
|
---
|
||||||
|
|
||||||
-- Get the zone.
|
-- Get the zone.
|
||||||
targetzone=mission.engageTarget:GetObject() --Core.Zone#ZONE
|
targetzone=targetobject --Core.Zone#ZONE
|
||||||
|
|
||||||
-- Random coordinate.
|
-- Random coordinate.
|
||||||
waypointcoord=targetzone:GetRandomCoordinate(nil , nil, surfacetypes)
|
waypointcoord=targetzone:GetRandomCoordinate(nil , nil, surfacetypes)
|
||||||
@ -5526,7 +5639,7 @@ function OPSGROUP:RouteToMission(mission, delay)
|
|||||||
---
|
---
|
||||||
|
|
||||||
-- Get the zone.
|
-- Get the zone.
|
||||||
targetzone=mission.engageTarget:GetObject() --Core.Zone#ZONE
|
targetzone=targetobject --Core.Zone#ZONE
|
||||||
|
|
||||||
-- Random coordinate.
|
-- Random coordinate.
|
||||||
waypointcoord=targetzone:GetRandomCoordinate(nil , nil, surfacetypes)
|
waypointcoord=targetzone:GetRandomCoordinate(nil , nil, surfacetypes)
|
||||||
@ -5536,7 +5649,8 @@ function OPSGROUP:RouteToMission(mission, delay)
|
|||||||
-- Hover
|
-- Hover
|
||||||
---
|
---
|
||||||
|
|
||||||
local zone=mission.engageTarget:GetObject() --Core.Zone#ZONE
|
local zone=targetobject --Core.Zone#ZONE
|
||||||
|
|
||||||
waypointcoord=zone:GetCoordinate()
|
waypointcoord=zone:GetCoordinate()
|
||||||
|
|
||||||
elseif mission.type==AUFTRAG.Type.RELOCATECOHORT then
|
elseif mission.type==AUFTRAG.Type.RELOCATECOHORT then
|
||||||
@ -5561,6 +5675,15 @@ function OPSGROUP:RouteToMission(mission, delay)
|
|||||||
-- Navy group: Route into direction of the target.
|
-- Navy group: Route into direction of the target.
|
||||||
waypointcoord=currentcoord:GetIntermediateCoordinate(ToCoordinate, 0.05)
|
waypointcoord=currentcoord:GetIntermediateCoordinate(ToCoordinate, 0.05)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
elseif mission.type==AUFTRAG.Type.CAPTUREZONE then
|
||||||
|
|
||||||
|
-- Get the zone.
|
||||||
|
targetzone=targetobject:GetZone()
|
||||||
|
|
||||||
|
-- Random coordinate.
|
||||||
|
waypointcoord=targetzone:GetRandomCoordinate(nil , nil, surfacetypes)
|
||||||
|
|
||||||
|
|
||||||
else
|
else
|
||||||
---
|
---
|
||||||
@ -5676,6 +5799,7 @@ function OPSGROUP:RouteToMission(mission, delay)
|
|||||||
end
|
end
|
||||||
|
|
||||||
waypoint=ARMYGROUP.AddWaypoint(self, waypointcoord, SpeedToMission, uid, formation, false)
|
waypoint=ARMYGROUP.AddWaypoint(self, waypointcoord, SpeedToMission, uid, formation, false)
|
||||||
|
|
||||||
elseif self:IsNavygroup() then
|
elseif self:IsNavygroup() then
|
||||||
|
|
||||||
waypoint=NAVYGROUP.AddWaypoint(self, waypointcoord, SpeedToMission, uid, UTILS.MetersToFeet(mission.missionAltitude or self.altitudeCruise), false)
|
waypoint=NAVYGROUP.AddWaypoint(self, waypointcoord, SpeedToMission, uid, UTILS.MetersToFeet(mission.missionAltitude or self.altitudeCruise), false)
|
||||||
@ -5686,6 +5810,8 @@ function OPSGROUP:RouteToMission(mission, delay)
|
|||||||
-- Add waypoint task. UpdateRoute is called inside.
|
-- Add waypoint task. UpdateRoute is called inside.
|
||||||
local waypointtask=self:AddTaskWaypoint(mission.DCStask, waypoint, mission.name, mission.prio, mission.duration)
|
local waypointtask=self:AddTaskWaypoint(mission.DCStask, waypoint, mission.name, mission.prio, mission.duration)
|
||||||
waypointtask.ismission=true
|
waypointtask.ismission=true
|
||||||
|
|
||||||
|
waypointtask.target=targetobject
|
||||||
|
|
||||||
-- Set waypoint task.
|
-- Set waypoint task.
|
||||||
mission:SetGroupWaypointTask(self, waypointtask)
|
mission:SetGroupWaypointTask(self, waypointtask)
|
||||||
@ -5712,6 +5838,8 @@ function OPSGROUP:RouteToMission(mission, delay)
|
|||||||
if targetzone and self:IsInZone(targetzone) then
|
if targetzone and self:IsInZone(targetzone) then
|
||||||
self:T(self.lid.."Already in mission zone ==> TaskExecute()")
|
self:T(self.lid.."Already in mission zone ==> TaskExecute()")
|
||||||
self:TaskExecute(waypointtask)
|
self:TaskExecute(waypointtask)
|
||||||
|
-- TODO: Calling PassingWaypoint here is probably better as it marks the mission waypoint as passed!
|
||||||
|
--self:PassingWaypoint(waypoint)
|
||||||
return
|
return
|
||||||
elseif d<25 then
|
elseif d<25 then
|
||||||
self:T(self.lid.."Already within 25 meters of mission waypoint ==> TaskExecute()")
|
self:T(self.lid.."Already within 25 meters of mission waypoint ==> TaskExecute()")
|
||||||
@ -5744,7 +5872,7 @@ function OPSGROUP:RouteToMission(mission, delay)
|
|||||||
---
|
---
|
||||||
-- Mission Specific Settings
|
-- Mission Specific Settings
|
||||||
---
|
---
|
||||||
self:_SetMissionOptions(mission)
|
self:_SetMissionOptions(mission)
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -6070,6 +6198,18 @@ function OPSGROUP:onafterPassingWaypoint(From, Event, To, Waypoint)
|
|||||||
-- Final zone reached ==> task done.
|
-- Final zone reached ==> task done.
|
||||||
self:TaskDone(task)
|
self:TaskDone(task)
|
||||||
|
|
||||||
|
elseif task and task.dcstask.id==AUFTRAG.SpecialTask.REARMING then
|
||||||
|
|
||||||
|
---
|
||||||
|
-- SPECIAL TASK: Rearming Mission
|
||||||
|
---
|
||||||
|
|
||||||
|
-- Debug info.
|
||||||
|
self:T(self.lid..string.format("FF Rearming Mission ==> Rearm()"))
|
||||||
|
|
||||||
|
-- Call rearm event.
|
||||||
|
self:Rearm()
|
||||||
|
|
||||||
else
|
else
|
||||||
|
|
||||||
---
|
---
|
||||||
@ -6898,7 +7038,7 @@ function OPSGROUP:onafterElementDamaged(From, Event, To, Element)
|
|||||||
|
|
||||||
local lifepoints=0
|
local lifepoints=0
|
||||||
|
|
||||||
if Element.DCSunit and Element.DCSunit:isExist() then
|
if Element.DCSunit then --and Element.DCSunit:isExist() then
|
||||||
|
|
||||||
-- Get life of unit
|
-- Get life of unit
|
||||||
lifepoints=Element.DCSunit:getLife()
|
lifepoints=Element.DCSunit:getLife()
|
||||||
@ -7361,8 +7501,10 @@ function OPSGROUP:CancelAllMissions()
|
|||||||
-- Cancel all missions.
|
-- Cancel all missions.
|
||||||
for _,_mission in pairs(self.missionqueue) do
|
for _,_mission in pairs(self.missionqueue) do
|
||||||
local mission=_mission --Ops.Auftrag#AUFTRAG
|
local mission=_mission --Ops.Auftrag#AUFTRAG
|
||||||
self:T(self.lid.."Cancelling mission "..tostring(mission:GetName()))
|
if mission:IsNotOver() then
|
||||||
self:MissionCancel(mission)
|
self:T(self.lid.."Cancelling mission "..tostring(mission:GetName()))
|
||||||
|
self:MissionCancel(mission)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
@ -10118,28 +10260,32 @@ end
|
|||||||
-- @return #OPSGROUP self
|
-- @return #OPSGROUP self
|
||||||
function OPSGROUP:_CheckDamage()
|
function OPSGROUP:_CheckDamage()
|
||||||
|
|
||||||
|
self:T(self.lid..string.format("Checking damage..."))
|
||||||
|
|
||||||
self.life=0
|
self.life=0
|
||||||
local damaged=false
|
local damaged=false
|
||||||
|
|
||||||
for _,_element in pairs(self.elements) do
|
for _,_element in pairs(self.elements) do
|
||||||
local element=_element --Ops.OpsGroup#OPSGROUP.Element
|
local element=_element --Ops.OpsGroup#OPSGROUP.Element
|
||||||
|
|
||||||
if element.status~=OPSGROUP.ElementStatus.DEAD and element.status~=OPSGROUP.ElementStatus.INUTERO then
|
if element.status~=OPSGROUP.ElementStatus.DEAD and element.status~=OPSGROUP.ElementStatus.INUTERO then
|
||||||
|
|
||||||
-- Current life points.
|
|
||||||
local life=element.unit:GetLife()
|
|
||||||
|
|
||||||
self.life=self.life+life
|
|
||||||
|
|
||||||
if life<element.life then
|
|
||||||
element.life=life
|
|
||||||
self:ElementDamaged(element)
|
|
||||||
damaged=true
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
-- Current life points.
|
||||||
|
local life=element.unit:GetLife()
|
||||||
|
|
||||||
|
self.life=self.life+life
|
||||||
|
|
||||||
|
if life<element.life then
|
||||||
|
element.life=life
|
||||||
|
self:ElementDamaged(element)
|
||||||
|
damaged=true
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- If anyone in the group was damaged, trigger event.
|
||||||
if damaged then
|
if damaged then
|
||||||
self:Damaged()
|
self:Damaged()
|
||||||
end
|
end
|
||||||
@ -10179,7 +10325,7 @@ function OPSGROUP:_CheckAmmoStatus()
|
|||||||
|
|
||||||
-- Check if rearming is completed.
|
-- Check if rearming is completed.
|
||||||
if self:IsRearming() then
|
if self:IsRearming() then
|
||||||
if ammo.Total==self.ammo.Total then
|
if ammo.Total>=self.ammo.Total then
|
||||||
self:Rearmed()
|
self:Rearmed()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -30,6 +30,9 @@
|
|||||||
-- @field #number Nred Number of red units in the zone.
|
-- @field #number Nred Number of red units in the zone.
|
||||||
-- @field #number Nblu Number of blue units in the zone.
|
-- @field #number Nblu Number of blue units in the zone.
|
||||||
-- @field #number Nnut Number of neutral units in the zone.
|
-- @field #number Nnut Number of neutral units in the zone.
|
||||||
|
-- @field #number Tred Threat level of red units in the zone.
|
||||||
|
-- @field #number Tblu Threat level of blue units in the zone.
|
||||||
|
-- @field #number Tnut Threat level of neutral units in the zone.
|
||||||
-- @field #number TminCaptured Time interval in seconds how long an attacker must have troops inside the zone to capture.
|
-- @field #number TminCaptured Time interval in seconds how long an attacker must have troops inside the zone to capture.
|
||||||
-- @field #number Tcaptured Time stamp (abs.) when the attacker destroyed all owning troops.
|
-- @field #number Tcaptured Time stamp (abs.) when the attacker destroyed all owning troops.
|
||||||
-- @field #table ObjectCategories Object categories for the scan.
|
-- @field #table ObjectCategories Object categories for the scan.
|
||||||
@ -43,6 +46,10 @@
|
|||||||
-- @field #string markerText Text shown in the maker.
|
-- @field #string markerText Text shown in the maker.
|
||||||
-- @field #table chiefs Chiefs that monitor this zone.
|
-- @field #table chiefs Chiefs that monitor this zone.
|
||||||
-- @field #table Missions Missions that are attached to this OpsZone.
|
-- @field #table Missions Missions that are attached to this OpsZone.
|
||||||
|
-- @field #number nunitsCapture Number of units necessary to capture a zone.
|
||||||
|
-- @field #number threatlevelCapture Threat level necessary to capture a zone.
|
||||||
|
-- @field Core.Set#SET_UNIT ScanUnitSet Set of scanned units.
|
||||||
|
-- @field Core.Set#SET_GROUP ScanGroupSet Set of scanned groups.
|
||||||
-- @extends Core.Fsm#FSM
|
-- @extends Core.Fsm#FSM
|
||||||
|
|
||||||
--- *Gentlemen, when the enemy is committed to a mistake we must not interrupt him too soon.* --- Horation Nelson
|
--- *Gentlemen, when the enemy is committed to a mistake we must not interrupt him too soon.* --- Horation Nelson
|
||||||
@ -64,6 +71,9 @@ OPSZONE = {
|
|||||||
Nred = 0,
|
Nred = 0,
|
||||||
Nblu = 0,
|
Nblu = 0,
|
||||||
Nnut = 0,
|
Nnut = 0,
|
||||||
|
Tred = 0,
|
||||||
|
Tblu = 0,
|
||||||
|
Tnut = 0,
|
||||||
chiefs = {},
|
chiefs = {},
|
||||||
Missions = {},
|
Missions = {},
|
||||||
}
|
}
|
||||||
@ -76,16 +86,16 @@ OPSZONE = {
|
|||||||
|
|
||||||
--- OPSZONE class version.
|
--- OPSZONE class version.
|
||||||
-- @field #string version
|
-- @field #string version
|
||||||
OPSZONE.version="0.3.1"
|
OPSZONE.version="0.4.0"
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
-- ToDo list
|
-- ToDo list
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
-- TODO: Capturing based on (total) threat level threshold. Unarmed units do not pose a threat and should not be able to hold a zone.
|
|
||||||
-- TODO: Pause/unpause evaluations.
|
-- TODO: Pause/unpause evaluations.
|
||||||
-- TODO: Capture time, i.e. time how long a single coalition has to be inside the zone to capture it.
|
|
||||||
-- TODO: Differentiate between ground attack and boming by air or arty.
|
-- TODO: Differentiate between ground attack and boming by air or arty.
|
||||||
|
-- DONE: Capture time, i.e. time how long a single coalition has to be inside the zone to capture it.
|
||||||
|
-- DONE: Capturing based on (total) threat level threshold. Unarmed units do not pose a threat and should not be able to hold a zone.
|
||||||
-- DONE: Can neutrals capture? No, since they are _neutral_!
|
-- DONE: Can neutrals capture? No, since they are _neutral_!
|
||||||
-- DONE: Capture airbases.
|
-- DONE: Capture airbases.
|
||||||
-- DONE: Can statics capture or hold a zone? No, unless explicitly requested by mission designer.
|
-- DONE: Can statics capture or hold a zone? No, unless explicitly requested by mission designer.
|
||||||
@ -100,10 +110,10 @@ OPSZONE.version="0.3.1"
|
|||||||
-- @param #number CoalitionOwner Initial owner of the coaliton. Default `coalition.side.NEUTRAL`.
|
-- @param #number CoalitionOwner Initial owner of the coaliton. Default `coalition.side.NEUTRAL`.
|
||||||
-- @return #OPSZONE self
|
-- @return #OPSZONE self
|
||||||
-- @usage
|
-- @usage
|
||||||
-- myopszone = OPSZONE:New(ZONE:FindByName("OpsZoneOne"),coalition.side.RED) -- base zone from the mission editor
|
-- myopszone = OPSZONE:New(ZONE:FindByName("OpsZoneOne"), coalition.side.RED) -- base zone from the mission editor
|
||||||
-- myopszone = OPSZONE:New(ZONE_RADIUS:New("OpsZoneTwo",mycoordinate:GetVec2(),5000),coalition.side.BLUE) -- radius zone of 5km at a coordinate
|
-- myopszone = OPSZONE:New(ZONE_RADIUS:New("OpsZoneTwo", mycoordinate:GetVec2(),5000),coalition.side.BLUE) -- radius zone of 5km at a coordinate
|
||||||
-- myopszone = OPSZONE:New(ZONE_RADIUS:New("Batumi")) -- airbase zone from Batumi Airbase, ca 2500m radius
|
-- myopszone = OPSZONE:New(ZONE_RADIUS:New("Batumi")) -- airbase zone from Batumi Airbase, ca 2500m radius
|
||||||
-- myopszone = OPSZONE:New(ZONE_AIRBASE:New("Batumi",6000),coalition.side.BLUE) -- airbase zone from Batumi Airbase, but with a specific radius of 6km
|
-- myopszone = OPSZONE:New(ZONE_AIRBASE:New("Batumi",6000),coalition.side.BLUE) -- airbase zone from Batumi Airbase, but with a specific radius of 6km
|
||||||
--
|
--
|
||||||
function OPSZONE:New(Zone, CoalitionOwner)
|
function OPSZONE:New(Zone, CoalitionOwner)
|
||||||
|
|
||||||
@ -151,6 +161,11 @@ function OPSZONE:New(Zone, CoalitionOwner)
|
|||||||
self.zoneName=Zone:GetName()
|
self.zoneName=Zone:GetName()
|
||||||
self.zoneRadius=Zone:GetRadius()
|
self.zoneRadius=Zone:GetRadius()
|
||||||
self.Missions = {}
|
self.Missions = {}
|
||||||
|
self.ScanUnitSet=SET_UNIT:New():FilterZones({Zone})
|
||||||
|
self.ScanGroupSet=SET_GROUP:New():FilterZones({Zone})
|
||||||
|
|
||||||
|
-- Add to database.
|
||||||
|
_DATABASE:AddOpsZone(self)
|
||||||
|
|
||||||
-- Current and previous owners.
|
-- Current and previous owners.
|
||||||
self.ownerCurrent=CoalitionOwner or coalition.side.NEUTRAL
|
self.ownerCurrent=CoalitionOwner or coalition.side.NEUTRAL
|
||||||
@ -165,9 +180,6 @@ function OPSZONE:New(Zone, CoalitionOwner)
|
|||||||
self.ownerPrevious=self.airbase:GetCoalition()
|
self.ownerPrevious=self.airbase:GetCoalition()
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Set time to capture.
|
|
||||||
self:SetTimeCapture()
|
|
||||||
|
|
||||||
-- Set object categories.
|
-- Set object categories.
|
||||||
self:SetObjectCategories()
|
self:SetObjectCategories()
|
||||||
self:SetUnitCategories()
|
self:SetUnitCategories()
|
||||||
@ -176,6 +188,11 @@ function OPSZONE:New(Zone, CoalitionOwner)
|
|||||||
self:SetDrawZone()
|
self:SetDrawZone()
|
||||||
self:SetMarkZone(true)
|
self:SetMarkZone(true)
|
||||||
|
|
||||||
|
-- Default capture parameters.
|
||||||
|
self:SetCaptureTime()
|
||||||
|
self:SetCaptureNunits()
|
||||||
|
self:SetCaptureThreatlevel()
|
||||||
|
|
||||||
-- Status timer.
|
-- Status timer.
|
||||||
self.timerStatus=TIMER:New(OPSZONE.Status, self)
|
self.timerStatus=TIMER:New(OPSZONE.Status, self)
|
||||||
|
|
||||||
@ -186,6 +203,8 @@ function OPSZONE:New(Zone, CoalitionOwner)
|
|||||||
-- From State --> Event --> To State
|
-- From State --> Event --> To State
|
||||||
self:AddTransition("Stopped", "Start", "Empty") -- Start FSM.
|
self:AddTransition("Stopped", "Start", "Empty") -- Start FSM.
|
||||||
self:AddTransition("*", "Stop", "Stopped") -- Stop FSM.
|
self:AddTransition("*", "Stop", "Stopped") -- Stop FSM.
|
||||||
|
|
||||||
|
self:AddTransition("*", "Evaluated", "*") -- Evaluation done.
|
||||||
|
|
||||||
self:AddTransition("*", "Captured", "Guarded") -- Zone was captured.
|
self:AddTransition("*", "Captured", "Guarded") -- Zone was captured.
|
||||||
|
|
||||||
@ -220,6 +239,23 @@ function OPSZONE:New(Zone, CoalitionOwner)
|
|||||||
-- @param #number delay Delay in seconds.
|
-- @param #number delay Delay in seconds.
|
||||||
|
|
||||||
|
|
||||||
|
--- Triggers the FSM event "Evaluated".
|
||||||
|
-- @function [parent=#OPSZONE] Evaluated
|
||||||
|
-- @param #OPSZONE self
|
||||||
|
|
||||||
|
--- Triggers the FSM event "Evaluated" after a delay.
|
||||||
|
-- @function [parent=#OPSZONE] __Evaluated
|
||||||
|
-- @param #OPSZONE self
|
||||||
|
-- @param #number delay Delay in seconds.
|
||||||
|
|
||||||
|
--- On after "Evaluated" event.
|
||||||
|
-- @function [parent=#OPSZONE] OnAfterEvaluated
|
||||||
|
-- @param #OPSZONE self
|
||||||
|
-- @param #string From From state.
|
||||||
|
-- @param #string Event Event.
|
||||||
|
-- @param #string To To state.
|
||||||
|
|
||||||
|
|
||||||
--- Triggers the FSM event "Captured".
|
--- Triggers the FSM event "Captured".
|
||||||
-- @function [parent=#OPSZONE] Captured
|
-- @function [parent=#OPSZONE] Captured
|
||||||
-- @param #OPSZONE self
|
-- @param #OPSZONE self
|
||||||
@ -369,27 +405,27 @@ function OPSZONE:SetUnitCategories(Categories)
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Set threat level threshold that the defending units must have to hold a zone.
|
--- Set threat level threshold that the offending units must have to capture a zone.
|
||||||
-- The reason why you might want to set this is that unarmed units (*e.g.* fuel trucks) should not be able to hold a zone as they do not pose a threat.
|
-- The reason why you might want to set this is that unarmed units (*e.g.* fuel trucks) should not be able to capture a zone as they do not pose a threat.
|
||||||
-- @param #OPSZONE self
|
-- @param #OPSZONE self
|
||||||
-- @param #number Threatlevel Threat level threshod. Default 0.
|
-- @param #number Threatlevel Threat level threshold. Default 0.
|
||||||
-- @return #OPSZONE self
|
-- @return #OPSZONE self
|
||||||
function OPSZONE:SetThreatlevelDefinding(Threatlevel)
|
function OPSZONE:SetCaptureThreatlevel(Threatlevel)
|
||||||
|
|
||||||
self.threatlevelDefending=Threatlevel or 0
|
self.threatlevelCapture=Threatlevel or 0
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Set how many units must be present in a zone to capture it. By default, one unit is enough.
|
||||||
--- Set threat level threshold that the offending units must have to capture a zone.
|
|
||||||
-- The reason why you might want to set this is that unarmed units (*e.g.* fuel trucks) should not be able to capture a zone as they do not pose a threat.
|
|
||||||
-- @param #OPSZONE self
|
-- @param #OPSZONE self
|
||||||
-- @param #number Threatlevel Threat level threshod. Default 0.
|
-- @param #number Nunits Number of units. Default 1.
|
||||||
-- @return #OPSZONE self
|
-- @return #OPSZONE self
|
||||||
function OPSZONE:SetThreatlevelOffending(Threatlevel)
|
function OPSZONE:SetCaptureNunits(Nunits)
|
||||||
|
|
||||||
self.threatlevelOffending=Threatlevel or 0
|
Nunits=Nunits or 1
|
||||||
|
|
||||||
|
self.nunitsCapture=Nunits
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
@ -399,7 +435,7 @@ end
|
|||||||
-- @param #OPSZONE self
|
-- @param #OPSZONE self
|
||||||
-- @param #number Tcapture Time in seconds. Default 0.
|
-- @param #number Tcapture Time in seconds. Default 0.
|
||||||
-- @return #OPSZONE self
|
-- @return #OPSZONE self
|
||||||
function OPSZONE:SetTimeCapture(Tcapture)
|
function OPSZONE:SetCaptureTime(Tcapture)
|
||||||
|
|
||||||
self.TminCaptured=Tcapture or 0
|
self.TminCaptured=Tcapture or 0
|
||||||
|
|
||||||
@ -479,6 +515,21 @@ function OPSZONE:GetCoordinate()
|
|||||||
return coordinate
|
return coordinate
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Get scanned units inside the zone.
|
||||||
|
-- @param #OPSZONE self
|
||||||
|
-- @return Core.Set#SET_UNIT Set of units inside the zone.
|
||||||
|
function OPSZONE:GetScannedUnitSet()
|
||||||
|
return self.ScanUnitSet
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Get scanned groups inside the zone.
|
||||||
|
-- @param #OPSZONE self
|
||||||
|
-- @return Core.Set#SET_GROUP Set of groups inside the zone.
|
||||||
|
function OPSZONE:GetScannedGroupSet()
|
||||||
|
return self.ScanGroupSet
|
||||||
|
end
|
||||||
|
|
||||||
--- Returns a random coordinate in the zone.
|
--- Returns a random coordinate in the zone.
|
||||||
-- @param #OPSZONE self
|
-- @param #OPSZONE self
|
||||||
-- @param #number inner (Optional) Minimal distance from the center of the zone in meters. Default is 0 m.
|
-- @param #number inner (Optional) Minimal distance from the center of the zone in meters. Default is 0 m.
|
||||||
@ -562,6 +613,22 @@ function OPSZONE:IsCoalition(Coalition)
|
|||||||
return is
|
return is
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Check if zone is started (not stopped).
|
||||||
|
-- @param #OPSZONE self
|
||||||
|
-- @return #boolean If `true`, zone is started.
|
||||||
|
function OPSZONE:IsStarted()
|
||||||
|
local is=not self:IsStopped()
|
||||||
|
return is
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Check if zone is stopped.
|
||||||
|
-- @param #OPSZONE self
|
||||||
|
-- @return #boolean If `true`, zone is stopped.
|
||||||
|
function OPSZONE:IsStopped()
|
||||||
|
local is=self:is("Stopped")
|
||||||
|
return is
|
||||||
|
end
|
||||||
|
|
||||||
--- Check if zone is guarded.
|
--- Check if zone is guarded.
|
||||||
-- @param #OPSZONE self
|
-- @param #OPSZONE self
|
||||||
-- @return #boolean If `true`, zone is guarded.
|
-- @return #boolean If `true`, zone is guarded.
|
||||||
@ -753,11 +820,7 @@ function OPSZONE:onafterEmpty(From, Event, To)
|
|||||||
-- Debug info.
|
-- Debug info.
|
||||||
self:T(self.lid..string.format("Zone is empty EVENT"))
|
self:T(self.lid..string.format("Zone is empty EVENT"))
|
||||||
|
|
||||||
-- Inform chief.
|
|
||||||
for _,_chief in pairs(self.chiefs) do
|
|
||||||
local chief=_chief --Ops.Chief#CHIEF
|
|
||||||
chief:ZoneEmpty(self)
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -771,17 +834,7 @@ function OPSZONE:onafterAttacked(From, Event, To, AttackerCoalition)
|
|||||||
|
|
||||||
-- Debug info.
|
-- Debug info.
|
||||||
self:T(self.lid..string.format("Zone is being attacked by coalition=%s!", tostring(AttackerCoalition)))
|
self:T(self.lid..string.format("Zone is being attacked by coalition=%s!", tostring(AttackerCoalition)))
|
||||||
|
|
||||||
-- Inform chief.
|
|
||||||
if AttackerCoalition then
|
|
||||||
for _,_chief in pairs(self.chiefs) do
|
|
||||||
local chief=_chief --Ops.Chief#CHIEF
|
|
||||||
if chief.coalition~=AttackerCoalition then
|
|
||||||
chief:ZoneAttacked(self)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--- On after "Defeated" event.
|
--- On after "Defeated" event.
|
||||||
@ -806,19 +859,24 @@ end
|
|||||||
-- @param #string Event Event.
|
-- @param #string Event Event.
|
||||||
-- @param #string To To state.
|
-- @param #string To To state.
|
||||||
function OPSZONE:onenterGuarded(From, Event, To)
|
function OPSZONE:onenterGuarded(From, Event, To)
|
||||||
|
|
||||||
|
if From~=To then
|
||||||
|
|
||||||
-- Debug info.
|
-- Debug info.
|
||||||
self:T(self.lid..string.format("Zone is guarded"))
|
self:T(self.lid..string.format("Zone is guarded"))
|
||||||
|
|
||||||
-- Not attacked any more.
|
-- Not attacked any more.
|
||||||
self.Tattacked=nil
|
self.Tattacked=nil
|
||||||
|
|
||||||
if self.drawZone then
|
if self.drawZone then
|
||||||
self.zone:UndrawZone()
|
|
||||||
|
|
||||||
local color=self:_GetZoneColor()
|
self.zone:UndrawZone()
|
||||||
|
|
||||||
|
local color=self:_GetZoneColor()
|
||||||
|
|
||||||
|
self.zone:DrawZone(nil, color, 1.0, color, 0.5)
|
||||||
|
end
|
||||||
|
|
||||||
self.zone:DrawZone(nil, color, 1.0, color, 0.5)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
@ -828,26 +886,43 @@ end
|
|||||||
-- @param #string From From state.
|
-- @param #string From From state.
|
||||||
-- @param #string Event Event.
|
-- @param #string Event Event.
|
||||||
-- @param #string To To state.
|
-- @param #string To To state.
|
||||||
function OPSZONE:onenterAttacked(From, Event, To)
|
-- @param #number AttackerCoalition Coalition of the attacking ground troops.
|
||||||
|
function OPSZONE:onenterAttacked(From, Event, To, AttackerCoalition)
|
||||||
-- Debug info.
|
|
||||||
self:T(self.lid..string.format("Zone is Attacked"))
|
|
||||||
|
|
||||||
-- Time stamp when the attack started.
|
-- Time stamp when the attack started.
|
||||||
self.Tattacked=timer.getAbsTime()
|
if From~="Attacked" then
|
||||||
|
|
||||||
-- Draw zone?
|
-- Debug info.
|
||||||
if self.drawZone then
|
self:T(self.lid..string.format("Zone is Attacked"))
|
||||||
self.zone:UndrawZone()
|
|
||||||
|
|
||||||
-- Color.
|
|
||||||
local color={1, 204/255, 204/255}
|
|
||||||
|
|
||||||
-- Draw zone.
|
|
||||||
self.zone:DrawZone(nil, color, 1.0, color, 0.5)
|
|
||||||
end
|
|
||||||
|
|
||||||
self:_CleanMissionTable()
|
-- Set time stamp.
|
||||||
|
self.Tattacked=timer.getAbsTime()
|
||||||
|
|
||||||
|
-- Inform chief.
|
||||||
|
if AttackerCoalition then
|
||||||
|
for _,_chief in pairs(self.chiefs) do
|
||||||
|
local chief=_chief --Ops.Chief#CHIEF
|
||||||
|
if chief.coalition~=AttackerCoalition then
|
||||||
|
chief:ZoneAttacked(self)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Draw zone?
|
||||||
|
if self.drawZone then
|
||||||
|
self.zone:UndrawZone()
|
||||||
|
|
||||||
|
-- Color.
|
||||||
|
local color={1, 204/255, 204/255}
|
||||||
|
|
||||||
|
-- Draw zone.
|
||||||
|
self.zone:DrawZone(nil, color, 1.0, color, 0.5)
|
||||||
|
end
|
||||||
|
|
||||||
|
self:_CleanMissionTable()
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--- On enter "Empty" event.
|
--- On enter "Empty" event.
|
||||||
@ -857,17 +932,27 @@ end
|
|||||||
-- @param #string To To state.
|
-- @param #string To To state.
|
||||||
function OPSZONE:onenterEmpty(From, Event, To)
|
function OPSZONE:onenterEmpty(From, Event, To)
|
||||||
|
|
||||||
-- Debug info.
|
if From~=To then
|
||||||
self:T(self.lid..string.format("Zone is empty now"))
|
|
||||||
|
|
||||||
if self.drawZone then
|
-- Debug info.
|
||||||
self.zone:UndrawZone()
|
self:T(self.lid..string.format("Zone is empty now"))
|
||||||
|
|
||||||
local color=self:_GetZoneColor()
|
|
||||||
|
|
||||||
self.zone:DrawZone(nil, color, 1.0, color, 0.2)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
-- Inform chief.
|
||||||
|
for _,_chief in pairs(self.chiefs) do
|
||||||
|
local chief=_chief --Ops.Chief#CHIEF
|
||||||
|
chief:ZoneEmpty(self)
|
||||||
|
end
|
||||||
|
|
||||||
|
if self.drawZone then
|
||||||
|
self.zone:UndrawZone()
|
||||||
|
|
||||||
|
local color=self:_GetZoneColor()
|
||||||
|
|
||||||
|
self.zone:DrawZone(nil, color, 1.0, color, 0.2)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
@ -892,6 +977,13 @@ function OPSZONE:Scan()
|
|||||||
local Nred=0
|
local Nred=0
|
||||||
local Nblu=0
|
local Nblu=0
|
||||||
local Nnut=0
|
local Nnut=0
|
||||||
|
|
||||||
|
local Tred=0
|
||||||
|
local Tblu=0
|
||||||
|
local Tnut=0
|
||||||
|
|
||||||
|
self.ScanGroupSet:Clear(false)
|
||||||
|
self.ScanUnitSet:Clear(false)
|
||||||
|
|
||||||
--- Function to evaluate the world search
|
--- Function to evaluate the world search
|
||||||
local function EvaluateZone(_ZoneObject)
|
local function EvaluateZone(_ZoneObject)
|
||||||
@ -939,13 +1031,35 @@ function OPSZONE:Scan()
|
|||||||
-- Get Coalition.
|
-- Get Coalition.
|
||||||
local Coalition=DCSUnit:getCoalition()
|
local Coalition=DCSUnit:getCoalition()
|
||||||
|
|
||||||
|
local tl=0
|
||||||
|
local unit=UNIT:Find(DCSUnit)
|
||||||
|
if unit then
|
||||||
|
|
||||||
|
-- Threat level of unit.
|
||||||
|
tl=unit:GetThreatLevel()
|
||||||
|
|
||||||
|
-- Add unit to set.
|
||||||
|
self.ScanUnitSet:AddUnit(unit)
|
||||||
|
|
||||||
|
-- Get group of unit.
|
||||||
|
local group=unit:GetGroup()
|
||||||
|
|
||||||
|
if group then
|
||||||
|
self.ScanGroupSet:AddGroup(group, true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
-- Increase counter.
|
-- Increase counter.
|
||||||
if Coalition==coalition.side.RED then
|
if Coalition==coalition.side.RED then
|
||||||
Nred=Nred+1
|
Nred=Nred+1
|
||||||
|
Tred=Tred+tl
|
||||||
elseif Coalition==coalition.side.BLUE then
|
elseif Coalition==coalition.side.BLUE then
|
||||||
Nblu=Nblu+1
|
Nblu=Nblu+1
|
||||||
|
Tblu=Tblu+tl
|
||||||
elseif Coalition==coalition.side.NEUTRAL then
|
elseif Coalition==coalition.side.NEUTRAL then
|
||||||
Nnut=Nnut+1
|
Nnut=Nnut+1
|
||||||
|
Tnut=Tnut+tl
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Debug info.
|
-- Debug info.
|
||||||
@ -1014,6 +1128,10 @@ function OPSZONE:Scan()
|
|||||||
self.Nred=Nred
|
self.Nred=Nred
|
||||||
self.Nblu=Nblu
|
self.Nblu=Nblu
|
||||||
self.Nnut=Nnut
|
self.Nnut=Nnut
|
||||||
|
|
||||||
|
self.Tblu=Tblu
|
||||||
|
self.Tred=Tred
|
||||||
|
self.Tnut=Tnut
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
@ -1028,6 +1146,30 @@ function OPSZONE:EvaluateZone()
|
|||||||
local Nblu=self.Nblu
|
local Nblu=self.Nblu
|
||||||
local Nnut=self.Nnut
|
local Nnut=self.Nnut
|
||||||
|
|
||||||
|
local Tnow=timer.getAbsTime()
|
||||||
|
|
||||||
|
--- Capture
|
||||||
|
-- @param #number coal Coaltion capturing.
|
||||||
|
local function captured(coal)
|
||||||
|
|
||||||
|
-- Blue captured red zone.
|
||||||
|
if not self.airbase then
|
||||||
|
|
||||||
|
-- Set time stamp if it does not exist.
|
||||||
|
if not self.Tcaptured then
|
||||||
|
self.Tcaptured=Tnow
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Check if enough time elapsed.
|
||||||
|
if Tnow-self.Tcaptured>=self.TminCaptured then
|
||||||
|
self:Captured(coal)
|
||||||
|
self.Tcaptured=nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
if self:IsRed() then
|
if self:IsRed() then
|
||||||
|
|
||||||
---
|
---
|
||||||
@ -1038,43 +1180,16 @@ function OPSZONE:EvaluateZone()
|
|||||||
|
|
||||||
-- No red units in red zone any more.
|
-- No red units in red zone any more.
|
||||||
|
|
||||||
if Nblu>0 then
|
if Nblu>=self.nunitsCapture and self.Tblu>=self.threatlevelCapture then
|
||||||
-- Blue captured red zone.
|
|
||||||
if not self.airbase then
|
-- Blue captued red zone.
|
||||||
local Tnow=timer.getAbsTime()
|
captured(coalition.side.BLUE)
|
||||||
|
|
||||||
-- Set time stamp if it does not exist.
|
elseif Nnut>=self.nunitsCapture and self.Tnut>=self.threatlevelCapture and self.neutralCanCapture then
|
||||||
if not self.Tcaptured then
|
|
||||||
self.Tcaptured=Tnow
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Check if enough time elapsed.
|
|
||||||
if Tnow-self.Tcaptured>=self.TminCaptured then
|
|
||||||
self:Captured(coalition.side.BLUE)
|
|
||||||
self.Tcaptured=nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
elseif Nnut>0 and self.neutralCanCapture then
|
|
||||||
-- Neutral captured red zone.
|
-- Neutral captured red zone.
|
||||||
if not self.airbase then
|
captured(coalition.side.NEUTRAL)
|
||||||
local Tnow=timer.getAbsTime()
|
|
||||||
|
|
||||||
-- Set time stamp if it does not exist.
|
|
||||||
if not self.Tcaptured then
|
|
||||||
self.Tcaptured=Tnow
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Check if enough time elapsed.
|
|
||||||
if Tnow-self.Tcaptured>=self.TminCaptured then
|
|
||||||
self:Captured(coalition.side.NEUTRAL)
|
|
||||||
self.Tcaptured=nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
else
|
|
||||||
-- Red zone is now empty (but will remain red).
|
|
||||||
if not self:IsEmpty() then
|
|
||||||
self:Empty()
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
else
|
else
|
||||||
@ -1117,21 +1232,16 @@ function OPSZONE:EvaluateZone()
|
|||||||
|
|
||||||
-- No blue units in blue zone any more.
|
-- No blue units in blue zone any more.
|
||||||
|
|
||||||
if Nred>0 then
|
if Nred>=self.nunitsCapture and self.Tred>=self.threatlevelCapture then
|
||||||
|
|
||||||
-- Red captured blue zone.
|
-- Red captured blue zone.
|
||||||
if not self.airbase then
|
captured(coalition.side.RED)
|
||||||
self:Captured(coalition.side.RED)
|
|
||||||
end
|
elseif Nnut>=self.nunitsCapture and self.Tnut>=self.threatlevelCapture and self.neutralCanCapture then
|
||||||
elseif Nnut>0 and self.neutralCanCapture then
|
|
||||||
-- Neutral captured blue zone.
|
-- Neutral captured blue zone.
|
||||||
if not self.airbase then
|
captured(coalition.side.NEUTRAL)
|
||||||
self:Captured(coalition.side.NEUTRAL)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
-- Blue zone is empty now.
|
|
||||||
if not self:IsEmpty() then
|
|
||||||
self:Empty()
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
else
|
else
|
||||||
@ -1152,7 +1262,7 @@ function OPSZONE:EvaluateZone()
|
|||||||
self:Defeated(coalition.side.RED)
|
self:Defeated(coalition.side.RED)
|
||||||
elseif self:IsEmpty() then
|
elseif self:IsEmpty() then
|
||||||
-- Blue units left zone and returned (or from initial Empty state).
|
-- Blue units left zone and returned (or from initial Empty state).
|
||||||
self:Guarded()
|
self:Guarded()
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
@ -1183,21 +1293,12 @@ function OPSZONE:EvaluateZone()
|
|||||||
self:Attacked()
|
self:Attacked()
|
||||||
end
|
end
|
||||||
self.isContested=true
|
self.isContested=true
|
||||||
elseif Nred>0 then
|
elseif Nred>=self.nunitsCapture and self.Tred>=self.threatlevelCapture then
|
||||||
-- Red captured neutral zone.
|
-- Red captured neutral zone.
|
||||||
if not self.airbase then
|
captured(coalition.side.RED)
|
||||||
self:Captured(coalition.side.RED)
|
elseif Nblu>=self.nunitsCapture and self.Tblu>=self.threatlevelCapture then
|
||||||
end
|
|
||||||
elseif Nblu>0 then
|
|
||||||
-- Blue captured neutral zone.
|
-- Blue captured neutral zone.
|
||||||
if not self.airbase then
|
captured(coalition.side.BLUE)
|
||||||
self:Captured(coalition.side.BLUE)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
-- Neutral zone is empty now.
|
|
||||||
if not self:IsEmpty() then
|
|
||||||
self:Empty()
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--end
|
--end
|
||||||
@ -1206,6 +1307,11 @@ function OPSZONE:EvaluateZone()
|
|||||||
self:E(self.lid.."ERROR: Unknown coaliton!")
|
self:E(self.lid.."ERROR: Unknown coaliton!")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-- No units of any coalition in zone any more ==> Empty!
|
||||||
|
if Nblu==0 and Nred==0 and Nnut==0 and (not self:IsEmpty()) then
|
||||||
|
self:Empty()
|
||||||
|
end
|
||||||
|
|
||||||
-- Finally, check airbase coalition
|
-- Finally, check airbase coalition
|
||||||
if self.airbase then
|
if self.airbase then
|
||||||
@ -1219,6 +1325,9 @@ function OPSZONE:EvaluateZone()
|
|||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Trigger event.
|
||||||
|
self:Evaluated()
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -1328,7 +1437,7 @@ function OPSZONE:_UpdateMarker()
|
|||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Get marker text
|
--- Get marker text.
|
||||||
-- @param #OPSZONE self
|
-- @param #OPSZONE self
|
||||||
-- @return #string Marker text.
|
-- @return #string Marker text.
|
||||||
function OPSZONE:_GetMarkerText()
|
function OPSZONE:_GetMarkerText()
|
||||||
@ -1337,8 +1446,10 @@ function OPSZONE:_GetMarkerText()
|
|||||||
local prevowner=UTILS.GetCoalitionName(self.ownerPrevious)
|
local prevowner=UTILS.GetCoalitionName(self.ownerPrevious)
|
||||||
|
|
||||||
-- Get marker text.
|
-- Get marker text.
|
||||||
local text=string.format("%s: Owner=%s [%s]\nState=%s [Contested=%s]\nBlue=%d, Red=%d, Neutral=%d",
|
local text=string.format("%s [N=%d, TL=%d T=%d]:\nOwner=%s [%s]\nState=%s [Contested=%s]\nBlue=%d [TL=%d]\nRed=%d [TL=%d]\nNeutral=%d [TL=%d]",
|
||||||
self.zoneName, owner, prevowner, self:GetState(), tostring(self:IsContested()), self.Nblu, self.Nred, self.Nnut)
|
self.zoneName, self.nunitsCapture or 0, self.threatlevelCapture or 0, self.TminCaptured or 0,
|
||||||
|
owner, prevowner, self:GetState(), tostring(self:IsContested()),
|
||||||
|
self.Nblu, self.Tblu, self.Nred, self.Tred, self.Nnut, self.Tnut)
|
||||||
|
|
||||||
return text
|
return text
|
||||||
end
|
end
|
||||||
|
|||||||
@ -84,6 +84,7 @@ TARGET = {
|
|||||||
-- @field #string COORDINATE Target is a COORDINATE.
|
-- @field #string COORDINATE Target is a COORDINATE.
|
||||||
-- @field #string AIRBASE Target is an AIRBASE.
|
-- @field #string AIRBASE Target is an AIRBASE.
|
||||||
-- @field #string ZONE Target is a ZONE object.
|
-- @field #string ZONE Target is a ZONE object.
|
||||||
|
-- @field #string OPSZONE Target is an OPSZONE object.
|
||||||
TARGET.ObjectType={
|
TARGET.ObjectType={
|
||||||
GROUP="Group",
|
GROUP="Group",
|
||||||
UNIT="Unit",
|
UNIT="Unit",
|
||||||
@ -92,6 +93,7 @@ TARGET.ObjectType={
|
|||||||
COORDINATE="Coordinate",
|
COORDINATE="Coordinate",
|
||||||
AIRBASE="Airbase",
|
AIRBASE="Airbase",
|
||||||
ZONE="Zone",
|
ZONE="Zone",
|
||||||
|
OPSZONE="OpsZone"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -151,7 +153,7 @@ _TARGETID=0
|
|||||||
|
|
||||||
--- TARGET class version.
|
--- TARGET class version.
|
||||||
-- @field #string version
|
-- @field #string version
|
||||||
TARGET.version="0.5.6"
|
TARGET.version="0.6.0"
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
-- TODO list
|
-- TODO list
|
||||||
@ -312,12 +314,18 @@ end
|
|||||||
-- * SET_SCENERY
|
-- * SET_SCENERY
|
||||||
-- * SET_OPSGROUP
|
-- * SET_OPSGROUP
|
||||||
-- * SET_ZONE
|
-- * SET_ZONE
|
||||||
|
-- * SET_OPSZONE
|
||||||
--
|
--
|
||||||
-- @param #TARGET self
|
-- @param #TARGET self
|
||||||
-- @param Wrapper.Positionable#POSITIONABLE Object The target UNIT, GROUP, STATIC, SCENERY, AIRBASE, COORDINATE, ZONE, SET_GROUP, SET_UNIT, SET_STATIC, SET_SCENERY, SET_ZONE
|
-- @param Wrapper.Positionable#POSITIONABLE Object The target UNIT, GROUP, STATIC, SCENERY, AIRBASE, COORDINATE, ZONE, SET_GROUP, SET_UNIT, SET_STATIC, SET_SCENERY, SET_ZONE
|
||||||
function TARGET:AddObject(Object)
|
function TARGET:AddObject(Object)
|
||||||
|
|
||||||
if Object:IsInstanceOf("SET_GROUP") or Object:IsInstanceOf("SET_UNIT") or Object:IsInstanceOf("SET_STATIC") or Object:IsInstanceOf("SET_SCENERY") or Object:IsInstanceOf("SET_OPSGROUP") then
|
if Object:IsInstanceOf("SET_GROUP") or
|
||||||
|
Object:IsInstanceOf("SET_UNIT") or
|
||||||
|
Object:IsInstanceOf("SET_STATIC") or
|
||||||
|
Object:IsInstanceOf("SET_SCENERY") or
|
||||||
|
Object:IsInstanceOf("SET_OPSGROUP") or
|
||||||
|
Object:IsInstanceOf("SET_OPSZONE") then
|
||||||
|
|
||||||
---
|
---
|
||||||
-- Sets
|
-- Sets
|
||||||
@ -984,6 +992,22 @@ function TARGET:_AddObject(Object)
|
|||||||
|
|
||||||
target.Life0=1
|
target.Life0=1
|
||||||
target.Life=1
|
target.Life=1
|
||||||
|
|
||||||
|
elseif Object:IsInstanceOf("OPSZONE") then
|
||||||
|
|
||||||
|
|
||||||
|
local zone=Object --Ops.OpsZone#OPSZONE
|
||||||
|
Object=zone
|
||||||
|
|
||||||
|
target.Type=TARGET.ObjectType.OPSZONE
|
||||||
|
target.Name=zone:GetName()
|
||||||
|
|
||||||
|
target.Coordinate=zone:GetCoordinate()
|
||||||
|
|
||||||
|
target.N0=target.N0+1
|
||||||
|
|
||||||
|
target.Life0=1
|
||||||
|
target.Life=1
|
||||||
|
|
||||||
else
|
else
|
||||||
self:E(self.lid.."ERROR: Unknown object type!")
|
self:E(self.lid.."ERROR: Unknown object type!")
|
||||||
@ -1102,7 +1126,7 @@ function TARGET:GetTargetLife(Target)
|
|||||||
|
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
elseif Target.Type==TARGET.ObjectType.ZONE then
|
elseif Target.Type==TARGET.ObjectType.ZONE or Target.Type==TARGET.ObjectType.OPSZONE then
|
||||||
|
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
@ -1310,6 +1334,13 @@ function TARGET:GetTargetVec3(Target, Average)
|
|||||||
|
|
||||||
local vec3=object:GetVec3()
|
local vec3=object:GetVec3()
|
||||||
return vec3
|
return vec3
|
||||||
|
|
||||||
|
elseif Target.Type==TARGET.ObjectType.OPSZONE then
|
||||||
|
|
||||||
|
local object=Target.Object --Ops.OpsZone#OPSZONE
|
||||||
|
|
||||||
|
local vec3=object:GetZone():GetVec3()
|
||||||
|
return vec3
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -1387,7 +1418,7 @@ function TARGET:GetTargetHeading(Target)
|
|||||||
-- A coordinate has no heading. Return 0.
|
-- A coordinate has no heading. Return 0.
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
elseif Target.Type==TARGET.ObjectType.ZONE then
|
elseif Target.Type==TARGET.ObjectType.ZONE or Target.Type==TARGET.ObjectType.OPSZONE then
|
||||||
|
|
||||||
local object=Target.Object --Core.Zone#ZONE
|
local object=Target.Object --Core.Zone#ZONE
|
||||||
|
|
||||||
@ -1665,6 +1696,10 @@ function TARGET:GetTargetCategory(Target)
|
|||||||
elseif Target.Type==TARGET.ObjectType.ZONE then
|
elseif Target.Type==TARGET.ObjectType.ZONE then
|
||||||
|
|
||||||
return TARGET.Category.ZONE
|
return TARGET.Category.ZONE
|
||||||
|
|
||||||
|
elseif Target.Type==TARGET.ObjectType.OPSZONE then
|
||||||
|
|
||||||
|
return TARGET.Category.OPSZONE
|
||||||
|
|
||||||
else
|
else
|
||||||
self:E("ERROR: unknown target category!")
|
self:E("ERROR: unknown target category!")
|
||||||
@ -1674,6 +1709,71 @@ function TARGET:GetTargetCategory(Target)
|
|||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Get coalition of target object. If an object has no coalition (*e.g.* a coordinate) it is returned as neutral.
|
||||||
|
-- @param #TARGET self
|
||||||
|
-- @param #TARGET.Object Target Target object.
|
||||||
|
-- @return #number Coalition number.
|
||||||
|
function TARGET:GetTargetCoalition(Target)
|
||||||
|
|
||||||
|
|
||||||
|
-- We take neutral for objects that do not have a coalition.
|
||||||
|
local coal=coalition.side.NEUTRAL
|
||||||
|
|
||||||
|
|
||||||
|
if Target.Type==TARGET.ObjectType.GROUP then
|
||||||
|
|
||||||
|
if Target.Object and Target.Object:IsAlive()~=nil then
|
||||||
|
local object=Target.Object --Wrapper.Group#GROUP
|
||||||
|
|
||||||
|
coal=object:GetCoalition()
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
elseif Target.Type==TARGET.ObjectType.UNIT then
|
||||||
|
|
||||||
|
if Target.Object and Target.Object:IsAlive()~=nil then
|
||||||
|
local object=Target.Object --Wrapper.Unit#UNIT
|
||||||
|
|
||||||
|
coal=object:GetCoalition()
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
elseif Target.Type==TARGET.ObjectType.STATIC then
|
||||||
|
local object=Target.Object --Wrapper.Static#STATIC
|
||||||
|
|
||||||
|
coal=object:GetCoalition()
|
||||||
|
|
||||||
|
elseif Target.Type==TARGET.ObjectType.SCENERY then
|
||||||
|
|
||||||
|
-- Scenery has no coalition.
|
||||||
|
|
||||||
|
elseif Target.Type==TARGET.ObjectType.AIRBASE then
|
||||||
|
local object=Target.Object --Wrapper.Airbase#AIRBASE
|
||||||
|
|
||||||
|
coal=object:GetCoalition()
|
||||||
|
|
||||||
|
elseif Target.Type==TARGET.ObjectType.COORDINATE then
|
||||||
|
|
||||||
|
-- Coordinate has no coalition.
|
||||||
|
|
||||||
|
elseif Target.Type==TARGET.ObjectType.ZONE then
|
||||||
|
|
||||||
|
-- Zone has no coalition.
|
||||||
|
|
||||||
|
elseif Target.Type==TARGET.ObjectType.OPSZONE then
|
||||||
|
local object=Target.Object --Ops.OpsZone#OPSZONE
|
||||||
|
|
||||||
|
coal=object:GetOwner()
|
||||||
|
|
||||||
|
else
|
||||||
|
self:E("ERROR: unknown target category!")
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
return coal
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
-- Misc Functions
|
-- Misc Functions
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
@ -1697,14 +1797,45 @@ end
|
|||||||
|
|
||||||
--- Get the first target objective alive.
|
--- Get the first target objective alive.
|
||||||
-- @param #TARGET self
|
-- @param #TARGET self
|
||||||
|
-- @param Core.Point#COORDINATE RefCoordinate (Optional) Reference coordinate to determine the closest target objective.
|
||||||
|
-- @param #table Coalitions (Optional) Only consider targets of the given coalition(s).
|
||||||
-- @return #TARGET.Object The target objective.
|
-- @return #TARGET.Object The target objective.
|
||||||
function TARGET:GetObjective()
|
function TARGET:GetObjective(RefCoordinate, Coalitions)
|
||||||
|
|
||||||
for _,_target in pairs(self.targets) do
|
if RefCoordinate then
|
||||||
local target=_target --#TARGET.Object
|
|
||||||
if target.Status~=TARGET.ObjectStatus.DEAD then
|
local dmin=math.huge
|
||||||
return target
|
local tmin=nil --#TARGET.Object
|
||||||
|
|
||||||
|
for _,_target in pairs(self.targets) do
|
||||||
|
local target=_target --#TARGET.Object
|
||||||
|
|
||||||
|
if target.Status~=TARGET.ObjectStatus.DEAD and (Coalitions==nil or UTILS.IsInTable(UTILS.EnsureTable(Coalitions), self:GetTargetCoalition(target))) then
|
||||||
|
|
||||||
|
local vec3=self:GetTargetVec3(target)
|
||||||
|
|
||||||
|
local d=UTILS.VecDist3D(vec3, RefCoordinate)
|
||||||
|
|
||||||
|
if d<dmin then
|
||||||
|
dmin=d
|
||||||
|
tmin=target
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
return tmin
|
||||||
|
else
|
||||||
|
|
||||||
|
for _,_target in pairs(self.targets) do
|
||||||
|
local target=_target --#TARGET.Object
|
||||||
|
if target.Status~=TARGET.ObjectStatus.DEAD and (Coalitions==nil or UTILS.IsInTable(UTILS.EnsureTable(Coalitions), self:GetTargetCoalition(target))) then
|
||||||
|
return target
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@ -1712,10 +1843,13 @@ end
|
|||||||
|
|
||||||
--- Get the first target object alive.
|
--- Get the first target object alive.
|
||||||
-- @param #TARGET self
|
-- @param #TARGET self
|
||||||
|
-- @param Core.Point#COORDINATE RefCoordinate Reference coordinate to determine the closest target objective.
|
||||||
|
-- @param #table Coalitions (Optional) Only consider targets of the given coalition(s).
|
||||||
-- @return Wrapper.Positionable#POSITIONABLE The target object or nil.
|
-- @return Wrapper.Positionable#POSITIONABLE The target object or nil.
|
||||||
function TARGET:GetObject()
|
function TARGET:GetObject(RefCoordinate, Coalitions)
|
||||||
|
|
||||||
local target=self:GetObjective()
|
local target=self:GetObjective(RefCoordinate, Coalitions)
|
||||||
|
|
||||||
if target then
|
if target then
|
||||||
return target.Object
|
return target.Object
|
||||||
end
|
end
|
||||||
@ -1726,8 +1860,9 @@ end
|
|||||||
--- Count alive objects.
|
--- Count alive objects.
|
||||||
-- @param #TARGET self
|
-- @param #TARGET self
|
||||||
-- @param #TARGET.Object Target Target objective.
|
-- @param #TARGET.Object Target Target objective.
|
||||||
|
-- @param #table Coalitions (Optional) Only count targets of the given coalition(s).
|
||||||
-- @return #number Number of alive target objects.
|
-- @return #number Number of alive target objects.
|
||||||
function TARGET:CountObjectives(Target)
|
function TARGET:CountObjectives(Target, Coalitions)
|
||||||
|
|
||||||
local N=0
|
local N=0
|
||||||
|
|
||||||
@ -1740,7 +1875,9 @@ function TARGET:CountObjectives(Target)
|
|||||||
for _,_unit in pairs(units or {}) do
|
for _,_unit in pairs(units or {}) do
|
||||||
local unit=_unit --Wrapper.Unit#UNIT
|
local unit=_unit --Wrapper.Unit#UNIT
|
||||||
if unit and unit:IsAlive()~=nil and unit:GetLife()>1 then
|
if unit and unit:IsAlive()~=nil and unit:GetLife()>1 then
|
||||||
N=N+1
|
if Coalitions==nil or UTILS.IsInTable(UTILS.EnsureTable(Coalitions), unit:GetCoalition()) then
|
||||||
|
N=N+1
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -1749,7 +1886,9 @@ function TARGET:CountObjectives(Target)
|
|||||||
local target=Target.Object --Wrapper.Unit#UNIT
|
local target=Target.Object --Wrapper.Unit#UNIT
|
||||||
|
|
||||||
if target and target:IsAlive()~=nil and target:GetLife()>1 then
|
if target and target:IsAlive()~=nil and target:GetLife()>1 then
|
||||||
N=N+1
|
if Coalitions==nil or UTILS.IsInTable(Coalitions, target:GetCoalition()) then
|
||||||
|
N=N+1
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
elseif Target.Type==TARGET.ObjectType.STATIC then
|
elseif Target.Type==TARGET.ObjectType.STATIC then
|
||||||
@ -1757,7 +1896,9 @@ function TARGET:CountObjectives(Target)
|
|||||||
local target=Target.Object --Wrapper.Static#STATIC
|
local target=Target.Object --Wrapper.Static#STATIC
|
||||||
|
|
||||||
if target and target:IsAlive() then
|
if target and target:IsAlive() then
|
||||||
N=N+1
|
if Coalitions==nil or UTILS.IsInTable(Coalitions, target:GetCoalition()) then
|
||||||
|
N=N+1
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
elseif Target.Type==TARGET.ObjectType.SCENERY then
|
elseif Target.Type==TARGET.ObjectType.SCENERY then
|
||||||
@ -1768,8 +1909,12 @@ function TARGET:CountObjectives(Target)
|
|||||||
|
|
||||||
elseif Target.Type==TARGET.ObjectType.AIRBASE then
|
elseif Target.Type==TARGET.ObjectType.AIRBASE then
|
||||||
|
|
||||||
|
local target=Target.Object --Wrapper.Airbase#AIRBASE
|
||||||
|
|
||||||
if Target.Status==TARGET.ObjectStatus.ALIVE then
|
if Target.Status==TARGET.ObjectStatus.ALIVE then
|
||||||
N=N+1
|
if Coalitions==nil or UTILS.IsInTable(Coalitions, target:GetCoalition()) then
|
||||||
|
N=N+1
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
elseif Target.Type==TARGET.ObjectType.COORDINATE then
|
elseif Target.Type==TARGET.ObjectType.COORDINATE then
|
||||||
@ -1779,7 +1924,15 @@ function TARGET:CountObjectives(Target)
|
|||||||
elseif Target.Type==TARGET.ObjectType.ZONE then
|
elseif Target.Type==TARGET.ObjectType.ZONE then
|
||||||
|
|
||||||
-- No target we can check!
|
-- No target we can check!
|
||||||
|
|
||||||
|
elseif Target.Type==TARGET.ObjectType.OPSZONE then
|
||||||
|
|
||||||
|
local target=Target.Object --Ops.OpsZone#OPSZONE
|
||||||
|
|
||||||
|
if Coalitions==nil or UTILS.IsInTable(Coalitions, target:GetOwner()) then
|
||||||
|
N=N+1
|
||||||
|
end
|
||||||
|
|
||||||
else
|
else
|
||||||
self:E(self.lid.."ERROR: Unknown target type! Cannot count targets")
|
self:E(self.lid.."ERROR: Unknown target type! Cannot count targets")
|
||||||
end
|
end
|
||||||
@ -1789,15 +1942,16 @@ end
|
|||||||
|
|
||||||
--- Count alive targets.
|
--- Count alive targets.
|
||||||
-- @param #TARGET self
|
-- @param #TARGET self
|
||||||
|
-- @param #table Coalitions (Optional) Only count targets of the given coalition(s).
|
||||||
-- @return #number Number of alive target objects.
|
-- @return #number Number of alive target objects.
|
||||||
function TARGET:CountTargets()
|
function TARGET:CountTargets(Coalitions)
|
||||||
|
|
||||||
local N=0
|
local N=0
|
||||||
|
|
||||||
for _,_target in pairs(self.targets) do
|
for _,_target in pairs(self.targets) do
|
||||||
local Target=_target --#TARGET.Object
|
local Target=_target --#TARGET.Object
|
||||||
|
|
||||||
N=N+self:CountObjectives(Target)
|
N=N+self:CountObjectives(Target, Coalitions)
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@ -1061,8 +1061,8 @@ function UTILS.Vec2Norm(a)
|
|||||||
end
|
end
|
||||||
|
|
||||||
--- Calculate the distance between two 2D vectors.
|
--- Calculate the distance between two 2D vectors.
|
||||||
-- @param DCS#Vec2 a Vector in 3D with x, y components.
|
-- @param DCS#Vec2 a Vector in 2D with x, y components.
|
||||||
-- @param DCS#Vec2 b Vector in 3D with x, y components.
|
-- @param DCS#Vec2 b Vector in 2D with x, y components.
|
||||||
-- @return #number Distance between the vectors.
|
-- @return #number Distance between the vectors.
|
||||||
function UTILS.VecDist2D(a, b)
|
function UTILS.VecDist2D(a, b)
|
||||||
|
|
||||||
@ -1446,6 +1446,30 @@ function UTILS.GetCoalitionName(Coalition)
|
|||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Get the enemy coalition for a given coalition.
|
||||||
|
-- @param #number Coalition The coalition ID.
|
||||||
|
-- @param #boolean Neutral Include neutral as enemy.
|
||||||
|
-- @return #table Enemy coalition table.
|
||||||
|
function UTILS.GetCoalitionEnemy(Coalition, Neutral)
|
||||||
|
|
||||||
|
local Coalitions={}
|
||||||
|
if Coalition then
|
||||||
|
if Coalition==coalition.side.RED then
|
||||||
|
Coalitions={coalition.side.BLUE}
|
||||||
|
elseif Coalition==coalition.side.BLUE then
|
||||||
|
Coalitions={coalition.side.RED}
|
||||||
|
elseif Coalition==coalition.side.NEUTRAL then
|
||||||
|
Coalitions={coalition.side.RED, coalition.side.BLUE}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if Neutral then
|
||||||
|
table.insert(Coalitions, coalition.side.NEUTRAL)
|
||||||
|
end
|
||||||
|
|
||||||
|
return Coalitions
|
||||||
|
end
|
||||||
|
|
||||||
--- Get the modulation name from its numerical value.
|
--- Get the modulation name from its numerical value.
|
||||||
-- @param #number Modulation The modulation enumerator number. Can be either 0 or 1.
|
-- @param #number Modulation The modulation enumerator number. Can be either 0 or 1.
|
||||||
-- @return #string The modulation name, i.e. "AM"=0 or "FM"=1. Anything else will return "Unknown".
|
-- @return #string The modulation name, i.e. "AM"=0 or "FM"=1. Anything else will return "Unknown".
|
||||||
@ -2695,3 +2719,51 @@ function UTILS.ToStringBRAANATO(FromGrp,ToGrp)
|
|||||||
end
|
end
|
||||||
return BRAANATO
|
return BRAANATO
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Check if an object is contained in a table.
|
||||||
|
-- @param #table Table The table.
|
||||||
|
-- @param #table Object The object to check.
|
||||||
|
-- @param #string Key (Optional) Key to check. By default, the object itself is checked.
|
||||||
|
-- @return #booolen Returns `true` if object is in table.
|
||||||
|
function UTILS.IsInTable(Table, Object, Key)
|
||||||
|
|
||||||
|
for key, object in pairs(Table) do
|
||||||
|
if Key then
|
||||||
|
if Object[Key]==object[Key] then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if object==Object then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Check if any object of multiple given objects is contained in a table.
|
||||||
|
-- @param #table Table The table.
|
||||||
|
-- @param #table Objects The objects to check.
|
||||||
|
-- @param #string Key (Optional) Key to check.
|
||||||
|
-- @return #booolen Returns `true` if object is in table.
|
||||||
|
function UTILS.IsAnyInTable(Table, Objects, Key)
|
||||||
|
|
||||||
|
for _,Object in pairs(UTILS.EnsureTable(Objects)) do
|
||||||
|
|
||||||
|
for key, object in pairs(Table) do
|
||||||
|
if Key then
|
||||||
|
if Object[Key]==object[Key] then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if object==Object then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user