Correct bitmap color depth
[kugel-rb.git] / apps / plugins / xobox.c
blob4dd673c370c9d52a4f518337f7a59e598d984513
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 == COWON_D2_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 == PHILIPS_SA9200_PAD
204 #define QUIT BUTTON_POWER
205 #define LEFT BUTTON_PREV
206 #define RIGHT BUTTON_NEXT
207 #define UP BUTTON_UP
208 #define DOWN BUTTON_DOWN
209 #define PAUSE BUTTON_MENU
211 #elif CONFIG_KEYPAD == ONDAVX747_PAD || \
212 CONFIG_KEYPAD == ONDAVX777_PAD || \
213 CONFIG_KEYPAD == MROBE500_PAD
215 #define QUIT BUTTON_POWER
217 #elif CONFIG_KEYPAD == SAMSUNG_YH_PAD
219 #define QUIT BUTTON_PLAY
220 #define LEFT BUTTON_LEFT
221 #define RIGHT BUTTON_RIGHT
222 #define UP BUTTON_UP
223 #define DOWN BUTTON_DOWN
224 #define PAUSE BUTTON_FFWD
226 #elif CONFIG_KEYPAD == PBELL_VIBE500_PAD
228 #define QUIT BUTTON_REC
229 #define LEFT BUTTON_PREV
230 #define RIGHT BUTTON_NEXT
231 #define UP BUTTON_UP
232 #define DOWN BUTTON_DOWN
233 #define PAUSE BUTTON_PLAY
235 #elif CONFIG_KEYPAD == MPIO_HD200_PAD
237 #define QUIT (BUTTON_REC|BUTTON_PLAY)
238 #define LEFT BUTTON_VOL_DOWN
239 #define RIGHT BUTTON_VOL_UP
240 #define UP BUTTON_PREV
241 #define DOWN BUTTON_NEXT
242 #define PAUSE BUTTON_PLAY
244 #else
245 #error No keymap defined!
246 #endif
248 #ifdef HAVE_TOUCHSCREEN
249 #ifndef QUIT
250 #define QUIT BUTTON_TOPLEFT
251 #endif
252 #ifndef LEFT
253 #define LEFT BUTTON_MIDLEFT
254 #endif
255 #ifndef RIGHT
256 #define RIGHT BUTTON_MIDRIGHT
257 #endif
258 #ifndef UP
259 #define UP BUTTON_TOPMIDDLE
260 #endif
261 #ifndef DOWN
262 #define DOWN BUTTON_BOTTOMMIDDLE
263 #endif
264 #ifndef PAUSE
265 #define PAUSE BUTTON_CENTER
266 #endif
267 #endif
269 #define MOVE_NO 0 /* player movement */
270 #define MOVE_UP 1 /* 1 */
271 #define MOVE_DN 2 /* 3 0 4 */
272 #define MOVE_LT 3 /* 2 */
273 #define MOVE_RT 4
275 /* ball movement (12 ways) */
276 /* UUL UR */
277 /* UL UR */
278 /* ULL . URR */
279 /* DLL DRR */
280 /* DL DR */
281 /* DDL DDR */
283 #define DIR_UU (1<<7)
284 #define DIR_U (1<<6)
285 #define DIR_RR (1<<5)
286 #define DIR_R (1<<4)
287 #define DIR_DD (1<<3)
288 #define DIR_D (1<<2)
289 #define DIR_LL (1<<1)
290 #define DIR_L (1<<0)
292 #define MOVE_UUR ( DIR_UU | DIR_R )
293 #define MOVE_UR ( DIR_U | DIR_R )
294 #define MOVE_URR ( DIR_U | DIR_RR )
295 #define MOVE_DRR ( DIR_D | DIR_RR )
296 #define MOVE_DR ( DIR_D | DIR_R )
297 #define MOVE_DDR ( DIR_DD | DIR_R )
298 #define MOVE_DDL ( DIR_DD | DIR_L )
299 #define MOVE_DL ( DIR_D | DIR_L )
300 #define MOVE_DLL ( DIR_D | DIR_LL )
301 #define MOVE_ULL ( DIR_U | DIR_LL )
302 #define MOVE_UL ( DIR_U | DIR_L )
303 #define MOVE_UUL ( DIR_UU | DIR_L )
305 #if (LCD_WIDTH>112) && (LCD_HEIGHT>64)
306 # define CUBE_SIZE 8 /* 8x22=176 */
307 # define pos(a) ((a)>>3)
308 #else
309 # define CUBE_SIZE 4
310 # define pos(a) ((a)>>2)
311 #endif
313 #define STARTING_QIXES 2
314 #define MAX_LEVEL 10
315 #define MAX_QIXES MAX_LEVEL+STARTING_QIXES
316 #define BOARD_W ((int)(LCD_WIDTH/CUBE_SIZE))
317 #define BOARD_H ((int)(LCD_HEIGHT/CUBE_SIZE))
318 #define BOARD_X (LCD_WIDTH-BOARD_W*CUBE_SIZE)/2
319 #define BOARD_Y (LCD_HEIGHT-BOARD_H*CUBE_SIZE)/2
321 #ifdef HAVE_LCD_COLOR
322 #define CLR_RED LCD_RGBPACK(255,0,0) /* used to imply danger */
323 #define CLR_LTBLUE LCD_RGBPACK(125, 145, 180) /* used for frame and filling */
324 #define PLR_COL LCD_WHITE /* color used for the player */
325 #elif LCD_DEPTH>=2
326 #define CLR_RED LCD_DARKGRAY /* used to imply danger */
327 #define CLR_LTBLUE LCD_LIGHTGRAY /* used for frame and filling */
328 #define PLR_COL LCD_BLACK /* color used for the player */
329 #endif
331 #if LCD_DEPTH>=2
332 #define EMPTIED LCD_BLACK /* empty spot */
333 #define FILLED CLR_LTBLUE /* filled spot */
334 #define TRAIL CLR_RED /* the red trail of the player */
335 #define QIX LCD_WHITE
336 #else
337 #define EMPTIED 0
338 #define FILLED 1
339 #define TRAIL 2
340 #define QIX 3
341 #endif
342 #define UNCHECKED 0
343 #define CHECKED 1
344 #define PAINTED -1
345 #define PIC_QIX 0
346 #define PIC_PLAYER 1
348 /* The time (in ms) for one iteration through the game loop - decrease this
349 to speed up the game - note that current_tick is (currently) only accurate
350 to 10ms.
352 static int speed = 6; /* CYCLETIME = (11-speed)*10 ms */
353 static int difficulty = 75; /* Percentage of screen that needs to be filled
354 * in order to win the game */
356 static bool quit = false;
358 static unsigned int board[BOARD_H][BOARD_W];
359 static int testboard[BOARD_H][BOARD_W];
361 #if CUBE_SIZE == 8
363 00011000 0x18 - 11100111 0xe7
364 00111100 0x3c - 11100111 0xe7
365 01111110 0x7e - 11000011 0xc3
366 11111111 0xff - 00000000 0x00
367 11111111 0xff - 00000000 0x00
368 01111110 0x7e - 11000011 0xc3
369 00111100 0x3c - 11100111 0xe7
370 00011000 0x18 - 11100111 0xe7
372 const unsigned char pics[2][8] = {
373 {0x18, 0x3c, 0x7e, 0xff, 0xff, 0x7e, 0x3c, 0x18}, /* Alien (QIX) */
374 {0xe7, 0xe7, 0xc3, 0x00, 0x00, 0xc3, 0xe7, 0xe7} /* Player (XONIX) */
376 #elif CUBE_SIZE == 4
378 0110 0x6 - 1001 0x9
379 1111 0xf - 0110 0x6
380 1111 0xf - 0110 0x6
381 0110 0x6 - 1001 0x9
383 const unsigned char pics[2][4] = {
384 {0x6, 0xf, 0xf, 0x6}, /* Alien (QIX) */
385 {0x9, 0x6, 0x6, 0x9} /* Player (XONIX) */
387 #else
388 #error Incorrect CUBE_SIZE value.
389 #endif
391 static struct qix
393 int velocity; /* velocity */
394 int x, y; /* position on screen */
395 int angle; /* angle */
396 } qixes[MAX_QIXES]; /* black_qix */
398 static struct splayer
400 int i, j; /* position on board */
401 int move, score, level, lives;
402 bool drawing;
403 bool gameover;
404 } player;
406 static int percentage_cache;
408 /*************************** STACK STUFF **********************/
410 /* the stack */
411 #define STACK_SIZE (2*BOARD_W*BOARD_H)
412 static struct pos
414 int x, y; /* position on board */
415 } stack[STACK_SIZE];
416 static int stackPointer;
418 static inline bool pop (struct pos *p)
420 if (stackPointer > 0) {
421 p->x = stack[stackPointer].x;
422 p->y = stack[stackPointer].y;
423 stackPointer--;
424 return true;
425 } else
426 return false; /* SE */
429 static inline bool push (struct pos *p)
431 if (stackPointer < STACK_SIZE - 1) {
432 stackPointer++;
433 stack[stackPointer].x = p->x;
434 stack[stackPointer].y = p->y;
435 return true;
436 } else
437 return false; /* SOF */
440 static inline void emptyStack (void)
442 stackPointer = 0;
445 /*********************** END OF STACK STUFF *********************/
447 /* calculate the new x coordinate of the ball according to angle and speed */
448 static inline int get_newx (int x, int len, int deg)
450 if (deg & DIR_R)
451 return x + len;
452 else if (deg & DIR_L)
453 return x - len;
454 else if (deg & DIR_RR)
455 return x + len * 2;
456 else /* (def & DIR_LL) */
457 return x - len * 2;
460 /* calculate the new y coordinate of the ball according to angle and speed */
461 static inline int get_newy (int y, int len, int deg)
463 if (deg & DIR_D)
464 return y + len;
465 else if (deg & DIR_U)
466 return y - len;
467 else if (deg & DIR_DD)
468 return y + len * 2;
469 else /* (deg & DIR_UU) */
470 return y - len * 2;
473 /* make random function get it's value from the device ticker */
474 static inline void randomize (void)
476 rb->srand (*rb->current_tick);
479 /* get a random number between 0 and range-1 */
480 static int t_rand (int range)
482 return rb->rand () % range;
485 /* initializes the test help board */
486 static void init_testboard (void)
488 int j; /* testboard */
489 for (j = 0; j < BOARD_H; j++)
490 /* UNCHEKED == (int)0 */
491 rb->memset( testboard[j], 0, BOARD_W * sizeof( int ) );
494 /* initializes the game board on with the player,qix's and black qix */
495 static void init_board (void)
497 int i, j;
498 for (j = 0; j < BOARD_H; j++)
499 for (i = 0; i < BOARD_W; i++) { /* make a nice cyan frame */
500 if ((i == 0) || (j <= 1) || (i == BOARD_W - 1)
501 || (j >= BOARD_H - 2))
502 board[j][i] = FILLED;
503 else
504 board[j][i] = EMPTIED;
507 /* (level+2) is the number of qixes */
508 for (j = 0; j < player.level + STARTING_QIXES; j++) {
509 qixes[j].velocity = t_rand (2) + 1; /* 1 or 2 pix-per-sec */
511 /* not on frame */
512 qixes[j].x = CUBE_SIZE*2 + 2*t_rand (((BOARD_W-4)*CUBE_SIZE)/2);
513 qixes[j].y = CUBE_SIZE*2 + 2*t_rand (((BOARD_H-4)*CUBE_SIZE)/2);
515 const int angle_table[] = {
516 MOVE_UUR, MOVE_UR, MOVE_URR, MOVE_DRR, MOVE_DR, MOVE_DDR,
517 MOVE_UUL, MOVE_UL, MOVE_ULL, MOVE_DLL, MOVE_DL, MOVE_DDL };
518 qixes[j].angle = angle_table[t_rand (12)];
519 #if CUBE_SIZE == 4
520 /* Work arround a nasty bug. FIXME */
521 if( qixes[j].angle & (DIR_LL|DIR_RR|DIR_UU|DIR_DD) )
522 qixes[j].velocity = 1;
523 #endif
525 /*black_qix.velocity=1;
526 black_qix.x=BOARD_X+(BOARD_W*CUBE_SIZE)/2-CUBE_SIZE/2;
527 black_qix.y=BOARD_Y+(BOARD_H*CUBE_SIZE)-CUBE_SIZE-CUBE_SIZE/2;
528 black_qix.angle=MOVE_UR; */
529 player.move = MOVE_NO;
530 player.drawing = false;
531 player.i = BOARD_W / 2;
532 player.j = 1;
534 percentage_cache = 0;
537 /* calculates the percentage of the screen filling */
538 static int percentage (void)
540 int i, j, filled = 0;
541 for (j = 2; j < BOARD_H - 2; j++)
542 for (i = 1; i < BOARD_W - 1; i++)
543 if (board[j][i] == FILLED)
544 filled++;
545 return (filled * 100) / ((BOARD_W - 2) * (BOARD_H - 4));
548 /* draw the board on with all the game figures */
549 static void refresh_board (void)
551 int i, j;
552 char str[25];
554 #if LCD_DEPTH>=2
555 rb->lcd_set_background (LCD_BLACK);
556 #endif
557 rb->lcd_clear_display ();
558 for (j = 0; j < BOARD_H; j++)
560 unsigned last_color = board[j][0];
561 int last_i = 0;
562 for (i = 1; i < BOARD_W; i++) {
563 if( last_color != board[j][i] )
565 #if LCD_DEPTH>=2
566 rb->lcd_set_foreground (last_color);
567 #else
568 if (last_color != EMPTIED)
569 #endif
570 rb->lcd_fillrect (BOARD_X + CUBE_SIZE * (last_i),
571 BOARD_Y + CUBE_SIZE * j,
572 CUBE_SIZE * (i - last_i), CUBE_SIZE );
573 last_color = board[j][i];
574 last_i = i;
577 #if LCD_DEPTH>=2
578 rb->lcd_set_foreground (last_color);
579 #else
580 if (last_color != EMPTIED)
581 #endif
582 rb->lcd_fillrect (BOARD_X + CUBE_SIZE * (last_i),
583 BOARD_Y + CUBE_SIZE * j,
584 CUBE_SIZE * (i - last_i), CUBE_SIZE);
587 #if LCD_DEPTH>=2
588 rb->lcd_set_foreground (LCD_BLACK);
589 rb->lcd_set_background (CLR_LTBLUE);
590 #else
591 rb->lcd_set_drawmode (DRMODE_COMPLEMENT);
592 #endif
593 rb->snprintf (str, sizeof (str), "Level %d", player.level + 1);
594 rb->lcd_putsxy (BOARD_X, BOARD_Y, str);
595 rb->snprintf (str, sizeof (str), "%d%%", percentage_cache);
596 rb->lcd_putsxy (BOARD_X + CUBE_SIZE * BOARD_W - 24, BOARD_Y, str);
597 rb->snprintf (str, sizeof (str), "Score: %d", player.score);
598 rb->lcd_putsxy (BOARD_X, BOARD_Y + CUBE_SIZE * BOARD_H - 8, str);
599 rb->snprintf (str, sizeof (str),
600 (player.lives != 1) ? "%d Lives" : "%d Life", player.lives);
601 #if LCD_DEPTH>=2
602 rb->lcd_putsxy (BOARD_X + CUBE_SIZE * BOARD_W - 60,
603 BOARD_Y + CUBE_SIZE * BOARD_H - 8, str);
604 #else
605 rb->lcd_putsxy (BOARD_X + CUBE_SIZE * BOARD_W - 40,
606 BOARD_Y + CUBE_SIZE * BOARD_H - 8, str);
607 #endif
609 #if LCD_DEPTH>=2
610 rb->lcd_set_foreground (PLR_COL);
611 rb->lcd_set_background (board[player.j][player.i]);
612 #endif
613 rb->lcd_mono_bitmap (pics[PIC_PLAYER], player.i * CUBE_SIZE + BOARD_X,
614 player.j * CUBE_SIZE + BOARD_Y, CUBE_SIZE, CUBE_SIZE);
616 #if LCD_DEPTH>=2
617 rb->lcd_set_background (EMPTIED);
618 rb->lcd_set_foreground (LCD_WHITE);
619 rb->lcd_set_drawmode (DRMODE_FG);
620 #else
621 rb->lcd_set_drawmode (DRMODE_FG);
622 #endif
623 for (j = 0; j < player.level + STARTING_QIXES; j++)
624 rb->lcd_mono_bitmap (pics[PIC_QIX], qixes[j].x + BOARD_X,
625 qixes[j].y + BOARD_Y, CUBE_SIZE, CUBE_SIZE);
626 #if LCD_DEPTH>=2
627 rb->lcd_set_foreground (LCD_BLACK);
628 #endif
629 rb->lcd_set_drawmode (DRMODE_SOLID);
631 rb->lcd_update ();
634 static inline int infested_area (int i, int j, int v)
636 struct pos p;
637 p.x = i;
638 p.y = j;
639 emptyStack ();
640 if (!push (&p))
641 return -1;
642 while (pop (&p)) {
643 if (testboard[p.y][p.x] == v) continue;
644 if (testboard[p.y][p.x] > UNCHECKED)
645 return 1; /* This area was previously flagged as infested */
646 testboard[p.y][p.x] = v;
647 if (board[p.y][p.x] == QIX)
648 return 1; /* Infested area */
650 struct pos p1 = { p.x+1, p.y };
651 if ((p1.x < BOARD_W)
652 && (board[p1.y][p1.x] != FILLED)
653 && (!push (&p1)))
654 return -1;
657 struct pos p1 = { p.x-1, p.y };
658 if ((p1.x >= 0)
659 && (board[p1.y][p1.x] != FILLED)
660 && (!push (&p1)))
661 return -1;
664 struct pos p1 = { p.x, p.y+1 };
665 if ((p1.y < BOARD_H)
666 && (board[p1.y][p1.x] != FILLED)
667 && (!push (&p1)))
668 return -1;
671 struct pos p1 = { p.x, p.y-1 };
672 if ((p1.y >= 0)
673 && (board[p1.y][p1.x] != FILLED)
674 && (!push (&p1)))
675 return -1;
678 return 0;
681 static inline int fill_area (int i, int j)
683 struct pos p;
684 p.x = i;
685 p.y = j;
686 int v = testboard[p.y][p.x];
687 emptyStack ();
688 if (!push (&p))
689 return -1;
690 while (pop (&p)) {
691 board[p.y][p.x] = FILLED;
692 testboard[p.y][p.x] = PAINTED;
694 struct pos p1 = { p.x+1, p.y };
695 if ((p1.x < BOARD_W)
696 && (testboard[p1.y][p1.x] == v)
697 && (!push (&p1)))
698 return -1;
701 struct pos p1 = { p.x-1, p.y };
702 if ((p1.x >= 0)
703 && (testboard[p1.y][p1.x] == v)
704 && (!push (&p1)))
705 return -1;
708 struct pos p1 = { p.x, p.y+1 };
709 if ((p1.y < BOARD_H)
710 && (testboard[p1.y][p1.x] == v)
711 && (!push (&p1)))
712 return -1;
715 struct pos p1 = { p.x, p.y-1 };
716 if ((p1.y >= 0)
717 && (testboard[p1.y][p1.x] == v)
718 && (!push (&p1)))
719 return -1;
722 return 0;
726 /* take care of stuff after xonix has landed on a filled spot */
727 static void complete_trail (int fill)
729 int i, j, ret;
730 for (j = 0; j < BOARD_H; j++) {
731 for (i = 0; i < BOARD_W; i++) {
732 if (board[j][i] == TRAIL) {
733 if (fill)
734 board[j][i] = FILLED;
735 else
736 board[j][i] = EMPTIED;
741 if (fill) {
742 int v = CHECKED;
743 for (i = 0; i < player.level + STARTING_QIXES; i++) /* add qixes to board */
744 board[pos(qixes[i].y - BOARD_Y)]
745 [pos(qixes[i].x - BOARD_X)] = QIX;
747 init_testboard();
748 for (j = 1; j < BOARD_H - 1; j++) {
749 for (i = 0; i < BOARD_W - 0; i++) {
750 if (board[j][i] != FILLED) {
751 ret = infested_area (i, j, v);
752 if (ret < 0 || ( ret == 0 && fill_area (i, j) ) )
753 quit = true;
754 v++;
759 for (i = 0; i < player.level + STARTING_QIXES; i++) /* add qixes to board */
760 board[pos(qixes[i].y - BOARD_Y)]
761 [pos(qixes[i].x - BOARD_X)] = EMPTIED;
762 percentage_cache = percentage();
765 rb->button_clear_queue();
768 /* returns the color the real pixel(x,y) on the lcd is pointing at */
769 static inline unsigned int getpixel (int x, int y)
771 const int a = pos (x - BOARD_X), b = pos (y - BOARD_Y);
772 if ((a > 0) && (a < BOARD_W) && (b > 0) && (b < BOARD_H)) /* if inside board */
773 return board[b][a];
774 else
775 return FILLED;
778 /* returns the color the ball on (newx,newy) is heading at *----*
779 checks the four edge points of the square if 1st of all | |
780 are a trail (cause it's a lose life situation) and 2nd | |
781 if it's filled so it needs to bounce. *____*
783 static inline unsigned int next_hit (int newx, int newy)
785 if ((getpixel (newx, newy) == TRAIL)
786 || (getpixel (newx, newy + CUBE_SIZE - 1) == TRAIL)
787 || (getpixel (newx + CUBE_SIZE - 1, newy) == TRAIL)
788 || (getpixel (newx + CUBE_SIZE - 1, newy + CUBE_SIZE - 1) == TRAIL))
789 return TRAIL;
790 else if ((getpixel (newx, newy) == FILLED)
791 || (getpixel (newx, newy + CUBE_SIZE - 1) == FILLED)
792 || (getpixel (newx + CUBE_SIZE - 1, newy) == FILLED)
793 || (getpixel (newx + CUBE_SIZE - 1, newy + CUBE_SIZE - 1) ==
794 FILLED))
795 return FILLED;
796 else
797 return EMPTIED;
800 static void die (void)
802 player.lives--;
803 if (player.lives == 0)
804 player.gameover = true;
805 else {
806 refresh_board ();
807 rb->splash (HZ, "Crash!");
808 complete_trail (false);
809 player.move = MOVE_NO;
810 player.drawing = false;
811 player.i = BOARD_W / 2;
812 player.j = 1;
816 /* returns true if the (side) of the block -***-
817 starting from (newx,newy) has any filled pixels * *
818 -***-
820 static inline bool line_check_lt (int newx, int newy)
822 return getpixel (newx, newy + CUBE_SIZE/2-1) == FILLED
823 && getpixel (newx, newy + CUBE_SIZE/2 ) == FILLED;
825 static inline bool line_check_rt (int newx, int newy)
827 return getpixel (newx + CUBE_SIZE-1, newy + CUBE_SIZE/2-1) == FILLED
828 && getpixel (newx + CUBE_SIZE-1, newy + CUBE_SIZE/2 ) == FILLED;
830 static inline bool line_check_up (int newx, int newy)
832 return getpixel (newx + CUBE_SIZE/2-1, newy) == FILLED
833 && getpixel (newx + CUBE_SIZE/2 , newy) == FILLED;
835 static inline bool line_check_dn (int newx, int newy)
837 return getpixel (newx + CUBE_SIZE/2-1, newy + CUBE_SIZE-1) == FILLED
838 && getpixel (newx + CUBE_SIZE/2 , newy + CUBE_SIZE-1) == FILLED;
841 static inline void move_qix (struct qix *q)
843 int newx, newy;
844 newx = get_newx (q->x, q->velocity, q->angle);
845 newy = get_newy (q->y, q->velocity, q->angle);
846 switch (next_hit (newx, newy))
848 case EMPTIED:
849 q->x = newx;
850 q->y = newy;
851 break;
852 case FILLED:
854 const int a = q->angle;
855 q->angle =
856 ((a&(DIR_UU|DIR_U))
857 ? (line_check_up (newx, newy) ? ((a&(DIR_UU|DIR_U))>>4)
858 : (a&(DIR_UU|DIR_U)))
859 : 0)
861 ((a&(DIR_RR|DIR_R))
862 ? (line_check_rt (newx, newy) ? ((a&(DIR_RR|DIR_R))>>4)
863 : (a&(DIR_RR|DIR_R)))
864 : 0)
866 ((a&(DIR_DD|DIR_D))
867 ? (line_check_dn (newx, newy) ? ((a&(DIR_DD|DIR_D))<<4)
868 : (a&(DIR_DD|DIR_D)))
869 : 0)
871 ((a&(DIR_LL|DIR_L))
872 ? (line_check_lt (newx, newy) ? ((a&(DIR_LL|DIR_L))<<4)
873 : (a&(DIR_LL|DIR_L)))
874 : 0);
875 q->x = get_newx (q->x, q->velocity, q->angle);
876 q->y = get_newy (q->y, q->velocity, q->angle);
877 break;
879 case TRAIL:
880 die();
881 break;
885 /* move the board forward timewise */
886 static inline void move_board (void)
888 int j, newi, newj;
890 for (j = 0; j < player.level + STARTING_QIXES; j++)
891 move_qix (&qixes[j]);
892 /* move_qix(&black_qix,true); */
893 if (player.move) {
894 newi = player.i;
895 newj = player.j;
896 switch (player.move) {
897 case MOVE_UP:
898 if (player.j > 1)
899 newj--;
900 break;
901 case MOVE_DN:
902 if (player.j < BOARD_H - 2)
903 newj++;
904 break;
905 case MOVE_LT:
906 if (player.i > 0)
907 newi--;
908 break;
909 case MOVE_RT:
910 if (player.i < BOARD_W - 1)
911 newi++;
912 break;
913 default:
914 break;
917 if ((player.drawing) && (board[newj][newi] == EMPTIED)) /* continue drawing */
918 board[newj][newi] = TRAIL;
919 else if ((player.drawing) && (board[newj][newi] == FILLED)) { /* finish drawing */
920 player.move = MOVE_NO; /* stop moving */
921 player.drawing = false;
922 complete_trail (true);
923 } else if ((board[player.j][player.i] == FILLED)
924 && (board[newj][newi] == EMPTIED)) {
925 /* start drawing */
926 player.drawing = true;
927 board[newj][newi] = TRAIL;
928 /* if the block after next is empty and we're moving onto filled, stop */
929 } else if ((board[newj][newi] == FILLED)
930 && (board[newj + newj-player.j][newi + newi-player.i] == EMPTIED)) {
931 player.move = MOVE_NO;
933 player.i = newi;
934 player.j = newj;
936 if (percentage_cache >= difficulty) { /* finished level */
937 refresh_board ();
938 rb->splashf (HZ * 2, "Level %d finished", player.level+1);
939 player.score += percentage_cache;
940 if (player.level < MAX_LEVEL)
941 player.level++;
942 init_board ();
943 refresh_board ();
944 rb->splash (HZ * 2, "Ready?");
948 /* init game's variables */
949 static void init_game (void)
951 player.level = 0;
952 player.score = 0;
953 player.lives = 3;
954 player.gameover = false;
955 player.drawing = false;
956 init_board ();
957 refresh_board ();
958 rb->splash (HZ * 2, "Ready?");
961 /* the main menu */
962 static bool _ingame;
963 static int xobox_menu_cb(int action, const struct menu_item_ex *this_item)
965 if(action == ACTION_REQUEST_MENUITEM
966 && !_ingame && ((intptr_t)this_item)==0)
967 return ACTION_EXIT_MENUITEM;
968 return action;
971 static int xobox_menu(bool ingame)
973 rb->button_clear_queue();
975 int selection = 0;
976 MENUITEM_STRINGLIST(main_menu, "Xobox Menu", xobox_menu_cb,
977 "Resume Game", "Start New Game",
978 "Speed", "Difficulty",
979 "Playback Control", "Quit");
980 _ingame = ingame;
982 while (true) {
983 switch (rb->do_menu(&main_menu, &selection, NULL, false)) {
984 case 0:
985 return 0;
986 case 1:
987 init_game ();
988 return 0;
989 case 2:
990 rb->set_int ("Speed", "", UNIT_INT, &speed, NULL, 1, 1, 10, NULL);
991 break;
992 case 3:
993 rb->set_int ("Difficulty", "", UNIT_INT, &difficulty, NULL,
994 5, 50, 95, NULL);
995 break;
996 case 4:
997 playback_control(NULL);
998 break;
999 case 5:
1000 return 1;
1001 case MENU_ATTACHED_USB:
1002 return 1;
1003 default:
1004 break;
1009 /* general keypad handler loop */
1010 static int xobox_loop (void)
1012 int button = 0;
1013 bool pause = false;
1014 int end;
1016 if (xobox_menu(false)) {
1017 return PLUGIN_OK;
1020 while (!quit) {
1021 end = *rb->current_tick + ((11-speed)*HZ)/100;
1023 #ifdef HAS_BUTTON_HOLD
1024 if (rb->button_hold()) {
1025 pause = true;
1026 rb->splash (HZ, "Paused");
1028 #endif
1030 button = rb->button_get_w_tmo (1);
1031 switch (button) {
1032 case UP:
1033 case UP|BUTTON_REPEAT:
1034 player.move = MOVE_UP;
1035 break;
1036 case DOWN:
1037 case DOWN|BUTTON_REPEAT:
1038 player.move = MOVE_DN;
1039 break;
1040 case LEFT:
1041 case LEFT|BUTTON_REPEAT:
1042 player.move = MOVE_LT;
1043 break;
1044 case RIGHT:
1045 case RIGHT|BUTTON_REPEAT:
1046 player.move = MOVE_RT;
1047 break;
1048 case PAUSE:
1049 pause = !pause;
1050 if (pause)
1051 rb->splash (HZ, "Paused");
1052 break;
1053 case QUIT:
1054 if (!pause) {
1055 if (xobox_menu(true)) {
1056 quit = true;
1059 break;
1060 default:
1061 if (rb->default_event_handler (button) == SYS_USB_CONNECTED)
1062 return PLUGIN_USB_CONNECTED;
1063 break;
1065 if (!pause) {
1066 move_board ();
1067 refresh_board ();
1069 if (player.gameover) {
1070 rb->splash (HZ, "Game Over!");
1071 if (xobox_menu(false)) {
1072 quit = true;
1076 if (TIME_BEFORE(*rb->current_tick, end))
1077 rb->sleep (end - *rb->current_tick);
1078 else
1079 rb->yield ();
1081 } /* end while */
1082 return PLUGIN_OK; /* for no warnings on compiling */
1085 /* plugin main procedure */
1086 enum plugin_status plugin_start (const void *parameter)
1088 int ret = PLUGIN_OK;
1090 (void) parameter;
1092 rb->lcd_setfont (FONT_SYSFIXED);
1093 #if LCD_DEPTH>=2
1094 rb->lcd_set_backdrop(NULL);
1095 #endif
1097 /* Turn off backlight timeout */
1098 backlight_force_on(); /* backlight control in lib/helper.c */
1100 randomize ();
1101 ret = xobox_loop ();
1103 /* Turn on backlight timeout (revert to settings) */
1104 backlight_use_settings(); /* backlight control in lib/helper.c */
1105 rb->lcd_setfont (FONT_UI);
1107 return ret;