Move RPG-Maker-style talking events onto NPCs as native conversations — dry run first, then apply.
Converting Events to Conversations
Imported RPG Maker maps keep their townspeople as map events: pages of commands that show messages, offer choices, hand out items and start quests. The converter turns those events into native NPC Conversations — nodes, choices, conditions and actions an NPC runs itself — so the talking characters no longer depend on the event system.
Everything here is in Database → NPCs → From events.
Convert a world
1. Open Database → NPCs and pick the From events section. 2. Leave Zone on Every zone (or pick one map), then click Run dry run. Nothing is written. 3. Read the totals: how many events talk, how many convert cleanly, how many are partial, too large or blocked, and how many would create a new NPC. 4. Open What can't come across to see every behavior the conversations can't carry, counted across the world, with the reason. 5. Filter the list (Ready to apply, Partial, Blocked…) and open an event's Details: why it would be skipped, notes, what wasn't carried, ordering warnings, quests it touches. Show converted nodes prints the exact conversation it would write. 6. Tick the events you're happy with (or Select ready), then Apply selected. 7. Check a few NPCs in Directory → (NPC) → Conversation and in game, then delete (or unbind) the converted events — that is a separate step on purpose.
Applying never deletes, moves or unbinds an event.
Where each conversation goes
| The event is… | The conversation is written to… |
|---|---|
| Bound to an NPC (its Bound NPC is set) | That NPC. |
| A character standing on or next to an NPC in the same zone | The nearest such NPC. |
| A character on its own | A new NPC at the event's tile, named after the event with RPG Maker tags removed (Guard <enemy: 20> becomes Guard), wearing the event's sprite, face and — for an inline shop — its goods. |
An object — no graphic, a tile graphic, or an RPG Maker object sheet whose name starts with ! (doors, chests, crystals, levers, job boards) | Nothing. It stays an event and is listed as Object — stays an event. A door beside a guard never lands in the guard's conversation. |
Objects are left alone because a door, a chest or a sign isn't a person: turning one into an NPC, or merging its "This door is locked" into whoever stands next to it, would put the wrong words in the wrong mouth. An object that is bound to an NPC still converts onto that NPC — binding it was your call. The dry-run script's --objects flag converts objects too, if you really want that.
Several events on one NPC
An NPC has one conversation, so when several events land on the same NPC — a townsperson's own event plus another character event standing beside them, or the same merchant event copied into every town — they are merged into one conversation instead of blocking each other.
1. The events are put in order: the event bound to the NPC first (the one in the NPC's own zone before the others), then unbound events by how close they stand, then map order.
2. Each event keeps its own page order, so within an event the highest page still wins. Across events, the first event that has a page for the player's situation answers.
3. Node ids get a prefix per event — e1_, e2_… — and opening nodes are named after their event (Mini Job · Page 2), so you can tell them apart in Directory → (NPC) → Conversation.
4. The first event's row writes the merged conversation; the others show Merge: part of one conversation. Ticking any of them ticks the whole merge — they are applied together.
| What the merge finds | What happens |
|---|---|
| An event that converts to the same conversation as one already in (only its own self switches differ) | Covered by that copy, noted as a duplicate. Progress players kept on the duplicate's own self switches doesn't carry over. |
| An event whose every page an earlier page beats (for example the bound event's page has no conditions) | Left out — its pages could never open. Its row says which page answers first. |
| An event with an inline shop that sells a different goods list | Left out — an NPC has one shop of its own. |
| An event that would push the merged conversation past the limits (64 nodes) | Left out and named; the rest still merge. With nothing that fits beside it, the bound event keeps exactly its own conversation. |
Left-out events are blocked, never cut short. Convert them onto their own NPC, or rework them by hand.
Status
| Status | Meaning |
|---|---|
| Converted | Everything that matters came across. |
| Partial | A conversation was made, but something couldn't be carried — see the event's details. Script commands the engine never ran don't count against it. |
| Too large | It would need more than 64 nodes, 12 choices on a node, 16 actions or 12 conditions. Nothing is written — it's never cut short. |
| Failed | Nothing usable came out (for example every talking page has a condition the conversation can't check). |
| Doesn't talk | No page shows a message or a choice — a door that only transfers, a chest, a cutscene. Left alone. |
An event is blocked (not applied) when it doesn't convert, when its bound NPC no longer exists, when a merge leaves it out, or when the NPC already has a conversation. Tick Replace NPCs' existing conversations to overwrite those.
How events become conversations
| In the event | In the conversation |
|---|---|
| Pages, highest wins | Opening nodes in reverse order, each with the page's conditions, so the conversation opens on the page RPG Maker would have run. |
| A page that can't answer a talk (autorun, parallel, empty) | Lower pages get the opposite of its conditions, so they still stay quiet while it would have run. |
| A talk page that shows nothing (it only flips a self switch, only opens the shop) | An opening node with no text whose one "(Continue)" carries the actions. Talking runs them, then shows the NPC's greeting and services if it has any (no window if it has none, or if the page opened the shop) — see Steps with no text run by themselves under Gotchas. |
| Consecutive Show Text | The pages of one node. RPG Maker codes are removed; \N[1] becomes {playerName}. A message spoken by someone else keeps its name as that page's speaker. |
| Show Choices | Choices on that node, each leading to its own follow-up node. |
| Cancel branch | Left out when it does nothing — closing the window is the cancel. When it has text or actions it becomes a Cancel choice. |
| Conditional Branch | Two opposite gates: after text, two "(Continue)" choices of which the player only ever sees one; at the start of a page, two opening nodes; at the start of a choice, the choice twice, each gated. A condition the conversation can't check counts as false — the else side is kept, and it is reported. |
| Label | Starts its own node. Text before it leads there with "(Continue)"; a label right before Show Choices becomes a menu node named after the label — it has no text of its own, so its choices are shown under the NPC's greeting. |
| Jump to Label | A link to the label's node: the choice's own next when the jump ends a choice's branch, a "(Continue)" choice when it follows text. A jump back (the "Anything else?" menu) loops; a jump forward skips the text in between. Anything the label's branch does next — the rest of the branch, then what follows the choices — comes along, as in RPG Maker. |
| Control Self Switch | Set flag ss:<zone>:<event>:<letter> — the event's own self switch, so players keep their progress. |
| Control Switches | Set flag on that switch number (player switches only). |
| Control Variables (set / add / subtract a number) | Set variable / Add to variable (player variables only). |
| Change Items / Change Gold | Give / take item, give / take silver. |
| Transfer Player | Teleport, on a "(Continue)" choice so the text is read first. |
| Start / advance / complete quest | The matching quest actions. |
| Open Shop | Open shop (an authored shop, or the NPC's own goods). The shop ends the conversation, so a message RPG Maker showed after the shop closed ("Come again!") is skipped. |
| Change State (add) / Apply Status Effect | Apply status effect. |
| Call Common Event | The common event's commands, inlined. |
| Sounds, waits, balloons, animations, fades, the event's own move routes, comments | Dropped as cosmetic (counted in the report). |
Gotchas
- The bound event still answers first. Talking to an NPC runs a bound event instead of its conversation until you delete or unbind the event. For an unbound event, the new NPC and the event stand on the same tile until you delete the event.
- Actions after a message run as its node opens. A reward handed over after a line of text is given when that text appears, so walking away can't skip the flag that stops it being handed out twice. Teleports and shops wait for "(Continue)". The report lists every node where this happened. When that action is the page's own Control Self Switch — the page is gated on the switch being OFF — the node's choices still work after it flips: a node's conditions are checked when it opens, not on every click.
- Steps with no text run by themselves. Many converted nodes have no text: a page that only sets its self switch or opens the shop, a gate whose single visible "(Continue)" leads into the real text, a menu label in front of Show Choices. The server runs each one and moves on without showing anything — actions and all, checked exactly as a click is — so the player goes straight from one line of text to the next, as in RPG Maker. It only stops at text, at a menu with two or more choices (shown under the NPC's greeting), or when the path ends. When a talk ends without having shown anything, a trainer, merchant or quest giver still answers with its greeting and services; a silent page that opens the shop leaves the shop open. See NPC Conversations → Steps with no text.
- Take item and take silver refuse the whole step when the player can't pay. RPG Maker took whatever the player had.
- Only the player's own switches and variables come across. A page or branch testing a global switch or variable is left out and reported — it is never widened to "always".
\V[n]can't be shown. Pages can't print a variable's value, so the line keeps the placeholder{var:n}and is reported — reword it.- Loop commands run once. A Loop body is walked a single time and reported. Menus built from Label + Jump to Label do loop.
- A menu that loops is capped at 40 steps. Every node the player enters counts, so a guard's "ask me anything" menu allows about twenty questions in one conversation before the server closes it with "That conversation has gone in circles." Talking again starts fresh.
- Actions on a looped node run once per conversation. A node's on-open actions fire the first time it opens in a conversation, not on every pass — RPG Maker re-ran them. The report says when a loop has such a node.
- A jump to a label that isn't there is skipped — RPG Maker and this engine both carry on past it — and reported, since it usually means a label was renamed or deleted. A jump that runs straight back into its own label before anything shows would spin forever, so that path ends there and is reported.
- Repeated menus are stored once. When several pages share the same menu (the same questions after a different greeting), the converted conversation keeps one copy of it.
- Scripts were already dead. Script commands and script conditions never ran in this engine; they are listed but don't make an event partial.
- No feature flag. The converter is an editor tool; the conversations it writes follow the NPC Conversations rules.
Dry run from a script
To check a whole project without opening the editor, run node scripts/event-conversation-dry-run.mjs with DATABASE_URL set (add --schema=<project schema> for a hosted project, or --pglite=<data dir> for a local one). It only reads, and prints the same totals — add --rows for one line per event, --event=<zone id>:<event id> for that event's nodes, or --out=report.json for everything.
With AI
event-conversation-preview runs the dry run and returns the same rows and totals. event-conversation-apply writes a selection — pass each event with the expectedTarget the preview gave it, and it skips any whose NPC has changed since. Both go through the same checks as the editor.
A merged event's row carries merge: its role (primary, merged, duplicate or not_merged), the primaryKey that writes it, and the members to select together. Apply every member; the primary comes back updated and the rest merged.