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

FiveM Prison System: Скрипт реального тюремного времени

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

Agency Scripts

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

Архитектура тюремной системы

Система тюрьмы , одна из самых значимых функций, которую можно добавить на ролевой сервер FiveM, так как она создает ощутимые последствия за преступную деятельность и дает заключенным игрокам интересное занятие вместо того, чтобы смотреть в стену тридцать минут. Архитектура строится вокруг таймера заключения, который отсчитывает время в реальном времени, набора тюремных активностей, позволяющих сократить срок через труд, и системы границ, ограничивающей игроков территорией тюрьмы до отбытия срока. Локация Bolingbroke Penitentiary в GTA V , стандартный выбор благодаря полностью смоделированному интерьеру с камерами, двором и прилегающими объектами. Ваша система нуждается в трех основных компонентах: логике назначения срока на сервере, которая назначает тюремное время и изымает оружие и контрабанду; клиентской части, которая контролирует границы и телепортирует игроков обратно, если они покидают периметр тюрьмы; и фреймворке активностей, дающем заключенным продуктивные задачи для проведения времени.

Схема базы данных и вынесение приговоров

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

CREATE TABLE IF NOT EXISTS prison_sentences (
    id INT AUTO_INCREMENT PRIMARY KEY,
    citizenid VARCHAR(50) NOT NULL,
    sentence_minutes INT NOT NULL,
    time_served INT DEFAULT 0,
    reduction_earned INT DEFAULT 0,
    jailed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    release_at TIMESTAMP NOT NULL,
    charges TEXT DEFAULT NULL,
    arresting_officer VARCHAR(50) DEFAULT NULL,
    status ENUM('active', 'released', 'escaped', 'pardoned') DEFAULT 'active',
    parole_eligible BOOLEAN DEFAULT FALSE,
    INDEX idx_citizen (citizenid),
    INDEX idx_status (status)
);

CREATE TABLE IF NOT EXISTS prison_activity_log (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    citizenid VARCHAR(50) NOT NULL,
    sentence_id INT NOT NULL,
    activity_type ENUM('mining', 'gym', 'cleaning', 'library', 'yard') NOT NULL,
    reduction_minutes INT DEFAULT 0,
    completed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (sentence_id) REFERENCES prison_sentences(id)
);

Назначение наказания игроку

Когда полиция использует команду jail, сервер проверяет права офицера, рассчитывает срок наказания по обвинениям, изымает оружие и нелегальные предметы у заключённого и телепортирует его в точку спауна внутри тюрьмы. Срок должен сразу сохраняться в базе данных, чтобы сохраняться после перезапуска сервера. Реализуйте наказание как серверную команду, которую могут выполнять только уполномоченные офицеры полиции:

RegisterCommand('jail', function(source, args)
    local src = source
    local Officer = QBCore.Functions.GetPlayer(src)
    if not Officer then return end

    -- Check police job
    if Officer.PlayerData.job.name ~= 'police' then
        TriggerClientEvent('QBCore:Notify', src, 'Unauthorized', 'error')
        return
    end

    local targetId = tonumber(args[1])
    local minutes = tonumber(args[2])
    local charges = table.concat(args, ' ', 3)

    if not targetId or not minutes or minutes <= 0 then
        TriggerClientEvent('QBCore:Notify', src, 'Usage: /jail [id] [minutes] [charges]', 'error')
        return
    end

    local Target = QBCore.Functions.GetPlayer(targetId)
    if not Target then
        TriggerClientEvent('QBCore:Notify', src, 'Player not found', 'error')
        return
    end

    local citizenid = Target.PlayerData.citizenid
    local releaseAt = os.date('!%Y-%m-%d %H:%M:%S', os.time() + (minutes * 60))

    -- Insert sentence
    local sentenceId = MySQL.insert.await(
        'INSERT INTO prison_sentences (citizenid, sentence_minutes, release_at, charges, arresting_officer) VALUES (?, ?, ?, ?, ?)',
        { citizenid, minutes, releaseAt, charges, Officer.PlayerData.citizenid }
    )

    -- Strip weapons and contraband
    local contraband = {'weapon_pistol', 'weapon_smg', 'lockpick', 'thermite'}
    for _, item in ipairs(contraband) do
        local playerItem = Target.Functions.GetItemByName(item)
        if playerItem then
            Target.Functions.RemoveItem(item, playerItem.amount)
        end
    end

    -- Set jail metadata and teleport
    Target.Functions.SetMetaData('injail', sentenceId)
    TriggerClientEvent('prison:client:enter', targetId, sentenceId, minutes)
    TriggerClientEvent('QBCore:Notify', src, 'Jailed ' .. GetPlayerName(targetId) .. ' for ' .. minutes .. ' minutes', 'success')
end, false)

Тюремные активности

Тюремные активности , это основной игровой цикл, который удерживает заключённых игроков вовлечёнными. Без них время в тюрьме становится наказанием, заставляющим игроков выходить из игры, что плохо для численности сервера. Разрабатывайте активности с компромиссом: выполнение работы сокращает срок, давая заключённым контроль над своим опытом. Каждая активность должна иметь время восстановления, чтобы предотвратить спам, и максимальный лимит сокращения за цикл активности, чтобы 60-минутный срок нельзя было очистить за 5 минут. Наиболее распространённые активности , добыча в карьере, тренировки в спортзале, уборка объекта и время в тюремной библиотеке. Каждая активность использует разный мини-игровой или интерактивный паттерн для разнообразия:

Config.PrisonActivities = {
    mining = {
        label = 'Quarry Mining',
        coords = vector3(1690.64, 2592.63, 45.56),
        radius = 15.0,
        reductionPerCycle = 2,   -- minutes reduced per completion
        cycleDuration = 45,       -- seconds per mining cycle
        cooldown = 30,            -- seconds between cycles
        maxReductionPerSession = 10,
        animation = { dict = 'amb@world_human_hammering@male@base', anim = 'base' },
        requiredProp = 'prop_tool_pickaxe',
    },
    gym = {
        label = 'Prison Gym',
        coords = vector3(1662.67, 2527.84, 45.56),
        radius = 10.0,
        reductionPerCycle = 1,
        cycleDuration = 30,
        cooldown = 60,
        maxReductionPerSession = 6,
        animation = { dict = 'amb@world_human_muscle_free_weights@male@barbell@base', anim = 'base' },
    },
    cleaning = {
        label = 'Facility Cleaning',
        coords = vector3(1653.18, 2499.26, 45.56),
        radius = 20.0,
        reductionPerCycle = 1,
        cycleDuration = 25,
        cooldown = 20,
        maxReductionPerSession = 8,
        animation = { dict = 'amb@world_human_janitor@male@base', anim = 'base' },
        requiredProp = 'prop_cs_broom',
    },
    library = {
        label = 'Prison Library',
        coords = vector3(1680.21, 2513.45, 45.56),
        radius = 8.0,
        reductionPerCycle = 1,
        cycleDuration = 60,
        cooldown = 45,
        maxReductionPerSession = 4,
        animation = { dict = 'amb@world_human_clipboard@male@base', anim = 'base' },
    },
}

-- Server-side activity completion handler
RegisterNetEvent('prison:server:completeActivity', function(activityType, sentenceId)
    local src = source
    local Player = QBCore.Functions.GetPlayer(src)
    if not Player then return end

    local citizenid = Player.PlayerData.citizenid
    local activity = Config.PrisonActivities[activityType]
    if not activity then return end

    -- Validate sentence is active
    local sentence = MySQL.single.await(
        'SELECT * FROM prison_sentences WHERE id = ? AND citizenid = ? AND status = "active"',
        { sentenceId, citizenid }
    )
    if not sentence then return end

    -- Check session reduction cap
    local sessionReduction = MySQL.scalar.await(
        'SELECT COALESCE(SUM(reduction_minutes), 0) FROM prison_activity_log WHERE sentence_id = ? AND activity_type = ? AND completed_at > DATE_SUB(NOW(), INTERVAL 1 HOUR)',
        { sentenceId, activityType }
    )
    if sessionReduction >= activity.maxReductionPerSession then
        TriggerClientEvent('QBCore:Notify', src, 'Maximum reduction reached for this activity', 'error')
        return
    end

    -- Apply reduction
    local reduction = activity.reductionPerCycle
    MySQL.update('UPDATE prison_sentences SET reduction_earned = reduction_earned + ?, release_at = DATE_SUB(release_at, INTERVAL ? MINUTE) WHERE id = ?',
        { reduction, reduction, sentenceId })

    MySQL.insert('INSERT INTO prison_activity_log (citizenid, sentence_id, activity_type, reduction_minutes) VALUES (?, ?, ?, ?)',
        { citizenid, sentenceId, activityType, reduction })

    TriggerClientEvent('QBCore:Notify', src, 'Sentence reduced by ' .. reduction .. ' minute(s)', 'success')
    TriggerClientEvent('prison:client:updateTimer', src, reduction)
end)

Система условно-досрочного освобождения

Система условно-досрочного освобождения добавляет слой ролевой игры между тюрьмой и полной свободой. После отбывания настраиваемого процента срока, обычно 60-75%, заключённый становится кандидатом на УДО. УДО может быть предоставлено судьёй или старшим полицейским через команду, что освобождает игрока досрочно, но с ограничениями. Освобождённые по УДО могут иметь комендантский час, требующий нахождения в собственности в определённые часы, обязанность регулярно отмечаться у надзирателя и запреты на владение оружием или посещение определённых зон. При нарушении условий УДО игрок автоматически возвращается в тюрьму с оставшимся сроком плюс дополнительным временем за нарушение. Статус УДО отслеживается в метаданных игрока, а сервер периодически проверяет соблюдение условий.

События побега из тюрьмы

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

Config.PrisonBreak = {
    minPoliceOnline = 5,
    cooldown = 28800, -- 8 hours between break attempts
    requiredItems = {
        { name = 'electronickit', amount = 2, label = 'Electronic Kit' },
        { name = 'thermite', amount = 3, label = 'Thermite Charge' },
    },
    phases = {
        {
            name = 'hack_security',
            label = 'Hack Security Grid',
            coords = vector3(1746.23, 2488.90, 45.80),
            duration = 60,
            minigame = { type = 'hack', difficulty = 'hard', attempts = 3 },
        },
        {
            name = 'breach_wall',
            label = 'Breach Perimeter Wall',
            coords = vector3(1651.78, 2569.33, 45.56),
            duration = 30,
            minigame = { type = 'thermite', time = 10 },
        },
        {
            name = 'disable_lockdown',
            label = 'Disable Lockdown Protocol',
            coords = vector3(1691.45, 2565.12, 45.56),
            duration = 45,
            minigame = { type = 'hack', difficulty = 'expert', attempts = 2 },
        },
    },
    escapeWindow = 300, -- 5 minutes to escape after all phases complete
    escapeRoutes = {
        { label = 'Main Gate', coords = vector3(1845.12, 2585.87, 45.67) },
        { label = 'Drainage Tunnel', coords = vector3(1617.34, 2523.90, 44.12) },
        { label = 'Helipad', coords = vector3(1700.89, 2565.34, 52.34) },
    },
}

Система охранников NPC

Охранники NPC обеспечивают фоновую безопасность вокруг тюрьмы и не позволяют заключённым просто так выходить. Размещайте охранников в ключевых узлах, таких как главные ворота, входы в камеры и вдоль периметра стены. Каждый охранник должен иметь маршрут патрулирования с путевыми точками, которые он циклично проходит, создавая окна, когда определённые зоны остаются без охраны. Охранники обнаруживают заключённых, покидающих разрешённые зоны, и реагируют устным предупреждением, затем преследуют и атакуют, если заключённый не подчиняется. Реализуйте обнаружение охранников с помощью проверок близости с учётом линии видимости, чтобы заключённые могли проскользнуть мимо, оставаясь за укрытиями. Во время события побега из тюрьмы появляйте дополнительных охранников в ключевых позициях и повышайте их уровень агрессии. Используйте нативные функции FiveM TaskPatrol и SetPedCombatAttributes обеспечить реалистичное поведение охранников на патруле. Обязательно установите их группу отношений в враждебную по отношению к заключённым, при этом оставаясь нейтральными к полиции и посетителям, чтобы избежать дружественного огня во время побегов из тюрьмы.

Ограничение и снятие ограничений

Система границ удерживает заключённых внутри периметра тюрьмы с помощью полигона, покрывающего всё учреждение. Когда игрок с активным сроком выходит за пределы зоны, он получает предупреждение и имеет 10 секунд, чтобы вернуться, иначе телепортируется обратно к точке спавна в тюрьме с добавлением времени к сроку как штраф за попытку побега. На клиенте каждые 2 секунды выполняется проверка позиции игрока относительно полигона тюрьмы. Для освобождения сервер запускает таймер, проверяющий все активные сроки каждые 30 секунд. По истечении срока система обновляет статус в базе данных на освобождён, очищает метаданные тюрьмы игрока, восстанавливает инвентарь из резервной копии, если она реализована, и телепортирует к выходу из тюрьмы с уведомлением о свободе. Если игрок офлайн при истечении срока, освобождение происходит при следующем входе через проверку просроченных сроков в событии загрузки игрока. Включите команду для полиции на досрочное освобождение в случаях ошибочного заключения или сделок через ролевую игру.

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

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

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

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