# Abilities and skills (combat.abilities) ## What it is for `combat.abilities` runs the abilities of any actor: a player skill, a caster enemy, a shrine, a boss move. An ability is data in a pack: a cost, a cooldown, tag conditions, a windup, an active window, a recovery, a bounded target shape and a list of effects. One controller per actor (created on first use) walks a cast through its phases, takes the cost, starts the cooldown and tests the targets once, nearest first, in the active window. Damage, healing and statuses go through `combat.core` (see the [combat guide](guide:combat)). Animation, sound and effects are not played here: every phase is announced as an `ability.cue` event that carries the presentation names the ability declares. The kit makes no humanoid or player-only assumption. ## Turn it on ```text gripforge_gamekit_install { project_id: "gkp_…", id: "combat.abilities" } gripforge_gamekit_configure { project_id: "gkp_…", id: "combat.abilities", config: { "globalCooldownFrames": 0, "pack": { … }, "inputBindings": [ … ] } } ``` It requires `game.engine` and uses `combat.core` when installed. The `use_ability` verb casts an ability from a script or a rule. ## First example A pack with one ability bound to a key. Pressing `KeyF` casts it once; holding the key does not cast again, and the cooldown (0.5 s) stays authoritative. While the game is paused the press does nothing. ```json { "inputBindings": [{ "action": "pulse", "ability": "pulse", "keys": ["KeyF"] }], "pack": { "schema": "gripforge.abilities/1", "id": "test", "name": "Test", "statuses": [], "abilities": [{ "id": "pulse", "name": "Pulse", "cooldown": 0.5, "timing": { "windup": 0.1, "active": 0.1, "recovery": 0.1 }, "targeting": { "shape": "circle", "range": 3, "team": "enemy", "maxTargets": 8 }, "effects": [{ "type": "damage", "amount": 30, "knockback": 2 }] }] } } ``` ## Settings and defaults | Setting | Default | Meaning | |---|---|---| | `pack` | empty pack | The abilities and statuses (see below). Moddable. | | `inputBindings` | none | Up to 16 player buttons `{ action, ability, label?, keys?, buttons? }`. A press edge casts the ability through the same controller; costs, cooldowns, targeting and pause still apply. | | `globalCooldownFrames` | `0` | Fixed simulation frames (0 to 3600) between two successful casts of one actor. | ## The ability pack `schema: "gripforge.abilities/1"`, an `id`, a `name`, then `abilities` and `statuses` (the same status entries as `combat.core`). An ability has: `id`, `name`, `tags`, `requires` (tags the caster must carry), `blocks` (tags that forbid the cast), `cooldown` (seconds), `cost { resource, amount }` (taken from the caster resources), `timing { windup 0.3, active 0.15, recovery 0.3 }`, `targeting { shape: self | arc | circle | line, range 2, arc 90, width 1, offset 0, height 3, maxTargets 16, team: enemy | ally | any, follow }`, 1 to 8 `effects` and a `presentation { animation, animationAsset, vfx, sound, socket }`. An effect is `damage`, `heal` or `status` (with `knockback` on damage), or the spatial `smoke`, `wall` and `teleport`. An instance placed by hand gets its starting resources from the `abilities` component, for example `{ "resources": [{ "id": "mana", "amount": 100 }] }`. ## Services Service `combat.abilities` (the first argument is always the casting entity): - `use(source, abilityId, yaw?)`: cast; answers `{ ok: true }` or `{ ok: false, reason }` with the reason `unknown_ability`, `dead`, `busy`, `global_cooldown`, `cooldown`, `unbound`, `conditions`, `invalid_aim`, `invalid_target`, `resource`, `paused`, or the reason of a tap. - `useTarget(source, abilityId, target, yaw?)`: cast at a named target; its team, range and life are checked again. - `cancel(source, force?)`: cancel the running cast; `cancelTarget(target)` cancels the casts aimed at a target. - `cooldown(source, abilityId)` and `globalCooldown(source)`: what is left. - `pack()` and `packRevision()`: the pack and its revision number. - `connect(port)`: use another backend than `combat.core` (a capability another kit serves, `{ service, methods }`). Verb: `use_ability { instance | entity, ability, yaw? }` (default entity: the player). ## Events - `ability.cue`: `{ source, ability, cast, phase, target?, presentation }` with `phase` one of `windup`, `active`, `hit`, `recovery`, `ended`, `cancelled`. `target` only comes with `hit`. The kit listens to `entity.despawned` and `combat.killed` to end casts. ## Hooks and modding | Hook | Payload (mutable first) | Effect | |---|---|---| | `ability.cast` | `{ source, ability, yaw }` (`yaw`) | Before the checks; cancel it to refuse the cast (a silence); nothing is spent. | | `ability.spend` | `{ source, ability, resource, amount }` (`amount`) | The cost about to be taken; cancelled or insufficient: refused with reason `resource`. | | `ability.apply` | `{ source, target, ability, scale }` (`scale`) | One target about to be hit; `scale` multiplies damage and heal; cancel to skip it. | | `ability.cooldown` | `{ source, ability, cooldown }` (`cooldown`) | The cooldown about to start; not cancellable. | `pack` and `inputBindings` are moddable, and so is the `abilities` collection. ## Engines Web and the native player run the Rust implementation. Godot, Unity and Unreal receive the controller as files: bind the project combat and presentation callbacks and validate in the destination project. ## When it does not do what you expect - A cast is refused: read the `reason` of `use` (`busy`, `cooldown`, `global_cooldown`, `conditions` for a missing or blocking tag, `resource`, `paused`, or a tap of `ability.cast`). - A held key casts only once: by design; release and press again. - `connect` fails on the native host: pass `{ service, methods }`, not an object of functions. ## Limits No animation or sound playback, no pathing. The spatial effects `smoke`, `wall` and `teleport` need a bound placement port and refuse before spending when none is bound. ## Versions Kit 1.8.0 (explicit `useTarget`, `globalCooldownFrames`), 1.6.0 (`inputBindings`), 1.3.0 (`ability.cooldown`), 1.2.0 (hooks). Requires `game.engine` 1.