Skip to content

Serverside NPCs serverside ​

The NPC this and its level view for serverside NPC scripts (npc.server.d.ts), on top of the server globals.

Source

Generated from templates/server/data/scripts/npc.server.d.ts. To change this page, edit the JSDoc in that file.

Overview ​

Ambient declarations for SERVERSIDE NPC scripts (the serverScript baked into a level's NPCs). NPC scripts also see everything in globals.d.ts (echo, triggerClient, server flags, timers, ...).

Inside every handler, this is the NPC itself (use function declarations, not arrow functions). Property writes are server-authoritative: they apply for EVERYONE in the level. Unknown properties are a per-NPC state bag that persists between handler calls (Graal's state-on-this convention).

  • export function onCreated(): void
    The NPC was instantiated: the first player activated the level, or the level's file was updated (hot reload re-creates every NPC).
  • export function onPlayerEnters(player: Player): void
    A player entered the NPC's level (fires again after a hot reload).
  • export function onPlayerChats(player: Player, chat: string): void
    A player in the level typed chat (script-set chat does not fire this).
  • export function onUpdate(dt: number): void
    Every server tick (20 TPS); dt is the tick length in seconds.
  • export function onAction<Name>(...): void
    A triggerAction landed on this NPC's shape. From a clientside script's triggerAction(x, y, name, ...params) the signature is (player: Player, ...params); from a serverside this.level.triggerAction(x, y, name, ...params) it is just (...params). <Name> is the action name with its first letter uppercased: triggerAction(x, y, 'openDoor') calls onActionOpenDoor.
  • export function onShotAt(x: number, y: number, data: any): void
    A projectile stopped in this NPC's level — hit a wall or ran out of lifetime — at (x, y). Coordinates are the projectile's space: level tiles, or gmap-global tiles when the level is a gmap member. data is the shoot call's payload. Fires on every server-scripted NPC of the level the projectile stopped over.
  • export function onShot(data: any): void
    A projectile hit this NPC's shape (the same hitbox onAction uses). Also fires on the NPC's CLIENTSIDE script on every client.
  • export function onMovementFinished(): void
    A this.move() with option 8 ended — reached its target, or stopped early at a wall (option 4). Fires once per flagged move.

setTimeout/setInterval/sleep work per-NPC: timer callbacks keep this bound to the NPC, and timers die when the NPC unloads (level hot reload).

Declarations ​

NpcLevel interface ​

ts
interface NpcLevel

A serverside NPC's view of its level (this.level): lets the NPC inspect the level, fire triggerAction at shapes in it, and put more NPCs into it.

NpcLevel.name ​

ts
readonly name: string

The level's file name, e.g. "start.glvl".

NpcLevel.triggerAction ​

ts
triggerAction(x: number, y: number, action: string, ...params: any[]): void

Fires onAction<Name> on every NPC whose shape contains (x, y) — tile units. Serverside handlers run here; clientside handlers run on every client in the level.

NpcLevel.onwall ​

ts
onwall(x: number, y: number, w?: number, h?: number): boolean

True when a blocking collision tile intersects [x, x+w) × [y, y+h), in tile units (fractions allowed). Omit w/h to test the single tile containing (x, y). Tiles outside the level count as blocked. Other blocking NPCs (see dontblock) and players' lower bodies block too; this NPC itself never does. A player's walking footprint is the lower body: onwall(player.x, player.y + 1, 2, 1).

NpcLevel.tiletype ​

ts
tiletype(x: number, y: number): number

Collision type of the tile containing (x, y): 1 walkable, 22 blocking, etc. -1 when out of range.

NpcLevel.shoot ​

ts
shoot(x: number, y: number, gan: string, angle: number, speed: number,
          lifetime: number, data?: any): Projectile | null

Spawns a projectile with its CENTER at (x, y) — this NPC's level-local tiles; when the level is a gmap member the projectile is promoted to gmap space and crosses member seams. gan is the animation it renders (e.g. 'arrow'; the flight angle picks the gani direction). angle is radians, Graal-style: 0=right, PI/2=up, PI=left, 3*PI/2=down. speed is tiles/sec (max 100), lifetime seconds (max 60). data is any JSON-serializable payload, delivered to every callback.

The projectile stops on the first blocking tile (onShotAt for scripts in that level), on lifetime expiry (onShotAt), on an NPC shape (onShot on that NPC, server + client) or on a player (onShot on the hit player's clientside weapon scripts). Returns null on bad arguments.

NOTE: inside NPC scripts always call this.level.shoot — a bare level is the player-scoped global for weapon scripts and returns null here.

NpcLevel.putnpc ​

ts
putnpc(x: number, y: number, serverScript: string, clientScript?: string): NpcThis | null

Spawns a LOCAL NPC at (x, y) — this NPC's level-local tiles. The script arguments are inline TypeScript SOURCE strings (Graal putnpc2-style), compiled through the same pipeline as level NPCs: the first compile of a novel script briefly blocks the server tick; identical sources are cached. Scripts can this.join('<class>') to pull in shared code.

The NPC is never saved to the level file: it lives until destroy() or server restart, surviving level hot reloads (/clearnpcs <level> in RC clears strays). Returns the NPC's full this-style handle — set x/ image/chat etc. on it directly — or null when the level can't be resolved.

NpcThis interface ​

ts
interface NpcThis

this inside a serverside NPC handler: the NPC itself. Writes to its properties are server-authoritative and replicate to everyone in the level; unknown properties are a per-NPC state bag.

NpcThis.id ​

ts
readonly id: number

Runtime id of this NPC (unique per server run).

NpcThis.name ​

ts
readonly name: string

"npc-<index>" (level NPCs) or "localnpc-<id>" (putnpc NPCs).

NpcThis.x ​

ts
x: number

Position in tiles (fractional allowed). Assigning teleports the NPC for everyone and cancels any move(). While a move() runs these read the interpolated position.

NpcThis.y ​

ts
y: number

Position in tiles (fractional allowed). Assigning teleports the NPC for everyone and cancels any move(). While a move() runs these read the interpolated position.

NpcThis.dir ​

ts
dir: number

Facing: 0 up, 1 left, 2 down, 3 right (Graal convention). Values wrap.

NpcThis.move ​

ts
move(dx: number, dy: number, time: number, options?: number): void

Graal's move(dx, dy, time, options): glides the NPC by (dx, dy) tiles over time seconds. Every client animates it smoothly on its own; the position isn't streamed while it moves.

Options is a bitmask (Graal's move flags; combine with |):

ts
1  cache      — queue behind the moves already pending instead of
                replacing them (without it, pending moves are dropped
                and this one starts from the current position)
2  append     — dx/dy are measured from the end of the queued path
                instead of the current position
4  blockcheck — stop at the last clear spot when the NPC would walk
                into a wall (a character NPC's walking footprint, else
                its shape/image); drops the rest of the queue
8  onMovementFinished fires when this move ends (reached or blocked)
16 apply direction — face the move's dominant axis when it starts

time <= 0 moves instantly (on the next update). Assigning x or y cancels every pending move.

NpcThis.image ​

ts
image: string

Asset-relative image, e.g. "images/sign.png". '' = invisible (unless showCharacter).

NpcThis.chat ​

ts
chat: string

Chat bubble text ('' clears it).

NpcThis.head ​

ts
head: string

Appearance when rendered as a character ('' = the gani defaults).

NpcThis.body ​

ts
body: string

Appearance when rendered as a character ('' = the gani defaults).

NpcThis.colors ​

ts
colors: string[]

Body colors as 'r,g,b' strings, like player.colors: [0] skin, [1] coat, [2] sleeves, [3] shoes, [4] belt. Always five entries; defaults to the player defaults. Assigning an index (this.colors[1] = '255,0,0') or an array updates everyone in the level; invalid indices or values are ignored. Only visible after showCharacter().

NpcThis.ani ​

ts
ani: string

Current animation name; only visible after showCharacter().

NpcThis.showCharacter ​

ts
showCharacter(): void

Renders the NPC as a gani character (idle) for everyone in the level.

NpcThis.setAni ​

ts
setAni(name: string): void

Plays an animation on this NPC for everyone in the level.

NpcThis.dontblock ​

ts
dontblock(): void

Graal dontblock(): players and other NPCs walk through this NPC, for everyone in the level. NPCs block by default — a character with its lower body (like walls block players), anything else with its shape or image; an NPC with neither never blocks.

NpcThis.blockagain ​

ts
blockagain(): void

Graal blockagain(): undoes dontblock() (blocking is the default).

NpcThis.blocking ​

ts
readonly blocking: boolean

False after dontblock().

NpcThis.setShape ​

ts
setShape(x: number, y: number, w: number, h: number): void

Sets the triggerAction hitbox: offset and size in PIXELS relative to the NPC's top-left. Without it, the image's pixel size is the shape; no image and no setShape = no shape, so no onAction can land.

NpcThis.level ​

ts
readonly level: NpcLevel

The level this NPC lives in.

NpcThis.join ​

ts
join(name: string): boolean

Joins this script to a class (scripts/classes/<name>.ts): the class's exported handlers fire after this script's own, its exported helpers become callable on this, and its onCreated runs now with this script as this. A serverside join also joins the class's clientside half (<name>.client.ts, when one exists) on every client. Re-joining re-checks the class version and re-fires its onCreated. Returns false when the class doesn't exist.

NpcThis.leave ​

ts
leave(name: string): boolean

Removes a joined class's handlers from this script.

NpcThis.joinedclasses ​

ts
readonly joinedclasses: readonly string[]

Currently joined class names, in join order.

NpcThis.destroy ​

ts
destroy(): boolean

Removes this NPC everywhere — serverside script (timers included) and every client's copy — if it is a LOCAL NPC (level.putnpc). Level-file NPCs can't be destroyed: returns false. Idempotent.

NpcThis.[key] ​

ts
[key: string]: any

Anything else is per-NPC script state, kept between handler calls.