Two optional systems that change how rewards work. With en-skills, players get better at what they do: skill XP comes from actions, and bonuses make those actions faster or stronger. With en-party, survivors team up and share XP with members nearby.
Both interfaces are written so your code never has to check whether the resource exists. Without en-skills, everyone is level 1 with no bonuses. Without en-party, everyone is a party of one.
Skills
API
| Side | Function | Returns | Without en-skills |
|---|---|---|---|
| both | encore.skills.isAvailable() | boolean | false |
| server | encore.skills.addXP(source, skill, amount) | boolean | false (nothing recorded) |
| server | encore.skills.getLevel(source, skill) | number | 1 |
| server | encore.skills.getBonus(source, key) | number | 0 |
| server | encore.skills.hasPerk(source, perk) | boolean | false |
| client | encore.skills.getLevel(skill) | number | 1 |
| client | encore.skills.getBonus(key) | number | 0 |
| client | encore.skills.hasPerk(perk) | boolean | false |
- A missing en-skills is silent. An en-skills export that errors returns the fallback and logs a warning once.
- Levels and bonuses go through
tonumber, so a bad value becomes the fallback. addXPandhasPerkreturntrueonly if en-skills returns exactlytrue.
Bonuses
A bonus is a fraction: 0.12 means 12%. These are the bonus keys in use:
stamina, meleeDamage, weaponDamage, accuracy, carryWeight (in kilograms, not a fraction), searchSpeed, extraLoot, healing, lockpick, hotwire, craftSpeed, craftRefund, repair, infectionResist, foodRisk.
Pass lockpick and hotwire bonuses to minigames as bonus.
Security
- Only the server can award XP. There is no client
addXP. - Client-side bonuses are good for feel: shorter progress bars, easier minigames. For anything that changes items, money or damage, read the bonus again on the server.
Examples
-- server: after a successful heal
encore.skills.addXP(source, 'medicine', 5)
local heal = 25 * (1 + encore.skills.getBonus(source, 'healing'))-- server: crafting refund and a perk
local refund = encore.skills.getBonus(source, 'craftRefund')
if math.random() < refund then
encore.inventory.addItem(source, 'scrap', 1)
end
if encore.skills.hasPerk(source, 'quick_hands') then
-- ...
end-- client: a faster search for skilled players
local speed = encore.skills.getBonus('searchSpeed')
local done = encore.progress({
label = 'Searching',
duration = math.floor(5000 * (1 - encore.math.clamp(speed, 0, 0.8))),
disable = { move = true },
})Contract
A resource named en-skills must export:
| Side | Export | Returns |
|---|---|---|
| server | AddSkillXP(source, skill, amount) | boolean |
| server | GetSkillLevel(source, skill) | number |
| server | GetBonus(source, key) | number |
| server | HasPerk(source, perk) | boolean |
| client | GetSkillLevel(skill) | number |
| client | GetBonus(key) | number |
| client | HasPerk(perk) | boolean |
en-skills also exports GetSkills, OpenMenu and CloseMenu, but the library doesn't use them, so a replacement doesn't need them for the interface. See en-skills.
Party
API
| Side | Function | Returns | Without en-party |
|---|---|---|---|
| both | encore.party.isAvailable() | boolean | false |
| server | encore.party.getPartyId(source) | number? | nil |
| server | encore.party.getMembers(source) | number[], including source | { source } |
| server | encore.party.getNearbyMembers(source, radius?) | number[], source first | { source } |
| server | encore.party.inSameParty(a, b) | boolean | a == b |
| server | encore.party.shareXP(source, amount, reason?) | boolean | The full amount goes to source |
| client | encore.party.getMembers() | server ids, including yours | Your own server id |
| client | encore.party.isMember(serverId) | boolean | true only for yourself |
Behaviour:
- A missing en-party is silent, because parties are optional. An export that errors logs a warning once.
- If en-party returns an empty list, the library treats it like the fallback and returns a list with just the player.
inSameParty(a, b)is alwaystruewhena == b.getNearbyMembersuses en-party's configured share radius unless you passradiusin metres.shareXPasks en-party to split the XP with nearby members. If en-party isn't running, or its export errors, the whole amount goes to the player throughexports['en-core']:AddXP(source, amount, reason). See XP and levels.
Security
Reward on the server only. Client-side membership is for display, such as marking party members; it doesn't prove anything to the server.
Examples
-- server: zombie kill reward
encore.party.shareXP(killer, 25, 'Zombie kill')
for _, member in ipairs(encore.party.getNearbyMembers(killer)) do
encore.skills.addXP(member, 'survival', 2)
end-- server: only party members may open each other's stash
RegisterNetEvent('my-stash:openFor', function(ownerId)
local src = source
if type(ownerId) ~= 'number' then return end
if not encore.party.inSameParty(src, ownerId) then return end
-- ...
end)-- client: tag party members on nameplates
for _, entry in ipairs(encore.getNearbyPlayers(GetEntityCoords(PlayerPedId()), 30.0)) do
if encore.party.isMember(entry.serverId) then
encore.drawText3d(entry.coords + vec3(0.0, 0.0, 1.1), 'PARTY')
end
endContract
A resource named en-party must export:
| Side | Export | Returns |
|---|---|---|
| server | GetPartyId(source) | number? |
| server | GetMembers(source) | number[], including source |
| server | GetNearbyMembers(source, radius?) | number[], including source, first |
| server | InSameParty(a, b) | boolean |
| server | ShareXP(source, amount, reason?) | boolean |
| client | GetMembers() | number[] of server ids, including yours |
| client | IsMember(serverId) | boolean |