Npc
<Npc> spawns a non-player character inside a World: the same walker, body, avatar, clips and navmesh path as the player’s character, driven by its routines instead of a person. World of Warcraft makes an NPC the same way, as data: a name, a title, a level, a faction, and a path with a pause at each point. An NPC with no routines stands and plays idle. An avatar is posed less often while it is small on screen or out of view, as another player’s character is, unless cullingMode says otherwise; a model is posed every frame.
import { Faction, Interact, Npc, NpcEvent, routines } from "@spawnite/engine";
const guard = routines("guard") .routine("post", (r) => r.wait({ between: [10, 20] }).turn(90)) .start("post");
export function Guard() { return ( <Npc name="Bram" title="Gate guard" level={8} faction={Faction.Neutral} position={[2, 0, 4]} routines={guard} > <Interact prompt="Talk" /> </Npc> );}It takes <CharacterSpawn>’s body fields, its look, position and facing, and these of its own:
nameandtitle: drawn over its head in Text’s white with a dark edge, the title under the name as WoW draws a subname. The AI tree names it by them and its faction: “Mira, a herbalist, friendly”.level: a number a game reads.faction:Faction.Friendly,NeutralorHostile, friendly by default. It says which side the NPC is on, and does not make it a target: an ability strikes an NPC that carriesTargetableTrait, which Targeting shows how to give it.routines: whatroutines()built.leash: metres from its spawn it may walk. A route point past the leash is skipped with a warning at spawn, and a walk that carries it past the leash ends and walks it home, as WoW’s evade does. A routed NPC has none by default, since its route bounds it.
Its children are behaviours on it, as on an Entity. <Interact prompt="Talk" /> shows the prompt when a player’s character comes close and emits the InteractedEvent event on a press, which a routine answers with NpcEvent.Interacted. <Dialog tree={...} /> gives it a conversation, which a press of that prompt starts for the player who pressed. While any conversation goes on, the NPC holds the TalkingTrait trait, which holds its routine alone.
What it wears
Section titled “What it wears”An NPC wears one look, from the same props the player’s character takes:
avatar: a nameregisterAvatarregistered, or the body itself.model: the name of a model the game registered, such as the example template’svisor-bot. Where its file has clips, it plays the ones its pose asks for,idleat rest,walkandrunblended by its speed, andjumpoff the ground.clipsnames the file’s own clip for each that the file names another way, such as{ run: "Sprint" }, andclipSpeedsgives the ground speed at which its walk and its run play at their own pace. A routine’splaystep names one of the poses’ clips, as Routines lists them, and a model plays its clip for that pose, throughclips, or else the file’s clip of that name, such asland; a model with neither plays nothing over its pose, and a dev world warns once. A clip under any other name, such as a wave, plays withplayClip, as on the player’s model.shapeandcolor: a primitive in a colour, the capsule when it names no look.
Two looks at once fail the typecheck, such as an avatar beside a model, or a color beside either: a color tints the primitive alone. This merchant wears the robot the player wears, and walks where it would run:
import { Faction, Interact, Npc } from "@spawnite/engine";
/** The lobby's merchant, in the robot the player wears: the game's * registered `visor-bot`, which walks where it would run. */export function Merchant() { return ( <Npc name="Tobin" title="Merchant" faction={Faction.Friendly} position={[4, 0, -3]} model="visor-bot" clips={{ run: "walk" }} > <Interact prompt="Talk" /> </Npc> );}What it holds
Section titled “What it holds”Its npc trait holds its name, title, level and faction, and what its routine is doing: the routine and the step, the seconds on the step, the route point it walks to and the pass along the route it is on, and the line a say step gave it, which draws right over its name, wrapped and faded with the camera’s distance. Its routines compile to a state machine whose trait dumps beside it under the name routines() took, miraRoutines for routines("mira"). A spawnite play dump shows both each step, so a test asserts where it is and what it is doing.
In the devtools
Section titled “In the devtools”The tree shows each NPC as a row under its name, and the Map tab counts it under the avatar it wears, where it wears one: a registered avatar by its name, and a body handed over whole by its name, or by its file’s name where it has none, which a production build hashes. The inspector shows its npc trait and its machine’s card: the routine, the step in words, such as walk stall to well to herbs, the machine’s state and a wait’s seconds left. The Wiki tab lists each NPC under the npc kind with its routines in words and, where it has a Dialog, its lines by node with the choices each offers. A conversation’s machine shows in the Wiki tab under machine, as any machine does.
In a room
Section titled “In a room”The room spawns and steps an NPC, and each page draws the one the room streams at its place in the scene, as it does an Entity. The routine system runs on the server, as Chase does, and a single-player game runs it on the page. A player’s press of its prompt reaches the room as Interact describes, and the stream carries its npc trait and its machine’s trait to every page, so each page draws the same line over its head. The room runs each conversation too, and streams its record to the talking player alone, as Dialog describes. Each page’s <Npc> compiles the same routines as it mounts, so the machine’s name is registered before the stream brings its trait, and the page’s inspector and Wiki tab show the routines in words as the room’s do.