# Adding a talkable NPC An NPC/sign is just an immovable `Entity` that mixes in `Dialogable`. Bumping it (a rejected move) opens the dialogue box. Talking is the same everywhere — you only supply **art** + **a story**. ## 1. Write the story Create `stories/.ink` — a line, a few choices, an ending: ```ink yo wassup * kick the sign it doesnt budge. * [say nothing] you have nothing to say to a sign. - -> END ``` ## 2. Compile it The game can't parse `.ink` (LÖVE has no lpeg), so compile to a `.lua` book: ```sh lua tools/compile_ink.lua ``` Re-run this **every time you edit the `.ink`** — the game loads the `.lua`, not the `.ink`. (Needs `lua` + `lua-lpeg` installed.) ## 3. Load the book In `main.lua`, next to `startSignBook`: ```lua Book = require "stories." ``` ## 4. Make the entity In `npc.lua`, copy `Sign` — pick your art and name your book. Include `Walker` too (it's what lets the NPC path to a Mark; see "Making an NPC walk" below), and call `self:initWalker()` after `Entity.initialize`: ```lua Oracle = class("Oracle", Entity) Oracle:include(Dialogable) Oracle:include(Walker) function Oracle:initialize(x, y) self.book = oracleBook -- from step 3 Entity.initialize(self, { x = x, y = y, color = "red", collision = "unmoveable", sprites = { red = loadSprite("art/npc/oracle_r.png"), green = loadSprite("art/npc/oracle_g.png"), blue = loadSprite("art/npc/oracle_b.png"), }, }) self:initWalker() end ``` Per-channel art lives in `art/npc/` as `_r/_g/_b.png` (16px cells). ## 5. Place it in a room Map a save class to your entity in `Room:createEntity` (`room.lua`), alongside the `npc` → `Sign` branch: ```lua if class == "oracle" then local entity = Oracle:new(x, y) self:registerNpc(entity) return entity end ``` Then add it to a room's `rooms/.sav` `objects` list: ```lua { class = "oracle", x = 4, y = 8 } ``` That's it — walk into it and it talks. NPCs are stamped into every colour's collision matrix, so they block movement and obey the world's colour rules (a red-only player reads them in red). # Marks — named destinations A **Mark** is a debug non-collidable: an editor-only annotation the game reads back. It carries a globally-unique name, and it's the only thing an NPC can be sent to. It is *not* a Note — it just shares the debug overlay toggle. In the editor, click the **target** tool at the bottom of the panel, then click a cell to drop one and type its name (colliding names auto-suffix `_2`, `_3`, …). Left-click an existing mark to rename it; right-click to delete. Marks show only under `DEBUG` while the overlay is on (`Tab`). You can also create marks from code so they exist without living in a `.sav`: ```lua room:addMark("gate", 5, 9) -- registers + renders in debug; skipped by serialize ``` # Making an NPC walk Any NPC that includes `Walker` (see step 4) can be sent to a Mark. From Ink, call the bound external function — e.g. to leave once the conversation ends: ```ink Oracle: The way is open now. Go. ~ walk_to("gate") -> END ``` `walk_to(name)` is wired for every Dialogable (`dialogable.lua`); it resolves the Mark by name and calls `npc:sendTo`. Under the hood the NPC becomes the player's RGB model — up to three colour copies, each running its **own** A* on its own collision layer (`pathfinding.lua`), stepping in real time and pathing across room seams over the world grid. Copies split apart and additively re-merge to white on arrival. Pathfinding is footprint-aware, so a 2×2 NPC routes its whole body around obstacles rather than threading a 1-wide gap. ## Testing without a story You don't need an Ink book to try it: in the editor (`DEBUG`), **left-click an NPC** to open the destination picker — a filterable list of every Mark. Type to filter, click a row (or Enter for the top match) to send it; Esc cancels.