Repository navigation
Expand file tree
/
Copy pathclient.lua
More file actions
365 lines (339 loc) · 13.9 KB
/
Copy pathclient.lua
File metadata and controls
365 lines (339 loc) · 13.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
local isUIOpen = false -- Variable to track if the UI is open or not
local playerSettings = {} -- Table to store ui settings of the player opening it
local freeCameraEnabled = false -- Variable to track if the free camera is enabled or not
--- Prints debug messages when debugging is enabled.
--- @param ... vararg Values to print.
--- @return nil
local DebugPrint = function(...)
if Config.Debug then
print(...)
end
end
--- Retrieves vehicle data including doors, windows, and neon availability.
--- @param vehicle number The vehicle entity.
--- @return table Table with fields 'doors', 'windows', and 'hasNeons'.
local GetVehicleData = function(vehicle)
local vehicleData = {}
local model = GetEntityModel(vehicle)
local numDoors = GetNumberOfVehicleDoors(vehicle)
DebugPrint("Vehicle has " .. numDoors .. " doors (including hood/trunk)")
local maxSeats = GetVehicleModelNumberOfSeats(model)
DebugPrint("Vehicle has " .. maxSeats .. " seats")
vehicleData.doors = {
fl = DoesVehicleHaveDoor(vehicle, 0),
fr = DoesVehicleHaveDoor(vehicle, 1),
rl = DoesVehicleHaveDoor(vehicle, 2) and numDoors > 2,
rr = DoesVehicleHaveDoor(vehicle, 3) and numDoors > 2,
hood = DoesVehicleHaveDoor(vehicle, 4),
trunk = DoesVehicleHaveDoor(vehicle, 5)
}
vehicleData.windows = {
fl = true,
fr = true,
rl = maxSeats > 2,
rr = maxSeats > 2
}
local neonCount = GetNumVehicleMods(vehicle, 48) or 0
vehicleData.hasNeons = neonCount > 0
DebugPrint("Has neon kit:", vehicleData.hasNeons)
vehicleData.class = GetVehicleClass(vehicle)
DebugPrint("Vehicle class:", vehicleData.class)
return vehicleData
end
--- Finds accessible seats for the vehicle.
--- @param vehicle number The vehicle entity.
--- @return table List of seat descriptors.
local GetAvailableSeats = function(vehicle)
local seats = {}
local maxSeats = GetVehicleModelNumberOfSeats(GetEntityModel(vehicle))
DebugPrint("Vehicle has " .. maxSeats .. " seats")
table.insert(seats, { id = "driver", name = "Driver Seat", index = -1 })
if maxSeats >= 2 then
table.insert(seats, { id = "passenger", name = "Front Passenger", index = 0 })
end
if maxSeats >= 3 then
table.insert(seats, { id = "rear_left", name = "Rear Left", index = 1 })
end
if maxSeats >= 4 then
table.insert(seats, { id = "rear_right", name = "Rear Right", index = 2 })
end
for i = 3, maxSeats - 2 do
if IsVehicleSeatAccessible(PlayerPedId(), vehicle, i, false, false) then
table.insert(seats, { id = "seat_" .. i, name = "Seat " .. (i + 1), index = i })
end
end
DebugPrint("Found " .. #seats .. " accessible seats")
return seats
end
--- Opens the Vehicle Control UI and sends data to NUI.
--- @return nil
local OpenUI = function()
if not isUIOpen then
isUIOpen = true
SetNuiFocus(true, true)
SetNuiFocusKeepInput(false)
local playerPed = PlayerPedId()
local vehicle = GetVehiclePedIsIn(playerPed, false)
if vehicle ~= 0 then
local vehicleData = GetVehicleData(vehicle)
SendNUIMessage({ type = "vehicleData", vehicleData = vehicleData })
local seats = GetAvailableSeats(vehicle)
SendNUIMessage({ type = "availableSeats", seats = seats })
end
SendNUIMessage({ type = "show" })
DebugPrint("Vehicle Control UI opened")
end
end
exports('OpenMenu', OpenUI)
RegisterNetEvent('sd-vehcontrol:client:openMenu', OpenUI)
--- Closes the Vehicle Control UI.
--- @return nil
local CloseUI = function()
if isUIOpen then
isUIOpen = false
SetNuiFocus(false, false)
SendNUIMessage({ type = "hide" })
DebugPrint("Vehicle Control UI closed")
end
end
--- Loads player settings from KVP.
--- @return table The loaded settings.
local LoadSettings = function()
local settingsJson = GetResourceKvpString("vehcontrol_settings")
if settingsJson then
playerSettings = json.decode(settingsJson) or {}
else
playerSettings = {}
end
DebugPrint("Settings loaded:", json.encode(playerSettings))
return playerSettings
end
--- Saves player settings to KVP.
--- @param settings table The settings to save.
--- @return nil
local SaveSettings = function(settings)
playerSettings = settings
SetResourceKvp("vehcontrol_settings", json.encode(settings))
DebugPrint("Settings saved:", json.encode(settings))
end
--- Handles the 'vehcontrol' command; opens UI if in vehicle.
--- @return nil
RegisterCommand(Config.CommandName, function()
local playerPed = PlayerPedId()
if IsPedInAnyVehicle(playerPed, false) then
OpenUI()
else
TriggerEvent("chat:addMessage", {
color = {255, 0, 0},
multiline = true,
args = {"Vehicle Control", "You must be in a vehicle to use this command."}
})
end
end, false)
--- Registers the key mapping for opening the UI.
--- @return nil
RegisterKeyMapping(Config.CommandName, "Open Vehicle Control UI", "keyboard", Config.KeyBind)
--- Closes UI when NUI sends 'close'.
--- @param data table NUI data.
--- @param cb function Callback to invoke.
--- @return nil
RegisterNUICallback("close", function(data, cb)
CloseUI()
cb({})
end)
--- Sends available seats to NUI.
--- @param data table NUI data.
--- @param cb function Callback to invoke.
--- @return nil
RegisterNUICallback("getAvailableSeats", function(data, cb)
local playerPed = PlayerPedId()
local vehicle = GetVehiclePedIsIn(playerPed, false)
if vehicle ~= 0 then
local seats = GetAvailableSeats(vehicle)
SendNUIMessage({ type = "availableSeats", seats = seats })
end
cb({})
end)
--- Sends vehicle data to NUI.
--- @param data table NUI data.
--- @param cb function Callback to invoke.
--- @return nil
RegisterNUICallback("getVehicleData", function(data, cb)
local playerPed = PlayerPedId()
local vehicle = GetVehiclePedIsIn(playerPed, false)
if vehicle ~= 0 then
local vehicleData = GetVehicleData(vehicle)
SendNUIMessage({ type = "vehicleData", vehicleData = vehicleData })
end
cb({})
end)
--- Handles UI actions for engine, doors, windows, lights, etc.
--- @param data table Action data containing 'action', 'state', or 'seat'.
--- @param cb function Callback to invoke.
--- @return nil
RegisterNUICallback("action", function(data, cb)
local playerPed = PlayerPedId()
local vehicle = GetVehiclePedIsIn(playerPed, false)
if vehicle ~= 0 then
if data.action == "engine" then
local isEngineRunning = GetIsVehicleEngineRunning(vehicle)
SetVehicleEngineOn(vehicle, not isEngineRunning, false, true)
DebugPrint("Engine toggled:", not isEngineRunning)
elseif data.action:find("door_") == 1 then
local doorIndex = -1
if data.action == "door_fl" then doorIndex = 0
elseif data.action == "door_fr" then doorIndex = 1
elseif data.action == "door_rl" then doorIndex = 2
elseif data.action == "door_rr" then doorIndex = 3 end
if doorIndex ~= -1 and DoesVehicleHaveDoor(vehicle, doorIndex) then
if GetVehicleDoorAngleRatio(vehicle, doorIndex) > 0.0 then
SetVehicleDoorShut(vehicle, doorIndex, false)
else
SetVehicleDoorOpen(vehicle, doorIndex, false, false)
end
DebugPrint("Door toggled:", doorIndex)
end
elseif data.action:find("window_") == 1 then
local windowIndex = -1
if data.action == "window_fl" then windowIndex = 0
elseif data.action == "window_fr" then windowIndex = 1
elseif data.action == "window_rl" then windowIndex = 2
elseif data.action == "window_rr" then windowIndex = 3 end
if windowIndex ~= -1 then
if IsVehicleWindowIntact(vehicle, windowIndex) then
RollDownWindow(vehicle, windowIndex)
else
RollUpWindow(vehicle, windowIndex)
end
DebugPrint("Window toggled:", windowIndex)
end
elseif data.action == "hood" then
if DoesVehicleHaveDoor(vehicle, 4) then
if GetVehicleDoorAngleRatio(vehicle, 4) > 0.0 then
SetVehicleDoorShut(vehicle, 4, false)
else
SetVehicleDoorOpen(vehicle, 4, false, false)
end
DebugPrint("Hood toggled")
end
elseif data.action == "trunk" then
if DoesVehicleHaveDoor(vehicle, 5) then
if GetVehicleDoorAngleRatio(vehicle, 5) > 0.0 then
SetVehicleDoorShut(vehicle, 5, false)
else
SetVehicleDoorOpen(vehicle, 5, false, false)
end
DebugPrint("Trunk toggled")
end
elseif data.action == "indicator_left" then
SetVehicleIndicatorLights(vehicle, 1, data.state)
DebugPrint("Left indicator toggled:", data.state)
elseif data.action == "indicator_right" then
SetVehicleIndicatorLights(vehicle, 0, data.state)
DebugPrint("Right indicator toggled:", data.state)
elseif data.action == "hazard" then
SetVehicleIndicatorLights(vehicle, 0, data.state)
SetVehicleIndicatorLights(vehicle, 1, data.state)
DebugPrint("Hazard lights toggled:", data.state)
elseif data.action == "lock" then
local lockStatus = GetVehicleDoorLockStatus(vehicle)
if lockStatus == 1 or lockStatus == 0 then
SetVehicleDoorsLocked(vehicle, 2)
PlayVehicleDoorCloseSound(vehicle, 1)
else
SetVehicleDoorsLocked(vehicle, 1)
PlayVehicleDoorOpenSound(vehicle, 0)
end
DebugPrint("Lock toggled")
elseif data.action:find("seat_") == 1 then
if data.seat and IsVehicleSeatFree(vehicle, data.seat) then
TaskWarpPedIntoVehicle(playerPed, vehicle, data.seat)
DebugPrint("Changed to seat:", data.seat)
else
DebugPrint("Seat is occupied or invalid:", data.seat)
end
elseif data.action == "lights_interior" then
SetVehicleInteriorlight(vehicle, not IsVehicleInteriorLightOn(vehicle))
DebugPrint("Interior lights toggled")
elseif data.action:find("neon_") == 1 then
local idxMap = { neon_front = 2, neon_rear = 3, neon_left = 0, neon_right = 1 }
if data.action ~= "neon_all" then
local idx = idxMap[data.action]
SetVehicleNeonLightEnabled(vehicle, idx, not IsVehicleNeonLightEnabled(vehicle, idx))
DebugPrint(data.action .. " toggled")
else
local state = not IsVehicleNeonLightEnabled(vehicle, 0)
for i = 0, 3 do SetVehicleNeonLightEnabled(vehicle, i, state) end
DebugPrint("All neons toggled")
end
elseif data.action == "toggle_all" then
local state, vd = data.state, GetVehicleData(vehicle)
for i = 0, 5 do
if DoesVehicleHaveDoor(vehicle, i) then
if state then SetVehicleDoorOpen(vehicle, i, false, false)
else SetVehicleDoorShut(vehicle, i, false) end
end
end
for i = 0, 3 do
if (i <= 1) or vd.windows.rl then
if state then RollDownWindow(vehicle, i) else RollUpWindow(vehicle, i) end
end
end
DebugPrint("Toggled all doors and windows:", state)
end
end
cb({})
end)
--- Saves settings received from NUI.
--- @param data table The settings data.
--- @param cb function Callback to invoke.
--- @return nil
RegisterNUICallback("saveSettings", function(data, cb)
SaveSettings(data)
cb({})
end)
--- Loads settings and sends them to NUI.
--- @param data table NUI data.
--- @param cb function Callback to invoke.
--- @return nil
RegisterNUICallback("loadSettings", function(data, cb)
local settings = LoadSettings()
SendNUIMessage({ type = "settings", settings = settings })
cb({})
end)
--- Event handler for resource start; loads settings.
--- @param resourceName string The resource name.
--- @return nil
AddEventHandler('onResourceStart', function(resourceName)
if GetCurrentResourceName() ~= resourceName then return end
DebugPrint('Vehicle Control UI started')
LoadSettings()
end)
--- Event handler for resource stop; closes UI if open.
--- @param resourceName string The resource name.
--- @return nil
AddEventHandler('onResourceStop', function(resourceName)
if GetCurrentResourceName() ~= resourceName then return end
if isUIOpen then CloseUI() end
DebugPrint('Vehicle Control UI stopped')
end)
--- Monitors player exit and closes UI if open.
--- @return nil
CreateThread(function()
while true do
Wait(500)
if isUIOpen and not IsPedInAnyVehicle(PlayerPedId(), false) then
CloseUI()
end
end
end)
--- Handles NUI toggle for free camera (“freeview”) mode.
--- @param data table NUI data (data.enabled == true to enable free camera)
--- @param cb function Callback to invoke.
--- @return nil
RegisterNUICallback("toggleFreeCamera", function(data, cb)
freeCameraEnabled = data.enabled
SetNuiFocusKeepInput(freeCameraEnabled)
DebugPrint("Free camera toggled:", freeCameraEnabled)
cb({})
end)