247 lines
8.4 KiB
Lua
247 lines
8.4 KiB
Lua
assert(lib.checkDependency('qbx_core', '1.19.0', true))
|
|
assert(lib.checkDependency('qbx_vehicles', '1.3.1', true))
|
|
lib.versionCheck('Qbox-project/qbx_garages')
|
|
|
|
---@class ErrorResult
|
|
---@field code string
|
|
---@field message string
|
|
|
|
---@class PlayerVehicle
|
|
---@field id number
|
|
---@field citizenid? string
|
|
---@field modelName string
|
|
---@field garage string
|
|
---@field state VehicleState
|
|
---@field depotPrice integer
|
|
---@field props table ox_lib properties table
|
|
|
|
Config = require 'config.server'
|
|
VEHICLES = exports.qbx_core:GetVehiclesByName()
|
|
Storage = require 'server.storage'
|
|
---@type table<string, GarageConfig>
|
|
Garages = Config.garages
|
|
|
|
lib.callback.register('qbx_garages:server:getGarages', function()
|
|
return Garages
|
|
end)
|
|
|
|
---Returns garages for use server side.
|
|
local function getGarages()
|
|
return Garages
|
|
end
|
|
exports('GetGarages', getGarages)
|
|
|
|
|
|
---@param name string
|
|
---@param config GarageConfig
|
|
local function registerGarage(name, config)
|
|
Garages[name] = config
|
|
TriggerClientEvent('qbx_garages:client:garageRegistered', -1, name, config)
|
|
TriggerEvent('qbx_garages:server:garageRegistered', name, config)
|
|
end
|
|
|
|
exports('RegisterGarage', registerGarage)
|
|
|
|
---Sets the vehicle's garage. It is the caller's responsibility to make sure the vehicle is not currently spawned in the world, or else this may have no effect.
|
|
---@param vehicleId integer
|
|
---@param garageName string
|
|
---@return boolean success, ErrorResult?
|
|
local function setVehicleGarage(vehicleId, garageName)
|
|
local garage = Garages[garageName]
|
|
if not garage then
|
|
return false, {
|
|
code = 'not_found',
|
|
message = string.format('garage name %s not found. Did you forget to register it?', garageName)
|
|
}
|
|
end
|
|
|
|
local state = garage.type == GarageType.DEPOT and VehicleState.IMPOUNDED or VehicleState.GARAGED
|
|
local numRowsAffected = Storage.setVehicleGarage(vehicleId, garageName, state)
|
|
if numRowsAffected == 0 then
|
|
return false, {
|
|
code = 'no_rows_changed',
|
|
message = string.format('no rows were changed for vehicleId=%s', vehicleId)
|
|
}
|
|
end
|
|
return true
|
|
end
|
|
|
|
exports('SetVehicleGarage', setVehicleGarage)
|
|
|
|
---Sets the vehicle's price for retrieval at a depot. Only affects vehicles that are OUT or IMPOUNDED.
|
|
---@param vehicleId integer
|
|
---@param depotPrice integer
|
|
---@return boolean success, ErrorResult?
|
|
local function setVehicleDepotPrice(vehicleId, depotPrice)
|
|
local numRowsAffected = Storage.setVehicleDepotPrice(vehicleId, depotPrice)
|
|
if numRowsAffected == 0 then
|
|
return false, {
|
|
code = 'no_rows_changed',
|
|
message = string.format('no rows were changed for vehicleId=%s', vehicleId)
|
|
}
|
|
end
|
|
return true
|
|
end
|
|
|
|
exports('SetVehicleDepotPrice', setVehicleDepotPrice)
|
|
|
|
function FindPlateOnServer(plate)
|
|
local vehicles = GetAllVehicles()
|
|
for i = 1, #vehicles do
|
|
if plate == GetVehicleNumberPlateText(vehicles[i]) then
|
|
return true
|
|
end
|
|
end
|
|
end
|
|
|
|
---@param garage string
|
|
---@return GarageType?
|
|
function GetGarageType(garage)
|
|
return Garages[garage]?.type
|
|
end
|
|
|
|
---@class PlayerVehiclesFilters
|
|
---@field citizenid? string
|
|
---@field states? VehicleState|VehicleState[]
|
|
---@field garage? string
|
|
|
|
---@param source number
|
|
---@param garageName string
|
|
---@return PlayerVehiclesFilters
|
|
function GetPlayerVehicleFilter(source, garageName)
|
|
local player = exports.qbx_core:GetPlayer(source)
|
|
local garage = Garages[garageName]
|
|
local filter = {}
|
|
filter.citizenid = not garage.shared and player.PlayerData.citizenid or nil
|
|
filter.states = garage.states or VehicleState.GARAGED
|
|
filter.garage = not garage.skipGarageCheck and garageName or nil
|
|
return filter
|
|
end
|
|
|
|
local function getCanAccessGarage(player, garage)
|
|
if garage.groups and not exports.qbx_core:HasPrimaryGroup(player.PlayerData.source, garage.groups) then
|
|
return false
|
|
end
|
|
if garage.canAccess ~= nil and not garage.canAccess(player.PlayerData.source) then
|
|
return false
|
|
end
|
|
return true
|
|
end
|
|
|
|
---@param playerVehicle PlayerVehicle
|
|
---@return VehicleType
|
|
local function getVehicleType(playerVehicle)
|
|
if VEHICLES[playerVehicle.modelName].category == 'helicopters' or VEHICLES[playerVehicle.modelName].category == 'planes' then
|
|
return VehicleType.AIR
|
|
elseif VEHICLES[playerVehicle.modelName].category == 'boats' then
|
|
return VehicleType.SEA
|
|
else
|
|
return VehicleType.CAR
|
|
end
|
|
end
|
|
|
|
---@param source number
|
|
---@param garageName string
|
|
---@return PlayerVehicle[]?
|
|
lib.callback.register('qbx_garages:server:getGarageVehicles', function(source, garageName)
|
|
local player = exports.qbx_core:GetPlayer(source)
|
|
local garage = Garages[garageName]
|
|
if not getCanAccessGarage(player, garage) then return end
|
|
local filter = GetPlayerVehicleFilter(source, garageName)
|
|
local playerVehicles = exports.qbx_vehicles:GetPlayerVehicles(filter)
|
|
local toSend = {}
|
|
if not playerVehicles[1] then return end
|
|
for _, vehicle in pairs(playerVehicles) do
|
|
if not FindPlateOnServer(vehicle.props.plate) then
|
|
local vehicleType = Garages[garageName].vehicleType
|
|
if vehicleType == getVehicleType(vehicle) then
|
|
toSend[#toSend + 1] = vehicle
|
|
end
|
|
end
|
|
end
|
|
return toSend
|
|
end)
|
|
|
|
---@param source number
|
|
---@param vehicleId string
|
|
---@param garageName string
|
|
---@return boolean
|
|
local function isParkable(source, vehicleId, garageName)
|
|
local garageType = GetGarageType(garageName)
|
|
--- DEPOTS are only for retrieving, not storing
|
|
if garageType == GarageType.DEPOT then return false end
|
|
if not vehicleId then return false end
|
|
local player = exports.qbx_core:GetPlayer(source)
|
|
local garage = Garages[garageName]
|
|
if not getCanAccessGarage(player, garage) then
|
|
return false
|
|
end
|
|
---@type PlayerVehicle
|
|
local playerVehicle = exports.qbx_vehicles:GetPlayerVehicle(vehicleId)
|
|
if getVehicleType(playerVehicle) ~= garage.vehicleType then
|
|
return false
|
|
end
|
|
if not garage.shared then
|
|
if playerVehicle.citizenid ~= player.PlayerData.citizenid then
|
|
return false
|
|
end
|
|
end
|
|
return true
|
|
end
|
|
|
|
lib.callback.register('qbx_garages:server:isParkable', function(source, garage, netId)
|
|
local vehicle = NetworkGetEntityFromNetworkId(netId)
|
|
local vehicleId = Entity(vehicle).state.vehicleid or exports.qbx_vehicles:GetVehicleIdByPlate(GetVehicleNumberPlateText(vehicle))
|
|
return isParkable(source, vehicleId, garage)
|
|
end)
|
|
|
|
---@param source number
|
|
---@param netId number
|
|
---@param props table ox_lib vehicle props https://github.com/overextended/ox_lib/blob/master/resource/vehicleProperties/client.lua#L3
|
|
---@param garage string
|
|
lib.callback.register('qbx_garages:server:parkVehicle', function(source, netId, props, garage)
|
|
assert(Garages[garage] ~= nil, string.format('Garage %s not found. Did you register this garage?', garage))
|
|
local vehicle = NetworkGetEntityFromNetworkId(netId)
|
|
local vehicleId = Entity(vehicle).state.vehicleid or exports.qbx_vehicles:GetVehicleIdByPlate(GetVehicleNumberPlateText(vehicle))
|
|
local owned = isParkable(source, vehicleId, garage) --Check ownership
|
|
if not owned then
|
|
exports.qbx_core:Notify(source, locale('error.not_owned'), 'error')
|
|
return
|
|
end
|
|
|
|
exports.qbx_vehicles:SaveVehicle(vehicle, {
|
|
garage = garage,
|
|
state = VehicleState.GARAGED,
|
|
props = props
|
|
})
|
|
|
|
exports.qbx_core:DeleteVehicle(vehicle)
|
|
end)
|
|
|
|
AddEventHandler('onResourceStart', function(resource)
|
|
if resource ~= cache.resource then return end
|
|
Wait(100)
|
|
if Config.autoRespawn then
|
|
Storage.moveOutVehiclesIntoGarages()
|
|
end
|
|
end)
|
|
|
|
---@param vehicleId string
|
|
---@return boolean? success true if successfully paid
|
|
lib.callback.register('qbx_garages:server:payDepotPrice', function(source, vehicleId)
|
|
local player = exports.qbx_core:GetPlayer(source)
|
|
local cashBalance = player.PlayerData.money.cash
|
|
local bankBalance = player.PlayerData.money.bank
|
|
|
|
local vehicle = exports.qbx_vehicles:GetPlayerVehicle(vehicleId)
|
|
local depotPrice = vehicle.depotPrice
|
|
if not depotPrice or depotPrice == 0 then return true end
|
|
if cashBalance >= depotPrice then
|
|
player.Functions.RemoveMoney('cash', depotPrice, 'paid-depot')
|
|
return true
|
|
elseif bankBalance >= depotPrice then
|
|
player.Functions.RemoveMoney('bank', depotPrice, 'paid-depot')
|
|
return true
|
|
end
|
|
end)
|