Назад к блогу
Guide8 мин чтения

Система ограблений и налётов FiveM: Банки, магазины и сейфы

Скрипт, вознаграждающий за ограбления и налёты в FiveM. Fleeca, Paleto, ювелирные магазины, копы и лучшие скрипты для налётов для суровых ролевых серверов.

Agency Scripts

Основатель и ведущий разработчик Agency Scripts

Обзор архитектуры системы ограблений

Ограбления и налёты , одни из самых захватывающих игровых циклов на любом FiveM ролевом сервере. Они объединяют преступников, планирующих сложную операцию, полицию, реагирующую с тактической точностью, переговорщиков по заложникам, управляющих напряжёнными ситуациями, и гражданских, оказавшихся в перекрёстном огне. Хорошо продуманная система ограблений выходит далеко за рамки простого взаимодействия, дающего игрокам деньги. Она требует многоуровневой архитектуры, которая управляет локациями, проверками предварительных условий, прогрессивными фазами мини-игр, распределением добычи, интеграцией с полицейским диспетчером, управлением перерывами и защитой от эксплойтов. Система должна строиться на трёх основных слоях: слой конфигурации, который определяет каждое место ограбления с его требованиями и наградами, серверный конечный автомат, отслеживающий активные ограбления и обеспечивающий соблюдение всех правил, и клиентский слой презентации, который отображает мини-игры, анимации и интерфейсы NUI. Все критические решения должны приниматься на стороне сервера, так как логика на стороне клиента легко эксплуатируется.

Настройка мест ограблений и уровней

Ваша система ограблений должна поддерживать несколько типов локаций с возрастающими уровнями сложности. Магазины у дома представляют начальный уровень, требующий минимального присутствия полиции и базовых инструментов. Отделения банка Fleeca находятся на среднем уровне с умеренными слоями безопасности и более высокими требованиями к полиции. Сейф Pacific Standard служит вершиной ограбления, требуя полной команды, продвинутого оборудования и значительного присутствия правоохранительных органов онлайн. Каждое место нуждается в конфигурационном элементе, который определяет его координаты, необходимые предметы, минимальное количество полиции, время перезарядки, диапазон наград и последовательность слоёв безопасности, которые необходимо преодолеть:

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},
        },
    },
}

Каждый уровень должен масштабироваться не только по награде, но и по сложности и риску. Ограбления магазинов , это быстрые дела на вынос, длящиеся менее двух минут, тогда как ограбление Pacific Standard , многофазная операция, которая может занять пятнадцать минут и более, давая полиции достаточно времени для установления периметра.

Машина состояний на стороне сервера

Машина состояний ограбления , это ядро системы. Она отслеживает каждое место ограбления через состояния жизненного цикла: ожидание, в процессе, перезарядка и заблокировано. Когда игрок начинает ограбление, сервер проверяет все условия перед переходом места из ожидания в процесс. Во время процесса сервер отслеживает, на каком уровне безопасности находятся преступники, какие игроки участвуют и сколько времени прошло. Если все игроки покидают зону или арестованы, ограбление автоматически проваливается и переходит в сокращённый период перезарядки. По завершении место переходит в состояние перезарядки на заданный период, после чего становится доступным снова:

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)

Машина состояний должна также обрабатывать крайние случаи, такие как перезапуски сервера во время активных ограблений. При старте сервера сбросьте все состояния ограблений в ожидание и очистите активных участников. Храните метки времени перезарядки в постоянной таблице, чтобы они сохранялись после перезапусков.

Интеграция миниигр

Миниигры превращают ограбления из пассивного ожидания в активное испытание навыков. Каждый тип уровня безопасности должен соответствовать разному стилю миниигры, проверяющей разные способности игрока. Миниигры взлома могут использовать пазлы на запоминание, задачи по соединению цепей или быстрые числовые последовательности. Установка термита требует точного тайминга для размещения зарядов до их срабатывания. Механика сверления проверяет устойчивость управления мышью, когда игрок проводит сверло через замок, не перегреваясь. Ключевой принцип дизайна , более сложные уровни должны использовать более сложные варианты этих миниигр с более жесткими временными рамками, большим количеством элементов для отслеживания или более высокой скоростью:

-- 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)

Неудачные попытки мини-игр должны иметь последствия. Неудачный взлом может вызвать дополнительные тревоги, сократить оставшееся время или заблокировать игрока на короткий штрафной период. Для высокоуровневых ограблений неудача в финальном взломе может полностью заблокировать сейф, заставляя команду отказаться от ограбления с частичным трофеем. Эти состояния неудачи добавляют напряжения и делают успешные завершения по-настоящему вознаграждающими.

Интеграция полицейского диспетчерского центра и реагирования

Система ограблений неполна без интеграции с полицией. При начале ограбления диспетчерская система должна отправлять оповещения разного уровня в зависимости от типа ограбления. Ограбление магазина создает простое тихое уведомление с локацией. Ограбление филиала Fleeca вызывает банковскую тревогу с названием филиала и рекомендованным уровнем реагирования. Ограбление Pacific Standard активирует полное экстренное оповещение с рекомендацией всем доступным подразделениям реагировать. Обеспечьте полицию обновлениями в реальном времени по ходу ограбления: когда преступники преодолевают новый уровень безопасности, когда внутри здания слышны выстрелы или когда преступники пытаются сбежать. Эти обновления помогают полиции координировать действия и создают динамичные сценарии, где офицеры решают, штурмовать ли сразу или установить периметр и вести переговоры. Реализуйте механику заложников, когда преступники могут брать NPC-кассиров в заложники, давая полиции роль переговорщиков и преступникам рычаги для задержки реакции полиции. Система заложников должна отслеживать количество заложников, были ли пострадавшие, и предоставлять подсказки для переговоров через телефонные звонки между преступниками и офицерами.

Распределение добычи и отмывание денег

Что получают игроки после ограбления так же важно, как и само ограбление. Вместо того чтобы сразу класть чистые деньги на банковские счета, награждайте преступников помеченными купюрами или украденными товарами, которые требуют дополнительных шагов для конвертации в используемую валюту. Такая концепция расширяет криминальный игровой цикл за пределы ограбления и создает взаимосвязанные системы. Помеченные купюры можно отмывать в определенных местах на карте за процентную комиссию, создавая решения с риском и вознаграждением о том, когда и где отмывать. Украденные украшения или электронику нужно сдавать через NPC дилеров, которые предлагают разные ставки в зависимости от редкости предмета и текущих рыночных условий. Реализуйте систему сумок с добычей, где физическую добычу должны нести игроки, что ограничивает их скорость передвижения и запрещает использование оружия, добавляя тактические соображения на этапе побега:

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)

Управление анти-эксплоитом и кулдауном

Системы ограблений привлекают эксплойтеров, поскольку они генерируют богатство. Реализуйте несколько уровней защиты от распространённых эксплойтов. Серверные проверки расстояния гарантируют, что игроки физически присутствуют на месте ограбления на протяжении всех фаз. Ограничение частоты предотвращает спам запусков ограблений. Отслеживание участников гарантирует, что только зарегистрированные участники могут проходить через уровни безопасности или получать добычу. Глобальные перерывы предотвращают спам ограблений по всему серверу, ограничивая количество одновременно активных ограблений. Персональные перерывы не позволяют одному игроку совершать цепочку ограблений подряд. Логируйте каждую попытку ограбления с отметками времени, идентификаторами участников, статусом успеха или неудачи и распределённой наградой, чтобы администраторы могли расследовать подозрительные паттерны. Отслеживайте невозможные по времени завершения, когда игрок проходит все уровни безопасности быстрее, чем это физически возможно. Внедрите систему репутации, где чрезмерная активность в ограблениях привлекает повышенное внимание полиции и более жёсткие последствия, создавая естественное ограничение, побуждающее игроков распределять свои преступные действия во времени.

Маршруты эвакуации и динамические события

Фаза побега , это момент, когда ограбления становятся по-настоящему непредсказуемыми. После того как добыча secured, преступники должны покинуть зону, неся тяжёлые сумки, ограничивающие движение. Система должна поддерживать несколько способов побега: наземные машины, припаркованные снаружи, лодки на близлежащих доках для банков у воды или вертолётные эвакуации с крыш для высокоуровневых ограблений. Добавьте динамические события во время побега, такие как шипы, которые полиция может развернуть, радиопереговоры с описанием подозреваемого автомобиля и случайные блокпосты на ключевых перекрёстках. Сервер должен отслеживать, покинули ли преступники зону ограбления, определённую настраиваемым радиусом вокруг банка, и только тогда считать ограбление полностью завершённым. Если все участники убиты или арестованы до выхода из зоны, добычу могут изъять полицейские как улики. Это создаёт значимую пост-ограбленческую игру для обеих сторон и гарантирует, что побег так же сложен и вознаграждаем, как и само ограбление.

Готовы начать?

Возьмите скрипты в нашем магазине или заходите в Discord за поддержкой, обновлениями и анонсами.