العودة إلى المدونة
Guide9 دقيقة قراءة

نظام السجن في FiveM: سكريبت وقت السجن المعنوي

ابنِ نظام سجن يتفاعل معه اللاعبون فعليًا. وظائف السجن، طرق الهروب، الحراس، الإفراج المشروط وأفضل سكريبتات السجن لـ 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)
);

حكم على لاعب

عندما يستخدم الشرطة أمر السجن، يتحقق الخادم من صلاحيات الضابط، يحسب الحكم بناءً على التهم، يزيل أسلحة السجين والأغراض غير القانونية، وينقله إلى نقطة ولادة داخل السجن. يجب حفظ الحكم في قاعدة البيانات فورًا ليبقى بعد إعادة تشغيل الخادم. نفذ الحكم كأمر على جانب الخادم يمكن فقط للضباط المخولين تنفيذه:

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%، يصبح مؤهلاً للإفراج المشروط. يمكن للقاضي أو ضابط شرطة كبير منح الإفراج المشروط من خلال أمر، مما يفرج عن اللاعب مبكراً لكنه يضعه تحت قيود. قد يكون لدى اللاعبين المفرج عنهم حظر تجول يتطلب وجودهم في ممتلكاتهم بين ساعات معينة، ومتطلبات لتسجيل الدخول مع ضابط الإفراج المشروط بانتظام، وحظر على حيازة الأسلحة أو دخول مناطق معينة. إذا انتهك اللاعب المفرج عنه أي شرط، يتم إعادته تلقائياً إلى السجن مع بقية حكمه بالإضافة إلى وقت إضافي للانتهاك. تتبع حالة الإفراج المشروط في بيانات تعريف اللاعب وتشغيل فحوصات دورية على الخادم للتحقق من الامتثال للشروط.

أحداث الهروب من السجن

هروب السجن هي أحداث على مستوى الخادم تخلق تمثيل أدوار عالي المخاطر لكل من المجرمين وإنفاذ القانون. يجب أن يتطلب هروب السجن تحضيرًا كبيرًا، ومشاركة عدة أشخاص، ويعطي الشرطة تحذيرًا كافيًا للرد. نفذه كحدث متعدد المراحل: يجب على فريق خارجي أولاً الحصول على عناصر محددة مثل مروحية، متفجرات، وأزياء تنكرية. ثم يجب عليهم اختراق نظام أمان السجن من خلال لعبة صغيرة صعبة على لوحة تحكم خارجية. بمجرد تعطيل الأمان، تفتح أبواب الزنازين لفترة محدودة ويمكن للسجناء محاولة الهروب عبر طرق محددة مسبقًا بينما تحاول شخصيات الحراس والشرطة المستجيبة إيقافهم. يجب أن يسمح النظام فقط بهروب السجن عندما يكون عدد أدنى من ضباط الشرطة متصلين لضمان استجابة عادلة:

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 ثوانٍ للعودة قبل أن يُعاد نقله إلى نقطة ظهور السجن مع إضافة وقت إضافي إلى حكمه كعقوبة لمحاولة الهروب. على جانب العميل، يتم تشغيل فحص دوري كل ثانيتين يقارن موقع اللاعب بمنطقة حدود السجن المضلع. للإفراج، يشغل الخادم حلقة مؤقت تفحص جميع الأحكام النشطة كل 30 ثانية. عند انتهاء الحكم، يقوم النظام بتحديث حالة قاعدة البيانات إلى الإفراج، يمسح بيانات السجن التعريفية للاعب، يستعيد مخزونه من نسخة احتياطية قبل السجن إذا نفذت واحدة، وينقله إلى مخرج السجن مع إشعار بأنه حر. إذا كان اللاعب غير متصل عند انتهاء حكمه، يتم التعامل مع الإفراج خلال تسجيل الدخول التالي بفحص الأحكام النشطة المنتهية في حدث تحميل اللاعب. يتضمن الأمر للشرطة للإفراج اليدوي المبكر عن اللاعبين في حالات مثل السجن الظالم أو صفقات الإقرار التي تم التفاوض عليها عبر التقمص.

الدمج مع أنظمة الشرطة والقانون

نظام السجن الخاص بك يجب أن يندمج بشكل وثيق مع MDT الشرطة وأي نظام محكمة على الخادم. عندما يُسجن لاعب، يجب تحديث سجله الجنائي في MDT تلقائيًا مع التهم، مدة الحكم، والضابط الذي أجرى الاعتقال. إذا كان لدى خادمك نظام محكمة، يجب أن تمر الأحكام الصادرة عن القاضي عبر نفس وظيفة السجن لضمان الاتساق. نفذ نظام استئناف الأحكام حيث يمكن للسجناء تقديم استئناف من مكتبة السجن الذي ينشئ تذكرة للمراجعة من قبل القضاة، مما قد يؤدي إلى تخفيض الأحكام أو الإفراج المبكر. تتبع العودة للجريمة بعد الإفراج عن طريق عد مرات سجن كل مواطن واستخدم هذه البيانات لتطبيق أحكام أشد على المجرمين المتكررين عبر نظام مضاعف. صدّر وظيفة السجن الخاصة بك بحيث يمكن لموارد أخرى مثل نظام المحكمة، الكشف التلقائي عن الجرائم، أو أوامر الإدارة أن ترسل اللاعبين إلى السجن عبر نفس المسار المعتمد.

جاهز للبدء؟

احصل على السكربتات من متجرنا، أو انضم إلى Discord للدعم والتحديثات ونظرة على ما هو قادم.