Advertisement
Guest User

Untitled

a guest
Nov 1st, 2015
85
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 16.78 KB | None | 0 0
  1. -- v0.9
  2.  
  3. controllingMainframe = 0  --the mainframe with the target prioritization card
  4. restrictSlot = 1          --ai will only control these weapons, and will orient the craft along those
  5. attackFromDistance = 750  -- distance at which do the attack run
  6. abortAttackDistance = 75 -- distance at which we disengage
  7. abortRunDistance = 500    -- distance at which we reengage
  8.  
  9. Kp = 10   --amount of control proportional to deviation
  10. Ki = 0.01  --amount of accumulation to contrast deviation (keep small!)
  11. Kd = 1   --amount of dampening proportional to oscillation
  12.  
  13. minAlt = 50
  14. maxAlt = 300
  15.  
  16.  
  17. useOptimizer = 1
  18.  
  19. function SimplexOptimizerFactory()
  20.     local this = {}
  21.     function this.CreateSimplexOptimizer(this,dims)
  22.         local simple = {}
  23.         simple.Dim=dims
  24.         simple.Simplex = {}
  25.        
  26.         for a,b in pairs(this) do
  27.             simple[a] = b
  28.         end
  29.         simple.CreateSimplexOptimizer = nil
  30.         simple.Update = simple.Initial
  31.        
  32.         return simple
  33.     end
  34.     function this.SortFirst(a,b)
  35.         if a[1]<b[1] then
  36.             return true
  37.         else
  38.             return false
  39.         end
  40.     end
  41.     function this.AddHint(this,hint)
  42.         if not this.Hint then
  43.             this.Hint = {}
  44.         end
  45.         table.insert(this.Hint,hint)
  46.     end
  47.     function this.Initial(this,result,sample) -- Initial phase, populate samples
  48.         if result then
  49.             local merge = {result,sample}
  50.             table.insert(this.Simplex,merge)
  51.         end
  52.         if this.Hint then
  53.             local thishint = table.remove(this.Hint)
  54.             if #this.Hint == 0 then
  55.                 this.Hint = nil
  56.             end
  57.             return thishint
  58.         end
  59.         if #this.Simplex > this.Dim then
  60.             table.sort(this.Simplex,this.SortFirst)
  61.             while #this.Simplex > this.Dim+1 do -- Drop worst samples to create a simplex
  62.                 table.remove(this.Simplex)
  63.             end
  64.             this.Update=this.SReflection
  65.             return this.Point(this,1) -- 1 = reflection
  66.         end
  67.         local ret = {}
  68.         for i=1,this.Dim do
  69.             table.insert(ret,0.5-math.random())
  70.         end
  71.         return ret
  72.     end
  73.     function this.Centroid(this)
  74.         local centroid = {}
  75.         local ndim = this.Dim
  76.         for i=1,ndim do
  77.             table.insert(centroid,0)
  78.         end
  79.         for i=1,ndim do
  80.             for a,b in pairs(this.Simplex[i][2]) do
  81.                 centroid[a] = centroid[a] + b
  82.             end
  83.         end
  84.         for a,b in pairs(centroid) do
  85.             centroid[a] = (b / ndim)
  86.         end
  87.         return centroid
  88.     end
  89.     function this.Point(this,pow)
  90.         local point = this.Centroid(this)
  91.         local ndim = this.Dim
  92.         for a,b in pairs(point) do
  93.             point[a] = b + pow*(b - this.Simplex[ndim+1][2][a])
  94.         end
  95.         return point
  96.     end
  97.     function this.SReflection(this,result,sample) -- Reflection stage
  98.         if result > this.Simplex[1][1] then -- If it is the best one go to expansion
  99.             this.Update = this.SExpansion
  100.             this.Reflected={result,sample}
  101.             return this.Point(this,2)
  102.         end
  103.         if result > this.Simplex[this.Dim][1] then -- if better than second worse
  104.             local ns = {result,sample}
  105.             table.insert(this.Simplex)
  106.             table.sort(this.Simplex,this.SortFirst)
  107.             table.remove(this.Simplex)
  108.             return this.Reflection(this)
  109.         end
  110.         --- here we do contraction
  111.         this.Update = this.SContraction
  112.         return this.Point(this,-0.5)
  113.     end
  114.     function this.SExpansion(this,result,sample)        
  115.         if result > this.Reflected[1] then -- if the epanded point is better than the reflected point
  116.             table.insert(this.Simplex,{result,sample})
  117.         else
  118.             table.insert(this.Simplex,this.Reflected)
  119.         end
  120.         table.sort(this.Simplex,this.SortFirst)
  121.         table.remove(this.Simplex)
  122.         this.Update = this.SReflection
  123.         return this.Point(this,1)
  124.     end
  125.     function this.SContraction(this,result,sample)
  126.         if result > this.Simplex[this.Dim+1][1] then -- If the contracted point is better than the worst point
  127.             table.insert(this.Simplex,{result,sample})
  128.             table.sort(this.Simplex,this.SortFirst)
  129.             table.remove(this.Simplex)
  130.         else -- do reduction
  131.             for i=2,this.Dim+1 do
  132.                 local arr = this.Simplex[i][2]
  133.                 for a,b in pairs(arr) do
  134.                     local x1 = this.Simplex[1][2][a]
  135.                     arr[a] = x1 + 0.5*(b-x1)
  136.                 end
  137.                 this.AddHint(this,arr)
  138.             end
  139.             while #this.Simplex > 1 do
  140.                 table.remove(this.Simplex)
  141.             end
  142.             this.Update=this.Initial
  143.             return this.Initial(this)
  144.         end
  145.     end
  146.     return this
  147. end
  148.  
  149. local function PIDFactory()
  150.     local this = {}
  151.     function this.pidupd(this,err)
  152.         local P = this.P
  153.         local I = this.I
  154.         local D = this.D
  155.         local Integrator = this.Integrator + I*err
  156.         local max = this.SatMax
  157.         local min = this.SatMin
  158.         local last = this.Last
  159.         this.Last = err
  160.         local delta = err-last
  161.         local out = P*err+D*delta+Integrator
  162.         if out > max then -- Integrator windup prevention
  163.             Integrator = this.Integrator
  164.             out = max
  165.         end
  166.         if out < min then -- Integrator windup prevention
  167.             Integrator = this.Integrator
  168.             out = min
  169.         end
  170.         this.Integrator = Integrator
  171.         return out
  172.     end
  173.     function this.pidconfig(this,p,i,d)
  174.         this.P = p
  175.         this.I = i
  176.         this.D = d
  177.     end
  178.     function this.pidlimits(this,max,min)
  179.         this.SatMax = max
  180.         this.SatMin = min
  181.     end
  182.     function this.CreatePID(this)
  183.         local pid = {}
  184.         pid.Update = this.pidupd
  185.         pid.Configure = this.pidconfig
  186.         pid.SetLimits = this.pidlimits
  187.         pid.P = 1 -- Just make it a simple passthrough for now
  188.         pid.I = 0
  189.         pid.D = 0
  190.         pid.Integrator=0
  191.         pid.Last=0
  192.         pid.SatMax = 1
  193.         pid.SatMin = -1
  194.         return pid
  195.     end
  196.     return this
  197. end
  198.  
  199. function killThatThing(I, weaponInfo, targetInfo, weaponIndex, turretSpinnerIndex)
  200.  
  201.     P = targetInfo.AimPointPosition
  202.     V = targetInfo.Velocity
  203.     WS = weaponInfo.Speed
  204.     G = I:GetGravityForAltitude(P.y).magnitude
  205.  
  206.  
  207.     T = (P - weaponInfo.GlobalPosition).magnitude / (WS*0.85) -- FIRST ESTIMATE OF FUTURE TARGET POSITION
  208.  
  209.  
  210.     FP = P + V*T  - I:GetVelocityVector()*T
  211.  -- position the target will be
  212.  
  213.     AD = FP - weaponInfo.GlobalPosition
  214.   -- direction direct to the target
  215.  
  216.  
  217.  
  218.     PJ = Mathf.Sqrt(AD.x*AD.x + AD.z*AD.z)
  219.  -- horizontal distance to the target
  220.  
  221.    
  222.     S2 = WS*WS
  223.   -- speed^2
  224.     S4 = S2*WS*WS
  225.   -- speed^4 (need these many times)
  226.     DELTA = S4 - G*(G*PJ*PJ + 2*AD.y * S2)
  227.  -- is there a solution to the parable?
  228.      
  229.  
  230.     if ( DELTA > 0 ) then
  231.         --ok we can reach it and now we have a better estimate
  232.        
  233.         AG = Mathf.Atan2(S2 - Mathf.Sqrt(DELTA),G*PJ)
  234.  --calculate angle
  235.        
  236.         --now we can calculate a better time to target using the horizontal speed
  237.         --as obtained from the firing angle
  238.        
  239. T = (P - weaponInfo.GlobalPosition).magnitude / (WS * Mathf.Cos(AG))
  240.        
  241.  
  242.         FP = P + V*T - I:GetVelocityVector()*T
  243.   --position target will be
  244.  
  245.         AD = FP - weaponInfo.GlobalPosition
  246.  -- line direct to the target position
  247.  
  248.  
  249.  
  250.         PJ = Mathf.Sqrt(AD.x*AD.x + AD.z*AD.z)
  251.   -- horizontal distance to the target
  252.  
  253.    
  254.      
  255.         DELTA = S4 - G*(G*PJ*PJ + 2*AD.y * S2)
  256.  -- check the parable solution
  257.  
  258.  
  259.         if ( DELTA > 0 ) then
  260.         --ok we can reach it and now we have a better estimate
  261.         PY = (S2 - Mathf.Sqrt(DELTA))/(G)
  262.  -- no need to calculate angle or tangent, just the elev
  263.  
  264.  
  265.  
  266.         AD.y = PY --assign new elevation to the firing direction
  267.  
  268.  
  269.         if(turretSpinnerIndex < 0) then
  270.            I:AimWeaponInDirection(weaponIndex, AD.x, AD.y, AD.z, weaponInfo.WeaponSlot)
  271.            I:FireWeapon(weaponIndex, weaponInfo.WeaponSlot)
  272.         else
  273.            I:AimWeaponInDirectionOnTurretOrSpinner(
  274.                  turretSpinnerIndex,weaponIndex, AD.x, AD.y, AD.z, weaponInfo.WeaponSlot)
  275.  
  276.          
  277.            I:FireWeaponOnTurretOrSpinner(
  278.                  turretSpinnerIndex,weaponIndex,weaponInfo.WeaponSlot)
  279.  
  280.         end
  281.  
  282.       end
  283.          
  284.     end
  285.    return AD
  286. end
  287.  
  288. factory = PIDFactory()
  289. yawPID = factory.CreatePID(factory)
  290. pitchPID = factory.CreatePID(factory)
  291. rollPID = factory.CreatePID(factory)
  292.  
  293. yawPID.SetLimits(yawPID,1,-1)
  294. pitchPID.SetLimits(pitchPID,1,-1)
  295. rollPID.SetLimits(rollPID,1,-1)
  296.  
  297. if (useOptimizer == 1) then
  298. SimpFact = SimplexOptimizerFactory()
  299.  
  300. yawOpti = SimpFact.CreateSimplexOptimizer(SimpFact,3)
  301. yawOpti.AddHint(yawOpti,{Kp,Ki,Kd})
  302. yawSample = yawOpti.Update(yawOpti,nil,nil)
  303. yawPID.Configure(yawPID,unpack(yawSample))
  304.  
  305. rollOpti = SimpFact.CreateSimplexOptimizer(SimpFact,3)
  306. rollOpti.AddHint(rollOpti,{Kp,Ki,Kd})
  307. rollSample = rollOpti.Update(rollOpti,nil,nil)
  308. rollPID.Configure(rollPID,unpack(rollSample))
  309.  
  310. pitchOpti = SimpFact.CreateSimplexOptimizer(SimpFact,3)
  311. pitchOpti.AddHint(pitchOpti,{Kp,Ki,Kd})
  312. pitchSample = pitchOpti.Update(pitchOpti,nil,nil)
  313. pitchPID.Configure(pitchPID,unpack(pitchSample))
  314.  
  315. else
  316. yawPID.Configure(yawPID,Kp,Ki,Kd)
  317. pitchPID.Configure(pitchPID,Kp,Ki,Kd)
  318. rollPID.Configure(rollPID,Kp,Ki,Kd)
  319. end
  320.  
  321.  
  322. yawScore = 0
  323. pitchScore = 0
  324. rollScore = 0
  325. accumulator = 0
  326.  
  327. function flyAlong(I, direction, normal)
  328.     I:RequestThrustControl(0, 1)
  329.     logstr = ""
  330.      dt = 1
  331.     lastYaw = eYaw
  332.     lastPitch = ePitch
  333.     lastRoll = eRoll
  334.     --I:RequestControl(mode,type,drive)
  335.     eYaw =  Vector3.Dot(
  336.             Vector3.ProjectOnPlane(direction, I:GetConstructUpVector() ),
  337.             I:GetConstructRightVector()
  338.             )
  339.     ePitch = Vector3.Dot(
  340.             Vector3.ProjectOnPlane(direction, I:GetConstructRightVector() ),
  341.             I:GetConstructUpVector()
  342.             )
  343.     eRoll = Vector3.Dot(
  344.             Vector3.ProjectOnPlane(normal, I:GetConstructForwardVector() ),
  345.             I:GetConstructRightVector()
  346.             )
  347.      
  348.     accumulator = accumulator + 1
  349.     yawScore = yawScore + math.abs(eYaw)
  350.     pitchScore = pitchScore + math.abs(ePitch)
  351.     rollScore = rollScore + math.abs(eRoll)
  352.    
  353.     if accumulator > 100 then
  354.         if(useOptimizer == 1) then
  355.             if (yawScore > 100) then
  356.               yawSample = yawOpti.Update(yawOpti,yawScore/accumulator,yawSample)
  357.               if(not yawSample == nil ) then yawPID.Configure(yawPID,unpack(yawSample)) end
  358.             end
  359.             if(pitchScore>100) then
  360.               pitchSample = pitchOpti.Update(pitchOpti,pitchScore/accumulator,pitchSample)
  361.               if(not pithSample == nil ) then pitchPID.Configure(pitchPID,unpack(pitchSample)) end
  362.             end
  363.             if(rollScore>100) then
  364.                rollSample = rollOpti.Update(rollOpti,rollScore/accumulator,rollSample)
  365.               if(not rollSample == nil ) then rollPID.Configure(rollPID,unpack(rollSample)) end
  366.             end
  367.         else
  368.             yawPID.Configure(yawPID,Kp,Ki,Kd)
  369.             pitchPID.Configure(pitchPID,Kp,Ki,Kd)
  370.             rollPID.Configure(rollPID,Kp,Ki,Kd)
  371.         end
  372.        
  373.         pitchScore=0
  374.         rollScore=0
  375.         yawScore = 0
  376.         accumulator = 0
  377.     end
  378.    
  379.    
  380.     CYaw =  yawPID.Update(yawPID,eYaw)
  381.     CPitch =  pitchPID.Update(pitchPID,ePitch)
  382.     CRoll =  rollPID.Update(rollPID,eRoll)
  383.    
  384.     --CYaw = 0.63661977*Mathf.Atan(CYaw*10)
  385.     --CPitch = 0.63661977*Mathf.Atan(CPitch*10)
  386.     --CRoll = 0.63661977*Mathf.Atan(CRoll*10)
  387.    
  388.     if ( CYaw < 0 ) then
  389.             --turn left
  390.             I:RequestControl(2,0,-CYaw)
  391.             logstr = logstr .. " left " .. -CYaw
  392.     else
  393.             --turn right
  394.             I:RequestControl(2,1,CYaw)
  395.             logstr = logstr .. " right " ..CYaw
  396.     end
  397.     if ( CPitch < 0 ) then
  398.             --pitch down
  399.             I:RequestControl(2,5,-CPitch)
  400.             logstr = logstr .. " down " .. -CPitch
  401.     else
  402.             --pitch up
  403.             I:RequestControl(2,4,CPitch)
  404.             logstr = logstr .. " up " .. CPitch
  405.     end    
  406.            
  407.     if ( CRoll < 0 ) then
  408.             -- roll left
  409.             logstr = logstr .. " rccw " .. -CRoll
  410.             I:RequestControl(2,2,-CRoll)
  411.     else
  412.             -- roll right  
  413.             I:RequestControl(2,3,CRoll)
  414.             logstr = logstr .. " rcw " .. CRoll
  415.  
  416.     end
  417.    
  418. end
  419.  
  420.  
  421. GAIN = 0 -- close in to target at max altitude
  422. SPLIT = 1 -- match target direction rolling down/up according to start alt
  423. ROLL = 2 -- if enemy vector are parallel, roll into tail
  424. TARGET = 3 -- aim to a shoot solution
  425. RUN = 4 -- too close and enemy not fast enough
  426. LOOP = 5 --enemy is faster loop on it's tail
  427.  
  428. attackState = GAIN
  429.  
  430. function performManeuver(I,AD,targetInfo,attackState)
  431.     P = targetInfo.AimPointPosition
  432.     V = targetInfo.Velocity
  433.     FP = P
  434.     TD = FP - I:GetConstructCenterOfMass()
  435.     com = I:GetConstructCenterOfMass()
  436.     fcom = I:GetConstructCenterOfMass() + I:GetVelocityVector()
  437.     normal =  -I:GetGravityForAltitude(I:GetConstructCenterOfMass().y)
  438.     if(attackState == GAIN) then
  439.         I:LogToHud("GAINING")
  440.         y = I:GetConstructCenterOfMass().y
  441.         if ( y < maxAlt - 50 ) then
  442.              AD = AD.normalized
  443.              AD.y = 1
  444.         else
  445.             AD.y = 0
  446.         end
  447.     end
  448.     if(attackState == RUN) then
  449.         V = targetInfo.Velocity
  450.        
  451.         I:LogToHud("RUNNING")
  452.         if (V.magnitude > I:GetVelocityMagnitude()*0.80) then
  453.             AD = V * -1
  454.         else
  455.             AD = TD * -1
  456.         end
  457.         y = I:GetConstructCenterOfMass().y
  458.         if ( y < maxAlt - 50 ) then
  459.              AD = AD.normalized
  460.              AD.y = 4
  461.         else
  462.             if ( y > (maxAlt - minAlt / 2) ) then
  463.                 AD = AD.normalized
  464.                 AD.y = -4
  465.              else
  466.                 AD.y=0
  467.              end
  468.         end
  469.     end
  470.     if(attackState == TARGET) then
  471.         I:LogToHud("TARGETING")
  472.         normal = TD.normalized
  473.         normal.y = 0.1
  474.     end
  475.     if(attackState == LOOP) then
  476.         I:LogToHud("LOOP")
  477.         AD = V * -1
  478.         normal = AD
  479.     end
  480.     -- emergency climb
  481.  
  482.     if (com.y < I:GetTerrainAltitudeForLocalPosition(com.x,0,com.z) + minAlt or fcom.y < I:GetTerrainAltitudeForLocalPosition(fcom.x,0,fcom.z) + minAlt) then
  483.       AD = Vector3(0,1,0)
  484.         I:LogToHud("EC")
  485.       normal =  -I:GetGravityForAltitude(I:GetConstructCenterOfMass().y)
  486.     end
  487.     flyAlong(I, AD, normal)
  488. end
  489.  
  490.  
  491.  
  492. function attackEnemy(I, AD, targetInfo)
  493.     P = targetInfo.AimPointPosition
  494.     V = targetInfo.Velocity
  495.     FP = P
  496.     TD = FP - I:GetConstructCenterOfMass()
  497.     distance = TD.magnitude
  498.  
  499.     if (distance > attackFromDistance) then
  500.         attackState = GAIN
  501.     else
  502.       if (distance < abortAttackDistance) then
  503.           attackState = RUN
  504.       else
  505.           if (attackState == GAIN or distance > abortRunDistance) then
  506.               attackState = TARGET
  507.           end
  508.           if (attackState == RUN and distance < abortRunDistance and Vector3.Dot(V.normalized,I:GetConstructForwardVector() )> 0.7) then
  509.              attackState = LOOP
  510.           end
  511.           if (attackState == LOOP and distance > abortAttackDistance and Vector3.Dot(V,I:GetConstructForwardVector() ) < 0) then
  512.              attackState = TARGET
  513.           end
  514.           if (attackState == LOOP and distance < abortAttackDistance and Vector3.Dot(V,I:GetConstructForwardVector() ) < 0) then
  515.              attackState = RUN
  516.           end
  517.            if (attackState == TARGET and  math.abs(Vector3.Dot(TD.normalized,I:GetConstructForwardVector() )) < 0.2 and distance < abortRunDistance) then
  518.              attackState = RUN
  519.           end
  520.       end
  521.     end
  522.  
  523.     performManeuver(I,AD,targetInfo,attackState)
  524.  
  525. end
  526.  
  527. function Update(I)
  528.    
  529.     targetInfo = I:GetTargetInfo(controllingMainframe, 0)
  530.     AttackDirection = nil
  531.     if(targetInfo.Valid) then
  532.         for weaponIndex=0,I:GetWeaponCount() do
  533.           weaponInfo = I:GetWeaponInfo(weaponIndex)
  534.      
  535.           if ((weaponInfo.WeaponType == 4 or weaponInfo.WeaponType == 0 )
  536.                and weaponInfo.Valid and weaponInfo.PlayerCurrentlyControllingIt == false
  537.                and weaponInfo.WeaponSlot == restrictSlot) then
  538.                AttackDirection = killThatThing(I, weaponInfo, targetInfo, weaponIndex, -1)
  539.           end
  540.         end
  541.        
  542.         for turretSpinnerIndex=0,I:GetTurretSpinnerCount() do
  543.             for weaponIndex=0,I:GetWeaponCountOnTurretOrSpinner(turretSpinnerIndex) do
  544.               weaponInfo = I:GetWeaponInfoOnTurretOrSpinner(turretSpinnerIndex, weaponIndex)
  545.                 if ((weaponInfo.WeaponType == 4 or weaponInfo.WeaponType == 0 )
  546.                 and weaponInfo.Valid and weaponInfo.PlayerCurrentlyControllingIt == false
  547.                 and weaponInfo.WeaponSlot == restrictSlot ) then
  548.                 AttackDirection = killThatThing(I, weaponInfo, targetInfo, weaponIndex, turretSpinnerIndex)
  549.                 end
  550.             end
  551.         end
  552.         if (AttackDirection == nil) then
  553.             P = targetInfo.AimPointPosition
  554.             V = targetInfo.Velocity
  555.        
  556.             FP = P + V
  557.        
  558.          
  559.               I:LogToHud("No weapon configured!!")
  560.             AttackDirection = FP - I:GetConstructCenterOfMass()
  561.         end
  562.         attackEnemy(I, AttackDirection, targetInfo)
  563.     else
  564.    
  565.  
  566.        I:LogToHud("IDLING")
  567.    
  568.     end
  569.        
  570. end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement