GBAlatro
A Demake of Balatro for the GBA
Loading...
Searching...
No Matches
game.c
1#include "game.h"
2
3#include "affine_background.h"
4#include "affine_background_gfx.h"
5#include "audio_utils.h"
6#include "background_main_menu_gfx.h"
7#include "button.h"
8#include "card.h"
9#include "game/blind_select.h"
10#include "game/common_ui.h"
11#include "game/game_over.h"
12#include "game/joker_row.h"
13#include "game/main_menu.h"
14#include "game/options_menu.h"
15#include "game/round.h"
16#include "game/round_end.h"
17#include "game/run_setup.h"
18#include "game/shop.h"
19#include "game_variables.h"
20#include "graphic_utils.h"
21#include "hand.h"
22#include "joker.h"
23#include "layout.h"
24#include "list.h"
25#include "random.h"
26#include "save.h"
27#include "selection_grid.h"
28#include "skip_tag.h"
29#include "soundbank.h"
30#include "splash_screen.h"
31#include "sprite.h"
32#include "state_machine.h"
33#include "timer.h"
34#include "tonc_memdef.h"
35#include "util.h"
36
37#include <maxmod.h>
38#include <stdint.h>
39#include <stdlib.h>
40#include <string.h>
41
42#define STRAIGHT_AND_FLUSH_SIZE_FOUR_FINGERS 4
43#define STRAIGHT_AND_FLUSH_SIZE_DEFAULT 5
44
45// SE sizes
46
47#define STARTING_ROUND 0
48#define STARTING_ANTE 1
49#define STARTING_MONEY 4
50#define STARTING_SCORE 0
51
52#define EXPIRE_ANIMATION_FRAME_COUNT 3
53
54// These functions need to be forward declared
55// so they're visible to the state_info array,
56// and the sub-state function tables.
57// This could be done, and maybe should be done,
58// with an X macro, but I'll leave that to the
59// reviewer(s).
60
61int deck_get_size(void);
62static int deck_get_max_size(void);
63
64// Consts
65
66// clang-format off
67// Rects left top right bottom
68
69// Rects for TTE (in pixels)
70
71// Score displayed in the same place as the hand type
72static const Rect TEMP_SCORE_RECT = {8, 64, 64, 72 };
73static const Rect SCORE_RECT = {24, 48, 64, 56 };
74
75static const Rect MONEY_TEXT_RECT = {8, 120, 64, 128 };
76static const Rect CHIPS_TEXT_RECT = {8, 80, 32, 88 };
77static const Rect MULT_TEXT_RECT = {40, 80, 64, 88 };
78
79// Rects with UNDEFINED are only used in tte_printf, they need to be fully defined
80// to be used with tte_erase_rect_wrapper()
81static const Rect HANDS_TEXT_RECT = {16, 104, UNDEFINED, UNDEFINED };
82static const Rect DISCARDS_TEXT_RECT = {48, 104, UNDEFINED, UNDEFINED };
83static const Rect DECK_SIZE_RECT = {200, 152, 240, 160 };
84static const Rect ROUND_TEXT_RECT = {48, 144, UNDEFINED, UNDEFINED };
85static const Rect ANTE_TEXT_RECT = {8, 144, UNDEFINED, UNDEFINED };
86// clang-format on
87
88static StateInfo state_info[] = {
89#define DEF_STATE_INFO(stateEnum, init_fn, update_fn, exit_fn) \
90 {.on_init = init_fn, .on_update = update_fn, .on_exit = exit_fn},
91#include "../include/def_state_info_table.h"
92#undef DEF_STATE_INFO
93};
94
95static StateMachine game_sm = STATE_MACHINE_DEFINE(state_info, GAME_STATE_MAX);
96
97// Initialization of the global vars
98// clang-format off
99GameVariables g_game_vars = {
100 .timer = 0,
101 // Setting the seed to an invalid value so that the Run Setup screen knows we're not reusing a previous Run's seed
102 .rng_info = {UNDEFINED, {0}},
103
104 .score = 0, .chips = 0, .mult = 0,
105 .hands = 0, .discards = 0,
106 .money = 0, .hand_size = DEFAULT_HAND_SIZE,
107 .ante = 0, .round = 0,
108 .deck = DECK_TYPE_RED,
109 .nb_played_hands = {0},
110
111 .best_hand_score = 0,
112 .nb_skipped_rounds = 0,
113 .nb_unused_discards = 0,
114
115 .current_blind = BLIND_TYPE_SMALL,
116 .next_boss_blind = BLIND_TYPE_BIG,
117 .blinds_states =
118 {
119 BLIND_STATE_CURRENT,
120 BLIND_STATE_UPCOMING,
121 BLIND_STATE_UPCOMING
122 },
123
124 .playing_blind_token = NULL,
125 .round_end_blind_token = NULL,
126
127 .game_speed = DEFAULT_GAME_SPEED,
128 .music_volume = DEFAULT_MUSIC_VOLUME,
129 .sound_volume = DEFAULT_SOUND_VOLUME,
130};
131// clang-format on
132
133static List s_owned_jokers_list;
134static List s_discarded_items_list;
135static List s_expired_jokers_list;
136
137// Stacks
138static Card* s_deck[MAX_DECK_SIZE] = {NULL};
139static int s_deck_top = -1;
140
141// Joker Special Variables
142static int s_shortcut_joker_count = 0;
143
144static int s_four_fingers_joker_count = 0;
145
146void deck_push(Card* card)
147{
148 if (s_deck_top >= MAX_DECK_SIZE - 1)
149 return;
150 s_deck[++s_deck_top] = card;
151}
152
153Card* deck_pop(void)
154{
155 if (s_deck_top < 0)
156 return NULL;
157 return s_deck[s_deck_top--];
158}
159
160void display_ante(void)
161{
162 tte_printf(
163 "#{P:%d,%d; cx:0x%X000}%ld#{cx:0x%X000}/%d",
164 ANTE_TEXT_RECT.left,
165 ANTE_TEXT_RECT.top,
166 TTE_YELLOW_PB,
167 g_game_vars.ante,
168 TTE_WHITE_PB,
169 MAX_ANTE
170 );
171}
172
173void game_init()
174{
175 state_machine_remove(&game_sm);
176 state_machine_register(&game_sm);
177 // Initialize all jokers list once
178 s_owned_jokers_list = list_init();
179 s_discarded_items_list = list_init();
180 s_expired_jokers_list = list_init();
181 // TODO: Move this to an initialization of the play scoring states
182
183 shop_reset();
184
185 g_game_vars.timer = TM_ZERO;
186 g_game_vars.score = STARTING_SCORE;
187 g_game_vars.chips = 0;
188 g_game_vars.mult = 0;
189 g_game_vars.hands = MAX_HANDS;
190 g_game_vars.discards = MAX_DISCARDS;
191 g_game_vars.money = STARTING_MONEY;
192 g_game_vars.hand_size = DEFAULT_HAND_SIZE;
193 g_game_vars.ante = STARTING_ANTE;
194 g_game_vars.round = STARTING_ROUND;
195 g_game_vars.deck = DECK_TYPE_RED;
196 for (int i = 0; i < HAND_TYPE_MAX; i++)
197 g_game_vars.nb_played_hands[i] = 0;
198 g_game_vars.best_hand_score = 0;
199 g_game_vars.nb_skipped_rounds = 0;
200 g_game_vars.nb_unused_discards = 0;
201 g_game_vars.current_blind = BLIND_TYPE_SMALL;
202 g_game_vars.blinds_states[0] = BLIND_STATE_CURRENT;
203 g_game_vars.blinds_states[1] = BLIND_STATE_UPCOMING;
204 g_game_vars.blinds_states[2] = BLIND_STATE_UPCOMING;
205}
206
207void game_reset()
208{
209 while (list_get_len(&s_owned_jokers_list) > 0)
210 {
211 JokerObject* joker_object = list_get_at_idx(&s_owned_jokers_list, 0);
212 remove_owned_joker(0);
213 joker_object_destroy(&joker_object);
214 }
215
216 tte_erase_screen();
217
218 // For some reason that I haven't figured out yet,
219 // if I don't destroy the blind tokens they won't
220 // show up on the next run.
221 sprite_destroy(&g_game_vars.playing_blind_token);
222 sprite_destroy(&g_game_vars.round_end_blind_token);
223
224 list_clear(&s_owned_jokers_list);
225 list_clear(&s_discarded_items_list);
226 list_clear(&s_expired_jokers_list);
227
228 game_init();
230
231 display_round();
232 display_score(g_game_vars.score);
233 display_chips();
234 display_mult();
235 display_hands();
236 display_discards();
237 display_money();
238 // Ante
239 display_ante();
240
241 affine_background_load_palette(affine_background_gfxPal);
242}
243
244void item_start_discard_animation(Item* item)
245{
246 item->tx = int2fx(ITEM_DISCARD_TARGET.x);
247 item->ty = int2fx(ITEM_DISCARD_TARGET.y);
248 list_push_back(&s_discarded_items_list, item);
249}
250
251static inline void discarded_items_update_loop(void)
252{
253 if (list_is_empty(&s_discarded_items_list))
254 {
255 return;
256 }
257
258 ListItr itr = list_itr_create(&s_discarded_items_list);
259 Item* item;
260
261 while ((item = list_itr_next(&itr)))
262 {
263 if (item->x == item->tx && item->y == item->ty)
264 {
266 item_dispose(&item);
267 }
268 }
269}
270
271static inline void held_jokers_update_loop(void)
272{
273 static const int SPACING_LUT[MAX_JOKERS_HELD_SIZE][MAX_JOKERS_HELD_SIZE] = {
274 {0, 0, 0, 0, 0 },
275 {13, -13, 0, 0, 0 },
276 {26, 0, -26, 0, 0 },
277 {39, 13, -13, -39, 0 },
278 {40, 20, 0, -20, -40}
279 };
280
281 FIXED hand_x = int2fx(HELD_JOKERS_POS.x);
282
283 ListItr itr = list_itr_create(&s_owned_jokers_list);
284 JokerObject* joker;
285 int jokers_top = list_get_len(&s_owned_jokers_list) - 1;
286 int i = 0;
287 while ((joker = list_itr_next(&itr)))
288 {
289 // Let the Shop handle the position of this Joker
290 if (joker != (JokerObject*)shop_get_description_item())
291 joker->tx = hand_x - int2fx(SPACING_LUT[jokers_top][i]);
292 i++;
293 }
294}
295
296bool joker_object_can_acquire(Item* joker_object)
297{
298 GBAL_RETURN_IF_NULL(joker_object, false);
299 return (list_get_len(get_jokers_list()) < MAX_JOKERS_HELD_SIZE);
300}
301
302static inline void expired_jokers_update_loop(void)
303{
304 if (list_is_empty(&s_expired_jokers_list))
305 {
306 return;
307 }
308
309 ListItr itr = list_itr_create(&s_expired_jokers_list);
310 JokerObject* joker_object;
311
312 while ((joker_object = list_itr_next(&itr)))
313 {
314 // let just enough frames pass that we see it rotating and shrinking
315 if (g_game_vars.timer % FRAMES(EXPIRE_ANIMATION_FRAME_COUNT) == 0)
316 {
317 // get joker idx
318 int expired_joker_idx = 0;
319 ListItr joker_itr = list_itr_create(&s_owned_jokers_list);
320 JokerObject* expired_joker;
321 while ((expired_joker = list_itr_next(&joker_itr)) && expired_joker != joker_object)
322 {
323 expired_joker_idx++;
324 }
325
326 // Removing expired Jokers here, instead of immediately like ones we
327 // sell or discard allow us to have a small shrink animation without
328 // the other owned Jokers rearranging themselves to fill the newly
329 // freed space, therefore obscuring the animation
330 remove_owned_joker(expired_joker_idx);
332 joker_object_destroy(&joker_object);
333 }
334 }
335}
336
337static inline void jokers_update_loop(void)
338{
339 held_jokers_update_loop();
340 expired_jokers_update_loop();
341}
342
343void game_update()
344{
345 rng_update();
346
347 g_game_vars.timer++;
348
349 jokers_update_loop();
350 discarded_items_update_loop();
351
353
355}
356
357void game_change_state(enum GameState new_game_state)
358{
359 g_game_vars.timer = TM_ZERO; // Reset the timer
360
361 state_machine_change_state(&game_sm, new_game_state);
362}
363
364enum GameState game_get_state(void)
365{
366 return game_sm.state;
367}
368
369bool is_joker_owned(int joker_id)
370{
371 ListItr itr = list_itr_create(&s_owned_jokers_list);
372 JokerObject* joker;
373
374 while ((joker = list_itr_next(&itr)))
375 {
376 if (joker->joker->id == joker_id)
377 {
378 return true;
379 }
380 }
381 return false;
382}
383
384List* get_jokers_list(void)
385{
386 return &s_owned_jokers_list;
387}
388
389List* get_expired_jokers_list(void)
390{
391 return &s_expired_jokers_list;
392}
393
394bool is_shortcut_joker_active(void)
395{
396 return s_shortcut_joker_count > 0;
397}
398
399int get_straight_and_flush_size(void)
400{
401 return s_four_fingers_joker_count > 0 ? STRAIGHT_AND_FLUSH_SIZE_FOUR_FINGERS
402 : STRAIGHT_AND_FLUSH_SIZE_DEFAULT;
403}
404
405void add_joker(JokerObject* joker_object)
406{
407 list_push_back(&s_owned_jokers_list, joker_object);
408 joker_object->ty = int2fx(HELD_JOKERS_POS.y);
409
410 // TODO: Extract to on_joker_added() callback
411 // In case the player gets multiple Four Fingers Jokers,
412 // only change size when the first one is added
413 if (joker_object->joker->id == FOUR_FINGERS_JOKER_ID)
414 {
415 s_four_fingers_joker_count++;
416 }
417
418 if (joker_object->joker->id == SHORTCUT_JOKER_ID)
419 {
420 s_shortcut_joker_count++;
421 }
422}
423
424void remove_owned_joker(int owned_joker_idx)
425{
426 // TODO: Extract to on_joker_removed() callback
427 JokerObject* joker_object = list_get_at_idx(&s_owned_jokers_list, owned_joker_idx);
428 // In case the player gets multiple Four Fingers Jokers,
429 // and only reset the size when all of them have been removed
430 if (joker_object->joker->id == FOUR_FINGERS_JOKER_ID)
431 {
432 s_four_fingers_joker_count--;
433 }
434
435 if (joker_object->joker->id == SHORTCUT_JOKER_ID)
436 {
437 s_shortcut_joker_count--;
438 }
439
440 // TODO: Move to site of joker_destroy()?
441 joker_set_rollable(joker_object->joker->id, true);
442 list_remove_at_idx(&s_owned_jokers_list, owned_joker_idx);
443}
444
445int get_deck_top(void)
446{
447 return s_deck_top;
448}
449
450int get_num_discards_remaining(void)
451{
452 return g_game_vars.discards;
453}
454
455int get_num_hands_remaining(void)
456{
457 return g_game_vars.hands;
458}
459
460void display_money()
461{
462 Rect money_text_rect = MONEY_TEXT_RECT;
463 tte_erase_rect_wrapper(MONEY_TEXT_RECT);
464
465 char money_str_buff[INT_MAX_DIGITS + 2]; // + 2 for null terminator and "$" sign
466 snprintf(money_str_buff, sizeof(money_str_buff), "$%ld", g_game_vars.money);
467
468 // Bias left so the number is centered and the "$" sign is on the left
469 update_text_rect_to_center_str(&money_text_rect, money_str_buff, SCREEN_LEFT);
470
471 tte_printf(
472 "#{P:%d,%d; cx:0x%X000}%s",
473 money_text_rect.left,
474 money_text_rect.top,
475 TTE_YELLOW_PB,
476 money_str_buff
477 );
478}
479
480void display_chips(void)
481{
482 Rect chips_text_rect = CHIPS_TEXT_RECT;
483
484 // In case of overflow, the rect overflow left by 1 char
485 Rect chips_text_overflow_rect = chips_text_rect;
486 chips_text_overflow_rect.left -= TTE_CHAR_SIZE;
487 tte_erase_rect_wrapper(chips_text_overflow_rect);
488
489 char chips_str_buff[UINT_MAX_DIGITS + 1];
491 g_game_vars.chips,
492 rect_width(&chips_text_rect) / TTE_CHAR_SIZE,
493 chips_str_buff
494 );
495
496 update_text_rect_to_right_align_str(&chips_text_rect, chips_str_buff, OVERFLOW_LEFT);
497
498 tte_printf(
499 "#{P:%d,%d; cx:0x%X000;}%s",
500 chips_text_rect.left,
501 chips_text_rect.top,
502 TTE_WHITE_PB,
503 chips_str_buff
504 );
506}
507
508void display_mult(void)
509{
510 Rect mult_text_overflow_rect = MULT_TEXT_RECT;
511 // In case of overflow the rect will overflow right by 1 char
512 mult_text_overflow_rect.right += TTE_CHAR_SIZE;
513 tte_erase_rect_wrapper(mult_text_overflow_rect);
514
515 char mult_str_buff[UINT_MAX_DIGITS + 1];
517 g_game_vars.mult,
518 rect_width(&MULT_TEXT_RECT) / TTE_CHAR_SIZE,
519 mult_str_buff
520 );
521
522 tte_printf(
523 "#{P:%d,%d; cx:0x%X000;}%s",
524 MULT_TEXT_RECT.left,
525 MULT_TEXT_RECT.top,
526 TTE_WHITE_PB,
527 mult_str_buff
528 );
529
531}
532
533void display_deck_size_max(void)
534{
535 // TODO: the text will overflow if deck max size exceeds 99,
536 // we will need a fix at some point for this
537 tte_erase_rect_wrapper(DECK_SIZE_RECT);
538 tte_printf(
539 "#{P:%d,%d; cx:0x%X000}%d/%d",
540 DECK_SIZE_RECT.left,
541 DECK_SIZE_RECT.top,
542 TTE_WHITE_PB,
543 deck_get_size(),
544 deck_get_max_size()
545 );
546}
547
548// Returns true if the card is *considered* a face card
549bool card_is_face(Card* card)
550{
551 // Card is a face card, or Pareidolia is present
552 return (
553 card->rank == JACK || card->rank == QUEEN || card->rank == KING ||
554 is_joker_owned(PAREIDOLIA_JOKER_ID)
555 );
556}
557
558void display_temp_score(u32 value)
559{
560 char temp_score_str_buff[UINT_MAX_DIGITS + 1];
561 Rect temp_score_rect = TEMP_SCORE_RECT;
563 value,
564 rect_width(&temp_score_rect) / TTE_CHAR_SIZE,
565 temp_score_str_buff
566 );
567 update_text_rect_to_center_str(&temp_score_rect, temp_score_str_buff, SCREEN_RIGHT);
568
569 tte_erase_rect_wrapper(TEMP_SCORE_RECT);
570 tte_printf(
571 "#{P:%d,%d; cx:0x%X000}%s",
572 temp_score_rect.left,
573 temp_score_rect.top,
574 TTE_WHITE_PB,
575 temp_score_str_buff
576 );
577}
578
579void erase_temp_score(void)
580{
581 tte_erase_rect_wrapper(TEMP_SCORE_RECT);
582}
583
584void display_score(u32 value)
585{
586 Rect score_rect = SCORE_RECT;
587 // Clear the existing text before redrawing
588 tte_erase_rect_wrapper(SCORE_RECT);
589
590 char score_str_buff[UINT_MAX_DIGITS + 1];
591
592 truncate_uint_to_suffixed_str(value, rect_width(&score_rect) / TTE_CHAR_SIZE, score_str_buff);
593 update_text_rect_to_center_str(&score_rect, score_str_buff, SCREEN_RIGHT);
594
595 tte_printf(
596 "#{P:%d,%d; cx:0x%X000}%s",
597 score_rect.left,
598 score_rect.top,
599 TTE_WHITE_PB,
600 score_str_buff
601 );
602}
603
604void display_round(void)
605{
606 // tte_erase_rect_wrapper(ROUND_TEXT_RECT);
607 tte_printf(
608 "#{P:%d,%d; cx:0x%X000}%ld",
609 ROUND_TEXT_RECT.left,
610 ROUND_TEXT_RECT.top,
611 TTE_YELLOW_PB,
612 g_game_vars.round
613 );
614}
615
616void display_hands(void)
617{
618 tte_printf(
619 "#{P:%d,%d; cx:0x%X000}%ld",
620 HANDS_TEXT_RECT.left,
621 HANDS_TEXT_RECT.top,
622 TTE_BLUE_PB,
623 g_game_vars.hands
624 );
625}
626
627void display_discards(void)
628{
629 tte_printf(
630 "#{P:%d,%d; cx:0x%X000}%ld",
631 DISCARDS_TEXT_RECT.left,
632 DISCARDS_TEXT_RECT.top,
633 TTE_RED_PB,
634 g_game_vars.discards
635 );
636}
637
638int deck_get_size(void)
639{
640 return s_deck_top + 1;
641}
642
643static int deck_get_max_size(void)
644{
645 // This is the max amount of cards that the player currently has in their possession
646 return get_hand_top() + get_played_top() + s_deck_top + get_discard_top() + 4;
647}
648
649void deck_shuffle(void)
650{
651 for (int i = s_deck_top; i > 0; i--)
652 {
653 int j = rng_get_u32(RNG_SEQ_CARD_SHUFFLE) % (i + 1);
654 Card* temp = s_deck[i];
655 s_deck[i] = s_deck[j];
656 s_deck[j] = temp;
657 }
658}
659
660void game_start(void)
661{
663 tte_colors_setup();
664
665 g_game_vars.hands = MAX_HANDS;
666 g_game_vars.discards = MAX_DISCARDS;
667
668 // Fill the deck with all the cards. Later on this can be replaced with a more dynamic system
669 // that allows for different decks and card types.
670 for (int suit = 0; suit < NUM_SUITS; suit++)
671 {
672 for (int rank = 0; rank < NUM_RANKS; rank++)
673 {
674 Card* card = card_new(suit, rank);
675 deck_push(card);
676 }
677 }
678
679 // Deck size/max size
680 tte_erase_rect_wrapper(DECK_SIZE_RECT);
681 tte_printf(
682 "#{P:%d,%d; cx:0x%X000}%d/%d",
683 DECK_SIZE_RECT.left,
684 DECK_SIZE_RECT.top,
685 TTE_WHITE_PB,
686 deck_get_size(),
687 deck_get_max_size()
688 );
689
690 display_round(); // Set the round display
691 display_score(g_game_vars.score); // Set the score display
692
693 display_chips(); // Set the chips display
694 display_mult(); // Set the multiplier display
695
696 display_hands(); // Hand
697 display_discards(); // Discard
698
699 display_money(); // Set the money display
700 display_ante();
701
702 game_change_state(GAME_STATE_BLIND_SELECT);
703}
Utilities for affine background on GBA.
@ AFFINE_BG_GAME
Display background for game play.
void affine_background_change_background(enum AffineBackgroundID new_bg)
Update the background id.
void affine_background_load_palette(const u16 *src)
Set the background palette.
Utilities for using maxmod to play sound effects.
void init_unbeaten_blinds_lists(void)
Initialize Boss and Showdown Blinds lists to roll from.
Definition blind.c:151
Blind select state functions.
A button structure containing the common button functionalities Including highlight and pressing func...
Common functions to render UI elements.
Game Over screen state functions.
Game global game variables struct definition.
Graphic utility functions.
void update_text_rect_to_right_align_str(Rect *rect, const char *str, enum OverflowDir overflow_direction)
Changes rect->left so it fits the digits of num exactly when right aligned to rect->right....
void update_text_rect_to_center_str(Rect *rect, const char *str, enum ScreenHorzDir bias_direction)
Updates a rect so a string is centered within it.
#define TTE_CHAR_SIZE
By default TTE characters occupy a single tile.
void tte_erase_rect_wrapper(Rect rect)
A wrapper for tte_erase_rect that would use the rect struct.
INLINE int rect_width(const Rect *rect)
Get the width of a rectangle.
Functions relative to manipulating and analyzing the contents of the Hand, a.k.a. the Cards we hold a...
int get_hand_top(void)
Get the position in hand array of the last card obtained.
Definition hand.c:106
void item_dispose(Item **item)
Destroys an item, freeing underlying resources, and manages rollable items sets if needed....
Definition item.c:147
Functions relative to the handling of Jokers.
void joker_set_rollable(int joker_id, bool rollable)
Set whether a Joker is available to be rolled for the shop, packs, etc.
Definition joker.c:345
All the functions used specifically to interact with Jokers. This includes browsing,...
Header of shared rects and points needed for ui.
A doubly-linked list.
bool list_is_empty(const List *list)
Definition list.c:62
List list_init(void)
Definition list.c:38
void * list_get_at_idx(List *list, unsigned int idx)
Definition list.c:219
int list_get_len(const List *list)
Definition list.c:214
void list_clear(List *list)
Definition list.c:44
void list_itr_remove_current_node(ListItr *itr)
Definition list.c:307
void * list_itr_next(ListItr *itr)
Definition list.c:281
bool list_remove_at_idx(List *list, unsigned int idx)
Definition list.c:237
void list_push_back(List *list, void *data)
Definition list.c:89
ListItr list_itr_create(List *list)
Definition list.c:257
Main menu state functions.
Options menu state functions.
Common functions to handle RNG manipulation. Using this interface has three goals:
void rng_update(void)
Accumulates CPU cycles for RNG seed generation, call exaclty once per frame.
Definition random.c:22
u32 rng_get_u32(enum RngSequence key)
Get the next "randomly" generated number in the sequence corresponding to the given type.
Definition random.c:56
API relative to the Rounds we play.
int get_discard_top(void)
Get the index of the last discarded card.
Definition round.c:270
void toggle_flaming_score(void)
Checks whether the score that would result from the current Chips and Mult exceeds the current Blind'...
Definition round.c:1446
int get_played_top(void)
Get the index of the last played card.
Definition round.c:248
Round end game state functions.
Setup Run menu state functions.
Utils functions to save/load data structures from/to the SRAM.
An implementation for a selection grid that handles directional selection.
Shop state functions.
Item * shop_get_description_item(void)
Get a pointer to the Card we are currently showing the description of.
Definition shop.c:196
void shop_reset(void)
Initialize the shop for a run. Resets all the shop data for the run, needs to be called once per run.
Definition shop.c:211
Data structures and functions related to the handling of Skip Tags.
Functions and constants for the splash screen intro.
Sprite system for Gbalatro.
void sprite_object_update_all(void)
Update all SpriteObjects, to be called once per frame in the main update loop.
Definition sprite.c:391
void sprite_destroy(Sprite **sprite)
Destroy Sprite.
Definition sprite.c:134
State Machine.
void state_machine_remove(StateMachine *state_machine)
Remove a statemachine's update function.
void state_machine_register(StateMachine *state_machine)
Register a statemachine to run it's update function once per frame.
void state_machine_change_state(StateMachine *state_machine, int new_state)
Calls the current state's on_exit, the new state's on_init, and sets the active update fn.
void state_machine_update(void)
Update registered state machines' update functions.
Definition card.h:46
A central location for all game variables.
A generic interface for all items that can appear in the shop or be in the inventory....
Definition item.h:83
An iterator into a list.
Definition list.h:91
A doubly-linked list.
Definition list.h:61
State machine callbacks.
State machine instance.
int state
The current state of the state machine, the offset into state_infos.
Timer constants.
Utilities relating around number string representation and protected arithmatic helper functions.
#define GBAL_RETURN_IF_NULL(param, ret_val)
Returns ret_val and prints a default error message if param is equal to NULL. Useful for checking arg...
Definition util.h:138
void truncate_uint_to_suffixed_str(uint32_t num, int num_req_chars, char out_str_buff[UINT_MAX_DIGITS+1])
Truncate an unsigned number into a suffixed string representation e.g. 12000 -> "12K" The least signi...
Definition util.c:116