From d64de26ded20fa60375c21fca5ab8dc97f516237 Mon Sep 17 00:00:00 2001 From: Frank Date: Thu, 29 Jul 2021 13:43:29 +0200 Subject: [PATCH] OPS - many fixes and improvements --- .../Moose/Functional/Warehouse.lua | 14 +- Moose Development/Moose/Ops/AirWing.lua | 4 +- Moose Development/Moose/Ops/Airboss.lua | 10 +- Moose Development/Moose/Ops/ArmyGroup.lua | 36 ++-- Moose Development/Moose/Ops/Auftrag.lua | 57 ++++-- Moose Development/Moose/Ops/FlightGroup.lua | 167 +++++++----------- Moose Development/Moose/Ops/NavyGroup.lua | 38 ++-- Moose Development/Moose/Ops/OpsGroup.lua | 143 +++++++++++---- Moose Development/Moose/Ops/OpsTransport.lua | 4 +- Moose Development/Moose/Ops/Squadron.lua | 17 +- 10 files changed, 288 insertions(+), 202 deletions(-) diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index cf02c728f..1c571b93c 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -4882,6 +4882,13 @@ function WAREHOUSE:onbeforeArrived(From, Event, To, group) local asset=self:FindAssetInDB(group) if asset then + + if asset.flightgroup and not asset.arrived then + --env.info("FF asset has a flightgroup. arrival will be handled there!") + asset.arrived=true + return false + end + if asset.arrived==true then -- Asset already arrived (e.g. if multiple units trigger the event via landing). return false @@ -4889,6 +4896,7 @@ function WAREHOUSE:onbeforeArrived(From, Event, To, group) asset.arrived=true --ensure this is not called again from the same asset group. return true end + end end @@ -6042,7 +6050,7 @@ function WAREHOUSE:_RouteGround(group, request) end for n,wp in ipairs(Waypoints) do - env.info(n) + --env.info(n) local tf=self:_SimpleTaskFunctionWP("warehouse:_PassingWaypoint",group, n, #Waypoints) group:SetTaskWaypoint(wp, tf) end @@ -9046,11 +9054,11 @@ function WAREHOUSE:_GetFlightplan(asset, departure, destination) -- Hot start. if asset.takeoffType and asset.takeoffType==COORDINATE.WaypointType.TakeOffParkingHot then - env.info("FF hot") + --env.info("FF hot") _type=COORDINATE.WaypointType.TakeOffParkingHot _action=COORDINATE.WaypointAction.FromParkingAreaHot else - env.info("FF cold") + --env.info("FF cold") end diff --git a/Moose Development/Moose/Ops/AirWing.lua b/Moose Development/Moose/Ops/AirWing.lua index 4b44b6b91..5e702896b 100644 --- a/Moose Development/Moose/Ops/AirWing.lua +++ b/Moose Development/Moose/Ops/AirWing.lua @@ -612,9 +612,9 @@ function AIRWING:RemoveAssetFromSquadron(Asset) end end ---- Add mission to queue. +--- Add a mission for the airwing. The airwing will pick the best available assets for the mission and lauch it when ready. -- @param #AIRWING self --- @param Ops.Auftrag#AUFTRAG Mission for this group. +-- @param Ops.Auftrag#AUFTRAG Mission Mission for this airwing. -- @return #AIRWING self function AIRWING:AddMission(Mission) diff --git a/Moose Development/Moose/Ops/Airboss.lua b/Moose Development/Moose/Ops/Airboss.lua index c3a45c8c5..e0cff612e 100644 --- a/Moose Development/Moose/Ops/Airboss.lua +++ b/Moose Development/Moose/Ops/Airboss.lua @@ -6118,6 +6118,11 @@ function AIRBOSS:_ScanCarrierZone() -- Get flight group if possible. local knownflight=self:_GetFlightFromGroupInQueue(group, self.flights) + + -- Unknown new AI flight. Create a new flight group. + if not knownflight and not self:_IsHuman(group) then + knownflight=self:_CreateFlightGroup(group) + end -- Get aircraft type name. local actype=group:GetTypeName() @@ -6175,10 +6180,7 @@ function AIRBOSS:_ScanCarrierZone() else - -- Unknown new AI flight. Create a new flight group. - if not self:_IsHuman(group) then - self:_CreateFlightGroup(group) - end + end diff --git a/Moose Development/Moose/Ops/ArmyGroup.lua b/Moose Development/Moose/Ops/ArmyGroup.lua index b0c0b2c91..2bcef539a 100644 --- a/Moose Development/Moose/Ops/ArmyGroup.lua +++ b/Moose Development/Moose/Ops/ArmyGroup.lua @@ -538,6 +538,24 @@ end function ARMYGROUP:onafterSpawned(From, Event, To) self:T(self.lid..string.format("Group spawned!")) + -- 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("Weight = %.1f kg\n", self:GetWeightTotal()) + text=text..string.format("Cargo bay = %.1f kg\n", self:GetFreeCargobay()) + 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 + -- Update position. self:_UpdatePosition() @@ -1179,24 +1197,6 @@ function ARMYGROUP:_InitGroup(Template) -- Set type name. self.actype=units[1]: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("Weight = %.1f kg\n", self:GetWeightTotal()) - text=text..string.format("Cargo bay = %.1f kg\n", self:GetFreeCargobay()) - 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 diff --git a/Moose Development/Moose/Ops/Auftrag.lua b/Moose Development/Moose/Ops/Auftrag.lua index 6dd08b4e6..c9bd4ca98 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. --- * Compatible with FLIGHTGROUP, NAVYGROUP, ARMYGROUP, AIRWING, WINGCOMMANDER and CHIEF classes +-- * Compatible with OPS classes like FLIGHTGROUP, NAVYGROUP, ARMYGROUP, AIRWING, etc. -- * FSM events when a mission is done, successful or failed -- -- === @@ -106,6 +106,7 @@ -- @field #number missionFraction Mission coordiante fraction. Default is 0.5. -- @field #number missionRange Mission range in meters. Used in AIRWING class. -- @field Core.Point#COORDINATE missionWaypointCoord Mission waypoint coordinate. +-- @field Core.Point#COORDINATE missionEgressCoord Mission egress waypoint coordinate. -- -- @field #table enrouteTasks Mission enroute tasks. -- @@ -434,8 +435,9 @@ AUFTRAG.TargetType={ --- Group specific data. Each ops group subscribed to this mission has different data for this. -- @type AUFTRAG.GroupData -- @field Ops.OpsGroup#OPSGROUP opsgroup The OPS group. --- @field Core.Point#COORDINATE waypointcoordinate Waypoint coordinate. +-- @field Core.Point#COORDINATE waypointcoordinate Ingress waypoint coordinate. -- @field #number waypointindex Waypoint index. +-- @field Core.Point#COORDINATE wpegresscoordinate Egress waypoint coordinate. -- @field Ops.OpsGroup#OPSGROUP.Task waypointtask Waypoint task. -- @field #string status Group mission status. -- @field Ops.AirWing#AIRWING.SquadronAsset asset The squadron asset. @@ -443,7 +445,7 @@ AUFTRAG.TargetType={ --- AUFTRAG class version. -- @field #string version -AUFTRAG.version="0.6.1" +AUFTRAG.version="0.7.0" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list @@ -938,7 +940,7 @@ end --- Create a STRIKE mission. Flight will attack the closest map object to the specified coordinate. -- @param #AUFTRAG self --- @param Core.Point#COORDINATE Target The target coordinate. Can also be given as a GROUP, UNIT or STATIC object. +-- @param Core.Point#COORDINATE Target The target coordinate. Can also be given as a GROUP, UNIT, STATIC or TARGET object. -- @param #number Altitude Engage altitude in feet. Default 2000 ft. -- @return #AUFTRAG self function AUFTRAG:NewSTRIKE(Target, Altitude) @@ -966,7 +968,7 @@ end --- Create a BOMBING mission. Flight will drop bombs a specified coordinate. -- @param #AUFTRAG self --- @param Core.Point#COORDINATE Target Target coordinate. Can also be specified as a GROUP, UNIT or STATIC object. +-- @param Core.Point#COORDINATE Target Target coordinate. Can also be specified as a GROUP, UNIT, STATIC or TARGET object. -- @param #number Altitude Engage altitude in feet. Default 25000 ft. -- @return #AUFTRAG self function AUFTRAG:NewBOMBING(Target, Altitude) @@ -1006,10 +1008,6 @@ function AUFTRAG:NewBOMBRUNWAY(Airdrome, Altitude) if type(Airdrome)=="string" then Airdrome=AIRBASE:FindByName(Airdrome) end - - if Airdrome:IsInstanceOf("AIRBASE") then - - end local mission=AUFTRAG:New(AUFTRAG.Type.BOMBRUNWAY) @@ -1110,9 +1108,7 @@ end function AUFTRAG:NewRESCUEHELO(Carrier) local mission=AUFTRAG:New(AUFTRAG.Type.RESCUEHELO) - - --mission.carrier=Carrier - + mission:_TargetFromObject(Carrier) -- Mission options: @@ -3138,12 +3134,45 @@ function AUFTRAG:GetMissionTypesText(MissionTypes) return text end ---- Set the mission waypoint coordinate where the mission is executed. +--- Set the mission waypoint coordinate where the mission is executed. Note that altitude is set via `:SetMissionAltitude`. -- @param #AUFTRAG self --- @return Core.Point#COORDINATE Coordinate where the mission is executed. +-- @param Core.Point#COORDINATE Coordinate Coordinate where the mission is executed. -- @return #AUFTRAG self function AUFTRAG:SetMissionWaypointCoord(Coordinate) + + -- Obviously a zone was passed. We get the coordinate. + if Coordinate:IsInstanceOf("ZONE_BASE") then + Coordinate=Coordinate:GetCoordinate() + end + self.missionWaypointCoord=Coordinate + return self +end + +--- Set the mission egress coordinate. This is the coordinate where the assigned group will go once the mission is finished. +-- @param #AUFTRAG self +-- @param Core.Point#COORDINATE Coordinate Egrees coordinate. +-- @param #number Altitude (Optional) Altitude in feet. Default is y component of coordinate. +-- @return #AUFTRAG self +function AUFTRAG:SetMissionEgressCoord(Coordinate, Altitude) + + -- Obviously a zone was passed. We get the coordinate. + if Coordinate:IsInstanceOf("ZONE_BASE") then + Coordinate=Coordinate:GetCoordinate() + end + + self.missionEgressCoord=Coordinate + + if Altitude then + self.missionEgressCoord.y=UTILS.FeetToMeters(Altitude) + end +end + +--- Get the mission egress coordinate if this was defined. +-- @param #AUFTRAG self +-- @return Core.Point#COORDINATE Coordinate Coordinate or nil. +function AUFTRAG:GetMissionEgressCoord() + return self.missionEgressCoord end --- Get coordinate of target. First unit/group of the set is used. diff --git a/Moose Development/Moose/Ops/FlightGroup.lua b/Moose Development/Moose/Ops/FlightGroup.lua index a10db29bf..c7a7e89c5 100644 --- a/Moose Development/Moose/Ops/FlightGroup.lua +++ b/Moose Development/Moose/Ops/FlightGroup.lua @@ -134,8 +134,8 @@ FLIGHTGROUP = { fuelcritical = nil, fuelcriticalthresh = nil, fuelcriticalrtb = false, - outofAAMrtb = true, - outofAGMrtb = true, + outofAAMrtb = false, + outofAGMrtb = false, squadron = nil, flightcontrol = nil, flaghold = nil, @@ -171,10 +171,6 @@ FLIGHTGROUP.Attribute = { OTHER="Other", } ---- Flight group element. --- @type FLIGHTGROUP.Element --- @extends Ops.OpsGroup#OPSGROUP.Element - --- FLIGHTGROUP class version. -- @field #string version FLIGHTGROUP.version="0.7.0" @@ -251,9 +247,9 @@ function FLIGHTGROUP:New(group) self:AddTransition("*", "FuelLow", "*") -- Fuel state of group is low. Default ~25%. self:AddTransition("*", "FuelCritical", "*") -- Fuel state of group is critical. Default ~10%. - self:AddTransition("*", "OutOfMissilesAA", "*") -- Group is out of A2A missiles. - self:AddTransition("*", "OutOfMissilesAG", "*") -- Group is out of A2G missiles. - self:AddTransition("*", "OutOfMissilesAS", "*") -- Group is out of A2S(ship) missiles. Not implemented yet! + self:AddTransition("*", "OutOfMissilesAA", "*") -- Group is out of A2A (air) missiles. + self:AddTransition("*", "OutOfMissilesAG", "*") -- Group is out of A2G (ground) missiles. + self:AddTransition("*", "OutOfMissilesAS", "*") -- Group is out of A2S (ship) missiles. self:AddTransition("Airborne", "EngageTarget", "Engaging") -- Engage targets. self:AddTransition("Engaging", "Disengage", "Airborne") -- Engagement over. @@ -1036,6 +1032,9 @@ function FLIGHTGROUP:onafterStatus(From, Event, To) self:FuelCritical() end + -- This causes severe problems as OutOfMissiles is called over and over again leading to many RTB calls. + if false then + -- Out of AA Missiles? CAP, GCICAP, INTERCEPT local CurrIsCap = false -- Out of AG Missiles? BAI, SEAD, CAS, STRIKE @@ -1057,6 +1056,8 @@ function FLIGHTGROUP:onafterStatus(From, Event, To) if (not self:CanAirToGround(false)) and CurrIsA2G then self:OutOfMissilesAG() end + + end end @@ -1595,6 +1596,34 @@ end -- @param #string To To state. function FLIGHTGROUP:onafterSpawned(From, Event, To) self:T(self.lid..string.format("Flight spawned")) + + -- Debug info. + if self.verbose>=1 then + local text=string.format("Initialized Flight 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("Range max = %.1f km\n", self.rangemax/1000) + text=text..string.format("Ceiling = %.1f feet\n", UTILS.MetersToFeet(self.ceiling)) + text=text..string.format("Weight = %.1f kg\n", self:GetWeightTotal()) + text=text..string.format("Cargo bay = %.1f kg\n", self:GetFreeCargobay()) + 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.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) + 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/B=%d/M=%d)\n", self.ammo.Total, self.ammo.Guns, self.ammo.Rockets, self.ammo.Bombs, self.ammo.Missiles) + text=text..string.format("FSM state = %s\n", self:GetState()) + text=text..string.format("Is alive = %s\n", tostring(self.group:IsAlive())) + text=text..string.format("LateActivate = %s\n", tostring(self:IsLateActivated())) + text=text..string.format("Uncontrolled = %s\n", tostring(self:IsUncontrolled())) + text=text..string.format("Start Air = %s\n", tostring(self:IsTakeoffAir())) + text=text..string.format("Start Cold = %s\n", tostring(self:IsTakeoffCold())) + text=text..string.format("Start Hot = %s\n", tostring(self:IsTakeoffHot())) + text=text..string.format("Start Rwy = %s\n", tostring(self:IsTakeoffRunway())) + self:I(self.lid..text) + end -- Update position. self:_UpdatePosition() @@ -1858,9 +1887,10 @@ function FLIGHTGROUP:onafterArrived(From, Event, To) self.flightcontrol:SetFlightStatus(self, FLIGHTCONTROL.FlightStatus.ARRIVED) end - -- Despawn in 5 min. + -- Check what to do. if self.airwing then - -- Let airwing do its thing. + -- Add the asset back to the airwing. + self.airwing:AddAsset(self.group, 1) elseif self.isLandingAtAirbase then local Template=UTILS.DeepCopy(self.template) --DCS#Template @@ -1963,7 +1993,7 @@ function FLIGHTGROUP:onafterDead(From, Event, To) else if self.airwing then -- Not all assets were destroyed (despawn) ==> Add asset back to airwing. - self.airwing:AddAsset(self.group, 1) + --self.airwing:AddAsset(self.group, 1) end end @@ -2131,7 +2161,7 @@ function FLIGHTGROUP:onafterOutOfMissilesAA(From, Event, To) if self.outofAAMrtb then -- Back to destination or home. local airbase=self.destbase or self.homebase - self:__RTB(-5,airbase) + self:__RTB(-5, airbase) end end @@ -2145,7 +2175,7 @@ function FLIGHTGROUP:onafterOutOfMissilesAG(From, Event, To) if self.outofAGMrtb then -- Back to destination or home. local airbase=self.destbase or self.homebase - self:__RTB(-5,airbase) + self:__RTB(-5, airbase) end end @@ -2288,20 +2318,31 @@ function FLIGHTGROUP:onbeforeRTB(From, Event, To, airbase, SpeedTo, SpeedHold) -- Check that coaliton is okay. We allow same (blue=blue, red=red) or landing on neutral bases. if airbase and airbase:GetCoalition()~=self.group:GetCoalition() and airbase:GetCoalition()>0 then - self:E(self.lid..string.format("ERROR: Wrong airbase coalition %d in RTB() call! We allow only same as group %d or neutral airbases 0.", airbase:GetCoalition(), self.group:GetCoalition())) - allowed=false + self:E(self.lid..string.format("ERROR: Wrong airbase coalition %d in RTB() call! We allow only same as group %d or neutral airbases 0", airbase:GetCoalition(), self.group:GetCoalition())) + return false + end + + if self.currbase and self.currbase:GetName()==airbase:GetName() then + self:E(self.lid.."WARNING: Currbase is already same as RTB airbase. RTB canceled!") + return false + end + + -- Check if the group has landed at an airbase. If so, we lost control and RTBing is not possible (only after a respawn). + if self:IsLanded() then + self:E(self.lid.."WARNING: Flight has already landed. RTB canceled!") + return false end if not self.group:IsAirborne(true) then -- this should really not happen, either the AUFTRAG is cancelled before the group was airborne or it is stuck at the ground for some reason - self:I(self.lid..string.format("WARNING: Group is not AIRBORNE ==> RTB event is suspended for 20 sec.")) + self:I(self.lid..string.format("WARNING: Group is not AIRBORNE ==> RTB event is suspended for 20 sec")) allowed=false Tsuspend=-20 local groupspeed = self.group:GetVelocityMPS() if groupspeed<=1 and not self:IsParking() then self.RTBRecallCount = self.RTBRecallCount+1 end - if self.RTBRecallCount > 6 then + if self.RTBRecallCount>6 then self:I(self.lid..string.format("WARNING: Group is not moving and was called RTB %d times. Assuming a problem and despawning!", self.RTBRecallCount)) self.RTBRecallCount=0 self:Despawn(5) @@ -3108,103 +3149,19 @@ function FLIGHTGROUP:_InitGroup(Template) self.tankertype=select(2, unit:IsTanker()) self.refueltype=select(2, unit:IsRefuelable()) - -- Debug info. - if self.verbose>=1 then - local text=string.format("Initialized Flight 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("Range max = %.1f km\n", self.rangemax/1000) - text=text..string.format("Ceiling = %.1f feet\n", UTILS.MetersToFeet(self.ceiling)) - text=text..string.format("Weight = %.1f kg\n", self:GetWeightTotal()) - text=text..string.format("Cargo bay = %.1f kg\n", self:GetFreeCargobay()) - 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.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) - 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/B=%d/M=%d)\n", self.ammo.Total, self.ammo.Guns, self.ammo.Rockets, self.ammo.Bombs, self.ammo.Missiles) - text=text..string.format("FSM state = %s\n", self:GetState()) - text=text..string.format("Is alive = %s\n", tostring(self.group:IsAlive())) - text=text..string.format("LateActivate = %s\n", tostring(self:IsLateActivated())) - text=text..string.format("Uncontrolled = %s\n", tostring(self:IsUncontrolled())) - text=text..string.format("Start Air = %s\n", tostring(self:IsTakeoffAir())) - text=text..string.format("Start Cold = %s\n", tostring(self:IsTakeoffCold())) - text=text..string.format("Start Hot = %s\n", tostring(self:IsTakeoffHot())) - text=text..string.format("Start Rwy = %s\n", tostring(self:IsTakeoffRunway())) - self:I(self.lid..text) - end - --env.info("DCS Unit BOOM_AND_RECEPTACLE="..tostring(Unit.RefuelingSystem.BOOM_AND_RECEPTACLE)) --env.info("DCS Unit PROBE_AND_DROGUE="..tostring(Unit.RefuelingSystem.PROBE_AND_DROGUE)) -- Init done. self.groupinitialized=true - + + else + self:E(self.lid.."ERROR: no unit in _InigGroup!") end return self end ---- Add an element to the flight group. --- @param #FLIGHTGROUP self --- @param #string unitname Name of unit. --- @return Ops.OpsGroup#OPSGROUP.Element The element or nil. -function FLIGHTGROUP:AddElementByName(unitname) - - local unit=UNIT:FindByName(unitname) - - if unit then - - local element={} --Ops.OpsGroup#OPSGROUP.Element - - element.name=unitname - element.status=OPSGROUP.ElementStatus.INUTERO - element.unit=unit - element.group=unit:GetGroup() - - - -- TODO: this is wrong when grouping is used! - local unittemplate=element.unit:GetTemplate() - - element.modex=unittemplate.onboard_num - element.skill=unittemplate.skill - element.payload=unittemplate.payload - element.pylons=unittemplate.payload and unittemplate.payload.pylons or nil --element.unit:GetTemplatePylons() - element.fuelmass0=unittemplate.payload and unittemplate.payload.fuel or 0 --element.unit:GetTemplatePayload().fuel - element.fuelmass=element.fuelmass0 - element.fuelrel=element.unit:GetFuel() - element.category=element.unit:GetUnitCategory() - element.categoryname=element.unit:GetCategoryName() - element.callsign=element.unit:GetCallsign() - element.size=element.unit:GetObjectSize() - - if element.skill=="Client" or element.skill=="Player" then - element.ai=false - element.client=CLIENT:FindByName(unitname) - else - 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) - - -- Add element to table. - table.insert(self.elements, element) - - if unit:IsAlive() then - self:ElementSpawned(element) - end - - return element - end - - return nil -end - --- Check if a unit is and element of the flightgroup. -- @param #FLIGHTGROUP self diff --git a/Moose Development/Moose/Ops/NavyGroup.lua b/Moose Development/Moose/Ops/NavyGroup.lua index a1209e0db..4925626c2 100644 --- a/Moose Development/Moose/Ops/NavyGroup.lua +++ b/Moose Development/Moose/Ops/NavyGroup.lua @@ -648,7 +648,7 @@ end -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. --- @param #NAVYGROUP.Element Element The group element. +-- @param Ops.OpsGroup#OPSGROUP.Element Element The group element. function NAVYGROUP:onafterElementSpawned(From, Event, To, Element) self:T(self.lid..string.format("Element spawned %s", Element.name)) @@ -665,6 +665,24 @@ end function NAVYGROUP:onafterSpawned(From, Event, To) self:T(self.lid..string.format("Group spawned!")) + -- 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("Weight = %.1f kg\n", self:GetWeightTotal()) + text=text..string.format("Cargo bay = %.1f kg\n", self:GetFreeCargobay()) + 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 + -- Update position. self:_UpdatePosition() @@ -1199,24 +1217,6 @@ function NAVYGROUP:_InitGroup(Template) -- Set type name. self.actype=units[1]: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("Weight = %.1f kg\n", self:GetWeightTotal()) - text=text..string.format("Cargo bay = %.1f kg\n", self:GetFreeCargobay()) - 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 diff --git a/Moose Development/Moose/Ops/OpsGroup.lua b/Moose Development/Moose/Ops/OpsGroup.lua index 78885d372..40d29583f 100644 --- a/Moose Development/Moose/Ops/OpsGroup.lua +++ b/Moose Development/Moose/Ops/OpsGroup.lua @@ -620,6 +620,7 @@ function OPSGROUP:New(group) self:AddTransition("*", "Unloading", "*") -- Carrier is unloading the cargo. self:AddTransition("*", "Unload", "*") -- Carrier unload a cargo group. self:AddTransition("*", "Unloaded", "*") -- Carrier unloaded all its current cargo. + self:AddTransition("*", "UnloadingDone", "*") -- Carrier is unloading the cargo. self:AddTransition("*", "Delivered", "*") -- Carrier delivered ALL cargo of the transport assignment. ------------------------ @@ -1883,13 +1884,33 @@ end --- Check if the group is **not** cargo. -- @param #OPSGROUP self --- @param #boolean CheckTransport If true or nil, also check if cargo is associated with a transport assignment. If not, we consider it not cargo. +-- @param #boolean CheckTransport If `true` or `nil`, also check if cargo is associated with a transport assignment. If not, we consider it not cargo. -- @return #boolean If true, group is *not* cargo. function OPSGROUP:IsNotCargo(CheckTransport) local notcargo=self.cargoStatus==OPSGROUP.CargoStatus.NOTCARGO - if self.cargoTransportUID==nil then - --notcargo=true + + if notcargo then + -- Not cargo. + return true + else + -- Is cargo (e.g. loaded or boarding) + + if CheckTransport then + -- Check if transport UID was set. + if self.cargoTransportUID==nil then + return true + else + -- Some transport UID was assigned. + return false + end + else + -- Is cargo. + return false + end + end + + return notcargo end @@ -2397,9 +2418,13 @@ function OPSGROUP:OnEventBirth(EventData) -- Get element. local element=self:GetElementByName(unitname) + + if element then - -- Set element to spawned state. - self:ElementSpawned(element) + -- Set element to spawned state. + self:ElementSpawned(element) + + end end @@ -2412,7 +2437,7 @@ function OPSGROUP:OnEventDead(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 - self:T(self.lid..string.format("EVENT: Unit %s dead!", EventData.IniUnitName)) + self:T2(self.lid..string.format("EVENT: Unit %s dead!", EventData.IniUnitName)) local unit=EventData.IniUnit local group=EventData.IniGroup @@ -2421,7 +2446,7 @@ function OPSGROUP:OnEventDead(EventData) -- Get element. local element=self:GetElementByName(unitname) - if element then + if element and element.status~=OPSGROUP.ElementStatus.DEAD then self:T(self.lid..string.format("EVENT: Element %s dead ==> destroyed", element.name)) self:ElementDestroyed(element) end @@ -2437,6 +2462,8 @@ function OPSGROUP:OnEventRemoveUnit(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 + self:T2(self.lid..string.format("EVENT: Unit %s removed!", EventData.IniUnitName)) + local unit=EventData.IniUnit local group=EventData.IniGroup local unitname=EventData.IniUnitName @@ -2444,7 +2471,7 @@ function OPSGROUP:OnEventRemoveUnit(EventData) -- Get element. local element=self:GetElementByName(unitname) - if element then + if element and element.status~=OPSGROUP.ElementStatus.DEAD then self:T(self.lid..string.format("EVENT: Element %s removed ==> dead", element.name)) self:ElementDead(element) end @@ -3026,7 +3053,7 @@ function OPSGROUP:onafterTaskExecute(From, Event, To, Task) -- If task is scheduled (not waypoint) set task. if Task.type==OPSGROUP.TaskType.SCHEDULED or Task.ismission then - + local DCStasks={} if Task.dcstask.id=='ComboTask' then -- Loop over all combo tasks. @@ -3053,7 +3080,12 @@ function OPSGROUP:onafterTaskExecute(From, Event, To, Task) local TaskFinal=self.group:TaskCombo({TaskControlled, TaskDone}) -- Set task for group. - self:SetTask(TaskFinal) + -- NOTE: I am pushing the task instead of setting it as it seems to keep the mission task alive. + -- There were issues that flights did not proceed to a later waypoint because the task did not finish until the fired missiles + -- impacted (took rather long). Then the flight flew to the nearest airbase and one lost completely the control over the group. + self:PushTask(TaskFinal) + --self:SetTask(TaskFinal) + elseif Task.type==OPSGROUP.TaskType.WAYPOINT then -- Waypoint tasks are executed elsewhere! @@ -3780,6 +3812,8 @@ function OPSGROUP:RouteToMission(mission, delay) if self.isGround and mission.optionFormation then formation=mission.optionFormation end + + --waypointcoord:MarkToAll(string.format("Mission %s alt=%d m", mission:GetName(), waypointcoord.y)) -- Add waypoint. local waypoint=self:AddWaypoint(waypointcoord, SpeedToMission, nil, formation, false) @@ -3793,6 +3827,11 @@ function OPSGROUP:RouteToMission(mission, delay) -- Set waypoint index. mission:SetGroupWaypointIndex(self, waypoint.uid) + + local egress=mission:GetMissionEgressCoord() + if egress then + local waypoint=self:AddWaypoint(egress, SpeedToMission, nil, formation, false) + end --- -- Mission Specific Settings @@ -4038,6 +4077,7 @@ function OPSGROUP:_SetWaypointTasks(Waypoint) -- Check if there is mission task if missiontask then + self:T(self.lid.."Executing mission task") self:TaskExecute(missiontask) return 1 end @@ -5218,8 +5258,8 @@ function OPSGROUP:_CheckCargoTransport() -- Unloading finished ==> pickup next batch or call it a day. if delivered then - self:T(self.lid.."Unloading finished ==> Unloaded") - self:Unloaded() + self:T(self.lid.."Unloading finished ==> UnloadingDone") + self:UnloadingDone() else self:Unloading() end @@ -5954,7 +5994,7 @@ function OPSGROUP:onafterPickup(From, Event, To) -- If this is a helo and no ZONE_AIRBASE was given, we make the helo land in the pickup zone. Coordinate:SetAltitude(200) - local waypoint=FLIGHTGROUP.AddWaypoint(self, Coordinate) ; waypoint.detour=true + local waypoint=FLIGHTGROUP.AddWaypoint(self, Coordinate) ; waypoint.detour=1 else self:E(self.lid.."ERROR: Carrier aircraft cannot land in Pickup zone! Specify a ZONE_AIRBASE as pickup zone") @@ -5982,7 +6022,7 @@ function OPSGROUP:onafterPickup(From, Event, To) end -- NAVYGROUP - local waypoint=NAVYGROUP.AddWaypoint(self, Coordinate, nil, uid) ; waypoint.detour=true + local waypoint=NAVYGROUP.AddWaypoint(self, Coordinate, nil, uid) ; waypoint.detour=1 -- Give cruise command. self:__Cruise(-2) @@ -6010,7 +6050,7 @@ function OPSGROUP:onafterPickup(From, Event, To) end -- ARMYGROUP - local waypoint=ARMYGROUP.AddWaypoint(self, Coordinate, nil, uid) ; waypoint.detour=true + local waypoint=ARMYGROUP.AddWaypoint(self, Coordinate, nil, uid) ; waypoint.detour=1 self:__Cruise(-2) @@ -6045,7 +6085,7 @@ function OPSGROUP:onafterLoading(From, Event, To) -- Check that group is NOT cargo and NOT acting as carrier already -- TODO: Need a better :IsBusy() function or :IsReadyForMission() :IsReadyForBoarding() :IsReadyForTransport() - if cargo.opsgroup:IsNotCargo() and not (cargo.opsgroup:IsPickingup() or cargo.opsgroup:IsLoading() or cargo.opsgroup:IsTransporting() or cargo.opsgroup:IsUnloading()) then + if cargo.opsgroup:IsNotCargo(true) and not (cargo.opsgroup:IsPickingup() or cargo.opsgroup:IsLoading() or cargo.opsgroup:IsTransporting() or cargo.opsgroup:IsUnloading()) then -- Check if cargo is in embark/pickup zone. local inzone=self.cargoTransport.embarkzone:IsCoordinateInZone(cargo.opsgroup:GetCoordinate()) @@ -6333,7 +6373,7 @@ function OPSGROUP:onafterTransport(From, Event, To) -- If this is a helo and no ZONE_AIRBASE was given, we make the helo land in the pickup zone. Coordinate:SetAltitude(200) - local waypoint=FLIGHTGROUP.AddWaypoint(self, Coordinate) ; waypoint.detour=true + local waypoint=FLIGHTGROUP.AddWaypoint(self, Coordinate) ; waypoint.detour=1 else self:E(self.lid.."ERROR: Carrier aircraft cannot land in Deploy zone! Specify a ZONE_AIRBASE as deploy zone") @@ -6356,7 +6396,7 @@ function OPSGROUP:onafterTransport(From, Event, To) end -- ARMYGROUP - local waypoint=ARMYGROUP.AddWaypoint(self, Coordinate, nil, self:GetWaypointCurrent().uid) ; waypoint.detour=true + local waypoint=ARMYGROUP.AddWaypoint(self, Coordinate, nil, self:GetWaypointCurrent().uid) ; waypoint.detour=1 -- Give cruise command. self:Cruise() @@ -6379,7 +6419,7 @@ function OPSGROUP:onafterTransport(From, Event, To) end -- NAVYGROUP - local waypoint=NAVYGROUP.AddWaypoint(self, Coordinate, nil, uid) ; waypoint.detour=true + local waypoint=NAVYGROUP.AddWaypoint(self, Coordinate, nil, uid) ; waypoint.detour=1 -- Give cruise command. self:Cruise() @@ -6612,6 +6652,9 @@ function OPSGROUP:onafterUnload(From, Event, To, OpsGroup, Coordinate, Activated -- Trigger "Disembarked" event. OpsGroup:Disembarked(OpsGroup:_GetMyCarrierGroup(), OpsGroup:_GetMyCarrierElement()) + + -- Trigger "Unloaded" event. + self:Unloaded(OpsGroup) -- Remove my carrier. OpsGroup:_RemoveMyCarrier() @@ -6623,10 +6666,21 @@ end -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. -function OPSGROUP:onafterUnloaded(From, Event, To) +-- @param #OPSGROUP OpsGroupCargo Cargo OPSGROUP that was unloaded from a carrier. +function OPSGROUP:onafterUnloaded(From, Event, To, OpsGroupCargo) + self:I(self.lid..string.format("Unloaded OPSGROUP %s", OpsGroupCargo:GetName())) +end + + +--- On after "UnloadingDone" event. +-- @param #OPSGROUP self +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +function OPSGROUP:onafterUnloadingDone(From, Event, To) -- Debug info - self:T(self.lid.."Cargo unloaded..") + self:T(self.lid.."Cargo unloading done..") -- Cancel landedAt task. if self:IsFlightgroup() and self:IsLandedAt() then @@ -6777,10 +6831,10 @@ function OPSGROUP:onafterBoard(From, Event, To, CarrierGroup, Carrier) self:ClearWaypoints(self.currentwp+1) if self.isArmygroup then - local waypoint=ARMYGROUP.AddWaypoint(self, Coordinate) ; waypoint.detour=0 + local waypoint=ARMYGROUP.AddWaypoint(self, Coordinate) ; waypoint.detour=1 self:Cruise() else - local waypoint=NAVYGROUP.AddWaypoint(self, Coordinate) ; waypoint.detour=0 + local waypoint=NAVYGROUP.AddWaypoint(self, Coordinate) ; waypoint.detour=1 self:Cruise() end @@ -7216,7 +7270,7 @@ function OPSGROUP:_CheckAmmoStatus() -- Guns. if self.outofGuns and ammo.Guns>0 then - self.outoffGuns=false + self.outofGuns=false end if ammo.Guns==0 and self.ammo.Guns>0 and not self.outofGuns then self.outofGuns=true @@ -7225,7 +7279,7 @@ function OPSGROUP:_CheckAmmoStatus() -- Rockets. if self.outofRockets and ammo.Rockets>0 then - self.outoffRockets=false + self.outofRockets=false end if ammo.Rockets==0 and self.ammo.Rockets>0 and not self.outofRockets then self.outofRockets=true @@ -7234,22 +7288,50 @@ function OPSGROUP:_CheckAmmoStatus() -- Bombs. if self.outofBombs and ammo.Bombs>0 then - self.outoffBombs=false + self.outofBombs=false end if ammo.Bombs==0 and self.ammo.Bombs>0 and not self.outofBombs then self.outofBombs=true self:OutOfBombs() end - -- Missiles. + -- Missiles (All). if self.outofMissiles and ammo.Missiles>0 then - self.outoffMissiles=false + self.outofMissiles=false end if ammo.Missiles==0 and self.ammo.Missiles>0 and not self.outofMissiles then self.outofMissiles=true self:OutOfMissiles() end + -- Missiles AA. + if self.outofMissilesAA and ammo.MissilesAA>0 then + self.outofMissilesAA=false + end + if ammo.MissilesAA and self.ammo.MissilesAA>0 and not self.outofMissilesAA then + self.outofMissilesAA=true + self:OutOfMissilesAA() + end + + -- Missiles AG. + if self.outofMissilesAG and ammo.MissilesAG>0 then + self.outofMissilesAG=false + end + if ammo.MissilesAG and self.ammo.MissilesAG>0 and not self.outofMissilesAG then + self.outofMissilesAG=true + self:OutOfMissilesAG() + end + + -- Missiles AS. + if self.outofMissilesAS and ammo.MissilesAS>0 then + self.outofMissilesAS=false + end + if ammo.MissilesAS and self.ammo.MissilesAS>0 and not self.outofMissilesAS then + self.outofMissilesAS=true + self:OutOfMissilesAS() + end + + -- Check if group is engaging. if self:IsEngaging() and ammo.Total==0 then self:Disengage() @@ -7725,7 +7807,7 @@ function OPSGROUP._TaskDone(group, opsgroup, task) -- Debug message. local text=string.format("_TaskDone %s", task.description) - opsgroup:T3(opsgroup.lid..text) + opsgroup:T(opsgroup.lid..text) -- Set current task to nil so that the next in line can be executed. if opsgroup then @@ -9352,7 +9434,8 @@ function OPSGROUP:_AddElementByName(unitname) -- Trigger spawned event if alive. if unit:IsAlive() then - self:ElementSpawned(element) + -- This needs to be slightly delayed (or moved elsewhere) or the first element will always trigger the group spawned event as it is not known that more elements are in the group. + self:__ElementSpawned(0.05, element) end return element diff --git a/Moose Development/Moose/Ops/OpsTransport.lua b/Moose Development/Moose/Ops/OpsTransport.lua index 61bd09885..351669719 100644 --- a/Moose Development/Moose/Ops/OpsTransport.lua +++ b/Moose Development/Moose/Ops/OpsTransport.lua @@ -849,8 +849,8 @@ function OPSTRANSPORT:onafterStatus(From, Event, To) local carrier=cargo.opsgroup:_GetMyCarrierElement() local name=carrier and carrier.name or "none" local cstate=carrier and carrier.status or "N/A" - text=text..string.format("\n- %s: %s [%s], weight=%d kg, carrier=%s [%s], delivered=%s", - cargo.opsgroup:GetName(), cargo.opsgroup.cargoStatus:upper(), cargo.opsgroup:GetState(), cargo.opsgroup:GetWeightTotal(), name, cstate, tostring(cargo.delivered)) + text=text..string.format("\n- %s: %s [%s], weight=%d kg, carrier=%s [%s], delivered=%s [UID=%s]", + cargo.opsgroup:GetName(), cargo.opsgroup.cargoStatus:upper(), cargo.opsgroup:GetState(), cargo.opsgroup:GetWeightTotal(), name, cstate, tostring(cargo.delivered), tostring(cargo.opsgroup.cargoTransportUID)) end text=text..string.format("\nCarriers:") diff --git a/Moose Development/Moose/Ops/Squadron.lua b/Moose Development/Moose/Ops/Squadron.lua index 266fe7f4c..2a535bbe2 100644 --- a/Moose Development/Moose/Ops/Squadron.lua +++ b/Moose Development/Moose/Ops/Squadron.lua @@ -608,15 +608,22 @@ end -- @return #number TACAN channel or *nil* if no channel is free. function SQUADRON:FetchTacan() + -- Get the smallest free channel if there is one. + local freechannel=nil for channel,free in pairs(self.tacanChannel) do - if free then - self:T(self.lid..string.format("Checking out Tacan channel %d", channel)) - self.tacanChannel[channel]=false - return channel + if free then + if freechannel==nil or channel