Node Effects
Create custom effects when skill tree nodes are unlocked
Node Effects
Learn how to create custom effects that trigger when players unlock skill tree nodes.
Overview
Node effects are callbacks that execute when a player unlocks a specific node. They can:
- Grant stat bonuses
- Unlock abilities
- Trigger events
- Modify gameplay mechanics
- Award items or currency
Registering Effects
Effects are registered using the registerNodeEffect export from Dime Skills:
exports.dime_skills:registerNodeEffect(nodeId, callback)Parameters:
nodeId(string): Must match the node ID in your tree configcallback(function): Called with(source, nodeData)
Basic Example
-- In your resource's server.lua
CreateThread(function()
-- Wait for dime_skills to load
while GetResourceState('dime_skills') ~= 'started' do
Wait(100)
end
-- Register effect for 'combat_damage' node
exports.dime_skills:registerNodeEffect('combat_damage', function(src, nodeData)
-- nodeData contains: value, data, and config from node definition
local damageBoost = nodeData.value or 0.10
-- Store in player state for other resources to read
Player(src).state:set('meleeDamageBoost', damageBoost, true)
-- Notify player
TriggerClientEvent('ox_lib:notify', src, {
type = 'success',
title = 'Node Unlocked!',
description = ('+%d%% Melee Damage'):format(damageBoost * 100)
})
end)
end)Effect Types
Stat Bonus Effects
Modify player stats when a node is unlocked:
-- Speed boost node
exports.dime_skills:registerNodeEffect('survival_speed', function(src, nodeData)
local boost = nodeData.value or 1.1 -- 10% speed boost
-- Use statebags for persistent state
Player(src).state:set('runSpeedMultiplier', boost, true)
-- Apply on client
TriggerClientEvent('myresource:applySpeedBoost', src, boost)
end)
-- Client-side handler
RegisterNetEvent('myresource:applySpeedBoost', function(multiplier)
SetRunSprintMultiplierForPlayer(PlayerId(), multiplier)
end)Ability Unlock Effects
Enable abilities when nodes are unlocked:
-- Double jump ability
exports.dime_skills:registerNodeEffect('movement_double_jump', function(src, nodeData)
Player(src).state:set('canDoubleJump', true, true)
TriggerClientEvent('abilities:unlock', src, 'double_jump', {
height = nodeData.data?.height or 2.0
})
end)
-- Execute ability (finishing move)
exports.dime_skills:registerNodeEffect('combat_execute', function(src, nodeData)
Player(src).state:set('canExecute', true, true)
TriggerClientEvent('combat:unlockExecute', src, {
damageThreshold = nodeData.data?.threshold or 0.2
})
end)Passive Effects
Grant passive bonuses:
-- Health regeneration
exports.dime_skills:registerNodeEffect('survival_regen', function(src, nodeData)
local regenRate = nodeData.value or 1 -- HP per second
Player(src).state:set('healthRegen', regenRate, true)
TriggerClientEvent('survival:startRegen', src, regenRate)
end)
-- Armor bonus
exports.dime_skills:registerNodeEffect('combat_armor', function(src, nodeData)
local armorBonus = nodeData.value or 25
-- Increase max armor
TriggerClientEvent('myresource:setMaxArmor', src, 100 + armorBonus)
end)Crafting/Job Effects
Unlock crafting recipes or job perks:
-- Advanced crafting recipes
exports.dime_skills:registerNodeEffect('crafting_advanced', function(src, nodeData)
-- Enable advanced crafting for this player
Player(src).state:set('advancedCrafting', true, true)
-- Could also grant access to crafting bench
TriggerEvent('crafting:grantAccess', src, 'advanced_bench')
end)
-- Job-specific perk
exports.dime_skills:registerNodeEffect('mechanic_efficiency', function(src, nodeData)
local efficiency = nodeData.value or 1.5 -- 50% faster repairs
Player(src).state:set('repairSpeed', efficiency, true)
end)Listening to Unlock Events
Instead of registering callbacks, you can listen to the global unlock event:
AddEventHandler('dime_skills:nodeUnlocked', function(src, nodeId, nodeData)
print(('Player %d unlocked: %s'):format(src, nodeId))
-- Handle multiple nodes in one listener
if nodeId == 'combat_damage_1' then
-- Apply damage boost
elseif nodeId == 'combat_damage_2' then
-- Apply bigger damage boost
elseif nodeId:match('^crafting_') then
-- Handle all crafting nodes
TriggerEvent('crafting:nodeUnlocked', src, nodeId)
end
end)RegisterNetEvent('dime_skills:nodeUnlocked', function(nodeId, nodeData)
-- Play unlock effects
PlaySoundFrontend(-1, 'MEDAL_UP', 'HUD_MINI_GAME_SOUNDSET', true)
-- Show unlock animation
TriggerEvent('myui:showUnlockAnimation', nodeId)
-- Apply client-side effects immediately
if nodeId == 'survival_speed' then
local boost = nodeData.value or 1.1
SetRunSprintMultiplierForPlayer(PlayerId(), boost)
end
end)Persisting Effects
Node effects should persist across sessions. Use statebags or database storage:
Using Statebags
-- On node unlock
exports.dime_skills:registerNodeEffect('combat_damage', function(src, nodeData)
Player(src).state:set('meleeDamageBoost', nodeData.value, true)
end)
-- On player load (re-apply effects for unlocked nodes)
AddEventHandler('playerJoined', function()
local src = source
local unlockedNodes = exports.dime_skilltree:getUnlockedNodes(src)
for _, nodeId in ipairs(unlockedNodes) do
local nodeInfo = exports.dime_skilltree:getNodeInfo(nodeId)
if nodeInfo and nodeInfo.effect.type == 'stat_bonus' then
-- Re-apply stat bonus
applyNodeEffect(src, nodeId, nodeInfo.effect)
end
end
end)Checking Unlocked Nodes
-- Before applying effect, check if player has node
local function getPlayerDamageBoost(src)
local baseBoost = 1.0
if exports.dime_skilltree:hasNode(src, 'combat_damage_1') then
baseBoost = baseBoost + 0.05
end
if exports.dime_skilltree:hasNode(src, 'combat_damage_2') then
baseBoost = baseBoost + 0.10
end
if exports.dime_skilltree:hasNode(src, 'combat_master') then
baseBoost = baseBoost + 0.15
end
return baseBoost
endComplete Example
Here's a complete example of a combat effects system:
-- combat_effects.lua (server)
local PlayerEffects = {}
-- Helper to update player effects
local function UpdatePlayerEffects(src)
local effects = PlayerEffects[src] or {
meleeDamage = 1.0,
weaponDamage = 1.0,
accuracy = 1.0,
armor = 100,
}
-- Check each combat node
if exports.dime_skilltree:hasNode(src, 'combat_melee_1') then
effects.meleeDamage = effects.meleeDamage + 0.05
end
if exports.dime_skilltree:hasNode(src, 'combat_melee_2') then
effects.meleeDamage = effects.meleeDamage + 0.10
end
if exports.dime_skilltree:hasNode(src, 'combat_ranged_1') then
effects.weaponDamage = effects.weaponDamage + 0.05
effects.accuracy = effects.accuracy + 0.05
end
if exports.dime_skilltree:hasNode(src, 'combat_armor') then
effects.armor = effects.armor + 25
end
if exports.dime_skilltree:hasNode(src, 'combat_master') then
effects.meleeDamage = effects.meleeDamage + 0.05
effects.weaponDamage = effects.weaponDamage + 0.05
end
PlayerEffects[src] = effects
-- Sync to client
TriggerClientEvent('combat:syncEffects', src, effects)
return effects
end
-- Register effects for all combat nodes
local combatNodes = {
'combat_melee_1', 'combat_melee_2',
'combat_ranged_1', 'combat_ranged_2',
'combat_armor', 'combat_master'
}
CreateThread(function()
while GetResourceState('dime_skills') ~= 'started' do Wait(100) end
for _, nodeId in ipairs(combatNodes) do
exports.dime_skills:registerNodeEffect(nodeId, function(src, nodeData)
UpdatePlayerEffects(src)
TriggerClientEvent('ox_lib:notify', src, {
type = 'success',
title = 'Combat Skill Unlocked',
description = 'Your combat abilities have improved!'
})
end)
end
end)
-- Re-apply on player load
RegisterNetEvent('QBCore:Server:OnPlayerLoaded', function()
UpdatePlayerEffects(source)
end)
-- Export for other resources
exports('getPlayerCombatEffects', function(src)
return PlayerEffects[src] or UpdatePlayerEffects(src)
end)
-- Cleanup on disconnect
AddEventHandler('playerDropped', function()
PlayerEffects[source] = nil
end)Best Practices
- Register effects early - Use
CreateThreadon resource start - Handle reconnects - Re-apply effects when players rejoin
- Use statebags - For effects other resources need to read
- Validate server-side - Never trust client for effect calculations
- Clean up on disconnect - Remove player data when they leave
- Stack effects carefully - Consider how multiple nodes combine
Related
- Configuration - Node configuration
- Exports - Available exports