diff --git a/Moose Development/Moose/Core/Point.lua b/Moose Development/Moose/Core/Point.lua index 366ad03e1..ee5af0965 100644 --- a/Moose Development/Moose/Core/Point.lua +++ b/Moose Development/Moose/Core/Point.lua @@ -1002,11 +1002,15 @@ do -- COORDINATE function COORDINATE:WaypointAir( AltType, Type, Action, Speed, SpeedLocked, airbase, DCSTasks, description ) self:F2( { AltType, Type, Action, Speed, SpeedLocked } ) - -- Defaults + -- Set alttype or "RADIO" which is AGL. AltType=AltType or "RADIO" + + -- Speedlocked by default if SpeedLocked==nil then SpeedLocked=true end + + -- Speed or default 500 km/h. Speed=Speed or 500 -- Waypoint array. @@ -1015,19 +1019,26 @@ do -- COORDINATE -- Coordinates. RoutePoint.x = self.x RoutePoint.y = self.z + -- Altitude. RoutePoint.alt = self.y RoutePoint.alt_type = AltType + -- Waypoint type. RoutePoint.type = Type or nil RoutePoint.action = Action or nil - -- Set speed/ETA. + + -- Speed. RoutePoint.speed = Speed/3.6 RoutePoint.speed_locked = SpeedLocked + + -- ETA. RoutePoint.ETA=nil - RoutePoint.ETA_locked = false + RoutePoint.ETA_locked = false + -- Waypoint description. RoutePoint.name=description + -- Airbase parameters for takeoff and landing points. if airbase then local AirbaseID = airbase:GetID() @@ -1036,31 +1047,24 @@ do -- COORDINATE RoutePoint.linkUnit = AirbaseID RoutePoint.helipadId = AirbaseID elseif AirbaseCategory == Airbase.Category.AIRDROME then - RoutePoint.airdromeId = AirbaseID + RoutePoint.airdromeId = AirbaseID else self:T("ERROR: Unknown airbase category in COORDINATE:WaypointAir()!") - end - end + end + + self:MarkToAll(string.format("Landing waypoint at airbase %s", airbase:GetName())) + end - - -- ["task"] = - -- { - -- ["id"] = "ComboTask", - -- ["params"] = - -- { - -- ["tasks"] = - -- { - -- }, -- end of ["tasks"] - -- }, -- end of ["params"] - -- }, -- end of ["task"] - -- Waypoint tasks. RoutePoint.task = {} RoutePoint.task.id = "ComboTask" RoutePoint.task.params = {} RoutePoint.task.params.tasks = DCSTasks or {} + -- Debug. self:T({RoutePoint=RoutePoint}) + + -- Return waypoint. return RoutePoint end diff --git a/Moose Development/Moose/Ops/RescueHelo.lua b/Moose Development/Moose/Ops/RescueHelo.lua index 71d67d596..86434721b 100644 --- a/Moose Development/Moose/Ops/RescueHelo.lua +++ b/Moose Development/Moose/Ops/RescueHelo.lua @@ -13,7 +13,8 @@ -- -- === -- --- ### Author: **funkyfranky** +-- ### Author: **funkyfranky** +-- ### Contributions: Flightcontrol (@{#AI_FORMATION} class) -- -- @module Ops.RescueHelo -- @image MOOSE.JPG @@ -26,6 +27,7 @@ -- @field Wrapper.Unit#UNIT carrier The carrier the helo is attached to. -- @field #string carriertype Carrier type. -- @field #string helogroupname Name of the late activated helo template group. +-- @field #string helogroupalias Spawn alias name of the group. Necessary for multiple RESCUEHELO objects in one mission. Uses groupname plus carrier name. -- @field Wrapper.Group#GROUP helo Helo group. -- @field #number takeoff Takeoff type. -- @field Wrapper.Airbase#AIRBASE airbase The airbase object acting as home base of the helo. @@ -45,6 +47,7 @@ -- @field #boolean rescuestopboat If true, stop carrier during rescue operations. -- @field #boolean carrierstop If true, route of carrier was stopped. -- @field #number HeloFuel0 Initial fuel of helo in percent. Necessary due to DCS bug that helo with full tank does not return fuel via API function. +-- @field #boolean rtb If true, Helo will be return to base on the next status check. -- @extends Core.Fsm#FSM --- Rescue Helo @@ -77,13 +80,13 @@ -- -- Once the helo is out of fuel, it will return to the carrier. When the helo lands, it will be respawned immidiately and go back on station. -- --- If a unit crashes or a pilot ejects within a radius of 100 km from the USS Stennis, the helo will automatically fly to the crash side and +-- If a unit crashes or a pilot ejects within a radius of 30 km from the USS Stennis, the helo will automatically fly to the crash side and -- rescue to pilot. This will take around 5 minutes. After that, the helo will return to the Stennis, land there and bring back the poor guy. -- When this is done, the helo will go back on station. -- -- # Fine Tuning -- --- The implementation allows to customize quite a few settings easily +-- The implementation allows to customize quite a few settings easily. -- -- ## Takeoff Type -- @@ -129,7 +132,22 @@ -- -- * @{#RESCUEHELO.SetAltitude}(*altitude*), where *altitude* is the altitude the helo flies at in meters. Default is 70 meters. -- * @{#RESCUEHELO.SetOffsetX}(*distance*)}, where *distance is the distance in the direction of movement of the carrier. Default is 200 meters. --- * @{#RESCUEHELO.SetOffsetZ}(*distance*)}, where *distance is the distance on the starboard side. Default is 200 meters. +-- * @{#RESCUEHELO.SetOffsetZ}(*distance*)}, where *distance is the distance on the starboard side. Default is 100 meters. +-- +-- ## Rescue Operations +-- +-- By default the rescue helo will start a rescue operation if an aircraft crashes or a pilot ejects in the vicinity of the carrier. +-- The standard "rescue zone" has a radius of 30 km around the carrier. The radius can be adjusted via the @{#RESCUEHELO.SetRescueZone}(*radius*) functions, +-- where *radius* is the radius of the zone in kilometers. If you use multiple rescue helos in the same mission, you might want to ensure that the radii +-- are not overlapping so that two helos try to rescue the same pilot. But it should not hurt either way. +-- +-- Once the helo reaches the crash site, the rescue operation will last 5 minutes. This time can be changed by @{#RESCUEHELO.SetRescueDuration(*time*), +-- where *time* is the duration in minutes. +-- +-- During the rescue operation, the helo will hover (orbit) over the crash site at a speed of 10 km/h. The speed can be set by @{#RESCUEHELO.SetRescueHoverSpeed}(*speed*), +-- where the *speed* is given in km/h. +-- +-- If no rescue operations should be carried out by the helo, this option can be completely disabled by using @{#RESCUEHELO.SetRescueOff}(). -- -- # Finite State Machine -- @@ -164,6 +182,7 @@ -- You have the option to enable the debug mode for this class via the @{#RESCUEHELO.SetDebugModeON} function. -- If enabled, text messages about the helo status will be displayed on screen and marks of the pattern created on the F10 map. -- +-- -- @field #RESCUEHELO RESCUEHELO = { ClassName = "RESCUEHELO", @@ -172,6 +191,7 @@ RESCUEHELO = { carrier = nil, carriertype = nil, helogroupname = nil, + helogroupalias = nil, helo = nil, airbase = nil, takeoff = nil, @@ -190,17 +210,19 @@ RESCUEHELO = { rescuespeed = nil, rescuestopboat = nil, HeloFuel0 = nil, - carrierstop = false, + rtb = nil, + carrierstop = nil, } --- Class version. -- @field #string version -RESCUEHELO.version="0.9.6" +RESCUEHELO.version="0.9.7" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- TODO: Add messages for rescue mission. -- TODO: Add option to stop carrier while rescue operation is in progress? Done but NOT working! -- DONE: Write documenation. -- DONE: Add option to deactivate the rescueing. @@ -250,7 +272,17 @@ function RESCUEHELO:New(carrierunit, helogroupname) self:SetRescueHoverSpeed() self:SetRescueDuration() self:SetRescueStopBoatOff() - + + -- Some more. + self.rtb=false + self.carrierstop=false + + --[[ + BASE:TraceOnOff(true) + BASE:TraceClass("RESCUEHELO") + BASE:TraceLevel(1) + ]] + ----------------------- --- FSM Transitions --- ----------------------- @@ -263,6 +295,7 @@ function RESCUEHELO:New(carrierunit, helogroupname) self:AddTransition("Stopped", "Start", "Running") self:AddTransition("Running", "Rescue", "Rescuing") self:AddTransition("Running", "RTB", "Returning") + self:AddTransition("Rescuing", "RTB", "Returning") self:AddTransition("*", "Run", "Running") self:AddTransition("*", "Status", "*") self:AddTransition("*", "Stop", "Stopped") @@ -301,11 +334,13 @@ function RESCUEHELO:New(carrierunit, helogroupname) --- Triggers the FSM event "RTB" that sends the helo home. -- @function [parent=#RESCUEHELO] RTB -- @param #RESCUEHELO self + -- @param Wrapper.Airbase#AIRBASE airbase The airbase to return to. Default is the home base. --- Triggers the FSM event "RTB" that sends the helo home after a delay. -- @function [parent=#RESCUEHELO] __RTB -- @param #RESCUEHELO self -- @param #number delay Delay in seconds. + -- @param Wrapper.Airbase#AIRBASE airbase The airbase to return to. Default is the home base. --- On after "RTB" event user function. Called when a the the helo returns to its home base. -- @function [parent=#RESCUEHELO] OnAfterRTB @@ -313,6 +348,7 @@ function RESCUEHELO:New(carrierunit, helogroupname) -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. + -- @param Wrapper.Airbase#AIRBASE airbase The airbase to return to. Default is the home base. --- Triggers the FSM event "Run". @@ -369,21 +405,22 @@ function RESCUEHELO:SetHomeBase(airbase) return self end ---- Set rescue zone radius. Crashed or ejected units inside this radius of the carrier will be rescued. +--- Set rescue zone radius. Crashed or ejected units inside this radius of the carrier will be rescued if possible. -- @param #RESCUEHELO self --- @param #number radius Radius of rescue zone in meters. Default is 100000 m = 100 km. +-- @param #number radius Radius of rescue zone in kilometers. Default is 30 km. -- @return #RESCUEHELO self function RESCUEHELO:SetRescueZone(radius) - self.rescuezone=ZONE_UNIT:New("Rescue Zone", self.carrier, radius or 100000) + radius=(radius or 30)*1000 + self.rescuezone=ZONE_UNIT:New("Rescue Zone", self.carrier, radius) return self end --- Set rescue hover speed. -- @param #RESCUEHELO self --- @param #number speed Speed in km/h. Default 25 km/h. +-- @param #number speed Speed in km/h. Default 10 km/h. -- @return #RESCUEHELO self function RESCUEHELO:SetRescueHoverSpeed(speed) - self.rescuespeed=UTILS.KmphToMps(speed or 25) + self.rescuespeed=UTILS.KmphToMps(speed or 10) return self end @@ -471,21 +508,21 @@ function RESCUEHELO:SetAltitude(alt) return self end ---- Set latitudinal offset to carrier. +--- Set offset parallel to orienation of carrier. -- @param #RESCUEHELO self --- @param #number distance Latitual offset distance in meters. Default 200 m. +-- @param #number distance Offset distance in meters. Default 200 m. -- @return #RESCUEHELO self function RESCUEHELO:SetOffsetX(distance) self.offsetX=distance or 200 return self end ---- Set longitudal offset to carrier. +--- Set offset perpendicular to orientation to carrier. -- @param #RESCUEHELO self --- @param #number distance Longitual offset distance in meters. Default 200 m. +-- @param #number distance Offset distance in meters. Default 100 m. -- @return #RESCUEHELO self function RESCUEHELO:SetOffsetZ(distance) - self.offsetZ=distance or 200 + self.offsetZ=distance or 100 return self end @@ -576,6 +613,13 @@ function RESCUEHELO:IsRescuing() return self:is("Rescuing") end +--- Check if FMS was stopped. +-- @param #RESCUEHELO self +-- @return #boolean If true, is stopped. +function RESCUEHELO:IsStopped() + return self:is("Stopped") +end + ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- EVENT functions ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -587,18 +631,39 @@ function RESCUEHELO:OnEventLand(EventData) local group=EventData.IniGroup --Wrapper.Group#GROUP if group:IsAlive() then + + -- Group name that landed. local groupname=group:GetName() - if groupname:match(self.helogroupname) then + -- Check that it was our helo that landed. + if groupname==self.helo:GetName() then -- Respawn the Helo. local text=string.format("Respawning rescue helo group %s at home base.", groupname) MESSAGE:New(text, 10, "DEBUG"):ToAllIf(self.Debug) self:T(self.lid..text) + if self:IsRescuing() then + + self:T(string.format("Rescue helo %s returned from rescue operation.", groupname)) + + end + if self.takeoff==SPAWN.Takeoff.Air or self.respawninair then - self:E("ERROR: Rescue helo %s landed. This should not happen for Takeoff=Air or respawninair=true!", groupname) + if self:IsRescuing() then + + self:T(string.format("Rescue helo %s returned from rescue operation.", groupname)) + + else + + self:T2(string.format("WARNING: Rescue helo %s landed. This should not happen for Takeoff=Air or respawninair=true unless a rescue operation finished.", groupname)) + + end + + -- Respawn helo at current airbase anyway. + self.helo=group:RespawnAtCurrentAirbase() + else @@ -609,6 +674,7 @@ function RESCUEHELO:OnEventLand(EventData) -- Restart the formation. self:__Run(10) + end end end @@ -686,10 +752,13 @@ function RESCUEHELO:onafterStart(From, Event, To) self:HandleEvent(EVENTS.Ejection, self._OnEventCrashOrEject) -- Delay before formation is started. - local delay=120 + local delay=120 - -- Spawn helo. - local Spawn=SPAWN:New(self.helogroupname):InitUnControlled(false) + -- Set unique alias for spawn. + self.helogroupalias=string.format("%s_%s", self.helogroupname, self.carrier:GetName()) + + -- Spawn helo. We need to introduce an alias in case this class is used twice. This would confuse the spawn routine. + local Spawn=SPAWN:NewWithAlias(self.helogroupname, self.helogroupalias) -- Spawn in air or at airbase. if self.takeoff==SPAWN.Takeoff.Air then @@ -697,11 +766,11 @@ function RESCUEHELO:onafterStart(From, Event, To) -- Carrier heading local hdg=self.carrier:GetHeading() - -- Spawn distance behind carrier. + -- Spawn distance in front of carrier. local dist=UTILS.NMToMeters(0.2) - -- Coordinate behind the carrier - local Carrier=self.carrier:GetCoordinate():SetAltitude(math.min(100, self.altitude)):Translate(dist, hdg) + -- Coordinate behind the carrier. Altitude at least 100 meters for spawning because it drops down a bit. + local Carrier=self.carrier:GetCoordinate():SetAltitude(math.max(140, self.altitude)):Translate(dist, hdg) -- Orientation of spawned group. Spawn:InitHeading(hdg) @@ -720,6 +789,9 @@ function RESCUEHELO:onafterStart(From, Event, To) -- Use an uncontrolled aircraft group. self.helo=GROUP:FindByName(self.helogroupname) + -- Also set the alias just in case. + self.helogroupalias=self.helogroupname + if self.helo:IsAlive() then -- Start uncontrolled group. @@ -770,7 +842,6 @@ function RESCUEHELO:onafterStart(From, Event, To) -- Init status check self:__Status(1) - end --- On after Status event. Checks player status. @@ -791,13 +862,50 @@ function RESCUEHELO:onafterStatus(From, Event, To) MESSAGE:New(text, 10, "DEBUG"):ToAllIf(self.Debug) self:T(self.lid..text) - -- If fuel < threshold ==> send helo to home base! - if fuel