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.
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 #
| Setting | Default | Meaning |
|---|---|---|
introSeconds | 1.5 | Seconds the boss waits after spawning (0 to 10). |
telegraphScale | 1 | Multiplies 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 } }
| Field | Default | Meaning |
|---|---|---|
at | none | Health 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. |
playerDefeat | die | end: 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. |
hold | 2 | Seconds 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:thresholdorplayer_down). It listens toenemy.spawned,combat.killed,entity.despawned, andcombat.damagedwhen a boss document hasend. - 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 #
| Hook | Meaning |
|---|---|
boss.start | An 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.change | The 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.play | The 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.end | The 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.