Combat Demo
App: apps/combat_demo/
Demonstrates the combat substrate (engine PLM-196 / ADR 0066 + 0069) with hand-built boxes: an
attacker’s sword Hitbox lunges at a target’s body Hurtbox.
Each box is a Jolt sensor riding its node; Jolt reports the overlap and the engine calls
Combat.onHit. The callback reads the hitbox’s opaque payload
as damage, subtracts it from a plain Wren health field, and on death disables the hurtbox so the corpse
stops taking hits.
For the Blender-authored path — hit/hurt meshes authored in Blender and reached via
node.hitboxes — see the Blender Combat Demo. This demo builds its
boxes by hand in Wren instead, which is the simplest way to see the surface.
There is no HP/damage/death type in the engine. Health here is a plain _hp field — exactly how a
real game (Hadal, LowTide) implements it. The engine’s job ends at detection.
Run from root
Section titled “Run from root”% ./plume3d combat_demoA GUI window shows the target’s HP (a game-side bar), whether the swing is active, and a running hit log.
What it does
Section titled “What it does”- On
init, builds a scene with an attacker node and a target node, a swordHitbox(owner = 1,tag = "sword",payload = 25) on the attacker, and a bodyHurtbox(owner = 2) on the target, then registersCombat.onHit(scene) { |hit| … }. - The callback is the game’s health model:
_hp = _hp - hit.payload. When_hpreaches0it sets_deadand_body.enabled = false— a corpse stops taking hits, a game decision the engine knows nothing about. - In
update, a 1.5 s swing cycle lunges the attacker into overlap with the target, holds briefly, then retreats. Toggling_sword.enabledis a game-driven “active window” — deactivating re-arms the next swing (a hitbox strikes each victim once per active period). A real game would insteadbindWindow(controller, "swing_hit")to an animation hit-window authored as Blender pose-markers.
Key pattern
Section titled “Key pattern”import "engine" for Scene, Hitbox, Hurtbox, Combat
var sword = Hitbox.sphere(attacker, null, "", 0.6)sword.owner = 1sword.tag = "sword"sword.payload = 25 // opaque to the engine — this game reads it as damagevar body = Hurtbox.box(target, null, "", 0.5, 1.0, 0.5)body.owner = 2
var hp = 100 // health is 100% game-sideCombat.onHit(scene) { |hit| hp = hp - hit.payload // engine detected; the game decides the cost if (hp <= 0) body.enabled = false // death is a game decision}Verify headless
Section titled “Verify headless”% ./plume3d --test apps/combat_demoChecks the frozen surface is bound + callable and the game-side health model is sound. The
detection mechanics (sensor overlap, dedup, i-frame/window gating, owner/layer filtering) are covered
by tests/unit/test_combat_wren.cpp; that combat sensors never leak into Physics.* world queries is
covered by tests/unit/test_combat_queries.cpp.