نظام العقارات في FiveM: وظائف الوكلاء وبيع العقارات
إطلاق اقتصاد وكلاء العقارات على خادم FiveM الخاص بك. قوائم، منازل مفتوحة، عمولات وأفضل سكربتات العقارات لخوادم QBCore و ESX.
Agency Scripts
المؤسس والمطور الرئيسي في Agency Scripts
هندسة نظام الممتلكات
يحول نظام العقارات خادم FiveM الخاص بك من مجموعة مبانٍ غير قابلة للوصول إلى سوق عقارات حي حيث يشتري اللاعبون ويبيعون ويؤجرون المنازل، الشقق، والمساحات التجارية. تركز البنية على قاعدة بيانات سجل الممتلكات التي تتتبع كل عقار متاح على الخادم مع موقعه، نوعه، المالك الحالي، القيمة السوقية، وتكوين الداخلية. على عكس نصوص الإسكان البسيطة التي تسمح فقط للاعبين بشراء قائمة محددة مسبقاً من الممتلكات، يتضمن نظام العقارات المصمم جيداً وظيفة وكيل عقارات تخلق معاملات يقودها اللاعبون مع عروض، تفاوض، وهياكل عمولة. يحتاج النظام إلى التكامل مع مورد قفل الأبواب الخاص بخادمك بحيث تمنح ملكية العقار أو تسحب الوصول تلقائياً إلى المبنى الفعلي، ومع أنظمة تحميل الداخلية مثل MLOs مخصصة أو الداخلية المنفصلة. خطط لثلاث فئات من الممتلكات من البداية: الممتلكات السكنية لسكن اللاعبين، الممتلكات التجارية للأعمال المملوكة للاعبين، والممتلكات المؤجرة التي تولد دخلاً سلبياً للمالكين.
قاعدة بيانات الممتلكات والقوائم
قاعدة بيانات العقارات هي الأساس الذي تبني عليه كل مكون آخر. يحتاج كل سجل عقار إلى بيانات وصفية واسعة تشمل الغلاف أو الديكور الداخلي MLO المستخدم، إحداثيات الدخول والخروج، توفر المرآب، سعة التخزين، وتاريخ التسعير. حافظ على جدول قوائم منفصل يتتبع العقارات المعروضة حاليًا في السوق، سعر الطلب، وكيل القائمة، ومدة الإدراج. يسمح هذا الفصل بتتبع تاريخ السوق وحساب اتجاهات قيمة العقارات مع مرور الوقت. فيما يلي مخطط قاعدة بيانات يدعم دورة حياة العقارات الكاملة:
CREATE TABLE IF NOT EXISTS properties (
id INT AUTO_INCREMENT PRIMARY KEY,
property_id VARCHAR(50) NOT NULL UNIQUE,
label VARCHAR(100) NOT NULL,
property_type ENUM('house', 'apartment', 'commercial', 'penthouse') NOT NULL,
owner VARCHAR(50) DEFAULT NULL,
price INT NOT NULL,
original_price INT NOT NULL,
interior VARCHAR(50) DEFAULT 'standard_1bed',
enter_coords JSON NOT NULL,
exit_coords JSON DEFAULT NULL,
garage_slots INT DEFAULT 0,
stash_slots INT DEFAULT 50,
has_wardrobe TINYINT DEFAULT 1,
is_furnished TINYINT DEFAULT 1,
neighborhood VARCHAR(50) DEFAULT 'downtown',
purchased_at BIGINT DEFAULT NULL,
INDEX idx_owner (owner),
INDEX idx_type (property_type),
INDEX idx_neighborhood (neighborhood)
);
CREATE TABLE IF NOT EXISTS property_listings (
id INT AUTO_INCREMENT PRIMARY KEY,
property_id VARCHAR(50) NOT NULL,
asking_price INT NOT NULL,
listing_agent VARCHAR(50) DEFAULT NULL,
listed_at BIGINT NOT NULL,
status ENUM('active', 'pending', 'sold', 'cancelled') DEFAULT 'active',
buyer VARCHAR(50) DEFAULT NULL,
sold_at BIGINT DEFAULT NULL,
sold_price INT DEFAULT NULL,
INDEX idx_status (status),
INDEX idx_agent (listing_agent)
);
املأ جدول العقارات بجميع العقارات المتوفرة على خادمك أثناء الإعداد الأولي. يجب أن يحتوي كل عقار على إحداثيات محددة بعناية لنقاط الدخول تتماشى مع مواقع الأبواب أو مناطق التفاعل. ال original_price الحقل يحفظ القيمة الأساسية لحسابات السوق بينما price الحقل يعكس القيمة السوقية الحالية التي تتغير بناءً على طلب الحي، التحسينات التي أجراها المالك، والظروف الاقتصادية العامة للخادم.
نظام وظيفة وكيل العقارات
وظيفة وكيل العقارات هي حيث يبرز العنصر البشري هذا النظام. يعمل الوكلاء كوسطاء بين المشترين والبائعين، يقومون بجولات عرض العقارات، التفاوض على الأسعار، وكسب عمولة على المبيعات المكتملة. عرّف درجات الوظيفة التي تفتح قوائم أفضل تدريجيًا: يتعامل الوكلاء المبتدئون مع الشقق والمنازل الصغيرة، ويصل الوكلاء الكبار إلى العقارات الفاخرة والمساحات التجارية، ومالك الوكالة يدير الفريق ويحصل على نسبة من جميع العمولات. عندما يتصل لاعب مهتم بشراء عقار بوكيل، يمكن للوكيل بدء جولة تعرض كلا اللاعبين إلى داخل العقار لجولة تعريفية. فيما يلي منطق العرض والمعاملة البيعية:
RegisterNetEvent('realestate:server:startShowing', function(propertyId, buyerId)
local src = source
local Agent = QBCore.Functions.GetPlayer(src)
local Buyer = QBCore.Functions.GetPlayer(buyerId)
if not Agent or not Buyer then return end
if Agent.PlayerData.job.name ~= 'realtor' then return end
local property = MySQL.query.await(
'SELECT * FROM properties WHERE property_id = ? AND owner IS NULL',
{propertyId}
)
if not property or not property[1] then
return TriggerClientEvent('QBCore:Notify', src, 'Property not available', 'error')
end
local prop = property[1]
local enterCoords = json.decode(prop.enter_coords)
-- Teleport both to property
TriggerClientEvent('realestate:client:enterShowing', src, prop, enterCoords)
TriggerClientEvent('realestate:client:enterShowing', buyerId, prop, enterCoords)
TriggerClientEvent('QBCore:Notify', buyerId,
'Showing: ' .. prop.label .. ' - $' .. prop.price, 'info')
end)
RegisterNetEvent('realestate:server:purchaseProperty', function(propertyId, buyerId, agreedPrice)
local src = source
local Agent = QBCore.Functions.GetPlayer(src)
local Buyer = QBCore.Functions.GetPlayer(buyerId)
if not Agent or not Buyer then return end
if Agent.PlayerData.job.name ~= 'realtor' then return end
local property = MySQL.query.await(
'SELECT * FROM properties WHERE property_id = ? AND owner IS NULL',
{propertyId}
)
if not property or not property[1] then return end
if not Buyer.Functions.RemoveMoney('bank', agreedPrice, 'property-purchase') then
return TriggerClientEvent('QBCore:Notify', src, 'Buyer cannot afford this', 'error')
end
local commission = math.floor(agreedPrice * 0.05)
Agent.Functions.AddMoney('bank', commission, 'realtor-commission')
MySQL.update(
'UPDATE properties SET owner = ?, price = ?, purchased_at = ? WHERE property_id = ?',
{Buyer.PlayerData.citizenid, agreedPrice, os.time(), propertyId}
)
MySQL.update(
'UPDATE property_listings SET status = "sold", buyer = ?, sold_at = ?, sold_price = ? WHERE property_id = ? AND status = "active"',
{Buyer.PlayerData.citizenid, os.time(), agreedPrice, propertyId}
)
TriggerClientEvent('QBCore:Notify', src,
'Sale complete! Commission: $' .. commission, 'success')
TriggerClientEvent('QBCore:Notify', buyerId,
'Congratulations! You now own ' .. property[1].label, 'success')
TriggerEvent('doorlock:server:updateProperty', propertyId, Buyer.PlayerData.citizenid)
end)
يجب أن يكون معدل العمولة قابلًا للتكوين وربما متغيرًا بناءً على مستوى وكيل العقارات. قد يكسب الوكلاء الكبار عمولة 7% بينما يكسب الصغار 3% فقط، مما يحفز التقدم الوظيفي داخل وظيفة العقارات. تتبع إجمالي حجم مبيعات كل وكيل وأرباح العمولة في قاعدة البيانات لإنشاء لوحات أداء وفتح مكافآت للأداءين الأعلى.
نظام الرهن العقاري والتمويل
لا يستطيع معظم اللاعبين شراء العقارات ذات القيمة العالية نقداً، لذا يزيد نظام الرهن العقاري بشكل كبير من مجموعة المشترين المحتملين ويجعل سوق العقارات متاحًا للاعبين الجدد. عندما لا يستطيع المشتري دفع السعر الكامل، قدم خيار الرهن العقاري الذي يتطلب نسبة دفعة أولى قابلة للتكوين، عادة 10-20% من قيمة العقار. يتم دفع الرصيد المتبقي على أقساط منتظمة تُخصم تلقائياً من حساب اللاعب المصرفي عند كل إعادة تشغيل للخادم أو في فترات مجدولة. تتبع تفاصيل الرهن العقاري بما في ذلك المبلغ الأساسي، سعر الفائدة، الرصيد المتبقي، مبلغ الدفعة الشهرية، وتاريخ الدفعات. إذا تخلف اللاعب عن العديد من الدفعات المتتالية، يتم تفعيل عملية الحجز التي تستعيد العقار وتعيد إدراجه في السوق بخصم. يخلق سعر الفائدة تكلفة حقيقية على الاقتراض تشجع اللاعبين على سداد الرهن العقاري مبكراً عند الإمكان، ويولد النظام استنزافًا ثابتًا في عرض النقود يساعد في مكافحة التضخم في اقتصاد الخادم.
نظام دخل الإيجار ونظام المالك
يجب أن يكون بإمكان اللاعبين الذين يمتلكون عدة عقارات تأجيرها للحصول على دخل سلبي، مما يخلق ديناميكية مالك-مستأجر تولد لعب أدوار مستمر. عندما يريد مالك العقار تأجير ممتلكاته، يحدد مبلغ الإيجار الشهري ويعرضها كعقارات متاحة للإيجار عبر وكالة العقارات أو قائمة مباشرة. يحصل المستأجرون على مفتاح الوصول إلى العقار ويمكنهم استخدام التخزين، والخزانة، والمرآب طوال مدة عقد الإيجار. يتم جمع الإيجار تلقائيًا وفق جدول قابل للتكوين ويودع في حساب المالك البنكي. إليك تنفيذ نظام الإيجار:
Config.RentalSettings = {
minRentPrice = 100,
maxRentPrice = 50000,
collectionInterval = 86400, -- every 24 hours real time
maxMissedPayments = 3,
depositMultiplier = 2, -- security deposit = 2x monthly rent
}
RegisterNetEvent('realestate:server:listForRent', function(propertyId, rentPrice)
local src = source
local Player = QBCore.Functions.GetPlayer(src)
if not Player then return end
local property = MySQL.query.await(
'SELECT * FROM properties WHERE property_id = ? AND owner = ?',
{propertyId, Player.PlayerData.citizenid}
)
if not property or not property[1] then return end
if rentPrice < Config.RentalSettings.minRentPrice or
rentPrice > Config.RentalSettings.maxRentPrice then
return TriggerClientEvent('QBCore:Notify', src, 'Invalid rent price', 'error')
end
MySQL.insert(
'INSERT INTO property_rentals (property_id, landlord, rent_price, deposit, listed_at) VALUES (?, ?, ?, ?, ?)',
{propertyId, Player.PlayerData.citizenid, rentPrice,
rentPrice * Config.RentalSettings.depositMultiplier, os.time()}
)
TriggerClientEvent('QBCore:Notify', src,
'Property listed for rent at $' .. rentPrice .. '/day', 'success')
end)
-- Automated rent collection thread
CreateThread(function()
while true do
Wait(1000 * 60 * 60) -- Check every hour
local rentals = MySQL.query.await(
'SELECT * FROM property_rentals WHERE tenant IS NOT NULL AND status = "active"'
)
for _, rental in ipairs(rentals or {}) do
if os.time() - rental.last_collected >= Config.RentalSettings.collectionInterval then
local success = ProcessRentPayment(rental)
if not success then
HandleMissedPayment(rental)
end
end
end
end
end)
تحمي وديعة الأمان الملاك من المستأجرين الذين يتسببون في أضرار للعقار أو يغادرون دون إشعار. عند انتهاء عقد الإيجار، تُعاد الوديعة مع خصم أي تكاليف أضرار. نفذ نظام حالة العقار الذي يتتبع التآكل والتمزق، مما يمنح الملاك سببًا مشروعًا لحجز جزء من الوديعة إذا لم يكن المستأجر حذرًا مع العقار. هذا يخلق سيناريوهات حل نزاعات طبيعية تدفع لعب الأدوار بين الملاك والمستأجرين.
تسعير السوق الديناميكي
سوق عقارات ثابت يصبح مملًا بسرعة بمجرد شراء كل العقارات المرغوبة. التسعير الديناميكي يحافظ على السوق مثيرًا من خلال تعديل قيم العقارات بناءً على النشاط الحقيقي في السيرفر. تتبع عدة عوامل تؤثر على قيمة الحي: عدد المبيعات الأخيرة في المنطقة، القرب من المواقع الشعبية مثل مراكز العمل والمتاجر، إحصائيات الجريمة من نظام الشرطة، والنسبة العامة للعقارات المشغولة إلى الفارغة. عندما يكون الطلب مرتفعًا مع العديد من المشتريات الحديثة، ترتفع قيم العقارات تدريجيًا، مما يخلق عنصرًا مضاربيًا حيث يربح المشترون الأوائل في المناطق الناشئة مع تطور الحي. على العكس، المناطق ذات الجريمة العالية أو التي تحتوي على العديد من العقارات الفارغة تشهد انخفاضًا في القيم. شغّل تعديل السوق كمهام مجدولة على السيرفر تعيد حساب كل قيم العقارات أسبوعيًا، مع تطبيق التغييرات تدريجيًا لتجنب صدمات السوق المفاجئة. عرض اتجاهات السوق في لوحة NUI لوكالة العقارات حتى يتمكن الوكلاء والمشترون من اتخاذ قرارات مستنيرة حول مكان الاستثمار.
عرض الممتلكات وواجهة NUI
واجهة NUI هي حيث يتصفح الوكلاء والمشترون العقارات المتاحة، يراجعون التفاصيل، ويبدؤون المعاملات. أنشئ صفحة قائمة عقارات نظيفة واحترافية تعرض كل عقار متاح مع صورته أو معاينة داخلية، الموقع على الخريطة، السعر، عدد الغرف، سعة المرآب، وتقييم الحي. ضمن خيارات التصفية والفرز ليتمكن المستخدمون من تضييق النتائج حسب نوع العقار، نطاق السعر، والموقع. عندما يختار الوكيل عقارًا للعرض، يجب أن يعرض النظام مسارًا إلى مدخل العقار وينبه المشتري للمتابعة. داخل العقار، قدم وضع جولة تفاعلية حيث يمكن للوكيل إبراز الميزات باستخدام علامات ثلاثية الأبعاد أو تعليقات نصية. بعد العرض، قدم للمشتري لوحة تأكيد شراء أو إيجار تعرض التفصيل المالي الكامل بما في ذلك السعر، الضرائب، عمولة الوكيل، وشروط الرهن العقاري إذا كانت مطبقة. يجب أن تتضمن الواجهة أيضًا تبويب سجل المعاملات حيث يمكن للاعبين مراجعة مشترياتهم، مبيعاتهم، ومدفوعات الإيجار السابقة للشفافية المالية الكاملة.
تخصيص الممتلكات وتقدير القيمة
السماح للمالكين بتخصيص ممتلكاتهم يضيف لمسة شخصية ويخلق اقتصاد ترقية يزيد من قيمة الممتلكات مع مرور الوقت. نفذ نظام وضع الأثاث الذي يتيح للاعبين شراء وتحديد مواقع قطع الأثاث داخل داخلية ممتلكاتهم، مشابه لكيفية تعامل GTA Online مع تخصيص الشقق. كل قطعة أثاث يتم شراؤها تزيد من قيمة الممتلكات المحسنة، لذا عندما يبيع المالك في النهاية، يسترد جزءًا من استثماره في الأثاث. إلى جانب الأثاث، قدم ترقيات هيكلية مثل تجديد المطابخ، إعادة تصميم الحمامات، وتركيب أنظمة الأمان التي تزيد من القيمة الأساسية للممتلكات بنسبة مئوية. تتبع كل التحسينات في جدول تعديلات الممتلكات حتى تعرض قائمة العقارات للمشترين المحتملين بالضبط ما هي الترقيات التي تم إجراؤها. يجب أن تدعم الممتلكات التجارية ترقيات خاصة بالأعمال مثل صناديق العرض للمتاجر، معدات المطبخ للمطاعم، أو رفوف الخوادم لشركات التكنولوجيا، مما يربط نظام الممتلكات بدورة اللعب الأوسع للأعمال. يخلق الجمع بين ديناميكيات السوق المعتمدة على الموقع والتحسينات التي يقودها اللاعبون نظامًا بيئيًا غنيًا للعقارات حيث تصبح العقارات استراتيجية شرعية لبناء الثروة بدلاً من مجرد مكان لتخزين الأشياء وتغيير الملابس.