GBAlatro
A Demake of Balatro for the GBA
Loading...
Searching...
No Matches
options_menu.c
1#include "game/options_menu.h"
2
3#include "affine_background.h"
4#include "audio_utils.h"
5#include "background_options_menu_gfx.h"
6#include "button.h"
7#include "card.h"
8#include "game.h"
9#include "game/common_ui.h"
10#include "game_variables.h"
11#include "graphic_utils.h"
12#include "save.h"
13#include "selection_grid.h"
14#include "soundbank.h"
15#include "util.h"
16
17#include <stdint.h>
18#include <string.h>
19#include <tonc.h>
20#include <tonc_math.h>
21#include <tonc_memdef.h>
22
23enum OptionButtonRows
24{
25 GAME_SPEED_ROW_IDX,
26 HIGH_CONTRAST_ROW_IDX,
27 MUSIC_VOLUME_ROW_IDX,
28 SOUND_VOLUME_ROW_IDX,
29 SAVE_BACK_ROW_IDX,
30 NB_OPTIONS_ROWS
31};
32enum OptionSpeedButtons
33{
34 GAME_SPEED_DOWN_BTN_IDX,
35 GAME_SPEED_UP_BTN_IDX,
36 NB_GAME_SPEED_BUTTONS
37};
38
39// Palette indices for colors that change at runtime (i.e. button outlines) start at 16 and are
40// sorted by the position of the buttons in the menu from top to bottom, and from left to right.
41// Indices for static colors (i.e. background and main button colors) range from 0 to 15.
42// The indexing is determined by background_options_menu_gfx.png.
43#define MENU_BUTTON_MAIN_COLOR_PAL_IDX 1
44#define SAVE_BUTTON_MAIN_COLOR_PAL_IDX 3
45#define BACK_BUTTON_MAIN_COLOR_PAL_IDX 4
46#define SPEED_DOWN_BUTTON_OUTLINE_COLOR_PAL_IDX 16
47#define SPEED_BUTTON_OUTLINE_COLOR_PAL_IDX 17
48#define SPEED_UP_BUTTON_OUTLINE_COLOR_PAL_IDX 18
49#define CONTRAST_BUTTON_OUTLINE_COLOR_PAL_IDX 19
50#define READABLE_BUTTON_OUTLINE_COLOR_PAL_IDX 20
51#define MUSIC_BUTTON_OUTLINE_COLOR_PAL_IDX 21
52#define SOUND_BUTTON_OUTLINE_COLOR_PAL_IDX 22
53#define SAVE_BUTTON_OUTLINE_COLOR_PAL_IDX 23
54#define BACK_BUTTON_OUTLINE_COLOR_PAL_IDX 24
55
56// Define selection grid for the menu buttons
57
58static void game_speed_down_on_pressed(void);
59static void game_speed_up_on_pressed(void);
60static void high_contrast_on_pressed(void);
61static void more_readable_on_pressed(void);
62static void save_on_pressed(void);
63static void back_on_pressed(void);
64static int options_menu_return_upper_rows_size(void);
65static int options_menu_return_card_sprites_row_size(void);
66static int options_menu_return_bottom_row_size(void);
67static void options_menu_row_on_key_transit(SelectionGrid* selection_grid, Selection* selection);
68static bool game_speed_row_on_selection_changed(
69 SelectionGrid* selection_grid,
70 int row_idx,
71 const Selection* prev_selection,
72 const Selection* new_selection
73);
74static bool music_volume_row_on_selection_changed(
75 SelectionGrid* selection_grid,
76 int row_idx,
77 const Selection* prev_selection,
78 const Selection* new_selection
79);
80static bool sound_volume_row_on_selection_changed(
81 SelectionGrid* selection_grid,
82 int row_idx,
83 const Selection* prev_selection,
84 const Selection* new_selection
85);
86static bool regular_button_row_on_selection_changed(
87 SelectionGrid* selection_grid,
88 int row_idx,
89 const Selection* prev_selection,
90 const Selection* new_selection
91);
92
93// clang-format off
94static SelectionGridRow options_menu_selection_rows[] = {
95 {
96 GAME_SPEED_ROW_IDX,
97 options_menu_return_upper_rows_size,
98 game_speed_row_on_selection_changed,
99 NULL,
100 {.wrap = true} // The wrapping is used to allow button activation with Left/Right arrows by
101 // putting logic into the `game_speed_row_on_selection_changed` function
102 },
103 {
104 HIGH_CONTRAST_ROW_IDX,
105 options_menu_return_card_sprites_row_size,
106 regular_button_row_on_selection_changed,
107 options_menu_row_on_key_transit,
108 {.wrap = false}
109 },
110 {
111 MUSIC_VOLUME_ROW_IDX,
112 options_menu_return_upper_rows_size,
113 music_volume_row_on_selection_changed,
114 NULL,
115 {.wrap = true} // Same trick as Game Speed button
116 },
117 {
118 SOUND_VOLUME_ROW_IDX,
119 options_menu_return_upper_rows_size,
120 sound_volume_row_on_selection_changed,
121 NULL,
122 {.wrap = true} // Same trick as Game Speed button
123 },
124 {
125 SAVE_BACK_ROW_IDX,
126 options_menu_return_bottom_row_size,
127 regular_button_row_on_selection_changed,
128 options_menu_row_on_key_transit,
129 {.wrap = false}
130 }
131};
132
133static Button options_menu_buttons[NB_OPTIONS_ROWS][2] = {
134 {{SPEED_BUTTON_OUTLINE_COLOR_PAL_IDX, MENU_BUTTON_MAIN_COLOR_PAL_IDX, NULL, NULL}},
135 {
136 {CONTRAST_BUTTON_OUTLINE_COLOR_PAL_IDX, MENU_BUTTON_MAIN_COLOR_PAL_IDX, high_contrast_on_pressed, NULL},
137 {READABLE_BUTTON_OUTLINE_COLOR_PAL_IDX, MENU_BUTTON_MAIN_COLOR_PAL_IDX, more_readable_on_pressed, NULL}
138 },
139 {{MUSIC_BUTTON_OUTLINE_COLOR_PAL_IDX, MENU_BUTTON_MAIN_COLOR_PAL_IDX, NULL, NULL}},
140 {{SOUND_BUTTON_OUTLINE_COLOR_PAL_IDX, MENU_BUTTON_MAIN_COLOR_PAL_IDX, NULL, NULL}},
141 {
142 {SAVE_BUTTON_OUTLINE_COLOR_PAL_IDX, SAVE_BUTTON_MAIN_COLOR_PAL_IDX, save_on_pressed, NULL},
143 {BACK_BUTTON_OUTLINE_COLOR_PAL_IDX, BACK_BUTTON_MAIN_COLOR_PAL_IDX, back_on_pressed, NULL},
144 }
145};
146
147static Button game_speed_buttons[] = {
148 {SPEED_DOWN_BUTTON_OUTLINE_COLOR_PAL_IDX, MENU_BUTTON_MAIN_COLOR_PAL_IDX, game_speed_down_on_pressed, NULL},
149 {SPEED_UP_BUTTON_OUTLINE_COLOR_PAL_IDX, MENU_BUTTON_MAIN_COLOR_PAL_IDX, game_speed_up_on_pressed, NULL},
150};
151// clang-format on
152
153const Selection OPTIONS_MENU_INIT_SEL = {0, 0};
154
155static SelectionGrid options_menu_selection_grid = {
156 options_menu_selection_rows,
157 NB_OPTIONS_ROWS,
158 OPTIONS_MENU_INIT_SEL
159};
160
161// Positions/Rects used to construct and update the menu
162// clang-format off
163
164// Values in tiles
165static const Rect OPTIONS_SPEED_DOWN_ACTIVE_BTN_SRC_RECT = { 0, 20, 0, 21};
166static const Rect OPTIONS_SPEED_DOWN_DISABLED_BTN_SRC_RECT = { 6, 22, 6, 23};
167static const BG_POINT OPTIONS_SPEED_DOWN_BTN_DEST_POS = {11, 3};
168static const Rect OPTIONS_SPEED_UP_ACTIVE_BTN_SRC_RECT = { 7, 20, 7, 21};
169static const Rect OPTIONS_SPEED_UP_DISABLED_BTN_SRC_RECT = { 7, 22, 7, 23};
170static const BG_POINT OPTIONS_SPEED_UP_BTN_DEST_POS = {18, 3};
171static const Rect OPTIONS_SPEED_VALUES[GAME_SPEED_MAX] = { { 3, 20, 4, 21},
172 { 0, 22, 1, 23},
173 { 2, 22, 3, 23},
174 { 4, 22, 5, 23} };
175static const BG_POINT OPTIONS_SPEED_VALUE_DEST_POS = {14, 3};
176
177static const Rect OPTIONS_CONTRAST_CHECK_NO_SRC_RECT = { 0, 24, 1, 25};
178static const Rect OPTIONS_CONTRAST_CHECK_YES_SRC_RECT = { 2, 24, 3, 25};
179static const BG_POINT OPTIONS_CONTRAST_CHECK_DEST_POS = {14, 7};
180static const Rect OPTIONS_READABLE_CHECK_NO_SRC_RECT = { 4, 24, 5, 25};
181static const Rect OPTIONS_READABLE_CHECK_YES_SRC_RECT = { 6, 24, 7, 25};
182static const BG_POINT OPTIONS_READABLE_CHECK_DEST_POS = {21, 7};
183
184static const Rect OPTIONS_MUSIC_SLIDER_FULL_SRC = { 9, 20, 9, 20};
185static const Rect OPTIONS_MUSIC_SLIDER_MID_SRC = {10, 20, 10, 20};
186static const Rect OPTIONS_MUSIC_SLIDER_EMPTY_SRC = {11, 20, 11, 20};
187static const Rect OPTIONS_SOUND_SLIDER_FULL_SRC = { 9, 22, 9, 22};
188static const Rect OPTIONS_SOUND_SLIDER_MID_SRC = {10, 22, 10, 22};
189static const Rect OPTIONS_SOUND_SLIDER_EMPTY_SRC = {11, 22, 11, 22};
190static const BG_POINT OPTIONS_MUSIC_SLIDER_START_POS = { 5, 11};
191static const BG_POINT OPTIONS_SOUND_SLIDER_START_POS = { 5, 14};
192static const BG_POINT OPTIONS_MUSIC_SLIDER_END_POS = {24, 11};
193static const BG_POINT OPTIONS_SOUND_SLIDER_END_POS = {24, 14};
194static const u8 OPTIONS_MUSIC_SLIDER_SEGMENT_LENGTH = (OPTIONS_MUSIC_SLIDER_END_POS.x - OPTIONS_MUSIC_SLIDER_START_POS.x + 1)
195 / VOLUME_OPTION_MAX;
196static const u8 OPTIONS_SOUND_SLIDER_SEGMENT_LENGTH = (OPTIONS_SOUND_SLIDER_END_POS.x - OPTIONS_SOUND_SLIDER_START_POS.x + 1)
197 / VOLUME_OPTION_MAX;
198
199// Values in pixels
200static const BG_POINT OPTIONS_GAME_SPEED_TEXT_POS = { 82, 16};
201static const BG_POINT OPTIONS_CARD_SPRITES_TEXT_POS = { 72, 48};
202static const BG_POINT OPTIONS_MUSIC_VOLUME_TEXT_POS = { 56, 80};
203static const BG_POINT OPTIONS_MUSIC_VALUE_TEXT_POS = {160, 80};
204static const BG_POINT OPTIONS_SOUND_VOLUME_TEXT_POS = { 56, 104};
205static const BG_POINT OPTIONS_SOUND_VALUE_TEXT_POS = {160, 104};
206static const BG_POINT OPTIONS_BACK_SAVE_TEXT_POS = { 64, 136};
207// clang-format on
208
209#define GAME_SPEED_ARROW_HIGHLIGHT_DURATION 10
210enum OptionSpeedButtons game_speed_arrow_highlight_button = GAME_SPEED_UP_BTN_IDX;
211static s32 game_speed_arrow_highlight_start = UNDEFINED;
212
213static void disable_all_game_speed_outlines_except_self(enum OptionSpeedButtons highlighted_btn)
214{
215 for (int i = 0; i < NB_GAME_SPEED_BUTTONS; i++)
216 {
217 button_set_highlight(&game_speed_buttons[i], i == highlighted_btn);
218 }
219}
220
221static void disable_all_outlines_except_self(Selection sel_btn)
222{
223 // These two get disabled no matter what here
224 disable_all_game_speed_outlines_except_self(NB_GAME_SPEED_BUTTONS);
225
226 for (int j = 0; j < NB_OPTIONS_ROWS; j++)
227 {
228 int nb_buttons_in_row;
229
230 switch (j)
231 {
232 case HIGH_CONTRAST_ROW_IDX:
233 nb_buttons_in_row = options_menu_return_card_sprites_row_size();
234 break;
235 case SAVE_BACK_ROW_IDX:
236 nb_buttons_in_row = options_menu_return_bottom_row_size();
237 break;
238 default:
239 nb_buttons_in_row = options_menu_return_upper_rows_size();
240 break;
241 }
242
243 for (int i = 0; i < nb_buttons_in_row; i++)
244 {
245 button_set_highlight(&options_menu_buttons[j][i], i == sel_btn.x && j == sel_btn.y);
246 }
247 }
248}
249
250static void update_game_speed_button_graphics()
251{
252 // check if need to disable game speed arrows
253
254 Rect speed_down_btn_tiles = (g_game_vars.game_speed == GAME_SPEED_MIN)
255 ? OPTIONS_SPEED_DOWN_DISABLED_BTN_SRC_RECT
256 : OPTIONS_SPEED_DOWN_ACTIVE_BTN_SRC_RECT;
257 main_bg_se_copy_rect(speed_down_btn_tiles, OPTIONS_SPEED_DOWN_BTN_DEST_POS);
258
259 Rect speed_up_btn_tiles = (g_game_vars.game_speed == GAME_SPEED_MAX)
260 ? OPTIONS_SPEED_UP_DISABLED_BTN_SRC_RECT
261 : OPTIONS_SPEED_UP_ACTIVE_BTN_SRC_RECT;
262 main_bg_se_copy_rect(speed_up_btn_tiles, OPTIONS_SPEED_UP_BTN_DEST_POS);
263
265 OPTIONS_SPEED_VALUES[g_game_vars.game_speed - 1],
266 OPTIONS_SPEED_VALUE_DEST_POS
267 );
268}
269
270static void update_high_contrast_button_graphics(void)
271{
272 Rect contrast_btn_tiles = (get_cards_high_contrast()) ? OPTIONS_CONTRAST_CHECK_YES_SRC_RECT
273 : OPTIONS_CONTRAST_CHECK_NO_SRC_RECT;
274 main_bg_se_copy_rect(contrast_btn_tiles, OPTIONS_CONTRAST_CHECK_DEST_POS);
275}
276
277static void update_more_readable_button_graphics(void)
278{
279 Rect readable_btn_tiles = (get_cards_more_readable()) ? OPTIONS_READABLE_CHECK_YES_SRC_RECT
280 : OPTIONS_READABLE_CHECK_NO_SRC_RECT;
281 main_bg_se_copy_rect(readable_btn_tiles, OPTIONS_READABLE_CHECK_DEST_POS);
282}
283
284static void update_volume_slider_graphics(enum OptionButtonRows sel_row)
285{
286 u8 slider_value;
287 BG_POINT slider_segment_dest;
288 int slider_segment_length;
289 Rect slider_segment_full;
290 Rect slider_segment_mid;
291 Rect slider_segment_empty;
292 BG_POINT slider_text_pos;
293
294 switch (sel_row)
295 {
296 case MUSIC_VOLUME_ROW_IDX:
297 slider_value = g_game_vars.music_volume;
298 slider_segment_dest = OPTIONS_MUSIC_SLIDER_START_POS;
299 slider_segment_length = OPTIONS_MUSIC_SLIDER_SEGMENT_LENGTH;
300 slider_segment_full = OPTIONS_MUSIC_SLIDER_FULL_SRC;
301 slider_segment_mid = OPTIONS_MUSIC_SLIDER_MID_SRC;
302 slider_segment_empty = OPTIONS_MUSIC_SLIDER_EMPTY_SRC;
303 slider_text_pos = OPTIONS_MUSIC_VALUE_TEXT_POS;
304 break;
305 case SOUND_VOLUME_ROW_IDX:
306 slider_value = g_game_vars.sound_volume;
307 slider_segment_dest = OPTIONS_SOUND_SLIDER_START_POS;
308 slider_segment_length = OPTIONS_SOUND_SLIDER_SEGMENT_LENGTH;
309 slider_segment_full = OPTIONS_SOUND_SLIDER_FULL_SRC;
310 slider_segment_mid = OPTIONS_SOUND_SLIDER_MID_SRC;
311 slider_segment_empty = OPTIONS_SOUND_SLIDER_EMPTY_SRC;
312 slider_text_pos = OPTIONS_SOUND_VALUE_TEXT_POS;
313 break;
314 // This function only takes in a volume slider
315 default:
316 return;
317 }
318
319 int i = 0;
320
321 // full part of the bar
322 for (; i < slider_value * slider_segment_length - 1; i++)
323 {
324 main_bg_se_copy_rect(slider_segment_full, slider_segment_dest);
325 slider_segment_dest.x++;
326 }
327
328 // at exactly `slider_value` we either:
329 // - are in the middle of the bar, then we draw the frontier between full and empty tiles
330 // - are at the end, then draw a full segment
331 if (slider_value == VOLUME_OPTION_MAX)
332 {
333 main_bg_se_copy_rect(slider_segment_full, slider_segment_dest);
334 }
335 else
336 {
337 if (slider_value != VOLUME_OPTION_MIN)
338 {
339 // draw middle point
340 main_bg_se_copy_rect(slider_segment_mid, slider_segment_dest);
341 i++;
342 slider_segment_dest.x++;
343 }
344
345 // empty part of the bar
346 for (; i < VOLUME_OPTION_MAX * slider_segment_length; i++)
347 {
348 main_bg_se_copy_rect(slider_segment_empty, slider_segment_dest);
349 slider_segment_dest.x++;
350 }
351 }
352
353 tte_printf(
354 "#{P:%d,%d; cx:0x%X000}%3d",
355 slider_text_pos.x,
356 slider_text_pos.y,
357 TTE_WHITE_PB,
358 (slider_value * VOLUME_OPTION_INCREMENT)
359 );
360}
361
363{
364 tte_erase_screen();
365
366 GRIT_CPY(pal_bg_mem, background_options_menu_gfxPal);
367 GRIT_CPY(&tile_mem[MAIN_BG_CBB], background_options_menu_gfxTiles);
368 GRIT_CPY(&se_mem[MAIN_BG_SBB], background_options_menu_gfxMap);
369
370 tte_printf(
371 "#{P:%d,%d; cx:0x%X000}Game Speed",
372 OPTIONS_GAME_SPEED_TEXT_POS.x,
373 OPTIONS_GAME_SPEED_TEXT_POS.y,
374 TTE_WHITE_PB
375 );
376 tte_printf(
377 "#{P:%d,%d; cx:0x%X000}Card Sprites",
378 OPTIONS_CARD_SPRITES_TEXT_POS.x,
379 OPTIONS_CARD_SPRITES_TEXT_POS.y,
380 TTE_WHITE_PB
381 );
382 tte_printf(
383 "#{P:%d,%d; cx:0x%X000}Music Volume",
384 OPTIONS_MUSIC_VOLUME_TEXT_POS.x,
385 OPTIONS_MUSIC_VOLUME_TEXT_POS.y,
386 TTE_WHITE_PB
387 );
388 tte_printf(
389 "#{P:%d,%d; cx:0x%X000}Sound Volume",
390 OPTIONS_SOUND_VOLUME_TEXT_POS.x,
391 OPTIONS_SOUND_VOLUME_TEXT_POS.y,
392 TTE_WHITE_PB
393 );
394 tte_printf(
395 "#{P:%d,%d; cx:0x%X000}Save Cancel",
396 OPTIONS_BACK_SAVE_TEXT_POS.x,
397 OPTIONS_BACK_SAVE_TEXT_POS.y,
398 TTE_WHITE_PB
399 );
400}
401
403{
404 change_background(BG_OPTIONS_MENU, false);
405
406 // Select game speed button by default
407 options_menu_selection_grid.selection = OPTIONS_MENU_INIT_SEL;
408 disable_all_outlines_except_self(OPTIONS_MENU_INIT_SEL);
409
410 // Do an update on the first frame
411 update_game_speed_button_graphics();
412 update_high_contrast_button_graphics();
413 update_more_readable_button_graphics();
414 update_volume_slider_graphics(MUSIC_VOLUME_ROW_IDX);
415 update_volume_slider_graphics(SOUND_VOLUME_ROW_IDX);
416}
417
419{
420 selection_grid_process_input(&options_menu_selection_grid);
421
422 // game speed arrows small animation: they stay highlighted for a few frames
423 if (game_speed_arrow_highlight_start != UNDEFINED &&
424 (g_game_vars.timer - game_speed_arrow_highlight_start) >
425 GAME_SPEED_ARROW_HIGHLIGHT_DURATION)
426 {
427 game_speed_arrow_highlight_start = UNDEFINED;
428 button_set_highlight(&game_speed_buttons[game_speed_arrow_highlight_button], false);
429 }
430}
431
433{
434 tte_erase_screen();
435}
436
437static void game_speed_down_on_pressed(void)
438{
439 g_game_vars.game_speed--;
440 game_speed_arrow_highlight_start = g_game_vars.timer;
441 game_speed_arrow_highlight_button = GAME_SPEED_DOWN_BTN_IDX;
442 disable_all_game_speed_outlines_except_self(GAME_SPEED_DOWN_BTN_IDX);
443 update_game_speed_button_graphics();
444}
445
446static void game_speed_up_on_pressed(void)
447{
448 g_game_vars.game_speed++;
449 game_speed_arrow_highlight_start = g_game_vars.timer;
450 game_speed_arrow_highlight_button = GAME_SPEED_UP_BTN_IDX;
451 disable_all_game_speed_outlines_except_self(GAME_SPEED_UP_BTN_IDX);
452 update_game_speed_button_graphics();
453}
454
458static void high_contrast_on_pressed(void)
459{
460 set_cards_high_contrast(!get_cards_high_contrast());
461 update_high_contrast_button_graphics();
462}
463
467static void more_readable_on_pressed(void)
468{
469 set_cards_more_readable(!get_cards_more_readable());
470 update_more_readable_button_graphics();
471}
472
476static void save_on_pressed(void)
477{
478 save_options();
479 game_change_state(GAME_STATE_MAIN_MENU);
480}
481
485static void back_on_pressed(void)
486{
487 load_options();
488 game_change_state(GAME_STATE_MAIN_MENU);
489}
490
496static int options_menu_return_upper_rows_size(void)
497{
498 return 1;
499}
500
506static int options_menu_return_card_sprites_row_size(void)
507{
508 return 2;
509}
510
516static int options_menu_return_bottom_row_size(void)
517{
518 return 2;
519}
520
521static void change_button_highlight(
522 int row_idx,
523 const Selection* prev_selection,
524 const Selection* new_selection
525)
526{
527 if (prev_selection->y == row_idx)
528 {
529 button_set_highlight(&options_menu_buttons[row_idx][prev_selection->x], false);
530 }
531
532 if (new_selection->y == row_idx)
533 {
534 play_sfx(SFX_BUTTON, MM_BASE_PITCH_RATE, BUTTON_SFX_VOLUME);
535 button_set_highlight(&options_menu_buttons[row_idx][new_selection->x], true);
536 }
537}
538
545static bool game_speed_row_on_selection_changed(
546 SelectionGrid* selection_grid,
547 int row_idx,
548 const Selection* prev_selection,
549 const Selection* new_selection
550)
551{
552 change_button_highlight(row_idx, prev_selection, new_selection);
553 disable_all_game_speed_outlines_except_self(NB_GAME_SPEED_BUTTONS);
554
555 // Can only change game speed by pressing left/right while staying on the button's row
556 if (prev_selection->y != new_selection->y)
557 return true;
558
559 if (key_hit(KEY_LEFT) && g_game_vars.game_speed > GAME_SPEED_MIN)
560 {
561 button_press(&game_speed_buttons[GAME_SPEED_DOWN_BTN_IDX]);
562 }
563 else if (key_hit(KEY_RIGHT) && g_game_vars.game_speed < GAME_SPEED_MAX)
564 {
565 button_press(&game_speed_buttons[GAME_SPEED_UP_BTN_IDX]);
566 }
567
568 return true;
569}
570
576static void options_menu_row_on_key_transit(SelectionGrid* selection_grid, Selection* selection)
577{
578 if (key_hit(SELECT_CARD))
579 {
580 button_press(&options_menu_buttons[selection->y][selection->x]);
581 }
582}
583
584static bool regular_button_row_on_selection_changed(
585 SelectionGrid* selection_grid,
586 int row_idx,
587 const Selection* prev_selection,
588 const Selection* new_selection
589)
590{
591 change_button_highlight(row_idx, prev_selection, new_selection);
592 return true;
593}
594
595static bool music_volume_row_on_selection_changed(
596 SelectionGrid* selection_grid,
597 int row_idx,
598 const Selection* prev_selection,
599 const Selection* new_selection
600)
601{
602 change_button_highlight(row_idx, prev_selection, new_selection);
603
604 if (prev_selection->y != new_selection->y)
605 return true;
606
607 if (key_hit(KEY_LEFT) && g_game_vars.music_volume > VOLUME_OPTION_MIN)
608 {
609 g_game_vars.music_volume--;
610 }
611 else if (key_hit(KEY_RIGHT) && g_game_vars.music_volume < VOLUME_OPTION_MAX)
612 {
613 g_game_vars.music_volume++;
614 }
615
616 update_volume_slider_graphics(MUSIC_VOLUME_ROW_IDX);
617 set_volume(volume_module_step_to_val(g_game_vars.music_volume));
618
619 return true;
620}
621
622static bool sound_volume_row_on_selection_changed(
623 SelectionGrid* selection_grid,
624 int row_idx,
625 const Selection* prev_selection,
626 const Selection* new_selection
627)
628{
629 change_button_highlight(row_idx, prev_selection, new_selection);
630
631 if (prev_selection->y != new_selection->y)
632 return true;
633
634 if (key_hit(KEY_LEFT) && g_game_vars.sound_volume > VOLUME_OPTION_MIN)
635 {
636 g_game_vars.sound_volume--;
637 }
638 else if (key_hit(KEY_RIGHT) && g_game_vars.sound_volume < VOLUME_OPTION_MAX)
639 {
640 g_game_vars.sound_volume++;
641 }
642
643 update_volume_slider_graphics(SOUND_VOLUME_ROW_IDX);
644
645 return true;
646}
Utilities for affine background on GBA.
Utilities for using maxmod to play sound effects.
#define MM_BASE_PITCH_RATE
The default pitch rate for the sound effect played.
Definition audio_utils.h:37
int volume_module_step_to_val(unsigned char step)
Get MaxMod module audio value from VOLUME_OPTION value.
Definition audio_utils.h:85
void play_sfx(mm_word id, mm_word rate, mm_byte volume)
Play a sound effect, wrapper for mmEffectEx() See https://maxmod.org/ref/functions/mm_sound_effect....
A button structure containing the common button functionalities Including highlight and pressing func...
void button_press(Button *button)
Execute a button press, by calling the button's on_pressed function. This checks the button's can_be_...
Definition button.c:18
void button_set_highlight(Button *button, bool highlight)
Set the button highlight on or off.
Definition button.c:8
Common functions to render UI elements.
void change_background(enum BackgroundId id, bool force_redraw)
Definition common_ui.c:35
Game global game variables struct definition.
Graphic utility functions.
void main_bg_se_copy_rect(Rect se_rect, BG_POINT dest_pos)
Copies a rect in the main background from se_rect to the position (x, y).
Options menu state functions.
void options_menu_change_background(void)
Change the options menu background.
void options_menu_on_exit(void)
Options menu cleanup (called when going back to main menu)
void options_menu_on_init(void)
Options menu state initialization.
void options_menu_on_update(void)
Options menu state update.
Utils functions to save/load data structures from/to the SRAM.
void load_options(void)
Load options values from SRAM.
Definition save.c:316
void save_options(void)
Save options values to SRAM.
Definition save.c:302
An implementation for a selection grid that handles directional selection.
void selection_grid_process_input(SelectionGrid *selection_grid)
Processes user input for the selection grid.
A button representation.
Definition button.h:31
The core selection grid struct, represents the grid itself and its state.
Utilities relating around number string representation and protected arithmatic helper functions.