[RELEASE] Vehicle Selling & Auction System

A script by Gamzky

No reviews yet.
[RELEASE] Vehicle Selling & Auction System main image

Full Description

:fire: New Vehicle Selling and Auction System :red_car:

Introducing a powerful vehicle selling system with a variety of interactive features for both direct selling and auctions. This system is perfect for any FiveM server looking to enhance vehicle trade dynamics.

Get it here: gamzkystore.com

Support: Discord

Preview video:

Key Features:

Advanced Vehicle Listing

  • Players can sell their vehicles using two methods (configurable):
    • Direct Sale: Set a fixed price for immediate purchase.
    • Auction: Players can list vehicles for auction, allowing others to place bids.
  • Detailed UI for setting price, sale type, and listing/auction duration (from 12 hours up to a full week).

Auction System

  • Place bids and track auctions in real-time. Players receive updates on the current highest bid and time remaining (while the menu is open).

Detailed Vehicle Info Menu

  • Each vehicle for sale comes with a detailed info dialog showing:
    • Vehicle name, plate, price, and sale expiration.
    • Performance modifications including engine, transmission, suspension, brakes, armor and turbo.

Secure

  • Secure events ensure proper validation when interacting with vehicles.
  • The system checks listing eligibility server-sided to prevent exploits or invalid entries.
  • It’s also possible to add your own checks to prevent unwanted listings.

Optimized

  • Automatically spawns vehicles at predefined spots when players approach the area.
  • Vehicles despawn when players move away, optimizing performance.
  • Removes unnecessary data from the vehicles to limit the amount of data being sent to the client.

Vehicle Properties & Tuning

  • Full support for vehicle properties, preserving modifications.

Installation

  • The resource is pretty much drag and drop. The database table will automatically be created if it doesn’t exist yet. It’s made for the latest version of ESX, but is easily adaptable to other frameworks using the bridge functions. (Contact me if you need help with this.)
  • If you want to prevent players taking their vehicles out of their garage, you can use the following export: exports.gs_sellvehicles:IsVehicleListed(plate)

If you have any questions or issues, feel free to reach out to me.

Accessible files:

Config
Config = {
    debug = false,                                  -- Disables some checks (Place bids on own vehicle, overwrite own bid, etc.) to make it easier for testing.
    requestZone = vector3(-207.9, -1964.03, 28.63), -- Coordinates of the zone in which the vehicles will be loaded.
    requestZoneRadius = 150,                        -- Radius of the zone in which the vehicles will be loaded.
    preventParkedVehicles = true,                   -- Prevent parked vehicles from being spawned in the spots.
    blip = {
        coords = vector3(-207.02, -2000.33, 27.74),
        sprite = 225,
        color = 3,
        scale = 1.1,
        label = 'Used Cars',
        description = 'The place to sell your car or buy a used vehicle.'
    },
    extendAuction = true,        -- Extend auction with 5 minutes if there is less than 5 minutes left and someone places a bid
    maxPerPlayer = 3,            -- Max number of vehicles a player can have listed
    minBidIncrease = 5000,       -- Minimum increase in bid price
    minListingPrice = 5000,      -- Minimum listing price
    maxListingPrice = 100000000, -- Maximum listing price
    sellMarker = {
        coords = vector3(-207.02, -2000.33, 27.74),
        type = 1,
        size = 1.5,
        color = 2,
    },
    sellOptions = { -- Remove an option from this list to disable it
        'direct',
        'auction',
    },
    durationOptions = {
        { hours = 12,  label = '12 hours' },
        { hours = 24,  label = '1 day' },
        { hours = 48,  label = '2 days' },
        { hours = 72,  label = '3 days',  default = true },
        { hours = 168, label = '1 week' },
    }
}

Config.Locales = {
    ['currency_separator'] = ',',
    ['loading'] = 'Loading...',
    ['sell_vehicle'] = 'Sell vehicle',
    ['buy_vehicle_for'] = 'Buy vehicle for $%s',
    -- Left out for display purposes...
}

tuneLabels = {
    modEngine = {
        [-1] = 'Stock Engine',
        [0] = 'EMS Upgrade, Level 1',
        [1] = 'EMS Upgrade, Level 2',
        [2] = 'EMS Upgrade, Level 3',
        [3] = 'EMS Upgrade, Level 4',
    },
    -- Left out for display purposes...
}

Spots = {
    vector4(-210.56, -1958.84, 27.22, 288.22),
    vector4(-209.69, -1961.79, 27.22, 287.11),
    vector4(-208.73, -1964.64, 27.22, 284.9),
    -- Left out for display purposes...
}

Client Bridge Functions
ESX = exports.es_extended:getSharedObject()

Functions = {}

Functions.SetVehicleProperties = function(vehicle, props)
    lib.setVehicleProperties(vehicle, props)
end

Functions.SpawnLocalVehicle = function(modelHash, coords, heading)
    lib.requestModel(modelHash)

    local vehicle = CreateVehicle(modelHash, coords.x, coords.y, coords.z, heading, false, true)

    SetModelAsNoLongerNeeded(modelHash)

    return vehicle
end

Functions.CanInteract = function(data)
    local playerPed = PlayerPedId()

    if IsPedInAnyVehicle(playerPed, false) then
        return false
    end

    return true
end

Functions.Notify = function(message)
    ESX.ShowNotification(message)
end

Functions.ApplyVehicleEffects = function(entity)
    -- Optimizations
    SetVehicleLodMultiplier(entity, 0.75)
    SetEntityLodDist(entity, 75)

    -- Freeze
    SetVehicleOnGroundProperly(entity)
    FreezeEntityPosition(entity, true)

    -- Lock doors
    SetVehicleDoorsLocked(entity, 2)

    -- Clean vehicle
    SetVehicleDirtLevel(entity, 0.0)

    -- Make vehicle invincible
    SetEntityInvincible(entity, true)
    SetEntityCanBeDamaged(entity, false)
    SetEntityProofs(entity, true, true, true, true, true, true, true, true)
end

Server Bridge Functions
ESX = exports.es_extended:getSharedObject()

Functions = {}

Functions.GetIdentifier = function(playerId)
    local xPlayer = ESX.GetPlayerFromId(playerId)
    return xPlayer.getIdentifier()
end

Functions.GetPlayerName = function(playerId)
    local xPlayer = ESX.GetPlayerFromId(playerId)
    return xPlayer.getName()
end

Functions.HasMoney = function(playerId, amount)
    local xPlayer = ESX.GetPlayerFromId(playerId)
    return xPlayer.getAccount('bank').money >= amount
end

Functions.RemoveMoney = function(playerId, amount)
    local xPlayer = ESX.GetPlayerFromId(playerId)
    xPlayer.removeAccountMoney('bank', amount)
end

Functions.AddMoney = function(playerId, amount)
    local xPlayer = ESX.GetPlayerFromId(playerId)
    xPlayer.addAccountMoney('bank', amount)
end

Functions.AddMoneyByIdentifier = function(identifier, amount)
    local xPlayer = ESX.GetPlayerFromIdentifier(identifier)
    if xPlayer then
        xPlayer.addAccountMoney('bank', amount)
    else
        -- Player is not online, add money directly in the database
        local curAccounts = MySQL.scalar.await('SELECT `accounts` FROM `users` WHERE `identifier` = ?', { identifier })
        if curAccounts then
            local accounts = json.decode(curAccounts)
            accounts.bank = accounts.bank + amount
            MySQL.update.await('UPDATE `users` SET `accounts` = ? WHERE `identifier` = ?', { json.encode(accounts), identifier })
        end
    end
end

Functions.TransferVehicleOwnership = function(identifier, plate)
    -- Set new owner and set vehicle as stored
    MySQL.update.await('UPDATE `owned_vehicles` SET `owner` = ?, `stored` = 1 WHERE `plate` = ?', { identifier, plate })
end

-- Add custom logic to check if a player can list a vehicle
Functions.CanListVehicle = function(playerId, data)
    local netId = data.netId
    local modelHash = data.modelHash -- Vehicle model hash
    local plate = data.plate

    -- Example when a player can not list a vehicle
    if (playerId == -1) then
        return {
            canList = false,
            message = '~r~You cannot sell this vehicle right now.'
        }
    end

    return {
        canList = true,
    }
end

AddEventHandler('gs_sellvehicles:OnVehicleSold', function(data)
    local buyerId = data.buyerId -- Buyer server ID
    local buyerIdentifier = data.buyerIdentifier
    local sellerIdentifier = data.sellerIdentifier
    local plate = data.plate
    local price = data.price
    local vehicleLabel = data.label

    -- Example: Add logging, or notify the seller
end)

AddEventHandler('gs_sellvehicles:OnAuctionEnd', function(data)
    local wasSold = data.wasSold
    local plate = data.plate
    local highestBid = data.highestBid
    local highestBidder = data.highestBidder -- Identifier of the player who bid the highest
    local seller = data.seller               -- Identifier of the player who listed the vehicle

    -- Example: Add logging, or notify the seller
end)

-- Exports
exports('IsVehicleListed', IsVehicleListed)
Commands (Server)
-- Commands for managing vehicle listings
lib.addCommand('removevehlisting', {
    help = 'Removes a vehicle listing.',
    restricted = { 'group.superadmin' },
    params = {
        { name = 'plate', help = 'The plate of the vehicle.', type = 'string', optional = true }
    }
}, function(source, args, raw)
    local plate = args.plate
    if not plate then
        return TriggerClientEvent('chat:addMessage', source, { args = { '^1ERROR', 'No plate specified.' } })
    end

    local vehicle = GetListingByPlate(plate)
    if not vehicle then
        return TriggerClientEvent('chat:addMessage', source, { args = { '^1ERROR', 'No listing found for the specified plate.' } })
    end

    if (vehicle.sell_type ~= 'direct') then
        return TriggerClientEvent('chat:addMessage', source, { args = { '^1ERROR', 'Use /endvehauction to end a direct sale.' } })
    end

    vehicle:delete()

    TriggerClientEvent('chat:addMessage', source, { args = { '^2SUCCESS', string.format('Vehicle listing with plate %s has been removed.', plate) } })
end)

-- Command to end a vehicle auction
lib.addCommand('endvehauction', {
    help = 'Ends a vehicle auction.',
    restricted = { 'group.superadmin' },
    params = {
        { name = 'plate', help = 'The plate of the vehicle.', type = 'string', optional = true }
    }
}, function(source, args, raw)
    local plate = args.plate
    if not plate then
        return TriggerClientEvent('chat:addMessage', source, { args = { '^1ERROR', 'No plate specified.' } })
    end

    local vehicle = GetListingByPlate(plate)
    if not vehicle then
        return TriggerClientEvent('chat:addMessage', source, { args = { '^1ERROR', string.format("No listing found with plate: '%s'", plate) } })
    end

    if (vehicle.sell_type ~= 'auction') then
        return TriggerClientEvent('chat:addMessage', source, { args = { '^1ERROR', 'Vehicle is not an auction.' } })
    end

    -- End the auction
    vehicle:endAuction()

    TriggerClientEvent('chat:addMessage', source, { args = { '^2SUCCESS', 'Vehicle auction ended.' } })
end)

Code is accessible No, but core functions are accessible
Subscription-based No
Lines (approximately) 2080
Requirements ox_lib, ox_target, oxmysql
Support Yes