نظام الميكانيكي في FiveM: التعديل، الإصلاح ووظائف المركبات
ابنِ نظام ميكانيكي كامل لـ FiveM. إصلاح، تعديل، سحب الحجز، واجهات المتاجر وأفضل سكريبتات الميكانيكي لسيرفرات QBCore و ESX.
Agency Scripts
المؤسس والمطور الرئيسي في Agency Scripts
تصميم بنية وظيفة الميكانيكي
نظام وظيفة ميكانيكي هو واحد من أكثر الميزات شعبية ومكافأة للتطوير لخادم رول بلاي FiveM. على عكس سكريبتات الإصلاح البسيطة التي فقط تستدعي SetVehicleFixed() ، نظام ميكانيكي مناسب يقدم اقتصاد وظيفة كامل مع أدوار متخصصة، مخزونات قطع، وتفاعلات بين اللاعبين تدعم التمثيل الدورى. يجب أن تأخذ البنية في الاعتبار وجود عدة ورش ميكانيكية عبر الخريطة، كل منها قد يملكه مجموعات لاعبين مختلفة، مع تسلسل درجات الوظيفة الذي يحدد من يمكنه إجراء أي إصلاحات. في الأساس، تحتاج إلى تكوين مشترك يحدد مواقع الورش، الخدمات المتاحة في كل مستوى، تسعير القطع، ودرجات الوظيفة المطلوبة لفتح الإصلاحات المتقدمة مثل تبديل المحرك أو تركيب التوربو. يجب أن يتكامل النظام مع إطار الوظائف الموجود في خادمك، سواء كان QBCore أو ESX أو حل مخصص، بحيث يقوم اللاعبون بتسجيل الدخول والخروج من دور الميكانيكي مع التعامل الصحيح للرواتب وتتبع حالة الخدمة.
درجات الوظائف ونظام الأذونات
نظام درجات الوظائف متعدد المستويات يمنح وظيفة الميكانيكي عمقًا ويشجع اللاعبين على التدرج في الرتب. ابدأ بتحديد درجات واضحة مع أذونات محددة: قد يكون المتدرب قادرًا فقط على غسل المركبات وإصلاح أضرار الهيكل البسيطة، الميكانيكي العادي يمكنه التعامل مع إصلاحات المحرك واستبدال الإطارات، الميكانيكي الكبير يفتح ضبط الأداء والطلاء بالرذاذ، وصاحب الورشة يمكنه إدارة الموظفين وتحديد أسعار الخدمة. خزّن تعريفات الدرجات هذه في تكوين مشترك حتى يتمكن العميل والسيرفر من الرجوع إليها بشكل متسق. إليك مثال على تكوين يربط الدرجات بالخدمات المسموح بها:
Config.MechanicGrades = {
[0] = {
label = 'Trainee',
services = {'wash', 'body_minor'},
payMultiplier = 0.7
},
[1] = {
label = 'Mechanic',
services = {'wash', 'body_minor', 'body_major', 'engine_repair', 'tire_replace', 'brake_repair'},
payMultiplier = 1.0
},
[2] = {
label = 'Senior Mechanic',
services = {'wash', 'body_minor', 'body_major', 'engine_repair', 'tire_replace',
'brake_repair', 'spray_paint', 'performance_tune', 'turbo_install'},
payMultiplier = 1.3
},
[3] = {
label = 'Shop Owner',
services = 'all',
payMultiplier = 1.5,
canManage = true
}
}
Config.ServicePrices = {
wash = 200,
body_minor = 800,
body_major = 2500,
engine_repair = 3500,
tire_replace = 1200,
brake_repair = 1500,
spray_paint = 5000,
performance_tune = 15000,
turbo_install = 25000
}
على جانب الخادم، تحقق دائمًا من درجة اللاعب قبل السماح بتنفيذ أي خدمة. لا تثق أبدًا بالعميل للإبلاغ عن الخدمات المتاحة لأن الغشاش قد يغير NUI لإرسال أي طلب خدمة. يجب أن يتحقق رد الخادم من وظيفة اللاعب الحالية، مستوى درجته، وما إذا كانت الخدمة المطلوبة موجودة في قائمة خدمات درجته قبل المتابعة بالإصلاح.
آليات إصلاح المركبات ونظام القطع
ميكانيكا الإصلاح الواقعية تتجاوز استدعاءًا واحدًا للنظام. بدلاً من إصلاح المركبة فورًا، قسم عملية الإصلاح إلى خطوات تتطلب وقتًا وتستهلك قطعًا من مخزون الميكانيكي. عندما يحضر اللاعب سيارته لإصلاح المحرك، يجب أن يمتلك الميكانيكي القطع الصحيحة في مخزونه الشخصي أو في مخزن الورشة المشترك. استخدم نظام شريط تقدم لمحاكاة استغراق الإصلاح وقتًا، مما يخلق لحظات لعب أدوار طبيعية حيث ينتظر الزبون ويتفاعل مع الميكانيكي. يجب أن تستهدف منطق الإصلاح مكونات المركبة المحددة باستخدام الوظائف الأصلية بحيث يمكنك إصلاح المحرك دون إصلاح أضرار الهيكل، أو استبدال الإطارات دون التأثير على صحة المحرك:
local RepairFunctions = {
engine_repair = function(vehicle)
local parts = {'engine_oil', 'spark_plugs', 'coolant'}
if not HasRequiredParts(parts) then
return false, 'Missing required parts'
end
RemovePartsFromInventory(parts)
-- Animate the repair
TaskTurnPedToFaceEntity(PlayerPedId(), vehicle, 1000)
Wait(1000)
if not StartProgressBar('Repairing engine...', 15000, 'mechanic_repair') then
return false, 'Repair cancelled'
end
SetVehicleEngineHealth(vehicle, 1000.0)
SetVehicleEngineOn(vehicle, true, true, false)
return true
end,
body_major = function(vehicle)
local parts = {'body_panel', 'filler_putty', 'paint_primer'}
if not HasRequiredParts(parts) then
return false, 'Missing required parts'
end
RemovePartsFromInventory(parts)
if not StartProgressBar('Repairing body damage...', 20000, 'mechanic_repair') then
return false, 'Repair cancelled'
end
SetVehicleBodyHealth(vehicle, 1000.0)
SetVehicleDeformationFixed(vehicle)
return true
end,
tire_replace = function(vehicle, tireIndex)
local parts = {'tire_set'}
if not HasRequiredParts(parts) then
return false, 'Missing required parts'
end
RemovePartsFromInventory(parts)
if not StartProgressBar('Replacing tire...', 8000, 'mechanic_repair') then
return false, 'Repair cancelled'
end
SetVehicleTyreFixed(vehicle, tireIndex)
return true
end
}
نظام القطع يخلق طبقة اقتصادية إضافية على خادمك. خزّن القطع لدى تجار الجملة NPC أو دع اللاعبين يصنعونها في مواقع صناعية. يمكن لمحلات الميكانيكا شراء القطع بالجملة بأسعار الجملة وتخزينها في مخزون مشترك للمحل، مما يعني أن مالك المحل يحتاج لإدارة سلسلة التوريد للحفاظ على سير العمل بسلاسة. تضيف هذه الديناميكية بين العرض والطلب عمقًا هائلًا لتجربة لعب الأدوار.
خدمات الطلاء بالرش والتجميل
الطلاء بالرش هو أحد الخدمات الأكثر إرضاءً بصريًا التي يمكن أن يقدمها ورشة الميكانيكا، وتنفيذه بشكل جيد يتطلب العمل مع عدة وظائف أصلية لألوان المركبات. يوفر FiveM وظائف أصلية للون الأساسي، اللون الثانوي، اللؤلؤي، لون العجلات، وقيم RGB مخصصة. قم ببناء واجهة اختيار ألوان تتيح للميكانيكي اختيار ألوان معدة مسبقًا أو إدخال قيم RGB مخصصة مقابل سعر مميز. يجب أن تعرض واجهة NUI معاينة حية من خلال تطبيق اللون مؤقتًا على المركبة أثناء تصفح الميكانيكي للخيارات. إليك كيفية التعامل مع تطبيق اللون على جانب الخادم بعد تأكيد الميكانيكي لعملية الطلاء ودفع العميل:
RegisterNetEvent('mechanic:server:applyPaint', function(netId, paintData, customerId)
local src = source
local Mechanic = QBCore.Functions.GetPlayer(src)
local Customer = QBCore.Functions.GetPlayer(customerId)
if not Mechanic or not Customer then return end
if Mechanic.PlayerData.job.name ~= 'mechanic' then return end
local gradeServices = Config.MechanicGrades[Mechanic.PlayerData.job.grade.level].services
if gradeServices ~= 'all' and not TableContains(gradeServices, 'spray_paint') then
return TriggerClientEvent('QBCore:Notify', src, 'Not authorized', 'error')
end
local price = paintData.isCustomRGB and Config.ServicePrices.spray_paint * 1.5
or Config.ServicePrices.spray_paint
if Customer.Functions.RemoveMoney('cash', price, 'mechanic-paint') then
local shopCut = math.floor(price * 0.6)
local mechanicCut = price - shopCut
Mechanic.Functions.AddMoney('cash', mechanicCut, 'mechanic-paint-commission')
AddToShopFunds(Mechanic.PlayerData.job.grade.level >= 3 and src, shopCut)
TriggerClientEvent('mechanic:client:applyPaint', customerId, netId, paintData)
TriggerClientEvent('QBCore:Notify', src, 'Paint applied - earned $' .. mechanicCut, 'success')
TriggerClientEvent('QBCore:Notify', customerId, 'Vehicle painted for $' .. price, 'success')
else
TriggerClientEvent('QBCore:Notify', src, 'Customer cannot afford this', 'error')
end
end)
بعيداً عن الطلاء الأساسي، فكر في إضافة تطبيق الرسومات، تركيب أضواء النيون، وتلوين النوافذ كخدمات تجميلية منفصلة. يجب أن يكون لكل خدمة متطلبات قطعها الخاصة ونقطة سعرها. على سبيل المثال، أضواء النيون تتطلب مجموعات نيون من مخزون القطع وتستخدم الـ SetVehicleNeonLightEnabled و SetVehicleNeonLightsColour natives لتطبيق التأثير البصري. يستخدم تلوين النوافذ SetVehicleWindowTint مع مستويات تلوين مختلفة قد تكون مقيدة بقوانين الخادم المحلية، مما يمنح الشرطة سببًا لإيقاف اللاعبين بسبب التلوينات غير القانونية.
ترقيات وضبط الأداء
ضبط الأداء هو المكان الذي يمكن للميكانيكيين أن يتألقوا فيه حقًا ويكسبوا أعلى الأرباح على الخادم. يوفر FiveM وظائف تعديل المركبات الواسعة من خلال SetVehicleMod function، التي تقبل نوع التعديل وفهرس التعديل. تغطي أنواع التعديلات ترقيات المحرك (النوع 11)، الفرامل (النوع 12)، النقل (النوع 13)، التعليق (النوع 15)، الدروع (النوع 16)، والتيربو (النوع 18). لكل نوع تعديل مستويات متعددة تحسن أداء المركبة تدريجيًا. أنشئ واجهة ضبط تظهر الترقيات المتاحة للمركبة الحالية، تكلفة كل مستوى، ومن الأفضل مقارنة إحصائية تظهر القيم قبل وبعد للسرعة، التسارع، الفرملة، والمناورة:
function GetAvailableUpgrades(vehicle)
local upgrades = {}
local modTypes = {
{type = 11, label = 'Engine', icon = 'fa-engine'},
{type = 12, label = 'Brakes', icon = 'fa-brake'},
{type = 13, label = 'Transmission', icon = 'fa-gears'},
{type = 15, label = 'Suspension', icon = 'fa-car'},
{type = 16, label = 'Armor', icon = 'fa-shield'},
{type = 18, label = 'Turbo', icon = 'fa-bolt'}
}
SetVehicleModKit(vehicle, 0)
for _, mod in ipairs(modTypes) do
local currentLevel = GetVehicleMod(vehicle, mod.type)
local maxLevel = GetNumVehicleMods(vehicle, mod.type)
local isTurbo = mod.type == 18
table.insert(upgrades, {
type = mod.type,
label = mod.label,
icon = mod.icon,
currentLevel = currentLevel,
maxLevel = isTurbo and 1 or maxLevel,
installed = isTurbo and IsToggleModOn(vehicle, mod.type) or currentLevel >= 0,
price = CalculateUpgradePrice(mod.type, currentLevel + 1)
})
end
return upgrades
end
function ApplyPerformanceUpgrade(vehicle, modType, level)
SetVehicleModKit(vehicle, 0)
if modType == 18 then
ToggleVehicleMod(vehicle, 18, true)
else
SetVehicleMod(vehicle, modType, level, false)
end
end
يجب أن تتطلب ترقيات الأداء قطع غيار ذات قيمة عالية مثل مجموعات التيربو، نواقل الحركة للسباق، أو مجموعات الفرامل الرياضية. يمكن الحصول على هذه القطع من موردين متخصصين أو تصنيعها بواسطة اللاعبين ذوي المهارات المناسبة، مما يخلق تبعيات بين الوظائف تعزز اقتصاد الخادم. تحقق دائمًا من طلبات الترقية على الخادم لمنع العملاء من تطبيق تعديلات لم يدفعوا ثمنها أو لا يمتلكون القطع اللازمة.
نظام الفوترة وتقاسم الإيرادات
نظام فوترة نظيف يربط تجربة الميكانيكي بأكملها ويدير المعاملات المالية بين الميكانيكي، العميل، والمتجر. عند إكمال الميكانيكي للخدمة، يتم إنشاء فاتورة مفصلة تسرد كل خدمة تم تنفيذها وتكلفتها. تُعرض الفاتورة على العميل من خلال إشعار أو نافذة NUI يجب عليه قبولها قبل معالجة الدفع. يتم تقسيم الإيرادات بين الميكانيكي الذي نفذ العمل وحساب أعمال المتجر، مع نسبة التقسيم القابلة للتكوين من قبل مالك المتجر. هذا نظام فوترة على جانب الخادم يدير إنشاء الفواتير ومعالجة الدفع:
local activeInvoices = {}
RegisterNetEvent('mechanic:server:createInvoice', function(customerId, services)
local src = source
local Mechanic = QBCore.Functions.GetPlayer(src)
local Customer = QBCore.Functions.GetPlayer(customerId)
if not Mechanic or not Customer then return end
if Mechanic.PlayerData.job.name ~= 'mechanic' then return end
local totalPrice = 0
local lineItems = {}
for _, service in ipairs(services) do
local price = Config.ServicePrices[service.name]
if price then
totalPrice = totalPrice + price
table.insert(lineItems, {
name = service.label,
price = price
})
end
end
local invoiceId = 'INV-' .. os.time() .. '-' .. math.random(1000, 9999)
activeInvoices[invoiceId] = {
mechanic = src,
customer = customerId,
total = totalPrice,
items = lineItems,
shopId = Mechanic.PlayerData.metadata.currentShop,
timestamp = os.time()
}
TriggerClientEvent('mechanic:client:showInvoice', customerId, invoiceId, lineItems, totalPrice)
end)
RegisterNetEvent('mechanic:server:payInvoice', function(invoiceId)
local src = source
local invoice = activeInvoices[invoiceId]
if not invoice or invoice.customer ~= src then return end
local Customer = QBCore.Functions.GetPlayer(src)
local Mechanic = QBCore.Functions.GetPlayer(invoice.mechanic)
if Customer.Functions.RemoveMoney('cash', invoice.total, 'mechanic-invoice') then
local mechanicShare = math.floor(invoice.total * 0.4)
local shopShare = invoice.total - mechanicShare
if Mechanic then
Mechanic.Functions.AddMoney('cash', mechanicShare, 'mechanic-wage')
TriggerClientEvent('QBCore:Notify', invoice.mechanic,
'Received $' .. mechanicShare .. ' for services', 'success')
end
UpdateShopBalance(invoice.shopId, shopShare)
activeInvoices[invoiceId] = nil
TriggerClientEvent('QBCore:Notify', src, 'Paid $' .. invoice.total, 'success')
else
TriggerClientEvent('QBCore:Notify', src, 'Not enough cash', 'error')
end
end)
فكر في دعم طرق دفع متعددة تشمل النقد، التحويلات البنكية، وحتى العملات المشفرة إذا كان لدى خادمك نظام عملات مشفرة. تتبع جميع المعاملات في سجل قاعدة بيانات حتى يتمكن أصحاب المتاجر من مراجعة تاريخ إيراداتهم، وتحديد أفضل الميكانيكيين أداءً، واكتشاف أي أنماط فواتير مشبوهة. يتيح هذا التتبع المالي أيضًا أنظمة الضرائب وتفاعلات لعب الأدوار الحكومية حيث يمكن للدولة تدقيق ورش الميكانيكيين.
إدارة جرد التخزين والمتجر
كل ورشة ميكانيكي تحتاج إلى مخزون مشترك حيث تُخزن القطع ويستطيع جميع الموظفين المناوبين الوصول إليه. دمج مع نظام المخزون الخاص بخادمك، سواء كان ox_inventory، qb-inventory، أو حل مخصص، لإنشاء مخزون خاص بالورشة لا يمكن الوصول إليه إلا من قبل الميكانيكيين المصرح لهم. يجب أن يكون لمالك الورشة القدرة على طلب قطع من NPC مورد جملة، مما يخصم المال من حساب أعمال الورشة ويضيف القطع إلى المخزون بعد تأخير تسليم قابل للتكوين. هذا التأخير يحاكي لوجستيات سلسلة التوريد الواقعية ويمنع إعادة التخزين الفورية. تتبع الوصول إلى المخزون في سجل حتى يتمكن مالك الورشة من رؤية أي ميكانيكي أخذ أي قطع، مما يمنع السرقة الداخلية. على جانب العميل، استخدم تفاعل هدف على عنصر معين داخل الورشة، مثل خزانة الأدوات أو رف القطع، لفتح المخزون. اجمع المخزون مع نظام تصنيع حيث يمكن للميكانيكيين تجميع قطع معقدة من مواد خام، مثل دمج غلاف التيربو، الدافع، وصمام النفايات في مجموعة تيربو كاملة، مضيفاً طبقة مهارية إضافية للعمل.
شاحنة السحب والمساعدة على الطريق
توسيع وظيفة الميكانيكي لتشمل خدمات شاحنة السحب والمساعدة على الطريق يزيد بشكل كبير من نشاط الوظيفة وإمكانات الإيرادات. يجب أن يكون بإمكان الميكانيكيين استئجار شاحنة سحب من متجرهم، والقيادة إلى موقع لاعب عالق، وربط المركبة المعطلة باستخدام AttachEntityToEntity native، واسحبه إلى الورشة للإصلاحات. نفذ نظام إرسال حيث يمكن للاعبين طلب المساعدة على الطريق من خلال هاتفهم، مما ينشئ علامة على الخريطة مرئية لجميع الميكانيكيين المناوبين. يحصل أول ميكانيكي يقبل المكالمة على الوظيفة، ويتم رسم مسار GPS إلى موقع العميل. بالنسبة لنظام التثبيت، احسب مواقع الإزاحة المناسبة بناءً على طراز شاحنة السحب لضمان جلوس المركبة المسحوبة بشكل صحيح على السرير المسطح. أضف رسومًا تعتمد على المسافة لخدمة السحب بحيث تكلف السحوبات الطويلة العميل أكثر، مما يحفز الميكانيكيين على التعامل مع المكالمات في منطقتهم مع السماح لهم بأخذ وظائف مربحة لمسافات طويلة عبر الخريطة.