Merge branch 'main' of https://github.com/cellos51/balatro-gba into hblank_affine_background_effects

This commit is contained in:
cellos51
2025-08-25 19:34:25 -04:00
29 changed files with 1308 additions and 503 deletions
+638 -372
View File
File diff suppressed because it is too large Load Diff
+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);
}
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];
}
@@ -59,11 +59,10 @@ void main_bg_se_clear_rect(Rect se_rect)
}
}
// 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))
{
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)
{
memcpy16(&(se_mat[MAIN_BG_SBB][y + direction][se_rect.left]),
&se_mat[MAIN_BG_SBB][y][se_rect.left],
memcpy16(&(se_mat[bg_sbb][y + direction][se_rect.left]),
&se_mat[bg_sbb][y][se_rect.left],
rect_width(&se_rect));
}
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)
{
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 height = rect_height(&se_rect);
u16 tile_map[height][width];
SE tile_map[height][width];
// Copy the rect to the tile map
for (int sy = 0; sy < height; sy++)
{
for (int sx = 0; sx < width; sx++)
{
BG_POINT pt;
pt.x = se_rect.left + sx;
pt.y = se_rect.top + sy;
tile_map[sy][sx] = main_bg_se_get_tile(pt);
}
memcpy16(&tile_map[sy][0],
&se_mat[MAIN_BG_SBB][se_rect.top + sy][se_rect.left],
width);
}
// TODO: Avoid overflow
// Copy the tilemap to the new rect position
for (int sy = 0; sy < height; sy++)
{
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)
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++)
{
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);
}
}
+16 -1
View File
@@ -130,6 +130,9 @@ void joker_init()
joker_gfxTiles[9] = joker_gfx9Tiles;
joker_gfxTiles[10] = joker_gfx10Tiles;
joker_gfxTiles[11] = joker_gfx11Tiles;
joker_gfxTiles[12] = joker_gfx12Tiles;
joker_gfxTiles[13] = joker_gfx13Tiles;
joker_gfxTiles[14] = joker_gfx14Tiles;
joker_gfxPal[0] = joker_gfx0Pal;
joker_gfxPal[1] = joker_gfx1Pal;
@@ -143,6 +146,9 @@ void joker_init()
joker_gfxPal[9] = joker_gfx9Pal;
joker_gfxPal[10] = joker_gfx10Pal;
joker_gfxPal[11] = joker_gfx11Pal;
joker_gfxPal[12] = joker_gfx12Pal;
joker_gfxPal[13] = joker_gfx13Pal;
joker_gfxPal[14] = joker_gfx14Pal;
for (int i = 0; i < num_spritesheets; i++)
{
@@ -181,6 +187,16 @@ JokerEffect joker_get_score_effect(Joker *joker, Card *scored_card)
return jinfo->effect(joker, scored_card);
}
int joker_get_sell_value(const Joker* joker)
{
if (joker == NULL)
{
return UNDEFINED;
}
return joker->value/2;
}
// JokerObject methods
JokerObject *joker_object_new(Joker *joker)
{
@@ -320,7 +336,6 @@ bool joker_object_score(JokerObject *joker_object, Card* scored_card, int *chips
return false;
}
void joker_object_set_selected(JokerObject* joker_object, bool selected)
{
if (joker_object == NULL)
+226 -25
View File
@@ -2,6 +2,7 @@
#include "joker.h"
#include "util.h"
#include "hand_analysis.h"
#include "list.h"
#include <stdlib.h>
static JokerEffect default_joker_effect(Joker *joker, Card *scored_card) {
@@ -191,16 +192,19 @@ static JokerEffect joker_stencil_effect(Joker *joker, Card *scored_card) {
if (scored_card != NULL)
return effect; // if card != null, we are not at the end-phase of scoring yet
List* jokers = get_jokers();
// +1 xmult per empty joker slot...
int num_jokers = get_jokers_top() + 1;
int num_jokers = list_get_size(jokers);
effect.xmult = (MAX_JOKERS_HELD_SIZE) - num_jokers;
// ...and also each stencil_joker adds +1 xmult
JokerObject** jokers = get_jokers();
for (int i = 0; i < num_jokers; i++ )
{
if (jokers[i]->joker->id == JOKER_STENCIL_ID)
JokerObject* joker_object = list_get(jokers, i);
if (joker_object->joker->id == JOKER_STENCIL_ID)
effect.xmult++;
}
@@ -290,30 +294,227 @@ static JokerEffect blackboard_joker_effect(Joker *joker, Card *scored_card) {
return effect;
}
static JokerEffect blue_joker_effect(Joker *joker, Card *scored_card) {
JokerEffect effect = {0};
if (scored_card != NULL)
return effect; // if card != null, we are not at the end-phase of scoring yet
effect.chips = (get_deck_top() + 1) * 2;
return effect;
}
// Using __attribute__((unused)) for jokers with no sprites yet to avoid warning
// Remove the attribute once they have sprites
__attribute__((unused))
static JokerEffect raised_fist_joker_effect(Joker *joker, Card *scored_card)
{
JokerEffect effect = {0};
if (scored_card != NULL)
return effect; // if card != null, we are not at the end-phase of scoring yet
// Find the lowest rank card in hand
// Aces are always considered high value, even in an ace-low straight
u8 lowest_value = IMPOSSIBLY_HIGH_CARD_VALUE;
CardObject** hand = get_hand_array();
int hand_size = hand_get_size();
for (int i = 0; i < hand_size; i++ )
{
u8 value = card_get_value(hand[i]->card);
if (lowest_value > value)
lowest_value = value;
}
if (lowest_value != IMPOSSIBLY_HIGH_CARD_VALUE)
effect.mult = lowest_value * 2;
return effect;
}
__attribute__((unused))
static JokerEffect reserved_parking_joker_effect(Joker *joker, Card *scored_card) {
JokerEffect effect = {0};
if (scored_card != NULL)
return effect; // if card != null, we are not at the end-phase of scoring yet
CardObject** hand = get_hand_array();
int hand_size = hand_get_size();
for (int i = 0; i < hand_size; i++ )
{
switch (hand[i]->card->rank) {
case KING: case QUEEN: case JACK:
if (random() % 2 == 0)
effect.money += 1;
default:
break;
}
}
return effect;
};
static JokerEffect business_card_joker_effect(Joker *joker, Card *scored_card) {
JokerEffect effect = {0};
if (scored_card == NULL)
return effect;
switch (scored_card->rank) {
case KING: case QUEEN: case JACK:
if (random() % 2 == 0)
effect.money = 2;
default:
break;
}
return effect;
}
static JokerEffect scholar_joker_effect(Joker *joker, Card *scored_card) {
JokerEffect effect = {0};
if (scored_card == NULL)
return effect;
if (scored_card->rank == ACE) {
effect.chips = 20;
effect.mult = 4;
}
return effect;
}
static JokerEffect scary_face_joker_effect(Joker *joker, Card *scored_card) {
JokerEffect effect = {0};
if (scored_card == NULL)
return effect;
switch (scored_card->rank) {
case KING: case QUEEN: case JACK:
effect.chips = 30;
default:
break;
}
return effect;
}
__attribute__((unused))
static JokerEffect abstract_joker_effect(Joker *joker, Card *scored_card) {
JokerEffect effect = {0};
if (scored_card != NULL)
return effect; // if card != null, we are not at the end-phase of scoring yet
// +1 xmult per occupied joker slot
int num_jokers = list_get_size(get_jokers());
effect.mult = num_jokers * 3;
return effect;
}
__attribute__((unused))
static JokerEffect bull_joker_effect(Joker *joker, Card *scored_card) {
JokerEffect effect = {0};
if (scored_card != NULL)
return effect; // if card != null, we are not at the end-phase of scoring yet
effect.chips = get_money() * 2;
return effect;
}
static JokerEffect smiley_face_joker_effect(Joker *joker, Card *scored_card) {
JokerEffect effect = {0};
if (scored_card == NULL)
return effect;
switch (scored_card->rank) {
case KING: case QUEEN: case JACK:
effect.mult = 5;
default:
break;
}
return effect;
}
static JokerEffect even_steven_joker_effect(Joker *joker, Card *scored_card) {
JokerEffect effect = {0};
if (scored_card == NULL)
return effect;
if (card_get_value(scored_card) % 2 == 0) {
switch (scored_card->rank) {
case KING: case QUEEN: case JACK:
break;
default:
effect.mult = 4;
}
}
return effect;
}
static JokerEffect odd_todd_joker_effect(Joker *joker, Card *scored_card) {
JokerEffect effect = {0};
if (scored_card == NULL)
return effect;
if (card_get_value(scored_card) % 2 == 1) // todo test ace
effect.chips = 31;
return effect;
}
/* The index of a joker in the registry matches its ID.
* The joker sprites are matched by ID so the position in the registry
* determines the joker's sprite.
* Each consecutive NUM_JOKERS_PER_SPRITESHEET (defined in joker.c) jokers
* share a spritesheet and thus a color palette.
* To make better use of color palettes jokers may be rearranged here
* (and put together in the matching spritesheet) to share a color palette.
* Otherwise the order is similar to the wiki.
*/
const JokerInfo joker_registry[] = {
{ COMMON_JOKER, 2, default_joker_effect }, // DEFAULT_JOKER_ID = 0
{ COMMON_JOKER, 5, greedy_joker_effect }, // GREEDY_JOKER_ID = 1
{ COMMON_JOKER, 5, lusty_joker_effect }, // etc...
{ COMMON_JOKER, 5, wrathful_joker_effect },
{ COMMON_JOKER, 5, gluttonous_joker_effect },
{ COMMON_JOKER, 3, jolly_joker_effect },
{ COMMON_JOKER, 4, zany_joker_effect },
{ COMMON_JOKER, 4, mad_joker_effect },
{ COMMON_JOKER, 4, crazy_joker_effect },
{ COMMON_JOKER, 4, droll_joker_effect },
{ COMMON_JOKER, 3, sly_joker_effect },
{ COMMON_JOKER, 4, wily_joker_effect },
{ COMMON_JOKER, 4, clever_joker_effect },
{ COMMON_JOKER, 4, devious_joker_effect },
{ COMMON_JOKER, 4, crafty_joker_effect },
{ COMMON_JOKER, 5, half_joker_effect },
{ UNCOMMON_JOKER, 8, joker_stencil_effect },
{ COMMON_JOKER, 5, banner_joker_effect },
{ COMMON_JOKER, 4, walkie_talkie_joker_effect },
{ UNCOMMON_JOKER, 8, fibonnaci_joker_effect },
{ UNCOMMON_JOKER, 6, blackboard_joker_effect },
{ COMMON_JOKER, 5, mystic_summit_joker_effect },
{ COMMON_JOKER, 4, misprint_joker_effect },
{ COMMON_JOKER, 5, greedy_joker_effect }, // GREEDY_JOKER_ID = 1
{ COMMON_JOKER, 5, lusty_joker_effect }, // etc... 2
{ COMMON_JOKER, 5, wrathful_joker_effect }, // 3
{ COMMON_JOKER, 5, gluttonous_joker_effect }, // 4
{ COMMON_JOKER, 3, jolly_joker_effect }, // 5
{ COMMON_JOKER, 4, zany_joker_effect }, // 6
{ COMMON_JOKER, 4, mad_joker_effect }, // 7
{ COMMON_JOKER, 4, crazy_joker_effect }, // 8
{ COMMON_JOKER, 4, droll_joker_effect }, // 9
{ COMMON_JOKER, 3, sly_joker_effect }, // 10
{ COMMON_JOKER, 4, wily_joker_effect }, // 11
{ COMMON_JOKER, 4, clever_joker_effect }, // 12
{ COMMON_JOKER, 4, devious_joker_effect }, // 13
{ COMMON_JOKER, 4, crafty_joker_effect }, // 14
{ COMMON_JOKER, 5, half_joker_effect }, // 15
{ UNCOMMON_JOKER, 8, joker_stencil_effect }, // 16
{ COMMON_JOKER, 5, banner_joker_effect }, // 17
{ COMMON_JOKER, 4, walkie_talkie_joker_effect }, // 18
{ UNCOMMON_JOKER, 8, fibonnaci_joker_effect }, // 19
{ UNCOMMON_JOKER, 6, blackboard_joker_effect }, // 20
{ COMMON_JOKER, 5, mystic_summit_joker_effect }, // 21
{ COMMON_JOKER, 4, misprint_joker_effect }, // 22
{ COMMON_JOKER, 4, even_steven_joker_effect }, // 23
{ COMMON_JOKER, 5, blue_joker_effect }, // 24
{ COMMON_JOKER, 4, odd_todd_joker_effect }, // 25
{ COMMON_JOKER, 4, scholar_joker_effect }, // 26
{ COMMON_JOKER, 4, business_card_joker_effect }, // 27
// Business card should be paired with Shortcut for palette optimization when it's added
{ COMMON_JOKER, 4, scary_face_joker_effect }, // 28
{ COMMON_JOKER, 4, smiley_face_joker_effect }, // 29
// The following jokers don't have sprites yet,
// uncomment them when their sprites are added.
#if 0
{ COMMON_JOKER, 5, raised_fist_joker_effect },
{ COMMON_JOKER, 6, reserved_parking_joker_effect },
{ COMMON_JOKER, 4, abstract_joker_effect },
{ UNCOMMON_JOKER, 6, bull_joker_effect},
#endif
};
static const size_t joker_registry_size = NUM_ELEM_IN_ARR(joker_registry);
+29 -12
View File
@@ -1,12 +1,13 @@
#include <stdlib.h>
#include <stdbool.h>
#include <stdint.h>
#include "list.h"
#include "util.h"
IntList *int_list_new(int init_size) {
IntList *list = (IntList *)malloc(sizeof(IntList));
List *list_new(int init_size) {
List *list = (List *)malloc(sizeof(List));
if (list == NULL) return NULL;
list->_array = (int *)malloc(sizeof(int) * init_size);
list->_array = (void **)malloc(sizeof(void*) * init_size);
if (!list->_array)
{
free(list);
@@ -17,7 +18,7 @@ IntList *int_list_new(int init_size) {
return list;
}
void int_list_destroy(IntList **list) {
void list_destroy(List **list) {
if (list == NULL || *list == NULL)
return;
{
@@ -28,22 +29,28 @@ void int_list_destroy(IntList **list) {
*list = NULL;
}
bool int_list_append(IntList *list, int value)
bool int_list_append(List *list, intptr_t value)
{
return list_append(list, (void*)value);
}
bool list_append(List *list, void *value)
{
if (list->size >= list->allocated_size)
{
int new_size = list->allocated_size * 2;
int *new_arr = (int *)realloc(list->_array, sizeof(int) * new_size);
void **new_arr = (void **)realloc(list->_array, sizeof(void*) * new_size);
if (new_arr == NULL)
return false;
list->_array = new_arr;
list->allocated_size = new_size;
}
list->_array[list->size++] = value;
return true;
}
bool int_list_remove_by_idx(IntList *list, int index) {
bool list_remove_by_idx(List *list, int index) {
if (index < 0 || index >= list->size)
return false;
for (int i = index; i < list->size - 1; ++i)
@@ -54,27 +61,37 @@ bool int_list_remove_by_idx(IntList *list, int index) {
return true;
}
bool int_list_remove_by_value(IntList *list, int value)
bool list_remove_by_value(List *list, void* value)
{
for (int i = 0; i < list->size; i++)
{
if (list->_array[i] == value)
{
return int_list_remove_by_idx(list, i);
return list_remove_by_idx(list, i);
}
}
return false;
}
int int_list_get(IntList *list, int index)
bool int_list_remove_by_value(List *list, intptr_t value)
{
return list_remove_by_value(list, (void*)value);
}
void* list_get(List *list, int index)
{
if (index < 0 || index >= list->size)
return 0;
return NULL;
return list->_array[index];
}
int int_list_get_size(IntList *list)
intptr_t int_list_get(List *list, int index)
{
return (intptr_t)list_get(list, index);
}
int list_get_size(List *list)
{
if (list == NULL)
{
+5 -5
View File
@@ -29,15 +29,15 @@ void init()
mmStart(MOD_MAIN_THEME, MM_PLAY_LOOP);
// 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_init_con();
// 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[13][15] = 0x7E40; // Blue
pal_bg_bank[14][15] = 0x213F; // Red
pal_bg_bank[15][15] = CLR_WHITE;
pal_bg_bank[TTE_YELLOW_PB][TTE_BIT_ON_CLR_IDX] = TEXT_CLR_YELLOW;
pal_bg_bank[TTE_BLUE_PB][TTE_BIT_ON_CLR_IDX] = TEXT_CLR_BLUE;
pal_bg_bank[TTE_RED_PB][TTE_BIT_ON_CLR_IDX] = TEXT_CLR_RED;
pal_bg_bank[TTE_WHITE_PB][TTE_BIT_ON_CLR_IDX] = TEXT_CLR_WHITE;
// Set up the video mode
// BG0 is the TTE text layer
+85
View File
@@ -0,0 +1,85 @@
#include "selection_grid.h"
static void selection_grid_process_directional_input(SelectionGrid *selection_grid)
{
int horz_tri_input = bit_tribool(key_hit(KEY_ANY), KI_RIGHT, KI_LEFT);
if (horz_tri_input != 0)
{
selection_grid_move_selection_horz(selection_grid, horz_tri_input);
/* Avoid handling both vertical and horizontal input at the same time,
* it creates all sorts of difficult edge cases.
*/
return;
}
int vert_tri_input = bit_tribool(key_hit(KEY_ANY), KI_DOWN, KI_UP);
if (vert_tri_input != 0)
{
selection_grid_move_selection_vert(selection_grid, vert_tri_input);
}
}
void selection_grid_move_selection_horz(SelectionGrid* selection_grid,
int direction_tribool)
{
Selection new_selection = selection_grid->selection;
new_selection.x += direction_tribool;
if( selection_grid->selection.y >= 0
&& selection_grid->selection.y < selection_grid->num_rows
&& new_selection.x >= 0
&& new_selection.x < selection_grid->rows[new_selection.y].get_size())
{
selection_grid->rows[new_selection.y].on_selection_changed(selection_grid, new_selection.y, &selection_grid->selection, &new_selection);
selection_grid->selection = new_selection;
}
}
void selection_grid_move_selection_vert(SelectionGrid *selection_grid, int direction_tribool)
{
Selection selection = selection_grid->selection;
Selection new_selection = selection;
new_selection.y += direction_tribool;
if (new_selection.y >= 0
&& new_selection.y < selection_grid->num_rows)
{
int new_row_size = selection_grid->rows[new_selection.y].get_size();
if (new_row_size <= 0)
return;
if (selection.x >= new_row_size)
{
// TODO: Maintain relative horizontal position
// Clip selection to row size
new_selection.x = new_row_size - 1;
}
if (selection.y >= 0 && selection.y < selection_grid->num_rows)
{
selection_grid->rows[selection.y].on_selection_changed(selection_grid, selection.y, &selection, &new_selection);
}
selection_grid->rows[new_selection.y].on_selection_changed(selection_grid, new_selection.y, &selection, &new_selection);
selection_grid->selection = new_selection;
}
}
void selection_grid_process_input(SelectionGrid *selection_grid)
{
if (selection_grid == NULL || selection_grid->rows == NULL)
return;
selection_grid_process_directional_input(selection_grid);
u32 non_directional_key = KEY_ANY & ~KEY_DIR;
if (key_hit(non_directional_key))
{
Selection* selection = &selection_grid->selection; // To make the next line shorter and more readable
selection_grid->rows[selection->y].on_key_hit(selection_grid, selection);
}
}
+20
View File
@@ -1,6 +1,7 @@
#include "sprite.h"
#include "util.h"
#include "audio_utils.h"
#include "soundbank.h"
#include <tonc.h>
#include <stdlib.h>
@@ -8,6 +9,7 @@
#define MAX_SPRITES 128
#define MAX_AFFINES 32
#define SPRITE_FOCUS_RAISE_PX 10
OBJ_ATTR obj_buffer[MAX_SPRITES];
OBJ_AFFINE *obj_aff_buffer = (OBJ_AFFINE*)obj_buffer;
@@ -117,6 +119,7 @@ SpriteObject* sprite_object_new()
sprite_object->sprite = NULL;
sprite_object_reset_transform(sprite_object);
sprite_object->selected = false;
sprite_object->focused = false;
return sprite_object;
}
@@ -239,3 +242,20 @@ Sprite* sprite_object_get_sprite(SpriteObject* sprite_object)
return NULL;
return sprite_object->sprite;
}
void sprite_object_set_focus(SpriteObject* sprite_object, bool focus)
{
if (sprite_object->focused == focus)
{
return;
}
sprite_object->focused = focus;
play_sfx(SFX_CARD_FOCUS , MM_BASE_PITCH_RATE + rand() % 512);
sprite_object->ty = sprite_object->ty + int2fx((focus ? -1 : 1) * SPRITE_FOCUS_RAISE_PX);
}
bool sprite_object_is_focused(SpriteObject* sprite_object)
{
return sprite_object->focused;
}