ब्लॉग पर वापस
Guide9 मिनट पढ़ें

FiveM क्राफ्टिंग सिस्टम: रेसिपी, स्टेशन और प्रगति

एक गहरा FiveM क्राफ्टिंग सिस्टम बनाएं। रेसिपी लॉजिक, वर्कबेंच, कौशल प्रगति और QBCore, ESX और ox_inventory के लिए सर्वश्रेष्ठ क्राफ्टिंग स्क्रिप्ट।

Agency Scripts

Agency Scripts के संस्थापक और प्रमुख डेवलपर

क्यों क्राफ्टिंग सिस्टम खिलाड़ी की भागीदारी बढ़ाते हैं

एक क्राफ्टिंग सिस्टम खिलाड़ियों को संसाधन इकट्ठा करने, मानचित्र का अन्वेषण करने, व्यापार के लिए अन्य खिलाड़ियों के साथ बातचीत करने, और कुछ ठोस बनाने में समय निवेश करने का कारण देता है। क्राफ्टिंग के बिना, आइटम या तो अनंत स्टॉक वाली दुकानों से आते हैं या एडमिन स्पॉन से, जो अर्थव्यवस्था को सपाट कर देते हैं और खिलाड़ी की एजेंसी को हटा देते हैं। एक अच्छी डिज़ाइन किया गया क्राफ्टिंग सिस्टम आपूर्ति श्रृंखलाएँ बनाता है जहाँ एक खिलाड़ी कच्चा माल खनन करता है, दूसरा उसे परिष्कृत करता है, और तीसरा अंतिम उत्पाद असेंबल करता है। यह परस्पर निर्भरता जैविक रोलप्ले इंटरैक्शन और आर्थिक गतिविधि उत्पन्न करती है। हथियार क्राफ्टिंग अपराधी अंडरवर्ल्ड में जोखिम और पुरस्कार की एक परत जोड़ती है, जबकि खाना पकाने, दर्जी, या इलेक्ट्रॉनिक्स मरम्मत जैसे कानूनी क्राफ्टिंग रास्ते वैध आय स्रोत प्रदान करते हैं। इस गाइड में, हम रेसिपी, वर्कबेंच, कौशल प्रगति, और ब्लूप्रिंट खोज के साथ एक पूर्ण क्राफ्टिंग फ्रेमवर्क बनाएंगे।

रेसिपी प्रणाली डिजाइन करना

रेसिपी किसी भी क्राफ्टिंग सिस्टम की मुख्य डेटा संरचना होती हैं। प्रत्येक रेसिपी यह परिभाषित करती है कि कौन से आइटम इनपुट के रूप में आवश्यक हैं, कौन सा आइटम आउटपुट के रूप में उत्पादित होता है, क्राफ्टिंग प्रक्रिया में कितना समय लगता है, कौन सा कौशल स्तर आवश्यक है, और वैकल्पिक रूप से कौन सा वर्कबेंच प्रकार आवश्यक है। रेसिपी को एक साझा कॉन्फ़िगरेशन फ़ाइल में स्टोर करें जिसे क्लाइंट और सर्वर दोनों एक्सेस कर सकें, क्योंकि क्लाइंट को क्राफ्टिंग UI दिखाने के लिए रेसिपी डेटा चाहिए जबकि सर्वर को क्राफ्टिंग प्रयासों को मान्य करने के लिए। तेज़ लुकअप के लिए एक अद्वितीय रेसिपी ID द्वारा इंडेक्स किए गए फ्लैट टेबल का उपयोग करें। शामिल करें category यूआई में रेसिपी को व्यवस्थित करने के लिए फ़ील्ड और एक successChance फ़ील्ड जिसे खिलाड़ी के कौशल स्तर द्वारा संशोधित किया जा सकता है, उच्च-स्तरीय क्राफ्टिंग में जोखिम तत्व जोड़ता है।

-- shared/config.lua (accessible by both client and server)
Config = {}

Config.Recipes = {
    -- Weapons
    pistol_craft = {
        label      = 'Craft Pistol',
        category   = 'weapons',
        result     = {item = 'weapon_pistol', count = 1},
        ingredients = {
            {item = 'steel_plate',    count = 3},
            {item = 'weapon_spring',  count = 2},
            {item = 'rubber_grip',    count = 1},
            {item = 'gun_oil',        count = 1},
        },
        duration    = 15000,  -- 15 seconds
        skillReq    = 50,     -- crafting skill level
        workbench   = 'weapons_bench',
        successBase = 0.75,   -- 75% base success
        xpReward    = 25,
    },
    -- Electronics
    radio_craft = {
        label      = 'Assemble Radio',
        category   = 'electronics',
        result     = {item = 'radio', count = 1},
        ingredients = {
            {item = 'electronic_parts', count = 2},
            {item = 'copper_wire',      count = 3},
            {item = 'plastic_casing',   count = 1},
        },
        duration    = 8000,
        skillReq    = 10,
        workbench   = 'electronics_bench',
        successBase = 0.90,
        xpReward    = 10,
    },
    -- Cooking
    sandwich_craft = {
        label      = 'Make Sandwich',
        category   = 'cooking',
        result     = {item = 'sandwich', count = 2},
        ingredients = {
            {item = 'bread',   count = 2},
            {item = 'cheese',  count = 1},
            {item = 'lettuce', count = 1},
        },
        duration    = 3000,
        skillReq    = 0,
        workbench   = 'kitchen',
        successBase = 1.0,
        xpReward    = 3,
    },
}

Config.Workbenches = {
    weapons_bench = {
        label  = 'Weapons Workbench',
        model  = 'prop_tool_bench02',
        coords = {
            {x = 1083.5, y = -1975.3, z = 31.5, h = 140.0},
        },
    },
    electronics_bench = {
        label  = 'Electronics Station',
        model  = 'prop_cs_server_drive',
        coords = {
            {x = 732.1, y = -1073.8, z = 22.2, h = 90.0},
        },
    },
    kitchen = {
        label  = 'Kitchen',
        model  = 'prop_cooker_03',
        coords = {
            {x = -1192.3, y = -896.1, z = 13.9, h = 35.0},
        },
    },
}

सर्वर-साइड क्राफ्टिंग लॉजिक

सभी क्राफ्टिंग सत्यापन सर्वर पर होना चाहिए ताकि शोषण से बचा जा सके। सर्वर जांचता है कि खिलाड़ी के पास आवश्यक सामग्री उनके इन्वेंटरी में है, कौशल आवश्यकता पूरी करता है, सही वर्कबेंच प्रकार के पास है, और पहले से क्राफ्टिंग प्रक्रिया में नहीं है। सत्यापन के बाद, यह सामग्री हटा देता है, अवधि टाइमर चलाता है, कौशल स्तर द्वारा संशोधित आधार संभावना के आधार पर सफलता की गणना करता है, और या तो क्राफ्ट किया गया आइटम प्रदान करता है या विफलता पर आंशिक सामग्री लौटाता है। क्लाइंट पर क्राफ्टिंग परिणामों की रिपोर्ट करने पर कभी भरोसा न करें। क्लाइंट को केवल एक विशिष्ट रेसिपी आईडी क्राफ्ट करने का अनुरोध भेजना चाहिए, और सर्वर स्वतंत्र रूप से हर शर्त की पुष्टि करता है। यह आइटम डुप्लीकेशन शोषण को रोकता है जहां संशोधित क्लाइंट बिना सामग्री खर्च किए आइटम क्राफ्ट करने का दावा करता है।

-- server.lua
local craftingPlayers = {} -- track who is currently crafting
local playerSkills = {}    -- cache of player crafting XP

RegisterNetEvent('crafting:attempt', function(recipeId)
    local src = source
    if craftingPlayers[src] then
        TriggerClientEvent('notifications:show', src, 'Error', 'Already crafting.', 'error')
        return
    end

    local recipe = Config.Recipes[recipeId]
    if not recipe then return end

    -- Check skill requirement
    local skill = GetPlayerCraftingSkill(src)
    if skill < recipe.skillReq then
        TriggerClientEvent('notifications:show', src,
            'Error', 'Requires crafting level ' .. recipe.skillReq .. '.', 'error')
        return
    end

    -- Check ingredients
    for _, ing in ipairs(recipe.ingredients) do
        local count = exports.ox_inventory:GetItemCount(src, ing.item)
        if count < ing.count then
            TriggerClientEvent('notifications:show', src,
                'Error', 'Missing materials.', 'error')
            return
        end
    end

    -- Remove ingredients
    for _, ing in ipairs(recipe.ingredients) do
        exports.ox_inventory:RemoveItem(src, ing.item, ing.count)
    end

    craftingPlayers[src] = true
    TriggerClientEvent('crafting:startProgress', src, recipe.duration, recipe.label)

    -- Wait for crafting duration
    SetTimeout(recipe.duration, function()
        craftingPlayers[src] = nil

        -- Calculate success chance (skill bonus caps at +20%)
        local skillBonus = math.min((skill - recipe.skillReq) * 0.005, 0.20)
        local finalChance = math.min(recipe.successBase + skillBonus, 1.0)

        if math.random() <= finalChance then
            exports.ox_inventory:AddItem(src, recipe.result.item, recipe.result.count)
            AddCraftingXP(src, recipe.xpReward)
            TriggerClientEvent('notifications:show', src,
                'Success', 'Crafted: ' .. recipe.label, 'success')
        else
            -- Return 50% of materials on failure
            for _, ing in ipairs(recipe.ingredients) do
                local refund = math.floor(ing.count * 0.5)
                if refund > 0 then
                    exports.ox_inventory:AddItem(src, ing.item, refund)
                end
            end
            TriggerClientEvent('notifications:show', src,
                'Failed', 'Crafting failed. Some materials recovered.', 'error')
        end
    end)
end)

Skill Progression और अनुभव

एक कौशल प्रगति प्रणाली लंबे समय के लक्ष्य और उन खिलाड़ियों के लिए उन्नति की भावना जोड़ती है जो क्राफ्टिंग में निवेश करते हैं। प्रत्येक सफल क्राफ्ट अनुभव अंक प्रदान करता है जो कौशल स्तरों की ओर जमा होते हैं। उच्च कौशल स्तर उन्नत रेसिपी अनलॉक करते हैं और कठिन क्राफ्ट की सफलता दर बढ़ाते हैं। कौशल डेटा को प्रत्येक चरित्र के लिए डेटाबेस में संग्रहित करें ताकि यह स्थायी रहे और खिलाड़ी खाते के बजाय चरित्र से जुड़ा हो। स्पष्ट मील के पत्थर परिभाषित करें: स्तर 0-10 बुनियादी खाना पकाने और मरम्मत की अनुमति देता है, स्तर 11-30 इलेक्ट्रॉनिक्स और बुनियादी उपकरण खोलता है, स्तर 31-60 हथियार घटक और उन्नत निर्माण सक्षम करता है, और स्तर 61-100 दुर्लभ और दंतकथात्मक रेसिपी अनलॉक करता है। वर्तमान कौशल स्तर और प्रगति बार को क्राफ्टिंग UI में प्रदर्शित करें ताकि खिलाड़ी हमेशा अगली अनलॉक के करीब होने का पता लगा सकें।

-- Skill system (server.lua continued)
local skillCache = {}

function GetPlayerCraftingSkill(src)
    if skillCache[src] then return skillCache[src].level end
    return 0
end

function AddCraftingXP(src, amount)
    if not skillCache[src] then
        skillCache[src] = {xp = 0, level = 0}
    end

    skillCache[src].xp = skillCache[src].xp + amount
    local newLevel = CalculateLevel(skillCache[src].xp)

    if newLevel > skillCache[src].level then
        skillCache[src].level = newLevel
        TriggerClientEvent('notifications:show', src,
            'Level Up!', 'Crafting level: ' .. newLevel, 'success', 6000)
        -- Check for newly unlocked recipes
        local unlocked = GetNewlyUnlockedRecipes(newLevel, skillCache[src].level)
        for _, recipe in ipairs(unlocked) do
            TriggerClientEvent('notifications:show', src,
                'Recipe Unlocked', recipe.label .. ' is now available.', 'info', 5000)
        end
    end

    skillCache[src].level = newLevel
    SaveCraftingSkill(src)
end

function CalculateLevel(xp)
    -- Each level requires progressively more XP
    -- Level 1 = 50xp, Level 2 = 150xp, Level 10 = 2750xp, etc.
    local level = 0
    local required = 50
    local remaining = xp
    while remaining >= required and level < 100 do
        remaining = remaining - required
        level = level + 1
        required = math.floor(required * 1.15)
    end
    return level
end

function SaveCraftingSkill(src)
    local charId = exports['multichar']:GetCharacterId(src)
    if not charId or not skillCache[src] then return end
    MySQL.update(
        'INSERT INTO character_skills (character_id, skill, xp, level) VALUES (?, ?, ?, ?) ON DUPLICATE KEY UPDATE xp = ?, level = ?',
        {charId, 'crafting', skillCache[src].xp, skillCache[src].level, skillCache[src].xp, skillCache[src].level}
    )
end

वर्कबेंच इंटरैक्शन और क्लाइंट UI

वर्कबेंच गेम दुनिया में भौतिक स्थान होते हैं जहाँ खिलाड़ी वस्तुएं बनाने जाते हैं। प्रत्येक वर्कबेंच प्रकार एक विशिष्ट श्रेणी की रेसिपी का समर्थन करता है: हथियारों के लिए हथियार बेंच, भोजन के लिए रसोई, गैजेट्स के लिए इलेक्ट्रॉनिक्स स्टेशन। क्लाइंट पक्ष पर, प्रत्येक वर्कबेंच स्थान पर एक मार्कर स्पॉन करें या ox_target जैसे लक्ष्य प्रणाली का उपयोग करके इंटरैक्शन पॉइंट बनाएं। जब खिलाड़ी इंटरैक्ट करता है, तो उनकी निकटता जांचें, वर्कबेंच प्रकार और खिलाड़ी के कौशल स्तर के आधार पर उपलब्ध रेसिपी को फ़िल्टर करें, और क्राफ्टिंग UI खोलें। UI को प्रत्येक रेसिपी का नाम, आवश्यक सामग्री के साथ वर्तमान इन्वेंटरी गणना, सफलता की संभावना, क्राफ्टिंग समय, और कौशल XP पुरस्कार दिखाना चाहिए। उन रेसिपी को हाइलाइट करें जहाँ खिलाड़ी के पास सभी आवश्यक सामग्री है और उन रेसिपी को धुंधला करें जहाँ सामग्री गायब है। क्राफ्टिंग के दौरान, उपयुक्त एनिमेशन चलाएं, प्रगति बार दिखाएं, और खिलाड़ी की गति को अक्षम करें ताकि खिलाड़ी क्राफ्ट के बीच में चलकर शोषण न कर सकें।

-- client.lua
local isCrafting = false

-- Setup workbench interaction points
CreateThread(function()
    for benchType, bench in pairs(Config.Workbenches) do
        for _, loc in ipairs(bench.coords) do
            exports.ox_target:addSphereZone({
                coords = vector3(loc.x, loc.y, loc.z),
                radius = 1.5,
                options = {
                    {
                        label = 'Use ' .. bench.label,
                        icon  = 'fas fa-tools',
                        onSelect = function()
                            OpenCraftingMenu(benchType)
                        end,
                        canInteract = function()
                            return not isCrafting
                        end
                    }
                }
            })
        end
    end
end)

function OpenCraftingMenu(benchType)
    local recipes = {}
    for id, recipe in pairs(Config.Recipes) do
        if recipe.workbench == benchType then
            recipes[#recipes + 1] = {
                id          = id,
                label       = recipe.label,
                category    = recipe.category,
                ingredients = recipe.ingredients,
                duration    = recipe.duration,
                skillReq    = recipe.skillReq,
                successBase = recipe.successBase,
                xpReward    = recipe.xpReward,
                result      = recipe.result,
            }
        end
    end
    -- Send to NUI or use a menu library
    SendNUIMessage({action = 'openCrafting', recipes = recipes, benchType = benchType})
    SetNuiFocus(true, true)
end

RegisterNetEvent('crafting:startProgress', function(duration, label)
    isCrafting = true
    local ped = PlayerPedId()
    FreezeEntityPosition(ped, true)

    -- Play crafting animation
    RequestAnimDict('mini@repair')
    while not HasAnimDictLoaded('mini@repair') do Wait(10) end
    TaskPlayAnim(ped, 'mini@repair', 'fixing_a_player', 8.0, -8.0, -1, 1, 0, false, false, false)

    -- Progress bar via ox_lib or custom NUI
    exports.ox_lib:progressBar({
        duration = duration,
        label    = label,
        useWhileDead  = false,
        canCancel     = false,
        disable = {move = true, car = true, combat = true},
    })

    ClearPedTasks(ped)
    FreezeEntityPosition(ped, false)
    isCrafting = false
end)

उत्पादन श्रृंखलाएँ और सामग्री संग्रहण

उत्पादन श्रृंखलाएँ गहराई पैदा करती हैं क्योंकि खिलाड़ियों को अंतिम उत्पाद बनाने से पहले कच्चे माल को कई चरणों से गुजरना पड़ता है। लोहे के अयस्क को भट्टी में स्टील के इनगॉट में पिघलाना होता है, जिन्हें फिर धातु कार्यशाला में स्टील प्लेटों में दबाया जाता है, जिन्हें अंत में हथियार बेंच पर आग्नेयास्त्र बनाने के लिए उपयोग किया जाता है। प्रत्येक चरण के लिए अलग कार्यशाला स्थान और संभवतः अलग कौशल की आवश्यकता होती है। यह श्रृंखला डिजाइन विशेषज्ञता को प्रोत्साहित करती है, जहाँ कुछ खिलाड़ी खनन और पिघलाने पर ध्यान केंद्रित करते हैं जबकि अन्य असेंबली में विशेषज्ञ होते हैं। सामग्री संग्रहण बिंदुओं को मानचित्र पर ज़ोन के रूप में फैलाना चाहिए जहाँ खिलाड़ी खनन, पौधों की कटाई, या जंकयार्ड से इलेक्ट्रॉनिक घटकों की खोज जैसे संग्रहण क्रियाएँ कर सकते हैं। संग्रहण नोड्स पर पुनःस्पॉन टाइमर का उपयोग करें ताकि संसाधन सीमित रहें और खिलाड़ी पहुँच के लिए प्रतिस्पर्धा करें, जिससे क्षेत्र-आधारित भूमिका निभाने वाले संघर्ष उत्पन्न हों।

ब्लूप्रिंट डिस्कवरी और दुर्लभ रेसिपीज़

सभी रेसिपी शुरू से उपलब्ध नहीं होनी चाहिए। ब्लूप्रिंट खोज एक अन्वेषण और प्रगति प्रोत्साहन जोड़ती है जो उन्नत रेसिपी को लूट ड्रॉप, क्वेस्ट पूर्णता, NPC खरीद, या यादृच्छिक विश्व घटनाओं के पीछे छुपाती है। जब कोई खिलाड़ी ब्लूप्रिंट खोजता है, तो वह उनके व्यक्तिगत रेसिपी सूची में जोड़ा जाता है जो डेटाबेस में संग्रहित होती है। ब्लूप्रिंट ट्रेडेबल आइटम हो सकते हैं, जो एक द्वितीयक बाजार बनाते हैं जहां दुर्लभ रेसिपी मूल्यवान वस्तुएं बन जाती हैं। उदाहरण के लिए, एक सैन्य-ग्रेड हथियार ब्लूप्रिंट केवल कुछ हीस्ट पुरस्कारों से गिर सकता है, जबकि एक दंतकथात्मक खाना पकाने की रेसिपी एक छिपे हुए NPC से खरीदी जा सकती है जो केवल विशिष्ट समय पर प्रकट होता है। क्राफ्टिंग UI खिलाड़ी के अनलॉक किए गए ब्लूप्रिंट्स को पूर्ण रेसिपी सूची के साथ जांचता है और केवल वे रेसिपी दिखाता है जिन तक उनकी पहुंच है, जिससे प्रगति की भावना पैदा होती है जहां हर नया ब्लूप्रिंट एक महत्वपूर्ण अनलॉक की तरह महसूस होता है जो उनकी क्राफ्टिंग क्षमताओं का विस्तार करता है।

-- Blueprint system (server.lua)
RegisterNetEvent('crafting:useBlueprint', function(blueprintItem)
    local src = source
    local charId = exports['multichar']:GetCharacterId(src)
    if not charId then return end

    -- Validate the player has the blueprint item
    local count = exports.ox_inventory:GetItemCount(src, blueprintItem)
    if count < 1 then return end

    -- Map blueprint items to recipe IDs
    local blueprintMap = {
        blueprint_pistol     = 'pistol_craft',
        blueprint_radio      = 'radio_craft',
        blueprint_armor      = 'armor_craft',
        blueprint_lockpick   = 'adv_lockpick_craft',
    }

    local recipeId = blueprintMap[blueprintItem]
    if not recipeId then return end

    -- Check if already unlocked
    local exists = MySQL.scalar.await(
        'SELECT 1 FROM character_blueprints WHERE character_id = ? AND recipe_id = ?',
        {charId, recipeId}
    )
    if exists then
        TriggerClientEvent('notifications:show', src,
            'Info', 'You already know this recipe.', 'info')
        return
    end

    -- Consume blueprint and unlock recipe
    exports.ox_inventory:RemoveItem(src, blueprintItem, 1)
    MySQL.insert(
        'INSERT INTO character_blueprints (character_id, recipe_id) VALUES (?, ?)',
        {charId, recipeId}
    )

    local recipe = Config.Recipes[recipeId]
    TriggerClientEvent('notifications:show', src,
        'Blueprint Learned', 'You can now craft: ' .. (recipe and recipe.label or recipeId), 'success', 6000)
end)

शुरू करने के लिए तैयार?

हमारी शॉप से स्क्रिप्ट लें, या सपोर्ट, अपडेट और आगे आने वाली चीज़ों की झलक के लिए Discord से जुड़ें।