zephr/game: various fixes and improvements

- zephr: handle newlines in text
- fix(zephr): don't overflow color channels bytes when multiplying
  colors
- fix(zephr): don't fallback to UI_CONSTRAINT_FIXED when setting the x
  or y constraint to UI_CONSTRAINT_RELATIVE_PIXELS
- game: added how to play dialog
- game: ability to start a new game
This commit is contained in:
2023-12-29 15:12:43 +02:00
parent 31f22a4ba7
commit e543b5f882
8 changed files with 322 additions and 223 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 630 B

+288 -217
View File
@@ -7,10 +7,7 @@
#include "timer.h"
#include "zephr.h"
#define ANIM_SPEED 2.0f
// TODO: configurable color palette
// TODO: particles when merging tiles
#define ANIM_SPEED 2.5f
Game game = {0};
@@ -87,7 +84,7 @@ Color get_color_from_tile(Tile tile) {
case 2048:
return ColorRGBA(237, 194, 46, 255);
default:
return ColorRGBA(205, 193, 180, 255);
return ColorRGBA(237, 194, 46, 255);
}
}
@@ -109,177 +106,10 @@ u8 get_tile_font_size(Tile tile) {
case 2048:
return 45;
default:
return 35;
return 40;
}
}
///////////////////////////////////
//
//
// Drawing
//
//
///////////////////////////////////
void draw_bg(void) {
Size window_size = zephr_get_window_size();
UIConstraints board_con = {0};
set_width_constraint(&board_con, window_size.width, UI_CONSTRAINT_FIXED);
set_height_constraint(&board_con, window_size.height, UI_CONSTRAINT_FIXED);
// bg
draw_quad(&board_con, ColorRGBA(248, 250, 230, 255), 0, ALIGN_TOP_LEFT);
// 2048 text
UIConstraints text_con = {0};
set_y_constraint(&text_con, 0.05f, UI_CONSTRAINT_RELATIVE);
set_width_constraint(&text_con, 1.5, UI_CONSTRAINT_RELATIVE_PIXELS);
draw_text("2048", 100, text_con, ColorRGBA(34, 234, 82, 155), ALIGN_TOP_CENTER);
// score
char score[16];
sprintf(score, "%d", game.score);
set_parent_constraint(&text_con, NULL);
set_x_constraint(&text_con, 0.05f, UI_CONSTRAINT_RELATIVE);
set_y_constraint(&text_con, 0.8f, UI_CONSTRAINT_RELATIVE);
draw_text("Score", 40, text_con, COLOR_BLACK, ALIGN_TOP_LEFT);
set_x_constraint(&text_con, 0.15f, UI_CONSTRAINT_RELATIVE);
draw_text(score, 40, text_con, COLOR_BLACK, ALIGN_TOP_LEFT);
}
void draw_quit_dialog(void) {
UIConstraints dialog_con = {0};
set_x_constraint(&dialog_con, 0, UI_CONSTRAINT_FIXED);
set_y_constraint(&dialog_con, 0, UI_CONSTRAINT_FIXED);
set_width_constraint(&dialog_con, 1.f, UI_CONSTRAINT_RELATIVE);
set_height_constraint(&dialog_con, 1.f, UI_CONSTRAINT_RELATIVE);
draw_quad(&dialog_con, ColorRGBA(0, 0, 0, 200), 0, ALIGN_CENTER);
set_width_constraint(&dialog_con, 0.4f, UI_CONSTRAINT_RELATIVE);
set_height_constraint(&dialog_con, 0.2f, UI_CONSTRAINT_RELATIVE);
draw_quad(&dialog_con, ColorRGBA(135, 124, 124, 255), 8, ALIGN_CENTER);
UIConstraints text_con = {0};
set_parent_constraint(&text_con, &dialog_con);
set_x_constraint(&text_con, 0, UI_CONSTRAINT_FIXED);
set_y_constraint(&text_con, -0.04f, UI_CONSTRAINT_RELATIVE);
set_width_constraint(&text_con, 1, UI_CONSTRAINT_RELATIVE_PIXELS);
draw_text("Are you sure you want to quit?", 36.f, text_con, COLOR_WHITE, ALIGN_CENTER);
UIConstraints btn_con = {0};
set_parent_constraint(&btn_con, &dialog_con);
set_width_constraint(&btn_con, 0.1f, UI_CONSTRAINT_RELATIVE);
set_height_constraint(&btn_con, 0.05f, UI_CONSTRAINT_RELATIVE);
set_y_constraint(&btn_con, -0.02f, UI_CONSTRAINT_RELATIVE);
set_x_constraint(&btn_con, -0.07f, UI_CONSTRAINT_RELATIVE);
if (draw_button(&btn_con, ColorRGBA(201, 146, 126, 255), "Yes", btn_con.height * 0.20f, ALIGN_BOTTOM_CENTER, BUTTON_STATE_ACTIVE)) {
game.quit_dialog = false;
zephr_quit();
}
set_y_constraint(&btn_con, -0.02f, UI_CONSTRAINT_RELATIVE);
set_x_constraint(&btn_con, 0.07f, UI_CONSTRAINT_RELATIVE);
if (draw_button(&btn_con, ColorRGBA(201, 146, 126, 255), "No", btn_con.height * 0.20f, ALIGN_BOTTOM_CENTER, BUTTON_STATE_ACTIVE)) {
game.quit_dialog = false;
}
}
void draw_ui(void) {
ButtonState bg_btns_state = game.quit_dialog ? BUTTON_STATE_INACTIVE : BUTTON_STATE_ACTIVE;
UIConstraints btn_con = {0};
set_x_constraint(&btn_con, -8, UI_CONSTRAINT_RELATIVE_PIXELS);
set_y_constraint(&btn_con, 8, UI_CONSTRAINT_RELATIVE_PIXELS);
set_width_constraint(&btn_con, 80, UI_CONSTRAINT_RELATIVE_PIXELS);
set_height_constraint(&btn_con, 1, UI_CONSTRAINT_ASPECT_RATIO);
if (draw_icon_button(&btn_con, ColorRGBA(201, 146, 126, 255), game.icon_textures[HELP_ICON], btn_con.height * 0.15f, ALIGN_TOP_RIGHT, bg_btns_state)) {
printf("help\n");
}
set_x_constraint(&btn_con, -8 - btn_con.width - 10, UI_CONSTRAINT_RELATIVE_PIXELS);
if (draw_icon_button(&btn_con, ColorRGBA(201, 146, 126, 255), game.icon_textures[SETTINGS_ICON], btn_con.height * 0.15f, ALIGN_TOP_RIGHT, bg_btns_state)) {
printf("settings\n");
}
set_x_constraint(&btn_con, 0, UI_CONSTRAINT_RELATIVE_PIXELS);
set_y_constraint(&btn_con, -0.05f, UI_CONSTRAINT_RELATIVE);
set_width_constraint(&btn_con, 200, UI_CONSTRAINT_RELATIVE_PIXELS);
set_height_constraint(&btn_con, 65, UI_CONSTRAINT_RELATIVE_PIXELS);
if (draw_button(&btn_con, ColorRGBA(201, 172, 126, 255), "New Game", btn_con.height * 0.25f, ALIGN_BOTTOM_CENTER, bg_btns_state)) {
printf("new game\n");
}
if (game.quit_dialog) {
draw_quit_dialog();
}
}
void draw_board(void) {
// board
UIConstraints board_con = {0};
set_y_constraint(&board_con, 0.05f, UI_CONSTRAINT_RELATIVE);
set_height_constraint(&board_con, 0.6f, UI_CONSTRAINT_RELATIVE);
set_width_constraint(&board_con, 1, UI_CONSTRAINT_ASPECT_RATIO);
const float board_border_radius = board_con.width * 0.02f;
draw_quad(&board_con, ColorRGBA(140, 154, 147, 255), board_border_radius, ALIGN_CENTER);
// empty tiles
const float tile_padding = board_con.width * 0.02f;
const float tile_height = board_con.height * 0.225f;
const float tile_board_radius = tile_height * 0.15f;
UIConstraints empty_tile_con = {0};
set_parent_constraint(&empty_tile_con, &board_con);
set_height_constraint(&empty_tile_con, tile_height, UI_CONSTRAINT_FIXED);
set_width_constraint(&empty_tile_con, 1, UI_CONSTRAINT_ASPECT_RATIO);
for (int i = 0; i < 4; i++) {
set_y_constraint(&empty_tile_con, (tile_height + tile_padding) * i + tile_padding, UI_CONSTRAINT_FIXED);
for (int j = 0; j < 4; j++) {
set_x_constraint(&empty_tile_con, (tile_height + tile_padding) * j + tile_padding, UI_CONSTRAINT_FIXED);
draw_quad(&empty_tile_con, ColorRGBA(120, 133, 126, 255), tile_board_radius, ALIGN_TOP_LEFT);
}
}
// filled tiles
UIConstraints text_con = {0};
set_width_constraint(&text_con, 1.5, UI_CONSTRAINT_RELATIVE_PIXELS);
UIConstraints tile_con = {0};
set_parent_constraint(&tile_con, &board_con);
set_height_constraint(&tile_con, tile_height, UI_CONSTRAINT_FIXED);
set_width_constraint(&tile_con, 1, UI_CONSTRAINT_ASPECT_RATIO);
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
Tile *tile = &game.board[i][j];
Color tile_color = get_color_from_tile(*tile);
char value[16];
sprintf(value, "%d", tile->value);
set_y_constraint(&tile_con, (f32)((tile_height + tile_padding) * i + tile_padding + game.board[i][j].anim_y_offset_relative * board_con.height), UI_CONSTRAINT_FIXED);
set_x_constraint(&tile_con, (f32)((tile_height + tile_padding) * j + tile_padding + game.board[i][j].anim_x_offset_relative * board_con.height), UI_CONSTRAINT_FIXED);
if (tile->value > 0) {
draw_quad(&tile_con, tile_color, tile_board_radius, ALIGN_TOP_LEFT);
// value text
set_parent_constraint(&text_con, &tile_con);
set_x_constraint(&text_con, 0, UI_CONSTRAINT_FIXED);
set_y_constraint(&text_con, 0, UI_CONSTRAINT_FIXED);
draw_text(value, get_tile_font_size(*tile), text_con, mult_color(tile_color, 0.5), ALIGN_CENTER);
}
}
}
}
///////////////////////////////////
//
//
@@ -289,7 +119,6 @@ void draw_board(void) {
///////////////////////////////////
void move_right(void) {
if (game.animating) return;
game.last_move_dir = MOVE_DIR_RIGHT;
for (int i = 0; i < 4; i++) {
@@ -340,7 +169,6 @@ void move_right(void) {
}
void move_left(void) {
if (game.animating) return;
game.last_move_dir = MOVE_DIR_LEFT;
for (int i = 0; i < 4; i++) {
@@ -392,7 +220,6 @@ void move_left(void) {
}
void move_down(void) {
if (game.animating) return;
game.last_move_dir = MOVE_DIR_DOWN;
for (int i = 2; i >= 0; i--) {
@@ -440,7 +267,6 @@ void move_down(void) {
}
void move_up(void) {
if (game.animating) return;
game.last_move_dir = MOVE_DIR_UP;
for (int i = 1; i < 4; i++) {
@@ -519,6 +345,280 @@ void spawn_random_tile(void) {
spawn_new_tile(x, y);
}
// TODO: use this
bool gameover(void) {
for (int y = 0; y < 4; y++) {
for (int x = 0; x < 4; x++) {
if (game.board[y][x].value == 0) {
return false;
}
for (int i = 0; i < 4; i++) {
u8 new_x = CORE_CLAMP(x + neighbors[i][0], 0, 3);
u8 new_y = CORE_CLAMP(y + neighbors[i][1], 0, 3);
if ((new_y != y || new_x != x) && game.board[y][x].value == game.board[new_y][new_x].value) {
return false;
}
}
}
}
return true;
}
void game_new(void) {
game.score = 0;
game.animating = false;
game.spawning_new_tile = false;
game.quit_dialog = false;
game.help_dialog = false;
for (int y = 0; y < 4; y++) {
for (int x = 0; x < 4; x++) {
game.board[y][x].value = 0;
game.board[y][x].new_value = 0;
game.board[y][x].merged = false;
game.board[y][x].tiles_to_move = 0;
game.board[y][x].anim_x_offset_relative = 0;
game.board[y][x].anim_y_offset_relative = 0;
}
}
for (int i = 0; i < 2; i++) {
spawn_random_tile();
}
}
void game_attempt_quit(void) {
game.help_dialog = false;
game.quit_dialog = true;
}
void game_init(void) {
srand(time(NULL));
game.icon_textures[HELP_ICON] = load_texture("assets/icons/help.png");
game.icon_textures[SETTINGS_ICON] = load_texture("assets/icons/settings.png");
game.icon_textures[CLOSE_ICON] = load_texture("assets/icons/close.png");
for (int i = 0; i < 2; i++) {
spawn_random_tile();
}
}
///////////////////////////////////
//
//
// Drawing
//
//
///////////////////////////////////
void draw_bg(void) {
Size window_size = zephr_get_window_size();
UIConstraints board_con = {0};
set_width_constraint(&board_con, window_size.width, UI_CONSTRAINT_FIXED);
set_height_constraint(&board_con, window_size.height, UI_CONSTRAINT_FIXED);
// bg
draw_quad(&board_con, ColorRGBA(248, 250, 230, 255), 0, ALIGN_TOP_LEFT);
// 2048 text
UIConstraints text_con = {0};
set_y_constraint(&text_con, 0.05f, UI_CONSTRAINT_RELATIVE);
set_width_constraint(&text_con, 1.5, UI_CONSTRAINT_RELATIVE_PIXELS);
draw_text("2048", 100, text_con, ColorRGBA(34, 234, 82, 155), ALIGN_TOP_CENTER);
// score
char score[16];
sprintf(score, "%d", game.score);
set_parent_constraint(&text_con, NULL);
set_x_constraint(&text_con, 0.05f, UI_CONSTRAINT_RELATIVE);
set_y_constraint(&text_con, 0.8f, UI_CONSTRAINT_RELATIVE);
draw_text("Score", 40, text_con, COLOR_BLACK, ALIGN_TOP_LEFT);
set_x_constraint(&text_con, 0.15f, UI_CONSTRAINT_RELATIVE);
draw_text(score, 40, text_con, COLOR_BLACK, ALIGN_TOP_LEFT);
}
void draw_quit_dialog(void) {
UIConstraints dialog_con = {0};
set_x_constraint(&dialog_con, 0, UI_CONSTRAINT_FIXED);
set_y_constraint(&dialog_con, 0, UI_CONSTRAINT_FIXED);
set_width_constraint(&dialog_con, 1.f, UI_CONSTRAINT_RELATIVE);
set_height_constraint(&dialog_con, 1.f, UI_CONSTRAINT_RELATIVE);
draw_quad(&dialog_con, ColorRGBA(0, 0, 0, 200), 0, ALIGN_CENTER);
set_width_constraint(&dialog_con, 0.4f, UI_CONSTRAINT_RELATIVE);
set_height_constraint(&dialog_con, 0.2f, UI_CONSTRAINT_RELATIVE);
draw_quad(&dialog_con, ColorRGBA(135, 124, 124, 255), 8, ALIGN_CENTER);
UIConstraints text_con = {0};
set_parent_constraint(&text_con, &dialog_con);
set_x_constraint(&text_con, 0, UI_CONSTRAINT_FIXED);
set_y_constraint(&text_con, -0.04f, UI_CONSTRAINT_RELATIVE);
set_width_constraint(&text_con, 1, UI_CONSTRAINT_RELATIVE_PIXELS);
draw_text("Are you sure you want to quit?", 36.f, text_con, COLOR_WHITE, ALIGN_CENTER);
UIConstraints btn_con = {0};
set_parent_constraint(&btn_con, &dialog_con);
set_width_constraint(&btn_con, 0.12f, UI_CONSTRAINT_RELATIVE);
set_height_constraint(&btn_con, 0.05f, UI_CONSTRAINT_RELATIVE);
set_y_constraint(&btn_con, -0.02f, UI_CONSTRAINT_RELATIVE);
set_x_constraint(&btn_con, -0.07f, UI_CONSTRAINT_RELATIVE);
if (draw_button(&btn_con, mult_color(COLOR_WHITE, 0.9f), "Yes", btn_con.height * 0.20f, ALIGN_BOTTOM_CENTER, BUTTON_STATE_ACTIVE)) {
zephr_quit();
}
set_y_constraint(&btn_con, -0.02f, UI_CONSTRAINT_RELATIVE);
set_x_constraint(&btn_con, 0.07f, UI_CONSTRAINT_RELATIVE);
if (draw_button(&btn_con, mult_color(COLOR_WHITE, 0.9f), "No", btn_con.height * 0.20f, ALIGN_BOTTOM_CENTER, BUTTON_STATE_ACTIVE)) {
game.quit_dialog = false;
}
}
void draw_help_dialog(void) {
UIConstraints dialog_con = {0};
set_width_constraint(&dialog_con, 1.f, UI_CONSTRAINT_RELATIVE);
set_height_constraint(&dialog_con, 1.f, UI_CONSTRAINT_RELATIVE);
set_x_constraint(&dialog_con, 0, UI_CONSTRAINT_FIXED);
set_y_constraint(&dialog_con, 0, UI_CONSTRAINT_FIXED);
draw_quad(&dialog_con, ColorRGBA(0, 0, 0, 200), 0, ALIGN_CENTER);
UIConstraints content_card_con = {0};
set_parent_constraint(&content_card_con, &dialog_con);
set_width_constraint(&content_card_con, 0.4f, UI_CONSTRAINT_RELATIVE);
set_height_constraint(&content_card_con, 0.3f, UI_CONSTRAINT_RELATIVE);
draw_quad(&content_card_con, ColorRGBA(135, 124, 124, 255), 8, ALIGN_CENTER);
UIConstraints text_con = {0};
set_parent_constraint(&text_con, &content_card_con);
set_width_constraint(&text_con, 1, UI_CONSTRAINT_RELATIVE_PIXELS);
set_x_constraint(&text_con, 0, UI_CONSTRAINT_FIXED);
set_y_constraint(&text_con, 40, UI_CONSTRAINT_RELATIVE_PIXELS);
draw_text("How to play", 64.f, text_con, COLOR_YELLOW, ALIGN_TOP_CENTER);
set_y_constraint(&text_con, 30, UI_CONSTRAINT_RELATIVE_PIXELS);
set_height_constraint(&text_con, 0.9f, UI_CONSTRAINT_RELATIVE);
draw_text("Use the arrow keys to move the tiles.\n"
"When two tiles with the same number touch, they\n"
"merge into one!\n\nYour goal is to reach 2048 without filling all the tiles", 30.f, text_con, COLOR_WHITE, ALIGN_CENTER);
UIConstraints btn_con = {0};
set_parent_constraint(&btn_con, &content_card_con);
set_width_constraint(&btn_con, 56, UI_CONSTRAINT_RELATIVE_PIXELS);
set_height_constraint(&btn_con, 1, UI_CONSTRAINT_ASPECT_RATIO);
set_y_constraint(&btn_con, -24, UI_CONSTRAINT_RELATIVE_PIXELS);
set_x_constraint(&btn_con, 24, UI_CONSTRAINT_RELATIVE_PIXELS);
if (draw_icon_button(&btn_con, mult_color(COLOR_WHITE, 0.8f), game.icon_textures[CLOSE_ICON], btn_con.height * 0.20f, ALIGN_TOP_RIGHT, BUTTON_STATE_ACTIVE)) {
game.help_dialog = false;
}
}
void draw_ui(void) {
ButtonState bg_btns_state = game.quit_dialog || game.help_dialog
? BUTTON_STATE_INACTIVE
: BUTTON_STATE_ACTIVE;
const int icon_btn_width = 80;
const int icon_btn_offset = 8;
const int icon_btns_padding = 10;
UIConstraints btn_con = {0};
set_x_constraint(&btn_con, -icon_btn_offset, UI_CONSTRAINT_RELATIVE_PIXELS);
set_y_constraint(&btn_con, icon_btn_offset, UI_CONSTRAINT_RELATIVE_PIXELS);
set_width_constraint(&btn_con, icon_btn_width, UI_CONSTRAINT_RELATIVE_PIXELS);
set_height_constraint(&btn_con, 1, UI_CONSTRAINT_ASPECT_RATIO);
if (draw_icon_button(&btn_con, ColorRGBA(201, 146, 126, 255), game.icon_textures[HELP_ICON], btn_con.height * 0.15f, ALIGN_TOP_RIGHT, bg_btns_state)) {
game.help_dialog = true;
}
set_x_constraint(&btn_con, -icon_btn_offset - icon_btn_width - icon_btns_padding, UI_CONSTRAINT_RELATIVE_PIXELS);
if (draw_icon_button(&btn_con, ColorRGBA(201, 146, 126, 255), game.icon_textures[SETTINGS_ICON], btn_con.height * 0.15f, ALIGN_TOP_RIGHT, bg_btns_state)) {
printf("TODO: settings\n");
}
set_x_constraint(&btn_con, 0, UI_CONSTRAINT_RELATIVE_PIXELS);
set_y_constraint(&btn_con, -0.05f, UI_CONSTRAINT_RELATIVE);
set_width_constraint(&btn_con, 200, UI_CONSTRAINT_RELATIVE_PIXELS);
set_height_constraint(&btn_con, 65, UI_CONSTRAINT_RELATIVE_PIXELS);
if (draw_button(&btn_con, ColorRGBA(201, 172, 126, 255), "New Game", btn_con.height * 0.25f, ALIGN_BOTTOM_CENTER, bg_btns_state)) {
game_new();
}
if (game.help_dialog) {
draw_help_dialog();
}
if (game.quit_dialog) {
draw_quit_dialog();
}
}
void draw_board(void) {
// board
UIConstraints board_con = {0};
set_y_constraint(&board_con, 0.05f, UI_CONSTRAINT_RELATIVE);
set_height_constraint(&board_con, 0.6f, UI_CONSTRAINT_RELATIVE);
set_width_constraint(&board_con, 1, UI_CONSTRAINT_ASPECT_RATIO);
const float board_border_radius = board_con.width * 0.02f;
draw_quad(&board_con, ColorRGBA(140, 154, 147, 255), board_border_radius, ALIGN_CENTER);
// empty tiles
const float tile_padding = board_con.width * 0.02f;
const float tile_height = board_con.height * 0.225f;
const float tile_board_radius = tile_height * 0.15f;
UIConstraints empty_tile_con = {0};
set_parent_constraint(&empty_tile_con, &board_con);
set_height_constraint(&empty_tile_con, tile_height, UI_CONSTRAINT_FIXED);
set_width_constraint(&empty_tile_con, 1, UI_CONSTRAINT_ASPECT_RATIO);
for (int i = 0; i < 4; i++) {
set_y_constraint(&empty_tile_con, (tile_height + tile_padding) * i + tile_padding, UI_CONSTRAINT_FIXED);
for (int j = 0; j < 4; j++) {
set_x_constraint(&empty_tile_con, (tile_height + tile_padding) * j + tile_padding, UI_CONSTRAINT_FIXED);
draw_quad(&empty_tile_con, ColorRGBA(120, 133, 126, 255), tile_board_radius, ALIGN_TOP_LEFT);
}
}
// filled tiles
UIConstraints text_con = {0};
set_width_constraint(&text_con, 1.5, UI_CONSTRAINT_RELATIVE_PIXELS);
UIConstraints tile_con = {0};
set_parent_constraint(&tile_con, &board_con);
set_height_constraint(&tile_con, tile_height, UI_CONSTRAINT_FIXED);
set_width_constraint(&tile_con, 1, UI_CONSTRAINT_ASPECT_RATIO);
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
Tile *tile = &game.board[i][j];
Color tile_color = get_color_from_tile(*tile);
char value[16];
sprintf(value, "%d", tile->value);
set_y_constraint(&tile_con, (f32)((tile_height + tile_padding) * i + tile_padding + game.board[i][j].anim_y_offset_relative * board_con.height), UI_CONSTRAINT_FIXED);
set_x_constraint(&tile_con, (f32)((tile_height + tile_padding) * j + tile_padding + game.board[i][j].anim_x_offset_relative * board_con.height), UI_CONSTRAINT_FIXED);
if (tile->value > 0) {
draw_quad(&tile_con, tile_color, tile_board_radius, ALIGN_TOP_LEFT);
// value text
set_parent_constraint(&text_con, &tile_con);
set_x_constraint(&text_con, 0, UI_CONSTRAINT_FIXED);
set_y_constraint(&text_con, 0, UI_CONSTRAINT_FIXED);
draw_text(value, get_tile_font_size(*tile), text_con, mult_color(tile_color, 0.5), ALIGN_CENTER);
}
}
}
}
void update_positions(f64 delta_t) {
if (game.animating) {
switch (game.last_move_dir) {
@@ -651,37 +751,6 @@ void update_positions(f64 delta_t) {
}
}
bool gameover(void) {
for (int y = 0; y < 4; y++) {
for (int x = 0; x < 4; x++) {
if (game.board[y][x].value == 0) {
return false;
}
for (int i = 0; i < 4; i++) {
u8 new_x = CORE_CLAMP(x + neighbors[i][0], 0, 3);
u8 new_y = CORE_CLAMP(y + neighbors[i][1], 0, 3);
if ((new_y != y || new_x != x) && game.board[y][x].value == game.board[new_y][new_x].value) {
return false;
}
}
}
}
return true;
}
void game_init(void) {
srand(time(NULL));
game.icon_textures[HELP_ICON] = load_texture("assets/icons/help.png");
game.icon_textures[SETTINGS_ICON] = load_texture("assets/icons/settings.png");
for (int i = 0; i < 2; i++) {
spawn_random_tile();
}
}
///////////////////////////////////
//
@@ -693,25 +762,26 @@ void game_init(void) {
void handle_keyboard_input(ZephrEvent e) {
bool can_move = !game.quit_dialog && !game.help_dialog && !game.animating;
if (e.key.mods & ZEPHR_KEY_MOD_CTRL && e.key.code == ZEPHR_KEYCODE_Q) {
game.quit_dialog = true;
game_attempt_quit();
} else if (e.key.code == ZEPHR_KEYCODE_ESCAPE) {
if (game.quit_dialog) {
game.quit_dialog = false;
}
game.quit_dialog = false;
game.help_dialog = false;
} else if (e.key.code == ZEPHR_KEYCODE_F11) {
zephr_toggle_fullscreen();
} else if (e.key.code == ZEPHR_KEYCODE_UP) {
if (!game.quit_dialog)
if (can_move)
move_up();
} else if (e.key.code == ZEPHR_KEYCODE_DOWN) {
if (!game.quit_dialog)
if (can_move)
move_down();
} else if (e.key.code == ZEPHR_KEYCODE_LEFT) {
if (!game.quit_dialog)
if (can_move)
move_left();
} else if (e.key.code == ZEPHR_KEYCODE_RIGHT) {
if (!game.quit_dialog)
if (can_move)
move_right();
}
}
@@ -730,7 +800,7 @@ void game_loop(void) {
if (game.quit_dialog) {
zephr_quit();
} else {
game.quit_dialog = true;
game_attempt_quit();
}
break;
case ZEPHR_EVENT_KEY_PRESSED:
@@ -746,6 +816,7 @@ void game_loop(void) {
last_frame = now;
update_positions(delta_t);
draw_bg();
draw_board();
draw_ui();
+2
View File
@@ -13,6 +13,7 @@ typedef enum MoveDir {
typedef enum IconTexture {
HELP_ICON,
SETTINGS_ICON,
CLOSE_ICON,
ICON_TEXTURE_COUNT,
} IconTexture;
@@ -37,6 +38,7 @@ typedef struct Game {
TextureId icon_textures[ICON_TEXTURE_COUNT];
bool quit_dialog;
bool help_dialog;
} Game;
void draw_board(void);
+15 -1
View File
@@ -11,6 +11,7 @@
#include "zephr.h"
#define FONT_PIXEL_SIZE 128
#define LINE_HEIGHT 2.0f
Shader font_shader;
unsigned int font_vao;
@@ -288,6 +289,11 @@ Sizef calculate_text_size(const char *text, int font_size) {
}
h = CORE_MAX(h, max_bearing_h - ch.bearing.height + ch.size.height);
if (text[i] == '\n') {
w = 0;
h += max_bearing_h;
}
}
size.width = (float)w * scale;
size.height = (float)h * scale;
@@ -337,12 +343,20 @@ GlyphInstanceList get_glyph_instance_list_from_text(const char *text, int font_s
// model matrix
int c = 0;
int x = 0;
int y = 0;
while (text[c] != '\0') {
Character ch = zephr_ctx->font.characters[(int)text[c]];
// subtract the bearing width of the first character to remove the extra space
// at the start of the text and move every char to the left by that width
float xpos = (x + (ch.bearing.width - first_char_bearing_w));
float ypos = (text_size.height - ch.bearing.height - (text_size.height - max_bearing_h));
float ypos = y + (text_size.height - ch.bearing.height - (text_size.height - max_bearing_h));
if (text[c] == '\n') {
x = 0;
y += max_bearing_h + (u32)(36.f * LINE_HEIGHT);
c++;
continue;
}
GlyphInstance instance = {
.position = (Vec4f){xpos, ypos, ch.size.width, ch.size.height},
+7
View File
@@ -29,6 +29,13 @@ typedef struct GlyphInstanceList {
int capacity;
} GlyphInstanceList;
// TODO: impl this to align text relative to its box
typedef enum TextAlign {
TEXT_ALIGN_LEFT,
TEXT_ALIGN_CENTER,
TEXT_ALIGN_RIGHT
} TextAlign;
void new_glyph_instance_list(GlyphInstanceList *list, u32 capacity);
int init_fonts(const char *font_path);
Sizef calculate_text_size(const char *text, int font_size);
+6 -2
View File
@@ -43,7 +43,6 @@ void set_parent_constraint(UIConstraints *constraints, UIConstraints *parent_con
void set_x_constraint(UIConstraints *constraints, float value, UIConstraint type) {
switch (type) {
case UI_CONSTRAINT_RELATIVE_PIXELS:
case UI_CONSTRAINT_ASPECT_RATIO:
// no-op. fallback to FIXED
case UI_CONSTRAINT_FIXED:
@@ -52,6 +51,9 @@ void set_x_constraint(UIConstraints *constraints, float value, UIConstraint type
case UI_CONSTRAINT_RELATIVE:
constraints->x = (value * zephr_ctx->window.size.width);
break;
case UI_CONSTRAINT_RELATIVE_PIXELS:
constraints->x = zephr_ctx->window.size.width / (float)zephr_ctx->screen_size.width * value;
break;
}
if (constraints->parent) {
@@ -61,7 +63,6 @@ void set_x_constraint(UIConstraints *constraints, float value, UIConstraint type
void set_y_constraint(UIConstraints *constraints, float value, UIConstraint type) {
switch (type) {
case UI_CONSTRAINT_RELATIVE_PIXELS:
case UI_CONSTRAINT_ASPECT_RATIO:
// no-op. fallback to FIXED
case UI_CONSTRAINT_FIXED:
@@ -70,6 +71,9 @@ void set_y_constraint(UIConstraints *constraints, float value, UIConstraint type
case UI_CONSTRAINT_RELATIVE:
constraints->y = (value * zephr_ctx->window.size.height);
break;
case UI_CONSTRAINT_RELATIVE_PIXELS:
constraints->y = zephr_ctx->window.size.height / (float)zephr_ctx->screen_size.height * value;
break;
}
if (constraints->parent) {
+1
View File
@@ -779,6 +779,7 @@ void zephr_init(const char* font_path, const char* icon_path, const char* window
zephr_ctx->cursors[ZEPHR_CURSOR_HRESIZE] = XCreateFontCursor(x11_display, XC_sb_h_double_arrow);
zephr_ctx->cursors[ZEPHR_CURSOR_VRESIZE] = XCreateFontCursor(x11_display, XC_sb_v_double_arrow);
// non-standard cursors
zephr_ctx->cursors[ZEPHR_CURSOR_DISABLED] = XcursorLibraryLoadCursor(x11_display, "crossed_circle");
x11_get_screen_size(&zephr_ctx->screen_size.width, &zephr_ctx->screen_size.height);
+3 -3
View File
@@ -77,9 +77,9 @@ void apply_translation(Matrix4x4 *matrix, Vec2f pos) {
Color mult_color(Color color, float amount) {
Color result = color;
result.r = CORE_CLAMP((u8)((float)result.r * amount), 0, 255);
result.g = CORE_CLAMP((u8)((float)result.g * amount), 0, 255);
result.b = CORE_CLAMP((u8)((float)result.b * amount), 0, 255);
result.r = CORE_CLAMP((i32)((float)result.r * amount), 0, 255);
result.g = CORE_CLAMP((i32)((float)result.g * amount), 0, 255);
result.b = CORE_CLAMP((i32)((float)result.b * amount), 0, 255);
return result;
}