--]]

-- Техническое задание для функции calculateSteeringInputs
-- Цель функции:
-- Функция предназначена для вычисления управляющих воздействий (m_fLeftRightSkid, m_fSteeringUpDown, m_fSteeringLeftRight) для управления самолетом, чтобы направить его к заданной цельной позиции в пространстве. Управление должно быть ограничено диапазоном значений от -255 до 255 для каждой оси.
-- 
-- Входные параметры:
-- currentPosition: Текущая позиция самолета в пространстве (vec3), характеризующаяся координатами (x, y, z).
-- currentRotation: Текущее вращение самолета, представленное в виде кватерниона (quat).
-- targetPosition: Целевая позиция в пространстве (vec3), к которой должен направляться самолет.
-- Выходные данные:
-- Вектор воздействий (vec3), содержащий три компонента, которые представляют собой величины управления по оси скольжения, тангажа и крена, соответственно.


local maf = require 'maf'
local vec3 = maf.vec3
local quat = maf.quat

local samem = require 'SAMemory'

samem.require 'CVehicle'
samem.require 'tHandlingData'
samem.require 'CPlane'
samem.require 'CTransmission'


local function calculateSteeringInputs(currentPosition, currentRotation, targetPosition)

    return pitchInput, yawInput, rollInput
end

-- Основная функция Lua
function main()
    -- Ожидание, пока игрок не начнет игру
    while not isPlayerPlaying(PLAYER_HANDLE) do wait(100) end
    wait(100)

    -- Основной игровой цикл
    while true do wait(0)
        
        if isCharInAnyCar(playerPed) then
            -- Получение объекта транспортного средства
            local car = storeCarCharIsInNoSave(playerPed)

            -- Получение ориентации и позиции транспортного средства
            local rotX, rotY, rotZ, rotW = getVehicleQuaternion(car)
            local positionX, positionY, positionZ = getCarCoordinates(car)

            -- Расчет целевой позиции, основываясь на центре экрана (камера)
            local resX, resY = getScreenResolution()
            local cameraX, cameraY, cameraZ = convertScreenCoordsToWorld3D(resX / 2, resY / 2, 100)

            -- Создание векторов для текущей и целевой позиции
            local currentPosition = vec3(positionX, positionY, positionZ)
            local currentRotation = quat(rotX, rotY, rotZ, rotW)
            local targetPosition = vec3(cameraX, cameraY, cameraZ)

            -- Вычисление управляющих воздействий для управления самолётом
            local pitchInput, yawInput, rollInput = calculateSteeringInputs(currentPosition, currentRotation, targetPosition)
            print("Steering Inputs: ", pitchInput, yawInput, rollInput)

            -- Проверка, существует ли транспортное средство
            if doesVehicleExist(car) then
                local carPointer = getCarPointer(car)
                local vehicle = samem.cast("CVehicle *", carPointer)
                
                -- Регистрация управления самолётом, если это самолёт
                if vehicle ~= samem.nullptr and vehicle.nVehicleClass == 0 then -- VEHICLE_TYPE_AUTOMOBILE = 0
                    local plane = samem.cast('CPlane *', vehicle)
                    plane.field_988 = 0
                    plane.field_98C = pitchInput * 1065353213
                    plane.field_990 = rollInput * 1065353213

                    print('UpDown Input: ', plane.field_98C / 1065353213)
                    print('LeftRight Input: ', plane.field_990 / 1065353213)
                end
            end
        end
    end
end



используя вышеуказанное тз и библиотеку маф

-- maf
-- https://github.com/bjornbytes/maf
-- MIT License

local ffi = type(jit) == 'table' and jit.status() and require 'ffi'
local vec3, quat

local forward
local vtmp1
local vtmp2
local qtmp1

vec3 = {
  __call = function(_, x, y, z)
    return setmetatable({ x = x or 0, y = y or 0, z = z or 0 }, vec3)
  end,

  __tostring = function(v)
    return string.format('(%f, %f, %f)', v.x, v.y, v.z)
  end,

  __add = function(v, u) return v:add(u, vec3()) end,
  __sub = function(v, u) return v:sub(u, vec3()) end,
  __mul = function(v, u)
    if vec3.isvec3(u) then return v:mul(u, vec3())
    elseif type(u) == 'number' then return v:scale(u, vec3())
    else error('vec3s can only be multiplied by vec3s and numbers') end
  end,
  __div = function(v, u)
    if vec3.isvec3(u) then return v:div(u, vec3())
    elseif type(u) == 'number' then return v:scale(1 / u, vec3())
    else error('vec3s can only be divided by vec3s and numbers') end
  end,
  __unm = function(v) return v:scale(-1) end,
  __len = function(v) return v:length() end,

  __index = {
    isvec3 = function(x)
      return ffi and ffi.istype('vec3', x) or getmetatable(x) == vec3
    end,

    clone = function(v)
      return vec3(v.x, v.y, v.z)
    end,

    unpack = function(v)
      return v.x, v.y, v.z
    end,

    set = function(v, x, y, z)
      if vec3.isvec3(x) then x, y, z = x.x, x.y, x.z end
      v.x = x
      v.y = y
      v.z = z
      return v
    end,

    add = function(v, u, out)
      out = out or v
      out.x = v.x + u.x
      out.y = v.y + u.y
      out.z = v.z + u.z
      return out
    end,

    sub = function(v, u, out)
      out = out or v
      out.x = v.x - u.x
      out.y = v.y - u.y
      out.z = v.z - u.z
      return out
    end,

    mul = function(v, u, out)
      out = out or v
      out.x = v.x * u.x
      out.y = v.y * u.y
      out.z = v.z * u.z
      return out
    end,

    div = function(v, u, out)
      out = out or v
      out.x = v.x / u.x
      out.y = v.y / u.y
      out.z = v.z / u.z
      return out
    end,

    scale = function(v, s, out)
      out = out or v
      out.x = v.x * s
      out.y = v.y * s
      out.z = v.z * s
      return out
    end,

    length = function(v)
      return math.sqrt(v.x * v.x + v.y * v.y + v.z * v.z)
    end,

    normalize = function(v, out)
      out = out or v
      local len = v:length()
      return len == 0 and v or v:scale(1 / len, out)
    end,

    distance = function(v, u)
      return vec3.sub(v, u, vtmp1):length()
    end,

    angle = function(v, u)
      return math.acos(v:dot(u) / (v:length() + u:length()))
    end,

    dot = function(v, u)
      return v.x * u.x + v.y * u.y + v.z * u.z
    end,

    cross = function(v, u, out)
      out = out or v
      local a, b, c = v.x, v.y, v.z
      out.x = b * u.z - c * u.y
      out.y = c * u.x - a * u.z
      out.z = a * u.y - b * u.x
      return out
    end,

    lerp = function(v, u, t, out)
      out = out or v
      out.x = v.x + (u.x - v.x) * t
      out.y = v.y + (u.y - v.y) * t
      out.z = v.z + (u.z - v.z) * t
      return out
    end,

    project = function(v, u, out)
      out = out or v
      local unorm = vtmp1
      u:normalize(unorm)
      local dot = v:dot(unorm)
      out.x = unorm.x * dot
      out.y = unorm.y * dot
      out.z = unorm.z * dot
      return out
    end,

    rotate = function(v, q, out)
      out = out or v
      local u, c, o = vtmp1, vtmp2, out
      u.x, u.y, u.z = q.x, q.y, q.z
      o.x, o.y, o.z = v.x, v.y, v.z
      u:cross(v, c)
      local uu = u:dot(u)
      local uv = u:dot(v)
      o:scale(q.w * q.w - uu)
      u:scale(2 * uv)
      c:scale(2 * q.w)
      return o:add(u:add(c))
    end
  }
}

quat = {
  __call = function(_, x, y, z, w)
    return setmetatable({ x = x, y = y, z = z, w = w }, quat)
  end,

  __tostring = function(q)
    return string.format('(%f, %f, %f, %f)', q.x, q.y, q.z, q.w)
  end,

  __add = function(q, r) return q:add(r, quat()) end,
  __sub = function(q, r) return q:sub(r, quat()) end,
  __mul = function(q, r)
    if quat.isquat(r) then return q:mul(r, quat())
    elseif vec3.isvec3(r) then return r:rotate(q, vec3())
    else error('quats can only be multiplied by quats and vec3s') end
  end,
  __unm = function(q) return q:scale(-1) end,
  __len = function(q) return q:length() end,

  __index = {
    isquat = function(x)
      return ffi and ffi.istype('quat', x) or getmetatable(x) == quat
    end,

    clone = function(q)
      return quat(q.x, q.y, q.z, q.w)
    end,

    unpack = function(q)
      return q.x, q.y, q.z, q.w
    end,

    set = function(q, x, y, z, w)
      if quat.isquat(x) then x, y, z, w = x.x, x.y, x.z, x.w end
      q.x = x
      q.y = y
      q.z = z
      q.w = w
      return q
    end,

    fromAngleAxis = function(angle, x, y, z)
      return quat():setAngleAxis(angle, x, y, z)
    end,

    setAngleAxis = function(q, angle, x, y, z)
      if vec3.isvec3(x) then x, y, z = x.x, x.y, x.z end
      local s = math.sin(angle * .5)
      local c = math.cos(angle * .5)
      q.x = x * s
      q.y = y * s
      q.z = z * s
      q.w = c
      return q
    end,

    getAngleAxis = function(q)
      if q.w > 1 or q.w < -1 then q:normalize() end
      local s = math.sqrt(1 - q.w * q.w)
      s = s < .0001 and 1 or 1 / s
      return 2 * math.acos(q.w), q.x * s, q.y * s, q.z * s
    end,

    between = function(u, v)
      return quat():setBetween(u, v)
    end,

    setBetween = function(q, u, v)
      local dot = u:dot(v)
      if dot > .99999 then
        q.x, q.y, q.z, q.w = 0, 0, 0, 1
        return q
      elseif dot < -.99999 then
        vtmp1.x, vtmp1.y, vtmp1.z = 1, 0, 0
        vtmp1:cross(u)
        if #vtmp1 < .00001 then
          vtmp1.x, vtmp1.y, vtmp1.z = 0, 1, 0
          vtmp1:cross(u)
        end
        vtmp1:normalize()
        return q:setAngleAxis(math.pi, vtmp1)
      end

      q.x, q.y, q.z = u.x, u.y, u.z
      vec3.cross(q, v)
      q.w = 1 + dot
      return q:normalize()
    end,

    fromDirection = function(x, y, z)
      return quat():setDirection(x, y, z)
    end,

    setDirection = function(q, x, y, z)
      if vec3.isvec3(x) then x, y, z = x.x, x.y, x.z end
      vtmp2.x, vtmp2.y, vtmp2.z = x, y, z
      return q:setBetween(forward, vtmp2)
    end,

    add = function(q, r, out)
      out = out or q
      out.x = q.x + r.x
      out.y = q.y + r.y
      out.z = q.z + r.z
      out.w = q.w + r.w
      return out
    end,

    sub = function(q, r, out)
      out = out or q
      out.x = q.x - r.x
      out.y = q.y - r.y
      out.z = q.z - r.z
      out.w = q.w - r.w
      return out
    end,

    mul = function(q, r, out)
      out = out or q
      local qx, qy, qz, qw = q:unpack()
      local rx, ry, rz, rw = r:unpack()
      out.x = qx * rw + qw * rx + qy * rz - qz * ry
      out.y = qy * rw + qw * ry + qz * rx - qx * rz
      out.z = qz * rw + qw * rz + qx * ry - qy * rx
      out.w = qw * rw - qx * rx - qy * ry - qz * rz
      return out
    end,

    scale = function(q, s, out)
      out = out or q
      out.x = q.x * s
      out.y = q.y * s
      out.z = q.z * s
      out.w = q.w * s
      return out
    end,

    length = function(q)
      return math.sqrt(q.x * q.x + q.y * q.y + q.z * q.z + q.w * q.w)
    end,

    normalize = function(q, out)
      out = out or q
      local len = q:length()
      return len == 0 and q or q:scale(1 / len, out)
    end,

    lerp = function(q, r, t, out)
      out = out or q
      r:scale(t, qtmp1)
      q:scale(1 - t, out)
      return out:add(qtmp1)
    end,

    slerp = function(q, r, t, out)
      out = out or q

      local dot = q.x * r.x + q.y * r.y + q.z * r.z + q.w * r.w
      if dot < 0 then
        dot = -dot
        r:scale(-1)
      end

      if 1 - dot < .0001 then
        return q:lerp(r, t, out)
      end

      local theta = math.acos(dot)
      q:scale(math.sin((1 - t) * theta), out)
      r:scale(math.sin(t * theta), qtmp1)
      return out:add(qtmp1):scale(1 / math.sin(theta))
    end
  }
}

if ffi then
  ffi.cdef [[
    typedef struct { double x, y, z; } vec3;
    typedef struct { double x, y, z, w; } quat;
  ]]

  vec3 = ffi.metatype('vec3', vec3)
  quat = ffi.metatype('quat', quat)
else
  setmetatable(vec3, vec3)
  setmetatable(quat, quat)
end

forward = vec3(0, 0, -1)
vtmp1 = vec3()
vtmp2 = vec3()
qtmp1 = quat()

return {
  vec3 = vec3,
  quat = quat,

  vector = vec3,
  rotation = quat
}

напиши функцию для моего кода, используя этот код

using System;
using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using UnityEngine.Windows;

public class AIController : MonoBehaviour {
    [SerializeField]
    Plane plane;
    [SerializeField]
    float steeringSpeed;
    [SerializeField]
    float minSpeed;
    [SerializeField]
    float maxSpeed;
    [SerializeField]
    float recoverSpeedMin;
    [SerializeField]
    float recoverSpeedMax;
    [SerializeField]
    LayerMask groundCollisionMask;
    [SerializeField]
    float groundCollisionDistance;
    [SerializeField]
    float groundAvoidanceAngle;
    [SerializeField]
    float groundAvoidanceMinSpeed;
    [SerializeField]
    float groundAvoidanceMaxSpeed;
    [SerializeField]
    float pitchUpThreshold;
    [SerializeField]
    float fineSteeringAngle;
    [SerializeField]
    float rollFactor;
    [SerializeField]
    float yawFactor;
    [SerializeField]
    bool canUseMissiles;
    [SerializeField]
    bool canUseCannon;
    [SerializeField]
    float missileLockFiringDelay;
    [SerializeField]
    float missileFiringCooldown;
    [SerializeField]
    float missileMinRange;
    [SerializeField]
    float missileMaxRange;
    [SerializeField]
    float missileMaxFireAngle;
    [SerializeField]
    float bulletSpeed;
    [SerializeField]
    float cannonRange;
    [SerializeField]
    float cannonMaxFireAngle;
    [SerializeField]
    float cannonBurstLength;
    [SerializeField]
    float cannonBurstCooldown;
    [SerializeField]
    float minMissileDodgeDistance;
    [SerializeField]
    float reactionDelayMin;
    [SerializeField]
    float reactionDelayMax;
    [SerializeField]
    float reactionDelayDistance;

    Target selfTarget;
    Plane targetPlane;
    Vector3 lastInput;
    bool isRecoveringSpeed;

    float missileDelayTimer;
    float missileCooldownTimer;

    bool cannonFiring;
    float cannonBurstTimer;
    float cannonCooldownTimer;

    struct ControlInput {
        public float time;
        public Vector3 targetPosition;
    }

    Queue<ControlInput> inputQueue;

    bool dodging;
    Vector3 lastDodgePoint;
    List<Vector3> dodgeOffsets;
    const float dodgeUpdateInterval = 0.25f;
    float dodgeTimer;

    void Start() {
        selfTarget = plane.GetComponent<Target>();

        if (plane.Target != null) {
            targetPlane = plane.Target.GetComponent<Plane>();
        }

        dodgeOffsets = new List<Vector3>();
        inputQueue = new Queue<ControlInput>();
    }

    Vector3 AvoidGround() {
        //roll level and pull up
        var roll = plane.Rigidbody.rotation.eulerAngles.z;
        if (roll > 180f) roll -= 360f;
        return new Vector3(-1, 0, Mathf.Clamp(-roll * rollFactor, -1, 1));
    }

    Vector3 RecoverSpeed() {
        //roll and pitch level
        var roll = plane.Rigidbody.rotation.eulerAngles.z;
        var pitch = plane.Rigidbody.rotation.eulerAngles.x;
        if (roll > 180f) roll -= 360f;
        if (pitch > 180f) pitch -= 360f;
        return new Vector3(Mathf.Clamp(-pitch, -1, 1), 0, Mathf.Clamp(-roll * rollFactor, -1, 1));
    }

    Vector3 GetTargetPosition() {
        if (plane.Target == null) {
            return plane.Rigidbody.position;
        }

        var targetPosition = plane.Target.Position;

        if (Vector3.Distance(targetPosition, plane.Rigidbody.position) < cannonRange) {
            return Utilities.FirstOrderIntercept(plane.Rigidbody.position, plane.Rigidbody.velocity, bulletSpeed, targetPosition, plane.Target.Velocity);
        }

        return targetPosition;
    }

    Vector3 CalculateSteering(float dt, Vector3 targetPosition) {
        if (plane.Target != null && targetPlane.Health == 0) {
            return new Vector3();
        }

        var error = targetPosition - plane.Rigidbody.position;
        error = Quaternion.Inverse(plane.Rigidbody.rotation) * error;   //transform into local space

        var errorDir = error.normalized;
        var pitchError = new Vector3(0, error.y, error.z).normalized;
        var rollError = new Vector3(error.x, error.y, 0).normalized;
        var yawError = new Vector3(error.x, 0, error.z).normalized;

        var targetInput = new Vector3();

        var pitch = Vector3.SignedAngle(Vector3.forward, pitchError, Vector3.right);
        if (-pitch < pitchUpThreshold) pitch += 360f;
        targetInput.x = pitch;

        if (Vector3.Angle(Vector3.forward, errorDir) < fineSteeringAngle) {
            var yaw = Vector3.SignedAngle(Vector3.forward, yawError, Vector3.up);
            targetInput.y = yaw * yawFactor;
        } else {
            var roll = Vector3.SignedAngle(Vector3.up, rollError, Vector3.forward);
            targetInput.z = roll * rollFactor;
        }

        targetInput.x = Mathf.Clamp(targetInput.x, -1, 1);
        targetInput.y = Mathf.Clamp(targetInput.y, -1, 1);
        targetInput.z = Mathf.Clamp(targetInput.z, -1, 1);

        var input = Vector3.MoveTowards(lastInput, targetInput, steeringSpeed * dt);
        lastInput = input;

        return input;
    }

    Vector3 GetMissileDodgePosition(float dt, Missile missile) {
        dodgeTimer = Mathf.Max(0, dodgeTimer - dt);
        var missilePos = missile.Rigidbody.position;

        var dist = Mathf.Max(minMissileDodgeDistance, Vector3.Distance(missilePos, plane.Rigidbody.position));

        //calculate dodge points
        if (dodgeTimer == 0) {
            var missileForward = missile.Rigidbody.rotation * Vector3.forward;
            dodgeOffsets.Clear();

            //4 dodge points: up, down, left, right

            dodgeOffsets.Add(new Vector3(0, dist, 0));
            dodgeOffsets.Add(new Vector3(0, -dist, 0));
            dodgeOffsets.Add(Vector3.Cross(missileForward, Vector3.up) * dist);
            dodgeOffsets.Add(Vector3.Cross(missileForward, Vector3.up) * -dist);

            dodgeTimer = dodgeUpdateInterval;
        }

        //select nearest dodge point
        float min = float.PositiveInfinity;
        Vector3 minDodge = missilePos + dodgeOffsets[0];

        foreach (var offset in dodgeOffsets) {
            var dodgePosition = missilePos + offset;
            var offsetDist = Vector3.Distance(dodgePosition, lastDodgePoint);

            if (offsetDist < min) {
                minDodge = dodgePosition;
                min = offsetDist;
            }
        }

        lastDodgePoint = minDodge;
        return minDodge;
    }

    float CalculateThrottle(float minSpeed, float maxSpeed) {
        float input = 0;

        if (plane.LocalVelocity.z < minSpeed) {
            input = 1;
        } else if (plane.LocalVelocity.z > maxSpeed) {
            input = -1;
        }

        return input;
    }

    void CalculateWeapons(float dt) {
        if (plane.Target == null) return;

        if (canUseMissiles) {
            CalculateMissiles(dt);
        }

        if (canUseCannon) {
            CalculateCannon(dt);
        }
    }

    void CalculateMissiles(float dt) {
        missileDelayTimer = Mathf.Max(0, missileDelayTimer - dt);
        missileCooldownTimer = Mathf.Max(0, missileCooldownTimer - dt);

        var error = plane.Target.Position - plane.Rigidbody.position;
        var range = error.magnitude;
        var targetDir = error.normalized;
        var targetAngle = Vector3.Angle(targetDir, plane.Rigidbody.rotation * Vector3.forward);

        if (!plane.MissileLocked || !(targetAngle < missileMaxFireAngle || (180f - targetAngle) < missileMaxFireAngle)) {
            //don't fire if not locked or target is too off angle
            //can fire if angle is close to 0 (chasing) or 180 (head on)
            missileDelayTimer = missileLockFiringDelay;
        }

        if (range < missileMaxRange && range > missileMinRange && missileDelayTimer == 0 && missileCooldownTimer == 0) {
            plane.TryFireMissile();
            missileCooldownTimer = missileFiringCooldown;
        }
    }

    void CalculateCannon(float dt) {
        if (targetPlane.Health == 0) {
            cannonFiring = false;
            return;
        }

        if (cannonFiring) {
            cannonBurstTimer = Mathf.Max(0, cannonBurstTimer - dt);

            if (cannonBurstTimer == 0) {
                cannonFiring = false;
                cannonCooldownTimer = cannonBurstCooldown;
                plane.SetCannonInput(false);
            }
        } else {
            cannonCooldownTimer = Mathf.Max(0, cannonCooldownTimer - dt);

            var targetPosition = Utilities.FirstOrderIntercept(plane.Rigidbody.position, plane.Rigidbody.velocity, bulletSpeed, plane.Target.Position, plane.Target.Velocity);

            var error = targetPosition - plane.Rigidbody.position;
            var range = error.magnitude;
            var targetDir = error.normalized;
            var targetAngle = Vector3.Angle(targetDir, plane.Rigidbody.rotation * Vector3.forward);

            if (range < cannonRange && targetAngle < cannonMaxFireAngle && cannonCooldownTimer == 0) {
                cannonFiring = true;
                cannonBurstTimer = cannonBurstLength;
                plane.SetCannonInput(true);
            }
        }
    }

    void SteerToTarget(float dt, Vector3 planePosition) {
        bool foundTarget = false;
        Vector3 steering = Vector3.zero;
        Vector3 targetPosition = Vector3.zero;

        var delay = reactionDelayMax;

        if (Vector3.Distance(planePosition, plane.Rigidbody.position) < reactionDelayDistance) {
            delay = reactionDelayMin;
        }

        while (inputQueue.Count > 0) {
            var input = inputQueue.Peek();

            if (input.time + delay <= Time.time) {
                targetPosition = input.targetPosition;
                inputQueue.Dequeue();
                foundTarget = true;
            } else {
                break;
            }
        }

        if (foundTarget) {
            steering = CalculateSteering(dt, targetPosition);
        }

        plane.SetControlInput(steering);
    }

    void FixedUpdate() {
        if (plane.Dead) return;
        var dt = Time.fixedDeltaTime;

        Vector3 steering = Vector3.zero;
        float throttle;
        bool emergency = false;
        Vector3 targetPosition = plane.Target.Position;

        var velocityRot = Quaternion.LookRotation(plane.Rigidbody.velocity.normalized);
        var ray = new Ray(plane.Rigidbody.position, velocityRot * Quaternion.Euler(groundAvoidanceAngle, 0, 0) * Vector3.forward);

        if (Physics.Raycast(ray, groundCollisionDistance + plane.LocalVelocity.z, groundCollisionMask.value)) {
            steering = AvoidGround();
            throttle = CalculateThrottle(groundAvoidanceMinSpeed, groundAvoidanceMaxSpeed);
            emergency = true;
        } else {
            var incomingMissile = selfTarget.GetIncomingMissile();
            if (incomingMissile != null) {
                if (dodging == false) {
                    //start dodging
                    dodging = true;
                    lastDodgePoint = plane.Rigidbody.position;
                    dodgeTimer = 0;
                }

                var dodgePosition = GetMissileDodgePosition(dt, incomingMissile);
                steering = CalculateSteering(dt, dodgePosition);
                emergency = true;
            } else {
                dodging = false;
                targetPosition = GetTargetPosition();
            }

            if (incomingMissile == null && (plane.LocalVelocity.z < recoverSpeedMin || isRecoveringSpeed)) {
                isRecoveringSpeed = plane.LocalVelocity.z < recoverSpeedMax;

                steering = RecoverSpeed();
                throttle = 1;
                emergency = true;
            } else {
                throttle = CalculateThrottle(minSpeed, maxSpeed);
            }
        }

        inputQueue.Enqueue(new ControlInput {
            time = Time.time,
            targetPosition = targetPosition,
        });

        plane.SetThrottleInput(throttle);

        if (emergency) {
            if (isRecoveringSpeed) {
                //reduce steering strength while recovering speed
                steering.x = Mathf.Clamp(steering.x, -0.5f, 0.5f);
            }

            plane.SetControlInput(steering);
        } else {
            SteerToTarget(dt, plane.Target.Position);
        }

        CalculateWeapons(dt);
    }
}