diff options
Diffstat (limited to 'apps')
-rw-r--r-- | apps/plugins/2048.c | 916 | ||||
-rw-r--r-- | apps/plugins/CATEGORIES | 1 | ||||
-rw-r--r-- | apps/plugins/SOURCES | 2 | ||||
-rw-r--r-- | apps/plugins/bitmaps/native/SOURCES | 22 | ||||
-rw-r--r-- | apps/plugins/bitmaps/native/_2048_background.103x103x24.bmp | bin | 0 -> 32258 bytes | |||
-rw-r--r-- | apps/plugins/bitmaps/native/_2048_background.121x121x24.bmp | bin | 0 -> 44166 bytes | |||
-rw-r--r-- | apps/plugins/bitmaps/native/_2048_background.168x168x24.bmp | bin | 0 -> 84794 bytes | |||
-rw-r--r-- | apps/plugins/bitmaps/native/_2048_background.224x224x24.bmp | bin | 0 -> 150650 bytes | |||
-rw-r--r-- | apps/plugins/bitmaps/native/_2048_background.56x56x24.bmp | bin | 0 -> 9530 bytes | |||
-rw-r--r-- | apps/plugins/bitmaps/native/_2048_tiles.12x12x24.bmp | bin | 0 -> 7898 bytes | |||
-rw-r--r-- | apps/plugins/bitmaps/native/_2048_tiles.22x22x24.bmp | bin | 0 -> 26258 bytes | |||
-rw-r--r-- | apps/plugins/bitmaps/native/_2048_tiles.26x26x24.bmp | bin | 0 -> 36626 bytes | |||
-rw-r--r-- | apps/plugins/bitmaps/native/_2048_tiles.36x36x24.bmp | bin | 0 -> 70106 bytes | |||
-rw-r--r-- | apps/plugins/bitmaps/native/_2048_tiles.48x48x24.bmp | bin | 0 -> 124538 bytes |
14 files changed, 941 insertions, 0 deletions
diff --git a/apps/plugins/2048.c b/apps/plugins/2048.c new file mode 100644 index 0000000000..9a74b0ff38 --- /dev/null +++ b/apps/plugins/2048.c | |||
@@ -0,0 +1,916 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2014 Franklin Wei | ||
11 | * | ||
12 | * Clone of 2048 by Gabriele Cirulli | ||
13 | * | ||
14 | * Thanks to [Saint], saratoga, and gevaerts for answering all my n00b | ||
15 | * questions :) | ||
16 | * | ||
17 | * This program is free software; you can redistribute it and/or | ||
18 | * modify it under the terms of the GNU General Public License | ||
19 | * as published by the Free Software Foundation; either version 2 | ||
20 | * of the License, or (at your option) any later version. | ||
21 | * | ||
22 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
23 | * KIND, either express or implied. | ||
24 | * | ||
25 | ***************************************************************************/ | ||
26 | |||
27 | /* TODO | ||
28 | * Sounds! | ||
29 | * Better animations! | ||
30 | */ | ||
31 | |||
32 | /* includes */ | ||
33 | |||
34 | #include <plugin.h> | ||
35 | |||
36 | #include "lib/display_text.h" | ||
37 | |||
38 | #include "lib/helper.h" | ||
39 | #include "lib/highscore.h" | ||
40 | #include "lib/playback_control.h" | ||
41 | #include "lib/pluginlib_actions.h" | ||
42 | #include "lib/pluginlib_exit.h" | ||
43 | |||
44 | #ifdef HAVE_LCD_BITMAP | ||
45 | #include "pluginbitmaps/_2048_background.h" | ||
46 | #include "pluginbitmaps/_2048_tiles.h" | ||
47 | #endif | ||
48 | |||
49 | /* defines */ | ||
50 | |||
51 | #define ANIM_SLEEPTIME (HZ/20) | ||
52 | #define GRID_SIZE 4 | ||
53 | #define HISCORES_FILE PLUGIN_GAMES_DATA_DIR "/2048.score" | ||
54 | #define NUM_SCORES 5 | ||
55 | #define NUM_STARTING_TILES 2 | ||
56 | #define RESUME_FILE PLUGIN_GAMES_DATA_DIR "/2048.save" | ||
57 | #define WHAT_FONT FONT_UI | ||
58 | #define SPACES (GRID_SIZE*GRID_SIZE) | ||
59 | #define MIN_SPACE (BMPHEIGHT__2048_tiles*0.134) /* space between tiles */ | ||
60 | #define MAX_UNDOS 64 | ||
61 | #define VERT_SPACING 4 | ||
62 | #define WINNING_TILE 2048 | ||
63 | |||
64 | /* screen-specific configuration */ | ||
65 | |||
66 | #if LCD_WIDTH<LCD_HEIGHT | ||
67 | /* tall screens */ | ||
68 | #define TITLE_X 0 | ||
69 | #define TITLE_Y 0 | ||
70 | #define BASE_Y (BMPHEIGHT__2048_tiles*1.5) | ||
71 | #define BASE_X (BMPHEIGHT__2048_tiles*.5-MIN_SPACE) | ||
72 | #define SCORE_X 0 | ||
73 | #define SCORE_Y (max_numeral_height) | ||
74 | #define BEST_SCORE_X 0 | ||
75 | #define BEST_SCORE_Y (2*max_numeral_height) | ||
76 | #else | ||
77 | /* wide screens or square screens*/ | ||
78 | #define TITLE_X 0 | ||
79 | #define TITLE_Y 0 | ||
80 | #define BASE_X (LCD_WIDTH-(GRID_SIZE*BMPHEIGHT__2048_tiles)-(((GRID_SIZE+1)*MIN_SPACE))) | ||
81 | #define BASE_Y (BMPHEIGHT__2048_tiles*.5-MIN_SPACE) | ||
82 | #define SCORE_X 0 | ||
83 | #define SCORE_Y (max_numeral_height) | ||
84 | #define BEST_SCORE_X 0 | ||
85 | #define BEST_SCORE_Y (2*max_numeral_height) | ||
86 | #endif | ||
87 | |||
88 | #define BACKGROUND_X (BASE_X-MIN_SPACE) | ||
89 | #define BACKGROUND_Y (BASE_Y-MIN_SPACE) | ||
90 | |||
91 | /* key mappings */ | ||
92 | |||
93 | #define KEY_UP PLA_UP | ||
94 | #define KEY_DOWN PLA_DOWN | ||
95 | #define KEY_LEFT PLA_LEFT | ||
96 | #define KEY_RIGHT PLA_RIGHT | ||
97 | #define KEY_EXIT PLA_CANCEL | ||
98 | #define KEY_UNDO PLA_SELECT | ||
99 | |||
100 | /* colors */ | ||
101 | |||
102 | #define BACKGROUND (LCD_RGBPACK(0xfa, 0xf8, 0xef)) | ||
103 | #define BOARD_BACKGROUND (LCD_RGBPACK(0xbb, 0xad, 0xa0)) | ||
104 | #define TEXT_COLOR (LCD_RGBPACK(0x77, 0x6e, 0x65)) | ||
105 | |||
106 | static const struct button_mapping *plugin_contexts[] = { pla_main_ctx }; | ||
107 | |||
108 | /* game data */ | ||
109 | struct game_ctx_t { | ||
110 | int grid[GRID_SIZE][GRID_SIZE]; | ||
111 | int score; | ||
112 | int cksum; /* sum of grid, XORed by score */ | ||
113 | bool already_won; | ||
114 | }; | ||
115 | |||
116 | static struct game_ctx_t ctx_data; | ||
117 | /* use a pointer to make save/load easier */ | ||
118 | static struct game_ctx_t *ctx=&ctx_data; | ||
119 | |||
120 | /* temporary data */ | ||
121 | static bool merged_grid[GRID_SIZE][GRID_SIZE]; | ||
122 | static int old_grid[GRID_SIZE][GRID_SIZE]; | ||
123 | static int max_numeral_height=-1; | ||
124 | static bool loaded=false; | ||
125 | |||
126 | /* first init_game will set this, when it is exceeded, it will be updated in the slide functions */ | ||
127 | static int best_score; | ||
128 | |||
129 | static bool abnormal_exit=true; | ||
130 | static struct highscore highscores[NUM_SCORES]; | ||
131 | |||
132 | /* returns a random int between min and max */ | ||
133 | static inline int rand_range(int min, int max) | ||
134 | { | ||
135 | return rb->rand()%(max-min+1)+min; | ||
136 | } | ||
137 | |||
138 | /* prepares to exit */ | ||
139 | static void cleanup(void) | ||
140 | { | ||
141 | backlight_use_settings(); | ||
142 | } | ||
143 | |||
144 | /* returns 2 or 4 */ | ||
145 | static inline int rand_2_or_4(void) | ||
146 | { | ||
147 | /* 1 in 10 chance of a four */ | ||
148 | if(rb->rand()%10==0) | ||
149 | return 4; | ||
150 | else | ||
151 | return 2; | ||
152 | } | ||
153 | |||
154 | /* display the help text */ | ||
155 | static bool do_help(void) | ||
156 | { | ||
157 | #ifdef HAVE_LCD_COLOR | ||
158 | rb->lcd_set_foreground(LCD_WHITE); | ||
159 | rb->lcd_set_background(LCD_BLACK); | ||
160 | #endif | ||
161 | rb->lcd_setfont(FONT_UI); | ||
162 | char* help_text[]= {"2048", "", "Aim", | ||
163 | "", "Join", "the", "numbers", "to", "get", "to", "the", "2048", "tile!", "", "", | ||
164 | "How", "to", "Play", "", | ||
165 | "", "Use", "the", "directional", "keys", "to", "move", "the", "tiles.", "When", | ||
166 | "two", "tiles", "with", "the", "same", "number", "touch,", "they", "merge", "into", "one!"}; | ||
167 | struct style_text style[]={ | ||
168 | {0, TEXT_CENTER|TEXT_UNDERLINE}, | ||
169 | {2, C_RED}, | ||
170 | {15, C_RED}, {16, C_RED}, {17,C_RED}, | ||
171 | LAST_STYLE_ITEM | ||
172 | }; | ||
173 | return display_text(ARRAYLEN(help_text), help_text, style,NULL,true); | ||
174 | } | ||
175 | |||
176 | /*** the logic for sliding ***/ | ||
177 | |||
178 | /* this is the helper function that does the actual tile moving */ | ||
179 | |||
180 | static inline void slide_internal(int startx, int starty, | ||
181 | int stopx, int stopy, | ||
182 | int dx, int dy, | ||
183 | int lookx, int looky) | ||
184 | { | ||
185 | int best_score_before=best_score; | ||
186 | for(int y=starty;y!=stopy;y+=dy) | ||
187 | { | ||
188 | for(int x=startx;x!=stopx;x+=dx) | ||
189 | { | ||
190 | if(ctx->grid[x+lookx][y+looky]==ctx->grid[x][y] && ctx->grid[x][y] && !merged_grid[x+lookx][y+looky] && !merged_grid[x][y]) /* Slide into */ | ||
191 | { | ||
192 | /* Each merged tile cannot be merged again */ | ||
193 | merged_grid[x+lookx][y+looky]=true; | ||
194 | ctx->grid[x+lookx][y+looky]=2*ctx->grid[x][y]; | ||
195 | ctx->score+=ctx->grid[x+lookx][y+looky]; | ||
196 | ctx->grid[x][y]=0; | ||
197 | } | ||
198 | else if(ctx->grid[x+lookx][y+looky]==0) /* Empty! */ | ||
199 | { | ||
200 | ctx->grid[x+lookx][y+looky]=ctx->grid[x][y]; | ||
201 | ctx->grid[x][y]=0; | ||
202 | } | ||
203 | } | ||
204 | } | ||
205 | if(ctx->score>best_score_before) | ||
206 | best_score=ctx->score; | ||
207 | } | ||
208 | |||
209 | /* these functions move each tile 1 space in the direction specified via calls to slide_internal */ | ||
210 | |||
211 | /* Up | ||
212 | 0 | ||
213 | 1 ^ ^ ^ ^ | ||
214 | 2 ^ ^ ^ ^ | ||
215 | 3 ^ ^ ^ ^ | ||
216 | 0 1 2 3 | ||
217 | */ | ||
218 | static void up(void) | ||
219 | { | ||
220 | slide_internal(0, 1, /* start values */ | ||
221 | GRID_SIZE, GRID_SIZE, /* stop values */ | ||
222 | 1, 1, /* delta values */ | ||
223 | 0, -1); /* lookahead values */ | ||
224 | } | ||
225 | /* Down | ||
226 | 0 v v v v | ||
227 | 1 v v v v | ||
228 | 2 v v v v | ||
229 | 3 | ||
230 | 0 1 2 3 | ||
231 | */ | ||
232 | static void down(void) | ||
233 | { | ||
234 | slide_internal(0, GRID_SIZE-2, | ||
235 | GRID_SIZE, -1, | ||
236 | 1, -1, | ||
237 | 0, 1); | ||
238 | } | ||
239 | /* Left | ||
240 | 0 < < < | ||
241 | 1 < < < | ||
242 | 2 < < < | ||
243 | 3 < < < | ||
244 | 0 1 2 3 | ||
245 | */ | ||
246 | static void left(void) | ||
247 | { | ||
248 | slide_internal(1, 0, | ||
249 | GRID_SIZE, GRID_SIZE, | ||
250 | 1, 1, | ||
251 | -1, 0); | ||
252 | } | ||
253 | /* Right | ||
254 | 0 > > > | ||
255 | 1 > > > | ||
256 | 2 > > > | ||
257 | 3 > > > | ||
258 | 0 1 2 3 | ||
259 | */ | ||
260 | static void right(void) | ||
261 | { | ||
262 | slide_internal(GRID_SIZE-2, 0, /* start */ | ||
263 | -1, GRID_SIZE, /* stop */ | ||
264 | -1, 1, /* delta */ | ||
265 | 1, 0); /* lookahead */ | ||
266 | } | ||
267 | |||
268 | /* slightly modified version of base 2 log, returns 1 when given zero, and log2(n)+1 for anything else */ | ||
269 | |||
270 | static inline int ilog2(int n) | ||
271 | { | ||
272 | if(n==0) | ||
273 | return 1; | ||
274 | int log=0; | ||
275 | while(n>1) | ||
276 | { | ||
277 | n>>=1; | ||
278 | ++log; | ||
279 | } | ||
280 | return log+1; | ||
281 | } | ||
282 | static void draw(void) | ||
283 | { | ||
284 | #ifdef HAVE_LCD_COLOR | ||
285 | rb->lcd_set_background(BACKGROUND); | ||
286 | #endif | ||
287 | rb->lcd_clear_display(); | ||
288 | |||
289 | /* draw the background */ | ||
290 | |||
291 | rb->lcd_bitmap(_2048_background, BACKGROUND_X, BACKGROUND_Y, BMPWIDTH__2048_background, BMPWIDTH__2048_background); | ||
292 | |||
293 | /* | ||
294 | grey_gray_bitmap(_2048_background, BACKGROUND_X, BACKGROUND_Y, BMPWIDTH__2048_background, BMPHEIGHT__2048_background); | ||
295 | */ | ||
296 | |||
297 | /* draw the grid */ | ||
298 | |||
299 | for(int y=0;y<GRID_SIZE;++y) | ||
300 | { | ||
301 | for(int x=0;x<GRID_SIZE;++x) | ||
302 | { | ||
303 | rb->lcd_bitmap_part(_2048_tiles, /* source */ | ||
304 | BMPWIDTH__2048_tiles-BMPHEIGHT__2048_tiles*ilog2(ctx->grid[x][y]), 0, /* source upper left corner */ | ||
305 | STRIDE(SCREEN_MAIN, BMPWIDTH__2048_tiles, BMPHEIGHT__2048_tiles), /* stride */ | ||
306 | (BMPHEIGHT__2048_tiles+MIN_SPACE)*x+BASE_X, (BMPHEIGHT__2048_tiles+MIN_SPACE)*y+BASE_Y, /* dest upper-left corner */ | ||
307 | BMPHEIGHT__2048_tiles, BMPHEIGHT__2048_tiles); /* size of the cut section */ | ||
308 | } | ||
309 | } | ||
310 | /* draw the title */ | ||
311 | char buf[32]; | ||
312 | #ifdef HAVE_LCD_COLOR | ||
313 | rb->lcd_set_foreground(TEXT_COLOR); | ||
314 | #endif | ||
315 | rb->snprintf(buf, 31, "%d", WINNING_TILE); | ||
316 | |||
317 | /* check if the title will go into the grid */ | ||
318 | int w, h; | ||
319 | rb->lcd_setfont(FONT_UI); | ||
320 | rb->font_getstringsize(buf, &w, &h, FONT_UI); | ||
321 | if(w+TITLE_X>=BACKGROUND_X && h+TITLE_Y>=BACKGROUND_Y) | ||
322 | { | ||
323 | /* if it goes into the grid, use the system font, which should be smaller */ | ||
324 | rb->lcd_setfont(FONT_SYSFIXED); | ||
325 | } | ||
326 | rb->lcd_putsxy(TITLE_X, TITLE_Y, buf); | ||
327 | |||
328 | /* draw the score */ | ||
329 | rb->snprintf(buf, 31, "Score: %d", ctx->score); | ||
330 | #ifdef HAVE_LCD_COLOR | ||
331 | rb->lcd_set_foreground(LCD_WHITE); | ||
332 | rb->lcd_set_background(BOARD_BACKGROUND); | ||
333 | #endif | ||
334 | rb->lcd_setfont(FONT_UI); | ||
335 | rb->font_getstringsize(buf, &w, &h, FONT_UI); | ||
336 | if(w+SCORE_X>=BACKGROUND_X && h+SCORE_Y>=BACKGROUND_Y) | ||
337 | { | ||
338 | /* score overflows */ | ||
339 | /* first see if it fits with Score: and FONT_SYSFIXED */ | ||
340 | rb->lcd_setfont(FONT_SYSFIXED); | ||
341 | rb->font_getstringsize(buf, &w, &h, FONT_SYSFIXED); | ||
342 | if(w+SCORE_X < BACKGROUND_X) | ||
343 | /* it fits, go and draw it */ | ||
344 | goto draw_lbl; | ||
345 | |||
346 | /* now try with S: and FONT_UI */ | ||
347 | rb->snprintf(buf, 31, "S: %d", ctx->score); | ||
348 | rb->font_getstringsize(buf, &w, &h, FONT_UI); | ||
349 | rb->lcd_setfont(FONT_UI); | ||
350 | if(w+SCORE_X<BACKGROUND_X) | ||
351 | goto draw_lbl; | ||
352 | |||
353 | /* now try with S: and FONT_SYSFIXED */ | ||
354 | rb->snprintf(buf, 31, "S: %d", ctx->score); | ||
355 | rb->font_getstringsize(buf, &w, &h, FONT_SYSFIXED); | ||
356 | rb->lcd_setfont(FONT_SYSFIXED); | ||
357 | if(w+SCORE_X<BACKGROUND_X) | ||
358 | goto draw_lbl; | ||
359 | |||
360 | /* then try without Score: and FONT_UI */ | ||
361 | rb->snprintf(buf, 31, "%d", ctx->score); | ||
362 | rb->font_getstringsize(buf, &w, &h, FONT_UI); | ||
363 | rb->lcd_setfont(FONT_UI); | ||
364 | if(w+SCORE_X<BACKGROUND_X) | ||
365 | goto draw_lbl; | ||
366 | |||
367 | /* as a last resort, don't use Score: and use the system font */ | ||
368 | rb->snprintf(buf, 31, "%d", ctx->score); | ||
369 | rb->lcd_setfont(FONT_SYSFIXED); | ||
370 | } | ||
371 | draw_lbl: | ||
372 | rb->lcd_putsxy(SCORE_X, SCORE_Y, buf); | ||
373 | |||
374 | /* draw the best score */ | ||
375 | |||
376 | rb->snprintf(buf, 31, "Best: %d", best_score); | ||
377 | #ifdef HAVE_LCD_COLOR | ||
378 | rb->lcd_set_foreground(LCD_WHITE); | ||
379 | rb->lcd_set_background(BOARD_BACKGROUND); | ||
380 | #endif | ||
381 | rb->lcd_setfont(FONT_UI); | ||
382 | rb->font_getstringsize(buf, &w, &h, FONT_UI); | ||
383 | if(w+BEST_SCORE_X>=BACKGROUND_X && h+BEST_SCORE_Y>=BACKGROUND_Y) | ||
384 | { | ||
385 | /* score overflows */ | ||
386 | /* first see if it fits with Score: and FONT_SYSFIXED */ | ||
387 | rb->lcd_setfont(FONT_SYSFIXED); | ||
388 | rb->font_getstringsize(buf, &w, &h, FONT_SYSFIXED); | ||
389 | if(w+BEST_SCORE_X < BACKGROUND_X) | ||
390 | /* it fits, go and draw it */ | ||
391 | goto draw_best; | ||
392 | |||
393 | /* now try with S: and FONT_UI */ | ||
394 | rb->snprintf(buf, 31, "B: %d", best_score); | ||
395 | rb->font_getstringsize(buf, &w, &h, FONT_UI); | ||
396 | rb->lcd_setfont(FONT_UI); | ||
397 | if(w+BEST_SCORE_X<BACKGROUND_X) | ||
398 | goto draw_best; | ||
399 | |||
400 | /* now try with S: and FONT_SYSFIXED */ | ||
401 | rb->snprintf(buf, 31, "B: %d", best_score); | ||
402 | rb->font_getstringsize(buf, &w, &h, FONT_SYSFIXED); | ||
403 | rb->lcd_setfont(FONT_SYSFIXED); | ||
404 | if(w+BEST_SCORE_X<BACKGROUND_X) | ||
405 | goto draw_best; | ||
406 | |||
407 | /* then try without Score: and FONT_UI */ | ||
408 | rb->snprintf(buf, 31, "%d", best_score); | ||
409 | rb->font_getstringsize(buf, &w, &h, FONT_UI); | ||
410 | rb->lcd_setfont(FONT_UI); | ||
411 | if(w+BEST_SCORE_X<BACKGROUND_X) | ||
412 | goto draw_best; | ||
413 | |||
414 | /* as a last resort, don't use Score: and use the system font */ | ||
415 | rb->snprintf(buf, 31, "%d", best_score); | ||
416 | rb->lcd_setfont(FONT_SYSFIXED); | ||
417 | } | ||
418 | draw_best: | ||
419 | rb->lcd_putsxy(BEST_SCORE_X, BEST_SCORE_Y, buf); | ||
420 | |||
421 | rb->lcd_update(); | ||
422 | /* revert the font back */ | ||
423 | rb->lcd_setfont(WHAT_FONT); | ||
424 | } | ||
425 | |||
426 | /* place a 2 or 4 in a random empty space */ | ||
427 | static void place_random(void) | ||
428 | { | ||
429 | int xpos[SPACES],ypos[SPACES]; | ||
430 | int back=0; | ||
431 | /* get the indexes of empty spaces */ | ||
432 | for(int y=0;y<GRID_SIZE;++y) | ||
433 | for(int x=0;x<GRID_SIZE;++x) | ||
434 | { | ||
435 | if(!ctx->grid[x][y]) | ||
436 | { | ||
437 | xpos[back]=x; | ||
438 | ypos[back]=y; | ||
439 | ++back; | ||
440 | } | ||
441 | } | ||
442 | if(!back) | ||
443 | /* no empty spaces */ | ||
444 | return; | ||
445 | int idx=rand_range(0,back-1); | ||
446 | ctx->grid[xpos[idx]][ypos[idx]]=rand_2_or_4(); | ||
447 | } | ||
448 | |||
449 | /* copies old_grid to ctx->grid */ | ||
450 | static void restore_old_grid(void) | ||
451 | { | ||
452 | memcpy(&ctx->grid, &old_grid, sizeof(int)*SPACES); | ||
453 | } | ||
454 | |||
455 | /* checks for a win or loss */ | ||
456 | static bool check_gameover(void) | ||
457 | { | ||
458 | int numempty=0; | ||
459 | for(int y=0;y<GRID_SIZE;++y) | ||
460 | { | ||
461 | for(int x=0;x<GRID_SIZE;++x) | ||
462 | { | ||
463 | if(ctx->grid[x][y]==0) | ||
464 | ++numempty; | ||
465 | if(ctx->grid[x][y]==WINNING_TILE && !ctx->already_won) | ||
466 | { | ||
467 | /* Let the user see the tile in its full glory... */ | ||
468 | draw(); | ||
469 | ctx->already_won=true; | ||
470 | rb->splash(HZ*2,"You win!"); | ||
471 | const struct text_message prompt={(const char*[]){"Keep going?"}, 1}; | ||
472 | enum yesno_res keepgoing=rb->gui_syncyesno_run(&prompt, NULL, NULL); | ||
473 | if(keepgoing==YESNO_NO) | ||
474 | return true; | ||
475 | else | ||
476 | return false; | ||
477 | } | ||
478 | } | ||
479 | } | ||
480 | if(!numempty) | ||
481 | { | ||
482 | /* No empty spaces, check for valid moves */ | ||
483 | /* Then, get the current score */ | ||
484 | int oldscore=ctx->score; | ||
485 | memset(&merged_grid,0,SPACES*sizeof(bool)); | ||
486 | up(); | ||
487 | if(memcmp(&old_grid, &ctx->grid, sizeof(int)*SPACES)) | ||
488 | { | ||
489 | restore_old_grid(); | ||
490 | ctx->score=oldscore; | ||
491 | return false; | ||
492 | } | ||
493 | restore_old_grid(); | ||
494 | memset(&merged_grid,0,SPACES*sizeof(bool)); | ||
495 | down(); | ||
496 | if(memcmp(&old_grid, &ctx->grid, sizeof(int)*SPACES)) | ||
497 | { | ||
498 | restore_old_grid(); | ||
499 | ctx->score=oldscore; | ||
500 | return false; | ||
501 | } | ||
502 | restore_old_grid(); | ||
503 | memset(&merged_grid,0,SPACES*sizeof(bool)); | ||
504 | left(); | ||
505 | if(memcmp(&old_grid, &ctx->grid, sizeof(int)*SPACES)) | ||
506 | { | ||
507 | restore_old_grid(); | ||
508 | ctx->score=oldscore; | ||
509 | return false; | ||
510 | } | ||
511 | restore_old_grid(); | ||
512 | memset(&merged_grid,0,SPACES*sizeof(bool)); | ||
513 | right(); | ||
514 | if(memcmp(&old_grid, &ctx->grid, sizeof(int)*SPACES)) | ||
515 | { | ||
516 | restore_old_grid(); | ||
517 | ctx->score=oldscore; | ||
518 | return false; | ||
519 | } | ||
520 | /* no more legal moves */ | ||
521 | ctx->score=oldscore; | ||
522 | draw(); /* Shame the player :) */ | ||
523 | rb->splash(HZ*2, "Game Over!"); | ||
524 | return true; | ||
525 | } | ||
526 | return false; | ||
527 | } | ||
528 | |||
529 | /* loads highscores from disk */ | ||
530 | /* creates an empty structure if the file does not exist */ | ||
531 | static void load_hs(void) | ||
532 | { | ||
533 | if(rb->file_exists(HISCORES_FILE)) | ||
534 | highscore_load(HISCORES_FILE, highscores, NUM_SCORES); | ||
535 | else | ||
536 | memset(highscores, 0, sizeof(struct highscore)*NUM_SCORES); | ||
537 | } | ||
538 | |||
539 | /* initialize the data structures */ | ||
540 | static void init_game(bool newgame) | ||
541 | { | ||
542 | best_score=highscores[0].score; | ||
543 | if(newgame) | ||
544 | { | ||
545 | /* initialize the game context */ | ||
546 | memset(ctx->grid, 0, sizeof(int)*SPACES); | ||
547 | for(int i=0;i<NUM_STARTING_TILES;++i) | ||
548 | { | ||
549 | place_random(); | ||
550 | } | ||
551 | ctx->score=0; | ||
552 | ctx->already_won=false; | ||
553 | } | ||
554 | /* using the menu resets the font */ | ||
555 | /* set it again here */ | ||
556 | rb->lcd_setfont(WHAT_FONT); | ||
557 | /* Now calculate font sizes */ | ||
558 | /* Now get the height of the font */ | ||
559 | rb->font_getstringsize("0123456789", NULL, &max_numeral_height,WHAT_FONT); | ||
560 | max_numeral_height+=VERT_SPACING; | ||
561 | backlight_ignore_timeout(); | ||
562 | rb->lcd_clear_display(); | ||
563 | draw(); | ||
564 | } | ||
565 | |||
566 | /* save the current game state */ | ||
567 | static void save_game(void) | ||
568 | { | ||
569 | rb->splash(0, "Saving..."); | ||
570 | int fd=rb->open(RESUME_FILE,O_WRONLY|O_CREAT, 0666); | ||
571 | if(fd<0) | ||
572 | { | ||
573 | return; | ||
574 | } | ||
575 | ctx->cksum=0; | ||
576 | for(int x=0;x<GRID_SIZE;++x) | ||
577 | for(int y=0;y<GRID_SIZE;++y) | ||
578 | ctx->cksum+=ctx->grid[x][y]; | ||
579 | ctx->cksum^=ctx->score; | ||
580 | rb->write(fd, ctx,sizeof(struct game_ctx_t)); | ||
581 | rb->close(fd); | ||
582 | rb->lcd_update(); | ||
583 | } | ||
584 | |||
585 | /* loads a saved game, returns true on success */ | ||
586 | static bool load_game(void) | ||
587 | { | ||
588 | int success=0; | ||
589 | int fd=rb->open(RESUME_FILE, O_RDONLY); | ||
590 | if(fd<0) | ||
591 | { | ||
592 | rb->remove(RESUME_FILE); | ||
593 | return false; | ||
594 | } | ||
595 | int numread=rb->read(fd, ctx, sizeof(struct game_ctx_t)); | ||
596 | int calc=0; | ||
597 | for(int x=0;x<GRID_SIZE;++x) | ||
598 | for(int y=0;y<GRID_SIZE;++y) | ||
599 | calc+=ctx->grid[x][y]; | ||
600 | calc^=ctx->score; | ||
601 | if(numread==sizeof(struct game_ctx_t) && calc==ctx->cksum) | ||
602 | ++success; | ||
603 | rb->close(fd); | ||
604 | rb->remove(RESUME_FILE); | ||
605 | return (success==1); | ||
606 | } | ||
607 | |||
608 | /* update the highscores with ctx->score */ | ||
609 | static void hs_check_update(bool noshow) | ||
610 | { | ||
611 | /* first, find the biggest tile to show as the level */ | ||
612 | int biggest=0; | ||
613 | for(int x=0;x<GRID_SIZE;++x) | ||
614 | { | ||
615 | for(int y=0;y<GRID_SIZE;++y) | ||
616 | { | ||
617 | if(ctx->grid[x][y]>biggest) | ||
618 | biggest=ctx->grid[x][y]; | ||
619 | } | ||
620 | } | ||
621 | int hs_idx=highscore_update(ctx->score,biggest, "", highscores,NUM_SCORES); | ||
622 | if(!noshow) | ||
623 | { | ||
624 | /* show the scores if there is a new high score */ | ||
625 | if(hs_idx>=0) | ||
626 | { | ||
627 | rb->splashf(HZ*2,"New High Score: %d", ctx->score); | ||
628 | rb->lcd_clear_display(); | ||
629 | highscore_show(hs_idx,highscores,NUM_SCORES,true); | ||
630 | } | ||
631 | } | ||
632 | highscore_save(HISCORES_FILE,highscores,NUM_SCORES); | ||
633 | } | ||
634 | |||
635 | /* asks the user if they wish to quit */ | ||
636 | static bool confirm_quit(void) | ||
637 | { | ||
638 | const struct text_message prompt={(const char*[]){"Are you sure?", "This will clear your current game."}, 2}; | ||
639 | enum yesno_res response=rb->gui_syncyesno_run(&prompt, NULL, NULL); | ||
640 | if(response==YESNO_NO) | ||
641 | return false; | ||
642 | else | ||
643 | return true; | ||
644 | } | ||
645 | |||
646 | /* show the pause menu */ | ||
647 | static int do_2048_pause_menu(void) | ||
648 | { | ||
649 | int sel=0; | ||
650 | MENUITEM_STRINGLIST(menu,"2048 Menu", NULL, | ||
651 | "Resume Game", | ||
652 | "Start New Game", | ||
653 | "High Scores", | ||
654 | "Playback Control", | ||
655 | "Help", | ||
656 | "Quit without Saving", | ||
657 | "Quit"); | ||
658 | bool quit=false; | ||
659 | while(!quit) | ||
660 | { | ||
661 | switch(rb->do_menu(&menu, &sel, NULL, false)) | ||
662 | { | ||
663 | case 0: | ||
664 | draw(); | ||
665 | return 0; | ||
666 | case 1: | ||
667 | { | ||
668 | if(!confirm_quit()) | ||
669 | break; | ||
670 | else | ||
671 | { | ||
672 | hs_check_update(false); | ||
673 | return 1; | ||
674 | } | ||
675 | } | ||
676 | case 2: | ||
677 | highscore_show(-1,highscores, NUM_SCORES, true); | ||
678 | break; | ||
679 | case 3: | ||
680 | playback_control(NULL); | ||
681 | break; | ||
682 | case 4: | ||
683 | do_help(); | ||
684 | break; | ||
685 | case 5: /* quit w/o saving */ | ||
686 | { | ||
687 | if(!confirm_quit()) | ||
688 | break; | ||
689 | else | ||
690 | { | ||
691 | return 2; | ||
692 | } | ||
693 | } | ||
694 | case 6: | ||
695 | return 3; | ||
696 | } | ||
697 | } | ||
698 | return 0; | ||
699 | } | ||
700 | |||
701 | static void exit_handler(void) | ||
702 | { | ||
703 | cleanup(); | ||
704 | if(abnormal_exit) | ||
705 | save_game(); | ||
706 | return; | ||
707 | } | ||
708 | static bool check_hs; | ||
709 | /* main game loop */ | ||
710 | static enum plugin_status do_game(bool newgame) | ||
711 | { | ||
712 | init_game(newgame); | ||
713 | rb_atexit(&exit_handler); | ||
714 | int made_move=0; | ||
715 | while(1) | ||
716 | { | ||
717 | #ifdef HAVE_ADJUSTABLE_CPU_FREQ | ||
718 | rb->cpu_boost(false); /* Save battery when idling */ | ||
719 | #endif | ||
720 | /* Wait for a button press */ | ||
721 | int button=pluginlib_getaction(-1, plugin_contexts, ARRAYLEN(plugin_contexts)); | ||
722 | made_move=0; | ||
723 | memset(&merged_grid,0,SPACES*sizeof(bool)); | ||
724 | memcpy(&old_grid, &ctx->grid, sizeof(int)*SPACES); | ||
725 | int grid_before_anim_step[GRID_SIZE][GRID_SIZE]; | ||
726 | #ifdef HAVE_ADJUSTABLE_CPU_FREQ | ||
727 | rb->cpu_boost(true); /* doing work now... */ | ||
728 | #endif | ||
729 | switch(button) | ||
730 | { | ||
731 | case KEY_UP: | ||
732 | for(int i=0;i<GRID_SIZE-1;++i) | ||
733 | { | ||
734 | memcpy(grid_before_anim_step, ctx->grid, sizeof(int)*SPACES); | ||
735 | up(); | ||
736 | if(memcmp(grid_before_anim_step, ctx->grid, sizeof(int)*SPACES)) | ||
737 | { | ||
738 | rb->sleep(ANIM_SLEEPTIME); | ||
739 | draw(); | ||
740 | } | ||
741 | } | ||
742 | made_move=1; | ||
743 | break; | ||
744 | case KEY_DOWN: | ||
745 | for(int i=0;i<GRID_SIZE-1;++i) | ||
746 | { | ||
747 | memcpy(grid_before_anim_step, ctx->grid, sizeof(int)*SPACES); | ||
748 | down(); | ||
749 | if(memcmp(grid_before_anim_step, ctx->grid, sizeof(int)*SPACES)) | ||
750 | { | ||
751 | rb->sleep(ANIM_SLEEPTIME); | ||
752 | draw(); | ||
753 | } | ||
754 | } | ||
755 | made_move=1; | ||
756 | break; | ||
757 | case KEY_LEFT: | ||
758 | for(int i=0;i<GRID_SIZE-1;++i) | ||
759 | { | ||
760 | memcpy(grid_before_anim_step, ctx->grid, sizeof(int)*SPACES); | ||
761 | left(); | ||
762 | if(memcmp(grid_before_anim_step, ctx->grid, sizeof(int)*SPACES)) | ||
763 | { | ||
764 | rb->sleep(ANIM_SLEEPTIME); | ||
765 | draw(); | ||
766 | } | ||
767 | } | ||
768 | made_move=1; | ||
769 | break; | ||
770 | case KEY_RIGHT: | ||
771 | for(int i=0;i<GRID_SIZE-1;++i) | ||
772 | { | ||
773 | memcpy(grid_before_anim_step, ctx->grid, sizeof(int)*SPACES); | ||
774 | right(); | ||
775 | if(memcmp(grid_before_anim_step, ctx->grid, sizeof(int)*SPACES)) | ||
776 | { | ||
777 | rb->sleep(ANIM_SLEEPTIME); | ||
778 | draw(); | ||
779 | } | ||
780 | } | ||
781 | made_move=1; | ||
782 | break; | ||
783 | case KEY_EXIT: | ||
784 | switch(do_2048_pause_menu()) | ||
785 | { | ||
786 | case 0: /* resume */ | ||
787 | break; | ||
788 | case 1: /* new game */ | ||
789 | init_game(true); | ||
790 | made_move=1; | ||
791 | continue; | ||
792 | break; | ||
793 | case 2: /* quit without saving */ | ||
794 | check_hs=true; | ||
795 | rb->remove(RESUME_FILE); | ||
796 | return PLUGIN_ERROR; | ||
797 | case 3: /* save and quit */ | ||
798 | check_hs=false; | ||
799 | save_game(); | ||
800 | return PLUGIN_ERROR; | ||
801 | } | ||
802 | break; | ||
803 | default: | ||
804 | { | ||
805 | exit_on_usb(button); /* handles poweroff and USB events */ | ||
806 | break; | ||
807 | } | ||
808 | } | ||
809 | if(made_move) | ||
810 | { | ||
811 | /* Check if we actually moved, then add random */ | ||
812 | if(memcmp(&old_grid, ctx->grid, sizeof(int)*SPACES)) | ||
813 | { | ||
814 | place_random(); | ||
815 | } | ||
816 | memcpy(&old_grid, ctx->grid, sizeof(int)*SPACES); | ||
817 | if(check_gameover()) | ||
818 | return PLUGIN_OK; | ||
819 | draw(); | ||
820 | } | ||
821 | #ifdef HAVE_ADJUSTABLE_CPU_FREQ | ||
822 | rb->cpu_boost(false); /* back to idle */ | ||
823 | #endif | ||
824 | rb->yield(); | ||
825 | } | ||
826 | } | ||
827 | |||
828 | /* decide if this_item should be shown in the main menu */ | ||
829 | /* used to hide resume option when there is no save */ | ||
830 | static int mainmenu_cb(int action, const struct menu_item_ex *this_item) | ||
831 | { | ||
832 | int idx=((intptr_t)this_item); | ||
833 | if(action==ACTION_REQUEST_MENUITEM && !loaded && (idx==0 || idx==5)) | ||
834 | return ACTION_EXIT_MENUITEM; | ||
835 | return action; | ||
836 | } | ||
837 | |||
838 | /* show the main menu */ | ||
839 | static enum plugin_status do_2048_menu(void) | ||
840 | { | ||
841 | int sel=0; | ||
842 | loaded=load_game(); | ||
843 | MENUITEM_STRINGLIST(menu,"2048 Menu", mainmenu_cb, "Resume Game", "Start New Game","High Scores","Playback Control", "Help", "Quit without Saving", "Quit"); | ||
844 | bool quit=false; | ||
845 | while(!quit) | ||
846 | { | ||
847 | int item; | ||
848 | switch(item=rb->do_menu(&menu,&sel,NULL,false)) | ||
849 | { | ||
850 | case 0: /* Start new game or resume a game */ | ||
851 | case 1: | ||
852 | { | ||
853 | if(item==1 && loaded) | ||
854 | { | ||
855 | if(!confirm_quit()) | ||
856 | break; | ||
857 | } | ||
858 | enum plugin_status ret=do_game(item==1); | ||
859 | switch(ret) | ||
860 | { | ||
861 | case PLUGIN_OK: | ||
862 | { | ||
863 | loaded=false; | ||
864 | rb->remove(RESUME_FILE); | ||
865 | hs_check_update(false); | ||
866 | break; | ||
867 | } | ||
868 | case PLUGIN_USB_CONNECTED: | ||
869 | save_game(); | ||
870 | /* Don't bother showing the high scores... */ | ||
871 | return ret; | ||
872 | case PLUGIN_ERROR: /* exit without menu */ | ||
873 | if(check_hs) | ||
874 | hs_check_update(false); | ||
875 | return PLUGIN_OK; | ||
876 | default: | ||
877 | break; | ||
878 | } | ||
879 | break; | ||
880 | } | ||
881 | case 2: | ||
882 | highscore_show(-1,highscores, NUM_SCORES, true); | ||
883 | break; | ||
884 | case 3: | ||
885 | playback_control(NULL); | ||
886 | break; | ||
887 | case 4: | ||
888 | do_help(); | ||
889 | break; | ||
890 | case 5: | ||
891 | if(confirm_quit()) | ||
892 | return PLUGIN_OK; | ||
893 | case 6: | ||
894 | if(loaded) | ||
895 | save_game(); | ||
896 | return PLUGIN_OK; | ||
897 | default: | ||
898 | break; | ||
899 | } | ||
900 | } | ||
901 | return PLUGIN_OK; | ||
902 | } | ||
903 | enum plugin_status plugin_start(const void* param) | ||
904 | { | ||
905 | (void)param; | ||
906 | rb->srand(*rb->current_tick); | ||
907 | load_hs(); | ||
908 | rb->lcd_setfont(WHAT_FONT); | ||
909 | |||
910 | /* now start the game menu */ | ||
911 | enum plugin_status ret=do_2048_menu(); | ||
912 | highscore_save(HISCORES_FILE,highscores,NUM_SCORES); | ||
913 | cleanup(); | ||
914 | abnormal_exit=false; | ||
915 | return ret; | ||
916 | } | ||
diff --git a/apps/plugins/CATEGORIES b/apps/plugins/CATEGORIES index 8a849aafc2..626dca0d32 100644 --- a/apps/plugins/CATEGORIES +++ b/apps/plugins/CATEGORIES | |||
@@ -1,3 +1,4 @@ | |||
1 | 2048,games | ||
1 | alpine_cdc,apps | 2 | alpine_cdc,apps |
2 | alarmclock,apps | 3 | alarmclock,apps |
3 | autostart,apps | 4 | autostart,apps |
diff --git a/apps/plugins/SOURCES b/apps/plugins/SOURCES index c56cd5eb46..9b7436ac20 100644 --- a/apps/plugins/SOURCES +++ b/apps/plugins/SOURCES | |||
@@ -213,6 +213,8 @@ nim.c | |||
213 | 213 | ||
214 | #if LCD_DEPTH > 1 /* non-mono bitmap targets */ | 214 | #if LCD_DEPTH > 1 /* non-mono bitmap targets */ |
215 | 215 | ||
216 | /* 2048 runs on 1-bit LCDs, but it is unplayable */ | ||
217 | 2048.c | ||
216 | matrix.c | 218 | matrix.c |
217 | 219 | ||
218 | #if (LCD_WIDTH > 138) | 220 | #if (LCD_WIDTH > 138) |
diff --git a/apps/plugins/bitmaps/native/SOURCES b/apps/plugins/bitmaps/native/SOURCES index 4b7f0e2670..d608062335 100644 --- a/apps/plugins/bitmaps/native/SOURCES +++ b/apps/plugins/bitmaps/native/SOURCES | |||
@@ -1,5 +1,27 @@ | |||
1 | #ifdef HAVE_LCD_BITMAP | 1 | #ifdef HAVE_LCD_BITMAP |
2 | 2 | ||
3 | /* 2048 */ | ||
4 | |||
5 | #define MIN(x,y) ((x<y)?x:y) | ||
6 | #if MIN(LCD_WIDTH, LCD_HEIGHT)>=240 | ||
7 | _2048_tiles.48x48x24.bmp | ||
8 | _2048_background.224x224x24.bmp | ||
9 | #elif MIN(LCD_WIDTH, LCD_HEIGHT)>=176 | ||
10 | _2048_tiles.36x36x24.bmp | ||
11 | _2048_background.168x168x24.bmp | ||
12 | #elif MIN(LCD_WIDTH, LCD_HEIGHT)>=132 || MIN(LCD_WIDTH, LCD_HEIGHT)>=128 | ||
13 | _2048_tiles.26x26x24.bmp | ||
14 | _2048_background.121x121x24.bmp | ||
15 | #elif MIN(LCD_WIDTH, LCD_HEIGHT)>=110 | ||
16 | _2048_tiles.22x22x24.bmp | ||
17 | _2048_background.103x103x24.bmp | ||
18 | #else | ||
19 | /* default to smallest bitmaps */ | ||
20 | _2048_tiles.12x12x24.bmp | ||
21 | _2048_background.56x56x24.bmp | ||
22 | #endif | ||
23 | #undef MIN | ||
24 | |||
3 | /* Brickmania */ | 25 | /* Brickmania */ |
4 | #ifdef HAVE_LCD_COLOR | 26 | #ifdef HAVE_LCD_COLOR |
5 | #if LCD_WIDTH >= 112 | 27 | #if LCD_WIDTH >= 112 |
diff --git a/apps/plugins/bitmaps/native/_2048_background.103x103x24.bmp b/apps/plugins/bitmaps/native/_2048_background.103x103x24.bmp new file mode 100644 index 0000000000..153126bc99 --- /dev/null +++ b/apps/plugins/bitmaps/native/_2048_background.103x103x24.bmp | |||
Binary files differ | |||
diff --git a/apps/plugins/bitmaps/native/_2048_background.121x121x24.bmp b/apps/plugins/bitmaps/native/_2048_background.121x121x24.bmp new file mode 100644 index 0000000000..1e6e3270d0 --- /dev/null +++ b/apps/plugins/bitmaps/native/_2048_background.121x121x24.bmp | |||
Binary files differ | |||
diff --git a/apps/plugins/bitmaps/native/_2048_background.168x168x24.bmp b/apps/plugins/bitmaps/native/_2048_background.168x168x24.bmp new file mode 100644 index 0000000000..c47b5d26d5 --- /dev/null +++ b/apps/plugins/bitmaps/native/_2048_background.168x168x24.bmp | |||
Binary files differ | |||
diff --git a/apps/plugins/bitmaps/native/_2048_background.224x224x24.bmp b/apps/plugins/bitmaps/native/_2048_background.224x224x24.bmp new file mode 100644 index 0000000000..531d5ed541 --- /dev/null +++ b/apps/plugins/bitmaps/native/_2048_background.224x224x24.bmp | |||
Binary files differ | |||
diff --git a/apps/plugins/bitmaps/native/_2048_background.56x56x24.bmp b/apps/plugins/bitmaps/native/_2048_background.56x56x24.bmp new file mode 100644 index 0000000000..c3008f8eae --- /dev/null +++ b/apps/plugins/bitmaps/native/_2048_background.56x56x24.bmp | |||
Binary files differ | |||
diff --git a/apps/plugins/bitmaps/native/_2048_tiles.12x12x24.bmp b/apps/plugins/bitmaps/native/_2048_tiles.12x12x24.bmp new file mode 100644 index 0000000000..e531c2b6e2 --- /dev/null +++ b/apps/plugins/bitmaps/native/_2048_tiles.12x12x24.bmp | |||
Binary files differ | |||
diff --git a/apps/plugins/bitmaps/native/_2048_tiles.22x22x24.bmp b/apps/plugins/bitmaps/native/_2048_tiles.22x22x24.bmp new file mode 100644 index 0000000000..186ba8bc95 --- /dev/null +++ b/apps/plugins/bitmaps/native/_2048_tiles.22x22x24.bmp | |||
Binary files differ | |||
diff --git a/apps/plugins/bitmaps/native/_2048_tiles.26x26x24.bmp b/apps/plugins/bitmaps/native/_2048_tiles.26x26x24.bmp new file mode 100644 index 0000000000..f7df5ba5a4 --- /dev/null +++ b/apps/plugins/bitmaps/native/_2048_tiles.26x26x24.bmp | |||
Binary files differ | |||
diff --git a/apps/plugins/bitmaps/native/_2048_tiles.36x36x24.bmp b/apps/plugins/bitmaps/native/_2048_tiles.36x36x24.bmp new file mode 100644 index 0000000000..e27ada133d --- /dev/null +++ b/apps/plugins/bitmaps/native/_2048_tiles.36x36x24.bmp | |||
Binary files differ | |||
diff --git a/apps/plugins/bitmaps/native/_2048_tiles.48x48x24.bmp b/apps/plugins/bitmaps/native/_2048_tiles.48x48x24.bmp new file mode 100644 index 0000000000..09165203ed --- /dev/null +++ b/apps/plugins/bitmaps/native/_2048_tiles.48x48x24.bmp | |||
Binary files differ | |||