diff --git a/client/main.lua b/client/main.lua
index 476e200..0c2da39 100644
--- a/client/main.lua
+++ b/client/main.lua
@@ -1,48 +1,47 @@
+local Config = Config
local HasAlreadyEnteredMarker, IsInShopMenu = false, false
local CurrentAction, CurrentActionMsg, LastZone, currentDisplayVehicle, CurrentVehicleData
-local CurrentActionData, Vehicles, Categories = {}, {}, {}
-local VehiclesByModel = {}
-local vehiclesByCategory = {}
-
-function getVehicleFromModel(model)
- return VehiclesByModel[model]
-end
-
-function PlayerManagement()
- if not Config.EnablePlayerManagement then
- return
- end
-
- if ESX.PlayerData.job.name ~= 'cardealer' then
- Config.Zones.ShopEntering.Type = -1
- Config.Zones.BossActions.Type = -1
- Config.Zones.ResellVehicle.Type = -1
- return
- end
- Config.Zones.ShopEntering.Type = 1
-
- if ESX.PlayerData.job.grade_name == 'boss' then
- Config.Zones.BossActions.Type = 1
- end
-end
+local CurrentActionData, Vehicles, Categories, VehiclesByModel, vehiclesByCategory, soldVehicles, cardealerVehicles, rentedVehicles = {}, {}, {}, {}, {}, {}, {}, {}
+local DoesEntityExist, NetworkRequestControlOfEntity, NetworkHasControlOfEntity, DisableControlAction, HasModelLoaded, RequestModel, DisableAllControlActions, FreezeEntityPosition, SetEntityCoords, SetEntityVisible = DoesEntityExist, NetworkRequestControlOfEntity, NetworkHasControlOfEntity, DisableControlAction, HasModelLoaded, RequestModel, DisableAllControlActions, FreezeEntityPosition, SetEntityCoords, SetEntityVisible
+
+Vehicles = GlobalState.vehicleShop.vehicles
+Categories = GlobalState.vehicleShop.categories
+VehiclesByModel = GlobalState.vehicleShop.vehiclesByModel
+soldVehicles = GlobalState.vehicleShop.soldVehicles
+cardealerVehicles = GlobalState.vehicleShop.cardealerVehicles
+rentedVehicles = GlobalState.vehicleShop.rentedVehicles
+
+AddStateBagChangeHandler('vehicleShop', 'global', function(bagName, key, value)
+ Vehicles = value.vehicles
+ Categories = value.categories
+ VehiclesByModel = value.vehiclesByModel
+ soldVehicles = value.soldVehicles
+ cardealerVehicles = value.cardealerVehicles
+ rentedVehicles = value.rentedVehicles
+end)
-RegisterNetEvent('esx:playerLoaded')
-AddEventHandler('esx:playerLoaded', function(xPlayer)
- PlayerManagement()
- TriggerServerEvent("esx_vehicleshop:getVehiclesAndCategories")
+CreateThread(function()
+ while true do
+ Wait(60000)
+ collectgarbage("collect")
+ end
end)
-RegisterNetEvent('esx_vehicleshop:updateVehiclesAndCategories', function(vehicles, categories, vehiclesByModel)
- Vehicles = vehicles
- Categories = categories
+local function getVehicleFromModel(model)
+ return VehiclesByModel[model]
+end
- VehiclesByModel = vehiclesByModel
+local function Init()
+ TriggerEvent('esx_vehicleshop:updateTables')
+
+ Wait(500)
table.sort(Vehicles, function(a, b)
return a.name < b.name
end)
- for _, vehicle in ipairs(Vehicles) do
+ for i = 1, #Vehicles, 1 do
+ local vehicle = Vehicles[i]
if IsModelInCdimage(joaat(vehicle.model)) then
local category = vehicle.category
@@ -50,16 +49,81 @@ RegisterNetEvent('esx_vehicleshop:updateVehiclesAndCategories', function(vehicle
vehiclesByCategory[category] = {}
end
- table.insert(vehiclesByCategory[category], vehicle)
+ TableInsert(vehiclesByCategory[category], vehicle)
else
print(('[^3WARNING^7] Ignoring vehicle ^5%s^7 due to invalid Model'):format(vehicle.model))
end
end
+
+ if Config.EnablePlayerManagement then
+ RegisterNetEvent('esx_phone:loaded')
+ AddEventHandler('esx_phone:loaded', function(phoneNumber, contacts)
+ local specialContact = {
+ name = TranslateCap('dealership'),
+ number = 'cardealer',
+ base64Icon = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAAEACAMAAABrrFhUAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAMAUExURQAAADMzMzszM0M0M0w0M1Q1M101M2U2M242M3Y3M383Moc4MpA4Mpg5MqE5Mqk6MrI6Mro7Mrw8Mr89M71DML5EO8I+NMU/NcBMLshANctBNs5CN8RULMddKsheKs9YLtBCONZEOdlFOtxGO99HPNhMNsplKM1nKM1uJtRhLddiLt5kMNJwJ9B2JNR/IeNIPeVJPehKPuRQOuhSO+lZOOlhNuloM+p3Lep/KupwMMFORsVYUcplXc1waNJ7delUSepgVexrYe12bdeHH9iIH9qQHd2YG+udH+OEJeuGJ+uOJeuVIuChGeSpF+aqGOykHOysGeeyFeuzFuyzFuq6E+27FO+Cee3CEdaGgdqTjvCNhfKYkvOkngAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJezdycAAAEAdFJOU////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////wBT9wclAAAACXBIWXMAAA7DAAAOwwHHb6hkAAAAGHRFWHRTb2Z0d2FyZQBwYWludC5uZXQgNC4xLjb9TgnoAAAQGElEQVR4Xt2d+WMUtxXHbS6bEGMPMcQQ04aEUnqYo9xJWvC6kAKmQLM2rdn//9+g0uir2Tl0PElPszP7+cnH7Fj6rPTeG2lmvfKld2azk8lk/36L/cnkZDbDIT3Sp4DZ8QS9dTI57tNDTwJOOu+4j/0TvDQz+QXMSG+7mUn+sZBZQELnNROcKhMZBXx+gS4k8+IzTpmBXAJOnqPxTDzPFRKyCODuvSKPgwwC2EZ+lxf4E4xwCzhBU7PBPQx4BWR88+fwDgNGAbMsM9/Ec8bygE3A5966L3nOlhiZBGSf+l2YggGLgBna1DMsE4FBQH9zvw1HLEgX0Evkt5GeEVIFMFztpJF6rZQm4DNasVDSEkKSgIVN/ibP0ZwoEgQsfPTPSZgH8QIG8vYr4gdBrIABvf2K2EEQKWBQb78ichBECRhE8O8SlQ5iBAQvcffFPhoYQoSAAQ5/TcQ0CBYw0OGvCZ4GoQIGF/3bhGaDQAELvfKhERgIwgQMePrPCQsEQQLwFwYPmksiQMCC1n1iCFgooQtYwLJfPPQFQ7KAUfU/wABVwMj6TzdAFDDY6tcOMR3SBIyw/1QDJAGj7D/RAEXA6Oa/hhIHCAJG23+SAb+AEfefYsArYET1nwlvTegVgBONFnTDik8ATjNi0BEbHgGjuP5147k6dgsYaQHQxF0OOAUMfv2LhnOVzCVg4OufdFwrpS4BePkSgA6ZcAhYggCocQRCu4ClCIAaeyC0CliaAKCwhgGrALxwaUC3OtgELFEAUNjCgEXAklQAdSzVgEUAXrRUoGstzAKWbgJIzJPAKGAJJ4DEOAmMAvCCpQPda2ASsJQTQGKa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',
+ }
+
+ TriggerEvent('esx_phone:addSpecialContact', specialContact.name, specialContact.number, specialContact.base64Icon)
+ end)
+ end
+
+ if Config.Blip.show then
+ CreateThread(function()
+ local blip = AddBlipForCoord(Config.Zones.ShopEntering.Pos)
+
+ SetBlipSprite (blip, Config.Blip.Sprite)
+ SetBlipDisplay(blip, Config.Blip.Display)
+ SetBlipScale (blip, Config.Blip.Scale)
+ SetBlipAsShortRange(blip, true)
+
+ BeginTextCommandSetBlipName('STRING')
+ AddTextComponentSubstringPlayerName(TranslateCap('car_dealer'))
+ EndTextCommandSetBlipName(blip)
+ end)
+ end
+
+ return true
+end
+
+local function PlayerManagement()
+ if not Config.EnablePlayerManagement then
+ return true
+ end
+
+ if LocalPlayer.state.job ~= 'cardealer' then
+ Config.Zones.ShopEntering.Type = -1
+ Config.Zones.BossActions.Type = -1
+ Config.Zones.ResellVehicle.Type = -1
+ return true
+ end
+ Config.Zones.ShopEntering.Type = 1
+
+ if LocalPlayer.state.job.grade_name == 'boss' then
+ Config.Zones.BossActions.Type = 1
+ end
+ return true
+end
+
+local function loadIpl()
+ RequestIpl('shr_int')
+
+ local interiorID = 7170
+ PinInteriorInMemory(interiorID)
+ ActivateInteriorEntitySet(interiorID, 'csr_beforeMission')
+ RefreshInterior(interiorID)
+end
+
+RegisterNetEvent('esx:playerLoaded')
+AddEventHandler('esx:playerLoaded', function(xPlayer)
+ Init()
+ PlayerManagement()
+ CreateThread(loadIpl)
end)
RegisterNetEvent('esx:setJob', PlayerManagement)
-function DeleteDisplayVehicleInsideShop()
+local function DeleteDisplayVehicleInsideShop()
local attempt = 0
if currentDisplayVehicle and DoesEntityExist(currentDisplayVehicle) then
@@ -75,48 +139,64 @@ function DeleteDisplayVehicleInsideShop()
end
end
-function ReturnVehicleProvider()
- ESX.TriggerServerCallback('esx_vehicleshop:getCommercialVehicles', function(vehicles)
- local elements = {}
-
- for k, v in ipairs(vehicles) do
- local returnPrice = ESX.Math.Round(v.price * 0.75)
- local vehicleLabel = getVehicleFromModel(v.vehicle).label
+local function ReturnVehicleProvider()
+ local elements = {
+ {
+ unselectable = true,
+ icon = "fas fa-car",
+ title = TranslateCap('car_dealer'),
+ },
+ }
+
+ for k, v in ipairs(cardealerVehicles) do
+ local returnPrice = ESX.Math.Round(v.price * 0.75)
+ local vehicleLabel = getVehicleFromModel(v.vehicle).label
+
+ TableInsert(elements, {
+ title = ('%s [%s]'):format(vehicleLabel, TranslateCap('generic_shopitem', ESX.Math.GroupDigits(returnPrice))),
+ name = v.vehicle
+ })
+ end
- table.insert(elements, {
- label = ('%s [%s]'):format(vehicleLabel, TranslateCap('generic_shopitem', ESX.Math.GroupDigits(returnPrice))),
- value = v.vehicle
- })
- end
+ ESX.OpenContext("right", elements, function(menu, element)
+ if not element.name then return ESX.CloseContext() end
+ TriggerServerEvent('esx_vehicleshop:returnProvider', element.name)
+ Wait(500)
+ ESX.CloseContext()
+ ReturnVehicleProvider()
+ end, function(menu)
+ end)
+end
- ESX.UI.Menu.Open('default', GetCurrentResourceName(), 'return_provider_menu', {
- title = TranslateCap('return_provider_menu'),
- align = 'top-left',
- elements = elements
- }, function(data, menu)
- TriggerServerEvent('esx_vehicleshop:returnProvider', data.current.value)
+local function StartShopRestriction()
+ while IsInShopMenu do
+ Wait(0)
- Wait(300)
- menu.close()
- ReturnVehicleProvider()
- end, function(data, menu)
- menu.close()
- end)
- end)
+ DisableControlAction(0, 75, true) -- Disable exit vehicle
+ DisableControlAction(27, 75, true) -- Disable exit vehicle
+ end
end
-function StartShopRestriction()
- CreateThread(function()
- while IsInShopMenu do
- Wait(0)
+local function WaitForVehicleToLoad(modelHash)
+ modelHash = (type(modelHash) == 'number' and modelHash or joaat(modelHash))
+
+ if not HasModelLoaded(modelHash) then
+ RequestModel(modelHash)
- DisableControlAction(0, 75, true) -- Disable exit vehicle
- DisableControlAction(27, 75, true) -- Disable exit vehicle
+ BeginTextCommandBusyspinnerOn('STRING')
+ AddTextComponentSubstringPlayerName(TranslateCap('shop_awaiting_model'))
+ EndTextCommandBusyspinnerOn(4)
+
+ while not HasModelLoaded(modelHash) do
+ Wait(0)
+ DisableAllControlActions(0)
end
- end)
+
+ BusyspinnerOff()
+ end
end
-function OpenShopMenu()
+local function OpenShopMenu()
if #Vehicles == 0 then
print('[^3ERROR^7] Vehicleshop has ^50^7 vehicles, please add some!')
return
@@ -124,10 +204,11 @@ function OpenShopMenu()
IsInShopMenu = true
- StartShopRestriction()
+ CreateThread(StartShopRestriction)
ESX.UI.Menu.CloseAll()
+ ESX.CloseContext()
- local playerPed = PlayerPedId()
+ local playerPed = ESX.PlayerData.ped
FreezeEntityPosition(playerPed, true)
SetEntityVisible(playerPed, false)
@@ -140,8 +221,7 @@ function OpenShopMenu()
local category = Categories[i]
local categoryVehicles = vehiclesByCategory[category.name]
local options = {}
- if categoryVehicles == nil then goto continue end
-
+
for j=1, #categoryVehicles, 1 do
local vehicle = categoryVehicles[j]
@@ -149,12 +229,12 @@ function OpenShopMenu()
firstVehicleData = vehicle
end
- table.insert(options, ('%s %s'):format(vehicle.name, TranslateCap('generic_shopitem', ESX.Math.GroupDigits(vehicle.price))))
+ TableInsert(options, ('%s %s'):format(vehicle.name, TranslateCap('generic_shopitem', ESX.Math.GroupDigits(vehicle.price))))
end
table.sort(options)
- table.insert(elements, {
+ TableInsert(elements, {
name = category.name,
label = category.label,
value = 0,
@@ -162,7 +242,6 @@ function OpenShopMenu()
max = #Categories[i],
options = options
})
- ::continue::
end
ESX.UI.Menu.Open('default', GetCurrentResourceName(), 'vehicle_shop', {
@@ -190,7 +269,7 @@ function OpenShopMenu()
CurrentActionMsg = TranslateCap('shop_menu')
CurrentActionData = {}
- local playerPed = PlayerPedId()
+ local playerPed = ESX.PlayerData.ped
FreezeEntityPosition(playerPed, false)
SetEntityVisible(playerPed, true)
SetEntityCoords(playerPed, Config.Zones.ShopEntering.Pos)
@@ -227,7 +306,7 @@ function OpenShopMenu()
end, function(data, menu)
menu.close()
DeleteDisplayVehicleInsideShop()
- local playerPed = PlayerPedId()
+ local playerPed = ESX.PlayerData.ped
CurrentAction = 'shop_menu'
CurrentActionMsg = TranslateCap('shop_menu')
@@ -240,12 +319,12 @@ function OpenShopMenu()
IsInShopMenu = false
end, function(data, menu)
local vehicleData = vehiclesByCategory[data.current.name][data.current.value + 1]
- local playerPed = PlayerPedId()
+ local playerPed = ESX.PlayerData.ped
+ DeleteDisplayVehicleInsideShop()
WaitForVehicleToLoad(vehicleData.model)
ESX.Game.SpawnLocalVehicle(vehicleData.model, Config.Zones.ShopInside.Pos, Config.Zones.ShopInside.Heading, function(vehicle)
- DeleteDisplayVehicleInsideShop()
currentDisplayVehicle = vehicle
TaskWarpPedIntoVehicle(playerPed, vehicle, -1)
FreezeEntityPosition(vehicle, true)
@@ -253,10 +332,10 @@ function OpenShopMenu()
end)
end)
+ DeleteDisplayVehicleInsideShop()
WaitForVehicleToLoad(firstVehicleData.model)
ESX.Game.SpawnLocalVehicle(firstVehicleData.model, Config.Zones.ShopInside.Pos, Config.Zones.ShopInside.Heading, function(vehicle)
- DeleteDisplayVehicleInsideShop()
currentDisplayVehicle = vehicle
TaskWarpPedIntoVehicle(playerPed, vehicle, -1)
FreezeEntityPosition(vehicle, true)
@@ -264,44 +343,26 @@ function OpenShopMenu()
end)
end
-function WaitForVehicleToLoad(modelHash)
- modelHash = (type(modelHash) == 'number' and modelHash or joaat(modelHash))
-
- if not HasModelLoaded(modelHash) then
- RequestModel(modelHash)
-
- BeginTextCommandBusyspinnerOn('STRING')
- AddTextComponentSubstringPlayerName(TranslateCap('shop_awaiting_model'))
- EndTextCommandBusyspinnerOn(4)
-
- while not HasModelLoaded(modelHash) do
- Wait(0)
- DisableAllControlActions(0)
- end
-
- BusyspinnerOff()
- end
-end
-
function OpenResellerMenu()
ESX.UI.Menu.CloseAll()
-
- ESX.UI.Menu.Open('default', GetCurrentResourceName(), 'reseller', {
- title = TranslateCap('car_dealer'),
- align = 'top-left',
- elements = {
- {label = TranslateCap('buy_vehicle'), value = 'buy_vehicle'},
- {label = TranslateCap('pop_vehicle'), value = 'pop_vehicle'},
- {label = TranslateCap('depop_vehicle'), value = 'depop_vehicle'},
- {label = TranslateCap('return_provider'), value = 'return_provider'},
- {label = TranslateCap('create_bill'), value = 'create_bill'},
- {label = TranslateCap('get_rented_vehicles'), value = 'get_rented_vehicles'},
- {label = TranslateCap('set_vehicle_owner_sell'), value = 'set_vehicle_owner_sell'},
- {label = TranslateCap('set_vehicle_owner_rent'), value = 'set_vehicle_owner_rent'},
- {label = TranslateCap('deposit_stock'), value = 'put_stock'},
- {label = TranslateCap('take_stock'), value = 'get_stock'}
- }}, function(data, menu)
- local action = data.current.value
+ ESX.CloseContext()
+
+ local elements = {
+ {unselectable = true, icon = 'fas fa-car', title = TranslateCap('car_dealer')},
+ {title = TranslateCap('buy_vehicle'), name = 'buy_vehicle'},
+ {title = TranslateCap('pop_vehicle'), name = 'pop_vehicle'},
+ {title = TranslateCap('depop_vehicle'), name = 'depop_vehicle'},
+ {title = TranslateCap('return_provider'), name = 'return_provider'},
+ {title = TranslateCap('create_bill'), name = 'create_bill'},
+ {title = TranslateCap('get_rented_vehicles'), name = 'get_rented_vehicles'},
+ {title = TranslateCap('set_vehicle_owner_sell'), name = 'set_vehicle_owner_sell'},
+ {title = TranslateCap('set_vehicle_owner_rent'), name = 'set_vehicle_owner_rent'},
+ {title = TranslateCap('deposit_stock'), name = 'put_stock'},
+ {title = TranslateCap('take_stock'), name = 'get_stock'},
+ }
+
+ ESX.OpenContext('right', elements, function(menu, element)
+ local action = element.name
if Config.OxInventory and (action == 'put_stock' or action == 'get_stock') then
exports.ox_inventory:openInventory('stash', 'society_cardealer')
@@ -323,28 +384,22 @@ function OpenResellerMenu()
ReturnVehicleProvider()
elseif action == 'create_bill' then
local closestPlayer, closestDistance = ESX.Game.GetClosestPlayer()
-
if closestPlayer ~= -1 and closestDistance < 3 then
- ESX.UI.Menu.Open('dialog', GetCurrentResourceName(), 'set_vehicle_owner_sell_amount', {
- title = TranslateCap('invoice_amount')
- }, function(data2, menu2)
- local amount = tonumber(data2.value)
-
- if amount == nil then
- ESX.ShowNotification(TranslateCap('invalid_amount'))
- else
- menu2.close()
- local closestPlayer, closestDistance = ESX.Game.GetClosestPlayer()
-
- if closestPlayer == -1 or closestDistance > 3.0 then
- ESX.ShowNotification(TranslateCap('no_players'))
- else
- TriggerServerEvent('esx_billing:sendBill', GetPlayerServerId(closestPlayer), 'society_cardealer', TranslateCap('car_dealer'), tonumber(data2.value))
+ ESX.CloseContext()
+ ESX.OpenContext('right', {{title = TranslateCap('invoice_amount'), input = true, inputType = 'number', inputValue = 0, inputMin = 0, name = 'invoice_amount'}}, function(menu2, element2)
+ if element2.name == 'invoice_amount' then
+ local amount = tonumber(element2.inputValue)
+ if amount ~= nil then
+ ESX.CloseContext()
+ local closestPlayer, closestDistance = ESX.Game.GetClosestPlayer()
+ if closestPlayer == -1 or closestDistance > 3.0 then
+ ESX.ShowNotification(TranslateCap('no_players'))
+ else
+ TriggerServerEvent('esx_billing:sendBill', GetPlayerServerId(closestPlayer), 'society_cardealer', TranslateCap('car_dealer'), amount)
+ end
end
end
- end, function(data2, menu2)
- menu2.close()
- end)
+ end, function(menu) end)
else
ESX.ShowNotification(TranslateCap('no_players'))
end
@@ -403,9 +458,7 @@ function OpenResellerMenu()
ESX.ShowNotification(TranslateCap('no_current_vehicle'))
end
end
- end, function(data, menu)
- menu.close()
-
+ end, function(menu)
CurrentAction = 'reseller_menu'
CurrentActionMsg = TranslateCap('shop_menu')
CurrentActionData = {}
@@ -413,66 +466,62 @@ function OpenResellerMenu()
end
function OpenPopVehicleMenu()
- ESX.TriggerServerCallback('esx_vehicleshop:getCommercialVehicles', function(vehicles)
- local elements = {}
+ local elements = {}
- for k,v in ipairs(vehicles) do
- local vehicleLabel = getVehicleFromModel(v.vehicle).label
+ for k,v in ipairs(cardealerVehicles) do
+ local vehicleLabel = getVehicleFromModel(v.vehicle).label
- table.insert(elements, {
- label = ('%s [%s]'):format(vehicleLabel, TranslateCap('generic_shopitem', ESX.Math.GroupDigits(v.price))),
- value = v.vehicle
- })
- end
+ TableInsert(elements, {
+ label = ('%s [%s]'):format(vehicleLabel, TranslateCap('generic_shopitem', ESX.Math.GroupDigits(v.price))),
+ value = v.vehicle
+ })
+ end
- ESX.UI.Menu.Open('default', GetCurrentResourceName(), 'commercial_vehicles', {
- title = TranslateCap('vehicle_dealer'),
- align = 'top-left',
- elements = elements
- }, function(data, menu)
- local model = data.current.value
+ ESX.UI.Menu.Open('default', GetCurrentResourceName(), 'commercial_vehicles', {
+ title = TranslateCap('vehicle_dealer'),
+ align = 'top-left',
+ elements = elements
+ }, function(data, menu)
+ local model = data.current.value
+ DeleteDisplayVehicleInsideShop()
- ESX.Game.SpawnVehicle(model, Config.Zones.ShopInside.Pos, Config.Zones.ShopInside.Heading, function(vehicle)
- DeleteDisplayVehicleInsideShop()
- currentDisplayVehicle = vehicle
+ ESX.Game.SpawnVehicle(model, Config.Zones.ShopInside.Pos, Config.Zones.ShopInside.Heading, function(vehicle)
+ currentDisplayVehicle = vehicle
- for i=1, #Vehicles, 1 do
- if model == Vehicles[i].model then
- CurrentVehicleData = Vehicles[i]
- break
- end
+ for i=1, #Vehicles, 1 do
+ if model == Vehicles[i].model then
+ CurrentVehicleData = Vehicles[i]
+ break
end
- end)
- end, function(data, menu)
- menu.close()
+ end
end)
+ end, function(data, menu)
+ menu.close()
end)
end
function OpenRentedVehiclesMenu()
- ESX.TriggerServerCallback('esx_vehicleshop:getRentedVehicles', function(vehicles)
- local elements = {}
+ local elements = {}
- for k,v in ipairs(vehicles) do
- local vehicleLabel = getVehicleFromModel(v.name).label
+ for k,v in ipairs(rentedVehicles) do
+ local vehicleLabel = getVehicleFromModel(v.name).label
- table.insert(elements, {
- label = ('%s: %s - %s'):format(v.playerName, vehicleLabel, v.plate),
- value = v.name
- })
- end
+ TableInsert(elements, {
+ label = ('%s: %s - %s'):format(v.playerName, vehicleLabel, v.plate),
+ value = v.name
+ })
+ end
- ESX.UI.Menu.Open('default', GetCurrentResourceName(), 'rented_vehicles', {
- title = TranslateCap('rent_vehicle'),
- align = 'top-left',
- elements = elements
- }, nil, function(data, menu)
- menu.close()
- end)
+ ESX.UI.Menu.Open('default', GetCurrentResourceName(), 'rented_vehicles', {
+ title = TranslateCap('rent_vehicle'),
+ align = 'top-left',
+ elements = elements
+ }, nil, function(data, menu)
+ menu.close()
end)
end
-function OpenBossActionsMenu()
+local function OpenBossActionsMenu()
ESX.UI.Menu.CloseAll()
ESX.UI.Menu.Open('default', GetCurrentResourceName(), 'reseller',{
@@ -488,21 +537,20 @@ function OpenBossActionsMenu()
end)
elseif data.current.value == 'sold_vehicles' then
- ESX.TriggerServerCallback('esx_vehicleshop:getSoldVehicles', function(customers)
local elements = {
head = { TranslateCap('customer_client'), TranslateCap('customer_model'), TranslateCap('customer_plate'), TranslateCap('customer_soldby'), TranslateCap('customer_date') },
rows = {}
}
- for i=1, #customers, 1 do
- table.insert(elements.rows, {
- data = customers[i],
+ for i=1, #soldVehicles, 1 do
+ TableInsert(elements.rows, {
+ data = soldVehicles[i],
cols = {
- customers[i].client,
- customers[i].model,
- customers[i].plate,
- customers[i].soldby,
- customers[i].date
+ soldVehicles[i].client,
+ soldVehicles[i].model,
+ soldVehicles[i].plate,
+ soldVehicles[i].soldby,
+ soldVehicles[i].date
}
})
end
@@ -512,7 +560,6 @@ function OpenBossActionsMenu()
end, function(data2, menu2)
menu2.close()
end)
- end)
end
end, function(data, menu)
@@ -530,7 +577,7 @@ function OpenGetStocksMenu()
for i=1, #items, 1 do
if items[i].count > 0 then
- table.insert(elements, {
+ TableInsert(elements, {
label = 'x' .. items[i].count .. ' ' .. items[i].label,
value = items[i].name
})
@@ -574,7 +621,7 @@ function OpenPutStocksMenu()
local item = inventory.items[i]
if item.count > 0 then
- table.insert(elements, {
+ TableInsert(elements, {
label = item.label .. ' x' .. item.count,
type = 'item_standard',
value = item.name
@@ -611,20 +658,20 @@ function OpenPutStocksMenu()
end)
end
-function hasEnteredMarker(zone)
+local function hasEnteredMarker(zone)
if zone == 'ShopEntering' then
if not Config.EnablePlayerManagement then
CurrentAction = 'shop_menu'
CurrentActionMsg = TranslateCap('shop_menu')
CurrentActionData = {}
end
- if ESX.PlayerData.job ~= nil and ESX.PlayerData.job.name == 'cardealer' then
+ if LocalPlayer.state.job ~= nil and LocalPlayer.state.job.name == 'cardealer' then
CurrentAction = 'reseller_menu'
CurrentActionMsg = TranslateCap('shop_menu')
CurrentActionData = {}
end
elseif zone == 'GiveBackVehicle' and Config.EnablePlayerManagement then
- local playerPed = PlayerPedId()
+ local playerPed = ESX.PlayerData.ped
if IsPedInAnyVehicle(playerPed, false) then
local vehicle = GetVehiclePedIsIn(playerPed, false)
@@ -634,7 +681,7 @@ function hasEnteredMarker(zone)
CurrentActionData = {vehicle = vehicle}
end
elseif zone == 'ResellVehicle' then
- local playerPed = PlayerPedId()
+ local playerPed = ESX.PlayerData.ped
if IsPedSittingInAnyVehicle(playerPed) then
local vehicle = GetVehiclePedIsIn(playerPed, false)
@@ -669,71 +716,44 @@ function hasEnteredMarker(zone)
end
end
- elseif zone == 'BossActions' and Config.EnablePlayerManagement and ESX.PlayerData.job ~= nil and ESX.PlayerData.job.name == 'cardealer' and ESX.PlayerData.job.grade_name == 'boss' then
+ elseif zone == 'BossActions' and Config.EnablePlayerManagement and LocalPlayer.state.job ~= nil and LocalPlayer.state.job.name == 'cardealer' and LocalPlayer.state.job.grade_name == 'boss' then
CurrentAction = 'boss_actions_menu'
CurrentActionMsg = TranslateCap('shop_menu')
CurrentActionData = {}
end
end
-function hasExitedMarker(zone)
+local function hasExitedMarker(zone)
if not IsInShopMenu then
ESX.UI.Menu.CloseAll()
+ ESX.CloseContext()
end
ESX.HideUI()
CurrentAction = nil
end
AddEventHandler('onResourceStop', function(resource)
- if resource == GetCurrentResourceName() then
- if IsInShopMenu then
- ESX.UI.Menu.CloseAll()
+ if resource ~= GetCurrentResourceName() then return end
+ if IsInShopMenu then
+ ESX.UI.Menu.CloseAll()
+ ESX.CloseContext()
- local playerPed = PlayerPedId()
+ local playerPed = ESX.PlayerData.ped
- FreezeEntityPosition(playerPed, false)
- SetEntityVisible(playerPed, true)
- SetEntityCoords(playerPed, Config.Zones.ShopEntering.Pos)
- end
-
- DeleteDisplayVehicleInsideShop()
+ FreezeEntityPosition(playerPed, false)
+ SetEntityVisible(playerPed, true)
+ SetEntityCoords(playerPed, Config.Zones.ShopEntering.Pos)
end
-end)
-
-if Config.EnablePlayerManagement then
- RegisterNetEvent('esx_phone:loaded')
- AddEventHandler('esx_phone:loaded', function(phoneNumber, contacts)
- local specialContact = {
- name = TranslateCap('dealership'),
- number = 'cardealer',
- base64Icon = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAAEACAMAAABrrFhUAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAMAUExURQAAADMzMzszM0M0M0w0M1Q1M101M2U2M242M3Y3M383Moc4MpA4Mpg5MqE5Mqk6MrI6Mro7Mrw8Mr89M71DML5EO8I+NMU/NcBMLshANctBNs5CN8RULMddKsheKs9YLtBCONZEOdlFOtxGO99HPNhMNsplKM1nKM1uJtRhLddiLt5kMNJwJ9B2JNR/IeNIPeVJPehKPuRQOuhSO+lZOOlhNuloM+p3Lep/KupwMMFORsVYUcplXc1waNJ7delUSepgVexrYe12bdeHH9iIH9qQHd2YG+udH+OEJeuGJ+uOJeuVIuChGeSpF+aqGOykHOysGeeyFeuzFuyzFuq6E+27FO+Cee3CEdaGgdqTjvCNhfKYkvOkngAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJezdycAAAEAdFJOU////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////wBT9wclAAAACXBIWXMAAA7DAAAOwwHHb6hkAAAAGHRFWHRTb2Z0d2FyZQBwYWludC5uZXQgNC4xLjb9TgnoAAAQGElEQVR4Xt2d+WMUtxXHbS6bEGMPMcQQ04aEUnqYo9xJWvC6kAKmQLM2rdn//9+g0uir2Tl0PElPszP7+cnH7Fj6rPTeG2lmvfKld2azk8lk/36L/cnkZDbDIT3Sp4DZ8QS9dTI57tNDTwJOOu+4j/0TvDQz+QXMSG+7mUn+sZBZQELnNROcKhMZBXx+gS4k8+IzTpmBXAJOnqPxTDzPFRKyCODuvSKPgwwC2EZ+lxf4E4xwCzhBU7PBPQx4BWR88+fwDgNGAbMsM9/Ec8bygE3A5966L3nOlhiZBGSf+l2YggGLgBna1DMsE4FBQH9zvw1HLEgX0Evkt5GeEVIFMFztpJF6rZQm4DNasVDSEkKSgIVN/ibP0ZwoEgQsfPTPSZgH8QIG8vYr4gdBrIABvf2K2EEQKWBQb78ichBECRhE8O8SlQ5iBAQvcffFPhoYQoSAAQ5/TcQ0CBYw0OGvCZ4GoQIGF/3bhGaDQAELvfKhERgIwgQMePrPCQsEQQLwFwYPmksiQMCC1n1iCFgooQtYwLJfPPQFQ7KAUfU/wABVwMj6TzdAFDDY6tcOMR3SBIyw/1QDJAGj7D/RAEXA6Oa/hhIHCAJG23+SAb+AEfefYsArYET1nwlvTegVgBONFnTDik8ATjNi0BEbHgGjuP5147k6dgsYaQHQxF0OOAUMfv2LhnOVzCVg4OufdFwrpS4BePkSgA6ZcAhYggCocQRCu4ClCIAaeyC0CliaAKCwhgGrALxwaUC3OtgELFEAUNjCgEXAklQAdSzVgEUAXrRUoGstzAKWbgJIzJPAKGAJJ4DEOAmMAvCCpQPda2ASsJQTQGKaBAYBS1YC1TGUQwYBOHgpQRdrdAUsaQRUdONgVwAOXVLQyTkdASO4CyiFzhMWbQEj3wbw094oaAtY2hSoaafCloClHwCdIdASgIOWGnQVNAWMeiOUSnPDtCkAh3Dz2MBD/G4BoLOKhgD2AfDo6Zv3v32y89v7929eP3n8AIf3RKMgbghgTQEPn/56hH56OXr/+ll/FhqJoC6AMwU8+RV9o/Ph6SO8ODf1RFAXwDcAnrjGvYMPT3sZB/UhUBeAXyfz+AP6E8HR2z6iIzosqQngugp4g77E8jr/KKhdEdQE4JeJPHiPfhCZHn7EVxVHz3CufKDLgrkAnhz4QA//6as7t653ead+uye/3i4qrt8+qHt4m3sQzIuhuQD8Kg3d///8FT1rc6h+fx3f1tk9mKpfCv79h7s4YybQaW4Buv//uoROdXAIKIrtvUrBdPcazpkHdLomgCUEquR/9Gd0yIBTgFBwoH4vDVy9h7PmoAqDlQD8IomnZdOPfo/emPAIENFAx4Lp7pWcBtDtSgBHCHykWm6b/iVeAcU24qQwcOkmzpwBHQa1AI4qUCXAf6IjZvwCiuKlOubTx+1LP+DU/OhqUAvAj1N4glajG2YoAioD74riBk7ODzoOARwzQNX/t9EJCyQBlYGXRZEtGWAOQADDDMAAQBds0AQUOg7cKopcyQBzAALwwxRIA4AqYBu5YLpTFFcy1USq50oAw36oGgBTdMAKUUCxq477dCi+zpQM1MKQEsBQBakUcKCab4cqoNhTB37aE19fyhIKVS2kBOBHCTxUzd1VrbdDFqCPnJZZJYuBsutcAtQigC8EhgjYwXXBq/K7HMmg7HopgGFHXIVAkbY80AUUd9ShOPZb/mRQ7pWXAvCDBFAFi6zlIUBAgUwgyiFJhmTAKEBdBn1yV4GSEAHX1bE6tfInAy2AYTlc5QC8Vy5CBBSv1ME6srAnA7k8LgUwhADVUhWvnAQJ2FEHz6srZgMyCEgB+DaBx6qhd9BOB0EC9DWBSoUS5mTAJuC1aqivDhaECdCpcG6Wd5GETQCWwgndChOgU+F8CBRXOEOhEsBwKYxdUH4B250hwJoMxCWxEJD+cBDq4E9oootAAYYhwBkK90sB+CYBxMAcAgxDoCi+x99Nh0kAYmAOAcYhwJcMmARgO1Reu/sIFmAcAmzJQApgqwPzCKiGAL4FTMlgJgQc4+sEsCGWR4AeAq0i49KP+ONJHAsBbIUwpRKOEKCHQGetgSMZTIQAfJmCaiGlEo4RoBdIO9fa3+HPp8AiQGfBTAKK2+o13QF2LT0UjkKAXhnZwbdz0pPBOATsqRft4dsa36Qmgy8rDFkQy0H5BGBdwLTekpoMZhwCdCHoXxGMFGCfA4K0ZDBbYbgW1AIovYoTgIUR83pDUjI4WWEoA/ILsOaBkpRkMBmHAOwU2vZdEpLBZIXho0LyCyjUq6yXm/GLJPsr+ILOQzzxMEffGJ5RAF5W3l9p4nd/UU15dP/+3bDhECjg4VvHMwAZBehbRrwcvf1bWG0QJuCZ8xGIjAJwQUTh6I9BGyhBArADaMO7Ny6IFKB3yUjshmTGIAGexyAwH53Ub5YOAHmQhkgW9LwQIkDdBTMCRMFEzgshAt7i/IOnvE2BGAhCBGDpb/iotTlagRgigPwU3KLBGjrplooAAaMJAdVVE+VW4wAB4U8CLozqosG/h0QXoDcAR0FVZ3hvtKUL0Os+o2B+4ewrjOkCIh8GXRDzxSNPYUwW4CmDh0b9nl1nYUwWMJoqSNHYSnTdZEleEBlNEQAa64f2wnifuiQ2oiJA0VpDtwUC8prgiIoA0LrithTGE+Ky+KiKAEX7xm1zYXxC3BgZVREA2tsoxk0k6s7QuIoARXenzlAYz2ibo/Qi4PDwUD/xlYF34vS4YcSPYRehWxgTd4dJHwrx7o6OOzu3XpKbSWX68rYe09f3aI4NO2mdW4uIAvxFwPSgNeVuYfmTh8NWZ3buEAyb7llqF8Y0Ac9wRjsHjdv4FHoBNJ2PhkXkbcJKuXGZulkYCwGEQsBXBHy0LIgHrOa7sNx3sOsVbH6EqV4Yy5uk/LfJPcD5bLwyvP2KXYZQMLXvIXj3i8wNqxXG8jY5fx70FAENz5sbG1v4UuJ/l3xM66Nrq3l2rwHDTTUlVSCQN0r6g4D7c5Gq/m9dOHd6teTM+tf4WfXIQyzz/n+9dgZnX6vO7jNg20+vbjYm3SvsLgJ0qN1cU80Dp8/jrUqcBRj/W+dP4cQlp9Y31c/1c1U2rHftoDAmCXAWAViB3lpH0+acxvuEW7ziQPxrdl9y6rz6jb6L0oL97l1VGJcCfCsCziJAKb6Isd9kTQ2ChIJAXdNuncUJG5xRZ/dsmxrvq1KIQKAemPBcDzqLAGX4QucNUqg26offIignwEXL2U9dlL/1hAFzJlRcvacemfHMAWcRULbwa7SoizJAvruhTanX1n9twO23+aBFiyuUp8acRYCnhaurZ+UB0UNA6t1C7DdxuvTrjoOGC4I5FAHOIqA8u6OFq6tlrIosBsokdg4nMnJOHnELh5uxZkIJBDiLYX0LmBE5vs6jMRZkvopMBHJpewOnsVBmGneilUdY+AUCnLWgazVUzoAtxwSQrIlj9AeCBCJngDG9zDkt++GcA/ZEWBT/gwDnHHDFAJmlPQNADYG4Yki80B5fwQVxkPOay3IlVSL77hXg2hGRIcDzFq2urouDokoBWQQ4I4BERgFXKeDMApUAZxB4YF8PFGPUM0cFcpR6ClYzYvBu4RwORCJwCXAlARkClABPIrReDAkB3hlQzoGohQEhwDsDVBjECwz4kiBJgMgElkEgBBir1CaiiVECXpH0yjyLF7SZvnQUwoKy60qA94OUHvwJN+w1EPPLWQQoRBN38IIgxIVw8wrTSBkEjFiWqSp+KruuBBA+SusGXtYCzXCB67YYCOOrrDWj+G/ZdSXANwckN40flIpmuBiqANVzCKB8nN7dK3hlHTTDxUAFXFY9hwDSFum9a3htDVoMiMVbBiQI+IfqOQRQ5oCgGwhoWSAWYhaIAh3XAogfKfljOxAQmqjWLaIg1AGyFo4BM6ASQH16rh0I/E0sr1ciIVSCenU0FMyASgBxDnQDgediUF0ORuMNMWdwYDDo9lwA/UMlm4HAW6skzICiuICTWImdAaoKElQCyEOgFQg20RIb8Xm6xDPATqml4XDQ6TgBzUDgGQIbOCwSzxD4CocFg07XBYQ8RFwPBO4lIbkakIQzz0ZHAB0C6wJChkAjELiWBLB7kcCmw++p2BQwHwB1AWGfrVsLBPZhir2LJC7iXAaip1cVAhsCwoZAPRDYDHD0377vFJ0B6gOgISDwA8ZrgcDcxjPRI7SJeeclwa6uAiV1AcEfJjEPBJuGWJVwEdRiy3BRdC4husjlcE1dQPhnzNcDQWt5eI3p7VdstASfTcmu9QHQFBD+Gev1iuDieuXg7Fes3Zdsrldl8Znq9og41FIQaAgIDIOS5qXB1oaEJfSZKM+eWFkJ0FlFU0BIMaSxLBYOl3kRJGkKiBgChjWCYdOIAB0BwYlAYlwsHCz1FCBoCYj7ZyOmxcKh0hoAHQFRQ2BMgaA1ADoCYv/bxlgCQe0qQNEREBUHBTfHEQjQyTldAcTHyDrcu4q/MWTKHfEGXQGxQ+D+/e/xVwYMuljDICD+nw79MPRA0CiCFQYBcamwZOCBoJ0CJSYB8ZNg4IEA3WtgFBAbByUDDgTdCCgwCkiYBAMOBKYJYBOQMAmGGwjQtRYWASmTYKCBwDgBrAKSJsEgA4F5AtgFJE2CIQYCdKuDVUDi/2AcWiAwlEAKq4DU/70yrEDwMzrVxS4gMQwMKhDYAoDAISAxDAwpEKBDJlwCkv8V61ACgTUACFwC0qoByTACgaUCUDgFMPwTqgEEAnsAlLgFJAfCAQQCRwCUeAQkB8LFBwJ0xIZPAIOBxQYCdMOKV0DkRkGDBQaC9jZAB6+AqA3TNgsLBM2NUBN+ASwGbn6DFvWLv/8UASwG7n2LNvUJof8kAQzlgOA7tKo/nAWQhiSAx8CNngOBuwDS0ATwGOg3END6TxXAEgd6DQSU+S+hCuAx0F8goPafLoDJQE+BgNz/AAEsNWFPgcBb/80JEMBxXSDoIRCguSSCBDBcHUsyBwLP9W+LMAE86TBvICCmP02ggPRVspKMgYBU/tUIFZC+UlqSLRC41j+NBAsYdCAIm/4lEQKGGwgCp39JjACmacAeCIKHvyRKANM04A0EEcNfEimAKRswBoK/o2GhxApgGgRcgSDy7RfEC+AZBDyBIDT510gQwDMIGAJB/NsvSBLAkw5SA0FU8K9IE8AzD5ICQcLoL0kVEP2ERR3zZzRR6Dz/EEy6gC+z9FBwL24D9XLAwocNBgEsa0URj11xdJ9JAMeCYfBjV/RlPydMAkRCSJ0IQYGA592XsAlIjwX0QMDXfVYBgsSMQAsE6ZG/Dq+A1GBACARMU7+CW4AgZRh4AgHvm1+SQYAYBvHRwBEILnO/+SVZBAjiHZgDQZ7eC3IJEHyOnAvdQPBT2vWOk4wCJFHXSs1AkHq14yGzAMEsXEIVCH5hTPgW8gsoOQlcSr9W/Jxr0rfoSUDJ7Jg0GCbHM7ygD/oUAGazk8mkMyL2J5OTWZ89L/ny5f+yiDXCPYKoAQAAAABJRU5ErkJggg==',
- }
-
- TriggerEvent('esx_phone:addSpecialContact', specialContact.name, specialContact.number, specialContact.base64Icon)
- end)
-end
-
--- Create Blips
-if Config.Blip.show then
- CreateThread(function()
- local blip = AddBlipForCoord(Config.Zones.ShopEntering.Pos)
-
- SetBlipSprite (blip, Config.Blip.Sprite)
- SetBlipDisplay(blip, Config.Blip.Display)
- SetBlipScale (blip, Config.Blip.Scale)
- SetBlipAsShortRange(blip, true)
- BeginTextCommandSetBlipName('STRING')
- AddTextComponentSubstringPlayerName(TranslateCap('car_dealer'))
- EndTextCommandSetBlipName(blip)
- end)
-end
+ ESX.HideUI()
+ DeleteDisplayVehicleInsideShop()
+end)
-- Enter / Exit marker events & Draw Markers
CreateThread(function()
while true do
Wait(0)
- local playerCoords = GetEntityCoords(PlayerPedId())
+ local playerCoords = GetEntityCoords(ESX.PlayerData.ped)
local isInMarker, letSleep, currentZone = false, true
for k,v in pairs(Config.Zones) do
@@ -764,7 +784,7 @@ CreateThread(function()
end
if letSleep then
- Wait(500)
+ Wait(1000)
end
end
end)
@@ -817,18 +837,9 @@ CreateThread(function()
CurrentAction = nil
end
else
- Wait(500)
+ Wait(1000)
end
end
end)
-CreateThread(function()
- RequestIpl('shr_int') -- Load walls and floor
-
- local interiorID = 7170
- PinInteriorInMemory(interiorID)
- ActivateInteriorEntitySet(interiorID, 'csr_beforeMission') -- Load large window
- RefreshInterior(interiorID)
-end)
-
if ESX.PlayerLoaded then PlayerManagement() end
diff --git a/client/utils.lua b/client/utils.lua
index 6d05de3..5f8af39 100644
--- a/client/utils.lua
+++ b/client/utils.lua
@@ -1,10 +1,9 @@
-local NumberCharset = {}
-local Charset = {}
+local NumberCharset, Charset = {}, {}
-for i = 48, 57 do table.insert(NumberCharset, string.char(i)) end
+for i = 48, 57 do TableInsert(NumberCharset, 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
+for i = 65, 90 do TableInsert(Charset, string.char(i)) end
+for i = 97, 122 do TableInsert(Charset, string.char(i)) end
function GeneratePlate()
math.randomseed(GetGameTimer())
@@ -39,3 +38,7 @@ function GetRandomLetter(length)
Wait(0)
return length > 0 and GetRandomLetter(length - 1) .. Charset[math.random(1, #Charset)] or ''
end
+
+function TableInsert(t, v)
+ t[#t + 1] = v
+end
\ No newline at end of file
diff --git a/server/main.lua b/server/main.lua
index d462829..2e2dd3d 100644
--- a/server/main.lua
+++ b/server/main.lua
@@ -1,11 +1,75 @@
-local categories, vehicles = {}, {}
-local vehiclesByModel = {}
-
+local Config = Config
+
+local vehicleShop = {
+ categories = {},
+ vehicles = {},
+ vehiclesByModel = {},
+ soldVehicles = {},
+ cardealerVehicles = {},
+ rentedVehicles = {}
+}
CreateThread(function()
- exports["esx_society"]:registerSociety('cardealer', TranslateCap('car_dealer'), 'society_cardealer', 'society_cardealer', 'society_cardealer', {type = 'private'})
+ while true do
+ Wait(60000)
+ collectgarbage("collect")
+ end
end)
+local function getCategories()
+ vehicleShop.categories = MySQL.query.await('SELECT * FROM vehicle_categories')
+ GlobalState.vehicleShop = vehicleShop
+ return true
+end
+
+local function getVehicles()
+ vehicleShop.vehicles = MySQL.query.await('SELECT vehicles.*, vehicle_categories.label AS categoryLabel FROM vehicles JOIN vehicle_categories ON vehicles.category = vehicle_categories.name')
+
+ for _, vehicle in pairs(vehicleShop.vehicles) do
+ vehicleShop.vehiclesByModel[vehicle.model] = vehicle
+ end
+
+ GlobalState.vehicleShop = vehicleShop
+ return true
+end
+
+local function getSoldVehicles()
+ vehicleShop.soldVehicles = MySQL.query.await('SELECT * FROM vehicle_sold ORDER BY DATE DESC')
+ GlobalState.vehicleShop = vehicleShop
+ return true
+end
+
+local function getCardealerVehicles()
+ vehicleShop.cardealerVehicles = MySQL.query.await('SELECT * FROM cardealer_vehicles ORDER BY vehicle ASC')
+ GlobalState.vehicleShop = vehicleShop
+ return true
+end
+
+local function getRentedVehicles()
+ MySQL.query('SELECT * FROM rented_vehicles ORDER BY player_name ASC', function(result)
+ vehicleShop.rentedVehicles = {}
+
+ for i = 1, #result do
+ local vehicle = result[i]
+ vehicleShop.rentedVehicles[#vehicleShop.rentedVehicles + 1] = {
+ name = vehicle.vehicle,
+ plate = vehicle.plate,
+ playerName = vehicle.player_name
+ }
+ end
+ GlobalState.vehicleShop = vehicleShop
+ return true
+ end)
+end
+
CreateThread(function()
+ exports["esx_society"]:registerSociety('cardealer', TranslateCap('car_dealer'), 'society_cardealer', 'society_cardealer', 'society_cardealer', {type = 'private'})
+
+ getCategories()
+ getVehicles()
+ getSoldVehicles()
+ getCardealerVehicles()
+ getRentedVehicles()
+
local char = Config.PlateLetters
char = char + Config.PlateNumbers
if Config.PlateUseSpace then char = char + 1 end
@@ -15,95 +79,75 @@ CreateThread(function()
end
end)
-function RemoveOwnedVehicle(plate)
+local function removeOwnedVehicle(plate)
MySQL.update('DELETE FROM owned_vehicles WHERE plate = ?', {plate})
end
-AddEventHandler('onResourceStart', function(resourceName)
- if resourceName == GetCurrentResourceName() then
- SQLVehiclesAndCategories()
- end
-end)
-
-function SQLVehiclesAndCategories()
- categories = MySQL.query.await('SELECT * FROM vehicle_categories')
- vehicles = MySQL.query.await('SELECT vehicles.*, vehicle_categories.label AS categoryLabel FROM vehicles JOIN vehicle_categories ON vehicles.category = vehicle_categories.name')
-
- for _, vehicle in pairs(vehicles) do
- vehiclesByModel[vehicle.model] = vehicle
- end
-
- TriggerClientEvent("esx_vehicleshop:updateVehiclesAndCategories", -1, vehicles, categories, vehiclesByModel)
-end
-
-function getVehicleFromModel(model)
- return vehiclesByModel[model]
+local function getVehicleFromModel(model)
+ return vehicleShop.vehiclesByModel[model]
end
-RegisterNetEvent("esx_vehicleshop:getVehiclesAndCategories", function()
- TriggerClientEvent("esx_vehicleshop:updateVehiclesAndCategories", source, vehicles, categories, vehiclesByModel)
-end)
-
RegisterNetEvent('esx_vehicleshop:setVehicleOwnedPlayerId')
AddEventHandler('esx_vehicleshop:setVehicleOwnedPlayerId', function(playerId, vehicleProps, model, label)
local xPlayer, xTarget = ESX.GetPlayerFromId(source), ESX.GetPlayerFromId(playerId)
- if xPlayer.job.name ~= 'cardealer' or not xTarget then
+ if Player(source).state.job ~= 'cardealer' or not xTarget then
return
end
-
- MySQL.scalar('SELECT id FROM cardealer_vehicles WHERE vehicle = ?', {model},
- function(id)
- if not id then
- return
+
+ if not model then return end
+
+ for i = 1, #vehicleShop.cardealerVehicles, 1 do
+ local v = vehicleShop.cardealerVehicles[i]
+ if v.vehicle == model then
+ local sqlDel = MySQL.update.await('DELETE FROM cardealer_vehicles WHERE id = ?', {v.id})
+ if not sqlDel then return end
+ table.remove(vehicleShop.cardealerVehicles, i)
+ GlobalState.vehicleShop = vehicleShop
+ break
end
+ end
- MySQL.update('DELETE FROM cardealer_vehicles WHERE id = ?', {id},
- function(rowsChanged)
- if rowsChanged == 1 then
- MySQL.insert('INSERT INTO owned_vehicles (owner, plate, vehicle) VALUES (?, ?, ?)', {xTarget.identifier, vehicleProps.plate, json.encode(vehicleProps)},
- function(id)
- xPlayer.showNotification(TranslateCap('vehicle_set_owned', vehicleProps.plate, xTarget.getName()))
- xTarget.showNotification(TranslateCap('vehicle_belongs', vehicleProps.plate))
- end)
-
- MySQL.insert('INSERT INTO vehicle_sold (client, model, plate, soldby, date) VALUES (?, ?, ?, ?, ?)', {xTarget.getName(), label, vehicleProps.plate, xPlayer.getName(), os.date('%Y-%m-%d %H:%M')})
- end
- end)
+ MySQL.insert('INSERT INTO owned_vehicles (owner, plate, vehicle) VALUES (?, ?, ?)', {xTarget.identifier, vehicleProps.plate, json.encode(vehicleProps)}, function(id)
+ xPlayer.showNotification(TranslateCap('vehicle_set_owned', vehicleProps.plate, xTarget.getName()))
+ xTarget.showNotification(TranslateCap('vehicle_belongs', vehicleProps.plate))
end)
-end)
-ESX.RegisterServerCallback('esx_vehicleshop:getSoldVehicles', function(source, cb)
- MySQL.query('SELECT client, model, plate, soldby, date FROM vehicle_sold ORDER BY DATE DESC', function(result)
- cb(result)
- end)
+ local sqlIns = MySQL.insert.await('INSERT INTO vehicle_sold (client, model, plate, soldby, date) VALUES (?, ?, ?, ?, ?)', {xTarget.getName(), label, vehicleProps.plate, xPlayer.getName(), os.date('%Y-%m-%d %H:%M')})
+ if not sqlIns then return end
+ vehicleShop.soldVehicles[#vehicleShop.soldVehicles + 1] = {xTarget.getName(), label, vehicleProps.plate, xPlayer.getName(), os.date('%Y-%m-%d %H:%M')}
+ GlobalState.vehicleShop = vehicleShop
end)
RegisterNetEvent('esx_vehicleshop:rentVehicle')
AddEventHandler('esx_vehicleshop:rentVehicle', function(vehicle, plate, rentPrice, playerId)
local xPlayer, xTarget = ESX.GetPlayerFromId(source), ESX.GetPlayerFromId(playerId)
- if xPlayer.job.name ~= 'cardealer' or not xTarget then
+ if Player(source).state.job ~= 'cardealer' or not xTarget then
return
end
-
- MySQL.single('SELECT id, price FROM cardealer_vehicles WHERE vehicle = ?', {vehicle},
- function(result)
- if not result then
- return
+
+ if not vehicle or not plate or not rentPrice then return end
+
+ local price = nil
+
+ for i = 1, #vehicleShop.cardealerVehicles, 1 do
+ local v = vehicleShop.cardealerVehicles[i]
+ if v.vehicle == vehicle then
+ price = v.price
+ local sqlDel = MySQL.update.await('DELETE FROM cardealer_vehicles WHERE id = ?', {v.id})
+ if not sqlDel then return end
+ table.remove(vehicleShop.cardealerVehicles, i)
+ GlobalState.vehicleShop = vehicleShop
+ break
end
+ end
+
+ if not price then return end
- MySQL.update('DELETE FROM cardealer_vehicles WHERE id = ?', {result.id},
- function(rowsChanged)
- if rowsChanged ~= 1 then
- return
- end
-
- MySQL.insert('INSERT INTO rented_vehicles (vehicle, plate, player_name, base_price, rent_price, owner) VALUES (?, ?, ?, ?, ?, ?)', {vehicle, plate, xTarget.getName(), result.price, rentPrice, xTarget.identifier},
- function(id)
- xPlayer.showNotification(TranslateCap('vehicle_set_rented', plate, xTarget.getName()))
- end)
- end)
+ MySQL.insert('INSERT INTO rented_vehicles (vehicle, plate, player_name, base_price, rent_price, owner) VALUES (?, ?, ?, ?, ?, ?)', {vehicle, plate, xTarget.getName(), price, rentPrice, xTarget.identifier},
+ function(id)
+ xPlayer.showNotification(TranslateCap('vehicle_set_rented', plate, xTarget.getName()))
end)
end)
@@ -115,10 +159,8 @@ AddEventHandler('esx_vehicleshop:getStockItem', function(itemName, count)
TriggerEvent('esx_addoninventory:getSharedInventory', 'society_cardealer', function(inventory)
local item = inventory.getItem(itemName)
- -- is there enough in the society?
if count > 0 and item.count >= count then
- -- can the player carry the said amount of x item?
if not xPlayer.canCarryItem(itemName, count) then
return xPlayer.showNotification(TranslateCap('player_cannot_hold'))
end
@@ -139,13 +181,14 @@ AddEventHandler('esx_vehicleshop:putStockItems', function(itemName, count)
TriggerEvent('esx_addoninventory:getSharedInventory', 'society_cardealer', function(inventory)
local item = inventory.getItem(itemName)
- if item.count >= 0 then
- xPlayer.removeInventoryItem(itemName, count)
- inventory.addItem(itemName, count)
- xPlayer.showNotification(TranslateCap('have_deposited', count, item.label))
- else
+ if item.count < 0 then
xPlayer.showNotification(TranslateCap('invalid_amount'))
+ return
end
+
+ xPlayer.removeInventoryItem(itemName, count)
+ inventory.addItem(itemName, count)
+ xPlayer.showNotification(TranslateCap('have_deposited', count, item.label))
end)
end)
@@ -153,51 +196,55 @@ ESX.RegisterServerCallback('esx_vehicleshop:buyVehicle', function(source, cb, mo
local xPlayer = ESX.GetPlayerFromId(source)
local modelPrice = getVehicleFromModel(model).price
- if modelPrice and xPlayer.getMoney() >= modelPrice then
- xPlayer.removeMoney(modelPrice, "Vehicle Purchase")
-
- MySQL.insert('INSERT INTO owned_vehicles (owner, plate, vehicle) VALUES (?, ?, ?)', {xPlayer.identifier, plate, json.encode({model = joaat(model), plate = plate})
- }, function(rowsChanged)
- xPlayer.showNotification(TranslateCap('vehicle_belongs', plate))
- ESX.OneSync.SpawnVehicle(joaat(model), Config.Zones.ShopOutside.Pos, Config.Zones.ShopOutside.Heading,{plate = plate}, function(vehicle)
- Wait(100)
- local vehicle = NetworkGetEntityFromNetworkId(vehicle)
- Wait(300)
- TaskWarpPedIntoVehicle(GetPlayerPed(source), vehicle, -1)
- end)
- cb(true)
- end)
- else
+ if not modelPrice then
+ cb(false)
+ return
+ end
+
+ if xPlayer.getMoney() < modelPrice then
cb(false)
+ return
end
-end)
-ESX.RegisterServerCallback('esx_vehicleshop:getCommercialVehicles', function(source, cb)
- MySQL.query('SELECT price, vehicle FROM cardealer_vehicles ORDER BY vehicle ASC', function(result)
- cb(result)
+ xPlayer.removeMoney(modelPrice, "Vehicle Purchase")
+
+ MySQL.insert('INSERT INTO owned_vehicles (owner, plate, vehicle) VALUES (?, ?, ?)', {xPlayer.identifier, plate, json.encode({model = joaat(model), plate = plate})
+ }, function(rowsChanged)
+ xPlayer.showNotification(TranslateCap('vehicle_belongs', plate))
+ ESX.OneSync.SpawnVehicle(joaat(model), Config.Zones.ShopOutside.Pos, Config.Zones.ShopOutside.Heading,{plate = plate}, function(vehicle)
+ Wait(100)
+ local vehicle = NetworkGetEntityFromNetworkId(vehicle)
+ Wait(300)
+ TaskWarpPedIntoVehicle(GetPlayerPed(source), vehicle, -1)
+ end)
+ cb(true)
end)
end)
ESX.RegisterServerCallback('esx_vehicleshop:buyCarDealerVehicle', function(source, cb, model)
- local xPlayer = ESX.GetPlayerFromId(source)
-
- if xPlayer.job.name ~= 'cardealer' then
+ if Player(source).state.job ~= 'cardealer' then
return cb(false)
end
+
local modelPrice = getVehicleFromModel(model).price
if not modelPrice then
return cb(false)
end
+
TriggerEvent('esx_addonaccount:getSharedAccount', 'society_cardealer', function(account)
if account.money < modelPrice then
return cb(false)
end
- account.removeMoney(modelPrice)
-
MySQL.insert('INSERT INTO cardealer_vehicles (vehicle, price) VALUES (?, ?)', {model, modelPrice},
function(rowsChanged)
+ if not rowsChanged then
+ cb(false)
+ return
+ end
+ account.removeMoney(modelPrice)
+ getCardealerVehicles()
cb(true)
end)
end)
@@ -207,71 +254,67 @@ RegisterNetEvent('esx_vehicleshop:returnProvider')
AddEventHandler('esx_vehicleshop:returnProvider', function(vehicleModel)
local xPlayer = ESX.GetPlayerFromId(source)
- if xPlayer.job.name ~= 'cardealer' then
+ if Player(source).state.job ~= 'cardealer' then
return
end
- MySQL.single('SELECT id, price FROM cardealer_vehicles WHERE vehicle = ?', {vehicleModel},
- function(result)
- if not result then
- return print(('[^3WARNING^7] Player ^5%s^7 Attempted To Sell Invalid Vehicle - ^5%s^7!'):format(source, vehicleModel))
+
+ local id = nil
+ local price = nil
+
+ for i = 1, #vehicleShop.cardealerVehicles, 1 do
+ local v = vehicleShop.cardealerVehicles[i]
+ if v.vehicle == vehicleModel then
+ id = v.id
+ price = v.price
+ local sqlDel = MySQL.update.await('DELETE FROM cardealer_vehicles WHERE id = ?', {v.id})
+ if not sqlDel then return end
+ table.remove(vehicleShop.cardealerVehicles, i)
+ GlobalState.vehicleShop = vehicleShop
+ break
end
+ end
+
+ if not id or not price then return end
- local id = result.id
-
- MySQL.update('DELETE FROM cardealer_vehicles WHERE id = ?', {id},
- function(rowsChanged)
- if rowsChanged ~= 1 then
- return
- end
- TriggerEvent('esx_addonaccount:getSharedAccount', 'society_cardealer', function(account)
- local price = ESX.Math.Round(result.price * 0.75)
- local vehicleLabel = getVehicleFromModel(vehicleModel).label
-
- account.addMoney(price)
- xPlayer.showNotification(TranslateCap('vehicle_sold_for', vehicleLabel, ESX.Math.GroupDigits(price)))
- end)
- end)
- end)
-end)
-
-ESX.RegisterServerCallback('esx_vehicleshop:getRentedVehicles', function(source, cb)
- MySQL.query('SELECT * FROM rented_vehicles ORDER BY player_name ASC', function(result)
- local vehicles = {}
-
- for i = 1, #result do
- local vehicle = result[i]
- vehicles[#vehicles + 1] = {
- name = vehicle.vehicle,
- plate = vehicle.plate,
- playerName = vehicle.player_name
- }
- end
+ TriggerEvent('esx_addonaccount:getSharedAccount', 'society_cardealer', function(account)
+ local vehPrice = ESX.Math.Round(price * 0.75)
+ local vehicleLabel = getVehicleFromModel(vehicleModel).label
- cb(vehicles)
+ account.addMoney(vehPrice)
+ xPlayer.showNotification(TranslateCap('vehicle_sold_for', vehicleLabel, ESX.Math.GroupDigits(vehPrice)))
end)
end)
ESX.RegisterServerCallback('esx_vehicleshop:giveBackVehicle', function(source, cb, plate)
- MySQL.single('SELECT base_price, vehicle FROM rented_vehicles WHERE plate = ?', {plate},
- function(result)
- if not result then
- return cb(false)
+ local basePrice, vehicle = nil, nil
+
+ if not plate then return end
+
+ for i = 1, #vehicleShop.rentedVehicles, 1 do
+ local v = vehicleShop.rentedVehicles[i]
+ if v.plate == plate then
+ basePrice = v.base_price
+ vehicle = v.vehicle
+ local sqlDel = MySQL.update.await('DELETE FROM rented_vehicles WHERE plate = ?', {plate})
+ if not sqlDel then return cb(false) end
+ table.remove(vehicleShop.rentedVehicles, i)
+ GlobalState.vehicleShop = vehicleShop
+ break
end
+ end
- MySQL.update('DELETE FROM rented_vehicles WHERE plate = ?', {plate},
- function()
- MySQL.insert('INSERT INTO cardealer_vehicles (vehicle, price) VALUES (?, ?)', {result.vehicle, result.base_price})
+ local sqlIns = MySQL.insert.await('INSERT INTO cardealer_vehicles (vehicle, price) VALUES (?, ?)', {vehicle, basePrice})
+ if not sqlIns then return cb(false) end
+ getCardealerVehicles()
- RemoveOwnedVehicle(plate)
- cb(true)
- end)
- end)
+ removeOwnedVehicle(plate)
+ cb(true)
end)
ESX.RegisterServerCallback('esx_vehicleshop:resellVehicle', function(source, cb, plate, model)
local xPlayer, resellPrice = ESX.GetPlayerFromId(source)
- if xPlayer.job.name == 'cardealer' or not Config.EnablePlayerManagement then
+ if Player(source).state.job == 'cardealer' or not Config.EnablePlayerManagement then
-- calculate the resell price
for i=1, #vehicles, 1 do
if joaat(vehicles[i].model) == model then
@@ -284,31 +327,31 @@ ESX.RegisterServerCallback('esx_vehicleshop:resellVehicle', function(source, cb,
print(('[^3WARNING^7] Player ^5%s^7 Attempted To Resell Invalid Vehicle - ^5%s^7!'):format(source, model))
return cb(false)
end
- MySQL.single('SELECT * FROM rented_vehicles WHERE plate = ?', {plate},
+ for i = 1, #vehicleShop.rentedVehicles, 1 do
+ if vehicleShop.rentedVehicles[i].plate == plate then
+ cb(false)
+ return
+ end
+ end
+ MySQL.single('SELECT * FROM owned_vehicles WHERE owner = ? AND plate = ?', {xPlayer.identifier, plate},
function(result)
- if result then -- is it a rented vehicle?
- return cb(false) -- it is, don't let the player sell it since he doesn't own it
+ if not result then
+ return cb(false)
end
- MySQL.single('SELECT * FROM owned_vehicles WHERE owner = ? AND plate = ?', {xPlayer.identifier, plate},
- function(result)
- if not result then -- does the owner match?
- return
- end
- local vehicle = json.decode(result.vehicle)
+ local vehicle = json.decode(result.vehicle)
- if vehicle.model ~= model then
- print(('[^3WARNING^7] Player ^5%s^7 Attempted To Resell Vehicle With Invalid Model - ^5%s^7!'):format(source, model))
- return cb(false)
- end
- if vehicle.plate ~= plate then
- print(('[^3WARNING^7] Player ^5%s^7 Attempted To Resell Vehicle With Invalid Plate - ^5%s^7!'):format(source, plate))
- return cb(false)
- end
+ if vehicle.model ~= model then
+ print(('[^3WARNING^7] Player ^5%s^7 Attempted To Resell Vehicle With Invalid Model - ^5%s^7!'):format(source, model))
+ return cb(false)
+ end
+ if vehicle.plate ~= plate then
+ print(('[^3WARNING^7] Player ^5%s^7 Attempted To Resell Vehicle With Invalid Plate - ^5%s^7!'):format(source, plate))
+ return cb(false)
+ end
- xPlayer.addMoney(resellPrice, "Sold Vehicle")
- RemoveOwnedVehicle(plate)
- cb(true)
- end)
+ xPlayer.addMoney(resellPrice, "Sold Vehicle")
+ removeOwnedVehicle(plate)
+ cb(true)
end)
end
end)
@@ -344,9 +387,7 @@ end)
RegisterNetEvent('esx_vehicleshop:setJobVehicleState')
AddEventHandler('esx_vehicleshop:setJobVehicleState', function(plate, state)
- local xPlayer = ESX.GetPlayerFromId(source)
-
- MySQL.update('UPDATE owned_vehicles SET `stored` = ? WHERE plate = ? AND job = ?', {state, plate, xPlayer.job.name},
+ MySQL.update('UPDATE owned_vehicles SET `stored` = ? WHERE plate = ? AND job = ?', {state, plate, Player(source).state.job},
function(rowsChanged)
if rowsChanged == 0 then
print(('[^3WARNING^7] Player ^5%s^7 Attempted To Exploit the Garage!'):format(source, plate))
@@ -354,7 +395,7 @@ AddEventHandler('esx_vehicleshop:setJobVehicleState', function(plate, state)
end)
end)
-function PayRent()
+local function payRent()
local timeStart = os.clock()
print('[^2INFO^7] ^5Rent Payments^7 Initiated')
@@ -439,9 +480,10 @@ function PayRent()
if next(unrentals) then
MySQL.prepare.await('DELETE FROM rented_vehicles WHERE owner = ? AND plate = ?', unrentals)
end
-
+
+ getRentedVehicles()
print(('[^2INFO^7] ^5Rent Payments^7 took ^5%s^7 ms to execute'):format(ESX.Math.Round((os.time() - timeStart) / 1000000, 2)))
end)
end
-TriggerEvent('cron:runAt', 22, 00, PayRent)
+TriggerEvent('cron:runAt', 22, 00, payRent)