fixing red:
[Rockbox.git] / apps / plugins / xobox.c
blob879d0e65f75cb4bf40ea7e1194271e8187dcf893
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 #elif (CONFIG_KEYPAD == GIGABEAT_S_PAD)
117 #define QUIT BUTTON_BACK
118 #define LEFT BUTTON_LEFT
119 #define RIGHT BUTTON_RIGHT
120 #define UP BUTTON_UP
121 #define DOWN BUTTON_DOWN
122 #define PAUSE BUTTON_PLAY
124 #elif (CONFIG_KEYPAD == MROBE100_PAD)
126 #define QUIT BUTTON_POWER
127 #define LEFT BUTTON_LEFT
128 #define RIGHT BUTTON_RIGHT
129 #define UP BUTTON_UP
130 #define DOWN BUTTON_DOWN
131 #define PAUSE BUTTON_DISPLAY
133 #elif CONFIG_KEYPAD == IAUDIO_M3_PAD
135 #define QUIT BUTTON_RC_REC
136 #define LEFT BUTTON_RC_REW
137 #define RIGHT BUTTON_RC_FF
138 #define UP BUTTON_RC_VOL_UP
139 #define DOWN BUTTON_RC_VOL_DOWN
140 #define PAUSE BUTTON_RC_PLAY
142 #elif CONFIG_KEYPAD == COWOND2_PAD
144 #define QUIT BUTTON_POWER
145 #define LEFT BUTTON_LEFT
146 #define RIGHT BUTTON_RIGHT
147 #define UP BUTTON_UP
148 #define DOWN BUTTON_DOWN
149 #define PAUSE BUTTON_SELECT
151 #else
152 #error No keymap defined!
153 #endif
155 #define MOVE_NO 0 /* player movement */
156 #define MOVE_UP 1 /* 1 */
157 #define MOVE_DN 2 /* 3 0 4 */
158 #define MOVE_LT 3 /* 2 */
159 #define MOVE_RT 4
161 /* ball movement (12 ways) */
162 /* UUL UR */
163 /* UL UR */
164 /* ULL . URR */
165 /* DLL DRR */
166 /* DL DR */
167 /* DDL DDR */
169 #define DIR_UU (1<<7)
170 #define DIR_U (1<<6)
171 #define DIR_RR (1<<5)
172 #define DIR_R (1<<4)
173 #define DIR_DD (1<<3)
174 #define DIR_D (1<<2)
175 #define DIR_LL (1<<1)
176 #define DIR_L (1<<0)
178 #define MOVE_UUR ( DIR_UU | DIR_R )
179 #define MOVE_UR ( DIR_U | DIR_R )
180 #define MOVE_URR ( DIR_U | DIR_RR )
181 #define MOVE_DRR ( DIR_D | DIR_RR )
182 #define MOVE_DR ( DIR_D | DIR_R )
183 #define MOVE_DDR ( DIR_DD | DIR_R )
184 #define MOVE_DDL ( DIR_DD | DIR_L )
185 #define MOVE_DL ( DIR_D | DIR_L )
186 #define MOVE_DLL ( DIR_D | DIR_LL )
187 #define MOVE_ULL ( DIR_U | DIR_LL )
188 #define MOVE_UL ( DIR_U | DIR_L )
189 #define MOVE_UUL ( DIR_UU | DIR_L )
191 #if (LCD_WIDTH>112) && (LCD_HEIGHT>64)
192 # define CUBE_SIZE 8 /* 8x22=176 */
193 # define pos(a) ((a)>>3)
194 #else
195 # define CUBE_SIZE 4
196 # define pos(a) ((a)>>2)
197 #endif
199 #define STARTING_QIXES 2
200 #define MAX_LEVEL 10
201 #define MAX_QIXES MAX_LEVEL+STARTING_QIXES
202 #define BOARD_W ((int)(LCD_WIDTH/CUBE_SIZE))
203 #define BOARD_H ((int)(LCD_HEIGHT/CUBE_SIZE))
204 #define BOARD_X (LCD_WIDTH-BOARD_W*CUBE_SIZE)/2
205 #define BOARD_Y (LCD_HEIGHT-BOARD_H*CUBE_SIZE)/2
207 #ifdef HAVE_LCD_COLOR
208 #define CLR_RED LCD_RGBPACK(255,0,0) /* used to imply danger */
209 #define CLR_LTBLUE LCD_RGBPACK(125, 145, 180) /* used for frame and filling */
210 #define PLR_COL LCD_WHITE /* color used for the player */
211 #elif LCD_DEPTH>=2
212 #define CLR_RED LCD_DARKGRAY /* used to imply danger */
213 #define CLR_LTBLUE LCD_LIGHTGRAY /* used for frame and filling */
214 #define PLR_COL LCD_BLACK /* color used for the player */
215 #endif
217 #if LCD_DEPTH>=2
218 #define EMPTIED LCD_BLACK /* empty spot */
219 #define FILLED CLR_LTBLUE /* filled spot */
220 #define TRAIL CLR_RED /* the red trail of the player */
221 #define QIX LCD_WHITE
222 #else
223 #define EMPTIED 0
224 #define FILLED 1
225 #define TRAIL 2
226 #define QIX 3
227 #endif
228 #define UNCHECKED 0
229 #define CHECKED 1
230 #define PAINTED -1
231 #define PIC_QIX 0
232 #define PIC_PLAYER 1
234 #define MENU_START 0
235 #define MENU_QUIT 1
237 /* The time (in ms) for one iteration through the game loop - decrease this
238 to speed up the game - note that current_tick is (currently) only accurate
239 to 10ms.
241 static int speed = 6; /* CYCLETIME = (11-speed)*10 ms */
242 static int difficulty = 75; /* Percentage of screen that needs to be filled
243 * in order to win the game */
245 static struct plugin_api *rb;
247 MEM_FUNCTION_WRAPPERS(rb);
249 static bool quit = false;
251 static unsigned int board[BOARD_H][BOARD_W];
252 static int testboard[BOARD_H][BOARD_W];
254 #if CUBE_SIZE == 8
256 00011000 0x18 - 11100111 0xe7
257 00111100 0x3c - 11100111 0xe7
258 01111110 0x7e - 11000011 0xc3
259 11111111 0xff - 00000000 0x00
260 11111111 0xff - 00000000 0x00
261 01111110 0x7e - 11000011 0xc3
262 00111100 0x3c - 11100111 0xe7
263 00011000 0x18 - 11100111 0xe7
265 const unsigned char pics[2][8] = {
266 {0x18, 0x3c, 0x7e, 0xff, 0xff, 0x7e, 0x3c, 0x18}, /* Alien (QIX) */
267 {0xe7, 0xe7, 0xc3, 0x00, 0x00, 0xc3, 0xe7, 0xe7} /* Player (XONIX) */
269 #elif CUBE_SIZE == 4
271 0110 0x6 - 1001 0x9
272 1111 0xf - 0110 0x6
273 1111 0xf - 0110 0x6
274 0110 0x6 - 1001 0x9
276 const unsigned char pics[2][4] = {
277 {0x6, 0xf, 0xf, 0x6}, /* Alien (QIX) */
278 {0x9, 0x6, 0x6, 0x9} /* Player (XONIX) */
280 #else
281 #error Incorrect CUBE_SIZE value.
282 #endif
284 static struct qix
286 int velocity; /* velocity */
287 int x, y; /* position on screen */
288 int angle; /* angle */
289 } qixes[MAX_QIXES]; /* black_qix */
291 static struct splayer
293 int i, j; /* position on board */
294 int move, score, level, lives;
295 bool drawing;
296 bool gameover;
297 } player;
299 static int percentage_cache;
301 /*************************** STACK STUFF **********************/
303 /* the stack */
304 #define STACK_SIZE (2*BOARD_W*BOARD_H)
305 static struct pos
307 int x, y; /* position on board */
308 } stack[STACK_SIZE];
309 static int stackPointer;
311 static inline bool pop (struct pos *p)
313 if (stackPointer > 0) {
314 p->x = stack[stackPointer].x;
315 p->y = stack[stackPointer].y;
316 stackPointer--;
317 return true;
318 } else
319 return false; /* SE */
322 static inline bool push (struct pos *p)
324 if (stackPointer < STACK_SIZE - 1) {
325 stackPointer++;
326 stack[stackPointer].x = p->x;
327 stack[stackPointer].y = p->y;
328 return true;
329 } else
330 return false; /* SOF */
333 static inline void emptyStack (void)
335 stackPointer = 0;
338 /*********************** END OF STACK STUFF *********************/
340 /* calculate the new x coordinate of the ball according to angle and speed */
341 static inline int get_newx (int x, int len, int deg)
343 if (deg & DIR_R)
344 return x + len;
345 else if (deg & DIR_L)
346 return x - len;
347 else if (deg & DIR_RR)
348 return x + len * 2;
349 else /* (def & DIR_LL) */
350 return x - len * 2;
353 /* calculate the new y coordinate of the ball according to angle and speed */
354 static inline int get_newy (int y, int len, int deg)
356 if (deg & DIR_D)
357 return y + len;
358 else if (deg & DIR_U)
359 return y - len;
360 else if (deg & DIR_DD)
361 return y + len * 2;
362 else /* (deg & DIR_UU) */
363 return y - len * 2;
366 /* make random function get it's value from the device ticker */
367 static inline void randomize (void)
369 rb->srand (*rb->current_tick);
372 /* get a random number between 0 and range-1 */
373 static int t_rand (int range)
375 return rb->rand () % range;
378 /* initializes the test help board */
379 static void init_testboard (void)
381 int j; /* testboard */
382 for (j = 0; j < BOARD_H; j++)
383 /* UNCHEKED == (int)0 */
384 rb->memset( testboard[j], 0, BOARD_W * sizeof( int ) );
387 /* initializes the game board on with the player,qix's and black qix */
388 static void init_board (void)
390 int i, j;
391 for (j = 0; j < BOARD_H; j++)
392 for (i = 0; i < BOARD_W; i++) { /* make a nice cyan frame */
393 if ((i == 0) || (j <= 1) || (i == BOARD_W - 1)
394 || (j >= BOARD_H - 2))
395 board[j][i] = FILLED;
396 else
397 board[j][i] = EMPTIED;
400 /* (level+2) is the number of qixes */
401 for (j = 0; j < player.level + STARTING_QIXES; j++) {
402 qixes[j].velocity = t_rand (2) + 1; /* 1 or 2 pix-per-sec */
404 /* not on frame */
405 qixes[j].x = CUBE_SIZE*2 + 2*t_rand (((BOARD_W-4)*CUBE_SIZE)/2);
406 qixes[j].y = CUBE_SIZE*2 + 2*t_rand (((BOARD_H-4)*CUBE_SIZE)/2);
408 const int angle_table[] = {
409 MOVE_UUR, MOVE_UR, MOVE_URR, MOVE_DRR, MOVE_DR, MOVE_DDR,
410 MOVE_UUL, MOVE_UL, MOVE_ULL, MOVE_DLL, MOVE_DL, MOVE_DDL };
411 qixes[j].angle = angle_table[t_rand (12)];
412 #if CUBE_SIZE == 4
413 /* Work arround a nasty bug. FIXME */
414 if( qixes[j].angle & (DIR_LL|DIR_RR|DIR_UU|DIR_DD) )
415 qixes[j].velocity = 1;
416 #endif
418 /*black_qix.velocity=1;
419 black_qix.x=BOARD_X+(BOARD_W*CUBE_SIZE)/2-CUBE_SIZE/2;
420 black_qix.y=BOARD_Y+(BOARD_H*CUBE_SIZE)-CUBE_SIZE-CUBE_SIZE/2;
421 black_qix.angle=MOVE_UR; */
422 player.move = MOVE_NO;
423 player.drawing = false;
424 player.i = BOARD_W / 2;
425 player.j = 1;
427 percentage_cache = 0;
430 /* calculates the percentage of the screen filling */
431 static int percentage (void)
433 int i, j, filled = 0;
434 for (j = 2; j < BOARD_H - 2; j++)
435 for (i = 1; i < BOARD_W - 1; i++)
436 if (board[j][i] == FILLED)
437 filled++;
438 return (filled * 100) / ((BOARD_W - 2) * (BOARD_H - 4));
441 /* draw the board on with all the game figures */
442 static void refresh_board (void)
444 int i, j;
445 char str[25];
447 #if LCD_DEPTH>=2
448 rb->lcd_set_background (LCD_BLACK);
449 #else
450 rb->lcd_clear_display ();
451 #endif
452 for (j = 0; j < BOARD_H; j++)
454 unsigned last_color = board[j][0];
455 int last_i = 0;
456 for (i = 1; i < BOARD_W; i++) {
457 if( last_color != board[j][i] )
459 #if LCD_DEPTH>=2
460 rb->lcd_set_foreground (last_color);
461 #else
462 if (last_color != EMPTIED)
463 #endif
464 rb->lcd_fillrect (BOARD_X + CUBE_SIZE * (last_i),
465 BOARD_Y + CUBE_SIZE * j,
466 CUBE_SIZE * (i - last_i), CUBE_SIZE );
467 last_color = board[j][i];
468 last_i = i;
471 #if LCD_DEPTH>=2
472 rb->lcd_set_foreground (last_color);
473 #else
474 if (last_color != EMPTIED)
475 #endif
476 rb->lcd_fillrect (BOARD_X + CUBE_SIZE * (last_i),
477 BOARD_Y + CUBE_SIZE * j,
478 CUBE_SIZE * (i - last_i), CUBE_SIZE);
481 #if LCD_DEPTH>=2
482 rb->lcd_set_foreground (LCD_BLACK);
483 rb->lcd_set_background (CLR_LTBLUE);
484 #else
485 rb->lcd_set_drawmode (DRMODE_COMPLEMENT);
486 #endif
487 rb->snprintf (str, sizeof (str), "Level %d", player.level + 1);
488 rb->lcd_putsxy (BOARD_X, BOARD_Y, str);
489 rb->snprintf (str, sizeof (str), "%d%%", percentage_cache);
490 rb->lcd_putsxy (BOARD_X + CUBE_SIZE * BOARD_W - 24, BOARD_Y, str);
491 rb->snprintf (str, sizeof (str), "Score: %d", player.score);
492 rb->lcd_putsxy (BOARD_X, BOARD_Y + CUBE_SIZE * BOARD_H - 8, str);
493 rb->snprintf (str, sizeof (str), "%d Lives", player.lives);
494 #if LCD_DEPTH>=2
495 rb->lcd_putsxy (BOARD_X + CUBE_SIZE * BOARD_W - 60,
496 BOARD_Y + CUBE_SIZE * BOARD_H - 8, str);
497 #else
498 rb->lcd_putsxy (BOARD_X + CUBE_SIZE * BOARD_W - 40,
499 BOARD_Y + CUBE_SIZE * BOARD_H - 8, str);
500 #endif
502 #if LCD_DEPTH>=2
503 rb->lcd_set_foreground (PLR_COL);
504 rb->lcd_set_background (board[player.j][player.i]);
505 #endif
506 rb->lcd_mono_bitmap (pics[PIC_PLAYER], player.i * CUBE_SIZE + BOARD_X,
507 player.j * CUBE_SIZE + BOARD_Y, CUBE_SIZE, CUBE_SIZE);
509 #if LCD_DEPTH>=2
510 rb->lcd_set_background (EMPTIED);
511 rb->lcd_set_foreground (LCD_WHITE);
512 rb->lcd_set_drawmode (DRMODE_FG);
513 #else
514 rb->lcd_set_drawmode (DRMODE_FG);
515 #endif
516 for (j = 0; j < player.level + STARTING_QIXES; j++)
517 rb->lcd_mono_bitmap (pics[PIC_QIX], qixes[j].x + BOARD_X,
518 qixes[j].y + BOARD_Y, CUBE_SIZE, CUBE_SIZE);
519 #if LCD_DEPTH>=2
520 rb->lcd_set_foreground (LCD_BLACK);
521 #endif
522 rb->lcd_set_drawmode (DRMODE_SOLID);
524 rb->lcd_update ();
527 static inline int infested_area (int i, int j, int v)
529 struct pos p;
530 p.x = i;
531 p.y = j;
532 emptyStack ();
533 if (!push (&p))
534 return -1;
535 while (pop (&p)) {
536 if (testboard[p.y][p.x] == v) continue;
537 if (testboard[p.y][p.x] > UNCHECKED)
538 return 1; /* This area was previously flagged as infested */
539 testboard[p.y][p.x] = v;
540 if (board[p.y][p.x] == QIX)
541 return 1; /* Infested area */
543 struct pos p1 = { p.x+1, p.y };
544 if ((p1.x < BOARD_W)
545 && (board[p1.y][p1.x] != FILLED)
546 && (!push (&p1)))
547 return -1;
550 struct pos p1 = { p.x-1, p.y };
551 if ((p1.x >= 0)
552 && (board[p1.y][p1.x] != FILLED)
553 && (!push (&p1)))
554 return -1;
557 struct pos p1 = { p.x, p.y+1 };
558 if ((p1.y < BOARD_H)
559 && (board[p1.y][p1.x] != FILLED)
560 && (!push (&p1)))
561 return -1;
564 struct pos p1 = { p.x, p.y-1 };
565 if ((p1.y >= 0)
566 && (board[p1.y][p1.x] != FILLED)
567 && (!push (&p1)))
568 return -1;
571 return 0;
574 static inline int fill_area (int i, int j)
576 struct pos p;
577 p.x = i;
578 p.y = j;
579 int v = testboard[p.y][p.x];
580 emptyStack ();
581 if (!push (&p))
582 return -1;
583 while (pop (&p)) {
584 board[p.y][p.x] = FILLED;
585 testboard[p.y][p.x] = PAINTED;
587 struct pos p1 = { p.x+1, p.y };
588 if ((p1.x < BOARD_W)
589 && (testboard[p1.y][p1.x] == v)
590 && (!push (&p1)))
591 return -1;
594 struct pos p1 = { p.x-1, p.y };
595 if ((p1.x >= 0)
596 && (testboard[p1.y][p1.x] == v)
597 && (!push (&p1)))
598 return -1;
601 struct pos p1 = { p.x, p.y+1 };
602 if ((p1.y < BOARD_H)
603 && (testboard[p1.y][p1.x] == v)
604 && (!push (&p1)))
605 return -1;
608 struct pos p1 = { p.x, p.y-1 };
609 if ((p1.y >= 0)
610 && (testboard[p1.y][p1.x] == v)
611 && (!push (&p1)))
612 return -1;
615 return 0;
619 /* take care of stuff after xonix has landed on a filled spot */
620 static void complete_trail (int fill)
622 int i, j, ret;
623 for (j = 0; j < BOARD_H; j++) {
624 for (i = 0; i < BOARD_W; i++) {
625 if (board[j][i] == TRAIL) {
626 if (fill)
627 board[j][i] = FILLED;
628 else
629 board[j][i] = EMPTIED;
634 if (fill) {
635 int v = CHECKED;
636 for (i = 0; i < player.level + STARTING_QIXES; i++) /* add qixes to board */
637 board[pos(qixes[i].y - BOARD_Y)]
638 [pos(qixes[i].x - BOARD_X)] = QIX;
640 init_testboard();
641 for (j = 1; j < BOARD_H - 1; j++) {
642 for (i = 0; i < BOARD_W - 0; i++) {
643 if (board[j][i] != FILLED) {
644 ret = infested_area (i, j, v);
645 if (ret < 0 || ( ret == 0 && fill_area (i, j) ) )
646 quit = true;
647 v++;
652 for (i = 0; i < player.level + STARTING_QIXES; i++) /* add qixes to board */
653 board[pos(qixes[i].y - BOARD_Y)]
654 [pos(qixes[i].x - BOARD_X)] = EMPTIED;
655 percentage_cache = percentage();
658 rb->button_clear_queue();
661 /* returns the color the real pixel(x,y) on the lcd is pointing at */
662 static inline unsigned int getpixel (int x, int y)
664 const int a = pos (x - BOARD_X), b = pos (y - BOARD_Y);
665 if ((a > 0) && (a < BOARD_W) && (b > 0) && (b < BOARD_H)) /* if inside board */
666 return board[b][a];
667 else
668 return FILLED;
671 /* returns the color the ball on (newx,newy) is heading at *----*
672 checks the four edge points of the square if 1st of all | |
673 are a trail (cause it's a lose life situation) and 2nd | |
674 if it's filled so it needs to bounce. *____*
676 static inline unsigned int next_hit (int newx, int newy)
678 if ((getpixel (newx, newy) == TRAIL)
679 || (getpixel (newx, newy + CUBE_SIZE - 1) == TRAIL)
680 || (getpixel (newx + CUBE_SIZE - 1, newy) == TRAIL)
681 || (getpixel (newx + CUBE_SIZE - 1, newy + CUBE_SIZE - 1) == TRAIL))
682 return TRAIL;
683 else if ((getpixel (newx, newy) == FILLED)
684 || (getpixel (newx, newy + CUBE_SIZE - 1) == FILLED)
685 || (getpixel (newx + CUBE_SIZE - 1, newy) == FILLED)
686 || (getpixel (newx + CUBE_SIZE - 1, newy + CUBE_SIZE - 1) ==
687 FILLED))
688 return FILLED;
689 else
690 return EMPTIED;
693 static void die (void)
695 player.lives--;
696 if (player.lives == 0)
697 player.gameover = true;
698 else {
699 refresh_board ();
700 rb->splash (HZ, "Crash!");
701 complete_trail (false);
702 player.move = MOVE_NO;
703 player.drawing = false;
704 player.i = BOARD_W / 2;
705 player.j = 1;
709 /* returns true if the (side) of the block -***-
710 starting from (newx,newy) has any filled pixels * *
711 -***-
713 static inline bool line_check_lt (int newx, int newy)
715 return getpixel (newx, newy + CUBE_SIZE/2-1) == FILLED
716 && getpixel (newx, newy + CUBE_SIZE/2 ) == FILLED;
718 static inline bool line_check_rt (int newx, int newy)
720 return getpixel (newx + CUBE_SIZE-1, newy + CUBE_SIZE/2-1) == FILLED
721 && getpixel (newx + CUBE_SIZE-1, newy + CUBE_SIZE/2 ) == FILLED;
723 static inline bool line_check_up (int newx, int newy)
725 return getpixel (newx + CUBE_SIZE/2-1, newy) == FILLED
726 && getpixel (newx + CUBE_SIZE/2 , newy) == FILLED;
728 static inline bool line_check_dn (int newx, int newy)
730 return getpixel (newx + CUBE_SIZE/2-1, newy + CUBE_SIZE-1) == FILLED
731 && getpixel (newx + CUBE_SIZE/2 , newy + CUBE_SIZE-1) == FILLED;
734 static inline void move_qix (struct qix *q)
736 int newx, newy;
737 newx = get_newx (q->x, q->velocity, q->angle);
738 newy = get_newy (q->y, q->velocity, q->angle);
739 switch (next_hit (newx, newy))
741 case EMPTIED:
742 q->x = newx;
743 q->y = newy;
744 break;
745 case FILLED:
747 const int a = q->angle;
748 q->angle =
749 ((a&(DIR_UU|DIR_U))
750 ? (line_check_up (newx, newy) ? ((a&(DIR_UU|DIR_U))>>4)
751 : (a&(DIR_UU|DIR_U)))
752 : 0)
754 ((a&(DIR_RR|DIR_R))
755 ? (line_check_rt (newx, newy) ? ((a&(DIR_RR|DIR_R))>>4)
756 : (a&(DIR_RR|DIR_R)))
757 : 0)
759 ((a&(DIR_DD|DIR_D))
760 ? (line_check_dn (newx, newy) ? ((a&(DIR_DD|DIR_D))<<4)
761 : (a&(DIR_DD|DIR_D)))
762 : 0)
764 ((a&(DIR_LL|DIR_L))
765 ? (line_check_lt (newx, newy) ? ((a&(DIR_LL|DIR_L))<<4)
766 : (a&(DIR_LL|DIR_L)))
767 : 0);
768 q->x = get_newx (q->x, q->velocity, q->angle);
769 q->y = get_newy (q->y, q->velocity, q->angle);
770 break;
772 case TRAIL:
773 die();
774 break;
778 /* move the board forward timewise */
779 static inline void move_board (void)
781 int j, newi, newj;
783 for (j = 0; j < player.level + STARTING_QIXES; j++)
784 move_qix (&qixes[j]);
785 /* move_qix(&black_qix,true); */
786 if (player.move) {
787 newi = player.i;
788 newj = player.j;
789 switch (player.move) {
790 case MOVE_UP:
791 if (player.j > 1)
792 newj--;
793 break;
794 case MOVE_DN:
795 if (player.j < BOARD_H - 2)
796 newj++;
797 break;
798 case MOVE_LT:
799 if (player.i > 0)
800 newi--;
801 break;
802 case MOVE_RT:
803 if (player.i < BOARD_W - 1)
804 newi++;
805 break;
806 default:
807 break;
810 if ((player.drawing) && (board[newj][newi] == EMPTIED)) /* continue drawing */
811 board[newj][newi] = TRAIL;
812 else if ((player.drawing) && (board[newj][newi] == FILLED)) { /* finish drawing */
813 player.move = MOVE_NO; /* stop moving */
814 player.drawing = false;
815 complete_trail (true);
816 } else if ((board[player.j][player.i] == FILLED)
817 && (board[newj][newi] == EMPTIED)) {
818 /* start drawing */
819 player.drawing = true;
820 board[newj][newi] = TRAIL;
821 /* if the block after next is empty and we're moving onto filled, stop */
822 } else if ((board[newj][newi] == FILLED)
823 && (board[newj + newj-player.j][newi + newi-player.i] == EMPTIED)) {
824 player.move = MOVE_NO;
826 player.i = newi;
827 player.j = newj;
829 if (percentage_cache >= difficulty) { /* finished level */
830 rb->splash (HZ * 2, "Level %d finished", player.level+1);
831 player.score += percentage_cache;
832 if (player.level < MAX_LEVEL)
833 player.level++;
834 init_board ();
835 refresh_board ();
836 rb->splash (HZ * 2, "Ready?");
840 /* the main menu */
841 static int game_menu (void)
843 MENUITEM_STRINGLIST(menu, "XOBOX Menu", NULL, "Start New Game",
844 "Speed","Difficulty","Quit");
845 int selection = 0;
846 #ifdef HAVE_LCD_COLOR
847 rb->lcd_set_foreground (rb->global_settings->fg_color);
848 rb->lcd_set_background (rb->global_settings->bg_color);
849 #elif LCD_DEPTH>=2
850 rb->lcd_set_foreground(LCD_BLACK);
851 rb->lcd_set_background(LCD_WHITE);
852 #endif
853 for (;;) {
854 rb->do_menu(&menu,&selection, NULL, false);
855 if (selection==1)
856 rb->set_int ("Speed", "", UNIT_INT, &speed, NULL, 1, 1, 10, NULL);
857 else if (selection==2)
858 rb->set_int ("Difficulty", "", UNIT_INT, &difficulty, NULL,
859 5, 50, 95, NULL);
860 else
861 break;
863 if (selection != MENU_START) {
864 selection = MENU_QUIT;
866 return selection;
869 /* init game's variables */
870 static void init_game (void)
872 player.level = 0;
873 player.score = 0;
874 player.lives = 3;
875 player.gameover = false;
876 player.drawing = false;
877 rb->lcd_setfont(FONT_SYSFIXED);
878 init_board ();
879 refresh_board ();
880 rb->splash (HZ * 2, "Ready?");
883 /* general keypad handler loop */
884 static int xobox_loop (void)
886 int button = 0, ret;
887 bool pause = false;
888 int end;
890 while (!quit) {
891 end = *rb->current_tick + ((11-speed)*HZ)/100;
893 #ifdef HAS_BUTTON_HOLD
894 if (rb->button_hold()) {
895 pause = true;
896 rb->splash (HZ, "PAUSED");
898 #endif
900 button = rb->button_get_w_tmo (true);
901 switch (button) {
902 case UP:
903 case UP|BUTTON_REPEAT:
904 player.move = MOVE_UP;
905 break;
906 case DOWN:
907 case DOWN|BUTTON_REPEAT:
908 player.move = MOVE_DN;
909 break;
910 case LEFT:
911 case LEFT|BUTTON_REPEAT:
912 player.move = MOVE_LT;
913 break;
914 case RIGHT:
915 case RIGHT|BUTTON_REPEAT:
916 player.move = MOVE_RT;
917 break;
918 case PAUSE:
919 pause = !pause;
920 if (pause)
921 rb->splash (HZ, "Paused");
922 break;
923 case QUIT:
924 ret = game_menu ();
925 if (ret == MENU_START)
926 init_game ();
927 else
929 quit = true;
930 continue;
932 break;
933 default:
934 if (rb->default_event_handler (button) == SYS_USB_CONNECTED)
935 return PLUGIN_USB_CONNECTED;
936 break;
938 if (!pause) {
939 move_board ();
940 refresh_board ();
942 if (player.gameover) {
943 rb->splash (HZ, "Game Over!");
944 ret = game_menu ();
945 if (ret == MENU_START)
946 init_game ();
947 else
948 quit = true;
951 if (end > *rb->current_tick)
952 rb->sleep (end - *rb->current_tick);
953 else
954 rb->yield ();
956 } /* end while */
957 return PLUGIN_OK; /* for no warnings on compiling */
960 /* plugin main procedure */
961 enum plugin_status plugin_start (struct plugin_api *api, void *parameter)
963 int ret = PLUGIN_OK;
965 (void) parameter;
966 rb = api;
968 rb->lcd_setfont (FONT_SYSFIXED);
969 #if LCD_DEPTH>=2
970 rb->lcd_set_backdrop(NULL);
971 #endif
973 /* Turn off backlight timeout */
974 backlight_force_on(rb); /* backlight control in lib/helper.c */
976 quit = false;
978 randomize ();
979 if (game_menu () == MENU_START) {
980 init_game ();
981 ret = xobox_loop ();
984 /* Turn on backlight timeout (revert to settings) */
985 backlight_use_settings(rb); /* backlight control in lib/helper.c */
986 rb->lcd_setfont (FONT_UI);
988 return ret;