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// Color palette indices
40#define MENU_BUTTON_MAIN_COLOR_PAL_IDX 1
41#define SAVE_BUTTON_MAIN_COLOR_PAL_IDX 3
42#define BACK_BUTTON_MAIN_COLOR_PAL_IDX 4
43#define SPEED_DOWN_BUTTON_OUTLINE_COLOR_PAL_IDX 16
44#define SPEED_BUTTON_OUTLINE_COLOR_PAL_IDX 17
45#define SPEED_UP_BUTTON_OUTLINE_COLOR_PAL_IDX 18
46#define CONTRAST_BUTTON_OUTLINE_COLOR_PAL_IDX 19
47#define READABLE_BUTTON_OUTLINE_COLOR_PAL_IDX 20
48#define MUSIC_BUTTON_OUTLINE_COLOR_PAL_IDX 21
49#define SOUND_BUTTON_OUTLINE_COLOR_PAL_IDX 22
50#define SAVE_BUTTON_OUTLINE_COLOR_PAL_IDX 23
51#define BACK_BUTTON_OUTLINE_COLOR_PAL_IDX 24
52
53// Define selection grid for the menu buttons
54
55static void game_speed_down_on_pressed(void);
56static void game_speed_up_on_pressed(void);
57static void high_contrast_on_pressed(void);
58static void more_readable_on_pressed(void);
59static void save_on_pressed(void);
60static void back_on_pressed(void);
61static int options_menu_return_upper_rows_size(void);
62static int options_menu_return_card_sprites_row_size(void);
63static int options_menu_return_bottom_row_size(void);
64static void options_menu_row_on_key_transit(SelectionGrid* selection_grid, Selection* selection);
65static bool game_speed_row_on_selection_changed(
66 SelectionGrid* selection_grid,
67 int row_idx,
68 const Selection* prev_selection,
69 const Selection* new_selection
70);
71static bool music_volume_row_on_selection_changed(
72 SelectionGrid* selection_grid,
73 int row_idx,
74 const Selection* prev_selection,
75 const Selection* new_selection
76);
77static bool sound_volume_row_on_selection_changed(
78 SelectionGrid* selection_grid,
79 int row_idx,
80 const Selection* prev_selection,
81 const Selection* new_selection
82);
83static bool regular_button_row_on_selection_changed(
84 SelectionGrid* selection_grid,
85 int row_idx,
86 const Selection* prev_selection,
87 const Selection* new_selection
88);
89
90// clang-format off
91static SelectionGridRow options_menu_selection_rows[] = {
92 {
93 GAME_SPEED_ROW_IDX,
94 options_menu_return_upper_rows_size,
95 game_speed_row_on_selection_changed,
96 NULL,
97 {.wrap = true} // The wrapping is used to allow button activation with Left/Right arrows by
98 // putting logic into the `game_speed_row_on_selection_changed` function
99 },
100 {
101 HIGH_CONTRAST_ROW_IDX,
102 options_menu_return_card_sprites_row_size,
103 regular_button_row_on_selection_changed,
104 options_menu_row_on_key_transit,
105 {.wrap = false}
106 },
107 {
108 MUSIC_VOLUME_ROW_IDX,
109 options_menu_return_upper_rows_size,
110 music_volume_row_on_selection_changed,
111 NULL,
112 {.wrap = true} // Same trick as Game Speed button
113 },
114 {
115 SOUND_VOLUME_ROW_IDX,
116 options_menu_return_upper_rows_size,
117 sound_volume_row_on_selection_changed,
118 NULL,
119 {.wrap = true} // Same trick as Game Speed button
120 },
121 {
122 SAVE_BACK_ROW_IDX,
123 options_menu_return_bottom_row_size,
124 regular_button_row_on_selection_changed,
125 options_menu_row_on_key_transit,
126 {.wrap = false}
127 }
128};
129
130static Button options_menu_buttons[NB_OPTIONS_ROWS][2] = {
131 {{SPEED_BUTTON_OUTLINE_COLOR_PAL_IDX, MENU_BUTTON_MAIN_COLOR_PAL_IDX, NULL, NULL}},
132 {
133 {CONTRAST_BUTTON_OUTLINE_COLOR_PAL_IDX, MENU_BUTTON_MAIN_COLOR_PAL_IDX, high_contrast_on_pressed, NULL},
134 {READABLE_BUTTON_OUTLINE_COLOR_PAL_IDX, MENU_BUTTON_MAIN_COLOR_PAL_IDX, more_readable_on_pressed, NULL}
135 },
136 {{MUSIC_BUTTON_OUTLINE_COLOR_PAL_IDX, MENU_BUTTON_MAIN_COLOR_PAL_IDX, NULL, NULL}},
137 {{SOUND_BUTTON_OUTLINE_COLOR_PAL_IDX, MENU_BUTTON_MAIN_COLOR_PAL_IDX, NULL, NULL}},
138 {
139 {SAVE_BUTTON_OUTLINE_COLOR_PAL_IDX, SAVE_BUTTON_MAIN_COLOR_PAL_IDX, save_on_pressed, NULL},
140 {BACK_BUTTON_OUTLINE_COLOR_PAL_IDX, BACK_BUTTON_MAIN_COLOR_PAL_IDX, back_on_pressed, NULL},
141 }
142};
143
144static Button game_speed_buttons[] = {
145 {SPEED_DOWN_BUTTON_OUTLINE_COLOR_PAL_IDX, MENU_BUTTON_MAIN_COLOR_PAL_IDX, game_speed_down_on_pressed, NULL},
146 {SPEED_UP_BUTTON_OUTLINE_COLOR_PAL_IDX, MENU_BUTTON_MAIN_COLOR_PAL_IDX, game_speed_up_on_pressed, NULL},
147};
148// clang-format on
149
150const Selection OPTIONS_MENU_INIT_SEL = {0, 0};
151
152static SelectionGrid options_menu_selection_grid = {
153 options_menu_selection_rows,
154 NB_OPTIONS_ROWS,
155 OPTIONS_MENU_INIT_SEL
156};
157
158// Positions/Rects used to construct and update the menu
159// clang-format off
160
161// Values in tiles
162static const Rect OPTIONS_SPEED_DOWN_ACTIVE_BTN_SRC_RECT = { 0, 20, 0, 21};
163static const Rect OPTIONS_SPEED_DOWN_DISABLED_BTN_SRC_RECT = { 6, 22, 6, 23};
164static const BG_POINT OPTIONS_SPEED_DOWN_BTN_DEST_POS = {11, 3};
165static const Rect OPTIONS_SPEED_UP_ACTIVE_BTN_SRC_RECT = { 7, 20, 7, 21};
166static const Rect OPTIONS_SPEED_UP_DISABLED_BTN_SRC_RECT = { 7, 22, 7, 23};
167static const BG_POINT OPTIONS_SPEED_UP_BTN_DEST_POS = {18, 3};
168static const Rect OPTIONS_SPEED_VALUES[GAME_SPEED_MAX] = { { 3, 20, 4, 21},
169 { 0, 22, 1, 23},
170 { 2, 22, 3, 23},
171 { 4, 22, 5, 23} };
172static const BG_POINT OPTIONS_SPEED_VALUE_DEST_POS = {14, 3};
173
174static const Rect OPTIONS_CONTRAST_CHECK_NO_SRC_RECT = { 0, 24, 1, 25};
175static const Rect OPTIONS_CONTRAST_CHECK_YES_SRC_RECT = { 2, 24, 3, 25};
176static const BG_POINT OPTIONS_CONTRAST_CHECK_DEST_POS = {14, 7};
177static const Rect OPTIONS_READABLE_CHECK_NO_SRC_RECT = { 4, 24, 5, 25};
178static const Rect OPTIONS_READABLE_CHECK_YES_SRC_RECT = { 6, 24, 7, 25};
179static const BG_POINT OPTIONS_READABLE_CHECK_DEST_POS = {21, 7};
180
181static const Rect OPTIONS_MUSIC_SLIDER_FULL_SRC = { 9, 20, 9, 20};
182static const Rect OPTIONS_MUSIC_SLIDER_MID_SRC = {10, 20, 10, 20};
183static const Rect OPTIONS_MUSIC_SLIDER_EMPTY_SRC = {11, 20, 11, 20};
184static const Rect OPTIONS_SOUND_SLIDER_FULL_SRC = { 9, 22, 9, 22};
185static const Rect OPTIONS_SOUND_SLIDER_MID_SRC = {10, 22, 10, 22};
186static const Rect OPTIONS_SOUND_SLIDER_EMPTY_SRC = {11, 22, 11, 22};
187static const BG_POINT OPTIONS_MUSIC_SLIDER_START_POS = { 5, 11};
188static const BG_POINT OPTIONS_SOUND_SLIDER_START_POS = { 5, 14};
189static const BG_POINT OPTIONS_MUSIC_SLIDER_END_POS = {24, 11};
190static const BG_POINT OPTIONS_SOUND_SLIDER_END_POS = {24, 14};
191static const u8 OPTIONS_MUSIC_SLIDER_SEGMENT_LENGTH = (OPTIONS_MUSIC_SLIDER_END_POS.x - OPTIONS_MUSIC_SLIDER_START_POS.x + 1)
192 / VOLUME_OPTION_MAX;
193static const u8 OPTIONS_SOUND_SLIDER_SEGMENT_LENGTH = (OPTIONS_SOUND_SLIDER_END_POS.x - OPTIONS_SOUND_SLIDER_START_POS.x + 1)
194 / VOLUME_OPTION_MAX;
195
196// Values in pixels
197static const BG_POINT OPTIONS_GAME_SPEED_TEXT_POS = { 82, 16};
198static const BG_POINT OPTIONS_CARD_SPRITES_TEXT_POS = { 72, 48};
199static const BG_POINT OPTIONS_MUSIC_VOLUME_TEXT_POS = { 56, 80};
200static const BG_POINT OPTIONS_MUSIC_VALUE_TEXT_POS = {160, 80};
201static const BG_POINT OPTIONS_SOUND_VOLUME_TEXT_POS = { 56, 104};
202static const BG_POINT OPTIONS_SOUND_VALUE_TEXT_POS = {160, 104};
203static const BG_POINT OPTIONS_BACK_SAVE_TEXT_POS = { 64, 136};
204// clang-format on
205
206#define GAME_SPEED_ARROW_HIGHLIGHT_DURATION 10
207enum OptionSpeedButtons game_speed_arrow_highlight_button = GAME_SPEED_UP_BTN_IDX;
208static s32 game_speed_arrow_highlight_start = UNDEFINED;
209
210static void disable_all_game_speed_outlines_except_self(enum OptionSpeedButtons highlighted_btn)
211{
212 for (int i = 0; i < NB_GAME_SPEED_BUTTONS; i++)
213 {
214 button_set_highlight(&game_speed_buttons[i], i == highlighted_btn);
215 }
216}
217
218static void disable_all_outlines_except_self(Selection sel_btn)
219{
220 // These two get disabled no matter what here
221 disable_all_game_speed_outlines_except_self(NB_GAME_SPEED_BUTTONS);
222
223 for (int j = 0; j < NB_OPTIONS_ROWS; j++)
224 {
225 int nb_buttons_in_row;
226
227 switch (j)
228 {
229 case HIGH_CONTRAST_ROW_IDX:
230 nb_buttons_in_row = options_menu_return_card_sprites_row_size();
231 break;
232 case SAVE_BACK_ROW_IDX:
233 nb_buttons_in_row = options_menu_return_bottom_row_size();
234 break;
235 default:
236 nb_buttons_in_row = options_menu_return_upper_rows_size();
237 break;
238 }
239
240 for (int i = 0; i < nb_buttons_in_row; i++)
241 {
242 button_set_highlight(&options_menu_buttons[j][i], i == sel_btn.x && j == sel_btn.y);
243 }
244 }
245}
246
247static void update_game_speed_button_graphics()
248{
249 // check if need to disable game speed arrows
250
251 Rect speed_down_btn_tiles = (g_game_vars.game_speed == GAME_SPEED_MIN)
252 ? OPTIONS_SPEED_DOWN_DISABLED_BTN_SRC_RECT
253 : OPTIONS_SPEED_DOWN_ACTIVE_BTN_SRC_RECT;
254 main_bg_se_copy_rect(speed_down_btn_tiles, OPTIONS_SPEED_DOWN_BTN_DEST_POS);
255
256 Rect speed_up_btn_tiles = (g_game_vars.game_speed == GAME_SPEED_MAX)
257 ? OPTIONS_SPEED_UP_DISABLED_BTN_SRC_RECT
258 : OPTIONS_SPEED_UP_ACTIVE_BTN_SRC_RECT;
259 main_bg_se_copy_rect(speed_up_btn_tiles, OPTIONS_SPEED_UP_BTN_DEST_POS);
260
262 OPTIONS_SPEED_VALUES[g_game_vars.game_speed - 1],
263 OPTIONS_SPEED_VALUE_DEST_POS
264 );
265}
266
267static void update_high_contrast_button_graphics(void)
268{
269 Rect contrast_btn_tiles = (get_cards_high_contrast()) ? OPTIONS_CONTRAST_CHECK_YES_SRC_RECT
270 : OPTIONS_CONTRAST_CHECK_NO_SRC_RECT;
271 main_bg_se_copy_rect(contrast_btn_tiles, OPTIONS_CONTRAST_CHECK_DEST_POS);
272}
273
274static void update_more_readable_button_graphics(void)
275{
276 Rect readable_btn_tiles = (get_cards_more_readable()) ? OPTIONS_READABLE_CHECK_YES_SRC_RECT
277 : OPTIONS_READABLE_CHECK_NO_SRC_RECT;
278 main_bg_se_copy_rect(readable_btn_tiles, OPTIONS_READABLE_CHECK_DEST_POS);
279}
280
281static void update_volume_slider_graphics(enum OptionButtonRows sel_row)
282{
283 u8 slider_value;
284 BG_POINT slider_segment_dest;
285 int slider_segment_length;
286 Rect slider_segment_full;
287 Rect slider_segment_mid;
288 Rect slider_segment_empty;
289 BG_POINT slider_text_pos;
290
291 switch (sel_row)
292 {
293 case MUSIC_VOLUME_ROW_IDX:
294 slider_value = g_game_vars.music_volume;
295 slider_segment_dest = OPTIONS_MUSIC_SLIDER_START_POS;
296 slider_segment_length = OPTIONS_MUSIC_SLIDER_SEGMENT_LENGTH;
297 slider_segment_full = OPTIONS_MUSIC_SLIDER_FULL_SRC;
298 slider_segment_mid = OPTIONS_MUSIC_SLIDER_MID_SRC;
299 slider_segment_empty = OPTIONS_MUSIC_SLIDER_EMPTY_SRC;
300 slider_text_pos = OPTIONS_MUSIC_VALUE_TEXT_POS;
301 break;
302 case SOUND_VOLUME_ROW_IDX:
303 slider_value = g_game_vars.sound_volume;
304 slider_segment_dest = OPTIONS_SOUND_SLIDER_START_POS;
305 slider_segment_length = OPTIONS_SOUND_SLIDER_SEGMENT_LENGTH;
306 slider_segment_full = OPTIONS_SOUND_SLIDER_FULL_SRC;
307 slider_segment_mid = OPTIONS_SOUND_SLIDER_MID_SRC;
308 slider_segment_empty = OPTIONS_SOUND_SLIDER_EMPTY_SRC;
309 slider_text_pos = OPTIONS_SOUND_VALUE_TEXT_POS;
310 break;
311 // This function only takes in a volume slider
312 default:
313 return;
314 }
315
316 int i = 0;
317
318 // full part of the bar
319 for (; i < slider_value * slider_segment_length - 1; i++)
320 {
321 main_bg_se_copy_rect(slider_segment_full, slider_segment_dest);
322 slider_segment_dest.x++;
323 }
324
325 // at exactly `slider_value` we either:
326 // - are in the middle of the bar, then we draw the frontier between full and empty tiles
327 // - are at the end, then draw a full segment
328 if (slider_value == VOLUME_OPTION_MAX)
329 {
330 main_bg_se_copy_rect(slider_segment_full, slider_segment_dest);
331 }
332 else
333 {
334 if (slider_value != VOLUME_OPTION_MIN)
335 {
336 // draw middle point
337 main_bg_se_copy_rect(slider_segment_mid, slider_segment_dest);
338 i++;
339 slider_segment_dest.x++;
340 }
341
342 // empty part of the bar
343 for (; i < VOLUME_OPTION_MAX * slider_segment_length; i++)
344 {
345 main_bg_se_copy_rect(slider_segment_empty, slider_segment_dest);
346 slider_segment_dest.x++;
347 }
348 }
349
350 tte_printf(
351 "#{P:%d,%d; cx:0x%X000}%3d",
352 slider_text_pos.x,
353 slider_text_pos.y,
354 TTE_WHITE_PB,
355 (slider_value * VOLUME_OPTION_INCREMENT)
356 );
357}
358
360{
361 tte_erase_screen();
362
363 GRIT_CPY(pal_bg_mem, background_options_menu_gfxPal);
364 GRIT_CPY(&tile_mem[MAIN_BG_CBB], background_options_menu_gfxTiles);
365 GRIT_CPY(&se_mem[MAIN_BG_SBB], background_options_menu_gfxMap);
366
367 tte_printf(
368 "#{P:%d,%d; cx:0x%X000}Game Speed",
369 OPTIONS_GAME_SPEED_TEXT_POS.x,
370 OPTIONS_GAME_SPEED_TEXT_POS.y,
371 TTE_WHITE_PB
372 );
373 tte_printf(
374 "#{P:%d,%d; cx:0x%X000}Card Sprites",
375 OPTIONS_CARD_SPRITES_TEXT_POS.x,
376 OPTIONS_CARD_SPRITES_TEXT_POS.y,
377 TTE_WHITE_PB
378 );
379 tte_printf(
380 "#{P:%d,%d; cx:0x%X000}Music Volume",
381 OPTIONS_MUSIC_VOLUME_TEXT_POS.x,
382 OPTIONS_MUSIC_VOLUME_TEXT_POS.y,
383 TTE_WHITE_PB
384 );
385 tte_printf(
386 "#{P:%d,%d; cx:0x%X000}Sound Volume",
387 OPTIONS_SOUND_VOLUME_TEXT_POS.x,
388 OPTIONS_SOUND_VOLUME_TEXT_POS.y,
389 TTE_WHITE_PB
390 );
391 tte_printf(
392 "#{P:%d,%d; cx:0x%X000}Save Cancel",
393 OPTIONS_BACK_SAVE_TEXT_POS.x,
394 OPTIONS_BACK_SAVE_TEXT_POS.y,
395 TTE_WHITE_PB
396 );
397}
398
400{
401 change_background(BG_OPTIONS_MENU, false);
402
403 // Select game speed button by default
404 options_menu_selection_grid.selection = OPTIONS_MENU_INIT_SEL;
405 disable_all_outlines_except_self(OPTIONS_MENU_INIT_SEL);
406
407 // Do an update on the first frame
408 update_game_speed_button_graphics();
409 update_high_contrast_button_graphics();
410 update_more_readable_button_graphics();
411 update_volume_slider_graphics(MUSIC_VOLUME_ROW_IDX);
412 update_volume_slider_graphics(SOUND_VOLUME_ROW_IDX);
413}
414
416{
417 selection_grid_process_input(&options_menu_selection_grid);
418
419 // game speed arrows small animation: they stay highlighted for a few frames
420 if (game_speed_arrow_highlight_start != UNDEFINED &&
421 (g_game_vars.timer - game_speed_arrow_highlight_start) >
422 GAME_SPEED_ARROW_HIGHLIGHT_DURATION)
423 {
424 game_speed_arrow_highlight_start = UNDEFINED;
425 button_set_highlight(&game_speed_buttons[game_speed_arrow_highlight_button], false);
426 }
427}
428
430{
431 tte_erase_screen();
432}
433
434static void game_speed_down_on_pressed(void)
435{
436 g_game_vars.game_speed--;
437 game_speed_arrow_highlight_start = g_game_vars.timer;
438 game_speed_arrow_highlight_button = GAME_SPEED_DOWN_BTN_IDX;
439 disable_all_game_speed_outlines_except_self(GAME_SPEED_DOWN_BTN_IDX);
440 update_game_speed_button_graphics();
441}
442
443static void game_speed_up_on_pressed(void)
444{
445 g_game_vars.game_speed++;
446 game_speed_arrow_highlight_start = g_game_vars.timer;
447 game_speed_arrow_highlight_button = GAME_SPEED_UP_BTN_IDX;
448 disable_all_game_speed_outlines_except_self(GAME_SPEED_UP_BTN_IDX);
449 update_game_speed_button_graphics();
450}
451
455static void high_contrast_on_pressed(void)
456{
457 set_cards_high_contrast(!get_cards_high_contrast());
458 update_high_contrast_button_graphics();
459}
460
464static void more_readable_on_pressed(void)
465{
466 set_cards_more_readable(!get_cards_more_readable());
467 update_more_readable_button_graphics();
468}
469
473static void save_on_pressed(void)
474{
475 save_options();
476 game_change_state(GAME_STATE_MAIN_MENU);
477}
478
482static void back_on_pressed(void)
483{
484 load_options();
485 game_change_state(GAME_STATE_MAIN_MENU);
486}
487
493static int options_menu_return_upper_rows_size(void)
494{
495 return 1;
496}
497
503static int options_menu_return_card_sprites_row_size(void)
504{
505 return 2;
506}
507
513static int options_menu_return_bottom_row_size(void)
514{
515 return 2;
516}
517
518static void change_button_highlight(
519 int row_idx,
520 const Selection* prev_selection,
521 const Selection* new_selection
522)
523{
524 if (prev_selection->y == row_idx)
525 {
526 button_set_highlight(&options_menu_buttons[row_idx][prev_selection->x], false);
527 }
528
529 if (new_selection->y == row_idx)
530 {
531 play_sfx(SFX_BUTTON, MM_BASE_PITCH_RATE, BUTTON_SFX_VOLUME);
532 button_set_highlight(&options_menu_buttons[row_idx][new_selection->x], true);
533 }
534}
535
542static bool game_speed_row_on_selection_changed(
543 SelectionGrid* selection_grid,
544 int row_idx,
545 const Selection* prev_selection,
546 const Selection* new_selection
547)
548{
549 change_button_highlight(row_idx, prev_selection, new_selection);
550 disable_all_game_speed_outlines_except_self(NB_GAME_SPEED_BUTTONS);
551
552 // Can only change game speed by pressing left/right while staying on the button's row
553 if (prev_selection->y != new_selection->y)
554 return true;
555
556 if (key_hit(KEY_LEFT) && g_game_vars.game_speed > GAME_SPEED_MIN)
557 {
558 button_press(&game_speed_buttons[GAME_SPEED_DOWN_BTN_IDX]);
559 }
560 else if (key_hit(KEY_RIGHT) && g_game_vars.game_speed < GAME_SPEED_MAX)
561 {
562 button_press(&game_speed_buttons[GAME_SPEED_UP_BTN_IDX]);
563 }
564
565 return true;
566}
567
573static void options_menu_row_on_key_transit(SelectionGrid* selection_grid, Selection* selection)
574{
575 if (key_hit(SELECT_CARD))
576 {
577 button_press(&options_menu_buttons[selection->y][selection->x]);
578 }
579}
580
581static bool regular_button_row_on_selection_changed(
582 SelectionGrid* selection_grid,
583 int row_idx,
584 const Selection* prev_selection,
585 const Selection* new_selection
586)
587{
588 change_button_highlight(row_idx, prev_selection, new_selection);
589 return true;
590}
591
592static bool music_volume_row_on_selection_changed(
593 SelectionGrid* selection_grid,
594 int row_idx,
595 const Selection* prev_selection,
596 const Selection* new_selection
597)
598{
599 change_button_highlight(row_idx, prev_selection, new_selection);
600
601 if (prev_selection->y != new_selection->y)
602 return true;
603
604 if (key_hit(KEY_LEFT) && g_game_vars.music_volume > VOLUME_OPTION_MIN)
605 {
606 g_game_vars.music_volume--;
607 }
608 else if (key_hit(KEY_RIGHT) && g_game_vars.music_volume < VOLUME_OPTION_MAX)
609 {
610 g_game_vars.music_volume++;
611 }
612
613 update_volume_slider_graphics(MUSIC_VOLUME_ROW_IDX);
614 set_volume(volume_module_step_to_val(g_game_vars.music_volume));
615
616 return true;
617}
618
619static bool sound_volume_row_on_selection_changed(
620 SelectionGrid* selection_grid,
621 int row_idx,
622 const Selection* prev_selection,
623 const Selection* new_selection
624)
625{
626 change_button_highlight(row_idx, prev_selection, new_selection);
627
628 if (prev_selection->y != new_selection->y)
629 return true;
630
631 if (key_hit(KEY_LEFT) && g_game_vars.sound_volume > VOLUME_OPTION_MIN)
632 {
633 g_game_vars.sound_volume--;
634 }
635 else if (key_hit(KEY_RIGHT) && g_game_vars.sound_volume < VOLUME_OPTION_MAX)
636 {
637 g_game_vars.sound_volume++;
638 }
639
640 update_volume_slider_graphics(SOUND_VOLUME_ROW_IDX);
641
642 return true;
643}
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 game_options_menu_on_exit(void)
Options menu cleanup (called when going back to main menu)
void game_options_menu_on_init(void)
Options menu state initialization.
void game_options_menu_on_update(void)
Options menu state update.
void game_options_menu_change_background(void)
Change the options menu background.
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.