Bubble vs window β€” the dialogue page renderer

DS game only γ¨γ‚“γŒγ‚Šγƒœγ‚¦γ‚·γ¨γŠγ—γ‚ƒγ‚Œγͺまほう぀かい The field guide documents the DS game's internals.

Why some lines pop up in a floating bubble and others fill the bottom dialog box, and the empirical 32-character-per-page cap that bubble text has to fit inside.

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:

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 dialogResult
Standard NPC conversation (the usual "talk to someone" flow)Window
Talk-topic answers from bubble-personality villagersBubble
Talk-topic answers from other villagersWindow
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:

FieldRecord 0 (window)Record 1 (bubble)
Line height6 px5 px
Max lines per page1 (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

OffsetFieldRole
+0x00vptrPoints to vtable at 0x021c33b4 (7 slots)
+0x24line_array_ptrGlyph buffer for the current page
+0x2dstate_byte9-state per-frame dispatcher
+0x42pos_x0x104 (window) or 0x80 (bubble)
+0x44pos_y0x8a (window) or 0x4c (bubble)
+0x4bbubble_selTHE selector byte. Read/written 33 times across the overlay. Indexes all style tables.
+0x84choice_widgetSub-renderable for multi-choice prompts
+0x108tag_widgetSub-renderable for speaker name tags

Key functions

AddressFunctionRole
0x021c0e3cdialogue_renderer_ctor()Allocates the 660-byte instance
0x021c0ef8dialogue_renderer_init_canonical()Canonical entry β€” param_4 is bubble_sel
0x021c0f48dialogue_renderer_init_method()Vtable slot 2 β€” populates instance, calls layout pipeline
0x021c115cdialogue_runtime_step()Vtable slot 3 β€” per-frame 9-state machine
0x021c144cdialogue_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