From 6c9ce55759567fa5b21bfaac64bda2ac5832ee3d Mon Sep 17 00:00:00 2001 From: Frank Date: Tue, 13 Oct 2020 00:29:31 +0200 Subject: [PATCH 01/10] Navygroup - Fixed Astar bug - Turn into wind stop --- Moose Development/Moose/Ops/NavyGroup.lua | 73 +++++++++++++++++------ Moose Development/Moose/Ops/OpsGroup.lua | 34 ++++++----- 2 files changed, 73 insertions(+), 34 deletions(-) diff --git a/Moose Development/Moose/Ops/NavyGroup.lua b/Moose Development/Moose/Ops/NavyGroup.lua index 1ef4a3473..ceb9b3f55 100644 --- a/Moose Development/Moose/Ops/NavyGroup.lua +++ b/Moose Development/Moose/Ops/NavyGroup.lua @@ -109,7 +109,8 @@ function NAVYGROUP:New(GroupName) self:AddTransition("*", "Cruise", "Cruising") -- Hold position. self:AddTransition("*", "TurnIntoWind", "IntoWind") -- Command the group to turn into the wind. - self:AddTransition("*", "TurnIntoWindOver", "Cruising") -- Turn into wind is over. + self:AddTransition("IntoWind", "TurnIntoWindStop", "IntoWind") -- Stop a turn into wind. + self:AddTransition("IntoWind", "TurnIntoWindOver", "Cruising") -- Turn into wind is over. self:AddTransition("*", "TurningStarted", "*") -- Group started turning. self:AddTransition("*", "TurningStopped", "*") -- Group stopped turning. @@ -144,14 +145,6 @@ function NAVYGROUP:New(GroupName) -- Initialize the group. self:_InitGroup() - -- Debug trace. - if false then - self.Debug=true - BASE:TraceOnOff(true) - BASE:TraceClass(self.ClassName) - BASE:TraceLevel(1) - end - -- Handle events: self:HandleEvent(EVENTS.Birth, self.OnEventBirth) self:HandleEvent(EVENTS.Dead, self.OnEventDead) @@ -272,7 +265,7 @@ function NAVYGROUP:AddTaskAttackGroup(TargetGroup, WeaponExpend, WeaponType, Clo return task end ---- Add aircraft recovery time window and recovery case. +--- Create a turn into wind window. Note that this is not executed as it not added to the queue. -- @param #NAVYGROUP self -- @param #string starttime Start time, e.g. "8:00" for eight o'clock. Default now. -- @param #string stoptime Stop time, e.g. "9:00" for nine o'clock. Default 90 minutes after start time. @@ -280,7 +273,7 @@ end -- @param #boolean uturn If true (or nil), carrier wil perform a U-turn and go back to where it came from before resuming its route to the next waypoint. If false, it will go directly to the next waypoint. -- @param #number offset Offset angle in degrees, e.g. to account for an angled runway. Default 0 deg. -- @return #NAVYGROUP.IntoWind Recovery window. -function NAVYGROUP:CreateTurnIntoWind(starttime, stoptime, speed, uturn, offset) +function NAVYGROUP:_CreateTurnIntoWind(starttime, stoptime, speed, uturn, offset) -- Absolute mission time in seconds. local Tnow=timer.getAbsTime() @@ -335,17 +328,17 @@ function NAVYGROUP:CreateTurnIntoWind(starttime, stoptime, speed, uturn, offset) return recovery end ---- Add aircraft recovery time window and recovery case. +--- Add a time window, where the groups steams into the wind. -- @param #NAVYGROUP self -- @param #string starttime Start time, e.g. "8:00" for eight o'clock. Default now. -- @param #string stoptime Stop time, e.g. "9:00" for nine o'clock. Default 90 minutes after start time. -- @param #number speed Speed in knots during turn into wind leg. --- @param #boolean uturn If true (or nil), carrier wil perform a U-turn and go back to where it came from before resuming its route to the next waypoint. If false, it will go directly to the next waypoint. +-- @param #boolean uturn If `true` (or `nil`), carrier wil perform a U-turn and go back to where it came from before resuming its route to the next waypoint. If false, it will go directly to the next waypoint. -- @param #number offset Offset angle in degrees, e.g. to account for an angled runway. Default 0 deg. --- @return #NAVYGROUP.IntoWind Recovery window. +-- @return #NAVYGROUP.IntoWind Turn into window data table. function NAVYGROUP:AddTurnIntoWind(starttime, stoptime, speed, uturn, offset) - local recovery=self:CreateTurnIntoWind(starttime, stoptime, speed, uturn, offset) + local recovery=self:_CreateTurnIntoWind(starttime, stoptime, speed, uturn, offset) --TODO: check if window is overlapping with an other and if extend the window. @@ -355,6 +348,29 @@ function NAVYGROUP:AddTurnIntoWind(starttime, stoptime, speed, uturn, offset) return recovery end +--- Remove steam into wind window from queue. If the window is currently active, it is stopped first. +-- @param #NAVYGROUP self +-- @param #NAVYGROUP.IntoWind IntoWindData Turn into window data table. +-- @return #NAVYGROUP self +function NAVYGROUP:RemoveTurnIntoWind(IntoWindData) + + -- Check if this is a window currently open. + if self.intowind and self.intowind.id==IntoWindData.Id then + self:TurnIntoWindStop() + return + end + + for i,_tiw in pairs(self.Qintowind) do + local tiw=_tiw --#NAVYGROUP.IntoWind + if tiw.Id==IntoWindData.Id then + table.remove(self.Qintowind, i) + break + end + end + + return self +end + --- Check if the group is currently holding its positon. -- @param #NAVYGROUP self @@ -757,14 +773,35 @@ function NAVYGROUP:onafterTurnIntoWind(From, Event, To, IntoWind) end +--- On before "TurnIntoWindStop" event. +-- @param #NAVYGROUP self +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +function NAVYGROUP:onbeforeTurnIntoWindStop(From, Event, To) + + if self.intowind then + return true + else + return false + end + +end + +--- On after "TurnIntoWindStop" event. +-- @param #NAVYGROUP self +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +function NAVYGROUP:onafterTurnIntoWindStop(From, Event, To) + self:TurnIntoWindOver() +end + --- On after "TurnIntoWindOver" event. -- @param #NAVYGROUP self -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. --- @param #number Duration Duration in seconds. --- @param #number Speed Speed in knots. --- @param #boolean Uturn Return to the place we came from. function NAVYGROUP:onafterTurnIntoWindOver(From, Event, To) -- Debug message. diff --git a/Moose Development/Moose/Ops/OpsGroup.lua b/Moose Development/Moose/Ops/OpsGroup.lua index 733cdd056..d0e7e5e78 100644 --- a/Moose Development/Moose/Ops/OpsGroup.lua +++ b/Moose Development/Moose/Ops/OpsGroup.lua @@ -3440,21 +3440,25 @@ function OPSGROUP._PassingWaypoint(group, opsgroup, uid) opsgroup.speed=wpnext.speed end + + -- Debug message. + local text=string.format("Group passing waypoint uid=%d", uid) + opsgroup:T(opsgroup.lid..text) - -- Check if the group is still pathfinding. - if opsgroup.ispathfinding and not waypoint.astar then - opsgroup.ispathfinding=false - end - - -- Check special waypoints. + -- Trigger PassingWaypoint event. if waypoint.astar then - + + -- Remove Astar waypoint. opsgroup:RemoveWaypointByID(uid) + -- Cruise. + opsgroup:Cruise() + elseif waypoint.detour then + -- Remove detour waypoint. opsgroup:RemoveWaypointByID(uid) - + -- Trigger event. opsgroup:DetourReached() @@ -3466,14 +3470,12 @@ function OPSGROUP._PassingWaypoint(group, opsgroup, uid) opsgroup:E("ERROR: waypoint.detour should be 0 or 1") end - end - - -- Debug message. - local text=string.format("Group passing waypoint uid=%d", uid) - opsgroup:T(opsgroup.lid..text) - - -- Trigger PassingWaypoint event. - if not (waypoint.astar or waypoint.detour) then + else + + -- Check if the group is still pathfinding. + if opsgroup.ispathfinding then + opsgroup.ispathfinding=false + end -- Increase passing counter. waypoint.npassed=waypoint.npassed+1 From 187093fa5a41654c766a6e3a1571a4015760b5b7 Mon Sep 17 00:00:00 2001 From: Frank Date: Wed, 14 Oct 2020 00:24:12 +0200 Subject: [PATCH 02/10] NavyGroup Update --- Moose Development/Moose/Ops/ArmyGroup.lua | 57 +++++- Moose Development/Moose/Ops/Auftrag.lua | 2 +- Moose Development/Moose/Ops/FlightGroup.lua | 4 +- Moose Development/Moose/Ops/NavyGroup.lua | 208 +++++++++++++------- Moose Development/Moose/Ops/OpsGroup.lua | 13 +- 5 files changed, 204 insertions(+), 80 deletions(-) diff --git a/Moose Development/Moose/Ops/ArmyGroup.lua b/Moose Development/Moose/Ops/ArmyGroup.lua index 115607b46..f32dca574 100644 --- a/Moose Development/Moose/Ops/ArmyGroup.lua +++ b/Moose Development/Moose/Ops/ArmyGroup.lua @@ -3,7 +3,16 @@ -- **Main Features:** -- -- * Dynamically add and remove waypoints. --- +-- * Convenient checks when the group enters or leaves a zone. +-- * Sophisticated task queueing system. +-- * Compatible with AUFTRAG class. +-- * Easy change of ROE, alarm state, formation and other settings. +-- * Many additional events that the mission designer can hook into. +-- +-- **Example Missions:** +-- +-- Demo missions can be found on [github](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/OPS%20-%20Armygroup). +-- -- === -- -- ### Author: **funkyfranky** @@ -15,6 +24,7 @@ -- @type ARMYGROUP -- @field #boolean adinfinitum Resume route at first waypoint when final waypoint is reached. -- @field #boolean formationPerma Formation that is used permanently and overrules waypoint formations. +-- @field #boolean isMobile If true, group is mobile. -- @extends Ops.OpsGroup#OPSGROUP --- *Your soul may belong to Jesus, but your ass belongs to the marines.* -- Eugene B. Sledge @@ -46,7 +56,8 @@ ARMYGROUP.version="0.3.0" -- TODO list ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- TODO: A lot. +-- TODO: Check if group is mobile. +-- TODO: Rearm. Specify a point where to go and wait until ammo is full. ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Constructor @@ -54,12 +65,12 @@ ARMYGROUP.version="0.3.0" --- Create a new ARMYGROUP class object. -- @param #ARMYGROUP self --- @param #string GroupName Name of the group. +-- @param Wrapper.Group#GROUP Group The group object. Can also be given by its group name as `#string`. -- @return #ARMYGROUP self -function ARMYGROUP:New(GroupName) +function ARMYGROUP:New(Group) -- Inherit everything from FSM class. - local self=BASE:Inherit(self, OPSGROUP:New(GroupName)) -- #ARMYGROUP + local self=BASE:Inherit(self, OPSGROUP:New(Group)) -- #ARMYGROUP -- Set some string id for output to DCS.log file. self.lid=string.format("ARMYGROUP %s | ", self.groupname) @@ -73,7 +84,10 @@ function ARMYGROUP:New(GroupName) -- Add FSM transitions. -- From State --> Event --> To State self:AddTransition("*", "FullStop", "Holding") -- Hold position. - self:AddTransition("*", "Cruise", "Cruising") -- Hold position. + self:AddTransition("*", "Cruise", "Cruising") -- Cruise along the given route of waypoints. + + self:AddTransition("*", "Rearm", "Rearming") -- Group is send to a coordinate and waits until ammo is refilled. + self:AddTransition("Rearming", "Rearmed", "Cruising") -- Group was rearmed. self:AddTransition("*", "Detour", "OnDetour") -- Make a detour to a coordinate and resume route afterwards. self:AddTransition("OnDetour", "DetourReached", "Cruising") -- Group reached the detour coordinate. @@ -224,11 +238,18 @@ end --- Check if the group is currently on a detour. -- @param #ARMYGROUP self --- @return #boolean If true, group is on a detour +-- @return #boolean If true, group is on a detour. function ARMYGROUP:IsOnDetour() return self:Is("OnDetour") end +--- Check if the group is currently rearming. +-- @param #ARMYGROUP self +-- @return #boolean If true, group is rearming. +function ARMYGROUP:IsRearming() + return self:Is("Rearming") +end + ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Status ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -508,6 +529,26 @@ function ARMYGROUP:onafterDetour(From, Event, To, Coordinate, Speed, Formation, end +--- On after "Rearm" event. +-- @param #ARMYGROUP self +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +-- @param Core.Point#COORDINATE Coordinate Coordinate where to rearm. +-- @param #number Formation Formation of the group. +function ARMYGROUP:onafterRearm(From, Event, To, Coordinate, Formation) + + -- ID of current waypoint. + local uid=self:GetWaypointCurrent().uid + + -- Add waypoint after current. + local wp=self:AddWaypoint(Coordinate, nil, uid, Formation, true) + + -- Set if we want to resume route after reaching the detour waypoint. + wp.detour=0 + +end + --- On after "DetourReached" event. -- @param #ARMYGROUP self -- @param #string From From state. @@ -549,7 +590,7 @@ function ARMYGROUP:onafterCruise(From, Event, To, Speed, Formation) end ---- On after Start event. Starts the ARMYGROUP FSM and event handlers. +--- On after "Stop" event. -- @param #ARMYGROUP self -- @param #string From From state. -- @param #string Event Event. diff --git a/Moose Development/Moose/Ops/Auftrag.lua b/Moose Development/Moose/Ops/Auftrag.lua index 002509c7d..b6c030455 100644 --- a/Moose Development/Moose/Ops/Auftrag.lua +++ b/Moose Development/Moose/Ops/Auftrag.lua @@ -7,7 +7,7 @@ -- * Set mission start/stop times. -- * Set mission priority and urgency (can cancel running missions). -- * Specific mission options for ROE, ROT, formation, etc. --- * Interface to FLIGHTGROUP, AIRWING and WINGCOMMANDER classes. +-- * Interface to FLIGHTGROUP, NAVYGROUP, ARMYGROUP, AIRWING, WINGCOMMANDER and CHIEF classes. -- * FSM events when a mission is done, successful or failed. -- -- === diff --git a/Moose Development/Moose/Ops/FlightGroup.lua b/Moose Development/Moose/Ops/FlightGroup.lua index 81a8f4bf6..f7d394d96 100644 --- a/Moose Development/Moose/Ops/FlightGroup.lua +++ b/Moose Development/Moose/Ops/FlightGroup.lua @@ -50,7 +50,7 @@ -- -- @extends Ops.OpsGroup#OPSGROUP ---- *To invent an airplane is nothing. To build one is something. To fly is everything.* -- Otto Lilienthal +--- *To invent an airplane is nothing; to build one is something; to fly is everything.* -- Otto Lilienthal -- -- === -- @@ -210,7 +210,7 @@ FLIGHTGROUP.version="0.6.0" --- Create a new FLIGHTGROUP object and start the FSM. -- @param #FLIGHTGROUP self --- @param Wrapper.Group#GROUP group The group object. Can also be given as #string with the name of the group. +-- @param Wrapper.Group#GROUP Group The group object. Can also be given by its group name as `#string`. -- @return #FLIGHTGROUP self function FLIGHTGROUP:New(group) diff --git a/Moose Development/Moose/Ops/NavyGroup.lua b/Moose Development/Moose/Ops/NavyGroup.lua index ceb9b3f55..e180ae53f 100644 --- a/Moose Development/Moose/Ops/NavyGroup.lua +++ b/Moose Development/Moose/Ops/NavyGroup.lua @@ -5,8 +5,18 @@ -- * Dynamically add and remove waypoints. -- * Let the group steam into the wind. -- * Command a full stop. +-- * Automatic pathfinding, e.g. around islands. +-- * Collision warning, if group is heading towards a land mass. -- * Let a submarine dive and surface. --- +-- * Sophisticated task queueing system. +-- * Compatible with AUFTRAG class. +-- * Convenient checks when the group enters or leaves a zone. +-- * Many additional events that the mission designer can hook into. +-- +-- **Example Missions:** +-- +-- Demo missions can be found on [github](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/OPS%20-%20Navygroup) +-- -- === -- -- ### Author: **funkyfranky** @@ -19,6 +29,7 @@ -- @field #boolean turning If true, group is currently turning. -- @field #NAVYGROUP.IntoWind intowind Into wind info. -- @field #table Qintowind Queue of "into wind" turns. +-- @field #number intowindcounter Counter of into wind IDs. -- @field #number depth Ordered depth in meters. -- @field #boolean collisionwarning If true, collition warning. -- @field #boolean pathfindingOn If true, enable pathfining. @@ -74,7 +85,9 @@ NAVYGROUP.version="0.5.0" -- TODO list ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- TODO: Collision warning. +-- TODO: Extend, shorten turn into wind windows +-- TODO: Skipper menu. +-- DONE: Collision warning. -- DONE: Detour, add temporary waypoint and resume route. -- DONE: Stop and resume route. -- DONE: Add waypoints. @@ -109,6 +122,7 @@ function NAVYGROUP:New(GroupName) self:AddTransition("*", "Cruise", "Cruising") -- Hold position. self:AddTransition("*", "TurnIntoWind", "IntoWind") -- Command the group to turn into the wind. + self:AddTransition("IntoWind", "TurnedIntoWind", "IntoWind") -- Group turned into wind. self:AddTransition("IntoWind", "TurnIntoWindStop", "IntoWind") -- Stop a turn into wind. self:AddTransition("IntoWind", "TurnIntoWindOver", "Cruising") -- Turn into wind is over. @@ -355,7 +369,8 @@ end function NAVYGROUP:RemoveTurnIntoWind(IntoWindData) -- Check if this is a window currently open. - if self.intowind and self.intowind.id==IntoWindData.Id then + if self.intowind and self.intowind.Id==IntoWindData.Id then + --env.info("FF stop in remove") self:TurnIntoWindStop() return end @@ -363,6 +378,7 @@ function NAVYGROUP:RemoveTurnIntoWind(IntoWindData) for i,_tiw in pairs(self.Qintowind) do local tiw=_tiw --#NAVYGROUP.IntoWind if tiw.Id==IntoWindData.Id then + --env.info("FF removing window "..tiw.Id) table.remove(self.Qintowind, i) break end @@ -543,6 +559,37 @@ function NAVYGROUP:onafterStatus(From, Event, To) end + --- + -- Recovery Windows + --- + + if self.verbose>=2 then + + -- Debug output: + local text=string.format(self.lid.."Turn into wind time windows:") + + -- Handle case with no recoveries. + if #self.Qintowind==0 then + text=text.." none!" + end + + -- Loop over all slots. + for i,_recovery in pairs(self.Qintowind) do + local recovery=_recovery --#NAVYGROUP.IntoWind + + -- Get start/stop clock strings. + local Cstart=UTILS.SecondsToClock(recovery.Tstart) + local Cstop=UTILS.SecondsToClock(recovery.Tstop) + + -- Debug text. + text=text..string.format("\n[%d] ID=%d Start=%s Stop=%s Open=%s Over=%s", i, recovery.Id, Cstart, Cstop, tostring(recovery.Open), tostring(recovery.Over)) + end + + -- Debug output. + self:I(self.lid..text) + + end + --- -- Tasks & Missions @@ -794,7 +841,7 @@ end -- @param #string Event Event. -- @param #string To To state. function NAVYGROUP:onafterTurnIntoWindStop(From, Event, To) - self:TurnIntoWindOver() + self:TurnIntoWindOver(self.intowind) end --- On after "TurnIntoWindOver" event. @@ -802,28 +849,54 @@ end -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. -function NAVYGROUP:onafterTurnIntoWindOver(From, Event, To) +-- @param #NAVYGROUP.IntoWind IntoWindData Data table. +function NAVYGROUP:onafterTurnIntoWindOver(From, Event, To, IntoWindData) - -- Debug message. - self:T2(self.lid.."Turn Into Wind Over!") + if IntoWindData and self.intowind and IntoWindData.Id==self.intowind.Id then - self.intowind.Over=true - self.intowind.Open=false + -- Debug message. + self:T2(self.lid.."Turn Into Wind Over!") - -- Remove additional waypoint. - self:RemoveWaypointByID(self.intowind.waypoint.uid) + -- Window over and not open anymore. + self.intowind.Over=true + self.intowind.Open=false + + -- Remove additional waypoint. + self:RemoveWaypointByID(self.intowind.waypoint.uid) + + if self.intowind.Uturn then + + --- + -- U-turn ==> Go to coordinate where we left the route. + --- + + -- Detour to where we left the route. + self:I(self.lid.."FF Turn Into Wind Over ==> Uturn!") + self:Detour(self.intowind.Coordinate, self:GetSpeedCruise(), 0, true) + + else + + --- + -- Go directly to next waypoint. + --- + + -- Next waypoint index and speed. + local indx=self:GetWaypointIndexNext() + local speed=self:GetWaypointSpeed(indx) + + -- Update route. + self:I(self.lid..string.format("FF Turn Into Wind Over ==> Next WP Index=%d at %.1f knots via update route!", indx, speed)) + self:__UpdateRoute(-1, indx, speed) + + end + + -- Set current window to nil. + self.intowind=nil + + -- Remove window from queue. + self:RemoveTurnIntoWind(IntoWindData) - if self.intowind.Uturn then - self:T(self.lid.."Turn Into Wind Over ==> Uturn!") - self:Detour(self.intowind.Coordinate, self:GetSpeedCruise(), 0, true) - else - self:T(self.lid.."FF Turn Into Wind Over ==> Next WP!") - local indx=self:GetWaypointIndexNext() - local speed=self:GetWaypointSpeed(indx) - self:__UpdateRoute(-1, indx, speed) end - - self.intowind=nil end @@ -911,6 +984,11 @@ end function NAVYGROUP:onafterTurningStopped(From, Event, To) self.turning=false self.collisionwarning=false + + if self:IsSteamingIntoWind() then + self:TurnedIntoWind() + end + end --- On after "CollisionWarning" event. @@ -1343,66 +1421,60 @@ function NAVYGROUP:_CheckTurnsIntoWind() -- Get current abs time. local time=timer.getAbsTime() - local Cnow=UTILS.SecondsToClock(time) - -- Debug output: - local text=string.format(self.lid.."Recovery time windows:") + if self.intowind then - -- Handle case with no recoveries. - if #self.Qintowind==0 then - text=text.." none!" - end + -- Check if time is over. + if time>=self.intowind.Tstop then + self:TurnIntoWindOver(self.intowind) + end + + else + + -- Get next window. + local IntoWind=self:GetNextTurnIntoWind() - -- Sort windows wrt to start time. - table.sort(self.Qintowind, function(a, b) return a.Tstart=recovery.Tstart and time=self.intowind.Tstop then - self:TurnIntoWindOver() - end - end end ---- Check queued turns into wind. +--- Get the next turn into wind window, which is not yet running. -- @param #NAVYGROUP self --- @return #NAVYGROUP.IntoWind Next into wind data. -function NAVYGROUP:GetNextTurnIntoWind() +-- @return #NAVYGROUP.IntoWind Next into wind data. Could be `nil` if there is not next window. +function NAVYGROUP:GetTurnIntoWindNext() - -- Loop over all windows. - for _,_recovery in pairs(self.Qintowind) do - local recovery=_recovery --#NAVYGROUP.IntoWind - + if #self.Qintowind>0 then + + -- Get current abs time. + local time=timer.getAbsTime() + + -- Sort windows wrt to start time. + table.sort(self.Qintowind, function(a, b) return a.Tstart=recovery.Tstart and time Date: Sun, 18 Oct 2020 00:02:48 +0200 Subject: [PATCH 03/10] Ops - Laser --- Moose Development/Moose/DCS.lua | 59 ++++ Moose Development/Moose/Ops/ArmyGroup.lua | 186 ++++++---- Moose Development/Moose/Ops/Auftrag.lua | 24 +- Moose Development/Moose/Ops/FlightGroup.lua | 28 +- Moose Development/Moose/Ops/NavyGroup.lua | 42 ++- Moose Development/Moose/Ops/OpsGroup.lua | 367 ++++++++++++++++++-- 6 files changed, 596 insertions(+), 110 deletions(-) diff --git a/Moose Development/Moose/DCS.lua b/Moose Development/Moose/DCS.lua index 15c73c29a..2d190d215 100644 --- a/Moose Development/Moose/DCS.lua +++ b/Moose Development/Moose/DCS.lua @@ -736,7 +736,66 @@ do -- Airbase end -- Airbase +do -- Spot + --- [DCS Class Spot](https://wiki.hoggitworld.com/view/DCS_Class_Spot) + -- Represents a spot from laser or IR-pointer. + -- @type Spot + -- @field #Spot.Category Category enum that stores spot categories. + + --- Enum that stores spot categories. + -- @type Spot.Category + -- @field #string INFRA_RED + -- @field #string LASER + + + --- Creates a laser ray emanating from the given object to a point in 3d space. + -- @function [parent=#Spot] createLaser + -- @param DCS#Object Source The source object of the laser. + -- @param DCS#Vec3 LocalRef An optional 3D offset for the source. + -- @param DCS#Vec3 Vec3 Target coordinate where the ray is pointing at. + -- @param #number LaserCode Any 4 digit number between 1111 and 1788. + -- @return #Spot + + --- Creates an infrared ray emanating from the given object to a point in 3d space. Can be seen with night vision goggles. + -- @function [parent=#Spot] createInfraRed + -- @param DCS#Object Source Source position of the IR ray. + -- @param DCS#Vec3 LocalRef An optional 3D offset for the source. + -- @param DCS#Vec3 Vec3 Target coordinate where the ray is pointing at. + -- @return #Spot + + --- Returns a vec3 table of the x, y, and z coordinates for the position of the given object in 3D space. Coordinates are dependent on the position of the maps origin. + -- @function [parent=#Spot] getPoint + -- @param #Spot self + -- @return DCS#Vec3 Point in 3D, where the beam is pointing at. + + --- Sets the destination point from which the source of the spot is drawn toward. + -- @function [parent=#Spot] setPoint + -- @param #Spot self + -- @param DCS#Vec3 Vec3 Point in 3D, where the beam is pointing at. + + --- Returns the number that is used to define the laser code for which laser designation can track. + -- @function [parent=#Spot] getCode + -- @param #Spot self + -- @return #number Code The laser code used. + + --- Sets the number that is used to define the laser code for which laser designation can track. + -- @function [parent=#Spot] setCode + -- @param #Spot self + -- @param #number Code The laser code. Default value is 1688. + + --- Destroys the spot. + -- @function [parent=#Spot] destroy + -- @param #Spot self + + --- Gets the category of the spot (laser or IR). + -- @function [parent=#Spot] getCategory + -- @param #Spot self + -- @return #string Category. + + Spot = {} --#Spot + +end -- Spot do -- Controller --- Controller is an object that performs A.I.-routines. Other words controller is an instance of A.I.. Controller stores current main task, active enroute tasks and behavior options. Controller performs commands. Please, read DCS A-10C GUI Manual EN.pdf chapter "Task Planning for Unit Groups", page 91 to understand A.I. system of DCS:A-10C. diff --git a/Moose Development/Moose/Ops/ArmyGroup.lua b/Moose Development/Moose/Ops/ArmyGroup.lua index f32dca574..a366d7a79 100644 --- a/Moose Development/Moose/Ops/ArmyGroup.lua +++ b/Moose Development/Moose/Ops/ArmyGroup.lua @@ -1,21 +1,29 @@ --- **Ops** - Enhanced Ground Group. -- --- **Main Features:** +-- ## Main Features: -- --- * Dynamically add and remove waypoints. --- * Convenient checks when the group enters or leaves a zone. --- * Sophisticated task queueing system. --- * Compatible with AUFTRAG class. --- * Easy change of ROE, alarm state, formation and other settings. --- * Many additional events that the mission designer can hook into. +-- * Patrol waypoints *ad infinitum* +-- * Easy change of ROE and alarm state, formation and other settings +-- * Dynamically add and remove waypoints +-- * Sophisticated task queueing system (know when DCS tasks start and end) +-- * Convenient checks when the group enters or leaves a zone +-- * Detection events for new, known and lost units +-- * Simple LASER and IR-pointer setup +-- * Compatible with AUFTRAG class +-- * Many additional events that the mission designer can hook into -- --- **Example Missions:** +-- === +-- +-- ## Example Missions: -- -- Demo missions can be found on [github](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/OPS%20-%20Armygroup). -- -- === -- -- ### Author: **funkyfranky** +-- +-- == +-- -- @module Ops.ArmyGroup -- @image OPS_ArmyGroup.png @@ -46,7 +54,12 @@ ARMYGROUP = { --- Army group element. -- @type ARMYGROUP.Element -- @field #string name Name of the element, i.e. the unit. +-- @field Wrapper.Unit#UNIT unit The UNIT object. +-- @field #string status The element status. -- @field #string typename Type name. +-- @field #number length Length of element in meters. +-- @field #number width Width of element in meters. +-- @field #number height Height of element in meters. --- Army Group version. -- @field #string version @@ -85,10 +98,7 @@ function ARMYGROUP:New(Group) -- From State --> Event --> To State self:AddTransition("*", "FullStop", "Holding") -- Hold position. self:AddTransition("*", "Cruise", "Cruising") -- Cruise along the given route of waypoints. - - self:AddTransition("*", "Rearm", "Rearming") -- Group is send to a coordinate and waits until ammo is refilled. - self:AddTransition("Rearming", "Rearmed", "Cruising") -- Group was rearmed. - + self:AddTransition("*", "Detour", "OnDetour") -- Make a detour to a coordinate and resume route afterwards. self:AddTransition("OnDetour", "DetourReached", "Cruising") -- Group reached the detour coordinate. @@ -178,6 +188,8 @@ end -- @return Ops.OpsGroup#OPSGROUP.Task The task table. function ARMYGROUP:AddTaskFireAtPoint(Coordinate, Clock, Radius, Nshots, WeaponType, Prio) + Coordinate=self:_CoordinateFromObject(Coordinate) + local DCStask=CONTROLLABLE.TaskFireAtPoint(nil, Coordinate:GetVec2(), Radius, Nshots, WeaponType) local task=self:AddTask(DCStask, Clock, nil, Prio) @@ -196,6 +208,8 @@ end -- @return Ops.OpsGroup#OPSGROUP.Task The task table. function ARMYGROUP:AddTaskWaypointFireAtPoint(Coordinate, Waypoint, Radius, Nshots, WeaponType, Prio) + Coordinate=self:_CoordinateFromObject(Coordinate) + Waypoint=Waypoint or self:GetWaypointNext() local DCStask=CONTROLLABLE.TaskFireAtPoint(nil, Coordinate:GetVec2(), Radius, Nshots, WeaponType) @@ -243,13 +257,6 @@ function ARMYGROUP:IsOnDetour() return self:Is("OnDetour") end ---- Check if the group is currently rearming. --- @param #ARMYGROUP self --- @return #boolean If true, group is rearming. -function ARMYGROUP:IsRearming() - return self:Is("Rearming") -end - ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Status ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -286,12 +293,24 @@ function ARMYGROUP:onafterStatus(From, Event, To) if self.detectionOn then self:_CheckDetectedUnits() end - - -- Update position etc. - self:_UpdatePosition() - -- Check if group got stuck. - self:_CheckStuck() + if self:IsRearming() then + + local rearmed=self:_CheckAmmoFull() + + if rearmed then + self:Rearmed() + end + + else + + -- Update position etc. + self:_UpdatePosition() + + -- Check if group got stuck. + self:_CheckStuck() + + end if self.verbose>=1 then @@ -303,7 +322,7 @@ function ARMYGROUP:onafterStatus(From, Event, To) local alarm=self:GetAlarmstate() local speed=UTILS.MpsToKnots(self.velocity) local speedEx=UTILS.MpsToKnots(self:GetExpectedSpeed()) - local formation=self.option.Formation + local formation=self.option.Formation or "unknown" -- Info text. local text=string.format("%s: Wp=%d/%d-->%d Speed=%.1f (%d) Heading=%03d ROE=%d Alarm=%d Formation=%s Tasks=%d Missions=%d", @@ -382,7 +401,11 @@ function ARMYGROUP:onafterSpawned(From, Event, To) end -- Update route. - self:Cruise(nil, self.option.Formation or self.optionDefault.Formation) + if #self.waypoints>1 then + self:Cruise(nil, self.option.Formation or self.optionDefault.Formation) + else + self:FullStop() + end end @@ -549,6 +572,35 @@ function ARMYGROUP:onafterRearm(From, Event, To, Coordinate, Formation) end +--- On after "Rearming" event. +-- @param #ARMYGROUP self +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +function ARMYGROUP:onafterRearming(From, Event, To) + + -- Get current position. + local pos=self:GetCoordinate() + + -- Create a new waypoint. + local wp=pos:WaypointGround(0) + + -- Create new route consisting of only this position ==> Stop! + self:Route({wp}) + +end + +--- On after "Rearmed" event. +-- @param #ARMYGROUP self +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +function ARMYGROUP:onafterRearmed(From, Event, To) + + self:_CheckGroupDone(1) + +end + --- On after "DetourReached" event. -- @param #ARMYGROUP self -- @param #string From From state. @@ -709,6 +761,8 @@ end -- @return Ops.OpsGroup#OPSGROUP.Waypoint Waypoint table. function ARMYGROUP:AddWaypoint(Coordinate, Speed, AfterWaypointWithID, Formation, Updateroute) + local coordinate=self:_CoordinateFromObject(Coordinate) + -- Set waypoint index. local wpnumber=self:GetWaypointIndexAfterID(AfterWaypointWithID) @@ -721,7 +775,7 @@ function ARMYGROUP:AddWaypoint(Coordinate, Speed, AfterWaypointWithID, Formation Speed=Speed or self:GetSpeedCruise() -- Create a Naval waypoint. - local wp=Coordinate:WaypointGround(UTILS.KnotsToKmph(Speed), Formation) + local wp=coordinate:WaypointGround(UTILS.KnotsToKmph(Speed), Formation) -- Create waypoint data table. local waypoint=self:_CreateWaypoint(wp) @@ -730,9 +784,9 @@ function ARMYGROUP:AddWaypoint(Coordinate, Speed, AfterWaypointWithID, Formation self:_AddWaypoint(waypoint, wpnumber) -- Get closest point to road. - waypoint.roadcoord=Coordinate:GetClosestPointToRoad(false) + waypoint.roadcoord=coordinate:GetClosestPointToRoad(false) if waypoint.roadcoord then - waypoint.roaddist=Coordinate:Get2DDistance(waypoint.roadcoord) + waypoint.roaddist=coordinate:Get2DDistance(waypoint.roadcoord) else waypoint.roaddist=1000*1000 --1000 km. end @@ -805,51 +859,61 @@ function ARMYGROUP:_InitGroup() for _,_unit in pairs(units) do local unit=_unit --Wrapper.Unit#UNIT + + -- TODO: this is wrong when grouping is used! + local unittemplate=unit:GetTemplate() local element={} --#ARMYGROUP.Element element.name=unit:GetName() - element.typename=unit:GetTypeName() + element.unit=unit element.status=OPSGROUP.ElementStatus.INUTERO - element.unit=unit + element.typename=unit:GetTypeName() + element.skill=unittemplate.skill or "Unknown" + element.ai=true + element.category=element.unit:GetUnitCategory() + element.categoryname=element.unit:GetCategoryName() + element.size, element.length, element.height, element.width=unit:GetObjectSize() + element.ammo0=self:GetAmmoUnit(unit, false) + + -- Debug text. + local text=string.format("Adding element %s: status=%s, skill=%s, category=%s (%d), size: %.1f (L=%.1f H=%.1f W=%.1f)", + element.name, element.status, element.skill, element.categoryname, element.category, element.size, element.length, element.height, element.width) + self:I(self.lid..text) + + -- Add element to table. table.insert(self.elements, element) - self:GetAmmoUnit(unit, false) + -- Get Descriptors. + self.descriptors=self.descriptors or unit:GetDesc() - if unit:IsAlive() then + -- Set type name. + self.actype=self.actype or unit:GetTypeName() + + if unit:IsAlive() then + -- Trigger spawned event. self:ElementSpawned(element) end end - -- Get first unit. This is used to extract other parameters. - local unit=self.group:GetUnit(1) - - if unit then - - self.descriptors=unit:GetDesc() - - self.actype=unit:GetTypeName() - - -- Debug info. - if self.verbose>=1 then - local text=string.format("Initialized Army Group %s:\n", self.groupname) - text=text..string.format("Unit type = %s\n", self.actype) - text=text..string.format("Speed max = %.1f Knots\n", UTILS.KmphToKnots(self.speedMax)) - text=text..string.format("Speed cruise = %.1f Knots\n", UTILS.KmphToKnots(self.speedCruise)) - text=text..string.format("Elements = %d\n", #self.elements) - text=text..string.format("Waypoints = %d\n", #self.waypoints) - text=text..string.format("Radio = %.1f MHz %s %s\n", self.radio.Freq, UTILS.GetModulationName(self.radio.Modu), tostring(self.radio.On)) - text=text..string.format("Ammo = %d (G=%d/R=%d/M=%d)\n", self.ammo.Total, self.ammo.Guns, self.ammo.Rockets, self.ammo.Missiles) - text=text..string.format("FSM state = %s\n", self:GetState()) - text=text..string.format("Is alive = %s\n", tostring(self:IsAlive())) - text=text..string.format("LateActivate = %s\n", tostring(self:IsLateActivated())) - self:I(self.lid..text) - end - - -- Init done. - self.groupinitialized=true - + -- Debug info. + if self.verbose>=1 then + local text=string.format("Initialized Army Group %s:\n", self.groupname) + text=text..string.format("Unit type = %s\n", self.actype) + text=text..string.format("Speed max = %.1f Knots\n", UTILS.KmphToKnots(self.speedMax)) + text=text..string.format("Speed cruise = %.1f Knots\n", UTILS.KmphToKnots(self.speedCruise)) + text=text..string.format("Elements = %d\n", #self.elements) + text=text..string.format("Waypoints = %d\n", #self.waypoints) + text=text..string.format("Radio = %.1f MHz %s %s\n", self.radio.Freq, UTILS.GetModulationName(self.radio.Modu), tostring(self.radio.On)) + text=text..string.format("Ammo = %d (G=%d/R=%d/M=%d)\n", self.ammo.Total, self.ammo.Guns, self.ammo.Rockets, self.ammo.Missiles) + text=text..string.format("FSM state = %s\n", self:GetState()) + text=text..string.format("Is alive = %s\n", tostring(self:IsAlive())) + text=text..string.format("LateActivate = %s\n", tostring(self:IsLateActivated())) + self:I(self.lid..text) end + + -- Init done. + self.groupinitialized=true return self end diff --git a/Moose Development/Moose/Ops/Auftrag.lua b/Moose Development/Moose/Ops/Auftrag.lua index b6c030455..54d186a09 100644 --- a/Moose Development/Moose/Ops/Auftrag.lua +++ b/Moose Development/Moose/Ops/Auftrag.lua @@ -1,18 +1,26 @@ --- **Ops** - Auftrag (mission) for Ops. -- --- **Main Features:** +-- ## Main Features: -- --- * Simplifies defining and executing DCS tasks. --- * Additional useful events. --- * Set mission start/stop times. --- * Set mission priority and urgency (can cancel running missions). +-- * Simplifies defining and executing DCS tasks +-- * Additional useful events +-- * Set mission start/stop times +-- * Set mission priority and urgency (can cancel running missions) -- * Specific mission options for ROE, ROT, formation, etc. --- * Interface to FLIGHTGROUP, NAVYGROUP, ARMYGROUP, AIRWING, WINGCOMMANDER and CHIEF classes. --- * FSM events when a mission is done, successful or failed. --- +-- * Compatible with FLIGHTGROUP, NAVYGROUP, ARMYGROUP, AIRWING, WINGCOMMANDER and CHIEF classes +-- * FSM events when a mission is done, successful or failed +-- -- === -- +-- ## 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 diff --git a/Moose Development/Moose/Ops/FlightGroup.lua b/Moose Development/Moose/Ops/FlightGroup.lua index f7d394d96..dc73d37e2 100644 --- a/Moose Development/Moose/Ops/FlightGroup.lua +++ b/Moose Development/Moose/Ops/FlightGroup.lua @@ -1,16 +1,30 @@ --- **Ops** - Enhanced Airborne Group. -- --- **Main Features:** +-- ## Main Features: -- --- * Monitor flight status of elements and/or the entire group. --- * Monitor fuel and ammo status. --- * Conveniently set radio freqencies, TACAN, ROE etc. --- * Sophisticated task queueing system. --- * Many additional events for each element and the whole group. +-- * Monitor flight status of elements and/or the entire group +-- * Monitor fuel and ammo status +-- * Conveniently set radio freqencies, TACAN, ROE etc +-- * Order helos to land at specifc coordinates +-- * Dynamically add and remove waypoints +-- * Sophisticated task queueing system (know when DCS tasks start and end) +-- * Convenient checks when the group enters or leaves a zone +-- * Detection events for new, known and lost units +-- * Simple LASER and IR-pointer setup +-- * Compatible with AUFTRAG class +-- * Many additional events that the mission designer can hook into -- -- === -- +-- ## Example Missions: +-- +-- Demo missions can be found on [github](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/OPS%20-%20Flightgroup). +-- +-- === +-- -- ### Author: **funkyfranky** +-- +-- === -- @module Ops.FlightGroup -- @image OPS_FlightGroup.png @@ -186,6 +200,7 @@ FLIGHTGROUP.version="0.6.0" -- TODO list ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- TODO: VTOL aircraft. -- TODO: Use new UnitLost event instead of crash/dead. -- TODO: Options EPLRS, Afterburner restrict etc. -- DONE: Add TACAN beacon. @@ -2729,6 +2744,7 @@ function FLIGHTGROUP:AddElementByName(unitname) element.ai=true end + -- Debug text. local text=string.format("Adding element %s: status=%s, skill=%s, modex=%s, fuelmass=%.1f (%d), category=%d, categoryname=%s, callsign=%s, ai=%s", element.name, element.status, element.skill, element.modex, element.fuelmass, element.fuelrel*100, element.category, element.categoryname, element.callsign, tostring(element.ai)) self:T(self.lid..text) diff --git a/Moose Development/Moose/Ops/NavyGroup.lua b/Moose Development/Moose/Ops/NavyGroup.lua index e180ae53f..dd0fb65a9 100644 --- a/Moose Development/Moose/Ops/NavyGroup.lua +++ b/Moose Development/Moose/Ops/NavyGroup.lua @@ -1,25 +1,33 @@ --- **Ops** - Enhanced Naval Group. -- --- **Main Features:** +-- ## Main Features: -- --- * Dynamically add and remove waypoints. --- * Let the group steam into the wind. --- * Command a full stop. --- * Automatic pathfinding, e.g. around islands. --- * Collision warning, if group is heading towards a land mass. --- * Let a submarine dive and surface. --- * Sophisticated task queueing system. --- * Compatible with AUFTRAG class. --- * Convenient checks when the group enters or leaves a zone. --- * Many additional events that the mission designer can hook into. +-- * Let the group steam into the wind +-- * Command a full stop +-- * Patrol waypoints *ad infinitum* +-- * Collision warning, if group is heading towards a land mass +-- * Automatic pathfinding, e.g. around islands +-- * Let a submarine dive and surface +-- * Manage TACAN and ICLS beacons +-- * Dynamically add and remove waypoints +-- * Sophisticated task queueing system (know when DCS tasks start and end) +-- * Convenient checks when the group enters or leaves a zone +-- * Detection events for new, known and lost units +-- * Simple LASER and IR-pointer setup +-- * Compatible with AUFTRAG class +-- * Many additional events that the mission designer can hook into -- --- **Example Missions:** +-- === +-- +-- ## Example Missions: -- -- Demo missions can be found on [github](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/OPS%20-%20Navygroup) -- -- === -- -- ### Author: **funkyfranky** +-- +-- === -- @module Ops.NavyGroup -- @image OPS_NavyGroup.png @@ -655,7 +663,11 @@ function NAVYGROUP:onafterSpawned(From, Event, To) end -- Update route. - self:Cruise() + if #self.waypoints>1 then + self:Cruise() + else + self:FullStop() + end end @@ -1124,10 +1136,10 @@ function NAVYGROUP:AddWaypoint(Coordinate, Speed, AfterWaypointWithID, Depth, Up -- Check if a coordinate was given or at least a positionable. if not Coordinate:IsInstanceOf("COORDINATE") then if Coordinate:IsInstanceOf("POSITIONABLE") or Coordinate:IsInstanceOf("ZONE_BASE") then - self:T(self.lid.."WARNING: Coordinate is not a COORDINATE but a POSITIONABLE. Trying to get coordinate") + self:T(self.lid.."WARNING: Coordinate is not a COORDINATE but a POSITIONABLE or ZONE. Trying to get coordinate") Coordinate=Coordinate:GetCoordinate() else - self:E(self.lid.."ERROR: Coordinate is neither a COORDINATE nor any POSITIONABLE!") + self:E(self.lid.."ERROR: Coordinate is neither a COORDINATE nor any POSITIONABLE or ZONE!") return nil end end diff --git a/Moose Development/Moose/Ops/OpsGroup.lua b/Moose Development/Moose/Ops/OpsGroup.lua index 4b3c44d41..7a9828e81 100644 --- a/Moose Development/Moose/Ops/OpsGroup.lua +++ b/Moose Development/Moose/Ops/OpsGroup.lua @@ -1,10 +1,12 @@ ---- **Ops** - Generic group enhancement functions. +--- **Ops** - Generic group enhancement. -- --- This class is **not** meant to be used itself by the end user. +-- This class is **not** meant to be used itself by the end user. It contains common functionalities of derived classes for air, ground and sea. -- -- === -- -- ### Author: **funkyfranky** +-- +-- === -- @module Ops.OpsGroup -- @image OPS_OpsGroup.png @@ -83,6 +85,8 @@ -- @field #OPSGROUP.Callsign callsign Current callsign settings. -- @field #OPSGROUP.Callsign callsignDefault Default callsign settings. -- +-- @field #OPSGROUP.Spot spot Laser and IR spot. +-- -- @extends Core.Fsm#FSM --- *A small group of determined and like-minded people can change the course of history.* --- Mahatma Gandhi @@ -136,6 +140,17 @@ OPSGROUP = { Ndestroyed = 0, } + +--- OPS group element. +-- @type OPSGROUP.Element +-- @field #string name Name of the element, i.e. the unit. +-- @field Wrapper.Unit#UNIT unit The UNIT object. +-- @field #string status The element status. +-- @field #string typename Type name. +-- @field #number length Length of element in meters. +-- @field #number width Width of element in meters. +-- @field #number height Height of element in meters. + --- Status of group element. -- @type OPSGROUP.ElementStatus -- @field #string INUTERO Element was not spawned yet or its status is unknown so far. @@ -236,7 +251,6 @@ OPSGROUP.TaskType={ -- @field #boolean EPLRS data link. -- @field #boolean Disperse Disperse under fire. - --- Weapon range data. -- @type OPSGROUP.WeaponData -- @field #number BitType Type of weapon. @@ -244,6 +258,16 @@ OPSGROUP.TaskType={ -- @field #number RangeMax Max range in meters. -- @field #number ReloadTime Time to reload in seconds. +--- Laser and IR spot data. +-- @type OPSGROUP.Spot +-- @field DCS#Spot Laser Laser spot. +-- @field DCS#Spot IR Infra-red spot. +-- @field #number Code Laser code. +-- @field Wrapper.Positionable#POSITIONABLE Target The target. +-- @field Core.Point#COORDINATE Coordinate where the spot is pointing. +-- @field #boolean On If true, the laser is on. +-- @field Core.Timer#TIMER timer Spot timer. + --- Ammo data. -- @type OPSGROUP.Ammo -- @field #number Total Total amount of ammo. @@ -281,13 +305,14 @@ OPSGROUP.TaskType={ --- NavyGroup version. -- @field #string version -OPSGROUP.version="0.5.0" +OPSGROUP.version="0.6.0" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- TODO: Implement common functions. +-- TODO: Suppression of fire. +-- TODO: Add pseudo function. ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Constructor @@ -320,6 +345,12 @@ function OPSGROUP:New(Group) -- Init inzone set. self.inzones=SET_ZONE:New() + -- Laser. + self.spot={} + self.spot.On=false + self.spot.timer=TIMER:New(OPSGROUP._UpdateLaser, self) + self:SetLaser() + -- Init task counter. self.taskcurrent=0 self.taskcounter=0 @@ -356,6 +387,14 @@ function OPSGROUP:New(Group) self:AddTransition("*", "EnterZone", "*") -- Group entered a certain zone. self:AddTransition("*", "LeaveZone", "*") -- Group leaves a certain zone. + self:AddTransition("*", "Rearm", "Rearming") -- Group is send to a coordinate and waits until ammo is refilled. + self:AddTransition("Rearming", "Rearming", "Rearming") -- Group has arrived at the rearming coodinate and is waiting to be fully rearmed. + self:AddTransition("Rearming", "Rearmed", "Cruising") -- Group was rearmed. + + self:AddTransition("*", "LaserOn", "*") -- Turn laser on. + self:AddTransition("*", "LaserOff", "*") -- Turn laser off. + self:AddTransition("*", "LaserCode", "*") -- Switch laser code. + self:AddTransition("*", "TaskExecute", "*") -- Group will execute a task. self:AddTransition("*", "TaskPause", "*") -- Pause current task. Not implemented yet! self:AddTransition("*", "TaskCancel", "*") -- Cancel current task. @@ -457,6 +496,19 @@ function OPSGROUP:SetDetection(Switch) 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 +-- @param #number Code Laser code. Default 1688. +-- @param #boolean IROff If true, then dont switch on the additional IR pointer. +-- @param #number UpdateTime Time interval in seconds the beam gets up for moving targets. Default every 0.5 sec. +-- @return #OPSGROUP self +function OPSGROUP:SetLaser(Code, IROff, UpdateTime) + self.spot.Code=Code or 1688 + self.spot.IRoff=IROff + return self +end + --- Define a SET of zones that trigger and event if the group enters or leaves any of the zones. -- @param #OPSGROUP self -- @param Core.Set#SET_ZONE CheckZonesSet Set of zones. @@ -502,9 +554,9 @@ function OPSGROUP:AddWeaponRange(RangeMin, RangeMax, BitType) return self end ---- +--- Get weapon data. -- @param #OPSGROUP self --- @param #number BitType +-- @param #number BitType Type of weapon. -- @return #OPSGROUP.WeaponData Weapon range data. function OPSGROUP:GetWeaponData(BitType) @@ -518,7 +570,6 @@ function OPSGROUP:GetWeaponData(BitType) end - --- Get set of detected units. -- @param #OPSGROUP self -- @return Core.Set#SET_UNIT Set of detected units. @@ -526,6 +577,41 @@ function OPSGROUP:GetDetectedUnits() return self.detectedunits end +--- Get highest detected threat. Detection must be turned on. The threat level is a number between 0 and 10, where 0 is the lowest, e.g. unarmed units. +-- @param #OPSGROUP self +-- @param #number ThreatLevelMin Only consider threats with level greater or equal to this number. Default 1 (so unarmed units wont be considered). +-- @param #number ThreatLevelMax Only consider threats with level smaller or queal to this number. Default 10. +-- @return Wrapper.Unit#UNIT Highest threat unit detected by the group or `nil` if no threat is currently detected. +-- @return #number Threat level. +function OPSGROUP:GetThreat(ThreatLevelMin, ThreatLevelMax) + + ThreatLevelMin=ThreatLevelMin or 1 + ThreatLevelMax=ThreatLevelMax or 10 + + local threat=nil --Wrapper.Unit#UNIT + local level=0 + for _,_unit in pairs(self.detectedunits:GetSet()) do + local unit=_unit --Wrapper.Unit#UNIT + + -- Get threatlevel of unit. + local threatlevel=unit:GetThreatLevel() + + -- Check if withing threasholds. + if threatlevel>=ThreatLevelMin and threatlevel<=ThreatLevelMax then + + if threatlevel1 then + + -- Store current position. + self.spot.vec3=vec3 + + -- Update beam coordinate. + self.spot.Coordinate:UpdateFromVec3(vec3) + + -- Set the new laser target point. + self.spot.Laser:setPoint(vec3) + self.spot.IR:setPoint(vec3) + + end + + else + + -- Switch laser off. + self:T(self.lid.."Target is not alive any more ==> switching LASER off") + self:LaserOff() + + end + + end + + end + +end + --- On after "ElementDestroyed" event. -- @param #OPSGROUP self @@ -2902,9 +3161,22 @@ end -- @param #OPSGROUP.Element Element The flight group element. function OPSGROUP:onafterElementDead(From, Event, To, Element) self:T(self.lid..string.format("Element dead %s", Element.name)) - + -- Set element status. self:_UpdateStatus(Element, OPSGROUP.ElementStatus.DEAD) + + -- Check if element was lasing and if so, switch to another unit alive to lase. + if self.spot.On and self.spot.element.name==Element.name then + + -- Switch laser off. + self:LaserOff() + + -- If there is another element alive, switch laser on again. + if self:GetNelements()>0 then + self:__LaserOn(-1, self.spot.Target or self.spot.Coordinate) + end + end + end --- On after "Dead" event. @@ -3172,10 +3444,19 @@ end -- @return #boolean If true, ammo is full. function OPSGROUP:_CheckAmmoFull() - for _,_ammo in pairs(self.ammo) do + -- Get current ammo. + local ammo=self:GetAmmoTot() + + for key,value in pairs(self.ammo) do + + if ammo[key] Date: Mon, 19 Oct 2020 23:33:39 +0200 Subject: [PATCH 04/10] Ops --- Moose Development/Moose/Core/Fsm.lua | 4 +- Moose Development/Moose/Ops/ArmyGroup.lua | 88 +++++++--- Moose Development/Moose/Ops/NavyGroup.lua | 81 ++++++---- Moose Development/Moose/Ops/OpsGroup.lua | 189 +++++++++++++++------- 4 files changed, 245 insertions(+), 117 deletions(-) diff --git a/Moose Development/Moose/Core/Fsm.lua b/Moose Development/Moose/Core/Fsm.lua index 80dfcb0c5..0b49c3449 100644 --- a/Moose Development/Moose/Core/Fsm.lua +++ b/Moose Development/Moose/Core/Fsm.lua @@ -913,7 +913,7 @@ do -- FSM --- Check if FSM is in state. -- @param #FSM self -- @param #string State State name. - -- @param #boolean If true, FSM is in this state. + -- @return #boolean If true, FSM is in this state. function FSM:Is( State ) return self.current == State end @@ -921,7 +921,7 @@ do -- FSM --- Check if FSM is in state. -- @param #FSM self -- @param #string State State name. - -- @param #boolean If true, FSM is in this state. + -- @return #boolean If true, FSM is in this state. function FSM:is(state) return self.current == state end diff --git a/Moose Development/Moose/Ops/ArmyGroup.lua b/Moose Development/Moose/Ops/ArmyGroup.lua index a366d7a79..df52c7cc8 100644 --- a/Moose Development/Moose/Ops/ArmyGroup.lua +++ b/Moose Development/Moose/Ops/ArmyGroup.lua @@ -101,6 +101,10 @@ function ARMYGROUP:New(Group) self:AddTransition("*", "Detour", "OnDetour") -- Make a detour to a coordinate and resume route afterwards. self:AddTransition("OnDetour", "DetourReached", "Cruising") -- Group reached the detour coordinate. + + self:AddTransition("*", "Rearm", "Rearm") -- Group is send to a coordinate and waits until ammo is refilled. + self:AddTransition("Rearm", "Rearming", "Rearming") -- Group has arrived at the rearming coodinate and is waiting to be fully rearmed. + self:AddTransition("Rearming", "Rearmed", "Cruising") -- Group was rearmed. ------------------------ --- Pseudo Functions --- @@ -294,23 +298,14 @@ function ARMYGROUP:onafterStatus(From, Event, To) self:_CheckDetectedUnits() end - if self:IsRearming() then - - local rearmed=self:_CheckAmmoFull() - - if rearmed then - self:Rearmed() - end - - else + -- Check ammo status. + self:_CheckAmmoStatus() - -- Update position etc. - self:_UpdatePosition() + -- Update position etc. + self:_UpdatePosition() - -- Check if group got stuck. - self:_CheckStuck() - - end + -- Check if group got stuck. + self:_CheckStuck() if self.verbose>=1 then @@ -325,8 +320,8 @@ function ARMYGROUP:onafterStatus(From, Event, To) local formation=self.option.Formation or "unknown" -- Info text. - local text=string.format("%s: Wp=%d/%d-->%d Speed=%.1f (%d) Heading=%03d ROE=%d Alarm=%d Formation=%s Tasks=%d Missions=%d", - fsmstate, self.currentwp, #self.waypoints, self:GetWaypointIndexNext(), speed, speedEx, self.heading, roe, alarm, formation, nTaskTot, nMissions) + local text=string.format("%s [ROE-AS=%d-%d T/M=%d/%d]: Wp=%d/%d-->%d (final %s), Speed=%.1f (%d), Heading=%03d", + fsmstate, roe, alarm, nTaskTot, nMissions, self.currentwp, #self.waypoints, self:GetWaypointIndexNext(), tostring(self.passedfinalwp), speed, speedEx, self.heading) self:I(self.lid..text) end @@ -398,6 +393,11 @@ function ARMYGROUP:onafterSpawned(From, Event, To) self:SetDefaultRadio(self.radio.Freq, self.radio.Modu, true) end + -- Formation + if not self.option.Formation then + self.option.Formation=self.optionDefault.Formation + end + end -- Update route. @@ -419,6 +419,10 @@ end -- @param #number Formation Formation of the group. function ARMYGROUP:onafterUpdateRoute(From, Event, To, n, Speed, Formation) + -- Debug info. + local text=string.format("Update route n=%s, Speed=%s, Formation=%s", tostring(n), tostring(Speed), tostring(Formation)) + self:T(self.lid..text) + -- Update route from this waypoint number onwards. n=n or self:GetWaypointIndexNext(self.adinfinitum) @@ -495,9 +499,6 @@ function ARMYGROUP:onafterUpdateRoute(From, Event, To, n, Speed, Formation) local wp=_wp local text=string.format("WP #%d UID=%d type=%s: Speed=%d m/s, alt=%d m, Action=%s", i, wp.uid and wp.uid or 0, wp.type, wp.speed, wp.alt, wp.action) self:T(text) - if false and wp.coordinate then - wp.coordinate:MarkToAll(text) - end end end @@ -523,6 +524,43 @@ function ARMYGROUP:onafterUpdateRoute(From, Event, To, n, Speed, Formation) end +--- On after "GotoWaypoint" event. Group will got to the given waypoint and execute its route from there. +-- @param #ARMYGROUP self +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +-- @param #number UID The goto waypoint unique ID. +-- @param #number Speed (Optional) Speed to waypoint in knots. +-- @param #number Formation (Optional) Formation to waypoint. +function ARMYGROUP:onafterGotoWaypoint(From, Event, To, UID, Speed, Formation) + + local n=self:GetWaypointIndex(UID) + + --env.info(string.format("FF AG Goto waypoint UID=%s Index=%s, Speed=%s, Formation=%s", tostring(UID), tostring(n), tostring(Speed), tostring(Formation))) + + if n then + + -- TODO: switch to re-enable waypoint tasks. + if false then + local tasks=self:GetTasksWaypoint(n) + + for _,_task in pairs(tasks) do + local task=_task --#OPSGROUP.Task + task.status=OPSGROUP.TaskStatus.SCHEDULED + end + + end + + -- Speed to waypoint. + Speed=Speed or self:GetSpeedToWaypoint(n) + + -- Update the route. + self:UpdateRoute(n, Speed, Formation) + + end + +end + --- On after "Detour" event. -- @param #ARMYGROUP self -- @param #string From From state. @@ -792,7 +830,7 @@ function ARMYGROUP:AddWaypoint(Coordinate, Speed, AfterWaypointWithID, Formation end -- Debug info. - self:I(self.lid..string.format("Adding waypoint UID=%d (index=%d), Speed=%.1f knots, Dist2Road=%d m, Action=%s", waypoint.uid, wpnumber, Speed, waypoint.roaddist, waypoint.action)) + self:T(self.lid..string.format("Adding waypoint UID=%d (index=%d), Speed=%.1f knots, Dist2Road=%d m, Action=%s", waypoint.uid, wpnumber, Speed, waypoint.roaddist, waypoint.action)) -- Update route. if Updateroute==nil or Updateroute==true then @@ -876,9 +914,11 @@ function ARMYGROUP:_InitGroup() element.ammo0=self:GetAmmoUnit(unit, false) -- Debug text. - local text=string.format("Adding element %s: status=%s, skill=%s, category=%s (%d), size: %.1f (L=%.1f H=%.1f W=%.1f)", - element.name, element.status, element.skill, element.categoryname, element.category, element.size, element.length, element.height, element.width) - self:I(self.lid..text) + if self.verbose>=2 then + local text=string.format("Adding element %s: status=%s, skill=%s, category=%s (%d), size: %.1f (L=%.1f H=%.1f W=%.1f)", + element.name, element.status, element.skill, element.categoryname, element.category, element.size, element.length, element.height, element.width) + self:I(self.lid..text) + end -- Add element to table. table.insert(self.elements, element) diff --git a/Moose Development/Moose/Ops/NavyGroup.lua b/Moose Development/Moose/Ops/NavyGroup.lua index dd0fb65a9..515aaf817 100644 --- a/Moose Development/Moose/Ops/NavyGroup.lua +++ b/Moose Development/Moose/Ops/NavyGroup.lua @@ -883,7 +883,7 @@ function NAVYGROUP:onafterTurnIntoWindOver(From, Event, To, IntoWindData) --- -- Detour to where we left the route. - self:I(self.lid.."FF Turn Into Wind Over ==> Uturn!") + self:T(self.lid.."FF Turn Into Wind Over ==> Uturn!") self:Detour(self.intowind.Coordinate, self:GetSpeedCruise(), 0, true) else @@ -897,7 +897,7 @@ function NAVYGROUP:onafterTurnIntoWindOver(From, Event, To, IntoWindData) local speed=self:GetWaypointSpeed(indx) -- Update route. - self:I(self.lid..string.format("FF Turn Into Wind Over ==> Next WP Index=%d at %.1f knots via update route!", indx, speed)) + self:T(self.lid..string.format("FF Turn Into Wind Over ==> Next WP Index=%d at %.1f knots via update route!", indx, speed)) self:__UpdateRoute(-1, indx, speed) end @@ -1238,51 +1238,64 @@ function NAVYGROUP:_InitGroup() for _,_unit in pairs(units) do local unit=_unit --Wrapper.Unit#UNIT + -- Get unit template. + local unittemplate=unit:GetTemplate() + local element={} --#NAVYGROUP.Element element.name=unit:GetName() - element.typename=unit:GetTypeName() - element.status=OPSGROUP.ElementStatus.INUTERO element.unit=unit + element.status=OPSGROUP.ElementStatus.INUTERO + element.typename=unit:GetTypeName() + element.skill=unittemplate.skill or "Unknown" + element.ai=true + element.category=element.unit:GetUnitCategory() + element.categoryname=element.unit:GetCategoryName() + element.size, element.length, element.height, element.width=unit:GetObjectSize() + element.ammo0=self:GetAmmoUnit(unit, false) + + -- Debug text. + if self.verbose>=2 then + local text=string.format("Adding element %s: status=%s, skill=%s, category=%s (%d), size: %.1f (L=%.1f H=%.1f W=%.1f)", + element.name, element.status, element.skill, element.categoryname, element.category, element.size, element.length, element.height, element.width) + self:I(self.lid..text) + end + + -- Add element to table. table.insert(self.elements, element) + + -- Get Descriptors. + self.descriptors=self.descriptors or unit:GetDesc() - self:GetAmmoUnit(unit, false) + -- Set type name. + self.actype=self.actype or unit:GetTypeName() - if unit:IsAlive() then + if unit:IsAlive() then + -- Trigger spawned event. self:ElementSpawned(element) end end - -- Get first unit. This is used to extract other parameters. - local unit=self.group:GetUnit(1) - - if unit then - - self.descriptors=unit:GetDesc() - - self.actype=unit:GetTypeName() - - -- Debug info. - if self.verbose>=1 then - local text=string.format("Initialized Navy Group %s:\n", self.groupname) - text=text..string.format("Unit type = %s\n", self.actype) - text=text..string.format("Speed max = %.1f Knots\n", UTILS.KmphToKnots(self.speedMax)) - text=text..string.format("Speed cruise = %.1f Knots\n", UTILS.KmphToKnots(self.speedCruise)) - text=text..string.format("Elements = %d\n", #self.elements) - text=text..string.format("Waypoints = %d\n", #self.waypoints) - text=text..string.format("Radio = %.1f MHz %s %s\n", self.radio.Freq, UTILS.GetModulationName(self.radio.Modu), tostring(self.radio.On)) - text=text..string.format("Ammo = %d (G=%d/R=%d/M=%d/T=%d)\n", self.ammo.Total, self.ammo.Guns, self.ammo.Rockets, self.ammo.Missiles, self.ammo.Torpedos) - text=text..string.format("FSM state = %s\n", self:GetState()) - text=text..string.format("Is alive = %s\n", tostring(self:IsAlive())) - text=text..string.format("LateActivate = %s\n", tostring(self:IsLateActivated())) - self:I(self.lid..text) - end - - -- Init done. - self.groupinitialized=true + -- Debug info. + if self.verbose>=1 then + local text=string.format("Initialized Navy Group %s:\n", self.groupname) + text=text..string.format("Unit type = %s\n", self.actype) + text=text..string.format("Speed max = %.1f Knots\n", UTILS.KmphToKnots(self.speedMax)) + text=text..string.format("Speed cruise = %.1f Knots\n", UTILS.KmphToKnots(self.speedCruise)) + text=text..string.format("Elements = %d\n", #self.elements) + text=text..string.format("Waypoints = %d\n", #self.waypoints) + text=text..string.format("Radio = %.1f MHz %s %s\n", self.radio.Freq, UTILS.GetModulationName(self.radio.Modu), tostring(self.radio.On)) + text=text..string.format("Ammo = %d (G=%d/R=%d/M=%d/T=%d)\n", self.ammo.Total, self.ammo.Guns, self.ammo.Rockets, self.ammo.Missiles, self.ammo.Torpedos) + text=text..string.format("FSM state = %s\n", self:GetState()) + text=text..string.format("Is alive = %s\n", tostring(self:IsAlive())) + text=text..string.format("LateActivate = %s\n", tostring(self:IsLateActivated())) + self:I(self.lid..text) end + -- Init done. + self.groupinitialized=true + return self end @@ -1444,7 +1457,7 @@ function NAVYGROUP:_CheckTurnsIntoWind() else -- Get next window. - local IntoWind=self:GetNextTurnIntoWind() + local IntoWind=self:GetTurnIntoWindNext() -- Start turn into wind. if IntoWind then diff --git a/Moose Development/Moose/Ops/OpsGroup.lua b/Moose Development/Moose/Ops/OpsGroup.lua index 7a9828e81..d9a889e0a 100644 --- a/Moose Development/Moose/Ops/OpsGroup.lua +++ b/Moose Development/Moose/Ops/OpsGroup.lua @@ -87,6 +87,8 @@ -- -- @field #OPSGROUP.Spot spot Laser and IR spot. -- +-- @field #OPSGROUP.Ammo ammo Initial ammuont of ammo. +-- -- @extends Core.Fsm#FSM --- *A small group of determined and like-minded people can change the course of history.* --- Mahatma Gandhi @@ -312,6 +314,8 @@ OPSGROUP.version="0.6.0" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO: Suppression of fire. +-- TODO: AI on/off. +-- TODO: Invisible/immortal. -- TODO: Add pseudo function. ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -387,10 +391,6 @@ function OPSGROUP:New(Group) self:AddTransition("*", "EnterZone", "*") -- Group entered a certain zone. self:AddTransition("*", "LeaveZone", "*") -- Group leaves a certain zone. - self:AddTransition("*", "Rearm", "Rearming") -- Group is send to a coordinate and waits until ammo is refilled. - self:AddTransition("Rearming", "Rearming", "Rearming") -- Group has arrived at the rearming coodinate and is waiting to be fully rearmed. - self:AddTransition("Rearming", "Rearmed", "Cruising") -- Group was rearmed. - self:AddTransition("*", "LaserOn", "*") -- Turn laser on. self:AddTransition("*", "LaserOff", "*") -- Turn laser off. self:AddTransition("*", "LaserCode", "*") -- Switch laser code. @@ -577,6 +577,13 @@ function OPSGROUP:GetDetectedUnits() return self.detectedunits end +--- Get inital amount of ammunition. +-- @param #OPSGROUP self +-- @return #OPSGROUP.Ammo Initial ammo table. +function OPSGROUP:GetAmmo0() + return self.ammo +end + --- Get highest detected threat. Detection must be turned on. The threat level is a number between 0 and 10, where 0 is the lowest, e.g. unarmed units. -- @param #OPSGROUP self -- @param #number ThreatLevelMin Only consider threats with level greater or equal to this number. Default 1 (so unarmed units wont be considered). @@ -611,7 +618,6 @@ function OPSGROUP:GetThreat(ThreatLevelMin, ThreatLevelMax) return threat, level end - --- Get MOOSE GROUP object. -- @param #OPSGROUP self -- @return Wrapper.Group#GROUP Moose group object. @@ -1089,7 +1095,8 @@ end -- @param #OPSGROUP self -- @return #boolean If true, group is rearming. function OPSGROUP:IsRearming() - return self:Is("Rearming") + local rearming=self:Is("Rearming") or self:Is("Rearm") + return rearming end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -2814,7 +2821,7 @@ function OPSGROUP:onafterPassingWaypoint(From, Event, To, Waypoint) end ---- On after "GotoWaypoint" event. Group will got to the given waypoint and execute its route from there. +--- Set tasks at this waypoint -- @param #OPSGROUP self -- @param #OPSGROUP.Waypoint Waypoint The waypoint. -- @return #number Number of tasks. @@ -3362,6 +3369,8 @@ function OPSGROUP:_CheckGroupDone(delay) -- Get current waypoint. local waypoint=self:GetWaypoint(self.currentwp) + + --env.info("FF CheckGroupDone") if waypoint then @@ -3439,6 +3448,56 @@ function OPSGROUP:_CheckGroupDone(delay) end +--- Check if group got stuck. +-- @param #OPSGROUP self +function OPSGROUP:_CheckStuck() + + -- Holding means we are not stuck. + if self:IsHolding() or self:Is("Rearming") then + return + end + + -- Current time. + local Tnow=timer.getTime() + + -- Expected speed in m/s. + local ExpectedSpeed=self:GetExpectedSpeed() + + -- Current speed in m/s. + local speed=self:GetVelocity() + + -- Check speed. + if speed<0.5 then + + if ExpectedSpeed>0 and not self.stuckTimestamp then + self:T2(self.lid..string.format("WARNING: Group came to an unexpected standstill. Speed=%.1f<%.1f m/s expected", speed, ExpectedSpeed)) + self.stuckTimestamp=Tnow + self.stuckVec3=self:GetVec3() + end + + else + -- Moving (again). + self.stuckTimestamp=nil + end + + -- Somehow we are not moving... + if self.stuckTimestamp then + + -- Time we are holding. + local holdtime=Tnow-self.stuckTimestamp + + if holdtime>=10*60 then + + self:E(self.lid..string.format("WARNING: Group came to an unexpected standstill. Speed=%.1f<%.1f m/s expected for %d sec", speed, ExpectedSpeed, holdtime)) + + --TODO: Stuck event! + + end + + end + +end + --- Check ammo is full. -- @param #OPSGROUP self -- @return #boolean If true, ammo is full. @@ -3459,6 +3518,72 @@ function OPSGROUP:_CheckAmmoFull() return true end +--- Check ammo status. +-- @param #OPSGROUP self +function OPSGROUP:_CheckAmmoStatus() + + -- First check if there was ammo initially. + if self.ammo.Total>0 then + + -- Get current ammo. + local ammo=self:GetAmmoTot() + + -- Check if rearming is completed. + if self:IsRearming() then + if ammo.Total==self.ammo.Total then + self:Rearmed() + end + end + + -- Total. + if self.outofAmmo and ammo.Total>0 then + self.outofAmmo=false + end + if ammo.Total==0 and not self.outofAmmo then + self.outofAmmo=true + self:OutOfAmmo() + end + + -- Guns. + if self.outofGuns and ammo.Guns>0 then + self.outoffGuns=false + end + if ammo.Guns==0 and self.ammo.Guns>0 and not self.outofGuns then + self.outofGuns=true + self:OutOfGuns() + end + + -- Rockets. + if self.outofRockets and ammo.Rockets>0 then + self.outoffRockets=false + end + if ammo.Rockets==0 and self.ammo.Rockets>0 and not self.outofRockets then + self.outofRockets=true + self:OutOfRockets() + end + + -- Bombs. + if self.outofBombs and ammo.Bombs>0 then + self.outoffBombs=false + end + if ammo.Bombs==0 and self.ammo.Bombs>0 and not self.outofBombs then + self.outofBombs=true + self:OutOfBombs() + end + + -- Missiles. + if self.outofMissiles and ammo.Missiles>0 then + self.outoffMissiles=false + end + if ammo.Missiles==0 and self.ammo.Missiles>0 and not self.outofMissiles then + self.outofMissiles=true + self:OutOfMissiles() + end + + end + +end + ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Status Info Common to Air, Land and Sea ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -5070,56 +5195,6 @@ function OPSGROUP:_MissileCategoryName(categorynumber) return cat end ---- Check if group got stuck. --- @param #OPSGROUP self -function OPSGROUP:_CheckStuck() - - -- Holding means we are not stuck. - if self:IsHolding() then - return - end - - -- Current time. - local Tnow=timer.getTime() - - -- Expected speed in m/s. - local ExpectedSpeed=self:GetExpectedSpeed() - - -- Current speed in m/s. - local speed=self:GetVelocity() - - -- Check speed. - if speed<0.5 then - - if ExpectedSpeed>0 and not self.stuckTimestamp then - self:T2(self.lid..string.format("WARNING: Group came to an unexpected standstill. Speed=%.1f<%.1f m/s expected", speed, ExpectedSpeed)) - self.stuckTimestamp=Tnow - self.stuckVec3=self:GetVec3() - end - - else - -- Moving (again). - self.stuckTimestamp=nil - end - - -- Somehow we are not moving... - if self.stuckTimestamp then - - -- Time we are holding. - local holdtime=Tnow-self.stuckTimestamp - - if holdtime>=10*60 then - - self:E(self.lid..string.format("WARNING: Group came to an unexpected standstill. Speed=%.1f<%.1f m/s expected for %d sec", speed, ExpectedSpeed, holdtime)) - - --TODO: Stuck event! - - end - - end - -end - --- Get coordinate from an object. -- @param #OPSGROUP self -- @param Wrapper.Object#OBJECT Object The object. From 8696ce0cc145918851996a50a7f35f46004ce14a Mon Sep 17 00:00:00 2001 From: Frank Date: Tue, 20 Oct 2020 23:04:15 +0200 Subject: [PATCH 05/10] Ops --- Moose Development/Moose/Ops/ArmyGroup.lua | 6 +- Moose Development/Moose/Ops/Auftrag.lua | 7 ++ Moose Development/Moose/Ops/FlightGroup.lua | 26 +++--- Moose Development/Moose/Ops/NavyGroup.lua | 4 +- Moose Development/Moose/Ops/OpsGroup.lua | 99 ++++++++++++++++----- Moose Development/Moose/Wrapper/Group.lua | 34 +++++++ 6 files changed, 135 insertions(+), 41 deletions(-) diff --git a/Moose Development/Moose/Ops/ArmyGroup.lua b/Moose Development/Moose/Ops/ArmyGroup.lua index df52c7cc8..31c0aa727 100644 --- a/Moose Development/Moose/Ops/ArmyGroup.lua +++ b/Moose Development/Moose/Ops/ArmyGroup.lua @@ -375,7 +375,7 @@ function ARMYGROUP:onafterSpawned(From, Event, To) -- Update position. self:_UpdatePosition() - if self.ai then + if self.isAI then -- Set default ROE. self:SwitchROE(self.option.ROE) @@ -859,8 +859,8 @@ function ARMYGROUP:_InitGroup() self.isNaval=false self.isGround=true - -- Ships are always AI. - self.ai=true + -- Ground are always AI. + self.isAI=true -- Is (template) group late activated. self.isLateActivated=self.template.lateActivation diff --git a/Moose Development/Moose/Ops/Auftrag.lua b/Moose Development/Moose/Ops/Auftrag.lua index 54d186a09..08bab7e77 100644 --- a/Moose Development/Moose/Ops/Auftrag.lua +++ b/Moose Development/Moose/Ops/Auftrag.lua @@ -1690,6 +1690,13 @@ function AUFTRAG:GetPriority() return self.prio end +--- Get casualties, i.e. number of units that died during this mission. +-- @param #AUFTRAG self +-- @return #number Number of dead units. +function AUFTRAG:GetCasualties() + return self.Ncasualties or 0 +end + --- Check if mission is "urgent". -- @param #AUFTRAG self -- @return #boolean If `true`, mission is "urgent". diff --git a/Moose Development/Moose/Ops/FlightGroup.lua b/Moose Development/Moose/Ops/FlightGroup.lua index dc73d37e2..1267c8cb8 100644 --- a/Moose Development/Moose/Ops/FlightGroup.lua +++ b/Moose Development/Moose/Ops/FlightGroup.lua @@ -411,7 +411,7 @@ function FLIGHTGROUP:SetFlightControl(flightcontrol) table.insert(flightcontrol.flights, self) -- Update flight's F10 menu. - if self.ai==false then + if self.isAI==false then self:_UpdateMenu(0.5) end @@ -1055,7 +1055,7 @@ function FLIGHTGROUP:OnEventEngineStartup(EventData) -- TODO: could be that this element is part of a human flight group. -- Problem: when player starts hot, the AI does too and starts to taxi immidiately :( -- when player starts cold, ? - if self.ai then + if self.isAI then self:ElementEngineOn(element) else if element.ai then @@ -1462,7 +1462,7 @@ function FLIGHTGROUP:onafterSpawned(From, Event, To) -- Update position. self:_UpdatePosition() - if self.ai then + if self.isAI then -- Set ROE. self:SwitchROE(self.option.ROE) @@ -1537,7 +1537,7 @@ function FLIGHTGROUP:onafterParking(From, Event, To) self.flightcontrol:SetFlightStatus(self, FLIGHTCONTROL.FlightStatus.PARKING) -- Update player menu. - if not self.ai then + if not self.isAI then self:_UpdateMenu(0.5) end @@ -1562,7 +1562,7 @@ function FLIGHTGROUP:onafterTaxiing(From, Event, To) if self.flightcontrol and airbase and self.flightcontrol.airbasename==airbase:GetName() then -- Add AI flight to takeoff queue. - if self.ai then + if self.isAI then -- AI flights go directly to TAKEOFF as we don't know when they finished taxiing. self.flightcontrol:SetFlightStatus(self, FLIGHTCONTROL.FlightStatus.TAKEOFF) else @@ -1601,7 +1601,7 @@ end function FLIGHTGROUP:onafterAirborne(From, Event, To) self:T(self.lid..string.format("Flight airborne")) - if self.ai then + if self.isAI then self:_CheckGroupDone(1) else self:_UpdateMenu() @@ -1752,7 +1752,7 @@ function FLIGHTGROUP:onbeforeUpdateRoute(From, Event, To, n) --end -- Only AI flights. - if not self.ai then + if not self.isAI then allowed=false end @@ -1855,7 +1855,7 @@ end -- @param #number delay Delay in seconds. function FLIGHTGROUP:_CheckGroupDone(delay) - if self:IsAlive() and self.ai then + if self:IsAlive() and self.isAI then if delay and delay>0 then -- Delayed call. @@ -2114,7 +2114,7 @@ function FLIGHTGROUP:onafterRTB(From, Event, To, airbase, SpeedTo, SpeedHold, Sp end - if self.ai then + if self.isAI then local routeto=false if fc or world.event.S_EVENT_KILL then @@ -2294,7 +2294,7 @@ function FLIGHTGROUP:onafterHolding(From, Event, To) -- Set flight status to holding self.flightcontrol:SetFlightStatus(self, FLIGHTCONTROL.FlightStatus.HOLDING) - if not self.ai then + if not self.isAI then self:_UpdateMenu() end @@ -2641,10 +2641,10 @@ function FLIGHTGROUP:_InitGroup() self.tacan=UTILS.DeepCopy(self.tacanDefault) -- Is this purely AI? - self.ai=not self:_IsHuman(group) + self.isAI=not self:_IsHuman(group) -- Create Menu. - if not self.ai then + if not self.isAI then self.menu=self.menu or {} self.menu.atc=self.menu.atc or {} self.menu.atc.root=self.menu.atc.root or MENU_GROUP:New(self.group, "ATC") @@ -2680,7 +2680,7 @@ function FLIGHTGROUP:_InitGroup() text=text..string.format("Ceiling = %.1f feet\n", UTILS.MetersToFeet(self.ceiling)) text=text..string.format("Tanker type = %s\n", tostring(self.tankertype)) text=text..string.format("Refuel type = %s\n", tostring(self.refueltype)) - text=text..string.format("AI = %s\n", tostring(self.ai)) + text=text..string.format("AI = %s\n", tostring(self.isAI)) text=text..string.format("Helicopter = %s\n", tostring(self.group:IsHelicopter())) text=text..string.format("Elements = %d\n", #self.elements) text=text..string.format("Waypoints = %d\n", #self.waypoints) diff --git a/Moose Development/Moose/Ops/NavyGroup.lua b/Moose Development/Moose/Ops/NavyGroup.lua index 515aaf817..6c6f54d44 100644 --- a/Moose Development/Moose/Ops/NavyGroup.lua +++ b/Moose Development/Moose/Ops/NavyGroup.lua @@ -639,7 +639,7 @@ function NAVYGROUP:onafterSpawned(From, Event, To) -- Update position. self:_UpdatePosition() - if self.ai then + if self.isAI then -- Set default ROE. self:SwitchROE(self.option.ROE) @@ -1198,7 +1198,7 @@ function NAVYGROUP:_InitGroup() --self.isSubmarine=self.group:IsSubmarine() -- Ships are always AI. - self.ai=true + self.isAI=true -- Is (template) group late activated. self.isLateActivated=self.template.lateActivation diff --git a/Moose Development/Moose/Ops/OpsGroup.lua b/Moose Development/Moose/Ops/OpsGroup.lua index d9a889e0a..82c9def44 100644 --- a/Moose Development/Moose/Ops/OpsGroup.lua +++ b/Moose Development/Moose/Ops/OpsGroup.lua @@ -23,7 +23,7 @@ -- @field #boolean isLateActivated Is the group late activated. -- @field #boolean isUncontrolled Is the group uncontrolled. -- @field #table elements Table of elements, i.e. units of the group. --- @field #boolean ai If true, group is purely AI. +-- @field #boolean isAI If true, group is purely AI. -- @field #boolean isAircraft If true, group is airplane or helicopter. -- @field #boolean isNaval If true, group is ships or submarine. -- @field #boolean isGround If true, group is some ground unit. @@ -262,12 +262,18 @@ OPSGROUP.TaskType={ --- Laser and IR spot data. -- @type OPSGROUP.Spot +-- @field #boolean CheckLOS If true, check LOS to target. +-- @field #boolean IRon If true, turn IR pointer on. +-- @field #number dt Update time interval in seconds. -- @field DCS#Spot Laser Laser spot. -- @field DCS#Spot IR Infra-red spot. -- @field #number Code Laser code. --- @field Wrapper.Positionable#POSITIONABLE Target The target. +-- @field Wrapper.Group#GROUP TargetGroup The target group. +-- @field Wrapper.Positionable#POSITIONABLE TargetUnit The current target unit. -- @field Core.Point#COORDINATE Coordinate where the spot is pointing. -- @field #boolean On If true, the laser is on. +-- @field #OPSGROUP.Element element The element of the group that is lasing. +-- @field DCS#Vec3 vec3 The 3D positon vector of the laser (and IR) spot. -- @field Core.Timer#TIMER timer Spot timer. --- Ammo data. @@ -496,19 +502,42 @@ function OPSGROUP:SetDetection(Switch) return self end ---- Set detection on or off. --- If detection is on, detected targets of the group will be evaluated and FSM events triggered. +--- Set LASER parameters. -- @param #OPSGROUP self -- @param #number Code Laser code. Default 1688. +-- @param #boolean CheckLOS Check if lasing unit has line of sight to target coordinate. -- @param #boolean IROff If true, then dont switch on the additional IR pointer. -- @param #number UpdateTime Time interval in seconds the beam gets up for moving targets. Default every 0.5 sec. -- @return #OPSGROUP self -function OPSGROUP:SetLaser(Code, IROff, UpdateTime) +function OPSGROUP:SetLaser(Code, CheckLOS, IROff, UpdateTime) self.spot.Code=Code or 1688 - self.spot.IRoff=IROff + self.spot.CheckLOS=CheckLOS + self.spot.IRon=not IROff + self.spot.dt=UpdateTime or 0.5 return self end +--- Get LASER code. +-- @param #OPSGROUP self +-- @return #number Current Laser code. +function OPSGROUP:GetLaserCode() + return self.spot.Code +end + +--- Get current LASER coordinate, i.e. where the beam is pointing at if the LASER is on. +-- @param #OPSGROUP self +-- @return Core.Point#COORDINATE Current position where the LASER is pointing at. +function OPSGROUP:GetLaserCoordinate() + return self.spot.Coordinate +end + +--- Get current target of the LASER. This can be a STATIC or UNIT object. +-- @param #OPSGROUP self +-- @return Wrapper.Positionable#POSITIONABLE Current target object. +function OPSGROUP:GetLaserTarget() + return self.spot.TargetUnit +end + --- Define a SET of zones that trigger and event if the group enters or leaves any of the zones. -- @param #OPSGROUP self -- @param Core.Set#SET_ZONE CheckZonesSet Set of zones. @@ -2984,8 +3013,15 @@ function OPSGROUP:onafterLaserOn(From, Event, To, Target) if target:IsAlive() then - self.spot.Target=Target - coord=Target:GetCoordinate() + if target:IsInstanceOf("GROUP") then + self.spot.TargetGroup=target + self.spot.TargetUnit=target:GetHighestThreat() + else + self.spot.TargetUnit=target + end + + -- Current coordinate + coord=self.spot.TargetUnit:GetCoordinate() else self:E("WARNING: LASER target is not alive!") @@ -3003,8 +3039,7 @@ function OPSGROUP:onafterLaserOn(From, Event, To, Target) if element then -- Debug message. - self:T(self.lid.."Switching LASER on") - + self:I(self.lid.."Switching LASER on") -- Start timer that calls the update twice per sec. self.spot.timer:Start(nil, 0.5) @@ -3023,11 +3058,12 @@ function OPSGROUP:onafterLaserOn(From, Event, To, Target) -- Create laser and IR beams. self.spot.Laser=Spot.createLaser(DCSunit, {x=0, y=offsetY, z=0}, self.spot.vec3, self.spot.Code or 1688) - self.spot.IR=Spot.createInfraRed(DCSunit, {x=0, y=offsetY, z=0}, self.spot.vec3) + if self.spot.IRon then + self.spot.IR=Spot.createInfraRed(DCSunit, {x=0, y=offsetY, z=0}, self.spot.vec3) + end -- Laser is on. self.spot.On=true - end end @@ -3091,16 +3127,16 @@ function OPSGROUP:_UpdateLaser() --´- -- Check if we have a POSITIONABLE to lase. - if self.spot.Target then + if self.spot.TargetUnit then --- -- Lasing a possibly moving target --- - if self.spot.Target:IsAlive() then + if self.spot.TargetUnit:IsAlive() then -- Get current target position. - local vec3=self.spot.Target:GetVec3() + local vec3=self.spot.TargetUnit:GetVec3() -- Calculate distance local dist=UTILS.VecDist3D(vec3, self.spot.vec3) @@ -3116,22 +3152,39 @@ function OPSGROUP:_UpdateLaser() -- Set the new laser target point. self.spot.Laser:setPoint(vec3) - self.spot.IR:setPoint(vec3) + if self.spot.IRon then + self.spot.IR:setPoint(vec3) + end end else - -- Switch laser off. - self:T(self.lid.."Target is not alive any more ==> switching LASER off") - self:LaserOff() + if self.spot.TargetGroup and self.spot.TargetGroup:IsAlive() then + + -- Get first alive unit in the group. + local unit=self.spot.TargetGroup:GetHighestThreat() + + if unit then + self:I(self.lid.."Switching to target other target unit in the group") + self.spot.TargetUnit=unit + else + -- Switch laser off. + self:T(self.lid.."Target is not alive any more ==> switching LASER off") + self:LaserOff() + end + + else - end + -- Switch laser off. + self:T(self.lid.."Target is not alive any more ==> switching LASER off") + self:LaserOff() + + end + end end - end - end @@ -3360,7 +3413,7 @@ end -- @param #number delay Delay in seconds. function OPSGROUP:_CheckGroupDone(delay) - if self:IsAlive() and self.ai then + if self:IsAlive() and self.isAI then if delay and delay>0 then -- Delayed call. diff --git a/Moose Development/Moose/Wrapper/Group.lua b/Moose Development/Moose/Wrapper/Group.lua index 34172a162..c028703ec 100644 --- a/Moose Development/Moose/Wrapper/Group.lua +++ b/Moose Development/Moose/Wrapper/Group.lua @@ -2169,6 +2169,40 @@ function GROUP:GetThreatLevel() return threatlevelMax end +--- Get the unit in the group with the highest threat level, which is still alive. +-- @param #GROUP self +-- @return Wrapper.Unit#UNIT The most dangerous unit in the group. +-- @return #number Threat level of the unit. +function GROUP:GetHighestThreat() + + -- Get units of the group. + local units=self:GetUnits() + + if units then + + local threat=nil ; local maxtl=0 + for _,_unit in pairs(units or {}) do + local unit=_unit --Wrapper.Unit#UNIT + + if unit and unit:IsAlive() then + + -- Threat level of group. + local tl=unit:GetThreatLevel() + + -- Check if greater the current threat. + if tl>maxtl then + maxtl=tl + threat=unit + end + end + end + + return threat, maxtl + end + + return nil, nil +end + --- Returns true if the first unit of the GROUP is in the air. -- @param Wrapper.Group#GROUP self From 2d6653c14a10d0d2e5f3d3d8ed8be3d3c89f24d3 Mon Sep 17 00:00:00 2001 From: Frank Date: Mon, 26 Oct 2020 08:01:56 +0100 Subject: [PATCH 06/10] Ops - Laser --- Moose Development/Moose/Core/Point.lua | 15 +- .../Moose/Functional/Warehouse.lua | 2 +- Moose Development/Moose/Ops/ArmyGroup.lua | 24 +- Moose Development/Moose/Ops/FlightGroup.lua | 2 +- Moose Development/Moose/Ops/OpsGroup.lua | 512 +++++++++++++++--- Moose Development/Moose/Wrapper/Unit.lua | 3 +- 6 files changed, 471 insertions(+), 87 deletions(-) diff --git a/Moose Development/Moose/Core/Point.lua b/Moose Development/Moose/Core/Point.lua index 6c9975ff1..3a5160f86 100644 --- a/Moose Development/Moose/Core/Point.lua +++ b/Moose Development/Moose/Core/Point.lua @@ -283,7 +283,13 @@ do -- COORDINATE return self end - + + --- Return the coordinates itself. Sounds stupid but can be useful for compatibility. + -- @param #COORDINATE self + -- @return #COORDINATE self + function COORDINATE:GetCoordinate() + return self + end --- Return the coordinates of the COORDINATE in Vec3 format. -- @param #COORDINATE self @@ -733,6 +739,13 @@ do -- COORDINATE return Velocity or 0 end + --- Return the "name" of the COORDINATE. Obviously, a coordinate does not have a name like a unit, static or group. So here we take the MGRS coordinates of the position. + -- @param #COORDINATE self + -- @return #string MGRS coordinates. + function COORDINATE:GetName() + local name=self:ToStringMGRS() + return rname + end --- Return velocity text of the COORDINATE. -- @param #COORDINATE self diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index e9661a754..6e0ee387f 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -7049,7 +7049,7 @@ function WAREHOUSE:_CheckRequestNow(request) -- If no transport is requested, assets need to be mobile unless it is a self request. local onlymobile=false - if type(request.transport)=="number" and request.ntransport==0 and not request.toself then + if type(request.ntransport)=="number" and request.ntransport==0 and not request.toself then onlymobile=true end diff --git a/Moose Development/Moose/Ops/ArmyGroup.lua b/Moose Development/Moose/Ops/ArmyGroup.lua index 31c0aa727..423d2463d 100644 --- a/Moose Development/Moose/Ops/ArmyGroup.lua +++ b/Moose Development/Moose/Ops/ArmyGroup.lua @@ -130,7 +130,9 @@ function ARMYGROUP:New(Group) -- Handle events: self:HandleEvent(EVENTS.Birth, self.OnEventBirth) self:HandleEvent(EVENTS.Dead, self.OnEventDead) - self:HandleEvent(EVENTS.RemoveUnit, self.OnEventRemoveUnit) + self:HandleEvent(EVENTS.RemoveUnit, self.OnEventRemoveUnit) + + --self:HandleEvent(EVENTS.Hit, self.OnEventHit) -- Start the status monitoring. self:__Status(-1) @@ -545,7 +547,7 @@ function ARMYGROUP:onafterGotoWaypoint(From, Event, To, UID, Speed, Formation) local tasks=self:GetTasksWaypoint(n) for _,_task in pairs(tasks) do - local task=_task --#OPSGROUP.Task + local task=_task --Ops.OpsGroup#OPSGROUP.Task task.status=OPSGROUP.TaskStatus.SCHEDULED end @@ -762,7 +764,7 @@ function ARMYGROUP:OnEventDead(EventData) end ---- Event function handling the crash of a unit. +--- Event function handling when a unit is removed from the game. -- @param #ARMYGROUP self -- @param Core.Event#EVENTDATA EventData Event data. function ARMYGROUP:OnEventRemoveUnit(EventData) @@ -785,6 +787,22 @@ function ARMYGROUP:OnEventRemoveUnit(EventData) end +--- Event function handling when a unit is hit. +-- @param #ARMYGROUP self +-- @param Core.Event#EVENTDATA EventData Event data. +function ARMYGROUP:OnEventHit(EventData) + + -- Check that this is the right group. + if EventData and EventData.IniGroup and EventData.IniUnit and EventData.IniGroupName and EventData.IniGroupName==self.groupname then + local unit=EventData.IniUnit + local group=EventData.IniGroup + local unitname=EventData.IniUnitName + + -- TODO: suppression + + end +end + ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Routing ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- diff --git a/Moose Development/Moose/Ops/FlightGroup.lua b/Moose Development/Moose/Ops/FlightGroup.lua index 1267c8cb8..87e207d8f 100644 --- a/Moose Development/Moose/Ops/FlightGroup.lua +++ b/Moose Development/Moose/Ops/FlightGroup.lua @@ -720,7 +720,7 @@ end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---- Update status. --- @param #FLIHGTGROUP self +-- @param #FLIGHTGROUP self function FLIGHTGROUP:onbeforeStatus(From, Event, To) -- First we check if elements are still alive. Could be that they were despawned without notice, e.g. when landing on a too small airbase. diff --git a/Moose Development/Moose/Ops/OpsGroup.lua b/Moose Development/Moose/Ops/OpsGroup.lua index 82c9def44..93b12fcf9 100644 --- a/Moose Development/Moose/Ops/OpsGroup.lua +++ b/Moose Development/Moose/Ops/OpsGroup.lua @@ -39,6 +39,7 @@ -- @field #table missionqueue Queue of missions. -- @field #number currentmission The ID (auftragsnummer) of the currently assigned AUFTRAG. -- @field Core.Set#SET_UNIT detectedunits Set of detected units. +-- @field Core.Set#SET_GROUP detectedgroups Set of detected groups. -- @field #string attribute Generalized attribute. -- @field #number speedMax Max speed in km/h. -- @field #number speedCruise Cruising speed in km/h. @@ -127,6 +128,7 @@ OPSGROUP = { missionqueue = {}, currentmission = nil, detectedunits = {}, + detectedgroups = {}, attribute = nil, checkzones = nil, inzones = nil, @@ -271,9 +273,11 @@ OPSGROUP.TaskType={ -- @field Wrapper.Group#GROUP TargetGroup The target group. -- @field Wrapper.Positionable#POSITIONABLE TargetUnit The current target unit. -- @field Core.Point#COORDINATE Coordinate where the spot is pointing. +-- @field #number TargetType Type of target: 0=coordinate, 1=static, 2=unit, 3=group. -- @field #boolean On If true, the laser is on. -- @field #OPSGROUP.Element element The element of the group that is lasing. -- @field DCS#Vec3 vec3 The 3D positon vector of the laser (and IR) spot. +-- @field DCS#Vec3 offset Local offset of the laser source. -- @field Core.Timer#TIMER timer Spot timer. --- Ammo data. @@ -347,19 +351,22 @@ function OPSGROUP:New(Group) end -- Set some string id for output to DCS.log file. - self.lid=string.format("OPSGROUP %s |", self.groupname) + self.lid=string.format("OPSGROUP %s | ", self.groupname) -- Init set of detected units. self.detectedunits=SET_UNIT:New() + -- Init set of detected groups. + self.detectedgroups=SET_GROUP:New() + -- Init inzone set. self.inzones=SET_ZONE:New() -- Laser. self.spot={} self.spot.On=false - self.spot.timer=TIMER:New(OPSGROUP._UpdateLaser, self) - self:SetLaser() + self.spot.timer=TIMER:New(self._UpdateLaser, self) + self:SetLaser(1688, true, false, 0.5) -- Init task counter. self.taskcurrent=0 @@ -380,11 +387,16 @@ function OPSGROUP:New(Group) self:AddTransition("*", "Respawn", "*") -- Respawn group. self:AddTransition("*", "PassingWaypoint", "*") -- Passing waypoint. - self:AddTransition("*", "DetectedUnit", "*") -- Add a newly detected unit to the detected units set. + self:AddTransition("*", "DetectedUnit", "*") -- Unit was detected (again) in this detection cycle. self:AddTransition("*", "DetectedUnitNew", "*") -- Add a newly detected unit to the detected units set. - self:AddTransition("*", "DetectedUnitKnown", "*") -- Add a newly detected unit to the detected units set. + self:AddTransition("*", "DetectedUnitKnown", "*") -- A known unit is still detected. self:AddTransition("*", "DetectedUnitLost", "*") -- Group lost a detected target. + self:AddTransition("*", "DetectedGroup", "*") -- Unit was detected (again) in this detection cycle. + self:AddTransition("*", "DetectedGroupNew", "*") -- Add a newly detected unit to the detected units set. + self:AddTransition("*", "DetectedGroupKnown", "*") -- A known unit is still detected. + self:AddTransition("*", "DetectedGroupLost", "*") -- Group lost a detected target group. + self:AddTransition("*", "PassingWaypoint", "*") -- Group passed a waypoint. self:AddTransition("*", "GotoWaypoint", "*") -- Group switches to a specific waypoint. @@ -400,6 +412,9 @@ function OPSGROUP:New(Group) self:AddTransition("*", "LaserOn", "*") -- Turn laser on. self:AddTransition("*", "LaserOff", "*") -- Turn laser off. self:AddTransition("*", "LaserCode", "*") -- Switch laser code. + self:AddTransition("*", "LaserPause", "*") -- Turn laser off temporarily. + self:AddTransition("*", "LaserLostLOS", "*") -- Lasing element lost line of sight. + self:AddTransition("*", "LaserGotLOS", "*") -- Lasing element got line of sight. self:AddTransition("*", "TaskExecute", "*") -- Group will execute a task. self:AddTransition("*", "TaskPause", "*") -- Pause current task. Not implemented yet! @@ -603,7 +618,14 @@ end -- @param #OPSGROUP self -- @return Core.Set#SET_UNIT Set of detected units. function OPSGROUP:GetDetectedUnits() - return self.detectedunits + return self.detectedunits or {} +end + +--- Get set of detected groups. +-- @param #OPSGROUP self +-- @return Core.Set#SET_GROUP Set of detected groups. +function OPSGROUP:GetDetectedGroups() + return self.detectedgroups or {} end --- Get inital amount of ammunition. @@ -1128,6 +1150,13 @@ function OPSGROUP:IsRearming() return rearming end +--- Check if the group has currently switched a LASER on. +-- @param #OPSGROUP self +-- @return #boolean If true, LASER of the group is on. +function OPSGROUP:IsLasing() + return self.spot.On +end + ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Waypoint Functions ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -2934,7 +2963,7 @@ function OPSGROUP:onafterGotoWaypoint(From, Event, To, UID) end ---- On after "DetectedUnit" event. Add newly detected unit to detected units set. +--- On after "DetectedUnit" event. -- @param #OPSGROUP self -- @param #string From From state. -- @param #string Event Event. @@ -2947,7 +2976,7 @@ function OPSGROUP:onafterDetectedUnit(From, Event, To, Unit) -- Debug. self:T2(self.lid..string.format("Detected unit %s", unitname)) - + if self.detectedunits:FindUnit(unitname) then -- Unit is already in the detected unit set ==> Trigger "DetectedUnitKnown" event. @@ -2955,11 +2984,11 @@ function OPSGROUP:onafterDetectedUnit(From, Event, To, Unit) else -- Unit is was not detected ==> Trigger "DetectedUnitNew" event. self:DetectedUnitNew(Unit) - end - + end + end ---- On after "DetectedUnitNew" event. +--- On after "DetectedUnitNew" event. Add newly detected unit to detected unit set. -- @param #OPSGROUP self -- @param #string From From state. -- @param #string Event Event. @@ -2972,6 +3001,44 @@ function OPSGROUP:onafterDetectedUnitNew(From, Event, To, Unit) self.detectedunits:AddUnit(Unit) end +--- On after "DetectedGroup" event. +-- @param #OPSGROUP self +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +-- @param Wrapper.Group#GROUP Group The detected Group. +function OPSGROUP:onafterDetectedGroup(From, Event, To, Group) + + -- Get group name. + local groupname=Group and Group:GetName() or "unknown" + + -- Debug. + self:I(self.lid..string.format("Detected group %s", groupname)) + + + if self.detectedgroups:FindGroup(groupname) then + -- Group is already in the detected set ==> Trigger "DetectedGroupKnown" event. + self:DetectedGroupKnown(Group) + else + -- Group is was not detected ==> Trigger "DetectedGroupNew" event. + self:DetectedGroupNew(Group) + end + +end + +--- On after "DetectedGroupNew" event. Add newly detected group to detected group set. +-- @param #OPSGROUP self +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +-- @param Wrapper.Group#GROUP Group The detected group. +function OPSGROUP:onafterDetectedGroupNew(From, Event, To, Group) + self:I(self.lid..string.format("Detected New group %s", Group:GetName())) + + -- Add unit to detected unit set. + self.detectedgroups:AddGroup(Group) +end + --- On after "EnterZone" event. Sets self.inzones[zonename]=true. -- @param #OPSGROUP self -- @param #string From From state. @@ -2996,6 +3063,77 @@ function OPSGROUP:onafterLeaveZone(From, Event, To, Zone) self.inzones:Remove(zonename, true) end +--- On before "LaserOn" event. +-- @param #OPSGROUP self +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +-- @param Core.Point#COORDINATE Target Target Coordinate. Target can also be any POSITIONABLE from which we can obtain its coordinates. +function OPSGROUP:onbeforeLaserOn(From, Event, To, Target) + + -- Check if LASER is already on. + if self.spot.On then + return false + end + + if Target then + + -- Target specified ==> set target. + self:SetLaserTarget(Target) + + else + -- No target specified. + --TODO: error message + return false + end + + -- Get the first element alive. + local element=self:GetElementAlive() + + if element then + + -- Set element. + self.spot.element=element + + -- Vec3. + self.spot.vec3=self.spot.Coordinate:GetVec3() + + -- Height offset. No offset for aircraft. We take the height for ground or naval. + local offsetY=0 + if self.isGround or self.isNaval then + offsetY=element.height + end + + -- Local offset of the LASER source. + self.spot.offset={x=0, y=offsetY, z=0} + + -- Check LOS. + if self.spot.CheckLOS then + + -- Check LOS. + self.spot.LOS=self:HasLoS(self.spot.Coordinate, self.spot.element, self.spot.offset) + + env.info(string.format("FF LOS=%s", tostring(self.spot.LOS))) + + if self.spot.LOS then + self:LaserGotLOS() + else + -- Try to switch laser on again in 10 sec. + env.info("FF no LOS currently. Trying to switch the laser on again in 10 sec.") + self:__LaserOn(-10, Target) + return false + end + + end + + else + self:E(self.lid.."ERROR: No element alive for lasing") + return false + end + + return true +end + --- On after "LaserOn" event. -- @param #OPSGROUP self -- @param #string From From state. @@ -3004,67 +3142,33 @@ end -- @param Core.Point#COORDINATE Target Target Coordinate. Target can also be any POSITIONABLE from which we can obtain its coordinates. function OPSGROUP:onafterLaserOn(From, Event, To, Target) - -- Assume we got a coordinate. - local coord=Target - - -- Check if we have a POSITIONABLE. - if Target:IsInstanceOf("POSITIONABLE") then - local target=Target --Wrapper.Positionable#POSITIONABLE - - if target:IsAlive() then - - if target:IsInstanceOf("GROUP") then - self.spot.TargetGroup=target - self.spot.TargetUnit=target:GetHighestThreat() - else - self.spot.TargetUnit=target - end - - -- Current coordinate - coord=self.spot.TargetUnit:GetCoordinate() - - else - self:E("WARNING: LASER target is not alive!") - return - end - + -- Debug message. + self:I(self.lid.."Switching LASER on") + + -- Start timer that calls the update twice per sec by default. + self.spot.timer:Start(nil, self.spot.dt) + + -- Get DCS unit. + local DCSunit=self.spot.element.unit:GetDCSObject() + + -- Create laser and IR beams. + self.spot.Laser=Spot.createLaser(DCSunit, self.spot.offset, self.spot.vec3, self.spot.Code or 1688) + if self.spot.IRon then + self.spot.IR=Spot.createInfraRed(DCSunit, self.spot.offset, self.spot.vec3) end - -- Set coordinate. - self.spot.Coordinate=coord - - -- Get the first element alive. - local element=self:GetElementAlive() + -- Laser is on. + self.spot.On=true - if element then +end - -- Debug message. - self:I(self.lid.."Switching LASER on") - - -- Start timer that calls the update twice per sec. - self.spot.timer:Start(nil, 0.5) - - -- Set element. - self.spot.element=element - - -- Get DCS unit. - local DCSunit=self.spot.element.unit:GetDCSObject() - - -- Vec3. - self.spot.vec3=coord:GetVec3() - - -- Height offset. - local offsetY=element.height - - -- Create laser and IR beams. - self.spot.Laser=Spot.createLaser(DCSunit, {x=0, y=offsetY, z=0}, self.spot.vec3, self.spot.Code or 1688) - if self.spot.IRon then - self.spot.IR=Spot.createInfraRed(DCSunit, {x=0, y=offsetY, z=0}, self.spot.vec3) - end - - -- Laser is on. - self.spot.On=true - end +--- On before "LaserOff" event. Check if LASER is on. +-- @param #OPSGROUP self +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +function OPSGROUP:onbeforeLaserOff(From, Event, To) + return self.spot.On end --- On after "LaserOff" event. @@ -3075,7 +3179,34 @@ end function OPSGROUP:onafterLaserOff(From, Event, To) -- Debug message. - self:T(self.lid.."Switching LASER off") + self:I(self.lid.."Switching LASER off") + + -- "Destroy" the laser beam. + self.spot.Laser:destroy() + self.spot.IR:destroy() + + -- Stop update timer. + self.spot.timer:Stop() + + -- Set to nil. + self.spot.Laser=nil + self.spot.IR=nil + self.spot.TargetUnit=nil + + -- Laser is off. + self.spot.On=false + +end + +--- On after "LaserPause" event. +-- @param #OPSGROUP self +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +function OPSGROUP:onafterLaserPause(From, Event, To) + + -- Debug message. + self:I(self.lid.."Switching LASER off temporarily.") -- "Destroy" the laser beam. self.spot.Laser:destroy() @@ -3102,12 +3233,12 @@ function OPSGROUP:onafterLaserCode(From, Event, To, Code) self.spot.Code=Code or 1688 -- Debug message. - self:T(self.lid..string.format("Setting LASER Code to %d", self.spot.Code)) + self:T2(self.lid..string.format("Setting LASER Code to %d", self.spot.Code)) if self.spot.On then -- Debug info. - self:T(self.lid..string.format("Updating LASER Code to %d", self.spot.Code)) + self:T(self.lid..string.format("New LASER Code is %d", self.spot.Code)) -- Set LASER code. self.spot.Laser:setCode(self.spot.Code) @@ -3115,6 +3246,143 @@ function OPSGROUP:onafterLaserCode(From, Event, To, Code) end +--- On after "LaserLostLOS" event. +-- @param #OPSGROUP self +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +function OPSGROUP:onafterLaserLostLOS(From, Event, To) + + -- Lost line of sight. + self.spot.LOS=false + + if self.spot.On then + + -- Switch laser off. + self:LaserPause() + + end + +end + +--- On after "LaserGotLOS" event. +-- @param #OPSGROUP self +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +function OPSGROUP:onafterLaserGotLOS(From, Event, To) + + -- Lost line of sight. + self.spot.LOS=true + + if not self.spot.On then + + local target=nil + + if self.spot.TargetType==0 then + target=self.spot.Coordinate + elseif self.spot.TargetType==1 or self.spot.TargetType==2 then + target=self.spot.TargetUnit + elseif self.spot.TargetType==3 then + target=self.spot.TargetGroup + end + + -- Switch laser back on. + if target then + self:__LaserOn(-1, target) + end + end + +end + +--- Set LASER target. +-- @param #OPSGROUP self +-- @param Wrapper.Positionable#POSITIONABLE Target The target to lase. Can also be a COORDINATE object. +function OPSGROUP:SetLaserTarget(Target) + + if Target then + + -- Check if we have a POSITIONABLE. + if Target:IsInstanceOf("POSITIONABLE") then + local target=Target --Wrapper.Positionable#POSITIONABLE + + if target:IsAlive() then + + if target:IsInstanceOf("GROUP") then + -- We got a GROUP as target. + self.spot.TargetGroup=target + self.spot.TargetUnit=target:GetHighestThreat() + self.spot.TargetType=3 + else + -- We got a UNIT or STATIC as target. + self.spot.TargetUnit=target + if target:IsInstanceOf("STATIC") then + self.spot.TargetType=1 + elseif target:IsInstanceOf("UNIT") then + self.spot.TargetType=2 + end + end + + else + self:E("WARNING: LASER target is not alive!") + return + end + + elseif Target:IsInstanceOf("COORDINATE") then + -- Coordinate as target. + self.spot.TargetType=0 + else + self:E(self.lid.."ERROR: LASER target should be a POSITIONABLE (GROUP, UNIT or STATIC) or a COORDINATE object!") + return + end + + -- Set coordinate. + self.spot.Coordinate=Target:GetCoordinate() + + end + +end + +--- Check if an element of the group has line of sight to a coordinate. +-- @param #OPSGROUP self +-- @param Core.Point#COORDINATE Coordinate The position to which we check the LoS. +-- @param #OPSGROUP.Element Element The (optinal) element. If not given, all elements are checked. +-- @param DCS#Vec3 Offset Offset vector of the element. +-- @return #boolean If `true`, there is line of sight to the specified coordinate. +function OPSGROUP:HasLoS(Coordinate, Element, Offset) + + -- Target vector. + local Vec3=Coordinate:GetVec3() + + --- Function to check LoS for an element of the group. + local function checklos(element) + local vec3=Element.unit:GetVec3() + if Offset then + vec3=UTILS.VecAdd(vec3, Offset) + end + local _los=land.isVisible(vec3, Vec3) + return _los + end + + if Element then + local los=checklos(Element) + return los + else + + for _,element in pairs(self.elements) do + -- Get LoS of this element. + local los=checklos(element) + if los then + return true + end + end + + return false + end + + return nil +end + --- Update laser point. -- @param #OPSGROUP self function OPSGROUP:_UpdateLaser() @@ -3124,7 +3392,7 @@ function OPSGROUP:_UpdateLaser() --- -- LASER is ON - --´- + --- -- Check if we have a POSITIONABLE to lase. if self.spot.TargetUnit then @@ -3166,25 +3434,51 @@ function OPSGROUP:_UpdateLaser() local unit=self.spot.TargetGroup:GetHighestThreat() if unit then - self:I(self.lid.."Switching to target other target unit in the group") + self:I(self.lid..string.format("Switching to target unit %s in the group", unit:GetName())) self.spot.TargetUnit=unit else -- Switch laser off. - self:T(self.lid.."Target is not alive any more ==> switching LASER off") - self:LaserOff() + self:I(self.lid.."Target is not alive any more ==> switching LASER off") + self:LaserOff() + return end else -- Switch laser off. - self:T(self.lid.."Target is not alive any more ==> switching LASER off") + self:I(self.lid.."Target is not alive any more ==> switching LASER off") self:LaserOff() - + return end end end end + + -- Check LOS. + if self.spot.CheckLOS then + + -- Check current LOS. + local los=self:HasLoS(self.spot.Coordinate, self.spot.element, self.spot.offset) + + env.info(string.format("FF check LOS=%s", tostring(los))) + + if los then + -- Got LOS + if not self.spot.LOS then + self:LaserGotLOS() + end + + else + -- No LOS currently + if self.spot.LOS then + self:LaserLostLOS() + end + + end + + end + end @@ -3233,7 +3527,29 @@ function OPSGROUP:onafterElementDead(From, Event, To, Element) -- If there is another element alive, switch laser on again. if self:GetNelements()>0 then - self:__LaserOn(-1, self.spot.Target or self.spot.Coordinate) + + -- New target if any. + local target=nil + + if self.spot.TargetType==0 then + -- Coordinate + target=self.spot.Coordinate + elseif self.spot.TargetType==1 or self.spot.TargetType==2 then + -- Static or unit + if self.spot.TargetUnit and self.spot.TargetUnit:IsAlive() then + target=self.spot.TargetUnit + end + elseif self.spot.TargetType==3 then + -- Group + if self.spot.TargetGroup and self.spot.TargetGroup:IsAlive() then + target=self.spot.TargetGroup + end + end + + -- Switch laser on again. + if target then + self:__LaserOn(-1, target) + end end end @@ -3356,6 +3672,7 @@ function OPSGROUP:_CheckDetectedUnits() local detectedtargets=self.group:GetDetectedTargets() local detected={} + local groups={} for DetectionObjectID, Detection in pairs(detectedtargets or {}) do local DetectedObject=Detection.object -- DCS#Object @@ -3375,9 +3692,22 @@ function OPSGROUP:_CheckDetectedUnits() -- Trigger detected unit event ==> This also triggers the DetectedUnitNew and DetectedUnitKnown events. self:DetectedUnit(unit) + -- Get group of unit. + local group=unit:GetGroup() + + -- Add group to table. + if group then + groups[group:GetName()]=group + end + end end end + + -- Call detected group event. + for groupname, group in pairs(groups) do + self:DetectedGroup(group) + end -- Loop over units in detected set. local lost={} @@ -3403,8 +3733,32 @@ function OPSGROUP:_CheckDetectedUnits() -- Remove lost units from detected set. self.detectedunits:RemoveUnitsByName(lost) - end + -- Loop over groups in detected set. + local lost={} + for _,_group in pairs(self.detectedgroups:GetSet()) do + local group=_group --Wrapper.Group#GROUP + + -- Loop over detected units + local gotit=false + for _,_du in pairs(groups) do + local du=_du --Wrapper.Group#GROUP + if group:GetName()==du:GetName() then + gotit=true + end + end + + if not gotit then + table.insert(lost, group:GetName()) + self:DetectedGroupLost(group) + end + + end + + -- Remove lost units from detected set. + self.detectedgroups:RemoveGroupsByName(lost) + + end end diff --git a/Moose Development/Moose/Wrapper/Unit.lua b/Moose Development/Moose/Wrapper/Unit.lua index fe1c86e15..7f36de4c4 100644 --- a/Moose Development/Moose/Wrapper/Unit.lua +++ b/Moose Development/Moose/Wrapper/Unit.lua @@ -492,8 +492,7 @@ end --- Returns the unit's group if it exist and nil otherwise. -- @param Wrapper.Unit#UNIT self --- @return Wrapper.Group#GROUP The Group of the Unit. --- @return #nil The DCS Unit is not existing or alive. +-- @return Wrapper.Group#GROUP The Group of the Unit or `nil` if the unit does not exist. function UNIT:GetGroup() self:F2( self.UnitName ) From 567f7cbc2941315ed70090e7e9136c19911d36b3 Mon Sep 17 00:00:00 2001 From: Frank Date: Tue, 27 Oct 2020 00:12:59 +0100 Subject: [PATCH 07/10] Update OpsGroup.lua - Laser --- Moose Development/Moose/Ops/OpsGroup.lua | 89 ++++++++++++++++++------ 1 file changed, 69 insertions(+), 20 deletions(-) diff --git a/Moose Development/Moose/Ops/OpsGroup.lua b/Moose Development/Moose/Ops/OpsGroup.lua index 93b12fcf9..3ead74115 100644 --- a/Moose Development/Moose/Ops/OpsGroup.lua +++ b/Moose Development/Moose/Ops/OpsGroup.lua @@ -278,6 +278,7 @@ OPSGROUP.TaskType={ -- @field #OPSGROUP.Element element The element of the group that is lasing. -- @field DCS#Vec3 vec3 The 3D positon vector of the laser (and IR) spot. -- @field DCS#Vec3 offset Local offset of the laser source. +-- @field DCS#Vec3 offsetTarget Offset of the target. -- @field Core.Timer#TIMER timer Spot timer. --- Ammo data. @@ -365,7 +366,8 @@ function OPSGROUP:New(Group) -- Laser. self.spot={} self.spot.On=false - self.spot.timer=TIMER:New(self._UpdateLaser, self) + self.spot.timer=TIMER:New(self._UpdateLaser, self) + self.spot.Coordinate=COORDINATE:New(0, 0, 0) self:SetLaser(1688, true, false, 0.5) -- Init task counter. @@ -3112,14 +3114,12 @@ function OPSGROUP:onbeforeLaserOn(From, Event, To, Target) -- Check LOS. self.spot.LOS=self:HasLoS(self.spot.Coordinate, self.spot.element, self.spot.offset) - - env.info(string.format("FF LOS=%s", tostring(self.spot.LOS))) - + if self.spot.LOS then self:LaserGotLOS() else -- Try to switch laser on again in 10 sec. - env.info("FF no LOS currently. Trying to switch the laser on again in 10 sec.") + self:T(self.lid.."LASER got no LOS currently. Trying to switch the laser on again in 10 sec") self:__LaserOn(-10, Target) return false end @@ -3142,9 +3142,6 @@ end -- @param Core.Point#COORDINATE Target Target Coordinate. Target can also be any POSITIONABLE from which we can obtain its coordinates. function OPSGROUP:onafterLaserOn(From, Event, To, Target) - -- Debug message. - self:I(self.lid.."Switching LASER on") - -- Start timer that calls the update twice per sec by default. self.spot.timer:Start(nil, self.spot.dt) @@ -3159,6 +3156,9 @@ function OPSGROUP:onafterLaserOn(From, Event, To, Target) -- Laser is on. self.spot.On=true + + -- Debug message. + self:T(self.lid.."Switching LASER on") end @@ -3179,7 +3179,7 @@ end function OPSGROUP:onafterLaserOff(From, Event, To) -- Debug message. - self:I(self.lid.."Switching LASER off") + self:T(self.lid.."Switching LASER off") -- "Destroy" the laser beam. self.spot.Laser:destroy() @@ -3206,7 +3206,7 @@ end function OPSGROUP:onafterLaserPause(From, Event, To) -- Debug message. - self:I(self.lid.."Switching LASER off temporarily.") + self:I(self.lid.."Switching LASER off temporarily") -- "Destroy" the laser beam. self.spot.Laser:destroy() @@ -3312,7 +3312,7 @@ function OPSGROUP:SetLaserTarget(Target) -- We got a GROUP as target. self.spot.TargetGroup=target self.spot.TargetUnit=target:GetHighestThreat() - self.spot.TargetType=3 + self.spot.TargetType=3 else -- We got a UNIT or STATIC as target. self.spot.TargetUnit=target @@ -3322,6 +3322,17 @@ function OPSGROUP:SetLaserTarget(Target) self.spot.TargetType=2 end end + + -- Get object size. + local size,x,y,z=self.spot.TargetUnit:GetObjectSize() + + if y then + self.spot.offsetTarget={x=0, y=y/2, z=0} + else + self.spot.offsetTarget={x=0, 2, z=0} + end + + --env.info(string.format("Target offset %.3f", y)) else self:E("WARNING: LASER target is not alive!") @@ -3331,36 +3342,71 @@ function OPSGROUP:SetLaserTarget(Target) elseif Target:IsInstanceOf("COORDINATE") then -- Coordinate as target. self.spot.TargetType=0 + self.spot.offsetTarget={x=0, y=0, z=0} else self:E(self.lid.."ERROR: LASER target should be a POSITIONABLE (GROUP, UNIT or STATIC) or a COORDINATE object!") return end + + -- Set vec3 and account for target offset. + self.spot.vec3=UTILS.VecAdd(Target:GetVec3(), self.spot.offsetTarget) + -- Set coordinate. - self.spot.Coordinate=Target:GetCoordinate() + self.spot.Coordinate:UpdateFromVec3(self.spot.vec3) end end +--- Get highest threat. +-- @param #OPSGROUP self +-- @return Wrapper.Unit#UNIT The highest threat unit. +-- @return #number Threat level of the unit. +function OPSGROUP:GetHighestThreat() + + local threat=nil + local levelmax=-1 + for _,_unit in pairs(self.detectedunits:GetSet()) do + local unit=_unit --Wrapper.Unit#UNIT + + local threatlevel=unit:GetThreatLevel() + + if threatlevel>levelmax then + threat=unit + levelmax=threatlevel + end + + end + + return threat, levelmax +end + --- Check if an element of the group has line of sight to a coordinate. -- @param #OPSGROUP self -- @param Core.Point#COORDINATE Coordinate The position to which we check the LoS. -- @param #OPSGROUP.Element Element The (optinal) element. If not given, all elements are checked. --- @param DCS#Vec3 Offset Offset vector of the element. +-- @param DCS#Vec3 OffsetElement Offset vector of the element. +-- @param DCS#Vec3 OffsetCoordinate Offset vector of the coordinate. -- @return #boolean If `true`, there is line of sight to the specified coordinate. -function OPSGROUP:HasLoS(Coordinate, Element, Offset) +function OPSGROUP:HasLoS(Coordinate, Element, OffsetElement, OffsetCoordinate) -- Target vector. local Vec3=Coordinate:GetVec3() - + + -- Optional offset. + if OffsetCoordinate then + Vec3=UTILS.VecAdd(vec3, OffsetCoordinate) + end + --- Function to check LoS for an element of the group. local function checklos(element) - local vec3=Element.unit:GetVec3() - if Offset then - vec3=UTILS.VecAdd(vec3, Offset) - end + local vec3=element.unit:GetVec3() + if OffsetElement then + vec3=UTILS.VecAdd(vec3, OffsetElement) + end local _los=land.isVisible(vec3, Vec3) + --self:I({los=_los, source=vec3, target=Vec3}) return _los end @@ -3406,6 +3452,9 @@ function OPSGROUP:_UpdateLaser() -- Get current target position. local vec3=self.spot.TargetUnit:GetVec3() + -- Add target offset. + vec3=UTILS.VecAdd(vec3, self.spot.offsetTarget) + -- Calculate distance local dist=UTILS.VecDist3D(vec3, self.spot.vec3) @@ -3461,7 +3510,7 @@ function OPSGROUP:_UpdateLaser() -- Check current LOS. local los=self:HasLoS(self.spot.Coordinate, self.spot.element, self.spot.offset) - env.info(string.format("FF check LOS=%s", tostring(los))) + --env.info(string.format("FF check LOS=%s", tostring(los))) if los then -- Got LOS From ac337b845910ae058b1e28473f3a84231d3c3567 Mon Sep 17 00:00:00 2001 From: Frank Date: Wed, 28 Oct 2020 09:06:22 +0100 Subject: [PATCH 08/10] Ops - Laser --- Moose Development/Moose/Core/Timer.lua | 19 +- Moose Development/Moose/Ops/OpsGroup.lua | 372 +++++++++++++---------- 2 files changed, 232 insertions(+), 159 deletions(-) diff --git a/Moose Development/Moose/Core/Timer.lua b/Moose Development/Moose/Core/Timer.lua index 26f809c1d..258996d68 100644 --- a/Moose Development/Moose/Core/Timer.lua +++ b/Moose Development/Moose/Core/Timer.lua @@ -27,6 +27,7 @@ -- @field #number dT Time interval between function calls in seconds. -- @field #number ncalls Counter of function calls. -- @field #number ncallsMax Max number of function calls. If reached, timer is stopped. +-- @field #boolean isrunning If `true`, timer is running. Else it was not started yet or was stopped. -- @extends Core.Base#BASE --- *Better three hours too soon than a minute too late.* - William Shakespeare @@ -111,7 +112,7 @@ _TIMERID=0 --- TIMER class version. -- @field #string version -TIMER.version="0.1.0" +TIMER.version="0.1.1" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list @@ -143,6 +144,9 @@ function TIMER:New(Function, ...) -- Number of function calls. self.ncalls=0 + -- Not running yet. + self.isrunning=false + -- Increase counter _TIMERID=_TIMERID+1 @@ -186,6 +190,9 @@ function TIMER:Start(Tstart, dT, Duration) -- Set log id. self.lid=string.format("TIMER UID=%d/%d | ", self.uid, self.tid) + -- Is now running. + self.isrunning=true + -- Debug info. self:T(self.lid..string.format("Starting Timer in %.3f sec, dT=%s, Tstop=%s", self.Tstart-Tnow, tostring(self.dT), tostring(self.Tstop))) @@ -210,6 +217,9 @@ function TIMER:Stop(Delay) self:T(self.lid..string.format("Stopping timer by removing timer function after %d calls!", self.ncalls)) timer.removeFunction(self.tid) + -- Not running any more. + self.isrunning=false + -- Remove DB entry. --_TIMERDB[self.uid]=nil @@ -229,6 +239,13 @@ function TIMER:SetMaxFunctionCalls(Nmax) return self end +--- Check if the timer has been started and was not stopped. +-- @param #TIMER self +-- @return #boolean If `true`, the timer is running. +function TIMER:IsRunning() + return self.isrunning +end + --- Call timer function. -- @param #TIMER self -- @param #number time DCS model time in seconds. diff --git a/Moose Development/Moose/Ops/OpsGroup.lua b/Moose Development/Moose/Ops/OpsGroup.lua index 3ead74115..97ef840df 100644 --- a/Moose Development/Moose/Ops/OpsGroup.lua +++ b/Moose Development/Moose/Ops/OpsGroup.lua @@ -275,6 +275,8 @@ OPSGROUP.TaskType={ -- @field Core.Point#COORDINATE Coordinate where the spot is pointing. -- @field #number TargetType Type of target: 0=coordinate, 1=static, 2=unit, 3=group. -- @field #boolean On If true, the laser is on. +-- @field #boolean Paused If true, laser is paused. +-- @field #boolean lostLOS If true, laser lost LOS. -- @field #OPSGROUP.Element element The element of the group that is lasing. -- @field DCS#Vec3 vec3 The 3D positon vector of the laser (and IR) spot. -- @field DCS#Vec3 offset Local offset of the laser source. @@ -415,6 +417,7 @@ function OPSGROUP:New(Group) self:AddTransition("*", "LaserOff", "*") -- Turn laser off. self:AddTransition("*", "LaserCode", "*") -- Switch laser code. self:AddTransition("*", "LaserPause", "*") -- Turn laser off temporarily. + self:AddTransition("*", "LaserResume", "*") -- Turn laser back on again if it was paused. self:AddTransition("*", "LaserLostLOS", "*") -- Lasing element lost line of sight. self:AddTransition("*", "LaserGotLOS", "*") -- Lasing element got line of sight. @@ -671,6 +674,76 @@ function OPSGROUP:GetThreat(ThreatLevelMin, ThreatLevelMax) return threat, level end +--- Get highest threat. +-- @param #OPSGROUP self +-- @return Wrapper.Unit#UNIT The highest threat unit. +-- @return #number Threat level of the unit. +function OPSGROUP:GetHighestThreat() + + local threat=nil + local levelmax=-1 + for _,_unit in pairs(self.detectedunits:GetSet()) do + local unit=_unit --Wrapper.Unit#UNIT + + local threatlevel=unit:GetThreatLevel() + + if threatlevel>levelmax then + threat=unit + levelmax=threatlevel + end + + end + + return threat, levelmax +end + +--- Check if an element of the group has line of sight to a coordinate. +-- @param #OPSGROUP self +-- @param Core.Point#COORDINATE Coordinate The position to which we check the LoS. +-- @param #OPSGROUP.Element Element The (optinal) element. If not given, all elements are checked. +-- @param DCS#Vec3 OffsetElement Offset vector of the element. +-- @param DCS#Vec3 OffsetCoordinate Offset vector of the coordinate. +-- @return #boolean If `true`, there is line of sight to the specified coordinate. +function OPSGROUP:HasLoS(Coordinate, Element, OffsetElement, OffsetCoordinate) + + -- Target vector. + local Vec3=Coordinate:GetVec3() + + -- Optional offset. + if OffsetCoordinate then + Vec3=UTILS.VecAdd(Vec3, OffsetCoordinate) + end + + --- Function to check LoS for an element of the group. + local function checklos(element) + local vec3=element.unit:GetVec3() + if OffsetElement then + vec3=UTILS.VecAdd(vec3, OffsetElement) + end + local _los=land.isVisible(vec3, Vec3) + --self:I({los=_los, source=vec3, target=Vec3}) + return _los + end + + if Element then + local los=checklos(Element) + return los + else + + for _,element in pairs(self.elements) do + -- Get LoS of this element. + local los=checklos(element) + if los then + return true + end + end + + return false + end + + return nil +end + --- Get MOOSE GROUP object. -- @param #OPSGROUP self -- @return Wrapper.Group#GROUP Moose group object. @@ -3085,7 +3158,7 @@ function OPSGROUP:onbeforeLaserOn(From, Event, To, Target) else -- No target specified. - --TODO: error message + self:E(self.lid.."ERROR: No target provided for LASER!") return false end @@ -3095,10 +3168,7 @@ function OPSGROUP:onbeforeLaserOn(From, Event, To, Target) if element then -- Set element. - self.spot.element=element - - -- Vec3. - self.spot.vec3=self.spot.Coordinate:GetVec3() + self.spot.element=element -- Height offset. No offset for aircraft. We take the height for ground or naval. local offsetY=0 @@ -3113,13 +3183,15 @@ function OPSGROUP:onbeforeLaserOn(From, Event, To, Target) if self.spot.CheckLOS then -- Check LOS. - self.spot.LOS=self:HasLoS(self.spot.Coordinate, self.spot.element, self.spot.offset) + local los=self:HasLoS(self.spot.Coordinate, self.spot.element, self.spot.offset) + + --self:I({los=los, coord=self.spot.Coordinate, offset=self.spot.offset}) - if self.spot.LOS then + if los then self:LaserGotLOS() else -- Try to switch laser on again in 10 sec. - self:T(self.lid.."LASER got no LOS currently. Trying to switch the laser on again in 10 sec") + self:I(self.lid.."LASER got no LOS currently. Trying to switch the laser on again in 10 sec") self:__LaserOn(-10, Target) return false end @@ -3143,7 +3215,9 @@ end function OPSGROUP:onafterLaserOn(From, Event, To, Target) -- Start timer that calls the update twice per sec by default. - self.spot.timer:Start(nil, self.spot.dt) + if not self.spot.timer:IsRunning() then + self.spot.timer:Start(nil, self.spot.dt) + end -- Get DCS unit. local DCSunit=self.spot.element.unit:GetDCSObject() @@ -3156,6 +3230,9 @@ function OPSGROUP:onafterLaserOn(From, Event, To, Target) -- Laser is on. self.spot.On=true + + -- No paused in case it was. + self.spot.Paused=false -- Debug message. self:T(self.lid.."Switching LASER on") @@ -3168,7 +3245,7 @@ end -- @param #string Event Event. -- @param #string To To state. function OPSGROUP:onbeforeLaserOff(From, Event, To) - return self.spot.On + return self.spot.On or self.spot.Paused end --- On after "LaserOff" event. @@ -3182,20 +3259,26 @@ function OPSGROUP:onafterLaserOff(From, Event, To) self:T(self.lid.."Switching LASER off") -- "Destroy" the laser beam. - self.spot.Laser:destroy() - self.spot.IR:destroy() + if self.spot.On then + self.spot.Laser:destroy() + self.spot.IR:destroy() + + -- Set to nil. + self.spot.Laser=nil + self.spot.IR=nil + end -- Stop update timer. self.spot.timer:Stop() - -- Set to nil. - self.spot.Laser=nil - self.spot.IR=nil + -- No target unit. self.spot.TargetUnit=nil -- Laser is off. self.spot.On=false + -- Not paused if it was. + self.spot.Paused=false end --- On after "LaserPause" event. @@ -3219,6 +3302,52 @@ function OPSGROUP:onafterLaserPause(From, Event, To) -- Laser is off. self.spot.On=false + -- Laser is paused. + self.spot.Paused=true + +end + +--- On before "LaserResume" event. +-- @param #OPSGROUP self +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +function OPSGROUP:onbeforeLaserResume(From, Event, To) + return self.spot.Paused +end + +--- On after "LaserResume" event. +-- @param #OPSGROUP self +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +function OPSGROUP:onafterLaserResume(From, Event, To) + + -- Debug info. + self:I(self.lid.."Resuming LASER") + + -- Unset paused. + self.spot.Paused=false + + -- Set target. + local target=nil + if self.spot.TargetType==0 then + target=self.spot.Coordinate + elseif self.spot.TargetType==1 or self.spot.TargetType==2 then + target=self.spot.TargetUnit + elseif self.spot.TargetType==3 then + target=self.spot.TargetGroup + end + + -- Switch laser back on. + if target then + + -- Debug message. + self:I(self.lid.."Switching LASER on again at target ".. target:GetName()) + + self:LaserOn(target) + end + end --- On after "LaserCode" event. Changes the LASER code. @@ -3253,8 +3382,11 @@ end -- @param #string To To state. function OPSGROUP:onafterLaserLostLOS(From, Event, To) - -- Lost line of sight. + -- No of sight. self.spot.LOS=false + + -- Lost line of sight. + self.spot.lostLOS=true if self.spot.On then @@ -3272,25 +3404,24 @@ end -- @param #string To To state. function OPSGROUP:onafterLaserGotLOS(From, Event, To) - -- Lost line of sight. + -- Has line of sight. self.spot.LOS=true - - if not self.spot.On then - local target=nil + env.info("FF Laser Got LOS") + + if self.spot.lostLOS then + + -- Did not loose LOS anymore. + self.spot.lostLOS=false - if self.spot.TargetType==0 then - target=self.spot.Coordinate - elseif self.spot.TargetType==1 or self.spot.TargetType==2 then - target=self.spot.TargetUnit - elseif self.spot.TargetType==3 then - target=self.spot.TargetGroup + env.info("FF had lost LOS and regained it") + + -- Resume laser if currently paused. + if self.spot.Paused then + env.info("FF laser was paused ==> resume") + self:LaserResume() end - -- Switch laser back on. - if target then - self:__LaserOn(-1, target) - end end end @@ -3327,7 +3458,7 @@ function OPSGROUP:SetLaserTarget(Target) local size,x,y,z=self.spot.TargetUnit:GetObjectSize() if y then - self.spot.offsetTarget={x=0, y=y/2, z=0} + self.spot.offsetTarget={x=0, y=y*0.75, z=0} else self.spot.offsetTarget={x=0, 2, z=0} end @@ -3347,161 +3478,85 @@ function OPSGROUP:SetLaserTarget(Target) self:E(self.lid.."ERROR: LASER target should be a POSITIONABLE (GROUP, UNIT or STATIC) or a COORDINATE object!") return end - - + -- Set vec3 and account for target offset. self.spot.vec3=UTILS.VecAdd(Target:GetVec3(), self.spot.offsetTarget) -- Set coordinate. - self.spot.Coordinate:UpdateFromVec3(self.spot.vec3) - + self.spot.Coordinate:UpdateFromVec3(self.spot.vec3) end end ---- Get highest threat. --- @param #OPSGROUP self --- @return Wrapper.Unit#UNIT The highest threat unit. --- @return #number Threat level of the unit. -function OPSGROUP:GetHighestThreat() - - local threat=nil - local levelmax=-1 - for _,_unit in pairs(self.detectedunits:GetSet()) do - local unit=_unit --Wrapper.Unit#UNIT - - local threatlevel=unit:GetThreatLevel() - - if threatlevel>levelmax then - threat=unit - levelmax=threatlevel - end - - end - - return threat, levelmax -end - ---- Check if an element of the group has line of sight to a coordinate. --- @param #OPSGROUP self --- @param Core.Point#COORDINATE Coordinate The position to which we check the LoS. --- @param #OPSGROUP.Element Element The (optinal) element. If not given, all elements are checked. --- @param DCS#Vec3 OffsetElement Offset vector of the element. --- @param DCS#Vec3 OffsetCoordinate Offset vector of the coordinate. --- @return #boolean If `true`, there is line of sight to the specified coordinate. -function OPSGROUP:HasLoS(Coordinate, Element, OffsetElement, OffsetCoordinate) - - -- Target vector. - local Vec3=Coordinate:GetVec3() - - -- Optional offset. - if OffsetCoordinate then - Vec3=UTILS.VecAdd(vec3, OffsetCoordinate) - end - - --- Function to check LoS for an element of the group. - local function checklos(element) - local vec3=element.unit:GetVec3() - if OffsetElement then - vec3=UTILS.VecAdd(vec3, OffsetElement) - end - local _los=land.isVisible(vec3, Vec3) - --self:I({los=_los, source=vec3, target=Vec3}) - return _los - end - - if Element then - local los=checklos(Element) - return los - else - - for _,element in pairs(self.elements) do - -- Get LoS of this element. - local los=checklos(element) - if los then - return true - end - end - - return false - end - - return nil -end - --- Update laser point. -- @param #OPSGROUP self function OPSGROUP:_UpdateLaser() - -- Firstly, check if laser is on. - if self.spot.On then + -- Check if we have a POSITIONABLE to lase. + if self.spot.TargetUnit then --- - -- LASER is ON + -- Lasing a possibly moving target --- - -- Check if we have a POSITIONABLE to lase. - if self.spot.TargetUnit then - - --- - -- Lasing a possibly moving target - --- - - if self.spot.TargetUnit:IsAlive() then + if self.spot.TargetUnit:IsAlive() then - -- Get current target position. - local vec3=self.spot.TargetUnit:GetVec3() - - -- Add target offset. - vec3=UTILS.VecAdd(vec3, self.spot.offsetTarget) - - -- Calculate distance - local dist=UTILS.VecDist3D(vec3, self.spot.vec3) - - -- Update laser if target moved more than one meter. - if dist>1 then - - -- Store current position. - self.spot.vec3=vec3 - - -- Update beam coordinate. - self.spot.Coordinate:UpdateFromVec3(vec3) - - -- Set the new laser target point. + -- Get current target position. + local vec3=self.spot.TargetUnit:GetVec3() + + -- Add target offset. + vec3=UTILS.VecAdd(vec3, self.spot.offsetTarget) + + -- Calculate distance + local dist=UTILS.VecDist3D(vec3, self.spot.vec3) + + -- Store current position. + self.spot.vec3=vec3 + + -- Update beam coordinate. + self.spot.Coordinate:UpdateFromVec3(vec3) + + -- Update laser if target moved more than one meter. + if dist>1 then + + -- If the laser is ON, set the new laser target point. + if self.spot.On then self.spot.Laser:setPoint(vec3) if self.spot.IRon then self.spot.IR:setPoint(vec3) end - end - else + end - if self.spot.TargetGroup and self.spot.TargetGroup:IsAlive() then - - -- Get first alive unit in the group. - local unit=self.spot.TargetGroup:GetHighestThreat() - - if unit then - self:I(self.lid..string.format("Switching to target unit %s in the group", unit:GetName())) - self.spot.TargetUnit=unit - else - -- Switch laser off. - self:I(self.lid.."Target is not alive any more ==> switching LASER off") - self:LaserOff() - return - end + else + + if self.spot.TargetGroup and self.spot.TargetGroup:IsAlive() then + + -- Get first alive unit in the group. + local unit=self.spot.TargetGroup:GetHighestThreat() + if unit then + self:I(self.lid..string.format("Switching to target unit %s in the group", unit:GetName())) + self.spot.TargetUnit=unit + -- We update the laser position in the next update cycle and then check the LOS. + return else - -- Switch laser off. self:I(self.lid.."Target is not alive any more ==> switching LASER off") self:LaserOff() - return + return end - end - end + else + + -- Switch laser off. + self:I(self.lid.."Target is not alive any more ==> switching LASER off") + self:LaserOff() + return + end + + end end -- Check LOS. @@ -3510,17 +3565,18 @@ function OPSGROUP:_UpdateLaser() -- Check current LOS. local los=self:HasLoS(self.spot.Coordinate, self.spot.element, self.spot.offset) - --env.info(string.format("FF check LOS=%s", tostring(los))) + env.info(string.format("FF check LOS current=%s previous=%s", tostring(los), tostring(self.spot.LOS))) if los then -- Got LOS - if not self.spot.LOS then + if self.spot.lostLOS then + --self:I({los=self.spot.LOS, coord=self.spot.Coordinate, offset=self.spot.offset}) self:LaserGotLOS() end else -- No LOS currently - if self.spot.LOS then + if not self.spot.lostLOS then self:LaserLostLOS() end From cd5b4ee1be5924efffae0cf7969b069ab2d5d3f8 Mon Sep 17 00:00:00 2001 From: Frank Date: Wed, 28 Oct 2020 09:44:33 +0100 Subject: [PATCH 09/10] Update AI_Formation.lua - fixed bug - set default mode to following --- Moose Development/Moose/AI/AI_Formation.lua | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Formation.lua b/Moose Development/Moose/AI/AI_Formation.lua index c1bfe5757..17c1b5345 100644 --- a/Moose Development/Moose/AI/AI_Formation.lua +++ b/Moose Development/Moose/AI/AI_Formation.lua @@ -184,11 +184,13 @@ function AI_FORMATION:New( FollowUnit, FollowGroupSet, FollowName, FollowBriefin self.FollowGroupSet:ForEachGroup( function( FollowGroup ) - self:E("Following") + --self:E("Following") FollowGroup:SetState( self, "Mode", self.__Enum.Mode.Formation ) end ) + self:SetFlightModeFormation() + self:SetFlightRandomization( 2 ) self:SetStartState( "None" ) @@ -997,7 +999,7 @@ function AI_FORMATION:SetFlightModeMission( FollowGroup ) FollowGroup:SetState( FollowGroup, "PreviousMode", FollowGroup:GetState( FollowGroup, "Mode" ) ) FollowGroup:SetState( FollowGroup, "Mode", self.__Enum.Mode.Mission ) else - self.EscortGroupSet:ForSomeGroupAlive( + self.FollowGroupSet:ForSomeGroupAlive( --- @param Core.Group#GROUP EscortGroup function( FollowGroup ) FollowGroup:SetState( FollowGroup, "PreviousMode", FollowGroup:GetState( FollowGroup, "Mode" ) ) @@ -1021,7 +1023,7 @@ function AI_FORMATION:SetFlightModeAttack( FollowGroup ) FollowGroup:SetState( FollowGroup, "PreviousMode", FollowGroup:GetState( FollowGroup, "Mode" ) ) FollowGroup:SetState( FollowGroup, "Mode", self.__Enum.Mode.Attack ) else - self.EscortGroupSet:ForSomeGroupAlive( + self.FollowGroupSet:ForSomeGroupAlive( --- @param Core.Group#GROUP EscortGroup function( FollowGroup ) FollowGroup:SetState( FollowGroup, "PreviousMode", FollowGroup:GetState( FollowGroup, "Mode" ) ) @@ -1045,7 +1047,7 @@ function AI_FORMATION:SetFlightModeFormation( FollowGroup ) FollowGroup:SetState( FollowGroup, "PreviousMode", FollowGroup:GetState( FollowGroup, "Mode" ) ) FollowGroup:SetState( FollowGroup, "Mode", self.__Enum.Mode.Formation ) else - self.EscortGroupSet:ForSomeGroupAlive( + self.FollowGroupSet:ForSomeGroupAlive( --- @param Core.Group#GROUP EscortGroup function( FollowGroup ) FollowGroup:SetState( FollowGroup, "PreviousMode", FollowGroup:GetState( FollowGroup, "Mode" ) ) From 0948f418cd1213f2fb7fd3f3a201db8d96d35daf Mon Sep 17 00:00:00 2001 From: Frank Date: Sat, 31 Oct 2020 11:57:23 +0100 Subject: [PATCH 10/10] Ops - Fixed payload not reduced --- Moose Development/Moose/Ops/AirWing.lua | 14 +++++++-- Moose Development/Moose/Ops/ArmyGroup.lua | 6 ++-- Moose Development/Moose/Ops/Auftrag.lua | 3 +- Moose Development/Moose/Ops/OpsGroup.lua | 36 +++++++++++++---------- 4 files changed, 38 insertions(+), 21 deletions(-) diff --git a/Moose Development/Moose/Ops/AirWing.lua b/Moose Development/Moose/Ops/AirWing.lua index 9a768dd73..0ea78cdc2 100644 --- a/Moose Development/Moose/Ops/AirWing.lua +++ b/Moose Development/Moose/Ops/AirWing.lua @@ -152,7 +152,7 @@ AIRWING = { --- AIRWING class version. -- @field #string version -AIRWING.version="0.5.0" +AIRWING.version="0.5.1" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- ToDo list @@ -472,11 +472,19 @@ function AIRWING:FetchPayloadFromStock(UnitType, MissionType, Payloads) return nil elseif #payloads==1 then -- Only one payload anyway. - return payloads[1] + local payload=payloads[1] --#AIRWING.Payload + if not payload.unlimited then + payload.navail=payload.navail-1 + end + return payload else -- Sort payloads. table.sort(payloads, sortpayloads) - return payloads[1] + local payload=payloads[1] --#AIRWING.Payload + if not payload.unlimited then + payload.navail=payload.navail-1 + end + return payload end end diff --git a/Moose Development/Moose/Ops/ArmyGroup.lua b/Moose Development/Moose/Ops/ArmyGroup.lua index 423d2463d..e834f8d4d 100644 --- a/Moose Development/Moose/Ops/ArmyGroup.lua +++ b/Moose Development/Moose/Ops/ArmyGroup.lua @@ -68,9 +68,11 @@ ARMYGROUP.version="0.3.0" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - + +-- TODO: Suppression of fire. -- TODO: Check if group is mobile. --- TODO: Rearm. Specify a point where to go and wait until ammo is full. +-- TODO: F10 menu. +-- DONE: Rearm. Specify a point where to go and wait until ammo is full. ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Constructor diff --git a/Moose Development/Moose/Ops/Auftrag.lua b/Moose Development/Moose/Ops/Auftrag.lua index 08bab7e77..1153ba5f6 100644 --- a/Moose Development/Moose/Ops/Auftrag.lua +++ b/Moose Development/Moose/Ops/Auftrag.lua @@ -1957,7 +1957,7 @@ function AUFTRAG:IsReadyToCancel() return true end - + -- Evaluate failure condition. One is enough. local failure=self:EvalConditionsAny(self.conditionFailure) if failure then @@ -1965,6 +1965,7 @@ function AUFTRAG:IsReadyToCancel() return true end + -- Evaluate success consitions. One is enough. local success=self:EvalConditionsAny(self.conditionSuccess) if success then diff --git a/Moose Development/Moose/Ops/OpsGroup.lua b/Moose Development/Moose/Ops/OpsGroup.lua index 97ef840df..bfc737cf7 100644 --- a/Moose Development/Moose/Ops/OpsGroup.lua +++ b/Moose Development/Moose/Ops/OpsGroup.lua @@ -320,15 +320,15 @@ OPSGROUP.TaskType={ --- NavyGroup version. -- @field #string version -OPSGROUP.version="0.6.0" +OPSGROUP.version="0.7.0" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- TODO: Suppression of fire. -- TODO: AI on/off. -- TODO: Invisible/immortal. +-- TODO: F10 menu. -- TODO: Add pseudo function. ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -525,13 +525,13 @@ end --- Set LASER parameters. -- @param #OPSGROUP self -- @param #number Code Laser code. Default 1688. --- @param #boolean CheckLOS Check if lasing unit has line of sight to target coordinate. +-- @param #boolean CheckLOS Check if lasing unit has line of sight to target coordinate. Default is `true`. -- @param #boolean IROff If true, then dont switch on the additional IR pointer. -- @param #number UpdateTime Time interval in seconds the beam gets up for moving targets. Default every 0.5 sec. -- @return #OPSGROUP self function OPSGROUP:SetLaser(Code, CheckLOS, IROff, UpdateTime) self.spot.Code=Code or 1688 - self.spot.CheckLOS=CheckLOS + self.spot.CheckLOS=CheckLOS and CheckLOS or true self.spot.IRon=not IROff self.spot.dt=UpdateTime or 0.5 return self @@ -3051,8 +3051,7 @@ function OPSGROUP:onafterDetectedUnit(From, Event, To, Unit) -- Debug. self:T2(self.lid..string.format("Detected unit %s", unitname)) - - + if self.detectedunits:FindUnit(unitname) then -- Unit is already in the detected unit set ==> Trigger "DetectedUnitKnown" event. self:DetectedUnitKnown(Unit) @@ -3070,6 +3069,8 @@ end -- @param #string To To state. -- @param Wrapper.Unit#UNIT Unit The detected unit. function OPSGROUP:onafterDetectedUnitNew(From, Event, To, Unit) + + -- Debug info. self:T(self.lid..string.format("Detected New unit %s", Unit:GetName())) -- Add unit to detected unit set. @@ -3087,10 +3088,9 @@ function OPSGROUP:onafterDetectedGroup(From, Event, To, Group) -- Get group name. local groupname=Group and Group:GetName() or "unknown" - -- Debug. - self:I(self.lid..string.format("Detected group %s", groupname)) - - + -- Debug info. + self:T(self.lid..string.format("Detected group %s", groupname)) + if self.detectedgroups:FindGroup(groupname) then -- Group is already in the detected set ==> Trigger "DetectedGroupKnown" event. self:DetectedGroupKnown(Group) @@ -3108,7 +3108,9 @@ end -- @param #string To To state. -- @param Wrapper.Group#GROUP Group The detected group. function OPSGROUP:onafterDetectedGroupNew(From, Event, To, Group) - self:I(self.lid..string.format("Detected New group %s", Group:GetName())) + + -- Debug info. + self:T(self.lid..string.format("Detected New group %s", Group:GetName())) -- Add unit to detected unit set. self.detectedgroups:AddGroup(Group) @@ -3382,6 +3384,8 @@ end -- @param #string To To state. function OPSGROUP:onafterLaserLostLOS(From, Event, To) + --env.info("FF lost LOS") + -- No of sight. self.spot.LOS=false @@ -3389,6 +3393,8 @@ function OPSGROUP:onafterLaserLostLOS(From, Event, To) self.spot.lostLOS=true if self.spot.On then + + --env.info("FF lost LOS ==> pause laser") -- Switch laser off. self:LaserPause() @@ -3407,18 +3413,18 @@ function OPSGROUP:onafterLaserGotLOS(From, Event, To) -- Has line of sight. self.spot.LOS=true - env.info("FF Laser Got LOS") + --env.info("FF Laser Got LOS") if self.spot.lostLOS then -- Did not loose LOS anymore. self.spot.lostLOS=false - env.info("FF had lost LOS and regained it") + --env.info("FF had lost LOS and regained it") -- Resume laser if currently paused. if self.spot.Paused then - env.info("FF laser was paused ==> resume") + --env.info("FF laser was paused ==> resume") self:LaserResume() end @@ -3565,7 +3571,7 @@ function OPSGROUP:_UpdateLaser() -- Check current LOS. local los=self:HasLoS(self.spot.Coordinate, self.spot.element, self.spot.offset) - env.info(string.format("FF check LOS current=%s previous=%s", tostring(los), tostring(self.spot.LOS))) + --env.info(string.format("FF check LOS current=%s previous=%s", tostring(los), tostring(self.spot.LOS))) if los then -- Got LOS