返回博客
Tutorial4 分钟阅读

FiveM机械师系统:调校、维修与车辆职业

构建完整的 FiveM 机械师系统。维修、调校、扣押拖车、商店 UI 以及适用于 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发送任意服务请求。服务器回调应检查玩家当前职业、等级以及请求服务是否存在于该等级的服务列表中,然后再执行修理。

车辆维修机制和零件系统

真实的维修机制超越单一的 native 调用。修车不再是瞬间完成,而是分步骤进行,需要时间并消耗机械师库存中的零件。当玩家带车来修发动机时,机械师必须在个人库存或店铺共享仓库中拥有正确的零件。使用进度条系统模拟维修时间,创造客户等待并与机械师互动的自然角色扮演时刻。维修逻辑应使用 native 函数针对特定车辆部件,这样可以修发动机而不修车身损伤,或更换轮胎而不影响发动机健康:

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批发商处备货,或让玩家在工业地点制造。修理店可批量购买零件以批发价存入共享库存,店主需管理供应链以保持业务顺畅。此供需动态极大丰富了角色扮演体验。

喷漆和美容服务

喷漆是机械车间提供的最具视觉满足感的服务之一,良好的实现需要使用多个车辆颜色 native 函数。FiveM 提供了主色、次色、珠光色、轮毂颜色和自定义 RGB 值的 natives。构建一个颜色选择器 UI,让机械师可以从预设颜色中选择或输入自定义 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)

除了基础喷漆,考虑添加涂装应用、霓虹灯安装和车窗染色作为独立的美容服务。每项服务应有自己的零件需求和价格点。例如,霓虹灯需要零件库存中的霓虹套件,并使用 SetVehicleNeonLightEnabledSetVehicleNeonLightsColour 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 路线到客户位置。对于连接系统,根据拖车模型计算正确的偏移位置,确保被拖车辆正确放置在平板上。对拖车服务添加基于距离的费用,距离越长费用越高,激励机械师优先处理本区域呼叫,同时仍允许他们接收跨地图的高利润长途任务。

准备好开始了吗?

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