From eac89f784d27de56edc3a2171f35c7b04fef57d8 Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Tue, 29 Aug 2017 16:46:56 +0200 Subject: [PATCH] Improvments for air spawn behaviour. --- Moose Development/Moose/AI/AI_RAT.lua | 158 +++++++++++++------------- 1 file changed, 81 insertions(+), 77 deletions(-) diff --git a/Moose Development/Moose/AI/AI_RAT.lua b/Moose Development/Moose/AI/AI_RAT.lua index 0cf3792e8..ec2d75183 100644 --- a/Moose Development/Moose/AI/AI_RAT.lua +++ b/Moose Development/Moose/AI/AI_RAT.lua @@ -11,10 +11,6 @@ myid="RAT | " -- @field #string prefix -- @field #RAT -- @extends #SPAWN - ---- Table of RAT --- @field #RAT RAT --- @param #string ClassName Name of class. RAT={ ClassName = "RAT", -- Name of class: RAT = Random Air Traffic. debug=true, -- Turn debug messages on or off. @@ -26,13 +22,12 @@ RAT={ friendly = "same", -- Possible departure/destination airport: all=blue+red+neutral, same=spawn+neutral, spawnonly=spawn, blue=blue+neutral, blueonly=blue, red=red+neutral, redonly=red. ctable = {}, -- Table with the valid coalitons from choice self.friendly. aircraft = {}, -- Table which holds the basic aircraft properties (speed, range, ...). - Vcruisemax=250, -- Max cruise speed in m/s (250 m/s = 900 km/h = 486 kt) + Vcruisemax=250, -- Max cruise speed in m/s (250 m/s = 900 km/h = 486 kt). Vclimb=1500, -- Default climb rate in ft/min. AlphaDescent=3.6, -- Default angle of descenti in degrees. A value of 3.6 follows the 3:1 rule of 3 miles of travel and 1000 ft descent. - FLcruise=200, -- Default cruise flight level in. FL200 = 20000 ft. roe = "hold", -- ROE of spawned groups, default is weapon hold (this is a peaceful class for civil aircraft or ferry missions). takeoff = "hot", -- Takeoff type: "hot", "cold", "runway", "air", "random". - mindist = 10000, -- Min distance from departure to destination in meters. + mindist = 5000, -- Min distance from departure to destination in meters. airports_map={}, -- All airports available on current map (Caucasus, Nevada, Normandy, ...). airports={}, -- All airports of friedly coalitions. airports_departure={}, -- Possible departure airports if unit/group is spawned at airport, spawnpoint=air or spawnpoint=airport. @@ -47,11 +42,10 @@ RAT={ --TODO list: --TODO: Add scheduled spawn and corresponding user functions. --TODO: Add possibility to spawn in air. ---TODO: Add from zones and to zones for aircraft to fly from/to. +--TODO: Add departure zones for air start. --TODO: Make more functions to adjust/set parameters. --TODO: Clean up debug messages. --DONE: Improve flight plan. Especially check FL against route length. ---TODO: Integrate RAT:DB? No, not now. --DONE: Add event handlers. --DONE: Respawn units when they have landed. --DONE: Change ROE state. @@ -61,8 +55,6 @@ RAT={ --TODO: Add possibility to continue journey at destination. Need "place" in event data for that. --DONE: Check that FARPS are not used as airbases for planes. Don't know if they appear in list of airports. --DONE: Add cases for helicopters. ---TODO: Lots of other little things... - --- Creates a new RAT object. -- @param #RAT self @@ -166,9 +158,10 @@ function RAT:_InitAircraft(DCSgroup) self.aircraft.Vmin = self.aircraft.Vmax*0.75 -- actual travel speed (random between ASmin and ASmax) - --TODO: This needs to be placed somewhere else! Randomization should not happen here. + --TODO: This needs to be placed somewhere else! Randomization should not happen here. Otherwise it is not changed for multiple spawns. self.aircraft.Vcruise = math.random(self.aircraft.Vmin, self.aircraft.Vmax) - -- limit travel speed to ~900 km/h + + -- Limit travel speed to ~900 km/h for jets. self.aircraft.Vcruise = math.min(self.aircraft.Vcruise, self.Vcruisemax) -- max climb speed in m/s @@ -186,11 +179,12 @@ function RAT:_InitAircraft(DCSgroup) -- service ceiling in meters self.aircraft.ceiling=DCSdesc.Hmax - -- default flight levels (ASL) + -- Default flight level (ASL). if self.category=="plane" then - self.aircraft.FLcruise=self.FLcruise*FL2m + -- For planes: FL200 = 20000 ft = 6096 m. + self.aircraft.FLcruise=200*FL2m else - --TODO: need to check these parameters for helos: FL005 = 152 m + -- For helos: FL005 = 500 ft = 152 m. self.aircraft.FLcruise=005*FL2m end @@ -256,6 +250,10 @@ end --- Spawn the AI aircraft with a route. +-- Sets the departure and destination airports and waypoints. +-- Modifies the spawn template. +-- Sets ROE/ROT. +-- Initializes the ratcraft array and event handlers. -- @param #RAT self function RAT:_SpawnWithRoute() @@ -459,7 +457,7 @@ function RAT:SetDeparture(name) end end ---- Set names of departure zones for spawning the AI aircraft. +--- Set departure zones for spawning the AI aircraft. -- @param #RAT self -- @param #table zonenames Table of zone names where spawning should happen. function RAT:SetDepartureZones(zonenames) @@ -467,8 +465,10 @@ function RAT:SetDepartureZones(zonenames) local z for _,name in pairs(zonenames) do if name:lower()=="zone template" then + -- Zone with radius 5 km around the template group in the ME. z=ZONE_GROUP:New("Zone Template", GROUP:FindByName(self.prefix), self.Rzone) else + -- Zone defined my user in the ME. z=ZONE:New(name) end if z then @@ -553,7 +553,7 @@ end -- @param #number minrange Minimum range to q in meters. -- @param #number maxrange Maximum range to q in meters. function RAT:_GetDestinations(q, minrange, maxrange) - local absolutemin=10000 -- Absolute minimum is 10 km. + local absolutemin=5000 -- Absolute minimum is 5 km. minrange=minrange or absolutemin -- Default min is absolute min. maxrange=maxrange or 10000000 -- Default max 10,000 km. -- Ensure that minrange is always > 10 km to ensure the destination != departure. @@ -624,7 +624,8 @@ function RAT:_GetAirportsOfCoalition() for _,airport in pairs(self.airports_map) do if airport:GetCoalition()==coalition then airport:GetTypeName() - -- remember that planes cannot land on FARPs. + -- Remember that planes cannot land on FARPs. + -- TODO: Probably have to add ships as well! if not (self.category=="plane" and airport:GetTypeName()=="FARP") then table.insert(self.airports, airport) end @@ -650,11 +651,11 @@ end -- @return #table Waypoints for DCS task route or spawn template. function RAT:Waypoint(Type, Coord, Speed, Altitude, Airport) - -- set altitude to input parameter or take y of 3D-coordinate + -- Altitude of input parameter or y-component of 3D-coordinate. local _Altitude=Altitude or Coord.y --TODO: _Type should be generalized to Grouptemplate.Type - --TODO: Only use _alttype="BARO" and add landheight for _alttype="RADIO". Don't know if "BARO" really works atm. + --TODO: Only use _alttype="BARO" and add landheight for _alttype="RADIO". Don't know if "RADIO" really works well. -- Land height at given coordinate. local Hland=Coord:GetLandHeight() @@ -690,7 +691,6 @@ function RAT:Waypoint(Type, Coord, Speed, Altitude, Airport) -- air start _Type="Turning Point" _Action="Turning Point" - _Altitude = 5000 _alttype="BARO" elseif Type:lower()=="climb" or Type:lower()=="cruise" then _Type="Turning Point" @@ -823,7 +823,7 @@ end -- @param Dcs.DCSTypes#Vec2 P1 The point to hold the position. -- @param #number Altitude The altitude AGL to hold the position. -- @param #number Speed The speed flying when holding the position in m/s. --- @return Dcs.DCSTasking.Task#Task +-- @return Dcs.DCSTasking.Task#Task DCSTask function RAT:_TaskHolding(P1, Altitude, Speed) local LandHeight = land.getHeight(P1) @@ -905,54 +905,69 @@ function RAT:_SetRoute() local kmh2ms=0.278 local FL2m=30.48 local nm2km=1.852 - - --TODO: Add case where we don't have a departure airport but rather a zone! -- DEPARTURE AIRPORT -- Departure airport or zone. local departure=self:_SetDeparture() - -- coordinates + -- Coordinates of departure point. local Pdeparture if self.takeoff=="air" then -- For an air start, we take a random point within the spawn zone. local vec2=departure:GetRandomVec2() - Pdeparture=COORDINATE:New(vec2.x, self.aircraft.FLcruise, vec2.y) + --Pdeparture=COORDINATE:New(vec2.x, self.aircraft.FLcruise, vec2.y) + Pdeparture=COORDINATE:NewFromVec2(vec2) else Pdeparture=departure:GetCoordinate() end - -- height ASL if departure - local H_departure=Pdeparture.y + -- Height ASL of departure point. + local H_departure + if self.takeoff=="air" then + -- Departure altitude is 70% of default cruise with 30% variation and limited to 1000 m AGL (50 m for helos). + local Hmin + if self.category=="pland" then + Hmin=1000 + else + Hmin=50 + end + H_departure=self:_Randomize(self.aircraft.FLcruise*0.7, 0.3, Pdeparture.y+Hmin, self.aircraft.FLcruise) + else + H_departure=Pdeparture.y + end -- DESTINATION AIRPORT - -- get all destination airports within reach and at least 10 km away from departure + -- Get all destination airports within reach and at least 10 km away from departure. self:_GetDestinations(Pdeparture, self.mindist, self.aircraft.Reff) + + -- Pick a destination airport. local destination=self:_SetDestination() + -- Check that departure and destination are not the same. Should not happen due to mindist. if destination:GetName()==departure:GetName() then local text="Destination and departure airport are identical: "..destination:GetName().." with ID "..destination:GetID() MESSAGE:New(text, 120):ToAll() error(myid..text) end - -- coordinates of destination + -- Coordinates of destination airport. local Pdestination=destination:GetCoordinate() - -- height ASL of destination + -- Height ASL of destination airport. local H_destination=Pdestination.y -- DESCENT/HOLDING POINT - -- get a random point between 10 and 20 km away from the destination + -- Get a random point between 10 and 20 km away from the destination. local Vholding if self.category=="plane" then Vholding=destination:GetCoordinate():GetRandomVec2InRadius(20000, 10000) else - -- for helos we set 500 to 1000 m + -- For helos we set a distance between 500 to 1000 m. Vholding=destination:GetCoordinate():GetRandomVec2InRadius(1000, 500) end + -- Coordinates of the holding point. y is the land height at that point. local Pholding=COORDINATE:NewFromVec2(Vholding) - -- holding point altitude + -- Holding point altitude. For planes between 800 and 1200 m AGL. For helos 80 to 120 m AGL. local h_holding if self.category=="plane" then h_holding=1000 @@ -961,7 +976,7 @@ function RAT:_SetRoute() end h_holding=self:_Randomize(h_holding, 0.2) - -- distance from holding point to destination + -- Distance from holding point to destination. local d_holding=Pholding:Get2DDistance(Pdestination) -- GENERAL @@ -972,54 +987,59 @@ function RAT:_SetRoute() local d_total=Pdeparture:Get2DDistance(Pholding) -- CLIMB and DESCENT angles + -- TODO: Randomize climb/descent angles. This did not work in rad. Need to convert to deg first. local AlphaClimb=self.aircraft.AlphaClimb --=self:_Randomize(self.aircraft.AlphaClimb, 0.1) local AlphaDescent=self.aircraft.AlphaDescent --self:_Randomize(self.aircraft.AlphaDescent, 0.1) --CRUISE - -- set min/max cruise altitudes + -- Set min/max cruise altitudes. local FLmax local FLmin local FLcruise=self.aircraft.FLcruise if self.category=="plane" then - -- min cruise alt is above 100 meter above holding point + -- Min cruise alt is just above holding point at destination or departure height, whatever is larger. FLmin=math.max(H_departure, H_destination+h_holding) -- Check if the distance between the two airports is large enough to reach the desired FL and descent again at the given climb/descent rates. - -- TODO: need to modify this for "air" start. - FLmax=self:_FLmax(AlphaClimb, AlphaDescent, d_total, H_departure) if self.takeoff=="air" then - FLmax=FLmax*2 + -- This is the case where we only descent to the ground at the given descent angle. + FLmax=d_total*math.tan(AlphaDescent)+H_destination + else + FLmax=self:_FLmax(AlphaClimb, AlphaDescent, d_total, H_departure) end + -- If the route is very short we set FLmin a bit lower than FLmax. if FLmin>FLmax then FLmin=FLmax*0.8 end + -- Again, if the route is too short to climb and descent, we set the default cruise alt at bit lower than the max we can reach. if FLcruise>FLmax then FLcruise=FLmax*0.9 end else - -- for helicopters we take cruise alt between 50 to 1000 meters above ground + -- For helicopters we take cruise alt between 50 to 1000 meters above ground. Default cruise alt is ~150 m. FLmin=math.max(H_departure, H_destination)+50 FLmax=math.max(H_departure, H_destination)+1000 end - -- set randomized cruise altitude: default +-50% but limited + -- Set randomized cruise altitude: default +-50% but limited to FLmin and FLmax. FLcruise=self:_Randomize(FLcruise, 0.5, FLmin, FLmax) - -- check that we are not above 90% of service ceiling + -- Finally, check that we are not above 90% of service ceiling. FLcruise=math.min(FLcruise, self.aircraft.ceiling*0.9) - --CLIMB - -- height of climb relative to ASL height of departure airport + -- CLIMB + -- Height of climb relative to ASL height of departure airport. local h_climb=FLcruise-H_departure -- x-distance of climb part - local d_climb=h_climb/math.tan(AlphaClimb) + local d_climb=math.abs(h_climb/math.tan(AlphaClimb)) -- time of climb in seconds local t_climb=h_climb/self.aircraft.Vclimb -- DESCENT - -- height difference for descent form cruise alt to holding point + -- Height difference for descent form cruise alt to holding point. local h_descent=FLcruise-H_destination-h_holding -- x-distance of descent part local d_descent=math.abs(h_descent/math.tan(AlphaDescent)) -- CRUISE + -- Distance of the cruising part. This should in principle not become negative, but can happen for very short legs. local d_cruise=d_total-d_climb-d_descent -- debug message @@ -1046,7 +1066,7 @@ function RAT:_SetRoute() MESSAGE:New(text, 60):ToAll() end - -- coordinates of route from depature (0) to cruise (1) to descent (2) to holing (3) to destination (4) + -- Coordinates of route from departure (0) to cruise (1) to descent (2) to holing (3) to destination (4). local c0=Pdeparture local c1=c0:Translate(d_climb, heading) local c2=c1:Translate(d_cruise, heading) @@ -1054,9 +1074,8 @@ function RAT:_SetRoute() local c3=Pholding local c4=Pdestination - -- convert coordinates into route waypoints - --TODO: modify this for air start - local wp0=self:Waypoint(self.takeoff, c0, self.aircraft.Vmin, 2, departure) + --Convert coordinates into route waypoints. + local wp0=self:Waypoint(self.takeoff, c0, self.aircraft.Vmin, H_departure, departure) local wp1=self:Waypoint("climb", c1, self.aircraft.Vmax, FLcruise) local wp2=self:Waypoint("cruise", c2, self.aircraft.Vcruise, FLcruise) --TODO: add the possibility for a holing point, i.e. we circle a bit before final approach. @@ -1085,15 +1104,16 @@ end -- @param #number h0 Height [m] of departure airport. Note we implicitly assume that the height difference between departure and destination is negligible. -- @return #number Maximal flight level in meters. function RAT:_FLmax(alpha, beta, d, h0) --- solve ASA triangle for one side (d) and two adjacent angles (alpha, beta) given +-- Solve ASA triangle for one side (d) and two adjacent angles (alpha, beta) given. local gamma=math.rad(180)-alpha-beta local a=d*math.sin(alpha)/math.sin(gamma) local b=d*math.sin(beta)/math.sin(gamma) local h1=b*math.sin(alpha) local h2=a*math.sin(beta) local FL2m=30.48 + -- h1 and h2 should be equal. local text=string.format("FLmax = FL%3.0f = %6.1f m.\n", h1/FL2m, h1) - text=text..string.format("FLmax = FL%3.0f = %6.1f m.", h2/FL2m, h2) + text=text..string.format("FLmax = FL%3.0f = %6.1f m.", h2/FL2m, h2) env.info(myid..text) return b*math.sin(alpha)+h0 end @@ -1106,18 +1126,9 @@ end -- @param #table waypoints The waypoints of the AI flight plan. function RAT:_ModifySpawnTemplate(waypoints) - -- point of Airbase - --local PointVec3 = Airbase:GetPointVec3() - + -- The 3D vector of the first waypoint, i.e. where we actually spawn the template group. local PointVec3 = {x=waypoints[1].x, y=waypoints[1].alt, z=waypoints[1].y} - local addheight - if self.takeoff=="air" then - addheight=5000 - else - addheight=2 - end - if self:_GetSpawnIndex(self.SpawnIndex+1) then -- Get copy of spawn template. @@ -1128,7 +1139,7 @@ function RAT:_ModifySpawnTemplate(waypoints) -- Translate the position of the Group Template to the Vec3. for UnitID = 1, #SpawnTemplate.units do - self:T( 'Before Translation SpawnTemplate.units['..UnitID..'].x = ' .. SpawnTemplate.units[UnitID].x .. ', SpawnTemplate.units['..UnitID..'].y = ' .. SpawnTemplate.units[UnitID].y ) + self:T('Before Translation SpawnTemplate.units['..UnitID..'].x = '..SpawnTemplate.units[UnitID].x..', SpawnTemplate.units['..UnitID..'].y = '..SpawnTemplate.units[UnitID].y) local UnitTemplate = SpawnTemplate.units[UnitID] local SX = UnitTemplate.x local SY = UnitTemplate.y @@ -1138,29 +1149,22 @@ function RAT:_ModifySpawnTemplate(waypoints) local TY = PointVec3.z + (SY-BY) SpawnTemplate.units[UnitID].x = TX SpawnTemplate.units[UnitID].y = TY - SpawnTemplate.units[UnitID].alt = PointVec3.y + addheight - self:T( 'After Translation SpawnTemplate.units['..UnitID..'].x = ' .. SpawnTemplate.units[UnitID].x .. ', SpawnTemplate.units['..UnitID..'].y = ' .. SpawnTemplate.units[UnitID].y ) + SpawnTemplate.units[UnitID].alt = PointVec3.y + self:T('After Translation SpawnTemplate.units['..UnitID..'].x = '..SpawnTemplate.units[UnitID].x..', SpawnTemplate.units['..UnitID..'].y = '..SpawnTemplate.units[UnitID].y) end - -- Copy waypoints into spawntemplate. - -- this way we avoid the "landing bug" DCS currently has :) + -- Copy waypoints into spawntemplate. By this we avoid the nasty DCS "landing bug" :) for i,wp in ipairs(waypoints) do SpawnTemplate.route.points[i]=wp end - --TODO: Is this really necessary. Should already be defined in _Waypoints() function - --SpawnTemplate.route.points[1].x = PointVec3.x - --SpawnTemplate.route.points[1].y = PointVec3.z - --SpawnTemplate.route.points[1].alt = PointVec3.y + addheight - - --SpawnTemplate.route.points[1].airdromeId = Airbase:GetID() - --SpawnTemplate.route.points[1].type = GROUPTEMPLATE.Takeoff[Takeoff] - + -- Also modify x,y of the template. Not sure why. SpawnTemplate.x = PointVec3.x SpawnTemplate.y = PointVec3.z -- Update modified template for spawn group. self.SpawnGroups[self.SpawnIndex].SpawnTemplate=SpawnTemplate + self:E(SpawnTemplate) end end