Loading the Kaya editor…
// The Lantern Market: a visual novel with a face. visual-novel.html draws the
// stage; this program owns the story. Press Space in Preview (or tap
// Continue) to advance, then choose what the old lantern-seller carries for
// you. Each advance is answered by the next line, each choice by its ending.
//
// faces.json declares the face: it reads vn.scene, vn.line, vn.choices and
// vn.end, and sends vn.advance and vn.choice {index, text}. No model is called.

mount(component: "visual-novel", layout: "full")

"🏮 The Lantern Market. Press Space in the Preview to advance, and choose when asked."

// The market at dusk, and an old seller who has been watching you for years.
emitEvent(eventType: "vn.scene", data: {location: "The floating-lantern market, dusk over the river", tone: "dusk"})
emitEvent(eventType: "vn.line", data: {speaker: "Old Wen", text: "Three years you've come to my stall and never once bought a lantern. Tonight, I think, you finally will.", emotion: "thinking"})
awaitEvent(eventType: "vn.advance")

// The offer: not paper and wax, but a trade.
emitEvent(eventType: "vn.scene", data: {location: "Wen's stall, as the last boats push off", tone: "dusk"})
emitEvent(eventType: "vn.line", data: {speaker: "Old Wen", text: "I don't sell paper and wax, child. I trade memories: one lantern, one memory, sent downriver to whoever you've lost. It comes back changed, mind. A little kinder. A little more honest. Never quite the one you sent.", emotion: "default"})
awaitEvent(eventType: "vn.advance")

// The question, and the choice.
emitEvent(eventType: "vn.line", data: {speaker: "Old Wen", text: "So tell me: whose face do you want the river to carry tonight?", emotion: "surprised"})
emitEvent(eventType: "vn.choices", data: {prompt: "Choose", options: ["Light one for my sister, and ask her to forgive me", "Light one for the boy I loved, and ask nothing at all", "Keep the coin, and light no lantern tonight"]})

// Any session client can post vn.choice straight to the event door, past the
// face's schema, so anything but the first two options keeps the coin.
let pick = awaitEvent(eventType: "vn.choice")
let idx = pick.content[0].data?.index ?? -1
emitEvent(eventType: "vn.choices", data: {prompt: "", options: []})

if idx == 0 {
  emitEvent(eventType: "vn.scene", data: {location: "Downriver, where the lanterns gather at the bend", tone: "dusk"})
  emitEvent(eventType: "vn.line", data: {speaker: "Old Wen", text: "There, past the bend: a small light flares brighter than the rest, as if it heard you. Forgiveness, it turns out, travels faster than guilt ever did.", emotion: "happy"})
} else if idx == 1 {
  emitEvent(eventType: "vn.scene", data: {location: "The riverbank, lanterns drifting past in silence", tone: "night"})
  emitEvent(eventType: "vn.line", data: {speaker: "Old Wen", text: "Your lantern catches a gust, spins twice, and settles facing back toward you instead of away. Some memories don't want to be sent. They only want to be witnessed once more, and then let go.", emotion: "sad"})
} else {
  emitEvent(eventType: "vn.line", data: {speaker: "Old Wen", text: "He nods slowly and pockets the coin anyway. 'Not every year has to be the year,' he says. 'The river keeps flowing. So, apparently, does your coin.'", emotion: "thinking"})
}

emitEvent(eventType: "vn.end", data: {title: "The end"})
"The end."