diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index ccb3fdbd3..e8f169c06 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -5453,57 +5453,63 @@ end -- @param #WAREHOUSE.Pendingitem request The request of the dead asset. function WAREHOUSE:onafterAssetDead(From, Event, To, asset, request) - -- Debug message. - local text=string.format("Asset %s from request id=%d is dead!", asset.templatename, request.uid) - self:T(self.lid..text) + if asset and request then - -- Here I need to get rid of the #CARGO at the end to obtain the original name again! - local groupname=asset.spawngroupname --self:_GetNameWithOut(group) - - -- Dont trigger a Remove event for the group sets. - local NoTriggerEvent=true - - if request.transporttype==WAREHOUSE.TransportType.SELFPROPELLED then - - --- - -- Easy case: Group can simply be removed from the cargogroupset. - --- - - -- Remove dead group from cargo group set. - request.cargogroupset:Remove(groupname, NoTriggerEvent) - self:T(self.lid..string.format("Removed selfpropelled cargo %s: ncargo=%d.", groupname, request.cargogroupset:Count())) - - else - - --- - -- Complicated case: Dead unit could be: - -- 1.) A Cargo unit (e.g. waiting to be picked up). - -- 2.) A Transport unit which itself holds cargo groups. - --- - - -- Check if this a cargo or transport group. - local istransport=not asset.iscargo --self:_GroupIsTransport(group, request) - - if istransport==true then - - -- Whole carrier group is dead. Remove it from the carrier group set. - request.transportgroupset:Remove(groupname, NoTriggerEvent) - self:T(self.lid..string.format("Removed transport %s: ntransport=%d", groupname, request.transportgroupset:Count())) - - elseif istransport==false then - - -- This must have been an alive cargo group that was killed outside the carrier, e.g. waiting to be transported or waiting to be put back. + -- Debug message. + local text=string.format("Asset %s from request id=%d is dead!", asset.templatename, request.uid) + self:T(self.lid..text) + + -- Here I need to get rid of the #CARGO at the end to obtain the original name again! + local groupname=asset.spawngroupname --self:_GetNameWithOut(group) + + -- Dont trigger a Remove event for the group sets. + local NoTriggerEvent=true + + if request.transporttype==WAREHOUSE.TransportType.SELFPROPELLED then + + --- + -- Easy case: Group can simply be removed from the cargogroupset. + --- + -- Remove dead group from cargo group set. request.cargogroupset:Remove(groupname, NoTriggerEvent) - self:T(self.lid..string.format("Removed transported cargo %s outside carrier: ncargo=%d", groupname, request.cargogroupset:Count())) - -- This as well? - --request.transportcargoset:RemoveCargosByName(RemoveCargoNames) - - else - --self:E(self.lid..string.format("ERROR: Group %s is neither cargo nor transport!", group:GetName())) - end - end + self:T(self.lid..string.format("Removed selfpropelled cargo %s: ncargo=%d.", groupname, request.cargogroupset:Count())) + else + + --- + -- Complicated case: Dead unit could be: + -- 1.) A Cargo unit (e.g. waiting to be picked up). + -- 2.) A Transport unit which itself holds cargo groups. + --- + + -- Check if this a cargo or transport group. + local istransport=not asset.iscargo --self:_GroupIsTransport(group, request) + + if istransport==true then + + -- Whole carrier group is dead. Remove it from the carrier group set. + request.transportgroupset:Remove(groupname, NoTriggerEvent) + self:T(self.lid..string.format("Removed transport %s: ntransport=%d", groupname, request.transportgroupset:Count())) + + elseif istransport==false then + + -- This must have been an alive cargo group that was killed outside the carrier, e.g. waiting to be transported or waiting to be put back. + -- Remove dead group from cargo group set. + request.cargogroupset:Remove(groupname, NoTriggerEvent) + self:T(self.lid..string.format("Removed transported cargo %s outside carrier: ncargo=%d", groupname, request.cargogroupset:Count())) + -- This as well? + --request.transportcargoset:RemoveCargosByName(RemoveCargoNames) + + else + --self:E(self.lid..string.format("ERROR: Group %s is neither cargo nor transport!", group:GetName())) + end + end + + else + self:E(self.lid.."ERROR: Asset and/or Request is nil in onafterAssetDead") + + end end diff --git a/Moose Development/Moose/Ops/ArmyGroup.lua b/Moose Development/Moose/Ops/ArmyGroup.lua index 98d372ade..819a7fae2 100644 --- a/Moose Development/Moose/Ops/ArmyGroup.lua +++ b/Moose Development/Moose/Ops/ArmyGroup.lua @@ -1405,7 +1405,9 @@ function ARMYGROUP:onbeforeEngageTarget(From, Event, To, Target) end -- Pause current mission. - if self.currentmission and self.currentmission>0 then + local mission=self:GetMissionCurrent() + + if mission and mission.type~=AUFTRAG.Type.GROUNDATTACK then self:T(self.lid.."Engage command but have current mission ==> Pausing mission!") self:PauseMission() dt=-0.1 @@ -1533,6 +1535,15 @@ function ARMYGROUP:onafterDisengage(From, Event, To) self:SwitchROE(self.engage.roe) self:SwitchAlarmstate(self.engage.alarmstate) + -- Get current task + local task=self:GetTaskCurrent() + + -- Get if current task is ground attack. + if task and task.dcstask.id==AUFTRAG.SpecialTask.GROUNDATTACK then + self:T(self.lid.."Disengage with current task GROUNDATTACK ==> Task Done!") + self:TaskDone(task) + end + -- Remove current waypoint if self.engage.Waypoint then self:RemoveWaypointByID(self.engage.Waypoint.uid) diff --git a/Moose Development/Moose/Ops/Auftrag.lua b/Moose Development/Moose/Ops/Auftrag.lua index eaab8d480..f0965d826 100644 --- a/Moose Development/Moose/Ops/Auftrag.lua +++ b/Moose Development/Moose/Ops/Auftrag.lua @@ -13,13 +13,13 @@ -- === -- -- ## Example Missions: --- +-- -- Demo missions can be found on [github](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/OPS%20-%20Auftrag). --- +-- -- === --- +-- -- ### Author: **funkyfranky** --- +-- -- === -- @module Ops.Auftrag -- @image OPS_Auftrag.png @@ -58,20 +58,20 @@ -- @field #number Nkills Number of (enemy) units killed by assets of this mission. -- @field #number Nelements Number of elements (units) assigned to mission. -- @field #number dTevaluate Time interval in seconds before the mission result is evaluated after mission is over. --- @field #number Tover Mission abs. time stamp, when mission was over. +-- @field #number Tover Mission abs. time stamp, when mission was over. -- @field #table conditionStart Condition(s) that have to be true, before the mission will be started. -- @field #table conditionSuccess If all conditions are true, the mission is cancelled. -- @field #table conditionFailure If all conditions are true, the mission is cancelled. -- @field #table conditionPush If all conditions are true, the mission is executed. Before, the group(s) wait at the mission execution waypoint. --- +-- -- @field #number orbitSpeed Orbit speed in m/s. -- @field #number orbitAltitude Orbit altitude in meters. -- @field #number orbitHeading Orbit heading in degrees. -- @field #number orbitLeg Length of orbit leg in meters. -- @field Core.Point#COORDINATE orbitRaceTrack Race-track orbit coordinate. --- +-- -- @field Ops.Target#TARGET engageTarget Target data to engage. --- +-- -- @field Core.Zone#ZONE_RADIUS engageZone *Circular* engagement zone. -- @field #table engageTargetTypes Table of target types that are engaged in the engagement zone. -- @field #number engageAltitude Engagement altitude in meters. @@ -82,34 +82,34 @@ -- @field #boolean engageAsGroup Group attack. -- @field #number engageMaxDistance Max engage distance. -- @field #number refuelSystem Refuel type (boom or probe) for TANKER missions. --- +-- -- @field Wrapper.Group#GROUP escortGroup The group to be escorted. -- @field DCS#Vec3 escortVec3 The 3D offset vector from the escorted group to the escort group. --- +-- -- @field #number facDesignation FAC designation type. -- @field #boolean facDatalink FAC datalink enabled. -- @field #number facFreq FAC radio frequency in MHz. -- @field #number facModu FAC radio modulation 0=AM 1=FM. --- +-- -- @field Core.Set#SET_GROUP transportGroupSet Groups to be transported. -- @field Core.Point#COORDINATE transportPickup Coordinate where to pickup the cargo. -- @field Core.Point#COORDINATE transportDropoff Coordinate where to drop off the cargo. -- @field #number transportPickupRadius Radius in meters for pickup zone. Default 500 m. --- +-- -- @field Ops.OpsTransport#OPSTRANSPORT opstransport OPS transport assignment. -- @field #number NcarriersMin Min number of required carrier assets. -- @field #number NcarriersMax Max number of required carrier assets. -- @field Core.Zone#ZONE transportDeployZone Deploy zone of an OPSTRANSPORT. -- @field Core.Zone#ZONE transportDisembarkZone Disembark zone of an OPSTRANSPORT. --- +-- -- @field #number artyRadius Radius in meters. -- @field #number artyShots Number of shots fired. -- @field #number artyAltitude Altitude in meters. Can be used for a Barrage. -- @field #number artyHeading Heading in degrees (for Barrage). -- @field #number artyAngle Shooting angle in degrees (for Barrage). --- +-- -- @field #string alert5MissionType Alert 5 mission type. This is the mission type, the alerted assets will be able to carry out. --- +-- -- @field Ops.Chief#CHIEF chief The CHIEF managing this mission. -- @field Ops.Commander#COMMANDER commander The COMMANDER managing this mission. -- @field #table assets Warehouse assets assigned for this mission. @@ -121,16 +121,16 @@ -- @field #table NassetsLegMin Number of required warehouse assets for each assigned legion. -- @field #table NassetsLegMax Number of required warehouse assets for each assigned legion. -- @field #table requestID The ID of the queued warehouse request. Necessary to cancel the request if the mission was cancelled before the request is processed. --- @field #table payloads User specified airwing payloads for this mission. Only these will be considered for the job! +-- @field #table payloads User specified airwing payloads for this mission. Only these will be considered for the job! -- @field Ops.AirWing#AIRWING.PatrolData patroldata Patrol data. --- +-- -- @field #table specialLegions User specified legions assigned for this mission. Only these will be considered for the job! -- @field #table specialCohorts User specified cohorts assigned for this mission. Only these will be considered for the job! -- @field #table transportLegions Legions explicitly requested for providing transport carrier assets. --- @field #table transportCohorts Cohorts explicitly requested for providing transport carrier assets. +-- @field #table transportCohorts Cohorts explicitly requested for providing transport carrier assets. -- @field #table escortLegions Legions explicitly requested for providing escorting assets. -- @field #table escortCohorts Cohorts explicitly requested for providing escorting assets. --- +-- -- @field #string missionTask Mission task. See `ENUMS.MissionTask`. -- @field #number missionAltitude Mission altitude in meters. -- @field #number missionSpeed Mission speed in km/h. @@ -139,20 +139,20 @@ -- @field Core.Point#COORDINATE missionWaypointCoord Mission waypoint coordinate. -- @field Core.Point#COORDINATE missionEgressCoord Mission egress waypoint coordinate. -- @field #number missionWaypointRadius Random radius in meters. --- +-- -- @field #table enrouteTasks Mission enroute tasks. --- +-- -- @field #number repeated Number of times mission was repeated. -- @field #number repeatedSuccess Number of times mission was repeated after a success. -- @field #number repeatedFailure Number of times mission was repeated after a failure. -- @field #number Nrepeat Number of times the mission is repeated. -- @field #number NrepeatFailure Number of times mission is repeated if failed. -- @field #number NrepeatSuccess Number of times mission is repeated if successful. --- +-- -- @field Ops.OpsGroup#OPSGROUP.Radio radio Radio freq and modulation. -- @field Ops.OpsGroup#OPSGROUP.Beacon tacan TACAN setting. -- @field Ops.OpsGroup#OPSGROUP.Beacon icls ICLS setting. --- +-- -- @field #number optionROE ROE. -- @field #number optionROT ROT. -- @field #number optionAlarm Alarm state. @@ -162,7 +162,7 @@ -- @field #number optionRTBammo RTB on out-of-ammo. -- @field #number optionRTBfuel RTB on out-of-fuel. -- @field #number optionECM ECM. --- +-- -- @extends Core.Fsm#FSM --- *A warrior's mission is to foster the success of others.* - Morihei Ueshiba @@ -170,162 +170,162 @@ -- === -- -- # The AUFTRAG Concept --- +-- -- The AUFTRAG class significantly simplifies the workflow of using DCS tasks. -- -- You can think of an AUFTRAG as document, which contains the mission briefing, i.e. information about the target location, mission altitude, speed and various other parameters. -- This document can be handed over directly to a pilot (or multiple pilots) via the @{Ops.FlightGroup#FLIGHTGROUP} class. The pilots will then execute the mission. --- +-- -- The AUFTRAG document can also be given to an AIRWING. The airwing will then determine the best assets (pilots and payloads) available for the job. --- +-- -- Similarly, an AUFTRAG can be given to ground or navel groups via the @{Ops.ArmyGroup#ARMYGROUP} or @{Ops.NavyGroup#NAVYGROUP} classes, respectively. These classes have also --- AIRWING analouges, which are called BRIGADE and FLEET. Brigades and fleets will likewise select the best assets they have available and pass on the AUFTRAG to them. --- --- +-- AIRWING analouges, which are called BRIGADE and FLEET. Brigades and fleets will likewise select the best assets they have available and pass on the AUFTRAG to them. +-- +-- -- One more up the food chain, an AUFTRAG can be passed to a COMMANDER. The commander will recruit the best assets of AIRWINGs, BRIGADEs and/or FLEETs and pass the job over to it. --- +-- -- -- # Airborne Missions --- +-- -- Several mission types are supported by this class. --- +-- -- ## Anti-Ship --- +-- -- An anti-ship mission can be created with the @{#AUFTRAG.NewANTISHIP}() function. --- +-- -- ## AWACS --- +-- -- An AWACS mission can be created with the @{#AUFTRAG.NewAWACS}() function. --- +-- -- ## BAI --- +-- -- A BAI mission can be created with the @{#AUFTRAG.NewBAI}() function. --- +-- -- ## Bombing --- +-- -- A bombing mission can be created with the @{#AUFTRAG.NewBOMBING}() function. --- +-- -- ## Bombing Runway --- +-- -- A bombing runway mission can be created with the @{#AUFTRAG.NewBOMBRUNWAY}() function. --- +-- -- ## Bombing Carpet --- +-- -- A carpet bombing mission can be created with the @{#AUFTRAG.NewBOMBCARPET}() function. --- +-- -- ## CAP --- +-- -- A CAP mission can be created with the @{#AUFTRAG.NewCAP}() function. --- +-- -- ## CAS --- +-- -- A CAS mission can be created with the @{#AUFTRAG.NewCAS}() function. --- +-- -- ## Escort --- +-- -- An escort mission can be created with the @{#AUFTRAG.NewESCORT}() function. --- +-- -- ## FACA --- +-- -- An FACA mission can be created with the @{#AUFTRAG.NewFACA}() function. --- +-- -- ## Ferry --- +-- -- Not implemented yet. --- +-- -- ## Intercept --- +-- -- An intercept mission can be created with the @{#AUFTRAG.NewINTERCEPT}() function. --- +-- -- ## Orbit --- +-- -- An orbit mission can be created with the @{#AUFTRAG.NewORBIT}() function. --- +-- -- ## GCICAP --- +-- -- An patrol mission can be created with the @{#AUFTRAG.NewGCICAP}() function. --- +-- -- ## RECON --- +-- -- An reconnaissance mission can be created with the @{#AUFTRAG.NewRECON}() function. --- +-- -- ## RESCUE HELO --- +-- -- An rescue helo mission can be created with the @{#AUFTRAG.NewRESCUEHELO}() function. --- +-- -- ## SEAD --- +-- -- An SEAD mission can be created with the @{#AUFTRAG.NewSEAD}() function. --- +-- -- ## STRIKE --- +-- -- An strike mission can be created with the @{#AUFTRAG.NewSTRIKE}() function. --- +-- -- ## Tanker --- +-- -- A refueling tanker mission can be created with the @{#AUFTRAG.NewTANKER}() function. --- +-- -- ## TROOPTRANSPORT --- +-- -- A troop transport mission can be created with the @{#AUFTRAG.NewTROOPTRANSPORT}() function. --- +-- -- ## HOVER --- +-- -- A mission for a helicoptre or VSTOL plane to Hover at a point for a certain amount of time can be created with the @{#AUFTRAG.NewHOVER}() function. --- +-- -- # Ground Missions --- +-- -- ## ARTY --- +-- -- An arty mission can be created with the @{#AUFTRAG.NewARTY}() function. --- +-- -- ## ARMORATTACK --- +-- -- A mission to send a tank group can be created with the @{#AUFTRAG.NewARMORATTACK}() function. --- +-- -- # Options and Parameters --- +-- -- TODO --- +-- -- # Assigning Missions --- +-- -- An AUFTRAG can be assigned to groups (FLIGHTGROUP, ARMYGROUP, NAVYGROUP), legions (AIRWING, BRIGADE, FLEET) or to a COMMANDER. --- +-- -- ## Group Level --- +-- -- ### Flight Group --- +-- -- Assigning an AUFTRAG to a flight group is done via the @{Ops.FlightGroup#FLIGHTGROUP.AddMission} function. See FLIGHTGROUP docs for details. --- +-- -- ### Army Group --- +-- -- Assigning an AUFTRAG to an army group is done via the @{Ops.ArmyGroup#ARMYGROUP.AddMission} function. See ARMYGROUP docs for details. --- +-- -- ### Navy Group --- +-- -- Assigning an AUFTRAG to a navy group is done via the @{Ops.NavyGroup#NAVYGROUP.AddMission} function. See NAVYGROUP docs for details. --- +-- -- ## Legion Level --- +-- -- Adding an AUFTRAG to an airwing is done via the @{Ops.AirWing#AIRWING.AddMission} function. See AIRWING docs for further details. -- Similarly, an AUFTRAG can be added to a brigade via the @{Ops.Brigade#BRIGADE.AddMission} function. --- +-- -- ## Commander Level --- +-- -- Assigning an AUFTRAG to acommander is done via the @{Ops.Commander#COMMANDER.AddMission} function. See COMMANDER docs for details. --- --- +-- +-- -- # Events --- +-- -- The AUFTRAG class creates many useful (FSM) events, which can be used in the mission designers script. --- --- TODO --- --- --- # Examples --- +-- -- TODO --- +-- +-- +-- # Examples +-- +-- TODO +-- -- -- @field #AUFTRAG AUFTRAG = { @@ -389,10 +389,11 @@ _AUFTRAGSNR=0 -- @field #string BARRAGE Barrage. -- @field #string ARMORATTACK Armor attack. -- @field #string CASENHANCED Enhanced CAS. --- @field #string HOVER Hover at a point. +-- @field #string HOVER Hover. +-- @field #string GROUNDATTACK Ground attack. AUFTRAG.Type={ ANTISHIP="Anti Ship", - AWACS="AWACS", + AWACS="AWACS", BAI="BAI", BOMBING="Bombing", BOMBRUNWAY="Bomb Runway", @@ -424,6 +425,7 @@ AUFTRAG.Type={ ARMORATTACK="Armor Attack", CASENHANCED="CAS Enhanced", HOVER="Hover", + GROUNDATTACK="Ground Attack" } --- Mission status of an assigned group. @@ -437,6 +439,7 @@ AUFTRAG.Type={ -- @field #string ARMOREDGUARD On guard with armor. -- @field #string BARRAGE Barrage. -- @field #string HOVER Hover. +-- @field #string GROUNDATTACK Ground attack. AUFTRAG.SpecialTask={ PATROLZONE="PatrolZone", RECON="ReconMission", @@ -448,6 +451,7 @@ AUFTRAG.SpecialTask={ BARRAGE="Barrage", ARMORATTACK="AmorAttack", HOVER="Hover", + GROUNDATTACK="Ground Attack", } --- Mission status. @@ -560,7 +564,7 @@ AUFTRAG.Category={ -- @field #number waypointindex Mission (ingress) Waypoint UID. -- @field #number waypointEgressUID Egress Waypoint UID. -- @field Core.Point#COORDINATE wpegresscoordinate Egress waypoint coordinate. --- +-- -- @field Ops.OpsGroup#OPSGROUP.Task waypointtask Waypoint task. -- @field #string status Group mission status. -- @field Functional.Warehouse#WAREHOUSE.Assetitem asset The warehouse asset. @@ -568,7 +572,7 @@ AUFTRAG.Category={ --- AUFTRAG class version. -- @field #string version -AUFTRAG.version="0.8.5" +AUFTRAG.version="0.9.0" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list @@ -608,22 +612,22 @@ function AUFTRAG:New(Type) -- Inherit everything from FSM class. local self=BASE:Inherit(self, FSM:New()) -- #AUFTRAG - + -- Increase global counter. _AUFTRAGSNR=_AUFTRAGSNR+1 - + -- Mission type. self.type=Type - + -- Auftragsnummer. self.auftragsnummer=_AUFTRAGSNR - + -- Log ID. self:_SetLogID() - + -- State is planned. self.status=AUFTRAG.Status.PLANNED - + -- Defaults . self:SetName() self:SetPriority() @@ -632,7 +636,7 @@ function AUFTRAG:New(Type) --self:SetRequiredCarriers() self.engageAsGroup=true self.dTevaluate=5 - + -- Init counters and stuff. self.repeated=0 self.repeatedSuccess=0 @@ -643,35 +647,35 @@ function AUFTRAG:New(Type) self.Ncasualties=0 self.Nkills=0 self.Nelements=0 - + -- FMS start state is PLANNED. self:SetStartState(self.status) - + -- PLANNED --> (QUEUED) --> (REQUESTED) --> SCHEDULED --> STARTED --> EXECUTING --> DONE self:AddTransition("*", "Planned", AUFTRAG.Status.PLANNED) -- Mission is in planning stage. Could be in the queue of a COMMANDER or CHIEF. self:AddTransition(AUFTRAG.Status.PLANNED, "Queued", AUFTRAG.Status.QUEUED) -- Mission is in queue of a LEGION. self:AddTransition(AUFTRAG.Status.QUEUED, "Requested", AUFTRAG.Status.REQUESTED) -- Mission assets have been requested from the warehouse. self:AddTransition(AUFTRAG.Status.REQUESTED, "Scheduled", AUFTRAG.Status.SCHEDULED) -- Mission added to the first ops group queue. - + self:AddTransition(AUFTRAG.Status.PLANNED, "Scheduled", AUFTRAG.Status.SCHEDULED) -- From planned directly to scheduled. - + self:AddTransition(AUFTRAG.Status.SCHEDULED, "Started", AUFTRAG.Status.STARTED) -- First asset has started the mission. self:AddTransition(AUFTRAG.Status.STARTED, "Executing", AUFTRAG.Status.EXECUTING) -- First asset is executing the mission. - + self:AddTransition("*", "Done", AUFTRAG.Status.DONE) -- All assets have reported that mission is done. - + self:AddTransition("*", "Cancel", AUFTRAG.Status.CANCELLED) -- Command to cancel the mission. - + self:AddTransition("*", "Success", AUFTRAG.Status.SUCCESS) self:AddTransition("*", "Failed", AUFTRAG.Status.FAILED) - + self:AddTransition("*", "Status", "*") self:AddTransition("*", "Stop", "*") - + self:AddTransition("*", "Repeat", AUFTRAG.Status.PLANNED) self:AddTransition("*", "ElementDestroyed", "*") - self:AddTransition("*", "GroupDead", "*") + self:AddTransition("*", "GroupDead", "*") self:AddTransition("*", "AssetDead", "*") ------------------------ @@ -711,7 +715,7 @@ function AUFTRAG:New(Type) -- @param #AUFTRAG self -- @param #string From From state. -- @param #string Event Event. - -- @param #string To To state. + -- @param #string To To state. --- Triggers the FSM event "Queued". @@ -728,7 +732,7 @@ function AUFTRAG:New(Type) -- @param #AUFTRAG self -- @param #string From From state. -- @param #string Event Event. - -- @param #string To To state. + -- @param #string To To state. --- Triggers the FSM event "Requested". @@ -745,7 +749,7 @@ function AUFTRAG:New(Type) -- @param #AUFTRAG self -- @param #string From From state. -- @param #string Event Event. - -- @param #string To To state. + -- @param #string To To state. --- Triggers the FSM event "Scheduled". @@ -762,7 +766,7 @@ function AUFTRAG:New(Type) -- @param #AUFTRAG self -- @param #string From From state. -- @param #string Event Event. - -- @param #string To To state. + -- @param #string To To state. --- Triggers the FSM event "Started". @@ -779,7 +783,7 @@ function AUFTRAG:New(Type) -- @param #AUFTRAG self -- @param #string From From state. -- @param #string Event Event. - -- @param #string To To state. + -- @param #string To To state. --- Triggers the FSM event "Executing". @@ -796,7 +800,7 @@ function AUFTRAG:New(Type) -- @param #AUFTRAG self -- @param #string From From state. -- @param #string Event Event. - -- @param #string To To state. + -- @param #string To To state. --- Triggers the FSM event "Cancel". @@ -814,7 +818,7 @@ function AUFTRAG:New(Type) -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. - + --- Triggers the FSM event "Done". -- @function [parent=#AUFTRAG] Done @@ -879,11 +883,11 @@ function AUFTRAG:New(Type) -- @param #AUFTRAG self -- @param #string From From state. -- @param #string Event Event. - -- @param #string To To state. - + -- @param #string To To state. + -- Init status update. self:__Status(-1) - + return self end @@ -899,25 +903,25 @@ end function AUFTRAG:NewANTISHIP(Target, Altitude) local mission=AUFTRAG:New(AUFTRAG.Type.ANTISHIP) - + mission:_TargetFromObject(Target) - + -- DCS task parameters: mission.engageWeaponType=ENUMS.WeaponFlag.Auto mission.engageWeaponExpend=AI.Task.WeaponExpend.ALL mission.engageAltitude=UTILS.FeetToMeters(Altitude or 2000) - + -- Mission options: mission.missionTask=ENUMS.MissionTask.ANTISHIPSTRIKE mission.missionAltitude=mission.engageAltitude mission.missionFraction=0.4 mission.optionROE=ENUMS.ROE.OpenFire mission.optionROT=ENUMS.ROT.EvadeFire - + mission.categories={AUFTRAG.Category.AIRCRAFT} - + mission.DCStask=mission:GetDCSMissionTask() - + return mission end @@ -932,26 +936,26 @@ end function AUFTRAG:NewHOVER(Coordinate, Altitude, Time, Speed, MissionAlt) local mission=AUFTRAG:New(AUFTRAG.Type.HOVER) - + -- Altitude. if Altitude then mission.hoverAltitude=Coordinate:GetLandHeight()+UTILS.FeetToMeters(Altitude) else mission.hoverAltitude=Coordinate:GetLandHeight()+UTILS.FeetToMeters(50) - end - + end + mission:_TargetFromObject(Coordinate) mission.hoverSpeed = 0.1 -- the DCS Task itself will shortly be build with this so MPS mission.hoverTime = Time or 300 - mission.missionSpeed = UTILS.KnotsToMps(Speed or 150) + mission.missionSpeed = UTILS.KnotsToMps(Speed or 150) -- Mission options: mission.missionAltitude=mission.MissionAlt or UTILS.FeetToMeters(1000) - mission.missionFraction=0.9 + mission.missionFraction=0.9 mission.optionROE=ENUMS.ROE.ReturnFire mission.optionROT=ENUMS.ROT.PassiveDefense - + mission.categories={AUFTRAG.Category.AIRCRAFT} mission.DCStask=mission:GetDCSMissionTask() @@ -963,26 +967,26 @@ end -- @param #AUFTRAG self -- @param Core.Point#COORDINATE Coordinate Where to orbit. -- @param #number Altitude Orbit altitude in feet. Default is y component of `Coordinate`. --- @param #number Speed Orbit speed in knots. Default 350 KIAS. +-- @param #number Speed Orbit speed in knots. Default 350 KIAS. -- @param #number Heading Heading of race-track pattern in degrees. If not specified, a circular orbit is performed. -- @param #number Leg Length of race-track in NM. If not specified, a circular orbit is performed. -- @return #AUFTRAG self function AUFTRAG:NewORBIT(Coordinate, Altitude, Speed, Heading, Leg) local mission=AUFTRAG:New(AUFTRAG.Type.ORBIT) - + -- Altitude. if Altitude then mission.orbitAltitude=UTILS.FeetToMeters(Altitude) else mission.orbitAltitude=Coordinate.y - end + end Coordinate.y=mission.orbitAltitude - + mission:_TargetFromObject(Coordinate) mission.orbitSpeed = UTILS.KnotsToMps(Speed or 350) -- the DCS Task itself will shortly be build with this so MPS - mission.missionSpeed = UTILS.KnotsToKmph(Speed or 350) + mission.missionSpeed = UTILS.KnotsToKmph(Speed or 350) if Heading and Leg then mission.orbitHeading=Heading @@ -990,13 +994,13 @@ function AUFTRAG:NewORBIT(Coordinate, Altitude, Speed, Heading, Leg) mission.orbitRaceTrack=Coordinate:Translate(mission.orbitLeg, mission.orbitHeading, true) end - + -- Mission options: - mission.missionAltitude=mission.orbitAltitude*0.9 - mission.missionFraction=0.9 + mission.missionAltitude=mission.orbitAltitude*0.9 + mission.missionFraction=0.9 mission.optionROE=ENUMS.ROE.ReturnFire mission.optionROT=ENUMS.ROT.PassiveDefense - + mission.categories={AUFTRAG.Category.AIRCRAFT} mission.DCStask=mission:GetDCSMissionTask() @@ -1008,7 +1012,7 @@ end -- @param #AUFTRAG self -- @param Core.Point#COORDINATE Coordinate Position where to orbit around. -- @param #number Altitude Orbit altitude in feet. Default is y component of `Coordinate`. --- @param #number Speed Orbit speed in knots. Default 350 KIAS. +-- @param #number Speed Orbit speed in knots. Default 350 KIAS. -- @return #AUFTRAG self function AUFTRAG:NewORBIT_CIRCLE(Coordinate, Altitude, Speed) @@ -1031,7 +1035,7 @@ function AUFTRAG:NewORBIT_RACETRACK(Coordinate, Altitude, Speed, Heading, Leg) Leg = Leg or 10 local mission=AUFTRAG:NewORBIT(Coordinate, Altitude, Speed, Heading, Leg) - + return mission end @@ -1048,18 +1052,18 @@ function AUFTRAG:NewGCICAP(Coordinate, Altitude, Speed, Heading, Leg) -- Create ORBIT first. local mission=AUFTRAG:NewORBIT_RACETRACK(Coordinate, Altitude, Speed, Heading, Leg) - + -- Mission type GCICAP. mission.type=AUFTRAG.Type.GCICAP - + mission:_SetLogID() - -- Mission options: + -- Mission options: mission.missionTask=ENUMS.MissionTask.INTERCEPT mission.optionROT=ENUMS.ROT.PassiveDefense - + mission.categories={AUFTRAG.Category.AIRCRAFT} - + return mission end @@ -1076,23 +1080,23 @@ function AUFTRAG:NewTANKER(Coordinate, Altitude, Speed, Heading, Leg, RefuelSyst -- Create ORBIT first. local mission=AUFTRAG:NewORBIT_RACETRACK(Coordinate, Altitude, Speed, Heading, Leg) - + -- Mission type TANKER. mission.type=AUFTRAG.Type.TANKER - + mission:_SetLogID() - + mission.refuelSystem=RefuelSystem - + -- Mission options: - mission.missionTask=ENUMS.MissionTask.REFUELING + mission.missionTask=ENUMS.MissionTask.REFUELING mission.optionROE=ENUMS.ROE.WeaponHold mission.optionROT=ENUMS.ROT.PassiveDefense - + mission.categories={AUFTRAG.Category.AIRCRAFT} - + mission.DCStask=mission:GetDCSMissionTask() - + return mission end @@ -1108,21 +1112,21 @@ function AUFTRAG:NewAWACS(Coordinate, Altitude, Speed, Heading, Leg) -- Create ORBIT first. local mission=AUFTRAG:NewORBIT_RACETRACK(Coordinate, Altitude, Speed, Heading, Leg) - + -- Mission type AWACS. mission.type=AUFTRAG.Type.AWACS - + mission:_SetLogID() - + -- Mission options: - mission.missionTask=ENUMS.MissionTask.AWACS + mission.missionTask=ENUMS.MissionTask.AWACS mission.optionROE=ENUMS.ROE.WeaponHold mission.optionROT=ENUMS.ROT.PassiveDefense - + mission.categories={AUFTRAG.Category.AIRCRAFT} - + mission.DCStask=mission:GetDCSMissionTask() - + return mission end @@ -1133,21 +1137,21 @@ end -- @param Wrapper.Positionable#POSITIONABLE Target The target to intercept. Can also be passed as simple @{Wrapper.Group#GROUP} or @{Wrapper.Unit#UNIT} object. -- @return #AUFTRAG self function AUFTRAG:NewINTERCEPT(Target) - + local mission=AUFTRAG:New(AUFTRAG.Type.INTERCEPT) - + mission:_TargetFromObject(Target) - + -- Mission options: - mission.missionTask=ENUMS.MissionTask.INTERCEPT - mission.missionFraction=0.1 + mission.missionTask=ENUMS.MissionTask.INTERCEPT + mission.missionFraction=0.1 mission.optionROE=ENUMS.ROE.OpenFire mission.optionROT=ENUMS.ROT.EvadeFire - + mission.categories={AUFTRAG.Category.AIRCRAFT} - + mission.DCStask=mission:GetDCSMissionTask() - + return mission end @@ -1172,24 +1176,24 @@ function AUFTRAG:NewCAP(ZoneCAP, Altitude, Speed, Coordinate, Heading, Leg, Targ -- Create ORBIT first. local mission=AUFTRAG:NewORBIT(Coordinate or ZoneCAP:GetCoordinate(), Altitude or 10000, Speed, Heading, Leg) - + -- Mission type CAP. mission.type=AUFTRAG.Type.CAP mission:_SetLogID() - + -- DCS task parameters: mission.engageZone=ZoneCAP mission.engageTargetTypes=TargetTypes or {"Air"} -- Mission options: - mission.missionTask=ENUMS.MissionTask.CAP + mission.missionTask=ENUMS.MissionTask.CAP mission.optionROE=ENUMS.ROE.OpenFire mission.optionROT=ENUMS.ROT.EvadeFire - + mission.categories={AUFTRAG.Category.AIRCRAFT} - + mission.DCStask=mission:GetDCSMissionTask() - + return mission end @@ -1214,24 +1218,24 @@ function AUFTRAG:NewCAS(ZoneCAS, Altitude, Speed, Coordinate, Heading, Leg, Targ -- Create ORBIT first. local mission=AUFTRAG:NewORBIT(Coordinate or ZoneCAS:GetCoordinate(), Altitude or 10000, Speed, Heading, Leg) - + -- Mission type CAS. mission.type=AUFTRAG.Type.CAS mission:_SetLogID() - + -- DCS Task options: mission.engageZone=ZoneCAS mission.engageTargetTypes=TargetTypes or {"Helicopters", "Ground Units", "Light armed ships"} - + -- Mission options: - mission.missionTask=ENUMS.MissionTask.CAS + mission.missionTask=ENUMS.MissionTask.CAS mission.optionROE=ENUMS.ROE.OpenFire mission.optionROT=ENUMS.ROT.EvadeFire - + mission.categories={AUFTRAG.Category.AIRCRAFT} mission.DCStask=mission:GetDCSMissionTask() - + return mission end @@ -1247,27 +1251,27 @@ end function AUFTRAG:NewCASENHANCED(CasZone, Altitude, Speed, RangeMax, NoEngageZoneSet, TargetTypes) local mission=AUFTRAG:New(AUFTRAG.Type.CASENHANCED) - + -- Ensure we got a ZONE and not just the zone name. if type(CasZone)=="string" then CasZone=ZONE:New(CasZone) end - + mission:_TargetFromObject(CasZone) - + mission.missionTask=mission:GetMissionTaskforMissionType(AUFTRAG.Type.CASENHANCED) - + mission:SetEngageDetected(RangeMax, TargetTypes or {"Helicopters", "Ground Units", "Light armed ships"}, CasZone, NoEngageZoneSet) - + mission.optionROE=ENUMS.ROE.OpenFire mission.optionROT=ENUMS.ROT.EvadeFire - - mission.missionFraction=1.0 + + mission.missionFraction=1.0 mission.missionSpeed=Speed and UTILS.KnotsToKmph(Speed) or nil mission.missionAltitude=Altitude and UTILS.FeetToMeters(Altitude) or nil - + mission.categories={AUFTRAG.Category.AIRCRAFT} - + mission.DCStask=mission:GetDCSMissionTask() return mission @@ -1287,26 +1291,26 @@ function AUFTRAG:NewFACA(Target, Designation, DataLink, Frequency, Modulation) local mission=AUFTRAG:New(AUFTRAG.Type.FACA) mission:_TargetFromObject(Target) - + -- TODO: check that target is really a group object! - + -- DCS Task options: mission.facDesignation=Designation --or AI.Task.Designation.AUTO mission.facDatalink=true mission.facFreq=Frequency or 133 mission.facModu=Modulation or radio.modulation.AM - + -- Mission options: mission.missionTask=ENUMS.MissionTask.AFAC mission.missionAltitude=nil mission.missionFraction=0.5 mission.optionROE=ENUMS.ROE.ReturnFire mission.optionROT=ENUMS.ROT.PassiveDefense - + mission.categories={AUFTRAG.Category.AIRCRAFT} mission.DCStask=mission:GetDCSMissionTask() - + return mission end @@ -1317,27 +1321,27 @@ end -- @param #number Altitude Engage altitude in feet. Default 5000 ft. -- @return #AUFTRAG self function AUFTRAG:NewBAI(Target, Altitude) - + local mission=AUFTRAG:New(AUFTRAG.Type.BAI) mission:_TargetFromObject(Target) - + -- DCS Task options: mission.engageWeaponType=ENUMS.WeaponFlag.AnyAG mission.engageWeaponExpend=AI.Task.WeaponExpend.ALL mission.engageAltitude=UTILS.FeetToMeters(Altitude or 5000) - + -- Mission options: mission.missionTask=ENUMS.MissionTask.GROUNDATTACK mission.missionAltitude=mission.engageAltitude mission.missionFraction=0.75 mission.optionROE=ENUMS.ROE.OpenFire mission.optionROT=ENUMS.ROT.PassiveDefense - + mission.categories={AUFTRAG.Category.AIRCRAFT} - - mission.DCStask=mission:GetDCSMissionTask() - + + mission.DCStask=mission:GetDCSMissionTask() + return mission end @@ -1347,16 +1351,16 @@ end -- @param #number Altitude Engage altitude in feet. Default 8000 ft. -- @return #AUFTRAG self function AUFTRAG:NewSEAD(Target, Altitude) - + local mission=AUFTRAG:New(AUFTRAG.Type.SEAD) mission:_TargetFromObject(Target) - + -- DCS Task options: mission.engageWeaponType=ENUMS.WeaponFlag.AnyAG --ENUMS.WeaponFlag.Cannons mission.engageWeaponExpend=AI.Task.WeaponExpend.ALL mission.engageAltitude=UTILS.FeetToMeters(Altitude or 8000) - + -- Mission options: mission.missionTask=ENUMS.MissionTask.SEAD mission.missionAltitude=mission.engageAltitude @@ -1364,11 +1368,11 @@ function AUFTRAG:NewSEAD(Target, Altitude) mission.optionROE=ENUMS.ROE.OpenFire mission.optionROT=ENUMS.ROT.EvadeFire --mission.optionROT=ENUMS.ROT.AllowAbortMission - + mission.categories={AUFTRAG.Category.AIRCRAFT} - - mission.DCStask=mission:GetDCSMissionTask() - + + mission.DCStask=mission:GetDCSMissionTask() + return mission end @@ -1380,25 +1384,25 @@ end function AUFTRAG:NewSTRIKE(Target, Altitude) local mission=AUFTRAG:New(AUFTRAG.Type.STRIKE) - + mission:_TargetFromObject(Target) - + -- DCS Task options: mission.engageWeaponType=ENUMS.WeaponFlag.AnyAG mission.engageWeaponExpend=AI.Task.WeaponExpend.ALL - mission.engageAltitude=UTILS.FeetToMeters(Altitude or 2000) - + mission.engageAltitude=UTILS.FeetToMeters(Altitude or 2000) + -- Mission options: mission.missionTask=ENUMS.MissionTask.GROUNDATTACK mission.missionAltitude=mission.engageAltitude mission.missionFraction=0.75 mission.optionROE=ENUMS.ROE.OpenFire mission.optionROT=ENUMS.ROT.PassiveDefense - + mission.categories={AUFTRAG.Category.AIRCRAFT} - + mission.DCStask=mission:GetDCSMissionTask() - + return mission end @@ -1410,9 +1414,9 @@ end function AUFTRAG:NewBOMBING(Target, Altitude) local mission=AUFTRAG:New(AUFTRAG.Type.BOMBING) - + mission:_TargetFromObject(Target) - + -- DCS task options: mission.engageWeaponType=ENUMS.WeaponFlag.AnyBomb mission.engageWeaponExpend=AI.Task.WeaponExpend.ALL @@ -1420,19 +1424,19 @@ function AUFTRAG:NewBOMBING(Target, Altitude) -- Mission options: mission.missionTask=ENUMS.MissionTask.GROUNDATTACK - mission.missionAltitude=mission.engageAltitude*0.8 + mission.missionAltitude=mission.engageAltitude*0.8 mission.missionFraction=0.5 mission.optionROE=ENUMS.ROE.OpenFire mission.optionROT=ENUMS.ROT.NoReaction -- No reaction is better. - + -- Evaluate result after 5 min. We might need time until the bombs have dropped and targets have been detroyed. mission.dTevaluate=5*60 - + mission.categories={AUFTRAG.Category.AIRCRAFT} - + -- Get DCS task. mission.DCStask=mission:GetDCSMissionTask() - + return mission end @@ -1448,9 +1452,9 @@ function AUFTRAG:NewBOMBRUNWAY(Airdrome, Altitude) end local mission=AUFTRAG:New(AUFTRAG.Type.BOMBRUNWAY) - - mission:_TargetFromObject(Airdrome) - + + mission:_TargetFromObject(Airdrome) + -- DCS task options: mission.engageWeaponType=ENUMS.WeaponFlag.AnyBomb mission.engageWeaponExpend=AI.Task.WeaponExpend.ALL @@ -1458,19 +1462,19 @@ function AUFTRAG:NewBOMBRUNWAY(Airdrome, Altitude) -- Mission options: mission.missionTask=ENUMS.MissionTask.RUNWAYATTACK - mission.missionAltitude=mission.engageAltitude*0.8 + mission.missionAltitude=mission.engageAltitude*0.8 mission.missionFraction=0.75 mission.optionROE=ENUMS.ROE.OpenFire mission.optionROT=ENUMS.ROT.PassiveDefense - + -- Evaluate result after 5 min. mission.dTevaluate=5*60 - + mission.categories={AUFTRAG.Category.AIRCRAFT} - + -- Get DCS task. mission.DCStask=mission:GetDCSMissionTask() - + return mission end @@ -1483,9 +1487,9 @@ end function AUFTRAG:NewBOMBCARPET(Target, Altitude, CarpetLength) local mission=AUFTRAG:New(AUFTRAG.Type.BOMBCARPET) - - mission:_TargetFromObject(Target) - + + mission:_TargetFromObject(Target) + -- DCS task options: mission.engageWeaponType=ENUMS.WeaponFlag.AnyBomb mission.engageWeaponExpend=AI.Task.WeaponExpend.ALL @@ -1496,19 +1500,19 @@ function AUFTRAG:NewBOMBCARPET(Target, Altitude, CarpetLength) -- Mission options: mission.missionTask=ENUMS.MissionTask.GROUNDATTACK - mission.missionAltitude=mission.engageAltitude*0.8 + mission.missionAltitude=mission.engageAltitude*0.8 mission.missionFraction=0.5 mission.optionROE=ENUMS.ROE.OpenFire mission.optionROT=ENUMS.ROT.NoReaction - + -- Evaluate result after 5 min. mission.dTevaluate=5*60 - + mission.categories={AUFTRAG.Category.AIRCRAFT} - + -- Get DCS task. mission.DCStask=mission:GetDCSMissionTask() - + return mission end @@ -1523,7 +1527,7 @@ end function AUFTRAG:NewESCORT(EscortGroup, OffsetVector, EngageMaxDistance, TargetTypes) local mission=AUFTRAG:New(AUFTRAG.Type.ESCORT) - + -- If only a string is passed we set a variable and check later if the group exists. if type(EscortGroup)=="string" then mission.escortGroupName=EscortGroup @@ -1531,23 +1535,23 @@ function AUFTRAG:NewESCORT(EscortGroup, OffsetVector, EngageMaxDistance, TargetT else mission:_TargetFromObject(EscortGroup) end - + -- DCS task parameters: mission.escortVec3=OffsetVector or {x=-100, y=0, z=200} mission.engageMaxDistance=EngageMaxDistance and UTILS.NMToMeters(EngageMaxDistance) or nil mission.engageTargetTypes=TargetTypes or {"Air"} - + -- Mission options: - mission.missionTask=ENUMS.MissionTask.ESCORT + mission.missionTask=ENUMS.MissionTask.ESCORT mission.missionFraction=0.1 mission.missionAltitude=1000 mission.optionROE=ENUMS.ROE.OpenFire -- TODO: what's the best ROE here? Make dependent on ESCORT or FOLLOW! mission.optionROT=ENUMS.ROT.PassiveDefense - + mission.categories={AUFTRAG.Category.AIRCRAFT} - + mission.DCStask=mission:GetDCSMissionTask() - + return mission end @@ -1560,17 +1564,17 @@ function AUFTRAG:NewRESCUEHELO(Carrier) local mission=AUFTRAG:New(AUFTRAG.Type.RESCUEHELO) mission:_TargetFromObject(Carrier) - + -- Mission options: mission.missionTask=ENUMS.MissionTask.NOTHING mission.missionFraction=0.5 mission.optionROE=ENUMS.ROE.WeaponHold mission.optionROT=ENUMS.ROT.NoReaction - + mission.categories={AUFTRAG.Category.HELICOPTER} - + mission.DCStask=mission:GetDCSMissionTask() - + return mission end @@ -1585,7 +1589,7 @@ end function AUFTRAG:NewTROOPTRANSPORT(TransportGroupSet, DropoffCoordinate, PickupCoordinate, PickupRadius) local mission=AUFTRAG:New(AUFTRAG.Type.TROOPTRANSPORT) - + if TransportGroupSet:IsInstanceOf("GROUP") then mission.transportGroupSet=SET_GROUP:New() mission.transportGroupSet:AddGroup(TransportGroupSet) @@ -1595,16 +1599,16 @@ function AUFTRAG:NewTROOPTRANSPORT(TransportGroupSet, DropoffCoordinate, PickupC mission:E(mission.lid.."ERROR: TransportGroupSet must be a GROUP or SET_GROUP object!") return nil end - + mission:_TargetFromObject(mission.transportGroupSet) - + mission.transportPickup=PickupCoordinate or mission:GetTargetCoordinate() mission.transportDropoff=DropoffCoordinate - + mission.transportPickupRadius=PickupRadius or 100 mission.missionTask=mission:GetMissionTaskforMissionType(AUFTRAG.Type.TROOPTRANSPORT) - + -- Debug. --mission.transportPickup:MarkToAll("Pickup Transport") --mission.transportDropoff:MarkToAll("Drop off") @@ -1612,9 +1616,9 @@ function AUFTRAG:NewTROOPTRANSPORT(TransportGroupSet, DropoffCoordinate, PickupC -- TODO: what's the best ROE here? mission.optionROE=ENUMS.ROE.ReturnFire mission.optionROT=ENUMS.ROT.PassiveDefense - + mission.categories={AUFTRAG.Category.HELICOPTER, AUFTRAG.Category.GROUND} - + mission.DCStask=mission:GetDCSMissionTask() return mission @@ -1631,27 +1635,27 @@ end function AUFTRAG:NewOPSTRANSPORT(CargoGroupSet, PickupZone, DeployZone) local mission=AUFTRAG:New(AUFTRAG.Type.OPSTRANSPORT) - + mission.transportGroupSet=CargoGroupSet - + mission:_TargetFromObject(mission.transportGroupSet) - + mission.opstransport=OPSTRANSPORT:New(CargoGroupSet, PickupZone, DeployZone) - + function mission.opstransport:OnAfterExecuting(From, Event, To) mission:Executing() end - + function mission.opstransport:OnAfterDelivered(From, Event, To) mission:Done() end - + -- TODO: what's the best ROE here? mission.optionROE=ENUMS.ROE.ReturnFire mission.optionROT=ENUMS.ROT.PassiveDefense - + mission.categories={AUFTRAG.Category.ALL} - + mission.DCStask=mission:GetDCSMissionTask() return mission @@ -1669,25 +1673,25 @@ end function AUFTRAG:NewARTY(Target, Nshots, Radius, Altitude) local mission=AUFTRAG:New(AUFTRAG.Type.ARTY) - + mission:_TargetFromObject(Target) - + mission.artyShots=Nshots or nil mission.artyRadius=Radius or 100 mission.artyAltitude=Altitude - + mission.engageWeaponType=ENUMS.WeaponFlag.Auto - + mission.optionROE=ENUMS.ROE.OpenFire -- Ground/naval need open fire! mission.optionAlarm=0 - + mission.missionFraction=0.0 - + -- Evaluate after 8 min. mission.dTevaluate=8*60 - + mission.categories={AUFTRAG.Category.GROUND, AUFTRAG.Category.NAVAL} - + mission.DCStask=mission:GetDCSMissionTask() return mission @@ -1705,27 +1709,27 @@ end function AUFTRAG:NewBARRAGE(Zone, Heading, Angle, Radius, Altitude, Nshots) local mission=AUFTRAG:New(AUFTRAG.Type.BARRAGE) - + mission:_TargetFromObject(Zone) - + mission.artyShots=Nshots mission.artyRadius=Radius or 100 mission.artyAltitude=Altitude mission.artyHeading=Heading mission.artyAngle=Angle - + mission.engageWeaponType=ENUMS.WeaponFlag.Auto - + mission.optionROE=ENUMS.ROE.OpenFire -- Ground/naval need open fire! mission.optionAlarm=0 - + mission.missionFraction=0.0 - + -- Evaluate after instantly. mission.dTevaluate=10 - + mission.categories={AUFTRAG.Category.GROUND, AUFTRAG.Category.NAVAL} - + mission.DCStask=mission:GetDCSMissionTask() return mission @@ -1741,28 +1745,28 @@ end function AUFTRAG:NewPATROLZONE(Zone, Speed, Altitude, Formation) local mission=AUFTRAG:New(AUFTRAG.Type.PATROLZONE) - + -- Ensure we got a ZONE and not just the zone name. if type(Zone)=="string" then Zone=ZONE:New(Zone) end - + mission:_TargetFromObject(Zone) - + mission.missionTask=mission:GetMissionTaskforMissionType(AUFTRAG.Type.PATROLZONE) - + mission.optionROE=ENUMS.ROE.OpenFire mission.optionROT=ENUMS.ROT.PassiveDefense mission.optionAlarm=ENUMS.AlarmState.Auto - - mission.missionFraction=1.0 + + mission.missionFraction=1.0 mission.missionSpeed=Speed and UTILS.KnotsToKmph(Speed) or nil mission.missionAltitude=Altitude and UTILS.FeetToMeters(Altitude) or nil - + mission.categories={AUFTRAG.Category.ALL} - + mission.DCStask=mission:GetDCSMissionTask() - + mission.DCStask.params.formation=Formation return mission @@ -1773,26 +1777,55 @@ end -- @param #AUFTRAG self -- @param Ops.Target#TARGET 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. +-- @param #string Formation The attack formation, e.g. "Wedge", "Vee" etc. -- @return #AUFTRAG self function AUFTRAG:NewARMORATTACK(Target, Speed, Formation) local mission=AUFTRAG:New(AUFTRAG.Type.ARMORATTACK) - + mission:_TargetFromObject(Target) - + mission.missionTask=mission:GetMissionTaskforMissionType(AUFTRAG.Type.ARMORATTACK) - + mission.optionROE=ENUMS.ROE.OpenFire mission.optionAlarm=ENUMS.AlarmState.Auto mission.optionFormation="On Road" mission.optionAttackFormation=Formation or "Wedge" - - mission.missionFraction=1.0 + + mission.missionFraction=1.0 mission.missionSpeed=Speed and UTILS.KnotsToKmph(Speed) or 20 - + mission.categories={AUFTRAG.Category.GROUND} - + + mission.DCStask=mission:GetDCSMissionTask() + + return mission +end + +--- **[GROUND]** Create a GROUNDATTACK mission. Ground group(s) will go to a target object and attack. +-- @param #AUFTRAG self +-- @param Wrapper.Positionable#POSITIONABLE Target The target to attack. Can be a GROUP, UNIT or STATIC object. +-- @param #number Speed Speed in knots. +-- @param #string Formation The attack formation, e.g. "Wedge", "Vee" etc. +-- @return #AUFTRAG self +function AUFTRAG:NewGROUNDATTACK(Target, Speed, Formation) + + local mission=AUFTRAG:New(AUFTRAG.Type.GROUNDATTACK) + + mission:_TargetFromObject(Target) + + mission.missionTask=mission:GetMissionTaskforMissionType(AUFTRAG.Type.GROUNDATTACK) + + mission.optionROE=ENUMS.ROE.OpenFire + mission.optionAlarm=ENUMS.AlarmState.Auto + mission.optionFormation="On Road" + mission.optionAttackFormation=Formation or "Wedge" + + mission.missionFraction=0.75 + mission.missionSpeed=Speed and UTILS.KnotsToKmph(Speed) or 20 + + mission.categories={AUFTRAG.Category.GROUND} + mission.DCStask=mission:GetDCSMissionTask() return mission @@ -1809,25 +1842,25 @@ end function AUFTRAG:NewRECON(ZoneSet, Speed, Altitude, Adinfinitum, Randomly) local mission=AUFTRAG:New(AUFTRAG.Type.RECON) - + mission:_TargetFromObject(ZoneSet) - + mission.missionTask=mission:GetMissionTaskforMissionType(AUFTRAG.Type.RECON) - + mission.optionROE=ENUMS.ROE.WeaponHold mission.optionROT=ENUMS.ROT.PassiveDefense mission.optionAlarm=ENUMS.AlarmState.Auto - + mission.missionFraction=0.5 mission.missionSpeed=Speed and UTILS.KnotsToKmph(Speed) or nil mission.missionAltitude=Altitude and UTILS.FeetToMeters(Altitude) or UTILS.FeetToMeters(2000) - + mission.categories={AUFTRAG.Category.ALL} - + mission.DCStask=mission:GetDCSMissionTask() mission.DCStask.params.adinfinitum=Adinfinitum mission.DCStask.params.randomly=Randomly - + if Randomly then local targets = mission.DCStask.params.target.targets -- Ops.Target#TARGET local shuffled = UTILS.ShuffleTable(targets) @@ -1844,18 +1877,18 @@ end function AUFTRAG:NewAMMOSUPPLY(Zone) local mission=AUFTRAG:New(AUFTRAG.Type.AMMOSUPPLY) - + mission:_TargetFromObject(Zone) - + mission.optionROE=ENUMS.ROE.WeaponHold mission.optionAlarm=ENUMS.AlarmState.Auto - + mission.missionFraction=1.0 - + mission.missionWaypointRadius=0 - + mission.categories={AUFTRAG.Category.GROUND} - + mission.DCStask=mission:GetDCSMissionTask() return mission @@ -1868,16 +1901,16 @@ end function AUFTRAG:NewFUELSUPPLY(Zone) local mission=AUFTRAG:New(AUFTRAG.Type.FUELSUPPLY) - + 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 @@ -1892,18 +1925,18 @@ end function AUFTRAG:NewALERT5(MissionType) local mission=AUFTRAG:New(AUFTRAG.Type.ALERT5) - + mission.missionTask=self:GetMissionTaskforMissionType(MissionType) - + mission.optionROE=ENUMS.ROE.WeaponHold mission.optionROT=ENUMS.ROT.NoReaction - + mission.alert5MissionType=MissionType - + mission.missionFraction=1.0 - + mission.categories={AUFTRAG.Category.AIRCRAFT} - + mission.DCStask=mission:GetDCSMissionTask() return mission @@ -1916,16 +1949,16 @@ end function AUFTRAG:NewONGUARD(Coordinate) local mission=AUFTRAG:New(AUFTRAG.Type.ONGUARD) - + mission:_TargetFromObject(Coordinate) - + 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 @@ -1939,18 +1972,18 @@ end function AUFTRAG:NewARMOREDGUARD(Coordinate,Formation) local mission=AUFTRAG:New(AUFTRAG.Type.ARMOREDGUARD) - + mission:_TargetFromObject(Coordinate) - + mission.optionROE=ENUMS.ROE.OpenFire mission.optionAlarm=ENUMS.AlarmState.Auto mission.optionFormation=Formation or "On Road" --mission.optionAttackFormation=Formation or "Wedge" - + mission.missionFraction=1.0 - + mission.categories={AUFTRAG.Category.GROUND} - + mission.DCStask=mission:GetDCSMissionTask() return mission @@ -1964,7 +1997,7 @@ end function AUFTRAG:NewFromTarget(Target, MissionType) local mission=nil --#AUFTRAG - + if MissionType==AUFTRAG.Type.ANTISHIP then mission=self:NewANTISHIP(Target, Altitude) elseif MissionType==AUFTRAG.Type.ARTY then @@ -1988,7 +2021,7 @@ function AUFTRAG:NewFromTarget(Target, MissionType) else return nil end - + return mission end @@ -2006,7 +2039,7 @@ function AUFTRAG:_DetermineAuftragType(Target) local auftrag=nil if Target:IsInstanceOf("GROUP") then - group=Target --Target is already a group. + group=Target --Target is already a group. elseif Target:IsInstanceOf("UNIT") then group=Target:GetGroup() elseif Target:IsInstanceOf("AIRBASE") then @@ -2014,69 +2047,69 @@ function AUFTRAG:_DetermineAuftragType(Target) elseif Target:IsInstanceOf("SCENERY") then scenery=Target end - + if group then local category=group:GetCategory() local attribute=group:GetAttribute() if category==Group.Category.AIRPLANE or category==Group.Category.HELICOPTER then - + --- -- A2A: Intercept --- - + auftrag=AUFTRAG.Type.INTERCEPT - + elseif category==Group.Category.GROUND or category==Group.Category.TRAIN then - + --- -- GROUND --- if attribute==GROUP.Attribute.GROUND_SAM then - + -- SEAD/DEAD - + auftrag=AUFTRAG.Type.SEAD - + elseif attribute==GROUP.Attribute.GROUND_AAA then - + auftrag=AUFTRAG.Type.BAI - + elseif attribute==GROUP.Attribute.GROUND_ARTILLERY then - + auftrag=AUFTRAG.Type.BAI - + elseif attribute==GROUP.Attribute.GROUND_INFANTRY then - + auftrag=AUFTRAG.Type.CAS - + elseif attribute==GROUP.Attribute.GROUND_TANK then - + auftrag=AUFTRAG.Type.BAI - + else auftrag=AUFTRAG.Type.BAI - + end - + elseif category==Group.Category.SHIP then - + --- -- NAVAL --- - + auftrag=AUFTRAG.Type.ANTISHIP - + else self:T(self.lid.."ERROR: Unknown Group category!") end - + elseif airbase then - auftrag=AUFTRAG.Type.BOMBRUNWAY + auftrag=AUFTRAG.Type.BOMBRUNWAY elseif scenery then auftrag=AUFTRAG.Type.STRIKE elseif coordinate then @@ -2093,11 +2126,11 @@ end function AUFTRAG:NewAUTO(EngageGroup) local mission=nil --#AUFTRAG - + local Target=EngageGroup local auftrag=self:_DetermineAuftragType(EngageGroup) - + if auftrag==AUFTRAG.Type.ANTISHIP then mission=AUFTRAG:NewANTISHIP(Target) elseif auftrag==AUFTRAG.Type.ARTY then @@ -2115,13 +2148,13 @@ function AUFTRAG:NewAUTO(EngageGroup) elseif auftrag==AUFTRAG.Type.CAP then mission=AUFTRAG:NewCAP(ZoneCAP,Altitude,Speed,Coordinate,Heading,Leg,TargetTypes) elseif auftrag==AUFTRAG.Type.CAS then - mission=AUFTRAG:NewCAS(ZoneCAS,Altitude,Speed,Coordinate,Heading,Leg,TargetTypes) + mission=AUFTRAG:NewCAS(ZoneCAS,Altitude,Speed,Coordinate,Heading,Leg,TargetTypes) elseif auftrag==AUFTRAG.Type.ESCORT then - mission=AUFTRAG:NewESCORT(EscortGroup,OffsetVector,EngageMaxDistance,TargetTypes) + mission=AUFTRAG:NewESCORT(EscortGroup,OffsetVector,EngageMaxDistance,TargetTypes) elseif auftrag==AUFTRAG.Type.FACA then mission=AUFTRAG:NewFACA(Target,Designation,DataLink,Frequency,Modulation) elseif auftrag==AUFTRAG.Type.FERRY then - -- Not implemented yet. + -- Not implemented yet. elseif auftrag==AUFTRAG.Type.GCICAP then mission=AUFTRAG:NewGCICAP(Coordinate,Altitude,Speed,Heading,Leg) elseif auftrag==AUFTRAG.Type.INTERCEPT then @@ -2129,7 +2162,7 @@ function AUFTRAG:NewAUTO(EngageGroup) elseif auftrag==AUFTRAG.Type.ORBIT then mission=AUFTRAG:NewORBIT(Coordinate,Altitude,Speed,Heading,Leg) elseif auftrag==AUFTRAG.Type.RECON then - -- Not implemented yet. + -- Not implemented yet. elseif auftrag==AUFTRAG.Type.RESCUEHELO then mission=AUFTRAG:NewRESCUEHELO(Carrier) elseif auftrag==AUFTRAG.Type.SEAD then @@ -2141,9 +2174,9 @@ function AUFTRAG:NewAUTO(EngageGroup) elseif auftrag==AUFTRAG.Type.TROOPTRANSPORT then mission=AUFTRAG:NewTROOPTRANSPORT(TransportGroupSet,DropoffCoordinate,PickupCoordinate) else - + end - + if mission then mission:SetPriority(10, true) end @@ -2164,7 +2197,7 @@ function AUFTRAG:SetTime(ClockStart, ClockStop) -- Current mission time. local Tnow=timer.getAbsTime() - + -- Set start time. Default in 5 sec. local Tstart=Tnow+5 if ClockStart and type(ClockStart)=="number" then @@ -2180,13 +2213,13 @@ function AUFTRAG:SetTime(ClockStart, ClockStop) elseif ClockStop and type(ClockStop)=="string" then Tstop=UTILS.ClockToSeconds(ClockStop) end - + self.Tstart=Tstart self.Tstop=Tstop if Tstop then self.duration=self.Tstop-self.Tstart - end + end return self end @@ -2218,7 +2251,7 @@ function AUFTRAG:SetPushTime(ClockPush) return self end ---- Set mission priority and (optional) urgency. Urgent missions can cancel other running missions. +--- Set mission priority and (optional) urgency. Urgent missions can cancel other running missions. -- @param #AUFTRAG self -- @param #number Prio Priority 1=high, 100=low. Default 50. -- @param #boolean Urgent If *true*, another running mission might be cancelled if it has a lower priority. @@ -2266,7 +2299,7 @@ end function AUFTRAG:SetRequiredAssets(NassetsMin, NassetsMax) self.NassetsMin=NassetsMin or 1 - + self.NassetsMax=NassetsMax or self.NassetsMin -- Ensure that max is at least equal to min. @@ -2301,14 +2334,14 @@ end function AUFTRAG:SetRequiredEscorts(NescortMin, NescortMax) self.NescortMin=NescortMin or 1 - + self.NescortMax=NescortMax or self.NescortMin -- Ensure that max is at least equal to min. if self.NescortMaxself.Tstop then return false end - + -- All start conditions true? local startme=self:EvalConditionsAll(self.conditionStart) - + if not startme then return false end - + -- We're good to go! return true @@ -3152,7 +3185,7 @@ end -- @param #AUFTRAG self -- @return #boolean If true, mission should be cancelled. function AUFTRAG:IsReadyToCancel() - + local Tnow=timer.getAbsTime() -- Stop time already passed. @@ -3162,20 +3195,20 @@ function AUFTRAG:IsReadyToCancel() -- Evaluate failure condition. One is enough. local failure=self:EvalConditionsAny(self.conditionFailure) - + if failure then self.failurecondition=true return true - end - + end + -- Evaluate success consitions. One is enough. local success=self:EvalConditionsAny(self.conditionSuccess) - + if success then self.successcondition=true return true end - + -- No criterion matched. return false end @@ -3186,7 +3219,7 @@ end -- @param #AUFTRAG self -- @return #boolean If true, mission groups can push. function AUFTRAG:IsReadyToPush() - + local Tnow=timer.getAbsTime() -- Push time passed? @@ -3196,7 +3229,7 @@ function AUFTRAG:IsReadyToPush() -- Evaluate push condition(s) if any. All need to be true. local push=self:EvalConditionsAll(self.conditionPush) - + return push end @@ -3209,15 +3242,15 @@ function AUFTRAG:EvalConditionsAll(Conditions) -- Any stop condition must be true. for _,_condition in pairs(Conditions or {}) do local condition=_condition --#AUFTRAG.Condition - + -- Call function. local istrue=condition.func(unpack(condition.arg)) - + -- Any false will return false. if not istrue then return false end - + end -- All conditions were true. @@ -3234,15 +3267,15 @@ function AUFTRAG:EvalConditionsAny(Conditions) -- Any stop condition must be true. for _,_condition in pairs(Conditions or {}) do local condition=_condition --#AUFTRAG.Condition - + -- Call function. local istrue=condition.func(unpack(condition.arg)) - + -- Any true will return true. if istrue then return true end - + end -- No condition was true. @@ -3262,69 +3295,70 @@ function AUFTRAG:onafterStatus(From, Event, To) -- Current abs. mission time. local Tnow=timer.getAbsTime() - + -- ESCORT: Check if only the group NAME of an escort had been specified. if self.escortGroupName then -- Try to find the group. local group=GROUP:FindByName(self.escortGroupName) if group and group:IsAlive() then - + -- Debug info. - self:T(self.lid..string.format("ESCORT group %s is now alive. Updating DCS task and adding group to TARGET", tostring(self.escortGroupName))) - + self:T(self.lid..string.format("ESCORT group %s is now alive. Updating DCS task and adding group to TARGET", tostring(self.escortGroupName))) + -- Add TARGET object. self.engageTarget:AddObject(group) - + -- Update DCS task with the known group ID. self.DCStask=self:GetDCSMissionTask() - + -- Set value to nil so we do not do this again in the next cycle. - self.escortGroupName=nil + self.escortGroupName=nil end end -- Number of alive mission targets. local Ntargets=self:CountMissionTargets() local Ntargets0=self:GetTargetInitialNumber() - + -- Number of alive groups attached to this mission. local Ngroups=self:CountOpsGroups() -- Check if mission is not OVER yet. if self:IsNotOver() then - + if self:CheckGroupsDone() then - + -- All groups have reported MISSON DONE. self:Done() - + elseif (self.Tstop and Tnow>self.Tstop+10) then -- Cancel mission if stop time passed. self:Cancel() - + elseif self.durationExe and self.Texecuting and Tnow-self.Texecuting>self.durationExe then - + -- Backup repeat values local Nrepeat=self.Nrepeat local NrepeatS=self.NrepeatSuccess local NrepeatF=self.NrepeatFailure -- Cancel mission if stop time passed. - self:Cancel() - + self:Cancel() + self.Nrepeat=Nrepeat self.NrepeatSuccess=NrepeatS self.NrepeatFailure=NrepeatF - + elseif (Ntargets0>0 and Ntargets==0) then -- Cancel mission if mission targets are gone (if there were any in the beginning). -- TODO: I commented this out for some reason but I forgot why... + self:T(self.lid.."No targets left cancelling mission!") self:Cancel() - + elseif self:IsExecuting() then - + -- Had the case that mission was in state Executing but all assigned groups were dead. -- TODO: might need to loop over all assigned groups if Ngroups==0 then @@ -3342,41 +3376,41 @@ function AUFTRAG:onafterStatus(From, Event, To) self:Done() end end - + end - + end - + -- Current FSM state. local fsmstate=self:GetState() - - -- Check for error. + + -- Check for error. if fsmstate~=self.status then self:T(self.lid..string.format("ERROR: FSM state %s != %s mission status!", fsmstate, self.status)) end - + -- General info. if self.verbose>=1 then - + -- Mission start stop time. local Cstart=UTILS.SecondsToClock(self.Tstart, true) local Cstop=self.Tstop and UTILS.SecondsToClock(self.Tstop, true) or "INF" - + local targetname=self:GetTargetName() or "unknown" - + local Nlegions=#self.legions local commander=self.commander and self.statusCommander or "N/A" local chief=self.chief and self.statusChief or "N/A" - + -- Info message. - self:T(self.lid..string.format("Status %s: Target=%s, T=%s-%s, assets=%d, groups=%d, targets=%d, legions=%d, commander=%s, chief=%s", + self:T(self.lid..string.format("Status %s: Target=%s, T=%s-%s, assets=%d, groups=%d, targets=%d, legions=%d, commander=%s, chief=%s", self.status, targetname, Cstart, Cstop, #self.assets, Ngroups, Ntargets, Nlegions, commander, chief)) end -- Group info. if self.verbose>=2 then -- Data on assigned groups. - local text="Group data:" + local text="Group data:" for groupname,_groupdata in pairs(self.groupdata) do local groupdata=_groupdata --#AUFTRAG.GroupData text=text..string.format("\n- %s: status mission=%s opsgroup=%s", groupname, groupdata.status, groupdata.opsgroup and groupdata.opsgroup:GetState() or "N/A") @@ -3394,12 +3428,12 @@ function AUFTRAG:onafterStatus(From, Event, To) else self:__Status(-30) end - + -- Update F10 marker. if self.markerOn then self:UpdateMarker() end - + end --- Evaluate mission outcome - success or failure. @@ -3409,51 +3443,51 @@ function AUFTRAG:Evaluate() -- Assume success and check if any failed condition applies. local failed=false - + -- Target damage in %. local targetdamage=self:GetTargetDamage() - + -- Own damage in %. local owndamage=self.Ncasualties/self.Nelements*100 -- Current number of mission targets. local Ntargets=self:CountMissionTargets() local Ntargets0=self:GetTargetInitialNumber() - + local Life=self:GetTargetLife() local Life0=self:GetTargetInitialLife() - - + + if Ntargets0>0 then - + --- -- Mission had targets --- - + -- Check if failed. if self.type==AUFTRAG.Type.TROOPTRANSPORT or self.type==AUFTRAG.Type.ESCORT then - + -- Transported or escorted groups have to survive. if Ntargets0 then failed=true end - + end - + else --- @@ -3464,13 +3498,13 @@ function AUFTRAG:Evaluate() if self.Nelements==self.Ncasualties then failed=true end - + end -- Any success condition true? local successCondition=self:EvalConditionsAny(self.conditionSuccess) - + -- Any failure condition true? local failureCondition=self:EvalConditionsAny(self.conditionFailure) @@ -3479,14 +3513,14 @@ function AUFTRAG:Evaluate() elseif successCondition then failed=false end - + -- Debug text. if self.verbose > 0 then local text=string.format("Evaluating mission:\n") text=text..string.format("Own casualties = %d/%d\n", self.Ncasualties, self.Nelements) text=text..string.format("Own losses = %.1f %%\n", owndamage) text=text..string.format("Killed units = %d\n", self.Nkills) - text=text..string.format("--------------------------\n") + text=text..string.format("--------------------------\n") text=text..string.format("Targets left = %d/%d\n", Ntargets, Ntargets0) text=text..string.format("Targets life = %.1f/%.1f\n", Life, Life0) text=text..string.format("Enemy losses = %.1f %%\n", targetdamage) @@ -3496,9 +3530,9 @@ function AUFTRAG:Evaluate() text=text..string.format("--------------------------\n") text=text..string.format("Final Success = %s\n", tostring(not failed)) text=text..string.format("=========================") - self:I(self.lid..text) + self:I(self.lid..text) end - + -- Trigger events. if failed then self:I(self.lid..string.format("Mission %d [%s] failed!", self.auftragsnummer, self.type)) @@ -3511,7 +3545,7 @@ function AUFTRAG:Evaluate() if self.chief then self.chief.Nsuccess=self.chief.Nsuccess+1 end - self:Success() + self:Success() end return self @@ -3575,13 +3609,13 @@ function AUFTRAG:SetGroupStatus(opsgroup, status) self:T(self.lid.."WARNING: Could not SET flight data for flight group. Setting status to DONE") end end - + -- Check if mission is NOT over. local isNotOver=self:IsNotOver() - + -- Check if all assigned groups are done. local groupsDone=self:CheckGroupsDone() - + -- Debug info. self:T2(self.lid..string.format("Setting OPSGROUP %s status to %s. IsNotOver=%s CheckGroupsDone=%s", opsgroup.groupname, self:GetGroupStatus(opsgroup), tostring(self:IsNotOver()), tostring(groupsDone))) @@ -3591,8 +3625,8 @@ function AUFTRAG:SetGroupStatus(opsgroup, status) self:Done() else self:T3(self.lid.."Mission NOT DONE yet!") - end - + end + return self end @@ -3602,16 +3636,16 @@ end -- @return #string The group status. function AUFTRAG:GetGroupStatus(opsgroup) self:T3(self.lid..string.format("Trying to get Flight status for flight group %s", opsgroup and opsgroup.groupname or "nil")) - + local groupdata=self:GetGroupData(opsgroup) - + if groupdata then return groupdata.status else - + self:T(self.lid..string.format("WARNING: Could not GET groupdata for opsgroup %s. Returning status DONE.", opsgroup and opsgroup.groupname or "nil")) return AUFTRAG.GroupStatus.DONE - + end end @@ -3626,7 +3660,7 @@ function AUFTRAG:AddLegion(Legion) -- Add legion to table. table.insert(self.legions, Legion) - + return self end @@ -3641,12 +3675,12 @@ function AUFTRAG:RemoveLegion(Legion) 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)) + self:T(self.lid..string.format("Removing legion %s", Legion.alias)) table.remove(self.legions, i) return self end end - + self:T(self.lid..string.format("ERROR: Legion %s not found and could not be removed!", Legion.alias)) return self end @@ -3795,11 +3829,11 @@ function AUFTRAG:CheckGroupsDone() if not (groupdata.status==AUFTRAG.GroupStatus.DONE or groupdata.status==AUFTRAG.GroupStatus.CANCELLED) then -- At least this flight is not DONE or CANCELLED. self:T(self.lid..string.format("CheckGroupsDone: OPSGROUP %s is not DONE or CANCELLED but in state %s. Mission NOT DONE!", groupdata.opsgroup.groupname, groupdata.status)) - return false + return false end end end - + -- Check status of all LEGIONs. for _,_legion in pairs(self.legions) do local legion=_legion --Ops.Legion#LEGION @@ -3810,7 +3844,7 @@ function AUFTRAG:CheckGroupsDone() return false end end - + -- Check commander status. if self.commander then if not self.statusCommander==AUFTRAG.Status.CANCELLED then @@ -3818,28 +3852,28 @@ function AUFTRAG:CheckGroupsDone() return false end end - + -- Check chief status. if self.chief then if not self.statusChief==AUFTRAG.Status.CANCELLED then - self:T(self.lid..string.format("CheckGroupsDone: CHIEF is not CANCELLED but in state %s. Mission NOT DONE!", self.statusChief)) + self:T(self.lid..string.format("CheckGroupsDone: CHIEF is not CANCELLED but in state %s. Mission NOT DONE!", self.statusChief)) return false end end - + -- These are early stages, where we might not even have a opsgroup defined to be checked. If there were any groups, we checked above. if self:IsPlanned() or self:IsQueued() or self:IsRequested() then self:T(self.lid..string.format("CheckGroupsDone: Mission is still in state %s [FSM=%s] (PLANNED or QUEUED or REQUESTED). Mission NOT DONE!", self.status, self:GetState())) return false end - + -- It could be that all flights were destroyed on the way to the mission execution waypoint. -- TODO: would be better to check if everybody is dead by now. if self:IsStarted() and self:CountOpsGroups()==0 then self:T(self.lid..string.format("CheckGroupsDone: Mission is STARTED state %s [FSM=%s] but count of alive OPSGROUP is zero. Mission DONE!", self.status, self:GetState())) return true end - + return true end @@ -3857,14 +3891,14 @@ function AUFTRAG:OnEventUnitLost(EventData) local unit=EventData.IniUnit local group=EventData.IniGroup local unitname=EventData.IniUnitName - + for _,_groupdata in pairs(self.groupdata) do local groupdata=_groupdata --#AUFTRAG.GroupData if groupdata and groupdata.opsgroup and groupdata.opsgroup.groupname==EventData.IniGroupName then self:T(self.lid..string.format("UNIT LOST event for opsgroup %s unit %s", groupdata.opsgroup.groupname, EventData.IniUnitName)) end end - + end end @@ -3905,14 +3939,14 @@ function AUFTRAG:onafterRequested(From, Event, To) self:T(self.lid..string.format("New mission status=%s", self.status)) end ---- On after "Assign" event. +--- On after "Assign" event. -- @param #AUFTRAG self -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. function AUFTRAG:onafterAssign(From, Event, To) self.status=AUFTRAG.Status.ASSIGNED - self:T(self.lid..string.format("New mission status=%s", self.status)) + self:T(self.lid..string.format("New mission status=%s", self.status)) end --- On after "Schedule" event. Mission is added to the mission queue of an OPSGROUP. @@ -3922,7 +3956,7 @@ end -- @param #string To To state. function AUFTRAG:onafterScheduled(From, Event, To) self.status=AUFTRAG.Status.SCHEDULED - self:T(self.lid..string.format("New mission status=%s", self.status)) + self:T(self.lid..string.format("New mission status=%s", self.status)) end --- On after "Start" event. @@ -3933,7 +3967,7 @@ end function AUFTRAG:onafterStarted(From, Event, To) self.status=AUFTRAG.Status.STARTED self.Tstarted=timer.getAbsTime() - self:T(self.lid..string.format("New mission status=%s", self.status)) + self:T(self.lid..string.format("New mission status=%s", self.status)) end --- On after "Execute" event. @@ -3944,7 +3978,7 @@ end function AUFTRAG:onafterExecuting(From, Event, To) self.status=AUFTRAG.Status.EXECUTING self.Texecuting=timer.getAbsTime() - self:T(self.lid..string.format("New mission status=%s", self.status)) + self:T(self.lid..string.format("New mission status=%s", self.status)) end --- On after "ElementDestroyed" event. @@ -3970,7 +4004,7 @@ function AUFTRAG:onafterGroupDead(From, Event, To, OpsGroup) if asset then self:AssetDead(asset) end - + end --- On after "AssetDead" event. @@ -3980,25 +4014,25 @@ end -- @param #string To To state. -- @param Functional.Warehouse#WAREHOUSE.Assetitem Asset The asset. function AUFTRAG:onafterAssetDead(From, Event, To, Asset) - - -- Number of groups alive. + + -- Number of groups alive. local N=self:CountOpsGroups() - + self:T(self.lid..string.format("Asset %s dead! Number of ops groups remaining %d", tostring(Asset.spawngroupname), N)) - + -- All assets dead? if N==0 then - + if self:IsNotOver() then - + -- Cancel mission. Wait for next mission update to evaluate SUCCESS or FAILURE. self:Cancel() - + else - + --self:E(self.lid.."ERROR: All assets are dead not but mission was already over... Investigate!") -- Now this can happen, because when a opsgroup dies (sometimes!), the mission is DONE - + end end @@ -4019,59 +4053,59 @@ function AUFTRAG:onafterCancel(From, Event, To) -- Debug info. self:T(self.lid..string.format("CANCELLING mission in status %s. Will wait for %d groups to report mission DONE before evaluation", self.status, Ngroups)) - + -- Time stamp. self.Tover=timer.getAbsTime() - + -- No more repeats. self.Nrepeat=self.repeated self.NrepeatFailure=self.repeatedFailure self.NrepeatSuccess=self.repeatedSuccess - + -- Not necessary to delay the evaluaton?! self.dTevaluate=0 - + if self.chief then -- Debug info. self:T(self.lid..string.format("CHIEF will cancel the mission. Will wait for mission DONE before evaluation!")) - + -- CHIEF will cancel the mission. self.chief:MissionCancel(self) - + elseif self.commander then - + -- Debug info. self:T(self.lid..string.format("COMMANDER will cancel the mission. Will wait for mission DONE before evaluation!")) - + -- COMMANDER will cancel the mission. self.commander:MissionCancel(self) elseif self.legions and #self.legions>0 then - + -- Loop over all LEGIONs. for _,_legion in pairs(self.legions or {}) do local legion=_legion --Ops.Legion#LEGION - + -- Debug info. self:T(self.lid..string.format("LEGION %s will cancel the mission. Will wait for mission DONE before evaluation!", legion.alias)) - + -- Legion will cancel all flight missions and remove queued request from warehouse queue. legion:MissionCancel(self) - + end - - else - + + else + -- Debug info. self:T(self.lid..string.format("No legion, commander or chief. Attached flights will cancel the mission on their own. Will wait for mission DONE before evaluation!")) - + -- Loop over all groups. for _,_groupdata in pairs(self.groupdata or {}) do local groupdata=_groupdata --#AUFTRAG.GroupData groupdata.opsgroup:MissionCancel(self) end - + end -- Special mission states. @@ -4090,21 +4124,21 @@ end function AUFTRAG:onafterDone(From, Event, To) self.status=AUFTRAG.Status.DONE self:T(self.lid..string.format("New mission status=%s", self.status)) - + -- Set time stamp. self.Tover=timer.getAbsTime() - + -- Not executing any more. self.Texecuting=nil - + -- Set status for CHIEF, COMMANDER and LEGIONs - self.statusChief=AUFTRAG.Status.DONE - self.statusCommander=AUFTRAG.Status.DONE + self.statusChief=AUFTRAG.Status.DONE + self.statusCommander=AUFTRAG.Status.DONE for _,_legion in pairs(self.legions) do local Legion=_legion --Ops.Legion#LEGION self:SetLegionStatus(Legion, AUFTRAG.Status.DONE) end - + end --- On after "Success" event. @@ -4116,17 +4150,17 @@ function AUFTRAG:onafterSuccess(From, Event, To) self.status=AUFTRAG.Status.SUCCESS self:T(self.lid..string.format("New mission status=%s", self.status)) - + -- Set status for CHIEF, COMMANDER and LEGIONs - self.statusChief=self.status - self.statusCommander=self.status + self.statusChief=self.status + self.statusCommander=self.status for _,_legion in pairs(self.legions) do local Legion=_legion --Ops.Legion#LEGION self:SetLegionStatus(Legion, self.status) - end - + end + local repeatme=self.repeatedSuccess Repeat mission!", self.repeated+1, N)) self:Repeat() - + else - + -- Stop mission. self:T(self.lid..string.format("Mission SUCCESS! Number of max repeats %d reached ==> Stopping mission!", self.repeated+1)) self:Stop() - + end end @@ -4160,34 +4194,34 @@ function AUFTRAG:onafterFailed(From, Event, To) self:T(self.lid..string.format("New mission status=%s", self.status)) -- Set status for CHIEF, COMMANDER and LEGIONs - self.statusChief=self.status - self.statusCommander=self.status + self.statusChief=self.status + self.statusCommander=self.status for _,_legion in pairs(self.legions) do local Legion=_legion --Ops.Legion#LEGION self:SetLegionStatus(Legion, self.status) - end - + end + local repeatme=self.repeatedFailure Repeat mission!", self.repeated+1, N)) self:Repeat() - + else - + -- Stop mission. self:T(self.lid..string.format("Mission FAILED! Number of max repeats %d reached ==> Stopping mission!", self.repeated+1)) self:Stop() - - end + + end end @@ -4200,7 +4234,7 @@ function AUFTRAG:onbeforeRepeat(From, Event, To) if not (self.chief or self.commander or #self.legions>0) then self:E(self.lid.."ERROR: Mission can only be repeated by a CHIEF, COMMANDER or LEGION! Stopping AUFTRAG") - self:Stop() + self:Stop() return false end @@ -4216,69 +4250,69 @@ function AUFTRAG:onafterRepeat(From, Event, To) -- Set mission status to PLANNED. self.status=AUFTRAG.Status.PLANNED - + -- Debug info. self:T(self.lid..string.format("New mission status=%s (on Repeat)", self.status)) - + -- Set status for CHIEF, COMMANDER and LEGIONs - self.statusChief=self.status - self.statusCommander=self.status + self.statusChief=self.status + self.statusCommander=self.status for _,_legion in pairs(self.legions) do local Legion=_legion --Ops.Legion#LEGION self:SetLegionStatus(Legion, self.status) - end + end -- Increase repeat counter. self.repeated=self.repeated+1 - + if self.chief then - + 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 - + end + -- Remove mission from airwing because WC will assign it again but maybe to a different wing. for _,_legion in pairs(self.legions) do local legion=_legion --Ops.Legion#LEGION legion:RemoveMission(self) - end - + end + elseif self.commander then - + self.statusCommander=AUFTRAG.Status.PLANNED - + -- Remove mission from airwing because WC will assign it again but maybe to a different wing. for _,_legion in pairs(self.legions) do local legion=_legion --Ops.Legion#LEGION legion:RemoveMission(self) self:SetLegionStatus(legion, AUFTRAG.Status.PLANNED) - end - + end + elseif #self.legions>0 then - + -- Remove mission from airwing because WC will assign it again but maybe to a different wing. for _,_legion in pairs(self.legions) do local legion=_legion --Ops.Legion#LEGION legion:RemoveMission(self) self:SetLegionStatus(legion, AUFTRAG.Status.PLANNED) legion:AddMission(self) - end - + end + else self:E(self.lid.."ERROR: Mission can only be repeated by a CHIEF, COMMANDER or LEGION! Stopping AUFTRAG") self:Stop() return end - - + + -- No mission assets. self.assets={} - - + + -- Remove OPS groups. This also removes the mission from the OPSGROUP mission queue. for groupname,_groupdata in pairs(self.groupdata) do local groupdata=_groupdata --#AUFTRAG.GroupData @@ -4286,18 +4320,18 @@ function AUFTRAG:onafterRepeat(From, Event, To) if opsgroup then self:DelOpsGroup(opsgroup) end - - end + + end -- No flight data. self.groupdata={} - + -- Reset casualties and units assigned. self.Ncasualties=0 self.Nelements=0 - + -- Update DCS mission task. Could be that the initial task (e.g. for bombing) was destroyed. Then we need to update the coordinate. self.DCStask=self:GetDCSMissionTask() - + -- Call status again. self:__Status(-30) @@ -4312,19 +4346,19 @@ function AUFTRAG:onafterStop(From, Event, To) -- Debug info. self:T(self.lid..string.format("STOPPED mission in status=%s. Removing missions from queues. Stopping CallScheduler!", self.status)) - + -- TODO: Mission should be OVER! we dont want to remove running missions from any queues. - + -- Remove mission from CHIEF queue. if self.chief then self.chief:RemoveMission(self) end - + -- Remove mission from WINGCOMMANDER queue. if self.commander then self.commander:RemoveMission(self) end - + -- Remove mission from LEGION queues. if #self.legions>0 then for _,_legion in pairs(self.legions) do @@ -4341,13 +4375,13 @@ function AUFTRAG:onafterStop(From, Event, To) -- No mission assets. self.assets={} - + -- No flight data. self.groupdata={} -- Clear pending scheduler calls. self.CallScheduler:Clear() - + end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -4360,26 +4394,26 @@ end function AUFTRAG:_TargetFromObject(Object) if not self.engageTarget then - + if Object and Object:IsInstanceOf("TARGET") then - + self.engageTarget=Object - + else --if Object then - + self.engageTarget=TARGET:New(Object) - - end - + + end + else - + -- Target was already specified elsewhere. - + end - + -- Debug info. --self:T2(self.lid..string.format("Mission Target %s Type=%s, Ntargets=%d, Lifepoints=%d", self.engageTarget.lid, self.engageTarget.lid, self.engageTarget.N0, self.engageTarget:GetLife())) - + return self end @@ -4390,11 +4424,11 @@ end function AUFTRAG:CountMissionTargets() local N=0 - + if self.engageTarget then N=self.engageTarget:CountTargets() end - + return N end @@ -4496,23 +4530,23 @@ end -- @param #AUFTRAG self -- @return Core.Point#COORDINATE The target coordinate or *nil*. function AUFTRAG:GetTargetCoordinate() - + if self.transportPickup then - - -- Special case where we defined a + + -- Special case where we defined a return self.transportPickup - + elseif self.engageTarget then local coord=self.engageTarget:GetCoordinate() return coord - + elseif self.type==AUFTRAG.Type.ALERT5 then - + -- For example, COMMANDER will not assign a coordiante. This will be done later, when the mission is assigned to an airwing. return nil - - else + + else self:T(self.lid.."ERROR: Cannot get target coordinate!") end @@ -4523,12 +4557,12 @@ end -- @param #AUFTRAG self -- @return #string Name of the target or "N/A". function AUFTRAG:GetTargetName() - + if self.engageTarget then local name=self.engageTarget:GetName() return name end - + return "N/A" end @@ -4540,13 +4574,13 @@ end function AUFTRAG:GetTargetDistance(FromCoord) local TargetCoord=self:GetTargetCoordinate() - + if TargetCoord and FromCoord then return TargetCoord:Get2DDistance(FromCoord) else self:T(self.lid.."ERROR: TargetCoord or FromCoord does not exist in AUFTRAG:GetTargetDistance() function! Returning 0") end - + return 0 end @@ -4579,13 +4613,13 @@ function AUFTRAG:DelAsset(Asset) for i,_asset in pairs(self.assets or {}) do local asset=_asset --Functional.Warehouse#WAREHOUSE.Assetitem - + if asset.uid==Asset.uid then self:T(self.lid..string.format("Removing asset \"%s\" from mission", tostring(Asset.spawngroupname))) table.remove(self.assets, i) return self end - + end return self @@ -4599,11 +4633,11 @@ function AUFTRAG:GetAssetByName(Name) for i,_asset in pairs(self.assets or {}) do local asset=_asset --Functional.Warehouse#WAREHOUSE.Assetitem - + if asset.spawngroupname==Name then return asset end - + end return nil @@ -4644,7 +4678,7 @@ end -- @return #AUFTRAG self function AUFTRAG:SetMissionWaypointCoord(Coordinate) - -- Obviously a zone was passed. We get the coordinate. + -- Obviously a zone was passed. We get the coordinate. if Coordinate:IsInstanceOf("ZONE_BASE") then Coordinate=Coordinate:GetCoordinate() end @@ -4665,17 +4699,17 @@ end --- Set the mission egress coordinate. This is the coordinate where the assigned group will go once the mission is finished. -- @param #AUFTRAG self -- @param Core.Point#COORDINATE Coordinate Egrees coordinate. --- @param #number Altitude (Optional) Altitude in feet. Default is y component of coordinate. +-- @param #number Altitude (Optional) Altitude in feet. Default is y component of coordinate. -- @return #AUFTRAG self function AUFTRAG:SetMissionEgressCoord(Coordinate, Altitude) - -- Obviously a zone was passed. We get the coordinate. + -- Obviously a zone was passed. We get the coordinate. if Coordinate:IsInstanceOf("ZONE_BASE") then Coordinate=Coordinate:GetCoordinate() end self.missionEgressCoord=Coordinate - + if Altitude then self.missionEgressCoord.y=UTILS.FeetToMeters(Altitude) end @@ -4706,14 +4740,14 @@ function AUFTRAG:GetMissionWaypointCoord(group, randomradius, surfacetypes) end -- Create waypoint coordinate half way between us and the target. - local waypointcoord=group:GetCoordinate():GetIntermediateCoordinate(self:GetTargetCoordinate(), self.missionFraction) + local waypointcoord=group:GetCoordinate():GetIntermediateCoordinate(self:GetTargetCoordinate(), self.missionFraction) local alt=waypointcoord.y - + -- Add some randomization. if randomradius then waypointcoord=ZONE_RADIUS:New("Temp", waypointcoord:GetVec2(), randomradius):GetRandomCoordinate(nil, nil, surfacetypes):SetAltitude(alt, false) end - + -- Set altitude of mission waypoint. if self.missionAltitude then waypointcoord:SetAltitude(self.missionAltitude, true) @@ -4743,22 +4777,22 @@ function AUFTRAG:UpdateMarker() text=text..string.format("\nOpsGroups %d/%d", self:CountOpsGroups(), self:GetNumberOfRequiredAssets()) if not self.marker then - + -- Get target coordinates. Can be nil! local targetcoord=self:GetTargetCoordinate() - + if self.markerCoaliton and self.markerCoaliton>=0 then self.marker=MARKER:New(targetcoord, text):ReadOnly():ToCoalition(self.markerCoaliton) else self.marker=MARKER:New(targetcoord, text):ReadOnly():ToAll() - end - + end + else - + if self.marker:GetText()~=text then self.marker:UpdateText(text) end - + end return self @@ -4774,109 +4808,109 @@ function AUFTRAG:GetDCSMissionTask(TaskControllable) -- Create DCS task based on current self. if self.type==AUFTRAG.Type.ANTISHIP then - + ---------------------- -- ANTISHIP Mission -- ---------------------- self:_GetDCSAttackTask(self.engageTarget, DCStasks) - + elseif self.type==AUFTRAG.Type.AWACS then - + ------------------- -- AWACS Mission -- - ------------------- + ------------------- local DCStask=CONTROLLABLE.EnRouteTaskAWACS(nil) - + table.insert(self.enrouteTasks, DCStask) - + elseif self.type==AUFTRAG.Type.BAI then - + ----------------- -- BAI Mission -- - ----------------- + ----------------- self:_GetDCSAttackTask(self.engageTarget, DCStasks) elseif self.type==AUFTRAG.Type.BOMBING then - + --------------------- -- BOMBING Mission -- --------------------- - + local DCStask=CONTROLLABLE.TaskBombing(nil, self:GetTargetVec2(), self.engageAsGroup, self.engageWeaponExpend, self.engageQuantity, self.engageDirection, self.engageAltitude, self.engageWeaponType, Divebomb) - + table.insert(DCStasks, DCStask) - + elseif self.type==AUFTRAG.Type.BOMBRUNWAY then - + ------------------------ -- BOMBRUNWAY Mission -- ------------------------ - + local DCStask=CONTROLLABLE.TaskBombingRunway(nil, self.engageTarget:GetObject(), self.engageWeaponType, self.engageWeaponExpend, self.engageQuantity, self.engageDirection, self.engageAsGroup) - - table.insert(DCStasks, DCStask) + + table.insert(DCStasks, DCStask) elseif self.type==AUFTRAG.Type.BOMBCARPET then - + ------------------------ -- BOMBCARPET Mission -- ------------------------ - + local DCStask=CONTROLLABLE.TaskCarpetBombing(nil, self:GetTargetVec2(), self.engageAsGroup, self.engageWeaponExpend, self.engageQuantity, self.engageDirection, self.engageAltitude, self.engageWeaponType, self.engageCarpetLength) - - table.insert(DCStasks, DCStask) + + table.insert(DCStasks, DCStask) elseif self.type==AUFTRAG.Type.CAP then - + ----------------- -- CAP Mission -- - ----------------- + ----------------- local DCStask=CONTROLLABLE.EnRouteTaskEngageTargetsInZone(nil, self.engageZone:GetVec2(), self.engageZone:GetRadius(), self.engageTargetTypes, Priority) - + table.insert(self.enrouteTasks, DCStask) - + elseif self.type==AUFTRAG.Type.CAS then - + ----------------- -- CAS Mission -- ----------------- local DCStask=CONTROLLABLE.EnRouteTaskEngageTargetsInZone(nil, self.engageZone:GetVec2(), self.engageZone:GetRadius(), self.engageTargetTypes, Priority) - + table.insert(self.enrouteTasks, DCStask) elseif self.type==AUFTRAG.Type.ESCORT then - + -------------------- -- ESCORT Mission -- -------------------- local DCStask=CONTROLLABLE.TaskEscort(nil, self.engageTarget:GetObject(), self.escortVec3, LastWaypointIndex, self.engageMaxDistance, self.engageTargetTypes) - + table.insert(DCStasks, DCStask) elseif self.type==AUFTRAG.Type.FACA then - + ----------------- -- FAC Mission -- - ----------------- + ----------------- local DCStask=CONTROLLABLE.TaskFAC_AttackGroup(nil, self.engageTarget:GetObject(), self.engageWeaponType, self.facDesignation, self.facDatalink, self.facFreq, self.facModu, CallsignName, CallsignNumber) - + table.insert(DCStasks, DCStask) - + elseif self.type==AUFTRAG.Type.FERRY then - + ------------------- -- FERRY Mission -- ------------------- - + -- TODO: Ferry mission type. How? - + elseif self.type==AUFTRAG.Type.INTERCEPT then ----------------------- @@ -4886,88 +4920,88 @@ function AUFTRAG:GetDCSMissionTask(TaskControllable) self:_GetDCSAttackTask(self.engageTarget, DCStasks) elseif self.type==AUFTRAG.Type.ORBIT then - + ------------------- -- ORBIT Mission -- ------------------- - + -- Done below as also other mission types use the orbit task. elseif self.type==AUFTRAG.Type.GCICAP then - + -------------------- -- GCICAP Mission -- -------------------- - + -- Done below as also other mission types use the orbit task. - + elseif self.type==AUFTRAG.Type.RECON then - + ------------------- -- RECON Mission -- - ------------------- - + ------------------- + local DCStask={} - + DCStask.id="ReconMission" - + -- We create a "fake" DCS task and pass the parameters to the OPSGROUP. local param={} param.target=self.engageTarget param.altitude=self.missionAltitude - param.speed=self.missionSpeed - param.lastindex=nil - + param.speed=self.missionSpeed + param.lastindex=nil + DCStask.params=param - - table.insert(DCStasks, DCStask) + + table.insert(DCStasks, DCStask) elseif self.type==AUFTRAG.Type.SEAD then - + ------------------ -- SEAD Mission -- - ------------------ + ------------------ --[[ local DCStask=CONTROLLABLE.EnRouteTaskEngageTargets(nil, nil ,{"Air Defence"} , 0) table.insert(self.enrouteTasks, DCStask) DCStask.key="SEAD" ]] - + self:_GetDCSAttackTask(self.engageTarget, DCStasks) - + elseif self.type==AUFTRAG.Type.STRIKE then - + -------------------- -- STRIKE Mission -- - -------------------- - + -------------------- + local DCStask=CONTROLLABLE.TaskAttackMapObject(nil, self:GetTargetVec2(), self.engageAsGroup, self.engageWeaponExpend, self.engageQuantity, self.engageDirection, self.engageAltitude, self.engageWeaponType) - + table.insert(DCStasks, DCStask) - + elseif self.type==AUFTRAG.Type.TANKER then - + -------------------- -- TANKER Mission -- - -------------------- + -------------------- local DCStask=CONTROLLABLE.EnRouteTaskTanker(nil) - - table.insert(self.enrouteTasks, DCStask) - + + table.insert(self.enrouteTasks, DCStask) + elseif self.type==AUFTRAG.Type.TROOPTRANSPORT then ---------------------------- -- TROOPTRANSPORT Mission -- ---------------------------- - + -- Task to embark the troops at the pick up point. local TaskEmbark=CONTROLLABLE.TaskEmbarking(TaskControllable, self.transportPickup, self.transportGroupSet, self.transportWaitForCargo) - + -- Task to disembark the troops at the drop off point. - local TaskDisEmbark=CONTROLLABLE.TaskDisembarking(TaskControllable, self.transportDropoff, self.transportGroupSet) - + local TaskDisEmbark=CONTROLLABLE.TaskDisembarking(TaskControllable, self.transportDropoff, self.transportGroupSet) + table.insert(DCStasks, TaskEmbark) table.insert(DCStasks, TaskDisEmbark) @@ -4978,26 +5012,26 @@ function AUFTRAG:GetDCSMissionTask(TaskControllable) -------------------------- local DCStask={} - + DCStask.id="OpsTransport" - + -- We create a "fake" DCS task and pass the parameters to the FLIGHTGROUP. - local param={} + local param={} DCStask.params=param - + table.insert(DCStasks, DCStask) - + elseif self.type==AUFTRAG.Type.RESCUEHELO then ------------------------- -- RESCUE HELO Mission -- ------------------------- - + local DCStask={} - + DCStask.id="Formation" - + -- We create a "fake" DCS task and pass the parameters to the FLIGHTGROUP. local param={} param.unitname=self:GetTargetName() @@ -5005,9 +5039,9 @@ function AUFTRAG:GetDCSMissionTask(TaskControllable) param.offsetZ=240 param.altitude=70 param.dtFollow=1.0 - + DCStask.params=param - + table.insert(DCStasks, DCStask) elseif self.type==AUFTRAG.Type.ARTY then @@ -5015,11 +5049,11 @@ function AUFTRAG:GetDCSMissionTask(TaskControllable) ------------------ -- ARTY Mission -- ------------------ - + local DCStask=CONTROLLABLE.TaskFireAtPoint(nil, self:GetTargetVec2(), self.artyRadius, self.artyShots, self.engageWeaponType, self.artyAltitude) - + table.insert(DCStasks, DCStask) - + elseif self.type==AUFTRAG.Type.BARRAGE then --------------------- @@ -5027,9 +5061,9 @@ function AUFTRAG:GetDCSMissionTask(TaskControllable) --------------------- local DCStask={} - + DCStask.id=AUFTRAG.SpecialTask.BARRAGE - + -- We create a "fake" DCS task and pass the parameters to the FLIGHTGROUP. local param={} param.zone=self:GetObjective() @@ -5039,9 +5073,9 @@ function AUFTRAG:GetDCSMissionTask(TaskControllable) param.angle=self.artyAngle param.shots=self.artyShots param.weaponTypoe=self.engageWeaponType - + DCStask.params=param - + table.insert(DCStasks, DCStask) elseif self.type==AUFTRAG.Type.PATROLZONE then @@ -5049,19 +5083,19 @@ function AUFTRAG:GetDCSMissionTask(TaskControllable) ------------------------- -- PATROL ZONE Mission -- ------------------------- - + local DCStask={} - + DCStask.id="PatrolZone" - + -- We create a "fake" DCS task and pass the parameters to the FLIGHTGROUP. local param={} param.zone=self:GetObjective() param.altitude=self.missionAltitude param.speed=self.missionSpeed - + DCStask.params=param - + table.insert(DCStasks, DCStask) elseif self.type==AUFTRAG.Type.CASENHANCED then @@ -5069,31 +5103,31 @@ function AUFTRAG:GetDCSMissionTask(TaskControllable) ------------------------- -- CAS ENHANCED Mission -- ------------------------- - + local DCStask={} - + DCStask.id="PatrolZone" - + -- We create a "fake" DCS task and pass the parameters to the FLIGHTGROUP. local param={} param.zone=self:GetObjective() param.altitude=self.missionAltitude param.speed=self.missionSpeed - + DCStask.params=param - + table.insert(DCStasks, DCStask) - + elseif self.type==AUFTRAG.Type.ARMORATTACK then ------------------------- -- ARMOR ATTACK Mission -- ------------------------- - + local DCStask={} - + DCStask.id=AUFTRAG.SpecialTask.ARMORATTACK - + -- We create a "fake" DCS task and pass the parameters to the ARMYGROUP. local param={} param.zone=self:GetObjective() @@ -5101,27 +5135,47 @@ function AUFTRAG:GetDCSMissionTask(TaskControllable) param.tzone=ZONE_RADIUS:New("ARMORATTACK-Zone-"..self.auftragsnummer,param.tVec2,1000) param.action="Wedge" param.speed=self.missionSpeed - + DCStask.params=param - - 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 ------------------------- -- AMMO SUPPLY Mission -- ------------------------- - + local DCStask={} - + DCStask.id=AUFTRAG.SpecialTask.AMMOSUPPLY - + -- 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.FUELSUPPLY then @@ -5129,113 +5183,113 @@ function AUFTRAG:GetDCSMissionTask(TaskControllable) ------------------------- -- FUEL SUPPLY Mission -- ------------------------- - + local DCStask={} - + DCStask.id=AUFTRAG.SpecialTask.FUELSUPPLY - + -- 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.ALERT5 then --------------------- -- ALERT 5 Mission -- --------------------- - + local DCStask={} - + DCStask.id=AUFTRAG.SpecialTask.ALERT5 - + -- We create a "fake" DCS task and pass the parameters to the OPSGROUP. local param={} - + DCStask.params=param - - table.insert(DCStasks, DCStask) - + + table.insert(DCStasks, DCStask) + elseif self.type==AUFTRAG.Type.HOVER then --------------------- -- HOVER Mission -- --------------------- - + local DCStask={} DCStask.id=AUFTRAG.SpecialTask.HOVER - + local param={} - + param.hoverAltitude=self.hoverAltitude param.hoverTime = self.hoverTime param.missionSpeed = self.missionSpeed param.missionAltitude = self.missionAltitude - + 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) - + --]] + + table.insert(DCStasks, DCStask) + elseif self.type==AUFTRAG.Type.ONGUARD or self.type==AUFTRAG.Type.ARMOREDGUARD then ---------------------- -- ON GUARD Mission -- ---------------------- - + local DCStask={} - + DCStask.id= self.type==AUFTRAG.Type.ONGUARD and AUFTRAG.SpecialTask.ONGUARD or AUFTRAG.SpecialTask.ARMOREDGUARD - + -- We create a "fake" DCS task and pass the parameters to the OPSGROUP. local param={} param.coordinate=self:GetObjective() - + DCStask.params=param - - table.insert(DCStasks, DCStask) - + + table.insert(DCStasks, DCStask) + else self:T(self.lid..string.format("ERROR: Unknown mission task!")) return nil end - - + + -- Set ORBIT task. Also applies to other missions: AWACS, TANKER, CAP, CAS. - if self.type==AUFTRAG.Type.ORBIT or + if self.type==AUFTRAG.Type.ORBIT or self.type==AUFTRAG.Type.CAP or self.type==AUFTRAG.Type.CAS or self.type==AUFTRAG.Type.GCICAP or - self.type==AUFTRAG.Type.AWACS or + self.type==AUFTRAG.Type.AWACS or self.type==AUFTRAG.Type.TANKER then ------------------- -- ORBIT Mission -- ------------------- - + local Coordinate=self:GetTargetCoordinate() - + local DCStask=CONTROLLABLE.TaskOrbit(nil, Coordinate, self.orbitAltitude, self.orbitSpeed, self.orbitRaceTrack) - + table.insert(DCStasks, DCStask) - + end - + -- Debug info. self:T3({missiontask=DCStasks}) @@ -5256,26 +5310,26 @@ end function AUFTRAG:_GetDCSAttackTask(Target, DCStasks) DCStasks=DCStasks or {} - + for _,_target in pairs(Target.targets) do local target=_target --Ops.Target#TARGET.Object if target.Type==TARGET.ObjectType.GROUP then - + local DCStask=CONTROLLABLE.TaskAttackGroup(nil, target.Object, self.engageWeaponType, self.engageWeaponExpend, self.engageQuantity, self.engageDirection, self.engageAltitude, self.engageAsGroup) - + table.insert(DCStasks, DCStask) - + elseif target.Type==TARGET.ObjectType.UNIT or target.Type==TARGET.ObjectType.STATIC then - + local DCStask=CONTROLLABLE.TaskAttackUnit(nil, target.Object, self.engageAsGroup, self.WeaponExpend, self.engageQuantity, self.engageDirection, self.engageAltitude, self.engageWeaponType) - + table.insert(DCStasks, DCStask) - + end - + end - + return DCStasks end @@ -5286,7 +5340,7 @@ end function AUFTRAG:GetMissionTaskforMissionType(MissionType) local mtask=ENUMS.MissionTask.NOTHING - + if MissionType==AUFTRAG.Type.ANTISHIP then mtask=ENUMS.MissionTask.ANTISHIPSTRIKE elseif MissionType==AUFTRAG.Type.AWACS then @@ -5353,7 +5407,7 @@ function AUFTRAG.CheckMissionType(MissionType, PossibleTypes) for _,canmission in pairs(PossibleTypes) do if canmission==MissionType then return true - end + end end return false @@ -5381,7 +5435,7 @@ function AUFTRAG.CheckMissionCapability(MissionTypes, Capabilities, All) else if capability.MissionType==MissionType then return true - end + end end end end @@ -5401,7 +5455,7 @@ end function AUFTRAG.CheckMissionCapabilityAny(MissionTypes, Capabilities) local res=AUFTRAG.CheckMissionCapability(MissionTypes, Capabilities, false) - + return res end @@ -5413,7 +5467,7 @@ end function AUFTRAG.CheckMissionCapabilityAll(MissionTypes, Capabilities) local res=AUFTRAG.CheckMissionCapability(MissionTypes, Capabilities, true) - + return res end diff --git a/Moose Development/Moose/Ops/Chief.lua b/Moose Development/Moose/Ops/Chief.lua index a90297ff0..d9eea0efb 100644 --- a/Moose Development/Moose/Ops/Chief.lua +++ b/Moose Development/Moose/Ops/Chief.lua @@ -100,15 +100,107 @@ -- local text=string.format("Strategy changd to %s", Strategy) -- MESSAGE:New(text, 120):ToAll() -- end +-- +-- # Resources +-- +-- A chief needs resources such as air, ground and naval assets. These can be added in form of AIRWINGs, BRIGADEs and FLEETs. +-- +-- Whenever the chief detects a target or receives a mission, he will select the best available assets and assign them to the mission. +-- The best assets are determined by their mission performance, payload performance (in case of air), distance to the target, skill level, etc. +-- +-- ## Adding Airwings +-- +-- Airwings can be added via the @{#CHIEF.AddAirwing}() function. +-- +-- ## Adding Brigades +-- +-- Brigades can be added via the @{#CHIEF.AddBrigade}() function. +-- +-- ## Adding Fleets +-- +-- Fleets are not implemented yet. +-- -- -- # Strategic (Capture) Zones -- --- Strategically important zones, which should be captured can be added via the @{#CHIEF.AddStrategicZone}() function. +-- Strategically important zones, which should be captured can be added via the @{#CHIEF.AddStrategicZone}(*OpsZone, Prio, Importance*) function. +-- The first parameter *OpsZone* is an @{Ops.OpsZone#OPSZONE} specifying the zone. This has to be a **circular zone** due to DCS API restrictions. +-- The second parameter *Prio* is the priority. The zone queue is sorted wrt to lower prio values. By default this is set to 50. +-- The third parameter *Importance* is the importance of the zone. By default this is `nil`. If you specify one zone with importance 2 and a second zone with +-- importance 3, then the zone of importance 2 is attacked first and only if that zone has been captured, zones that have importances with higher values are attacked. -- --- If the zone is currently owned by another coalition and enemy ground troops are present in the zone, a CAS and an ARTY mission are lauchned, provided assets are available. +-- For example: -- --- Once the zone is cleaned of enemy forces, ground (infantry) troops are send there. These require a transportation via helicopters. --- So in order to deploy our own troops, infantry assets with `AUFTRAG.Type.ONGUARD` and helicopters with `AUFTRAG.Type.OPSTRANSPORT` need to be available. +-- local myStratZone=myChief:AddStrategicZone(myOpsZone, nil , 2) +-- +-- Will at a strategic zone with importance 2. +-- +-- If the zone is currently owned by another coalition and enemy ground troops are present in the zone, a CAS and an ARTY mission are lauchned: +-- +-- * A mission of type `AUFTRAG.Type.CASENHANCED` is started if assets are available that can carry out this mission type. +-- * A mission of type `AUFTRAG.Type.ARTY` is started provided assets are available. +-- +-- The CAS flight(s) will patrol the zone randomly and take out enemy ground units they detect. It can always be possible that the enemies cannot be detected however. +-- The assets will shell the zone. However, it is unlikely that they hit anything as they do not have any information about the location of the enemies. +-- +-- Once the zone is cleaned of enemy forces, ground troops are send there. By default, two missions are launched: +-- +-- * First mission is of type `AUFTRAG.Type.ONGUARD` and will send infantry groups. These are transported by helicopters. Therefore, helo assets with `AUFTRAG.Type.OPSTRANSPORT` need to be available. +-- * The second mission is also of type `AUFTRAG.Type.ONGUARD` but will send tanks if these are available. +-- +-- ## Customized Reaction +-- +-- The default mission types and number of assets can be customized for the two scenarious (zone empty or zone occupied by the enemy). +-- +-- In order to do this, you need to create resource lists (one for each scenario) via the @{#CHIEF.CreateResource}() function. +-- These list can than be used to replace the default resources employed with +-- +-- * @{CHIEF.SetStrategicZoneResourceOccupied}(*StrateticZone, ResourceOccupied*) for the case that the zone is occupied by the enemy and +-- * @{CHIEF.SetStrategicZoneResourceEmpty}(*StrateticZone, ResourceEmpty*) for the case that the zone is empty. +-- +-- The first parameter *StrateticZone* is the strategic zone object that is returned by the @{#CHIEF.AddStrategicZone}() function. +-- The second parameter is the resource list created with the @{#CHIEF.CreateResource}() function. +-- +-- For example: +-- +-- -- Create a resource list of mission types and required assets for the case that the zone is occupied. +-- -- Here, we create an enhanced CAS mission and employ at least on and at most two asset groups. +-- local ResourceOccupied=myChief:CreateResource(AUFTRAG.Type.CASENHANCED, 1, 2) +-- -- We also add ARTY missions with at least one and at most two assets. We additionally require these to be MLRS groups (and not howitzers). +-- myChief:AddToResource(ResourceOccupied, AUFTRAG.Type.ARTY, 1, 2, nil, "MLRS") +-- -- Add at least one RECON mission that uses UAV type assets. +-- myChief:AddToResource(ResourceOccupied, AUFTRAG.Type.RECON, 1, nil, GROUP.Attribute.AIR_UAV) +-- -- Add at least one but at most two BOMBCARPET missions. +-- myChief:AddToResource(ResourceOccupied, AUFTRAG.Type.BOMBCARPET, 1, 2) +-- +-- -- Replace the default list with the customized one. +-- myChief:SetStrategicZoneResourceOccupied(myStratZone, ResourceOccupied) +-- +-- +-- -- Create a resource list of mission types and required assets for the case that the zone is empty. +-- -- Here, we create an ONGUARD mission and employ at least on and at most five infantry assets. +-- local ResourceEmpty=myChief:CreateResource(AUFTRAG.Type.ONGUARD, 1, 5, GROUP.Attribute.GROUND_INFANTRY) +-- -- Additionally, we send up to three tank groups. +-- myChief:AddToResource(ResourceEmpty, AUFTRAG.Type.ONGUARD, 1, 3, GROUP.Attribute.GROUND_TANK) +-- -- Finally, we send two groups that patrol the zone. +-- myChief:AddToResource(ResourceEmpty, AUFTRAG.Type.PATROLZONE, 2) +-- +-- -- Set this to be the resources employed when the zone is empty. +-- myChief:SetStrategicZoneResourceEmpty(myStratZone, ResourceEmpty) +-- +-- As the location of the enemies is not known, only mission types that don't require and explicit target group are possible. These are +-- +-- * `AUFTRAG.Type.CASENHANCED` +-- * `AUFTRAG.Type.ARTY` +-- * `AUFTRAG.Type.PATROLZONE` +-- * `AUFTRAG.Type.ONGUARD` +-- * `AUFTRAG.Type.RECON` +-- * `AUFTRAG.Type.AMMOSUPPLY` +-- * `AUFTRAG.Type.BOMBING` +-- * `AUFTRAG.Type.BOMBCARPET` +-- * `AUFTRAG.Type.BARRAGE` +-- +-- ## Events -- -- Whenever a strategic zone is captured by us the FSM event @{#CHIEF.ZoneCaptured} is triggered and customized further actions can be executed -- with the @{#CHIEF.OnAfterZoneCaptured}() function. @@ -176,16 +268,23 @@ CHIEF.Strategy = { -- @type CHIEF.StrategicZone -- @field Ops.OpsZone#OPSZONE opszone OPS zone. -- @field #number prio Priority. --- @field #number importance Importance --- @field Ops.Auftrag#AUFTRAG missionPatrol Patrol mission. --- @field Ops.Auftrag#AUFTRAG missionCAS CAS mission. --- @field Ops.Auftrag#AUFTRAG missionPatrol Patrol mission. --- @field Ops.Auftrag#AUFTRAG missionARTY Artillery mission. +-- @field #number importance Importance. +-- @field #table resourceEmpty Resource list. +-- @field #table resourceOccup Resource list. +-- @field #table missions Mission. +--- Resource. +-- @type CHIEF.Resource +-- @field #string MissionType Mission type, e.g. `AUFTRAG.Type.BAI`. +-- @field #number Nmin Min number of assets. +-- @field #number Nmax Max number of assets. +-- @field #table Attributes Generalized attribute, e.g. `{GROUP.Attribute.GROUND_INFANTRY}`. +-- @field #table Properties Properties ([DCS attributes](https://wiki.hoggitworld.com/view/DCS_enum_attributes)), e.g. `"Attack helicopters"` or `"Mobile AAA"`. +-- @field Ops.Auftrag#AUFTRAG mission Attached mission. --- CHIEF class version. -- @field #string version -CHIEF.version="0.2.0" +CHIEF.version="0.3.0" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list @@ -600,6 +699,88 @@ function CHIEF:SetDefcon(Defcon) return self end +--- Create a new resource list of required assets. +-- @param #CHIEF self +-- @param #string MissionType The mission type. +-- @param #number Nmin Min number of required assets. Default 1. +-- @param #number Nmax Max number of requried assets. Default 1. +-- @param #table Attributes Generalized attribute(s). Default `nil`. +-- @param #table Properties DCS attribute(s). Default `nil`. +-- @return #table The resource object. +function CHIEF:CreateResource(MissionType, Nmin, Nmax, Attributes, Properties) + + local resource={} + + self:AddToResource(resource, MissionType, Nmin, Nmax, Attributes, Properties) + + return resource +end + +--- Add mission type and number of required assets to resource. +-- @param #CHIEF self +-- @param #table Resource Resource table. +-- @param #string MissionType Mission Type. +-- @param #number Nmin Min number of required assets. +-- @param #number Nmax Max number of requried assets. +-- @param #table Attributes Generalized attribute(s). +-- @param #table Properties DCS attribute(s). Default `nil`. +-- @return #CHIEF self +function CHIEF:AddToResource(Resource, MissionType, Nmin, Nmax, Attributes, Properties) + + -- Ensure table. + if Attributes and type(Attributes)~="table" then + Attributes={Attributes} + end + + -- Ensure table. + if Properties and type(Properties)~="table" then + Properties={Properties} + end + + -- Create new resource table. + local resource={} --#CHIEF.Resource + resource.MissionType=MissionType + resource.Nmin=Nmin or 1 + resource.Nmax=Nmax or 1 + resource.Attributes=Attributes or {} + resource.Properties=Properties or {} + + -- Add to table. + table.insert(Resource, resource) + + -- Debug output. + if self.verbose>10 then + local text="Resource:" + for _,_r in pairs(Resource) do + local r=_r --#CHIEF.Resource + text=text..string.format("\nmission=%s, Nmin=%d, Nmax=%d, attribute=%s, properties=%s", r.MissionType, r.Nmin, r.Nmax, tostring(r.Attributes[1]), tostring(r.Properties[1])) + end + self:I(self.lid..text) + end + + return self +end + +--- Delete mission type from resource list. All running missions are cancelled. +-- @param #CHIEF self +-- @param #table Resource Resource table. +-- @param #string MissionType Mission Type. +-- @return #CHIEF self +function CHIEF:DeleteFromResource(Resource, MissionType) + + for i=#Resource,1,-1 do + local resource=Resource[i] --#CHIEF.Resource + if resource.MissionType==MissionType then + if resource.mission and resource.mission:IsNotOver() then + resource.mission:Cancel() + end + table.remove(Resource, i) + end + end + + return self +end + --- Get defence condition. -- @param #CHIEF self -- @param #string Current Defence condition. See @{#CHIEF.DEFCON}, e.g. `CHIEF.DEFCON.RED`. @@ -813,11 +994,22 @@ function CHIEF:RemoveTarget(Target) end --- Add strategically important zone. +-- By default two resource lists are created. One for the case that the zone is empty and the other for the case that the zone is occupied +-- Empty: +-- +-- * `AUFTRAG.Type.ARTY` with Nmin=1, Nmax=2 +-- * `AUFTRAG.Type.CASENHANCED` with Nmin=1, Nmax=2 +-- +-- Occupied: +-- +-- * `AUFTRAG.Type.ONGUARD` with Nmin=1 and Nmax=3 assets, Attribute=`GROUP.Attribute.GROUND_INFANTRY`. +-- * `AUFTRAG.Type.ONGURAD` with Nmin=1 and Nmax=1 assets, Attribute=`GROUP.Attribute.GROUND_TANK`. +-- -- @param #CHIEF self -- @param Ops.OpsZone#OPSZONE OpsZone OPS zone object. -- @param #number Priority Priority. -- @param #number Importance Importance. --- @return #CHIEF self +-- @return #CHIEF.StrategicZone The strategic zone. function CHIEF:AddStrategicZone(OpsZone, Priority, Importance) local stratzone={} --#CHIEF.StrategicZone @@ -825,11 +1017,21 @@ function CHIEF:AddStrategicZone(OpsZone, Priority, Importance) stratzone.opszone=OpsZone stratzone.prio=Priority or 50 stratzone.importance=Importance + + stratzone.missions={} -- Start ops zone. if OpsZone:IsStopped() then OpsZone:Start() end + + -- Add resources if zone is occupied. + stratzone.resourceOccup=self:CreateResource(AUFTRAG.Type.ARTY, 1, 2) + self:AddToResource(stratzone.resourceOccup, AUFTRAG.Type.CASENHANCED, 1, 2) + + -- Add resources if zone is empty + stratzone.resourceEmpty=self:CreateResource(AUFTRAG.Type.ONGUARD, 1, 3, GROUP.Attribute.GROUND_INFANTRY) + self:AddToResource(stratzone.resourceEmpty, AUFTRAG.Type.ONGUARD, 1, 1, GROUP.Attribute.GROUND_TANK) -- Add to table. table.insert(self.zonequeue, stratzone) @@ -837,9 +1039,56 @@ function CHIEF:AddStrategicZone(OpsZone, Priority, Importance) -- Add chief so we get informed when something happens. OpsZone:_AddChief(self) + return stratzone +end + +--- Set the resource list of missions and assets employed when the zone is empty. +-- @param #CHIEF self +-- @param #CHIEF.StrategicZone StrategicZone The strategic zone. +-- @param #CHIEF.Resource Resource Resource list of missions and assets. +-- @param #boolean NoCopy If `true`, do **not** create a deep copy of the resource. +-- @return #CHIEF self +function CHIEF:SetStrategicZoneResourceEmpty(StrategicZone, Resource, NoCopy) + if NoCopy then + StrategicZone.resourceEmpty=Resource + else + StrategicZone.resourceEmpty=UTILS.DeepCopy(Resource) + end return self end +--- Set the resource list of missions and assets employed when the zone is occupied by the enemy. +-- @param #CHIEF self +-- @param #CHIEF.StrategicZone StrategicZone The strategic zone. +-- @param #CHIEF.Resource Resource Resource list of missions and assets. +-- @param #boolean NoCopy If `true`, do **not** create a deep copy of the resource. +-- @return #CHIEF self +function CHIEF:SetStrategicZoneResourceOccupied(StrategicZone, Resource, NoCopy) + if NoCopy then + StrategicZone.resourceOccup=Resource + else + StrategicZone.resourceOccup=UTILS.DeepCopy(Resource) + end + return self +end + +--- Get the resource list of missions and assets employed when the zone is empty. +-- @param #CHIEF self +-- @param #CHIEF.StrategicZone StrategicZone The strategic zone. +-- @return #CHIEF.Resource Resource list of missions and assets. +function CHIEF:GetStrategicZoneResourceEmpty(StrategicZone) + return StrategicZone.resourceEmpty +end + +--- Get the resource list of missions and assets employed when the zone is occupied by the enemy. +-- @param #CHIEF self +-- @param #CHIEF.StrategicZone StrategicZone The strategic zone. +-- @return #CHIEF.Resource Resource list of missions and assets. +function CHIEF:GetStrategicZoneResourceOccupied(StrategicZone) + return StrategicZone.resourceOccup +end + + --- Remove strategically important zone. All runing missions are cancelled. -- @param #CHIEF self -- @param Ops.OpsZone#OPSZONE OpsZone OPS zone object. @@ -860,15 +1109,23 @@ function CHIEF:RemoveStrategicZone(OpsZone, Delay) -- Debug info. self:T(self.lid..string.format("Removing OPS zone \"%s\" from queue! All running missions will be cancelled", OpsZone.zoneName)) - - -- Cancel all running missions. - for _,_entry in pairs(OpsZone.Missions or {}) do - local entry = _entry -- Ops.OpsZone#OPSZONE.MISSION - if entry.Coalition==self.coalition and entry.Mission and entry.Mission:IsNotOver() then - entry.Mission:Cancel() + + -- Cancel running missions. + for _,_resource in pairs(stratzone.resourceEmpty) do + local resource=_resource --#CHIEF.Resource + if resource.mission and resource.mission:IsNotOver() then + resource.mission:Cancel() end end + -- Cancel running missions. + for _,_resource in pairs(stratzone.resourceOccup) do + local resource=_resource --#CHIEF.Resource + if resource.mission and resource.mission:IsNotOver() then + resource.mission:Cancel() + end + end + -- Remove from table. table.remove(self.zonequeue, i) @@ -1566,13 +1823,14 @@ function CHIEF:_TacticalOverview() local NmissionsRunni=self.commander:CountMissions(AUFTRAG.Type, true) local Ntargets=#self.targetqueue local Nzones=#self.zonequeue + local Nagents=self.detectionset:CountAlive() -- Info message local text=string.format("Tactical Overview\n") text=text..string.format("=================\n") -- Strategy and defcon info. - text=text..string.format("Strategy: %s - Defcon: %s\n", self.strategy, self.Defcon) + text=text..string.format("Strategy: %s - Defcon: %s - Agents=%s\n", self.strategy, self.Defcon, Nagents) -- Contact info. text=text..string.format("Contacts: %d [Border=%d, Conflict=%d, Attack=%d]\n", Ncontacts, self.Nborder, self.Nconflict, self.Nattack) @@ -1866,11 +2124,6 @@ end -- @param #CHIEF self function CHIEF:CheckOpsZoneQueue() - -- Passive strategy ==> Do not act. - if self:IsPassive() then - return - end - -- Number of zones. local Nzones=#self.zonequeue @@ -1878,10 +2131,45 @@ function CHIEF:CheckOpsZoneQueue() if Nzones==0 then return nil end + + -- Loop over strategic zone and remove stopped zones. + for i=Nzones, 1, -1 do + local stratzone=self.zonequeue[i] --#CHIEF.StrategicZone + if stratzone.opszone:IsStopped() then + self:RemoveStrategicZone(stratzone.opszone) + end + end + + -- Loop over strategic zones and cancel missions for occupied zones if zone is not occupied any more. + for _,_startzone in pairs(self.zonequeue) do + local stratzone=_startzone --#CHIEF.StrategicZone + + -- Current owner of the zone. + local ownercoalition=stratzone.opszone:GetOwner() + + -- Check if we own the zone or it is empty. + if ownercoalition==self.coalition or stratzone.opszone:IsEmpty() then + + -- Loop over resources. + for _,_resource in pairs(stratzone.resourceOccup or {}) do + local resource=_resource --#CHIEF.Resource + + -- Cancel running missions. + if resource.mission then + resource.mission:Cancel() + end + + end + end + end + + -- Passive strategy ==> Do not act. + if self:IsPassive() then + return + end -- Check if total number of missions is reached. local NoLimit=self:_CheckMissionLimit("Total") - --env.info("FF chief zone total nolimit="..tostring(NoLimit)) if NoLimit==false then return nil end @@ -1911,19 +2199,15 @@ function CHIEF:CheckOpsZoneQueue() -- Current owner of the zone. local ownercoalition=stratzone.opszone:GetOwner() + + -- Name of the zone. + local zoneName=stratzone.opszone.zone:GetName() -- Check coalition and importance. if ownercoalition~=self.coalition and (stratzone.importance==nil or stratzone.importance<=vip) and (not stratzone.opszone:IsStopped()) then - - -- Has a patrol mission? - local hasMissionPatrol=stratzone.opszone:_FindMissions(self.coalition,AUFTRAG.Type.ONGUARD) or stratzone.opszone:_FindMissions(self.coalition,AUFTRAG.Type.ARMOREDGUARD) - -- Has a CAS mission? - local hasMissionCAS=stratzone.opszone:_FindMissions(self.coalition,AUFTRAG.Type.CASENHANCED) - -- Has a ARTY mission? - local hasMissionARTY=stratzone.opszone:_FindMissions(self.coalition,AUFTRAG.Type.ARTY) -- Debug info. - self:T(self.lid..string.format("Zone %s [%s] is owned by coalition %d", stratzone.opszone.zone:GetName(), stratzone.opszone:GetState(), ownercoalition)) + self:T(self.lid..string.format("Zone %s [%s] is owned by coalition %d", zoneName, stratzone.opszone:GetState(), ownercoalition)) if stratzone.opszone:IsEmpty() then @@ -1932,19 +2216,29 @@ function CHIEF:CheckOpsZoneQueue() -- -- We send ground troops to capture the zone. --- - - if not hasMissionPatrol then - - -- Debug info. - self:T3(self.lid..string.format("Zone is empty ==> Recruit Patrol zone infantry assets")) - - -- Recruit ground assets that - local recruitedI=self:RecruitAssetsForZone(stratzone, AUFTRAG.Type.ONGUARD, 1, 3, {Group.Category.GROUND}, {GROUP.Attribute.GROUND_INFANTRY}) - local recruitedT=self:RecruitAssetsForZone(stratzone, AUFTRAG.Type.ARMOREDGUARD, 1, 1, {Group.Category.GROUND}, {GROUP.Attribute.GROUND_TANK}) + + for _,_resource in pairs(stratzone.resourceEmpty or {}) do + local resource=_resource --#CHIEF.Resource - -- Debug info. - self:T(self.lid..string.format("Zone is empty ==> Recruit Patrol zone infantry assets=%s", tostring(recruitedI))) - self:T(self.lid..string.format("Zone is empty ==> Recruit Patrol zone armored assets=%s", tostring(recruitedT))) + -- Mission type. + local missionType=resource.MissionType + + if (not resource.mission) or resource.mission:IsOver() then + + -- Debug info. + self:T2(self.lid..string.format("Zone \"%s\" is empty ==> Recruiting for mission type %s: Nmin=%d, Nmax=%d", zoneName, missionType, resource.Nmin, resource.Nmax)) + + -- Recruit assets. + local recruited=self:RecruitAssetsForZone(stratzone, resource) + + if recruited then + self:T(self.lid..string.format("Successfully recruited assets for empty zone \"%s\" [mission type=%s]", zoneName, missionType)) + else + self:T(self.lid..string.format("Could not recruited assets for empty zone \"%s\" [mission type=%s]", zoneName, missionType)) + end + + end + end else @@ -1954,79 +2248,35 @@ function CHIEF:CheckOpsZoneQueue() -- -- We first send a CAS flight to eliminate enemy activity. --- - - if not hasMissionCAS then - -- Debug message. - self:T3(self.lid..string.format("Zone is NOT empty ==> Recruit CAS assets")) + for _,_resource in pairs(stratzone.resourceOccup or {}) do + local resource=_resource --#CHIEF.Resource + + -- Mission type. + local missionType=resource.MissionType + + if (not resource.mission) or resource.mission:IsOver() then + -- Debug info. + self:T2(self.lid..string.format("Zone %s is NOT empty ==> Recruiting for mission type %s: Nmin=%d, Nmax=%d", zoneName, missionType, resource.Nmin, resource.Nmax)) + + -- Recruit assets. + local recruited=self:RecruitAssetsForZone(stratzone, resource) - -- Recruite CAS assets. - local recruited=self:RecruitAssetsForZone(stratzone, AUFTRAG.Type.CASENHANCED, 1, 1) - - -- Debug message. - self:T(self.lid..string.format("Zone is NOT empty ==> Recruit CAS assets=%s", tostring(recruited))) - - end + if recruited then + self:T(self.lid..string.format("Successfully recruited assets for occupied zone %s, mission type=%s", zoneName, missionType)) + else + self:T(self.lid..string.format("Could not recruited assets for occupied zone %s, mission type=%s", zoneName, missionType)) + end + + end - if not hasMissionARTY then - - -- Debug message. - self:T3(self.lid..string.format("Zone is NOT empty ==> Recruit ARTY assets")) - - - -- Recruite CAS assets. - local recruited=self:RecruitAssetsForZone(stratzone, AUFTRAG.Type.ARTY, 1, 1) - - -- Debug message. - self:T(self.lid..string.format("Zone is NOT empty ==> Recruit ARTY assets=%s", tostring(recruited))) - end - + end end end - - -- Loop over strategic zone. - for _,_startzone in pairs(self.zonequeue) do - local stratzone=_startzone --#CHIEF.StrategicZone - - -- Current owner of the zone. - local ownercoalition=stratzone.opszone:GetOwner() - - -- Has a patrol mission? - local hasMissionPATROL=stratzone.opszone:_FindMissions(self.coalition,AUFTRAG.Type.PATROLZONE) - - -- Has a CAS mission? - local hasMissionCAS, CASMissions = stratzone.opszone:_FindMissions(self.coalition,AUFTRAG.Type.CASENHANCED) - local hasMissionARTY, ARTYMissions = stratzone.opszone:_FindMissions(self.coalition,AUFTRAG.Type.ARTY) - - if ownercoalition==self.coalition and stratzone.opszone:IsEmpty() and hasMissionCAS then - -- Cancel CAS mission if zone is ours and no enemies are present. - -- TODO: Might want to check if we still have CAS capable assets in stock?! - --stratzone.missionCAS:Cancel() - for _,_auftrag in pairs(CASMissions) do - _auftrag:Cancel() - end - end - - if ownercoalition==self.coalition and hasMissionARTY then - -- Cancel ARTY mission if zone is ours and no enemies are present. - for _,_auftrag in pairs(ARTYMissions) do - _auftrag:Cancel() - end - end - - end - - -- Loop over strategic zone and remove stopped zones. - for i=#self.zonequeue, 1, -1 do - local stratzone=self.zonequeue[i] --#CHIEF.StrategicZone - if stratzone.opszone:IsStopped() then - self:RemoveStrategicZone(stratzone.opszone) - end - end end @@ -2360,13 +2610,9 @@ end --- Recruit assets for a given OPS zone. -- @param #CHIEF self -- @param #CHIEF.StrategicZone StratZone The strategic zone. --- @param #string MissionType Mission Type. --- @param #number NassetsMin Min number of required assets. --- @param #number NassetsMax Max number of required assets. --- @param #table Categories Group categories of the assets. --- @param #table Attributes Generalized group attributes. +-- @param #CHIEF.Resource Resource The required resources. -- @return #boolean If `true` enough assets could be recruited. -function CHIEF:RecruitAssetsForZone(StratZone, MissionType, NassetsMin, NassetsMax, Categories, Attributes) +function CHIEF:RecruitAssetsForZone(StratZone, Resource) -- Cohorts. local Cohorts={} @@ -2385,7 +2631,15 @@ function CHIEF:RecruitAssetsForZone(StratZone, MissionType, NassetsMin, NassetsM end end - end + end + + -- Shortcuts. + local MissionType=Resource.MissionType + local NassetsMin=Resource.Nmax + local NassetsMax=Resource.Nmax + local Categories=Resource.Categories + local Attributes=Resource.Attributes + local Properties=Resource.Properties -- Target position. local TargetVec2=StratZone.opszone.zone:GetVec2() @@ -2398,26 +2652,36 @@ function CHIEF:RecruitAssetsForZone(StratZone, MissionType, NassetsMin, NassetsM RangeMax=UTILS.NMToMeters(250) end - -- Set max range to 50 NM because we use armor + -- Set max range to 50 NM because we use armor. if MissionType==AUFTRAG.Type.ARMOREDGUARD then RangeMax=UTILS.NMToMeters(50) end -- Recruite infantry assets. - local recruited, assets, legions=LEGION.RecruitCohortAssets(Cohorts, MissionType, nil, NassetsMin, NassetsMax, TargetVec2, nil, RangeMax, nil, nil, nil, Categories, Attributes) + local recruited, assets, legions=LEGION.RecruitCohortAssets(Cohorts, MissionType, nil, NassetsMin, NassetsMax, TargetVec2, nil, RangeMax, nil, nil, nil, Categories, Attributes, Properties) if recruited then + -- Mission for zone. + local mission=nil --Ops.Auftrag#AUFTRAG + -- Debug messgage. - self:T2(self.lid..string.format("Recruited %d assets for %s mission STRATEGIC zone %s", #assets, MissionType, tostring(StratZone.opszone.zoneName))) + self:T2(self.lid..string.format("Recruited %d assets for %s mission STRATEGIC zone %s", #assets, MissionType, tostring(StratZone.opszone.zoneName))) + + local TargetZone = StratZone.opszone.zone + local TargetCoord = TargetZone:GetCoordinate() if MissionType==AUFTRAG.Type.PATROLZONE or MissionType==AUFTRAG.Type.ONGUARD then - + + --- + -- PATROLZONE or ONGUARD + --- + -- Debug messgage. self:T2(self.lid..string.format("Recruited %d assets for PATROL mission", #assets)) - local recruitedTrans=true - local transport=nil + -- First check if we need a transportation. + local recruitedTrans=true ; local transport=nil if Attributes and Attributes[1]==GROUP.Attribute.GROUND_INFANTRY then -- Categories. Currently only helicopters are allowed due to problems with ground transports (might get stuck, might not be a land connection. @@ -2425,55 +2689,66 @@ function CHIEF:RecruitAssetsForZone(StratZone, MissionType, NassetsMin, NassetsM local Categories=self.TransportCategories -- Recruit transport assets for infantry. - recruitedTrans, transport=LEGION.AssignAssetsForTransport(self.commander, self.commander.legions, assets, 1, 1, StratZone.opszone.zone, nil, Categories) + recruitedTrans, transport=LEGION.AssignAssetsForTransport(self.commander, self.commander.legions, assets, 1, 1, TargetZone, nil, Categories) end if recruitedTrans then - - -- Create Patrol zone mission. - local mission=nil --Ops.Auftrag#AUFTRAG if MissionType==AUFTRAG.Type.PATROLZONE then - mission=AUFTRAG:NewPATROLZONE(StratZone.opszone.zone) - mission:SetEngageDetected(25, {"Ground Units", "Light armed ships", "Helicopters"}) + mission=AUFTRAG:NewPATROLZONE(TargetZone) + elseif MissionType==AUFTRAG.Type.ONGUARD then - mission=AUFTRAG:NewONGUARD(StratZone.opszone.zone:GetRandomCoordinate(), nil, nil, {land.SurfaceType.LAND}) + mission=AUFTRAG:NewONGUARD(TargetZone:GetRandomCoordinate(nil, nil, {land.SurfaceType.LAND})) end - mission:SetEngageDetected() - - -- Add assets to mission. - for _,asset in pairs(assets) do - mission:AddAsset(asset) - end - + + -- Engage detected targets. + mission:SetEngageDetected(25, {"Ground Units", "Light armed ships", "Helicopters"}) + -- Attach OPS transport to mission. mission.opstransport=transport - - -- Assign mission to legions. - self:MissionAssign(mission, legions) - - -- Attach mission to ops zone. - StratZone.opszone:_AddMission(self.coalition, MissionType, mission) - + -- Set ops zone to transport. - transport.opszone=StratZone.opszone - transport.chief=self - transport.commander=self.commander - - return true + if transport then + transport.opszone=StratZone.opszone + transport.chief=self + transport.commander=self.commander + end + else + -- No transport ==> no mission! + self:T(self.lid..string.format("Could not allocate transport of OPSZONE infantry!")) LEGION.UnRecruitAssets(assets) return false end - elseif MissionType==AUFTRAG.Type.CASENHANCED then + + --- + -- CAS ENHANCED + --- -- Create Patrol zone mission. - local caszone = StratZone.opszone.zone - local coord = caszone:GetCoordinate() - local height = UTILS.MetersToFeet(coord:GetLandHeight())+2500 + local height = UTILS.MetersToFeet(TargetCoord:GetLandHeight())+2500 + + local Speed=200 + if assets[1] then + if assets[1].speedmax then + Speed = UTILS.KmphToKnots(assets[1].speedmax * 0.7) or 200 + end + end + + -- CAS mission. + mission=AUFTRAG:NewCASENHANCED(TargetZone, height, Speed) + + elseif MissionType==AUFTRAG.Type.CAS then + + --- + -- CAS + --- + + -- Create Patrol zone mission. + local height = UTILS.MetersToFeet(TargetCoord:GetLandHeight())+2500 local Speed = 200 if assets[1] then @@ -2481,82 +2756,80 @@ function CHIEF:RecruitAssetsForZone(StratZone, MissionType, NassetsMin, NassetsM Speed = UTILS.KmphToKnots(assets[1].speedmax * 0.7) or 200 end end - - --local mission=AUFTRAG:NewCAS(caszone,height,Speed,coord,math.random(0,359),Leg) - local mission=AUFTRAG:NewCASENHANCED(caszone, height, Speed) - -- Add assets to mission. - for _,asset in pairs(assets) do - mission:AddAsset(asset) - end - - -- Assign mission to legions. - self:MissionAssign(mission, legions) - - -- Attach mission to ops zone. - StratZone.opszone:_AddMission(self.coalition, MissionType, mission) - - return true - - elseif MissionType==AUFTRAG.Type.CAS then - - -- Create Patrol zone mission. - local caszone = StratZone.opszone.zone - local coord = caszone:GetCoordinate() - local height = UTILS.MetersToFeet(coord:GetLandHeight())+2500 - local Speed = 200 - --local mission=AUFTRAG:NewPATROLZONE(caszone) - if assets[1] then - if assets[1].speedmax then - Speed = UTILS.KmphToKnots(assets[1].speedmax * 0.7) or 200 - end - end - --local Speed = UTILS.KmphToKnots(assets[1].speedmax * 0.7) or 200 - local Leg = caszone:GetRadius() <= 10000 and 5 or UTILS.MetersToNM(caszone:GetRadius()) - local mission=AUFTRAG:NewCAS(caszone,height,Speed,coord,math.random(0,359),Leg) - mission:SetEngageDetected(25, {"Ground Units", "Light armed ships", "Helicopters"}) - mission:SetWeaponExpend(AI.Task.WeaponExpend.ALL) - mission:SetMissionSpeed(Speed) + -- Leg length. + local Leg = TargetZone:GetRadius() <= 10000 and 5 or UTILS.MetersToNM(TargetZone:GetRadius()) - -- Add assets to mission. - for _,asset in pairs(assets) do - mission:AddAsset(asset) - end - - -- Assign mission to legions. - self:MissionAssign(mission, legions) - - -- Attach mission to ops zone. - StratZone.opszone:_AddMission(self.coalition, MissionType, mission) + -- CAS mission. + mission=AUFTRAG:NewCAS(TargetZone, height, Speed, TargetCoord, math.random(0,359), Leg) - return true elseif MissionType==AUFTRAG.Type.ARTY then + + --- + -- ARTY + --- -- Create ARTY zone mission. - local TargetZone = StratZone.opszone.zone - local Target = TargetZone:GetCoordinate() local Radius = TargetZone:GetRadius() - local mission=AUFTRAG:NewARTY(Target,120,Radius) - - -- Add assets to mission. - for _,asset in pairs(assets) do - mission:AddAsset(asset) - end - - -- Assign mission to legions. - self:MissionAssign(mission, legions) - - -- Attach mission to ops zone. - StratZone.opszone:_AddMission(self.coalition, MissionType, mission) - - return true + + mission=AUFTRAG:NewARTY(TargetCoord, 120, Radius) + elseif MissionType==AUFTRAG.Type.ARMOREDGUARD then + + --- + -- ARMORGUARD + --- -- Create Armored on guard mission - local TargetZone = StratZone.opszone.zone - local Target = TargetZone:GetCoordinate() - local mission=AUFTRAG:NewARMOREDGUARD(Target) - + mission=AUFTRAG:NewARMOREDGUARD(TargetCoord) + + elseif MissionType==AUFTRAG.Type.BOMBCARPET then + + --- + -- BOMB CARPET + --- + + -- Create ARTY zone mission. + mission=AUFTRAG:NewBOMBCARPET(TargetCoord, nil, 1000) + + elseif MissionType==AUFTRAG.Type.BOMBING then + + --- + -- BOMBING + --- + + local coord=TargetZone:GetRandomCoordinate() + + mission=AUFTRAG:NewBOMBING(TargetCoord) + + elseif MissionType==AUFTRAG.Type.RECON then + + --- + -- RECON + --- + + mission=AUFTRAG:NewRECON(TargetZone, nil, 5000) + + elseif MissionType==AUFTRAG.Type.BARRAGE then + + --- + -- BARRAGE + --- + + mission=AUFTRAG:NewBARRAGE(TargetZone) + + elseif MissionType==AUFTRAG.Type.AMMOSUPPLY then + + --- + -- AMMO SUPPLY + --- + + mission=AUFTRAG:NewAMMOSUPPLY(TargetZone) + + end + + if mission then + -- Add assets to mission. for _,asset in pairs(assets) do mission:AddAsset(asset) @@ -2567,8 +2840,18 @@ function CHIEF:RecruitAssetsForZone(StratZone, MissionType, NassetsMin, NassetsM -- Attach mission to ops zone. StratZone.opszone:_AddMission(self.coalition, MissionType, mission) + + -- Attach mission to resource. + Resource.mission=mission - return true + return true + else + + -- Mission not supported. + self:E(self.lid..string.format("ERROR: Mission type not supported for OPSZONE! Unrecruiting assets...")) + LEGION.UnRecruitAssets(assets) + + return false end end diff --git a/Moose Development/Moose/Ops/Cohort.lua b/Moose Development/Moose/Ops/Cohort.lua index eda955a9e..2cdb30689 100644 --- a/Moose Development/Moose/Ops/Cohort.lua +++ b/Moose Development/Moose/Ops/Cohort.lua @@ -35,6 +35,8 @@ -- @field #number Nkilled Number of destroyed asset groups. -- @field #number engageRange Mission range in meters. -- @field #string attribute Generalized attribute of the cohort template group. +-- @field #table descriptors DCS descriptors. +-- @field #table properties DCS attributes. -- @field #table tacanChannel List of TACAN channels available to the cohort. -- @field #number radioFreq Radio frequency in MHz the cohort uses. -- @field #number radioModu Radio modulation the cohort uses. @@ -73,17 +75,20 @@ COHORT = { tacanChannel = {}, weightAsset = 99999, cargobayLimit = 0, + descriptors = {}, + properties = {}, } --- COHORT class version. -- @field #string version -COHORT.version="0.1.0" +COHORT.version="0.2.0" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO: Create FLOTILLA class. +-- DONE: Added check for properties. -- DONE: Make general so that PLATOON and SQUADRON can inherit this class. ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -127,6 +132,15 @@ function COHORT:New(TemplateGroupName, Ngroups, CohortName) -- Aircraft type. self.aircrafttype=self.templategroup:GetTypeName() + + -- Get descriptors. + self.descriptors=self.templategroup:GetUnit(1):GetDesc() + + -- Properties (DCS attributes). + self.properties=self.descriptors.attributes + + -- Print properties. + --self:I(self.properties) -- Defaults. self.Ngroups=Ngroups or 3 @@ -321,6 +335,21 @@ function COHORT:AddMissionCapability(MissionTypes, Performance) return self end +--- Check if cohort assets have a given property (DCS attribute). +-- @param #COHORT self +-- @param #string Property The property. +-- @return #boolean If `true`, cohort assets have the attribute. +function COHORT:HasProperty(Property) + + for _,property in pairs(self.properties) do + if Property==property then + return true + end + end + + return false +end + --- Get mission types this cohort is able to perform. -- @param #COHORT self -- @return #table Table of mission types. Could be empty {}. diff --git a/Moose Development/Moose/Ops/FlightGroup.lua b/Moose Development/Moose/Ops/FlightGroup.lua index 04302153e..f8b69aea8 100644 --- a/Moose Development/Moose/Ops/FlightGroup.lua +++ b/Moose Development/Moose/Ops/FlightGroup.lua @@ -166,6 +166,20 @@ FLIGHTGROUP.Attribute = { OTHER="Other", } +--- Radio Text. +-- @type FLIGHTGROUP.RadioText +-- @field #string normal +-- @field #string enhanced + +--- Radio messages. +-- @type FLIGHTGROUP.RadioMessage +-- @field #FLIGHTGROUP.RadioText AIRBORNE +-- @field #FLIGHTGROUP.RadioText TAXIING +FLIGHTGROUP.RadioMessage = { + AIRBORNE={normal="Airborn", enhanced="Airborn"}, + TAXIING={normal="Taxiing", enhanced="Taxiing"}, +} + --- FLIGHTGROUP class version. -- @field #string version FLIGHTGROUP.version="0.7.1" diff --git a/Moose Development/Moose/Ops/Legion.lua b/Moose Development/Moose/Ops/Legion.lua index 8da5350d8..8588fc0a4 100644 --- a/Moose Development/Moose/Ops/Legion.lua +++ b/Moose Development/Moose/Ops/Legion.lua @@ -1964,10 +1964,11 @@ end -- @param #number TotalWeight Total cargo weight in kg. -- @param #table Categories Group categories. -- @param #table Attributes Group attributes. See `GROUP.Attribute.` +-- @param #table Properties DCS attributes. -- @return #boolean If `true` enough assets could be recruited. -- @return #table Recruited assets. **NOTE** that we set the `asset.isReserved=true` flag so it cant be recruited by anyone else. -- @return #table Legions of recruited assets. -function LEGION.RecruitCohortAssets(Cohorts, MissionTypeRecruit, MissionTypeOpt, NreqMin, NreqMax, TargetVec2, Payloads, RangeMax, RefuelSystem, CargoWeight, TotalWeight, Categories, Attributes) +function LEGION.RecruitCohortAssets(Cohorts, MissionTypeRecruit, MissionTypeOpt, NreqMin, NreqMax, TargetVec2, Payloads, RangeMax, RefuelSystem, CargoWeight, TotalWeight, Categories, Attributes, Properties) -- The recruited assets. local Assets={} @@ -2006,7 +2007,26 @@ function LEGION.RecruitCohortAssets(Cohorts, MissionTypeRecruit, MissionTypeOpt, else return true end - end + end + + --- Function to check property. + local function CheckProperty(_cohort) + local cohort=_cohort --Ops.Cohort#COHORT + if Properties and #Properties>0 then + for _,Property in pairs(Properties) do + for _,property in pairs(cohort.properties) do + if Property==property then + return true + end + end + end + else + return true + end + end + + --BASE:I({Attributes=Attributes}) + --BASE:I({Properties=Properties}) -- Loops over cohorts. for _,_cohort in pairs(Cohorts) do @@ -2045,12 +2065,15 @@ function LEGION.RecruitCohortAssets(Cohorts, MissionTypeRecruit, MissionTypeOpt, -- Right attribute. local RightAttribute=CheckAttribute(cohort) + -- Right property (DCS attribute). + local RightProperty=CheckProperty(cohort) + -- Debug info. - cohort:T(cohort.lid..string.format("State=%s: Capable=%s, InRange=%s, Refuel=%s, CanCarry=%s, RightCategory=%s, RightAttribute=%s", - cohort:GetState(), tostring(Capable), tostring(InRange), tostring(Refuel), tostring(CanCarry), tostring(RightCategory), tostring(RightAttribute))) + cohort:T2(cohort.lid..string.format("State=%s: Capable=%s, InRange=%s, Refuel=%s, CanCarry=%s, RightCategory=%s, RightAttribute=%s, RightProperty=%s", + cohort:GetState(), tostring(Capable), tostring(InRange), tostring(Refuel), tostring(CanCarry), tostring(RightCategory), tostring(RightAttribute), tostring(RightProperty))) -- Check OnDuty, capable, in range and refueling type (if TANKER). - if cohort:IsOnDuty() and Capable and InRange and Refuel and CanCarry and RightCategory and RightAttribute then + if cohort:IsOnDuty() and Capable and InRange and Refuel and CanCarry and RightCategory and RightAttribute and RightProperty then -- Recruit assets from cohort. local assets, npayloads=cohort:RecruitAssets(MissionTypeRecruit, 999) @@ -2100,7 +2123,7 @@ function LEGION.RecruitCohortAssets(Cohorts, MissionTypeRecruit, MissionTypeOpt, --- -- Found enough assets --- - + -- Add assets to mission. local cargobay=0 for i=1,Nassets do diff --git a/Moose Development/Moose/Ops/OpsGroup.lua b/Moose Development/Moose/Ops/OpsGroup.lua index 5f21422da..4634143cc 100644 --- a/Moose Development/Moose/Ops/OpsGroup.lua +++ b/Moose Development/Moose/Ops/OpsGroup.lua @@ -34,6 +34,7 @@ -- @field #boolean isDead If true, the whole group is dead. -- @field #table waypoints Table of waypoints. -- @field #table waypoints0 Table of initial waypoints. +-- @field #boolean useMEtasks If `true`, use tasks set in the ME. Default `false`. -- @field Wrapper.Airbase#AIRBASE homebase The home base of the flight group. -- @field Wrapper.Airbase#AIRBASE destbase The destination base of the flight group. -- @field Wrapper.Airbase#AIRBASE currbase The current airbase of the flight group, i.e. where it is currently located or landing at. @@ -190,6 +191,7 @@ OPSGROUP = { mycarrier = {}, carrierLoader = {}, carrierUnloader = {}, + useMEtasks = false, } @@ -956,6 +958,84 @@ function OPSGROUP:GetCruiseAltitude() return alt end +--- Set current altitude. +-- @param #OPSGROUP self +-- @param #number Altitude Altitude in feet. Default is 10,000 ft for airplanes and 1,500 feet for helicopters. +-- @param #boolean Keep If `true` the group will maintain that speed on passing waypoints. If `nil` or `false` the group will return to the speed as defined by their route. +-- @return #OPSGROUP self +function OPSGROUP:SetAltitude(Altitude, Keep, RadarAlt) + if Altitude then + Altitude=UTILS.FeetToMeters(Altitude) + else + if self:IsFlightgroup() then + if self.isHelo then + Altitude=UTILS.FeetToMeters(1500) + else + Altitude=UTILS.FeetToMeters(10000) + end + else + Altitude=0 + end + end + + local AltType="BARO" + if RadarAlt then + AltType="RADIO" + end + + if self.controller then + self.controller:setAltitude(Altitude, Keep, AltType) + end + + return self +end + +--- Set current altitude. +-- @param #OPSGROUP self +-- @return #number Altitude in feet. +function OPSGROUP:GetAltitude() + + local alt=0 + + if self.group then + + alt=self.group:GetUnit(1):GetAltitude() + + alt=UTILS.MetersToFeet(alt) + + end + + return alt +end + +--- Set current speed. +-- @param #OPSGROUP self +-- @param #number Speed Speed in knots. Default is 70% of max speed. +-- @param #boolean Keep If `true` the group will maintain that speed on passing waypoints. If `nil` or `false` the group will return to the speed as defined by their route. +-- @param #boolean AltCorrected If `true`, use altitude corrected indicated air speed. +-- @return #OPSGROUP self +function OPSGROUP:SetSpeed(Speed, Keep, AltCorrected) + if Speed then + + else + Speed=UTILS.KmphToKnots(self.speedMax) + end + + + if AltCorrected then + local altitude=self:GetAltitude() + Speed=UTILS.KnotsToAltKIAS(Speed, altitude) + end + + Speed=UTILS.KnotsToMps(Speed) + + if self.controller then + self.controller:setSpeed(Speed, Keep) + end + + return self +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 @@ -1837,14 +1917,18 @@ end -- @param #string Culture Culture, e.g. "en-GB" (default). -- @param #string Voice Specific voice. Overrides `Gender` and `Culture`. -- @param #number Port SRS port. Default 5002. +-- @param #string PathToGoogleKey Full path to the google credentials JSON file, e.g. `"C:\Users\myUsername\Downloads\key.json"`. -- @return #OPSGROUP self -function OPSGROUP:SetSRS(PathToSRS, Gender, Culture, Voice, Port) +function OPSGROUP:SetSRS(PathToSRS, Gender, Culture, Voice, Port, PathToGoogleKey) self.useSRS=true self.msrs=MSRS:New(PathToSRS, self.frequency, self.modulation) self.msrs:SetGender(Gender) self.msrs:SetCulture(Culture) self.msrs:SetVoice(Voice) self.msrs:SetPort(Port) + if PathToGoogleKey then + self.msrs:SetGoogle(PathToGoogleKey) + end self.msrs:SetCoalition(self:GetCoalition()) return self end @@ -1853,11 +1937,12 @@ end -- @param #OPSGROUP self -- @param #string Text Text of transmission. -- @param #number Delay Delay in seconds before the transmission is started. +-- @param #boolean SayCallsign If `true`, the callsign is prepended to the given text. Default `false`. -- @return #OPSGROUP self -function OPSGROUP:RadioTransmission(Text, Delay) +function OPSGROUP:RadioTransmission(Text, Delay, SayCallsign) if Delay and Delay>0 then - self:ScheduleOnce(Delay, OPSGROUP.RadioTransmission, self, Text, 0) + self:ScheduleOnce(Delay, OPSGROUP.RadioTransmission, self, Text, 0, SayCallsign) else if self.useSRS and self.msrs then @@ -1866,9 +1951,14 @@ function OPSGROUP:RadioTransmission(Text, Delay) self.msrs:SetFrequencies(freq) self.msrs:SetModulations(modu) + + if SayCallsign then + local callsign=self:GetCallsignName() + Text=string.format("%s, %s", callsign, Text) + end -- Debug info. - self:T(self.lid..string.format("Radio transmission on %.3f MHz %s: %s", freq, UTILS.GetModulationName(modu), Text)) + self:I(self.lid..string.format("Radio transmission on %.3f MHz %s: %s", freq, UTILS.GetModulationName(modu), Text)) self.msrs:PlayText(Text) end @@ -3717,12 +3807,26 @@ function OPSGROUP:onafterTaskExecute(From, Event, To, Task) else -- FLIGHTGROUP not implemented (intended!) for this AUFTRAG type. end - - --- + + elseif Task.dcstask.id==AUFTRAG.SpecialTask.GROUNDATTACK then + + --- + -- Task "Ground Attack" Mission. + --- + + -- Engage target. + local target=Task.dcstask.params.target --Ops.Target#TARGET + + if target then + self:EngageTarget(target) + end + + elseif Task.dcstask.id==AUFTRAG.SpecialTask.HOVER then + + --- -- Task "Hover" Mission. --- - - elseif Task.dcstask.id==AUFTRAG.SpecialTask.HOVER then + if self.isFlightgroup then self:T("We are Special Auftrag HOVER, hovering now ...") --self:I({Task.dcstask.params}) @@ -3746,6 +3850,7 @@ function OPSGROUP:onafterTaskExecute(From, Event, To, Task) local timer = TIMER:New(FlyOn,helo,Speed,CruiseAlt,Task) timer:Start(time) end + else -- If task is scheduled (not waypoint) set task. @@ -3862,6 +3967,8 @@ function OPSGROUP:onafterTaskCancel(From, Event, To, Task) done=true elseif Task.dcstask.id==AUFTRAG.SpecialTask.ONGUARD or Task.dcstask.id==AUFTRAG.SpecialTask.ARMOREDGUARD then done=true + elseif Task.dcstask.id==AUFTRAG.SpecialTask.GROUNDATTACK then + done=true elseif stopflag==1 or (not self:IsAlive()) or self:IsDead() or self:IsStopped() then -- Manual call TaskDone if setting flag to one was not successful. done=true @@ -4624,8 +4731,7 @@ function OPSGROUP:RouteToMission(mission, delay) end -- Get ingress waypoint. - if mission.type==AUFTRAG.Type.PATROLZONE or mission.type==AUFTRAG.Type.BARRAGE or mission.type==AUFTRAG.Type.AMMOSUPPLY - or mission.type.FUELSUPPLY then + if mission.type==AUFTRAG.Type.PATROLZONE or mission.type==AUFTRAG.Type.BARRAGE or mission.type==AUFTRAG.Type.AMMOSUPPLY or mission.type.FUELSUPPLY then local zone=mission.engageTarget:GetObject() --Core.Zone#ZONE waypointcoord=zone:GetRandomCoordinate(nil , nil, surfacetypes) elseif mission.type==AUFTRAG.Type.ONGUARD or mission.type==AUFTRAG.Type.ARMOREDGUARD then @@ -9344,7 +9450,7 @@ function OPSGROUP:_InitWaypoints(WpIndexMin, WpIndexMax) -- Get DCS waypoint tasks set in the ME. EXPERIMENTAL! local DCStasks=wp.task and wp.task.params.tasks or nil - if DCStasks then + if DCStasks and self.useMEtasks then for _,DCStask in pairs(DCStasks) do -- Wrapped Actions are commands. We do not take those. if DCStask.id and DCStask.id~="WrappedAction" then @@ -10515,6 +10621,29 @@ function OPSGROUP:SwitchCallsign(CallsignName, CallsignNumber) return self end +--- Get callsign +-- @param #OPSGROUP self +-- @return #string Callsign name, e.g. Uzi-1 +function OPSGROUP:GetCallsignName() + + local numberSquad=self.callsign.NumberSquad or self.callsignDefault.NumberSquad + local numberGroup=self.callsign.NumberGroup or self.callsignDefault.NumberGroup + + local callsign="Unknown 1" + + if numberSquad and numberGroup then + + local nameSquad=UTILS.GetCallsignName(numberSquad) + + callsign=string.format("%s %d", nameSquad, numberGroup) + + else + + end + + return callsign +end + ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Element and Group Status Functions ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- diff --git a/Moose Development/Moose/Ops/OpsZone.lua b/Moose Development/Moose/Ops/OpsZone.lua index 575bb4919..c738d9608 100644 --- a/Moose Development/Moose/Ops/OpsZone.lua +++ b/Moose Development/Moose/Ops/OpsZone.lua @@ -587,14 +587,6 @@ function OPSZONE:onafterStop(From, Event, To) -- Unhandle events. self:UnHandleEvent(EVENTS.BaseCaptured) - - -- Cancel all running missions. - for _,_entry in pairs(self.Missions or {}) do - local entry = _entry -- Ops.OpsZone#OPSZONE.MISSION - if entry.Mission and entry.Mission:IsNotOver() then - entry.Mission:Cancel() - end - end -- Stop FSM scheduler. self.CallScheduler:Clear()