Reverting parts of r19760 that was mistakenly committed.
[kugel-rb.git] / apps / plugins / xobox.c
blob3cf66637d179ea61c3475363c18f6e58bedc1dea
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 "lib/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 == SANSA_CLIP_PAD) || \
91 (CONFIG_KEYPAD == SANSA_FUZE_PAD)
93 #define QUIT BUTTON_POWER
94 #define LEFT BUTTON_LEFT
95 #define RIGHT BUTTON_RIGHT
96 #define UP BUTTON_UP
97 #define DOWN BUTTON_DOWN
98 #define PAUSE BUTTON_HOME
100 #elif (CONFIG_KEYPAD == SANSA_M200_PAD)
102 #define QUIT BUTTON_POWER
103 #define LEFT BUTTON_LEFT
104 #define RIGHT BUTTON_RIGHT
105 #define UP BUTTON_UP
106 #define DOWN BUTTON_DOWN
107 #define PAUSE BUTTON_SELECT
109 #elif CONFIG_KEYPAD == IRIVER_H10_PAD
111 #define QUIT BUTTON_POWER
112 #define LEFT BUTTON_LEFT
113 #define RIGHT BUTTON_RIGHT
114 #define UP BUTTON_SCROLL_UP
115 #define DOWN BUTTON_SCROLL_DOWN
116 #define PAUSE BUTTON_PLAY
118 #elif CONFIG_KEYPAD == RECORDER_PAD
120 #define QUIT BUTTON_OFF
121 #define LEFT BUTTON_LEFT
122 #define RIGHT BUTTON_RIGHT
123 #define DOWN BUTTON_DOWN
124 #define UP BUTTON_UP
125 #define PAUSE BUTTON_PLAY
127 #elif CONFIG_KEYPAD == ONDIO_PAD
129 #define QUIT BUTTON_OFF
130 #define LEFT BUTTON_LEFT
131 #define RIGHT BUTTON_RIGHT
132 #define DOWN BUTTON_DOWN
133 #define UP BUTTON_UP
134 #define PAUSE BUTTON_MENU
136 #elif (CONFIG_KEYPAD == GIGABEAT_S_PAD)
138 #define QUIT BUTTON_BACK
139 #define LEFT BUTTON_LEFT
140 #define RIGHT BUTTON_RIGHT
141 #define UP BUTTON_UP
142 #define DOWN BUTTON_DOWN
143 #define PAUSE BUTTON_PLAY
145 #elif (CONFIG_KEYPAD == MROBE100_PAD)
147 #define QUIT BUTTON_POWER
148 #define LEFT BUTTON_LEFT
149 #define RIGHT BUTTON_RIGHT
150 #define UP BUTTON_UP
151 #define DOWN BUTTON_DOWN
152 #define PAUSE BUTTON_DISPLAY
154 #elif CONFIG_KEYPAD == IAUDIO_M3_PAD
156 #define QUIT BUTTON_RC_REC
157 #define LEFT BUTTON_RC_REW
158 #define RIGHT BUTTON_RC_FF
159 #define UP BUTTON_RC_VOL_UP
160 #define DOWN BUTTON_RC_VOL_DOWN
161 #define PAUSE BUTTON_RC_PLAY
163 #elif CONFIG_KEYPAD == COWOND2_PAD
165 #define QUIT BUTTON_POWER
167 #elif CONFIG_KEYPAD == IAUDIO67_PAD
169 #define QUIT BUTTON_POWER
170 #define LEFT BUTTON_LEFT
171 #define RIGHT BUTTON_RIGHT
172 #define UP BUTTON_STOP
173 #define DOWN BUTTON_PLAY
174 #define PAUSE BUTTON_MENU
176 #elif CONFIG_KEYPAD == CREATIVEZVM_PAD
178 #define QUIT BUTTON_BACK
179 #define LEFT BUTTON_LEFT
180 #define RIGHT BUTTON_RIGHT
181 #define UP BUTTON_UP
182 #define DOWN BUTTON_DOWN
183 #define PAUSE BUTTON_PLAY
185 #else
186 #error No keymap defined!
187 #endif
189 #ifdef HAVE_TOUCHSCREEN
190 #ifndef QUIT
191 #define QUIT BUTTON_TOPLEFT
192 #endif
193 #ifndef LEFT
194 #define LEFT BUTTON_MIDLEFT
195 #endif
196 #ifndef RIGHT
197 #define RIGHT BUTTON_MIDRIGHT
198 #endif
199 #ifndef UP
200 #define UP BUTTON_TOPMIDDLE
201 #endif
202 #ifndef DOWN
203 #define DOWN BUTTON_BOTTOMMIDDLE
204 #endif
205 #ifndef PAUSE
206 #define PAUSE BUTTON_CENTER
207 #endif
208 #endif
210 #define MOVE_NO 0 /* player movement */
211 #define MOVE_UP 1 /* 1 */
212 #define MOVE_DN 2 /* 3 0 4 */
213 #define MOVE_LT 3 /* 2 */
214 #define MOVE_RT 4
216 /* ball movement (12 ways) */
217 /* UUL UR */
218 /* UL UR */
219 /* ULL . URR */
220 /* DLL DRR */
221 /* DL DR */
222 /* DDL DDR */
224 #define DIR_UU (1<<7)
225 #define DIR_U (1<<6)
226 #define DIR_RR (1<<5)
227 #define DIR_R (1<<4)
228 #define DIR_DD (1<<3)
229 #define DIR_D (1<<2)
230 #define DIR_LL (1<<1)
231 #define DIR_L (1<<0)
233 #define MOVE_UUR ( DIR_UU | DIR_R )
234 #define MOVE_UR ( DIR_U | DIR_R )
235 #define MOVE_URR ( DIR_U | DIR_RR )
236 #define MOVE_DRR ( DIR_D | DIR_RR )
237 #define MOVE_DR ( DIR_D | DIR_R )
238 #define MOVE_DDR ( DIR_DD | DIR_R )
239 #define MOVE_DDL ( DIR_DD | DIR_L )
240 #define MOVE_DL ( DIR_D | DIR_L )
241 #define MOVE_DLL ( DIR_D | DIR_LL )
242 #define MOVE_ULL ( DIR_U | DIR_LL )
243 #define MOVE_UL ( DIR_U | DIR_L )
244 #define MOVE_UUL ( DIR_UU | DIR_L )
246 #if (LCD_WIDTH>112) && (LCD_HEIGHT>64)
247 # define CUBE_SIZE 8 /* 8x22=176 */
248 # define pos(a) ((a)>>3)
249 #else
250 # define CUBE_SIZE 4
251 # define pos(a) ((a)>>2)
252 #endif
254 #define STARTING_QIXES 2
255 #define MAX_LEVEL 10
256 #define MAX_QIXES MAX_LEVEL+STARTING_QIXES
257 #define BOARD_W ((int)(LCD_WIDTH/CUBE_SIZE))
258 #define BOARD_H ((int)(LCD_HEIGHT/CUBE_SIZE))
259 #define BOARD_X (LCD_WIDTH-BOARD_W*CUBE_SIZE)/2
260 #define BOARD_Y (LCD_HEIGHT-BOARD_H*CUBE_SIZE)/2
262 #ifdef HAVE_LCD_COLOR
263 #define CLR_RED LCD_RGBPACK(255,0,0) /* used to imply danger */
264 #define CLR_LTBLUE LCD_RGBPACK(125, 145, 180) /* used for frame and filling */
265 #define PLR_COL LCD_WHITE /* color used for the player */
266 #elif LCD_DEPTH>=2
267 #define CLR_RED LCD_DARKGRAY /* used to imply danger */
268 #define CLR_LTBLUE LCD_LIGHTGRAY /* used for frame and filling */
269 #define PLR_COL LCD_BLACK /* color used for the player */
270 #endif
272 #if LCD_DEPTH>=2
273 #define EMPTIED LCD_BLACK /* empty spot */
274 #define FILLED CLR_LTBLUE /* filled spot */
275 #define TRAIL CLR_RED /* the red trail of the player */
276 #define QIX LCD_WHITE
277 #else
278 #define EMPTIED 0
279 #define FILLED 1
280 #define TRAIL 2
281 #define QIX 3
282 #endif
283 #define UNCHECKED 0
284 #define CHECKED 1
285 #define PAINTED -1
286 #define PIC_QIX 0
287 #define PIC_PLAYER 1
289 #define MENU_START 0
290 #define MENU_QUIT 1
292 /* The time (in ms) for one iteration through the game loop - decrease this
293 to speed up the game - note that current_tick is (currently) only accurate
294 to 10ms.
296 static int speed = 6; /* CYCLETIME = (11-speed)*10 ms */
297 static int difficulty = 75; /* Percentage of screen that needs to be filled
298 * in order to win the game */
300 static const struct plugin_api *rb;
302 MEM_FUNCTION_WRAPPERS(rb);
304 static bool quit = false;
306 static unsigned int board[BOARD_H][BOARD_W];
307 static int testboard[BOARD_H][BOARD_W];
309 #if CUBE_SIZE == 8
311 00011000 0x18 - 11100111 0xe7
312 00111100 0x3c - 11100111 0xe7
313 01111110 0x7e - 11000011 0xc3
314 11111111 0xff - 00000000 0x00
315 11111111 0xff - 00000000 0x00
316 01111110 0x7e - 11000011 0xc3
317 00111100 0x3c - 11100111 0xe7
318 00011000 0x18 - 11100111 0xe7
320 const unsigned char pics[2][8] = {
321 {0x18, 0x3c, 0x7e, 0xff, 0xff, 0x7e, 0x3c, 0x18}, /* Alien (QIX) */
322 {0xe7, 0xe7, 0xc3, 0x00, 0x00, 0xc3, 0xe7, 0xe7} /* Player (XONIX) */
324 #elif CUBE_SIZE == 4
326 0110 0x6 - 1001 0x9
327 1111 0xf - 0110 0x6
328 1111 0xf - 0110 0x6
329 0110 0x6 - 1001 0x9
331 const unsigned char pics[2][4] = {
332 {0x6, 0xf, 0xf, 0x6}, /* Alien (QIX) */
333 {0x9, 0x6, 0x6, 0x9} /* Player (XONIX) */
335 #else
336 #error Incorrect CUBE_SIZE value.
337 #endif
339 static struct qix
341 int velocity; /* velocity */
342 int x, y; /* position on screen */
343 int angle; /* angle */
344 } qixes[MAX_QIXES]; /* black_qix */
346 static struct splayer
348 int i, j; /* position on board */
349 int move, score, level, lives;
350 bool drawing;
351 bool gameover;
352 } player;
354 static int percentage_cache;
356 /*************************** STACK STUFF **********************/
358 /* the stack */
359 #define STACK_SIZE (2*BOARD_W*BOARD_H)
360 static struct pos
362 int x, y; /* position on board */
363 } stack[STACK_SIZE];
364 static int stackPointer;
366 static inline bool pop (struct pos *p)
368 if (stackPointer > 0) {
369 p->x = stack[stackPointer].x;
370 p->y = stack[stackPointer].y;
371 stackPointer--;
372 return true;
373 } else
374 return false; /* SE */
377 static inline bool push (struct pos *p)
379 if (stackPointer < STACK_SIZE - 1) {
380 stackPointer++;
381 stack[stackPointer].x = p->x;
382 stack[stackPointer].y = p->y;
383 return true;
384 } else
385 return false; /* SOF */
388 static inline void emptyStack (void)
390 stackPointer = 0;
393 /*********************** END OF STACK STUFF *********************/
395 /* calculate the new x coordinate of the ball according to angle and speed */
396 static inline int get_newx (int x, int len, int deg)
398 if (deg & DIR_R)
399 return x + len;
400 else if (deg & DIR_L)
401 return x - len;
402 else if (deg & DIR_RR)
403 return x + len * 2;
404 else /* (def & DIR_LL) */
405 return x - len * 2;
408 /* calculate the new y coordinate of the ball according to angle and speed */
409 static inline int get_newy (int y, int len, int deg)
411 if (deg & DIR_D)
412 return y + len;
413 else if (deg & DIR_U)
414 return y - len;
415 else if (deg & DIR_DD)
416 return y + len * 2;
417 else /* (deg & DIR_UU) */
418 return y - len * 2;
421 /* make random function get it's value from the device ticker */
422 static inline void randomize (void)
424 rb->srand (*rb->current_tick);
427 /* get a random number between 0 and range-1 */
428 static int t_rand (int range)
430 return rb->rand () % range;
433 /* initializes the test help board */
434 static void init_testboard (void)
436 int j; /* testboard */
437 for (j = 0; j < BOARD_H; j++)
438 /* UNCHEKED == (int)0 */
439 rb->memset( testboard[j], 0, BOARD_W * sizeof( int ) );
442 /* initializes the game board on with the player,qix's and black qix */
443 static void init_board (void)
445 int i, j;
446 for (j = 0; j < BOARD_H; j++)
447 for (i = 0; i < BOARD_W; i++) { /* make a nice cyan frame */
448 if ((i == 0) || (j <= 1) || (i == BOARD_W - 1)
449 || (j >= BOARD_H - 2))
450 board[j][i] = FILLED;
451 else
452 board[j][i] = EMPTIED;
455 /* (level+2) is the number of qixes */
456 for (j = 0; j < player.level + STARTING_QIXES; j++) {
457 qixes[j].velocity = t_rand (2) + 1; /* 1 or 2 pix-per-sec */
459 /* not on frame */
460 qixes[j].x = CUBE_SIZE*2 + 2*t_rand (((BOARD_W-4)*CUBE_SIZE)/2);
461 qixes[j].y = CUBE_SIZE*2 + 2*t_rand (((BOARD_H-4)*CUBE_SIZE)/2);
463 const int angle_table[] = {
464 MOVE_UUR, MOVE_UR, MOVE_URR, MOVE_DRR, MOVE_DR, MOVE_DDR,
465 MOVE_UUL, MOVE_UL, MOVE_ULL, MOVE_DLL, MOVE_DL, MOVE_DDL };
466 qixes[j].angle = angle_table[t_rand (12)];
467 #if CUBE_SIZE == 4
468 /* Work arround a nasty bug. FIXME */
469 if( qixes[j].angle & (DIR_LL|DIR_RR|DIR_UU|DIR_DD) )
470 qixes[j].velocity = 1;
471 #endif
473 /*black_qix.velocity=1;
474 black_qix.x=BOARD_X+(BOARD_W*CUBE_SIZE)/2-CUBE_SIZE/2;
475 black_qix.y=BOARD_Y+(BOARD_H*CUBE_SIZE)-CUBE_SIZE-CUBE_SIZE/2;
476 black_qix.angle=MOVE_UR; */
477 player.move = MOVE_NO;
478 player.drawing = false;
479 player.i = BOARD_W / 2;
480 player.j = 1;
482 percentage_cache = 0;
485 /* calculates the percentage of the screen filling */
486 static int percentage (void)
488 int i, j, filled = 0;
489 for (j = 2; j < BOARD_H - 2; j++)
490 for (i = 1; i < BOARD_W - 1; i++)
491 if (board[j][i] == FILLED)
492 filled++;
493 return (filled * 100) / ((BOARD_W - 2) * (BOARD_H - 4));
496 /* draw the board on with all the game figures */
497 static void refresh_board (void)
499 int i, j;
500 char str[25];
502 #if LCD_DEPTH>=2
503 rb->lcd_set_background (LCD_BLACK);
504 #else
505 rb->lcd_clear_display ();
506 #endif
507 for (j = 0; j < BOARD_H; j++)
509 unsigned last_color = board[j][0];
510 int last_i = 0;
511 for (i = 1; i < BOARD_W; i++) {
512 if( last_color != board[j][i] )
514 #if LCD_DEPTH>=2
515 rb->lcd_set_foreground (last_color);
516 #else
517 if (last_color != EMPTIED)
518 #endif
519 rb->lcd_fillrect (BOARD_X + CUBE_SIZE * (last_i),
520 BOARD_Y + CUBE_SIZE * j,
521 CUBE_SIZE * (i - last_i), CUBE_SIZE );
522 last_color = board[j][i];
523 last_i = i;
526 #if LCD_DEPTH>=2
527 rb->lcd_set_foreground (last_color);
528 #else
529 if (last_color != EMPTIED)
530 #endif
531 rb->lcd_fillrect (BOARD_X + CUBE_SIZE * (last_i),
532 BOARD_Y + CUBE_SIZE * j,
533 CUBE_SIZE * (i - last_i), CUBE_SIZE);
536 #if LCD_DEPTH>=2
537 rb->lcd_set_foreground (LCD_BLACK);
538 rb->lcd_set_background (CLR_LTBLUE);
539 #else
540 rb->lcd_set_drawmode (DRMODE_COMPLEMENT);
541 #endif
542 rb->snprintf (str, sizeof (str), "Level %d", player.level + 1);
543 rb->lcd_putsxy (BOARD_X, BOARD_Y, str);
544 rb->snprintf (str, sizeof (str), "%d%%", percentage_cache);
545 rb->lcd_putsxy (BOARD_X + CUBE_SIZE * BOARD_W - 24, BOARD_Y, str);
546 rb->snprintf (str, sizeof (str), "Score: %d", player.score);
547 rb->lcd_putsxy (BOARD_X, BOARD_Y + CUBE_SIZE * BOARD_H - 8, str);
548 rb->snprintf (str, sizeof (str),
549 (player.lives != 1) ? "%d Lives" : "%d Life", player.lives);
550 #if LCD_DEPTH>=2
551 rb->lcd_putsxy (BOARD_X + CUBE_SIZE * BOARD_W - 60,
552 BOARD_Y + CUBE_SIZE * BOARD_H - 8, str);
553 #else
554 rb->lcd_putsxy (BOARD_X + CUBE_SIZE * BOARD_W - 40,
555 BOARD_Y + CUBE_SIZE * BOARD_H - 8, str);
556 #endif
558 #if LCD_DEPTH>=2
559 rb->lcd_set_foreground (PLR_COL);
560 rb->lcd_set_background (board[player.j][player.i]);
561 #endif
562 rb->lcd_mono_bitmap (pics[PIC_PLAYER], player.i * CUBE_SIZE + BOARD_X,
563 player.j * CUBE_SIZE + BOARD_Y, CUBE_SIZE, CUBE_SIZE);
565 #if LCD_DEPTH>=2
566 rb->lcd_set_background (EMPTIED);
567 rb->lcd_set_foreground (LCD_WHITE);
568 rb->lcd_set_drawmode (DRMODE_FG);
569 #else
570 rb->lcd_set_drawmode (DRMODE_FG);
571 #endif
572 for (j = 0; j < player.level + STARTING_QIXES; j++)
573 rb->lcd_mono_bitmap (pics[PIC_QIX], qixes[j].x + BOARD_X,
574 qixes[j].y + BOARD_Y, CUBE_SIZE, CUBE_SIZE);
575 #if LCD_DEPTH>=2
576 rb->lcd_set_foreground (LCD_BLACK);
577 #endif
578 rb->lcd_set_drawmode (DRMODE_SOLID);
580 rb->lcd_update ();
583 static inline int infested_area (int i, int j, int v)
585 struct pos p;
586 p.x = i;
587 p.y = j;
588 emptyStack ();
589 if (!push (&p))
590 return -1;
591 while (pop (&p)) {
592 if (testboard[p.y][p.x] == v) continue;
593 if (testboard[p.y][p.x] > UNCHECKED)
594 return 1; /* This area was previously flagged as infested */
595 testboard[p.y][p.x] = v;
596 if (board[p.y][p.x] == QIX)
597 return 1; /* Infested area */
599 struct pos p1 = { p.x+1, p.y };
600 if ((p1.x < BOARD_W)
601 && (board[p1.y][p1.x] != FILLED)
602 && (!push (&p1)))
603 return -1;
606 struct pos p1 = { p.x-1, p.y };
607 if ((p1.x >= 0)
608 && (board[p1.y][p1.x] != FILLED)
609 && (!push (&p1)))
610 return -1;
613 struct pos p1 = { p.x, p.y+1 };
614 if ((p1.y < BOARD_H)
615 && (board[p1.y][p1.x] != FILLED)
616 && (!push (&p1)))
617 return -1;
620 struct pos p1 = { p.x, p.y-1 };
621 if ((p1.y >= 0)
622 && (board[p1.y][p1.x] != FILLED)
623 && (!push (&p1)))
624 return -1;
627 return 0;
630 static inline int fill_area (int i, int j)
632 struct pos p;
633 p.x = i;
634 p.y = j;
635 int v = testboard[p.y][p.x];
636 emptyStack ();
637 if (!push (&p))
638 return -1;
639 while (pop (&p)) {
640 board[p.y][p.x] = FILLED;
641 testboard[p.y][p.x] = PAINTED;
643 struct pos p1 = { p.x+1, p.y };
644 if ((p1.x < BOARD_W)
645 && (testboard[p1.y][p1.x] == v)
646 && (!push (&p1)))
647 return -1;
650 struct pos p1 = { p.x-1, p.y };
651 if ((p1.x >= 0)
652 && (testboard[p1.y][p1.x] == v)
653 && (!push (&p1)))
654 return -1;
657 struct pos p1 = { p.x, p.y+1 };
658 if ((p1.y < BOARD_H)
659 && (testboard[p1.y][p1.x] == v)
660 && (!push (&p1)))
661 return -1;
664 struct pos p1 = { p.x, p.y-1 };
665 if ((p1.y >= 0)
666 && (testboard[p1.y][p1.x] == v)
667 && (!push (&p1)))
668 return -1;
671 return 0;
675 /* take care of stuff after xonix has landed on a filled spot */
676 static void complete_trail (int fill)
678 int i, j, ret;
679 for (j = 0; j < BOARD_H; j++) {
680 for (i = 0; i < BOARD_W; i++) {
681 if (board[j][i] == TRAIL) {
682 if (fill)
683 board[j][i] = FILLED;
684 else
685 board[j][i] = EMPTIED;
690 if (fill) {
691 int v = CHECKED;
692 for (i = 0; i < player.level + STARTING_QIXES; i++) /* add qixes to board */
693 board[pos(qixes[i].y - BOARD_Y)]
694 [pos(qixes[i].x - BOARD_X)] = QIX;
696 init_testboard();
697 for (j = 1; j < BOARD_H - 1; j++) {
698 for (i = 0; i < BOARD_W - 0; i++) {
699 if (board[j][i] != FILLED) {
700 ret = infested_area (i, j, v);
701 if (ret < 0 || ( ret == 0 && fill_area (i, j) ) )
702 quit = true;
703 v++;
708 for (i = 0; i < player.level + STARTING_QIXES; i++) /* add qixes to board */
709 board[pos(qixes[i].y - BOARD_Y)]
710 [pos(qixes[i].x - BOARD_X)] = EMPTIED;
711 percentage_cache = percentage();
714 rb->button_clear_queue();
717 /* returns the color the real pixel(x,y) on the lcd is pointing at */
718 static inline unsigned int getpixel (int x, int y)
720 const int a = pos (x - BOARD_X), b = pos (y - BOARD_Y);
721 if ((a > 0) && (a < BOARD_W) && (b > 0) && (b < BOARD_H)) /* if inside board */
722 return board[b][a];
723 else
724 return FILLED;
727 /* returns the color the ball on (newx,newy) is heading at *----*
728 checks the four edge points of the square if 1st of all | |
729 are a trail (cause it's a lose life situation) and 2nd | |
730 if it's filled so it needs to bounce. *____*
732 static inline unsigned int next_hit (int newx, int newy)
734 if ((getpixel (newx, newy) == TRAIL)
735 || (getpixel (newx, newy + CUBE_SIZE - 1) == TRAIL)
736 || (getpixel (newx + CUBE_SIZE - 1, newy) == TRAIL)
737 || (getpixel (newx + CUBE_SIZE - 1, newy + CUBE_SIZE - 1) == TRAIL))
738 return TRAIL;
739 else if ((getpixel (newx, newy) == FILLED)
740 || (getpixel (newx, newy + CUBE_SIZE - 1) == FILLED)
741 || (getpixel (newx + CUBE_SIZE - 1, newy) == FILLED)
742 || (getpixel (newx + CUBE_SIZE - 1, newy + CUBE_SIZE - 1) ==
743 FILLED))
744 return FILLED;
745 else
746 return EMPTIED;
749 static void die (void)
751 player.lives--;
752 if (player.lives == 0)
753 player.gameover = true;
754 else {
755 refresh_board ();
756 rb->splash (HZ, "Crash!");
757 complete_trail (false);
758 player.move = MOVE_NO;
759 player.drawing = false;
760 player.i = BOARD_W / 2;
761 player.j = 1;
765 /* returns true if the (side) of the block -***-
766 starting from (newx,newy) has any filled pixels * *
767 -***-
769 static inline bool line_check_lt (int newx, int newy)
771 return getpixel (newx, newy + CUBE_SIZE/2-1) == FILLED
772 && getpixel (newx, newy + CUBE_SIZE/2 ) == FILLED;
774 static inline bool line_check_rt (int newx, int newy)
776 return getpixel (newx + CUBE_SIZE-1, newy + CUBE_SIZE/2-1) == FILLED
777 && getpixel (newx + CUBE_SIZE-1, newy + CUBE_SIZE/2 ) == FILLED;
779 static inline bool line_check_up (int newx, int newy)
781 return getpixel (newx + CUBE_SIZE/2-1, newy) == FILLED
782 && getpixel (newx + CUBE_SIZE/2 , newy) == FILLED;
784 static inline bool line_check_dn (int newx, int newy)
786 return getpixel (newx + CUBE_SIZE/2-1, newy + CUBE_SIZE-1) == FILLED
787 && getpixel (newx + CUBE_SIZE/2 , newy + CUBE_SIZE-1) == FILLED;
790 static inline void move_qix (struct qix *q)
792 int newx, newy;
793 newx = get_newx (q->x, q->velocity, q->angle);
794 newy = get_newy (q->y, q->velocity, q->angle);
795 switch (next_hit (newx, newy))
797 case EMPTIED:
798 q->x = newx;
799 q->y = newy;
800 break;
801 case FILLED:
803 const int a = q->angle;
804 q->angle =
805 ((a&(DIR_UU|DIR_U))
806 ? (line_check_up (newx, newy) ? ((a&(DIR_UU|DIR_U))>>4)
807 : (a&(DIR_UU|DIR_U)))
808 : 0)
810 ((a&(DIR_RR|DIR_R))
811 ? (line_check_rt (newx, newy) ? ((a&(DIR_RR|DIR_R))>>4)
812 : (a&(DIR_RR|DIR_R)))
813 : 0)
815 ((a&(DIR_DD|DIR_D))
816 ? (line_check_dn (newx, newy) ? ((a&(DIR_DD|DIR_D))<<4)
817 : (a&(DIR_DD|DIR_D)))
818 : 0)
820 ((a&(DIR_LL|DIR_L))
821 ? (line_check_lt (newx, newy) ? ((a&(DIR_LL|DIR_L))<<4)
822 : (a&(DIR_LL|DIR_L)))
823 : 0);
824 q->x = get_newx (q->x, q->velocity, q->angle);
825 q->y = get_newy (q->y, q->velocity, q->angle);
826 break;
828 case TRAIL:
829 die();
830 break;
834 /* move the board forward timewise */
835 static inline void move_board (void)
837 int j, newi, newj;
839 for (j = 0; j < player.level + STARTING_QIXES; j++)
840 move_qix (&qixes[j]);
841 /* move_qix(&black_qix,true); */
842 if (player.move) {
843 newi = player.i;
844 newj = player.j;
845 switch (player.move) {
846 case MOVE_UP:
847 if (player.j > 1)
848 newj--;
849 break;
850 case MOVE_DN:
851 if (player.j < BOARD_H - 2)
852 newj++;
853 break;
854 case MOVE_LT:
855 if (player.i > 0)
856 newi--;
857 break;
858 case MOVE_RT:
859 if (player.i < BOARD_W - 1)
860 newi++;
861 break;
862 default:
863 break;
866 if ((player.drawing) && (board[newj][newi] == EMPTIED)) /* continue drawing */
867 board[newj][newi] = TRAIL;
868 else if ((player.drawing) && (board[newj][newi] == FILLED)) { /* finish drawing */
869 player.move = MOVE_NO; /* stop moving */
870 player.drawing = false;
871 complete_trail (true);
872 } else if ((board[player.j][player.i] == FILLED)
873 && (board[newj][newi] == EMPTIED)) {
874 /* start drawing */
875 player.drawing = true;
876 board[newj][newi] = TRAIL;
877 /* if the block after next is empty and we're moving onto filled, stop */
878 } else if ((board[newj][newi] == FILLED)
879 && (board[newj + newj-player.j][newi + newi-player.i] == EMPTIED)) {
880 player.move = MOVE_NO;
882 player.i = newi;
883 player.j = newj;
885 if (percentage_cache >= difficulty) { /* finished level */
886 rb->splashf (HZ * 2, "Level %d finished", player.level+1);
887 player.score += percentage_cache;
888 if (player.level < MAX_LEVEL)
889 player.level++;
890 init_board ();
891 refresh_board ();
892 rb->splash (HZ * 2, "Ready?");
896 /* the main menu */
897 static int game_menu (void)
899 MENUITEM_STRINGLIST(menu, "XOBOX Menu", NULL, "Start New Game",
900 "Speed","Difficulty","Quit");
901 int selection = 0;
902 #ifdef HAVE_LCD_COLOR
903 rb->lcd_set_foreground (rb->global_settings->fg_color);
904 rb->lcd_set_background (rb->global_settings->bg_color);
905 #elif LCD_DEPTH>=2
906 rb->lcd_set_foreground(LCD_BLACK);
907 rb->lcd_set_background(LCD_WHITE);
908 #endif
909 for (;;) {
910 rb->do_menu(&menu,&selection, NULL, false);
911 if (selection==1)
912 rb->set_int ("Speed", "", UNIT_INT, &speed, NULL, 1, 1, 10, NULL);
913 else if (selection==2)
914 rb->set_int ("Difficulty", "", UNIT_INT, &difficulty, NULL,
915 5, 50, 95, NULL);
916 else
917 break;
919 if (selection != MENU_START) {
920 selection = MENU_QUIT;
922 return selection;
925 /* init game's variables */
926 static void init_game (void)
928 player.level = 0;
929 player.score = 0;
930 player.lives = 3;
931 player.gameover = false;
932 player.drawing = false;
933 rb->lcd_setfont(FONT_SYSFIXED);
934 init_board ();
935 refresh_board ();
936 rb->splash (HZ * 2, "Ready?");
939 /* general keypad handler loop */
940 static int xobox_loop (void)
942 int button = 0, ret;
943 bool pause = false;
944 int end;
946 while (!quit) {
947 end = *rb->current_tick + ((11-speed)*HZ)/100;
949 #ifdef HAS_BUTTON_HOLD
950 if (rb->button_hold()) {
951 pause = true;
952 rb->splash (HZ, "PAUSED");
954 #endif
956 button = rb->button_get_w_tmo (1);
957 switch (button) {
958 case UP:
959 case UP|BUTTON_REPEAT:
960 player.move = MOVE_UP;
961 break;
962 case DOWN:
963 case DOWN|BUTTON_REPEAT:
964 player.move = MOVE_DN;
965 break;
966 case LEFT:
967 case LEFT|BUTTON_REPEAT:
968 player.move = MOVE_LT;
969 break;
970 case RIGHT:
971 case RIGHT|BUTTON_REPEAT:
972 player.move = MOVE_RT;
973 break;
974 case PAUSE:
975 pause = !pause;
976 if (pause)
977 rb->splash (HZ, "Paused");
978 break;
979 case QUIT:
980 ret = game_menu ();
981 if (ret == MENU_START)
982 init_game ();
983 else
985 quit = true;
986 continue;
988 break;
989 default:
990 if (rb->default_event_handler (button) == SYS_USB_CONNECTED)
991 return PLUGIN_USB_CONNECTED;
992 break;
994 if (!pause) {
995 move_board ();
996 refresh_board ();
998 if (player.gameover) {
999 rb->splash (HZ, "Game Over!");
1000 ret = game_menu ();
1001 if (ret == MENU_START)
1002 init_game ();
1003 else
1004 quit = true;
1007 if (end > *rb->current_tick)
1008 rb->sleep (end - *rb->current_tick);
1009 else
1010 rb->yield ();
1012 } /* end while */
1013 return PLUGIN_OK; /* for no warnings on compiling */
1016 /* plugin main procedure */
1017 enum plugin_status plugin_start (const struct plugin_api *api, const void *parameter)
1019 int ret = PLUGIN_OK;
1021 (void) parameter;
1022 rb = api;
1024 rb->lcd_setfont (FONT_SYSFIXED);
1025 #if LCD_DEPTH>=2
1026 rb->lcd_set_backdrop(NULL);
1027 #endif
1029 /* Turn off backlight timeout */
1030 backlight_force_on(rb); /* backlight control in lib/helper.c */
1032 quit = false;
1034 randomize ();
1035 if (game_menu () == MENU_START) {
1036 init_game ();
1037 ret = xobox_loop ();
1040 /* Turn on backlight timeout (revert to settings) */
1041 backlight_use_settings(rb); /* backlight control in lib/helper.c */
1042 rb->lcd_setfont (FONT_UI);
1044 return ret;