#include "draw.h" #include #include #include #include #include "fonts.h" #include "game_vexed_icons.h" #include "ui.h" #include "move.h" #include "utils.h" //----------------------------------------------------------------------------- u_int32_t frameNo = 0; int r1 = 0, r2 = 0; //----------------------------------------------------------------------------- void draw_app(Canvas* canvas, Game* game) { canvas_clear(canvas); if((game->state == MAIN_MENU) || (game->state == RESET_PROMPT)) { draw_main_menu(canvas, game); } if(game->state == ABOUT) { draw_about(canvas, game, frameNo); } if(game->state == INTRO) { draw_intro(canvas, game, frameNo); } if(game->state == RESET_PROMPT) { draw_reset_prompt(canvas, game); } if(game->state == INVALID_PROMPT) { draw_invalid_prompt(canvas, game); } if(game->state >= SELECT_BRICK) { draw_playground(canvas, game); switch(game->state) { case SELECT_BRICK: draw_movable(canvas, game, frameNo); break; case SOLUTION_SELECT: draw_direction_solution(canvas, game, frameNo); break; case SELECT_DIRECTION: draw_direction(canvas, game, frameNo); break; case MOVE_SIDES: draw_ani_sides(canvas, game); break; case MOVE_GRAVITY: draw_ani_gravity(canvas, game); break; case EXPLODE: draw_ani_explode(canvas, game); break; default: break; } draw_scores(canvas, game, frameNo); draw_playfield_hint(canvas, game); switch(game->state) { case PAUSED: draw_paused(canvas, game); break; case HISTOGRAM: draw_histogram(canvas, game->stats); break; case SOLUTION_PROMPT: draw_solution_prompt(canvas, game); break; case GAME_OVER: draw_game_over(canvas, game->gameOverReason); break; case LEVEL_FINISHED: draw_level_finished(canvas, game); break; default: break; } } frameNo++; } //----------------------------------------------------------------------------- void draw_intro(Canvas* canvas, Game* game, uint32_t frameNo) { if(frameNo % 2 == 1) { if(game->move.frameNo < 100) game->move.frameNo++; } canvas_set_color(canvas, ColorBlack); if((game->move.frameNo < 12)) { uint8_t x, y; for(y = 0; y < SIZE_Y_BG; y++) { for(x = 0; x < SIZE_X_BG; x++) { canvas_draw_icon( canvas, x * TILE_SIZE, y * TILE_SIZE, tile_to_icon(game->bg[y][x], false)); } } } if((game->move.frameNo < 4)) { gray_canvas(canvas); } if(game->move.frameNo > 7) { canvas_set_color(canvas, ColorXOR); canvas_draw_box(canvas, 0, 0, GUI_DISPLAY_WIDTH, GUI_DISPLAY_HEIGHT); canvas_draw_icon(canvas, 0, 0, &I_logo_vexed_big); } if(game->move.frameNo > 11) { canvas_set_color(canvas, ColorBlack); canvas_draw_icon(canvas, 0, 0, &I_logo_vexed_big); } if(game->move.frameNo == 24) { game->state = MAIN_MENU; } } void draw_about(Canvas* canvas, Game* game, uint32_t frameNo) { if(frameNo % 10 == 9) { randomize_bg(&game->bg); } if(frameNo % 50 == 49) { r1 = rand(); r2 = rand(); randomize_bg(&game->bg); } uint8_t sx, sy; for(sy = 0; sy < SIZE_Y_BG; sy++) { for(sx = 0; sx < SIZE_X_BG; sx++) { canvas_draw_icon( canvas, (sx * TILE_SIZE) - (r1 % 7), sy * TILE_SIZE - (r2 % 7), tile_to_icon(game->bg[sy][sx], false)); } } //gray_canvas(canvas); // back for os buttons canvas_set_color(canvas, ColorWhite); canvas_draw_rbox(canvas, 44, 50, GUI_DISPLAY_WIDTH, GUI_DISPLAY_HEIGHT, 3); const uint8_t y = dialog_frame(canvas, 92, 43, false, true, "Exit game?"); canvas_set_custom_u8g2_font(canvas, app_u8g2_font_squeezed_r6_tr); canvas_set_color(canvas, ColorBlack); canvas_draw_str_aligned( canvas, GUI_DISPLAY_WIDTH / 2, y + 2, AlignCenter, AlignTop, "(c) 2024 Dominik Dzienia"); canvas_draw_str_aligned( canvas, GUI_DISPLAY_WIDTH / 2, y + 13, AlignCenter, AlignTop, "based on Vexed 2.2"); canvas_draw_str_aligned( canvas, GUI_DISPLAY_WIDTH / 2, y + 21, AlignCenter, AlignTop, "(c) 2006 Vexed Dev Team"); canvas_set_color(canvas, ColorBlack); canvas_set_font(canvas, FontSecondary); elements_button_center(canvas, "Exit"); elements_button_right_back(canvas, "Back"); } void draw_set_info(Canvas* canvas, Game* game) { BoundingBox box; const uint8_t w = 118; const uint8_t h = 46; const uint8_t x = (GUI_DISPLAY_WIDTH - w) / 2; const uint8_t y = dialog_frame(canvas, w, h, false, false, furi_string_get_cstr(game->levelSet->title)); canvas_set_custom_u8g2_font(canvas, app_u8g2_font_squeezed_r6_tr); canvas_set_color(canvas, ColorBlack); canvas_draw_str_aligned( canvas, GUI_DISPLAY_WIDTH / 2, y, AlignCenter, AlignTop, furi_string_get_cstr(game->levelSet->author)); canvas_draw_str_aligned( canvas, GUI_DISPLAY_WIDTH / 2, y + 8, AlignCenter, AlignTop, furi_string_get_cstr(game->levelSet->url)); canvas_draw_hline_dotted(canvas, x, y + 16, w); set_bounding_box(&box, x + 3, y + 16, w - 6, 16); elements_multiline_text_aligned_limited( canvas, &box, box.width / 2, box.height / 2, 2, AlignCenter, AlignCenter, furi_string_get_cstr(game->levelSet->description)); } //----------------------------------------------------------------------------- void draw_level_info(Canvas* canvas, Game* game) { int bufSize = 80; char buf[bufSize]; memset(buf, 0, bufSize); snprintf( buf, sizeof(buf), "%s #%u", furi_string_get_cstr(game->levelSet->title), game->selectedLevel + 1); const uint8_t x = (GUI_DISPLAY_WIDTH - 100) / 2; const uint8_t y = dialog_frame(canvas, 100, 40, false, false, buf); canvas_set_color(canvas, ColorBlack); canvas_draw_vline_dotted(canvas, GUI_DISPLAY_CENTER_X, y, 30); canvas_set_font(canvas, FontSecondary); canvas_set_custom_u8g2_font(canvas, app_u8g2_font_squeezed_r7_tr); canvas_draw_str_aligned(canvas, x + 25, y + 4, AlignCenter, AlignTop, "Moves/Par"); memset(buf, 0, bufSize); if(game->levelSet->scores[game->selectedLevel].moves == 0) { snprintf( buf, sizeof(buf), "??? / %u", game->levelSet->pars[game->selectedLevel]); } else { snprintf( buf, sizeof(buf), "%u / %u", game->levelSet->scores[game->selectedLevel].moves, game->levelSet->pars[game->selectedLevel]); } canvas_set_font(canvas, FontPrimary); canvas_draw_str_aligned(canvas, x + 25, y + 22, AlignCenter, AlignBottom, buf); canvas_set_custom_u8g2_font(canvas, app_u8g2_font_squeezed_r7_tr); canvas_draw_str_aligned(canvas, x + 75, y + 4, AlignCenter, AlignTop, "Score"); memset(buf, 0, bufSize); if(game->score == 0) { snprintf(buf, sizeof(buf), "on par"); } else { snprintf(buf, sizeof(buf), "%+d", game->score); } canvas_set_font(canvas, FontPrimary); canvas_draw_str_aligned(canvas, x + 75, y + 22, AlignCenter, AlignBottom, buf); } //----------------------------------------------------------------------------- void draw_main_menu_new_game(Canvas* canvas, Game* game) { canvas_set_color(canvas, ColorBlack); canvas_set_font(canvas, FontSecondary); elements_button_center(canvas, "Start"); if(game->hasContinue) { canvas_set_custom_u8g2_font(canvas, app_u8g2_font_squeezed_r7_tr); elements_button_right(canvas, "Continue"); canvas_draw_str_aligned( canvas, GUI_DISPLAY_CENTER_X, 37, AlignCenter, AlignTop, "!!! Forgets all progress and scores !!!"); } } //----------------------------------------------------------------------------- void draw_main_menu_continue(Canvas* canvas, Game* game) { int bufSize = 80; char buf[bufSize]; int scorebufSize = 10; char scorebufSet[scorebufSize]; bool hasNext = (game->continueLevel + 1) < game->levelSet->maxLevel; memset(scorebufSet, 0, scorebufSize); if(game->score == 0) { snprintf(scorebufSet, sizeof(scorebufSet), "par"); } else { snprintf(scorebufSet, sizeof(scorebufSet), "%+d", game->score); } canvas_set_color(canvas, ColorBlack); canvas_set_font(canvas, FontSecondary); elements_button_left(canvas, "New"); elements_button_center(canvas, "Start"); canvas_set_custom_u8g2_font(canvas, app_u8g2_font_squeezed_r7_tr); elements_button_right(canvas, "Custom"); memset(buf, 0, bufSize); if(hasNext) { snprintf( buf, sizeof(buf), "%s (%s), #%d", furi_string_get_cstr(game->continueSet), scorebufSet, game->continueLevel + 2); } else { snprintf(buf, sizeof(buf), "%s finished!", furi_string_get_cstr(game->continueSet)); } canvas_draw_str_aligned(canvas, GUI_DISPLAY_CENTER_X, 37, AlignCenter, AlignTop, buf); } //----------------------------------------------------------------------------- void draw_main_menu_custom(Canvas* canvas, Game* game) { int bufSize = 80; char buf[bufSize]; int scorebufSize = 10; char scorebuf[scorebufSize]; canvas_set_color(canvas, ColorBlack); canvas_set_custom_u8g2_font(canvas, app_u8g2_font_squeezed_r7_tr); main_menu_pill( canvas, 35, 90, game->mainMenuBtn == LEVELSET_BTN, game->setPos > 0, game->setPos < game->setCount - 1, furi_string_get_cstr(game->selectedSet)); memset(scorebuf, 0, scorebufSize); score_for_level(game, game->selectedLevel, scorebuf, scorebufSize); canvas_set_font(canvas, FontSecondary); memset(buf, 0, bufSize); snprintf( buf, sizeof(buf), "%u of %u (%s)", game->selectedLevel + 1, game->levelSet->maxLevel, scorebuf); main_menu_pill( canvas, 50, 90, game->mainMenuBtn == LEVELNO_BTN, game->selectedLevel > 0, game->selectedLevel < game->levelSet->maxLevel - 1, buf); } //----------------------------------------------------------------------------- void draw_main_menu(Canvas* canvas, Game* game) { canvas_set_color(canvas, ColorBlack); canvas_draw_line(canvas, 0, 6, GUI_DISPLAY_WIDTH, 6); canvas_draw_line(canvas, 0, 9, GUI_DISPLAY_WIDTH, 9); canvas_set_color(canvas, ColorBlack); canvas_draw_rbox(canvas, GUI_DISPLAY_CENTER_X - 16 - 6, 1, 32 + 12, 14, 3); canvas_set_color(canvas, ColorWhite); canvas_draw_icon(canvas, GUI_DISPLAY_CENTER_X - 16, 2, &I_logo_vexed_mini); canvas_set_font(canvas, FontSecondary); main_menu_pill( canvas, 20, 90, game->mainMenuBtn == MODE_BTN, game->mainMenuMode != NEW_GAME, game->mainMenuMode != CUSTOM, game_mode_label(game->mainMenuMode)); switch(game->mainMenuMode) { case CONTINUE: draw_main_menu_continue(canvas, game); break; case CUSTOM: draw_main_menu_custom(canvas, game); break; case NEW_GAME: default: draw_main_menu_new_game(canvas, game); break; } if(game->mainMenuInfo) { gray_canvas(canvas); if(game->mainMenuBtn == LEVELSET_BTN) { draw_set_info(canvas, game); } else if(game->mainMenuBtn == LEVELNO_BTN) { draw_level_info(canvas, game); } canvas_set_color(canvas, ColorBlack); canvas_set_font(canvas, FontSecondary); elements_button_center(canvas, "Start"); elements_button_right_back(canvas, "Back"); } } //----------------------------------------------------------------------------- void draw_playground(Canvas* canvas, Game* game) { Neighbors tiles; uint8_t tile, x, y, sx, sy, ex, ey; bool whiteB = (game->state == LEVEL_FINISHED) || (game->solutionMode); for(y = 0; y < SIZE_Y; y++) { for(x = 0; x < SIZE_X; x++) { tile = game->board[y][x]; sx = x * TILE_SIZE; sy = y * TILE_SIZE; ex = ((x + 1) * TILE_SIZE) - 1; ey = ((y + 1) * TILE_SIZE) - 1; if(tile > 0) { if((game->state == MOVE_SIDES) && (x == game->move.x) && (y == game->move.y)) continue; if(((game->state == MOVE_GRAVITY) || (game->state == EXPLODE)) && (game->toAnimate[y][x] == 1)) continue; canvas_set_color(canvas, ColorBlack); canvas_draw_icon(canvas, sx, sy, tile_to_icon(tile, game->state == GAME_OVER)); } if(tile == WALL_TILE) { tiles = find_neighbors(&game->board, x, y); // UP if(tiles.u != WALL_TILE) { canvas_set_color(canvas, ColorBlack); canvas_draw_line(canvas, sx, sy + 1, ex, sy + 1); canvas_set_color(canvas, ColorWhite); canvas_draw_line(canvas, sx, sy, ex, sy); if(whiteB) canvas_draw_line(canvas, sx, sy + 2, ex, sy + 2); } // DOWN if(tiles.d != WALL_TILE) { canvas_set_color(canvas, ColorBlack); canvas_draw_line(canvas, sx, ey, ex, ey); canvas_set_color(canvas, ColorWhite); if(whiteB) canvas_draw_line(canvas, sx, ey - 1, ex, ey - 1); } // LEFT if(tiles.l != WALL_TILE) { canvas_set_color(canvas, ColorBlack); canvas_draw_line( canvas, sx + 1, sy + ((tiles.u != WALL_TILE) ? 1 : 0), sx + 1, ey); canvas_set_color(canvas, ColorWhite); canvas_draw_line(canvas, sx, sy, sx, ey); if(whiteB) canvas_draw_line( canvas, sx + 2, sy + ((tiles.u != WALL_TILE) ? 2 : 0), sx + 2, ey - ((tiles.d != WALL_TILE) ? 2 : 0)); } // RIGHT if(tiles.r != WALL_TILE) { canvas_set_color(canvas, ColorBlack); canvas_draw_line(canvas, ex, (sy) + ((tiles.u != WALL_TILE) ? 1 : 0), ex, ey); canvas_set_color(canvas, ColorWhite); if(whiteB) canvas_draw_line( canvas, ex - 1, sy + ((tiles.u != WALL_TILE) ? 2 : 0), ex - 1, ey - ((tiles.d != WALL_TILE) ? 2 : 0)); } if((tiles.dl != WALL_TILE) && (tiles.l == WALL_TILE)) { canvas_set_color(canvas, ColorBlack); canvas_draw_line(canvas, sx, ey, sx + 1, ey); canvas_set_color(canvas, ColorWhite); if(whiteB) canvas_draw_line(canvas, sx, ey - 1, sx + 2, ey - 1); } if((tiles.ur != WALL_TILE) && (tiles.u == WALL_TILE)) { canvas_set_color(canvas, ColorBlack); canvas_draw_line(canvas, ex, sy, ex, sy + 1); canvas_set_color(canvas, ColorWhite); if(whiteB) canvas_draw_line(canvas, ex - 1, sy, ex - 1, sy + 2); } if(tiles.ul != WALL_TILE) { canvas_set_color(canvas, ColorWhite); canvas_draw_dot(canvas, sx, sy); if(whiteB) canvas_draw_dot(canvas, sx + 2, sy + 2); if(tiles.l == WALL_TILE) { canvas_set_color(canvas, ColorBlack); canvas_draw_line(canvas, sx, sy + 1, sx + 1, sy + 1); canvas_set_color(canvas, ColorWhite); if(whiteB) canvas_draw_line(canvas, sx, sy + 2, sx + 1, sy + 2); } if(tiles.u == WALL_TILE) { canvas_set_color(canvas, ColorBlack); canvas_draw_line(canvas, sx + 1, sy, sx + 1, sy + 1); canvas_set_color(canvas, ColorWhite); if(whiteB) canvas_draw_line(canvas, sx + 2, sy, sx + 2, sy + 1); } } if((tiles.dr != WALL_TILE) && (tiles.r == WALL_TILE) && (tiles.d == WALL_TILE)) { canvas_set_color(canvas, ColorWhite); if(whiteB) canvas_draw_line(canvas, ex - 1, ey - 1, ex - 1, ey); } } } } } //----------------------------------------------------------------------------- void draw_movable(Canvas* canvas, Game* game, uint32_t frameNo) { bool oddFrame = (frameNo % 20 < 10); if(game->currentMovable != MOVABLE_NOT_FOUND) { canvas_set_color(canvas, ColorBlack); uint8_t x = coord_x(game->currentMovable); uint8_t y = coord_y(game->currentMovable); uint8_t how_movable = game->movables[y][x]; if((how_movable & MOVABLE_LEFT) != 0) { canvas_draw_icon( canvas, (x - 1) * TILE_SIZE + (oddFrame ? 0 : 1), y * TILE_SIZE, &I_arr_l); } if((how_movable & MOVABLE_RIGHT) != 0) { canvas_draw_icon( canvas, (x + 1) * TILE_SIZE + (oddFrame ? 1 : 0), y * TILE_SIZE, &I_arr_r); } canvas_draw_frame( canvas, x * TILE_SIZE - 1, y * TILE_SIZE - 1, TILE_SIZE + 3, TILE_SIZE + 3); if(oddFrame) { canvas_draw_frame( canvas, x * TILE_SIZE - 2, y * TILE_SIZE - 2, TILE_SIZE + 5, TILE_SIZE + 5); } else { canvas_draw_frame(canvas, x * TILE_SIZE, y * TILE_SIZE, TILE_SIZE + 1, TILE_SIZE + 1); } } } //----------------------------------------------------------------------------- void draw_direction(Canvas* canvas, Game* game, uint32_t frameNo) { bool oddFrame = (frameNo % 20 < 10); if(game->currentMovable != MOVABLE_NOT_FOUND) { canvas_set_color(canvas, ColorBlack); uint8_t x = coord_x(game->currentMovable); uint8_t y = coord_y(game->currentMovable); if(oddFrame) { canvas_draw_icon(canvas, (x - 1) * TILE_SIZE, y * TILE_SIZE, &I_mov_l); canvas_draw_icon(canvas, ((x + 1) * TILE_SIZE) + 1, y * TILE_SIZE, &I_mov_r); } } } //----------------------------------------------------------------------------- void draw_direction_solution(Canvas* canvas, Game* game, uint32_t frameNo) { bool oddFrame = (frameNo % 20 < 10); if(game->currentMovable != MOVABLE_NOT_FOUND) { canvas_set_color(canvas, ColorBlack); uint8_t x = coord_x(game->currentMovable); uint8_t y = coord_y(game->currentMovable); uint8_t how_movable = game->movables[y][x]; if((how_movable & MOVABLE_LEFT) != 0) { canvas_draw_icon( canvas, (x - 1) * TILE_SIZE + (oddFrame ? 0 : 1), y * TILE_SIZE, &I_arr_l); } if((how_movable & MOVABLE_RIGHT) != 0) { canvas_draw_icon( canvas, (x + 1) * TILE_SIZE + (oddFrame ? 1 : 0), y * TILE_SIZE, &I_mov_r); } canvas_draw_frame( canvas, x * TILE_SIZE - 1, y * TILE_SIZE - 1, TILE_SIZE + 3, TILE_SIZE + 3); if(oddFrame) { canvas_draw_frame( canvas, x * TILE_SIZE - 2, y * TILE_SIZE - 2, TILE_SIZE + 5, TILE_SIZE + 5); } else { canvas_draw_frame(canvas, x * TILE_SIZE, y * TILE_SIZE, TILE_SIZE + 1, TILE_SIZE + 1); } } game->move.frameNo--; if(game->move.frameNo == 0) { solution_move(game); } } //----------------------------------------------------------------------------- void draw_ani_sides(Canvas* canvas, Game* game) { uint8_t tile, sx, sy, deltaX; if(game->state == MOVE_SIDES) { tile = game->board[game->move.y][game->move.x]; deltaX = ((game->move.dir & MOVABLE_LEFT) != 0) ? -1 : 1; sx = (game->move.x * TILE_SIZE) + (deltaX * game->move.frameNo); sy = game->move.y * TILE_SIZE; canvas_set_color(canvas, ColorBlack); canvas_draw_icon(canvas, sx, sy, tile_to_icon(tile, game->state == GAME_OVER)); game->move.frameNo++; if(game->move.frameNo > TILE_SIZE) { stop_move(game); } } } //----------------------------------------------------------------------------- void draw_ani_gravity(Canvas* canvas, Game* game) { uint8_t tile, x, y, sx, sy; if(game->state == MOVE_GRAVITY) { for(y = 0; y < SIZE_Y; y++) { for(x = 0; x < SIZE_X; x++) { tile = game->board[y][x]; sx = x * TILE_SIZE; sy = y * TILE_SIZE; if((tile > 0) && (game->toAnimate[y][x] == 1)) { canvas_set_color(canvas, ColorBlack); canvas_draw_icon( canvas, sx, sy + game->move.frameNo, tile_to_icon(tile, game->state == GAME_OVER)); } } } if(game->move.delay > 0) { game->move.delay--; return; } game->move.frameNo++; if(game->move.frameNo > TILE_SIZE) { stop_gravity(game); } } } //----------------------------------------------------------------------------- void draw_ani_explode(Canvas* canvas, Game* game) { uint8_t tile, x, y, sx, sy, cx, cy, s, o; if(game->state == EXPLODE) { for(y = 0; y < SIZE_Y; y++) { for(x = 0; x < SIZE_X; x++) { tile = game->board[y][x]; if((tile > 0) && (game->toAnimate[y][x] == 1)) { sx = x * TILE_SIZE; sy = y * TILE_SIZE; cx = sx + 4; cy = sy + 4; if((game->move.delay % 4 < 2) || (game->move.delay > 8)) { canvas_set_color(canvas, ColorBlack); canvas_draw_icon( canvas, sx, sy, tile_to_icon(tile, game->state == GAME_OVER)); } if(game->move.frameNo > 0) { canvas_set_color(canvas, ColorXOR); o = MIN(((game->move.frameNo - 1) / 2), (uint8_t)4); s = (o * 2) + 1; canvas_draw_box(canvas, cx - o, cy - o, s, s); } } } } if(game->move.delay > 0) { game->move.delay--; return; } game->move.frameNo++; if(game->move.frameNo > 10) { stop_explosion(game); } } } //----------------------------------------------------------------------------- void draw_scores(Canvas* canvas, Game* game, uint32_t frameNo) { BoundingBox box; int bufSize = 80; char buf[bufSize]; bool showScore = (frameNo % 200) < 100; canvas_set_color(canvas, ColorBlack); canvas_draw_rbox(canvas, 82, 1, 46, 17, 2); canvas_set_custom_u8g2_font(canvas, app_u8g2_font_squeezed_r6_tr); canvas_set_color(canvas, ColorWhite); set_bounding_box(&box, 82, 1, 46, 17); elements_multiline_text_aligned_limited( canvas, &box, box.width / 2, box.height / 2, 2, AlignCenter, AlignCenter, furi_string_get_cstr(game->levelData->title)); if(game->solutionMode) { canvas_set_color(canvas, ColorBlack); canvas_set_custom_u8g2_font(canvas, app_u8g2_font_wedge_tr); canvas_draw_str_aligned(canvas, 104, 27, AlignCenter, AlignTop, "Solution"); canvas_set_custom_u8g2_font(canvas, app_u8g2_font_tom_thumb_4x6_mr); memset(buf, 0, bufSize); snprintf(buf, sizeof(buf), "%d of %d", game->solutionStep + 1, game->solutionTotal); canvas_draw_str_aligned(canvas, 104, 34, AlignCenter, AlignTop, buf); } else { canvas_set_color(canvas, ColorBlack); canvas_set_custom_u8g2_font(canvas, app_u8g2_font_wedge_tr); canvas_draw_str_aligned( canvas, 104, 20, AlignCenter, AlignTop, showScore ? "Score" : "Level"); canvas_set_custom_u8g2_font(canvas, app_u8g2_font_tom_thumb_4x6_mr); memset(buf, 0, bufSize); if(showScore) { if(game->score == 0) { snprintf(buf, sizeof(buf), "on par"); } else { snprintf(buf, sizeof(buf), "%+d", game->score); } } else { snprintf(buf, sizeof(buf), "%u/%u", game->currentLevel + 1, game->levelSet->maxLevel); } canvas_draw_str_aligned(canvas, 104, 27, AlignCenter, AlignTop, buf); canvas_set_custom_u8g2_font(canvas, app_u8g2_font_wedge_tr); canvas_draw_str_aligned( canvas, 104, 34, AlignCenter, AlignTop, showScore ? "Best" : "Moves"); canvas_set_custom_u8g2_font(canvas, app_u8g2_font_tom_thumb_4x6_mr); memset(buf, 0, bufSize); if(showScore) { snprintf(buf, sizeof(buf), "%s", game->parLabel); } else { snprintf(buf, sizeof(buf), "%u/%u", game->gameMoves, game->levelData->gamePar); } canvas_draw_str_aligned(canvas, 104, 41, AlignCenter, AlignTop, buf); } } //----------------------------------------------------------------------------- void draw_paused(Canvas* canvas, Game* game) { gray_canvas(canvas); menu_pill( canvas, 0, MENU_PAUSED_COUNT, ((game->menuPausedPos == 0) && (game->undoMovable != MOVABLE_NOT_FOUND)), game->undoMovable == MOVABLE_NOT_FOUND, "Undo", &I_ico_undo); menu_pill( canvas, 1, MENU_PAUSED_COUNT, game->menuPausedPos == 1, false, "Restart", &I_ico_restart); menu_pill(canvas, 2, MENU_PAUSED_COUNT, game->menuPausedPos == 2, false, "Menu", &I_ico_home); menu_pill(canvas, 3, MENU_PAUSED_COUNT, game->menuPausedPos == 3, false, "Skip", &I_ico_skip); menu_pill(canvas, 4, MENU_PAUSED_COUNT, game->menuPausedPos == 4, false, "Count", &I_ico_hist); menu_pill( canvas, 5, MENU_PAUSED_COUNT, game->menuPausedPos == 5, false, "Solve", &I_ico_check); canvas_set_color(canvas, ColorBlack); canvas_set_font(canvas, FontSecondary); elements_button_right_back(canvas, "Back to game"); } //----------------------------------------------------------------------------- void draw_histogram(Canvas* canvas, Stats* stats) { gray_canvas(canvas); panel_histogram(canvas, furi_string_get_cstr(stats->bricksNonZero), stats->statsNonZero); } void draw_playfield_hint(Canvas* canvas, Game* game) { if(game->state == SELECT_BRICK) { if((game->currentMovable != MOVABLE_NOT_FOUND) && (movable_dir(&game->movables, game->currentMovable) == MOVABLE_BOTH)) { hint_pill_double(canvas, "Select", "Choose", &I_hint_2); } else { hint_pill_double(canvas, "Select", "Move", &I_hint_1); } } if(game->state == SELECT_DIRECTION) { hint_pill_double(canvas, "Move", "Cancel", &I_hint_3); } if(game->state == SOLUTION_SELECT || game->solutionMode) { hint_pill_double(canvas, "ANY", "CANCEL", &I_hint_4); } else { if(game->state == MOVE_SIDES) { hint_pill_single(canvas, "moving.."); } if(game->state == MOVE_GRAVITY) { hint_pill_single(canvas, "falling.."); } if(game->state == EXPLODE) { hint_pill_single(canvas, "BOOM!"); } } } //----------------------------------------------------------------------------- void draw_game_over(Canvas* canvas, GameOver gameOverReason) { gray_canvas(canvas); const uint8_t y = dialog_frame(canvas, 100, 38, true, false, "Game Over!"); canvas_set_color(canvas, ColorBlack); canvas_set_font(canvas, FontSecondary); if(gameOverReason == CANNOT_MOVE) { canvas_draw_str_aligned( canvas, GUI_DISPLAY_CENTER_X, y + 8, AlignCenter, AlignTop, "Cannot move"); } else if(gameOverReason == BRICKS_LEFT) { canvas_draw_str_aligned( canvas, GUI_DISPLAY_CENTER_X, y + 8, AlignCenter, AlignTop, "Unpaired bricks left"); } elements_button_left(canvas, "Retry"); elements_button_center(canvas, "Menu"); elements_button_right_back(canvas, "Undo"); } //----------------------------------------------------------------------------- void draw_level_finished(Canvas* canvas, Game* game) { int bufSize = 80; char buf[bufSize]; bool hasNext = game->currentLevel < game->levelSet->maxLevel - 1; gray_canvas(canvas); const uint8_t x = (GUI_DISPLAY_WIDTH - 100) / 2; const uint8_t y = dialog_frame(canvas, 100, 40, true, false, hasNext ? "Level finished!" : "Pack finished!"); canvas_set_color(canvas, ColorBlack); canvas_draw_vline_dotted(canvas, GUI_DISPLAY_CENTER_X, y, 30); canvas_set_font(canvas, FontSecondary); canvas_set_custom_u8g2_font(canvas, app_u8g2_font_squeezed_r7_tr); canvas_draw_str_aligned(canvas, x + 25, y + 4, AlignCenter, AlignTop, "Moves/Par"); memset(buf, 0, bufSize); snprintf(buf, sizeof(buf), "%u / %u", game->gameMoves, game->levelData->gamePar); canvas_set_font(canvas, FontPrimary); canvas_draw_str_aligned(canvas, x + 25, y + 22, AlignCenter, AlignBottom, buf); canvas_set_custom_u8g2_font(canvas, app_u8g2_font_squeezed_r7_tr); canvas_draw_str_aligned(canvas, x + 75, y + 4, AlignCenter, AlignTop, "Score"); memset(buf, 0, bufSize); if(game->score == 0) { snprintf(buf, sizeof(buf), "on par"); } else { snprintf(buf, sizeof(buf), "%+d", game->score); } canvas_set_font(canvas, FontPrimary); canvas_draw_str_aligned(canvas, x + 75, y + 22, AlignCenter, AlignBottom, buf); canvas_set_font(canvas, FontSecondary); if(hasNext) { elements_button_center(canvas, "Next"); elements_button_right_back(canvas, "Menu"); } else { elements_button_center(canvas, "Menu"); } } //----------------------------------------------------------------------------- void draw_solution_prompt(Canvas* canvas, Game* game) { gray_canvas(canvas); const uint8_t y = dialog_frame(canvas, 100, 40, true, false, "Show solution?"); const bool penalty = solution_will_have_penalty(game); canvas_set_color(canvas, ColorBlack); canvas_set_custom_u8g2_font(canvas, app_u8g2_font_squeezed_r7_tr); if(penalty) { canvas_draw_str_aligned( canvas, GUI_DISPLAY_CENTER_X, y + 4, AlignCenter, AlignTop, "It has one-time penalty"); canvas_draw_str_aligned( canvas, GUI_DISPLAY_CENTER_X, y + 15, AlignCenter, AlignTop, "of additional 5 point"); } else { canvas_draw_str_aligned( canvas, GUI_DISPLAY_CENTER_X, y + 4, AlignCenter, AlignTop, "Show solution?"); } elements_button_center(canvas, "Show"); elements_button_right_back(canvas, "Resign"); } //----------------------------------------------------------------------------- void draw_reset_prompt(Canvas* canvas, Game* game) { UNUSED(game); gray_canvas(canvas); const uint8_t y = dialog_frame(canvas, 110, 45, true, false, "Reset game?"); canvas_set_color(canvas, ColorBlack); canvas_set_custom_u8g2_font(canvas, app_u8g2_font_squeezed_r7_tr); canvas_draw_str_aligned( canvas, GUI_DISPLAY_CENTER_X, y + 2, AlignCenter, AlignTop, "Starting new game will reset"); canvas_draw_str_aligned( canvas, GUI_DISPLAY_CENTER_X, y + 10, AlignCenter, AlignTop, "all progress and scores!"); canvas_draw_str_aligned( canvas, GUI_DISPLAY_CENTER_X, y + 21, AlignCenter, AlignTop, "Are you sure?"); canvas_set_font(canvas, FontSecondary); elements_button_center(canvas, "Confirm"); canvas_set_custom_u8g2_font(canvas, app_u8g2_font_squeezed_r7_tr); elements_button_right_back(canvas, "Back"); } //----------------------------------------------------------------------------- void draw_invalid_prompt(Canvas* canvas, Game* game) { UNUSED(game); gray_canvas(canvas); const uint8_t y = dialog_frame(canvas, 110, 45, true, false, "Invalid level"); canvas_set_color(canvas, ColorBlack); canvas_set_custom_u8g2_font(canvas, app_u8g2_font_squeezed_r7_tr); canvas_draw_str_aligned( canvas, GUI_DISPLAY_CENTER_X, y + 2, AlignCenter, AlignTop, "Cannot load/parse level!"); canvas_draw_str_aligned( canvas, GUI_DISPLAY_CENTER_X, y + 10, AlignCenter, AlignTop, furi_string_get_cstr(game->errorMsg)); canvas_draw_str_aligned( canvas, GUI_DISPLAY_CENTER_X, y + 21, AlignCenter, AlignTop, "Repair or remove file!"); canvas_set_font(canvas, FontSecondary); elements_button_center(canvas, "Understood"); }