diff --git a/audio/card_deselect.wav b/audio/card_deselect.wav new file mode 100644 index 0000000..f52611b Binary files /dev/null and b/audio/card_deselect.wav differ diff --git a/audio/draw.wav b/audio/card_draw.wav similarity index 100% rename from audio/draw.wav rename to audio/card_draw.wav diff --git a/audio/card_focus.wav b/audio/card_focus.wav new file mode 100644 index 0000000..4bdebf9 Binary files /dev/null and b/audio/card_focus.wav differ diff --git a/audio/card_select.wav b/audio/card_select.wav new file mode 100644 index 0000000..a50b85a Binary files /dev/null and b/audio/card_select.wav differ diff --git a/graphics/background_gfx.png b/graphics/background_gfx.png index a4b4221..ebdaeda 100644 Binary files a/graphics/background_gfx.png and b/graphics/background_gfx.png differ diff --git a/include/card.h b/include/card.h index a969073..6872b18 100644 --- a/include/card.h +++ b/include/card.h @@ -26,6 +26,16 @@ #define KING 11 #define ACE 12 +// Hand states +enum HandState +{ + HAND_DRAW, + HAND_SELECT, + HAND_PLAY, + HAND_DISCARD +}; + +// Card types typedef struct { u8 suit; @@ -52,11 +62,18 @@ void card_object_destroy(CardObject **card_object); // Card functions void card_init(); void card_update(); -void card_draw(); // Hand functions -void hand_index(int index); // This makes the card at the given index the one that's being looked at +void hand_set_focus(int index); // This makes the card at the given index the one that's being looked at +int hand_get_focus(); // This gets the index of the card that's being looked at void hand_select(); // This lets the player select the card they're looking at for the next action void hand_change_sort(); // This sorts the hand by suit or rank +void hand_discard(); // This discards the selected cards +int hand_get_size(); // This gets the size of the hand +int hand_get_max_size(); // This gets the maximum size of the hand (this doesn't get the array max, it gets the total amount of cards that you have) + +// Deck functions +int deck_get_size(); // This gets the size of the deck +int deck_get_max_size(); // This gets the maximum size of the deck (this doesn't get the array max, it gets the total amount of cards that you have) #endif // CARD_H \ No newline at end of file diff --git a/source/card.c b/source/card.c index 6587b4f..5d7785d 100644 --- a/source/card.c +++ b/source/card.c @@ -8,6 +8,7 @@ #define MAX_HAND_SIZE 16 #define MAX_DECK_SIZE 52 +#define MAX_SELECTION_SIZE 5 // Card sprites lookup table. First index is the suit, second index is the rank. The value is the tile index. const static u16 card_sprite_lut[4][13] = { @@ -17,13 +18,20 @@ const static u16 card_sprite_lut[4][13] = { {624, 640, 656, 672, 688, 704, 720, 736, 752, 768, 784, 800, 816} }; +static enum HandState hand_state = HAND_DRAW; + +static CardObject *played[MAX_SELECTION_SIZE] = {NULL}; static CardObject *hand[MAX_HAND_SIZE] = {NULL}; static Card *deck[MAX_DECK_SIZE] = {NULL}; -static int deck_top = -1; +static int played_top = -1; static int hand_top = -1; +static int deck_top = -1; + +static int hand_size = 8; // Default hand size is 8 +static int cards_drawn = 0; +static int hand_selections = 0; -static int hand_size = 8; static int card_focused = 0; static bool sort_by_suit = false; @@ -62,7 +70,7 @@ static inline void sort_cards() } } - // Update the sprites in the hand by destroying them and creating new ones in the correct order. + // Update the sprites in the hand by destroying them and creating new ones in the correct order // (This is feels like a diabolical solution but like literally how else would you do this) for (int i = 0; i <= hand_top; i++) { @@ -77,18 +85,32 @@ static inline void sort_cards() if (hand[i] != NULL) { hand[i]->sprite = sprite_new(ATTR0_SQUARE | ATTR0_4BPP | ATTR0_AFF, ATTR1_SIZE_32, card_sprite_lut[hand[i]->card->suit][hand[i]->card->rank], 0, i); + sprite_position(hand[i]->sprite, fx2int(hand[i]->x), fx2int(hand[i]->y)); } } } -// Deck private functions -void deck_push(Card *card) +// Played stack +inline void played_push(CardObject *card_object) +{ + if (played_top >= MAX_SELECTION_SIZE - 1) return; + played[++played_top] = card_object; +} + +inline CardObject *played_pop() +{ + if (played_top < 0) return NULL; + return played[played_top--]; +} + +// Deck stack +inline void deck_push(Card *card) { if (deck_top >= MAX_DECK_SIZE - 1) return; deck[++deck_top] = card; } -Card *deck_pop() +inline Card *deck_pop() { if (deck_top < 0) return NULL; return deck[deck_top--]; @@ -131,8 +153,8 @@ CardObject *card_object_new(Card *card) void card_object_destroy(CardObject **card_object) { if (*card_object == NULL) return; - card_destroy(&(*card_object)->card); sprite_destroy(&(*card_object)->sprite); + card_destroy(&(*card_object)->card); free(*card_object); *card_object = NULL; } @@ -142,9 +164,9 @@ void card_draw() if (deck_top < 0 || hand_top >= hand_size - 1 || hand_top >= MAX_HAND_SIZE - 1) return; CardObject *card_object = card_object_new(deck_pop()); - - static const FIXED deck_x = 208 << FIX_SHIFT; - static const FIXED deck_y = 110 << FIX_SHIFT; + + const FIXED deck_x = int2fx(208); + const FIXED deck_y = int2fx(110); card_object->x = deck_x; card_object->y = deck_y; @@ -154,8 +176,8 @@ void card_draw() // Sort the hand after drawing a card sort_cards(); - static mm_sound_effect sfx_draw = {{ SFX_DRAW }, (int)(1.0f * (1<<10)), 0, 255, 128,}; - + const int pitch_lut[MAX_HAND_SIZE] = {1024, 1048, 1072, 1096, 1120, 1144, 1168, 1192, 1216, 1240, 1264, 1288, 1312, 1336, 1360, 1384}; + mm_sound_effect sfx_draw = {{SFX_CARD_DRAW}, pitch_lut[cards_drawn], 0, 255, 128,}; mmEffectEx(&sfx_draw); } @@ -184,40 +206,132 @@ void card_init() void card_update() // This whole function is currently pretty unoptimized due to the fixed point math, but that can be fixed later with a LUT { + const int update_frame = 10; // This is the number of frames between each card event + + static int timer = 0; + if (hand_state == HAND_DRAW && cards_drawn < hand_size) + { + if (timer % update_frame == 0) // Draw a card every 10 frames + { + cards_drawn++; + card_draw(); + } + timer++; + } + else if (hand_state == HAND_DRAW) + { + hand_state = HAND_SELECT; // Change the hand state to select after drawing all the cards + cards_drawn = 0; + timer = 0; + } + + bool discarded_card = false; + for (int i = 0; i < MAX_HAND_SIZE; i++) { if (hand[i] != NULL) { - static const int spacing_lut[MAX_HAND_SIZE] = {28, 28, 28, 28, 27, 21, 18, 15, 13, 12, 10, 9, 9, 8, 8, 7}; // This is a stupid way to do this but I don't care - static const FIXED hand_x = 120 << FIX_SHIFT; - static const FIXED hand_y = 90 << FIX_SHIFT; + const int spacing_lut[MAX_HAND_SIZE] = {28, 28, 28, 28, 27, 21, 18, 15, 13, 12, 10, 9, 9, 8, 8, 7}; // This is a stupid way to do this but I don't care + + FIXED hand_x = int2fx(120); + FIXED hand_y = int2fx(90); - FIXED hand_y_offset = hand_y; + switch (hand_state) + { + case HAND_DRAW: + hand_x = hand_x + (int2fx(i) - int2fx(hand_top) / 2) * -spacing_lut[hand_top]; + break; + case HAND_SELECT: + if (i == card_focused && !hand[i]->selected) + { + hand_y -= (10 << FIX_SHIFT); + } + else if (i != card_focused && hand[i]->selected) + { + hand_y -= (15 << FIX_SHIFT); + } + else if (i == card_focused && hand[i]->selected) + { + hand_y -= (20 << FIX_SHIFT); + } - if (i == card_focused && !hand[i]->selected) - { - hand_y_offset -= (10 << FIX_SHIFT); - } - else if (i != card_focused && hand[i]->selected) - { - hand_y_offset -= (15 << FIX_SHIFT); - } - else if (i == card_focused && hand[i]->selected) - { - hand_y_offset -= (20 << FIX_SHIFT); + if (i != card_focused && hand[i]->y > hand_y) + { + hand[i]->y = hand_y; + hand[i]->vy = 0; + } + + hand_x = hand_x + (int2fx(i) - int2fx(hand_top) / 2) * -spacing_lut[hand_top]; // TODO: Change this later to reference a 2D LUT of positions + break; + case HAND_DISCARD: // TODO: Add sound + static bool sound_played = false; + + if (hand[i]->selected) + { + if (!discarded_card) + { + hand_x = int2fx(240); + hand_y = int2fx(70); + + if (hand[i]->x >= hand_x) + { + card_object_destroy(&hand[i]); + sort_cards(); + + hand_top--; + cards_drawn++; // This technically isn't drawing cards, I'm just reusing the variable + sound_played = false; + } + + if (!sound_played) + { + const int pitch_lut[MAX_SELECTION_SIZE] = {1024, 960, 896, 832, 768}; + mm_sound_effect sfx_draw = {{SFX_CARD_DRAW}, pitch_lut[cards_drawn], 0, 255, 128,}; + mmEffectEx(&sfx_draw); + sound_played = true; + } + + discarded_card = true; + } + else + { + hand_y -= (15 << FIX_SHIFT); + hand_x = hand_x + (int2fx(i) - int2fx(hand_top) / 2) * -spacing_lut[hand_top]; + } + } + else + { + hand_x = hand_x + (int2fx(i) - int2fx(hand_top) / 2) * -spacing_lut[hand_top]; + } + + if (i == hand_top && discarded_card == false) + { + hand_state = HAND_DRAW; + sound_played = false; + cards_drawn = 0; + hand_selections = 0; + timer = 0; + } + + timer++; + break; + case HAND_PLAY: + + break; } - hand[i]->vx += (hand_x + (int2fx(i) - int2fx(hand_top) / 2) * -spacing_lut[hand_top] - hand[i]->x) / 8; // TODO: Change this later to reference a 2D LUT of positions - hand[i]->vy += (hand_y_offset - hand[i]->y) / 8; + hand[i]->vx += (hand_x - hand[i]->x) / 8; + hand[i]->vy += (hand_y - hand[i]->y) / 8; // set velocity to 0 if it's close enough to the target - if (hand[i]->vx < float2fx(0.01f) && hand[i]->vx > float2fx(-0.01f) && hand[i]->vy < float2fx(0.01f) && hand[i]->vy > float2fx(-0.01f)) + const float epsilon = float2fx(0.01f); + if (hand[i]->vx < epsilon && hand[i]->vx > -epsilon && hand[i]->vy < epsilon && hand[i]->vy > -epsilon) { hand[i]->vx = 0; hand[i]->vy = 0; - hand[i]->x = hand_x + (int2fx(i) - int2fx(hand_top) / 2) * -spacing_lut[hand_top]; - hand[i]->y = hand_y_offset; + hand[i]->x = hand_x; + hand[i]->y = hand_y; } else { @@ -227,7 +341,7 @@ void card_update() // This whole function is currently pretty unoptimized due to hand[i]->x += hand[i]->vx; hand[i]->y += hand[i]->vy; } - + obj_aff_rotate(hand[i]->sprite->aff, -hand[i]->vx); sprite_position(hand[i]->sprite, fx2int(hand[i]->x), fx2int(hand[i]->y)); } @@ -235,20 +349,73 @@ void card_update() // This whole function is currently pretty unoptimized due to } // Hand functions -void hand_index(int index) +void hand_set_focus(int index) { if (index < 0 || index > hand_top) return; card_focused = index; + + mm_sound_effect sfx_focus = {{SFX_CARD_FOCUS}, 1024 + rand() % 512, 0, 255, 128,}; + mmEffectEx(&sfx_focus); +} + +int hand_get_focus() +{ + return card_focused; } void hand_select() { - if (hand[card_focused] == NULL) return; - hand[card_focused]->selected = !hand[card_focused]->selected; + if (hand_state != HAND_SELECT || hand[card_focused] == NULL) return; + + if (hand[card_focused]->selected) + { + hand[card_focused]->selected = false; + hand_selections--; + + mm_sound_effect sfx_select = {{SFX_CARD_SELECT}, 1024, 0, 255, 128,}; + mmEffectEx(&sfx_select); + } + else if (hand_selections < MAX_SELECTION_SIZE) + { + hand[card_focused]->selected = true; + hand_selections++; + + mm_sound_effect sfx_deselect = {{SFX_CARD_DESELECT}, 1024, 0, 255, 128,}; + mmEffectEx(&sfx_deselect); + } } void hand_change_sort() { sort_by_suit = !sort_by_suit; sort_cards(); +} + +void hand_discard() +{ + if (hand_state != HAND_SELECT) return; + hand_state = HAND_DISCARD; + card_focused = 0; +} + +int hand_get_size() +{ + return hand_top + 1; +} + +int hand_get_max_size() +{ + return hand_size; +} + +// Deck functions + +int deck_get_size() +{ + return deck_top + 1; +} + +int deck_get_max_size() +{ + return MAX_DECK_SIZE; // This shouldn't be the array max size, it should be the total amount of cards that you have but I can't do that until I implement a way for discarded cards to still be stored in ram } \ No newline at end of file diff --git a/source/game.c b/source/game.c index f0ebbcb..beed453 100644 --- a/source/game.c +++ b/source/game.c @@ -3,6 +3,7 @@ #include #include #include +#include #include "sprite.h" #include "card.h" @@ -14,24 +15,13 @@ void game_init() void game_update() { - static int timer = 0; - timer++; - - if (timer % 12 == 0) - { - card_draw(); - } - - static int hand_index_selected = 0; if (key_hit(KEY_LEFT)) { - hand_index_selected++; - hand_index(hand_index_selected); + hand_set_focus(hand_get_focus() + 1); // The reason why this adds 1 is because the hand is drawn from right to left. There is no particular reason for this, it's just how I did it. } - if (key_hit(KEY_RIGHT)) + else if (key_hit(KEY_RIGHT)) { - hand_index_selected--; - hand_index(hand_index_selected); + hand_set_focus(hand_get_focus() - 1); } if (key_hit(KEY_A)) @@ -39,8 +29,16 @@ void game_update() hand_select(); } - if (key_hit(KEY_SELECT)) + if (key_hit(KEY_B)) { hand_change_sort(); } + + if (key_hit(KEY_SELECT)) + { + hand_discard(); + } + + tte_printf("#{es; P:128,130}%d/%d", hand_get_size(), hand_get_max_size()); + tte_printf("#{P:200,155}%d/%d", deck_get_size(), deck_get_max_size()); } \ No newline at end of file diff --git a/source/main.c b/source/main.c index b1dc029..287616f 100644 --- a/source/main.c +++ b/source/main.c @@ -1,6 +1,5 @@ #include #include - #include #include "sprite.h" @@ -17,16 +16,21 @@ void init() { - // Initialize Maxmod irq_init(NULL); irq_add(II_VBLANK, mmVBlank); - mmInitDefault((mm_addr)soundbank_bin, 8); + // Initialize maxmod + mmInitDefault((mm_addr)soundbank_bin, 12); mmStart(MOD_MAIN_THEME, MM_PLAY_LOOP); + // Initialize text engine + tte_init_se(1, BG_CBB(1) | BG_SBB(31) | BG_8BPP, 0, CLR_WHITE, 11, NULL, NULL); + tte_init_con(); + // Set up the video mode - REG_DISPCNT = DCNT_MODE0 | DCNT_BG0 | DCNT_OBJ_1D | DCNT_OBJ; - REG_BG0CNT = BG_CBB(0) | BG_SBB(30) | BG_8BPP | BG_REG_64x32; + REG_DISPCNT = DCNT_MODE0 | DCNT_OBJ_1D | DCNT_BG0 | DCNT_BG1 | DCNT_OBJ; + REG_BG0CNT = BG_PRIO(1) | BG_CBB(0) | BG_SBB(30) | BG_8BPP | BG_REG_64x32; + REG_BG1CNT = BG_PRIO(0) | BG_CBB(1) | BG_SBB(31) | BG_8BPP; // Load the tiles and palette memcpy(pal_bg_mem, background_gfxPal, background_gfxPalLen);