7
7
#include <furi_hal_resources.h>
8
8
#include <furi_hal_gpio.h>
9
9
#include <dolphin/dolphin.h>
10
+ #include "tetris_icons.h"
10
11
11
12
#define BORDER_OFFSET 1
12
13
#define MARGIN_OFFSET 3
13
14
#define BLOCK_HEIGHT 6
14
15
#define BLOCK_WIDTH 6
15
16
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
18
22
19
23
#define MAX_FALL_SPEED 500
20
24
#define MIN_FALL_SPEED 100
@@ -62,10 +66,12 @@ static Piece shapes[] = {
62
66
{.p = {{5 , 1 }, {5 , 0 }, {6 , 0 }, {6 , 1 }}, .rotIdx = 0 , .offsetType = OffsetTypeO } // O
63
67
};
64
68
65
- typedef enum { GameStatePlaying , GameStateGameOver } GameState ;
69
+ typedef enum { GameStatePlaying , GameStateGameOver , GameStatePaused } GameState ;
66
70
67
71
typedef struct {
68
72
bool playField [FIELD_HEIGHT ][FIELD_WIDTH ];
73
+ bool bag [7 ];
74
+ uint8_t next_id ;
69
75
Piece currPiece ;
70
76
uint16_t numLines ;
71
77
uint16_t fallSpeed ;
@@ -85,61 +91,107 @@ typedef struct {
85
91
} TetrisEvent ;
86
92
87
93
static 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
+ }
91
132
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 );
95
153
}
96
154
97
155
static void tetris_game_draw_playfield (Canvas * const canvas , const TetrisState * tetris_state ) {
98
- // Playfield: 11 x 24
99
-
100
156
for (int y = 0 ; y < FIELD_HEIGHT ; y ++ ) {
101
157
for (int x = 0 ; x < FIELD_WIDTH ; x ++ ) {
102
158
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 (
114
160
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 ));
125
163
}
126
164
}
127
165
}
128
166
}
129
167
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
+
130
181
static void tetris_game_render_callback (Canvas * const canvas , void * ctx ) {
131
182
furi_assert (ctx );
132
183
const TetrisState * tetris_state = ctx ;
133
184
furi_mutex_acquire (tetris_state -> mutex , FuriWaitForever );
134
185
135
186
tetris_game_draw_border (canvas );
136
187
tetris_game_draw_playfield (canvas , tetris_state );
188
+ tetris_game_draw_next_piece (canvas , tetris_state );
137
189
138
190
// Show score on the game field
139
- if (tetris_state -> gameState == GameStatePlaying ) {
191
+ if (tetris_state -> gameState == GameStatePlaying || tetris_state -> gameState == GameStatePaused ) {
140
192
char buffer2 [6 ];
141
193
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 );
143
195
}
144
196
145
197
if (tetris_state -> gameState == GameStateGameOver ) {
@@ -158,6 +210,22 @@ static void tetris_game_render_callback(Canvas* const canvas, void* ctx) {
158
210
canvas_set_font (canvas , FontSecondary );
159
211
canvas_draw_str_aligned (canvas , 32 , 73 , AlignCenter , AlignBottom , buffer );
160
212
}
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
+ }
161
229
furi_mutex_release (tetris_state -> mutex );
162
230
}
163
231
@@ -168,13 +236,42 @@ static void tetris_game_input_callback(InputEvent* input_event, FuriMessageQueue
168
236
furi_message_queue_put (event_queue , & event , FuriWaitForever );
169
237
}
170
238
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
+
171
264
static void tetris_game_init_state (TetrisState * tetris_state ) {
172
265
tetris_state -> gameState = GameStatePlaying ;
173
266
tetris_state -> numLines = 0 ;
174
267
tetris_state -> fallSpeed = MAX_FALL_SPEED ;
175
268
memset (tetris_state -> playField , 0 , sizeof (tetris_state -> playField ));
269
+ memset (tetris_state -> bag , 0 , sizeof (tetris_state -> bag ));
176
270
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 ));
178
275
179
276
furi_timer_start (tetris_state -> timer , tetris_state -> fallSpeed );
180
277
}
@@ -294,7 +391,8 @@ static void tetris_game_update_timer_callback(FuriMessageQueue* event_queue) {
294
391
295
392
static void
296
393
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 ;
298
396
299
397
tetris_game_remove_curr_piece (tetris_state );
300
398
@@ -335,7 +433,8 @@ static void
335
433
}
336
434
337
435
// 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 ];
339
438
if (!tetris_game_is_valid_pos (tetris_state , spawnedPiece -> p )) {
340
439
tetris_state -> gameState = GameStateGameOver ;
341
440
} else {
@@ -439,7 +538,21 @@ int32_t tetris_game_app() {
439
538
}
440
539
break ;
441
540
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
+ }
443
556
break ;
444
557
default :
445
558
break ;
0 commit comments