Guide
One socket, one feed out, two commands in. About two hours for a first character if your engine can already move an actor to a point and make it say a line.
lw dev runs the brain. Your engine connects to ws://localhost:8765 and exchanges JSON messages, one per line. Engines that cannot hold a socket (Papyrus) use the HTTP-shim pattern instead: see Wire.
{ "type": "state", "tab": "my-game", "world": "my-game",
"player": { "x": 0.0, "z": 0.0, "armed": false },
"npcs": [ { "i": 0, "name": "Hulda", "x": 8.0, "z": 4.0, "handle": 1000, "armed": false, "activeExpert": "idle" } ] }
world must equal the manifest's world. i is the slot commands come back on; handle is your stable id so renames never lose a mind. Optional fields (place tags, identity, objects) are in Wire.
Move: loadScenario names an engine action in modes[].expert and a target. Keep executing it until the next one for that slot, and report it back as activeExpert.
{ "cmd": "loadScenario", "npc": 0, "name": "Hulda", "handle": 1000,
"target": "guard", "modes": [ { "cond": "default", "expert": "navigate-to", "target": "guard" } ] }
Speak: say carries a line already decided; render it, do not filter it.
{ "cmd": "say", "npc": 0, "name": "Hulda", "text": "Guard. I've been robbed." }
| engine action | your engine does |
|---|---|
| approach | walk to speaking distance of the target, face it |
| navigate-to | walk to the target or point, stop |
| follow | stay within a few units as the target moves |
| guard | hold position, keep eyes on the target |
| evade | move away fast |
| retreat | back off a few steps, hold |
| confront | close to arm's length, square up, hold |
| say | speak the line, no movement |
That is every reflex and every shipped plan. sit, eat, lean, lead are bodies you add when a level lists the verb.
python -m pipeline.verb_manifest --check lw.manifest.json --executes approach,navigate-to,follow,guard,evade,retreat,confront,say
One line back: every verb executable, or CANNOT execute here: sit (sit). Bind the action; never edit the brain.
living-world-web3/src/main.js opens the socket to ws://localhost:8765, posts the feed from the scene, and executes loadScenario and say on the meshes. It is the client the mock town's browser build uses.pipeline/skyrim_bridge.py, the HTTP-shim pattern for an engine with no sockets.