local socket		= require "socket"
local maf			= require "maf"
local vec3, quat	= maf.vec3, maf.quat
local samem			= require "SAMemory"

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

-- Constants for controlling plane steering behavior
local PITCH_UP_THRESHOLD	= -15		-- Pitch angle threshold in degrees
local FINE_STEERING_ANGLE	= 15		-- Angle for fine-steering adjustments in degrees
local YAW_FACTOR			= 1			-- Multiplicative factor for yaw adjustment
local ROLL_FACTOR			= 1			-- Multiplicative factor for roll adjustment
local STEERING_SPEED 		= 100.0		-- Speed of steering control in units per second

-- Direction vectors
local DIRECTION = {
	FORWARD		= vec3(0, 1, 0),
	BACKWARD	= vec3(0, -1, 0),
	RIGHT		= vec3(1, 0, 0),
	LEFT		= vec3(-1, 0, 0),
	UP			= vec3(0, 0, 1),
	DOWN		= vec3(0, 0, -1)
}

-- Calculate the signed angle between two vectors with respect to a reference axis
local function calculateSignedAngle(from, to, axis)
	local fromNormalized, toNormalized = from:clone():normalize(), to:clone():normalize()
	local dotProduct = fromNormalized:dot(toNormalized)
	local angle = math.acos(math.max(-1, math.min(1, dotProduct)))	-- Clamp the dot product
	local crossProduct = fromNormalized:cross(toNormalized)
	local sign = crossProduct:dot(axis) < 0 and -1 or 1
	return angle * sign
end

-- Calculate the angle between two vectors in degrees
local function calculateAngleBetweenVectors(v1, v2)
	local normV1, normV2 = v1:normalize(), v2:normalize()
	local dotProduct = math.max(-1, math.min(1, normV1:dot(normV2)))	-- Clamp the dot product
	return math.deg(math.acos(dotProduct))
end

-- Obtain the conjugate of a quaternion
local function conjugateQuaternion(q)
	return quat(-q.x, -q.y, -q.z, q.w)
end

-- Smooth step function
local function smoothstep(edge0, edge1, x)
	x = math.max(0, math.min(1, (x - edge0) / (edge1 - edge0)))
	return x * x * (3 - 2 * x)
end

-- Compute the steering input for the plane to move towards a target position
local function computeSteeringInput(plane, dt, targetPosition)
	local planePosition = vec3(getCarCoordinates(plane))
	local planeRotation = quat(getVehicleQuaternion(plane))

	local positionError = targetPosition - planePosition
	local distanceError = positionError:length()
	local localError = conjugateQuaternion(planeRotation) * positionError

	local errorDir = localError:normalize()
	local pitchError, rollError, yawError
	pitchError = vec3(0, localError.y, localError.z):normalize()
	rollError = vec3(localError.x, localError.y, 0):normalize()
	yawError = vec3(localError.x, 0, localError.z):normalize()

	local targetInput = vec3()
	local pitch = calculateSignedAngle(DIRECTION.FORWARD, pitchError, DIRECTION.RIGHT)

	if -pitch < PITCH_UP_THRESHOLD then
		pitch = (pitch + 360) % 360
	end
	targetInput.x = pitch

	local angleError = calculateAngleBetweenVectors(DIRECTION.FORWARD, rollError)
	local minAngle, maxAngle = -5, FINE_STEERING_ANGLE

	local angleError = calculateAngleBetweenVectors(DIRECTION.FORWARD, rollError)
	local dynamicYawFactor = smoothstep(minAngle, maxAngle, angleError) * YAW_FACTOR

	if angleError < FINE_STEERING_ANGLE then
		local yaw = -calculateSignedAngle(DIRECTION.FORWARD, yawError, DIRECTION.UP)
		targetInput.y = yaw * dynamicYawFactor
	else
		local roll = calculateSignedAngle(DIRECTION.UP, rollError, DIRECTION.FORWARD)
		targetInput.z = roll * ROLL_FACTOR
	end

	local function clampInput(value)
		return math.max(-1, math.min(1, value))
	end

	targetInput.x, targetInput.y, targetInput.z = clampInput(targetInput.x), clampInput(targetInput.y), clampInput(targetInput.z)

	local input = vec3()
	input.x = clampInput(input.x + (targetInput.x - input.x) * STEERING_SPEED * dt)
	input.y = clampInput(input.y + (targetInput.y - input.y) * STEERING_SPEED * dt)
	input.z = clampInput(input.z + (targetInput.z - input.z) * STEERING_SPEED * dt)

	return input
end

function main()
	local lastTime = socket.gettime()

	while true do
		wait(0)

		local currentTime = socket.gettime()
		local dt = currentTime - lastTime
		lastTime = currentTime

		local plane = storeCarCharIsInNoSave(playerPed)
		local planePosition = vec3(getCarCoordinates(plane))
		local camPosition = vec3(getActiveCameraCoordinates())
		camPosition.z = camPosition.z - 0.1
		local camDirection = (vec3(getActiveCameraPointAt()) - camPosition):normalize() * 10
		local targetPosition = planePosition + camDirection

		local wposX, wposY = convert3DCoordsToScreen(targetPosition:unpack())
		dxDrawRing(wposX, wposY - 65, 15, 0.75, 0, 1, 285212671, false, 1)

		local pitchInput, yawInput, rollInput = computeSteeringInput(plane, dt * 10, targetPosition):unpack()

		if doesVehicleExist(plane) then
			local carPointer = getCarPointer(plane)
			local vehicle = samem.cast("CVehicle *", carPointer)

			if vehicle ~= samem.nullptr and vehicle.nVehicleClass == 0 then
				local plane = samem.cast('CPlane *', vehicle)
				plane.field_988 = yawInput
				plane.field_98C = pitchInput
				plane.field_990 = rollInput
				printStringNow(yawInput, 1)
			end
		end
	end
end

function math.sign(x)
	if x > 0 then
		return 1
	elseif x < 0 then
		return -1
	else
		return 0
	end
end

function dxDrawRing(posX, posY, radius, width, startAngle, amount, color, absoluteAmount, anglesPerLine)
	if type(posX) ~= "number" or type(posY) ~= "number" or type(startAngle) ~= "number" or type(amount) ~= "number" then
		print("Error: One of the required parameters (posX, posY, startAngle, amount) is not a number.")
		return false
	end

	radius = type(radius) == "number" and radius or 50
	width = type(width) == "number" and width or 5
	color = color or 0xFFFFFFFF
	absoluteAmount = type(absoluteAmount) == "boolean" and absoluteAmount or false
	anglesPerLine = type(anglesPerLine) == "number" and anglesPerLine or 1

	local stopAngle = absoluteAmount and (amount + startAngle) or ((amount * 360) + startAngle)

	for angle = startAngle, stopAngle, anglesPerLine do
		local radAngle = math.rad(angle)
		local startX = math.cos(radAngle) * (radius - width)
		local startY = math.sin(radAngle) * (radius - width)
		local endX = math.cos(radAngle) * (radius + width)
		local endY = math.sin(radAngle) * (radius + width)

		renderDrawLine(startX + posX, startY + posY, endX + posX, endY + posY, width, color)
	end

	return math.floor((stopAngle - startAngle) / anglesPerLine)
end


поменяй порядок функций чтобы он был более удобным и логичным