** 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] return self.FLIGHTGROUPS[groupname]
end 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. --- Add a flight control to the data base.
-- @param #DATABASE self -- @param #DATABASE self
-- @param Ops.FlightControl#FLIGHTCONTROL flightcontrol -- @param Ops.FlightControl#FLIGHTCONTROL flightcontrol

View File

@ -6395,6 +6395,14 @@ do -- SET_OPSGROUP
self:Added(ObjectName, object) self:Added(ObjectName, object)
end 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. --- 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. -- **NOTE** that an OPSGROUP is automatically created from the GROUP if it does not exist already.
-- @param Core.Set#SET_OPSGROUP self -- @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_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_TRUCK} Unarmed ground vehicles, which has the DCS "Truck" attribute.
-- * @{#WAREHOUSE.Attribute.GROUND_INFANTRY} Ground infantry assets. -- * @{#WAREHOUSE.Attribute.GROUND_INFANTRY} Ground infantry assets.
-- * @{#WAREHOUSE.Attribute.GROUND_IFV} Ground infantry fighting vehicle.
-- * @{#WAREHOUSE.Attribute.GROUND_ARTILLERY} Artillery assets. -- * @{#WAREHOUSE.Attribute.GROUND_ARTILLERY} Artillery assets.
-- * @{#WAREHOUSE.Attribute.GROUND_TANK} Tanks (modern or old). -- * @{#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. -- * @{#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_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_TRUCK Unarmed ground vehicles, which has the DCS "Truck" attribute.
-- @field #string GROUND_INFANTRY Ground infantry assets. -- @field #string GROUND_INFANTRY Ground infantry assets.
-- @field #string GROUND_IFV Ground infantry fighting vehicle.
-- @field #string GROUND_ARTILLERY Artillery assets. -- @field #string GROUND_ARTILLERY Artillery assets.
-- @field #string GROUND_TANK Tanks (modern or old). -- @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. -- @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_APC="Ground_APC",
GROUND_TRUCK="Ground_Truck", GROUND_TRUCK="Ground_Truck",
GROUND_INFANTRY="Ground_Infantry", GROUND_INFANTRY="Ground_Infantry",
GROUND_IFV="Ground_IFV",
GROUND_ARTILLERY="Ground_Artillery", GROUND_ARTILLERY="Ground_Artillery",
GROUND_TANK="Ground_Tank", GROUND_TANK="Ground_Tank",
GROUND_TRAIN="Ground_Train", GROUND_TRAIN="Ground_Train",
@ -8352,9 +8355,10 @@ function WAREHOUSE:_GetAttribute(group)
--- Ground --- --- Ground ---
-------------- --------------
-- 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 truck=group:HasAttribute("Trucks") and group:GetCategory()==Group.Category.GROUND
local infantry=group:HasAttribute("Infantry") local infantry=group:HasAttribute("Infantry")
local ifv=group:HasAttribute("IFV")
local artillery=group:HasAttribute("Artillery") local artillery=group:HasAttribute("Artillery")
local tank=group:HasAttribute("Old Tanks") or group:HasAttribute("Modern Tanks") local tank=group:HasAttribute("Old Tanks") or group:HasAttribute("Modern Tanks")
local aaa=group:HasAttribute("AAA") local aaa=group:HasAttribute("AAA")
@ -8391,6 +8395,8 @@ function WAREHOUSE:_GetAttribute(group)
attribute=WAREHOUSE.Attribute.AIR_UAV attribute=WAREHOUSE.Attribute.AIR_UAV
elseif apc then elseif apc then
attribute=WAREHOUSE.Attribute.GROUND_APC attribute=WAREHOUSE.Attribute.GROUND_APC
elseif ifv then
attribute=WAREHOUSE.Attribute.GROUND_IFV
elseif infantry then elseif infantry then
attribute=WAREHOUSE.Attribute.GROUND_INFANTRY attribute=WAREHOUSE.Attribute.GROUND_INFANTRY
elseif artillery then elseif artillery then

View File

@ -34,6 +34,11 @@
-- @field #boolean isMobile If true, group is mobile. -- @field #boolean isMobile If true, group is mobile.
-- @field #ARMYGROUP.Target engage Engage target. -- @field #ARMYGROUP.Target engage Engage target.
-- @field Core.Set#SET_ZONE retreatZones Set of retreat zones. -- @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 -- @extends Ops.OpsGroup#OPSGROUP
--- *Your soul may belong to Jesus, but your ass belongs to the marines.* -- Eugene B Sledge --- *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("*", "Retreat", "Retreating") -- Order a retreat.
self:AddTransition("Retreating", "Retreated", "Retreated") -- Group retreated. 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("Cruising", "EngageTarget", "Engaging") -- Engage a target from Cruising state
self:AddTransition("Holding", "EngageTarget", "Engaging") -- Engage a target from Holding state self:AddTransition("Holding", "EngageTarget", "Engaging") -- Engage a target from Holding state
self:AddTransition("OnDetour", "EngageTarget", "Engaging") -- Engage a target from OnDetour state self:AddTransition("OnDetour", "EngageTarget", "Engaging") -- Engage a target from OnDetour state
@ -280,6 +288,7 @@ function ARMYGROUP:New(group)
-- @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 Wrapper.Group#GROUP Group the group to be engaged.
-- @param #number Speed Speed in knots.
-- @param #string Formation Formation used in the engagement. -- @param #string Formation Formation used in the engagement.
--- Triggers the FSM event "EngageTarget" after a delay. --- Triggers the FSM event "EngageTarget" after a delay.
@ -287,6 +296,7 @@ function ARMYGROUP:New(group)
-- @param #ARMYGROUP self -- @param #ARMYGROUP self
-- @param #number delay Delay in seconds. -- @param #number delay Delay in seconds.
-- @param Wrapper.Group#GROUP Group the group to be engaged. -- @param Wrapper.Group#GROUP Group the group to be engaged.
-- @param #number Speed Speed in knots.
-- @param #string Formation Formation used in the engagement. -- @param #string Formation Formation used in the engagement.
@ -297,6 +307,7 @@ function ARMYGROUP:New(group)
-- @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 Wrapper.Group#GROUP Group the group to be engaged.
-- @param #number Speed Speed in knots.
-- @param #string Formation Formation used in the engagement. -- @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.Birth, self.OnEventBirth)
self:HandleEvent(EVENTS.Dead, self.OnEventDead) self:HandleEvent(EVENTS.Dead, self.OnEventDead)
self:HandleEvent(EVENTS.RemoveUnit, self.OnEventRemoveUnit) self:HandleEvent(EVENTS.RemoveUnit, self.OnEventRemoveUnit)
--self:HandleEvent(EVENTS.Hit, self.OnEventHit) self:HandleEvent(EVENTS.Hit, self.OnEventHit)
-- Start the status monitoring. -- Start the status monitoring.
self.timerStatus=TIMER:New(self.Status, self):Start(1, 30) self.timerStatus=TIMER:New(self.Status, self):Start(1, 30)
@ -572,6 +583,47 @@ function ARMYGROUP:AddRetreatZone(RetreatZone)
return self return self
end 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. --- Check if the group is currently holding its positon.
-- @param #ARMYGROUP self -- @param #ARMYGROUP self
-- @return #boolean If true, group was ordered to hold. -- @return #boolean If true, group was ordered to hold.
@ -654,7 +706,11 @@ function ARMYGROUP:Status()
if timer.getAbsTime()>self.Twaiting+self.dTwait then if timer.getAbsTime()>self.Twaiting+self.dTwait then
self.Twaiting=nil self.Twaiting=nil
self.dTwait=nil self.dTwait=nil
self:Cruise() if self:_CountPausedMissions()>0 then
self:UnpauseMission()
else
self:Cruise()
end
end end
end end
end end
@ -799,22 +855,6 @@ end
-- DCS Events ==> See OPSGROUP -- 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 -- FSM Events
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@ -1126,7 +1166,7 @@ function ARMYGROUP:onafterUpdateRoute(From, Event, To, n, N, Speed, Formation)
self.speedWp=wp.speed self.speedWp=wp.speed
-- Debug output. -- Debug output.
if self.verbose>=10 or true then if self.verbose>=10 then
for i,_wp in pairs(waypoints) do for i,_wp in pairs(waypoints) do
local wp=_wp --Ops.OpsGroup#OPSGROUP.Waypoint local wp=_wp --Ops.OpsGroup#OPSGROUP.Waypoint
@ -1228,6 +1268,16 @@ end
function ARMYGROUP:onafterOutOfAmmo(From, Event, To) function ARMYGROUP:onafterOutOfAmmo(From, Event, To)
self:T(self.lid..string.format("Group is out of ammo at t=%.3f", timer.getTime())) 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. -- Fist, check if we want to rearm once out-of-ammo.
--TODO: IsMobile() check --TODO: IsMobile() check
if self.rearmOnOutOfAmmo then if self.rearmOnOutOfAmmo then
@ -1251,16 +1301,6 @@ function ARMYGROUP:onafterOutOfAmmo(From, Event, To)
self:__RTZ(-1) self:__RTZ(-1)
end 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 end
@ -1292,6 +1332,15 @@ function ARMYGROUP:onbeforeRearm(From, Event, To, Coordinate, Formation)
allowed=false allowed=false
end 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... -- Try again...
if dt then if dt then
self:T(self.lid..string.format("Trying Rearm again in %.2f sec", dt)) 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()) self.engage.Coordinate=UTILS.DeepCopy(self.engage.Target:GetCoordinate())
-- Get a coordinate close to the target. -- 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. -- Backup ROE and alarm state.
self.engage.roe=self:GetROE() self.engage.roe=self:GetROE()
@ -1752,6 +1801,21 @@ function ARMYGROUP:onafterCruise(From, Event, To, Speed, Formation)
end 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 -- Routing
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@ -1996,6 +2060,100 @@ function ARMYGROUP:FindNearestAmmoSupply(Radius)
return nil, nil return nil, nil
end 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 return Nmin, Nmax
end 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. --- **[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. -- Only used if the mission is handled by a **LEGION** (AIRWING, BRIGADE, FLEET) or higher level.
-- @param #AUFTRAG self -- @param #AUFTRAG self

View File

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

View File

@ -987,6 +987,31 @@ function COHORT:CountAssets(InStock, MissionTypes, Attributes)
return N return N
end 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. --- Get assets for a mission.
-- @param #COHORT self -- @param #COHORT self
-- @param #string MissionType Mission type. -- @param #string MissionType Mission type.
@ -1021,7 +1046,7 @@ function COHORT:RecruitAssets(MissionType, Npayloads)
if not (isRequested or isReserved) then if not (isRequested or isReserved) then
-- Check if asset is currently on a mission (STARTED or QUEUED). -- 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. -- Asset is already on a mission.
--- ---
@ -1034,7 +1059,7 @@ function COHORT:RecruitAssets(MissionType, Npayloads)
elseif self.legion:IsAssetOnMission(asset, AUFTRAG.Type.NOTHING) then 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) table.insert(assets, asset)
elseif self.legion:IsAssetOnMission(asset, AUFTRAG.Type.GCICAP) and MissionType==AUFTRAG.Type.INTERCEPT then 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. -- Status.
if self.verbose>=1 then 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) self:T(self.lid..text)
end end
@ -1032,6 +1032,21 @@ function COMMANDER:onafterStatus(From, Event, To)
self:I(self.lid..text) self:I(self.lid..text)
end 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 -- TRANSPORTS
--- ---
@ -1214,6 +1229,20 @@ function COMMANDER:CheckTargetQueue()
return nil return nil
end 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. -- Check if total number of missions is reached.
local NoLimit=self:_CheckMissionLimit("Total") local NoLimit=self:_CheckMissionLimit("Total")
if NoLimit==false then if NoLimit==false then
@ -1248,6 +1277,9 @@ function COMMANDER:CheckTargetQueue()
-- Is this target important enough. -- Is this target important enough.
local isImportant=(target.importance==nil or target.importance<=vip) local isImportant=(target.importance==nil or target.importance<=vip)
-- Check ALL start conditions are true.
local isReadyStart=target:EvalConditionsAll(target.conditionStart)
-- Debug message. -- Debug message.
local text=string.format("Target %s: Alive=%s, Threat=%s, Important=%s", target:GetName(), tostring(isAlive), tostring(isThreat), tostring(isImportant)) 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) self:T2(self.lid..text)
@ -1270,10 +1302,13 @@ function COMMANDER:CheckTargetQueue()
local mission=AUFTRAG:NewFromTarget(target, missionType) local mission=AUFTRAG:NewFromTarget(target, missionType)
if mission then if mission then
-- Set mission parameters.
mission:SetRequiredAssets(resource.Nmin, resource.Nmax) mission:SetRequiredAssets(resource.Nmin, resource.Nmax)
mission:SetRequiredAttribute(resource.Attributes) mission:SetRequiredAttribute(resource.Attributes)
mission:SetRequiredProperty(resource.Properties) mission:SetRequiredProperty(resource.Properties)
-- Set resource mission.
resource.mission=mission resource.mission=mission
-- Add mission to queue. -- Add mission to queue.

View File

@ -2232,9 +2232,22 @@ function FLIGHTGROUP:_CheckGroupDone(delay, waittime)
return return
end end
-- First check if there is a paused mission. -- Number of tasks remaining.
if self.missionpaused then local nTasks=self:CountRemainingTasks()
self:T(self.lid..string.format("Found paused mission %s [%s]. Unpausing mission...", self.missionpaused.name, self.missionpaused.type))
-- 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() self:UnpauseMission()
return return
end end
@ -2251,15 +2264,6 @@ function FLIGHTGROUP:_CheckGroupDone(delay, waittime)
return return
end 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. -- Debug info.
self:T(self.lid..string.format("Remaining (final=%s): missions=%d, tasks=%d, transports=%d", tostring(self.passedfinalwp), nMissions, nTasks, nTransports)) 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 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. -- Create a brand new cluster.
local newcluster=self:_CreateClusterFromContact(contact) 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:Get2DDistance(contact.position)
local dist=Contact.position:DistanceFromPointVec2(contact.position) local dist=Contact.position:DistanceFromPointVec2(contact.position)
-- AIR - check for spatial proximity -- AIR - check for spatial proximity (corrected because airprox was always false for ctype~=INTEL.Ctype.AIRCRAFT)
local airprox = false local airprox = true
if contact.ctype == INTEL.Ctype.AIRCRAFT then if contact.ctype == INTEL.Ctype.AIRCRAFT then
self:T(string.format("Cluster Alt=%d | Contact Alt=%d",cluster.altitude,contact.altitude)) self:T(string.format("Cluster Alt=%d | Contact Alt=%d",cluster.altitude,contact.altitude))
local adist = math.abs(cluster.altitude - contact.altitude) local adist = math.abs(cluster.altitude - contact.altitude)
if adist < UTILS.FeetToMeters(10000) then -- limit to 10kft if adist > UTILS.FeetToMeters(10000) then -- limit to 10kft
airprox = true airprox = false
end end
end end
@ -1903,17 +1906,17 @@ function INTEL:_GetClosestClusterOfContact(Contact)
local dist=self:_GetDistContactToCluster(Contact, cluster) local dist=self:_GetDistContactToCluster(Contact, cluster)
-- AIR - check for spatial proximity -- AIR - check for spatial proximity (ff: Changed because airprox was always false for ctype~=AIRCRAFT!)
local airprox = false local airprox=true
if Contact.ctype == INTEL.Ctype.AIRCRAFT then if Contact.ctype == INTEL.Ctype.AIRCRAFT then
if not cluster.altitude then if not cluster.altitude then
cluster.altitude = self:GetClusterAltitude(cluster,true) cluster.altitude = self:GetClusterAltitude(cluster,true)
end end
local adist = math.abs(cluster.altitude - Contact.altitude) local adist = math.abs(cluster.altitude - Contact.altitude)
self:T(string.format("Cluster Alt=%d | Contact Alt=%d",cluster.altitude,Contact.altitude)) self:T(string.format("Cluster Alt=%d | Contact Alt=%d",cluster.altitude,Contact.altitude))
if adist < UTILS.FeetToMeters(10000) then if adist > UTILS.FeetToMeters(10000) then
airprox = true airprox = false
end end
end end
if dist<distmin and airprox then if dist<distmin and airprox then

View File

@ -1858,6 +1858,24 @@ function LEGION:CountAssets(InStock, MissionTypes, Attributes)
return N return N
end 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. --- Count total number of assets in LEGION warehouse stock that also have a payload.
-- @param #LEGION self -- @param #LEGION self
-- @param #boolean Payloads (Optional) Specifc payloads to consider. Default all. -- @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 if timer.getAbsTime()>self.Twaiting+self.dTwait then
self.Twaiting=nil self.Twaiting=nil
self.dTwait=nil self.dTwait=nil
self:Cruise() if self:_CountPausedMissions()>0 then
self:UnpauseMission()
else
self:Cruise()
end
end end
end end
end end

View File

@ -68,9 +68,10 @@
-- @field Core.Timer#TIMER timerQueueUpdate Timer for queue updates. -- @field Core.Timer#TIMER timerQueueUpdate Timer for queue updates.
-- @field #boolean groupinitialized If true, group parameters were initialized. -- @field #boolean groupinitialized If true, group parameters were initialized.
-- @field #boolean detectionOn If true, detected units of the group are analyzed. -- @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 Ndestroyed Number of destroyed units.
-- @field #number Nkills Number kills of this groups. -- @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. -- @field #boolean rearmOnOutOfAmmo If `true`, group will go to rearm once it runs out of ammo.
-- --
@ -185,6 +186,7 @@ OPSGROUP = {
callsign = {}, callsign = {},
Ndestroyed = 0, Ndestroyed = 0,
Nkills = 0, Nkills = 0,
Nhit = 0,
weaponData = {}, weaponData = {},
cargoqueue = {}, cargoqueue = {},
cargoBay = {}, cargoBay = {},
@ -192,6 +194,7 @@ OPSGROUP = {
carrierLoader = {}, carrierLoader = {},
carrierUnloader = {}, carrierUnloader = {},
useMEtasks = false, useMEtasks = false,
pausedmissions = {},
} }
@ -206,6 +209,7 @@ OPSGROUP = {
-- @field #boolean ai If true, element is AI. -- @field #boolean ai If true, element is AI.
-- @field #string skill Skill level. -- @field #string skill Skill level.
-- @field #string playerName Name of player if this is a client. -- @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 zoneBoundingbox Bounding box zone of the element unit.
-- @field Core.Zone#ZONE_POLYGON_BASE zoneLoad Loading zone. -- @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("*", "InUtero", "InUtero") -- Deactivated group goes back to mummy.
self:AddTransition("*", "Stop", "Stopped") -- Stop FSM. 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("*", "Damaged", "*") -- Someone in the group took damage.
self:AddTransition("*", "Destroyed", "*") -- The whole group is dead.
self:AddTransition("*", "UpdateRoute", "*") -- Update route of group. self:AddTransition("*", "UpdateRoute", "*") -- Update route of group.
@ -706,6 +711,7 @@ function OPSGROUP:New(group)
self:AddTransition("*", "ElementDestroyed", "*") -- An element was destroyed. self:AddTransition("*", "ElementDestroyed", "*") -- An element was destroyed.
self:AddTransition("*", "ElementDead", "*") -- An element is dead. self:AddTransition("*", "ElementDead", "*") -- An element is dead.
self:AddTransition("*", "ElementDamaged", "*") -- An element was damaged. 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("*", "Board", "*") -- Group is ordered to board the carrier.
self:AddTransition("*", "Embarked", "*") -- Group was loaded into a cargo carrier. self:AddTransition("*", "Embarked", "*") -- Group was loaded into a cargo carrier.
@ -1176,6 +1182,105 @@ function OPSGROUP:IsTargetDetected(TargetObject)
return false return false
end 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. --- Set LASER parameters.
-- @param #OPSGROUP self -- @param #OPSGROUP self
-- @param #number Code Laser code. Default 1688. -- @param #number Code Laser code. Default 1688.
@ -2623,6 +2728,61 @@ function OPSGROUP:IsAwaitingLift(Transport)
return false return false
end 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. --- Check if the group is currently boarding a carrier.
-- @param #OPSGROUP self -- @param #OPSGROUP self
-- @param #string CarrierGroupName (Optional) Additionally check if group is boarding this particular carrier group. -- @param #string CarrierGroupName (Optional) Additionally check if group is boarding this particular carrier group.
@ -3213,7 +3373,36 @@ function OPSGROUP:OnEventBirth(EventData)
end 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 #OPSGROUP self
-- @param Core.Event#EVENTDATA EventData Event data. -- @param Core.Event#EVENTDATA EventData Event data.
function OPSGROUP:OnEventDead(EventData) function OPSGROUP:OnEventDead(EventData)
@ -4491,8 +4680,10 @@ function OPSGROUP:RemoveMission(Mission)
end end
-- Take care of a paused mission. -- Take care of a paused mission.
if self.missionpaused and self.missionpaused.auftragsnummer==Mission.auftragsnummer then for j,mid in pairs(self.pausedmissions) do
self.missionpaused=nil if Mission.auftragsnummer==mid then
table.remove(self.pausedmission, j)
end
end end
-- Remove mission from queue. -- Remove mission from queue.
@ -4865,7 +5056,7 @@ function OPSGROUP:onafterPauseMission(From, Event, To)
self:_RemoveMissionWaypoints(Mission) self:_RemoveMissionWaypoints(Mission)
-- Set mission to pause so we can unpause it later. -- Set mission to pause so we can unpause it later.
self.missionpaused=Mission table.insert(self.pausedmissions, 1, Mission.auftragsnummer)
end end
@ -4878,18 +5069,27 @@ end
-- @param #string To To state. -- @param #string To To state.
function OPSGROUP:onafterUnpauseMission(From, Event, To) function OPSGROUP:onafterUnpauseMission(From, Event, To)
-- Debug info. -- Get paused mission.
self:T(self.lid..string.format("Unpausing 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 -- Start mission.
self:MissionStart(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 end
self.missionpaused=nil
else else
self:T(self.lid.."ERROR: No mission to unpause!") self:T(self.lid.."ERROR: No mission to unpause!")
end end
@ -4911,14 +5111,15 @@ function OPSGROUP:onafterMissionCancel(From, Event, To, Mission)
-- Current 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 if Mission.type==AUFTRAG.Type.ALERT5 or
Mission.type==AUFTRAG.Type.ONGUARD or Mission.type==AUFTRAG.Type.ONGUARD or
Mission.type==AUFTRAG.Type.ARMOREDGUARD 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.AIRDEFENSE or
Mission.type==AUFTRAG.Type.EWR then Mission.type==AUFTRAG.Type.EWR then
-- Trigger mission don task.
self:MissionDone(Mission) self:MissionDone(Mission)
return return
@ -5144,10 +5345,6 @@ function OPSGROUP:RouteToMission(mission, delay)
return return
end end
if self.speedMax<=3.6 or mission.teleport then
--self:ClearWaypoints()
end
-- ID of current waypoint. -- ID of current waypoint.
local uid=self:GetWaypointCurrentUID() local uid=self:GetWaypointCurrentUID()
@ -5157,8 +5354,6 @@ function OPSGROUP:RouteToMission(mission, delay)
-- Current coordinate of the group. -- Current coordinate of the group.
local currentcoord=self:GetCoordinate() local currentcoord=self:GetCoordinate()
currentcoord:MarkToAll(mission:GetName(),ReadOnly,Text)
-- Road connection. -- Road connection.
local roadcoord=currentcoord:GetClosestPointToRoad() local roadcoord=currentcoord:GetClosestPointToRoad()
@ -5181,12 +5376,9 @@ function OPSGROUP:RouteToMission(mission, delay)
surfacetypes={land.SurfaceType.WATER, land.SurfaceType.SHALLOW_WATER} surfacetypes={land.SurfaceType.WATER, land.SurfaceType.SHALLOW_WATER}
end 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
--env.info(self.lid.."FF mission waypoint in embark zone")
-- Get transport zone combo. -- Get transport zone combo.
local tzc=mission.opstransport:GetTZCofCargo(self.groupname) local tzc=mission.opstransport:GetTZCofCargo(self.groupname)
@ -5200,7 +5392,6 @@ function OPSGROUP:RouteToMission(mission, delay)
else else
-- Get a random coordinate inside the pickup zone. -- Get a random coordinate inside the pickup zone.
waypointcoord=pickupzone:GetRandomCoordinate() waypointcoord=pickupzone:GetRandomCoordinate()
--waypointcoord:MarkToAll(self.lid.." embark here")
end end
elseif mission.type==AUFTRAG.Type.PATROLZONE or elseif mission.type==AUFTRAG.Type.PATROLZONE or
@ -5219,7 +5410,6 @@ function OPSGROUP:RouteToMission(mission, delay)
-- Random coordinate. -- Random coordinate.
waypointcoord=targetzone:GetRandomCoordinate(nil , nil, surfacetypes) 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 elseif mission.type==AUFTRAG.Type.ONGUARD or mission.type==AUFTRAG.Type.ARMOREDGUARD then
--- ---
-- Guard -- Guard
@ -5338,65 +5528,41 @@ function OPSGROUP:RouteToMission(mission, delay)
-- ARTY -- ARTY
--- ---
-- Coord -- Target Coord.
local coord=waypointcoord local targetcoord=mission:GetTargetCoordinate()
-- Get weapon range.
local weapondata=self:GetWeaponData(mission.engageWeaponType)
local coordInRange=nil --Core.Point#COORDINATE -- In range already?
if weapondata then local inRange=self:InWeaponRange(targetcoord, mission.engageWeaponType)
-- Get target coordinate. if inRange then
local targetcoord=mission:GetTargetCoordinate()
-- Heading to target. env.info("FF in range!")
local heading=coord:HeadingTo(targetcoord)
-- Distance to target. waypointcoord=self:GetCoordinate(true)
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
else 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 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 end
@ -5568,6 +5734,7 @@ function OPSGROUP:_QueueUpdate()
-- Current mission but new mission is urgent with higher prio. -- Current mission but new mission is urgent with higher prio.
if mission.urgent and mission.prio<currentmission.prio then if mission.urgent and mission.prio<currentmission.prio then
self:T(self.lid.."FF got urgent mission with higher prio!")
self:MissionCancel(currentmission) self:MissionCancel(currentmission)
self:__MissionStart(1, mission) self:__MissionStart(1, mission)
end end
@ -5617,9 +5784,17 @@ end
-- @param #number Duration Duration how long the group will be waiting in seconds. Default `nil` (=forever). -- @param #number Duration Duration how long the group will be waiting in seconds. Default `nil` (=forever).
function OPSGROUP:onbeforeWait(From, Event, To, Duration) function OPSGROUP:onbeforeWait(From, Event, To, Duration)
env.info("FF before wait")
local allowed=true local allowed=true
local Tsuspend=nil local Tsuspend=nil
local mission=self:GetMissionCurrent()
if mission then
self:PauseMission()
return true
end
-- Check for a current task. -- Check for a current task.
if self.taskcurrent>0 then if self.taskcurrent>0 then
self:T(self.lid..string.format("WARNING: Got current task ==> WAIT event is suspended for 30 sec!")) 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 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. --- On after "ElementDestroyed" event.
-- @param #OPSGROUP self -- @param #OPSGROUP self
-- @param #string From From state. -- @param #string From From state.
@ -6818,9 +7024,9 @@ function OPSGROUP:Teleport(Coordinate, Delay, NoPauseMission)
if self:IsFlightgroup() then if self:IsFlightgroup() then
Template.route.points[1]=Coordinate:WaypointAir("BARO", COORDINATE.WaypointType.TurningPoint, COORDINATE.WaypointAction.TurningPoint, 300, true, nil, nil, "Spawnpoint") Template.route.points[1]=Coordinate:WaypointAir("BARO", COORDINATE.WaypointType.TurningPoint, COORDINATE.WaypointAction.TurningPoint, 300, true, nil, nil, "Spawnpoint")
elseif self:IsArmygroup() then elseif self:IsArmygroup() then
Template.route.points[1]=Coordinate:WaypointGround() Template.route.points[1]=Coordinate:WaypointGround(0)
elseif self:IsNavygroup() then elseif self:IsNavygroup() then
Template.route.points[1]=Coordinate:WaypointNaval() Template.route.points[1]=Coordinate:WaypointNaval(0)
end end
-- Template units. -- Template units.
@ -6892,6 +7098,7 @@ function OPSGROUP:_Respawn(Delay, Template, Reset)
-- Number of destroyed units. -- Number of destroyed units.
self.Ndestroyed=0 self.Ndestroyed=0
self.Nhit=0
-- Check if group is currently alive. -- Check if group is currently alive.
if self:IsAlive() then if self:IsAlive() then
@ -7192,6 +7399,8 @@ function OPSGROUP:onafterStop(From, Event, To)
self:UnHandleEvent(EVENTS.Ejection) self:UnHandleEvent(EVENTS.Ejection)
self:UnHandleEvent(EVENTS.Crash) self:UnHandleEvent(EVENTS.Crash)
self.currbase=nil self.currbase=nil
elseif self.isArmygroup then
self:UnHandleEvent(EVENTS.Hit)
end end
for _,_mission in pairs(self.missionqueue) do for _,_mission in pairs(self.missionqueue) do
@ -9038,7 +9247,7 @@ function OPSGROUP:onafterUnloaded(From, Event, To, OpsGroupCargo)
OpsGroupCargo:Returned() OpsGroupCargo:Returned()
end end
if OpsGroupCargo.missionpaused then if self:_CountPausedMissions()>0 then
OpsGroupCargo:UnpauseMission() OpsGroupCargo:UnpauseMission()
end end
@ -9618,9 +9827,13 @@ function OPSGROUP:_CheckGroupDone(delay)
-- Number of cargo transports remaining. -- Number of cargo transports remaining.
local nTransports=self:CountRemainingTransports() local nTransports=self:CountRemainingTransports()
-- First check if there is a paused mission that -- Number of paused missions.
if self.missionpaused and nMissions==1 then local nPaused=self:_CountPausedMissions()
self:T(self.lid..string.format("Found paused mission %s [%s]. Unpausing mission...", self.missionpaused.name, self.missionpaused.type))
-- 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() self:UnpauseMission()
return return
end end
@ -12413,7 +12626,8 @@ function OPSGROUP:_AddElementByName(unitname)
element.gid=element.DCSunit:getNumber() element.gid=element.DCSunit:getNumber()
element.uid=element.DCSunit:getID() element.uid=element.DCSunit:getID()
--element.group=unit:GetGroup() --element.group=unit:GetGroup()
element.controller=element.DCSunit:getController() element.controller=element.DCSunit:getController()
element.Nhit=0
element.opsgroup=self element.opsgroup=self
-- Skill etc. -- Skill etc.
@ -12431,7 +12645,7 @@ function OPSGROUP:_AddElementByName(unitname)
element.categoryname=unit:GetCategoryName() element.categoryname=unit:GetCategoryName()
element.typename=unit:GetTypeName() element.typename=unit:GetTypeName()
-- Describtors.
--self:I({desc=element.descriptors}) --self:I({desc=element.descriptors})
-- Ammo. -- Ammo.

View File

@ -37,6 +37,7 @@
-- @field Ops.Intelligence#INTEL.Contact contact Contact attached to this target. -- @field Ops.Intelligence#INTEL.Contact contact Contact attached to this target.
-- @field #boolean isDestroyed If true, target objects were destroyed. -- @field #boolean isDestroyed If true, target objects were destroyed.
-- @field #table resources Resource list. -- @field #table resources Resource list.
-- @field #table conditionStart Start condition functions.
-- @extends Core.Fsm#FSM -- @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 --- **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, Ndead = 0,
elements = {}, elements = {},
casualties = {}, casualties = {},
threatlevel0 = 0 threatlevel0 = 0,
conditionStart = {},
} }
@ -318,6 +320,95 @@ function TARGET:SetImportance(Importance)
return self return self
end 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. --- Add mission type and number of required assets to resource.
-- @param #TARGET self -- @param #TARGET self
-- @param #string MissionType Mission Type. -- @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_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_TRUCK Unarmed ground vehicles, which has the DCS "Truck" attribute.
-- @field #string GROUND_INFANTRY Ground infantry assets. -- @field #string GROUND_INFANTRY Ground infantry assets.
-- @field #string GROUND_IFV Ground Infantry Fighting Vehicle.
-- @field #string GROUND_ARTILLERY Artillery assets. -- @field #string GROUND_ARTILLERY Artillery assets.
-- @field #string GROUND_TANK Tanks (modern or old). -- @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. -- @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_APC="Ground_APC",
GROUND_TRUCK="Ground_Truck", GROUND_TRUCK="Ground_Truck",
GROUND_INFANTRY="Ground_Infantry", GROUND_INFANTRY="Ground_Infantry",
GROUND_IFV="Ground_IFV",
GROUND_ARTILLERY="Ground_Artillery", GROUND_ARTILLERY="Ground_Artillery",
GROUND_TANK="Ground_Tank", GROUND_TANK="Ground_Tank",
GROUND_TRAIN="Ground_Train", GROUND_TRAIN="Ground_Train",
@ -2378,13 +2380,14 @@ function GROUP:GetAttribute()
--- Ground --- --- Ground ---
-------------- --------------
-- 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 truck=self:HasAttribute("Trucks") and self:GetCategory()==Group.Category.GROUND
local infantry=self:HasAttribute("Infantry") local infantry=self:HasAttribute("Infantry")
local artillery=self:HasAttribute("Artillery") local artillery=self:HasAttribute("Artillery")
local tank=self:HasAttribute("Old Tanks") or self:HasAttribute("Modern Tanks") local tank=self:HasAttribute("Old Tanks") or self:HasAttribute("Modern Tanks")
local aaa=self:HasAttribute("AAA") local aaa=self:HasAttribute("AAA")
local ewr=self:HasAttribute("EWR") local ewr=self:HasAttribute("EWR")
local ifv=self:HasAttribute("IFV")
local sam=self:HasAttribute("SAM elements") and (not self:HasAttribute("AAA")) local sam=self:HasAttribute("SAM elements") and (not self:HasAttribute("AAA"))
-- Train -- Train
local train=self:GetCategory()==Group.Category.TRAIN local train=self:GetCategory()==Group.Category.TRAIN
@ -2432,6 +2435,8 @@ function GROUP:GetAttribute()
attribute=GROUP.Attribute.GROUND_APC attribute=GROUP.Attribute.GROUND_APC
elseif infantry then elseif infantry then
attribute=GROUP.Attribute.GROUND_INFANTRY attribute=GROUP.Attribute.GROUND_INFANTRY
elseif ifv then
attribute=GROUP.Attribute.GROUND_IFV
elseif truck then elseif truck then
attribute=GROUP.Attribute.GROUND_TRUCK attribute=GROUP.Attribute.GROUND_TRUCK
elseif train then elseif train then