CHIEF
- Added option to customize reaction on strategic zones

AUFTRAG
- Added new type GROUNDATTACK
This commit is contained in:
Frank 2022-04-01 11:17:43 +02:00
parent e8303064b9
commit c5a50d23b6
9 changed files with 716 additions and 288 deletions

View File

@ -5453,57 +5453,63 @@ end
-- @param #WAREHOUSE.Pendingitem request The request of the dead asset.
function WAREHOUSE:onafterAssetDead(From, Event, To, asset, request)
-- Debug message.
local text=string.format("Asset %s from request id=%d is dead!", asset.templatename, request.uid)
self:T(self.lid..text)
if asset and request then
-- Here I need to get rid of the #CARGO at the end to obtain the original name again!
local groupname=asset.spawngroupname --self:_GetNameWithOut(group)
-- Dont trigger a Remove event for the group sets.
local NoTriggerEvent=true
if request.transporttype==WAREHOUSE.TransportType.SELFPROPELLED then
---
-- Easy case: Group can simply be removed from the cargogroupset.
---
-- Remove dead group from cargo group set.
request.cargogroupset:Remove(groupname, NoTriggerEvent)
self:T(self.lid..string.format("Removed selfpropelled cargo %s: ncargo=%d.", groupname, request.cargogroupset:Count()))
else
---
-- Complicated case: Dead unit could be:
-- 1.) A Cargo unit (e.g. waiting to be picked up).
-- 2.) A Transport unit which itself holds cargo groups.
---
-- Check if this a cargo or transport group.
local istransport=not asset.iscargo --self:_GroupIsTransport(group, request)
if istransport==true then
-- Whole carrier group is dead. Remove it from the carrier group set.
request.transportgroupset:Remove(groupname, NoTriggerEvent)
self:T(self.lid..string.format("Removed transport %s: ntransport=%d", groupname, request.transportgroupset:Count()))
elseif istransport==false then
-- This must have been an alive cargo group that was killed outside the carrier, e.g. waiting to be transported or waiting to be put back.
-- Debug message.
local text=string.format("Asset %s from request id=%d is dead!", asset.templatename, request.uid)
self:T(self.lid..text)
-- Here I need to get rid of the #CARGO at the end to obtain the original name again!
local groupname=asset.spawngroupname --self:_GetNameWithOut(group)
-- Dont trigger a Remove event for the group sets.
local NoTriggerEvent=true
if request.transporttype==WAREHOUSE.TransportType.SELFPROPELLED then
---
-- Easy case: Group can simply be removed from the cargogroupset.
---
-- Remove dead group from cargo group set.
request.cargogroupset:Remove(groupname, NoTriggerEvent)
self:T(self.lid..string.format("Removed transported cargo %s outside carrier: ncargo=%d", groupname, request.cargogroupset:Count()))
-- This as well?
--request.transportcargoset:RemoveCargosByName(RemoveCargoNames)
else
--self:E(self.lid..string.format("ERROR: Group %s is neither cargo nor transport!", group:GetName()))
end
end
self:T(self.lid..string.format("Removed selfpropelled cargo %s: ncargo=%d.", groupname, request.cargogroupset:Count()))
else
---
-- Complicated case: Dead unit could be:
-- 1.) A Cargo unit (e.g. waiting to be picked up).
-- 2.) A Transport unit which itself holds cargo groups.
---
-- Check if this a cargo or transport group.
local istransport=not asset.iscargo --self:_GroupIsTransport(group, request)
if istransport==true then
-- Whole carrier group is dead. Remove it from the carrier group set.
request.transportgroupset:Remove(groupname, NoTriggerEvent)
self:T(self.lid..string.format("Removed transport %s: ntransport=%d", groupname, request.transportgroupset:Count()))
elseif istransport==false then
-- This must have been an alive cargo group that was killed outside the carrier, e.g. waiting to be transported or waiting to be put back.
-- Remove dead group from cargo group set.
request.cargogroupset:Remove(groupname, NoTriggerEvent)
self:T(self.lid..string.format("Removed transported cargo %s outside carrier: ncargo=%d", groupname, request.cargogroupset:Count()))
-- This as well?
--request.transportcargoset:RemoveCargosByName(RemoveCargoNames)
else
--self:E(self.lid..string.format("ERROR: Group %s is neither cargo nor transport!", group:GetName()))
end
end
else
self:E(self.lid.."ERROR: Asset and/or Request is nil in onafterAssetDead")
end
end

View File

@ -1405,7 +1405,9 @@ function ARMYGROUP:onbeforeEngageTarget(From, Event, To, Target)
end
-- Pause current mission.
if self.currentmission and self.currentmission>0 then
local mission=self:GetMissionCurrent()
if mission and mission.type~=AUFTRAG.Type.GROUNDATTACK then
self:T(self.lid.."Engage command but have current mission ==> Pausing mission!")
self:PauseMission()
dt=-0.1
@ -1533,6 +1535,15 @@ function ARMYGROUP:onafterDisengage(From, Event, To)
self:SwitchROE(self.engage.roe)
self:SwitchAlarmstate(self.engage.alarmstate)
-- Get current task
local task=self:GetTaskCurrent()
-- Get if current task is ground attack.
if task and task.dcstask.id==AUFTRAG.SpecialTask.GROUNDATTACK then
self:T(self.lid.."Disengage with current task GROUNDATTACK ==> Task Done!")
self:TaskDone(task)
end
-- Remove current waypoint
if self.engage.Waypoint then
self:RemoveWaypointByID(self.engage.Waypoint.uid)

View File

@ -385,6 +385,7 @@ _AUFTRAGSNR=0
-- @field #string BARRAGE Barrage.
-- @field #string ARMORATTACK Armor attack.
-- @field #string CASENHANCED Enhanced CAS.
-- @field #string GROUNDATTACK Ground attack.
AUFTRAG.Type={
ANTISHIP="Anti Ship",
AWACS="AWACS",
@ -419,6 +420,7 @@ AUFTRAG.Type={
ARMORATTACK="Armor Attack",
CASENHANCED="CAS Enhanced",
HOVER="Hover",
GROUNDATTACK="Ground Attack"
}
--- Mission status of an assigned group.
@ -431,6 +433,7 @@ AUFTRAG.Type={
-- @field #string ONGUARD On guard.
-- @field #string ARMOREDGUARD On guard with armor.
-- @field #string BARRAGE Barrage.
-- @field #string GROUNDATTACK Ground attack.
AUFTRAG.SpecialTask={
PATROLZONE="PatrolZone",
RECON="ReconMission",
@ -442,6 +445,7 @@ AUFTRAG.SpecialTask={
BARRAGE="Barrage",
ARMORATTACK="AmorAttack",
HOVER="Hover",
GROUNDATTACK="Ground Attack",
}
--- Mission status.
@ -562,7 +566,7 @@ AUFTRAG.Category={
--- AUFTRAG class version.
-- @field #string version
AUFTRAG.version="0.8.5"
AUFTRAG.version="0.9.0"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list
@ -1792,6 +1796,35 @@ function AUFTRAG:NewARMORATTACK(Target, Speed, Formation)
return mission
end
--- **[GROUND]** Create a GROUNDATTACK mission. Ground group(s) will go to a target object and attack.
-- @param #AUFTRAG self
-- @param Wrapper.Positionable#POSITIONABLE Target The target to attack. Can be a GROUP, UNIT or STATIC object.
-- @param #number Speed Speed in knots.
-- @param #string Formation The attack formation, e.g. "Wedge", "Vee" etc.
-- @return #AUFTRAG self
function AUFTRAG:NewGROUNDATTACK(Target, Speed, Formation)
local mission=AUFTRAG:New(AUFTRAG.Type.GROUNDATTACK)
mission:_TargetFromObject(Target)
mission.missionTask=mission:GetMissionTaskforMissionType(AUFTRAG.Type.GROUNDATTACK)
mission.optionROE=ENUMS.ROE.OpenFire
mission.optionAlarm=ENUMS.AlarmState.Auto
mission.optionFormation="On Road"
mission.optionAttackFormation=Formation or "Wedge"
mission.missionFraction=0.75
mission.missionSpeed=Speed and UTILS.KnotsToKmph(Speed) or 20
mission.categories={AUFTRAG.Category.GROUND}
mission.DCStask=mission:GetDCSMissionTask()
return mission
end
--- **[AIR, GROUND, NAVAL]** Create a RECON mission.
-- @param #AUFTRAG self
-- @param Core.Set#SET_ZONE ZoneSet The recon zones.
@ -3315,6 +3348,7 @@ function AUFTRAG:onafterStatus(From, Event, To)
-- Cancel mission if mission targets are gone (if there were any in the beginning).
-- TODO: I commented this out for some reason but I forgot why...
self:T(self.lid.."No targets left cancelling mission!")
self:Cancel()
elseif self:IsExecuting() then
@ -5098,6 +5132,26 @@ function AUFTRAG:GetDCSMissionTask(TaskControllable)
table.insert(DCStasks, DCStask)
elseif self.type==AUFTRAG.Type.GROUNDATTACK then
---------------------------
-- GROUND ATTACK Mission --
---------------------------
local DCStask={}
DCStask.id=AUFTRAG.SpecialTask.GROUNDATTACK
-- We create a "fake" DCS task and pass the parameters to the ARMYGROUP.
local param={}
param.target=self:GetTargetData()
param.action="Wedge"
param.speed=self.missionSpeed
DCStask.params=param
table.insert(DCStasks, DCStask)
elseif self.type==AUFTRAG.Type.AMMOSUPPLY then
-------------------------

View File

@ -100,15 +100,107 @@
-- local text=string.format("Strategy changd to %s", Strategy)
-- MESSAGE:New(text, 120):ToAll()
-- end
--
-- # Resources
--
-- A chief needs resources such as air, ground and naval assets. These can be added in form of AIRWINGs, BRIGADEs and FLEETs.
--
-- Whenever the chief detects a target or receives a mission, he will select the best available assets and assign them to the mission.
-- The best assets are determined by their mission performance, payload performance (in case of air), distance to the target, skill level, etc.
--
-- ## Adding Airwings
--
-- Airwings can be added via the @{#CHIEF.AddAirwing}() function.
--
-- ## Adding Brigades
--
-- Brigades can be added via the @{#CHIEF.AddBrigade}() function.
--
-- ## Adding Fleets
--
-- Fleets are not implemented yet.
--
--
-- # Strategic (Capture) Zones
--
-- Strategically important zones, which should be captured can be added via the @{#CHIEF.AddStrategicZone}() function.
-- Strategically important zones, which should be captured can be added via the @{#CHIEF.AddStrategicZone}(*OpsZone, Prio, Importance*) function.
-- The first parameter *OpsZone* is an @{Ops.OpsZone#OPSZONE} specifying the zone. This has to be a **circular zone** due to DCS API restrictions.
-- The second parameter *Prio* is the priority. The zone queue is sorted wrt to lower prio values. By default this is set to 50.
-- The third parameter *Importance* is the importance of the zone. By default this is `nil`. If you specify one zone with importance 2 and a second zone with
-- importance 3, then the zone of importance 2 is attacked first and only if that zone has been captured, zones that have importances with higher values are attacked.
--
-- If the zone is currently owned by another coalition and enemy ground troops are present in the zone, a CAS and an ARTY mission are lauchned, provided assets are available.
-- For example:
--
-- Once the zone is cleaned of enemy forces, ground (infantry) troops are send there. These require a transportation via helicopters.
-- So in order to deploy our own troops, infantry assets with `AUFTRAG.Type.ONGUARD` and helicopters with `AUFTRAG.Type.OPSTRANSPORT` need to be available.
-- local myStratZone=myChief:AddStrategicZone(myOpsZone, nil , 2)
--
-- Will at a strategic zone with importance 2.
--
-- If the zone is currently owned by another coalition and enemy ground troops are present in the zone, a CAS and an ARTY mission are lauchned:
--
-- * A mission of type `AUFTRAG.Type.CASENHANCED` is started if assets are available that can carry out this mission type.
-- * A mission of type `AUFTRAG.Type.ARTY` is started provided assets are available.
--
-- The CAS flight(s) will patrol the zone randomly and take out enemy ground units they detect. It can always be possible that the enemies cannot be detected however.
-- The assets will shell the zone. However, it is unlikely that they hit anything as they do not have any information about the location of the enemies.
--
-- Once the zone is cleaned of enemy forces, ground troops are send there. By default, two missions are launched:
--
-- * First mission is of type `AUFTRAG.Type.ONGUARD` and will send infantry groups. These are transported by helicopters. Therefore, helo assets with `AUFTRAG.Type.OPSTRANSPORT` need to be available.
-- * The second mission is also of type `AUFTRAG.Type.ONGUARD` but will send tanks if these are available.
--
-- ## Customized Reaction
--
-- The default mission types and number of assets can be customized for the two scenarious (zone empty or zone occupied by the enemy).
--
-- In order to do this, you need to create resource lists (one for each scenario) via the @{#CHIEF.CreateResource}() function.
-- These list can than be used to replace the default resources employed with
--
-- * @{CHIEF.SetStrategicZoneResourceOccupied}(*StrateticZone, ResourceOccupied*) for the case that the zone is occupied by the enemy and
-- * @{CHIEF.SetStrategicZoneResourceEmpty}(*StrateticZone, ResourceEmpty*) for the case that the zone is empty.
--
-- The first parameter *StrateticZone* is the strategic zone object that is returned by the @{#CHIEF.AddStrategicZone}() function.
-- The second parameter is the resource list created with the @{#CHIEF.CreateResource}() function.
--
-- For example:
--
-- -- Create a resource list of mission types and required assets for the case that the zone is occupied.
-- -- Here, we create an enhanced CAS mission and employ at least on and at most two asset groups.
-- local ResourceOccupied=myChief:CreateResource(AUFTRAG.Type.CASENHANCED, 1, 2)
-- -- We also add ARTY missions with at least one and at most two assets. We additionally require these to be MLRS groups (and not howitzers).
-- myChief:AddToResource(ResourceOccupied, AUFTRAG.Type.ARTY, 1, 2, nil, "MLRS")
-- -- Add at least one RECON mission that uses UAV type assets.
-- myChief:AddToResource(ResourceOccupied, AUFTRAG.Type.RECON, 1, nil, GROUP.Attribute.AIR_UAV)
-- -- Add at least one but at most two BOMBCARPET missions.
-- myChief:AddToResource(ResourceOccupied, AUFTRAG.Type.BOMBCARPET, 1, 2)
--
-- -- Replace the default list with the customized one.
-- myChief:SetStrategicZoneResourceOccupied(myStratZone, ResourceOccupied)
--
--
-- -- Create a resource list of mission types and required assets for the case that the zone is empty.
-- -- Here, we create an ONGUARD mission and employ at least on and at most five infantry assets.
-- local ResourceEmpty=myChief:CreateResource(AUFTRAG.Type.ONGUARD, 1, 5, GROUP.Attribute.GROUND_INFANTRY)
-- -- Additionally, we send up to three tank groups.
-- myChief:AddToResource(ResourceEmpty, AUFTRAG.Type.ONGUARD, 1, 3, GROUP.Attribute.GROUND_TANK)
-- -- Finally, we send two groups that patrol the zone.
-- myChief:AddToResource(ResourceEmpty, AUFTRAG.Type.PATROLZONE, 2)
--
-- -- Set this to be the resources employed when the zone is empty.
-- myChief:SetStrategicZoneResourceEmpty(myStratZone, ResourceEmpty)
--
-- As the location of the enemies is not known, only mission types that don't require and explicit target group are possible. These are
--
-- * `AUFTRAG.Type.CASENHANCED`
-- * `AUFTRAG.Type.ARTY`
-- * `AUFTRAG.Type.PATROLZONE`
-- * `AUFTRAG.Type.ONGUARD`
-- * `AUFTRAG.Type.RECON`
-- * `AUFTRAG.Type.AMMOSUPPLY`
-- * `AUFTRAG.Type.BOMBING`
-- * `AUFTRAG.Type.BOMBCARPET`
-- * `AUFTRAG.Type.BARRAGE`
--
-- ## Events
--
-- Whenever a strategic zone is captured by us the FSM event @{#CHIEF.ZoneCaptured} is triggered and customized further actions can be executed
-- with the @{#CHIEF.OnAfterZoneCaptured}() function.
@ -176,16 +268,23 @@ CHIEF.Strategy = {
-- @type CHIEF.StrategicZone
-- @field Ops.OpsZone#OPSZONE opszone OPS zone.
-- @field #number prio Priority.
-- @field #number importance Importance
-- @field Ops.Auftrag#AUFTRAG missionPatrol Patrol mission.
-- @field Ops.Auftrag#AUFTRAG missionCAS CAS mission.
-- @field Ops.Auftrag#AUFTRAG missionPatrol Patrol mission.
-- @field Ops.Auftrag#AUFTRAG missionARTY Artillery mission.
-- @field #number importance Importance.
-- @field #table resourceEmpty Resource list.
-- @field #table resourceOccup Resource list.
-- @field #table missions Mission.
--- Resource.
-- @type CHIEF.Resource
-- @field #string MissionType Mission type, e.g. `AUFTRAG.Type.BAI`.
-- @field #number Nmin Min number of assets.
-- @field #number Nmax Max number of assets.
-- @field #table Attributes Generalized attribute, e.g. `{GROUP.Attribute.GROUND_INFANTRY}`.
-- @field #table Properties Properties ([DCS attributes](https://wiki.hoggitworld.com/view/DCS_enum_attributes)), e.g. `"Attack helicopters"` or `"Mobile AAA"`.
-- @field Ops.Auftrag#AUFTRAG mission Attached mission.
--- CHIEF class version.
-- @field #string version
CHIEF.version="0.2.0"
CHIEF.version="0.3.0"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list
@ -600,6 +699,88 @@ function CHIEF:SetDefcon(Defcon)
return self
end
--- Create a new resource list of required assets.
-- @param #CHIEF self
-- @param #string MissionType The mission type.
-- @param #number Nmin Min number of required assets. Default 1.
-- @param #number Nmax Max number of requried assets. Default 1.
-- @param #table Attributes Generalized attribute(s). Default `nil`.
-- @param #table Properties DCS attribute(s). Default `nil`.
-- @return #table The resource object.
function CHIEF:CreateResource(MissionType, Nmin, Nmax, Attributes, Properties)
local resource={}
self:AddToResource(resource, MissionType, Nmin, Nmax, Attributes, Properties)
return resource
end
--- Add mission type and number of required assets to resource.
-- @param #CHIEF self
-- @param #table Resource Resource table.
-- @param #string MissionType Mission Type.
-- @param #number Nmin Min number of required assets.
-- @param #number Nmax Max number of requried assets.
-- @param #table Attributes Generalized attribute(s).
-- @param #table Properties DCS attribute(s). Default `nil`.
-- @return #CHIEF self
function CHIEF:AddToResource(Resource, MissionType, Nmin, Nmax, Attributes, Properties)
-- Ensure table.
if Attributes and type(Attributes)~="table" then
Attributes={Attributes}
end
-- Ensure table.
if Properties and type(Properties)~="table" then
Properties={Properties}
end
-- Create new resource table.
local resource={} --#CHIEF.Resource
resource.MissionType=MissionType
resource.Nmin=Nmin or 1
resource.Nmax=Nmax or 1
resource.Attributes=Attributes or {}
resource.Properties=Properties or {}
-- Add to table.
table.insert(Resource, resource)
-- Debug output.
if self.verbose>10 then
local text="Resource:"
for _,_r in pairs(Resource) do
local r=_r --#CHIEF.Resource
text=text..string.format("\nmission=%s, Nmin=%d, Nmax=%d, attribute=%s, properties=%s", r.MissionType, r.Nmin, r.Nmax, tostring(r.Attributes[1]), tostring(r.Properties[1]))
end
self:I(self.lid..text)
end
return self
end
--- Delete mission type from resource list. All running missions are cancelled.
-- @param #CHIEF self
-- @param #table Resource Resource table.
-- @param #string MissionType Mission Type.
-- @return #CHIEF self
function CHIEF:DeleteFromResource(Resource, MissionType)
for i=#Resource,1,-1 do
local resource=Resource[i] --#CHIEF.Resource
if resource.MissionType==MissionType then
if resource.mission and resource.mission:IsNotOver() then
resource.mission:Cancel()
end
table.remove(Resource, i)
end
end
return self
end
--- Get defence condition.
-- @param #CHIEF self
-- @param #string Current Defence condition. See @{#CHIEF.DEFCON}, e.g. `CHIEF.DEFCON.RED`.
@ -813,11 +994,22 @@ function CHIEF:RemoveTarget(Target)
end
--- Add strategically important zone.
-- By default two resource lists are created. One for the case that the zone is empty and the other for the case that the zone is occupied
-- Empty:
--
-- * `AUFTRAG.Type.ARTY` with Nmin=1, Nmax=2
-- * `AUFTRAG.Type.CASENHANCED` with Nmin=1, Nmax=2
--
-- Occupied:
--
-- * `AUFTRAG.Type.ONGUARD` with Nmin=1 and Nmax=3 assets, Attribute=`GROUP.Attribute.GROUND_INFANTRY`.
-- * `AUFTRAG.Type.ONGURAD` with Nmin=1 and Nmax=1 assets, Attribute=`GROUP.Attribute.GROUND_TANK`.
--
-- @param #CHIEF self
-- @param Ops.OpsZone#OPSZONE OpsZone OPS zone object.
-- @param #number Priority Priority.
-- @param #number Importance Importance.
-- @return #CHIEF self
-- @return #CHIEF.StrategicZone The strategic zone.
function CHIEF:AddStrategicZone(OpsZone, Priority, Importance)
local stratzone={} --#CHIEF.StrategicZone
@ -825,11 +1017,21 @@ function CHIEF:AddStrategicZone(OpsZone, Priority, Importance)
stratzone.opszone=OpsZone
stratzone.prio=Priority or 50
stratzone.importance=Importance
stratzone.missions={}
-- Start ops zone.
if OpsZone:IsStopped() then
OpsZone:Start()
end
-- Add resources if zone is occupied.
stratzone.resourceOccup=self:CreateResource(AUFTRAG.Type.ARTY, 1, 2)
self:AddToResource(stratzone.resourceOccup, AUFTRAG.Type.CASENHANCED, 1, 2)
-- Add resources if zone is empty
stratzone.resourceEmpty=self:CreateResource(AUFTRAG.Type.ONGUARD, 1, 3, GROUP.Attribute.GROUND_INFANTRY)
self:AddToResource(stratzone.resourceEmpty, AUFTRAG.Type.ONGUARD, 1, 1, GROUP.Attribute.GROUND_TANK)
-- Add to table.
table.insert(self.zonequeue, stratzone)
@ -837,9 +1039,56 @@ function CHIEF:AddStrategicZone(OpsZone, Priority, Importance)
-- Add chief so we get informed when something happens.
OpsZone:_AddChief(self)
return stratzone
end
--- Set the resource list of missions and assets employed when the zone is empty.
-- @param #CHIEF self
-- @param #CHIEF.StrategicZone StrategicZone The strategic zone.
-- @param #CHIEF.Resource Resource Resource list of missions and assets.
-- @param #boolean NoCopy If `true`, do **not** create a deep copy of the resource.
-- @return #CHIEF self
function CHIEF:SetStrategicZoneResourceEmpty(StrategicZone, Resource, NoCopy)
if NoCopy then
StrategicZone.resourceEmpty=Resource
else
StrategicZone.resourceEmpty=UTILS.DeepCopy(Resource)
end
return self
end
--- Set the resource list of missions and assets employed when the zone is occupied by the enemy.
-- @param #CHIEF self
-- @param #CHIEF.StrategicZone StrategicZone The strategic zone.
-- @param #CHIEF.Resource Resource Resource list of missions and assets.
-- @param #boolean NoCopy If `true`, do **not** create a deep copy of the resource.
-- @return #CHIEF self
function CHIEF:SetStrategicZoneResourceOccupied(StrategicZone, Resource, NoCopy)
if NoCopy then
StrategicZone.resourceOccup=Resource
else
StrategicZone.resourceOccup=UTILS.DeepCopy(Resource)
end
return self
end
--- Get the resource list of missions and assets employed when the zone is empty.
-- @param #CHIEF self
-- @param #CHIEF.StrategicZone StrategicZone The strategic zone.
-- @return #CHIEF.Resource Resource list of missions and assets.
function CHIEF:GetStrategicZoneResourceEmpty(StrategicZone)
return StrategicZone.resourceEmpty
end
--- Get the resource list of missions and assets employed when the zone is occupied by the enemy.
-- @param #CHIEF self
-- @param #CHIEF.StrategicZone StrategicZone The strategic zone.
-- @return #CHIEF.Resource Resource list of missions and assets.
function CHIEF:GetStrategicZoneResourceOccupied(StrategicZone)
return StrategicZone.resourceOccup
end
--- Remove strategically important zone. All runing missions are cancelled.
-- @param #CHIEF self
-- @param Ops.OpsZone#OPSZONE OpsZone OPS zone object.
@ -860,15 +1109,23 @@ function CHIEF:RemoveStrategicZone(OpsZone, Delay)
-- Debug info.
self:T(self.lid..string.format("Removing OPS zone \"%s\" from queue! All running missions will be cancelled", OpsZone.zoneName))
-- Cancel all running missions.
for _,_entry in pairs(OpsZone.Missions or {}) do
local entry = _entry -- Ops.OpsZone#OPSZONE.MISSION
if entry.Coalition==self.coalition and entry.Mission and entry.Mission:IsNotOver() then
entry.Mission:Cancel()
-- Cancel running missions.
for _,_resource in pairs(stratzone.resourceEmpty) do
local resource=_resource --#CHIEF.Resource
if resource.mission and resource.mission:IsNotOver() then
resource.mission:Cancel()
end
end
-- Cancel running missions.
for _,_resource in pairs(stratzone.resourceOccup) do
local resource=_resource --#CHIEF.Resource
if resource.mission and resource.mission:IsNotOver() then
resource.mission:Cancel()
end
end
-- Remove from table.
table.remove(self.zonequeue, i)
@ -1566,13 +1823,14 @@ function CHIEF:_TacticalOverview()
local NmissionsRunni=self.commander:CountMissions(AUFTRAG.Type, true)
local Ntargets=#self.targetqueue
local Nzones=#self.zonequeue
local Nagents=self.detectionset:CountAlive()
-- Info message
local text=string.format("Tactical Overview\n")
text=text..string.format("=================\n")
-- Strategy and defcon info.
text=text..string.format("Strategy: %s - Defcon: %s\n", self.strategy, self.Defcon)
text=text..string.format("Strategy: %s - Defcon: %s - Agents=%s\n", self.strategy, self.Defcon, Nagents)
-- Contact info.
text=text..string.format("Contacts: %d [Border=%d, Conflict=%d, Attack=%d]\n", Ncontacts, self.Nborder, self.Nconflict, self.Nattack)
@ -1866,11 +2124,6 @@ end
-- @param #CHIEF self
function CHIEF:CheckOpsZoneQueue()
-- Passive strategy ==> Do not act.
if self:IsPassive() then
return
end
-- Number of zones.
local Nzones=#self.zonequeue
@ -1878,10 +2131,45 @@ function CHIEF:CheckOpsZoneQueue()
if Nzones==0 then
return nil
end
-- Loop over strategic zone and remove stopped zones.
for i=Nzones, 1, -1 do
local stratzone=self.zonequeue[i] --#CHIEF.StrategicZone
if stratzone.opszone:IsStopped() then
self:RemoveStrategicZone(stratzone.opszone)
end
end
-- Loop over strategic zones and cancel missions for occupied zones if zone is not occupied any more.
for _,_startzone in pairs(self.zonequeue) do
local stratzone=_startzone --#CHIEF.StrategicZone
-- Current owner of the zone.
local ownercoalition=stratzone.opszone:GetOwner()
-- Check if we own the zone or it is empty.
if ownercoalition==self.coalition or stratzone.opszone:IsEmpty() then
-- Loop over resources.
for _,_resource in pairs(stratzone.resourceOccup or {}) do
local resource=_resource --#CHIEF.Resource
-- Cancel running missions.
if resource.mission then
resource.mission:Cancel()
end
end
end
end
-- Passive strategy ==> Do not act.
if self:IsPassive() then
return
end
-- Check if total number of missions is reached.
local NoLimit=self:_CheckMissionLimit("Total")
--env.info("FF chief zone total nolimit="..tostring(NoLimit))
if NoLimit==false then
return nil
end
@ -1911,19 +2199,15 @@ function CHIEF:CheckOpsZoneQueue()
-- Current owner of the zone.
local ownercoalition=stratzone.opszone:GetOwner()
-- Name of the zone.
local zoneName=stratzone.opszone.zone:GetName()
-- Check coalition and importance.
if ownercoalition~=self.coalition and (stratzone.importance==nil or stratzone.importance<=vip) and (not stratzone.opszone:IsStopped()) then
-- Has a patrol mission?
local hasMissionPatrol=stratzone.opszone:_FindMissions(self.coalition,AUFTRAG.Type.ONGUARD) or stratzone.opszone:_FindMissions(self.coalition,AUFTRAG.Type.ARMOREDGUARD)
-- Has a CAS mission?
local hasMissionCAS=stratzone.opszone:_FindMissions(self.coalition,AUFTRAG.Type.CASENHANCED)
-- Has a ARTY mission?
local hasMissionARTY=stratzone.opszone:_FindMissions(self.coalition,AUFTRAG.Type.ARTY)
-- Debug info.
self:T(self.lid..string.format("Zone %s [%s] is owned by coalition %d", stratzone.opszone.zone:GetName(), stratzone.opszone:GetState(), ownercoalition))
self:T(self.lid..string.format("Zone %s [%s] is owned by coalition %d", zoneName, stratzone.opszone:GetState(), ownercoalition))
if stratzone.opszone:IsEmpty() then
@ -1932,19 +2216,29 @@ function CHIEF:CheckOpsZoneQueue()
--
-- We send ground troops to capture the zone.
---
if not hasMissionPatrol then
-- Debug info.
self:T3(self.lid..string.format("Zone is empty ==> Recruit Patrol zone infantry assets"))
-- Recruit ground assets that
local recruitedI=self:RecruitAssetsForZone(stratzone, AUFTRAG.Type.ONGUARD, 1, 3, {Group.Category.GROUND}, {GROUP.Attribute.GROUND_INFANTRY})
local recruitedT=self:RecruitAssetsForZone(stratzone, AUFTRAG.Type.ARMOREDGUARD, 1, 1, {Group.Category.GROUND}, {GROUP.Attribute.GROUND_TANK})
for _,_resource in pairs(stratzone.resourceEmpty or {}) do
local resource=_resource --#CHIEF.Resource
-- Debug info.
self:T(self.lid..string.format("Zone is empty ==> Recruit Patrol zone infantry assets=%s", tostring(recruitedI)))
self:T(self.lid..string.format("Zone is empty ==> Recruit Patrol zone armored assets=%s", tostring(recruitedT)))
-- Mission type.
local missionType=resource.MissionType
if (not resource.mission) or resource.mission:IsOver() then
-- Debug info.
self:T2(self.lid..string.format("Zone \"%s\" is empty ==> Recruiting for mission type %s: Nmin=%d, Nmax=%d", zoneName, missionType, resource.Nmin, resource.Nmax))
-- Recruit assets.
local recruited=self:RecruitAssetsForZone(stratzone, resource)
if recruited then
self:T(self.lid..string.format("Successfully recruited assets for empty zone \"%s\" [mission type=%s]", zoneName, missionType))
else
self:T(self.lid..string.format("Could not recruited assets for empty zone \"%s\" [mission type=%s]", zoneName, missionType))
end
end
end
else
@ -1954,79 +2248,35 @@ function CHIEF:CheckOpsZoneQueue()
--
-- We first send a CAS flight to eliminate enemy activity.
---
if not hasMissionCAS then
-- Debug message.
self:T3(self.lid..string.format("Zone is NOT empty ==> Recruit CAS assets"))
for _,_resource in pairs(stratzone.resourceOccup or {}) do
local resource=_resource --#CHIEF.Resource
-- Mission type.
local missionType=resource.MissionType
if (not resource.mission) or resource.mission:IsOver() then
-- Debug info.
self:T2(self.lid..string.format("Zone %s is NOT empty ==> Recruiting for mission type %s: Nmin=%d, Nmax=%d", zoneName, missionType, resource.Nmin, resource.Nmax))
-- Recruit assets.
local recruited=self:RecruitAssetsForZone(stratzone, resource)
-- Recruite CAS assets.
local recruited=self:RecruitAssetsForZone(stratzone, AUFTRAG.Type.CASENHANCED, 1, 1)
-- Debug message.
self:T(self.lid..string.format("Zone is NOT empty ==> Recruit CAS assets=%s", tostring(recruited)))
end
if recruited then
self:T(self.lid..string.format("Successfully recruited assets for occupied zone %s, mission type=%s", zoneName, missionType))
else
self:T(self.lid..string.format("Could not recruited assets for occupied zone %s, mission type=%s", zoneName, missionType))
end
end
if not hasMissionARTY then
-- Debug message.
self:T3(self.lid..string.format("Zone is NOT empty ==> Recruit ARTY assets"))
-- Recruite CAS assets.
local recruited=self:RecruitAssetsForZone(stratzone, AUFTRAG.Type.ARTY, 1, 1)
-- Debug message.
self:T(self.lid..string.format("Zone is NOT empty ==> Recruit ARTY assets=%s", tostring(recruited)))
end
end
end
end
-- Loop over strategic zone.
for _,_startzone in pairs(self.zonequeue) do
local stratzone=_startzone --#CHIEF.StrategicZone
-- Current owner of the zone.
local ownercoalition=stratzone.opszone:GetOwner()
-- Has a patrol mission?
local hasMissionPATROL=stratzone.opszone:_FindMissions(self.coalition,AUFTRAG.Type.PATROLZONE)
-- Has a CAS mission?
local hasMissionCAS, CASMissions = stratzone.opszone:_FindMissions(self.coalition,AUFTRAG.Type.CASENHANCED)
local hasMissionARTY, ARTYMissions = stratzone.opszone:_FindMissions(self.coalition,AUFTRAG.Type.ARTY)
if ownercoalition==self.coalition and stratzone.opszone:IsEmpty() and hasMissionCAS then
-- Cancel CAS mission if zone is ours and no enemies are present.
-- TODO: Might want to check if we still have CAS capable assets in stock?!
--stratzone.missionCAS:Cancel()
for _,_auftrag in pairs(CASMissions) do
_auftrag:Cancel()
end
end
if ownercoalition==self.coalition and hasMissionARTY then
-- Cancel ARTY mission if zone is ours and no enemies are present.
for _,_auftrag in pairs(ARTYMissions) do
_auftrag:Cancel()
end
end
end
-- Loop over strategic zone and remove stopped zones.
for i=#self.zonequeue, 1, -1 do
local stratzone=self.zonequeue[i] --#CHIEF.StrategicZone
if stratzone.opszone:IsStopped() then
self:RemoveStrategicZone(stratzone.opszone)
end
end
end
@ -2360,13 +2610,9 @@ end
--- Recruit assets for a given OPS zone.
-- @param #CHIEF self
-- @param #CHIEF.StrategicZone StratZone The strategic zone.
-- @param #string MissionType Mission Type.
-- @param #number NassetsMin Min number of required assets.
-- @param #number NassetsMax Max number of required assets.
-- @param #table Categories Group categories of the assets.
-- @param #table Attributes Generalized group attributes.
-- @param #CHIEF.Resource Resource The required resources.
-- @return #boolean If `true` enough assets could be recruited.
function CHIEF:RecruitAssetsForZone(StratZone, MissionType, NassetsMin, NassetsMax, Categories, Attributes)
function CHIEF:RecruitAssetsForZone(StratZone, Resource)
-- Cohorts.
local Cohorts={}
@ -2385,7 +2631,15 @@ function CHIEF:RecruitAssetsForZone(StratZone, MissionType, NassetsMin, NassetsM
end
end
end
end
-- Shortcuts.
local MissionType=Resource.MissionType
local NassetsMin=Resource.Nmax
local NassetsMax=Resource.Nmax
local Categories=Resource.Categories
local Attributes=Resource.Attributes
local Properties=Resource.Properties
-- Target position.
local TargetVec2=StratZone.opszone.zone:GetVec2()
@ -2398,26 +2652,36 @@ function CHIEF:RecruitAssetsForZone(StratZone, MissionType, NassetsMin, NassetsM
RangeMax=UTILS.NMToMeters(250)
end
-- Set max range to 50 NM because we use armor
-- Set max range to 50 NM because we use armor.
if MissionType==AUFTRAG.Type.ARMOREDGUARD then
RangeMax=UTILS.NMToMeters(50)
end
-- Recruite infantry assets.
local recruited, assets, legions=LEGION.RecruitCohortAssets(Cohorts, MissionType, nil, NassetsMin, NassetsMax, TargetVec2, nil, RangeMax, nil, nil, nil, Categories, Attributes)
local recruited, assets, legions=LEGION.RecruitCohortAssets(Cohorts, MissionType, nil, NassetsMin, NassetsMax, TargetVec2, nil, RangeMax, nil, nil, nil, Categories, Attributes, Properties)
if recruited then
-- Mission for zone.
local mission=nil --Ops.Auftrag#AUFTRAG
-- Debug messgage.
self:T2(self.lid..string.format("Recruited %d assets for %s mission STRATEGIC zone %s", #assets, MissionType, tostring(StratZone.opszone.zoneName)))
self:T2(self.lid..string.format("Recruited %d assets for %s mission STRATEGIC zone %s", #assets, MissionType, tostring(StratZone.opszone.zoneName)))
local TargetZone = StratZone.opszone.zone
local TargetCoord = TargetZone:GetCoordinate()
if MissionType==AUFTRAG.Type.PATROLZONE or MissionType==AUFTRAG.Type.ONGUARD then
---
-- PATROLZONE or ONGUARD
---
-- Debug messgage.
self:T2(self.lid..string.format("Recruited %d assets for PATROL mission", #assets))
local recruitedTrans=true
local transport=nil
-- First check if we need a transportation.
local recruitedTrans=true ; local transport=nil
if Attributes and Attributes[1]==GROUP.Attribute.GROUND_INFANTRY then
-- Categories. Currently only helicopters are allowed due to problems with ground transports (might get stuck, might not be a land connection.
@ -2425,55 +2689,66 @@ function CHIEF:RecruitAssetsForZone(StratZone, MissionType, NassetsMin, NassetsM
local Categories=self.TransportCategories
-- Recruit transport assets for infantry.
recruitedTrans, transport=LEGION.AssignAssetsForTransport(self.commander, self.commander.legions, assets, 1, 1, StratZone.opszone.zone, nil, Categories)
recruitedTrans, transport=LEGION.AssignAssetsForTransport(self.commander, self.commander.legions, assets, 1, 1, TargetZone, nil, Categories)
end
if recruitedTrans then
-- Create Patrol zone mission.
local mission=nil --Ops.Auftrag#AUFTRAG
if MissionType==AUFTRAG.Type.PATROLZONE then
mission=AUFTRAG:NewPATROLZONE(StratZone.opszone.zone)
mission:SetEngageDetected(25, {"Ground Units", "Light armed ships", "Helicopters"})
mission=AUFTRAG:NewPATROLZONE(TargetZone)
elseif MissionType==AUFTRAG.Type.ONGUARD then
mission=AUFTRAG:NewONGUARD(StratZone.opszone.zone:GetRandomCoordinate(), nil, nil, {land.SurfaceType.LAND})
mission=AUFTRAG:NewONGUARD(TargetZone:GetRandomCoordinate(nil, nil, {land.SurfaceType.LAND}))
end
mission:SetEngageDetected()
-- Add assets to mission.
for _,asset in pairs(assets) do
mission:AddAsset(asset)
end
-- Engage detected targets.
mission:SetEngageDetected(25, {"Ground Units", "Light armed ships", "Helicopters"})
-- Attach OPS transport to mission.
mission.opstransport=transport
-- Assign mission to legions.
self:MissionAssign(mission, legions)
-- Attach mission to ops zone.
StratZone.opszone:_AddMission(self.coalition, MissionType, mission)
-- Set ops zone to transport.
transport.opszone=StratZone.opszone
transport.chief=self
transport.commander=self.commander
return true
if transport then
transport.opszone=StratZone.opszone
transport.chief=self
transport.commander=self.commander
end
else
-- No transport ==> no mission!
self:T(self.lid..string.format("Could not allocate transport of OPSZONE infantry!"))
LEGION.UnRecruitAssets(assets)
return false
end
elseif MissionType==AUFTRAG.Type.CASENHANCED then
---
-- CAS ENHANCED
---
-- Create Patrol zone mission.
local caszone = StratZone.opszone.zone
local coord = caszone:GetCoordinate()
local height = UTILS.MetersToFeet(coord:GetLandHeight())+2500
local height = UTILS.MetersToFeet(TargetCoord:GetLandHeight())+2500
local Speed=200
if assets[1] then
if assets[1].speedmax then
Speed = UTILS.KmphToKnots(assets[1].speedmax * 0.7) or 200
end
end
-- CAS mission.
mission=AUFTRAG:NewCASENHANCED(TargetZone, height, Speed)
elseif MissionType==AUFTRAG.Type.CAS then
---
-- CAS
---
-- Create Patrol zone mission.
local height = UTILS.MetersToFeet(TargetCoord:GetLandHeight())+2500
local Speed = 200
if assets[1] then
@ -2481,82 +2756,80 @@ function CHIEF:RecruitAssetsForZone(StratZone, MissionType, NassetsMin, NassetsM
Speed = UTILS.KmphToKnots(assets[1].speedmax * 0.7) or 200
end
end
--local mission=AUFTRAG:NewCAS(caszone,height,Speed,coord,math.random(0,359),Leg)
local mission=AUFTRAG:NewCASENHANCED(caszone, height, Speed)
-- Add assets to mission.
for _,asset in pairs(assets) do
mission:AddAsset(asset)
end
-- Assign mission to legions.
self:MissionAssign(mission, legions)
-- Attach mission to ops zone.
StratZone.opszone:_AddMission(self.coalition, MissionType, mission)
return true
elseif MissionType==AUFTRAG.Type.CAS then
-- Create Patrol zone mission.
local caszone = StratZone.opszone.zone
local coord = caszone:GetCoordinate()
local height = UTILS.MetersToFeet(coord:GetLandHeight())+2500
local Speed = 200
--local mission=AUFTRAG:NewPATROLZONE(caszone)
if assets[1] then
if assets[1].speedmax then
Speed = UTILS.KmphToKnots(assets[1].speedmax * 0.7) or 200
end
end
--local Speed = UTILS.KmphToKnots(assets[1].speedmax * 0.7) or 200
local Leg = caszone:GetRadius() <= 10000 and 5 or UTILS.MetersToNM(caszone:GetRadius())
local mission=AUFTRAG:NewCAS(caszone,height,Speed,coord,math.random(0,359),Leg)
mission:SetEngageDetected(25, {"Ground Units", "Light armed ships", "Helicopters"})
mission:SetWeaponExpend(AI.Task.WeaponExpend.ALL)
mission:SetMissionSpeed(Speed)
-- Leg length.
local Leg = TargetZone:GetRadius() <= 10000 and 5 or UTILS.MetersToNM(TargetZone:GetRadius())
-- Add assets to mission.
for _,asset in pairs(assets) do
mission:AddAsset(asset)
end
-- Assign mission to legions.
self:MissionAssign(mission, legions)
-- Attach mission to ops zone.
StratZone.opszone:_AddMission(self.coalition, MissionType, mission)
-- CAS mission.
mission=AUFTRAG:NewCAS(TargetZone, height, Speed, TargetCoord, math.random(0,359), Leg)
return true
elseif MissionType==AUFTRAG.Type.ARTY then
---
-- ARTY
---
-- Create ARTY zone mission.
local TargetZone = StratZone.opszone.zone
local Target = TargetZone:GetCoordinate()
local Radius = TargetZone:GetRadius()
local mission=AUFTRAG:NewARTY(Target,120,Radius)
-- Add assets to mission.
for _,asset in pairs(assets) do
mission:AddAsset(asset)
end
-- Assign mission to legions.
self:MissionAssign(mission, legions)
-- Attach mission to ops zone.
StratZone.opszone:_AddMission(self.coalition, MissionType, mission)
return true
mission=AUFTRAG:NewARTY(TargetCoord, 120, Radius)
elseif MissionType==AUFTRAG.Type.ARMOREDGUARD then
---
-- ARMORGUARD
---
-- Create Armored on guard mission
local TargetZone = StratZone.opszone.zone
local Target = TargetZone:GetCoordinate()
local mission=AUFTRAG:NewARMOREDGUARD(Target)
mission=AUFTRAG:NewARMOREDGUARD(TargetCoord)
elseif MissionType==AUFTRAG.Type.BOMBCARPET then
---
-- BOMB CARPET
---
-- Create ARTY zone mission.
mission=AUFTRAG:NewBOMBCARPET(TargetCoord, nil, 1000)
elseif MissionType==AUFTRAG.Type.BOMBING then
---
-- BOMBING
---
local coord=TargetZone:GetRandomCoordinate()
mission=AUFTRAG:NewBOMBING(TargetCoord)
elseif MissionType==AUFTRAG.Type.RECON then
---
-- RECON
---
mission=AUFTRAG:NewRECON(TargetZone, nil, 5000)
elseif MissionType==AUFTRAG.Type.BARRAGE then
---
-- BARRAGE
---
mission=AUFTRAG:NewBARRAGE(TargetZone)
elseif MissionType==AUFTRAG.Type.AMMOSUPPLY then
---
-- AMMO SUPPLY
---
mission=AUFTRAG:NewAMMOSUPPLY(TargetZone)
end
if mission then
-- Add assets to mission.
for _,asset in pairs(assets) do
mission:AddAsset(asset)
@ -2567,8 +2840,18 @@ function CHIEF:RecruitAssetsForZone(StratZone, MissionType, NassetsMin, NassetsM
-- Attach mission to ops zone.
StratZone.opszone:_AddMission(self.coalition, MissionType, mission)
-- Attach mission to resource.
Resource.mission=mission
return true
return true
else
-- Mission not supported.
self:E(self.lid..string.format("ERROR: Mission type not supported for OPSZONE! Unrecruiting assets..."))
LEGION.UnRecruitAssets(assets)
return false
end
end

View File

@ -35,6 +35,8 @@
-- @field #number Nkilled Number of destroyed asset groups.
-- @field #number engageRange Mission range in meters.
-- @field #string attribute Generalized attribute of the cohort template group.
-- @field #table descriptors DCS descriptors.
-- @field #table properties DCS attributes.
-- @field #table tacanChannel List of TACAN channels available to the cohort.
-- @field #number radioFreq Radio frequency in MHz the cohort uses.
-- @field #number radioModu Radio modulation the cohort uses.
@ -73,17 +75,20 @@ COHORT = {
tacanChannel = {},
weightAsset = 99999,
cargobayLimit = 0,
descriptors = {},
properties = {},
}
--- COHORT class version.
-- @field #string version
COHORT.version="0.1.0"
COHORT.version="0.2.0"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO: Create FLOTILLA class.
-- DONE: Added check for properties.
-- DONE: Make general so that PLATOON and SQUADRON can inherit this class.
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@ -127,6 +132,15 @@ function COHORT:New(TemplateGroupName, Ngroups, CohortName)
-- Aircraft type.
self.aircrafttype=self.templategroup:GetTypeName()
-- Get descriptors.
self.descriptors=self.templategroup:GetUnit(1):GetDesc()
-- Properties (DCS attributes).
self.properties=self.descriptors.attributes
-- Print properties.
--self:I(self.properties)
-- Defaults.
self.Ngroups=Ngroups or 3
@ -321,6 +335,21 @@ function COHORT:AddMissionCapability(MissionTypes, Performance)
return self
end
--- Check if cohort assets have a given property (DCS attribute).
-- @param #COHORT self
-- @param #string Property The property.
-- @return #boolean If `true`, cohort assets have the attribute.
function COHORT:HasProperty(Property)
for _,property in pairs(self.properties) do
if Property==property then
return true
end
end
return false
end
--- Get mission types this cohort is able to perform.
-- @param #COHORT self
-- @return #table Table of mission types. Could be empty {}.

View File

@ -166,6 +166,20 @@ FLIGHTGROUP.Attribute = {
OTHER="Other",
}
--- Radio Text.
-- @type FLIGHTGROUP.RadioText
-- @field #string normal
-- @field #string enhanced
--- Radio messages.
-- @type FLIGHTGROUP.RadioMessage
-- @field #FLIGHTGROUP.RadioText AIRBORNE
-- @field #FLIGHTGROUP.RadioText TAXIING
FLIGHTGROUP.RadioMessage = {
AIRBORNE={normal="Airborn", enhanced="Airborn"},
TAXIING={normal="Taxiing", enhanced="Taxiing"},
}
--- FLIGHTGROUP class version.
-- @field #string version
FLIGHTGROUP.version="0.7.1"

View File

@ -1964,10 +1964,11 @@ end
-- @param #number TotalWeight Total cargo weight in kg.
-- @param #table Categories Group categories.
-- @param #table Attributes Group attributes. See `GROUP.Attribute.`
-- @param #table Properties DCS attributes.
-- @return #boolean If `true` enough assets could be recruited.
-- @return #table Recruited assets. **NOTE** that we set the `asset.isReserved=true` flag so it cant be recruited by anyone else.
-- @return #table Legions of recruited assets.
function LEGION.RecruitCohortAssets(Cohorts, MissionTypeRecruit, MissionTypeOpt, NreqMin, NreqMax, TargetVec2, Payloads, RangeMax, RefuelSystem, CargoWeight, TotalWeight, Categories, Attributes)
function LEGION.RecruitCohortAssets(Cohorts, MissionTypeRecruit, MissionTypeOpt, NreqMin, NreqMax, TargetVec2, Payloads, RangeMax, RefuelSystem, CargoWeight, TotalWeight, Categories, Attributes, Properties)
-- The recruited assets.
local Assets={}
@ -2006,7 +2007,26 @@ function LEGION.RecruitCohortAssets(Cohorts, MissionTypeRecruit, MissionTypeOpt,
else
return true
end
end
end
--- Function to check property.
local function CheckProperty(_cohort)
local cohort=_cohort --Ops.Cohort#COHORT
if Properties and #Properties>0 then
for _,Property in pairs(Properties) do
for _,property in pairs(cohort.properties) do
if Property==property then
return true
end
end
end
else
return true
end
end
--BASE:I({Attributes=Attributes})
--BASE:I({Properties=Properties})
-- Loops over cohorts.
for _,_cohort in pairs(Cohorts) do
@ -2045,12 +2065,15 @@ function LEGION.RecruitCohortAssets(Cohorts, MissionTypeRecruit, MissionTypeOpt,
-- Right attribute.
local RightAttribute=CheckAttribute(cohort)
-- Right property (DCS attribute).
local RightProperty=CheckProperty(cohort)
-- Debug info.
cohort:T(cohort.lid..string.format("State=%s: Capable=%s, InRange=%s, Refuel=%s, CanCarry=%s, RightCategory=%s, RightAttribute=%s",
cohort:GetState(), tostring(Capable), tostring(InRange), tostring(Refuel), tostring(CanCarry), tostring(RightCategory), tostring(RightAttribute)))
cohort:T2(cohort.lid..string.format("State=%s: Capable=%s, InRange=%s, Refuel=%s, CanCarry=%s, RightCategory=%s, RightAttribute=%s, RightProperty=%s",
cohort:GetState(), tostring(Capable), tostring(InRange), tostring(Refuel), tostring(CanCarry), tostring(RightCategory), tostring(RightAttribute), tostring(RightProperty)))
-- Check OnDuty, capable, in range and refueling type (if TANKER).
if cohort:IsOnDuty() and Capable and InRange and Refuel and CanCarry and RightCategory and RightAttribute then
if cohort:IsOnDuty() and Capable and InRange and Refuel and CanCarry and RightCategory and RightAttribute and RightProperty then
-- Recruit assets from cohort.
local assets, npayloads=cohort:RecruitAssets(MissionTypeRecruit, 999)
@ -2100,7 +2123,7 @@ function LEGION.RecruitCohortAssets(Cohorts, MissionTypeRecruit, MissionTypeOpt,
---
-- Found enough assets
---
-- Add assets to mission.
local cargobay=0
for i=1,Nassets do

View File

@ -3807,12 +3807,26 @@ function OPSGROUP:onafterTaskExecute(From, Event, To, Task)
else
-- FLIGHTGROUP not implemented (intended!) for this AUFTRAG type.
end
---
elseif Task.dcstask.id==AUFTRAG.SpecialTask.GROUNDATTACK then
---
-- Task "Ground Attack" Mission.
---
-- Engage target.
local target=Task.dcstask.params.target --Ops.Target#TARGET
if target then
self:EngageTarget(target)
end
elseif Task.dcstask.id==AUFTRAG.SpecialTask.HOVER then
---
-- Task "Hover" Mission.
---
elseif Task.dcstask.id==AUFTRAG.SpecialTask.HOVER then
if self.isFlightgroup then
self:T("We are Special Auftrag HOVER, hovering now ...")
--self:I({Task.dcstask.params})
@ -3836,6 +3850,7 @@ function OPSGROUP:onafterTaskExecute(From, Event, To, Task)
local timer = TIMER:New(FlyOn,helo,Speed,CruiseAlt,Task)
timer:Start(time)
end
else
-- If task is scheduled (not waypoint) set task.
@ -3952,6 +3967,8 @@ function OPSGROUP:onafterTaskCancel(From, Event, To, Task)
done=true
elseif Task.dcstask.id==AUFTRAG.SpecialTask.ONGUARD or Task.dcstask.id==AUFTRAG.SpecialTask.ARMOREDGUARD then
done=true
elseif Task.dcstask.id==AUFTRAG.SpecialTask.GROUNDATTACK then
done=true
elseif stopflag==1 or (not self:IsAlive()) or self:IsDead() or self:IsStopped() then
-- Manual call TaskDone if setting flag to one was not successful.
done=true
@ -4714,8 +4731,7 @@ function OPSGROUP:RouteToMission(mission, delay)
end
-- Get ingress waypoint.
if mission.type==AUFTRAG.Type.PATROLZONE or mission.type==AUFTRAG.Type.BARRAGE or mission.type==AUFTRAG.Type.AMMOSUPPLY
or mission.type.FUELSUPPLY then
if mission.type==AUFTRAG.Type.PATROLZONE or mission.type==AUFTRAG.Type.BARRAGE or mission.type==AUFTRAG.Type.AMMOSUPPLY or mission.type.FUELSUPPLY then
local zone=mission.engageTarget:GetObject() --Core.Zone#ZONE
waypointcoord=zone:GetRandomCoordinate(nil , nil, surfacetypes)
elseif mission.type==AUFTRAG.Type.ONGUARD or mission.type==AUFTRAG.Type.ARMOREDGUARD then

View File

@ -587,14 +587,6 @@ function OPSZONE:onafterStop(From, Event, To)
-- Unhandle events.
self:UnHandleEvent(EVENTS.BaseCaptured)
-- Cancel all running missions.
for _,_entry in pairs(self.Missions or {}) do
local entry = _entry -- Ops.OpsZone#OPSZONE.MISSION
if entry.Mission and entry.Mission:IsNotOver() then
entry.Mission:Cancel()
end
end
-- Stop FSM scheduler.
self.CallScheduler:Clear()