Advertisement
Guest User

Untitled

a guest
Nov 21st, 2017
201
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 14.78 KB | None | 0 0
  1. ---------------
  2. --- Tankers ---
  3. ---------------
  4. --
  5. -- This script manages the creation and replacement of tankers during the mission.
  6. --
  7. -- /!\ IMPORTANT /!\ This script relies on the MOOSE framework to run
  8. --
  9. --
  10. -- To use this script, create one or more tanker group(s) in your mission,
  11. -- and set them to "Late Activation" on the runway/parking start. Make sure
  12. -- that the tanker group has one waypoing set as "Orbit", and that the group
  13. -- has the "Refueling task".
  14. --
  15. -- You may also want to set the radio frequency on your tankers.
  16. --
  17. -- At the start of the mission, the script will start one tanker of each group,
  18. -- and will monitor its status, spawning a new tanker if the previous one
  19. -- runs out of fuel or is destroyed.
  20. --
  21. --
  22.  
  23. ----------
  24. -- TODO --
  25. ----------
  26. -- + Automatically set radio frequency on every tanker
  27.  
  28. -------------------
  29. -- CONFIGURATION --
  30. -------------------
  31.  
  32. -- Tanker list
  33. -- -----------
  34. -- Comma separated list of groups that are to be managed by the script.
  35. -- note, TACAN are on channel Y
  36. _TANKER_UNITS = {
  37.   {NAME='Tanker_Texaco',   TACAN=5 },
  38.   {NAME='Tanker_Arco1_1',  TACAN=8 },
  39.   {NAME='Tanker_Arco1_2',  TACAN=10},
  40.   {NAME='Tanker_Shell2_1', TACAN=9 },
  41.   {NAME='Tanker_Shell2_2', TACAN=11},
  42.   {NAME='RED TANKER 121',  TACAN=12},
  43. }
  44.  
  45. -- Minimum fuel
  46. ---------------
  47. -- Represents the minimum amount of fuel for a tanker unit to keep patrolling.
  48. -- When the fuel threshold is hit, a new tanker will be spawned at the airbase,
  49. -- while the tanker that is low of fuel maintains its orbit. When the new tanker
  50. -- arrives, the previous one is sent back to base.
  51. --
  52. -- When setting this value, keep in mind that, with the remaining amount of fuel,
  53. -- the tanker will have to:
  54. --      A) Orbit until a replacing tanker arrives
  55. --      B) Get home
  56. --
  57. -- The value is written as a decimal expressing a percentage
  58. -- Ex:
  59. --      0.2 = 20%
  60. _TANKER_MIN_FUEL = 0.2
  61.  
  62. -- Debug
  63. --------
  64. -- If this is set to "true", then additional information about the current
  65. -- tankers' status will be printed in the log file
  66. _TANKER_DEBUG = false
  67.  
  68. -- Reinforcement zone radius
  69. ----------------------------
  70. --
  71. -- This value defines a radius around the "Orbit" point of the tanker.
  72. --
  73. -- When the new tanker arrives within this distance of the intended
  74. -- orbiting waypoint, the out-of-fuel waiting tanker will be sent home.
  75. --
  76. -- The distans is expressed in meters
  77. _TANKER_REINF_RADIUS = 5000
  78.  
  79.  
  80. ---------------------------------
  81. -- DO NOT EDIT BELOW THIS LINE --
  82. ---------------------------------
  83. _TANKER = {}
  84. _TANKER._fsm = {}
  85. _TANKER.INFO = function(text)
  86.     env.info('TANKER: '..text)
  87. end
  88. _TANKER.DEBUG = function(text)
  89.     if _TANKER_DEBUG then
  90.         _TANKER.INFO(text)
  91.    end
  92. end
  93. _TANKER.UNITS = _TANKER_UNITS
  94. _TANKER.MIN_FUEL = _TANKER_MIN_FUEL
  95.  
  96. if _TANKER_DEBUG then
  97.     _TANKER.DEBUG_MENU = MENU_COALITION:New( coalition.side.BLUE, 'Tanker debug')
  98. else
  99.     _TANKER.DEBUG_MENU = nil
  100. end
  101.  
  102. --- Custom Tanker "class"
  103. _TANKER.Tanker = {}
  104. function _TANKER.Tanker:New( group )
  105.  
  106.     -- Copy the group spawned
  107.     local self = group
  108.    
  109.     self.going_home = false
  110.     self.route = self:GetTaskRoute()
  111.    
  112.     function self:Debug( text )
  113.         _TANKER.DEBUG(self:GetName()..': '..text)
  114.     end
  115.    
  116.     if _TANKER.DEBUG_MENU then
  117.         self.debug_menu = MENU_COALITION_COMMAND:New(
  118.             coalition.side.BLUE,
  119.             'Destroy '..self:GetName(),
  120.             _TANKER.DEBUG_MENU,
  121.             self.Destroy,
  122.             self
  123.         )
  124.     end
  125.    
  126.     self:Debug('hullo? Am I a tanker now?')
  127.    
  128.     --- Send the tanker back to its homeplate
  129.     function self:RTB()
  130.    
  131.         if not self.going_home then -- check that the tanker isn't already going home
  132.      
  133.             self:Debug('screw you guys, I\' going home') -- let the world know
  134.        
  135.             -- Send the tanker to its last waypoint
  136.             local command = self:CommandSwitchWayPoint( 2, 1 )
  137.             self:SetCommand( command )
  138.            
  139.             -- Create a 5km radius zone around the home plate
  140.             local last_wp = self.route[1]
  141.             self.rtb_zone = ZONE_RADIUS:New(
  142.                 'rtb_'..self:GetName(),
  143.                 {x=last_wp.x, y=last_wp.y},
  144.                 20000
  145.             )
  146.            
  147.             -- Wait for the tanker to enter the zone; when it's in, remove all tasks, and force it to land
  148.             self.rtb_scheduler = SCHEDULER:New(
  149.                 self,
  150.                 function()
  151.                     self:Debug('daddy, is it far yet ?')
  152.                     if self and self:IsAlive() then
  153.                         if self:IsCompletelyInZone(self.rtb_zone) then
  154.                             self:Debug('no place like home')
  155.                             self:ClearTasks()
  156.                             self:RouteRTB()
  157.                             self.rtb_scheduler:Stop()
  158.                             self:remove_debug_menu()
  159.                         end
  160.                     end
  161.                 end,
  162.                 {}, 10, 10, 0, 0 )
  163.  
  164.             -- Wait for the tanker to stop, and remove it from the game once it has
  165.             self.despawn_scheduler = SCHEDULER:New(self,
  166.                 function()
  167.                     self:Debug('I am so tired...')
  168.                     if self and self:IsAlive() then
  169.                         local velocity = self:GetUnit(1):GetVelocity()
  170.                         local total_speed = math.abs(velocity.x) + math.abs(velocity.y) + math.abs(velocity.z)
  171.                         if total_speed < 3 then -- increased from 1
  172.                             self:Debug('Goodbye, cruel world !')
  173.                             self:Destroy()
  174.                         end
  175.                     end
  176.                 end,
  177.                 {}, 10, 10, 0, 0)
  178.  
  179.             self.going_home = true
  180.         end
  181.     end
  182.    
  183.     --- Create a zone around the first waypoint found with an "Orbit" task
  184.     function self:ZoneFromOrbitWaypoint()
  185.        
  186.         -- "x" & "y" are the waypoint's location
  187.         local x
  188.         local y
  189.        
  190.         -- Iterate over all waypoints
  191.         for _, wp_ in ipairs(self.route) do
  192.        
  193.             -- Iterate over the tasks
  194.             for _, task_ in ipairs(wp_['task']['params']['tasks']) do
  195.            
  196.                 -- Waypoint found;
  197.                 if task_['id'] == 'Orbit' then
  198.                    
  199.                     -- Save position
  200.                     x = wp_['x']
  201.                     y = wp_['y']
  202.                    
  203.                     -- Break the loop
  204.                     break
  205.                    
  206.                 -- Manages special cases
  207.                 elseif task_['id'] == 'ControlledTask' then          
  208.                     if task_['params']['task']['id'] == 'Orbit' then
  209.                         x = wp_['x']
  210.                         y = wp_['y']
  211.                         break
  212.                     end          
  213.                 end
  214.             end
  215.        
  216.             -- If waypoint found, break the loop, no need to iterate over the rest
  217.             if not x == nil then break end
  218.         end
  219.      
  220.         -- If the waypoint has been found, create a 5k radius zone around it and return it
  221.         if x then
  222.             self:Debug('creating ')
  223.             return ZONE_RADIUS:New(self:GetName(), {x=x, y=y}, _TANKER_REINF_RADIUS)
  224.         end
  225.  
  226.     end
  227.    
  228.     --- Returns the fuel onboard, as a percentage
  229.     function self:GetFuel()
  230.         local fuel_left = self:GetUnit(1):GetFuel()
  231.         self:Debug('I got '..fuel_left..' fuel left.')
  232.         return fuel_left
  233.     end
  234.    
  235.     --- Return the Tanker "instance"
  236.     return self
  237. end
  238.  
  239.  
  240. --- Declare a Finite State Machine "class" to manage the tankers
  241. _TANKER.FSM = {
  242.     previous_tankers = {}
  243. }
  244.  
  245.  
  246. function _TANKER.FSM:Debug( text )
  247.     _TANKER.DEBUG('FSM: '..self.template_name..': '..text)
  248. end
  249.  
  250. function _TANKER.FSM:New( template )
  251.    
  252.     -- Inherit from MOOSE's FSM
  253.     local self = BASE:Inherit( self, FSM:New() )
  254.    
  255.     -- Template name is the name of the group in the ME to copy from
  256.     self.template_name = template.NAME
  257.     self.tacan_channel = template.TACAN
  258.    
  259.     self:Debug('FSM created')
  260.    
  261.     -- Declare the possible transitions
  262.     FSM.SetStartState(self, 'INIT')
  263.     FSM.AddTransition(self,'INIT','Ready','NO_TANKER')
  264.     FSM.AddTransition(self, '*', 'Failure', 'INIT')
  265.     FSM.AddTransition(self, '*', 'Destroyed', 'NO_TANKER')
  266.     FSM.AddTransition(self, 'NO_TANKER', 'Spawned', 'EN_ROUTE')
  267.     FSM.AddTransition(self, 'EN_ROUTE', 'Arrived', 'ORBIT')
  268.     FSM.AddTransition(self, 'ORBIT', 'WaitRTB', 'EN_ROUTE')
  269.    
  270.     -- Log errors on special event "Failure"
  271.     function self:OnBeforeFailure(from, to, event, text)
  272.         self:Debug('ERROR: '..text)
  273.     end
  274.    
  275.     -- Spawn a tanker group
  276.     function self:SpawnNewTanker()    
  277.         self.group = _TANKER.Tanker:New(self.spawner:Spawn())
  278.    
  279.         -- Schedules the creation of the TACAN 10 seconds later, so the unit has time to appear
  280.         self.tacan_scheduler = SCHEDULER:New(
  281.           nil,
  282.           function( fsm )
  283.             if fsm.beacon ~= nil then
  284.               fsm.beacon:StopAATACAN()
  285.               fsm.group:Debug('stopping previous TACAN on channel: '..fsm.tacan_channel..'Y')
  286.             end
  287.             local unit = fsm.group:GetUnit(1)
  288.             fsm.beacon = unit:GetBeacon()
  289.             fsm.beacon:AATACAN(fsm.tacan_channel, fsm.template_name, true)
  290.             fsm.group:Debug('starting TACAN on channel: '..fsm.tacan_channel..'Y')
  291.           end, { self }, 10
  292.         )
  293.     end
  294.    
  295.     -- Triggered when the FSM is ready to work
  296.     function self:OnLeaveINIT(from, event, to)
  297.         self:Debug('initializing FSM')
  298.         self:Debug('creating spawner')
  299.         self.spawner = SPAWN:New(self.template_name)
  300.     end
  301.    
  302.     -- Triggered when there is no *active* tanker
  303.     function self:OnEnterNO_TANKER(from, event, to)
  304.         self:Debug('no tanker available; spawning new tanker')
  305.        
  306.         self:SpawnNewTanker()
  307.        
  308.         -- If we come from "INIT" (the first ever state)
  309.         if from == 'INIT' then
  310.        
  311.             -- Create a zone around the first "Orbit" waypoint
  312.             self:Debug('registering zone')
  313.             local zone = self.group:ZoneFromOrbitWaypoint()
  314.            
  315.             -- If we didn't find an "Orbit" waypoint, fail miserably
  316.             if zone == nil then
  317.                 self:Failure('no waypoint with task "Orbit" found for template: '..self.template_name)
  318.             end
  319.            
  320.             -- Otherwise, we're golden
  321.             self.zone = zone
  322.        
  323.         end
  324.        
  325.         -- Let the FSM know we have a valid tanker
  326.         self:Debug('registering new group: '..self.group:GetName())
  327.         self:Spawned()
  328.      
  329.     end
  330.    
  331.     -- Triggered when there's a tanker making its way to the refueling track
  332.     function self:OnEnterEN_ROUTE(from, event, to)
  333.    
  334.         -- Triggered by a tanker almost out of fuel
  335.         if event == 'WaitRTB' then
  336.             self:Debug('a new tanker is on its way')
  337.         end
  338.      
  339.         -- Periodic check for tanker position
  340.         self:Debug('monitoring tanker progress to Orbit waypoint')
  341.         self.monitor_arriving_tanker = SCHEDULER:New(
  342.             nil,
  343.             function(fsm, event)
  344.            
  345.                 -- Check that it's still alive
  346.                 if not fsm.group:IsAlive() then
  347.                     fsm:Debug('transiting tanker has been destroyed')
  348.                    
  349.                     -- Kill the periodic check
  350.                     fsm.monitor_arriving_tanker:Stop()
  351.                    
  352.                     -- Let the FSM know that someone derped
  353.                     fsm:Destroyed()
  354.                
  355.                 -- When the tanker arrives at the orbit waypoint
  356.                 elseif fsm.group:IsCompletelyInZone(fsm.zone) then
  357.                
  358.                     fsm:Debug('tanker has arrived')
  359.                    
  360.                     -- Kill the periodic check
  361.                     fsm.monitor_arriving_tanker:Stop()
  362.                    
  363.                     -- If an WaitRTB event triggered the state
  364.                     if event == 'WaitRTB' then            
  365.                    
  366.                         self:Debug('sending previous tanker home')
  367.                         for _, tanker in ipairs(fsm.previous_tankers) do
  368.                             if tanker and tanker:IsAlive() then
  369.                                 tanker:RTB()
  370.                             end
  371.                         end
  372.                     end
  373.            
  374.                     -- Let the FSM know that we now have a tanker on station
  375.                     fsm:Arrived()
  376.                 end
  377.             end,
  378.             {self, event}, 10, 10, 0, 0
  379.         )    
  380.     end
  381.    
  382.     -- "Normal" state of the FSM; there's a tanker orbiting
  383.     function self:OnEnterORBIT(from, event, to)
  384.         self:Debug('tanker is orbiting at waypoint')
  385.        
  386.         -- Periodic check
  387.         self.monitor_orbiting_tanker = SCHEDULER:New(
  388.         nil,
  389.         function()
  390.            
  391.             -- Is the tanker dead ?
  392.             if not self.group:IsAlive() then
  393.                 self:Debug('orbiting tanker has been destroyed')
  394.                
  395.                 -- kill the check
  396.                 self.monitor_orbiting_tanker:Stop()
  397.                
  398.                 -- remove the debug menu
  399.                 self.group:remove_debug_menu()
  400.                
  401.                 -- let the FSM know
  402.                 self:Destroyed()
  403.            
  404.             -- Is the tanker out of fuel ?
  405.             elseif self.group:GetFuel() <= _TANKER_MIN_FUEL then
  406.                 self:Debug('tanker will soon be out of fuel, spawning a new one')
  407.                
  408.                 -- Register the tanker for later RTB
  409.                 table.insert (self.previous_tankers, self.group)
  410.                
  411.                 -- Kill the check
  412.                 self.monitor_orbiting_tanker:Stop()
  413.                
  414.                 -- Start a new tanker on the apron
  415.                 self:SpawnNewTanker()
  416.                
  417.                 -- Switch to the waiting state
  418.                 self:WaitRTB()
  419.             end
  420.         end,
  421.         {}, 10, 10, 0, 0)    
  422.     end
  423.    
  424.     return self
  425. end
  426.  
  427. -- Start the tanker module
  428. do
  429.  
  430.     _TANKER.INFO('TANKER: INIT: START')
  431.    
  432.     -- Iterate over all the tanker templates
  433.     for _, unit in ipairs( _TANKER_UNITS ) do
  434.    
  435.         _TANKER.DEBUG('INIT: initializing tanker unit: '..unit.NAME)
  436.        
  437.         -- Create the FSM
  438.         local fsm = _TANKER.FSM:New(unit)
  439.        
  440.         -- Add it for sanity's sake
  441.         _TANKER._fsm[unit.NAME] = fsm
  442.        
  443.         -- And start it
  444.         fsm:Ready()
  445.     end
  446.        
  447.     _TANKER.INFO('TANKER: INIT: DONE')
  448.  
  449. end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement