diff --git a/Moose Development/Moose/Ops/ArmyGroup.lua b/Moose Development/Moose/Ops/ArmyGroup.lua index f27cd357a..a5754efd9 100644 --- a/Moose Development/Moose/Ops/ArmyGroup.lua +++ b/Moose Development/Moose/Ops/ArmyGroup.lua @@ -101,6 +101,7 @@ function ARMYGROUP:New(Group) self.lid=string.format("ARMYGROUP %s | ", self.groupname) -- Defaults + self.isArmygroup=true self:SetDefaultROE() self:SetDefaultAlarmstate() self:SetDetection() diff --git a/Moose Development/Moose/Ops/Auftrag.lua b/Moose Development/Moose/Ops/Auftrag.lua index 2711c190a..7a952cba8 100644 --- a/Moose Development/Moose/Ops/Auftrag.lua +++ b/Moose Development/Moose/Ops/Auftrag.lua @@ -101,6 +101,7 @@ -- -- @field #string missionTask Mission task. See `ENUMS.MissionTask`. -- @field #number missionAltitude Mission altitude in meters. +-- @field #number missionSpeed Mission speed in km/h. -- @field #number missionFraction Mission coordiante fraction. Default is 0.5. -- @field #number missionRange Mission range in meters. Used in AIRWING class. -- @field Core.Point#COORDINATE missionWaypointCoord Mission waypoint coordinate. @@ -441,7 +442,7 @@ AUFTRAG.TargetType={ --- AUFTRAG class version. -- @field #string version -AUFTRAG.version="0.5.0" +AUFTRAG.version="0.6.0" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list @@ -1196,18 +1197,22 @@ end --- Create a PATROLZONE mission. Group(s) will go to the zone and patrol it randomly. -- @param #AUFTRAG self -- @param Core.Zone#ZONE Zone The patrol zone. +-- @param #number Speed Speed in knots. +-- @param #number Altitude Altitude in feet. Only for airborne units. Default 2000 feet ASL. -- @return #AUFTRAG self -function AUFTRAG:NewPATROLZONE(Zone) +function AUFTRAG:NewPATROLZONE(Zone, Speed, Altitude) local mission=AUFTRAG:New(AUFTRAG.Type.PATROLZONE) - mission:_TargetFromObject(Zone) - + mission:_TargetFromObject(Zone) + 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.DCStask=mission:GetDCSMissionTask() @@ -3425,9 +3430,9 @@ function AUFTRAG:GetDCSMissionTask(TaskControllable) -- We create a "fake" DCS task and pass the parameters to the FLIGHTGROUP. local param={} - param.zonename=self:GetTargetName() param.zone=self:GetObjective() - param.altitude=70 + param.altitude=self.missionAltitude + param.speed=self.missionSpeed DCStask.params=param diff --git a/Moose Development/Moose/Ops/FlightGroup.lua b/Moose Development/Moose/Ops/FlightGroup.lua index 5a8d8cc68..fb5d28510 100644 --- a/Moose Development/Moose/Ops/FlightGroup.lua +++ b/Moose Development/Moose/Ops/FlightGroup.lua @@ -251,6 +251,7 @@ function FLIGHTGROUP:New(group) self:SetDefaultROE() self:SetDefaultROT() self:SetDetection() + self.isFlightgroup=true -- Holding flag. self.flaghold=USERFLAG:New(string.format("%s_FlagHold", self.groupname)) @@ -277,10 +278,9 @@ function FLIGHTGROUP:New(group) self:AddTransition("*", "OutOfMissilesAG", "*") -- Group is out of A2G missiles. Not implemented yet! self:AddTransition("*", "OutOfMissilesAS", "*") -- Group is out of A2G missiles. Not implemented yet! - self:AddTransition("Airborne", "EngageTargets", "Engaging") -- Engage targets. + self:AddTransition("Airborne", "EngageTarget", "Engaging") -- Engage targets. self:AddTransition("Engaging", "Disengage", "Airborne") -- Engagement over. - self:AddTransition("*", "ElementParking", "*") -- An element is parking. self:AddTransition("*", "ElementEngineOn", "*") -- An element spooled up the engines. self:AddTransition("*", "ElementTaxiing", "*") -- An element is taxiing to the runway. @@ -291,7 +291,6 @@ function FLIGHTGROUP:New(group) self:AddTransition("*", "ElementOutOfAmmo", "*") -- An element is completely out of ammo. - self:AddTransition("*", "Parking", "Parking") -- The whole flight group is parking. self:AddTransition("*", "Taxiing", "Taxiing") -- The whole flight group is taxiing. self:AddTransition("*", "Takeoff", "Airborne") -- The whole flight group is airborne. @@ -514,13 +513,14 @@ end --- Enable to automatically engage detected targets. -- @param #FLIGHTGROUP self --- @param #number RangeMax Max range in NM. Only detected targets within this radius will be engaged. Default is 25 NM. +-- @param #number RangeMax Max range in NM. Only detected targets within this radius from the group will be engaged. Default is 25 NM. -- @param #table TargetTypes Types of target attributes that will be engaged. See [DCS enum attributes](https://wiki.hoggitworld.com/view/DCS_enum_attributes). Default "All". -- @param Core.Set#SET_ZONE EngageZoneSet Set of zones in which targets are engaged. Default is anywhere. -- @param Core.Set#SET_ZONE NoEngageZoneSet Set of zones in which targets are *not* engaged. Default is nowhere. --- @return #OPSGROUP self +-- @return #FLIGHTGROUP self function FLIGHTGROUP:SetEngageDetectedOn(RangeMax, TargetTypes, EngageZoneSet, NoEngageZoneSet) + -- Ensure table. if TargetTypes then if type(TargetTypes)~="table" then TargetTypes={TargetTypes} @@ -528,13 +528,27 @@ function FLIGHTGROUP:SetEngageDetectedOn(RangeMax, TargetTypes, EngageZoneSet, N else TargetTypes={"All"} end + + -- Ensure SET_ZONE if ZONE is provided. + if EngageZoneSet and EngageZoneSet:IsInstanceOf("ZONE_BASE") then + local zoneset=SET_ZONE:New():AddZone(EngageZoneSet) + EngageZoneSet=zoneset + end + if NoEngageZoneSet and NoEngageZoneSet:IsInstanceOf("ZONE_BASE") then + local zoneset=SET_ZONE:New():AddZone(NoEngageZoneSet) + NoEngageZoneSet=zoneset + end + -- Set parameters. self.engagedetectedOn=true self.engagedetectedRmax=UTILS.NMToMeters(RangeMax or 25) self.engagedetectedTypes=TargetTypes self.engagedetectedEngageZones=EngageZoneSet self.engagedetectedNoEngageZones=NoEngageZoneSet + -- Ensure detection is ON or it does not make any sense. + self:SetDetection(true) + return self end @@ -543,10 +557,10 @@ end -- @return #OPSGROUP self function FLIGHTGROUP:SetEngageDetectedOff() self.engagedetectedOn=false + return self end - --- Enable that the group is despawned after landing. This can be useful to avoid DCS taxi issues with other AI or players or jamming taxiways. -- @param #FLIGHTGROUP self -- @return #FLIGHTGROUP self @@ -1014,8 +1028,7 @@ function FLIGHTGROUP:onafterStatus(From, Event, To) if self:IsAirborne() and self.detectionOn and self.engagedetectedOn then - env.info("FF check detected:") - + -- Target. local targetgroup=nil --Wrapper.Group#GROUP local targetdist=math.huge @@ -1023,8 +1036,6 @@ function FLIGHTGROUP:onafterStatus(From, Event, To) for _,_group in pairs(self.detectedgroups:GetSet()) do local group=_group --Wrapper.Group#GROUP - env.info("group "..group:GetName()) - if group and group:IsAlive() then local targetcoord=group:GetCoordinate() @@ -1077,7 +1088,6 @@ function FLIGHTGROUP:onafterStatus(From, Event, To) if insideEngage and not insideNoEngage then targetdist=distance targetgroup=group - env.info("targetgroup "..group:GetName()) end end @@ -1087,8 +1097,7 @@ function FLIGHTGROUP:onafterStatus(From, Event, To) end if targetgroup then - env.info("engage target! "..targetgroup:GetName()) - self:EngageTargets(targetgroup) + self:EngageTarget(targetgroup) end end @@ -1911,8 +1920,14 @@ function FLIGHTGROUP:onbeforeUpdateRoute(From, Event, To, n) end if self.taskcurrent>0 then - self:E(self.lid.."Update route denied because taskcurrent>0") - allowed=false + + local task=self:GetTaskCurrent() + if task.dcstask.id=="PatrolZone" then + -- For patrol zone, we need to allow the update. + else + self:E(self.lid.."Update route denied because taskcurrent>0") + allowed=false + end end -- Not good, because mission will never start. Better only check if there is a current task! @@ -2035,6 +2050,11 @@ function FLIGHTGROUP:_CheckGroupDone(delay) self:UnpauseMission() return end + + -- Group is currently engaging. + if self:IsEngaging() then + return + end -- Number of tasks remaining. local nTasks=self:CountRemainingTasks() @@ -2493,24 +2513,24 @@ function FLIGHTGROUP:onafterHolding(From, Event, To) end ---- On after "EngageTargets" event. Order to engage a set of units. +--- On after "EngageTarget" event. -- @param #FLIGHTGROUP self -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. --- @param Core.Set#SET_UNIT TargetUnitSet -function FLIGHTGROUP:onafterEngageTargets(From, Event, To, Target) - - env.info("FF engage targets") +-- @param #table Target Target object. Can be a UNIT, STATIC, GROUP, SET_UNIT or SET_GROUP object. +function FLIGHTGROUP:onafterEngageTarget(From, Event, To, Target) + -- DCS task. local DCStask=nil - if Target:IsInstanceOf("UNIT") then + -- Check target object. + if Target:IsInstanceOf("UNIT") or Target:IsInstanceOf("STATIC") then + + DCStask=self:GetGroup():TaskAttackUnit(Target, true) elseif Target:IsInstanceOf("GROUP") then - - env.info("FF engage targets GROUP!") - + DCStask=self:GetGroup():TaskAttackGroup(Target, nil, nil, nil, nil, nil, nil, true) elseif Target:IsInstanceOf("SET_UNIT") then @@ -2527,13 +2547,25 @@ function FLIGHTGROUP:onafterEngageTargets(From, Event, To, Target) DCStask=self:GetGroup():TaskCombo(DCSTasks) elseif Target:IsInstanceOf("SET_GROUP") then + + local DCSTasks={} + + for _,_unit in pairs(Target:GetSet()) do --detected by =HRP= Zero + local unit=_unit --Wrapper.Unit#UNIT + local task=self:GetGroup():TaskAttackGroup(Target, nil, nil, nil, nil, nil, nil, true) + table.insert(DCSTasks) + end + + -- Task combo. + DCStask=self:GetGroup():TaskCombo(DCSTasks) else - + self:E("ERROR: unknown Target in EngageTarget! Needs to be a UNIT, STATIC, GROUP, SET_UNIT or SET_GROUP") + return end - -- Create new task.The description "Task_Engage" is checked. - local Task=self:NewTaskScheduled(DCStask, 1, "Task_Engage", 0) + -- Create new task.The description "Engage_Target" is checked so do not change that lightly. + local Task=self:NewTaskScheduled(DCStask, 1, "Engage_Target", 0) -- Backup ROE setting. Task.backupROE=self:GetROE() @@ -2541,6 +2573,12 @@ function FLIGHTGROUP:onafterEngageTargets(From, Event, To, Target) -- Switch ROE to open fire self:SwitchROE(ENUMS.ROE.OpenFire) + -- Pause current mission. + local mission=self:GetMissionCurrent() + if mission then + self:PauseMission() + end + -- Execute task. self:TaskExecute(Task) @@ -2553,9 +2591,7 @@ end -- @param #string To To state. -- @param Core.Set#SET_UNIT TargetUnitSet function FLIGHTGROUP:onafterDisengage(From, Event, To) - - env.info("FF disengage targets") - + self:T(self.lid.."Disengage target") end --- On before "LandAt" event. Check we have a helo group. diff --git a/Moose Development/Moose/Ops/NavyGroup.lua b/Moose Development/Moose/Ops/NavyGroup.lua index 6c6f54d44..7fa71c335 100644 --- a/Moose Development/Moose/Ops/NavyGroup.lua +++ b/Moose Development/Moose/Ops/NavyGroup.lua @@ -123,6 +123,7 @@ function NAVYGROUP:New(GroupName) self:SetDefaultAlarmstate() self:SetPatrolAdInfinitum(true) self:SetPathfinding(false) + self.isNavygroup=true -- Add FSM transitions. -- From State --> Event --> To State diff --git a/Moose Development/Moose/Ops/OpsGroup.lua b/Moose Development/Moose/Ops/OpsGroup.lua index 0638bfbf5..a79f5e1c4 100644 --- a/Moose Development/Moose/Ops/OpsGroup.lua +++ b/Moose Development/Moose/Ops/OpsGroup.lua @@ -22,6 +22,9 @@ -- @field #table template Template of the group. -- @field #boolean isLateActivated Is the group late activated. -- @field #boolean isUncontrolled Is the group uncontrolled. +-- @field #boolean isFlightgroup Is a FLIGHTGROUP. +-- @field #boolean isArmygroup Is an ARMYGROUP. +-- @field #boolean isNavygroup Is a NAVYGROUP. -- @field #table elements Table of elements, i.e. units of the group. -- @field #boolean isAI If true, group is purely AI. -- @field #boolean isAircraft If true, group is airplane or helicopter. @@ -327,7 +330,7 @@ OPSGROUP.TaskType={ --- NavyGroup version. -- @field #string version -OPSGROUP.version="0.7.0" +OPSGROUP.version="0.7.1" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list @@ -2133,7 +2136,7 @@ function OPSGROUP:onafterTaskExecute(From, Event, To, Task) if self.taskcurrent>0 then self:TaskCancel() end - + -- Set current task. self.taskcurrent=Task.id @@ -2169,10 +2172,25 @@ function OPSGROUP:onafterTaskExecute(From, Event, To, Task) elseif Task.dcstask.id=="PatrolZone" then - local Coordinate=Task.dcstask.params.zone:GetCoordinate() - - FLIGHTGROUP.AddWaypoint(self, Coordinate, Speed, AfterWaypointWithID, Altitude) - + --- + -- Task patrol zone. + --- + + -- Parameters. + local zone=Task.dcstask.params.zone --Core.Zone#ZONE + local Coordinate=zone:GetRandomCoordinate() + local Speed=UTILS.KmphToKnots(Task.dcstask.params.speed or self.speedCruise) + local Altitude=Task.dcstask.params.altitude and UTILS.MetersToFeet(Task.dcstask.params.altitude) or nil + + -- New waypoint. + if self.isFlightgroup then + FLIGHTGROUP.AddWaypoint(self, Coordinate, Speed, AfterWaypointWithID, Altitude) + elseif self.isNavygroup then + ARMYGROUP.AddWaypoint(self, Coordinate, Speed, AfterWaypointWithID, Formation) + elseif self.isArmygroup then + NAVYGROUP.AddWaypoint(self, Coordinate, Speed, AfterWaypointWithID, Altitude) + end + else -- If task is scheduled (not waypoint) set task. @@ -2252,6 +2270,8 @@ function OPSGROUP:onafterTaskCancel(From, Event, To, Task) if Task.dcstask.id=="Formation" then Task.formation:Stop() done=true + elseif Task.dcstask.id=="PatrolZone" 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 @@ -2272,7 +2292,7 @@ function OPSGROUP:onafterTaskCancel(From, Event, To, Task) end else - + local text=string.format("WARNING: No (current) task to cancel!") self:E(self.lid..text) @@ -2307,7 +2327,7 @@ function OPSGROUP:onafterTaskDone(From, Event, To, Task) -- Debug message. local text=string.format("Task done: %s ID=%d", Task.description, Task.id) - self:T(self.lid..text) + self:I(self.lid..text) -- No current task. if Task.id==self.taskcurrent then @@ -2872,6 +2892,8 @@ function OPSGROUP:RouteToMission(mission, delay) end end + + elseif mission.type==AUFTRAG.Type.PATROLZONE then end @@ -3005,13 +3027,24 @@ function OPSGROUP:onafterPassingWaypoint(From, Event, To, Waypoint) -- Get the current task. local task=self:GetTaskCurrent() - if task and task.id=="PatrolZone" then + if task and task.dcstask.id=="PatrolZone" then - local zone=task.dcstask.params.zone --Core.Zone#ZONE - - local Coordinate=zone:GetRandomCoordinate() + -- Remove old waypoint. + self:RemoveWaypointByID(Waypoint.uid) + + local zone=task.dcstask.params.zone --Core.Zone#ZONE + local Coordinate=zone:GetRandomCoordinate() + local Speed=UTILS.KmphToKnots(task.dcstask.params.speed or self.speedCruise) + local Altitude=task.dcstask.params.altitude and UTILS.MetersToFeet(task.dcstask.params.altitude) or nil - FLIGHTGROUP.AddWaypoint(self,Coordinate, Speed, AfterWaypointWithID, Altitude, Updateroute) + if self.isFlightgroup then + FLIGHTGROUP.AddWaypoint(self, Coordinate, Speed, AfterWaypointWithID, Altitude) + elseif self.isNavygroup then + ARMYGROUP.AddWaypoint(self, Coordinate, Speed, AfterWaypointWithID, Formation) + elseif self.isArmygroup then + NAVYGROUP.AddWaypoint(self, Coordinate, Speed, AfterWaypointWithID, Altitude) + end + else @@ -4208,7 +4241,6 @@ function OPSGROUP:_CheckAmmoStatus() self.outofAmmo=false end if ammo.Total==0 and not self.outofAmmo then - env.info("FF out of ammo") self.outofAmmo=true self:OutOfAmmo() end diff --git a/Moose Development/Moose/Ops/Target.lua b/Moose Development/Moose/Ops/Target.lua index 41a53905a..7aea445b2 100644 --- a/Moose Development/Moose/Ops/Target.lua +++ b/Moose Development/Moose/Ops/Target.lua @@ -72,6 +72,7 @@ TARGET = { -- @field #string SCENERY Target is a SCENERY object. -- @field #string COORDINATE Target is a COORDINATE. -- @field #string AIRBASE Target is an AIRBASE. +-- @field #string ZONE Target is a ZONE object. TARGET.ObjectType={ GROUP="Group", UNIT="Unit", @@ -79,6 +80,7 @@ TARGET.ObjectType={ SCENERY="Scenery", COORDINATE="Coordinate", AIRBASE="Airbase", + ZONE="Zone", } @@ -89,12 +91,14 @@ TARGET.ObjectType={ -- @field #string NAVAL -- @field #string AIRBASE -- @field #string COORDINATE +-- @field #string ZONE TARGET.Category={ AIRCRAFT="Aircraft", GROUND="Ground", NAVAL="Naval", AIRBASE="Airbase", COORDINATE="Coordinate", + ZONE="Zone", } --- Object status. @@ -124,7 +128,7 @@ _TARGETID=0 --- TARGET class version. -- @field #string version -TARGET.version="0.3.0" +TARGET.version="0.3.1" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list @@ -658,12 +662,12 @@ function TARGET:_AddObject(Object) elseif Object:IsInstanceOf("ZONE_BASE") then local zone=Object --Core.Zone#ZONE_BASE - Object=zone:GetCoordinate() + Object=zone --:GetCoordinate() - target.Type=TARGET.ObjectType.COORDINATE + target.Type=TARGET.ObjectType.ZONE target.Name=zone:GetName() - target.Coordinate=Object + target.Coordinate=zone:GetCoordinate() target.Life0=1 target.Life=1 @@ -773,7 +777,13 @@ function TARGET:GetTargetLife(Target) elseif Target.Type==TARGET.ObjectType.COORDINATE then return 1 + + elseif Target.Type==TARGET.ObjectType.ZONE then + + return 1 + else + self:E("ERROR: unknown target object type in GetTargetLife!") end end @@ -865,6 +875,13 @@ function TARGET:GetTargetVec3(Target) local vec3={x=object.x, y=object.y, z=object.z} return vec3 + elseif Target.Type==TARGET.ObjectType.ZONE then + + local object=Target.Object --Core.Zone#ZONE + + local vec3=object:GetVec3() + return vec3 + end self:E(self.lid.."ERROR: Unknown TARGET type! Cannot get Vec3") @@ -1032,7 +1049,13 @@ function TARGET:GetTargetCategory(Target) elseif Target.Type==TARGET.ObjectType.COORDINATE then return TARGET.Category.COORDINATE - + + elseif Target.Type==TARGET.ObjectType.ZONE then + + return TARGET.Category.ZONE + + else + self:E("ERROR: unknown target category!") end return category @@ -1141,6 +1164,10 @@ function TARGET:CountObjectives(Target) -- No target we can check! + elseif Target.Type==TARGET.ObjectType.ZONE then + + -- No target we can check! + else self:E(self.lid.."ERROR: Unknown target type! Cannot count targets") end