Tongari Boushi shows dialog in two different shapes. Most NPC conversations play in a wide window pinned to the bottom of the dialog screen β the kind you'd expect from any DS RPG. But a lot of the game's chatter also pops up in a small floating bubble: idle one-liners from NPCs you walk past, food-critic verdicts, interaction prompts, and certain talk-topic answers depending on the NPC's personality type. Same engine, same fonts, very different containers. This page explains how the engine decides which one to draw, and why the bubble version has a real ceiling on how much English text it can hold.
What is overlay 157?
Overlay 157 is the dialogue page renderer. Every on-screen text bubble and every bottom-dialog text window the player sees in conversation is built by code inside this overlay β it lays out lines, runs the typewriter animation, picks colors, draws the bubble shape or the window border, and tells the sprite hardware where to put each glyph. A handful of other game systems live inside the same overlay too (most notably the rumor system, the one where you can ask an NPC for gossip about another NPC), but the bulk of what ov157 does, every time a dialog runs, is render text.
Like all overlays, ov157 gets swapped into a shared RAM slot when it's needed and unloaded when it isn't. See the overlay knowledgebase for the general shape of how overlays load. For the byte-level wire format ov157 reads from, see the MSG wire format.
Bubble vs window β how the engine picks
The text itself doesn't choose its container. The bubble-vs-window decision is made entirely by whatever piece of game logic asked for the dialog, one layer above the renderer. The renderer receives a single selector byte from its caller and obeys it:
- Selector = 0 β window. 260 Γ 138 pixels, pinned to the bottom of the screen. Three lines of text per page, roughly thirty characters each.
- Selector = 1 through 4 β bubble. 128 Γ 76 pixels, floating, with a tail pointing at the speaker. Up to two short lines per page.
For village residents: personality decides
Each of the 140 village-resident NPCs (the animal villagers who move in and out of your town) has a personality type stored in the game's NPC data file. There are 12 personality types (6 male, 6 female), and each personality type maps to its own talk-topic overlay β a chunk of game code that handles how that personality's conversations play out.
Two of those 12 personality types have their talk-topic overlays coded to use bubble rendering for their dialog. The other ten use windows. So which shape you see when you chat with a villager depends on who they are β specifically, on their personality type. You can't change it and the text file doesn't control it.
For special characters and objects: separate paths
The game's NPC data file also has sections for special/staff NPCs (the principal, teachers, shopkeepers, mail carriers), supernatural characters (ghosts, gremlins, will-o'-wisps), and legendary creatures (Nessie, Unicorn, Dragon, etc.). These characters don't go through the personality-based overlay dispatch that villagers use β they have their own dialog paths. The rendering mode for their conversations is determined by whichever game system triggers their dialog.
For onemsg floating tips: always bubbles
The data/onemsg/*.ofs files β the short one-liners NPCs
say as you walk past them β always render as floating bubbles. They
enter the engine through a dedicated bubble wrapper that hardcodes
selector = 4. There is no way for an onemsg entry to render as a
window.
Other known bubble paths
| What triggered the dialog | Result |
|---|---|
| Standard NPC conversation (the usual "talk to someone" flow) | Window |
| Talk-topic answers from bubble-personality villagers | Bubble |
| Talk-topic answers from other villagers | Window |
| Interaction prompts (small confirm bubbles) | Bubble |
| Event confirms (e.g. the "you got an item" pop) | Bubble |
| Onemsg floating tips (NPCs you walk past) | Bubble |
| Food-critic verdicts (the reveal bubble at the end of judging) | Bubble |
The 32-character bubble cap
The window path has plenty of room β three lines of roughly thirty characters each. The bubble path is the tight one, and the bubble cap turned out to be one of those translation-side traps that's easy to miss until your text starts clipping.
Empirically, a single bubble page fits about 32 visible characters, across up to two short lines. We derived that number two different ways and they landed on the same answer:
From the data: 15,083 onemsg phrase segments
The data/onemsg/*.ofs files hold all the floating-tip
bubble text (the short things NPCs say as you walk past them). When
we scanned every visible-character length across all 5,749 entries
in those files β 15,083 distinct bubble-page segments after splitting
on page breaks β the distribution had a hard cliff:
length (chars) count
0 - 4 6095
5 - 9 1506
10 - 14 2010
15 - 19 2342
20 - 24 1972
25 - 29 1033
30 - 34 125
35+ 0 Zero segments above 33 characters. Across more than fifteen thousand samples. That's the kind of distribution you get when the people who wrote the original Japanese were sizing to a real engine ceiling, not just a stylistic preference.
From the engine: the per-style record at 0x021c336e
Independently, we read the renderer's own style record out of ov157. The bubble's inner text-bounding-box width is 76 pixels (the window's is 117), and the font is a 16-pixel proportional font that averages about 4.5 pixels per character for typical mixed lowercase Latin prose. Two lines per page. That works out to:
76 px width Γ· ~4.5 px per char β 16-17 chars per line
2 lines per page β 32-34 chars per page
Which lines up exactly with the empirical cliff. Bubble pages with
a lot of wide glyphs (heavy on m, w,
W, capital letters) will clip a bit sooner, around 28
chars; pages full of thin glyphs (i, l,
punctuation) can squeeze closer to 36 before they bind. For
everyday English we treat 32 characters per page as
the safe practical cap, with the 28-32 range flagged as a soft
warning zone for wide-glyph content.
Why this matters for players
If you spot a bubble where text gets cut off, runs off the bubble border, or simply ends mid-word with no continuation β that's almost certainly a translation overflow against this 32-character cap. The English version of a line that fit comfortably in Japanese can easily blow past the limit, because Japanese packs more visible meaning per glyph than English does.
These cases are real, fixable bugs, and the fix is to rewrite the English line to fit. If you see one, please drop a note via the bug-reports board, ideally with the location (what NPC, what activity, what they were talking about) and a screenshot or short description of what was clipped. The bubble-cap discovery directly drove a sweep of overflow fixes in the v2.4.x patch line, and players catching ones we missed is how the long tail gets cleaned up.
Inside ov157: the renderer's architecture
Overlay 157 was fully semantically decoded β all 251 functions named
and classified. The dialogue renderer portion uses a 660-byte
(0x294) instance struct, allocated by
dialogue_renderer_ctor() at 0x021c0e3c.
The canonical entry point is dialogue_renderer_init_canonical()
at 0x021c0ef8, called from the ARM9 universal dialog
parser at FUN_02066c58. The fourth parameter is the
bubble selector byte described above.
Per-style records at 0x021c336e
Two 31-byte (0x1f) records describe the visual parameters
for each rendering mode. The renderer indexes these by the bubble
selector byte:
| Field | Record 0 (window) | Record 1 (bubble) |
|---|---|---|
| Line height | 6 px | 5 px |
| Max lines per page | 1 (per rendered page segment) | 2 |
| Text color (RGB) | (216, 216, 249) | (228, 204, 249) |
| Drop shadow color | (100, 100, —) | — |
| Origin offset (x, y) | (0, 0) | (+56, +80) |
| Position (x, y) | (0x104, 0x8a) | (0x80, 0x4c) |
Instance struct key offsets
| Offset | Field | Role |
|---|---|---|
+0x00 | vptr | Points to vtable at 0x021c33b4 (7 slots) |
+0x24 | line_array_ptr | Glyph buffer for the current page |
+0x2d | state_byte | 9-state per-frame dispatcher |
+0x42 | pos_x | 0x104 (window) or 0x80 (bubble) |
+0x44 | pos_y | 0x8a (window) or 0x4c (bubble) |
+0x4b | bubble_sel | THE selector byte. Read/written 33 times across the overlay. Indexes all style tables. |
+0x84 | choice_widget | Sub-renderable for multi-choice prompts |
+0x108 | tag_widget | Sub-renderable for speaker name tags |
Key functions
| Address | Function | Role |
|---|---|---|
0x021c0e3c | dialogue_renderer_ctor() | Allocates the 660-byte instance |
0x021c0ef8 | dialogue_renderer_init_canonical() | Canonical entry β param_4 is bubble_sel |
0x021c0f48 | dialogue_renderer_init_method() | Vtable slot 2 β populates instance, calls layout pipeline |
0x021c115c | dialogue_runtime_step() | Vtable slot 3 β per-frame 9-state machine |
0x021c144c | dialogue_finalize_frame() | Vtable slot 4 β per-frame blit |
The ov157 detail page in the per-overlay catalog has the full function listing. The rumor (uwasa) system that shares this overlay has its own set of 8 state machines handling NPC reputation, gossip listening, and save-buffer writes.
Related
- Message system overview β wire format, opcode catalog, shared payloads.
- MSG wire format β the byte stream ov157 reads from.
- Overlay knowledgebase β how overlays load and swap in the first place.
- Per-overlay catalog β the full list, including ov157's detail page.