script_name('testoviy')

local memory = require 'memory'
local samem = require 'SAMemory'
local inspect = require 'inspect'

samem.require 'CVehicle'
samem.require 'tHandlingData'
samem.require 'CAutomobile'

	local speedThreshold		= 0.02 -- 0.02
	local rollOnToWheelsForce	= 0.0035
	
	local CAR_WHEEL_FRONT_LEFT  = 0
    local CAR_WHEEL_REAR_LEFT   = 1
    local CAR_WHEEL_FRONT_RIGHT = 2
    local CAR_WHEEL_REAR_RIGHT  = 3
	
	local UP		= 0
	local DOWN		= 1
	local LEFT		= 2
	local RIGHT		= 3
	local FORWARD	= 4	
	local BACK		= 5
	
	local dirVecs = {
	 [0] = { x = 0.0,  y = 0.0,  z = 1.0  }, -- UP
	 [1] = { x = 0.0,  y = 0.0,  z = -1.0 }, -- DOWN
	 [2] = { x = -1.0, y = 0.0,  z = 0.0  }, -- LEFT
	 [3] = { x = 1.0,  y = 0.0,  z = 0.0  }, -- RIGHT
	 [4] = { x = 0.0,  y = 1.0,  z = 0.0  }, -- FORWARD
	 [5] = { x = 0.0,  y = -1.0, z = 0.0  }	 -- BACK
	}

-- --- Main
-- function main()
	-- while not isPlayerPlaying(PLAYER_HANDLE) do wait(100) end
	-- wait(1000)

	-- while true do wait(0)
		-- local car = storeCarCharIsInNoSave(playerPed)
		-- print("Dot Product:", getCarForwardSpeed(car))	
		-- getCarSuspension(car)
		-- getCarMass(car)
	-- end
-- end


function main()
	while not isPlayerPlaying(PLAYER_HANDLE) do wait(100) end
	wait(1000)

	while true do wait(0)
		if isCharInAnyCar(playerPed) then
			local car = storeCarCharIsInNoSave(playerPed)
			if doesVehicleExist(car) then
			
				local forwardSpeed						= getCarForwardSpeed(car)
				local m_fWheelsSuspensionCompression	= getCarSuspension(car)
				local m_fMass, m_fTurnMass				= getCarMass(car)
				local x1, y1, z1, x2, y2, z2			= getModelDimensions(getCarModel(car))
				local m_vecMax_x						= math.max(x1, x2)
				
				--if forwardSpeed < speedThreshold then
				if forwardSpeed > speedThreshold then
					local fDir = 0.0
					local leftSuspended = m_fWheelsSuspensionCompression[CAR_WHEEL_FRONT_LEFT] == 1.0 and
										  m_fWheelsSuspensionCompression[CAR_WHEEL_REAR_LEFT] == 1.0 and
										  (m_fWheelsSuspensionCompression[CAR_WHEEL_FRONT_RIGHT] < 1.0 or 
										  m_fWheelsSuspensionCompression[CAR_WHEEL_REAR_RIGHT] < 1.0)
										  
					local rightSuspended = m_fWheelsSuspensionCompression[CAR_WHEEL_FRONT_RIGHT] == 1.0 and
										   m_fWheelsSuspensionCompression[CAR_WHEEL_REAR_RIGHT]== 1.0 and
										   (m_fWheelsSuspensionCompression[CAR_WHEEL_FRONT_LEFT] < 1.0 or 
										   m_fWheelsSuspensionCompression[CAR_WHEEL_REAR_LEFT] < 1.0)

					if leftSuspended then
						fDir = 1.0
					elseif rightSuspended then
						fDir = -1.0
					end

					if fDir ~= 0.0 then
						local right = cross(getCarSide(car, FORWARD), dirVecs[UP])
						
						if math.abs(dot(right, getCarSide(car, RIGHT))) < 0.6 then
						
							--local colModel = GetVehicleModelInfo():GetColModel()
							
							local force = multiply(getCarSide(car, UP), fDir * rollOnToWheelsForce * m_fTurnMass)
							
							local point = multiply(getCarSide(car, RIGHT), m_vecMax_x)
							
							--print(inspect(force, point))
							--print(inspect(multiply(negative(right), rollOnToWheelsForce * m_fMass * fDir)))
							applyTurnForce(car, force, point)
							applyMoveForce(car, multiply(multiply(multiply(negative(right), rollOnToWheelsForce), m_fMass), fDir))
							-- лол тут не используется эта функция, нахуя я ее сюда перенес applyForce(car, vecForce, point, bUpdateTurnSpeed)
							--ApplyMoveForce(-right * rollOnToWheelsForce * m_fMass * fDir)
							--applyForceToCar(car, point.x, point.y, point.z, force.x, force.y, force.z)
							--renderDrawBox(500, 200, 500, 50, 0xFFD00000)
						end
					end
				end
		
			end
		end
	end
end

-- не был уверен что getCarSpeed выдает скорость относительно оси Y
-- поэтому использовал такой костыль с получением ее из вектора
function getCarForwardSpeed(car)
    local moveSpeed = { x = 0.0, y = 0.0, z = 0.0 }
    moveSpeed.x, moveSpeed.y, moveSpeed.z = getCarSpeedVector(car)

	-- поворот вектора 'вперед' на кватернион авто
	forwardDirection = getCarSide(car, FORWARD)

	-- расчет скалярного произведения векторов ( вектора скорости авто и вектора ранее повернутого )
    return dot(moveSpeed, forwardDirection)
end

function getCarSide(car, side)   
    local baseVector = dirVecs[side]
    
    local qx, qy, qz, qw = getVehicleQuaternion(car)
    local rotatedVector = rotateVectorByQuaternion(baseVector, qx, qy, qz, qw)
    
    return rotatedVector
end


function getCarSuspension(car)
    if doesVehicleExist(car) then
		local carPointer = getCarPointer(car)
		local vehicle = samem.cast("CVehicle *", carPointer)
		if vehicle ~= samem.nullptr then
			if vehicle.nVehicleClass == 0 then -- на самом деле это не класс, а тип. VEHICLE_TYPE_AUTOMOBILE  = 0
				local automobile = samem.cast('CAutomobile *', vehicle)
				local wheelsSuspensionCompression = automobile.wheelsDistancesToGround1
				--print(automobile.vLastCollisionPosn.x, automobile.vLastCollisionPosn.y, automobile.vLastCollisionPosn.z )
				--automobile.nPhysicalFlags.bSubmergedInWater = 1
				-- print(
					-- wheelsSuspensionCompression[1],
					-- wheelsSuspensionCompression[2],
					-- wheelsSuspensionCompression[3],
					-- wheelsSuspensionCompression[4]
				-- )
				
				return wheelsSuspensionCompression
			end
		end
	end
end

function getCarMass(car)
    if doesVehicleExist(car) then
		value = getCarModel(car)
		value = memory.getint32(value * 0x4 + 0xA9B0C8, false)
		value = memory.getint16(value + 0x4A, false)
		
		local mass = memory.getfloat(value * 0xE0 + 0xC2B9DC + 0x4, false)
		local turnMass = memory.getfloat(value * 0xE0 + 0xC2B9DC + 0xC, false)
		
		--print (mass, turnMass)
		return mass, turnMass
	end
end

-- Dot product function
function dot(v, u)
  return v.x * u.x + v.y * u.y + v.z * u.z
end

-- Cross product function
function cross(v, u)
  return {
    x = v.y * u.z - v.z * u.y,
    y = v.z * u.x - v.x * u.z,
    z = v.x * u.y - v.y * u.x
  }
end

function rotateVectorByQuaternion(vector, qx, qy, qz, qw)
    local ix = qw * vector.x + qy * vector.z - qz * vector.y
    local iy = qw * vector.y + qz * vector.x - qx * vector.z
    local iz = qw * vector.z + qx * vector.y - qy * vector.x
    local iw = -qx * vector.x - qy * vector.y - qz * vector.z

    return {
        x = ix * qw + iw * -qx + iy * -qz - iz * -qy,
        y = iy * qw + iw * -qy + iz * -qx - ix * -qz,
        z = iz * qw + iw * -qz + ix * -qy - iy * -qx
    }
end

function multiply(vector, scalar)
    return {
        x = vector.x * scalar,
        y = vector.y * scalar,
        z = vector.z * scalar
    }
end

function negative(vector)
    return {
        x = vector.x * -1,
        y = vector.y * -1,
        z = vector.z * -1
    }
end

function subtract(v1, v2)
    return {
        x = v1.x - v2.x,
        y = v1.y - v2.y,
        z = v1.z - v2.z
    }
end

function add(v1, v2)
    return {
        x = v1.x + v2.x,
        y = v1.y + v2.y,
        z = v1.z + v2.z
    }
end

function divide(vector, scalar)
    if scalar ~= 0 then
		return {
			x = vector.x / scalar,
			y = vector.y / scalar,
			z = vector.z / scalar
		}
	end
end


-- Define function for applying turn force
function applyTurnForce(car, force, point)
    if doesVehicleExist(car) then
		local carPointer = getCarPointer(car)
		local vehicle = samem.cast("CVehicle *", carPointer)
		if vehicle ~= samem.nullptr then
			if vehicle.nVehicleClass == 0 then -- на самом деле это не класс, а тип. VEHICLE_TYPE_AUTOMOBILE  = 0
				local automobile = samem.cast('CAutomobile *', vehicle)
				local qx, qy, qz, qw = getVehicleQuaternion(car)
				--local wheelsSuspensionCompression = automobile.wheelsDistancesToGround1
				--print(automobile.vLastCollisionPosn.x, automobile.vLastCollisionPosn.y, automobile.vLastCollisionPosn.z )
				--automobile.nPhysicalFlags.bSubmergedInWater = 1

				if not (automobile.nPhysicalFlags.bDisableTurnForce == 1) then

					local vecCentreOfMassMultiplied = {}
					if not (automobile.nPhysicalFlags.bInfiniteMass == 1) then

						vecCentreOfMassMultiplied = rotateVectorByQuaternion(automobile.vCentreOfMass, qx, qy, qz, qw) --getMatrix(physical):transformVector(physical.m_vecCentreOfMass)
					end

					if (automobile.nPhysicalFlags.bDisableMoveForce == 1) then

						point.z = 0.0
						force.z = 0.0
					end

					local vecDifference = subtract(point, vecCentreOfMassMultiplied)
					--print(automobile.vTurnSpeed.x, automobile.vTurnSpeed.y, automobile.vTurnSpeed.z)
					-- неправильный рассчет вроде как не тот порядок automobile.vTurnSpeed = divide(add(automobile.vTurnSpeed, cross(vecDifference, force)), automobile.fTurnMass)
					automobile.vTurnSpeed = add(automobile.vTurnSpeed, divide(cross(vecDifference, force), automobile.fTurnMass))
					--print(automobile.vTurnSpeed.x, automobile.vTurnSpeed.y, automobile.vTurnSpeed.z)
				end
			end
		end
	end
end

-- -- Define function for applying force
-- function applyForce(car, vecForce, point, bUpdateTurnSpeed)
    -- if doesVehicleExist(car) then
		-- local carPointer = getCarPointer(car)
		-- local vehicle = samem.cast("CVehicle *", carPointer)
		-- if vehicle ~= samem.nullptr then

			-- if vehicle.nVehicleClass == 0 then -- на самом деле это не класс, а тип. VEHICLE_TYPE_AUTOMOBILE  = 0
				-- local automobile = samem.cast('CAutomobile *', vehicle)
				-- local qx, qy, qz, qw = getVehicleQuaternion(car)


				-- local vecMoveSpeedForce = vecForce
				-- if (automobile.nPhysicalFlags.bDisableZ == 1) then
					-- vecMoveSpeedForce.z = 0.0
				-- end

				-- if not (automobile.nPhysicalFlags.bInfiniteMass == 1) and not (automobile.nPhysicalFlags.bDisableMoveForce == 1) then
					-- automobile.vMoveSpeed = divide(add(automobile.vMoveSpeed, vecMoveSpeedForce), automobile.fMass)
				-- end

				-- if not (automobile.nPhysicalFlags.bDisableTurnForce == 1) and bUpdateTurnSpeed then
					-- local vecCentreOfMassMultiplied = {}
					-- local fTurnMass = automobile.fTurnMass
					-- if (automobile.nPhysicalFlags.bInfiniteMass == 1) then
						-- fTurnMass = fTurnMass + automobile.vCentreOfMass.z * automobile.fMass * automobile.vCentreOfMass.z * 0.5
					-- else
						-- vecCentreOfMassMultiplied = rotateVectorByQuaternion(automobile.vCentreOfMass, qx, qy, qz, qw)--getMatrix(physical):transformVector(physical.vCentreOfMass)
					-- end

					-- if automobile.nPhysicalFlags.bDisableMoveForce then
						-- point.z = 0.0
						-- vecForce.z = 0.0
					-- end

					-- local distance = subtract(point, vecCentreOfMassMultiplied)
					
					-- automobile.vTurnSpeed = add(automobile.vTurnSpeed, divide(cross(distance, vecForce), automobile.fTurnMass))
				-- end
			-- end
		-- end
	-- end
-- end

function applyMoveForce(car, force)
    if doesVehicleExist(car) then
		local carPointer = getCarPointer(car)
		local vehicle = samem.cast("CVehicle *", carPointer)
		if vehicle ~= samem.nullptr then
			if vehicle.nVehicleClass == 0 then -- на самом деле это не класс, а тип. VEHICLE_TYPE_AUTOMOBILE  = 0
				local automobile = samem.cast('CAutomobile *', vehicle)
				local qx, qy, qz, qw = getVehicleQuaternion(car)

				if not (automobile.nPhysicalFlags.bInfiniteMass == 1) and not (automobile.nPhysicalFlags.bDisableMoveForce == 1) then
					if (automobile.nPhysicalFlags.bDisableZ == 1) then
						force.z = 0.0
					end
					automobile.vMoveSpeed = add(automobile.vMoveSpeed, divide(force, automobile.fMass))
				end
			end
		end
	end
end



function getCarSuspension(car)
    if doesVehicleExist(car) then
		local carPointer = getCarPointer(car)
		local vehicle = samem.cast("CVehicle *", carPointer)
		if vehicle ~= samem.nullptr then
			if vehicle.nVehicleClass == 0 then -- на самом деле это не класс, а тип. VEHICLE_TYPE_AUTOMOBILE  = 0
				local automobile = samem.cast('CAutomobile *', vehicle)
				local wheelsSuspensionCompression = automobile.wheelsDistancesToGround1
				--print(automobile.vLastCollisionPosn.x, automobile.vLastCollisionPosn.y, automobile.vLastCollisionPosn.z )
				--automobile.nPhysicalFlags.bSubmergedInWater = 1
				-- print(
					-- wheelsSuspensionCompression[1],
					-- wheelsSuspensionCompression[2],
					-- wheelsSuspensionCompression[3],
					-- wheelsSuspensionCompression[4]
				-- )
				
				return wheelsSuspensionCompression
			end
		end
	end
end



отрефактори код