From 59a59b78e9697655dfd1b20efd57a1ca8d02b35b Mon Sep 17 00:00:00 2001 From: RogueMaster Date: Tue, 25 Oct 2022 03:25:48 -0400 Subject: [PATCH] game15 --- applications/plugins/game15/README.md | 8 +- applications/plugins/game15/game15.c | 208 +++++++++++++----- applications/plugins/game15/game15.fap | Bin 9580 -> 0 bytes .../plugins/game15/images/Game15Popup.png | Bin 0 -> 2267 bytes .../plugins/game15/images/Game15Restore.png | Bin 0 -> 2156 bytes 5 files changed, 155 insertions(+), 61 deletions(-) delete mode 100644 applications/plugins/game15/game15.fap create mode 100644 applications/plugins/game15/images/Game15Popup.png create mode 100644 applications/plugins/game15/images/Game15Restore.png diff --git a/applications/plugins/game15/README.md b/applications/plugins/game15/README.md index ec6b08634..493983180 100644 --- a/applications/plugins/game15/README.md +++ b/applications/plugins/game15/README.md @@ -1,8 +1,12 @@ -#Game "15" for Flipper Zero + +# Game "15" for Flipper Zero Logic game [Wikipedia](https://en.wikipedia.org/wiki/15_puzzle) ![Game screen](images/Game15.png) -FAP file for firmware 0.69.1 +![Restore game](images/Game15Restore.png) +![Popoup](images/Game15Popup.png) + +FAP file for firmware 0.69.1 diff --git a/applications/plugins/game15/game15.c b/applications/plugins/game15/game15.c index 939b49e6e..7e1053b4c 100644 --- a/applications/plugins/game15/game15.c +++ b/applications/plugins/game15/game15.c @@ -11,8 +11,9 @@ #define CELL_HEIGHT 8 #define MOVE_TICKS 5 #define KEY_STACK_SIZE 16 -#define TOP_RECORD_DIRECTORY "/ext/apps/Games" -#define TOP_RECORD_FILENAME TOP_RECORD_DIRECTORY "/game15.top" +#define SAVING_DIRECTORY "/ext/apps/Games" +#define SAVING_FILENAME SAVING_DIRECTORY "/game15.save" +#define POPUP_MENU_ITEMS 2 typedef enum { DirectionNone, @@ -22,7 +23,7 @@ typedef enum { DirectionRight } direction_e; -typedef enum { ScenePlay, SceneWin } scene_e; +typedef enum { ScenePlay, SceneWin, ScenePopup } scene_e; typedef struct { uint8_t cell_index; @@ -31,13 +32,24 @@ typedef struct { uint8_t move_ticks; } moving_cell_t; -static scene_e scene; -static uint8_t board[16]; -static uint16_t top_record; -static uint16_t move_count; -static uint32_t tick_count; +typedef struct { + uint16_t top_record; + scene_e scene; + uint16_t move_count; + uint32_t tick_count; + uint8_t board[16]; +} game_state_t; + +static game_state_t game_state; static NotificationApp* notification; static moving_cell_t moving_cell; +static uint8_t loaded_saving_ticks; +static uint8_t popup_menu_selected_item; + +static const char* popup_menu_strings[] = { + "Continue", + "Reset" +}; static uint8_t keys[KEY_STACK_SIZE]; static uint8_t key_stack_head = 0; @@ -105,29 +117,32 @@ static int key_stack_push(uint8_t value) { return -1; } -static void storage_top_record_load() { +static bool storage_game_state_load() { Storage* storage = furi_record_open(RECORD_STORAGE); File* file = storage_file_alloc(storage); - if(storage_file_open(file, TOP_RECORD_FILENAME, FSAM_READ, FSOM_OPEN_EXISTING)) - storage_file_read(file, &top_record, 2); + uint16_t bytes_readed = 0; + if(storage_file_open(file, SAVING_FILENAME, FSAM_READ, FSOM_OPEN_EXISTING)) + bytes_readed = storage_file_read(file, &game_state, sizeof(game_state_t)); storage_file_close(file); storage_file_free(file); furi_record_close(RECORD_STORAGE); + return bytes_readed == sizeof(game_state_t); } -static void storage_top_record_save() { +static void storage_game_state_save() { Storage* storage = furi_record_open(RECORD_STORAGE); - if(storage_common_stat(storage, TOP_RECORD_DIRECTORY, NULL) == FSE_NOT_EXIST) { - if(!storage_simply_mkdir(storage, TOP_RECORD_DIRECTORY)) { + if(storage_common_stat(storage, SAVING_DIRECTORY, NULL) == FSE_NOT_EXIST) { + if(!storage_simply_mkdir(storage, SAVING_DIRECTORY)) { return; } } File* file = storage_file_alloc(storage); - if(storage_file_open(file, TOP_RECORD_FILENAME, FSAM_WRITE, FSOM_CREATE_ALWAYS)) - storage_file_write(file, &top_record, 2); + if(storage_file_open(file, SAVING_FILENAME, FSAM_WRITE, FSOM_CREATE_ALWAYS)) { + storage_file_write(file, &game_state, sizeof(game_state_t)); + } storage_file_close(file); storage_file_free(file); furi_record_close(RECORD_STORAGE); @@ -138,7 +153,7 @@ static void set_moving_cell_by_direction(direction_e direction) { moving_cell.zero_index = 0xff; for(int i = 0; i < 16; i++) { - if(!board[i]) { + if(!game_state.board[i]) { moving_cell.zero_index = i; break; } @@ -168,51 +183,52 @@ static void set_moving_cell_by_direction(direction_e direction) { static bool is_board_has_solution() { uint8_t i, j, inv = 0; for(i = 0; i < 16; ++i) - if(board[i]) + if(game_state.board[i]) for(j = 0; j < i; ++j) - if(board[j] > board[i]) ++inv; + if(game_state.board[j] > game_state.board[i]) ++inv; for(i = 0; i < 16; ++i) - if(board[i] == 0) inv += 1 + i / 4; + if(game_state.board[i] == 0) inv += 1 + i / 4; return inv % 2 == 0; } static void board_init() { for(int i = 0; i < 16; i++) { - board[i] = (i + 1) % 16; + game_state.board[i] = (i + 1) % 16; } do { for(int i = 15; i >= 1; i--) { int j = rand() % (i + 1); - uint8_t tmp = board[j]; - board[j] = board[i]; - board[i] = tmp; + uint8_t tmp = game_state.board[j]; + game_state.board[j] = game_state.board[i]; + game_state.board[i] = tmp; } } while(!is_board_has_solution()); } static void game_init() { - scene = ScenePlay; - move_count = 0; - tick_count = 0; - storage_top_record_load(); + game_state.scene = ScenePlay; + game_state.move_count = 0; + game_state.tick_count = 0; moving_cell.move_direction = DirectionNone; board_init(); key_stack_init(); + popup_menu_selected_item = 0; } static bool is_board_solved() { for(int i = 0; i < 16; i++) - if(((i + 1) % 16) != board[i]) return false; + if(((i + 1) % 16) != game_state.board[i]) return false; return true; } static void game_tick() { - switch(scene) { + switch(game_state.scene) { case ScenePlay: - tick_count++; - + game_state.tick_count++; + if (loaded_saving_ticks) + loaded_saving_ticks--; if(moving_cell.move_direction == DirectionNone && !key_stack_is_empty()) { set_moving_cell_by_direction(key_stack_pop()); if(moving_cell.move_direction == DirectionNone) { @@ -224,18 +240,19 @@ static void game_tick() { if(moving_cell.move_direction != DirectionNone) { moving_cell.move_ticks++; if(moving_cell.move_ticks == MOVE_TICKS) { - board[moving_cell.zero_index] = board[moving_cell.cell_index]; - board[moving_cell.cell_index] = 0; + game_state.board[moving_cell.zero_index] = + game_state.board[moving_cell.cell_index]; + game_state.board[moving_cell.cell_index] = 0; moving_cell.move_direction = DirectionNone; - move_count++; + game_state.move_count++; } if(is_board_solved()) { notification_message(notification, &sequence_double_vibro); - if(move_count < top_record || top_record == 0) { - top_record = move_count; - storage_top_record_save(); + if(game_state.move_count < game_state.top_record || game_state.top_record == 0) { + game_state.top_record = game_state.move_count; + storage_game_state_save(); } - scene = SceneWin; + game_state.scene = SceneWin; } } break; @@ -243,6 +260,32 @@ static void game_tick() { case SceneWin: if(!key_stack_is_empty()) game_init(); break; + + case ScenePopup: + if (!key_stack_is_empty()) { + switch(key_stack_pop()) + { + case DirectionDown: + popup_menu_selected_item++; + popup_menu_selected_item = popup_menu_selected_item % POPUP_MENU_ITEMS; + break; + case DirectionUp: + popup_menu_selected_item--; + popup_menu_selected_item = popup_menu_selected_item % POPUP_MENU_ITEMS; + break; + case DirectionNone: + if (popup_menu_selected_item == 0) { + game_state.scene = ScenePlay; + notification_message(notification, &sequence_single_vibro); + } + else if (popup_menu_selected_item == 1) { + notification_message(notification, &sequence_single_vibro); + game_init(); + } + break; + } + } + break; } } @@ -255,9 +298,9 @@ static void draw_cell(Canvas* canvas, uint8_t x, uint8_t y, uint8_t cell_number) static void board_draw(Canvas* canvas) { for(int i = 0; i < 16; i++) { - if(board[i]) { + if(game_state.board[i]) { if(moving_cell.move_direction == DirectionNone || moving_cell.cell_index != i) - draw_cell(canvas, (i % 4) * 20 + 7, (i / 4) * 16 + 1, board[i]); + draw_cell(canvas, (i % 4) * 20 + 7, (i / 4) * 16 + 1, game_state.board[i]); if(moving_cell.move_direction != DirectionNone && moving_cell.cell_index == i) { uint8_t from_x = (moving_cell.cell_index % 4) * 20 + 7; uint8_t from_y = (moving_cell.cell_index / 4) * 16 + 1; @@ -265,7 +308,7 @@ static void board_draw(Canvas* canvas) { uint8_t to_y = (moving_cell.zero_index / 4) * 16 + 1; int now_x = from_x + (to_x - from_x) * moving_cell.move_ticks / MOVE_TICKS; int now_y = from_y + (to_y - from_y) * moving_cell.move_ticks / MOVE_TICKS; - draw_cell(canvas, now_x, now_y, board[i]); + draw_cell(canvas, now_x, now_y, game_state.board[i]); } } } @@ -296,32 +339,40 @@ static void plate_draw( } static void info_draw(Canvas* canvas) { - plate_draw(canvas, 1, pic_top, top_record, true); - plate_draw(canvas, 22, pic_move, move_count, false); - plate_draw(canvas, 43, pic_time, tick_count / FPS, false); + plate_draw(canvas, 1, pic_top, game_state.top_record, true); + plate_draw(canvas, 22, pic_move, game_state.move_count, false); + plate_draw(canvas, 43, pic_time, game_state.tick_count / FPS, false); +} + +static void gray_screen(Canvas* const canvas) { + canvas_set_color(canvas, ColorWhite); + for(int x = 0; x < 128; x += 2) { + for(int y = 0; y < 64; y++) { + canvas_draw_dot(canvas, x + (y % 2 == 1 ? 0 : 1), y); + } + } } static void render_callback(Canvas* const canvas) { canvas_set_color(canvas, ColorWhite); canvas_draw_box(canvas, 0, 0, 128, 64); - if(scene == ScenePlay || scene == SceneWin) { + if(game_state.scene == ScenePlay || game_state.scene == SceneWin || game_state.scene == ScenePopup) { canvas_set_color(canvas, ColorBlack); board_draw(canvas); info_draw(canvas); - canvas_set_color(canvas, ColorWhite); - canvas_draw_box(canvas, 0, 0, 128, 64); - canvas_set_color(canvas, ColorBlack); - canvas_draw_str(canvas, 10, 10, "0123456789"); - } - if(scene == SceneWin) { - canvas_set_color(canvas, ColorWhite); - for(int x = 0; x < 128; x += 2) { - for(int y = 0; y < 64; y++) { - canvas_draw_dot(canvas, x + (y % 2 == 1 ? 0 : 1), y); - } + if (loaded_saving_ticks && game_state.scene != ScenePopup) { + canvas_set_color(canvas, ColorWhite); + canvas_draw_rbox(canvas, 20, 24, 88, 16, 4); + canvas_set_color(canvas, ColorBlack); + canvas_draw_rframe(canvas, 20, 24, 88, 16, 4); + canvas_draw_str_aligned(canvas, 64, 32, AlignCenter, AlignCenter, "Restore game ..."); } + } + + if(game_state.scene == SceneWin) { + gray_screen(canvas); canvas_draw_box(canvas, 7, 20, 114, 24); canvas_set_color(canvas, ColorBlack); canvas_draw_box(canvas, 8, 21, 112, 22); @@ -330,6 +381,23 @@ static void render_callback(Canvas* const canvas) { canvas_set_color(canvas, ColorBlack); canvas_draw_xbm(canvas, 14, 27, 100, 10, pic_puzzled); } + else if (game_state.scene == ScenePopup) { + gray_screen(canvas); + canvas_set_color(canvas, ColorWhite); + canvas_draw_rbox(canvas, 28, 16, 72, 32, 4); + canvas_set_color(canvas, ColorBlack); + canvas_draw_rframe(canvas, 28, 16, 72, 32, 4); + + for(int i=0; i < POPUP_MENU_ITEMS; i++) { + if ( i == popup_menu_selected_item) { + canvas_set_color(canvas, ColorBlack); + canvas_draw_box(canvas, 34, 20 + 12 * i, 60, 12); + } + + canvas_set_color(canvas, i == popup_menu_selected_item ? ColorWhite : ColorBlack); + canvas_draw_str_aligned(canvas, 64, 26 + 12 * i, AlignCenter, AlignCenter, popup_menu_strings[i]); + } + } } static void game_event_handler(GameEvent const event) { @@ -349,10 +417,21 @@ static void game_event_handler(GameEvent const event) { key_stack_push(DirectionLeft); break; case InputKeyOk: - key_stack_push(DirectionNone); + if (game_state.scene == ScenePlay) { + game_state.scene = ScenePopup; + key_stack_init(); + } + else + key_stack_push(DirectionNone); break; case InputKeyBack: - sandbox_loop_exit(); + if (game_state.scene == ScenePopup) { + game_state.scene = ScenePlay; + } + else { + storage_game_state_save(); + sandbox_loop_exit(); + } break; } } @@ -376,6 +455,17 @@ static void game_free() { int32_t game15_app() { game_alloc(); game_init(); + + loaded_saving_ticks = 0; + if(storage_game_state_load()) { + if (game_state.scene != ScenePlay) + game_init(); + else + loaded_saving_ticks = FPS; + } + else + game_init(); + sandbox_init( FPS, (SandboxRenderCallback)render_callback, (SandboxEventHandler)game_event_handler); sandbox_loop(); diff --git a/applications/plugins/game15/game15.fap b/applications/plugins/game15/game15.fap deleted file mode 100644 index 2348c1fbb4e6d63c7f385d31cd318a6b22aa567e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9580 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 diff --git a/applications/plugins/game15/images/Game15Popup.png b/applications/plugins/game15/images/Game15Popup.png new file mode 100644 index 0000000000000000000000000000000000000000..1df14729fc0e2bf59b9603ba5a0ee159ccadc6e3 GIT binary patch literal 2267 zcmZWre^3)=8vX*M@y8fw9hy50#9qfKQ_h2;9E}=4%N4O#VJZOwLU7b#;Y1(=G$D|) z?J(L?`~~Wv1d3D$7%Tzf5!qy}RuoAhN|FkMFmSS%01Lz_|L*ZPK_SXY><4p9FQr*^AZ}ehiLg?V$r3hE=udcN7n^T#lr#PX6^&avB8#rhG{L_5NXdYu2;vxGc>+uaZ<7qn zOG?L9fen}NJvRcd6AL9zw=2+_MR|}mNeCHHquJEQ0Wog_Q2PdmZnzK*gt|;FMC+BB zZn;hc15tw4((3jFnkvndEiE)af6_S8o5V=7V@BH5r6a7~2!rZ=`K>8|W~8`RldSWPNthz7uA504-&SX*FuPcT%i zRdf&j_($Uy=(m~FB#=w>)E!37wa&!WUq=GDfGd`xbLP)1A>a=yJ%kyemDiB)u9y0j zmdHJFhcHo5B1hks;tIU}qN*G@VG<0rF+?nlJXM*BRiUdzyFBF{revHnyC%|{4<=sf zW2Ke$d$Kl5QB)h$h0|2vQU&2evgR40tR}~SScWOna;K!ul`br2;B9`(qb9CL2QGAF zAtWoXR7JHfTP!K2)TAmWq?kUrvD|D&uOkLqmQmwqv@yp3_$UT%u3L=>pEoXz>my3D zet|Chm43Kr{oe#!RT%|)#WL6RkihhMk1`zvYid&IlikojC678c@vwhinbNz-Am2|~ z0(5id0@Fvbz~S`P>FOMzwM$Rv;e~Kf+mIY_etf-9X#L5OO35kq9gO7A2gMcT0bwVci}Xe4{jlUgyv7e4l165j3nl(7;}H|U!o zJzed};mqD=R%T)ojYyLC%BJ55d(Oj?H6x2V%SzB$8x@@YwP9{-*ce6*H8e-gjcWs6 zo;ll*Pk+}*6<*(06MhIe=OjH*@69YaJFbuCKrXKh7d)q_ zz1sWd{3ucTK5FB|1?MZ_qSdyEP>IAwQ;p9AR~u-mGPYoZZvGSvfNZjOtn(-f@%?l5 z(2=VI$FcI`hfgD;MTKLLZFOB=`?Dsx>TyF6Oa4IYJ+5Dr{2Uu}fC9FT+Z_kyi2z~b zbIl{^_iu0I0->4MxJ%)pM^EiLPG-g0`_1}HBvjmXm;yv5Pnn62(xU|(wqA)dN3}n| zMQNUO5MAC)Q$>8@%+vdxU2jCj2@YKlPlI6fG&S z7~x%Fh_ZY5h^9IC8ryexoe`aVY#=yX@9-WECD%}kJ2wmGHzR_dswaAM-yMUFFW36{ z>V};#wnei&l5LCJo+=CE%*Z0&=WLr5OyLqHnMMh*+78iGr?rZD`D&OQ()imqS7BZh z5Vib^AjK_&9Lg_tc#CL8`vAKhdO-l&#ElXu?$W{FV+^b!gqqupxtD|Cw>SJ9DotBX zcF7o12KsaALp~_h*CM6=Y3I!;4&iOy>uFJUdUQnYBA83D3GZoXnKrbNa6%8S0aw1T zA;uzsJAH!Q^(5tGDXGm;nszrNq0E3lK3twa+0m&qx=iY)lfo@{|3m+m$J1xpT*v$8 zzwf0dN05OCfL-o^Q_)PiVLSg!6LH4jU{2!WMoe_XM0?(Wf$Q%+~On5Hk_s_zHjXEfF3agM-R@i0#6?upOWGKC0zBu z>8viq)ru$P@GQPH)-qr>rr*#QIyyN+98E3(6`v@6-Gm@Tsf)A_W><9R0RFcE;{LKH KM!GZOr_%0p2jT8RpRf&qLG3|7O#kd%P<=;%;^ z7Evn(NEL>sC@2(!1Z-6ztr8UyC6b3j%}PQNP?BvDNE!+qFZ|fKyR&=F{l0U)^WDwa z8Ae_>|I_&ZfQ6x3H|+*smi-n3oF9p;8!rBhcbT*En)2)7pF)xYyF==)=IuMO@l*e{OJ@D*oSLM+jKd*Ccg75QMYeTr zaH%3F3xLdX_7q*ZJw%w^-=R04CZ{ znTOJ8LxemQZB?zvXDBnUXbA{jiJ9CZsmEb!RbQQPeJM}h61PLj=DF&Sg6Ba5!43rR z?@@V@$FB$8I@elkd;@nZ9-=u4oGCee~zL z7r3i^NTRBZS}w4iB9>VWe6J`cIL|$G@_)z)SfwQ6UERR7f^Uo~kkbV}avnfgnuai- zyqdk@2Z)RJASTzy9z0SZq1mhL+Xb_WQ5iuK_9;ZAn#l&9o+{N$epp&+(8u1P(5EG1%bJCjf6jNFb@ zAc*!&$Q)<`@f}A{e48Uuc^PNTQf7ZFQ+&m5?4Ibs#u0f^W0ZBr%P5)c+cIP;#J)Fb z^Rba|%N2gpFZo2TWS;;4?BU*a8L8hPpavCEUT1q=d^*O7)5fLi7hi*FVF#}Wm3ivc z*BC_aVl|#?A@Pz?;BBPXdILA#_m8w-NaJS8NCnkAYjO-^NrV}RR>{$A`FDEe3i zm=}q9Mx~u6$}|=n1UDiStE^z&@zC-$Y=fY>Oy5292%q*HLu@}jUbdz+ z%@Zom9_!Nkbiap%99Oa4-#v?F=sP3Uw;PI@OAGgU=))L?kTwdhRaZ)y(f(%}($()N zB)xk}wThG4f#AEvCU0vHtm8j9An8Jfs*Y(^m{^pak*h(E?y29_Wpm*PD|bvz)wUd> z9IW`NySLZKpWSIVtO)PrcUxy^%^+8ZO&{@p6?5^jmp)wYLe zp5_km|2psw$=BmE@YHEiT@hRqGjy}#TysQ_5GcB!A@Gnnyz1+rF-1%L_S0G@x@QSm z+v6DY`gCOtmy9|nkIOVxHz5Pk@(m8CwH$Hie>gp%yY_WBwM_9ns#4=-hwHWduNKi& zAIkKRxvWtVrB-;)EPJlcRGT9+St>#;kM(>zNjNOGss@14Wy;N%C$LI-C2c;nf3*8e zzOc9x+M#I>bHDC*jiu)T1k!F~d#MB2;o~O~DdpVmkZU@WkEC`V@Ldk9!?AiV->ag0hRvw#+QW;u` z-~Q&n5k;a^_l`x&uOx{uU^xK0rwmxuDC%19pHcV>^^TC!)t0hD+hI9K`B)Ht*l_Q3 zL34&Hb%t@E#Aqq;pr${&+Yq3LdAahsCmgy>?pS|!v5-#dytrPDCzKSb8~UStI&#pD z2xgoJLIF0uF`rFaXwB`Bq}F%7JRPvChXkAldfzml@fEHX0ts!h_P zZ!?q35mjtL4peF44ac8{bx$jsJGtrd)?{i`{f8&dh6&jwE@9&Kza3vQ)#-9of4Fop=sqpgMTI96j$<3RQTtCJWJ6MH_o@P zD_jWxC+o>Bhwa7-VjvTv@pwi0^a7*xES`?yIx21;u<_<=FgGRmwwJ*QqE&~yiL`$r zfGa