Add platform file for Ipod 1G / 2G. Now only the front image is missing for building...
[Rockbox.git] / apps / plugins / xobox.c
blob616a45605fbdf8dc3465f4d6f71c58aea04e971b
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"
23 PLUGIN_HEADER
25 #if (CONFIG_KEYPAD == IRIVER_H100_PAD) || (CONFIG_KEYPAD == IRIVER_H300_PAD)
27 #define QUIT BUTTON_OFF
28 #define LEFT BUTTON_LEFT
29 #define RIGHT BUTTON_RIGHT
30 #define PAUSE BUTTON_MODE
31 #define UP BUTTON_UP
32 #define DOWN BUTTON_DOWN
34 #define RC_QUIT BUTTON_RC_STOP
36 #elif (CONFIG_KEYPAD == ARCHOS_AV300_PAD)
38 #define QUIT BUTTON_OFF
39 #define LEFT BUTTON_LEFT
40 #define RIGHT BUTTON_RIGHT
41 #define PAUSE BUTTON_ON
42 #define UP BUTTON_UP
43 #define DOWN BUTTON_DOWN
45 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
46 (CONFIG_KEYPAD == IPOD_3G_PAD) || \
47 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
49 #define QUIT (BUTTON_SELECT | BUTTON_MENU)
50 #define LEFT BUTTON_LEFT
51 #define RIGHT BUTTON_RIGHT
52 #define PAUSE BUTTON_SELECT
53 #define MENU_UP BUTTON_SCROLL_FWD
54 #define MENU_DOWN BUTTON_SCROLL_BACK
55 #define UP BUTTON_MENU
56 #define DOWN BUTTON_PLAY
58 #elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD
60 #define QUIT BUTTON_POWER
61 #define LEFT BUTTON_LEFT
62 #define RIGHT BUTTON_RIGHT
63 #define UP BUTTON_UP
64 #define DOWN BUTTON_DOWN
65 #define PAUSE BUTTON_PLAY
67 #elif (CONFIG_KEYPAD == GIGABEAT_PAD)
69 #define QUIT BUTTON_POWER
70 #define LEFT BUTTON_LEFT
71 #define RIGHT BUTTON_RIGHT
72 #define UP BUTTON_UP
73 #define DOWN BUTTON_DOWN
74 #define PAUSE BUTTON_A
76 #elif (CONFIG_KEYPAD == SANSA_E200_PAD)
78 #define QUIT BUTTON_POWER
79 #define LEFT BUTTON_LEFT
80 #define RIGHT BUTTON_RIGHT
81 #define UP BUTTON_UP
82 #define DOWN BUTTON_DOWN
83 #define PAUSE BUTTON_REC
86 #elif CONFIG_KEYPAD == IRIVER_H10_PAD
88 #define QUIT BUTTON_POWER
89 #define LEFT BUTTON_LEFT
90 #define RIGHT BUTTON_RIGHT
91 #define UP BUTTON_SCROLL_UP
92 #define DOWN BUTTON_SCROLL_DOWN
93 #define PAUSE BUTTON_PLAY
95 #elif CONFIG_KEYPAD == RECORDER_PAD
97 #define QUIT BUTTON_OFF
98 #define LEFT BUTTON_LEFT
99 #define RIGHT BUTTON_RIGHT
100 #define DOWN BUTTON_DOWN
101 #define UP BUTTON_UP
102 #define PAUSE BUTTON_PLAY
104 #elif CONFIG_KEYPAD == ONDIO_PAD
106 #define QUIT BUTTON_OFF
107 #define LEFT BUTTON_LEFT
108 #define RIGHT BUTTON_RIGHT
109 #define DOWN BUTTON_DOWN
110 #define UP BUTTON_UP
111 #define PAUSE BUTTON_MENU
113 #else
114 #error Unsupported keypad
115 #endif
117 #define MOVE_NO 0 /* player movement */
118 #define MOVE_UP 1 /* 1 */
119 #define MOVE_DN 2 /* 3 0 4 */
120 #define MOVE_LT 3 /* 2 */
121 #define MOVE_RT 4
123 /* ball movement (12 ways) */
124 /* UUL UR */
125 /* UL UR */
126 /* ULL . URR */
127 /* DLL DRR */
128 /* DL DR */
129 /* DDL DDR */
131 #define DIR_UU (1<<7)
132 #define DIR_U (1<<6)
133 #define DIR_RR (1<<5)
134 #define DIR_R (1<<4)
135 #define DIR_DD (1<<3)
136 #define DIR_D (1<<2)
137 #define DIR_LL (1<<1)
138 #define DIR_L (1<<0)
140 #define MOVE_UUR ( DIR_UU | DIR_R )
141 #define MOVE_UR ( DIR_U | DIR_R )
142 #define MOVE_URR ( DIR_U | DIR_RR )
143 #define MOVE_DRR ( DIR_D | DIR_RR )
144 #define MOVE_DR ( DIR_D | DIR_R )
145 #define MOVE_DDR ( DIR_DD | DIR_R )
146 #define MOVE_DDL ( DIR_DD | DIR_L )
147 #define MOVE_DL ( DIR_D | DIR_L )
148 #define MOVE_DLL ( DIR_D | DIR_LL )
149 #define MOVE_ULL ( DIR_U | DIR_LL )
150 #define MOVE_UL ( DIR_U | DIR_L )
151 #define MOVE_UUL ( DIR_UU | DIR_L )
153 #if (LCD_WIDTH>112) && (LCD_HEIGHT>64)
154 # define CUBE_SIZE 8 /* 8x22=176 */
155 # define pos(a) ((a)>>3)
156 #else
157 # define CUBE_SIZE 4
158 # define pos(a) ((a)>>2)
159 #endif
161 #define STARTING_QIXES 2
162 #define MAX_LEVEL 10
163 #define MAX_QIXES MAX_LEVEL+STARTING_QIXES
164 #define BOARD_W ((int)(LCD_WIDTH/CUBE_SIZE))
165 #define BOARD_H ((int)(LCD_HEIGHT/CUBE_SIZE))
166 #define BOARD_X (LCD_WIDTH-BOARD_W*CUBE_SIZE)/2
167 #define BOARD_Y (LCD_HEIGHT-BOARD_H*CUBE_SIZE)/2
169 #ifdef HAVE_LCD_COLOR
170 #define CLR_RED LCD_RGBPACK(255,0,0) /* used to imply danger */
171 #define CLR_LTBLUE LCD_RGBPACK(125, 145, 180) /* used for frame and filling */
172 #define PLR_COL LCD_WHITE /* color used for the player */
173 #elif LCD_DEPTH>=2
174 #define CLR_RED LCD_DARKGRAY /* used to imply danger */
175 #define CLR_LTBLUE LCD_LIGHTGRAY /* used for frame and filling */
176 #define PLR_COL LCD_BLACK /* color used for the player */
177 #endif
179 #if LCD_DEPTH>=2
180 #define EMPTIED LCD_BLACK /* empty spot */
181 #define FILLED CLR_LTBLUE /* filled spot */
182 #define TRAIL CLR_RED /* the red trail of the player */
183 #define QIX LCD_WHITE
184 #else
185 #define EMPTIED 0
186 #define FILLED 1
187 #define TRAIL 2
188 #define QIX 3
189 #endif
190 #define UNCHECKED 0
191 #define CHECKED 1
192 #define PAINTED -1
193 #define PIC_QIX 0
194 #define PIC_PLAYER 1
196 #define MENU_START 0
197 #define MENU_QUIT 1
199 /* The time (in ms) for one iteration through the game loop - decrease this
200 to speed up the game - note that current_tick is (currently) only accurate
201 to 10ms.
203 static int speed = 6; /* CYCLETIME = (11-speed)*10 ms */
204 static int difficulty = 75; /* Percentage of screen that needs to be filled
205 * in order to win the game */
207 static struct plugin_api *rb;
209 MEM_FUNCTION_WRAPPERS(rb);
211 static bool quit = false;
213 static unsigned int board[BOARD_H][BOARD_W];
214 static int testboard[BOARD_H][BOARD_W];
216 #if CUBE_SIZE == 8
218 00011000 0x18 - 11100111 0xe7
219 00111100 0x3c - 11100111 0xe7
220 01111110 0x7e - 11000011 0xc3
221 11111111 0xff - 00000000 0x00
222 11111111 0xff - 00000000 0x00
223 01111110 0x7e - 11000011 0xc3
224 00111100 0x3c - 11100111 0xe7
225 00011000 0x18 - 11100111 0xe7
227 const unsigned char pics[2][8] = {
228 {0x18, 0x3c, 0x7e, 0xff, 0xff, 0x7e, 0x3c, 0x18}, /* Alien (QIX) */
229 {0xe7, 0xe7, 0xc3, 0x00, 0x00, 0xc3, 0xe7, 0xe7} /* Player (XONIX) */
231 #elif CUBE_SIZE == 4
233 0110 0x6 - 1001 0x9
234 1111 0xf - 0110 0x6
235 1111 0xf - 0110 0x6
236 0110 0x6 - 1001 0x9
238 const unsigned char pics[2][4] = {
239 {0x6, 0xf, 0xf, 0x6}, /* Alien (QIX) */
240 {0x9, 0x6, 0x6, 0x9} /* Player (XONIX) */
242 #else
243 #error Incorrect CUBE_SIZE value.
244 #endif
246 static struct qix
248 int velocity; /* velocity */
249 int x, y; /* position on screen */
250 int angle; /* angle */
251 } qixes[MAX_QIXES]; /* black_qix */
253 static struct splayer
255 int i, j; /* position on board */
256 int move, score, level, lives;
257 bool drawing;
258 bool gameover;
259 } player;
261 static int percentage_cache;
263 /*************************** STACK STUFF **********************/
265 /* the stack */
266 #define STACK_SIZE (2*BOARD_W*BOARD_H)
267 static struct pos
269 int x, y; /* position on board */
270 } stack[STACK_SIZE];
271 static int stackPointer;
273 static inline bool pop (struct pos *p)
275 if (stackPointer > 0) {
276 p->x = stack[stackPointer].x;
277 p->y = stack[stackPointer].y;
278 stackPointer--;
279 return true;
280 } else
281 return false; /* SE */
284 static inline bool push (struct pos *p)
286 if (stackPointer < STACK_SIZE - 1) {
287 stackPointer++;
288 stack[stackPointer].x = p->x;
289 stack[stackPointer].y = p->y;
290 return true;
291 } else
292 return false; /* SOF */
295 static inline void emptyStack (void)
297 stackPointer = 0;
300 /*********************** END OF STACK STUFF *********************/
302 /* calculate the new x coordinate of the ball according to angle and speed */
303 static inline int get_newx (int x, int len, int deg)
305 if (deg & DIR_R)
306 return x + len;
307 else if (deg & DIR_L)
308 return x - len;
309 else if (deg & DIR_RR)
310 return x + len * 2;
311 else /* (def & DIR_LL) */
312 return x - len * 2;
315 /* calculate the new y coordinate of the ball according to angle and speed */
316 static inline int get_newy (int y, int len, int deg)
318 if (deg & DIR_D)
319 return y + len;
320 else if (deg & DIR_U)
321 return y - len;
322 else if (deg & DIR_DD)
323 return y + len * 2;
324 else /* (deg & DIR_UU) */
325 return y - len * 2;
328 /* make random function get it's value from the device ticker */
329 static inline void randomize (void)
331 rb->srand (*rb->current_tick);
334 /* get a random number between 0 and range-1 */
335 static int t_rand (int range)
337 return rb->rand () % range;
340 /* initializes the test help board */
341 static void init_testboard (void)
343 int j; /* testboard */
344 for (j = 0; j < BOARD_H; j++)
345 /* UNCHEKED == (int)0 */
346 rb->memset( testboard[j], 0, BOARD_W * sizeof( int ) );
349 /* initializes the game board on with the player,qix's and black qix */
350 static void init_board (void)
352 int i, j;
353 for (j = 0; j < BOARD_H; j++)
354 for (i = 0; i < BOARD_W; i++) { /* make a nice cyan frame */
355 if ((i == 0) || (j <= 1) || (i == BOARD_W - 1)
356 || (j >= BOARD_H - 2))
357 board[j][i] = FILLED;
358 else
359 board[j][i] = EMPTIED;
362 /* (level+2) is the number of qixes */
363 for (j = 0; j < player.level + STARTING_QIXES; j++) {
364 qixes[j].velocity = t_rand (2) + 1; /* 1 or 2 pix-per-sec */
366 /* not on frame */
367 qixes[j].x = CUBE_SIZE*2 + 2*t_rand (((BOARD_W-4)*CUBE_SIZE)/2);
368 qixes[j].y = CUBE_SIZE*2 + 2*t_rand (((BOARD_H-4)*CUBE_SIZE)/2);
370 const int angle_table[] = {
371 MOVE_UUR, MOVE_UR, MOVE_URR, MOVE_DRR, MOVE_DR, MOVE_DDR,
372 MOVE_UUL, MOVE_UL, MOVE_ULL, MOVE_DLL, MOVE_DL, MOVE_DDL };
373 qixes[j].angle = angle_table[t_rand (12)];
374 #if CUBE_SIZE == 4
375 /* Work arround a nasty bug. FIXME */
376 if( qixes[j].angle & (DIR_LL|DIR_RR|DIR_UU|DIR_DD) )
377 qixes[j].velocity = 1;
378 #endif
380 /*black_qix.velocity=1;
381 black_qix.x=BOARD_X+(BOARD_W*CUBE_SIZE)/2-CUBE_SIZE/2;
382 black_qix.y=BOARD_Y+(BOARD_H*CUBE_SIZE)-CUBE_SIZE-CUBE_SIZE/2;
383 black_qix.angle=MOVE_UR; */
384 player.move = MOVE_NO;
385 player.drawing = false;
386 player.i = BOARD_W / 2;
387 player.j = 1;
389 percentage_cache = 0;
392 /* calculates the percentage of the screen filling */
393 static int percentage (void)
395 int i, j, filled = 0;
396 for (j = 2; j < BOARD_H - 2; j++)
397 for (i = 1; i < BOARD_W - 1; i++)
398 if (board[j][i] == FILLED)
399 filled++;
400 return (filled * 100) / ((BOARD_W - 2) * (BOARD_H - 4));
403 /* draw the board on with all the game figures */
404 static void refresh_board (void)
406 int i, j;
407 char str[25];
409 #if LCD_DEPTH>=2
410 rb->lcd_set_background (LCD_BLACK);
411 #else
412 rb->lcd_clear_display ();
413 #endif
414 for (j = 0; j < BOARD_H; j++)
416 unsigned last_color = board[j][0];
417 int last_i = 0;
418 for (i = 1; i < BOARD_W; i++) {
419 if( last_color != board[j][i] )
421 #if LCD_DEPTH>=2
422 rb->lcd_set_foreground (last_color);
423 #else
424 if (last_color != EMPTIED)
425 #endif
426 rb->lcd_fillrect (BOARD_X + CUBE_SIZE * (last_i),
427 BOARD_Y + CUBE_SIZE * j,
428 CUBE_SIZE * (i - last_i), CUBE_SIZE );
429 last_color = board[j][i];
430 last_i = i;
433 #if LCD_DEPTH>=2
434 rb->lcd_set_foreground (last_color);
435 #else
436 if (last_color != EMPTIED)
437 #endif
438 rb->lcd_fillrect (BOARD_X + CUBE_SIZE * (last_i),
439 BOARD_Y + CUBE_SIZE * j,
440 CUBE_SIZE * (i - last_i), CUBE_SIZE);
443 #if LCD_DEPTH>=2
444 rb->lcd_set_foreground (LCD_BLACK);
445 rb->lcd_set_background (CLR_LTBLUE);
446 #else
447 rb->lcd_set_drawmode (DRMODE_COMPLEMENT);
448 #endif
449 rb->snprintf (str, sizeof (str), "Level %d", player.level + 1);
450 rb->lcd_putsxy (BOARD_X, BOARD_Y, str);
451 rb->snprintf (str, sizeof (str), "%d%%", percentage_cache);
452 rb->lcd_putsxy (BOARD_X + CUBE_SIZE * BOARD_W - 24, BOARD_Y, str);
453 rb->snprintf (str, sizeof (str), "Score: %d", player.score);
454 rb->lcd_putsxy (BOARD_X, BOARD_Y + CUBE_SIZE * BOARD_H - 8, str);
455 rb->snprintf (str, sizeof (str), "%d Lives", player.lives);
456 #if LCD_DEPTH>=2
457 rb->lcd_putsxy (BOARD_X + CUBE_SIZE * BOARD_W - 60,
458 BOARD_Y + CUBE_SIZE * BOARD_H - 8, str);
459 #else
460 rb->lcd_putsxy (BOARD_X + CUBE_SIZE * BOARD_W - 40,
461 BOARD_Y + CUBE_SIZE * BOARD_H - 8, str);
462 #endif
464 #if LCD_DEPTH>=2
465 rb->lcd_set_foreground (PLR_COL);
466 rb->lcd_set_background (board[player.j][player.i]);
467 #endif
468 rb->lcd_mono_bitmap (pics[PIC_PLAYER], player.i * CUBE_SIZE + BOARD_X,
469 player.j * CUBE_SIZE + BOARD_Y, CUBE_SIZE, CUBE_SIZE);
471 #if LCD_DEPTH>=2
472 rb->lcd_set_background (EMPTIED);
473 rb->lcd_set_foreground (LCD_WHITE);
474 rb->lcd_set_drawmode (DRMODE_FG);
475 #else
476 rb->lcd_set_drawmode (DRMODE_FG);
477 #endif
478 for (j = 0; j < player.level + STARTING_QIXES; j++)
479 rb->lcd_mono_bitmap (pics[PIC_QIX], qixes[j].x + BOARD_X,
480 qixes[j].y + BOARD_Y, CUBE_SIZE, CUBE_SIZE);
481 #if LCD_DEPTH>=2
482 rb->lcd_set_foreground (LCD_BLACK);
483 #endif
484 rb->lcd_set_drawmode (DRMODE_SOLID);
486 rb->lcd_update ();
489 static inline int infested_area (int i, int j, int v)
491 struct pos p;
492 p.x = i;
493 p.y = j;
494 emptyStack ();
495 if (!push (&p))
496 return -1;
497 while (pop (&p)) {
498 if (testboard[p.y][p.x] == v) continue;
499 if (testboard[p.y][p.x] > UNCHECKED)
500 return 1; /* This area was previously flagged as infested */
501 testboard[p.y][p.x] = v;
502 if (board[p.y][p.x] == QIX)
503 return 1; /* Infested area */
505 struct pos p1 = { p.x+1, p.y };
506 if ((p1.x < BOARD_W)
507 && (board[p1.y][p1.x] != FILLED)
508 && (!push (&p1)))
509 return -1;
512 struct pos p1 = { p.x-1, p.y };
513 if ((p1.x >= 0)
514 && (board[p1.y][p1.x] != FILLED)
515 && (!push (&p1)))
516 return -1;
519 struct pos p1 = { p.x, p.y+1 };
520 if ((p1.y < BOARD_H)
521 && (board[p1.y][p1.x] != FILLED)
522 && (!push (&p1)))
523 return -1;
526 struct pos p1 = { p.x, p.y-1 };
527 if ((p1.y >= 0)
528 && (board[p1.y][p1.x] != FILLED)
529 && (!push (&p1)))
530 return -1;
533 return 0;
536 static inline int fill_area (int i, int j)
538 struct pos p;
539 p.x = i;
540 p.y = j;
541 int v = testboard[p.y][p.x];
542 emptyStack ();
543 if (!push (&p))
544 return -1;
545 while (pop (&p)) {
546 board[p.y][p.x] = FILLED;
547 testboard[p.y][p.x] = PAINTED;
549 struct pos p1 = { p.x+1, p.y };
550 if ((p1.x < BOARD_W)
551 && (testboard[p1.y][p1.x] == v)
552 && (!push (&p1)))
553 return -1;
556 struct pos p1 = { p.x-1, p.y };
557 if ((p1.x >= 0)
558 && (testboard[p1.y][p1.x] == v)
559 && (!push (&p1)))
560 return -1;
563 struct pos p1 = { p.x, p.y+1 };
564 if ((p1.y < BOARD_H)
565 && (testboard[p1.y][p1.x] == v)
566 && (!push (&p1)))
567 return -1;
570 struct pos p1 = { p.x, p.y-1 };
571 if ((p1.y >= 0)
572 && (testboard[p1.y][p1.x] == v)
573 && (!push (&p1)))
574 return -1;
577 return 0;
581 /* take care of stuff after xonix has landed on a filled spot */
582 static void complete_trail (int fill)
584 int i, j, ret;
585 for (j = 0; j < BOARD_H; j++) {
586 for (i = 0; i < BOARD_W; i++) {
587 if (board[j][i] == TRAIL) {
588 if (fill)
589 board[j][i] = FILLED;
590 else
591 board[j][i] = EMPTIED;
596 if (fill) {
597 int v = CHECKED;
598 for (i = 0; i < player.level + STARTING_QIXES; i++) /* add qixes to board */
599 board[pos(qixes[i].y - BOARD_Y)]
600 [pos(qixes[i].x - BOARD_X)] = QIX;
602 init_testboard();
603 for (j = 1; j < BOARD_H - 1; j++) {
604 for (i = 0; i < BOARD_W - 0; i++) {
605 if (board[j][i] != FILLED) {
606 ret = infested_area (i, j, v);
607 if (ret < 0 || ( ret == 0 && fill_area (i, j) ) )
608 quit = true;
609 v++;
614 for (i = 0; i < player.level + STARTING_QIXES; i++) /* add qixes to board */
615 board[pos(qixes[i].y - BOARD_Y)]
616 [pos(qixes[i].x - BOARD_X)] = EMPTIED;
617 percentage_cache = percentage();
620 rb->button_clear_queue();
623 /* returns the color the real pixel(x,y) on the lcd is pointing at */
624 static inline unsigned int getpixel (int x, int y)
626 const int a = pos (x - BOARD_X), b = pos (y - BOARD_Y);
627 if ((a > 0) && (a < BOARD_W) && (b > 0) && (b < BOARD_H)) /* if inside board */
628 return board[b][a];
629 else
630 return FILLED;
633 /* returns the color the ball on (newx,newy) is heading at *----*
634 checks the four edge points of the square if 1st of all | |
635 are a trail (cause it's a lose life situation) and 2nd | |
636 if it's filled so it needs to bounce. *____*
638 static inline unsigned int next_hit (int newx, int newy)
640 if ((getpixel (newx, newy) == TRAIL)
641 || (getpixel (newx, newy + CUBE_SIZE - 1) == TRAIL)
642 || (getpixel (newx + CUBE_SIZE - 1, newy) == TRAIL)
643 || (getpixel (newx + CUBE_SIZE - 1, newy + CUBE_SIZE - 1) == TRAIL))
644 return TRAIL;
645 else if ((getpixel (newx, newy) == FILLED)
646 || (getpixel (newx, newy + CUBE_SIZE - 1) == FILLED)
647 || (getpixel (newx + CUBE_SIZE - 1, newy) == FILLED)
648 || (getpixel (newx + CUBE_SIZE - 1, newy + CUBE_SIZE - 1) ==
649 FILLED))
650 return FILLED;
651 else
652 return EMPTIED;
655 static void die (void)
657 player.lives--;
658 if (player.lives == 0)
659 player.gameover = true;
660 else {
661 refresh_board ();
662 rb->splash (HZ, "Crash!");
663 complete_trail (false);
664 player.move = MOVE_NO;
665 player.drawing = false;
666 player.i = BOARD_W / 2;
667 player.j = 1;
671 /* returns true if the (side) of the block -***-
672 starting from (newx,newy) has any filled pixels * *
673 -***-
675 static inline bool line_check_lt (int newx, int newy)
677 return getpixel (newx, newy + CUBE_SIZE/2-1) == FILLED
678 && getpixel (newx, newy + CUBE_SIZE/2 ) == FILLED;
680 static inline bool line_check_rt (int newx, int newy)
682 return getpixel (newx + CUBE_SIZE-1, newy + CUBE_SIZE/2-1) == FILLED
683 && getpixel (newx + CUBE_SIZE-1, newy + CUBE_SIZE/2 ) == FILLED;
685 static inline bool line_check_up (int newx, int newy)
687 return getpixel (newx + CUBE_SIZE/2-1, newy) == FILLED
688 && getpixel (newx + CUBE_SIZE/2 , newy) == FILLED;
690 static inline bool line_check_dn (int newx, int newy)
692 return getpixel (newx + CUBE_SIZE/2-1, newy + CUBE_SIZE-1) == FILLED
693 && getpixel (newx + CUBE_SIZE/2 , newy + CUBE_SIZE-1) == FILLED;
696 static inline void move_qix (struct qix *q)
698 int newx, newy;
699 newx = get_newx (q->x, q->velocity, q->angle);
700 newy = get_newy (q->y, q->velocity, q->angle);
701 switch (next_hit (newx, newy))
703 case EMPTIED:
704 q->x = newx;
705 q->y = newy;
706 break;
707 case FILLED:
709 const int a = q->angle;
710 q->angle =
711 ((a&(DIR_UU|DIR_U))
712 ? (line_check_up (newx, newy) ? ((a&(DIR_UU|DIR_U))>>4)
713 : (a&(DIR_UU|DIR_U)))
714 : 0)
716 ((a&(DIR_RR|DIR_R))
717 ? (line_check_rt (newx, newy) ? ((a&(DIR_RR|DIR_R))>>4)
718 : (a&(DIR_RR|DIR_R)))
719 : 0)
721 ((a&(DIR_DD|DIR_D))
722 ? (line_check_dn (newx, newy) ? ((a&(DIR_DD|DIR_D))<<4)
723 : (a&(DIR_DD|DIR_D)))
724 : 0)
726 ((a&(DIR_LL|DIR_L))
727 ? (line_check_lt (newx, newy) ? ((a&(DIR_LL|DIR_L))<<4)
728 : (a&(DIR_LL|DIR_L)))
729 : 0);
730 q->x = get_newx (q->x, q->velocity, q->angle);
731 q->y = get_newy (q->y, q->velocity, q->angle);
732 break;
734 case TRAIL:
735 die();
736 break;
740 /* move the board forward timewise */
741 static inline void move_board (void)
743 int j, newi, newj;
745 for (j = 0; j < player.level + STARTING_QIXES; j++)
746 move_qix (&qixes[j]);
747 /* move_qix(&black_qix,true); */
748 if (player.move) {
749 newi = player.i;
750 newj = player.j;
751 switch (player.move) {
752 case MOVE_UP:
753 if (player.j > 1)
754 newj--;
755 break;
756 case MOVE_DN:
757 if (player.j < BOARD_H - 2)
758 newj++;
759 break;
760 case MOVE_LT:
761 if (player.i > 0)
762 newi--;
763 break;
764 case MOVE_RT:
765 if (player.i < BOARD_W - 1)
766 newi++;
767 break;
768 default:
769 break;
772 if ((player.drawing) && (board[newj][newi] == EMPTIED)) /* continue drawing */
773 board[newj][newi] = TRAIL;
774 else if ((player.drawing) && (board[newj][newi] == FILLED)) { /* finish drawing */
775 player.move = MOVE_NO; /* stop moving */
776 player.drawing = false;
777 complete_trail (true);
778 } else if ((board[player.j][player.i] == FILLED)
779 && (board[newj][newi] == EMPTIED)) {
780 /* start drawing */
781 player.drawing = true;
782 board[newj][newi] = TRAIL;
783 /* if the block after next is empty and we're moving onto filled, stop */
784 } else if ((board[newj][newi] == FILLED)
785 && (board[newj + newj-player.j][newi + newi-player.i] == EMPTIED)) {
786 player.move = MOVE_NO;
788 player.i = newi;
789 player.j = newj;
791 if (percentage_cache >= difficulty) { /* finished level */
792 rb->splash (HZ * 2, "Level %d finished", player.level+1);
793 player.score += percentage_cache;
794 if (player.level < MAX_LEVEL)
795 player.level++;
796 init_board ();
797 refresh_board ();
798 rb->splash (HZ * 2, "Ready?");
802 /* the main menu */
803 static int game_menu (void)
805 MENUITEM_STRINGLIST(menu, "XOBOX Menu", NULL, "Start New Game",
806 "Speed","Difficulty","Quit");
807 int selection = 0;
808 #ifdef HAVE_LCD_COLOR
809 rb->lcd_set_foreground (rb->global_settings->fg_color);
810 rb->lcd_set_background (rb->global_settings->bg_color);
811 #elif LCD_DEPTH>=2
812 rb->lcd_set_foreground(LCD_BLACK);
813 rb->lcd_set_background(LCD_WHITE);
814 #endif
815 for (;;) {
816 rb->do_menu(&menu,&selection);
817 if (selection==1)
818 rb->set_int ("Speed", "", UNIT_INT, &speed, NULL, 1, 1, 10, NULL);
819 else if (selection==2)
820 rb->set_int ("Difficulty", "", UNIT_INT, &difficulty, NULL,
821 5, 50, 95, NULL);
822 else
823 break;
825 if (selection != MENU_START) {
826 selection = MENU_QUIT;
828 return selection;
831 /* init game's variables */
832 static void init_game (void)
834 player.level = 0;
835 player.score = 0;
836 player.lives = 3;
837 player.gameover = false;
838 player.drawing = false;
839 rb->lcd_setfont(FONT_SYSFIXED);
840 init_board ();
841 refresh_board ();
842 rb->splash (HZ * 2, "Ready?");
845 /* general keypad handler loop */
846 static int xobox_loop (void)
848 int button = 0, ret;
849 bool pause = false;
850 int end;
852 while (!quit) {
853 end = *rb->current_tick + ((11-speed)*HZ)/100;
855 #ifdef HAS_BUTTON_HOLD
856 if (rb->button_hold()) {
857 pause = true;
858 rb->splash (HZ, "PAUSED");
860 #endif
862 button = rb->button_get_w_tmo (true);
863 switch (button) {
864 case UP:
865 case UP|BUTTON_REPEAT:
866 player.move = MOVE_UP;
867 break;
868 case DOWN:
869 case DOWN|BUTTON_REPEAT:
870 player.move = MOVE_DN;
871 break;
872 case LEFT:
873 case LEFT|BUTTON_REPEAT:
874 player.move = MOVE_LT;
875 break;
876 case RIGHT:
877 case RIGHT|BUTTON_REPEAT:
878 player.move = MOVE_RT;
879 break;
880 case PAUSE:
881 pause = !pause;
882 if (pause)
883 rb->splash (HZ, "Paused");
884 break;
885 case QUIT:
886 ret = game_menu ();
887 if (ret == MENU_START)
888 init_game ();
889 else
891 quit = true;
892 continue;
894 break;
895 default:
896 if (rb->default_event_handler (button) == SYS_USB_CONNECTED)
897 return PLUGIN_USB_CONNECTED;
898 break;
900 if (!pause) {
901 move_board ();
902 refresh_board ();
904 if (player.gameover) {
905 rb->splash (HZ, "Game Over!");
906 ret = game_menu ();
907 if (ret == MENU_START)
908 init_game ();
909 else
910 quit = true;
913 if (end > *rb->current_tick)
914 rb->sleep (end - *rb->current_tick);
915 else
916 rb->yield ();
918 } /* end while */
919 return PLUGIN_OK; /* for no warnings on compiling */
922 /* plugin main procedure */
923 enum plugin_status plugin_start (struct plugin_api *api, void *parameter)
925 int ret = PLUGIN_OK;
927 (void) parameter;
928 rb = api;
930 rb->lcd_setfont (FONT_SYSFIXED);
931 #if LCD_DEPTH>=2
932 rb->lcd_set_backdrop(NULL);
933 #endif
935 /* Permanently enable the backlight (unless the user has turned it off) */
936 if (rb->global_settings->backlight_timeout > 0)
937 rb->backlight_set_timeout (1);
939 quit = false;
941 randomize ();
942 if (game_menu () == MENU_START) {
943 init_game ();
944 ret = xobox_loop ();
947 rb->backlight_set_timeout (rb->global_settings->backlight_timeout);
948 rb->lcd_setfont (FONT_UI);
950 return ret;