From bd9022c01097486fca40e24306db198346db43c4 Mon Sep 17 00:00:00 2001 From: Frank Date: Tue, 31 Oct 2023 10:45:56 +0100 Subject: [PATCH 01/26] ARMYGROUP Added parameter to delay init group --- Moose Development/Moose/Ops/ArmyGroup.lua | 144 ++++++++++++---------- Moose Development/Moose/Ops/OpsGroup.lua | 6 +- Moose Development/Moose/Wrapper/Group.lua | 2 + 3 files changed, 83 insertions(+), 69 deletions(-) diff --git a/Moose Development/Moose/Ops/ArmyGroup.lua b/Moose Development/Moose/Ops/ArmyGroup.lua index 1bbad51e8..ac893ae85 100644 --- a/Moose Development/Moose/Ops/ArmyGroup.lua +++ b/Moose Development/Moose/Ops/ArmyGroup.lua @@ -2043,82 +2043,90 @@ end -- @param #ARMYGROUP self -- @param #table Template Template used to init the group. Default is `self.template`. -- @return #ARMYGROUP self -function ARMYGROUP:_InitGroup(Template) +function ARMYGROUP:_InitGroup(Template, Delay) - -- First check if group was already initialized. - if self.groupinitialized then - self:T(self.lid.."WARNING: Group was already initialized! Will NOT do it again!") - return - end - - -- Get template of group. - local template=Template or self:_GetTemplate() - - -- Ground are always AI. - self.isAI=true - - -- Is (template) group late activated. - self.isLateActivated=template.lateActivation - - -- Ground groups cannot be uncontrolled. - self.isUncontrolled=false - - -- Max speed in km/h. - self.speedMax=self.group:GetSpeedMax() - - -- Is group mobile? - if self.speedMax>3.6 then - self.isMobile=true + if Delay and Delay>0 then + self:ScheduleOnce(Delay, ARMYGROUP._InitGroup, self, Template, 0) else - self.isMobile=false - end - - -- Cruise speed in km/h - self.speedCruise=self.speedMax*0.7 - - -- Group ammo. - self.ammo=self:GetAmmoTot() - - -- Radio parameters from template. - self.radio.On=false -- Radio is always OFF for ground. - self.radio.Freq=133 - self.radio.Modu=radio.modulation.AM - - -- Set default radio. - self:SetDefaultRadio(self.radio.Freq, self.radio.Modu, self.radio.On) - - -- Get current formation from first waypoint. - self.option.Formation=template.route.points[1].action - - -- Set default formation to "on road". - self.optionDefault.Formation=ENUMS.Formation.Vehicle.OnRoad - -- Default TACAN off. - self:SetDefaultTACAN(nil, nil, nil, nil, true) - self.tacan=UTILS.DeepCopy(self.tacanDefault) + -- First check if group was already initialized. + if self.groupinitialized then + self:T(self.lid.."WARNING: Group was already initialized! Will NOT do it again!") + return + end + + self:I(self.lid.."FF Initializing Group") - -- Units of the group. - local units=self.group:GetUnits() + -- Get template of group. + local template=Template or self:_GetTemplate() + + -- Ground are always AI. + self.isAI=true + + -- Is (template) group late activated. + self.isLateActivated=template.lateActivation + + -- Ground groups cannot be uncontrolled. + self.isUncontrolled=false + + -- Max speed in km/h. + self.speedMax=self.group:GetSpeedMax() + + -- Is group mobile? + if self.speedMax>3.6 then + self.isMobile=true + else + self.isMobile=false + end + + -- Cruise speed in km/h + self.speedCruise=self.speedMax*0.7 + + -- Group ammo. + self.ammo=self:GetAmmoTot() + + -- Radio parameters from template. + self.radio.On=false -- Radio is always OFF for ground. + self.radio.Freq=133 + self.radio.Modu=radio.modulation.AM + + -- Set default radio. + self:SetDefaultRadio(self.radio.Freq, self.radio.Modu, self.radio.On) + + -- Get current formation from first waypoint. + self.option.Formation=template.route.points[1].action + + -- Set default formation to "on road". + self.optionDefault.Formation=ENUMS.Formation.Vehicle.OnRoad - -- DCS group. - local dcsgroup=Group.getByName(self.groupname) - local size0=dcsgroup:getInitialSize() + -- Default TACAN off. + self:SetDefaultTACAN(nil, nil, nil, nil, true) + self.tacan=UTILS.DeepCopy(self.tacanDefault) + + -- Units of the group. + local units=self.group:GetUnits() + + -- DCS group. + local dcsgroup=Group.getByName(self.groupname) + local size0=dcsgroup:getInitialSize() + local u=dcsgroup:getUnits() + + -- Quick check. + if #units~=size0 then + self:T(self.lid..string.format("ERROR: Got #units=%d but group consists of %d units! u=%d", #units, size0, #u)) + end + + -- Add elemets. + for _,unit in pairs(units) do + local unitname=unit:GetName() + self:_AddElementByName(unitname) + end + - -- Quick check. - if #units~=size0 then - self:T(self.lid..string.format("ERROR: Got #units=%d but group consists of %d units!", #units, size0)) + -- Init done. + self.groupinitialized=true end - -- Add elemets. - for _,unit in pairs(units) do - local unitname=unit:GetName() - self:_AddElementByName(unitname) - end - - - -- Init done. - self.groupinitialized=true - return self end diff --git a/Moose Development/Moose/Ops/OpsGroup.lua b/Moose Development/Moose/Ops/OpsGroup.lua index ba562e542..2f423a8fa 100644 --- a/Moose Development/Moose/Ops/OpsGroup.lua +++ b/Moose Development/Moose/Ops/OpsGroup.lua @@ -1768,6 +1768,8 @@ function OPSGROUP:GetDCSUnit(UnitNumber) if DCSGroup then local unit=DCSGroup:getUnit(UnitNumber or 1) return unit + else + self:E(self.lid..string.format("ERROR: DCS group does not exist! Cannot get unit")) end return nil @@ -3517,9 +3519,11 @@ function OPSGROUP:OnEventBirth(EventData) local element=self:GetElementByName(unitname) if element and element.status~=OPSGROUP.ElementStatus.SPAWNED then - + -- Debug info. self:T(self.lid..string.format("EVENT: Element %s born ==> spawned", unitname)) + + self:T2(self.lid..string.format("DCS unit=%s isExist=%s", tostring(EventData.IniDCSUnit:getName()), tostring(EventData.IniDCSUnit:isExist()) )) -- Set element to spawned state. self:ElementSpawned(element) diff --git a/Moose Development/Moose/Wrapper/Group.lua b/Moose Development/Moose/Wrapper/Group.lua index faeb810a5..6d67c46dd 100644 --- a/Moose Development/Moose/Wrapper/Group.lua +++ b/Moose Development/Moose/Wrapper/Group.lua @@ -305,6 +305,8 @@ function GROUP:GetDCSObject() if DCSGroup then return DCSGroup + else + env.error("ERROR: Could not get DCS group object!") end return nil From a4704d0e2fa2726ec6dca11a807480db21fd6993 Mon Sep 17 00:00:00 2001 From: Frank Date: Sun, 26 Nov 2023 23:57:21 +0100 Subject: [PATCH 02/26] Update ArmyGroup.lua --- Moose Development/Moose/Ops/ArmyGroup.lua | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Moose Development/Moose/Ops/ArmyGroup.lua b/Moose Development/Moose/Ops/ArmyGroup.lua index ac893ae85..3d105fa34 100644 --- a/Moose Development/Moose/Ops/ArmyGroup.lua +++ b/Moose Development/Moose/Ops/ArmyGroup.lua @@ -976,7 +976,8 @@ function ARMYGROUP:onafterSpawned(From, Event, To) -- Update route. if Nwp>1 and self.isMobile then self:T(self.lid..string.format("Got %d waypoints on spawn ==> Cruise in -1.0 sec!", Nwp)) - self:__Cruise(-1, nil, self.option.Formation) + --self:__Cruise(-1, nil, self.option.Formation) + self:__Cruise(-1) else self:T(self.lid.."No waypoints on spawn ==> Full Stop!") self:FullStop() @@ -1948,7 +1949,7 @@ function ARMYGROUP:onafterCruise(From, Event, To, Speed, Formation) self.dTwait=nil -- Debug info. - self:T(self.lid.."Cruise ==> Update route in 0.01 sec") + self:T(self.lid..string.format("Cruise ==> Update route in 0.01 sec (speed=%s, formation=%s)", tostring(Speed), tostring(Formation))) -- Update route. self:__UpdateRoute(-0.01, nil, nil, Speed, Formation) @@ -2003,7 +2004,7 @@ function ARMYGROUP:AddWaypoint(Coordinate, Speed, AfterWaypointWithID, Formation elseif self.optionDefault.Formation then Formation = self.optionDefault.Formation elseif self.option.Formation then - Formation = self.option.Formation + Formation = self.option.Formation else -- Default formation is on road. Formation = ENUMS.Formation.Vehicle.OnRoad From 2fce93d9252b7cbbbef4e4e6f76de4b567481af5 Mon Sep 17 00:00:00 2001 From: Frank Date: Mon, 12 Feb 2024 00:10:36 +0100 Subject: [PATCH 03/26] RAT --- Moose Development/Moose/Functional/RAT.lua | 64 +++++++++++++++++++++- Moose Development/Moose/Ops/OpsGroup.lua | 2 + 2 files changed, 64 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/Functional/RAT.lua b/Moose Development/Moose/Functional/RAT.lua index 2bd592767..bc4df08fd 100644 --- a/Moose Development/Moose/Functional/RAT.lua +++ b/Moose Development/Moose/Functional/RAT.lua @@ -483,6 +483,32 @@ RAT.status={ EventCrash="Crashed", } +--- Categories of the RAT class. +-- @type RAT.RatCraft +-- @field Wrapper.Group#Group group The aircraft group. +-- @field Ops.FlightGroup#FLIGHTGROUP flightgroup The flight group. +-- @field #table destination Destination of this group. +-- @field #table departure Departure place of this group. +-- @field #table waypoints Waypoints. +-- @field #boolean airborne Whether this group is airborne. +-- @field #number nunits Number of units. +-- @field #number Tground Time stamp on ground. +-- @field #number Pground ? +-- @field #number Uground ? +-- @field #number Tlastcheck Time stamp of last check. +-- @field #table P0 ? +-- @field #table Pnow ? +-- @field #number Distance Distance travelled in meters. +-- @field #number takeoff Takeoff type. +-- @field #number landing Laning type. +-- @field #table wpholding Holding waypoint. +-- @field #table wpfinal Final waypoint. +-- @field #boolean active Whether the group is actie or uncontrolled. +-- @field #string status Status of the group. +-- @field #string livery Livery of the group. +-- @field #boolean despawnme Despawn group if `true`. +-- @field #number nrespawn Number of respawns. + --- RAT friendly coalitions. -- @list coal RAT.coal={ @@ -545,13 +571,14 @@ RAT.id="RAT | " --- RAT version. -- @list version RAT.version={ - version = "2.3.9", + version = "3.0.0", print = true, } ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --TODO list: +--TODO: Integrate FLIGHTGROUP --DONE: Add scheduled spawn. --DONE: Add possibility to spawn in air. --DONE: Add departure zones for air start. @@ -2151,6 +2178,9 @@ function RAT:_SpawnWithRoute(_departure, _destination, _takeoff, _landing, _live -- Actually spawn the group. local group=self:SpawnWithIndex(self.SpawnIndex) -- Wrapper.Group#GROUP + + -- Create a flightgroup object. + local flightgroup=FLIGHTGROUP:New(group) -- Increase counter of alive groups (also uncontrolled ones). self.alive=self.alive+1 @@ -2194,6 +2224,7 @@ function RAT:_SpawnWithRoute(_departure, _destination, _takeoff, _landing, _live -- Init ratcraft array. self.ratcraft[self.SpawnIndex]={} self.ratcraft[self.SpawnIndex]["group"]=group + self.ratcraft[self.SpawnIndex]["flightgroup"]=flightgroup self.ratcraft[self.SpawnIndex]["destination"]=destination self.ratcraft[self.SpawnIndex]["departure"]=departure self.ratcraft[self.SpawnIndex]["waypoints"]=waypoints @@ -3728,6 +3759,26 @@ function RAT:_SetStatus(group, status) end end +--- Get RatCraft from a given group. +-- @param #RAT self +-- @param Wrapper.Group#GROUP group Group. +-- @return #RAT.RatCraft Rat craft object. +function RAT:_GetRatcraftFromGroup(group) + + if group then + + -- Get index from groupname. + local index=self:GetSpawnIndexFromGroup(group) + + if self.ratcraft[index] then + return self.ratcraft[index] + end + + end + + return nil +end + --- Get status of group. -- @param #RAT self -- @param Wrapper.Group#GROUP group Group. @@ -3786,6 +3837,9 @@ function RAT:_OnBirth(EventData) status=RAT.status.EventBirth end self:_SetStatus(SpawnGroup, status) + + local ratcraft=self.ratcraft[i] --#RAT.RatCraft + -- Get some info ablout this flight. local i=self:GetSpawnIndexFromGroup(SpawnGroup) @@ -4222,6 +4276,8 @@ function RAT:_Despawn(group, delay) local index=self:GetSpawnIndexFromGroup(group) if index ~= nil then + + local ratcraft=self.ratcraft[index] self.ratcraft[index].group=nil self.ratcraft[index]["status"]="Dead" @@ -4308,9 +4364,13 @@ function RAT:_Destroy(group) _DATABASE:DeleteUnit(DCSUnit:getName()) end end + + local ratcraft=self:_GetRatcraftFromGroup(group) + + ratcraft.flightgroup:Destroy(0) -- Destroy DCS group. - DCSGroup:destroy() + --DCSGroup:destroy() DCSGroup = nil end diff --git a/Moose Development/Moose/Ops/OpsGroup.lua b/Moose Development/Moose/Ops/OpsGroup.lua index d6d4ce6d5..85b545207 100644 --- a/Moose Development/Moose/Ops/OpsGroup.lua +++ b/Moose Development/Moose/Ops/OpsGroup.lua @@ -2199,6 +2199,8 @@ function OPSGROUP:Destroy(Delay) if Delay and Delay>0 then self:ScheduleOnce(Delay, OPSGROUP.Destroy, self, 0) else + + self:T(self.lid.."Destroying group!") -- Get all units. local units=self:GetDCSUnits() From 2fcb31f3ac8087050c6830f31c75566bffba064a Mon Sep 17 00:00:00 2001 From: Frank Date: Fri, 16 Feb 2024 00:11:43 +0100 Subject: [PATCH 04/26] Update RAT.lua --- Moose Development/Moose/Functional/RAT.lua | 31 +++++++++++----------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/Moose Development/Moose/Functional/RAT.lua b/Moose Development/Moose/Functional/RAT.lua index bc4df08fd..399d8a505 100644 --- a/Moose Development/Moose/Functional/RAT.lua +++ b/Moose Development/Moose/Functional/RAT.lua @@ -4349,25 +4349,26 @@ function RAT:_Destroy(group) if DCSGroup and DCSGroup:isExist() then - -- Cread one single Dead event and delete units from database. - local triggerdead=true - for _,DCSUnit in pairs(DCSGroup:getUnits()) do - - -- Dead event. - if DCSUnit then - if triggerdead then - self:_CreateEventDead(timer.getTime(), DCSUnit) - triggerdead=false - end - - -- Delete from data base. - _DATABASE:DeleteUnit(DCSUnit:getName()) - end - end +-- -- Cread one single Dead event and delete units from database. +-- local triggerdead=true +-- for _,DCSUnit in pairs(DCSGroup:getUnits()) do +-- +-- -- Dead event. +-- if DCSUnit then +-- if triggerdead then +-- self:_CreateEventDead(timer.getTime(), DCSUnit) +-- triggerdead=false +-- end +-- +-- -- Delete from data base. +-- _DATABASE:DeleteUnit(DCSUnit:getName()) +-- end +-- end local ratcraft=self:_GetRatcraftFromGroup(group) ratcraft.flightgroup:Destroy(0) + ratcraft.flightgroup:__Stop(0.1) -- Destroy DCS group. --DCSGroup:destroy() From e3f523d6482be04730aa7fcc6be483cc5626c28a Mon Sep 17 00:00:00 2001 From: Frank Date: Fri, 23 Feb 2024 00:31:15 +0100 Subject: [PATCH 05/26] Update RAT.lua --- Moose Development/Moose/Functional/RAT.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/Functional/RAT.lua b/Moose Development/Moose/Functional/RAT.lua index bc4df08fd..2158cb03f 100644 --- a/Moose Development/Moose/Functional/RAT.lua +++ b/Moose Development/Moose/Functional/RAT.lua @@ -483,7 +483,7 @@ RAT.status={ EventCrash="Crashed", } ---- Categories of the RAT class. +--- Datastructure of a spawned RAT group. -- @type RAT.RatCraft -- @field Wrapper.Group#Group group The aircraft group. -- @field Ops.FlightGroup#FLIGHTGROUP flightgroup The flight group. From 22051009425b0457ce83506856cf9ca5190c5045 Mon Sep 17 00:00:00 2001 From: Frank Date: Sun, 24 Mar 2024 22:51:11 +0100 Subject: [PATCH 06/26] RAT --- Moose Development/Moose/Core/Base.lua | 14 + Moose Development/Moose/Functional/RAT.lua | 357 +++++++++++---------- 2 files changed, 206 insertions(+), 165 deletions(-) diff --git a/Moose Development/Moose/Core/Base.lua b/Moose Development/Moose/Core/Base.lua index 6ab927288..52ebbd99f 100644 --- a/Moose Development/Moose/Core/Base.lua +++ b/Moose Development/Moose/Core/Base.lua @@ -1157,6 +1157,20 @@ function BASE:_Serialize(Arguments) return text end +--- (Internal) Serialize arguments +-- @param #BASE self +-- @param #table Arguments +-- @return #string Text +function BASE:_Serialize(Arguments) + local text=UTILS.BasicSerialize(Arguments) +-- local text = UTILS.PrintTableToLog({Arguments}, 0, true) +-- text = string.gsub(text,"\n","") +-- text = string.gsub(text,"%(%(","%(") +-- text = string.gsub(text,"%)%)","%)") +-- text = string.gsub(text,"(%s+)","") + return text +end + --- Trace a function call. This function is private. -- @param #BASE self -- @param Arguments A #table or any field. diff --git a/Moose Development/Moose/Functional/RAT.lua b/Moose Development/Moose/Functional/RAT.lua index 7951bd07a..64cc7947f 100644 --- a/Moose Development/Moose/Functional/RAT.lua +++ b/Moose Development/Moose/Functional/RAT.lua @@ -2121,7 +2121,7 @@ end -- @param #number _nrespawn Number of already performed respawn attempts (e.g. spawning on runway bug). -- @param #table parkingdata Explicitly specify the parking spots when spawning at an airport. -- @return #number Spawn index. -function RAT:_SpawnWithRoute(_departure, _destination, _takeoff, _landing, _livery, _waypoint, _lastpos, _nrespawn, parkingdata) +function RAT:_SpawnWithRoute(_departure, _destination, _takeoff, _landing, _livery, _waypoint, _lastpos, _nrespawn, parkingdata, index) self:F({rat=RAT.id, departure=_departure, destination=_destination, takeoff=_takeoff, landing=_landing, livery=_livery, waypoint=_waypoint, lastpos=_lastpos, nrespawn=_nrespawn}) -- Set takeoff type. @@ -2313,185 +2313,212 @@ end -- @param #number delay Delay before respawn function RAT:_Respawn(index, lastpos, delay) - -- Get the spawn index from group - --local index=self:GetSpawnIndexFromGroup(group) - - -- Get departure and destination from previous journey. - local departure=self.ratcraft[index].departure - local destination=self.ratcraft[index].destination - local takeoff=self.ratcraft[index].takeoff - local landing=self.ratcraft[index].landing - local livery=self.ratcraft[index].livery - local lastwp=self.ratcraft[index].waypoints[#self.ratcraft[index].waypoints] - --local lastpos=group:GetCoordinate() - - local _departure=nil - local _destination=nil - local _takeoff=nil - local _landing=nil - local _livery=nil - local _lastwp=nil - local _lastpos=nil - - if self.continuejourney then - - -- We continue our journey from the old departure airport. - _departure=destination:GetName() - - -- Use the same livery for next aircraft. - _livery=livery - - -- Last known position of the aircraft, which should be the sparking spot location. - -- Note: we have to check that it was supposed to land and not respawned directly after landing or after takeoff. - -- TODO: Need to think if continuejourney with respawn_after_takeoff actually makes sense. - if landing==RAT.wp.landing and lastpos and not (self.respawn_at_landing or self.respawn_after_takeoff) then - -- Check that we have an airport or FARP but not a ship (which would be categroy 1). - if destination:GetCategory()==4 then - _lastpos=lastpos + if delay and delay>0 then + + self:ScheduleOnce(delay, RAT._Respawn, self, index, lastpos, 0) + + else + + local ratcraft=self.ratcraft[index] --#RAT.RatCraft + + -- Get departure and destination from previous journey. + local departure=self.ratcraft[index].departure + local destination=self.ratcraft[index].destination + local takeoff=self.ratcraft[index].takeoff + local landing=self.ratcraft[index].landing + local livery=self.ratcraft[index].livery + local lastwp=self.ratcraft[index].waypoints[#self.ratcraft[index].waypoints] + + local flightgroup=ratcraft.flightgroup + local parkingdata=nil + env.info("Respawning ratcraft") + for _,_element in pairs(flightgroup.elements) do + local element=_element --Ops.OpsGroup#OPSGROUP.Element + + env.info(string.format("element %s", element.name)) + + + if element.parking then + if parkingdata==nil then + parkingdata={} + end + env.info(string.format("element %s at spot id=%d", element.name, element.parking.TerminalID)) + table.insert(parkingdata, element.parking) + else + env.info(string.format("element %s not SPOT", element.name)) end end - - if self.destinationzone then - - -- Case: X --> Zone --> Zone --> Zone - _takeoff=RAT.wp.air - _landing=RAT.wp.air - - elseif self.returnzone then - - -- Case: X --> Zone --> X, X --> Zone --> X - -- We flew to a zone and back. Takeoff type does not change. - _takeoff=self.takeoff - - -- If we took of in air we also want to land "in air". - if self.takeoff==RAT.wp.air then - _landing=RAT.wp.air - else - _landing=RAT.wp.landing - end - - -- Departure stays the same. (The destination is the zone here.) - _departure=departure:GetName() - - else - - -- Default case. Takeoff and landing type does not change. - _takeoff=self.takeoff - _landing=self.landing - - end - - elseif self.commute then - - -- We commute between departure and destination. - - if self.starshape==true then - if destination:GetName()==self.homebase then - -- We are at our home base ==> destination is again randomly selected. - _departure=self.homebase - _destination=nil -- destination will be set anew - else - -- We are not a our home base ==> we fly back to our home base. - _departure=destination:GetName() - _destination=self.homebase - end - else - -- Simply switch departure and destination. + + local _departure=nil + local _destination=nil + local _takeoff=nil + local _landing=nil + local _livery=nil + local _lastwp=nil + local _lastpos=nil + + if self.continuejourney then + + -- We continue our journey from the old departure airport. _departure=destination:GetName() - _destination=departure:GetName() - end - - -- Use the same livery for next aircraft. - _livery=livery - - -- Last known position of the aircraft, which should be the sparking spot location. - -- Note: we have to check that it was supposed to land and not respawned directly after landing or after takeoff. - -- TODO: Need to think if commute with respawn_after_takeoff actually makes sense. - if landing==RAT.wp.landing and lastpos and not (self.respawn_at_landing or self.respawn_after_takeoff) then - -- Check that we have landed on an airport or FARP but not a ship (which would be categroy 1). - if destination:GetCategory()==4 then - _lastpos=lastpos + + -- Use the same livery for next aircraft. + _livery=livery + + -- Last known position of the aircraft, which should be the sparking spot location. + -- Note: we have to check that it was supposed to land and not respawned directly after landing or after takeoff. + -- TODO: Need to think if continuejourney with respawn_after_takeoff actually makes sense. + if landing==RAT.wp.landing and lastpos and not (self.respawn_at_landing or self.respawn_after_takeoff) then + -- Check that we have an airport or FARP but not a ship (which would be categroy 1). + if destination:GetCategory()==4 then + _lastpos=lastpos + end end - end - - -- Handle takeoff type. - if self.destinationzone then - -- self.takeoff is either RAT.wp.air or RAT.wp.cold - -- self.landing is RAT.wp.Air - - if self.takeoff==RAT.wp.air then - - -- Case: Zone <--> Zone (both have takeoff air) - _takeoff=RAT.wp.air -- = self.takeoff (because we just checked) - _landing=RAT.wp.air -- = self.landing (because destinationzone) - - else - - -- Case: Airport <--> Zone - if takeoff==RAT.wp.air then - -- Last takeoff was air so we are at the airport now, takeoff is from ground. - _takeoff=self.takeoff -- must be either hot/cold/runway/hotcold - _landing=RAT.wp.air -- must be air = self.landing (because destinationzone) + + if self.destinationzone then + + -- Case: X --> Zone --> Zone --> Zone + _takeoff=RAT.wp.air + _landing=RAT.wp.air + + elseif self.returnzone then + + -- Case: X --> Zone --> X, X --> Zone --> X + -- We flew to a zone and back. Takeoff type does not change. + _takeoff=self.takeoff + + -- If we took of in air we also want to land "in air". + if self.takeoff==RAT.wp.air then + _landing=RAT.wp.air else - -- Last takeoff was on ground so we are at a zone now ==> takeoff in air, landing at airport. - _takeoff=RAT.wp.air _landing=RAT.wp.landing end - + + -- Departure stays the same. (The destination is the zone here.) + _departure=departure:GetName() + + else + + -- Default case. Takeoff and landing type does not change. + _takeoff=self.takeoff + _landing=self.landing + end - - elseif self.returnzone then - - -- We flew to a zone and back. No need to swap departure and destination. - _departure=departure:GetName() - _destination=destination:GetName() - - -- Takeoff and landing should also not change. - _takeoff=self.takeoff - _landing=self.landing - + + elseif self.commute then + + -- We commute between departure and destination. + + if self.starshape==true then + if destination:GetName()==self.homebase then + -- We are at our home base ==> destination is again randomly selected. + _departure=self.homebase + _destination=nil -- destination will be set anew + else + -- We are not a our home base ==> we fly back to our home base. + _departure=destination:GetName() + _destination=self.homebase + end + else + -- Simply switch departure and destination. + _departure=destination:GetName() + _destination=departure:GetName() + end + + -- Use the same livery for next aircraft. + _livery=livery + + -- Last known position of the aircraft, which should be the sparking spot location. + -- Note: we have to check that it was supposed to land and not respawned directly after landing or after takeoff. + -- TODO: Need to think if commute with respawn_after_takeoff actually makes sense. + if landing==RAT.wp.landing and lastpos and not (self.respawn_at_landing or self.respawn_after_takeoff) then + -- Check that we have landed on an airport or FARP but not a ship (which would be categroy 1). + if destination:GetCategory()==4 then + _lastpos=lastpos + end + end + + -- Handle takeoff type. + if self.destinationzone then + -- self.takeoff is either RAT.wp.air or RAT.wp.cold + -- self.landing is RAT.wp.Air + + if self.takeoff==RAT.wp.air then + + -- Case: Zone <--> Zone (both have takeoff air) + _takeoff=RAT.wp.air -- = self.takeoff (because we just checked) + _landing=RAT.wp.air -- = self.landing (because destinationzone) + + else + + -- Case: Airport <--> Zone + if takeoff==RAT.wp.air then + -- Last takeoff was air so we are at the airport now, takeoff is from ground. + _takeoff=self.takeoff -- must be either hot/cold/runway/hotcold + _landing=RAT.wp.air -- must be air = self.landing (because destinationzone) + else + -- Last takeoff was on ground so we are at a zone now ==> takeoff in air, landing at airport. + _takeoff=RAT.wp.air + _landing=RAT.wp.landing + end + + end + + elseif self.returnzone then + + -- We flew to a zone and back. No need to swap departure and destination. + _departure=departure:GetName() + _destination=destination:GetName() + + -- Takeoff and landing should also not change. + _takeoff=self.takeoff + _landing=self.landing + + end + end - + + -- Take the last waypoint as initial waypoint for next plane. + if _takeoff==RAT.wp.air and (self.continuejourney or self.commute) then + _lastwp=lastwp + end + + -- Debug + self:T2({departure=_departure, destination=_destination, takeoff=_takeoff, landing=_landing, livery=_livery, lastwp=_lastwp}) + + -- We should give it at least 3 sec since this seems to be the time until free parking spots after despawn are available again (Sirri Island test). + local respawndelay + if delay then + respawndelay=delay + elseif self.respawn_delay then + respawndelay=self.respawn_delay+3 -- despawn happens after self.respawndelay. We add another 3 sec for free parking. + else + respawndelay=3 + end + + -- Spawn new group. + local arg={} + arg.self=self + arg.departure=_departure + arg.destination=_destination + arg.takeoff=_takeoff + arg.landing=_landing + arg.livery=_livery + arg.lastwp=_lastwp + arg.lastpos=_lastpos + arg.parkingdata=parkingdata + self:T(RAT.id..string.format("%s delayed respawn in %.1f seconds.", self.alias, respawndelay)) + SCHEDULER:New(nil, self._SpawnWithRouteTimer, {arg}, respawndelay) + end - -- Take the last waypoint as initial waypoint for next plane. - if _takeoff==RAT.wp.air and (self.continuejourney or self.commute) then - _lastwp=lastwp - end - - -- Debug - self:T2({departure=_departure, destination=_destination, takeoff=_takeoff, landing=_landing, livery=_livery, lastwp=_lastwp}) - - -- We should give it at least 3 sec since this seems to be the time until free parking spots after despawn are available again (Sirri Island test). - local respawndelay - if delay then - respawndelay=delay - elseif self.respawn_delay then - respawndelay=self.respawn_delay+3 -- despawn happens after self.respawndelay. We add another 3 sec for free parking. - else - respawndelay=3 - end - - -- Spawn new group. - local arg={} - arg.self=self - arg.departure=_departure - arg.destination=_destination - arg.takeoff=_takeoff - arg.landing=_landing - arg.livery=_livery - arg.lastwp=_lastwp - arg.lastpos=_lastpos - self:T(RAT.id..string.format("%s delayed respawn in %.1f seconds.", self.alias, respawndelay)) - SCHEDULER:New(nil, self._SpawnWithRouteTimer, {arg}, respawndelay) - end --- Delayed spawn function called by scheduler. -- @param #RAT self --- @param #table arg Parameters: arg.self, arg.departure, arg.destination, arg.takeoff, arg.landing, arg.livery, arg.lastwp, arg.lastpos +-- @param #table arg Parameters: arg.self, arg.departure, arg.destination, arg.takeoff, arg.landing, arg.livery, arg.lastwp, arg.lastpos, arg.parkingdata function RAT._SpawnWithRouteTimer(arg) - RAT._SpawnWithRoute(arg.self, arg.departure, arg.destination, arg.takeoff, arg.landing, arg.livery, arg.lastwp, arg.lastpos) + RAT._SpawnWithRoute(arg.self, arg.departure, arg.destination, arg.takeoff, arg.landing, arg.livery, arg.lastwp, arg.lastpos, nil, arg.parkingdata) end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -4093,7 +4120,7 @@ function RAT:_OnEngineShutdown(EventData) -- Respawn group. local idx=self:GetSpawnIndexFromGroup(SpawnGroup) local coord=SpawnGroup:GetCoordinate() - self:_Respawn(idx, coord) + self:_Respawn(idx, coord, 3) end -- Despawn group. From 82f4c9d52648be4f87be668c17c8a525edae6961 Mon Sep 17 00:00:00 2001 From: Frank Date: Tue, 26 Mar 2024 22:26:58 +0100 Subject: [PATCH 07/26] Update RAT.lua --- Moose Development/Moose/Functional/RAT.lua | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/Moose Development/Moose/Functional/RAT.lua b/Moose Development/Moose/Functional/RAT.lua index 64cc7947f..4bd1e7bac 100644 --- a/Moose Development/Moose/Functional/RAT.lua +++ b/Moose Development/Moose/Functional/RAT.lua @@ -2348,6 +2348,9 @@ function RAT:_Respawn(index, lastpos, delay) env.info(string.format("element %s not SPOT", element.name)) end end + + flightgroup:Despawn(0, true) + flightgroup:Stop() local _departure=nil local _destination=nil @@ -4121,12 +4124,16 @@ function RAT:_OnEngineShutdown(EventData) local idx=self:GetSpawnIndexFromGroup(SpawnGroup) local coord=SpawnGroup:GetCoordinate() self:_Respawn(idx, coord, 3) + + else + + -- Despawn group. + text="Event: Group "..SpawnGroup:GetName().." will be destroyed now." + self:T(RAT.id..text) + self:_Despawn(SpawnGroup) + end - -- Despawn group. - text="Event: Group "..SpawnGroup:GetName().." will be destroyed now." - self:T(RAT.id..text) - self:_Despawn(SpawnGroup) end From 24eaa7441cdaeac616e66580f10d3ef86bc9ede6 Mon Sep 17 00:00:00 2001 From: Frank Date: Wed, 27 Mar 2024 22:15:17 +0100 Subject: [PATCH 08/26] Update RAT.lua --- Moose Development/Moose/Functional/RAT.lua | 27 ++++++---------------- 1 file changed, 7 insertions(+), 20 deletions(-) diff --git a/Moose Development/Moose/Functional/RAT.lua b/Moose Development/Moose/Functional/RAT.lua index 4bd1e7bac..aad17571f 100644 --- a/Moose Development/Moose/Functional/RAT.lua +++ b/Moose Development/Moose/Functional/RAT.lua @@ -2350,7 +2350,11 @@ function RAT:_Respawn(index, lastpos, delay) end flightgroup:Despawn(0, true) - flightgroup:Stop() + flightgroup:__Stop(0.1) + -- This is usually done in _Despawn + ratcraft.group=nil + ratcraft.status="Dead" + -- TODO: remove ratcraft from self.ratcraft table local _departure=nil local _destination=nil @@ -2500,30 +2504,13 @@ function RAT:_Respawn(index, lastpos, delay) end -- Spawn new group. - local arg={} - arg.self=self - arg.departure=_departure - arg.destination=_destination - arg.takeoff=_takeoff - arg.landing=_landing - arg.livery=_livery - arg.lastwp=_lastwp - arg.lastpos=_lastpos - arg.parkingdata=parkingdata - self:T(RAT.id..string.format("%s delayed respawn in %.1f seconds.", self.alias, respawndelay)) - SCHEDULER:New(nil, self._SpawnWithRouteTimer, {arg}, respawndelay) + self:T(RAT.id..string.format("%s delayed respawn in %.1f seconds.", self.alias, respawndelay)) + self:ScheduleOnce(respawndelay, RAT._SpawnWithRoute, self,_departure,_destination,_takeoff,_landing,_livery, nil,_lastpos, nil, nil) end end ---- Delayed spawn function called by scheduler. --- @param #RAT self --- @param #table arg Parameters: arg.self, arg.departure, arg.destination, arg.takeoff, arg.landing, arg.livery, arg.lastwp, arg.lastpos, arg.parkingdata -function RAT._SpawnWithRouteTimer(arg) - RAT._SpawnWithRoute(arg.self, arg.departure, arg.destination, arg.takeoff, arg.landing, arg.livery, arg.lastwp, arg.lastpos, nil, arg.parkingdata) -end - ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- Set the route of the AI plane. Due to DCS landing bug, this has to be done before the unit is spawned. From be3c41891925260667317a947b75ded240f47fb0 Mon Sep 17 00:00:00 2001 From: Frank Date: Thu, 28 Mar 2024 23:15:50 +0100 Subject: [PATCH 09/26] Update RAT.lua - Improved keeping parking spot after respawn - Improved ratcraft object --- Moose Development/Moose/Functional/RAT.lua | 206 ++++++++++++--------- 1 file changed, 122 insertions(+), 84 deletions(-) diff --git a/Moose Development/Moose/Functional/RAT.lua b/Moose Development/Moose/Functional/RAT.lua index aad17571f..7a7c2da6d 100644 --- a/Moose Development/Moose/Functional/RAT.lua +++ b/Moose Development/Moose/Functional/RAT.lua @@ -485,6 +485,7 @@ RAT.status={ --- Datastructure of a spawned RAT group. -- @type RAT.RatCraft +-- @field #number index Spawn index. -- @field Wrapper.Group#Group group The aircraft group. -- @field Ops.FlightGroup#FLIGHTGROUP flightgroup The flight group. -- @field #table destination Destination of this group. @@ -2121,7 +2122,7 @@ end -- @param #number _nrespawn Number of already performed respawn attempts (e.g. spawning on runway bug). -- @param #table parkingdata Explicitly specify the parking spots when spawning at an airport. -- @return #number Spawn index. -function RAT:_SpawnWithRoute(_departure, _destination, _takeoff, _landing, _livery, _waypoint, _lastpos, _nrespawn, parkingdata, index) +function RAT:_SpawnWithRoute(_departure, _destination, _takeoff, _landing, _livery, _waypoint, _lastpos, _nrespawn, parkingdata) self:F({rat=RAT.id, departure=_departure, destination=_destination, takeoff=_takeoff, landing=_landing, livery=_livery, waypoint=_waypoint, lastpos=_lastpos, nrespawn=_nrespawn}) -- Set takeoff type. @@ -2220,57 +2221,61 @@ function RAT:_SpawnWithRoute(_departure, _destination, _takeoff, _landing, _live -- Set ROT, default is "no reaction". self:_SetROT(group, self.rot) - + -- Init ratcraft array. - self.ratcraft[self.SpawnIndex]={} - self.ratcraft[self.SpawnIndex]["group"]=group - self.ratcraft[self.SpawnIndex]["flightgroup"]=flightgroup - self.ratcraft[self.SpawnIndex]["destination"]=destination - self.ratcraft[self.SpawnIndex]["departure"]=departure - self.ratcraft[self.SpawnIndex]["waypoints"]=waypoints - self.ratcraft[self.SpawnIndex]["airborne"]=group:InAir() - self.ratcraft[self.SpawnIndex]["nunits"]=group:GetInitialSize() + local ratcraft={} --#RAT.RatCraft + ratcraft.index=self.SpawnIndex + ratcraft.group=group + ratcraft.flightgroup=flightgroup + ratcraft.destination=destination + ratcraft.departure=departure + ratcraft.waypoints=waypoints + ratcraft.airborne=group:InAir() + ratcraft.nunits=group:GetInitialSize() -- Time and position on ground. For check if aircraft is stuck somewhere. if group:InAir() then - self.ratcraft[self.SpawnIndex]["Tground"]=nil - self.ratcraft[self.SpawnIndex]["Pground"]=nil - self.ratcraft[self.SpawnIndex]["Uground"]=nil - self.ratcraft[self.SpawnIndex]["Tlastcheck"]=nil + ratcraft.Tground=nil + ratcraft.Pground=nil + ratcraft.Uground=nil + ratcraft.Tlastcheck=nil else - self.ratcraft[self.SpawnIndex]["Tground"]=timer.getTime() - self.ratcraft[self.SpawnIndex]["Pground"]=group:GetCoordinate() - self.ratcraft[self.SpawnIndex]["Uground"]={} + ratcraft.Tground=timer.getTime() + ratcraft.Pground=group:GetCoordinate() + ratcraft.Uground={} for _,_unit in pairs(group:GetUnits()) do local _unitname=_unit:GetName() - self.ratcraft[self.SpawnIndex]["Uground"][_unitname]=_unit:GetCoordinate() + ratcraft.Uground[_unitname]=_unit:GetCoordinate() end - self.ratcraft[self.SpawnIndex]["Tlastcheck"]=timer.getTime() + ratcraft.Tlastcheck=timer.getTime() end -- Initial and current position. For calculating the travelled distance. - self.ratcraft[self.SpawnIndex]["P0"]=group:GetCoordinate() - self.ratcraft[self.SpawnIndex]["Pnow"]=group:GetCoordinate() - self.ratcraft[self.SpawnIndex]["Distance"]=0 + ratcraft.P0=group:GetCoordinate() + ratcraft.Pnow=group:GetCoordinate() + ratcraft.Distance=0 -- Each aircraft gets its own takeoff type. - self.ratcraft[self.SpawnIndex].takeoff=takeoff - self.ratcraft[self.SpawnIndex].landing=landing - self.ratcraft[self.SpawnIndex].wpholding=WPholding - self.ratcraft[self.SpawnIndex].wpfinal=WPfinal + ratcraft.takeoff=takeoff + ratcraft.landing=landing + ratcraft.wpholding=WPholding + ratcraft.wpfinal=WPfinal -- Aircraft is active or spawned in uncontrolled state. - self.ratcraft[self.SpawnIndex].active=not self.uncontrolled + ratcraft.active=not self.uncontrolled -- Set status to spawned. This will be overwritten in birth event. - self.ratcraft[self.SpawnIndex]["status"]=RAT.status.Spawned + ratcraft.status=RAT.status.Spawned -- Livery - self.ratcraft[self.SpawnIndex].livery=livery + ratcraft.livery=livery -- If this switch is set to true, the aircraft will be despawned the next time the status function is called. - self.ratcraft[self.SpawnIndex].despawnme=false + ratcraft.despawnme=false -- Number of preformed spawn attempts for this group. - self.ratcraft[self.SpawnIndex].nrespawn=nrespawn + ratcraft.nrespawn=nrespawn + + -- Add ratcaft to table. + self.ratcraft[self.SpawnIndex]=ratcraft -- Create submenu for this group. if self.f10menu then @@ -2318,43 +2323,47 @@ function RAT:_Respawn(index, lastpos, delay) self:ScheduleOnce(delay, RAT._Respawn, self, index, lastpos, 0) else + self:T(RAT.id..string.format("Respawning ratcraft with index=%d", index)) + -- Ratcraft object local ratcraft=self.ratcraft[index] --#RAT.RatCraft -- Get departure and destination from previous journey. - local departure=self.ratcraft[index].departure - local destination=self.ratcraft[index].destination - local takeoff=self.ratcraft[index].takeoff - local landing=self.ratcraft[index].landing - local livery=self.ratcraft[index].livery - local lastwp=self.ratcraft[index].waypoints[#self.ratcraft[index].waypoints] - + local departure=ratcraft.departure + local destination=ratcraft.destination + local takeoff=ratcraft.takeoff + local landing=ratcraft.landing + local livery=ratcraft.livery + local lastwp=ratcraft.waypoints[#ratcraft.waypoints] local flightgroup=ratcraft.flightgroup + + local parkingdata=nil - env.info("Respawning ratcraft") for _,_element in pairs(flightgroup.elements) do local element=_element --Ops.OpsGroup#OPSGROUP.Element - - env.info(string.format("element %s", element.name)) - - + if element.parking then + -- Init table. if parkingdata==nil then parkingdata={} end - env.info(string.format("element %s at spot id=%d", element.name, element.parking.TerminalID)) - table.insert(parkingdata, element.parking) + + self:T(RAT.id..string.format("Element %s was parking at spot id=%d", element.name, element.parking.TerminalID)) + table.insert(parkingdata, UTILS.DeepCopy(element.parking)) else - env.info(string.format("element %s not SPOT", element.name)) + self:E(RAT.id..string.format("WARNING: Element %s did NOT have a not parking spot!", tostring(element.name))) end end - + + -- Despawn flight group flightgroup:Despawn(0, true) flightgroup:__Stop(0.1) + + -- This is usually done in _Despawn ratcraft.group=nil ratcraft.status="Dead" - -- TODO: remove ratcraft from self.ratcraft table + self.ratcraft[index]=nil local _departure=nil local _destination=nil @@ -2505,7 +2514,7 @@ function RAT:_Respawn(index, lastpos, delay) -- Spawn new group. self:T(RAT.id..string.format("%s delayed respawn in %.1f seconds.", self.alias, respawndelay)) - self:ScheduleOnce(respawndelay, RAT._SpawnWithRoute, self,_departure,_destination,_takeoff,_landing,_livery, nil,_lastpos, nil, nil) + self:ScheduleOnce(respawndelay, RAT._SpawnWithRoute, self,_departure,_destination,_takeoff,_landing,_livery, nil,_lastpos, nil, parkingdata) end @@ -3711,8 +3720,9 @@ function RAT:Status(message, forID) else -- Group does not exist. - local text=string.format("Group does not exist in loop ratcraft status.") + local text=string.format("Group does not exist in loop ratcraft status for spawn index=%d", spawnindex) self:T2(RAT.id..text) + self:T2(ratcraft) end end @@ -3724,6 +3734,30 @@ function RAT:Status(message, forID) end +--- Remove ratcraft from self.ratcraft table. +-- @param #RAT self +-- @param #RAT.RatCraft ratcraft The ratcraft to be removed. +-- @return #RAT self +function RAT:_RemoveRatcraft(ratcraft) + + self.ratcraft[ratcraft.index]=nil + + return self +end + +--- Get ratcraft from group. +-- @param #RAT self +-- @param Wrapper.Group#Group group The group object. +-- @return #RAT.RatCraft The ratcraft object. +function RAT:_GetRatcraftFromGroup(group) + + local index=self:GetSpawnIndexFromGroup(group) + + local ratcraft=self.ratcraft[index] + + return ratcraft +end + --- Get (relative) life of first unit of a group. -- @param #RAT self -- @param Wrapper.Group#GROUP group Group of unit. @@ -3752,19 +3786,19 @@ function RAT:_SetStatus(group, status) if group and group:IsAlive() then -- Get index from groupname. - local index=self:GetSpawnIndexFromGroup(group) + local ratcraft=self:_GetRatcraftFromGroup(group) - if self.ratcraft[index] then + if ratcraft then -- Set new status. - self.ratcraft[index].status=status + ratcraft.status=status -- No status update message for "first waypoint", "holding" local no1 = status==RAT.status.Departure local no2 = status==RAT.status.EventBirthAir local no3 = status==RAT.status.Holding - local text=string.format("Flight %s: %s.", group:GetName(), status) + local text=string.format("Flight %s: %s", group:GetName(), status) self:T(RAT.id..text) if not (no1 or no2 or no3) then @@ -3805,12 +3839,12 @@ function RAT:GetStatus(group) if group and group:IsAlive() then -- Get index from groupname. - local index=self:GetSpawnIndexFromGroup(group) + local ratcraft=self:_GetRatcraftFromGroup(group) - if self.ratcraft[index] then + if ratcraft then -- Set new status. - return self.ratcraft[index].status + return ratcraft.status end @@ -3853,19 +3887,20 @@ function RAT:_OnBirth(EventData) else status=RAT.status.EventBirth end - self:_SetStatus(SpawnGroup, status) - - local ratcraft=self.ratcraft[i] --#RAT.RatCraft + self:_SetStatus(SpawnGroup, status) -- Get some info ablout this flight. local i=self:GetSpawnIndexFromGroup(SpawnGroup) - local _departure=self.ratcraft[i].departure:GetName() - local _destination=self.ratcraft[i].destination:GetName() - local _nrespawn=self.ratcraft[i].nrespawn - local _takeoff=self.ratcraft[i].takeoff - local _landing=self.ratcraft[i].landing - local _livery=self.ratcraft[i].livery + + local ratcraft=self.ratcraft[i] --#RAT.RatCraft + + local _departure=ratcraft.departure:GetName() + local _destination=ratcraft.destination:GetName() + local _nrespawn=ratcraft.nrespawn + local _takeoff=ratcraft.takeoff + local _landing=ratcraft.landing + local _livery=ratcraft.livery -- Some is only useful for an actual airbase (not a zone). local _airbase=AIRBASE:FindByName(_departure) @@ -4252,9 +4287,10 @@ function RAT:_OnCrash(EventData) if EventPrefix and EventPrefix == self.alias then -- Update number of alive units in the group. + local ratcraft=self:_GetRatcraftFromGroup(SpawnGroup) local _i=self:GetSpawnIndexFromGroup(SpawnGroup) - self.ratcraft[_i].nunits=self.ratcraft[_i].nunits-1 - local _n=self.ratcraft[_i].nunits + ratcraft.nunits=ratcraft.nunits-1 + local _n=ratcraft.nunits local _n0=SpawnGroup:GetInitialSize() -- Debug info. @@ -4279,7 +4315,7 @@ function RAT:_OnCrash(EventData) else if self.Debug then - self:E(RAT.id.."ERROR: Group does not exist in RAT:_OnCrash().") + self:E(RAT.id.."ERROR: Group does not exist in RAT:_OnCrash()!") end end end @@ -4327,9 +4363,6 @@ function RAT:_Despawn(group, delay) self.ratcraft[index].despawnme=nil self.ratcraft[index].nrespawn=nil ]] - -- Remove ratcraft table entry. - --table.remove(self.ratcraft, index) - -- We should give it at least 3 sec since this seems to be the time until free parking spots after despawn are available again (Sirri Island test). local despawndelay=0 @@ -4689,14 +4722,16 @@ function RAT._WaypointFunction(group, rat, wp) -- Current time and Spawnindex. local Tnow=timer.getTime() - local sdx=rat:GetSpawnIndexFromGroup(group) + + -- Get ratcraft object. + local ratcraft=rat:_GetRatcraftFromGroup(group) -- Departure and destination names. - local departure=rat.ratcraft[sdx].departure:GetName() - local destination=rat.ratcraft[sdx].destination:GetName() - local landing=rat.ratcraft[sdx].landing - local WPholding=rat.ratcraft[sdx].wpholding - local WPfinal=rat.ratcraft[sdx].wpfinal + local departure=ratcraft.departure:GetName() + local destination=ratcraft.destination:GetName() + local landing=ratcraft.landing + local WPholding=ratcraft.wpholding + local WPfinal=ratcraft.wpfinal -- For messages @@ -5297,6 +5332,7 @@ function RAT:_ModifySpawnTemplate(waypoints, livery, spawnplace, departure, take spots=departure:GetFreeParkingSpotsTable(termtype, true) elseif parkingdata~=nil then -- Parking data explicitly set by user as input parameter. + self:T2("Spawning with explicit parking data") nfree=#parkingdata spots=parkingdata else @@ -5353,13 +5389,15 @@ function RAT:_ModifySpawnTemplate(waypoints, livery, spawnplace, departure, take end -- Get parking data (just for debugging). - local parkingdata=departure:GetParkingSpotsTable(termtype) - self:T2(RAT.id..string.format("Parking at %s, terminal type %s:", departure:GetName(), tostring(termtype))) - for _,_spot in pairs(parkingdata) do - self:T2(RAT.id..string.format("%s, Termin Index = %3d, Term Type = %03d, Free = %5s, TOAC = %5s, Term ID0 = %3d, Dist2Rwy = %4d", - departure:GetName(), _spot.TerminalID, _spot.TerminalType,tostring(_spot.Free),tostring(_spot.TOAC),_spot.TerminalID0,_spot.DistToRwy)) + if false then + local parkingdata=departure:GetParkingSpotsTable(termtype) + self:T2(RAT.id..string.format("Parking at %s, terminal type %s:", departure:GetName(), tostring(termtype))) + for _,_spot in pairs(parkingdata) do + self:T2(RAT.id..string.format("%s, Termin Index = %3d, Term Type = %03d, Free = %5s, TOAC = %5s, Term ID0 = %3d, Dist2Rwy = %4d", + departure:GetName(), _spot.TerminalID, _spot.TerminalType,tostring(_spot.Free),tostring(_spot.TOAC),_spot.TerminalID0,_spot.DistToRwy)) + end + self:T(RAT.id..string.format("%s at %s: free parking spots = %d - number of units = %d", self.alias, departure:GetName(), nfree, nunits)) end - self:T(RAT.id..string.format("%s at %s: free parking spots = %d - number of units = %d", self.alias, departure:GetName(), nfree, nunits)) -- Set this to true if not enough spots are available for emergency air start. From 879ea847e987f151ac25230d46be260d82fcaeba Mon Sep 17 00:00:00 2001 From: Frank Date: Fri, 29 Mar 2024 21:54:13 +0100 Subject: [PATCH 10/26] RAT v3 - improved stuff for helos --- Moose Development/Moose/Functional/RAT.lua | 39 +++++++++++++++++---- Moose Development/Moose/Wrapper/Airbase.lua | 2 +- 2 files changed, 33 insertions(+), 8 deletions(-) diff --git a/Moose Development/Moose/Functional/RAT.lua b/Moose Development/Moose/Functional/RAT.lua index 7a7c2da6d..29c6b0307 100644 --- a/Moose Development/Moose/Functional/RAT.lua +++ b/Moose Development/Moose/Functional/RAT.lua @@ -2182,6 +2182,21 @@ function RAT:_SpawnWithRoute(_departure, _destination, _takeoff, _landing, _live -- Create a flightgroup object. local flightgroup=FLIGHTGROUP:New(group) + + function flightgroup.OnAfterPassingWaypoint(flightgroup, From, Event, To, Waypoint) + local waypoint=Waypoint --Ops.OpsGroup#OPSGROUP.Waypoint + + self:T(RAT.id..string.format("RAT passed waypoint %s [uid=%d]", waypoint.name, waypoint.uid)) + + RAT._WaypointFunction(group, self, waypoint.uid) + + end + + function flightgroup:OnAfterPassedFinalWaypoint(From, Event, To) + + self:T(RAT.id..string.format("RAT passed FINAL waypoint")) + + end -- Increase counter of alive groups (also uncontrolled ones). self.alive=self.alive+1 @@ -3515,6 +3530,8 @@ end -- @param #number forID (Optional) Send message only for this ID. function RAT:Status(message, forID) + self:T(RAT.id.."Checking status") + -- Optional arguments. if message==nil then message=false @@ -3530,13 +3547,17 @@ function RAT:Status(message, forID) local nalive=0 -- Loop over all ratcraft. - for spawnindex,ratcraft in ipairs(self.ratcraft) do + for spawnindex,ratcraft in pairs(self.ratcraft) do + + self:T(RAT.id..string.format("Ratcraft Index=%s", tostring(spawnindex))) -- Get group. local group=ratcraft.group --Wrapper.Group#GROUP if group and group:IsAlive() and (group:GetCoordinate() or group:GetVec3()) then nalive=nalive+1 + + self:T(RAT.id..string.format("Ratcraft Index=%s is ALIVE", tostring(spawnindex))) -- Gather some information. local prefix=self:_GetPrefixFromGroup(group) @@ -3709,12 +3730,13 @@ function RAT:Status(message, forID) local idx=self:GetSpawnIndexFromGroup(group) local coord=group:GetCoordinate() self:_Respawn(idx, coord, 0) + else + -- Despawn old group. + if self.despawnair then + self:_Despawn(group, 0) + end end - -- Despawn old group. - if self.despawnair then - self:_Despawn(group, 0) - end end @@ -4738,7 +4760,7 @@ function RAT._WaypointFunction(group, rat, wp) local text -- Info on passing waypoint. - text=string.format("Flight %s passing waypoint #%d %s.", group:GetName(), wp, rat.waypointdescriptions[wp]) + text=string.format("Flight %s passing waypoint #%d %s", group:GetName(), wp, rat.waypointdescriptions[wp]) BASE.T(rat, RAT.id..text) -- New status. @@ -4770,7 +4792,7 @@ function RAT._WaypointFunction(group, rat, wp) MESSAGE:New(text, 10):ToAllIf(rat.Debug) BASE.T(rat, RAT.id..text) -- Enable despawn switch. Next time the status function is called, the aircraft will be despawned. - rat.ratcraft[sdx].despawnme=true + ratcraft.despawnme=true end end end @@ -5330,6 +5352,9 @@ function RAT:_ModifySpawnTemplate(waypoints, livery, spawnplace, departure, take self:T(RAT.id..string.format("Group %s is spawned on farp/ship/runway %s.", self.alias, departure:GetName())) nfree=departure:GetFreeParkingSpotsNumber(termtype, true) spots=departure:GetFreeParkingSpotsTable(termtype, true) + -- Had a case at a Gas Platform where nfree=1 but spots from GetFreeParkingSpotsTable were empty. + --spots=departure:GetParkingSpotsTable(termtype) + self:T(RAT.id..string.format("Free nfree=%d nspots=%d", nfree, #spots)) elseif parkingdata~=nil then -- Parking data explicitly set by user as input parameter. self:T2("Spawning with explicit parking data") diff --git a/Moose Development/Moose/Wrapper/Airbase.lua b/Moose Development/Moose/Wrapper/Airbase.lua index c58904917..dc29f290c 100644 --- a/Moose Development/Moose/Wrapper/Airbase.lua +++ b/Moose Development/Moose/Wrapper/Airbase.lua @@ -1494,7 +1494,7 @@ function AIRBASE:GetFreeParkingSpotsTable(termtype, allowTOAC) -- Put coordinates of free spots into table. local freespots={} for _,_spot in pairs(parkingfree) do - if AIRBASE._CheckTerminalType(_spot.Term_Type, termtype) and _spot.Term_Index>0 then + if AIRBASE._CheckTerminalType(_spot.Term_Type, termtype) then -- and _spot.Term_Index>0 then --Not sure why I had this in. But caused problems now for a Gas platform where a valid spot was not included! if (allowTOAC and allowTOAC==true) or _spot.TO_AC==false then local spot=self:_GetParkingSpotByID(_spot.Term_Index) From ef8c71d27c83390b2274b5acef2757f7688f3641 Mon Sep 17 00:00:00 2001 From: Frank Date: Sat, 30 Mar 2024 22:22:00 +0100 Subject: [PATCH 11/26] RAT - FLIGHTCONTROL - RAT ATC --- Moose Development/Moose/Functional/RAT.lua | 517 +++++++++++------- Moose Development/Moose/Ops/FlightControl.lua | 7 +- Moose Development/Moose/Ops/FlightGroup.lua | 1 + 3 files changed, 323 insertions(+), 202 deletions(-) diff --git a/Moose Development/Moose/Functional/RAT.lua b/Moose Development/Moose/Functional/RAT.lua index 29c6b0307..e7716981b 100644 --- a/Moose Development/Moose/Functional/RAT.lua +++ b/Moose Development/Moose/Functional/RAT.lua @@ -53,6 +53,7 @@ --- RAT class -- @type RAT -- @field #string ClassName Name of the Class. +-- @field #string lid Log identifier. -- @field #boolean Debug Turn debug messages on or off. -- @field Wrapper.Group#GROUP templategroup Group serving as template for the RAT aircraft. -- @field #string alias Alias for spawned group. @@ -545,7 +546,16 @@ RAT.ROT={ } --- RAT ATC. --- @list ATC +-- @type RAT.ATC +-- @field #boolean init True if ATC was initialized. +-- @field #table flight List of flights. +-- @field #table airport List of airports. +-- @field #number unregistered Enumerator for unregistered flights unregistered=-1. +-- @field #number Nclearance Number of flights that get landing clearance simultaniously. Default 2. +-- @field #number delay Delay between landing flights in seconds. Default 240 sec. +-- @field #boolean messages If `true`, ATC sends messages. +-- @field #number T0 Time stamp [sec, timer.getTime()] when ATC was initialized. +-- @field #number onfinal Enumerator onfinal=100. RAT.ATC={ init=false, flight={}, @@ -629,15 +639,18 @@ function RAT:New(groupname, alias) -- Inherit SPAWN class. self=BASE:Inherit(self, SPAWN:NewWithAlias(groupname, alias)) -- #RAT - + + -- Log id. + self.lid=string.format("RAT %s | ", alias or groupname) + -- Version info. if RAT.version.print then - env.info(RAT.id.."Version "..RAT.version.version) + env.info(self.lid.."Version "..RAT.version.version) RAT.version.print=false - end + end -- Welcome message. - self:F(RAT.id..string.format("Creating new RAT object from template: %s.", groupname)) + self:F(self.lid..string.format("Creating new RAT object from template: %s.", groupname)) -- Set alias. alias=alias or groupname @@ -650,7 +663,7 @@ function RAT:New(groupname, alias) -- Check the group actually exists. if DCSgroup==nil then - self:E(RAT.id..string.format("ERROR: Group with name %s does not exist in the mission editor!", groupname)) + self:E(self.lid..string.format("ERROR: Group with name %s does not exist in the mission editor!", groupname)) return nil end @@ -827,7 +840,7 @@ function RAT:Spawn(naircraft) end end text=text..string.format("******************************************************\n") - self:T(RAT.id..text) + self:T(self.lid..text) -- Create submenus. if self.f10menu then @@ -901,13 +914,13 @@ function RAT:_CheckConsistency() -- Only zones but not takeoff air == > Enable takeoff air. if self.Ndeparture_Zones>0 and self.takeoff~=RAT.wp.air then self.takeoff=RAT.wp.air - self:E(RAT.id..string.format("ERROR: At least one zone defined as departure and takeoff is NOT set to air. Enabling air start for RAT group %s!", self.alias)) + self:E(self.lid..string.format("ERROR: At least one zone defined as departure and takeoff is NOT set to air. Enabling air start for RAT group %s!", self.alias)) end -- No airport and no zone specified. if self.Ndeparture_Airports==0 and self.Ndeparture_Zone==0 then self.random_departure=true local text=string.format("No airports or zones found given in SetDeparture(). Enabling random departure airports for RAT group %s!", self.alias) - self:E(RAT.id.."ERROR: "..text) + self:E(self.lid.."ERROR: "..text) MESSAGE:New(text, 30):ToAll() end end @@ -929,20 +942,20 @@ function RAT:_CheckConsistency() if self.Ndestination_Zones>0 and self.landing~=RAT.wp.air and not self.returnzone then self.landing=RAT.wp.air self.destinationzone=true - self:E(RAT.id.."ERROR: At least one zone defined as destination and landing is NOT set to air. Enabling destination zone!") + self:E(self.lid.."ERROR: At least one zone defined as destination and landing is NOT set to air. Enabling destination zone!") end -- No specified airport and no zone found at all. if self.Ndestination_Airports==0 and self.Ndestination_Zones==0 then self.random_destination=true local text="No airports or zones found given in SetDestination(). Enabling random destination airports!" - self:E(RAT.id.."ERROR: "..text) + self:E(self.lid.."ERROR: "..text) MESSAGE:New(text, 30):ToAll() end end -- Destination zone and return zone should not be used together. if self.destinationzone and self.returnzone then - self:E(RAT.id.."ERROR: Destination zone _and_ return to zone not possible! Disabling return to zone.") + self:E(self.lid.."ERROR: Destination zone _and_ return to zone not possible! Disabling return to zone.") self.returnzone=false end -- If returning to a zone, we set the landing type to "air" if takeoff is in air. @@ -1223,7 +1236,7 @@ function RAT:SetDeparture(departurenames) names={departurenames} else -- error message - self:E(RAT.id.."ERROR: Input parameter must be a string or a table in SetDeparture()!") + self:E(self.lid.."ERROR: Input parameter must be a string or a table in SetDeparture()!") end -- Put names into arrays. @@ -1236,7 +1249,7 @@ function RAT:SetDeparture(departurenames) -- If it is not an airport, we assume it is a zone. table.insert(self.departure_ports, name) else - self:E(RAT.id.."ERROR: No departure airport or zone found with name "..name) + self:E(self.lid.."ERROR: No departure airport or zone found with name "..name) end end @@ -1263,7 +1276,7 @@ function RAT:SetDestination(destinationnames) names={destinationnames} else -- Error message. - self:E(RAT.id.."ERROR: Input parameter must be a string or a table in SetDestination()!") + self:E(self.lid.."ERROR: Input parameter must be a string or a table in SetDestination()!") end -- Put names into arrays. @@ -1276,7 +1289,7 @@ function RAT:SetDestination(destinationnames) -- If it is not an airport, we assume it is a zone. table.insert(self.destination_ports, name) else - self:E(RAT.id.."ERROR: No destination airport or zone found with name "..name) + self:E(self.lid.."ERROR: No destination airport or zone found with name "..name) end end @@ -2044,7 +2057,7 @@ function RAT:_InitAircraft(DCSgroup) self.category=RAT.cat.heli else self.category="other" - self:E(RAT.id.."ERROR: Group of RAT is neither airplane nor helicopter!") + self:E(self.lid.."ERROR: Group of RAT is neither airplane nor helicopter!") end -- Get type of aircraft. @@ -2100,7 +2113,7 @@ function RAT:_InitAircraft(DCSgroup) text=text..string.format("Eff range = %6.1f km (with 95 percent initial fuel amount)\n", self.aircraft.Reff/1000) text=text..string.format("Ceiling = %6.1f km = FL%3.0f\n", self.aircraft.ceiling/1000, self.aircraft.ceiling/RAT.unit.FL2m) text=text..string.format("******************************************************\n") - self:T(RAT.id..text) + self:T(self.lid..text) end @@ -2123,7 +2136,7 @@ end -- @param #table parkingdata Explicitly specify the parking spots when spawning at an airport. -- @return #number Spawn index. function RAT:_SpawnWithRoute(_departure, _destination, _takeoff, _landing, _livery, _waypoint, _lastpos, _nrespawn, parkingdata) - self:F({rat=RAT.id, departure=_departure, destination=_destination, takeoff=_takeoff, landing=_landing, livery=_livery, waypoint=_waypoint, lastpos=_lastpos, nrespawn=_nrespawn}) + self:F({rat=self.lid, departure=_departure, destination=_destination, takeoff=_takeoff, landing=_landing, livery=_livery, waypoint=_waypoint, lastpos=_lastpos, nrespawn=_nrespawn}) -- Set takeoff type. local takeoff=self.takeoff @@ -2166,7 +2179,7 @@ function RAT:_SpawnWithRoute(_departure, _destination, _takeoff, _landing, _live -- Choose random livery. livery=self.livery[math.random(#self.livery)] local text=string.format("Chosen livery for group %s: %s", self:_AnticipatedGroupName(), livery) - self:T(RAT.id..text) + self:T(self.lid..text) else livery=nil end @@ -2186,7 +2199,7 @@ function RAT:_SpawnWithRoute(_departure, _destination, _takeoff, _landing, _live function flightgroup.OnAfterPassingWaypoint(flightgroup, From, Event, To, Waypoint) local waypoint=Waypoint --Ops.OpsGroup#OPSGROUP.Waypoint - self:T(RAT.id..string.format("RAT passed waypoint %s [uid=%d]", waypoint.name, waypoint.uid)) + self:T(self.lid..string.format("RAT passed waypoint %s [uid=%d]", waypoint.name, waypoint.uid)) RAT._WaypointFunction(group, self, waypoint.uid) @@ -2194,13 +2207,13 @@ function RAT:_SpawnWithRoute(_departure, _destination, _takeoff, _landing, _live function flightgroup:OnAfterPassedFinalWaypoint(From, Event, To) - self:T(RAT.id..string.format("RAT passed FINAL waypoint")) + self:T(self.lid..string.format("RAT passed FINAL waypoint")) end -- Increase counter of alive groups (also uncontrolled ones). self.alive=self.alive+1 - self:T(RAT.id..string.format("Alive groups counter now = %d.",self.alive)) + self:T(self.lid..string.format("Alive groups counter now = %d.",self.alive)) -- ATC is monitoring this flight (if it is supposed to land). if self.ATCswitch and landing==RAT.wp.landing then @@ -2323,7 +2336,7 @@ end function RAT:ClearForLanding(name) trigger.action.setUserFlag(name, 1) local flagvalue=trigger.misc.getUserFlag(name) - self:T(RAT.id.."ATC: User flag value (landing) for "..name.." set to "..flagvalue) + self:T(self.lid.."ATC: User flag value (landing) for "..name.." set to "..flagvalue) end --- Respawn a group. @@ -2338,7 +2351,7 @@ function RAT:_Respawn(index, lastpos, delay) self:ScheduleOnce(delay, RAT._Respawn, self, index, lastpos, 0) else - self:T(RAT.id..string.format("Respawning ratcraft with index=%d", index)) + self:T(self.lid..string.format("Respawning ratcraft with index=%d", index)) -- Ratcraft object local ratcraft=self.ratcraft[index] --#RAT.RatCraft @@ -2363,10 +2376,10 @@ function RAT:_Respawn(index, lastpos, delay) parkingdata={} end - self:T(RAT.id..string.format("Element %s was parking at spot id=%d", element.name, element.parking.TerminalID)) + self:T(self.lid..string.format("Element %s was parking at spot id=%d", element.name, element.parking.TerminalID)) table.insert(parkingdata, UTILS.DeepCopy(element.parking)) else - self:E(RAT.id..string.format("WARNING: Element %s did NOT have a not parking spot!", tostring(element.name))) + self:E(self.lid..string.format("WARNING: Element %s did NOT have a not parking spot!", tostring(element.name))) end end @@ -2528,7 +2541,7 @@ function RAT:_Respawn(index, lastpos, delay) end -- Spawn new group. - self:T(RAT.id..string.format("%s delayed respawn in %.1f seconds.", self.alias, respawndelay)) + self:T(self.lid..string.format("%s delayed respawn in %.1f seconds.", self.alias, respawndelay)) self:ScheduleOnce(respawndelay, RAT._SpawnWithRoute, self,_departure,_destination,_takeoff,_landing,_livery, nil,_lastpos, nil, parkingdata) end @@ -2607,7 +2620,7 @@ function RAT:_SetRoute(takeoff, landing, _departure, _destination, _waypoint) departure=ZONE:FindByName(_departure) else local text=string.format("ERROR! Specified departure airport %s does not exist for %s.", _departure, self.alias) - self:E(RAT.id..text) + self:E(self.lid..text) end else @@ -2620,7 +2633,7 @@ function RAT:_SetRoute(takeoff, landing, _departure, _destination, _waypoint) -- Return nil if no departure could be found. if not departure then local text=string.format("ERROR! No valid departure airport could be found for %s.", self.alias) - self:E(RAT.id..text) + self:E(self.lid..text) return nil end @@ -2687,7 +2700,7 @@ function RAT:_SetRoute(takeoff, landing, _departure, _destination, _waypoint) mindist=math.max(self.mindist, mindist) local text=string.format("Adjusting min distance to %d km (for given min FL%03d)", mindist/1000, self.FLminuser/RAT.unit.FL2m) - self:T(RAT.id..text) + self:T(self.lid..text) end -- DESTINATION AIRPORT @@ -2705,7 +2718,7 @@ function RAT:_SetRoute(takeoff, landing, _departure, _destination, _waypoint) destination=ZONE:FindByName(_destination) else local text=string.format("ERROR: Specified destination airport/zone %s does not exist for %s!", _destination, self.alias) - self:E(RAT.id.."ERROR: "..text) + self:E(self.lid.."ERROR: "..text) end else @@ -2734,7 +2747,7 @@ function RAT:_SetRoute(takeoff, landing, _departure, _destination, _waypoint) if not destination then local text=string.format("No valid destination airport could be found for %s!", self.alias) MESSAGE:New(text, 60):ToAll() - self:E(RAT.id.."ERROR: "..text) + self:E(self.lid.."ERROR: "..text) return nil end @@ -2742,7 +2755,7 @@ function RAT:_SetRoute(takeoff, landing, _departure, _destination, _waypoint) if destination:GetName()==departure:GetName() then local text=string.format("%s: Destination and departure are identical. Airport/zone %s.", self.alias, destination:GetName()) MESSAGE:New(text, 30):ToAll() - self:E(RAT.id.."ERROR: "..text) + self:E(self.lid.."ERROR: "..text) end -- Get a random point inside zone return zone. @@ -3004,7 +3017,7 @@ function RAT:_SetRoute(takeoff, landing, _departure, _destination, _waypoint) text=text..string.format("h_descent_max = %6.1f m\n", h_descent_max) end text=text..string.format("******************************************************\n") - self:T2(RAT.id..text) + self:T2(self.lid..text) -- Ensure that cruise distance is positve. Can be slightly negative in special cases. And we don't want to turn back. if d_cruise<0 then @@ -3211,10 +3224,10 @@ function RAT:_PickDeparture(takeoff) if takeoff==RAT.wp.air then dep=ZONE:FindByName(name) else - self:E(RAT.id..string.format("ERROR! Takeoff is not in air. Cannot use %s as departure.", name)) + self:E(self.lid..string.format("ERROR! Takeoff is not in air. Cannot use %s as departure.", name)) end else - self:E(RAT.id..string.format("ERROR: No airport or zone found with name %s.", name)) + self:E(self.lid..string.format("ERROR: No airport or zone found with name %s.", name)) end -- Add to departures table. @@ -3227,7 +3240,7 @@ function RAT:_PickDeparture(takeoff) end -- Info message. - self:T(RAT.id..string.format("Number of possible departures for %s= %d", self.alias, #departures)) + self:T(self.lid..string.format("Number of possible departures for %s= %d", self.alias, #departures)) -- Select departure airport or zone. local departure=departures[math.random(#departures)] @@ -3240,9 +3253,9 @@ function RAT:_PickDeparture(takeoff) text=string.format("%s: Chosen departure airport: %s (ID %d)", self.alias, departure:GetName(), departure:GetID()) end --MESSAGE:New(text, 30):ToAllIf(self.Debug) - self:T(RAT.id..text) + self:T(self.lid..text) else - self:E(RAT.id..string.format("ERROR! No departure airport or zone found for %s.", self.alias)) + self:E(self.lid..string.format("ERROR! No departure airport or zone found for %s.", self.alias)) departure=nil end @@ -3323,10 +3336,10 @@ function RAT:_PickDestination(departure, q, minrange, maxrange, random, landing) if landing==RAT.wp.air then dest=ZONE:FindByName(name) else - self:E(RAT.id..string.format("ERROR! Landing is not in air. Cannot use zone %s as destination!", name)) + self:E(self.lid..string.format("ERROR! Landing is not in air. Cannot use zone %s as destination!", name)) end else - self:E(RAT.id..string.format("ERROR! No airport or zone found with name %s", name)) + self:E(self.lid..string.format("ERROR! No airport or zone found with name %s", name)) end if dest then @@ -3338,7 +3351,7 @@ function RAT:_PickDestination(departure, q, minrange, maxrange, random, landing) table.insert(destinations, dest) else local text=string.format("Destination %s is ouside range. Distance = %5.1f km, min = %5.1f km, max = %5.1f km.", name, distance, minrange, maxrange) - self:T(RAT.id..text) + self:T(self.lid..text) end end @@ -3347,7 +3360,7 @@ function RAT:_PickDestination(departure, q, minrange, maxrange, random, landing) end -- Info message. - self:T(RAT.id..string.format("Number of possible destinations = %s.", #destinations)) + self:T(self.lid..string.format("Number of possible destinations = %s.", #destinations)) if #destinations > 0 then --- Compare distance of destination airports. @@ -3379,11 +3392,11 @@ function RAT:_PickDestination(departure, q, minrange, maxrange, random, landing) else text=string.format("%s Chosen destination airport: %s (ID %d).", self.alias, destination:GetName(), destination:GetID()) end - self:T(RAT.id..text) + self:T(self.lid..text) --MESSAGE:New(text, 30):ToAllIf(self.Debug) else - self:E(RAT.id.."ERROR! No destination airport or zone found.") + self:E(self.lid.."ERROR! No destination airport or zone found.") destination=nil end @@ -3471,11 +3484,11 @@ function RAT:_GetAirportsOfMap() table.insert(self.airports_map, _myab) local text="MOOSE: Airport ID = ".._myab:GetID().." and Name = ".._myab:GetName()..", Category = ".._myab:GetCategory()..", TypeName = ".._myab:GetTypeName() - self:T(RAT.id..text) + self:T(self.lid..text) else - self:E(RAT.id..string.format("WARNING: Airbase %s does not exsist as MOOSE object!", tostring(_name))) + self:E(self.lid..string.format("WARNING: Airbase %s does not exsist as MOOSE object!", tostring(_name))) end end @@ -3518,7 +3531,7 @@ function RAT:_GetAirportsOfCoalition() if #self.airports==0 then local text=string.format("No possible departure/destination airports found for RAT %s.", tostring(self.alias)) MESSAGE:New(text, 10):ToAll() - self:E(RAT.id..text) + self:E(self.lid..text) end end @@ -3530,7 +3543,7 @@ end -- @param #number forID (Optional) Send message only for this ID. function RAT:Status(message, forID) - self:T(RAT.id.."Checking status") + self:T(self.lid.."Checking status") -- Optional arguments. if message==nil then @@ -3549,7 +3562,7 @@ function RAT:Status(message, forID) -- Loop over all ratcraft. for spawnindex,ratcraft in pairs(self.ratcraft) do - self:T(RAT.id..string.format("Ratcraft Index=%s", tostring(spawnindex))) + self:T(self.lid..string.format("Ratcraft Index=%s", tostring(spawnindex))) -- Get group. local group=ratcraft.group --Wrapper.Group#GROUP @@ -3557,7 +3570,7 @@ function RAT:Status(message, forID) if group and group:IsAlive() and (group:GetCoordinate() or group:GetVec3()) then nalive=nalive+1 - self:T(RAT.id..string.format("Ratcraft Index=%s is ALIVE", tostring(spawnindex))) + self:T(self.lid..string.format("Ratcraft Index=%s is ALIVE", tostring(spawnindex))) -- Gather some information. local prefix=self:_GetPrefixFromGroup(group) @@ -3621,7 +3634,7 @@ function RAT:Status(message, forID) local Ug=unitcoord:Get2DDistance(ratcraft.Uground[unitname]) -- Debug info - self:T2(RAT.id..string.format("Unit %s travelled distance on ground %.1f m since %d seconds.", unitname, Ug, dTlast)) + self:T2(self.lid..string.format("Unit %s travelled distance on ground %.1f m since %d seconds.", unitname, Ug, dTlast)) -- If aircraft did not move more than 50 m since last check, we call it stationary and despawn it. -- Aircraft which are spawned uncontrolled or starting their engines are not counted. @@ -3694,7 +3707,7 @@ function RAT:Status(message, forID) text=text..string.format("\nTime on ground = %6.0f seconds\n", Tg) text=text..string.format("Position change = %8.1f m since %3.0f seconds.", Dg, dTlast) end - self:T(RAT.id..text) + self:T(self.lid..text) if message then MESSAGE:New(text, 20):ToAll() end @@ -3706,14 +3719,14 @@ function RAT:Status(message, forID) -- Despawn unit if it did not move more then 50 m in the last 180 seconds. if stationary then local text=string.format("Group %s is despawned after being %d seconds inaktive on ground.", self.alias, dTlast) - self:T(RAT.id..text) + self:T(self.lid..text) self:_Despawn(group) end -- Despawn group if life is < 10% and distance travelled < 100 m. if life<10 and Dtravel<100 then local text=string.format("Damaged group %s is despawned. Life = %3.0f", self.alias, life) - self:T(RAT.id..text) + self:T(self.lid..text) self:_Despawn(group) end @@ -3723,7 +3736,7 @@ function RAT:Status(message, forID) if ratcraft.despawnme then local text=string.format("Flight %s will be despawned NOW!", self.alias) - self:T(RAT.id..text) + self:T(self.lid..text) -- Respawn group if (not self.norespawn) and (not self.respawn_after_takeoff) then @@ -3743,7 +3756,7 @@ function RAT:Status(message, forID) else -- Group does not exist. local text=string.format("Group does not exist in loop ratcraft status for spawn index=%d", spawnindex) - self:T2(RAT.id..text) + self:T2(self.lid..text) self:T2(ratcraft) end @@ -3751,7 +3764,7 @@ function RAT:Status(message, forID) -- Alive groups. local text=string.format("Alive groups of %s: %d, nalive=%d/%d", self.alias, self.alive, nalive, self.ngroups) - self:T(RAT.id..text) + self:T(self.lid..text) MESSAGE:New(text, 20):ToAllIf(message and not forID) end @@ -3791,10 +3804,10 @@ function RAT:_GetLife(group) if unit then life=unit:GetLife()/unit:GetLife0()*100 else - self:T2(RAT.id.."ERROR! Unit does not exist in RAT_Getlife(). Returning zero.") + self:T2(self.lid.."ERROR! Unit does not exist in RAT_Getlife(). Returning zero.") end else - self:T2(RAT.id.."ERROR! Group does not exist in RAT_Getlife(). Returning zero.") + self:T2(self.lid.."ERROR! Group does not exist in RAT_Getlife(). Returning zero.") end return life end @@ -3821,7 +3834,7 @@ function RAT:_SetStatus(group, status) local no3 = status==RAT.status.Holding local text=string.format("Flight %s: %s", group:GetName(), status) - self:T(RAT.id..text) + self:T(self.lid..text) if not (no1 or no2 or no3) then MESSAGE:New(text, 10):ToAllIf(self.reportstatus) @@ -3883,7 +3896,7 @@ end -- @param Core.Event#EVENTDATA EventData function RAT:_OnBirth(EventData) self:F3(EventData) - self:T3(RAT.id.."Captured event birth!") + self:T3(self.lid.."Captured event birth!") local SpawnGroup = EventData.IniGroup --Wrapper.Group#GROUP @@ -3898,7 +3911,7 @@ function RAT:_OnBirth(EventData) if EventPrefix == self.alias then local text="Event: Group "..SpawnGroup:GetName().." was born." - self:T(RAT.id..text) + self:T(self.lid..text) -- Set status. local status="unknown in birth" @@ -3943,7 +3956,7 @@ function RAT:_OnBirth(EventData) -- Error message. local text=string.format("ERROR: RAT group of %s was spawned on runway. Group #%d will be despawned immediately!", self.alias, i) MESSAGE:New(text,30):ToAllIf(self.Debug) - self:E(RAT.id..text) + self:E(self.lid..text) if self.Debug then SpawnGroup:FlareRed() end @@ -3959,7 +3972,7 @@ function RAT:_OnBirth(EventData) -- This creates a completely new group, i.e. livery etc from earlier flights (continuejourney, commute) is not taken over. text=string.format("Try spawning new aircraft of group %s at another location. Attempt %d of max %d.", self.alias,_nrespawn,self.onrunwaymaxretry) MESSAGE:New(text,10):ToAllIf(self.Debug) - self:T(RAT.id..text) + self:T(self.lid..text) -- Spawn new group. self:_SpawnWithRoute(nil, nil, nil, nil, nil, nil, nil, _nrespawn) @@ -3969,7 +3982,7 @@ function RAT:_OnBirth(EventData) if self.respawn_inair and not self.uncontrolled then text=string.format("Spawning new aircraft of group %s in air since no parking slot is available at %s.", self.alias, _departure) MESSAGE:New(text,10):ToAll() - self:T(RAT.id..text) + self:T(self.lid..text) -- Spawn new group at this airport but already in air. self:_SpawnWithRoute(_departure, _destination, RAT.wp.air, _landing, _livery) @@ -3986,7 +3999,7 @@ function RAT:_OnBirth(EventData) if ontop then local text=string.format("ERROR: Group of %s was spawned on top of another unit. Group #%d will be despawned immediately!", self.alias, i) MESSAGE:New(text,30):ToAllIf(self.Debug) - self:T(RAT.id..text) + self:T(self.lid..text) if self.Debug then SpawnGroup:FlareYellow() end @@ -3997,7 +4010,7 @@ function RAT:_OnBirth(EventData) end end else - self:T2(RAT.id.."ERROR: Group does not exist in RAT:_OnBirth().") + self:T2(self.lid.."ERROR: Group does not exist in RAT:_OnBirth().") end end @@ -4007,7 +4020,7 @@ end -- @param Core.Event#EVENTDATA EventData function RAT:_OnEngineStartup(EventData) self:F3(EventData) - self:T3(RAT.id.."Captured event EngineStartup!") + self:T3(self.lid.."Captured event EngineStartup!") local SpawnGroup = EventData.IniGroup --Wrapper.Group#GROUP @@ -4022,7 +4035,7 @@ function RAT:_OnEngineStartup(EventData) if EventPrefix == self.alias then local text="Event: Group "..SpawnGroup:GetName().." started engines." - self:T(RAT.id..text) + self:T(self.lid..text) -- Set status. local status @@ -4036,7 +4049,7 @@ function RAT:_OnEngineStartup(EventData) end else - self:T2(RAT.id.."ERROR: Group does not exist in RAT:_EngineStartup().") + self:T2(self.lid.."ERROR: Group does not exist in RAT:_EngineStartup().") end end @@ -4058,7 +4071,7 @@ function RAT:_OnTakeoff(EventData) if EventPrefix == self.alias then local text="Event: Group "..SpawnGroup:GetName().." is airborne." - self:T(RAT.id..text) + self:T(self.lid..text) -- Set status. local status=RAT.status.EventTakeoff @@ -4066,7 +4079,7 @@ function RAT:_OnTakeoff(EventData) if self.respawn_after_takeoff then text="Event: Group "..SpawnGroup:GetName().." will be respawned after takeoff." - self:T(RAT.id..text) + self:T(self.lid..text) -- Respawn group. We respawn with no parameters from the old flight. self:_SpawnWithRoute(nil, nil, nil, nil, nil, nil, nil, nil) @@ -4076,7 +4089,7 @@ function RAT:_OnTakeoff(EventData) end else - self:T2(RAT.id.."ERROR: Group does not exist in RAT:_OnTakeoff().") + self:T2(self.lid.."ERROR: Group does not exist in RAT:_OnTakeoff().") end end @@ -4098,7 +4111,7 @@ function RAT:_OnLand(EventData) if EventPrefix == self.alias then local text="Event: Group "..SpawnGroup:GetName().." landed." - self:T(RAT.id..text) + self:T(self.lid..text) -- Set status. local status=RAT.status.EventLand @@ -4111,7 +4124,7 @@ function RAT:_OnLand(EventData) if self.respawn_at_landing and not self.norespawn then text="Event: Group "..SpawnGroup:GetName().." will be respawned." - self:T(RAT.id..text) + self:T(self.lid..text) -- Respawn group. local idx=self:GetSpawnIndexFromGroup(SpawnGroup) @@ -4123,7 +4136,7 @@ function RAT:_OnLand(EventData) end else - self:T2(RAT.id.."ERROR: Group does not exist in RAT:_OnLand().") + self:T2(self.lid.."ERROR: Group does not exist in RAT:_OnLand().") end end @@ -4132,7 +4145,7 @@ end -- @param Core.Event#EVENTDATA EventData function RAT:_OnEngineShutdown(EventData) self:F3(EventData) - self:T3(RAT.id.."Captured event EngineShutdown!") + self:T3(self.lid.."Captured event EngineShutdown!") local SpawnGroup = EventData.IniGroup --Wrapper.Group#GROUP @@ -4151,7 +4164,7 @@ function RAT:_OnEngineShutdown(EventData) local currentstate=self:GetStatus(SpawnGroup) local text=string.format("Event: Unit %s of group %s shut down its engines. Current state %s.", EventData.IniUnitName, SpawnGroup:GetName(), currentstate) - self:T(RAT.id..text) + self:T(self.lid..text) -- Check that this is not the second unit of the group so that we dont trigger re- and despawns twice. if currentstate~=RAT.status.EventEngineShutdown and currentstate~="Dead" then @@ -4162,7 +4175,7 @@ function RAT:_OnEngineShutdown(EventData) if not self.respawn_at_landing and not self.norespawn then text=string.format("Event: Group %s will be respawned. Current state %s => new state %s.", SpawnGroup:GetName(), currentstate, status) - self:T(RAT.id..text) + self:T(self.lid..text) -- Respawn group. local idx=self:GetSpawnIndexFromGroup(SpawnGroup) @@ -4173,7 +4186,7 @@ function RAT:_OnEngineShutdown(EventData) -- Despawn group. text="Event: Group "..SpawnGroup:GetName().." will be destroyed now." - self:T(RAT.id..text) + self:T(self.lid..text) self:_Despawn(SpawnGroup) end @@ -4185,7 +4198,7 @@ function RAT:_OnEngineShutdown(EventData) end else - self:T2(RAT.id.."ERROR: Group does not exist in RAT:_OnEngineShutdown().") + self:T2(self.lid.."ERROR: Group does not exist in RAT:_OnEngineShutdown().") end end @@ -4194,7 +4207,7 @@ end -- @param Core.Event#EVENTDATA EventData function RAT:_OnHit(EventData) self:F3(EventData) - self:T(RAT.id..string.format("Captured event Hit by %s! Initiator %s. Target %s", self.alias, tostring(EventData.IniUnitName), tostring(EventData.TgtUnitName))) + self:T(self.lid..string.format("Captured event Hit by %s! Initiator %s. Target %s", self.alias, tostring(EventData.IniUnitName), tostring(EventData.TgtUnitName))) local SpawnGroup = EventData.TgtGroup --Wrapper.Group#GROUP @@ -4206,7 +4219,7 @@ function RAT:_OnHit(EventData) -- Check that the template name actually belongs to this object. if EventPrefix and EventPrefix == self.alias then -- Debug info. - self:T(RAT.id..string.format("Event: Group %s was hit. Unit %s.", SpawnGroup:GetName(), tostring(EventData.TgtUnitName))) + self:T(self.lid..string.format("Event: Group %s was hit. Unit %s.", SpawnGroup:GetName(), tostring(EventData.TgtUnitName))) local text=string.format("%s, unit %s was hit!", self.alias, EventData.TgtUnitName) MESSAGE:New(text, 10):ToAllIf(self.reportstatus or self.Debug) @@ -4219,7 +4232,7 @@ end -- @param Core.Event#EVENTDATA EventData function RAT:_OnDeadOrCrash(EventData) self:F3(EventData) - self:T3(RAT.id.."Captured event DeadOrCrash!") + self:T3(self.lid.."Captured event DeadOrCrash!") local SpawnGroup = EventData.IniGroup --Wrapper.Group#GROUP @@ -4238,7 +4251,7 @@ function RAT:_OnDeadOrCrash(EventData) -- Debug info. local text=string.format("Event: Group %s crashed or died. Alive counter = %d.", SpawnGroup:GetName(), self.alive) - self:T(RAT.id..text) + self:T(self.lid..text) -- Split crash and dead events. if EventData.id == world.event.S_EVENT_CRASH then @@ -4262,7 +4275,7 @@ end -- @param Core.Event#EVENTDATA EventData function RAT:_OnDead(EventData) self:F3(EventData) - self:T3(RAT.id.."Captured event Dead!") + self:T3(self.lid.."Captured event Dead!") local SpawnGroup = EventData.IniGroup --Wrapper.Group#GROUP @@ -4277,7 +4290,7 @@ function RAT:_OnDead(EventData) if EventPrefix == self.alias then local text=string.format("Event: Group %s died. Unit %s.", SpawnGroup:GetName(), EventData.IniUnitName) - self:T(RAT.id..text) + self:T(self.lid..text) -- Set status. local status=RAT.status.EventDead @@ -4287,7 +4300,7 @@ function RAT:_OnDead(EventData) end else - self:T2(RAT.id.."ERROR: Group does not exist in RAT:_OnDead().") + self:T2(self.lid.."ERROR: Group does not exist in RAT:_OnDead().") end end @@ -4296,7 +4309,7 @@ end -- @param Core.Event#EVENTDATA EventData function RAT:_OnCrash(EventData) self:F3(EventData) - self:T3(RAT.id.."Captured event Crash!") + self:T3(self.lid.."Captured event Crash!") local SpawnGroup = EventData.IniGroup --Wrapper.Group#GROUP @@ -4317,7 +4330,7 @@ function RAT:_OnCrash(EventData) -- Debug info. local text=string.format("Event: Group %s crashed. Unit %s. Units still alive %d of %d.", SpawnGroup:GetName(), EventData.IniUnitName, _n, _n0) - self:T(RAT.id..text) + self:T(self.lid..text) -- Set status. local status=RAT.status.EventCrash @@ -4326,7 +4339,7 @@ function RAT:_OnCrash(EventData) -- Respawn group if all units are dead. if _n==0 and self.respawn_after_crash and not self.norespawn then local text=string.format("No units left of group %s. Group will be respawned now.", SpawnGroup:GetName()) - self:T(RAT.id..text) + self:T(self.lid..text) -- Respawn group. local idx=self:GetSpawnIndexFromGroup(SpawnGroup) local coord=SpawnGroup:GetCoordinate() @@ -4337,7 +4350,7 @@ function RAT:_OnCrash(EventData) else if self.Debug then - self:E(RAT.id.."ERROR: Group does not exist in RAT:_OnCrash()!") + self:E(self.lid.."ERROR: Group does not exist in RAT:_OnCrash()!") end end end @@ -4398,7 +4411,7 @@ function RAT:_Despawn(group, delay) -- This will destroy the DCS group and create a single DEAD event. --if despawndelay>0.5 then - self:T(RAT.id..string.format("%s delayed despawn in %.1f seconds.", self.alias, despawndelay)) + self:T(self.lid..string.format("%s delayed despawn in %.1f seconds.", self.alias, despawndelay)) SCHEDULER:New(nil, self._Destroy, {self, group}, despawndelay) --else --self:_Destroy(group) @@ -4546,7 +4559,7 @@ function RAT:_Waypoint(index, description, Type, Coord, Speed, Altitude, Airport _Action="Turning Point" _alttype="BARO" else - self:E(RAT.id.."ERROR: Unknown waypoint type in RAT:Waypoint() function!") + self:E(self.lid.."ERROR: Unknown waypoint type in RAT:Waypoint() function!") _Type="Turning Point" _Action="Turning Point" _alttype="RADIO" @@ -4574,7 +4587,7 @@ function RAT:_Waypoint(index, description, Type, Coord, Speed, Altitude, Airport text=text..string.format("No airport/zone specified\n") end text=text.."******************************************************\n" - self:T2(RAT.id..text) + self:T2(self.lid..text) -- define waypoint local RoutePoint = {} @@ -4608,7 +4621,7 @@ function RAT:_Waypoint(index, description, Type, Coord, Speed, Altitude, Airport elseif AirbaseCategory == Airbase.Category.AIRDROME then RoutePoint.airdromeId = AirbaseID else - self:T(RAT.id.."Unknown Airport category in _Waypoint()!") + self:T(self.lid.."Unknown Airport category in _Waypoint()!") end end -- properties @@ -4676,7 +4689,7 @@ function RAT:_Routeinfo(waypoints, comment) text=text..string.format("******************************************************\n") -- Debug info. - self:T2(RAT.id..text) + self:T2(self.lid..text) -- return total route length in meters return total @@ -4761,7 +4774,7 @@ function RAT._WaypointFunction(group, rat, wp) -- Info on passing waypoint. text=string.format("Flight %s passing waypoint #%d %s", group:GetName(), wp, rat.waypointdescriptions[wp]) - BASE.T(rat, RAT.id..text) + rat:T(rat.lid..text) -- New status. local status=rat.waypointstatus[wp] @@ -4785,12 +4798,12 @@ function RAT._WaypointFunction(group, rat, wp) if wp==WPfinal then text=string.format("Flight %s arrived at final destination %s.", group:GetName(), destination) MESSAGE:New(text, 10):ToAllIf(rat.reportstatus) - BASE.T(rat, RAT.id..text) + rat:T(rat.lid..text) if landing==RAT.wp.air then text=string.format("Activating despawn switch for flight %s! Group will be detroyed soon.", group:GetName()) MESSAGE:New(text, 10):ToAllIf(rat.Debug) - BASE.T(rat, RAT.id..text) + rat:T(rat.lid..text) -- Enable despawn switch. Next time the status function is called, the aircraft will be despawned. ratcraft.despawnme=true end @@ -4842,7 +4855,6 @@ end --- Randomly activates an uncontrolled aircraft. -- @param #RAT self function RAT:_ActivateUncontrolled() - self:F() -- Spawn indices of uncontrolled inactive aircraft. local idx={} @@ -4852,14 +4864,15 @@ function RAT:_ActivateUncontrolled() local nactive=0 -- Loop over RAT groups and count the active ones. - for spawnindex,ratcraft in pairs(self.ratcraft) do + for spawnindex,_ratcraft in pairs(self.ratcraft) do + local ratcraft=_ratcraft --#RAT.RatCraft local group=ratcraft.group --Wrapper.Group#GROUP if group and group:IsAlive() then - local text=string.format("Uncontrolled: Group = %s (spawnindex = %d), active = %s.", ratcraft.group:GetName(), spawnindex, tostring(ratcraft.active)) - self:T2(RAT.id..text) + local text=string.format("Uncontrolled: Group = %s (spawnindex = %d), active = %s", ratcraft.group:GetName(), spawnindex, tostring(ratcraft.active)) + self:T2(self.lid..text) if ratcraft.active then nactive=nactive+1 @@ -4871,19 +4884,36 @@ function RAT:_ActivateUncontrolled() end -- Debug message. - local text=string.format("Uncontrolled: Ninactive = %d, Nactive = %d (of max %d).", #idx, nactive, self.activate_max) - self:T(RAT.id..text) + local text=string.format("Uncontrolled: Ninactive = %d, Nactive = %d (of max %d)", #idx, nactive, self.activate_max) + self:T(self.lid..text) if #idx>0 and nactive Less effort. - self:T(RAT.id..string.format("Group %s is spawned on farp/ship/runway %s.", self.alias, departure:GetName())) + self:T(self.lid..string.format("Group %s is spawned on farp/ship/runway %s.", self.alias, departure:GetName())) nfree=departure:GetFreeParkingSpotsNumber(termtype, true) spots=departure:GetFreeParkingSpotsTable(termtype, true) -- Had a case at a Gas Platform where nfree=1 but spots from GetFreeParkingSpotsTable were empty. --spots=departure:GetParkingSpotsTable(termtype) - self:T(RAT.id..string.format("Free nfree=%d nspots=%d", nfree, #spots)) + self:T(self.lid..string.format("Free nfree=%d nspots=%d", nfree, #spots)) elseif parkingdata~=nil then -- Parking data explicitly set by user as input parameter. self:T2("Spawning with explicit parking data") @@ -5365,18 +5431,18 @@ function RAT:_ModifySpawnTemplate(waypoints, livery, spawnplace, departure, take if self.category==RAT.cat.heli then if termtype==nil then -- Try exclusive helo spots first. - self:T(RAT.id..string.format("Helo group %s is spawned at %s using terminal type %d.", self.alias, departure:GetName(), AIRBASE.TerminalType.HelicopterOnly)) + self:T(self.lid..string.format("Helo group %s is spawned at %s using terminal type %d.", self.alias, departure:GetName(), AIRBASE.TerminalType.HelicopterOnly)) spots=departure:FindFreeParkingSpotForAircraft(TemplateGroup, AIRBASE.TerminalType.HelicopterOnly, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits) nfree=#spots if nfree air start!", self.SpawnTemplatePrefix, departure:GetName())) + self:E(self.lid..string.format("WARNING: Group %s has no parking spots at %s ==> air start!", self.SpawnTemplatePrefix, departure:GetName())) -- Not enough parking spots at the airport ==> Spawn in air. spawnonground=false @@ -5488,7 +5554,7 @@ function RAT:_ModifySpawnTemplate(waypoints, livery, spawnplace, departure, take PointVec3.y=PointVec3:GetLandHeight()+math.random(500,3000) end else - self:E(RAT.id..string.format("WARNING: Group %s has no parking spots at %s ==> No emergency air start or uncontrolled spawning ==> No spawn!", self.SpawnTemplatePrefix, departure:GetName())) + self:E(self.lid..string.format("WARNING: Group %s has no parking spots at %s ==> No emergency air start or uncontrolled spawning ==> No spawn!", self.SpawnTemplatePrefix, departure:GetName())) return nil end end @@ -5522,14 +5588,14 @@ function RAT:_ModifySpawnTemplate(waypoints, livery, spawnplace, departure, take -- Ships and FARPS seem to have a build in queue. if spawnonship or spawnonfarp or spawnonrunway or automatic then - self:T(RAT.id..string.format("RAT group %s spawning at farp, ship or runway %s.", self.alias, departure:GetName())) + self:T(self.lid..string.format("RAT group %s spawning at farp, ship or runway %s.", self.alias, departure:GetName())) -- Spawn on ship. We take only the position of the ship. SpawnTemplate.units[UnitID].x = PointVec3.x --TX SpawnTemplate.units[UnitID].y = PointVec3.z --TY SpawnTemplate.units[UnitID].alt = PointVec3.y else - self:T(RAT.id..string.format("RAT group %s spawning at airbase %s on parking spot id %d", self.alias, departure:GetName(), parkingindex[UnitID])) + self:T(self.lid..string.format("RAT group %s spawning at airbase %s on parking spot id %d", self.alias, departure:GetName(), parkingindex[UnitID])) -- Get coordinates of parking spot. SpawnTemplate.units[UnitID].x = parkingspots[UnitID].x @@ -5538,7 +5604,7 @@ function RAT:_ModifySpawnTemplate(waypoints, livery, spawnplace, departure, take end else - self:T(RAT.id..string.format("RAT group %s spawning in air at %s.", self.alias, departure:GetName())) + self:T(self.lid..string.format("RAT group %s spawning in air at %s.", self.alias, departure:GetName())) -- Spawn in air as requested initially. Original template orientation is perserved, altitude is already correctly set. SpawnTemplate.units[UnitID].x = TX @@ -5560,8 +5626,8 @@ function RAT:_ModifySpawnTemplate(waypoints, livery, spawnplace, departure, take end -- Debug info. - self:T2(RAT.id..string.format("RAT group %s unit number %d: Parking = %s",self.alias, UnitID, tostring(UnitTemplate.parking))) - self:T2(RAT.id..string.format("RAT group %s unit number %d: Parking ID = %s",self.alias, UnitID, tostring(UnitTemplate.parking_id))) + self:T2(self.lid..string.format("RAT group %s unit number %d: Parking = %s",self.alias, UnitID, tostring(UnitTemplate.parking))) + self:T2(self.lid..string.format("RAT group %s unit number %d: Parking ID = %s",self.alias, UnitID, tostring(UnitTemplate.parking_id))) -- Set initial heading. @@ -5625,30 +5691,61 @@ function RAT:_ModifySpawnTemplate(waypoints, livery, spawnplace, departure, take end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +--- RAT ATC +--- + +--- Data structure a RAT ATC airbase object. +-- @type RAT.AtcAirport +-- @field #table queue Queue. +-- @field #boolean busy Whether airport is busy. +-- @field #table onfinal List of flights on final. +-- @field #number Nonfinal Number of flights on final. +-- @field #number Tlastclearance Time stamp when last flight started final approach. + +--- Data structure a RAT ATC airbase object. +-- @type RAT.AtcFlight +-- @field #table destination The destination airbase. +-- @field #number Tarrive Time stamp when flight arrived at holding. +-- @field #number holding Holding time. +-- @field #number Tonfinal Time stamp when flight started final approach. --- Initializes the ATC arrays and starts schedulers. -- @param #RAT self -- @param #table airports_map List of all airports of the map. function RAT:_ATCInit(airports_map) + if not RAT.ATC.init then - local text - text="Starting RAT ATC.\nSimultanious = "..RAT.ATC.Nclearance.."\n".."Delay = "..RAT.ATC.delay + + local text="Starting RAT ATC.\nSimultanious = "..RAT.ATC.Nclearance.."\n".."Delay = "..RAT.ATC.delay BASE:T(RAT.id..text) - RAT.ATC.init=true + for _,ap in pairs(airports_map) do - local name=ap:GetName() - RAT.ATC.airport[name]={} - RAT.ATC.airport[name].queue={} - RAT.ATC.airport[name].busy=false - RAT.ATC.airport[name].onfinal={} - RAT.ATC.airport[name].Nonfinal=0 - RAT.ATC.airport[name].traffic=0 - RAT.ATC.airport[name].Tlastclearance=nil + local airbase=ap --Wrapper.Airbase#AIRBASE + local name=airbase:GetName() + + local fc=_DATABASE:GetFlightControl(name) + + if not fc then + + RAT.ATC.airport[name]={} + RAT.ATC.airport[name].queue={} + RAT.ATC.airport[name].busy=false + RAT.ATC.airport[name].onfinal={} + RAT.ATC.airport[name].Nonfinal=0 + RAT.ATC.airport[name].traffic=0 + RAT.ATC.airport[name].Tlastclearance=nil + + end end - SCHEDULER:New(nil, RAT._ATCCheck, {self}, 5, 15) + + SCHEDULER:New(nil, RAT._ATCCheck, {self}, 5, 15) SCHEDULER:New(nil, RAT._ATCStatus, {self}, 5, 60) - RAT.ATC.T0=timer.getTime() + + RAT.ATC.T0=timer.getTime() end + + -- Init done + RAT.ATC.init=true end --- Adds andd initializes a new flight after it was spawned. @@ -5656,7 +5753,16 @@ end -- @param #string name Group name of the flight. -- @param #string dest Name of the destination airport. function RAT:_ATCAddFlight(name, dest) - BASE:T(string.format("%sATC %s: Adding flight %s with destination %s.", RAT.id, dest, name, dest)) + -- Debug info + BASE:T(RAT.id..string.format("ATC %s: Adding flight %s with destination %s.", dest, name, dest)) + + local flight={} --#RAT.AtcFlight + flight.destination=dest + flight.Tarrive=-1 + flight.holding=-1 + flight.Tarrive=-1 + + -- Create new flight RAT.ATC.flight[name]={} RAT.ATC.flight[name].destination=dest RAT.ATC.flight[name].Tarrive=-1 @@ -5681,7 +5787,7 @@ end -- @param #string name Group name of the flight. -- @param #number time Time the fight first registered. function RAT:_ATCRegisterFlight(name, time) - BASE:T(RAT.id.."Flight ".. name.." registered at ATC for landing clearance.") + BASE:T(RAT.id..string.format("Flight %s registered at ATC for landing clearance.", name)) RAT.ATC.flight[name].Tarrive=time RAT.ATC.flight[name].holding=0 end @@ -5699,36 +5805,41 @@ function RAT:_ATCStatus() -- Holding time at destination. local hold=RAT.ATC.flight[name].holding local dest=RAT.ATC.flight[name].destination + + if RAT.ATC.airport[dest] then - if hold >= 0 then - - -- Some string whether the runway is busy or not. - local busy="Runway state is unknown" - if RAT.ATC.airport[dest].Nonfinal>0 then - busy="Runway is occupied by "..RAT.ATC.airport[dest].Nonfinal + if hold >= 0 then + + -- Some string whether the runway is busy or not. + local busy="Runway state is unknown" + if RAT.ATC.airport[dest].Nonfinal>0 then + busy="Runway is occupied by "..RAT.ATC.airport[dest].Nonfinal + else + busy="Runway is currently clear" + end + + -- Aircraft is holding. + local text=string.format("ATC %s: Flight %s is holding for %i:%02d. %s.", dest, name, hold/60, hold%60, busy) + BASE:T(RAT.id..text) + + elseif hold==RAT.ATC.onfinal then + + -- Aircarft is on final approach for landing. + local Tfinal=Tnow-RAT.ATC.flight[name].Tonfinal + + local text=string.format("ATC %s: Flight %s is on final. Waiting %i:%02d for landing event.", dest, name, Tfinal/60, Tfinal%60) + BASE:T(RAT.id..text) + + elseif hold==RAT.ATC.unregistered then + + -- Aircraft has not arrived at holding point. + --self:T(string.format("ATC %s: Flight %s is not registered yet (hold %d).", dest, name, hold)) + else - busy="Runway is currently clear" + BASE:E(RAT.id.."ERROR: Unknown holding time in RAT:_ATCStatus().") end - - -- Aircraft is holding. - local text=string.format("ATC %s: Flight %s is holding for %i:%02d. %s.", dest, name, hold/60, hold%60, busy) - BASE:T(RAT.id..text) - - elseif hold==RAT.ATC.onfinal then - - -- Aircarft is on final approach for landing. - local Tfinal=Tnow-RAT.ATC.flight[name].Tonfinal - - local text=string.format("ATC %s: Flight %s is on final. Waiting %i:%02d for landing event.", dest, name, Tfinal/60, Tfinal%60) - BASE:T(RAT.id..text) - - elseif hold==RAT.ATC.unregistered then - - -- Aircraft has not arrived at holding point. - --self:T(string.format("ATC %s: Flight %s is not registered yet (hold %d).", dest, name, hold)) - else - BASE:E(RAT.id.."ERROR: Unknown holding time in RAT:_ATCStatus().") + -- Not a RAT.ATC airport (should be managed by a FLIGHTCONTROL) end end @@ -5769,13 +5880,13 @@ function RAT:_ATCCheck() RAT.ATC.flight[flight].holding=Tnow-RAT.ATC.flight[flight].Tarrive -- Debug message. - local text=string.format("ATC %s: Flight %s runway is busy. You are #%d of %d in landing queue. Your holding time is %i:%02d.", name, flight,qID, nqueue, RAT.ATC.flight[flight].holding/60, RAT.ATC.flight[flight].holding%60) - BASE:T(RAT.id..text) + local text=string.format("ATC %s: Flight %s runway is busy. You are #%d of %d in landing queue. Your holding time is %i:%02d.", name, flight, qID, nqueue, RAT.ATC.flight[flight].holding/60, RAT.ATC.flight[flight].holding%60) + BASE:T(self.lid..text) else local text=string.format("ATC %s: Flight %s was cleared for landing. Your holding time was %i:%02d.", name, flight, RAT.ATC.flight[flight].holding/60, RAT.ATC.flight[flight].holding%60) - BASE:T(RAT.id..text) + BASE:T(self.lid..text) -- Clear flight for landing. RAT:_ATCClearForLanding(name, flight) @@ -5796,30 +5907,40 @@ end -- @param #string airport Name of destination airport. -- @param #string flight Group name of flight, which gets landing clearence. function RAT:_ATCClearForLanding(airport, flight) + -- Flight is cleared for landing. RAT.ATC.flight[flight].holding=RAT.ATC.onfinal + -- Airport runway is busy now. RAT.ATC.airport[airport].busy=true + -- Flight which is landing. RAT.ATC.airport[airport].onfinal[flight]=flight + -- Number of planes on final approach. RAT.ATC.airport[airport].Nonfinal=RAT.ATC.airport[airport].Nonfinal+1 + -- Last time an aircraft got landing clearance. RAT.ATC.airport[airport].Tlastclearance=timer.getTime() + -- Current time. RAT.ATC.flight[flight].Tonfinal=timer.getTime() + -- Set user flag to 1 ==> stop condition for holding. trigger.action.setUserFlag(flight, 1) + + -- Get flag value. local flagvalue=trigger.misc.getUserFlag(flight) -- Debug message. - local text1=string.format("ATC %s: Flight %s cleared for landing (flag=%d).", airport, flight, flagvalue) + BASE:T(RAT.id..string.format("ATC %s: Flight %s cleared for landing (flag=%d).", airport, flight, flagvalue)) + if string.find(flight,"#") then flight = string.match(flight,"^(.+)#") end - local text2=string.format("ATC %s: Flight %s you are cleared for landing.", airport, flight) - BASE:T( RAT.id..text1) - MESSAGE:New(text2, 10):ToAllIf(RAT.ATC.messages) + local text=string.format("ATC %s: Flight %s you are cleared for landing.", airport, flight) + MESSAGE:New(text, 10):ToAllIf(RAT.ATC.messages) + end --- Takes care of organisational stuff after a plane has landed. @@ -5856,17 +5977,15 @@ function RAT:_ATCFlightLanded(name) local TrafficPerHour=RAT.ATC.airport[dest].traffic/(timer.getTime()-RAT.ATC.T0)*3600 -- Debug info - local text1=string.format("ATC %s: Flight %s landed. Tholding = %i:%02d, Tfinal = %i:%02d.", dest, name, Thold/60, Thold%60, Tfinal/60, Tfinal%60) - local text2=string.format("ATC %s: Number of flights still on final %d.", dest, RAT.ATC.airport[dest].Nonfinal) - local text3=string.format("ATC %s: Traffic report: Number of planes landed in total %d. Flights/hour = %3.2f.", dest, RAT.ATC.airport[dest].traffic, TrafficPerHour) - if string.find(name,"#") then - name = string.match(name,"^(.+)#") - end - local text4=string.format("ATC %s: Flight %s landed. Welcome to %s.", dest, name, dest) - BASE:T(RAT.id..text1) - BASE:T(RAT.id..text2) - BASE:T(RAT.id..text3) - MESSAGE:New(text4, 10):ToAllIf(RAT.ATC.messages) + BASE:T(RAT.id..string.format("ATC %s: Flight %s landed. Tholding = %i:%02d, Tfinal = %i:%02d.", dest, name, Thold/60, Thold%60, Tfinal/60, Tfinal%60)) + BASE:T(RAT.id..string.format("ATC %s: Number of flights still on final %d.", dest, RAT.ATC.airport[dest].Nonfinal)) + BASE:T(RAT.id..string.format("ATC %s: Traffic report: Number of planes landed in total %d. Flights/hour = %3.2f.", dest, RAT.ATC.airport[dest].traffic, TrafficPerHour)) + + if string.find(name,"#") then + name = string.match(name,"^(.+)#") + end + local text=string.format("ATC %s: Flight %s landed. Welcome to %s.", dest, name, dest) + MESSAGE:New(text, 10):ToAllIf(RAT.ATC.messages) end end diff --git a/Moose Development/Moose/Ops/FlightControl.lua b/Moose Development/Moose/Ops/FlightControl.lua index d918956e1..4fe930d59 100644 --- a/Moose Development/Moose/Ops/FlightControl.lua +++ b/Moose Development/Moose/Ops/FlightControl.lua @@ -1063,9 +1063,10 @@ function FLIGHTCONTROL:onafterStatusUpdate() -- Check if runway was repaired. if self:IsRunwayOperational()==false then local Trepair=self:GetRunwayRepairtime() - self:I(self.lid..string.format("Runway still destroyed! Will be repaired in %d sec", Trepair)) if Trepair==0 then self:RunwayRepaired() + else + self:I(self.lid..string.format("Runway still destroyed! Will be repaired in %d sec", Trepair)) end end @@ -1835,7 +1836,7 @@ function FLIGHTCONTROL:_GetNextFightParking() local text="Parking flights:" for i,_flight in pairs(Qparking) do local flight=_flight --Ops.FlightGroup#FLIGHTGROUP - text=text..string.format("\n[%d] %s [%s], state=%s [%s]: Tparking=%.1f sec", i, flight.groupname, flight.actype, flight:GetState(), self:GetFlightStatus(flight), flight:GetParkingTime()) + text=text..string.format("\n[%d] %s [%s], state=%s [%s]: Tparking=%.1f sec", i, flight.groupname, tostring(flight.actype), flight:GetState(), self:GetFlightStatus(flight), flight:GetParkingTime()) end self:I(self.lid..text) end @@ -2131,7 +2132,7 @@ function FLIGHTCONTROL:_InitParkingSpots() local isalive=unit:IsAlive() - --env.info(string.format("FF parking spot %d is occupied by unit %s alive=%s", spot.TerminalID, unitname, tostring(isalive))) + self:T2(self.lid..string.format("FF parking spot %d is occupied by unit %s alive=%s", spot.TerminalID, unitname, tostring(isalive))) if isalive then diff --git a/Moose Development/Moose/Ops/FlightGroup.lua b/Moose Development/Moose/Ops/FlightGroup.lua index 8a0095954..364d1930d 100644 --- a/Moose Development/Moose/Ops/FlightGroup.lua +++ b/Moose Development/Moose/Ops/FlightGroup.lua @@ -786,6 +786,7 @@ function FLIGHTGROUP:SetReadyForTakeoff(ReadyTO, Delay) if Delay and Delay>0 then self:ScheduleOnce(Delay, FLIGHTGROUP.SetReadyForTakeoff, self, ReadyTO, 0) else + self:T(self.lid.."Set Ready for Takeoff switch for flightcontrol") self.isReadyTO=ReadyTO end return self From a5632ec3a4c86c778fb197b132e81aff6dad1cf9 Mon Sep 17 00:00:00 2001 From: Frank Date: Sun, 31 Mar 2024 23:33:37 +0200 Subject: [PATCH 12/26] RAT - More cleanup OPSGROUP - Fixed group init (masterunit) --- Moose Development/Moose/Core/Spawn.lua | 4 +- Moose Development/Moose/Functional/RAT.lua | 283 +++++++++++++------- Moose Development/Moose/Ops/FlightGroup.lua | 6 +- Moose Development/Moose/Ops/OpsGroup.lua | 2 +- 4 files changed, 192 insertions(+), 103 deletions(-) diff --git a/Moose Development/Moose/Core/Spawn.lua b/Moose Development/Moose/Core/Spawn.lua index 19af529b0..f1be985a6 100644 --- a/Moose Development/Moose/Core/Spawn.lua +++ b/Moose Development/Moose/Core/Spawn.lua @@ -3238,7 +3238,7 @@ end --- Get the index from a given group. -- The function will search the name of the group for a #, and will return the number behind the #-mark. function SPAWN:GetSpawnIndexFromGroup( SpawnGroup ) - self:F2( { self.SpawnTemplatePrefix, self.SpawnAliasPrefix, SpawnGroup } ) + self:F3( { self.SpawnTemplatePrefix, self.SpawnAliasPrefix, SpawnGroup } ) local IndexString = string.match( SpawnGroup:GetName(), "#(%d*)$" ):sub( 2 ) local Index = tonumber( IndexString ) @@ -3250,7 +3250,7 @@ end --- Return the last maximum index that can be used. function SPAWN:_GetLastIndex() - self:F( { self.SpawnTemplatePrefix, self.SpawnAliasPrefix } ) + self:F3( { self.SpawnTemplatePrefix, self.SpawnAliasPrefix } ) return self.SpawnMaxGroups end diff --git a/Moose Development/Moose/Functional/RAT.lua b/Moose Development/Moose/Functional/RAT.lua index e7716981b..733959754 100644 --- a/Moose Development/Moose/Functional/RAT.lua +++ b/Moose Development/Moose/Functional/RAT.lua @@ -2193,22 +2193,57 @@ function RAT:_SpawnWithRoute(_departure, _destination, _takeoff, _landing, _live -- Actually spawn the group. local group=self:SpawnWithIndex(self.SpawnIndex) -- Wrapper.Group#GROUP + -- Group name. + local groupname=group:GetName() + -- Create a flightgroup object. local flightgroup=FLIGHTGROUP:New(group) + -- Setting holding time to nil + flightgroup.holdtime=nil + + local _self=self + + --- Function called when passing a waypoint. function flightgroup.OnAfterPassingWaypoint(flightgroup, From, Event, To, Waypoint) local waypoint=Waypoint --Ops.OpsGroup#OPSGROUP.Waypoint + -- Debug info. self:T(self.lid..string.format("RAT passed waypoint %s [uid=%d]", waypoint.name, waypoint.uid)) + -- Call RAT waypoint function. + -- * calls _ATCRegisterFlight at holding waypoint ==> now in OnAfterHolding + -- * sets despawnme switch at final waypoint if landing=air + -- * sets status RAT._WaypointFunction(group, self, waypoint.uid) end - function flightgroup:OnAfterPassedFinalWaypoint(From, Event, To) - + --- Function called when passing the final waypoint + function flightgroup.OnAfterPassedFinalWaypoint(flightgroup, From, Event, To) self:T(self.lid..string.format("RAT passed FINAL waypoint")) + --TODO: Set despawnme switch if landing=air + end + --- Function called when flight is RTB. + function flightgroup.OnAfterRTB(flightgroup, From, Event, To, airbase, SpeedTo, SpeedHold, SpeedLand) + self:T(self.lid..string.format("RAT group is RTB")) + end + + --- Function called when flight is holding. + function flightgroup.OnAfterHolding(flightgroup, From,Event,To) + self:T(self.lid..string.format("RAT group is HOLDING ==> ATCRegisterFlight")) + self:_ATCRegisterFlight(groupname, timer.getTime()) + + -- Register aircraft at ATC. + if self.ATCswitch then + if self.f10menu then + MENU_MISSION_COMMAND:New("Clear for landing", self.Menu[self.SubMenuName].groups[self.SpawnIndex], self.ClearForLanding, self, groupname) + end + self:_ATCRegisterFlight(groupname, timer.getTime()) + end + + end -- Increase counter of alive groups (also uncontrolled ones). @@ -2218,9 +2253,9 @@ function RAT:_SpawnWithRoute(_departure, _destination, _takeoff, _landing, _live -- ATC is monitoring this flight (if it is supposed to land). if self.ATCswitch and landing==RAT.wp.landing then if self.returnzone then - self:_ATCAddFlight(group:GetName(), departure:GetName()) + self:_ATCAddFlight(groupname, departure:GetName()) else - self:_ATCAddFlight(group:GetName(), destination:GetName()) + self:_ATCAddFlight(groupname, destination:GetName()) end end @@ -2228,6 +2263,8 @@ function RAT:_SpawnWithRoute(_departure, _destination, _takeoff, _landing, _live if self.placemarkers then self:_PlaceMarkers(waypoints, self.SpawnIndex) end + + -- TODO: Use FLIGHTGROUP functions for invisible, immortal, etc. -- Set group to be invisible. if self.invisible then @@ -3121,14 +3158,14 @@ function RAT:_SetRoute(takeoff, landing, _departure, _destination, _waypoint) -- Holding and final destination. if landing==RAT.wp.landing then - -- Holding point - c[#c+1]=Pholding - wp[#wp+1]=self:_Waypoint(#wp+1, "Holding Point", RAT.wp.holding, c[#wp+1], VxHolding, H_holding+h_holding) - self.waypointdescriptions[#wp]="Holding Point" - self.waypointstatus[#wp]=RAT.status.Holding - wpholding=#wp + -- Holding point (removed the holding point because FLIGHTGROUP sends group to holding point with RTB command after the last waypoint) +-- c[#c+1]=Pholding +-- wp[#wp+1]=self:_Waypoint(#wp+1, "Holding Point", RAT.wp.holding, c[#wp+1], VxHolding, H_holding+h_holding) +-- self.waypointdescriptions[#wp]="Holding Point" +-- self.waypointstatus[#wp]=RAT.status.Holding +-- wpholding=#wp - -- Final destination. + -- Final destination (leave this in because FLIGHTGROUP needs to know that we want to land and removes the landing waypoint automatically) c[#c+1]=Pdestination wp[#wp+1]=self:_Waypoint(#wp+1, "Final Destination", landing, c[#wp+1], VxFinal, H_destination, destination) self.waypointdescriptions[#wp]="Final Destination" @@ -4321,29 +4358,41 @@ function RAT:_OnCrash(EventData) -- Check that the template name actually belongs to this object. if EventPrefix and EventPrefix == self.alias then - -- Update number of alive units in the group. + -- Get ratcraft object of this group. local ratcraft=self:_GetRatcraftFromGroup(SpawnGroup) - local _i=self:GetSpawnIndexFromGroup(SpawnGroup) - ratcraft.nunits=ratcraft.nunits-1 - local _n=ratcraft.nunits - local _n0=SpawnGroup:GetInitialSize() + + if ratcraft then - -- Debug info. - local text=string.format("Event: Group %s crashed. Unit %s. Units still alive %d of %d.", SpawnGroup:GetName(), EventData.IniUnitName, _n, _n0) - self:T(self.lid..text) - - -- Set status. - local status=RAT.status.EventCrash - self:_SetStatus(SpawnGroup, status) - - -- Respawn group if all units are dead. - if _n==0 and self.respawn_after_crash and not self.norespawn then - local text=string.format("No units left of group %s. Group will be respawned now.", SpawnGroup:GetName()) + -- Update number of alive units in the group. + ratcraft.nunits=ratcraft.nunits-1 + + -- Number of initial units. + local _n0=SpawnGroup:GetInitialSize() + + -- Debug info. + local text=string.format("Event: Group %s crashed. Unit %s. Units still alive %d of %d.", SpawnGroup:GetName(), EventData.IniUnitName, ratcraft.nunits, _n0) self:T(self.lid..text) - -- Respawn group. - local idx=self:GetSpawnIndexFromGroup(SpawnGroup) - local coord=SpawnGroup:GetCoordinate() - self:_Respawn(idx, coord) + + -- Set status. + local status=RAT.status.EventCrash + self:_SetStatus(SpawnGroup, status) + + -- Respawn group if all units are dead. + if ratcraft.nunits==0 and self.respawn_after_crash and not self.norespawn then + + -- Debug info. + self:T(self.lid..string.format("No units left of group %s. Group will be respawned now.", SpawnGroup:GetName())) + + -- Get spawn index + local idx=self:GetSpawnIndexFromGroup(SpawnGroup) + local coord=SpawnGroup:GetCoordinate() + + -- Respawn group. + self:_Respawn(idx, coord) + end + + else + self:E(self.lid..string.format("ERROR: Could not find ratcraft object for crashed group %s!", SpawnGroup:GetName())) end end @@ -4784,7 +4833,9 @@ function RAT._WaypointFunction(group, rat, wp) -- Aircraft arrived at holding point text=string.format("Flight %s to %s ATC: Holding and awaiting landing clearance.", group:GetName(), destination) + rat:T(rat.lid..text) MESSAGE:New(text, 10):ToAllIf(rat.reportstatus) + -- Register aircraft at ATC. if rat.ATCswitch then @@ -5700,6 +5751,7 @@ end -- @field #boolean busy Whether airport is busy. -- @field #table onfinal List of flights on final. -- @field #number Nonfinal Number of flights on final. +-- @field #number traffic Number of flights that landed (just for stats). -- @field #number Tlastclearance Time stamp when last flight started final approach. --- Data structure a RAT ATC airbase object. @@ -5717,7 +5769,7 @@ function RAT:_ATCInit(airports_map) if not RAT.ATC.init then local text="Starting RAT ATC.\nSimultanious = "..RAT.ATC.Nclearance.."\n".."Delay = "..RAT.ATC.delay - BASE:T(RAT.id..text) + BASE:I(RAT.id..text) for _,ap in pairs(airports_map) do local airbase=ap --Wrapper.Airbase#AIRBASE @@ -5727,6 +5779,14 @@ function RAT:_ATCInit(airports_map) if not fc then + local airport={} --#RAT.AtcAirport + airport.queue={} + airport.busy=false + airport.onfinal={} + airport.Nonfinal=0 + airport.traffic=0 + airport.Tlastclearance=nil + RAT.ATC.airport[name]={} RAT.ATC.airport[name].queue={} RAT.ATC.airport[name].busy=false @@ -5754,7 +5814,7 @@ end -- @param #string dest Name of the destination airport. function RAT:_ATCAddFlight(name, dest) -- Debug info - BASE:T(RAT.id..string.format("ATC %s: Adding flight %s with destination %s.", dest, name, dest)) + BASE:I(RAT.id..string.format("ATC %s: Adding flight %s with destination %s.", dest, name, dest)) local flight={} --#RAT.AtcFlight flight.destination=dest @@ -5787,7 +5847,7 @@ end -- @param #string name Group name of the flight. -- @param #number time Time the fight first registered. function RAT:_ATCRegisterFlight(name, time) - BASE:T(RAT.id..string.format("Flight %s registered at ATC for landing clearance.", name)) + BASE:I(RAT.id..string.format("Flight %s registered at ATC for landing clearance.", name)) RAT.ATC.flight[name].Tarrive=time RAT.ATC.flight[name].holding=0 end @@ -5800,27 +5860,30 @@ function RAT:_ATCStatus() -- Current time. local Tnow=timer.getTime() - for name,_ in pairs(RAT.ATC.flight) do + for name,_flight in pairs(RAT.ATC.flight) do + local flight=_flight --#RAT.AtcFlight -- Holding time at destination. local hold=RAT.ATC.flight[name].holding local dest=RAT.ATC.flight[name].destination - if RAT.ATC.airport[dest] then + local airport=RAT.ATC.airport[dest] --#RAT.AtcAirport + + if airport then if hold >= 0 then -- Some string whether the runway is busy or not. local busy="Runway state is unknown" - if RAT.ATC.airport[dest].Nonfinal>0 then - busy="Runway is occupied by "..RAT.ATC.airport[dest].Nonfinal + if airport.Nonfinal>0 then + busy="Runway is occupied by "..airport.Nonfinal else busy="Runway is currently clear" end -- Aircraft is holding. local text=string.format("ATC %s: Flight %s is holding for %i:%02d. %s.", dest, name, hold/60, hold%60, busy) - BASE:T(RAT.id..text) + BASE:I(RAT.id..text) elseif hold==RAT.ATC.onfinal then @@ -5828,7 +5891,7 @@ function RAT:_ATCStatus() local Tfinal=Tnow-RAT.ATC.flight[name].Tonfinal local text=string.format("ATC %s: Flight %s is on final. Waiting %i:%02d for landing event.", dest, name, Tfinal/60, Tfinal%60) - BASE:T(RAT.id..text) + BASE:I(RAT.id..text) elseif hold==RAT.ATC.unregistered then @@ -5850,46 +5913,51 @@ end function RAT:_ATCCheck() -- Init queue of flights at all airports. + -- TODO: Global function RAT:_ATCQueue() -- Current time. local Tnow=timer.getTime() - for name,_ in pairs(RAT.ATC.airport) do + for airportname,_airport in pairs(RAT.ATC.airport) do + local airport=_airport --#RAT.AtcAirport - for qID,flight in ipairs(RAT.ATC.airport[name].queue) do + for qID,flightname in pairs(airport.queue) do + local flight=RAT.ATC.flight[flightname] --#RAT.AtcFlight -- Number of aircraft in queue. - local nqueue=#RAT.ATC.airport[name].queue + local nqueue=#airport.queue -- Conditions to clear an aircraft for landing - local landing1 - if RAT.ATC.airport[name].Tlastclearance then + local landing1=false + if airport.Tlastclearance then -- Landing if time is enough and less then two planes are on final. - landing1=(Tnow-RAT.ATC.airport[name].Tlastclearance > RAT.ATC.delay) and RAT.ATC.airport[name].Nonfinal < RAT.ATC.Nclearance - else - landing1=false + landing1=(Tnow-airport.Tlastclearance > RAT.ATC.delay) and airport.Nonfinal < RAT.ATC.Nclearance end + -- No other aircraft is on final. - local landing2=RAT.ATC.airport[name].Nonfinal==0 + local landing2=airport.Nonfinal==0 if not landing1 and not landing2 then -- Update holding time. - RAT.ATC.flight[flight].holding=Tnow-RAT.ATC.flight[flight].Tarrive + flight.holding=Tnow-flight.Tarrive -- Debug message. - local text=string.format("ATC %s: Flight %s runway is busy. You are #%d of %d in landing queue. Your holding time is %i:%02d.", name, flight, qID, nqueue, RAT.ATC.flight[flight].holding/60, RAT.ATC.flight[flight].holding%60) - BASE:T(self.lid..text) + local text=string.format("ATC %s: Flight %s runway is busy. You are #%d of %d in landing queue. Your holding time is %i:%02d.", + airportname, flightname, qID, nqueue, flight.holding/60, flight.holding%60) + BASE:I(self.lid..text) else - local text=string.format("ATC %s: Flight %s was cleared for landing. Your holding time was %i:%02d.", name, flight, RAT.ATC.flight[flight].holding/60, RAT.ATC.flight[flight].holding%60) - BASE:T(self.lid..text) + local text=string.format("ATC %s: Flight %s was cleared for landing. Your holding time was %i:%02d.", + airportname, flightname, flight.holding/60, flight.holding%60) + BASE:I(self.lid..text) -- Clear flight for landing. - RAT:_ATCClearForLanding(name, flight) + -- TODO: Global function + RAT:_ATCClearForLanding(airportname, flightname) end @@ -5898,49 +5966,63 @@ function RAT:_ATCCheck() end -- Update queue of flights at all airports. + -- TODO: Global function RAT:_ATCQueue() end --- Giving landing clearance for aircraft by setting user flag. -- @param #RAT self --- @param #string airport Name of destination airport. --- @param #string flight Group name of flight, which gets landing clearence. -function RAT:_ATCClearForLanding(airport, flight) +-- @param #string airportname Name of destination airport. +-- @param #string flightname Group name of flight, which gets landing clearence. +function RAT:_ATCClearForLanding(airportname, flightname) - -- Flight is cleared for landing. - RAT.ATC.flight[flight].holding=RAT.ATC.onfinal + -- Find FLIGHTGROUP in database. + local flightgroup=_DATABASE:FindOpsGroup(flightname) --Ops.FlightGroup#FLIGHTGROUP - -- Airport runway is busy now. - RAT.ATC.airport[airport].busy=true + if flightgroup then - -- Flight which is landing. - RAT.ATC.airport[airport].onfinal[flight]=flight - - -- Number of planes on final approach. - RAT.ATC.airport[airport].Nonfinal=RAT.ATC.airport[airport].Nonfinal+1 - - -- Last time an aircraft got landing clearance. - RAT.ATC.airport[airport].Tlastclearance=timer.getTime() - - -- Current time. - RAT.ATC.flight[flight].Tonfinal=timer.getTime() - - -- Set user flag to 1 ==> stop condition for holding. - trigger.action.setUserFlag(flight, 1) - - -- Get flag value. - local flagvalue=trigger.misc.getUserFlag(flight) + -- Give clear to land signal. + flightgroup:ClearToLand() + + + local flight=RAT.ATC.flight[flightname] --#RAT.AtcFlight + + -- Flight is cleared for landing. + flight.holding=RAT.ATC.onfinal + + -- Current time. + flight.Tonfinal=timer.getTime() - -- Debug message. - BASE:T(RAT.id..string.format("ATC %s: Flight %s cleared for landing (flag=%d).", airport, flight, flagvalue)) - if string.find(flight,"#") then - flight = string.match(flight,"^(.+)#") + local airport=RAT.ATC.airport[airportname] --#RAT.AtcAirport + + -- Airport runway is busy now. + airport.busy=true + + -- Flight which is landing. + airport.onfinal[flight]=flight + + -- Number of planes on final approach. + airport.Nonfinal=airport.Nonfinal+1 + + -- Last time an aircraft got landing clearance. + airport.Tlastclearance=timer.getTime() + + + -- Debug message. + BASE:I(RAT.id..string.format("ATC %s: Flight %s cleared for landing", airportname, flightname)) + + if string.find(flight,"#") then + flight = string.match(flight,"^(.+)#") + end + local text=string.format("ATC %s: Flight %s you are cleared for landing.", airportname, flightname) + MESSAGE:New(text, 10):ToAllIf(RAT.ATC.messages) + + else + BASE:E("Could not clear flight for landing!") end - local text=string.format("ATC %s: Flight %s you are cleared for landing.", airport, flight) - MESSAGE:New(text, 10):ToAllIf(RAT.ATC.messages) - + end --- Takes care of organisational stuff after a plane has landed. @@ -5948,38 +6030,43 @@ end -- @param #string name Group name of flight. function RAT:_ATCFlightLanded(name) - if RAT.ATC.flight[name] then + local flight=RAT.ATC.flight[name] --#RAT.AtcFlight + + if flight then -- Destination airport. - local dest=RAT.ATC.flight[name].destination + local dest=flight.destination -- Times for holding and final approach. local Tnow=timer.getTime() - local Tfinal=Tnow-RAT.ATC.flight[name].Tonfinal - local Thold=RAT.ATC.flight[name].Tonfinal-RAT.ATC.flight[name].Tarrive + local Tfinal=Tnow-flight.Tonfinal + local Thold=flight.Tonfinal-flight.Tarrive + + local airport=RAT.ATC.airport[dest] --#RAT.AtcAirport -- Airport is not busy any more. - RAT.ATC.airport[dest].busy=false + airport.busy=false -- No aircraft on final any more. - RAT.ATC.airport[dest].onfinal[name]=nil + airport.onfinal[name]=nil -- Decrease number of aircraft on final. - RAT.ATC.airport[dest].Nonfinal=RAT.ATC.airport[dest].Nonfinal-1 + airport.Nonfinal=airport.Nonfinal-1 -- Remove this flight from list of flights. + -- TODO: Global function RAT:_ATCDelFlight(RAT.ATC.flight, name) -- Increase landing counter to monitor traffic. - RAT.ATC.airport[dest].traffic=RAT.ATC.airport[dest].traffic+1 + airport.traffic=airport.traffic+1 -- Number of planes landing per hour. - local TrafficPerHour=RAT.ATC.airport[dest].traffic/(timer.getTime()-RAT.ATC.T0)*3600 + local TrafficPerHour=aiport.traffic/(timer.getTime()-RAT.ATC.T0)*3600 -- Debug info - BASE:T(RAT.id..string.format("ATC %s: Flight %s landed. Tholding = %i:%02d, Tfinal = %i:%02d.", dest, name, Thold/60, Thold%60, Tfinal/60, Tfinal%60)) - BASE:T(RAT.id..string.format("ATC %s: Number of flights still on final %d.", dest, RAT.ATC.airport[dest].Nonfinal)) - BASE:T(RAT.id..string.format("ATC %s: Traffic report: Number of planes landed in total %d. Flights/hour = %3.2f.", dest, RAT.ATC.airport[dest].traffic, TrafficPerHour)) + BASE:I(RAT.id..string.format("ATC %s: Flight %s landed. Tholding = %i:%02d, Tfinal = %i:%02d.", dest, name, Thold/60, Thold%60, Tfinal/60, Tfinal%60)) + BASE:I(RAT.id..string.format("ATC %s: Number of flights still on final %d.", dest, airport.Nonfinal)) + BASE:I(RAT.id..string.format("ATC %s: Traffic report: Number of planes landed in total %d. Flights/hour = %3.2f.", dest, airport.traffic, TrafficPerHour)) if string.find(name,"#") then name = string.match(name,"^(.+)#") diff --git a/Moose Development/Moose/Ops/FlightGroup.lua b/Moose Development/Moose/Ops/FlightGroup.lua index 364d1930d..953b3e5ac 100644 --- a/Moose Development/Moose/Ops/FlightGroup.lua +++ b/Moose Development/Moose/Ops/FlightGroup.lua @@ -59,6 +59,7 @@ -- @field #boolean prohibitAB Disallow (true) or allow (false) AI to use the afterburner. -- @field #boolean jettisonEmptyTanks Allow (true) or disallow (false) AI to jettison empty fuel tanks. -- @field #boolean jettisonWeapons Allow (true) or disallow (false) AI to jettison weapons if in danger. +-- @field #number holdtime Time [s] flight is holding before going on final. Set to nil for indefinitely. -- -- @extends Ops.OpsGroup#OPSGROUP @@ -273,6 +274,7 @@ function FLIGHTGROUP:New(group) -- Holding flag. self.flaghold=USERFLAG:New(string.format("%s_FlagHold", self.groupname)) self.flaghold:Set(0) + self.holdtime=2*60 -- Add FSM transitions. -- From State --> Event --> To State @@ -2098,7 +2100,7 @@ function FLIGHTGROUP:onafterSpawned(From, Event, To) -- 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("Unit type = %s\n", tostring(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)) @@ -3199,7 +3201,7 @@ function FLIGHTGROUP:_LandAtAirbase(airbase, SpeedTo, SpeedHold, SpeedLand) self.flaghold:Set(0) -- Set holding time. - local holdtime=2*60 + local holdtime=self.holdtime if fc or self.airboss then holdtime=nil end diff --git a/Moose Development/Moose/Ops/OpsGroup.lua b/Moose Development/Moose/Ops/OpsGroup.lua index 0233efc00..3d5d088d0 100644 --- a/Moose Development/Moose/Ops/OpsGroup.lua +++ b/Moose Development/Moose/Ops/OpsGroup.lua @@ -597,7 +597,7 @@ function OPSGROUP:New(group) if units then local masterunit=units[1] --Wrapper.Unit#UNIT - if unit then + if masterunit then -- Get Descriptors. self.descriptors=masterunit:GetDesc() From 1fdb3b7daa949ae858c8ecc335d5838837ba5be7 Mon Sep 17 00:00:00 2001 From: Frank Date: Tue, 2 Apr 2024 17:33:41 +0200 Subject: [PATCH 13/26] RAT - Improved respawn/despawn - Added stuck check --- Moose Development/Moose/Functional/RAT.lua | 569 ++++++++------------ Moose Development/Moose/Ops/FlightGroup.lua | 113 +++- Moose Development/Moose/Ops/OpsGroup.lua | 9 +- Moose Development/Moose/Wrapper/Group.lua | 5 +- 4 files changed, 332 insertions(+), 364 deletions(-) diff --git a/Moose Development/Moose/Functional/RAT.lua b/Moose Development/Moose/Functional/RAT.lua index 733959754..4c8fca65e 100644 --- a/Moose Development/Moose/Functional/RAT.lua +++ b/Moose Development/Moose/Functional/RAT.lua @@ -43,8 +43,6 @@ -- -- ### Author: **funkyfranky** -- --- ### Contributions: FlightControl --- -- === -- @module Functional.RAT -- @image RAT.JPG @@ -589,6 +587,8 @@ RAT.version={ ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --TODO list: +--TODO: Add max number of spawns +--TODO: Add Stop function --TODO: Integrate FLIGHTGROUP --DONE: Add scheduled spawn. --DONE: Add possibility to spawn in air. @@ -635,7 +635,6 @@ RAT.version={ -- @usage yak1:RAT("RAT_YAK") will create a RAT object called "yak1". The template group in the mission editor must have the name "RAT_YAK". -- @usage yak2:RAT("RAT_YAK", "Yak2") will create a RAT object "yak2". The template group in the mission editor must have the name "RAT_YAK" but the group will be called "Yak2" in e.g. the F10 menu. function RAT:New(groupname, alias) - BASE:F({groupname=groupname, alias=alias}) -- Inherit SPAWN class. self=BASE:Inherit(self, SPAWN:NewWithAlias(groupname, alias)) -- #RAT @@ -685,6 +684,12 @@ function RAT:New(groupname, alias) return self end +--- Stop RAT spawning by unhandling events, stoping schedulers etc. +-- @param #RAT self +function RAT:Stop() + -- TODO +end + ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Spawn function ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -880,6 +885,8 @@ function RAT:Spawn(naircraft) -- Start scheduled spawning. SCHEDULER:New(nil, self._SpawnWithRoute, {self}, Tstart, dt, 0.0, Tstop) + + --self.sid_spawn=self.Scheduler:Schedule(MasterObject,SchedulerFunction,SchedulerArguments,Start,Repeat,RandomizeFactor,Stop,TraceLevel,Fsm) -- Start scheduled activation of uncontrolled groups. if self.uncontrolled and self.activate_uncontrolled then @@ -914,7 +921,7 @@ function RAT:_CheckConsistency() -- Only zones but not takeoff air == > Enable takeoff air. if self.Ndeparture_Zones>0 and self.takeoff~=RAT.wp.air then self.takeoff=RAT.wp.air - self:E(self.lid..string.format("ERROR: At least one zone defined as departure and takeoff is NOT set to air. Enabling air start for RAT group %s!", self.alias)) + self:E(self.lid..string.format("WARNING: At least one zone defined as departure and takeoff is NOT set to air. Enabling air start for RAT group %s!", self.alias)) end -- No airport and no zone specified. if self.Ndeparture_Airports==0 and self.Ndeparture_Zone==0 then @@ -942,7 +949,7 @@ function RAT:_CheckConsistency() if self.Ndestination_Zones>0 and self.landing~=RAT.wp.air and not self.returnzone then self.landing=RAT.wp.air self.destinationzone=true - self:E(self.lid.."ERROR: At least one zone defined as destination and landing is NOT set to air. Enabling destination zone!") + self:E(self.lid.."WARNING: At least one zone defined as destination and landing is NOT set to air. Enabling destination zone!") end -- No specified airport and no zone found at all. if self.Ndestination_Airports==0 and self.Ndestination_Zones==0 then @@ -1525,7 +1532,8 @@ function RAT:SetMaxRespawnTriedWhenSpawnedOnRunway(n) return self end ---- Aircraft will be respawned directly after take-off. +--- A new aircraft is spawned directly after the last one took off. This creates a lot of outbound traffic. Aircraft are not respawned after they reached their destination. +-- Therefore, this option is not to be used with the "commute" or "continue journey" options. -- @param #RAT self -- @return #RAT RAT self object. function RAT:RespawnAfterTakeoff() @@ -1600,15 +1608,6 @@ function RAT:CheckOnTop(switch, radius) return self end ---- Put parking spot coordinates in a data base for future use of aircraft. (Obsolete! API function will be removed soon.) --- @param #RAT self --- @param #boolean switch If true, parking spots are memorized. This is also the default setting. --- @return #RAT RAT self object. -function RAT:ParkingSpotDB(switch) - self:E("RAT ParkingSpotDB function is obsolete and will be removed soon!") - return self -end - --- Enable Radio. Overrules the ME setting. -- @param #RAT self -- @return #RAT RAT self object. @@ -2199,23 +2198,33 @@ function RAT:_SpawnWithRoute(_departure, _destination, _takeoff, _landing, _live -- Create a flightgroup object. local flightgroup=FLIGHTGROUP:New(group) - -- Setting holding time to nil + -- Setting holding time to nil so that flight never gets landing clearance. + -- TODO: Make this dependent on ATC switch. flightgroup.holdtime=nil - local _self=self + -- No automatic despawning if group gets stuck. + flightgroup.stuckDespawn=false + --- Function called when passing a waypoint. - function flightgroup.OnAfterPassingWaypoint(flightgroup, From, Event, To, Waypoint) + function flightgroup.OnAfterPassingWaypoint(Flightgroup, From, Event, To, Waypoint) local waypoint=Waypoint --Ops.OpsGroup#OPSGROUP.Waypoint + local flightgroup=Flightgroup --Ops.FlightGroup#FLIGHTGROUP + + local wp=waypoint.uid -- Debug info. - self:T(self.lid..string.format("RAT passed waypoint %s [uid=%d]", waypoint.name, waypoint.uid)) + self:T(self.lid..string.format("RAT passed waypoint %s [uid=%d]: %s", waypoint.name, waypoint.uid, tostring(self.waypointdescriptions[wp]))) -- Call RAT waypoint function. -- * calls _ATCRegisterFlight at holding waypoint ==> now in OnAfterHolding -- * sets despawnme switch at final waypoint if landing=air -- * sets status RAT._WaypointFunction(group, self, waypoint.uid) + + if waypoint.uid==3 then + --flightgroup:SelfDestruction(Delay,ExplosionPower,ElementName) + end end @@ -2241,9 +2250,25 @@ function RAT:_SpawnWithRoute(_departure, _destination, _takeoff, _landing, _live MENU_MISSION_COMMAND:New("Clear for landing", self.Menu[self.SubMenuName].groups[self.SpawnIndex], self.ClearForLanding, self, groupname) end self:_ATCRegisterFlight(groupname, timer.getTime()) - end - - + end + end + + function flightgroup.OnAfterLandAtAirbase(Flightgroup, From, Event, To, Airport) + self:T(self.lid..string.format("RAT group landed at airbase")) + end + + function flightgroup.OnAfterArrived(Flightgroup, From, Event, To) + self:T(self.lid..string.format("RAT group arrived")) + end + + --- Function called when a group got stuck. + function flightgroup.OnAfterStuck(Flightgroup, From, Event, To, Stucktime) + local flightgroup=Flightgroup --Ops.FlightGroup#FLIGHTGROUP + self:T(self.lid..string.format("Group %s got stuck for %d seconds", flightgroup:GetName(), Stucktime)) + if Stucktime>10*60 then + self:_Respawn(flightgroup.group) + end + end -- Increase counter of alive groups (also uncontrolled ones). @@ -2297,24 +2322,8 @@ function RAT:_SpawnWithRoute(_departure, _destination, _takeoff, _landing, _live ratcraft.waypoints=waypoints ratcraft.airborne=group:InAir() ratcraft.nunits=group:GetInitialSize() - -- Time and position on ground. For check if aircraft is stuck somewhere. - if group:InAir() then - ratcraft.Tground=nil - ratcraft.Pground=nil - ratcraft.Uground=nil - ratcraft.Tlastcheck=nil - else - ratcraft.Tground=timer.getTime() - ratcraft.Pground=group:GetCoordinate() - ratcraft.Uground={} - for _,_unit in pairs(group:GetUnits()) do - local _unitname=_unit:GetName() - ratcraft.Uground[_unitname]=_unit:GetCoordinate() - end - ratcraft.Tlastcheck=timer.getTime() - end + -- Initial and current position. For calculating the travelled distance. - ratcraft.P0=group:GetCoordinate() ratcraft.Pnow=group:GetCoordinate() ratcraft.Distance=0 @@ -2376,26 +2385,34 @@ function RAT:ClearForLanding(name) self:T(self.lid.."ATC: User flag value (landing) for "..name.." set to "..flagvalue) end ---- Respawn a group. +--- Respawn a group. The original group is despawned and a new group is spawed. -- @param #RAT self --- @param #number index Spawn index. +-- @param Wrapper.Group#GROUP group The group that should be respawned. -- @param Core.Point#COORDINATE lastpos Last known position of the group. --- @param #number delay Delay before respawn -function RAT:_Respawn(index, lastpos, delay) +-- @param #number delay Delay before respawn in seconds. +function RAT:_Respawn(group, lastpos, delay) if delay and delay>0 then - self:ScheduleOnce(delay, RAT._Respawn, self, index, lastpos, 0) + self:ScheduleOnce(delay, RAT._Respawn, self, group, lastpos, 0) else - self:T(self.lid..string.format("Respawning ratcraft with index=%d", index)) - - -- Ratcraft object - local ratcraft=self.ratcraft[index] --#RAT.RatCraft + if group then + self:T(self.lid..string.format("Respawning ratcraft from group %s", group:GetName())) + else + self:E(self.lid..string.format("ERROR: group is nil in _Respawn!")) + return nil + end + + -- Get ratcraft from group. + local ratcraft=self:_GetRatcraftFromGroup(group) + + -- Get last known position. + lastpos=lastpos or group:GetCoordinate() -- Get departure and destination from previous journey. local departure=ratcraft.departure - local destination=ratcraft.destination + local destination=ratcraft.destination --Wrapper.Airbase#AIRBASE local takeoff=ratcraft.takeoff local landing=ratcraft.landing local livery=ratcraft.livery @@ -2403,35 +2420,31 @@ function RAT:_Respawn(index, lastpos, delay) local flightgroup=ratcraft.flightgroup + -- In case we stay at the same airport, we save the parking data to respawn at the same spot. local parkingdata=nil - for _,_element in pairs(flightgroup.elements) do - local element=_element --Ops.OpsGroup#OPSGROUP.Element + if self.continuejourney or self.commute then + for _,_element in pairs(flightgroup.elements) do + local element=_element --Ops.OpsGroup#OPSGROUP.Element + + if element.parking then + -- Init table. + if parkingdata==nil then + parkingdata={} + end - if element.parking then - -- Init table. - if parkingdata==nil then - parkingdata={} + self:T(self.lid..string.format("Element %s was parking at spot id=%d", element.name, element.parking.TerminalID)) + table.insert(parkingdata, UTILS.DeepCopy(element.parking)) + else + self:E(self.lid..string.format("WARNING: Element %s did NOT have a not parking spot!", tostring(element.name))) end - - self:T(self.lid..string.format("Element %s was parking at spot id=%d", element.name, element.parking.TerminalID)) - table.insert(parkingdata, UTILS.DeepCopy(element.parking)) - else - self:E(self.lid..string.format("WARNING: Element %s did NOT have a not parking spot!", tostring(element.name))) end end - - -- Despawn flight group - flightgroup:Despawn(0, true) - flightgroup:__Stop(0.1) - - -- This is usually done in _Despawn - ratcraft.group=nil - ratcraft.status="Dead" - self.ratcraft[index]=nil + -- Despawn old group. + self:_Despawn(ratcraft.group) local _departure=nil - local _destination=nil + local _destination=nil --Wrapper.Airbase#AIRBASE local _takeoff=nil local _landing=nil local _livery=nil @@ -2585,6 +2598,76 @@ function RAT:_Respawn(index, lastpos, delay) end +--- Despawn group. The `FLIGHTGROUP` is despawned and stopped. The ratcraft is removed from the self.ratcraft table. Menues are removed. +-- @param #RAT self +-- @param Wrapper.Group#GROUP group Group to be despawned. +-- @param #number delay Delay in seconds before the despawn happens. Default is `self.respawn_delay`. +function RAT:_Despawn(group, delay) + + delay=delay or self.respawn_delay + + if delay and delay>0 then + -- Delayed call. + self:ScheduleOnce(delay, RAT._Despawn, self, group, 0) + else + + if group then + + -- Get spawnindex of group. + local index=self:GetSpawnIndexFromGroup(group) + + if index then + + -- Debug info. + self:T(self.lid..string.format("Despawning group %s (index=%d)", group:GetName(), index)) + + -- Get ratcraft. + local ratcraft=self.ratcraft[index] --#RAT.RatCraft + + self.ratcraft[index].group=nil + self.ratcraft[index]["status"]="Dead" + + --TODO: Maybe here could be some more arrays deleted? Somehow this causes issues! + --[[ + --self.ratcraft[index]["group"]=group + self.ratcraft[index]["destination"]=nil + self.ratcraft[index]["departure"]=nil + self.ratcraft[index]["waypoints"]=nil + self.ratcraft[index]["airborne"]=nil + self.ratcraft[index]["Tground"]=nil + self.ratcraft[index]["Pground"]=nil + self.ratcraft[index]["Tlastcheck"]=nil + self.ratcraft[index]["P0"]=nil + self.ratcraft[index]["Pnow"]=nil + self.ratcraft[index]["Distance"]=nil + self.ratcraft[index].takeoff=nil + self.ratcraft[index].landing=nil + self.ratcraft[index].wpholding=nil + self.ratcraft[index].wpfinal=nil + self.ratcraft[index].active=false + self.ratcraft[index]["status"]=nil + self.ratcraft[index].livery=nil + self.ratcraft[index].despawnme=nil + self.ratcraft[index].nrespawn=nil + ]] + + --ratcraft.flightgroup:Destroy(0) + ratcraft.flightgroup:Despawn() + ratcraft.flightgroup:__Stop(0.1) + + + self.ratcraft[index]=nil + + -- Remove submenu for this group. + if self.f10menu and self.SubMenuName ~= nil then + self.Menu[self.SubMenuName]["groups"][index]:Remove() + end + + end + end + end +end + ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- Set the route of the AI plane. Due to DCS landing bug, this has to be done before the unit is spawned. @@ -3582,14 +3665,6 @@ function RAT:Status(message, forID) self:T(self.lid.."Checking status") - -- Optional arguments. - if message==nil then - message=false - end - if forID==nil then - forID=false - end - -- Current time. local Tnow=timer.getTime() @@ -3597,14 +3672,15 @@ function RAT:Status(message, forID) local nalive=0 -- Loop over all ratcraft. - for spawnindex,ratcraft in pairs(self.ratcraft) do + for spawnindex,_ratcraft in pairs(self.ratcraft) do + local ratcraft=_ratcraft --#RAT.RatCraft self:T(self.lid..string.format("Ratcraft Index=%s", tostring(spawnindex))) -- Get group. local group=ratcraft.group --Wrapper.Group#GROUP - if group and group:IsAlive() and (group:GetCoordinate() or group:GetVec3()) then + if group and group:IsAlive() then nalive=nalive+1 self:T(self.lid..string.format("Ratcraft Index=%s is ALIVE", tostring(spawnindex))) @@ -3614,104 +3690,26 @@ function RAT:Status(message, forID) local life=self:_GetLife(group) local fuel=group:GetFuel()*100.0 local airborne=group:InAir() - local coords=group:GetCoordinate() or group:GetVec3() - local alt=1000 - if coords then - alt=coords.y or 1000 - end - --local vel=group:GetVelocityKMH() + local coords=group:GetCoordinate() + local alt=coords~=nil and coords.y or 1000 local departure=ratcraft.departure:GetName() local destination=ratcraft.destination:GetName() local type=self.aircraft.type local status=ratcraft.status local active=ratcraft.active - local Nunits=ratcraft.nunits -- group:GetSize() + local Nunits=ratcraft.nunits local N0units=group:GetInitialSize() - -- Monitor time and distance on ground. - local Tg=0 - local Dg=0 - local dTlast=0 - local stationary=false --lets assume, we did move - if airborne then - -- Aircraft is airborne. - ratcraft["Tground"]=nil - ratcraft["Pground"]=nil - ratcraft["Uground"]=nil - ratcraft["Tlastcheck"]=nil - else - --Aircraft is on ground. - if ratcraft["Tground"] then - -- Aircraft was already on ground. Calculate total time on ground. - Tg=Tnow-ratcraft["Tground"] - - -- Distance on ground since last check. - Dg=coords:Get2DDistance(ratcraft["Pground"]) - - -- Time interval since last check. - dTlast=Tnow-ratcraft["Tlastcheck"] - - -- If more than Tinactive seconds passed since last check ==> check how much we moved meanwhile. - if dTlast > self.Tinactive then - - --[[ - if Dg<50 and active and status~=RAT.status.EventBirth then - stationary=true - end - ]] - - -- Loop over all units. - for _,_unit in pairs(group:GetUnits()) do - - if _unit and _unit:IsAlive() then - - -- Unit name, coord and distance since last check. - local unitname=_unit:GetName() - local unitcoord=_unit:GetCoordinate() - local Ug=unitcoord:Get2DDistance(ratcraft.Uground[unitname]) - - -- Debug info - self:T2(self.lid..string.format("Unit %s travelled distance on ground %.1f m since %d seconds.", unitname, Ug, dTlast)) - - -- If aircraft did not move more than 50 m since last check, we call it stationary and despawn it. - -- Aircraft which are spawned uncontrolled or starting their engines are not counted. - if Ug<50 and active and status~=RAT.status.EventBirth then - stationary=true - end - - -- Update coords. - ratcraft["Uground"][unitname]=unitcoord - end - end - - -- Set the current time to know when the next check is necessary. - ratcraft["Tlastcheck"]=Tnow - ratcraft["Pground"]=coords - end - - else - -- First time we see that the aircraft is on ground. Initialize the times and position. - ratcraft["Tground"]=Tnow - ratcraft["Tlastcheck"]=Tnow - ratcraft["Pground"]=coords - ratcraft["Uground"]={} - for _,_unit in pairs(group:GetUnits()) do - local unitname=_unit:GetName() - ratcraft.Uground[unitname]=_unit:GetCoordinate() - end - end - end - -- Monitor travelled distance since last check. - local Pn=coords - local Dtravel=Pn:Get2DDistance(ratcraft["Pnow"]) - ratcraft["Pnow"]=Pn + local Pnow=coords + local Dtravel=Pnow:Get2DDistance(ratcraft.Pnow) + ratcraft.Pnow=Pnow -- Add up the travelled distance. - ratcraft["Distance"]=ratcraft["Distance"]+Dtravel + ratcraft.Distance=ratcraft.Distance+Dtravel -- Distance remaining to destination. - local Ddestination=Pn:Get2DDistance(ratcraft.destination:GetCoordinate()) + local Ddestination=Pnow:Get2DDistance(ratcraft.destination:GetCoordinate()) -- Status report. if (forID and spawnindex==forID) or (not forID) then @@ -3734,60 +3732,36 @@ function RAT:Status(message, forID) else text=text.." [on ground]\n" end - text=text..string.format("Fuel = %3.0f %%\n", fuel) - text=text..string.format("Life = %3.0f %%\n", life) - text=text..string.format("FL%03d = %i m ASL\n", alt/RAT.unit.FL2m, alt) - --text=text..string.format("Speed = %i km/h\n", vel) - text=text..string.format("Distance travelled = %6.1f km\n", ratcraft["Distance"]/1000) - text=text..string.format("Distance to destination = %6.1f km", Ddestination/1000) - if not airborne then - text=text..string.format("\nTime on ground = %6.0f seconds\n", Tg) - text=text..string.format("Position change = %8.1f m since %3.0f seconds.", Dg, dTlast) - end + text=text..string.format("Fuel = %3.0f %%\n", fuel) + text=text..string.format("Life = %3.0f %%\n", life) + text=text..string.format("FL%03d = %i m ASL\n", alt/RAT.unit.FL2m, alt) + text=text..string.format("Distance travelled = %6.1f km\n", ratcraft["Distance"]/1000) + text=text..string.format("Distance to dest = %6.1f km", Ddestination/1000) self:T(self.lid..text) if message then MESSAGE:New(text, 20):ToAll() end end - -- Despawn groups if they are on ground and don't move or are damaged. - if not airborne then - - -- Despawn unit if it did not move more then 50 m in the last 180 seconds. - if stationary then - local text=string.format("Group %s is despawned after being %d seconds inaktive on ground.", self.alias, dTlast) - self:T(self.lid..text) - self:_Despawn(group) - end - - -- Despawn group if life is < 10% and distance travelled < 100 m. - if life<10 and Dtravel<100 then - local text=string.format("Damaged group %s is despawned. Life = %3.0f", self.alias, life) - self:T(self.lid..text) - self:_Despawn(group) - end - - end - -- Despawn groups after they have reached their destination zones. if ratcraft.despawnme then - local text=string.format("Flight %s will be despawned NOW!", self.alias) - self:T(self.lid..text) - - -- Respawn group - if (not self.norespawn) and (not self.respawn_after_takeoff) then - local idx=self:GetSpawnIndexFromGroup(group) - local coord=group:GetCoordinate() - self:_Respawn(idx, coord, 0) - else + if self.norespawn or self.respawn_after_takeoff then + -- Despawn old group. - if self.despawnair then + if self.despawnair then + self:T(self.lid..string.format("[STATUS despawnme] Flight %s will be despawned NOW and NO new group is created!", self.alias)) self:_Despawn(group, 0) end + + else + + -- Despawn old group and respawn a new one. + self:T(self.lid..string.format("[STATUS despawnme] Flight %s will be despawned NOW and a new group is respawned!", self.alias)) + self:_Respawn(group) + end - end else @@ -4164,9 +4138,7 @@ function RAT:_OnLand(EventData) self:T(self.lid..text) -- Respawn group. - local idx=self:GetSpawnIndexFromGroup(SpawnGroup) - local coord=SpawnGroup:GetCoordinate() - self:_Respawn(idx, coord) + self:_Respawn(SpawnGroup) end end @@ -4215,9 +4187,7 @@ function RAT:_OnEngineShutdown(EventData) self:T(self.lid..text) -- Respawn group. - local idx=self:GetSpawnIndexFromGroup(SpawnGroup) - local coord=SpawnGroup:GetCoordinate() - self:_Respawn(idx, coord, 3) + self:_Respawn(SpawnGroup, nil, 3) else @@ -4382,13 +4352,9 @@ function RAT:_OnCrash(EventData) -- Debug info. self:T(self.lid..string.format("No units left of group %s. Group will be respawned now.", SpawnGroup:GetName())) - - -- Get spawn index - local idx=self:GetSpawnIndexFromGroup(SpawnGroup) - local coord=SpawnGroup:GetCoordinate() - + -- Respawn group. - self:_Respawn(idx, coord) + self:_Respawn(SpawnGroup) end else @@ -4404,117 +4370,6 @@ function RAT:_OnCrash(EventData) end end ---- Despawn unit. Unit gets destoyed and group is set to nil. --- Index of ratcraft array is taken from spawned group name. --- @param #RAT self --- @param Wrapper.Group#GROUP group Group to be despawned. --- @param #number delay Delay in seconds before the despawn happens. -function RAT:_Despawn(group, delay) - - if group ~= nil then - - -- Get spawnindex of group. - local index=self:GetSpawnIndexFromGroup(group) - - if index ~= nil then - - local ratcraft=self.ratcraft[index] - - self.ratcraft[index].group=nil - self.ratcraft[index]["status"]="Dead" - - --TODO: Maybe here could be some more arrays deleted? - --TODO: Somehow this causes issues. - --[[ - --self.ratcraft[index]["group"]=group - self.ratcraft[index]["destination"]=nil - self.ratcraft[index]["departure"]=nil - self.ratcraft[index]["waypoints"]=nil - self.ratcraft[index]["airborne"]=nil - self.ratcraft[index]["Tground"]=nil - self.ratcraft[index]["Pground"]=nil - self.ratcraft[index]["Tlastcheck"]=nil - self.ratcraft[index]["P0"]=nil - self.ratcraft[index]["Pnow"]=nil - self.ratcraft[index]["Distance"]=nil - self.ratcraft[index].takeoff=nil - self.ratcraft[index].landing=nil - self.ratcraft[index].wpholding=nil - self.ratcraft[index].wpfinal=nil - self.ratcraft[index].active=false - self.ratcraft[index]["status"]=nil - self.ratcraft[index].livery=nil - self.ratcraft[index].despawnme=nil - self.ratcraft[index].nrespawn=nil - ]] - - -- We should give it at least 3 sec since this seems to be the time until free parking spots after despawn are available again (Sirri Island test). - local despawndelay=0 - if delay then - -- Explicitly requested delay time. - despawndelay=delay - elseif self.respawn_delay then - -- Despawn afer respawn_delay. Actual respawn happens in +3 seconds to allow for free parking. - despawndelay=self.respawn_delay - end - - -- This will destroy the DCS group and create a single DEAD event. - --if despawndelay>0.5 then - self:T(self.lid..string.format("%s delayed despawn in %.1f seconds.", self.alias, despawndelay)) - SCHEDULER:New(nil, self._Destroy, {self, group}, despawndelay) - --else - --self:_Destroy(group) - --end - - -- Remove submenu for this group. - if self.f10menu and self.SubMenuName ~= nil then - self.Menu[self.SubMenuName]["groups"][index]:Remove() - end - - end - end -end - ---- Destroys the RAT DCS group and all of its DCS units. --- Note that this raises a DEAD event at run-time. --- So all event listeners will catch the DEAD event of this DCS group. --- @param #RAT self --- @param Wrapper.Group#GROUP group The RAT group to be destroyed. -function RAT:_Destroy(group) - self:F2(group) - - local DCSGroup = group:GetDCSObject() -- DCS#Group - - if DCSGroup and DCSGroup:isExist() then - --- -- Cread one single Dead event and delete units from database. --- local triggerdead=true --- for _,DCSUnit in pairs(DCSGroup:getUnits()) do --- --- -- Dead event. --- if DCSUnit then --- if triggerdead then --- self:_CreateEventDead(timer.getTime(), DCSUnit) --- triggerdead=false --- end --- --- -- Delete from data base. --- _DATABASE:DeleteUnit(DCSUnit:getName()) --- end --- end - - local ratcraft=self:_GetRatcraftFromGroup(group) - - ratcraft.flightgroup:Destroy(0) - ratcraft.flightgroup:__Stop(0.1) - - -- Destroy DCS group. - --DCSGroup:destroy() - DCSGroup = nil - end - - return nil -end --- Create a Dead event. -- @param #RAT self @@ -4673,30 +4528,37 @@ function RAT:_Waypoint(index, description, Type, Coord, Speed, Altitude, Airport self:T(self.lid.."Unknown Airport category in _Waypoint()!") end end - -- properties - RoutePoint.properties = { - ["vnav"] = 1, - ["scale"] = 0, - ["angle"] = 0, - ["vangle"] = 0, - ["steer"] = 2, - } - -- tasks - local TaskCombo = {} - local TaskHolding = self:_TaskHolding({x=Coord.x, y=Coord.z}, Altitude, Speed, self:_Randomize(90,0.9)) - local TaskWaypoint = self:_TaskFunction("RAT._WaypointFunction", self, index) - - RoutePoint.task = {} - RoutePoint.task.id = "ComboTask" - RoutePoint.task.params = {} - - TaskCombo[#TaskCombo+1]=TaskWaypoint - if Type==RAT.wp.holding then - TaskCombo[#TaskCombo+1]=TaskHolding + + if false then + + -- TODO: Disable the tasks as this is done by FLIGHTGROUP now. Delete when working + + -- properties + RoutePoint.properties = { + ["vnav"] = 1, + ["scale"] = 0, + ["angle"] = 0, + ["vangle"] = 0, + ["steer"] = 2, + } + -- tasks + local TaskCombo = {} + local TaskHolding = self:_TaskHolding({x=Coord.x, y=Coord.z}, Altitude, Speed, self:_Randomize(90,0.9)) + local TaskWaypoint = self:_TaskFunction("RAT._WaypointFunction", self, index) + + RoutePoint.task = {} + RoutePoint.task.id = "ComboTask" + RoutePoint.task.params = {} + + TaskCombo[#TaskCombo+1]=TaskWaypoint + if Type==RAT.wp.holding then + TaskCombo[#TaskCombo+1]=TaskHolding + end + + RoutePoint.task.params.tasks = TaskCombo + end - RoutePoint.task.params.tasks = TaskCombo - -- Return waypoint. return RoutePoint end @@ -4846,6 +4708,7 @@ function RAT._WaypointFunction(group, rat, wp) end end + -- TODO: Move this to OnAfter Final Waypoint if wp==WPfinal then text=string.format("Flight %s arrived at final destination %s.", group:GetName(), destination) MESSAGE:New(text, 10):ToAllIf(rat.reportstatus) @@ -6013,8 +5876,8 @@ function RAT:_ATCClearForLanding(airportname, flightname) -- Debug message. BASE:I(RAT.id..string.format("ATC %s: Flight %s cleared for landing", airportname, flightname)) - if string.find(flight,"#") then - flight = string.match(flight,"^(.+)#") + if string.find(flightname,"#") then + flightname = string.match(flightname,"^(.+)#") end local text=string.format("ATC %s: Flight %s you are cleared for landing.", airportname, flightname) MESSAGE:New(text, 10):ToAllIf(RAT.ATC.messages) diff --git a/Moose Development/Moose/Ops/FlightGroup.lua b/Moose Development/Moose/Ops/FlightGroup.lua index 953b3e5ac..e6471c2a4 100644 --- a/Moose Development/Moose/Ops/FlightGroup.lua +++ b/Moose Development/Moose/Ops/FlightGroup.lua @@ -1257,9 +1257,12 @@ function FLIGHTGROUP:Status() -- Check ammo status. self:_CheckAmmoStatus() - -- Check damage. + -- Check damage. self:_CheckDamage() + -- Check if stuck while taxiing. + self:_CheckStuck() + -- Get current mission (if any). local mission=self:GetMissionCurrent() @@ -1627,6 +1630,9 @@ function FLIGHTGROUP:Status() if not mission then self.Twaiting=nil self.dTwait=nil + + -- Check if group is done. + -- TODO: Not sure why I introduced this here. self:_CheckGroupDone() end @@ -2139,6 +2145,10 @@ function FLIGHTGROUP:onafterSpawned(From, Event, To) self.isDestroyed=false if self.isAI then + + -- TODO: Could be that element is spawned UNCONTROLLED. + -- In that case, the commands are not yet used. + -- This should be shifted to something like after ACTIVATED -- Set ROE. self:SwitchROE(self.option.ROE) @@ -2740,6 +2750,7 @@ function FLIGHTGROUP:onafterOutOfMissilesAA(From, Event, To) if self.outofAAMrtb then -- Back to destination or home. local airbase=self.destbase or self.homebase + self:T(self.lid.."Calling RTB in onafterOutOfMissilesAA") self:__RTB(-5, airbase) end end @@ -2754,6 +2765,7 @@ function FLIGHTGROUP:onafterOutOfMissilesAG(From, Event, To) if self.outofAGMrtb then -- Back to destination or home. local airbase=self.destbase or self.homebase + self:T(self.lid.."Calling RTB in onafterOutOfMissilesAG") self:__RTB(-5, airbase) end end @@ -2843,8 +2855,8 @@ function FLIGHTGROUP:_CheckGroupDone(delay, waittime) -- Number of remaining tasks/missions? if nTasks==0 and nMissions==0 and nTransports==0 then - local destbase=self.destbase or self.homebase - local destzone=self.destzone or self.homezone + local destbase=self.destbase or self.homebase --Wrapper.Airbase#AIRBASE + local destzone=self.destzone or self.homezone --Wrapper.Airbase#AIRBASE -- Send flight to destination. if waittime then @@ -2855,8 +2867,11 @@ function FLIGHTGROUP:_CheckGroupDone(delay, waittime) self:T(self.lid.."Passed Final WP and No current and/or future missions/tasks/transports AND parking at destination airbase ==> Arrived!") self:Arrived() else - self:T(self.lid.."Passed Final WP and No current and/or future missions/tasks/transports ==> RTB!") - self:__RTB(-0.1, destbase) + -- Only send RTB if current base is not yet the destination + if self.currbase==nil or self.currbase.AirbaseName~=destbase.AirbaseName then + self:T(self.lid.."Passed Final WP and No current and/or future missions/tasks/transports ==> RTB!") + self:__RTB(-0.1, destbase) + end end elseif destzone then self:T(self.lid.."Passed Final WP and No current and/or future missions/tasks/transports ==> RTZ!") @@ -2984,6 +2999,7 @@ function FLIGHTGROUP:onbeforeRTB(From, Event, To, airbase, SpeedTo, SpeedHold) end if Tsuspend and not allowed then + self:T(self.lid.."Calling RTB in onbeforeRTB") self:__RTB(Tsuspend, airbase, SpeedTo, SpeedHold) end @@ -3364,8 +3380,8 @@ function FLIGHTGROUP:onafterWait(From, Event, To, Duration, Altitude, Speed) -- Set time stamp. self.Twaiting=timer.getAbsTime() - -- Max waiting - self.dTwait=Duration + -- Max waiting time in seconds. + self.dTwait=Duration end @@ -3664,6 +3680,7 @@ function FLIGHTGROUP:onafterFuelLow(From, Event, To) -- Send back to airbase. if airbase and self.fuellowrtb then + self:T(self.lid.."Calling RTB in onafterFuelLow") self:RTB(airbase) --TODO: RTZ end @@ -3688,6 +3705,7 @@ function FLIGHTGROUP:onafterFuelCritical(From, Event, To) local airbase=self.destbase or self.homebase if airbase and self.fuelcriticalrtb and not self:IsGoing4Fuel() then + self:T(self.lid.."Calling RTB in onafterFuelCritical") self:RTB(airbase) --TODO: RTZ end @@ -4835,6 +4853,87 @@ function FLIGHTGROUP:_GetTerminal(_attribute, _category) return _terminal end +--- Check if group got stuck. This overwrites the OPSGROUP function. +-- Here we only check if stuck whilst taxiing. +-- @param #FLIGHTGROUP self +-- @param #boolean Despawn If `true`, despawn group if stuck. +-- @return #number Time in seconds the group got stuck or nil if not stuck. +function FLIGHTGROUP:_CheckStuck(Despawn) + + -- Cases we are not stuck. + if not self:IsTaxiing() then + return nil + end + + -- Current time. + local Tnow=timer.getTime() + + -- Expected speed in m/s. + local ExpectedSpeed=5 + + -- Current speed in m/s. + local speed=self:GetVelocity() + + -- Check speed. + if speed<0.1 then + + if ExpectedSpeed>0 and not self.stuckTimestamp then + self:T2(self.lid..string.format("WARNING: Group came to an unexpected standstill. Speed=%.1f<%.1f m/s expected", speed, ExpectedSpeed)) + self.stuckTimestamp=Tnow + self.stuckVec3=self:GetVec3() + end + + else + -- Moving (again). + self.stuckTimestamp=nil + end + + local holdtime=nil + + -- Somehow we are not moving... + if self.stuckTimestamp then + + -- Time we are holding. + holdtime=Tnow-self.stuckTimestamp + + -- Trigger stuck event. + self:Stuck(holdtime) + + if holdtime>=5*60 and holdtime<15*60 then + + -- Debug warning. + self:T(self.lid..string.format("WARNING: Group came to an unexpected standstill. Speed=%.1f<%.1f m/s expected for %d sec", speed, ExpectedSpeed, holdtime)) + + elseif holdtime>=15*60 then + + -- Debug warning. + self:T(self.lid..string.format("WARNING: Group came to an unexpected standstill. Speed=%.1f<%.1f m/s expected for %d sec", speed, ExpectedSpeed, holdtime)) + + -- Look for a current mission and cancel it as we do not seem to be able to perform it. + local mission=self:GetMissionCurrent() + + if mission then + self:T(self.lid..string.format("WARNING: Cancelling mission %s [%s] due to being stuck", mission:GetName(), mission:GetType())) + self:MissionCancel(mission) + end + + if self.stuckDespawn then + if self.legion then + self:T(self.lid..string.format("Asset is returned to its legion after being stuck!")) + self:ReturnToLegion() + else + self:T(self.lid..string.format("Despawning group after being stuck!")) + self:Despawn() + end + end + + end + + end + + return holdtime +end + ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- OPTION FUNCTIONS ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- diff --git a/Moose Development/Moose/Ops/OpsGroup.lua b/Moose Development/Moose/Ops/OpsGroup.lua index 3d5d088d0..556b13a1a 100644 --- a/Moose Development/Moose/Ops/OpsGroup.lua +++ b/Moose Development/Moose/Ops/OpsGroup.lua @@ -117,6 +117,10 @@ -- @field #string callsignAlias Callsign alias. -- -- @field #OPSGROUP.Spot spot Laser and IR spot. +-- +-- @field DCS#Vec3 stuckVec3 Position where the group got stuck. +-- @field #number stuckTimestamp Time stamp [sec], when the group got stuck. +-- @field #boolean stuckDespawn If `true`, group gets despawned after beeing stuck for a certain time. -- -- @field #OPSGROUP.Ammo ammo Initial ammount of ammo. -- @field #OPSGROUP.WeaponData weaponData Weapon data table with key=BitType. @@ -676,10 +680,11 @@ function OPSGROUP:New(group) self:AddTransition("*", "UpdateRoute", "*") -- Update route of group. self:AddTransition("*", "PassingWaypoint", "*") -- Group passed a waypoint. - self:AddTransition("*", "PassedFinalWaypoint", "*") -- Group passed the waypoint. + self:AddTransition("*", "PassedFinalWaypoint", "*") -- Group passed the waypoint. self:AddTransition("*", "GotoWaypoint", "*") -- Group switches to a specific waypoint. self:AddTransition("*", "Wait", "*") -- Group will wait for further orders. + self:AddTransition("*", "Stuck", "*") -- Group got stuck. self:AddTransition("*", "DetectedUnit", "*") -- Unit was detected (again) in this detection cycle. self:AddTransition("*", "DetectedUnitNew", "*") -- Add a newly detected unit to the detected units set. @@ -1889,7 +1894,7 @@ end --- Get current velocity of the group. -- @param #OPSGROUP self --- @param #string UnitName (Optional) Get heading of a specific unit of the group. Default is from the first existing unit in the group. +-- @param #string UnitName (Optional) Get velocity of a specific unit of the group. Default is from the first existing unit in the group. -- @return #number Velocity in m/s. function OPSGROUP:GetVelocity(UnitName) diff --git a/Moose Development/Moose/Wrapper/Group.lua b/Moose Development/Moose/Wrapper/Group.lua index 96ea2599d..e3dd652cd 100644 --- a/Moose Development/Moose/Wrapper/Group.lua +++ b/Moose Development/Moose/Wrapper/Group.lua @@ -359,14 +359,15 @@ end -- @param #GROUP self -- @return DCS#Group The DCS Group. function GROUP:GetDCSObject() + + -- Get DCS group. local DCSGroup = Group.getByName( self.GroupName ) if DCSGroup then return DCSGroup - else - env.error("ERROR: Could not get DCS group object!") end + self:E(string.format("ERROR: Could not get DCS group object of group %s because DCS object could not be found!", tostring(self.GroupName))) return nil end From 2c67a66d88b5d798655bdba98fdaaf2d1bc40f8e Mon Sep 17 00:00:00 2001 From: Frank Date: Tue, 2 Apr 2024 23:18:30 +0200 Subject: [PATCH 14/26] Update RAT.lua --- Moose Development/Moose/Functional/RAT.lua | 359 ++++++++++----------- 1 file changed, 170 insertions(+), 189 deletions(-) diff --git a/Moose Development/Moose/Functional/RAT.lua b/Moose Development/Moose/Functional/RAT.lua index 4c8fca65e..408ec1058 100644 --- a/Moose Development/Moose/Functional/RAT.lua +++ b/Moose Development/Moose/Functional/RAT.lua @@ -866,7 +866,10 @@ function RAT:Spawn(naircraft) local Tstop=Tstart+dt*(self.ngroups-1) -- Status check and report scheduler. - SCHEDULER:New(nil, self.Status, {self}, Tstart+1, self.statusinterval) + --SCHEDULER:New(nil, self.Status, {self}, Tstart+1, self.statusinterval) + --self.sid_Status=self:ScheduleRepeat(Start,Repeat,RandomizeFactor,Stop,SchedulerFunction,...) + self.sid_Status=self:ScheduleRepeat(Tstart+1, self.statusinterval, nil, nil, RAT.Status, self) + -- Handle events. self:HandleEvent(EVENTS.Birth, self._OnBirth) @@ -884,13 +887,13 @@ function RAT:Spawn(naircraft) end -- Start scheduled spawning. - SCHEDULER:New(nil, self._SpawnWithRoute, {self}, Tstart, dt, 0.0, Tstop) - - --self.sid_spawn=self.Scheduler:Schedule(MasterObject,SchedulerFunction,SchedulerArguments,Start,Repeat,RandomizeFactor,Stop,TraceLevel,Fsm) + --SCHEDULER:New(nil, self._SpawnWithRoute, {self}, Tstart, dt, 0.0, Tstop) + self.sid_Spawn=self:ScheduleRepeat(Tstart, dt, 0.0, Tstop, RAT._SpawnWithRoute, self) -- Start scheduled activation of uncontrolled groups. if self.uncontrolled and self.activate_uncontrolled then - SCHEDULER:New(nil, self._ActivateUncontrolled, {self}, self.activate_delay, self.activate_delta, self.activate_frand) + --SCHEDULER:New(nil, self._ActivateUncontrolled, {self}, self.activate_delay, self.activate_delta, self.activate_frand) + self.sid_Activate=self:ScheduleRepeat(self.activate_delay, self.activate_delta, self.activate_frand, nil, RAT._ActivateUncontrolled, self) end return true @@ -1710,7 +1713,7 @@ function RAT:Uncontrolled() return self end ---- Activate uncontrolled aircraft. +--- Define how aircraft that are spawned in uncontrolled state are activate. -- @param #RAT self -- @param #number maxactivated Maximal numnber of activated aircraft. Absolute maximum will be the number of spawned groups. Default is 1. -- @param #number delay Time delay in seconds before (first) aircraft is activated. Default is 1 second. @@ -2240,14 +2243,19 @@ function RAT:_SpawnWithRoute(_departure, _destination, _takeoff, _landing, _live end --- Function called when flight is holding. - function flightgroup.OnAfterHolding(flightgroup, From,Event,To) - self:T(self.lid..string.format("RAT group is HOLDING ==> ATCRegisterFlight")) - self:_ATCRegisterFlight(groupname, timer.getTime()) + function flightgroup.OnAfterHolding(Flightgroup, From, Event, To) + local flightgroup=Flightgroup --Ops.FlightGroup#FLIGHTGROUP + + if self.ATCswitch then + self:T(self.lid..string.format("RAT group is HOLDING ==> ATCRegisterFlight")) + self:_ATCRegisterFlight(groupname, timer.getTime()) + end -- Register aircraft at ATC. if self.ATCswitch then if self.f10menu then - MENU_MISSION_COMMAND:New("Clear for landing", self.Menu[self.SubMenuName].groups[self.SpawnIndex], self.ClearForLanding, self, groupname) + --MENU_MISSION_COMMAND:New("Clear for landing", self.Menu[self.SubMenuName].groups[self.SpawnIndex], self.ClearForLanding, self, groupname) + MENU_MISSION_COMMAND:New("Clear for landing", self.Menu[self.SubMenuName].groups[self.SpawnIndex], flightgroup.ClearToLand, flightgroup) end self:_ATCRegisterFlight(groupname, timer.getTime()) end @@ -2452,6 +2460,10 @@ function RAT:_Respawn(group, lastpos, delay) local _lastpos=nil if self.continuejourney then + + --- + -- Continue Journey + --- -- We continue our journey from the old departure airport. _departure=destination:GetName() @@ -2462,7 +2474,7 @@ function RAT:_Respawn(group, lastpos, delay) -- Last known position of the aircraft, which should be the sparking spot location. -- Note: we have to check that it was supposed to land and not respawned directly after landing or after takeoff. -- TODO: Need to think if continuejourney with respawn_after_takeoff actually makes sense. - if landing==RAT.wp.landing and lastpos and not (self.respawn_at_landing or self.respawn_after_takeoff) then + if landing==RAT.wp.landing and not (self.respawn_at_landing or self.respawn_after_takeoff) then -- Check that we have an airport or FARP but not a ship (which would be categroy 1). if destination:GetCategory()==4 then _lastpos=lastpos @@ -2500,7 +2512,11 @@ function RAT:_Respawn(group, lastpos, delay) end elseif self.commute then - + + --- + -- Commute + --- + -- We commute between departure and destination. if self.starshape==true then @@ -2623,39 +2639,14 @@ function RAT:_Despawn(group, delay) -- Get ratcraft. local ratcraft=self.ratcraft[index] --#RAT.RatCraft - - self.ratcraft[index].group=nil - self.ratcraft[index]["status"]="Dead" - - --TODO: Maybe here could be some more arrays deleted? Somehow this causes issues! - --[[ - --self.ratcraft[index]["group"]=group - self.ratcraft[index]["destination"]=nil - self.ratcraft[index]["departure"]=nil - self.ratcraft[index]["waypoints"]=nil - self.ratcraft[index]["airborne"]=nil - self.ratcraft[index]["Tground"]=nil - self.ratcraft[index]["Pground"]=nil - self.ratcraft[index]["Tlastcheck"]=nil - self.ratcraft[index]["P0"]=nil - self.ratcraft[index]["Pnow"]=nil - self.ratcraft[index]["Distance"]=nil - self.ratcraft[index].takeoff=nil - self.ratcraft[index].landing=nil - self.ratcraft[index].wpholding=nil - self.ratcraft[index].wpfinal=nil - self.ratcraft[index].active=false - self.ratcraft[index]["status"]=nil - self.ratcraft[index].livery=nil - self.ratcraft[index].despawnme=nil - self.ratcraft[index].nrespawn=nil - ]] - --ratcraft.flightgroup:Destroy(0) + -- Despawn flightgroup and stop. ratcraft.flightgroup:Despawn() ratcraft.flightgroup:__Stop(0.1) - + -- Nil ratcraft in table. + self.ratcraft[index].group=nil + self.ratcraft[index]["status"]="Dead" self.ratcraft[index]=nil -- Remove submenu for this group. @@ -3916,109 +3907,106 @@ function RAT:_OnBirth(EventData) -- Get the template name of the group. This can be nil if this was not a spawned group. local EventPrefix = self:_GetPrefixFromGroup(SpawnGroup) - if EventPrefix then + -- Check that the template name actually belongs to this object. + if EventPrefix and EventPrefix == self.alias then - -- Check that the template name actually belongs to this object. - if EventPrefix == self.alias then + local text="Event: Group "..SpawnGroup:GetName().." was born." + self:T(self.lid..text) - local text="Event: Group "..SpawnGroup:GetName().." was born." - self:T(self.lid..text) + -- Set status. + local status="unknown in birth" + if SpawnGroup:InAir() then + status=RAT.status.EventBirthAir + elseif self.uncontrolled then + status=RAT.status.Uncontrolled + else + status=RAT.status.EventBirth + end + self:_SetStatus(SpawnGroup, status) + - -- Set status. - local status="unknown in birth" - if SpawnGroup:InAir() then - status=RAT.status.EventBirthAir - elseif self.uncontrolled then - status=RAT.status.Uncontrolled + -- Get some info ablout this flight. + local i=self:GetSpawnIndexFromGroup(SpawnGroup) + + local ratcraft=self.ratcraft[i] --#RAT.RatCraft + + local _departure=ratcraft.departure:GetName() + local _destination=ratcraft.destination:GetName() + local _nrespawn=ratcraft.nrespawn + local _takeoff=ratcraft.takeoff + local _landing=ratcraft.landing + local _livery=ratcraft.livery + + -- Some is only useful for an actual airbase (not a zone). + local _airbase=AIRBASE:FindByName(_departure) + + -- Check if aircraft group was accidentally spawned on the runway. + -- This can happen due to no parking slots available and other DCS bugs. + local onrunway=false + if _airbase then + -- Check that we did not want to spawn at a runway or in air. + if self.checkonrunway and _takeoff ~= RAT.wp.runway and _takeoff ~= RAT.wp.air then + onrunway=_airbase:CheckOnRunWay(SpawnGroup, self.onrunwayradius, false) + end + end + + -- Workaround if group was spawned on runway. + if onrunway then + + -- Error message. + local text=string.format("ERROR: RAT group of %s was spawned on runway. Group #%d will be despawned immediately!", self.alias, i) + MESSAGE:New(text,30):ToAllIf(self.Debug) + self:E(self.lid..text) + if self.Debug then + SpawnGroup:FlareRed() + end + + -- Despawn the group. + self:_Despawn(SpawnGroup) + + -- Try to respawn the group if there is at least another airport or random airport selection is used. + if (self.Ndeparture_Airports>=2 or self.random_departure) and _nrespawn=2 or self.random_departure) and _nrespawn Date: Wed, 3 Apr 2024 14:27:10 +0200 Subject: [PATCH 15/26] Update RAT.lua --- Moose Development/Moose/Functional/RAT.lua | 488 +++++++-------------- 1 file changed, 149 insertions(+), 339 deletions(-) diff --git a/Moose Development/Moose/Functional/RAT.lua b/Moose Development/Moose/Functional/RAT.lua index 408ec1058..4643c73ac 100644 --- a/Moose Development/Moose/Functional/RAT.lua +++ b/Moose Development/Moose/Functional/RAT.lua @@ -487,26 +487,21 @@ RAT.status={ -- @field #number index Spawn index. -- @field Wrapper.Group#Group group The aircraft group. -- @field Ops.FlightGroup#FLIGHTGROUP flightgroup The flight group. --- @field #table destination Destination of this group. --- @field #table departure Departure place of this group. +-- @field Wrapper.Airbase#AIRBASE destination Destination of this group. Can also be a ZONE. +-- @field Wrapper.Airbase#AIRBASE departure Departure place of this group. Can also be a ZONE. -- @field #table waypoints Waypoints. -- @field #boolean airborne Whether this group is airborne. -- @field #number nunits Number of units. --- @field #number Tground Time stamp on ground. --- @field #number Pground ? --- @field #number Uground ? --- @field #number Tlastcheck Time stamp of last check. --- @field #table P0 ? --- @field #table Pnow ? +-- @field Core.Point#COORDINATE Pnow Current position. -- @field #number Distance Distance travelled in meters. -- @field #number takeoff Takeoff type. -- @field #number landing Laning type. --- @field #table wpholding Holding waypoint. --- @field #table wpfinal Final waypoint. --- @field #boolean active Whether the group is actie or uncontrolled. +-- @field #table wpdesc Waypoint descriptins. +-- @field #table wpstatus Waypoint status. +-- @field #boolean active Whether the group is active or uncontrolled. -- @field #string status Status of the group. -- @field #string livery Livery of the group. --- @field #boolean despawnme Despawn group if `true`. +-- @field #boolean despawnme Despawn group if `true` in the next status update. -- @field #number nrespawn Number of respawns. --- RAT friendly coalitions. @@ -714,7 +709,7 @@ function RAT:Spawn(naircraft) -- Init RAT ATC if not already done. if self.ATCswitch and not RAT.ATC.init then - self:_ATCInit(self.airports_map) + RAT._ATCInit(self.airports_map) end -- Create F10 main menu if it does not exists yet. @@ -2165,7 +2160,7 @@ function RAT:_SpawnWithRoute(_departure, _destination, _takeoff, _landing, _live end -- Set flight plan. - local departure, destination, waypoints, WPholding, WPfinal = self:_SetRoute(takeoff, landing, _departure, _destination, _waypoint) + local departure, destination, waypoints, wpdesc, wpstatus = self:_SetRoute(takeoff, landing, _departure, _destination, _waypoint) -- Return nil if we could not find a departure destination or waypoints if not (departure and destination and waypoints) then @@ -2208,77 +2203,7 @@ function RAT:_SpawnWithRoute(_departure, _destination, _takeoff, _landing, _live -- No automatic despawning if group gets stuck. flightgroup.stuckDespawn=false - - --- Function called when passing a waypoint. - function flightgroup.OnAfterPassingWaypoint(Flightgroup, From, Event, To, Waypoint) - local waypoint=Waypoint --Ops.OpsGroup#OPSGROUP.Waypoint - local flightgroup=Flightgroup --Ops.FlightGroup#FLIGHTGROUP - - local wp=waypoint.uid - - -- Debug info. - self:T(self.lid..string.format("RAT passed waypoint %s [uid=%d]: %s", waypoint.name, waypoint.uid, tostring(self.waypointdescriptions[wp]))) - - -- Call RAT waypoint function. - -- * calls _ATCRegisterFlight at holding waypoint ==> now in OnAfterHolding - -- * sets despawnme switch at final waypoint if landing=air - -- * sets status - RAT._WaypointFunction(group, self, waypoint.uid) - - if waypoint.uid==3 then - --flightgroup:SelfDestruction(Delay,ExplosionPower,ElementName) - end - end - - --- Function called when passing the final waypoint - function flightgroup.OnAfterPassedFinalWaypoint(flightgroup, From, Event, To) - self:T(self.lid..string.format("RAT passed FINAL waypoint")) - --TODO: Set despawnme switch if landing=air - end - - --- Function called when flight is RTB. - function flightgroup.OnAfterRTB(flightgroup, From, Event, To, airbase, SpeedTo, SpeedHold, SpeedLand) - self:T(self.lid..string.format("RAT group is RTB")) - end - - --- Function called when flight is holding. - function flightgroup.OnAfterHolding(Flightgroup, From, Event, To) - local flightgroup=Flightgroup --Ops.FlightGroup#FLIGHTGROUP - - if self.ATCswitch then - self:T(self.lid..string.format("RAT group is HOLDING ==> ATCRegisterFlight")) - self:_ATCRegisterFlight(groupname, timer.getTime()) - end - - -- Register aircraft at ATC. - if self.ATCswitch then - if self.f10menu then - --MENU_MISSION_COMMAND:New("Clear for landing", self.Menu[self.SubMenuName].groups[self.SpawnIndex], self.ClearForLanding, self, groupname) - MENU_MISSION_COMMAND:New("Clear for landing", self.Menu[self.SubMenuName].groups[self.SpawnIndex], flightgroup.ClearToLand, flightgroup) - end - self:_ATCRegisterFlight(groupname, timer.getTime()) - end - end - - function flightgroup.OnAfterLandAtAirbase(Flightgroup, From, Event, To, Airport) - self:T(self.lid..string.format("RAT group landed at airbase")) - end - - function flightgroup.OnAfterArrived(Flightgroup, From, Event, To) - self:T(self.lid..string.format("RAT group arrived")) - end - - --- Function called when a group got stuck. - function flightgroup.OnAfterStuck(Flightgroup, From, Event, To, Stucktime) - local flightgroup=Flightgroup --Ops.FlightGroup#FLIGHTGROUP - self:T(self.lid..string.format("Group %s got stuck for %d seconds", flightgroup:GetName(), Stucktime)) - if Stucktime>10*60 then - self:_Respawn(flightgroup.group) - end - - end - -- Increase counter of alive groups (also uncontrolled ones). self.alive=self.alive+1 self:T(self.lid..string.format("Alive groups counter now = %d.",self.alive)) @@ -2338,8 +2263,8 @@ function RAT:_SpawnWithRoute(_departure, _destination, _takeoff, _landing, _live -- Each aircraft gets its own takeoff type. ratcraft.takeoff=takeoff ratcraft.landing=landing - ratcraft.wpholding=WPholding - ratcraft.wpfinal=WPfinal + ratcraft.wpdesc=wpdesc + ratcraft.wpstatus=wpstatus -- Aircraft is active or spawned in uncontrolled state. ratcraft.active=not self.uncontrolled @@ -2380,6 +2305,102 @@ function RAT:_SpawnWithRoute(_departure, _destination, _takeoff, _landing, _live MENU_MISSION_COMMAND:New("Status report", self.Menu[self.SubMenuName].groups[self.SpawnIndex], self.Status, self, true, self.SpawnIndex) end + --- Function called when passing a waypoint. + function flightgroup.OnAfterPassingWaypoint(Flightgroup, From, Event, To, Waypoint) + local waypoint=Waypoint --Ops.OpsGroup#OPSGROUP.Waypoint + local flightgroup=Flightgroup --Ops.FlightGroup#FLIGHTGROUP + + local wpid=waypoint.uid + + local ratcraft=self:_GetRatcraftFromGroup(flightgroup.group) + + local wpdescription=tostring(ratcraft.wpdesc[wpid]) + local wpstatus=ratcraft.wpstatus[wpid] + + -- Debug info. + self:T(self.lid..string.format("RAT passed waypoint %s [uid=%d]: %s [status=%s]", waypoint.name, wpid, wpdescription, wpstatus)) + + -- Set status + self:_SetStatus(group, wpstatus) + + if waypoint.uid==3 then + --flightgroup:SelfDestruction(Delay,ExplosionPower,ElementName) + end + + end + + --- Function called when passing the FINAL waypoint + function flightgroup.OnAfterPassedFinalWaypoint(flightgroup, From, Event, To) + + self:T(self.lid..string.format("RAT passed FINAL waypoint")) + + -- Info message. + local text=string.format("Flight %s arrived at final destination %s.", group:GetName(), destination) + MESSAGE:New(text, 10):ToAllIf(self.reportstatus) + self:T(self.lid..text) + + if landing==RAT.wp.air then + -- Final waypoint is air ==> Despawn flight + + -- Info message. + local text=string.format("Activating despawn switch for flight %s! Group will be detroyed soon.", group:GetName()) + MESSAGE:New(text, 10):ToAllIf(self.Debug) + self:T(self.lid..text) + + -- Enable despawn switch. Next time the status function is called, the aircraft will be despawned. + ratcraft.despawnme=true + end + + end + + --- Function called when flight is RTB. + function flightgroup.OnAfterRTB(flightgroup, From, Event, To, airbase, SpeedTo, SpeedHold, SpeedLand) + self:T(self.lid..string.format("RAT group is RTB")) + end + + --- Function called when flight is holding. + function flightgroup.OnAfterHolding(Flightgroup, From, Event, To) + local flightgroup=Flightgroup --Ops.FlightGroup#FLIGHTGROUP + + -- Aircraft arrived at holding point + local text=string.format("Flight %s to %s ATC: Holding and awaiting landing clearance.", group:GetName(), destination) + self:T(rat.lid..text) + MESSAGE:New(text, 10):ToAllIf(self.reportstatus) + + + -- Register aircraft at ATC. + if self.ATCswitch then + self:T(self.lid..string.format("RAT group is HOLDING ==> ATCRegisterFlight")) + + -- Create F10 menu + if self.f10menu then + MENU_MISSION_COMMAND:New("Clear for landing", self.Menu[self.SubMenuName].groups[self.SpawnIndex], flightgroup.ClearToLand, flightgroup) + end + + -- Register at ATC + RAT._ATCRegisterFlight(groupname, timer.getTime()) + end + end + + function flightgroup.OnAfterLandAtAirbase(Flightgroup, From, Event, To, Airport) + self:T(self.lid..string.format("RAT group landed at airbase")) + end + + function flightgroup.OnAfterArrived(Flightgroup, From, Event, To) + self:T(self.lid..string.format("RAT group arrived")) + end + + --- Function called when a group got stuck. + function flightgroup.OnAfterStuck(Flightgroup, From, Event, To, Stucktime) + local flightgroup=Flightgroup --Ops.FlightGroup#FLIGHTGROUP + self:T(self.lid..string.format("Group %s got stuck for %d seconds", flightgroup:GetName(), Stucktime)) + if Stucktime>10*60 then + self:_Respawn(flightgroup.group) + end + + end + + return self.SpawnIndex end @@ -2473,7 +2494,8 @@ function RAT:_Respawn(group, lastpos, delay) -- Last known position of the aircraft, which should be the sparking spot location. -- Note: we have to check that it was supposed to land and not respawned directly after landing or after takeoff. - -- TODO: Need to think if continuejourney with respawn_after_takeoff actually makes sense. + -- DONE: Need to think if continuejourney with respawn_after_takeoff actually makes sense? + -- No, does not make sense. Disable it in consistency check. if landing==RAT.wp.landing and not (self.respawn_at_landing or self.respawn_after_takeoff) then -- Check that we have an airport or FARP but not a ship (which would be categroy 1). if destination:GetCategory()==4 then @@ -2671,7 +2693,8 @@ end -- @return Wrapper.Airbase#AIRBASE Departure airbase. -- @return Wrapper.Airbase#AIRBASE Destination airbase. -- @return #table Table of flight plan waypoints. --- @return #nil If no valid departure or destination airport could be found. +-- @return #table Table of waypoint descriptions. +-- @return #table Table of waypoint status. function RAT:_SetRoute(takeoff, landing, _departure, _destination, _waypoint) -- Max cruise speed. @@ -2717,7 +2740,7 @@ function RAT:_SetRoute(takeoff, landing, _departure, _destination, _waypoint) -- DEPARTURE AIRPORT -- Departure airport or zone. - local departure=nil + local departure=nil --Wrapper.Airbase#AIRBASE if _departure then if self:_AirportExists(_departure) then -- Check if new departure is an airport. @@ -2749,7 +2772,7 @@ function RAT:_SetRoute(takeoff, landing, _departure, _destination, _waypoint) end -- Coordinates of departure point. - local Pdeparture + local Pdeparture --Core.Point#COORDINATE if takeoff==RAT.wp.air then if _waypoint then -- Use coordinates of previous flight (commute or journey). @@ -2815,7 +2838,7 @@ function RAT:_SetRoute(takeoff, landing, _departure, _destination, _waypoint) end -- DESTINATION AIRPORT - local destination=nil + local destination=nil --Wrapper.Airbase#AIRBASE if _destination then if self:_AirportExists(_destination) then @@ -2883,7 +2906,7 @@ function RAT:_SetRoute(takeoff, landing, _departure, _destination, _waypoint) end -- Get destination coordinate. Either in a zone or exactly at the airport. - local Pdestination + local Pdestination --Core.Point#COORDINATE if landing==RAT.wp.air then local vec2=destination:GetRandomVec2() Pdestination=COORDINATE:NewFromVec2(vec2) @@ -3262,9 +3285,9 @@ function RAT:_SetRoute(takeoff, landing, _departure, _destination, _waypoint) -- Return departure, destination and waypoints. if self.returnzone then -- We return the actual zone here because returning the departure leads to problems with commute. - return departure, destination_returnzone, waypoints, wpholding, wpfinal + return departure, destination_returnzone, waypoints, wpholding, wpfinal, self.waypointdescriptions, self.waypointstatus else - return departure, destination, waypoints, wpholding, wpfinal + return departure, destination, waypoints, wpholding, wpfinal, self.waypointdescriptions, self.waypointstatus end end @@ -4045,7 +4068,7 @@ function RAT:_OnEngineStartup(EventData) self:_SetStatus(SpawnGroup, status) end - + else self:T2(self.lid.."ERROR: Group does not exist in RAT:_EngineStartup().") end @@ -4111,7 +4134,7 @@ function RAT:_OnLand(EventData) -- ATC plane landed. Take it out of the queue and set runway to free. if self.ATCswitch then - RAT:_ATCFlightLanded(SpawnGroup:GetName()) + RAT._ATCFlightLanded(SpawnGroup:GetName()) end if self.respawn_at_landing and not self.norespawn then @@ -4351,22 +4374,6 @@ function RAT:_OnCrash(EventData) end ---- Create a Dead event. --- @param #RAT self --- @param DCS#Time EventTime The time stamp of the event. --- @param DCS#Object Initiator The initiating object of the event. -function RAT:_CreateEventDead(EventTime, Initiator) - self:F( { EventTime, Initiator } ) - - local Event = { - id = world.event.S_EVENT_DEAD, - time = EventTime, - initiator = Initiator, - } - - world.onEvent( Event ) -end - ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- Create a waypoint that can be used with the Route command. @@ -4508,36 +4515,6 @@ function RAT:_Waypoint(index, description, Type, Coord, Speed, Altitude, Airport self:T(self.lid.."Unknown Airport category in _Waypoint()!") end end - - if false then - - -- TODO: Disable the tasks as this is done by FLIGHTGROUP now. Delete when working - - -- properties - RoutePoint.properties = { - ["vnav"] = 1, - ["scale"] = 0, - ["angle"] = 0, - ["vangle"] = 0, - ["steer"] = 2, - } - -- tasks - local TaskCombo = {} - local TaskHolding = self:_TaskHolding({x=Coord.x, y=Coord.z}, Altitude, Speed, self:_Randomize(90,0.9)) - local TaskWaypoint = self:_TaskFunction("RAT._WaypointFunction", self, index) - - RoutePoint.task = {} - RoutePoint.task.id = "ComboTask" - RoutePoint.task.params = {} - - TaskCombo[#TaskCombo+1]=TaskWaypoint - if Type==RAT.wp.holding then - TaskCombo[#TaskCombo+1]=TaskHolding - end - - RoutePoint.task.params.tasks = TaskCombo - - end -- Return waypoint. return RoutePoint @@ -4588,154 +4565,6 @@ end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---- Orbit at a specified position at a specified alititude with a specified speed. --- @param #RAT self --- @param DCS#Vec2 P1 The point to hold the position. --- @param #number Altitude The altitude ASL at which to hold the position. --- @param #number Speed The speed flying when holding the position in m/s. --- @param #number Duration Duration of holding pattern in seconds. --- @return DCS#Task DCSTask -function RAT:_TaskHolding(P1, Altitude, Speed, Duration) - - --local LandHeight = land.getHeight(P1) - - --TODO: randomize P1 - -- Second point is 3 km north of P1 and 200 m for helos. - local dx=3000 - local dy=0 - if self.category==RAT.cat.heli then - dx=200 - dy=0 - end - - local P2={} - P2.x=P1.x+dx - P2.y=P1.y+dy - local Task = { - id = 'Orbit', - params = { - pattern = AI.Task.OrbitPattern.RACE_TRACK, - --pattern = AI.Task.OrbitPattern.CIRCLE, - point = P1, - point2 = P2, - speed = Speed, - altitude = Altitude - } - } - - local DCSTask={} - DCSTask.id="ControlledTask" - DCSTask.params={} - DCSTask.params.task=Task - - if self.ATCswitch then - -- Set stop condition for holding. Either flag=1 or after max. X min holding. - local userflagname=string.format("%s#%03d", self.alias, self.SpawnIndex+1) - local maxholdingduration=60*120 - DCSTask.params.stopCondition={userFlag=userflagname, userFlagValue=1, duration=maxholdingduration} - else - DCSTask.params.stopCondition={duration=Duration} - end - - return DCSTask -end - ---- Function which is called after passing every waypoint. Info on waypoint is given and special functions are executed. --- @param Wrapper.Group#GROUP group Group of aircraft. --- @param #RAT rat RAT object. --- @param #number wp Waypoint index. Running number of the waypoints. Determines the actions to be executed. -function RAT._WaypointFunction(group, rat, wp) - - -- Current time and Spawnindex. - local Tnow=timer.getTime() - - -- Get ratcraft object. - local ratcraft=rat:_GetRatcraftFromGroup(group) - - -- Departure and destination names. - local departure=ratcraft.departure:GetName() - local destination=ratcraft.destination:GetName() - local landing=ratcraft.landing - local WPholding=ratcraft.wpholding - local WPfinal=ratcraft.wpfinal - - - -- For messages - local text - - -- Info on passing waypoint. - text=string.format("Flight %s passing waypoint #%d %s", group:GetName(), wp, rat.waypointdescriptions[wp]) - rat:T(rat.lid..text) - - -- New status. - local status=rat.waypointstatus[wp] - rat:_SetStatus(group, status) - - if wp==WPholding then - - -- Aircraft arrived at holding point - text=string.format("Flight %s to %s ATC: Holding and awaiting landing clearance.", group:GetName(), destination) - rat:T(rat.lid..text) - MESSAGE:New(text, 10):ToAllIf(rat.reportstatus) - - - -- Register aircraft at ATC. - if rat.ATCswitch then - if rat.f10menu then - -- TODO: get index and exchange with flightgroup landing clearance function - MENU_MISSION_COMMAND:New("Clear for landing", rat.Menu[rat.SubMenuName].groups[sdx], rat.ClearForLanding, rat, group:GetName()) - end - rat._ATCRegisterFlight(rat, group:GetName(), Tnow) - end - end - - -- TODO: Move this to OnAfter Final Waypoint - if wp==WPfinal then - text=string.format("Flight %s arrived at final destination %s.", group:GetName(), destination) - MESSAGE:New(text, 10):ToAllIf(rat.reportstatus) - rat:T(rat.lid..text) - - if landing==RAT.wp.air then - text=string.format("Activating despawn switch for flight %s! Group will be detroyed soon.", group:GetName()) - MESSAGE:New(text, 10):ToAllIf(rat.Debug) - rat:T(rat.lid..text) - -- Enable despawn switch. Next time the status function is called, the aircraft will be despawned. - ratcraft.despawnme=true - end - end -end - ---- Task function. --- @param #RAT self --- @param #string FunctionString Name of the function to be called. -function RAT:_TaskFunction(FunctionString, ... ) - self:F2({FunctionString, arg}) - - local DCSTask - local ArgumentKey - - -- Templatename and anticipated name the group will get - local templatename=self.templategroup:GetName() - local groupname=self:_AnticipatedGroupName() - - local DCSScript = {} - DCSScript[#DCSScript+1] = "local MissionControllable = GROUP:FindByName(\""..groupname.."\") " - DCSScript[#DCSScript+1] = "local RATtemplateControllable = GROUP:FindByName(\""..templatename.."\") " - - if arg and arg.n > 0 then - ArgumentKey = '_' .. tostring(arg):match("table: (.*)") - self.templategroup:SetState(self.templategroup, ArgumentKey, arg) - DCSScript[#DCSScript+1] = "local Arguments = RATtemplateControllable:GetState(RATtemplateControllable, '" .. ArgumentKey .. "' ) " - DCSScript[#DCSScript+1] = FunctionString .. "( MissionControllable, unpack( Arguments ) )" - else - DCSScript[#DCSScript+1] = FunctionString .. "( MissionControllable )" - end - - DCSTask = self.templategroup:TaskWrappedAction(self.templategroup:CommandDoScript(table.concat(DCSScript))) - - return DCSTask -end - --- Anticipated group name from alias and spawn index. -- @param #RAT self -- @param #number index Spawnindex of group if given or self.SpawnIndex+1 by default. @@ -4829,10 +4658,10 @@ function RAT:_CommandStartUncontrolled(group) group:SetCommand(StartCommand) -- Spawn index. - local index=self:GetSpawnIndexFromGroup(group) + local ratcraft=self:_GetRatcraftFromGroup(group) -- Set status to active. - self.ratcraft[index].active=true + ratcraft.active=true -- Set status to "Ready and Starting Engines". self:_SetStatus(group, RAT.status.EventBirth) @@ -5600,15 +5429,14 @@ end --- Data structure a RAT ATC airbase object. -- @type RAT.AtcFlight --- @field #table destination The destination airbase. +-- @field #string destination Name of the destination airbase. -- @field #number Tarrive Time stamp when flight arrived at holding. -- @field #number holding Holding time. -- @field #number Tonfinal Time stamp when flight started final approach. --- Initializes the ATC arrays and starts schedulers. --- @param #RAT self -- @param #table airports_map List of all airports of the map. -function RAT:_ATCInit(airports_map) +function RAT._ATCInit(airports_map) if not RAT.ATC.init then @@ -5631,19 +5459,13 @@ function RAT:_ATCInit(airports_map) airport.traffic=0 airport.Tlastclearance=nil - RAT.ATC.airport[name]={} - RAT.ATC.airport[name].queue={} - RAT.ATC.airport[name].busy=false - RAT.ATC.airport[name].onfinal={} - RAT.ATC.airport[name].Nonfinal=0 - RAT.ATC.airport[name].traffic=0 - RAT.ATC.airport[name].Tlastclearance=nil + RAT.ATC.airport[name]=airport end end - SCHEDULER:New(nil, RAT._ATCCheck, {self}, 5, 15) - SCHEDULER:New(nil, RAT._ATCStatus, {self}, 5, 60) + SCHEDULER:New(nil, RAT._ATCCheck, {}, 5, 15) + SCHEDULER:New(nil, RAT._ATCStatus, {}, 5, 60) RAT.ATC.T0=timer.getTime() end @@ -5659,26 +5481,21 @@ end function RAT:_ATCAddFlight(name, dest) -- Debug info BASE:I(RAT.id..string.format("ATC %s: Adding flight %s with destination %s.", dest, name, dest)) - + + -- Create new flight local flight={} --#RAT.AtcFlight flight.destination=dest flight.Tarrive=-1 flight.holding=-1 flight.Tarrive=-1 - -- Create new flight - RAT.ATC.flight[name]={} - RAT.ATC.flight[name].destination=dest - RAT.ATC.flight[name].Tarrive=-1 - RAT.ATC.flight[name].holding=-1 - RAT.ATC.flight[name].Tonfinal=-1 + RAT.ATC.flight[name]=flight end --- Deletes a flight from ATC lists after it landed. --- @param #RAT self -- @param #table t Table. -- @param #string entry Flight name which shall be deleted. -function RAT:_ATCDelFlight(t,entry) +function RAT._ATCDelFlight(t,entry) for k,_ in pairs(t) do if k==entry then t[entry]=nil @@ -5690,7 +5507,7 @@ end -- @param #RAT self -- @param #string name Group name of the flight. -- @param #number time Time the fight first registered. -function RAT:_ATCRegisterFlight(name, time) +function RAT._ATCRegisterFlight(name, time) BASE:I(RAT.id..string.format("Flight %s registered at ATC for landing clearance.", name)) RAT.ATC.flight[name].Tarrive=time RAT.ATC.flight[name].holding=0 @@ -5698,8 +5515,7 @@ end --- ATC status report about flights. --- @param #RAT self -function RAT:_ATCStatus() +function RAT._ATCStatus() -- Current time. local Tnow=timer.getTime() @@ -5753,12 +5569,10 @@ function RAT:_ATCStatus() end --- Main ATC function. Updates the landing queue of all airports and inceases holding time for all flights. --- @param #RAT self -function RAT:_ATCCheck() +function RAT._ATCCheck() -- Init queue of flights at all airports. - -- TODO: Global function - RAT:_ATCQueue() + RAT._ATCQueue() -- Current time. local Tnow=timer.getTime() @@ -5800,8 +5614,7 @@ function RAT:_ATCCheck() BASE:I(self.lid..text) -- Clear flight for landing. - -- TODO: Global function - RAT:_ATCClearForLanding(airportname, flightname) + RAT._ATCClearForLanding(airportname, flightname) end @@ -5810,16 +5623,14 @@ function RAT:_ATCCheck() end -- Update queue of flights at all airports. - -- TODO: Global function - RAT:_ATCQueue() + RAT._ATCQueue() end --- Giving landing clearance for aircraft by setting user flag. --- @param #RAT self -- @param #string airportname Name of destination airport. -- @param #string flightname Group name of flight, which gets landing clearence. -function RAT:_ATCClearForLanding(airportname, flightname) +function RAT._ATCClearForLanding(airportname, flightname) -- Find FLIGHTGROUP in database. local flightgroup=_DATABASE:FindOpsGroup(flightname) --Ops.FlightGroup#FLIGHTGROUP @@ -5870,9 +5681,8 @@ function RAT:_ATCClearForLanding(airportname, flightname) end --- Takes care of organisational stuff after a plane has landed. --- @param #RAT self -- @param #string name Group name of flight. -function RAT:_ATCFlightLanded(name) +function RAT._ATCFlightLanded(name) local flight=RAT.ATC.flight[name] --#RAT.AtcFlight @@ -5898,8 +5708,7 @@ function RAT:_ATCFlightLanded(name) airport.Nonfinal=airport.Nonfinal-1 -- Remove this flight from list of flights. - -- TODO: Global function - RAT:_ATCDelFlight(RAT.ATC.flight, name) + RAT._ATCDelFlight(RAT.ATC.flight, name) -- Increase landing counter to monitor traffic. airport.traffic=airport.traffic+1 @@ -5922,8 +5731,10 @@ function RAT:_ATCFlightLanded(name) end --- Creates a landing queue for all flights holding at airports. Aircraft with longest holding time gets first permission to land. --- @param #RAT self -function RAT:_ATCQueue() +function RAT._ATCQueue() + + -- Current time + local Tnow=timer.getTime() for airport,_ in pairs(RAT.ATC.airport) do @@ -5931,16 +5742,15 @@ function RAT:_ATCQueue() local _queue={} -- Loop over all flights. - for name,_ in pairs(RAT.ATC.flight) do - --fvh - local Tnow=timer.getTime() + for name,_flight in pairs(RAT.ATC.flight) do + local flight=_flight --#RAT.AtcFlight -- Update holding time (unless holing is set to onfinal=-100) - if RAT.ATC.flight[name].holding>=0 then - RAT.ATC.flight[name].holding=Tnow-RAT.ATC.flight[name].Tarrive + if flight.holding>=0 then + flight.holding=Tnow-flight.Tarrive end - local hold=RAT.ATC.flight[name].holding - local dest=RAT.ATC.flight[name].destination + local hold=flight.holding + local dest=flight.destination -- Flight is holding at this airport. if hold>=0 and airport==dest then From a924a0b641ffbb7728e8cea884120518beac19b5 Mon Sep 17 00:00:00 2001 From: Frank Date: Wed, 3 Apr 2024 21:56:24 +0200 Subject: [PATCH 16/26] Update RAT.lua --- Moose Development/Moose/Functional/RAT.lua | 93 ++++++++++++---------- 1 file changed, 52 insertions(+), 41 deletions(-) diff --git a/Moose Development/Moose/Functional/RAT.lua b/Moose Development/Moose/Functional/RAT.lua index 4643c73ac..d76057be2 100644 --- a/Moose Development/Moose/Functional/RAT.lua +++ b/Moose Development/Moose/Functional/RAT.lua @@ -2219,7 +2219,7 @@ function RAT:_SpawnWithRoute(_departure, _destination, _takeoff, _landing, _live -- Place markers of waypoints on F10 map. if self.placemarkers then - self:_PlaceMarkers(waypoints, self.SpawnIndex) + self:_PlaceMarkers(waypoints, wpdesc, self.SpawnIndex) end -- TODO: Use FLIGHTGROUP functions for invisible, immortal, etc. @@ -2334,8 +2334,10 @@ function RAT:_SpawnWithRoute(_departure, _destination, _takeoff, _landing, _live self:T(self.lid..string.format("RAT passed FINAL waypoint")) + local ratcraft=self:_GetRatcraftFromGroup(flightgroup.group) + -- Info message. - local text=string.format("Flight %s arrived at final destination %s.", group:GetName(), destination) + local text=string.format("Flight %s arrived at final destination %s.", group:GetName(), destination:GetName()) MESSAGE:New(text, 10):ToAllIf(self.reportstatus) self:T(self.lid..text) @@ -2362,9 +2364,11 @@ function RAT:_SpawnWithRoute(_departure, _destination, _takeoff, _landing, _live function flightgroup.OnAfterHolding(Flightgroup, From, Event, To) local flightgroup=Flightgroup --Ops.FlightGroup#FLIGHTGROUP + local ratcraft=self:_GetRatcraftFromGroup(flightgroup.group) + -- Aircraft arrived at holding point - local text=string.format("Flight %s to %s ATC: Holding and awaiting landing clearance.", group:GetName(), destination) - self:T(rat.lid..text) + local text=string.format("Flight %s to %s ATC: Holding and awaiting landing clearance.", group:GetName(), destination:GetName()) + self:T(self.lid..text) MESSAGE:New(text, 10):ToAllIf(self.reportstatus) @@ -3161,14 +3165,16 @@ function RAT:_SetRoute(takeoff, landing, _departure, _destination, _waypoint) -- Waypoints and coordinates local wp={} local c={} + local waypointdescriptions={} + local waypointstatus={} local wpholding=nil local wpfinal=nil -- Departure/Take-off c[#c+1]=Pdeparture wp[#wp+1]=self:_Waypoint(#wp+1, "Departure", takeoff, c[#wp+1], VxClimb, H_departure, departure) - self.waypointdescriptions[#wp]="Departure" - self.waypointstatus[#wp]=RAT.status.Departure + waypointdescriptions[#wp]="Departure" + waypointstatus[#wp]=RAT.status.Departure -- Climb if takeoff==RAT.wp.air then @@ -3182,8 +3188,8 @@ function RAT:_SetRoute(takeoff, landing, _departure, _destination, _waypoint) c[#c+1]=c[#c]:Translate(d_climb, heading) wp[#wp+1]=self:_Waypoint(#wp+1, "Begin of Cruise", RAT.wp.cruise, c[#wp+1], VxCruise, FLcruise) - self.waypointdescriptions[#wp]="Begin of Cruise" - self.waypointstatus[#wp]=RAT.status.Cruise + waypointdescriptions[#wp]="Begin of Cruise" + waypointstatus[#wp]=RAT.status.Cruise end else @@ -3193,12 +3199,12 @@ function RAT:_SetRoute(takeoff, landing, _departure, _destination, _waypoint) c[#c+1]=c[#c]:Translate(d_climb/2, heading) wp[#wp+1]=self:_Waypoint(#wp+1, "Climb", RAT.wp.climb, c[#wp+1], VxClimb, H_departure+(FLcruise-H_departure)/2) - self.waypointdescriptions[#wp]="Climb" - self.waypointstatus[#wp]=RAT.status.Climb + waypointdescriptions[#wp]="Climb" + waypointstatus[#wp]=RAT.status.Climb wp[#wp+1]=self:_Waypoint(#wp+1, "Begin of Cruise", RAT.wp.cruise, c[#wp+1], VxCruise, FLcruise) - self.waypointdescriptions[#wp]="Begin of Cruise" - self.waypointstatus[#wp]=RAT.status.Cruise + waypointdescriptions[#wp]="Begin of Cruise" + waypointstatus[#wp]=RAT.status.Cruise end @@ -3208,8 +3214,8 @@ function RAT:_SetRoute(takeoff, landing, _departure, _destination, _waypoint) if self.returnzone then c[#c+1]=Preturn wp[#wp+1]=self:_Waypoint(#wp+1, "Return Zone", RAT.wp.cruise, c[#wp+1], VxCruise, FLcruise) - self.waypointdescriptions[#wp]="Return Zone" - self.waypointstatus[#wp]=RAT.status.Uturn + waypointdescriptions[#wp]="Return Zone" + waypointstatus[#wp]=RAT.status.Uturn end if landing==RAT.wp.air then @@ -3217,23 +3223,23 @@ function RAT:_SetRoute(takeoff, landing, _departure, _destination, _waypoint) -- Next waypoint is already the final destination. c[#c+1]=Pdestination wp[#wp+1]=self:_Waypoint(#wp+1, "Final Destination", RAT.wp.finalwp, c[#wp+1], VxCruise, FLcruise) - self.waypointdescriptions[#wp]="Final Destination" - self.waypointstatus[#wp]=RAT.status.Destination + waypointdescriptions[#wp]="Final Destination" + waypointstatus[#wp]=RAT.status.Destination elseif self.returnzone then -- The little bit back to end of cruise. c[#c+1]=c[#c]:Translate(d_cruise/2, heading-180) wp[#wp+1]=self:_Waypoint(#wp+1, "End of Cruise", RAT.wp.cruise, c[#wp+1], VxCruise, FLcruise) - self.waypointdescriptions[#wp]="End of Cruise" - self.waypointstatus[#wp]=RAT.status.Descent + waypointdescriptions[#wp]="End of Cruise" + waypointstatus[#wp]=RAT.status.Descent else c[#c+1]=c[#c]:Translate(d_cruise, heading) wp[#wp+1]=self:_Waypoint(#wp+1, "End of Cruise", RAT.wp.cruise, c[#wp+1], VxCruise, FLcruise) - self.waypointdescriptions[#wp]="End of Cruise" - self.waypointstatus[#wp]=RAT.status.Descent + waypointdescriptions[#wp]="End of Cruise" + waypointstatus[#wp]=RAT.status.Descent end @@ -3242,13 +3248,13 @@ function RAT:_SetRoute(takeoff, landing, _departure, _destination, _waypoint) if self.returnzone then c[#c+1]=c[#c]:Translate(d_descent/2, heading-180) wp[#wp+1]=self:_Waypoint(#wp+1, "Descent", RAT.wp.descent, c[#wp+1], VxDescent, FLcruise-(FLcruise-(h_holding+H_holding))/2) - self.waypointdescriptions[#wp]="Descent" - self.waypointstatus[#wp]=RAT.status.DescentHolding + waypointdescriptions[#wp]="Descent" + waypointstatus[#wp]=RAT.status.DescentHolding else c[#c+1]=c[#c]:Translate(d_descent/2, heading) wp[#wp+1]=self:_Waypoint(#wp+1, "Descent", RAT.wp.descent, c[#wp+1], VxDescent, FLcruise-(FLcruise-(h_holding+H_holding))/2) - self.waypointdescriptions[#wp]="Descent" - self.waypointstatus[#wp]=RAT.status.DescentHolding + waypointdescriptions[#wp]="Descent" + waypointstatus[#wp]=RAT.status.DescentHolding end end @@ -3258,15 +3264,15 @@ function RAT:_SetRoute(takeoff, landing, _departure, _destination, _waypoint) -- Holding point (removed the holding point because FLIGHTGROUP sends group to holding point with RTB command after the last waypoint) -- c[#c+1]=Pholding -- wp[#wp+1]=self:_Waypoint(#wp+1, "Holding Point", RAT.wp.holding, c[#wp+1], VxHolding, H_holding+h_holding) --- self.waypointdescriptions[#wp]="Holding Point" --- self.waypointstatus[#wp]=RAT.status.Holding +-- waypointdescriptions[#wp]="Holding Point" +-- waypointstatus[#wp]=RAT.status.Holding -- wpholding=#wp -- Final destination (leave this in because FLIGHTGROUP needs to know that we want to land and removes the landing waypoint automatically) c[#c+1]=Pdestination wp[#wp+1]=self:_Waypoint(#wp+1, "Final Destination", landing, c[#wp+1], VxFinal, H_destination, destination) - self.waypointdescriptions[#wp]="Final Destination" - self.waypointstatus[#wp]=RAT.status.Destination + waypointdescriptions[#wp]="Final Destination" + waypointstatus[#wp]=RAT.status.Destination end @@ -3280,14 +3286,14 @@ function RAT:_SetRoute(takeoff, landing, _departure, _destination, _waypoint) end -- Some info on the route. - self:_Routeinfo(waypoints, "Waypoint info in set_route:") + self:_Routeinfo(waypoints, "Waypoint info in set_route:", waypointdescriptions) -- Return departure, destination and waypoints. if self.returnzone then -- We return the actual zone here because returning the departure leads to problems with commute. - return departure, destination_returnzone, waypoints, wpholding, wpfinal, self.waypointdescriptions, self.waypointstatus + return departure, destination_returnzone, waypoints, waypointdescriptions, waypointstatus else - return departure, destination, waypoints, wpholding, wpfinal, self.waypointdescriptions, self.waypointstatus + return departure, destination, waypoints, waypointdescriptions, waypointstatus end end @@ -4526,8 +4532,9 @@ end -- @param #RAT self -- @param #table waypoints Waypoints of the flight plan. -- @param #string comment Some comment to identify the provided information. +-- @param #table waypointdescriptions Waypoint descriptions. -- @return #number total Total route length in meters. -function RAT:_Routeinfo(waypoints, comment) +function RAT:_Routeinfo(waypoints, comment, waypointdescriptions) local text=string.format("\n******************************************************\n") text=text..string.format("Template = %s\n", self.SpawnTemplatePrefix) if comment then @@ -4537,7 +4544,7 @@ function RAT:_Routeinfo(waypoints, comment) -- info on coordinate and altitude for i=1,#waypoints do local p=waypoints[i] - text=text..string.format("WP #%i: x = %6.1f km, y = %6.1f km, alt = %6.1f m %s\n", i-1, p.x/1000, p.y/1000, p.alt, self.waypointdescriptions[i]) + text=text..string.format("WP #%i: x = %6.1f km, y = %6.1f km, alt = %6.1f m %s\n", i-1, p.x/1000, p.y/1000, p.alt, waypointdescriptions[i]) end -- info on distance between waypoints local total=0.0 @@ -4551,7 +4558,7 @@ function RAT:_Routeinfo(waypoints, comment) local d=math.sqrt((x1-x2)^2 + (y1-y2)^2) local heading=self:_Course(point1, point2) total=total+d - text=text..string.format("Distance from WP %i-->%i = %6.1f km. Heading = %03d : %s - %s\n", i-1, i, d/1000, heading, self.waypointdescriptions[i], self.waypointdescriptions[i+1]) + text=text..string.format("Distance from WP %i-->%i = %6.1f km. Heading = %03d : %s - %s\n", i-1, i, d/1000, heading, waypointdescriptions[i], waypointdescriptions[i+1]) end text=text..string.format("Total distance = %6.1f km\n", total/1000) text=text..string.format("******************************************************\n") @@ -4996,12 +5003,13 @@ end --- Place markers of the waypoints. Note we assume a very specific number and type of waypoints here. -- @param #RAT self -- @param #table waypoints Table with waypoints. +-- @param #table waypointdescriptions Table with waypoint descriptions -- @param #number index Spawn index of group. -function RAT:_PlaceMarkers(waypoints, index) +function RAT:_PlaceMarkers(waypoints, waypointdescriptions, index) for i=1,#waypoints do - self:_SetMarker(self.waypointdescriptions[i], waypoints[i], index) + self:_SetMarker(waypointdescriptions[i], waypoints[i], index) if self.Debug then - local text=string.format("Marker at waypoint #%d: %s for flight #%d", i, self.waypointdescriptions[i], index) + local text=string.format("Marker at waypoint #%d: %s for flight #%d", i, waypointdescriptions[i], index) self:T2(self.lid..text) end end @@ -5488,6 +5496,7 @@ function RAT:_ATCAddFlight(name, dest) flight.Tarrive=-1 flight.holding=-1 flight.Tarrive=-1 + --flight.Tonfinal=-1 RAT.ATC.flight[name]=flight end @@ -5498,6 +5507,7 @@ end function RAT._ATCDelFlight(t,entry) for k,_ in pairs(t) do if k==entry then + BASE:I(RAT.id..string.format("Removing flight %s from queue", entry)) t[entry]=nil end end @@ -5548,7 +5558,7 @@ function RAT._ATCStatus() elseif hold==RAT.ATC.onfinal then -- Aircarft is on final approach for landing. - local Tfinal=Tnow-RAT.ATC.flight[name].Tonfinal + local Tfinal=Tnow-flight.Tonfinal local text=string.format("ATC %s: Flight %s is on final. Waiting %i:%02d for landing event.", dest, name, Tfinal/60, Tfinal%60) BASE:I(RAT.id..text) @@ -5605,13 +5615,13 @@ function RAT._ATCCheck() -- Debug message. local text=string.format("ATC %s: Flight %s runway is busy. You are #%d of %d in landing queue. Your holding time is %i:%02d.", airportname, flightname, qID, nqueue, flight.holding/60, flight.holding%60) - BASE:I(self.lid..text) + BASE:I(RAT.id..text) else local text=string.format("ATC %s: Flight %s was cleared for landing. Your holding time was %i:%02d.", airportname, flightname, flight.holding/60, flight.holding%60) - BASE:I(self.lid..text) + BASE:I(RAT.id..text) -- Clear flight for landing. RAT._ATCClearForLanding(airportname, flightname) @@ -5656,7 +5666,7 @@ function RAT._ATCClearForLanding(airportname, flightname) airport.busy=true -- Flight which is landing. - airport.onfinal[flight]=flight + airport.onfinal[flightname]=flight -- Number of planes on final approach. airport.Nonfinal=airport.Nonfinal+1 @@ -5749,6 +5759,7 @@ function RAT._ATCQueue() if flight.holding>=0 then flight.holding=Tnow-flight.Tarrive end + local hold=flight.holding local dest=flight.destination From 5fd8139f0069962bcdf4dac976403ebeea644f1e Mon Sep 17 00:00:00 2001 From: Frank Date: Thu, 4 Apr 2024 14:21:52 +0200 Subject: [PATCH 17/26] Update RAT.lua - RATMANAGER --- Moose Development/Moose/Functional/RAT.lua | 150 +++++++++++---------- 1 file changed, 80 insertions(+), 70 deletions(-) diff --git a/Moose Development/Moose/Functional/RAT.lua b/Moose Development/Moose/Functional/RAT.lua index d76057be2..3ff6fb91b 100644 --- a/Moose Development/Moose/Functional/RAT.lua +++ b/Moose Development/Moose/Functional/RAT.lua @@ -5873,7 +5873,7 @@ function RATMANAGER:New(ntot) self.ntot=ntot or 1 -- Debug info - self:E(RATMANAGER.id..string.format("Creating manager for %d groups.", ntot)) + self:I(RATMANAGER.id..string.format("Creating manager for %d groups", ntot)) return self end @@ -5915,68 +5915,71 @@ end function RATMANAGER:Start(delay) -- Time delay. - local delay=delay or 5 + delay=delay or 5 - -- Info text. - local text=string.format(RATMANAGER.id.."RAT manager will be started in %d seconds.\n", delay) - text=text..string.format("Managed groups:\n") - for i=1,self.nrat do - text=text..string.format("- %s with min groups %d\n", self.name[i], self.min[i]) + if delay and delay>0 then + + -- Info text. + local text=string.format(RATMANAGER.id.."RAT manager will be started in %d seconds.\n", delay) + text=text..string.format("Managed groups:\n") + for i=1,self.nrat do + text=text..string.format("- %s with min groups %d\n", self.name[i], self.min[i]) + end + text=text..string.format("Number of constantly alive groups %d", self.ntot) + self:E(text) + + -- Delayed call + self:ScheduleOnce(delay, RATMANAGER.Start, self, 0) + + else + + -- Ensure that ntot is at least sum of min RAT groups. + local n=0 + for i=1,self.nrat do + n=n+self.min[i] + end + self.ntot=math.max(self.ntot, n) + + -- Get randum number of new RAT groups. + local N=self:_RollDice(self.nrat, self.ntot, self.min, self.alive) + + -- Loop over all RAT objects and spawn groups. + local time=0.0 + for i=1,self.nrat do + for j=1,N[i] do + time=time+self.dTspawn + --SCHEDULER:New(nil, RAT._SpawnWithRoute, {self.rat[i]}, time) + self:ScheduleOnce(time, RAT._SpawnWithRoute, self.rat[i]) + end + end + + -- Start activation scheduler for uncontrolled aircraft. + for i=1,self.nrat do + local rat=self.rat[i] --#RAT + if rat.uncontrolled and rat.activate_uncontrolled then + -- Start activating stuff but not before the latest spawn has happend. + local Tactivate=math.max(time+1, rat.activate_delay) + --SCHEDULER:New(self.rat[i], self.rat[i]._ActivateUncontrolled, {self.rat[i]}, Tactivate, self.rat[i].activate_delta, self.rat[i].activate_frand) + self:ScheduleRepeat(Tactivate,rat.activate_delta, rat.activate_frand, nil,rat._ActivateUncontrolled, rat) + end + end + + -- Start the manager. But not earlier than the latest spawn has happened! + local TstartManager=math.max(time+1, self.Tcheck) + + -- Start manager scheduler. + self.manager, self.managerid = SCHEDULER:New(self, self._Manage, {self}, TstartManager, self.Tcheck) --Core.Scheduler#SCHEDULER + + -- Info + local text=string.format(RATMANAGER.id.."Starting RAT manager with scheduler ID %s in %d seconds. Repeat interval %d seconds.", self.managerid, TstartManager, self.Tcheck) + self:I(text) + + end - text=text..string.format("Number of constantly alive groups %d", self.ntot) - self:E(text) - - -- Start scheduler. - SCHEDULER:New(nil, self._Start, {self}, delay) return self end ---- Instantly starts the RAT manager and spawns the initial random number RAT groups for each RAT object. --- @param #RATMANAGER self --- @return #RATMANAGER RATMANAGER self object. -function RATMANAGER:_Start() - - -- Ensure that ntot is at least sum of min RAT groups. - local n=0 - for i=1,self.nrat do - n=n+self.min[i] - end - self.ntot=math.max(self.ntot, n) - - -- Get randum number of new RAT groups. - local N=self:_RollDice(self.nrat, self.ntot, self.min, self.alive) - - -- Loop over all RAT objects and spawn groups. - local time=0.0 - for i=1,self.nrat do - for j=1,N[i] do - time=time+self.dTspawn - SCHEDULER:New(nil, RAT._SpawnWithRoute, {self.rat[i]}, time) - end - end - - -- Start activation scheduler for uncontrolled aircraft. - for i=1,self.nrat do - if self.rat[i].uncontrolled and self.rat[i].activate_uncontrolled then - -- Start activating stuff but not before the latest spawn has happend. - local Tactivate=math.max(time+1, self.rat[i].activate_delay) - SCHEDULER:New(self.rat[i], self.rat[i]._ActivateUncontrolled, {self.rat[i]}, Tactivate, self.rat[i].activate_delta, self.rat[i].activate_frand) - end - end - - -- Start the manager. But not earlier than the latest spawn has happened! - local TstartManager=math.max(time+1, self.Tcheck) - - -- Start manager scheduler. - self.manager, self.managerid = SCHEDULER:New(self, self._Manage, {self}, TstartManager, self.Tcheck) --Core.Scheduler#SCHEDULER - - -- Info - local text=string.format(RATMANAGER.id.."Starting RAT manager with scheduler ID %s in %d seconds. Repeat interval %d seconds.", self.managerid, TstartManager, self.Tcheck) - self:E(text) - - return self -end --- Stops the RAT manager. -- @param #RATMANAGER self @@ -5984,19 +5987,26 @@ end -- @return #RATMANAGER RATMANAGER self object. function RATMANAGER:Stop(delay) delay=delay or 1 - self:E(string.format(RATMANAGER.id.."Manager will be stopped in %d seconds.", delay)) - SCHEDULER:New(nil, self._Stop, {self}, delay) + + + if delay and delay>0 then + + self:I(RATMANAGER.id..string.format("Manager will be stopped in %d seconds.", delay)) + + self:ScheduleOnce(delay, RATMANAGER.Stop, self, 0) + + else + + self:I(RATMANAGER.id..string.format("Stopping manager with scheduler ID %s", self.managerid)) + + self.manager:Stop(self.managerid) + + end + + return self end ---- Instantly stops the RAT manager by terminating its scheduler. --- @param #RATMANAGER self --- @return #RATMANAGER RATMANAGER self object. -function RATMANAGER:_Stop() - self:E(string.format(RATMANAGER.id.."Stopping manager with scheduler ID %s.", self.managerid)) - self.manager:Stop(self.managerid) - return self -end --- Sets the time interval between checks of alive RAT groups. Default is 60 seconds. -- @param #RATMANAGER self @@ -6025,8 +6035,7 @@ function RATMANAGER:_Manage() local ntot=self:_Count() -- Debug info. - local text=string.format("Number of alive groups %d. New groups to be spawned %d.", ntot, self.ntot-ntot) - self:T(RATMANAGER.id..text) + self:T(RATMANAGER.id..string.format("Number of alive groups %d. New groups to be spawned %d.", ntot, self.ntot-ntot)) -- Get number of necessary spawns. local N=self:_RollDice(self.nrat, self.ntot, self.min, self.alive) @@ -6037,7 +6046,8 @@ function RATMANAGER:_Manage() for j=1,N[i] do time=time+self.dTspawn self.planned[i]=self.planned[i]+1 - SCHEDULER:New(nil, RATMANAGER._Spawn, {self, i}, time) + --SCHEDULER:New(nil, RATMANAGER._Spawn, {self, i}, time) + self:ScheduleOnce(time, RATMANAGER._Spawn, self, i) end end end From 08fb4e3736d10b1bd52d9bdd0004006b816e5a2a Mon Sep 17 00:00:00 2001 From: Frank Date: Thu, 4 Apr 2024 19:08:18 +0200 Subject: [PATCH 18/26] Update RAT.lua - Improved ATC and FLIGHTCONTROL integration --- Moose Development/Moose/Functional/RAT.lua | 35 +++++++++++++++------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/Moose Development/Moose/Functional/RAT.lua b/Moose Development/Moose/Functional/RAT.lua index 3ff6fb91b..cddecb8e7 100644 --- a/Moose Development/Moose/Functional/RAT.lua +++ b/Moose Development/Moose/Functional/RAT.lua @@ -2196,9 +2196,10 @@ function RAT:_SpawnWithRoute(_departure, _destination, _takeoff, _landing, _live -- Create a flightgroup object. local flightgroup=FLIGHTGROUP:New(group) - -- Setting holding time to nil so that flight never gets landing clearance. - -- TODO: Make this dependent on ATC switch. - flightgroup.holdtime=nil + -- Setting holding time to nil so that flight never gets landing clearance. This is done by the RAT ATC (FC sets holdtime to nil in FLIGHTGROUP). + if self.ATCswitch then + flightgroup.holdtime=nil + end -- No automatic despawning if group gets stuck. flightgroup.stuckDespawn=false @@ -2210,10 +2211,16 @@ function RAT:_SpawnWithRoute(_departure, _destination, _takeoff, _landing, _live -- ATC is monitoring this flight (if it is supposed to land). if self.ATCswitch and landing==RAT.wp.landing then + + -- Get destination airbase name. For returnzone, this is the departure airbase. + local airbasename=destination:GetName() if self.returnzone then - self:_ATCAddFlight(groupname, departure:GetName()) - else - self:_ATCAddFlight(groupname, destination:GetName()) + airbasename=departure:GetName() + end + + -- Add flight (if there is no FC at the airbase) + if not _DATABASE:GetFlightControl(airbasename) then + self:_ATCAddFlight(groupname, airbasename) end end @@ -2366,14 +2373,18 @@ function RAT:_SpawnWithRoute(_departure, _destination, _takeoff, _landing, _live local ratcraft=self:_GetRatcraftFromGroup(flightgroup.group) + local destinationname=ratcraft.destination:GetName() + -- Aircraft arrived at holding point - local text=string.format("Flight %s to %s ATC: Holding and awaiting landing clearance.", group:GetName(), destination:GetName()) + local text=string.format("Flight %s to %s ATC: Holding and awaiting landing clearance.", groupname, destinationname) self:T(self.lid..text) MESSAGE:New(text, 10):ToAllIf(self.reportstatus) - - -- Register aircraft at ATC. - if self.ATCswitch then + -- Get FLIGHTCONTROL if there is any. + local fc=_DATABASE:GetFlightControl(destinationname) + + -- Register aircraft at RAT ATC (but only if there is no FLIGHTCONTROL) + if self.ATCswitch and not fc then self:T(self.lid..string.format("RAT group is HOLDING ==> ATCRegisterFlight")) -- Create F10 menu @@ -2386,10 +2397,12 @@ function RAT:_SpawnWithRoute(_departure, _destination, _takeoff, _landing, _live end end - function flightgroup.OnAfterLandAtAirbase(Flightgroup, From, Event, To, Airport) + --- Function called when the group landed at an airbase. + function flightgroup.OnAfterLanded(Flightgroup, From, Event, To, Airport) self:T(self.lid..string.format("RAT group landed at airbase")) end + --- Function called when the group arrived at their parking positions. function flightgroup.OnAfterArrived(Flightgroup, From, Event, To) self:T(self.lid..string.format("RAT group arrived")) end From 532cc0b4df84bd049bcca03e18d6f7b137ec4788 Mon Sep 17 00:00:00 2001 From: Frank Date: Thu, 4 Apr 2024 23:39:56 +0200 Subject: [PATCH 19/26] RAT - options set via flightgroup - fixed little bug in OPSGROUP emission default --- Moose Development/Moose/Functional/RAT.lua | 101 ++++++++++----------- Moose Development/Moose/Ops/OpsGroup.lua | 4 +- 2 files changed, 52 insertions(+), 53 deletions(-) diff --git a/Moose Development/Moose/Functional/RAT.lua b/Moose Development/Moose/Functional/RAT.lua index cddecb8e7..fd567806e 100644 --- a/Moose Development/Moose/Functional/RAT.lua +++ b/Moose Development/Moose/Functional/RAT.lua @@ -2228,29 +2228,30 @@ function RAT:_SpawnWithRoute(_departure, _destination, _takeoff, _landing, _live if self.placemarkers then self:_PlaceMarkers(waypoints, wpdesc, self.SpawnIndex) end - - -- TODO: Use FLIGHTGROUP functions for invisible, immortal, etc. -- Set group to be invisible. if self.invisible then - self:_CommandInvisible(group, true) + flightgroup:SetDefaultInvisible(true) + flightgroup:SwitchInvisible(true) end -- Set group to be immortal. if self.immortal then - self:_CommandImmortal(group, true) + flightgroup:SetDefaultImmortal(true) + flightgroup:SwitchImmortal(true) end -- Set group to be immortal. if self.eplrs then - group:CommandEPLRS(true, 1) + flightgroup:SetDefaultEPLRS(true) + flightgroup:SwitchEPLRS(true) end -- Set ROE, default is "weapon hold". - self:_SetROE(group, self.roe) + self:_SetROE(flightgroup, self.roe) -- Set ROT, default is "no reaction". - self:_SetROT(group, self.rot) + self:_SetROT(flightgroup, self.rot) -- Init ratcraft array. local ratcraft={} --#RAT.RatCraft @@ -4651,42 +4652,22 @@ function RAT:_ActivateUncontrolled() if departureFlightControl then self:T(self.lid..string.format("RAT group %s is ready for takeoff", group:GetName())) - ratcraft.flightgroup:SetReadyForTakeoff(true) - ratcraft.active=true + ratcraft.flightgroup:SetReadyForTakeoff(true) else -- Start aircraft. self:T(self.lid..string.format("RAT group %s is switching to controlled now", group:GetName())) - self:_CommandStartUncontrolled(group) + ratcraft.flightgroup:StartUncontrolled() end + + -- Set status to active. + ratcraft.active=true + + -- Set status to "Ready and Starting Engines". + self:_SetStatus(group, RAT.status.EventBirth) end end ---- Start uncontrolled aircraft group. --- @param #RAT self --- @param Wrapper.Group#GROUP group Group to be activated. -function RAT:_CommandStartUncontrolled(group) - - -- Start command. - local StartCommand = {id = 'Start', params = {}} - - -- Debug message - local text=string.format("Uncontrolled: Activating group %s.", group:GetName()) - self:T(self.lid..text) - - -- Activate group. - group:SetCommand(StartCommand) - - -- Spawn index. - local ratcraft=self:_GetRatcraftFromGroup(group) - - -- Set status to active. - ratcraft.active=true - - -- Set status to "Ready and Starting Engines". - self:_SetStatus(group, RAT.status.EventBirth) -end - --- Set RAT group to (in-)visible for other AI forces. -- @param #RAT self -- @param Wrapper.Group#GROUP group Group to be set (in)visible. @@ -4888,33 +4869,51 @@ end --- Set ROE for a group. -- @param #RAT self --- @param Wrapper.Group#GROUP group Group for which the ROE is set. +-- @param Ops.FlightGroup#FLIGHTGROUP flightgroup Group for which the ROE is set. -- @param #string roe ROE of group. -function RAT:_SetROE(group, roe) - self:T(self.lid.."Setting ROE to "..roe.." for group "..group:GetName()) - if self.roe==RAT.ROE.returnfire then - group:OptionROEReturnFire() - elseif self.roe==RAT.ROE.weaponfree then - group:OptionROEWeaponFree() +function RAT:_SetROE(flightgroup, roe) + + roe=roe or self.roe + + self:T(self.lid.."Setting ROE to "..roe.." for group "..flightgroup:GetName()) + + local _roe=ENUMS.ROE.WeaponHold + if roe==RAT.ROE.returnfire then + _roe=ENUMS.ROE.ReturnFire + elseif roe==RAT.ROE.weaponfree then + _roe=ENUMS.ROE.OpenFireWeaponFree else - group:OptionROEHoldFire() + end + + flightgroup:SetDefaultROE(_roe) + flightgroup:SwitchROE(_roe) + end --- Set ROT for a group. -- @param #RAT self --- @param Wrapper.Group#GROUP group Group for which the ROT is set. +-- @param Ops.FlightGroup#FLIGHTGROUP flightgroup Group for which the ROT is set. -- @param #string rot ROT of group. -function RAT:_SetROT(group, rot) - self:T(self.lid.."Setting ROT to "..rot.." for group "..group:GetName()) - if self.rot==RAT.ROT.passive then - group:OptionROTPassiveDefense() - elseif self.rot==RAT.ROT.evade then - group:OptionROTEvadeFire() +function RAT:_SetROT(flightgroup, rot) + + rot=rot or self.rot + + self:T(self.lid.."Setting ROT to "..rot.." for group "..flightgroup:GetName()) + + local _rot=ENUMS.ROT.NoReaction + if rot==RAT.ROT.passive then + _rot=ENUMS.ROT.PassiveDefense + elseif rot==RAT.ROT.evade then + _rot=ENUMS.ROT.EvadeFire else - group:OptionROTNoReaction() + end + + flightgroup:SetDefaultROT(_rot) + flightgroup:SwitchROT(_rot) + end diff --git a/Moose Development/Moose/Ops/OpsGroup.lua b/Moose Development/Moose/Ops/OpsGroup.lua index 556b13a1a..d8ae870b1 100644 --- a/Moose Development/Moose/Ops/OpsGroup.lua +++ b/Moose Development/Moose/Ops/OpsGroup.lua @@ -12031,7 +12031,7 @@ function OPSGROUP:GetEPLRS() return self.option.EPLRS or self.optionDefault.EPLRS end ---- Set the default EPLRS for the group. +--- Set the default emission state for the group. -- @param #OPSGROUP self -- @param #boolean OnOffSwitch If `true`, EPLRS is on by default. If `false` default EPLRS setting is off. If `nil`, default is on if group has EPLRS and off if it does not have a datalink. -- @return #OPSGROUP self @@ -12040,7 +12040,7 @@ function OPSGROUP:SetDefaultEmission(OnOffSwitch) if OnOffSwitch==nil then self.optionDefault.Emission=true else - self.optionDefault.EPLRS=OnOffSwitch + self.optionDefault.Emission=OnOffSwitch end return self From 473001c95b851dcec90d23c2bd660b28cf1f0aef Mon Sep 17 00:00:00 2001 From: Frank Date: Thu, 4 Apr 2024 23:50:24 +0200 Subject: [PATCH 20/26] Update RAT.lua --- Moose Development/Moose/Functional/RAT.lua | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Moose Development/Moose/Functional/RAT.lua b/Moose Development/Moose/Functional/RAT.lua index fd567806e..c9816042e 100644 --- a/Moose Development/Moose/Functional/RAT.lua +++ b/Moose Development/Moose/Functional/RAT.lua @@ -2299,14 +2299,14 @@ function RAT:_SpawnWithRoute(_departure, _destination, _takeoff, _landing, _live self.Menu[self.SubMenuName].groups[self.SpawnIndex]=MENU_MISSION:New(name, self.Menu[self.SubMenuName].groups) -- F10/RAT//Group X/Set ROE self.Menu[self.SubMenuName].groups[self.SpawnIndex]["roe"]=MENU_MISSION:New("Set ROE", self.Menu[self.SubMenuName].groups[self.SpawnIndex]) - MENU_MISSION_COMMAND:New("Weapons hold", self.Menu[self.SubMenuName].groups[self.SpawnIndex]["roe"], self._SetROE, self, group, RAT.ROE.weaponhold) - MENU_MISSION_COMMAND:New("Weapons free", self.Menu[self.SubMenuName].groups[self.SpawnIndex]["roe"], self._SetROE, self, group, RAT.ROE.weaponfree) - MENU_MISSION_COMMAND:New("Return fire", self.Menu[self.SubMenuName].groups[self.SpawnIndex]["roe"], self._SetROE, self, group, RAT.ROE.returnfire) + MENU_MISSION_COMMAND:New("Weapons hold", self.Menu[self.SubMenuName].groups[self.SpawnIndex]["roe"], self._SetROE, self, flightgroup, RAT.ROE.weaponhold) + MENU_MISSION_COMMAND:New("Weapons free", self.Menu[self.SubMenuName].groups[self.SpawnIndex]["roe"], self._SetROE, self, flightgroup, RAT.ROE.weaponfree) + MENU_MISSION_COMMAND:New("Return fire", self.Menu[self.SubMenuName].groups[self.SpawnIndex]["roe"], self._SetROE, self, flightgroup, RAT.ROE.returnfire) -- F10/RAT//Group X/Set ROT self.Menu[self.SubMenuName].groups[self.SpawnIndex]["rot"]=MENU_MISSION:New("Set ROT", self.Menu[self.SubMenuName].groups[self.SpawnIndex]) - MENU_MISSION_COMMAND:New("No reaction", self.Menu[self.SubMenuName].groups[self.SpawnIndex]["rot"], self._SetROT, self, group, RAT.ROT.noreaction) - MENU_MISSION_COMMAND:New("Passive defense", self.Menu[self.SubMenuName].groups[self.SpawnIndex]["rot"], self._SetROT, self, group, RAT.ROT.passive) - MENU_MISSION_COMMAND:New("Evade on fire", self.Menu[self.SubMenuName].groups[self.SpawnIndex]["rot"], self._SetROT, self, group, RAT.ROT.evade) + MENU_MISSION_COMMAND:New("No reaction", self.Menu[self.SubMenuName].groups[self.SpawnIndex]["rot"], self._SetROT, self, flightgroup, RAT.ROT.noreaction) + MENU_MISSION_COMMAND:New("Passive defense", self.Menu[self.SubMenuName].groups[self.SpawnIndex]["rot"], self._SetROT, self, flightgroup, RAT.ROT.passive) + MENU_MISSION_COMMAND:New("Evade on fire", self.Menu[self.SubMenuName].groups[self.SpawnIndex]["rot"], self._SetROT, self, flightgroup, RAT.ROT.evade) -- F10/RAT//Group X/ MENU_MISSION_COMMAND:New("Despawn group", self.Menu[self.SubMenuName].groups[self.SpawnIndex], self._Despawn, self, group) MENU_MISSION_COMMAND:New("Place markers", self.Menu[self.SubMenuName].groups[self.SpawnIndex], self._PlaceMarkers, self, waypoints, self.SpawnIndex) From 7d3f1235e75848b0e6ca077cc852eff0a7d15f35 Mon Sep 17 00:00:00 2001 From: Frank Date: Fri, 5 Apr 2024 23:57:54 +0200 Subject: [PATCH 21/26] Update RAT.lua - respawn on landing adjustment --- Moose Development/Moose/Functional/RAT.lua | 27 ++++++++-------------- 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/Moose Development/Moose/Functional/RAT.lua b/Moose Development/Moose/Functional/RAT.lua index c9816042e..35fc177c4 100644 --- a/Moose Development/Moose/Functional/RAT.lua +++ b/Moose Development/Moose/Functional/RAT.lua @@ -582,6 +582,8 @@ RAT.version={ ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --TODO list: +--TODO: Add unlimited fuel option (and disable range check). This also needs to be added to FLIGHTGROUP +--TODO: --TODO: Add max number of spawns --TODO: Add Stop function --TODO: Integrate FLIGHTGROUP @@ -1487,14 +1489,12 @@ end --- Make aircraft respawn the moment they land rather than at engine shut down. -- @param #RAT self --- @param #number delay (Optional) Delay in seconds until respawn happens after landing. Default is 180 seconds. Minimum is 1.0 seconds. +-- @param #number delay (Optional) Delay in seconds until respawn happens after landing. Default is 1 second. Minimum is 1 second. -- @return #RAT RAT self object. function RAT:RespawnAfterLanding(delay) self:F2(delay) - delay = delay or 180 self.respawn_at_landing=true - delay=math.max(1.0, delay) - self.respawn_delay=delay + self:SetRespawnDelay(delay) return self end @@ -2432,11 +2432,11 @@ function RAT:ClearForLanding(name) self:T(self.lid.."ATC: User flag value (landing) for "..name.." set to "..flagvalue) end ---- Respawn a group. The original group is despawned and a new group is spawed. +--- Despawn the original group and re-spawn a new one. -- @param #RAT self -- @param Wrapper.Group#GROUP group The group that should be respawned. -- @param Core.Point#COORDINATE lastpos Last known position of the group. --- @param #number delay Delay before respawn in seconds. +-- @param #number delay Delay before despawn in seconds. function RAT:_Respawn(group, lastpos, delay) if delay and delay>0 then @@ -2637,14 +2637,7 @@ function RAT:_Respawn(group, lastpos, delay) self:T2({departure=_departure, destination=_destination, takeoff=_takeoff, landing=_landing, livery=_livery, lastwp=_lastwp}) -- We should give it at least 3 sec since this seems to be the time until free parking spots after despawn are available again (Sirri Island test). - local respawndelay - if delay then - respawndelay=delay - elseif self.respawn_delay then - respawndelay=self.respawn_delay+3 -- despawn happens after self.respawndelay. We add another 3 sec for free parking. - else - respawndelay=3 - end + local respawndelay=self.respawn_delay or 1 -- Spawn new group. self:T(self.lid..string.format("%s delayed respawn in %.1f seconds.", self.alias, respawndelay)) @@ -2657,11 +2650,9 @@ end --- Despawn group. The `FLIGHTGROUP` is despawned and stopped. The ratcraft is removed from the self.ratcraft table. Menues are removed. -- @param #RAT self -- @param Wrapper.Group#GROUP group Group to be despawned. --- @param #number delay Delay in seconds before the despawn happens. Default is `self.respawn_delay`. +-- @param #number delay Delay in seconds before the despawn happens. Default is immidiately. function RAT:_Despawn(group, delay) - delay=delay or self.respawn_delay - if delay and delay>0 then -- Delayed call. self:ScheduleOnce(delay, RAT._Despawn, self, group, 0) @@ -3785,7 +3776,7 @@ function RAT:Status(message, forID) -- Despawn old group. if self.despawnair then self:T(self.lid..string.format("[STATUS despawnme] Flight %s will be despawned NOW and NO new group is created!", self.alias)) - self:_Despawn(group, 0) + self:_Despawn(group) end else From 21412e0061192fae221c46757466d17722884f27 Mon Sep 17 00:00:00 2001 From: Frank Date: Mon, 8 Apr 2024 01:18:04 +0200 Subject: [PATCH 22/26] Update RAT.lua - Added Stop function --- Moose Development/Moose/Functional/RAT.lua | 58 +++++++++++++++++++--- 1 file changed, 52 insertions(+), 6 deletions(-) diff --git a/Moose Development/Moose/Functional/RAT.lua b/Moose Development/Moose/Functional/RAT.lua index 35fc177c4..c0538499f 100644 --- a/Moose Development/Moose/Functional/RAT.lua +++ b/Moose Development/Moose/Functional/RAT.lua @@ -146,6 +146,7 @@ -- @field #boolean parkingverysafe If true, parking spots are considered as non-free until a possible aircraft has left and taken off. Default false. -- @field #boolean despawnair If true, aircraft are despawned when they reach their destination zone. Default. -- @field #boolean eplrs If true, turn on EPLSR datalink for the RAT group. +-- @field #number NspawnMax Max number of spawns. -- @extends Core.Spawn#SPAWN --- Implements an easy to use way to randomly fill your map with AI aircraft. @@ -683,8 +684,47 @@ end --- Stop RAT spawning by unhandling events, stoping schedulers etc. -- @param #RAT self -function RAT:Stop() - -- TODO +-- @param #number delay Delay before stop in seconds. +function RAT:Stop(delay) + self:T3(self.lid..string.format("Stopping RAT! Delay %s sec!", tostring(delay))) + + if delay and delay>0 then + self:T2(self.lid..string.format("Stopping RAT in %d sec!", delay)) + self:ScheduleOnce(delay, RAT.Stop, self) + else + + self:T(self.lid.."Stopping RAT: Clearing schedulers and unhandling events!") + + if self.sid_Activate then + self.Scheduler:ScheduleStop(self.sid_Activate) + end + + if self.sid_Spawn then + self.Scheduler:ScheduleStop(self.sid_Spawn) + end + + if self.sid_Status then + self.Scheduler:ScheduleStop(self.sid_Status) + end + + + if self.Scheduler then + self.Scheduler:Clear() + end + + self.norespawn=true + + -- Un-Handle events. + self:UnHandleEvent(EVENTS.Birth) + self:UnHandleEvent(EVENTS.EngineStartup) + self:UnHandleEvent(EVENTS.Takeoff) + self:UnHandleEvent(EVENTS.Land) + self:UnHandleEvent(EVENTS.EngineShutdown) + self:UnHandleEvent(EVENTS.Dead) + self:UnHandleEvent(EVENTS.Crash) + self:UnHandleEvent(EVENTS.Hit) + + end end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -863,8 +903,6 @@ function RAT:Spawn(naircraft) local Tstop=Tstart+dt*(self.ngroups-1) -- Status check and report scheduler. - --SCHEDULER:New(nil, self.Status, {self}, Tstart+1, self.statusinterval) - --self.sid_Status=self:ScheduleRepeat(Start,Repeat,RandomizeFactor,Stop,SchedulerFunction,...) self.sid_Status=self:ScheduleRepeat(Tstart+1, self.statusinterval, nil, nil, RAT.Status, self) @@ -884,12 +922,10 @@ function RAT:Spawn(naircraft) end -- Start scheduled spawning. - --SCHEDULER:New(nil, self._SpawnWithRoute, {self}, Tstart, dt, 0.0, Tstop) self.sid_Spawn=self:ScheduleRepeat(Tstart, dt, 0.0, Tstop, RAT._SpawnWithRoute, self) -- Start scheduled activation of uncontrolled groups. if self.uncontrolled and self.activate_uncontrolled then - --SCHEDULER:New(nil, self._ActivateUncontrolled, {self}, self.activate_delay, self.activate_delta, self.activate_frand) self.sid_Activate=self:ScheduleRepeat(self.activate_delay, self.activate_delta, self.activate_frand, nil, RAT._ActivateUncontrolled, self) end @@ -2134,6 +2170,14 @@ end -- @return #number Spawn index. function RAT:_SpawnWithRoute(_departure, _destination, _takeoff, _landing, _livery, _waypoint, _lastpos, _nrespawn, parkingdata) self:F({rat=self.lid, departure=_departure, destination=_destination, takeoff=_takeoff, landing=_landing, livery=_livery, waypoint=_waypoint, lastpos=_lastpos, nrespawn=_nrespawn}) + + -- Check if max spawn limit exists and is reached. SpawnIndex counting starts at 0, hence greater equal and not greater. + if self.NspawnMax and self.SpawnIndex>=self.NspawnMax then + self:T(self.lid..string.format("Max limit of spawns reached %d >= %d! Will not spawn any more groups", self.NspawnMax, self.SpawnIndex)) + return + else + self:T2(self.lid..string.format("Spawning with spawn index=%d", self.SpawnIndex)) + end -- Set takeoff type. local takeoff=self.takeoff @@ -4636,12 +4680,14 @@ function RAT:_ActivateUncontrolled() -- Get departure airbase (if exists) local departureAirbase=self:_GetDepartureAirbase(ratcraft) + -- Get FLIGHTCONTROL of departure airbase (if it exists) local departureFlightControl=nil if departureAirbase then departureFlightControl=_DATABASE:GetFlightControl(departureAirbase:GetName()) end if departureFlightControl then + -- Group is ready to taxi. FLIGHCONTROL will start self:T(self.lid..string.format("RAT group %s is ready for takeoff", group:GetName())) ratcraft.flightgroup:SetReadyForTakeoff(true) else From 833206a3b544f18c043fa2e09a73733e764f85fe Mon Sep 17 00:00:00 2001 From: Frank Date: Tue, 16 Apr 2024 16:42:26 +0200 Subject: [PATCH 23/26] RAT - Uncontrolled --- Moose Development/Moose/Core/Spawn.lua | 7 +- Moose Development/Moose/Functional/RAT.lua | 99 +++++++++++++++------- 2 files changed, 72 insertions(+), 34 deletions(-) diff --git a/Moose Development/Moose/Core/Spawn.lua b/Moose Development/Moose/Core/Spawn.lua index cac86abf3..281d599cc 100644 --- a/Moose Development/Moose/Core/Spawn.lua +++ b/Moose Development/Moose/Core/Spawn.lua @@ -292,9 +292,10 @@ SPAWN = { --- Enumerator for spawns at airbases -- @type SPAWN.Takeoff --- @extends Wrapper.Group#GROUP.Takeoff - --- @field #SPAWN.Takeoff Takeoff +-- @field #number Air Take off happens in air. +-- @field #number Runway Spawn on runway. Does not work in MP! +-- @field #number Hot Spawn at parking with engines on. +-- @field #number Cold Spawn at parking with engines off. SPAWN.Takeoff = { Air = 1, Runway = 2, diff --git a/Moose Development/Moose/Functional/RAT.lua b/Moose Development/Moose/Functional/RAT.lua index c0538499f..a27582f27 100644 --- a/Moose Development/Moose/Functional/RAT.lua +++ b/Moose Development/Moose/Functional/RAT.lua @@ -432,7 +432,7 @@ RAT={ ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- Categories of the RAT class. --- @list cat +-- @type RAT.cat -- @field #string plane Plane. -- @field #string heli Heli. RAT.cat={ @@ -441,7 +441,7 @@ RAT.cat={ } --- RAT waypoint type. --- @list wp +-- @type RAT.wp RAT.wp={ coldorhot=0, air=1, @@ -457,7 +457,7 @@ RAT.wp={ } --- RAT aircraft status. --- @list status +-- @type RAT.status RAT.status={ -- Waypoint states. Departure="At departure point", @@ -506,7 +506,7 @@ RAT.status={ -- @field #number nrespawn Number of respawns. --- RAT friendly coalitions. --- @list coal +-- @type RAT.coal RAT.coal={ same="same", sameonly="sameonly", @@ -514,7 +514,7 @@ RAT.coal={ } --- RAT unit conversions. --- @list unit +-- @type RAT.unit RAT.unit={ ft2meter=0.305, kmh2ms=0.278, @@ -524,7 +524,7 @@ RAT.unit={ } --- RAT rules of engagement. --- @list ROE +-- @type RAT.ROE RAT.ROE={ weaponhold="hold", weaponfree="free", @@ -532,7 +532,7 @@ RAT.ROE={ } --- RAT reaction to threat. --- @list ROT +-- @type RAT.ROT RAT.ROT={ evade="evade", passive="passive", @@ -1523,6 +1523,15 @@ function RAT:SetSpawnInterval(interval) return self end +--- Set max number of groups that will be spawned. When this limit is reached, no more RAT groups are spawned. +-- @param #RAT self +-- @param #number Nmax Max number of groups. Default `nil`=unlimited. +-- @return #RAT RAT self object. +function RAT:SetSpawnLimit(Nmax) + self.NspawnMax=Nmax + return self +end + --- Make aircraft respawn the moment they land rather than at engine shut down. -- @param #RAT self -- @param #number delay (Optional) Delay in seconds until respawn happens after landing. Default is 1 second. Minimum is 1 second. @@ -2224,9 +2233,17 @@ function RAT:_SpawnWithRoute(_departure, _destination, _takeoff, _landing, _live else livery=nil end + + -- We set the aircraft to uncontrolled if the departure airbase has a FLIGHTCONTROL. + local uncontrolled=self.uncontrolled + local isFlightcontrol=self:_IsFlightControlAirbase(departure) + if takeoff~=RAT.wp.air and departure and isFlightcontrol then + takeoff=RAT.wp.cold + uncontrolled=true + end -- Modify the spawn template to follow the flight plan. - local successful=self:_ModifySpawnTemplate(waypoints, livery, _lastpos, departure, takeoff, parkingdata) + local successful=self:_ModifySpawnTemplate(waypoints, livery, _lastpos, departure, takeoff, parkingdata, uncontrolled) if not successful then return nil end @@ -2263,7 +2280,7 @@ function RAT:_SpawnWithRoute(_departure, _destination, _takeoff, _landing, _live end -- Add flight (if there is no FC at the airbase) - if not _DATABASE:GetFlightControl(airbasename) then + if not self:_IsFlightControlAirbase(airbasename) then self:_ATCAddFlight(groupname, airbasename) end end @@ -2272,6 +2289,13 @@ function RAT:_SpawnWithRoute(_departure, _destination, _takeoff, _landing, _live if self.placemarkers then self:_PlaceMarkers(waypoints, wpdesc, self.SpawnIndex) end + + -- Set group ready for takeoff at the FLIGHTCONTROL (if we do not do via a scheduler). + if isFlightcontrol and not self.activate_uncontrolled then + local N=math.random(120) + self:T(self.lid..string.format("Flight will be ready for takeoff in %d seconds", N)) + flightgroup:SetReadyForTakeoff(true, N) + end -- Set group to be invisible. if self.invisible then @@ -2319,7 +2343,7 @@ function RAT:_SpawnWithRoute(_departure, _destination, _takeoff, _landing, _live ratcraft.wpstatus=wpstatus -- Aircraft is active or spawned in uncontrolled state. - ratcraft.active=not self.uncontrolled + ratcraft.active=not uncontrolled -- Set status to spawned. This will be overwritten in birth event. ratcraft.status=RAT.status.Spawned @@ -2466,6 +2490,31 @@ function RAT:_SpawnWithRoute(_departure, _destination, _takeoff, _landing, _live return self.SpawnIndex end +--- Check if a given airbase has a FLIGHTCONTROL. +-- @param #RAT self +-- @param Wrapper.Airbase#AIRBASE airbase The airbase. +-- @return #boolean `true` if the airbase has a FLIGHTCONTROL. +function RAT:_IsFlightControlAirbase(airbase) + + if type(airbase)=="table" then + airbase=airbase:GetName() + end + + if airbase then + + local fc=_DATABASE:GetFlightControl(airbase) + + if fc then + self:T(self.lid..string.format("Airbase %s has a FLIGHTCONTROL running", airbase)) + return true + else + return false + end + + end + + return nil +end --- Clear flight for landing. Sets tigger value to 1. -- @param #RAT self @@ -3693,22 +3742,12 @@ function RAT:_GetAirportsOfCoalition() local airport=_airport --Wrapper.Airbase#AIRBASE local category=airport:GetAirbaseCategory() if airport:GetCoalition()==coalition then - -- Planes cannot land on FARPs. - --local condition1=self.category==RAT.cat.plane and airport:GetTypeName()=="FARP" - local condition1=self.category==RAT.cat.plane and category==Airbase.Category.HELIPAD - -- Planes cannot land on ships. - --local condition2=self.category==RAT.cat.plane and airport:GetCategory()==1 - local condition2=self.category==RAT.cat.plane and category==Airbase.Category.SHIP - -- Check that airport has the requested terminal types. - -- NOT good here because we would also not allow any airport zones! - --[[ - local nspots=1 - if self.termtype then - nspots=airport:GetParkingSpotsNumber(self.termtype) - end - local condition3 = nspots==0 - ]] + -- Planes cannot land on FARPs. + local condition1=self.category==RAT.cat.plane and category==Airbase.Category.HELIPAD + + -- Planes cannot land on ships. + local condition2=self.category==RAT.cat.plane and category==Airbase.Category.SHIP if not (condition1 or condition2) then table.insert(self.airports, airport) @@ -5108,8 +5147,9 @@ end -- @param Wrapper.Airbase#AIRBASE departure Departure airbase or zone. -- @param #number takeoff Takeoff type. -- @param #table parkingdata Parking data, i.e. parking spot coordinates and terminal ids for all units of the group. +-- @param #boolean uncontrolled If `true`, group is spawned uncontrolled. -- @return #boolean True if modification was successful or nil if not, e.g. when no parking space was found and spawn in air is disabled. -function RAT:_ModifySpawnTemplate(waypoints, livery, spawnplace, departure, takeoff, parkingdata) +function RAT:_ModifySpawnTemplate(waypoints, livery, spawnplace, departure, takeoff, parkingdata, uncontrolled) self:F2({waypoints=waypoints, livery=livery, spawnplace=spawnplace, departure=departure, takeoff=takeoff, parking=parkingdata}) -- The 3D vector of the first waypoint, i.e. where we actually spawn the template group. @@ -5160,9 +5200,9 @@ function RAT:_ModifySpawnTemplate(waypoints, livery, spawnplace, departure, take self:T(SpawnTemplate) -- Spawn aircraft in uncontrolled state. - if self.uncontrolled then + if self.uncontrolled or uncontrolled then -- This is used in the SPAWN:SpawnWithIndex() function. Some values are overwritten there! - self.SpawnUnControlled=true + --self.SpawnUnControlled=true SpawnTemplate.uncontrolled=true end @@ -5348,9 +5388,6 @@ function RAT:_ModifySpawnTemplate(waypoints, livery, spawnplace, departure, take end - ---- new - -- Translate the position of the Group Template to the Vec3. for UnitID = 1, nunits do From 3fa3644e1eea263e8e3b60595c90f41f6b9d50a0 Mon Sep 17 00:00:00 2001 From: Frank Date: Sun, 21 Apr 2024 10:58:13 +0200 Subject: [PATCH 24/26] ARMYGROUP - on road fix --- Moose Development/Moose/Ops/ArmyGroup.lua | 3 +++ Moose Development/Moose/Ops/OpsGroup.lua | 2 ++ 2 files changed, 5 insertions(+) diff --git a/Moose Development/Moose/Ops/ArmyGroup.lua b/Moose Development/Moose/Ops/ArmyGroup.lua index 9978f55e5..4dff8027c 100644 --- a/Moose Development/Moose/Ops/ArmyGroup.lua +++ b/Moose Development/Moose/Ops/ArmyGroup.lua @@ -976,6 +976,8 @@ function ARMYGROUP:onafterSpawned(From, Event, To) -- Update route. if Nwp>1 and self.isMobile then self:T(self.lid..string.format("Got %d waypoints on spawn ==> Cruise in -1.0 sec!", Nwp)) + local wp=self:GetWaypointNext() + self.option.Formation=wp.action --self:__Cruise(-1, nil, self.option.Formation) self:__Cruise(-1) else @@ -1288,6 +1290,7 @@ function ARMYGROUP:onafterUpdateRoute(From, Event, To, n, N, Speed, Formation) -- Current set speed in m/s. self.speedWp=wp.speed + self:T(self.lid..string.format("Expected/waypoint speed=%.1f m/s", self.speedWp)) -- Debug output. if self.verbose>=10 then --or self.attribute==GROUP.Attribute.GROUND_APC then diff --git a/Moose Development/Moose/Ops/OpsGroup.lua b/Moose Development/Moose/Ops/OpsGroup.lua index d8ae870b1..45d2288d9 100644 --- a/Moose Development/Moose/Ops/OpsGroup.lua +++ b/Moose Development/Moose/Ops/OpsGroup.lua @@ -6684,6 +6684,7 @@ function OPSGROUP:onafterPassingWaypoint(From, Event, To, Waypoint) local wpnext=self:GetWaypointNext() if wpnext then self.speedWp=wpnext.speed + self:T(self.lid..string.format("Expected/waypoint speed=%.1f m/s", self.speedWp)) end end @@ -11399,6 +11400,7 @@ function OPSGROUP:_InitWaypoints(WpIndexMin, WpIndexMax) -- Expected speed to the first waypoint. if i<=2 then self.speedWp=wp.speed + self:T(self.lid..string.format("Expected/waypoint speed=%.1f m/s", self.speedWp)) end -- Speed in knots. From dfaccd6aa50e694363841639a83af85121042175 Mon Sep 17 00:00:00 2001 From: Frank Date: Tue, 23 Apr 2024 23:12:17 +0200 Subject: [PATCH 25/26] Update Message.lua --- Moose Development/Moose/Core/Message.lua | 41 ++++++++++++++---------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/Moose Development/Moose/Core/Message.lua b/Moose Development/Moose/Core/Message.lua index 1ffcb1516..6c15c5dc3 100644 --- a/Moose Development/Moose/Core/Message.lua +++ b/Moose Development/Moose/Core/Message.lua @@ -395,29 +395,36 @@ end --- Sends a MESSAGE to all players. -- @param #MESSAGE self -- @param Core.Settings#Settings Settings (Optional) Settings for message display. --- @return #MESSAGE +-- @param #number Delay (Optional) Delay in seconds before the message is send. Default instantly (`nil`). +-- @return #MESSAGE self -- @usage -- --- -- Send a message created to all players. --- MessageAll = MESSAGE:New( "To all Players: BLUE has won! Each player of BLUE wins 50 points!", "End of Mission", 25 ):ToAll() --- or --- MESSAGE:New( "To all Players: BLUE has won! Each player of BLUE wins 50 points!", "End of Mission", 25 ):ToAll() --- or --- MessageAll = MESSAGE:New( "To all Players: BLUE has won! Each player of BLUE wins 50 points!", "End of Mission", 25 ) --- MessageAll:ToAll() +-- -- Send a message created to all players. +-- MessageAll = MESSAGE:New( "To all Players: BLUE has won! Each player of BLUE wins 50 points!", "End of Mission", 25 ):ToAll() +-- or +-- MESSAGE:New( "To all Players: BLUE has won! Each player of BLUE wins 50 points!", "End of Mission", 25 ):ToAll() +-- or +-- MessageAll = MESSAGE:New( "To all Players: BLUE has won! Each player of BLUE wins 50 points!", "End of Mission", 25 ) +-- MessageAll:ToAll() -- -function MESSAGE:ToAll( Settings ) +function MESSAGE:ToAll( Settings, Delay ) self:F() - if self.MessageType then - local Settings = Settings or _SETTINGS -- Core.Settings#SETTINGS - self.MessageDuration = Settings:GetMessageTime( self.MessageType ) - self.MessageCategory = "" -- self.MessageType .. ": " - end + if Delay and Delay>0 then + self:ScheduleOnce(Delay, MESSAGE.ToAll, self, Settings, 0) + else - if self.MessageDuration ~= 0 then - self:T( self.MessageCategory .. self.MessageText:gsub( "\n$", "" ):gsub( "\n$", "" ) .. " / " .. self.MessageDuration ) - trigger.action.outText( self.MessageCategory .. self.MessageText:gsub( "\n$", "" ):gsub( "\n$", "" ), self.MessageDuration, self.ClearScreen ) + if self.MessageType then + local Settings = Settings or _SETTINGS -- Core.Settings#SETTINGS + self.MessageDuration = Settings:GetMessageTime( self.MessageType ) + self.MessageCategory = "" -- self.MessageType .. ": " + end + + if self.MessageDuration ~= 0 then + self:T( self.MessageCategory .. self.MessageText:gsub( "\n$", "" ):gsub( "\n$", "" ) .. " / " .. self.MessageDuration ) + trigger.action.outText( self.MessageCategory .. self.MessageText:gsub( "\n$", "" ):gsub( "\n$", "" ), self.MessageDuration, self.ClearScreen ) + end + end return self From efb687cbb589407adb8c589896342888ea1b455f Mon Sep 17 00:00:00 2001 From: Frank Date: Tue, 23 Apr 2024 23:18:53 +0200 Subject: [PATCH 26/26] Update Base.lua --- Moose Development/Moose/Core/Base.lua | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/Moose Development/Moose/Core/Base.lua b/Moose Development/Moose/Core/Base.lua index 9d3ecec73..c4be4ef30 100644 --- a/Moose Development/Moose/Core/Base.lua +++ b/Moose Development/Moose/Core/Base.lua @@ -1157,19 +1157,14 @@ function BASE:_Serialize(Arguments) return text end ---- (Internal) Serialize arguments --- @param #BASE self --- @param #table Arguments --- @return #string Text -function BASE:_Serialize(Arguments) - local text=UTILS.BasicSerialize(Arguments) --- local text = UTILS.PrintTableToLog({Arguments}, 0, true) --- text = string.gsub(text,"\n","") --- text = string.gsub(text,"%(%(","%(") --- text = string.gsub(text,"%)%)","%)") --- text = string.gsub(text,"(%s+)","") - return text -end +----- (Internal) Serialize arguments +---- @param #BASE self +---- @param #table Arguments +---- @return #string Text +--function BASE:_Serialize(Arguments) +-- local text=UTILS.BasicSerialize(Arguments) +-- return text +--end --- Trace a function call. This function is private. -- @param #BASE self