77#include <furi_hal_resources.h>
88#include <furi_hal_gpio.h>
99#include <dolphin/dolphin.h>
10+ #include "tetris_icons.h"
1011
1112#define BORDER_OFFSET 1
1213#define MARGIN_OFFSET 3
1314#define BLOCK_HEIGHT 6
1415#define BLOCK_WIDTH 6
1516
16- #define FIELD_WIDTH 11
17- #define FIELD_HEIGHT 24
17+ #define FIELD_WIDTH 10
18+ #define FIELD_HEIGHT 20
19+
20+ #define FIELD_X_OFFSET 3
21+ #define FIELD_Y_OFFSET 20
1822
1923#define MAX_FALL_SPEED 500
2024#define MIN_FALL_SPEED 100
@@ -62,10 +66,12 @@ static Piece shapes[] = {
6266 {.p = {{5 , 1 }, {5 , 0 }, {6 , 0 }, {6 , 1 }}, .rotIdx = 0 , .offsetType = OffsetTypeO } // O
6367};
6468
65- typedef enum { GameStatePlaying , GameStateGameOver } GameState ;
69+ typedef enum { GameStatePlaying , GameStateGameOver , GameStatePaused } GameState ;
6670
6771typedef struct {
6872 bool playField [FIELD_HEIGHT ][FIELD_WIDTH ];
73+ bool bag [7 ];
74+ uint8_t next_id ;
6975 Piece currPiece ;
7076 uint16_t numLines ;
7177 uint16_t fallSpeed ;
@@ -85,61 +91,107 @@ typedef struct {
8591} TetrisEvent ;
8692
8793static void tetris_game_draw_border (Canvas * const canvas ) {
88- canvas_draw_line (canvas , 0 , 0 , 0 , 127 );
89- canvas_draw_line (canvas , 0 , 127 , 63 , 127 );
90- canvas_draw_line (canvas , 63 , 127 , 63 , 0 );
94+ canvas_draw_line (
95+ canvas ,
96+ FIELD_X_OFFSET ,
97+ FIELD_Y_OFFSET ,
98+ FIELD_X_OFFSET ,
99+ FIELD_Y_OFFSET + FIELD_HEIGHT * 5 + 7 );
100+ canvas_draw_line (
101+ canvas ,
102+ FIELD_X_OFFSET ,
103+ FIELD_Y_OFFSET + FIELD_HEIGHT * 5 + 7 ,
104+ FIELD_X_OFFSET + FIELD_WIDTH * 5 + 8 ,
105+ FIELD_Y_OFFSET + FIELD_HEIGHT * 5 + 7 );
106+ canvas_draw_line (
107+ canvas ,
108+ FIELD_X_OFFSET + FIELD_WIDTH * 5 + 8 ,
109+ FIELD_Y_OFFSET + FIELD_HEIGHT * 5 + 7 ,
110+ FIELD_X_OFFSET + FIELD_WIDTH * 5 + 8 ,
111+ FIELD_Y_OFFSET );
112+
113+ canvas_draw_line (
114+ canvas ,
115+ FIELD_X_OFFSET + 2 ,
116+ FIELD_Y_OFFSET + 0 ,
117+ FIELD_X_OFFSET + 2 ,
118+ FIELD_Y_OFFSET + FIELD_HEIGHT * 5 + 5 );
119+ canvas_draw_line (
120+ canvas ,
121+ FIELD_X_OFFSET + 2 ,
122+ FIELD_Y_OFFSET + FIELD_HEIGHT * 5 + 5 ,
123+ FIELD_X_OFFSET + FIELD_WIDTH * 5 + 6 ,
124+ FIELD_Y_OFFSET + FIELD_HEIGHT * 5 + 5 );
125+ canvas_draw_line (
126+ canvas ,
127+ FIELD_X_OFFSET + FIELD_WIDTH * 5 + 6 ,
128+ FIELD_Y_OFFSET + FIELD_HEIGHT * 5 + 5 ,
129+ FIELD_X_OFFSET + FIELD_WIDTH * 5 + 6 ,
130+ FIELD_Y_OFFSET );
131+ }
91132
92- canvas_draw_line (canvas , 2 , 0 , 2 , 125 );
93- canvas_draw_line (canvas , 2 , 125 , 61 , 125 );
94- canvas_draw_line (canvas , 61 , 125 , 61 , 0 );
133+ static void tetris_game_draw_block (Canvas * const canvas , uint16_t xOffset , uint16_t yOffset ) {
134+ canvas_draw_rframe (
135+ canvas ,
136+ BORDER_OFFSET + MARGIN_OFFSET + xOffset ,
137+ BORDER_OFFSET + MARGIN_OFFSET + yOffset - 1 ,
138+ BLOCK_WIDTH ,
139+ BLOCK_HEIGHT ,
140+ 1 );
141+ canvas_draw_dot (
142+ canvas ,
143+ BORDER_OFFSET + MARGIN_OFFSET + xOffset + 2 ,
144+ BORDER_OFFSET + MARGIN_OFFSET + yOffset + 1 );
145+ canvas_draw_dot (
146+ canvas ,
147+ BORDER_OFFSET + MARGIN_OFFSET + xOffset + 3 ,
148+ BORDER_OFFSET + MARGIN_OFFSET + yOffset + 1 );
149+ canvas_draw_dot (
150+ canvas ,
151+ BORDER_OFFSET + MARGIN_OFFSET + xOffset + 2 ,
152+ BORDER_OFFSET + MARGIN_OFFSET + yOffset + 2 );
95153}
96154
97155static void tetris_game_draw_playfield (Canvas * const canvas , const TetrisState * tetris_state ) {
98- // Playfield: 11 x 24
99-
100156 for (int y = 0 ; y < FIELD_HEIGHT ; y ++ ) {
101157 for (int x = 0 ; x < FIELD_WIDTH ; x ++ ) {
102158 if (tetris_state -> playField [y ][x ]) {
103- uint16_t xOffset = x * 5 ;
104- uint16_t yOffset = y * 5 ;
105-
106- canvas_draw_rframe (
107- canvas ,
108- BORDER_OFFSET + MARGIN_OFFSET + xOffset ,
109- BORDER_OFFSET + MARGIN_OFFSET + yOffset - 1 ,
110- BLOCK_WIDTH ,
111- BLOCK_HEIGHT ,
112- 1 );
113- canvas_draw_dot (
159+ tetris_game_draw_block (
114160 canvas ,
115- BORDER_OFFSET + MARGIN_OFFSET + xOffset + 2 ,
116- BORDER_OFFSET + MARGIN_OFFSET + yOffset + 1 );
117- canvas_draw_dot (
118- canvas ,
119- BORDER_OFFSET + MARGIN_OFFSET + xOffset + 3 ,
120- BORDER_OFFSET + MARGIN_OFFSET + yOffset + 1 );
121- canvas_draw_dot (
122- canvas ,
123- BORDER_OFFSET + MARGIN_OFFSET + xOffset + 2 ,
124- BORDER_OFFSET + MARGIN_OFFSET + yOffset + 2 );
161+ FIELD_X_OFFSET + x * (BLOCK_WIDTH - 1 ),
162+ FIELD_Y_OFFSET + y * (BLOCK_HEIGHT - 1 ));
125163 }
126164 }
127165 }
128166}
129167
168+ static void tetris_game_draw_next_piece (Canvas * const canvas , const TetrisState * tetris_state ) {
169+ Piece * next_piece = & shapes [tetris_state -> next_id ];
170+ for (int i = 0 ; i < 4 ; i ++ ) {
171+ uint8_t x = next_piece -> p [i ].x ;
172+ uint8_t y = next_piece -> p [i ].y ;
173+
174+ tetris_game_draw_block (
175+ canvas ,
176+ FIELD_X_OFFSET + x * (BLOCK_WIDTH - 1 ) - (BLOCK_WIDTH * 4 ),
177+ y * (BLOCK_HEIGHT - 1 ));
178+ }
179+ }
180+
130181static void tetris_game_render_callback (Canvas * const canvas , void * ctx ) {
131182 furi_assert (ctx );
132183 const TetrisState * tetris_state = ctx ;
133184 furi_mutex_acquire (tetris_state -> mutex , FuriWaitForever );
134185
135186 tetris_game_draw_border (canvas );
136187 tetris_game_draw_playfield (canvas , tetris_state );
188+ tetris_game_draw_next_piece (canvas , tetris_state );
137189
138190 // Show score on the game field
139- if (tetris_state -> gameState == GameStatePlaying ) {
191+ if (tetris_state -> gameState == GameStatePlaying || tetris_state -> gameState == GameStatePaused ) {
140192 char buffer2 [6 ];
141193 snprintf (buffer2 , sizeof (buffer2 ), "%u" , tetris_state -> numLines );
142- canvas_draw_str_aligned (canvas , 58 , 10 , AlignRight , AlignBottom , buffer2 );
194+ canvas_draw_str_aligned (canvas , 62 , 10 , AlignRight , AlignBottom , buffer2 );
143195 }
144196
145197 if (tetris_state -> gameState == GameStateGameOver ) {
@@ -158,6 +210,22 @@ static void tetris_game_render_callback(Canvas* const canvas, void* ctx) {
158210 canvas_set_font (canvas , FontSecondary );
159211 canvas_draw_str_aligned (canvas , 32 , 73 , AlignCenter , AlignBottom , buffer );
160212 }
213+
214+ if (tetris_state -> gameState == GameStatePaused ) {
215+ // 128 x 64
216+ canvas_set_color (canvas , ColorWhite );
217+ canvas_draw_box (canvas , 1 , 52 , 62 , 24 );
218+
219+ canvas_set_color (canvas , ColorBlack );
220+ canvas_draw_frame (canvas , 1 , 52 , 62 , 24 );
221+
222+ canvas_set_font (canvas , FontPrimary );
223+ canvas_draw_str (canvas , 4 , 63 , "Paused" );
224+
225+ canvas_set_font (canvas , FontSecondary );
226+ canvas_draw_str (canvas , 4 , 73 , "hold to quit" );
227+ canvas_draw_icon (canvas , 22 , 66 , & I_Pin_back_arrow_10x8 );
228+ }
161229 furi_mutex_release (tetris_state -> mutex );
162230}
163231
@@ -168,13 +236,42 @@ static void tetris_game_input_callback(InputEvent* input_event, FuriMessageQueue
168236 furi_message_queue_put (event_queue , & event , FuriWaitForever );
169237}
170238
239+ static uint8_t tetris_game_get_next_piece (TetrisState * tetris_state ) {
240+ bool full = true;
241+ for (int i = 0 ; i < 7 ; i ++ ) {
242+ if (!tetris_state -> bag [i ]) {
243+ full = false;
244+ break ;
245+ }
246+ }
247+ if (full == true) {
248+ for (int i = 0 ; i < 7 ; i ++ ) {
249+ tetris_state -> bag [i ] = false;
250+ }
251+ }
252+
253+ int next_piece = rand () % 7 ;
254+ while (tetris_state -> bag [next_piece ]) {
255+ next_piece = rand () % 7 ;
256+ }
257+ tetris_state -> bag [next_piece ] = true;
258+ int result = tetris_state -> next_id ;
259+ tetris_state -> next_id = next_piece ;
260+
261+ return result ;
262+ }
263+
171264static void tetris_game_init_state (TetrisState * tetris_state ) {
172265 tetris_state -> gameState = GameStatePlaying ;
173266 tetris_state -> numLines = 0 ;
174267 tetris_state -> fallSpeed = MAX_FALL_SPEED ;
175268 memset (tetris_state -> playField , 0 , sizeof (tetris_state -> playField ));
269+ memset (tetris_state -> bag , 0 , sizeof (tetris_state -> bag ));
176270
177- memcpy (& tetris_state -> currPiece , & shapes [rand () % 7 ], sizeof (tetris_state -> currPiece ));
271+ // init next_piece display
272+ tetris_game_get_next_piece (tetris_state );
273+ int next_piece_id = tetris_game_get_next_piece (tetris_state );
274+ memcpy (& tetris_state -> currPiece , & shapes [next_piece_id ], sizeof (tetris_state -> currPiece ));
178275
179276 furi_timer_start (tetris_state -> timer , tetris_state -> fallSpeed );
180277}
@@ -294,7 +391,8 @@ static void tetris_game_update_timer_callback(FuriMessageQueue* event_queue) {
294391
295392static void
296393 tetris_game_process_step (TetrisState * tetris_state , Piece * newPiece , bool wasDownMove ) {
297- if (tetris_state -> gameState == GameStateGameOver ) return ;
394+ if (tetris_state -> gameState == GameStateGameOver || tetris_state -> gameState == GameStatePaused )
395+ return ;
298396
299397 tetris_game_remove_curr_piece (tetris_state );
300398
@@ -335,7 +433,8 @@ static void
335433 }
336434
337435 // Check for game over
338- Piece * spawnedPiece = & shapes [rand () % 7 ];
436+ int next_piece_id = tetris_game_get_next_piece (tetris_state );
437+ Piece * spawnedPiece = & shapes [next_piece_id ];
339438 if (!tetris_game_is_valid_pos (tetris_state , spawnedPiece -> p )) {
340439 tetris_state -> gameState = GameStateGameOver ;
341440 } else {
@@ -439,7 +538,21 @@ int32_t tetris_game_app() {
439538 }
440539 break ;
441540 case InputKeyBack :
442- processing = false;
541+ if (event .input .type == InputTypeLong ) {
542+ processing = false;
543+ } else if (event .input .type == InputTypePress ) {
544+ switch (tetris_state -> gameState ) {
545+ case GameStatePaused :
546+ tetris_state -> gameState = GameStatePlaying ;
547+ break ;
548+ case GameStatePlaying :
549+ tetris_state -> gameState = GameStatePaused ;
550+ break ;
551+
552+ default :
553+ break ;
554+ }
555+ }
443556 break ;
444557 default :
445558 break ;
0 commit comments