Bring mpegplayer backlight fix to the other plugins, this also fixes some wrongly...
[Rockbox.git] / apps / plugins / xobox.c
blob9a112aa5eb2f66ff2b3dd2f8cab076b7d5c12ad3
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)
79 #define QUIT BUTTON_POWER
80 #define LEFT BUTTON_LEFT
81 #define RIGHT BUTTON_RIGHT
82 #define UP BUTTON_UP
83 #define DOWN BUTTON_DOWN
84 #define PAUSE BUTTON_REC
87 #elif CONFIG_KEYPAD == IRIVER_H10_PAD
89 #define QUIT BUTTON_POWER
90 #define LEFT BUTTON_LEFT
91 #define RIGHT BUTTON_RIGHT
92 #define UP BUTTON_SCROLL_UP
93 #define DOWN BUTTON_SCROLL_DOWN
94 #define PAUSE BUTTON_PLAY
96 #elif CONFIG_KEYPAD == RECORDER_PAD
98 #define QUIT BUTTON_OFF
99 #define LEFT BUTTON_LEFT
100 #define RIGHT BUTTON_RIGHT
101 #define DOWN BUTTON_DOWN
102 #define UP BUTTON_UP
103 #define PAUSE BUTTON_PLAY
105 #elif CONFIG_KEYPAD == ONDIO_PAD
107 #define QUIT BUTTON_OFF
108 #define LEFT BUTTON_LEFT
109 #define RIGHT BUTTON_RIGHT
110 #define DOWN BUTTON_DOWN
111 #define UP BUTTON_UP
112 #define PAUSE BUTTON_MENU
114 #else
115 #error Unsupported keypad
116 #endif
118 #define MOVE_NO 0 /* player movement */
119 #define MOVE_UP 1 /* 1 */
120 #define MOVE_DN 2 /* 3 0 4 */
121 #define MOVE_LT 3 /* 2 */
122 #define MOVE_RT 4
124 /* ball movement (12 ways) */
125 /* UUL UR */
126 /* UL UR */
127 /* ULL . URR */
128 /* DLL DRR */
129 /* DL DR */
130 /* DDL DDR */
132 #define DIR_UU (1<<7)
133 #define DIR_U (1<<6)
134 #define DIR_RR (1<<5)
135 #define DIR_R (1<<4)
136 #define DIR_DD (1<<3)
137 #define DIR_D (1<<2)
138 #define DIR_LL (1<<1)
139 #define DIR_L (1<<0)
141 #define MOVE_UUR ( DIR_UU | DIR_R )
142 #define MOVE_UR ( DIR_U | DIR_R )
143 #define MOVE_URR ( DIR_U | DIR_RR )
144 #define MOVE_DRR ( DIR_D | DIR_RR )
145 #define MOVE_DR ( DIR_D | DIR_R )
146 #define MOVE_DDR ( DIR_DD | DIR_R )
147 #define MOVE_DDL ( DIR_DD | DIR_L )
148 #define MOVE_DL ( DIR_D | DIR_L )
149 #define MOVE_DLL ( DIR_D | DIR_LL )
150 #define MOVE_ULL ( DIR_U | DIR_LL )
151 #define MOVE_UL ( DIR_U | DIR_L )
152 #define MOVE_UUL ( DIR_UU | DIR_L )
154 #if (LCD_WIDTH>112) && (LCD_HEIGHT>64)
155 # define CUBE_SIZE 8 /* 8x22=176 */
156 # define pos(a) ((a)>>3)
157 #else
158 # define CUBE_SIZE 4
159 # define pos(a) ((a)>>2)
160 #endif
162 #define STARTING_QIXES 2
163 #define MAX_LEVEL 10
164 #define MAX_QIXES MAX_LEVEL+STARTING_QIXES
165 #define BOARD_W ((int)(LCD_WIDTH/CUBE_SIZE))
166 #define BOARD_H ((int)(LCD_HEIGHT/CUBE_SIZE))
167 #define BOARD_X (LCD_WIDTH-BOARD_W*CUBE_SIZE)/2
168 #define BOARD_Y (LCD_HEIGHT-BOARD_H*CUBE_SIZE)/2
170 #ifdef HAVE_LCD_COLOR
171 #define CLR_RED LCD_RGBPACK(255,0,0) /* used to imply danger */
172 #define CLR_LTBLUE LCD_RGBPACK(125, 145, 180) /* used for frame and filling */
173 #define PLR_COL LCD_WHITE /* color used for the player */
174 #elif LCD_DEPTH>=2
175 #define CLR_RED LCD_DARKGRAY /* used to imply danger */
176 #define CLR_LTBLUE LCD_LIGHTGRAY /* used for frame and filling */
177 #define PLR_COL LCD_BLACK /* color used for the player */
178 #endif
180 #if LCD_DEPTH>=2
181 #define EMPTIED LCD_BLACK /* empty spot */
182 #define FILLED CLR_LTBLUE /* filled spot */
183 #define TRAIL CLR_RED /* the red trail of the player */
184 #define QIX LCD_WHITE
185 #else
186 #define EMPTIED 0
187 #define FILLED 1
188 #define TRAIL 2
189 #define QIX 3
190 #endif
191 #define UNCHECKED 0
192 #define CHECKED 1
193 #define PAINTED -1
194 #define PIC_QIX 0
195 #define PIC_PLAYER 1
197 #define MENU_START 0
198 #define MENU_QUIT 1
200 /* The time (in ms) for one iteration through the game loop - decrease this
201 to speed up the game - note that current_tick is (currently) only accurate
202 to 10ms.
204 static int speed = 6; /* CYCLETIME = (11-speed)*10 ms */
205 static int difficulty = 75; /* Percentage of screen that needs to be filled
206 * in order to win the game */
208 static struct plugin_api *rb;
210 MEM_FUNCTION_WRAPPERS(rb);
212 static bool quit = false;
214 static unsigned int board[BOARD_H][BOARD_W];
215 static int testboard[BOARD_H][BOARD_W];
217 #if CUBE_SIZE == 8
219 00011000 0x18 - 11100111 0xe7
220 00111100 0x3c - 11100111 0xe7
221 01111110 0x7e - 11000011 0xc3
222 11111111 0xff - 00000000 0x00
223 11111111 0xff - 00000000 0x00
224 01111110 0x7e - 11000011 0xc3
225 00111100 0x3c - 11100111 0xe7
226 00011000 0x18 - 11100111 0xe7
228 const unsigned char pics[2][8] = {
229 {0x18, 0x3c, 0x7e, 0xff, 0xff, 0x7e, 0x3c, 0x18}, /* Alien (QIX) */
230 {0xe7, 0xe7, 0xc3, 0x00, 0x00, 0xc3, 0xe7, 0xe7} /* Player (XONIX) */
232 #elif CUBE_SIZE == 4
234 0110 0x6 - 1001 0x9
235 1111 0xf - 0110 0x6
236 1111 0xf - 0110 0x6
237 0110 0x6 - 1001 0x9
239 const unsigned char pics[2][4] = {
240 {0x6, 0xf, 0xf, 0x6}, /* Alien (QIX) */
241 {0x9, 0x6, 0x6, 0x9} /* Player (XONIX) */
243 #else
244 #error Incorrect CUBE_SIZE value.
245 #endif
247 static struct qix
249 int velocity; /* velocity */
250 int x, y; /* position on screen */
251 int angle; /* angle */
252 } qixes[MAX_QIXES]; /* black_qix */
254 static struct splayer
256 int i, j; /* position on board */
257 int move, score, level, lives;
258 bool drawing;
259 bool gameover;
260 } player;
262 static int percentage_cache;
264 /*************************** STACK STUFF **********************/
266 /* the stack */
267 #define STACK_SIZE (2*BOARD_W*BOARD_H)
268 static struct pos
270 int x, y; /* position on board */
271 } stack[STACK_SIZE];
272 static int stackPointer;
274 static inline bool pop (struct pos *p)
276 if (stackPointer > 0) {
277 p->x = stack[stackPointer].x;
278 p->y = stack[stackPointer].y;
279 stackPointer--;
280 return true;
281 } else
282 return false; /* SE */
285 static inline bool push (struct pos *p)
287 if (stackPointer < STACK_SIZE - 1) {
288 stackPointer++;
289 stack[stackPointer].x = p->x;
290 stack[stackPointer].y = p->y;
291 return true;
292 } else
293 return false; /* SOF */
296 static inline void emptyStack (void)
298 stackPointer = 0;
301 /*********************** END OF STACK STUFF *********************/
303 /* calculate the new x coordinate of the ball according to angle and speed */
304 static inline int get_newx (int x, int len, int deg)
306 if (deg & DIR_R)
307 return x + len;
308 else if (deg & DIR_L)
309 return x - len;
310 else if (deg & DIR_RR)
311 return x + len * 2;
312 else /* (def & DIR_LL) */
313 return x - len * 2;
316 /* calculate the new y coordinate of the ball according to angle and speed */
317 static inline int get_newy (int y, int len, int deg)
319 if (deg & DIR_D)
320 return y + len;
321 else if (deg & DIR_U)
322 return y - len;
323 else if (deg & DIR_DD)
324 return y + len * 2;
325 else /* (deg & DIR_UU) */
326 return y - len * 2;
329 /* make random function get it's value from the device ticker */
330 static inline void randomize (void)
332 rb->srand (*rb->current_tick);
335 /* get a random number between 0 and range-1 */
336 static int t_rand (int range)
338 return rb->rand () % range;
341 /* initializes the test help board */
342 static void init_testboard (void)
344 int j; /* testboard */
345 for (j = 0; j < BOARD_H; j++)
346 /* UNCHEKED == (int)0 */
347 rb->memset( testboard[j], 0, BOARD_W * sizeof( int ) );
350 /* initializes the game board on with the player,qix's and black qix */
351 static void init_board (void)
353 int i, j;
354 for (j = 0; j < BOARD_H; j++)
355 for (i = 0; i < BOARD_W; i++) { /* make a nice cyan frame */
356 if ((i == 0) || (j <= 1) || (i == BOARD_W - 1)
357 || (j >= BOARD_H - 2))
358 board[j][i] = FILLED;
359 else
360 board[j][i] = EMPTIED;
363 /* (level+2) is the number of qixes */
364 for (j = 0; j < player.level + STARTING_QIXES; j++) {
365 qixes[j].velocity = t_rand (2) + 1; /* 1 or 2 pix-per-sec */
367 /* not on frame */
368 qixes[j].x = CUBE_SIZE*2 + 2*t_rand (((BOARD_W-4)*CUBE_SIZE)/2);
369 qixes[j].y = CUBE_SIZE*2 + 2*t_rand (((BOARD_H-4)*CUBE_SIZE)/2);
371 const int angle_table[] = {
372 MOVE_UUR, MOVE_UR, MOVE_URR, MOVE_DRR, MOVE_DR, MOVE_DDR,
373 MOVE_UUL, MOVE_UL, MOVE_ULL, MOVE_DLL, MOVE_DL, MOVE_DDL };
374 qixes[j].angle = angle_table[t_rand (12)];
375 #if CUBE_SIZE == 4
376 /* Work arround a nasty bug. FIXME */
377 if( qixes[j].angle & (DIR_LL|DIR_RR|DIR_UU|DIR_DD) )
378 qixes[j].velocity = 1;
379 #endif
381 /*black_qix.velocity=1;
382 black_qix.x=BOARD_X+(BOARD_W*CUBE_SIZE)/2-CUBE_SIZE/2;
383 black_qix.y=BOARD_Y+(BOARD_H*CUBE_SIZE)-CUBE_SIZE-CUBE_SIZE/2;
384 black_qix.angle=MOVE_UR; */
385 player.move = MOVE_NO;
386 player.drawing = false;
387 player.i = BOARD_W / 2;
388 player.j = 1;
390 percentage_cache = 0;
393 /* calculates the percentage of the screen filling */
394 static int percentage (void)
396 int i, j, filled = 0;
397 for (j = 2; j < BOARD_H - 2; j++)
398 for (i = 1; i < BOARD_W - 1; i++)
399 if (board[j][i] == FILLED)
400 filled++;
401 return (filled * 100) / ((BOARD_W - 2) * (BOARD_H - 4));
404 /* draw the board on with all the game figures */
405 static void refresh_board (void)
407 int i, j;
408 char str[25];
410 #if LCD_DEPTH>=2
411 rb->lcd_set_background (LCD_BLACK);
412 #else
413 rb->lcd_clear_display ();
414 #endif
415 for (j = 0; j < BOARD_H; j++)
417 unsigned last_color = board[j][0];
418 int last_i = 0;
419 for (i = 1; i < BOARD_W; i++) {
420 if( last_color != board[j][i] )
422 #if LCD_DEPTH>=2
423 rb->lcd_set_foreground (last_color);
424 #else
425 if (last_color != EMPTIED)
426 #endif
427 rb->lcd_fillrect (BOARD_X + CUBE_SIZE * (last_i),
428 BOARD_Y + CUBE_SIZE * j,
429 CUBE_SIZE * (i - last_i), CUBE_SIZE );
430 last_color = board[j][i];
431 last_i = i;
434 #if LCD_DEPTH>=2
435 rb->lcd_set_foreground (last_color);
436 #else
437 if (last_color != EMPTIED)
438 #endif
439 rb->lcd_fillrect (BOARD_X + CUBE_SIZE * (last_i),
440 BOARD_Y + CUBE_SIZE * j,
441 CUBE_SIZE * (i - last_i), CUBE_SIZE);
444 #if LCD_DEPTH>=2
445 rb->lcd_set_foreground (LCD_BLACK);
446 rb->lcd_set_background (CLR_LTBLUE);
447 #else
448 rb->lcd_set_drawmode (DRMODE_COMPLEMENT);
449 #endif
450 rb->snprintf (str, sizeof (str), "Level %d", player.level + 1);
451 rb->lcd_putsxy (BOARD_X, BOARD_Y, str);
452 rb->snprintf (str, sizeof (str), "%d%%", percentage_cache);
453 rb->lcd_putsxy (BOARD_X + CUBE_SIZE * BOARD_W - 24, BOARD_Y, str);
454 rb->snprintf (str, sizeof (str), "Score: %d", player.score);
455 rb->lcd_putsxy (BOARD_X, BOARD_Y + CUBE_SIZE * BOARD_H - 8, str);
456 rb->snprintf (str, sizeof (str), "%d Lives", player.lives);
457 #if LCD_DEPTH>=2
458 rb->lcd_putsxy (BOARD_X + CUBE_SIZE * BOARD_W - 60,
459 BOARD_Y + CUBE_SIZE * BOARD_H - 8, str);
460 #else
461 rb->lcd_putsxy (BOARD_X + CUBE_SIZE * BOARD_W - 40,
462 BOARD_Y + CUBE_SIZE * BOARD_H - 8, str);
463 #endif
465 #if LCD_DEPTH>=2
466 rb->lcd_set_foreground (PLR_COL);
467 rb->lcd_set_background (board[player.j][player.i]);
468 #endif
469 rb->lcd_mono_bitmap (pics[PIC_PLAYER], player.i * CUBE_SIZE + BOARD_X,
470 player.j * CUBE_SIZE + BOARD_Y, CUBE_SIZE, CUBE_SIZE);
472 #if LCD_DEPTH>=2
473 rb->lcd_set_background (EMPTIED);
474 rb->lcd_set_foreground (LCD_WHITE);
475 rb->lcd_set_drawmode (DRMODE_FG);
476 #else
477 rb->lcd_set_drawmode (DRMODE_FG);
478 #endif
479 for (j = 0; j < player.level + STARTING_QIXES; j++)
480 rb->lcd_mono_bitmap (pics[PIC_QIX], qixes[j].x + BOARD_X,
481 qixes[j].y + BOARD_Y, CUBE_SIZE, CUBE_SIZE);
482 #if LCD_DEPTH>=2
483 rb->lcd_set_foreground (LCD_BLACK);
484 #endif
485 rb->lcd_set_drawmode (DRMODE_SOLID);
487 rb->lcd_update ();
490 static inline int infested_area (int i, int j, int v)
492 struct pos p;
493 p.x = i;
494 p.y = j;
495 emptyStack ();
496 if (!push (&p))
497 return -1;
498 while (pop (&p)) {
499 if (testboard[p.y][p.x] == v) continue;
500 if (testboard[p.y][p.x] > UNCHECKED)
501 return 1; /* This area was previously flagged as infested */
502 testboard[p.y][p.x] = v;
503 if (board[p.y][p.x] == QIX)
504 return 1; /* Infested area */
506 struct pos p1 = { p.x+1, p.y };
507 if ((p1.x < BOARD_W)
508 && (board[p1.y][p1.x] != FILLED)
509 && (!push (&p1)))
510 return -1;
513 struct pos p1 = { p.x-1, p.y };
514 if ((p1.x >= 0)
515 && (board[p1.y][p1.x] != FILLED)
516 && (!push (&p1)))
517 return -1;
520 struct pos p1 = { p.x, p.y+1 };
521 if ((p1.y < BOARD_H)
522 && (board[p1.y][p1.x] != FILLED)
523 && (!push (&p1)))
524 return -1;
527 struct pos p1 = { p.x, p.y-1 };
528 if ((p1.y >= 0)
529 && (board[p1.y][p1.x] != FILLED)
530 && (!push (&p1)))
531 return -1;
534 return 0;
537 static inline int fill_area (int i, int j)
539 struct pos p;
540 p.x = i;
541 p.y = j;
542 int v = testboard[p.y][p.x];
543 emptyStack ();
544 if (!push (&p))
545 return -1;
546 while (pop (&p)) {
547 board[p.y][p.x] = FILLED;
548 testboard[p.y][p.x] = PAINTED;
550 struct pos p1 = { p.x+1, p.y };
551 if ((p1.x < BOARD_W)
552 && (testboard[p1.y][p1.x] == v)
553 && (!push (&p1)))
554 return -1;
557 struct pos p1 = { p.x-1, p.y };
558 if ((p1.x >= 0)
559 && (testboard[p1.y][p1.x] == v)
560 && (!push (&p1)))
561 return -1;
564 struct pos p1 = { p.x, p.y+1 };
565 if ((p1.y < BOARD_H)
566 && (testboard[p1.y][p1.x] == v)
567 && (!push (&p1)))
568 return -1;
571 struct pos p1 = { p.x, p.y-1 };
572 if ((p1.y >= 0)
573 && (testboard[p1.y][p1.x] == v)
574 && (!push (&p1)))
575 return -1;
578 return 0;
582 /* take care of stuff after xonix has landed on a filled spot */
583 static void complete_trail (int fill)
585 int i, j, ret;
586 for (j = 0; j < BOARD_H; j++) {
587 for (i = 0; i < BOARD_W; i++) {
588 if (board[j][i] == TRAIL) {
589 if (fill)
590 board[j][i] = FILLED;
591 else
592 board[j][i] = EMPTIED;
597 if (fill) {
598 int v = CHECKED;
599 for (i = 0; i < player.level + STARTING_QIXES; i++) /* add qixes to board */
600 board[pos(qixes[i].y - BOARD_Y)]
601 [pos(qixes[i].x - BOARD_X)] = QIX;
603 init_testboard();
604 for (j = 1; j < BOARD_H - 1; j++) {
605 for (i = 0; i < BOARD_W - 0; i++) {
606 if (board[j][i] != FILLED) {
607 ret = infested_area (i, j, v);
608 if (ret < 0 || ( ret == 0 && fill_area (i, j) ) )
609 quit = true;
610 v++;
615 for (i = 0; i < player.level + STARTING_QIXES; i++) /* add qixes to board */
616 board[pos(qixes[i].y - BOARD_Y)]
617 [pos(qixes[i].x - BOARD_X)] = EMPTIED;
618 percentage_cache = percentage();
621 rb->button_clear_queue();
624 /* returns the color the real pixel(x,y) on the lcd is pointing at */
625 static inline unsigned int getpixel (int x, int y)
627 const int a = pos (x - BOARD_X), b = pos (y - BOARD_Y);
628 if ((a > 0) && (a < BOARD_W) && (b > 0) && (b < BOARD_H)) /* if inside board */
629 return board[b][a];
630 else
631 return FILLED;
634 /* returns the color the ball on (newx,newy) is heading at *----*
635 checks the four edge points of the square if 1st of all | |
636 are a trail (cause it's a lose life situation) and 2nd | |
637 if it's filled so it needs to bounce. *____*
639 static inline unsigned int next_hit (int newx, int newy)
641 if ((getpixel (newx, newy) == TRAIL)
642 || (getpixel (newx, newy + CUBE_SIZE - 1) == TRAIL)
643 || (getpixel (newx + CUBE_SIZE - 1, newy) == TRAIL)
644 || (getpixel (newx + CUBE_SIZE - 1, newy + CUBE_SIZE - 1) == TRAIL))
645 return TRAIL;
646 else if ((getpixel (newx, newy) == FILLED)
647 || (getpixel (newx, newy + CUBE_SIZE - 1) == FILLED)
648 || (getpixel (newx + CUBE_SIZE - 1, newy) == FILLED)
649 || (getpixel (newx + CUBE_SIZE - 1, newy + CUBE_SIZE - 1) ==
650 FILLED))
651 return FILLED;
652 else
653 return EMPTIED;
656 static void die (void)
658 player.lives--;
659 if (player.lives == 0)
660 player.gameover = true;
661 else {
662 refresh_board ();
663 rb->splash (HZ, "Crash!");
664 complete_trail (false);
665 player.move = MOVE_NO;
666 player.drawing = false;
667 player.i = BOARD_W / 2;
668 player.j = 1;
672 /* returns true if the (side) of the block -***-
673 starting from (newx,newy) has any filled pixels * *
674 -***-
676 static inline bool line_check_lt (int newx, int newy)
678 return getpixel (newx, newy + CUBE_SIZE/2-1) == FILLED
679 && getpixel (newx, newy + CUBE_SIZE/2 ) == FILLED;
681 static inline bool line_check_rt (int newx, int newy)
683 return getpixel (newx + CUBE_SIZE-1, newy + CUBE_SIZE/2-1) == FILLED
684 && getpixel (newx + CUBE_SIZE-1, newy + CUBE_SIZE/2 ) == FILLED;
686 static inline bool line_check_up (int newx, int newy)
688 return getpixel (newx + CUBE_SIZE/2-1, newy) == FILLED
689 && getpixel (newx + CUBE_SIZE/2 , newy) == FILLED;
691 static inline bool line_check_dn (int newx, int newy)
693 return getpixel (newx + CUBE_SIZE/2-1, newy + CUBE_SIZE-1) == FILLED
694 && getpixel (newx + CUBE_SIZE/2 , newy + CUBE_SIZE-1) == FILLED;
697 static inline void move_qix (struct qix *q)
699 int newx, newy;
700 newx = get_newx (q->x, q->velocity, q->angle);
701 newy = get_newy (q->y, q->velocity, q->angle);
702 switch (next_hit (newx, newy))
704 case EMPTIED:
705 q->x = newx;
706 q->y = newy;
707 break;
708 case FILLED:
710 const int a = q->angle;
711 q->angle =
712 ((a&(DIR_UU|DIR_U))
713 ? (line_check_up (newx, newy) ? ((a&(DIR_UU|DIR_U))>>4)
714 : (a&(DIR_UU|DIR_U)))
715 : 0)
717 ((a&(DIR_RR|DIR_R))
718 ? (line_check_rt (newx, newy) ? ((a&(DIR_RR|DIR_R))>>4)
719 : (a&(DIR_RR|DIR_R)))
720 : 0)
722 ((a&(DIR_DD|DIR_D))
723 ? (line_check_dn (newx, newy) ? ((a&(DIR_DD|DIR_D))<<4)
724 : (a&(DIR_DD|DIR_D)))
725 : 0)
727 ((a&(DIR_LL|DIR_L))
728 ? (line_check_lt (newx, newy) ? ((a&(DIR_LL|DIR_L))<<4)
729 : (a&(DIR_LL|DIR_L)))
730 : 0);
731 q->x = get_newx (q->x, q->velocity, q->angle);
732 q->y = get_newy (q->y, q->velocity, q->angle);
733 break;
735 case TRAIL:
736 die();
737 break;
741 /* move the board forward timewise */
742 static inline void move_board (void)
744 int j, newi, newj;
746 for (j = 0; j < player.level + STARTING_QIXES; j++)
747 move_qix (&qixes[j]);
748 /* move_qix(&black_qix,true); */
749 if (player.move) {
750 newi = player.i;
751 newj = player.j;
752 switch (player.move) {
753 case MOVE_UP:
754 if (player.j > 1)
755 newj--;
756 break;
757 case MOVE_DN:
758 if (player.j < BOARD_H - 2)
759 newj++;
760 break;
761 case MOVE_LT:
762 if (player.i > 0)
763 newi--;
764 break;
765 case MOVE_RT:
766 if (player.i < BOARD_W - 1)
767 newi++;
768 break;
769 default:
770 break;
773 if ((player.drawing) && (board[newj][newi] == EMPTIED)) /* continue drawing */
774 board[newj][newi] = TRAIL;
775 else if ((player.drawing) && (board[newj][newi] == FILLED)) { /* finish drawing */
776 player.move = MOVE_NO; /* stop moving */
777 player.drawing = false;
778 complete_trail (true);
779 } else if ((board[player.j][player.i] == FILLED)
780 && (board[newj][newi] == EMPTIED)) {
781 /* start drawing */
782 player.drawing = true;
783 board[newj][newi] = TRAIL;
784 /* if the block after next is empty and we're moving onto filled, stop */
785 } else if ((board[newj][newi] == FILLED)
786 && (board[newj + newj-player.j][newi + newi-player.i] == EMPTIED)) {
787 player.move = MOVE_NO;
789 player.i = newi;
790 player.j = newj;
792 if (percentage_cache >= difficulty) { /* finished level */
793 rb->splash (HZ * 2, "Level %d finished", player.level+1);
794 player.score += percentage_cache;
795 if (player.level < MAX_LEVEL)
796 player.level++;
797 init_board ();
798 refresh_board ();
799 rb->splash (HZ * 2, "Ready?");
803 /* the main menu */
804 static int game_menu (void)
806 MENUITEM_STRINGLIST(menu, "XOBOX Menu", NULL, "Start New Game",
807 "Speed","Difficulty","Quit");
808 int selection = 0;
809 #ifdef HAVE_LCD_COLOR
810 rb->lcd_set_foreground (rb->global_settings->fg_color);
811 rb->lcd_set_background (rb->global_settings->bg_color);
812 #elif LCD_DEPTH>=2
813 rb->lcd_set_foreground(LCD_BLACK);
814 rb->lcd_set_background(LCD_WHITE);
815 #endif
816 for (;;) {
817 rb->do_menu(&menu,&selection);
818 if (selection==1)
819 rb->set_int ("Speed", "", UNIT_INT, &speed, NULL, 1, 1, 10, NULL);
820 else if (selection==2)
821 rb->set_int ("Difficulty", "", UNIT_INT, &difficulty, NULL,
822 5, 50, 95, NULL);
823 else
824 break;
826 if (selection != MENU_START) {
827 selection = MENU_QUIT;
829 return selection;
832 /* init game's variables */
833 static void init_game (void)
835 player.level = 0;
836 player.score = 0;
837 player.lives = 3;
838 player.gameover = false;
839 player.drawing = false;
840 rb->lcd_setfont(FONT_SYSFIXED);
841 init_board ();
842 refresh_board ();
843 rb->splash (HZ * 2, "Ready?");
846 /* general keypad handler loop */
847 static int xobox_loop (void)
849 int button = 0, ret;
850 bool pause = false;
851 int end;
853 while (!quit) {
854 end = *rb->current_tick + ((11-speed)*HZ)/100;
856 #ifdef HAS_BUTTON_HOLD
857 if (rb->button_hold()) {
858 pause = true;
859 rb->splash (HZ, "PAUSED");
861 #endif
863 button = rb->button_get_w_tmo (true);
864 switch (button) {
865 case UP:
866 case UP|BUTTON_REPEAT:
867 player.move = MOVE_UP;
868 break;
869 case DOWN:
870 case DOWN|BUTTON_REPEAT:
871 player.move = MOVE_DN;
872 break;
873 case LEFT:
874 case LEFT|BUTTON_REPEAT:
875 player.move = MOVE_LT;
876 break;
877 case RIGHT:
878 case RIGHT|BUTTON_REPEAT:
879 player.move = MOVE_RT;
880 break;
881 case PAUSE:
882 pause = !pause;
883 if (pause)
884 rb->splash (HZ, "Paused");
885 break;
886 case QUIT:
887 ret = game_menu ();
888 if (ret == MENU_START)
889 init_game ();
890 else
892 quit = true;
893 continue;
895 break;
896 default:
897 if (rb->default_event_handler (button) == SYS_USB_CONNECTED)
898 return PLUGIN_USB_CONNECTED;
899 break;
901 if (!pause) {
902 move_board ();
903 refresh_board ();
905 if (player.gameover) {
906 rb->splash (HZ, "Game Over!");
907 ret = game_menu ();
908 if (ret == MENU_START)
909 init_game ();
910 else
911 quit = true;
914 if (end > *rb->current_tick)
915 rb->sleep (end - *rb->current_tick);
916 else
917 rb->yield ();
919 } /* end while */
920 return PLUGIN_OK; /* for no warnings on compiling */
923 /* plugin main procedure */
924 enum plugin_status plugin_start (struct plugin_api *api, void *parameter)
926 int ret = PLUGIN_OK;
928 (void) parameter;
929 rb = api;
931 rb->lcd_setfont (FONT_SYSFIXED);
932 #if LCD_DEPTH>=2
933 rb->lcd_set_backdrop(NULL);
934 #endif
936 /* Turn off backlight timeout */
937 backlight_force_on(); /* backlight control in lib/helper.c */
939 quit = false;
941 randomize ();
942 if (game_menu () == MENU_START) {
943 init_game ();
944 ret = xobox_loop ();
947 /* Turn on backlight timeout (revert to settings) */
948 backlight_use_settings(); /* backlight control in lib/helper.c */
949 rb->lcd_setfont (FONT_UI);
951 return ret;