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
	local rollOnToWheelsForce	= 0.0025
	
	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
					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)))
							applyForceToCar(car, point.x, point.y, point.z, force.x, force.y, force.z)
							
						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(
					-- 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




мой код работает очень странно. почему это может быть? вот оригинал 

void CPhysical::ApplyTurnForce(CVector force, CVector point)
{
    if (!physicalFlags.bDisableTurnForce)
    {
        CVector vecCentreOfMassMultiplied{};
        if (!physicalFlags.bInfiniteMass)
            vecCentreOfMassMultiplied = GetMatrix().TransformVector(m_vecCentreOfMass);

        if (physicalFlags.bDisableMoveForce) {
            point.z = 0.0f;
            force.z = 0.0f;
        }
        CVector vecDifference = point - vecCentreOfMassMultiplied;
        m_vecTurnSpeed += CrossProduct(vecDifference, force) / m_fTurnMass;
    }
}

// 0x542B50
void CPhysical::ApplyForce(CVector vecForce, CVector point, bool bUpdateTurnSpeed)
{
    CVector vecMoveSpeedForce = vecForce;
    if (physicalFlags.bDisableZ)
        vecMoveSpeedForce.z = 0.0f;

    if (!physicalFlags.bInfiniteMass && !physicalFlags.bDisableMoveForce)
        m_vecMoveSpeed += vecMoveSpeedForce / m_fMass;

    if (!physicalFlags.bDisableTurnForce && bUpdateTurnSpeed) {
        CVector vecCentreOfMassMultiplied{};
        float fTurnMass = m_fTurnMass;
        if (physicalFlags.bInfiniteMass)
            fTurnMass += m_vecCentreOfMass.z * m_fMass * m_vecCentreOfMass.z * 0.5f;
        else
            vecCentreOfMassMultiplied = GetMatrix().TransformVector(m_vecCentreOfMass);

        if (physicalFlags.bDisableMoveForce) {
            point.z = 0.0f;
            vecForce.z = 0.0f;
        }

        CVector distance = point - vecCentreOfMassMultiplied;
        m_vecTurnSpeed += CrossProduct(distance, vecForce) / fTurnMass;
    }
}