Fix typo in the menu entry
[kugel-rb.git] / apps / plugins / xobox.c
blob26f9bc2f4d1c564d76c0d46b46d22ef7445d2e6c
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2006 Eli Sherer
11 * 2007 Antoine Cellerier
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation; either version 2
16 * of the License, or (at your option) any later version.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
21 ****************************************************************************/
23 #include "plugin.h"
24 #include "lib/helper.h"
25 #include "lib/playback_control.h"
27 PLUGIN_HEADER
29 #if (CONFIG_KEYPAD == IRIVER_H100_PAD) || (CONFIG_KEYPAD == IRIVER_H300_PAD)
31 #define QUIT BUTTON_OFF
32 #define LEFT BUTTON_LEFT
33 #define RIGHT BUTTON_RIGHT
34 #define PAUSE BUTTON_MODE
35 #define UP BUTTON_UP
36 #define DOWN BUTTON_DOWN
38 #define RC_QUIT BUTTON_RC_STOP
40 #elif (CONFIG_KEYPAD == ARCHOS_AV300_PAD)
42 #define QUIT BUTTON_OFF
43 #define LEFT BUTTON_LEFT
44 #define RIGHT BUTTON_RIGHT
45 #define PAUSE BUTTON_ON
46 #define UP BUTTON_UP
47 #define DOWN BUTTON_DOWN
49 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
50 (CONFIG_KEYPAD == IPOD_3G_PAD) || \
51 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
53 #define QUIT (BUTTON_SELECT | BUTTON_MENU)
54 #define LEFT BUTTON_LEFT
55 #define RIGHT BUTTON_RIGHT
56 #define PAUSE BUTTON_SELECT
57 #define MENU_UP BUTTON_SCROLL_FWD
58 #define MENU_DOWN BUTTON_SCROLL_BACK
59 #define UP BUTTON_MENU
60 #define DOWN BUTTON_PLAY
62 #elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD
64 #define QUIT BUTTON_POWER
65 #define LEFT BUTTON_LEFT
66 #define RIGHT BUTTON_RIGHT
67 #define UP BUTTON_UP
68 #define DOWN BUTTON_DOWN
69 #define PAUSE BUTTON_PLAY
71 #elif (CONFIG_KEYPAD == GIGABEAT_PAD)
73 #define QUIT BUTTON_POWER
74 #define LEFT BUTTON_LEFT
75 #define RIGHT BUTTON_RIGHT
76 #define UP BUTTON_UP
77 #define DOWN BUTTON_DOWN
78 #define PAUSE BUTTON_A
80 #elif (CONFIG_KEYPAD == SANSA_E200_PAD) || \
81 (CONFIG_KEYPAD == SANSA_C200_PAD)
83 #define QUIT BUTTON_POWER
84 #define LEFT BUTTON_LEFT
85 #define RIGHT BUTTON_RIGHT
86 #define UP BUTTON_UP
87 #define DOWN BUTTON_DOWN
88 #define PAUSE BUTTON_REC
90 #elif (CONFIG_KEYPAD == SANSA_CLIP_PAD)
92 #define QUIT BUTTON_POWER
93 #define LEFT BUTTON_LEFT
94 #define RIGHT BUTTON_RIGHT
95 #define UP BUTTON_UP
96 #define DOWN BUTTON_DOWN
97 #define PAUSE BUTTON_HOME
99 #elif (CONFIG_KEYPAD == SANSA_FUZE_PAD)
101 #define QUIT (BUTTON_HOME|BUTTON_REPEAT)
102 #define LEFT BUTTON_LEFT
103 #define RIGHT BUTTON_RIGHT
104 #define UP BUTTON_UP
105 #define DOWN BUTTON_DOWN
106 #define PAUSE BUTTON_SELECT
108 #elif (CONFIG_KEYPAD == SANSA_M200_PAD)
110 #define QUIT BUTTON_POWER
111 #define LEFT BUTTON_LEFT
112 #define RIGHT BUTTON_RIGHT
113 #define UP BUTTON_UP
114 #define DOWN BUTTON_DOWN
115 #define PAUSE BUTTON_SELECT
117 #elif CONFIG_KEYPAD == IRIVER_H10_PAD
119 #define QUIT BUTTON_POWER
120 #define LEFT BUTTON_LEFT
121 #define RIGHT BUTTON_RIGHT
122 #define UP BUTTON_SCROLL_UP
123 #define DOWN BUTTON_SCROLL_DOWN
124 #define PAUSE BUTTON_PLAY
126 #elif CONFIG_KEYPAD == RECORDER_PAD
128 #define QUIT BUTTON_OFF
129 #define LEFT BUTTON_LEFT
130 #define RIGHT BUTTON_RIGHT
131 #define DOWN BUTTON_DOWN
132 #define UP BUTTON_UP
133 #define PAUSE BUTTON_PLAY
135 #elif CONFIG_KEYPAD == ONDIO_PAD
137 #define QUIT BUTTON_OFF
138 #define LEFT BUTTON_LEFT
139 #define RIGHT BUTTON_RIGHT
140 #define DOWN BUTTON_DOWN
141 #define UP BUTTON_UP
142 #define PAUSE BUTTON_MENU
144 #elif (CONFIG_KEYPAD == GIGABEAT_S_PAD)
146 #define QUIT BUTTON_BACK
147 #define LEFT BUTTON_LEFT
148 #define RIGHT BUTTON_RIGHT
149 #define UP BUTTON_UP
150 #define DOWN BUTTON_DOWN
151 #define PAUSE BUTTON_PLAY
153 #elif (CONFIG_KEYPAD == MROBE100_PAD)
155 #define QUIT BUTTON_POWER
156 #define LEFT BUTTON_LEFT
157 #define RIGHT BUTTON_RIGHT
158 #define UP BUTTON_UP
159 #define DOWN BUTTON_DOWN
160 #define PAUSE BUTTON_DISPLAY
162 #elif CONFIG_KEYPAD == IAUDIO_M3_PAD
164 #define QUIT BUTTON_RC_REC
165 #define LEFT BUTTON_RC_REW
166 #define RIGHT BUTTON_RC_FF
167 #define UP BUTTON_RC_VOL_UP
168 #define DOWN BUTTON_RC_VOL_DOWN
169 #define PAUSE BUTTON_RC_PLAY
171 #elif CONFIG_KEYPAD == COWOND2_PAD
173 #define QUIT BUTTON_POWER
175 #elif CONFIG_KEYPAD == IAUDIO67_PAD
177 #define QUIT BUTTON_POWER
178 #define LEFT BUTTON_LEFT
179 #define RIGHT BUTTON_RIGHT
180 #define UP BUTTON_STOP
181 #define DOWN BUTTON_PLAY
182 #define PAUSE BUTTON_MENU
184 #elif CONFIG_KEYPAD == CREATIVEZVM_PAD
186 #define QUIT BUTTON_BACK
187 #define LEFT BUTTON_LEFT
188 #define RIGHT BUTTON_RIGHT
189 #define UP BUTTON_UP
190 #define DOWN BUTTON_DOWN
191 #define PAUSE BUTTON_PLAY
193 #elif CONFIG_KEYPAD == PHILIPS_HDD1630_PAD
195 #define QUIT BUTTON_POWER
196 #define LEFT BUTTON_LEFT
197 #define RIGHT BUTTON_RIGHT
198 #define UP BUTTON_UP
199 #define DOWN BUTTON_DOWN
200 #define PAUSE BUTTON_VIEW
202 #elif CONFIG_KEYPAD == ONDAVX747_PAD || CONFIG_KEYPAD == MROBE500_PAD
204 #define QUIT BUTTON_POWER
206 #else
207 #error No keymap defined!
208 #endif
210 #ifdef HAVE_TOUCHSCREEN
211 #ifndef QUIT
212 #define QUIT BUTTON_TOPLEFT
213 #endif
214 #ifndef LEFT
215 #define LEFT BUTTON_MIDLEFT
216 #endif
217 #ifndef RIGHT
218 #define RIGHT BUTTON_MIDRIGHT
219 #endif
220 #ifndef UP
221 #define UP BUTTON_TOPMIDDLE
222 #endif
223 #ifndef DOWN
224 #define DOWN BUTTON_BOTTOMMIDDLE
225 #endif
226 #ifndef PAUSE
227 #define PAUSE BUTTON_CENTER
228 #endif
229 #endif
231 #define MOVE_NO 0 /* player movement */
232 #define MOVE_UP 1 /* 1 */
233 #define MOVE_DN 2 /* 3 0 4 */
234 #define MOVE_LT 3 /* 2 */
235 #define MOVE_RT 4
237 /* ball movement (12 ways) */
238 /* UUL UR */
239 /* UL UR */
240 /* ULL . URR */
241 /* DLL DRR */
242 /* DL DR */
243 /* DDL DDR */
245 #define DIR_UU (1<<7)
246 #define DIR_U (1<<6)
247 #define DIR_RR (1<<5)
248 #define DIR_R (1<<4)
249 #define DIR_DD (1<<3)
250 #define DIR_D (1<<2)
251 #define DIR_LL (1<<1)
252 #define DIR_L (1<<0)
254 #define MOVE_UUR ( DIR_UU | DIR_R )
255 #define MOVE_UR ( DIR_U | DIR_R )
256 #define MOVE_URR ( DIR_U | DIR_RR )
257 #define MOVE_DRR ( DIR_D | DIR_RR )
258 #define MOVE_DR ( DIR_D | DIR_R )
259 #define MOVE_DDR ( DIR_DD | DIR_R )
260 #define MOVE_DDL ( DIR_DD | DIR_L )
261 #define MOVE_DL ( DIR_D | DIR_L )
262 #define MOVE_DLL ( DIR_D | DIR_LL )
263 #define MOVE_ULL ( DIR_U | DIR_LL )
264 #define MOVE_UL ( DIR_U | DIR_L )
265 #define MOVE_UUL ( DIR_UU | DIR_L )
267 #if (LCD_WIDTH>112) && (LCD_HEIGHT>64)
268 # define CUBE_SIZE 8 /* 8x22=176 */
269 # define pos(a) ((a)>>3)
270 #else
271 # define CUBE_SIZE 4
272 # define pos(a) ((a)>>2)
273 #endif
275 #define STARTING_QIXES 2
276 #define MAX_LEVEL 10
277 #define MAX_QIXES MAX_LEVEL+STARTING_QIXES
278 #define BOARD_W ((int)(LCD_WIDTH/CUBE_SIZE))
279 #define BOARD_H ((int)(LCD_HEIGHT/CUBE_SIZE))
280 #define BOARD_X (LCD_WIDTH-BOARD_W*CUBE_SIZE)/2
281 #define BOARD_Y (LCD_HEIGHT-BOARD_H*CUBE_SIZE)/2
283 #ifdef HAVE_LCD_COLOR
284 #define CLR_RED LCD_RGBPACK(255,0,0) /* used to imply danger */
285 #define CLR_LTBLUE LCD_RGBPACK(125, 145, 180) /* used for frame and filling */
286 #define PLR_COL LCD_WHITE /* color used for the player */
287 #elif LCD_DEPTH>=2
288 #define CLR_RED LCD_DARKGRAY /* used to imply danger */
289 #define CLR_LTBLUE LCD_LIGHTGRAY /* used for frame and filling */
290 #define PLR_COL LCD_BLACK /* color used for the player */
291 #endif
293 #if LCD_DEPTH>=2
294 #define EMPTIED LCD_BLACK /* empty spot */
295 #define FILLED CLR_LTBLUE /* filled spot */
296 #define TRAIL CLR_RED /* the red trail of the player */
297 #define QIX LCD_WHITE
298 #else
299 #define EMPTIED 0
300 #define FILLED 1
301 #define TRAIL 2
302 #define QIX 3
303 #endif
304 #define UNCHECKED 0
305 #define CHECKED 1
306 #define PAINTED -1
307 #define PIC_QIX 0
308 #define PIC_PLAYER 1
310 #define MENU_START 0
311 #define MENU_QUIT 1
313 /* The time (in ms) for one iteration through the game loop - decrease this
314 to speed up the game - note that current_tick is (currently) only accurate
315 to 10ms.
317 static int speed = 6; /* CYCLETIME = (11-speed)*10 ms */
318 static int difficulty = 75; /* Percentage of screen that needs to be filled
319 * in order to win the game */
321 static bool quit = false;
323 static unsigned int board[BOARD_H][BOARD_W];
324 static int testboard[BOARD_H][BOARD_W];
326 #if CUBE_SIZE == 8
328 00011000 0x18 - 11100111 0xe7
329 00111100 0x3c - 11100111 0xe7
330 01111110 0x7e - 11000011 0xc3
331 11111111 0xff - 00000000 0x00
332 11111111 0xff - 00000000 0x00
333 01111110 0x7e - 11000011 0xc3
334 00111100 0x3c - 11100111 0xe7
335 00011000 0x18 - 11100111 0xe7
337 const unsigned char pics[2][8] = {
338 {0x18, 0x3c, 0x7e, 0xff, 0xff, 0x7e, 0x3c, 0x18}, /* Alien (QIX) */
339 {0xe7, 0xe7, 0xc3, 0x00, 0x00, 0xc3, 0xe7, 0xe7} /* Player (XONIX) */
341 #elif CUBE_SIZE == 4
343 0110 0x6 - 1001 0x9
344 1111 0xf - 0110 0x6
345 1111 0xf - 0110 0x6
346 0110 0x6 - 1001 0x9
348 const unsigned char pics[2][4] = {
349 {0x6, 0xf, 0xf, 0x6}, /* Alien (QIX) */
350 {0x9, 0x6, 0x6, 0x9} /* Player (XONIX) */
352 #else
353 #error Incorrect CUBE_SIZE value.
354 #endif
356 static struct qix
358 int velocity; /* velocity */
359 int x, y; /* position on screen */
360 int angle; /* angle */
361 } qixes[MAX_QIXES]; /* black_qix */
363 static struct splayer
365 int i, j; /* position on board */
366 int move, score, level, lives;
367 bool drawing;
368 bool gameover;
369 } player;
371 static int percentage_cache;
373 /*************************** STACK STUFF **********************/
375 /* the stack */
376 #define STACK_SIZE (2*BOARD_W*BOARD_H)
377 static struct pos
379 int x, y; /* position on board */
380 } stack[STACK_SIZE];
381 static int stackPointer;
383 static inline bool pop (struct pos *p)
385 if (stackPointer > 0) {
386 p->x = stack[stackPointer].x;
387 p->y = stack[stackPointer].y;
388 stackPointer--;
389 return true;
390 } else
391 return false; /* SE */
394 static inline bool push (struct pos *p)
396 if (stackPointer < STACK_SIZE - 1) {
397 stackPointer++;
398 stack[stackPointer].x = p->x;
399 stack[stackPointer].y = p->y;
400 return true;
401 } else
402 return false; /* SOF */
405 static inline void emptyStack (void)
407 stackPointer = 0;
410 /*********************** END OF STACK STUFF *********************/
412 /* calculate the new x coordinate of the ball according to angle and speed */
413 static inline int get_newx (int x, int len, int deg)
415 if (deg & DIR_R)
416 return x + len;
417 else if (deg & DIR_L)
418 return x - len;
419 else if (deg & DIR_RR)
420 return x + len * 2;
421 else /* (def & DIR_LL) */
422 return x - len * 2;
425 /* calculate the new y coordinate of the ball according to angle and speed */
426 static inline int get_newy (int y, int len, int deg)
428 if (deg & DIR_D)
429 return y + len;
430 else if (deg & DIR_U)
431 return y - len;
432 else if (deg & DIR_DD)
433 return y + len * 2;
434 else /* (deg & DIR_UU) */
435 return y - len * 2;
438 /* make random function get it's value from the device ticker */
439 static inline void randomize (void)
441 rb->srand (*rb->current_tick);
444 /* get a random number between 0 and range-1 */
445 static int t_rand (int range)
447 return rb->rand () % range;
450 /* initializes the test help board */
451 static void init_testboard (void)
453 int j; /* testboard */
454 for (j = 0; j < BOARD_H; j++)
455 /* UNCHEKED == (int)0 */
456 rb->memset( testboard[j], 0, BOARD_W * sizeof( int ) );
459 /* initializes the game board on with the player,qix's and black qix */
460 static void init_board (void)
462 int i, j;
463 for (j = 0; j < BOARD_H; j++)
464 for (i = 0; i < BOARD_W; i++) { /* make a nice cyan frame */
465 if ((i == 0) || (j <= 1) || (i == BOARD_W - 1)
466 || (j >= BOARD_H - 2))
467 board[j][i] = FILLED;
468 else
469 board[j][i] = EMPTIED;
472 /* (level+2) is the number of qixes */
473 for (j = 0; j < player.level + STARTING_QIXES; j++) {
474 qixes[j].velocity = t_rand (2) + 1; /* 1 or 2 pix-per-sec */
476 /* not on frame */
477 qixes[j].x = CUBE_SIZE*2 + 2*t_rand (((BOARD_W-4)*CUBE_SIZE)/2);
478 qixes[j].y = CUBE_SIZE*2 + 2*t_rand (((BOARD_H-4)*CUBE_SIZE)/2);
480 const int angle_table[] = {
481 MOVE_UUR, MOVE_UR, MOVE_URR, MOVE_DRR, MOVE_DR, MOVE_DDR,
482 MOVE_UUL, MOVE_UL, MOVE_ULL, MOVE_DLL, MOVE_DL, MOVE_DDL };
483 qixes[j].angle = angle_table[t_rand (12)];
484 #if CUBE_SIZE == 4
485 /* Work arround a nasty bug. FIXME */
486 if( qixes[j].angle & (DIR_LL|DIR_RR|DIR_UU|DIR_DD) )
487 qixes[j].velocity = 1;
488 #endif
490 /*black_qix.velocity=1;
491 black_qix.x=BOARD_X+(BOARD_W*CUBE_SIZE)/2-CUBE_SIZE/2;
492 black_qix.y=BOARD_Y+(BOARD_H*CUBE_SIZE)-CUBE_SIZE-CUBE_SIZE/2;
493 black_qix.angle=MOVE_UR; */
494 player.move = MOVE_NO;
495 player.drawing = false;
496 player.i = BOARD_W / 2;
497 player.j = 1;
499 percentage_cache = 0;
502 /* calculates the percentage of the screen filling */
503 static int percentage (void)
505 int i, j, filled = 0;
506 for (j = 2; j < BOARD_H - 2; j++)
507 for (i = 1; i < BOARD_W - 1; i++)
508 if (board[j][i] == FILLED)
509 filled++;
510 return (filled * 100) / ((BOARD_W - 2) * (BOARD_H - 4));
513 /* draw the board on with all the game figures */
514 static void refresh_board (void)
516 int i, j;
517 char str[25];
519 #if LCD_DEPTH>=2
520 rb->lcd_set_background (LCD_BLACK);
521 #endif
522 rb->lcd_clear_display ();
523 for (j = 0; j < BOARD_H; j++)
525 unsigned last_color = board[j][0];
526 int last_i = 0;
527 for (i = 1; i < BOARD_W; i++) {
528 if( last_color != board[j][i] )
530 #if LCD_DEPTH>=2
531 rb->lcd_set_foreground (last_color);
532 #else
533 if (last_color != EMPTIED)
534 #endif
535 rb->lcd_fillrect (BOARD_X + CUBE_SIZE * (last_i),
536 BOARD_Y + CUBE_SIZE * j,
537 CUBE_SIZE * (i - last_i), CUBE_SIZE );
538 last_color = board[j][i];
539 last_i = i;
542 #if LCD_DEPTH>=2
543 rb->lcd_set_foreground (last_color);
544 #else
545 if (last_color != EMPTIED)
546 #endif
547 rb->lcd_fillrect (BOARD_X + CUBE_SIZE * (last_i),
548 BOARD_Y + CUBE_SIZE * j,
549 CUBE_SIZE * (i - last_i), CUBE_SIZE);
552 #if LCD_DEPTH>=2
553 rb->lcd_set_foreground (LCD_BLACK);
554 rb->lcd_set_background (CLR_LTBLUE);
555 #else
556 rb->lcd_set_drawmode (DRMODE_COMPLEMENT);
557 #endif
558 rb->snprintf (str, sizeof (str), "Level %d", player.level + 1);
559 rb->lcd_putsxy (BOARD_X, BOARD_Y, str);
560 rb->snprintf (str, sizeof (str), "%d%%", percentage_cache);
561 rb->lcd_putsxy (BOARD_X + CUBE_SIZE * BOARD_W - 24, BOARD_Y, str);
562 rb->snprintf (str, sizeof (str), "Score: %d", player.score);
563 rb->lcd_putsxy (BOARD_X, BOARD_Y + CUBE_SIZE * BOARD_H - 8, str);
564 rb->snprintf (str, sizeof (str),
565 (player.lives != 1) ? "%d Lives" : "%d Life", player.lives);
566 #if LCD_DEPTH>=2
567 rb->lcd_putsxy (BOARD_X + CUBE_SIZE * BOARD_W - 60,
568 BOARD_Y + CUBE_SIZE * BOARD_H - 8, str);
569 #else
570 rb->lcd_putsxy (BOARD_X + CUBE_SIZE * BOARD_W - 40,
571 BOARD_Y + CUBE_SIZE * BOARD_H - 8, str);
572 #endif
574 #if LCD_DEPTH>=2
575 rb->lcd_set_foreground (PLR_COL);
576 rb->lcd_set_background (board[player.j][player.i]);
577 #endif
578 rb->lcd_mono_bitmap (pics[PIC_PLAYER], player.i * CUBE_SIZE + BOARD_X,
579 player.j * CUBE_SIZE + BOARD_Y, CUBE_SIZE, CUBE_SIZE);
581 #if LCD_DEPTH>=2
582 rb->lcd_set_background (EMPTIED);
583 rb->lcd_set_foreground (LCD_WHITE);
584 rb->lcd_set_drawmode (DRMODE_FG);
585 #else
586 rb->lcd_set_drawmode (DRMODE_FG);
587 #endif
588 for (j = 0; j < player.level + STARTING_QIXES; j++)
589 rb->lcd_mono_bitmap (pics[PIC_QIX], qixes[j].x + BOARD_X,
590 qixes[j].y + BOARD_Y, CUBE_SIZE, CUBE_SIZE);
591 #if LCD_DEPTH>=2
592 rb->lcd_set_foreground (LCD_BLACK);
593 #endif
594 rb->lcd_set_drawmode (DRMODE_SOLID);
596 rb->lcd_update ();
599 static inline int infested_area (int i, int j, int v)
601 struct pos p;
602 p.x = i;
603 p.y = j;
604 emptyStack ();
605 if (!push (&p))
606 return -1;
607 while (pop (&p)) {
608 if (testboard[p.y][p.x] == v) continue;
609 if (testboard[p.y][p.x] > UNCHECKED)
610 return 1; /* This area was previously flagged as infested */
611 testboard[p.y][p.x] = v;
612 if (board[p.y][p.x] == QIX)
613 return 1; /* Infested area */
615 struct pos p1 = { p.x+1, p.y };
616 if ((p1.x < BOARD_W)
617 && (board[p1.y][p1.x] != FILLED)
618 && (!push (&p1)))
619 return -1;
622 struct pos p1 = { p.x-1, p.y };
623 if ((p1.x >= 0)
624 && (board[p1.y][p1.x] != FILLED)
625 && (!push (&p1)))
626 return -1;
629 struct pos p1 = { p.x, p.y+1 };
630 if ((p1.y < BOARD_H)
631 && (board[p1.y][p1.x] != FILLED)
632 && (!push (&p1)))
633 return -1;
636 struct pos p1 = { p.x, p.y-1 };
637 if ((p1.y >= 0)
638 && (board[p1.y][p1.x] != FILLED)
639 && (!push (&p1)))
640 return -1;
643 return 0;
646 static inline int fill_area (int i, int j)
648 struct pos p;
649 p.x = i;
650 p.y = j;
651 int v = testboard[p.y][p.x];
652 emptyStack ();
653 if (!push (&p))
654 return -1;
655 while (pop (&p)) {
656 board[p.y][p.x] = FILLED;
657 testboard[p.y][p.x] = PAINTED;
659 struct pos p1 = { p.x+1, p.y };
660 if ((p1.x < BOARD_W)
661 && (testboard[p1.y][p1.x] == v)
662 && (!push (&p1)))
663 return -1;
666 struct pos p1 = { p.x-1, p.y };
667 if ((p1.x >= 0)
668 && (testboard[p1.y][p1.x] == v)
669 && (!push (&p1)))
670 return -1;
673 struct pos p1 = { p.x, p.y+1 };
674 if ((p1.y < BOARD_H)
675 && (testboard[p1.y][p1.x] == v)
676 && (!push (&p1)))
677 return -1;
680 struct pos p1 = { p.x, p.y-1 };
681 if ((p1.y >= 0)
682 && (testboard[p1.y][p1.x] == v)
683 && (!push (&p1)))
684 return -1;
687 return 0;
691 /* take care of stuff after xonix has landed on a filled spot */
692 static void complete_trail (int fill)
694 int i, j, ret;
695 for (j = 0; j < BOARD_H; j++) {
696 for (i = 0; i < BOARD_W; i++) {
697 if (board[j][i] == TRAIL) {
698 if (fill)
699 board[j][i] = FILLED;
700 else
701 board[j][i] = EMPTIED;
706 if (fill) {
707 int v = CHECKED;
708 for (i = 0; i < player.level + STARTING_QIXES; i++) /* add qixes to board */
709 board[pos(qixes[i].y - BOARD_Y)]
710 [pos(qixes[i].x - BOARD_X)] = QIX;
712 init_testboard();
713 for (j = 1; j < BOARD_H - 1; j++) {
714 for (i = 0; i < BOARD_W - 0; i++) {
715 if (board[j][i] != FILLED) {
716 ret = infested_area (i, j, v);
717 if (ret < 0 || ( ret == 0 && fill_area (i, j) ) )
718 quit = true;
719 v++;
724 for (i = 0; i < player.level + STARTING_QIXES; i++) /* add qixes to board */
725 board[pos(qixes[i].y - BOARD_Y)]
726 [pos(qixes[i].x - BOARD_X)] = EMPTIED;
727 percentage_cache = percentage();
730 rb->button_clear_queue();
733 /* returns the color the real pixel(x,y) on the lcd is pointing at */
734 static inline unsigned int getpixel (int x, int y)
736 const int a = pos (x - BOARD_X), b = pos (y - BOARD_Y);
737 if ((a > 0) && (a < BOARD_W) && (b > 0) && (b < BOARD_H)) /* if inside board */
738 return board[b][a];
739 else
740 return FILLED;
743 /* returns the color the ball on (newx,newy) is heading at *----*
744 checks the four edge points of the square if 1st of all | |
745 are a trail (cause it's a lose life situation) and 2nd | |
746 if it's filled so it needs to bounce. *____*
748 static inline unsigned int next_hit (int newx, int newy)
750 if ((getpixel (newx, newy) == TRAIL)
751 || (getpixel (newx, newy + CUBE_SIZE - 1) == TRAIL)
752 || (getpixel (newx + CUBE_SIZE - 1, newy) == TRAIL)
753 || (getpixel (newx + CUBE_SIZE - 1, newy + CUBE_SIZE - 1) == TRAIL))
754 return TRAIL;
755 else if ((getpixel (newx, newy) == FILLED)
756 || (getpixel (newx, newy + CUBE_SIZE - 1) == FILLED)
757 || (getpixel (newx + CUBE_SIZE - 1, newy) == FILLED)
758 || (getpixel (newx + CUBE_SIZE - 1, newy + CUBE_SIZE - 1) ==
759 FILLED))
760 return FILLED;
761 else
762 return EMPTIED;
765 static void die (void)
767 player.lives--;
768 if (player.lives == 0)
769 player.gameover = true;
770 else {
771 refresh_board ();
772 rb->splash (HZ, "Crash!");
773 complete_trail (false);
774 player.move = MOVE_NO;
775 player.drawing = false;
776 player.i = BOARD_W / 2;
777 player.j = 1;
781 /* returns true if the (side) of the block -***-
782 starting from (newx,newy) has any filled pixels * *
783 -***-
785 static inline bool line_check_lt (int newx, int newy)
787 return getpixel (newx, newy + CUBE_SIZE/2-1) == FILLED
788 && getpixel (newx, newy + CUBE_SIZE/2 ) == FILLED;
790 static inline bool line_check_rt (int newx, int newy)
792 return getpixel (newx + CUBE_SIZE-1, newy + CUBE_SIZE/2-1) == FILLED
793 && getpixel (newx + CUBE_SIZE-1, newy + CUBE_SIZE/2 ) == FILLED;
795 static inline bool line_check_up (int newx, int newy)
797 return getpixel (newx + CUBE_SIZE/2-1, newy) == FILLED
798 && getpixel (newx + CUBE_SIZE/2 , newy) == FILLED;
800 static inline bool line_check_dn (int newx, int newy)
802 return getpixel (newx + CUBE_SIZE/2-1, newy + CUBE_SIZE-1) == FILLED
803 && getpixel (newx + CUBE_SIZE/2 , newy + CUBE_SIZE-1) == FILLED;
806 static inline void move_qix (struct qix *q)
808 int newx, newy;
809 newx = get_newx (q->x, q->velocity, q->angle);
810 newy = get_newy (q->y, q->velocity, q->angle);
811 switch (next_hit (newx, newy))
813 case EMPTIED:
814 q->x = newx;
815 q->y = newy;
816 break;
817 case FILLED:
819 const int a = q->angle;
820 q->angle =
821 ((a&(DIR_UU|DIR_U))
822 ? (line_check_up (newx, newy) ? ((a&(DIR_UU|DIR_U))>>4)
823 : (a&(DIR_UU|DIR_U)))
824 : 0)
826 ((a&(DIR_RR|DIR_R))
827 ? (line_check_rt (newx, newy) ? ((a&(DIR_RR|DIR_R))>>4)
828 : (a&(DIR_RR|DIR_R)))
829 : 0)
831 ((a&(DIR_DD|DIR_D))
832 ? (line_check_dn (newx, newy) ? ((a&(DIR_DD|DIR_D))<<4)
833 : (a&(DIR_DD|DIR_D)))
834 : 0)
836 ((a&(DIR_LL|DIR_L))
837 ? (line_check_lt (newx, newy) ? ((a&(DIR_LL|DIR_L))<<4)
838 : (a&(DIR_LL|DIR_L)))
839 : 0);
840 q->x = get_newx (q->x, q->velocity, q->angle);
841 q->y = get_newy (q->y, q->velocity, q->angle);
842 break;
844 case TRAIL:
845 die();
846 break;
850 /* move the board forward timewise */
851 static inline void move_board (void)
853 int j, newi, newj;
855 for (j = 0; j < player.level + STARTING_QIXES; j++)
856 move_qix (&qixes[j]);
857 /* move_qix(&black_qix,true); */
858 if (player.move) {
859 newi = player.i;
860 newj = player.j;
861 switch (player.move) {
862 case MOVE_UP:
863 if (player.j > 1)
864 newj--;
865 break;
866 case MOVE_DN:
867 if (player.j < BOARD_H - 2)
868 newj++;
869 break;
870 case MOVE_LT:
871 if (player.i > 0)
872 newi--;
873 break;
874 case MOVE_RT:
875 if (player.i < BOARD_W - 1)
876 newi++;
877 break;
878 default:
879 break;
882 if ((player.drawing) && (board[newj][newi] == EMPTIED)) /* continue drawing */
883 board[newj][newi] = TRAIL;
884 else if ((player.drawing) && (board[newj][newi] == FILLED)) { /* finish drawing */
885 player.move = MOVE_NO; /* stop moving */
886 player.drawing = false;
887 complete_trail (true);
888 } else if ((board[player.j][player.i] == FILLED)
889 && (board[newj][newi] == EMPTIED)) {
890 /* start drawing */
891 player.drawing = true;
892 board[newj][newi] = TRAIL;
893 /* if the block after next is empty and we're moving onto filled, stop */
894 } else if ((board[newj][newi] == FILLED)
895 && (board[newj + newj-player.j][newi + newi-player.i] == EMPTIED)) {
896 player.move = MOVE_NO;
898 player.i = newi;
899 player.j = newj;
901 if (percentage_cache >= difficulty) { /* finished level */
902 rb->splashf (HZ * 2, "Level %d finished", player.level+1);
903 player.score += percentage_cache;
904 if (player.level < MAX_LEVEL)
905 player.level++;
906 init_board ();
907 refresh_board ();
908 rb->splash (HZ * 2, "Ready?");
912 /* init game's variables */
913 static void init_game (void)
915 player.level = 0;
916 player.score = 0;
917 player.lives = 3;
918 player.gameover = false;
919 player.drawing = false;
920 init_board ();
921 refresh_board ();
922 rb->splash (HZ * 2, "Ready?");
925 /* the main menu */
926 static int xobox_menu(bool ingame)
928 rb->button_clear_queue();
929 int choice = 0;
930 if (ingame) {
931 MENUITEM_STRINGLIST (main_menu, "Xobox Menu", NULL,
932 "Resume Game",
933 "Restart Level",
934 "Speed",
935 "Difficult",
936 "Playback Control",
937 "Quit");
939 while (true) {
940 choice = rb->do_menu(&main_menu, &choice, NULL, false);
941 switch (choice) {
942 case 0:
943 return 0;
944 case 1:
945 init_game ();
946 return 0;
947 case 2:
948 rb->set_int ("Speed", "", UNIT_INT, &speed, NULL, 1, 1, 10, NULL);
949 break;
950 case 3:
951 rb->set_int ("Difficulty", "", UNIT_INT, &difficulty, NULL,
952 5, 50, 95, NULL);
953 break;
954 case 4:
955 playback_control(NULL);
956 break;
957 case 5:
958 return 1;
959 case MENU_ATTACHED_USB:
960 return 1;
961 default:
962 break;
966 else {
967 MENUITEM_STRINGLIST (main_menu, "Xobox Menu", NULL,
968 "Start Game",
969 "Speed",
970 "Difficulty",
971 "Playback Control",
972 "Quit");
974 while (true) {
975 choice = rb->do_menu(&main_menu, &choice, NULL, false);
976 switch (choice) {
977 case 0:
978 init_game ();
979 return 0;
980 case 1:
981 rb->set_int ("Speed", "", UNIT_INT, &speed, NULL, 1, 1, 10, NULL);
982 break;
983 case 2:
984 rb->set_int ("Difficulty", "", UNIT_INT, &difficulty, NULL,
985 5, 50, 95, NULL);
986 break;
987 case 3:
988 playback_control(NULL);
989 break;
990 case 4:
991 return 1;
992 case MENU_ATTACHED_USB:
993 return 1;
994 default:
995 break;
1001 /* general keypad handler loop */
1002 static int xobox_loop (void)
1004 int button = 0;
1005 bool pause = false;
1006 int end;
1008 if (xobox_menu(false)==1) {
1009 return PLUGIN_OK;
1012 while (!quit) {
1013 end = *rb->current_tick + ((11-speed)*HZ)/100;
1015 #ifdef HAS_BUTTON_HOLD
1016 if (rb->button_hold()) {
1017 pause = true;
1018 rb->splash (HZ, "PAUSED");
1020 #endif
1022 button = rb->button_get_w_tmo (1);
1023 switch (button) {
1024 case UP:
1025 case UP|BUTTON_REPEAT:
1026 player.move = MOVE_UP;
1027 break;
1028 case DOWN:
1029 case DOWN|BUTTON_REPEAT:
1030 player.move = MOVE_DN;
1031 break;
1032 case LEFT:
1033 case LEFT|BUTTON_REPEAT:
1034 player.move = MOVE_LT;
1035 break;
1036 case RIGHT:
1037 case RIGHT|BUTTON_REPEAT:
1038 player.move = MOVE_RT;
1039 break;
1040 case PAUSE:
1041 pause = !pause;
1042 if (pause)
1043 rb->splash (HZ, "Paused");
1044 break;
1045 case QUIT:
1046 if (xobox_menu(true)==1) {
1047 quit = true;
1049 break;
1050 default:
1051 if (rb->default_event_handler (button) == SYS_USB_CONNECTED)
1052 return PLUGIN_USB_CONNECTED;
1053 break;
1055 if (!pause) {
1056 move_board ();
1057 refresh_board ();
1059 if (player.gameover) {
1060 rb->splash (HZ, "Game Over!");
1061 if (xobox_menu(false)==1) {
1062 quit = true;
1066 if (end > *rb->current_tick)
1067 rb->sleep (end - *rb->current_tick);
1068 else
1069 rb->yield ();
1071 } /* end while */
1072 return PLUGIN_OK; /* for no warnings on compiling */
1075 /* plugin main procedure */
1076 enum plugin_status plugin_start (const void *parameter)
1078 int ret = PLUGIN_OK;
1080 (void) parameter;
1082 rb->lcd_setfont (FONT_SYSFIXED);
1083 #if LCD_DEPTH>=2
1084 rb->lcd_set_backdrop(NULL);
1085 #endif
1087 /* Turn off backlight timeout */
1088 backlight_force_on(); /* backlight control in lib/helper.c */
1090 randomize ();
1091 ret = xobox_loop ();
1093 /* Turn on backlight timeout (revert to settings) */
1094 backlight_use_settings(); /* backlight control in lib/helper.c */
1095 rb->lcd_setfont (FONT_UI);
1097 return ret;