Bosses

Usage guide of enemy.boss: bosses as data, health-gated phases, telegraphed patterns (slam, sweep, lunge, nova, summon), a scripted end instead of a death, boss bar variables, events and hooks.

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

Also read offline — CLI: grip-forge docs enemy-bossMCP tool: gripforge_docs { "topic": "enemy-boss" }

Boss fights (enemy.boss) #

What it is for #

Boss fights as data: health-gated phases that cycle telegraphed patterns (slam, sweep, lunge, expanding rings and summons), with an intro, a boss health bar and a defeat event. A boss is an enemy of enemy.ai whose document names a boss document. Shared concepts of the family are in the Enemies in your game guide.

Turn it on #

gripforge_gamekit_install   { project_id: "gkp_…", id: "enemy.boss" }
gripforge_gamekit_configure { project_id: "gkp_…", id: "enemy.boss",
                              config: { "introSeconds": 1.5, "telegraphScale": 1 } }

Needs enemy.ai, combat.melee, combat.core, game.engine; installing the kit brings them along.

First example #

<!-- test: kits/game_engine/host/tests/boss_recovery.rs::nova_recovery_reaches_only_the_last_real_strike_and_holds_the_brain --> An enemy names a boss document; the boss has one phase and one pattern. This is the shape the test builds (an optional recovery of 0 to 60 seconds follows a pattern; a nova applies it only after its last ring).

{ "enemies": [{ "id": "keeper", "name": "Keeper", "health": 100, "boss": "keeper", "attacks": [] }],
  "bosses":  [{ "id": "keeper", "name": "Keeper",
    "patterns": { "slam": { "type": "slam", "damage": 20, "radius": 4, "windup": 0.8, "at": "target" } },
    "phases":   [{ "id": "only", "untilHealth": 0, "patterns": ["slam"], "pause": 1.5 }] }] }

Settings and defaults #

SettingDefaultMeaning
introSeconds1.5Seconds the boss waits after spawning (0 to 10).
telegraphScale1Multiplies every pattern windup (0.25 to 3).

A document of bosses holds patterns (id to pattern) and phases (id, untilHealth, patterns, pause, enrage). A pattern has a type: slam (damage, radius, windup, at), sweep (range, arc), lunge (distance, width), nova (rings, radiusStep, interval) or summon (enemy, count); each has an optional recovery. The boss model is bound to the asset slot boss_model.

Scripted end #

<!-- test: kits/game_engine/host/tests/boss_end.rs::the_defeat_of_the_player_ends_the_fight_without_a_death_or_a_respawn --> A fight the story decides (a battle that must be lost, a boss that flees, a scene that takes over before the last blow) is an optional end on the boss document, instead of a death:

{ "id": "breaker", "name": "The Breaker",
  "patterns": { "sweep": { "type": "sweep", "damage": 25, "range": 4, "arc": 160, "windup": 0.9 } },
  "phases":   [{ "id": "only", "untilHealth": 0, "patterns": ["sweep"], "pause": 1.2 }],
  "end":      { "at": 0.1, "playerDefeat": "end", "hold": 2 } }
FieldDefaultMeaning
atnoneHealth ratio (0 to 1) at or below which the fight ends; a lethal blow ends it too and leaves the boss that fraction of its health.
playerDefeatdieend: a lethal blow on the player while this boss is the active one ends the fight too; the player keeps 1 health and no player.died is emitted, so checkpoints and end screens do not react.
hold2Seconds the player takes no damage after the end (0 to 30).

Once the end is accepted the boss stands still for good, plays no more patterns and takes no more damage, and boss.ended is emitted once per fight. Start what follows from that event, for instance a trigger that plays a cinematic. Without end nothing changes: the boss dies and boss.defeated is emitted.

Services, events, variables #

  • Events: boss.started (entity, boss, name), boss.phase (entity, boss, phase), boss.pattern (entity, boss, pattern, type), boss.defeated (entity, boss), boss.ended (entity, boss, reason: threshold or player_down). It listens to enemy.spawned, combat.killed, entity.despawned, and combat.damaged when a boss document has end.
  • Variables: boss.name, boss.health, boss.max, boss.phase (they feed the boss bar).
  • Component boss { "doc": "warden" }: on an enemy instance, makes it fight as that boss even when its enemies document names none.

Hooks and modding #

HookMeaning
boss.startAn enemy is about to become the active boss; cancelled, it fights with its patterns but is not announced and takes no boss bar.
boss.phase.changeThe health ratio puts the boss in another phase; phase is mutable (the id of another phase of the document); cancelled, it stays in the current one.
boss.pattern.playThe boss is about to play the next pattern of its phase; pattern and telegraphScale are mutable; cancelled, the pattern is skipped and the boss pauses.
boss.endThe fight of a boss with end is about to end (reason, ratio); cancelled, the fight goes on as without end for that reason and it is not asked again.

Moddable: both settings and the collection bosses.

Limits #

Runs as the same Rust logic on the web, in the native player, Godot, Unity and Unreal. The hooks, the optional recovery and the scripted end are served by the web and native Rust logic; the Godot, Unity and Unreal deliveries keep the earlier fixed recoveries and do not implement end. With playerDefeat, only the local player is watched: in co-op the other players still go down as usual. Unbound, the boss is drawn as a large capsule: bind a rigged model to boss_model.

Versions #

Kit 1.6.0 (hooks in 1.4.0, optional recovery in 1.5.0, scripted end in 1.6.0); service enemy.boss 1.0.0. It relies on enemy.behaviors ^1 (from enemy.ai), combat.melee ^1, combat.health ^1 and game.engine ^1.