A couple more tweaks
[Rockbox.git] / apps / plugins / xobox.c
blobf51e98ecff883a7d48a615d384587d547208496d
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) || \
78 (CONFIG_KEYPAD == SANSA_C200_PAD)
80 #define QUIT BUTTON_POWER
81 #define LEFT BUTTON_LEFT
82 #define RIGHT BUTTON_RIGHT
83 #define UP BUTTON_UP
84 #define DOWN BUTTON_DOWN
85 #define PAUSE BUTTON_REC
88 #elif CONFIG_KEYPAD == IRIVER_H10_PAD
90 #define QUIT BUTTON_POWER
91 #define LEFT BUTTON_LEFT
92 #define RIGHT BUTTON_RIGHT
93 #define UP BUTTON_SCROLL_UP
94 #define DOWN BUTTON_SCROLL_DOWN
95 #define PAUSE BUTTON_PLAY
97 #elif CONFIG_KEYPAD == RECORDER_PAD
99 #define QUIT BUTTON_OFF
100 #define LEFT BUTTON_LEFT
101 #define RIGHT BUTTON_RIGHT
102 #define DOWN BUTTON_DOWN
103 #define UP BUTTON_UP
104 #define PAUSE BUTTON_PLAY
106 #elif CONFIG_KEYPAD == ONDIO_PAD
108 #define QUIT BUTTON_OFF
109 #define LEFT BUTTON_LEFT
110 #define RIGHT BUTTON_RIGHT
111 #define DOWN BUTTON_DOWN
112 #define UP BUTTON_UP
113 #define PAUSE BUTTON_MENU
115 #else
116 #error Unsupported keypad
117 #endif
119 #define MOVE_NO 0 /* player movement */
120 #define MOVE_UP 1 /* 1 */
121 #define MOVE_DN 2 /* 3 0 4 */
122 #define MOVE_LT 3 /* 2 */
123 #define MOVE_RT 4
125 /* ball movement (12 ways) */
126 /* UUL UR */
127 /* UL UR */
128 /* ULL . URR */
129 /* DLL DRR */
130 /* DL DR */
131 /* DDL DDR */
133 #define DIR_UU (1<<7)
134 #define DIR_U (1<<6)
135 #define DIR_RR (1<<5)
136 #define DIR_R (1<<4)
137 #define DIR_DD (1<<3)
138 #define DIR_D (1<<2)
139 #define DIR_LL (1<<1)
140 #define DIR_L (1<<0)
142 #define MOVE_UUR ( DIR_UU | DIR_R )
143 #define MOVE_UR ( DIR_U | DIR_R )
144 #define MOVE_URR ( DIR_U | DIR_RR )
145 #define MOVE_DRR ( DIR_D | DIR_RR )
146 #define MOVE_DR ( DIR_D | DIR_R )
147 #define MOVE_DDR ( DIR_DD | DIR_R )
148 #define MOVE_DDL ( DIR_DD | DIR_L )
149 #define MOVE_DL ( DIR_D | DIR_L )
150 #define MOVE_DLL ( DIR_D | DIR_LL )
151 #define MOVE_ULL ( DIR_U | DIR_LL )
152 #define MOVE_UL ( DIR_U | DIR_L )
153 #define MOVE_UUL ( DIR_UU | DIR_L )
155 #if (LCD_WIDTH>112) && (LCD_HEIGHT>64)
156 # define CUBE_SIZE 8 /* 8x22=176 */
157 # define pos(a) ((a)>>3)
158 #else
159 # define CUBE_SIZE 4
160 # define pos(a) ((a)>>2)
161 #endif
163 #define STARTING_QIXES 2
164 #define MAX_LEVEL 10
165 #define MAX_QIXES MAX_LEVEL+STARTING_QIXES
166 #define BOARD_W ((int)(LCD_WIDTH/CUBE_SIZE))
167 #define BOARD_H ((int)(LCD_HEIGHT/CUBE_SIZE))
168 #define BOARD_X (LCD_WIDTH-BOARD_W*CUBE_SIZE)/2
169 #define BOARD_Y (LCD_HEIGHT-BOARD_H*CUBE_SIZE)/2
171 #ifdef HAVE_LCD_COLOR
172 #define CLR_RED LCD_RGBPACK(255,0,0) /* used to imply danger */
173 #define CLR_LTBLUE LCD_RGBPACK(125, 145, 180) /* used for frame and filling */
174 #define PLR_COL LCD_WHITE /* color used for the player */
175 #elif LCD_DEPTH>=2
176 #define CLR_RED LCD_DARKGRAY /* used to imply danger */
177 #define CLR_LTBLUE LCD_LIGHTGRAY /* used for frame and filling */
178 #define PLR_COL LCD_BLACK /* color used for the player */
179 #endif
181 #if LCD_DEPTH>=2
182 #define EMPTIED LCD_BLACK /* empty spot */
183 #define FILLED CLR_LTBLUE /* filled spot */
184 #define TRAIL CLR_RED /* the red trail of the player */
185 #define QIX LCD_WHITE
186 #else
187 #define EMPTIED 0
188 #define FILLED 1
189 #define TRAIL 2
190 #define QIX 3
191 #endif
192 #define UNCHECKED 0
193 #define CHECKED 1
194 #define PAINTED -1
195 #define PIC_QIX 0
196 #define PIC_PLAYER 1
198 #define MENU_START 0
199 #define MENU_QUIT 1
201 /* The time (in ms) for one iteration through the game loop - decrease this
202 to speed up the game - note that current_tick is (currently) only accurate
203 to 10ms.
205 static int speed = 6; /* CYCLETIME = (11-speed)*10 ms */
206 static int difficulty = 75; /* Percentage of screen that needs to be filled
207 * in order to win the game */
209 static struct plugin_api *rb;
211 MEM_FUNCTION_WRAPPERS(rb);
213 static bool quit = false;
215 static unsigned int board[BOARD_H][BOARD_W];
216 static int testboard[BOARD_H][BOARD_W];
218 #if CUBE_SIZE == 8
220 00011000 0x18 - 11100111 0xe7
221 00111100 0x3c - 11100111 0xe7
222 01111110 0x7e - 11000011 0xc3
223 11111111 0xff - 00000000 0x00
224 11111111 0xff - 00000000 0x00
225 01111110 0x7e - 11000011 0xc3
226 00111100 0x3c - 11100111 0xe7
227 00011000 0x18 - 11100111 0xe7
229 const unsigned char pics[2][8] = {
230 {0x18, 0x3c, 0x7e, 0xff, 0xff, 0x7e, 0x3c, 0x18}, /* Alien (QIX) */
231 {0xe7, 0xe7, 0xc3, 0x00, 0x00, 0xc3, 0xe7, 0xe7} /* Player (XONIX) */
233 #elif CUBE_SIZE == 4
235 0110 0x6 - 1001 0x9
236 1111 0xf - 0110 0x6
237 1111 0xf - 0110 0x6
238 0110 0x6 - 1001 0x9
240 const unsigned char pics[2][4] = {
241 {0x6, 0xf, 0xf, 0x6}, /* Alien (QIX) */
242 {0x9, 0x6, 0x6, 0x9} /* Player (XONIX) */
244 #else
245 #error Incorrect CUBE_SIZE value.
246 #endif
248 static struct qix
250 int velocity; /* velocity */
251 int x, y; /* position on screen */
252 int angle; /* angle */
253 } qixes[MAX_QIXES]; /* black_qix */
255 static struct splayer
257 int i, j; /* position on board */
258 int move, score, level, lives;
259 bool drawing;
260 bool gameover;
261 } player;
263 static int percentage_cache;
265 /*************************** STACK STUFF **********************/
267 /* the stack */
268 #define STACK_SIZE (2*BOARD_W*BOARD_H)
269 static struct pos
271 int x, y; /* position on board */
272 } stack[STACK_SIZE];
273 static int stackPointer;
275 static inline bool pop (struct pos *p)
277 if (stackPointer > 0) {
278 p->x = stack[stackPointer].x;
279 p->y = stack[stackPointer].y;
280 stackPointer--;
281 return true;
282 } else
283 return false; /* SE */
286 static inline bool push (struct pos *p)
288 if (stackPointer < STACK_SIZE - 1) {
289 stackPointer++;
290 stack[stackPointer].x = p->x;
291 stack[stackPointer].y = p->y;
292 return true;
293 } else
294 return false; /* SOF */
297 static inline void emptyStack (void)
299 stackPointer = 0;
302 /*********************** END OF STACK STUFF *********************/
304 /* calculate the new x coordinate of the ball according to angle and speed */
305 static inline int get_newx (int x, int len, int deg)
307 if (deg & DIR_R)
308 return x + len;
309 else if (deg & DIR_L)
310 return x - len;
311 else if (deg & DIR_RR)
312 return x + len * 2;
313 else /* (def & DIR_LL) */
314 return x - len * 2;
317 /* calculate the new y coordinate of the ball according to angle and speed */
318 static inline int get_newy (int y, int len, int deg)
320 if (deg & DIR_D)
321 return y + len;
322 else if (deg & DIR_U)
323 return y - len;
324 else if (deg & DIR_DD)
325 return y + len * 2;
326 else /* (deg & DIR_UU) */
327 return y - len * 2;
330 /* make random function get it's value from the device ticker */
331 static inline void randomize (void)
333 rb->srand (*rb->current_tick);
336 /* get a random number between 0 and range-1 */
337 static int t_rand (int range)
339 return rb->rand () % range;
342 /* initializes the test help board */
343 static void init_testboard (void)
345 int j; /* testboard */
346 for (j = 0; j < BOARD_H; j++)
347 /* UNCHEKED == (int)0 */
348 rb->memset( testboard[j], 0, BOARD_W * sizeof( int ) );
351 /* initializes the game board on with the player,qix's and black qix */
352 static void init_board (void)
354 int i, j;
355 for (j = 0; j < BOARD_H; j++)
356 for (i = 0; i < BOARD_W; i++) { /* make a nice cyan frame */
357 if ((i == 0) || (j <= 1) || (i == BOARD_W - 1)
358 || (j >= BOARD_H - 2))
359 board[j][i] = FILLED;
360 else
361 board[j][i] = EMPTIED;
364 /* (level+2) is the number of qixes */
365 for (j = 0; j < player.level + STARTING_QIXES; j++) {
366 qixes[j].velocity = t_rand (2) + 1; /* 1 or 2 pix-per-sec */
368 /* not on frame */
369 qixes[j].x = CUBE_SIZE*2 + 2*t_rand (((BOARD_W-4)*CUBE_SIZE)/2);
370 qixes[j].y = CUBE_SIZE*2 + 2*t_rand (((BOARD_H-4)*CUBE_SIZE)/2);
372 const int angle_table[] = {
373 MOVE_UUR, MOVE_UR, MOVE_URR, MOVE_DRR, MOVE_DR, MOVE_DDR,
374 MOVE_UUL, MOVE_UL, MOVE_ULL, MOVE_DLL, MOVE_DL, MOVE_DDL };
375 qixes[j].angle = angle_table[t_rand (12)];
376 #if CUBE_SIZE == 4
377 /* Work arround a nasty bug. FIXME */
378 if( qixes[j].angle & (DIR_LL|DIR_RR|DIR_UU|DIR_DD) )
379 qixes[j].velocity = 1;
380 #endif
382 /*black_qix.velocity=1;
383 black_qix.x=BOARD_X+(BOARD_W*CUBE_SIZE)/2-CUBE_SIZE/2;
384 black_qix.y=BOARD_Y+(BOARD_H*CUBE_SIZE)-CUBE_SIZE-CUBE_SIZE/2;
385 black_qix.angle=MOVE_UR; */
386 player.move = MOVE_NO;
387 player.drawing = false;
388 player.i = BOARD_W / 2;
389 player.j = 1;
391 percentage_cache = 0;
394 /* calculates the percentage of the screen filling */
395 static int percentage (void)
397 int i, j, filled = 0;
398 for (j = 2; j < BOARD_H - 2; j++)
399 for (i = 1; i < BOARD_W - 1; i++)
400 if (board[j][i] == FILLED)
401 filled++;
402 return (filled * 100) / ((BOARD_W - 2) * (BOARD_H - 4));
405 /* draw the board on with all the game figures */
406 static void refresh_board (void)
408 int i, j;
409 char str[25];
411 #if LCD_DEPTH>=2
412 rb->lcd_set_background (LCD_BLACK);
413 #else
414 rb->lcd_clear_display ();
415 #endif
416 for (j = 0; j < BOARD_H; j++)
418 unsigned last_color = board[j][0];
419 int last_i = 0;
420 for (i = 1; i < BOARD_W; i++) {
421 if( last_color != board[j][i] )
423 #if LCD_DEPTH>=2
424 rb->lcd_set_foreground (last_color);
425 #else
426 if (last_color != EMPTIED)
427 #endif
428 rb->lcd_fillrect (BOARD_X + CUBE_SIZE * (last_i),
429 BOARD_Y + CUBE_SIZE * j,
430 CUBE_SIZE * (i - last_i), CUBE_SIZE );
431 last_color = board[j][i];
432 last_i = i;
435 #if LCD_DEPTH>=2
436 rb->lcd_set_foreground (last_color);
437 #else
438 if (last_color != EMPTIED)
439 #endif
440 rb->lcd_fillrect (BOARD_X + CUBE_SIZE * (last_i),
441 BOARD_Y + CUBE_SIZE * j,
442 CUBE_SIZE * (i - last_i), CUBE_SIZE);
445 #if LCD_DEPTH>=2
446 rb->lcd_set_foreground (LCD_BLACK);
447 rb->lcd_set_background (CLR_LTBLUE);
448 #else
449 rb->lcd_set_drawmode (DRMODE_COMPLEMENT);
450 #endif
451 rb->snprintf (str, sizeof (str), "Level %d", player.level + 1);
452 rb->lcd_putsxy (BOARD_X, BOARD_Y, str);
453 rb->snprintf (str, sizeof (str), "%d%%", percentage_cache);
454 rb->lcd_putsxy (BOARD_X + CUBE_SIZE * BOARD_W - 24, BOARD_Y, str);
455 rb->snprintf (str, sizeof (str), "Score: %d", player.score);
456 rb->lcd_putsxy (BOARD_X, BOARD_Y + CUBE_SIZE * BOARD_H - 8, str);
457 rb->snprintf (str, sizeof (str), "%d Lives", player.lives);
458 #if LCD_DEPTH>=2
459 rb->lcd_putsxy (BOARD_X + CUBE_SIZE * BOARD_W - 60,
460 BOARD_Y + CUBE_SIZE * BOARD_H - 8, str);
461 #else
462 rb->lcd_putsxy (BOARD_X + CUBE_SIZE * BOARD_W - 40,
463 BOARD_Y + CUBE_SIZE * BOARD_H - 8, str);
464 #endif
466 #if LCD_DEPTH>=2
467 rb->lcd_set_foreground (PLR_COL);
468 rb->lcd_set_background (board[player.j][player.i]);
469 #endif
470 rb->lcd_mono_bitmap (pics[PIC_PLAYER], player.i * CUBE_SIZE + BOARD_X,
471 player.j * CUBE_SIZE + BOARD_Y, CUBE_SIZE, CUBE_SIZE);
473 #if LCD_DEPTH>=2
474 rb->lcd_set_background (EMPTIED);
475 rb->lcd_set_foreground (LCD_WHITE);
476 rb->lcd_set_drawmode (DRMODE_FG);
477 #else
478 rb->lcd_set_drawmode (DRMODE_FG);
479 #endif
480 for (j = 0; j < player.level + STARTING_QIXES; j++)
481 rb->lcd_mono_bitmap (pics[PIC_QIX], qixes[j].x + BOARD_X,
482 qixes[j].y + BOARD_Y, CUBE_SIZE, CUBE_SIZE);
483 #if LCD_DEPTH>=2
484 rb->lcd_set_foreground (LCD_BLACK);
485 #endif
486 rb->lcd_set_drawmode (DRMODE_SOLID);
488 rb->lcd_update ();
491 static inline int infested_area (int i, int j, int v)
493 struct pos p;
494 p.x = i;
495 p.y = j;
496 emptyStack ();
497 if (!push (&p))
498 return -1;
499 while (pop (&p)) {
500 if (testboard[p.y][p.x] == v) continue;
501 if (testboard[p.y][p.x] > UNCHECKED)
502 return 1; /* This area was previously flagged as infested */
503 testboard[p.y][p.x] = v;
504 if (board[p.y][p.x] == QIX)
505 return 1; /* Infested area */
507 struct pos p1 = { p.x+1, p.y };
508 if ((p1.x < BOARD_W)
509 && (board[p1.y][p1.x] != FILLED)
510 && (!push (&p1)))
511 return -1;
514 struct pos p1 = { p.x-1, p.y };
515 if ((p1.x >= 0)
516 && (board[p1.y][p1.x] != FILLED)
517 && (!push (&p1)))
518 return -1;
521 struct pos p1 = { p.x, p.y+1 };
522 if ((p1.y < BOARD_H)
523 && (board[p1.y][p1.x] != FILLED)
524 && (!push (&p1)))
525 return -1;
528 struct pos p1 = { p.x, p.y-1 };
529 if ((p1.y >= 0)
530 && (board[p1.y][p1.x] != FILLED)
531 && (!push (&p1)))
532 return -1;
535 return 0;
538 static inline int fill_area (int i, int j)
540 struct pos p;
541 p.x = i;
542 p.y = j;
543 int v = testboard[p.y][p.x];
544 emptyStack ();
545 if (!push (&p))
546 return -1;
547 while (pop (&p)) {
548 board[p.y][p.x] = FILLED;
549 testboard[p.y][p.x] = PAINTED;
551 struct pos p1 = { p.x+1, p.y };
552 if ((p1.x < BOARD_W)
553 && (testboard[p1.y][p1.x] == v)
554 && (!push (&p1)))
555 return -1;
558 struct pos p1 = { p.x-1, p.y };
559 if ((p1.x >= 0)
560 && (testboard[p1.y][p1.x] == v)
561 && (!push (&p1)))
562 return -1;
565 struct pos p1 = { p.x, p.y+1 };
566 if ((p1.y < BOARD_H)
567 && (testboard[p1.y][p1.x] == v)
568 && (!push (&p1)))
569 return -1;
572 struct pos p1 = { p.x, p.y-1 };
573 if ((p1.y >= 0)
574 && (testboard[p1.y][p1.x] == v)
575 && (!push (&p1)))
576 return -1;
579 return 0;
583 /* take care of stuff after xonix has landed on a filled spot */
584 static void complete_trail (int fill)
586 int i, j, ret;
587 for (j = 0; j < BOARD_H; j++) {
588 for (i = 0; i < BOARD_W; i++) {
589 if (board[j][i] == TRAIL) {
590 if (fill)
591 board[j][i] = FILLED;
592 else
593 board[j][i] = EMPTIED;
598 if (fill) {
599 int v = CHECKED;
600 for (i = 0; i < player.level + STARTING_QIXES; i++) /* add qixes to board */
601 board[pos(qixes[i].y - BOARD_Y)]
602 [pos(qixes[i].x - BOARD_X)] = QIX;
604 init_testboard();
605 for (j = 1; j < BOARD_H - 1; j++) {
606 for (i = 0; i < BOARD_W - 0; i++) {
607 if (board[j][i] != FILLED) {
608 ret = infested_area (i, j, v);
609 if (ret < 0 || ( ret == 0 && fill_area (i, j) ) )
610 quit = true;
611 v++;
616 for (i = 0; i < player.level + STARTING_QIXES; i++) /* add qixes to board */
617 board[pos(qixes[i].y - BOARD_Y)]
618 [pos(qixes[i].x - BOARD_X)] = EMPTIED;
619 percentage_cache = percentage();
622 rb->button_clear_queue();
625 /* returns the color the real pixel(x,y) on the lcd is pointing at */
626 static inline unsigned int getpixel (int x, int y)
628 const int a = pos (x - BOARD_X), b = pos (y - BOARD_Y);
629 if ((a > 0) && (a < BOARD_W) && (b > 0) && (b < BOARD_H)) /* if inside board */
630 return board[b][a];
631 else
632 return FILLED;
635 /* returns the color the ball on (newx,newy) is heading at *----*
636 checks the four edge points of the square if 1st of all | |
637 are a trail (cause it's a lose life situation) and 2nd | |
638 if it's filled so it needs to bounce. *____*
640 static inline unsigned int next_hit (int newx, int newy)
642 if ((getpixel (newx, newy) == TRAIL)
643 || (getpixel (newx, newy + CUBE_SIZE - 1) == TRAIL)
644 || (getpixel (newx + CUBE_SIZE - 1, newy) == TRAIL)
645 || (getpixel (newx + CUBE_SIZE - 1, newy + CUBE_SIZE - 1) == TRAIL))
646 return TRAIL;
647 else if ((getpixel (newx, newy) == FILLED)
648 || (getpixel (newx, newy + CUBE_SIZE - 1) == FILLED)
649 || (getpixel (newx + CUBE_SIZE - 1, newy) == FILLED)
650 || (getpixel (newx + CUBE_SIZE - 1, newy + CUBE_SIZE - 1) ==
651 FILLED))
652 return FILLED;
653 else
654 return EMPTIED;
657 static void die (void)
659 player.lives--;
660 if (player.lives == 0)
661 player.gameover = true;
662 else {
663 refresh_board ();
664 rb->splash (HZ, "Crash!");
665 complete_trail (false);
666 player.move = MOVE_NO;
667 player.drawing = false;
668 player.i = BOARD_W / 2;
669 player.j = 1;
673 /* returns true if the (side) of the block -***-
674 starting from (newx,newy) has any filled pixels * *
675 -***-
677 static inline bool line_check_lt (int newx, int newy)
679 return getpixel (newx, newy + CUBE_SIZE/2-1) == FILLED
680 && getpixel (newx, newy + CUBE_SIZE/2 ) == FILLED;
682 static inline bool line_check_rt (int newx, int newy)
684 return getpixel (newx + CUBE_SIZE-1, newy + CUBE_SIZE/2-1) == FILLED
685 && getpixel (newx + CUBE_SIZE-1, newy + CUBE_SIZE/2 ) == FILLED;
687 static inline bool line_check_up (int newx, int newy)
689 return getpixel (newx + CUBE_SIZE/2-1, newy) == FILLED
690 && getpixel (newx + CUBE_SIZE/2 , newy) == FILLED;
692 static inline bool line_check_dn (int newx, int newy)
694 return getpixel (newx + CUBE_SIZE/2-1, newy + CUBE_SIZE-1) == FILLED
695 && getpixel (newx + CUBE_SIZE/2 , newy + CUBE_SIZE-1) == FILLED;
698 static inline void move_qix (struct qix *q)
700 int newx, newy;
701 newx = get_newx (q->x, q->velocity, q->angle);
702 newy = get_newy (q->y, q->velocity, q->angle);
703 switch (next_hit (newx, newy))
705 case EMPTIED:
706 q->x = newx;
707 q->y = newy;
708 break;
709 case FILLED:
711 const int a = q->angle;
712 q->angle =
713 ((a&(DIR_UU|DIR_U))
714 ? (line_check_up (newx, newy) ? ((a&(DIR_UU|DIR_U))>>4)
715 : (a&(DIR_UU|DIR_U)))
716 : 0)
718 ((a&(DIR_RR|DIR_R))
719 ? (line_check_rt (newx, newy) ? ((a&(DIR_RR|DIR_R))>>4)
720 : (a&(DIR_RR|DIR_R)))
721 : 0)
723 ((a&(DIR_DD|DIR_D))
724 ? (line_check_dn (newx, newy) ? ((a&(DIR_DD|DIR_D))<<4)
725 : (a&(DIR_DD|DIR_D)))
726 : 0)
728 ((a&(DIR_LL|DIR_L))
729 ? (line_check_lt (newx, newy) ? ((a&(DIR_LL|DIR_L))<<4)
730 : (a&(DIR_LL|DIR_L)))
731 : 0);
732 q->x = get_newx (q->x, q->velocity, q->angle);
733 q->y = get_newy (q->y, q->velocity, q->angle);
734 break;
736 case TRAIL:
737 die();
738 break;
742 /* move the board forward timewise */
743 static inline void move_board (void)
745 int j, newi, newj;
747 for (j = 0; j < player.level + STARTING_QIXES; j++)
748 move_qix (&qixes[j]);
749 /* move_qix(&black_qix,true); */
750 if (player.move) {
751 newi = player.i;
752 newj = player.j;
753 switch (player.move) {
754 case MOVE_UP:
755 if (player.j > 1)
756 newj--;
757 break;
758 case MOVE_DN:
759 if (player.j < BOARD_H - 2)
760 newj++;
761 break;
762 case MOVE_LT:
763 if (player.i > 0)
764 newi--;
765 break;
766 case MOVE_RT:
767 if (player.i < BOARD_W - 1)
768 newi++;
769 break;
770 default:
771 break;
774 if ((player.drawing) && (board[newj][newi] == EMPTIED)) /* continue drawing */
775 board[newj][newi] = TRAIL;
776 else if ((player.drawing) && (board[newj][newi] == FILLED)) { /* finish drawing */
777 player.move = MOVE_NO; /* stop moving */
778 player.drawing = false;
779 complete_trail (true);
780 } else if ((board[player.j][player.i] == FILLED)
781 && (board[newj][newi] == EMPTIED)) {
782 /* start drawing */
783 player.drawing = true;
784 board[newj][newi] = TRAIL;
785 /* if the block after next is empty and we're moving onto filled, stop */
786 } else if ((board[newj][newi] == FILLED)
787 && (board[newj + newj-player.j][newi + newi-player.i] == EMPTIED)) {
788 player.move = MOVE_NO;
790 player.i = newi;
791 player.j = newj;
793 if (percentage_cache >= difficulty) { /* finished level */
794 rb->splash (HZ * 2, "Level %d finished", player.level+1);
795 player.score += percentage_cache;
796 if (player.level < MAX_LEVEL)
797 player.level++;
798 init_board ();
799 refresh_board ();
800 rb->splash (HZ * 2, "Ready?");
804 /* the main menu */
805 static int game_menu (void)
807 MENUITEM_STRINGLIST(menu, "XOBOX Menu", NULL, "Start New Game",
808 "Speed","Difficulty","Quit");
809 int selection = 0;
810 #ifdef HAVE_LCD_COLOR
811 rb->lcd_set_foreground (rb->global_settings->fg_color);
812 rb->lcd_set_background (rb->global_settings->bg_color);
813 #elif LCD_DEPTH>=2
814 rb->lcd_set_foreground(LCD_BLACK);
815 rb->lcd_set_background(LCD_WHITE);
816 #endif
817 for (;;) {
818 rb->do_menu(&menu,&selection);
819 if (selection==1)
820 rb->set_int ("Speed", "", UNIT_INT, &speed, NULL, 1, 1, 10, NULL);
821 else if (selection==2)
822 rb->set_int ("Difficulty", "", UNIT_INT, &difficulty, NULL,
823 5, 50, 95, NULL);
824 else
825 break;
827 if (selection != MENU_START) {
828 selection = MENU_QUIT;
830 return selection;
833 /* init game's variables */
834 static void init_game (void)
836 player.level = 0;
837 player.score = 0;
838 player.lives = 3;
839 player.gameover = false;
840 player.drawing = false;
841 rb->lcd_setfont(FONT_SYSFIXED);
842 init_board ();
843 refresh_board ();
844 rb->splash (HZ * 2, "Ready?");
847 /* general keypad handler loop */
848 static int xobox_loop (void)
850 int button = 0, ret;
851 bool pause = false;
852 int end;
854 while (!quit) {
855 end = *rb->current_tick + ((11-speed)*HZ)/100;
857 #ifdef HAS_BUTTON_HOLD
858 if (rb->button_hold()) {
859 pause = true;
860 rb->splash (HZ, "PAUSED");
862 #endif
864 button = rb->button_get_w_tmo (true);
865 switch (button) {
866 case UP:
867 case UP|BUTTON_REPEAT:
868 player.move = MOVE_UP;
869 break;
870 case DOWN:
871 case DOWN|BUTTON_REPEAT:
872 player.move = MOVE_DN;
873 break;
874 case LEFT:
875 case LEFT|BUTTON_REPEAT:
876 player.move = MOVE_LT;
877 break;
878 case RIGHT:
879 case RIGHT|BUTTON_REPEAT:
880 player.move = MOVE_RT;
881 break;
882 case PAUSE:
883 pause = !pause;
884 if (pause)
885 rb->splash (HZ, "Paused");
886 break;
887 case QUIT:
888 ret = game_menu ();
889 if (ret == MENU_START)
890 init_game ();
891 else
893 quit = true;
894 continue;
896 break;
897 default:
898 if (rb->default_event_handler (button) == SYS_USB_CONNECTED)
899 return PLUGIN_USB_CONNECTED;
900 break;
902 if (!pause) {
903 move_board ();
904 refresh_board ();
906 if (player.gameover) {
907 rb->splash (HZ, "Game Over!");
908 ret = game_menu ();
909 if (ret == MENU_START)
910 init_game ();
911 else
912 quit = true;
915 if (end > *rb->current_tick)
916 rb->sleep (end - *rb->current_tick);
917 else
918 rb->yield ();
920 } /* end while */
921 return PLUGIN_OK; /* for no warnings on compiling */
924 /* plugin main procedure */
925 enum plugin_status plugin_start (struct plugin_api *api, void *parameter)
927 int ret = PLUGIN_OK;
929 (void) parameter;
930 rb = api;
932 rb->lcd_setfont (FONT_SYSFIXED);
933 #if LCD_DEPTH>=2
934 rb->lcd_set_backdrop(NULL);
935 #endif
937 /* Turn off backlight timeout */
938 backlight_force_on(rb); /* backlight control in lib/helper.c */
940 quit = false;
942 randomize ();
943 if (game_menu () == MENU_START) {
944 init_game ();
945 ret = xobox_loop ();
948 /* Turn on backlight timeout (revert to settings) */
949 backlight_use_settings(rb); /* backlight control in lib/helper.c */
950 rb->lcd_setfont (FONT_UI);
952 return ret;