Skip to content

Chapter 48 — Abilities System

Part VII — Systems Reference

Complete reference for the ability system — definitions, effects, statuses, resistances, AI intent, review, and authoring tools.

The ability system spans three layers:

LayerFilesPurpose
Ability Coreability-core.tsResolution pipeline: costs, checks, targeting, effect dispatch, cooldowns
Ability Effectsability-effects.tsEffect handlers: damage, heal, stat-modify, status apply/remove
Ability Intentability-intent.tsAI scoring: self/AoE/single paths, resistance awareness, cleanse valuation
Ability Reviewability-review.tsRuntime tracing: per-use breakdowns, inspector, formatted output
Ability Summaryability-summary.tsAuthoring-time analysis: pack summary, balance audit, Markdown/JSON export
Ability Buildersability-builders.tsConvenience factories for common ability patterns
Status Semanticsstatus-semantics.tsTag vocabulary, registry, resistance-aware status application

Ability Core and Effects are EngineModules (runtime). Intent, Summary, and Builders are pure functions (authoring/AI time). Review is an EngineModule that observes runtime events.

Every ability is an AbilityDefinition:

const fireball: AbilityDefinition = {
id: 'fireball',
name: 'Fireball',
verb: 'use-ability',
tags: ['magic', 'fire', 'damage'],
costs: [{ resourceId: 'stamina', amount: 3 }],
target: { type: 'all-enemies' },
checks: [{ stat: 'will', difficulty: 7 }],
effects: [
{ type: 'damage', target: 'target', params: { amount: 5, damageType: 'fire' } },
],
cooldown: 3,
};
TypeBehavior
selfActor only
singleOne enemy (requires targetIds)
all-enemiesAll enemies in the actor’s zone
zoneZone-targeted (no entity target)
noneNo target needed

Each cost deducts a named resource: { resourceId: string, amount: number }. The engine validates that the actor has enough before resolving. Common resources: stamina, hp, mana, morale, infection, power, fatigue, composure.

Each check tests stat + roll(1-20) >= difficulty * 2. The onFail field controls what happens when a check fails:

  • 'abort' — entire ability fails (costs still deducted)
  • 'half-damage' — effects apply at reduced intensity
  • Omitted — ability continues but the check is noted as failed

After use, the ability cannot be used again for cooldown ticks. Cooldown bands for analysis:

BandTicks
instant0
short1–2
medium3–4
long5+

Optional conditions that must be true before use. Common condition types:

  • has-tag — actor must have a specific tag (e.g., investigator, survivor)
  • Evaluated before costs are deducted; rejected abilities emit ability.rejected

Six built-in effect types:

EffectParamsDescription
damageamount, damageTypeDeal damage to target
healamount, resourceRestore a resource (default: hp)
stat-modifystat, amountTemporarily adjust a stat
resource-modifyresource, amountAdd/subtract a resource
apply-statusstatusId, durationApply a status effect (resistance-aware)
remove-status-by-tagtags (comma-separated)Remove statuses matching any listed tag

Statuses are defined separately from abilities and registered globally:

const rattled: StatusDefinition = {
id: 'rattled',
name: 'Rattled',
tags: ['fear', 'debuff'],
stacking: 'replace',
duration: { type: 'ticks', value: 2 },
ui: { icon: '!', color: '#e74c3c', description: 'Shaken by primal fury' },
};

All status tags must come from the 11-tag vocabulary:

TagMeaningCleansed By
buffPositive effect
debuffGeneric negativeBroad cleanse
fearPsychological terrorFear cleanse
controlMind control, tauntControl cleanse
blindVision impairmentBlind cleanse
stanceTactical posture
holyDivine effect
breachSystem/armor breachBreach cleanse
poisonOngoing toxic damagePoison cleanse
supernaturalOtherworldly force
woundPhysical injury
  • replace — new application replaces existing (resets duration)
  • stack — multiple instances accumulate (up to maxStacks)
  • refresh — resets duration without adding stacks

Entities can have resistance profiles on their resistances field:

const boss: EntityState = {
id: 'elder-vampire',
resistances: { fear: 'immune', holy: 'vulnerable' },
// ...
};
LevelEffect
immuneStatus is completely blocked; ability.status.immune event fires
resistantDuration halved; ability.status.resisted event fires
vulnerableDuration doubled; ability.status.vulnerable event fires

Resistance is checked against all tags on the status definition. Priority: immune > resistant > vulnerable. If a status has tags [fear, debuff] and the target has { fear: 'immune' }, the status is blocked entirely.

The resistance system has a deliberate defensive bias — immune always wins over vulnerable when both match different tags on the same status.

Cleanse abilities use remove-status-by-tag to strip matching statuses:

effects: [
{ type: 'remove-status-by-tag', target: 'actor', params: { tags: 'fear,blind' } },
],

This removes any status on the actor whose registered tags include fear OR blind. Design guidance:

  • Pair every status-heavy pack with at least one cleanse ability
  • Cleanse should cover 1–3 tags (overbroad cleanse is flagged by validation)
  • Costs should be meaningful — free cleanse undermines status gameplay

The AI scoring system (ability-intent.ts) evaluates abilities in three paths:

Evaluates heal/buff/cleanse value based on actor state:

  • Heal value scales with missing HP percentage
  • Cleanse value scales with active debuff count
  • Buff value is lower priority unless specific conditions met

Scores based on number of valid targets:

  • More enemies = higher value
  • Penalized if many targets are immune to the ability’s status effects

Scores based on target health, status, and resistance:

  • Damage preferred against high-HP targets
  • Status preferred against targets without existing debuffs
  • Resistance awareness: immune targets get heavy score penalty; vulnerable targets get bonus

Every score includes a contributions array explaining the decision:

contributions: [
{ factor: 'damage-value', value: 5, weight: 1.0, delta: 50 },
{ factor: 'resistance-penalty', value: -100, weight: 1.0, delta: -100 },
]

Building a genre-native ability pack:

  1. 3–4 abilities per pack — enough for tactical variety without filler
  2. Identity-first — each ability should reinforce the genre fantasy
  3. Resource loops — costs should create meaningful tension (e.g., infection-as-cost in zombie)
  4. Genre-native statuses — one status per pack is typical; use semantic tags from the vocabulary
  5. At least one cleanse — if the pack applies statuses, pair with a counter
  6. One signature ability — cooldown >= 4, multiple effect types, defines the pack’s identity
  7. Resistance profiles on key enemies — bosses should resist the pack’s primary status
  • Filler abilities: plain damage-only abilities with no interesting cost or check
  • Reskinned spam: multiple abilities with identical damage + same status, different names
  • Overbroad cleanse: removing > 3 tag families makes cleanse feel mandatory
  • Missing counters: applying statuses without any removal path
  • Zero-cost zero-cooldown: free instant abilities bypass resource tension

The ability-review module records an AbilityTrace for every ability use:

--- Ability Trace [tick 3] [offensive] ---
Survivor → War Cry (war-cry)
Outcome: success
Targets: Shambler, Runner
Costs:
stamina: -3 (15 → 12)
infection: -5 (10 → 5)
Checks:
nerve vs 6: roll 14 → PASS
Effects:
[status] Shambler
[status-immune] Bloater Alpha (immune)
Summary: Survivor used War Cry on Shambler, Runner — 2 effects applied

Traces include resistance outcomes (immune, resisted, vulnerable) and are categorized as offensive/defensive/control/utility.

The review module registers a debug inspector showing:

  • Recent traces (last 5)
  • Active cooldowns with ticks remaining
  • Active statuses with expiry
  • Entity resistance profiles

summarizeAbilityPack(genre, abilities, opts?) produces:

  • Ability count, average cooldown, cooldown band distribution
  • Cost economy (total per resource type)
  • Effect type distribution (damage, heal, status, etc.)
  • Target type distribution
  • Statuses applied and cleanse tags covered
  • Abilities by category (offensive/defensive/control/utility)
  • Abilities by resource type
  • Resistance profile count (when entities provided)

auditAbilityBalance(packs) scans for balance problems:

FlagSeverityTrigger
zero-costinfoAbility has no resource costs
no-cooldowninfoAbility has cooldown of 0
extreme-damagewarningDamage > 2x pack mean
no-cleanseinfoPack applies statuses but has no cleanse
status-heavy-low-counterinfoPack applies >= 3 statuses but <= 1 cleanse
resource-economy-skewinfoOne resource used in > 70% of costs
signature-missinginfoPack has >= 3 abilities but none qualifies as signature
thin-packadvisoryPack has <= 2 abilities
no-tactical-triangleinfoPack >= 3 abilities but missing offense/defense/control

compareAbilityPacks(packs) produces a PackComparisonMatrix with identity profiles per pack, a status ecosystem summary, and recommendations. Each PackIdentityProfile includes:

  • Dominant category, signature ability, resource identity
  • Status family and cleanse tag coverage
  • Tactical triangle completeness
  • Distinctiveness score (0–100)

formatPackComparisonMarkdown(matrix) renders the matrix as a comparison table.

  • formatAbilityPackMarkdown(summary) — pack identity, tactical triangle, abilities, cost economy, cooldown distribution, categories, status interactions, resistance profiles
  • formatAbilityPackJSON(summary) — serializable object with all summary fields
  • formatPackComparisonMarkdown(matrix) — cross-pack comparison table with ecosystem summary

validateAbilityPack(abilities, ruleset, tags?) checks:

  • Costs reference resources that exist in the ruleset (implicit: hp, stamina)
  • Checks reference stats that exist
  • Effects reference known stats/resources
  • Status tags are from the semantic vocabulary (when tags provided)

Advisories (non-blocking warnings):

  • zero-cost-zero-cooldown — suspicious free + instant ability
  • overbroad-cleanse — cleanse covers > 3 semantic tags
  • excessive-duplicate-semantics — > 50% of pack applies same status tag
  • high-cost-low-value — expensive ability with lowest pack damage

validateStatusDefinitionPack(statuses, tags?) checks status definitions against the tag vocabulary.

Four convenience builders in ability-builders.ts:

import { buildDamageAbility, buildCleanseAbility } from '@ai-rpg-engine/modules';
const strike = buildDamageAbility({
id: 'power-strike', name: 'Power Strike',
damage: 5, damageType: 'melee',
stat: 'might', difficulty: 5,
costs: [{ resourceId: 'stamina', amount: 3 }],
cooldown: 2, tags: ['combat', 'damage'],
});
const purify = buildCleanseAbility({
id: 'purify', name: 'Purify',
cleanseTags: ['fear', 'poison'],
costs: [{ resourceId: 'stamina', amount: 2 }],
cooldown: 3, tags: ['support', 'cleanse'],
});

buildAbilitySuite(genre, abilities, ruleset) validates + summarizes + audits in one call.

Register ability modules in your starter pack’s setup.ts:

import { statusCore, createAbilityCore, createAbilityEffects, createAbilityReview } from '@ai-rpg-engine/modules';
const modules = [
statusCore,
createAbilityCore({
abilities: myAbilities,
statMapping: { power: 'might', precision: 'agility', focus: 'will' },
}),
createAbilityEffects(),
createAbilityReview(),
];

The statMapping tells the ability system which genre-specific stats correspond to the generic roles used by AI scoring. Register status definitions globally before use:

import { registerStatusDefinitions } from '@ai-rpg-engine/modules';
registerStatusDefinitions(myStatusDefinitions);

All 10 starter packs have 3+ abilities, cleanse coverage, and resistance profiles as of Phase 5:

PackAbilitiesStatusesCleanseResistancesIdentity
Fantasy31Yes2 entitiesDivine caster — holy damage + healing + cleanse
Cyberpunk31Yes2 entitiesNetrunner — ICE damage + debugging + nano-heal
Weird West31Yes2 entitiesGunslinger — AoE dust + grit cleanse + dead-eye
Vampire42Yes2 entitiesPredator — drain + mesmerize + blood fury + purge
Gladiator41Yes1 entityArena fighter — cleave + rally + challenge + resolve
Ronin41Yes2 entitiesDisciplined blade — iaijutsu + calm + ward + center
Pirate41Yes1 entitySwashbuckler — broadside + dirty fight + shanty + rum
Detective41Yes1 entityInvestigator — deduce + composure + expose + cleanse
Zombie41Yes2 entitiesSurvivor — swing + triage + war-cry + instinct
Colony41Yes2 entitiesCommander — plasma + protocol + override + reboot

The 11-tag semantic vocabulary: buff, debuff, fear, control, blind, stance, holy, breach, poison, supernatural, wound.

  • Active tags (used by statuses): fear, control, blind, stance, holy, breach, supernatural, buff, debuff
  • Vocabulary reserve (unused): poison, wound — available for future packs
  • Uncleansable: stance (intentional — self-applied posture, not a debuff)
  • Dead abilities: an ability that AI never prefers in any scenario (no pack has this currently)
  • Spam loops: heal/cleanse abilities repeatedly chosen when not needed (prevented by low-HP/debuff gating)
  • Overbroad cleanse: cleansing > 3 tag families makes it universally optimal (no pack does this)
  • Thin packs: < 3 abilities limits tactical variety (all packs now have >= 3)
  • Missing tactical triangle: a pack needs offense + defense/support + control for situational variety

A healthy pack has:

  1. Tactical triangle — offense, defense/support, and control/utility abilities
  2. Resource tension — costs create meaningful choices (not just “spend stamina”)
  3. Identity clarity — a unique resource, status family, or signature ability
  4. Resistance-aware enemies — at least one boss/elite with relevant resistances
  5. No dead abilities — every ability is the best choice in at least one scenario

See also: Combat System, Items and Status Effects, Crimson Court, Iron Colosseum, Jade Veil