429 lines
16 KiB
Lua
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)
|