Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

vehicleshop stable version #87

Merged
merged 23 commits into from
Aug 30, 2024
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 57 additions & 21 deletions client/main.lua
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ local insideShop = nil
---@field balance number
---@field paymentsLeft integer
---@field paymentAmount number
---@field brand string
---@field name string
---@field vehId number

---@param data VehicleFinanceClient
local function financePayment(data)
Expand All @@ -24,7 +27,12 @@ local function financePayment(data)
if not dialog then return end

local paymentAmount = tonumber(dialog[1])
TriggerServerEvent('qbx_vehicleshop:server:financePayment', paymentAmount, data)
TriggerServerEvent('qbx_vehicleshop:server:financePayment', paymentAmount, {
vehId = data.vehId,
paymentAmount = data.paymentAmount,
balance = data.balance,
paymentsLeft = data.paymentsLeft
})
end

local function confirmationCheck()
Expand All @@ -43,7 +51,7 @@ end

---@param data VehicleFinanceClient
local function showVehicleFinanceMenu(data)
local vehLabel = VEHICLES[data.vehicle].brand..' '..VEHICLES[data.vehicle].name
local vehLabel = data.brand..' '..data.name
local vehFinance = {
{
title = 'Finance Information',
Expand All @@ -62,7 +70,7 @@ local function showVehicleFinanceMenu(data)
onSelect = function()
local check = confirmationCheck()
if check == 'confirm' then
TriggerServerEvent('qbx_vehicleshop:server:financePaymentFull', {vehBalance = data.balance, vehPlate = data.vehiclePlate})
TriggerServerEvent('qbx_vehicleshop:server:financePaymentFull', {vehBalance = data.balance, vehId = data.vehId})
else
lib.showContext('vehicleFinance')
end
Expand All @@ -86,18 +94,21 @@ local function showFinancedVehiclesMenu()
local ownedVehicles = {}

if vehicles == nil or #vehicles == 0 then return exports.qbx_core:Notify(locale('error.nofinanced'), 'error') end

for _, v in pairs(vehicles) do
if v.balance and v.balance > 0 then
local name = VEHICLES[v.vehicle].name
local vehicle = VEHICLES[v.modelName]
local plate = v.plate:upper()
ownedVehicles[#ownedVehicles + 1] = {
title = name,
title = vehicle.name,
description = locale('menus.veh_platetxt')..plate,
icon = 'fa-solid fa-car-side',
arrow = true,
onSelect = function()
showVehicleFinanceMenu({
vehicle = v.vehicle,
vehId = v.id,
name = vehicle.name,
brand = vehicle.brand,
vehiclePlate = plate,
balance = v.balance,
paymentsLeft = v.paymentsleft,
Expand Down Expand Up @@ -418,6 +429,21 @@ local function openVehicleSellMenu(targetVehicle)
lib.showContext('vehicleMenu')
end

--- End test drive
local function endTestDrive()
TriggerServerEvent('qbx_vehicleshop:server:deleteVehicle', testDriveVeh)
testDriveVeh = 0
inTestDrive = false
LocalPlayer.state:set('isInTestDrive', false, true)
Frowmza marked this conversation as resolved.
Show resolved Hide resolved
exports.qbx_core:Notify(locale('general.testdrive_complete'), 'success')
end

RegisterCommand('endtestdrive', function()
if not inTestDrive then return end

endTestDrive()
end, false)
Frowmza marked this conversation as resolved.
Show resolved Hide resolved

--- Starts the test drive timer based on time and shop
---@param time number
local function startTestDriveTimer(time)
Expand All @@ -429,10 +455,7 @@ local function startTestDriveTimer(time)
local currentGameTime = GetGameTimer()
local secondsLeft = currentGameTime - gameTimer
if currentGameTime < gameTimer + timeMs and secondsLeft >= timeMs - 50 then
TriggerServerEvent('qbx_vehicleshop:server:deleteVehicle', testDriveVeh)
testDriveVeh = 0
inTestDrive = false
exports.qbx_core:Notify(locale('general.testdrive_complete'), 'success')
endTestDrive()
end
qbx.drawText2d({ text = locale('general.testdrive_timer')..math.ceil(time - secondsLeft / 1000), coords = vec2(1.0, 1.38), scale = 0.5})
Wait(0)
Expand Down Expand Up @@ -523,12 +546,6 @@ end
local shopVehs = {}

local function init()
CreateThread(function()
for name, shop in pairs(config.shops) do
createShop(shop.zone.shape, name)
end
end)

CreateThread(function()
if config.finance.enable then
lib.zones.box({
Expand All @@ -552,12 +569,14 @@ local function init()
end)

CreateThread(function()
for shopName in pairs(config.shops) do
for shopName, shop in pairs(config.shops) do
createShop(shop.zone.shape, shopName)

local showroomVehicles = config.shops[shopName].showroomVehicles
for i = 1, #showroomVehicles do
local showroomVehicle = showroomVehicles[i]
local veh = createShowroomVehicle(showroomVehicle.vehicle, showroomVehicle.coords)
shopVehs[#shopVehs + 1] = veh
shopVehs[i] = veh
if config.useTarget then
createVehicleTarget(shopName, veh, i)
else
Expand Down Expand Up @@ -594,9 +613,21 @@ RegisterNetEvent('qbx_vehicleshop:client:testDrive', function(args)
if not args then return end

inTestDrive = true
LocalPlayer.state:set('isInTestDrive', true, true)
local testDrive = config.shops[insideShop].testDrive
local plate = 'TEST'..lib.string.random('1111')
local netId = lib.callback.await('qbx_vehicleshop:server:spawnVehicle', false, args.vehicle, testDrive.spawn, plate)
local netId = lib.callback.await('qbx_vehicleshop:server:spawnVehicle', false, {
model = args.vehicle,
coords = testDrive.spawn,
plate = plate
})

lib.waitFor(function()
if NetworkDoesEntityExistWithNetworkId(netId) then
return true
end
end, 'netId not exist')

testDriveVeh = netId
exports.qbx_core:Notify(locale('general.testdrive_timenoti', testDrive.limit), 'inform')
startTestDriveTimer(testDrive.limit * 60)
Expand Down Expand Up @@ -634,7 +665,12 @@ end)
---@param plate string
RegisterNetEvent('qbx_vehicleshop:client:buyShowroomVehicle', function(vehicle, plate, vehicleId)
local tempShop = insideShop -- temp hacky way of setting the shop because it changes after the callback has returned since you are outside the zone
local netId = lib.callback.await('qbx_vehicleshop:server:spawnVehicle', false, vehicle, config.shops[tempShop].vehicleSpawn, plate, vehicleId)
local netId = lib.callback.await('qbx_vehicleshop:server:spawnVehicle', false, {
model = vehicle,
coords = config.shops[tempShop].vehicleSpawn,
plate = plate,
vehicleId = vehicleId
})
local veh = NetToVeh(netId)
local props = lib.getVehicleProperties(veh)
props.plate = plate
Expand Down Expand Up @@ -695,6 +731,6 @@ end)

AddEventHandler('onResourceStart', function(resource)
if GetCurrentResourceName() == resource then
if LocalPlayer.state.isLoggedIn then init() end
init()
end
end)
3 changes: 2 additions & 1 deletion config/server.lua
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@ return {
paymentInterval = 24, -- time in hours between payment being due
preventSelling = false, -- prevents players from using /transfervehicle if financed
},
saleTimeout = 60000 -- Delay between attempts to sell/gift a vehicle. Prevents abuse
saleTimeout = 60000, -- Delay between attempts to sell/gift a vehicle. Prevents abuse
deleteUnpaidFinancedVehicle = false -- true to delete unpaid vehicles from database, otherwise it will edit citizenid to hide from db select
}
1 change: 0 additions & 1 deletion fxmanifest.lua
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ client_scripts {
server_scripts {
'@oxmysql/lib/MySQL.lua',
'server/main.lua',
'server/storage.lua',
}

files {
Expand Down
82 changes: 27 additions & 55 deletions server/storage.lua → server/finance.lua
Original file line number Diff line number Diff line change
Expand Up @@ -13,64 +13,47 @@
---@field insertVehicleEntityRequest InsertVehicleEntityRequest
---@field vehicleFinance VehicleFinanceServer

---@class VehicleFinancingEntity
---@field vehicleId integer
---@field balance number
---@field paymentamount number
---@field paymentsleft integer
---@field financetime number

local finance = {}

---@param request InsertVehicleEntityWithFinanceRequest
function InsertVehicleEntityWithFinance(request)
function finance.insertVehicleEntityWithFinance(request)
local insertVehicleEntityRequest = request.insertVehicleEntityRequest
local vehicleId = exports.qbx_vehicles:CreatePlayerVehicle({
model = request.insertVehicleEntityRequest.model,
citizenid = request.insertVehicleEntityRequest.citizenId,
model = insertVehicleEntityRequest.model,
citizenid = insertVehicleEntityRequest.citizenId,
props = {
plate = request.insertVehicleEntityRequest.plate
plate = insertVehicleEntityRequest.plate
}
})

local vehicleFinance = request.vehicleFinance
MySQL.insert('INSERT INTO vehicle_financing (vehicleId, balance, paymentamount, paymentsleft, financetime) VALUES (?, ?, ?, ?, ?)', {
vehicleId,
request.vehicleFinance.balance,
request.vehicleFinance.payment,
request.vehicleFinance.paymentsLeft,
request.vehicleFinance.timer
vehicleFinance.balance,
vehicleFinance.payment,
vehicleFinance.paymentsLeft,
vehicleFinance.timer
})

return vehicleId
end

---@alias VehicleEntity table

---@class VehicleFinancingEntity
---@field vehicleId integer
---@field balance number
---@field paymentamount number
---@field paymentsleft integer
---@field financetime number

---@param citizenId string
---@return VehicleEntity[]
function FetchVehicleEntitiesByCitizenId(citizenId)
return MySQL.query.await('SELECT * FROM player_vehicles WHERE citizenid = ?', {citizenId})
end

---@param plate string
---@return VehicleEntity
function FetchVehicleEntityByPlate(plate)
return MySQL.single.await('SELECT * FROM player_vehicles WHERE plate = ?', {plate})
end

---@param plate string
---@return boolean
function DoesVehicleEntityExist(plate)
local count = MySQL.scalar.await('SELECT COUNT(*) FROM player_vehicles WHERE plate = ?', {plate})
return count > 0
end

---@param time number
---@param vehicleId integer
function UpdateVehicleEntityFinanceTime(time, vehicleId)
function finance.updateVehicleEntityFinanceTime(time, vehicleId)
MySQL.update('UPDATE vehicle_financing SET financetime = ? WHERE vehicleId = ?', {time, vehicleId})
end

---@param vehicleFinance VehicleFinanceServer
---@param plate string
function UpdateVehicleFinance(vehicleFinance, plate)
local vehicleId = exports.qbx_vehicles:GetVehicleIdByPlate(plate)
---@param vehicleId number
function finance.updateVehicleFinance(vehicleFinance, vehicleId)
if vehicleFinance.balance == 0 then
MySQL.query('DELETE FROM vehicle_financing WHERE vehicleId = ?', {
vehicleId
Expand All @@ -86,35 +69,24 @@ function UpdateVehicleFinance(vehicleFinance, plate)
end
end

---@param citizenId string
---@param license string
---@param vehicleId integer
function UpdateVehicleEntityOwner(citizenId, license, vehicleId)
MySQL.update('UPDATE player_vehicles SET citizenid = ?, license = ? WHERE id = ?', {citizenId, license, vehicleId})
end

---@param id integer
---@return VehicleFinancingEntity
function FetchFinancedVehicleEntityById(id)
function finance.fetchFinancedVehicleEntityById(id)
return MySQL.single.await('SELECT * FROM vehicle_financing WHERE vehicleId = ? AND balance > 0 AND financetime < 1', {id})
end

---@param vehicleId integer
---@return boolean
function FetchIsFinanced(vehicleId)
function finance.fetchIsFinanced(vehicleId)
return MySQL.scalar.await('SELECT 1 FROM vehicle_financing WHERE vehicleId = ? AND balance > 0', {
vehicleId
}) ~= nil
end

---@param citizenId string
---@return VehicleFinancingEntity
function FetchFinancedVehicleEntitiesByCitizenId(citizenId)
function finance.fetchFinancedVehicleEntitiesByCitizenId(citizenId)
return MySQL.query.await('SELECT vehicle_financing.* FROM vehicle_financing INNER JOIN player_vehicles ON player_vehicles.citizenid = ? WHERE vehicle_financing.vehicleId = player_vehicles.id AND vehicle_financing.balance > 0 AND vehicle_financing.financetime > 1', {citizenId})
end

---@param license string
---@return VehicleFinancingEntity
function FetchFinancedVehicleEntitiesByLicense(license)
return MySQL.query.await('SELECT vf.*, p.citizenid FROM vehicle_financing AS vf INNER JOIN players AS p ON p.citizenid = ? INNER JOIN player_vehicles AS pv ON pv.citizenid = p.citizenid AND vf.balance > 0 AND vf.financetime < 1', {license})
end
return finance
Loading