Advertisement
Antigluon

Dive bomber mod to Madwand's AI

Dec 13th, 2015
127
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 30.26 KB | None | 0 0
  1. --[[
  2. Advanced aerial AI, version 4.21
  3. Created by Madwand 10/24/2015
  4. Use and modify this code however you like, however please credit me
  5. if you use this AI or a derivative of it in a tournament, or you publish a blueprint
  6. using it. Also let me know if you make any significant improvements,
  7. so I can add them back into the code.
  8. Dive bomber mod by TauNeutrino - change DiveAlt to the altitude you want to go to when dropping bombs.
  9.  
  10. Documentation on the options is available at http://pastebin.com/36eFGAzV
  11. --]]
  12.  
  13. -- BASIC OPTIONS
  14. AngleBeforeTurn = 10
  15. AngleBeforeRoll = 30
  16. AttackRunDistance = 600
  17. AbortRunDistance = 100
  18. ClosingDistance = 1000
  19. ForceAttackTime = 15
  20. CruiseAltitude = 250
  21. MaxElevationAngle = 30
  22. CruiseSpeed = 5
  23. AttackRunSpeed = 5
  24. EscapeSpeed = 5
  25. RollingSpeed = 5
  26. ClosingSpeed = 5
  27. OrbitSpawn = true
  28. DiveAltitude = 50
  29.  
  30. -- AIRSHIP/HELICOPTER OPTIONS
  31. UseAltitudeJets = false
  32. UseSpinners = false
  33. MinHelicopterBladeSpeed = 10
  34. MaxHelicopterBladeSpeed = 30
  35. UseVTOLBeneathSpeed = 0
  36. VTOLEngines = nil
  37.  
  38. -- TERRAIN AVOIDANCE OPTIONS
  39. TerrainAvoidanceStrategy = 1
  40. MinAltitude = 100
  41. MaxAltitude = 400
  42. TerrainLookahead = {0,1,2,4}
  43. MaxTerrainSpeed = 5
  44.  
  45. -- WATER START OPTIONS
  46. DeployAlt = 5
  47. ReleaseAlt = 15
  48. EnableEnginesAlt = 5
  49.  
  50. -- COLLISION AVOIDANCE OPTIONS
  51. CollisionTThreshold = 4
  52. CollisionDetectionHeight = 30
  53. CollisionAngle = 40
  54.  
  55. -- FLOCKING OPTIONS
  56. NeighborRadius = 0
  57. IgnoreBelowSpeed = 0
  58. FlockWithBlueprintNames = 'all'
  59. AlignmentWeight = 1
  60. CohesionWeight = 1
  61. SeparationWeight = 1.5
  62. InjuredWeight = 0
  63. LongRangeWeight = .5
  64. TerrainAvoidanceWeight = 0
  65. TargetWeight = 1
  66.  
  67. -- "DOGFIGHTING" OPTIONS
  68. MatchTargetAltitude = false
  69. MatchAltitudeRange = 600
  70. MatchAltitudeOffset = 0
  71. MinMatchingAltitude = 100
  72.  
  73. -- VECTOR THRUST OPTIONS
  74. VTSpinners = nil
  75. MaxVTAngle = {30,30,30,90} -- yaw, roll, pitch, VTOL
  76. VTProportional = {1,1,1}
  77. VTDelta = {.1,.1,0}
  78. VTOLSpinners = 'all'
  79.  
  80. -- MISSILE AVOIDANCE OPTIONS
  81. WarningMainframe = -1
  82. RunAwayTTT = 2
  83. RunAngle = 180
  84. DodgeTTT = 1
  85. DodgingRollTolerance = 60
  86. DangerRadius = 20
  87. NormalSpeed = 100
  88. CraftRadius = 10
  89. DodgingSpeed = 5
  90.  
  91. -- ADVANCED OPTIONS
  92. MODE = 2
  93. AltitudeClamp = .2
  94. PitchDamping = 90
  95. YawDamping = 90
  96. RollDamping = 45
  97. MainDriveControlType = 0
  98. MinimumSpeed = 0
  99. MaximumSpeed = 999
  100. MaxRollAngle = 120
  101. RollTolerance = 30
  102. DebugMode = false
  103. UsePreferredSide = false
  104. UsePredictiveGuidance = false
  105. AltitudeOffset = 0
  106. AngleOfEscape = AngleBeforeTurn
  107. ExcludeSpinners = nil
  108.  
  109. --Static variables. Do not change these.
  110. DRIVELEFT = 0
  111. DRIVERIGHT = 1
  112. ROLLLEFT = 2
  113. ROLLRIGHT = 3
  114. DRIVEUP = 4
  115. DRIVEDOWN = 5
  116. DRIVEMAIN = 8
  117. DriveTypeDesc = {'Left','Right','RLeft','RRight','PUp','PDown','','','Main'}
  118.  
  119. CruiseAltOld = CruiseAltitude
  120. firstpass = true
  121. state = "cruise"
  122. onattackrun = false
  123. WaterStarted = false
  124. NextAttackTime = 0
  125. OverTerrain = false
  126. PitchCorrection = 0
  127. RollSpinners = {}
  128. PitchSpinners = {}
  129. YawSpinners = {}
  130. DownSpinners = {}
  131. AllSpinners = {}
  132. ExcludedSpinners = {}
  133. HeliSpinners = {}
  134. RollEngines = {}
  135. PitchEngines = {}
  136. EngineDF = {}
  137. SpinnerStartRotations={}
  138. DYaw = 0
  139. NumSpinners = 0
  140. NumEngines = 0
  141.  
  142. --Try to pitch (or yaw, if necessary) to a given angle of elevation
  143. function AdjustElevationToAngle(I,DesiredElevation)
  144.   local PitchSpeedCheck = math.abs(DesiredElevation-Pitch)/PitchDamping
  145.   local YawSpeedCheck = math.abs(DesiredElevation-Pitch)/YawDamping
  146.   if (Pitch > DesiredElevation) then
  147.     if (math.abs(Roll)>135) then
  148.       Control(I,DRIVEUP,PitchSpeedCheck,DPitch)
  149.     elseif (Roll<-45) then
  150.       Control(I,DRIVERIGHT,YawSpeedCheck,DYaw)
  151.     elseif (Roll>45) then
  152.       Control(I,DRIVELEFT,YawSpeedCheck,-DYaw)
  153.     elseif (math.abs(Roll)<45 and state~="rolling") then
  154.       Control(I,DRIVEDOWN,PitchSpeedCheck,-DPitch)
  155.     end
  156.   elseif (Pitch < DesiredElevation) then
  157.     if (math.abs(Roll)>135 and state~="rolling") then
  158.       Control(I,DRIVEDOWN,PitchSpeedCheck,-DPitch)
  159.     elseif (Roll<-45) then
  160.       Control(I,DRIVELEFT,YawSpeedCheck,-DYaw)
  161.     elseif (Roll>45) then
  162.       Control(I,DRIVERIGHT,YawSpeedCheck,DYaw)
  163.     elseif (math.abs(Roll)<45) then
  164.       Control(I,DRIVEUP,PitchSpeedCheck,DPitch)
  165.     end
  166.   end
  167. end
  168.  
  169. --Calculate desired altitude and go there
  170. function AdjustAltitude(I, Engaged, Pos, CollisionAlt)
  171.   local DesiredAltitude = CruiseAltitude
  172.   local MatchingAltitude = Engaged and MatchTargetAltitude and Pos.GroundDistance < MatchAltitudeRange
  173.   if MatchingAltitude then
  174.     local EnemyAlt = UsePredictiveGuidance and CollisionAlt or Pos.AltitudeAboveSeaLevel
  175.     DesiredAltitude = math.max(math.min(EnemyAlt+MatchAltitudeOffset,MaxAltitude),MinMatchingAltitude)
  176.   end
  177.  
  178.   if (TerrainAvoidanceStrategy>0) then
  179.     DesiredAltitude = AdjustAltitudeForTerrain(I, DesiredAltitude, MatchingAltitude)
  180.   end
  181.  
  182.   local AltPower = limiter((DesiredAltitude-Alt)*AltitudeClamp,1)
  183.   if ((Alt < DesiredAltitude or VTOLEngines) and I:GetVelocityMagnitude()<UseVTOLBeneathSpeed) then
  184.     VTPower[4]=math.max(.2,AltPower)
  185.     AdjustElevationToAngle(I, 0)
  186.   else
  187.     local Angle = math.min(math.abs(Alt-DesiredAltitude)*AltitudeClamp,MaxElevationAngle)
  188.     if ((Alt<MinAltitude and Pitch<0) or (Alt>MaxAltitude and Pitch>0)) then
  189.       Angle = MaxElevationAngle
  190.     end -- pull up/down with full strength if we are below/above min/max altitudes
  191.     AdjustElevationToAngle(I, (Alt < DesiredAltitude) and Angle or -Angle)
  192.   end
  193.  
  194.   AdjustAltitudeJets(I,AltPower)
  195.   AdjustHelicopterBlades(I,AltPower)
  196.  
  197.   return DesiredAltitude
  198. end
  199.  
  200. function AdjustAltitudeJets(I,AltPower)
  201.   if not UseAltitudeJets then return end
  202.   if (AltPower>0) then
  203.     I:RequestThrustControl(4,AltPower)
  204.     vertical = "up"
  205.   else
  206.     I:RequestThrustControl(5,-AltPower)
  207.     vertical = "down"
  208.   end
  209. end
  210.  
  211. function ControlVTOLPower(I)
  212.   local DriveFraction = {}
  213.   if VTPower[4]~=0 then
  214.     for k,v in pairs(PitchEngines) do DriveFraction[k] = VTPower[4]*EngineDF[k] end
  215.     ComputeEngines(DriveFraction, RollEngines, 2)
  216.     ComputeEngines(DriveFraction, PitchEngines, 3)
  217.   else
  218.     for k,v in pairs(PitchEngines) do DriveFraction[k] = EngineDF[k] end
  219.   end
  220.   for k, v in pairs(PitchEngines) do I:Component_SetFloatLogic(9,k,DriveFraction[k]) end
  221. end
  222.  
  223. function AdjustHelicopterBlades(I,AltPower)
  224.   if not UseSpinners then return end
  225.   local MidSpeed = (MaxHelicopterBladeSpeed-MinHelicopterBladeSpeed)/2
  226.   local SpinSpeed = MinHelicopterBladeSpeed+MidSpeed+AltPower*MidSpeed
  227.   if not VTSpinners then
  228.     for p = 0, NumSpinners-1 do
  229.       if not ExcludedSpinners[p] then I:SetSpinnerContinuousSpeed(p, SpinSpeed) end
  230.     end
  231.   else
  232.     for k,v in pairs(HeliSpinners) do I:SetSpinnerContinuousSpeed(v, SpinSpeed) end
  233.   end
  234. end
  235.  
  236. -- Get the current stats about angles of the vehicle
  237. function GetAngleStats(I)
  238.   Roll = I:GetConstructRoll()
  239. -- transform roll into roll we are familiar with from the game
  240.   if (Roll > 180) then Roll = Roll-360 end
  241.  
  242.   Pitch = I:GetConstructPitch()
  243. -- transform pitch into pitch we are familiar with from the game
  244.   if (Pitch > 180) then
  245.     Pitch = 360 - Pitch
  246.   elseif (Pitch>0) then
  247.     Pitch = -Pitch
  248.   end
  249.  
  250.   Yaw = I:GetConstructYaw()
  251.  
  252. -- Many vehicles need to keep their nose pitched upwards relative to velocity.
  253. -- This uses basic machine learning to calculate the upwards pitch correction.
  254.   if not VTPower or VTPower[4]==0 then -- if we haven't been in VTOL mode
  255.     VPitch = math.deg(math.asin(I:GetVelocityVectorNormalized().y)) -- observed pitch from velocity
  256.     PitchCorrection = limiter(PitchCorrection + .01*(Pitch-VPitch-PitchCorrection),MaxElevationAngle)
  257.     Pitch = Pitch - PitchCorrection
  258.   end
  259.  
  260.   local AngularVelocity = I:GetLocalAngularVelocity()
  261.   DPitch = -AngularVelocity.x
  262.   DYaw = DYaw+.1*(AngularVelocity.y-DYaw)
  263.   DRoll = AngularVelocity.z
  264.  
  265.   CoM = I:GetConstructCenterOfMass()
  266.   Alt = CoM.y
  267.   ForwardV = I:GetConstructForwardVector()
  268. end
  269.  
  270. function sign(x)
  271.   return x>0 and 1 or x<0 and -1 or 0
  272. end
  273.  
  274. function GetModifiedAzimuth(Azimuth, Offset)
  275.   local Azimuth = Azimuth-(UsePreferredSide and 1 or sign(Azimuth))*Offset
  276.   if math.abs(Azimuth)>180 then return Azimuth-sign(Azimuth)*360 end
  277.   return Azimuth
  278. end
  279.  
  280. -- returns true if we are yawing, false if we are rolling
  281. function TurnTowardsAzimuth(I, Azimuth, Offset, DesiredAltitude, RTolerance, Flock)
  282.   Azimuth = GetModifiedAzimuth(Azimuth, Offset)
  283.   if Flock>0 then Azimuth=Flocking(I,Azimuth,Flock>1) end
  284.   if (VTPower[4]==0 and math.abs(Azimuth) > AngleBeforeRoll-Offset) then -- roll to turn
  285.     RollTowardsAzimuth(I,Azimuth,DesiredAltitude,RTolerance)
  286.     YawTowardsAzimuth(I,Azimuth)
  287.     state = "rolling"
  288.     return false
  289.   end -- yaw to turn
  290.   AdjustRollToAngle(I, 0)
  291.   state = "yawing"
  292.   YawTowardsAzimuth(I,Azimuth)
  293.   return true
  294. end
  295.  
  296. -- Try to roll the craft and pull up on pitch controls to face a given azimuth (relative to the vehicles facing)
  297. -- Will try to set roll angle at one that will maintain a given altitude
  298. function RollTowardsAzimuth(I,Azimuth,DesiredAltitude,Tolerance)
  299. -- depending on our altitude, RollAngle will be set to 90 +/- 40 degrees to climb or descend as appropriate
  300.   RollAngle = sign(Azimuth)*math.min(MaxRollAngle, 90+limiter((Alt-DesiredAltitude)*.66, 30))
  301.  
  302.   AdjustRollToAngle(I,RollAngle)
  303.  
  304.   if (sign(Roll)==sign(Azimuth) and Roll >= RollAngle-Tolerance and Roll <= RollAngle+Tolerance) then -- start pitching
  305.     Control(I, DRIVEUP,1,0)
  306.   end
  307. end
  308.  
  309. -- Roll the vehicle to a specified roll angle
  310. function AdjustRollToAngle(I,Angle)
  311.   local RollSpeedCheck = math.abs(Angle-Roll)/RollDamping
  312.   if (Roll < Angle) then
  313.     Control(I, ROLLLEFT,RollSpeedCheck,DRoll)
  314.   elseif (Roll > Angle) then
  315.     Control(I, ROLLRIGHT,RollSpeedCheck,-DRoll)
  316.   end
  317.   --I:LogToHud(string.format("%.2f %.2f %.2f %.2f", Angle, Roll, RollSpeedCheck, DRoll))
  318. end
  319.  
  320. -- transform an absolute azimuth into one relative to the nose of the vehicle
  321. function GetRelativeAzimuth(AbsAzimuth)
  322.   local Azimuth = math.abs(Yaw-AbsAzimuth)
  323.   if (Azimuth>180) then Azimuth = 360-Azimuth end
  324.   if (Yaw>AbsAzimuth) then return Azimuth end
  325.   return -Azimuth
  326. end
  327.      
  328. -- Yaw the vehicle towards a given aziumth (relative to the vehicle's facing)
  329. function YawTowardsAzimuth(I,Azimuth)
  330.   if (math.abs(Roll)<30) then
  331.     local YawSpeedCheck = math.abs(Azimuth)/YawDamping
  332.     if (Azimuth > 0) then
  333.       Control(I, DRIVELEFT,YawSpeedCheck,-DYaw)
  334.     elseif (Azimuth < 0) then
  335.       Control(I, DRIVERIGHT,YawSpeedCheck,DYaw)
  336.     end
  337.     return true
  338.   end
  339.   return false
  340. end
  341.  
  342. -- No enemies, just cruise along
  343. function Cruise(I)
  344.   if OrbitSpawn and I:GetNumberOfMainframes()>0 then
  345.     SpawnInfo = I:GetTargetPositionInfoForPosition(0, SpawnPos.x, 0, SpawnPos.z)
  346.     NavigateToPoint(I, false, SpawnInfo)
  347.   else
  348.     if I:GetNumberOfMainframes()==0 then
  349.       I:LogToHud("No AI mainframe found; please add one!")
  350.     end
  351.     state = "cruise"
  352.     AdjustAltitude(I,false,nil,0)
  353.     AdjustRollToAngle(I, 0)
  354.     SetSpeed(I, CruiseSpeed)
  355.     NextAttackTime = I:GetTime()+ForceAttackTime
  356.   end
  357. end
  358.  
  359. -- Given the current velocity vector and an azimuth, get the expected terrain height in that direction
  360. function GetTerrainAltitude(I, VelocityV, Angle)
  361.   local TerrainAltitude = -999
  362.   for i,Lookahead in ipairs(TerrainLookahead) do
  363.     Position = CoM + Quaternion.Euler(0,Angle,0) * VelocityV * Lookahead
  364.     TerrainAltitude = math.max(TerrainAltitude,I:GetTerrainAltitudeForPosition(Position))
  365.   end
  366.   return TerrainAltitude
  367. end
  368.  
  369. -- Adjust a given altitude to compensate for terrain, according to "TerrainAvoidanceStrategy"
  370. -- and other terrain-avoidance-specific parameters.
  371. -- Last parameter forces the use of "TerrainAvoidanceStrategy" 2
  372. -- returns new altitude that should be used
  373. function AdjustAltitudeForTerrain(I, Altitude, StrategyOverride)
  374.   VelocityV = I:GetVelocityVector()
  375.   TerrainAltitude = math.max(GetTerrainAltitude(I, VelocityV, 0),GetTerrainAltitude(I, VelocityV, 30),GetTerrainAltitude(I, VelocityV, -30))
  376.  
  377.   OverTerrain = false
  378.   if (TerrainAltitude + MinAltitude > Altitude or TerrainAltitude>0) then -- we'll need to avoid the terrain
  379.     OverTerrain = TerrainAltitude + MinAltitude > Altitude
  380.    
  381.     if(StrategyOverride or TerrainAvoidanceStrategy == 2) then -- don't change altitude unless needed
  382.       Altitude = math.max(Altitude, TerrainAltitude + MinAltitude)
  383.     elseif(TerrainAvoidanceStrategy == 1) then -- just add Altitude to terrain height
  384.       Altitude = math.max(math.min(TerrainAltitude+Altitude, MaxAltitude), TerrainAltitude + MinAltitude)
  385.     end
  386.   end
  387.  
  388.   --I:LogToHud(string.format("TA: %.2f Here: %.2f Alt: %.2f", TerrainAltitude, I:GetTerrainAltitudeForLocalPosition(0, 0, 0), Altitude))
  389.  
  390.   return Altitude
  391. end
  392.  
  393. function GetIntercept(I, Pos)
  394.   local mySpeed = I:GetVelocityMagnitude()
  395.   local TTT = FindConvergence(I, Pos.Position, Pos.Velocity, CoM, mySpeed, mySpeed*.75)
  396.   local Prediction = Pos.Position + Pos.Velocity * TTT
  397.   return TTT, I:GetTargetPositionInfoForPosition(0, Prediction.x, Prediction.y, Prediction.z).Azimuth, Prediction.y
  398. end
  399.  
  400. -- Perform a water start check. Returns true if movement is permitted.
  401. function WaterStartCheck(I)
  402.   if (Alt < DeployAlt) then
  403.     I:Component_SetBoolLogicAll(0, true)
  404.     WaterStarted = true
  405.   elseif (WaterStarted and Alt > ReleaseAlt) then
  406.     I:Component_SetBoolLogicAll(0, false)
  407.     WaterStarted = false
  408.   end
  409.  
  410.   if (not WaterStarted or Alt>=EnableEnginesAlt) then
  411.     return true
  412.   end
  413.   return false
  414. end
  415.  
  416. function SetSpeed(I, Speed)
  417.   if (I:GetVelocityMagnitude()>MaximumSpeed) then Speed=1
  418.   elseif (I:GetVelocityMagnitude()<MinimumSpeed) then Speed=5 end
  419.   if MainDriveControlType==1 then
  420.     I:RequestThrustControl(0,Speed/5)
  421.   elseif MainDriveControlType==2 then
  422.     I:RequestControl(MODE,DRIVEMAIN,5)
  423.     for k, v in pairs(Main) do I:Component_SetFloatLogic(9,v,Speed/5) end
  424.   else
  425.     I:RequestControl(MODE,DRIVEMAIN,Speed)
  426.   end
  427. end
  428.  
  429. function limiter(p,l)
  430.   return math.min(l,math.max(-l,p))
  431. end
  432.  
  433. function Control(I, Type, SpeedCheck, Delta)
  434.   local DoFType = math.floor(Type/2)+1
  435.   if (SpeedCheck>Delta) then
  436.     I:RequestControl(MODE, Type, 1)
  437.     DriveDesc[DoFType] = DriveTypeDesc[Type+1]
  438.   end
  439.   VTPower[DoFType] = (Type%2*2-1)*limiter(SpeedCheck*VTProportional[DoFType]-Delta*VTDelta[DoFType],1)
  440. end
  441.  
  442. function ComputeSpinners(Rotation, Spinners, Axis)
  443.   for Spinner,Dir in pairs(Spinners) do
  444.     Rotation[Spinner] = Rotation[Spinner] + Dir*VTPower[Axis]*MaxVTAngle[Axis]
  445.   end
  446. end
  447.  
  448. function ComputeEngines(DriveFraction, Engines, Axis)
  449.   for Engine,Dir in pairs(Engines) do
  450.     if Dir==sign(VTPower[Axis]) then
  451.       DriveFraction[Engine] = DriveFraction[Engine]*(1-math.abs(VTPower[Axis]))
  452.     end
  453.   end
  454. end
  455.  
  456. function VectorEngines(I)
  457.   local RL,RR,RY = 0,0,0
  458.  
  459.   local Rotation = {}
  460.   for k,v in pairs(AllSpinners) do Rotation[v] = 0 end
  461.  
  462.   if (state=="rolling" and VTPower[3]>0) then
  463.     VTPower[3]=0
  464.   end
  465.   ComputeSpinners(Rotation, YawSpinners, 1)
  466.   ComputeSpinners(Rotation, RollSpinners, 2)
  467.   ComputeSpinners(Rotation, PitchSpinners, 3)
  468.   if (VTOLEngines and VTPower[4]~=0) then -- VTOL Mode
  469.     for Spinner,Dir in pairs(DownSpinners) do Rotation[Spinner] = Dir*MaxVTAngle[4] end
  470.   else
  471.     ComputeSpinners(Rotation, DownSpinners, 4)
  472.   end
  473.  
  474.   RotateSpinnersTo(I,Rotation)
  475.  
  476.   --I:LogToHud(string.format("Y: %.02f R: %.02f P: %.02f", VTPower[1], VTPower[2], VTPower[3]))
  477. end
  478.  
  479. function ClassifySpinner(I,p)
  480.   if VTSpinners=='all' and ExcludedSpinners[p] then return end
  481.   local info = I:GetSpinnerInfo(p)
  482.   local h,a,b = EulerAngles(info.LocalRotation)
  483.   local pos = info.LocalPositionRelativeToCom
  484.   SpinnerStartRotations[p]=info.LocalRotation
  485.   a=math.floor(a+.5)
  486.   b=math.floor(b+.5)
  487.   if (a==0 and b==0) then
  488.     tinsert(pos.z,YawSpinners,1,-1,p)
  489.     AddSpinner(pos.y,-1,p)
  490.   elseif (a==0 and math.abs(b)>170) then
  491.     tinsert(pos.z,YawSpinners,-1,1,p)
  492.     AddSpinner(pos.y,1,p)
  493.   else
  494.     local h,a,b = EulerAngles(info.LocalRotation*Quaternion.Euler(0, 0, 90))
  495.     h=math.floor(h+.5)
  496.     a=math.floor(a+.5)
  497.     if (h==0 and a==0) then -- pointed right
  498.       tinsert(pos.z,PitchSpinners,1,-1,p)
  499.       if VTOLSpinners=='all' and ExcludedSpinners[p] then tinsert(pos.z,DownSpinners,-1,-1,p) end
  500.       AddSpinner(pos.x,-1,p)
  501.     elseif (a==0 and math.abs(h)>170) then -- pointed left
  502.       tinsert(pos.z,PitchSpinners,-1,1,p)
  503.       if VTOLSpinners=='all' and ExcludedSpinners[p] then tinsert(pos.z,DownSpinners,1,1,p) end
  504.       AddSpinner(pos.x,1,p)
  505.     end
  506.   end
  507. end
  508.  
  509. function ClassifyVTOLSpinner(I,p)
  510.   local info = I:GetSpinnerInfo(p)
  511.   local h,a,b = EulerAngles(info.LocalRotation*Quaternion.Euler(0, 0, 90))
  512.   local pos = info.LocalPositionRelativeToCom
  513.   h=math.floor(h+.5)
  514.   a=math.floor(a+.5)
  515.   --I:Log(string.format("Spinner: %d Orientation: (%.2f, %.2f, %.2f) Position: (%.2f, %.2f, %.2f)", p, h, a, b, pos.x, pos.y, pos.z))
  516.   if (h==0 and a==0) then
  517.     tinsert(pos.z,DownSpinners,-1,-1,p)
  518.   elseif (a==0 and math.abs(h)>170) then
  519.     tinsert(pos.z,DownSpinners,1,1,p)
  520.   end
  521. end
  522.  
  523. function GetPosRelativeToCoM(pos)
  524.   local rot = Quaternion.Inverse(Quaternion.LookRotation(ForwardV))
  525.   local rpos = rot*(pos-CoM)
  526.   for i, p in ipairs(rpos) do rpos[i]=math.floor(p*10+.5)/10 end
  527.   return rpos
  528. end
  529.  
  530. function ClassifyEngine(I,p,force)
  531.   local info = I:Component_GetBlockInfo(9,p)
  532.   if (force or info.LocalForwards.y==1) then
  533.     pos = GetPosRelativeToCoM(info.Position)
  534.     EngineDF[p] = I:Component_GetFloatLogic(9,p)
  535.     tinsert(pos.z,PitchEngines,1,-1,p)
  536.     if pos.x<-1.1 then  -- on left
  537.       tinsert(pos.z,RollEngines,-1,-1,p)
  538.     elseif pos.x>1.1 then -- on right
  539.       tinsert(pos.z,RollEngines,1,1,p)
  540.     end
  541.   end
  542. end
  543.  
  544. function AddSpinner(comp,dir,p)
  545.   if comp<-1 then  -- on left
  546.     RollSpinners[p] = dir
  547.   elseif comp>1 then -- on right
  548.     RollSpinners[p] = -dir
  549.   end
  550.   table.insert(AllSpinners,p)
  551. end
  552.      
  553. function tinsert(z,s,x,y,p)
  554.   s[p] = z>0 and x or y
  555. end
  556.      
  557. function EulerAngles(q1)
  558.   local sqw = q1.w*q1.w
  559.   local sqx = q1.x*q1.x
  560.   local sqy = q1.y*q1.y
  561.   local sqz = q1.z*q1.z
  562.   local unit = sqx + sqy + sqz + sqw --if normalised is one, otherwise is correction factor
  563.   local test = q1.x*q1.y + q1.z*q1.w
  564.   local heading, attitude, bank
  565.   if (test > 0.499*unit) then --singularity at north pole
  566.     heading = 2 * math.atan2(q1.x,q1.w)
  567.     attitude = math.pi/2;
  568.     bank = 0
  569.   elseif (test < -0.499*unit) then --singularity at south pole
  570.     heading = -2 * math.atan2(q1.x,q1.w)
  571.     attitude = -math.pi/2
  572.     bank = 0
  573.   else
  574.     heading = math.atan2(2*q1.y*q1.w-2*q1.x*q1.z , sqx - sqy - sqz + sqw)
  575.     attitude = math.asin(2*test/unit)
  576.     bank = math.atan2(2*q1.x*q1.w-2*q1.y*q1.z , -sqx + sqy - sqz + sqw)
  577.   end
  578.   return math.deg(heading), math.deg(attitude), math.deg(bank)
  579. end
  580.  
  581. function RotateSpinnersTo(I, Spinners)
  582.   for Spinner, NewAngle in pairs(Spinners) do
  583.     local Angle = EulerAngles(Quaternion.Inverse(SpinnerStartRotations[Spinner]) * I:GetSpinnerInfo(Spinner).LocalRotation)
  584.     local DeflectAngle = limiter(limiter(NewAngle,90)-Angle,43)
  585.     if math.abs(DeflectAngle)<2 then DeflectAngle=0 end
  586.     I:SetSpinnerContinuousSpeed(Spinner,DeflectAngle*30/43)
  587.   end
  588. end
  589.  
  590. function FindConvergence(I, tPos, tVel, wPos, wSpeed, minConv)
  591.    local relativePosition = wPos - tPos
  592.    local distance = Vector3.Magnitude(relativePosition)
  593.    local targetAngle = I:Maths_AngleBetweenVectors(relativePosition, tVel)
  594.    local tSpeed = Vector3.Magnitude(tVel)
  595.  
  596.    local a = tSpeed^2 - wSpeed^2
  597.    local b = -2 * tSpeed * distance * math.cos(math.rad(targetAngle))
  598.    local c = distance^2
  599.    local det = math.sqrt(b^2-4*a*c)
  600.    local ttt = distance / minConv
  601.  
  602.    if det > 0 then
  603.       local root1 = math.min((-b + det)/(2*a), (-b - det)/(2*a))
  604.       local root2 = math.max((-b + det)/(2*a), (-b - det)/(2*a))
  605.       ttt = (root1 > 0 and root1) or (root2 > 0 and root2) or ttt
  606.    end
  607.    return ttt
  608. end
  609.  
  610. function CheckMissileWarnings(I)
  611.   if WarningMainframe<0 then return 0 end
  612.  
  613.   local NumWarnings = I:GetNumberOfWarnings(WarningMainframe)
  614.   local MinTTT = math.max(RunAwayTTT,DodgeTTT)
  615.   local MinWarning
  616.   local OwnVelocity = ForwardV * math.max(NormalSpeed, I:GetVelocityMagnitude())
  617.  
  618.   for w = 0, NumWarnings - 1 do
  619.     local Warning = I:GetMissileWarning(WarningMainframe, w)
  620.     if Warning.Valid and I:Maths_AngleBetweenVectors(Warning.Velocity, CoM - Warning.Position) < 90 then
  621.       local mSpeed = Vector3.Magnitude(Warning.Velocity)
  622.       local TTT = FindConvergence(I, CoM, OwnVelocity, Warning.Position, mSpeed, mSpeed*.75)
  623.       local PredictedPosition = CoM + OwnVelocity * TTT
  624.       local Orthogonal = (Warning.Position + Vector3.Normalize(Warning.Velocity)
  625.                          * Vector3.Distance(Warning.Position, PredictedPosition)
  626.                          * math.cos(math.rad(I:Maths_AngleBetweenVectors(Warning.Velocity, PredictedPosition - Warning.Position))))
  627.                          - PredictedPosition
  628.       local AdjustedPosition = PredictedPosition + Vector3.ClampMagnitude(Orthogonal, CraftRadius)
  629.       local Radius = (Vector3.Distance(Warning.Position, AdjustedPosition) / 2)
  630.                      / math.cos(math.rad(I:Maths_AngleBetweenVectors(Warning.Velocity, AdjustedPosition - Warning.Position) - 90))
  631.       if TTT < MinTTT and Radius > DangerRadius then
  632.         MinTTT = TTT
  633.         MinWarning = Warning
  634.       end
  635.     end
  636.   end
  637.  
  638.   if (MinTTT < math.max(RunAwayTTT,DodgeTTT)) then
  639.     local AdjustedPosition = CoM + OwnVelocity * math.min(MinTTT,0.75)
  640.     local ApproachAngle=I:Maths_AngleBetweenVectors(MinWarning.Velocity, AdjustedPosition - MinWarning.Position)
  641.     if (MinTTT < DodgeTTT) then
  642.       if (math.abs(MinWarning.Azimuth) < 10 or math.abs(MinWarning.Azimuth) > 170) then
  643.         return sign(Roll)*120
  644.       end
  645.      
  646.       local Orthogonal = (MinWarning.Position + Vector3.Normalize(MinWarning.Velocity)
  647.                          * Vector3.Distance(MinWarning.Position, AdjustedPosition)
  648.                          * math.cos(math.rad(ApproachAngle))) - AdjustedPosition
  649.       local Objective = I:GetConstructPosition() - Orthogonal
  650.       return I:GetTargetPositionInfoForPosition(0, Objective.x, Objective.y, Objective.z).Azimuth
  651.     end
  652.     local Angle=MinWarning.Azimuth>0 and RunAngle or -RunAngle
  653.     local Objective = I:GetConstructPosition()+Quaternion.Euler(0,Angle,0)*(MinWarning.Position-CoM)
  654.     return I:GetTargetPositionInfoForPosition(0, Objective.x, 0, Objective.z).Azimuth
  655.   end
  656.   return 0
  657. end
  658.  
  659. function ClassifyEngines(I)
  660.   if NumEngines==I:Component_GetCount(9) then return end
  661.   NumEngines=I:Component_GetCount(9)
  662.  
  663.   Main={} -- try to figure out which drives are pointed backwards
  664.   for p = 0, NumEngines - 1 do
  665.     local info=I:Component_GetBlockInfo(9,p)
  666.     if (info.LocalForwards.z==1 and GetPosRelativeToCoM(info.Position)==info.LocalPositionRelativeToCom) then
  667.       table.insert(Main,p)
  668.     end
  669.   end
  670.  
  671.   if VTOLEngines=='all' then
  672.     for p = 0, NumEngines - 1 do ClassifyEngine(I,p,false) end
  673.   end
  674. end
  675.  
  676. function ClassifySpinners(I)
  677.   if NumSpinners==I:GetSpinnerCount() then return end
  678.   NumSpinners=I:GetSpinnerCount()
  679.  
  680.   if VTSpinners=='all' then
  681.     for p = 0, NumSpinners - 1 do ClassifySpinner(I,p) end
  682.   end
  683. end
  684.  
  685. function NameMatches(Name)
  686.   if FlockWithBlueprintNames=='all' then return true end
  687.   for k,v in pairs(FlockWithBlueprintNames) do
  688.     if v==string.sub(Name,1,string.len(v)) then return true end
  689.   end
  690.   return false
  691. end
  692.  
  693. function Flocking(I, Azimuth, Escaping)
  694.   if NeighborRadius==0 then return Azimuth end
  695.  
  696.   local FCount = I:GetFriendlyCount()
  697.   local A = Vector3(0,0,0)
  698.   local C,S,J,L,E = A,A,A,A,A
  699.   local Near,Aligning,Injured,Far,Terrain=0,0,0,0,0
  700.   for f = 0, FCount-1 do
  701.     local FInfo = I:GetFriendlyInfo(f)
  702.     if FInfo.Valid then
  703.       local Dist=FInfo.CenterOfMass-CoM
  704.       if Dist.magnitude<NeighborRadius then
  705.         if FInfo.Velocity.magnitude>=IgnoreBelowSpeed and NameMatches(FInfo.BlueprintName) then
  706.           A=A+FInfo.Velocity  -- Alignment
  707.           C=C+FInfo.CenterOfMass -- Cohesion
  708.           J=J+FInfo.CenterOfMass*(1-FInfo.HealthFraction) -- Injured cohesion
  709.           Aligning=Aligning+1
  710.           Injured=Injured+(1-FInfo.HealthFraction)
  711.         end
  712.         S=S+Dist -- Separation
  713.         Near=Near+1
  714.       elseif FInfo.Velocity.magnitude>=IgnoreBelowSpeed and NameMatches(FInfo.BlueprintName) then
  715.         L=L+FInfo.CenterOfMass -- Long-range cohesion
  716.         Far=Far+1
  717.       end
  718.     end
  719.   end
  720.  
  721.   if TerrainAvoidanceWeight~=0 then
  722.     local ForwardV = Vector3.forward * NeighborRadius
  723.     local Angles = {0,45,90,135,180,225,270,315}
  724.     for k, a in pairs(Angles) do
  725.       local Position = CoM + Quaternion.Euler(0,a,0) * ForwardV
  726.       local TerrainAltitude = I:GetTerrainAltitudeForPosition(Position)
  727.       if (TerrainAltitude + MinAltitude > Alt) then
  728.         local Dist=Position-CoM
  729.         E=E+Dist*(1 + math.min(50,TerrainAltitude + MinAltitude - Alt)*.02)
  730.         Terrain=Terrain+1
  731.       end
  732.     end
  733.     if Terrain>0 then E=(-E/Terrain).normalized*TerrainAvoidanceWeight end
  734.   end
  735.  
  736.   if I:GetNumberOfMainframes() > 0 then
  737.     local ECount = I:GetNumberOfTargets(0)
  738.     for e = 1, ECount-1 do
  739.       local EInfo = I:GetTargetPositionInfo(0,e)
  740.       if EInfo.Valid then
  741.         local Dist=EInfo.Position-CoM
  742.         if Dist.magnitude<NeighborRadius and math.abs(EInfo.Position.y-Alt)<CollisionDetectionHeight then
  743.           S=S+Dist -- Separation
  744.           Near=Near+1
  745.         end
  746.       end
  747.     end
  748.   end
  749.  
  750.   if Near+Far+Terrain==0 then return Azimuth end
  751.   if Aligning>0 then
  752.     A=(A/Aligning).normalized*AlignmentWeight
  753.     C=(C/Aligning-CoM).normalized*CohesionWeight
  754.   end
  755.   if Injured>0 then J=(J/Injured-CoM).normalized*InjuredWeight end
  756.   if Far>0 then L=(L/Far-CoM).normalized*LongRangeWeight end
  757.   if Near>0 then S=(-S/Near).normalized*SeparationWeight end
  758.  
  759.   local T=Quaternion.Euler(0,Yaw-Azimuth,0)*Vector3.forward*TargetWeight*(Escaping and 0 or 1)
  760.   local Objective = I:GetConstructPosition()+A+C+S+J+L+E+T
  761.   return I:GetTargetPositionInfoForPosition(0, Objective.x, 0, Objective.z).Azimuth
  762. end
  763.  
  764. -- given information about a target, navigates to it
  765. function NavigateToPoint(I, Engaged, Pos)
  766.   Speed = EscapeSpeed
  767.   local DodgeAngle = CheckMissileWarnings(I)
  768.   local CollisionTTT, CollisionAzimuth, CollisionAlt = GetIntercept(I, Pos)
  769.   local DesiredAltitude = AdjustAltitude(I,Engaged,Pos,CollisionAlt)
  770.  
  771.   if (DodgeAngle~=0) then -- dodge missiles!
  772.     TurnTowardsAzimuth(I, DodgeAngle, 0, DesiredAltitude, DodgingRollTolerance, 0)
  773.     Speed = DodgingSpeed
  774.     state = "dodging"
  775.     onattackrun = true
  776.   elseif (Engaged and CollisionTTT<CollisionTThreshold and
  777.           (math.abs(Pos.AltitudeAboveSeaLevel-Alt)<CollisionDetectionHeight or
  778.            math.abs(CollisionAlt-Alt)<CollisionDetectionHeight) and
  779.            math.abs(CollisionAzimuth)<CollisionAngle) then
  780.     TurnTowardsAzimuth(I, CollisionAzimuth, CollisionAngle, DesiredAltitude, DodgingRollTolerance, 0)
  781.     Speed = DodgingSpeed
  782.     state = "collision"
  783.     onattackrun = true
  784.   elseif (Pos.GroundDistance > ClosingDistance) then
  785.     Speed = TurnTowardsAzimuth(I, CollisionAzimuth, 0, DesiredAltitude, RollTolerance, 1) and ClosingSpeed or RollingSpeed
  786.     state = "closing"
  787.     onattackrun = true
  788.     CruiseAltitude = DiveAltitude
  789.   elseif onattackrun then
  790.     local Azimuth = UsePredictiveGuidance and CollisionAzimuth or Pos.Azimuth
  791.     Speed = TurnTowardsAzimuth(I, Azimuth, AngleBeforeTurn, DesiredAltitude, RollTolerance, 1) and AttackRunSpeed or RollingSpeed
  792.     if (Pos.GroundDistance < AbortRunDistance) then
  793.       onattackrun = false
  794.       NextAttackTime = I:GetTime()+ForceAttackTime
  795.       EscapeAngle = GetModifiedAzimuth(Yaw-Azimuth, AngleOfEscape)
  796.     end
  797.   else -- not on attack run, just go forwards until we're out of range
  798.     TurnTowardsAzimuth(I, GetRelativeAzimuth(EscapeAngle), 0, DesiredAltitude, RollTolerance, 2)
  799.     state = "escaping"
  800.     onattackrun = Pos.GroundDistance > AttackRunDistance or I:GetTime()>NextAttackTime
  801.     CruiseAltitude = CruiseAltOld
  802.   end
  803.  
  804.   if OverTerrain then
  805.     Speed = math.min(Speed,MaxTerrainSpeed)
  806.   end
  807.   if not Engaged then
  808.     Speed = math.min(Speed,CruiseSpeed)
  809.   end
  810.   SetSpeed(I, Speed)
  811. end
  812.  
  813. -- Calculate all movement for the vehicle
  814. function Movement(I)
  815.   if firstpass then
  816.     firstpass = false
  817.     SpawnPos = CoM
  818.     EscapeAngle = Yaw
  819.     if type(AltitudeOffset) == "table" then
  820.       math.randomseed(I:GetTime()+CoM.x+CoM.y+CoM.z)
  821.       AltitudeOffset=math.floor(math.random()*(AltitudeOffset[2]-AltitudeOffset[1])+AltitudeOffset[1])
  822.       I:Log("AltitudeOffset: "..AltitudeOffset)
  823.     end
  824.     CruiseAltitude = CruiseAltitude+AltitudeOffset
  825.     MaxAltitude = MaxAltitude+AltitudeOffset
  826.     MatchAltitudeOffset = MatchAltitudeOffset+AltitudeOffset
  827.     MinMatchingAltitude = MinMatchingAltitude+AltitudeOffset
  828.  
  829.     if type(VTOLEngines) == "table" then
  830.       for k, p in pairs(VTOLEngines) do ClassifyEngine(I,p,true) end
  831.     end
  832.  
  833.     if type(VTSpinners) == "table" then
  834.       local Used = {}
  835.       for k, p in pairs(VTSpinners) do
  836.         ClassifySpinner(I,p)
  837.         Used[p]=true
  838.       end
  839.       for p = 0, I:GetSpinnerCount()-1 do
  840.         if not Used[p] then table.insert(HeliSpinners,p) end
  841.       end
  842.     end
  843.     if type(VTOLSpinners) == "table" then
  844.       for k, p in pairs(VTOLSpinners) do ClassifyVTOLSpinner(I,p) end
  845.     end
  846.     if ExcludeSpinners then
  847.       for k, p in pairs(ExcludeSpinners) do ExcludedSpinners[p]=true end
  848.     end
  849.   end
  850.   ClassifyEngines(I)
  851.   ClassifySpinners(I)
  852.  
  853.   DriveDesc = {'','','',''}
  854.   VTPower = {0,0,0,0} -- yaw, roll, pitch, VTOL
  855.    
  856.   if (I:GetNumberOfMainframes() > 0 and I:GetNumberOfTargets(0) > 0) then
  857.     local TargetPos = I:GetTargetPositionInfo(0,0)
  858.     if TargetPos.Valid then
  859.       NavigateToPoint(I, true, TargetPos)
  860.     else
  861.       Cruise(I)
  862.     end
  863.   else
  864.     Cruise(I)
  865.   end
  866.  
  867.   if VTSpinners then VectorEngines(I) end
  868.   if VTOLEngines then ControlVTOLPower(I) end
  869.  
  870.   if DebugMode then
  871.     --I:LogToHud(string.format("%.2f %.2f", DPitch, math.abs(Roll)))
  872.     I:LogToHud(string.format("%s %s %s %s %d", state, DriveDesc[1], DriveDesc[2], DriveDesc[3], Speed))
  873.     --I:Log(string.format("Y:%.2f R:%.2f P:%.2f", Yaw, Roll, Pitch))
  874.   end
  875. end
  876.  
  877. -- Main update function. Everything starts here.
  878. function Update(I)
  879.   GetAngleStats(I)
  880.   if  not I:IsDocked() and WaterStartCheck(I) then
  881.     Movement(I)
  882.   else
  883.     for p = 0, I:GetSpinnerCount()-1 do I:SetSpinnerRotationAngle(p,0) end
  884.   end
  885. end
  886. --
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement