monitor/resource/menu/client/cl_vehicle.lua
2025-04-16 22:30:27 +07:00

429 lines
16 KiB
Lua

-- Prevent running if menu is disabled
if not TX_MENU_ENABLED then return end
-- =============================================
-- This file is for all vehicle-related menu features
-- =============================================
--[[ NUI CALLBACKS ]]
local function getPedVehicle()
local ped = PlayerPedId()
local veh
if IS_REDM and IsPedOnMount(ped) then
veh = GetMount(ped)
else
veh = GetVehiclePedIsIn(ped, false)
end
if veh and veh > 0 then
return veh
else
return nil
end
end
-- NOTE: this is not a complete list, but most others have the type "automobile"
local vehClassNamesEnum = {
[8] = "bike",
[11] = "trailer",
[13] = "bike",
[14] = "boat",
[15] = "heli",
[16] = "plane",
[21] = "train",
}
-- Since we don't have the vehicle types on the server, we need this translation table
-- NOTE: this list was generated for game build 2802/mpchristmas3
-- How to update the list: https://gist.github.com/tabarra/32ef90524188093ab4218ee7b5121269
local mismatchedTypes = {
["airtug"] = "automobile", -- trailer
["avisa"] = "submarine", -- boat
["blimp"] = "heli", -- plane
["blimp2"] = "heli", -- plane
["blimp3"] = "heli", -- plane
["caddy"] = "automobile", -- trailer
["caddy2"] = "automobile", -- trailer
["caddy3"] = "automobile", -- trailer
["chimera"] = "automobile", -- bike
["docktug"] = "automobile", -- trailer
["forklift"] = "automobile", -- trailer
["kosatka"] = "submarine", -- boat
["mower"] = "automobile", -- trailer
["policeb"] = "bike", -- automobile
["ripley"] = "automobile", -- trailer
["rrocket"] = "automobile", -- bike
["sadler"] = "automobile", -- trailer
["sadler2"] = "automobile", -- trailer
["scrap"] = "automobile", -- trailer
["slamtruck"] = "automobile", -- trailer
["Stryder"] = "automobile", -- bike
["submersible"] = "submarine", -- boat
["submersible2"] = "submarine", -- boat
["thruster"] = "heli", -- automobile
["towtruck"] = "automobile", -- trailer
["towtruck2"] = "automobile", -- trailer
["tractor"] = "automobile", -- trailer
["tractor2"] = "automobile", -- trailer
["tractor3"] = "automobile", -- trailer
["trailersmall2"] = "trailer", -- automobile
["utillitruck"] = "automobile", -- trailer
["utillitruck2"] = "automobile", -- trailer
["utillitruck3"] = "automobile", -- trailer
}
local function handleSpawnRequestFivem(model)
if not IsModelAVehicle(model) then
debugPrint("^1Model provided is not a vehicle: " .. model)
return false
end
--Resolve vehicle type, required for server setter
--NOTE: check if GetVehicleTypeFromName is already available
local modelType
if mismatchedTypes[model] then
modelType = mismatchedTypes[model]
else
local modelClassNumber = GetVehicleClassFromName(model)
modelType = vehClassNamesEnum[modelClassNumber] or "automobile"
end
--Request from server
TriggerServerEvent('txsv:req:vehicle:spawn:fivem', model, modelType)
return true
end
local function handleSpawnRequestRedm(model)
--check if model is valid vehicle or horse (IsThisModelAHorse)
if not IsModelAVehicle(model) and not Citizen.InvokeNative(0x772A1969F649E902, GetHashKey(model)) then
debugPrint("^1Model provided is not a vehicle or horse: " .. model)
return false
end
--request
TriggerServerEvent('txsv:req:vehicle:spawn:redm', model)
return true
end
local gameSpawnReqHandler = IS_FIVEM and handleSpawnRequestFivem or handleSpawnRequestRedm
RegisterSecureNuiCallback('spawnVehicle', function(data, cb)
if type(data) ~= 'table' or type(data.model) ~= 'string' then
error("Invalid spawnVehicle NUI callback data")
end
if not IsModelValid(data.model) then
debugPrint("^1Invalid vehicle/horse model requested: " .. data.model)
sendSnackbarMessage(
'error',
'nui_menu.page_main.vehicle.spawn.dialog_error',
true,
{ modelName = data.model }
)
elseif not gameSpawnReqHandler(data.model) then
sendSnackbarMessage(
'error',
'nui_menu.page_main.vehicle.spawn.dialog_error',
true,
{ modelName = data.model }
)
else
sendSnackbarMessage(
'info',
'nui_menu.page_main.vehicle.spawn.dialog_info',
true,
{ modelName = data.model }
)
end
cb({})
end)
RegisterCommand('txAdmin:menu:spawnVehicle', function()
if not menuIsAccessible then return end
if not DoesPlayerHavePerm(menuPermissions, 'menu.vehicle') then
return sendSnackbarMessage('error', 'nui_menu.misc.no_perms', true)
end
toggleMenuVisibility(true)
SetNuiFocus(true, true)
sendMenuMessage('openSpawnVehicleDialog', {})
end)
local function reqVehicleDelete(_, cb)
local veh = getPedVehicle()
if veh == nil then
return sendSnackbarMessage('error', 'nui_menu.page_main.vehicle.not_in_veh_error', true)
end
local vehNetId = NetworkGetNetworkIdFromEntity(veh)
TriggerServerEvent("txsv:req:vehicle:delete", vehNetId)
if cb then cb({ success = true }) end
end
RegisterSecureNuiCallback('deleteVehicle', reqVehicleDelete)
RegisterCommand('txAdmin:menu:deleteVehicle', function()
if not menuIsAccessible then return end
if not DoesPlayerHavePerm(menuPermissions, 'menu.vehicle') then
return sendSnackbarMessage('error', 'nui_menu.misc.no_perms', true)
end
reqVehicleDelete()
end)
local function reqVehicleFix(_, cb)
local veh = getPedVehicle()
if veh == nil then
return sendSnackbarMessage('error', 'nui_menu.page_main.vehicle.not_in_veh_error', true)
end
TriggerServerEvent('txsv:req:vehicle:fix')
if cb then cb({}) end
end
RegisterSecureNuiCallback('fixVehicle', reqVehicleFix)
RegisterCommand('txAdmin:menu:fixVehicle', function()
if not menuIsAccessible then return end
if not DoesPlayerHavePerm(menuPermissions, 'menu.vehicle') then
return sendSnackbarMessage('error', 'nui_menu.misc.no_perms', true)
end
reqVehicleFix()
end)
local function reqVehicleBoost(_, cb)
local veh = getPedVehicle()
if veh == nil then
return sendSnackbarMessage('error', 'nui_menu.page_main.vehicle.not_in_veh_error', true)
end
TriggerServerEvent('txsv:req:vehicle:boost')
if cb then cb({}) end
end
RegisterSecureNuiCallback('boostVehicle', reqVehicleBoost)
RegisterCommand('txAdmin:menu:boostVehicle', function()
if not menuIsAccessible then return end
if not DoesPlayerHavePerm(menuPermissions, 'menu.vehicle') then
return sendSnackbarMessage('error', 'nui_menu.misc.no_perms', true)
end
reqVehicleBoost()
end)
--[[ EVENT HANDLERS + FUNCTION LOGIC ]]
local function setVehicleHandlingValue(veh, field, newValue)
-- local currValue = GetVehicleHandlingFloat(veh, 'CHandlingData', field)
SetVehicleHandlingField(veh, 'CHandlingData', field, newValue * 1.0)
end
local function setVehicleHandlingModifier(veh, field, multiplier)
local currValue = GetVehicleHandlingFloat(veh, 'CHandlingData', field)
local newValue = (multiplier * 1.0) * currValue;
SetVehicleHandlingField(veh, 'CHandlingData', field, newValue)
end
local boostableVehicleClasses = {
[0] = 'Compacts',
[1] = 'Sedans',
[2] = 'SUVs',
[3] = 'Coupes',
[4] = 'Muscle',
[5] = 'Sports Classics',
[6] = 'Sports',
[7] = 'Super',
-- [8]='Motorcycles',
[9] = 'Off-road',
-- [10]='Industrial',
[11] = 'Utility',
[12] = 'Vans',
-- [13]='Cycles',
-- [14]='Boats',
-- [15]='Helicopters',
-- [16]='Planes',
[17] = 'Service',
[18] = 'Emergency',
[19] = 'Military',
[20] = 'Commercial',
-- [21]='Trains',
[22] = 'Open Wheel'
}
local function boostVehicleFivem()
local ped = PlayerPedId()
local veh = GetVehiclePedIsIn(ped, false)
--Check if in vehicle
if not veh or veh <= 0 then
return sendSnackbarMessage('error', 'nui_menu.page_main.vehicle.not_in_veh_error', true)
end
--Check if vehicle already boosted
--NOTE: state bags were too complicated, and checking for specific float didn't work due to precision
local boostedFlag = GetVehicleHandlingFloat(veh, 'CHandlingData', 'fInitialDriveMaxFlatVel')
if boostedFlag == 300.401214599609375 then
return sendSnackbarMessage('error', 'nui_menu.page_main.vehicle.boost.already_boosted', true)
end
--Check if vehicle is in fact a car
local vehClass = GetVehicleClass(veh)
if not boostableVehicleClasses[vehClass] then
return sendSnackbarMessage('error', 'nui_menu.page_main.vehicle.boost.unsupported_class', true)
end
--Modify car
setVehicleHandlingValue(veh, 'fInitialDriveMaxFlatVel', 300.40120); --the signature, don't change
setVehicleHandlingValue(veh, 'fHandBrakeForce', 10.0);
setVehicleHandlingValue(veh, 'fBrakeForce', 20.0);
setVehicleHandlingModifier(veh, 'fTractionCurveMin', 2.1);
setVehicleHandlingModifier(veh, 'fTractionCurveMax', 2.5);
setVehicleHandlingModifier(veh, 'fInitialDriveForce', 2.0); --accelerates real fast, almost no side effects
setVehicleHandlingModifier(veh, 'fDriveInertia', 1.25);
setVehicleHandlingValue(veh, 'fInitialDragCoeff', 10.0);
SetVehicleHandlingVector(veh, 'CHandlingData', 'vecInertiaMultiplier', vector3(0.1, 0.1, 0.1))
setVehicleHandlingValue(veh, 'fAntiRollBarForce', 0.0001); --testar, o certo é 0~1
setVehicleHandlingValue(veh, 'fTractionLossMult', 0.00001); --testar, o certo é >1
setVehicleHandlingValue(veh, 'fRollCentreHeightFront', 0.5); --testar, o certo é 0~1
setVehicleHandlingValue(veh, 'fRollCentreHeightRear', 0.5); --testar, o certo é 0~1
playLibrarySound('confirm')
SetVehicleCanBreak(veh, false) -- If this is set to false, the vehicle simply can't break
SetVehicleEngineCanDegrade(veh, false) -- Engine strong
SetVehicleMod(veh, 15, 3, false) -- Max Suspension
SetVehicleMod(veh, 11, 3, false) -- Max Engine
SetVehicleMod(veh, 16, 4, false) -- Max Armor
SetVehicleMod(veh, 12, 2, false) -- Max Brakes
SetVehicleMod(veh, 13, 2, false) -- Max Transmission
ToggleVehicleMod(veh, 18, true) -- modTurbo
SetVehicleMod(veh, 18, 0, false) -- Turbo
SetVehicleNitroEnabled(veh, true) -- Gives the vehicle a nitro boost
SetVehicleTurboPressure(veh, 100.0) -- Pressure of the turbo is 100%
EnableVehicleExhaustPops(veh, true) -- This forces the exhaust to always "pop"
SetVehicleCheatPowerIncrease(veh, 1.8) -- Torque multiplier
sendSnackbarMessage('success', 'nui_menu.page_main.vehicle.boost.success', true)
end
local function boostVehicleRedm()
local ped = PlayerPedId()
local horse = IsPedOnMount(ped) and GetMount(ped) or false
if not horse then
return sendSnackbarMessage('error', 'nui_menu.page_main.vehicle.boost.redm_not_mounted', true)
end
local boostedFlag = Citizen.InvokeNative(0x200373A8DF081F22, horse, 0)
if boostedFlag then
return sendSnackbarMessage('error', 'nui_menu.page_main.vehicle.boost.already_boosted', true)
end
local duration = 4000.0
-- Inner/Outter Health
Citizen.InvokeNative(0x4AF5A4C7B9157D14, horse, 0, duration, true) --EnableAttributeCoreOverpower
Citizen.InvokeNative(0xF6A7C08DF2E28B28, horse, 0, duration, true) --EnableAttributeOverpower
-- Inner/Outter Stamina
Citizen.InvokeNative(0x4AF5A4C7B9157D14, horse, 1, duration, true) --EnableAttributeCoreOverpower
Citizen.InvokeNative(0xF6A7C08DF2E28B28, horse, 1, duration, true) --EnableAttributeOverpower
AnimpostfxPlay('PlayerOverpower')
sendSnackbarMessage('success', 'nui_menu.page_main.vehicle.boost.success', true)
end
local boostVehicleFunc = IS_FIVEM and boostVehicleFivem or boostVehicleRedm
RegisterNetEvent('txcl:vehicle:boost', boostVehicleFunc)
-- Fix vehicle
RegisterNetEvent('txcl:vehicle:fix', function()
local ped = PlayerPedId()
local veh = GetVehiclePedIsIn(ped, false)
if veh and veh > 0 then
SetVehicleUndriveable(veh, false)
SetVehicleFixed(veh)
SetVehicleEngineOn(veh, true, false)
SetVehicleDirtLevel(veh, 0.0)
SetVehicleOnGroundProperly(veh)
elseif IS_REDM and IsPedOnMount(ped) then
local horse = GetMount(ped)
ResurrectPed(horse)
SetEntityHealth(horse, GetEntityMaxHealth(horse))
Citizen.InvokeNative(0xC6258F41D86676E0, horse, 0, 100) -- SetAttributeCoreValue
Citizen.InvokeNative(0xC6258F41D86676E0, horse, 1, 100) -- SetAttributeCoreValue
Citizen.InvokeNative(0xC6258F41D86676E0, horse, 2, 100) -- SetAttributeCoreValue
end
sendSnackbarMessage('success', 'nui_menu.page_main.vehicle.fix.success', true)
end)
-- Spawn vehicle - used in redm
RegisterNetEvent('txcl:vehicle:spawn:redm', function(model)
if not IS_REDM then return end
if type(model) ~= 'string' then return end
-- check model
local modelHash = GetHashKey(model)
local isVehicle = IsModelAVehicle(model)
local isHorse = Citizen.InvokeNative(0x772A1969F649E902, modelHash) --IsThisModelAHorse
if not isVehicle and not isHorse then
return debugPrint("^1Model provided is not a vehicle or horse: " .. model)
end
-- get player data
local playerPed = PlayerPedId()
local playerCoords = GetEntityCoords(playerPed)
local playerHeading = GetEntityHeading(playerPed)
local currentVeh = GetVehiclePedIsIn(playerPed, false)
if IsPedOnMount(playerPed) then
currentVeh = GetMount(playerPed)
end
local currentVehVelocity
if currentVeh then
currentVehVelocity = GetEntityVelocity(currentVeh)
DeleteEntity(currentVeh)
end
-- request new model
RequestModel(modelHash)
while not HasModelLoaded(modelHash) do
Wait(15)
end
-- spawn it
local newVeh
if isVehicle then
newVeh = CreateVehicle(modelHash, playerCoords, playerHeading, true, false, false)
SetPedIntoVehicle(playerPed, newVeh, -1)
SetVehicleOnGroundProperly(newVeh)
else
newVeh = CreatePed(modelHash, playerCoords, playerHeading, true, false)
-- Citizen.InvokeNative(0x77FF8D35EEC6BBC4, newVeh, 1, 0) --EquipMetaPedOutfitPreset
Citizen.InvokeNative(0x283978A15512B2FE, newVeh, true) --SetRandomOutfitVariation
Citizen.InvokeNative(0x028F76B6E78246EB, playerPed, newVeh, -1) --SetPedOntoMount
end
-- preserving speed, doesn't work well for horses
if currentVehVelocity then
SetEntityVelocity(newVeh, currentVehVelocity)
end
SetModelAsNoLongerNeeded(modelHash)
sendSnackbarMessage('success', 'nui_menu.page_main.vehicle.spawn.dialog_success', true)
end)
-- Spawn vehicles, with support for entity lockdown - used in fivem
RegisterNetEvent('txcl:seatInVehicle', function(vehNetID, seat, oldVehVelocity)
if type(vehNetID) ~= 'number' then return end
if type(seat) ~= 'number' then return end
local attemptsCounter = 0
local attemptsLimit = 400 -- 400*5 = 2s
while not NetworkDoesEntityExistWithNetworkId(vehNetID) and attemptsCounter < attemptsLimit do
Wait(5)
end
if not NetworkDoesEntityExistWithNetworkId(vehNetID) then
return sendSnackbarMessage('error', 'Failed to seat into vehicle (net=' .. vehNetID .. ')', false)
end
local veh = NetToVeh(vehNetID)
if veh and veh > 0 then
SetPedIntoVehicle(PlayerPedId(), veh, seat)
if seat == -1 then
SetVehicleEngineOn(veh, true, true, false)
SetVehicleOnGroundProperly(veh)
if type(oldVehVelocity) ~= 'vector3' then
SetEntityVelocity(veh, oldVehVelocity)
end
sendSnackbarMessage('success', 'nui_menu.page_main.vehicle.spawn.dialog_success', true)
end
end
end)