** ARMYGROUP**
- Added suppression option

**COMMANDER**
- Added function to add targets with defined resources

**OPSGROUP**
- Added option to pause multiple missions

**INTEL**
- Fixed bug in cluster calc

**LEGION**
- Added function to get alive opsgroups

**TARGET**
* Added start condition
This commit is contained in:
Frank 2022-05-27 22:14:21 +02:00
parent d3d815f26a
commit ae54cd8fde
15 changed files with 774 additions and 160 deletions

View File

@ -1543,6 +1543,33 @@ function DATABASE:FindOpsGroup(groupname)
return self.FLIGHTGROUPS[groupname]
end
--- Find an OPSGROUP (FLIGHTGROUP, ARMYGROUP, NAVYGROUP) in the data base for a given unit.
-- @param #DATABASE self
-- @param #string unitname Unit name. Can also be passed as UNIT object.
-- @return Ops.OpsGroup#OPSGROUP OPS group object.
function DATABASE:FindOpsGroupFromUnit(unitname)
local unit=nil --Wrapper.Unit#UNIT
local groupname
-- Get group and group name.
if type(unitname)=="string" then
unit=UNIT:FindByName(unitname)
else
unit=unitname
end
if unit then
groupname=unit:GetGroup():GetName()
end
if groupname then
return self.FLIGHTGROUPS[groupname]
else
return nil
end
end
--- Add a flight control to the data base.
-- @param #DATABASE self
-- @param Ops.FlightControl#FLIGHTCONTROL flightcontrol

View File

@ -6395,6 +6395,14 @@ do -- SET_OPSGROUP
self:Added(ObjectName, object)
end
--- Adds a @{Core.Base#BASE} object in the @{Core.Set#SET_BASE}, using the Object Name as the index.
-- @param #SET_BASE self
-- @param Ops.OpsGroup#OPSGROUP Object Ops group
-- @return Core.Base#BASE The added BASE Object.
function SET_OPSGROUP:AddObject(Object)
self:Add(Object.groupname, Object)
end
--- Add a GROUP or OPSGROUP object to the set.
-- **NOTE** that an OPSGROUP is automatically created from the GROUP if it does not exist already.
-- @param Core.Set#SET_OPSGROUP self

View File

@ -349,6 +349,7 @@
-- * @{#WAREHOUSE.Attribute.GROUND_APC} Infantry carriers, in particular Amoured Personell Carrier. This can be used to transport other assets.
-- * @{#WAREHOUSE.Attribute.GROUND_TRUCK} Unarmed ground vehicles, which has the DCS "Truck" attribute.
-- * @{#WAREHOUSE.Attribute.GROUND_INFANTRY} Ground infantry assets.
-- * @{#WAREHOUSE.Attribute.GROUND_IFV} Ground infantry fighting vehicle.
-- * @{#WAREHOUSE.Attribute.GROUND_ARTILLERY} Artillery assets.
-- * @{#WAREHOUSE.Attribute.GROUND_TANK} Tanks (modern or old).
-- * @{#WAREHOUSE.Attribute.GROUND_TRAIN} Trains. Not that trains are **not** yet properly implemented in DCS and cannot be used currently.
@ -1704,6 +1705,7 @@ WAREHOUSE.Descriptor = {
-- @field #string GROUND_APC Infantry carriers, in particular Amoured Personell Carrier. This can be used to transport other assets.
-- @field #string GROUND_TRUCK Unarmed ground vehicles, which has the DCS "Truck" attribute.
-- @field #string GROUND_INFANTRY Ground infantry assets.
-- @field #string GROUND_IFV Ground infantry fighting vehicle.
-- @field #string GROUND_ARTILLERY Artillery assets.
-- @field #string GROUND_TANK Tanks (modern or old).
-- @field #string GROUND_TRAIN Trains. Not that trains are **not** yet properly implemented in DCS and cannot be used currently.
@ -1730,6 +1732,7 @@ WAREHOUSE.Attribute = {
GROUND_APC="Ground_APC",
GROUND_TRUCK="Ground_Truck",
GROUND_INFANTRY="Ground_Infantry",
GROUND_IFV="Ground_IFV",
GROUND_ARTILLERY="Ground_Artillery",
GROUND_TANK="Ground_Tank",
GROUND_TRAIN="Ground_Train",
@ -8352,9 +8355,10 @@ function WAREHOUSE:_GetAttribute(group)
--- Ground ---
--------------
-- Ground
local apc=group:HasAttribute("Infantry carriers")
local apc=group:HasAttribute("APC") --("Infantry carriers")
local truck=group:HasAttribute("Trucks") and group:GetCategory()==Group.Category.GROUND
local infantry=group:HasAttribute("Infantry")
local ifv=group:HasAttribute("IFV")
local artillery=group:HasAttribute("Artillery")
local tank=group:HasAttribute("Old Tanks") or group:HasAttribute("Modern Tanks")
local aaa=group:HasAttribute("AAA")
@ -8391,6 +8395,8 @@ function WAREHOUSE:_GetAttribute(group)
attribute=WAREHOUSE.Attribute.AIR_UAV
elseif apc then
attribute=WAREHOUSE.Attribute.GROUND_APC
elseif ifv then
attribute=WAREHOUSE.Attribute.GROUND_IFV
elseif infantry then
attribute=WAREHOUSE.Attribute.GROUND_INFANTRY
elseif artillery then

View File

@ -34,6 +34,11 @@
-- @field #boolean isMobile If true, group is mobile.
-- @field #ARMYGROUP.Target engage Engage target.
-- @field Core.Set#SET_ZONE retreatZones Set of retreat zones.
-- @field #boolean suppressOn Bla
-- @field #boolean isSuppressed Bla
-- @field #number TsuppressMin Bla
-- @field #number TsuppressMax Bla
-- @field #number TsuppressAve Bla
-- @extends Ops.OpsGroup#OPSGROUP
--- *Your soul may belong to Jesus, but your ass belongs to the marines.* -- Eugene B Sledge
@ -122,6 +127,9 @@ function ARMYGROUP:New(group)
self:AddTransition("*", "Retreat", "Retreating") -- Order a retreat.
self:AddTransition("Retreating", "Retreated", "Retreated") -- Group retreated.
self:AddTransition("*", "Suppressed", "*") -- Group is suppressed
self:AddTransition("*", "Unsuppressed", "*") -- Group is unsuppressed.
self:AddTransition("Cruising", "EngageTarget", "Engaging") -- Engage a target from Cruising state
self:AddTransition("Holding", "EngageTarget", "Engaging") -- Engage a target from Holding state
self:AddTransition("OnDetour", "EngageTarget", "Engaging") -- Engage a target from OnDetour state
@ -280,6 +288,7 @@ function ARMYGROUP:New(group)
-- @function [parent=#ARMYGROUP] EngageTarget
-- @param #ARMYGROUP self
-- @param Wrapper.Group#GROUP Group the group to be engaged.
-- @param #number Speed Speed in knots.
-- @param #string Formation Formation used in the engagement.
--- Triggers the FSM event "EngageTarget" after a delay.
@ -287,6 +296,7 @@ function ARMYGROUP:New(group)
-- @param #ARMYGROUP self
-- @param #number delay Delay in seconds.
-- @param Wrapper.Group#GROUP Group the group to be engaged.
-- @param #number Speed Speed in knots.
-- @param #string Formation Formation used in the engagement.
@ -297,6 +307,7 @@ function ARMYGROUP:New(group)
-- @param #string Event Event.
-- @param #string To To state.
-- @param Wrapper.Group#GROUP Group the group to be engaged.
-- @param #number Speed Speed in knots.
-- @param #string Formation Formation used in the engagement.
@ -386,7 +397,7 @@ function ARMYGROUP:New(group)
self:HandleEvent(EVENTS.Birth, self.OnEventBirth)
self:HandleEvent(EVENTS.Dead, self.OnEventDead)
self:HandleEvent(EVENTS.RemoveUnit, self.OnEventRemoveUnit)
--self:HandleEvent(EVENTS.Hit, self.OnEventHit)
self:HandleEvent(EVENTS.Hit, self.OnEventHit)
-- Start the status monitoring.
self.timerStatus=TIMER:New(self.Status, self):Start(1, 30)
@ -572,6 +583,47 @@ function ARMYGROUP:AddRetreatZone(RetreatZone)
return self
end
--- Set suppression on. average, minimum and maximum time a unit is suppressed each time it gets hit.
-- @param #ARMYGROUP self
-- @param #number Tave Average time [seconds] a group will be suppressed. Default is 15 seconds.
-- @param #number Tmin (Optional) Minimum time [seconds] a group will be suppressed. Default is 5 seconds.
-- @param #number Tmax (Optional) Maximum time a group will be suppressed. Default is 25 seconds.
-- @return #ARMYGROUP self
function ARMYGROUP:SetSuppressionOn(Tave, Tmin, Tmax)
-- Activate suppression.
self.suppressionOn=true
-- Minimum suppression time is input or default 5 sec (but at least 1 second).
self.TsuppressMin=Tmin or 1
self.TsuppressMin=math.max(self.TsuppressMin, 1)
-- Maximum suppression time is input or default but at least Tmin.
self.TsuppressMax=Tmax or 15
self.TsuppressMax=math.max(self.TsuppressMax, self.TsuppressMin)
-- Expected suppression time is input or default but at leat Tmin and at most Tmax.
self.TsuppressAve=Tave or 10
self.TsuppressAve=math.max(self.TsuppressMin)
self.TsuppressAve=math.min(self.TsuppressMax)
-- Debug Info
self:T(self.lid..string.format("Set ave suppression time to %d seconds.", self.TsuppressAve))
self:T(self.lid..string.format("Set min suppression time to %d seconds.", self.TsuppressMin))
self:T(self.lid..string.format("Set max suppression time to %d seconds.", self.TsuppressMax))
return self
end
--- Set suppression off.
-- @param #ARMYGROUP self
-- @return #ARMYGROUP self
function ARMYGROUP:SetSuppressionOff()
-- Activate suppression.
self.suppressionOn=false
end
--- Check if the group is currently holding its positon.
-- @param #ARMYGROUP self
-- @return #boolean If true, group was ordered to hold.
@ -654,7 +706,11 @@ function ARMYGROUP:Status()
if timer.getAbsTime()>self.Twaiting+self.dTwait then
self.Twaiting=nil
self.dTwait=nil
self:Cruise()
if self:_CountPausedMissions()>0 then
self:UnpauseMission()
else
self:Cruise()
end
end
end
end
@ -799,22 +855,6 @@ end
-- DCS Events ==> See OPSGROUP
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Event function handling when a unit is hit.
-- @param #ARMYGROUP self
-- @param Core.Event#EVENTDATA EventData Event data.
function ARMYGROUP:OnEventHit(EventData)
-- Check that this is the right group.
if EventData and EventData.IniGroup and EventData.IniUnit and EventData.IniGroupName and EventData.IniGroupName==self.groupname then
local unit=EventData.IniUnit
local group=EventData.IniGroup
local unitname=EventData.IniUnitName
-- TODO: suppression
end
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- FSM Events
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@ -1126,7 +1166,7 @@ function ARMYGROUP:onafterUpdateRoute(From, Event, To, n, N, Speed, Formation)
self.speedWp=wp.speed
-- Debug output.
if self.verbose>=10 or true then
if self.verbose>=10 then
for i,_wp in pairs(waypoints) do
local wp=_wp --Ops.OpsGroup#OPSGROUP.Waypoint
@ -1228,6 +1268,16 @@ end
function ARMYGROUP:onafterOutOfAmmo(From, Event, To)
self:T(self.lid..string.format("Group is out of ammo at t=%.3f", timer.getTime()))
-- Get current task.
local task=self:GetTaskCurrent()
if task then
if task.dcstask.id=="FireAtPoint" or task.dcstask.id==AUFTRAG.SpecialTask.BARRAGE then
self:T(self.lid..string.format("Cancelling current %s task because out of ammo!", task.dcstask.id))
self:TaskCancel(task)
end
end
-- Fist, check if we want to rearm once out-of-ammo.
--TODO: IsMobile() check
if self.rearmOnOutOfAmmo then
@ -1251,16 +1301,6 @@ function ARMYGROUP:onafterOutOfAmmo(From, Event, To)
self:__RTZ(-1)
end
-- Get current task.
local task=self:GetTaskCurrent()
if task then
if task.dcstask.id=="FireAtPoint" or task.dcstask.id==AUFTRAG.SpecialTask.BARRAGE then
self:T(self.lid..string.format("Cancelling current %s task because out of ammo!", task.dcstask.id))
self:TaskCancel(task)
end
end
end
@ -1292,6 +1332,15 @@ function ARMYGROUP:onbeforeRearm(From, Event, To, Coordinate, Formation)
allowed=false
end
-- Check if coordinate is provided.
if allowed and not Coordinate then
local truck=self:FindNearestAmmoSupply()
if truck and truck:IsAlive() then
self:__Rearm(-0.1, truck:GetCoordinate(), Formation)
end
return false
end
-- Try again...
if dt then
self:T(self.lid..string.format("Trying Rearm again in %.2f sec", dt))
@ -1589,7 +1638,7 @@ function ARMYGROUP:onafterEngageTarget(From, Event, To, Target, Speed, Formation
self.engage.Coordinate=UTILS.DeepCopy(self.engage.Target:GetCoordinate())
-- Get a coordinate close to the target.
local intercoord=self:GetCoordinate():GetIntermediateCoordinate(self.engage.Coordinate, 0.9)
local intercoord=self:GetCoordinate():GetIntermediateCoordinate(self.engage.Coordinate, 0.95)
-- Backup ROE and alarm state.
self.engage.roe=self:GetROE()
@ -1752,6 +1801,21 @@ function ARMYGROUP:onafterCruise(From, Event, To, Speed, Formation)
end
--- On after "Hit" event.
-- @param #ARMYGROUP self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param Wrapper.Unit#UNIT Enemy Unit that hit the element or `nil`.
function ARMYGROUP:onafterHit(From, Event, To, Enemy)
self:T(self.lid..string.format("ArmyGroup hit by %s", Enemy and Enemy:GetName() or "unknown"))
if self.suppressionOn then
env.info(self.lid.."FF suppress")
self:_Suppress()
end
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Routing
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@ -1996,6 +2060,100 @@ function ARMYGROUP:FindNearestAmmoSupply(Radius)
return nil, nil
end
--- Suppress fire of the group by setting its ROE to weapon hold.
-- @param #ARMYGROUP self
function ARMYGROUP:_Suppress()
-- Current time.
local Tnow=timer.getTime()
-- Current ROE
local currROE=self:GetROE()
-- Get randomized time the unit is suppressed.
local sigma=(self.TsuppressMax-self.TsuppressMin)/4
-- Gaussian distribution.
local Tsuppress=UTILS.RandomGaussian(self.TsuppressAve,sigma,self.TsuppressMin, self.TsuppressMax)
-- Time at which the suppression is over.
local renew=true
if not self.TsuppressionOver then
-- Group is not suppressed currently.
self.TsuppressionOver=Tnow+Tsuppress
-- Group will hold their weapons.
self:SwitchROE(ENUMS.ROE.WeaponHold)
-- Backup ROE.
self.suppressionROE=currROE
else
-- Check if suppression is longer than current time.
if Tsuppress+Tnow > self.TsuppressionOver then
self.TsuppressionOver=Tnow+Tsuppress
else
renew=false
end
end
-- Recovery event will be called in Tsuppress seconds.
if renew then
self:__Unsuppressed(self.TsuppressionOver-Tnow)
end
-- Debug message.
self:T(self.lid..string.format("Suppressed for %d sec", Tsuppress))
end
--- Before "Recovered" event. Check if suppression time is over.
-- @param #ARMYGROUP self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @return #boolean
function ARMYGROUP:onbeforeUnsuppressed(From, Event, To)
-- Current time.
local Tnow=timer.getTime()
-- Debug info
self:T(self.lid..string.format("onbeforeRecovered: Time now: %d - Time over: %d", Tnow, self.TsuppressionOver))
-- Recovery is only possible if enough time since the last hit has passed.
if Tnow >= self.TsuppressionOver then
return true
else
return false
end
end
--- After "Recovered" event. Group has recovered and its ROE is set back to the "normal" unsuppressed state. Optionally the group is flared green.
-- @param #ARMYGROUP self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
function ARMYGROUP:onafterUnsuppressed(From, Event, To)
-- Debug message.
local text=string.format("Group %s has recovered!", self:GetName())
MESSAGE:New(text, 10):ToAll()
self:T(self.lid..text)
-- Set ROE back to default.
self:SwitchROE(self.suppressionROE)
-- Flare unit green.
if true then
self.group:FlareGreen()
end
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

View File

@ -2529,6 +2529,21 @@ function AUFTRAG:GetRequiredAssets(Legion)
return Nmin, Nmax
end
--- **[LEGION, COMMANDER, CHIEF]** Set that only alive (spawned) assets are considered.
-- @param #AUFTRAG self
-- @param #boolean Switch If true or nil, only active assets. If false
-- @return #AUFTRAG self
function AUFTRAG:SetAssetsStayAlive(Switch)
if Switch==nil then
Switch=true
end
self.assetStayAlive=Switch
return self
end
--- **[LEGION, COMMANDER, CHIEF]** Define how many assets are required that escort the mission assets.
-- Only used if the mission is handled by a **LEGION** (AIRWING, BRIGADE, FLEET) or higher level.
-- @param #AUFTRAG self

View File

@ -955,6 +955,7 @@ end
function CHIEF:AddTarget(Target)
if not self:IsTarget(Target) then
Target.chief=self
table.insert(self.targetqueue, Target)
end
@ -1536,7 +1537,7 @@ function CHIEF:onafterStatus(From, Event, To)
for _,_target in pairs(self.targetqueue) do
local target=_target --Ops.Target#TARGET
if target and target:IsAlive() and target.mission and target.mission:IsNotOver() then
if target and target:IsAlive() and target.chief and target.mission and target.mission:IsNotOver() then
local inborder=self:CheckTargetInZones(target, self.borderzoneset)

View File

@ -987,6 +987,31 @@ function COHORT:CountAssets(InStock, MissionTypes, Attributes)
return N
end
--- Get OPSGROUPs.
-- @param #COHORT self
-- @param #table MissionTypes (Optional) Count only assest that can perform certain mission type(s). Default is all types.
-- @param #table Attributes (Optional) Count only assest that have a certain attribute(s), e.g. `WAREHOUSE.Attribute.AIR_BOMBER`.
-- @return Core.Set#SET_OPSGROUPS Ops groups set.
function COHORT:GetOpsGroups(MissionTypes, Attributes)
local set=SET_OPSGROUP:New()
for _,_asset in pairs(self.assets) do
local asset=_asset --Functional.Warehouse#WAREHOUSE.Assetitem
if MissionTypes==nil or AUFTRAG.CheckMissionCapability(MissionTypes, self.missiontypes) then
if Attributes==nil or self:CheckAttribute(Attributes) then
if asset.flightgroup and asset.flightgroup:IsAlive() then
--set:AddObject(asset.flightgroup)
set:AddGroup(asset.flightgroup)
end
end
end
end
return set
end
--- Get assets for a mission.
-- @param #COHORT self
-- @param #string MissionType Mission type.
@ -1021,7 +1046,7 @@ function COHORT:RecruitAssets(MissionType, Npayloads)
if not (isRequested or isReserved) then
-- Check if asset is currently on a mission (STARTED or QUEUED).
if self.legion:IsAssetOnMission(asset) then
if self.legion:IsAssetOnMission(asset) then
---
-- Asset is already on a mission.
---
@ -1034,7 +1059,7 @@ function COHORT:RecruitAssets(MissionType, Npayloads)
elseif self.legion:IsAssetOnMission(asset, AUFTRAG.Type.NOTHING) then
-- Relocation: Take all assets. Mission will be cancelled.
-- Assets on mission NOTHING are considered.
table.insert(assets, asset)
elseif self.legion:IsAssetOnMission(asset, AUFTRAG.Type.GCICAP) and MissionType==AUFTRAG.Type.INTERCEPT then

View File

@ -837,7 +837,7 @@ function COMMANDER:onafterStatus(From, Event, To)
-- Status.
if self.verbose>=1 then
local text=string.format("Status %s: Legions=%d, Missions=%d, Transports", fsmstate, #self.legions, #self.missionqueue, #self.transportqueue)
local text=string.format("Status %s: Legions=%d, Missions=%d, Targets=%d, Transports=%d", fsmstate, #self.legions, #self.missionqueue, #self.targetqueue, #self.transportqueue)
self:T(self.lid..text)
end
@ -1032,6 +1032,21 @@ function COMMANDER:onafterStatus(From, Event, To)
self:I(self.lid..text)
end
---
-- TARGETS
---
-- Target queue.
if self.verbose>=2 and #self.targetqueue>0 then
local text="Target queue:"
for i,_target in pairs(self.targetqueue) do
local target=_target --Ops.Target#TARGET
text=text..string.format("\n[%d] %s: status=%s, life=%d", i, target:GetName(), target:GetState(), target:GetLife())
end
self:I(self.lid..text)
end
---
-- TRANSPORTS
---
@ -1214,6 +1229,20 @@ function COMMANDER:CheckTargetQueue()
return nil
end
-- Remove done targets.
for i=#self.targetqueue,1,-1 do
local target=self.targetqueue[i] --Ops.Target#TARGET
if (not target:IsAlive()) or target:EvalConditionsAny(target.conditionStop) then
for _,_resource in pairs(target.resources) do
local resource=_resource --Ops.Target#TARGET.Resource
if resource.mission and resource.mission:IsNotOver() then
self:MissionCancel(resource.mission)
end
end
table.remove(self.targetqueue, i)
end
end
-- Check if total number of missions is reached.
local NoLimit=self:_CheckMissionLimit("Total")
if NoLimit==false then
@ -1248,6 +1277,9 @@ function COMMANDER:CheckTargetQueue()
-- Is this target important enough.
local isImportant=(target.importance==nil or target.importance<=vip)
-- Check ALL start conditions are true.
local isReadyStart=target:EvalConditionsAll(target.conditionStart)
-- Debug message.
local text=string.format("Target %s: Alive=%s, Threat=%s, Important=%s", target:GetName(), tostring(isAlive), tostring(isThreat), tostring(isImportant))
self:T2(self.lid..text)
@ -1270,10 +1302,13 @@ function COMMANDER:CheckTargetQueue()
local mission=AUFTRAG:NewFromTarget(target, missionType)
if mission then
-- Set mission parameters.
mission:SetRequiredAssets(resource.Nmin, resource.Nmax)
mission:SetRequiredAttribute(resource.Attributes)
mission:SetRequiredProperty(resource.Properties)
-- Set resource mission.
resource.mission=mission
-- Add mission to queue.

View File

@ -2232,9 +2232,22 @@ function FLIGHTGROUP:_CheckGroupDone(delay, waittime)
return
end
-- First check if there is a paused mission.
if self.missionpaused then
self:T(self.lid..string.format("Found paused mission %s [%s]. Unpausing mission...", self.missionpaused.name, self.missionpaused.type))
-- Number of tasks remaining.
local nTasks=self:CountRemainingTasks()
-- Number of mission remaining.
local nMissions=self:CountRemainingMissison()
-- Number of cargo transports remaining.
local nTransports=self:CountRemainingTransports()
-- Number of paused missions.
local nPaused=self:_CountPausedMissions()
-- First check if there is a paused mission and that all remaining missions are paused. If there are other missions in the queue, we will run those.
if nPaused>0 and nPaused==nMissions then
local missionpaused=self:_GetPausedMission()
self:T(self.lid..string.format("Found paused mission %s [%s]. Unpausing mission...", missionpaused.name, missionpaused.type))
self:UnpauseMission()
return
end
@ -2251,15 +2264,6 @@ function FLIGHTGROUP:_CheckGroupDone(delay, waittime)
return
end
-- Number of tasks remaining.
local nTasks=self:CountRemainingTasks()
-- Number of mission remaining.
local nMissions=self:CountRemainingMissison()
-- Number of cargo transports remaining.
local nTransports=self:CountRemainingTransports()
-- Debug info.
self:T(self.lid..string.format("Remaining (final=%s): missions=%d, tasks=%d, transports=%d", tostring(self.passedfinalwp), nMissions, nTasks, nTransports))

View File

@ -1454,6 +1454,9 @@ function INTEL:PaintPicture()
else
-- Debug info.
self:T(self.lid..string.format("Paint Picture: contact %s has no closest cluster ==> Create new cluster", contact.groupname))
-- Create a brand new cluster.
local newcluster=self:_CreateClusterFromContact(contact)
@ -1817,13 +1820,13 @@ function INTEL:IsContactConnectedToCluster(contact, cluster)
--local dist=Contact.position:Get2DDistance(contact.position)
local dist=Contact.position:DistanceFromPointVec2(contact.position)
-- AIR - check for spatial proximity
local airprox = false
-- AIR - check for spatial proximity (corrected because airprox was always false for ctype~=INTEL.Ctype.AIRCRAFT)
local airprox = true
if contact.ctype == INTEL.Ctype.AIRCRAFT then
self:T(string.format("Cluster Alt=%d | Contact Alt=%d",cluster.altitude,contact.altitude))
local adist = math.abs(cluster.altitude - contact.altitude)
if adist < UTILS.FeetToMeters(10000) then -- limit to 10kft
airprox = true
if adist > UTILS.FeetToMeters(10000) then -- limit to 10kft
airprox = false
end
end
@ -1903,17 +1906,17 @@ function INTEL:_GetClosestClusterOfContact(Contact)
local dist=self:_GetDistContactToCluster(Contact, cluster)
-- AIR - check for spatial proximity
local airprox = false
-- AIR - check for spatial proximity (ff: Changed because airprox was always false for ctype~=AIRCRAFT!)
local airprox=true
if Contact.ctype == INTEL.Ctype.AIRCRAFT then
if not cluster.altitude then
cluster.altitude = self:GetClusterAltitude(cluster,true)
end
local adist = math.abs(cluster.altitude - Contact.altitude)
self:T(string.format("Cluster Alt=%d | Contact Alt=%d",cluster.altitude,Contact.altitude))
if adist < UTILS.FeetToMeters(10000) then
airprox = true
end
if not cluster.altitude then
cluster.altitude = self:GetClusterAltitude(cluster,true)
end
local adist = math.abs(cluster.altitude - Contact.altitude)
self:T(string.format("Cluster Alt=%d | Contact Alt=%d",cluster.altitude,Contact.altitude))
if adist > UTILS.FeetToMeters(10000) then
airprox = false
end
end
if dist<distmin and airprox then

View File

@ -1858,6 +1858,24 @@ function LEGION:CountAssets(InStock, MissionTypes, Attributes)
return N
end
--- Get OPSGROUPs that are spawned and alive.
-- @param #LEGION self
-- @param #table MissionTypes (Optional) Get only assest that can perform certain mission type(s). Default is all types.
-- @param #table Attributes (Optional) Get only assest that have a certain attribute(s), e.g. `WAREHOUSE.Attribute.AIR_BOMBER`.
-- @return Core.Set#SET_OPSGROUP The set of OPSGROUPs. Can be empty if no groups are spawned or alive!
function LEGION:GetOpsGroups(MissionTypes, Attributes)
local setLegion=SET_OPSGROUP:New()
for _,_cohort in pairs(self.cohorts) do
local cohort=_cohort --Ops.Cohort#COHORT
local setcohort=cohort:GetOpsGroups(MissionTypes, Attributes)
setLegion:AddSet(setcohort)
end
return setLegion
end
--- Count total number of assets in LEGION warehouse stock that also have a payload.
-- @param #LEGION self
-- @param #boolean Payloads (Optional) Specifc payloads to consider. Default all.

View File

@ -780,7 +780,11 @@ function NAVYGROUP:Status(From, Event, To)
if timer.getAbsTime()>self.Twaiting+self.dTwait then
self.Twaiting=nil
self.dTwait=nil
self:Cruise()
if self:_CountPausedMissions()>0 then
self:UnpauseMission()
else
self:Cruise()
end
end
end
end

View File

@ -68,9 +68,10 @@
-- @field Core.Timer#TIMER timerQueueUpdate Timer for queue updates.
-- @field #boolean groupinitialized If true, group parameters were initialized.
-- @field #boolean detectionOn If true, detected units of the group are analyzed.
-- @field Ops.Auftrag#AUFTRAG missionpaused Paused mission.
-- @field #table pausedmissions Paused missions.
-- @field #number Ndestroyed Number of destroyed units.
-- @field #number Nkills Number kills of this groups.
-- @field #number Nhit Number of hits taken.
--
-- @field #boolean rearmOnOutOfAmmo If `true`, group will go to rearm once it runs out of ammo.
--
@ -185,6 +186,7 @@ OPSGROUP = {
callsign = {},
Ndestroyed = 0,
Nkills = 0,
Nhit = 0,
weaponData = {},
cargoqueue = {},
cargoBay = {},
@ -192,6 +194,7 @@ OPSGROUP = {
carrierLoader = {},
carrierUnloader = {},
useMEtasks = false,
pausedmissions = {},
}
@ -206,6 +209,7 @@ OPSGROUP = {
-- @field #boolean ai If true, element is AI.
-- @field #string skill Skill level.
-- @field #string playerName Name of player if this is a client.
-- @field #number Nhit Number of times the element was hit.
--
-- @field Core.Zone#ZONE_POLYGON_BASE zoneBoundingbox Bounding box zone of the element unit.
-- @field Core.Zone#ZONE_POLYGON_BASE zoneLoad Loading zone.
@ -646,8 +650,9 @@ function OPSGROUP:New(group)
self:AddTransition("*", "InUtero", "InUtero") -- Deactivated group goes back to mummy.
self:AddTransition("*", "Stop", "Stopped") -- Stop FSM.
self:AddTransition("*", "Destroyed", "*") -- The whole group is dead.
self:AddTransition("*", "Hit", "*") -- Someone in the group was hit.
self:AddTransition("*", "Damaged", "*") -- Someone in the group took damage.
self:AddTransition("*", "Destroyed", "*") -- The whole group is dead.
self:AddTransition("*", "UpdateRoute", "*") -- Update route of group.
@ -706,6 +711,7 @@ function OPSGROUP:New(group)
self:AddTransition("*", "ElementDestroyed", "*") -- An element was destroyed.
self:AddTransition("*", "ElementDead", "*") -- An element is dead.
self:AddTransition("*", "ElementDamaged", "*") -- An element was damaged.
self:AddTransition("*", "ElementHit", "*") -- An element was hit.
self:AddTransition("*", "Board", "*") -- Group is ordered to board the carrier.
self:AddTransition("*", "Embarked", "*") -- Group was loaded into a cargo carrier.
@ -1176,6 +1182,105 @@ function OPSGROUP:IsTargetDetected(TargetObject)
return false
end
--- Check if a given coordinate is in weapon range.
-- @param #OPSGROUP self
-- @param Core.Point#COORDINATE TargetCoord Coordinate of the target.
-- @param #number WeaponBitType Weapon type.
-- @param Core.Point#COORDINATE RefCoord Reference coordinate.
-- @return #boolean If `true`, coordinate is in range.
function OPSGROUP:InWeaponRange(TargetCoord, WeaponBitType, RefCoord)
RefCoord=RefCoord or self:GetCoordinate()
local dist=TargetCoord:Get2DDistance(RefCoord)
if WeaponBitType then
local weapondata=self:GetWeaponData(WeaponBitType)
if weapondata then
if dist>=weapondata.RangeMin and dist<=weapondata.RangeMax then
return true
else
return false
end
end
else
for _,_weapondata in pairs(self.weaponData or {}) do
local weapondata=_weapondata --#OPSGROUP.WeaponData
if dist>=weapondata.RangeMin and dist<=weapondata.RangeMax then
return true
end
end
return false
end
return nil
end
--- Get a coordinate, which is in weapon range.
-- @param #OPSGROUP self
-- @param Core.Point#COORDINATE TargetCoord Coordinate of the target.
-- @param #number WeaponBitType Weapon type.
-- @param Core.Point#COORDINATE RefCoord Reference coordinate.
-- @return Core.Point#COORDINATE Coordinate in weapon range
function OPSGROUP:GetCoordinateInRange(TargetCoord, WeaponBitType, RefCoord)
local coordInRange=nil --Core.Point#COORDINATE
RefCoord=RefCoord or self:GetCoordinate()
-- Get weapon range.
local weapondata=self:GetWeaponData(WeaponBitType)
if weapondata then
-- Heading to target.
local heading=RefCoord:HeadingTo(TargetCoord)
-- Distance to target.
local dist=RefCoord:Get2DDistance(TargetCoord)
-- Check if we are within range.
if dist>weapondata.RangeMax then
local d=(dist-weapondata.RangeMax)*1.05
-- New waypoint coord.
coordInRange=RefCoord:Translate(d, heading)
-- Debug info.
self:T(self.lid..string.format("Out of max range = %.1f km for weapon %s", weapondata.RangeMax/1000, tostring(WeaponBitType)))
elseif dist<weapondata.RangeMin then
local d=(dist-weapondata.RangeMin)*1.05
-- New waypoint coord.
coordInRange=RefCoord:Translate(d, heading)
-- Debug info.
self:T(self.lid..string.format("Out of min range = %.1f km for weapon %s", weapondata.RangeMax/1000, tostring(WeaponBitType)))
else
-- Debug info.
self:T(self.lid..string.format("Already in range for weapon %s", tostring(WeaponBitType)))
end
else
self:T(self.lid..string.format("No weapon data for weapon type %s", tostring(WeaponBitType)))
end
return coordInRange
end
--- Set LASER parameters.
-- @param #OPSGROUP self
-- @param #number Code Laser code. Default 1688.
@ -2623,6 +2728,61 @@ function OPSGROUP:IsAwaitingLift(Transport)
return false
end
--- Get paused mission.
-- @param #OPSGROUP self
-- @return Ops.Auftrag#AUFTRAG Paused mission or nil.
function OPSGROUP:_GetPausedMission()
if self.pausedmissions and #self.pausedmissions>0 then
for _,mid in pairs(self.pausedmissions) do
if mid then
local mission=self:GetMissionByID(mid)
if mission and mission:IsNotOver() then
return mission
end
end
end
end
return nil
end
--- Count paused mission.
-- @param #OPSGROUP self
-- @return #number Number of paused missions.
function OPSGROUP:_CountPausedMissions()
local N=0
if self.pausedmissions and #self.pausedmissions>0 then
for _,mid in pairs(self.pausedmissions) do
local mission=self:GetMissionByID(mid)
if mission and mission:IsNotOver() then
N=N+1
end
end
end
return N
end
--- Remove paused mission from the table.
-- @param #OPSGROUP self
-- @param #number AuftragsNummer Mission ID of the paused mission to remove.
-- @return #OPSGROUP self
function OPSGROUP:_RemovePausedMission(AuftragsNummer)
if self.pausedmissions and #self.pausedmissions>0 then
for i=#self.pausedmissions,1,-1 do
local mid=self.pausedmissions[i]
if mid==AuftragsNummer then
table.remove(self.pausedmissions, i)
return self
end
end
end
return self
end
--- Check if the group is currently boarding a carrier.
-- @param #OPSGROUP self
-- @param #string CarrierGroupName (Optional) Additionally check if group is boarding this particular carrier group.
@ -3213,7 +3373,36 @@ function OPSGROUP:OnEventBirth(EventData)
end
--- Event function handling the crash of a unit.
--- Event function handling the hit of a unit.
-- @param #OPSGROUP self
-- @param Core.Event#EVENTDATA EventData Event data.
function OPSGROUP:OnEventHit(EventData)
-- Check that this is the right group. Here the hit group is stored as target.
if EventData and EventData.TgtGroup and EventData.TgtUnit and EventData.TgtGroupName and EventData.TgtGroupName==self.groupname then
self:T2(self.lid..string.format("EVENT: Unit %s hit!", EventData.TgtUnitName))
local unit=EventData.TgtUnit
local group=EventData.TgtGroup
local unitname=EventData.TgtUnitName
-- Get element.
local element=self:GetElementByName(unitname)
-- Increase group hit counter.
self.Nhit=self.Nhit or 0
self.Nhit=self.Nhit + 1
if element and element.status~=OPSGROUP.ElementStatus.DEAD then
-- Trigger Element Hit Event.
self:ElementHit(element, EventData.IniUnit)
end
end
end
--- Event function handling the dead of a unit.
-- @param #OPSGROUP self
-- @param Core.Event#EVENTDATA EventData Event data.
function OPSGROUP:OnEventDead(EventData)
@ -4491,8 +4680,10 @@ function OPSGROUP:RemoveMission(Mission)
end
-- Take care of a paused mission.
if self.missionpaused and self.missionpaused.auftragsnummer==Mission.auftragsnummer then
self.missionpaused=nil
for j,mid in pairs(self.pausedmissions) do
if Mission.auftragsnummer==mid then
table.remove(self.pausedmission, j)
end
end
-- Remove mission from queue.
@ -4865,7 +5056,7 @@ function OPSGROUP:onafterPauseMission(From, Event, To)
self:_RemoveMissionWaypoints(Mission)
-- Set mission to pause so we can unpause it later.
self.missionpaused=Mission
table.insert(self.pausedmissions, 1, Mission.auftragsnummer)
end
@ -4878,18 +5069,27 @@ end
-- @param #string To To state.
function OPSGROUP:onafterUnpauseMission(From, Event, To)
-- Debug info.
self:T(self.lid..string.format("Unpausing mission"))
-- Get paused mission.
local mission=self:_GetPausedMission()
if self.missionpaused then
if mission then
local mission=self:GetMissionByID(self.missionpaused.auftragsnummer)
-- Debug info.
self:T(self.lid..string.format("Unpausing mission %s [%s]", mission:GetName(), mission:GetType()))
if mission then
self:MissionStart(mission)
-- Start mission.
self:MissionStart(mission)
-- Remove mission from
for i,mid in pairs(self.pausedmissions) do
--self:T(self.lid..string.format("Checking paused mission", mid))
if mid==mission.auftragsnummer then
self:T(self.lid..string.format("Removing paused mission id=%d", mid))
table.remove(self.pausedmissions, i)
break
end
end
self.missionpaused=nil
else
self:T(self.lid.."ERROR: No mission to unpause!")
end
@ -4911,14 +5111,15 @@ function OPSGROUP:onafterMissionCancel(From, Event, To, Mission)
-- Current Mission
---
-- Alert 5 missoins dont have a task set, which could be cancelled.
-- Some missions dont have a task set, which could be cancelled.
if Mission.type==AUFTRAG.Type.ALERT5 or
Mission.type==AUFTRAG.Type.ONGUARD or
Mission.type==AUFTRAG.Type.ARMOREDGUARD or
Mission.type==AUFTRAG.Type.NOTHING or
--Mission.type==AUFTRAG.Type.NOTHING or
Mission.type==AUFTRAG.Type.AIRDEFENSE or
Mission.type==AUFTRAG.Type.EWR then
-- Trigger mission don task.
self:MissionDone(Mission)
return
@ -5144,10 +5345,6 @@ function OPSGROUP:RouteToMission(mission, delay)
return
end
if self.speedMax<=3.6 or mission.teleport then
--self:ClearWaypoints()
end
-- ID of current waypoint.
local uid=self:GetWaypointCurrentUID()
@ -5157,8 +5354,6 @@ function OPSGROUP:RouteToMission(mission, delay)
-- Current coordinate of the group.
local currentcoord=self:GetCoordinate()
currentcoord:MarkToAll(mission:GetName(),ReadOnly,Text)
-- Road connection.
local roadcoord=currentcoord:GetClosestPointToRoad()
@ -5181,12 +5376,9 @@ function OPSGROUP:RouteToMission(mission, delay)
surfacetypes={land.SurfaceType.WATER, land.SurfaceType.SHALLOW_WATER}
end
-- Get ingress waypoint.
if mission.opstransport and not mission.opstransport:IsCargoDelivered(self.groupname) then
--env.info(self.lid.."FF mission waypoint in embark zone")
-- Get transport zone combo.
local tzc=mission.opstransport:GetTZCofCargo(self.groupname)
@ -5200,7 +5392,6 @@ function OPSGROUP:RouteToMission(mission, delay)
else
-- Get a random coordinate inside the pickup zone.
waypointcoord=pickupzone:GetRandomCoordinate()
--waypointcoord:MarkToAll(self.lid.." embark here")
end
elseif mission.type==AUFTRAG.Type.PATROLZONE or
@ -5219,7 +5410,6 @@ function OPSGROUP:RouteToMission(mission, delay)
-- Random coordinate.
waypointcoord=targetzone:GetRandomCoordinate(nil , nil, surfacetypes)
waypointcoord:MarkToAll(mission:GetName(),ReadOnly,Text)
elseif mission.type==AUFTRAG.Type.ONGUARD or mission.type==AUFTRAG.Type.ARMOREDGUARD then
---
-- Guard
@ -5338,65 +5528,41 @@ function OPSGROUP:RouteToMission(mission, delay)
-- ARTY
---
-- Coord
local coord=waypointcoord
-- Target Coord.
local targetcoord=mission:GetTargetCoordinate()
-- Get weapon range.
local weapondata=self:GetWeaponData(mission.engageWeaponType)
local coordInRange=nil --Core.Point#COORDINATE
if weapondata then
-- In range already?
local inRange=self:InWeaponRange(targetcoord, mission.engageWeaponType)
-- Get target coordinate.
local targetcoord=mission:GetTargetCoordinate()
if inRange then
-- Heading to target.
local heading=coord:HeadingTo(targetcoord)
env.info("FF in range!")
-- Distance to target.
local dist=coord:Get2DDistance(targetcoord)
-- Check if we are within range.
if dist>weapondata.RangeMax then
local d=(dist-weapondata.RangeMax)*1.1
-- New waypoint coord.
coordInRange=coord:Translate(d, heading)
-- Debug info.
self:T(self.lid..string.format("Out of max range = %.1f km for weapon %s", weapondata.RangeMax/1000, tostring(mission.engageWeaponType)))
elseif dist<weapondata.RangeMin then
local d=(dist-weapondata.RangeMin)*1.1
-- New waypoint coord.
coordInRange=coord:Translate(d, heading)
-- Debug info.
self:T(self.lid..string.format("Out of min range = %.1f km for weapon %s", weapondata.RangeMax/1000, tostring(mission.engageWeaponType)))
end
waypointcoord=self:GetCoordinate(true)
else
self:T(self.lid..string.format("No weapon data for weapon type %s", tostring(mission.engageWeaponType)))
end
if coordInRange then
local coordInRange=self:GetCoordinateInRange(targetcoord, mission.engageWeaponType, waypointcoord)
if coordInRange then
-- Add waypoint at
local waypoint=nil --#OPSGROUP.Waypoint
if self:IsFlightgroup() then
waypoint=FLIGHTGROUP.AddWaypoint(self, waypointcoord, SpeedToMission, uid, UTILS.MetersToFeet(mission.missionAltitude or self.altitudeCruise), false)
elseif self:IsArmygroup() then
waypoint=ARMYGROUP.AddWaypoint(self, waypointcoord, SpeedToMission, uid, mission.optionFormation, false)
elseif self:IsNavygroup() then
waypoint=NAVYGROUP.AddWaypoint(self, waypointcoord, SpeedToMission, uid, UTILS.MetersToFeet(mission.missionAltitude or self.altitudeCruise), false)
end
waypoint.missionUID=mission.auftragsnummer
-- Set waypoint coord to be the one in range. Take care of proper waypoint uid.
waypointcoord=coordInRange
uid=waypoint.uid
-- Add waypoint at
local waypoint=nil --#OPSGROUP.Waypoint
if self:IsFlightgroup() then
waypoint=FLIGHTGROUP.AddWaypoint(self, waypointcoord, SpeedToMission, uid, UTILS.MetersToFeet(mission.missionAltitude or self.altitudeCruise), false)
elseif self:IsArmygroup() then
waypoint=ARMYGROUP.AddWaypoint(self, waypointcoord, SpeedToMission, uid, mission.optionFormation, false)
elseif self:IsNavygroup() then
waypoint=NAVYGROUP.AddWaypoint(self, waypointcoord, SpeedToMission, uid, UTILS.MetersToFeet(mission.missionAltitude or self.altitudeCruise), false)
end
waypoint.missionUID=mission.auftragsnummer
-- Set waypoint coord to be the one in range. Take care of proper waypoint uid.
waypointcoord=coordInRange
uid=waypoint.uid
end
@ -5568,6 +5734,7 @@ function OPSGROUP:_QueueUpdate()
-- Current mission but new mission is urgent with higher prio.
if mission.urgent and mission.prio<currentmission.prio then
self:T(self.lid.."FF got urgent mission with higher prio!")
self:MissionCancel(currentmission)
self:__MissionStart(1, mission)
end
@ -5617,9 +5784,17 @@ end
-- @param #number Duration Duration how long the group will be waiting in seconds. Default `nil` (=forever).
function OPSGROUP:onbeforeWait(From, Event, To, Duration)
env.info("FF before wait")
local allowed=true
local Tsuspend=nil
local mission=self:GetMissionCurrent()
if mission then
self:PauseMission()
return true
end
-- Check for a current task.
if self.taskcurrent>0 then
self:T(self.lid..string.format("WARNING: Got current task ==> WAIT event is suspended for 30 sec!"))
@ -6652,6 +6827,37 @@ function OPSGROUP:onafterElementDamaged(From, Event, To, Element)
end
--- On after "ElementHit" event.
-- @param #OPSGROUP self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param #OPSGROUP.Element Element The flight group element.
-- @param Wrapper.Unit#UNIT Enemy Unit that hit the element or `nil`.
function OPSGROUP:onafterElementHit(From, Event, To, Element, Enemy)
-- Increase element hit counter.
Element.Nhit=Element.Nhit+1
-- Debug message.
self:T(self.lid..string.format("Element hit %s by %s [n=%d, N=%d]", Element.name, Enemy and Enemy:GetName() or "unknown", Element.Nhit, self.Nhit))
-- Group was hit.
self:__Hit(-3, Enemy)
end
--- On after "Hit" event.
-- @param #OPSGROUP self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param Wrapper.Unit#UNIT Enemy Unit that hit the element or `nil`.
function OPSGROUP:onafterHit(From, Event, To, Enemy)
self:T(self.lid..string.format("Group hit by %s", Enemy and Enemy:GetName() or "unknown"))
end
--- On after "ElementDestroyed" event.
-- @param #OPSGROUP self
-- @param #string From From state.
@ -6818,9 +7024,9 @@ function OPSGROUP:Teleport(Coordinate, Delay, NoPauseMission)
if self:IsFlightgroup() then
Template.route.points[1]=Coordinate:WaypointAir("BARO", COORDINATE.WaypointType.TurningPoint, COORDINATE.WaypointAction.TurningPoint, 300, true, nil, nil, "Spawnpoint")
elseif self:IsArmygroup() then
Template.route.points[1]=Coordinate:WaypointGround()
Template.route.points[1]=Coordinate:WaypointGround(0)
elseif self:IsNavygroup() then
Template.route.points[1]=Coordinate:WaypointNaval()
Template.route.points[1]=Coordinate:WaypointNaval(0)
end
-- Template units.
@ -6892,6 +7098,7 @@ function OPSGROUP:_Respawn(Delay, Template, Reset)
-- Number of destroyed units.
self.Ndestroyed=0
self.Nhit=0
-- Check if group is currently alive.
if self:IsAlive() then
@ -7192,6 +7399,8 @@ function OPSGROUP:onafterStop(From, Event, To)
self:UnHandleEvent(EVENTS.Ejection)
self:UnHandleEvent(EVENTS.Crash)
self.currbase=nil
elseif self.isArmygroup then
self:UnHandleEvent(EVENTS.Hit)
end
for _,_mission in pairs(self.missionqueue) do
@ -9038,7 +9247,7 @@ function OPSGROUP:onafterUnloaded(From, Event, To, OpsGroupCargo)
OpsGroupCargo:Returned()
end
if OpsGroupCargo.missionpaused then
if self:_CountPausedMissions()>0 then
OpsGroupCargo:UnpauseMission()
end
@ -9618,9 +9827,13 @@ function OPSGROUP:_CheckGroupDone(delay)
-- Number of cargo transports remaining.
local nTransports=self:CountRemainingTransports()
-- First check if there is a paused mission that
if self.missionpaused and nMissions==1 then
self:T(self.lid..string.format("Found paused mission %s [%s]. Unpausing mission...", self.missionpaused.name, self.missionpaused.type))
-- Number of paused missions.
local nPaused=self:_CountPausedMissions()
-- First check if there is a paused mission and that all remaining missions are paused. If there are other missions in the queue, we will run those.
if nPaused>0 and nPaused==nMissions then
local missionpaused=self:_GetPausedMission()
self:T(self.lid..string.format("Found paused mission %s [%s]. Unpausing mission...", missionpaused.name, missionpaused.type))
self:UnpauseMission()
return
end
@ -12413,7 +12626,8 @@ function OPSGROUP:_AddElementByName(unitname)
element.gid=element.DCSunit:getNumber()
element.uid=element.DCSunit:getID()
--element.group=unit:GetGroup()
element.controller=element.DCSunit:getController()
element.controller=element.DCSunit:getController()
element.Nhit=0
element.opsgroup=self
-- Skill etc.
@ -12431,7 +12645,7 @@ function OPSGROUP:_AddElementByName(unitname)
element.categoryname=unit:GetCategoryName()
element.typename=unit:GetTypeName()
-- Describtors.
--self:I({desc=element.descriptors})
-- Ammo.

View File

@ -37,6 +37,7 @@
-- @field Ops.Intelligence#INTEL.Contact contact Contact attached to this target.
-- @field #boolean isDestroyed If true, target objects were destroyed.
-- @field #table resources Resource list.
-- @field #table conditionStart Start condition functions.
-- @extends Core.Fsm#FSM
--- **It is far more important to be able to hit the target than it is to haggle over who makes a weapon or who pulls a trigger** -- Dwight D Eisenhower
@ -65,7 +66,8 @@ TARGET = {
Ndead = 0,
elements = {},
casualties = {},
threatlevel0 = 0
threatlevel0 = 0,
conditionStart = {},
}
@ -318,6 +320,95 @@ function TARGET:SetImportance(Importance)
return self
end
--- Add start condition.
-- @param #TARGET self
-- @param #function ConditionFunction Function that needs to be true before the mission can be started. Must return a #boolean.
-- @param ... Condition function arguments if any.
-- @return #TARGET self
function TARGET:AddConditionStart(ConditionFunction, ...)
local condition={} --Ops.Auftrag#AUFTRAG.Condition
condition.func=ConditionFunction
condition.arg={}
if arg then
condition.arg=arg
end
table.insert(self.conditionStart, condition)
return self
end
--- Add stop condition.
-- @param #TARGET self
-- @param #function ConditionFunction Function that needs to be true before the mission can be started. Must return a #boolean.
-- @param ... Condition function arguments if any.
-- @return #TARGET self
function TARGET:AddConditionStop(ConditionFunction, ...)
local condition={} --Ops.Auftrag#AUFTRAG.Condition
condition.func=ConditionFunction
condition.arg={}
if arg then
condition.arg=arg
end
table.insert(self.conditionStop, condition)
return self
end
--- Check if all given condition are true.
-- @param #TARGET self
-- @param #table Conditions Table of conditions.
-- @return #boolean If true, all conditions were true. Returns false if at least one condition returned false.
function TARGET:EvalConditionsAll(Conditions)
-- Any stop condition must be true.
for _,_condition in pairs(Conditions or {}) do
local condition=_condition --Ops.Auftrag#AUFTRAG.Condition
-- Call function.
local istrue=condition.func(unpack(condition.arg))
-- Any false will return false.
if not istrue then
return false
end
end
-- All conditions were true.
return true
end
--- Check if any of the given conditions is true.
-- @param #TARGET self
-- @param #table Conditions Table of conditions.
-- @return #boolean If true, at least one condition is true.
function TARGET:EvalConditionsAny(Conditions)
-- Any stop condition must be true.
for _,_condition in pairs(Conditions or {}) do
local condition=_condition --Ops.Auftrag#AUFTRAG.Condition
-- Call function.
local istrue=condition.func(unpack(condition.arg))
-- Any true will return true.
if istrue then
return true
end
end
-- No condition was true.
return false
end
--- Add mission type and number of required assets to resource.
-- @param #TARGET self
-- @param #string MissionType Mission Type.

View File

@ -187,6 +187,7 @@ GROUPTEMPLATE.Takeoff = {
-- @field #string GROUND_APC Infantry carriers, in particular Amoured Personell Carrier. This can be used to transport other assets.
-- @field #string GROUND_TRUCK Unarmed ground vehicles, which has the DCS "Truck" attribute.
-- @field #string GROUND_INFANTRY Ground infantry assets.
-- @field #string GROUND_IFV Ground Infantry Fighting Vehicle.
-- @field #string GROUND_ARTILLERY Artillery assets.
-- @field #string GROUND_TANK Tanks (modern or old).
-- @field #string GROUND_TRAIN Trains. Not that trains are **not** yet properly implemented in DCS and cannot be used currently.
@ -213,6 +214,7 @@ GROUP.Attribute = {
GROUND_APC="Ground_APC",
GROUND_TRUCK="Ground_Truck",
GROUND_INFANTRY="Ground_Infantry",
GROUND_IFV="Ground_IFV",
GROUND_ARTILLERY="Ground_Artillery",
GROUND_TANK="Ground_Tank",
GROUND_TRAIN="Ground_Train",
@ -2378,13 +2380,14 @@ function GROUP:GetAttribute()
--- Ground ---
--------------
-- Ground
local apc=self:HasAttribute("Infantry carriers")
local apc=self:HasAttribute("APC")
local truck=self:HasAttribute("Trucks") and self:GetCategory()==Group.Category.GROUND
local infantry=self:HasAttribute("Infantry")
local artillery=self:HasAttribute("Artillery")
local tank=self:HasAttribute("Old Tanks") or self:HasAttribute("Modern Tanks")
local aaa=self:HasAttribute("AAA")
local ewr=self:HasAttribute("EWR")
local ifv=self:HasAttribute("IFV")
local sam=self:HasAttribute("SAM elements") and (not self:HasAttribute("AAA"))
-- Train
local train=self:GetCategory()==Group.Category.TRAIN
@ -2432,6 +2435,8 @@ function GROUP:GetAttribute()
attribute=GROUP.Attribute.GROUND_APC
elseif infantry then
attribute=GROUP.Attribute.GROUND_INFANTRY
elseif ifv then
attribute=GROUP.Attribute.GROUND_IFV
elseif truck then
attribute=GROUP.Attribute.GROUND_TRUCK
elseif train then