-
Notifications
You must be signed in to change notification settings - Fork 17
/
Copy pathclient.lua
301 lines (247 loc) · 13.3 KB
/
client.lua
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
if Config.EnableTowCommands then
CreateThread(function()
TriggerEvent('chat:addSuggestion', '/tow', 'Start the process to tow/attach a vehicle.')
TriggerEvent('chat:addSuggestion', '/untow', 'Start the process to untow a specific attached vehicle.')
end)
end
-- Requests network control of an entity
local function RequestNetworkControlOfObject(netId, itemEntity)
if NetworkDoesNetworkIdExist(netId) then
NetworkRequestControlOfNetworkId(netId)
while not NetworkHasControlOfNetworkId(netId) do
Wait(100)
NetworkRequestControlOfNetworkId(netId)
end
end
if DoesEntityExist(itemEntity) then
NetworkRequestControlOfEntity(itemEntity)
while not NetworkHasControlOfEntity(itemEntity) do
Wait(100)
NetworkRequestControlOfEntity(itemEntity)
end
end
end
-- Checks if the vehicle is allowed to be used to tow
---@param vehicle - The vehicle entity to check
---@return boolean - True/false if the vehicle is allowed to be used to tow
local function isAllowedTowVehicle(vehicle)
if Config.AllowAllVehiclesToTow then return true end
local isAllowed = false
for _, veh in pairs(Config.AllowedTowVehicles) do
if GetEntityModel(vehicle) == GetHashKey(veh) then
isAllowed = true
break
end
end
return isAllowed
end
-- Draws a marker above the target object that the player is looking at
-- If the object is hovered, it will draw a yellow marker, once selected it will draw a red marker
-- This needs to be called every frame to render correctly
---@param targetVehicle - The vehicle entity to draw the marker on
---@param isSelected - boolean - Whether the vehicle had already been selected or is just hovered over
local function DrawMarkerOnTarget(targetVehicle, isSelected, markerType)
-- local markerType = 0
local scale = 0.3
local alpha = 255
local bounce = true
local faceCam = false
local iUnk = 0
local rotate = false
local textureDict = nil
local textureName = nil
local drawOnEnts = false
local pos = GetEntityCoords(targetVehicle, true)
local colorYellow = { red = 255, green = 255, blue = 0 }
local colorRed = { red = 255, green = 50, blue = 0 }
local color = colorYellow
-- If isSelected then use color red, else use color yellow
if isSelected then color = colorRed end
DrawMarker(markerType, pos.x, pos.y, pos.z + 2, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, scale, scale, scale - 0.1, color.red, color.green, color.blue, alpha, bounce, faceCam, iUnk, rotate, textureDict, textureName, drawOnEnts)
end
-- Used by the raycast functions to get the direction the camera is looking
local function RotationToDirection(rotation)
local adjustedRotation = {
x = (math.pi / 180) * rotation.x,
y = (math.pi / 180) * rotation.y,
z = (math.pi / 180) * rotation.z
}
local direction = {
x = -math.sin(adjustedRotation.z) * math.abs(math.cos(adjustedRotation.x)),
y = math.cos(adjustedRotation.z) * math.abs(math.cos(adjustedRotation.x)),
z = math.sin(adjustedRotation.x)
}
return direction
end
-- Uses a RayCast to get the entity, coords, and whether we "hit" something with the raycast
---@param distance - The distance to cast the ray
---@return hit, coords, entity - Whether the raycast hit something, the coords of the hit, and the entity that was hit
local function RayCastGamePlayCamera(distance)
local cameraRotation = GetGameplayCamRot()
local cameraCoord = GetGameplayCamCoord()
local direction = RotationToDirection(cameraRotation)
local destination = {
x = cameraCoord.x + direction.x * distance,
y = cameraCoord.y + direction.y * distance,
z = cameraCoord.z + direction.z * distance
}
local _, hit, coords, _, entity = GetShapeTestResult(StartShapeTestRay(cameraCoord.x, cameraCoord.y, cameraCoord.z, destination.x, destination.y, destination.z, -1, PlayerPedId(), 0))
return hit, coords, entity
end
-- Draws a line and marker at the end of the raycast where the player is looking
---@param coords - The coords to draw the line and marker at
local function DrawRayCastLine(coords)
local color = { r = 37, g = 192, b = 192, a = 200 }
local position = GetEntityCoords(PlayerPedId())
if coords.x ~= 0.0 and coords.y ~= 0.0 then
DrawLine(position.x, position.y, position.z, coords.x, coords.y, coords.z, color.r, color.g, color.b, color.a)
DrawMarker(28, coords.x, coords.y, coords.z, 0.0, 0.0, 0.0, 0.0, 180.0, 0.0, 0.05, 0.05, 0.05, color.r, color.g, color.b, color.a, false, true, 2, nil, nil, false)
end
end
-- Activates the ray cast mode to get the target object the player is looking at
-- Listens for keypress to detect when the player has selected the target
-- Checks if the vehicle is allowed to be used to tow and that the vehicle is not the same as the already selected vehicle
---@param shouldCheckAllowedVehicles - boolean - Whether to check if the vehicle is allowed to be used to tow
---@param otherSelectedVehicle - The vehicle entity that has already been selected
---@param shouldDrawOutline - boolean - Whether to draw an outline on the selected vehicle/object
---@return vehicle - The vehicle entity that the player has selected
local function rayCastGetSelectedVehicle(shouldCheckAllowedVehicles, otherSelectedVehicle, shouldDrawOutline)
local hit, coords, targetObj = RayCastGamePlayCamera(Config.TowDistance)
-- Draw the line to the coords where the player is looking
DrawRayCastLine(coords)
if hit and DoesEntityExist(targetObj) and targetObj ~= otherSelectedVehicle and (Config.AllowHaulingProps or IsEntityAVehicle(targetObj)) then
DrawMarkerOnTarget(targetObj, false, 0)
-- Listen for keypress of ConfirmVehicleSelectionKey key
if (IsControlJustPressed(0, Config.ConfirmVehicleSelectionKey)) then
if not shouldCheckAllowedVehicles or isAllowedTowVehicle(targetObj) then
if shouldDrawOutline then
SetEntityDrawOutline(targetObj, true)
SetEntityDrawOutlineColor(0, 255, 255, 255)
SetEntityDrawOutlineShader(0)
end
return targetObj
end
end
end
end
-- Attaches the targetObject onto the towVehicle in its current position,
-- respecting both the targetObjects rotation and offset from the towVehicle
-- If the vehicle + object are not touching, it will not attach them
---@param towVehicle - The vehicle entity that will be doing the towing
---@param targetObject - The vehicle entity that will be towed
local function attachTargetObjectToTowVehicle(towVehicle, targetObject)
local towNetId = NetworkGetNetworkIdFromEntity(towVehicle)
local targetNetId = NetworkGetNetworkIdFromEntity(targetObject)
-- Get control of the entities. This makes it so you dont need to sit in the vehicle first
RequestNetworkControlOfObject(targetNetId, targetObject)
RequestNetworkControlOfObject(towNetId, towVehicle)
-- Necessary to make sure the player has control of both entities
Wait(500)
local isTargetTouchingTowVehicle = IsEntityTouchingEntity(towVehicle, targetObject)
if isTargetTouchingTowVehicle then
-- Calculate target object rotation relative to the tow vehicle
-- This ensures that the object maintains its current rotation when attached to the tow vehicle
local targetObjectRotation = GetEntityRotation(targetObject, 2)
local towVehicleRotation = GetEntityRotation(towVehicle, 2)
local newRotX = targetObjectRotation.x - towVehicleRotation.x
local newRotY = targetObjectRotation.y - towVehicleRotation.y
local newRotZ = targetObjectRotation.z - towVehicleRotation.z
-- This ensures the target vehicle is attached to the tow vehicle at its current position
local offsetCoords = GetOffsetFromEntityGivenWorldCoords(towVehicle, GetEntityCoords(targetObject))
AttachEntityToEntity(targetObject, towVehicle, 0, offsetCoords.x, offsetCoords.y, offsetCoords.z, newRotX, newRotY, newRotZ, 0, true, true, false, 2, true)
Notify('Object has been strapped down.', 'success', 5000)
else
Notify('Target object must be touching the tow vehicle in order to be towed', 'error', 7500)
end
end
-- Run a thread to handle selecting the tow vehicle and target vehicle
local function startTowingSelectMode()
local towVehicle = nil
local targetObject = nil
local hasNotifiedSelectTowTruck = false
local hasNotifiedSelectTarget = false
local hasNotifiedConfirm = false
local isTowingSelectModeEnabled = true
CreateThread(function()
local instructionalButtons = nil
while isTowingSelectModeEnabled do
-- STEP 1: Runs a raycast to get the vehicle the player wants to tow to
if not towVehicle then
if not hasNotifiedSelectTowTruck then
hasNotifiedSelectTowTruck = true
instructionalButtons = DrawInstructionalButtons(Config.ConfirmVehicleSelectionKey, "Select tow vehicle", Config.ExitVehicleSelectionKey, "Cancel")
end
towVehicle = rayCastGetSelectedVehicle(true, nil, true)
-- STEP 2: Runs a raycast to get the vehicle/object the player wants to be towed
elseif not targetObject then
if not hasNotifiedSelectTarget then
hasNotifiedSelectTarget = true
instructionalButtons = DrawInstructionalButtons(Config.ConfirmVehicleSelectionKey, "Select target to tow", Config.ExitVehicleSelectionKey, "Cancel")
end
targetObject = rayCastGetSelectedVehicle(false, towVehicle, true)
-- STEP 3: Notify the player to press ConfirmVehicleSelectionKey to confirm the selections and complete the tow
else
if not hasNotifiedConfirm then
hasNotifiedConfirm = true
instructionalButtons = DrawInstructionalButtons(Config.ConfirmVehicleSelectionKey, "Confirm selection", Config.ExitVehicleSelectionKey, "Cancel")
end
-- Listen for keypress of the selection key to attach the vehicles together
if (IsControlJustPressed(0, Config.ConfirmVehicleSelectionKey)) then
isTowingSelectModeEnabled = false
-- Remove outlines on confirm
SetEntityDrawOutline(towVehicle, false)
SetEntityDrawOutline(targetObject, false)
attachTargetObjectToTowVehicle(towVehicle, targetObject)
end
end
-- Draw markers on selected tow vehicle
if towVehicle and isTowingSelectModeEnabled then
DrawMarkerOnTarget(towVehicle, true, 39)
end
-- Listen for keypress of exit vehicle selection key to break out of the thread and cancel select mode
if (IsControlJustPressed(0, Config.ExitVehicleSelectionKey)) then
SetEntityDrawOutline(towVehicle, false)
SetEntityDrawOutline(targetObject, false)
isTowingSelectModeEnabled = false
end
DrawScaleformMovieFullscreen(instructionalButtons, 255, 255, 255, 255, 0)
Wait(1)
end
end)
end
-- Run a thread to handle selecting the target vehicle that you want to untow
local function startUnTowSelectMode()
local targetObject = nil
local isUnTowingSelectModeEnabled = true
local instructionalButtons = DrawInstructionalButtons(Config.ConfirmVehicleSelectionKey, "Select target to untow", Config.ExitVehicleSelectionKey, "Cancel")
CreateThread(function()
while isUnTowingSelectModeEnabled do
-- targetObject will be undefined until the player selects it from the raycast selection mode
targetObject = rayCastGetSelectedVehicle(false, nil, false)
if targetObject and IsEntityAttachedToAnyVehicle(targetObject) then
-- Get control of the entities. This makes it so you dont need to sit in the vehicle first
local netId = NetworkGetNetworkIdFromEntity(targetObject)
RequestNetworkControlOfObject(netId, targetObject)
Wait(500)
DetachEntity(targetObject, true, false)
isUnTowingSelectModeEnabled = false
Notify("Vehicle detached", "success", 3000)
elseif targetObject then
Notify("Vehicle is not attached to anything", "error", 3000)
end
-- Listen for keypress of exit vehicle selection key to exit out of the loop
if (IsControlJustPressed(0, Config.ExitVehicleSelectionKey)) then
-- Exit out of the thread and reset any variables
isUnTowingSelectModeEnabled = false
end
DrawScaleformMovieFullscreen(instructionalButtons, 255, 255, 255, 255, 0)
Wait(1)
end
end)
end
RegisterNetEvent('wp-hauling:client:startTowSelection', function()
startTowingSelectMode()
end)
RegisterNetEvent('wp-hauling:client:startUntowSelection', function()
startUnTowSelectMode()
end)