-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy patheditable.lua
More file actions
227 lines (199 loc) · 9.04 KB
/
editable.lua
File metadata and controls
227 lines (199 loc) · 9.04 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
Editable = Editable or {}
--[[
/$$ /$$ /$$
| $$ | $$ | $$
| $$$$$$$ /$$ /$$ /$$$$$$$ /$$$$$$ /$$$$$$ /$$$$$$ | $$ /$$$$$$ /$$$$$$ /$$$$$$/$$$$
| $$__ $$| $$ | $$ /$$__ $$ /$$__ $$|____ $$ /$$$$$$|____ $$| $$ |____ $$ /$$__ $$| $$_ $$_ $$
| $$ \ $$| $$ | $$| $$ | $$| $$ \__/ /$$$$$$$|______/ /$$$$$$$| $$ /$$$$$$$| $$ \__/| $$ \ $$ \ $$
| $$ | $$| $$ | $$| $$ | $$| $$ /$$__ $$ /$$__ $$| $$ /$$__ $$| $$ | $$ | $$ | $$
| $$ | $$| $$$$$$$| $$$$$$$| $$ | $$$$$$$ | $$$$$$$| $$| $$$$$$$| $$ | $$ | $$ | $$
|__/ |__/ \____ $$ \_______/|__/ \_______/ \_______/|__/ \_______/|__/ |__/ |__/ |__/
/$$ | $$
| $$$$$$/
\______/
--]]
if IsDuplicityVersion() then
Editable.Server = Editable.Server or {}
-- =========================================================
-- Internal helpers
-- =========================================================
---Get the job name of an online player. Framework-dependent.
---Returns nil in standalone mode.
---@param source number
---@return string|nil
local function getPlayerJob(source)
if Framework and Framework.Type == 'esx' and Framework.ESX then
local xPlayer = Framework.ESX.GetPlayerFromId(source)
if xPlayer and xPlayer.getJob then
return xPlayer.getJob().name
end
elseif Framework and Framework.Type == 'qbcore' and Framework.QB then
local player = Framework.QB.Functions.GetPlayer(source)
if player and player.PlayerData and player.PlayerData.job then
return player.PlayerData.job.name
end
end
return nil
end
---Look up the online player ID that owns a vehicle by plate.
---Uses oxmysql for a DB lookup (ESX: owned_vehicles, QB: player_vehicles).
---Falls back to checking who is currently sitting in the vehicle if DB is unavailable.
---@param plate string
---@return number|nil player source id, or nil if owner is offline/unknown
local function getVehicleOwner(plate)
plate = plate:gsub('^%s*(.-)%s*$', '%1'):upper()
if GetResourceState('oxmysql') == 'started' then
local ok, result = pcall(function()
if Framework and Framework.Type == 'esx' then
return exports.oxmysql:executeSync(
'SELECT owner FROM owned_vehicles WHERE plate = ? LIMIT 1',
{ plate }
)
elseif Framework and Framework.Type == 'qbcore' then
return exports.oxmysql:executeSync(
'SELECT citizenid FROM player_vehicles WHERE plate = ? LIMIT 1',
{ plate }
)
end
return nil
end)
if ok and result and result[1] then
if Framework.Type == 'esx' then
local identifier = result[1].owner
local xPlayer = Framework.ESX.GetPlayerFromIdentifier(identifier)
return xPlayer and xPlayer.source or nil
elseif Framework.Type == 'qbcore' then
local citizenid = result[1].citizenid
if Framework.QB.Functions.GetPlayerByCitizenId then
local player = Framework.QB.Functions.GetPlayerByCitizenId(citizenid)
return player and player.PlayerData.source or nil
end
end
end
end
-- Fallback: check who is currently sitting in the vehicle
for _, playerId in ipairs(GetActivePlayers()) do
local ped = GetPlayerPed(playerId)
if ped and ped ~= 0 then
local veh = GetVehiclePedIsIn(ped, false)
if veh and veh ~= 0 then
local vehPlate = GetVehicleNumberPlateText(veh)
if type(vehPlate) == 'string' then
vehPlate = vehPlate:gsub('^%s*(.-)%s*$', '%1'):upper()
if vehPlate == plate then
return tonumber(playerId)
end
end
end
end
end
return nil
end
-- Expose getVehicleOwner as a callback so other resources/clients can query it.
lib.callback.register('hydra_alarm:getVehicleOwner', function(_, plate)
if type(plate) ~= 'string' or plate == '' then
return nil
end
return getVehicleOwner(plate)
end)
-- =========================================================
-- Owner notification
-- =========================================================
---Notify the owner of a vehicle when their alarm triggers.
---Uses framework-dependent DB lookup via getVehicleOwner.
---@param plate string
---@param reason string
---@return nil
function Editable.Server.OwnerNotify(plate, reason)
local ownerSource = getVehicleOwner(plate)
if ownerSource then
Editable.Server.Notify(ownerSource, L('notify.owner_alarm', plate, reason), 'warning', 6000)
end
end
-- =========================================================
-- Dispatch
-- =========================================================
---Notify all online players whose job matches Config.DispatchJobs.
---Replace or extend with a dedicated dispatch resource call if needed (ps-dispatch, cd_dispatch, etc.).
---@param coords vector3|nil
---@param plate string
---@param reason string
---@return nil
function Editable.Server.Dispatch(coords, plate, reason)
local message = L('notify.dispatch_message', plate, reason)
local jobLookup = {}
for _, v in pairs(Config.DispatchJobs or {}) do
jobLookup[v] = true
end
for _, playerId in ipairs(GetActivePlayers()) do
local job = getPlayerJob(tonumber(playerId))
if job and jobLookup[job] then
Editable.Server.Notify(tonumber(playerId), message, 'warning', 8000)
end
end
-- Uncomment to also call ps-dispatch alongside the job notify:
-- TriggerClientEvent('ps-dispatch:server:CreateDispatchCall', -1, {
-- callLocation = coords,
-- callCode = { code = '10-50', snippet = 'Vehicle Alarm' },
-- message = message,
-- flashes = true,
-- image = '',
-- blip = { sprite = 225, scale = 1.2, colour = 1, flashes = true, text = 'Vehicle Alarm', time = 15000 },
-- jobs = Config.DispatchJobs,
-- })
end
---Send a notification to one player.
---@param playerId number
---@param message string
---@param notifyType? 'info'|'success'|'warning'|'error'|string
---@param duration? number
---@return nil
function Editable.Server.Notify(playerId, message, notifyType, duration)
notifyType = notifyType or 'info'
duration = duration or 5000
if GetResourceState('okokNotify') ~= 'missing' then
TriggerClientEvent('okokNotify:Alert', playerId, 'Alarm', message, duration, notifyType, false)
return
end
if Framework and Framework.Type == 'esx' then
--print('ESX notify: ' .. message) --- IGNORE ---
TriggerClientEvent('esx:showNotification', playerId, message)
return
end
if Framework and Framework.Type == 'qbcore' then
TriggerClientEvent('QBCore:Notify', playerId, message, notifyType, duration)
return
end
TriggerClientEvent('hydra_alarm:notify', playerId, message, notifyType, duration)
end
else
Editable.Client = Editable.Client or {}
---Local client notification helper.
---Replace internals with your own UI export if needed.
---@param message string
---@param notifyType? 'info'|'success'|'warning'|'error'|string
---@param duration? number
---@return nil
function Editable.Client.Notify(message, notifyType, duration)
notifyType = notifyType or 'info'
duration = duration or 5000
if GetResourceState('okokNotify') ~= 'missing' then
exports['okokNotify']:Alert('Alarm', message, duration, notifyType, false)
return
end
if Framework and Framework.Type == 'esx' then
TriggerEvent('esx:showNotification', message)
return
end
if Framework and Framework.Type == 'qbcore' then
TriggerEvent('QBCore:Notify', message, notifyType, duration)
return
end
BeginTextCommandThefeedPost('STRING')
AddTextComponentSubstringPlayerName(message)
EndTextCommandThefeedPostTicker(false, false)
end
RegisterNetEvent('hydra_alarm:notify', function(message, notifyType, duration)
Editable.Client.Notify(message, notifyType, duration)
end)
end