(Manual conflict res on game.c) Merged main into ttm/joker-scop-1

This commit is contained in:
MeirGavish
2025-08-17 10:37:03 +03:00
9 changed files with 447 additions and 228 deletions
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

+14 -14
View File
@@ -3,23 +3,23 @@
#include <stdbool.h> #include <stdbool.h>
/* Defining an int list for simplicity typedef struct List
* If another list is needed, a generic void*
* list should be defined....
*/
typedef struct IntList
{ {
int* _array; void** _array;
int size; int size;
int allocated_size; int allocated_size;
} IntList; } List;
IntList *int_list_new(int init_size); List *list_new(int init_size);
void int_list_destroy(IntList **list); void list_destroy(List **list);
bool int_list_append(IntList *list, int value); bool list_append(List *list, void *value);
bool int_list_remove_by_idx(IntList *list, int index); bool list_remove_by_idx(List *list, int index);
int int_list_get(IntList *list, int index); void* list_get(List *list, int index);
int int_list_get_size(IntList *list); int list_get_size(List *list);
bool int_list_remove_by_value(IntList *list, int value); bool list_remove_by_value(List *list, void *value);
bool int_list_append(List *list, intptr_t value);
intptr_t int_list_get(List *list, int index);
bool int_list_remove_by_value(List *list, intptr_t value);
#endif #endif
+41
View File
@@ -0,0 +1,41 @@
#ifndef SELECTION_GRID_H
#define SELECTION_GRID_H
#include <tonc.h>
typedef POINT Selection;
struct SelectionGridRow;
struct SelectionGrid;
typedef struct SelectionGridRow SelectionGridRow;
typedef struct SelectionGrid SelectionGrid;
// Called whenever there is a change in the selection cursor
// row_idx is the index of the row whose function is invoked - can be used to identify whether it is the previous or new selection row.
typedef void (*RowOnSelectionChangedFunc)(SelectionGrid* selection_grid, int row_idx, const Selection* prev_selection, const Selection* new_selection);
typedef int (*RowGetSizeFunc)();
// Called for any non-directional key hit
// The key will not be passed, the function will have to check key_hit() etc. for the key it wants to check
typedef void (*RowOnKeyHitFunc)(SelectionGrid* selection_grid, Selection* selection);
struct SelectionGridRow
{
int row_idx;
RowGetSizeFunc get_size;
RowOnSelectionChangedFunc on_selection_changed;
RowOnKeyHitFunc on_key_hit;
};
struct SelectionGrid
{
SelectionGridRow* rows;
const int num_rows;
Selection selection;
};
void selection_grid_process_input(SelectionGrid* selection_grid);
void selection_grid_move_selection_horz(SelectionGrid* selection_grid, int direction_tribool);
void selection_grid_move_selection_vert(SelectionGrid* selection_grid, int direction_tribool);
#endif
+4 -1
View File
@@ -27,6 +27,8 @@ typedef struct
FIXED rotation; FIXED rotation;
FIXED vrotation; FIXED vrotation;
bool selected; bool selected;
bool focused;
} SpriteObject; } SpriteObject;
// Sprite methods // Sprite methods
@@ -58,6 +60,7 @@ void sprite_object_shake(SpriteObject* sprite_object, mm_word sound_id);
void sprite_object_set_selected(SpriteObject* sprite_object, bool selected); void sprite_object_set_selected(SpriteObject* sprite_object, bool selected);
bool sprite_object_is_selected(SpriteObject* sprite_object); bool sprite_object_is_selected(SpriteObject* sprite_object);
Sprite* sprite_object_get_sprite(SpriteObject* sprite_object); Sprite* sprite_object_get_sprite(SpriteObject* sprite_object);
void sprite_object_set_focus(SpriteObject* sprite_object, bool focus);
bool sprite_object_is_focused(SpriteObject* sprite_object);
#endif // SPRITE_H #endif // SPRITE_H
+259 -201
View File
@@ -15,6 +15,7 @@
#include "graphic_utils.h" #include "graphic_utils.h"
#include "tonc_video.h" #include "tonc_video.h"
#include "audio_utils.h" #include "audio_utils.h"
#include "selection_grid.h"
#include "background_gfx.h" #include "background_gfx.h"
#include "background_shop_gfx.h" #include "background_shop_gfx.h"
@@ -185,7 +186,7 @@ int get_money(void) {
return money; return money;
} }
IntList* jokers_available_to_shop; // List of joker IDs List* jokers_available_to_shop; // List of joker IDs
// Consts // Consts
@@ -251,12 +252,15 @@ static const Rect ROUND_END_NUM_HANDS_RECT = {88, 116, UNDEFINED, UNDEF
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 };
#define ITEM_SHOP_Y 71 // TODO: Needs to be a rect?
//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 SCORED_CARD_TEXT_Y 48 #define SCORED_CARD_TEXT_Y 48
#define HIGHLIGHT_COLOR 0xFFFF
#define PITCH_STEP_DISCARD_SFX (-64) #define PITCH_STEP_DISCARD_SFX (-64)
#define PITCH_STEP_DRAW_SFX 24 #define PITCH_STEP_DRAW_SFX 24
#define PITCH_STEP_UNDISCARD_SFX 2*PITCH_STEP_DRAW_SFX #define PITCH_STEP_UNDISCARD_SFX 2*PITCH_STEP_DRAW_SFX
@@ -1010,11 +1014,11 @@ void game_set_state(enum GameState new_game_state)
game_state = new_game_state; game_state = new_game_state;
} }
void jokers_avialable_to_shop_init() void jokers_available_to_shop_init()
{ {
int num_defined_jokers = get_joker_registry_size(); int num_defined_jokers = get_joker_registry_size();
jokers_available_to_shop = int_list_new(num_defined_jokers); jokers_available_to_shop = list_new(num_defined_jokers);
for (int i = 0; i < num_defined_jokers; i++) for (intptr_t i = 0; i < num_defined_jokers; i++)
{ {
// Add all joker IDs sequentially // Add all joker IDs sequentially
int_list_append(jokers_available_to_shop, i); int_list_append(jokers_available_to_shop, i);
@@ -1023,7 +1027,7 @@ void jokers_avialable_to_shop_init()
void game_init() void game_init()
{ {
jokers_avialable_to_shop_init(); jokers_available_to_shop_init();
hands = max_hands; hands = max_hands;
discards = max_discards; discards = max_discards;
@@ -1797,11 +1801,6 @@ static void held_jokers_update_loop()
jokers[i]->sprite_object->tx = hand_x - int2fx(spacing_lut[jokers_top][i]); jokers[i]->sprite_object->tx = hand_x - int2fx(spacing_lut[jokers_top][i]);
jokers[i]->sprite_object->ty = hand_y; jokers[i]->sprite_object->ty = hand_y;
if (joker_object_is_selected(jokers[i]))
{
jokers[i]->sprite_object->ty -= int2fx(10);
}
joker_object_update(jokers[i]); joker_object_update(jokers[i]);
} }
} }
@@ -2145,44 +2144,60 @@ void game_round_end()
} }
} }
static void game_shop_create_items(JokerObject *shop_jokers[]) // Shop
static List *shop_jokers = NULL;
#define REROLL_BASE_COST 5 // Base cost for rerolling the shop items
static int reroll_cost = REROLL_BASE_COST;
#define NEXT_ROUND_BTN_SEL_X 0
#define NEXT_ROUND_BTN_FRAME_PAL_IDX 5
#define NEXT_ROUND_BTN_PAL_IDX 16
#define REROLL_BTN_FRAME_PAL_IDX 7
#define REROLL_BTN_PAL_IDX 3
static void game_shop_create_items()
{ {
tte_erase_rect_wrapper(SHOP_PRICES_TEXT_RECT); tte_erase_rect_wrapper(SHOP_PRICES_TEXT_RECT);
if (int_list_get_size(jokers_available_to_shop) == 0) if (list_get_size(jokers_available_to_shop) == 0)
{ {
// No jokers to create // No jokers to create
return; return;
} }
shop_jokers = list_new(MAX_SHOP_JOKERS);
for (int i = 0; i < MAX_SHOP_JOKERS; i++) for (int i = 0; i < MAX_SHOP_JOKERS; i++)
{ {
// TODO: weight the random choice by joker rarity // TODO: weight the random choice by joker rarity
int joker_idx = random() % int_list_get_size(jokers_available_to_shop); int joker_idx = random() % list_get_size(jokers_available_to_shop);
int joker_id = int_list_get(jokers_available_to_shop, joker_idx); intptr_t joker_id = int_list_get(jokers_available_to_shop, joker_idx);
int_list_remove_by_idx(jokers_available_to_shop, joker_idx); list_remove_by_idx(jokers_available_to_shop, joker_idx);
shop_jokers[i] = joker_object_new(joker_new(joker_id)); JokerObject *joker_object = joker_object_new(joker_new(joker_id));
shop_jokers[i]->sprite_object->x = int2fx(120 + i * 32);
shop_jokers[i]->sprite_object->y = int2fx(160);
shop_jokers[i]->sprite_object->tx = shop_jokers[i]->sprite_object->x;
shop_jokers[i]->sprite_object->ty = int2fx(71);
int x = fx2int(shop_jokers[i]->sprite_object->tx) + TILE_SIZE - (get_digits_even(shop_jokers[i]->joker->value) - 1) * TILE_SIZE; joker_object->sprite_object->x = int2fx(120 + i * CARD_SPRITE_SIZE);
int y = fx2int(shop_jokers[i]->sprite_object->ty) + CARD_SPRITE_SIZE + TILE_SIZE; joker_object->sprite_object->y = int2fx(160);
tte_printf("#{P:%d,%d; cx:0xC000}$%d", x, y, shop_jokers[i]->joker->value); joker_object->sprite_object->tx = joker_object->sprite_object->x;
joker_object->sprite_object->ty = int2fx(ITEM_SHOP_Y);
sprite_position(joker_object_get_sprite(shop_jokers[i]), fx2int(shop_jokers[i]->sprite_object->x), fx2int(shop_jokers[i]->sprite_object->y)); int x = fx2int(joker_object->sprite_object->tx) + TILE_SIZE - (get_digits_even(joker_object->joker->value) - 1) * TILE_SIZE;
int y = fx2int(joker_object->sprite_object->ty) + CARD_SPRITE_SIZE + TILE_SIZE;
tte_printf("#{P:%d,%d; cx:0xC000}$%d", x, y, joker_object->joker->value);
sprite_position(joker_object_get_sprite(joker_object), fx2int(joker_object->sprite_object->x), fx2int(joker_object->sprite_object->y));
list_append(shop_jokers, joker_object);
} }
} }
// Intro sequence (menu and shop icon coming into frame) // Intro sequence (menu and shop icon coming into frame)
static void game_shop_intro(JokerObject* shop_jokers[]) static void game_shop_intro()
{ {
main_bg_se_copy_rect_1_tile_vert(POP_MENU_ANIM_RECT, SE_UP); main_bg_se_copy_rect_1_tile_vert(POP_MENU_ANIM_RECT, SE_UP);
if (timer == 1) if (timer == 1)
{ {
game_shop_create_items(shop_jokers); game_shop_create_items();
} }
if (timer >= 7) // Shift the shop icon if (timer >= 7) // Shift the shop icon
@@ -2209,27 +2224,31 @@ static void game_shop_intro(JokerObject* shop_jokers[])
} }
} }
static void game_shop_reroll(JokerObject** shop_jokers, int *reroll_cost) static void game_shop_reroll(int *reroll_cost)
{ {
money -= *reroll_cost; money -= *reroll_cost;
display_money(money); // Update the money display display_money(money); // Update the money display
for (int i = 0; i < MAX_SHOP_JOKERS; i++) for (int i = 0; i < list_get_size(shop_jokers); i++)
{ {
if (shop_jokers[i] != NULL) JokerObject *joker_object = list_get(shop_jokers, i);
if (joker_object != NULL)
{ {
int_list_append(jokers_available_to_shop, shop_jokers[i]->joker->id); int_list_append(jokers_available_to_shop, joker_object->joker->id);
joker_object_destroy(&shop_jokers[i]); // Destroy the joker object if it exists joker_object_destroy(&joker_object); // Destroy the joker object if it exists
} }
} }
game_shop_create_items(shop_jokers); list_destroy(&shop_jokers);
game_shop_create_items();
for (int i = 0; i < MAX_SHOP_JOKERS; i++) for (int i = 0; i < list_get_size(shop_jokers); i++)
{ {
if (shop_jokers[i] != NULL) JokerObject *joker_object = list_get(shop_jokers, i);
if (joker_object != NULL)
{ {
shop_jokers[i]->sprite_object->y = shop_jokers[i]->sprite_object->ty; // Set the y position to the target position joker_object->sprite_object->y = joker_object->sprite_object->ty; // Set the y position to the target position
joker_object_shake(shop_jokers[i], UNDEFINED); // Give the joker a little wiggle animation joker_object_shake(joker_object, UNDEFINED); // Give the joker a little wiggle animation
} }
} }
@@ -2237,125 +2256,156 @@ static void game_shop_reroll(JokerObject** shop_jokers, int *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:0xF000}$%d", SHOP_REROLL_RECT.left, SHOP_REROLL_RECT.top, *reroll_cost);
} }
// Shop menu input and selection // Shop input
static void game_shop_process_user_input(JokerObject** shop_jokers) static int shop_top_row_get_size()
{ {
static const int REROLL_BASE_COST = 5; // Base cost for rerolling the shop items return list_get_size(shop_jokers) + 1; // + 1 to account for next round button
static int reroll_cost = REROLL_BASE_COST; }
// temp variables for future implementation static void game_shop_buy_joker(int shop_joker_idx)
const ushort max_items_top = MAX_SHOP_JOKERS; {
const ushort max_items_bottom = 0; JokerObject *joker_object = list_get(shop_jokers, shop_joker_idx);
joker_push(joker_object);
money -= joker_object->joker->value; // Deduct the money spent on the joker
display_money(money); // Update the money display
Rect joker_price_rect;
joker_price_rect.left = fx2int(joker_object->sprite_object->tx);
// 2*TILE_SIZE because the item is highlighted and raised by 1 additional tile
joker_price_rect.top = fx2int(joker_object->sprite_object->ty) + CARD_SPRITE_SIZE + 2 * TILE_SIZE;
joker_price_rect.right = joker_price_rect.left + TILE_SIZE * 3;
joker_price_rect.bottom = joker_price_rect.top + TILE_SIZE;
tte_erase_rect_wrapper(joker_price_rect);
list_remove_by_idx(shop_jokers, shop_joker_idx); // Remove the joker from the shop
}
static void shop_top_row_on_key_hit(SelectionGrid* selection_grid, Selection* selection)
{
if (!key_hit(SELECT_CARD))
return;
if (selection->x == NEXT_ROUND_BTN_SEL_X)
{
// Go to next blind selection game state
state = 2; // Go to the outro sequence state
timer = 0; // Reset the timer
reroll_cost = REROLL_BASE_COST;
memcpy16(&pal_bg_mem[NEXT_ROUND_BTN_FRAME_PAL_IDX], &pal_bg_mem[6], 1);
// memcpy16(&pal_bg_mem[16], &pal_bg_mem[6], 1);
// This changes the color of the button to a dark red.
// However, it shares a palette with the shop icon, so it will change the color of the shop icon as well.
// And I don't care enough to fix it right now.
}
else
{
int shop_joker_idx = selection->x - 1; // - 1 to account for next round button
JokerObject *joker_object = list_get(shop_jokers, shop_joker_idx);
if (joker_object == NULL
|| jokers_top >= MAX_JOKERS_HELD_SIZE - 1
|| money < joker_object->joker->value)
{
return;
}
game_shop_buy_joker(shop_joker_idx);
// In Balatro the selection actually stays on the purchased joker it's easier to just move it left
selection_grid_move_selection_horz(selection_grid, -1);
}
}
static void shop_top_row_on_selection_changed(SelectionGrid* selection_grid, int row_idx,
const Selection* prev_selection,
const Selection* new_selection)
{
// The selection grid system only guarantees that the new selection is within bounds
if (prev_selection->y == row_idx && prev_selection->x >= 0 && prev_selection->x < shop_top_row_get_size())
{
if (prev_selection->x == NEXT_ROUND_BTN_SEL_X)
{
// Remove next round button highlight
memcpy16(&pal_bg_mem[NEXT_ROUND_BTN_FRAME_PAL_IDX], &pal_bg_mem[NEXT_ROUND_BTN_PAL_IDX], 1);
}
else
{
JokerObject *joker = list_get(shop_jokers, prev_selection->x - 1);
sprite_object_set_focus(joker->sprite_object, false);
// -1 to account for next round button
}
}
if (new_selection->y == row_idx)
{
if (new_selection->x == NEXT_ROUND_BTN_SEL_X)
{
// Highlight next round button
memset16(&pal_bg_mem[NEXT_ROUND_BTN_FRAME_PAL_IDX], HIGHLIGHT_COLOR, 1);
}
else
{
JokerObject *joker = list_get(shop_jokers, new_selection->x - 1);
sprite_object_set_focus(joker->sprite_object, true);
// -1 to account for next round button
}
}
}
static int shop_bottom_row_get_size()
{
return 1; // Currently only the reroll button
}
static void shop_bottom_row_on_selection_changed(SelectionGrid* selection_grid, int row_idx, const Selection* prev_selection, const Selection* new_selection)
{
if (row_idx == prev_selection->y)
{
// Remove highlight
memcpy16(&pal_bg_mem[REROLL_BTN_FRAME_PAL_IDX], &pal_bg_mem[REROLL_BTN_PAL_IDX], 1);
}
else if (row_idx == new_selection->y)
{
memset16(&pal_bg_mem[REROLL_BTN_FRAME_PAL_IDX], HIGHLIGHT_COLOR, 1);
}
}
static void shop_bottom_row_on_key_hit(SelectionGrid* selection_grid, Selection* selection)
{
if (money >= reroll_cost)
{
game_shop_reroll(&reroll_cost);
}
}
SelectionGridRow shop_selection_rows[] =
{
{0, shop_top_row_get_size, shop_top_row_on_selection_changed, shop_top_row_on_key_hit},
{1, shop_bottom_row_get_size, shop_bottom_row_on_selection_changed, shop_bottom_row_on_key_hit}
};
static const Selection SHOP_INIT_SEL = {-1, 0};
SelectionGrid shop_selection_grid = {shop_selection_rows, NUM_ELEM_IN_ARR(shop_selection_rows), SHOP_INIT_SEL};
// Shop menu input and selection
static void game_shop_process_user_input()
{
if (timer == 1) if (timer == 1)
{ {
// The selection grid is initialized outside of bounds and moved
// to trigger the selection change so the initial selection is visible
shop_selection_grid.selection = SHOP_INIT_SEL;
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:0xF000}$%d", SHOP_REROLL_RECT.left, SHOP_REROLL_RECT.top, reroll_cost);
} }
// Shop input logic // Shop input logic
if (key_hit(KEY_UP)) selection_grid_process_input(&shop_selection_grid);
{
selection_y = 0;
if (selection_x > max_items_top)
{
selection_x = max_items_top;
}
}
else if (key_hit(KEY_DOWN))
{
selection_y = 1;
if (selection_x > max_items_bottom)
{
selection_x = max_items_bottom;
}
}
else if (key_hit(KEY_LEFT))
{
if (selection_x > 0)
{
selection_x--;
}
}
else if (key_hit(KEY_RIGHT))
{
// TODO: Don't allow cursor on sold items
if (selection_y == 0 && selection_x < max_items_top)
{
selection_x++;
}
else if (selection_y == 1 && selection_x < max_items_bottom)
{
selection_x++;
}
}
memcpy16(&pal_bg_mem[7], &pal_bg_mem[3], 1);
memcpy16(&pal_bg_mem[5], &pal_bg_mem[16], 1);
// Shop selection logic
if (selection_x == 0 && selection_y == 0) // Next round button
{
memset16(&pal_bg_mem[5], 0xFFFF, 1);
if (key_hit(SELECT_CARD))
{
// Go to next blind selection game state
state = 2; // Go to the outro sequence state
timer = 0; // Reset the timer
reroll_cost = REROLL_BASE_COST;
memcpy16(&pal_bg_mem[5], &pal_bg_mem[6], 1);
// memcpy16(&pal_bg_mem[16], &pal_bg_mem[6], 1);
// This changes the color of the button to a dark red.
// However, it shares a palette with the shop icon, so it will change the color of the shop icon as well.
// And I don't care enough to fix it right now.
}
}
else if (selection_x == 0 && selection_y == 1) // Reroll button
{
memset16(&pal_bg_mem[7], 0xFFFF, 1);
if (key_hit(SELECT_CARD) && money >= reroll_cost)
{
game_shop_reroll(shop_jokers, &reroll_cost);
}
}
for (int i = 0; i < MAX_SHOP_JOKERS; i++) // Item selection logic
{
if (shop_jokers[i] != NULL)
{
if (i == selection_x - 1 && selection_y == 0)
{
shop_jokers[i]->sprite_object->ty = int2fx(61);
if (key_hit(SELECT_CARD) && jokers_top < MAX_JOKERS_HELD_SIZE - 1 && money >= shop_jokers[i]->joker->value)
{
joker_push(shop_jokers[i]);
money -= shop_jokers[i]->joker->value; // Deduct the money spent on the joker
display_money(money); // Update the money display
Rect joker_price_rect;
joker_price_rect.left = fx2int(shop_jokers[i]->sprite_object->tx);
// 2*TILE_SIZE because the item is highlighted and raised by 1 additional tile
joker_price_rect.top = fx2int(shop_jokers[i]->sprite_object->ty) + CARD_SPRITE_SIZE + 2*TILE_SIZE;
joker_price_rect.right = joker_price_rect.left + TILE_SIZE * 3;
joker_price_rect.bottom = joker_price_rect.top + TILE_SIZE;
tte_erase_rect_wrapper(joker_price_rect);
shop_jokers[i] = NULL; // Remove the joker from the shop
}
}
else
{
shop_jokers[i]->sprite_object->ty = int2fx(71);
}
}
}
} }
static void game_shop_lights_anim_frame() static void game_shop_lights_anim_frame()
@@ -2383,20 +2433,66 @@ static void game_shop_lights_anim_frame()
memcpy16(&pal_bg_mem[8], &shifted_palette[3], 1); memcpy16(&pal_bg_mem[8], &shifted_palette[3], 1);
} }
// Outro sequence (menu and shop icon going out of frame)
static void game_shop_outro()
{
// Shift the shop panel
main_bg_se_move_rect_1_tile_vert(POP_MENU_ANIM_RECT, SE_DOWN);
main_bg_se_copy_rect_1_tile_vert(TOP_LEFT_PANEL_ANIM_RECT, SE_UP);
if (timer == 1)
{
tte_erase_rect_wrapper(SHOP_PRICES_TEXT_RECT); // Erase the shop prices text
for (int i = 0; i < list_get_size(shop_jokers); i++)
{
JokerObject *joker_object = list_get(shop_jokers, i);
if (joker_object != NULL)
{
joker_object->sprite_object->ty = int2fx(160);
}
}
int y = 6;
memset16(&se_mem[MAIN_BG_SBB][32 * (y - 1)], 0x0006, 1);
memset16(&se_mem[MAIN_BG_SBB][1 + 32 * (y - 1)], 0x0007, 2);
memset16(&se_mem[MAIN_BG_SBB][3 + 32 * (y - 1)], 0x0008, 1);
memset16(&se_mem[MAIN_BG_SBB][4 + 32 * (y - 1)], 0x0009, 4);
memset16(&se_mem[MAIN_BG_SBB][7 + 32 * (y - 1)], 0x000A, 1);
memset16(&se_mem[MAIN_BG_SBB][8 + 32 * (y - 1)], 0x0406, 1);
}
else if (timer == 2)
{
int y = 5;
memset16(&se_mem[MAIN_BG_SBB][32 * (y - 1)], 0x0001, 1);
memset16(&se_mem[MAIN_BG_SBB][1 + 32 * (y - 1)], 0x0002, 7);
memset16(&se_mem[MAIN_BG_SBB][8 + 32 * (y - 1)], 0x0401, 1);
}
if (timer >= MENU_POP_OUT_ANIM_FRAMES)
{
state = 3; // Go to the next state
timer = 0; // Reset the timer
}
}
void game_shop() void game_shop()
{ {
change_background(BG_ID_SHOP); change_background(BG_ID_SHOP);
static JokerObject *shop_jokers[MAX_SHOP_JOKERS] = {NULL}; if (shop_jokers != NULL)
for (int i = 0; i < MAX_SHOP_JOKERS; i++)
{ {
if (shop_jokers[i] != NULL) for (int i = 0; i < list_get_size(shop_jokers); i++)
{ {
joker_object_update(shop_jokers[i]); JokerObject *joker_object = list_get(shop_jokers, i);
if (joker_object != NULL)
{
joker_object_update(joker_object);
}
} }
} }
if (timer % 20 == 0) if (timer % 20 == 0)
{ {
@@ -2407,73 +2503,35 @@ void game_shop()
{ {
case 0: case 0:
{ {
game_shop_intro(shop_jokers); game_shop_intro();
break; break;
} }
case 1: case 1:
{ {
game_shop_process_user_input(shop_jokers); game_shop_process_user_input();
break; break;
} }
case 2: // Outro sequence (menu and shop icon going out of frame) case 2:
{ {
// Shift the shop panel game_shop_outro();
main_bg_se_move_rect_1_tile_vert(POP_MENU_ANIM_RECT, SE_DOWN);
main_bg_se_copy_rect_1_tile_vert(TOP_LEFT_PANEL_ANIM_RECT, SE_UP);
if (timer == 1)
{
tte_erase_rect_wrapper(SHOP_PRICES_TEXT_RECT); // Erase the shop prices text
for (int i = 0; i < MAX_SHOP_JOKERS; i++)
{
if (shop_jokers[i] != NULL)
{
shop_jokers[i]->sprite_object->ty = int2fx(160);
}
}
int y = 6;
memset16(&se_mem[MAIN_BG_SBB][32 * (y - 1)], 0x0006, 1);
memset16(&se_mem[MAIN_BG_SBB][1 + 32 * (y - 1)], 0x0007, 2);
memset16(&se_mem[MAIN_BG_SBB][3 + 32 * (y - 1)], 0x0008, 1);
memset16(&se_mem[MAIN_BG_SBB][4 + 32 * (y - 1)], 0x0009, 4);
memset16(&se_mem[MAIN_BG_SBB][7 + 32 * (y - 1)], 0x000A, 1);
memset16(&se_mem[MAIN_BG_SBB][8 + 32 * (y - 1)], 0x0406, 1);
}
else if (timer == 2)
{
int y = 5;
memset16(&se_mem[MAIN_BG_SBB][32 * (y - 1)], 0x0001, 1);
memset16(&se_mem[MAIN_BG_SBB][1 + 32 * (y - 1)], 0x0002, 7);
memset16(&se_mem[MAIN_BG_SBB][8 + 32 * (y - 1)], 0x0401, 1);
}
if (timer >= MENU_POP_OUT_ANIM_FRAMES)
{
state = 3; // Go to the next state
timer = 0; // Reset the timer
}
break; break;
} }
default: default:
state = 0; // Reset the state state = 0; // Reset the state
selection_x = 0; // Reset the selection for (int i = 0; i < list_get_size(shop_jokers); i++)
selection_y = 0; // Reset the selection
for (int i = 0; i < MAX_SHOP_JOKERS; i++)
{ {
if (shop_jokers[i] != NULL) JokerObject* joker_object = list_get(shop_jokers, i);
if (joker_object != NULL)
{ {
// Make the joker available back to shop // Make the joker available back to shop
int_list_append(jokers_available_to_shop, shop_jokers[i]->joker->id); int_list_append(jokers_available_to_shop, (intptr_t)joker_object->joker->id);
} }
joker_object_destroy(&shop_jokers[i]); // Destroy the joker objects joker_object_destroy(&joker_object); // Destroy the joker objects
} }
list_destroy(&shop_jokers);
increment_blind(BLIND_DEFEATED); // TODO: Move to game_round_end()? increment_blind(BLIND_DEFEATED); // TODO: Move to game_round_end()?
game_set_state(GAME_BLIND_SELECT); // If we reach here, we should go to the blind select state game_set_state(GAME_BLIND_SELECT); // If we reach here, we should go to the blind select state
+28 -12
View File
@@ -3,10 +3,10 @@
#include "list.h" #include "list.h"
#include "util.h" #include "util.h"
IntList *int_list_new(int init_size) { List *list_new(int init_size) {
IntList *list = (IntList *)malloc(sizeof(IntList)); List *list = (List *)malloc(sizeof(List));
if (list == NULL) return NULL; if (list == NULL) return NULL;
list->_array = (int *)malloc(sizeof(int) * init_size); list->_array = (void **)malloc(sizeof(void*) * init_size);
if (!list->_array) if (!list->_array)
{ {
free(list); free(list);
@@ -17,7 +17,7 @@ IntList *int_list_new(int init_size) {
return list; return list;
} }
void int_list_destroy(IntList **list) { void list_destroy(List **list) {
if (list == NULL || *list == NULL) if (list == NULL || *list == NULL)
return; return;
{ {
@@ -28,22 +28,28 @@ void int_list_destroy(IntList **list) {
*list = NULL; *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) if (list->size >= list->allocated_size)
{ {
int new_size = list->allocated_size * 2; 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) if (new_arr == NULL)
return false; return false;
list->_array = new_arr; list->_array = new_arr;
list->allocated_size = new_size; list->allocated_size = new_size;
} }
list->_array[list->size++] = value; list->_array[list->size++] = value;
return true; 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) if (index < 0 || index >= list->size)
return false; return false;
for (int i = index; i < list->size - 1; ++i) for (int i = index; i < list->size - 1; ++i)
@@ -54,27 +60,37 @@ bool int_list_remove_by_idx(IntList *list, int index) {
return true; 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++) for (int i = 0; i < list->size; i++)
{ {
if (list->_array[i] == value) if (list->_array[i] == value)
{ {
return int_list_remove_by_idx(list, i); return list_remove_by_idx(list, i);
} }
} }
return false; 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) if (index < 0 || index >= list->size)
return 0; return NULL;
return list->_array[index]; 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) if (list == NULL)
{ {
+82
View File
@@ -0,0 +1,82 @@
#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 (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);
}
}
+19
View File
@@ -1,6 +1,7 @@
#include "sprite.h" #include "sprite.h"
#include "util.h" #include "util.h"
#include "audio_utils.h" #include "audio_utils.h"
#include "soundbank.h"
#include <tonc.h> #include <tonc.h>
#include <stdlib.h> #include <stdlib.h>
@@ -8,6 +9,7 @@
#define MAX_SPRITES 128 #define MAX_SPRITES 128
#define MAX_AFFINES 32 #define MAX_AFFINES 32
#define SPRITE_FOCUS_RAISE_PX 10
OBJ_ATTR obj_buffer[MAX_SPRITES]; OBJ_ATTR obj_buffer[MAX_SPRITES];
OBJ_AFFINE *obj_aff_buffer = (OBJ_AFFINE*)obj_buffer; OBJ_AFFINE *obj_aff_buffer = (OBJ_AFFINE*)obj_buffer;
@@ -239,3 +241,20 @@ Sprite* sprite_object_get_sprite(SpriteObject* sprite_object)
return NULL; return NULL;
return sprite_object->sprite; 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;
}