diff --git a/Moose Development/Moose/Functional/Artillery.lua b/Moose Development/Moose/Functional/Artillery.lua index 3c24e381a..9e389bebe 100644 --- a/Moose Development/Moose/Functional/Artillery.lua +++ b/Moose Development/Moose/Functional/Artillery.lua @@ -3428,7 +3428,7 @@ function ARTY:_FireAtCoord(coord, radius, nshells, weapontype) local fire=group:TaskFireAtPoint(vec2, radius, nshells, weapontype) -- Execute task. - group:PushTask(fire) + group:SetTask(fire) end --- Set task for firing at a coordinate. diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index 8d2bfd8da..0ff01cfbd 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -1745,7 +1745,7 @@ _WAREHOUSEDB = { --- Warehouse class version. -- @field #string version -WAREHOUSE.version="0.9.3" +WAREHOUSE.version="0.9.4" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO: Warehouse todo list. @@ -3686,7 +3686,7 @@ function WAREHOUSE:onafterAddAsset(From, Event, To, group, ngroups, forceattribu self:_DebugMessage(string.format("Warehouse %s: Adding %d NEW assets of group %s to stock.", self.alias, n, tostring(group:GetName())), 5) -- This is a group that is not in the db yet. Add it n times. - local assets=self:_RegisterAsset(group, n, forceattribute, forcecargobay, forceweight, loadradius, liveries, skill) + local assets=self:_RegisterAsset(group, n, forceattribute, forcecargobay, forceweight, loadradius, liveries, skill, assignment) -- Add created assets to stock of this warehouse. for _,asset in pairs(assets) do @@ -3722,8 +3722,9 @@ end -- @param #number loadradius Radius in meters when cargo is loaded into the carrier. -- @param #table liveries Table of liveries. -- @param DCS#AI.Skill skill Skill of AI. +-- @param #string assignment Assignment attached to the asset item. -- @return #table A table containing all registered assets. -function WAREHOUSE:_RegisterAsset(group, ngroups, forceattribute, forcecargobay, forceweight, loadradius, liveries, skill) +function WAREHOUSE:_RegisterAsset(group, ngroups, forceattribute, forcecargobay, forceweight, loadradius, liveries, skill, assignment) self:F({groupname=group:GetName(), ngroups=ngroups, forceattribute=forceattribute, forcecargobay=forcecargobay, forceweight=forceweight}) -- Set default. @@ -3825,6 +3826,7 @@ function WAREHOUSE:_RegisterAsset(group, ngroups, forceattribute, forcecargobay, asset.livery=liveries[math.random(#liveries)] end asset.skill=skill + asset.assignment=assignment if i==1 then self:_AssetItemInfo(asset) @@ -3939,8 +3941,15 @@ function WAREHOUSE:onbeforeAddRequest(From, Event, To, warehouse, AssetDescripto okay=false end + elseif AssetDescriptor==WAREHOUSE.Descriptor.ASSIGNMENT then + + if type(AssetDescriptorValue)~="string" then + self:_ErrorMessage("ERROR: Invalid request. Asset assignment type must be passed as a string!", 5) + okay=false + end + else - self:_ErrorMessage("ERROR: Invalid request. Asset descriptor is not ATTRIBUTE, CATEGORY, GROUPNAME or UNITTYPE!", 5) + self:_ErrorMessage("ERROR: Invalid request. Asset descriptor is not ATTRIBUTE, CATEGORY, GROUPNAME, UNITTYPE or ASSIGNMENT!", 5) okay=false end diff --git a/Moose Development/Moose/Ops/Airboss.lua b/Moose Development/Moose/Ops/Airboss.lua index 8ebe84dc4..0764f2490 100644 --- a/Moose Development/Moose/Ops/Airboss.lua +++ b/Moose Development/Moose/Ops/Airboss.lua @@ -16,7 +16,7 @@ -- * Advanced F10 radio menu including carrier info, weather, radio frequencies, TACAN/ICLS channels, player LSO grades, marking of zones etc. -- * Recovery tanker and refueling option via integration of @{Ops.RecoveryTanker} class. -- * Rescue helicopter option via @{Ops.RescueHelo} class. --- * Combine multiple human players to sections (WIP). +-- * Combine multiple human players to sections. -- * Many parameters customizable by convenient user API functions. -- * Multiple carrier support due to object oriented approach. -- * Unlimited number of players. @@ -32,7 +32,7 @@ -- **Supported Aircraft:** -- -- * [F/A-18C Hornet Lot 20](https://forums.eagle.ru/forumdisplay.php?f=557) (Player & AI) --- * [F-14B Tomcat](https://forums.eagle.ru/forumdisplay.php?f=395) (Player & AI) [**WIP**] +-- * [F-14B Tomcat](https://forums.eagle.ru/forumdisplay.php?f=395) (Player & AI) -- * [A-4E Skyhawk Community Mod](https://forums.eagle.ru/showthread.php?t=224989) (Player & AI) -- * [AV-8B N/A Harrier](https://forums.eagle.ru/forumdisplay.php?f=555) (Player & AI) [**WIP**] -- * F/A-18C Hornet (AI) @@ -46,10 +46,8 @@ -- the no other fixed wing aircraft (human or AI controlled) are supposed to land on the Tarawa. Currently only Case I is supported. Case II/III take slightly steps from the CVN carrier. -- However, the two Case II/III pattern are very similar so this is not a big drawback. -- --- Heatblur's mighty F-14B Tomcat has just been added (March 13th 2019). Beware that this is currently WIP - both the module and the AIRBOSS implementation. +-- Heatblur's mighty F-14B Tomcat has been added (March 13th 2019) as well. -- --- **PLEASE NOTE** that his class is work in progress. Many/most things work already very nicely but there a lot of cases I did not run into yet. --- Therefore, your *constructive* feedback is both necessary and appreciated! -- -- ## Discussion -- @@ -174,7 +172,6 @@ -- @field #number NmaxStack Number of max flights per stack. Default 2. -- @field #boolean handleai If true (default), handle AI aircraft. -- @field Ops.RecoveryTanker#RECOVERYTANKER tanker Recovery tanker flying overhead of carrier. --- @field Functional.Warehouse#WAREHOUSE warehouse Warehouse object of the carrier. -- @field DCS#Vec3 Corientation Carrier orientation in space. -- @field DCS#Vec3 Corientlast Last known carrier orientation. -- @field Core.Point#COORDINATE Cposition Carrier position. @@ -1182,7 +1179,6 @@ AIRBOSS = { NmaxStack = nil, handleai = nil, tanker = nil, - warehouse = nil, Corientation = nil, Corientlast = nil, Cposition = nil, @@ -1802,6 +1798,15 @@ function AIRBOSS:New(carriername, alias) -- Init player scores table. self.playerscores={} + + -- Initialize ME waypoints. + self:_InitWaypoints() + + -- Current waypoint. + self.currentwp=1 + + -- Patrol route. + self:_PatrolRoute() ------------- --- Defaults: @@ -2432,6 +2437,19 @@ function AIRBOSS:SetExcludeAI(setgroup) return self end +--- Add a group to the exclude set. If no set exists, it is created. +-- @param #AIRBOSS self +-- @param Wrapper.Group#GROUP group The group to be excluded. +-- @return #AIRBOSS self +function AIRBOSS:AddExcludeAI(group) + + self.excludesetAI=self.excludesetAI or SET_GROUP:New() + + self.excludesetAI:AddGroup(group) + + return self +end + --- Close currently running recovery window and stop recovery ops. Recovery window is deleted. -- @param #AIRBOSS self -- @param #number delay (Optional) Delay in seconds before the window is deleted. @@ -3044,15 +3062,6 @@ function AIRBOSS:SetRecoveryTanker(recoverytanker) return self end ---- Define warehouse associated with the carrier. --- @param #AIRBOSS self --- @param Functional.Warehouse#WAREHOUSE warehouse Warehouse object of the carrier. --- @return #AIRBOSS self -function AIRBOSS:SetWarehouse(warehouse) - self.warehouse=warehouse - return self -end - --- Set default player skill. New players will be initialized with this skill. -- -- * "Flight Student" = @{#AIRBOSS.Difficulty.Easy} @@ -3202,9 +3211,6 @@ function AIRBOSS:onafterStart(From, Event, To) self.Corientlast=self.Corientation self.Tpupdate=timer.getTime() - -- Init patrol route of carrier. - self:_PatrolRoute(1) - -- Check if no recovery window is set. DISABLED! if #self.recoverytimes==0 and false then @@ -3232,9 +3238,6 @@ function AIRBOSS:onafterStart(From, Event, To) self:HandleEvent(EVENTS.PlayerLeaveUnit, self._PlayerLeft) self:HandleEvent(EVENTS.MissionEnd) - -- DCS event handler. - --world.addEventHandler(self) - -- Start status check in 1 second. self:__Status(1) end @@ -13219,6 +13222,42 @@ function AIRBOSS:_GetNextWaypoint() return nextwp,Nextwp end + +--- Initialize Mission Editor waypoints. +-- @param #AIRBOSS self +-- @return #AIRBOSS self +function AIRBOSS:_InitWaypoints() + + -- Waypoints of group as defined in the ME. + local Waypoints=self.carrier:GetGroup():GetTemplateRoutePoints() + + -- Init array. + self.waypoints={} + + -- Set waypoint table. + for i,point in ipairs(Waypoints) do + + -- Coordinate of the waypoint + local coord=COORDINATE:New(point.x, point.alt, point.y) + + -- Set velocity of the coordinate. + coord:SetVelocity(point.speed) + + -- Add to table. + table.insert(self.waypoints, coord) + + -- Debug info. + if self.Debug then + coord:MarkToAll(string.format("Carrier Waypoint %d, Speed=%.1f knots", i, UTILS.MpsToKnots(point.speed))) + end + + end + + return self +end + +--[[ + --- Patrol carrier. -- @param #AIRBOSS self -- @param #number n Current waypoint. @@ -13272,6 +13311,58 @@ function AIRBOSS:_PatrolRoute(n) return self end +]] + +--- Patrol carrier. +-- @param #AIRBOSS self +-- @param #number n Next waypoint number. +-- @return #AIRBOSS self +function AIRBOSS:_PatrolRoute(n) + + -- Get next waypoint coordinate and number. + local nextWP, N=self:_GetNextWaypoint() + + -- Default resume is to next waypoint. + n=n or N + + -- Get carrier group. + local CarrierGroup=self.carrier:GetGroup() + + -- Waypoints table. + local Waypoints={} + + -- Create a waypoint from the current coordinate. + local wp=self:GetCoordinate():WaypointGround(CarrierGroup:GetVelocityKMH()) + + -- Add current position as first waypoint. + table.insert(Waypoints, wp) + + -- Loop over waypoints. + for i=n,#self.waypoints do + local coord=self.waypoints[i] --Core.Point#COORDINATE + + -- Create a waypoint from the coordinate. + local wp=coord:WaypointGround(UTILS.MpsToKmph(coord.Velocity)) + + -- Passing waypoint taskfunction + local TaskPassingWP=CarrierGroup:TaskFunction("AIRBOSS._PassingWaypoint", self, i, #self.waypoints) + + -- Call task function when carrier arrives at waypoint. + CarrierGroup:SetTaskWaypoint(wp, TaskPassingWP) + + -- Add waypoint to table. + table.insert(Waypoints, wp) + end + + -- Route carrier group. + CarrierGroup:Route(Waypoints) + + return self +end + + + + --- Estimated the carrier position at some point in the future given the current waypoints and speeds. -- @param #AIRBOSS self -- @return DCS#time ETA abs. time in seconds. diff --git a/Moose Development/Moose/Ops/Skipper.lua b/Moose Development/Moose/Ops/Skipper.lua index 9d98dccb2..a15a5f43a 100644 --- a/Moose Development/Moose/Ops/Skipper.lua +++ b/Moose Development/Moose/Ops/Skipper.lua @@ -15,23 +15,14 @@ -- @type SKIPPER -- @field #string ClassName Name of the class. -- @field #boolean Debug Debug mode. Messages to all about status. --- @field #string lid Class id string for output to DCS log file. --- @field #string theatre The DCS map used in the mission. +-- @field #string sid Class id string for output to DCS log file. -- @field #string carriername The name of the carrier unit. --- @field Wrapper.Unit#UNIT carrier The carrier unit. -- @field Wrapper.Group#GROUP group The carrier strike group. --- @field #string alias Skipper alias. -- @field #table menu Table of menu items. --- @field #boolean menusingle If true, menus are optimized for a single carrier per coalition. --- @field #table waypoints Table of waypoint coordinates as defined in the mission editor. --- @field #number currentwp Current waypoint, i.e. the one that was passed last. Counting starts a one. --- @field Ops.Airboss#AIRBOSS airboss The airboss of the carrier. -- @field Functional.Warehouse#WAREHOUSE warehouse The warehouse of the carrier. -- @field Functional.Artillery#ARTY arty The artillery object of the carrier. --- @field Core.Zone#ZONE_UNIT zoneCCA Carrier Controlled Area, 50 NM zone around the carrier. -- @field #table intruders Table of intruders, i.e. groups inside the CCA. Each element is of type #SKIPPPER.Intruder. --- @field #boolean adinfinitum If true, carrier patrols ad infinitum, i.e. after reaching the final waypoint continues again. --- @extends Core.Fsm#FSM +-- @extends Ops.Airboss#AIRBOSS --- Be surprised! -- @@ -46,24 +37,13 @@ -- @field #SKIPPER SKIPPER = { ClassName = "SKIPPER", - Debug = false, - lid = nil, - theatre = nil, + sid = nil, carriername = nil, - carrier = nil, group = nil, - alias = nil, menu = nil, - menusingle = nil, - waypoints = nil, - currentwp = nil, - airboss = nil, warehouse = nil, arty = nil, - zoneCCA = nil, - zoneCCZ = nil, intruders = {}, - adinfinitum = nil, } --- Intruder. @@ -82,7 +62,7 @@ SKIPPER = { --- FlightControl class version. -- @field #string version -SKIPPER.version="0.0.3" +SKIPPER.version="0.0.5" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list @@ -102,17 +82,19 @@ SKIPPER.version="0.0.3" function SKIPPER:New(carriername, alias) -- Inherit everything from FSM class. - local self=BASE:Inherit(self, FSM:New()) -- #SKIPPER - - self.carriername=carriername - self.carrier=UNIT:FindByName(carriername) + --local self=BASE:Inherit(self, FSM:New()) -- #SKIPPER + local self=BASE:Inherit(self, AIRBOSS:New(carriername, alias)) -- #SKIPPER if not self.carrier then BASE:E(string.format("ERROR: Could not find carrier %s!", carriername)) return nil end - self.alias=alias or carriername + --self.alias=alias or carriername + + --self: + + self.carriername=carriername self.group=self.carrier:GetGroup() @@ -120,10 +102,19 @@ function SKIPPER:New(carriername, alias) self.warehouse=WAREHOUSE:New(carriername, self.alias) - self.airboss=AIRBOSS:New(carriername, self.alias) + local skipper=self + + --- Function for WAREHOUSE self request events. + function self.warehouse:OnAfterSelfRequest(From,Event,To,groupset,request) + env.info("FF Warehous self request!") + skipper:_SelfRequest(self, groupset, request) + end + -- Set some string id for output to DCS.log file. - self.lid=string.format("SKIPPER %s |", self.carriername) + self.sid=string.format("SKIPPER %s |", self.carriername) + + --[[ -- Current map. self.theatre=env.mission.theatre @@ -143,10 +134,14 @@ function SKIPPER:New(carriername, alias) -- Start State. self:SetStartState("Stopped") + --]] + -- Add FSM transitions. -- From State --> Event --> To State - self:AddTransition("Stopped", "Start", "Running") -- Start FSM. - self:AddTransition("*", "Status", "*") -- Update status. + --self:AddTransition("Stopped", "Start", "Running") -- Start FSM. + --self:AddTransition("*", "Status", "*") -- Update status. + + self:AddTransition("*", "SkipperStatus", "*") -- Skipper status update. self:AddTransition("*", "IntruderAlert", "*") -- New intruder detected. ------------------------ @@ -244,14 +239,6 @@ function SKIPPER:GetCoordinate() return self.carrier:GetCoordinate() end - ---- Get SKIPPER object associated with the carrier. --- @param #SKIPPER self --- @return Ops.Airboss#AIRBOSS Airboss object. -function SKIPPER:GetAirboss() - return self.airboss -end - --- Get WAREHOUSE object associated with the carrier. -- @param #SKIPPER self -- @return Functional.Warehouse#WAREHOUSE Warehouse object. @@ -273,10 +260,13 @@ end --- Start SKIPPER FSM. Handle events. -- @param #SKIPPER self -function SKIPPER:onafterStart() +function SKIPPER:onafterStart(From, Event, To) - -- Events are handled my MOOSE. - self:I(self.lid..string.format("Starting SKIPPER v%s for carrier %s on map %s.", SKIPPER.version, self.carriername, self.theatre)) + -- Start parent airboss. + self:GetParent(self).onafterStart(self, From, Event, To) + + -- Info. + self:I(self.sid..string.format("Starting SKIPPER v%s for carrier %s on map %s.", SKIPPER.version, self.carriername, self.theatre)) -- Start ARTY. self.arty:Start() @@ -284,9 +274,6 @@ function SKIPPER:onafterStart() -- Start Warehouse. self.warehouse:Start() - -- Start Airboss. - self.airboss:Start() - -- Add F10 radio menu. self:_SetMenuCoalition() @@ -299,29 +286,26 @@ function SKIPPER:onafterStart() self:HandleEvent(EVENTS.Crash) -- Init status updates. - self:__Status(-1) + self:__SkipperStatus(-1) end --- Update status. -- @param #SKIPPER self -function SKIPPER:onafterStatus() +function SKIPPER:onafterSkipperStatus(From, Event, To) + + -- Get State of parent airboss. + --self:GetParent(self).onafterStatus(self, From, Event, To) local fsmstate=self:GetState() -- Check zone for flights inbound. self:_CheckIntruder() - -- Check parking spots. - --self:_CheckParking() - - -- Check waiting and landing queue. - --self:_CheckQueues() - -- Info text. local text=string.format("State %s", fsmstate) - self:I(self.lid..text) + self:I(self.sid..text) - self:__Status(-30) + self:__SkipperStatus(-30) end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -343,7 +327,7 @@ function SKIPPER:_CheckIntruder() local RCCZ=self.zoneCCA:GetRadius() -- Debug info. - self:T(self.lid..string.format("Scanning Carrier Controlled Area. Radius=%.1f NM.", UTILS.MetersToNM(RCCZ))) + self:T(self.sid..string.format("Scanning Carrier Controlled Area. Radius=%.1f NM.", UTILS.MetersToNM(RCCZ))) -- Scan units in carrier zone. local _,_,_,unitscan=coord:ScanObjects(RCCZ, true, false, false) @@ -372,6 +356,9 @@ function SKIPPER:_CheckIntruder() end end + -- Intruder menu needs update. + local updateintrudermenu=false + -- Find out if any known intruder is not in the CCA any more. for i=#self.intruders,1,-1 do local intruder=self.intruders[i] --#SKIPPER.Intruder @@ -386,8 +373,10 @@ function SKIPPER:_CheckIntruder() end end + -- Intruder is gone! if not gotit then table.remove(self.intruders, i) + updateintrudermenu=true end end @@ -406,15 +395,11 @@ function SKIPPER:_CheckIntruder() if not gotit then - -- Get thread level. - local tl, tt=group:GetThreatLevel() - -- Create a new intruder table. local intruder={} --#SKIPPER.Intruder intruder.coalition=group:GetCoalition() intruder.group=group - intruder.threadlevel=tl - intruder.threadtext=tt + intruder.threadlevel, intruder.threadtext=group:GetUnit(1):GetThreatLevel() intruder.time0=timer.getAbsTime() intruder.dist0=self:GetCoordinate():Get2DDistance(group:GetCoordinate()) intruder.groupname=groupname @@ -426,14 +411,20 @@ function SKIPPER:_CheckIntruder() table.insert(self.intruders, intruder) -- Add menu. - self:_AddIntruderMenu(intruder) + --self:_AddIntruderMenu(intruder) -- Trigger alert! self:IntruderAlert(intruder) + updateintrudermenu=true end end + -- Update intruder menu if necessary. + if updateintrudermenu then + self:_SortIntruders() + self:_UpdateIntruderMenu() + end end @@ -442,6 +433,8 @@ end -- Misc Functions ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +--[[ + --- Patrol carrier. -- @param #SKIPPER self -- @return #SKIPPER self @@ -475,6 +468,8 @@ function SKIPPER:_InitWaypoints() return self end + + --- Patrol carrier. -- @param #SKIPPER self -- @param #number n Next waypoint number. @@ -579,6 +574,8 @@ function SKIPPER:_GetNextWaypoint() return nextwp,Nextwp end +]] + ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Menu Functions ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -612,17 +609,11 @@ function SKIPPER:_SetMenuCoalition() end menu.Skipper=MENU_COALITION:New(Coalition, self.alias, menu.Root) end + + ------------------ + -- Defence Menu -- + ------------------ - menu.SetROE = MENU_COALITION:New(Coalition, "Set ROE", menu.Skipper) - menu.SetROE_Hold = MENU_COALITION_COMMAND:New(Coalition, "Weapon Hold", menu.SetROE, self._SetROE, self, "Hold") - menu.SetROE_Free = MENU_COALITION_COMMAND:New(Coalition, "Weapon Free", menu.SetROE, self._SetROE, self, "Free") - menu.SetROE_Return= MENU_COALITION_COMMAND:New(Coalition, "Return Fire", menu.SetROE, self._SetROE, self, "Return") - - -- Alarm state does not seem to apply for ships. - --menu.SetROE_Green = MENU_COALITION_COMMAND:New(Coalition, "State Green", menu.SetROE, self._SetALS, self, "Green") - --menu.SetROE_Red = MENU_COALITION_COMMAND:New(Coalition, "State Red", menu.SetROE, self._SetALS, self, "Red") - --menu.SetROE_Auto = MENU_COALITION_COMMAND:New(Coalition, "State Auto", menu.SetROE, self._SetALS, self, "Auto") - menu.SetSpeed = MENU_COALITION:New(Coalition, "Set Speed", menu.Skipper) menu.SetSpeed_00 = MENU_COALITION_COMMAND:New(Coalition, "Hold Position", menu.SetSpeed, self._SetSpeed, self, 0) menu.SetSpeed_05 = MENU_COALITION_COMMAND:New(Coalition, "5 knots", menu.SetSpeed, self._SetSpeed, self, 5) @@ -633,79 +624,195 @@ function SKIPPER:_SetMenuCoalition() menu.SetSpeed_30 = MENU_COALITION_COMMAND:New(Coalition, "30 knots", menu.SetSpeed, self._SetSpeed, self, 30) menu.SetSpeed_99 = MENU_COALITION_COMMAND:New(Coalition, "Restore Route", menu.SetSpeed, self.CarrierResume, self) - menu.Defence = MENU_COALITION:New(Coalition, "Defence", menu.Skipper) - menu.Defence_Intruders = MENU_COALITION:New(Coalition, "Intruders", menu.Defence) - menu.Defence_Ammo = MENU_COALITION_COMMAND:New(Coalition, "Report Ammo", menu.Defence, self.arty.GetAmmo, self.arty, true) - menu.Defence_IntruderR = MENU_COALITION_COMMAND:New(Coalition, "Report Intruders", menu.Defence, self._ListIntruders, self) + menu.Recovery=MENU_COALITION:New(Coalition, "Recovery", menu.Skipper) - if self.airboss then - menu.Recovery=MENU_COALITION:New(Coalition, "Recovery", menu.Skipper) - - -- Set wind on deck. - menu.SetWoD = MENU_COALITION:New(Coalition, "Wind on Deck", menu.Recovery) - menu.SetWoD_10 = MENU_COALITION_COMMAND:New(Coalition, "10 knots", menu.SetWoD, self._SetWoD, self, 10) - menu.SetWoD_15 = MENU_COALITION_COMMAND:New(Coalition, "15 knots", menu.SetWoD, self._SetWoD, self, 15) - menu.SetWoD_20 = MENU_COALITION_COMMAND:New(Coalition, "20 knots", menu.SetWoD, self._SetWoD, self, 20) - menu.SetWoD_25 = MENU_COALITION_COMMAND:New(Coalition, "25 knots", menu.SetWoD, self._SetWoD, self, 25) - menu.SetWoD_30 = MENU_COALITION_COMMAND:New(Coalition, "30 knots", menu.SetWoD, self._SetWoD, self, 30) - - -- Set Duration. - menu.SetRtime = MENU_COALITION:New(Coalition, "Duration", menu.Recovery) - menu.SetRtime_15 = MENU_COALITION_COMMAND:New(Coalition, "15 min", menu.SetRtime, self._SetRtime, self, 15) - menu.SetRtime_30 = MENU_COALITION_COMMAND:New(Coalition, "30 min", menu.SetRtime, self._SetRtime, self, 30) - menu.SetRtime_45 = MENU_COALITION_COMMAND:New(Coalition, "45 min", menu.SetRtime, self._SetRtime, self, 45) - menu.SetRtime_60 = MENU_COALITION_COMMAND:New(Coalition, "60 min", menu.SetRtime, self._SetRtime, self, 60) - menu.SetRtime_90 = MENU_COALITION_COMMAND:New(Coalition, "90 min", menu.SetRtime, self._SetRtime, self, 90) - - -- Start/Stop. - menu.SetUturn = MENU_COALITION_COMMAND:New(Coalition, "U-turn On/Off", menu.Recovery, self._SetUturn, self) - menu.CaseI = MENU_COALITION_COMMAND:New(Coalition, "Start CASE I", menu.Recovery, self._StartCaseX, self, 1) - menu.CaseII = MENU_COALITION_COMMAND:New(Coalition, "Start CASE II", menu.Recovery, self._StartCaseX, self, 2) - menu.CaseIII = MENU_COALITION_COMMAND:New(Coalition, "Start CASE III", menu.Recovery, self._StartCaseX, self, 3) - menu.Rstop = MENU_COALITION_COMMAND:New(Coalition, "Stop Recovery", menu.Recovery, self._Rstop, self) + -- Set wind on deck. + menu.SetWoD = MENU_COALITION:New(Coalition, "Wind on Deck", menu.Recovery) + menu.SetWoD_10 = MENU_COALITION_COMMAND:New(Coalition, "10 knots", menu.SetWoD, self._SetWoD, self, 10) + menu.SetWoD_15 = MENU_COALITION_COMMAND:New(Coalition, "15 knots", menu.SetWoD, self._SetWoD, self, 15) + menu.SetWoD_20 = MENU_COALITION_COMMAND:New(Coalition, "20 knots", menu.SetWoD, self._SetWoD, self, 20) + menu.SetWoD_25 = MENU_COALITION_COMMAND:New(Coalition, "25 knots", menu.SetWoD, self._SetWoD, self, 25) + menu.SetWoD_30 = MENU_COALITION_COMMAND:New(Coalition, "30 knots", menu.SetWoD, self._SetWoD, self, 30) + + -- Set Duration. + menu.SetRtime = MENU_COALITION:New(Coalition, "Duration", menu.Recovery) + menu.SetRtime_15 = MENU_COALITION_COMMAND:New(Coalition, "15 min", menu.SetRtime, self._SetRtime, self, 15) + menu.SetRtime_30 = MENU_COALITION_COMMAND:New(Coalition, "30 min", menu.SetRtime, self._SetRtime, self, 30) + menu.SetRtime_45 = MENU_COALITION_COMMAND:New(Coalition, "45 min", menu.SetRtime, self._SetRtime, self, 45) + menu.SetRtime_60 = MENU_COALITION_COMMAND:New(Coalition, "60 min", menu.SetRtime, self._SetRtime, self, 60) + menu.SetRtime_90 = MENU_COALITION_COMMAND:New(Coalition, "90 min", menu.SetRtime, self._SetRtime, self, 90) + + -- Start/Stop. + menu.SetUturn = MENU_COALITION_COMMAND:New(Coalition, "U-turn On/Off", menu.Recovery, self._SetUturn, self) + menu.CaseI = MENU_COALITION_COMMAND:New(Coalition, "Start CASE I", menu.Recovery, self._StartCaseX, self, 1) + menu.CaseII = MENU_COALITION_COMMAND:New(Coalition, "Start CASE II", menu.Recovery, self._StartCaseX, self, 2) + menu.CaseIII = MENU_COALITION_COMMAND:New(Coalition, "Start CASE III", menu.Recovery, self._StartCaseX, self, 3) + menu.Rstop = MENU_COALITION_COMMAND:New(Coalition, "Stop Recovery", menu.Recovery, self._Rstop, self) + + + ------------------ + -- Defence Menu -- + ------------------ + + menu.Defence = MENU_COALITION:New(Coalition, "Defence", menu.Skipper) + menu.Defence_SetROE = MENU_COALITION:New(Coalition, "Set ROE", menu.Defence) + menu.Defence_SetROE_Hold = MENU_COALITION_COMMAND:New(Coalition, "Weapon Hold", menu.Defence_SetROE, self._SetROE, self, "Hold") + menu.Defence_SetROE_Free = MENU_COALITION_COMMAND:New(Coalition, "Weapon Free", menu.Defence_SetROE, self._SetROE, self, "Free") + menu.Defence_SetROE_Return = MENU_COALITION_COMMAND:New(Coalition, "Return Fire", menu.Defence_SetROE, self._SetROE, self, "Return") + + menu.Defence_Intruders = MENU_COALITION:New(Coalition, "Intruders", menu.Defence) + menu.Defence_Ammo = MENU_COALITION_COMMAND:New(Coalition, "Report Ammo", menu.Defence, self.arty.GetAmmo, self.arty, true) + menu.Defence_IntruderR = MENU_COALITION_COMMAND:New(Coalition, "Report Intruders", menu.Defence, self._ListIntruders, self) + menu.Defence_LaunchCAP = MENU_COALITION_COMMAND:New(Coalition, "Lauch CAP", menu.Defence, self.LaunchCAP, self) + + -------------------- + -- Warehouse Menu -- + -------------------- + + if self.warehouse then + menu.Warehouse=MENU_COALITION:New(Coalition, "Warehouse", menu.Skipper) + menu.Warehouse_Assets = MENU_COALITION_COMMAND:New(Coalition, "Reports On/Off", menu.Warehouse, self.WarehouseReportsToggle, self) + menu.Warehouse_Assets = MENU_COALITION_COMMAND:New(Coalition, "Report Assets", menu.Warehouse, self.ReportWarehouseStock, self) end end ---- Intruders. +--- Warehouse reports on/off. -- @param #SKIPPER self --- @param #SKIPPER.Intruder intruder The intruder data. --- @param #number weapontype Type of weapon. -function SKIPPER:LaunchCAP(intruder, weapontype) +function SKIPPER:WarehouseReportsToggle() + self.warehouse.Report=not self.warehouse.Report + MESSAGE:New(string.format("Warehouse reports are now %s", tostring(self.warehouse.Report)), 10, "SKIPPER", true):ToCoalition(self:GetCoalition()) +end - self.warehouse:AddRequest(self.warehouse, WAREHOUSE.Descriptor.ATTRIBUTE, WAREHOUSE.Attribute.AIR_FIGHTER, 2, nil, nil, nil, "CAP") - - local capcoord=self.zoneCCA:GetRandomCoordinate(self.zoneCCA:GetRadius()/2) - - local airboss=self.airboss - function self.warehouse:OnAfterSelfRequest(From, Event, To, Groupset, Request) - local request=Request --Functional.Warehouse#WAREHOUSE.Pendingitem - local groupset=Groupset --Core.Set#SET_GROUP +--- Report warehouse stock. +-- @param #SKIPPER self +function SKIPPER:ReportWarehouseStock() + local text=self.warehouse:_GetStockAssetsText(false) + MESSAGE:New(text, 10, "SKIPPER", true):ToCoalition(self:GetCoalition()) +end + + +--- Launch a Intercept flight. +-- @param #SKIPPER self +-- @param #SKIPPER.Intruder intruder Intruder group. +function SKIPPER:LaunchIntercept(intruder) + + --local n=self.warehouse:GetNumberOfAssets(WAREHOUSE.Descriptor.ASSIGNMENT, WAREHOUSE.Attribute.AIR_FIGHTER, false) + local n=self.warehouse:GetNumberOfAssets(WAREHOUSE.Descriptor.ASSIGNMENT, "Intercept", false) + + if n>0 then + + self.warehouse:AddRequest(self.warehouse, WAREHOUSE.Descriptor.ASSIGNMENT, "Intercept", 1, nil, nil, nil, "Intercept") - for _,_group in pairs(groupset:GetSet()) do - local group=_group --Wrapper.Group#GROUP + local capcoord=intruder.group:GetCoordinate() - if request.assignment=="CAP" then + capcoord:MarkToAll("Intruder coord") + + --[[ + function self.warehouse.OnAfterSelfRequest(warehouse, From, Event, To, Groupset, Request) + local request=Request --Functional.Warehouse#WAREHOUSE.Pendingitem + local groupset=Groupset --Core.Set#SET_GROUP - local capcoord=self.zoneCCA - - local taskOrbit=group:TaskOrbit(capcoord, 100000, 1000, capcoord:Translate(UTILS.NMToMeters(20), 0)) + for _,_group in pairs(groupset:GetSet()) do + local group=_group --Wrapper.Group#GROUP - local wp={} - wp[1]=self:GetCoordinate():WaypointAirTakeOffParking() - wp[2]=self:GetCoordinate():WaypointAirTurningPoint("Baro", 300 , {taskOrbit}, "CAP") + if request.assignment=="Intercept" then - --airboss:SetExcludeAI() + -- Task orbit. + local tasks={} + local taskAttack=group:TaskAttackUnit(intruder.group:GetUnit(1)) + + local speed=group:GetSpeedMax() + local altitude=intruder.group:GetAltitude() + + -- Create waypoints. + local wp={} + wp[1]=self:GetCoordinate():WaypointAirTakeOffParking() + wp[2]=self:GetCoordinate():SetAltitude(altitude):WaypointAirTurningPoint(COORDINATE.WaypointAltType.BARO, speed, {taskAttack}, "Attack Intruder") + + group:StartUncontrolled() + + --airboss:SetExcludeAI() + group:Route(wp) + + end end - + end + ]] + + else + MESSAGE:New("No INTERCEPT fighters currently available", 5, "SKIPPER"):ToCoalition(self:GetCoalition()) + end +end + +--- Handle self requests from carrier warehouse. +-- @param #SKIPPER self +-- @param Functional.Warehouse#WAREHOUSE warehouse The (carrier) warehouse object. +-- @param Core.Set#SET_GROUP groupset Group set. +-- @param Functional.Warehouse#WAREHOUSE.Pendingitem request Request. +function SKIPPER:_SelfRequest(warehouse, groupset, request) + + local assignment=request.assignment + + env.info(string.format("FF warehouse self request assignment %s", tostring(assignment))) + +end + + +--- Launch a CAP flight. +-- @param #SKIPPER self +function SKIPPER:LaunchCAP() + + --local n=self.warehouse:GetNumberOfAssets(WAREHOUSE.Descriptor.ASSIGNMENT, WAREHOUSE.Attribute.AIR_FIGHTER, false) + local n=self.warehouse:GetNumberOfAssets(WAREHOUSE.Descriptor.ASSIGNMENT, "CAP", false) + + if n>0 then + + self.warehouse:AddRequest(self.warehouse, WAREHOUSE.Descriptor.ATTRIBUTE, WAREHOUSE.Attribute.AIR_FIGHTER, 1, nil, nil, nil, "CAP") + + local capcoord=self.zoneCCA:GetRandomCoordinate(self.zoneCCA:GetRadius()/2) + + capcoord:MarkToAll("CAP coord") + + + --[[ + function self.warehouse.OnAfterSelfRequest(warehouse, From, Event, To, Groupset, Request) + local request=Request --Functional.Warehouse#WAREHOUSE.Pendingitem + local groupset=Groupset --Core.Set#SET_GROUP + + for _,_group in pairs(groupset:GetSet()) do + local group=_group --Wrapper.Group#GROUP + + if request.assignment=="CAP" then + + -- Task orbit. + local taskOrbit=group:TaskOrbit(capcoord, UTILS.FeetToMeters(20000), UTILS.KnotsToMps(400), capcoord:Translate(UTILS.NMToMeters(15), 0)) + + local wp={} + wp[1]=self:GetCoordinate():WaypointAirTakeOffParking() + wp[2]=self:GetCoordinate():WaypointAirTurningPoint("Baro", UTILS.KnotsToKph(350), {taskOrbit}, "CAP") + + group:StartUncontrolled() + + --airboss:SetExcludeAI() + group:Route(wp) + + end + + end + end + ]] + + else + MESSAGE:New("No CAP fighters currently available", 5, "SKIPPER"):ToCoalition(self:GetCoalition()) end end ---- Intruders. +--- Enage an intruder by ARTY. -- @param #SKIPPER self -- @param #SKIPPER.Intruder intruder The intruder data. -- @param #number weapontype Type of weapon. @@ -729,6 +836,32 @@ function SKIPPER:EngageArty(intruder, weapontype) end end +--- Sort intruders table. +-- @param #SKIPPER self +function SKIPPER:_SortIntruders() + + -- Sort potential section members wrt to distance to lead. + local function _sort(_a,_b) + local a=_a --#SKIPPER.Intruder + local b=_b --#SKIPPER.Intruder + return a.threadlevel>b.threadlevel + end + + table.sort(self.intruders, _sort) +end + +--- Add sub menu for this intruder. +-- @param #SKIPPER self +-- @param #SKIPPER.Intruder intruder The intruder data. +-- @param #boolean removed If true, an intruder was removed. If false or nil, a new intruder was added. +function SKIPPER:_UpdateIntruderMenu() + + for _,_intruder in ipairs(self.intruders) do + local intruder=_intruder --#SKIPPER.Intruder + self:_AddIntruderMenu(intruder) + end + +end --- Add sub menu for this intruder. -- @param #SKIPPER self @@ -746,6 +879,8 @@ function SKIPPER:_AddIntruderMenu(intruder) MENU_COALITION_COMMAND:New(Coalition, "Engage Cruise M", menu, self.EngageArty, self, intruder, ARTY.WeaponType.CruiseMissile) elseif intruder.category==Group.Category.SHIP then MENU_COALITION_COMMAND:New(Coalition, "Engage", menu, self.EngageArty, self, intruder) + elseif intruder.category==Group.Category.AIRPLANE or intruder.category==Group.Category.HELICOPTER then + MENU_COALITION_COMMAND:New(Coalition, "Engage A2A", menu, self.LaunchIntercept, self, intruder) end -- Set menu. @@ -791,35 +926,34 @@ end -- @param #SKIPPER self -- @param #number case Recovery case (1,2,3). function SKIPPER:_StartCaseX(case) - - if self.airboss then - self.airboss.skipperTime=self.airboss.skipperTime or 30 - self.airboss.skipperSpeed=self.airboss.skipperSpeed or 25 - if self.airboss.skipperUturn==nil then - self.airboss.skipperUturn=false - end - - -- Inform player. - local text=string.format("Case %d recovery will start in 5 min for %d min. Wind on deck %d knots. U-turn=%s.", case, self.airboss.skipperTime, self.airboss.skipperSpeed, tostring(self.airboss.skipperUturn)) - - if self.airboss:IsRecovering() then - text="negative, carrier is already recovering." - MESSAGE:New(string.format(text), 5, self.ClassName):ToCoalition(self:GetCoalition()) - return - end - - -- Recovery staring in 5 min for 30 min. - local t0=timer.getAbsTime()+5*60 - local t9=t0+self.airboss.skipperTime*60 - local C0=UTILS.SecondsToClock(t0) - local C9=UTILS.SecondsToClock(t9) - - -- Carrier will turn into the wind. Wind on deck 25 knots. U-turn on. - self.airboss:AddRecoveryWindow(C0, C9, case, 30, true, self.airboss.skipperSpeed, self.airboss.skipperUturn) - - MESSAGE:New(string.format(text), 5, self.ClassName):ToCoalition(self:GetCoalition()) + --[[ + self.skipperTime=self.skipperTime or 30 + self.skipperSpeed=self.skipperSpeed or 25 + if self.skipperUturn==nil then + self.skipperUturn=false end + ]] + + -- Inform player. + local text=string.format("Case %d recovery will start in 5 min for %d min. Wind on deck %d knots. U-turn=%s.", case, self.skipperTime, self.skipperSpeed, tostring(self.skipperUturn)) + + if self:IsRecovering() then + text="negative, carrier is already recovering." + MESSAGE:New(string.format(text), 5, self.ClassName):ToCoalition(self:GetCoalition()) + return + end + + -- Recovery staring in 5 min for 30 min. + local t0=timer.getAbsTime()+5*60 + local t9=t0+self.skipperTime*60 + local C0=UTILS.SecondsToClock(t0) + local C9=UTILS.SecondsToClock(t9) + + -- Carrier will turn into the wind. Wind on deck 25 knots. U-turn on. + self:AddRecoveryWindow(C0, C9, case, 30, true, self.skipperSpeed, self.skipperUturn) + + MESSAGE:New(string.format(text), 5, self.ClassName):ToCoalition(self:GetCoalition()) end @@ -829,12 +963,9 @@ end -- @param #SKIPPER self function SKIPPER:_SetUturn() - if self.airboss then - self.airboss.skipperUturn=not self.airboss.skipperUturn + self.skipperUturn=not self.skipperUturn - MESSAGE:New(string.format("Recovery U-turn is now %s.", tostring(self.airboss.skipperUturn)), 5, self.ClassName):ToCoalition(self:GetCoalition()) - end - + MESSAGE:New(string.format("Recovery U-turn is now %s.", tostring(self.skipperUturn)), 5, self.ClassName):ToCoalition(self:GetCoalition()) end --- Set manual recovery duration. @@ -842,12 +973,9 @@ end -- @param #number time Duration in minutes. function SKIPPER:_SetRtime(time) - if self.airboss then - self.airboss.skipperTime=time - - MESSAGE:New(string.format("Recovery duration set to %d min.", time), 5, self.ClassName):ToCoalition(self:GetCoalition()) - end - + self.skipperTime=time + + MESSAGE:New(string.format("Recovery duration set to %d min.", time), 5, self.ClassName):ToCoalition(self:GetCoalition()) end @@ -856,12 +984,9 @@ end -- @param #number speed Speed in knots. function SKIPPER:_SetWoD(speed) - if self.airboss then - self.airboss.skipperSpeed=speed - - MESSAGE:New(string.format("Wind on Deck set to %d knots.", speed), 5, self.ClassName):ToCoalition(self:GetCoalition()) - end - + self.skipperSpeed=speed + + MESSAGE:New(string.format("Wind on Deck set to %d knots.", speed), 5, self.ClassName):ToCoalition(self:GetCoalition()) end --- Set new speed for all waypoints.