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. -- @param #WAREHOUSE.Pendingitem request The request of the dead asset.
function WAREHOUSE:onafterAssetDead(From, Event, To, asset, request) function WAREHOUSE:onafterAssetDead(From, Event, To, asset, request)
-- Debug message. if asset and request then
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! -- Debug message.
local groupname=asset.spawngroupname --self:_GetNameWithOut(group) local text=string.format("Asset %s from request id=%d is dead!", asset.templatename, request.uid)
self:T(self.lid..text)
-- Dont trigger a Remove event for the group sets.
local NoTriggerEvent=true -- 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)
if request.transporttype==WAREHOUSE.TransportType.SELFPROPELLED then
-- Dont trigger a Remove event for the group sets.
--- local NoTriggerEvent=true
-- Easy case: Group can simply be removed from the cargogroupset.
--- if request.transporttype==WAREHOUSE.TransportType.SELFPROPELLED then
-- Remove dead group from cargo group set. ---
request.cargogroupset:Remove(groupname, NoTriggerEvent) -- Easy case: Group can simply be removed from the cargogroupset.
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. -- Remove dead group from cargo group set.
request.cargogroupset:Remove(groupname, NoTriggerEvent) request.cargogroupset:Remove(groupname, NoTriggerEvent)
self:T(self.lid..string.format("Removed transported cargo %s outside carrier: ncargo=%d", groupname, request.cargogroupset:Count())) self:T(self.lid..string.format("Removed selfpropelled cargo %s: 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
---
-- 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 end

View File

@ -1405,7 +1405,9 @@ function ARMYGROUP:onbeforeEngageTarget(From, Event, To, Target)
end end
-- Pause current mission. -- 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:T(self.lid.."Engage command but have current mission ==> Pausing mission!")
self:PauseMission() self:PauseMission()
dt=-0.1 dt=-0.1
@ -1533,6 +1535,15 @@ function ARMYGROUP:onafterDisengage(From, Event, To)
self:SwitchROE(self.engage.roe) self:SwitchROE(self.engage.roe)
self:SwitchAlarmstate(self.engage.alarmstate) 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 -- Remove current waypoint
if self.engage.Waypoint then if self.engage.Waypoint then
self:RemoveWaypointByID(self.engage.Waypoint.uid) self:RemoveWaypointByID(self.engage.Waypoint.uid)

View File

@ -385,6 +385,7 @@ _AUFTRAGSNR=0
-- @field #string BARRAGE Barrage. -- @field #string BARRAGE Barrage.
-- @field #string ARMORATTACK Armor attack. -- @field #string ARMORATTACK Armor attack.
-- @field #string CASENHANCED Enhanced CAS. -- @field #string CASENHANCED Enhanced CAS.
-- @field #string GROUNDATTACK Ground attack.
AUFTRAG.Type={ AUFTRAG.Type={
ANTISHIP="Anti Ship", ANTISHIP="Anti Ship",
AWACS="AWACS", AWACS="AWACS",
@ -419,6 +420,7 @@ AUFTRAG.Type={
ARMORATTACK="Armor Attack", ARMORATTACK="Armor Attack",
CASENHANCED="CAS Enhanced", CASENHANCED="CAS Enhanced",
HOVER="Hover", HOVER="Hover",
GROUNDATTACK="Ground Attack"
} }
--- Mission status of an assigned group. --- Mission status of an assigned group.
@ -431,6 +433,7 @@ AUFTRAG.Type={
-- @field #string ONGUARD On guard. -- @field #string ONGUARD On guard.
-- @field #string ARMOREDGUARD On guard with armor. -- @field #string ARMOREDGUARD On guard with armor.
-- @field #string BARRAGE Barrage. -- @field #string BARRAGE Barrage.
-- @field #string GROUNDATTACK Ground attack.
AUFTRAG.SpecialTask={ AUFTRAG.SpecialTask={
PATROLZONE="PatrolZone", PATROLZONE="PatrolZone",
RECON="ReconMission", RECON="ReconMission",
@ -442,6 +445,7 @@ AUFTRAG.SpecialTask={
BARRAGE="Barrage", BARRAGE="Barrage",
ARMORATTACK="AmorAttack", ARMORATTACK="AmorAttack",
HOVER="Hover", HOVER="Hover",
GROUNDATTACK="Ground Attack",
} }
--- Mission status. --- Mission status.
@ -562,7 +566,7 @@ AUFTRAG.Category={
--- AUFTRAG class version. --- AUFTRAG class version.
-- @field #string version -- @field #string version
AUFTRAG.version="0.8.5" AUFTRAG.version="0.9.0"
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list -- TODO list
@ -1792,6 +1796,35 @@ function AUFTRAG:NewARMORATTACK(Target, Speed, Formation)
return mission return mission
end 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. --- **[AIR, GROUND, NAVAL]** Create a RECON mission.
-- @param #AUFTRAG self -- @param #AUFTRAG self
-- @param Core.Set#SET_ZONE ZoneSet The recon zones. -- @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). -- 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... -- TODO: I commented this out for some reason but I forgot why...
self:T(self.lid.."No targets left cancelling mission!")
self:Cancel() self:Cancel()
elseif self:IsExecuting() then elseif self:IsExecuting() then
@ -5098,6 +5132,26 @@ function AUFTRAG:GetDCSMissionTask(TaskControllable)
table.insert(DCStasks, DCStask) 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 elseif self.type==AUFTRAG.Type.AMMOSUPPLY then
------------------------- -------------------------

View File

@ -100,15 +100,107 @@
-- local text=string.format("Strategy changd to %s", Strategy) -- local text=string.format("Strategy changd to %s", Strategy)
-- MESSAGE:New(text, 120):ToAll() -- MESSAGE:New(text, 120):ToAll()
-- end -- 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 -- # 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. -- local myStratZone=myChief:AddStrategicZone(myOpsZone, nil , 2)
-- So in order to deploy our own troops, infantry assets with `AUFTRAG.Type.ONGUARD` and helicopters with `AUFTRAG.Type.OPSTRANSPORT` need to be available. --
-- 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 -- 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. -- with the @{#CHIEF.OnAfterZoneCaptured}() function.
@ -176,16 +268,23 @@ CHIEF.Strategy = {
-- @type CHIEF.StrategicZone -- @type CHIEF.StrategicZone
-- @field Ops.OpsZone#OPSZONE opszone OPS zone. -- @field Ops.OpsZone#OPSZONE opszone OPS zone.
-- @field #number prio Priority. -- @field #number prio Priority.
-- @field #number importance Importance -- @field #number importance Importance.
-- @field Ops.Auftrag#AUFTRAG missionPatrol Patrol mission. -- @field #table resourceEmpty Resource list.
-- @field Ops.Auftrag#AUFTRAG missionCAS CAS mission. -- @field #table resourceOccup Resource list.
-- @field Ops.Auftrag#AUFTRAG missionPatrol Patrol mission. -- @field #table missions Mission.
-- @field Ops.Auftrag#AUFTRAG missionARTY Artillery 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. --- CHIEF class version.
-- @field #string version -- @field #string version
CHIEF.version="0.2.0" CHIEF.version="0.3.0"
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list -- TODO list
@ -600,6 +699,88 @@ function CHIEF:SetDefcon(Defcon)
return self return self
end 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. --- Get defence condition.
-- @param #CHIEF self -- @param #CHIEF self
-- @param #string Current Defence condition. See @{#CHIEF.DEFCON}, e.g. `CHIEF.DEFCON.RED`. -- @param #string Current Defence condition. See @{#CHIEF.DEFCON}, e.g. `CHIEF.DEFCON.RED`.
@ -813,11 +994,22 @@ function CHIEF:RemoveTarget(Target)
end end
--- Add strategically important zone. --- 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 #CHIEF self
-- @param Ops.OpsZone#OPSZONE OpsZone OPS zone object. -- @param Ops.OpsZone#OPSZONE OpsZone OPS zone object.
-- @param #number Priority Priority. -- @param #number Priority Priority.
-- @param #number Importance Importance. -- @param #number Importance Importance.
-- @return #CHIEF self -- @return #CHIEF.StrategicZone The strategic zone.
function CHIEF:AddStrategicZone(OpsZone, Priority, Importance) function CHIEF:AddStrategicZone(OpsZone, Priority, Importance)
local stratzone={} --#CHIEF.StrategicZone local stratzone={} --#CHIEF.StrategicZone
@ -825,11 +1017,21 @@ function CHIEF:AddStrategicZone(OpsZone, Priority, Importance)
stratzone.opszone=OpsZone stratzone.opszone=OpsZone
stratzone.prio=Priority or 50 stratzone.prio=Priority or 50
stratzone.importance=Importance stratzone.importance=Importance
stratzone.missions={}
-- Start ops zone. -- Start ops zone.
if OpsZone:IsStopped() then if OpsZone:IsStopped() then
OpsZone:Start() OpsZone:Start()
end 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. -- Add to table.
table.insert(self.zonequeue, stratzone) table.insert(self.zonequeue, stratzone)
@ -837,9 +1039,56 @@ function CHIEF:AddStrategicZone(OpsZone, Priority, Importance)
-- Add chief so we get informed when something happens. -- Add chief so we get informed when something happens.
OpsZone:_AddChief(self) 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 return self
end 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. --- Remove strategically important zone. All runing missions are cancelled.
-- @param #CHIEF self -- @param #CHIEF self
-- @param Ops.OpsZone#OPSZONE OpsZone OPS zone object. -- @param Ops.OpsZone#OPSZONE OpsZone OPS zone object.
@ -860,15 +1109,23 @@ function CHIEF:RemoveStrategicZone(OpsZone, Delay)
-- Debug info. -- Debug info.
self:T(self.lid..string.format("Removing OPS zone \"%s\" from queue! All running missions will be cancelled", OpsZone.zoneName)) self:T(self.lid..string.format("Removing OPS zone \"%s\" from queue! All running missions will be cancelled", OpsZone.zoneName))
-- Cancel all running missions. -- Cancel running missions.
for _,_entry in pairs(OpsZone.Missions or {}) do for _,_resource in pairs(stratzone.resourceEmpty) do
local entry = _entry -- Ops.OpsZone#OPSZONE.MISSION local resource=_resource --#CHIEF.Resource
if entry.Coalition==self.coalition and entry.Mission and entry.Mission:IsNotOver() then if resource.mission and resource.mission:IsNotOver() then
entry.Mission:Cancel() resource.mission:Cancel()
end end
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. -- Remove from table.
table.remove(self.zonequeue, i) table.remove(self.zonequeue, i)
@ -1566,13 +1823,14 @@ function CHIEF:_TacticalOverview()
local NmissionsRunni=self.commander:CountMissions(AUFTRAG.Type, true) local NmissionsRunni=self.commander:CountMissions(AUFTRAG.Type, true)
local Ntargets=#self.targetqueue local Ntargets=#self.targetqueue
local Nzones=#self.zonequeue local Nzones=#self.zonequeue
local Nagents=self.detectionset:CountAlive()
-- Info message -- Info message
local text=string.format("Tactical Overview\n") local text=string.format("Tactical Overview\n")
text=text..string.format("=================\n") text=text..string.format("=================\n")
-- Strategy and defcon info. -- 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. -- Contact info.
text=text..string.format("Contacts: %d [Border=%d, Conflict=%d, Attack=%d]\n", Ncontacts, self.Nborder, self.Nconflict, self.Nattack) 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 -- @param #CHIEF self
function CHIEF:CheckOpsZoneQueue() function CHIEF:CheckOpsZoneQueue()
-- Passive strategy ==> Do not act.
if self:IsPassive() then
return
end
-- Number of zones. -- Number of zones.
local Nzones=#self.zonequeue local Nzones=#self.zonequeue
@ -1878,10 +2131,45 @@ function CHIEF:CheckOpsZoneQueue()
if Nzones==0 then if Nzones==0 then
return nil return nil
end 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. -- Check if total number of missions is reached.
local NoLimit=self:_CheckMissionLimit("Total") local NoLimit=self:_CheckMissionLimit("Total")
--env.info("FF chief zone total nolimit="..tostring(NoLimit))
if NoLimit==false then if NoLimit==false then
return nil return nil
end end
@ -1911,19 +2199,15 @@ function CHIEF:CheckOpsZoneQueue()
-- Current owner of the zone. -- Current owner of the zone.
local ownercoalition=stratzone.opszone:GetOwner() local ownercoalition=stratzone.opszone:GetOwner()
-- Name of the zone.
local zoneName=stratzone.opszone.zone:GetName()
-- Check coalition and importance. -- Check coalition and importance.
if ownercoalition~=self.coalition and (stratzone.importance==nil or stratzone.importance<=vip) and (not stratzone.opszone:IsStopped()) then 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. -- 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 if stratzone.opszone:IsEmpty() then
@ -1932,19 +2216,29 @@ function CHIEF:CheckOpsZoneQueue()
-- --
-- We send ground troops to capture the zone. -- We send ground troops to capture the zone.
--- ---
if not hasMissionPatrol then for _,_resource in pairs(stratzone.resourceEmpty or {}) do
local resource=_resource --#CHIEF.Resource
-- 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})
-- Debug info. -- Mission type.
self:T(self.lid..string.format("Zone is empty ==> Recruit Patrol zone infantry assets=%s", tostring(recruitedI))) local missionType=resource.MissionType
self:T(self.lid..string.format("Zone is empty ==> Recruit Patrol zone armored assets=%s", tostring(recruitedT)))
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 end
else else
@ -1954,79 +2248,35 @@ function CHIEF:CheckOpsZoneQueue()
-- --
-- We first send a CAS flight to eliminate enemy activity. -- We first send a CAS flight to eliminate enemy activity.
--- ---
if not hasMissionCAS then
-- Debug message. for _,_resource in pairs(stratzone.resourceOccup or {}) do
self:T3(self.lid..string.format("Zone is NOT empty ==> Recruit CAS assets")) 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. if recruited then
local recruited=self:RecruitAssetsForZone(stratzone, AUFTRAG.Type.CASENHANCED, 1, 1) self:T(self.lid..string.format("Successfully recruited assets for occupied zone %s, mission type=%s", zoneName, missionType))
else
-- Debug message. self:T(self.lid..string.format("Could not recruited assets for occupied zone %s, mission type=%s", zoneName, missionType))
self:T(self.lid..string.format("Zone is NOT empty ==> Recruit CAS assets=%s", tostring(recruited))) end
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
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 end
@ -2360,13 +2610,9 @@ end
--- Recruit assets for a given OPS zone. --- Recruit assets for a given OPS zone.
-- @param #CHIEF self -- @param #CHIEF self
-- @param #CHIEF.StrategicZone StratZone The strategic zone. -- @param #CHIEF.StrategicZone StratZone The strategic zone.
-- @param #string MissionType Mission Type. -- @param #CHIEF.Resource Resource The required resources.
-- @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.
-- @return #boolean If `true` enough assets could be recruited. -- @return #boolean If `true` enough assets could be recruited.
function CHIEF:RecruitAssetsForZone(StratZone, MissionType, NassetsMin, NassetsMax, Categories, Attributes) function CHIEF:RecruitAssetsForZone(StratZone, Resource)
-- Cohorts. -- Cohorts.
local Cohorts={} local Cohorts={}
@ -2385,7 +2631,15 @@ function CHIEF:RecruitAssetsForZone(StratZone, MissionType, NassetsMin, NassetsM
end end
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. -- Target position.
local TargetVec2=StratZone.opszone.zone:GetVec2() local TargetVec2=StratZone.opszone.zone:GetVec2()
@ -2398,26 +2652,36 @@ function CHIEF:RecruitAssetsForZone(StratZone, MissionType, NassetsMin, NassetsM
RangeMax=UTILS.NMToMeters(250) RangeMax=UTILS.NMToMeters(250)
end 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 if MissionType==AUFTRAG.Type.ARMOREDGUARD then
RangeMax=UTILS.NMToMeters(50) RangeMax=UTILS.NMToMeters(50)
end end
-- Recruite infantry assets. -- 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 if recruited then
-- Mission for zone.
local mission=nil --Ops.Auftrag#AUFTRAG
-- Debug messgage. -- 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 if MissionType==AUFTRAG.Type.PATROLZONE or MissionType==AUFTRAG.Type.ONGUARD then
---
-- PATROLZONE or ONGUARD
---
-- Debug messgage. -- Debug messgage.
self:T2(self.lid..string.format("Recruited %d assets for PATROL mission", #assets)) self:T2(self.lid..string.format("Recruited %d assets for PATROL mission", #assets))
local recruitedTrans=true -- First check if we need a transportation.
local transport=nil local recruitedTrans=true ; local transport=nil
if Attributes and Attributes[1]==GROUP.Attribute.GROUND_INFANTRY then 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. -- 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 local Categories=self.TransportCategories
-- Recruit transport assets for infantry. -- 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 end
if recruitedTrans then if recruitedTrans then
-- Create Patrol zone mission.
local mission=nil --Ops.Auftrag#AUFTRAG
if MissionType==AUFTRAG.Type.PATROLZONE then if MissionType==AUFTRAG.Type.PATROLZONE then
mission=AUFTRAG:NewPATROLZONE(StratZone.opszone.zone) mission=AUFTRAG:NewPATROLZONE(TargetZone)
mission:SetEngageDetected(25, {"Ground Units", "Light armed ships", "Helicopters"})
elseif MissionType==AUFTRAG.Type.ONGUARD then 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 end
mission:SetEngageDetected()
-- Engage detected targets.
-- Add assets to mission. mission:SetEngageDetected(25, {"Ground Units", "Light armed ships", "Helicopters"})
for _,asset in pairs(assets) do
mission:AddAsset(asset)
end
-- Attach OPS transport to mission. -- Attach OPS transport to mission.
mission.opstransport=transport 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. -- Set ops zone to transport.
transport.opszone=StratZone.opszone if transport then
transport.chief=self transport.opszone=StratZone.opszone
transport.commander=self.commander transport.chief=self
transport.commander=self.commander
return true end
else else
-- No transport ==> no mission!
self:T(self.lid..string.format("Could not allocate transport of OPSZONE infantry!"))
LEGION.UnRecruitAssets(assets) LEGION.UnRecruitAssets(assets)
return false return false
end end
elseif MissionType==AUFTRAG.Type.CASENHANCED then elseif MissionType==AUFTRAG.Type.CASENHANCED then
---
-- CAS ENHANCED
---
-- Create Patrol zone mission. -- Create Patrol zone mission.
local caszone = StratZone.opszone.zone local height = UTILS.MetersToFeet(TargetCoord:GetLandHeight())+2500
local coord = caszone:GetCoordinate()
local height = UTILS.MetersToFeet(coord: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 local Speed = 200
if assets[1] then 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 Speed = UTILS.KmphToKnots(assets[1].speedmax * 0.7) or 200
end end
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. -- Leg length.
for _,asset in pairs(assets) do local Leg = TargetZone:GetRadius() <= 10000 and 5 or UTILS.MetersToNM(TargetZone:GetRadius())
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)
-- Add assets to mission. -- CAS mission.
for _,asset in pairs(assets) do mission=AUFTRAG:NewCAS(TargetZone, height, Speed, TargetCoord, math.random(0,359), Leg)
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.ARTY then elseif MissionType==AUFTRAG.Type.ARTY then
---
-- ARTY
---
-- Create ARTY zone mission. -- Create ARTY zone mission.
local TargetZone = StratZone.opszone.zone
local Target = TargetZone:GetCoordinate()
local Radius = TargetZone:GetRadius() local Radius = TargetZone:GetRadius()
local mission=AUFTRAG:NewARTY(Target,120,Radius)
mission=AUFTRAG:NewARTY(TargetCoord, 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
elseif MissionType==AUFTRAG.Type.ARMOREDGUARD then elseif MissionType==AUFTRAG.Type.ARMOREDGUARD then
---
-- ARMORGUARD
---
-- Create Armored on guard mission -- Create Armored on guard mission
local TargetZone = StratZone.opszone.zone mission=AUFTRAG:NewARMOREDGUARD(TargetCoord)
local Target = TargetZone:GetCoordinate()
local mission=AUFTRAG:NewARMOREDGUARD(Target) 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. -- Add assets to mission.
for _,asset in pairs(assets) do for _,asset in pairs(assets) do
mission:AddAsset(asset) mission:AddAsset(asset)
@ -2567,8 +2840,18 @@ function CHIEF:RecruitAssetsForZone(StratZone, MissionType, NassetsMin, NassetsM
-- Attach mission to ops zone. -- Attach mission to ops zone.
StratZone.opszone:_AddMission(self.coalition, MissionType, mission) 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
end end

View File

@ -35,6 +35,8 @@
-- @field #number Nkilled Number of destroyed asset groups. -- @field #number Nkilled Number of destroyed asset groups.
-- @field #number engageRange Mission range in meters. -- @field #number engageRange Mission range in meters.
-- @field #string attribute Generalized attribute of the cohort template group. -- @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 #table tacanChannel List of TACAN channels available to the cohort.
-- @field #number radioFreq Radio frequency in MHz the cohort uses. -- @field #number radioFreq Radio frequency in MHz the cohort uses.
-- @field #number radioModu Radio modulation the cohort uses. -- @field #number radioModu Radio modulation the cohort uses.
@ -73,17 +75,20 @@ COHORT = {
tacanChannel = {}, tacanChannel = {},
weightAsset = 99999, weightAsset = 99999,
cargobayLimit = 0, cargobayLimit = 0,
descriptors = {},
properties = {},
} }
--- COHORT class version. --- COHORT class version.
-- @field #string version -- @field #string version
COHORT.version="0.1.0" COHORT.version="0.2.0"
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list -- TODO list
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO: Create FLOTILLA class. -- TODO: Create FLOTILLA class.
-- DONE: Added check for properties.
-- DONE: Make general so that PLATOON and SQUADRON can inherit this class. -- DONE: Make general so that PLATOON and SQUADRON can inherit this class.
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@ -127,6 +132,15 @@ function COHORT:New(TemplateGroupName, Ngroups, CohortName)
-- Aircraft type. -- Aircraft type.
self.aircrafttype=self.templategroup:GetTypeName() 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. -- Defaults.
self.Ngroups=Ngroups or 3 self.Ngroups=Ngroups or 3
@ -321,6 +335,21 @@ function COHORT:AddMissionCapability(MissionTypes, Performance)
return self return self
end 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. --- Get mission types this cohort is able to perform.
-- @param #COHORT self -- @param #COHORT self
-- @return #table Table of mission types. Could be empty {}. -- @return #table Table of mission types. Could be empty {}.

View File

@ -166,6 +166,20 @@ FLIGHTGROUP.Attribute = {
OTHER="Other", 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. --- FLIGHTGROUP class version.
-- @field #string version -- @field #string version
FLIGHTGROUP.version="0.7.1" FLIGHTGROUP.version="0.7.1"

View File

@ -1964,10 +1964,11 @@ end
-- @param #number TotalWeight Total cargo weight in kg. -- @param #number TotalWeight Total cargo weight in kg.
-- @param #table Categories Group categories. -- @param #table Categories Group categories.
-- @param #table Attributes Group attributes. See `GROUP.Attribute.` -- @param #table Attributes Group attributes. See `GROUP.Attribute.`
-- @param #table Properties DCS attributes.
-- @return #boolean If `true` enough assets could be recruited. -- @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 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. -- @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. -- The recruited assets.
local Assets={} local Assets={}
@ -2006,7 +2007,26 @@ function LEGION.RecruitCohortAssets(Cohorts, MissionTypeRecruit, MissionTypeOpt,
else else
return true return true
end 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. -- Loops over cohorts.
for _,_cohort in pairs(Cohorts) do for _,_cohort in pairs(Cohorts) do
@ -2045,12 +2065,15 @@ function LEGION.RecruitCohortAssets(Cohorts, MissionTypeRecruit, MissionTypeOpt,
-- Right attribute. -- Right attribute.
local RightAttribute=CheckAttribute(cohort) local RightAttribute=CheckAttribute(cohort)
-- Right property (DCS attribute).
local RightProperty=CheckProperty(cohort)
-- Debug info. -- Debug info.
cohort:T(cohort.lid..string.format("State=%s: Capable=%s, InRange=%s, Refuel=%s, CanCarry=%s, RightCategory=%s, RightAttribute=%s", 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))) 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). -- 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. -- Recruit assets from cohort.
local assets, npayloads=cohort:RecruitAssets(MissionTypeRecruit, 999) local assets, npayloads=cohort:RecruitAssets(MissionTypeRecruit, 999)
@ -2100,7 +2123,7 @@ function LEGION.RecruitCohortAssets(Cohorts, MissionTypeRecruit, MissionTypeOpt,
--- ---
-- Found enough assets -- Found enough assets
--- ---
-- Add assets to mission. -- Add assets to mission.
local cargobay=0 local cargobay=0
for i=1,Nassets do for i=1,Nassets do

View File

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

View File

@ -587,14 +587,6 @@ function OPSZONE:onafterStop(From, Event, To)
-- Unhandle events. -- Unhandle events.
self:UnHandleEvent(EVENTS.BaseCaptured) 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. -- Stop FSM scheduler.
self.CallScheduler:Clear() self.CallScheduler:Clear()