en-skills adds skills that improve as you use them:
| Skill | Trained by |
|---|---|
| Endurance | Sprinting and swimming |
| Strength | Melee hits |
| Shooting | Landing shots |
| Scavenging | Searching bodies |
| Medicine | Using medical items |
| Thievery | Lockpicking and hotwiring |
| Crafting | Crafting |
| Mechanics | Repairing vehicles |
Each skill level adds small bonuses, such as more stamina, more carry weight, faster searches or better lockpicking, which other resources read.
Your character's en-core level also earns perk points, which you spend in a perk tree (press K). A perk can require a skill level, a character level or other perks, and gives a bigger bonus. You can reset your perks for cash, on a cooldown.
At a glance
| Depends on | en-core, en-ui |
| Start after | Both of the above |
| Database tables | None. Data lives in character metadata (metadata.skills) |
| Config files | config/shared.lua |
| Key binds | en_skills_menu (K) |
| Library | encore.skills, safe to call even when en-skills isn't running |
How it works
Levels
Skills go from level 1 to Config.maxLevel. Reaching level 2 takes curve.base XP, and each later level takes curve.growth times the XP of the previous level. With the defaults, the total XP needed is:
| Level | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
|---|---|---|---|---|---|---|---|---|---|
| Total XP | 100 | 250 | 475 | 813 | 1319 | 2078 | 3217 | 4926 | 7489 |
To stop farming, the same skill can't gain XP for the same player more often than every Config.awardCooldown seconds. Hitting players never grants XP.
Bonuses
Each skill's bonuses are added once for every level above 1. The totals from skills and perks are summed and published to the player's skillBonuses state bag.
| Skill | Bonus per level |
|---|---|
endurance | stamina 0.06 |
strength | meleeDamage 0.03, carryWeight 1.0 kg |
shooting | weaponDamage 0.02, accuracy 0.04 |
scavenging | searchSpeed 0.04, extraLoot 0.015 |
medicine | healing 0.05 |
thievery | lockpick 0.05, hotwire 0.03 |
crafting | craftSpeed 0.04 |
mechanics | repair 0.05, hotwire 0.02 |
Who reads each bonus key:
| Key | Read by |
|---|---|
stamina, meleeDamage, weaponDamage, accuracy | en-skills itself (GTA stats and damage modifiers) |
carryWeight (kg) | en-inventory, through the carryBonus state bag |
searchSpeed, extraLoot, infectionResist | en-zombies |
healing, foodRisk | en-consumables |
lockpick, hotwire, repair | en-vehiclekeys, en-doorlock, en-garages |
craftSpeed, craftRefund | en-crafting |
The client applies GTA stats (MP0_STAMINA, MP0_LUNG_CAPACITY, MP0_STRENGTH, MP0_SHOOTING_ABILITY) and weapon and melee damage modifiers whenever bonuses change, and again every 30 s.
XP en-skills awards itself
| Activity | XP |
|---|---|
firearmHit | shooting +1 per hit on a non-player ped |
meleeHit | strength +2 per hit on a non-player ped |
enduranceMinute | endurance +4 per minute sprinting or swimming |
corpseSearched | scavenging +4 |
medicalUsed | medicine +6, for items with category medical |
Other XP comes from the resources that use the skill, such as crafting, lockpicking and repairs.
Perk points
Points are calculated, not stored:
points = Config.startingPoints + (characterLevel - 1) * Config.pointsPerLevel - pointsSpentIf a character loses XP and drops a level, they can briefly have spent more points than they have. See XP and levels.
Default perks: marathon, second_wind, pack_mule, brawler, steady_hands, marksman, quick_hands, keen_eye, field_medic, iron_gut (character level 8), thick_skin, light_fingers, hot_start, tinkerer, thrifty, grease_monkey.
Configuration
| Key | Default | What it does |
|---|---|---|
Config.keys.menu | 'K' | Skills and perks menu |
Config.maxLevel | 10 | Skill level cap |
Config.curve | { base = 100, growth = 1.5 } | XP curve |
Config.pointsPerLevel | 1 | Perk points per character level after the first |
Config.startingPoints | 1 | Starting perk points |
Config.respec | cost = 2500, moneyType = 'cash', cooldownHours = 24 | Perk reset price and cooldown |
Config.awardCooldown | 1.0 | Minimum seconds between XP awards for the same skill and player |
Config.skills | 8 skills | label, icon, description, bonuses = { [key] = perLevel } |
Config.order | list | Display order of skills |
Config.activity | see above | XP en-skills awards itself |
Config.perks | 16 perks | id, label, description, skill and level (optional), coreLevel (optional), requires = { perkIds }, cost, bonuses |
Adding a skill and a perk chain
Config.skills.survival = {
label = 'Survival', icon = 'temperature',
description = 'Hardier in the cold and on bad food. Improves by surviving.',
bonuses = { foodRisk = 0.03, infectionResist = 0.01 },
}
Config.order[#Config.order + 1] = 'survival'
Config.perks[#Config.perks + 1] = { id = 'forager', label = 'Forager', skill = 'survival', level = 3, cost = 1,
description = 'Bad water rarely makes you sick.', bonuses = { foodRisk = 0.3 } }
Config.perks[#Config.perks + 1] = { id = 'deep_pockets', label = 'Deep Pockets', coreLevel = 10, cost = 2,
requires = { 'pack_mule', 'forager' }, description = 'Carry 8 kg more.', bonuses = { carryWeight = 8 } }A new skill only gains XP when something awards it. Call encore.skills.addXP(source, 'survival', amount) from your own resource.
Skills or perks you remove from config are dropped from characters when they load.
Library
Other resources should use encore.skills from the EndCore library rather than the exports. It returns safe defaults if en-skills isn't running. See Skills and party.
| Side | Functions |
|---|---|
| Server | addXP(source, skill, amount), getLevel(source, skill), getBonus(source, key), hasPerk(source, perk) |
| Client | getLevel(skill), getBonus(key), hasPerk(perk) |
| Both | isAvailable() |
Exports
Server
| Export | Arguments | Returns |
|---|---|---|
AddSkillXP | source, skill, amount | boolean. False for an unknown skill, an amount of 0 or less, a maxed skill, or within awardCooldown |
GetSkillLevel | source, skill | number (1 if unknown or not loaded) |
GetBonus | source, key | number (0 by default) |
HasPerk | source, id | boolean |
GetSkills | source | { xp, levels, perks, points } or nil |
Client
| Export | Returns |
|---|---|
GetSkillLevel(skill) | number |
GetBonus(key) | number, from LocalPlayer.state.skillBonuses |
HasPerk(id) | boolean |
GetSkills() | { xp, levels, perks } |
OpenMenu() / CloseMenu() | Opens or closes the skills page |
Commands
| Command | Restricted | What it does |
|---|---|---|
/skillxp <target> <skill> <amount> | group.admin | Grants skill XP, ignoring the cooldown |
/resetperks <target> | group.admin | Refunds all perks for free and clears the respec cooldown |
Events
Emitted, server-local:
| Event | Payload |
|---|---|
en-skills:server:levelUp | source, skill, newLevel, previousLevel |
en-skills:server:perkUnlocked | source, perkId |
en-skills:server:respec | source |
en-skills listens to weaponDamageEvent, en-zombies:server:corpseLooted, en-consumables:server:consumed, encore:server:onPlayerLoaded, encore:server:onPlayerUnload and encore:server:levelChanged.
State bags
| Key | Scope | Meaning |
|---|---|---|
skillBonuses | Player, replicated | Table of summed bonus keys |
carryBonus | Player, replicated | Extra carry weight in kg (the carryWeight bonus) |
Character metadata holds skills = { xp = { [skill] = number }, perks = { [perkId] = true }, respecAt = unixTime }. See Player data.
Examples
Award Survival XP from a campfire-cooking script and scale a result by a bonus:
-- server
AddEventHandler('mycooking:server:cooked', function(source)
encore.skills.addXP(source, 'survival', 5)
end)
local function searchTime(source, base)
local bonus = encore.skills.getBonus(source, 'searchSpeed')
return math.floor(base * (1 - math.min(0.5, bonus)))
endLock an action behind a perk:
if not encore.skills.hasPerk(source, 'field_medic') then
return false, 'You need the Field Medic perk.'
endCelebrate level-ups:
AddEventHandler('en-skills:server:levelUp', function(source, skill, newLevel)
if newLevel == 10 then
print(('%s mastered %s'):format(GetPlayerName(source), skill))
end
end)