diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index 613e02475..6a76b5349 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -5968,6 +5968,14 @@ function WAREHOUSE:_SpawnAssetAircraft(alias, asset, request, parking, uncontrol _action=COORDINATE.WaypointAction.FromParkingAreaHot uncontrolled=false end + + local airstart=asset.takeoffType and asset.takeoffType==COORDINATE.WaypointType.TurningPoint or false + + if airstart then + _type=COORDINATE.WaypointType.TurningPoint + _action=COORDINATE.WaypointAction.TurningPoint + uncontrolled=false + end -- Set route points. @@ -5975,7 +5983,15 @@ function WAREHOUSE:_SpawnAssetAircraft(alias, asset, request, parking, uncontrol -- Get flight path if the group goes to another warehouse by itself. if request.toself then - local wp=self.airbase:GetCoordinate():WaypointAir("RADIO", _type, _action, 0, false, self.airbase, {}, "Parking") + + local coord=self.airbase:GetCoordinate() + + if airstart then + coord:SetAltitude(math.random(1000, 2000)) + end + + -- Single waypoint. + local wp=coord:WaypointAir("RADIO", _type, _action, 0, false, self.airbase, {}, "Parking") template.route.points={wp} else template.route.points=self:_GetFlightplan(asset, self.airbase, request.warehouse.airbase) @@ -5999,7 +6015,7 @@ function WAREHOUSE:_SpawnAssetAircraft(alias, asset, request, parking, uncontrol else - if #parking<#template.units then + if #parking<#template.units and not airstart then local text=string.format("ERROR: Not enough parking! Free parking = %d < %d aircraft to be spawned.", #parking, #template.units) self:_DebugMessage(text) return nil @@ -6021,14 +6037,25 @@ function WAREHOUSE:_SpawnAssetAircraft(alias, asset, request, parking, uncontrol unit.x=coord.x unit.y=coord.z unit.alt=coord.y + + if airstart then + unit.alt=math.random(1000, 2000) + end unit.parking_id = nil unit.parking = nil else - local coord=parking[i].Coordinate --Core.Point#COORDINATE - local terminal=parking[i].TerminalID --#number + local coord=nil --Core.Point#COORDINATE + local terminal=nil --#number + + if airstart then + coord=self.airbase:GetCoordinate():SetAltitude(math.random(1000, 2000)) + else + coord=parking[i].Coordinate + terminal=parking[i].TerminalID + end if self.Debug then coord:MarkToAll(string.format("Spawnplace unit %s terminal %d.", unit.name, terminal)) diff --git a/Moose Development/Moose/Ops/AirWing.lua b/Moose Development/Moose/Ops/AirWing.lua index e940de02d..102955153 100644 --- a/Moose Development/Moose/Ops/AirWing.lua +++ b/Moose Development/Moose/Ops/AirWing.lua @@ -47,10 +47,14 @@ -- -- @field Ops.RescueHelo#RESCUEHELO rescuehelo The rescue helo. -- @field Ops.RecoveryTanker#RECOVERYTANKER recoverytanker The recoverytanker. +-- +-- @field #string takeoffType Take of type. +-- @field #boolean despawnAfterLanding Aircraft are despawned after landing. +-- @field #boolean despawnAfterHolding Aircraft are despawned after holding. -- -- @extends Ops.Legion#LEGION ---- *I fly because it releases my mind from the tyranny of petty things* -- Antoine de Saint-Exupery +--- *I fly because it releases my mind from the tyranny of petty things.* -- Antoine de Saint-Exupery -- -- === -- @@ -171,14 +175,14 @@ AIRWING = { --- AIRWING class version. -- @field #string version -AIRWING.version="0.9.1" +AIRWING.version="0.9.2" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- ToDo list ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO: Check that airbase has enough parking spots if a request is BIG. --- TODO: Spawn in air ==> Needs WAREHOUSE update. +-- DONE: Spawn in air ==> Needs WAREHOUSE update. -- DONE: Spawn hot. -- DONE: Make special request to transfer squadrons to anther airwing (or warehouse). -- DONE: Add squadrons to warehouse. @@ -818,6 +822,77 @@ function AIRWING:SetAirboss(airboss) return self end +--- Set takeoff type. All assets of this squadron will be spawned with cold (default) or hot engines. +-- Spawning on runways is not supported. +-- @param #AIRWING self +-- @param #string TakeoffType Take off type: "Cold" (default) or "Hot" with engines on or "Air" for spawning in air. +-- @return #AIRWING self +function AIRWING:SetTakeoffType(TakeoffType) + TakeoffType=TakeoffType or "Cold" + if TakeoffType:lower()=="hot" then + self.takeoffType=COORDINATE.WaypointType.TakeOffParkingHot + elseif TakeoffType:lower()=="cold" then + self.takeoffType=COORDINATE.WaypointType.TakeOffParking + elseif TakeoffType:lower()=="air" then + self.takeoffType=COORDINATE.WaypointType.TurningPoint + else + self.takeoffType=COORDINATE.WaypointType.TakeOffParking + end + return self +end + +--- Set takeoff type cold (default). All assets of this squadron will be spawned with engines off (cold). +-- @param #AIRWING self +-- @return #AIRWING self +function AIRWING:SetTakeoffCold() + self:SetTakeoffType("Cold") + return self +end + +--- Set takeoff type hot. All assets of this squadron will be spawned with engines on (hot). +-- @param #AIRWING self +-- @return #AIRWING self +function AIRWING:SetTakeoffHot() + self:SetTakeoffType("Hot") + return self +end + +--- Set takeoff type air. All assets of this squadron will be spawned in air above the airbase. +-- @param #AIRWING self +-- @return #AIRWING self +function AIRWING:SetTakeoffAir() + self:SetTakeoffType("Air") + return self +end + +--- Set despawn after landing. Aircraft will be despawned after the landing event. +-- Can help to avoid DCS AI taxiing issues. +-- @param #AIRWING self +-- @param #boolean Switch If `true` (default), activate despawn after landing. +-- @return #AIRWING self +function AIRWING:SetDespawnAfterLanding(Switch) + if Switch then + self.despawnAfterLanding=Switch + else + self.despawnAfterLanding=true + end + return self +end + +--- Set despawn after holding. Aircraft will be despawned when they arrive at their holding position at the airbase. +-- Can help to avoid DCS AI taxiing issues. +-- @param #AIRWING self +-- @param #boolean Switch If `true` (default), activate despawn after landing. +-- @return #AIRWING self +function AIRWING:SetDespawnAfterHolding(Switch) + if Switch then + self.despawnAfterHolding=Switch + else + self.despawnAfterHolding=true + end + return self +end + ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Start & Status ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- diff --git a/Moose Development/Moose/Ops/ArmyGroup.lua b/Moose Development/Moose/Ops/ArmyGroup.lua index 88b252ec4..b506021c6 100644 --- a/Moose Development/Moose/Ops/ArmyGroup.lua +++ b/Moose Development/Moose/Ops/ArmyGroup.lua @@ -64,7 +64,7 @@ ARMYGROUP = { --- Army Group version. -- @field #string version -ARMYGROUP.version="0.7.2" +ARMYGROUP.version="0.7.3" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list @@ -669,18 +669,50 @@ function ARMYGROUP:Status() if alive~=nil then if self.verbose>=1 then + + -- Number of elements. + local nelem=self:CountElements() + local Nelem=#self.elements -- Get number of tasks and missions. local nTaskTot, nTaskSched, nTaskWP=self:CountRemainingTasks() local nMissions=self:CountRemainingMissison() - local roe=self:GetROE() - local alarm=self:GetAlarmstate() + -- ROE and Alarm State. + local roe=self:GetROE() or -1 + local als=self:GetAlarmstate() or -1 + + -- Waypoint stuff. + local wpidxCurr=self.currentwp + local wpuidCurr=self:GetWaypointUIDFromIndex(wpidxCurr) or 0 + local wpidxNext=self:GetWaypointIndexNext() or 0 + local wpuidNext=self:GetWaypointUIDFromIndex(wpidxNext) or 0 + local wpN=#self.waypoints or 0 + local wpF=tostring(self.passedfinalwp) + + -- Speed. local speed=UTILS.MpsToKnots(self.velocity or 0) local speedEx=UTILS.MpsToKnots(self:GetExpectedSpeed()) - local formation=self.option.Formation or "unknown" - local ammo=self:GetAmmoTot() + -- Altitude. + local alt=self.position and self.position.y or 0 + + -- Heading in degrees. + local hdg=self.heading or 0 + + -- TODO: GetFormation function. + local formation=self.option.Formation or "unknown" + + -- Life points. + local life=self.life or 0 + + -- Total ammo. + local ammo=self:GetAmmoTot().Total + + -- Detected units. + local ndetected=self.detectionOn and tostring(self.detectedunits:Count()) or "OFF" + + -- Get cargo weight. local cargo=0 for _,_element in pairs(self.elements) do local element=_element --Ops.OpsGroup#OPSGROUP.Element @@ -688,9 +720,9 @@ function ARMYGROUP:Status() end -- Info text. - local text=string.format("%s [ROE-AS=%d-%d T/M=%d/%d]: Wp=%d/%d-->%d (final %s), Life=%.1f, Speed=%.1f (%d), Heading=%03d, Ammo=%d, Cargo=%.1f", - fsmstate, roe, alarm, nTaskTot, nMissions, self.currentwp, #self.waypoints, self:GetWaypointIndexNext(), tostring(self.passedfinalwp), self.life or 0, speed, speedEx, self.heading or 0, ammo.Total, cargo) - self:T(self.lid..text) + local text=string.format("%s [%d/%d]: ROE/AS=%d/%d | T/M=%d/%d | Wp=%d[%d]-->%d[%d]/%d [%s] | Life=%.1f | v=%.1f (%d) | Hdg=%03d | Ammo=%d | Detect=%s | Cargo=%.1f", + fsmstate, nelem, Nelem, roe, als, nTaskTot, nMissions, wpidxCurr, wpuidCurr, wpidxNext, wpuidNext, wpN, wpF, life, speed, speedEx, hdg, ammo, ndetected, cargo) + self:I(self.lid..text) end @@ -844,6 +876,9 @@ function ARMYGROUP:onafterSpawned(From, Event, To) -- Set default Alarm State. self:SwitchAlarmstate(self.option.Alarm) + -- Set emission. + self:SwitchEmission(self.option.Emission) + -- Set default EPLRS. self:SwitchEPLRS(self.option.EPLRS) @@ -1145,7 +1180,7 @@ function ARMYGROUP:onbeforeRearm(From, Event, To, Coordinate, Formation) local allowed=true -- Pause current mission. - if self.currentmission and self.currentmission>0 then + if self:IsOnMission() then self:T(self.lid.."Rearm command but have current mission ==> Pausing mission!") self:PauseMission() dt=-0.1 diff --git a/Moose Development/Moose/Ops/Auftrag.lua b/Moose Development/Moose/Ops/Auftrag.lua index 905a68bb0..87c481c84 100644 --- a/Moose Development/Moose/Ops/Auftrag.lua +++ b/Moose Development/Moose/Ops/Auftrag.lua @@ -2303,7 +2303,7 @@ function AUFTRAG:SetTime(ClockStart, ClockStop) return self end ---- Set time how low the mission is executed. Once this time limit has passed, the mission is cancelled. +--- Set time how long the mission is executed. Once this time limit has passed, the mission is cancelled. -- @param #AUFTRAG self -- @param #number Duration Duration in seconds. -- @return #AUFTRAG self diff --git a/Moose Development/Moose/Ops/Chief.lua b/Moose Development/Moose/Ops/Chief.lua index 50130d0a2..18536827f 100644 --- a/Moose Development/Moose/Ops/Chief.lua +++ b/Moose Development/Moose/Ops/Chief.lua @@ -2513,6 +2513,7 @@ function CHIEF:_GetMissionPerformanceFromTarget(Target) -- Bomb runway. table.insert(missionperf, self:_CreateMissionPerformance(AUFTRAG.Type.BOMBRUNWAY, 100)) + table.insert(missionperf, self:_CreateMissionPerformance(AUFTRAG.Type.ARTY, 30)) elseif static then @@ -2520,10 +2521,10 @@ function CHIEF:_GetMissionPerformanceFromTarget(Target) -- STATIC --- - table.insert(missionperf, self:_CreateMissionPerformance(AUFTRAG.Type.BAI, 100)) - table.insert(missionperf, self:_CreateMissionPerformance(AUFTRAG.Type.BOMBING, 70)) + table.insert(missionperf, self:_CreateMissionPerformance(AUFTRAG.Type.BAI, 100)) + table.insert(missionperf, self:_CreateMissionPerformance(AUFTRAG.Type.BOMBING, 70)) table.insert(missionperf, self:_CreateMissionPerformance(AUFTRAG.Type.BOMBCARPET, 50)) - table.insert(missionperf, self:_CreateMissionPerformance(AUFTRAG.Type.ARTY, 30)) + table.insert(missionperf, self:_CreateMissionPerformance(AUFTRAG.Type.ARTY, 30)) elseif scenery then @@ -2531,10 +2532,10 @@ function CHIEF:_GetMissionPerformanceFromTarget(Target) -- SCENERY --- - table.insert(missionperf, self:_CreateMissionPerformance(AUFTRAG.Type.STRIKE, 100)) - table.insert(missionperf, self:_CreateMissionPerformance(AUFTRAG.Type.BOMBING, 70)) + table.insert(missionperf, self:_CreateMissionPerformance(AUFTRAG.Type.STRIKE, 100)) + table.insert(missionperf, self:_CreateMissionPerformance(AUFTRAG.Type.BOMBING, 70)) table.insert(missionperf, self:_CreateMissionPerformance(AUFTRAG.Type.BOMBCARPET, 50)) - table.insert(missionperf, self:_CreateMissionPerformance(AUFTRAG.Type.ARTY, 30)) + table.insert(missionperf, self:_CreateMissionPerformance(AUFTRAG.Type.ARTY, 30)) elseif coordinate then @@ -2542,9 +2543,9 @@ function CHIEF:_GetMissionPerformanceFromTarget(Target) -- COORDINATE --- - table.insert(missionperf, self:_CreateMissionPerformance(AUFTRAG.Type.BOMBING, 100)) + table.insert(missionperf, self:_CreateMissionPerformance(AUFTRAG.Type.BOMBING, 100)) table.insert(missionperf, self:_CreateMissionPerformance(AUFTRAG.Type.BOMBCARPET, 50)) - table.insert(missionperf, self:_CreateMissionPerformance(AUFTRAG.Type.ARTY, 30)) + table.insert(missionperf, self:_CreateMissionPerformance(AUFTRAG.Type.ARTY, 30)) end diff --git a/Moose Development/Moose/Ops/Cohort.lua b/Moose Development/Moose/Ops/Cohort.lua index a41fc240e..7da4b9eaa 100644 --- a/Moose Development/Moose/Ops/Cohort.lua +++ b/Moose Development/Moose/Ops/Cohort.lua @@ -83,7 +83,7 @@ COHORT = { --- COHORT class version. -- @field #string version -COHORT.version="0.3.0" +COHORT.version="0.3.2" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list diff --git a/Moose Development/Moose/Ops/FlightGroup.lua b/Moose Development/Moose/Ops/FlightGroup.lua index 6dd769eb3..530d5413c 100644 --- a/Moose Development/Moose/Ops/FlightGroup.lua +++ b/Moose Development/Moose/Ops/FlightGroup.lua @@ -51,7 +51,8 @@ -- @field #number Tparking Abs. mission time stamp when the group was spawned uncontrolled and is parking. -- @field #table menu F10 radio menu. -- @field #string controlstatus Flight control status. --- @field #boolean despawnAfterLanding If true, group is despawned after landed at an airbase. +-- @field #boolean despawnAfterLanding If `true`, group is despawned after landed at an airbase. +-- @field #boolean despawnAfterHolding If `true`, group is despawned after reaching the holding point. -- @field #number RTBRecallCount Number that counts RTB calls. -- -- @extends Ops.OpsGroup#OPSGROUP @@ -182,7 +183,7 @@ FLIGHTGROUP.RadioMessage = { --- FLIGHTGROUP class version. -- @field #string version -FLIGHTGROUP.version="0.7.2" +FLIGHTGROUP.version="0.7.3" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list @@ -522,6 +523,14 @@ function FLIGHTGROUP:SetDespawnAfterLanding() return self end +--- Enable that the group is despawned after holding. This can be useful to avoid DCS taxi issues with other AI or players or jamming taxiways. +-- @param #FLIGHTGROUP self +-- @return #FLIGHTGROUP self +function FLIGHTGROUP:SetDespawnAfterHolding() + self.despawnAfterHolding=true + return self +end + --- Check if flight is parking. -- @param #FLIGHTGROUP self @@ -818,25 +827,63 @@ function FLIGHTGROUP:Status() -- Short info. if self.verbose>=1 then + -- Number of elements. local nelem=self:CountElements() local Nelem=#self.elements + + -- Get number of tasks and missions. local nTaskTot, nTaskSched, nTaskWP=self:CountRemainingTasks() local nMissions=self:CountRemainingMissison() - local currT=self.taskcurrent or "None" - local currM=self.currentmission or "None" - local currW=self.currentwp or 0 - local nWp=self.waypoints and #self.waypoints or 0 + + -- ROE and Alarm State. + local roe=self:GetROE() or -1 + local als=self:GetAlarmstate() or -1 + + -- Waypoint stuff. + local wpidxCurr=self.currentwp + local wpuidCurr=self:GetWaypointUIDFromIndex(wpidxCurr) or 0 + local wpidxNext=self:GetWaypointIndexNext() or 0 + local wpuidNext=self:GetWaypointUIDFromIndex(wpidxNext) or 0 + local wpN=#self.waypoints or 0 + local wpF=tostring(self.passedfinalwp) + + -- Speed. + local speed=UTILS.MpsToKnots(self.velocity or 0) + local speedEx=UTILS.MpsToKnots(self:GetExpectedSpeed()) + + -- Altitude. + local alt=self.position and self.position.y or 0 + + -- Heading in degrees. + local hdg=self.heading or 0 + + -- TODO: GetFormation function. + local formation=self.option.Formation or "unknown" + + -- Life points. + local life=self.life or 0 + + -- Total ammo. + local ammo=self:GetAmmoTot().Total + + -- Detected units. + local ndetected=self.detectionOn and tostring(self.detectedunits:Count()) or "OFF" + + -- Get cargo weight. + local cargo=0 + for _,_element in pairs(self.elements) do + local element=_element --Ops.OpsGroup#OPSGROUP.Element + cargo=cargo+element.weightCargo + end + + -- Home and destination base. local home=self.homebase and self.homebase:GetName() or "unknown" local dest=self.destbase and self.destbase:GetName() or "unknown" - local fc=self.flightcontrol and self.flightcontrol.airbasename or "N/A" local curr=self.currbase and self.currbase:GetName() or "N/A" - - local ndetected=self.detectionOn and tostring(self.detectedunits:Count()) or "OFF" - - local text=string.format("Status %s [%d/%d]: T/M=%d/%d [Current %s/%s] [%s], Waypoint=%d/%d [%s], Base=%s [%s-->%s]", - fsmstate, nelem, Nelem, - nTaskTot, nMissions, currT, currM, tostring(self:HasTaskController()), - currW, nWp, tostring(self.passedfinalwp), curr, home, dest) + + -- Info text. + local text=string.format("%s [%d/%d]: ROE/AS=%d/%d | T/M=%d/%d | Wp=%d[%d]-->%d[%d]/%d [%s] | Life=%.1f | v=%.1f (%d) | Hdg=%03d | Ammo=%d | Detect=%s | Cargo=%.1f | Base=%s [%s-->%s]", + fsmstate, nelem, Nelem, roe, als, nTaskTot, nMissions, wpidxCurr, wpuidCurr, wpidxNext, wpuidNext, wpN, wpF, life, speed, speedEx, hdg, ammo, ndetected, cargo, curr, home, dest) self:I(self.lid..text) end @@ -1341,31 +1388,46 @@ function FLIGHTGROUP:onafterElementLanded(From, Event, To, Element, airbase) -- Debug info. self:T2(self.lid..string.format("Element landed %s at %s airbase", Element.name, airbase and airbase:GetName() or "unknown")) + + -- Set element status. + self:_UpdateStatus(Element, OPSGROUP.ElementStatus.LANDED, airbase) - if self.despawnAfterLanding then + -- Helos with skids land directly on parking spots. + if self.isHelo then - -- Despawn the element. - self:DespawnElement(Element) - - else - - -- Set element status. - self:_UpdateStatus(Element, OPSGROUP.ElementStatus.LANDED, airbase) - - -- Helos with skids land directly on parking spots. - if self.isHelo then - - local Spot=self:GetParkingSpot(Element, 10, airbase) - - if Spot then - self:_SetElementParkingAt(Element, Spot) - self:_UpdateStatus(Element, OPSGROUP.ElementStatus.ARRIVED) - end + local Spot=self:GetParkingSpot(Element, 10, airbase) + if Spot then + self:_SetElementParkingAt(Element, Spot) + self:_UpdateStatus(Element, OPSGROUP.ElementStatus.ARRIVED) end - end - + end + + -- Despawn after landing. + if self.despawnAfterLanding then + + if self.legion then + + if airbase and self.legion.airbase and airbase.AirbaseName==self.legion.airbase.AirbaseName then + + if self:IsLanded() then + -- Everybody landed ==> Return to legion. Will despawn the last one. + self:ReturnToLegion() + else + -- Despawn the element. + self:DespawnElement(Element) + end + + end + + else + + -- Despawn the element. + self:DespawnElement(Element) + + end + end end --- On after "ElementArrived" event. @@ -1744,7 +1806,8 @@ function FLIGHTGROUP:onafterArrived(From, Event, To) self:T(self.lid..string.format("Airwing asset group %s arrived ==> Adding asset back to stock of airwing %s", self.groupname, airwing.alias)) -- Add the asset back to the airwing. - airwing:AddAsset(self.group, 1) + --airwing:AddAsset(self.group, 1) + self:ReturnToLegion(1) elseif self.isLandingAtAirbase then @@ -2687,6 +2750,16 @@ function FLIGHTGROUP:onafterHolding(From, Event, To) -- Set holding flag to 0 (just in case). self.flaghold:Set(0) + + -- Despawn after holding. + if self.despawnAfterHolding then + if self.legion then + self:ReturnToLegion(1) + else + self:Despawn(1) + end + return + end -- Holding time stamp. self.Tholding=timer.getAbsTime() diff --git a/Moose Development/Moose/Ops/Legion.lua b/Moose Development/Moose/Ops/Legion.lua index bb4838d3a..8a7963164 100644 --- a/Moose Development/Moose/Ops/Legion.lua +++ b/Moose Development/Moose/Ops/Legion.lua @@ -47,7 +47,7 @@ LEGION = { --- LEGION class version. -- @field #string version -LEGION.version="0.3.1" +LEGION.version="0.3.2" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- ToDo list @@ -1229,7 +1229,7 @@ function LEGION:onafterNewAsset(From, Event, To, asset, assignment) end -- Set takeoff type. - asset.takeoffType=cohort.takeoffType + asset.takeoffType=cohort.takeoffType~=nil and cohort.takeoffType or self.takeoffType -- Set parking IDs. asset.parkingIDs=cohort.parkingIDs @@ -1335,6 +1335,7 @@ function LEGION:onafterAssetSpawned(From, Event, To, group, asset, request) -- Did not return yet. asset.Treturned=nil + --- -- Cohort --- @@ -1360,7 +1361,7 @@ function LEGION:onafterAssetSpawned(From, Event, To, group, asset, request) if cohort.fuellowRefuel then flightgroup:SetFuelLowRefuel(cohort.fuellowRefuel) end - + -- Assignment. local assignment=request.assignment @@ -1379,6 +1380,16 @@ function LEGION:onafterAssetSpawned(From, Event, To, group, asset, request) -- Get Mission (if any). local mission=self:GetMissionByID(uid) + + local despawnLanding=cohort.despawnAfterLanding~=nil and cohort.despawnAfterLanding or self.despawnAfterLanding + if despawnLanding then + flightgroup:SetDespawnAfterLanding() + end + + local despawnHolding=cohort.despawnAfterHolding~=nil and cohort.despawnAfterHolding or self.despawnAfterHolding + if despawnHolding then + flightgroup:SetDespawnAfterHolding() + end -- Add mission to flightgroup queue. if mission then diff --git a/Moose Development/Moose/Ops/NavyGroup.lua b/Moose Development/Moose/Ops/NavyGroup.lua index f7caccd32..d276d70f1 100644 --- a/Moose Development/Moose/Ops/NavyGroup.lua +++ b/Moose Development/Moose/Ops/NavyGroup.lua @@ -90,18 +90,18 @@ NAVYGROUP = { --- NavyGroup version. -- @field #string version -NAVYGROUP.version="0.7.1" +NAVYGROUP.version="0.7.3" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- TODO: Add RTZ. -- TODO: Add Retreat. --- TODO: Add EngageTarget. -- TODO: Submaries. -- TODO: Extend, shorten turn into wind windows. -- TODO: Skipper menu. +-- DONE: Add EngageTarget. +-- DONE: Add RTZ. -- DONE: Collision warning. -- DONE: Detour, add temporary waypoint and resume route. -- DONE: Stop and resume route. @@ -790,17 +790,19 @@ function NAVYGROUP:Status(From, Event, To) if alive~=nil then if self.verbose>=1 then + + -- Number of elements. + local nelem=self:CountElements() + local Nelem=#self.elements -- Get number of tasks and missions. local nTaskTot, nTaskSched, nTaskWP=self:CountRemainingTasks() local nMissions=self:CountRemainingMissison() - - local intowind=self:IsSteamingIntoWind() and UTILS.SecondsToClock(self.intowind.Tstop-timer.getAbsTime(), true) or "N/A" - local turning=tostring(self:IsTurning()) - local alt=self.position and self.position.y or 0 - local speed=UTILS.MpsToKnots(self.velocity or 0) - local speedExpected=UTILS.MpsToKnots(self:GetExpectedSpeed()) + -- ROE and Alarm State. + local roe=self:GetROE() or -1 + local als=self:GetAlarmstate() or -1 + -- Waypoint stuff. local wpidxCurr=self.currentwp local wpuidCurr=self:GetWaypointUIDFromIndex(wpidxCurr) or 0 @@ -808,16 +810,40 @@ function NAVYGROUP:Status(From, Event, To) local wpuidNext=self:GetWaypointUIDFromIndex(wpidxNext) or 0 local wpN=#self.waypoints or 0 local wpF=tostring(self.passedfinalwp) - local wpDist=UTILS.MetersToNM(self:GetDistanceToWaypoint() or 0) - local wpETA=UTILS.SecondsToClock(self:GetTimeToWaypoint() or 0, true) - -- Current ROE and alarm state. - local roe=self:GetROE() or 0 - local als=self:GetAlarmstate() or 0 - + -- Speed. + local speed=UTILS.MpsToKnots(self.velocity or 0) + local speedEx=UTILS.MpsToKnots(self:GetExpectedSpeed()) + + -- Altitude. + local alt=self.position and self.position.y or 0 + + -- Heading in degrees. + local hdg=self.heading or 0 + + -- Life points. + local life=self.life or 0 + + -- Total ammo. + local ammo=self:GetAmmoTot().Total + + -- Detected units. + local ndetected=self.detectionOn and tostring(self.detectedunits:Count()) or "OFF" + + -- Get cargo weight. + local cargo=0 + for _,_element in pairs(self.elements) do + local element=_element --Ops.OpsGroup#OPSGROUP.Element + cargo=cargo+element.weightCargo + end + + -- Into wind and turning status. + local intowind=self:IsSteamingIntoWind() and UTILS.SecondsToClock(self.intowind.Tstop-timer.getAbsTime(), true) or "N/A" + local turning=tostring(self:IsTurning()) + -- Info text. - local text=string.format("%s [ROE=%d,AS=%d, T/M=%d/%d]: Wp=%d[%d]-->%d[%d] /%d [%s] Dist=%.1f NM ETA=%s - Speed=%.1f (%.1f) kts, Depth=%.1f m, Hdg=%03d, Turn=%s Collision=%d IntoWind=%s", - fsmstate, roe, als, nTaskTot, nMissions, wpidxCurr, wpuidCurr, wpidxNext, wpuidNext, wpN, wpF, wpDist, wpETA, speed, speedExpected, alt, self.heading or 0, turning, freepath, intowind) + local text=string.format("%s [%d/%d]: ROE/AS=%d/%d | T/M=%d/%d | Wp=%d[%d]-->%d[%d]/%d [%s] | Life=%.1f | v=%.1f (%d) | Hdg=%03d | Ammo=%d | Detect=%s | Cargo=%.1f | Turn=%s Collision=%d IntoWind=%s", + fsmstate, nelem, Nelem, roe, als, nTaskTot, nMissions, wpidxCurr, wpuidCurr, wpidxNext, wpuidNext, wpN, wpF, life, speed, speedEx, hdg, ammo, ndetected, cargo, turning, freepath, intowind) self:I(self.lid..text) end diff --git a/Moose Development/Moose/Ops/OpsGroup.lua b/Moose Development/Moose/Ops/OpsGroup.lua index 1ae612d69..8907ad8b6 100644 --- a/Moose Development/Moose/Ops/OpsGroup.lua +++ b/Moose Development/Moose/Ops/OpsGroup.lua @@ -1751,7 +1751,7 @@ end function OPSGROUP:ReturnToLegion(Delay) if Delay and Delay>0 then - self.scheduleIDDespawn=self:ScheduleOnce(Delay, OPSGROUP.ReturnToLegion, self) + self:ScheduleOnce(Delay, OPSGROUP.ReturnToLegion, self) else if self.legion then @@ -4115,7 +4115,7 @@ function OPSGROUP:onafterTaskDone(From, Event, To, Task) --- -- Mission Paused: Do nothing! Just set the current mission to nil so we can launch a new one. --- - if self.currentmission and self.currentmission==Mission.auftragsnummer then + if self:IsOnMission(Mission.auftragsnummer) then self.currentmission=nil end -- Remove mission waypoints. @@ -4409,9 +4409,23 @@ end --- Check if group is currently on a mission. -- @param #OPSGROUP self --- @return #boolean If `true`, group is currently on a mission -function OPSGROUP:IsOnMission() - return self.currentmission~=nil +-- @param #number MissionUID (Optional) Check if group is currently on a mission with this UID. Default is to check for any current mission. +-- @return #boolean If `true`, group is currently on a mission. +function OPSGROUP:IsOnMission(MissionUID) + if self.currentmission==nil then + -- No current mission. + return false + else + if MissionUID then + -- Return if on specific mission. + return MissionUID==self.currentmission + else + -- Is on any mission. + return true + end + end + -- Is on any mission. + return true end --- On before "MissionStart" event. @@ -4582,7 +4596,7 @@ end -- @param Ops.Auftrag#AUFTRAG Mission The mission to be cancelled. function OPSGROUP:onafterMissionCancel(From, Event, To, Mission) - if self.currentmission and Mission.auftragsnummer==self.currentmission then + if self:IsOnMission(Mission.auftragsnummer) then --- -- Current Mission @@ -4661,7 +4675,7 @@ function OPSGROUP:onafterMissionDone(From, Event, To, Mission) Mission:SetGroupStatus(self, AUFTRAG.GroupStatus.DONE) -- Set current mission to nil. - if self.currentmission and Mission.auftragsnummer==self.currentmission then + if self:IsOnMission(Mission.auftragsnummer) then self.currentmission=nil end @@ -6261,13 +6275,16 @@ function OPSGROUP:Teleport(Coordinate, Delay) --Coordinate:MarkToAll("Teleport "..self.groupname) -- Check if we have a mission running. - if self.currentmission>0 then + if self:IsOnMission() then self:T(self.lid.."Pausing current mission") self:PauseMission() end -- Get copy of template. local Template=UTILS.DeepCopy(self.template) --DCS#Template + + -- Set late activation of template to current state. + Template.lateActivation=self:IsLateActivated() -- Template units. local units=Template.units diff --git a/Moose Development/Moose/Ops/Squadron.lua b/Moose Development/Moose/Ops/Squadron.lua index 58e9a9257..43d3c7a22 100644 --- a/Moose Development/Moose/Ops/Squadron.lua +++ b/Moose Development/Moose/Ops/Squadron.lua @@ -44,8 +44,10 @@ -- @field #table tacanChannel List of TACAN channels available to the squadron. -- @field #number radioFreq Radio frequency in MHz the squad uses. -- @field #number radioModu Radio modulation the squad uses. --- @field #number takeoffType Take of type. +-- @field #string takeoffType Take of type. -- @field #table parkingIDs Parking IDs for this squadron. +-- @field #boolean despawnAfterLanding Aircraft are despawned after landing. +-- @field #boolean despawnAfterHolding Aircraft are despawned after holding. -- @extends Ops.Cohort#COHORT --- *It is unbelievable what a squadron of twelve aircraft did to tip the balance* -- Adolf Galland @@ -58,8 +60,6 @@ -- -- -- --- --- -- @field #SQUADRON SQUADRON = { ClassName = "SQUADRON", @@ -74,7 +74,7 @@ SQUADRON = { --- SQUADRON class version. -- @field #string version -SQUADRON.version="0.8.0" +SQUADRON.version="0.8.1" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list @@ -147,7 +147,7 @@ end --- Set takeoff type. All assets of this squadron will be spawned with cold (default) or hot engines. -- Spawning on runways is not supported. -- @param #SQUADRON self --- @param #string TakeoffType Take off type: "Cold" (default) or "Hot" with engines on. +-- @param #string TakeoffType Take off type: "Cold" (default) or "Hot" with engines on or "Air" for spawning in air. -- @return #SQUADRON self function SQUADRON:SetTakeoffType(TakeoffType) TakeoffType=TakeoffType or "Cold" @@ -155,6 +155,8 @@ function SQUADRON:SetTakeoffType(TakeoffType) self.takeoffType=COORDINATE.WaypointType.TakeOffParkingHot elseif TakeoffType:lower()=="cold" then self.takeoffType=COORDINATE.WaypointType.TakeOffParking + elseif TakeoffType:lower()=="air" then + self.takeoffType=COORDINATE.WaypointType.TurningPoint else self.takeoffType=COORDINATE.WaypointType.TakeOffParking end @@ -177,6 +179,43 @@ function SQUADRON:SetTakeoffHot() return self end +--- Set takeoff type air. All assets of this squadron will be spawned in air above the airbase. +-- @param #SQUADRON self +-- @return #SQUADRON self +function SQUADRON:SetTakeoffAir() + self:SetTakeoffType("Air") + return self +end + +--- Set despawn after landing. Aircraft will be despawned after the landing event. +-- Can help to avoid DCS AI taxiing issues. +-- @param #SQUADRON self +-- @param #boolean Switch If `true` (default), activate despawn after landing. +-- @return #SQUADRON self +function SQUADRON:SetDespawnAfterLanding(Switch) + if Switch then + self.despawnAfterLanding=Switch + else + self.despawnAfterLanding=true + end + return self +end + +--- Set despawn after holding. Aircraft will be despawned when they arrive at their holding position at the airbase. +-- Can help to avoid DCS AI taxiing issues. +-- @param #SQUADRON self +-- @param #boolean Switch If `true` (default), activate despawn after holding. +-- @return #SQUADRON self +function SQUADRON:SetDespawnAfterHolding(Switch) + if Switch then + self.despawnAfterHolding=Switch + else + self.despawnAfterHolding=true + end + return self +end + + --- Set low fuel threshold. -- @param #SQUADRON self -- @param #number LowFuel Low fuel threshold in percent. Default 25. diff --git a/Moose Development/Moose/Utilities/Utils.lua b/Moose Development/Moose/Utilities/Utils.lua index 56d6e63c8..f45cd7ab5 100644 --- a/Moose Development/Moose/Utilities/Utils.lua +++ b/Moose Development/Moose/Utilities/Utils.lua @@ -809,7 +809,7 @@ function UTILS.BeaufortScale(speed) return bn,bd end ---- Split string at seperators. C.f. http://stackoverflow.com/questions/1426954/split-string-in-lua +--- Split string at seperators. C.f. [split-string-in-lua](http://stackoverflow.com/questions/1426954/split-string-in-lua). -- @param #string str Sting to split. -- @param #string sep Speparator for split. -- @return #table Split text.