Add NPC dialogue
Goal
A third person in the arena with a script of their own: two lines and a yes/no question, their own name above the box, and their own expression per line. No change to the dialogue system.
Files you will edit
src/dialogue.jsonsrc/prefabs.ts
Steps
Write the script.
src/dialogue.jsonis a record of conversations keyed by name. Each has aspeaker, a list oflines, and an optionalquestionwith ayesand ano.facenames a preset inFACES, insrc/systems/dialogue.ts—neutralorsmileship with the template.json{ "smith": { "speaker": "Smith", "lines": [ { "text": "You will want the key before you try that door.", "face": "neutral" }, { "text": "I would fetch it myself, but I am busy.", "face": "smile" } ], "question": { "text": "Want me to point at the chest?", "face": "smile", "yes": { "text": "That one. The obvious one.", "face": "smile" }, "no": { "text": "Brave. Good luck.", "face": "neutral" } } } }Tag an NPC with it. Which script an NPC runs is a tag, so the level stays declarative and nothing is patched up after the spawn. In
src/prefabs.ts:ts/** Tag: this NPC runs the `smith` conversation in `src/dialogue.json`. */ export const Smith: Record<string, never> = {}; /** The smith: a third person to talk to. */ export const SmithPrefab = prefab({ name: 'smith', body: NPC_BODY, character: 'char.guide', components: [Interactable, Npc, Smith], });Point
scriptOfat the tag. One line insrc/systems/dialogue.tschooses the key:tsexport function scriptOf(ctx: GameContext, npc: number): string { if (hasComponent(ctx.world, npc, Smith)) return 'smith'; return hasComponent(ctx.world, npc, Guide) ? 'guide' : 'wanderer'; }Put them in the level. A point in
src/arena.tsand an entry in thespawnslist insrc/game.ts, lifted byNPC_CENTRElike the others.
That is the whole recipe. Everything else is already done for you: character.setExpression and character.lookAt go out for every line, and dialogueState.active freezes the hero while anyone is talking. Both are the dialogue system's job, not the script's.
Whether any of it is drawn is the host's decision, not the script's. Out of the box the NPC is a capsule and the expression and the gaze are recorded on adapter.animationOf(entity) with one warning. Give the template a character bridge — Give an NPC a face — and the same commands move a real splat head, with nothing in src/ changing.
Verify
sh
npm test -w templates/third-person
npm run dev -w templates/third-personWalk up to the smith. The HUD shows speaker Smith and the first line, E advances, and the question offers 1 yes and 2 no. In the headless tests, scripts.smith.lines is readable straight out of the import and dialogueState.script is 'smith' once the conversation starts.
See also
- Build your first adventure — the whole loop
- Add an interactable — the other thing
Ecan start - Add a locomotion state — what the hero does meanwhile
- Characters — what expressions and gaze will drive