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

نظام تأجير السيارات في FiveM: برمجة المركبات المؤقتة

إضافة مركبات للإيجار إلى خادم FiveM الخاص بك. مفاتيح مؤقتة، ودائع، منطق الإرجاع التلقائي وأفضل سكربتات الإيجار لشبكات QBCore وESX والوظائف المستقلة.

Agency Scripts

المؤسس والمطور الرئيسي في Agency Scripts

لماذا تعزز أنظمة الإيجار اللعب التمثيلي

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

تكوين الأسطول وفئات المركبات

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

Config.RentalLocations = {
    ['airport'] = {
        label = 'LS Airport Car Rental',
        pedModel = 's_f_y_airhostess_01',
        pedCoords = vector4(-1037.34, -2736.82, 20.17, 238.52),
        spawnPoint = vector4(-1029.12, -2728.91, 20.17, 148.76),
        returnZone = vector3(-1035.00, -2730.00, 20.17),
        returnRadius = 10.0,
        categories = {'economy', 'midrange', 'premium', 'motorcycle'},
    },
    ['downtown'] = {
        label = 'Downtown Bike Rental',
        pedModel = 'a_m_y_business_02',
        pedCoords = vector4(-259.68, -892.41, 31.08, 69.24),
        spawnPoint = vector4(-264.30, -888.12, 31.08, 339.87),
        returnZone = vector3(-260.00, -890.00, 31.08),
        returnRadius = 8.0,
        categories = {'motorcycle', 'bicycle'},
    },
}

Config.RentalFleet = {
    economy = {
        {model = 'blista', label = 'Blista', priceHour = 150, priceDay = 800, deposit = 500},
        {model = 'asea', label = 'Asea', priceHour = 120, priceDay = 650, deposit = 400},
        {model = 'stanier', label = 'Stanier', priceHour = 130, priceDay = 700, deposit = 450},
    },
    midrange = {
        {model = 'schafter2', label = 'Schafter', priceHour = 300, priceDay = 1600, deposit = 1000},
        {model = 'oracle', label = 'Oracle', priceHour = 280, priceDay = 1500, deposit = 900},
    },
    premium = {
        {model = 'felon', label = 'Felon GT', priceHour = 600, priceDay = 3200, deposit = 2500},
        {model = 'exemplar', label = 'Exemplar', priceHour = 550, priceDay = 3000, deposit = 2000},
    },
    motorcycle = {
        {model = 'pcj', label = 'PCJ-600', priceHour = 100, priceDay = 500, deposit = 300},
        {model = 'sanchez', label = 'Sanchez', priceHour = 80, priceDay = 400, deposit = 250},
    },
    bicycle = {
        {model = 'cruiser', label = 'Cruiser Bike', priceHour = 30, priceDay = 150, deposit = 50},
        {model = 'tribike', label = 'Tri-Cycles Race', priceHour = 25, priceDay = 120, deposit = 50},
    },
}

عملية الإيجار وواجهة NUI

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

RegisterNetEvent('rental:server:rentVehicle', function(locationId, vehicleModel, duration)
    local src = source
    local Player = QBCore.Functions.GetPlayer(src)
    if not Player then return end

    local location = Config.RentalLocations[locationId]
    if not location then return end

    -- Check if player already has an active rental
    if ActiveRentals[Player.PlayerData.citizenid] then
        TriggerClientEvent('QBCore:Notify', src, 'You already have an active rental', 'error')
        return
    end

    -- Find vehicle config
    local vehicleConfig = nil
    for _, category in pairs(Config.RentalFleet) do
        for _, v in ipairs(category) do
            if v.model == vehicleModel then
                vehicleConfig = v
                break
            end
        end
        if vehicleConfig then break end
    end

    if not vehicleConfig then return end

    -- Calculate cost
    local rentalFee = duration == 'day' and vehicleConfig.priceDay or vehicleConfig.priceHour
    local totalCost = rentalFee + vehicleConfig.deposit

    if Player.Functions.GetMoney('bank') < totalCost then
        TriggerClientEvent('QBCore:Notify', src, 'Insufficient funds. Need $' .. totalCost, 'error')
        return
    end

    -- Charge player
    Player.Functions.RemoveMoney('bank', totalCost, 'vehicle-rental')

    -- Generate rental plate
    local plate = 'RNT' .. math.random(10000, 99999)

    -- Store active rental
    ActiveRentals[Player.PlayerData.citizenid] = {
        model = vehicleModel,
        plate = plate,
        deposit = vehicleConfig.deposit,
        rentalFee = rentalFee,
        startTime = os.time(),
        duration = duration,
        expiresAt = os.time() + (duration == 'day' and 86400 or 3600),
        locationId = locationId,
        initialHealth = 1000,
    }

    TriggerClientEvent('rental:client:spawnVehicle', src, vehicleModel, plate, location.spawnPoint)
    TriggerClientEvent('QBCore:Notify', src, 'Vehicle rented! Return before your time expires.', 'success')
end)

آليات وديعة الضرر والإرجاع

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

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

    local citizenid = Player.PlayerData.citizenid
    local rental = ActiveRentals[citizenid]
    if not rental then return end

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

    -- Check plate matches
    local plate = GetVehicleNumberPlateText(vehicle)
    if plate:trim() ~= rental.plate then
        TriggerClientEvent('QBCore:Notify', src, 'This is not your rental vehicle', 'error')
        return
    end

    -- Calculate damage
    local bodyHealth = GetVehicleBodyHealth(vehicle)
    local engineHealth = GetVehicleEngineHealth(vehicle)
    local damagePercent = 1.0 - ((bodyHealth + engineHealth) / 2000.0)
    local repairCost = math.floor(rental.deposit * damagePercent)

    -- Calculate late fee
    local lateFee = 0
    if os.time() > rental.expiresAt then
        local overHours = math.ceil((os.time() - rental.expiresAt) / 3600)
        lateFee = overHours * 200
    end

    -- Refund deposit minus costs
    local refund = math.max(0, rental.deposit - repairCost - lateFee)

    if refund > 0 then
        Player.Functions.AddMoney('bank', refund, 'rental-deposit-refund')
    end

    DeleteEntity(vehicle)
    ActiveRentals[citizenid] = nil

    local message = string.format('Vehicle returned. Deposit: $%d | Repairs: $%d | Late fee: $%d | Refund: $%d',
        rental.deposit, repairCost, lateFee, refund)
    TriggerClientEvent('QBCore:Notify', src, message, refund > 0 and 'success' or 'warning')
end)

نظام انتهاء الصلاحية والإرجاع التلقائي

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

مواقع تأجير متعددة

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

تتبع GPS ومكافحة السرقة

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

تكامل الأعمال وتأجير اللاعبين المملوك

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

جاهز للبدء؟

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