A bit of adapting.
[Rockbox.git] / apps / plugins / rockblox.c
blob45f3c4f14e5ed7428b92a6309e34849b60a0c6e1
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2005 Eli Sherer
12 * Heavily modified for embedded use by Björn Stenberg (bjorn@haxx.se)
14 * All files in this archive are subject to the GNU General Public License.
15 * See the file COPYING in the source tree root for full license agreement.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
21 #include "plugin.h"
22 #include "highscore.h"
23 #include "playergfx.h"
24 #include "helper.h"
26 PLUGIN_HEADER
28 #if (CONFIG_KEYPAD == IPOD_4G_PAD) || \
29 (CONFIG_KEYPAD == IPOD_3G_PAD) || \
30 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
32 #define ROCKBLOX_OFF (BUTTON_MENU | BUTTON_SELECT)
33 #define ROCKBLOX_ROTATE_RIGHT BUTTON_SCROLL_BACK
34 #define ROCKBLOX_ROTATE_RIGHT2 (BUTTON_MENU | BUTTON_REL)
35 #define ROCKBLOX_ROTATE_LEFT BUTTON_SCROLL_FWD
36 #define ROCKBLOX_LEFT BUTTON_LEFT
37 #define ROCKBLOX_RIGHT BUTTON_RIGHT
38 #define ROCKBLOX_DOWN BUTTON_PLAY
39 #define ROCKBLOX_RESTART (BUTTON_SELECT | BUTTON_PLAY)
40 #define ROCKBLOX_DROP (BUTTON_SELECT | BUTTON_REL)
42 #define SCROLL_WHEEL
44 #elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || \
45 (CONFIG_KEYPAD == IRIVER_H300_PAD)
47 #define ROCKBLOX_OFF BUTTON_OFF
48 #define ROCKBLOX_ROTATE_RIGHT BUTTON_UP
49 #define ROCKBLOX_ROTATE_LEFT BUTTON_SELECT
50 #define ROCKBLOX_DOWN BUTTON_DOWN
51 #define ROCKBLOX_LEFT BUTTON_LEFT
52 #define ROCKBLOX_RIGHT BUTTON_RIGHT
53 #define ROCKBLOX_DROP BUTTON_MODE
54 #define ROCKBLOX_RESTART BUTTON_ON
56 #define ROCKBLOX_RC_OFF BUTTON_RC_STOP
58 #elif CONFIG_KEYPAD == RECORDER_PAD
60 #define ROCKBLOX_OFF BUTTON_OFF
61 #define ROCKBLOX_ROTATE_RIGHT BUTTON_UP
62 #define ROCKBLOX_ROTATE_LEFT BUTTON_PLAY
63 #define ROCKBLOX_DOWN BUTTON_DOWN
64 #define ROCKBLOX_LEFT BUTTON_LEFT
65 #define ROCKBLOX_RIGHT BUTTON_RIGHT
66 #define ROCKBLOX_DROP BUTTON_ON
67 #define ROCKBLOX_RESTART BUTTON_F1
69 #elif CONFIG_KEYPAD == ARCHOS_AV300_PAD
71 #define ROCKBLOX_OFF BUTTON_OFF
72 #define ROCKBLOX_ROTATE_RIGHT BUTTON_UP
73 #define ROCKBLOX_ROTATE_LEFT BUTTON_SELECT
74 #define ROCKBLOX_DOWN BUTTON_DOWN
75 #define ROCKBLOX_LEFT BUTTON_LEFT
76 #define ROCKBLOX_RIGHT BUTTON_RIGHT
77 #define ROCKBLOX_DROP BUTTON_ON
78 #define ROCKBLOX_RESTART BUTTON_F1
80 #elif CONFIG_KEYPAD == PLAYER_PAD
82 #define ROCKBLOX_OFF BUTTON_STOP
83 #define ROCKBLOX_ROTATE_RIGHT BUTTON_PLAY
84 #define ROCKBLOX_ROTATE_LEFT (BUTTON_ON|BUTTON_PLAY)
85 #define ROCKBLOX_DOWN BUTTON_MENU
86 #define ROCKBLOX_LEFT BUTTON_LEFT
87 #define ROCKBLOX_RIGHT BUTTON_RIGHT
88 #define ROCKBLOX_DROP_PRE BUTTON_ON
89 #define ROCKBLOX_DROP (BUTTON_ON|BUTTON_REL)
91 #elif CONFIG_KEYPAD == ONDIO_PAD
93 #define ROCKBLOX_OFF BUTTON_OFF
94 #define ROCKBLOX_ROTATE_RIGHT BUTTON_UP
95 #define ROCKBLOX_ROTATE_LEFT (BUTTON_MENU|BUTTON_UP)
96 #define ROCKBLOX_DOWN BUTTON_DOWN
97 #define ROCKBLOX_LEFT BUTTON_LEFT
98 #define ROCKBLOX_RIGHT BUTTON_RIGHT
99 #define ROCKBLOX_DROP_PRE BUTTON_MENU
100 #define ROCKBLOX_DROP (BUTTON_MENU|BUTTON_REL)
102 #elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD
104 #define ROCKBLOX_OFF BUTTON_POWER
105 #define ROCKBLOX_ROTATE_RIGHT BUTTON_UP
106 #define ROCKBLOX_ROTATE_LEFT BUTTON_SELECT
107 #define ROCKBLOX_DOWN BUTTON_DOWN
108 #define ROCKBLOX_LEFT BUTTON_LEFT
109 #define ROCKBLOX_RIGHT BUTTON_RIGHT
110 #define ROCKBLOX_DROP BUTTON_REC
111 #define ROCKBLOX_RESTART BUTTON_PLAY
113 #elif CONFIG_KEYPAD == SANSA_E200_PAD
115 #define ROCKBLOX_OFF BUTTON_POWER
116 #define ROCKBLOX_ROTATE_RIGHT BUTTON_SCROLL_UP
117 #define ROCKBLOX_ROTATE_LEFT BUTTON_SCROLL_DOWN
118 #define ROCKBLOX_DOWN BUTTON_DOWN
119 #define ROCKBLOX_LEFT BUTTON_LEFT
120 #define ROCKBLOX_RIGHT BUTTON_RIGHT
121 #define ROCKBLOX_DROP BUTTON_SELECT
122 #define ROCKBLOX_RESTART BUTTON_REC
124 #elif CONFIG_KEYPAD == SANSA_C200_PAD
126 #define ROCKBLOX_OFF BUTTON_POWER
127 #define ROCKBLOX_ROTATE_RIGHT BUTTON_UP
128 #define ROCKBLOX_ROTATE_RIGHT2 BUTTON_VOL_DOWN
129 #define ROCKBLOX_ROTATE_LEFT BUTTON_VOL_UP
130 #define ROCKBLOX_DOWN BUTTON_DOWN
131 #define ROCKBLOX_LEFT BUTTON_LEFT
132 #define ROCKBLOX_RIGHT BUTTON_RIGHT
133 #define ROCKBLOX_DROP BUTTON_SELECT
134 #define ROCKBLOX_RESTART BUTTON_REC
136 #elif CONFIG_KEYPAD == IRIVER_H10_PAD
138 #define ROCKBLOX_OFF BUTTON_POWER
139 #define ROCKBLOX_ROTATE_RIGHT BUTTON_SCROLL_UP
140 #define ROCKBLOX_ROTATE_LEFT BUTTON_REW
141 #define ROCKBLOX_DOWN BUTTON_SCROLL_DOWN
142 #define ROCKBLOX_LEFT BUTTON_LEFT
143 #define ROCKBLOX_RIGHT BUTTON_RIGHT
144 #define ROCKBLOX_DROP BUTTON_FF
145 #define ROCKBLOX_RESTART BUTTON_PLAY
147 #elif CONFIG_KEYPAD == GIGABEAT_PAD
149 #define ROCKBLOX_OFF BUTTON_POWER
150 #define ROCKBLOX_ROTATE_RIGHT BUTTON_VOL_DOWN
151 #define ROCKBLOX_ROTATE_LEFT BUTTON_VOL_UP
152 #define ROCKBLOX_ROTATE BUTTON_UP
153 #define ROCKBLOX_DOWN BUTTON_DOWN
154 #define ROCKBLOX_LEFT BUTTON_LEFT
155 #define ROCKBLOX_RIGHT BUTTON_RIGHT
156 #define ROCKBLOX_DROP BUTTON_SELECT
157 #define ROCKBLOX_RESTART BUTTON_A
159 #elif CONFIG_KEYPAD == IRIVER_IFP7XX_PAD
161 #define ROCKBLOX_OFF BUTTON_PLAY
162 #define ROCKBLOX_ROTATE_RIGHT BUTTON_UP
163 #define ROCKBLOX_ROTATE_LEFT BUTTON_SELECT
164 #define ROCKBLOX_DOWN BUTTON_DOWN
165 #define ROCKBLOX_LEFT BUTTON_LEFT
166 #define ROCKBLOX_RIGHT BUTTON_RIGHT
167 #define ROCKBLOX_DROP BUTTON_MODE
168 #define ROCKBLOX_RESTART BUTTON_EQ
170 #elif CONFIG_KEYPAD == MROBE500_PAD
171 #define ROCKBLOX_OFF BUTTON_POWER
172 #define ROCKBLOX_ROTATE_RIGHT BUTTON_RC_VOL_UP
173 #define ROCKBLOX_ROTATE_LEFT BUTTON_RC_VOL_DOWN
174 #define ROCKBLOX_DOWN BUTTON_RC_DOWN
175 #define ROCKBLOX_LEFT BUTTON_LEFT
176 #define ROCKBLOX_RIGHT BUTTON_RIGHT
177 #define ROCKBLOX_DROP BUTTON_RC_HEART
178 #define ROCKBLOX_RESTART BUTTON_RC_MODE
180 #endif
182 #define BLOCKS_NUM 7
183 #define EMPTY_BLOCK 7
185 #define BOARD_WIDTH 10
187 #ifdef HAVE_LCD_BITMAP
189 #define BOARD_HEIGHT 20
191 #if (LCD_WIDTH == 480) && (LCD_HEIGHT == 640)
193 #define BLOCK_WIDTH 30
194 #define BLOCK_HEIGHT 30
195 #define BOARD_X 14
196 #define BOARD_Y 2
197 #define PREVIEW_X 342
198 #define PREVIEW_Y 482
199 #define LABEL_X 344
200 #define SCORE_Y 58
201 #define LEVEL_Y 142
202 #define LINES_Y 218
203 #define HIGH_LABEL_X 344
204 #define HIGH_SCORE_Y 326
205 #define HIGH_LEVEL_Y 344
207 #elif (LCD_WIDTH == 320) && (LCD_HEIGHT == 240)
209 #define BLOCK_WIDTH 12
210 #define BLOCK_HEIGHT 12
211 #define BOARD_X 86
212 #define BOARD_Y 0
213 #define PREVIEW_X 12
214 #define PREVIEW_Y 11
215 #define LABEL_X 242
216 #define SCORE_Y 25
217 #define LEVEL_Y 70
218 #define LINES_Y 105
220 #elif (LCD_WIDTH == 240) && (LCD_HEIGHT == 320)
222 #define BLOCK_WIDTH 15
223 #define BLOCK_HEIGHT 15
224 #define BOARD_X 7
225 #define BOARD_Y 1
226 #define PREVIEW_X 171
227 #define PREVIEW_Y 241
228 #define LABEL_X 172
229 #define SCORE_Y 29
230 #define LEVEL_Y 71
231 #define LINES_Y 109
232 #define HIGH_LABEL_X 172
233 #define HIGH_SCORE_Y 163
234 #define HIGH_LEVEL_Y 172
236 #elif (LCD_WIDTH == 220) && (LCD_HEIGHT == 176)
238 #define BLOCK_WIDTH 8
239 #define BLOCK_HEIGHT 8
240 #define BOARD_X 27
241 #define BOARD_Y 5
242 #define PREVIEW_X 158
243 #define PREVIEW_Y 130
244 #define LABEL_X 147
245 #define SCORE_Y 20
246 #define LEVEL_Y 65
247 #define LINES_Y 100
249 #elif (LCD_WIDTH == 176) && (LCD_HEIGHT == 132)
251 #define BLOCK_WIDTH 6
252 #define BLOCK_HEIGHT 6
253 #define BOARD_X 25
254 #define BOARD_Y 1
255 #define PREVIEW_X 126
256 #define PREVIEW_Y 102
257 #define LABEL_X 112
258 #define SCORE_Y 17
259 #define LEVEL_Y 49
260 #define LINES_Y 81
262 #elif (LCD_WIDTH == 176) && (LCD_HEIGHT == 220)
264 #define BLOCK_WIDTH 10
265 #define BLOCK_HEIGHT 10
266 #define BOARD_X 6
267 #define BOARD_Y 10
268 #define PREVIEW_X 124
269 #define PREVIEW_Y 167
270 #define LABEL_X 117
271 #define SCORE_Y 24
272 #define LEVEL_Y 65
273 #define LINES_Y 103
274 #elif (LCD_WIDTH == 160) && (LCD_HEIGHT == 128)
276 #define BLOCK_WIDTH 6
277 #define BLOCK_HEIGHT 6
278 #define BOARD_X 22
279 #define BOARD_Y 3
280 #define PREVIEW_X 114
281 #define PREVIEW_Y 100
282 #define LABEL_X 101
283 #define SCORE_Y 17
284 #define LEVEL_Y 49
285 #define LINES_Y 82
287 #elif (LCD_WIDTH == 128) && (LCD_HEIGHT == 128)
289 #define BLOCK_WIDTH 6
290 #define BLOCK_HEIGHT 6
291 #define BOARD_X 4
292 #define BOARD_Y 3
293 #define PREVIEW_X 84
294 #define PREVIEW_Y 100
295 #define LABEL_X 71
296 #define SCORE_Y 17
297 #define LEVEL_Y 49
298 #define LINES_Y 82
300 #elif (LCD_WIDTH == 132) && (LCD_HEIGHT == 80)
302 #define BLOCK_WIDTH 4
303 #define BLOCK_HEIGHT 4
304 #define BOARD_X 10
305 #define BOARD_Y 0
306 #define PREVIEW_X 89
307 #define PREVIEW_Y 61
308 #define LABEL_X 78
309 #define SCORE_Y 10
310 #define LEVEL_Y 30
311 #define LINES_Y 50
313 #elif (LCD_WIDTH == 128) && (LCD_HEIGHT == 64)
315 #define BLOCK_WIDTH 3
316 #define BLOCK_HEIGHT 3
317 #define BOARD_X 9
318 #define BOARD_Y 3
319 #define PREVIEW_X 53
320 #define PREVIEW_Y 5
321 #define LABEL_X 70
322 #define SCORE_Y 32
323 #define LEVEL_Y 13
324 #define LINES_Y 51
326 #elif (LCD_WIDTH == 112) && (LCD_HEIGHT == 64)
328 #define BLOCK_WIDTH 4
329 #define BLOCK_HEIGHT 3
330 #define BOARD_X 9
331 #define BOARD_Y 3
332 #define PREVIEW_X 59
333 #define PREVIEW_Y 5
334 #define LABEL_X 59
335 #define SCORE_Y 32
336 #define LEVEL_Y 13
337 #define LEVEL_X 78
338 #define LINES_Y 51
340 #elif (LCD_WIDTH == 138) && (LCD_HEIGHT == 110)
342 #define BLOCK_WIDTH 5
343 #define BLOCK_HEIGHT 5
344 #define BOARD_X 14
345 #define BOARD_Y 0
346 #define PREVIEW_X 98
347 #define PREVIEW_Y 88
348 #define LABEL_X 80
349 #define SCORE_Y 15
350 #define LEVEL_Y 45
351 #define LINES_Y 74
353 #endif
355 #ifndef LEVEL_X
356 #define LEVEL_X LABEL_X
357 #endif
359 #ifndef LINES_X
360 #define LINES_X LABEL_X
361 #endif
363 #define MYLCD(fn) rb->lcd_ ## fn
365 extern const fb_data rockblox_background[];
367 #else /* HAVE_LCD_CHARCELLS */
369 #define BOARD_HEIGHT 14
371 #define BLOCK_WIDTH 1
372 #define BLOCK_HEIGHT 1
373 #define BOARD_X 5
374 #define BOARD_Y 0
375 #define PREVIEW_X 15
376 #define PREVIEW_Y 1
378 #define MYLCD(fn) pgfx_ ## fn
380 #endif
382 /* <<Explanation on Rockblox shapes>>
385 %% - O has 1 orientation
387 %% %
388 %% %% - Z has 2 orientations
391 %% %
392 %% %% - S has 2 orientations
396 % %%%% - I has 2 orientations
399 % %%
400 % % % %%% - L has 4 orientations
401 %% %%% % %
403 % %%s
404 % % % %%% - J has 4 orientations
405 %% %%% % %
407 % % %%%
408 %% % %% % - T has 4 orientations
409 % %%% %
413 /* must have variable */
414 static struct plugin_api *rb;
416 static bool gameover = false;
417 /* c=current f=figure o=orientation n=next */
418 static int lines = 0, level = 0, score = 0, cx, cy, cf, co, nf;
419 static short board[BOARD_HEIGHT][BOARD_WIDTH]; /* 20 rows of 10 blocks */
421 #ifdef SCROLL_WHEEL
422 int wheel_events = 0, last_wheel_event = 0;
423 bool wheel_enabled = false;
424 #endif
426 static const short scoring[4] = { /* scoring for each number of lines */
427 #if BOARD_HEIGHT == 20
428 40 /* single */ , 100 /* double */ , 300 /* triple */ , 1200 /* rockblox */
429 #elif BOARD_HEIGHT == 14 /* Player special values */
430 60 /* single */ , 150 /* double */ , 500 /* triple */ , 2000 /* rockblox */
431 #endif
434 struct figure
436 #if LCD_DEPTH >= 2
437 unsigned short color[3]; /* color of figure (light,middle,shadow) */
438 #endif
439 unsigned short max_or; /* max orientations */
440 signed short shapeX[4], shapeY[4]; /* implementation of figures */
443 /* array of figures */
444 figures[BLOCKS_NUM] = {
445 /* O */
447 #if LCD_DEPTH >= 16
448 {LCD_RGBPACK (153, 255, 255), LCD_RGBPACK(0, 255, 255),
449 LCD_RGBPACK(0,153,153)},
450 #elif LCD_DEPTH == 2
451 {LCD_WHITE, LCD_LIGHTGRAY, LCD_DARKGRAY},
452 #endif
454 {-1, 0, -1, 0},
455 {0, 0, 1, 1}
457 /* I */
459 #if LCD_DEPTH >= 16
460 {LCD_RGBPACK (255, 153, 128), LCD_RGBPACK (255, 0, 0),
461 LCD_RGBPACK (153, 0, 0)},
462 #elif LCD_DEPTH == 2
463 {LCD_WHITE, LCD_LIGHTGRAY, LCD_DARKGRAY},
464 #endif
466 {-2, -1, 0, 1},
467 {0, 0, 0, 0}
469 /* 'Z' */
471 #if LCD_DEPTH >= 16
472 {LCD_RGBPACK (153, 255, 153), LCD_RGBPACK (0, 255, 0),
473 LCD_RGBPACK (0, 153, 0)},
474 #elif LCD_DEPTH == 2
475 {LCD_WHITE, LCD_LIGHTGRAY, LCD_DARKGRAY},
476 #endif
478 {0, 1, -1, 0},
479 {0, 0, 1, 1}
481 /* 'S' */
483 #if LCD_DEPTH >= 16
484 {LCD_RGBPACK (153, 153, 255), LCD_RGBPACK (0, 0, 255),
485 LCD_RGBPACK (0, 0, 153)},
486 #elif LCD_DEPTH == 2
487 {LCD_WHITE, LCD_LIGHTGRAY, LCD_DARKGRAY},
488 #endif
490 {-1, 0, 0, 1},
491 {0, 0, 1, 1}
493 /* 'L' */
495 #if LCD_DEPTH >= 16
496 {LCD_RGBPACK (255, 255, 153), LCD_RGBPACK (255, 255, 0),
497 LCD_RGBPACK (153, 153, 0)},
498 #elif LCD_DEPTH == 2
499 {LCD_WHITE, LCD_LIGHTGRAY, LCD_DARKGRAY},
500 #endif
502 {-1, 0, 1, 1},
503 {0, 0, 0, 1}
505 /* 'J' */
507 #if LCD_DEPTH >= 16
508 {LCD_RGBPACK (255, 153, 255), LCD_RGBPACK (255, 0, 255),
509 LCD_RGBPACK (153, 0, 153)},
510 #elif LCD_DEPTH == 2
511 {LCD_WHITE, LCD_LIGHTGRAY, LCD_DARKGRAY},
512 #endif
514 {-1, 0, 1, -1},
515 {0, 0, 0, 1}
517 /* 'T' */
519 #if LCD_DEPTH >= 16
520 {LCD_RGBPACK (204, 204, 204), LCD_RGBPACK (153, 153, 153),
521 LCD_RGBPACK (85, 85, 85)},
522 #elif LCD_DEPTH == 2
523 {LCD_WHITE, LCD_LIGHTGRAY, LCD_DARKGRAY},
524 #endif
526 {-1, 0, 1, 0},
527 {0, 0, 0, 1}
531 /* Rockbox File System only supports full filenames inc dir */
532 #define HIGH_SCORE PLUGIN_GAMES_DIR "/rockblox.score"
533 #define MAX_HIGH_SCORES 5
534 /* Default High Scores... */
535 struct highscore Highest[MAX_HIGH_SCORES];
537 /* get random number from (0) to (range-1) */
538 static int t_rand (int range)
540 return rb->rand () % range;
543 /* init the board array to have no blocks */
544 static void init_board (void)
546 int i, j;
547 for (i = 0; i < BOARD_WIDTH; i++)
548 for (j = 0; j < BOARD_HEIGHT; j++)
549 board[j][i] = EMPTY_BLOCK;
552 /* show the score, level and lines */
553 static void show_details (void)
555 char str[25]; /* for strings */
557 #ifdef HAVE_LCD_BITMAP
558 #if LCD_DEPTH >= 2
559 rb->lcd_set_foreground (LCD_BLACK);
560 rb->lcd_set_background (LCD_WHITE);
561 #endif
562 rb->snprintf (str, sizeof (str), "%d", score);
563 rb->lcd_putsxy (LABEL_X, SCORE_Y, str);
564 rb->snprintf (str, sizeof (str), "%d", level);
565 rb->lcd_putsxy (LEVEL_X, LEVEL_Y, str);
566 rb->snprintf (str, sizeof (str), "%d", lines);
567 rb->lcd_putsxy (LINES_X, LINES_Y, str);
568 #else /* HAVE_LCD_CHARCELLS */
569 rb->snprintf (str, sizeof (str), "L%d/%d", level, lines);
570 rb->lcd_puts (5, 0, str);
571 rb->snprintf (str, sizeof (str), "S%d", score);
572 rb->lcd_puts (5, 1, str);
573 #endif
576 static void init_rockblox (void)
578 #ifdef HIGH_SCORE_Y
579 int i;
580 char str[25]; /* for strings */
581 #endif
582 highscore_update(score, level, Highest, MAX_HIGH_SCORES);
584 level = 1;
585 lines = 0;
586 score = 0;
587 gameover = false;
588 nf = t_rand (BLOCKS_NUM);
589 init_board ();
590 #ifdef HAVE_LCD_BITMAP
591 rb->lcd_bitmap (rockblox_background, 0, 0, LCD_WIDTH, LCD_HEIGHT);
592 #else /* HAVE_LCD_CHARCELLS */
593 pgfx_display (0, 0);
594 pgfx_display_block (3, 0, 3, 1);
595 pgfx_display_block (4, 0, 3, 0);
596 pgfx_clear_display();
597 pgfx_fillrect (3, 0, 2, 14);
598 pgfx_fillrect (15, 7, 2, 7);
599 pgfx_update();
600 #endif
601 show_details ();
602 #ifdef HIGH_SCORE_Y
603 for (i = MAX_HIGH_SCORES-1; i>=0; i--)
605 rb->snprintf (str, sizeof (str), "%06d L%1d", Highest[i].score, Highest[i].level);
606 rb->lcd_putsxy (HIGH_LABEL_X, HIGH_SCORE_Y + (10 * ((MAX_HIGH_SCORES-1) - i)), str);
608 #endif
611 static inline int level_speed(int level)
613 #if BOARD_HEIGHT == 20
614 return (5*HZ) / (level + 9);
615 #elif BOARD_HEIGHT == 14
616 return (7*HZ) / (level + 9);
617 #endif
620 static int getRelativeX (int figure, int square, int orientation)
622 switch (orientation) {
623 case 0:
624 return figures[figure].shapeX[square];
625 case 1:
626 return figures[figure].shapeY[square];
627 case 2:
628 return -figures[figure].shapeX[square];
629 case 3:
630 return -figures[figure].shapeY[square];
631 default:
632 return 0;
636 static int getRelativeY (int figure, int square, int orientation)
638 switch (orientation) {
639 case 0:
640 return figures[figure].shapeY[square];
641 case 1:
642 return -figures[figure].shapeX[square];
643 case 2:
644 return -figures[figure].shapeY[square];
645 case 3:
646 return figures[figure].shapeX[square];
647 default:
648 return 0;
652 /* redraw the while board on the screen */
653 static void refresh_board (void)
655 int i, j, x, y, block;
657 #if LCD_DEPTH >= 2
658 rb->lcd_set_foreground (LCD_BLACK);
659 #elif LCD_DEPTH == 1
660 MYLCD(set_drawmode) (DRMODE_SOLID | DRMODE_INVERSEVID);
661 #endif
663 MYLCD(fillrect) (BOARD_X, BOARD_Y, BOARD_WIDTH * BLOCK_WIDTH,
664 BOARD_HEIGHT * BLOCK_HEIGHT);
666 #if LCD_DEPTH == 1
667 MYLCD(set_drawmode) (DRMODE_SOLID);
668 #endif
670 for (i = 0; i < BOARD_WIDTH; i++)
671 for (j = 0; j < BOARD_HEIGHT; j++) {
672 block = board[j][i];
673 if (block != EMPTY_BLOCK) {
674 #ifdef HAVE_LCD_BITMAP
675 #if LCD_DEPTH >= 2
676 /* middle drawing */
677 rb->lcd_set_foreground (figures[block].color[1]);
678 #endif
679 rb->lcd_fillrect (BOARD_X + i * BLOCK_WIDTH,
680 BOARD_Y + j * BLOCK_HEIGHT,
681 BLOCK_WIDTH, BLOCK_HEIGHT);
682 #if LCD_DEPTH >= 2
683 /* light drawing */
684 rb->lcd_set_foreground (figures[block].color[0]);
685 #endif
686 rb->lcd_vline (BOARD_X + i * BLOCK_WIDTH,
687 BOARD_Y + j * BLOCK_HEIGHT,
688 BOARD_Y + (j + 1) * BLOCK_HEIGHT - 2);
689 rb->lcd_hline (BOARD_X + i * BLOCK_WIDTH,
690 BOARD_X + (i + 1) * BLOCK_WIDTH - 2,
691 BOARD_Y + j * BLOCK_HEIGHT);
692 #if LCD_DEPTH >= 2
693 /* shadow drawing */
694 rb->lcd_set_foreground (figures[block].color[2]);
695 #endif
696 rb->lcd_vline (BOARD_X + (i + 1) * BLOCK_WIDTH - 1,
697 BOARD_Y + j * BLOCK_HEIGHT + 1,
698 BOARD_Y + (j + 1) * BLOCK_HEIGHT - 1);
699 rb->lcd_hline (BOARD_X + i * BLOCK_WIDTH + 1,
700 BOARD_X + (i + 1) * BLOCK_WIDTH - 1,
701 BOARD_Y + (j + 1) * BLOCK_HEIGHT - 1);
702 #else /* HAVE_LCD_CHARCELLS */
703 pgfx_drawpixel (BOARD_X + i, BOARD_Y + j);
704 #endif
708 for (i = 0; i < 4; i++) {
709 x = getRelativeX (cf, i, co) + cx;
710 y = getRelativeY (cf, i, co) + cy;
711 #ifdef HAVE_LCD_BITMAP
712 #if LCD_DEPTH >= 2
713 rb->lcd_set_foreground (figures[cf].color[1]); /* middle drawing */
714 #endif
715 rb->lcd_fillrect (BOARD_X + x * BLOCK_WIDTH,
716 BOARD_Y + y * BLOCK_HEIGHT,
717 BLOCK_WIDTH, BLOCK_HEIGHT);
718 #if LCD_DEPTH >= 2
719 rb->lcd_set_foreground (figures[cf].color[0]); /* light drawing */
720 #endif
721 rb->lcd_vline (BOARD_X + x * BLOCK_WIDTH, BOARD_Y + y * BLOCK_HEIGHT,
722 BOARD_Y + (y + 1) * BLOCK_HEIGHT - 2);
723 rb->lcd_hline (BOARD_X + x * BLOCK_WIDTH,
724 BOARD_X + (x + 1) * BLOCK_WIDTH - 2,
725 BOARD_Y + y * BLOCK_HEIGHT);
726 #if LCD_DEPTH >= 2
727 rb->lcd_set_foreground (figures[cf].color[2]); /* shadow drawing */
728 #endif
729 rb->lcd_vline (BOARD_X + (x + 1) * BLOCK_WIDTH - 1,
730 BOARD_Y + y * BLOCK_HEIGHT + 1,
731 BOARD_Y + (y + 1) * BLOCK_HEIGHT - 1);
732 rb->lcd_hline (BOARD_X + x * BLOCK_WIDTH + 1,
733 BOARD_X + (x + 1) * BLOCK_WIDTH - 1,
734 BOARD_Y + (y + 1) * BLOCK_HEIGHT - 1);
735 #else /* HAVE_LCD_CHARCELLS */
736 pgfx_drawpixel (BOARD_X + x, BOARD_Y + y);
737 #endif
739 MYLCD(update) ();
742 static bool canMoveTo (int x, int y, int newOrientation)
744 int i, rx, ry;
745 for (i = 0; i < 4; i++) {
746 ry = getRelativeY (cf, i, newOrientation) + y;
747 rx = getRelativeX (cf, i, newOrientation) + x;
748 if ((rx < 0 || rx >= BOARD_WIDTH) ||
749 (ry < 0 || ry >= BOARD_HEIGHT) || (board[ry][rx] != EMPTY_BLOCK))
750 return false;
752 return true;
755 /* draws the preview of next block in the preview window */
756 static void draw_next_block (void)
758 int i, rx, ry;
759 /* clear preview window first */
760 #if LCD_DEPTH >= 2
761 rb->lcd_set_foreground (LCD_BLACK);
762 #elif LCD_DEPTH == 1
763 MYLCD(set_drawmode) (DRMODE_SOLID | DRMODE_INVERSEVID);
764 #endif
766 /* 4x4 */
767 MYLCD(fillrect) (PREVIEW_X, PREVIEW_Y, BLOCK_WIDTH * 4, BLOCK_HEIGHT * 4);
769 #if LCD_DEPTH == 1
770 MYLCD(set_drawmode) (DRMODE_SOLID);
771 #endif
773 /* draw the lightgray rectangles */
774 #if LCD_DEPTH >= 16
775 rb->lcd_set_foreground (LCD_RGBPACK (40, 40, 40));
776 #elif LCD_DEPTH == 2
777 rb->lcd_set_foreground (LCD_DARKGRAY);
778 #endif
780 #if LCD_DEPTH >= 2
781 for (rx = 0; rx < 4; rx++)
782 for (ry = 0; ry < 4; ry++)
783 rb->lcd_drawrect (PREVIEW_X + rx * BLOCK_WIDTH,
784 PREVIEW_Y + ry * BLOCK_HEIGHT, BLOCK_WIDTH,
785 BLOCK_HEIGHT);
786 #endif
788 /* draw the figure */
789 for (i = 0; i < 4; i++) {
790 rx = getRelativeX (nf, i, 0) + 2;
791 ry = getRelativeY (nf, i, 0) + 2;
792 #ifdef HAVE_LCD_BITMAP
793 #if LCD_DEPTH >= 2
794 rb->lcd_set_foreground (figures[nf].color[1]); /* middle drawing */
795 #endif
796 rb->lcd_fillrect (PREVIEW_X + rx * BLOCK_WIDTH,
797 PREVIEW_Y + ry * BLOCK_HEIGHT,
798 BLOCK_WIDTH, BLOCK_HEIGHT);
799 #if LCD_DEPTH >= 2
800 rb->lcd_set_foreground (figures[nf].color[0]); /* light drawing */
801 #endif
802 rb->lcd_vline (PREVIEW_X + rx * BLOCK_WIDTH,
803 PREVIEW_Y + ry * BLOCK_HEIGHT,
804 PREVIEW_Y + (ry + 1) * BLOCK_HEIGHT - 2);
805 rb->lcd_hline (PREVIEW_X + rx * BLOCK_WIDTH,
806 PREVIEW_X + (rx + 1) * BLOCK_WIDTH - 2,
807 PREVIEW_Y + ry * BLOCK_HEIGHT);
808 #if LCD_DEPTH >= 2
809 rb->lcd_set_foreground (figures[nf].color[2]); /* shadow drawing */
810 #endif
811 rb->lcd_vline (PREVIEW_X + (rx + 1) * BLOCK_WIDTH - 1,
812 PREVIEW_Y + ry * BLOCK_HEIGHT + 1,
813 PREVIEW_Y + (ry + 1) * BLOCK_HEIGHT - 1);
814 rb->lcd_hline (PREVIEW_X + rx * BLOCK_WIDTH + 1,
815 PREVIEW_X + (rx + 1) * BLOCK_WIDTH - 1,
816 PREVIEW_Y + (ry + 1) * BLOCK_HEIGHT - 1);
817 #else /* HAVE_LCD_CHARCELLS */
818 pgfx_drawpixel (PREVIEW_X + rx, PREVIEW_Y + ry);
819 #endif
824 /* move the block to a relative location */
825 static void move_block (int x, int y, int o)
827 if (canMoveTo (cx + x, cy + y, o)) {
828 cy += y;
829 cx += x;
830 co = o;
834 /* try to add a new block to play with (return true if gameover) */
835 static void new_block (void)
837 cy = 1;
838 cx = 5;
839 cf = nf;
840 co = 0; /* start at the same orientation all time */
841 nf = t_rand (BLOCKS_NUM);
842 gameover = !canMoveTo (cx, cy, co);
844 draw_next_block ();
848 /* check for filled lines and do what necessary */
849 static int check_lines (void)
851 int i, j, y;
852 int rockblox = 0;
854 for (j = 0; j < BOARD_HEIGHT; j++) {
855 for (i = 0; ((i < BOARD_WIDTH) && (board[j][i] != EMPTY_BLOCK)); i++);
856 if (i == BOARD_WIDTH) { /* woo hoo, we have a line */
857 rockblox++;
858 for (y = j; y > 0; y--)
859 for (i = 0; i < BOARD_WIDTH; i++)
860 board[y][i] = board[y - 1][i]; /* fall line */
864 return rockblox;
867 /* moves down the figure and returns true if gameover */
868 static void move_down (void)
870 int l, i, rx, ry;
872 if (!canMoveTo (cx, cy + 1, co)) {
873 /* save figure to board */
874 for (i = 0; i < 4; i++) {
875 rx = getRelativeX (cf, i, co) + cx;
876 ry = getRelativeY (cf, i, co) + cy;
877 board[ry][rx] = cf;
879 /* check if formed some lines */
880 l = check_lines ();
881 if (l) {
882 /* the original scoring from "http://en.wikipedia.org/wiki/Rockblox" */
883 score += scoring[l - 1] * level;
884 lines += l;
885 level = (int) lines / 10 + 1;
888 /* show details */
889 show_details ();
891 /* generate a new figure */
892 new_block ();
893 } else
894 move_block (0, 1, co);
897 static int rockblox_loop (void)
899 int button;
900 int lastbutton = BUTTON_NONE;
901 long next_down_tick = *rb->current_tick + level_speed(level);
903 new_block ();
905 while (1) {
906 #ifdef HAS_BUTTON_HOLD
907 if (rb->button_hold ()) {
908 /* Turn on backlight timeout (revert to settings) */
909 backlight_use_settings(rb); /* backlight control in lib/helper.c */
910 rb->splash(0, "Paused");
911 while (rb->button_hold ())
912 rb->sleep(HZ/10);
914 /* Turn off backlight timeout */
915 backlight_force_on(rb); /* backlight control in lib/helper.c */
917 /* get rid of the splash text */
918 rb->lcd_bitmap (rockblox_background, 0, 0, LCD_WIDTH, LCD_HEIGHT);
919 show_details ();
920 draw_next_block ();
921 refresh_board ();
923 #endif
925 button = rb->button_get_w_tmo (MAX(next_down_tick - *rb->current_tick, 1));
926 switch (button) {
927 #ifdef ROCKBLOX_RC_OFF
928 case ROCKBLOX_RC_OFF:
929 #endif
930 case ROCKBLOX_OFF:
931 return PLUGIN_OK;
933 #if defined(ROCKBLOX_ROTATE)
934 case ROCKBLOX_ROTATE:
935 #endif
936 case ROCKBLOX_ROTATE_RIGHT:
937 case ROCKBLOX_ROTATE_RIGHT | BUTTON_REPEAT:
938 #ifdef SCROLL_WHEEL
939 /* if the wheel is disabled, add an event to the stack. */
940 if(wheel_enabled == false)
941 wheel_events++;
943 /* if it's enabled, go ahead and rotate.. */
944 if(wheel_enabled)
945 #endif
946 move_block (0, 0, (co + 1) % figures[cf].max_or);
947 break;
949 case ROCKBLOX_ROTATE_LEFT:
950 case ROCKBLOX_ROTATE_LEFT | BUTTON_REPEAT:
951 #ifdef SCROLL_WHEEL
952 if(wheel_enabled == false)
953 wheel_events++;
955 if(wheel_enabled)
956 #endif
957 move_block (0, 0,
958 (co + figures[cf].max_or -
959 1) % figures[cf].max_or);
960 break;
962 #ifdef ROCKBLOX_ROTATE_RIGHT2
963 case ROCKBLOX_ROTATE_RIGHT2:
964 move_block (0, 0, (co + 1) % figures[cf].max_or);
965 break;
966 #endif
968 case ROCKBLOX_DOWN:
969 case ROCKBLOX_DOWN | BUTTON_REPEAT:
970 move_block (0, 1, co);
971 break;
973 case ROCKBLOX_RIGHT:
974 case ROCKBLOX_RIGHT | BUTTON_REPEAT:
975 move_block (1, 0, co);
976 break;
978 case ROCKBLOX_LEFT:
979 case ROCKBLOX_LEFT | BUTTON_REPEAT:
980 move_block (-1, 0, co);
981 break;
983 case ROCKBLOX_DROP:
984 #ifdef ROCKBLOX_DROP_PRE
985 if (lastbutton != ROCKBLOX_DROP_PRE)
986 break;
987 #endif
988 while (canMoveTo (cx, cy + 1, co))
989 move_block (0, 1, co);
990 break;
991 #ifdef ROCKBLOX_RESTART
992 case ROCKBLOX_RESTART:
993 rb->splash (HZ * 1, "Restarting...");
994 init_rockblox ();
995 new_block ();
996 break;
997 #endif
999 default:
1000 if (rb->default_event_handler (button) == SYS_USB_CONNECTED)
1001 return PLUGIN_USB_CONNECTED;
1002 break;
1004 if (button != BUTTON_NONE)
1005 lastbutton = button;
1007 #ifdef SCROLL_WHEEL
1008 /* check if we should enable the scroll wheel, if events
1009 * begin to stack up... */
1010 if(wheel_enabled == false)
1012 /* stopped rotating the wheel, reset the count */
1013 if(wheel_events == last_wheel_event)
1015 last_wheel_event = 0;
1016 wheel_events = 0;
1018 /* rotated the wheel a while constantly, enable it. */
1019 else if(wheel_events > 3)
1021 wheel_enabled = true;
1024 /* this evens out the last event and the "current" event.
1025 * if we get an event next time through button reading, it will
1026 * remain ahead of last_event. if we don't, they'll end up equaling
1027 * each other.. thus, the scroll count will be reset. */
1028 if(wheel_enabled == false && wheel_events > last_wheel_event)
1029 last_wheel_event++;
1031 #endif
1033 if (TIME_AFTER(*rb->current_tick, next_down_tick)) {
1034 move_down ();
1035 next_down_tick += level_speed(level);
1036 if (TIME_AFTER(*rb->current_tick, next_down_tick))
1037 /* restart time "raster" when we had to wait longer than usual
1038 * (pause, game restart etc) */
1039 next_down_tick = *rb->current_tick + level_speed(level);
1042 if (gameover) {
1043 #if LCD_DEPTH >= 2
1044 rb->lcd_set_foreground (LCD_BLACK);
1045 #endif
1046 rb->splash (HZ * 2, "Game Over");
1047 init_rockblox ();
1048 new_block ();
1051 refresh_board ();
1054 return PLUGIN_OK;
1057 enum plugin_status plugin_start (struct plugin_api *api, void *parameter)
1059 int ret;
1061 (void) parameter;
1062 rb = api;
1064 rb->srand (*rb->current_tick);
1066 /* Load HighScore if any */
1067 highscore_init(rb);
1068 highscore_load(HIGH_SCORE,Highest,MAX_HIGH_SCORES);
1070 #if LCD_DEPTH > 1
1071 rb->lcd_set_backdrop(NULL);
1072 #endif
1074 #ifdef HAVE_LCD_BITMAP
1075 rb->lcd_setfont (FONT_SYSFIXED);
1076 #else
1077 if (!pgfx_init(rb, 4, 2))
1079 rb->splash(HZ*2, "Old LCD :(");
1080 return PLUGIN_OK;
1082 #endif
1083 /* Turn off backlight timeout */
1084 backlight_force_on(rb); /* backlight control in lib/helper.c */
1086 init_rockblox ();
1087 ret = rockblox_loop ();
1089 #ifdef HAVE_LCD_BITMAP
1090 rb->lcd_setfont (FONT_UI);
1091 #else
1092 pgfx_release();
1093 #endif
1094 /* Save user's HighScore */
1095 highscore_save(HIGH_SCORE,Highest,MAX_HIGH_SCORES);
1096 /* Restore user's original backlight setting */
1097 rb->backlight_set_timeout (rb->global_settings->backlight_timeout);
1099 return ret;