返回博客
Tutorial3 分钟阅读

FiveM 房地产系统:房地产经纪人工作与房产销售

在您的FiveM服务器上启动房地产经济。房源、开放日、佣金及适用于QBCore和ESX服务器的顶级房地产脚本。

Agency Scripts

Agency Scripts 创始人兼首席开发者

物业系统架构

房地产系统将您的 FiveM 服务器从一堆无法进入的建筑转变为活跃的房产市场,玩家可以买卖和出租住宅、公寓及商业空间。架构围绕房产登记数据库,跟踪服务器上每个可用房产的位置、类型、当前所有者、市场价值和内部配置。与仅允许玩家购买预设房产列表的简单住房脚本不同,设计良好的房地产系统包含房地产经纪人职位,创造玩家驱动的交易,包括看房、谈判和佣金结构。系统需与服务器的门锁资源集成,使房产所有权自动授予或撤销对实体建筑的访问权限,并与定制 MLO 或实例化内部加载系统集成。初期规划三类房产:用于玩家居住的住宅、玩家拥有的商业物业和为房东产生被动收入的出租物业。

物业数据库与列表

物业数据库是所有其他组件构建的基础。每条物业记录需要包含大量元数据,包括所用壳体或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界面是代理和买家浏览可售房产、查看详情和发起交易的地方。构建干净专业的房产列表页面,显示每个房产的照片或内部预览、地图位置、价格、房间数、车库容量和社区评分。包括筛选和排序选项,方便用户按房产类型、价格区间和位置缩小结果。当代理选择房产进行展示时,系统应显示到房产入口的路线并通知买家跟随。房产内提供互动导览模式,代理可用3D标记或文字注释突出展示特色。展示结束后,向买家展示购买或租赁确认面板,显示完整财务明细,包括价格、税费、代理佣金及按揭条款(如适用)。界面还应包含交易历史标签,供玩家查看过去的购买、销售和租金支付,确保财务透明。

物业定制与价值增值

允许业主自定义房产增添个人特色,创造升级经济,提升房产价值。实现家具摆放系统,允许玩家购买并在房产内部摆放家具,类似GTA Online的公寓定制。每件家具购买都会提升房产改善价值,业主最终出售时可回收部分家具投资。除家具外,提供结构升级,如厨房装修、浴室改造和安全系统安装,按百分比提升房产基础价值。所有改进记录在房产改造表中,房产列表向潜在买家展示具体升级内容。商业房产支持特定业务升级,如商店展示柜、餐厅厨房设备或科技公司服务器机架,将房产系统与更广泛的商业玩法循环结合。基于位置的市场动态与玩家驱动的改进相结合,打造丰富的房产生态系统,使房地产成为合法的财富积累策略,而不仅仅是存放物品和换装的场所。

准备好开始了吗?

在我们的商店获取脚本,或加入 Discord 获取支持、更新以及新功能预告。