>
Guide 2026-05-20

Système de braquages et casses pour FiveM

TDYSKY

TDYSKY

Fondateur et développeur principal chez Agency Scripts

Présentation de l'architecture du système de vol

Les vols et les braquages ​​font partie des boucles de jeu les plus excitantes sur n'importe quel serveur de jeu de rôle FiveM. Ils rassemblent des criminels planifiant une opération complexe, des policiers réagissant avec une précision tactique, des négociateurs en otage gérant des impasses tendues et des civils pris entre deux feux. Un système de vol bien conçu va bien au-delà d’une simple invite d’interaction qui remet de l’argent aux joueurs. Il nécessite une architecture en couches qui gère la gestion de l'emplacement, les vérifications des prérequis, les phases de mini-jeu progressifs, la distribution du butin, l'intégration des répartitions de police, la gestion des temps de recharge et les protections anti-exploit. Le système doit être construit avec trois couches principales : une couche de configuration qui définit chaque lieu de vol avec ses exigences et ses récompenses, une machine d'état côté serveur qui suit les vols actifs et applique toutes les règles, et une couche de présentation côté client qui restitue les mini-jeux, les animations et les interfaces NUI. Chaque décision critique doit être prise côté serveur, car la logique côté client est trivialement exploitable.

Configuration des emplacements et des niveaux de vol

Ton système de vol doit prendre en charge plusieurs types de lieux avec des niveaux de difficulté croissants. Les dépanneurs représentent le niveau d'entrée, nécessitant une présence policière minimale et des outils de base. Les succursales des banques Fleeca se situent au niveau intermédiaire, avec des niveaux de sécurité modérés et des exigences policières plus élevées. Le coffre-fort Pacific Standard constitue le braquage suprême, exigeant un équipage complet, un équipement de pointe et une présence importante des forces de l'ordre en ligne. Chaque emplacement nécessite une entrée de configuration qui définit ses coordonnées, les éléments requis, le nombre minimum de policiers, la durée du temps de recharge, la plage de récompenses et la séquence de couches de sécurité qui doivent être vaincues :

Config.Robberies = {
    ['store_1'] = {
        label = 'LTD Gasoline - Davis',
        type = 'store',
        coords = vector3(-47.02, -1757.23, 29.42),
        register = vector3(-43.43, -1748.60, 29.42),
        tier = 1,
        cooldown = 1800,  -- 30 minutes
        minPolice = 2,
        requiredItems = {'weapon_pistol'},
        reward = {min = 2500, max = 6000, type = 'cash'},
        securityLayers = {
            {type = 'intimidate', time = 30},
            {type = 'grab_cash', time = 15},
        },
    },
    ['fleeca_legion'] = {
        label = 'Fleeca Bank - Legion Square',
        type = 'bank',
        coords = vector3(149.73, -1042.65, 29.37),
        vault = vector3(144.87, -1044.16, 29.37),
        tier = 2,
        cooldown = 7200,  -- 2 hours
        minPolice = 4,
        requiredItems = {'electronickit', 'thermite'},
        reward = {min = 45000, max = 90000, type = 'markedbills'},
        securityLayers = {
            {type = 'hack', difficulty = 'medium', time = 25},
            {type = 'thermite', time = 10},
            {type = 'drill', time = 40},
        },
    },
    ['pacific_standard'] = {
        label = 'Pacific Standard Bank',
        type = 'vault',
        coords = vector3(255.85, 225.60, 101.88),
        vault = vector3(262.20, 222.10, 101.68),
        tier = 3,
        cooldown = 14400,  -- 4 hours
        minPolice = 6,
        requiredItems = {'electronickit', 'thermite', 'advancedlaptop', 'duffelbag'},
        reward = {min = 250000, max = 500000, type = 'markedbills'},
        securityLayers = {
            {type = 'hack', difficulty = 'hard', time = 20},
            {type = 'hack', difficulty = 'hard', time = 20},
            {type = 'thermite', time = 8},
            {type = 'drill', time = 60},
            {type = 'hack', difficulty = 'expert', time = 15},
        },
    },
}

Chaque niveau doit évoluer non seulement en termes de récompense, mais également en termes de complexité et de risque. Les vols de magasin sont des opérations rapides de type smash-and-gripp qui durent moins de deux minutes, tandis que le braquage du Pacific Standard est une opération en plusieurs phases qui peut prendre quinze minutes ou plus, laissant à la police suffisamment de temps pour établir un périmètre.

Machine à états côté serveur

La machine d’État du vol est le cœur du système. Il suit chaque lieu de vol tout au long de son cycle de vie : inactif, en cours, en temps de recharge et verrouillé. Lorsqu'un joueur lance un vol, le serveur valide toutes les conditions préalables avant de faire passer l'emplacement d'inactif à en cours. Pendant l'état en cours, le serveur suit sur quelle couche de sécurité se trouvent actuellement les criminels, quels joueurs participent et combien de temps s'est écoulé. Si tous les joueurs quittent la zone ou sont arrêtés, le vol échoue automatiquement et entre dans un temps de recharge raccourci. Une fois terminé, l'emplacement entre en état de refroidissement pendant la durée configurée avant de redevenir disponible :

local RobberyState = {}

function InitializeRobbery(robberyId)
    RobberyState[robberyId] = {
        status = 'idle',      -- idle | active | cooldown | locked
        participants = {},
        currentLayer = 0,
        startedAt = 0,
        cooldownUntil = 0,
    }
end

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

    local config = Config.Robberies[robberyId]
    local state = RobberyState[robberyId]
    if not config or not state then return end

    -- Validate state
    if state.status ~= 'idle' then
        TriggerClientEvent('QBCore:Notify', src, 'This location is not available', 'error')
        return
    end

    -- Check cooldown
    if os.time() < state.cooldownUntil then
        TriggerClientEvent('QBCore:Notify', src, 'Location on cooldown', 'error')
        return
    end

    -- Check minimum police online
    local policeCount = GetOnlinePolicCount()
    if policeCount < config.minPolice then
        TriggerClientEvent('QBCore:Notify', src, 'Not enough police online', 'error')
        return
    end

    -- Check required items
    for _, item in ipairs(config.requiredItems) do
        if not Player.Functions.GetItemByName(item) then
            TriggerClientEvent('QBCore:Notify', src, 'Missing required equipment', 'error')
            return
        end
    end

    -- Start robbery
    state.status = 'active'
    state.participants = {src}
    state.currentLayer = 1
    state.startedAt = os.time()

    -- Alert police dispatch
    TriggerEvent('dispatch:server:notify', {
        type = 'robbery',
        coords = config.coords,
        message = 'Silent alarm triggered at ' .. config.label,
        tier = config.tier,
    })

    -- Send first security layer to client
    TriggerClientEvent('robbery:client:startLayer', src, robberyId, config.securityLayers[1])
end)

La machine d'état doit également gérer les cas extrêmes tels que les redémarrages de serveur lors de vols actifs. Lorsque le serveur démarre, réinitialisez tous les états de vol au repos et effacez tous les participants actifs. Stockez les horodatages de temps de recharge dans une table persistante afin qu'ils survivent aux redémarrages.

Intégration de mini-jeux

Les mini-jeux transforment les vols d'une expérience d'attente passive en un défi de compétences actif. Chaque type de couche de sécurité doit correspondre à un style de mini-jeu différent qui teste les capacités des différents joueurs. Les mini-jeux de piratage peuvent utiliser des énigmes de correspondance de mémoire, des défis de connexion de circuits ou des séquences de nombres rapides. Le placement de thermite nécessite un timing précis pour placer les charges avant qu'elles ne s'éteignent. Les mécanismes de forage testent le contrôle constant de la souris tandis que le joueur fait passer un foret à travers un mécanisme de verrouillage sans surchauffe. Le principe clé de conception est que les niveaux les plus difficiles devraient utiliser des variantes plus complexes de ces mini-jeux avec des fenêtres de timing plus serrées, plus d'éléments à suivre ou des vitesses plus rapides :

-- Client-side minigame dispatcher
function StartMinigame(layerType, difficulty, callback)
    if layerType == 'hack' then
        local config = HackDifficulty[difficulty]
        exports['ps-ui']:Circle(function(success)
            callback(success)
        end, config.circles, config.speed)

    elseif layerType == 'thermite' then
        exports['ps-ui']:Thermite(function(success)
            callback(success)
        end, 10, 5, 3)

    elseif layerType == 'drill' then
        exports['drilling']:StartDrill(function(success)
            callback(success)
        end)

    elseif layerType == 'intimidate' then
        -- Store clerk intimidation: aim weapon at NPC
        StartClerkIntimidation(function(success)
            callback(success)
        end)
    end
end

RegisterNetEvent('robbery:client:startLayer', function(robberyId, layer)
    local ped = PlayerPedId()

    -- Play appropriate animation
    if layer.type == 'hack' then
        PlayHackingAnimation(ped)
    elseif layer.type == 'drill' then
        PlayDrillingAnimation(ped)
    end

    StartMinigame(layer.type, layer.difficulty, function(success)
        ClearPedTasks(ped)
        TriggerServerEvent('robbery:server:layerResult', robberyId, success)
    end)
end)

Les tentatives de mini-jeu ratées devraient avoir des conséquences. Un piratage raté peut déclencher des alarmes supplémentaires, réduire le temps restant ou verrouiller le joueur pendant une brève période de pénalité. Pour les braquages ​​de haut niveau, l’échec du piratage final pourrait verrouiller entièrement le coffre-fort, obligeant l’équipage à abandonner le vol avec seulement un butin partiel. Ces états d’échec ajoutent de la tension et rendent les réussites véritablement gratifiantes.

Intégration de la répartition et de la réponse de la police

Un système de vol n’est qu’à moitié complet sans l’intégration de la police. Lorsqu'un vol commence, le système de répartition doit envoyer des alertes classées en fonction du niveau de vol. Un vol dans un dépanneur génère une simple notification d'alarme silencieuse avec l'emplacement du magasin. Une agence Fleeca déclenche une alarme bancaire avec le nom de l'agence et un niveau de réponse suggéré. Le braquage du Pacific Standard active une diffusion d'urgence complète recommandant à toutes les unités disponibles de réagir. Fournissez à la police des mises à jour en temps réel à mesure que le vol progresse : lorsque des criminels franchissent un nouveau niveau de sécurité, lorsque des coups de feu sont tirés à l'intérieur du bâtiment ou lorsque des criminels tentent de fuir. Ces mises à jour aident la police à coordonner sa réponse et à créer des scénarios dynamiques dans lesquels les agents peuvent décider d'ouvrir une infraction immédiatement ou d'établir un périmètre et de négocier. Mettez en œuvre un mécanisme de prise d'otages grâce auquel les criminels peuvent prendre en otage les caissiers de banque des PNJ, donnant ainsi un rôle aux négociateurs de la police et fournissant aux criminels un levier pour retarder la réponse de la police. Le système de prise d'otages doit suivre le nombre d'otages détenus, savoir si certains ont été blessés, et fournir des invites de négociation par le biais d'appels téléphoniques entre les criminels et les agents qui ont répondu.

Distribution de butin et blanchiment d’argent

Ce que les joueurs reçoivent d'un vol compte autant que le vol lui-même. Au lieu de déposer de l’argent liquide directement sur des comptes bancaires, récompensez les criminels avec des billets marqués ou des biens volés qui nécessitent des étapes supplémentaires pour être convertis en monnaie utilisable. Cette conception étend la boucle de jeu criminelle au-delà du braquage et crée des systèmes interconnectés. Les billets marqués peuvent être blanchis à des endroits spécifiques sur la carte moyennant un pourcentage, créant ainsi des décisions risque-récompense sur le moment et le lieu de blanchiment. Les bijoux ou appareils électroniques volés doivent être clôturés par l'intermédiaire de revendeurs PNJ qui proposent des tarifs variables en fonction de la rareté de l'article et des conditions actuelles du marché. Implémentez un système de sacs de butin où le butin physique doit être transporté par les joueurs, limitant leur vitesse de déplacement et empêchant l'utilisation d'armes, ce qui ajoute des considérations tactiques à la phase d'évasion :

RegisterNetEvent('robbery:server:distributeLoot', function(robberyId)
    local src = source
    local config = Config.Robberies[robberyId]
    local state = RobberyState[robberyId]
    if not config or not state then return end

    if state.status ~= 'active' then return end

    local rewardAmount = math.random(config.reward.min, config.reward.max)
    local participants = state.participants
    local perPlayer = math.floor(rewardAmount / #participants)

    for _, playerId in ipairs(participants) do
        local Player = QBCore.Functions.GetPlayer(playerId)
        if Player then
            if config.reward.type == 'markedbills' then
                local billStacks = math.ceil(perPlayer / 5000)
                for i = 1, billStacks do
                    local amount = math.min(5000, perPlayer - ((i - 1) * 5000))
                    Player.Functions.AddItem('markedbills', 1, nil, {
                        worth = amount,
                        source = config.label,
                    })
                end
            elseif config.reward.type == 'cash' then
                Player.Functions.AddMoney('cash', perPlayer, 'robbery-' .. robberyId)
            end
            TriggerClientEvent('inventory:client:ItemBox', playerId, QBCore.Shared.Items['markedbills'], 'add')
        end
    end

    -- Set cooldown
    state.status = 'cooldown'
    state.cooldownUntil = os.time() + config.cooldown
    state.participants = {}

    -- Log the robbery for admin review
    LogRobbery(robberyId, participants, rewardAmount)
end)

Gestion anti-exploit et temps de recharge

Les systèmes de vol attirent les exploiteurs car ils génèrent de la richesse. Mettez en œuvre plusieurs couches de protection contre les exploits courants. Les contrôles de distance côté serveur garantissent que les joueurs sont physiquement présents sur le lieu du vol tout au long de chaque phase. La limitation du débit empêche les joueurs de spammer les événements de début de vol. Le suivi des participants garantit que seuls les participants inscrits peuvent progresser à travers les couches de sécurité ou recevoir du butin. Les temps de recharge globaux empêchent le spam de vol à l'échelle du serveur en limitant le nombre de vols pouvant être actifs simultanément. Les temps de recharge par joueur empêchent un seul joueur de voler en chaîne plusieurs emplacements consécutivement. Enregistrez chaque tentative de vol avec les horodatages, les identifiants des participants, le statut de réussite ou d'échec et la récompense distribuée afin que les administrateurs puissent enquêter sur les modèles suspects. Surveillez les temps d'exécution impossibles où un joueur termine toutes les couches de sécurité plus rapidement qu'il ne devrait être physiquement possible. Mettez en œuvre un système de réputation dans lequel les vols excessifs attirent davantage l'attention de la police et des conséquences plus sévères, créant ainsi un frein naturel qui encourage les joueurs à espacer leurs activités criminelles.

Itinéraires d'évacuation et événements dynamiques

La phase d'évasion est celle où les vols deviennent vraiment imprévisibles. Une fois le butin sécurisé, les criminels doivent s'échapper de la zone en portant de lourds sacs polochons qui restreignent leurs mouvements. Concevez le système pour prendre en charge plusieurs méthodes d'évacuation : véhicules terrestres garés à l'extérieur, bateaux sur les quais à proximité pour les berges du front de mer ou extractions par hélicoptère depuis les toits pour des braquages ​​de haut niveau. Ajoutez des événements dynamiques pendant la phase d'évasion, tels que des bandes à crampons pour pneus que la police peut déployer, des mises à jour par conversation radio qui révèlent la description du véhicule suspect et des barrages routiers aléatoires aux intersections clés. Le serveur doit vérifier si les criminels ont réussi à quitter la zone de vol, définie comme un rayon configurable autour de la banque, et ensuite seulement marquer le vol comme étant entièrement terminé. Si tous les participants sont tués ou arrêtés avant de vider la zone, le butin devrait pouvoir être récupéré par la police comme preuve. Cela crée un gameplay post-vol significatif pour les deux parties et garantit que la fuite est tout aussi difficile et gratifiante que le braquage lui-même.

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.