Alchemy
Loading...
Searching...
No Matches

typing.c

Go to the documentation of this file.
1/// @file
2/// The per-frame message update: typing text and running its control codes.
3/// Part of window.c, the window object, compiled only through it.
4
6
7// The battle message canvas: 30 columns of 4 tiles. In a column each word is
8// one 8-pixel row of 4bpp pixels, so a column is 32 pixel rows of one word.
9#define CANVAS_COLUMNS 30
10#define CANVAS_COLUMN_WORDS 32
11#define CANVAS_KEPT_ROWS 8 ///< the top 8 pixel rows stay put
12
13/// Scrolls the battle message canvas up by 6 pixel rows per step, below its
14/// top 8 rows, and clears the rows this uncovers.
16 s32 shift = step * 6;
17 u32 *base = UI_CANVAS_VRAM;
18 s32 n;
19
20 for (n = 0; n < CANVAS_COLUMNS; n++) {
21 u32 *column = base + n * CANVAS_COLUMN_WORDS;
22
23 Dma_Set(column + CANVAS_KEPT_ROWS + shift, column + CANVAS_KEPT_ROWS,
25 REG_DMA3);
26 Iwram_FillWords(column + (CANVAS_COLUMN_WORDS - shift), shift * 4, 0);
27 }
28}
29
30/// What UiWork_StepChannelScript reports.
31enum {
32 CHANNEL_STEP_RUNNING = 0, ///< the text goes on next frame
33 CHANNEL_STEP_ENDED = 8, ///< the text has ended; the window stays open
34 CHANNEL_STEP_CLOSE = 9, ///< the text is dismissed: close the window
35};
36
37/// The per-frame message update: each message channel whose window is fully
38/// open runs its window effect or types more text, and on the text's end marks
39/// the window done (state 1) or closes it.
41 struct UiChannelSlot *channel = UI_WORK->channels;
42 s32 channelNo = 0;
43
44 do {
45 struct UiWindow *current = channel->work;
46
47 if (current != NULL && current->frame == 0 && current->duration == 0) {
48 u16 flags = current->flags;
49
50 if (flags == 0) {
51 channel->work = NULL;
52 } else {
53 s32 pending = current->effect;
54 s32 step;
55
56 if (pending != 0) {
58 } else {
59 step = UiWork_StepChannelScript(channel);
60 switch (step) {
62 channel->work->state = 1;
63 break;
64 case CHANNEL_STEP_CLOSE: {
65 struct UiWindow *window = channel->work;
66
67 UiWork_Finalize(window, window->flags & UI_WINDOW_INSTANT);
68 // pending is 0 here: it clears the channel.
69 channel->x = pending;
70 channel->y = pending;
71 channel->entry = pending;
72 channel->countdown = pending;
73 channel->colour = pending;
74 channel->outline = pending;
75 channel->lineSpacing = pending;
76 channel->work->state = 1;
77 break;
78 }
79 }
80 }
81 }
82 }
83 channelNo++;
84 channel++;
85 } while (channelNo != 3);
86}
87
88/// Types one frame's share of a channel's text: runs control codes (new line,
89/// new page, pauses, colours) and draws characters, playing the speaker's voice
90/// blips. Returns a CHANNEL_STEP_ result.
92 struct UiRenderWork *work = UI_WORK;
93 // Preserve the native's initial volatile sample, separate from later reads.
94 u32 keys = gKeysHeld;
96 s32 lag;
97 u32 code;
98 u32 nextCode;
99 struct UiWindow *window;
100 s32 x;
101 s32 y;
102 u32 newWidth;
103 u32 newHeight;
104 s32 oldWidth;
105 s32 oldHeight;
106 s32 entryIndex;
107 s32 nextEntryIndex;
108 s32 pixelX;
109 s32 pixelY;
110 s32 voiceGap;
111 s32 glyphWidth;
112 s32 glyphAdvance;
113
114 (void)keys;
115
116 // In battle, text runs at 3 glyphs a frame, and 5 more for each frame the
117 // game is running behind (up to two).
118 if (work->menuState != 0) {
119 lag = gLagFramesShown;
120 CLAMP(lag, 0, 2);
121 glyphsLeft = lag * 5 + 3;
122 }
123
124 // Finish scrolling the canvas up a line before any more text.
125 if (channel->transition != 0) {
127 channel->transition = (s16)channel->transition - 1;
129 }
130 // With no button held, wait out the delay after the last glyph.
131 if (gKeysHeld == 0 && channel->glyphDelay != 0) {
132 channel->glyphDelay--;
134 }
135
136 // Run control codes and draw characters until this frame's share is done.
137 do {
138 code = TEXT_END;
139 if (channel->ended == 0)
140 code = work->entries[channel->entry];
141
142 if (code < TEXT_COMMANDS) {
143 switch (code) {
144 case TEXT_NEWLINE:
145 // Canvas lines are 13 pixels apart, and from the second line on the
146 // canvas scrolls up instead; other windows' lines are 15 apart.
147 channel->x = channel->startX;
148 if (channel->work->flags & UI_WINDOW_TEXT_CANVAS) {
149 if (channel->y >= 13 << 8) {
151 glyphsLeft = 1;
152 } else {
153 channel->y += 13 << 8;
154 }
155 } else {
156 channel->y += 15 << 8;
157 if (channel->line <= 2)
158 channel->line++;
159 }
160 break;
161
162 case TEXT_PAGE:
163 if (work->mode != 0 && channel->countdown < 900)
165 channel->countdown = 919;
166 if (UiText_CheckTextDismissed(channel) == 0) {
167 // Until the button: show the next-page arrow in the bottom-right
168 // corner.
169 struct UiWindow *pane = channel->work;
170
171#if TBS_PATCH_1
172 if (pane->width == 0 || pane->height == 0 ||
173 work->pageArrowShown != 0)
174 break;
175#else
176 if (work->pageArrowShown != 0)
177 break;
178#endif
179 UiText_DrawGlyph(pane, 1, pane->width * 4 - 8, pane->height * 8 - 16,
180 1);
181 work->pageArrowShown = 1;
182 break;
183 }
184 // Clear the window, and refit it to the next page's text.
185 window = channel->work;
186 x = window->x;
187 y = window->y;
188 oldWidth = window->width;
189 oldHeight = window->height;
190 entryIndex = channel->entry;
191 work->pageArrowShown = 0;
193 if (channel->flags == 0 && (window->width | window->height) != 0)
194 UiWindow_EraseBorderRect(window->x, window->y, window->width,
195 window->height);
196 entryIndex = (entryIndex + 1) & RENDER_ENTRY_MASK;
197 if (work->entries[entryIndex] != 0 &&
198 (window->width | window->height) != 0) {
199 if (channel->flags != 0) {
200 UiWindow_EraseBorderRect(window->x, window->y, window->width,
201 window->height);
202 } else {
203 UiWindow_FitOnScreen(entryIndex, &x, &y, &newWidth, &newHeight,
204 channel->spacing, 0);
205 if (window->flags & UI_WINDOW_ANCHOR_BOTTOM) {
206 if (oldHeight != newHeight)
207 y -= newHeight - oldHeight;
208 if (y < 0)
209 y = 0;
210 }
211 if (!(window->flags & UI_WINDOW_FIXED_X)) {
212 // Keep the window centred where it was.
213 x += (s32)(oldWidth - newWidth) / 4;
214 UiWindow_FitOnScreen(entryIndex, &x, &y, &newWidth, &newHeight,
215 channel->spacing, 2);
216 }
217 window->x = x;
218 window->y = y;
219 window->width = newWidth;
220 window->height = newHeight;
221 }
222 UiWindow_DrawFrame(window->x, window->y, window->width,
223 window->height);
224 }
225 channel->x = channel->startX;
226 channel->y = 0;
227 channel->line = 0;
228#if TBS_PATCH_3
229 channel->colour = 15;
230 channel->outline = 0;
231 channel->lineSpacing = 10;
233#endif
236 break;
237
238 case TEXT_WAIT_KEY:
239 if (work->mode != 0 && channel->countdown < 900)
241 if (UiText_CheckTextDismissed(channel) != 0)
242 return CHANNEL_STEP_CLOSE;
243 channel->countdown = 919;
244 break;
245
246 case TEXT_PAUSE_SHORT:
247 if (channel->countdown == 0)
248 channel->countdown = 20;
249 work->speakerVoiceCountdown = 0;
251 break;
252
254 if (channel->countdown == 0)
255 channel->countdown = 120;
256 work->speakerVoiceCountdown = 0;
258 break;
259
260 case TEXT_PAUSE_LONG:
261 if (channel->countdown == 0)
262 channel->countdown = 60;
263 work->speakerVoiceCountdown = 0;
264 break;
265
266 case TEXT_COLOUR:
267 channel->entry = (channel->entry + 1) & RENDER_ENTRY_MASK;
268 channel->colour = work->entries[channel->entry];
270 break;
271
272 case TEXT_OUTLINE:
273 channel->entry = (channel->entry + 1) & RENDER_ENTRY_MASK;
274 channel->outline = work->entries[channel->entry];
276 break;
277
279 channel->entry = (channel->entry + 1) & RENDER_ENTRY_MASK;
280 channel->lineSpacing = work->entries[channel->entry];
282 break;
283
285 channel->colour = 15;
286 channel->outline = 0;
287 channel->lineSpacing = 10;
289 break;
290
292 channel->entry = (channel->entry + 1) & RENDER_ENTRY_MASK;
293 channel->work->effect = work->entries[channel->entry];
294 channel->countdown = 10;
295 channel->entry = (channel->entry + 1) & RENDER_ENTRY_MASK;
296 break;
297
298 case TEXT_END:
299 case TEXT_END_ALT:
300 channel->ended = 1;
301 return CHANNEL_STEP_ENDED;
302 }
303
304 if (work->menuState == 0)
305 glyphsLeft = 1;
306 } else {
307 // A character: draw it at the pen position, rounded to whole pixels.
308 pixelX = (channel->x + 128) / 256;
309 pixelY = (channel->y + 128) / 256;
311 nextEntryIndex = channel->entry;
312#if (LANGUAGE == LANGUAGE_JAPANESE) || (LANGUAGE == LANGUAGE_ENGLISH)
313 if (work->mode != 0)
314 pixelX += 8;
315#endif
316 nextEntryIndex = (nextEntryIndex + 1) & RENDER_ENTRY_MASK;
317 nextCode = work->entries[nextEntryIndex];
318#if (LANGUAGE == LANGUAGE_JAPANESE) || (LANGUAGE == LANGUAGE_ENGLISH)
319 // A kana mark after the character is drawn with it.
320 if (nextCode == TEXT_VOICED_MARK) {
321 code |= TEXT_CODE_VOICED;
322 channel->entry = (channel->entry + 1) & RENDER_ENTRY_MASK;
323 } else if (nextCode == TEXT_SEMIVOICED_MARK) {
324 code |= TEXT_CODE_SEMIVOICED;
325 channel->entry = (channel->entry + 1) & RENDER_ENTRY_MASK;
326 }
327#endif
328#if (LANGUAGE != LANGUAGE_JAPANESE)
329 // Two narrow characters that fit in one 16-pixel cell together are drawn
330 // as one pair, the second in the code's high byte.
331#if (LANGUAGE == LANGUAGE_SPANISH)
332 if (!(channel->work->flags & UI_WINDOW_TEXT_CANVAS) &&
333 code > TEXT_SPACE && nextCode > TEXT_SPACE) {
334 s16 pair = gUiText_Glyphs[code - TEXT_SPACE].width +
335 gUiText_Glyphs[nextCode - TEXT_SPACE].width;
336
337 if (work->outline == 1) {
338 if ((u16)pair <= 14) {
339 code |= nextCode << 8;
340 channel->entry = (channel->entry + 1) & RENDER_ENTRY_MASK;
341 }
342 } else if ((u16)pair <= 15) {
343 code |= nextCode << 8;
344 channel->entry = (channel->entry + 1) & RENDER_ENTRY_MASK;
345 }
346 }
347#else
348 if (!(channel->work->flags & UI_WINDOW_TEXT_CANVAS) &&
349 code > TEXT_SPACE && nextCode > TEXT_SPACE &&
351 gUiText_Glyphs[nextCode - TEXT_SPACE].width) <= 15) {
352 code |= nextCode << 8;
353 channel->entry = (channel->entry + 1) & RENDER_ENTRY_MASK;
354 }
355#endif
356#endif
357 glyphWidth = UiText_DrawGlyph(channel->work, code, pixelX, pixelY, 0);
359 if (glyphWidth != 0) {
360 // The speaker's voice blip (speakerVoiceSound, the first of four
361 // sounds) every few characters, and the pen moves on by the glyph's
362 // width.
363 if (work->speakerVoiceSound != 0) {
364 if (work->speakerVoiceCountdown == 0) {
365 if (code != TEXT_SPACE) {
366 AudioCommand_PlayFar(work->speakerVoiceSound + (code & 3));
367 work->speakerVoiceCountdown = voiceGap;
368 }
369 } else {
370 work->speakerVoiceCountdown--;
371 }
372 }
373 glyphAdvance = glyphWidth << 8;
374#if (LANGUAGE != LANGUAGE_JAPANESE)
375 if (code == TEXT_SPACE)
376 glyphAdvance += channel->spacing[channel->line];
377#else
378 glyphAdvance += channel->spacing[channel->line];
379#endif
380 channel->x += glyphAdvance;
381 }
382#if (LANGUAGE == LANGUAGE_JAPANESE)
383 else
384 return CHANNEL_STEP_CLOSE;
385#endif
386 if (code == TEXT_SPACE && work->menuState == 0)
387 glyphsLeft = 1;
388 }
389
390 // A pause holds the channel on this entry until its countdown ends.
391 if (channel->countdown != 0) {
392 channel->countdown--;
393 if (channel->countdown != 0)
394 continue;
395 }
396 channel->entry = (channel->entry + 1) & RENDER_ENTRY_MASK;
397 } while (--glyphsLeft != 0);
398
400}
static __inline__ void Dma_Set(const volatile void *source, volatile void *destination, u32 control, volatile u32 *channel)
Starts a DMA transfer: stores the source, destination and control words into channel's registers with...
Definition dma.h:62
void AudioCommand_PlayFar(s32 id)
volatile u16 gLagFramesShown
gLagFrameCount as the last frame left it; the text typing paces itself by it.
void UiWindow_DrawFrame(s32 x, s32 y, u32 width, u32 height)
Draws an empty window: a frame around a filled interior, at tile (x, y) of width by height tiles.
Definition frames.c:131
struct GameState gGameState
const struct FontGlyph gUiText_Glyphs[]
#define REG_DMA3
Definition io_reg.h:153
static s32 Iwram_FillWords(void *destination, s32 size, u32 value)
Definition iwram_call.h:59
volatile u32 gKeysHeld
Keys down now.
u32 gKeysPressedLatch
Presses since a reader last cleared it.
#define CLAMP(v, low, high)
Keeps v within low to high, the low bound tested first.
Definition macros.h:23
void UiWindow_FitOnScreen(s32 entryIndex, s32 *windowX, s32 *windowY, u32 *windowWidth, u32 *windowHeight, u16 *spacing, u32 flags)
Sizes a window for decoded text no and moves (*px, *py) so the window fits on the screen.
Definition measure.c:17
#define NULL
Definition bilibin.h:6
u16 width
pixels
Definition glyph.h:29
u8 messageSpeed
0 to 2; 1 on a new game
Definition game_state.h:66
A text channel, one of three, which types decoded text into a window a few characters a frame and run...
Definition uiwindow.h:43
u16 outline
outline colour; 0 for none
Definition uiwindow.h:54
u16 colour
text colour; 15 by default
Definition uiwindow.h:53
u16 y
pen, 8.8 fixed-point pixels
Definition uiwindow.h:46
u16 flags
nonzero keeps the window's size from page to page
Definition uiwindow.h:60
u16 glyphDelay
frames to wait after a glyph while no button is held
Definition uiwindow.h:59
u16 x
pen, 8.8 fixed-point pixels
Definition uiwindow.h:45
u16 spacing[4]
Extra advance per line, in 1/256 pixels: after every character in Japanese, after a space in the othe...
Definition uiwindow.h:49
u16 ended
1 once a TEXT_END code is reached
Definition uiwindow.h:58
u16 transition
frames left of the canvas scrolling up a line
Definition uiwindow.h:56
u16 lineSpacing
10 by default
Definition uiwindow.h:55
u16 line
line of the page, indexes spacing; at most 3
Definition uiwindow.h:50
u16 startX
pen x each new line returns to, 8.8 fixed-point pixels
Definition uiwindow.h:57
struct UiWindow * work
the window typed into; NULL when free
Definition uiwindow.h:44
u16 entry
next code in UiRenderWork.entries
Definition uiwindow.h:51
u16 countdown
frames left of a pause, a page wait or a swell
Definition uiwindow.h:52
The window system's work: the windows' tilemap, the windows, the three text channels,...
Definition window.h:19
u8 mode
Set while Mind Read shows thoughts: round frame corners, colour 8 text with no shadow,...
Definition window.h:35
u16 speakerVoiceCountdown
characters until the next voice blip
Definition window.h:61
u16 entries[RENDER_ENTRY_MASK+1]
decoded text, a ring of codes
Definition window.h:46
u16 speakerVoiceSound
first of four voice blip sounds; 0 for none
Definition window.h:60
u16 glyphResource
VRAM cache slot of the page arrow; 99 when none.
Definition window.h:50
u16 outline
the current channel's, copied in to draw
Definition window.h:44
u8 menuState
1 from the work's setup; 2 while a battle message is decoded.
Definition window.h:39
u8 pageArrowShown
1 while the next-page arrow is shown
Definition window.h:62
A window: a framed rectangle of the tilemap with its sprite outputs, its opening or closing animation...
Definition uiwindow.h:11
u16 frame
The zoom's step: counts down from 7 to 0 while opening, and up from 0 to duration while closing.
Definition uiwindow.h:28
u16 y
tiles
Definition uiwindow.h:16
u16 effect
The effect a message's TEXT_WINDOW_STATE code sets.
Definition uiwindow.h:21
u16 flags
UI_WINDOW_ flags; 0 once closing or free.
Definition uiwindow.h:25
u16 state
0 while a message channel is typing into the window; 1 once its text has ended or closed the window,...
Definition uiwindow.h:24
u16 x
tiles
Definition uiwindow.h:15
u16 height
tiles
Definition uiwindow.h:14
u16 width
tiles
Definition uiwindow.h:13
s16 duration
frames of the zoom under way; 0 when none
Definition uiwindow.h:29
#define RENDER_ENTRY_MASK
Definition tbs_edition.h:68
const u8 gUiText_SpeedDelays[3]
Frames to wait after each glyph while no button is held.
Definition text_speed.c:8
const u8 gUiText_SpeedCueGaps[3]
Glyphs between two of the speaker's voice blips.
Definition text_speed.c:12
const u8 gUiText_SpeedSteps[3]
Glyphs a message channel draws each frame.
Definition text_speed.c:10
signed int s32
Definition types.h:14
unsigned int u32
Definition types.h:15
signed short s16
Definition types.h:12
unsigned short u16
Definition types.h:13
void UiWork_ProcessRenderChannels(void)
The per-frame message update: each message channel whose window is fully open runs its window effect ...
Definition typing.c:40
#define CANVAS_COLUMN_WORDS
Definition typing.c:10
#define CANVAS_COLUMNS
Definition typing.c:9
s32 UiWork_StepChannelScript(struct UiChannelSlot *channel)
Types one frame's share of a channel's text: runs control codes (new line, new page,...
Definition typing.c:91
void UiText_ScrollCanvasUp(s32 step)
Scrolls the battle message canvas up by 6 pixel rows per step, below its top 8 rows,...
Definition typing.c:15
#define CANVAS_KEPT_ROWS
the top 8 pixel rows stay put
Definition typing.c:11
@ CHANNEL_STEP_ENDED
the text has ended; the window stays open
Definition typing.c:33
@ CHANNEL_STEP_RUNNING
the text goes on next frame
Definition typing.c:32
@ CHANNEL_STEP_CLOSE
the text is dismissed: close the window
Definition typing.c:34
#define UI_CANVAS_VRAM
Definition window.h:45
#define UI_WORK
Definition window.h:267
@ UI_WINDOW_TEXT_CANVAS
text is drawn into a canvas of bitmap tiles (UiWindow_MapTextCanvasTiles)
Definition window.h:31
@ UI_WINDOW_FIXED_X
when a new page resizes the window, its left edge stays put
Definition window.h:39
@ UI_WINDOW_ANCHOR_BOTTOM
when a new page resizes the window, its bottom edge stays put
Definition window.h:37
@ UI_WINDOW_INSTANT
opens and closes at once, with no zoom
Definition window.h:29
#define DMA_COPY32(count)
The DMA control word that copies count words.
Definition window.h:112
s32 Resource_ResetEntry(u32 no)
Frees cache entry no: its blocks go back and the entry is marked free (offset 0xffff).
Definition vram_cache.c:106
s32 UiText_CheckPauseSkipped(struct UiChannelSlot *channel)
Ends a channel's pause early: when the message waits on a sound that has finished (busy),...
Definition messages.c:257
s32 UiText_CheckTextDismissed(struct UiChannelSlot *channel)
As UiText_CheckPauseSkipped, but on a newly pressed button: the latched presses while UiRenderWork....
Definition messages.c:279
void UiWindow_StepSwellEffect(struct UiChannelSlot *channel)
Steps a channel's window effect (UI_WINDOW_EFFECT_SWELL): the frame is drawn a tile bigger on every s...
Definition messages.c:177
s32 UiText_DrawGlyph(struct UiWindow *window, s32 character, s32 x, s32 y, s32 mode)
Draws a character of a window's text with its top-left corner at pixel (x, y) of the window's interio...
Definition sprites.c:17
What every part of the window object shares: its includes, edition values, macros and the codes of de...
@ TEXT_SPACE
@ TEXT_PAUSE_SHORT
a 20-frame pause a button can cut short
@ TEXT_WINDOW_STATE
the next entry is the window effect to run
@ TEXT_END
@ TEXT_PAUSE_LONGEST
a 120-frame pause a button can cut short
@ TEXT_OUTLINE
the next entry is the outline colour
@ TEXT_WAIT_KEY
wait for a button, then close the window
@ TEXT_COLOURS_RESET
@ TEXT_COMMANDS
@ TEXT_VOICED_MARK
dakuten
@ TEXT_PAGE
wait for a button, then clear the window for a new page
@ TEXT_SEMIVOICED_MARK
handakuten
@ TEXT_END_ALT
@ TEXT_NEWLINE
@ TEXT_COLOUR
the next entry is the text colour
@ TEXT_LINE_SPACING
the next entry is the line spacing
@ TEXT_PAUSE_LONG
a 60-frame pause
#define TEXT_CODE_VOICED
#define TEXT_CODE_SEMIVOICED
#define NO_GLYPH_RESOURCE
A glyph resource id meaning no glyph resource is loaded.
void RenderOutput_PrepareForRedraw(struct UiWindow *window)
Clears a window's contents: redraws its empty frame and releases the render outputs it carries.
Definition windows.c:392
void UiText_ApplyChannelStyle(struct UiChannelSlot *channel)
Copies the channel's text colour, outline and line spacing into the shared glyph renderer before that...
Definition windows.c:652
void UiWindow_EraseBorderRect(s32 x, s32 y, u32 width, u32 height)
Erases a window's rectangle of the tilemap (x, y, width and height in tiles), clamped to the screen.
Definition windows.c:187
void UiWork_Finalize(struct UiWindow *window, s32 instant)
Closes a window: clears its contents and remembers its last rectangle.
Definition windows.c:349
void UiText_StartCanvasScroll(struct UiChannelSlot *channel)
Starts a channel scrolling its canvas up a line, over two frames.
Definition windows.c:661