diff --git a/Moose Development/Moose/Ops/FlightGroup.lua b/Moose Development/Moose/Ops/FlightGroup.lua index c7b32e0eb..8544a3b4b 100644 --- a/Moose Development/Moose/Ops/FlightGroup.lua +++ b/Moose Development/Moose/Ops/FlightGroup.lua @@ -196,17 +196,6 @@ FLIGHTGROUP.Attribute = { -- @field #number damage Damage of element in percent. -- @field Wrapper.Airbase#AIRBASE.ParkingSpot parking The parking spot table the element is parking on. ---- Ammo data. --- @type FLIGHTGROUP.Ammo --- @field #number Total Total amount of ammo. --- @field #number Guns Amount of gun shells. --- @field #number Bombs Amount of bombs. --- @field #number Rockets Amount of rockets. --- @field #number Missiles Amount of missiles. --- @field #number MissilesAA Amount of air-to-air missiles. --- @field #number MissilesAG Amount of air-to-ground missiles. --- @field #number MissilesAS Amount of anti-ship missiles. - --- FLIGHTGROUP class version. -- @field #string version @@ -1054,67 +1043,7 @@ function FLIGHTGROUP:onafterStatus(From, Event, To) end end -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- Queue Update: Missions & Tasks -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---- On after "QueueUpdate" event. --- @param #FLIGHTGROUP self --- @param #string From From state. --- @param #string Event Event. --- @param #string To To state. -function FLIGHTGROUP:onafterQueueUpdate(From, Event, To) - - --- - -- Mission - --- - - -- First check if group is alive? Late activated groups are activated and uncontrolled units are started automatically. - if self:IsAlive()~=nil then - - local mission=self:_GetNextMission() - - if mission then - - local currentmission=self:GetMissionCurrent() - - if currentmission then - - -- Current mission but new mission is urgent with higher prio. - if mission.urgent and mission.prio0 then -- Delayed call. - self:ScheduleOnce(delay, FLIGHTGROUP._CheckFlightDone, self) + self:ScheduleOnce(delay, FLIGHTGROUP._CheckGroupDone, self) else -- First check if there is a paused mission that @@ -2424,7 +2352,7 @@ function FLIGHTGROUP:onafterRefueled(From, Event, To) self:I(self.lid..text) -- Check if flight is done. - self:_CheckFlightDone(1) + self:_CheckGroupDone(1) end @@ -3209,7 +3137,7 @@ function FLIGHTGROUP:InitWaypoints(waypoints) end -- Update route (when airborne). - --self:_CheckFlightDone(1) + --self:_CheckGroupDone(1) self:__UpdateRoute(-1) end @@ -3271,7 +3199,7 @@ function FLIGHTGROUP:AddWaypointAir(coordinate, wpnumber, speed, updateroute) -- Update route. if updateroute==nil or updateroute==true then - self:_CheckFlightDone(1) + self:_CheckGroupDone(1) --self:__UpdateRoute(-1) end @@ -3366,211 +3294,6 @@ function FLIGHTGROUP:_GetOnboardNumber(unitname) return nil end ---- Get the number of shells a unit or group currently has. For a group the ammo count of all units is summed up. --- @param #FLIGHTGROUP self --- @param #FLIGHTGROUP.Element element The element. --- @return #FLIGHTGROUP.Ammo Ammo data. -function FLIGHTGROUP:GetAmmoElement(element) - return self:GetAmmoUnit(element.unit) -end - ---- Get total amount of ammunition of the whole group. --- @param #FLIGHTGROUP self --- @return #FLIGHTGROUP.Ammo Ammo data. -function FLIGHTGROUP:GetAmmoTot() - - local units=self.group:GetUnits() - - local Ammo={} --#FLIGHTGROUP.Ammo - Ammo.Total=0 - Ammo.Guns=0 - Ammo.Rockets=0 - Ammo.Bombs=0 - Ammo.Missiles=0 - Ammo.MissilesAA=0 - Ammo.MissilesAG=0 - Ammo.MissilesAS=0 - - for _,_unit in pairs(units) do - local unit=_unit --Wrapper.Unit#UNIT - - if unit and unit:IsAlive()~=nil then - - -- Get ammo of the unit. - local ammo=self:GetAmmoUnit(unit) - - -- Add up total. - Ammo.Total=Ammo.Total+ammo.Total - Ammo.Guns=Ammo.Guns+ammo.Guns - Ammo.Rockets=Ammo.Rockets+ammo.Rockets - Ammo.Bombs=Ammo.Bombs+ammo.Bombs - Ammo.Missiles=Ammo.Missiles+ammo.Missiles - Ammo.MissilesAA=Ammo.MissilesAA+ammo.MissilesAA - Ammo.MissilesAG=Ammo.MissilesAG+ammo.MissilesAG - Ammo.MissilesAS=Ammo.MissilesAS+ammo.MissilesAS - - end - - end - - return Ammo -end - ---- Get the number of shells a unit or group currently has. For a group the ammo count of all units is summed up. --- @param #FLIGHTGROUP self --- @param Wrapper.Unit#UNIT unit The unit object. --- @param #boolean display Display ammo table as message to all. Default false. --- @return #FLIGHTGROUP.Ammo Ammo data. -function FLIGHTGROUP:GetAmmoUnit(unit, display) - - -- Default is display false. - if display==nil then - display=false - end - - -- Init counter. - local nammo=0 - local nshells=0 - local nrockets=0 - local nmissiles=0 - local nmissilesAA=0 - local nmissilesAG=0 - local nmissilesAS=0 - local nbombs=0 - - -- Output. - local text=string.format("FLIGHTGROUP group %s - unit %s:\n", self.groupname, unit:GetName()) - - -- Get ammo table. - local ammotable=unit:GetAmmo() - - if ammotable then - - local weapons=#ammotable - - -- Loop over all weapons. - for w=1,weapons do - - -- Number of current weapon. - local Nammo=ammotable[w]["count"] - - -- Type name of current weapon. - local Tammo=ammotable[w]["desc"]["typeName"] - - local _weaponString = UTILS.Split(Tammo,"%.") - local _weaponName = _weaponString[#_weaponString] - - -- Get the weapon category: shell=0, missile=1, rocket=2, bomb=3 - local Category=ammotable[w].desc.category - - -- Get missile category: Weapon.MissileCategory AAM=1, SAM=2, BM=3, ANTI_SHIP=4, CRUISE=5, OTHER=6 - local MissileCategory=nil - if Category==Weapon.Category.MISSILE then - MissileCategory=ammotable[w].desc.missileCategory - end - - -- We are specifically looking for shells or rockets here. - if Category==Weapon.Category.SHELL then - - -- Add up all shells. - nshells=nshells+Nammo - - -- Debug info. - text=text..string.format("- %d shells of type %s\n", Nammo, _weaponName) - - elseif Category==Weapon.Category.ROCKET then - - -- Add up all rockets. - nrockets=nrockets+Nammo - - -- Debug info. - text=text..string.format("- %d rockets of type %s\n", Nammo, _weaponName) - - elseif Category==Weapon.Category.BOMB then - - -- Add up all rockets. - nbombs=nbombs+Nammo - - -- Debug info. - text=text..string.format("- %d bombs of type %s\n", Nammo, _weaponName) - - elseif Category==Weapon.Category.MISSILE then - - -- Add up all cruise missiles (category 5) - if MissileCategory==Weapon.MissileCategory.AAM then - nmissiles=nmissiles+Nammo - nmissilesAA=nmissilesAA+Nammo - elseif MissileCategory==Weapon.MissileCategory.ANTI_SHIP then - nmissiles=nmissiles+Nammo - nmissilesAS=nmissilesAS+Nammo - elseif MissileCategory==Weapon.MissileCategory.BM then - nmissiles=nmissiles+Nammo - nmissilesAG=nmissilesAG+Nammo - elseif MissileCategory==Weapon.MissileCategory.OTHER then - nmissiles=nmissiles+Nammo - nmissilesAG=nmissilesAG+Nammo - end - - -- Debug info. - text=text..string.format("- %d %s missiles of type %s\n", Nammo, self:_MissileCategoryName(MissileCategory), _weaponName) - - else - - -- Debug info. - text=text..string.format("- %d unknown ammo of type %s (category=%d, missile category=%s)\n", Nammo, Tammo, Category, tostring(MissileCategory)) - - end - - end - end - - -- Debug text and send message. - if display then - self:I(self.lid..text) - else - self:T3(self.lid..text) - end - MESSAGE:New(text, 10):ToAllIf(display) - - -- Total amount of ammunition. - nammo=nshells+nrockets+nmissiles+nbombs - - local ammo={} --#FLIGHTGROUP.Ammo - ammo.Total=nammo - ammo.Guns=nshells - ammo.Rockets=nrockets - ammo.Bombs=nbombs - ammo.Missiles=nmissiles - ammo.MissilesAA=nmissilesAA - ammo.MissilesAG=nmissilesAG - ammo.MissilesAS=nmissilesAS - - return ammo -end - - ---- Returns a name of a missile category. --- @param #FLIGHTGROUP self --- @param #number categorynumber Number of missile category from weapon missile category enumerator. See https://wiki.hoggitworld.com/view/DCS_Class_Weapon --- @return #string Missile category name. -function FLIGHTGROUP:_MissileCategoryName(categorynumber) - local cat="unknown" - if categorynumber==Weapon.MissileCategory.AAM then - cat="air-to-air" - elseif categorynumber==Weapon.MissileCategory.SAM then - cat="surface-to-air" - elseif categorynumber==Weapon.MissileCategory.BM then - cat="ballistic" - elseif categorynumber==Weapon.MissileCategory.ANTI_SHIP then - cat="anti-ship" - elseif categorynumber==Weapon.MissileCategory.CRUISE then - cat="cruise" - elseif categorynumber==Weapon.MissileCategory.OTHER then - cat="other" - end - return cat -end - --- Checks if a human player sits in the unit. -- @param #FLIGHTGROUP self -- @param Wrapper.Unit#UNIT unit Aircraft unit. diff --git a/Moose Development/Moose/Ops/NavyGroup.lua b/Moose Development/Moose/Ops/NavyGroup.lua index 3940688d0..8b639aa08 100644 --- a/Moose Development/Moose/Ops/NavyGroup.lua +++ b/Moose Development/Moose/Ops/NavyGroup.lua @@ -31,6 +31,7 @@ -- @field #NAVYGROUP NAVYGROUP = { ClassName = "NAVYGROUP", + verbose = 2, } --- Navy group element. @@ -140,6 +141,7 @@ end --- Add a *scheduled* task. -- @param #NAVYGROUP self -- @param Core.Point#COORDINATE Coordinate Coordinate of the target. +-- @param #number Radius Radius in meters. Default 100 m. -- @param #number Nshots Number of shots to fire. Default 3. -- @param #number WeaponType Type of weapon. Default auto. -- @param #string Clock Time when to start the attack. @@ -148,10 +150,24 @@ function NAVYGROUP:AddTaskFireAtPoint(Coordinate, Radius, Nshots, WeaponType, Cl local DCStask=CONTROLLABLE.TaskFireAtPoint(nil, Coordinate:GetVec2(), Radius, Nshots, WeaponType) - + self:AddTask(DCStask, Clock, nil, Prio) end +--- Add a *scheduled* task. +-- @param #NAVYGROUP self +-- @param Wrapper.Group#GROUP TargetGroup Target group. +-- @param #number WeaponExpend How much weapons does are used. +-- @param #number WeaponType Type of weapon. Default auto. +-- @param #string Clock Time when to start the attack. +-- @param #number Prio Priority of the task. +function NAVYGROUP:AddTaskAttackGroup(TargetGroup, WeaponExpend, WeaponType, Clock, Prio) + + local DCStask=CONTROLLABLE.TaskAttackGroup(nil, TargetGroup, WeaponType, WeaponExpend, AttackQty, Direction, Altitude, AttackQtyLimit, GroupAttack) + + self:AddTask(DCStask, Clock, nil, Prio) + +end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Status @@ -163,6 +179,15 @@ function NAVYGROUP:onafterStatus(From, Event, To) -- FSM state. local fsmstate=self:GetState() + + --- + -- Detection + --- + + -- Check if group has detected any units. + if self.detectionOn then + self:_CheckDetectedUnits() + end -- Current heading and position of the carrier. @@ -173,11 +198,76 @@ function NAVYGROUP:onafterStatus(From, Event, To) -- Check water is ahead. local collision=self:_CheckCollisionCoord(pos:Translate(self.collisiondist or 5000, hdg)) + local nTaskTot, nTaskSched, nTaskWP=self:CountRemainingTasks() + local nMissions=self:CountRemainingMissison() + -- Info text. - local text=string.format("State %s: Speed=%.1f knots Heading=%03d collision=%s", fsmstate, speed, hdg, tostring(collision)) + local text=string.format("State %s: Speed=%.1f knots Heading=%03d collision=%s Tasks=%d Missions=%d", fsmstate, speed, hdg, tostring(collision), nTaskTot, nMissions) self:I(self.lid..text) - self:__Status(-30) + + --- + -- Tasks + --- + + -- Task queue. + if #self.taskqueue>0 and self.verbose>1 then + local text=string.format("Tasks #%d", #self.taskqueue) + for i,_task in pairs(self.taskqueue) do + local task=_task --#FLIGHTGROUP.Task + local name=task.description + local taskid=task.dcstask.id or "unknown" + local status=task.status + local clock=UTILS.SecondsToClock(task.time, true) + local eta=task.time-timer.getAbsTime() + local started=task.timestamp and UTILS.SecondsToClock(task.timestamp, true) or "N/A" + local duration=-1 + if task.duration then + duration=task.duration + if task.timestamp then + -- Time the task is running. + duration=task.duration-(timer.getAbsTime()-task.timestamp) + else + -- Time the task is supposed to run. + duration=task.duration + end + end + -- Output text for element. + if task.type==OPSGROUP.TaskType.SCHEDULED then + text=text..string.format("\n[%d] %s (%s): status=%s, scheduled=%s (%d sec), started=%s, duration=%d", i, taskid, name, status, clock, eta, started, duration) + elseif task.type==OPSGROUP.TaskType.WAYPOINT then + text=text..string.format("\n[%d] %s (%s): status=%s, waypoint=%d, started=%s, duration=%d, stopflag=%d", i, taskid, name, status, task.waypoint, started, duration, task.stopflag:Get()) + end + end + self:I(self.lid..text) + end + + --- + -- Missions + --- + + -- Current mission name. + if self.verbose>0 then + local Mission=self:GetMissionByID(self.currentmission) + + -- Current status. + local text=string.format("Missions %d, Current: %s", self:CountRemainingMissison(), Mission and Mission.name or "none") + for i,_mission in pairs(self.missionqueue) do + local mission=_mission --Ops.Auftrag#AUFTRAG + local Cstart= UTILS.SecondsToClock(mission.Tstart, true) + local Cstop = mission.Tstop and UTILS.SecondsToClock(mission.Tstop, true) or "INF" + text=text..string.format("\n[%d] %s (%s) status=%s (%s), Time=%s-%s, prio=%d wp=%s targets=%d", + i, tostring(mission.name), mission.type, mission:GetFlightStatus(self), tostring(mission.status), Cstart, Cstop, mission.prio, tostring(mission:GetFlightWaypointIndex(self)), mission:CountMissionTargets()) + end + self:I(self.lid..text) + end + + + + -- Next check in ~30 seconds. + if not self:IsStopped() then + self:__Status(-30) + end end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -209,7 +299,7 @@ function NAVYGROUP:onafterSpawned(From, Event, To) if self.ai then -- Set default ROE and ROT options. - self:SetOptionROE() + self:SetOptionROE(self.roe) end @@ -252,6 +342,7 @@ function NAVYGROUP:onafterUpdateRoute(From, Event, To, n, Speed, Depth) -- Set speed. wp.speed=UTILS.KmphToMps(speed) + wp.alt=-depth -- Add waypoint. table.insert(waypoints, wp) @@ -259,9 +350,11 @@ function NAVYGROUP:onafterUpdateRoute(From, Event, To, n, Speed, Depth) if #waypoints>1 then + + self:I(self.lid..string.format("Updateing route: WP=%d, Speed=%.1f knots, depth=%d meters", #self.waypoints-n+1, UTILS.KmphToKnots(speed), depth)) -- Route group to all defined waypoints remaining. - self.group:Route(waypoints, 1) + self:Route(waypoints) else @@ -269,7 +362,11 @@ function NAVYGROUP:onafterUpdateRoute(From, Event, To, n, Speed, Depth) -- No waypoints left --- - self:UpdateRoute(1) + self:I(self.lid..string.format("No waypoints left")) + + -- TODO: Switch to waypoint 1 + + --self:UpdateRoute(1) end @@ -299,7 +396,7 @@ function NAVYGROUP:onafterTurnIntoWind(From, Event, To, Duration, Speed, Uturn) wp[1]=coord:WaypointNaval(Speed) wp[2]=Coord:WaypointNaval(Speed) - self.group:Route(wp, 1) + self:Route(wp) end @@ -317,7 +414,7 @@ function NAVYGROUP:onafterFullStop(From, Event, To) local wp=pos:WaypointNaval(0) -- Create new route consisting of only this position ==> Stop! - self.group:Route({wp}) + self:Route({wp}) end @@ -340,6 +437,7 @@ end -- @param #number Depth Dive depth in meters. function NAVYGROUP:onafterDive(From, Event, To, Depth) + env.info("FF Diving") self:UpdateRoute(nil, nil, Depth) end @@ -478,6 +576,8 @@ function NAVYGROUP:_InitGroup() element.status=OPSGROUP.ElementStatus.INUTERO table.insert(self.elements, element) + self:GetAmmoUnit(unit, true) + if unit:IsAlive() then self:ElementSpawned(element) end @@ -639,6 +739,33 @@ function NAVYGROUP:_CheckTurning() self.turning=turning end +--- Check if group is done, i.e. +-- +-- * passed the final waypoint, +-- * no current task +-- * no current mission +-- * number of remaining tasks is zero +-- * number of remaining missions is zero +-- +-- @param #NAVYGROUP self +-- @param #number delay Delay in seconds. +function NAVYGROUP:_CheckGroupDone(delay) + + if self:IsAlive() and self.ai then + + if delay and delay>0 then + -- Delayed call. + self:ScheduleOnce(delay, NAVYGROUP._CheckGroupDone, self) + else + + -- TODO: What? + + end + + end + +end + ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- diff --git a/Moose Development/Moose/Ops/OpsGroup.lua b/Moose Development/Moose/Ops/OpsGroup.lua index e0df5e110..cfcbebea6 100644 --- a/Moose Development/Moose/Ops/OpsGroup.lua +++ b/Moose Development/Moose/Ops/OpsGroup.lua @@ -16,6 +16,7 @@ -- @field #string lid Class id string for output to DCS log file. -- @field #string groupname Name of the group. -- @field Wrapper.Group#GROUP group Group object. +-- @field #table template Template of the group. -- @field #boolean isLateActivated Is the group late activated. -- @field #table elements Table of elements, i.e. units of the group. -- @field #boolean ai If true, group is purely AI. @@ -34,6 +35,7 @@ -- @field #number speedmax Max speed in km/h. -- @field #number speedCruise Cruising speed in km/h. -- @field #boolean passedfinalwp Group has passed the final waypoint. +-- @field #boolean respawning Group is being respawned. -- @field Core.Set#SET_ZONE checkzones Set of zones. -- @field Core.Set#SET_ZONE inzones Set of zones in which the group is currently in. -- @field #boolean groupinitialized If true, group parameters were initialized. @@ -113,6 +115,7 @@ OPSGROUP = { checkzones = nil, inzones = nil, groupinitialized = nil, + respawning = nil, } --- Status of group element. @@ -183,6 +186,19 @@ OPSGROUP.TaskType={ -- @field DCS#Task DCStask DCS task structure table. -- @field #number WaypointIndex Waypoint number at which the enroute task is added. +--- Ammo data. +-- @type OPSGROUP.Ammo +-- @field #number Total Total amount of ammo. +-- @field #number Guns Amount of gun shells. +-- @field #number Bombs Amount of bombs. +-- @field #number Rockets Amount of rockets. +-- @field #number Torpedos Amount of torpedos. +-- @field #number Missiles Amount of missiles. +-- @field #number MissilesAA Amount of air-to-air missiles. +-- @field #number MissilesAG Amount of air-to-ground missiles. +-- @field #number MissilesAS Amount of anti-ship missiles. +-- @field #number MissilesCR Amount of cruise missiles. +-- @field #number MissilesBM Amount of ballistic missiles. --- NavyGroup version. -- @field #string version @@ -604,7 +620,7 @@ function OPSGROUP:RemoveWaypoint(wpindex) end env.info("FF update route -1 after waypoint removed") - self:_CheckFlightDone() + self:_CheckGroupDone() else @@ -805,7 +821,7 @@ function OPSGROUP:AddTaskWaypoint(task, waypointindex, description, prio, durati self:T3({newtask=newtask}) -- Update route. - --self:_CheckFlightDone(1) + --self:_CheckGroupDone(1) self:__UpdateRoute(-1) return newtask @@ -926,7 +942,7 @@ function OPSGROUP:RemoveTask(Task) -- Update route if this is a waypoint task. if task.type==OPSGROUP.TaskType.WAYPOINT and task.status==OPSGROUP.TaskStatus.SCHEDULED then - self:_CheckFlightDone(1) + self:_CheckGroupDone(1) --self:__UpdateRoute(-1) end @@ -1215,8 +1231,8 @@ function OPSGROUP:onafterTaskDone(From, Event, To, Task) --Mission paused. Do nothing! end else - self:I(self.lid.."FF Task Done but NO mission found ==> _CheckFlightDone in 1 sec") - self:_CheckFlightDone(1) + self:I(self.lid.."FF Task Done but NO mission found ==> _CheckGroupDone in 1 sec") + self:_CheckGroupDone(1) end end @@ -1566,7 +1582,7 @@ function OPSGROUP:onafterMissionCancel(From, Event, To, Mission) Mission:SetFlightStatus(self, AUFTRAG.FlightStatus.CANCELLED) -- Send flight RTB or WAIT if nothing left to do. - self:_CheckFlightDone(1) + self:_CheckGroupDone(1) end @@ -1608,7 +1624,7 @@ function OPSGROUP:onafterMissionDone(From, Event, To, Mission) -- TODO: reset mission specific parameters like radio, ROE etc. -- Check if flight is done. - self:_CheckFlightDone(1) + self:_CheckGroupDone(1) end @@ -1703,6 +1719,73 @@ function OPSGROUP:RouteToMission(mission, delay) end end +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- Queue Update: Missions & Tasks +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- On after "QueueUpdate" event. +-- @param #OPSGROUP self +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +function OPSGROUP:onafterQueueUpdate(From, Event, To) + + --- + -- Mission + --- + + -- First check if group is alive? Late activated groups are activated and uncontrolled units are started automatically. + if self:IsAlive()~=nil then + + local mission=self:_GetNextMission() + + if mission then + + local currentmission=self:GetMissionCurrent() + + if currentmission then + + -- Current mission but new mission is urgent with higher prio. + if mission.urgent and mission.prio