diff --git a/Moose Development/Moose/Core/Point.lua b/Moose Development/Moose/Core/Point.lua index 25efa066e..8a6e0ffda 100644 --- a/Moose Development/Moose/Core/Point.lua +++ b/Moose Development/Moose/Core/Point.lua @@ -342,18 +342,18 @@ do -- COORDINATE return x - Precision <= self.x and x + Precision >= self.x and z - Precision <= self.z and z + Precision >= self.z end - --- Returns if the 2 coordinates are at the same 2D position. + --- Scan/find objects (units, statics, scenery) within a certain radius around the coordinate using the world.searchObjects() DCS API function. -- @param #COORDINATE self -- @param #number radius (Optional) Scan radius in meters. Default 100 m. -- @param #boolean scanunits (Optional) If true scan for units. Default true. -- @param #boolean scanstatics (Optional) If true scan for static objects. Default true. -- @param #boolean scanscenery (Optional) If true scan for scenery objects. Default false. - -- @return True if units were found. - -- @return True if statics were found. - -- @return True if scenery objects were found. - -- @return Unit objects found. - -- @return Static objects found. - -- @return Scenery objects found. + -- @return #boolean True if units were found. + -- @return #boolean True if statics were found. + -- @return #boolean True if scenery objects were found. + -- @return #table Table of MOOSE @[#Wrapper.Unit#UNIT} objects found. + -- @return #table Table of DCS static objects found. + -- @return #table Table of DCS scenery objects found. function COORDINATE:ScanObjects(radius, scanunits, scanstatics, scanscenery) self:F(string.format("Scanning in radius %.1f m.", radius)) @@ -405,18 +405,17 @@ do -- COORDINATE local ObjectCategory = ZoneObject:getCategory() -- Check for unit or static objects - --if (ObjectCategory == Object.Category.UNIT and ZoneObject:isExist() and ZoneObject:isActive()) then - if (ObjectCategory == Object.Category.UNIT and ZoneObject:isExist()) then + if ObjectCategory==Object.Category.UNIT and ZoneObject:isExist() then table.insert(Units, UNIT:Find(ZoneObject)) gotunits=true - elseif (ObjectCategory == Object.Category.STATIC and ZoneObject:isExist()) then + elseif ObjectCategory==Object.Category.STATIC and ZoneObject:isExist() then table.insert(Statics, ZoneObject) gotstatics=true - elseif ObjectCategory == Object.Category.SCENERY then + elseif ObjectCategory==Object.Category.SCENERY then table.insert(Scenery, ZoneObject) gotscenery=true diff --git a/Moose Development/Moose/Core/Radio.lua b/Moose Development/Moose/Core/Radio.lua index 89e6b9d95..90bfa23ef 100644 --- a/Moose Development/Moose/Core/Radio.lua +++ b/Moose Development/Moose/Core/Radio.lua @@ -480,7 +480,7 @@ end -- -- myBeacon:TACAN(20, "Y", "TEXACO", true) -- Activate the beacon function BEACON:ActivateTACAN(Channel, Mode, Message, Bearing, Duration) - self:F({TACANChannel, Message, Bearing, BeaconDuration}) + self:I({channel=Channel, mode=Mode, callsign=Message, bearing=Bearing, duration=Duration}) -- Get frequency. local Frequency=UTILS.TACANToFrequency(Channel, Mode) @@ -496,12 +496,6 @@ function BEACON:ActivateTACAN(Channel, Mode, Message, Bearing, Duration) self:E({"The POSITIONABLE you want to attach the AA Tacan Beacon is not an aircraft! The BEACON is not emitting.", self.Positionable}) end - -- Using the beacon type 4 (BEACON_TYPE_TACAN). For System, I'm using 5 (TACAN_TANKER_MODE_Y) if the beacon shows its bearing or 14 (TACAN_AA_MODE_Y) if it does not. - local System=14 - if Bearing then - System = 5 - end - -- Beacon type. local Type=BEACON.Type.TACAN @@ -517,14 +511,14 @@ function BEACON:ActivateTACAN(Channel, Mode, Message, Bearing, Duration) -- Attached unit. local UnitID=self.Positionable:GetID() - -- Debug + -- Debug. self:T({"TACAN BEACON started!"}) -- Start beacon. self.Positionable:CommandActivateBeacon(Type, System, Frequency, UnitID, Channel, Mode, AA, Message, Bearing) - -- Stop sheduler - if Duration then -- Schedule the stop of the BEACON if asked by the MD + -- Stop sheduler. + if Duration then self.Positionable:DeactivateBeacon(Duration) end diff --git a/Moose Development/Moose/Functional/CarrierTrainer.lua b/Moose Development/Moose/Functional/CarrierTrainer.lua index e4caa999f..ef68dfc38 100644 --- a/Moose Development/Moose/Functional/CarrierTrainer.lua +++ b/Moose Development/Moose/Functional/CarrierTrainer.lua @@ -14,7 +14,7 @@ -- -- === -- --- ### Authors: **funkyfranky** (MOOSE class implementation and enhancements), **Bankler** (original idea and script) +-- ### Authors: **funkyfranky**, **Bankler** (Carrier trainer idea and script) -- -- @module Functional.Airboss -- @image MOOSE.JPG @@ -34,7 +34,7 @@ -- @field Core.Radio#RADIO LSOradio Radio for LSO calls. -- @field Core.Radio#RADIO Carrierradio Radio for carrier calls. -- @field Core.Zone#ZONE_UNIT startZone Zone in which the pattern approach starts. --- @field Core.Zone#ZONE_UNIT giantZone Large zone around the carrier to welcome players. +-- @field Core.Zone#ZONE_UNIT carrierZone Large zone around the carrier to welcome players. -- @field Core.Zone#ZONE_UNIT registerZone Zone behind the carrier to register for a new approach. -- @field #table players Table of players. -- @field #table menuadded Table of units where the F10 radio menu was added. @@ -49,6 +49,8 @@ -- @field #number rwyangle Angle of the runway wrt to carrier "nose". For the Stennis ~ -10 degrees. -- @field #number sterndist Distance in meters from carrier coordinate to the end of the deck. -- @field #number deckheight Height of the deck in meters. +-- @field #table Qmarshal Queue of marshalling aircraft groups. +-- @field #table Qpattern Queue of aircraft groups in the landing pattern. -- @extends Core.Fsm#FSM --- Practice Carrier Landings @@ -79,7 +81,7 @@ AIRBOSS = { Carrierfreq = nil, registerZone = nil, startZone = nil, - giantZone = nil, + carrierZone = nil, players = {}, menuadded = {}, Upwind = {}, @@ -90,7 +92,7 @@ AIRBOSS = { Wake = {}, Groove = {}, Trap = {}, - rwyangle = -10, + rwyangle = -9, sterndist =-100, deckheight = 22, Qpattern = {}, @@ -276,7 +278,7 @@ AIRBOSS.MenuF10={} --- Carrier trainer class version. -- @field #string version -AIRBOSS.version="0.2.1w" +AIRBOSS.version="0.2.2" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list @@ -312,11 +314,14 @@ function AIRBOSS:New(carriername, alias) -- Set carrier unit. self.carrier=UNIT:FindByName(carriername) + -- Carrier zones. if self.carrier then - -- Carrier zones. - self.registerZone = ZONE_UNIT:New("registerZone", self.carrier, 2500, {dx = -5000, dy = 100, relative_to_unit=true}) - self.startZone = ZONE_UNIT:New("startZone", self.carrier, 1000, {dx = -2000, dy = 100, relative_to_unit=true}) - self.giantZone = ZONE_UNIT:New("giantZone", self.carrier, 30000, {dx = 0, dy = 0, relative_to_unit=true}) + -- Zone 5 km astern and 100 m starboard of the carrier with radius of 2.5 km. + self.registerZone = ZONE_UNIT:New("registerZone", self.carrier, 2.5*1000, {dx = -5000, dy = 100, relative_to_unit=true}) + -- Zone 2 km astern and 100 m starboard of the carrier with a radius of 1 km. + self.startZone = ZONE_UNIT:New("startZone", self.carrier, 1.0*1000, {dx = -2000, dy = 100, relative_to_unit=true}) + -- Zone around the carrier with a radius of 30 km. + self.carrierZone = ZONE_UNIT:New("carrierZone", self.carrier, 10.0*1000) else -- Carrier unit does not exist error. local text=string.format("ERROR: Carrier unit %s could not be found! Make sure this UNIT is defined in the mission editor and check the spelling of the unit name carefully.", carriername) @@ -461,7 +466,7 @@ function AIRBOSS:onafterStart(From, Event, To) self:I(self.lid..string.format("Starting Carrier Training %s for carrier unit %s of type %s.", AIRBOSS.version, self.carrier:GetName(), self.carriertype)) -- Activate TACAN. - if self.TACANchannel~=nil and self.TACANmolde~=nil then + if self.TACANchannel~=nil and self.TACANmode~=nil then self.beacon:ActivateTACAN(self.TACANchannel, self.TACANmode, "STN", true) end @@ -486,6 +491,9 @@ end -- @param #string To To state. function AIRBOSS:onafterStatus(From, Event, To) + -- Scan carrier zone for new aircraft. + self:_ScanCarrierZone() + -- Check player status. self:_CheckPlayerStatus() @@ -503,7 +511,92 @@ function AIRBOSS:onafterStop(From, Event, To) self:UnHandleEvent(EVENTS.Land) end ---- Carrier trainer event handler for event birth. +--- Check if new aircraft arrived +-- @param #AIRBOSS self +function AIRBOSS:_ScanCarrierZone() + + env.info("FF Scanning Carrier Zone") + + -- Carrier position. + local coord=self.carrier:GetCoordinate() + + -- Scan units in carrier zone. + local _,_,_,unitsin =coord:ScanObjects(10*1000, true, false, false) + --local _,_,_,unitsout=coord:ScanObjects(15*1000, true, false, false) + + for _,_unit in pairs(unitsin) do + local unit=_unit --Wrapper.Unit#UNIT + + if unit:IsAir() then + + local group=unit:GetGroup() + local unitname=unit:GetName() + local groupname=group:GetName() + + local text=string.format("In carrier zone: unit=%s group=%s", unitname, groupname) + --env.info(text) + + if self.Qmarshal[groupname]==nil then + + env.info("FF marshal group="..groupname) + self:_Marshal(group) + + end + + end + end + +--[[ + for _,_unitin in pairs(unitsin) do + local unitin=_unitin --Wrapper.Unit#UNIT + if unit:IsAir()() then + local text=string.format("Aircraft in carrier zone = ", unit:GetName()) + env.info(text) + end + end +]] + +end + +--- Orbit at a specified position at a specified alititude with a specified speed. +-- @param #AIRBOSS self +-- @param Wrapper.Group#GROUP group Group +function AIRBOSS:_Marshal(group) + + local groupname=group:GetName() + + local Coord=self.carrier:GetCoordinate() + local Altitude=UTILS.FeetToMeters(2000) + local Speed=UTILS.KnotsToMps(272) + + local DCSTask={} + DCSTask.id="ControlledTask" + DCSTask.params={} + DCSTask.params.task=group:TaskOrbit(Coord, Altitude, Speed) + DCSTask.params.stopCondition={userFlag=groupname, userFlagValue=1} + + -- Set waypoint landing on Carrier. + local wp={} + wp[1]=self.carrier:GetCoordinate():SetAltitude(Altitude):WaypointAirTurningPoint(nil, Speed, {DCSTask}, string.format("Marshal @ %d ft %d knots", Altitude, Speed)) + wp[2]=self.carrier:GetCoordinate():WaypointAirLanding(Speed, AIRBASE:FindByName(self.carrier:GetName()), nil, "Landing") + group:WayPointInitialize(wp) + group:Route(wp, 0) + + self.Qmarshal[groupname]=group +end + + + +--- Check if new aircraft group arrived. +-- @param #AIRBOSS self +-- @param Wrapper.Group#GROUP group Aircraft group. +function AIRBOSS:_AddGroupMarshall(group) + local groupname=group:GetName() + self.Qmarshal[groupname]=group + +end + +--- Check current player status. -- @param #AIRBOSS self function AIRBOSS:_CheckPlayerStatus() @@ -523,7 +616,7 @@ function AIRBOSS:_CheckPlayerStatus() self:_DetailedPlayerStatus(playerData) end - if unit:IsInZone(self.giantZone) then + if unit:IsInZone(self.carrierZone) then -- Check if player was previously not inside the zone. if playerData.inbigzone==false then @@ -736,7 +829,7 @@ function AIRBOSS:_InitPlayer(unitname) playerData.difficulty=playerData.difficulty or AIRBOSS.Difficulty.NORMAL -- Player is in the big zone around the carrier. - playerData.inbigzone=playerData.unit:IsInZone(self.giantZone) + playerData.inbigzone=playerData.unit:IsInZone(self.carrierZone) -- Init stuff for this round. playerData=self:_InitNewRound(playerData) diff --git a/Moose Development/Moose/Wrapper/Controllable.lua b/Moose Development/Moose/Wrapper/Controllable.lua index 30a1d73dd..91bc437a4 100644 --- a/Moose Development/Moose/Wrapper/Controllable.lua +++ b/Moose Development/Moose/Wrapper/Controllable.lua @@ -550,9 +550,9 @@ end ---- Executes a command action +--- Executes a command action for the CONTROLLABLE. -- @param #CONTROLLABLE self --- @param DCS#Command DCSCommand +-- @param DCS#Command DCSCommand The command to be executed. -- @return #CONTROLLABLE self function CONTROLLABLE:SetCommand( DCSCommand ) self:F2( DCSCommand ) @@ -640,8 +640,9 @@ function CONTROLLABLE:StartUncontrolled(delay) return self end ---- Give the CONTROLLABLE the command to activate a beacon. See See https://wiki.hoggitworld.com/view/DCS_command_activateBeacon +--- Give the CONTROLLABLE the command to activate a beacon. See [DCS_command_activateBeacon](https://wiki.hoggitworld.com/view/DCS_command_activateBeacon) on Hoggit. -- For specific beacons like TACAN use the more convenient @{#BEACON} class. +-- Note that a controllable can only have one beacon activated at a time with the execption of ICLS. -- @param #CONTROLLABLE self -- @param Core.Radio#BEACON.Type Type Beacon type (VOR, DME, TACAN, RSBN, ILS etc). -- @param Core.Radio#BEACON.System System Beacon system (VOR, DME, TACAN, RSBN, ILS etc). @@ -649,9 +650,9 @@ end -- @param #number UnitID The ID of the unit the beacon is attached to. Usefull if more units are in one group. -- @param #number Channel Channel the beacon is using. For, e.g. TACAN beacons. -- @param #string ModeChannel The TACAN mode of the beacon, i.e. "X" or "Y". --- @param #boolean AA If true, create and Air-Air beacon. IF nil, automatically set if CONTROLLABLE is an air unit. +-- @param #boolean AA If true, create and Air-Air beacon. IF nil, automatically set if CONTROLLABLE depending on whether unit is and aircraft or not. -- @param #string Callsign Morse code identification callsign. --- @param #boolean Bearing If true, beacon provides bearing information (if supported). +-- @param #boolean Bearing If true, beacon provides bearing information - if supported by the unit the beacon is attached to. -- @param #number Delay (Optional) Delay in seconds before the beacon is activated. -- @return #CONTROLLABLE self function CONTROLLABLE:CommandActivateBeacon(Type, System, Frequency, UnitID, Channel, ModeChannel, AA, Callsign, Bearing, Delay) @@ -734,6 +735,25 @@ function CONTROLLABLE:CommandDeactivateBeacon(Delay) return self end +--- Deactivate the ICLS of the CONTROLLABLE. +-- @param #CONTROLLABLE self +-- @param #number Delay (Optional) Delay in seconds before the ICLS is deactivated. +-- @return #CONTROLLABLE self +function CONTROLLABLE:CommandDeactivateICLS(Delay) + self:F() + + -- Command to deactivate + local CommandDeactivateICLS={id='DeactivateICLS', params={}} + + if Delay and Delay>0 then + SCHEDULER:New(nil, self.CommandDeactivateICLS, {self}, Delay) + else + self:SetCommand(CommandDeactivateICLS) + end + + return self +end + -- TASKS FOR AIR CONTROLLABLES --- (AIR) Attack a Controllable.