feat(vehicle): garages

This commit is contained in:
Andyyy7666
2023-09-26 10:24:07 +02:00
parent fcc0ce4551
commit 6ff461839d
5 changed files with 594 additions and 27 deletions

243
client/vehicle/data.lua Normal file
View File

@@ -0,0 +1,243 @@
return {
{
garageType = "land",
groups = {"sahp", "lspd", "bcso"},
ped = vector4(452.83, -1027.79, 28.54, 2.49),
vehicleSpawns = {
vector4(446.19, -1025.47, 28.24, 185.96),
vector4(438.75, -1026.09, 28.38, 184.29),
vector4(434.98, -1026.61, 28.46, 185.99),
vector4(431.26, -1027.31, 28.53, 185.59),
vector4(427.52, -1026.84, 28.58, 184.25)
}
},
{
garageType = "land",
impound = true,
ped = vector4(407.99, -1624.74, 29.29, 229.93),
vehicleSpawns = {
vector4(396.34, -1644.28, 28.86, 319.10),
vector4(398.56, -1646.42, 28.8616, 319.18),
vector4(400.68, -1648.86, 28.86, 140.78),
vector4(403.32, -1650.54, 28.86, 319.92),
vector4(403.21, -1650.68, 28.86, 139.40)
}
},
{
garageType = "plane",
ped = vector4(-941.05, -2966.04, 13.95, 136.06),
vehicleSpawns = {
vector4(-974.98, -2977.90, 14.55, 59.91)
}
},
{
garageType = "heli",
ped = vector4(-731.00, -1394.54, 5.00, 245.52),
vehicleSpawns = {
vector4(-746.01, -1469.39, 5.68, 139.27),
vector4(-725.26, -1444.72, 5.68, 139.58)
}
},
{
garageType = "plane",
ped = vector4(1742.77, 3298.25, 41.22, 132.91),
vehicleSpawns = {
vector4(1734.26, 3250.98, 41.96, 81.11),
vector4(1729.32, 3270.69, 41.74, 145.39)
}
},
{
garageType = "plane",
ped = vector4(-1241.26, -3391.48, 13.94, 35.03),
vehicleSpawns = {
vector4(-1254.71, -3388.15, 14.54, 330.04),
vector4(-1270.36, -3378.80, 14.54, 330.20),
vector4(-1286.05, -3369.44, 14.54, 329.99)
}
},
{
garageType = "plane",
ped = vector4(-1621.10, -3151.63, 13.99, 41.26),
vehicleSpawns = {
vector4(-1634.04, -3147.84, 14.60, 330.28),
vector4(-1649.75, -3138.34, 14.60, 329.20),
vector4(-1665.42, -3129.26, 14.60, 330.10)
}
},
{
garageType = "heli",
ped = vector4(-1121.87, -2839.88, 13.95, 150.58),
vehicleSpawns = {
vector4(-1178.71, -2846.51, 14.62, 150.16),
vector4(-1146.40, -2865.22, 14.62, 151.08),
vector4(-1112.85, -2884.66, 14.62, 149.70)
}
},
{
garageType = "water",
ped = vector4(-831.03, -1359.53, 5.00, 299.80),
vehicleSpawns = {
vector4(-846.16, -1362.07, 0.39, 110.54),
vector4(-842.53, -1372.00, 0.39, 111.54),
vector4(-849.29, -1353.26, 0.38, 109.53),
vector4(-852.26, -1345.09, 0.41, 110.52),
vector4(-855.49, -1336.60, 0.40, 107.43),
vector4(-858.63, -1328.30, 0.40, 109.82),
vector4(-839.35, -1380.30, 0.39, 109.27),
vector4(-836.38, -1388.93, 0.41, 110.11),
vector4(-833.29, -1397.32, 0.40, 110.80),
vector4(-830.34, -1405.65, 0.37, 110.16)
}
},
{
garageType = "land",
ped = vector4(-280.32, -888.42, 31.32, 250.68),
vehicleSpawns = {
vector4(-282.36, -915.11, 30.38, 68.81),
vector4(-284.27, -918.37, 30.38, 70.58),
vector4(-285.44, -921.82, 30.38, 250.22),
vector4(-285.70, -887.62, 30.38, 167.04),
vector4(-292.70, -885.93, 30.38, 167.09),
vector4(-285.76, -887.47, 30.38, 169.59),
vector4(-300.44, -885.14, 30.38, 347.05),
vector4(-303.59, -883.71, 30.38, 167.69),
vector4(-309.38, -896.88, 30.38, 167.15),
vector4(-312.98, -896.30, 30.38, 166.46),
vector4(-316.68, -896.26, 30.37, 347.94),
vector4(-311.06, -881.94, 30.38, 168.21),
vector4(-314.78, -881.86, 30.37, 348.45)
}
},
{
garageType = "land",
ped = vector4(597.53, 91.08, 93.13, 250.54),
vehicleSpawns = {
vector4(598.53, 98.37, 92.27, 69.47),
vector4(599.79, 102.00, 92.27, 249.38),
vector4(608.21, 103.90, 92.18, 248.98),
vector4(600.60, 111.38, 92.27, 73.07),
vector4(609.91, 107.59, 92.23, 68.77),
vector4(601.36, 114.99, 92.27, 250.19),
vector4(611.07, 111.39, 92.29, 249.69),
vector4(603.52, 118.52, 92.26, 67.62),
vector4(612.74, 114.94, 92.28, 69.01),
vector4(604.00, 122.58, 92.27, 249.76),
vector4(613.78, 118.83, 92.29, 248.82),
vector4(622.43, 115.48, 91.99, 70.39),
vector4(628.56, 110.25, 91.47, 249.30),
vector4(620.67, 111.89, 92.03, 250.24),
vector4(618.41, 104.51, 91.97, 72.22),
vector4(624.87, 99.25, 91.36, 63.32),
vector4(616.59, 100.82, 91.97, 249.00)
}
},
{
garageType = "land",
ped = vector4(100.64, -1072.88, 29.37, 341.55),
vehicleSpawns = {
vector4(106.13, -1063.24, 28.51, 66.86),
vector4(107.82, -1059.73, 28.51, 66.99),
vector4(112.39, -1049.71, 28.52, 67.40),
vector4(110.76, -1053.09, 28.51, 68.13),
vector4(109.06, -1056.38, 28.51, 66.45),
vector4(117.53, -1081.03, 28.50, 181.40),
vector4(119.09, -1069.44, 28.50, 181.12),
vector4(121.22, -1081.42, 28.50, 0.57),
vector4(122.32, -1070.46, 28.50, 0.67),
vector4(125.62, -1069.54, 28.50, 179.25),
vector4(124.83, -1081.17, 28.50, 180.37),
vector4(128.62, -1081.71, 28.50, 1.36),
vector4(128.93, -1070.62, 28.50, 359.89),
vector4(132.24, -1069.35, 28.50, 181.54),
vector4(132.31, -1081.45, 28.50, 180.86),
vector4(135.93, -1081.35, 28.50, 358.82),
vector4(135.64, -1070.75, 28.50, 0.43),
vector4(139.75, -1081.91, 28.50, 182.18),
vector4(138.86, -1070.37, 28.50, 181.90),
vector4(143.55, -1081.41, 28.50, 0.89),
vector4(147.13, -1081.48, 28.50, 179.02),
vector4(150.95, -1081.49, 28.50, 358.79),
vector4(154.64, -1081.32, 28.50, 179.51),
vector4(158.38, -1082.03, 28.50, 1.57),
vector4(162.10, -1081.32, 28.50, 179.88)
}
},
{
garageType = "land",
ped = vector4(214.84, -806.24, 30.81, 342.23),
vehicleSpawns = {
vector4(251.26, -774.63, 29.98, 67.34),
vector4(245.36, -772.34, 30.02, 66.81),
vector4(219.19, -765.93, 30.14, 249.78),
vector4(228.11, -768.94, 30.10, 249.66),
vector4(233.68, -771.18, 30.07, 249.82),
vector4(244.01, -775.06, 29.99, 249.41),
vector4(249.53, -777.07, 29.95, 251.09),
vector4(248.74, -779.52, 29.92, 69.14),
vector4(243.64, -777.63, 29.96, 69.23),
vector4(233.47, -773.91, 30.05, 69.88),
vector4(218.62, -768.58, 30.14, 69.81),
vector4(217.02, -770.83, 30.16, 248.11),
vector4(226.09, -773.95, 30.09, 249.30),
vector4(231.82, -776.14, 30.04, 249.17),
vector4(242.05, -779.91, 29.93, 246.99),
vector4(247.72, -782.02, 29.88, 251.04),
vector4(216.19, -773.65, 30.16, 67.90),
vector4(225.82, -776.85, 30.08, 65.64),
vector4(230.77, -778.89, 30.03, 69.40),
vector4(241.72, -782.84, 29.90, 69.13),
vector4(247.12, -784.93, 29.84, 67.56),
vector4(246.23, -787.13, 29.82, 247.56),
vector4(239.40, -784.68, 29.90, 248.06),
vector4(230.42, -781.41, 30.01, 247.48),
vector4(224.14, -779.01, 30.07, 247.39),
vector4(215.32, -775.95, 30.17, 248.99),
vector4(243.81, -792.19, 29.77, 246.23),
vector4(238.08, -789.95, 29.85, 250.62),
vector4(228.14, -786.30, 30.01, 249.07),
vector4(221.98, -783.92, 30.08, 248.25),
vector4(213.41, -781.01, 30.19, 249.21),
vector4(214.77, -778.70, 30.17, 67.86),
vector4(224.02, -781.91, 30.07, 68.82),
vector4(229.34, -784.05, 30.01, 66.57),
vector4(239.87, -787.83, 29.86, 69.37),
vector4(245.18, -789.87, 29.79, 68.00),
vector4(241.85, -797.20, 29.72, 245.51),
vector4(236.57, -795.07, 29.82, 247.54),
vector4(226.12, -791.34, 29.99, 247.84),
vector4(220.61, -789.19, 30.08, 248.09),
vector4(212.00, -786.22, 30.21, 248.93),
vector4(212.91, -783.71, 30.19, 68.61),
vector4(222.11, -786.89, 30.07, 70.10),
vector4(227.84, -789.16, 29.99, 67.55),
vector4(237.73, -792.73, 29.82, 69.40),
vector4(243.54, -794.88, 29.73, 68.74),
vector4(237.03, -812.64, 29.59, 243.63),
vector4(207.66, -798.70, 30.29, 70.06),
vector4(216.56, -801.92, 30.10, 70.31),
vector4(222.28, -804.17, 29.98, 69.97),
vector4(232.41, -807.95, 29.74, 69.42),
vector4(238.16, -810.17, 29.60, 68.95),
vector4(238.32, -807.43, 29.63, 248.28),
vector4(233.32, -805.44, 29.75, 248.13),
vector4(222.46, -801.41, 29.98, 248.50),
vector4(216.85, -799.11, 30.10, 248.54),
vector4(207.60, -795.90, 30.29, 248.64),
vector4(209.28, -793.74, 30.25, 69.82),
vector4(218.31, -796.92, 30.08, 68.55),
vector4(224.50, -799.34, 29.96, 68.63),
vector4(234.40, -803.02, 29.76, 66.67),
vector4(240.09, -805.27, 29.64, 69.02),
vector4(240.53, -802.44, 29.67, 248.95),
vector4(234.46, -800.13, 29.79, 249.40),
vector4(224.30, -796.51, 29.98, 248.31),
vector4(218.23, -794.00, 30.09, 249.09),
vector4(209.84, -791.10, 30.24, 251.56),
vector4(211.09, -788.68, 30.22, 69.53),
vector4(220.17, -791.73, 30.06, 70.56),
vector4(225.84, -794.12, 29.98, 67.53),
vector4(236.38, -797.85, 29.79, 69.39),
vector4(216.56, -801.90, 30.79, 69.39)
}
},
}

265
client/vehicle/garages.lua Normal file
View File

@@ -0,0 +1,265 @@
local locations = require "client.vehicle.data"
local sprite = {
["water"] = 356,
["heli"] = 360,
["plane"] = 359,
["land"] = 357
}
local garageTypes = {
["water"] = 14,
["heli"] = 15,
["plane"] = 16
}
local clothing = {
{
face = {
drawable = 1,
texture = 1
},
undershirt = {
drawable = 0,
texture = 0
},
torso = {
drawable = 1,
texture = 1
},
leg = {
drawable = 0,
texture = 0
},
glasses = {
drawable = 1,
texture = 0
},
hat = {
drawable = -1,
texture = -1
},
},
{
leg = {
drawable = 0,
texture = 1
},
undershirt = {
drawable = 0,
texture = 0
},
face = {
drawable = 0,
texture = 0
},
torso = {
drawable = 0,
texture = 2
},
glasses = {
drawable = -1,
texture = -1
},
hat = {
drawable = 0,
texture = 0
},
},
{
face = {
drawable = 0,
texture = 2
},
undershirt = {
drawable = 0,
texture = 0
},
torso = {
drawable = 1,
texture = 2
},
leg = {
drawable = 0,
texture = 0
},
hat = {
drawable = -1,
texture = -1
},
glasses = {
drawable = -1,
texture = -1
},
}
}
local function getClosestOwnedVehicle()
local coords = GetEntityCoords(cache.ped)
local vehicles = lib.getNearbyVehicles(coords, 50.0, true)
local nearestVeh = {}
local function setNearestVehicle(veh)
local state = Entity(veh.vehicle).state
if not state.owner or state.owner ~= NDCore.player?.id then return end
local nearestDist = nearestVeh.dist
local dist = #(coords-veh.coords)
if not nearestDist or dist < nearestDist then
nearestVeh.dist = dist
nearestVeh.coords = veh.coords
nearestVeh.entity = veh.vehicle
end
end
for i=1, #vehicles do
setNearestVehicle(vehicles[i])
end
return nearestVeh.entity, nearestVeh.coords, nearestVeh.dist
end
local function parkVehicle(veh)
if not veh or not DoesEntityExist(veh) then
return NDCore.notify({
title = "Garage",
description = "No owned vehicle found nearby.",
type = "error",
position = "bottom",
duration = 3000
})
end
if GetPedInVehicleSeat(veh, -1) ~= 0 then
NDCore.notify({
title = "Garage",
description = "Player in vehicle!",
type = "error",
position = "bottom",
duration = 3000
})
return
end
local properties = lib.getVehicleProperties(veh)
properties.class = GetVehicleClass(veh)
TriggerServerEvent("ND_Vehicles:storeVehicle", VehToNet(veh))
end
local function isVehicleClassForGarage(class, garageType)
if not class then return true end
for garType, garClass in pairs(garageTypes) do
print(garType, garageType, garClass, class)
if garType == garageType and garClass == class then
return true
end
end
end
local function getEngineStatus(health)
if health > 950 then
return "Perfect"
elseif health > 750 then
return "Good"
elseif health > 500 then
return "Bad"
end
return "Very bad"
end
local function createMenuOptions(vehicle, vehicleSpawns)
local props = vehicle.properties
local makeName = GetLabelText(GetMakeNameFromVehicleModel(props.model))
local modelName = GetLabelText(GetDisplayNameFromVehicleModel(props.model))
local metadata = {
{label = "Plate", value = vehicle.properties.plate},
{label = "Fuel", value = ("%d%s"):format(vehicle.properties.fuelLevel, "%")},
{label = "Engine status", value = getEngineStatus(vehicle.properties.engineHealth)}
}
if not makeName or makeName == "NULL" then
makeName = ""
else
metadata[#metadata+1] = {label = "Make", value = makeName}
makeName = makeName .. " "
end
if not modelName or modelName == "NULL" then
modelName = ""
else
metadata[#metadata+1] = {label = "Model", value = modelName}
end
return {
title = ("%s%s"):format(makeName, modelName),
metadata = metadata,
onSelect = function(args)
TriggerServerEvent("ND_Vehicles:takeVehicle", vehicle.id, vehicleSpawns)
end,
}
end
local function createMenu(vehicles, garageType, vehicleSpawns)
local options = {
{
title = "Park vehicle",
onSelect = function(args)
local veh = getClosestOwnedVehicle()
parkVehicle(veh)
end
}
}
for _, vehicle in ipairs(vehicles) do
if vehicle.available and isVehicleClassForGarage(vehicle.properties.class, garageType) then
options[#options+1] = createMenuOptions(vehicle, vehicleSpawns)
end
end
return {
id = ("garage_%s"):format(garageType),
title = "Parking garage",
options = options,
onExit = function()
garageOpen = false
end
}
end
for i=1, #locations do
local location = locations[i]
NDCore.createAiPed({
model = `s_m_y_airworker`,
coords = location.ped,
distance = 45.0,
clothing = clothing[math.random(1, #clothing)],
blip = {
label = ("Parking garage (%s)"):format(location.garageType),
sprite = sprite[location.garageType],
scale = 0.7,
color = 3
},
anim = {
dict = "anim@amb@casino@valet_scenario@pose_d@",
clip = "base_a_m_y_vinewood_01"
},
options = {
{
name = "nd_core:garagePed",
icon = "fa-solid fa-warehouse",
label = "View garage",
distance = 2.0,
canInteract = function(entity, distance, coords, name, bone)
if not location.groups then return true end
local groups = location.groups
local playerGroups = NDCore.player?.groups
for i=1, #groups do
if playerGroups?[groups[i]] then
return true
end
end
end,
onSelect = function(data)
local vehicles = lib.callback.await("ND_Vehicles:getOwnedVehicles") or {}
local menu = createMenu(vehicles, location.garageType, location.vehicleSpawns)
lib.registerContext(menu)
lib.showContext(menu.id)
end
}
},
})
end

741
client/vehicle/main.lua Normal file
View File

@@ -0,0 +1,741 @@
local vehicleColorNames = {
[0] = "Black",
[1] = "Black",
[2] = "Black",
[3] = "Silver",
[4] = "Silver",
[5] = "Silver",
[6] = "Gray",
[7] = "Silver",
[8] = "Silver",
[9] = "Silver",
[10] = "Metal",
[11] = "Grey",
[12] = "Black",
[13] = "Gray",
[14] = "Grey",
[15] = "Black",
[16] = "Black",
[17] = "Silver",
[18] = "Silver",
[19] = "Metal",
[20] = "Silver",
[21] = "Black",
[22] = "Graphite",
[23] = "Silver",
[24] = "Silver",
[25] = "Silver",
[26] = "Silver",
[27] = "Red",
[28] = "Red",
[29] = "Red",
[30] = "Red",
[31] = "Red",
[32] = "Red",
[33] = "Red",
[34] = "Red",
[35] = "Red",
[36] = "Orange",
[37] = "Gold",
[38] = "Orange",
[39] = "Red",
[40] = "Red",
[41] = "Orange",
[42] = "Yellow",
[43] = "Red",
[44] = "Red",
[45] = "Red",
[46] = "Red",
[47] = "Red",
[48] = "Red",
[49] = "Green",
[50] = "Green",
[51] = "Green",
[52] = "Green",
[53] = "Green",
[54] = "Green",
[55] = "Green",
[56] = "Green",
[57] = "Green",
[58] = "Green",
[59] = "Green",
[60] = "Green",
[61] = "Blue",
[62] = "Blue",
[63] = "Blue",
[64] = "Blue",
[65] = "Blue",
[66] = "Blue",
[67] = "Blue",
[68] = "Blue",
[69] = "Blue",
[70] = "Blue",
[71] = "Blue",
[72] = "Blue",
[73] = "Blue",
[74] = "Blue",
[75] = "Blue",
[76] = "Blue",
[77] = "Blue",
[78] = "Blue",
[79] = "Bblue",
[80] = "Blue",
[81] = "Blue",
[82] = "Blue",
[83] = "Blue",
[84] = "Blue",
[85] = "Blue",
[86] = "Blue",
[87] = "Blue",
[88] = "Yellow",
[89] = "Yellow",
[90] = "Bronze",
[91] = "Yellow",
[92] = "Lime",
[93] = "Champagne",
[94] = "Beige",
[95] = "Ivory",
[96] = "Brown",
[97] = "Brown",
[98] = "Brown",
[99] = "Beige",
[100] = "Brown",
[101] = "Brown",
[102] = "Beechwood",
[103] = "Beechwood",
[104] = "Orange",
[105] = "Sand",
[106] = "Sand",
[107] = "Cream",
[108] = "Brown",
[109] = "Brown",
[110] = "Brown",
[111] = "White",
[112] = "White",
[113] = "Beige",
[114] = "Brown",
[115] = "Brown",
[116] = "Beige",
[117] = "Steel",
[118] = "Steel",
[119] = "Aluminium",
[120] = "Chrome",
[121] = "White",
[122] = "White",
[123] = "Orange",
[124] = "Orange",
[125] = "Green",
[126] = "Yellow",
[127] = "Blue",
[128] = "Green",
[129] = "Brown",
[130] = "Orange",
[131] = "White",
[132] = "White",
[133] = "Green",
[134] = "White",
[135] = "Pink",
[136] = "pink",
[137] = "Pink",
[138] = "Orange",
[139] = "Green",
[140] = "Blue",
[141] = "Black",
[142] = "Black",
[143] = "Black",
[144] = "Green",
[145] = "Purple",
[146] = "Blue",
[147] = "Black",
[148] = "Purple",
[149] = "Purple",
[150] = "Red",
[151] = "Green",
[152] = "Green",
[153] = "Brown",
[154] = "Tan",
[155] = "Green",
[156] = "ALLOY",
[157] = "Blue",
}
local vehicleClassNames = {
[0] = "Compact",
[1] = "Sedan",
[2] = "SUV",
[3] = "Coupe",
[4] = "Muscle",
[5] = "Sports Classic",
[6] = "Sport",
[7] = "Super",
[8] = "Motorcycle",
[9] = "Off-road",
[10] = "Industrial",
[11] = "Utility",
[12] = "Van",
[13] = "Cycle",
[14] = "Boat",
[15] = "Helicopter",
[16] = "Plane",
[17] = "Service",
[18] = "Emergency",
[19] = "Military",
[20] = "Commercial",
[21] = "Train",
[22] = "Open wheel"
}
local cruiseSpeedSet = 0
local cruiseSpeedVehicle = 0
local cruiseControlEnabled = false
local playerVehicle = cache.seat == -1 and cache.vehicle
local cloudTime = GetCloudTimeAsInt()
local vehicleLockCheckTime = {
lastCheck = cloudTime,
lastUse = cloudTime
}
local keyCheckTime = {
lastCheck = cloudTime,
hasKey = false
}
local vehicleClassNotDisableAirControl = {
[8] = true, --motorcycle
[13] = true, --bicycles
[14] = true, --boats
[15] = true, --helicopter
[16] = true, --plane
[19] = true --military
}
local function getVehicleBlipSprite(entity)
if not IsEntityAVehicle(entity) then
return 148 -- circle blip
end
local class = GetVehicleClass(entity)
local model = GetEntityModel(entity)
local classBlip = {
[16] = 423, -- plane
[8] = 226, -- motorcycle
[15] = 64, -- helicopter
[14] = 427, -- boat
[6] = 825, -- sports
[7] = 523, -- super
[2] = 821, -- SUV
[4] = 663 -- muscle
}
local typeBlip = {
[`seashark`] = 471,
[`marquis`] = 410,
[`rhino`] = 421,
[`hydra`] = 424,
[`lazer`] = 424,
[`taxi`] = 198,
[`trash`] = 318,
[`trash2`] = 318
}
return typeBlip[model] or classBlip[class] or 225 -- 255 is default car blip
end
RegisterNetEvent("ND_Vehicles:blip", function(netId, status)
local time = GetCloudTimeAsInt()
while not NetworkDoesNetworkIdExist(netId) or not NetworkDoesEntityExistWithNetworkId(netId) and time-GetCloudTimeAsInt() < 5 do
Wait(100)
end
local veh = NetToVeh(netId)
if not veh then return end
if not status then
local blip = GetBlipFromEntity(veh)
if not blip or not DoesBlipExist(blip) then return end
return RemoveBlip(blip)
end
local blip = AddBlipForEntity(veh)
SetBlipSprite(blip, getVehicleBlipSprite(veh))
SetBlipColour(blip, 0)
SetBlipScale(blip, 0.8)
SetBlipAsShortRange(blip, true)
BeginTextCommandSetBlipName("STRING")
AddTextComponentSubstringPlayerName("Personal vehicle")
EndTextCommandSetBlipName(blip)
end)
RegisterNetEvent("ND_Vehicles:syncAlarm", function(netid)
local veh = NetToVeh(netid)
if not veh then return end
SetVehicleAlarmTimeLeft(veh, 1)
SetVehicleAlarm(veh, true)
StartVehicleAlarm(veh)
end)
RegisterNetEvent("ND_VehicleSystem:setOwnedIfNot", function(netid)
local veh = NetToVeh(netid)
if not veh then return end
setVehicleOwned(veh, true)
setVehicleLocked(veh, true)
end)
AddStateBagChangeHandler("props", nil, function(bagName, key, value, reserved, replicated)
local entity = GetEntityFromStateBagName(bagName)
if entity == 0 or not value or not NetworkGetEntityOwner(entity) == cache.playerId then return end
lib.setVehicleProperties(entity, value)
end)
local function playKeyFob(veh)
local keyFob
local ped = cache.ped
local coords = GetEntityCoords(ped)
if #(coords-GetEntityCoords(veh)) > 25.0 then return end
if not playerVehicle then
ClearPedTasks(ped)
lib.requestAnimDict("anim@mp_player_intmenu@key_fob@")
TaskPlayAnim(ped, "anim@mp_player_intmenu@key_fob@", "fob_click_fp", 8.0, 8.0, -1, 48, 1, false, false, false)
keyFob = CreateObject(`lr_prop_carkey_fob`, 0, 0, 0, true, true, true)
AttachEntityToEntity(keyFob, ped, GetPedBoneIndex(ped, 0xDEAD), 0.12, 0.04, -0.025, -100.0, 100.0, 0.0, true, true, false, true, 1, true)
Wait(700)
end
PlaySoundFromEntity(-1, "Remote_Control_Fob", ped, "PI_Menu_Sounds", true, 0)
SetVehicleLights(veh, 2)
Wait(100)
SetVehicleLights(veh, 0)
Wait(200)
SetVehicleLights(veh, 2)
Wait(100)
SetVehicleLights(veh, 0)
return keyFob and DeleteEntity(keyFob)
end
RegisterNetEvent("ND_Vehicles:keyFob", function(vehicleNetId)
playKeyFob(NetToVeh(vehicleNetId))
end)
local dontLock = {
[8] = true, -- Motorcycles
[13] = true, -- Cycles
[14] = true -- boats
}
AddStateBagChangeHandler("locked", nil, function(bagName, key, value, reserved, replicated)
local entity = GetEntityFromStateBagName(bagName)
if entity == 0 or value == nil or dontLock[GetVehicleClass(entity)] then return end
if value then
-- SetVehicleDoorsLockedForAllPlayers(entity, true)
return SetVehicleDoorsLocked(entity, 2)
end
CreateThread(function()
while GetVehiclePedIsEntering(cache.ped) == entity do Wait(10) end
-- SetVehicleDoorsLockedForAllPlayers(entity, false)
SetVehicleDoorsLocked(entity, 0)
end)
end)
lib.callback.register("ND_Vehicles:getProps", function()
local veh = GetVehiclePedIsIn(cache.ped)
local props = lib.getVehicleProperties(veh)
local colorPrimary, colorSecondary = GetVehicleColours(veh)
props.colorNamePrimary = vehicleColorNames[colorPrimary]
props.colorNameSecondary = vehicleColorNames[colorSecondary]
props.colorName = props.colorNamePrimary == props.colorNameSecondary and props.colorNamePrimary or ("%s & %s"):format(props.colorNamePrimary, props.colorNameSecondary)
props.className = vehicleClassNames[GetVehicleClass(veh)]
props.makeName = GetLabelText(GetMakeNameFromVehicleModel(props.model))
props.modelName = GetLabelText(GetDisplayNameFromVehicleModel(props.model))
return props
end)
lib.callback.register("ND_Vehicles:getVehicleModelMakeLabel", function(model)
local make = GetLabelText(GetMakeNameFromVehicleModel(model))
local name = GetLabelText(GetDisplayNameFromVehicleModel(model))
if make == "NULL" then
return name
elseif name == "NULL" then
return make
end
return ("%s %s"):format(make, name)
end)
local function hasVehicleKeys(veh, checkEngine)
local state = Entity(veh).state
if Config.ox_inventory and Config.useInventoryForKeys then
local metadata = {
vehPlate = GetVehicleNumberPlateText(veh),
keyEnabled = true
}
local hasKey = exports.ox_inventory:GetItemCount("keys", metadata) > 0
return hasKey or checkEngine and state.hotwired
end
local keys = state and state.keys
local player = NDCore.getPlayer()
local hasKey = player and keys and keys[player.id]
return hasKey or checkEngine and state.hotwired
end
local function hasVehicleKeysCheck(veh)
local time = GetCloudTimeAsInt()
if time-keyCheckTime.lastCheck < 5 then
return keyCheckTime.hasKey
end
local hasKey = hasVehicleKeys(veh, true)
keyCheckTime.lastCheck = time
keyCheckTime.hasKey = hasKey
return hasKey
end
local function getNearestVehicle(hasKeysForOnly)
local coords = GetEntityCoords(cache.ped)
local vehicles = lib.getNearbyVehicles(coords, 25.0, true)
local nearestVeh = {}
local function setNearestVehicle(veh)
if hasKeysForOnly and not hasVehicleKeys(veh.vehicle) then return end
local nearestDist = nearestVeh.dist
local dist = #(coords-veh.coords)
if not nearestDist or dist < nearestDist then
nearestVeh.dist = dist
nearestVeh.coords = veh.coords
nearestVeh.entity = veh.vehicle
end
end
for i=1, #vehicles do
setNearestVehicle(vehicles[i])
end
return nearestVeh.entity, nearestVeh.coords, nearestVeh.dist
end
local vehicleLockKeybind = lib.addKeybind({
name = "vehicleKey",
description = "Unlock/lock vehicle (double click)",
defaultKey = "E",
onPressed = function(self)
local time = GetCloudTimeAsInt()
if time-vehicleLockCheckTime.lastCheck < 1 and time-vehicleLockCheckTime.lastUse > 1 then
vehicleLockCheckTime.lastUse = time
local veh = getNearestVehicle(true)
if not veh then return end
TriggerServerEvent("ND_Vehicles:toggleVehicleLock", VehToNet(veh))
end
vehicleLockCheckTime.lastCheck = time
end
})
NDCore.isResourceStarted("ox_inventory", function(started)
Config.ox_inventory = started
if not started or not Config.useInventoryForKeys then
return vehicleLockKeybind:disable(false)
end
Wait(1000)
vehicleLockKeybind:disable(true)
exports.ox_inventory:displayMetadata({
vehPlate = "Plate",
vehModel = "Model"
})
end)
-- save wheels steering angle.
CreateThread(function()
local angle = 0.0
while true do
Wait(300)
if playerVehicle then
if GetIsTaskActive(cache.ped, 2) then
SetVehicleSteeringAngle(playerVehicle, angle)
end
angle = DoesEntityExist(playerVehicle) and GetVehicleSteeringAngle(playerVehicle)
end
end
end)
CreateThread(function()
local wait = 500
while true do
Wait(wait)
playerVehicle = cache.seat == -1 and cache.vehicle
if playerVehicle then
if Config.disableVehicleAirControl and not vehicleClassNotDisableAirControl[GetVehicleClass(playerVehicle)] and (IsEntityInAir(playerVehicle) or IsEntityUpsidedown(playerVehicle)) then
wait = 0
DisableControlAction(0, 59) -- disable vehicle air control.
DisableControlAction(0, 60)
elseif not GetIsVehicleEngineRunning(playerVehicle) and not hasVehicleKeysCheck(playerVehicle) then
wait = 0
DisableControlAction(0, 59)
if DoesEntityExist(playerVehicle) and IsVehicleEngineStarting(playerVehicle) then
SetVehicleEngineOn(playerVehicle, false, true, true) -- don't turn on engine if no keys.
end
else
wait = 500
end
elseif wait ~= 500 then
wait = 500
end
end
end)
local function hotwireVehicle()
local state = playerVehicle and Entity(playerVehicle).state
if not playerVehicle or state.hotwired then return end
local finished = false
lib.requestModel(`imp_prop_impexp_pliers_02`)
lib.requestModel(`prop_tool_screwdvr01`)
lib.requestAnimDict("veh@handler@base")
if GetFollowVehicleCamViewMode() > 2 then
SetFollowVehicleCamViewMode(0)
end
CreateThread(function()
while not finished do
Wait(0)
DisableFirstPersonCamThisFrame()
end
end)
local modelWithHandles = {`seashark`, `seashark2`, `seashark3`}
local vehicleModel = GetEntityModel(playerVehicle)
local bikeHandles = GetVehicleClass(playerVehicle) == 8 or lib.table.contains(modelWithHandles, vehicleModel)
local success = lib.progressCircle({
duration = math.random(10000, 20000),
label = "Hotwiring",
useWhileDead = false,
allowRagdoll = false,
allowCuffed = false,
allowFalling = false,
canCancel = true,
anim = {
dict = bikeHandles and "anim@veh@boat@jetski@front@base" or "veh@handler@base",
clip = "hotwire"
},
disable = {
move = true,
car = true,
combat = true
},
prop = {
{
model = `imp_prop_impexp_pliers_02`,
bone = 0x49D9, -- SKEL_R_Hand
pos = vec3(0.1, -0.05, 0.0),
rot = vec3(-1.5, -15.0, -1.5)
},
{
model = `prop_tool_screwdvr01`,
bone = 0xDEAD, -- SKEL_R_Hand
pos = vec3(0.1, 0.08, -0.03),
rot = vec3(90.0, 0.0, 0.0)
}
}
})
finished = true
if not success then return end
if not playerVehicle then return false, true end
TriggerServerEvent("ND_Vehicles:hotwire", VehToNet(playerVehicle))
return true, true
end
local function lockpickVehicle()
if cache.vehicle then return end
local pos = GetEntityCoords(cache.ped)
local rot = GetEntityRotation(cache.ped, 2)
local veh = lib.getClosestVehicle(pos, 2.5, false)
if not veh then return end
local dificulties = {
"easy",
"medium",
"hard"
}
local dificultyTime = {
easy = 500,
medium = 800,
hard = 1000
}
lib.requestAnimDict("veh@break_in@0h@p_m_one@")
for i=1, 7 do
TaskPlayAnimAdvanced(cache.ped, "veh@break_in@0h@p_m_one@", "std_force_entry_ds", pos.x, pos.y, pos.z+0.025, rot.x, rot.y, rot.z, 8.0, 8.0, 1800, 28, 0.1)
local dificulty = dificulties[math.random(1, #dificulties)]
local success = lib.skillCheck(dificulty)
if not success or not DoesEntityExist(veh) or #(pos-GetEntityCoords(veh)) > 2.5 then
TriggerServerEvent("ND_Vehicles:lockpick", VehToNet(veh), false)
return false, true
end
Wait(dificultyTime[dificulty])
end
veh = lib.getClosestVehicle(pos, 2.5, false)
if not veh then return false, true end
TriggerServerEvent("ND_Vehicles:lockpick", VehToNet(veh), true)
PlaySoundFromEntity(-1, "Remote_Control_Fob", cache.ped, "PI_Menu_Sounds", true, 0)
return true, true
end
exports("lockpick", function(data, slot)
local _, used = lockpickVehicle()
if used then
exports.ox_inventory:useItem(data)
end
end)
exports("hotwire", function(data, slot)
local _, used = hotwireVehicle()
if used then
exports.ox_inventory:useItem(data)
end
end)
exports("keyControl", function(action, slot)
for item, data in pairs(exports.ox_inventory:Items()) do
local metadata = data.metadata
if data.slot == slot then
if metadata and not metadata.keyEnabled then
return lib.notify({
title = "No signal",
description = "Vehicle key disabled.",
type = "error",
position = "bottom-right",
duration = 3000
})
end
break
end
end
exports.ox_inventory:closeInventory()
if action == "trunk" then
local veh = getNearestVehicle(true)
if not veh then return end
playKeyFob(veh)
if GetVehicleDoorAngleRatio(veh, 5) > 0.0 then
SetVehicleDoorShut(veh, 5)
else
SetVehicleDoorOpen(veh, 5, false)
end
elseif action == "disable" then
TriggerServerEvent("ND_Vehicles:disableKey", slot)
lib.notify({
title = "Key disabled",
description = "This vehicle key has been disabled and cannot be used anymore.",
type = "inform",
position = "bottom-right",
duration = 3000
})
end
end)
local function cruiseControl()
cruiseSpeedVehicle = GetEntitySpeed(playerVehicle) * 2.236936
if not playerVehicle then
lib.notify({
title = "Cruise control",
description = "Vehicle cruise control disabled.",
type = "inform",
position = "bottom-right",
duration = 3000
})
return
end
if cruiseSpeedVehicle < cruiseSpeedSet/3 then
lib.notify({
title = "Cruise control",
description = "Vehicle cruise control disabled.",
type = "inform",
position = "bottom-right",
duration = 3000
})
return
end
if cruiseSpeedVehicle < cruiseSpeedSet then
SetControlNormal(0, 71, 0.6)
end
return true
end
lib.addKeybind({
name = "vehicleCruiseControl",
description = "Toggle vehicle cruise control",
defaultKey = "",
onPressed = function(self)
if cruiseControlEnabled and cruiseSpeedVehicle-1 > cruiseSpeedSet then
cruiseSpeedSet = cruiseSpeedVehicle
return lib.notify({
title = "Cruise control",
description = ("Increased to %d mph."):format(math.floor(cruiseSpeedSet)),
type = "inform",
position = "bottom-right",
duration = 3000
})
elseif cruiseControlEnabled then
cruiseControlEnabled = false
return lib.notify({
title = "Cruise control",
description = "Vehicle cruise control disabled.",
type = "inform",
position = "bottom-right",
duration = 3000
})
end
if not playerVehicle then return end
cruiseControlEnabled = true
cruiseSpeedVehicle = math.floor(GetEntitySpeed(playerVehicle) * 2.236936)
cruiseSpeedSet = cruiseSpeedVehicle
lib.notify({
title = "Cruise control",
description = ("Set to %d mph."):format(math.floor(cruiseSpeedSet)),
type = "inform",
position = "bottom-right",
duration = 3000
})
CreateThread(function()
while cruiseControlEnabled and cruiseControl() do Wait(0) end
cruiseControlEnabled = false
end)
end
})
lib.addKeybind({
name = "vehicleShuffleSeat",
description = "Suffle vehicle seat.",
defaultKey = "",
onPressed = function(self)
if not cache.vehicle then return end
local seats = {
[-1] = 0,
[0] = -1,
[1] = 2,
[2] = 1,
[3] = 4,
[4] = 3,
[5] = 6,
[6] = 5
}
SetPedIntoVehicle(cache.ped, cache.vehicle, seats[cache.seat])
end
})
lib.onCache("ped", function(value)
SetPedConfigFlag(value, 184, true)
end)
lib.onCache("vehicle", function(value)
local veh = value or cache.vehicle
local blip = GetBlipFromEntity(veh)
if not blip or not DoesBlipExist(blip) then return end
SetBlipAlpha(blip, value and 0 or 255)
end)
SetTimeout(500, function()
SetPedConfigFlag(cache.ped, 184, true)
end)