Volver al blog
Tutorial9 min de lectura

Sistema de alquiler de vehículos para FiveM: programa vehículos temporales

Añade vehículos de alquiler a tu servidor FiveM con llaves temporales, depósitos y devolución automática. Scripts para QBCore, ESX y standalone.

Agency Scripts

Fundador y desarrollador principal en Agency Scripts

Por qué los sistemas de alquiler mejoran el roleplay

No todos los jugadores de un servidor de roleplay tienen un vehículo, sobre todo los personajes nuevos que acaban de empezar. Un sistema de alquiler de coches y motos cubre esa necesidad al ofrecer transporte temporal a precios asequibles, crear oportunidades naturales de roleplay en los mostradores de alquiler y dar a los jugadores nuevos la libertad de explorar el mapa sin depender del transporte público ni tener que ir a pie. Además del transporte básico, los sistemas de alquiler permiten hacer roleplay turístico, donde los visitantes alquilan descapotables para recorrer paisajes, realizar trabajos de reparto que requieren tipos de vehículos específicos y organizar eventos donde los jugadores necesitan vehículos iguales para paseos en grupo o carreras. El sistema también genera un flujo de ingresos pasivos para la economía del servidor mediante las tarifas de alquiler, los depósitos por daños y las penalizaciones por devolver los vehículos tarde.

Configuración de flotas y categorías de vehículos

Organiza tu flota de alquiler en categorías que se adapten a las distintas necesidades y presupuestos de los jugadores. Los vehículos económicos ofrecen transporte básico y asequible, los coches de gama media ofrecen más comodidad y rendimiento, los vehículos prémium son ideales para experiencias de lujo y las motocicletas ofrecen una alternativa de dos ruedas. Cada categoría debe incluir varias opciones de vehículos con precios claros. Incluye tarifas por hora y por día para que los jugadores puedan elegir la opción más económica según sus necesidades. Define la flota en una tabla de configuración que especifique todos los vehículos disponibles junto con su precio de alquiler, depósito requerido, categoría y modelo de aparición:

Config.RentalLocations = {
    ['airport'] = {
        label = 'LS Airport Car Rental',
        pedModel = 's_f_y_airhostess_01',
        pedCoords = vector4(-1037.34, -2736.82, 20.17, 238.52),
        spawnPoint = vector4(-1029.12, -2728.91, 20.17, 148.76),
        returnZone = vector3(-1035.00, -2730.00, 20.17),
        returnRadius = 10.0,
        categories = {'economy', 'midrange', 'premium', 'motorcycle'},
    },
    ['downtown'] = {
        label = 'Downtown Bike Rental',
        pedModel = 'a_m_y_business_02',
        pedCoords = vector4(-259.68, -892.41, 31.08, 69.24),
        spawnPoint = vector4(-264.30, -888.12, 31.08, 339.87),
        returnZone = vector3(-260.00, -890.00, 31.08),
        returnRadius = 8.0,
        categories = {'motorcycle', 'bicycle'},
    },
}

Config.RentalFleet = {
    economy = {
        {model = 'blista', label = 'Blista', priceHour = 150, priceDay = 800, deposit = 500},
        {model = 'asea', label = 'Asea', priceHour = 120, priceDay = 650, deposit = 400},
        {model = 'stanier', label = 'Stanier', priceHour = 130, priceDay = 700, deposit = 450},
    },
    midrange = {
        {model = 'schafter2', label = 'Schafter', priceHour = 300, priceDay = 1600, deposit = 1000},
        {model = 'oracle', label = 'Oracle', priceHour = 280, priceDay = 1500, deposit = 900},
    },
    premium = {
        {model = 'felon', label = 'Felon GT', priceHour = 600, priceDay = 3200, deposit = 2500},
        {model = 'exemplar', label = 'Exemplar', priceHour = 550, priceDay = 3000, deposit = 2000},
    },
    motorcycle = {
        {model = 'pcj', label = 'PCJ-600', priceHour = 100, priceDay = 500, deposit = 300},
        {model = 'sanchez', label = 'Sanchez', priceHour = 80, priceDay = 400, deposit = 250},
    },
    bicycle = {
        {model = 'cruiser', label = 'Cruiser Bike', priceHour = 30, priceDay = 150, deposit = 50},
        {model = 'tribike', label = 'Tri-Cycles Race', priceHour = 25, priceDay = 120, deposit = 50},
    },
}

Proceso de alquiler e interfaz NUI

El proceso de alquiler comienza cuando un jugador se acerca al NPC del mostrador de alquiler. Mediante un sistema de interacción o una comprobación de proximidad, abre un menú NUI que muestre las categorías de vehículos disponibles y opciones de filtrado. Cada vehículo anunciado debe mostrar el nombre del modelo, una imagen de vista previa si es posible, las tarifas por hora y por día, el depósito requerido y su disponibilidad. Cuando el jugador selecciona un vehículo y confirma la duración del alquiler, el servidor procesa la transacción. El cargo total corresponde a la tarifa de alquiler por el período seleccionado más el depósito por daños. El sistema retiene el depósito y lo devuelve cuando se entrega el vehículo sin daños. Implementa la confirmación del alquiler como un evento del servidor que valide que el jugador tiene fondos suficientes, compruebe que el vehículo seleccionado está disponible, cobre al jugador y genere el vehículo de alquiler en el punto de aparición designado:

RegisterNetEvent('rental:server:rentVehicle', function(locationId, vehicleModel, duration)
    local src = source
    local Player = QBCore.Functions.GetPlayer(src)
    if not Player then return end

    local location = Config.RentalLocations[locationId]
    if not location then return end

    -- Check if player already has an active rental
    if ActiveRentals[Player.PlayerData.citizenid] then
        TriggerClientEvent('QBCore:Notify', src, 'You already have an active rental', 'error')
        return
    end

    -- Find vehicle config
    local vehicleConfig = nil
    for _, category in pairs(Config.RentalFleet) do
        for _, v in ipairs(category) do
            if v.model == vehicleModel then
                vehicleConfig = v
                break
            end
        end
        if vehicleConfig then break end
    end

    if not vehicleConfig then return end

    -- Calculate cost
    local rentalFee = duration == 'day' and vehicleConfig.priceDay or vehicleConfig.priceHour
    local totalCost = rentalFee + vehicleConfig.deposit

    if Player.Functions.GetMoney('bank') < totalCost then
        TriggerClientEvent('QBCore:Notify', src, 'Insufficient funds. Need $' .. totalCost, 'error')
        return
    end

    -- Charge player
    Player.Functions.RemoveMoney('bank', totalCost, 'vehicle-rental')

    -- Generate rental plate
    local plate = 'RNT' .. math.random(10000, 99999)

    -- Store active rental
    ActiveRentals[Player.PlayerData.citizenid] = {
        model = vehicleModel,
        plate = plate,
        deposit = vehicleConfig.deposit,
        rentalFee = rentalFee,
        startTime = os.time(),
        duration = duration,
        expiresAt = os.time() + (duration == 'day' and 86400 or 3600),
        locationId = locationId,
        initialHealth = 1000,
    }

    TriggerClientEvent('rental:client:spawnVehicle', src, vehicleModel, plate, location.spawnPoint)
    TriggerClientEvent('QBCore:Notify', src, 'Vehicle rented! Return before your time expires.', 'success')
end)

Depósito por daños y mecánicas de devolución

El sistema de depósitos por daños responsabiliza a los jugadores por el trato que dan a los vehículos de alquiler. Cuando se devuelve el vehículo, compara sus valores de salud actuales con los valores iniciales registrados al alquilarlo. Los vehículos de GTA tienen valores de salud independientes para el motor, la carrocería y cada uno de sus componentes, lo que te permite evaluar los daños con precisión. Calcula el coste de reparación según los daños sufridos y descuéntalo del depósito antes de devolver el resto. Si los daños superan el importe del depósito, el jugador no recibe ningún reembolso y se le puede cobrar una tarifa adicional. Si el vehículo se devuelve en perfecto estado, se reembolsa el depósito completo. Este sistema fomenta la conducción cuidadosa sin penalizar por arañazos leves, inevitables debido a la física de conducción de GTA:

RegisterNetEvent('rental:server:returnVehicle', function(vehicleNet)
    local src = source
    local Player = QBCore.Functions.GetPlayer(src)
    if not Player then return end

    local citizenid = Player.PlayerData.citizenid
    local rental = ActiveRentals[citizenid]
    if not rental then return end

    local vehicle = NetworkGetEntityFromNetworkId(vehicleNet)
    if not DoesEntityExist(vehicle) then return end

    -- Check plate matches
    local plate = GetVehicleNumberPlateText(vehicle)
    if plate:trim() ~= rental.plate then
        TriggerClientEvent('QBCore:Notify', src, 'This is not your rental vehicle', 'error')
        return
    end

    -- Calculate damage
    local bodyHealth = GetVehicleBodyHealth(vehicle)
    local engineHealth = GetVehicleEngineHealth(vehicle)
    local damagePercent = 1.0 - ((bodyHealth + engineHealth) / 2000.0)
    local repairCost = math.floor(rental.deposit * damagePercent)

    -- Calculate late fee
    local lateFee = 0
    if os.time() > rental.expiresAt then
        local overHours = math.ceil((os.time() - rental.expiresAt) / 3600)
        lateFee = overHours * 200
    end

    -- Refund deposit minus costs
    local refund = math.max(0, rental.deposit - repairCost - lateFee)

    if refund > 0 then
        Player.Functions.AddMoney('bank', refund, 'rental-deposit-refund')
    end

    DeleteEntity(vehicle)
    ActiveRentals[citizenid] = nil

    local message = string.format('Vehicle returned. Deposit: $%d | Repairs: $%d | Late fee: $%d | Refund: $%d',
        rental.deposit, repairCost, lateFee, refund)
    TriggerClientEvent('QBCore:Notify', src, message, refund > 0 and 'success' or 'warning')
end)

Sistema de caducidad y devolución automática

Los vehículos de alquiler no deberían permanecer activos indefinidamente. Implementa un sistema de temporizador que haga un seguimiento de cuándo vence cada alquiler y actúe en consecuencia. Envía una notificación al jugador treinta minutos antes del vencimiento, otra cuando falten diez minutos y una última advertencia cuando falten cinco minutos. Cuando venza el alquiler, empieza a cobrar recargos por minuto de retraso hasta alcanzar un límite máximo. Si el jugador no devuelve el vehículo durante el período de gracia posterior al vencimiento, elimina automáticamente el vehículo y confisca todo el depósito. Esto evita que los vehículos de alquiler abandonados llenen el servidor. La comprobación del vencimiento debe ejecutarse periódicamente en el servidor, recorriendo todos los alquileres activos y actuando según el tiempo restante. Guarda el ID de red del vehículo para poder eliminarlo a distancia si es necesario, y contempla el caso de que el vehículo se destruya antes de que venza el alquiler, cerrándolo automáticamente y confiscando el depósito completo.

Varias ubicaciones de alquiler

Coloca los puntos de alquiler en lugares estratégicos del mapa donde el transporte temporal tenga más sentido. El aeropuerto es una opción obvia, ya que atiende a los jugadores que acaban de llegar a la ciudad. Las ubicaciones del centro, cerca de edificios gubernamentales, atienden a los jugadores que asisten a juicios o reuniones de negocios. Las ubicaciones de la playa ofrecen motocicletas y bicicletas para actividades recreativas a lo largo de la costa. Cada ubicación puede ofrecer una selección distinta de la flota: el aeropuerto tendrá toda la gama, desde vehículos económicos hasta prémium, mientras que las ubicaciones de la playa solo ofrecerán motocicletas y bicicletas. Personaliza el modelo del NPC de cada ubicación para que encaje con el entorno. Los alquileres del aeropuerto usan un modelo profesional de negocios, los de la playa uno informal de surfista y los del centro uno de empleado con traje. Esta atención al detalle hace que cada punto de alquiler parezca un negocio distinto, en vez de una copia idéntica del mismo sistema.

Seguimiento GPS y medidas antirrobo

Los vehículos de alquiler deberían tener seguimiento GPS integrado para que la empresa de alquiler y el jugador puedan localizarlos en cualquier momento. Muestra la ubicación del vehículo de alquiler en el mapa del jugador con un icono de blip distintivo. Si el vehículo se aleja más allá de un límite configurable, envía una notificación de advertencia. Implementa medidas antirrobo que impidan que otros jugadores guarden o aparquen en un garaje un vehículo de alquiler, para garantizar que siga bajo el control del sistema de alquileres. Cuando un jugador intente guardar un vehículo de alquiler en su garaje personal, el sistema debe bloquear la acción con una notificación que explique que no se pueden guardar vehículos de alquiler. Para las motocicletas y bicicletas, añade un candado virtual que solo pueda abrir el jugador que las alquiló, para evitar robos oportunistas. Si roban un vehículo de alquiler, el jugador que lo alquiló debería poder denunciarlo mediante una aplicación del teléfono, lo que activa una señal GPS que le ayuda a localizarlo. Registra el kilometraje total durante el alquiler para posibles recargos por kilometraje elevado, añadiendo otra capa de gestión realista de los alquileres de vehículos.

Integración con negocios y alquileres de propiedad de los jugadores

En servidores con un sistema de propiedad de negocios, permite que los jugadores compren y gestionen su propio negocio de alquileres. El propietario del negocio configura la flota comprando vehículos para añadirlos al grupo de alquileres, fija los precios, contrata empleados para atender el mostrador y recauda los ingresos de los alquileres. Los propietarios de negocios deberían tener un panel de gestión que muestre los alquileres activos, los ingresos diarios, el nivel de utilización de la flota y los costes de mantenimiento de los vehículos que acumulan daños con el paso de los alquileres. Los empleados pueden encargarse de las operaciones de atención al público, como tramitar alquileres y devoluciones, inspeccionar los vehículos devueltos para detectar daños y escalar las disputas al propietario. Este enfoque impulsado por los jugadores transforma el sistema de alquileres, de una mecánica de juego estática a un negocio dinámico que refleja el espíritu emprendedor de los servidores de roleplay y crea oportunidades de empleo para los jugadores que prefieren los roles de atención al cliente a los de acción intensa.

¿Listo para empezar?

Consigue los scripts en nuestra tienda, o únete al Discord para soporte, novedades y un adelanto de lo que viene.