Merge pull request #105 from cellos51/game_over_screen

Game over (and win) screen
This commit is contained in:
MeirGavish
2025-08-22 02:10:07 +03:00
committed by GitHub
6 changed files with 415 additions and 202 deletions
Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

+1
View File
@@ -32,6 +32,7 @@ enum GameState
GAME_SHOP, GAME_SHOP,
GAME_BLIND_SELECT, GAME_BLIND_SELECT,
GAME_LOSE, GAME_LOSE,
GAME_WIN
}; };
enum HandState enum HandState
+46 -17
View File
@@ -1,6 +1,8 @@
#ifndef GRAPHIC_UTILS_H #ifndef GRAPHIC_UTILS_H
#define GRAPHIC_UTILS_H #define GRAPHIC_UTILS_H
#include <tonc_video.h>
/* This file contains general utils and wrappers that relate to /* This file contains general utils and wrappers that relate to
* graphics/video/vram and generally displaying things on the screen. * graphics/video/vram and generally displaying things on the screen.
* Mostly wrappers and defines for using tonc. * Mostly wrappers and defines for using tonc.
@@ -24,6 +26,19 @@
#define PAL_ROW_LEN 16 #define PAL_ROW_LEN 16
#define NUM_PALETTES 16 #define NUM_PALETTES 16
#define TTE_BIT_UNPACK_OFFSET 14
#define TTE_BIT_ON_CLR_IDX TTE_BIT_UNPACK_OFFSET + 1
#define TTE_YELLOW_PB 12 // 0xC
#define TTE_BLUE_PB 13 // 0xD
#define TTE_RED_PB 14 // 0xE
#define TTE_WHITE_PB 15 // 0xF
#define TEXT_CLR_YELLOW RGB15(31, 20, 0) // 0x029F
#define TEXT_CLR_BLUE RGB15(0, 18, 31) // 0x7E40
#define TEXT_CLR_RED RGB15(31, 9, 8) // 0x213F
#define TEXT_CLR_WHITE CLR_WHITE
/* Dimensions for a screenblock. /* Dimensions for a screenblock.
* A 1024 size screenblock is arranged in a grid of 32x32 screen entries * A 1024 size screenblock is arranged in a grid of 32x32 screen entries
* Interestingly since each block is 8x8 pixels, the 240x160 GBA screen * Interestingly since each block is 8x8 pixels, the 240x160 GBA screen
@@ -52,33 +67,37 @@
// By default TTE characters occupy a single tile // By default TTE characters occupy a single tile
#define TTE_CHAR_SIZE TILE_SIZE #define TTE_CHAR_SIZE TILE_SIZE
typedef struct // When making this, missed that it already exists in tonc_math.h
{ typedef RECT Rect;
int left;
int top;
int right;
int bottom;
} Rect;
/* Gets the screenblock tile for the given coordinates (x, y). /* Gets the screenblock entry for the given coordinates (x, y).
* x and y are in number of tiles. * x and y are in number of tiles.
* Returns the screenblock tile. * Returns the screenblock entry.
*/ */
u16 main_bg_se_get_tile(BG_POINT pos); SE main_bg_se_get_se(BG_POINT pos);
INLINE int rect_width(const Rect* rect) INLINE int rect_width(const Rect* rect)
{ {
/* Extra parens to avoid issues in case compiler turns INLINE into macro /* Extra parens to avoid issues in case compiler turns INLINE into macro
* Not sure if necessary, could be just paranoia * Not sure if necessary, could be just paranoia
*/ */
return (((rect)->right) - ((rect)->left) + 1); return (((rect)->right) - ((rect)->left) + 1);
} }
INLINE int rect_height(const Rect* rect) INLINE int rect_height(const Rect* rect)
{ {
return (((rect)->bottom) - ((rect)->top) + 1); return (((rect)->bottom) - ((rect)->top) + 1);
} }
/* Copies an SE rect vertically in direction by a single tile.
* bg_sbb is the SBB of the background in which to move the rect
* direction must be either SE_UP or SE_DOWN.
* se_rect dimensions are in number of tiles.
*
* NOTE: This does not work with TTE_SBB, probably because it's 4BPP...
*/
void bg_se_copy_rect_1_tile_vert(u16 bg_sbb, Rect se_rect, int direction);
/* Clears a rect in the main background. /* Clears a rect in the main background.
* The se_rect dimensions need to be in number of tiles. * The se_rect dimensions need to be in number of tiles.
*/ */
@@ -96,11 +115,21 @@ void main_bg_se_copy_rect_1_tile_vert(Rect se_rect, int direction);
*/ */
void main_bg_se_copy_rect(Rect se_rect, BG_POINT pos); void main_bg_se_copy_rect(Rect se_rect, BG_POINT pos);
/* Copies a tile to a rect in the main background. /* Copies a screen entry to a rect in the main background.
* se_rect dimensions are in number of tiles. * se_rect dimensions are in number of tiles.
* The tile is copied to the top left corner of the rect. * The tile is copied to the top left corner of the rect.
*/ */
void main_bg_se_copy_tile_to_rect(u16 tile, Rect se_rect); void main_bg_se_fill_rect_with_se(SE tile, Rect se_rect);
/* Copies a 3x3 rect into se_rect_dest, the 3x3 rect is stretched to fill se_rect_dest.
* The corners are copied, the sides are stretched, and the center is filled.
* The parameter se_rect_src_3x3_top_left points to the top left corner of the source
* 3x3 rect.
* Dest rect sides can be of length 2, then the sides are not copied, only the corners.
* But dest rect sides must be at least 2.
*/
void main_bg_se_copy_expand_3x3_rect(Rect se_rect_dest, BG_POINT se_rect_src_3x3_top_left);
/* Moves a rect in the main background vertically in direction by a single tile. /* Moves a rect in the main background vertically in direction by a single tile.
* Note that tiles in the previous location will be transparent (0x000) * Note that tiles in the previous location will be transparent (0x000)
* so maybe copy would be a better choice if you don't want to delete things * so maybe copy would be a better choice if you don't want to delete things
+262 -162
View File
@@ -186,7 +186,7 @@ static const Rect POP_MENU_ANIM_RECT = {9, 7, 24, 31 };
// This is because when popping, the target position is blank so we just animate // This is because when popping, the target position is blank so we just animate
// the whole rect so we don't have to track its position // the whole rect so we don't have to track its position
static const Rect SINGLE_BLIND_SELECT_RECT = { 9, 7, 13, 32 }; static const Rect SINGLE_BLIND_SELECT_RECT = {9, 7, 13, 32 };
static const Rect HAND_BG_RECT_SELECTING = {9, 11, 24, 17 }; static const Rect HAND_BG_RECT_SELECTING = {9, 11, 24, 17 };
// TODO: Currently unused, remove? // TODO: Currently unused, remove?
@@ -205,6 +205,9 @@ static const Rect TOP_LEFT_PANEL_ANIM_RECT = {0, 0, 8, 4 };
static const BG_POINT TOP_LEFT_BLIND_TITLE_POINT = {0, 21, }; static const BG_POINT TOP_LEFT_BLIND_TITLE_POINT = {0, 21, };
static const Rect BIG_BLIND_TITLE_SRC_RECT = {0, 26, 8, 26 }; static const Rect BIG_BLIND_TITLE_SRC_RECT = {0, 26, 8, 26 };
static const Rect BOSS_BLIND_TITLE_SRC_RECT = {0, 27, 8, 27 }; static const Rect BOSS_BLIND_TITLE_SRC_RECT = {0, 27, 8, 27 };
static const BG_POINT GAME_OVER_SRC_RECT_3X3_POS = {25, 29};
static const Rect GAME_OVER_DIALOG_DEST_RECT= {11, 21, 23, 26};
static const Rect GAME_OVER_ANIM_RECT = {11, 8, 23, 26};
// Rects for TTE (in pixels) // Rects for TTE (in pixels)
static const Rect HAND_SIZE_RECT = {128, 128, 152, 160 }; // Seems to include both SELECT and PLAYING static const Rect HAND_SIZE_RECT = {128, 128, 152, 160 }; // Seems to include both SELECT and PLAYING
@@ -236,7 +239,10 @@ static const Rect ROUND_END_BLIND_REWARD_RECT = { 168, 96, UNDEFINED, UNDEF
static const Rect ROUND_END_NUM_HANDS_RECT = {88, 116, UNDEFINED, UNDEFINED }; static const Rect ROUND_END_NUM_HANDS_RECT = {88, 116, UNDEFINED, UNDEFINED };
static const Rect HAND_REWARD_RECT = {168, UNDEFINED, UNDEFINED, UNDEFINED }; static const Rect HAND_REWARD_RECT = {168, UNDEFINED, UNDEFINED, UNDEFINED };
static const Rect CASHOUT_RECT = {88, 72, UNDEFINED, UNDEFINED }; static const Rect CASHOUT_RECT = {88, 72, UNDEFINED, UNDEFINED };
static const Rect SHOP_REROLL_RECT = {88, 96, UNDEFINED, UNDEFINED }; static const Rect SHOP_REROLL_RECT = {88, 96, UNDEFINED, UNDEFINED };
static const Rect GAME_LOSE_MSG_TEXT_RECT = {104, 72, UNDEFINED, UNDEFINED};
// 1 character to the right oF GAME_LOSE
static const Rect GAME_WIN_MSG_TEXT_RECT = {112, 72, UNDEFINED, UNDEFINED};
static const BG_POINT HELD_JOKERS_POS = {108, 10}; static const BG_POINT HELD_JOKERS_POS = {108, 10};
static const BG_POINT JOKER_DISCARD_TARGET = {240, 30}; static const BG_POINT JOKER_DISCARD_TARGET = {240, 30};
@@ -245,6 +251,7 @@ static const BG_POINT JOKER_DISCARD_TARGET = {240, 30};
//TODO: Properly define and use //TODO: Properly define and use
#define MENU_POP_OUT_ANIM_FRAMES 20 #define MENU_POP_OUT_ANIM_FRAMES 20
#define GAME_OVER_ANIM_FRAMES 15
#define SCORED_CARD_TEXT_Y 48 #define SCORED_CARD_TEXT_Y 48
@@ -516,7 +523,7 @@ void change_background(int id)
obj_unhide(blind_select_tokens[BIG_BLIND]->obj, 0); obj_unhide(blind_select_tokens[BIG_BLIND]->obj, 0);
obj_unhide(blind_select_tokens[BOSS_BLIND]->obj, 0); obj_unhide(blind_select_tokens[BOSS_BLIND]->obj, 0);
const int default_y = 89 + (TILE_SIZE * 12); // Default y position for the blind select tokens. 8 is the size of a tile and 12 is the amound of tiles the background is shifted down by const int default_y = 89 + (TILE_SIZE * 12); // Default y position for the blind select tokens. 12 is the amound of tiles the background is shifted down by
sprite_position(blind_select_tokens[SMALL_BLIND], 80, default_y); sprite_position(blind_select_tokens[SMALL_BLIND], 80, default_y);
sprite_position(blind_select_tokens[BIG_BLIND], 120, default_y); sprite_position(blind_select_tokens[BIG_BLIND], 120, default_y);
sprite_position(blind_select_tokens[BOSS_BLIND], 160, default_y); sprite_position(blind_select_tokens[BOSS_BLIND], 160, default_y);
@@ -632,7 +639,7 @@ void display_temp_score(int value)
{ {
int x_offset = 40 - get_digits_even(value) * TILE_SIZE; int x_offset = 40 - get_digits_even(value) * TILE_SIZE;
tte_erase_rect_wrapper(TEMP_SCORE_RECT); tte_erase_rect_wrapper(TEMP_SCORE_RECT);
tte_printf("#{P:%d,%d; cx:0xF000}%d", x_offset, TEMP_SCORE_RECT.top, value); tte_printf("#{P:%d,%d; cx:0x%X000}%d", x_offset, TEMP_SCORE_RECT.top, TTE_WHITE_PB, value);
} }
void display_score(int value) void display_score(int value)
@@ -661,14 +668,14 @@ void display_score(int value)
int rect_width = SCORE_RECT.right - SCORE_RECT.left; int rect_width = SCORE_RECT.right - SCORE_RECT.left;
int x_offset = SCORE_RECT.left + (rect_width - text_width) / 2; int x_offset = SCORE_RECT.left + (rect_width - text_width) / 2;
tte_printf("#{P:%d,48; cx:0xF000}%d%c", x_offset, display_value, score_suffix); tte_printf("#{P:%d,48; cx:0x%X000}%d%c", x_offset, TTE_WHITE_PB, display_value, score_suffix);
} }
void display_money(int value) void display_money(int value)
{ {
int x_offset = 32 - get_digits_odd(value) * TILE_SIZE; int x_offset = 32 - get_digits_odd(value) * TILE_SIZE;
tte_erase_rect_wrapper(MONEY_TEXT_RECT); tte_erase_rect_wrapper(MONEY_TEXT_RECT);
tte_printf("#{P:%d,%d; cx:0xC000}$%d", x_offset, MONEY_TEXT_RECT.top, value); tte_printf("#{P:%d,%d; cx:0x%X000}$%d", x_offset, MONEY_TEXT_RECT.top, TTE_YELLOW_PB, value);
} }
void display_chips(int value) void display_chips(int value)
@@ -676,19 +683,19 @@ void display_chips(int value)
Rect chips_text_rect = CHIPS_TEXT_RECT; Rect chips_text_rect = CHIPS_TEXT_RECT;
tte_erase_rect_wrapper(CHIPS_TEXT_RECT); tte_erase_rect_wrapper(CHIPS_TEXT_RECT);
update_text_rect_to_right_align_num(&chips_text_rect, value, OVERFLOW_LEFT); update_text_rect_to_right_align_num(&chips_text_rect, value, OVERFLOW_LEFT);
tte_printf("#{P:%d,%d; cx:0xF000;}%d", chips_text_rect.left, chips_text_rect.top, value); tte_printf("#{P:%d,%d; cx:0x%X000;}%d", chips_text_rect.left, chips_text_rect.top, TTE_WHITE_PB, value);
} }
void display_mult(int value) void display_mult(int value)
{ {
tte_erase_rect_wrapper(MULT_TEXT_RECT); tte_erase_rect_wrapper(MULT_TEXT_RECT);
tte_printf("#{P:%d,%d; cx:0xF000;}%d", MULT_TEXT_RECT.left, MULT_TEXT_RECT.top, value); // Mult tte_printf("#{P:%d,%d; cx:0x%X000;}%d", MULT_TEXT_RECT.left, MULT_TEXT_RECT.top, TTE_WHITE_PB, value); // Mult
} }
void display_round(int value) void display_round(int value)
{ {
//tte_erase_rect_wrapper(ROUND_TEXT_RECT); //tte_erase_rect_wrapper(ROUND_TEXT_RECT);
tte_printf("#{P:%d,%d; cx:0xC000}%d", ROUND_TEXT_RECT.left, ROUND_TEXT_RECT.top, round); tte_printf("#{P:%d,%d; cx:0x%X000}%d", ROUND_TEXT_RECT.left, ROUND_TEXT_RECT.top, TTE_YELLOW_PB, round);
} }
void display_ante(int value) void display_ante(int value)
@@ -712,7 +719,7 @@ static void print_hand_type(const char* hand_type_str)
{ {
if (hand_type_str == NULL) if (hand_type_str == NULL)
return; // NULL-checking paranoia return; // NULL-checking paranoia
tte_printf("#{P:%d,%d; cx:0xF000}%s", HAND_TYPE_RECT.left, HAND_TYPE_RECT.top, hand_type_str); tte_printf("#{P:%d,%d; cx:0x%X000}%s", HAND_TYPE_RECT.left, HAND_TYPE_RECT.top, TTE_WHITE_PB, hand_type_str);
} }
void set_hand() void set_hand()
@@ -922,7 +929,7 @@ void increment_blind(enum BlindState increment_reason)
} }
} }
void game_round_init() static void game_round_init()
{ {
// once we have a main menu, move the seed set there. this is only here now because there isn't a way to set the seed to a psudorandom value unless there is something to wait on // once we have a main menu, move the seed set there. this is only here now because there isn't a way to set the seed to a psudorandom value unless there is something to wait on
set_seed(rng_seed); set_seed(rng_seed);
@@ -968,13 +975,35 @@ void game_round_init()
blind_req_text_rect.left -= TILE_SIZE; // Move left by one character width to make room for 'k' blind_req_text_rect.left -= TILE_SIZE; // Move left by one character width to make room for 'k'
} }
tte_printf("#{P:%d,%d; cx:0xE000}%d%c", blind_req_text_rect.left, blind_req_text_rect.top, blind_requirement, score_suffix); // Blind requirement tte_printf("#{P:%d,%d; cx:0x%X000}%d%c", blind_req_text_rect.left, blind_req_text_rect.top, TTE_RED_PB, blind_requirement, score_suffix); // Blind requirement
tte_printf("#{P:%d,%d; cx:0xC000}$%d", BLIND_REWARD_RECT.left, BLIND_REWARD_RECT.top, blind_get_reward(current_blind)); // Blind reward tte_printf("#{P:%d,%d; cx:0x%X000}$%d", BLIND_REWARD_RECT.left, BLIND_REWARD_RECT.top, TTE_YELLOW_PB, blind_get_reward(current_blind)); // Blind reward
deck_shuffle(); // Shuffle the deck at the start of the round deck_shuffle(); // Shuffle the deck at the start of the round
} }
void init_game_state(enum GameState game_state_to_init) static void game_over_init()
{
// Clears the round end menu
main_bg_se_clear_rect(POP_MENU_ANIM_RECT);
main_bg_se_copy_expand_3x3_rect(GAME_OVER_DIALOG_DEST_RECT, GAME_OVER_SRC_RECT_3X3_POS);
}
static void game_lose_init()
{
game_over_init();
// Using the text color to match the "Game Over" text
affine_background_set_color(TEXT_CLR_RED);
}
static void game_win_init()
{
game_over_init();
// Using the text color to match the "You Win" text
affine_background_set_color(TEXT_CLR_BLUE);
}
static void init_game_state(enum GameState game_state_to_init)
{ {
// Switch written out, add init for states as needed // Switch written out, add init for states as needed
switch (game_state_to_init) switch (game_state_to_init)
@@ -989,6 +1018,10 @@ void init_game_state(enum GameState game_state_to_init)
case GAME_BLIND_SELECT: case GAME_BLIND_SELECT:
break; break;
case GAME_LOSE: case GAME_LOSE:
game_lose_init();
break;
case GAME_WIN:
game_win_init();
break; break;
default: default:
break; break;
@@ -996,7 +1029,7 @@ void init_game_state(enum GameState game_state_to_init)
} }
// Game functions // Game functions
void game_set_state(enum GameState new_game_state) static void game_set_state(enum GameState new_game_state)
{ {
timer = 0; // Reset the timer timer = 0; // Reset the timer
init_game_state(new_game_state); init_game_state(new_game_state);
@@ -1048,7 +1081,7 @@ void game_init()
change_background(BG_ID_BLIND_SELECT); change_background(BG_ID_BLIND_SELECT);
tte_printf("#{P:%d,%d; cx:0xF000}%d/%d", DECK_SIZE_RECT.left, DECK_SIZE_RECT.top, deck_get_size(), deck_get_max_size()); // Deck size/max size tte_printf("#{P:%d,%d; cx:0x%X000}%d/%d", DECK_SIZE_RECT.left, DECK_SIZE_RECT.top, TTE_WHITE_PB, deck_get_size(), deck_get_max_size()); // Deck size/max size
display_round(round); // Set the round display display_round(round); // Set the round display
display_score(score); // Set the score display display_score(score); // Set the score display
@@ -1059,10 +1092,9 @@ void game_init()
display_hands(hands); // Hand display_hands(hands); // Hand
display_discards(discards); // Discard display_discards(discards); // Discard
//tte_printf("#{P:24,120; cx:0xC000}$%d", money); // Money
display_money(money); // Set the money display display_money(money); // Set the money display
tte_printf("#{P:%d,%d; cx:0xC000}%d#{cx:0xF000}/%d", ANTE_TEXT_RECT.left, ANTE_TEXT_RECT.top, ante, MAX_ANTE); // Ante tte_printf("#{P:%d,%d; cx:0x%X000}%d#{cx:0x%X000}/%d", ANTE_TEXT_RECT.left, ANTE_TEXT_RECT.top, TTE_YELLOW_PB, ante, TTE_WHITE_PB, MAX_ANTE); // Ante
} }
static void game_playing_process_hand_select_input() static void game_playing_process_hand_select_input()
@@ -1136,7 +1168,7 @@ static void game_playing_process_hand_select_input()
selection_y = 0; selection_y = 0;
display_hands(--discards); display_hands(--discards);
set_hand(); set_hand();
tte_printf("#{P:%d,%d; cx:0xE000}%d", DISCARDS_TEXT_RECT.left, DISCARDS_TEXT_RECT.top, discards); tte_printf("#{P:%d,%d; cx:0x%X000}%d", DISCARDS_TEXT_RECT.left, DISCARDS_TEXT_RECT.top, TTE_RED_PB, discards);
} }
} }
} }
@@ -1236,6 +1268,37 @@ static void game_playing_process_card_draw()
} }
} }
static bool game_round_is_over()
{
return hands == 0 || score >= blind_get_requirement(current_blind, ante);
}
static void game_playing_handle_round_over()
{
enum GameState next_state = GAME_ROUND_END;
if (score >= blind_get_requirement(current_blind, ante))
{
if (current_blind == BOSS_BLIND)
{
if (ante < MAX_ANTE)
{
display_ante(++ante);
}
else
{
next_state = GAME_WIN;
}
}
}
else if (hands == 0)
{
next_state = GAME_LOSE;
}
game_set_state(next_state);
}
static void game_playing_discarded_cards_loop() static void game_playing_discarded_cards_loop()
{ {
// Discarded cards loop (mainly for shuffling) // Discarded cards loop (mainly for shuffling)
@@ -1274,26 +1337,97 @@ static void game_playing_discarded_cards_loop()
if (discard_top == -1 && discarded_card_object == NULL) // If there are no more discarded cards, stop shuffling if (discard_top == -1 && discarded_card_object == NULL) // If there are no more discarded cards, stop shuffling
{ {
game_set_state(GAME_ROUND_END); // Set the game state back to playing // After HAND_SHUFFLING the round is over
game_playing_handle_round_over();
} }
} }
} }
static const int HAND_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
void card_in_hand_loop_handle_discard_and_shuffling(int card_idx, bool* discarded_card, FIXED* hand_x, FIXED* hand_y, bool* sound_played, bool* break_loop)
{
if (hand_state != HAND_DISCARD && hand_state != HAND_SHUFFLING)
{
// Assumes hand_state is one of these
return;
}
*break_loop = false;
if (card_object_is_selected(hand[card_idx]) || hand_state == HAND_SHUFFLING)
{
if (!*discarded_card)
{
*hand_x = int2fx(240);
*hand_y = int2fx(70);
if (!*sound_played)
{
play_sfx(SFX_CARD_DRAW, MM_BASE_PITCH_RATE + cards_drawn * PITCH_STEP_DISCARD_SFX);
*sound_played = true;
}
if (hand[card_idx]->sprite_object->x >= *hand_x)
{
discard_push(hand[card_idx]->card);
card_object_destroy(&hand[card_idx]);
sort_cards();
hand_top--;
cards_drawn++; // This technically isn't drawing cards, I'm just reusing the variable
*sound_played = false;
timer = 0;
*hand_y = hand[card_idx]->sprite_object->y;
*hand_x = hand[card_idx]->sprite_object->x;
}
*discarded_card = true;
}
else
{
if (hand_state == HAND_DISCARD)
{
*hand_y -= int2fx(15); // Don't raise the card if we're mass discarding, it looks stupid.
}
else // hand_state == HAND_SHUFFLING
{
*hand_y += int2fx(24);
}
*hand_x = *hand_x + (int2fx(card_idx) - int2fx(hand_top) / 2) * -HAND_SPACING_LUT[hand_top];
}
}
else
{
*hand_x = *hand_x + (int2fx(card_idx) - int2fx(hand_top) / 2) * -HAND_SPACING_LUT[hand_top];
}
if (card_idx == 0 && *discarded_card == false && timer % FRAMES(10) == 0)
{
// This is never reached in the case of HAND_SHUFFLING. // Not sure why but that's how it's supposed to be.
hand_state = HAND_DRAW;
*sound_played = false;
cards_drawn = 0;
hand_selections = 0;
timer = 0;
*break_loop = true;
return;
};
}
static void cards_in_hand_update_loop(bool* discarded_card, int* played_selections, bool* sound_played) static void cards_in_hand_update_loop(bool* discarded_card, int* played_selections, bool* sound_played)
{ {
for (int i = hand_top + 1; i >= 0; i--) // Start from the end of the hand and work backwards because that's how Balatro does it for (int i = hand_top + 1; i >= 0; i--) // Start from the end of the hand and work backwards because that's how Balatro does it
{ {
if (hand[i] != NULL) if (hand[i] != NULL)
{ {
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_x = int2fx(120);
FIXED hand_y = int2fx(90); FIXED hand_y = int2fx(90);
switch (hand_state) switch (hand_state)
{ {
case HAND_DRAW: case HAND_DRAW:
hand_x = hand_x + (int2fx(i) - int2fx(hand_top) / 2) * -spacing_lut[hand_top]; hand_x = hand_x + (int2fx(i) - int2fx(hand_top) / 2) * -HAND_SPACING_LUT[hand_top];
break; break;
case HAND_SELECT: case HAND_SELECT:
bool is_focused = (i == selection_x && selection_y == 0); bool is_focused = (i == selection_x && selection_y == 0);
@@ -1317,72 +1451,18 @@ static void cards_in_hand_update_loop(bool* discarded_card, int* played_selectio
hand[i]->sprite_object->vy = 0; hand[i]->sprite_object->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 hand_x = hand_x + (int2fx(i) - int2fx(hand_top) / 2) * -HAND_SPACING_LUT[hand_top]; // TODO: Change this later to reference a 2D LUT of positions
break; break;
case HAND_SHUFFLING: case HAND_SHUFFLING:
/* FALL THROUGH */ /* FALL THROUGH */
case HAND_DISCARD: // TODO: Add sound case HAND_DISCARD: // TODO: Add sound
if (card_object_is_selected(hand[i]) || hand_state == HAND_SHUFFLING) bool break_loop;
{ card_in_hand_loop_handle_discard_and_shuffling(i, discarded_card, &hand_x, &hand_y, sound_played, &break_loop);
if (!*discarded_card) if (break_loop) break;
{
hand_x = int2fx(240);
hand_y = int2fx(70);
if (!*sound_played)
{
play_sfx(SFX_CARD_DRAW, MM_BASE_PITCH_RATE + cards_drawn*PITCH_STEP_DISCARD_SFX);
*sound_played = true;
}
if (hand[i]->sprite_object->x >= hand_x)
{
discard_push(hand[i]->card);
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;
timer = 0;
hand_y = hand[i]->sprite_object->y;
hand_x = hand[i]->sprite_object->x;
}
*discarded_card = true;
}
else
{
if (hand_state == HAND_DISCARD)
{
hand_y -= int2fx(15); // Don't raise the card if we're mass discarding, it looks stupid.
}
else
{
hand_y += int2fx(24);
}
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 == 0 && *discarded_card == false && timer % FRAMES(10) == 0)
{
hand_state = HAND_DRAW;
*sound_played = false;
cards_drawn = 0;
hand_selections = 0;
timer = 0;
break;
}
break; break;
case HAND_PLAY: case HAND_PLAY:
hand_x = hand_x + (int2fx(i) - int2fx(hand_top) / 2) * -spacing_lut[hand_top]; hand_x = hand_x + (int2fx(i) - int2fx(hand_top) / 2) * -HAND_SPACING_LUT[hand_top];
hand_y += int2fx(24); hand_y += int2fx(24);
if (card_object_is_selected(hand[i]) && *discarded_card == false && timer % FRAMES(10) == 0) if (card_object_is_selected(hand[i]) && *discarded_card == false && timer % FRAMES(10) == 0)
@@ -1556,7 +1636,7 @@ static void cards_in_hand_update_loop(bool* discarded_card, int* played_selectio
break; break;
case HAND_PLAYING: // Don't need to do anything here, just wait for the player to select cards case HAND_PLAYING: // Don't need to do anything here, just wait for the player to select cards
hand_x = hand_x + (int2fx(i) - int2fx(hand_top) / 2) * -spacing_lut[hand_top]; hand_x = hand_x + (int2fx(i) - int2fx(hand_top) / 2) * -HAND_SPACING_LUT[hand_top];
hand_y += int2fx(24); hand_y += int2fx(24);
break; break;
} }
@@ -1744,14 +1824,9 @@ static void played_cards_update_loop(bool* discarded_card, int* played_selection
if (i == played_top) if (i == played_top)
{ {
if (score >= blind_get_requirement(current_blind, ante)) if (game_round_is_over())
{ {
hand_state = HAND_SHUFFLING; hand_state = HAND_SHUFFLING;
if (current_blind == BOSS_BLIND)
{
display_ante(++ante);
}
} }
else else
{ {
@@ -1782,55 +1857,6 @@ static void played_cards_update_loop(bool* discarded_card, int* played_selection
} }
} }
static void discarded_jokers_update_loop()
{
if (discarded_jokers == NULL)
return;
// Iterating backwards because of removal within loop
for (int i = list_get_size(discarded_jokers) - 1; i >= 0; i--)
{
JokerObject* joker_object = list_get(discarded_jokers, i);
joker_object_update(joker_object);
if (joker_object->sprite_object->x == joker_object->sprite_object->tx
&& joker_object->sprite_object->y == joker_object->sprite_object->ty)
{
list_remove_by_idx(discarded_jokers, i);
joker_object_destroy(&joker_object);
}
}
}
static void held_jokers_update_loop()
{
const int spacing_lut[MAX_JOKERS_HELD_SIZE][MAX_JOKERS_HELD_SIZE] =
{
{0, 0, 0, 0, 0},
{13, -13, 0, 0, 0},
{26, 0, -26, 0, 0},
{39, 13, -13, -39, 0},
{40, 20, 0, -20, -40}
};
FIXED hand_x = int2fx(HELD_JOKERS_POS.x);
int jokers_top = list_get_size(jokers) - 1;
for (int i = jokers_top; i >= 0; i--)
{
JokerObject *joker = list_get(jokers, i);
joker->sprite_object->tx = hand_x - int2fx(spacing_lut[jokers_top][i]);
joker_object_update(joker);
}
}
static void jokers_update_loop()
{
held_jokers_update_loop();
discarded_jokers_update_loop();
}
static void game_playing_ui_text_update() static void game_playing_ui_text_update()
{ {
static int last_hand_size = 0; static int last_hand_size = 0;
@@ -1840,14 +1866,14 @@ static void game_playing_ui_text_update()
{ {
if (background == BG_ID_CARD_SELECTING) if (background == BG_ID_CARD_SELECTING)
{ {
tte_printf("#{P:%d,%d; cx:0xF000}%d/%d", HAND_SIZE_RECT_SELECT.left, HAND_SIZE_RECT_SELECT.top, hand_get_size(), hand_get_max_size()); // Hand size/max size tte_printf("#{P:%d,%d; cx:0x%X000}%d/%d", HAND_SIZE_RECT_SELECT.left, HAND_SIZE_RECT_SELECT.top, TTE_WHITE_PB, hand_get_size(), hand_get_max_size()); // Hand size/max size
} }
else if (background == BG_ID_CARD_PLAYING) else if (background == BG_ID_CARD_PLAYING)
{ {
tte_printf("#{P:%d,%d; cx:0xF000}%d/%d", HAND_SIZE_RECT_PLAYING.left, HAND_SIZE_RECT_PLAYING.top, hand_get_size(), hand_get_max_size()); // Hand size/max size tte_printf("#{P:%d,%d; cx:0x%X000}%d/%d", HAND_SIZE_RECT_PLAYING.left, HAND_SIZE_RECT_PLAYING.top, TTE_WHITE_PB, hand_get_size(), hand_get_max_size()); // Hand size/max size
} }
tte_printf("#{P:%d,%d; cx:0xF000}%d/%d", DECK_SIZE_RECT.left, DECK_SIZE_RECT.top, deck_get_size(), deck_get_max_size()); // Deck size/max size tte_printf("#{P:%d,%d; cx:0x%X000}%d/%d", DECK_SIZE_RECT.left, DECK_SIZE_RECT.top, TTE_WHITE_PB, deck_get_size(), deck_get_max_size()); // Deck size/max size
last_hand_size = hand_get_size(); last_hand_size = hand_get_size();
last_deck_size = deck_get_size(); last_deck_size = deck_get_size();
@@ -1870,13 +1896,6 @@ static void game_round_end_cashout()
void game_playing() void game_playing()
{ {
// TODO: Blind rect sliding into view animation...
if (hand_state == HAND_SELECT && hands == 0)
{
// TODO: Check if it's safe to change to game_set_state() without side effects
game_state = GAME_LOSE;
}
// Background logic (thissss might be moved to the card'ssss logic later. I'm a sssssnake) // Background logic (thissss might be moved to the card'ssss logic later. I'm a sssssnake)
if (hand_state == HAND_DRAW || hand_state == HAND_DISCARD || hand_state == HAND_SELECT) if (hand_state == HAND_DRAW || hand_state == HAND_DISCARD || hand_state == HAND_SELECT)
{ {
@@ -1956,7 +1975,7 @@ void game_round_end()
int blind_req = blind_get_requirement(current_blind, current_ante); int blind_req = blind_get_requirement(current_blind, current_ante);
update_text_rect_to_right_align_num(&blind_req_rect, blind_req, OVERFLOW_RIGHT); update_text_rect_to_right_align_num(&blind_req_rect, blind_req, OVERFLOW_RIGHT);
tte_printf("#{P:%d,%d; cx:0xE000}%d", blind_req_rect.left, blind_req_rect.top, blind_req); tte_printf("#{P:%d,%d; cx:0x%X000}%d", blind_req_rect.left, blind_req_rect.top, TTE_RED_PB, blind_req);
if (timer == 1) if (timer == 1)
{ {
@@ -2001,8 +2020,8 @@ void game_round_end()
if (blind_reward > 0) if (blind_reward > 0)
{ {
blind_reward--; blind_reward--;
tte_printf("#{P:%d,%d; cx:0xC000}$%d", BLIND_REWARD_RECT.left , BLIND_REWARD_RECT.top, blind_reward); tte_printf("#{P:%d,%d; cx:0x%X000}$%d", BLIND_REWARD_RECT.left , BLIND_REWARD_RECT.top, TTE_YELLOW_PB, blind_reward);
tte_printf("#{P:%d,%d; cx:0xC000}$%d", ROUND_END_BLIND_REWARD_RECT.left, ROUND_END_BLIND_REWARD_RECT.top, blind_get_reward(current_blind) - blind_reward); tte_printf("#{P:%d,%d; cx:0x%X000}$%d", ROUND_END_BLIND_REWARD_RECT.left, ROUND_END_BLIND_REWARD_RECT.top, TTE_YELLOW_PB, blind_get_reward(current_blind) - blind_reward);
} }
else if (timer > FRAMES(20)) else if (timer > FRAMES(20))
{ {
@@ -2083,7 +2102,7 @@ void game_round_end()
int x = (8 + timer) * TILE_SIZE; int x = (8 + timer) * TILE_SIZE;
int y = (13) * TILE_SIZE; int y = (13) * TILE_SIZE;
tte_printf("#{P:%d,%d; cx:0xF000}.", x, y); tte_printf("#{P:%d,%d; cx:0x%X000}.", x, y, TTE_WHITE_PB);
} }
else if (timer >= 30 && hand_reward > 0) // Wait an additional 15 frames since the last sequenced action else if (timer >= 30 && hand_reward > 0) // Wait an additional 15 frames since the last sequenced action
{ {
@@ -2094,13 +2113,13 @@ void game_round_end()
single_line_rect.bottom = single_line_rect.top + 1; single_line_rect.bottom = single_line_rect.top + 1;
main_bg_se_copy_rect_1_tile_vert(single_line_rect, SE_DOWN); main_bg_se_copy_rect_1_tile_vert(single_line_rect, SE_DOWN);
tte_printf("#{P:%d,%d; cx:0xD000}%d #{cx:0xF000}Hands", ROUND_END_NUM_HANDS_RECT.left, ROUND_END_NUM_HANDS_RECT.top, hand_reward); // Print the hand reward tte_printf("#{P:%d,%d; cx:0x%X000}%d #{cx:0x%X000}Hands", ROUND_END_NUM_HANDS_RECT.left, ROUND_END_NUM_HANDS_RECT.top, TTE_BLUE_PB, hand_reward, TTE_WHITE_PB); // Print the hand reward
} }
else if (timer > 45 && timer % FRAMES(20) == 0) // After 15 frames, every 20 frames, increment the hand reward text until the hand reward variable is depleted else if (timer > 45 && timer % FRAMES(20) == 0) // After 15 frames, every 20 frames, increment the hand reward text until the hand reward variable is depleted
{ {
int y = (13 + hand_y) * TILE_SIZE; int y = (13 + hand_y) * TILE_SIZE;
hand_reward--; hand_reward--;
tte_printf("#{P:%d, %d; cx:0xC000}$%d", HAND_REWARD_RECT.left, y, hands - hand_reward); // Print the hand reward tte_printf("#{P:%d, %d; cx:0x%X000}$%d", HAND_REWARD_RECT.left, y, TTE_YELLOW_PB, hands - hand_reward); // Print the hand reward
} }
} }
@@ -2120,17 +2139,17 @@ void game_round_end()
Rect top_rect = {11, 8, 22, 8}; Rect top_rect = {11, 8, 22, 8};
BG_POINT top_point = {6, 29}; BG_POINT top_point = {6, 29};
main_bg_se_copy_tile_to_rect(main_bg_se_get_tile(top_point), top_rect); main_bg_se_fill_rect_with_se(main_bg_se_get_se(top_point), top_rect);
Rect middle_rect = {11, 9, 22, 9}; Rect middle_rect = {11, 9, 22, 9};
BG_POINT middle_point = {6, 30}; BG_POINT middle_point = {6, 30};
main_bg_se_copy_tile_to_rect(main_bg_se_get_tile(middle_point), middle_rect); main_bg_se_fill_rect_with_se(main_bg_se_get_se(middle_point), middle_rect);
Rect bottom_rect = {11, 10, 22, 10}; Rect bottom_rect = {11, 10, 22, 10};
BG_POINT bottom_point = {6, 31}; BG_POINT bottom_point = {6, 31};
main_bg_se_copy_tile_to_rect(main_bg_se_get_tile(bottom_point), bottom_rect); main_bg_se_fill_rect_with_se(main_bg_se_get_se(bottom_point), bottom_rect);
tte_printf("#{P:%d, %d; cx:0xF000}Cash Out: $%d", CASHOUT_RECT.left, CASHOUT_RECT.top, hands + blind_get_reward(current_blind)); // Print the cash out amount tte_printf("#{P:%d, %d; cx:0x%X000}Cash Out: $%d", CASHOUT_RECT.left, CASHOUT_RECT.top, TTE_WHITE_PB, hands + blind_get_reward(current_blind)); // Print the cash out amount
} }
else if (timer > FRAMES(40) && key_hit(SELECT_CARD)) // Wait until the player presses A to cash out else if (timer > FRAMES(40) && key_hit(SELECT_CARD)) // Wait until the player presses A to cash out
{ {
@@ -2186,7 +2205,7 @@ void print_price_under_sprite_object(SpriteObject *sprite_object, int price)
{ {
int x = fx2int(sprite_object->tx) + TILE_SIZE - (get_digits_even(price) - 1) * TILE_SIZE; int x = fx2int(sprite_object->tx) + TILE_SIZE - (get_digits_even(price) - 1) * TILE_SIZE;
int y = fx2int(sprite_object->ty) + CARD_SPRITE_SIZE + TILE_SIZE; // TODO: Should probably extract the sprite size int y = fx2int(sprite_object->ty) + CARD_SPRITE_SIZE + TILE_SIZE; // TODO: Should probably extract the sprite size
tte_printf("#{P:%d,%d; cx:0xC000}$%d", x, y, price); tte_printf("#{P:%d,%d; cx:0x%X000}$%d", x, y, TTE_YELLOW_PB, price);
} }
void erase_price_under_sprite_object(SpriteObject *sprite_object) void erase_price_under_sprite_object(SpriteObject *sprite_object)
@@ -2296,7 +2315,7 @@ static void game_shop_reroll(int *reroll_cost)
} }
(*reroll_cost)++; (*reroll_cost)++;
tte_printf("#{P:%d,%d; cx:0xF000}$%d", SHOP_REROLL_RECT.left, SHOP_REROLL_RECT.top, *reroll_cost); tte_printf("#{P:%d,%d; cx:0x%X000}$%d", SHOP_REROLL_RECT.left, SHOP_REROLL_RECT.top, TTE_WHITE_PB, *reroll_cost);
} }
static int jokers_sel_row_get_size() static int jokers_sel_row_get_size()
@@ -2503,7 +2522,7 @@ static void game_shop_process_user_input()
// to trigger the selection change so the initial selection is visible // to trigger the selection change so the initial selection is visible
shop_selection_grid.selection = SHOP_INIT_SEL; shop_selection_grid.selection = SHOP_INIT_SEL;
selection_grid_move_selection_horz(&shop_selection_grid, 1); selection_grid_move_selection_horz(&shop_selection_grid, 1);
tte_printf("#{P:%d,%d; cx:0xF000}$%d", SHOP_REROLL_RECT.left, SHOP_REROLL_RECT.top, reroll_cost); tte_printf("#{P:%d,%d; cx:0x%X000}$%d", SHOP_REROLL_RECT.left, SHOP_REROLL_RECT.top, TTE_WHITE_PB, reroll_cost);
} }
// Shop input logic // Shop input logic
@@ -2805,6 +2824,84 @@ void game_blind_select()
} }
} }
static void discarded_jokers_update_loop()
{
if (discarded_jokers == NULL)
return;
// Iterating backwards because of removal within loop
for (int i = list_get_size(discarded_jokers) - 1; i >= 0; i--)
{
JokerObject* joker_object = list_get(discarded_jokers, i);
joker_object_update(joker_object);
if (joker_object->sprite_object->x == joker_object->sprite_object->tx
&& joker_object->sprite_object->y == joker_object->sprite_object->ty)
{
list_remove_by_idx(discarded_jokers, i);
joker_object_destroy(&joker_object);
}
}
}
static void held_jokers_update_loop()
{
const int spacing_lut[MAX_JOKERS_HELD_SIZE][MAX_JOKERS_HELD_SIZE] =
{
{0, 0, 0, 0, 0},
{13, -13, 0, 0, 0},
{26, 0, -26, 0, 0},
{39, 13, -13, -39, 0},
{40, 20, 0, -20, -40}
};
FIXED hand_x = int2fx(HELD_JOKERS_POS.x);
int jokers_top = list_get_size(jokers) - 1;
for (int i = jokers_top; i >= 0; i--)
{
JokerObject *joker = list_get(jokers, i);
joker->sprite_object->tx = hand_x - int2fx(spacing_lut[jokers_top][i]);
joker_object_update(joker);
}
}
static void jokers_update_loop()
{
held_jokers_update_loop();
discarded_jokers_update_loop();
}
static void game_over_anim_frame()
{
main_bg_se_move_rect_1_tile_vert(GAME_OVER_ANIM_RECT, SE_UP);
}
static void game_lose()
{
if (timer < GAME_OVER_ANIM_FRAMES)
{
game_over_anim_frame();
}
else if (timer == GAME_OVER_ANIM_FRAMES)
{
tte_printf("#{P:%d,%d; cx:0x%X000}GAME OVER", GAME_LOSE_MSG_TEXT_RECT.left, GAME_LOSE_MSG_TEXT_RECT.top, TTE_RED_PB);
}
}
static void game_win()
{
if (timer < GAME_OVER_ANIM_FRAMES)
{
game_over_anim_frame();
}
else if (timer == GAME_OVER_ANIM_FRAMES)
{
tte_printf("#{P:%d,%d; cx:0x%X000}YOU WIN", GAME_WIN_MSG_TEXT_RECT.left, GAME_WIN_MSG_TEXT_RECT.top, TTE_BLUE_PB);
}
}
void game_update() void game_update()
{ {
timer++; timer++;
@@ -2835,7 +2932,10 @@ void game_update()
game_blind_select(); game_blind_select();
break; break;
case GAME_LOSE: case GAME_LOSE:
// Handle lose logic here game_lose();
break;
case GAME_WIN:
game_win();
break; break;
} }
} }
+101 -18
View File
@@ -24,7 +24,7 @@ static void clip_se_rect_to_screenblock(Rect* rect)
clip_se_rect_to_bounding_rect(rect, &FULL_SCREENBLOCK_RECT); clip_se_rect_to_bounding_rect(rect, &FULL_SCREENBLOCK_RECT);
} }
u16 main_bg_se_get_tile(BG_POINT pos) SE main_bg_se_get_se(BG_POINT pos)
{ {
return se_mat[MAIN_BG_SBB][pos.y][pos.x]; return se_mat[MAIN_BG_SBB][pos.y][pos.x];
} }
@@ -59,11 +59,10 @@ void main_bg_se_clear_rect(Rect se_rect)
} }
} }
// Internal static function to merge implementation of move/copy functions. // Internal static function to merge implementation of move/copy functions.
static void main_bg_se_copy_or_move_rect_1_tile_vert(Rect se_rect, int direction, bool move) static void bg_se_copy_or_move_rect_1_tile_vert(u16 bg_sbb, Rect se_rect, int direction, bool move)
{ {
if (se_rect.left > se_rect.right if (se_rect.left > se_rect.right
|| (direction != SE_UP && direction != SE_DOWN)) || (direction != SE_UP && direction != SE_DOWN))
{ {
return; return;
@@ -77,17 +76,32 @@ static void main_bg_se_copy_or_move_rect_1_tile_vert(Rect se_rect, int direction
for (int y = start; y != end - direction; y -= direction) for (int y = start; y != end - direction; y -= direction)
{ {
memcpy16(&(se_mat[MAIN_BG_SBB][y + direction][se_rect.left]), memcpy16(&(se_mat[bg_sbb][y + direction][se_rect.left]),
&se_mat[MAIN_BG_SBB][y][se_rect.left], &se_mat[bg_sbb][y][se_rect.left],
rect_width(&se_rect)); rect_width(&se_rect));
} }
if (move) if (move)
{ {
memset16(&se_mat[MAIN_BG_SBB][end][se_rect.left], 0x0000, rect_width(&se_rect)); memset16(&se_mat[bg_sbb][end][se_rect.left], 0x0000, rect_width(&se_rect));
} }
} }
static void main_bg_se_copy_or_move_rect_1_tile_vert(Rect se_rect, int direction, bool move)
{
bg_se_copy_or_move_rect_1_tile_vert(MAIN_BG_SBB, se_rect, direction, move);
}
void bg_se_copy_rect_1_tile_vert(u16 bg_sbb, Rect se_rect, int direction)
{
bg_se_copy_or_move_rect_1_tile_vert(MAIN_BG_SBB, se_rect, direction, false);
}
void bg_se_move_rect_1_tile_vert(u16 bg_sbb, Rect se_rect, int direction)
{
bg_se_copy_or_move_rect_1_tile_vert(MAIN_BG_SBB, se_rect, direction, true);
}
void main_bg_se_copy_rect_1_tile_vert(Rect se_rect, int direction) void main_bg_se_copy_rect_1_tile_vert(Rect se_rect, int direction)
{ {
main_bg_se_copy_or_move_rect_1_tile_vert(se_rect, direction, false); main_bg_se_copy_or_move_rect_1_tile_vert(se_rect, direction, false);
@@ -108,20 +122,18 @@ void main_bg_se_copy_rect(Rect se_rect, BG_POINT pos)
int width = rect_width(&se_rect); int width = rect_width(&se_rect);
int height = rect_height(&se_rect); int height = rect_height(&se_rect);
u16 tile_map[height][width]; SE tile_map[height][width];
// Copy the rect to the tile map // Copy the rect to the tile map
for (int sy = 0; sy < height; sy++) for (int sy = 0; sy < height; sy++)
{ {
for (int sx = 0; sx < width; sx++) memcpy16(&tile_map[sy][0],
{ &se_mat[MAIN_BG_SBB][se_rect.top + sy][se_rect.left],
BG_POINT pt; width);
pt.x = se_rect.left + sx;
pt.y = se_rect.top + sy;
tile_map[sy][sx] = main_bg_se_get_tile(pt);
}
} }
// TODO: Avoid overflow
// Copy the tilemap to the new rect position
for (int sy = 0; sy < height; sy++) for (int sy = 0; sy < height; sy++)
{ {
memcpy16(&se_mat[MAIN_BG_SBB][pos.y + sy][pos.x], memcpy16(&se_mat[MAIN_BG_SBB][pos.y + sy][pos.x],
@@ -130,7 +142,7 @@ void main_bg_se_copy_rect(Rect se_rect, BG_POINT pos)
} }
} }
void main_bg_se_copy_tile_to_rect(u16 tile, Rect se_rect) void main_bg_se_fill_rect_with_se(SE se, Rect se_rect)
{ {
if (se_rect.left > se_rect.right || se_rect.top > se_rect.bottom) if (se_rect.left > se_rect.right || se_rect.top > se_rect.bottom)
return; return;
@@ -143,7 +155,78 @@ void main_bg_se_copy_tile_to_rect(u16 tile, Rect se_rect)
for (int sy = 0; sy < height; sy++) for (int sy = 0; sy < height; sy++)
{ {
memset16(&se_mat[MAIN_BG_SBB][se_rect.top + sy][se_rect.left], tile, width); memset16(&se_mat[MAIN_BG_SBB][se_rect.top + sy][se_rect.left], se, width);
}
}
// Helper: Copy the corners of a 3x3 tile block
static void main_bg_se_expand_3x3_copy_corners(const Rect* se_dest_rect, const BG_POINT* src_top_left_pnt, int dest_rect_width, int dest_rect_height)
{
SE top_left_se = se_mat[MAIN_BG_SBB][src_top_left_pnt->y][src_top_left_pnt->x];
se_mat[MAIN_BG_SBB][se_dest_rect->top][se_dest_rect->left] = top_left_se;
SE top_right_se = se_mat[MAIN_BG_SBB][src_top_left_pnt->y][src_top_left_pnt->x + 2];
se_mat[MAIN_BG_SBB][se_dest_rect->top][se_dest_rect->left + dest_rect_width - 1] = top_right_se;
SE bottom_left_se = se_mat[MAIN_BG_SBB][src_top_left_pnt->y + 2][src_top_left_pnt->x];
se_mat[MAIN_BG_SBB][se_dest_rect->top + dest_rect_height - 1][se_dest_rect->left] = bottom_left_se;
SE bottom_right_se = se_mat[MAIN_BG_SBB][src_top_left_pnt->y + 2][src_top_left_pnt->x + 2];
se_mat[MAIN_BG_SBB][se_dest_rect->top + dest_rect_height - 1][se_dest_rect->left + dest_rect_width - 1] = bottom_right_se;
}
// Helper: Copy the top and bottom sides of a 3x3 tile block
static void main_bg_se_expand_3x3_copy_top_bottom(const Rect* se_dest_rect, const BG_POINT* src_top_left_pnt, int dest_rect_width)
{
if (dest_rect_width > 2)
{
SE top_middle_se = se_mat[MAIN_BG_SBB][src_top_left_pnt->y][src_top_left_pnt->x + 1];
SE bottom_middle_se = se_mat[MAIN_BG_SBB][src_top_left_pnt->y + 2][src_top_left_pnt->x + 1];
memset16(&se_mat[MAIN_BG_SBB][se_dest_rect->top][se_dest_rect->left + 1], top_middle_se, dest_rect_width - 2);
memset16(&se_mat[MAIN_BG_SBB][se_dest_rect->bottom][se_dest_rect->left + 1], bottom_middle_se, dest_rect_width - 2);
}
}
// Helper: Copy the left and right sides of a 3x3 tile block
static void main_bg_se_expand_3x3_copy_left_right(const Rect* se_dest_rect, const BG_POINT* src_top_left_pnt, int dest_rect_width, int dest_rect_height)
{
SE middle_left_se = se_mat[MAIN_BG_SBB][src_top_left_pnt->y + 1][src_top_left_pnt->x];
SE middle_right_se = se_mat[MAIN_BG_SBB][src_top_left_pnt->y + 1][src_top_left_pnt->x + 2];
for (int y = 1; y < dest_rect_height - 1; y++)
{
se_mat[MAIN_BG_SBB][se_dest_rect->top + y][se_dest_rect->left] = middle_left_se;
se_mat[MAIN_BG_SBB][se_dest_rect->top + y][se_dest_rect->left + dest_rect_width - 1] = middle_right_se;
}
}
void main_bg_se_copy_expand_3x3_rect(Rect se_dest_rect, BG_POINT src_top_left_pnt)
{
clip_se_rect_to_screenblock(&se_dest_rect);
int dest_rect_width = rect_width(&se_dest_rect);
int dest_rect_height = rect_height(&se_dest_rect);
// Verify the dest rect is at least 2x2
if (dest_rect_width < 2 || dest_rect_height < 2)
{
return;
}
// Copy the corners
main_bg_se_expand_3x3_copy_corners(&se_dest_rect, &src_top_left_pnt, dest_rect_width, dest_rect_height);
// Copy top and bottom sides
main_bg_se_expand_3x3_copy_top_bottom(&se_dest_rect, &src_top_left_pnt, dest_rect_width);
// Copy left and right sides
main_bg_se_expand_3x3_copy_left_right(&se_dest_rect, &src_top_left_pnt, dest_rect_width, dest_rect_height);
// Fill the center if needed
if (dest_rect_width > 2 && dest_rect_height > 2)
{
SE middle_fill_se = se_mat[MAIN_BG_SBB][src_top_left_pnt.y + 1][src_top_left_pnt.x + 1];
Rect dest_inner_fill_rect = {se_dest_rect.left + 1, se_dest_rect.top + 1, se_dest_rect.right - 1, se_dest_rect.bottom - 1};
main_bg_se_fill_rect_with_se(middle_fill_se, dest_inner_fill_rect);
} }
} }
+5 -5
View File
@@ -28,15 +28,15 @@ void init()
mmStart(MOD_MAIN_THEME, MM_PLAY_LOOP); mmStart(MOD_MAIN_THEME, MM_PLAY_LOOP);
// Initialize text engine // Initialize text engine
tte_init_se(0, BG_CBB(TTE_CBB) | BG_SBB(TTE_SBB), 0, CLR_WHITE, 14, NULL, NULL); tte_init_se(0, BG_CBB(TTE_CBB) | BG_SBB(TTE_SBB), 0, CLR_WHITE, TTE_BIT_UNPACK_OFFSET, NULL, NULL);
tte_erase_screen(); tte_erase_screen();
tte_init_con(); tte_init_con();
// TTE palette setup // TTE palette setup
pal_bg_bank[12][15] = 0x029F; // Yellow. honestly fuck libtonc because i cannot figure out how you're supposed to select a color from the palette index so i'm doing it like this pal_bg_bank[TTE_YELLOW_PB][TTE_BIT_ON_CLR_IDX] = TEXT_CLR_YELLOW;
pal_bg_bank[13][15] = 0x7E40; // Blue pal_bg_bank[TTE_BLUE_PB][TTE_BIT_ON_CLR_IDX] = TEXT_CLR_BLUE;
pal_bg_bank[14][15] = 0x213F; // Red pal_bg_bank[TTE_RED_PB][TTE_BIT_ON_CLR_IDX] = TEXT_CLR_RED;
pal_bg_bank[15][15] = CLR_WHITE; pal_bg_bank[TTE_WHITE_PB][TTE_BIT_ON_CLR_IDX] = TEXT_CLR_WHITE;
// Set up the video mode // Set up the video mode
// BG0 is the TTE text layer // BG0 is the TTE text layer