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

FiveM प्रतिष्ठा सिस्टम: आँकड़े, कौशल और प्रगति

FiveM में प्रतिष्ठा और कौशल प्रगति जोड़ें। स्टैट सूत्र, अनलॉक, UI बार और खिलाड़ियों को लंबे समय तक मेहनत करने के लिए प्रेरित करने वाली सर्वश्रेष्ठ प्रतिष्ठा स्क्रिप्ट।

Agency Scripts

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

रेपुटेशन सिस्टम रोलप्ले को कैसे बदलते हैं

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

प्लेयर स्किल्स के लिए डेटाबेस स्कीमा

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

-- SQL schema
CREATE TABLE IF NOT EXISTS player_skills (
    citizenid VARCHAR(50) NOT NULL,
    skill_name VARCHAR(50) NOT NULL,
    xp INT DEFAULT 0,
    level INT DEFAULT 1,
    prestige INT DEFAULT 0,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (citizenid, skill_name)
);

CREATE TABLE IF NOT EXISTS player_reputation (
    citizenid VARCHAR(50) NOT NULL,
    faction VARCHAR(50) NOT NULL,
    reputation INT DEFAULT 0,
    tier VARCHAR(20) DEFAULT 'neutral',
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (citizenid, faction)
);

XP गणना और स्तर वक्र

एक सपाट XP वक्र जहां हर स्तर के लिए समान अनुभव की आवश्यकता होती है, अप्रसन्न महसूस होता है क्योंकि शुरुआती स्तर बहुत तेजी से गुजरते हैं जबकि बाद के स्तर समान लगते हैं। सबसे अच्छा तरीका एक बहुपद वक्र का उपयोग करना है जहां प्रत्येक स्तर के लिए क्रमिक रूप से अधिक XP की आवश्यकता होती है, लेकिन वृद्धि इतनी तीव्र नहीं होती कि अधिकतम स्तर अप्राप्य हो जाए। एक सामान्य सूत्र है requiredXP = baseXP * (level ^ exponent) जहाँ 100 का बेस और 1.5 का एक्सपोनेंट एक स्मूद कर्व बनाता है। स्तर 1 के लिए 100 XP आवश्यक है, स्तर 10 के लिए लगभग 3,162 XP, और स्तर 50 के लिए लगभग 35,355 XP। यह शुरुआती प्रगति को तेज़ रखता है जबकि उच्च स्तरों को वास्तविक उपलब्धियों की तरह महसूस कराता है जिन्हें समर्पित खेल के साथ हासिल किया जाता है।

-- shared/skills_config.lua
SkillsConfig = {}

SkillsConfig.BaseXP = 100
SkillsConfig.Exponent = 1.5
SkillsConfig.MaxLevel = 50

function SkillsConfig.GetRequiredXP(level)
    return math.floor(SkillsConfig.BaseXP * (level ^ SkillsConfig.Exponent))
end

function SkillsConfig.GetTotalXPForLevel(targetLevel)
    local total = 0
    for i = 1, targetLevel - 1 do
        total = total + SkillsConfig.GetRequiredXP(i)
    end
    return total
end

-- Skill definitions with XP sources
SkillsConfig.Skills = {
    driving = {
        label = 'Driving',
        icon = 'fa-car',
        xpSources = {
            { action = 'distance_driven', xpPer = 1, unit = 'per 500m' },
            { action = 'race_won', xpPer = 150, unit = 'per win' },
            { action = 'no_crash_streak', xpPer = 50, unit = 'per 5min' },
        }
    },
    shooting = {
        label = 'Marksmanship',
        icon = 'fa-crosshairs',
        xpSources = {
            { action = 'headshot', xpPer = 25, unit = 'per kill' },
            { action = 'bodyshot', xpPer = 10, unit = 'per kill' },
            { action = 'range_practice', xpPer = 5, unit = 'per target' },
        }
    },
    mechanic = {
        label = 'Mechanic',
        icon = 'fa-wrench',
        xpSources = {
            { action = 'vehicle_repaired', xpPer = 30, unit = 'per repair' },
            { action = 'engine_swap', xpPer = 100, unit = 'per swap' },
            { action = 'custom_tune', xpPer = 75, unit = 'per tune' },
        }
    },
    cooking = {
        label = 'Cooking',
        icon = 'fa-utensils',
        xpSources = {
            { action = 'meal_cooked', xpPer = 20, unit = 'per meal' },
            { action = 'recipe_discovered', xpPer = 50, unit = 'per recipe' },
        }
    },
    stamina = {
        label = 'Stamina',
        icon = 'fa-running',
        xpSources = {
            { action = 'distance_sprinted', xpPer = 1, unit = 'per 200m' },
            { action = 'swimming', xpPer = 2, unit = 'per 100m' },
        }
    },
}

सर्वर-साइड XP प्रबंधन

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

-- server/skills_manager.lua
local playerCache = {}
local SAVE_INTERVAL = 60000 -- flush to DB every 60 seconds

-- Load player skills from DB on join
AddEventHandler('playerJoining', function()
    local src = source
    local citizenid = GetCitizenId(src)
    if not citizenid then return end

    local skills = MySQL.query.await('SELECT * FROM player_skills WHERE citizenid = ?', {citizenid})
    playerCache[citizenid] = {}
    for _, row in ipairs(skills or {}) do
        playerCache[citizenid][row.skill_name] = {
            xp = row.xp,
            level = row.level,
            prestige = row.prestige,
            dirty = false
        }
    end
end)

function AddSkillXP(citizenid, skillName, amount)
    if not SkillsConfig.Skills[skillName] then return end
    if not playerCache[citizenid] then return end

    local skill = playerCache[citizenid][skillName]
    if not skill then
        skill = { xp = 0, level = 1, prestige = 0, dirty = false }
        playerCache[citizenid][skillName] = skill
    end

    -- Apply prestige multiplier (each prestige = +5% XP)
    local multiplier = 1.0 + (skill.prestige * 0.05)
    local finalXP = math.floor(amount * multiplier)

    skill.xp = skill.xp + finalXP
    skill.dirty = true

    -- Check for level up
    local required = SkillsConfig.GetRequiredXP(skill.level)
    while skill.xp >= required and skill.level < SkillsConfig.MaxLevel do
        skill.xp = skill.xp - required
        skill.level = skill.level + 1
        required = SkillsConfig.GetRequiredXP(skill.level)

        -- Notify player of level up
        local src = GetPlayerByCitizenId(citizenid)
        if src then
            TriggerClientEvent('skills:levelUp', src, skillName, skill.level)
        end

        -- Immediate save on level up
        SaveSkill(citizenid, skillName, skill)
    end

    return skill
end

exports('AddSkillXP', AddSkillXP)

Skill Trees और अनलॉक करने योग्य क्षमताएं

Skill trees कच्चे स्तर बढ़ाने के ऊपर एक रणनीतिक परत जोड़ते हैं। प्रत्येक स्तर 20 के मैकेनिक स्तर पर हर खिलाड़ी समान होने के बजाय, skill trees उन्हें विशेषज्ञता प्रदान करते हैं। एक मैकेनिक इंजन प्रदर्शन में अंक निवेश कर सकता है, टर्बो किट जोड़ने की क्षमता अनलॉक करता है, जबकि दूसरा बॉडीवर्क में विशेषज्ञता प्राप्त करता है, विशेष पेंट जॉब्स और लिवरियों तक पहुंच प्राप्त करता है। प्रत्येक स्तर एक skill पॉइंट प्रदान करता है जिसे शाखित पेड़ में खर्च किया जा सकता है। पेड़ संरचना सरल माता-पिता-बच्चा संबंध का उपयोग करती है जहां बच्चे नोड को अनलॉक करने के लिए माता-पिता का अनलॉक होना और न्यूनतम skill स्तर आवश्यक होता है। यह खिलाड़ियों को सबसे शक्तिशाली क्षमताओं की ओर जल्दी बढ़ने से रोकता है बिना मूलभूत दक्षता बनाए।

-- shared/skill_trees.lua
SkillTrees = {
    mechanic = {
        { id = 'basic_repair', label = 'Basic Repair', reqLevel = 1, parent = nil,
          effect = { type = 'speed', value = 1.0 }, desc = 'Standard repair speed' },
        { id = 'fast_repair', label = 'Quick Hands', reqLevel = 5, parent = 'basic_repair',
          effect = { type = 'speed', value = 1.3 }, desc = '30% faster repairs' },
        { id = 'engine_diag', label = 'Engine Diagnostics', reqLevel = 10, parent = 'basic_repair',
          effect = { type = 'unlock', value = 'engine_scan' }, desc = 'Scan engine health remotely' },
        { id = 'turbo_kit', label = 'Turbo Installation', reqLevel = 20, parent = 'engine_diag',
          effect = { type = 'unlock', value = 'install_turbo' }, desc = 'Install turbo kits on vehicles' },
        { id = 'master_tune', label = 'Master Tuner', reqLevel = 35, parent = 'turbo_kit',
          effect = { type = 'unlock', value = 'advanced_tune' }, desc = 'Access to advanced ECU tuning' },
        { id = 'body_expert', label = 'Body Expert', reqLevel = 10, parent = 'basic_repair',
          effect = { type = 'unlock', value = 'custom_paint' }, desc = 'Unlock exclusive paint options' },
        { id = 'livery_master', label = 'Livery Master', reqLevel = 25, parent = 'body_expert',
          effect = { type = 'unlock', value = 'custom_livery' }, desc = 'Create and apply custom liveries' },
    },
}

-- server: unlock a skill tree node
function UnlockNode(citizenid, skillName, nodeId)
    local tree = SkillTrees[skillName]
    if not tree then return false, 'No tree for skill' end

    local node = nil
    for _, n in ipairs(tree) do
        if n.id == nodeId then node = n break end
    end
    if not node then return false, 'Node not found' end

    local skill = GetPlayerSkill(citizenid, skillName)
    if not skill or skill.level < node.reqLevel then
        return false, 'Level too low'
    end

    if node.parent then
        local parentUnlocked = IsNodeUnlocked(citizenid, skillName, node.parent)
        if not parentUnlocked then return false, 'Parent node locked' end
    end

    MySQL.insert('INSERT INTO player_skill_nodes (citizenid, skill_name, node_id) VALUES (?, ?, ?)',
        {citizenid, skillName, nodeId})

    return true
end

प्रतिष्ठा स्तर और फेक्शन स्थिति

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

-- shared/reputation_config.lua
ReputationConfig = {
    tiers = {
        { name = 'hostile',   minRep = -1000, color = '#ef4444' },
        { name = 'unfriendly', minRep = -500, color = '#f97316' },
        { name = 'neutral',   minRep = 0,     color = '#94a3b8' },
        { name = 'friendly',  minRep = 500,   color = '#22c55e' },
        { name = 'honored',   minRep = 1500,  color = '#3b82f6' },
        { name = 'revered',   minRep = 3000,  color = '#a855f7' },
    },
    factions = {
        police   = { label = 'LSPD', opposing = {'cartel', 'gang_ballas'} },
        ems      = { label = 'EMS',  opposing = {} },
        cartel   = { label = 'Madrazo Cartel', opposing = {'police'} },
        mechanic = { label = 'LS Customs', opposing = {} },
        gang_ballas = { label = 'Ballas', opposing = {'police', 'gang_families'} },
        gang_families = { label = 'Families', opposing = {'gang_ballas'} },
    },
    opposingPenalty = 0.5, -- lose 50% of gained rep from opposing factions
}

-- server: modify reputation with faction cascading
function ModifyReputation(citizenid, faction, amount)
    local config = ReputationConfig.factions[faction]
    if not config then return end

    -- Apply to primary faction
    AdjustRep(citizenid, faction, amount)

    -- Penalize opposing factions
    if amount > 0 then
        for _, opposing in ipairs(config.opposing) do
            local penalty = math.floor(amount * ReputationConfig.opposingPenalty)
            AdjustRep(citizenid, opposing, -penalty)
        end
    end
end

The Prestige System

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

-- server: prestige a maxed skill
function PrestigeSkill(citizenid, skillName)
    local skill = playerCache[citizenid] and playerCache[citizenid][skillName]
    if not skill then return false, 'Skill not found' end
    if skill.level < SkillsConfig.MaxLevel then return false, 'Not max level' end

    -- Reset level and XP, increment prestige
    skill.level = 1
    skill.xp = 0
    skill.prestige = skill.prestige + 1
    skill.dirty = true

    -- Save immediately
    SaveSkill(citizenid, skillName, skill)

    -- Grant prestige reward
    local src = GetPlayerByCitizenId(citizenid)
    if src then
        TriggerClientEvent('skills:prestige', src, skillName, skill.prestige)
        -- Unlock prestige-specific items
        if skill.prestige == 1 then
            exports.ox_inventory:AddItem(src, 'prestige_badge_'..skillName, 1)
        elseif skill.prestige == 5 then
            exports.ox_inventory:AddItem(src, 'gold_tool_'..skillName, 1)
        end
    end

    return true, skill.prestige
end

RegisterNetEvent('skills:requestPrestige', function(skillName)
    local src = source
    local citizenid = GetCitizenId(src)
    local success, result = PrestigeSkill(citizenid, skillName)
    if success then
        lib.notify(src, { title = 'Prestige!', description = ('Prestige %d achieved for %s'):format(result, skillName), type = 'success' })
    else
        lib.notify(src, { title = 'Cannot Prestige', description = result, type = 'error' })
    end
end)

क्लाइंट-साइड UI और पैसिव XP ट्रैकिंग

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

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

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