**CHIEF**
- Added resources as parameters to `:AddStrategicZone` function

**COMMANDER**
- Added function to relocate cohorts `:RelocateCohort`

**AUFTRAG**
- Added new type `AIRDEFENSE`
- Added new type `EWR`
- Added option to teleport assets to the mission ingress waypoint via `:SetTeleport`
- Added `:SetRequiredAttribute` and `:SetRequiredProperty` functions
- Added `:SetEmission` function

**LEGION**
- Fixed bug that assets on GCI dont get additional score for INTERCEPT missions
- Assets on ONGUARD or PATROLZONE are not considered for ARTY and GROUNDATTACK missions
- Added option for transport to `RelocateCohort` function
- Ground/naval assets now automatically return when out of ammo

**OPSGROUP**
- Immobile groups are teleported to mission ingress point

**RECOVERYTANKER**
- Added parameter to set TACAN mode/band (e.g. "X")

**GROUP**
- Fixed bug in `:GetSpeedMax` function

**BEACON**
- Allowed TACAN "X" mode for AA
This commit is contained in:
Frank 2022-04-27 22:36:13 +02:00
parent 2d02f4f962
commit ed0a3a22ab
16 changed files with 1061 additions and 274 deletions

View File

@ -170,6 +170,8 @@ end
function BEACON:ActivateTACAN(Channel, Mode, Message, Bearing, Duration)
self:T({channel=Channel, mode=Mode, callsign=Message, bearing=Bearing, duration=Duration})
Mode=Mode or "Y"
-- Get frequency.
local Frequency=UTILS.TACANToFrequency(Channel, Mode)
@ -187,11 +189,16 @@ function BEACON:ActivateTACAN(Channel, Mode, Message, Bearing, Duration)
-- Check if unit is an aircraft and set system accordingly.
local AA=self.Positionable:IsAir()
if AA then
System=5 --NOTE: 5 is how you cat the correct tanker behaviour! --BEACON.System.TACAN_TANKER
-- Check if "Y" mode is selected for aircraft.
if Mode~="Y" then
self:E({"WARNING: The POSITIONABLE you want to attach the AA Tacan Beacon is an aircraft: Mode should Y !The BEACON is not emitting.", self.Positionable})
if Mode=="X" then
--self:E({"WARNING: The POSITIONABLE you want to attach the AA Tacan Beacon is an aircraft: Mode should Y!", self.Positionable})
System=BEACON.System.TACAN_TANKER_X
else
System=BEACON.System.TACAN_TANKER_Y
end
end
@ -267,13 +274,12 @@ function BEACON:AATACAN(TACANChannel, Message, Bearing, BeaconDuration)
IsValid = false
end
-- I'm using the beacon type 4 (BEACON_TYPE_TACAN). For System, I'm using 5 (TACAN_TANKER_MODE_Y) if the bearing shows its bearing
-- or 14 (TACAN_AA_MODE_Y) if it does not
-- I'm using the beacon type 4 (BEACON_TYPE_TACAN). For System, I'm using 5 (TACAN_TANKER_MODE_Y) if the bearing shows its bearing or 14 (TACAN_AA_MODE_Y) if it does not
local System
if Bearing then
System = 5
System = BEACON.System.TACAN_TANKER_Y
else
System = 14
System = BEACON.System.TACAN_AA_MODE_Y
end
if IsValid then -- Starts the BEACON
@ -281,10 +287,13 @@ function BEACON:AATACAN(TACANChannel, Message, Bearing, BeaconDuration)
self.Positionable:SetCommand({
id = "ActivateBeacon",
params = {
type = 4,
type = BEACON.Type.TACAN,
system = System,
callsign = Message,
AA = true,
frequency = Frequency,
bearing = Bearing,
modeChannel = "Y",
}
})

View File

@ -60,6 +60,7 @@
-- @field #number DrawID Unique ID of the drawn zone on the F10 map.
-- @field #table Color Table with four entries, e.g. {1, 0, 0, 0.15}. First three are RGB color code. Fourth is the transparency Alpha value.
-- @field #number ZoneID ID of zone. Only zones defined in the ME have an ID!
-- @field #number Surface Type of surface. Only determined at the center of the zone!
-- @extends Core.Fsm#FSM
@ -111,6 +112,7 @@ ZONE_BASE = {
DrawID=nil,
Color={},
ZoneID=nil,
Sureface=nil,
}
@ -335,15 +337,22 @@ end
-- @param #ZONE_BASE self
-- @return #nil The bounding square.
function ZONE_BASE:GetBoundingSquare()
--return { x1 = 0, y1 = 0, x2 = 0, y2 = 0 }
return nil
end
--- Get surface type of the zone.
-- @param #ZONE_BASE self
-- @return DCS#SurfaceType Type of surface.
function ZONE_BASE:GetSurfaceType()
local coord=self:GetCoordinate()
local surface=coord:GetSurfaceType()
return surface
end
--- Bound the zone boundaries with a tires.
-- @param #ZONE_BASE self
function ZONE_BASE:BoundZone()
self:F2()
end
@ -1281,8 +1290,8 @@ end
--- Returns a @{Core.Point#COORDINATE} object reflecting a random 3D location within the zone.
-- @param #ZONE_RADIUS self
-- @param #number inner (Optional) Minimal distance from the center of the zone. Default is 0.
-- @param #number outer (Optional) Maximal distance from the outer edge of the zone. Default is the radius of the zone.
-- @param #number inner (Optional) Minimal distance from the center of the zone in meters. Default is 0 m.
-- @param #number outer (Optional) Maximal distance from the outer edge of the zone in meters. Default is the radius of the zone.
-- @param #table surfacetypes (Optional) Table of surface types. Can also be a single surface type. We will try max 1000 times to find the right type!
-- @return Core.Point#COORDINATE The random coordinate.
function ZONE_RADIUS:GetRandomCoordinate(inner, outer, surfacetypes)

View File

@ -299,6 +299,9 @@ function AIRWING:AddSquadron(Squadron)
elseif Squadron.attribute==GROUP.Attribute.AIR_TANKER then
self:NewPayload(Squadron.templategroup, -1, AUFTRAG.Type.TANKER)
end
-- Relocate mission.
self:NewPayload(Squadron.templategroup, -1, AUFTRAG.Type.RELOCATECOHORT, 0)
-- Set airwing to squadron.
Squadron:SetAirwing(self)

View File

@ -33,7 +33,6 @@
-- @field #boolean formationPerma Formation that is used permanently and overrules waypoint formations.
-- @field #boolean isMobile If true, group is mobile.
-- @field #ARMYGROUP.Target engage Engage target.
-- @field #boolean retreatOnOutOfAmmo If true, the group will automatically retreat when out of ammo. Needs a retreat zone!
-- @field Core.Set#SET_ZONE retreatZones Set of retreat zones.
-- @extends Ops.OpsGroup#OPSGROUP
@ -710,7 +709,7 @@ function ARMYGROUP:Status()
local ammo=self:GetAmmoTot().Total
-- Detected units.
local ndetected=self.detectionOn and tostring(self.detectedunits:Count()) or "OFF"
local ndetected=self.detectionOn and tostring(self.detectedunits:Count()) or "Off"
-- Get cargo weight.
local cargo=0

View File

@ -71,6 +71,8 @@
-- @field Core.Point#COORDINATE orbitRaceTrack Race-track orbit coordinate.
--
-- @field Ops.Target#TARGET engageTarget Target data to engage.
--
-- @field #boolean teleport Groups are teleported to the mission ingress waypoint.
--
-- @field Core.Zone#ZONE_RADIUS engageZone *Circular* engagement zone.
-- @field #table engageTargetTypes Table of target types that are engaged in the engagement zone.
@ -162,6 +164,7 @@
-- @field #number optionRTBammo RTB on out-of-ammo.
-- @field #number optionRTBfuel RTB on out-of-fuel.
-- @field #number optionECM ECM.
-- @field #boolean optionEmission Emission is on or off.
--
-- @extends Core.Fsm#FSM
@ -312,7 +315,16 @@
--
-- ## Commander Level
--
-- Assigning an AUFTRAG to acommander is done via the @{Ops.Commander#COMMANDER.AddMission} function. See COMMANDER docs for details.
-- Assigning an AUFTRAG to a commander is done via the @{Ops.Commander#COMMANDER.AddMission} function.
-- The commander will select the best assets available from all the legions under his command. See COMMANDER docs for details.
--
-- ## Chief Level
--
-- Assigning an AUFTRAG to a commander is done via the @{Ops.Chief#CHIEF.AddMission} function. The chief will simply pass on the mission to his/her commander.
--
-- # Transportation
--
-- TODO
--
--
-- # Events
@ -393,6 +405,8 @@ _AUFTRAGSNR=0
-- @field #string GROUNDATTACK Ground attack.
-- @field #string CARGOTRANSPORT Cargo transport.
-- @field #string RELOCATECOHORT Relocate a cohort from one legion to another.
-- @field #string AIRDEFENSE Air defense.
-- @field #string EWR Early Warning Radar.
-- @field #string NOTHING Nothing.
AUFTRAG.Type={
ANTISHIP="Anti Ship",
@ -430,8 +444,10 @@ AUFTRAG.Type={
HOVER="Hover",
GROUNDATTACK="Ground Attack",
CARGOTRANSPORT="Cargo Transport",
RELOCATECOHORT="Relocate Cohort",
AIRDEFENSE="Air Defence",
EWR="Early Warning Radar",
NOTHING="Nothing",
RELOCATECOHORT="Relocate Cohort",
}
--- Special task description.
@ -448,8 +464,9 @@ AUFTRAG.Type={
-- @field #string HOVER Hover.
-- @field #string GROUNDATTACK Ground attack.
-- @field #string FERRY Ferry mission.
-- @field #string NOTHING Nothing.
-- @field #string RELOCATECOHORT Relocate cohort.
-- @field #string AIRDEFENSE Air defense.
-- @field #string NOTHING Nothing.
AUFTRAG.SpecialTask={
FORMATION="Formation",
PATROLZONE="PatrolZone",
@ -464,8 +481,10 @@ AUFTRAG.SpecialTask={
HOVER="Hover",
GROUNDATTACK="Ground Attack",
FERRY="Ferry",
RELOCATECOHORT="Relocate Cohort",
AIRDEFENSE="Air Defense",
EWR="Early Warning Radar",
NOTHING="Nothing",
RELOCATECOHORT="Relocate Cohort",
}
--- Mission status.
@ -586,7 +605,7 @@ AUFTRAG.Category={
--- AUFTRAG class version.
-- @field #string version
AUFTRAG.version="0.9.3"
AUFTRAG.version="0.9.4"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list
@ -647,7 +666,6 @@ function AUFTRAG:New(Type)
self:SetPriority()
self:SetTime()
self:SetRequiredAssets()
--self:SetRequiredCarriers()
self.engageAsGroup=true
self.dTevaluate=5
@ -1341,7 +1359,7 @@ function AUFTRAG:NewBAI(Target, Altitude)
mission:_TargetFromObject(Target)
-- DCS Task options:
mission.engageWeaponType=ENUMS.WeaponFlag.AnyAG
mission.engageWeaponType=ENUMS.WeaponFlag.Auto
mission.engageWeaponExpend=AI.Task.WeaponExpend.ALL
mission.engageAltitude=UTILS.FeetToMeters(Altitude or 5000)
@ -1362,7 +1380,7 @@ end
--- **[AIR]** Create a SEAD mission.
-- @param #AUFTRAG self
-- @param Wrapper.Positionable#POSITIONABLE Target The target to attack. Can be a GROUP or UNIT object.
-- @param #number Altitude Engage altitude in feet. Default 8000 ft.
-- @param #number Altitude Engage altitude in feet. Default 25000 ft.
-- @return #AUFTRAG self
function AUFTRAG:NewSEAD(Target, Altitude)
@ -1371,9 +1389,9 @@ function AUFTRAG:NewSEAD(Target, Altitude)
mission:_TargetFromObject(Target)
-- DCS Task options:
mission.engageWeaponType=ENUMS.WeaponFlag.AnyAG --ENUMS.WeaponFlag.Cannons
mission.engageWeaponType=ENUMS.WeaponFlag.Auto
mission.engageWeaponExpend=AI.Task.WeaponExpend.ALL
mission.engageAltitude=UTILS.FeetToMeters(Altitude or 8000)
mission.engageAltitude=UTILS.FeetToMeters(Altitude or 25000)
-- Mission options:
mission.missionTask=ENUMS.MissionTask.SEAD
@ -1402,7 +1420,7 @@ function AUFTRAG:NewSTRIKE(Target, Altitude)
mission:_TargetFromObject(Target)
-- DCS Task options:
mission.engageWeaponType=ENUMS.WeaponFlag.AnyAG
mission.engageWeaponType=ENUMS.WeaponFlag.Auto
mission.engageWeaponExpend=AI.Task.WeaponExpend.ALL
mission.engageAltitude=UTILS.FeetToMeters(Altitude or 2000)
@ -1432,7 +1450,7 @@ function AUFTRAG:NewBOMBING(Target, Altitude)
mission:_TargetFromObject(Target)
-- DCS task options:
mission.engageWeaponType=ENUMS.WeaponFlag.AnyBomb
mission.engageWeaponType=ENUMS.WeaponFlag.Auto
mission.engageWeaponExpend=AI.Task.WeaponExpend.ALL
mission.engageAltitude=UTILS.FeetToMeters(Altitude or 25000)
@ -1470,7 +1488,7 @@ function AUFTRAG:NewBOMBRUNWAY(Airdrome, Altitude)
mission:_TargetFromObject(Airdrome)
-- DCS task options:
mission.engageWeaponType=ENUMS.WeaponFlag.AnyBomb
mission.engageWeaponType=ENUMS.WeaponFlag.Auto
mission.engageWeaponExpend=AI.Task.WeaponExpend.ALL
mission.engageAltitude=UTILS.FeetToMeters(Altitude or 25000)
@ -1505,7 +1523,7 @@ function AUFTRAG:NewBOMBCARPET(Target, Altitude, CarpetLength)
mission:_TargetFromObject(Target)
-- DCS task options:
mission.engageWeaponType=ENUMS.WeaponFlag.AnyBomb
mission.engageWeaponType=ENUMS.WeaponFlag.Auto
mission.engageWeaponExpend=AI.Task.WeaponExpend.ALL
mission.engageAltitude=UTILS.FeetToMeters(Altitude or 25000)
mission.engageCarpetLength=CarpetLength or 500
@ -1996,10 +2014,55 @@ function AUFTRAG:NewONGUARD(Coordinate)
return mission
end
--- **[PRIVATE, AIR, GROUND, NAVAL]** Create a mission to relocate assets to another LEGION.
--- **[GROUND, NAVAL]** Create an AIRDEFENSE mission.
-- @param #AUFTRAG self
-- @param Core.Zone#ZONE Zone Zone where the air defense group(s) should be stationed.
-- @return #AUFTRAG self
function AUFTRAG:NewAIRDEFENSE(Zone)
local mission=AUFTRAG:New(AUFTRAG.Type.AIRDEFENSE)
mission:_TargetFromObject(Zone)
mission.optionROE=ENUMS.ROE.OpenFire
mission.optionAlarm=ENUMS.AlarmState.Auto
mission.missionFraction=1.0
mission.categories={AUFTRAG.Category.GROUND, AUFTRAG.Category.NAVAL}
mission.DCStask=mission:GetDCSMissionTask()
return mission
end
--- **[GROUND]** Create an EWR mission.
-- @param #AUFTRAG self
-- @param Core.Zone#ZONE Zone Zone where the Early Warning Radar group(s) should be stationed.
-- @return #AUFTRAG self
function AUFTRAG:NewEWR(Zone)
local mission=AUFTRAG:New(AUFTRAG.Type.EWR)
mission:_TargetFromObject(Zone)
mission.optionROE=ENUMS.ROE.WeaponHold
mission.optionAlarm=ENUMS.AlarmState.Auto
mission.missionFraction=1.0
mission.categories={AUFTRAG.Category.GROUND}
mission.DCStask=mission:GetDCSMissionTask()
return mission
end
--- **[PRIVATE, AIR, GROUND, NAVAL]** Create a mission to relocate all cohort assets to another LEGION.
-- @param #AUFTRAG self
-- @param Ops.Legion#LEGION Legion The new legion.
-- @param Ops.Cohort#COHORT Cohort The new cohort.
-- @param Ops.Cohort#COHORT Cohort The cohort to be relocated.
-- @return #AUFTRAG self
function AUFTRAG:_NewRELOCATECOHORT(Legion, Cohort)
@ -2312,6 +2375,14 @@ function AUFTRAG:SetDuration(Duration)
return self
end
--- Set that mission assets are teleported to the mission execution waypoint.
-- @param #AUFTRAG self
-- @return #AUFTRAG self
function AUFTRAG:SetTeleport()
self.teleport=true
return self
end
--- Set mission push time. This is the time the mission is executed. If the push time is not passed, the group will wait at the mission execution waypoint.
-- @param #AUFTRAG self
@ -2637,6 +2708,29 @@ function AUFTRAG:AddTransportCarriers(Carriers)
end
--- Set required attribute(s) the assets must have.
-- @param #AUFTRAG self
-- @param #table Attributes Generalized attribute(s).
-- @return #AUFTRAG self
function AUFTRAG:SetRequiredAttribute(Attributes)
if Attributes and type(Attributes)~="table" then
Attributes={Attributes}
end
self.attributes=Attributes
end
--- Set required property or properties the assets must have.
-- These are DCS attributes.
-- @param #AUFTRAG self
-- @param #table Properties Property or table of properties.
-- @return #AUFTRAG self
function AUFTRAG:SetRequiredProperty(Properties)
if Properties and type(Properties)~="table" then
Properties={Properties}
end
self.properties=Properties
end
--- Set number of required carrier groups if an OPSTRANSPORT assignment is required.
-- @param #AUFTRAG self
-- @param #number NcarriersMin Number of carriers *at least* required. Default 1.
@ -2806,6 +2900,21 @@ function AUFTRAG:SetEPLRS(OnOffSwitch)
return self
end
--- Set emission setting for this mission.
-- @param #AUFTRAG self
-- @param #boolean OnOffSwitch If `true` or `nil`, emission is on. If `false`, emission is off.
-- @return #AUFTRAG self
function AUFTRAG:SetEmission(OnOffSwitch)
if OnOffSwitch==nil then
self.optionEmission=true
else
self.optionEmission=OnOffSwitch
end
return self
end
--- Set formation for this mission.
-- @param #AUFTRAG self
-- @param #number Formation Formation.
@ -3765,12 +3874,19 @@ function AUFTRAG:RemoveLegion(Legion)
-- Loop over legions
for i=#self.legions,1,-1 do
local legion=self.legions[i] --Ops.Legion#LEGION
if legion.alias==Legion.alias then
-- Debug info.
self:T(self.lid..string.format("Removing legion %s", Legion.alias))
table.remove(self.legions, i)
-- Set legion status to nil.
self.statusLegion[Legion.alias]=nil
return self
end
end
self:T(self.lid..string.format("ERROR: Legion %s not found and could not be removed!", Legion.alias))
@ -4392,15 +4508,15 @@ function AUFTRAG:onafterRepeat(From, Event, To)
if self.chief then
-- Set status for chief.
self.statusChief=AUFTRAG.Status.PLANNED
-- Remove mission from wingcommander because Chief will assign it again.
if self.commander then
self.commander:RemoveMission(self)
self.statusCommander=AUFTRAG.Status.PLANNED
end
-- Remove mission from airwing because WC will assign it again but maybe to a different wing.
-- Remove mission from legions because commander will assign it again but maybe to different legion(s).
for _,_legion in pairs(self.legions) do
local legion=_legion --Ops.Legion#LEGION
legion:RemoveMission(self)
@ -4408,9 +4524,10 @@ function AUFTRAG:onafterRepeat(From, Event, To)
elseif self.commander then
-- Set status for commander.
self.statusCommander=AUFTRAG.Status.PLANNED
-- Remove mission from airwing because WC will assign it again but maybe to a different wing.
-- Remove mission from legion(s) because commander will assign it again but maybe to different legion(s).
for _,_legion in pairs(self.legions) do
local legion=_legion --Ops.Legion#LEGION
legion:RemoveMission(self)
@ -5440,19 +5557,6 @@ function AUFTRAG:GetDCSMissionTask(TaskControllable)
DCStask.params=param
--[[ Task script.
local DCSScript = {}
local altitude = self.hoverAltitude
DCSScript[#DCSScript+1] = 'local group = ...'
DCSScript[#DCSScript+1] = 'local helo = GROUP:Find(group)'
DCSScript[#DCSScript+1] = 'helo:SetSpeed(0.1,true)'
DCSScript[#DCSScript+1] = string.format('helo:SetAltitude(UTILS.FeetToMeters(%d),true,"BARO")',altitude) -- Call the function, e.g. myfunction.(warehouse,mygroup)
-- Create task.
local DCSTask=CONTROLLABLE.TaskWrappedAction(self, CONTROLLABLE.CommandDoScript(self, table.concat(DCSScript)))
--]]
table.insert(DCStasks, DCStask)
elseif self.type==AUFTRAG.Type.ONGUARD or self.type==AUFTRAG.Type.ARMOREDGUARD then
@ -5473,6 +5577,46 @@ function AUFTRAG:GetDCSMissionTask(TaskControllable)
table.insert(DCStasks, DCStask)
elseif self.type==AUFTRAG.Type.AIRDEFENSE then
------------------------
-- AIRDEFENSE Mission --
------------------------
local DCStask={}
DCStask.id=AUFTRAG.SpecialTask.AIRDEFENSE
-- We create a "fake" DCS task and pass the parameters to the OPSGROUP.
local param={}
param.zone=self:GetObjective()
DCStask.params=param
table.insert(DCStasks, DCStask)
elseif self.type==AUFTRAG.Type.EWR then
-----------------
-- EWR Mission --
-----------------
local DCStask={}
DCStask.id=AUFTRAG.SpecialTask.EWR
-- We create a "fake" DCS task and pass the parameters to the OPSGROUP.
local param={}
param.zone=self:GetObjective()
DCStask.params=param
table.insert(DCStasks, DCStask)
-- EWR is an enroute task
local Enroutetask=CONTROLLABLE.EnRouteTaskEWR()
table.insert(self.enrouteTasks, Enroutetask)
else
self:T(self.lid..string.format("ERROR: Unknown mission task!"))
return nil

View File

@ -153,17 +153,12 @@
-- 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.
-- These lists can than passed as additional parameters to the @{#CHIEF.AddStrategicZone} function.
--
-- For example:
--
-- -- Create a resource list of mission types and required assets for the case that the zone is occupied.
-- --- 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).
@ -173,11 +168,8 @@
-- -- 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.
-- --- 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.
@ -185,10 +177,10 @@
-- -- 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)
-- -- Add stratetic zone with customized reaction.
-- myChief:AddStrategicZone(myOpsZone, nil , 2, ResourceOccupied, 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
-- As the location of the enemies is not known, only mission types that don't require an explicit target group are possible. These are
--
-- * `AUFTRAG.Type.CASENHANCED`
-- * `AUFTRAG.Type.ARTY`
@ -284,7 +276,7 @@ CHIEF.Strategy = {
--- CHIEF class version.
-- @field #string version
CHIEF.version="0.3.0"
CHIEF.version="0.3.1"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list
@ -1006,23 +998,28 @@ 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:
-- 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.
--
-- Occupied:
--
-- * `AUFTRAG.Type.ARTY` with Nmin=1, Nmax=2
-- * `AUFTRAG.Type.CASENHANCED` with Nmin=1, Nmax=2
--
-- Occupied:
-- Empty:
--
-- * `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`.
--
-- Resources can be created with the @{#CHIEF.CreateResource} and @{#CHIEF.AddToResource} functions.
--
-- @param #CHIEF self
-- @param Ops.OpsZone#OPSZONE OpsZone OPS zone object.
-- @param #number Priority Priority.
-- @param #number Importance Importance.
-- @param #number Priority Priority. Default 50.
-- @param #number Importance Importance. Default nil.
-- @param #CHIEF.Resource ResourceOccupied (Optional) Resources used then zone is occupied by the enemy.
-- @param #CHIEF.Resource ResourceEmpty (Optional) Resources used then zone is empty.
-- @return #CHIEF.StrategicZone The strategic zone.
function CHIEF:AddStrategicZone(OpsZone, Priority, Importance)
function CHIEF:AddStrategicZone(OpsZone, Priority, Importance, ResourceOccupied, ResourceEmpty)
local stratzone={} --#CHIEF.StrategicZone
@ -1038,12 +1035,20 @@ function CHIEF:AddStrategicZone(OpsZone, Priority, Importance)
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)
if ResourceOccupied then
stratzone.resourceOccup=UTILS.DeepCopy(ResourceOccupied)
else
stratzone.resourceOccup=self:CreateResource(AUFTRAG.Type.ARTY, 1, 2)
self:AddToResource(stratzone.resourceOccup, AUFTRAG.Type.CASENHANCED, 1, 2)
end
-- 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)
if ResourceEmpty then
stratzone.resourceEmpty=UTILS.DeepCopy(ResourceEmpty)
else
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)
end
-- Add to table.
table.insert(self.zonequeue, stratzone)
@ -2442,14 +2447,15 @@ function CHIEF:_GetMissionPerformanceFromTarget(Target)
table.insert(missionperf, self:_CreateMissionPerformance(AUFTRAG.Type.SEAD, 100))
table.insert(missionperf, self:_CreateMissionPerformance(AUFTRAG.Type.GROUNDATTACK, 50))
table.insert(missionperf, self:_CreateMissionPerformance(AUFTRAG.Type.ARTY, 30))
elseif attribute==GROUP.Attribute.GROUND_EWR then
-- EWR
table.insert(missionperf, self:_CreateMissionPerformance(AUFTRAG.Type.SEAD, 100))
table.insert(missionperf, self:_CreateMissionPerformance(AUFTRAG.Type.BAI, 90))
table.insert(missionperf, self:_CreateMissionPerformance(AUFTRAG.Type.BAI, 100))
table.insert(missionperf, self:_CreateMissionPerformance(AUFTRAG.Type.GROUNDATTACK, 50))
table.insert(missionperf, self:_CreateMissionPerformance(AUFTRAG.Type.ARTY, 30))
elseif attribute==GROUP.Attribute.GROUND_AAA then
@ -2484,11 +2490,13 @@ function CHIEF:_GetMissionPerformanceFromTarget(Target)
table.insert(missionperf, self:_CreateMissionPerformance(AUFTRAG.Type.CAS, 100))
table.insert(missionperf, self:_CreateMissionPerformance(AUFTRAG.Type.GROUNDATTACK, 50))
table.insert(missionperf, self:_CreateMissionPerformance(AUFTRAG.Type.ARMORATTACK, 40))
table.insert(missionperf, self:_CreateMissionPerformance(AUFTRAG.Type.ARTY, 30))
else
table.insert(missionperf, self:_CreateMissionPerformance(AUFTRAG.Type.BAI, 100))
table.insert(missionperf, self:_CreateMissionPerformance(AUFTRAG.Type.GROUNDATTACK, 50))
table.insert(missionperf, self:_CreateMissionPerformance(AUFTRAG.Type.ARTY, 30))
end

View File

@ -26,6 +26,9 @@
-- @field #string aircrafttype Type of the units the cohort is using.
-- @field #number category Group category of the assets: `Group.Category.AIRPLANE`, `Group.Category.HELICOPTER`, `Group.Category.GROUND`, `Group.Category.SHIP`, `Group.Category.TRAIN`.
-- @field Wrapper.Group#GROUP templategroup Template group.
-- @field #boolean isAir
-- @field #boolean isGround Is ground.
-- @field #boolean isNaval Is naval.
-- @field #table assets Cohort assets.
-- @field #table missiontypes Capabilities (mission types and performances) of the cohort.
-- @field #number maintenancetime Time in seconds needed for maintenance of a returned flight.
@ -83,7 +86,7 @@ COHORT = {
--- COHORT class version.
-- @field #string version
COHORT.version="0.3.2"
COHORT.version="0.3.4"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list
@ -226,6 +229,74 @@ function COHORT:New(TemplateGroupName, Ngroups, CohortName)
-- @param #COHORT self
-- @param #number delay Delay in seconds.
--- Triggers the FSM event "Pause".
-- @function [parent=#COHORT] Pause
-- @param #COHORT self
--- Triggers the FSM event "Pause" after a delay.
-- @function [parent=#COHORT] __Pause
-- @param #COHORT self
-- @param #number delay Delay in seconds.
--- On after "Pause" event.
-- @function [parent=#AUFTRAG] OnAfterPause
-- @param #AUFTRAG self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
--- Triggers the FSM event "Unpause".
-- @function [parent=#COHORT] Unpause
-- @param #COHORT self
--- Triggers the FSM event "Unpause" after a delay.
-- @function [parent=#COHORT] __Unpause
-- @param #COHORT self
-- @param #number delay Delay in seconds.
--- On after "Unpause" event.
-- @function [parent=#AUFTRAG] OnAfterUnpause
-- @param #AUFTRAG self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
--- Triggers the FSM event "Relocate".
-- @function [parent=#COHORT] Relocate
-- @param #COHORT self
--- Triggers the FSM event "Relocate" after a delay.
-- @function [parent=#COHORT] __Relocate
-- @param #COHORT self
-- @param #number delay Delay in seconds.
--- On after "Relocate" event.
-- @function [parent=#AUFTRAG] OnAfterRelocate
-- @param #AUFTRAG self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
--- Triggers the FSM event "Relocated".
-- @function [parent=#COHORT] Relocated
-- @param #COHORT self
--- Triggers the FSM event "Relocated" after a delay.
-- @function [parent=#COHORT] __Relocated
-- @param #COHORT self
-- @param #number delay Delay in seconds.
--- On after "Relocated" event.
-- @function [parent=#AUFTRAG] OnAfterRelocated
-- @param #AUFTRAG self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
return self
end
@ -905,9 +976,9 @@ end
-- @return #table Assets that can do the required mission.
-- @return #number Number of payloads still available after recruiting the assets.
function COHORT:RecruitAssets(MissionType, Npayloads)
self:T("RecruitAssets for " .. MissionType .. " with " ..Npayloads)
-- Debug info.
self:T3(self.lid..string.format("Recruiting asset for Mission type=%s", MissionType))
self:T2(self.lid..string.format("Recruiting asset for Mission type=%s", MissionType))
-- Recruited assets.
local assets={}
@ -916,32 +987,60 @@ function COHORT:RecruitAssets(MissionType, Npayloads)
for _,_asset in pairs(self.assets) do
local asset=_asset --Functional.Warehouse#WAREHOUSE.Assetitem
--self:I("Looking at Asset " .. asset.spawngroupname)
-- Get info.
local isRequested=asset.requested
local isReserved=asset.isReserved
local isSpawned=asset.spawned
local isOnMission=self.legion:IsAssetOnMission(asset)
local opsgroup=asset.flightgroup
-- Debug info.
self:T(self.lid..string.format("Asset %s: requested=%s, reserved=%s, spawned=%s, onmission=%s",
asset.spawngroupname, tostring(isRequested), tostring(isReserved), tostring(isSpawned), tostring(isOnMission)))
-- First check that asset is not requested or reserved. This could happen if multiple requests are processed simultaniously.
if not (asset.requested or asset.isReserved) then
if not (isRequested or isReserved) then
--self:I("Not requested or reserved")
-- Check if asset is currently on a mission (STARTED or QUEUED).
if self.legion:IsAssetOnMission(asset) then
if self.legion:IsAssetOnMission(asset) then
---
-- Asset is already on a mission.
---
-- Check if this asset is currently on a GCICAP mission (STARTED or EXECUTING).
if self.legion:IsAssetOnMission(asset, AUFTRAG.Type.GCICAP) and MissionType==AUFTRAG.Type.INTERCEPT then
if MissionType==AUFTRAG.Type.RELOCATECOHORT then
-- Relocation: Take all assets. Mission will be cancelled.
table.insert(assets, asset)
elseif self.legion:IsAssetOnMission(asset, AUFTRAG.Type.GCICAP) and MissionType==AUFTRAG.Type.INTERCEPT then
-- Check if the payload of this asset is compatible with the mission.
-- Note: we do not check the payload as an asset that is on a GCICAP mission should be able to do an INTERCEPT as well!
self:T(self.lid..string.format("Adding asset on GCICAP mission for an INTERCEPT mission"))
table.insert(assets, asset)
elseif self.legion:IsAssetOnMission(asset, AUFTRAG.Type.ONGUARD) and (MissionType==AUFTRAG.Type.ARTY or MissionType==AUFTRAG.Type.GROUNDATTACK) then
if not opsgroup:IsOutOfAmmo() then
self:T(self.lid..string.format("Adding asset on ONGUARD mission for an XXX mission"))
table.insert(assets, asset)
end
elseif self.legion:IsAssetOnMission(asset, AUFTRAG.Type.PATROLZONE) and (MissionType==AUFTRAG.Type.ARTY or MissionType==AUFTRAG.Type.GROUNDATTACK) then
if not opsgroup:IsOutOfAmmo() then
self:T(self.lid..string.format("Adding asset on PATROLZONE mission for an XXX mission"))
table.insert(assets, asset)
end
elseif self.legion:IsAssetOnMission(asset, AUFTRAG.Type.ALERT5) and AUFTRAG.CheckMissionCapability(MissionType, asset.payload.capabilities) then
-- Check if the payload of this asset is compatible with the mission.
self:T(self.lid..string.format("Adding asset on ALERT 5 mission for %s mission", MissionType))
table.insert(assets, asset)
table.insert(assets, asset)
end
else

View File

@ -2,7 +2,7 @@
--
-- **Main Features:**
--
-- * Manages AIRWINGS, BRIGADEs and FLOTILLAs
-- * Manages AIRWINGS, BRIGADEs and FLEETs
-- * Handles missions (AUFTRAG) and finds the best assets for the job
--
-- ===
@ -95,7 +95,7 @@
--
-- The COMMANDER will
--
-- # OPSGROUP on Mission
-- ## OPSGROUP on Mission
--
-- Whenever an OPSGROUP (FLIGHTGROUP, ARMYGROUP or NAVYGROUP) is send on a mission, the `OnAfterOpsOnMission()` event is triggered.
-- Mission designers can hook into the event with the @{#COMMANDER.OnAfterOpsOnMission}() function
@ -104,7 +104,7 @@
-- -- Your code
-- end
--
-- # Canceling a Mission
-- ## Canceling a Mission
--
-- A mission can be cancelled with the @{#COMMMANDER.MissionCancel}() function
--
@ -136,7 +136,7 @@ COMMANDER = {
--- COMMANDER class version.
-- @field #string version
COMMANDER.version="0.1.1"
COMMANDER.version="0.1.2"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list
@ -398,7 +398,7 @@ function COMMANDER:AddAirwing(Airwing)
return self
end
--- Add an BRIGADE to the commander.
--- Add a BRIGADE to the commander.
-- @param #COMMANDER self
-- @param Ops.Brigade#BRIGADE Brigade The brigade to add.
-- @return #COMMANDER self
@ -410,6 +410,19 @@ function COMMANDER:AddBrigade(Brigade)
return self
end
--- Add a FLEET to the commander.
-- @param #COMMANDER self
-- @param Ops.Fleet#FLEET Fleet The fleet to add.
-- @return #COMMANDER self
function COMMANDER:AddFleet(Fleet)
-- Add legion.
self:AddLegion(Fleet)
return self
end
--- Add a LEGION to the commander.
-- @param #COMMANDER self
-- @param Ops.Legion#LEGION Legion The legion to add.
@ -653,6 +666,83 @@ function COMMANDER:IsMission(Mission)
return false
end
--- Relocate a cohort to another legion.
-- Assets in stock are spawned and routed to the new legion.
-- If assets are spawned, running missions will be cancelled.
-- Cohort assets will not be available until relocation is finished.
-- @param #COMMANDER self
-- @param Ops.Cohort#COHORT Cohort The cohort to be relocated.
-- @param Ops.Legion#LEGION Legion The legion where the cohort is relocated to.
-- @param #number Delay Delay in seconds before relocation takes place. Default `nil`, *i.e.* ASAP.
-- @param #number NcarriersMin Min number of transport carriers in case the troops should be transported. Default `nil` for no transport.
-- @param #number NcarriersMax Max number of transport carriers.
-- @param #table TransportLegions Legion(s) assigned for transportation. Default is all legions of the commander.
-- @return #COMMANDER self
function COMMANDER:RelocateCohort(Cohort, Legion, Delay, NcarriersMin, NcarriersMax, TransportLegions)
if Delay and Delay>0 then
self:ScheduleOnce(Delay, COMMANDER.RelocateCohort, self, Cohort, Legion, 0, NcarriersMin, NcarriersMax, TransportLegions)
else
-- Add cohort to legion.
if Legion:IsCohort(Cohort.name) then
self:E(self.lid..string.format("ERROR: Cohort %s is already part of new legion %s ==> CANNOT Relocate!", Cohort.name, Legion.alias))
return self
else
table.insert(Legion.cohorts, Cohort)
end
-- Old legion.
local LegionOld=Cohort.legion
-- Check that cohort is part of this legion
if not LegionOld:IsCohort(Cohort.name) then
self:E(self.lid..string.format("ERROR: Cohort %s is NOT part of this legion %s ==> CANNOT Relocate!", Cohort.name, self.alias))
return self
end
-- Check that legions are different.
if LegionOld.alias==Legion.alias then
self:E(self.lid..string.format("ERROR: old legion %s is same as new legion %s ==> CANNOT Relocate!", LegionOld.alias, Legion.alias))
return self
end
-- Trigger Relocate event.
Cohort:Relocate()
-- Create a relocation mission.
local mission=AUFTRAG:_NewRELOCATECOHORT(Legion, Cohort)
-- Assign cohort to mission.
mission:AssignCohort(Cohort)
-- All assets required.
mission:SetRequiredAssets(#Cohort.assets)
-- Set transportation.
if NcarriersMin and NcarriersMin>0 then
mission:SetRequiredTransport(Legion.spawnzone, NcarriersMin, NcarriersMax)
end
-- Assign transport legions.
if TransportLegions then
for _,legion in pairs(TransportLegions) do
mission:AssignTransportLegion(legion)
end
else
for _,legion in pairs(self.legions) do
mission:AssignTransportLegion(legion)
end
end
-- Add mission.
self:AddMission(mission)
end
return self
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Start & Status
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@ -974,7 +1064,7 @@ function COMMANDER:onafterMissionCancel(From, Event, To, Mission)
end
--- On after "TransportAssign" event. Transport is added to a LEGION mission queue.
--- On after "TransportAssign" event. Transport is added to a LEGION transport queue.
-- @param #COMMANDER self
-- @param #string From From state.
-- @param #string Event Event.
@ -1215,7 +1305,8 @@ function COMMANDER:RecruitAssetsForMission(Mission)
local Payloads=Mission.payloads
-- Recruite assets.
local recruited, assets, legions=LEGION.RecruitCohortAssets(Cohorts, Mission.type, Mission.alert5MissionType, NreqMin, NreqMax, TargetVec2, Payloads, Mission.engageRange, Mission.refuelSystem)
local recruited, assets, legions=LEGION.RecruitCohortAssets(Cohorts, Mission.type, Mission.alert5MissionType, NreqMin, NreqMax, TargetVec2, Payloads,
Mission.engageRange, Mission.refuelSystem, nil, nil, nil, Mission.attributes, Mission.properties, {Mission.engageWeaponType})
return recruited, assets, legions
end

View File

@ -867,7 +867,7 @@ function FLIGHTGROUP:Status()
local ammo=self:GetAmmoTot().Total
-- Detected units.
local ndetected=self.detectionOn and tostring(self.detectedunits:Count()) or "OFF"
local ndetected=self.detectionOn and tostring(self.detectedunits:Count()) or "Off"
-- Get cargo weight.
local cargo=0

View File

@ -430,13 +430,16 @@ end
-- Cohort assets will not be available until relocation is finished.
-- @param #LEGION self
-- @param Ops.Cohort#COHORT Cohort The cohort to be relocated.
-- @param Ops.Legion#LEGION Legion.
-- @param #number Delay Delay in seconds before relocation takes place. Default 0 sec.
-- @param Ops.Legion#LEGION Legion The legion where the cohort is relocated to.
-- @param #number Delay Delay in seconds before relocation takes place. Default `nil`, *i.e.* ASAP.
-- @param #number NcarriersMin Min number of transport carriers in case the troops should be transported. Default `nil` for no transport.
-- @param #number NcarriersMax Max number of transport carriers.
-- @param #table TransportLegions Legion(s) assigned for transportation. Default is that transport assets can only be recruited from this legion.
-- @return #LEGION self
function LEGION:RelocateCohort(Cohort, Legion, Delay)
function LEGION:RelocateCohort(Cohort, Legion, Delay, NcarriersMin, NcarriersMax, TransportLegions)
if Delay and Delay>0 then
self:ScheduleOnce(Delay, LEGION.RelocateCohort, self, Cohort, Legion, 0)
self:ScheduleOnce(Delay, LEGION.RelocateCohort, self, Cohort, Legion, 0, NcarriersMin, NcarriersMax, TransportLegions)
else
-- Add cohort to legion.
@ -465,17 +468,45 @@ function LEGION:RelocateCohort(Cohort, Legion, Delay)
-- Create a relocation mission.
local mission=AUFTRAG:_NewRELOCATECOHORT(Legion, Cohort)
-- Add assets to mission.
mission:_AddAssets(Cohort.assets)
-- Debug info.
self:I(self.lid..string.format("Relocating Cohort %s [nassets=%d] to legion %s", Cohort.name, #Cohort.assets, Legion.alias))
if false then
--- Disabled for now.
-- Assign mission to this legion.
self:MissionAssign(mission, {self})
-- Add assets to mission.
mission:_AddAssets(Cohort.assets)
-- Debug info.
self:I(self.lid..string.format("Relocating Cohort %s [nassets=%d] to legion %s", Cohort.name, #Cohort.assets, Legion.alias))
-- Assign mission to this legion.
self:MissionAssign(mission, {self})
else
-- Assign cohort to mission.
mission:AssignCohort(Cohort)
-- All assets required.
mission:SetRequiredAssets(#Cohort.assets)
-- Set transportation.
if NcarriersMin and NcarriersMin>0 then
mission:SetRequiredTransport(Legion.spawnzone, NcarriersMin, NcarriersMax)
end
-- Assign transport legions.
if TransportLegions then
for _,legion in pairs(TransportLegions) do
mission:AssignTransportLegion(legion)
end
end
-- Add mission.
self:AddMission(mission)
end
end
return self
end
--- Get cohort by name.
@ -810,7 +841,7 @@ function LEGION:onafterMissionRequest(From, Event, To, Mission)
---
if asset.flightgroup then
-- Add new mission.
asset.flightgroup:AddMission(Mission)
@ -822,27 +853,29 @@ function LEGION:onafterMissionRequest(From, Event, To, Mission)
local currM=asset.flightgroup:GetMissionCurrent()
if currM then
-- Cancel?
local cancel=false
-- Pause?
local pause=false
-- Check if mission is INTERCEPT and asset is currently on GCI mission. If so, GCI is paused.
if currM.type==AUFTRAG.Type.GCICAP and Mission.type==AUFTRAG.Type.INTERCEPT then
self:T(self.lid..string.format("Pausing %s mission %s to send flight on intercept mission %s", currM.type, currM.name, Mission.name))
asset.flightgroup:PauseMission()
pause=true
elseif (currM.type==AUFTRAG.Type.ONGUARD or currM.type==AUFTRAG.Type.PATROLZONE) and (Mission.type==AUFTRAG.Type.ARTY or Mission.type==AUFTRAG.Type.GROUNDATTACK) then
pause=true
end
-- Cancel current ALERT5 mission
-- Cancel current ALERT5 mission.
if currM.type==AUFTRAG.Type.ALERT5 then
cancel=true
end
-- Cancel the current mission.
if currM.type==AUFTRAG.Type.ONGUARD or currM.type==AUFTRAG.Type.ARMOREDGUARD then
cancel=true
end
-- Cancel current mission for relcation.
if Mission.type==AUFTRAG.Type.RELOCATECOHORT then
cancel=true
-- Get request ID.
local requestID=currM.requestID[self.alias]
-- Get request.
@ -853,18 +886,26 @@ function LEGION:onafterMissionRequest(From, Event, To, Mission)
request.cargogroupset:Remove(asset.spawngroupname, true)
else
self:E(self.lid.."ERROR: no request for spawned asset!")
end
end
end
-- Cancel mission.
if cancel then
self:T(self.lid..string.format("Cancel current mission %s [%s] to send group on mission %s [%s]", currM.name, currM.type, Mission.name, Mission.type))
asset.flightgroup:MissionCancel(currM)
elseif pause then
self:T(self.lid..string.format("Pausing current mission %s [%s] to send group on mission %s [%s]", currM.name, currM.type, Mission.name, Mission.type))
asset.flightgroup:PauseMission()
end
-- Not reserved any more.
asset.isReserved=false
end
-- Trigger event.
self:__OpsOnMission(5, asset.flightgroup, Mission)
self:__OpsOnMission(2, asset.flightgroup, Mission)
else
self:E(self.lid.."ERROR: OPSGROUP for asset does NOT exist but it seems to be SPAWNED (asset.spawned=true)!")
@ -893,6 +934,13 @@ function LEGION:onafterMissionRequest(From, Event, To, Mission)
-- Set asset to requested! Important so that new requests do not use this asset!
asset.requested=true
-- Spawned asset are not requested.
if asset.spawned then
asset.requested=false
end
-- Not reserved and more.
asset.isReserved=false
-- Set mission task so that the group is spawned with the right one.
@ -1344,7 +1392,6 @@ function LEGION:onafterAssetSpawned(From, Event, To, group, asset, request)
local Tacan=cohort:FetchTacan()
if Tacan then
asset.tacan=Tacan
--flightgroup:SetDefaultTACAN(Tacan,Morse,UnitName,Band,OffSwitch)
flightgroup:SwitchTACAN(Tacan, Morse, UnitName, Band)
end
@ -1400,6 +1447,11 @@ function LEGION:onafterAssetSpawned(From, Event, To, group, asset, request)
-- Add mission to flightgroup queue. If mission has an OPSTRANSPORT attached, all added OPSGROUPS are added as CARGO for a transport.
flightgroup:AddMission(mission)
-- RTZ on out of ammo.
if self:IsBrigade() or self:IsFleet() then
flightgroup:SetReturnOnOutOfAmmo()
end
-- Trigger event.
self:__OpsOnMission(5, flightgroup, mission)
@ -1669,26 +1721,7 @@ function LEGION:IsAssetOnMission(asset, MissionTypes)
end
end
-- Alternative: run over all missions and compare to mission assets.
--[[
for _,_mission in pairs(self.missionqueue) do
local mission=_mission --Ops.Auftrag#AUFTRAG
if mission:IsNotOver() then
for _,_asset in pairs(mission.assets) do
local sqasset=_asset --Functional.Warehouse#WAREHOUSE.Assetitem
if sqasset.uid==asset.uid then
return true
end
end
end
end
]]
return false
end
@ -1907,7 +1940,6 @@ function LEGION:CountAssetsOnMission(MissionTypes, Cohort)
end
end
--env.info(string.format("FF N=%d Np=%d, Nq=%d", Np+Nq, Np, Nq))
return Np+Nq, Np, Nq
end
@ -2043,11 +2075,11 @@ function LEGION:RecruitAssetsForMission(Mission)
-- No escort cohorts/legions given ==> take own cohorts.
if #Cohorts==0 then
Cohorts=self.cohorts
end
end
-- Recuit assets.
local recruited, assets, legions=LEGION.RecruitCohortAssets(Cohorts, Mission.type, Mission.alert5MissionType, NreqMin, NreqMax, TargetVec2, Payloads,
Mission.engageRange, Mission.refuelSystem, nil, nil, nil, nil, nil, {Mission.engageWeaponType})
Mission.engageRange, Mission.refuelSystem, nil, nil, nil, Mission.attributes, Mission.properties, {Mission.engageWeaponType})
return recruited, assets, legions
end
@ -2226,10 +2258,14 @@ function LEGION.RecruitCohortAssets(Cohorts, MissionTypeRecruit, MissionTypeOpt,
local cohort=_cohort --Ops.Cohort#COHORT
if WeaponTypes and #WeaponTypes>0 then
for _,WeaponType in pairs(WeaponTypes) do
for _,_weaponData in pairs(cohort.weaponData or {}) do
local weaponData=_weaponData --Ops.OpsGroup#OPSGROUP.WeaponData
if weaponData.BitType==WeaponType then
return true
if WeaponType==ENUMS.WeaponFlag.Auto then
return true
else
for _,_weaponData in pairs(cohort.weaponData or {}) do
local weaponData=_weaponData --Ops.OpsGroup#OPSGROUP.WeaponData
if weaponData.BitType==WeaponType then
return true
end
end
end
end
@ -2262,9 +2298,7 @@ function LEGION.RecruitCohortAssets(Cohorts, MissionTypeRecruit, MissionTypeOpt,
Refuel=false
end
end
--env.info(string.format("Cohort=%s: RefuelSystem=%s, TankerSystem=%s ==> Refuel=%s", cohort.name, tostring(RefuelSystem), tostring(cohort.tankerSystem), tostring(Refuel)))
-- Is capable of the mission type?
local Capable=AUFTRAG.CheckMissionCapability({MissionTypeRecruit}, cohort.missiontypes)
@ -2283,12 +2317,19 @@ function LEGION.RecruitCohortAssets(Cohorts, MissionTypeRecruit, MissionTypeOpt,
-- Right weapon type.
local RightWeapon=CheckWeapon(cohort)
-- Cohort ready to execute mission.
local Ready=cohort:IsOnDuty()
if MissionTypeRecruit==AUFTRAG.Type.RELOCATECOHORT then
Ready=cohort:IsRelocating()
Capable=true
end
-- Debug info.
cohort:T2(cohort.lid..string.format("State=%s: Capable=%s, InRange=%s, Refuel=%s, CanCarry=%s, Category=%s, Attribute=%s, Property=%s, Weapon=%s",
cohort:GetState(), tostring(Capable), tostring(InRange), tostring(Refuel), tostring(CanCarry), tostring(RightCategory), tostring(RightAttribute), tostring(RightProperty), tostring(RightWeapon)))
-- Check OnDuty, capable, in range and refueling type (if TANKER).
if cohort:IsOnDuty() and Capable and InRange and Refuel and CanCarry and RightCategory and RightAttribute and RightProperty and RightWeapon then
if Ready and Capable and InRange and Refuel and CanCarry and RightCategory and RightAttribute and RightProperty and RightWeapon then
-- Recruit assets from cohort.
local assets, npayloads=cohort:RecruitAssets(MissionTypeRecruit, 999)
@ -2702,9 +2743,10 @@ function LEGION.CalculateAssetMissionScore(asset, MissionType, TargetVec2, Inclu
-- Reduce score for legions that are futher away.
score=score-distance
-- Intercepts need to be carried out quickly. We prefer spawned assets.
-- Check for spawned assets.
if asset.spawned and asset.flightgroup and asset.flightgroup:IsAlive() then
-- Get current mission.
local currmission=asset.flightgroup:GetMissionCurrent()
if currmission then
@ -2712,10 +2754,13 @@ function LEGION.CalculateAssetMissionScore(asset, MissionType, TargetVec2, Inclu
if currmission.type==AUFTRAG.Type.ALERT5 and currmission.alert5MissionType==MissionType then
-- Prefer assets that are on ALERT5 for this mission type.
score=score+25
elseif currmission==AUFTRAG.Type.GCICAP and MissionType==AUFTRAG.Type.INTERCEPT then
elseif currmission.type==AUFTRAG.Type.GCICAP and MissionType==AUFTRAG.Type.INTERCEPT then
-- Prefer assets that are on GCICAP to perform INTERCEPTS
score=score+25
elseif (currmission.type==AUFTRAG.Type.ONGUARD or currmission.type==AUFTRAG.Type.PATROLZONE) and (MissionType==AUFTRAG.Type.ARTY or MissionType==AUFTRAG.Type.GROUNDATTACK) then
score=score+25
end
end
if MissionType==AUFTRAG.Type.OPSTRANSPORT or MissionType==AUFTRAG.Type.AMMOSUPPLY or MissionType==AUFTRAG.Type.AWACS or MissionType==AUFTRAG.Type.FUELSUPPLY or MissionType==AUFTRAG.Type.TANKER then
@ -2742,6 +2787,10 @@ function LEGION.CalculateAssetMissionScore(asset, MissionType, TargetVec2, Inclu
-- Max speed of assets.
-- Fuel amount?
-- Range of assets?
if asset.legion and asset.legion.verbose>=2 then
asset.legion:I(asset.legion.lid..string.format("Asset %s [spawned=%s] score=%d", asset.spawngroupname, tostring(asset.spawned), score))
end
return score
end

View File

@ -828,7 +828,7 @@ function NAVYGROUP:Status(From, Event, To)
local ammo=self:GetAmmoTot().Total
-- Detected units.
local ndetected=self.detectionOn and tostring(self.detectedunits:Count()) or "OFF"
local ndetected=self.detectionOn and tostring(self.detectedunits:Count()) or "Off"
-- Get cargo weight.
local cargo=0

View File

@ -18,6 +18,7 @@
-- @field #string lid Class id string for output to DCS log file.
-- @field #string groupname Name of the group.
-- @field Wrapper.Group#GROUP group Group object.
-- @field DCS#Group dcsgroup The DCS group object.
-- @field DCS#Controller controller The DCS controller of the group.
-- @field DCS#Template template Template table of the group.
-- @field #table elements Table of elements, i.e. units of the group.
@ -57,6 +58,7 @@
-- @field #number speedMax Max speed in km/h.
-- @field #number speedCruise Cruising speed in km/h.
-- @field #number speedWp Speed to the next waypoint in m/s.
-- @field #boolean isMobile If `true`, group is mobile (speed > 1 m/s)
-- @field #boolean passedfinalwp Group has passed the final waypoint.
-- @field #number wpcounter Running number counting waypoints.
-- @field Core.Set#SET_ZONE checkzones Set of zones.
@ -200,6 +202,7 @@ OPSGROUP = {
-- @field Wrapper.Unit#UNIT unit The UNIT object.
-- @field Wrapper.Group#GROUP group The GROUP object.
-- @field DCS#Unit DCSunit The DCS unit object.
-- @field DCS#Controller controller The DCS controller of the unit.
-- @field #boolean ai If true, element is AI.
-- @field #string skill Skill level.
--
@ -466,7 +469,7 @@ OPSGROUP.CargoStatus={
--- OpsGroup version.
-- @field #string version
OPSGROUP.version="0.7.7"
OPSGROUP.version="0.7.8"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list
@ -1045,6 +1048,108 @@ function OPSGROUP:SetDetection(Switch)
return self
end
--- Get DCS group object.
-- @param #OPSGROUP self
-- @return DCS#Group DCS group object.
function OPSGROUP:GetDCSObject()
return self.dcsgroup
end
--- Set detection on or off.
-- If detection is on, detected targets of the group will be evaluated and FSM events triggered.
-- @param #OPSGROUP self
-- @param Wrapper.Positionable#POSITIONABLE TargetObject The target object.
-- @param #boolean KnowType Make type known.
-- @param #boolean KnowDist Make distance known.
-- @param #number Delay Delay in seconds before the target is known.
-- @return #OPSGROUP self
function OPSGROUP:KnowTarget(TargetObject, KnowType, KnowDist, Delay)
if Delay and Delay>0 then
-- Delayed call.
self:ScheduleOnce(Delay, OPSGROUP.KnowTarget, self, TargetObject, KnowType, KnowDist, 0)
else
if TargetObject:IsInstanceOf("GROUP") then
TargetObject=TargetObject:GetUnit(1)
elseif TargetObject:IsInstanceOf("OPSGROUP") then
TargetObject=TargetObject.group:GetUnit(1)
end
-- Get the DCS object.
local object=TargetObject:GetDCSObject()
for _,_element in pairs(self.elements) do
local element=_element --#OPSGROUP.Element
if element.controller then
element.controller:knowTarget(object, true, true)
--self:T(self.lid..string.format("Element %s should now know target %s", element.name, TargetObject:GetName()))
end
end
-- Debug info.
self:T(self.lid..string.format("We should now know target %s", TargetObject:GetName()))
end
return self
end
--- Check if target is detected.
-- @param #OPSGROUP self
-- @param Wrapper.Positionable#POSITIONABLE TargetObject The target object.
-- @return #boolean If `true`, target was detected.
function OPSGROUP:IsTargetDetected(TargetObject)
local objects={}
if TargetObject:IsInstanceOf("GROUP") then
for _,unit in pairs(TargetObject:GetUnits()) do
table.insert(objects, unit:GetDCSObject())
end
elseif TargetObject:IsInstanceOf("OPSGROUP") then
for _,unit in pairs(TargetObject.group:GetUnits()) do
table.insert(objects, unit:GetDCSObject())
end
elseif TargetObject:IsInstanceOf("UNIT") or TargetObject:IsInstanceOf("STATIC") then
table.insert(objects, TargetObject:GetDCSObject())
end
for _,object in pairs(objects or {}) do
-- Check group controller.
local detected, visible, lastTime, type, distance, lastPos, lastVel = self.controller:isTargetDetected(object, 1, 2, 4, 8, 16, 32)
--env.info(self.lid..string.format("Detected target %s: %s", TargetObject:GetName(), tostring(detected)))
if detected then
return true
end
-- Check all elements.
for _,_element in pairs(self.elements) do
local element=_element --#OPSGROUP.Element
if element.controller then
-- Check.
local detected, visible, lastTime, type, distance, lastPos, lastVel=
element.controller:isTargetDetected(object, 1, 2, 4, 8, 16, 32)
--env.info(self.lid..string.format("Element %s detected target %s: %s", element.name, TargetObject:GetName(), tostring(detected)))
if detected then
return true
end
end
end
end
return false
end
--- Set LASER parameters.
-- @param #OPSGROUP self
-- @param #number Code Laser code. Default 1688.
@ -1285,6 +1390,23 @@ function OPSGROUP:SetRearmOnOutOfAmmo()
return self
end
--- Set that group is retreating once it runs out of ammo.
-- @param #OPSGROUP self
-- @return #OPSGROUP self
function OPSGROUP:SetRetreatOnOutOfAmmo()
self.retreatOnOutOfAmmo=true
return self
end
--- Set that group is return to legion once it runs out of ammo.
-- @param #OPSGROUP self
-- @return #OPSGROUP self
function OPSGROUP:SetReturnOnOutOfAmmo()
self.rtzOnOutOfAmmo=true
return self
end
--- Check if an element of the group has line of sight to a coordinate.
-- @param #OPSGROUP self
-- @param Core.Point#COORDINATE Coordinate The position to which we check the LoS.
@ -3633,6 +3755,29 @@ function OPSGROUP:onbeforeTaskExecute(From, Event, To, Task)
end
end
if Mission and Mission.opstransport then
local delivered=Mission.opstransport:IsCargoDelivered(self.groupname)
if not delivered then
local dt=30
-- Debug info.
self:T(self.lid..string.format("Mission %s task execute suspended for %d seconds because we were not delivered", Mission.name, dt))
-- Reexecute task.
self:__TaskExecute(-dt, Task)
if (self:IsArmygroup() or self:IsNavygroup()) and self:IsCruising() then
self:FullStop()
end
-- Deny transition.
return false
end
end
return true
end
@ -3817,6 +3962,21 @@ function OPSGROUP:onafterTaskExecute(From, Event, To, Task)
-- FLIGHTGROUP not implemented (intended!) for this AUFTRAG type.
end
elseif Task.dcstask.id==AUFTRAG.SpecialTask.AIRDEFENSE or Task.dcstask.id==AUFTRAG.SpecialTask.EWR then
---
-- Task "AIRDEFENSE" or "EWR" Mission.
---
-- Just stay put.
--TODO: Change ALARM STATE
if self:IsArmygroup() or self:IsNavygroup() then
self:FullStop()
else
-- FLIGHTGROUP not implemented (intended!) for this AUFTRAG type.
end
elseif Task.dcstask.id==AUFTRAG.SpecialTask.GROUNDATTACK or Task.dcstask.id==AUFTRAG.SpecialTask.ARMORATTACK then
---
@ -3903,20 +4063,67 @@ function OPSGROUP:onafterTaskExecute(From, Event, To, Task)
-- If task is scheduled (not waypoint) set task.
if Task.type==OPSGROUP.TaskType.SCHEDULED or Task.ismission then
local DCSTask=nil --UTILS.DeepCopy(Task.dcstask)
-- DCS task.
local DCSTask=nil
-- BARRAGE is special!
if Task.dcstask.id==AUFTRAG.SpecialTask.BARRAGE then
---
-- BARRAGE
-- Current vec2.
local vec2=self:GetVec2()
-- Task parameters.
local param=Task.dcstask.params
-- Set heading and altitude.
local heading=param.heading or math.random(1, 360)
local Altitude=param.altitude or 500
local Alpha=param.angle or math.random(45, 85)
local distance=Altitude/math.tan(math.rad(Alpha))
local tvec2=UTILS.Vec2Translate(vec2, distance, heading)
-- Debug info.
self:T(self.lid..string.format("Barrage: Shots=%s, Altitude=%d m, Angle=%d°, heading=%03d°, distance=%d m", tostring(param.shots), Altitude, Alpha, heading, distance))
-- Set fire at point task.
DCSTask=CONTROLLABLE.TaskFireAtPoint(nil, tvec2, param.radius, param.shots, param.weaponType, Altitude)
elseif Task.ismission and Task.dcstask.id=='FireAtPoint' then
-- Copy DCS task.
DCSTask=UTILS.DeepCopy(Task.dcstask)
-- Get current ammo.
local ammo=self:GetAmmoTot()
-- Number of ammo avail.
local nAmmo=ammo.Total
if DCSTask.params.weaponType then
--TODO: use weapon type infor, e.g. for cruise missiles
end
--TODO: Update target location while we're at it anyway.
--TODO: Adjust mission result evaluation time? E.g. cruise missiles can fly a long time depending on target distance.
-- Number of shots to be fired.
local nShots=DCSTask.params.expendQty or 1
-- Debug info.
self:T(self.lid..string.format("Fire at point with nshots=%d of %d", nShots, nAmmo))
-- Only fire number of avail shots.
nShots=math.min(nShots, nAmmo)
-- Set quantity of task.
DCSTask.params.expendQty=nShots
else
---
-- Take DCS task
---
DCSTask=Task.dcstask
end
@ -4126,12 +4333,12 @@ function OPSGROUP:onafterTaskDone(From, Event, To, Task)
else
if Task.description=="Engage_Target" then
self:T(self.lid.."Taske DONE Engage_Target ==> Cruise")
self:T(self.lid.."Task DONE Engage_Target ==> Cruise")
self:Disengage()
end
if Task.description==AUFTRAG.SpecialTask.ONGUARD or Task.description==AUFTRAG.SpecialTask.ARMOREDGUARD then
self:T(self.lid.."Taske DONE OnGuard ==> Cruise")
self:T(self.lid.."Task DONE OnGuard ==> Cruise")
self:Cruise()
end
@ -4173,7 +4380,6 @@ function OPSGROUP:AddMission(Mission)
table.insert(self.missionqueue, Mission)
-- ad infinitum?
self.adinfinitum = Mission.DCStask.params.adinfinitum and Mission.DCStask.params.adinfinitum or false
-- Info text.
@ -4335,7 +4541,7 @@ function OPSGROUP:_GetNextMission()
for _,_opsgroup in pairs(cargos) do
local opscargo=_opsgroup --Ops.OpsGroup#OPSGROUP
if opscargo.groupname==self.groupname then
isTransport=false
--isTransport=false
break
end
end
@ -4345,9 +4551,16 @@ function OPSGROUP:_GetNextMission()
local isScheduled=mission:GetGroupStatus(self)==AUFTRAG.GroupStatus.SCHEDULED
local isReadyToGo=(mission:IsReadyToGo() or self.legion)
local isImportant=(mission.importance==nil or mission.importance<=vip)
-- Everything on go?
local go=isScheduled and isReadyToGo and isImportant and isTransport and isEscort
-- Debug info.
self:T3(self.lid..string.format("Mission %s [%s]: Go=%s [Scheduled=%s, Ready=%s, Important=%s, Transport=%s, Escort=%s]", mission:GetName(), mission:GetType(), tostring(go),
tostring(isScheduled), tostring(isReadyToGo), tostring(isImportant), tostring(isTransport), tostring(isEscort)))
-- Check necessary conditions.
if isScheduled and isReadyToGo and isImportant and isTransport and isEscort then
if go then
return mission
end
@ -4483,7 +4696,7 @@ function OPSGROUP:onafterMissionStart(From, Event, To, Mission)
Mission:__Started(3)
-- Route group to mission zone.
if self.speedMax>3.6 then
if self.speedMax>3.6 or true then
self:RouteToMission(Mission, 3)
@ -4492,10 +4705,12 @@ function OPSGROUP:onafterMissionStart(From, Event, To, Mission)
-- IMMOBILE Group
---
--env.info("FF Immobile GROUP")
env.info(self.lid.."FF Immobile GROUP")
-- Add waypoint task. UpdateRoute is called inside.
local Clock=Mission.Tpush and UTILS.SecondsToClock(Mission.Tpush) or 5
-- Add mission task.
local Task=self:AddTask(Mission.DCStask, Clock, Mission.name, Mission.prio, Mission.duration)
Task.ismission=true
@ -4554,6 +4769,8 @@ function OPSGROUP:onafterPauseMission(From, Event, To)
-- Cancelling the mission is actually cancelling the current task.
self:TaskCancel(Task)
self:_RemoveMissionWaypoints(Mission)
-- Set mission to pause so we can unpause it later.
self.missionpaused=Mission
@ -4603,8 +4820,14 @@ function OPSGROUP:onafterMissionCancel(From, Event, To, Mission)
---
-- Alert 5 missoins dont have a task set, which could be cancelled.
if Mission.type==AUFTRAG.Type.ALERT5 or Mission.type==AUFTRAG.Type.ONGUARD or Mission.type==AUFTRAG.Type.ARMOREDGUARD then
if Mission.type==AUFTRAG.Type.ALERT5 or
Mission.type==AUFTRAG.Type.ONGUARD or
Mission.type==AUFTRAG.Type.ARMOREDGUARD or
Mission.type==AUFTRAG.Type.AIRDEFENSE or
Mission.type==AUFTRAG.Type.EWR then
self:MissionDone(Mission)
return
end
@ -4705,10 +4928,14 @@ function OPSGROUP:onafterMissionDone(From, Event, To, Mission)
if Mission.optionAlarm then
self:SwitchAlarmstate()
end
-- Alarm state to default.
-- EPLRS to default.
if Mission.optionEPLRS then
self:SwitchEPLRS()
end
-- Emission to default.
if Mission.optionEmission then
self:SwitchEmission()
end
-- Formation to default.
if Mission.optionFormation then
self:SwitchFormation()
@ -4821,7 +5048,10 @@ function OPSGROUP:RouteToMission(mission, delay)
-- Ingress waypoint coordinate where the mission is executed.
local waypointcoord=nil --Core.Point#COORDINATE
-- Target zone.
local targetzone=nil --Core.Zone#ZONE
-- Random radius of 1000 meters.
local randomradius=mission.missionWaypointRadius or 1000
@ -4832,27 +5062,81 @@ function OPSGROUP:RouteToMission(mission, delay)
elseif self:IsNavygroup() then
surfacetypes={land.SurfaceType.WATER, land.SurfaceType.SHALLOW_WATER}
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
local zone=mission.engageTarget:GetObject() --Core.Zone#ZONE
waypointcoord=zone:GetRandomCoordinate(nil , nil, surfacetypes)
-- Get ingress waypoint.
if mission.opstransport and not mission.opstransport:IsCargoDelivered(self.groupname) then
--env.info(self.lid.."FF mission waypoint in embark zone")
-- Get transport zone combo.
local tzc=mission.opstransport:GetTZCofCargo(self.groupname)
local pickupzone=tzc.PickupZone
if self:IsInZone(pickupzone) then
-- We are already in the pickup zone.
self:PauseMission()
self:FullStop()
return
else
-- Get a random coordinate inside the pickup zone.
waypointcoord=pickupzone:GetRandomCoordinate()
--waypointcoord:MarkToAll(self.lid.." embark here")
end
elseif mission.type==AUFTRAG.Type.PATROLZONE or
mission.type==AUFTRAG.Type.BARRAGE or
mission.type==AUFTRAG.Type.AMMOSUPPLY or
mission.type==AUFTRAG.Type.FUELSUPPLY or
mission.type==AUFTRAG.Type.AIRDEFENSE or
mission.type==AUFTRAG.Type.EWR then
---
-- Missions with ZONE as target
---
-- Get the zone.
targetzone=mission.engageTarget:GetObject() --Core.Zone#ZONE
-- Random coordinate.
waypointcoord=targetzone:GetRandomCoordinate(nil , nil, surfacetypes)
elseif mission.type==AUFTRAG.Type.ONGUARD or mission.type==AUFTRAG.Type.ARMOREDGUARD then
---
-- Guard
---
-- Mission waypoint
waypointcoord=mission:GetMissionWaypointCoord(self.group, nil, surfacetypes)
elseif mission.type==AUFTRAG.Type.RELOCATECOHORT then
elseif mission.type==AUFTRAG.Type.HOVER then
---
-- Hover
---
local zone=mission.engageTarget:GetObject() --Core.Zone#ZONE
waypointcoord=zone:GetCoordinate()
elseif mission.type==AUFTRAG.Type.RELOCATECOHORT then
---
-- Relocation
---
-- Roughly go to the new legion.
local ToCoordinate=mission.DCStask.params.legion:GetCoordinate()
if self.isFlightgroup then
waypointcoord=self:GetCoordinate():GetIntermediateCoordinate(ToCoordinate, 0.2):SetAltitude(self.altitudeCruise)
else
waypointcoord=self:GetCoordinate():GetIntermediateCoordinate(ToCoordinate, 0.05)
end
else
waypointcoord=mission:GetMissionWaypointCoord(self.group, randomradius, surfacetypes)
end
if mission.type==AUFTRAG.Type.HOVER then
local zone=mission.engageTarget:GetObject() --Core.Zone#ZONE
waypointcoord=zone:GetCoordinate()
---
-- Default case
---
waypointcoord=mission:GetMissionWaypointCoord(self.group, randomradius, surfacetypes)
end
-- Add enroute tasks.
@ -4962,6 +5246,7 @@ function OPSGROUP:RouteToMission(mission, delay)
end
end
-- Add waypoint.
local waypoint=nil --#OPSGROUP.Waypoint
@ -5001,18 +5286,53 @@ function OPSGROUP:RouteToMission(mission, delay)
mission:SetGroupEgressWaypointUID(self, Ewaypoint.uid)
end
-- Get current pos.
local coord=self:GetCoordinate()
-- Distance to waypoint coordinate.
local d=coord:Get2DDistance(waypointcoord)
-- Debug info.
self:T(self.lid..string.format("FF distance to ingress waypoint=%.1f m", d))
-- Check if we are already where we want to be.
if targetzone and self:IsInZone(targetzone) then
self:T(self.lid.."Already in mission zone ==> TaskExecute()")
self:TaskExecute(waypointtask)
return
elseif d<25 then
self:T(self.lid.."Already within 25 meters of mission waypoint ==> TaskExecute()")
self:TaskExecute(waypointtask)
return
end
-- Check if group is mobile. Note that some immobile units report a speed of 1 m/s = 3.6 km/h.
if self.speedMax<=3.6 or mission.teleport then
-- Teleport to waypoint coordinate. Mission will not be paused.
self:Teleport(waypointcoord, nil, true)
-- Execute task in one second.
self:__TaskExecute(-1, waypointtask)
else
-- Give cruise command/update route.
if self:IsArmygroup() then
self:Cruise(SpeedToMission)
elseif self:IsNavygroup() then
self:Cruise(SpeedToMission)
elseif self:IsFlightgroup() then
self:UpdateRoute()
end
end
---
-- Mission Specific Settings
---
self:_SetMissionOptions(mission)
if self:IsArmygroup() then
self:Cruise(SpeedToMission)
elseif self:IsNavygroup() then
self:Cruise(SpeedToMission)
elseif self:IsFlightgroup() then
self:UpdateRoute()
end
self:_SetMissionOptions(mission)
end
end
@ -5038,6 +5358,10 @@ function OPSGROUP:_SetMissionOptions(mission)
if mission.optionEPLRS then
self:SwitchEPLRS(mission.optionEPLRS)
end
-- Emission
if mission.optionEPLRS then
self:SwitchEmission(mission.optionEmission)
end
-- Formation
if mission.optionFormation and self:IsFlightgroup() then
self:SwitchFormation(mission.optionFormation)
@ -5441,6 +5765,13 @@ function OPSGROUP:_SetWaypointTasks(Waypoint)
-- Check if there is mission task
if missiontask then
self:T(self.lid.."Executing mission task")
local mission=self:GetMissionByTaskID(missiontask.id)
if mission then
if mission.opstransport and not mission.opstransport:IsCargoDelivered(self.groupname) then
self:PauseMission()
return
end
end
self:TaskExecute(missiontask)
return 1
end
@ -5497,8 +5828,6 @@ end
-- @param #number Speed (Optional) Speed to waypoint in knots.
function OPSGROUP:onafterGotoWaypoint(From, Event, To, UID, Speed)
--env.info("FF goto waypoint uid="..tostring(UID))
local n=self:GetWaypointIndex(UID)
if n then
@ -6263,11 +6592,12 @@ end
-- @param #OPSGROUP self
-- @param Core.Point#COORDINATE Coordinate Coordinate where the group is teleported to.
-- @param #number Delay Delay in seconds before respawn happens. Default 0.
-- @param #boolean NoPauseMission If `true`, dont pause a running mission.
-- @return #OPSGROUP self
function OPSGROUP:Teleport(Coordinate, Delay)
function OPSGROUP:Teleport(Coordinate, Delay, NoPauseMission)
if Delay and Delay>0 then
self:ScheduleOnce(Delay, OPSGROUP.Teleport, self, Coordinate)
self:ScheduleOnce(Delay, OPSGROUP.Teleport, self, Coordinate, 0, NoPauseMission)
else
-- Debug message.
@ -6275,8 +6605,8 @@ function OPSGROUP:Teleport(Coordinate, Delay)
--Coordinate:MarkToAll("Teleport "..self.groupname)
-- Check if we have a mission running.
if self:IsOnMission() then
self:T(self.lid.."Pausing current mission")
if self:IsOnMission() and not NoPauseMission then
self:T(self.lid.."Pausing current mission for telport")
self:PauseMission()
end
@ -6285,6 +6615,14 @@ function OPSGROUP:Teleport(Coordinate, Delay)
-- Set late activation of template to current state.
Template.lateActivation=self:IsLateActivated()
-- Not uncontrolled.
Template.uncontrolled=false
-- Set waypoint in air for flighgroups.
if self:IsFlightgroup() then
Template.route.points[1]=Coordinate:WaypointAir("BARO", COORDINATE.WaypointType.TurningPoint, COORDINATE.WaypointAction.TurningPoint, 300, true, nil, nil, "Spawnpoint")
end
-- Template units.
local units=Template.units
@ -7840,17 +8178,36 @@ function OPSGROUP:onafterLoading(From, Event, To)
local cargos={}
for _,_cargo in pairs(self.cargoTZC.Cargos) do
local cargo=_cargo --Ops.OpsGroup#OPSGROUP.CargoGroup
-- Check if this group can carry the cargo.
local canCargo=self:CanCargo(cargo.opsgroup)
-- Check if this group is currently acting as carrier.
local isCarrier=cargo.opsgroup:IsPickingup() or cargo.opsgroup:IsLoading() or cargo.opsgroup:IsTransporting() or cargo.opsgroup:IsUnloading()
local isOnMission=cargo.opsgroup:IsOnMission()
-- Check if cargo is not already cargo.
local isNotCargo=cargo.opsgroup:IsNotCargo(true)
-- Check if cargo is holding.
local isHolding=cargo.opsgroup:IsHolding()
-- Check if cargo is in embark/pickup zone.
-- Added InUtero here, if embark zone is moving (ship) and cargo has been spawned late activated and its position is not updated. Not sure if that breaks something else!
local inzone=cargo.opsgroup:IsInZone(self.cargoTZC.EmbarkZone) --or cargo.opsgroup:IsInUtero()
local inZone=cargo.opsgroup:IsInZone(self.cargoTZC.EmbarkZone) --or cargo.opsgroup:IsInUtero()
-- Check if cargo is currently on a mission.
local isOnMission=cargo.opsgroup:IsOnMission()
-- Check if current mission is using this ops transport.
if isOnMission then
local mission=cargo.opsgroup:GetMissionCurrent()
if mission and mission.opstransport and mission.opstransport.uid==self.cargoTransport.uid then
isOnMission=not cargo.opsgroup:IsHolding()
end
end
-- TODO: Need a better :IsBusy() function or :IsReadyForMission() :IsReadyForBoarding() :IsReadyForTransport()
if self:CanCargo(cargo.opsgroup) and inzone and cargo.opsgroup:IsNotCargo(true) and (not (cargo.delivered or cargo.opsgroup:IsDead() or isCarrier or isOnMission)) then
if canCargo and inZone and isNotCargo and isHolding and (not (cargo.delivered or cargo.opsgroup:IsDead() or isCarrier or isOnMission)) then
table.insert(cargos, cargo)
end
end
@ -8482,6 +8839,10 @@ function OPSGROUP:onafterUnloaded(From, Event, To, OpsGroupCargo)
OpsGroupCargo:Returned()
end
if OpsGroupCargo.missionpaused then
OpsGroupCargo:UnpauseMission()
end
end
@ -9037,6 +9398,11 @@ function OPSGROUP:_CheckGroupDone(delay)
return
end
if self:IsBoarding() then
self:T(self.lid.."Boarding! Group NOT done...")
return
end
-- Group is waiting. We deny all updates.
if self:IsWaiting() then
-- If group is waiting, we assume that is the way it is meant to be.
@ -11740,6 +12106,7 @@ function OPSGROUP:_AddElementByName(unitname)
element.gid=element.DCSunit:getNumber()
element.uid=element.DCSunit:getID()
--element.group=unit:GetGroup()
element.controller=element.DCSunit:getController()
element.opsgroup=self
-- Skill etc.

View File

@ -656,58 +656,6 @@ function OPSTRANSPORT:GetEmbarkZone(TransportZoneCombo)
return TransportZoneCombo.EmbarkZone
end
--[[
--- Set transfer carrier(s). These are carrier groups, where the cargo is directly loaded into when disembarked.
-- @param #OPSTRANSPORT self
-- @param Core.Set#SET_GROUP Carriers Carrier set. Can also be passed as a #GROUP, #OPSGROUP or #SET_OPSGROUP object.
-- @param #OPSTRANSPORT.TransportZoneCombo TransportZoneCombo Transport zone combo.
-- @return #OPSTRANSPORT self
function OPSTRANSPORT:SetEmbarkCarriers(Carriers, TransportZoneCombo)
-- Debug info.
self:T(self.lid.."Setting embark carriers!")
-- Use default TZC if no transport zone combo is provided.
TransportZoneCombo=TransportZoneCombo or self.tzcDefault
if Carriers:IsInstanceOf("GROUP") or Carriers:IsInstanceOf("OPSGROUP") then
local carrier=self:_GetOpsGroupFromObject(Carriers)
if carrier then
table.insert(TransportZoneCombo.EmbarkCarriers, carrier)
end
elseif Carriers:IsInstanceOf("SET_GROUP") or Carriers:IsInstanceOf("SET_OPSGROUP") then
for _,object in pairs(Carriers:GetSet()) do
local carrier=self:_GetOpsGroupFromObject(object)
if carrier then
table.insert(TransportZoneCombo.EmbarkCarriers, carrier)
end
end
else
self:E(self.lid.."ERROR: Carriers must be a GROUP, OPSGROUP, SET_GROUP or SET_OPSGROUP object!")
end
return self
end
--- Get embark transfer carrier(s). These are carrier groups, where the cargo is directly loaded into when disembarked.
-- @param #OPSTRANSPORT self
-- @param #OPSTRANSPORT.TransportZoneCombo TransportZoneCombo Transport zone combo.
-- @return #table Table of carrier OPS groups.
function OPSTRANSPORT:GetEmbarkCarriers(TransportZoneCombo)
-- Use default TZC if no transport zone combo is provided.
TransportZoneCombo=TransportZoneCombo or self.tzcDefault
return TransportZoneCombo.EmbarkCarriers
end
]]
--- Set disembark zone.
-- @param #OPSTRANSPORT self
-- @param Core.Zone#ZONE DisembarkZone Zone where the troops are disembarked.
@ -1366,6 +1314,25 @@ function OPSTRANSPORT:AddAssetCargo(Asset, TransportZoneCombo)
return self
end
--- Get transport zone combo of cargo group.
-- @param #OPSTRANSPORT self
-- @param #string GroupName Group name of cargo.
-- @return #OPSTRANSPORT.TransportZoneCombo TransportZoneCombo Transport zone combo.
function OPSTRANSPORT:GetTZCofCargo(GroupName)
for _,_tzc in pairs(self.tzCombos) do
local tzc=_tzc --#OPSTRANSPORT.TransportZoneCombo
for _,_cargo in pairs(tzc.Cargos) do
local cargo=_cargo --Ops.OpsGroup#OPSGROUP.CargoGroup
if cargo.opsgroup:GetName()==GroupName then
return tzc
end
end
end
return nil
end
--- Add LEGION to the transport.
-- @param #OPSTRANSPORT self
-- @param Ops.Legion#LEGION Legion The legion.
@ -1638,6 +1605,24 @@ function OPSTRANSPORT:onafterStatusUpdate(From, Event, To)
end
end
--- Check if a cargo group was delivered.
-- @param #OPSTRANSPORT self
-- @param #string GroupName Name of the group.
-- @return #boolean If `true`, cargo was delivered.
function OPSTRANSPORT:IsCargoDelivered(GroupName)
for _,_cargo in pairs(self:GetCargos()) do
local cargo=_cargo --Ops.OpsGroup#OPSGROUP.CargoGroup
if cargo.opsgroup:GetName()==GroupName then
return cargo.delivered
end
end
return nil
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- FSM Event Functions
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

View File

@ -428,6 +428,21 @@ function OPSZONE:GetCoordinate()
return coordinate
end
--- Returns a random coordinate in the zone.
-- @param #OPSZONE self
-- @param #number inner (Optional) Minimal distance from the center of the zone in meters. Default is 0 m.
-- @param #number outer (Optional) Maximal distance from the outer edge of the zone in meters. Default is the radius of the zone.
-- @param #table surfacetypes (Optional) Table of surface types. Can also be a single surface type. We will try max 1000 times to find the right type!
-- @return Core.Point#COORDINATE The random coordinate.
function OPSZONE:GetRandomCoordinate(inner, outer, surfacetypes)
local zone=self:GetZone()
local coord=zone:GetRandomCoordinate(inner, outer, surfacetypes)
return coord
end
--- Get zone name.
-- @param #OPSZONE self
-- @return #string Name of the zone.

View File

@ -31,7 +31,7 @@
-- @field #string tankergroupname Name of the late activated tanker template group.
-- @field Wrapper.Group#GROUP tanker Tanker group.
-- @field Wrapper.Airbase#AIRBASE airbase The home airbase object of the tanker. Normally the aircraft carrier.
-- @field Core.Radio#BEACON beacon Tanker TACAN beacon.
-- @field Core.Beacon#BEACON beacon Tanker TACAN beacon.
-- @field #number TACANchannel TACAN channel. Default 1.
-- @field #string TACANmode TACAN mode, i.e. "X" or "Y". Default "Y". Use only "Y" for AA TACAN stations!
-- @field #string TACANmorse TACAN morse code. Three letters identifying the TACAN station. Default "TKR".
@ -784,10 +784,11 @@ end
-- @param #RECOVERYTANKER self
-- @param #number channel TACAN channel. Default 1.
-- @param #string morse TACAN morse code identifier. Three letters. Default "TKR".
-- @param #string mode TACAN mode, which can be either "Y" (default) or "X".
-- @return #RECOVERYTANKER self
function RECOVERYTANKER:SetTACAN(channel, morse)
function RECOVERYTANKER:SetTACAN(channel, morse, mode)
self.TACANchannel=channel or 1
self.TACANmode="Y"
self.TACANmode=mode or "Y"
self.TACANmorse=morse or "TKR"
self.TACANon=true
return self
@ -1625,7 +1626,6 @@ function RECOVERYTANKER:_ActivateTACAN(delay)
if delay and delay>0 then
-- Schedule TACAN activation.
--SCHEDULER:New(nil, self._ActivateTACAN, {self}, delay)
self:ScheduleOnce(delay, RECOVERYTANKER._ActivateTACAN, self)
else

View File

@ -529,26 +529,32 @@ function GROUP:HasAttribute(attribute, all)
-- Get all units of the group.
local _units=self:GetUnits()
local _allhave=true
local _onehas=false
if _units then
for _,_unit in pairs(_units) do
local _unit=_unit --Wrapper.Unit#UNIT
if _unit then
local _hastit=_unit:HasAttribute(attribute)
if _hastit==true then
_onehas=true
else
_allhave=false
end
end
local _allhave=true
local _onehas=false
for _,_unit in pairs(_units) do
local _unit=_unit --Wrapper.Unit#UNIT
if _unit then
local _hastit=_unit:HasAttribute(attribute)
if _hastit==true then
_onehas=true
else
_allhave=false
end
end
end
if all==true then
return _allhave
else
return _onehas
end
end
if all==true then
return _allhave
else
return _onehas
end
return nil
end
--- Returns the maximum speed of the group.
@ -563,16 +569,19 @@ function GROUP:GetSpeedMax()
local Units=self:GetUnits()
local speedmax=0
local speedmax=nil
for _,unit in pairs(Units) do
local unit=unit --Wrapper.Unit#UNIT
local speed=unit:GetSpeedMax()
if speedmax==0 then
speedmax=speed
elseif speed<speedmax then
if speedmax==nil or speed<speedmax then
speedmax=speed
end
--env.info(string.format("FF unit %s: speed=%.1f, speedmax=%.1f", unit:GetName(), speed, speedmax))
end
return speedmax