Alchemy
Loading...
Searching...
No Matches

messages.c

Go to the documentation of this file.
1/// @file
2/// Messages shown at an actor, and the speaker's voice.
3/// Part of psynergy_effects.c, compiled only through it.
4
6
7#ifndef UBFIX
8/// The work slots from the window work's onward, seen as the window work and,
9/// twelve slots on, the event work in slot 27.
12 u8 unk04[0x2c];
14};
15#endif
16
17/// The event command "open message": opens the event's current message in a
18/// speech window next to the speaking actor, with the speaker's portrait
19/// beside it when it has one, and moves on to the next message. The low 12
20/// bits of actor are the speaker (0 to 7, a party member, speaks from the
21/// leader); its high bits place the windows: 0x1000 portrait on the left,
22/// 0x2000 on the right, 0x4000 message above the speaker, 0x8000 never above.
23/// Returns the message window.
25 struct UiRenderWork *win;
26 struct FieldWork *work;
27 s32 sprite;
28 s32 extra;
29 s32 portraitTop;
30 s32 margin;
31 s32 height;
32 s32 width;
33 s32 top;
34 s32 left;
35 s32 pos[3];
36 struct UiWindow *handle;
37 s32 portrait;
38 s32 flags;
39 s32 message;
40 struct ObjectRuntime *object;
41 s32 x;
42 s32 y;
43 s32 found;
44 s32 column;
45#if !TBS_PATCH_1
46 s32 none;
47#endif
48
49 win = (struct UiRenderWork *)gWindowWork[0];
50 // UB: the fabricated WorkPointers aggregate reaches from the window slot
51 // into event slot 27. Its named load adds eight bytes in native EN
52 // (0x344 to 0x34c).
53#ifdef UBFIX
54 work = (struct FieldWork *)gEventWork;
55#else
56 work = (struct FieldWork *)((struct WorkPointers *)gWindowWork)->event;
57#endif
58 handle = 0;
59 portrait = 0;
60 sprite = ObjectTable_ReadActiveValue(actor);
61 extra = 0;
62 portraitTop = 0;
63 margin = 4;
64 flags = actor & 0xf000;
65 message = work->message;
66 actor &= 0xfff;
67 x = 0;
68 object = ObjectTable_Get(actor);
69 work->speaker = actor;
70 y = 0;
71 found = 0;
72 if (work->fastForward == 0) {
73 // Find the speaker on screen, in 8-pixel tiles.
74 if (object != 0) {
75 if (work->mode == MODE_WORLD_MAP) {
76 Render_ProjectPoint(&object->x, pos);
77 x = pos[0] >> 3;
78 y = pos[1] >> 3;
79 found = 1;
80 y -= 2;
81 } else {
82 if (Object_GetScreenPosition(actor, pos) != -1)
83 found = 1;
84 else
85 found = 0;
86 x = pos[0] >> 3;
87 y = pos[1] >> 3;
88 }
89 } else if (actor <= 7) {
90 sprite = actor;
92 if (work->mode == MODE_WORLD_MAP) {
93 Render_ProjectPoint(&object->x, pos);
94 x = pos[0] >> 3;
95 y = pos[1] >> 3;
96 found = 1;
97 } else {
99 found = 1;
100 else
101 found = 0;
102 x = pos[0] >> 3;
103 y = pos[1] >> 3;
104 }
105 }
106
107 // Place the message above or below the speaker, or mid-screen when the
108 // speaker is not on screen.
109 if (!found) {
110 left = 15;
111 top = 10;
112 } else {
113 left = 0;
114 top = 0;
115 UiText_GetResourceDimensionsAltFar(message, &left, &top, (u32 *)&width,
116 (u32 *)&height);
117 left = x - width / 2;
118 if (flags & SPEAKER_WINDOW_ABOVE)
119 top = y - height - 1;
120 else if (!(flags & SPEAKER_WINDOW_BELOW) && y <= 8)
121 top = y - height - 1;
122 else
123 top = y + 4;
124 }
125
126 // Place the portrait beside the speaker, on the side with more room.
127 if (win->mode != 0)
128 margin = 5;
129 column = x;
130 if (flags & SPEAKER_PORTRAIT_LEFT) {
131 column = x - margin - 2;
132 if (column < 0)
133 column = 0;
134 } else if (flags & SPEAKER_PORTRAIT_RIGHT) {
135 column += 2;
136 if (column + margin > 29)
137 column = 29 - margin;
138 } else if (column <= 15) {
139 column = column - margin - 2;
140 if (column < 0)
141 column = x + 2;
142 } else {
143 column += 2;
144 if (column + margin > 29)
145 column = x - margin - 2;
146 }
147
148 // A speaker with a portrait: put the portrait's 5 rows just above or
149 // below the message, and grow the message to the portrait's height.
150#if TBS_PATCH_1
151 found = Localization_LookupEntryIdFar(sprite);
152 if (found != -1) {
153 UiText_GetResourceDimensionsAltFar(message, &left, &top, (u32 *)&width,
154 (u32 *)&height);
155 message = -1;
156 if (top > y)
157 portraitTop = top - 5;
158 else
159 portraitTop = top + height;
160 if (portraitTop < 0)
161 portraitTop = top + height;
162 else if (portraitTop + 5 > 19)
163 portraitTop = top - 5;
164 if (top < portraitTop) {
165 s32 lines = height;
166
167 UiText_GetResourceDimensionsFar(message, &left, &top, (u32 *)&width,
168 (u32 *)&height);
169 message = -1;
170 extra = lines - height + 1;
171 }
172 } else if (top < y) {
173 s32 lines = height;
174
175 UiText_GetResourceDimensionsFar(message, &left, &top, (u32 *)&width,
176 (u32 *)&height);
177 extra = lines - height + 1;
178 message = found;
179 }
180#else
181 none = Localization_LookupEntryIdFar(sprite);
182 found = -1;
183 if (none != found) {
184 UiText_GetResourceDimensionsAltFar(message, &left, &top, (u32 *)&width,
185 (u32 *)&height);
186 message = found;
187 if (top > y)
188 portraitTop = top - 5;
189 else
190 portraitTop = top + height;
191 if (portraitTop < 0)
192 portraitTop = top + height;
193 else if (portraitTop + 5 > 19)
194 portraitTop = top - 5;
195 }
196 if (top < y) {
197 s32 lines = height;
198
199 UiText_GetResourceDimensionsFar(message, &left, &top, (u32 *)&width,
200 (u32 *)&height);
201 message = -1;
202 extra = lines - height + 1;
203 }
204#endif
205 if (column < 0)
206 column = 0;
207 else if (column + margin > 29)
208 column = 29 - margin;
209
210 // Open the message (with the speaker's voice outside mode 1) and the
211 // portrait, and wait for both to be drawn.
212 if (win->mode != 0) {
213 WaitFrames(8);
214 if (extra != 0)
215 handle =
216 UiText_OpenMessageWindowFar(message, left, top + extra - 1, 18);
217 else
218 handle = UiText_OpenMessageWindowFar(message, left, top, 2);
219 } else {
220 s32 voice = BattleFx_GetResourceId(sprite);
221
222 if (extra != 0)
223 handle = UiText_OpenMessageWindowFar(message, left, top + extra - 1,
224 (voice << 16) | 17);
225 else
226 handle =
227 UiText_OpenMessageWindowFar(message, left, top, (voice << 16) | 1);
228 }
229 if (win->mode != 0)
230 portrait =
231 (s32)UiWindow_CreateWithSideObjectFar(sprite, 0, column, portraitTop);
232 else
233 portrait =
234 (s32)UiWindow_CreateWithSideObjectFar(sprite, 0, column, portraitTop);
235 while (!UiWork_IsCompleteFar())
236 WaitFrames(1);
237 }
238 work->messageWindow = (s32)handle;
239 work->sideWindow = portrait;
240 work->message++;
241 return handle;
242}
243
244/// The event command "show message": opens the current message at actor
245/// (UiText_OpenMessageAtObject) and waits until it has been read. The message
246/// closes by itself after 600 frames, or at once while Select, L, R and A are
247/// all held.
249 s32 member;
250 struct FieldWork *runtime = (struct FieldWork *)gEventWork;
251 struct UiWindow *window = UiText_OpenMessageAtObject(actor, flags);
252 s32 sprite;
253 u32 frames;
254
255 frames = 0;
256 WaitFrames(1);
257 sprite = ObjectTable_ReadActiveValue(actor);
258 if (actor <= 7) {
259 member = actor & 0x0fff;
260
261 if (Scene_FindActorPlacement(member) == 0) {
262 sprite = member;
263 }
264 }
266
267 if (runtime->fastForward == 0) {
268 while (UiWork_IsIdleFar(window) == 0) {
269 WaitFrames(1);
270 frames++;
271 if (frames > 600 || ((gKeysHeld & KEY_SELECT) && (gKeysHeld & KEY_R) &&
272 (gKeysHeld & KEY_L) && (gKeysHeld & KEY_A))) {
274 }
275 }
276 }
277
278 WaitFrames(1);
279}
280
281/// The event command "show message and wait": shows the message, then waits
282/// wait frames.
286}
287
288/// The event command "ask yes or no": opens the question at actor, lets the
289/// leader answer, then shows the next message for yes or the one after it for
290/// no. Returns 0 for yes, 1 for no.
292 s32 answer;
293 struct FieldWork *runtime;
294 struct GameState *save;
295
296 UiText_OpenMessageAtObject(actor, flags);
297 save = &gGameState;
299 if (answer == 0) {
300 FieldEvent_ShowMessageAndWait(actor, flags);
301 runtime = (struct FieldWork *)gEventWork;
302 runtime->message += 1;
303 } else {
304 runtime = (struct FieldWork *)gEventWork;
305 runtime->message += 1;
306 FieldEvent_ShowMessageAndWait(actor, flags);
307 }
308 return answer;
309}
310
312
313/// A speaker-message entry point retained by its overlay veneer, with no
314/// current scene caller. Records the speaker, waits for the message unless
315/// fast-forwarding, and advances the message index.
316/// XXX: this and Event_ShowMessageAtPosition apply a 32-unit offset and
317/// 312/220 limits before calling a window service that takes tiles. A caller
318/// establishing the intended position would settle its units and conversion.
320 // BUG: the native lookup discards its result and leaves pos unset.
321#ifdef UBFIX
322 s32 pos[3];
323 struct ObjectRuntime *object;
324#else
325 struct {
326 s32 x, y;
327 } pos;
328#endif
329 struct FieldWork *state = (struct FieldWork *)gEventWork;
330
331 speaker &= 0xfff;
332#ifdef UBFIX
333 // Only the actor slots hold actor pointers; ObjectTable_Get's legacy range
334 // also exposes the event fields after them.
335 if ((u32)speaker < sizeof(state->actors) / sizeof(state->actors[0]))
336 object = ObjectTable_Get(speaker);
337 else
338 object = NULL;
339#else
341#endif
342 state->speaker = speaker;
343 if (state->fastForward == 0) {
344#ifdef UBFIX
345 s32 positionFound = 0;
346
347 // Supply the actual speaker position as UiText_OpenMessageAtObject does.
348 // A missing actor or failed projection has no position to open around;
349 // still consume the message, as the fast-forward path does.
350 if (object != NULL) {
351 if (state->mode == MODE_WORLD_MAP)
352 positionFound =
354 (const u8 *)object + offsetof(struct ObjectRuntime, x), pos) !=
355 0;
356 else
357 positionFound = Object_GetScreenPosition(speaker, pos) != -1;
358 }
359 if (positionFound) {
360 s32 px = pos[0];
361 s32 py = pos[1];
362 s32 win;
363#else
364 s32 px = pos.x;
365 s32 py = pos.y;
366 s32 win;
367#endif
368
369 if (py > 119)
370 py += 32;
371 else
372 py -= 32;
373 px = px < 8 ? 8 : px;
374 if (px > 312)
375 px = 312;
376 py = py < 20 ? 20 : py;
377 if (py > 220)
378 py = 220;
379 win = (s32)UiText_OpenMessageWindowFar(state->message, px, py, 1);
380 state->messageWindow = win;
381 while (UiWork_IsIdleFar((struct UiWindow *)win) == 0)
382 WaitFrames(1);
383#ifdef UBFIX
384 }
385#endif
386 }
387 state->message++;
388}
389
390/// Retained by its overlay veneer; no current caller establishes the
391/// coordinates forwarded to Event_ShowMessageAtPosition.
392void ObjectTable_CallRefreshHook(s32 unused0, s32 unused1, s32 x, s32 y) {
393 Event_ShowMessageAtPosition(unused0, unused1, x, y);
394}
395
396/// A retained coordinate-message entry point: waits for the message and
397/// advances the event's message index. Its placement units are unresolved,
398/// as in Event_ShowSpeakerMessage.
399void Event_ShowMessageAtPosition(s32 unused0, s32 unused1, s32 x, s32 y) {
400 struct FieldWork *state = (struct FieldWork *)gEventWork;
401 s32 py = y;
402 s32 px = x;
403 s32 minX = 8;
404 s32 minY = 20;
405 s32 window;
406
407 if (py > 119)
408 py += 32;
409 else
410 py -= 32;
411
412 if (x < minX)
413 px = minX;
414 if (px > 312)
415 px = 312;
416 CLAMP(py, minY, 220);
417
418 window = (s32)UiText_OpenMessageWindowFar(state->message, px, py, 1);
419 while (UiWork_IsIdleFar((struct UiWindow *)window) == 0)
420 WaitFrames(1);
421 state->message++;
422}
423
424/// Closes the portrait of an actor's sprite, if it has one.
426 s32 sprite;
427
428 sprite = ObjectTable_ReadActiveValue(actor);
429 if (sprite != -1)
431}
432
433/// The event command "show two messages and wait": two actors speak at once,
434/// each message at its own (x, y) with its portrait at (portraitX, portraitY),
435/// all in tiles. Waits for A, B, L or R, then closes both.
436void Battle_ShowPairedUnitWorkAndWait(s32 first, s32 firstX, s32 firstY,
437 s32 firstPortraitX, s32 firstPortraitY,
438 s32 second, s32 secondX, s32 secondY,
439 s32 secondPortraitX,
440 s32 secondPortraitY) {
441 struct FieldWork *field = (struct FieldWork *)gEventWork;
442 s32 firstSprite = ObjectTable_ReadActiveValue(first);
443 s32 secondSprite = ObjectTable_ReadActiveValue(second);
444 s32 firstWindow;
445 s32 secondWindow;
446
447 firstWindow = (s32)UiText_OpenMessageWindowFar(
448 field->message++, firstX, firstY,
449 BattleFx_GetResourceId(firstSprite) << 16);
450 UiWindow_CreateWithSideObjectFar(firstSprite, 0, firstPortraitX,
451 firstPortraitY);
452
453 secondWindow = (s32)UiText_OpenMessageWindowFar(
454 field->message++, secondX, secondY,
455 BattleFx_GetResourceId(secondSprite) << 16);
456 UiWindow_CreateWithSideObjectFar(secondSprite, 0, secondPortraitX,
457 secondPortraitY);
458
459 while (UiWork_IsCompleteFar() == 0)
460 WaitFrames(1);
461
462 WaitFrames(1);
463 while ((gKeysPressed & (KEY_A | KEY_B | KEYS_SHOULDERS)) == 0)
464 WaitFrames(1);
465
466 WaitFrames(1);
470 WaitFrames(1);
471
472 while (UiWork_IsIdleFar((struct UiWindow *)firstWindow) == 0)
473 WaitFrames(1);
474 while (UiWork_IsIdleFar((struct UiWindow *)secondWindow) == 0)
475 WaitFrames(1);
476
477 WaitFrames(1);
478}
479
480/// Sets the voice of the messages that follow to that of actor's sprite, at
481/// the rate the message speed option picks (gUi_RenderResultValues). With
482/// 0x80000000 the messages are silent.
484 struct UiRenderWork *state = (struct UiRenderWork *)gWindowWork[0];
485 u8 rate;
486 s32 voice;
487
488 // Direct u16 stores shrink the English function from 0x58 to 0x54 bytes.
489 // Keep the original signed stores; the corresponding u16 alias is legal.
490 if (actor == 0x80000000) {
491 *(s16 *)&state->speakerVoiceSound = (rate = 0);
492 *(s16 *)&state->speakerVoiceCountdown = rate;
493 } else {
496 *(s16 *)&state->speakerVoiceSound = voice;
497 *(s16 *)&state->speakerVoiceCountdown = rate;
498 }
499}
s32 ObjectTable_ReadActiveValue(s32 actor)
Returns the sprite (the first animation's id) of the actor in the low 12 bits of actor,...
void * ObjectTable_Get(u32 actorIndex)
Returns the actor in slot index (0 to 65), or NULL.
Definition actors.c:462
u8 gUi_RenderResultValues[]
s32 Object_GetScreenPosition(s32 objectId, s32 *position)
Finds where an actor is on screen: position[0] and position[1] receive its x and y in pixels,...
Definition context.c:12
s32 UiText_GetResourceDimensionsFar(s32 no, s32 *x, s32 *y, u32 *width, u32 *height)
s32 UiText_GetResourceDimensionsAltFar(s32 no, s32 *x, s32 *y, u32 *width, u32 *height)
s32 Localization_LookupEntryIdFar(u32 value)
struct UiWindow * UiWindow_CreateWithSideObjectFar(s32 arg0, s32 arg1, s32 x, s32 y)
s32 UiWork_IsIdleFar(struct UiWindow *window)
void UiWork_FinalizeEntityMatchingLocalizedIdFar(u32 entry)
s32 UiWork_IsCompleteFar(void)
struct UiWindow * UiText_OpenMessageWindowFar(s32 message, s32 x, s32 y, u32 packed)
void UiWork_FinalizePendingCoreFar(void)
#define SPEAKER_PORTRAIT_LEFT
Definition field_event.h:54
#define SPEAKER_WINDOW_ABOVE
Definition field_event.h:56
#define SPEAKER_PORTRAIT_RIGHT
Definition field_event.h:55
#define SPEAKER_WINDOW_BELOW
Definition field_event.h:57
void WaitFrames(s32 frames)
The game's frame loop: runs frames frames and returns.
Definition frame.c:97
struct GameState gGameState
@ KEY_B
Definition io_reg.h:244
@ KEY_SELECT
Definition io_reg.h:245
@ KEY_R
Definition io_reg.h:251
@ KEY_A
Definition io_reg.h:243
@ KEYS_SHOULDERS
Definition io_reg.h:256
@ KEY_L
Definition io_reg.h:252
volatile u32 gKeysHeld
Keys down now.
volatile u32 gKeysPressed
Keys that went down this frame.
#define CLAMP(v, low, high)
Keeps v within low to high, the low bound tested first.
Definition macros.h:23
struct ScenePlacement * Scene_FindActorPlacement(s32 id)
Returns the placement of actor id in the scene's placement tables, or NULL: for a party character (0 ...
Definition events.c:14
s32 Event_AskYesNoWithActorAnimation(s32 actor, s32 forceBottom)
The event command "choose yes or no": once the message on screen has been read, asks yes or no below ...
Definition events.c:416
void FieldEvent_WaitFrames(s32 frames)
The event command "wait": waits the requested frames, unless debug fast forward (Event_UpdateDebugSki...
Definition events.c:55
struct UiWindow * UiText_OpenMessageAtObject(s32 actor, s32 unused)
The event command "open message": opens the event's current message in a speech window next to the sp...
Definition messages.c:24
void Ui_SetMessageVoiceFromActor(s32 actor)
Sets the voice of the messages that follow to that of actor's sprite, at the rate the message speed o...
Definition messages.c:483
void ObjectTable_RunIfActive(s32 actor)
Closes the portrait of an actor's sprite, if it has one.
Definition messages.c:425
void Event_ShowSpeakerMessage(s32 speaker)
A speaker-message entry point retained by its overlay veneer, with no current scene caller.
Definition messages.c:319
void ObjectTable_CallRefreshHook(s32 unused0, s32 unused1, s32 x, s32 y)
Retained by its overlay veneer; no current caller establishes the coordinates forwarded to Event_Show...
Definition messages.c:392
void BattleEventRuntime_EmptyCallback(void)
Definition messages.c:311
void Event_ShowMessageAtPosition(s32 unused0, s32 unused1, s32 x, s32 y)
A retained coordinate-message entry point: waits for the message and advances the event's message ind...
Definition messages.c:399
void FieldEvent_ShowMessageAndWait(s32 actor, s32 flags)
The event command "show message": opens the current message at actor (UiText_OpenMessageAtObject) and...
Definition messages.c:248
void BattlePres_RunActionThenWaitIfModeZero(s32 actor, s32 flags, s32 wait)
The event command "show message and wait": shows the message, then waits wait frames.
Definition messages.c:283
void Battle_ShowPairedUnitWorkAndWait(s32 first, s32 firstX, s32 firstY, s32 firstPortraitX, s32 firstPortraitY, s32 second, s32 secondX, s32 secondY, s32 secondPortraitX, s32 secondPortraitY)
The event command "show two messages and wait": two actors speak at once, each message at its own (x,...
Definition messages.c:436
s32 FieldEvent_AskYesNoAndShowResponse(s32 actor, s32 flags)
The event command "ask yes or no": opens the question at actor, lets the leader answer,...
Definition messages.c:291
What every part of psynergy_effects.c shares: its includes and the macros and types more than one par...
s32 BattleFx_GetResourceId(u32 id)
Returns the portrait shown beside the messages of sprite id (0x100 plus its definition's value),...
Definition speakers.c:40
struct EventWork * gEventWork
#define NULL
Definition bilibin.h:6
u8 gWindowWork[]
#define offsetof(type, member)
Definition stddef.h:19
Full event work allocated by Field_RunScene (heap slot 27).
s16 event
A raised event for Scene_RunRaisedEvent, or -1 when characters fell to poison or hazard damage,...
s32 fastForward
debug: skip timed event waits and actor messages
s32 speaker
the actor speaking; the leader at each event
s16 message
the next message, advanced as each window opens
s32 sideWindow
the speaker's portrait window
s16 mode
MODE_WORLD_MAP (3) on the world map.
s32 messageWindow
the open message window, 0 for none
struct FieldActor * actors[66]
The actors by id: 0 to 7 the party's characters, 8 on the placed actors.
The game's state: play time, coins, the scene and the leader's position, the active party and the opt...
Definition game_state.h:14
u8 messageSpeed
0 to 2; 1 on a new game
Definition game_state.h:66
s32 selectedActor
the leader: its unit and its actor
Definition game_state.h:59
One of the 64 field objects in the object pool (heap slot 5): its script, position,...
s32 x
16.16 fixed-point pixels
s32 y
16.16 fixed-point pixels
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 speakerVoiceSound
first of four voice blip sounds; 0 for none
Definition window.h:60
A window: a framed rectangle of the tilemap with its sprite outputs, its opening or closing animation...
Definition uiwindow.h:11
u16 flags
UI_WINDOW_ flags; 0 once closing or free.
Definition uiwindow.h:25
The work slots from the window work's onward, seen as the window work and, twelve slots on,...
Definition messages.c:10
struct FieldWork * event
Definition messages.c:13
u8 * window
Definition messages.c:11
u8 unk04[0x2c]
Definition messages.c:12
s32 Render_ProjectPoint(const void *point, s32 *screen)
Reads three word-aligned coordinates from point, transforms them by gTransform and projects them to t...
Definition transform.c:364
signed int s32
Definition types.h:14
unsigned int u32
Definition types.h:15
signed short s16
Definition types.h:12
unsigned char u8
Definition types.h:11
#define MODE_WORLD_MAP
the world map
Definition object.h:78