Bump version numbers for 3.13
[maemo-rb.git] / apps / plugins / xobox.c
blob2696b09c171574fdfc3f43561adaa4084bff6b39
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"
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_HDD6330_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 == PHILIPS_SA9200_PAD
213 #define QUIT BUTTON_POWER
214 #define LEFT BUTTON_PREV
215 #define RIGHT BUTTON_NEXT
216 #define UP BUTTON_UP
217 #define DOWN BUTTON_DOWN
218 #define PAUSE BUTTON_MENU
220 #elif CONFIG_KEYPAD == ONDAVX747_PAD || \
221 CONFIG_KEYPAD == ONDAVX777_PAD || \
222 CONFIG_KEYPAD == MROBE500_PAD
224 #define QUIT BUTTON_POWER
226 #elif CONFIG_KEYPAD == SAMSUNG_YH_PAD
228 #define QUIT BUTTON_PLAY
229 #define LEFT BUTTON_LEFT
230 #define RIGHT BUTTON_RIGHT
231 #define UP BUTTON_UP
232 #define DOWN BUTTON_DOWN
233 #define PAUSE BUTTON_FFWD
235 #elif CONFIG_KEYPAD == PBELL_VIBE500_PAD
237 #define QUIT BUTTON_REC
238 #define LEFT BUTTON_PREV
239 #define RIGHT BUTTON_NEXT
240 #define UP BUTTON_UP
241 #define DOWN BUTTON_DOWN
242 #define PAUSE BUTTON_PLAY
244 #elif CONFIG_KEYPAD == MPIO_HD200_PAD
246 #define QUIT (BUTTON_REC|BUTTON_PLAY)
247 #define LEFT BUTTON_VOL_DOWN
248 #define RIGHT BUTTON_VOL_UP
249 #define UP BUTTON_REW
250 #define DOWN BUTTON_FF
251 #define PAUSE BUTTON_PLAY
253 #elif CONFIG_KEYPAD == MPIO_HD300_PAD
255 #define QUIT (BUTTON_MENU | BUTTON_REPEAT)
256 #define LEFT BUTTON_REW
257 #define RIGHT BUTTON_FF
258 #define UP BUTTON_UP
259 #define DOWN BUTTON_DOWN
260 #define PAUSE BUTTON_PLAY
262 #elif CONFIG_KEYPAD == SANSA_FUZEPLUS_PAD
264 #define QUIT BUTTON_POWER
265 #define LEFT BUTTON_LEFT
266 #define RIGHT BUTTON_RIGHT
267 #define UP BUTTON_UP
268 #define DOWN BUTTON_DOWN
269 #define PAUSE BUTTON_PLAYPAUSE
271 #elif CONFIG_KEYPAD == SANSA_CONNECT_PAD
273 #define QUIT BUTTON_POWER
274 #define LEFT BUTTON_LEFT
275 #define RIGHT BUTTON_RIGHT
276 #define UP BUTTON_UP
277 #define DOWN BUTTON_DOWN
278 #define PAUSE BUTTON_SELECT
280 #elif (CONFIG_KEYPAD == SAMSUNG_YPR0_PAD)
282 #define QUIT BUTTON_BACK
283 #define LEFT BUTTON_LEFT
284 #define RIGHT BUTTON_RIGHT
285 #define UP BUTTON_UP
286 #define DOWN BUTTON_DOWN
287 #define PAUSE BUTTON_SELECT
289 #elif (CONFIG_KEYPAD == HM60X_PAD) || \
290 (CONFIG_KEYPAD == HM801_PAD)
292 #define QUIT BUTTON_POWER
293 #define LEFT BUTTON_LEFT
294 #define RIGHT BUTTON_RIGHT
295 #define UP BUTTON_UP
296 #define DOWN BUTTON_DOWN
297 #define PAUSE BUTTON_SELECT
299 #else
300 #error No keymap defined!
301 #endif
303 #ifdef HAVE_TOUCHSCREEN
304 #ifndef QUIT
305 #define QUIT BUTTON_TOPLEFT
306 #endif
307 #ifndef LEFT
308 #define LEFT BUTTON_MIDLEFT
309 #endif
310 #ifndef RIGHT
311 #define RIGHT BUTTON_MIDRIGHT
312 #endif
313 #ifndef UP
314 #define UP BUTTON_TOPMIDDLE
315 #endif
316 #ifndef DOWN
317 #define DOWN BUTTON_BOTTOMMIDDLE
318 #endif
319 #ifndef PAUSE
320 #define PAUSE BUTTON_CENTER
321 #endif
322 #endif
324 #define MOVE_NO 0 /* player movement */
325 #define MOVE_UP 1 /* 1 */
326 #define MOVE_DN 2 /* 3 0 4 */
327 #define MOVE_LT 3 /* 2 */
328 #define MOVE_RT 4
330 /* ball movement (12 ways) */
331 /* UUL UR */
332 /* UL UR */
333 /* ULL . URR */
334 /* DLL DRR */
335 /* DL DR */
336 /* DDL DDR */
338 #define DIR_UU (1<<7)
339 #define DIR_U (1<<6)
340 #define DIR_RR (1<<5)
341 #define DIR_R (1<<4)
342 #define DIR_DD (1<<3)
343 #define DIR_D (1<<2)
344 #define DIR_LL (1<<1)
345 #define DIR_L (1<<0)
347 #define MOVE_UUR ( DIR_UU | DIR_R )
348 #define MOVE_UR ( DIR_U | DIR_R )
349 #define MOVE_URR ( DIR_U | DIR_RR )
350 #define MOVE_DRR ( DIR_D | DIR_RR )
351 #define MOVE_DR ( DIR_D | DIR_R )
352 #define MOVE_DDR ( DIR_DD | DIR_R )
353 #define MOVE_DDL ( DIR_DD | DIR_L )
354 #define MOVE_DL ( DIR_D | DIR_L )
355 #define MOVE_DLL ( DIR_D | DIR_LL )
356 #define MOVE_ULL ( DIR_U | DIR_LL )
357 #define MOVE_UL ( DIR_U | DIR_L )
358 #define MOVE_UUL ( DIR_UU | DIR_L )
360 #if (LCD_WIDTH>112) && (LCD_HEIGHT>64)
361 # define CUBE_SIZE 8 /* 8x22=176 */
362 # define pos(a) ((a)>>3)
363 #else
364 # define CUBE_SIZE 4
365 # define pos(a) ((a)>>2)
366 #endif
368 #define STARTING_QIXES 2
369 #define MAX_LEVEL 10
370 #define MAX_QIXES MAX_LEVEL+STARTING_QIXES
371 #define BOARD_W ((int)(LCD_WIDTH/CUBE_SIZE))
372 #define BOARD_H ((int)(LCD_HEIGHT/CUBE_SIZE))
373 #define BOARD_X (LCD_WIDTH-BOARD_W*CUBE_SIZE)/2
374 #define BOARD_Y (LCD_HEIGHT-BOARD_H*CUBE_SIZE)/2
376 #ifdef HAVE_LCD_COLOR
377 #define CLR_RED LCD_RGBPACK(255,0,0) /* used to imply danger */
378 #define CLR_LTBLUE LCD_RGBPACK(125, 145, 180) /* used for frame and filling */
379 #define PLR_COL LCD_WHITE /* color used for the player */
380 #elif LCD_DEPTH>=2
381 #define CLR_RED LCD_DARKGRAY /* used to imply danger */
382 #define CLR_LTBLUE LCD_LIGHTGRAY /* used for frame and filling */
383 #define PLR_COL LCD_BLACK /* color used for the player */
384 #endif
386 #if LCD_DEPTH>=2
387 #define EMPTIED LCD_BLACK /* empty spot */
388 #define FILLED CLR_LTBLUE /* filled spot */
389 #define TRAIL CLR_RED /* the red trail of the player */
390 #define QIX LCD_WHITE
391 #else
392 #define EMPTIED 0
393 #define FILLED 1
394 #define TRAIL 2
395 #define QIX 3
396 #endif
397 #define UNCHECKED 0
398 #define CHECKED 1
399 #define PAINTED -1
400 #define PIC_QIX 0
401 #define PIC_PLAYER 1
403 /* The time (in ms) for one iteration through the game loop - decrease this
404 to speed up the game - note that current_tick is (currently) only accurate
405 to 10ms.
407 static int speed = 6; /* CYCLETIME = (11-speed)*10 ms */
408 static int difficulty = 75; /* Percentage of screen that needs to be filled
409 * in order to win the game */
411 static bool quit = false;
413 static unsigned int board[BOARD_H][BOARD_W];
414 static int testboard[BOARD_H][BOARD_W];
416 #if CUBE_SIZE == 8
418 00011000 0x18 - 11100111 0xe7
419 00111100 0x3c - 11100111 0xe7
420 01111110 0x7e - 11000011 0xc3
421 11111111 0xff - 00000000 0x00
422 11111111 0xff - 00000000 0x00
423 01111110 0x7e - 11000011 0xc3
424 00111100 0x3c - 11100111 0xe7
425 00011000 0x18 - 11100111 0xe7
427 const unsigned char pics[2][8] = {
428 {0x18, 0x3c, 0x7e, 0xff, 0xff, 0x7e, 0x3c, 0x18}, /* Alien (QIX) */
429 {0xe7, 0xe7, 0xc3, 0x00, 0x00, 0xc3, 0xe7, 0xe7} /* Player (XONIX) */
431 #elif CUBE_SIZE == 4
433 0110 0x6 - 1001 0x9
434 1111 0xf - 0110 0x6
435 1111 0xf - 0110 0x6
436 0110 0x6 - 1001 0x9
438 const unsigned char pics[2][4] = {
439 {0x6, 0xf, 0xf, 0x6}, /* Alien (QIX) */
440 {0x9, 0x6, 0x6, 0x9} /* Player (XONIX) */
442 #else
443 #error Incorrect CUBE_SIZE value.
444 #endif
446 static struct qix
448 int velocity; /* velocity */
449 int x, y; /* position on screen */
450 int angle; /* angle */
451 } qixes[MAX_QIXES]; /* black_qix */
453 static struct splayer
455 int i, j; /* position on board */
456 int move, score, level, lives;
457 bool drawing;
458 bool gameover;
459 } player;
461 static int percentage_cache;
463 /*************************** STACK STUFF **********************/
465 /* the stack */
466 #define STACK_SIZE (2*BOARD_W*BOARD_H)
467 static struct pos
469 int x, y; /* position on board */
470 } stack[STACK_SIZE];
471 static int stackPointer;
473 static inline bool pop (struct pos *p)
475 if (stackPointer > 0) {
476 p->x = stack[stackPointer].x;
477 p->y = stack[stackPointer].y;
478 stackPointer--;
479 return true;
480 } else
481 return false; /* SE */
484 static inline bool push (struct pos *p)
486 if (stackPointer < STACK_SIZE - 1) {
487 stackPointer++;
488 stack[stackPointer].x = p->x;
489 stack[stackPointer].y = p->y;
490 return true;
491 } else
492 return false; /* SOF */
495 static inline void emptyStack (void)
497 stackPointer = 0;
500 /*********************** END OF STACK STUFF *********************/
502 /* calculate the new x coordinate of the ball according to angle and speed */
503 static inline int get_newx (int x, int len, int deg)
505 if (deg & DIR_R)
506 return x + len;
507 else if (deg & DIR_L)
508 return x - len;
509 else if (deg & DIR_RR)
510 return x + len * 2;
511 else /* (def & DIR_LL) */
512 return x - len * 2;
515 /* calculate the new y coordinate of the ball according to angle and speed */
516 static inline int get_newy (int y, int len, int deg)
518 if (deg & DIR_D)
519 return y + len;
520 else if (deg & DIR_U)
521 return y - len;
522 else if (deg & DIR_DD)
523 return y + len * 2;
524 else /* (deg & DIR_UU) */
525 return y - len * 2;
528 /* make random function get it's value from the device ticker */
529 static inline void randomize (void)
531 rb->srand (*rb->current_tick);
534 /* get a random number between 0 and range-1 */
535 static int t_rand (int range)
537 return rb->rand () % range;
540 /* initializes the test help board */
541 static void init_testboard (void)
543 int j; /* testboard */
544 for (j = 0; j < BOARD_H; j++)
545 /* UNCHEKED == (int)0 */
546 rb->memset( testboard[j], 0, BOARD_W * sizeof( int ) );
549 /* initializes the game board on with the player,qix's and black qix */
550 static void init_board (void)
552 int i, j;
553 for (j = 0; j < BOARD_H; j++)
554 for (i = 0; i < BOARD_W; i++) { /* make a nice cyan frame */
555 if ((i == 0) || (j <= 1) || (i == BOARD_W - 1)
556 || (j >= BOARD_H - 2))
557 board[j][i] = FILLED;
558 else
559 board[j][i] = EMPTIED;
562 /* (level+2) is the number of qixes */
563 for (j = 0; j < player.level + STARTING_QIXES; j++) {
564 qixes[j].velocity = t_rand (2) + 1; /* 1 or 2 pix-per-sec */
566 /* not on frame */
567 qixes[j].x = CUBE_SIZE*2 + 2*t_rand (((BOARD_W-4)*CUBE_SIZE)/2);
568 qixes[j].y = CUBE_SIZE*2 + 2*t_rand (((BOARD_H-4)*CUBE_SIZE)/2);
570 const int angle_table[] = {
571 MOVE_UUR, MOVE_UR, MOVE_URR, MOVE_DRR, MOVE_DR, MOVE_DDR,
572 MOVE_UUL, MOVE_UL, MOVE_ULL, MOVE_DLL, MOVE_DL, MOVE_DDL };
573 qixes[j].angle = angle_table[t_rand (12)];
574 #if CUBE_SIZE == 4
575 /* Work arround a nasty bug. FIXME */
576 if( qixes[j].angle & (DIR_LL|DIR_RR|DIR_UU|DIR_DD) )
577 qixes[j].velocity = 1;
578 #endif
580 /*black_qix.velocity=1;
581 black_qix.x=BOARD_X+(BOARD_W*CUBE_SIZE)/2-CUBE_SIZE/2;
582 black_qix.y=BOARD_Y+(BOARD_H*CUBE_SIZE)-CUBE_SIZE-CUBE_SIZE/2;
583 black_qix.angle=MOVE_UR; */
584 player.move = MOVE_NO;
585 player.drawing = false;
586 player.i = BOARD_W / 2;
587 player.j = 1;
589 percentage_cache = 0;
592 /* calculates the percentage of the screen filling */
593 static int percentage (void)
595 int i, j, filled = 0;
596 for (j = 2; j < BOARD_H - 2; j++)
597 for (i = 1; i < BOARD_W - 1; i++)
598 if (board[j][i] == FILLED)
599 filled++;
600 return (filled * 100) / ((BOARD_W - 2) * (BOARD_H - 4));
603 /* draw the board on with all the game figures */
604 static void refresh_board (void)
606 int i, j;
607 int x;
609 #if LCD_DEPTH>=2
610 rb->lcd_set_background (LCD_BLACK);
611 #endif
612 rb->lcd_clear_display ();
613 for (j = 0; j < BOARD_H; j++)
615 unsigned last_color = board[j][0];
616 int last_i = 0;
617 for (i = 1; i < BOARD_W; i++) {
618 if( last_color != board[j][i] )
620 #if LCD_DEPTH>=2
621 rb->lcd_set_foreground (last_color);
622 #else
623 if (last_color != EMPTIED)
624 #endif
625 rb->lcd_fillrect (BOARD_X + CUBE_SIZE * (last_i),
626 BOARD_Y + CUBE_SIZE * j,
627 CUBE_SIZE * (i - last_i), CUBE_SIZE );
628 last_color = board[j][i];
629 last_i = i;
632 #if LCD_DEPTH>=2
633 rb->lcd_set_foreground (last_color);
634 #else
635 if (last_color != EMPTIED)
636 #endif
637 rb->lcd_fillrect (BOARD_X + CUBE_SIZE * (last_i),
638 BOARD_Y + CUBE_SIZE * j,
639 CUBE_SIZE * (i - last_i), CUBE_SIZE);
642 #if LCD_DEPTH>=2
643 rb->lcd_set_foreground (LCD_BLACK);
644 rb->lcd_set_background (CLR_LTBLUE);
645 #else
646 rb->lcd_set_drawmode (DRMODE_COMPLEMENT);
647 #endif
648 rb->lcd_putsxyf (BOARD_X, BOARD_Y, "Level %d", player.level + 1);
649 rb->lcd_putsxyf (BOARD_X + CUBE_SIZE * BOARD_W - 24, BOARD_Y, "%d%%",
650 percentage_cache);
651 rb->lcd_putsxyf (BOARD_X, BOARD_Y + CUBE_SIZE * BOARD_H - 8, "Score: %d",
652 player.score);
653 #if LCD_DEPTH>=2
654 x = BOARD_X + CUBE_SIZE * BOARD_W - 60;
655 #else
656 x = BOARD_X + CUBE_SIZE * BOARD_W - 40;
657 #endif
658 rb->lcd_putsxyf (x, BOARD_Y + CUBE_SIZE * BOARD_H - 8,
659 (player.lives != 1) ? "%d Lives" : "%d Life", player.lives);
661 #if LCD_DEPTH>=2
662 rb->lcd_set_foreground (PLR_COL);
663 rb->lcd_set_background (board[player.j][player.i]);
664 #endif
665 rb->lcd_mono_bitmap (pics[PIC_PLAYER], player.i * CUBE_SIZE + BOARD_X,
666 player.j * CUBE_SIZE + BOARD_Y, CUBE_SIZE, CUBE_SIZE);
668 #if LCD_DEPTH>=2
669 rb->lcd_set_background (EMPTIED);
670 rb->lcd_set_foreground (LCD_WHITE);
671 rb->lcd_set_drawmode (DRMODE_FG);
672 #else
673 rb->lcd_set_drawmode (DRMODE_FG);
674 #endif
675 for (j = 0; j < player.level + STARTING_QIXES; j++)
676 rb->lcd_mono_bitmap (pics[PIC_QIX], qixes[j].x + BOARD_X,
677 qixes[j].y + BOARD_Y, CUBE_SIZE, CUBE_SIZE);
678 #if LCD_DEPTH>=2
679 rb->lcd_set_foreground (LCD_BLACK);
680 #endif
681 rb->lcd_set_drawmode (DRMODE_SOLID);
683 rb->lcd_update ();
686 static inline int infested_area (int i, int j, int v)
688 struct pos p;
689 p.x = i;
690 p.y = j;
691 emptyStack ();
692 if (!push (&p))
693 return -1;
694 while (pop (&p)) {
695 if (testboard[p.y][p.x] == v) continue;
696 if (testboard[p.y][p.x] > UNCHECKED)
697 return 1; /* This area was previously flagged as infested */
698 testboard[p.y][p.x] = v;
699 if (board[p.y][p.x] == QIX)
700 return 1; /* Infested area */
702 struct pos p1 = { p.x+1, p.y };
703 if ((p1.x < BOARD_W)
704 && (board[p1.y][p1.x] != FILLED)
705 && (!push (&p1)))
706 return -1;
709 struct pos p1 = { p.x-1, p.y };
710 if ((p1.x >= 0)
711 && (board[p1.y][p1.x] != FILLED)
712 && (!push (&p1)))
713 return -1;
716 struct pos p1 = { p.x, p.y+1 };
717 if ((p1.y < BOARD_H)
718 && (board[p1.y][p1.x] != FILLED)
719 && (!push (&p1)))
720 return -1;
723 struct pos p1 = { p.x, p.y-1 };
724 if ((p1.y >= 0)
725 && (board[p1.y][p1.x] != FILLED)
726 && (!push (&p1)))
727 return -1;
730 return 0;
733 static inline int fill_area (int i, int j)
735 struct pos p;
736 p.x = i;
737 p.y = j;
738 int v = testboard[p.y][p.x];
739 emptyStack ();
740 if (!push (&p))
741 return -1;
742 while (pop (&p)) {
743 board[p.y][p.x] = FILLED;
744 testboard[p.y][p.x] = PAINTED;
746 struct pos p1 = { p.x+1, p.y };
747 if ((p1.x < BOARD_W)
748 && (testboard[p1.y][p1.x] == v)
749 && (!push (&p1)))
750 return -1;
753 struct pos p1 = { p.x-1, p.y };
754 if ((p1.x >= 0)
755 && (testboard[p1.y][p1.x] == v)
756 && (!push (&p1)))
757 return -1;
760 struct pos p1 = { p.x, p.y+1 };
761 if ((p1.y < BOARD_H)
762 && (testboard[p1.y][p1.x] == v)
763 && (!push (&p1)))
764 return -1;
767 struct pos p1 = { p.x, p.y-1 };
768 if ((p1.y >= 0)
769 && (testboard[p1.y][p1.x] == v)
770 && (!push (&p1)))
771 return -1;
774 return 0;
778 /* take care of stuff after xonix has landed on a filled spot */
779 static void complete_trail (int fill)
781 int i, j, ret;
782 for (j = 0; j < BOARD_H; j++) {
783 for (i = 0; i < BOARD_W; i++) {
784 if (board[j][i] == TRAIL) {
785 if (fill)
786 board[j][i] = FILLED;
787 else
788 board[j][i] = EMPTIED;
793 if (fill) {
794 int v = CHECKED;
795 for (i = 0; i < player.level + STARTING_QIXES; i++) /* add qixes to board */
796 board[pos(qixes[i].y - BOARD_Y)]
797 [pos(qixes[i].x - BOARD_X)] = QIX;
799 init_testboard();
800 for (j = 1; j < BOARD_H - 1; j++) {
801 for (i = 0; i < BOARD_W - 0; i++) {
802 if (board[j][i] != FILLED) {
803 ret = infested_area (i, j, v);
804 if (ret < 0 || ( ret == 0 && fill_area (i, j) ) )
805 quit = true;
806 v++;
811 for (i = 0; i < player.level + STARTING_QIXES; i++) /* add qixes to board */
812 board[pos(qixes[i].y - BOARD_Y)]
813 [pos(qixes[i].x - BOARD_X)] = EMPTIED;
814 percentage_cache = percentage();
817 rb->button_clear_queue();
820 /* returns the color the real pixel(x,y) on the lcd is pointing at */
821 static inline unsigned int getpixel (int x, int y)
823 const int a = pos (x - BOARD_X), b = pos (y - BOARD_Y);
824 if ((a > 0) && (a < BOARD_W) && (b > 0) && (b < BOARD_H)) /* if inside board */
825 return board[b][a];
826 else
827 return FILLED;
830 /* returns the color the ball on (newx,newy) is heading at *----*
831 checks the four edge points of the square if 1st of all | |
832 are a trail (cause it's a lose life situation) and 2nd | |
833 if it's filled so it needs to bounce. *____*
835 static inline unsigned int next_hit (int newx, int newy)
837 if ((getpixel (newx, newy) == TRAIL)
838 || (getpixel (newx, newy + CUBE_SIZE - 1) == TRAIL)
839 || (getpixel (newx + CUBE_SIZE - 1, newy) == TRAIL)
840 || (getpixel (newx + CUBE_SIZE - 1, newy + CUBE_SIZE - 1) == TRAIL))
841 return TRAIL;
842 else if ((getpixel (newx, newy) == FILLED)
843 || (getpixel (newx, newy + CUBE_SIZE - 1) == FILLED)
844 || (getpixel (newx + CUBE_SIZE - 1, newy) == FILLED)
845 || (getpixel (newx + CUBE_SIZE - 1, newy + CUBE_SIZE - 1) ==
846 FILLED))
847 return FILLED;
848 else
849 return EMPTIED;
852 static void die (void)
854 player.lives--;
855 if (player.lives == 0)
856 player.gameover = true;
857 else {
858 refresh_board ();
859 rb->splash (HZ, "Crash!");
860 complete_trail (false);
861 player.move = MOVE_NO;
862 player.drawing = false;
863 player.i = BOARD_W / 2;
864 player.j = 1;
868 /* returns true if the (side) of the block -***-
869 starting from (newx,newy) has any filled pixels * *
870 -***-
872 static inline bool line_check_lt (int newx, int newy)
874 return getpixel (newx, newy + CUBE_SIZE/2-1) == FILLED
875 && getpixel (newx, newy + CUBE_SIZE/2 ) == FILLED;
877 static inline bool line_check_rt (int newx, int newy)
879 return getpixel (newx + CUBE_SIZE-1, newy + CUBE_SIZE/2-1) == FILLED
880 && getpixel (newx + CUBE_SIZE-1, newy + CUBE_SIZE/2 ) == FILLED;
882 static inline bool line_check_up (int newx, int newy)
884 return getpixel (newx + CUBE_SIZE/2-1, newy) == FILLED
885 && getpixel (newx + CUBE_SIZE/2 , newy) == FILLED;
887 static inline bool line_check_dn (int newx, int newy)
889 return getpixel (newx + CUBE_SIZE/2-1, newy + CUBE_SIZE-1) == FILLED
890 && getpixel (newx + CUBE_SIZE/2 , newy + CUBE_SIZE-1) == FILLED;
893 static inline void move_qix (struct qix *q)
895 int newx, newy;
896 newx = get_newx (q->x, q->velocity, q->angle);
897 newy = get_newy (q->y, q->velocity, q->angle);
898 switch (next_hit (newx, newy))
900 case EMPTIED:
901 q->x = newx;
902 q->y = newy;
903 break;
904 case FILLED:
906 const int a = q->angle;
907 q->angle =
908 ((a&(DIR_UU|DIR_U))
909 ? (line_check_up (newx, newy) ? ((a&(DIR_UU|DIR_U))>>4)
910 : (a&(DIR_UU|DIR_U)))
911 : 0)
913 ((a&(DIR_RR|DIR_R))
914 ? (line_check_rt (newx, newy) ? ((a&(DIR_RR|DIR_R))>>4)
915 : (a&(DIR_RR|DIR_R)))
916 : 0)
918 ((a&(DIR_DD|DIR_D))
919 ? (line_check_dn (newx, newy) ? ((a&(DIR_DD|DIR_D))<<4)
920 : (a&(DIR_DD|DIR_D)))
921 : 0)
923 ((a&(DIR_LL|DIR_L))
924 ? (line_check_lt (newx, newy) ? ((a&(DIR_LL|DIR_L))<<4)
925 : (a&(DIR_LL|DIR_L)))
926 : 0);
927 q->x = get_newx (q->x, q->velocity, q->angle);
928 q->y = get_newy (q->y, q->velocity, q->angle);
929 break;
931 case TRAIL:
932 die();
933 break;
937 /* move the board forward timewise */
938 static inline void move_board (void)
940 int j, newi, newj;
942 for (j = 0; j < player.level + STARTING_QIXES; j++)
943 move_qix (&qixes[j]);
944 /* move_qix(&black_qix,true); */
945 if (player.move) {
946 newi = player.i;
947 newj = player.j;
948 switch (player.move) {
949 case MOVE_UP:
950 if (player.j > 1)
951 newj--;
952 break;
953 case MOVE_DN:
954 if (player.j < BOARD_H - 2)
955 newj++;
956 break;
957 case MOVE_LT:
958 if (player.i > 0)
959 newi--;
960 break;
961 case MOVE_RT:
962 if (player.i < BOARD_W - 1)
963 newi++;
964 break;
965 default:
966 break;
969 if ((player.drawing) && (board[newj][newi] == EMPTIED)) /* continue drawing */
970 board[newj][newi] = TRAIL;
971 else if ((player.drawing) && (board[newj][newi] == FILLED)) { /* finish drawing */
972 player.move = MOVE_NO; /* stop moving */
973 player.drawing = false;
974 complete_trail (true);
975 } else if ((board[player.j][player.i] == FILLED)
976 && (board[newj][newi] == EMPTIED)) {
977 /* start drawing */
978 player.drawing = true;
979 board[newj][newi] = TRAIL;
980 /* if the block after next is empty and we're moving onto filled, stop */
981 } else if ((board[newj][newi] == FILLED)
982 && (board[newj + newj-player.j][newi + newi-player.i] == EMPTIED)) {
983 player.move = MOVE_NO;
985 player.i = newi;
986 player.j = newj;
988 if (percentage_cache >= difficulty) { /* finished level */
989 refresh_board ();
990 rb->splashf (HZ * 2, "Level %d finished", player.level+1);
991 player.score += percentage_cache;
992 if (player.level < MAX_LEVEL)
993 player.level++;
994 init_board ();
995 refresh_board ();
996 rb->button_clear_queue();
997 rb->splash (HZ * 2, "Ready?");
1001 /* init game's variables */
1002 static void init_game (void)
1004 player.level = 0;
1005 player.score = 0;
1006 player.lives = 3;
1007 player.gameover = false;
1008 player.drawing = false;
1009 init_board ();
1010 refresh_board ();
1011 rb->splash (HZ * 2, "Ready?");
1014 /* the main menu */
1015 static bool _ingame;
1016 static int xobox_menu_cb(int action, const struct menu_item_ex *this_item)
1018 if(action == ACTION_REQUEST_MENUITEM
1019 && !_ingame && ((intptr_t)this_item)==0)
1020 return ACTION_EXIT_MENUITEM;
1021 return action;
1024 static int xobox_menu(bool ingame)
1026 rb->button_clear_queue();
1028 int selection = 0;
1029 MENUITEM_STRINGLIST(main_menu, "Xobox Menu", xobox_menu_cb,
1030 "Resume Game", "Start New Game",
1031 "Speed", "Difficulty",
1032 "Playback Control", "Quit");
1033 _ingame = ingame;
1035 while (true) {
1036 switch (rb->do_menu(&main_menu, &selection, NULL, false)) {
1037 case 0:
1038 return 0;
1039 case 1:
1040 init_game ();
1041 return 0;
1042 case 2:
1043 rb->set_int ("Speed", "", UNIT_INT, &speed, NULL, 1, 1, 10, NULL);
1044 break;
1045 case 3:
1046 rb->set_int ("Difficulty", "", UNIT_INT, &difficulty, NULL,
1047 5, 50, 95, NULL);
1048 break;
1049 case 4:
1050 playback_control(NULL);
1051 break;
1052 case 5:
1053 return 1;
1054 case MENU_ATTACHED_USB:
1055 return 1;
1056 default:
1057 break;
1062 /* general keypad handler loop */
1063 static int xobox_loop (void)
1065 int button = 0;
1066 bool pause = false;
1067 int end;
1069 if (xobox_menu(false)) {
1070 return PLUGIN_OK;
1073 while (!quit) {
1074 end = *rb->current_tick + ((11-speed)*HZ)/100;
1076 #ifdef HAS_BUTTON_HOLD
1077 if (rb->button_hold()) {
1078 pause = true;
1079 rb->splash (HZ, "Paused");
1081 #endif
1083 button = rb->button_get_w_tmo (1);
1084 switch (button) {
1085 case UP:
1086 case UP|BUTTON_REPEAT:
1087 player.move = MOVE_UP;
1088 break;
1089 case DOWN:
1090 case DOWN|BUTTON_REPEAT:
1091 player.move = MOVE_DN;
1092 break;
1093 case LEFT:
1094 case LEFT|BUTTON_REPEAT:
1095 player.move = MOVE_LT;
1096 break;
1097 case RIGHT:
1098 case RIGHT|BUTTON_REPEAT:
1099 player.move = MOVE_RT;
1100 break;
1101 case PAUSE:
1102 pause = !pause;
1103 if (pause)
1104 rb->splash (HZ, "Paused");
1105 break;
1106 case QUIT:
1107 if (!pause) {
1108 if (xobox_menu(true)) {
1109 quit = true;
1112 break;
1113 default:
1114 if (rb->default_event_handler (button) == SYS_USB_CONNECTED)
1115 return PLUGIN_USB_CONNECTED;
1116 break;
1118 if (!pause) {
1119 move_board ();
1120 refresh_board ();
1122 if (player.gameover) {
1123 rb->splash (HZ, "Game Over!");
1124 if (xobox_menu(false)) {
1125 quit = true;
1129 if (TIME_BEFORE(*rb->current_tick, end))
1130 rb->sleep (end - *rb->current_tick);
1131 else
1132 rb->yield ();
1134 } /* end while */
1135 return PLUGIN_OK; /* for no warnings on compiling */
1138 /* plugin main procedure */
1139 enum plugin_status plugin_start (const void *parameter)
1141 int ret = PLUGIN_OK;
1143 (void) parameter;
1145 rb->lcd_setfont (FONT_SYSFIXED);
1146 #if LCD_DEPTH>=2
1147 rb->lcd_set_backdrop(NULL);
1148 #endif
1150 /* Turn off backlight timeout */
1151 backlight_ignore_timeout();
1153 randomize ();
1154 ret = xobox_loop ();
1156 /* Turn on backlight timeout (revert to settings) */
1157 backlight_use_settings();
1158 rb->lcd_setfont (FONT_UI);
1160 return ret;