skills-and-advancement
Handles skill tree structures, attribute calculations, and advancement systems in the Oxidus LPC codebase.
Install
mkdir -p .claude/skills/skills-and-advancement && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/18133" && unzip -o skill.zip -d .claude/skills/skills-and-advancement && rm skill.zipInstalls to .claude/skills/skills-and-advancement
Activation
This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.
Understand and work with the skill tree and advancement systems in Oxidus. Covers the nested skill tree, dot-path addressing, use-based improvement, the query_skill / query_raw_skill / query_skill_level / query_raw_skill_level API grid, has_skill existence checks, boon integration, XP, TNL formula, leveling, attributes, and how skills interact with combat and NPCs.Key capabilities
- →Navigate a nested skill tree using dot notation
- →Query raw skill levels without boons
- →Query effective skill levels with boon modifiers
- →Check for skill existence using has_skill()
- →Improve skills transparently through use-based progression
How it works
The skill system uses a nested tree structure for skills, allowing dot-path addressing. Players improve skills through use, with a chance to increase their level, and boons can modify effective skill levels.
Inputs & outputs
When to use skills-and-advancement
- →Querying skill levels
- →Implementing new advancement logic
- →Debugging skill tree attributes
About this skill
Skills and Advancement Skill
You are helping work with the Oxidus skill and advancement systems. Follow the lpc-coding-style skill for all LPC formatting.
Architecture Overview
skills.lpc (std/living/skills.lpc) — nested skill tree, use-based improvement
advancement.lpc (std/living/advancement.lpc) — per-living XP/level state
advance.lpc (adm/daemons/advance.lpc) — TNL formula, kill_xp, earn_xp
attributes.lpc (std/living/attributes.lpc) — STR/DEX/CON/INT/WIS/CHA
boon.lpc (std/living/boon.lpc) — buff/debuff modifiers on skills and vitals
All of these are inherited by STD_BODY and apply to both players and NPCs.
Skill System — std/living/skills.lpc
Storage Structure
Skills are a nested tree, not a flat mapping:
skills = ([
"combat": ([
"level": 3.47,
"subskills": ([
"melee": ([
"level": 2.15,
"subskills": ([
"slashing": ([ "level": 4.82, "subskills": ([]) ]),
"piercing": ([ "level": 1.03, "subskills": ([]) ]),
"bludgeoning": ([ "level": 2.60, "subskills": ([]) ]),
"unarmed": ([ "level": 1.55, "subskills": ([]) ]),
]),
]),
"defence": ([
"level": 1.90,
"subskills": ([
"dodge": ([ "level": 3.21, "subskills": ([]) ]),
"parry": ([ "level": 1.10, "subskills": ([]) ]),
]),
]),
]),
]),
])
Dot notation addresses nodes: "combat.melee.slashing" navigates the tree.
The integer part of the level is the effective skill level. The fractional part is progress toward the next level (0-99%).
A private find_skill_node(string skill) helper walks the dot-path and returns the live node mapping (or 0). Every read/leaf-mutate function delegates to it — add_skill and remove_skill keep their own walks because they need creation / parent-ref semantics.
Default Skill Tree (from config)
The tree lives under SKILLS.learnable in adm/etc/default.lpml:
combat
defence: dodge, parry
melee: attack, bludgeoning, piercing, slashing, unarmed
social: barter, charm, intimidate, persuade
general: appraise, hide, jump, listen, search, spot, swim
Full dot-path examples: "combat.melee.slashing", "combat.defence.dodge", "social.barter", "general.swim".
"combat.defence.evade" is used by combat but is not in the learnable tree — it is created on first use by assure_skill().
Improvement Tuning Knobs (config)
The rest of the SKILLS block holds the numbers use_skill reads on every call:
| Key | Description |
|---|---|
SKILLS.improve_chance.floor | The percent chance a use_skill roll decays toward, and never drops below, when the caller passes no improvement_chance |
SKILLS.improve_chance.ceiling | The bonus above the floor at skill 0, and the hyperbolic scale it erodes on. The chance starts at floor + ceiling and halves that bonus once the raw skill reaches ceiling |
SKILLS.default_gain | Progress bound used when the caller passes no improvement |
SKILLS.cap_factor | Multiplied by the living's level to get the per-node skill cap |
COMBAT.NPC_SKILL_MULTIPLIER is the matching knob for NPC skill levels.
Read the current values from adm/etc/default.lpml, and never restate them in code, comments, or a call site. They are tuning knobs and they move; a literal copied out of that file is wrong the next time it is turned. Everything below describes the shape of the maths, not the numbers going into it.
Key Functions
| Function | Signature | Description |
|---|---|---|
add_skill | int (string skill, float level) | Creates skill at dot-path. Intermediates created at level 1.0. Does not overwrite existing nodes. Returns 1 on success |
remove_skill | int (string skill) | Removes leaf node |
has_skill | int (string skill) | Returns 1 if the node exists, 0 otherwise. Use this for existence checks instead of nullp(query_raw_skill(...)) |
query_raw_skill | float (string skill) | Raw float level — no flooring, no boon |
query_skill | float (string skill) | Raw float level + boon modifier |
query_raw_skill_level | float (string skill) | floor(level) — no boon |
query_skill_level | float (string skill) | floor(level) + query_effective_boon("skill", skill). The function combat math uses |
set_skill_level | int (string skill, float level) | Sets exact float level. Requires intermediates to already exist; will not create them |
query_skills | mapping () | Returns a copy of the entire tree |
set_skills | void (mapping s) | Replaces the tree wholesale (no-op if s is not a mapping) |
use_skill | int (string skill, mixed improvement, mixed improvement_chance) | Players only. Rolls for improvement, chooses a node, clamps the gain, and applies it. Auto-creates the skill if missing. improvement overrides the SKILLS.default_gain progress bound; improvement_chance overrides the level-scaled roll chance. See improvement algorithm below |
improve_skill | float (string skill_name, mixed potential_progress) | Players only. Applies progress to one node — no path walk, no bubble-up, no cap. Defaults to SKILLS.default_gain. use_skill is the entry point; call this directly only when you deliberately want to bypass the cap and the weighted pick |
determine_skill_to_improve | private string (string skill_name, float skill_cap) | Builds the node-and-ancestors candidate list, drops any node at or over skill_cap, weighted-draws one survivor. undefined if all are capped |
clamp_improvement | private float (string skill_name, float improvement) | Trims a proposed gain to the distance remaining to that node's cap; 0.0 if already at or over |
query_skill_progress | int (string skill) | Fractional part of the level as a 0-99 integer |
modify_skill_level | int (string skill, int level) | Replace level with an int. Like set_skill_level but accepts int and doesn't enforce a minimum |
assure_skill | int (string skill) | Creates at level 1.0 if missing, tells the player they gained a new skill |
wipe_skills | void () | Resets to empty mapping |
initialize_missing_skills | void (mapping, string) | Creates any missing skills from a config-shaped tree |
adjust_skills_by_npc_level | int (float level) | NPC-only: seeds every skill in the tree to level * COMBAT.NPC_SKILL_MULTIPLIER. Errors if called on a user |
Use-Based Improvement
Players improve skills transparently by using them — no skill points or manual allocation.
use_skill() is the only entry point, and it is player-only — the first thing it does after validating its argument is pcp(), and a non-player returns 0 without touching the tree. An unknown skill is created and then rolled in the same call:
varargs int use_skill(string skill, mixed improvement, mixed improvement_chance) {
// not a player -> return 0, nothing happens
// unknown skill -> assure_skill() at 1.0, then carry on
// then -> pick a node, roll against THAT node, clamp, improve
}
The node is drawn before the roll, and the chance is read from the drawn node's level, not the named skill's. That is what lets an under-trained ancestor climb at its own rate instead of being throttled by the child that was actually used.
The chance is not flat, and it falls as the skill rises. Absent an improvement_chance argument it is improve_chance.floor + (improve_chance.ceiling - dim_hyperbolic(raw, improve_chance.ceiling)), where raw is query_raw_skill() of the drawn node and dim_hyperbolic(v, s) == (s * v) / (s + v).
The hyperbolic term is the portion of the ceiling bonus that skill has already worn away, so the chance starts at floor + ceiling, has shed half the bonus by the time the raw skill reaches ceiling, and approaches the floor asymptotically without ever reaching it. A novice trains quickly, a veteran slowly, and no skill ever stops improving outright — the cap does that.
The erosion is deliberately gentle: it is hyperbolic rather than exponential, so the rate decays over the whole skill range instead of collapsing in the first few levels. Roughly, with the current config:
| Raw skill | Chance |
|---|---|
| 1 | ~24% |
| 20 | ~15% |
| 60 | ~10% |
| 150 | ~7% |
Read SKILLS.improve_chance for the live numbers; the shape is what matters here. If a future tuning pass wants a sharper falloff, swap dim_hyperbolic for dim_exponential_decay on the same subtraction — that is the knob, not the config values.
use_skill() is called throughout the codebase:
- Combat: attacker trains weapon skill after each swing, defender trains defence skill on every hit attempt.
- Any system can call
use_skill("general.swim")etc. to trigger organic improvement.
improvement replaces the SKILLS.default_gain bound for this call. It is a bound, not an award — improve_skill applies random_float() of it.
improvement_chance replaces the whole computed chance for this call, as a flat percentage rolled against random_float(100.0). Pass it when a call site needs a rate that does not track the skill's own level — a guaranteed tick at 100.0, or a deliberately rare one. Omit it everywhere else, because the level-scaled default is what keeps advancement curves consistent across the lib.
Omit it unless the call site genuinely wants to advance at a different rate from everything else, and if you do pass one, read SKILLS.default_gain before choosing the literal — the argument is an absolute bound, not a multiplier, so whether a given number speeds a call site up or slows it down depends entirely on where the default currently sits. Existing spell and ability sites pass literals (`victim-
Content truncated.
When not to use it
- →When working with flat skill systems
- →When skill points or manual allocation are required for progression
- →When attributes are directly modifying skill checks
Limitations
- →Skills are a nested tree, not a flat mapping
- →Attributes are currently independent of skills
- →Progress is tiny by default, requiring mod_adjust for low-frequency callers
How it compares
This skill system features a nested, use-based progression model where skills improve transparently through actions, differing from systems that require manual skill point allocation or flat skill lists.
Compared to similar skills
skills-and-advancement side by side with the closest alternatives in the catalog.
| Skill | Installs | Updated | Safety | Difficulty |
|---|---|---|---|---|
| skills-and-advancement (this skill) | 0 | 3mo | No flags | Advanced |
| azure-eventhub-py | 1 | 5mo | Review | Intermediate |
| benchling-integration | 1 | 10mo | No flags | Advanced |
| latchbio-integration | 1 | 10mo | Review | Advanced |
Try saying
Example prompts that trigger this skill in your AI assistant.
You might also like
azure-eventhub-py
microsoft
Azure Event Hubs SDK for Python streaming. Use for high-throughput event ingestion, producers, consumers, and checkpointing. Triggers: "event hubs", "EventHubProducerClient", "EventHubConsumerClient", "streaming", "partitions".
benchling-integration
davila7
Benchling R&D platform integration. Access registry (DNA, proteins), inventory, ELN entries, workflows via API, build Benchling Apps, query Data Warehouse, for lab data management automation.
latchbio-integration
davila7
Latch platform for bioinformatics workflows. Build pipelines with Latch SDK, @workflow/@task decorators, deploy serverless workflows, LatchFile/LatchDir, Nextflow/Snakemake integration.
r-code
dslc-io
Guide for writing R code. Use when writing new functions, designing APIs, or reviewing/modifying existing R code.
jq
diegosouzapw
jq \u2014 JSON Querying and Transformation workflow skill. Use this skill when the user needs Expert jq usage for JSON querying, filtering, transformation, and pipeline integration. Practical patterns for real shell workflows and the operator should preserve the upstream workflow, copied support fil
remote-compute
kbaseincubator
Run arbitrary scripts on KBase compute nodes via the CDM Task Service (CTS). Use when the user needs to move compute off their notebook or local machine — e.g., running bioinformatics tools, heavy data processing, or anything that benefits from dedicated CPU/memory on a remote node.