Client side:
--
-- c_gravityWheels.lua
--
local gravityTab = {}
local enableBindKey = true
local enableGravOnStart = false
local lerpValue = 0.95 -- the closer to 1 the slower the transition
local updatePauseLocal = 15 -- update pause for localPlayer (miliseconds)
local updatePauseGlobal = 100 -- update pause for others (miliseconds)
local searchVector = {0, 2.3, -6}
local theTikGap = 0.2 -- here you set how many seconds to wait after switching gravity wheels on/off
local switch_key = 'g' -- define the key that switches the gravity effect
local searchVectorLen = nil
function getVehicleGravityVector(thisVeh, xVec, yVec, zVec)
local px, py, pz = getElementPosition(thisVeh)
local offX, offY, offZ = getPositionFromElementOffset(thisVeh, xVec, yVec, zVec)
if not isLineOfSightClear(px, py, pz, offX, offY, offZ, true, false, false, true) then
local i:~, hitX, hitY, hitZ, _, normalX, normalY, normalZ = processLineOfSight(px, py, pz, offX, offY, offZ, true, false, false, true)
if normalZ > 0.96 then
--dxDrawLine3D(px, py, pz, hitX, hitY, hitZ, tocolor(0, 0, 255, 230), 2)
return {0, 0, -1}
end
local grnDst = getDistanceBetweenPoints3D(px, py, pz, hitX, hitY, hitZ)
local col = tocolor (0, 255, 0, 230)
--dxDrawLine3D(px, py, pz, hitX, hitY, hitZ, tocolor(0, 255, 0, 230), 2)
return {-normalX, -normalY, -normalZ}
else
--dxDrawLine3D(px, py, pz, offX, offY, offZ, tocolor(255, 0, 0, 230 ), 2)
return {0, 0, -1}
end
end
-- this function sets gravity based on surface normals (use only for localPlayer)
function localVehicleGravity(thisVeh)
local lerpAdj = lerpValue ^ (25 / localFPS)
local rx2, ry2, rz2, rx1, ry1, rz1
if gravityTab[thisVeh].vec then
rx2, ry2, rz2 = unpack(gravityTab[thisVeh].vec)
else
gravityTab[thisVeh].vec = {0, 0, -1}
rx2, ry2, rz2 = unpack(gravityTab[thisVeh].vec)
end
rx1, ry1, rz1 = unpack(getVehicleGravityVector(thisVeh, searchVector[1], searchVector[2], searchVector[3]))
gravityTab[thisVeh].vec = {lerpAngle(rx1, rx2, lerpAdj), lerpAngle(ry1, ry2, lerpAdj), lerpAngle( rz1, rz2, lerpAdj)}
return setVehicleGravity(thisVeh, normalizeVector(unpack(gravityTab[thisVeh].vec)))
end
function smoothGravityNormal(thisVeh)
local lerpAdj = lerpValue ^ (25 / localFPS)
local rx2, ry2, rz2 = unpack(gravityTab[thisVeh].vec)
local rx1, ry1, rz1 = 0, 0, -1
gravityTab[thisVeh].vec = {lerpAngle(rx1, rx2, lerpAdj), lerpAngle(ry1, ry2, lerpAdj), lerpAngle( rz1, rz2, lerpAdj)}
setVehicleGravity(thisVeh, unpack(gravityTab[thisVeh].vec))
end
-- this function sets gravity just like an old magnet whels script (for other players)
function globalVehicleGravity(thisVeh)
local px, py, pz = getElementPosition(thisVeh)
local offX, offY, offZ = getPositionFromElementOffset(thisVeh, 0, 0, -searchVectorLen)
if not isLineOfSightClear(px, py, pz, offX, offY, offZ, true, false, false, true) then
local teX, teY, teZ = normalizeVector(offX - px, offY - py, offZ - pz)
setVehicleGravity(thisVeh, teX, teY, teZ)
else
setVehicleGravity(thisVeh, 0, 0, -1)
end
end
----------------------------------------------------------------------------------------------------------------------------
-- switching
----------------------------------------------------------------------------------------------------------------------------
local isGWEnabled = false
function switchGravityWheels(doEnable)
local thisVeh = getPedOccupiedVehicle(localPlayer)
if not thisVeh then return false end
if not getVehicleOccupant(thisVeh, 0) == localPlayer then
return false
end
if not gravityTab[thisVeh] then
gravityTab[thisVeh] = {}
gravityTab[thisVeh].enable = false
gravityTab[thisVeh].vec = {0, 0, -1}
end
gravityTab[thisVeh].enable = doEnable
triggerServerEvent("onSwitchGravityWheels", resourceRoot, gravityTab[thisVeh].enable, thisVeh)
end
local getLastTack = getTickCount()
function toggleGravityWheels()
local thisVeh = getPedOccupiedVehicle(localPlayer)
if not thisVeh then return end
if not getVehicleOccupant(thisVeh, 0) == localPlayer then
return
end
if (getTickCount() - getLastTack < theTikGap * 1000) then
outputChatBox('Gravity Wheels: Wait '..theTikGap..' seconds.', 255, 0, 0)
return
end
if not gravityTab[thisVeh] then
gravityTab[thisVeh] = {}
gravityTab[thisVeh].enable = false
gravityTab[thisVeh].vec = {0, 0, -1}
end
gravityTab[thisVeh].enable = not gravityTab[thisVeh].enable
triggerServerEvent("onSwitchGravityWheels", resourceRoot, gravityTab[thisVeh].enable, thisVeh)
getLastTack = getTickCount()
end
function gravityWheelsPlayerQuit(thisPed, thisVeh)
if thisVeh then
if not gravityTab[thisVeh] then
gravityTab[thisVeh] = {}
end
gravityTab[thisVeh].enable = false
gravityTab[thisVeh].vec = {0, 0, -1}
setVehicleGravity(thisVeh, 0, 0, -1)
end
end
function gravityWheelsSwitch(isEnable, thisPed, thisVeh)
if thisVeh then
if not gravityTab[thisVeh] then
gravityTab[thisVeh] = {}
end
gravityTab[thisVeh].enable = isEnable
if thisPed ~= localPlayer then
gravityTab[thisVeh].vec = {0, 0, -1}
setVehicleGravity(thisVeh, 0, 0, -1)
else
isGWEnabled = gravityTab[thisVeh].enable
end
end
end
function gravityWheelsReset(thisPed, thisVeh)
if thisVeh then
if not gravityTab[thisVeh] then
gravityTab[thisVeh] = {}
end
gravityTab[thisVeh].enable = false
gravityTab[thisVeh].vec = {0, 0, -1}
setVehicleGravity(thisVeh, 0, 0, -1)
if thisPed == localPlayer then
isGWEnabled = gravityTab[thisVeh].enable
end
end
end
----------------------------------------------------------------------------------------------------------------------------
-- events
----------------------------------------------------------------------------------------------------------------------------
addEventHandler("onClientResourceStart", getResourceRootElement(getThisResource()), function()
searchVectorLen = vectorLength(searchVector[1], searchVector[2], searchVector[3])
triggerServerEvent("onPlayerStartGravityWheels", resourceRoot, enableGravOnStart)
if enableBindKey then
outputChatBox('Hit '..switch_key..' to turn on/off gravity wheels', 255, 155, 0)
bindKey(switch_key, "down", toggleGravityWheels)
end
end
)
addEventHandler("onClientResourceStop", getResourceRootElement(getThisResource()), function()
for index,thisVeh in ipairs(getElementsByType("vehicle")) do
if gravityTab[thisVeh] then
setVehicleGravity(thisVeh, 0, 0, -1)
end
end
end
)
local scx, scy = guiGetScreenSize()
addEventHandler("onClientRender", root,
function()
dxDrawText("Gravity Wheels: "..tostring(isGWEnabled), scx * 0.78, scy * 0.25)
local thisVeh = getPedOccupiedVehicle(localPlayer)
if thisVeh then
if gravityTab[thisVeh] then
if gravityTab[thisVeh].enable then
localVehicleGravity(thisVeh)
else
if gravityTab[thisVeh].vec then
if gravityTab[thisVeh].vec[3] > -0.99999999 then
smoothGravityNormal(thisVeh)
end
end
end
end
end
end
)
local getLastTick = getTickCount()
addEventHandler("onClientRender", root,
function()
if (getTickCount() - getLastTick < updatePauseGlobal) then return end
for id,thisPed in ipairs(getElementsByType("player")) do
if thisPed ~= localPlayer then
local thisVeh = getPedOccupiedVehicle(thisPed)
if thisVeh then
if isElementStreamedIn(thisVeh) then
if gravityTab[thisVeh] then
if gravityTab[thisVeh].enable then
if isElementStreamedIn(thisVeh) then
globalVehicleGravity(thisVeh)
end
end
end
end
end
end
end
getLastTick = getTickCount()
end
)
addEvent("gravityWheelsPlayerQuit", true)
addEvent("gravityWheelsPlayerSwitch", true)
addEvent("gravityWheelsPlayerReset", true)
addEventHandler("gravityWheelsPlayerQuit", resourceRoot, gravityWheelsPlayerQuit)
addEventHandler("gravityWheelsPlayerSwitch", resourceRoot, gravityWheelsSwitch)
addEventHandler("gravityWheelsPlayerReset", resourceRoot, gravityWheelsReset)
----------------------------------------------------------------------------------------------------------------------------
-- math
----------------------------------------------------------------------------------------------------------------------------
function toxz(x, y, z)
return math.acos(z or 0), math.atan2(y or 0, x or 0)
end
function fromxz(rx, rz)
return {math.cos(rz) * math.sin(rx), math.sin(rz) * math.sin(rx), math.cos(rx)}
end
function lerpAngle(a1, a2, t)
if a2 - a1 > math.pi then
a2 = a2 - 2 * math.pi
elseif a1 - a2 > math.pi then
a1 = a1 - 2 * math.pi
end
return a1 + (a2 - a1) * t
end
function getPositionFromElementOffset(element, offX, offY, offZ)
local m = getElementMatrix(element)
local x = offX * m[1][1] + offY * m[2][1] + offZ * m[3][1] + m[4][1]
local y = offX * m[1][2] + offY * m[2][2] + offZ * m[3][2] + m[4][2]
local z = offX * m[1][3] + offY * m[2][3] + offZ * m[3][3] + m[4][3]
return x, y, z
end
function vectorLength(vecX, vecY, vecZ)
return math.sqrt(vecX * vecX + vecY * vecY + vecZ * vecZ)
end
function normalizeVector(vecX, vecY, vecZ)
local vecLen = math.sqrt(vecX * vecX + vecY * vecY + vecZ * vecZ)
local vecNX = vecX / vecLen
local vecNY = vecY / vecLen
local vecNZ = vecZ / vecLen
return vecNX, vecNY, vecNZ
end
local function updateFPS(msSinceLastFrame)
localFPS = (1 / msSinceLastFrame) * 1000
end
addEventHandler("onClientPreRender", root, updateFPS)
Server side:
--
--s_gravityWheels.lua
--
local isGWEnable = {}
local lastVehicle = {}
local enableOnStart = {}
function gravityWheelsPlayerJoin(isSwEnabled)
isGWEnable[client] = isSwEnabled
enableOnStart[client] = isSwEnabled
local clientVeh = getPedOccupiedVehicle(client)
if clientVeh and isSwEnabled then
lastVehicle[client] = clientVeh
end
for _,thisPlayer in ipairs(getElementsByType("player")) do
local thisVeh = getPedOccupiedVehicle(thisPlayer)
if thisVeh then
triggerClientEvent(client, "gravityWheelsPlayerSwitch", resourceRoot, isGWEnable[thisPlayer], thisPlayer, thisVeh)
end
end
end
function gravityWheelsPlayerQuit()
if getElementType(source) == "player" then
isGWEnable[source] = false
local lastVeh = getPedOccupiedVehicle(source)
lastVehicle[source] = lastVeh
triggerClientEvent("gravityWheelsPlayerQuit", resourceRoot, source, lastVehicle[source])
end
end
function onSwitchGravityWheels(isON, thisVeh)
isGWEnable[client] = isON
lastVehicle[client] = thisVeh
triggerClientEvent("gravityWheelsPlayerSwitch", resourceRoot, isGWEnable[client], client, thisVeh)
end
addEvent("onPlayerStartGravityWheels",true)
addEventHandler("onPlayerStartGravityWheels", resourceRoot, gravityWheelsPlayerJoin)
addEvent("onSwitchGravityWheels",true)
addEventHandler("onSwitchGravityWheels", resourceRoot, onSwitchGravityWheels)
addEventHandler("onPlayerQuit", root, gravityWheelsPlayerQuit)
function resetGravityOnVehicleExit(thePlayer)
if isGWEnable[thePlayer] then
isGWEnable[thePlayer] = false
triggerClientEvent("gravityWheelsPlayerReset", resourceRoot, thePlayer, source)
end
end
function resetGravityOnVehicleEnter(thePlayer)
if isGWEnable[thePlayer] and not enableOnStart[thePlayer] then
isGWEnable[thePlayer] = false
triggerClientEvent("gravityWheelsPlayerReset", resourceRoot, thePlayer, source)
else
if enableOnStart[thePlayer] then
isGWEnable[thePlayer] = true
lastVehicle[thePlayer] = source
triggerClientEvent("gravityWheelsPlayerSwitch", resourceRoot, isGWEnable[thePlayer], thePlayer, source)
end
end
end
addEventHandler("onVehicleExit", root, resetGravityOnVehicleExit)
addEventHandler("onVehicleEnter", root, resetGravityOnVehicleEnter)
function resetGravityOnPlayerDeath()
if isGWEnable[source] then
triggerClientEvent("gravityWheelsPlayerReset", resourceRoot, source, lastVehicle[source])
isGWEnable[source] = false
lastVehicle[source] = false
end
end
addEventHandler("onPlayerWasted", root, resetGravityOnPlayerDeath)
function setOnOnMapStarting()
for _,thisPlayer in ipairs(getElementsByType("player")) do
if enableOnStart[thisPlayer] then
local thisVeh = getPedOccupiedVehicle(thisPlayer)
if thisVeh then
isGWEnable[thisPlayer] = true
lastVehicle[thisPlayer] = thisVeh
triggerClientEvent("gravityWheelsPlayerSwitch", resourceRoot, isGWEnable[thisPlayer], thisPlayer, thisVeh)
end
end
end
end
addEvent("onMapStarting", true)
addEventHandler("onMapStarting", root, setOnOnMapStarting)
addEventHandler("onResourceStop", resourceRoot, function()
isGWEnable = {}
lastVehicle = {}
enableOnStart = {}
end
)