Alchemy
Loading...
Searching...
No Matches

summon_menu.c

Go to the documentation of this file.
1/// @file
2/// The battle summon menu.
3/// Part of battle_command.c, the menu object, compiled only through it.
4
6
7#if TBS_PATCH_1
8#define SUMMON_COST_X 13 ///< Column for the required Djinni count
9#else
10#define SUMMON_COST_X 10
11#endif
12
13#define SUMMON_END 32 ///< End of summon list
14
15/// Shows learned summons four per page; returns the selected summon number.
16/// Cancel returns -1.
17/// standby: number of standby Djinn for each element
18s32 SummonMenu_SelectSummon(s32 unused0, s32 unused1, const u8 *standby) {
19 struct CursorSprite cursor;
20 struct MenuSprite sprites[4];
21 u8 learnedSummons[33]; // Learned summons
22 u8 orderedSummons[33]; // Order with usable summons first
23 u16 text[64];
24 s32 iconSlots[4];
25 u8 visibleSummons[4];
26 s32 glyph;
27 s32 count;
28 struct UiWindow *window;
29 struct UiRenderWork *render;
30 s32 drawnRow, drawnFirstEntry, selectedRow, firstEntry;
31 s32 cursorHandle;
32 struct UiWindow *description, *counts;
33 s32 mask, preferredRow;
34 struct MenuPoint cursorPosition;
35 const struct SummonDefinition *summon;
36 struct BattleMenuWork *navigation;
37 struct MenuSprite *sprite;
38 struct CursorSprite *cur;
39 s32 orderedCount;
40 s32 id, tile, affordable;
41 s32 pressed, repeated, result;
42 s32 index;
43
44 render = UI_WORK;
45 drawnRow = -1;
46 drawnFirstEntry = -1;
47 // One 16x16 sprite.
48 cursorHandle = (s16)VramCache_AllocEntry(128);
49 description = UiWindow_Create(0, 4, 30, 4, 42);
50 counts = UiWindow_Create(20, 8, 10, 3, 6);
51 mask = 0;
52 BATTLE_MENU_GET_CURSOR(firstEntry, selectedRow, preferredRow);
53 window =
55
56 // Icon sprites for each row.
57 for (index = 0; index < 4; index++) {
58 s32 y = index * 2;
59
60 sprite = &sprites[index];
61 MenuSprite_Init(sprites[index], MENU_SPRITE_SIZE_16);
62 sprite->oam.f.x = window->x * 8 + 8;
63 sprite->oam.f.y = (y + window->y) * 8 + 4;
64 }
65 for (index = 0; index < 4; index++) {
66 s32 slot = VramCache_AllocEntry(128);
67 iconSlots[index] = slot;
68 sprites[index].oam.f.tile = VramCache_Reload(slot, -1);
69 }
70
71 // Move usable summons to the front in reverse traversal; keep others in
72 // order.
73 count = Summon_ListLearnedFar(learnedSummons);
74 /* FAKEMATCH(one-pass): the first scan's exit inside a block that runs once
75 * leaves the main loop's select block in place; as a plain block, 88 of 946
76 * instructions differ in tbs-en. */
77 do {
78 s32 i;
79
80 orderedCount = 0;
81 for (i = count - 1; i >= 0; i--) {
82 const struct SummonDefinition *definition;
83 s32 n, k;
84
85 k = learnedSummons[i];
86 definition = SummonDefinition_Get(k);
87 for (n = 0; n <= 3; n++) {
88 if (definition->djinnRequired[n] > standby[n])
89 break;
90 }
91 if (n == 4) {
92 orderedSummons[orderedCount++] = k;
93 learnedSummons[i] = SUMMON_END;
94 }
95 }
96 } while (0);
97 {
98 s32 i, k;
99
100 for (i = 0; i < count; i++) {
101 k = learnedSummons[i];
102 if (k != SUMMON_END)
103 orderedSummons[orderedCount++] = k;
104 }
105 }
106 orderedSummons[orderedCount] = SUMMON_END;
107
108 for (;;) {
109 cur = &cursor;
110
111 //------------------------------------------------------------------
112 // Redraw when the page or row changes.
113 //------------------------------------------------------------------
114 if (firstEntry != drawnFirstEntry || selectedRow != drawnRow) {
115 render->menuBusy = 1;
116 Ui_SetRectHighlight(window->x + 1, window->y + drawnRow * 2 + 1,
117 window->width - 2, 1, 15);
119
120 // Description and required Djinni elements.
121 {
122 const struct SummonDefinition *definition;
123 s32 element;
124
125 definition =
126 SummonDefinition_Get(orderedSummons[firstEntry + selectedRow]);
128 MsgAbilityDescription,
130 UiText_RenderWideStringAtOffset(text, (struct UiWindow *)description, 0,
131 4);
132 drawnRow = selectedRow;
133 mask = 0;
134 for (element = 0; element <= 3; element++) {
135 if (definition->djinnRequired[element] != 0)
136 mask |= 1 << element;
137 }
138 }
139
140 // Standby counts and the four summons on this page.
141 if (firstEntry != drawnFirstEntry) {
143 {
144 s32 element;
145
146 for (element = 0; element <= 3; element++) {
148 element * 2, 0, 0);
149 UiWindow_PutGlyph(counts, standby[element] + '0', element * 2 + 1,
150 0, 0);
151 }
152 }
153 {
154 s32 element, col, y;
155
156 index = 0;
157 if ((id = orderedSummons[firstEntry + index]) != SUMMON_END) {
158 next:
159 summon = SummonDefinition_Get(id);
160 col = 0;
161 for (element = 0; element <= 3; element++) {
162 if (summon->djinnRequired[element] > standby[element])
163 break;
164 }
165 affordable = element == 4;
166 Ability_LoadGlyph(summon->nameMessageId & 0x3fff, 0,
167 &iconSlots[index], &glyph, 1);
168 y = index * 2;
169 sprites[index].oam.f.tile = glyph;
170 // A summon the party cannot afford is drawn in colour 2.
171 if (!affordable)
174 SummonDefinition_Get(id)->nameMessageId + MsgAbilityName,
175 window, 16, index * 16);
176 for (element = 0; element <= 3; element++) {
177 if (summon->djinnRequired[element] != 0) {
179 col * 2 + SUMMON_COST_X, y, 0);
180 UiWindow_PutGlyph(window, summon->djinnRequired[element] + '0',
181 col * 2 + SUMMON_COST_X + 1, y, 0);
182 col++;
183 }
184 }
186 visibleSummons[index++] = 1;
187 if (index <= 3 &&
188 (id = orderedSummons[firstEntry + index]) != SUMMON_END)
189 goto next;
190 }
191 while (index <= 3)
192 visibleSummons[index++] = 0;
193 }
194 drawnFirstEntry = firstEntry;
195 }
196
197 // Page numbers
198 if (count > 4) {
199 for (index = 0; index < (count + 3) / 4; index++) {
200 tile = index + LIST_TILE_PAGE_NUMBER;
201 if (index == firstEntry / 4)
202 tile = index + LIST_TILE_PAGE_NUMBER_LIT;
204 window, tile, window->width - (count + 3) / 4 + index - 2, -1, 0);
205 }
206 }
207 Ui_SetRectHighlight(window->x + 1, window->y + selectedRow * 2 + 1,
208 window->width - 2, 1, 14);
209 render->dirty = 1;
210 render->menuBusy = 0;
211 }
212
213 //------------------------------------------------------------------
214 // Per-frame display
215 //------------------------------------------------------------------
216 for (index = 0; index <= 3; index++) {
217 if (visibleSummons[index] != 0)
218 Runtime_PushSlotEntry((s32 *)&sprites[index], 240);
219 }
220
221 // The cursor wobbles every four frames.
222 cursorPosition.x = window->x * 8 - 2;
223 cursorPosition.y = (selectedRow * 2 + window->y) * 8 + 20;
224 ((u32 *)cur)[1] = MENU_SPRITE_SIZE_16;
225 ((u32 *)cur)[2] = 0;
226 cur->tile =
228 cur->x = cursorPosition.x + ((gFrameCount & 4) >> 1) - 4;
229 cur->y = cursorPosition.y - ((gFrameCount & 4) >> 2) - 8;
230 /* FAKEMATCH: the cursor pushed through sprite, re-pointed at it here, keeps
231 * the frame loop's registers; as &cursor or cur pushed directly, 87
232 * instructions differ in tbs-en. */
233 sprite = (struct MenuSprite *)&cursor;
234 Runtime_PushSlotEntry(sprite, 242);
235
236 // Blink required elements' standby counts every eight frames.
237 {
238 s32 element, tile, blink;
239
240 blink = gFrameCount & 8;
241 for (element = 0; element <= 3; element++) {
242 tile = 15 - (blink != 0);
243 if ((1 << element & mask) == 0)
244 tile = 15;
245 Ui_SetRectHighlight(counts->x + element * 2 + 1, counts->y + 1, 2, 1,
246 tile);
247 }
248 }
249
250 // Page numbers (current page blinking) and left/right arrows.
251 if (count > 4) {
252 for (index = 0; index < (count + 3) / 4; index++) {
253 tile = index + LIST_TILE_PAGE_NUMBER;
254 if ((gFrameCount & 15) <= 11 && index == firstEntry / 4)
255 tile = index + LIST_TILE_PAGE_NUMBER_LIT;
257 window, tile, window->width - (count + 3) / 4 + index - 2, -1, 0);
258 }
260 window->width - (count + 3) / 4 - 3, -1, 0);
262 -1, 0);
263 UiRender_MarkRow(render, window->y - 1);
264 }
265
266 //------------------------------------------------------------------
267 // Input
268 //------------------------------------------------------------------
269 BATTLE_MENU_SET_CURSOR(firstEntry, selectedRow, preferredRow);
270 navigation = gLinkCountdownWork;
271 pressed = gKeysPressed;
272 repeated = gKeysRepeat;
273
274 // Automatic input confirms every 120 frames.
275 if (navigation->tutorialStep != 0) {
276 repeated = 0;
277 pressed = 0;
278 if (navigation->tutorialDelay == 0) {
279 navigation->tutorialDelay = 120;
280 repeated = KEY_A;
281 pressed = KEY_A;
282 } else {
283 navigation->tutorialDelay--;
284 }
285 }
286 if (pressed & KEY_A) {
287 result = orderedSummons[firstEntry + selectedRow];
288 break;
289 }
290 if (gLinkCountdownWork->timer == 0 || (pressed & KEY_B)) {
292 result = -1;
293 break;
294 }
295 if (repeated & KEY_DOWN) {
297 selectedRow++;
298 if (selectedRow == 4 || firstEntry + selectedRow == count)
299 selectedRow = 0;
300 preferredRow = selectedRow;
301 } else if (repeated & KEY_UP) {
303 selectedRow--;
304 if (selectedRow < 0) {
305 // On the last page the cursor goes to its last entry.
306 if (firstEntry == ((count - 1) / 4) * 4)
307 selectedRow = count - firstEntry - 1;
308 else
309 selectedRow = 3;
310 }
311 preferredRow = selectedRow;
312 } else if (repeated & KEY_RIGHT) {
313 // Next page, wrapping from last to first.
316 if (firstEntry + 4 >= count) {
317 if (firstEntry != 0) {
318 firstEntry = 0;
319 selectedRow = preferredRow;
320 }
321 } else {
322 firstEntry += 4;
323 selectedRow = preferredRow;
324 if (firstEntry == ((count - 1) / 4) * 4) {
325 selectedRow = count - firstEntry - 1;
326 if (selectedRow > preferredRow)
327 selectedRow = preferredRow;
328 }
329 }
330 } else if (repeated & KEY_LEFT) {
331 // Previous page, wrapping from first to last.
334 if (firstEntry != 0) {
335 firstEntry -= 4;
336 selectedRow = preferredRow;
337 } else {
338 firstEntry = ((count - 1) / 4) * 4;
339 selectedRow = preferredRow;
340 if (firstEntry != 0) {
341 selectedRow = count - firstEntry - 1;
342 if (selectedRow > preferredRow)
343 selectedRow = preferredRow;
344 }
345 }
346 }
347 WaitFrames(1);
348 }
349
350 // Cleanup
351 WaitFrames(1);
352 for (index = 0; index < 4; index++)
353 Resource_ResetEntry(iconSlots[index]);
354 Resource_ResetEntry((u16)cursorHandle);
355 UiWork_Finalize(counts, 1);
356 UiWork_Finalize(description, 1);
357 UiWork_Finalize(window, 1);
358 WaitFrames(1);
359 return result;
360}
What every part of the menu object shares: its includes, edition values, macros and the link round st...
#define BATTLE_MENU_MESSAGE_COPY
#define SUMMON_MENU_WINDOW_X
@ LIST_TILE_PAGES_RIGHT
Right arrow.
@ SUMMON_ELEMENT_TILE
Element icon.
@ LIST_TILE_PAGE_NUMBER
Page number.
@ LIST_TILE_PAGES_LEFT
Left arrow.
@ LIST_TILE_PAGE_NUMBER_LIT
Current page number.
#define SUMMON_MENU_WINDOW_WIDTH
#define BATTLE_MENU_MESSAGE_CAPACITY
void Ui_SetRectHighlight(s32 x, s32 y, s32 width, s32 height, s32 paletteBit)
Sets bit 12 of each tile in a screen-space rectangle, preserving the other tile attributes.
s32 Summon_ListLearnedFar(u8 *output)
const struct SummonDefinition * SummonDefinition_Get(u32 summon_id)
Far call to SummonDefinition_GetNear.
void AudioCommand_PlayFar(s32 id)
void Input_ResetKeyRepeat(void)
Restarts the key repeat delay, so held keys wait the full delay again.
Definition frame.c:289
void WaitFrames(s32 frames)
The game's frame loop: runs frames frames and returns.
Definition frame.c:97
volatile u32 gFrameCount
Frames WaitFrames has finished.
void UiText_RenderWideStringAtOffset(u16 *text, struct UiWindow *window, s32 x, s32 y)
Draws a zero-terminated string of 16-bit character codes into a text canvas window at pixel (x,...
Definition glyphs.c:258
u8 gResource_FixedBlockBTiles[]
@ KEY_UP
Definition io_reg.h:249
@ KEY_B
Definition io_reg.h:244
@ KEY_LEFT
Definition io_reg.h:248
@ KEY_RIGHT
Definition io_reg.h:247
@ KEY_A
Definition io_reg.h:243
@ KEY_DOWN
Definition io_reg.h:250
volatile u32 gKeysPressed
Keys that went down this frame.
volatile u32 gKeysRepeat
Pressed keys, repeating while held: after 19 frames, then every 6.
struct BattleMenuWork * gLinkCountdownWork
#define MenuSprite_Init(sprite, shape)
Sets sprite shape (attr0/attr1) and clears the remaining fields.
Definition menu_list.h:76
#define MENU_SPRITE_SIZE_16
Definition menu_list.h:67
#define BATTLE_MENU_SET_CURSOR(page_, row_, preferred_)
Saves the selection: page, row and return row.
Definition menu_list.h:183
#define BATTLE_MENU_GET_CURSOR(page_, row_, preferred_)
Restores the previous selection: page, row and return row.
Definition menu_list.h:163
void Runtime_PushSlotEntry(void *slotEntry, s32 slot)
Links a sprite entry into depth slot slot (clamped to 0..255) for this frame.
Definition render.c:79
#define SE_MENU_CANCEL
Definition songs.h:106
#define SE_MENU_CURSOR_MOVE
Definition songs.h:104
The battle command menus' work, heap slot 57 (0x104 bytes), reached as gLinkCountdownWork.
Definition menu_list.h:107
s32 tutorialDelay
frames until the tutorial's next key press
Definition menu_list.h:142
s32 timer
Frames left to choose commands in a link battle; -1 outside link battles.
Definition menu_list.h:131
s32 tutorialStep
The Djinn tutorial's step: 0 off, 1 from the start of each round while FLAG_DJINN_TUTORIAL is set,...
Definition menu_list.h:141
A cursor or prompt sprite: a list link and its OAM attributes as fields.
Definition menu_list.h:50
A point on the screen where a menu shows its cursor.
Definition menu_list.h:97
s32 y
screen pixels
Definition menu_list.h:99
s32 x
screen pixels
Definition menu_list.h:98
A menu sprite: a list link and its OAM attributes, as two words or as fields.
Definition menu_list.h:24
union MenuSprite::@002225022013150231017316170057321303227123371224 oam
A summon: its name's message and the Djinn of each element it needs.
u16 nameMessageId
The summon's ability id: its name is message MsgAbilityName plus it, and its low 14 bits pick the ico...
u8 djinnRequired[4]
standby Djinn needed, by element
The window system's work: the windows' tilemap, the windows, the three text channels,...
Definition window.h:19
u8 dirty
The tilemap's 4-row bands to upload: bit 0 for all, bit n + 1 for band n.
Definition window.h:32
u8 menuBusy
while set, the tilemap is not uploaded
Definition window.h:40
A window: a framed rectangle of the tilemap with its sprite outputs, its opening or closing animation...
Definition uiwindow.h:11
u16 y
tiles
Definition uiwindow.h:16
u16 x
tiles
Definition uiwindow.h:15
u16 width
tiles
Definition uiwindow.h:13
#define SUMMON_END
End of summon list.
Definition summon_menu.c:13
s32 SummonMenu_SelectSummon(s32 unused0, s32 unused1, const u8 *standby)
Shows learned summons four per page; returns the selected summon number.
Definition summon_menu.c:18
#define SUMMON_COST_X
Column for the required Djinni count.
Definition summon_menu.c:8
void UiWork_SetParamNibble(s32 param)
Sets the text colour (0 to 15) of drawn text.
Definition text_tiles.c:524
void UiText_DrawCharacterAtOffset(s32 message, const struct UiWindow *window, u32 offsetX, u32 offsetY)
Decodes a message and draws it as tiles in a window at pixel (offsetX, offsetY) of its interior.
Definition text_tiles.c:552
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
unsigned char u8
Definition types.h:11
#define UiRender_MarkRow(render, y)
Requests transfer of the four-row tilemap band containing window row y.
Definition uiwindow.h:73
#define UI_WORK
Definition window.h:267
s32 VramCache_AllocEntry(s32 size)
Takes a free cache entry, gives it size bytes of sprite VRAM and returns its number.
Definition vram_cache.c:227
s32 VramCache_Reload(s32 no, s32 src)
Copies src again into entry no's VRAM, keeping its size.
Definition vram_cache.c:238
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
void Ability_LoadGlyph(s32 action, s32 withBase, s32 *slot, s32 *tile, s32 reuse)
Builds the Psynergy icon of a battle action, as UiIcon_BuildAbilityIconTiles.
Definition icons.c:234
void UiWindow_PutGlyph(struct UiWindow *win, u32 tile, u32 x, u32 y, s32 mode)
Puts a character into a window's tilemap as a tile at tile (x, y) of its interior.
Definition sprites.c:187
void UiWindow_SetTilemapEntry(struct UiWindow *window, s32 value, s32 x, s32 y, u32 mode)
Writes value into a window's tilemap at tile (x, y) of its interior, with the palette mode names (TIL...
Definition sprites.c:345
s32 Ui_FillVramBlockPattern(void)
Fills the text canvas with the window background colour (4).
Definition windows.c:619
void UiWork_Finalize(struct UiWindow *window, s32 instant)
Closes a window: clears its contents and remembers its last rectangle.
Definition windows.c:349
void RenderOutput_RedrawSavedRect(struct UiWindow *window)
Redraws a window's empty frame at its current rectangle.
Definition windows.c:400
struct UiWindow * UiWindow_Create(s32 x, s32 y, s32 width, s32 height, s32 attrs)
Opens a window at tile (x, y) of width by height tiles in the first free window slot,...
Definition windows.c:279