fix some problems with the menu code:
[kugel-rb.git] / apps / plugins / xobox.c
blob59e080d2e072609cb52765c45ba8e7c884c65414
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 "helper.h"
26 PLUGIN_HEADER
28 #if (CONFIG_KEYPAD == IRIVER_H100_PAD) || (CONFIG_KEYPAD == IRIVER_H300_PAD)
30 #define QUIT BUTTON_OFF
31 #define LEFT BUTTON_LEFT
32 #define RIGHT BUTTON_RIGHT
33 #define PAUSE BUTTON_MODE
34 #define UP BUTTON_UP
35 #define DOWN BUTTON_DOWN
37 #define RC_QUIT BUTTON_RC_STOP
39 #elif (CONFIG_KEYPAD == ARCHOS_AV300_PAD)
41 #define QUIT BUTTON_OFF
42 #define LEFT BUTTON_LEFT
43 #define RIGHT BUTTON_RIGHT
44 #define PAUSE BUTTON_ON
45 #define UP BUTTON_UP
46 #define DOWN BUTTON_DOWN
48 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
49 (CONFIG_KEYPAD == IPOD_3G_PAD) || \
50 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
52 #define QUIT (BUTTON_SELECT | BUTTON_MENU)
53 #define LEFT BUTTON_LEFT
54 #define RIGHT BUTTON_RIGHT
55 #define PAUSE BUTTON_SELECT
56 #define MENU_UP BUTTON_SCROLL_FWD
57 #define MENU_DOWN BUTTON_SCROLL_BACK
58 #define UP BUTTON_MENU
59 #define DOWN BUTTON_PLAY
61 #elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD
63 #define QUIT BUTTON_POWER
64 #define LEFT BUTTON_LEFT
65 #define RIGHT BUTTON_RIGHT
66 #define UP BUTTON_UP
67 #define DOWN BUTTON_DOWN
68 #define PAUSE BUTTON_PLAY
70 #elif (CONFIG_KEYPAD == GIGABEAT_PAD)
72 #define QUIT BUTTON_POWER
73 #define LEFT BUTTON_LEFT
74 #define RIGHT BUTTON_RIGHT
75 #define UP BUTTON_UP
76 #define DOWN BUTTON_DOWN
77 #define PAUSE BUTTON_A
79 #elif (CONFIG_KEYPAD == SANSA_E200_PAD) || \
80 (CONFIG_KEYPAD == SANSA_C200_PAD)
82 #define QUIT BUTTON_POWER
83 #define LEFT BUTTON_LEFT
84 #define RIGHT BUTTON_RIGHT
85 #define UP BUTTON_UP
86 #define DOWN BUTTON_DOWN
87 #define PAUSE BUTTON_REC
90 #elif CONFIG_KEYPAD == IRIVER_H10_PAD
92 #define QUIT BUTTON_POWER
93 #define LEFT BUTTON_LEFT
94 #define RIGHT BUTTON_RIGHT
95 #define UP BUTTON_SCROLL_UP
96 #define DOWN BUTTON_SCROLL_DOWN
97 #define PAUSE BUTTON_PLAY
99 #elif CONFIG_KEYPAD == RECORDER_PAD
101 #define QUIT BUTTON_OFF
102 #define LEFT BUTTON_LEFT
103 #define RIGHT BUTTON_RIGHT
104 #define DOWN BUTTON_DOWN
105 #define UP BUTTON_UP
106 #define PAUSE BUTTON_PLAY
108 #elif CONFIG_KEYPAD == ONDIO_PAD
110 #define QUIT BUTTON_OFF
111 #define LEFT BUTTON_LEFT
112 #define RIGHT BUTTON_RIGHT
113 #define DOWN BUTTON_DOWN
114 #define UP BUTTON_UP
115 #define PAUSE BUTTON_MENU
117 #elif (CONFIG_KEYPAD == GIGABEAT_S_PAD)
119 #define QUIT BUTTON_BACK
120 #define LEFT BUTTON_LEFT
121 #define RIGHT BUTTON_RIGHT
122 #define UP BUTTON_UP
123 #define DOWN BUTTON_DOWN
124 #define PAUSE BUTTON_PLAY
126 #elif (CONFIG_KEYPAD == MROBE100_PAD)
128 #define QUIT BUTTON_POWER
129 #define LEFT BUTTON_LEFT
130 #define RIGHT BUTTON_RIGHT
131 #define UP BUTTON_UP
132 #define DOWN BUTTON_DOWN
133 #define PAUSE BUTTON_DISPLAY
135 #elif CONFIG_KEYPAD == IAUDIO_M3_PAD
137 #define QUIT BUTTON_RC_REC
138 #define LEFT BUTTON_RC_REW
139 #define RIGHT BUTTON_RC_FF
140 #define UP BUTTON_RC_VOL_UP
141 #define DOWN BUTTON_RC_VOL_DOWN
142 #define PAUSE BUTTON_RC_PLAY
144 #elif CONFIG_KEYPAD == COWOND2_PAD
146 #define QUIT BUTTON_POWER
148 #elif CONFIG_KEYPAD == IAUDIO67_PAD
150 #define QUIT BUTTON_POWER
151 #define LEFT BUTTON_LEFT
152 #define RIGHT BUTTON_RIGHT
153 #define UP BUTTON_STOP
154 #define DOWN BUTTON_PLAY
155 #define PAUSE BUTTON_MENU
157 #else
158 #error No keymap defined!
159 #endif
161 #ifdef HAVE_TOUCHSCREEN
162 #ifndef QUIT
163 #define QUIT BUTTON_TOPLEFT
164 #endif
165 #ifndef LEFT
166 #define LEFT BUTTON_MIDLEFT
167 #endif
168 #ifndef RIGHT
169 #define RIGHT BUTTON_MIDRIGHT
170 #endif
171 #ifndef UP
172 #define UP BUTTON_TOPMIDDLE
173 #endif
174 #ifndef DOWN
175 #define DOWN BUTTON_BOTTOMMIDDLE
176 #endif
177 #ifndef PAUSE
178 #define PAUSE BUTTON_CENTER
179 #endif
180 #endif
182 #define MOVE_NO 0 /* player movement */
183 #define MOVE_UP 1 /* 1 */
184 #define MOVE_DN 2 /* 3 0 4 */
185 #define MOVE_LT 3 /* 2 */
186 #define MOVE_RT 4
188 /* ball movement (12 ways) */
189 /* UUL UR */
190 /* UL UR */
191 /* ULL . URR */
192 /* DLL DRR */
193 /* DL DR */
194 /* DDL DDR */
196 #define DIR_UU (1<<7)
197 #define DIR_U (1<<6)
198 #define DIR_RR (1<<5)
199 #define DIR_R (1<<4)
200 #define DIR_DD (1<<3)
201 #define DIR_D (1<<2)
202 #define DIR_LL (1<<1)
203 #define DIR_L (1<<0)
205 #define MOVE_UUR ( DIR_UU | DIR_R )
206 #define MOVE_UR ( DIR_U | DIR_R )
207 #define MOVE_URR ( DIR_U | DIR_RR )
208 #define MOVE_DRR ( DIR_D | DIR_RR )
209 #define MOVE_DR ( DIR_D | DIR_R )
210 #define MOVE_DDR ( DIR_DD | DIR_R )
211 #define MOVE_DDL ( DIR_DD | DIR_L )
212 #define MOVE_DL ( DIR_D | DIR_L )
213 #define MOVE_DLL ( DIR_D | DIR_LL )
214 #define MOVE_ULL ( DIR_U | DIR_LL )
215 #define MOVE_UL ( DIR_U | DIR_L )
216 #define MOVE_UUL ( DIR_UU | DIR_L )
218 #if (LCD_WIDTH>112) && (LCD_HEIGHT>64)
219 # define CUBE_SIZE 8 /* 8x22=176 */
220 # define pos(a) ((a)>>3)
221 #else
222 # define CUBE_SIZE 4
223 # define pos(a) ((a)>>2)
224 #endif
226 #define STARTING_QIXES 2
227 #define MAX_LEVEL 10
228 #define MAX_QIXES MAX_LEVEL+STARTING_QIXES
229 #define BOARD_W ((int)(LCD_WIDTH/CUBE_SIZE))
230 #define BOARD_H ((int)(LCD_HEIGHT/CUBE_SIZE))
231 #define BOARD_X (LCD_WIDTH-BOARD_W*CUBE_SIZE)/2
232 #define BOARD_Y (LCD_HEIGHT-BOARD_H*CUBE_SIZE)/2
234 #ifdef HAVE_LCD_COLOR
235 #define CLR_RED LCD_RGBPACK(255,0,0) /* used to imply danger */
236 #define CLR_LTBLUE LCD_RGBPACK(125, 145, 180) /* used for frame and filling */
237 #define PLR_COL LCD_WHITE /* color used for the player */
238 #elif LCD_DEPTH>=2
239 #define CLR_RED LCD_DARKGRAY /* used to imply danger */
240 #define CLR_LTBLUE LCD_LIGHTGRAY /* used for frame and filling */
241 #define PLR_COL LCD_BLACK /* color used for the player */
242 #endif
244 #if LCD_DEPTH>=2
245 #define EMPTIED LCD_BLACK /* empty spot */
246 #define FILLED CLR_LTBLUE /* filled spot */
247 #define TRAIL CLR_RED /* the red trail of the player */
248 #define QIX LCD_WHITE
249 #else
250 #define EMPTIED 0
251 #define FILLED 1
252 #define TRAIL 2
253 #define QIX 3
254 #endif
255 #define UNCHECKED 0
256 #define CHECKED 1
257 #define PAINTED -1
258 #define PIC_QIX 0
259 #define PIC_PLAYER 1
261 #define MENU_START 0
262 #define MENU_QUIT 1
264 /* The time (in ms) for one iteration through the game loop - decrease this
265 to speed up the game - note that current_tick is (currently) only accurate
266 to 10ms.
268 static int speed = 6; /* CYCLETIME = (11-speed)*10 ms */
269 static int difficulty = 75; /* Percentage of screen that needs to be filled
270 * in order to win the game */
272 static const struct plugin_api *rb;
274 MEM_FUNCTION_WRAPPERS(rb);
276 static bool quit = false;
278 static unsigned int board[BOARD_H][BOARD_W];
279 static int testboard[BOARD_H][BOARD_W];
281 #if CUBE_SIZE == 8
283 00011000 0x18 - 11100111 0xe7
284 00111100 0x3c - 11100111 0xe7
285 01111110 0x7e - 11000011 0xc3
286 11111111 0xff - 00000000 0x00
287 11111111 0xff - 00000000 0x00
288 01111110 0x7e - 11000011 0xc3
289 00111100 0x3c - 11100111 0xe7
290 00011000 0x18 - 11100111 0xe7
292 const unsigned char pics[2][8] = {
293 {0x18, 0x3c, 0x7e, 0xff, 0xff, 0x7e, 0x3c, 0x18}, /* Alien (QIX) */
294 {0xe7, 0xe7, 0xc3, 0x00, 0x00, 0xc3, 0xe7, 0xe7} /* Player (XONIX) */
296 #elif CUBE_SIZE == 4
298 0110 0x6 - 1001 0x9
299 1111 0xf - 0110 0x6
300 1111 0xf - 0110 0x6
301 0110 0x6 - 1001 0x9
303 const unsigned char pics[2][4] = {
304 {0x6, 0xf, 0xf, 0x6}, /* Alien (QIX) */
305 {0x9, 0x6, 0x6, 0x9} /* Player (XONIX) */
307 #else
308 #error Incorrect CUBE_SIZE value.
309 #endif
311 static struct qix
313 int velocity; /* velocity */
314 int x, y; /* position on screen */
315 int angle; /* angle */
316 } qixes[MAX_QIXES]; /* black_qix */
318 static struct splayer
320 int i, j; /* position on board */
321 int move, score, level, lives;
322 bool drawing;
323 bool gameover;
324 } player;
326 static int percentage_cache;
328 /*************************** STACK STUFF **********************/
330 /* the stack */
331 #define STACK_SIZE (2*BOARD_W*BOARD_H)
332 static struct pos
334 int x, y; /* position on board */
335 } stack[STACK_SIZE];
336 static int stackPointer;
338 static inline bool pop (struct pos *p)
340 if (stackPointer > 0) {
341 p->x = stack[stackPointer].x;
342 p->y = stack[stackPointer].y;
343 stackPointer--;
344 return true;
345 } else
346 return false; /* SE */
349 static inline bool push (struct pos *p)
351 if (stackPointer < STACK_SIZE - 1) {
352 stackPointer++;
353 stack[stackPointer].x = p->x;
354 stack[stackPointer].y = p->y;
355 return true;
356 } else
357 return false; /* SOF */
360 static inline void emptyStack (void)
362 stackPointer = 0;
365 /*********************** END OF STACK STUFF *********************/
367 /* calculate the new x coordinate of the ball according to angle and speed */
368 static inline int get_newx (int x, int len, int deg)
370 if (deg & DIR_R)
371 return x + len;
372 else if (deg & DIR_L)
373 return x - len;
374 else if (deg & DIR_RR)
375 return x + len * 2;
376 else /* (def & DIR_LL) */
377 return x - len * 2;
380 /* calculate the new y coordinate of the ball according to angle and speed */
381 static inline int get_newy (int y, int len, int deg)
383 if (deg & DIR_D)
384 return y + len;
385 else if (deg & DIR_U)
386 return y - len;
387 else if (deg & DIR_DD)
388 return y + len * 2;
389 else /* (deg & DIR_UU) */
390 return y - len * 2;
393 /* make random function get it's value from the device ticker */
394 static inline void randomize (void)
396 rb->srand (*rb->current_tick);
399 /* get a random number between 0 and range-1 */
400 static int t_rand (int range)
402 return rb->rand () % range;
405 /* initializes the test help board */
406 static void init_testboard (void)
408 int j; /* testboard */
409 for (j = 0; j < BOARD_H; j++)
410 /* UNCHEKED == (int)0 */
411 rb->memset( testboard[j], 0, BOARD_W * sizeof( int ) );
414 /* initializes the game board on with the player,qix's and black qix */
415 static void init_board (void)
417 int i, j;
418 for (j = 0; j < BOARD_H; j++)
419 for (i = 0; i < BOARD_W; i++) { /* make a nice cyan frame */
420 if ((i == 0) || (j <= 1) || (i == BOARD_W - 1)
421 || (j >= BOARD_H - 2))
422 board[j][i] = FILLED;
423 else
424 board[j][i] = EMPTIED;
427 /* (level+2) is the number of qixes */
428 for (j = 0; j < player.level + STARTING_QIXES; j++) {
429 qixes[j].velocity = t_rand (2) + 1; /* 1 or 2 pix-per-sec */
431 /* not on frame */
432 qixes[j].x = CUBE_SIZE*2 + 2*t_rand (((BOARD_W-4)*CUBE_SIZE)/2);
433 qixes[j].y = CUBE_SIZE*2 + 2*t_rand (((BOARD_H-4)*CUBE_SIZE)/2);
435 const int angle_table[] = {
436 MOVE_UUR, MOVE_UR, MOVE_URR, MOVE_DRR, MOVE_DR, MOVE_DDR,
437 MOVE_UUL, MOVE_UL, MOVE_ULL, MOVE_DLL, MOVE_DL, MOVE_DDL };
438 qixes[j].angle = angle_table[t_rand (12)];
439 #if CUBE_SIZE == 4
440 /* Work arround a nasty bug. FIXME */
441 if( qixes[j].angle & (DIR_LL|DIR_RR|DIR_UU|DIR_DD) )
442 qixes[j].velocity = 1;
443 #endif
445 /*black_qix.velocity=1;
446 black_qix.x=BOARD_X+(BOARD_W*CUBE_SIZE)/2-CUBE_SIZE/2;
447 black_qix.y=BOARD_Y+(BOARD_H*CUBE_SIZE)-CUBE_SIZE-CUBE_SIZE/2;
448 black_qix.angle=MOVE_UR; */
449 player.move = MOVE_NO;
450 player.drawing = false;
451 player.i = BOARD_W / 2;
452 player.j = 1;
454 percentage_cache = 0;
457 /* calculates the percentage of the screen filling */
458 static int percentage (void)
460 int i, j, filled = 0;
461 for (j = 2; j < BOARD_H - 2; j++)
462 for (i = 1; i < BOARD_W - 1; i++)
463 if (board[j][i] == FILLED)
464 filled++;
465 return (filled * 100) / ((BOARD_W - 2) * (BOARD_H - 4));
468 /* draw the board on with all the game figures */
469 static void refresh_board (void)
471 int i, j;
472 char str[25];
474 #if LCD_DEPTH>=2
475 rb->lcd_set_background (LCD_BLACK);
476 #else
477 rb->lcd_clear_display ();
478 #endif
479 for (j = 0; j < BOARD_H; j++)
481 unsigned last_color = board[j][0];
482 int last_i = 0;
483 for (i = 1; i < BOARD_W; i++) {
484 if( last_color != board[j][i] )
486 #if LCD_DEPTH>=2
487 rb->lcd_set_foreground (last_color);
488 #else
489 if (last_color != EMPTIED)
490 #endif
491 rb->lcd_fillrect (BOARD_X + CUBE_SIZE * (last_i),
492 BOARD_Y + CUBE_SIZE * j,
493 CUBE_SIZE * (i - last_i), CUBE_SIZE );
494 last_color = board[j][i];
495 last_i = i;
498 #if LCD_DEPTH>=2
499 rb->lcd_set_foreground (last_color);
500 #else
501 if (last_color != EMPTIED)
502 #endif
503 rb->lcd_fillrect (BOARD_X + CUBE_SIZE * (last_i),
504 BOARD_Y + CUBE_SIZE * j,
505 CUBE_SIZE * (i - last_i), CUBE_SIZE);
508 #if LCD_DEPTH>=2
509 rb->lcd_set_foreground (LCD_BLACK);
510 rb->lcd_set_background (CLR_LTBLUE);
511 #else
512 rb->lcd_set_drawmode (DRMODE_COMPLEMENT);
513 #endif
514 rb->snprintf (str, sizeof (str), "Level %d", player.level + 1);
515 rb->lcd_putsxy (BOARD_X, BOARD_Y, str);
516 rb->snprintf (str, sizeof (str), "%d%%", percentage_cache);
517 rb->lcd_putsxy (BOARD_X + CUBE_SIZE * BOARD_W - 24, BOARD_Y, str);
518 rb->snprintf (str, sizeof (str), "Score: %d", player.score);
519 rb->lcd_putsxy (BOARD_X, BOARD_Y + CUBE_SIZE * BOARD_H - 8, str);
520 rb->snprintf (str, sizeof (str),
521 (player.lives != 1) ? "%d Lives" : "%d Life", player.lives);
522 #if LCD_DEPTH>=2
523 rb->lcd_putsxy (BOARD_X + CUBE_SIZE * BOARD_W - 60,
524 BOARD_Y + CUBE_SIZE * BOARD_H - 8, str);
525 #else
526 rb->lcd_putsxy (BOARD_X + CUBE_SIZE * BOARD_W - 40,
527 BOARD_Y + CUBE_SIZE * BOARD_H - 8, str);
528 #endif
530 #if LCD_DEPTH>=2
531 rb->lcd_set_foreground (PLR_COL);
532 rb->lcd_set_background (board[player.j][player.i]);
533 #endif
534 rb->lcd_mono_bitmap (pics[PIC_PLAYER], player.i * CUBE_SIZE + BOARD_X,
535 player.j * CUBE_SIZE + BOARD_Y, CUBE_SIZE, CUBE_SIZE);
537 #if LCD_DEPTH>=2
538 rb->lcd_set_background (EMPTIED);
539 rb->lcd_set_foreground (LCD_WHITE);
540 rb->lcd_set_drawmode (DRMODE_FG);
541 #else
542 rb->lcd_set_drawmode (DRMODE_FG);
543 #endif
544 for (j = 0; j < player.level + STARTING_QIXES; j++)
545 rb->lcd_mono_bitmap (pics[PIC_QIX], qixes[j].x + BOARD_X,
546 qixes[j].y + BOARD_Y, CUBE_SIZE, CUBE_SIZE);
547 #if LCD_DEPTH>=2
548 rb->lcd_set_foreground (LCD_BLACK);
549 #endif
550 rb->lcd_set_drawmode (DRMODE_SOLID);
552 rb->lcd_update ();
555 static inline int infested_area (int i, int j, int v)
557 struct pos p;
558 p.x = i;
559 p.y = j;
560 emptyStack ();
561 if (!push (&p))
562 return -1;
563 while (pop (&p)) {
564 if (testboard[p.y][p.x] == v) continue;
565 if (testboard[p.y][p.x] > UNCHECKED)
566 return 1; /* This area was previously flagged as infested */
567 testboard[p.y][p.x] = v;
568 if (board[p.y][p.x] == QIX)
569 return 1; /* Infested area */
571 struct pos p1 = { p.x+1, p.y };
572 if ((p1.x < BOARD_W)
573 && (board[p1.y][p1.x] != FILLED)
574 && (!push (&p1)))
575 return -1;
578 struct pos p1 = { p.x-1, p.y };
579 if ((p1.x >= 0)
580 && (board[p1.y][p1.x] != FILLED)
581 && (!push (&p1)))
582 return -1;
585 struct pos p1 = { p.x, p.y+1 };
586 if ((p1.y < BOARD_H)
587 && (board[p1.y][p1.x] != FILLED)
588 && (!push (&p1)))
589 return -1;
592 struct pos p1 = { p.x, p.y-1 };
593 if ((p1.y >= 0)
594 && (board[p1.y][p1.x] != FILLED)
595 && (!push (&p1)))
596 return -1;
599 return 0;
602 static inline int fill_area (int i, int j)
604 struct pos p;
605 p.x = i;
606 p.y = j;
607 int v = testboard[p.y][p.x];
608 emptyStack ();
609 if (!push (&p))
610 return -1;
611 while (pop (&p)) {
612 board[p.y][p.x] = FILLED;
613 testboard[p.y][p.x] = PAINTED;
615 struct pos p1 = { p.x+1, p.y };
616 if ((p1.x < BOARD_W)
617 && (testboard[p1.y][p1.x] == v)
618 && (!push (&p1)))
619 return -1;
622 struct pos p1 = { p.x-1, p.y };
623 if ((p1.x >= 0)
624 && (testboard[p1.y][p1.x] == v)
625 && (!push (&p1)))
626 return -1;
629 struct pos p1 = { p.x, p.y+1 };
630 if ((p1.y < BOARD_H)
631 && (testboard[p1.y][p1.x] == v)
632 && (!push (&p1)))
633 return -1;
636 struct pos p1 = { p.x, p.y-1 };
637 if ((p1.y >= 0)
638 && (testboard[p1.y][p1.x] == v)
639 && (!push (&p1)))
640 return -1;
643 return 0;
647 /* take care of stuff after xonix has landed on a filled spot */
648 static void complete_trail (int fill)
650 int i, j, ret;
651 for (j = 0; j < BOARD_H; j++) {
652 for (i = 0; i < BOARD_W; i++) {
653 if (board[j][i] == TRAIL) {
654 if (fill)
655 board[j][i] = FILLED;
656 else
657 board[j][i] = EMPTIED;
662 if (fill) {
663 int v = CHECKED;
664 for (i = 0; i < player.level + STARTING_QIXES; i++) /* add qixes to board */
665 board[pos(qixes[i].y - BOARD_Y)]
666 [pos(qixes[i].x - BOARD_X)] = QIX;
668 init_testboard();
669 for (j = 1; j < BOARD_H - 1; j++) {
670 for (i = 0; i < BOARD_W - 0; i++) {
671 if (board[j][i] != FILLED) {
672 ret = infested_area (i, j, v);
673 if (ret < 0 || ( ret == 0 && fill_area (i, j) ) )
674 quit = true;
675 v++;
680 for (i = 0; i < player.level + STARTING_QIXES; i++) /* add qixes to board */
681 board[pos(qixes[i].y - BOARD_Y)]
682 [pos(qixes[i].x - BOARD_X)] = EMPTIED;
683 percentage_cache = percentage();
686 rb->button_clear_queue();
689 /* returns the color the real pixel(x,y) on the lcd is pointing at */
690 static inline unsigned int getpixel (int x, int y)
692 const int a = pos (x - BOARD_X), b = pos (y - BOARD_Y);
693 if ((a > 0) && (a < BOARD_W) && (b > 0) && (b < BOARD_H)) /* if inside board */
694 return board[b][a];
695 else
696 return FILLED;
699 /* returns the color the ball on (newx,newy) is heading at *----*
700 checks the four edge points of the square if 1st of all | |
701 are a trail (cause it's a lose life situation) and 2nd | |
702 if it's filled so it needs to bounce. *____*
704 static inline unsigned int next_hit (int newx, int newy)
706 if ((getpixel (newx, newy) == TRAIL)
707 || (getpixel (newx, newy + CUBE_SIZE - 1) == TRAIL)
708 || (getpixel (newx + CUBE_SIZE - 1, newy) == TRAIL)
709 || (getpixel (newx + CUBE_SIZE - 1, newy + CUBE_SIZE - 1) == TRAIL))
710 return TRAIL;
711 else if ((getpixel (newx, newy) == FILLED)
712 || (getpixel (newx, newy + CUBE_SIZE - 1) == FILLED)
713 || (getpixel (newx + CUBE_SIZE - 1, newy) == FILLED)
714 || (getpixel (newx + CUBE_SIZE - 1, newy + CUBE_SIZE - 1) ==
715 FILLED))
716 return FILLED;
717 else
718 return EMPTIED;
721 static void die (void)
723 player.lives--;
724 if (player.lives == 0)
725 player.gameover = true;
726 else {
727 refresh_board ();
728 rb->splash (HZ, "Crash!");
729 complete_trail (false);
730 player.move = MOVE_NO;
731 player.drawing = false;
732 player.i = BOARD_W / 2;
733 player.j = 1;
737 /* returns true if the (side) of the block -***-
738 starting from (newx,newy) has any filled pixels * *
739 -***-
741 static inline bool line_check_lt (int newx, int newy)
743 return getpixel (newx, newy + CUBE_SIZE/2-1) == FILLED
744 && getpixel (newx, newy + CUBE_SIZE/2 ) == FILLED;
746 static inline bool line_check_rt (int newx, int newy)
748 return getpixel (newx + CUBE_SIZE-1, newy + CUBE_SIZE/2-1) == FILLED
749 && getpixel (newx + CUBE_SIZE-1, newy + CUBE_SIZE/2 ) == FILLED;
751 static inline bool line_check_up (int newx, int newy)
753 return getpixel (newx + CUBE_SIZE/2-1, newy) == FILLED
754 && getpixel (newx + CUBE_SIZE/2 , newy) == FILLED;
756 static inline bool line_check_dn (int newx, int newy)
758 return getpixel (newx + CUBE_SIZE/2-1, newy + CUBE_SIZE-1) == FILLED
759 && getpixel (newx + CUBE_SIZE/2 , newy + CUBE_SIZE-1) == FILLED;
762 static inline void move_qix (struct qix *q)
764 int newx, newy;
765 newx = get_newx (q->x, q->velocity, q->angle);
766 newy = get_newy (q->y, q->velocity, q->angle);
767 switch (next_hit (newx, newy))
769 case EMPTIED:
770 q->x = newx;
771 q->y = newy;
772 break;
773 case FILLED:
775 const int a = q->angle;
776 q->angle =
777 ((a&(DIR_UU|DIR_U))
778 ? (line_check_up (newx, newy) ? ((a&(DIR_UU|DIR_U))>>4)
779 : (a&(DIR_UU|DIR_U)))
780 : 0)
782 ((a&(DIR_RR|DIR_R))
783 ? (line_check_rt (newx, newy) ? ((a&(DIR_RR|DIR_R))>>4)
784 : (a&(DIR_RR|DIR_R)))
785 : 0)
787 ((a&(DIR_DD|DIR_D))
788 ? (line_check_dn (newx, newy) ? ((a&(DIR_DD|DIR_D))<<4)
789 : (a&(DIR_DD|DIR_D)))
790 : 0)
792 ((a&(DIR_LL|DIR_L))
793 ? (line_check_lt (newx, newy) ? ((a&(DIR_LL|DIR_L))<<4)
794 : (a&(DIR_LL|DIR_L)))
795 : 0);
796 q->x = get_newx (q->x, q->velocity, q->angle);
797 q->y = get_newy (q->y, q->velocity, q->angle);
798 break;
800 case TRAIL:
801 die();
802 break;
806 /* move the board forward timewise */
807 static inline void move_board (void)
809 int j, newi, newj;
811 for (j = 0; j < player.level + STARTING_QIXES; j++)
812 move_qix (&qixes[j]);
813 /* move_qix(&black_qix,true); */
814 if (player.move) {
815 newi = player.i;
816 newj = player.j;
817 switch (player.move) {
818 case MOVE_UP:
819 if (player.j > 1)
820 newj--;
821 break;
822 case MOVE_DN:
823 if (player.j < BOARD_H - 2)
824 newj++;
825 break;
826 case MOVE_LT:
827 if (player.i > 0)
828 newi--;
829 break;
830 case MOVE_RT:
831 if (player.i < BOARD_W - 1)
832 newi++;
833 break;
834 default:
835 break;
838 if ((player.drawing) && (board[newj][newi] == EMPTIED)) /* continue drawing */
839 board[newj][newi] = TRAIL;
840 else if ((player.drawing) && (board[newj][newi] == FILLED)) { /* finish drawing */
841 player.move = MOVE_NO; /* stop moving */
842 player.drawing = false;
843 complete_trail (true);
844 } else if ((board[player.j][player.i] == FILLED)
845 && (board[newj][newi] == EMPTIED)) {
846 /* start drawing */
847 player.drawing = true;
848 board[newj][newi] = TRAIL;
849 /* if the block after next is empty and we're moving onto filled, stop */
850 } else if ((board[newj][newi] == FILLED)
851 && (board[newj + newj-player.j][newi + newi-player.i] == EMPTIED)) {
852 player.move = MOVE_NO;
854 player.i = newi;
855 player.j = newj;
857 if (percentage_cache >= difficulty) { /* finished level */
858 rb->splashf (HZ * 2, "Level %d finished", player.level+1);
859 player.score += percentage_cache;
860 if (player.level < MAX_LEVEL)
861 player.level++;
862 init_board ();
863 refresh_board ();
864 rb->splash (HZ * 2, "Ready?");
868 /* the main menu */
869 static int game_menu (void)
871 MENUITEM_STRINGLIST(menu, "XOBOX Menu", NULL, "Start New Game",
872 "Speed","Difficulty","Quit");
873 int selection = 0;
874 #ifdef HAVE_LCD_COLOR
875 rb->lcd_set_foreground (rb->global_settings->fg_color);
876 rb->lcd_set_background (rb->global_settings->bg_color);
877 #elif LCD_DEPTH>=2
878 rb->lcd_set_foreground(LCD_BLACK);
879 rb->lcd_set_background(LCD_WHITE);
880 #endif
881 for (;;) {
882 rb->do_menu(&menu,&selection, NULL, false);
883 if (selection==1)
884 rb->set_int ("Speed", "", UNIT_INT, &speed, NULL, 1, 1, 10, NULL);
885 else if (selection==2)
886 rb->set_int ("Difficulty", "", UNIT_INT, &difficulty, NULL,
887 5, 50, 95, NULL);
888 else
889 break;
891 if (selection != MENU_START) {
892 selection = MENU_QUIT;
894 return selection;
897 /* init game's variables */
898 static void init_game (void)
900 player.level = 0;
901 player.score = 0;
902 player.lives = 3;
903 player.gameover = false;
904 player.drawing = false;
905 rb->lcd_setfont(FONT_SYSFIXED);
906 init_board ();
907 refresh_board ();
908 rb->splash (HZ * 2, "Ready?");
911 /* general keypad handler loop */
912 static int xobox_loop (void)
914 int button = 0, ret;
915 bool pause = false;
916 int end;
918 while (!quit) {
919 end = *rb->current_tick + ((11-speed)*HZ)/100;
921 #ifdef HAS_BUTTON_HOLD
922 if (rb->button_hold()) {
923 pause = true;
924 rb->splash (HZ, "PAUSED");
926 #endif
928 button = rb->button_get_w_tmo (1);
929 switch (button) {
930 case UP:
931 case UP|BUTTON_REPEAT:
932 player.move = MOVE_UP;
933 break;
934 case DOWN:
935 case DOWN|BUTTON_REPEAT:
936 player.move = MOVE_DN;
937 break;
938 case LEFT:
939 case LEFT|BUTTON_REPEAT:
940 player.move = MOVE_LT;
941 break;
942 case RIGHT:
943 case RIGHT|BUTTON_REPEAT:
944 player.move = MOVE_RT;
945 break;
946 case PAUSE:
947 pause = !pause;
948 if (pause)
949 rb->splash (HZ, "Paused");
950 break;
951 case QUIT:
952 ret = game_menu ();
953 if (ret == MENU_START)
954 init_game ();
955 else
957 quit = true;
958 continue;
960 break;
961 default:
962 if (rb->default_event_handler (button) == SYS_USB_CONNECTED)
963 return PLUGIN_USB_CONNECTED;
964 break;
966 if (!pause) {
967 move_board ();
968 refresh_board ();
970 if (player.gameover) {
971 rb->splash (HZ, "Game Over!");
972 ret = game_menu ();
973 if (ret == MENU_START)
974 init_game ();
975 else
976 quit = true;
979 if (end > *rb->current_tick)
980 rb->sleep (end - *rb->current_tick);
981 else
982 rb->yield ();
984 } /* end while */
985 return PLUGIN_OK; /* for no warnings on compiling */
988 /* plugin main procedure */
989 enum plugin_status plugin_start (const struct plugin_api *api, const void *parameter)
991 int ret = PLUGIN_OK;
993 (void) parameter;
994 rb = api;
996 rb->lcd_setfont (FONT_SYSFIXED);
997 #if LCD_DEPTH>=2
998 rb->lcd_set_backdrop(NULL);
999 #endif
1001 /* Turn off backlight timeout */
1002 backlight_force_on(rb); /* backlight control in lib/helper.c */
1004 quit = false;
1006 randomize ();
1007 if (game_menu () == MENU_START) {
1008 init_game ();
1009 ret = xobox_loop ();
1012 /* Turn on backlight timeout (revert to settings) */
1013 backlight_use_settings(rb); /* backlight control in lib/helper.c */
1014 rb->lcd_setfont (FONT_UI);
1016 return ret;