SK

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.zip

Installs 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.
367 charsno explicit “when” triggerlonger than Claude Code's old 250-char listing cap (fine on current versions)
Advanced

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

You give it
Skill dot-path (e.g., 'combat.melee.slashing'), optional level for adding/setting skills, mixed mod_adjust for use_skill
You get back
Skill level (float or integer), existence status of a skill, updated skill progress

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:

KeyDescription
SKILLS.improve_chance.floorPercent chance floor for a use_skill roll
SKILLS.improve_chance.ceilingHyperbolic scale added to the floor; the chance rises toward floor + ceiling as the skill grows
SKILLS.default_gainProgress bound used when the caller passes no improvement
SKILLS.cap_factorMultiplied 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

FunctionSignatureDescription
add_skillint (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_skillint (string skill)Removes leaf node
has_skillint (string skill)Returns 1 if the node exists, 0 otherwise. Use this for existence checks instead of nullp(query_raw_skill(...))
query_raw_skillfloat (string skill)Raw float level — no flooring, no boon
query_skillfloat (string skill)Raw float level + boon modifier
query_raw_skill_levelfloat (string skill)floor(level) — no boon
query_skill_levelfloat (string skill)floor(level) + query_effective_boon("skill", skill). The function combat math uses
set_skill_levelint (string skill, float level)Sets exact float level. Requires intermediates to already exist; will not create them
query_skillsmapping ()Returns a copy of the entire tree
set_skillsvoid (mapping s)Replaces the tree wholesale (no-op if s is not a mapping)
use_skillint (string skill, mixed improvement)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. See improvement algorithm below
improve_skillfloat (string skill_name, mixed potential_progress)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_improveprivate 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_improvementprivate 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_progressint (string skill)Fractional part of the level as a 0-99 integer
modify_skill_levelint (string skill, int level)Replace level with an int. Like set_skill_level but accepts int and doesn't enforce a minimum
assure_skillint (string skill)Creates at level 1.0 if missing, tells the player they gained a new skill
wipe_skillsvoid ()Resets to empty mapping
initialize_missing_skillsvoid (mapping, string)Creates any missing skills from a config-shaped tree
adjust_skills_by_npc_levelint (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. An unknown skill is created instead of rolled, so the first use costs a call:

varargs int use_skill(string skill, mixed improvement) {
    // unknown skill -> assure_skill() at 1.0, no roll this call
    // known skill   -> roll, then pick a node, clamp, improve
}

The chance is not flat. It is improve_chance.floor + dim_hyperbolic(raw, improve_chance.ceiling), where dim_hyperbolic(v, s) == (s * v) / (s + v). That starts at the floor, reaches floor + ceiling/2 when the raw skill equals the ceiling, and approaches floor + ceiling asymptotically — higher skill means a more frequent roll, and the cap is what slows advancement down.

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.

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->use_skill("combat.defence.evade", 0.1);) that were chosen against an older default.

Improvement Algorithm

Selection and application are separate functions. use_skill() orchestrates:

  1. Roll. random_float(100.0) < improve_chance.floor + dim_hyperbolic(raw, improve_chance.ceiling). Fail -> return 0.
  2. Selectdetermine_skill_to_improve(skill, query_level() * cap_factor):
    • Candidates are the skill itself and every ancestor: "combat.melee.slashing" -> ({ "combat", "combat.melee", "combat.melee.slashing" }).
    • Any candidate whose query_raw_skill() is at or over the cap is dropped.
    • Survivors are weighted (segments + 1) * 3 and drawn with element_of_weighted(). For a full 3-segment path: leaf 12, middle 9, root 6 — 44% / 33% / 22%. As parents cap out, the surviving weights redistribute toward the leaf.
    • All capped -> undefined, and use_skill returns 0 even though the roll succeeded.
  3. Clampclamp_improvement(chosen, improvement) trims the bound to cap - current, so a near-cap node gets a proportionally smaller bound.
  4. Applyimprove_skill(chosen, clamped) coerces the bound to a float (float as-is, int promoted, functional evaluated against this_object(), omitted -> SKILLS.default_gain via ??=), adds random_float(bound) to that node, and notifies the player if the floored level rose.

This means using "combat.melee.slashing" can also improve "combat.melee" or "combat" — but with lower probability. Parent skills grow organically as their children are used, but more slowly because they are picked less often.

The cap is query_level() * SKILLS.cap_factor — base level, not query_effective_level(), so a level boon does not raise the ceiling. Progress rolls freely over intervening level boundaries; only the cap halts it, and the only way to lift it is to level.

Query API Grid

Four query functions form an orthogonal grid over two axes: floored vs raw flo


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.

SkillInstallsUpdatedSafetyDifficulty
skills-and-advancement (this skill)02moNo flagsAdvanced
azure-eventhub-py13moReviewIntermediate
benchling-integration18moNo flagsAdvanced
latchbio-integration18moReviewAdvanced

Try saying

Example prompts that trigger this skill in your AI assistant.

Search skills

Search the agent skills registry