diff --git a/.editorconfig b/.editorconfig
index 9b425256..4bfca33b 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -1,9 +1,9 @@
root = true
[*]
-indent_size = 4
-indent_style = tab
-end_of_line = crlf
+indent_size = 2
+indent_style = space
+end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
-insert_final_newline = true
\ No newline at end of file
+insert_final_newline = true
diff --git a/client/bootstrap.lua b/client/bootstrap.lua
new file mode 100644
index 00000000..e658763c
--- /dev/null
+++ b/client/bootstrap.lua
@@ -0,0 +1,17 @@
+OnESX = function(cb)
+
+ local ESX = nil
+
+ Citizen.CreateThread(function()
+
+ while ESX == nil do
+ TriggerEvent('esx:getSharedObject', function(obj) ESX = obj end)
+ Citizen.Wait(0)
+ end
+
+ cb(ESX)
+
+ end)
+
+end
+
diff --git a/client/functions.lua b/client/functions.lua
index aced3015..06db3e2d 100644
--- a/client/functions.lua
+++ b/client/functions.lua
@@ -1,4 +1,5 @@
ESX = {}
+ESX.Modules = {}
ESX.PlayerData = {}
ESX.PlayerLoaded = false
ESX.CurrentRequestId = 0
@@ -32,6 +33,30 @@ ESX.ClearTimeout = function(i)
ESX.TimeoutCallbacks[i] = nil
end
+ESX.CreateFrame = function(name, url, visible)
+ visible = (visible == nil) and true or false
+ SendNUIMessage({action = 'create_frame', name = name, url = url, visible = visible})
+end
+
+ESX.SendFrameMessage = function(name, msg)
+ SendNUIMessage({target = name, data = msg})
+end
+
+ESX.FocusFrame = function(name, cursor)
+ SendNUIMessage({action = 'focus_frame', name = name})
+ SetNuiFocus(true, cursor)
+end
+
+RegisterNUICallback('nui_ready', function(data, cb)
+ TriggerEvent('esx:nui_ready')
+ cb('')
+end)
+
+RegisterNUICallback('frame_message', function(data, cb)
+ TriggerEvent('esx:frame_message', data.name, data.msg, data.cb)
+ cb('')
+end)
+
ESX.IsPlayerLoaded = function()
return ESX.PlayerLoaded
end
@@ -44,41 +69,66 @@ ESX.SetPlayerData = function(key, val)
ESX.PlayerData[key] = val
end
-ESX.ShowNotification = function(msg, flash, saveToBrief, hudColorIndex)
- if saveToBrief == nil then saveToBrief = true end
- AddTextEntry('esxNotification', msg)
- BeginTextCommandThefeedPost('esxNotification')
- if hudColorIndex then ThefeedNextPostBackgroundColor(hudColorIndex) end
- EndTextCommandThefeedPostTicker(flash or false, saveToBrief)
+ESX.ShowNotification = function(msg)
+ SetNotificationTextEntry('STRING')
+ AddTextComponentString(msg)
+ DrawNotification(0,1)
end
ESX.ShowAdvancedNotification = function(sender, subject, msg, textureDict, iconType, flash, saveToBrief, hudColorIndex)
- if saveToBrief == nil then saveToBrief = true end
- AddTextEntry('esxAdvancedNotification', msg)
- BeginTextCommandThefeedPost('esxAdvancedNotification')
- if hudColorIndex then ThefeedNextPostBackgroundColor(hudColorIndex) end
+
+ if saveToBrief == nil then
+ saveToBrief = true
+ end
+
+ BeginTextCommandThefeedPost('STRING')
+ AddTextComponentSubstringPlayerName(msg)
+
+ if hudColorIndex then
+ ThefeedNextPostBackgroundColor(hudColorIndex)
+ end
+
EndTextCommandThefeedPostMessagetext(textureDict, textureDict, false, iconType, sender, subject)
- EndTextCommandThefeedPostTicker(flash or false, saveToBrief)
+ EndTextCommandThefeedPostTicker(flash or false, saveToBrief)
+
end
ESX.ShowHelpNotification = function(msg, thisFrame, beep, duration)
- AddTextEntry('esxHelpNotification', msg)
+
+ BeginTextCommandDisplayHelp('STRING')
+ AddTextComponentSubstringPlayerName(msg)
if thisFrame then
- DisplayHelpTextThisFrame('esxHelpNotification', false)
+ DisplayHelpTextThisFrame(msg, false)
else
if beep == nil then beep = true end
BeginTextCommandDisplayHelp('esxHelpNotification')
EndTextCommandDisplayHelp(0, false, beep, duration or -1)
- end
+ end
+
end
-ESX.ShowFloatingHelpNotification = function(msg, coords)
- AddTextEntry('esxFloatingHelpNotification', msg)
- SetFloatingHelpTextWorldPosition(1, coords)
- SetFloatingHelpTextStyle(1, 1, 2, -1, 3, 0)
- BeginTextCommandDisplayHelp('esxFloatingHelpNotification')
- EndTextCommandDisplayHelp(2, false, false, -1)
+ESX.ShowFloatingHelpNotification = function(msg, coords, timeout)
+
+ timeout = timeout or 5000
+ local start = GetGameTimer()
+
+ Citizen.CreateThread(function()
+
+ while (GetGameTimer() - start) < timeout do
+
+ SetFloatingHelpTextWorldPosition(1, coords.x, coords.y, coords.z)
+ SetFloatingHelpTextStyle(1, 1, 2, -1, 3, 0)
+ BeginTextCommandDisplayHelp('STRING')
+ AddTextComponentSubstringPlayerName(msg)
+ EndTextCommandDisplayHelp(2, false, true, -1)
+
+ Citizen.Wait(0)
+
+ end
+
+ end)
+
end
ESX.TriggerServerCallback = function(name, cb, ...)
@@ -94,7 +144,7 @@ ESX.TriggerServerCallback = function(name, cb, ...)
end
ESX.UI.HUD.SetDisplay = function(opacity)
- SendNUIMessage({
+ ESX.SendFrameMessage('hud', {
action = 'setHUDDisplay',
opacity = opacity
})
@@ -116,7 +166,7 @@ ESX.UI.HUD.RegisterElement = function(name, index, priority, html, data)
table.insert(ESX.UI.HUD.RegisteredElements, name)
- SendNUIMessage({
+ ESX.SendFrameMessage('hud', {
action = 'insertHUDElement',
name = name,
index = index,
@@ -136,14 +186,14 @@ ESX.UI.HUD.RemoveElement = function(name)
end
end
- SendNUIMessage({
+ ESX.SendFrameMessage('hud', {
action = 'deleteHUDElement',
name = name
})
end
ESX.UI.HUD.UpdateElement = function(name, data)
- SendNUIMessage({
+ ESX.SendFrameMessage('hud', {
action = 'updateHUDElement',
name = name,
data = data
@@ -281,7 +331,7 @@ ESX.UI.Menu.IsOpen = function(type, namespace, name)
end
ESX.UI.ShowInventoryItemNotification = function(add, item, count)
- SendNUIMessage({
+ ESX.SendFrameMessage('hud', {
action = 'inventoryNotification',
add = add,
item = item,
@@ -487,7 +537,7 @@ end
ESX.Game.GetClosestObject = function(coords, modelFilter) return ESX.Game.GetClosestEntity(ESX.Game.GetObjects(), false, coords, modelFilter) end
ESX.Game.GetClosestPed = function(coords, modelFilter) return ESX.Game.GetClosestEntity(ESX.Game.GetPeds(true), false, coords, modelFilter) end
-ESX.Game.GetClosestPlayer = function(coords) return ESX.Game.GetClosestEntity(ESX.Game.GetPlayers(true, true), true, coords, nil) end
+ESX.Game.GetClosestPlayer = function(coords, modelFilter) return ESX.Game.GetClosestEntity(ESX.Game.GetPlayers(true, true), true, coords, modelFilter) end
ESX.Game.GetClosestVehicle = function(coords, modelFilter) return ESX.Game.GetClosestEntity(ESX.Game.GetVehicles(), false, coords, modelFilter) end
ESX.Game.GetPlayersInArea = function(coords, maxDistance) return EnumerateEntitiesWithinDistance(ESX.Game.GetPlayers(true, true), true, coords, maxDistance) end
ESX.Game.GetVehiclesInArea = function(coords, maxDistance) return EnumerateEntitiesWithinDistance(ESX.Game.GetVehicles(), false, coords, maxDistance) end
@@ -559,7 +609,6 @@ ESX.Game.GetVehicleProperties = function(vehicle)
bodyHealth = ESX.Math.Round(GetVehicleBodyHealth(vehicle), 1),
engineHealth = ESX.Math.Round(GetVehicleEngineHealth(vehicle), 1),
- tankHealth = ESX.Math.Round(GetVehiclePetrolTankHealth(vehicle), 1),
fuelLevel = ESX.Math.Round(GetVehicleFuelLevel(vehicle), 1),
dirtLevel = ESX.Math.Round(GetVehicleDirtLevel(vehicle), 1),
@@ -649,7 +698,6 @@ ESX.Game.SetVehicleProperties = function(vehicle, props)
if props.plateIndex then SetVehicleNumberPlateTextIndex(vehicle, props.plateIndex) end
if props.bodyHealth then SetVehicleBodyHealth(vehicle, props.bodyHealth + 0.0) end
if props.engineHealth then SetVehicleEngineHealth(vehicle, props.engineHealth + 0.0) end
- if props.tankHealth then SetVehiclePetrolTankHealth(vehicle, props.tankHealth + 0.0) end
if props.fuelLevel then SetVehicleFuelLevel(vehicle, props.fuelLevel + 0.0) end
if props.dirtLevel then SetVehicleDirtLevel(vehicle, props.dirtLevel + 0.0) end
if props.color1 then SetVehicleColours(vehicle, props.color1, colorSecondary) end
@@ -932,10 +980,15 @@ ESX.ShowInventory = function()
ESX.Streaming.RequestAnimDict(dict)
if type == 'item_weapon' then
+
menu1.close()
- TaskPlayAnim(playerPed, dict, anim, 8.0, 1.0, 1000, 16, 0.0, false, false, false)
- Citizen.Wait(1000)
- TriggerServerEvent('esx:removeInventoryItem', type, item)
+
+ Citizen.CreateThread(function()
+ TaskPlayAnim(playerPed, dict, anim, 8.0, 1.0, 1000, 16, 0.0, false, false, false)
+ Citizen.Wait(1000)
+ TriggerServerEvent('esx:removeInventoryItem', type, item)
+ end)
+
else
ESX.UI.Menu.Open('dialog', GetCurrentResourceName(), 'inventory_item_count_remove', {
title = _U('amount')
@@ -943,11 +996,16 @@ ESX.ShowInventory = function()
local quantity = tonumber(data2.value)
if quantity and quantity > 0 and data.current.count >= quantity then
+
menu2.close()
menu1.close()
- TaskPlayAnim(playerPed, dict, anim, 8.0, 1.0, 1000, 16, 0.0, false, false, false)
- Citizen.Wait(1000)
- TriggerServerEvent('esx:removeInventoryItem', type, item, quantity)
+
+ Citizen.CreateThread(function()
+ TaskPlayAnim(playerPed, dict, anim, 8.0, 1.0, 1000, 16, 0.0, false, false, false)
+ Citizen.Wait(1000)
+ TriggerServerEvent('esx:removeInventoryItem', type, item, quantity)
+ end)
+
else
ESX.ShowNotification(_U('amount_invalid'))
end
diff --git a/client/main.lua b/client/main.lua
index 3f1e9c30..a66d515f 100644
--- a/client/main.lua
+++ b/client/main.lua
@@ -1,4 +1,4 @@
-local isPaused, isDead, pickups = false, false, {}
+local isLoadoutLoaded, isPaused, pickups = false, false, {}
Citizen.CreateThread(function()
while true do
@@ -13,35 +13,17 @@ end)
RegisterNetEvent('esx:playerLoaded')
AddEventHandler('esx:playerLoaded', function(playerData)
- ESX.PlayerLoaded = true
+
+ ESX.PlayerLoaded = true
ESX.PlayerData = playerData
- -- check if player is coming from loading screen
- if GetEntityModel(PlayerPedId()) == GetHashKey('PLAYER_ZERO') then
- local defaultModel = GetHashKey('a_m_y_stbla_02')
- RequestModel(defaultModel)
+ local playerPed = PlayerPedId()
- while not HasModelLoaded(defaultModel) do
- Citizen.Wait(10)
- end
-
- SetPlayerModel(PlayerId(), defaultModel)
- SetPedDefaultComponentVariation(PlayerPedId())
- SetPedRandomComponentVariation(PlayerPedId(), true)
- SetModelAsNoLongerNeeded(defaultModel)
+ if Config.EnablePvP then
+ SetCanAttackFriendly(playerPed, true, false)
+ NetworkSetFriendlyFireOption(true)
end
- -- freeze the player
- FreezeEntityPosition(PlayerPedId(), true)
-
- -- enable PVP
- SetCanAttackFriendly(PlayerPedId(), true, false)
- NetworkSetFriendlyFireOption(true)
-
- -- disable wanted level
- ClearPlayerWantedLevel(PlayerId())
- SetMaxWantedLevel(0)
-
if Config.EnableHud then
for k,v in ipairs(playerData.accounts) do
local accountTpl = '

{{money}}
'
@@ -60,42 +42,47 @@ AddEventHandler('esx:playerLoaded', function(playerData)
})
end
- ESX.Game.Teleport(PlayerPedId(), {
- x = playerData.coords.x,
- y = playerData.coords.y,
- z = playerData.coords.z + 0.25,
- heading = playerData.coords.heading
- }, function()
+ -- Bringing back spawnmanager, see commit of Smallo92 at https://github.com/extendedmode/extendedmode/commit/9979c204f1237091e94fdd46580c9e7ebc79bca7
+ exports.spawnmanager:spawnPlayer({
+ x = playerData.coords.x,
+ y = playerData.coords.y,
+ z = playerData.coords.z,
+ heading = playerData.coords.heading,
+ model = 'mp_m_freemode_01',
+ skipFade = false
+ }, function()
+
TriggerServerEvent('esx:onPlayerSpawn')
TriggerEvent('esx:onPlayerSpawn')
- TriggerEvent('playerSpawned') -- compatibility with old scripts, will be removed soon
- TriggerEvent('esx:restoreLoadout')
+ TriggerEvent('esx:restoreLoadout')
+
+ StartServerSyncLoops()
+
+ end)
- Citizen.Wait(3000)
- ShutdownLoadingScreen()
- FreezeEntityPosition(PlayerPedId(), false)
- DoScreenFadeIn(10000)
- StartServerSyncLoops()
- end)
end)
RegisterNetEvent('esx:setMaxWeight')
AddEventHandler('esx:setMaxWeight', function(newMaxWeight) ESX.PlayerData.maxWeight = newMaxWeight end)
-AddEventHandler('esx:onPlayerSpawn', function() isDead = false end)
-AddEventHandler('esx:onPlayerDeath', function() isDead = true end)
+AddEventHandler('esx:onPlayerSpawn', function() ESX.IsDead = false end)
+AddEventHandler('esx:onPlayerDeath', function() ESX.IsDead = true end)
+AddEventHandler('skinchanger:loadDefaultModel', function() isLoadoutLoaded = false end)
AddEventHandler('skinchanger:modelLoaded', function()
- while not ESX.PlayerLoaded do
+
+ while not ESX.PlayerLoaded do
Citizen.Wait(100)
end
- TriggerEvent('esx:restoreLoadout')
+ TriggerEvent('esx:restoreLoadout')
+
end)
AddEventHandler('esx:restoreLoadout', function()
local playerPed = PlayerPedId()
local ammoTypes = {}
+
RemoveAllPedWeapons(playerPed, true)
for k,v in ipairs(ESX.PlayerData.loadout) do
@@ -109,6 +96,7 @@ AddEventHandler('esx:restoreLoadout', function()
for k2,v2 in ipairs(v.components) do
local componentHash = ESX.GetWeaponComponent(weaponName, v2).hash
+
GiveWeaponComponentToPed(playerPed, weaponHash, componentHash)
end
@@ -117,6 +105,8 @@ AddEventHandler('esx:restoreLoadout', function()
ammoTypes[ammoType] = true
end
end
+
+ isLoadoutLoaded = true
end)
RegisterNetEvent('esx:setAccountMoney')
@@ -245,7 +235,7 @@ RegisterNetEvent('esx:setJob')
AddEventHandler('esx:setJob', function(job)
if Config.EnableHud then
ESX.UI.HUD.UpdateElement('job', {
- job_label = job.label,
+ job_label = job.label,
grade_label = job.grade_label
})
end
@@ -268,42 +258,91 @@ AddEventHandler('esx:spawnVehicle', function(vehicleName)
end)
RegisterNetEvent('esx:createPickup')
-AddEventHandler('esx:createPickup', function(pickupId, label, coords, type, name, components, tintIndex)
- local function setObjectProperties(object)
- SetEntityAsMissionEntity(object, true, false)
- PlaceObjectOnGroundProperly(object)
- FreezeEntityPosition(object, true)
- SetEntityCollision(object, false, true)
+AddEventHandler('esx:createPickup', function(pickupId, label, playerId, type, name, components, tintIndex)
+
+ local playerPed = GetPlayerPed(GetPlayerFromServerId(playerId))
+ local entityCoords, forwardVector = GetEntityCoords(playerPed), GetEntityForwardVector(playerPed)
+ local objectCoords = (entityCoords + forwardVector * 1.0)
+
+ local function setupPickup(obj)
+
+ SetEntityAsMissionEntity(obj, true, false)
+ PlaceObjectOnGroundProperly(obj)
+ FreezeEntityPosition(obj, true)
+ SetEntityCollision(obj, false, true)
pickups[pickupId] = {
- obj = object,
+ obj = obj,
label = label,
inRange = false,
- coords = vector3(coords.x, coords.y, coords.z)
+ coords = objectCoords
}
+
end
if type == 'item_weapon' then
- local weaponHash = GetHashKey(name)
- ESX.Streaming.RequestWeaponAsset(weaponHash)
- local pickupObject = CreateWeaponObject(weaponHash, 50, coords.x, coords.y, coords.z, true, 1.0, 0)
- SetWeaponObjectTintIndex(pickupObject, tintIndex)
- for k,v in ipairs(components) do
- local component = ESX.GetWeaponComponent(name, v)
- GiveWeaponComponentToWeaponObject(pickupObject, component.hash)
- end
+ Citizen.CreateThread(function()
+
+ local weaponHash = GetHashKey(name)
+
+ ESX.Streaming.RequestWeaponAsset(weaponHash)
+ local pickupObject = CreateWeaponObject(weaponHash, 50, objectCoords, true, 1.0, 0)
+ SetWeaponObjectTintIndex(pickupObject, tintIndex)
+
+ for k,v in ipairs(components) do
+ local component = ESX.GetWeaponComponent(name, v)
+ GiveWeaponComponentToWeaponObject(pickupObject, component.hash)
+ end
+
+ setupPickup(pickupObject)
+
+ end)
- setObjectProperties(pickupObject)
else
- ESX.Game.SpawnLocalObject('prop_money_bag_01', coords, setObjectProperties)
+
+ ESX.Game.SpawnLocalObject('prop_money_bag_01', objectCoords, function(obj)
+ setupPickup(obj)
+ end)
end
end)
+
RegisterNetEvent('esx:createMissingPickups')
AddEventHandler('esx:createMissingPickups', function(missingPickups)
for pickupId,pickup in pairs(missingPickups) do
- TriggerEvent('esx:createPickup', pickupId, pickup.label, pickup.coords, pickup.type, pickup.name, pickup.components, pickup.tintIndex)
+ local pickupObject = nil
+
+ if pickup.type == 'item_weapon' then
+ ESX.Streaming.RequestWeaponAsset(GetHashKey(pickup.name))
+ pickupObject = CreateWeaponObject(GetHashKey(pickup.name), 50, pickup.coords.x, pickup.coords.y, pickup.coords.z, true, 1.0, 0)
+ SetWeaponObjectTintIndex(pickupObject, pickup.tintIndex)
+
+ for k,componentName in ipairs(pickup.components) do
+ local component = ESX.GetWeaponComponent(pickup.name, componentName)
+ GiveWeaponComponentToWeaponObject(pickupObject, component.hash)
+ end
+ else
+ ESX.Game.SpawnLocalObject('prop_money_bag_01', pickup.coords, function(obj)
+ pickupObject = obj
+ end)
+
+ while not pickupObject do
+ Citizen.Wait(10)
+ end
+ end
+
+ SetEntityAsMissionEntity(pickupObject, true, false)
+ PlaceObjectOnGroundProperly(pickupObject)
+ FreezeEntityPosition(pickupObject, true)
+ SetEntityCollision(pickupObject, false, true)
+
+ pickups[pickupId] = {
+ obj = pickupObject,
+ label = pickup.label,
+ inRange = false,
+ coords = vector3(pickup.coords.x, pickup.coords.y, pickup.coords.z)
+ }
end
end)
@@ -387,7 +426,7 @@ function StartServerSyncLoops()
while true do
Citizen.Wait(0)
- if isDead then
+ if ESX.IsDead then
Citizen.Wait(500)
else
local playerPed = PlayerPedId()
@@ -428,17 +467,20 @@ function StartServerSyncLoops()
end)
end
-Citizen.CreateThread(function()
- while true do
- Citizen.Wait(0)
+-- Disable wanted level
+if Config.DisableWantedLevel then
+ Citizen.CreateThread(function()
+ while true do
+ Citizen.Wait(0)
+ local playerId = PlayerId()
- if IsControlJustReleased(0, 289) then
- if IsInputDisabled(0) and not isDead and not ESX.UI.Menu.IsOpen('default', 'es_extended', 'inventory') then
- ESX.ShowInventory()
+ if GetPlayerWantedLevel(playerId) ~= 0 then
+ SetPlayerWantedLevel(playerId, 0, false)
+ SetPlayerWantedLevelNow(playerId, false)
end
end
- end
-end)
+ end)
+end
-- Pickups
Citizen.CreateThread(function()
@@ -470,14 +512,14 @@ Citizen.CreateThread(function()
end
end
- label = ('%s~n~%s'):format(label, _U('threw_pickup_prompt'))
end
- ESX.Game.Utils.DrawText3D({
+ ESX.ShowFloatingHelpNotification(label, {
x = pickup.coords.x,
y = pickup.coords.y,
z = pickup.coords.z + 0.25
- }, label, 1.2, 1)
+ }, 100)
+
elseif pickup.inRange then
pickup.inRange = false
end
@@ -488,3 +530,18 @@ Citizen.CreateThread(function()
end
end
end)
+
+AddEventHandler('luaconsole:getHandlers', function(cb)
+
+ local name = GetCurrentResourceName()
+
+ cb(name, function(code, env)
+ if env ~= nil then
+ for k,v in pairs(env) do _ENV[k] = v end
+ return load(code, 'lc:' .. name, 'bt', _ENV)
+ else
+ return load(code, 'lc:' .. name, 'bt')
+ end
+ end)
+
+end)
diff --git a/client/wrapper.lua b/client/wrapper.lua
index 441312d8..382e08de 100644
--- a/client/wrapper.lua
+++ b/client/wrapper.lua
@@ -6,7 +6,7 @@ RegisterNUICallback('__chunk', function(data, cb)
if data['end'] then
local msg = json.decode(Chunks[data.id])
- TriggerEvent(GetCurrentResourceName() .. ':message:' .. data.__type, msg)
+ TriggerEvent(data.__namespace .. ':message:' .. data.__type, msg)
Chunks[data.id] = nil
end
diff --git a/common/functions.lua b/common/functions.lua
index e47b5b7f..ff4099eb 100644
--- a/common/functions.lua
+++ b/common/functions.lua
@@ -4,6 +4,19 @@ for i = 48, 57 do table.insert(Charset, string.char(i)) end
for i = 65, 90 do table.insert(Charset, string.char(i)) end
for i = 97, 122 do table.insert(Charset, string.char(i)) end
+-- Be careful with this, use it for some config file parsing and such
+ESX.EvalFile = function(resource, file, env)
+
+ env = env or {}
+ env._G = env
+ local code = LoadResourceFile(resource, file)
+
+ load(code, code, 't', env)()
+
+ return env
+
+end
+
ESX.GetRandomString = function(length)
math.randomseed(GetGameTimer())
diff --git a/config.lua b/config.lua
index 8f6134ff..acba2923 100644
--- a/config.lua
+++ b/config.lua
@@ -10,21 +10,12 @@ Config.Accounts = {
Config.StartingAccountMoney = {bank = 50000}
Config.EnableSocietyPayouts = false -- pay from the society account that the player is employed at? Requirement: esx_society
+Config.DisableWantedLevel = true
Config.EnableHud = true -- enable the default hud? Display current job and accounts (black, bank & cash)
+Config.EnablePvP = true -- enable pvp?
Config.MaxWeight = 24 -- the max inventory weight without backpack
-Config.PaycheckInterval = 7 * 60000 -- how often to recieve pay checks in milliseconds
-Config.EnableDebug = false
-Config.IncompatibleResourcesToStop = {
- ['essentialmode'] = 'ES for short, the performance heavy RP framework no one uses - and source for the random unwanted ZAP ads you\'re seeing',
- ['es_admin2'] = 'Adminstration tool for the ancient ES framework that wont work with ESX',
- ['esplugin_mysql'] = 'MySQL "plugin" for the ancient ES framework that has a SQL injection vulnerability',
- ['es_ui'] = 'Money HUD for ES',
- ['spawnmanager'] = 'Default resource that takes care of spawning players, ESX does this already',
- ['mapmanager'] = 'Default resource that was required by spawnmanager, but neither are used',
- ['basic-gamemode'] = 'Resource that is solely for choosing the default game type',
- ['fivem'] = 'Resource that is solely for choosing the default game type',
- ['fivem-map-hipster'] = 'Default spawn locations for mapmanager',
- ['fivem-map-skater'] = 'Default spawn locations for mapmanager',
- ['baseevents'] = 'Default resource for handling death events, ESX does this already'
-}
+Config.PaycheckInterval = 7 * 60000 -- how often to recieve pay checks in milliseconds
+
+Config.EnableDebug = false
+Config.InventoryKey = 'REPLAY_START_STOP_RECORDING_SECONDARY' -- Key F2 by default
diff --git a/fxmanifest.lua b/fxmanifest.lua
index 0f1c2f78..77150781 100644
--- a/fxmanifest.lua
+++ b/fxmanifest.lua
@@ -4,7 +4,7 @@ game 'gta5'
description 'ES Extended'
-version '1.2.0'
+version '2.0.0'
server_scripts {
'@async/async.lua',
@@ -65,40 +65,54 @@ client_scripts {
'common/modules/math.lua',
'common/modules/table.lua',
- 'common/functions.lua'
+ 'common/functions.lua',
}
ui_page {
- 'html/ui.html'
+ 'hud/index.html'
}
files {
+ 'client/bootstrap.lua',
'locale.js',
- 'html/ui.html',
-
- 'html/css/app.css',
-
- 'html/js/mustache.min.js',
- 'html/js/wrapper.js',
- 'html/js/app.js',
-
- 'html/fonts/pdown.ttf',
- 'html/fonts/bankgothic.ttf',
-
- 'html/img/accounts/bank.png',
- 'html/img/accounts/black_money.png',
- 'html/img/accounts/money.png'
+ 'hud/**/*',
}
exports {
- 'getSharedObject'
+ 'getSharedObject',
+ 'OnESX',
}
server_exports {
- 'getSharedObject'
+ 'getSharedObject',
+ 'OnESX',
}
dependencies {
'mysql-async',
'async'
}
+
+-- ESX Modules
+esxmodule = function(name)
+
+ file('modules/' .. name .. '/data/**/*')
+
+ client_script('modules/' .. name .. '/client/module.lua')
+ client_script('modules/' .. name .. '/client/main.lua')
+ client_script('modules/' .. name .. '/client/events.lua')
+
+ server_script('modules/' .. name .. '/server/module.lua')
+ server_script('modules/' .. name .. '/server/main.lua')
+ server_script('modules/' .. name .. '/server/events.lua')
+
+end
+
+esxmodule 'input'
+esxmodule 'hud'
+esxmodule 'menu_default'
+esxmodule 'menu_dialog'
+esxmodule 'menu_list'
+esxmodule 'interact'
+
+esxmodule 'job_police'
diff --git a/hud/app.css b/hud/app.css
new file mode 100644
index 00000000..d1ab073f
--- /dev/null
+++ b/hud/app.css
@@ -0,0 +1,14 @@
+html, body, #frames, #frames > div, #frames > div > iframe {
+ width : 100vw;
+ height : 100vh;
+ margin : 0;
+ padding : 0;
+ border : 0;
+ background-color: transparent;
+}
+
+#frames > div, #frames > div > iframe {
+ position: absolute;
+ left: 0;
+ top: 0;
+}
diff --git a/hud/app.js b/hud/app.js
new file mode 100644
index 00000000..2a821f5a
--- /dev/null
+++ b/hud/app.js
@@ -0,0 +1,109 @@
+(() => {
+
+ class ESX {
+
+ constructor() {
+
+ this.frames = {};
+ this.modulePath = '../modules';
+
+ window.addEventListener('message', e => this.onMessage(e.data));
+
+ $.post('http://es_extended/nui_ready', '{}');
+
+ }
+
+ createFrame(name, url, visible = true) {
+
+ const frame = document.createElement('div');
+ const iframe = document.createElement('iframe');
+
+ frame.appendChild(iframe);
+
+ iframe.src = url;
+ this.frames[name] = {frame, iframe};
+
+ this.frames[name].iframe.addEventListener('message', e => this.onFrameMessage(name, e.data));
+ this.frames[name].frame.style.pointerEvents = 'none';
+
+ document.querySelector('#frames').appendChild(frame);
+
+ if(!visible)
+ this.hideFrame(name);
+
+ return this.frames[name];
+
+ }
+
+ destroyFrame(name) {
+ document.querySelector('#frames').removeChild(this.frames[name].frame);
+ this.frames[name].frame.remove();
+ }
+
+ showFrame(name) {
+ this.frames[name].frame.style.display = 'block';
+ }
+
+ hideFrame(name) {
+ this.frames[name].frame.style.display = 'none';
+ }
+
+ focusFrame(name) {
+
+ for(let k in this.frames) {
+
+ if(k === name)
+ this.frames[k].frame.style.pointerEvents = 'all';
+ else
+ this.frames[k].frame.style.pointerEvents = 'none';
+ }
+
+ this.frames[name].iframe.contentWindow.focus();
+
+ }
+
+ onMessage(msg) {
+
+ if(msg.target) {
+
+ if(this.frames[msg.target])
+ this.frames[msg.target].iframe.contentWindow.postMessage(msg.data);
+ else
+ console.error('[esx:nui] cannot find frame : ' + msg.target);
+
+ } else {
+
+ switch(msg.action) {
+
+ case 'create_frame' : {
+ this.createFrame(msg.name, msg.url, msg.visible);
+ break;
+ }
+
+ case 'destroy_frame' : {
+ this.destroyFrame(msg.name);
+ break;
+ }
+
+ case 'focus_frame' : {
+ this.focusFrame(msg.name);
+ break;
+ }
+
+ default: break;
+ }
+
+ }
+
+
+ }
+
+ onFrameMessage(name, msg) {
+ $.post('http://es_extended/frame_message', JSON.stringify({name, msg}));
+ }
+
+ }
+
+ ESX = new ESX();
+
+})();
diff --git a/hud/index.html b/hud/index.html
new file mode 100644
index 00000000..cf60df14
--- /dev/null
+++ b/hud/index.html
@@ -0,0 +1,13 @@
+
+
+ esx
+
+
+
+
+
+
+
+
+
+
diff --git a/hud/wrapper.js b/hud/wrapper.js
new file mode 100644
index 00000000..df9523c9
--- /dev/null
+++ b/hud/wrapper.js
@@ -0,0 +1,43 @@
+(() => {
+
+ let ESXWrapper = {};
+ ESXWrapper.MessageSize = 1024;
+ ESXWrapper.messageId = 0;
+
+ window.SendMessage = function (namespace, type, msg) {
+
+ ESXWrapper.messageId = (ESXWrapper.messageId < 65535) ? ESXWrapper.messageId + 1 : 0;
+ const str = JSON.stringify(msg);
+
+ for (let i = 0; i < str.length; i++) {
+
+ let count = 0;
+ let chunk = '';
+
+ while (count < ESXWrapper.MessageSize && i < str.length) {
+
+ chunk += str[i];
+
+ count++;
+ i++;
+ }
+
+ i--;
+
+ const data = {
+ __namespace: namespace,
+ __type: type,
+ id: ESXWrapper.messageId,
+ chunk: chunk
+ }
+
+ if (i == str.length - 1)
+ data.end = true;
+
+ $.post('http://es_extended/__chunk', JSON.stringify(data));
+
+ }
+
+ }
+
+})()
diff --git a/locale.lua b/locale.lua
index 68de6651..c092385d 100644
--- a/locale.lua
+++ b/locale.lua
@@ -19,3 +19,13 @@ end
function _U(str, ...) -- Translate string first char uppercase
return tostring(_(str, ...):gsub("^%l", string.upper))
end
+
+function LoadLocale(ns, lang, data)
+
+ Locales[lang] = Locales[lang] or {}
+
+ for k,v in pairs(data) do
+ Locales[lang][ns .. ':' .. k] =v
+ end
+
+end
diff --git a/locales/pl.lua b/locales/pl.lua
index 35012487..2a04b378 100644
--- a/locales/pl.lua
+++ b/locales/pl.lua
@@ -10,9 +10,9 @@ Locales['pl'] = {
['giveammo'] = 'daj amunicje',
['amountammo'] = 'ilość amunicji',
['noammo'] = 'nie posiadasz wystarczającej ilości amunicji!',
- ['gave_item'] = 'dałeś/aś ~y~%sx~s~ ~b~%s~s~ dla ~y~%s~s~',
- ['received_item'] = 'otrzymałeś/aś ~y~%sx~s~ ~b~%s~s~ od ~b~%s~s~',
- ['gave_weapon'] = 'dałeś/aś ~b~%s~s~ dla ~y~%s~s~',
+ ['gave_item'] = 'dajesz ~y~%sx~s~ ~b~%s~s~ dla ~y~%s~s~',
+ ['received_item'] = 'otrzymujesz ~y~%sx~s~ ~b~%s~s~ od ~b~%s~s~',
+ ['gave_weapon'] = 'dajesz ~b~%s~s~ dla ~y~%s~s~',
['gave_weapon_ammo'] = 'dałeś/aś ~o~%sx %s~s~ do ~b~%s~s~ dla ~y~%s~s~',
['gave_weapon_withammo'] = 'dałeś/aś ~b~%s~s~ z ~o~%sx %s~s~ dla ~y~%s~s~',
['gave_weapon_hasalready'] = '~y~%s~s~ już posiada ~y~%s~s~',
@@ -22,8 +22,8 @@ Locales['pl'] = {
['received_weapon_withammo'] = 'otrzymałeś/aś ~b~%s~s~ z ~o~%sx %s~s~ od ~b~%s~s~',
['received_weapon_hasalready'] = '~b~%s~s~ próbował/a przekazać ci ~y~%s~s~, lecz już posiadasz jedno',
['received_weapon_noweapon'] = '~b~%s~s~ próbował/a przekazać ci amunicje do ~y~%s~s~, lecz nie posiadasz tej broni',
- ['gave_account_money'] = 'dałeś/aś ~g~%s$~s~ (%s) dla ~y~%s~s~',
- ['received_account_money'] = 'otrzymałeś/aś ~g~%s$~s~ (%s) od ~b~%s~s~',
+ ['gave_account_money'] = 'dajesz ~g~%s$~s~ (%s) dla ~y~%s~s~',
+ ['received_account_money'] = 'otrzymujesz ~g~%s$~s~ (%s) od ~b~%s~s~',
['amount_invalid'] = 'nieprawidłowa ilość',
['players_nearby'] = 'brak graczy w pobliżu',
['ex_inv_lim'] = 'akcja nie jest możliwa, nie możesz mieć więcej ~y~%s~s~',
@@ -33,75 +33,76 @@ Locales['pl'] = {
['threw_account'] = 'wyrzuciłeś/aś ~g~$%s~s~ ~b~%s~s~',
['threw_weapon'] = 'wyrzuciłeś/aś ~b~%s~s~',
['threw_weapon_ammo'] = 'wyrzuciłeś/aś ~b~%s~s~ z ~o~%sx %s~s~',
- ['threw_weapon_already'] = 'już posiadasz taką samą broń',
- ['threw_cannot_pickup'] = 'nie możesz tego podnieść, gdyż masz pełny ekwipunek!',
- ['threw_pickup_prompt'] = 'naciśnij ~y~E~s~ aby podnieść',
+ ['threw_weapon_already'] = 'you already carry the same weapon',
+ ['threw_cannot_pickup'] = 'you cannot pickup that because your inventory is full!',
+ ['threw_pickup_prompt'] = 'press ~y~E~s~ to pickup',
-- Key mapping
- ['keymap_showinventory'] = 'pokaż ekwipunek',
+ ['keymap_showinventory'] = 'show Inventory',
-- Salary related
- ['received_salary'] = 'otrzymałeś/aś wynagrodzenie: ~g~%s$~s~',
- ['received_help'] = 'otrzymałeś/aś zapomogę: ~g~$%s~s~',
+ ['received_salary'] = 'otrzymałeś wynagrodzenie: ~g~%s$~s~',
+ ['received_help'] = 'otrzymałem zapomogę: ~g~$%s~s~',
['company_nomoney'] = 'firma, w której pracujesz, jest zbyt biedna, by wypłacić twoją pensję',
- ['received_paycheck'] = 'otrzymano wypłate',
+ ['received_paycheck'] = 'otrzymana wypłata',
['bank'] = 'bank',
['account_bank'] = 'bank',
- ['account_black_money'] = 'brudne pieniądze',
- ['account_money'] = 'pieniądze',
+ ['account_black_money'] = 'dirty Money',
+ ['account_money'] = 'cash',
+
['act_imp'] = 'działanie niemożliwe',
['in_vehicle'] = 'nie możesz przekazywać przedmiotów w pojeździe',
-- Commands
- ['command_car'] = 'przywołaj pojazd',
- ['command_car_car'] = 'nazwa lub hash przywołanego pojazdu',
- ['command_cardel'] = 'usuń pojazd w pobliżu',
- ['command_cardel_radius'] = 'opcjonalnie usuń każdy pojazd w obszarze',
- ['command_clear'] = 'wyczyść czat',
- ['command_clearall'] = 'wyczyść czat dla wszystkich graczy',
- ['command_clearinventory'] = 'wyczyść ekwipunek gracza',
- ['command_clearloadout'] = 'wyczyść wyposarzenie gracza',
- ['command_giveaccountmoney'] = 'daj pieniądze na podany typ konta',
- ['command_giveaccountmoney_account'] = 'prawidłowy typ konta',
- ['command_giveaccountmoney_amount'] = 'ilość do dodania',
- ['command_giveaccountmoney_invalid'] = 'nieprawidłowy typ konta',
- ['command_giveitem'] = 'daj przedmiot graczowi',
- ['command_giveitem_item'] = 'nazwa przedmiotu',
- ['command_giveitem_count'] = 'ilość przedimotu',
- ['command_giveweapon'] = 'daj broń graczowi',
- ['command_giveweapon_weapon'] = 'nazwa broni',
- ['command_giveweapon_ammo'] = 'ilość amunicji',
- ['command_giveweapon_hasalready'] = 'gracz już posiada tą broń',
- ['command_giveweaponcomponent'] = 'daj komponent do broni graczowi',
- ['command_giveweaponcomponent_component'] = 'nazwa komponentu',
- ['command_giveweaponcomponent_invalid'] = 'nieprawidłowy komponent do broni',
- ['command_giveweaponcomponent_hasalready'] = 'gracz już posiada ten komponent do tej broni',
- ['command_giveweaponcomponent_missingweapon'] = 'gracz nie posiada tej broni',
- ['command_save'] = 'zapisz gracza w bazie danych',
- ['command_saveall'] = 'zapisz wszystkich graczy w bazie danych',
- ['command_setaccountmoney'] = 'ustaw ilość pieniędzy danego konta dla gracza',
- ['command_setaccountmoney_amount'] = 'ilość pieniędzy do ustawienia',
- ['command_setcoords'] = 'teleportuj na koordynaty',
+ ['command_car'] = 'spawn an vehicle',
+ ['command_car_car'] = 'vehicle spawn name or hash',
+ ['command_cardel'] = 'delete vehicle in proximity',
+ ['command_cardel_radius'] = 'optional, delete every vehicle within the specified radius',
+ ['command_clear'] = 'clear chat',
+ ['command_clearall'] = 'clear chat for all players',
+ ['command_clearinventory'] = 'clear player inventory',
+ ['command_clearloadout'] = 'clear a player loadout',
+ ['command_giveaccountmoney'] = 'give account money',
+ ['command_giveaccountmoney_account'] = 'valid account name',
+ ['command_giveaccountmoney_amount'] = 'amount to add',
+ ['command_giveaccountmoney_invalid'] = 'invalid account name',
+ ['command_giveitem'] = 'give an item to a player',
+ ['command_giveitem_item'] = 'item name',
+ ['command_giveitem_count'] = 'item count',
+ ['command_giveweapon'] = 'give a weapon to a player',
+ ['command_giveweapon_weapon'] = 'weapon name',
+ ['command_giveweapon_ammo'] = 'ammo count',
+ ['command_giveweapon_hasalready'] = 'player already has that weapon',
+ ['command_giveweaponcomponent'] = 'give weapon component',
+ ['command_giveweaponcomponent_component'] = 'component name',
+ ['command_giveweaponcomponent_invalid'] = 'invalid weapon component',
+ ['command_giveweaponcomponent_hasalready'] = 'player already has that weapon component',
+ ['command_giveweaponcomponent_missingweapon'] = 'player does not have that weapon',
+ ['command_save'] = 'save a player to database',
+ ['command_saveall'] = 'save all players to database',
+ ['command_setaccountmoney'] = 'set account money for a player',
+ ['command_setaccountmoney_amount'] = 'amount of money to set',
+ ['command_setcoords'] = 'teleport to coordinates',
['command_setcoords_x'] = 'x axis',
['command_setcoords_y'] = 'y axis',
['command_setcoords_z'] = 'z axis',
- ['command_setjob'] = 'ustaw prace dla gracza',
- ['command_setjob_job'] = 'nazwa pracy',
- ['command_setjob_grade'] = 'stanowisko w pracy',
- ['command_setjob_invalid'] = 'praca, stanowisko lub obydwa są nieprawidłowe',
- ['command_setgroup'] = 'ustaw grupe gracza',
- ['command_setgroup_group'] = 'nazwa grupy',
- ['commanderror_argumentmismatch'] = 'nieprawiłowa ilość argumentów (przeszło %s, wymagane %s)',
- ['commanderror_argumentmismatch_number'] = 'nieprawidłowy typ argumentu #%s (przeszedł tekst, wymagany numer)',
- ['commanderror_invaliditem'] = 'nieprawidłowa nazwa przedmiotu',
- ['commanderror_invalidweapon'] = 'nieprawidłowa broń',
- ['commanderror_console'] = 'podana komenda nie może zostać uruchomiona przez konsole',
- ['commanderror_invalidcommand'] = '^3%s^0 nie jest poprawną komendą!',
- ['commanderror_invalidplayerid'] = 'brak dostepnego gracza pasującego do podanego id serwerowego',
- ['commandgeneric_playerid'] = 'id gracza',
+ ['command_setjob'] = 'set job for a player',
+ ['command_setjob_job'] = 'job name',
+ ['command_setjob_grade'] = 'job grade',
+ ['command_setjob_invalid'] = 'the job, grade or both are invalid',
+ ['command_setgroup'] = 'set player group',
+ ['command_setgroup_group'] = 'group name',
+ ['commanderror_argumentmismatch'] = 'argument count mismatch (passed %s, wanted %s)',
+ ['commanderror_argumentmismatch_number'] = 'argument #%s type mismatch (passed string, wanted number)',
+ ['commanderror_invaliditem'] = 'invalid item name',
+ ['commanderror_invalidweapon'] = 'invalid weapon',
+ ['commanderror_console'] = 'that command can not be run from console',
+ ['commanderror_invalidcommand'] = '^3%s^0 is not an valid command!',
+ ['commanderror_invalidplayerid'] = 'there is no player online matching that server id',
+ ['commandgeneric_playerid'] = 'player id',
-- Locale settings
- ['locale_digit_grouping_symbol'] = ',',
+ ['locale_digit_grouping_symbol'] = ' ',
['locale_currency'] = '$%s',
-- Weapons
@@ -183,42 +184,42 @@ Locales['pl'] = {
-- Weapon Components
['component_clip_default'] = 'domyślny tłumik',
['component_clip_extended'] = 'rozszerzony tłumik',
- ['component_clip_drum'] = 'magazynek bębnowy',
- ['component_clip_box'] = 'magazynek',
+ ['component_clip_drum'] = 'drum Magazine',
+ ['component_clip_box'] = 'box Magazine',
['component_flashlight'] = 'latarka',
['component_scope'] = 'luneta',
['component_scope_advanced'] = 'zaawansowana luneta',
['component_suppressor'] = 'tłumik',
['component_grip'] = 'uchwyt',
- ['component_luxary_finish'] = 'luksusowe wykończenie broni',
+ ['component_luxary_finish'] = 'luxary Weapon Finish',
-- Weapon Ammo
- ['ammo_rounds'] = 'nabój/oi',
- ['ammo_shells'] = 'pocisk(ów)',
- ['ammo_charge'] = 'naładowania',
- ['ammo_petrol'] = 'galon(y) paliwa',
- ['ammo_firework'] = 'fajerwerka/i',
- ['ammo_rockets'] = 'rakieta/y',
- ['ammo_grenadelauncher'] = 'granat(y)',
- ['ammo_grenade'] = 'granat(y)',
- ['ammo_stickybomb'] = 'bomba/y',
- ['ammo_pipebomb'] = 'bomba/y',
- ['ammo_smokebomb'] = 'bomba/y',
- ['ammo_molotov'] = 'kontail(e)',
- ['ammo_proxmine'] = 'mina/y',
- ['ammo_bzgas'] = 'puszka/ek',
- ['ammo_ball'] = 'kula/e',
- ['ammo_snowball'] = 'snieżka/i',
- ['ammo_flare'] = 'flara/y',
- ['ammo_flaregun'] = 'flara/y',
+ ['ammo_rounds'] = 'round(s)',
+ ['ammo_shells'] = 'shell(s)',
+ ['ammo_charge'] = 'charge',
+ ['ammo_petrol'] = 'gallons of fuel',
+ ['ammo_firework'] = 'firework(s)',
+ ['ammo_rockets'] = 'rocket(s)',
+ ['ammo_grenadelauncher'] = 'grenade(s)',
+ ['ammo_grenade'] = 'grenade(s)',
+ ['ammo_stickybomb'] = 'bomb(s)',
+ ['ammo_pipebomb'] = 'bomb(s)',
+ ['ammo_smokebomb'] = 'bomb(s)',
+ ['ammo_molotov'] = 'cocktail(s)',
+ ['ammo_proxmine'] = 'mine(s)',
+ ['ammo_bzgas'] = 'can(s)',
+ ['ammo_ball'] = 'ball(s)',
+ ['ammo_snowball'] = 'snowball(s)',
+ ['ammo_flare'] = 'flare(s)',
+ ['ammo_flaregun'] = 'flare(s)',
-- Weapon Tints
- ['tint_default'] = 'domyślny skin',
- ['tint_green'] = 'zielony skin',
- ['tint_gold'] = 'złoty skin',
- ['tint_pink'] = 'różowy skin',
- ['tint_army'] = 'wojskowy skin',
- ['tint_lspd'] = 'niebieski skin',
- ['tint_orange'] = 'pomarańczowy skin',
- ['tint_platinum'] = 'platynowy skin',
+ ['tint_default'] = 'default skin',
+ ['tint_green'] = 'green skin',
+ ['tint_gold'] = 'gold skin',
+ ['tint_pink'] = 'pink skin',
+ ['tint_army'] = 'army skin',
+ ['tint_lspd'] = 'blue skin',
+ ['tint_orange'] = 'orange skin',
+ ['tint_platinum'] = 'platinum skin',
}
diff --git a/locales/sc.lua b/locales/sc.lua
index 229403de..cfe39ae2 100644
--- a/locales/sc.lua
+++ b/locales/sc.lua
@@ -42,7 +42,7 @@ Locales['sc'] = {
-- Salary related
['received_salary'] = '你收到了你的工资: ~g~$%s~s~',
- ['received_help'] = '你领取到了你的失业救济金: ~g~$%s~s~',
+ ['received_help'] = '鉴于你良好的表现,系统赠送给你一些奖励: ~g~$%s~s~',
['company_nomoney'] = '你受雇的公司太穷了,无法支付你的工资。',
['received_paycheck'] = '收到转账',
['bank'] = '花园银行',
diff --git a/locales/tc.lua b/locales/tc.lua
index 663259fb..9e31c8db 100644
--- a/locales/tc.lua
+++ b/locales/tc.lua
@@ -42,7 +42,7 @@ Locales['tc'] = {
-- Salary related
['received_salary'] = '您收到您的薪水: ~g~$%s~s~',
- ['received_help'] = '你領取到了你的失業救濟金: ~g~$%s~s~',
+ ['received_help'] = '由於您表現良好,系統給予您一些獎勵: ~g~$%s~s~',
['company_nomoney'] = '您的公司太窮了,無法給予您薪水。',
['received_paycheck'] = '收到轉帳',
['bank'] = '花園銀行',
diff --git a/modules/hud/client/events.lua b/modules/hud/client/events.lua
new file mode 100644
index 00000000..9c491962
--- /dev/null
+++ b/modules/hud/client/events.lua
@@ -0,0 +1,5 @@
+local self = ESX.Modules['hud']
+
+AddEventHandler('esx:nui_ready', function()
+ ESX.CreateFrame('hud', 'nui://' .. GetCurrentResourceName() .. '/modules/hud/data/html/ui.html')
+end)
diff --git a/modules/hud/client/main.lua b/modules/hud/client/main.lua
new file mode 100644
index 00000000..3f66e079
--- /dev/null
+++ b/modules/hud/client/main.lua
@@ -0,0 +1,4 @@
+
+local self = ESX.Modules['hud']
+
+
diff --git a/modules/hud/client/module.lua b/modules/hud/client/module.lua
new file mode 100644
index 00000000..8c81ec03
--- /dev/null
+++ b/modules/hud/client/module.lua
@@ -0,0 +1,5 @@
+ESX.Modules['hud'] = {};
+local self = ESX.Modules['hud']
+
+
+
diff --git a/html/css/app.css b/modules/hud/data/html/css/app.css
similarity index 98%
rename from html/css/app.css
rename to modules/hud/data/html/css/app.css
index b487523f..b6f1147c 100644
--- a/html/css/app.css
+++ b/modules/hud/data/html/css/app.css
@@ -20,8 +20,8 @@ html {
padding: 4px;
text-shadow: -1px -1px 0 #000, 1px -1px 0 #000, -1px 1px 0 #000, 1px 1px 0 #000;
text-align: right;
- top: 80;
- right: 40;
+ top: 20;
+ right: 20;
}
#inventory_notifications {
diff --git a/html/fonts/bankgothic.ttf b/modules/hud/data/html/fonts/bankgothic.ttf
similarity index 100%
rename from html/fonts/bankgothic.ttf
rename to modules/hud/data/html/fonts/bankgothic.ttf
diff --git a/html/fonts/pdown.ttf b/modules/hud/data/html/fonts/pdown.ttf
similarity index 100%
rename from html/fonts/pdown.ttf
rename to modules/hud/data/html/fonts/pdown.ttf
diff --git a/html/img/accounts/bank.png b/modules/hud/data/html/img/accounts/bank.png
similarity index 100%
rename from html/img/accounts/bank.png
rename to modules/hud/data/html/img/accounts/bank.png
diff --git a/html/img/accounts/black_money.png b/modules/hud/data/html/img/accounts/black_money.png
similarity index 100%
rename from html/img/accounts/black_money.png
rename to modules/hud/data/html/img/accounts/black_money.png
diff --git a/html/img/accounts/money.png b/modules/hud/data/html/img/accounts/money.png
similarity index 100%
rename from html/img/accounts/money.png
rename to modules/hud/data/html/img/accounts/money.png
diff --git a/html/js/app.js b/modules/hud/data/html/js/app.js
similarity index 100%
rename from html/js/app.js
rename to modules/hud/data/html/js/app.js
diff --git a/html/js/mustache.min.js b/modules/hud/data/html/js/mustache.min.js
similarity index 100%
rename from html/js/mustache.min.js
rename to modules/hud/data/html/js/mustache.min.js
diff --git a/html/js/wrapper.js b/modules/hud/data/html/js/wrapper.js
similarity index 88%
rename from html/js/wrapper.js
rename to modules/hud/data/html/js/wrapper.js
index 0d208709..d85201b1 100644
--- a/html/js/wrapper.js
+++ b/modules/hud/data/html/js/wrapper.js
@@ -33,7 +33,7 @@
if (i == str.length - 1)
data.end = true;
- $.post('http://' + namespace + '/__chunk', JSON.stringify(data));
+ $.post('http://' + GetCurrentResourceName() + '/__chunk', JSON.stringify(data));
}
diff --git a/html/ui.html b/modules/hud/data/html/ui.html
similarity index 100%
rename from html/ui.html
rename to modules/hud/data/html/ui.html
diff --git a/modules/hud/server/events.lua b/modules/hud/server/events.lua
new file mode 100644
index 00000000..d0124661
--- /dev/null
+++ b/modules/hud/server/events.lua
@@ -0,0 +1,2 @@
+local self = ESX.Modules['hud']
+
diff --git a/modules/hud/server/main.lua b/modules/hud/server/main.lua
new file mode 100644
index 00000000..52b7e9d2
--- /dev/null
+++ b/modules/hud/server/main.lua
@@ -0,0 +1,3 @@
+
+local self = ESX.Modules['hud']
+
diff --git a/modules/hud/server/module.lua b/modules/hud/server/module.lua
new file mode 100644
index 00000000..e1db6d5c
--- /dev/null
+++ b/modules/hud/server/module.lua
@@ -0,0 +1,3 @@
+ESX.Modules['hud'] = {};
+local self = ESX.Modules['hud']
+
diff --git a/modules/input/client/events.lua b/modules/input/client/events.lua
new file mode 100644
index 00000000..6a248bdc
--- /dev/null
+++ b/modules/input/client/events.lua
@@ -0,0 +1 @@
+local self = ESX.Modules['input']
diff --git a/modules/input/client/main.lua b/modules/input/client/main.lua
new file mode 100644
index 00000000..12f29bb1
--- /dev/null
+++ b/modules/input/client/main.lua
@@ -0,0 +1,67 @@
+local self = ESX.Modules['input']
+
+Citizen.CreateThread(function()
+ while true do
+
+ local events = {
+ pressed = {},
+ released = {},
+ dpressed = {},
+ dreleased = {},
+ }
+
+ for group, ids in pairs(self.RegisteredControls) do
+
+ for i=1, #ids, 1 do
+
+ local id = ids[i]
+
+ if self.IsControlEnabled(group, id) then
+
+ if IsControlJustPressed(group, id) then
+ events.pressed[#events.pressed + 1] = {group, id, self.LastPressed[group][id]}
+ end
+
+ if IsControlJustReleased(group, id) then
+ events.released[#events.released + 1] = {group, id, self.LastReleased[group][id]}
+ end
+
+ else
+
+ DisableControlAction(group, id, true);
+
+ if IsDisabledControlJustPressed(group, id) then
+ events.dpressed[#events.dpressed + 1] = {group, id, self.LastDisabledPressed[group][id]}
+ self.LastDisabledPressed[group][id] = GetGameTimer()
+ end
+
+ if IsDisabledControlJustReleased(group, id) then
+ events.dreleased[#events.dreleased + 1] = {group, id, self.LastDisabledReleased[group][id]}
+ self.LastDisabledReleased[group][id] = GetGameTimer()
+ end
+
+ end
+
+ end
+ end
+
+ for i=1, #events.pressed, 1 do
+ TriggerEvent('esx:input:pressed:' .. events.pressed[i][1] .. ':' .. events.pressed[i][2], events.pressed[i][3])
+ end
+
+ for i=1, #events.released, 1 do
+ TriggerEvent('esx:input:released:' .. events.released[i][1] .. ':' .. events.released[i][2], events.released[i][3])
+ end
+
+ for i=1, #events.dpressed, 1 do
+ TriggerEvent('esx:input:disabled:pressed:' .. events.dpressed[i][1] .. ':' .. events.dpressed[i][2], events.dpressed[i][3])
+ end
+
+ for i=1, #events.dreleased, 1 do
+ TriggerEvent('esx:input:disabled:released:' .. events.dreleased[i][1] .. ':' .. events.dreleased[i][2], events.dreleased[i][3])
+ end
+
+ Citizen.Wait(0)
+
+ end
+end)
diff --git a/modules/input/client/module.lua b/modules/input/client/module.lua
new file mode 100644
index 00000000..7d8c5ea1
--- /dev/null
+++ b/modules/input/client/module.lua
@@ -0,0 +1,466 @@
+ESX.Modules['input'] = {}
+local self = ESX.Modules['input']
+self.RegisteredControls = {}
+self.EnabledControls = {}
+self.LastPressed = {}
+self.LastDisabledPressed = {}
+self.LastReleased = {}
+self.LastDisabledReleased = {}
+
+self.Groups = {
+ MOVE = 0,
+ LOOK = 1,
+ WHEEL = 2,
+ CELLPHONE_NAVIGATE = 3,
+ CELLPHONE_NAVIGATE_UD = 4,
+ CELLPHONE_NAVIGATE_LR = 5,
+ FRONTEND_DPAD_ALL = 6,
+ FRONTEND_DPAD_UD = 7,
+ FRONTEND_DPAD_LR = 8,
+ FRONTEND_LSTICK_ALL = 9,
+ FRONTEND_RSTICK_ALL = 10,
+ FRONTEND_GENERIC_UD = 11,
+ FRONTEND_GENERIC_LR = 12,
+ FRONTEND_GENERIC_ALL = 13,
+ FRONTEND_BUMPERS = 14,
+ FRONTEND_TRIGGERS = 15,
+ FRONTEND_STICKS = 16,
+ SCRIPT_DPAD_ALL = 17,
+ SCRIPT_DPAD_UD = 18,
+ SCRIPT_DPAD_LR = 19,
+ SCRIPT_LSTICK_ALL = 20,
+ SCRIPT_RSTICK_ALL = 21,
+ SCRIPT_BUMPERS = 22,
+ SCRIPT_TRIGGERS = 23,
+ WEAPON_WHEEL_CYCLE = 24,
+ FLY = 25,
+ SUB = 26,
+ VEH_MOVE_ALL = 27,
+ CURSOR = 28,
+ CURSOR_SCROLL = 29,
+ SNIPER_ZOOM_SECONDARY = 30,
+ VEH_HYDRAULICS_CONTROL = 31,
+}
+
+self.Controls = {
+ NEXT_CAMERA = 0,
+ LOOK_LR = 1,
+ LOOK_UD = 2,
+ LOOK_UP_ONLY = 3,
+ LOOK_DOWN_ONLY = 4,
+ LOOK_LEFT_ONLY = 5,
+ LOOK_RIGHT_ONLY = 6,
+ CINEMATIC_SLOWMO = 7,
+ SCRIPTED_FLY_UD = 8,
+ SCRIPTED_FLY_LR = 9,
+ SCRIPTED_FLY_ZUP = 10,
+ SCRIPTED_FLY_ZDOWN = 11,
+ WEAPON_WHEEL_UD = 12,
+ WEAPON_WHEEL_LR = 13,
+ WEAPON_WHEEL_NEXT = 14,
+ WEAPON_WHEEL_PREV = 15,
+ SELECT_NEXT_WEAPON = 16,
+ SELECT_PREV_WEAPON = 17,
+ SKIP_CUTSCENE = 18,
+ CHARACTER_WHEEL = 19,
+ MULTIPLAYER_INFO = 20,
+ SPRINT = 21,
+ JUMP = 22,
+ ENTER = 23,
+ ATTACK = 24,
+ AIM = 25,
+ LOOK_BEHIND = 26,
+ PHONE = 27,
+ SPECIAL_ABILITY = 28,
+ SPECIAL_ABILITY_SECONDARY = 29,
+ MOVE_LR = 30,
+ MOVE_UD = 31,
+ MOVE_UP_ONLY = 32,
+ MOVE_DOWN_ONLY = 33,
+ MOVE_LEFT_ONLY = 34,
+ MOVE_RIGHT_ONLY = 35,
+ DUCK = 36,
+ SELECT_WEAPON = 37,
+ PICKUP = 38,
+ SNIPER_ZOOM = 39,
+ SNIPER_ZOOM_IN_ONLY = 40,
+ SNIPER_ZOOM_OUT_ONLY = 41,
+ SNIPER_ZOOM_IN_SECONDARY = 42,
+ SNIPER_ZOOM_OUT_SECONDARY = 43,
+ COVER = 44,
+ RELOAD = 45,
+ TALK = 46,
+ DETONATE = 47,
+ HUD_SPECIAL = 48,
+ ARREST = 49,
+ ACCURATE_AIM = 50,
+ CONTEXT = 51,
+ CONTEXT_SECONDARY = 52,
+ WEAPON_SPECIAL = 53,
+ WEAPON_SPECIAL_TWO = 54,
+ DIVE = 55,
+ DROP_WEAPON = 56,
+ DROP_AMMO = 57,
+ THROW_GRENADE = 58,
+ VEH_MOVE_LR = 59,
+ VEH_MOVE_UD = 60,
+ VEH_MOVE_UP_ONLY = 61,
+ VEH_MOVE_DOWN_ONLY = 62,
+ VEH_MOVE_LEFT_ONLY = 63,
+ VEH_MOVE_RIGHT_ONLY = 64,
+ VEH_SPECIAL = 65,
+ VEH_GUN_LR = 66,
+ VEH_GUN_UD = 67,
+ VEH_AIM = 68,
+ VEH_ATTACK = 69,
+ VEH_ATTACK2 = 70,
+ VEH_ACCELERATE = 71,
+ VEH_BRAKE = 72,
+ VEH_DUCK = 73,
+ VEH_HEADLIGHT = 74,
+ VEH_EXIT = 75,
+ VEH_HANDBRAKE = 76,
+ VEH_HOTWIRE_LEFT = 77,
+ VEH_HOTWIRE_RIGHT = 78,
+ VEH_LOOK_BEHIND = 79,
+ VEH_CIN_CAM = 80,
+ VEH_NEXT_RADIO = 81,
+ VEH_PREV_RADIO = 82,
+ VEH_NEXT_RADIO_TRACK = 83,
+ VEH_PREV_RADIO_TRACK = 84,
+ VEH_RADIO_WHEEL = 85,
+ VEH_HORN = 86,
+ VEH_FLY_THROTTLE_UP = 87,
+ VEH_FLY_THROTTLE_DOWN = 88,
+ VEH_FLY_YAW_LEFT = 89,
+ VEH_FLY_YAW_RIGHT = 90,
+ VEH_PASSENGER_AIM = 91,
+ VEH_PASSENGER_ATTACK = 92,
+ VEH_SPECIAL_ABILITY_FRANKLIN = 93,
+ VEH_STUNT_UD = 94,
+ VEH_CINEMATIC_UD = 95,
+ VEH_CINEMATIC_UP_ONLY = 96,
+ VEH_CINEMATIC_DOWN_ONLY = 97,
+ VEH_CINEMATIC_LR = 98,
+ VEH_SELECT_NEXT_WEAPON = 99,
+ VEH_SELECT_PREV_WEAPON = 100,
+ VEH_ROOF = 101,
+ VEH_JUMP = 102,
+ VEH_GRAPPLING_HOOK = 103,
+ VEH_SHUFFLE = 104,
+ VEH_DROP_PROJECTILE = 105,
+ VEH_MOUSE_CONTROL_OVERRIDE = 106,
+ VEH_FLY_ROLL_LR = 107,
+ VEH_FLY_ROLL_LEFT_ONLY = 108,
+ VEH_FLY_ROLL_RIGHT_ONLY = 109,
+ VEH_FLY_PITCH_UD = 110,
+ VEH_FLY_PITCH_UP_ONLY = 111,
+ VEH_FLY_PITCH_DOWN_ONLY = 112,
+ VEH_FLY_UNDERCARRIAGE = 113,
+ VEH_FLY_ATTACK = 114,
+ VEH_FLY_SELECT_NEXT_WEAPON = 115,
+ VEH_FLY_SELECT_PREV_WEAPON = 116,
+ VEH_FLY_SELECT_TARGET_LEFT = 117,
+ VEH_FLY_SELECT_TARGET_RIGHT = 118,
+ VEH_FLY_VERTICAL_FLIGHT_MODE = 119,
+ VEH_FLY_DUCK = 120,
+ VEH_FLY_ATTACK_CAMERA = 121,
+ VEH_FLY_MOUSE_CONTROL_OVERRIDE = 122,
+ VEH_SUB_TURN_LR = 123,
+ VEH_SUB_TURN_LEFT_ONLY = 124,
+ VEH_SUB_TURN_RIGHT_ONLY = 125,
+ VEH_SUB_PITCH_UD = 126,
+ VEH_SUB_PITCH_UP_ONLY = 127,
+ VEH_SUB_PITCH_DOWN_ONLY = 128,
+ VEH_SUB_THROTTLE_UP = 129,
+ VEH_SUB_THROTTLE_DOWN = 130,
+ VEH_SUB_ASCEND = 131,
+ VEH_SUB_DESCEND = 132,
+ VEH_SUB_TURN_HARD_LEFT = 133,
+ VEH_SUB_TURN_HARD_RIGHT = 134,
+ VEH_SUB_MOUSE_CONTROL_OVERRIDE = 135,
+ VEH_PUSHBIKE_PEDAL = 136,
+ VEH_PUSHBIKE_SPRINT = 137,
+ VEH_PUSHBIKE_FRONT_BRAKE = 138,
+ VEH_PUSHBIKE_REAR_BRAKE = 139,
+ MELEE_ATTACK_LIGHT = 140,
+ MELEE_ATTACK_HEAVY = 141,
+ MELEE_ATTACK_ALTERNATE = 142,
+ MELEE_BLOCK = 143,
+ PARACHUTE_DEPLOY = 144,
+ PARACHUTE_DETACH = 145,
+ PARACHUTE_TURN_LR = 146,
+ PARACHUTE_TURN_LEFT_ONLY = 147,
+ PARACHUTE_TURN_RIGHT_ONLY = 148,
+ PARACHUTE_PITCH_UD = 149,
+ PARACHUTE_PITCH_UP_ONLY = 150,
+ PARACHUTE_PITCH_DOWN_ONLY = 151,
+ PARACHUTE_BRAKE_LEFT = 152,
+ PARACHUTE_BRAKE_RIGHT = 153,
+ PARACHUTE_SMOKE = 154,
+ PARACHUTE_PRECISION_LANDING = 155,
+ MAP = 156,
+ SELECT_WEAPON_UNARMED = 157,
+ SELECT_WEAPON_MELEE = 158,
+ SELECT_WEAPON_HANDGUN = 159,
+ SELECT_WEAPON_SHOTGUN = 160,
+ SELECT_WEAPON_SMG = 161,
+ SELECT_WEAPON_AUTO_RIFLE = 162,
+ SELECT_WEAPON_SNIPER = 163,
+ SELECT_WEAPON_HEAVY = 164,
+ SELECT_WEAPON_SPECIAL = 165,
+ SELECT_CHARACTER_MICHAEL = 166,
+ SELECT_CHARACTER_FRANKLIN = 167,
+ SELECT_CHARACTER_TREVOR = 168,
+ SELECT_CHARACTER_MULTIPLAYER = 169,
+ SAVE_REPLAY_CLIP = 170,
+ SPECIAL_ABILITY_PC = 171,
+ CELLPHONE_UP = 172,
+ CELLPHONE_DOWN = 173,
+ CELLPHONE_LEFT = 174,
+ CELLPHONE_RIGHT = 175,
+ CELLPHONE_SELECT = 176,
+ CELLPHONE_CANCEL = 177,
+ CELLPHONE_OPTION = 178,
+ CELLPHONE_EXTRA_OPTION = 179,
+ CELLPHONE_SCROLL_FORWARD = 180,
+ CELLPHONE_SCROLL_BACKWARD = 181,
+ CELLPHONE_CAMERA_FOCUS_LOCK = 182,
+ CELLPHONE_CAMERA_GRID = 183,
+ CELLPHONE_CAMERA_SELFIE = 184,
+ CELLPHONE_CAMERA_DOF = 185,
+ CELLPHONE_CAMERA_EXPRESSION = 186,
+ FRONTEND_DOWN = 187,
+ FRONTEND_UP = 188,
+ FRONTEND_LEFT = 189,
+ FRONTEND_RIGHT = 190,
+ FRONTEND_RDOWN = 191,
+ FRONTEND_RUP = 192,
+ FRONTEND_RLEFT = 193,
+ FRONTEND_RRIGHT = 194,
+ FRONTEND_AXIS_X = 195,
+ FRONTEND_AXIS_Y = 196,
+ FRONTEND_RIGHT_AXIS_X = 197,
+ FRONTEND_RIGHT_AXIS_Y = 198,
+ FRONTEND_PAUSE = 199,
+ FRONTEND_PAUSE_ALTERNATE = 200,
+ FRONTEND_ACCEPT = 201,
+ FRONTEND_CANCEL = 202,
+ FRONTEND_X = 203,
+ FRONTEND_Y = 204,
+ FRONTEND_LB = 205,
+ FRONTEND_RB = 206,
+ FRONTEND_LT = 207,
+ FRONTEND_RT = 208,
+ FRONTEND_LS = 209,
+ FRONTEND_RS = 210,
+ FRONTEND_LEADERBOARD = 211,
+ FRONTEND_SOCIAL_CLUB = 212,
+ FRONTEND_SOCIAL_CLUB_SECONDARY = 213,
+ FRONTEND_DELETE = 214,
+ FRONTEND_ENDSCREEN_ACCEPT = 215,
+ FRONTEND_ENDSCREEN_EXPAND = 216,
+ FRONTEND_SELECT = 217,
+ SCRIPT_LEFT_AXIS_X = 218,
+ SCRIPT_LEFT_AXIS_Y = 219,
+ SCRIPT_RIGHT_AXIS_X = 220,
+ SCRIPT_RIGHT_AXIS_Y = 221,
+ SCRIPT_RUP = 222,
+ SCRIPT_RDOWN = 223,
+ SCRIPT_RLEFT = 224,
+ SCRIPT_RRIGHT = 225,
+ SCRIPT_LB = 226,
+ SCRIPT_RB = 227,
+ SCRIPT_LT = 228,
+ SCRIPT_RT = 229,
+ SCRIPT_LS = 230,
+ SCRIPT_RS = 231,
+ SCRIPT_PAD_UP = 232,
+ SCRIPT_PAD_DOWN = 233,
+ SCRIPT_PAD_LEFT = 234,
+ SCRIPT_PAD_RIGHT = 235,
+ SCRIPT_SELECT = 236,
+ CURSOR_ACCEPT = 237,
+ CURSOR_CANCEL = 238,
+ CURSOR_X = 239,
+ CURSOR_Y = 240,
+ CURSOR_SCROLL_UP = 241,
+ CURSOR_SCROLL_DOWN = 242,
+ ENTER_CHEAT_CODE = 243,
+ INTERACTION_MENU = 244,
+ MP_TEXT_CHAT_ALL = 245,
+ MP_TEXT_CHAT_TEAM = 246,
+ MP_TEXT_CHAT_FRIENDS = 247,
+ MP_TEXT_CHAT_CREW = 248,
+ PUSH_TO_TALK = 249,
+ CREATOR_LS = 250,
+ CREATOR_RS = 251,
+ CREATOR_LT = 252,
+ CREATOR_RT = 253,
+ CREATOR_MENU_TOGGLE = 254,
+ CREATOR_ACCEPT = 255,
+ CREATOR_DELETE = 256,
+ ATTACK2 = 257,
+ RAPPEL_JUMP = 258,
+ RAPPEL_LONG_JUMP = 259,
+ RAPPEL_SMASH_WINDOW = 260,
+ PREV_WEAPON = 261,
+ NEXT_WEAPON = 262,
+ MELEE_ATTACK1 = 263,
+ MELEE_ATTACK2 = 264,
+ WHISTLE = 265,
+ MOVE_LEFT = 266,
+ MOVE_RIGHT = 267,
+ MOVE_UP = 268,
+ MOVE_DOWN = 269,
+ LOOK_LEFT = 270,
+ LOOK_RIGHT = 271,
+ LOOK_UP = 272,
+ LOOK_DOWN = 273,
+ SNIPER_ZOOM_IN = 274,
+ SNIPER_ZOOM_OUT = 275,
+ SNIPER_ZOOM_IN_ALTERNATE = 276,
+ SNIPER_ZOOM_OUT_ALTERNATE = 277,
+ VEH_MOVE_LEFT = 278,
+ VEH_MOVE_RIGHT = 279,
+ VEH_MOVE_UP = 280,
+ VEH_MOVE_DOWN = 281,
+ VEH_GUN_LEFT = 282,
+ VEH_GUN_RIGHT = 283,
+ VEH_GUN_UP = 284,
+ VEH_GUN_DOWN = 285,
+ VEH_LOOK_LEFT = 286,
+ VEH_LOOK_RIGHT = 287,
+ REPLAY_START_STOP_RECORDING = 288,
+ REPLAY_START_STOP_RECORDING_SECONDARY = 289,
+ SCALED_LOOK_LR = 290,
+ SCALED_LOOK_UD = 291,
+ SCALED_LOOK_UP_ONLY = 292,
+ SCALED_LOOK_DOWN_ONLY = 293,
+ SCALED_LOOK_LEFT_ONLY = 294,
+ SCALED_LOOK_RIGHT_ONLY = 295,
+ REPLAY_MARKER_DELETE = 296,
+ REPLAY_CLIP_DELETE = 297,
+ REPLAY_PAUSE = 298,
+ REPLAY_REWIND = 299,
+ REPLAY_FFWD = 300,
+ REPLAY_NEWMARKER = 301,
+ REPLAY_RECORD = 302,
+ REPLAY_SCREENSHOT = 303,
+ REPLAY_HIDEHUD = 304,
+ REPLAY_STARTPOINT = 305,
+ REPLAY_ENDPOINT = 306,
+ REPLAY_ADVANCE = 307,
+ REPLAY_BACK = 308,
+ REPLAY_TOOLS = 309,
+ REPLAY_RESTART = 310,
+ REPLAY_SHOWHOTKEY = 311,
+ REPLAY_CYCLEMARKERLEFT = 312,
+ REPLAY_CYCLEMARKERRIGHT = 313,
+ REPLAY_FOVINCREASE = 314,
+ REPLAY_FOVDECREASE = 315,
+ REPLAY_CAMERAUP = 316,
+ REPLAY_CAMERADOWN = 317,
+ REPLAY_SAVE = 318,
+ REPLAY_TOGGLETIME = 319,
+ REPLAY_TOGGLETIPS = 320,
+ REPLAY_PREVIEW = 321,
+ REPLAY_TOGGLE_TIMELINE = 322,
+ REPLAY_TIMELINE_PICKUP_CLIP = 323,
+ REPLAY_TIMELINE_DUPLICATE_CLIP = 324,
+ REPLAY_TIMELINE_PLACE_CLIP = 325,
+ REPLAY_CTRL = 326,
+ REPLAY_TIMELINE_SAVE = 327,
+ REPLAY_PREVIEW_AUDIO = 328,
+ VEH_DRIVE_LOOK = 329,
+ VEH_DRIVE_LOOK2 = 330,
+ VEH_FLY_ATTACK2 = 331,
+ RADIO_WHEEL_UD = 332,
+ RADIO_WHEEL_LR = 333,
+ VEH_SLOWMO_UD = 334,
+ VEH_SLOWMO_UP_ONLY = 335,
+ VEH_SLOWMO_DOWN_ONLY = 336,
+ VEH_HYDRAULICS_CONTROL_TOGGLE = 337,
+ VEH_HYDRAULICS_CONTROL_LEFT = 338,
+ VEH_HYDRAULICS_CONTROL_RIGHT = 339,
+ VEH_HYDRAULICS_CONTROL_UP = 340,
+ VEH_HYDRAULICS_CONTROL_DOWN = 341,
+ VEH_HYDRAULICS_CONTROL_UD = 342,
+ VEH_HYDRAULICS_CONTROL_LR = 343,
+ MAP_POI = 344,
+ INPUT_REPLAY_SNAPMATIC_PHOTO = 345,
+}
+
+self.RegisterControl = function(group, id)
+ if ESX.Table.IndexOf(self.RegisteredControls[group], id) == -1 then
+ self.RegisteredControls[group][#self.RegisteredControls[group] + 1] = id
+ end
+end
+
+self.UnregisterControl = function(group, id)
+ if ESX.Table.IndexOf(self.RegisteredControls[group], id) ~= -1 then
+ table.remove(self.RegisteredControls[group], ESX.Table.IndexOf(self.RegisteredControls[group], id))
+ end
+end
+
+self.EnableControl = function(group, id)
+ self.EnabledControls[group][id] = self.EnabledControls[group][id] + 1
+end
+
+self.DisableControl = function(group, id)
+ self.EnabledControls[group][id] = self.EnabledControls[group][id] - 1
+end
+
+self.IsControlRegistered = function(group, id)
+ return ESX.Table.IndexOf(self.RegisteredControls[group], id) ~= -1
+end
+
+self.IsControlPressed = function(group, id)
+ return self.IsControlEnabled(group, id) and (IsControlPressed(group, id))
+end
+
+self.IsDisabledControlPressed = function(group, id)
+ return (not self.IsControlEnabled(group, id)) and (IsDisabledControlPressed(group, id))
+end
+
+self.IsControlEnabled = function(group, id)
+ return self.EnabledControls[group][id] >= 0
+end
+
+for k1, group in pairs(self.Groups) do
+
+ self.RegisteredControls[group] = {}
+ self.EnabledControls[group] = {}
+ self.LastPressed[group] = {}
+ self.LastDisabledPressed[group] = {}
+ self.LastReleased[group] = {}
+ self.LastDisabledReleased[group] = {}
+
+ for k2, id in pairs(self.Controls) do
+ self.EnabledControls[group][id] = 0
+ self.LastPressed[group][id] = -1
+ self.LastDisabledPressed[group][id] = -1
+ self.LastReleased[group][id] = -1
+ self.LastDisabledReleased[group][id] = -1
+ end
+
+end
+
+self.On = function(event, group, id, cb)
+
+ return AddEventHandler('esx:input:' .. event .. ':' .. group .. ':' .. id, cb)
+
+end
+
+self.InitESX = function()
+
+ self.RegisterControl(Input.Groups.MOVE, Input.Controls[Config.InventoryKey])
+ self.On('released', Input.Groups.MOVE, Input.Controls[Config.InventoryKey], function(lastPressed)
+
+ if (not ESX.IsDead) and (not ESX.UI.Menu.IsOpen('default', 'es_extended', 'inventory')) then
+ ESX.ShowInventory()
+ end
+
+ end)
+
+end
diff --git a/modules/input/server/events.lua b/modules/input/server/events.lua
new file mode 100644
index 00000000..6a248bdc
--- /dev/null
+++ b/modules/input/server/events.lua
@@ -0,0 +1 @@
+local self = ESX.Modules['input']
diff --git a/modules/input/server/main.lua b/modules/input/server/main.lua
new file mode 100644
index 00000000..6a248bdc
--- /dev/null
+++ b/modules/input/server/main.lua
@@ -0,0 +1 @@
+local self = ESX.Modules['input']
diff --git a/modules/input/server/module.lua b/modules/input/server/module.lua
new file mode 100644
index 00000000..1d041737
--- /dev/null
+++ b/modules/input/server/module.lua
@@ -0,0 +1,2 @@
+ESX.Modules['input'] = {}
+local self = ESX.Modules['input']
diff --git a/modules/interact/client/events.lua b/modules/interact/client/events.lua
new file mode 100644
index 00000000..caf76c7a
--- /dev/null
+++ b/modules/interact/client/events.lua
@@ -0,0 +1,12 @@
+local self = ESX.Modules['interact']
+
+AddEventHandler('esx:interact:register', self.Register)
+
+-- Do not use this in prod ! internal event only
+AddEventHandler('esx:interact:enter', function(name, data)
+ TriggerEvent('esx:interact:enter:' .. name, data)
+end)
+
+AddEventHandler('esx:interact:exit', function(name, data)
+ TriggerEvent('esx:interact:exit:' .. name, data)
+end)
diff --git a/modules/interact/client/main.lua b/modules/interact/client/main.lua
new file mode 100644
index 00000000..78a956a9
--- /dev/null
+++ b/modules/interact/client/main.lua
@@ -0,0 +1,194 @@
+local self = ESX.Modules['interact']
+
+self.LOVE_PLAYER_GROUP = AddRelationshipGroup('LOVE_PLAYER')
+
+SetRelationshipBetweenGroups(0, self.LOVE_PLAYER_GROUP, 'PLAYER')
+
+Citizen.CreateThread(function()
+
+ while true do
+
+ Citizen.Wait(100)
+
+ local ped = PlayerPedId()
+ local coords = GetOffsetFromEntityInWorldCoords(ped, 0.0, 0.0, -1.0)
+
+ self.Cache.player.ped = ped
+ self.Cache.player.coords = vector3(table.unpack(coords))
+
+ local toRemove = {}
+
+ for i=1, #self.Data, 1 do
+
+ local data = self.Data[i]
+
+ if data.pos == nil then
+
+ print('[esx_interact] data.pos is nil => ' .. json.encode(data))
+
+ else
+
+ local distance = #(data.pos - self.Cache.player.coords);
+ if
+ (distance <= data.distance) and
+ (ESX.Table.FindIndex(self.Cache.current, function(e) return e.__id == data.__id end) == -1)
+ then
+
+ if (data.check == nil) or data.check(self.Cache.player.ped, self.Cache.player.coords) then
+ data.playing = false
+ self.Cache.current[#self.Cache.current + 1] = data
+ end
+
+ elseif (distance > data.distance) then
+
+ local idx = ESX.Table.FindIndex(self.Cache.current, function(e) return e.__id == data.__id end)
+
+ if idx ~= -1 then
+ toRemove[#toRemove + 1] = idx
+ end
+
+ end
+
+ end
+
+ end
+
+ if #toRemove > 0 then
+ self.Cache.current = ESX.Table.Filter(self.Cache.current, function(e) return ESX.Table.IndexOf(toRemove, e.__id) ~= -1 end)
+ end
+
+ end
+
+end)
+
+-- Markers
+Citizen.CreateThread(function()
+ while true do
+
+ Citizen.Wait(0)
+
+ for i=1, #self.Cache.current, 1 do
+
+ local curr = self.Cache.current[i]
+
+ if curr.type == 'marker' then
+
+ DrawMarker(
+ curr.mtype,
+ curr.pos.x + 0.0, curr.pos.y + 0.0, curr.pos.z + 0.0,
+ 0.0, 0.0, 0.0,
+ 0.0, 0.0, 0.0,
+ curr.size.x + 0.0, curr.size.y + 0.0, curr.size.z + 0.0,
+ curr.color.r, curr.color.g, curr.color.b, curr.color.a,
+ curr.bobUpAndDown, curr.faceCamera, 2, curr.rotate, nil, nil, false
+ )
+
+ end
+
+ end
+
+ end
+end)
+
+-- NPCs
+Citizen.CreateThread(function()
+ while true do
+
+ Citizen.Wait(1000)
+
+ for i=1, #self.Cache.current, 1 do
+
+ local curr = self.Cache.current[i]
+
+ if curr.type == 'npc' then
+
+ if IsModelValid(curr.model) and IsModelInCdimage(curr.model) then
+
+ local found = false
+
+ local closestPed, closestDistance = ESX.Game.GetClosestPed({
+ x = curr.pos.x + 0.0,
+ y = curr.pos.y + 0.0,
+ z = curr.pos.z + 0.0
+ }, {Cache.player.ped})
+
+ if closestPed ~= -1 then
+
+ local model = GetEntityModel(closestPed)
+
+ if (model == curr.model) and (closestDistance <= 10.0) then
+
+ found = true
+
+ if (curr.lib ~= nil) and (curr.anim ~= nil) then
+
+ if not HasAnimDictLoaded(curr.lib) then
+ RequestAnimDict(curr.lib)
+ end
+
+ if HasAnimDictLoaded(curr.lib) and (not curr.playing) then
+ curr.playing = true
+ TaskPlayAnim(closestPed, curr.lib, curr.anim, 8.0, -8.0, -1, 1, 1.0, false, false, false)
+ end
+
+ end
+
+ end
+
+ end
+
+ if not found then
+
+ print('Create ped : ' .. curr.model .. ' @ ' .. curr.pos.x .. ' ' .. curr.pos.y .. ' ' .. curr.pos.z)
+
+ RequestModel(curr.model)
+
+ while not HasModelLoaded(curr.model) do
+ Citizen.Wait(0)
+ end
+
+ curr.__ped = CreatePed(26, curr.model, curr.pos.x + 0.0, curr.pos.y + 0.0, curr.pos.z + 0.0, curr.heading + 0.0, true, false)
+
+ SetModelAsNoLongerNeeded(curr.model)
+
+ SetPedRelationshipGroupHash(curr.__ped, self.LOVE_PLAYER_GROUP)
+ SetBlockingOfNonTemporaryEvents(curr.__ped, true)
+ TaskStandStill(curr.__ped, -1)
+ SetEntityInvincible(curr.__ped, true)
+ FreezeEntityPosition(curr.__ped, true)
+
+ end
+
+ else
+ print('Invalid/inexistent model => ' .. curr.model)
+ end
+
+ end
+
+ end
+
+ end
+end)
+
+Citizen.CreateThread(function()
+ while true do
+
+ Citizen.Wait(100)
+
+ for i=1, #self.Cache.current, 1 do
+
+ local data = self.Cache.current[i]
+ local distance = #(data.pos - self.Cache.player.coords);
+
+ if (distance <= data.radius) and (not self.Cache.using[data.__id]) then
+ self.Cache.using[data.__id] = true
+ TriggerEvent('esx:interact:enter', data.name, data)
+ elseif (distance > data.radius) and self.Cache.using[data.__id] then
+ self.Cache.using[data.__id] = nil
+ TriggerEvent('esx:interact:exit', data.name, data)
+ end
+
+ end
+
+ end
+end)
diff --git a/modules/interact/client/module.lua b/modules/interact/client/module.lua
new file mode 100644
index 00000000..7fab0039
--- /dev/null
+++ b/modules/interact/client/module.lua
@@ -0,0 +1,47 @@
+ESX.Modules['interact'] = {};
+local self = ESX.Modules['interact']
+
+self.Id = 0
+self.Data = {}
+
+self.Cache = {
+ player = {
+ ped = 0,
+ coords = vector3(0.0, 0.0, 0.0)
+ },
+ current = {},
+ using = {},
+}
+
+self.Register = function(data)
+
+ local idx = -1
+
+ for i=1, #self.Data, 1 do
+ if self.Data[i].name == data.name then
+ idx = i
+ break
+ end
+ end
+
+ if self.Id >= 65535 then
+ data.__id = 1
+ else
+ data.__id = self.Id + 1
+ end
+
+ data.size = (type(data.size) == 'number') and {x = data.size, y = data.size, z = data.size} or data.size
+
+ if data.faceCamera == nil then data.faceCamera = false end
+ if data.bobUpAndDown == nil then data.bobUpAndDown = false end
+ if data.rotate == nil then data.rotate = false end
+
+ self.Id = data.__id
+
+ if idx == -1 then
+ self.Data[#self.Data + 1] = data
+ else
+ self.Data[idx] = data
+ end
+
+end
diff --git a/modules/interact/server/events.lua b/modules/interact/server/events.lua
new file mode 100644
index 00000000..841e735c
--- /dev/null
+++ b/modules/interact/server/events.lua
@@ -0,0 +1,2 @@
+local self = ESX.Modules['interact']
+
diff --git a/modules/interact/server/main.lua b/modules/interact/server/main.lua
new file mode 100644
index 00000000..d1ba7452
--- /dev/null
+++ b/modules/interact/server/main.lua
@@ -0,0 +1,3 @@
+
+local self = ESX.Modules['interact']
+
diff --git a/modules/interact/server/module.lua b/modules/interact/server/module.lua
new file mode 100644
index 00000000..fdccf53a
--- /dev/null
+++ b/modules/interact/server/module.lua
@@ -0,0 +1,3 @@
+ESX.Modules['interact'] = {};
+local self = ESX.Modules['interact']
+
diff --git a/modules/job_police/client/events.lua b/modules/job_police/client/events.lua
new file mode 100644
index 00000000..961c7597
--- /dev/null
+++ b/modules/job_police/client/events.lua
@@ -0,0 +1,311 @@
+local self = ESX.Modules['job_police']
+
+-- Locals
+local Input = ESX.Modules['input']
+
+RegisterNetEvent('esx:setJob')
+AddEventHandler('esx:setJob', function(job)
+ Citizen.Wait(5000)
+ TriggerServerEvent('esx_policejob:forceBlip')
+end)
+
+RegisterNetEvent('esx_phone:loaded')
+AddEventHandler('esx_phone:loaded', function(phoneNumber, contacts)
+
+ local specialContact = {
+ name = _U('job_police:phone_police'),
+ number = 'police',
+ base64Icon = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6NDFGQTJDRkI0QUJCMTFFN0JBNkQ5OENBMUI4QUEzM0YiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6NDFGQTJDRkM0QUJCMTFFN0JBNkQ5OENBMUI4QUEzM0YiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo0MUZBMkNGOTRBQkIxMUU3QkE2RDk4Q0ExQjhBQTMzRiIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo0MUZBMkNGQTRBQkIxMUU3QkE2RDk4Q0ExQjhBQTMzRiIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PoW66EYAAAjGSURBVHjapJcLcFTVGcd/u3cfSXaTLEk2j80TCI8ECI9ABCyoiBqhBVQqVG2ppVKBQqUVgUl5OU7HKqNOHUHU0oHamZZWoGkVS6cWAR2JPJuAQBPy2ISEvLN57+v2u2E33e4k6Ngz85+9d++95/zP9/h/39GpqsqiRYsIGz8QZAq28/8PRfC+4HT4fMXFxeiH+GC54NeCbYLLATLpYe/ECx4VnBTsF0wWhM6lXY8VbBE0Ch4IzLcpfDFD2P1TgrdC7nMCZLRxQ9AkiAkQCn77DcH3BC2COoFRkCSIG2JzLwqiQi0RSmCD4JXbmNKh0+kc/X19tLtc9Ll9sk9ZS1yoU71YIk3xsbEx8QaDEc2ttxmaJSKC1ggSKBK8MKwTFQVXRzs3WzpJGjmZgvxcMpMtWIwqsjztvSrlzjYul56jp+46qSmJmMwR+P3+4aZ8TtCprRkk0DvUW7JjmV6lsqoKW/pU1q9YQOE4Nxkx4ladE7zd8ivuVmJQfXZKW5dx5EwPRw4fxNx2g5SUVLw+33AkzoRaQDP9SkFu6OKqz0uF8yaz7vsOL6ycQVLkcSg/BlWNsjuFoKE1knqDSl5aNnmPLmThrE0UvXqQqvJPyMrMGorEHwQfEha57/3P7mXS684GFjy8kreLppPUuBXfyd/ibeoS2kb0mWPANhJdYjb61AxUvx5PdT3+4y+Tb3mTd19ZSebE+VTXVGNQlHAC7w4VhH8TbA36vKq6ilnzlvPSunHw6Trc7XpZ14AyfgYeyz18crGN1Alz6e3qwNNQSv4dZox1h/BW9+O7eIaEsVv41Y4XeHJDG83Nl4mLTwzGhJYtx0PzNTjOB9KMTlc7Nkcem39YAGU7cbeBKVLMPGMVf296nMd2VbBq1wmizHoqqm/wrS1/Zf0+N19YN2PIu1fcIda4Vk66Zx/rVi+jo9eIX9wZGGcFXUMR6BHUa76/2ezioYcXMtpyAl91DSaTfDxlJbtLprHm2ecpObqPuTPzSNV9yKz4a4zJSuLo71/j8Q17ON69EmXiPIlNMe6FoyzOqWPW/MU03Lw5EFcyKghTrNDh7+/vw545mcJcWbTiGKpRdGPMXbx90sGmDaux6sXk+kimjU+BjnMkx3kYP34cXrFuZ+3nrHi6iDMt92JITcPjk3R3naRwZhpuNSqoD93DKaFVU7j2dhcF8+YzNlpErbIBTVh8toVccbaysPB+4pMcuPw25kwSsau7BIlmHpy3guaOPtISYyi/UkaJM5Lpc5agq5Xkcl6gIHkmqaMn0dtylcjIyPThCNyhaXyfR2W0I1our0v6qBii07ih5rDtGSOxNVdk1y4R2SR8jR/g7hQD9l1jUeY/WLJB5m39AlZN4GZyIQ1fFJNsEgt0duBIc5GRkcZF53mNwIzhXPDgQPoZIkiMkbTxtstDMVnmFA4cOsbz2/aKjSQjev4Mp9ZAg+hIpFhB3EH5Yal16+X+Kq3dGfxkzRY+KauBjBzREvGN0kNCTARu94AejBLMHorAQ7cEQMGs2cXvkWshYLDi6e9l728O8P1XW6hKeB2yv42q18tjj+iFTGoSi+X9jJM9RTxS9E+OHT0krhNiZqlbqraoT7RAU5bBGrEknEBhgJks7KXbLS8qERI0ErVqF/Y4K6NHZfLZB+/wzJvncacvFd91oXO3o/O40MfZKJOKu/rne+mRQByXM4lYreb1tUnkizVVA/0SpfpbWaCNBeEE5gb/UH19NLqEgDF+oNDQWcn41Cj0EXFEWqzkOIyYekslFkThsvMxpIyE2hIc6lXGZ6cPyK7Nnk5OipixRdxgUESAYmhq68VsGgy5CYKCUAJTg0+izApXne3CJFmUTwg4L3FProFxU+6krqmXu3MskkhSD2av41jLdzlnfFrSdCZxyqfMnppN6ZUa7pwt0h3fiK9DCt4IO9e7YqisvI7VYgmNv7mhBKKD/9psNi5dOMv5ZjukjsLdr0ffWsyTi6eSlfcA+dmiVyOXs+/sHNZu3M6PdxzgVO9GmDSHsSNqmTz/R6y6Xxqma4fwaS5Mn85n1ZE0Vl3CHBER3lUNEhiURpPJRFdTOcVnpUJnPIhR7cZXfoH5UYc5+E4RzRH3sfSnl9m2dSMjE+Tz9msse+o5dr7UwcQ5T3HwlWUkNuzG3dKFSTbsNs7m/Y8vExOlC29UWkMJlAxKoRQMR3IC7x85zOn6fHS50+U/2Untx2R1voinu5no+DQmz7yPXmMKZnsu0wrm0Oe3YhOVHdm8A09dBQYhTv4T7C+xUPrZh8Qn2MMr4qcDSRfoirWgKAvtgOpv1JI8Zi77X15G7L+fxeOUOiUFxZiULD5fSlNzNM62W+k1yq5gjajGX/ZHvOIyxd+Fkj+P092rWP/si0Qr7VisMaEWuCiYonXFwbAUTWWPYLV245NITnGkUXnpI9butLJn2y6iba+hlp7C09qBcvoN7FYL9mhxo1/y/LoEXK8Pv6qIC8WbBY/xr9YlPLf9dZT+OqKTUwfmDBm/GOw7ws4FWpuUP2gJEZvKqmocuXPZuWYJMzKuSsH+SNwh3bo0p6hao6HeEqwYEZ2M6aKWd3PwTCy7du/D0F1DsmzE6/WGLr5LsDF4LggnYBacCOboQLHQ3FFfR58SR+HCR1iQH8ukhA5s5o5AYZMwUqOp74nl8xvRHDlRTsnxYpJsUjtsceHt2C8Fm0MPJrphTkZvBc4It9RKLOFx91Pf0Igu0k7W2MmkOewS2QYJUJVWVz9VNbXUVVwkyuAmKTFJayrDo/4Jwe/CT0aGYTrWVYEeUfsgXssMRcpyenraQJa0VX9O3ZU+Ma1fax4xGxUsUVFkOUbcama1hf+7+LmA9juHWshwmwOE1iMmCFYEzg1jtIm1BaxW6wCGGoFdewPfvyE4ertTiv4rHC73B855dwp2a23bbd4tC1hvhOCbX7b4VyUQKhxrtSOaYKngasizvwi0RmOS4O1QZf2yYfiaR+73AvhTQEVf+rpn9/8IMAChKDrDzfsdIQAAAABJRU5ErkJggg=='
+ }
+
+ TriggerEvent('esx_phone:addSpecialContact', specialContact.name, specialContact.number, specialContact.base64Icon)
+
+end)
+
+-- don't show dispatches if the player isn't in service
+AddEventHandler('esx_phone:cancelMessage', function(dispatchNumber)
+
+ if ESX.PlayerData.job and ESX.PlayerData.job.name == 'police' and ESX.PlayerData.job.name == dispatchNumber then
+ -- if esx_service is enabled
+ if self.Config.EnableESXService and not playerInService then
+ CancelEvent()
+ end
+ end
+
+end)
+
+AddEventHandler('esx_policejob:hasEnteredMarker', function(station, part, partNum)
+ if part == 'Cloakroom' then
+ self.CurrentAction = 'menu_cloakroom'
+ self.CurrentActionMsg = _U('job_police:open_cloackroom')
+ self.CurrentActionData = {}
+ elseif part == 'Armory' then
+ self.CurrentAction = 'menu_armory'
+ self.CurrentActionMsg = _U('job_police:open_armory')
+ self.CurrentActionData = {station = station}
+ elseif part == 'Vehicles' then
+ self.CurrentAction = 'menu_vehicle_spawner'
+ self.CurrentActionMsg = _U('job_police:garage_prompt')
+ self.CurrentActionData = {station = station, part = part, partNum = partNum}
+ elseif part == 'Helicopters' then
+ self.CurrentAction = 'Helicopters'
+ self.CurrentActionMsg = _U('job_police:helicopter_prompt')
+ self.CurrentActionData = {station = station, part = part, partNum = partNum}
+ elseif part == 'BossActions' then
+ self.CurrentAction = 'menu_boss_actions'
+ self.CurrentActionMsg = _U('job_police:open_bossmenu')
+ self.CurrentActionData = {}
+ end
+end)
+
+AddEventHandler('esx_policejob:hasExitedMarker', function(station, part, partNum)
+ if not isInShopMenu then
+ ESX.UI.Menu.CloseAll()
+ end
+
+ self.CurrentAction = nil
+end)
+
+AddEventHandler('esx_policejob:hasEnteredEntityZone', function(entity)
+ local playerPed = PlayerPedId()
+
+ if ESX.PlayerData.job and ESX.PlayerData.job.name == 'police' and IsPedOnFoot(playerPed) then
+ self.CurrentAction = 'remove_entity'
+ self.CurrentActionMsg = _U('job_police:remove_prop')
+ self.CurrentActionData = {entity = entity}
+ end
+
+ if GetEntityModel(entity) == GetHashKey('p_ld_stinger_s') then
+ local playerPed = PlayerPedId()
+ local coords = GetEntityCoords(playerPed)
+
+ if IsPedInAnyVehicle(playerPed, false) then
+ local vehicle = GetVehiclePedIsIn(playerPed)
+
+ for i=0, 7, 1 do
+ SetVehicleTyreBurst(vehicle, i, true, 1000)
+ end
+ end
+ end
+end)
+
+AddEventHandler('esx_policejob:hasExitedEntityZone', function(entity)
+ if self.CurrentAction == 'remove_entity' then
+ self.CurrentAction = nil
+ end
+end)
+
+RegisterNetEvent('esx_policejob:handcuff')
+AddEventHandler('esx_policejob:handcuff', function()
+ self.IsHandcuffed = not self.IsHandcuffed
+ local playerPed = PlayerPedId()
+
+ if self.IsHandcuffed then
+ RequestAnimDict('mp_arresting')
+ while not HasAnimDictLoaded('mp_arresting') do
+ Citizen.Wait(100)
+ end
+
+ TaskPlayAnim(playerPed, 'mp_arresting', 'idle', 8.0, -8, -1, 49, 0, 0, 0, 0)
+
+ SetEnableHandcuffs(playerPed, true)
+ DisablePlayerFiring(playerPed, true)
+ SetCurrentPedWeapon(playerPed, GetHashKey('WEAPON_UNARMED'), true) -- unarm player
+ SetPedCanPlayGestureAnims(playerPed, false)
+ FreezeEntityPosition(playerPed, true)
+ DisplayRadar(false)
+
+ if self.Config.EnableHandcuffTimer then
+ if handcuffTimer.active then
+ ESX.ClearTimeout(handcuffTimer.task)
+ end
+
+ StartHandcuffTimer()
+ end
+ else
+ if self.Config.EnableHandcuffTimer and handcuffTimer.active then
+ ESX.ClearTimeout(handcuffTimer.task)
+ end
+
+ ClearPedSecondaryTask(playerPed)
+ SetEnableHandcuffs(playerPed, false)
+ DisablePlayerFiring(playerPed, false)
+ SetPedCanPlayGestureAnims(playerPed, true)
+ FreezeEntityPosition(playerPed, false)
+ DisplayRadar(true)
+ end
+end)
+
+RegisterNetEvent('esx_policejob:unrestrain')
+AddEventHandler('esx_policejob:unrestrain', function()
+
+ if self.IsHandcuffed then
+
+ local playerPed = PlayerPedId()
+
+ self.IsHandcuffed = false
+
+ ClearPedSecondaryTask(playerPed)
+ SetEnableHandcuffs(playerPed, false)
+ DisablePlayerFiring(playerPed, false)
+ SetPedCanPlayGestureAnims(playerPed, true)
+ FreezeEntityPosition(playerPed, false)
+ DisplayRadar(true)
+
+ -- end timer
+ if self.Config.EnableHandcuffTimer and handcuffTimer.active then
+ ESX.ClearTimeout(handcuffTimer.task)
+ end
+ end
+end)
+
+RegisterNetEvent('esx_policejob:drag')
+AddEventHandler('esx_policejob:drag', function(copId)
+ if self.IsHandcuffed then
+ dragStatus.isDragged = not dragStatus.isDragged
+ dragStatus.CopId = copId
+ end
+end)
+
+RegisterNetEvent('esx_policejob:putInVehicle')
+AddEventHandler('esx_policejob:putInVehicle', function()
+ if self.IsHandcuffed then
+ local playerPed = PlayerPedId()
+ local coords = GetEntityCoords(playerPed)
+
+ if IsAnyVehicleNearPoint(coords, 5.0) then
+ local vehicle = GetClosestVehicle(coords, 5.0, 0, 71)
+
+ if DoesEntityExist(vehicle) then
+ local maxSeats, freeSeat = GetVehicleMaxNumberOfPassengers(vehicle)
+
+ for i=maxSeats - 1, 0, -1 do
+ if IsVehicleSeatFree(vehicle, i) then
+ freeSeat = i
+ break
+ end
+ end
+
+ if freeSeat then
+ TaskWarpPedIntoVehicle(playerPed, vehicle, freeSeat)
+ dragStatus.isDragged = false
+ end
+ end
+ end
+ end
+end)
+
+RegisterNetEvent('esx_policejob:OutVehicle')
+AddEventHandler('esx_policejob:OutVehicle', function()
+ local playerPed = PlayerPedId()
+
+ if IsPedSittingInAnyVehicle(playerPed) then
+ local vehicle = GetVehiclePedIsIn(playerPed, false)
+ TaskLeaveVehicle(playerPed, vehicle, 16)
+ end
+end)
+
+RegisterNetEvent('esx_policejob:updateBlip')
+AddEventHandler('esx_policejob:updateBlip', function()
+
+ -- Refresh all blips
+ for k, existingBlip in pairs(self.BlipsCops) do
+ RemoveBlip(existingBlip)
+ end
+
+ -- Clean the blip table
+ self.BlipsCops = {}
+
+ -- Enable blip?
+ if self.Config.EnableESXService and not playerInService then
+ return
+ end
+
+ if not self.Config.EnableJobBlip then
+ return
+ end
+
+ -- Is the player a cop? In that case show all the blips for other cops
+ if ESX.PlayerData.job and ESX.PlayerData.job.name == 'police' then
+ ESX.TriggerServerCallback('esx_society:getOnlinePlayers', function(players)
+ for i=1, #players, 1 do
+ if players[i].job.name == 'police' then
+ local id = GetPlayerFromServerId(players[i].source)
+ if NetworkIsPlayerActive(id) and GetPlayerPed(id) ~= PlayerPedId() then
+ CreateBlip(id)
+ end
+ end
+ end
+ end)
+ end
+
+end)
+
+AddEventHandler('playerSpawned', function(spawn)
+ isDead = false
+ TriggerEvent('esx_policejob:unrestrain')
+
+ if not hasAlreadyJoined then
+ TriggerServerEvent('esx_policejob:spawned')
+ end
+ hasAlreadyJoined = true
+end)
+
+AddEventHandler('esx:onPlayerDeath', function(data)
+ isDead = true
+end)
+
+AddEventHandler('onResourceStop', function(resource)
+ if resource == GetCurrentResourceName() then
+ TriggerEvent('esx_policejob:unrestrain')
+ TriggerEvent('esx_phone:removeSpecialContact', 'police')
+
+ if self.Config.EnableESXService then
+ TriggerServerEvent('esx_service:disableService', 'police')
+ end
+
+ if self.Config.EnableHandcuffTimer and self.HandcuffTimer.active then
+ ESX.ClearTimeout(self.HandcuffTimer.task)
+ end
+ end
+end)
+
+-- Key Controls
+Input.On('released', Input.Groups.MOVE, Input.Controls.PICKUP, function(lastPressed)
+
+ if self.CurrentAction and self.IsPolice() then
+ self.CurrentAction()
+ self.CurrentAction = nil
+ end
+
+end)
+
+Input.On('released', Input.Groups.MOVE, Input.Controls.SELECT_CHARACTER_FRANKLIN, function(lastPressed)
+
+ if (not self.IsDead) and self.IsPolice() and (not ESX.UI.Menu.IsOpen('default', GetCurrentResourceName(), 'police_actions')) then
+
+ if not self.Config.EnableESXService then
+ OpenPoliceActionsMenu()
+ elseif playerInService then
+ OpenPoliceActionsMenu()
+ else
+ ESX.ShowNotification(_U('job_police:service_not'))
+ end
+
+ end
+
+end)
+
+Input.On('released', Input.Groups.MOVE, Input.Controls.PICKUP, function(lastPressed)
+
+ if self.CurrentAction and self.IsPolice() and self.CurrentTask.busy then
+
+ ESX.ShowNotification(_U('job_police:impound_canceled'))
+ ESX.ClearTimeout(self.CurrentTask.task)
+ ClearPedTasks(PlayerPedId())
+
+ self.CurrentTask.busy = false
+
+ end
+
+end)
diff --git a/modules/job_police/client/main.lua b/modules/job_police/client/main.lua
new file mode 100644
index 00000000..b5a21e69
--- /dev/null
+++ b/modules/job_police/client/main.lua
@@ -0,0 +1,157 @@
+local self = ESX.Modules['job_police']
+
+-- Init
+self.Init()
+
+-- Drag
+Citizen.CreateThread(function()
+
+ local wasDragged = false
+
+ while true do
+ Citizen.Wait(0)
+ local playerPed = PlayerPedId()
+
+ if self.IsHandcuffed and self.DragStatus.isDragged then
+
+ local targetPed = GetPlayerPed(GetPlayerFromServerId(self.DragStatus.CopId))
+
+ if DoesEntityExist(targetPed) and IsPedOnFoot(targetPed) and not IsPedDeadOrDying(targetPed, true) then
+
+ if not wasDragged then
+ AttachEntityToEntity(playerPed, targetPed, 11816, 0.54, 0.54, 0.0, 0.0, 0.0, 0.0, false, false, false, false, 2, true)
+ wasDragged = true
+ else
+ Citizen.Wait(1000)
+ end
+
+ else
+
+ wasDragged = false
+ self.DragStatus.isDragged = false
+ DetachEntity(playerPed, true, false)
+
+ end
+
+ elseif wasDragged then
+
+ wasDragged = false
+ DetachEntity(playerPed, true, false)
+
+ else
+
+ Citizen.Wait(500)
+
+ end
+
+ end
+end)
+
+-- Handcuff
+Citizen.CreateThread(function()
+ while true do
+ Citizen.Wait(0)
+ local playerPed = PlayerPedId()
+
+ if self.IsHandcuffed then
+ DisableControlAction(0, 1, true) -- Disable pan
+ DisableControlAction(0, 2, true) -- Disable tilt
+ DisableControlAction(0, 24, true) -- Attack
+ DisableControlAction(0, 257, true) -- Attack 2
+ DisableControlAction(0, 25, true) -- Aim
+ DisableControlAction(0, 263, true) -- Melee Attack 1
+ DisableControlAction(0, 32, true) -- W
+ DisableControlAction(0, 34, true) -- A
+ DisableControlAction(0, 31, true) -- S
+ DisableControlAction(0, 30, true) -- D
+
+ DisableControlAction(0, 45, true) -- Reload
+ DisableControlAction(0, 22, true) -- Jump
+ DisableControlAction(0, 44, true) -- Cover
+ DisableControlAction(0, 37, true) -- Select Weapon
+ DisableControlAction(0, 23, true) -- Also 'enter'?
+
+ DisableControlAction(0, 288, true) -- Disable phone
+ DisableControlAction(0, 289, true) -- Inventory
+ DisableControlAction(0, 170, true) -- Animations
+ DisableControlAction(0, 167, true) -- Job
+
+ DisableControlAction(0, 0, true) -- Disable changing view
+ DisableControlAction(0, 26, true) -- Disable looking behind
+ DisableControlAction(0, 73, true) -- Disable clearing animation
+ DisableControlAction(2, 199, true) -- Disable pause screen
+
+ DisableControlAction(0, 59, true) -- Disable steering in vehicle
+ DisableControlAction(0, 71, true) -- Disable driving forward in vehicle
+ DisableControlAction(0, 72, true) -- Disable reversing in vehicle
+
+ DisableControlAction(2, 36, true) -- Disable going stealth
+
+ DisableControlAction(0, 47, true) -- Disable weapon
+ DisableControlAction(0, 264, true) -- Disable melee
+ DisableControlAction(0, 257, true) -- Disable melee
+ DisableControlAction(0, 140, true) -- Disable melee
+ DisableControlAction(0, 141, true) -- Disable melee
+ DisableControlAction(0, 142, true) -- Disable melee
+ DisableControlAction(0, 143, true) -- Disable melee
+ DisableControlAction(0, 75, true) -- Disable exit vehicle
+ DisableControlAction(27, 75, true) -- Disable exit vehicle
+
+ if IsEntityPlayingAnim(playerPed, 'mp_arresting', 'idle', 3) ~= 1 then
+ ESX.Streaming.RequestAnimDict('mp_arresting', function()
+ TaskPlayAnim(playerPed, 'mp_arresting', 'idle', 8.0, -8, -1, 49, 0.0, false, false, false)
+ end)
+ end
+ else
+ Citizen.Wait(500)
+ end
+ end
+end)
+
+-- Enter / Exit entity zone events
+Citizen.CreateThread(function()
+ local trackedEntities = {
+ 'prop_roadcone02a',
+ 'prop_barrier_work05',
+ 'p_ld_stinger_s',
+ 'prop_boxpile_07d',
+ 'hei_prop_cash_crate_half_full'
+ }
+
+ while true do
+ Citizen.Wait(500)
+
+ local playerPed = PlayerPedId()
+ local playerCoords = GetEntityCoords(playerPed)
+
+ local closestDistance = -1
+ local closestEntity = nil
+
+ for i=1, #trackedEntities, 1 do
+ local object = GetClosestObjectOfType(playerCoords, 3.0, GetHashKey(trackedEntities[i]), false, false, false)
+
+ if DoesEntityExist(object) then
+ local objCoords = GetEntityCoords(object)
+ local distance = #(playerCoords - objCoords)
+
+ if closestDistance == -1 or closestDistance > distance then
+ closestDistance = distance
+ closestEntity = object
+ end
+ end
+ end
+
+ if closestDistance ~= -1 and closestDistance <= 3.0 then
+ if LastEntity ~= closestEntity then
+ TriggerEvent('esx_policejob:hasEnteredEntityZone', closestEntity)
+ LastEntity = closestEntity
+ end
+ else
+ if LastEntity then
+ TriggerEvent('esx_policejob:hasExitedEntityZone', LastEntity)
+ LastEntity = nil
+ end
+ end
+ end
+end)
+
diff --git a/modules/job_police/client/module.lua b/modules/job_police/client/module.lua
new file mode 100644
index 00000000..ed9d78dd
--- /dev/null
+++ b/modules/job_police/client/module.lua
@@ -0,0 +1,1517 @@
+ESX.Modules['job_police'] = {}
+local self = ESX.Modules['job_police']
+
+-- Properties
+self.Config = ESX.EvalFile(GetCurrentResourceName(), 'modules/job_police/data/config.lua', {
+ vector3 = vector3
+})['Config']
+
+self.CurrentActionData = {}
+self.HandcuffTimer = {}
+self.DragStatus = {isDragged = false}
+self.BlipsCops = {}
+self.CurrentTask = {}
+self.HasAlreadyEnteredMarker = false
+self.IsDead = false
+self.IsHandcuffed = false
+self.HasAlreadyJoined = false
+self.PlayerInService = false
+self.LastStation = nil
+self.LastPart = nil
+self.LastPartNum = nil
+self.LastEntity = nil
+self.CurrentAction = nil
+self.CurrentActionMsg = nil
+self.IsInShopMenu = false
+self.SpawnedVehicles = {}
+
+-- Locals
+local Interact = ESX.Modules['interact']
+local Input = ESX.Modules['input']
+
+-- Methods
+self.Init = function()
+
+ self.RegisterControls()
+
+ local translations = ESX.EvalFile(GetCurrentResourceName(), 'modules/job_police/data/locales/' .. Config.Locale .. '.lua')['Translations']
+ LoadLocale('job_police', Config.Locale, translations)
+
+ for stationName, station in pairs(self.Config.PoliceStations) do
+
+ -- Blip
+ local blip = AddBlipForCoord(station.Blip.Coords)
+
+ SetBlipSprite (blip, station.Blip.Sprite)
+ SetBlipDisplay(blip, station.Blip.Display)
+ SetBlipScale (blip, station.Blip.Scale)
+ SetBlipColour (blip, station.Blip.Colour)
+ SetBlipAsShortRange(blip, true)
+
+ BeginTextCommandSetBlipName('STRING')
+ AddTextComponentSubstringPlayerName(_U('job_police:map_blip'))
+ EndTextCommandSetBlipName(blip)
+
+ -- Cloakrooms
+ for i=1, #station.Cloakrooms, 1 do
+
+ local cloakroom = station.Cloakrooms[i]
+ local key = 'job_police:' .. stationName .. 'cloakroom:' .. tostring(i)
+
+ Interact.Register({
+ name = key,
+ type = 'marker',
+ distance = self.Config.DrawDistance,
+ radius = 2.0,
+ pos = cloakroom,
+ size = 1.0,
+ mtype = 20,
+ color = self.Config.MarkerColor,
+ rotate = true,
+ check = self.IsPolice,
+ })
+
+ AddEventHandler('esx:interact:enter:' .. key, function(data)
+
+ ESX.ShowHelpNotification(_U('job_police:open_cloackroom'))
+
+ self.CurrentAction = function()
+ self.OpenCloakroomMenu(station)
+ end
+
+ end)
+
+ AddEventHandler('esx:interact:exit:' .. key, function(data)
+ self.CurrentAction = nil
+ end)
+
+ end
+
+ -- Armories
+ for i=1, #station.Armories, 1 do
+
+ local armory = station.Armories[i]
+ local key = 'job_police:' .. stationName .. ':armory:' .. tostring(i)
+
+ Interact.Register({
+ name = key,
+ type = 'marker',
+ distance = self.Config.DrawDistance,
+ radius = 1.0,
+ pos = armory,
+ size = 0.5,
+ mtype = 21,
+ color = self.Config.MarkerColor,
+ rotate = true,
+ check = self.IsPolice,
+ })
+
+ AddEventHandler('esx:interact:enter:' .. key, function(data)
+
+ ESX.ShowHelpNotification(_U('job_police:open_armory'))
+
+ self.CurrentAction = function()
+ self.OpenArmoryMenu(station)
+ end
+
+ end)
+
+ AddEventHandler('esx:interact:exit:' .. key, function(data)
+ self.CurrentAction = nil
+ end)
+
+ end
+
+ -- Vehicles
+ for i=1, #station.Vehicles, 1 do
+
+ local vehicle = station.Vehicles[i]
+ local key = 'job_police:' .. stationName .. ':garage:' .. tostring(i)
+
+ Interact.Register({
+ name = key,
+ type = 'marker',
+ distance = self.Config.DrawDistance,
+ radius = 2.0,
+ pos = vehicle.Spawner,
+ size = 1.0,
+ mtype = 36,
+ color = self.Config.MarkerColor,
+ rotate = true,
+ check = self.IsPolice,
+ config = vehicle
+ })
+
+ AddEventHandler('esx:interact:enter:' .. key, function(data)
+
+ ESX.ShowHelpNotification(_U('job_police:garage_prompt'))
+
+ self.CurrentAction = function()
+ self.OpenVehicleSpawnerMenu('car', data.config)
+ end
+
+ end)
+
+ AddEventHandler('esx:interact:exit:' .. key, function(data)
+ self.CurrentAction = nil
+ end)
+
+ end
+
+ -- Helicopters
+ for i=1, #station.Helicopters, 1 do
+
+ local heli = station.Helicopters[i]
+ local key = 'job_police:' .. stationName .. ':garage_heli:' .. tostring(i)
+
+ Interact.Register({
+ name = key,
+ type = 'marker',
+ distance = self.Config.DrawDistance,
+ radius = 2.0,
+ pos = heli.Spawner,
+ size = 1.0,
+ mtype = 36,
+ color = self.Config.MarkerColor,
+ rotate = true,
+ check = self.IsPolice,
+ config = heli
+ })
+
+ AddEventHandler('esx:interact:enter:' .. key, function(data)
+
+ ESX.ShowHelpNotification(_U('job_police:helicopter_prompt'))
+
+ self.CurrentAction = function()
+ self.OpenVehicleSpawnerMenu('helicopter', data.config)
+ end
+
+ end)
+
+ AddEventHandler('esx:interact:exit:' .. key, function(data)
+ self.CurrentAction = nil
+ end)
+
+ end
+
+ end
+
+end
+
+self.RegisterControls = function()
+
+ Input.RegisterControl(Input.Groups.MOVE, Input.Controls.PICKUP)
+ Input.RegisterControl(Input.Groups.MOVE, Input.Controls.SELECT_CHARACTER_FRANKLIN)
+ Input.RegisterControl(Input.Groups.MOVE, Input.Controls.PICKUP)
+
+end
+
+self.IsPolice = function(playerPed, coords)
+ return ESX.PlayerData.job and ESX.PlayerData.job.name == 'police'
+end
+
+self.CleanPlayer = function(playerPed)
+ SetPedArmour(playerPed, 0)
+ ClearPedBloodDamage(playerPed)
+ ResetPedVisibleDamage(playerPed)
+ ClearPedLastWeaponDamage(playerPed)
+ ResetPedMovementClipset(playerPed, 0)
+end
+
+self.SetUniform = function(uniform, playerPed)
+
+ TriggerEvent('skinchanger:getSkin', function(skin)
+
+ local uniformObject
+
+ if skin.sex == 0 then
+ uniformObject = self.Config.Uniforms[uniform].male
+ else
+ uniformObject = self.Config.Uniforms[uniform].female
+ end
+
+ if uniformObject then
+ TriggerEvent('skinchanger:loadClothes', skin, uniformObject)
+
+ if uniform == 'bullet_wear' then
+ SetPedArmour(playerPed, 100)
+ end
+ else
+ ESX.ShowNotification(_U('job_police:no_outfit'))
+ end
+ end)
+
+end
+
+self.OpenCloakroomMenu = function()
+
+ local playerPed = PlayerPedId()
+ local grade = ESX.PlayerData.job.grade_name
+
+ local elements = {
+ {label = _U('job_police:citizen_wear'), value = 'citizen_wear'},
+ {label = _U('job_police:bullet_wear'), uniform = 'bullet_wear'},
+ {label = _U('job_police:gilet_wear'), uniform = 'gilet_wear'},
+ {label = _U('job_police:police_wear'), uniform = grade}
+ }
+
+ if self.Config.EnableCustomPeds then
+ for k,v in ipairs(Config.CustomPeds.shared) do
+ table.insert(elements, {label = v.label, value = 'freemode_ped', maleModel = v.maleModel, femaleModel = v.femaleModel})
+ end
+
+ for k,v in ipairs(Config.CustomPeds[grade]) do
+ table.insert(elements, {label = v.label, value = 'freemode_ped', maleModel = v.maleModel, femaleModel = v.femaleModel})
+ end
+ end
+
+ ESX.UI.Menu.CloseAll()
+
+ ESX.UI.Menu.Open('default', GetCurrentResourceName(), 'cloakroom', {
+ title = _U('job_police:cloakroom'),
+ align = 'top-left',
+ elements = elements
+ }, function(data, menu)
+
+ menu.close()
+
+ self.CleanPlayer(playerPed)
+
+ if data.current.value == 'citizen_wear' then
+ if self.Config.EnableNonFreemodePeds then
+ ESX.TriggerServerCallback('esx_skin:getPlayerSkin', function(skin, jobSkin)
+ local isMale = skin.sex == 0
+
+ TriggerEvent('skinchanger:loadDefaultModel', isMale, function()
+ ESX.TriggerServerCallback('esx_skin:getPlayerSkin', function(skin)
+ TriggerEvent('skinchanger:loadSkin', skin)
+ TriggerEvent('esx:restoreLoadout')
+ end)
+ end)
+
+ end)
+ else
+ ESX.TriggerServerCallback('esx_skin:getPlayerSkin', function(skin)
+ TriggerEvent('skinchanger:loadSkin', skin)
+ end)
+ end
+
+ if self.Config.EnableESXService then
+ ESX.TriggerServerCallback('esx_service:isInService', function(isInService)
+ if isInService then
+ PlayerInService = false
+
+ local notification = {
+ title = _U('job_police:service_anonunce'),
+ subject = '',
+ msg = _U('job_police:service_out_announce', GetPlayerName(PlayerId())),
+ iconType = 1
+ }
+
+ TriggerServerEvent('esx_service:notifyAllInService', notification, 'police')
+
+ TriggerServerEvent('esx_service:disableService', 'police')
+ TriggerEvent('esx_policejob:updateBlip')
+ ESX.ShowNotification(_U('job_police:service_out'))
+ end
+ end, 'police')
+ end
+ end
+
+ if self.Config.EnableESXService and data.current.value ~= 'citizen_wear' then
+ local awaitService
+
+ ESX.TriggerServerCallback('esx_service:isInService', function(isInService)
+ if not isInService then
+
+ ESX.TriggerServerCallback('esx_service:enableService', function(canTakeService, maxInService, inServiceCount)
+ if not canTakeService then
+ ESX.ShowNotification(_U('job_police:service_max', inServiceCount, maxInService))
+ else
+ awaitService = true
+ PlayerInService = true
+
+ local notification = {
+ title = _U('job_police:service_anonunce'),
+ subject = '',
+ msg = _U('job_police:service_in_announce', GetPlayerName(PlayerId())),
+ iconType = 1
+ }
+
+ TriggerServerEvent('esx_service:notifyAllInService', notification, 'police')
+ TriggerEvent('esx_policejob:updateBlip')
+ ESX.ShowNotification(_U('job_police:service_in'))
+ end
+ end, 'police')
+
+ else
+ awaitService = true
+ end
+ end, 'police')
+
+ while awaitService == nil do
+ Citizen.Wait(5)
+ end
+
+ -- if we couldn't enter service don't let the player get changed
+ if not awaitService then
+ return
+ end
+ end
+
+ if data.current.uniform then
+ self.SetUniform(data.current.uniform, playerPed)
+ elseif data.current.value == 'freemode_ped' then
+ local modelHash
+
+ ESX.TriggerServerCallback('esx_skin:getPlayerSkin', function(skin, jobSkin)
+ if skin.sex == 0 then
+ modelHash = GetHashKey(data.current.maleModel)
+ else
+ modelHash = GetHashKey(data.current.femaleModel)
+ end
+
+ ESX.Streaming.RequestModel(modelHash, function()
+ SetPlayerModel(PlayerId(), modelHash)
+ SetModelAsNoLongerNeeded(modelHash)
+ SetPedDefaultComponentVariation(PlayerPedId())
+
+ TriggerEvent('esx:restoreLoadout')
+ end)
+ end)
+ end
+ end, function(data, menu)
+
+ menu.close()
+
+ end, nil, function()
+
+ ESX.ShowHelpNotification(_U('job_police:open_cloackroom'))
+
+ self.CurrentAction = function()
+ self.OpenCloakroomMenu(station)
+ end
+
+ end)
+
+end
+
+self.OpenArmoryMenu = function(station)
+ local elements = {
+ {label = _U('job_police:buy_weapons'), value = 'buy_weapons'}
+ }
+
+ if self.Config.EnableArmoryManagement then
+ table.insert(elements, {label = _U('job_police:get_weapon'), value = 'get_weapon'})
+ table.insert(elements, {label = _U('job_police:put_weapon'), value = 'put_weapon'})
+ table.insert(elements, {label = _U('job_police:remove_object'), value = 'get_stock'})
+ table.insert(elements, {label = _U('job_police:deposit_object'), value = 'put_stock'})
+ end
+
+ ESX.UI.Menu.CloseAll()
+
+ ESX.UI.Menu.Open('default', GetCurrentResourceName(), 'armory', {
+ title = _U('job_police:armory'),
+ align = 'top-left',
+ elements = elements
+ }, function(data, menu)
+
+ if data.current.value == 'get_weapon' then
+ self.OpenGetWeaponMenu()
+ elseif data.current.value == 'put_weapon' then
+ self.OpenPutWeaponMenu()
+ elseif data.current.value == 'buy_weapons' then
+ self.OpenBuyWeaponsMenu()
+ elseif data.current.value == 'put_stock' then
+ self.OpenPutStocksMenu()
+ elseif data.current.value == 'get_stock' then
+ self.OpenGetStocksMenu()
+ end
+
+ end, function(data, menu)
+ menu.close()
+ end, nil, function()
+
+
+ ESX.ShowHelpNotification(_U('job_police:open_armory'))
+
+ self.CurrentAction = function()
+ self.OpenArmoryMenu(station)
+ end
+
+ return true
+
+ end)
+
+end
+
+self.OpenPoliceActionsMenu = function()
+ ESX.UI.Menu.CloseAll()
+
+ ESX.UI.Menu.Open('default', GetCurrentResourceName(), 'police_actions', {
+ title = 'Police',
+ align = 'top-left',
+ elements = {
+ {label = _U('job_police:citizen_interaction'), value = 'citizen_interaction'},
+ {label = _U('job_police:vehicle_interaction'), value = 'vehicle_interaction'},
+ {label = _U('job_police:object_spawner'), value = 'object_spawner'}
+ }}, function(data, menu)
+ if data.current.value == 'citizen_interaction' then
+ local elements = {
+ {label = _U('job_police:id_card'), value = 'identity_card'},
+ {label = _U('job_police:search'), value = 'search'},
+ {label = _U('job_police:handcuff'), value = 'handcuff'},
+ {label = _U('job_police:drag'), value = 'drag'},
+ {label = _U('job_police:put_in_vehicle'), value = 'put_in_vehicle'},
+ {label = _U('job_police:out_the_vehicle'), value = 'out_the_vehicle'},
+ {label = _U('job_police:fine'), value = 'fine'},
+ {label = _U('job_police:unpaid_bills'), value = 'unpaid_bills'}
+ }
+
+ if self.Config.EnableLicenses then
+ table.insert(elements, {label = _U('job_police:license_check'), value = 'license'})
+ end
+
+ ESX.UI.Menu.Open('default', GetCurrentResourceName(), 'citizen_interaction', {
+ title = _U('job_police:citizen_interaction'),
+ align = 'top-left',
+ elements = elements
+ }, function(data2, menu2)
+ local closestPlayer, closestDistance = ESX.Game.GetClosestPlayer()
+ if closestPlayer ~= -1 and closestDistance <= 3.0 then
+ local action = data2.current.value
+
+ if action == 'identity_card' then
+ OpenIdentityCardMenu(closestPlayer)
+ elseif action == 'search' then
+ OpenBodySearchMenu(closestPlayer)
+ elseif action == 'handcuff' then
+ TriggerServerEvent('esx_policejob:handcuff', GetPlayerServerId(closestPlayer))
+ elseif action == 'drag' then
+ TriggerServerEvent('esx_policejob:drag', GetPlayerServerId(closestPlayer))
+ elseif action == 'put_in_vehicle' then
+ TriggerServerEvent('esx_policejob:putInVehicle', GetPlayerServerId(closestPlayer))
+ elseif action == 'out_the_vehicle' then
+ TriggerServerEvent('esx_policejob:OutVehicle', GetPlayerServerId(closestPlayer))
+ elseif action == 'fine' then
+ OpenFineMenu(closestPlayer)
+ elseif action == 'license' then
+ ShowPlayerLicense(closestPlayer)
+ elseif action == 'unpaid_bills' then
+ OpenUnpaidBillsMenu(closestPlayer)
+ end
+ else
+ ESX.ShowNotification(_U('job_police:no_players_nearby'))
+ end
+ end, function(data2, menu2)
+ menu2.close()
+ end)
+ elseif data.current.value == 'vehicle_interaction' then
+ local elements = {}
+ local playerPed = PlayerPedId()
+ local vehicle = ESX.Game.GetVehicleInDirection()
+
+ if DoesEntityExist(vehicle) then
+ table.insert(elements, {label = _U('job_police:vehicle_info'), value = 'vehicle_infos'})
+ table.insert(elements, {label = _U('job_police:pick_lock'), value = 'hijack_vehicle'})
+ table.insert(elements, {label = _U('job_police:impound'), value = 'impound'})
+ end
+
+ table.insert(elements, {label = _U('job_police:search_database'), value = 'search_database'})
+
+ ESX.UI.Menu.Open('default', GetCurrentResourceName(), 'vehicle_interaction', {
+ title = _U('job_police:vehicle_interaction'),
+ align = 'top-left',
+ elements = elements
+ }, function(data2, menu2)
+ local coords = GetEntityCoords(playerPed)
+ vehicle = ESX.Game.GetVehicleInDirection()
+ action = data2.current.value
+
+ if action == 'search_database' then
+ LookupVehicle()
+ elseif DoesEntityExist(vehicle) then
+ if action == 'vehicle_infos' then
+ local vehicleData = ESX.Game.GetVehicleProperties(vehicle)
+ OpenVehicleInfosMenu(vehicleData)
+ elseif action == 'hijack_vehicle' then
+ if IsAnyVehicleNearPoint(coords.x, coords.y, coords.z, 3.0) then
+ TaskStartScenarioInPlace(playerPed, 'WORLD_HUMAN_WELDING', 0, true)
+ Citizen.Wait(20000)
+ ClearPedTasksImmediately(playerPed)
+
+ SetVehicleDoorsLocked(vehicle, 1)
+ SetVehicleDoorsLockedForAllPlayers(vehicle, false)
+ ESX.ShowNotification(_U('job_police:vehicle_unlocked'))
+ end
+ elseif action == 'impound' then
+ -- is the script busy?
+ if CurrentTask.busy then
+ return
+ end
+
+ ESX.ShowHelpNotification(_U('job_police:impound_prompt'))
+ TaskStartScenarioInPlace(playerPed, 'CODE_HUMAN_MEDIC_TEND_TO_DEAD', 0, true)
+
+ CurrentTask.busy = true
+ CurrentTask.task = ESX.SetTimeout(10000, function()
+ ClearPedTasks(playerPed)
+ ImpoundVehicle(vehicle)
+ Citizen.Wait(100) -- sleep the entire script to let stuff sink back to reality
+ end)
+
+ -- keep track of that vehicle!
+ Citizen.CreateThread(function()
+ while CurrentTask.busy do
+ Citizen.Wait(1000)
+
+ vehicle = GetClosestVehicle(coords.x, coords.y, coords.z, 3.0, 0, 71)
+ if not DoesEntityExist(vehicle) and CurrentTask.busy then
+ ESX.ShowNotification(_U('job_police:impound_canceled_moved'))
+ ESX.ClearTimeout(CurrentTask.task)
+ ClearPedTasks(playerPed)
+ CurrentTask.busy = false
+ break
+ end
+ end
+ end)
+ end
+ else
+ ESX.ShowNotification(_U('job_police:no_vehicles_nearby'))
+ end
+
+ end)
+
+ elseif data.current.value == 'object_spawner' then
+ ESX.UI.Menu.Open('default', GetCurrentResourceName(), 'citizen_interaction', {
+ title = _U('job_police:traffic_interaction'),
+ align = 'top-left',
+ elements = {
+ {label = _U('job_police:cone'), model = 'prop_roadcone02a'},
+ {label = _U('job_police:barrier'), model = 'prop_barrier_work05'},
+ {label = _U('job_police:spikestrips'), model = 'p_ld_stinger_s'},
+ {label = _U('job_police:box'), model = 'prop_boxpile_07d'},
+ {label = _U('job_police:cash'), model = 'hei_prop_cash_crate_half_full'}
+ }}, function(data2, menu2)
+ local playerPed = PlayerPedId()
+ local coords, forward = GetEntityCoords(playerPed), GetEntityForwardVector(playerPed)
+ local objectCoords = (coords + forward * 1.0)
+
+ ESX.Game.SpawnObject(data2.current.model, objectCoords, function(obj)
+ SetEntityHeading(obj, GetEntityHeading(playerPed))
+ PlaceObjectOnGroundProperly(obj)
+ end)
+ end, function(data2, menu2)
+ menu2.close()
+ end)
+ end
+ end)
+
+end
+
+self.OpenIdentityCardMenu = function(player)
+ ESX.TriggerServerCallback('esx_policejob:getOtherPlayerData', function(data)
+ local elements = {
+ {label = _U('job_police:name', data.name)},
+ {label = _U('job_police:job', ('%s - %s'):format(data.job, data.grade))}
+ }
+
+ if self.Config.EnableESXIdentity then
+ table.insert(elements, {label = _U('job_police:sex', _U(data.sex))})
+ table.insert(elements, {label = _U('job_police:dob', data.dob)})
+ table.insert(elements, {label = _U('job_police:height', data.height)})
+ end
+
+ if data.drunk then
+ table.insert(elements, {label = _U('job_police:bac', data.drunk)})
+ end
+
+ if data.licenses then
+ table.insert(elements, {label = _U('job_police:license_label')})
+
+ for i=1, #data.licenses, 1 do
+ table.insert(elements, {label = data.licenses[i].label})
+ end
+ end
+
+ ESX.UI.Menu.Open('default', GetCurrentResourceName(), 'citizen_interaction', {
+ title = _U('job_police:citizen_interaction'),
+ align = 'top-left',
+ elements = elements
+ }, nil, function(data, menu)
+ menu.close()
+ end)
+ end, GetPlayerServerId(player))
+end
+
+self.OpenBodySearchMenu = function(player)
+ ESX.TriggerServerCallback('esx_policejob:getOtherPlayerData', function(data)
+ local elements = {}
+
+ for i=1, #data.accounts, 1 do
+ if data.accounts[i].name == 'black_money' and data.accounts[i].money > 0 then
+ table.insert(elements, {
+ label = _U('job_police:confiscate_dirty', ESX.Math.Round(data.accounts[i].money)),
+ value = 'black_money',
+ itemType = 'item_account',
+ amount = data.accounts[i].money
+ })
+
+ break
+ end
+ end
+
+ table.insert(elements, {label = _U('job_police:guns_label')})
+
+ for i=1, #data.weapons, 1 do
+ table.insert(elements, {
+ label = _U('job_police:confiscate_weapon', ESX.GetWeaponLabel(data.weapons[i].name), data.weapons[i].ammo),
+ value = data.weapons[i].name,
+ itemType = 'item_weapon',
+ amount = data.weapons[i].ammo
+ })
+ end
+
+ table.insert(elements, {label = _U('job_police:inventory_label')})
+
+ for i=1, #data.inventory, 1 do
+ if data.inventory[i].count > 0 then
+ table.insert(elements, {
+ label = _U('job_police:confiscate_inv', data.inventory[i].count, data.inventory[i].label),
+ value = data.inventory[i].name,
+ itemType = 'item_standard',
+ amount = data.inventory[i].count
+ })
+ end
+ end
+
+ ESX.UI.Menu.Open('default', GetCurrentResourceName(), 'body_search', {
+ title = _U('job_police:search'),
+ align = 'top-left',
+ elements = elements
+ }, function(data, menu)
+ if data.current.value then
+ TriggerServerEvent('esx_policejob:confiscatePlayerItem', GetPlayerServerId(player), data.current.itemType, data.current.value, data.current.amount)
+ OpenBodySearchMenu(player)
+ end
+ end, function(data, menu)
+ menu.close()
+ end)
+ end, GetPlayerServerId(player))
+end
+
+self.OpenFineMenu = function(player)
+ ESX.UI.Menu.Open('default', GetCurrentResourceName(), 'fine', {
+ title = _U('job_police:fine'),
+ align = 'top-left',
+ elements = {
+ {label = _U('job_police:traffic_offense'), value = 0},
+ {label = _U('job_police:minor_offense'), value = 1},
+ {label = _U('job_police:average_offense'), value = 2},
+ {label = _U('job_police:major_offense'), value = 3}
+ }}, function(data, menu)
+ OpenFineCategoryMenu(player, data.current.value)
+ end, function(data, menu)
+ menu.close()
+ end)
+end
+
+self.OpenFineCategoryMenu = function(player, category)
+ ESX.TriggerServerCallback('esx_policejob:getFineList', function(fines)
+ local elements = {}
+
+ for k,fine in ipairs(fines) do
+ table.insert(elements, {
+ label = ('%s %s'):format(fine.label, _U('job_police:armory_item', ESX.Math.GroupDigits(fine.amount))),
+ value = fine.id,
+ amount = fine.amount,
+ fineLabel = fine.label
+ })
+ end
+
+ ESX.UI.Menu.Open('default', GetCurrentResourceName(), 'fine_category', {
+ title = _U('job_police:fine'),
+ align = 'top-left',
+ elements = elements
+ }, function(data, menu)
+ menu.close()
+
+ if self.Config.EnablePlayerManagement then
+ TriggerServerEvent('esx_billing:sendBill', GetPlayerServerId(player), 'society_police', _U('job_police:fine_total', data.current.fineLabel), data.current.amount)
+ else
+ TriggerServerEvent('esx_billing:sendBill', GetPlayerServerId(player), '', _U('job_police:fine_total', data.current.fineLabel), data.current.amount)
+ end
+
+ ESX.SetTimeout(300, function()
+ OpenFineCategoryMenu(player, category)
+ end)
+ end, function(data, menu)
+ menu.close()
+ end)
+ end, category)
+end
+
+self.LookupVehicle = function()
+ ESX.UI.Menu.Open('dialog', GetCurrentResourceName(), 'lookup_vehicle', {
+ title = _U('job_police:search_database_title'),
+ }, function(data, menu)
+ local length = string.len(data.value)
+ if not data.value or length < 2 or length > 8 then
+ ESX.ShowNotification(_U('job_police:search_database_error_invalid'))
+ else
+ ESX.TriggerServerCallback('esx_policejob:getVehicleInfos', function(retrivedInfo)
+ local elements = {{label = _U('job_police:plate', retrivedInfo.plate)}}
+ menu.close()
+
+ if not retrivedInfo.owner then
+ table.insert(elements, {label = _U('job_police:owner_unknown')})
+ else
+ table.insert(elements, {label = _U('job_police:owner', retrivedInfo.owner)})
+ end
+
+ ESX.UI.Menu.Open('default', GetCurrentResourceName(), 'vehicle_infos', {
+ title = _U('job_police:vehicle_info'),
+ align = 'top-left',
+ elements = elements
+ }, nil, function(data2, menu2)
+ menu2.close()
+ end)
+ end, data.value)
+
+ end
+ end, function(data, menu)
+ menu.close()
+ end)
+end
+
+self.ShowPlayerLicense = function(player)
+ local elements = {}
+
+ ESX.TriggerServerCallback('esx_policejob:getOtherPlayerData', function(playerData)
+ if playerData.licenses then
+ for i=1, #playerData.licenses, 1 do
+ if playerData.licenses[i].label and playerData.licenses[i].type then
+ table.insert(elements, {
+ label = playerData.licenses[i].label,
+ type = playerData.licenses[i].type
+ })
+ end
+ end
+ end
+
+ ESX.UI.Menu.Open('default', GetCurrentResourceName(), 'manage_license', {
+ title = _U('job_police:license_revoke'),
+ align = 'top-left',
+ elements = elements,
+ }, function(data, menu)
+ ESX.ShowNotification(_U('job_police:licence_you_revoked', data.current.label, playerData.name))
+ TriggerServerEvent('esx_policejob:message', GetPlayerServerId(player), _U('job_police:license_revoked', data.current.label))
+
+ TriggerServerEvent('esx_license:removeLicense', GetPlayerServerId(player), data.current.type)
+
+ ESX.SetTimeout(300, function()
+ ShowPlayerLicense(player)
+ end)
+ end, function(data, menu)
+ menu.close()
+ end)
+
+ end, GetPlayerServerId(player))
+end
+
+self.OpenUnpaidBillsMenu = function(player)
+ local elements = {}
+
+ ESX.TriggerServerCallback('esx_billing:getTargetBills', function(bills)
+ for k,bill in ipairs(bills) do
+ table.insert(elements, {
+ label = ('%s - %s'):format(bill.label, _U('job_police:armory_item', ESX.Math.GroupDigits(bill.amount))),
+ billId = bill.id
+ })
+ end
+
+ ESX.UI.Menu.Open('default', GetCurrentResourceName(), 'billing', {
+ title = _U('job_police:unpaid_bills'),
+ align = 'top-left',
+ elements = elements
+ }, nil, function(data, menu)
+ menu.close()
+ end)
+ end, GetPlayerServerId(player))
+end
+
+self.OpenVehicleInfosMenu = function(vehicleData)
+ ESX.TriggerServerCallback('esx_policejob:getVehicleInfos', function(retrivedInfo)
+ local elements = {{label = _U('job_police:plate', retrivedInfo.plate)}}
+
+ if not retrivedInfo.owner then
+ table.insert(elements, {label = _U('job_police:owner_unknown')})
+ else
+ table.insert(elements, {label = _U('job_police:owner', retrivedInfo.owner)})
+ end
+
+ ESX.UI.Menu.Open('default', GetCurrentResourceName(), 'vehicle_infos', {
+ title = _U('job_police:vehicle_info'),
+ align = 'top-left',
+ elements = elements
+ }, nil, function(data, menu)
+ menu.close()
+ end)
+ end, vehicleData.plate)
+end
+
+self.OpenGetWeaponMenu = function()
+ ESX.TriggerServerCallback('esx_policejob:getArmoryWeapons', function(weapons)
+ local elements = {}
+
+ for i=1, #weapons, 1 do
+ if weapons[i].count > 0 then
+ table.insert(elements, {
+ label = 'x' .. weapons[i].count .. ' ' .. ESX.GetWeaponLabel(weapons[i].name),
+ value = weapons[i].name
+ })
+ end
+ end
+
+ ESX.UI.Menu.Open('default', GetCurrentResourceName(), 'armory_get_weapon', {
+ title = _U('job_police:get_weapon_menu'),
+ align = 'top-left',
+ elements = elements
+ }, function(data, menu)
+ menu.close()
+
+ ESX.TriggerServerCallback('esx_policejob:removeArmoryWeapon', function()
+ OpenGetWeaponMenu()
+ end, data.current.value)
+ end, function(data, menu)
+ menu.close()
+ end)
+ end)
+end
+
+self.OpenPutWeaponMenu = function()
+ local elements = {}
+ local playerPed = PlayerPedId()
+ local weaponList = ESX.GetWeaponList()
+
+ for i=1, #weaponList, 1 do
+ local weaponHash = GetHashKey(weaponList[i].name)
+
+ if HasPedGotWeapon(playerPed, weaponHash, false) and weaponList[i].name ~= 'WEAPON_UNARMED' then
+ table.insert(elements, {
+ label = weaponList[i].label,
+ value = weaponList[i].name
+ })
+ end
+ end
+
+ ESX.UI.Menu.Open('default', GetCurrentResourceName(), 'armory_put_weapon', {
+ title = _U('job_police:put_weapon_menu'),
+ align = 'top-left',
+ elements = elements
+ }, function(data, menu)
+ menu.close()
+
+ ESX.TriggerServerCallback('esx_policejob:addArmoryWeapon', function()
+ OpenPutWeaponMenu()
+ end, data.current.value, true)
+ end, function(data, menu)
+ menu.close()
+ end)
+end
+
+self.OpenBuyWeaponsMenu = function()
+ local elements = {}
+ local playerPed = PlayerPedId()
+
+ local authorizedWeapons = {}
+
+ for i=1, ESX.PlayerData.job.grade, 1 do
+
+ local weapons = self.Config.AuthorizedWeapons[i]
+
+ for j=1, #weapons, 1 do
+ authorizedWeapons[#authorizedWeapons + 1] = weapons[j]
+ end
+
+ end
+
+ for k,v in ipairs(authorizedWeapons) do
+ local weaponNum, weapon = ESX.GetWeapon(v.weapon)
+ local components, label = {}
+ local hasWeapon = HasPedGotWeapon(playerPed, GetHashKey(v.weapon), false)
+
+ if v.components then
+ for i=1, #v.components do
+ if v.components[i] then
+ local component = weapon.components[i]
+ local hasComponent = HasPedGotWeaponComponent(playerPed, GetHashKey(v.weapon), component.hash)
+
+ if hasComponent then
+ label = ('%s: %s'):format(component.label, _U('job_police:armory_owned'))
+ else
+ if v.components[i] > 0 then
+ label = ('%s: %s'):format(component.label, _U('job_police:armory_item', ESX.Math.GroupDigits(v.components[i])))
+ else
+ label = ('%s: %s'):format(component.label, _U('job_police:armory_free'))
+ end
+ end
+
+ table.insert(components, {
+ label = label,
+ componentLabel = component.label,
+ hash = component.hash,
+ name = component.name,
+ price = v.components[i],
+ hasComponent = hasComponent,
+ componentNum = i
+ })
+ end
+ end
+ end
+
+ if hasWeapon and v.components then
+ label = ('%s: >'):format(weapon.label)
+ elseif hasWeapon and not v.components then
+ label = ('%s: %s'):format(weapon.label, _U('job_police:armory_owned'))
+ else
+ if v.price > 0 then
+ label = ('%s: %s'):format(weapon.label, _U('job_police:armory_item', ESX.Math.GroupDigits(v.price)))
+ else
+ label = ('%s: %s'):format(weapon.label, _U('job_police:armory_free'))
+ end
+ end
+
+ table.insert(elements, {
+ label = label,
+ weaponLabel = weapon.label,
+ name = weapon.name,
+ components = components,
+ price = v.price,
+ hasWeapon = hasWeapon
+ })
+ end
+
+ ESX.UI.Menu.Open('default', GetCurrentResourceName(), 'armory_buy_weapons', {
+ title = _U('job_police:armory_weapontitle'),
+ align = 'top-left',
+ elements = elements
+ }, function(data, menu)
+ if data.current.hasWeapon then
+ if #data.current.components > 0 then
+ OpenWeaponComponentShop(data.current.components, data.current.name, menu)
+ end
+ else
+ ESX.TriggerServerCallback('esx_policejob:buyWeapon', function(bought)
+ if bought then
+ if data.current.price > 0 then
+ ESX.ShowNotification(_U('job_police:armory_bought', data.current.weaponLabel, ESX.Math.GroupDigits(data.current.price)))
+ end
+
+ menu.close()
+ OpenBuyWeaponsMenu()
+ else
+ ESX.ShowNotification(_U('job_police:armory_money'))
+ end
+ end, data.current.name, 1)
+ end
+ end, function(data, menu)
+ menu.close()
+ end)
+end
+
+self.OpenWeaponComponentShop = function(components, weaponName, parentShop)
+ ESX.UI.Menu.Open('default', GetCurrentResourceName(), 'armory_buy_weapons_components', {
+ title = _U('job_police:armory_componenttitle'),
+ align = 'top-left',
+ elements = components
+ }, function(data, menu)
+ if data.current.hasComponent then
+ ESX.ShowNotification(_U('job_police:armory_hascomponent'))
+ else
+ ESX.TriggerServerCallback('esx_policejob:buyWeapon', function(bought)
+ if bought then
+ if data.current.price > 0 then
+ ESX.ShowNotification(_U('job_police:armory_bought', data.current.componentLabel, ESX.Math.GroupDigits(data.current.price)))
+ end
+
+ menu.close()
+ parentShop.close()
+ OpenBuyWeaponsMenu()
+ else
+ ESX.ShowNotification(_U('job_police:armory_money'))
+ end
+ end, weaponName, 2, data.current.componentNum)
+ end
+ end, function(data, menu)
+ menu.close()
+ end)
+end
+
+self.OpenGetStocksMenu = function()
+ ESX.TriggerServerCallback('esx_policejob:getStockItems', function(items)
+ local elements = {}
+
+ for i=1, #items, 1 do
+ table.insert(elements, {
+ label = 'x' .. items[i].count .. ' ' .. items[i].label,
+ value = items[i].name
+ })
+ end
+
+ ESX.UI.Menu.Open('default', GetCurrentResourceName(), 'stocks_menu', {
+ title = _U('job_police:police_stock'),
+ align = 'top-left',
+ elements = elements
+ }, function(data, menu)
+ local itemName = data.current.value
+
+ ESX.UI.Menu.Open('dialog', GetCurrentResourceName(), 'stocks_menu_get_item_count', {
+ title = _U('job_police:quantity')
+ }, function(data2, menu2)
+ local count = tonumber(data2.value)
+
+ if not count then
+ ESX.ShowNotification(_U('job_police:quantity_invalid'))
+ else
+ menu2.close()
+ menu.close()
+ TriggerServerEvent('esx_policejob:getStockItem', itemName, count)
+
+ Citizen.Wait(300)
+ OpenGetStocksMenu()
+ end
+ end, function(data2, menu2)
+ menu2.close()
+ end)
+ end, function(data, menu)
+ menu.close()
+ end)
+ end)
+end
+
+self.OpenPutStocksMenu = function()
+ ESX.TriggerServerCallback('esx_policejob:getPlayerInventory', function(inventory)
+ local elements = {}
+
+ for i=1, #inventory.items, 1 do
+ local item = inventory.items[i]
+
+ if item.count > 0 then
+ table.insert(elements, {
+ label = item.label .. ' x' .. item.count,
+ type = 'item_standard',
+ value = item.name
+ })
+ end
+ end
+
+ ESX.UI.Menu.Open('default', GetCurrentResourceName(), 'stocks_menu', {
+ title = _U('job_police:inventory'),
+ align = 'top-left',
+ elements = elements
+ }, function(data, menu)
+ local itemName = data.current.value
+
+ ESX.UI.Menu.Open('dialog', GetCurrentResourceName(), 'stocks_menu_put_item_count', {
+ title = _U('job_police:quantity')
+ }, function(data2, menu2)
+ local count = tonumber(data2.value)
+
+ if not count then
+ ESX.ShowNotification(_U('job_police:quantity_invalid'))
+ else
+ menu2.close()
+ menu.close()
+ TriggerServerEvent('esx_policejob:putStockItems', itemName, count)
+
+ Citizen.Wait(300)
+ OpenPutStocksMenu()
+ end
+ end, function(data2, menu2)
+ menu2.close()
+ end)
+ end, function(data, menu)
+ menu.close()
+ end)
+ end)
+end
+
+-- Create blip for colleagues
+self.CreateBlip = function(id)
+ local ped = GetPlayerPed(id)
+ local blip = GetBlipFromEntity(ped)
+
+ if not DoesBlipExist(blip) then -- Add blip and create head display on player
+ blip = AddBlipForEntity(ped)
+ SetBlipSprite(blip, 1)
+ ShowHeadingIndicatorOnBlip(blip, true) -- Player Blip indicator
+ SetBlipRotation(blip, math.ceil(GetEntityHeading(ped))) -- update rotation
+ SetBlipNameToPlayerName(blip, id) -- update blip name
+ SetBlipScale(blip, 0.85) -- set scale
+ SetBlipAsShortRange(blip, true)
+
+ table.insert(blipsCops, blip) -- add blip to array so we can remove it later
+ end
+end
+
+-- handcuff timer, unrestrain the player after an certain amount of time
+self.StartHandcuffTimer = function()
+ if self.Config.EnableHandcuffTimer and handcuffTimer.active then
+ ESX.ClearTimeout(handcuffTimer.task)
+ end
+
+ handcuffTimer.active = true
+
+ handcuffTimer.task = ESX.SetTimeout(Config.HandcuffTimer, function()
+ ESX.ShowNotification(_U('job_police:unrestrained_timer'))
+ TriggerEvent('esx_policejob:unrestrain')
+ handcuffTimer.active = false
+ end)
+
+end
+
+-- TODO
+-- - return to garage if owned
+-- - message owner that his vehicle has been impounded
+self.ImpoundVehicle = function(vehicle)
+ --local vehicleName = GetLabelText(GetDisplayNameFromVehicleModel(GetEntityModel(vehicle)))
+ ESX.Game.DeleteVehicle(vehicle)
+ ESX.ShowNotification(_U('job_police:impound_successful'))
+ currentTask.busy = false
+end
+
+self.OpenVehicleSpawnerMenu = function(type, config)
+
+ local playerCoords = GetEntityCoords(PlayerPedId())
+
+ ESX.UI.Menu.Open('default', GetCurrentResourceName(), 'vehicle', {
+ title = _U('job_police:garage_title'),
+ align = 'top-left',
+ elements = {
+ {label = _U('job_police:garage_storeditem'), action = 'garage'},
+ {label = _U('job_police:garage_storeitem'), action = 'store_garage'},
+ {label = _U('job_police:garage_buyitem'), action = 'buy_vehicle'}
+ }}, function(data, menu)
+
+ if data.current.action == 'buy_vehicle' then
+
+ local shopElements = {}
+ local authorizedVehicles = {}
+
+ for i=1, ESX.PlayerData.job.grade, 1 do
+
+ local vehicles = self.Config.AuthorizedVehicles[type][i]
+
+ for j=1, #vehicles, 1 do
+ authorizedVehicles[#authorizedVehicles + 1] = vehicles[j]
+ end
+
+ end
+
+ if authorizedVehicles then
+ if #authorizedVehicles > 0 then
+ for k,vehicle in ipairs(authorizedVehicles) do
+ if IsModelInCdimage(vehicle.model) then
+ local vehicleLabel = GetLabelText(GetDisplayNameFromVehicleModel(vehicle.model))
+
+ table.insert(shopElements, {
+ label = ('%s - %s'):format(vehicleLabel, _U('job_police:shop_item', ESX.Math.GroupDigits(vehicle.price))),
+ name = vehicleLabel,
+ model = vehicle.model,
+ price = vehicle.price,
+ props = vehicle.props,
+ type = type
+ })
+ end
+ end
+
+ if #shopElements > 0 then
+ self.OpenShopMenu(shopElements, config)
+ else
+ ESX.ShowNotification(_U('job_police:garage_notauthorized'))
+ end
+ else
+ ESX.ShowNotification(_U('job_police:garage_notauthorized'))
+ end
+ else
+ ESX.ShowNotification(_U('job_police:garage_notauthorized'))
+ end
+ elseif data.current.action == 'garage' then
+ local garage = {}
+
+ ESX.TriggerServerCallback('esx_vehicleshop:retrieveJobVehicles', function(jobVehicles)
+ if #jobVehicles > 0 then
+ local allVehicleProps = {}
+
+ for k,v in ipairs(jobVehicles) do
+ local props = json.decode(v.vehicle)
+
+ if IsModelInCdimage(props.model) then
+ local vehicleName = GetLabelText(GetDisplayNameFromVehicleModel(props.model))
+ local label = ('%s - %s: '):format(vehicleName, props.plate)
+
+ if v.stored then
+ label = label .. ('%s'):format(_U('job_police:garage_stored'))
+ else
+ label = label .. ('%s'):format(_U('job_police:garage_notstored'))
+ end
+
+ table.insert(garage, {
+ label = label,
+ stored = v.stored,
+ model = props.model,
+ plate = props.plate
+ })
+
+ allVehicleProps[props.plate] = props
+ end
+ end
+
+ if #garage > 0 then
+ ESX.UI.Menu.Open('default', GetCurrentResourceName(), 'vehicle_garage', {
+ title = _U('job_police:garage_title'),
+ align = 'top-left',
+ elements = garage
+ }, function(data2, menu2)
+ if data2.current.stored then
+
+
+ local foundSpawn, spawnPoint = self.GetAvailableVehicleSpawnPoint(config.SpawnPoints)
+
+ if foundSpawn then
+ menu2.close()
+
+ ESX.Game.SpawnVehicle(data2.current.model, spawnPoint.coords, spawnPoint.heading, function(vehicle)
+ local vehicleProps = allVehicleProps[data2.current.plate]
+ ESX.Game.SetVehicleProperties(vehicle, vehicleProps)
+
+ TriggerServerEvent('esx_vehicleshop:setJobVehicleState', data2.current.plate, false)
+ ESX.ShowNotification(_U('job_police:garage_released'))
+ end)
+ end
+
+ else
+ ESX.ShowNotification(_U('job_police:garage_notavailable'))
+ end
+ end, function(data2, menu2)
+ menu2.close()
+ end)
+ else
+ ESX.ShowNotification(_U('job_police:garage_empty'))
+ end
+ else
+ ESX.ShowNotification(_U('job_police:garage_empty'))
+ end
+ end, type)
+
+ elseif data.current.action == 'store_garage' then
+ StoreNearbyVehicle(playerCoords)
+ end
+ end, function(data, menu)
+ menu.close()
+ end, nil, function()
+
+ if type == 'car' then
+
+ ESX.ShowHelpNotification(_U('job_police:garage_prompt'))
+
+ self.CurrentAction = function()
+ self.OpenVehicleSpawnerMenu('car', data.config)
+ end
+
+ elseif type == 'helicopter' then
+
+ ESX.ShowHelpNotification(_U('job_police:helicopter_prompt'))
+
+ self.CurrentAction = function()
+ self.OpenVehicleSpawnerMenu('helicopter', data.config)
+ end
+
+ end
+
+ end)
+
+end
+
+self.StoreNearbyVehicle = function(playerCoords)
+ local vehicles, vehiclePlates = ESX.Game.GetVehiclesInArea(playerCoords, 30.0), {}
+
+ if #vehicles > 0 then
+ for k,v in ipairs(vehicles) do
+
+ -- Make sure the vehicle we're saving is empty, or else it wont be deleted
+ if GetVehicleNumberOfPassengers(v) == 0 and IsVehicleSeatFree(v, -1) then
+ table.insert(vehiclePlates, {
+ vehicle = v,
+ plate = ESX.Math.Trim(GetVehicleNumberPlateText(v))
+ })
+ end
+ end
+ else
+ ESX.ShowNotification(_U('job_police:garage_store_nearby'))
+ return
+ end
+
+ ESX.TriggerServerCallback('esx_policejob:storeNearbyVehicle', function(storeSuccess, foundNum)
+ if storeSuccess then
+ local vehicleId = vehiclePlates[foundNum]
+ local attempts = 0
+ ESX.Game.DeleteVehicle(vehicleId.vehicle)
+ IsBusy = true
+
+ Citizen.CreateThread(function()
+ BeginTextCommandBusyspinnerOn('STRING')
+ AddTextComponentSubstringPlayerName(_U('job_police:garage_storing'))
+ EndTextCommandBusyspinnerOn(4)
+
+ while IsBusy do
+ Citizen.Wait(100)
+ end
+
+ BusyspinnerOff()
+ end)
+
+ -- Workaround for vehicle not deleting when other players are near it.
+ while DoesEntityExist(vehicleId.vehicle) do
+ Citizen.Wait(500)
+ attempts = attempts + 1
+
+ -- Give up
+ if attempts > 30 then
+ break
+ end
+
+ vehicles = ESX.Game.GetVehiclesInArea(playerCoords, 30.0)
+ if #vehicles > 0 then
+ for k,v in ipairs(vehicles) do
+ if ESX.Math.Trim(GetVehicleNumberPlateText(v)) == vehicleId.plate then
+ ESX.Game.DeleteVehicle(v)
+ break
+ end
+ end
+ end
+ end
+
+ IsBusy = false
+ ESX.ShowNotification(_U('job_police:garage_has_stored'))
+ else
+ ESX.ShowNotification(_U('job_police:garage_has_notstored'))
+ end
+ end, vehiclePlates)
+end
+
+self.GetAvailableVehicleSpawnPoint = function(spawnPoints)
+
+ local found, foundSpawnPoint = false, nil
+
+ for i=1, #spawnPoints, 1 do
+ if ESX.Game.IsSpawnPointClear(spawnPoints[i].coords, spawnPoints[i].radius) then
+ found, foundSpawnPoint = true, spawnPoints[i]
+ break
+ end
+ end
+
+ if found then
+ return true, foundSpawnPoint
+ else
+ ESX.ShowNotification(_U('job_police:vehicle_blocked'))
+ return false
+ end
+end
+
+self.OpenShopMenu = function(elements, config)
+
+ local playerPed = PlayerPedId()
+ isInShopMenu = true
+
+ ESX.UI.Menu.Open('default', GetCurrentResourceName(), 'vehicle_shop', {
+ title = _U('job_police:vehicleshop_title'),
+ align = 'top-left',
+ elements = elements
+ }, function(data, menu)
+
+ menu.close()
+
+ ESX.UI.Menu.Open('default', GetCurrentResourceName(), 'vehicle_shop_confirm', {
+ title = _U('job_police:vehicleshop_confirm', data.current.name, data.current.price),
+ align = 'top-left',
+ elements = {
+ {label = _U('job_police:confirm_no'), value = 'no'},
+ {label = _U('job_police:confirm_yes'), value = 'yes'}
+ }}, function(data2, menu2)
+
+ menu2.close()
+
+ if data2.current.value == 'yes' then
+
+ ESX.TriggerServerCallback('esx_policejob:canBuyVehicle', function (success)
+
+ if success then
+
+ local foundSpawn, spawnPoint = self.GetAvailableVehicleSpawnPoint(config.SpawnPoints)
+
+ if foundSpawn then
+
+ ESX.Game.SpawnVehicle(data.current.model, spawnPoint.coords, spawnPoint.heading, function(vehicle)
+
+ local props = ESX.Game.GetVehicleProperties(vehicle)
+ props.plate = exports['esx_vehicleshop']:GeneratePlate() -- TODO Should be in core
+
+ SetVehicleNumberPlateText(vehicle, props.plate)
+ TaskWarpPedIntoVehicle(playerPed, vehicle, -1)
+
+ TriggerServerEvent('esx_policejob:buyJobVehicle', props, data.current.type)
+
+ ESX.ShowNotification(_U('job_police:vehicleshop_bought', data.current.name, ESX.Math.GroupDigits(data.current.price)))
+
+ end)
+
+ end
+
+ else
+ ESX.ShowNotification(_U('job_police:vehicleshop_money'))
+ menu2.close()
+ end
+
+ end, data.current.model, data.current.type)
+
+ end
+
+ end, function(data2, menu2)
+ menu2.close()
+ end)
+
+ end)
+
+end
+
+self.DeleteSpawnedVehicles = function()
+
+ for i=1, #self.SpawnedVehicles, 1 do
+ ESX.Game.DeleteVehicle(self.SpawnedVehicles[i])
+ end
+
+ self.SpawnedVehicles = {}
+
+end
+
+self.WaitForVehicleToLoad = function(modelHash)
+
+ modelHash = (type(modelHash) == 'number' and modelHash or GetHashKey(modelHash))
+
+ if not HasModelLoaded(modelHash) then
+ RequestModel(modelHash)
+
+ BeginTextCommandBusyspinnerOn('STRING')
+ AddTextComponentSubstringPlayerName(_U('vehicleshop_awaiting_model'))
+ EndTextCommandBusyspinnerOn(4)
+
+ while not HasModelLoaded(modelHash) do
+ Citizen.Wait(0)
+ DisableAllControlActions(0)
+ end
+
+ BusyspinnerOff()
+ end
+end
diff --git a/modules/job_police/data/config.lua b/modules/job_police/data/config.lua
new file mode 100644
index 00000000..b039d9b1
--- /dev/null
+++ b/modules/job_police/data/config.lua
@@ -0,0 +1,319 @@
+Config = {}
+
+Config.DrawDistance = 100.0
+Config.MarkerType = 1
+Config.MarkerSize = {x = 1.5, y = 1.5, z = 0.5}
+Config.MarkerColor = {r = 50, g = 50, b = 204, a = 100}
+
+Config.EnablePlayerManagement = false
+Config.EnableArmoryManagement = false
+Config.EnableESXIdentity = false -- enable if you're using esx_identity
+Config.EnableLicenses = false -- enable if you're using esx_license
+
+Config.EnableHandcuffTimer = true -- enable handcuff timer? will unrestrain player after the time ends
+Config.HandcuffTimer = 10 * 60000 -- 10 mins
+
+Config.EnableJobBlip = false -- enable blips for cops on duty, requires esx_society
+Config.EnableCustomPeds = false -- enable custom peds in cloak room? See Config.CustomPeds below to customize peds
+
+Config.EnableESXService = false -- enable esx service?
+Config.MaxInService = 5
+
+Config.Locale = 'fr'
+
+Config.PoliceStations = {
+
+ LSPD = {
+
+ Blip = {
+ Coords = vector3(425.1, -979.5, 30.7),
+ Sprite = 60,
+ Display = 4,
+ Scale = 1.2,
+ Colour = 29
+ },
+
+ Cloakrooms = {
+ vector3(452.6, -992.8, 30.6)
+ },
+
+ Armories = {
+ vector3(451.7, -980.1, 30.6)
+ },
+
+ Vehicles = {
+ {
+ Spawner = vector3(454.6, -1017.4, 28.4),
+ InsideShop = vector3(228.5, -993.5, -99.5),
+ SpawnPoints = {
+ {coords = vector3(438.4, -1018.3, 27.7), heading = 90.0, radius = 6.0},
+ {coords = vector3(441.0, -1024.2, 28.3), heading = 90.0, radius = 6.0},
+ {coords = vector3(453.5, -1022.2, 28.0), heading = 90.0, radius = 6.0},
+ {coords = vector3(450.9, -1016.5, 28.1), heading = 90.0, radius = 6.0}
+ }
+ },
+
+ {
+ Spawner = vector3(473.3, -1018.8, 28.0),
+ InsideShop = vector3(228.5, -993.5, -99.0),
+ SpawnPoints = {
+ {coords = vector3(475.9, -1021.6, 28.0), heading = 276.1, radius = 6.0},
+ {coords = vector3(484.1, -1023.1, 27.5), heading = 302.5, radius = 6.0}
+ }
+ }
+ },
+
+ Helicopters = {
+ {
+ Spawner = vector3(461.1, -981.5, 43.6),
+ InsideShop = vector3(477.0, -1106.4, 43.0),
+ SpawnPoints = {
+ {coords = vector3(449.5, -981.2, 43.6), heading = 92.6, radius = 10.0}
+ }
+ }
+ },
+
+ BossActions = {
+ vector3(448.4, -973.2, 30.6)
+ }
+
+ }
+
+}
+
+Config.AuthorizedWeapons = {
+ { -- recruit
+ {weapon = 'WEAPON_APPISTOL', components = {0, 0, 1000, 4000, nil}, price = 10000},
+ {weapon = 'WEAPON_NIGHTSTICK', price = 0},
+ {weapon = 'WEAPON_STUNGUN', price = 1500},
+ {weapon = 'WEAPON_FLASHLIGHT', price = 80}
+ },
+
+ { -- officer
+ {weapon = 'WEAPON_ADVANCEDRIFLE', components = {0, 6000, 1000, 4000, 8000, nil}, price = 50000},
+ },
+
+ { -- sergeant
+ {weapon = 'WEAPON_PUMPSHOTGUN', components = {2000, 6000, nil}, price = 70000},
+ },
+
+ { -- lieutenant
+ },
+
+ { -- boss
+ }
+}
+
+Config.AuthorizedVehicles = {
+ car = {
+ { -- recruit
+
+ },
+
+ { -- officer
+ {model = 'police3', price = 20000}
+ },
+
+ { -- sergeant
+ {model = 'policet', price = 18500},
+ {model = 'policeb', price = 30500}
+ },
+
+ { -- lieutenant
+ {model = 'riot', price = 70000},
+ {model = 'fbi2', price = 60000}
+ },
+
+ { -- boss
+
+ }
+ },
+
+ helicopter = {
+
+ { -- recruit
+
+ },
+
+ { -- officer
+
+ },
+
+ {-- sergeant
+
+ },
+
+ { -- lieutenant
+ {model = 'polmav', props = {modLivery = 0}, price = 200000}
+ },
+
+ { -- boss
+ {model = 'polmav', props = {modLivery = 0}, price = 100000}
+ }
+ }
+}
+
+Config.CustomPeds = {
+ shared = {
+ {label = 'Sheriff Ped', maleModel = 's_m_y_sheriff_01', femaleModel = 's_f_y_sheriff_01'},
+ {label = 'Police Ped', maleModel = 's_m_y_cop_01', femaleModel = 's_f_y_cop_01'}
+ },
+
+ recruit = {},
+
+ officer = {},
+
+ sergeant = {},
+
+ lieutenant = {},
+
+ boss = {
+ {label = 'SWAT Ped', maleModel = 's_m_y_swat_01', femaleModel = 's_m_y_swat_01'}
+ }
+}
+
+-- CHECK SKINCHANGER CLIENT MAIN.LUA for matching elements
+Config.Uniforms = {
+ recruit = {
+ male = {
+ tshirt_1 = 59, tshirt_2 = 1,
+ torso_1 = 55, torso_2 = 0,
+ decals_1 = 0, decals_2 = 0,
+ arms = 41,
+ pants_1 = 25, pants_2 = 0,
+ shoes_1 = 25, shoes_2 = 0,
+ helmet_1 = 46, helmet_2 = 0,
+ chain_1 = 0, chain_2 = 0,
+ ears_1 = 2, ears_2 = 0
+ },
+ female = {
+ tshirt_1 = 36, tshirt_2 = 1,
+ torso_1 = 48, torso_2 = 0,
+ decals_1 = 0, decals_2 = 0,
+ arms = 44,
+ pants_1 = 34, pants_2 = 0,
+ shoes_1 = 27, shoes_2 = 0,
+ helmet_1 = 45, helmet_2 = 0,
+ chain_1 = 0, chain_2 = 0,
+ ears_1 = 2, ears_2 = 0
+ }
+ },
+
+ officer = {
+ male = {
+ tshirt_1 = 58, tshirt_2 = 0,
+ torso_1 = 55, torso_2 = 0,
+ decals_1 = 0, decals_2 = 0,
+ arms = 41,
+ pants_1 = 25, pants_2 = 0,
+ shoes_1 = 25, shoes_2 = 0,
+ helmet_1 = -1, helmet_2 = 0,
+ chain_1 = 0, chain_2 = 0,
+ ears_1 = 2, ears_2 = 0
+ },
+ female = {
+ tshirt_1 = 35, tshirt_2 = 0,
+ torso_1 = 48, torso_2 = 0,
+ decals_1 = 0, decals_2 = 0,
+ arms = 44,
+ pants_1 = 34, pants_2 = 0,
+ shoes_1 = 27, shoes_2 = 0,
+ helmet_1 = -1, helmet_2 = 0,
+ chain_1 = 0, chain_2 = 0,
+ ears_1 = 2, ears_2 = 0
+ }
+ },
+
+ sergeant = {
+ male = {
+ tshirt_1 = 58, tshirt_2 = 0,
+ torso_1 = 55, torso_2 = 0,
+ decals_1 = 8, decals_2 = 1,
+ arms = 41,
+ pants_1 = 25, pants_2 = 0,
+ shoes_1 = 25, shoes_2 = 0,
+ helmet_1 = -1, helmet_2 = 0,
+ chain_1 = 0, chain_2 = 0,
+ ears_1 = 2, ears_2 = 0
+ },
+ female = {
+ tshirt_1 = 35, tshirt_2 = 0,
+ torso_1 = 48, torso_2 = 0,
+ decals_1 = 7, decals_2 = 1,
+ arms = 44,
+ pants_1 = 34, pants_2 = 0,
+ shoes_1 = 27, shoes_2 = 0,
+ helmet_1 = -1, helmet_2 = 0,
+ chain_1 = 0, chain_2 = 0,
+ ears_1 = 2, ears_2 = 0
+ }
+ },
+
+ lieutenant = {
+ male = {
+ tshirt_1 = 58, tshirt_2 = 0,
+ torso_1 = 55, torso_2 = 0,
+ decals_1 = 8, decals_2 = 2,
+ arms = 41,
+ pants_1 = 25, pants_2 = 0,
+ shoes_1 = 25, shoes_2 = 0,
+ helmet_1 = -1, helmet_2 = 0,
+ chain_1 = 0, chain_2 = 0,
+ ears_1 = 2, ears_2 = 0
+ },
+ female = {
+ tshirt_1 = 35, tshirt_2 = 0,
+ torso_1 = 48, torso_2 = 0,
+ decals_1 = 7, decals_2 = 2,
+ arms = 44,
+ pants_1 = 34, pants_2 = 0,
+ shoes_1 = 27, shoes_2 = 0,
+ helmet_1 = -1, helmet_2 = 0,
+ chain_1 = 0, chain_2 = 0,
+ ears_1 = 2, ears_2 = 0
+ }
+ },
+
+ boss = {
+ male = {
+ tshirt_1 = 58, tshirt_2 = 0,
+ torso_1 = 55, torso_2 = 0,
+ decals_1 = 8, decals_2 = 3,
+ arms = 41,
+ pants_1 = 25, pants_2 = 0,
+ shoes_1 = 25, shoes_2 = 0,
+ helmet_1 = -1, helmet_2 = 0,
+ chain_1 = 0, chain_2 = 0,
+ ears_1 = 2, ears_2 = 0
+ },
+ female = {
+ tshirt_1 = 35, tshirt_2 = 0,
+ torso_1 = 48, torso_2 = 0,
+ decals_1 = 7, decals_2 = 3,
+ arms = 44,
+ pants_1 = 34, pants_2 = 0,
+ shoes_1 = 27, shoes_2 = 0,
+ helmet_1 = -1, helmet_2 = 0,
+ chain_1 = 0, chain_2 = 0,
+ ears_1 = 2, ears_2 = 0
+ }
+ },
+
+ bullet_wear = {
+ male = {
+ bproof_1 = 11, bproof_2 = 1
+ },
+ female = {
+ bproof_1 = 13, bproof_2 = 1
+ }
+ },
+
+ gilet_wear = {
+ male = {
+ tshirt_1 = 59, tshirt_2 = 1
+ },
+ female = {
+ tshirt_1 = 36, tshirt_2 = 1
+ }
+ }
+}
diff --git a/modules/job_police/data/locales/br.lua b/modules/job_police/data/locales/br.lua
new file mode 100644
index 00000000..9423af49
--- /dev/null
+++ b/modules/job_police/data/locales/br.lua
@@ -0,0 +1,151 @@
+Translations = {
+ -- Cloackroom
+ ['cloakroom'] = 'Vestiário',
+ ['citizen_wear'] = 'Roupa casual',
+ ['police_wear'] = 'Uniforme da Polícia',
+ ['gilet_wear'] = 'orange reflective jacket',
+ ['bullet_wear'] = 'bulletproof vest',
+ ['no_outfit'] = 'there\'s no uniform that fits you!',
+ ['open_cloackroom'] = 'Pressione ~INPUT_CONTEXT~ para se trocar',
+ -- Armory
+ ['remove_object'] = 'take object',
+ ['deposit_object'] = 'deposit object',
+ ['get_weapon'] = 'Pegar arma',
+ ['put_weapon'] = 'Entregar arma',
+ ['buy_weapons'] = 'Comprar armas',
+ ['armory'] = 'Arsenal',
+ ['open_armory'] = 'Pressione ~INPUT_CONTEXT~ para acessar o arsenal',
+ ['armory_owned'] = 'owned',
+ ['armory_free'] = 'free',
+ ['armory_item'] = '$%s',
+ ['armory_weapontitle'] = 'armory - Buy weapon',
+ ['armory_componenttitle'] = 'armory - Weapon attatchments',
+ ['armory_bought'] = 'you bought an ~y~%s~s~ for ~g~$%s~s~',
+ ['armory_money'] = 'you cannot afford that weapon',
+ ['armory_hascomponent'] = 'you have that attatchment equiped!',
+ ['get_weapon_menu'] = 'armory - Withdraw Weapon',
+ ['put_weapon_menu'] = 'armory - Store Weapon',
+ -- Vehicles
+ ['vehicle_menu'] = 'vehicle',
+ ['vehicle_blocked'] = 'all available spawn points are currently blocked!',
+ ['garage_prompt'] = 'press ~INPUT_CONTEXT~ to access the ~y~Vehicle Actions~s~.',
+ ['garage_title'] = 'vehicle Actions',
+ ['garage_stored'] = 'stored',
+ ['garage_notstored'] = 'not in garage',
+ ['garage_storing'] = 'we\'re attempting to remove the vehicle, make sure no players are around it.',
+ ['garage_has_stored'] = 'the vehicle has been stored in your garage',
+ ['garage_has_notstored'] = 'no nearby owned vehicles were found',
+ ['garage_notavailable'] = 'your vehicle is not stored in the garage.',
+ ['garage_blocked'] = 'there\'s no available spawn points!',
+ ['garage_empty'] = 'you dont have any vehicles in your garage.',
+ ['garage_released'] = 'your vehicle has been released from the garage.',
+ ['garage_store_nearby'] = 'there is no nearby vehicles.',
+ ['garage_storeditem'] = 'open garage',
+ ['garage_storeitem'] = 'store vehicle in garage',
+ ['garage_buyitem'] = 'vehicle shop',
+ ['garage_notauthorized'] = 'you\'re not authorized to buy this kind of vehicles.',
+ ['helicopter_prompt'] = 'press ~INPUT_CONTEXT~ to access the ~y~Helicopter Actions~s~.',
+ ['shop_item'] = '$%s',
+ ['vehicleshop_title'] = 'vehicle Shop',
+ ['vehicleshop_confirm'] = 'do you want to buy this vehicle?',
+ ['vehicleshop_bought'] = 'you have bought ~y~%s~s~ for ~g~$%s~s~',
+ ['vehicleshop_money'] = 'you cannot afford that vehicle',
+ ['vehicleshop_awaiting_model'] = 'the vehicle is currently ~g~DOWNLOADING & LOADING~s~ please wait',
+ ['confirm_no'] = 'no',
+ ['confirm_yes'] = 'yes',
+ -- Service
+ ['service_max'] = 'you cannot enter service, max officers in service: %s/%s',
+ ['service_not'] = 'you have not entered service! You\'ll have to get changed first.',
+ ['service_anonunce'] = 'service information',
+ ['service_in'] = 'you\'ve entered service, welcome!',
+ ['service_in_announce'] = 'operator ~y~%s~s~ has entered service!',
+ ['service_out'] = 'you have left service.',
+ ['service_out_announce'] = 'operator ~y~%s~s~ has left their service.',
+ -- Action Menu
+ ['citizen_interaction'] = 'Interagir com o cidadão',
+ ['vehicle_interaction'] = 'Interagir com o veículo',
+ ['object_spawner'] = 'Interagir com as rodovias',
+
+ ['id_card'] = 'Carteira de identidade',
+ ['search'] = 'Procurar',
+ ['handcuff'] = 'Algemar / Soltar',
+ ['drag'] = 'drag',
+ ['put_in_vehicle'] = 'Colocar no veículo',
+ ['out_the_vehicle'] = 'take out of vehicle',
+ ['fine'] = 'Multa',
+ ['unpaid_bills'] = 'manage unpaid bills',
+ ['license_check'] = 'manage license',
+ ['license_revoke'] = 'revoke license',
+ ['license_revoked'] = 'your ~b~%s~s~ has been ~y~revoked~s~!',
+ ['licence_you_revoked'] = 'you revoked a ~b~%s~s~ which belonged to ~y~%s~s~',
+ ['no_players_nearby'] = 'nenhum jogador nas proximidades',
+ ['being_searched'] = 'you are being ~y~searched~s~ by the ~b~Police~s~',
+ -- Vehicle interaction
+ ['vehicle_info'] = 'Informações',
+ ['pick_lock'] = 'Trancar veículo',
+ ['vehicle_unlocked'] = 'Veículo ~g~destravado~s~',
+ ['no_vehicles_nearby'] = 'Nenhum veículo nas proximidades',
+ ['impound'] = 'apreender carro',
+ ['impound_prompt'] = 'Pressione ~INPUT_CONTEXT~ para cancelar a ~y~apreender~s~',
+ ['impound_canceled'] = 'you canceled the impound',
+ ['impound_canceled_moved'] = 'o apreender foi cancelado porque o veículo mudou',
+ ['impound_successful'] = 'você apreendeu o veículo',
+ ['search_database'] = 'informação do veículo',
+ ['search_database_title'] = 'informações do veículo - pesquisa com número de registro',
+ ['search_database_error_invalid'] = 'que ~r~não~s~ é um número de registro ~y~válido~s~',
+ -- Traffic interaction
+ ['traffic_interaction'] = 'Interagir com as rodovias',
+ ['cone'] = 'Cones',
+ ['barrier'] = 'Barreira',
+ ['spikestrips'] = 'Fita de pregos',
+ ['box'] = 'Caixa',
+ ['cash'] = 'Caixa de dinheiro',
+ -- ID Card Menu
+ ['name'] = 'name: %s',
+ ['job'] = 'job: %s',
+ ['sex'] = 'sex: %s',
+ ['dob'] = 'DOB: %s',
+ ['height'] = 'height: %s',
+ ['bac'] = 'BAC: %s',
+ ['unknown'] = 'unknown',
+ ['male'] = 'male',
+ ['female'] = 'female',
+ -- Body Search Menu
+ ['guns_label'] = '--- Armas ---',
+ ['inventory_label'] = '--- Inventário ---',
+ ['license_label'] = ' --- Licenses ---',
+ ['confiscate'] = 'confiscar %s',
+ ['confiscate_weapon'] = 'confiscate %s with %s bullets',
+ ['confiscate_inv'] = 'Confiscar %sx %s',
+ ['confiscate_dirty'] = 'Confiscar dinheiro sujo: $%s',
+ ['you_confiscated'] = 'you confiscated ~y~%sx~s~ ~b~%s~s~ from ~b~%s~s~',
+ ['got_confiscated'] = '~y~%sx~s~ ~b~%s~s~ were confiscated by ~y~%s~s~',
+ ['you_confiscated_account'] = 'you confiscated ~g~$%s~s~ (%s) from ~b~%s~s~',
+ ['got_confiscated_account'] = '~g~$%s~s~ (%s) was confiscated by ~y~%s~s~',
+ ['you_confiscated_weapon'] = 'you confiscated ~b~%s~s~ from ~b~%s~s~ with ~o~%s~s~ bullets',
+ ['got_confiscated_weapon'] = 'your ~b~%s~s~ with ~o~%s~s~ bullets was confiscated by ~y~%s~s~',
+ ['traffic_offense'] = 'Infrações de transito',
+ ['minor_offense'] = 'Infração leve',
+ ['average_offense'] = 'Infração média',
+ ['major_offense'] = 'Infração grave',
+ ['fine_total'] = 'multa: %s',
+ -- Vehicle Info Menu
+ ['plate'] = 'placa: %s',
+ ['owner_unknown'] = 'proprietário: Desconhecido',
+ ['owner'] = 'proprietário: %s',
+ -- Boss Menu
+ ['open_bossmenu'] = 'Pressione ~INPUT_CONTEXT~ para abrir o menu',
+ ['quantity_invalid'] = 'invalid quantity',
+ ['have_withdrawn'] = 'you have withdrawn ~y~%sx~s~ ~b~%s~s~',
+ ['have_deposited'] = 'you have deposited ~y~%sx~s~ ~b~%s~s~',
+ ['quantity'] = 'quantity',
+ ['inventory'] = 'inventory',
+ ['police_stock'] = 'police Stock',
+ -- Misc
+ ['remove_prop'] = 'Pressione ~INPUT_CONTEXT~ para remover o objeto',
+ ['map_blip'] = 'Departamento de Polícia',
+ ['unrestrained_timer'] = 'you feel your handcuffs slowly losing grip and fading away.',
+ -- Notifications
+ ['alert_police'] = 'Alerta da Polícia',
+ ['phone_police'] = 'police',
+}
diff --git a/modules/job_police/data/locales/cs.lua b/modules/job_police/data/locales/cs.lua
new file mode 100644
index 00000000..aad0524c
--- /dev/null
+++ b/modules/job_police/data/locales/cs.lua
@@ -0,0 +1,151 @@
+Translations = {
+ -- Cloakroom
+ ['cloakroom'] = 'satna',
+ ['citizen_wear'] = 'civilní oblek',
+ ['police_wear'] = 'policejní oblek',
+ ['gilet_wear'] = 'oranžová reflexní vesta',
+ ['bullet_wear'] = 'neprůstřelná vesta',
+ ['no_outfit'] = 'není zde žádná uniforma, která by ti sedla!',
+ ['open_cloackroom'] = 'stiskni ~INPUT_CONTEXT~ pro změnu ~y~oblečení~s~.',
+ -- Armory
+ ['remove_object'] = 'vzít objekt',
+ ['deposit_object'] = 'odevzdat objekt',
+ ['get_weapon'] = 'vzít zbraň ze zbrojnice',
+ ['put_weapon'] = 'uchovat zbraň ve zbrojnici',
+ ['buy_weapons'] = 'koupit zbraně',
+ ['armory'] = 'zbrojnice',
+ ['open_armory'] = 'stiskni ~INPUT_CONTEXT~ pro přístup do ~y~zbrojnice~s~.',
+ ['armory_owned'] = 'vlastněno',
+ ['armory_free'] = 'zdarma',
+ ['armory_item'] = '$%s',
+ ['armory_weapontitle'] = 'zbrojnice',
+ ['armory_componenttitle'] = 'zbrojnice - Příslušenství ke zbraním',
+ ['armory_bought'] = 'zakoupil jsi ~y~%s~s~ za ~g~$%s~s~',
+ ['armory_money'] = 'nemáš dostatek peněz na tuto zbraň',
+ ['armory_hascomponent'] = 'toto příslušenství máš již nainstalováno!',
+ ['get_weapon_menu'] = 'zbrojnice - Vzít zbraň',
+ ['put_weapon_menu'] = 'zbrojnice - Uchovat zbraň',
+ -- Vehicles
+ ['vehicle_menu'] = 'vozidlo',
+ ['vehicle_blocked'] = 'vsechny dostupne spawn pointy jsou blokovany!',
+ ['garage_prompt'] = 'stiskni ~INPUT_CONTEXT~ pro otevreni ~y~akce vozidel~s~.',
+ ['garage_title'] = 'akce vozidel',
+ ['garage_stored'] = 'uloženo',
+ ['garage_notstored'] = 'není v garáži',
+ ['garage_storing'] = 'pokousime se o odstraneni vozidla, ujisti se, ze kolem nej nejsou hraci.',
+ ['garage_has_stored'] = 'vozidlo bylo ulozeno do garaze',
+ ['garage_has_notstored'] = 'zadne nejblizsi vlastnene vozidlo nenalezeno',
+ ['garage_notavailable'] = 'tvoje vozidlo neni ulozeno v garazi.',
+ ['garage_blocked'] = 'nejsou zde zadne spawn pointy!',
+ ['garage_empty'] = 'zadne vozidlo nemas v garazi.',
+ ['garage_released'] = 'tvoje vozidlo bylo vyjmuto z garaze.',
+ ['garage_store_nearby'] = 'nejsou poblíž zádná vozidla.',
+ ['garage_storeditem'] = 'otevřít garáž',
+ ['garage_storeitem'] = 'uchovat vozidlo v garáži',
+ ['garage_buyitem'] = 'prodej vozidel',
+ ['garage_notauthorized'] = 'nemas opravneni ke koupi tohoto vozidla.',
+ ['helicopter_prompt'] = 'stiskni ~INPUT_CONTEXT~ pro pristup k ~y~akcim Helikopter~s~.',
+ ['shop_item'] = '$%s',
+ ['vehicleshop_title'] = 'prodejce vozidel',
+ ['vehicleshop_confirm'] = 'opravdu chces koupit toto vozidlo?',
+ ['vehicleshop_bought'] = 'koupil jsi ~y~%s~s~ za ~r~$%s~s~',
+ ['vehicleshop_money'] = 'toto vozidlo si nemuzete dovolit',
+ ['vehicleshop_awaiting_model'] = 'vozidlo se prave ~g~stahuje a nacita~s~ prosim pockej',
+ ['confirm_no'] = 'ne',
+ ['confirm_yes'] = 'ano',
+ -- Service
+ ['service_max'] = 'nemuzete vstoupit do sluzby, max dustojníci v provozu: %s/%s',
+ ['service_not'] = 'nezadali jste sluzbu! Nejprve se musíte zmenit.',
+ ['service_anonunce'] = 'informace o sluzbe',
+ ['service_in'] = 'vstoupil jsi do sluzby, vitej!',
+ ['service_in_announce'] = 'operator ~y~%s~s~ se pripojil do sluzby!',
+ ['service_out'] = 'opustil jsi sluzbu.',
+ ['service_out_announce'] = 'operator ~y~%s~s~ opustil jejich sluzbu.',
+ -- Action Menu
+ ['citizen_interaction'] = 'Interakce s občanem',
+ ['vehicle_interaction'] = 'Interakce vozidla',
+ ['object_spawner'] = 'Objekty',
+
+ ['id_card'] = 'Občanský průkaz',
+ ['search'] = 'prohledat',
+ ['handcuff'] = 'poutat / odpoutat',
+ ['drag'] = 'prenést',
+ ['put_in_vehicle'] = 'vlozit do vozidla',
+ ['out_the_vehicle'] = 'vytahnout z vozidla',
+ ['fine'] = 'pokuta',
+ ['unpaid_bills'] = 'spravovat nezaplacené pokuty',
+ ['license_check'] = 'spravovat průkazy',
+ ['license_revoke'] = 'zneplatnit průkaz',
+ ['license_revoked'] = 'vase ~b~%s~s~ bylo ~y~zruseno~s~!',
+ ['licence_you_revoked'] = 'zrusil jsi ~b~%s~s~ ktery patril ~y~%s~s~',
+ ['no_players_nearby'] = 'zadny hrac pobliz!',
+ ['being_searched'] = 'prave jsi ~y~prohledavan~s~ ~b~policii~s~',
+ -- Vehicle interaction
+ ['vehicle_info'] = 'informace o vozidle',
+ ['pick_lock'] = 'vypáčit vozidlo',
+ ['vehicle_unlocked'] = 'vozidlo je ~g~Odemčeno~s~',
+ ['no_vehicles_nearby'] = 'there is no vehicles nearby',
+ ['impound'] = 'odtahnout vozidlo',
+ ['impound_prompt'] = 'zmackni ~INPUT_CONTEXT~ pro zruseni ~y~odtahnuti~s~',
+ ['impound_canceled'] = 'zrusil jsi odtah',
+ ['impound_canceled_moved'] = 'odtah byl zrusen,protoze se vozidlo pohlo!',
+ ['impound_successful'] = 'uspesne jsi odtahl vozidlo',
+ ['search_database'] = 'informace o vozidle',
+ ['search_database_title'] = 'informace o vozidle - pomocí registracnich cisel',
+ ['search_database_error_invalid'] = 'tohle ~r~neni~s~ ~y~spravne~s~ registracni cislo',
+ -- Traffic interaction
+ ['traffic_interaction'] = 'interakce provozu',
+ ['cone'] = 'kuzel',
+ ['barrier'] = 'bariera',
+ ['spikestrips'] = 'ostnaty pas',
+ ['box'] = 'box',
+ ['cash'] = 'box penez',
+ -- ID Card Menu
+ ['name'] = 'jméno: %s',
+ ['job'] = 'práce: %s',
+ ['sex'] = 'pohlaví: %s',
+ ['dob'] = 'DOB: %s',
+ ['height'] = 'výška: %s',
+ ['bac'] = 'BAC: %s',
+ ['unknown'] = 'neznámé',
+ ['male'] = 'muž',
+ ['female'] = 'žena',
+ -- Body Search Menu
+ ['guns_label'] = '--- Zbraně ---',
+ ['inventory_label'] = '--- Inventář ---',
+ ['license_label'] = ' --- Průkazy ---',
+ ['confiscate'] = 'zabavit %s',
+ ['confiscate_weapon'] = 'zabavit %s s %s naboji',
+ ['confiscate_inv'] = 'zabavit %sx %s',
+ ['confiscate_dirty'] = 'zabavit spinave penize: $%s',
+ ['you_confiscated'] = 'zabavil jsi ~y~%sx~s~ ~b~%s~s~ od ~b~%s~s~',
+ ['got_confiscated'] = '~y~%sx~s~ ~b~%s~s~ byly zabaveny od ~y~%s~s~',
+ ['you_confiscated_account'] = 'zabavil jsi ~g~$%s~s~ (%s) od ~b~%s~s~',
+ ['got_confiscated_account'] = '~g~$%s~s~ (%s) byly zabaveny ~y~%s~s~',
+ ['you_confiscated_weapon'] = 'zabavil jsi ~b~%s~s~ od ~b~%s~s~ s ~o~%s~s~ naboji ',
+ ['got_confiscated_weapon'] = 'tvoje ~b~%s~s~ s ~o~%s~s~ bylo zabaveno od ~y~%s~s~',
+ ['traffic_offense'] = 'dopravní přestupek',
+ ['minor_offense'] = 'malý přestupek',
+ ['average_offense'] = 'přestupek',
+ ['major_offense'] = 'trestný čin',
+ ['fine_total'] = 'pokuta: %s',
+ -- Vehicle Info Menu
+ ['plate'] = 'SPZ: %s',
+ ['owner_unknown'] = 'vlastník: Neznámý',
+ ['owner'] = 'vlastník: %s',
+ -- Boss Menu
+ ['open_bossmenu'] = 'stiskni ~INPUT_CONTEXT~ pro otevření menu',
+ ['quantity_invalid'] = 'neplatné množství',
+ ['have_withdrawn'] = 'vybral jsi ~y~%sx~s~ ~b~%s~s~',
+ ['have_deposited'] = 'vložil jsi ~y~%sx~s~ ~b~%s~s~',
+ ['quantity'] = 'množství',
+ ['inventory'] = 'inventář',
+ ['police_stock'] = 'policejní sklad',
+ -- Misc
+ ['remove_prop'] = 'stiskni ~INPUT_CONTEXT~ pro odstranění předmětu',
+ ['map_blip'] = 'policejní stanice',
+ ['unrestrained_timer'] = 'cítíš, že tvé pouta pomalu ztrácejí přilnavost a padají.',
+ -- Notifications
+ ['alert_police'] = 'policejní poplach',
+ ['phone_police'] = 'policie',
+}
diff --git a/modules/job_police/data/locales/de.lua b/modules/job_police/data/locales/de.lua
new file mode 100644
index 00000000..639fb1d7
--- /dev/null
+++ b/modules/job_police/data/locales/de.lua
@@ -0,0 +1,151 @@
+Translations = {
+ -- Cloackroom
+ ['cloakroom'] = 'Garderobe',
+ ['citizen_wear'] = 'Zivilkleidung',
+ ['police_wear'] = 'Arbeitskleidung',
+ ['gilet_wear'] = 'orange reflective jacket',
+ ['bullet_wear'] = 'bulletproof vest',
+ ['no_outfit'] = 'there\'s no uniform that fits you!',
+ ['open_cloackroom'] = 'Drücke ~INPUT_CONTEXT~ um dich umzuziehen',
+ -- Armory
+ ['remove_object'] = 'take object',
+ ['deposit_object'] = 'deposit object',
+ ['get_weapon'] = 'Waffen holen',
+ ['put_weapon'] = 'Waffen bringen',
+ ['buy_weapons'] = 'Waffen kaufen',
+ ['armory'] = 'Waffenkammer',
+ ['open_armory'] = 'Drücke ~INPUT_CONTEXT~ um die Waffenkammer zu öffnen',
+ ['armory_owned'] = 'owned',
+ ['armory_free'] = 'free',
+ ['armory_item'] = '$%s',
+ ['armory_weapontitle'] = 'armory - Buy weapon',
+ ['armory_componenttitle'] = 'armory - Weapon attatchments',
+ ['armory_bought'] = 'you bought an ~y~%s~s~ for ~g~$%s~s~',
+ ['armory_money'] = 'you cannot afford that weapon',
+ ['armory_hascomponent'] = 'you have that attatchment equiped!',
+ ['get_weapon_menu'] = 'armory - Withdraw Weapon',
+ ['put_weapon_menu'] = 'armory - Store Weapon',
+ -- Vehicles
+ ['vehicle_menu'] = 'vehicle',
+ ['vehicle_blocked'] = 'all available spawn points are currently blocked!',
+ ['garage_prompt'] = 'press ~INPUT_CONTEXT~ to access the ~y~Vehicle Actions~s~.',
+ ['garage_title'] = 'vehicle Actions',
+ ['garage_stored'] = 'stored',
+ ['garage_notstored'] = 'not in garage',
+ ['garage_storing'] = 'we\'re attempting to remove the vehicle, make sure no players are around it.',
+ ['garage_has_stored'] = 'the vehicle has been stored in your garage',
+ ['garage_has_notstored'] = 'no nearby owned vehicles were found',
+ ['garage_notavailable'] = 'your vehicle is not stored in the garage.',
+ ['garage_blocked'] = 'there\'s no available spawn points!',
+ ['garage_empty'] = 'you dont have any vehicles in your garage.',
+ ['garage_released'] = 'your vehicle has been released from the garage.',
+ ['garage_store_nearby'] = 'there is no nearby vehicles.',
+ ['garage_storeditem'] = 'open garage',
+ ['garage_storeitem'] = 'store vehicle in garage',
+ ['garage_buyitem'] = 'vehicle shop',
+ ['garage_notauthorized'] = 'you\'re not authorized to buy this kind of vehicles.',
+ ['helicopter_prompt'] = 'press ~INPUT_CONTEXT~ to access the ~y~Helicopter Actions~s~.',
+ ['shop_item'] = '$%s',
+ ['vehicleshop_title'] = 'vehicle Shop',
+ ['vehicleshop_confirm'] = 'do you want to buy this vehicle?',
+ ['vehicleshop_bought'] = 'you have bought ~y~%s~s~ for ~r~$%s~s~',
+ ['vehicleshop_money'] = 'you cannot afford that vehicle',
+ ['vehicleshop_awaiting_model'] = 'the vehicle is currently ~g~DOWNLOADING & LOADING~s~ please wait',
+ ['confirm_no'] = 'no',
+ ['confirm_yes'] = 'yes',
+ -- Service
+ ['service_max'] = 'you cannot enter service, max officers in service: %s/%s',
+ ['service_not'] = 'you have not entered service! You\'ll have to get changed first.',
+ ['service_anonunce'] = 'service information',
+ ['service_in'] = 'you\'ve entered service, welcome!',
+ ['service_in_announce'] = 'operator ~y~%s~s~ has entered service!',
+ ['service_out'] = 'you have left service.',
+ ['service_out_announce'] = 'operator ~y~%s~s~ has left their service.',
+ -- Action Menu
+ ['citizen_interaction'] = 'Zivilistenaktionen',
+ ['vehicle_interaction'] = 'Fahrzeuginteraktionen',
+ ['object_spawner'] = 'Objekt Spawner',
+
+ ['id_card'] = 'ID Karte',
+ ['search'] = 'Suche',
+ ['handcuff'] = 'Festnehmen / Freilassen',
+ ['drag'] = 'drag',
+ ['put_in_vehicle'] = 'In Fahrzeug setzen',
+ ['out_the_vehicle'] = 'take out of vehicle',
+ ['fine'] = 'Strafe',
+ ['unpaid_bills'] = 'manage unpaid bills',
+ ['license_check'] = 'manage license',
+ ['license_revoke'] = 'revoke license',
+ ['license_revoked'] = 'your ~b~%s~s~ has been ~y~revoked~s~!',
+ ['licence_you_revoked'] = 'you revoked a ~b~%s~s~ which belonged to ~y~%s~s~',
+ ['no_players_nearby'] = 'keine Spieler in der Nähe',
+ ['being_searched'] = 'you are being ~y~searched~s~ by the ~b~Police~s~',
+ -- Vehicle interaction
+ ['vehicle_info'] = 'Fahrzeug Info',
+ ['pick_lock'] = 'Fahrzeug öffnen',
+ ['vehicle_unlocked'] = 'Fahrzeug ~g~offen~s~',
+ ['no_vehicles_nearby'] = 'Keine Fahrzeuge in der Nähe',
+ ['impound'] = 'impound vehicle',
+ ['impound_prompt'] = 'press ~INPUT_CONTEXT~ to cancel the ~y~impound~s~',
+ ['impound_canceled'] = 'you canceled the impound',
+ ['impound_canceled_moved'] = 'the impound has been canceled because the vehicle moved',
+ ['impound_successful'] = 'you have impounded the vehicle',
+ ['search_database'] = 'vehicle information',
+ ['search_database_title'] = 'vehicle information - search with registration number',
+ ['search_database_error_invalid'] = 'that is ~r~not~s~ a ~y~valid~s~ registration number',
+ -- Traffic interaction
+ ['traffic_interaction'] = 'Straßeninteraktionen',
+ ['cone'] = 'Hütchen',
+ ['barrier'] = 'Barriere',
+ ['spikestrips'] = 'Nagelband',
+ ['box'] = 'Box',
+ ['cash'] = 'Box mit Geld',
+ -- ID Card Menu
+ ['name'] = 'name: %s',
+ ['job'] = 'job: %s',
+ ['sex'] = 'sex: %s',
+ ['dob'] = 'DOB: %s',
+ ['height'] = 'height: %s',
+ ['bac'] = 'BAC: %s',
+ ['unknown'] = 'unknown',
+ ['male'] = 'male',
+ ['female'] = 'female',
+ -- Body Search Menu
+ ['guns_label'] = '--- Waffen ---',
+ ['inventory_label'] = '--- Inventar ---',
+ ['license_label'] = ' --- Licenses ---',
+ ['confiscate'] = 'konfeszieren %s',
+ ['confiscate_weapon'] = 'confiscate %s with %s bullets',
+ ['confiscate_inv'] = 'konfeziere %sx %s',
+ ['confiscate_dirty'] = 'schwarzgeld konfesziert: $%s',
+ ['you_confiscated'] = 'you confiscated ~y~%sx~s~ ~b~%s~s~ from ~b~%s~s~',
+ ['got_confiscated'] = '~y~%sx~s~ ~b~%s~s~ were confiscated by ~y~%s~s~',
+ ['you_confiscated_account'] = 'you confiscated ~g~$%s~s~ (%s) from ~b~%s~s~',
+ ['got_confiscated_account'] = '~g~$%s~s~ (%s) was confiscated by ~y~%s~s~',
+ ['you_confiscated_weapon'] = 'you confiscated ~b~%s~s~ from ~b~%s~s~ with ~o~%s~s~ bullets',
+ ['got_confiscated_weapon'] = 'your ~b~%s~s~ with ~o~%s~s~ bullets was confiscated by ~y~%s~s~',
+ ['traffic_offense'] = 'Verkehrs vergehen',
+ ['minor_offense'] = 'Geringes vergehen',
+ ['average_offense'] = 'Normales vergehen',
+ ['major_offense'] = 'Hohes vergehen',
+ ['fine_total'] = 'strafe: %s',
+ -- Vehicle Info Menu
+ ['plate'] = 'plate: %s',
+ ['owner_unknown'] = 'besitzer: Unbekannt',
+ ['owner'] = 'besitzer: %s',
+ -- Boss Menu
+ ['open_bossmenu'] = 'Drücke ~INPUT_CONTEXT~ um das Menü zu öffnen',
+ ['quantity_invalid'] = 'invalid quantity',
+ ['have_withdrawn'] = 'you have withdrawn ~y~%sx~s~ ~b~%s~s~',
+ ['have_deposited'] = 'you have deposited ~y~%sx~s~ ~b~%s~s~',
+ ['quantity'] = 'quantity',
+ ['inventory'] = 'inventory',
+ ['police_stock'] = 'police Stock',
+ -- Misc
+ ['remove_prop'] = 'Drücke ~INPUT_CONTEXT~ um das Objekt zu entfernen',
+ ['map_blip'] = 'Polizeistation',
+ ['unrestrained_timer'] = 'you feel your handcuffs slowly losing grip and fading away.',
+ -- Notifications
+ ['alert_police'] = 'Polizei alamieren',
+ ['phone_police'] = 'police',
+}
diff --git a/modules/job_police/data/locales/en.lua b/modules/job_police/data/locales/en.lua
new file mode 100644
index 00000000..c230c8bc
--- /dev/null
+++ b/modules/job_police/data/locales/en.lua
@@ -0,0 +1,151 @@
+Translations = {
+ -- Cloakroom
+ ['cloakroom'] = 'locker room',
+ ['citizen_wear'] = 'civilian Outfit',
+ ['police_wear'] = 'police Outfit',
+ ['gilet_wear'] = 'orange reflective jacket',
+ ['bullet_wear'] = 'bulletproof vest',
+ ['no_outfit'] = 'there\'s no uniform that fits you!',
+ ['open_cloackroom'] = 'press ~INPUT_CONTEXT~ to change ~y~clothes~s~.',
+ -- Armory
+ ['remove_object'] = 'withdraw object',
+ ['deposit_object'] = 'deposit object',
+ ['get_weapon'] = 'withdraw weapon from armory',
+ ['put_weapon'] = 'store weapon in armory',
+ ['buy_weapons'] = 'buy weapons',
+ ['armory'] = 'armory',
+ ['open_armory'] = 'press ~INPUT_CONTEXT~ to access the ~y~Armory~s~.',
+ ['armory_owned'] = 'owned',
+ ['armory_free'] = 'free',
+ ['armory_item'] = '$%s',
+ ['armory_weapontitle'] = 'armory - Buy weapon',
+ ['armory_componenttitle'] = 'armory - Weapon attatchments',
+ ['armory_bought'] = 'you bought an ~y~%s~s~ for ~g~$%s~s~',
+ ['armory_money'] = 'you cannot afford that weapon',
+ ['armory_hascomponent'] = 'you have that attatchment equiped!',
+ ['get_weapon_menu'] = 'armory - Withdraw Weapon',
+ ['put_weapon_menu'] = 'armory - Store Weapon',
+ -- Vehicles
+ ['vehicle_menu'] = 'vehicle',
+ ['vehicle_blocked'] = 'all available spawn points are currently blocked!',
+ ['garage_prompt'] = 'press ~INPUT_CONTEXT~ to access the ~y~Vehicle Actions~s~.',
+ ['garage_title'] = 'vehicle Actions',
+ ['garage_stored'] = 'stored',
+ ['garage_notstored'] = 'not in garage',
+ ['garage_storing'] = 'we\'re attempting to remove the vehicle, make sure no players are around it.',
+ ['garage_has_stored'] = 'the vehicle has been stored in your garage',
+ ['garage_has_notstored'] = 'no nearby owned vehicles were found',
+ ['garage_notavailable'] = 'your vehicle is not stored in the garage.',
+ ['garage_blocked'] = 'there\'s no available spawn points!',
+ ['garage_empty'] = 'you dont have any vehicles in your garage.',
+ ['garage_released'] = 'your vehicle has been released from the garage.',
+ ['garage_store_nearby'] = 'there is no nearby vehicles.',
+ ['garage_storeditem'] = 'open garage',
+ ['garage_storeitem'] = 'store vehicle in garage',
+ ['garage_buyitem'] = 'vehicle shop',
+ ['garage_notauthorized'] = 'you\'re not authorized to buy this kind of vehicles.',
+ ['helicopter_prompt'] = 'press ~INPUT_CONTEXT~ to access the ~y~Helicopter Actions~s~.',
+ ['shop_item'] = '$%s',
+ ['vehicleshop_title'] = 'vehicle Shop',
+ ['vehicleshop_confirm'] = 'do you want to buy this vehicle?',
+ ['vehicleshop_bought'] = 'you have bought ~y~%s~s~ for ~r~$%s~s~',
+ ['vehicleshop_money'] = 'you cannot afford that vehicle',
+ ['vehicleshop_awaiting_model'] = 'the vehicle is currently ~g~DOWNLOADING & LOADING~s~ please wait',
+ ['confirm_no'] = 'no',
+ ['confirm_yes'] = 'yes',
+ -- Service
+ ['service_max'] = 'you cannot enter service, max officers in service: %s/%s',
+ ['service_not'] = 'you have not entered service! You\'ll have to get changed first.',
+ ['service_anonunce'] = 'service information',
+ ['service_in'] = 'you\'ve entered service, welcome!',
+ ['service_in_announce'] = 'operator ~y~%s~s~ has entered service!',
+ ['service_out'] = 'you have left service.',
+ ['service_out_announce'] = 'operator ~y~%s~s~ has left their service.',
+ -- Action Menu
+ ['citizen_interaction'] = 'citizen Interaction',
+ ['vehicle_interaction'] = 'vehicle Interaction',
+ ['object_spawner'] = 'object Spawner',
+
+ ['id_card'] = 'ID Card',
+ ['search'] = 'search',
+ ['handcuff'] = 'cuff / Uncuff',
+ ['drag'] = 'escort',
+ ['put_in_vehicle'] = 'put in Vehicle',
+ ['out_the_vehicle'] = 'drag out from vehicle',
+ ['fine'] = 'fine',
+ ['unpaid_bills'] = 'manage unpaid bills',
+ ['license_check'] = 'manage license',
+ ['license_revoke'] = 'revoke license',
+ ['license_revoked'] = 'your ~b~%s~s~ has been ~y~revoked~s~!',
+ ['licence_you_revoked'] = 'you revoked a ~b~%s~s~ which belonged to ~y~%s~s~',
+ ['no_players_nearby'] = 'there is no player(s) nearby!',
+ ['being_searched'] = 'you are being ~y~searched~s~ by the ~b~Police~s~',
+ -- Vehicle interaction
+ ['vehicle_info'] = 'vehicle Info',
+ ['pick_lock'] = 'lockpick Vehicle',
+ ['vehicle_unlocked'] = 'vehicle ~g~Unlocked~s~',
+ ['no_vehicles_nearby'] = 'there is no vehicles nearby',
+ ['impound'] = 'impound vehicle',
+ ['impound_prompt'] = 'press ~INPUT_CONTEXT~ to cancel the ~y~impound~s~',
+ ['impound_canceled'] = 'you canceled the impound',
+ ['impound_canceled_moved'] = 'the impound has been canceled because the vehicle moved',
+ ['impound_successful'] = 'you have impounded the vehicle',
+ ['search_database'] = 'vehicle information',
+ ['search_database_title'] = 'vehicle information - search with registration number',
+ ['search_database_error_invalid'] = 'that is ~r~not~s~ a ~y~valid~s~ registration number',
+ -- Traffic interaction
+ ['traffic_interaction'] = 'interaction Traffic',
+ ['cone'] = 'cone',
+ ['barrier'] = 'barrier',
+ ['spikestrips'] = 'spikestrips',
+ ['box'] = 'box',
+ ['cash'] = 'box of cash',
+ -- ID Card Menu
+ ['name'] = 'name: %s',
+ ['job'] = 'job: %s',
+ ['sex'] = 'sex: %s',
+ ['dob'] = 'DOB: %s',
+ ['height'] = 'height: %s',
+ ['bac'] = 'BAC: %s',
+ ['unknown'] = 'unknown',
+ ['male'] = 'male',
+ ['female'] = 'female',
+ -- Body Search Menu
+ ['guns_label'] = '--- Guns ---',
+ ['inventory_label'] = '--- Inventory ---',
+ ['license_label'] = ' --- Licenses ---',
+ ['confiscate'] = 'confiscate %s',
+ ['confiscate_weapon'] = 'confiscate %s with %s bullets',
+ ['confiscate_inv'] = 'confiscate %sx %s',
+ ['confiscate_dirty'] = 'confiscate dirty money: $%s',
+ ['you_confiscated'] = 'you confiscated ~y~%sx~s~ ~b~%s~s~ from ~b~%s~s~',
+ ['got_confiscated'] = '~y~%sx~s~ ~b~%s~s~ were confiscated by ~y~%s~s~',
+ ['you_confiscated_account'] = 'you confiscated ~g~$%s~s~ (%s) from ~b~%s~s~',
+ ['got_confiscated_account'] = '~g~$%s~s~ (%s) was confiscated by ~y~%s~s~',
+ ['you_confiscated_weapon'] = 'you confiscated ~b~%s~s~ from ~b~%s~s~ with ~o~%s~s~ bullets',
+ ['got_confiscated_weapon'] = 'your ~b~%s~s~ with ~o~%s~s~ bullets was confiscated by ~y~%s~s~',
+ ['traffic_offense'] = 'traffic Offense',
+ ['minor_offense'] = 'minor Offense',
+ ['average_offense'] = 'average Offense',
+ ['major_offense'] = 'major Offense',
+ ['fine_total'] = 'fine: %s',
+ -- Vehicle Info Menu
+ ['plate'] = 'plate: %s',
+ ['owner_unknown'] = 'owner: Unknown',
+ ['owner'] = 'owner: %s',
+ -- Boss Menu
+ ['open_bossmenu'] = 'press ~INPUT_CONTEXT~ to open the menu',
+ ['quantity_invalid'] = 'invalid quantity',
+ ['have_withdrawn'] = 'you have withdrawn ~y~%sx~s~ ~b~%s~s~',
+ ['have_deposited'] = 'you have deposited ~y~%sx~s~ ~b~%s~s~',
+ ['quantity'] = 'quantity',
+ ['inventory'] = 'inventory',
+ ['police_stock'] = 'police Stock',
+ -- Misc
+ ['remove_prop'] = 'press ~INPUT_CONTEXT~ to delete the object',
+ ['map_blip'] = 'police Station',
+ ['unrestrained_timer'] = 'you feel your handcuffs slowly losing grip and fading away.',
+ -- Notifications
+ ['alert_police'] = 'police alert',
+ ['phone_police'] = 'police',
+}
diff --git a/modules/job_police/data/locales/es.lua b/modules/job_police/data/locales/es.lua
new file mode 100644
index 00000000..21a9af87
--- /dev/null
+++ b/modules/job_police/data/locales/es.lua
@@ -0,0 +1,151 @@
+Translations = {
+ -- Cloakroom
+ ['cloakroom'] = 'Taquilla',
+ ['citizen_wear'] = 'Ropa civil',
+ ['police_wear'] = 'Ropa CNP',
+ ['gilet_wear'] = 'orange reflective jacket',
+ ['bullet_wear'] = 'bulletproof vest',
+ ['no_outfit'] = 'there\'s no uniform that fits you!',
+ ['open_cloackroom'] = 'Presionar ~INPUT_CONTEXT~ para abrir la taquilla',
+ -- Armory
+ ['remove_object'] = 'take object',
+ ['deposit_object'] = 'deposit object',
+ ['get_weapon'] = 'Coger arma',
+ ['put_weapon'] = 'Depositar arma',
+ ['buy_weapons'] = 'Comprar armas',
+ ['armory'] = 'Arsenal',
+ ['open_armory'] = 'Presionarr ~INPUT_CONTEXT~ para acceder a la armeria',
+ ['armory_owned'] = 'owned',
+ ['armory_free'] = 'free',
+ ['armory_item'] = '$%s',
+ ['armory_weapontitle'] = 'armory - Buy weapon',
+ ['armory_componenttitle'] = 'armory - Weapon attatchments',
+ ['armory_bought'] = 'you bought an ~y~%s~s~ for ~g~$%s~s~',
+ ['armory_money'] = 'you cannot afford that weapon',
+ ['armory_hascomponent'] = 'you have that attatchment equiped!',
+ ['get_weapon_menu'] = 'armory - Withdraw Weapon',
+ ['put_weapon_menu'] = 'armory - Store Weapon',
+ -- Vehicles
+ ['vehicle_menu'] = 'vehicle',
+ ['vehicle_blocked'] = 'all available spawn points are currently blocked!',
+ ['garage_prompt'] = 'press ~INPUT_CONTEXT~ to access the ~y~Vehicle Actions~s~.',
+ ['garage_title'] = 'vehicle Actions',
+ ['garage_stored'] = 'stored',
+ ['garage_notstored'] = 'not in garage',
+ ['garage_storing'] = 'we\'re attempting to remove the vehicle, make sure no players are around it.',
+ ['garage_has_stored'] = 'the vehicle has been stored in your garage',
+ ['garage_has_notstored'] = 'no nearby owned vehicles were found',
+ ['garage_notavailable'] = 'your vehicle is not stored in the garage.',
+ ['garage_blocked'] = 'there\'s no available spawn points!',
+ ['garage_empty'] = 'you dont have any vehicles in your garage.',
+ ['garage_released'] = 'your vehicle has been released from the garage.',
+ ['garage_store_nearby'] = 'there is no nearby vehicles.',
+ ['garage_storeditem'] = 'open garage',
+ ['garage_storeitem'] = 'store vehicle in garage',
+ ['garage_buyitem'] = 'vehicle shop',
+ ['garage_notauthorized'] = 'you\'re not authorized to buy this kind of vehicles.',
+ ['helicopter_prompt'] = 'press ~INPUT_CONTEXT~ to access the ~y~Helicopter Actions~s~.',
+ ['shop_item'] = '$%s',
+ ['vehicleshop_title'] = 'vehicle Shop',
+ ['vehicleshop_confirm'] = 'do you want to buy this vehicle?',
+ ['vehicleshop_bought'] = 'you have bought ~y~%s~s~ for ~r~$%s~s~',
+ ['vehicleshop_money'] = 'you cannot afford that vehicle',
+ ['vehicleshop_awaiting_model'] = 'the vehicle is currently ~g~DOWNLOADING & LOADING~s~ please wait',
+ ['confirm_no'] = 'no',
+ ['confirm_yes'] = 'yes',
+ -- Service
+ ['service_max'] = 'you cannot enter service, max officers in service: %s/%s',
+ ['service_not'] = 'you have not entered service! You\'ll have to get changed first.',
+ ['service_anonunce'] = 'service information',
+ ['service_in'] = 'you\'ve entered service, welcome!',
+ ['service_in_announce'] = 'operator ~y~%s~s~ has entered service!',
+ ['service_out'] = 'you have left service.',
+ ['service_out_announce'] = 'operator ~y~%s~s~ has left their service.',
+ -- Action Menu
+ ['citizen_interaction'] = 'Interacción ciudadana',
+ ['vehicle_interaction'] = 'Interacción vehículo',
+ ['object_spawner'] = 'Colocar objetos',
+
+ ['id_card'] = 'Documento de identidad',
+ ['search'] = 'Buscar',
+ ['handcuff'] = 'Poner/quitar Esposas',
+ ['drag'] = 'escoltar',
+ ['put_in_vehicle'] = 'Meter en el vehículo',
+ ['out_the_vehicle'] = 'Sacar del vehículo',
+ ['fine'] = 'Multa',
+ ['unpaid_bills'] = 'manage unpaid bills',
+ ['license_check'] = 'manage license',
+ ['license_revoke'] = 'revoke license',
+ ['license_revoked'] = 'your ~b~%s~s~ has been ~y~revoked~s~!',
+ ['licence_you_revoked'] = 'you revoked a ~b~%s~s~ which belonged to ~y~%s~s~',
+ ['no_players_nearby'] = 'no hay jugadores cerca',
+ ['being_searched'] = 'you are being ~y~searched~s~ by the ~b~Police~s~',
+ -- Vehicle interaction
+ ['vehicle_info'] = 'Información del vehículo',
+ ['pick_lock'] = 'Forzar coche',
+ ['vehicle_unlocked'] = 'Vehículo desbloqueado~s~',
+ ['no_vehicles_nearby'] = 'No hay vehículos cerca',
+ ['impound'] = 'impound vehicle',
+ ['impound_prompt'] = 'press ~INPUT_CONTEXT~ to cancel the ~y~impound~s~',
+ ['impound_canceled'] = 'you canceled the impound',
+ ['impound_canceled_moved'] = 'the impound has been canceled because the vehicle moved',
+ ['impound_successful'] = 'you have impounded the vehicle',
+ ['search_database'] = 'vehicle information',
+ ['search_database_title'] = 'vehicle information - search with registration number',
+ ['search_database_error_invalid'] = 'that is ~r~not~s~ a ~y~valid~s~ registration number',
+ -- Traffic interaction
+ ['traffic_interaction'] = 'Rutas de interacción',
+ ['cone'] = 'Cono',
+ ['barrier'] = 'Barrera',
+ ['spikestrips'] = 'Grada',
+ ['box'] = 'Caja',
+ ['cash'] = 'Dinero',
+ -- ID Card Menu
+ ['name'] = 'name: %s',
+ ['job'] = 'job: %s',
+ ['sex'] = 'sex: %s',
+ ['dob'] = 'DOB: %s',
+ ['height'] = 'height: %s',
+ ['bac'] = 'BAC: %s',
+ ['unknown'] = 'unknown',
+ ['male'] = 'male',
+ ['female'] = 'female',
+ -- Body Search Menu
+ ['guns_label'] = '--- Armas ---',
+ ['inventory_label'] = '--- Inventario ---',
+ ['license_label'] = ' --- Licenses ---',
+ ['confiscate'] = 'confiscar %s',
+ ['confiscate_weapon'] = 'confiscate %s with %s bullets',
+ ['confiscate_inv'] = 'confiscar %sx %s',
+ ['confiscate_dirty'] = 'confiscar dinero negro: €%s',
+ ['you_confiscated'] = 'you confiscated ~y~%sx~s~ ~b~%s~s~ from ~b~%s~s~',
+ ['got_confiscated'] = '~y~%sx~s~ ~b~%s~s~ were confiscated by ~y~%s~s~',
+ ['you_confiscated_account'] = 'you confiscated ~g~€%s~s~ (%s) from ~b~%s~s~',
+ ['got_confiscated_account'] = '~g~€%s~s~ (%s) was confiscated by ~y~%s~s~',
+ ['you_confiscated_weapon'] = 'you confiscated ~b~%s~s~ from ~b~%s~s~ with ~o~%s~s~ bullets',
+ ['got_confiscated_weapon'] = 'your ~b~%s~s~ with ~o~%s~s~ bullets was confiscated by ~y~%s~s~',
+ ['traffic_offense'] = 'Delito de tráfico',
+ ['minor_offense'] = 'Delito menor',
+ ['average_offense'] = 'Delito medio',
+ ['major_offense'] = 'Delito grave',
+ ['fine_total'] = 'Multa total: %s',
+ -- Vehicle Info Menu
+ ['plate'] = 'n°: %s',
+ ['owner_unknown'] = 'propietario: Desconocido',
+ ['owner'] = 'propietario: %s',
+ -- Boss Menu
+ ['open_bossmenu'] = 'presionar ~INPUT_CONTEXT~ para abrir el menú',
+ ['quantity_invalid'] = 'cantidad invalida',
+ ['have_withdrawn'] = 'you have withdrawn ~y~%sx~s~ ~b~%s~s~',
+ ['have_deposited'] = 'you have deposited ~y~%sx~s~ ~b~%s~s~',
+ ['quantity'] = 'cantidad',
+ ['inventory'] = 'inventario',
+ ['police_stock'] = 'almacen Policial',
+ -- Misc
+ ['remove_prop'] = 'presionar ~INPUT_CONTEXT~ para eliminar el objeto',
+ ['map_blip'] = 'comisaría de policía',
+ ['unrestrained_timer'] = 'you feel your handcuffs slowly losing grip and fading away.',
+ -- Notifications
+ ['alert_police'] = 'alerta policia',
+ ['phone_police'] = 'policia',
+}
diff --git a/modules/job_police/data/locales/fi.lua b/modules/job_police/data/locales/fi.lua
new file mode 100644
index 00000000..732f1db7
--- /dev/null
+++ b/modules/job_police/data/locales/fi.lua
@@ -0,0 +1,151 @@
+Translations = {
+ -- Cloakroom
+ ['cloakroom'] = 'vaatelokero',
+ ['citizen_wear'] = 'siviiliasu',
+ ['police_wear'] = 'poliisiasu',
+ ['gilet_wear'] = 'huomioliivi',
+ ['bullet_wear'] = 'luotiliivi',
+ ['no_outfit'] = 'täällä ei ole sinulle sopivaa asua',
+ ['open_cloackroom'] = 'paina ~INPUT_CONTEXT~ vaihtaaksesi ~y~vaatteita~s~.',
+ -- Armory
+ ['remove_object'] = 'ota esine',
+ ['deposit_object'] = 'talleta esine',
+ ['get_weapon'] = 'ota ase',
+ ['put_weapon'] = 'laita ase pois',
+ ['buy_weapons'] = 'osta aseita',
+ ['armory'] = 'asevarasto',
+ ['open_armory'] = 'paina ~INPUT_CONTEXT~ avataksesi asevarasto',
+ ['armory_owned'] = 'owned',
+ ['armory_free'] = 'free',
+ ['armory_item'] = '$%s',
+ ['armory_weapontitle'] = 'armory - Buy weapon',
+ ['armory_componenttitle'] = 'armory - Weapon attatchments',
+ ['armory_bought'] = 'you bought an ~y~%s~s~ for ~g~$%s~s~',
+ ['armory_money'] = 'you cannot afford that weapon',
+ ['armory_hascomponent'] = 'you have that attatchment equiped!',
+ ['get_weapon_menu'] = 'armory - Withdraw Weapon',
+ ['put_weapon_menu'] = 'armory - Store Weapon',
+ -- Vehicles
+ ['vehicle_menu'] = 'vehicle',
+ ['vehicle_blocked'] = 'all available spawn points are currently blocked!',
+ ['garage_prompt'] = 'press ~INPUT_CONTEXT~ to access the ~y~Vehicle Actions~s~.',
+ ['garage_title'] = 'vehicle Actions',
+ ['garage_stored'] = 'stored',
+ ['garage_notstored'] = 'not in garage',
+ ['garage_storing'] = 'we\'re attempting to remove the vehicle, make sure no players are around it.',
+ ['garage_has_stored'] = 'the vehicle has been stored in your garage',
+ ['garage_has_notstored'] = 'no nearby owned vehicles were found',
+ ['garage_notavailable'] = 'your vehicle is not stored in the garage.',
+ ['garage_blocked'] = 'there\'s no available spawn points!',
+ ['garage_empty'] = 'you dont have any vehicles in your garage.',
+ ['garage_released'] = 'your vehicle has been released from the garage.',
+ ['garage_store_nearby'] = 'there is no nearby vehicles.',
+ ['garage_storeditem'] = 'open garage',
+ ['garage_storeitem'] = 'store vehicle in garage',
+ ['garage_buyitem'] = 'vehicle shop',
+ ['garage_notauthorized'] = 'you\'re not authorized to buy this kind of vehicles.',
+ ['helicopter_prompt'] = 'press ~INPUT_CONTEXT~ to access the ~y~Helicopter Actions~s~.',
+ ['shop_item'] = '$%s',
+ ['vehicleshop_title'] = 'vehicle Shop',
+ ['vehicleshop_confirm'] = 'do you want to buy this vehicle?',
+ ['vehicleshop_bought'] = 'you have bought ~y~%s~s~ for ~r~$%s~s~',
+ ['vehicleshop_money'] = 'you cannot afford that vehicle',
+ ['vehicleshop_awaiting_model'] = 'the vehicle is currently ~g~DOWNLOADING & LOADING~s~ please wait',
+ ['confirm_no'] = 'no',
+ ['confirm_yes'] = 'yes',
+ -- Service
+ ['service_max'] = 'you cannot enter service, max officers in service: %s/%s',
+ ['service_not'] = 'you have not entered service! You\'ll have to get changed first.',
+ ['service_anonunce'] = 'service information',
+ ['service_in'] = 'you\'ve entered service, welcome!',
+ ['service_in_announce'] = 'operator ~y~%s~s~ has entered service!',
+ ['service_out'] = 'you have left service.',
+ ['service_out_announce'] = 'operator ~y~%s~s~ has left their service.',
+ -- Action Menu
+ ['citizen_interaction'] = 'siviilin vuorovaikutus',
+ ['vehicle_interaction'] = 'ajoneuvon vuorovaikutus',
+ ['object_spawner'] = 'objekti spawneri',
+
+ ['id_card'] = 'henkilöllisyystodistus',
+ ['search'] = 'tutki',
+ ['handcuff'] = 'raudat On/Off',
+ ['drag'] = 'raahaa',
+ ['put_in_vehicle'] = 'laita ajoneuvoon',
+ ['out_the_vehicle'] = 'ota ulos ajoneuvosta',
+ ['fine'] = 'sakko',
+ ['unpaid_bills'] = 'hallinoi maksamattomia laskuja',
+ ['license_check'] = 'hallitse lisenssejä',
+ ['license_revoke'] = 'kumoa lisenssi',
+ ['license_revoked'] = 'sinun ~b~%s~s~ ~y~kumottiin~s~!',
+ ['licence_you_revoked'] = 'sinä kumosit ~b~%s~s~ mikä kuului henkilölle ~y~%s~s~',
+ ['no_players_nearby'] = 'ei pelaajia lähettyvillä',
+ ['being_searched'] = 'you are being ~y~searched~s~ by the ~b~Police~s~',
+ -- Vehicle interaction
+ ['vehicle_info'] = 'ajoneuvon tiedot',
+ ['pick_lock'] = 'tiirikoi ovet',
+ ['vehicle_unlocked'] = 'ajoneuvo ~g~Avattu~s~',
+ ['no_vehicles_nearby'] = 'ei ajoneuvoja lähettyvillä',
+ ['impound'] = 'takavarikoi ajoneuvo',
+ ['impound_prompt'] = 'paina ~INPUT_CONTEXT~ peruaksesi ~y~takavarikointi~s~',
+ ['impound_canceled'] = 'sinä peruit takavarikoinnin',
+ ['impound_canceled_moved'] = 'takavarikointi peruuntui koska ajoneuvo liikku',
+ ['impound_successful'] = 'takavarikoit ajoneuvon',
+ ['search_database'] = 'ajoneuvon tiedot',
+ ['search_database_title'] = 'ajoneuvon tiedot - etsi rekisterinumerolla',
+ ['search_database_error_invalid'] = 'tämä ~r~ei ole~s~ ~y~voimassa oleva~s~ rekisterinumero',
+ -- Traffic interaction
+ ['traffic_interaction'] = 'liikenteen vuorovaikutus',
+ ['cone'] = 'kartio',
+ ['barrier'] = 'este',
+ ['spikestrips'] = 'piikkimatto',
+ ['box'] = 'laatikko',
+ ['cash'] = 'rahalaatikko',
+ -- ID Card Menu
+ ['name'] = 'nimi: %s',
+ ['job'] = 'työ: %s',
+ ['sex'] = 'sukupuoli: %s',
+ ['dob'] = 'syntymäaika: %s',
+ ['height'] = 'pituus: %s',
+ ['bac'] = 'alkometri: %s',
+ ['unknown'] = 'tuntematon',
+ ['male'] = 'mies',
+ ['female'] = 'nainen',
+ -- Body Search Menu
+ ['guns_label'] = '--- Aseet ---',
+ ['inventory_label'] = '--- Reppu ---',
+ ['license_label'] = ' --- Lisenssit ---',
+ ['confiscate'] = 'takavarikoi %s',
+ ['confiscate_weapon'] = 'confiscate %s with %s bullets',
+ ['confiscate_inv'] = 'takavarikoi %sx %s',
+ ['confiscate_dirty'] = 'takavarikoi likainen raha: $%s',
+ ['you_confiscated'] = 'sinä takavarioit ~y~%sx~s~ ~b~%s~s~ pelaajalta ~b~%s~s~',
+ ['got_confiscated'] = '~y~%sx~s~ ~b~%s~s~ takavarikoitiin sinulta pelaajan ~y~%s~s~ toimesta',
+ ['you_confiscated_account'] = 'sinä takavarikoit ~g~$%s~s~ (%s) pelaajalta ~b~%s~s~',
+ ['got_confiscated_account'] = '~g~$%s~s~ (%s) takavarikoitiin sinulta pelaajan ~y~%s~s~ toimesta',
+ ['you_confiscated_weapon'] = 'sinä takavarikoit ~b~%s~s~ pelaajalta ~b~%s~s~ jossa oli ~o~%s~s~ panosta',
+ ['got_confiscated_weapon'] = 'sinun ~b~%s~s~ jossa oli ~o~%s~s~ panosta takavarikoitiin sinulta~y~%s~s~',
+ ['traffic_offense'] = 'liikenne rikokset',
+ ['minor_offense'] = 'lievät rikokset',
+ ['average_offense'] = 'keskisuuret rikokset',
+ ['major_offense'] = 'vakavat rikokset',
+ ['fine_total'] = 'sakko: %s',
+ --Vehicle Info Menu
+ ['plate'] = 'kilpi: %s',
+ ['owner_unknown'] = 'omistaja: Tuntematon',
+ ['owner'] = 'omistaja: %s',
+ --Boss Menu
+ ['open_bossmenu'] = 'paina ~INPUT_CONTEXT~ avataksesi valikon',
+ ['quantity_invalid'] = 'invalid quantity',
+ ['have_withdrawn'] = 'sinä otit varastosta ~y~%sx~s~ ~b~%s~s~',
+ ['have_deposited'] = 'sinä talletit varastoon ~y~%sx~s~ ~b~%s~s~',
+ ['quantity'] = 'määrä',
+ ['inventory'] = 'varasto',
+ ['police_stock'] = 'poliisin Varasto',
+ -- Misc
+ ['remove_prop'] = 'paina ~INPUT_CONTEXT~ poistaaksesi objektin',
+ ['map_blip'] = 'poliisilaitos',
+ ['unrestrained_timer'] = 'tunnet kuinka hitaasti käsiraudat alkavat löystyä ja irtoavat',
+ -- Notifications
+ ['alert_police'] = 'hälyytys Poliisi',
+ ['phone_police'] = 'poliisi',
+}
diff --git a/modules/job_police/data/locales/fr.lua b/modules/job_police/data/locales/fr.lua
new file mode 100644
index 00000000..42ea497e
--- /dev/null
+++ b/modules/job_police/data/locales/fr.lua
@@ -0,0 +1,151 @@
+Translations = {
+ -- Cloakroom
+ ['cloakroom'] = 'vestiaire',
+ ['citizen_wear'] = 'tenue Civil',
+ ['police_wear'] = 'tenue Policier',
+ ['gilet_wear'] = 'gilet orange',
+ ['bullet_wear'] = 'gilet pare-balles',
+ ['no_outfit'] = 'il n\'y a pas d\'uniforme à votre taille...',
+ ['open_cloackroom'] = 'appuyez sur ~INPUT_CONTEXT~ pour vous changer',
+ -- Armory
+ ['remove_object'] = 'prendre Objet',
+ ['deposit_object'] = 'déposer objet',
+ ['get_weapon'] = 'prendre Arme',
+ ['put_weapon'] = 'déposer Arme',
+ ['buy_weapons'] = 'acheter Armes',
+ ['armory'] = 'armurerie',
+ ['open_armory'] = 'appuyez sur ~INPUT_CONTEXT~ pour accéder à l\'armurerie',
+ ['armory_owned'] = 'possédé',
+ ['armory_free'] = 'gratuit',
+ ['armory_item'] = '$%s',
+ ['armory_weapontitle'] = 'armurerie - Acheter une arme',
+ ['armory_componenttitle'] = 'armurerie - Accessoires d\'armes',
+ ['armory_bought'] = 'vous achetez un ~y~%s~s~ pour ~g~$%s~s~',
+ ['armory_money'] = 'vous ne pouvez pas acheter cette arme',
+ ['armory_hascomponent'] = 'vous avez cet accessoire équipé!',
+ ['get_weapon_menu'] = 'armurerie - Retirer arme',
+ ['put_weapon_menu'] = 'armurerie - Stocker arme',
+ -- Vehicles
+ ['vehicle_menu'] = 'véhicule',
+ ['vehicle_blocked'] = 'tous les points de spawn sont bloqués!',
+ ['garage_prompt'] = 'appuyez sur ~INPUT_CONTEXT~ pour accéder aux ~y~Actions Véhicule~s~.',
+ ['garage_title'] = 'actions Véhicules',
+ ['garage_stored'] = 'rangé',
+ ['garage_notstored'] = 'sorti(e)',
+ ['garage_storing'] = 'tentative de suppression du véhicule, assurez-vous que personne ne soit autour.',
+ ['garage_has_stored'] = 'le véhicule a bien été rangé dans le garage',
+ ['garage_has_notstored'] = 'aucun véhicule dans le garage',
+ ['garage_notavailable'] = 'votre véhicule n\'est pas rangé dans le garage.',
+ ['garage_blocked'] = 'la sortie du garage est obstruée!',
+ ['garage_empty'] = 'vous n\'avez aucun véhicule dans le garage.',
+ ['garage_released'] = 'votre véhicule a été sorti.',
+ ['garage_store_nearby'] = 'aucun véhicule a proximité.',
+ ['garage_storeditem'] = 'ouvrir le garage',
+ ['garage_storeitem'] = 'ranger le véhicule',
+ ['garage_buyitem'] = 'magasin véhicule',
+ ['garage_notauthorized'] = 'you\'re not authorized to buy this kind of vehicles.',
+ ['helicopter_prompt'] = 'appuyez sur ~INPUT_CONTEXT~ pour accéder aux ~y~Actions de l\'hélicoptère~s~.',
+ ['shop_item'] = '$%s',
+ ['vehicleshop_title'] = 'magasin véhicule',
+ ['vehicleshop_confirm'] = 'voulez-vous acheter ce véhicule?',
+ ['vehicleshop_bought'] = 'vous avez acheté ~y~%s~s~ pour ~r~$%s~s~',
+ ['vehicleshop_money'] = 'vous ne pouvez pas acheter ce véhicule',
+ ['vehicleshop_awaiting_model'] = 'le véhicule est actuellement en ~g~PRÉPARATION~s~ veuillez patienter',
+ ['confirm_no'] = 'non',
+ ['confirm_yes'] = 'oui',
+ -- Service
+ ['service_max'] = 'vous ne pouvez pas entrer en service, officiers en service: %s/%s',
+ ['service_not'] = 'vous n\'êtes pas en service! Vous devez d\'abord enfiler votre tenue.',
+ ['service_anonunce'] = 'prise de service',
+ ['service_in'] = 'vous êtes en service, bon courage!',
+ ['service_in_announce'] = 'l\'officier ~y~%s~s~ est entré en service!',
+ ['service_out'] = 'vous avez terminé votre service.',
+ ['service_out_announce'] = 'l\'officier ~y~%s~s~ a quitté son service.',
+ -- Action Menu
+ ['citizen_interaction'] = 'interaction citoyen',
+ ['vehicle_interaction'] = 'interaction véhicule',
+ ['object_spawner'] = 'placer objets',
+
+ ['id_card'] = 'carte d\'identité',
+ ['search'] = 'fouiller',
+ ['handcuff'] = 'menotter / Démenotter',
+ ['drag'] = 'escorter',
+ ['put_in_vehicle'] = 'mettre dans véhicule',
+ ['out_the_vehicle'] = 'sortir du véhicule',
+ ['fine'] = 'Amende',
+ ['unpaid_bills'] = 'gérer les amendes impayées',
+ ['license_check'] = 'gérer les licences',
+ ['license_revoke'] = 'révoquer la licence',
+ ['license_revoked'] = 'votre ~b~%s~s~ a été ~y~révoqué~s~!',
+ ['licence_you_revoked'] = 'vous avez révoqué un ~b~%s~s~ qui appartenait à ~y~%s~s~',
+ ['no_players_nearby'] = 'aucun joueur à proximité',
+ ['being_searched'] = 'vous êtes ~y~recherché(e)~s~ par la ~b~Police~s~',
+ -- Vehicle interaction
+ ['vehicle_info'] = 'infos véhicule',
+ ['pick_lock'] = 'crocheter véhicule',
+ ['vehicle_unlocked'] = 'véhicule ~g~déverouillé~s~',
+ ['no_vehicles_nearby'] = 'aucun véhicule à proximité',
+ ['impound'] = 'véhicule en fourrière',
+ ['impound_prompt'] = 'appuyez sur ~INPUT_CONTEXT~ pour annuler la ~y~saisie du véhicule~s~',
+ ['impound_canceled'] = 'vous avez annulé la saisie',
+ ['impound_canceled_moved'] = 'la saisie a été annulée parce que le véhicule a déménagé',
+ ['impound_successful'] = 'vous avez saisi le véhicule',
+ ['search_database'] = 'vehicle information',
+ ['search_database_title'] = 'informations sur le véhicule - recherche avec numéro d\'enregistrement',
+ ['search_database_error_invalid'] = 'Ce n\'est ~r~pas~s~ un ~y~numéro d\'enregistrement valide~s~',
+ -- Traffic interaction
+ ['traffic_interaction'] = 'interaction routière',
+ ['cone'] = 'plot',
+ ['barrier'] = 'barrière',
+ ['spikestrips'] = 'herse',
+ ['box'] = 'caisse',
+ ['cash'] = 'caisse',
+ -- ID Card Menu
+ ['name'] = 'nom: %s',
+ ['job'] = 'métier: %s',
+ ['sex'] = 'sexe: %s',
+ ['dob'] = 'DOB: %s',
+ ['height'] = 'taille: %s',
+ ['bac'] = 'BAC: %s',
+ ['unknown'] = 'inconnu',
+ ['male'] = 'homme',
+ ['female'] = 'femme',
+ -- Body Search Menu
+ ['guns_label'] = '--- Armes ---',
+ ['inventory_label'] = '--- Inventaire ---',
+ ['license_label'] = ' --- Licenses ---',
+ ['confiscate'] = 'confisquer %s',
+ ['confiscate_weapon'] = 'confisqué %s avec %s balles',
+ ['confiscate_inv'] = 'confisquer %sx %s',
+ ['confiscate_dirty'] = 'confisquer argent sale: €%s',
+ ['you_confiscated'] = 'vous avez confisqué ~y~%sx~s~ ~b~%s~s~ à ~b~%s~s~',
+ ['got_confiscated'] = '~y~%sx~s~ ~b~%s~s~ ont été confisqués par ~y~%s~s~',
+ ['you_confiscated_account'] = 'vous avez confisqué ~g~$%s~s~ (%s) à ~b~%s~s~',
+ ['got_confiscated_account'] = '~g~$%s~s~ (%s) ont été confisqués par ~y~%s~s~',
+ ['you_confiscated_weapon'] = 'vous avez confisqué ~b~%s~s~ à ~b~%s~s~ avec ~o~%s~s~ balles',
+ ['got_confiscated_weapon'] = 'votre ~b~%s~s~ avec ~o~%s~s~ balles a été confisqué par ~y~%s~s~',
+ ['traffic_offense'] = 'code de la route',
+ ['minor_offense'] = 'délit mineur',
+ ['average_offense'] = 'délit moyen',
+ ['major_offense'] = 'délit grave',
+ ['fine_total'] = 'amende: %s',
+ -- Vehicle Info Menu
+ ['plate'] = 'n°: %s',
+ ['owner_unknown'] = 'propriétaire: Inconnu',
+ ['owner'] = 'propriétaire: %s',
+ -- Boss Menu
+ ['open_bossmenu'] = 'appuyez sur ~INPUT_CONTEXT~ pour ouvrir le menu',
+ ['quantity_invalid'] = 'quantité invalide',
+ ['have_withdrawn'] = 'vous avez retiré ~y~%sx~s~ ~b~%s~s~',
+ ['have_deposited'] = 'vous avez déposé ~y~%sx~s~ ~b~%s~s~',
+ ['quantity'] = 'quantité',
+ ['inventory'] = 'inventaire',
+ ['police_stock'] = 'coffre de la police',
+ -- Misc
+ ['remove_prop'] = 'appuyez sur ~INPUT_CONTEXT~ pour enlever l\'objet',
+ ['map_blip'] = 'Commissariat',
+ ['unrestrained_timer'] = 'vous sentez que vos menottes deviennent fragiles.',
+ -- Notifications
+ ['alert_police'] = 'alerte police',
+ ['phone_police'] = 'police',
+}
diff --git a/modules/job_police/data/locales/ko.lua b/modules/job_police/data/locales/ko.lua
new file mode 100644
index 00000000..29ef383d
--- /dev/null
+++ b/modules/job_police/data/locales/ko.lua
@@ -0,0 +1,151 @@
+Translations = {
+ -- Cloakroom
+ ['cloakroom'] = '라커룸',
+ ['citizen_wear'] = '민간인 복장',
+ ['police_wear'] = '경찰 복장',
+ ['gilet_wear'] = '오렌지 반사 재킷',
+ ['bullet_wear'] = '방탄 조끼',
+ ['no_outfit'] = '당신에게 맞는 유니폼이 없습니다!',
+ ['open_cloackroom'] = '~y~옷~s~을 변경하려면 ~INPUT_CONTEXT~를 누르십시오.',
+ -- Armory
+ ['remove_object'] = '오브젝트를 빼다',
+ ['deposit_object'] = '오브젝트를 두다',
+ ['get_weapon'] = '병기고에서 무기를 빼다',
+ ['put_weapon'] = '병기고에 무기를 넣다',
+ ['buy_weapons'] = '무기 구매',
+ ['armory'] = '병기고',
+ ['open_armory'] = '~INPUT_CONTEXT~를 눌러 ~y~병기고~s~에 접근하십시오.',
+ ['armory_owned'] = '소유',
+ ['armory_free'] = '자유',
+ ['armory_item'] = '$%s',
+ ['armory_weapontitle'] = '병기고 - 무기 구매',
+ ['armory_componenttitle'] = '병기고 - 무기 부착물',
+ ['armory_bought'] = '당신은 ~g~$%s~s~에 ~y~%s~s~를 구매했습니다',
+ ['armory_money'] = '당신은 이 무기를 살 수 없습니다',
+ ['armory_hascomponent'] = '이 부착물이 장착되어 있습니다!',
+ ['get_weapon_menu'] = '병기고 - 무기 빼다',
+ ['put_weapon_menu'] = '병기고 - 무기 넣다',
+ -- Vehicles
+ ['vehicle_menu'] = '차량',
+ ['vehicle_blocked'] = '모든 스폰 지점이 차단되었습니다!',
+ ['garage_prompt'] = '~INPUT_CONTEXT~를 눌러 ~y~차량 동작~s~에 액세스하십시오.',
+ ['garage_title'] = '차량 행동',
+ ['garage_stored'] = '저장됨',
+ ['garage_notstored'] = '차고가 아님',
+ ['garage_storing'] = '차량을 제거하려고 합니다. 주변에 아무도 없는지 확인하십시오.',
+ ['garage_has_stored'] = '차량이 차고에 보관되었습니다',
+ ['garage_has_notstored'] = '차고에 차량이 없습니다',
+ ['garage_notavailable'] = '차량이 차고에 저장되어 있지 않습니다',
+ ['garage_blocked'] = '차고 출구가 막혔습니다!',
+ ['garage_empty'] = '차고에 차량이 없습니다.',
+ ['garage_released'] = '차량이 출시되었습니다',
+ ['garage_store_nearby'] = '주변에 차량이 없습니다.',
+ ['garage_storeditem'] = '차고 열기',
+ ['garage_storeitem'] = '차량 정리',
+ ['garage_buyitem'] = '차량 상점',
+ ['garage_notauthorized'] = 'you\'re not authorized to buy this kind of vehicles.',
+ ['helicopter_prompt'] = '~y~헬리콥터 동작~s~에 액세스하려면 ~INPUT_CONTEXT~를 누릅니다.',
+ ['shop_item'] = '$%s',
+ ['vehicleshop_title'] = '차량 판매점',
+ ['vehicleshop_confirm'] = '이 차량을 사고 싶습니까?',
+ ['vehicleshop_bought'] = '당신은 ~r~$%s~s~에 ~y~%s~s~를 구매했습니다',
+ ['vehicleshop_money'] = '이 차량을 살 수 없습니다',
+ ['vehicleshop_awaiting_model'] = '차량이 현재 ~g~다운로드 및 로딩중입니다~s~. 잠시만 기다려주십시오',
+ ['confirm_no'] = '아니요',
+ ['confirm_yes'] = '예',
+ -- Service
+ ['service_max'] = '서비스에 진입 할 수 없습니다. 최대 임원 : %s/%s',
+ ['service_not'] = '당신은 서비스를 입력하지 않았습니다! 먼저 변경해야합니다.',
+ ['service_anonunce'] = '서비스 정보',
+ ['service_in'] = '당신은 서비스에 들어갔다, 환영합니다!',
+ ['service_in_announce'] = '~y~%s~s~가 서비스에 들어 왔습니다!',
+ ['service_out'] = '당신은 서비스를 떠났습니다.',
+ ['service_out_announce'] = '~y~%s~s~가 서비스를 떠났습니다.',
+ -- Action Menu
+ ['citizen_interaction'] = '시민 상호 작용',
+ ['vehicle_interaction'] = '차량 상호 작용',
+ ['object_spawner'] = '오브젝트 Spawner',
+
+ ['id_card'] = '신분증',
+ ['search'] = '수색',
+ ['handcuff'] = '수갑을 채우다 / 수갑을 풀어주다',
+ ['drag'] = '호위',
+ ['put_in_vehicle'] = '차량에 넣다',
+ ['out_the_vehicle'] = '차량을 끌다',
+ ['fine'] = '벌금',
+ ['unpaid_bills'] = '미지급 벌금 관리',
+ ['license_check'] = '라이선스 관리',
+ ['license_revoke'] = '라이선스 취소',
+ ['license_revoked'] = '~b~%s~s~는 ~y~취소~s~입니다!',
+ ['licence_you_revoked'] = '~y~%s~s~에 속하는 ~b~%s~s~를 취소했습니다.',
+ ['no_players_nearby'] = '근처에 플레이어가 없습니다!',
+ ['being_searched'] = '~b~경찰~s~이 당신을 ~y~수색~s~',
+ -- Vehicle interaction
+ ['vehicle_info'] = '차량 정보',
+ ['pick_lock'] = '차량 락픽',
+ ['vehicle_unlocked'] = '차량 ~g~잠금 해제~s~',
+ ['no_vehicles_nearby'] = '근처에 차량이 없습니다',
+ ['impound'] = '차량을 견인하다',
+ ['impound_prompt'] = '~y~견인~s~을 취소하려면 ~INPUT_CONTEXT~를 누릅니다',
+ ['impound_canceled'] = '당신은 견인을 취소했습니다',
+ ['impound_canceled_moved'] = '차량이 움직였기 때문에 견인이 취소되었습니다',
+ ['impound_successful'] = '당신은 차량을 견인했습니다',
+ ['search_database'] = '차량 정보',
+ ['search_database_title'] = '차량 정보 - 등록번호로 검색',
+ ['search_database_error_invalid'] = '~y~유효한~s~ 등록 번호가 ~r~아닙니다.~s~',
+ -- Traffic interaction
+ ['traffic_interaction'] = '교통 상호 작용',
+ ['cone'] = '원뿔',
+ ['barrier'] = '장벽',
+ ['spikestrips'] = '스파이크 스트립',
+ ['box'] = '상자',
+ ['cash'] = '현금 상자',
+ -- ID Card Menu
+ ['name'] = '이름: %s',
+ ['job'] = '직업: %s',
+ ['sex'] = '성별: %s',
+ ['dob'] = '생일: %s',
+ ['height'] = '키: %s',
+ ['bac'] = '혈중 알코올 농도: %s',
+ ['unknown'] = '알 수 없는',
+ ['male'] = '남자',
+ ['female'] = '여자',
+ -- Body Search Menu
+ ['guns_label'] = '--- 총 ---',
+ ['inventory_label'] = '--- 인벤토리 ---',
+ ['license_label'] = ' --- 라이선스 ---',
+ ['confiscate'] = '%s 을 압수',
+ ['confiscate_weapon'] = '%s 와 %s 총알을 압수',
+ ['confiscate_inv'] = '%sx %s 을 압수',
+ ['confiscate_dirty'] = '더러운 돈을 압수: $%s',
+ ['you_confiscated'] = '당신은 ~y~%sx~s~ ~b~%s~s~을 ~b~%s~s~에게서 압수했습니다',
+ ['got_confiscated'] = '~y~%sx~s~ ~b~%s~s~이(가) ~y~%s~s~에 압수되었습니다',
+ ['you_confiscated_account'] = '당신은 ~g~$%s~s~ (%s) 를 ~b~%s~s~ 에게서 압수했습니다.',
+ ['got_confiscated_account'] = '~g~$%s~s~ (%s) 이(가) ~y~%s~s~에 압수되었습니다',
+ ['you_confiscated_weapon'] = '당신은 ~b~%s~s~을 ~b~%s~s~에게서 압수하고 ~o~%s~s~ 총알도 압수했습니다.',
+ ['got_confiscated_weapon'] = '당신의 ~b~%s~s~와 ~o~%s~s~ 총알이 ~y~%s~s~에 의해 압수되었습니다.',
+ ['traffic_offense'] = '교통 위반',
+ ['minor_offense'] = '경범죄',
+ ['average_offense'] = '일반 범죄',
+ ['major_offense'] = '심각한 범죄',
+ ['fine_total'] = '벌금: %s',
+ -- Vehicle Info Menu
+ ['plate'] = '번호판: %s',
+ ['owner_unknown'] = '소유자: 알 수 없는',
+ ['owner'] = '소유자: %s',
+ -- Boss Menu
+ ['open_bossmenu'] = '~INPUT_CONTEXT~를 눌러 메뉴를 엽니다.',
+ ['quantity_invalid'] = '유효하지 않은 수량',
+ ['have_withdrawn'] = '당신은 ~y~%sx~s~ ~b~%s~s~ 뺐다 ',
+ ['have_deposited'] = '당신은 ~y~%sx~s~ ~b~%s~s~ 넣었다',
+ ['quantity'] = '수량',
+ ['inventory'] = '인벤토리',
+ ['police_stock'] = '경찰 자리',
+ -- Misc
+ ['remove_prop'] = '~INPUT_CONTEXT~를 눌러 오브젝트를 삭제하십시오.',
+ ['map_blip'] = '경찰서',
+ ['unrestrained_timer'] = '당신의 수갑이 천천히 풀리는 것을 느낍니다.',
+ -- Notifications
+ ['alert_police'] = '경찰 경보',
+ ['phone_police'] = '경찰',
+}
diff --git a/modules/job_police/data/locales/pl.lua b/modules/job_police/data/locales/pl.lua
new file mode 100644
index 00000000..f85d4fa3
--- /dev/null
+++ b/modules/job_police/data/locales/pl.lua
@@ -0,0 +1,151 @@
+Translations = {
+ -- Cloakroom
+ ['cloakroom'] = 'szatnia',
+ ['citizen_wear'] = 'ubranie Cywilne',
+ ['police_wear'] = 'mundur',
+ ['gilet_wear'] = 'kamizelka odblaskowa',
+ ['bullet_wear'] = 'kamizelka kuloodporna',
+ ['no_outfit'] = 'brak ubrania',
+ ['open_cloackroom'] = 'naciśnij ~INPUT_CONTEXT~ aby zmienić ~y~ubranie~s~.',
+ -- Armory
+ ['remove_object'] = 'weź przedmiot',
+ ['deposit_object'] = 'zdeponuj przedmiot',
+ ['get_weapon'] = 'weź broń',
+ ['put_weapon'] = 'odłóż broń',
+ ['buy_weapons'] = 'kup Broń',
+ ['armory'] = 'zbrojownia',
+ ['open_armory'] = 'naciśnij ~INPUT_CONTEXT~ żeby uzyskać dostęp do zbrojowni',
+ ['armory_owned'] = 'owned',
+ ['armory_free'] = 'free',
+ ['armory_item'] = '$%s',
+ ['armory_weapontitle'] = 'armory - Buy weapon',
+ ['armory_componenttitle'] = 'armory - Weapon attatchments',
+ ['armory_bought'] = 'you bought an ~y~%s~s~ for ~g~$%s~s~',
+ ['armory_money'] = 'you cannot afford that weapon',
+ ['armory_hascomponent'] = 'you have that attatchment equiped!',
+ ['get_weapon_menu'] = 'armory - Withdraw Weapon',
+ ['put_weapon_menu'] = 'armory - Store Weapon',
+ -- Vehicles
+ ['vehicle_menu'] = 'vehicle',
+ ['vehicle_blocked'] = 'all available spawn points are currently blocked!',
+ ['garage_prompt'] = 'press ~INPUT_CONTEXT~ to access the ~y~Vehicle Actions~s~.',
+ ['garage_title'] = 'vehicle Actions',
+ ['garage_stored'] = 'stored',
+ ['garage_notstored'] = 'not in garage',
+ ['garage_storing'] = 'we\'re attempting to remove the vehicle, make sure no players are around it.',
+ ['garage_has_stored'] = 'the vehicle has been stored in your garage',
+ ['garage_has_notstored'] = 'no nearby owned vehicles were found',
+ ['garage_notavailable'] = 'your vehicle is not stored in the garage.',
+ ['garage_blocked'] = 'there\'s no available spawn points!',
+ ['garage_empty'] = 'you dont have any vehicles in your garage.',
+ ['garage_released'] = 'your vehicle has been released from the garage.',
+ ['garage_store_nearby'] = 'there is no nearby vehicles.',
+ ['garage_storeditem'] = 'open garage',
+ ['garage_storeitem'] = 'store vehicle in garage',
+ ['garage_buyitem'] = 'vehicle shop',
+ ['garage_notauthorized'] = 'you\'re not authorized to buy this kind of vehicles.',
+ ['helicopter_prompt'] = 'press ~INPUT_CONTEXT~ to access the ~y~Helicopter Actions~s~.',
+ ['shop_item'] = '$%s',
+ ['vehicleshop_title'] = 'vehicle Shop',
+ ['vehicleshop_confirm'] = 'do you want to buy this vehicle?',
+ ['vehicleshop_bought'] = 'you have bought ~y~%s~s~ for ~r~$%s~s~',
+ ['vehicleshop_money'] = 'you cannot afford that vehicle',
+ ['vehicleshop_awaiting_model'] = 'the vehicle is currently ~g~DOWNLOADING & LOADING~s~ please wait',
+ ['confirm_no'] = 'no',
+ ['confirm_yes'] = 'yes',
+ -- Service
+ ['service_max'] = 'nie możesz wejść do służby, maksymalna liczba oficerów w służbie: %s/%s',
+ ['service_not'] = 'nie rozpoczynasz służby! Najpierw musisz się przebrać.',
+ ['service_anonunce'] = 'informacje o służbie',
+ ['service_in'] = 'rozpoczynasz służbe, Witaj!',
+ ['service_in_announce'] = 'operator ~y~%s~s~ rozpoczyna służbę!',
+ ['service_out'] = 'opuszczasz służbę.',
+ ['service_out_announce'] = 'operator ~y~%s~s~ opuszcza służbe.',
+ -- Action Menu
+ ['citizen_interaction'] = 'interakcja z cywilami',
+ ['vehicle_interaction'] = 'interakcja z pojazdami',
+ ['object_spawner'] = 'przedmioty do postawienia',
+
+ ['id_card'] = 'dowód osobisty',
+ ['search'] = 'przeszukaj',
+ ['handcuff'] = 'zakuj/Rozkuj Kajdanki ',
+ ['drag'] = 'przemieść podejrzanego',
+ ['put_in_vehicle'] = 'wsadź do pojazdu',
+ ['out_the_vehicle'] = 'wyciągnij z pojazdu',
+ ['fine'] = 'mandaty',
+ ['unpaid_bills'] = 'zarządzaj niezapłaconymi rachunkami',
+ ['license_check'] = 'zarządzaj licencjami',
+ ['license_revoke'] = 'unieważnij licencje',
+ ['license_revoked'] = 'twoja licencja ~b~%s~s~ została ~y~unieważniona~s~!',
+ ['licence_you_revoked'] = 'unieważniasz ~b~%s~s~ które należały do ~y~%s~s~',
+ ['no_players_nearby'] = 'brak graczy w pobliżu',
+ ['being_searched'] = 'you are being ~y~searched~s~ by the ~b~Police~s~',
+ -- Vehicle interaction
+ ['vehicle_info'] = 'informacje o pojeździe',
+ ['pick_lock'] = 'odblokuj pojazd',
+ ['vehicle_unlocked'] = 'pojazd ~g~Odblokowany~s~',
+ ['no_vehicles_nearby'] = 'brak pojazdu w pobliżu',
+ ['impound'] = 'zajmij pojazd',
+ ['impound_prompt'] = 'naciśnij ~INPUT_CONTEXT~ żeby unieważnić ~y~zajęcie~s~',
+ ['impound_canceled'] = 'unieważniasz zajęcie',
+ ['impound_canceled_moved'] = 'zajęcie zostało anulowane, ponieważ pojazd przemieścił się',
+ ['impound_successful'] = 'zajmujesz pojazd',
+ ['search_database'] = 'informacje o pojeździe',
+ ['search_database_title'] = 'informacjeo pojeździe - przeszukaj używając numeru rejestracyjnego',
+ ['search_database_error_invalid'] = 'to ~r~nie~s~ jest ~y~poprawny~s~ rnumer rejestracyjny',
+ -- Traffic interaction
+ ['traffic_interaction'] = 'interakcje dla ruchu drogowego',
+ ['cone'] = 'pachołek',
+ ['barrier'] = 'barierka',
+ ['spikestrips'] = 'kolczatka',
+ ['box'] = 'pudła',
+ ['cash'] = 'paleta z pieniędzmi',
+ -- ID Card Menu
+ ['name'] = 'name: %s',
+ ['job'] = 'praca: %s',
+ ['sex'] = 'płeć: %s',
+ ['dob'] = 'data urodzenia: %s',
+ ['height'] = 'wzrost: %s',
+ ['bac'] = 'BAC: %s',
+ ['unknown'] = 'nieznany',
+ ['male'] = 'mężczyzna',
+ ['female'] = 'kobieta',
+ -- Body Search Menu
+ ['guns_label'] = '--- Bronie ---',
+ ['inventory_label'] = '--- Ekwipunek ---',
+ ['license_label'] = ' --- Licenses ---',
+ ['confiscate'] = 'skonfiskuj %s',
+ ['confiscate_weapon'] = 'skonfiskuj %s z %s kulami',
+ ['confiscate_inv'] = 'skonfiskuj %s x %s',
+ ['confiscate_dirty'] = 'skonfiskuj brudne pieniądze: $%s',
+ ['you_confiscated'] = 'skonfiskowałeś ~y~%sx~s~ ~b~%s~s~ od ~b~%s~s~',
+ ['got_confiscated'] = '~y~%sx~s~ ~b~%s~s~ zostały skonfiskowane przez ~y~%s~s~',
+ ['you_confiscated_account'] = 'skonfiskowałeś ~g~%s$~s~ (%s) od ~b~%s~s~',
+ ['got_confiscated_account'] = '~g~%s$~s~ (%s) zostały skonfiskowane przez ~y~%s~s~',
+ ['you_confiscated_weapon'] = 'skonfiskowałeś ~b~%s~s~ od ~b~%s~s~ z ~o~%s~s~ pociskami',
+ ['got_confiscated_weapon'] = 'twój ~b~%s~s~ z ~o~%s~s~ kulami został skonfiskowany przez ~y~%s~s~',
+ ['traffic_offense'] = 'wykroczenia drogowe',
+ ['minor_offense'] = 'niewielkie wykroczenia',
+ ['average_offense'] = 'średnie wykroczenia',
+ ['major_offense'] = 'duże wykroczenia',
+ ['fine_total'] = 'mandat: %s',
+ -- Vehicle Info Menu
+ ['plate'] = 'tablica rejestracyjna: %s',
+ ['owner_unknown'] = 'właściciel: Nieznany',
+ ['owner'] = 'właściciel: %s',
+ -- Boss Menu
+ ['open_bossmenu'] = 'naciśnij ~INPUT_CONTEXT~ aby otworzyć menu',
+ ['quantity_invalid'] = 'nieprawidłowa ilość',
+ ['have_withdrawn'] = 'wyjmujesz z depozytu ~y~%sx~s~ ~b~%s~s~',
+ ['have_deposited'] = 'zdeponowano ~y~%sx~s~ ~b~%s~s~',
+ ['quantity'] = 'ilość',
+ ['inventory'] = 'ekwipunek',
+ ['police_stock'] = 'zapasy policji',
+ -- Misc
+ ['remove_prop'] = 'naciśnij ~INPUT_CONTEXT~ aby usunąć ten obiekt',
+ ['map_blip'] = 'komisariat Policji',
+ ['unrestrained_timer'] = 'czujesz, że twoje kajdanki powoli tracą przyczepność i znikają.',
+ -- Notifications
+ ['alert_police'] = 'ostrzeż policję',
+ ['phone_police'] = 'police',
+}
diff --git a/modules/job_police/data/locales/sv.lua b/modules/job_police/data/locales/sv.lua
new file mode 100644
index 00000000..e1ac59d1
--- /dev/null
+++ b/modules/job_police/data/locales/sv.lua
@@ -0,0 +1,151 @@
+Translations = {
+ -- Cloakroom
+ ['cloakroom'] = 'omklädningsrum',
+ ['citizen_wear'] = 'civila kläder',
+ ['police_wear'] = 'polisuniform',
+ ['gilet_wear'] = 'reflektiv väst',
+ ['bullet_wear'] = 'skottsäker väst',
+ ['no_outfit'] = 'det finns ingen uniform som passar dig!',
+ ['open_cloackroom'] = 'tryck ~INPUT_CONTEXT~ för att välja ~y~kläder~s~.',
+ -- Armory
+ ['remove_object'] = 'ta ut objekt',
+ ['deposit_object'] = 'lägg in objekt',
+ ['get_weapon'] = 'ta ut vapen',
+ ['put_weapon'] = 'lägg in vapen',
+ ['buy_weapons'] = 'köp vapen',
+ ['armory'] = 'vapenförråd',
+ ['open_armory'] = 'tryck ~INPUT_CONTEXT~ för att komma åt ~y~vapenförrådet~s~.',
+ ['armory_owned'] = 'ägd',
+ ['armory_free'] = 'gratis',
+ ['armory_item'] = '%s SEK',
+ ['armory_weapontitle'] = 'vapenförråd - Köp vapen',
+ ['armory_componenttitle'] = 'vapenförråd - Vapen tillbehör',
+ ['armory_bought'] = 'du köpte ~y~%s~s~ för ~g~%s SEK~s~',
+ ['armory_money'] = 'du har inte råd med det vapnet',
+ ['armory_hascomponent'] = 'du har redan det tillbehöret!',
+ ['get_weapon_menu'] = 'vapenförråd - Ta ut vapen',
+ ['put_weapon_menu'] = 'vapenförråd - Spara vapen',
+ -- Vehicles
+ ['vehicle_menu'] = 'fordon',
+ ['vehicle_blocked'] = 'det finns ingen tillgänglig plats att ställa ut fordonet på!',
+ ['garage_prompt'] = 'tryck ~INPUT_CONTEXT~ för att komma åt ~y~garaget~s~.',
+ ['garage_title'] = 'garage',
+ ['garage_stored'] = 'inställt',
+ ['garage_notstored'] = 'uttaget',
+ ['garage_storing'] = 'vi håller på att ställa in ditt fordon, se till att ingen är i närheten.',
+ ['garage_has_stored'] = 'fordonet har ställts in i garaget',
+ ['garage_has_notstored'] = 'inget ägt fordon finns i närheten',
+ ['garage_notavailable'] = 'ditt fordon är inte inställt i ditt garage.',
+ ['garage_blocked'] = 'det finns ingen tillgänglig plats att ställa ut fordonet på!',
+ ['garage_empty'] = 'du har inga fordon i ditt garage.',
+ ['garage_released'] = 'ditt fordon har tagits ut från garaget.',
+ ['garage_store_nearby'] = 'det finns inget fordon i närheten.',
+ ['garage_storeditem'] = 'öppna garage',
+ ['garage_storeitem'] = 'ställ in fordon',
+ ['garage_buyitem'] = 'fordonshandel',
+ ['garage_notauthorized'] = 'du har inte tillgång till att köpa dessa fordon.',
+ ['helicopter_prompt'] = 'tryck ~INPUT_CONTEXT~ för att komma åt ~y~helikoptergaraget~s~.',
+ ['shop_item'] = '%s SEK',
+ ['vehicleshop_title'] = 'fordonshandel',
+ ['vehicleshop_confirm'] = 'vill du köpa detta fordon?',
+ ['vehicleshop_bought'] = 'du köpte ~y~%s~s~ för ~g~%s SEK~s~',
+ ['vehicleshop_money'] = 'du har inte råd med detta fordon',
+ ['vehicleshop_awaiting_model'] = 'fordonet ~g~LADDAS NED OCH LADDAS IN~s~ var god vänta',
+ ['confirm_no'] = 'nej',
+ ['confirm_yes'] = 'ja',
+ -- Service
+ ['service_max'] = 'du kan inte gå tjänst, poliser i tjänst: %s/%s',
+ ['service_not'] = 'du är inte i tjänst! Byt om för att gå in i tjänst.',
+ ['service_anonunce'] = 'tjänsteinformation',
+ ['service_in'] = 'du deltog i tjänst, välkommen!',
+ ['service_in_announce'] = 'operatör ~y~%s~s~ har deltagit tjänst!',
+ ['service_out'] = 'du har lämnat tjänst.',
+ ['service_out_announce'] = 'operatör ~y~%s~s~ har lämnat deras tjänst.',
+ -- Action Menu
+ ['citizen_interaction'] = 'handlingar mot civila',
+ ['vehicle_interaction'] = 'handlingar på bilar',
+ ['object_spawner'] = 'ta fram ett objekt',
+
+ ['id_card'] = 'ID-Kort',
+ ['search'] = 'sök igenom',
+ ['handcuff'] = 'handbojor',
+ ['drag'] = 'dra',
+ ['put_in_vehicle'] = 'sätt in i fordon',
+ ['out_the_vehicle'] = 'dra ut ur fordon',
+ ['fine'] = 'Ge böter',
+ ['unpaid_bills'] = 'se obetalda räkningar',
+ ['license_check'] = 'se licenser',
+ ['license_revoke'] = 'återkalla licenser',
+ ['license_revoked'] = 'ditt ~b~%s~s~ har blivit ~y~återkallat~s~!',
+ ['licence_you_revoked'] = 'du återkallade ett ~b~%s~s~ som tillhörde ~y~%s~s~',
+ ['no_players_nearby'] = 'det finns ingen i närheten',
+ ['being_searched'] = 'du blir ~y~visiterad~s~ av ~b~Polisen~s~',
+ -- Vehicle interaction
+ ['vehicle_info'] = 'fordon',
+ ['pick_lock'] = 'bryt upp fordon',
+ ['vehicle_unlocked'] = 'fordonet har ~g~låsts upp~s~',
+ ['no_vehicles_nearby'] = 'inga fordon i närheten',
+ ['impound'] = 'bärga fordonet',
+ ['impound_prompt'] = 'tryck ~INPUT_CONTEXT~ för att avbryta ~y~bärgningen~s~',
+ ['impound_canceled'] = 'du avbröt bärgningen',
+ ['impound_canceled_moved'] = 'bärgningen avbröts på grund av att fordonet har rört sig',
+ ['impound_successful'] = 'du har bärgat fordonet',
+ ['search_database'] = 'fordonsuppgifter',
+ ['search_database_title'] = 'fordonsuppgifter - sök med registreringsnummer',
+ ['search_database_error_invalid'] = 'det är ~r~inte~s~ ett ~y~giltigt~s~ registreringsnummer',
+ -- Traffic interaction
+ ['traffic_interaction'] = 'trafiksåtgärder',
+ ['cone'] = 'kon',
+ ['barrier'] = 'barriär',
+ ['spikestrips'] = 'spikmatta',
+ ['box'] = 'låda',
+ ['cash'] = 'låda med pengar',
+ -- ID Card Menu
+ ['name'] = 'namn: %s',
+ ['job'] = 'jobb: %s',
+ ['sex'] = 'kön: %s',
+ ['dob'] = 'födelsedatum: %s',
+ ['height'] = 'längd: %s',
+ ['bac'] = 'alkohol i blodet: %s',
+ ['unknown'] = 'okänt',
+ ['male'] = 'man',
+ ['female'] = 'kvinna',
+ -- Body Search Menu
+ ['guns_label'] = '--- Vapen ---',
+ ['inventory_label'] = '--- Inventory ---',
+ ['license_label'] = ' --- Licenser ---',
+ ['confiscate'] = 'beslagta %s',
+ ['confiscate_weapon'] = 'beslagta %s med %s skott',
+ ['confiscate_inv'] = 'beslagta %sx %s',
+ ['confiscate_dirty'] = 'beslagta svarta pengar: %s SEK',
+ ['you_confiscated'] = 'du beslagtog ~y~%sx~s~ ~b~%s~s~ från ~b~%s~s~',
+ ['got_confiscated'] = '~y~%sx~s~ ~b~%s~s~ beslagtogs av ~y~%s~s~',
+ ['you_confiscated_account'] = 'du beslagtog ~g~%s SEK~s~ (%s) från ~b~%s~s~',
+ ['got_confiscated_account'] = '~g~%s SEK~s~ (%s) beslagtogs av ~y~%s~s~',
+ ['you_confiscated_weapon'] = 'du beslagtog ~b~%s~s~ från ~b~%s~s~ med ~o~%s~s~ skott',
+ ['got_confiscated_weapon'] = 'din ~b~%s~s~ med ~o~%s~s~ skott beslagtogs av ~y~%s~s~',
+ ['traffic_offense'] = 'brott mot trafikregler',
+ ['minor_offense'] = 'mindre lagbrott',
+ ['average_offense'] = 'medel lagbrott',
+ ['major_offense'] = 'grovt lagbrott',
+ ['fine_total'] = 'böter: %s',
+ -- Vehicle Info Menu
+ ['plate'] = 'reg nummer: %s',
+ ['owner_unknown'] = 'ägare: Okänt',
+ ['owner'] = 'ägare: %s',
+ -- Boss Menu
+ ['open_bossmenu'] = 'tryck ~INPUT_CONTEXT~ för att öppna menyn',
+ ['quantity_invalid'] = 'otillgängligt antal',
+ ['have_withdrawn'] = 'du har tagit ut ~y~x%s~s~ ~b~%s~s~',
+ ['have_deposited'] = 'du har lagrat ~y~x%s~s~ ~b~%s~s~',
+ ['quantity'] = 'antal',
+ ['inventory'] = 'förråd',
+ ['police_stock'] = 'polisförråd',
+ -- Misc
+ ['remove_prop'] = 'tryck ~INPUT_CONTEXT~ för att ta bort objektet',
+ ['map_blip'] = 'polisstation',
+ ['unrestrained_timer'] = 'dina handklovar har försvunnit',
+ -- Notifications
+ ['alert_police'] = 'meddela polisen',
+ ['phone_police'] = 'polisen',
+}
diff --git a/modules/job_police/server/events.lua b/modules/job_police/server/events.lua
new file mode 100644
index 00000000..4b2d0d87
--- /dev/null
+++ b/modules/job_police/server/events.lua
@@ -0,0 +1,515 @@
+local self = ESX.Modules['job_police']
+
+RegisterNetEvent('esx_policejob:confiscatePlayerItem')
+AddEventHandler('esx_policejob:confiscatePlayerItem', function(target, itemType, itemName, amount)
+ local _source = source
+ local sourceXPlayer = ESX.GetPlayerFromId(_source)
+ local targetXPlayer = ESX.GetPlayerFromId(target)
+
+ if sourceXPlayer.job.name ~= 'police' then
+ print(('esx_policejob: %s attempted to confiscate!'):format(xPlayer.identifier))
+ return
+ end
+
+ if itemType == 'item_standard' then
+ local targetItem = targetXPlayer.getInventoryItem(itemName)
+ local sourceItem = sourceXPlayer.getInventoryItem(itemName)
+
+ -- does the target player have enough in their inventory?
+ if targetItem.count > 0 and targetItem.count <= amount then
+
+ -- can the player carry the said amount of x item?
+ if sourceXPlayer.canCarryItem(itemName, sourceItem.count) then
+ targetXPlayer.removeInventoryItem(itemName, amount)
+ sourceXPlayer.addInventoryItem (itemName, amount)
+ sourceXPlayer.showNotification(_U('you_confiscated', amount, sourceItem.label, targetXPlayer.name))
+ targetXPlayer.showNotification(_U('got_confiscated', amount, sourceItem.label, sourceXPlayer.name))
+ else
+ sourceXPlayer.showNotification(_U('quantity_invalid'))
+ end
+ else
+ sourceXPlayer.showNotification(_U('quantity_invalid'))
+ end
+
+ elseif itemType == 'item_account' then
+ targetXPlayer.removeAccountMoney(itemName, amount)
+ sourceXPlayer.addAccountMoney (itemName, amount)
+
+ sourceXPlayer.showNotification(_U('you_confiscated_account', amount, itemName, targetXPlayer.name))
+ targetXPlayer.showNotification(_U('got_confiscated_account', amount, itemName, sourceXPlayer.name))
+
+ elseif itemType == 'item_weapon' then
+ if amount == nil then amount = 0 end
+ targetXPlayer.removeWeapon(itemName, amount)
+ sourceXPlayer.addWeapon (itemName, amount)
+
+ sourceXPlayer.showNotification(_U('you_confiscated_weapon', ESX.GetWeaponLabel(itemName), targetXPlayer.name, amount))
+ targetXPlayer.showNotification(_U('got_confiscated_weapon', ESX.GetWeaponLabel(itemName), amount, sourceXPlayer.name))
+ end
+end)
+
+RegisterNetEvent('esx_policejob:handcuff')
+AddEventHandler('esx_policejob:handcuff', function(target)
+ local xPlayer = ESX.GetPlayerFromId(source)
+
+ if xPlayer.job.name == 'police' then
+ TriggerClientEvent('esx_policejob:handcuff', target)
+ else
+ print(('esx_policejob: %s attempted to handcuff a player (not cop)!'):format(xPlayer.identifier))
+ end
+end)
+
+RegisterNetEvent('esx_policejob:drag')
+AddEventHandler('esx_policejob:drag', function(target)
+ local xPlayer = ESX.GetPlayerFromId(source)
+
+ if xPlayer.job.name == 'police' then
+ TriggerClientEvent('esx_policejob:drag', target, source)
+ else
+ print(('esx_policejob: %s attempted to drag (not cop)!'):format(xPlayer.identifier))
+ end
+end)
+
+RegisterNetEvent('esx_policejob:putInVehicle')
+AddEventHandler('esx_policejob:putInVehicle', function(target)
+ local xPlayer = ESX.GetPlayerFromId(source)
+
+ if xPlayer.job.name == 'police' then
+ TriggerClientEvent('esx_policejob:putInVehicle', target)
+ else
+ print(('esx_policejob: %s attempted to put in vehicle (not cop)!'):format(xPlayer.identifier))
+ end
+end)
+
+RegisterNetEvent('esx_policejob:OutVehicle')
+AddEventHandler('esx_policejob:OutVehicle', function(target)
+ local xPlayer = ESX.GetPlayerFromId(source)
+
+ if xPlayer.job.name == 'police' then
+ TriggerClientEvent('esx_policejob:OutVehicle', target)
+ else
+ print(('esx_policejob: %s attempted to drag out from vehicle (not cop)!'):format(xPlayer.identifier))
+ end
+end)
+
+RegisterNetEvent('esx_policejob:getStockItem')
+AddEventHandler('esx_policejob:getStockItem', function(itemName, count)
+ local _source = source
+ local xPlayer = ESX.GetPlayerFromId(_source)
+
+ TriggerEvent('esx_addoninventory:getSharedInventory', 'society_police', function(inventory)
+ local inventoryItem = inventory.getItem(itemName)
+
+ -- is there enough in the society?
+ if count > 0 and inventoryItem.count >= count then
+
+ -- can the player carry the said amount of x item?
+ if xPlayer.canCarryItem(itemName, count) then
+ inventory.removeItem(itemName, count)
+ xPlayer.addInventoryItem(itemName, count)
+ xPlayer.showNotification(_U('have_withdrawn', count, inventoryItem.label))
+ else
+ xPlayer.showNotification(_U('quantity_invalid'))
+ end
+ else
+ xPlayer.showNotification(_U('quantity_invalid'))
+ end
+ end)
+end)
+
+RegisterNetEvent('esx_policejob:putStockItems')
+AddEventHandler('esx_policejob:putStockItems', function(itemName, count)
+ local xPlayer = ESX.GetPlayerFromId(source)
+ local sourceItem = xPlayer.getInventoryItem(itemName)
+
+ TriggerEvent('esx_addoninventory:getSharedInventory', 'society_police', function(inventory)
+ local inventoryItem = inventory.getItem(itemName)
+
+ -- does the player have enough of the item?
+ if sourceItem.count >= count and count > 0 then
+ xPlayer.removeInventoryItem(itemName, count)
+ inventory.addItem(itemName, count)
+ xPlayer.showNotification(_U('have_deposited', count, inventoryItem.label))
+ else
+ xPlayer.showNotification(_U('quantity_invalid'))
+ end
+ end)
+end)
+
+ESX.RegisterServerCallback('esx_policejob:getOtherPlayerData', function(source, cb, target, notify)
+ local xPlayer = ESX.GetPlayerFromId(target)
+
+ if notify then
+ xPlayer.showNotification(_U('being_searched'))
+ end
+
+ if xPlayer then
+ local data = {
+ name = xPlayer.getName(),
+ job = xPlayer.job.label,
+ grade = xPlayer.job.grade_label,
+ inventory = xPlayer.getInventory(),
+ accounts = xPlayer.getAccounts(),
+ weapons = xPlayer.getLoadout()
+ }
+
+ if self.Config.EnableESXIdentity then
+ data.dob = xPlayer.get('dateofbirth')
+ data.height = xPlayer.get('height')
+
+ if xPlayer.get('sex') == 'm' then data.sex = 'male' else data.sex = 'female' end
+ end
+
+ TriggerEvent('esx_status:getStatus', target, 'drunk', function(status)
+ if status then
+ data.drunk = ESX.Math.Round(status.percent)
+ end
+
+ if self.Config.EnableLicenses then
+ TriggerEvent('esx_license:getLicenses', target, function(licenses)
+ data.licenses = licenses
+ cb(data)
+ end)
+ else
+ cb(data)
+ end
+ end)
+ end
+end)
+
+ESX.RegisterServerCallback('esx_policejob:getFineList', function(source, cb, category)
+ MySQL.Async.fetchAll('SELECT * FROM fine_types WHERE category = @category', {
+ ['@category'] = category
+ }, function(fines)
+ cb(fines)
+ end)
+end)
+
+ESX.RegisterServerCallback('esx_policejob:getVehicleInfos', function(source, cb, plate)
+ MySQL.Async.fetchAll('SELECT owner FROM owned_vehicles WHERE plate = @plate', {
+ ['@plate'] = plate
+ }, function(result)
+ local retrivedInfo = {plate = plate}
+
+ if result[1] then
+ local xPlayer = ESX.GetPlayerFromIdentifier(result[1].owner)
+
+ -- is the owner online?
+ if xPlayer then
+ retrivedInfo.owner = xPlayer.getName()
+ cb(retrivedInfo)
+ elseif self.Config.EnableESXIdentity then
+ MySQL.Async.fetchAll('SELECT firstname, lastname FROM users WHERE identifier = @identifier', {
+ ['@identifier'] = result[1].owner
+ }, function(result2)
+ if result2[1] then
+ retrivedInfo.owner = ('%s %s'):format(result2[1].firstname, result2[1].lastname)
+ cb(retrivedInfo)
+ else
+ cb(retrivedInfo)
+ end
+ end)
+ else
+ cb(retrivedInfo)
+ end
+ else
+ cb(retrivedInfo)
+ end
+ end)
+end)
+
+ESX.RegisterServerCallback('esx_policejob:getArmoryWeapons', function(source, cb)
+ TriggerEvent('esx_datastore:getSharedDataStore', 'society_police', function(store)
+ local weapons = store.get('weapons')
+
+ if weapons == nil then
+ weapons = {}
+ end
+
+ cb(weapons)
+ end)
+end)
+
+ESX.RegisterServerCallback('esx_policejob:addArmoryWeapon', function(source, cb, weaponName, removeWeapon)
+ local xPlayer = ESX.GetPlayerFromId(source)
+
+ if removeWeapon then
+ xPlayer.removeWeapon(weaponName)
+ end
+
+ TriggerEvent('esx_datastore:getSharedDataStore', 'society_police', function(store)
+ local weapons = store.get('weapons') or {}
+ local foundWeapon = false
+
+ for i=1, #weapons, 1 do
+ if weapons[i].name == weaponName then
+ weapons[i].count = weapons[i].count + 1
+ foundWeapon = true
+ break
+ end
+ end
+
+ if not foundWeapon then
+ table.insert(weapons, {
+ name = weaponName,
+ count = 1
+ })
+ end
+
+ store.set('weapons', weapons)
+ cb()
+ end)
+end)
+
+ESX.RegisterServerCallback('esx_policejob:removeArmoryWeapon', function(source, cb, weaponName)
+ local xPlayer = ESX.GetPlayerFromId(source)
+ xPlayer.addWeapon(weaponName, 500)
+
+ TriggerEvent('esx_datastore:getSharedDataStore', 'society_police', function(store)
+ local weapons = store.get('weapons') or {}
+
+ local foundWeapon = false
+
+ for i=1, #weapons, 1 do
+ if weapons[i].name == weaponName then
+ weapons[i].count = (weapons[i].count > 0 and weapons[i].count - 1 or 0)
+ foundWeapon = true
+ break
+ end
+ end
+
+ if not foundWeapon then
+ table.insert(weapons, {
+ name = weaponName,
+ count = 0
+ })
+ end
+
+ store.set('weapons', weapons)
+ cb()
+ end)
+end)
+
+ESX.RegisterServerCallback('esx_policejob:buyWeapon', function(source, cb, weaponName, type, componentNum)
+
+ local xPlayer = ESX.GetPlayerFromId(source)
+ local authorizedWeapons = {}
+
+ for i=1, xPlayer.job.grade, 1 do
+
+ local weapons = self.Config.AuthorizedWeapons[i]
+
+ for j=1, #weapons, 1 do
+ authorizedWeapons[#authorizedWeapons + 1] = weapons[j]
+ end
+
+ end
+
+ for k,v in ipairs(authorizedWeapons) do
+ if v.weapon == weaponName then
+ selectedWeapon = v
+ break
+ end
+ end
+
+ if not selectedWeapon then
+ print(('esx_policejob: %s attempted to buy an invalid weapon.'):format(xPlayer.identifier))
+ cb(false)
+ else
+
+ TriggerEvent('esx_addonaccount:getSharedAccount', 'society_police', function(account)
+
+ -- Weapon
+ if type == 1 then
+
+ if account.money >= selectedWeapon.price then
+
+ account.removeMoney(selectedWeapon.price)
+ xPlayer.addWeapon(weaponName, 100)
+
+ cb(true)
+
+ else
+ cb(false)
+ end
+
+ -- Weapon Component
+ elseif type == 2 then
+ local price = selectedWeapon.components[componentNum]
+ local weaponNum, weapon = ESX.GetWeapon(weaponName)
+ local component = weapon.components[componentNum]
+
+ if component then
+ if account.money >= price then
+
+ account.removeMoney(price)
+ xPlayer.addWeaponComponent(weaponName, component.name)
+
+ cb(true)
+
+ else
+ cb(false)
+ end
+ else
+ print(('esx_policejob: %s attempted to buy an invalid weapon component.'):format(xPlayer.identifier))
+ cb(false)
+ end
+ end
+
+ end)
+
+ end
+end)
+
+ESX.RegisterServerCallback('esx_policejob:canBuyVehicle', function(source, cb, model, type)
+
+ local xPlayer = ESX.GetPlayerFromId(source)
+ local price = self.GetPriceFromHash(GetHashKey(model), xPlayer.job.grade, type)
+
+ -- vehicle model not found
+ if price == -1 then
+ cb(false)
+ else
+
+ TriggerEvent('esx_addonaccount:getSharedAccount', 'society_police', function(account)
+
+ if account.money >= price then
+ cb(true)
+ else
+ cb(false)
+ end
+
+ end)
+
+ end
+
+end)
+
+RegisterNetEvent('esx_policejob:buyJobVehicle')
+AddEventHandler('esx_policejob:buyJobVehicle', function(vehicleProps, type)
+
+ local _source = source
+ local xPlayer = ESX.GetPlayerFromId(_source)
+ local price = self.GetPriceFromHash(vehicleProps.model, xPlayer.job.grade, type)
+
+ -- vehicle model not found
+ if price == -1 then
+ print(('esx_policejob: %s attempted to exploit the shop! (invalid vehicle model)'):format(xPlayer.identifier))
+ else
+
+ TriggerEvent('esx_addonaccount:getSharedAccount', 'society_police', function(account)
+
+ if account.money >= price then
+
+ account.removeMoney(price)
+
+ MySQL.Async.execute('INSERT INTO owned_vehicles (owner, vehicle, plate, type, job, `stored`) VALUES (@owner, @vehicle, @plate, @type, @job, @stored)', {
+ ['@owner'] = xPlayer.identifier,
+ ['@vehicle'] = json.encode(vehicleProps),
+ ['@plate'] = vehicleProps.plate,
+ ['@type'] = type,
+ ['@job'] = xPlayer.job.name,
+ ['@stored'] = true
+ })
+
+ else
+ print(('esx_policejob: %s attempted to exploit the shop! (not enough money)'):format(xPlayer.identifier))
+ end
+
+ end)
+
+ end
+
+end)
+
+ESX.RegisterServerCallback('esx_policejob:storeNearbyVehicle', function(source, cb, nearbyVehicles)
+ local xPlayer = ESX.GetPlayerFromId(source)
+ local foundPlate, foundNum
+
+ for k,v in ipairs(nearbyVehicles) do
+ local result = MySQL.Sync.fetchAll('SELECT plate FROM owned_vehicles WHERE owner = @owner AND plate = @plate AND job = @job', {
+ ['@owner'] = xPlayer.identifier,
+ ['@plate'] = v.plate,
+ ['@job'] = xPlayer.job.name
+ })
+
+ if result[1] then
+ foundPlate, foundNum = result[1].plate, k
+ break
+ end
+ end
+
+ if not foundPlate then
+ cb(false)
+ else
+ MySQL.Async.execute('UPDATE owned_vehicles SET `stored` = true WHERE owner = @owner AND plate = @plate AND job = @job', {
+ ['@owner'] = xPlayer.identifier,
+ ['@plate'] = foundPlate,
+ ['@job'] = xPlayer.job.name
+ }, function (rowsChanged)
+ if rowsChanged == 0 then
+ print(('esx_policejob: %s has exploited the garage!'):format(xPlayer.identifier))
+ cb(false)
+ else
+ cb(true, foundNum)
+ end
+ end)
+ end
+end)
+
+ESX.RegisterServerCallback('esx_policejob:getStockItems', function(source, cb)
+ TriggerEvent('esx_addoninventory:getSharedInventory', 'society_police', function(inventory)
+ cb(inventory.items)
+ end)
+end)
+
+ESX.RegisterServerCallback('esx_policejob:getPlayerInventory', function(source, cb)
+ local xPlayer = ESX.GetPlayerFromId(source)
+ local items = xPlayer.inventory
+
+ cb({items = items})
+end)
+
+AddEventHandler('playerDropped', function()
+ -- Save the source in case we lose it (which happens a lot)
+ local playerId = source
+
+ -- Did the player ever join?
+ if playerId then
+ local xPlayer = ESX.GetPlayerFromId(playerId)
+
+ -- Is it worth telling all clients to refresh?
+ if xPlayer and xPlayer.job.name == 'police' then
+ Citizen.Wait(5000)
+ TriggerClientEvent('esx_policejob:updateBlip', -1)
+ end
+ end
+end)
+
+RegisterNetEvent('esx_policejob:spawned')
+AddEventHandler('esx_policejob:spawned', function()
+ local xPlayer = ESX.GetPlayerFromId(playerId)
+
+ if xPlayer and xPlayer.job.name == 'police' then
+ Citizen.Wait(5000)
+ TriggerClientEvent('esx_policejob:updateBlip', -1)
+ end
+end)
+
+RegisterNetEvent('esx_policejob:forceBlip')
+AddEventHandler('esx_policejob:forceBlip', function()
+ TriggerClientEvent('esx_policejob:updateBlip', -1)
+end)
+
+AddEventHandler('onResourceStart', function(resource)
+ if resource == GetCurrentResourceName() then
+ Citizen.Wait(5000)
+ TriggerClientEvent('esx_policejob:updateBlip', -1)
+ end
+end)
+
+AddEventHandler('onResourceStop', function(resource)
+ if resource == GetCurrentResourceName() then
+ TriggerEvent('esx_phone:removeNumber', 'police')
+ end
+end)
diff --git a/modules/job_police/server/main.lua b/modules/job_police/server/main.lua
new file mode 100644
index 00000000..449ba44d
--- /dev/null
+++ b/modules/job_police/server/main.lua
@@ -0,0 +1,15 @@
+local self = ESX.Modules['job_police']
+
+if self.Config.EnableESXService then
+ TriggerEvent('esx_service:activateService', 'police', self.Config.MaxInService)
+end
+
+AddEventHandler('onResourceStart', function(resource)
+
+ if resource == 'esx_society' then
+ TriggerEvent('esx_society:registerSociety', 'police', 'Police', 'society_police', 'society_police', 'society_police', {type = 'public'})
+ elseif resource == 'esx_phone' then
+ TriggerEvent('esx_phone:registerNumber', 'police', _U('alert_police'), true, true)
+ end
+
+end)
diff --git a/modules/job_police/server/module.lua b/modules/job_police/server/module.lua
new file mode 100644
index 00000000..2c8a7409
--- /dev/null
+++ b/modules/job_police/server/module.lua
@@ -0,0 +1,31 @@
+ESX.Modules['job_police'] = {};
+local self = ESX.Modules['job_police']
+
+-- Properties
+self.Config = ESX.EvalFile(GetCurrentResourceName(), 'modules/job_police/data/config.lua', {
+ vector3 = vector3
+})['Config']
+
+self.GetPriceFromHash = function(vehicleHash, jobGrade, type)
+
+ local authorizedVehicles = {}
+
+ for i=1, jobGrade, 1 do
+
+ local vehicles = self.Config.AuthorizedVehicles[type][i]
+
+ for j=1, #vehicles, 1 do
+ authorizedVehicles[#authorizedVehicles + 1] = vehicles[j]
+ end
+
+ end
+
+ for k,v in ipairs(authorizedVehicles) do
+ if GetHashKey(v.model) == vehicleHash then
+ return v.price
+ end
+ end
+
+ return -1
+
+end
diff --git a/modules/menu_default/client/events.lua b/modules/menu_default/client/events.lua
new file mode 100644
index 00000000..7d8347e7
--- /dev/null
+++ b/modules/menu_default/client/events.lua
@@ -0,0 +1,71 @@
+local self = ESX.Modules['menu_default']
+
+AddEventHandler('esx:nui_ready', function()
+ ESX.CreateFrame('menu_default', 'nui://' .. GetCurrentResourceName() .. '/modules/menu_default/data/html/ui.html')
+end)
+
+ESX.Modules['input'].On('pressed', 0, 18, function(lastTime)
+ if (GetGameTimer() - lastTime) < 150 then return end
+ ESX.SendFrameMessage('menu_default', {action = 'controlPressed', control = 'ENTER'})
+end)
+
+ESX.Modules['input'].On('pressed', 0, 177, function(lastTime)
+ if (GetGameTimer() - lastTime) < 150 then return end
+ ESX.SendFrameMessage('menu_default', {action = 'controlPressed', control = 'BACKSPACE'})
+end)
+
+ESX.Modules['input'].On('pressed', 0, 27, function(lastTime)
+ if (GetGameTimer() - lastTime) < 300 then return end
+ ESX.SendFrameMessage('menu_default', {action = 'controlPressed', control = 'TOP'})
+end)
+
+ESX.Modules['input'].On('pressed', 0, 173, function(lastTime)
+ if (GetGameTimer() - lastTime) < 300 then return end
+ ESX.SendFrameMessage('menu_default', {action = 'controlPressed', control = 'DOWN'})
+end)
+
+ESX.Modules['input'].On('pressed', 0, 174, function(lastTime)
+ if (GetGameTimer() - lastTime) < 300 then return end
+ ESX.SendFrameMessage('menu_default', {action = 'controlPressed', control = 'LEFT'})
+end)
+
+ESX.Modules['input'].On('pressed', 0, 175, function(lastTime)
+ if (GetGameTimer() - lastTime) < 300 then return end
+ ESX.SendFrameMessage('menu_default', {action = 'controlPressed', control = 'RIGHT'})
+end)
+
+
+AddEventHandler('menu_default:message:menu_submit', function(data)
+ local menu = ESX.UI.Menu.GetOpened(self.MenuType, data._namespace, data._name)
+
+ if menu.submit ~= nil then
+ menu.submit(data, menu)
+ end
+end)
+
+AddEventHandler('menu_default:message:menu_cancel', function(data)
+
+ local menu = ESX.UI.Menu.GetOpened(self.MenuType, data._namespace, data._name)
+
+ if menu.cancel ~= nil then
+ menu.cancel(data, menu)
+ end
+end)
+
+AddEventHandler('menu_default:message:menu_change', function(data)
+ local menu = ESX.UI.Menu.GetOpened(self.MenuType, data._namespace, data._name)
+
+ for i=1, #data.elements, 1 do
+ menu.setElement(i, 'value', data.elements[i].value)
+
+ if data.elements[i].selected then
+ menu.setElement(i, 'selected', true)
+ else
+ menu.setElement(i, 'selected', false)
+ end
+ end
+
+ if menu.change ~= nil then
+ menu.change(data, menu)
+ end
+end)
diff --git a/modules/menu_default/client/main.lua b/modules/menu_default/client/main.lua
new file mode 100644
index 00000000..332fbded
--- /dev/null
+++ b/modules/menu_default/client/main.lua
@@ -0,0 +1,11 @@
+
+local self = ESX.Modules['menu_default']
+
+ESX.UI.Menu.RegisterType(self.MenuType, self.OpenMenu, self.CloseMenu)
+
+ESX.Modules['input'].RegisterControl(0, 18)
+ESX.Modules['input'].RegisterControl(0, 177)
+ESX.Modules['input'].RegisterControl(0, 27)
+ESX.Modules['input'].RegisterControl(0, 173)
+ESX.Modules['input'].RegisterControl(0, 174)
+ESX.Modules['input'].RegisterControl(0, 175)
diff --git a/modules/menu_default/client/module.lua b/modules/menu_default/client/module.lua
new file mode 100644
index 00000000..87355a79
--- /dev/null
+++ b/modules/menu_default/client/module.lua
@@ -0,0 +1,26 @@
+ESX.Modules['menu_default'] = {};
+local self = ESX.Modules['menu_default']
+
+self.GUI = {}
+self.GUI.Time = 0
+self.MenuType = 'default'
+
+self.OpenMenu = function(namespace, name, data)
+ ESX.SendFrameMessage('menu_default', {
+ action = 'openMenu',
+ namespace = namespace,
+ name = name,
+ data = data
+ })
+end
+
+self.CloseMenu = function(namespace, name)
+ ESX.SendFrameMessage('menu_default', {
+ action = 'closeMenu',
+ namespace = namespace,
+ name = name,
+ data = data
+ })
+end
+
+
diff --git a/modules/menu_default/data/html/css/app.css b/modules/menu_default/data/html/css/app.css
new file mode 100644
index 00000000..4ad1a0fb
--- /dev/null
+++ b/modules/menu_default/data/html/css/app.css
@@ -0,0 +1,94 @@
+@font-face {
+ font-family: bankgothic;
+ src: url('../fonts/bankgothic.ttf');
+}
+
+@font-face {
+ font-family: pcdown;
+ src: url('../fonts/pdown.ttf');
+}
+
+.menu {
+ font-family: bankgothic;
+ min-width: 400px;
+ color: #fff;
+ box-shadow: 0px 0px 50px 0px #000;
+ position: absolute;
+}
+
+.menu.align-left {
+ left: 40;
+ top: 50%;
+ transform: translate(0, -50%);
+}
+
+.menu.align-top-left {
+ left: 40;
+ top: 40;
+}
+
+.menu.align-top {
+ left: 50%;
+ top: 40;
+ transform: translate(-50%, 0);
+}
+
+.menu.align-top-right {
+ right: 10;
+ top: 40;
+}
+
+.menu.align-right {
+ right: 40;
+ top: 50%;
+ transform: translate(0, -50%);
+}
+
+.menu.align-bottom-right {
+ right: 40;
+ bottom: 40;
+}
+
+.menu.align-bottom {
+ left: 50%;
+ bottom: 40;
+ transform: translate(-50%, 0);
+}
+
+.menu.align-bottom-left {
+ left: 40;
+ bottom: 40;
+}
+
+.menu.align-center {
+ left: 50%;
+ top: 50%;
+ transform: translate(-50%, -50%);
+}
+
+.menu .head {
+ background-color: #506be6;
+ text-align: center;
+ height: 40px;
+ line-height: 40px;
+}
+
+.menu .menu-items {
+ max-height: 600px;
+ overflow-y: auto;
+}
+
+.menu .menu-items .menu-item {
+ height: 40px;
+ display: block;
+ background-color: #f1f1f1;
+ box-shadow: inset 1px 0px 0px 1px #b8b8b8;
+ height: 32px;
+ line-height: 32px;
+ color: #3A3A3A;
+ text-align: center;
+}
+
+.menu .menu-items .menu-item.selected {
+ background-color: #d0d0d0;
+}
diff --git a/modules/menu_default/data/html/fonts/bankgothic.ttf b/modules/menu_default/data/html/fonts/bankgothic.ttf
new file mode 100644
index 00000000..f3d8049b
Binary files /dev/null and b/modules/menu_default/data/html/fonts/bankgothic.ttf differ
diff --git a/modules/menu_default/data/html/fonts/pdown.ttf b/modules/menu_default/data/html/fonts/pdown.ttf
new file mode 100644
index 00000000..69c76cf3
Binary files /dev/null and b/modules/menu_default/data/html/fonts/pdown.ttf differ
diff --git a/modules/menu_default/data/html/js/app.js b/modules/menu_default/data/html/js/app.js
new file mode 100644
index 00000000..a397695f
--- /dev/null
+++ b/modules/menu_default/data/html/js/app.js
@@ -0,0 +1,346 @@
+(function(){
+ let MenuTpl =
+ '' +
+ ''
+ ;
+
+ window.ESX_MENU = {};
+ ESX_MENU.ResourceName = 'menu_default';
+ ESX_MENU.opened = {};
+ ESX_MENU.focus = [];
+ ESX_MENU.pos = {};
+
+ ESX_MENU.open = function(namespace, name, data) {
+ if (typeof ESX_MENU.opened[namespace] == 'undefined') {
+ ESX_MENU.opened[namespace] = {};
+ }
+
+ if (typeof ESX_MENU.opened[namespace][name] != 'undefined') {
+ ESX_MENU.close(namespace, name);
+ }
+
+ if (typeof ESX_MENU.pos[namespace] == 'undefined') {
+ ESX_MENU.pos[namespace] = {};
+ }
+
+ for (let i=0; i {
+ switch (data.action) {
+
+ case 'openMenu': {
+ ESX_MENU.open(data.namespace, data.name, data.data);
+ break;
+ }
+
+ case 'closeMenu': {
+ ESX_MENU.close(data.namespace, data.name);
+ break;
+ }
+
+ case 'controlPressed': {
+ switch (data.control) {
+
+ case 'ENTER': {
+ let focused = ESX_MENU.getFocused();
+
+ if (typeof focused != 'undefined') {
+ let menu = ESX_MENU.opened[focused.namespace][focused.name];
+ let pos = ESX_MENU.pos[focused.namespace][focused.name];
+ let elem = menu.elements[pos];
+
+ if (menu.elements.length > 0) {
+ ESX_MENU.submit(focused.namespace, focused.name, {...elem, index: pos + 1});
+ }
+ }
+
+ break;
+ }
+
+ case 'BACKSPACE': {
+ let focused = ESX_MENU.getFocused();
+
+ if (typeof focused != 'undefined') {
+ ESX_MENU.cancel(focused.namespace, focused.name);
+ }
+
+ break;
+ }
+
+ case 'TOP': {
+ let focused = ESX_MENU.getFocused();
+
+ if (typeof focused != 'undefined') {
+ let menu = ESX_MENU.opened[focused.namespace][focused.name];
+ let pos = ESX_MENU.pos[focused.namespace][focused.name];
+
+ if (pos > 0) {
+ ESX_MENU.pos[focused.namespace][focused.name]--;
+ } else {
+ ESX_MENU.pos[focused.namespace][focused.name] = menu.elements.length - 1;
+ }
+
+ let elem = menu.elements[ESX_MENU.pos[focused.namespace][focused.name]];
+
+ for (let i=0; i min) {
+ elem.value--;
+ ESX_MENU.change(focused.namespace, focused.name, elem);
+ }
+
+ ESX_MENU.render();
+ break;
+ }
+
+ default: break;
+ }
+
+ $('#menu_' + focused.namespace + '_' + focused.name).find('.menu-item.selected')[0].scrollIntoView();
+ }
+
+ break;
+ }
+
+ case 'RIGHT': {
+ let focused = ESX_MENU.getFocused();
+
+ if (typeof focused != 'undefined') {
+ let menu = ESX_MENU.opened[focused.namespace][focused.name];
+ let pos = ESX_MENU.pos[focused.namespace][focused.name];
+ let elem = menu.elements[pos];
+
+ switch(elem.type) {
+ case 'default': break;
+
+ case 'slider': {
+ if (typeof elem.options != 'undefined' && elem.value < elem.options.length - 1) {
+ elem.value++;
+ ESX_MENU.change(focused.namespace, focused.name, elem);
+ }
+
+ if (typeof elem.max != 'undefined' && elem.value < elem.max) {
+ elem.value++;
+ ESX_MENU.change(focused.namespace, focused.name, elem);
+ }
+
+ ESX_MENU.render();
+ break;
+ }
+
+ default: break;
+ }
+
+ $('#menu_' + focused.namespace + '_' + focused.name).find('.menu-item.selected')[0].scrollIntoView();
+ }
+
+ break;
+ }
+
+ default: break;
+ }
+
+ break;
+ }
+ }
+ };
+
+ window.onload = function(e){
+ window.addEventListener('message', (event) => {
+ onData(event.data);
+ });
+ };
+
+})();
diff --git a/modules/menu_default/data/html/js/mustache.min.js b/modules/menu_default/data/html/js/mustache.min.js
new file mode 100644
index 00000000..520cfcb9
--- /dev/null
+++ b/modules/menu_default/data/html/js/mustache.min.js
@@ -0,0 +1 @@
+(function defineMustache(global,factory){if(typeof exports==="object"&&exports&&typeof exports.nodeName!=="string"){factory(exports)}else if(typeof define==="function"&&define.amd){define(["exports"],factory)}else{global.Mustache={};factory(global.Mustache)}})(this,function mustacheFactory(mustache){var objectToString=Object.prototype.toString;var isArray=Array.isArray||function isArrayPolyfill(object){return objectToString.call(object)==="[object Array]"};function isFunction(object){return typeof object==="function"}function typeStr(obj){return isArray(obj)?"array":typeof obj}function escapeRegExp(string){return string.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g,"\\$&")}function hasProperty(obj,propName){return obj!=null&&typeof obj==="object"&&propName in obj}var regExpTest=RegExp.prototype.test;function testRegExp(re,string){return regExpTest.call(re,string)}var nonSpaceRe=/\S/;function isWhitespace(string){return!testRegExp(nonSpaceRe,string)}var entityMap={"&":"&","<":"<",">":">",'"':""","'":"'","/":"/","`":"`","=":"="};function escapeHtml(string){return String(string).replace(/[&<>"'`=\/]/g,function fromEntityMap(s){return entityMap[s]})}var whiteRe=/\s*/;var spaceRe=/\s+/;var equalsRe=/\s*=/;var curlyRe=/\s*\}/;var tagRe=/#|\^|\/|>|\{|&|=|!/;function parseTemplate(template,tags){if(!template)return[];var sections=[];var tokens=[];var spaces=[];var hasTag=false;var nonSpace=false;function stripSpace(){if(hasTag&&!nonSpace){while(spaces.length)delete tokens[spaces.pop()]}else{spaces=[]}hasTag=false;nonSpace=false}var openingTagRe,closingTagRe,closingCurlyRe;function compileTags(tagsToCompile){if(typeof tagsToCompile==="string")tagsToCompile=tagsToCompile.split(spaceRe,2);if(!isArray(tagsToCompile)||tagsToCompile.length!==2)throw new Error("Invalid tags: "+tagsToCompile);openingTagRe=new RegExp(escapeRegExp(tagsToCompile[0])+"\\s*");closingTagRe=new RegExp("\\s*"+escapeRegExp(tagsToCompile[1]));closingCurlyRe=new RegExp("\\s*"+escapeRegExp("}"+tagsToCompile[1]))}compileTags(tags||mustache.tags);var scanner=new Scanner(template);var start,type,value,chr,token,openSection;while(!scanner.eos()){start=scanner.pos;value=scanner.scanUntil(openingTagRe);if(value){for(var i=0,valueLength=value.length;i0?sections[sections.length-1][4]:nestedTokens;break;default:collector.push(token)}}return nestedTokens}function Scanner(string){this.string=string;this.tail=string;this.pos=0}Scanner.prototype.eos=function eos(){return this.tail===""};Scanner.prototype.scan=function scan(re){var match=this.tail.match(re);if(!match||match.index!==0)return"";var string=match[0];this.tail=this.tail.substring(string.length);this.pos+=string.length;return string};Scanner.prototype.scanUntil=function scanUntil(re){var index=this.tail.search(re),match;switch(index){case-1:match=this.tail;this.tail="";break;case 0:match="";break;default:match=this.tail.substring(0,index);this.tail=this.tail.substring(index)}this.pos+=match.length;return match};function Context(view,parentContext){this.view=view;this.cache={".":this.view};this.parent=parentContext}Context.prototype.push=function push(view){return new Context(view,this)};Context.prototype.lookup=function lookup(name){var cache=this.cache;var value;if(cache.hasOwnProperty(name)){value=cache[name]}else{var context=this,names,index,lookupHit=false;while(context){if(name.indexOf(".")>0){value=context.view;names=name.split(".");index=0;while(value!=null&&index")value=this.renderPartial(token,context,partials,originalTemplate);else if(symbol==="&")value=this.unescapedValue(token,context);else if(symbol==="name")value=this.escapedValue(token,context);else if(symbol==="text")value=this.rawValue(token);if(value!==undefined)buffer+=value}return buffer};Writer.prototype.renderSection=function renderSection(token,context,partials,originalTemplate){var self=this;var buffer="";var value=context.lookup(token[1]);function subRender(template){return self.render(template,context,partials)}if(!value)return;if(isArray(value)){for(var j=0,valueLength=value.length;j
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+