Advertisement
6dwavenminer

FTD Smart Lua Missile System V2

Aug 15th, 2017
2,234
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 39.74 KB | None | 0 0
  1. --FTD Smart Missile Guidance System
  2. --Author: 6dwavenminer
  3. --Version: 2.1.2
  4.  
  5.  
  6. --User Config Variables
  7.  
  8. --Launcher vehicle variables
  9. MainFrameID = 0; --Which MainFrame to connect to
  10. UseWeaponsSlots = false;
  11. --If 'true' will use all weapons control blocks using the weapons slot of the variable 'WeaponID'
  12. --If 'false' will use the weapons control block with the ID of the variable 'WeaponID'
  13. WeaponID = 0;
  14. MaxDistanceBetweenLuaTranscieverAndMissileControl = 10; --The maximum distance between a lua transciever and its corrisponding missile control block.
  15.  
  16. --Missile variables
  17. MissileInterceptor = false; --Set true if using being used for missile interception
  18. TargetDetonationRange = -10; --If the distance between target and missile is below this value, blow up.
  19.  
  20. --Target acquisition variables
  21. MultiTarget = true;
  22. MaxRange = 4000; --Targets above this range from the missile launcher will be ignored.
  23. MaxTargetHeight = 10000; --Targets above this height will be ignored.
  24. MinTargetHeight = -20; --Targets below this range will be ignored.
  25.  
  26. --Safe detonation variables
  27. SafeDetonteAltitude = 500; --If there are no target, goto and detonte at this altitude.
  28. DetonateMissileUponEmptyFuel = true; --If the missile has run out of fuel, detonate?
  29.  
  30. --Missile boost variables
  31. UseFuelDefinedThrustControl = true;
  32. --If true: Uses the predicted time to intercept and the amount of remaining fuel to calculate thrust value. More adaptable to different ranges.
  33. --If false: Uses the 'BaseMissileThrust' added to the target speed multiplied by 'MissileThrustTargetSpeedMultiplier' to calculate thrust.
  34. --In both: If target is below 'BoostInterceptionTime' then thrust is multiplied by 'BoostThrustMultiplier'
  35. BaseMissileThrust = 300; --Base thrust for missile.
  36. MissileThrustTargetSpeedMultiplier = 10.0; --Value is multiplied by target's speed then added to base thrust of missile, useful for fast targets. Not used is UseFuelDefinedThrustControl = true.
  37. BoostInterceptionTime = 6.0;    --When the intercept time falls below this value,'BoostThrust' is added to missile thrust, useful for Thumpers.
  38. BoostThrustMultiplier = 1.2; --Value multiplied to missile thrust once within boost range.
  39.  
  40. --Check angle to intercept variables
  41. InterceptionTurnAngleTollerance = 5; --Added tollerance when calculating whether to steer away from target as interception is deemed currently impossible due to too steep an angle.
  42.  
  43. --Cruising variables
  44. BaseDirectEngagementGroundRange = 240; --Below this ground range to target, head directly to target.
  45. DirectEngagementGroundRangeSpeedMultipler = 0.6; --Added onto the engagment range after it has been multiplied by the target speed, used for fast targets.
  46. CruisingAltitude = 250; --While en route travel at this altitude.
  47. CruisingAltitudeAboveTerrain = 30; --If terrain nears cruising altitude, travel this heigh above the terrain.
  48. MaxAllowedCruisingAltitude = 10000; --Missile will try and aviod going above this altitude
  49. MinAllowedCruisingAltitude = 10; --Missile will try and aviod going below this altitude
  50.  
  51. --SeaDive variables
  52. GroundRangeToTargetToSeaDive = 200;--Below this ground range to target, start diving underwater for explosives buff.
  53. MaxTargetHeightForSeaDive = 5; --Max height of enemy aimpoint above the water to use seadiving on.
  54. AdditionalSeaDiveDepth = 5; --Additional depth added to enemy aimpoint when seadriving.
  55.  
  56. --Anti ally collsion variables
  57. AntiAlliedCollisionPredictionTimeLimit = 8; --Used to prevent missile-allied vessel collisions, number is seconds into the future predicted.
  58. AntiAlliedCollisionPredictionTimeStep = 1; --The time step in seconds used to prevent missile-allied vessel collisions.
  59. AntiAlliedCollisionAdditionalSafetyRange = 5; --Additional range to border of allies to avoid.
  60.  
  61. --End of User Config Variables
  62.  
  63. --Global Variables
  64. MissileLauncherPosList = {};
  65. PreviousTimeForTargets = 0;
  66. PreviousTimeForMissiles = 0;
  67.  
  68. UseableLuaTransceiverList = {};
  69.  
  70. UpdateThrust = true;
  71.  
  72. NumOfMissiles = 0;
  73. Missiles = {};
  74. PreviousTimeForMissiles = 0;
  75.  
  76. NumOfTargets = 0;
  77. Targets = {};
  78.  
  79. function GetUseableLuaTranscivers(I)
  80.     MissileLauncherPosList = {};
  81.     for MissileLauncherID, MissileLauncherPos in pairs(MissileLauncherPosList) do
  82.         MissileLauncherPos[MissileLauncherID] = nil;
  83.     end
  84.     if UseWeaponsSlots == true then
  85.         for i = 0, I:GetWeaponCount()-1 do
  86.             local WeaponInfo = I:GetWeaponInfo(i);
  87.             if WeaponInfo.WeaponSlot == WeaponID then
  88.                 MissileLauncherPosList[i] = WeaponInfo.GlobalPosition;
  89.             end
  90.         end
  91.     else
  92.         local WeaponInfo = I:GetWeaponInfo(WeaponID);
  93.         MissileLauncherPosList[0] = WeaponInfo.GlobalPosition;
  94.     end
  95.  
  96.     local t;
  97.     local ValidTransceiverList = {};
  98.     local UseableLuaTransceiverCount = 0;
  99.     for t = 0, I:GetLuaTransceiverCount()-1 do
  100.         local MissileLauncher2LuaTransceiverDistance = 64000;
  101.         for MissileLauncherID, MissileLauncherPos in pairs(MissileLauncherPosList) do
  102.             local MissileLauncher2LuaTransceiverDistanceBuffer = Vector3.Distance(MissileLauncherPos,I:GetLuaTransceiverInfo(t).Position);
  103.             if MissileLauncher2LuaTransceiverDistance > MissileLauncher2LuaTransceiverDistanceBuffer then
  104.                 MissileLauncher2LuaTransceiverDistance = MissileLauncher2LuaTransceiverDistanceBuffer;
  105.             end
  106.         end
  107.         if MissileLauncher2LuaTransceiverDistance < MaxDistanceBetweenLuaTranscieverAndMissileControl then
  108.             UseableLuaTransceiverList[UseableLuaTransceiverCount] = t;
  109.             ValidTransceiverList[UseableLuaTransceiverCount] = true;
  110.             UseableLuaTransceiverCount = UseableLuaTransceiverCount + 1;
  111.         end
  112.     end
  113.     for TransceiverID, Transceiver in pairs(UseableLuaTransceiverList) do
  114.         if (ValidTransceiverList[TransceiverID] ~= true)then
  115.             UseableLuaTransceiverList[TransceiverID] = nil;
  116.         end
  117.         ValidTransceiverList[TransceiverID] = false;
  118.     end
  119.     ValidTransceiverList = nil;
  120. end
  121.  
  122. --Check that the WeaponID corrisponds to a missile launcher
  123. function WeaponsCheck(I)
  124.     if UseWeaponsSlots == false then
  125.         if I:GetWeaponInfo(WeaponID).Valid == false then
  126.             I:LogToHud("Invalid WeaponID, change WeaponID in LUA code");
  127.         else
  128.             if I:GetWeaponInfo(WeaponID).WeaponType == 4 then --Turret check
  129.                 if not I:GetWeaponInfoOnTurretOrSpinner(WeaponID, 0).WeaponType == 5 then --Weapons check, is it a missile launcher?
  130.                     I:LogToHud("Weapon is not missile launcher, change WeaponID in LUA code, note this weapon is on a turret or spinner,further debugging maybe required");
  131.                 else
  132.                     GetUseableLuaTranscivers(I);
  133.                 end
  134.             else
  135.                 if not I:GetWeaponInfo(WeaponID).WeaponType == 5 then --Weapons check, is it a missile launcher?
  136.                     I:LogToHud("Weapon is not missile launcher, change WeaponID in LUA code");
  137.                 else
  138.                     GetUseableLuaTranscivers(I);
  139.                 end
  140.             end
  141.         end
  142.     else
  143.         if WeaponID > 5 then
  144.             I:LogToHud("WeaponsSlots only go upto 5, either change the variable 'WeaponID' or change 'UseWeaponsSlots' to false ");
  145.         else
  146.             GetUseableLuaTranscivers(I);
  147.         end
  148.     end
  149. end
  150.  
  151. --Creates a list of all targets within range
  152. function GenerateTargetList(I)
  153.     local TargetPos;
  154.     local n;
  155.     local ValidTargetList = {};
  156.     NumOfTargets = 0;
  157.     for n=0,I:GetNumberOfTargets(MainFrameID)-1 do
  158.         TargetPos = I:GetTargetInfo(MainFrameID,n).AimPointPosition;
  159.        
  160.         local TargetRange = 64000;
  161.         for MissileLauncherID, MissileLauncherPos in pairs(MissileLauncherPosList) do
  162.             local TargetRangeBuffer = Vector3.Distance(MissileLauncherPos,TargetPos);
  163.             if TargetRange > TargetRangeBuffer then
  164.                 TargetRange = TargetRangeBuffer;
  165.             end
  166.         end
  167.        
  168.         if (TargetRange < MaxRange) and (TargetPos.y < MaxTargetHeight) and (TargetPos.y > MinTargetHeight) then
  169.             local TargetInfo = I:GetTargetInfo(MainFrameID,n);
  170.             if TargetInfo.Valid == true then
  171.                 local TargetID = TargetInfo.Id;    
  172.                 --If we've not seen the target before, collect data on it
  173.                 if Targets[TargetID] == nil then
  174.                     Targets[TargetID] = {};
  175.                     Targets[TargetID].Index = n;
  176.                     Targets[TargetID].Velocity = TargetInfo.Velocity;
  177.                     Targets[TargetID].Acceleration = Vector3(0,0,0);
  178.                 end
  179.                 --Update target values
  180.                 Targets[TargetID].Score = TargetInfo.Score;
  181.                 Targets[TargetID].Position = TargetInfo.AimPointPosition;
  182.                 Targets[TargetID].PreviousVelocity = Targets[TargetID].Velocity;
  183.                 Targets[TargetID].Velocity = TargetInfo.Velocity;
  184.                 Targets[TargetID].Speed = TargetInfo.Velocity.magnitude;
  185.                 ValidTargetList[TargetID] = true;  
  186.            
  187.                 local TimeDifference = I:GetTime() - PreviousTimeForTargets;
  188.                 if TimeDifference > 0.1 then
  189.                     Targets[TargetID].Acceleration = (Targets[TargetID].Velocity - Targets[TargetID].PreviousVelocity) / TimeDifference;
  190.                     PreviousTimeForTargets = I:GetTime();
  191.                 end
  192.            
  193.                 NumOfTargets = NumOfTargets + 1;
  194.             end
  195.         end
  196.     end
  197.     --Cycle through all known missiles to remove the dead ones
  198.     for TargetID, Target in pairs(Targets) do
  199.         if (ValidTargetList[TargetID] ~= true)then
  200.             Targets[TargetID] = nil;
  201.         end
  202.     end
  203.     ValidTargetList = nil;
  204. end
  205.  
  206.  
  207. --Creates a list of all targets within range
  208. function GenerateTargetListMissileInterceptor(I)
  209.     local TargetPos;
  210.     local n;
  211.     local ValidTargetList = {};
  212.     NumOfTargets = 0;
  213.     for n=0,I:GetNumberOfWarnings(MainFrameID)-1 do
  214.         TargetPos = I:GetMissileWarning(MainFrameID,n).Position;
  215.        
  216.         local TargetRange = 64000;
  217.         for MissileLauncherID, MissileLauncherPos in pairs(MissileLauncherPosList) do
  218.             local TargetRangeBuffer = Vector3.Distance(MissileLauncherPos,TargetPos);
  219.             if TargetRange > TargetRangeBuffer then
  220.                 TargetRange = TargetRangeBuffer;
  221.             end
  222.         end
  223.        
  224.         TargetRange = Vector3.Distance(I:GetWeaponInfo(WeaponID).GlobalPosition,TargetPos);
  225.         if (TargetRange < MaxRange) and (TargetPos.y < MaxTargetHeight) and (TargetPos.y > MinTargetHeight) then
  226.             local TargetInfo = I:GetMissileWarning(MainFrameID,n);
  227.             if TargetInfo.Valid == true then
  228.                 local TargetID = TargetInfo.Id;    
  229.                 --If we've not seen the target before, collect data on it
  230.                 if Targets[TargetID] == nil then
  231.                     Targets[TargetID] = {};
  232.                     Targets[TargetID].Index = n;
  233.                     Targets[TargetID].Velocity = TargetInfo.Velocity;
  234.                     Targets[TargetID].Acceleration = Vector3(0,0,0);
  235.                 end
  236.                 --Update target values
  237.                 Targets[TargetID].Position = TargetInfo.Position;
  238.                 Targets[TargetID].PreviousVelocity = Targets[TargetID].Velocity;
  239.                 Targets[TargetID].Velocity = TargetInfo.Velocity;
  240.                 Targets[TargetID].Speed = TargetInfo.Velocity.magnitude;
  241.                
  242.                
  243.                 local VectorToInterceptionAimPos = I:GetConstructPosition() - Targets[TargetID].Position;
  244.                 local TargetAngleToInterceptionAimPos;
  245.                 if (VectorToInterceptionAimPos.magnitude ~= 0) and  (Targets[TargetID].Speed ~= 0) then
  246.                     TargetAngleToInterceptionAimPos = math.deg(math.acos(Vector3.Dot(VectorToInterceptionAimPos,  Targets[TargetID].Velocity) / (VectorToInterceptionAimPos.magnitude *  Targets[TargetID].Speed)));
  247.                 else
  248.                     TargetAngleToInterceptionAimPos = math.deg(math.acos(Vector3.Dot(VectorToInterceptionAimPos,  Targets[TargetID].Velocity) / 0.001));
  249.                 end
  250.                
  251.                 Targets[TargetID].Score = (100 / (Vector3.Distance(TargetInfo.Position, I:GetConstructPosition()) + 1)) * (1 - (TargetAngleToInterceptionAimPos / 180.1)); --Missiles closer to construct, and that are also pointing towards it, get a higher score
  252.                 ValidTargetList[TargetID] = true;  
  253.            
  254.                 local TimeDifference = I:GetTime() - PreviousTimeForTargets;
  255.                 if TimeDifference > 0.5 then
  256.                     Targets[TargetID].Acceleration = (Targets[TargetID].Velocity - Targets[TargetID].PreviousVelocity) / TimeDifference;
  257.                     PreviousTimeForTargets = I:GetTime();
  258.                 end
  259.            
  260.                 NumOfTargets = NumOfTargets + 1;
  261.             end
  262.         end
  263.     end
  264.     --Cycle through all known missiles to remove the dead ones
  265.     for TargetID, Target in pairs(Targets) do
  266.         if (ValidTargetList[TargetID] ~= true)then
  267.             Targets[TargetID] = nil;
  268.         end
  269.     end
  270.     ValidTargetList = nil;
  271. end
  272.  
  273.  
  274.  
  275. --Creates a list of all useable missiles
  276. function GenerateMissileList(I)
  277.     NumOfMissiles = 0;
  278.     local ValidMissileList = {};
  279.     local i;
  280.     local m;
  281.     for TransceiverID, Transceiver in pairs( UseableLuaTransceiverList) do
  282.         for m=0, I:GetLuaControlledMissileCount(Transceiver)-1 do
  283.             local MissileInfo = I:GetLuaControlledMissileInfo(Transceiver,m);
  284.             if MissileInfo.Valid == true then
  285.                 local MissileID = MissileInfo.Id;
  286.            
  287.                 --If we've not seen the missile before, collect data on it
  288.                 if Missiles[MissileID] == nil then     
  289.                     Missiles[MissileID] = {};
  290.                     Missiles[MissileID].TransceiverIndex = Transceiver;
  291.                     Missiles[MissileID].MissileIndex = m;
  292.                     Missiles[MissileID].Velocity = MissileInfo.Velocity;
  293.                     Missiles[MissileID].AimPoint = Vector3(0, 0, 0);
  294.                     Missiles[MissileID].InterceptionTime = 0;
  295.                
  296.                     PreviousTimeForMissiles = I:GetTime();
  297.                     Missiles[MissileID].Acceleration = Vector3(0,0,0);
  298.                
  299.                     Missiles[MissileID].NumOfThrusters = 0;
  300.                     Missiles[MissileID].NumOfTorpedoPropellers = 0;
  301.                     Missiles[MissileID].NumOfShortRangeThrusters = 0;
  302.                     Missiles[MissileID].NumOfFins = 0;
  303.                     Missiles[MissileID].NumOfRegulators = 0;
  304.                     Missiles[MissileID].Fuel = 0;
  305.                     Missiles[MissileID].AngleToInterceptionAimPos = 0;
  306.                     Missiles[MissileID].MaxTurnSpeed = 0;
  307.                     Missiles[MissileID].MinimumTurnRadius = 1;
  308.                     Missiles[MissileID].FlightTimeLeft = 60;
  309.                     Missiles[MissileID].MissileLength = 0;
  310.                     local MissileExtraInfo = I:GetMissileInfo(Transceiver, m);--Lagfest
  311.                     if (MissileExtraInfo ~= nil) then
  312.                         --Find out about the variable thrusters
  313.                         local ThrusterIndexes = {};
  314.                         for j,part in pairs(MissileExtraInfo.Parts) do
  315.                             if (string.find(part.Name, 'variable')) then
  316.                                 table.insert(ThrusterIndexes, j);
  317.                                 Missiles[MissileID].NumOfThrusters = Missiles[MissileID].NumOfThrusters + 1;
  318.                             end
  319.                             if (string.find(part.Name, 'short')) then
  320.                                 Missiles[MissileID].NumOfShortRangeThrusters = Missiles[MissileID].NumOfShortRangeThrusters + 1;
  321.                             end
  322.                             if (string.find(part.Name, 'propeller')) then
  323.                                 Missiles[MissileID].NumOfTorpedoPropellers = Missiles[MissileID].NumOfTorpedoPropellers + 1;
  324.                             end
  325.                             if (string.find(part.Name, 'fuel')) then
  326.                                 Missiles[MissileID].Fuel = Missiles[MissileID].Fuel + 5000;
  327.                             end
  328.                             if (string.find(part.Name, 'fins')) then
  329.                                 Missiles[MissileID].NumOfFins = Missiles[MissileID].NumOfFins + 1;
  330.                             end
  331.                             if (string.find(part.Name, 'regulator')) then
  332.                                 Missiles[MissileID].NumOfRegulators = Missiles[MissileID].NumOfRegulators + 1;
  333.                             end
  334.                             if (string.find(part.Name, 'thumper')) then
  335.                                 Missiles[MissileID].MissileLength = Missiles[MissileID].MissileLength + 1;
  336.                             end
  337.                             Missiles[MissileID].MissileLength = Missiles[MissileID].MissileLength + 0.5;
  338.                         end
  339.                         Missiles[MissileID].ThrusterIndexes = ThrusterIndexes;
  340.                         ThrusterIndexes = nil;
  341.                     end
  342.                     Missiles[MissileID].Thrust = BaseMissileThrust;
  343.                 end
  344.            
  345.                 --Update missile values
  346.                 Missiles[MissileID].TimeSinceLaunch = MissileInfo.TimeSinceLaunch;
  347.                 Missiles[MissileID].TimeLeftTillDespawn = 60 + (Missiles[MissileID].NumOfRegulators * 180) - Missiles[MissileID].TimeSinceLaunch; --Time left before despawn
  348.                 Missiles[MissileID].Position = MissileInfo.Position;
  349.                 Missiles[MissileID].PreviousVelocity = Missiles[MissileID].Velocity;
  350.                 Missiles[MissileID].Velocity = MissileInfo.Velocity;
  351.                 Missiles[MissileID].Speed = MissileInfo.Velocity.magnitude;
  352.                 local TimeDifference = I:GetTime() - PreviousTimeForMissiles;
  353.                 if TimeDifference > 0.3 then
  354.                     UpdateThrust = true;
  355.                     Missiles[MissileID].Acceleration = (Missiles[MissileID].Velocity - Missiles[MissileID].PreviousVelocity) / TimeDifference;
  356.                    
  357.                     local VariableThrusterFuelUse = 0;
  358.                     local ShortRangeThrusterFuelUse = 0;
  359.                     local TorpedoPropellerFuelUse = 0;
  360.                     if Missiles[MissileID].NumOfThrusters ~= 0 then
  361.                         VariableThrusterFuelUse = (Missiles[MissileID].NumOfThrusters * Missiles[MissileID].Thrust);
  362.                     end
  363.                     if Missiles[MissileID].NumOfShortRangeThrusters ~= 0 then
  364.                         ShortRangeThrusterFuelUse = (Missiles[MissileID].NumOfShortRangeThrusters * 1000);
  365.                     end
  366.                     if Missiles[MissileID].NumOfTorpedoPropellers ~= 0 then
  367.                         TorpedoPropellerFuelUse = (Missiles[MissileID].NumOfTorpedoPropellers * 37.5);
  368.                     end
  369.                        
  370.                     if Missiles[MissileID].Fuel > 0 then
  371.                         if Missiles[MissileID].Position.y > 0 then --Above water
  372.                             Missiles[MissileID].Fuel = Missiles[MissileID].Fuel - (TimeDifference * (VariableThrusterFuelUse + ShortRangeThrusterFuelUse));
  373.                         else    --Below the water
  374.                             Missiles[MissileID].Fuel = Missiles[MissileID].Fuel - (TimeDifference * TorpedoPropellerFuelUse);
  375.                         end
  376.                     else
  377.                         Missiles[MissileID].Fuel = 0;
  378.                     end
  379.                     Missiles[MissileID].MaxTurnSpeed = 10 * math.sqrt(Missiles[MissileID].Speed) * (Missiles[MissileID].NumOfFins / Missiles[MissileID].MissileLength);
  380.                     Missiles[MissileID].MinimumTurnRadius = 5.73 * math.sqrt(Missiles[MissileID].Speed) * (Missiles[MissileID].MissileLength / Missiles[MissileID].NumOfFins);
  381.                     if Missiles[MissileID].Position.y > 0 then --Above water
  382.                         if (Missiles[MissileID].NumOfThrusters ~= 0) or (Missiles[MissileID].NumOfShortRangeThrusters ~= 0)then
  383.                             Missiles[MissileID].FlightTimeLeft = Missiles[MissileID].Fuel / (VariableThrusterFuelUse + ShortRangeThrusterFuelUse);
  384.                         end
  385.                     else    --Below the water
  386.                         if Missiles[MissileID].NumOfTorpedoPropellers ~= 0 then
  387.                             Missiles[MissileID].FlightTimeLeft = Missiles[MissileID].Fuel / (Missiles[MissileID].NumOfTorpedoPropellers * 37.5);
  388.                         end
  389.                     end
  390.                     if Missiles[MissileID].FlightTimeLeft > Missiles[MissileID].TimeLeftTillDespawn then
  391.                         Missiles[MissileID].FlightTimeLeft = Missiles[MissileID].TimeLeftTillDespawn;
  392.                     end
  393.                     PreviousTimeForMissiles = I:GetTime();
  394.                 else
  395.                     UpdateThrust = false;
  396.                 end
  397.            
  398.                 ValidMissileList[MissileID] = true;
  399.                 NumOfMissiles = NumOfMissiles + 1;
  400.             end
  401.         end
  402.         IncrementalListOfTargetIDs = nil;
  403.     end
  404.     --Cycle through all known missiles to remove the dead ones
  405.     for MissileID, Missile in pairs(Missiles) do
  406.         if (ValidMissileList[MissileID] ~= true)then
  407.             Missiles[MissileID] = nil;
  408.         end
  409.     end
  410.     ValidMissileList = nil;
  411. end
  412.  
  413.  
  414. --If theres no targets go up to safe distance to detonte the missle
  415. function NoTargetSafelyDetonte(I)
  416.     for MissileID, Missile in pairs(Missiles) do
  417.         if I:GetLuaControlledMissileInfo(Missile.TransceiverIndex, Missile.MissileIndex).Valid == true then --Double check the missile is still live
  418.             Missile.AimPoint = Vector3(Missile.Position.x, SafeDetonteAltitude, Missile.Position.z);
  419.             if ((Missile.Position.y > SafeDetonteAltitude) and (SafeDetonteAltitude > 0)) or ((Missile.Position.y < SafeDetonteAltitude) and (SafeDetonteAltitude <= 0)) then
  420.                 I:DetonateLuaControlledMissile(Missile.TransceiverIndex,Missile.MissileIndex);
  421.             end
  422.         end
  423.     end
  424. end
  425.  
  426.  
  427. --Calculate how many to missiles each target
  428. function CalculateMissilesPerTarget(I,EnemyFleetTotalScore,ScoreOffset)
  429.     for ID, Target in pairs(Targets) do
  430.         if NumOfTargets == 1 then
  431.             Target.MissilesPerTarget = NumOfMissiles;
  432.         else
  433.             if EnemyFleetTotalScore ~= 0 then
  434.                 Target.MissilesPerTarget = ((Target.Score + ScoreOffset) / EnemyFleetTotalScore) * NumOfMissiles;
  435.             else
  436.                 Target.MissilesPerTarget = 1;
  437.             end
  438.             Target.MissilesPerTarget = math.floor(Target.MissilesPerTarget + 0.5);
  439.         end
  440.     end
  441. end
  442.  
  443.  
  444. function CalculateEnemyFleetScore(I)
  445.     local EnemyFleetTotalScore = 0;
  446.     local LowestScore = 100000;
  447.     local HighestScore = -100000;
  448.     local ScoreOffset = 0;
  449.  
  450.     --Find highest and lowest enemy scores
  451.     for ID, Target in pairs(Targets) do
  452.         if (Target.Score) < LowestScore then
  453.             LowestScore = Target.Score;
  454.         end
  455.         if (Target.Score) > HighestScore then
  456.             HighestScore = Target.Score;
  457.         end
  458.     end
  459.  
  460.     --Calculate score offset
  461.     ScoreOffset = (HighestScore / 2) - LowestScore;
  462.     --Calculate enemy fleet score
  463.     for ID, Target in pairs(Targets) do
  464.         EnemyFleetTotalScore = EnemyFleetTotalScore + ScoreOffset + Target.Score;
  465.     end
  466.     CalculateMissilesPerTarget(I,EnemyFleetTotalScore,ScoreOffset);
  467. end
  468.  
  469.  
  470. function AssignTargetForMissile(I)
  471. --Generate a incremental list of target IDs
  472.     local IncrementalListOfTargetIDs = {};
  473.     local IncrementalIndex = 0;
  474.     local MaxScore = -65536
  475.     for ID, Target in pairs(Targets) do
  476.         if Target ~= nil then   --Check we have a target
  477.             if MultiTarget == true then
  478.                 IncrementalListOfTargetIDs[IncrementalIndex] = ID;
  479.                 IncrementalIndex = IncrementalIndex + 1;
  480.             else
  481.                 --Target enemy with highest score
  482.                 if Target.Score > MaxScore then
  483.                     IncrementalListOfTargetIDs[IncrementalIndex] = ID;
  484.                     MaxScore = Target.Score
  485.                 end
  486.             end
  487.         end
  488.     end
  489.    
  490.  
  491.     --Cycle through every missile and assign it a target
  492.     local MissileNum = 0;
  493.     local TargetNum = 0;
  494.     for MissileID, Missile in pairs(Missiles) do
  495.         if I:GetLuaControlledMissileInfo(Missile.TransceiverIndex, Missile.MissileIndex).Valid == true then --Double check the missile is still live
  496.            
  497.             if MultiTarget == true then
  498.                 MissileNum = MissileNum + 1;
  499.                 if MissileNum > Targets[IncrementalListOfTargetIDs[TargetNum]].MissilesPerTarget then
  500.                     if TargetNum < NumOfTargets - 1 then
  501.                         TargetNum = TargetNum + 1;
  502.                         MissileNum = 0;
  503.                     end
  504.                 end
  505.                 Missile.TargetID = IncrementalListOfTargetIDs[TargetNum];
  506.             else
  507.                 --Only target highest scoring enemy
  508.                 Missile.TargetID = IncrementalListOfTargetIDs[TargetNum];
  509.             end
  510.            
  511.         end
  512.     end
  513.     IncrementalListOfTargetIDs = nil;
  514. end
  515.  
  516.  
  517. --Calculate distance ignoring vertical component
  518. function CalculateGroundDistanceToTarget(I, MissileID)
  519.     local GroundDistanceToTarget = 1000;
  520.     if Missiles[MissileID] ~= nil then
  521.         if I:GetLuaControlledMissileInfo(Missiles[MissileID].TransceiverIndex, Missiles[MissileID].MissileIndex).Valid == true then --Double check the missile is still live
  522.             if Targets[Missiles[MissileID].TargetID] ~= nil then
  523.                 local TwoDMissilePos = Missiles[MissileID].Position * 1; --Multiply by one to only copy the data, not the reference!
  524.                 local TwoDTargetPos = Targets[Missiles[MissileID].TargetID].Position * 1; --Multiply by one to only copy the data, not the reference!
  525.                 TwoDMissilePos.y = 0;
  526.                 TwoDTargetPos.y = 0;
  527.                 GroundDistanceToTarget = Vector3.Distance(TwoDMissilePos,TwoDTargetPos);
  528.             end
  529.         end
  530.     end
  531.     return GroundDistanceToTarget;
  532. end
  533.  
  534. function PredictInterceptPosition(I)
  535.     for MissileID, Missile in pairs(Missiles) do
  536.         if I:GetLuaControlledMissileInfo(Missile.TransceiverIndex, Missile.MissileIndex).Valid == true then --Double check the missile is still live
  537.             if Targets[Missile.TargetID] ~= nil then    --Check we have a target
  538.                 local TargetPosition = Targets[Missile.TargetID].Position;
  539.                 local TargetVelocity = Targets[Missile.TargetID].Velocity;
  540.                 local TargetSpeed = Vector3.Magnitude(TargetVelocity);
  541.                 local TargetAcceleration = Targets[Missile.TargetID].Acceleration;
  542.            
  543.                 local MissilePosition = Missile.Position;
  544.                 local MissileSpeed = Missile.Speed;
  545.                 local MissileAcceleration = Missile.Acceleration;
  546.            
  547.                 local SummedSpeedAcceleration = Vector3.Magnitude(MissileAcceleration + TargetAcceleration);
  548.                 local SummedSpeed = MissileSpeed + TargetSpeed;
  549.                 local IntecpetionRange;
  550.                 local InterceptionTime;
  551.                 local InterceptionAimPos = TargetPosition;
  552.                
  553.                 for i=0, 12 do
  554.                     IntecpetionRange = Vector3.Distance(MissilePosition, InterceptionAimPos);
  555.                     if SummedSpeedAcceleration ~= 0 then
  556.                         InterceptionTime = (-SummedSpeed + math.sqrt(math.abs(math.pow(SummedSpeed, 2) + (2 * SummedSpeedAcceleration * IntecpetionRange)))) / SummedSpeedAcceleration;
  557.                     elseif SummedSpeed ~= 0 then
  558.                         InterceptionTime = IntecpetionRange / SummedSpeed;
  559.                     else
  560.                         InterceptionTime = Missile.FlightTimeLeft;
  561.                     end
  562.                     --Sanity check InterceptionTime
  563.                     if InterceptionTime > Missile.FlightTimeLeft then
  564.                         InterceptionTime = Missile.FlightTimeLeft;
  565.                     end
  566.                     InterceptionAimPos = TargetPosition + (TargetVelocity * InterceptionTime) + (TargetAcceleration * 0.5 * math.pow(InterceptionTime,2));
  567.                 end
  568.                
  569.                 Missile.InterceptionTime = InterceptionTime;
  570.                 Missile.AimPoint = Vector3(InterceptionAimPos.x,InterceptionAimPos.y,InterceptionAimPos.z);
  571.             end
  572.         end
  573.     end
  574. end
  575.  
  576. --Check if the missile can actually aim at the correct point fast enough
  577. function CheckAngleToIntercept(I)
  578.     for MissileID, Missile in pairs(Missiles) do
  579.         if I:GetLuaControlledMissileInfo(Missile.TransceiverIndex, Missile.MissileIndex).Valid == true then --Double check the missile is still live
  580.             if Targets[Missile.TargetID] ~= nil then    --Check we have a target
  581.                 local VectorToInterceptionAimPos = Missile.AimPoint - Missile.Position;--Targets[Missile.TargetID].Position --Missile.AimPoint
  582.                 if (VectorToInterceptionAimPos.magnitude ~= 0) and  (Missile.Speed ~= 0) then
  583.                     Missile.AngleToInterceptionAimPos = math.deg(math.acos(Vector3.Dot(VectorToInterceptionAimPos,  Missile.Velocity) / (VectorToInterceptionAimPos.magnitude *  Missile.Speed)));
  584.                 else
  585.                     Missile.AngleToInterceptionAimPos = math.deg(math.acos(Vector3.Dot(VectorToInterceptionAimPos,  Missile.Velocity) / 0.001));
  586.                 end
  587.                 if ((Missile.AngleToInterceptionAimPos > ((Missile.MaxTurnSpeed * Missile.InterceptionTime) + InterceptionTurnAngleTollerance)) and (Vector3.Distance(Missile.Position, Targets[Missile.TargetID].Position) > 10)) then
  588.                     --Missile can't turn in time, change trajectory
  589.                     Missile.AimPoint = Targets[Missile.TargetID].Position + (Missile.Velocity * 4); --Purposly aim the missile behind the target so it can try for another pass
  590.                     Missile.AimPoint.y = CruisingAltitude;
  591.                 end
  592.             end
  593.         end
  594.     end
  595. end
  596.  
  597.  
  598. --To allow quick change in height, use a closer aimpoint than target, but still in the same direction.
  599. function TargetCloserThanTarget(I, MissileID, GroundDistanceToTarget)
  600.     if I:GetLuaControlledMissileInfo(Missiles[MissileID].TransceiverIndex, Missiles[MissileID].MissileIndex).Valid == true then --Double check the missile is still live
  601.         if Targets[Missiles[MissileID].TargetID] ~= nil then
  602.             if GroundDistanceToTarget ~= 0 then
  603.                 local CoordinateMultiplier = (Vector3.Magnitude(Missiles[MissileID].Velocity) * 2) / GroundDistanceToTarget;
  604.                 local Missle_TargetDifferentialPosX =  Missiles[MissileID].Position.x - Missiles[MissileID].AimPoint.x;
  605.                 local Missle_TargetDifferentialPosZ =  Missiles[MissileID].Position.z - Missiles[MissileID].AimPoint.z;
  606.                 Missiles[MissileID].AimPoint.x = Missiles[MissileID].Position.x - (Missle_TargetDifferentialPosX * CoordinateMultiplier);
  607.                 Missiles[MissileID].AimPoint.z = Missiles[MissileID].Position.z - (Missle_TargetDifferentialPosZ * CoordinateMultiplier);
  608.             end
  609.         end
  610.     end
  611. end
  612.  
  613. --En route go to attlitude till near target
  614. function CruiseTowardsTarget(I)
  615.     for MissileID, Missile in pairs(Missiles) do
  616.         if Missile ~= nil then
  617.             if I:GetLuaControlledMissileInfo(Missile.TransceiverIndex, Missile.MissileIndex).Valid == true then --Double check the missile is still live
  618.                 if Targets[Missile.TargetID] ~= nil then
  619.                     local GroundDistanceToTarget = CalculateGroundDistanceToTarget(I, MissileID);
  620.                
  621.                     local MissileFuturePosition = Missile.Position + (Missile.Velocity * 2);
  622.                     local MissileFutureTerrainHeight = I:GetTerrainAltitudeForPosition(MissileFuturePosition);
  623.                     --Ground avoidance till near target
  624.                     if (GroundDistanceToTarget > 50 + (2 * Vector3.Magnitude(Targets[Missile.TargetID].Velocity))) and (MissileFutureTerrainHeight + CruisingAltitudeAboveTerrain > MissileFuturePosition.y) then
  625.                         Missile.AimPoint.y = MissileFutureTerrainHeight + CruisingAltitudeAboveTerrain;
  626.                         TargetCloserThanTarget(I, MissileID, GroundDistanceToTarget);
  627.                
  628.                     --Goto crusing height till near target
  629.                     elseif GroundDistanceToTarget > BaseDirectEngagementGroundRange + (DirectEngagementGroundRangeSpeedMultipler * Vector3.Magnitude(Targets[Missile.TargetID].Velocity)) then
  630.                         Missile.AimPoint.y = CruisingAltitude;
  631.                         TargetCloserThanTarget(I, MissileID, GroundDistanceToTarget);
  632.                     end    
  633.                
  634.                 end
  635.             end
  636.         end
  637.     end
  638. end
  639.  
  640.  
  641.  
  642. --If target is near the surface of the sea, dive to get explosive buff
  643. function MissileSeaDive(I)
  644.     for MissileID, Missile in pairs(Missiles) do
  645.         if I:GetLuaControlledMissileInfo(Missile.TransceiverIndex, Missile.MissileIndex).Valid == true then --Double check the missile is still live
  646.             if Targets[Missiles[MissileID].TargetID] ~= nil then
  647.                 local GroundDistanceToTarget = CalculateGroundDistanceToTarget(I, MissileID);
  648.                 if (GroundDistanceToTarget < GroundRangeToTargetToSeaDive) and (Targets[Missiles[MissileID].TargetID].Position.y < MaxTargetHeightForSeaDive) then
  649.                     Missiles[MissileID].AimPoint.y = Targets[Missiles[MissileID].TargetID].Position.y - AdditionalSeaDiveDepth;
  650.                 end
  651.             end
  652.         end
  653.     end
  654. end
  655.  
  656.  
  657. --Checks missiles curret appoximate trajectory for any possible collisions with predicted allied postions and attempts to aviod them
  658. function AllyCollisionAviodance(I)
  659.     for MissileID, Missile in pairs(Missiles) do
  660.         if I:GetLuaControlledMissileInfo(Missile.TransceiverIndex, Missile.MissileIndex).Valid == true then --Double check the missile is still live
  661.             local NumOfFriendlies = I:GetFriendlyCount();
  662.             if NumOfFriendlies > 0 then
  663.                 --Find all possible allied collsions
  664.                 local n;
  665.                 local t;
  666.                 local MissileTimeToReachAimPos;
  667.                 local MissileFuturePos;
  668.                 local FutureAllyReferencePosition;
  669.                 local FutureAllyReferencePositionPositiveSize;
  670.                 local FutureAllyReferencePositionNegativeSize;
  671.                 local PositiveAviodCoOrdinates;
  672.                 local NegativeAviodCoOrdinates;
  673.                 local MissileFutureCollisionPos;
  674.                 local PositiveEvadeDistance;
  675.                 local NegativeEvadeDistance;
  676.                 local NearestTimeCollsion = AntiAlliedCollisionPredictionTimeLimit;
  677.                 local NearestTimeCollsionAlliedIndex = nil;
  678.        
  679.                 local FriendlyInfo;
  680.                 for n=0, NumOfFriendlies-1 do
  681.                     FriendlyInfo = I:GetFriendlyInfo(n);
  682.                     if FriendlyInfo.Valid == true then
  683.                         if Missile.Velocity.magnitude ~= 0 then
  684.                             MissileTimeToReachAimPos = Vector3.Distance(Missile.Position, Missile.AimPoint) / Missile.Velocity.magnitude;
  685.                         else
  686.                             MissileTimeToReachAimPos = Vector3.Distance(Missile.Position, Missile.AimPoint) / 0.001;
  687.                         end
  688.                         for t=0, AntiAlliedCollisionPredictionTimeLimit-1, AntiAlliedCollisionPredictionTimeStep do
  689.                             if MissileTimeToReachAimPos > t then --Don't care is the ally is further away than the enemy target
  690.                                 if t ~= 0 then
  691.                                     MissileFuturePos = Missile.Position + ((Missile.AimPoint - Missile.Position) / t);
  692.                                 else
  693.                                     MissileFuturePos = Missile.Position + ((Missile.AimPoint - Missile.Position) / 0.001);
  694.                                 end
  695.                                 FutureAllyReferencePosition = FriendlyInfo.ReferencePosition + (FriendlyInfo.Velocity * t);
  696.                                 FutureAllyReferencePositionPositiveSize = FutureAllyReferencePosition + FriendlyInfo.PositiveSize + Vector3(AntiAlliedCollisionAdditionalSafetyRange,AntiAlliedCollisionAdditionalSafetyRange,AntiAlliedCollisionAdditionalSafetyRange);
  697.                                 FutureAllyReferencePositionNegativeSize = FutureAllyReferencePosition + FriendlyInfo.NegativeSize - Vector3(AntiAlliedCollisionAdditionalSafetyRange,AntiAlliedCollisionAdditionalSafetyRange,AntiAlliedCollisionAdditionalSafetyRange);
  698.                                 if  (MissileFuturePos.x <= FutureAllyReferencePositionPositiveSize.x)
  699.                                 and (MissileFuturePos.x >= FutureAllyReferencePositionNegativeSize.x)
  700.                                 and (MissileFuturePos.y <= FutureAllyReferencePositionPositiveSize.y)
  701.                                 and (MissileFuturePos.y >= FutureAllyReferencePositionNegativeSize.y)
  702.                                 and (MissileFuturePos.z <= FutureAllyReferencePositionPositiveSize.z)
  703.                                 and (MissileFuturePos.z >= FutureAllyReferencePositionNegativeSize.z)
  704.                                 then
  705.                                     --Will probably collide with ally, record time and friendly Index
  706.                                     if t < NearestTimeCollsion then
  707.                                         NearestTimeCollsion = t;
  708.                                         NearestTimeCollsionAlliedIndex = FriendlyInfo.Id;
  709.                                     end
  710.                                     break   --No use checking if it will collide later with the same target...
  711.                                 end
  712.                             end
  713.                         end
  714.                     end
  715.                 end
  716.  
  717.                 --Calculate where the missile can't go
  718.                 PositiveAviodCoOrdinates = Vector3(-100000,100000,-100000);
  719.                 NegativeAviodCoOrdinates = Vector3(100000,100000,100000);
  720.                 if NearestTimeCollsionAlliedIndex ~= nil then
  721.                     FriendlyInfo = I:GetFriendlyInfo(NearestTimeCollsionAlliedIndex);
  722.    
  723.                     FutureAllyReferencePosition = FriendlyInfo.ReferencePosition + (FriendlyInfo.Velocity * NearestTimeCollsion);
  724.                     FutureAllyReferencePositionPositiveSize = FutureAllyReferencePosition + FriendlyInfo.PositiveSize + Vector3(AntiAlliedCollisionAdditionalSafetyRange, AntiAlliedCollisionAdditionalSafetyRange, AntiAlliedCollisionAdditionalSafetyRange);
  725.                     FutureAllyReferencePositionNegativeSize = FutureAllyReferencePosition + FriendlyInfo.NegativeSize - Vector3(AntiAlliedCollisionAdditionalSafetyRange, AntiAlliedCollisionAdditionalSafetyRange, AntiAlliedCollisionAdditionalSafetyRange);
  726.    
  727.                     if (PositiveAviodCoOrdinates.x <= FutureAllyReferencePositionPositiveSize.x) then
  728.                         PositiveAviodCoOrdinates.x = FutureAllyReferencePositionPositiveSize.x;
  729.                     end
  730.                     if (NegativeAviodCoOrdinates.x >= FutureAllyReferencePositionPositiveSize.x) then
  731.                         NegativeAviodCoOrdinates.x = FutureAllyReferencePositionNegativeSize.x;
  732.                     end
  733.                     if (PositiveAviodCoOrdinates.y <= FutureAllyReferencePositionPositiveSize.y) then
  734.                         PositiveAviodCoOrdinates.y = FutureAllyReferencePositionPositiveSize.y;
  735.                     end
  736.                     if (NegativeAviodCoOrdinates.y >= FutureAllyReferencePositionPositiveSize.y) then
  737.                         NegativeAviodCoOrdinates.y = FutureAllyReferencePositionNegativeSize.y;
  738.                     end
  739.                     if (PositiveAviodCoOrdinates.z <= FutureAllyReferencePositionPositiveSize.z) then
  740.                         PositiveAviodCoOrdinates.z = FutureAllyReferencePositionPositiveSize.z;
  741.                     end
  742.                     if (NegativeAviodCoOrdinates.z >= FutureAllyReferencePositionPositiveSize.z) then
  743.                         NegativeAviodCoOrdinates.z = FutureAllyReferencePositionNegativeSize.z;
  744.                     end
  745.                 end
  746.  
  747.                 --Find the nearest non-collsion point while trying to maintain route to target
  748.                 if NearestTimeCollsionAlliedIndex ~= nil then
  749.                     if NearestTimeCollsion ~= 0 then
  750.                         MissileFutureCollisionPos = Missile.Position + ((Missile.AimPoint - Missile.Position) / NearestTimeCollsion);
  751.                     else
  752.                         MissileFutureCollisionPos = Missile.Position + ((Missile.AimPoint - Missile.Position) / 0.0001);
  753.                     end
  754.                     PositiveEvadeDistance = Vector3.Magnitude(MissileFutureCollisionPos - PositiveAviodCoOrdinates);
  755.                     NegativeEvadeDistance = Vector3.Magnitude(MissileFutureCollisionPos - NegativeAviodCoOrdinates);
  756.                     --Now we try and steer the missile away from the possible collisions
  757.                     if PositiveEvadeDistance < NegativeEvadeDistance  then
  758.                         Missile.AimPoint = PositiveAviodCoOrdinates;
  759.                         if PositiveEvadeDistance.y > MaxAllowedCruisingAltitude then
  760.                             Missile.AimPoint.y = MaxAllowedCruisingAltitude;
  761.                         end
  762.                     else
  763.                         Missile.AimPoint = NegativeAviodCoOrdinates;
  764.                         if NegativeAviodCoOrdinates.y < MinAllowedCruisingAltitude then
  765.                             Missile.AimPoint.y = MinAllowedCruisingAltitude;
  766.                         end
  767.                     end
  768.                 end
  769.             end
  770.         end
  771.     end
  772.  
  773. end
  774.  
  775. --Finally command the missiles to aim at thier aimpoints
  776. function AimMissiles(I)
  777.     for MissileID, Missile in pairs(Missiles) do
  778.         if I:IsLuaControlledMissileAnInterceptor(Missile.TransceiverIndex, Missile.MissileIndex) then
  779.             I:SetLuaControlledMissileInterceptorStandardGuidanceOnOff(Missile.TransceiverIndex, Missile.MissileIndex, false);
  780.         end
  781.         if I:GetLuaControlledMissileInfo(Missile.TransceiverIndex, Missile.MissileIndex).Valid == true then --Double check the missile is still live
  782.             I:SetLuaControlledMissileAimPoint(Missile.TransceiverIndex, Missile.MissileIndex, Missile.AimPoint.x, Missile.AimPoint.y, Missile.AimPoint.z);
  783.         end
  784.     end
  785. end
  786.  
  787.  
  788. function ControlMissileThrust(I)
  789.     for MissileID, Missile in pairs(Missiles) do
  790.         if I:GetLuaControlledMissileInfo(Missile.TransceiverIndex, Missile.MissileIndex).Valid == true then --Double check the missile is still live
  791.             if UseFuelDefinedThrustControl == true then
  792.                 if Missile.InterceptionTime ~= 0 then
  793.                     Missile.Thrust = BaseMissileThrust * (Missile.FlightTimeLeft / Missile.InterceptionTime) * (1 - (Missile.AngleToInterceptionAimPos / 180.1));
  794.                 else
  795.                     Missile.Thrust = BaseMissileThrust * (Missile.FlightTimeLeft / 0.001) * (1 - (Missile.AngleToInterceptionAimPos / 180.1));
  796.                 end
  797.             else
  798.                 local TargetSpeed = 0;
  799.                 if Targets[Missile.TargetID] ~= nil then    --Check we have a target
  800.                     TargetSpeed = Vector3.Magnitude(Targets[Missile.TargetID].Velocity);
  801.                 else
  802.                     TargetSpeed = 0;
  803.                 end
  804.                    
  805.                 local VectorToInterceptionAimPos = Missile.AimPoint - Missile.Position
  806.                 local AngleToInterceptionAimPos;
  807.                 if (VectorToInterceptionAimPos.magnitude ~= 0) and  (Missile.Velocity.magnitude ~= 0) then
  808.                     AngleToInterceptionAimPos = math.deg(math.acos(Vector3.Dot(VectorToInterceptionAimPos,  Missile.Velocity) / (VectorToInterceptionAimPos.magnitude *  Missile.Velocity.magnitude)));
  809.                 else
  810.                     AngleToInterceptionAimPos = math.deg(math.acos(Vector3.Dot(VectorToInterceptionAimPos,  Missile.Velocity) / 0.001));
  811.                 end
  812.                 Missile.Thrust =  MissileThrustTargetSpeedMultiplier * TargetSpeed * (1 - (AngleToInterceptionAimPos / 180.1)) + BaseMissileThrust;
  813.             end
  814.  
  815.             if Missile.InterceptionTime < BoostInterceptionTime then
  816.                 Missile.Thrust = Missile.Thrust * BoostThrustMultiplier;
  817.             end
  818.            
  819.             if Missile.Thrust > 10000 then  --Clamp result
  820.                 Missile.Thrust = 10000;
  821.             end
  822.            
  823.         end
  824.     end
  825. end
  826.  
  827.  
  828. --Changes the missile thrust every time the fuel amount is updated
  829. function SetMissileThrust(I)
  830.     if UpdateThrust == true then
  831.         ControlMissileThrust(I);
  832.         for MissileID, Missile in pairs(Missiles) do
  833.             if I:GetLuaControlledMissileInfo(Missile.TransceiverIndex, Missile.MissileIndex).Valid == true then --Double check the missile is still live
  834.                 local MissileExtraInfo = I:GetMissileInfo(Missile.TransceiverIndex, Missile.MissileIndex);--Lagfest
  835.                 if (Missile.NumOfThrusters > 0) and (MissileExtraInfo ~= nil) then
  836.                     for i,Index in pairs(Missile.ThrusterIndexes) do
  837.                         if (MissileExtraInfo.Parts[Index] ~= nil) and (Missile.Thrust ~= nil) then
  838.                             MissileExtraInfo.Parts[Index]:SendRegister(2, Missile.Thrust);
  839.                         end
  840.                     end
  841.                 end
  842.                 MissileExtraInfo = nil;
  843.             end
  844.         end
  845.     end
  846. end
  847.  
  848.  
  849. function ProximityDetonation(I)
  850.     for MissileID, Missile in pairs(Missiles) do
  851.         if I:GetLuaControlledMissileInfo(Missile.TransceiverIndex, Missile.MissileIndex).Valid == true then --Double check the missile is still live
  852.             if Targets[Missile.TargetID] ~= nil then
  853.                     if Vector3.Distance(Missile.Position, Targets[Missile.TargetID].Position) < TargetDetonationRange then
  854.                     I:DetonateLuaControlledMissile(Missile.TransceiverIndex, Missile.MissileIndex);
  855.                 end
  856.             end
  857.         end
  858.     end
  859. end
  860.  
  861. function EmptyFuelDetonation(I)
  862.     for MissileID, Missile in pairs(Missiles) do
  863.         if I:GetLuaControlledMissileInfo(Missile.TransceiverIndex, Missile.MissileIndex).Valid == true then --Double check the missile is still live
  864.             if (Missile.Fuel <= 0) and (DetonateMissileUponEmptyFuel == true) and (Missile.TimeSinceLaunch > 1.0) then
  865.                 I:DetonateLuaControlledMissile(Missile.TransceiverIndex, Missile.MissileIndex);
  866.             end
  867.         end
  868.     end
  869. end
  870.  
  871.  
  872.  
  873. --Main
  874. function Update(I)
  875.     --Startup
  876.     WeaponsCheck(I);
  877.     if MissileInterceptor == true then
  878.         GenerateTargetListMissileInterceptor(I);
  879.     else
  880.         GenerateTargetList(I);
  881.     end
  882.     GenerateMissileList(I);
  883.     --End Startup
  884.     if NumOfTargets <=0 then
  885.         NoTargetSafelyDetonte(I);  
  886.     else
  887.         CalculateEnemyFleetScore(I); --Also calculates how many missiles are to be fired at each target
  888.         AssignTargetForMissile(I);
  889.    
  890.         --Guide missiles towards thier targets
  891.         PredictInterceptPosition(I);
  892.         CheckAngleToIntercept(I);
  893.         CruiseTowardsTarget(I);
  894.         MissileSeaDive(I);
  895.     end
  896.     AllyCollisionAviodance(I); --Check to make sure we don't blow up any allies
  897.     AimMissiles(I); --Now aim at target
  898.     SetMissileThrust(I);
  899.     ProximityDetonation(I);
  900.     EmptyFuelDetonation(I);
  901.    
  902. end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement