FS#9281 Rename of splash functions.
[kugel-rb.git] / apps / plugins / xobox.c
blob0faf8a27be9e7a622406fef62bbeaf5e967d1af8
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 #else
149 #error No keymap defined!
150 #endif
152 #ifdef HAVE_TOUCHPAD
153 #ifndef QUIT
154 #define QUIT BUTTON_TOPLEFT
155 #endif
156 #ifndef LEFT
157 #define LEFT BUTTON_MIDLEFT
158 #endif
159 #ifndef RIGHT
160 #define RIGHT BUTTON_MIDRIGHT
161 #endif
162 #ifndef UP
163 #define UP BUTTON_TOPMIDDLE
164 #endif
165 #ifndef DOWN
166 #define DOWN BUTTON_BOTTOMMIDDLE
167 #endif
168 #ifndef PAUSE
169 #define PAUSE BUTTON_CENTER
170 #endif
171 #endif
173 #define MOVE_NO 0 /* player movement */
174 #define MOVE_UP 1 /* 1 */
175 #define MOVE_DN 2 /* 3 0 4 */
176 #define MOVE_LT 3 /* 2 */
177 #define MOVE_RT 4
179 /* ball movement (12 ways) */
180 /* UUL UR */
181 /* UL UR */
182 /* ULL . URR */
183 /* DLL DRR */
184 /* DL DR */
185 /* DDL DDR */
187 #define DIR_UU (1<<7)
188 #define DIR_U (1<<6)
189 #define DIR_RR (1<<5)
190 #define DIR_R (1<<4)
191 #define DIR_DD (1<<3)
192 #define DIR_D (1<<2)
193 #define DIR_LL (1<<1)
194 #define DIR_L (1<<0)
196 #define MOVE_UUR ( DIR_UU | DIR_R )
197 #define MOVE_UR ( DIR_U | DIR_R )
198 #define MOVE_URR ( DIR_U | DIR_RR )
199 #define MOVE_DRR ( DIR_D | DIR_RR )
200 #define MOVE_DR ( DIR_D | DIR_R )
201 #define MOVE_DDR ( DIR_DD | DIR_R )
202 #define MOVE_DDL ( DIR_DD | DIR_L )
203 #define MOVE_DL ( DIR_D | DIR_L )
204 #define MOVE_DLL ( DIR_D | DIR_LL )
205 #define MOVE_ULL ( DIR_U | DIR_LL )
206 #define MOVE_UL ( DIR_U | DIR_L )
207 #define MOVE_UUL ( DIR_UU | DIR_L )
209 #if (LCD_WIDTH>112) && (LCD_HEIGHT>64)
210 # define CUBE_SIZE 8 /* 8x22=176 */
211 # define pos(a) ((a)>>3)
212 #else
213 # define CUBE_SIZE 4
214 # define pos(a) ((a)>>2)
215 #endif
217 #define STARTING_QIXES 2
218 #define MAX_LEVEL 10
219 #define MAX_QIXES MAX_LEVEL+STARTING_QIXES
220 #define BOARD_W ((int)(LCD_WIDTH/CUBE_SIZE))
221 #define BOARD_H ((int)(LCD_HEIGHT/CUBE_SIZE))
222 #define BOARD_X (LCD_WIDTH-BOARD_W*CUBE_SIZE)/2
223 #define BOARD_Y (LCD_HEIGHT-BOARD_H*CUBE_SIZE)/2
225 #ifdef HAVE_LCD_COLOR
226 #define CLR_RED LCD_RGBPACK(255,0,0) /* used to imply danger */
227 #define CLR_LTBLUE LCD_RGBPACK(125, 145, 180) /* used for frame and filling */
228 #define PLR_COL LCD_WHITE /* color used for the player */
229 #elif LCD_DEPTH>=2
230 #define CLR_RED LCD_DARKGRAY /* used to imply danger */
231 #define CLR_LTBLUE LCD_LIGHTGRAY /* used for frame and filling */
232 #define PLR_COL LCD_BLACK /* color used for the player */
233 #endif
235 #if LCD_DEPTH>=2
236 #define EMPTIED LCD_BLACK /* empty spot */
237 #define FILLED CLR_LTBLUE /* filled spot */
238 #define TRAIL CLR_RED /* the red trail of the player */
239 #define QIX LCD_WHITE
240 #else
241 #define EMPTIED 0
242 #define FILLED 1
243 #define TRAIL 2
244 #define QIX 3
245 #endif
246 #define UNCHECKED 0
247 #define CHECKED 1
248 #define PAINTED -1
249 #define PIC_QIX 0
250 #define PIC_PLAYER 1
252 #define MENU_START 0
253 #define MENU_QUIT 1
255 /* The time (in ms) for one iteration through the game loop - decrease this
256 to speed up the game - note that current_tick is (currently) only accurate
257 to 10ms.
259 static int speed = 6; /* CYCLETIME = (11-speed)*10 ms */
260 static int difficulty = 75; /* Percentage of screen that needs to be filled
261 * in order to win the game */
263 static const struct plugin_api *rb;
265 MEM_FUNCTION_WRAPPERS(rb);
267 static bool quit = false;
269 static unsigned int board[BOARD_H][BOARD_W];
270 static int testboard[BOARD_H][BOARD_W];
272 #if CUBE_SIZE == 8
274 00011000 0x18 - 11100111 0xe7
275 00111100 0x3c - 11100111 0xe7
276 01111110 0x7e - 11000011 0xc3
277 11111111 0xff - 00000000 0x00
278 11111111 0xff - 00000000 0x00
279 01111110 0x7e - 11000011 0xc3
280 00111100 0x3c - 11100111 0xe7
281 00011000 0x18 - 11100111 0xe7
283 const unsigned char pics[2][8] = {
284 {0x18, 0x3c, 0x7e, 0xff, 0xff, 0x7e, 0x3c, 0x18}, /* Alien (QIX) */
285 {0xe7, 0xe7, 0xc3, 0x00, 0x00, 0xc3, 0xe7, 0xe7} /* Player (XONIX) */
287 #elif CUBE_SIZE == 4
289 0110 0x6 - 1001 0x9
290 1111 0xf - 0110 0x6
291 1111 0xf - 0110 0x6
292 0110 0x6 - 1001 0x9
294 const unsigned char pics[2][4] = {
295 {0x6, 0xf, 0xf, 0x6}, /* Alien (QIX) */
296 {0x9, 0x6, 0x6, 0x9} /* Player (XONIX) */
298 #else
299 #error Incorrect CUBE_SIZE value.
300 #endif
302 static struct qix
304 int velocity; /* velocity */
305 int x, y; /* position on screen */
306 int angle; /* angle */
307 } qixes[MAX_QIXES]; /* black_qix */
309 static struct splayer
311 int i, j; /* position on board */
312 int move, score, level, lives;
313 bool drawing;
314 bool gameover;
315 } player;
317 static int percentage_cache;
319 /*************************** STACK STUFF **********************/
321 /* the stack */
322 #define STACK_SIZE (2*BOARD_W*BOARD_H)
323 static struct pos
325 int x, y; /* position on board */
326 } stack[STACK_SIZE];
327 static int stackPointer;
329 static inline bool pop (struct pos *p)
331 if (stackPointer > 0) {
332 p->x = stack[stackPointer].x;
333 p->y = stack[stackPointer].y;
334 stackPointer--;
335 return true;
336 } else
337 return false; /* SE */
340 static inline bool push (struct pos *p)
342 if (stackPointer < STACK_SIZE - 1) {
343 stackPointer++;
344 stack[stackPointer].x = p->x;
345 stack[stackPointer].y = p->y;
346 return true;
347 } else
348 return false; /* SOF */
351 static inline void emptyStack (void)
353 stackPointer = 0;
356 /*********************** END OF STACK STUFF *********************/
358 /* calculate the new x coordinate of the ball according to angle and speed */
359 static inline int get_newx (int x, int len, int deg)
361 if (deg & DIR_R)
362 return x + len;
363 else if (deg & DIR_L)
364 return x - len;
365 else if (deg & DIR_RR)
366 return x + len * 2;
367 else /* (def & DIR_LL) */
368 return x - len * 2;
371 /* calculate the new y coordinate of the ball according to angle and speed */
372 static inline int get_newy (int y, int len, int deg)
374 if (deg & DIR_D)
375 return y + len;
376 else if (deg & DIR_U)
377 return y - len;
378 else if (deg & DIR_DD)
379 return y + len * 2;
380 else /* (deg & DIR_UU) */
381 return y - len * 2;
384 /* make random function get it's value from the device ticker */
385 static inline void randomize (void)
387 rb->srand (*rb->current_tick);
390 /* get a random number between 0 and range-1 */
391 static int t_rand (int range)
393 return rb->rand () % range;
396 /* initializes the test help board */
397 static void init_testboard (void)
399 int j; /* testboard */
400 for (j = 0; j < BOARD_H; j++)
401 /* UNCHEKED == (int)0 */
402 rb->memset( testboard[j], 0, BOARD_W * sizeof( int ) );
405 /* initializes the game board on with the player,qix's and black qix */
406 static void init_board (void)
408 int i, j;
409 for (j = 0; j < BOARD_H; j++)
410 for (i = 0; i < BOARD_W; i++) { /* make a nice cyan frame */
411 if ((i == 0) || (j <= 1) || (i == BOARD_W - 1)
412 || (j >= BOARD_H - 2))
413 board[j][i] = FILLED;
414 else
415 board[j][i] = EMPTIED;
418 /* (level+2) is the number of qixes */
419 for (j = 0; j < player.level + STARTING_QIXES; j++) {
420 qixes[j].velocity = t_rand (2) + 1; /* 1 or 2 pix-per-sec */
422 /* not on frame */
423 qixes[j].x = CUBE_SIZE*2 + 2*t_rand (((BOARD_W-4)*CUBE_SIZE)/2);
424 qixes[j].y = CUBE_SIZE*2 + 2*t_rand (((BOARD_H-4)*CUBE_SIZE)/2);
426 const int angle_table[] = {
427 MOVE_UUR, MOVE_UR, MOVE_URR, MOVE_DRR, MOVE_DR, MOVE_DDR,
428 MOVE_UUL, MOVE_UL, MOVE_ULL, MOVE_DLL, MOVE_DL, MOVE_DDL };
429 qixes[j].angle = angle_table[t_rand (12)];
430 #if CUBE_SIZE == 4
431 /* Work arround a nasty bug. FIXME */
432 if( qixes[j].angle & (DIR_LL|DIR_RR|DIR_UU|DIR_DD) )
433 qixes[j].velocity = 1;
434 #endif
436 /*black_qix.velocity=1;
437 black_qix.x=BOARD_X+(BOARD_W*CUBE_SIZE)/2-CUBE_SIZE/2;
438 black_qix.y=BOARD_Y+(BOARD_H*CUBE_SIZE)-CUBE_SIZE-CUBE_SIZE/2;
439 black_qix.angle=MOVE_UR; */
440 player.move = MOVE_NO;
441 player.drawing = false;
442 player.i = BOARD_W / 2;
443 player.j = 1;
445 percentage_cache = 0;
448 /* calculates the percentage of the screen filling */
449 static int percentage (void)
451 int i, j, filled = 0;
452 for (j = 2; j < BOARD_H - 2; j++)
453 for (i = 1; i < BOARD_W - 1; i++)
454 if (board[j][i] == FILLED)
455 filled++;
456 return (filled * 100) / ((BOARD_W - 2) * (BOARD_H - 4));
459 /* draw the board on with all the game figures */
460 static void refresh_board (void)
462 int i, j;
463 char str[25];
465 #if LCD_DEPTH>=2
466 rb->lcd_set_background (LCD_BLACK);
467 #else
468 rb->lcd_clear_display ();
469 #endif
470 for (j = 0; j < BOARD_H; j++)
472 unsigned last_color = board[j][0];
473 int last_i = 0;
474 for (i = 1; i < BOARD_W; i++) {
475 if( last_color != board[j][i] )
477 #if LCD_DEPTH>=2
478 rb->lcd_set_foreground (last_color);
479 #else
480 if (last_color != EMPTIED)
481 #endif
482 rb->lcd_fillrect (BOARD_X + CUBE_SIZE * (last_i),
483 BOARD_Y + CUBE_SIZE * j,
484 CUBE_SIZE * (i - last_i), CUBE_SIZE );
485 last_color = board[j][i];
486 last_i = i;
489 #if LCD_DEPTH>=2
490 rb->lcd_set_foreground (last_color);
491 #else
492 if (last_color != EMPTIED)
493 #endif
494 rb->lcd_fillrect (BOARD_X + CUBE_SIZE * (last_i),
495 BOARD_Y + CUBE_SIZE * j,
496 CUBE_SIZE * (i - last_i), CUBE_SIZE);
499 #if LCD_DEPTH>=2
500 rb->lcd_set_foreground (LCD_BLACK);
501 rb->lcd_set_background (CLR_LTBLUE);
502 #else
503 rb->lcd_set_drawmode (DRMODE_COMPLEMENT);
504 #endif
505 rb->snprintf (str, sizeof (str), "Level %d", player.level + 1);
506 rb->lcd_putsxy (BOARD_X, BOARD_Y, str);
507 rb->snprintf (str, sizeof (str), "%d%%", percentage_cache);
508 rb->lcd_putsxy (BOARD_X + CUBE_SIZE * BOARD_W - 24, BOARD_Y, str);
509 rb->snprintf (str, sizeof (str), "Score: %d", player.score);
510 rb->lcd_putsxy (BOARD_X, BOARD_Y + CUBE_SIZE * BOARD_H - 8, str);
511 rb->snprintf (str, sizeof (str), "%d Lives", player.lives);
512 #if LCD_DEPTH>=2
513 rb->lcd_putsxy (BOARD_X + CUBE_SIZE * BOARD_W - 60,
514 BOARD_Y + CUBE_SIZE * BOARD_H - 8, str);
515 #else
516 rb->lcd_putsxy (BOARD_X + CUBE_SIZE * BOARD_W - 40,
517 BOARD_Y + CUBE_SIZE * BOARD_H - 8, str);
518 #endif
520 #if LCD_DEPTH>=2
521 rb->lcd_set_foreground (PLR_COL);
522 rb->lcd_set_background (board[player.j][player.i]);
523 #endif
524 rb->lcd_mono_bitmap (pics[PIC_PLAYER], player.i * CUBE_SIZE + BOARD_X,
525 player.j * CUBE_SIZE + BOARD_Y, CUBE_SIZE, CUBE_SIZE);
527 #if LCD_DEPTH>=2
528 rb->lcd_set_background (EMPTIED);
529 rb->lcd_set_foreground (LCD_WHITE);
530 rb->lcd_set_drawmode (DRMODE_FG);
531 #else
532 rb->lcd_set_drawmode (DRMODE_FG);
533 #endif
534 for (j = 0; j < player.level + STARTING_QIXES; j++)
535 rb->lcd_mono_bitmap (pics[PIC_QIX], qixes[j].x + BOARD_X,
536 qixes[j].y + BOARD_Y, CUBE_SIZE, CUBE_SIZE);
537 #if LCD_DEPTH>=2
538 rb->lcd_set_foreground (LCD_BLACK);
539 #endif
540 rb->lcd_set_drawmode (DRMODE_SOLID);
542 rb->lcd_update ();
545 static inline int infested_area (int i, int j, int v)
547 struct pos p;
548 p.x = i;
549 p.y = j;
550 emptyStack ();
551 if (!push (&p))
552 return -1;
553 while (pop (&p)) {
554 if (testboard[p.y][p.x] == v) continue;
555 if (testboard[p.y][p.x] > UNCHECKED)
556 return 1; /* This area was previously flagged as infested */
557 testboard[p.y][p.x] = v;
558 if (board[p.y][p.x] == QIX)
559 return 1; /* Infested area */
561 struct pos p1 = { p.x+1, p.y };
562 if ((p1.x < BOARD_W)
563 && (board[p1.y][p1.x] != FILLED)
564 && (!push (&p1)))
565 return -1;
568 struct pos p1 = { p.x-1, p.y };
569 if ((p1.x >= 0)
570 && (board[p1.y][p1.x] != FILLED)
571 && (!push (&p1)))
572 return -1;
575 struct pos p1 = { p.x, p.y+1 };
576 if ((p1.y < BOARD_H)
577 && (board[p1.y][p1.x] != FILLED)
578 && (!push (&p1)))
579 return -1;
582 struct pos p1 = { p.x, p.y-1 };
583 if ((p1.y >= 0)
584 && (board[p1.y][p1.x] != FILLED)
585 && (!push (&p1)))
586 return -1;
589 return 0;
592 static inline int fill_area (int i, int j)
594 struct pos p;
595 p.x = i;
596 p.y = j;
597 int v = testboard[p.y][p.x];
598 emptyStack ();
599 if (!push (&p))
600 return -1;
601 while (pop (&p)) {
602 board[p.y][p.x] = FILLED;
603 testboard[p.y][p.x] = PAINTED;
605 struct pos p1 = { p.x+1, p.y };
606 if ((p1.x < BOARD_W)
607 && (testboard[p1.y][p1.x] == v)
608 && (!push (&p1)))
609 return -1;
612 struct pos p1 = { p.x-1, p.y };
613 if ((p1.x >= 0)
614 && (testboard[p1.y][p1.x] == v)
615 && (!push (&p1)))
616 return -1;
619 struct pos p1 = { p.x, p.y+1 };
620 if ((p1.y < BOARD_H)
621 && (testboard[p1.y][p1.x] == v)
622 && (!push (&p1)))
623 return -1;
626 struct pos p1 = { p.x, p.y-1 };
627 if ((p1.y >= 0)
628 && (testboard[p1.y][p1.x] == v)
629 && (!push (&p1)))
630 return -1;
633 return 0;
637 /* take care of stuff after xonix has landed on a filled spot */
638 static void complete_trail (int fill)
640 int i, j, ret;
641 for (j = 0; j < BOARD_H; j++) {
642 for (i = 0; i < BOARD_W; i++) {
643 if (board[j][i] == TRAIL) {
644 if (fill)
645 board[j][i] = FILLED;
646 else
647 board[j][i] = EMPTIED;
652 if (fill) {
653 int v = CHECKED;
654 for (i = 0; i < player.level + STARTING_QIXES; i++) /* add qixes to board */
655 board[pos(qixes[i].y - BOARD_Y)]
656 [pos(qixes[i].x - BOARD_X)] = QIX;
658 init_testboard();
659 for (j = 1; j < BOARD_H - 1; j++) {
660 for (i = 0; i < BOARD_W - 0; i++) {
661 if (board[j][i] != FILLED) {
662 ret = infested_area (i, j, v);
663 if (ret < 0 || ( ret == 0 && fill_area (i, j) ) )
664 quit = true;
665 v++;
670 for (i = 0; i < player.level + STARTING_QIXES; i++) /* add qixes to board */
671 board[pos(qixes[i].y - BOARD_Y)]
672 [pos(qixes[i].x - BOARD_X)] = EMPTIED;
673 percentage_cache = percentage();
676 rb->button_clear_queue();
679 /* returns the color the real pixel(x,y) on the lcd is pointing at */
680 static inline unsigned int getpixel (int x, int y)
682 const int a = pos (x - BOARD_X), b = pos (y - BOARD_Y);
683 if ((a > 0) && (a < BOARD_W) && (b > 0) && (b < BOARD_H)) /* if inside board */
684 return board[b][a];
685 else
686 return FILLED;
689 /* returns the color the ball on (newx,newy) is heading at *----*
690 checks the four edge points of the square if 1st of all | |
691 are a trail (cause it's a lose life situation) and 2nd | |
692 if it's filled so it needs to bounce. *____*
694 static inline unsigned int next_hit (int newx, int newy)
696 if ((getpixel (newx, newy) == TRAIL)
697 || (getpixel (newx, newy + CUBE_SIZE - 1) == TRAIL)
698 || (getpixel (newx + CUBE_SIZE - 1, newy) == TRAIL)
699 || (getpixel (newx + CUBE_SIZE - 1, newy + CUBE_SIZE - 1) == TRAIL))
700 return TRAIL;
701 else if ((getpixel (newx, newy) == FILLED)
702 || (getpixel (newx, newy + CUBE_SIZE - 1) == FILLED)
703 || (getpixel (newx + CUBE_SIZE - 1, newy) == FILLED)
704 || (getpixel (newx + CUBE_SIZE - 1, newy + CUBE_SIZE - 1) ==
705 FILLED))
706 return FILLED;
707 else
708 return EMPTIED;
711 static void die (void)
713 player.lives--;
714 if (player.lives == 0)
715 player.gameover = true;
716 else {
717 refresh_board ();
718 rb->splash (HZ, "Crash!");
719 complete_trail (false);
720 player.move = MOVE_NO;
721 player.drawing = false;
722 player.i = BOARD_W / 2;
723 player.j = 1;
727 /* returns true if the (side) of the block -***-
728 starting from (newx,newy) has any filled pixels * *
729 -***-
731 static inline bool line_check_lt (int newx, int newy)
733 return getpixel (newx, newy + CUBE_SIZE/2-1) == FILLED
734 && getpixel (newx, newy + CUBE_SIZE/2 ) == FILLED;
736 static inline bool line_check_rt (int newx, int newy)
738 return getpixel (newx + CUBE_SIZE-1, newy + CUBE_SIZE/2-1) == FILLED
739 && getpixel (newx + CUBE_SIZE-1, newy + CUBE_SIZE/2 ) == FILLED;
741 static inline bool line_check_up (int newx, int newy)
743 return getpixel (newx + CUBE_SIZE/2-1, newy) == FILLED
744 && getpixel (newx + CUBE_SIZE/2 , newy) == FILLED;
746 static inline bool line_check_dn (int newx, int newy)
748 return getpixel (newx + CUBE_SIZE/2-1, newy + CUBE_SIZE-1) == FILLED
749 && getpixel (newx + CUBE_SIZE/2 , newy + CUBE_SIZE-1) == FILLED;
752 static inline void move_qix (struct qix *q)
754 int newx, newy;
755 newx = get_newx (q->x, q->velocity, q->angle);
756 newy = get_newy (q->y, q->velocity, q->angle);
757 switch (next_hit (newx, newy))
759 case EMPTIED:
760 q->x = newx;
761 q->y = newy;
762 break;
763 case FILLED:
765 const int a = q->angle;
766 q->angle =
767 ((a&(DIR_UU|DIR_U))
768 ? (line_check_up (newx, newy) ? ((a&(DIR_UU|DIR_U))>>4)
769 : (a&(DIR_UU|DIR_U)))
770 : 0)
772 ((a&(DIR_RR|DIR_R))
773 ? (line_check_rt (newx, newy) ? ((a&(DIR_RR|DIR_R))>>4)
774 : (a&(DIR_RR|DIR_R)))
775 : 0)
777 ((a&(DIR_DD|DIR_D))
778 ? (line_check_dn (newx, newy) ? ((a&(DIR_DD|DIR_D))<<4)
779 : (a&(DIR_DD|DIR_D)))
780 : 0)
782 ((a&(DIR_LL|DIR_L))
783 ? (line_check_lt (newx, newy) ? ((a&(DIR_LL|DIR_L))<<4)
784 : (a&(DIR_LL|DIR_L)))
785 : 0);
786 q->x = get_newx (q->x, q->velocity, q->angle);
787 q->y = get_newy (q->y, q->velocity, q->angle);
788 break;
790 case TRAIL:
791 die();
792 break;
796 /* move the board forward timewise */
797 static inline void move_board (void)
799 int j, newi, newj;
801 for (j = 0; j < player.level + STARTING_QIXES; j++)
802 move_qix (&qixes[j]);
803 /* move_qix(&black_qix,true); */
804 if (player.move) {
805 newi = player.i;
806 newj = player.j;
807 switch (player.move) {
808 case MOVE_UP:
809 if (player.j > 1)
810 newj--;
811 break;
812 case MOVE_DN:
813 if (player.j < BOARD_H - 2)
814 newj++;
815 break;
816 case MOVE_LT:
817 if (player.i > 0)
818 newi--;
819 break;
820 case MOVE_RT:
821 if (player.i < BOARD_W - 1)
822 newi++;
823 break;
824 default:
825 break;
828 if ((player.drawing) && (board[newj][newi] == EMPTIED)) /* continue drawing */
829 board[newj][newi] = TRAIL;
830 else if ((player.drawing) && (board[newj][newi] == FILLED)) { /* finish drawing */
831 player.move = MOVE_NO; /* stop moving */
832 player.drawing = false;
833 complete_trail (true);
834 } else if ((board[player.j][player.i] == FILLED)
835 && (board[newj][newi] == EMPTIED)) {
836 /* start drawing */
837 player.drawing = true;
838 board[newj][newi] = TRAIL;
839 /* if the block after next is empty and we're moving onto filled, stop */
840 } else if ((board[newj][newi] == FILLED)
841 && (board[newj + newj-player.j][newi + newi-player.i] == EMPTIED)) {
842 player.move = MOVE_NO;
844 player.i = newi;
845 player.j = newj;
847 if (percentage_cache >= difficulty) { /* finished level */
848 rb->splashf (HZ * 2, "Level %d finished", player.level+1);
849 player.score += percentage_cache;
850 if (player.level < MAX_LEVEL)
851 player.level++;
852 init_board ();
853 refresh_board ();
854 rb->splash (HZ * 2, "Ready?");
858 /* the main menu */
859 static int game_menu (void)
861 MENUITEM_STRINGLIST(menu, "XOBOX Menu", NULL, "Start New Game",
862 "Speed","Difficulty","Quit");
863 int selection = 0;
864 #ifdef HAVE_LCD_COLOR
865 rb->lcd_set_foreground (rb->global_settings->fg_color);
866 rb->lcd_set_background (rb->global_settings->bg_color);
867 #elif LCD_DEPTH>=2
868 rb->lcd_set_foreground(LCD_BLACK);
869 rb->lcd_set_background(LCD_WHITE);
870 #endif
871 for (;;) {
872 rb->do_menu(&menu,&selection, NULL, false);
873 if (selection==1)
874 rb->set_int ("Speed", "", UNIT_INT, &speed, NULL, 1, 1, 10, NULL);
875 else if (selection==2)
876 rb->set_int ("Difficulty", "", UNIT_INT, &difficulty, NULL,
877 5, 50, 95, NULL);
878 else
879 break;
881 if (selection != MENU_START) {
882 selection = MENU_QUIT;
884 return selection;
887 /* init game's variables */
888 static void init_game (void)
890 player.level = 0;
891 player.score = 0;
892 player.lives = 3;
893 player.gameover = false;
894 player.drawing = false;
895 rb->lcd_setfont(FONT_SYSFIXED);
896 init_board ();
897 refresh_board ();
898 rb->splash (HZ * 2, "Ready?");
901 /* general keypad handler loop */
902 static int xobox_loop (void)
904 int button = 0, ret;
905 bool pause = false;
906 int end;
908 while (!quit) {
909 end = *rb->current_tick + ((11-speed)*HZ)/100;
911 #ifdef HAS_BUTTON_HOLD
912 if (rb->button_hold()) {
913 pause = true;
914 rb->splash (HZ, "PAUSED");
916 #endif
918 button = rb->button_get_w_tmo (1);
919 switch (button) {
920 case UP:
921 case UP|BUTTON_REPEAT:
922 player.move = MOVE_UP;
923 break;
924 case DOWN:
925 case DOWN|BUTTON_REPEAT:
926 player.move = MOVE_DN;
927 break;
928 case LEFT:
929 case LEFT|BUTTON_REPEAT:
930 player.move = MOVE_LT;
931 break;
932 case RIGHT:
933 case RIGHT|BUTTON_REPEAT:
934 player.move = MOVE_RT;
935 break;
936 case PAUSE:
937 pause = !pause;
938 if (pause)
939 rb->splash (HZ, "Paused");
940 break;
941 case QUIT:
942 ret = game_menu ();
943 if (ret == MENU_START)
944 init_game ();
945 else
947 quit = true;
948 continue;
950 break;
951 default:
952 if (rb->default_event_handler (button) == SYS_USB_CONNECTED)
953 return PLUGIN_USB_CONNECTED;
954 break;
956 if (!pause) {
957 move_board ();
958 refresh_board ();
960 if (player.gameover) {
961 rb->splash (HZ, "Game Over!");
962 ret = game_menu ();
963 if (ret == MENU_START)
964 init_game ();
965 else
966 quit = true;
969 if (end > *rb->current_tick)
970 rb->sleep (end - *rb->current_tick);
971 else
972 rb->yield ();
974 } /* end while */
975 return PLUGIN_OK; /* for no warnings on compiling */
978 /* plugin main procedure */
979 enum plugin_status plugin_start (const struct plugin_api *api, const void *parameter)
981 int ret = PLUGIN_OK;
983 (void) parameter;
984 rb = api;
986 rb->lcd_setfont (FONT_SYSFIXED);
987 #if LCD_DEPTH>=2
988 rb->lcd_set_backdrop(NULL);
989 #endif
991 /* Turn off backlight timeout */
992 backlight_force_on(rb); /* backlight control in lib/helper.c */
994 quit = false;
996 randomize ();
997 if (game_menu () == MENU_START) {
998 init_game ();
999 ret = xobox_loop ();
1002 /* Turn on backlight timeout (revert to settings) */
1003 backlight_use_settings(rb); /* backlight control in lib/helper.c */
1004 rb->lcd_setfont (FONT_UI);
1006 return ret;