FS#8708: D2/m:robe500 touchscreen keymaps by Andreas Mueller.
[kugel-rb.git] / apps / plugins / xobox.c
blob331650eddc2482b03ded21b26fa8e2134b8788c2
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2006 Eli Sherer
11 * 2007 Antoine Cellerier
13 * All files in this archive are subject to the GNU General Public License.
14 * See the file COPYING in the source tree root for full license agreement.
16 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
17 * KIND, either express or implied.
19 ****************************************************************************/
21 #include "plugin.h"
22 #include "helper.h"
24 PLUGIN_HEADER
26 #if (CONFIG_KEYPAD == IRIVER_H100_PAD) || (CONFIG_KEYPAD == IRIVER_H300_PAD)
28 #define QUIT BUTTON_OFF
29 #define LEFT BUTTON_LEFT
30 #define RIGHT BUTTON_RIGHT
31 #define PAUSE BUTTON_MODE
32 #define UP BUTTON_UP
33 #define DOWN BUTTON_DOWN
35 #define RC_QUIT BUTTON_RC_STOP
37 #elif (CONFIG_KEYPAD == ARCHOS_AV300_PAD)
39 #define QUIT BUTTON_OFF
40 #define LEFT BUTTON_LEFT
41 #define RIGHT BUTTON_RIGHT
42 #define PAUSE BUTTON_ON
43 #define UP BUTTON_UP
44 #define DOWN BUTTON_DOWN
46 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
47 (CONFIG_KEYPAD == IPOD_3G_PAD) || \
48 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
50 #define QUIT (BUTTON_SELECT | BUTTON_MENU)
51 #define LEFT BUTTON_LEFT
52 #define RIGHT BUTTON_RIGHT
53 #define PAUSE BUTTON_SELECT
54 #define MENU_UP BUTTON_SCROLL_FWD
55 #define MENU_DOWN BUTTON_SCROLL_BACK
56 #define UP BUTTON_MENU
57 #define DOWN BUTTON_PLAY
59 #elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD
61 #define QUIT BUTTON_POWER
62 #define LEFT BUTTON_LEFT
63 #define RIGHT BUTTON_RIGHT
64 #define UP BUTTON_UP
65 #define DOWN BUTTON_DOWN
66 #define PAUSE BUTTON_PLAY
68 #elif (CONFIG_KEYPAD == GIGABEAT_PAD)
70 #define QUIT BUTTON_POWER
71 #define LEFT BUTTON_LEFT
72 #define RIGHT BUTTON_RIGHT
73 #define UP BUTTON_UP
74 #define DOWN BUTTON_DOWN
75 #define PAUSE BUTTON_A
77 #elif (CONFIG_KEYPAD == SANSA_E200_PAD) || \
78 (CONFIG_KEYPAD == SANSA_C200_PAD)
80 #define QUIT BUTTON_POWER
81 #define LEFT BUTTON_LEFT
82 #define RIGHT BUTTON_RIGHT
83 #define UP BUTTON_UP
84 #define DOWN BUTTON_DOWN
85 #define PAUSE BUTTON_REC
88 #elif CONFIG_KEYPAD == IRIVER_H10_PAD
90 #define QUIT BUTTON_POWER
91 #define LEFT BUTTON_LEFT
92 #define RIGHT BUTTON_RIGHT
93 #define UP BUTTON_SCROLL_UP
94 #define DOWN BUTTON_SCROLL_DOWN
95 #define PAUSE BUTTON_PLAY
97 #elif CONFIG_KEYPAD == RECORDER_PAD
99 #define QUIT BUTTON_OFF
100 #define LEFT BUTTON_LEFT
101 #define RIGHT BUTTON_RIGHT
102 #define DOWN BUTTON_DOWN
103 #define UP BUTTON_UP
104 #define PAUSE BUTTON_PLAY
106 #elif CONFIG_KEYPAD == ONDIO_PAD
108 #define QUIT BUTTON_OFF
109 #define LEFT BUTTON_LEFT
110 #define RIGHT BUTTON_RIGHT
111 #define DOWN BUTTON_DOWN
112 #define UP BUTTON_UP
113 #define PAUSE BUTTON_MENU
115 #elif (CONFIG_KEYPAD == GIGABEAT_S_PAD)
117 #define QUIT BUTTON_BACK
118 #define LEFT BUTTON_LEFT
119 #define RIGHT BUTTON_RIGHT
120 #define UP BUTTON_UP
121 #define DOWN BUTTON_DOWN
122 #define PAUSE BUTTON_PLAY
124 #elif (CONFIG_KEYPAD == MROBE100_PAD)
126 #define QUIT BUTTON_POWER
127 #define LEFT BUTTON_LEFT
128 #define RIGHT BUTTON_RIGHT
129 #define UP BUTTON_UP
130 #define DOWN BUTTON_DOWN
131 #define PAUSE BUTTON_DISPLAY
133 #elif CONFIG_KEYPAD == IAUDIO_M3_PAD
135 #define QUIT BUTTON_RC_REC
136 #define LEFT BUTTON_RC_REW
137 #define RIGHT BUTTON_RC_FF
138 #define UP BUTTON_RC_VOL_UP
139 #define DOWN BUTTON_RC_VOL_DOWN
140 #define PAUSE BUTTON_RC_PLAY
142 #elif CONFIG_KEYPAD == COWOND2_PAD
144 #define QUIT BUTTON_POWER
146 #else
147 #error No keymap defined!
148 #endif
150 #ifdef HAVE_TOUCHPAD
151 #ifndef QUIT
152 #define QUIT BUTTON_TOPLEFT
153 #endif
154 #ifndef LEFT
155 #define LEFT BUTTON_MIDLEFT
156 #endif
157 #ifndef RIGHT
158 #define RIGHT BUTTON_MIDRIGHT
159 #endif
160 #ifndef UP
161 #define UP BUTTON_TOPMIDDLE
162 #endif
163 #ifndef DOWN
164 #define DOWN BUTTON_BOTTOMMIDDLE
165 #endif
166 #ifndef PAUSE
167 #define PAUSE BUTTON_CENTER
168 #endif
169 #endif
171 #define MOVE_NO 0 /* player movement */
172 #define MOVE_UP 1 /* 1 */
173 #define MOVE_DN 2 /* 3 0 4 */
174 #define MOVE_LT 3 /* 2 */
175 #define MOVE_RT 4
177 /* ball movement (12 ways) */
178 /* UUL UR */
179 /* UL UR */
180 /* ULL . URR */
181 /* DLL DRR */
182 /* DL DR */
183 /* DDL DDR */
185 #define DIR_UU (1<<7)
186 #define DIR_U (1<<6)
187 #define DIR_RR (1<<5)
188 #define DIR_R (1<<4)
189 #define DIR_DD (1<<3)
190 #define DIR_D (1<<2)
191 #define DIR_LL (1<<1)
192 #define DIR_L (1<<0)
194 #define MOVE_UUR ( DIR_UU | DIR_R )
195 #define MOVE_UR ( DIR_U | DIR_R )
196 #define MOVE_URR ( DIR_U | DIR_RR )
197 #define MOVE_DRR ( DIR_D | DIR_RR )
198 #define MOVE_DR ( DIR_D | DIR_R )
199 #define MOVE_DDR ( DIR_DD | DIR_R )
200 #define MOVE_DDL ( DIR_DD | DIR_L )
201 #define MOVE_DL ( DIR_D | DIR_L )
202 #define MOVE_DLL ( DIR_D | DIR_LL )
203 #define MOVE_ULL ( DIR_U | DIR_LL )
204 #define MOVE_UL ( DIR_U | DIR_L )
205 #define MOVE_UUL ( DIR_UU | DIR_L )
207 #if (LCD_WIDTH>112) && (LCD_HEIGHT>64)
208 # define CUBE_SIZE 8 /* 8x22=176 */
209 # define pos(a) ((a)>>3)
210 #else
211 # define CUBE_SIZE 4
212 # define pos(a) ((a)>>2)
213 #endif
215 #define STARTING_QIXES 2
216 #define MAX_LEVEL 10
217 #define MAX_QIXES MAX_LEVEL+STARTING_QIXES
218 #define BOARD_W ((int)(LCD_WIDTH/CUBE_SIZE))
219 #define BOARD_H ((int)(LCD_HEIGHT/CUBE_SIZE))
220 #define BOARD_X (LCD_WIDTH-BOARD_W*CUBE_SIZE)/2
221 #define BOARD_Y (LCD_HEIGHT-BOARD_H*CUBE_SIZE)/2
223 #ifdef HAVE_LCD_COLOR
224 #define CLR_RED LCD_RGBPACK(255,0,0) /* used to imply danger */
225 #define CLR_LTBLUE LCD_RGBPACK(125, 145, 180) /* used for frame and filling */
226 #define PLR_COL LCD_WHITE /* color used for the player */
227 #elif LCD_DEPTH>=2
228 #define CLR_RED LCD_DARKGRAY /* used to imply danger */
229 #define CLR_LTBLUE LCD_LIGHTGRAY /* used for frame and filling */
230 #define PLR_COL LCD_BLACK /* color used for the player */
231 #endif
233 #if LCD_DEPTH>=2
234 #define EMPTIED LCD_BLACK /* empty spot */
235 #define FILLED CLR_LTBLUE /* filled spot */
236 #define TRAIL CLR_RED /* the red trail of the player */
237 #define QIX LCD_WHITE
238 #else
239 #define EMPTIED 0
240 #define FILLED 1
241 #define TRAIL 2
242 #define QIX 3
243 #endif
244 #define UNCHECKED 0
245 #define CHECKED 1
246 #define PAINTED -1
247 #define PIC_QIX 0
248 #define PIC_PLAYER 1
250 #define MENU_START 0
251 #define MENU_QUIT 1
253 /* The time (in ms) for one iteration through the game loop - decrease this
254 to speed up the game - note that current_tick is (currently) only accurate
255 to 10ms.
257 static int speed = 6; /* CYCLETIME = (11-speed)*10 ms */
258 static int difficulty = 75; /* Percentage of screen that needs to be filled
259 * in order to win the game */
261 static struct plugin_api *rb;
263 MEM_FUNCTION_WRAPPERS(rb);
265 static bool quit = false;
267 static unsigned int board[BOARD_H][BOARD_W];
268 static int testboard[BOARD_H][BOARD_W];
270 #if CUBE_SIZE == 8
272 00011000 0x18 - 11100111 0xe7
273 00111100 0x3c - 11100111 0xe7
274 01111110 0x7e - 11000011 0xc3
275 11111111 0xff - 00000000 0x00
276 11111111 0xff - 00000000 0x00
277 01111110 0x7e - 11000011 0xc3
278 00111100 0x3c - 11100111 0xe7
279 00011000 0x18 - 11100111 0xe7
281 const unsigned char pics[2][8] = {
282 {0x18, 0x3c, 0x7e, 0xff, 0xff, 0x7e, 0x3c, 0x18}, /* Alien (QIX) */
283 {0xe7, 0xe7, 0xc3, 0x00, 0x00, 0xc3, 0xe7, 0xe7} /* Player (XONIX) */
285 #elif CUBE_SIZE == 4
287 0110 0x6 - 1001 0x9
288 1111 0xf - 0110 0x6
289 1111 0xf - 0110 0x6
290 0110 0x6 - 1001 0x9
292 const unsigned char pics[2][4] = {
293 {0x6, 0xf, 0xf, 0x6}, /* Alien (QIX) */
294 {0x9, 0x6, 0x6, 0x9} /* Player (XONIX) */
296 #else
297 #error Incorrect CUBE_SIZE value.
298 #endif
300 static struct qix
302 int velocity; /* velocity */
303 int x, y; /* position on screen */
304 int angle; /* angle */
305 } qixes[MAX_QIXES]; /* black_qix */
307 static struct splayer
309 int i, j; /* position on board */
310 int move, score, level, lives;
311 bool drawing;
312 bool gameover;
313 } player;
315 static int percentage_cache;
317 /*************************** STACK STUFF **********************/
319 /* the stack */
320 #define STACK_SIZE (2*BOARD_W*BOARD_H)
321 static struct pos
323 int x, y; /* position on board */
324 } stack[STACK_SIZE];
325 static int stackPointer;
327 static inline bool pop (struct pos *p)
329 if (stackPointer > 0) {
330 p->x = stack[stackPointer].x;
331 p->y = stack[stackPointer].y;
332 stackPointer--;
333 return true;
334 } else
335 return false; /* SE */
338 static inline bool push (struct pos *p)
340 if (stackPointer < STACK_SIZE - 1) {
341 stackPointer++;
342 stack[stackPointer].x = p->x;
343 stack[stackPointer].y = p->y;
344 return true;
345 } else
346 return false; /* SOF */
349 static inline void emptyStack (void)
351 stackPointer = 0;
354 /*********************** END OF STACK STUFF *********************/
356 /* calculate the new x coordinate of the ball according to angle and speed */
357 static inline int get_newx (int x, int len, int deg)
359 if (deg & DIR_R)
360 return x + len;
361 else if (deg & DIR_L)
362 return x - len;
363 else if (deg & DIR_RR)
364 return x + len * 2;
365 else /* (def & DIR_LL) */
366 return x - len * 2;
369 /* calculate the new y coordinate of the ball according to angle and speed */
370 static inline int get_newy (int y, int len, int deg)
372 if (deg & DIR_D)
373 return y + len;
374 else if (deg & DIR_U)
375 return y - len;
376 else if (deg & DIR_DD)
377 return y + len * 2;
378 else /* (deg & DIR_UU) */
379 return y - len * 2;
382 /* make random function get it's value from the device ticker */
383 static inline void randomize (void)
385 rb->srand (*rb->current_tick);
388 /* get a random number between 0 and range-1 */
389 static int t_rand (int range)
391 return rb->rand () % range;
394 /* initializes the test help board */
395 static void init_testboard (void)
397 int j; /* testboard */
398 for (j = 0; j < BOARD_H; j++)
399 /* UNCHEKED == (int)0 */
400 rb->memset( testboard[j], 0, BOARD_W * sizeof( int ) );
403 /* initializes the game board on with the player,qix's and black qix */
404 static void init_board (void)
406 int i, j;
407 for (j = 0; j < BOARD_H; j++)
408 for (i = 0; i < BOARD_W; i++) { /* make a nice cyan frame */
409 if ((i == 0) || (j <= 1) || (i == BOARD_W - 1)
410 || (j >= BOARD_H - 2))
411 board[j][i] = FILLED;
412 else
413 board[j][i] = EMPTIED;
416 /* (level+2) is the number of qixes */
417 for (j = 0; j < player.level + STARTING_QIXES; j++) {
418 qixes[j].velocity = t_rand (2) + 1; /* 1 or 2 pix-per-sec */
420 /* not on frame */
421 qixes[j].x = CUBE_SIZE*2 + 2*t_rand (((BOARD_W-4)*CUBE_SIZE)/2);
422 qixes[j].y = CUBE_SIZE*2 + 2*t_rand (((BOARD_H-4)*CUBE_SIZE)/2);
424 const int angle_table[] = {
425 MOVE_UUR, MOVE_UR, MOVE_URR, MOVE_DRR, MOVE_DR, MOVE_DDR,
426 MOVE_UUL, MOVE_UL, MOVE_ULL, MOVE_DLL, MOVE_DL, MOVE_DDL };
427 qixes[j].angle = angle_table[t_rand (12)];
428 #if CUBE_SIZE == 4
429 /* Work arround a nasty bug. FIXME */
430 if( qixes[j].angle & (DIR_LL|DIR_RR|DIR_UU|DIR_DD) )
431 qixes[j].velocity = 1;
432 #endif
434 /*black_qix.velocity=1;
435 black_qix.x=BOARD_X+(BOARD_W*CUBE_SIZE)/2-CUBE_SIZE/2;
436 black_qix.y=BOARD_Y+(BOARD_H*CUBE_SIZE)-CUBE_SIZE-CUBE_SIZE/2;
437 black_qix.angle=MOVE_UR; */
438 player.move = MOVE_NO;
439 player.drawing = false;
440 player.i = BOARD_W / 2;
441 player.j = 1;
443 percentage_cache = 0;
446 /* calculates the percentage of the screen filling */
447 static int percentage (void)
449 int i, j, filled = 0;
450 for (j = 2; j < BOARD_H - 2; j++)
451 for (i = 1; i < BOARD_W - 1; i++)
452 if (board[j][i] == FILLED)
453 filled++;
454 return (filled * 100) / ((BOARD_W - 2) * (BOARD_H - 4));
457 /* draw the board on with all the game figures */
458 static void refresh_board (void)
460 int i, j;
461 char str[25];
463 #if LCD_DEPTH>=2
464 rb->lcd_set_background (LCD_BLACK);
465 #else
466 rb->lcd_clear_display ();
467 #endif
468 for (j = 0; j < BOARD_H; j++)
470 unsigned last_color = board[j][0];
471 int last_i = 0;
472 for (i = 1; i < BOARD_W; i++) {
473 if( last_color != board[j][i] )
475 #if LCD_DEPTH>=2
476 rb->lcd_set_foreground (last_color);
477 #else
478 if (last_color != EMPTIED)
479 #endif
480 rb->lcd_fillrect (BOARD_X + CUBE_SIZE * (last_i),
481 BOARD_Y + CUBE_SIZE * j,
482 CUBE_SIZE * (i - last_i), CUBE_SIZE );
483 last_color = board[j][i];
484 last_i = i;
487 #if LCD_DEPTH>=2
488 rb->lcd_set_foreground (last_color);
489 #else
490 if (last_color != EMPTIED)
491 #endif
492 rb->lcd_fillrect (BOARD_X + CUBE_SIZE * (last_i),
493 BOARD_Y + CUBE_SIZE * j,
494 CUBE_SIZE * (i - last_i), CUBE_SIZE);
497 #if LCD_DEPTH>=2
498 rb->lcd_set_foreground (LCD_BLACK);
499 rb->lcd_set_background (CLR_LTBLUE);
500 #else
501 rb->lcd_set_drawmode (DRMODE_COMPLEMENT);
502 #endif
503 rb->snprintf (str, sizeof (str), "Level %d", player.level + 1);
504 rb->lcd_putsxy (BOARD_X, BOARD_Y, str);
505 rb->snprintf (str, sizeof (str), "%d%%", percentage_cache);
506 rb->lcd_putsxy (BOARD_X + CUBE_SIZE * BOARD_W - 24, BOARD_Y, str);
507 rb->snprintf (str, sizeof (str), "Score: %d", player.score);
508 rb->lcd_putsxy (BOARD_X, BOARD_Y + CUBE_SIZE * BOARD_H - 8, str);
509 rb->snprintf (str, sizeof (str), "%d Lives", player.lives);
510 #if LCD_DEPTH>=2
511 rb->lcd_putsxy (BOARD_X + CUBE_SIZE * BOARD_W - 60,
512 BOARD_Y + CUBE_SIZE * BOARD_H - 8, str);
513 #else
514 rb->lcd_putsxy (BOARD_X + CUBE_SIZE * BOARD_W - 40,
515 BOARD_Y + CUBE_SIZE * BOARD_H - 8, str);
516 #endif
518 #if LCD_DEPTH>=2
519 rb->lcd_set_foreground (PLR_COL);
520 rb->lcd_set_background (board[player.j][player.i]);
521 #endif
522 rb->lcd_mono_bitmap (pics[PIC_PLAYER], player.i * CUBE_SIZE + BOARD_X,
523 player.j * CUBE_SIZE + BOARD_Y, CUBE_SIZE, CUBE_SIZE);
525 #if LCD_DEPTH>=2
526 rb->lcd_set_background (EMPTIED);
527 rb->lcd_set_foreground (LCD_WHITE);
528 rb->lcd_set_drawmode (DRMODE_FG);
529 #else
530 rb->lcd_set_drawmode (DRMODE_FG);
531 #endif
532 for (j = 0; j < player.level + STARTING_QIXES; j++)
533 rb->lcd_mono_bitmap (pics[PIC_QIX], qixes[j].x + BOARD_X,
534 qixes[j].y + BOARD_Y, CUBE_SIZE, CUBE_SIZE);
535 #if LCD_DEPTH>=2
536 rb->lcd_set_foreground (LCD_BLACK);
537 #endif
538 rb->lcd_set_drawmode (DRMODE_SOLID);
540 rb->lcd_update ();
543 static inline int infested_area (int i, int j, int v)
545 struct pos p;
546 p.x = i;
547 p.y = j;
548 emptyStack ();
549 if (!push (&p))
550 return -1;
551 while (pop (&p)) {
552 if (testboard[p.y][p.x] == v) continue;
553 if (testboard[p.y][p.x] > UNCHECKED)
554 return 1; /* This area was previously flagged as infested */
555 testboard[p.y][p.x] = v;
556 if (board[p.y][p.x] == QIX)
557 return 1; /* Infested area */
559 struct pos p1 = { p.x+1, p.y };
560 if ((p1.x < BOARD_W)
561 && (board[p1.y][p1.x] != FILLED)
562 && (!push (&p1)))
563 return -1;
566 struct pos p1 = { p.x-1, p.y };
567 if ((p1.x >= 0)
568 && (board[p1.y][p1.x] != FILLED)
569 && (!push (&p1)))
570 return -1;
573 struct pos p1 = { p.x, p.y+1 };
574 if ((p1.y < BOARD_H)
575 && (board[p1.y][p1.x] != FILLED)
576 && (!push (&p1)))
577 return -1;
580 struct pos p1 = { p.x, p.y-1 };
581 if ((p1.y >= 0)
582 && (board[p1.y][p1.x] != FILLED)
583 && (!push (&p1)))
584 return -1;
587 return 0;
590 static inline int fill_area (int i, int j)
592 struct pos p;
593 p.x = i;
594 p.y = j;
595 int v = testboard[p.y][p.x];
596 emptyStack ();
597 if (!push (&p))
598 return -1;
599 while (pop (&p)) {
600 board[p.y][p.x] = FILLED;
601 testboard[p.y][p.x] = PAINTED;
603 struct pos p1 = { p.x+1, p.y };
604 if ((p1.x < BOARD_W)
605 && (testboard[p1.y][p1.x] == v)
606 && (!push (&p1)))
607 return -1;
610 struct pos p1 = { p.x-1, p.y };
611 if ((p1.x >= 0)
612 && (testboard[p1.y][p1.x] == v)
613 && (!push (&p1)))
614 return -1;
617 struct pos p1 = { p.x, p.y+1 };
618 if ((p1.y < BOARD_H)
619 && (testboard[p1.y][p1.x] == v)
620 && (!push (&p1)))
621 return -1;
624 struct pos p1 = { p.x, p.y-1 };
625 if ((p1.y >= 0)
626 && (testboard[p1.y][p1.x] == v)
627 && (!push (&p1)))
628 return -1;
631 return 0;
635 /* take care of stuff after xonix has landed on a filled spot */
636 static void complete_trail (int fill)
638 int i, j, ret;
639 for (j = 0; j < BOARD_H; j++) {
640 for (i = 0; i < BOARD_W; i++) {
641 if (board[j][i] == TRAIL) {
642 if (fill)
643 board[j][i] = FILLED;
644 else
645 board[j][i] = EMPTIED;
650 if (fill) {
651 int v = CHECKED;
652 for (i = 0; i < player.level + STARTING_QIXES; i++) /* add qixes to board */
653 board[pos(qixes[i].y - BOARD_Y)]
654 [pos(qixes[i].x - BOARD_X)] = QIX;
656 init_testboard();
657 for (j = 1; j < BOARD_H - 1; j++) {
658 for (i = 0; i < BOARD_W - 0; i++) {
659 if (board[j][i] != FILLED) {
660 ret = infested_area (i, j, v);
661 if (ret < 0 || ( ret == 0 && fill_area (i, j) ) )
662 quit = true;
663 v++;
668 for (i = 0; i < player.level + STARTING_QIXES; i++) /* add qixes to board */
669 board[pos(qixes[i].y - BOARD_Y)]
670 [pos(qixes[i].x - BOARD_X)] = EMPTIED;
671 percentage_cache = percentage();
674 rb->button_clear_queue();
677 /* returns the color the real pixel(x,y) on the lcd is pointing at */
678 static inline unsigned int getpixel (int x, int y)
680 const int a = pos (x - BOARD_X), b = pos (y - BOARD_Y);
681 if ((a > 0) && (a < BOARD_W) && (b > 0) && (b < BOARD_H)) /* if inside board */
682 return board[b][a];
683 else
684 return FILLED;
687 /* returns the color the ball on (newx,newy) is heading at *----*
688 checks the four edge points of the square if 1st of all | |
689 are a trail (cause it's a lose life situation) and 2nd | |
690 if it's filled so it needs to bounce. *____*
692 static inline unsigned int next_hit (int newx, int newy)
694 if ((getpixel (newx, newy) == TRAIL)
695 || (getpixel (newx, newy + CUBE_SIZE - 1) == TRAIL)
696 || (getpixel (newx + CUBE_SIZE - 1, newy) == TRAIL)
697 || (getpixel (newx + CUBE_SIZE - 1, newy + CUBE_SIZE - 1) == TRAIL))
698 return TRAIL;
699 else if ((getpixel (newx, newy) == FILLED)
700 || (getpixel (newx, newy + CUBE_SIZE - 1) == FILLED)
701 || (getpixel (newx + CUBE_SIZE - 1, newy) == FILLED)
702 || (getpixel (newx + CUBE_SIZE - 1, newy + CUBE_SIZE - 1) ==
703 FILLED))
704 return FILLED;
705 else
706 return EMPTIED;
709 static void die (void)
711 player.lives--;
712 if (player.lives == 0)
713 player.gameover = true;
714 else {
715 refresh_board ();
716 rb->splash (HZ, "Crash!");
717 complete_trail (false);
718 player.move = MOVE_NO;
719 player.drawing = false;
720 player.i = BOARD_W / 2;
721 player.j = 1;
725 /* returns true if the (side) of the block -***-
726 starting from (newx,newy) has any filled pixels * *
727 -***-
729 static inline bool line_check_lt (int newx, int newy)
731 return getpixel (newx, newy + CUBE_SIZE/2-1) == FILLED
732 && getpixel (newx, newy + CUBE_SIZE/2 ) == FILLED;
734 static inline bool line_check_rt (int newx, int newy)
736 return getpixel (newx + CUBE_SIZE-1, newy + CUBE_SIZE/2-1) == FILLED
737 && getpixel (newx + CUBE_SIZE-1, newy + CUBE_SIZE/2 ) == FILLED;
739 static inline bool line_check_up (int newx, int newy)
741 return getpixel (newx + CUBE_SIZE/2-1, newy) == FILLED
742 && getpixel (newx + CUBE_SIZE/2 , newy) == FILLED;
744 static inline bool line_check_dn (int newx, int newy)
746 return getpixel (newx + CUBE_SIZE/2-1, newy + CUBE_SIZE-1) == FILLED
747 && getpixel (newx + CUBE_SIZE/2 , newy + CUBE_SIZE-1) == FILLED;
750 static inline void move_qix (struct qix *q)
752 int newx, newy;
753 newx = get_newx (q->x, q->velocity, q->angle);
754 newy = get_newy (q->y, q->velocity, q->angle);
755 switch (next_hit (newx, newy))
757 case EMPTIED:
758 q->x = newx;
759 q->y = newy;
760 break;
761 case FILLED:
763 const int a = q->angle;
764 q->angle =
765 ((a&(DIR_UU|DIR_U))
766 ? (line_check_up (newx, newy) ? ((a&(DIR_UU|DIR_U))>>4)
767 : (a&(DIR_UU|DIR_U)))
768 : 0)
770 ((a&(DIR_RR|DIR_R))
771 ? (line_check_rt (newx, newy) ? ((a&(DIR_RR|DIR_R))>>4)
772 : (a&(DIR_RR|DIR_R)))
773 : 0)
775 ((a&(DIR_DD|DIR_D))
776 ? (line_check_dn (newx, newy) ? ((a&(DIR_DD|DIR_D))<<4)
777 : (a&(DIR_DD|DIR_D)))
778 : 0)
780 ((a&(DIR_LL|DIR_L))
781 ? (line_check_lt (newx, newy) ? ((a&(DIR_LL|DIR_L))<<4)
782 : (a&(DIR_LL|DIR_L)))
783 : 0);
784 q->x = get_newx (q->x, q->velocity, q->angle);
785 q->y = get_newy (q->y, q->velocity, q->angle);
786 break;
788 case TRAIL:
789 die();
790 break;
794 /* move the board forward timewise */
795 static inline void move_board (void)
797 int j, newi, newj;
799 for (j = 0; j < player.level + STARTING_QIXES; j++)
800 move_qix (&qixes[j]);
801 /* move_qix(&black_qix,true); */
802 if (player.move) {
803 newi = player.i;
804 newj = player.j;
805 switch (player.move) {
806 case MOVE_UP:
807 if (player.j > 1)
808 newj--;
809 break;
810 case MOVE_DN:
811 if (player.j < BOARD_H - 2)
812 newj++;
813 break;
814 case MOVE_LT:
815 if (player.i > 0)
816 newi--;
817 break;
818 case MOVE_RT:
819 if (player.i < BOARD_W - 1)
820 newi++;
821 break;
822 default:
823 break;
826 if ((player.drawing) && (board[newj][newi] == EMPTIED)) /* continue drawing */
827 board[newj][newi] = TRAIL;
828 else if ((player.drawing) && (board[newj][newi] == FILLED)) { /* finish drawing */
829 player.move = MOVE_NO; /* stop moving */
830 player.drawing = false;
831 complete_trail (true);
832 } else if ((board[player.j][player.i] == FILLED)
833 && (board[newj][newi] == EMPTIED)) {
834 /* start drawing */
835 player.drawing = true;
836 board[newj][newi] = TRAIL;
837 /* if the block after next is empty and we're moving onto filled, stop */
838 } else if ((board[newj][newi] == FILLED)
839 && (board[newj + newj-player.j][newi + newi-player.i] == EMPTIED)) {
840 player.move = MOVE_NO;
842 player.i = newi;
843 player.j = newj;
845 if (percentage_cache >= difficulty) { /* finished level */
846 rb->splash (HZ * 2, "Level %d finished", player.level+1);
847 player.score += percentage_cache;
848 if (player.level < MAX_LEVEL)
849 player.level++;
850 init_board ();
851 refresh_board ();
852 rb->splash (HZ * 2, "Ready?");
856 /* the main menu */
857 static int game_menu (void)
859 MENUITEM_STRINGLIST(menu, "XOBOX Menu", NULL, "Start New Game",
860 "Speed","Difficulty","Quit");
861 int selection = 0;
862 #ifdef HAVE_LCD_COLOR
863 rb->lcd_set_foreground (rb->global_settings->fg_color);
864 rb->lcd_set_background (rb->global_settings->bg_color);
865 #elif LCD_DEPTH>=2
866 rb->lcd_set_foreground(LCD_BLACK);
867 rb->lcd_set_background(LCD_WHITE);
868 #endif
869 for (;;) {
870 rb->do_menu(&menu,&selection, NULL, false);
871 if (selection==1)
872 rb->set_int ("Speed", "", UNIT_INT, &speed, NULL, 1, 1, 10, NULL);
873 else if (selection==2)
874 rb->set_int ("Difficulty", "", UNIT_INT, &difficulty, NULL,
875 5, 50, 95, NULL);
876 else
877 break;
879 if (selection != MENU_START) {
880 selection = MENU_QUIT;
882 return selection;
885 /* init game's variables */
886 static void init_game (void)
888 player.level = 0;
889 player.score = 0;
890 player.lives = 3;
891 player.gameover = false;
892 player.drawing = false;
893 rb->lcd_setfont(FONT_SYSFIXED);
894 init_board ();
895 refresh_board ();
896 rb->splash (HZ * 2, "Ready?");
899 /* general keypad handler loop */
900 static int xobox_loop (void)
902 int button = 0, ret;
903 bool pause = false;
904 int end;
906 while (!quit) {
907 end = *rb->current_tick + ((11-speed)*HZ)/100;
909 #ifdef HAS_BUTTON_HOLD
910 if (rb->button_hold()) {
911 pause = true;
912 rb->splash (HZ, "PAUSED");
914 #endif
916 button = rb->button_get_w_tmo (true);
917 switch (button) {
918 case UP:
919 case UP|BUTTON_REPEAT:
920 player.move = MOVE_UP;
921 break;
922 case DOWN:
923 case DOWN|BUTTON_REPEAT:
924 player.move = MOVE_DN;
925 break;
926 case LEFT:
927 case LEFT|BUTTON_REPEAT:
928 player.move = MOVE_LT;
929 break;
930 case RIGHT:
931 case RIGHT|BUTTON_REPEAT:
932 player.move = MOVE_RT;
933 break;
934 case PAUSE:
935 pause = !pause;
936 if (pause)
937 rb->splash (HZ, "Paused");
938 break;
939 case QUIT:
940 ret = game_menu ();
941 if (ret == MENU_START)
942 init_game ();
943 else
945 quit = true;
946 continue;
948 break;
949 default:
950 if (rb->default_event_handler (button) == SYS_USB_CONNECTED)
951 return PLUGIN_USB_CONNECTED;
952 break;
954 if (!pause) {
955 move_board ();
956 refresh_board ();
958 if (player.gameover) {
959 rb->splash (HZ, "Game Over!");
960 ret = game_menu ();
961 if (ret == MENU_START)
962 init_game ();
963 else
964 quit = true;
967 if (end > *rb->current_tick)
968 rb->sleep (end - *rb->current_tick);
969 else
970 rb->yield ();
972 } /* end while */
973 return PLUGIN_OK; /* for no warnings on compiling */
976 /* plugin main procedure */
977 enum plugin_status plugin_start (struct plugin_api *api, void *parameter)
979 int ret = PLUGIN_OK;
981 (void) parameter;
982 rb = api;
984 rb->lcd_setfont (FONT_SYSFIXED);
985 #if LCD_DEPTH>=2
986 rb->lcd_set_backdrop(NULL);
987 #endif
989 /* Turn off backlight timeout */
990 backlight_force_on(rb); /* backlight control in lib/helper.c */
992 quit = false;
994 randomize ();
995 if (game_menu () == MENU_START) {
996 init_game ();
997 ret = xobox_loop ();
1000 /* Turn on backlight timeout (revert to settings) */
1001 backlight_use_settings(rb); /* backlight control in lib/helper.c */
1002 rb->lcd_setfont (FONT_UI);
1004 return ret;