From bd9022c01097486fca40e24306db198346db43c4 Mon Sep 17 00:00:00 2001 From: Frank Date: Tue, 31 Oct 2023 10:45:56 +0100 Subject: [PATCH 01/29] 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/29] 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/29] 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/29] 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/29] 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/29] 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/29] 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/29] 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/29] 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/29] 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/29] 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/29] 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/29] 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/29] 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/29] 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/29] 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/29] 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/29] 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/29] 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/29] 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/29] 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/29] 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/29] 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/29] 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 2220f1829fd3fa5a70f5d2a8982cbe4712435249 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 23 Apr 2024 10:13:09 +0200 Subject: [PATCH 25/29] #STRATEGO -- add SetStrategoZone --- .../Moose/Functional/Stratego.lua | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/Moose Development/Moose/Functional/Stratego.lua b/Moose Development/Moose/Functional/Stratego.lua index 97bf316f5..07bac3837 100644 --- a/Moose Development/Moose/Functional/Stratego.lua +++ b/Moose Development/Moose/Functional/Stratego.lua @@ -15,6 +15,7 @@ -- -- @module Functional.Stratego -- @image Functional.Stratego.png +-- Last Update April 2024 --- @@ -42,6 +43,7 @@ -- @field #number CaptureUnits -- @field #number CaptureThreatlevel -- @field #boolean ExcludeShips +-- @field Core.Zone#ZONE StrategoZone -- @extends Core.Base#BASE -- @extends Core.Fsm#FSM @@ -154,6 +156,7 @@ -- @{#STRATEGO.FindRoute}(): Find a route between two nodes. -- @{#STRATEGO.SetCaptureOptions}(): Set how many units of which minimum threat level are needed to capture one node (i.e. the underlying OpsZone). -- @{#STRATEGO.SetDebug}(): Set debug and draw options. +-- @{#STRATEGO.SetStrategoZone}(): Set a zone to restrict STRATEGO analytics to, can be any kind of ZONE Object. -- -- -- ## Visualisation example code for the Syria map: @@ -177,7 +180,7 @@ STRATEGO = { debug = false, drawzone = false, markzone = false, - version = "0.2.6", + version = "0.2.7", portweight = 3, POIweight = 1, maxrunways = 3, @@ -377,6 +380,15 @@ function STRATEGO:SetDebug(Debug,DrawZones,MarkZones) return self end +--- [USER] Restrict Stratego to analyse this zone only. +-- @param #STRATEGO self +-- @param Core.Zone#ZONE Zone The Zone to restrict Stratego to, can be any kind of ZONE Object. +-- @return #STRATEGO self +function STRATEGO:SetStrategoZone(Zone) + self.StrategoZone = Zone + return self +end + --- [USER] Set weights for nodes and routes to determine their importance. -- @param #STRATEGO self -- @param #number MaxRunways Set the maximum number of runways the big (equals strategic) airbases on the map have. Defaults to 3. The weight of an airbase node hence equals the number of runways. @@ -425,12 +437,19 @@ function STRATEGO:AnalyseBases() local airbasetable = self.airbasetable local nonconnectedab = self.nonconnectedab local easynames = self.easynames + local zone = self.StrategoZone -- Core.Zone#ZONE_POLYGON -- find bases with >= 1 runways self.bases:ForEach( function(afb) local ab = afb -- Wrapper.Airbase#AIRBASE + local abvec2 = ab:GetVec2() if self.ExcludeShips and ab:IsShip() then return end + if zone ~= nil then + if not zone:IsVec2InZone(abvec2) then + return + end + end local abname = ab:GetName() local runways = ab:GetRunways() local numrwys = #runways From dfaccd6aa50e694363841639a83af85121042175 Mon Sep 17 00:00:00 2001 From: Frank Date: Tue, 23 Apr 2024 23:12:17 +0200 Subject: [PATCH 26/29] 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 27/29] 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 From ebc355ee6aa2a8c3ba2c405d394bf8f91764177c Mon Sep 17 00:00:00 2001 From: Thomas <72444570+Applevangelist@users.noreply.github.com> Date: Wed, 24 Apr 2024 10:46:16 +0200 Subject: [PATCH 28/29] Shapes - added images (#2122) * Update Triangle.lua (#2121) * Update Polygon.lua (#2120) * Update Oval.lua (#2119) * Update Line.lua (#2118) * Update Cube.lua (#2117) * Update Circle.lua (#2116) --- Moose Development/Moose/Shapes/Circle.lua | 1 + Moose Development/Moose/Shapes/Cube.lua | 1 + Moose Development/Moose/Shapes/Line.lua | 1 + Moose Development/Moose/Shapes/Oval.lua | 1 + Moose Development/Moose/Shapes/Polygon.lua | 1 + Moose Development/Moose/Shapes/Triangle.lua | 1 + 6 files changed, 6 insertions(+) diff --git a/Moose Development/Moose/Shapes/Circle.lua b/Moose Development/Moose/Shapes/Circle.lua index 3c4efc10c..39461c522 100644 --- a/Moose Development/Moose/Shapes/Circle.lua +++ b/Moose Development/Moose/Shapes/Circle.lua @@ -4,6 +4,7 @@ -- -- === -- @module Shapes.CIRCLE +-- @image MOOSE.JPG --- CIRCLE class. -- @type CIRCLE diff --git a/Moose Development/Moose/Shapes/Cube.lua b/Moose Development/Moose/Shapes/Cube.lua index 18448fe85..f030ebeae 100644 --- a/Moose Development/Moose/Shapes/Cube.lua +++ b/Moose Development/Moose/Shapes/Cube.lua @@ -4,6 +4,7 @@ -- -- === -- @module Shapes.CUBE +-- @image MOOSE.JPG --- LINE class. -- @type CUBE diff --git a/Moose Development/Moose/Shapes/Line.lua b/Moose Development/Moose/Shapes/Line.lua index 9b860000d..ef321ce32 100644 --- a/Moose Development/Moose/Shapes/Line.lua +++ b/Moose Development/Moose/Shapes/Line.lua @@ -4,6 +4,7 @@ -- -- === -- @module Shapes.LINE +-- @image MOOSE.JPG --- LINE class. -- @type LINE diff --git a/Moose Development/Moose/Shapes/Oval.lua b/Moose Development/Moose/Shapes/Oval.lua index d1a65b58d..40ddc3317 100644 --- a/Moose Development/Moose/Shapes/Oval.lua +++ b/Moose Development/Moose/Shapes/Oval.lua @@ -4,6 +4,7 @@ -- -- === -- @module Shapes.OVAL +-- @image MOOSE.JPG --- OVAL class. -- @type OVAL diff --git a/Moose Development/Moose/Shapes/Polygon.lua b/Moose Development/Moose/Shapes/Polygon.lua index 0d0707570..d0253fa04 100644 --- a/Moose Development/Moose/Shapes/Polygon.lua +++ b/Moose Development/Moose/Shapes/Polygon.lua @@ -4,6 +4,7 @@ -- -- === -- @module Shapes.POLYGON +-- @image MOOSE.JPG --- POLYGON class. -- @type POLYGON diff --git a/Moose Development/Moose/Shapes/Triangle.lua b/Moose Development/Moose/Shapes/Triangle.lua index e0e752ce8..747407d2e 100644 --- a/Moose Development/Moose/Shapes/Triangle.lua +++ b/Moose Development/Moose/Shapes/Triangle.lua @@ -5,6 +5,7 @@ -- -- === -- @module Shapes.TRIANGLE +-- @image MOOSE.JPG --- LINE class. -- @type CUBE From 2f34526c55e4262118d953896ed4132d91ed386c Mon Sep 17 00:00:00 2001 From: Thomas <72444570+Applevangelist@users.noreply.github.com> Date: Wed, 24 Apr 2024 13:26:51 +0200 Subject: [PATCH 29/29] Update build-docs.yml --- .github/workflows/build-docs.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml index 5769e409b..c28165ca3 100644 --- a/.github/workflows/build-docs.yml +++ b/.github/workflows/build-docs.yml @@ -57,6 +57,7 @@ jobs: - name: Update apt-get (needed for act docker image) run: | + sudo rm /etc/apt/sources.list.d/microsoft-prod.list sudo apt-get -qq update - name: Install tree