#include "common.h" #include "sprites.h" // 1bpp packed u32 gba_btn_a_data[] = { 0xc0300804, 0x844242c2, 0x030c1020, 0x21424243, 0x42440408, 0x30c00000, 0x42222010, 0x0c030000, }; u32 gba_btn_b_data[] = { 0xc0300804, 0xc442c242, 0x030c1020, 0x21424142, 0x42c40408, 0x30c00000, 0x42212010, 0x0c030000, }; u32 gba_btn_updown_data[] = { 0x00f01010, 0x90d01010, 0x001f1011, 0x13171111, 0x101020c0, 0x00000000, 0x11110807, 0x00000000, }; u32 gba_btn_leftright_data[] = { 0x000000f8, 0x0888c8e8, 0x0000000f, 0x1020202f, 0xc88808f8, 0x00000000, 0x2020100f, 0x00000000, }; u32 gba_btn_l_data[] = { 0xfc028181, 0x818101ff, 0xff808080, 0x808380ff, }; u32 gba_btn_r_data[] = { 0xff01c141, 0xc14101ff, 0x3f408081, 0x808180ff, }; u32 gba_btn_ab_shadow_data[] = { 0x00020204, 0x040830c0, 0x00404020, 0x20100c03, }; u32 gba_btn_up_shadow_data[] = { 0x00000010, 0x1020c000, 0x00000010, 0x10080700, }; u32 gba_btn_down_shadow_data[] = { 0x00000000, 0x10f00000, 0x00000000, 0x101f0000, }; u32 gba_btn_leftright_shadow_data[] = { 0x00000000, 0x000008f8, 0x00000000, 0x2020100f, }; u32 gba_btn_lr_shadow_data[] = { 0x00000000, 0x000001ff, 0x00000000, 0x000080ff, }; u32 gba_btn_startselect_data[] = { 0x0000f804, 0xfcf80000, 0x00001f20, 0x3f1f0000, 0x000000f8, 0x04f80000, 0x0000001f, 0x201f0000, }; u32 gba_btn_start_text[] = { 0x00008080, 0x00800000, 0x0000bb91, 0x92930000, 0x00009b2a, 0x1b2a0000, 0x00000301, 0x01010000, }; u32 gba_btn_select_text[] = { 0x0000e060, 0x80e00000, 0x00002e26, 0x22ee0000, 0x0000ee26, 0x22ee0000, 0x00000e04, 0x04040000, }; u32 gba_btn_fx_downup[] = { 0x00000000, 0x00000000, 0x04000000, 0x00000000, 0x40000100, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00020100, 0x00000000, 0x00800001, 0x01000000, 0x00000100, 0x00000000, 0x00000080, 0x00000000, 0x00000100, 0x00000000, 0x00000000, 0x01010000, 0x00000102, 0x00000000, 0x00000000, 0x40000000, 0x00000000, 0x00000000, 0x00000000, 0x00000100, 0x00000000, 0x04000000, }; u32 gba_btn_fx_leftright[] = { 0x00000000, 0x00000000, 0x00000100, 0x00000000, 0x04000000, 0x00000100, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x04020000, 0x00000000, 0x18000000, 0x00000002, 0x04000000, 0x00000000, 0x00000000, 0x00000008, 0x04000000, 0x00000000, 0x30000000, 0x00000000, 0x04080000, 0x00000000, 0x00000000, 0x00001000, 0x00000000, 0x00000000, 0x40000000, 0x00000000, 0x00001000, 0x00000000, }; u32 gba_btn_fx_lr[] = { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00008004, 0x00000002, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000080, 0x80810200, 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x00000080, 0x80000000, 0x000000c0, 0x80010000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000080, 0x80000000, 0x00000020, 0x00000000, 0x00000000, }; u32 gba_btn_fx_ab[] = { 0x00000000, 0x00408000, 0x00000080, 0x80800001, 0x00000000, 0x00000080, 0x00000000, 0x00020100, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x38000000, 0x00000080, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x1c000000, 0x00000001, 0x40200000, 0x00000000, 0x00008080, 0x80000000, 0x00000000, 0x00000000, 0x02040000, 0x00000000, 0x00000000, 0x20400000, 0x00808000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x04020000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0c000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x30000000, 0x00000000, 0x00201000, 0x00000000, 0x00000000, 0x80800000, 0x00000000, 0x00000000, 0x00040800, 0x00000000, 0x00000000, 0x20000000, 0x00800000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x04000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x04000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x20000000, 0x00000000, 0x00001000, 0x00000000, 0x00000000, 0x00800000, 0x00000000, 0x00000000, 0x00000800, 0x00000000, }; u32 gba_btn_fx_startselect[] = { 0x00000000, 0x00000000, 0x00000000, 0x10000002, 0x00000001, 0x10000080, 0x00000000, 0x00000000, 0x00000000, 0x00000080, 0x00000408, 0x00000001, 0x00014120, 0x00000000, 0x00000000, 0x00000003, 0x00000000, 0x000000c0, 0x00020400, 0x00000000, 0x01814000, 0x00000000, 0x00000000, 0x00000006, 0x00000000, 0x00000040, 0x00020000, 0x00000000, 0x01800000, 0x00000000, 0x00000000, 0x00000004, }; typedef enum { BTN_STATE_IDLE = 0, BTN_STATE_PRESSED, BTN_STATE_RELEASED, BTN_STATE_HOLD, } BtnState; typedef struct AnimationEntry { int x_offset; int y_offset; int tile_offset; // NOTE: Maybe this should be a part of an animation? // For example, hidden state, h/v flip. u16 hidden; // u16 obj_attr_1; // u16 obj_attr_2; } AnimationEntry; typedef struct ObjState { size_t id; int x; int y; u32 *data; size_t base_tile; size_t n_tiles; size_t frames; u16 obj_attr_0; u16 obj_attr_1; u16 obj_attr_2; } ObjState; AnimationEntry *btn_animation[] = { &(AnimationEntry[]){ {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 1, 0, 0}, {0, 1, 0, 0}, {0, 2, 0, 0}, {0, 2, 0, 0}, {0, 2, 0, 0}, {0, 2, 0, 0}, }, &(AnimationEntry[]){ {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, }, &(AnimationEntry[]){ {0, 0, 0, 1}, {0, 0, 0, 0}, {0, 0, 4, 0}, {0, 0, 4, 0}, {0, 0, 8, 0}, {0, 0, 8, 0}, {0, 0, 12, 0}, {0, 0, 12, 1}, }, }; AnimationEntry *btn_animation_ab[] = { &(AnimationEntry[]){ {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 1, 0, 0}, {0, 1, 0, 0}, {0, 2, 0, 0}, {0, 2, 0, 0}, {0, 2, 0, 0}, {0, 2, 0, 0}, }, &(AnimationEntry[]){ {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, }, &(AnimationEntry[]){ {0, 0, 0, 1}, {0, 0, 0, 1}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 16, 0}, {0, 0, 16, 0}, {0, 0, 32, 0}, {0, 0, 32, 1}, }, }; AnimationEntry *btn_animation_startselect[] = { &(AnimationEntry[]){ {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 2, 0}, {0, 0, 2, 0}, {0, 0, 2, 0}, {0, 0, 2, 0}, {0, 0, 2, 0}, {0, 0, 2, 0}, }, &(AnimationEntry[]){ {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, }, &(AnimationEntry[]){ {0, 0, 0, 1}, {0, 0, 4, 0}, {0, 0, 4, 0}, {0, 0, 8, 0}, {0, 0, 8, 0}, {0, 0, 12, 0}, {0, 0, 12, 0}, {0, 0, 12, 1}, }, }; typedef struct ButtonSprite { ObjState *sprites; AnimationEntry **animations; size_t frame; size_t n_obj; size_t n_frames; BtnState state; } ButtonSprite; void init_button_sprite(ButtonSprite *btn) { for (size_t i = 0; i < btn->n_obj; ++i) { btn->sprites[i].id = load_packed_sprite_data( btn->sprites[i].data, btn->sprites[i].n_tiles, btn->sprites[i].frames); btn->sprites[i].base_tile = sprites[btn->sprites[i].id].tile_start; } } void button_tick(ButtonSprite *btn) { // Nothing to do here. if (btn->state == BTN_STATE_IDLE) { return; } // Reset animation state. if (btn->state == BTN_STATE_PRESSED && btn->frame != 0) { btn->frame = 0; } // Continue the animation. if (btn->state == BTN_STATE_HOLD || btn->state == BTN_STATE_PRESSED ) { if(btn->frame < btn->n_frames - 1) { btn->frame++; } } // Finish the animation and return to idle. if (btn->state == BTN_STATE_RELEASED) { if (btn->frame > 0 && btn->frame < btn->n_frames - 1) { btn->frame++; } else { btn->frame = 0; btn->state = BTN_STATE_IDLE; } } for (size_t i = 0; i < btn->n_obj; ++i) { AnimationEntry anim_frame = btn->animations[i][btn->frame]; int x = btn->sprites[i].x + anim_frame.x_offset; int y = btn->sprites[i].y + anim_frame.y_offset; int base_tile = btn->sprites[i].base_tile + anim_frame.tile_offset; // Clear the previous x/y coordinate and base tiles. btn->sprites[i].obj_attr_0 &= ~0xFF; btn->sprites[i].obj_attr_1 &= ~0x1FF; btn->sprites[i].obj_attr_2 &= ~0x3FF; // Update x/y/tile and hidden state from the animations. btn->sprites[i].obj_attr_0 |= OBJ_Y_COORD(y); btn->sprites[i].obj_attr_1 |= OBJ_X_COORD(x); btn->sprites[i].obj_attr_2 |= base_tile; if (anim_frame.hidden) { btn->sprites[i].obj_attr_0 |= OBJ_HIDDEN; } else { btn->sprites[i].obj_attr_0 &= ~OBJ_HIDDEN; } // Update OBJ attributes. OBJ_ATTR_0(btn->sprites[i].id) = btn->sprites[i].obj_attr_0; OBJ_ATTR_1(btn->sprites[i].id) = btn->sprites[i].obj_attr_1; OBJ_ATTR_2(btn->sprites[i].id) = btn->sprites[i].obj_attr_2; } } ButtonSprite buttons[] = { // DOWN. { .frame = 0, .n_obj = 3, .n_frames = 8, .state = BTN_STATE_RELEASED, .animations = &btn_animation, .sprites = &(ObjState[]){ { .id = 0, .x = SCREEN_WIDTH / 2 - 64 - 16, .y = SCREEN_HEIGHT / 2 + 29, .data = &gba_btn_updown_data, .n_tiles = 4, .frames = 1, .obj_attr_0 = 0, .obj_attr_1 = OBJ_V_FLIP | OBJ_SIZE_MID, .obj_attr_2 = 0 }, { .id = 0, .x = SCREEN_WIDTH / 2 - 64 - 16, .y = SCREEN_HEIGHT / 2 + 29 + 11, .data = &gba_btn_down_shadow_data, .n_tiles = 2, .frames = 1, .obj_attr_0 = OBJ_SHAPE_WIDE, .obj_attr_1 = OBJ_SIZE_SMALL, .obj_attr_2 = 0 }, { .id = 0, .x = SCREEN_WIDTH / 2 - 64 - 16 - 8, .y = SCREEN_HEIGHT / 2 + 29 + 17, .data = &gba_btn_fx_downup, .n_tiles = 4, .frames = 4, .obj_attr_0 = OBJ_SHAPE_WIDE, .obj_attr_1 = OBJ_SIZE_MID, .obj_attr_2 = 0 }, }, }, // UP. { .frame = 0, .n_obj = 3, .n_frames = 8, .state = BTN_STATE_RELEASED, .animations = &btn_animation, .sprites = &(ObjState[]){ { .id = 0, .x = SCREEN_WIDTH / 2 - 64 - 16, .y = SCREEN_HEIGHT / 2 + 32 - 18, .data = &gba_btn_updown_data, .n_tiles = 4, .frames = 1, .obj_attr_0 = 0, .obj_attr_1 = OBJ_SIZE_MID, .obj_attr_2 = 0 }, { .id = 0, .x = SCREEN_WIDTH / 2 - 64 - 16, .y = SCREEN_HEIGHT / 2 + 32 - 18 + 7, .data = &gba_btn_up_shadow_data, .n_tiles = 2, .frames = 1, .obj_attr_0 = OBJ_SHAPE_WIDE, .obj_attr_1 = OBJ_SIZE_SMALL, .obj_attr_2 = 0 }, { .id = 0, .x = SCREEN_WIDTH / 2 - 64 - 16 - 8, .y = SCREEN_HEIGHT / 2 + 32 - 18 - 7, .data = &gba_btn_fx_downup, .n_tiles = 4, .frames = 4, .obj_attr_0 = OBJ_SHAPE_WIDE, .obj_attr_1 = OBJ_V_FLIP | OBJ_SIZE_MID, .obj_attr_2 = 0 }, }, }, // LEFT { .frame = 0, .n_obj = 3, .n_frames = 8, .state = BTN_STATE_RELEASED, .animations = &btn_animation, .sprites = &(ObjState[]){ { .id = 0, .x = SCREEN_WIDTH / 2 - 64 - 16 - 10, .y = SCREEN_HEIGHT / 2 + 32 - 10, .data = &gba_btn_leftright_data, .n_tiles = 4, .frames = 1, .obj_attr_0 = 0, .obj_attr_1 = OBJ_SIZE_MID, .obj_attr_2 = 0 }, { .id = 0, .x = SCREEN_WIDTH / 2 - 64 - 16 - 10, .y = SCREEN_HEIGHT / 2 + 32 - 10 + 6, .data = &gba_btn_leftright_shadow_data, .n_tiles = 2, .frames = 1, .obj_attr_0 = OBJ_SHAPE_WIDE, .obj_attr_1 = OBJ_SIZE_SMALL, .obj_attr_2 = 0 }, { .id = 0, .x = SCREEN_WIDTH / 2 - 64 - 16 - 10 - 6, .y = SCREEN_HEIGHT / 2 + 32 - 10 - 8, .data = &gba_btn_fx_leftright, .n_tiles = 4, .frames = 4, .obj_attr_0 = OBJ_SHAPE_TALL, .obj_attr_1 = OBJ_H_FLIP | OBJ_SIZE_MID, .obj_attr_2 = 0 }, }, }, // RIGHT. { .frame = 0, .n_obj = 3, .n_frames = 8, .state = BTN_STATE_RELEASED, .animations = &btn_animation, .sprites = &(ObjState[]){ { .id = 0, .x = SCREEN_WIDTH / 2 - 64 - 16 + 11, .y = SCREEN_HEIGHT / 2 + 32 - 10, .data = &gba_btn_leftright_data, .n_tiles = 4, .frames = 1, .obj_attr_0 = 0, .obj_attr_1 = OBJ_H_FLIP | OBJ_SIZE_MID, .obj_attr_2 = 0 }, { .id = 0, .x = SCREEN_WIDTH / 2 - 64 - 16 + 11, .y = SCREEN_HEIGHT / 2 + 32 - 10 + 6, .data = &gba_btn_leftright_shadow_data, .n_tiles = 2, .frames = 1, .obj_attr_0 = OBJ_SHAPE_WIDE, .obj_attr_1 = OBJ_H_FLIP | OBJ_SIZE_SMALL, .obj_attr_2 = 0 }, { .id = 0, .x = SCREEN_WIDTH / 2 - 64 - 16 + 11 + 14, .y = SCREEN_HEIGHT / 2 + 32 - 10 - 8, .data = &gba_btn_fx_leftright, .n_tiles = 4, .frames = 4, .obj_attr_0 = OBJ_SHAPE_TALL, .obj_attr_1 = OBJ_SIZE_MID, .obj_attr_2 = 0 }, }, }, // L. { .frame = 0, .n_obj = 3, .n_frames = 8, .state = BTN_STATE_RELEASED, .animations = &btn_animation, .sprites = &(ObjState[]){ { .id = 0, .x = SCREEN_WIDTH / 2 - 64 - 28, .y = SCREEN_HEIGHT / 2 - 32 - 20, .data = &gba_btn_l_data, .n_tiles = 2, .frames = 1, .obj_attr_0 = OBJ_SHAPE_WIDE, .obj_attr_1 = OBJ_SIZE_SMALL, .obj_attr_2 = 0 }, { .id = 0, .x = SCREEN_WIDTH / 2 - 64 - 28, .y = SCREEN_HEIGHT / 2 - 32 - 20 + 2, .data = &gba_btn_lr_shadow_data, .n_tiles = 2, .frames = 1, .obj_attr_0 = OBJ_SHAPE_WIDE, .obj_attr_1 = OBJ_SIZE_SMALL, .obj_attr_2 = 0 }, { .id = 0, .x = SCREEN_WIDTH / 2 - 64 - 28 - 12, .y = SCREEN_HEIGHT / 2 - 32 - 20 - 12, .data = &gba_btn_fx_lr, .n_tiles = 4, .frames = 4, .obj_attr_0 = OBJ_SHAPE_SQUARE, .obj_attr_1 = OBJ_SIZE_MID, .obj_attr_2 = 0 }, }, }, // R. { .frame = 0, .n_obj = 3, .n_frames = 8, .state = BTN_STATE_RELEASED, .animations = &btn_animation, .sprites = &(ObjState[]){ { .id = 0, .x = SCREEN_WIDTH / 2 + 32 + 20 + 24, .y = SCREEN_HEIGHT / 2 - 32 - 20, .data = &gba_btn_r_data, .n_tiles = 2, .frames = 1, .obj_attr_0 = OBJ_SHAPE_WIDE, .obj_attr_1 = OBJ_SIZE_SMALL, .obj_attr_2 = 0 }, { .id = 0, .x = SCREEN_WIDTH / 2 + 32 + 20 + 24, .y = SCREEN_HEIGHT / 2 - 32 - 20 + 2, .data = &gba_btn_lr_shadow_data, .n_tiles = 2, .frames = 1, .obj_attr_0 = OBJ_SHAPE_WIDE, .obj_attr_1 = OBJ_SIZE_SMALL, .obj_attr_2 = 0 }, { .id = 0, .x = SCREEN_WIDTH / 2 + 32 + 20 + 12 + 24, .y = SCREEN_HEIGHT / 2 - 32 - 20 - 12, .data = &gba_btn_fx_lr, .n_tiles = 4, .frames = 4, .obj_attr_0 = OBJ_SHAPE_SQUARE, .obj_attr_1 = OBJ_H_FLIP | OBJ_SIZE_MID, .obj_attr_2 = 0 }, }, }, // A. { .frame = 0, .n_obj = 3, .n_frames = 8, .state = BTN_STATE_RELEASED, .animations = &btn_animation_ab, .sprites = &(ObjState[]){ { .id = 0, .x = SCREEN_WIDTH / 2 + 32 + 20 + 20, .y = SCREEN_HEIGHT / 2 + 32 - 16, .data = &gba_btn_a_data, .n_tiles = 4, .frames = 1, .obj_attr_0 = OBJ_SHAPE_SQUARE, .obj_attr_1 = OBJ_SIZE_MID, .obj_attr_2 = 0 }, { .id = 0, .x = SCREEN_WIDTH / 2 + 32 + 20 + 20, .y = SCREEN_HEIGHT / 2 + 32 - 16 + 8, .data = &gba_btn_ab_shadow_data, .n_tiles = 2, .frames = 1, .obj_attr_0 = OBJ_SHAPE_WIDE, .obj_attr_1 = OBJ_SIZE_SMALL, .obj_attr_2 = 0 }, { .id = 0, .x = SCREEN_WIDTH / 2 + 32 + 19 - 7 + 20, .y = SCREEN_HEIGHT / 2 + 32 - 23, .data = &gba_btn_fx_ab, .n_tiles = 16, .frames = 3, .obj_attr_0 = OBJ_SHAPE_SQUARE, .obj_attr_1 = OBJ_SIZE_BIG, .obj_attr_2 = 0 }, }, }, // B. { .frame = 0, .n_obj = 3, .n_frames = 8, .state = BTN_STATE_RELEASED, .animations = &btn_animation_ab, .sprites = &(ObjState[]){ { .id = 0, .x = SCREEN_WIDTH / 2 + 32 + 20, .y = SCREEN_HEIGHT / 2 + 32, .data = &gba_btn_b_data, .n_tiles = 4, .frames = 1, .obj_attr_0 = OBJ_SHAPE_SQUARE, .obj_attr_1 = OBJ_SIZE_MID, .obj_attr_2 = 0 }, { .id = 0, .x = SCREEN_WIDTH / 2 + 32 + 20, .y = SCREEN_HEIGHT / 2 + 32 + 8, .data = &gba_btn_ab_shadow_data, .n_tiles = 2, .frames = 1, .obj_attr_0 = OBJ_SHAPE_WIDE, .obj_attr_1 = OBJ_SIZE_SMALL, .obj_attr_2 = 0 }, { .id = 0, .x = SCREEN_WIDTH / 2 + 32 - 8 + 20, .y = SCREEN_HEIGHT / 2 + 33 + 8 - 16, .data = &gba_btn_fx_ab, .n_tiles = 16, .frames = 3, .obj_attr_0 = OBJ_SHAPE_SQUARE, .obj_attr_1 = OBJ_SIZE_BIG, .obj_attr_2 = 0 }, }, }, // START. { .frame = 0, .n_obj = 3, .n_frames = 8, .state = BTN_STATE_RELEASED, .animations = &btn_animation_startselect, .sprites = &(ObjState[]){ { .id = 0, .x = SCREEN_WIDTH / 2 - 10 + 26 - 14, .y = SCREEN_HEIGHT / 2 + 40 + 10, .data = &gba_btn_startselect_data, .n_tiles = 2, .frames = 2, .obj_attr_0 = OBJ_SHAPE_WIDE, .obj_attr_1 = OBJ_SIZE_SMALL, .obj_attr_2 = 0 }, { .id = 0, .x = SCREEN_WIDTH / 2 - 18 + 26 - 14, .y = SCREEN_HEIGHT / 2 + 46 + 10, .data = &gba_btn_start_text, .n_tiles = 4, .frames = 1, .obj_attr_0 = OBJ_SHAPE_WIDE, .obj_attr_1 = OBJ_SIZE_MID, .obj_attr_2 = 0 }, { .id = 0, .x = SCREEN_WIDTH / 2 - 19 + 26 - 14, .y = SCREEN_HEIGHT / 2 + 37 + 10, .data = &gba_btn_fx_startselect, .n_tiles = 4, .frames = 4, .obj_attr_0 = OBJ_SHAPE_WIDE, .obj_attr_1 = OBJ_SIZE_MID, .obj_attr_2 = 0 }, }, }, // SELECT. { .frame = 0, .n_obj = 3, .n_frames = 8, .state = BTN_STATE_RELEASED, .animations = &btn_animation_startselect, .sprites = &(ObjState[]){ { .id = 0, .x = SCREEN_WIDTH / 2 - 10 - 14, .y = SCREEN_HEIGHT / 2 + 40 + 10, .data = &gba_btn_startselect_data, .n_tiles = 2, .frames = 2, .obj_attr_0 = OBJ_SHAPE_WIDE, .obj_attr_1 = OBJ_SIZE_SMALL, .obj_attr_2 = 0 }, { .id = 0, .x = SCREEN_WIDTH / 2 - 18 - 14, .y = SCREEN_HEIGHT / 2 + 46 + 10, .data = &gba_btn_select_text, .n_tiles = 4, .frames = 1, .obj_attr_0 = OBJ_SHAPE_WIDE, .obj_attr_1 = OBJ_SIZE_MID, .obj_attr_2 = 0 }, { .id = 0, .x = SCREEN_WIDTH / 2 - 19 - 14, .y = SCREEN_HEIGHT / 2 + 37 + 10, .data = &gba_btn_fx_startselect, .n_tiles = 4, .frames = 4, .obj_attr_0 = OBJ_SHAPE_WIDE, .obj_attr_1 = OBJ_SIZE_MID, .obj_attr_2 = 0 }, }, }, }; static inline void init_button_sprites(void) { for (size_t i = 0; i < sizeof(buttons) / sizeof(ButtonSprite); ++i) { init_button_sprite(&buttons[i]); } } static inline void update_button_sprites(void) { if (key_pressed(KEY_DOWN)) { buttons[0].state = BTN_STATE_PRESSED; } else if (key_hold(KEY_DOWN)) { buttons[0].state = BTN_STATE_HOLD; } else { buttons[0].state = BTN_STATE_RELEASED; } if (key_pressed(KEY_UP)) { buttons[1].state = BTN_STATE_PRESSED; } else if (key_hold(KEY_UP)) { buttons[1].state = BTN_STATE_HOLD; } else { buttons[1].state = BTN_STATE_RELEASED; } if (key_pressed(KEY_LEFT)) { buttons[2].state = BTN_STATE_PRESSED; } else if (key_hold(KEY_LEFT)) { buttons[2].state = BTN_STATE_HOLD; } else { buttons[2].state = BTN_STATE_RELEASED; } if (key_pressed(KEY_RIGHT)) { buttons[3].state = BTN_STATE_PRESSED; } else if (key_hold(KEY_RIGHT)) { buttons[3].state = BTN_STATE_HOLD; } else { buttons[3].state = BTN_STATE_RELEASED; } if (key_pressed(KEY_L)) { buttons[4].state = BTN_STATE_PRESSED; } else if (key_hold(KEY_L)) { buttons[4].state = BTN_STATE_HOLD; } else { buttons[4].state = BTN_STATE_RELEASED; } if (key_pressed(KEY_R)) { buttons[5].state = BTN_STATE_PRESSED; } else if (key_hold(KEY_R)) { buttons[5].state = BTN_STATE_HOLD; } else { buttons[5].state = BTN_STATE_RELEASED; } if (key_pressed(KEY_A)) { buttons[6].state = BTN_STATE_PRESSED; } else if (key_hold(KEY_A)) { buttons[6].state = BTN_STATE_HOLD; } else { buttons[6].state = BTN_STATE_RELEASED; } if (key_pressed(KEY_B)) { buttons[7].state = BTN_STATE_PRESSED; } else if (key_hold(KEY_B)) { buttons[7].state = BTN_STATE_HOLD; } else { buttons[7].state = BTN_STATE_RELEASED; } if (key_pressed(KEY_START)) { buttons[8].state = BTN_STATE_PRESSED; } else if (key_hold(KEY_START)) { buttons[8].state = BTN_STATE_HOLD; } else { buttons[8].state = BTN_STATE_RELEASED; } if (key_pressed(KEY_SELECT)) { buttons[9].state = BTN_STATE_PRESSED; } else if (key_hold(KEY_SELECT)) { buttons[9].state = BTN_STATE_HOLD; } else { buttons[9].state = BTN_STATE_RELEASED; } for (size_t i = 0; i < sizeof(buttons) / sizeof(ButtonSprite); ++i) { button_tick(&buttons[i]); } }