Levels and skills

Experience, a level curve, attribute and skill points, skill trees as data, and the summed stats other kits read.

Source: module-kits:kits/progression_levels/usage/en.mdView Markdown source

Also read offline — CLI: grip-forge docs progression-levelsMCP tool: gripforge_docs { "topic": "progression-levels" }

Levels, attributes and skills (progression.levels) #

What it is for #

progression.levels gives a game experience points, levels, points to spend on attributes and on skills, and the stats they grant. XP comes from the kills made by the player, from missions and from your own rules. A level is bought with a growing curve; each level grants attribute points and skill points. Skills are plain data (the skills collection) with trees, costs, minimum level and prerequisites. Everything unlocked is summed into stats that combat.core applies: maximum health, damage and armour. Each player entity has its own sheet, so co-op works; the HUD variables show the sheet of the HUD player. The kit shows the level badge and the character sheet through an ui.webui fragment.

Turn it on #

gripforge_gamekit_install   { project_id: "gkp_…", id: "progression.levels" }
gripforge_gamekit_configure { project_id: "gkp_…", id: "progression.levels",
                              config: { "maxLevel": 30, "baseXp": 120, "xpByTag": { "boss": 500, "elite": 80 } } }

It requires game.engine. Kills only count when combat.core is installed (the kit listens to combat.killed); the stats reach the player's health and damage through combat.health and combat.damage. The key C opens the character sheet (input action character_sheet).

Skills are a data collection of the project:

{ "skills": [
  { "id": "cut", "name": "Cut", "tree": "one_handed", "cost": 1, "stats": { "damage_add": 2 } },
  { "id": "deep", "name": "Deep cut", "tree": "one_handed", "requires": ["cut"], "minLevel": 3, "stats": { "damage_add": 3 } }
] }

First example #

<!-- test: kits/game_engine/host/tests/progression_levels.rs::xp_gives_levels_points_and_stats --> The test sets maxLevel: 3, baseXp: 100, growth: 2 and the two skills above, then drives the service progression.levels:

  1. toNext() answers 100; addXp(120.5) crosses level 2 and answers the XP kept (20.5).
  2. points() is { attribute: 1, skill: 1 }. unlock("deep") is false (level and prerequisite), unlock("cut") is true.
  3. spendAttribute("health") is true once, then false (no point left).
  4. stats() is { damage_add: 2, health_max: 10 }.
  5. A big addXp(500) stops at the cap: level() is 3, toNext() is 0, and unlock("deep") now works.

Settings and defaults #

SettingTypeDefaultMeaning
maxLevelinteger (2..999)50level cap; XP stops there
baseXpnumber100XP to reach level 2
growthnumber (1..3)1.18each level needs this times the previous cost
xpPerKillnumber20XP for a player kill when the victim has no xp component (0: none)
xpByTagobject{}XP by victim tag, e.g. { "boss": 500 }; the highest matching tag wins over xpPerKill
attributePointsPerLevelinteger (0..20)1attribute points granted per level
skillPointsPerLevelinteger (0..20)1skill points granted per level
attributeslisthealth, stamina, magickawhat an attribute point buys: { id, label, stat, perPoint }
healOnLevelUpbooleantruea level up restores the player to full health

The default attributes raise the stats health_max, stamina_max and mana_max by 10 per point. The cost of going from level L to L + 1 is baseXp x growth^(L - 1), rounded. A victim's own xp component, if it has one, is its worth.

A skill of the skills collection has id, name, and optionally tree, description, cost (skill points, default 1), requires (skills to unlock first), minLevel and stats (numbers granted while unlocked). All settings and the skills collection are moddable.

Services, events, variables #

The service progression.levels (about the HUD player or the first sheet):

  • xp(), level(), toNext(): current XP, level, and XP missing for the next level (0 at the cap).
  • addXp(amount, reason?): adds XP, answers the XP kept.
  • points(): { attribute, skill } points available.
  • attributes(): the attributes with their current rank.
  • spendAttribute(id): spends one point; false if none left, unknown attribute, or a hook refused.
  • skills(): the skills with unlocked and available; unlocked(id), canUnlock(id), unlock(id).
  • stat(name) and stats(): summed stats. Stats understood by other kits: health_max (through combat.health), damage_add and damage_mult (an outgoing modifier of combat.damage), armor (an incoming reduction of 100 / (100 + armor)). Any other name is kept for your own use.
  • sheet(open?): opens or closes the character sheet and answers its state.

Events: progression.xp (amount, xp, level, reason), progression.levelup (level, attributePoints, skillPoints), progression.attribute (attribute, rank, stat, value), progression.skill (skill, tree), progression.stats (stats). The kit listens to combat.killed, mission.completed (XP from rewards.xp) and game.phase (a pause closes the sheet).

Variables: progression.sheet, progression.level, progression.xp, progression.xpNext, progression.attributePoints, progression.skillPoints.

Verbs for rules and scripts: add_xp (action, amount, optional reason), unlock_skill (action, skill), and the conditions level_at_least (level) and skill_unlocked (skill):

{ "add_xp": 50 }
{ "unlock_skill": "cut" }
{ "level_at_least": 5 }

Hooks and modding #

  • xp.gain: { player, amount, reason, level }; amount is mutable; cancellable. Cancelled or 0: no XP, no event.
  • level.up: { player, level, attributePoints, skillPoints }; both point counts are mutable; cancellable (the level is not gained, the XP is kept and the level is asked again at the next gain).
  • attribute.spend: { player, attribute, stat, rank, cost }; cost is mutable; cancellable.
  • skill.unlock: { player, skill, tree, cost }; cost is mutable; cancellable.

A mod can double XP in a zone, grant extra points at level 10, or refuse a skill while a quest is open, without touching the kit. The settings and the skills collection can also be changed by a mod, and an entry can extend another.

Engines #

The logic is one Rust module, identical on the web, the native player, Godot, Unity and Unreal. The level badge and the character sheet are an ui.webui fragment.

When it does not do what you expect #

  • No XP from kills: the killer must be the player and combat.killed must be emitted (install combat.core); xpPerKill may be 0; or an xp.gain tap cancelled it.
  • unlock(id) answers false: not enough skill points, level below minLevel, a prerequisite missing, already unlocked, or a hook refused. canUnlock(id) tells before trying.
  • Maximum health does not change: the stat is health_max and needs combat.health.
  • XP stays at 0 at the top: the cap was reached.

Limits #

  • No respec: points are not given back.
  • Stats other than health_max, damage_add, damage_mult and armor are only summed; your game reads them with stat.
  • The damage modifiers follow the sheet of the HUD player.

Versions #

Kit version 1.2.0 (service contract 1.1.0). 1.1.0 added one sheet per player entity (co-op); 1.2.0 added the hooks and the moddable settings.