>
Tutoriel 2026-05-22

Système de dépanneuse et fourrière pour FiveM

TDYSKY

TDYSKY

Fondateur et développeur principal chez Agency Scripts

Présentation du système de dépanneuse

Un système de dépanneuse et de fourrière crée tout un écosystème d'emplois sur ton serveur FiveM, offrant aux joueurs un cheminement de carrière civil qui interagit naturellement avec les forces de l'ordre, les mécaniciens et les autres conducteurs. Le système implique plusieurs composants interconnectés : une couche de gestion des tâches qui gère les pointages d'entrée et de sortie, un système de répartition qui achemine les demandes de remorquage aux conducteurs disponibles, des mécanismes de fixation des véhicules qui simulent un remorquage réaliste, une fourrière qui stocke les véhicules saisis et abandonnés et une structure tarifaire qui régit le coût de récupération des véhicules mis en fourrière. Lorsqu'il est bien conçu, le travail de dépanneuse devient l'un des rôles les plus socialement engageants sur le serveur, car chaque appel implique une interaction directe avec un autre joueur qui a besoin d'aide avec son véhicule en panne ou garé illégalement.

Configuration du travail et système de service

Commencez par définir le travail de la dépanneuse dans le système de travail de ton cadre. Les joueurs devraient pouvoir se présenter à un emplacement de dépannage, recevoir une dépanneuse de l'entreprise et commencer à accepter des appels de répartition. Le système de service doit suivre les chauffeurs de remorquage actifs afin que le système de répartition sache qui est disponible. Lorsqu'un joueur arrive, faites apparaître un camion à plateau à partir d'une liste prédéfinie de modèles de véhicules de remorquage et attribuez-le à ce joueur. À leur départ, le véhicule de société doit être restitué ou supprimé. Stockez le statut de service du joueur côté serveur afin qu'il persiste correctement et puisse être interrogé par le système de répartition :

Config.TowJob = {
    jobName = 'tow',
    clockInLocation = vector3(409.09, -1622.57, 29.29),
    vehicleSpawn = vector4(405.87, -1631.14, 29.29, 228.35),
    towVehicles = {
        {model = 'flatbed', label = 'Flatbed Truck', rank = 0},
        {model = 'towtruck', label = 'Tow Truck (Hook)', rank = 1},
        {model = 'towtruck2', label = 'Heavy Tow Truck', rank = 2},
    },
    impoundLot = vector3(409.59, -1637.87, 29.29),
    impoundReturn = vector4(403.12, -1640.23, 29.29, 138.92),
    payPerTow = {min = 350, max = 750},
    payPerImpound = 500,
}

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

    if Player.PlayerData.job.name ~= Config.TowJob.jobName then
        TriggerClientEvent('QBCore:Notify', src, 'You are not a tow driver', 'error')
        return
    end

    Player.Functions.SetJobDuty(true)
    TowDrivers[src] = {
        onDuty = true,
        currentCall = nil,
        vehicleNet = nil,
        towedVehicle = nil,
    }

    TriggerClientEvent('QBCore:Notify', src, 'You are now on duty', 'success')
    TriggerClientEvent('tow:client:spawnVehicle', src)
end)

Mécanique de fixation des véhicules

Le principal défi technique d'un système de remorquage consiste à attacher un véhicule à un autre d'une manière qui semble réaliste et se comporte correctement pendant la conduite. FiveM fournit le AttachEntityToEntity natif à cet effet, mais obtenir les bonnes positions de décalage nécessite un calibrage minutieux pour chaque modèle de dépanneuse. L'approche à plateau fonctionne mieux pour la plupart des véhicules : la voiture remorquée est placée au-dessus de la plate-forme avec des décalages appropriés afin qu'elle repose naturellement sur le lit. Pour les dépanneuses à crochet, les roues avant du véhicule remorqué sont soulevées tandis que les roues arrière restent au sol. Chaque style d'attachement nécessite des vecteurs de décalage et des valeurs de rotation différents. Testez ces valeurs dans le jeu en effectuant de petits ajustements jusqu'à ce que le véhicule soit correctement installé sans passer par le camion :

function AttachVehicleToFlatbed(towTruck, targetVehicle)
    local towModel = GetEntityModel(towTruck)

    -- Offsets calibrated for the flatbed model
    local offsets = {
        ['flatbed'] = {x = 0.0, y = -0.5, z = 1.1, rx = 0.0, ry = 0.0, rz = 0.0},
    }

    local offset = offsets['flatbed']
    if not offset then return false end

    -- Freeze target vehicle physics
    SetEntityCollision(targetVehicle, false, false)
    FreezeEntityPosition(targetVehicle, true)

    -- Attach to flatbed
    AttachEntityToEntity(
        targetVehicle, towTruck,
        0, -- bone index
        offset.x, offset.y, offset.z,
        offset.rx, offset.ry, offset.rz,
        false, false, false, false, 0, true
    )

    -- Store attachment state
    TriggerServerEvent('tow:server:vehicleAttached', VehToNet(towTruck), VehToNet(targetVehicle))
    return true
end

function DetachVehicleFromFlatbed(towTruck, targetVehicle)
    DetachEntity(targetVehicle, true, true)
    SetEntityCollision(targetVehicle, true, true)
    FreezeEntityPosition(targetVehicle, false)

    -- Place vehicle on ground behind the truck
    local truckCoords = GetEntityCoords(towTruck)
    local truckHeading = GetEntityHeading(towTruck)
    local behind = GetOffsetFromEntityInWorldCoords(towTruck, 0.0, -12.0, 0.0)

    SetEntityCoords(targetVehicle, behind.x, behind.y, behind.z)
    SetEntityHeading(targetVehicle, truckHeading)
    SetVehicleOnGroundProperly(targetVehicle)

    TriggerServerEvent('tow:server:vehicleDetached', VehToNet(towTruck))
end

Un détail important est la désactivation des collisions sur le véhicule attaché pour éviter les problèmes physiques. Lorsque deux véhicules entrent en collision active et sont attachés, le moteur du jeu peut produire de violentes secousses ou lancer les véhicules dans les airs. Geler la position du véhicule cible et désactiver sa collision lorsqu'il est attaché élimine complètement ces problèmes.

Système de demande de répartition et de remorquage

Le système de répartition met en relation les joueurs qui ont besoin d’un remorquage avec les chauffeurs de dépanneuse disponibles. Les joueurs peuvent demander un remorquage via leur téléphone, via un PNJ d'atelier de mécanique ou via des cabines d'appel en bordure de route placées autour de la carte. Lorsqu'une demande arrive, le serveur vérifie les chauffeurs de remorquage disponibles et achemine l'appel vers le plus proche. Si aucun chauffeur n'est en service, proposez un service de remorquage PNJ de secours qui facture des frais plus élevés et téléporte le véhicule à la fourrière après un délai. La notification d'expédition doit inclure l'emplacement du demandeur, le modèle du véhicule et la raison du remorquage, telle qu'une panne, un accident ou un stationnement illégal. Donnez au conducteur du remorqueur la possibilité d'accepter ou de refuser l'appel, les appels refusés étant acheminés vers le conducteur le plus proche :

RegisterNetEvent('tow:server:requestTow', function(reason)
    local src = source
    local Player = QBCore.Functions.GetPlayer(src)
    if not Player then return end

    local playerCoords = GetEntityCoords(GetPlayerPed(src))
    local vehicle = GetVehiclePedIsIn(GetPlayerPed(src), true)
    local vehicleModel = vehicle ~= 0 and GetDisplayNameFromVehicleModel(GetEntityModel(vehicle)) or 'Unknown'

    -- Find nearest available tow driver
    local nearestDriver = nil
    local nearestDist = math.huge

    for driverId, data in pairs(TowDrivers) do
        if data.onDuty and not data.currentCall then
            local driverCoords = GetEntityCoords(GetPlayerPed(driverId))
            local dist = #(playerCoords - driverCoords)
            if dist < nearestDist then
                nearestDist = dist
                nearestDriver = driverId
            end
        end
    end

    if nearestDriver then
        TowDrivers[nearestDriver].currentCall = {
            requesterId = src,
            coords = playerCoords,
            vehicle = vehicleModel,
            reason = reason,
            timestamp = os.time(),
        }

        TriggerClientEvent('tow:client:incomingCall', nearestDriver, {
            coords = playerCoords,
            vehicle = vehicleModel,
            reason = reason,
            playerName = Player.PlayerData.charinfo.firstname,
        })

        TriggerClientEvent('QBCore:Notify', src, 'Tow truck dispatched. ETA based on driver location.', 'success')
    else
        TriggerClientEvent('QBCore:Notify', src, 'No tow drivers available. NPC service will arrive shortly.', 'info')
        StartNPCTowService(src, playerCoords, vehicle)
    end
end)

Gestion des lots de fourrière

La fourrière est l'endroit où les véhicules remorqués et saisis sont entreposés jusqu'à ce que leurs propriétaires les récupèrent. Concevez la base de données de mise en fourrière pour savoir quels véhicules sont actuellement mis en fourrière, qui les a mis en fourrière, quand ils ont été mis en fourrière et les frais requis pour leur libération. La police devrait être en mesure de mettre les véhicules en fourrière directement lors des contrôles routiers ou après des poursuites, ces fourrières entraînant des frais plus élevés ou des délais de détention obligatoires plus longs. L'interface de la fourrière doit afficher une liste des véhicules mis en fourrière du joueur avec des détails tels que le modèle du véhicule, le numéro de plaque, la date de mise en fourrière, la raison et les frais de sortie. Les joueurs paient les frais au comptoir de la fourrière et leur véhicule apparaît à un point de retour désigné à proximité :

CREATE TABLE IF NOT EXISTS impounded_vehicles (
    id INT AUTO_INCREMENT PRIMARY KEY,
    plate VARCHAR(10) NOT NULL,
    citizenid VARCHAR(50) NOT NULL,
    vehicle_data LONGTEXT NOT NULL,
    impound_reason ENUM('tow', 'police', 'abandoned', 'parking') DEFAULT 'tow',
    impound_fee INT DEFAULT 250,
    impounded_by VARCHAR(50) DEFAULT NULL,
    impounded_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    hold_until TIMESTAMP DEFAULT NULL,
    released_at TIMESTAMP DEFAULT NULL,
    INDEX idx_plate (plate),
    INDEX idx_citizen (citizenid),
    INDEX idx_released (released_at)
);

-- Server-side: Impound a vehicle
RegisterNetEvent('tow:server:impoundVehicle', function(vehicleNet, reason)
    local src = source
    local Player = QBCore.Functions.GetPlayer(src)
    if not Player then return end

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

    local plate = GetVehicleNumberPlateText(vehicle)
    local vehicleData = QBCore.Functions.GetVehicleProperties(vehicle)
    local fee = Config.ImpoundFees[reason] or 250

    MySQL.insert('INSERT INTO impounded_vehicles (plate, citizenid, vehicle_data, impound_reason, impound_fee, impounded_by) VALUES (?, ?, ?, ?, ?, ?)', {
        plate:trim(),
        GetVehicleOwner(plate),
        json.encode(vehicleData),
        reason,
        fee,
        Player.PlayerData.citizenid,
    })

    DeleteEntity(vehicle)
    local pay = math.random(Config.TowJob.payPerImpound - 100, Config.TowJob.payPerImpound + 100)
    Player.Functions.AddMoney('bank', pay, 'tow-impound')
    TriggerClientEvent('QBCore:Notify', src, 'Vehicle impounded. Earned $' .. pay, 'success')
end)

Application automatisée du stationnement

Ajoutez un système automatisé qui détecte les véhicules garés dans des zones réglementées et les signale pour le remorquage après une période de grâce configurable. Définissez des zones de stationnement restreintes autour des bouches d’incendie, des arrêts de bus, des commissariats de police et des entrées d’urgence des hôpitaux. Lorsqu'un joueur se gare dans une zone réglementée et s'en va, démarrez un chronomètre. Si le véhicule reste sans surveillance après l'expiration du délai de grâce, générez une demande de remorquage et informez les conducteurs de remorquage disponibles. Cela crée un travail organique pour les conducteurs de dépanneuses sans obliger les joueurs à appeler manuellement le service. Le système ne doit signaler que les véhicules appartenant aux joueurs, car les véhicules des PNJ sont gérés par le moteur de jeu et disparaissent naturellement. Incluez une notification au propriétaire du véhicule indiquant que sa voiture a été signalée pour le remorquage, lui donnant la possibilité de revenir et de la déplacer avant l'arrivée de la dépanneuse.

Revenus et progression

Structurez le travail de remorquage avec un système de progression qui récompense un travail constant. Les nouveaux conducteurs de remorquage commencent au rang zéro avec accès au camion à plateau de base et gagnent un tarif de base par remorquage effectué. Au fur et à mesure qu'ils accumulent des remorquages ​​terminés, ils se classent et débloquent des véhicules de remorquage plus lourds capables de gérer des véhicules plus gros comme des camions et des bus, l'accès à des contrats de fourrière de la police qui paient plus et une dépanneuse personnelle qu'ils peuvent personnaliser. Suivez les statistiques telles que le nombre total de remorquages ​​effectués, la distance parcourue pendant le remorquage et le temps de réponse moyen pour répartir les appels. Affichez ces statistiques dans un tableau de bord de travail accessible via un PNJ ou une application téléphonique. Envisagez de mettre en place un système de réputation dans lequel les conducteurs de remorquage qui répondent systématiquement rapidement et terminent leur travail sans endommager les véhicules gagnent des multiplicateurs de bonus sur leur salaire, tandis que les conducteurs qui ignorent les appels ou endommagent les véhicules pendant le remorquage s'exposent à des pénalités salariales. Cela crée une incitation naturelle à la qualité qui profite à l'ensemble de la communauté des serveurs en garantissant que les services de remorquage sont fiables et professionnels.

Gestion des cas Edge et mise en réseau

Les systèmes de remorquage présentent plusieurs cas délicats qui nécessitent une manipulation soigneuse. Lorsqu'un conducteur de remorquage se déconnecte pendant le remorquage d'un véhicule, le serveur doit détecter la déconnexion et détacher et mettre à la terre en toute sécurité le véhicule remorqué afin qu'il ne flotte pas dans les airs ou ne tombe pas sur la carte. Si le propriétaire du véhicule se connecte alors que sa voiture est attachée à une dépanneuse, il devrait voir son véhicule marqué comme remorqué dans sa liste de véhicules avec des informations sur le conducteur du remorqueur. Gérez soigneusement la propriété de l'entité du véhicule, car le rattachement natif ne fonctionne de manière fiable que lorsque le client attachant est propriétaire des deux entités. Utiliser NetworkRequestControlOfEntity pour s'assurer que le client du conducteur du remorqueur contrôle les deux véhicules avant de tenter l'attachement. Pour la synchronisation multijoueur, diffusez l'état de l'attachement à tous les joueurs à proximité afin qu'ils voient le véhicule remorqué dans la bonne position sur leurs écrans. Les sacs d'État fonctionnent bien pour cela car ils se répliquent automatiquement sur tous les clients à portée de l'entité.

Partager cet article

Prêt à améliorer votre serveur ?

Découvrez nos scripts FiveM premium dans la boutique Agency Scripts ou rejoignez notre communauté Discord pour le support et les mises à jour.