diff options
author | Ben Basha <benbasha@rockbox.org> | 2006-03-12 09:35:53 +0000 |
---|---|---|
committer | Ben Basha <benbasha@rockbox.org> | 2006-03-12 09:35:53 +0000 |
commit | b7a96707bc462c493ff04cce08b2c971eb92f7dc (patch) | |
tree | 4b5ab2a3ace539dc8641f9ee76209c416a487233 /apps | |
parent | a3cfdef7c8464c15c2c26fb7a80c77b82632645e (diff) | |
download | rockbox-b7a96707bc462c493ff04cce08b2c971eb92f7dc.tar.gz rockbox-b7a96707bc462c493ff04cce08b2c971eb92f7dc.zip |
New plugin - Xobox - xonix clone by Eli Sherer with changes by me and Mikael Magnusson
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@9011 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps')
-rw-r--r-- | apps/plugins/SOURCES | 5 | ||||
-rw-r--r-- | apps/plugins/xobox.c | 813 |
2 files changed, 818 insertions, 0 deletions
diff --git a/apps/plugins/SOURCES b/apps/plugins/SOURCES index ce6bb33081..7cd2882ab6 100644 --- a/apps/plugins/SOURCES +++ b/apps/plugins/SOURCES | |||
@@ -77,6 +77,11 @@ rockboy.c | |||
77 | #endif | 77 | #endif |
78 | #endif | 78 | #endif |
79 | 79 | ||
80 | /* not support recorder models for now */ | ||
81 | #if (LCD_WIDTH > 112) && (LCD_HEIGHT > 64) | ||
82 | xobox.c | ||
83 | #endif | ||
84 | |||
80 | #endif /* HAVE_LCD_BITMAP */ | 85 | #endif /* HAVE_LCD_BITMAP */ |
81 | 86 | ||
82 | #ifdef HAVE_LCD_CHARCELLS /* Player model only */ | 87 | #ifdef HAVE_LCD_CHARCELLS /* Player model only */ |
diff --git a/apps/plugins/xobox.c b/apps/plugins/xobox.c new file mode 100644 index 0000000000..b578ccbf53 --- /dev/null +++ b/apps/plugins/xobox.c | |||
@@ -0,0 +1,813 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2006 Eli Sherer | ||
11 | * | ||
12 | * All files in this archive are subject to the GNU General Public License. | ||
13 | * See the file COPYING in the source tree root for full license agreement. | ||
14 | * | ||
15 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
16 | * KIND, either express or implied. | ||
17 | * | ||
18 | ****************************************************************************/ | ||
19 | |||
20 | #include "plugin.h" | ||
21 | |||
22 | PLUGIN_HEADER | ||
23 | |||
24 | #if (CONFIG_KEYPAD == IRIVER_H100_PAD) || (CONFIG_KEYPAD == IRIVER_H300_PAD) | ||
25 | |||
26 | #define QUIT BUTTON_OFF | ||
27 | #define LEFT BUTTON_LEFT | ||
28 | #define RIGHT BUTTON_RIGHT | ||
29 | #define PAUSE BUTTON_MODE | ||
30 | #define UP BUTTON_UP | ||
31 | #define DOWN BUTTON_DOWN | ||
32 | #define SELECT BUTTON_SELECT | ||
33 | |||
34 | #elif (CONFIG_KEYPAD == IPOD_3G_PAD) || \ | ||
35 | (CONFIG_KEYPAD == IPOD_4G_PAD) | ||
36 | |||
37 | #define QUIT (BUTTON_SELECT | BUTTON_MENU) | ||
38 | #define LEFT BUTTON_LEFT | ||
39 | #define RIGHT BUTTON_RIGHT | ||
40 | #define PAUSE BUTTON_SELECT | ||
41 | #define SELECT BUTTON_SELECT | ||
42 | #define UP BUTTON_MENU | ||
43 | #define DOWN BUTTON_PLAY | ||
44 | |||
45 | #elif CONFIG_KEYPAD == IAUDIO_X5_PAD | ||
46 | |||
47 | #define QUIT BUTTON_POWER | ||
48 | #define LEFT BUTTON_LEFT | ||
49 | #define RIGHT BUTTON_RIGHT | ||
50 | #define SELECT BUTTON_SELECT | ||
51 | #define UP BUTTON_UP | ||
52 | #define DOWN BUTTON_DOWN | ||
53 | #define PAUSE BUTTON_PLAY | ||
54 | |||
55 | #elif (CONFIG_KEYPAD == GIGABEAT_PAD) | ||
56 | |||
57 | #define QUIT BUTTON_A | ||
58 | #define LEFT BUTTON_LEFT | ||
59 | #define RIGHT BUTTON_RIGHT | ||
60 | #define SELECT BUTTON_SELECT | ||
61 | #define UP BUTTON_UP | ||
62 | #define DOWN BUTTON_DOWN | ||
63 | #define PAUSE BUTTON_MENU | ||
64 | |||
65 | #else | ||
66 | #error Unsupported keypad | ||
67 | #endif | ||
68 | |||
69 | #define MOVE_NO 0 /* player movement */ | ||
70 | #define MOVE_UP 1 /* 1 */ | ||
71 | #define MOVE_DN 2 /* 3 0 4 */ | ||
72 | #define MOVE_LT 3 /* 2 */ | ||
73 | #define MOVE_RT 4 | ||
74 | |||
75 | #define MOVE_UUR 0 /* ball movement (12 ways) */ | ||
76 | #define MOVE_UR 1 | ||
77 | #define MOVE_URR 2 | ||
78 | #define MOVE_DRR 3 /* UUL110UUR */ | ||
79 | #define MOVE_DR 4 /* UL10 1UR */ | ||
80 | #define MOVE_DDR 5 /* ULL9 . 2URR */ | ||
81 | #define MOVE_DDL 6 /* DLL8 3DRR */ | ||
82 | #define MOVE_DL 7 /* DL7 4DR */ | ||
83 | #define MOVE_DLL 8 /* DDL6 5DDR */ | ||
84 | #define MOVE_ULL 9 | ||
85 | #define MOVE_UL 10 | ||
86 | #define MOVE_UUL 11 | ||
87 | |||
88 | #define CUBE_SIZE 8 /* 8x22=176 */ | ||
89 | #define STARTING_QIXES 2 | ||
90 | #define MAX_LEVEL 10 | ||
91 | #define MAX_QIXES MAX_LEVEL+STARTING_QIXES | ||
92 | #define BOARD_W ((int)LCD_WIDTH/CUBE_SIZE) | ||
93 | #define BOARD_H ((int)LCD_HEIGHT/CUBE_SIZE) | ||
94 | #define BOARD_X (LCD_WIDTH-BOARD_W*CUBE_SIZE)/2 | ||
95 | #define BOARD_Y (LCD_HEIGHT-BOARD_H*CUBE_SIZE)/2 | ||
96 | |||
97 | #ifdef HAVE_LCD_COLOR | ||
98 | #define CLR_RED LCD_RGBPACK(255,0,0) /* used to imply danger */ | ||
99 | #define CLR_BLUE LCD_RGBPACK(0,0,128) /* used for menu selection */ | ||
100 | #define CLR_CYAN LCD_RGBPACK(0,128,128) /* used for frame and filling */ | ||
101 | #else | ||
102 | #define CLR_RED LCD_DARKGRAY /* used to imply danger */ | ||
103 | #define CLR_BLUE LCD_LIGHTGRAY /* used for menu selection */ | ||
104 | #define CLR_CYAN LCD_LIGHTGRAY /* used for frame and filling */ | ||
105 | #endif | ||
106 | |||
107 | #define EMPTIED LCD_BLACK /* empty spot */ | ||
108 | #define FILLED CLR_CYAN /* filled spot */ | ||
109 | #define TRAIL CLR_RED /* the red trail of the player */ | ||
110 | #define QIX LCD_WHITE | ||
111 | #define UNCHECKED 0 | ||
112 | #define CHECKED 1 | ||
113 | #define PIC_QIX 0 | ||
114 | #define PIC_PLAYER 1 | ||
115 | |||
116 | /* The time (in ms) for one iteration through the game loop - decrease this | ||
117 | to speed up the game - note that current_tick is (currently) only accurate | ||
118 | to 10ms. | ||
119 | */ | ||
120 | #define CYCLETIME 50 | ||
121 | |||
122 | static struct plugin_api *rb; | ||
123 | static bool quit = false; | ||
124 | |||
125 | static unsigned short board[BOARD_H][BOARD_W], | ||
126 | testboard[BOARD_H][BOARD_W], boardcopy[BOARD_H][BOARD_W]; | ||
127 | |||
128 | /* | ||
129 | 00001000 0x08 - 01110111 0x77 | ||
130 | 00011100 0x1c - 01110111 0x77 | ||
131 | 00111110 0x3e - 01100011 0x63 | ||
132 | 01111111 0x7f - 00000000 0x00 | ||
133 | 00111110 0x3e - 01100011 0x63 | ||
134 | 00011100 0x1c - 01110111 0x77 | ||
135 | 00001000 0x08 - 01110111 0x77 | ||
136 | 00000000 0x00 - 00000000 0x00 | ||
137 | */ | ||
138 | const unsigned char pics[2][8] = { | ||
139 | {0x08, 0x1c, 0x3e, 0x7f, 0x3e, 0x1c, 0x08, 0x00}, /* Alien (QIX) */ | ||
140 | {0x77, 0x77, 0x63, 0x00, 0x63, 0x77, 0x77, 0x00} /* Player (XONIX) */ | ||
141 | }; | ||
142 | |||
143 | static struct qix | ||
144 | { | ||
145 | short velocity; /* velocity */ | ||
146 | short x, y; /* position on screen */ | ||
147 | short angle; /* angle */ | ||
148 | } qixes[MAX_QIXES]; /* black_qix */ | ||
149 | |||
150 | static struct splayer | ||
151 | { | ||
152 | short i, j; /* position on board */ | ||
153 | short move, score, level, lives; | ||
154 | bool drawing; | ||
155 | bool gameover; | ||
156 | } player; | ||
157 | |||
158 | /*************************** STACK STUFF **********************/ | ||
159 | |||
160 | /* the stack */ | ||
161 | #define STACK_SIZE BOARD_W*BOARD_H | ||
162 | static struct pos | ||
163 | { | ||
164 | int x, y; /* position on board */ | ||
165 | } stack[STACK_SIZE]; | ||
166 | static int stackPointer; | ||
167 | |||
168 | /* div function (divide two numbers and truncate the answer) */ | ||
169 | static inline int div (int a, int b) | ||
170 | { | ||
171 | return (a/b); | ||
172 | } | ||
173 | |||
174 | static bool pop (struct pos *p) | ||
175 | { | ||
176 | if (stackPointer > 0) { | ||
177 | p->x = stack[stackPointer].x; | ||
178 | p->y = stack[stackPointer].y; | ||
179 | stackPointer--; | ||
180 | return true; | ||
181 | } else | ||
182 | return false; /* SE */ | ||
183 | } | ||
184 | |||
185 | static bool push (struct pos *p) | ||
186 | { | ||
187 | if (stackPointer < STACK_SIZE - 1) { | ||
188 | stackPointer++; | ||
189 | stack[stackPointer].x = p->x; | ||
190 | stack[stackPointer].y = p->y; | ||
191 | return true; | ||
192 | } else | ||
193 | return false; /* SOF */ | ||
194 | } | ||
195 | |||
196 | static void emptyStack (void) | ||
197 | { | ||
198 | stackPointer = 0; | ||
199 | /* int x, y; | ||
200 | while(pop(&x, &y)); */ | ||
201 | } | ||
202 | |||
203 | /*********************** END OF STACK STUFF *********************/ | ||
204 | |||
205 | |||
206 | /* calculate the new x coordinate of the ball according to angle and speed */ | ||
207 | static int get_newx (int x, int len, int deg) | ||
208 | { | ||
209 | int dx; | ||
210 | if ((deg == MOVE_DRR) || (deg == MOVE_URR)) | ||
211 | dx = 2; | ||
212 | else if ((deg == MOVE_DDR) || (deg == MOVE_UUR) || (deg == MOVE_DR) | ||
213 | || (deg == MOVE_UR)) | ||
214 | dx = 1; | ||
215 | else if ((deg == MOVE_DDL) || (deg == MOVE_UUL) || (deg == MOVE_DL) | ||
216 | || (deg == MOVE_UL)) | ||
217 | dx = -1; | ||
218 | else | ||
219 | dx = -2; | ||
220 | return x + dx * len; | ||
221 | } | ||
222 | |||
223 | /* calculate the new y coordinate of the ball according to angle and speed */ | ||
224 | static int get_newy (int y, int len, int deg) | ||
225 | { | ||
226 | int dy; | ||
227 | if ((deg == MOVE_DRR) || (deg == MOVE_DLL) || (deg == MOVE_DR) | ||
228 | || (deg == MOVE_DL)) | ||
229 | dy = 1; | ||
230 | else if ((deg == MOVE_DDR) || (deg == MOVE_DDL)) | ||
231 | dy = 2; | ||
232 | else if ((deg == MOVE_UUR) || (deg == MOVE_UUL)) | ||
233 | dy = -2; | ||
234 | else | ||
235 | dy = -1; | ||
236 | return y + dy * len; | ||
237 | } | ||
238 | |||
239 | /* make random function get it's value from the device ticker */ | ||
240 | static inline void randomize (void) | ||
241 | { | ||
242 | rb->srand (*rb->current_tick); | ||
243 | } | ||
244 | |||
245 | /* get a random number between 0 and range-1 */ | ||
246 | static int t_rand (int range) | ||
247 | { | ||
248 | return rb->rand () % range; | ||
249 | } | ||
250 | |||
251 | /* initializes the test help board */ | ||
252 | static void init_testboard (void) | ||
253 | { | ||
254 | int i, j; /* testboard */ | ||
255 | for (j = 0; j < BOARD_H; j++) | ||
256 | for (i = 0; i < BOARD_W; i++) | ||
257 | testboard[j][i] = UNCHECKED; | ||
258 | } | ||
259 | |||
260 | /* initializes the game board on with the player,qix's and black qix */ | ||
261 | static void init_board (void) | ||
262 | { | ||
263 | int i, j; | ||
264 | for (j = 0; j < BOARD_H; j++) | ||
265 | for (i = 0; i < BOARD_W; i++) { /* make a nice cyan frame */ | ||
266 | if ((i == 0) || (j == 1) || (j == 0) || (i == BOARD_W - 1) | ||
267 | || (j == BOARD_H - 1) || (j == BOARD_H - 2)) | ||
268 | board[j][i] = FILLED; | ||
269 | else | ||
270 | board[j][i] = EMPTIED; | ||
271 | } | ||
272 | /* (level+2) is the number of qixes */ | ||
273 | for (j = 0; j < player.level + STARTING_QIXES; j++) { | ||
274 | qixes[j].velocity = t_rand (2) + 1; /* 1 or 2 pix-per-sec */ | ||
275 | |||
276 | /* not on frame */ | ||
277 | qixes[j].x = | ||
278 | BOARD_X + t_rand (((BOARD_W - 4) * CUBE_SIZE) - 2 * CUBE_SIZE) + | ||
279 | 2 * CUBE_SIZE; | ||
280 | qixes[j].y = | ||
281 | BOARD_Y + t_rand (((BOARD_H - 6) * CUBE_SIZE) - 2 * CUBE_SIZE) + | ||
282 | 3 * CUBE_SIZE; | ||
283 | |||
284 | qixes[j].angle = t_rand (12); | ||
285 | } | ||
286 | /*black_qix.velocity=1; | ||
287 | black_qix.x=BOARD_X+(BOARD_W*CUBE_SIZE)/2-CUBE_SIZE/2; | ||
288 | black_qix.y=BOARD_Y+(BOARD_H*CUBE_SIZE)-CUBE_SIZE-CUBE_SIZE/2; | ||
289 | black_qix.angle=MOVE_UR; */ | ||
290 | player.move = MOVE_NO; | ||
291 | player.drawing = false; | ||
292 | player.i = BOARD_W / 2; | ||
293 | player.j = 1; | ||
294 | } | ||
295 | |||
296 | /* calculates the percentage of the screen filling */ | ||
297 | static int percentage (void) | ||
298 | { | ||
299 | int i, j, filled = 0; | ||
300 | for (j = 2; j < BOARD_H - 2; j++) | ||
301 | for (i = 1; i < BOARD_W - 1; i++) | ||
302 | if (board[j][i] == FILLED) | ||
303 | filled++; | ||
304 | return filled * 100 / ((BOARD_W - 2) * (BOARD_H - 4)); | ||
305 | } | ||
306 | |||
307 | /* draw the board on with all the game figures */ | ||
308 | static void refresh_board (void) | ||
309 | { | ||
310 | int i, j; | ||
311 | char str[25]; | ||
312 | |||
313 | rb->lcd_set_background (LCD_BLACK); | ||
314 | rb->lcd_clear_display (); | ||
315 | for (j = 0; j < BOARD_H; j++) | ||
316 | for (i = 0; i < BOARD_W; i++) { | ||
317 | rb->lcd_set_foreground (board[j][i]); | ||
318 | rb->lcd_fillrect (BOARD_X + CUBE_SIZE * i, BOARD_Y + CUBE_SIZE * j, | ||
319 | CUBE_SIZE, CUBE_SIZE); | ||
320 | } | ||
321 | rb->lcd_set_foreground (LCD_BLACK); | ||
322 | rb->lcd_set_background (CLR_CYAN); | ||
323 | rb->snprintf (str, sizeof (str), "Level %d", player.level + 1); | ||
324 | rb->lcd_putsxy (BOARD_X, BOARD_Y, str); | ||
325 | rb->snprintf (str, sizeof (str), "%d%%", percentage ()); | ||
326 | rb->lcd_putsxy (BOARD_X + CUBE_SIZE * BOARD_W - 24, BOARD_Y, str); | ||
327 | rb->snprintf (str, sizeof (str), "Score: %d", player.score); | ||
328 | rb->lcd_putsxy (BOARD_X, BOARD_Y + CUBE_SIZE * BOARD_H - 8, str); | ||
329 | rb->snprintf (str, sizeof (str), "%d Lives", player.lives); | ||
330 | rb->lcd_putsxy (BOARD_X + CUBE_SIZE * BOARD_W - 60, | ||
331 | BOARD_Y + CUBE_SIZE * BOARD_H - 8, str); | ||
332 | |||
333 | rb->lcd_set_foreground (LCD_WHITE); | ||
334 | rb->lcd_set_background (board[player.j][player.i]); | ||
335 | rb->lcd_mono_bitmap (pics[PIC_PLAYER], player.i * CUBE_SIZE + BOARD_X, | ||
336 | player.j * CUBE_SIZE + BOARD_Y, CUBE_SIZE, CUBE_SIZE); | ||
337 | rb->lcd_set_background (EMPTIED); | ||
338 | for (j = 0; j < player.level + STARTING_QIXES; j++) | ||
339 | rb->lcd_mono_bitmap (pics[PIC_QIX], qixes[j].x + BOARD_X, | ||
340 | qixes[j].y + BOARD_Y, CUBE_SIZE, CUBE_SIZE); | ||
341 | rb->lcd_set_foreground (LCD_BLACK); | ||
342 | |||
343 | rb->lcd_update (); | ||
344 | } | ||
345 | |||
346 | static inline int infested_area (int i, int j) | ||
347 | { | ||
348 | struct pos p, p1, p2, p3, p4; | ||
349 | bool hit = false; | ||
350 | p.x = i; | ||
351 | p.y = j; | ||
352 | emptyStack (); | ||
353 | init_testboard (); | ||
354 | if (!push (&p)) | ||
355 | return -1; | ||
356 | while ((pop (&p)) && (!hit)) { | ||
357 | hit = (boardcopy[p.y][p.x] == QIX); | ||
358 | testboard[p.y][p.x] = CHECKED; | ||
359 | if (hit) | ||
360 | return true; /*save some time and space */ | ||
361 | p1.x = p.x + 1; | ||
362 | p1.y = p.y; | ||
363 | p2.x = p.x - 1; | ||
364 | p2.y = p.y; | ||
365 | p3.x = p.x; | ||
366 | p3.y = p.y + 1; | ||
367 | p4.x = p.x; | ||
368 | p4.y = p.y - 1; | ||
369 | if ((p1.x < BOARD_W) && (p1.x >= 0) && (p1.y < BOARD_H) && (p1.y >= 0) | ||
370 | && (testboard[p1.y][p1.x] == UNCHECKED)) | ||
371 | if (board[p1.y][p1.x] != FILLED) | ||
372 | if (!push (&p1)) | ||
373 | return -1; | ||
374 | if ((p2.x < BOARD_W) && (p2.x >= 0) && (p2.y < BOARD_H) && (p2.y >= 0) | ||
375 | && (testboard[p2.y][p2.x] == UNCHECKED)) | ||
376 | if (board[p2.y][p2.x] != FILLED) | ||
377 | if (!push (&p2)) | ||
378 | return -1; | ||
379 | if ((p3.x < BOARD_W) && (p3.x >= 0) && (p3.y < BOARD_H) && (p3.y >= 0) | ||
380 | && (testboard[p3.y][p3.x] == UNCHECKED)) | ||
381 | if (board[p3.y][p3.x] != FILLED) | ||
382 | if (!push (&p3)) | ||
383 | return -1; | ||
384 | if ((p4.x < BOARD_W) && (p4.x >= 0) && (p4.y < BOARD_H) && (p4.y >= 0) | ||
385 | && (testboard[p4.y][p4.x] == UNCHECKED)) | ||
386 | if (board[p4.y][p4.x] != FILLED) | ||
387 | if (!push (&p4)) | ||
388 | return -1; | ||
389 | } | ||
390 | return (hit ? 1 : 0); | ||
391 | } | ||
392 | |||
393 | static inline int fill_area (int i, int j) | ||
394 | { | ||
395 | struct pos p, p1, p2, p3, p4; | ||
396 | p.x = i; | ||
397 | p.y = j; | ||
398 | emptyStack (); | ||
399 | init_testboard (); | ||
400 | if (!push (&p)) | ||
401 | return -1; | ||
402 | while (pop (&p)) { | ||
403 | board[p.y][p.x] = FILLED; | ||
404 | testboard[p.y][p.x] = CHECKED; | ||
405 | p1.x = p.x + 1; | ||
406 | p1.y = p.y; | ||
407 | p2.x = p.x - 1; | ||
408 | p2.y = p.y; | ||
409 | p3.x = p.x; | ||
410 | p3.y = p.y + 1; | ||
411 | p4.x = p.x; | ||
412 | p4.y = p.y - 1; | ||
413 | if ((p1.x < BOARD_W) && (p1.x >= 0) && (p1.y < BOARD_H) && (p1.y >= 0) | ||
414 | && (testboard[p1.y][p1.x] == UNCHECKED)) | ||
415 | if (board[p1.y][p1.x] == EMPTIED) | ||
416 | if (!push (&p1)) | ||
417 | return -1; | ||
418 | if ((p2.x < BOARD_W) && (p2.x >= 0) && (p2.y < BOARD_H) && (p2.y >= 0) | ||
419 | && (testboard[p2.y][p2.x] == UNCHECKED)) | ||
420 | if (board[p2.y][p2.x] == EMPTIED) | ||
421 | if (!push (&p2)) | ||
422 | return -1; | ||
423 | if ((p3.x < BOARD_W) && (p3.x >= 0) && (p3.y < BOARD_H) && (p3.y >= 0) | ||
424 | && (testboard[p3.y][p3.x] == UNCHECKED)) | ||
425 | if (board[p3.y][p3.x] == EMPTIED) | ||
426 | if (!push (&p3)) | ||
427 | return -1; | ||
428 | if ((p4.x < BOARD_W) && (p4.x >= 0) && (p4.y < BOARD_H) && (p4.y >= 0) | ||
429 | && (testboard[p4.y][p4.x] == UNCHECKED)) | ||
430 | if (board[p4.y][p4.x] == EMPTIED) | ||
431 | if (!push (&p4)) | ||
432 | return -1; | ||
433 | } | ||
434 | return 1; | ||
435 | } | ||
436 | |||
437 | |||
438 | /* take care of stuff after xonix has landed on a filled spot */ | ||
439 | static void complete_trail (void) | ||
440 | { | ||
441 | int i, j, ret; | ||
442 | for (j = 0; j < BOARD_H; j++) | ||
443 | for (i = 0; i < BOARD_W; i++) | ||
444 | if (board[j][i] == TRAIL) | ||
445 | board[j][i] = FILLED; | ||
446 | |||
447 | for (j = 0; j < BOARD_H; j++) | ||
448 | for (i = 0; i < BOARD_W; i++) | ||
449 | boardcopy[j][i] = board[j][i]; | ||
450 | for (i = 0; i < player.level + STARTING_QIXES; i++) /* add qixes to board */ | ||
451 | boardcopy[div (qixes[i].y - BOARD_Y, CUBE_SIZE)][div | ||
452 | (qixes[i].x - BOARD_X, | ||
453 | CUBE_SIZE)] = QIX; | ||
454 | |||
455 | for (j = 1; j < BOARD_H - 1; j++) | ||
456 | for (i = 0; i < BOARD_W - 0; i++) | ||
457 | if (board[j][i] != FILLED) { | ||
458 | ret = infested_area (i, j); | ||
459 | if (ret < 0) | ||
460 | quit = true; | ||
461 | else if (ret == 0) { | ||
462 | ret = fill_area (i, j); | ||
463 | if (ret < 0) | ||
464 | quit = true; | ||
465 | } | ||
466 | } | ||
467 | } | ||
468 | |||
469 | /* returns the color the real pixel(x,y) on the lcd is pointing at */ | ||
470 | static unsigned short getpixel (int x, int y) | ||
471 | { | ||
472 | int a = div (x - BOARD_X, CUBE_SIZE), b = div (y - BOARD_Y, CUBE_SIZE); | ||
473 | if ((a > 0) && (a < BOARD_W) && (b > 0) && (b < BOARD_H)) /* if inside board */ | ||
474 | return board[b][a]; | ||
475 | else | ||
476 | return FILLED; | ||
477 | } | ||
478 | |||
479 | /* returns the color the ball on (newx,newy) is heading at *----* | ||
480 | checks the four edge points of the square if 1st of all | | | ||
481 | are a trail (cause it's a lose life situation) and 2nd | | | ||
482 | if it's filled so it needs to bounce. *____* | ||
483 | */ | ||
484 | static inline unsigned short next_hit (int newx, int newy) | ||
485 | { | ||
486 | if ((getpixel (newx, newy) == TRAIL) | ||
487 | || (getpixel (newx, newy + CUBE_SIZE - 1) == TRAIL) | ||
488 | || (getpixel (newx + CUBE_SIZE - 1, newy) == TRAIL) | ||
489 | || (getpixel (newx + CUBE_SIZE - 1, newy + CUBE_SIZE - 1) == TRAIL)) | ||
490 | return TRAIL; | ||
491 | else if ((getpixel (newx, newy) == FILLED) | ||
492 | || (getpixel (newx, newy + CUBE_SIZE - 1) == FILLED) | ||
493 | || (getpixel (newx + CUBE_SIZE - 1, newy) == FILLED) | ||
494 | || (getpixel (newx + CUBE_SIZE - 1, newy + CUBE_SIZE - 1) == | ||
495 | FILLED)) | ||
496 | return FILLED; | ||
497 | else | ||
498 | return EMPTIED; | ||
499 | } | ||
500 | |||
501 | /* returns true if the (side) of the block -***- | ||
502 | starting from (newx,newy) has any filled pixels * * | ||
503 | -***- | ||
504 | */ | ||
505 | static bool line_check (int newx, int newy, int side) | ||
506 | { | ||
507 | int i = 0; | ||
508 | bool filled = false; | ||
509 | for (i = 3; ((i < CUBE_SIZE - 3) && (!filled)); i++) { | ||
510 | switch (side) { | ||
511 | case MOVE_LT: | ||
512 | filled = getpixel (newx, newy + i) == FILLED; | ||
513 | break; | ||
514 | case MOVE_RT: | ||
515 | filled = getpixel (newx + CUBE_SIZE - 1, newy + i) == FILLED; | ||
516 | break; | ||
517 | case MOVE_UP: | ||
518 | filled = getpixel (newx + i, newy) == FILLED; | ||
519 | break; | ||
520 | case MOVE_DN: | ||
521 | filled = getpixel (newx + i, newy + CUBE_SIZE - 1) == FILLED; | ||
522 | break; | ||
523 | } | ||
524 | } | ||
525 | return filled; | ||
526 | } | ||
527 | |||
528 | static void move_qix (struct qix *q) | ||
529 | { | ||
530 | int newx, newy, dir; | ||
531 | unsigned short nexthit; | ||
532 | newx = get_newx (q->x, q->velocity, q->angle); | ||
533 | newy = get_newy (q->y, q->velocity, q->angle); | ||
534 | nexthit = next_hit (newx, newy); | ||
535 | if (nexthit == EMPTIED) { | ||
536 | q->x = newx; | ||
537 | q->y = newy; | ||
538 | } else if (nexthit == FILLED) { | ||
539 | dir = q->angle; | ||
540 | switch (dir) { | ||
541 | case MOVE_URR: | ||
542 | case MOVE_UUR: | ||
543 | case MOVE_UR: /* up-right (can hit ceiling or right wall) */ | ||
544 | if (line_check (newx, newy, MOVE_UP) | ||
545 | && line_check (newx, newy, MOVE_RT)) | ||
546 | q->angle = q->angle + 6; | ||
547 | else if (line_check (newx, newy, MOVE_UP)) | ||
548 | q->angle = 5 - q->angle; /* 5=180/(360/12)-1 */ | ||
549 | else | ||
550 | q->angle = 11 - q->angle; /* 11=360/(360/12)-1 */ | ||
551 | break; | ||
552 | case MOVE_ULL: | ||
553 | case MOVE_UUL: | ||
554 | case MOVE_UL: /* up-left (can hit ceiling or left wall) */ | ||
555 | if (line_check (newx, newy, MOVE_UP) | ||
556 | && line_check (newx, newy, MOVE_LT)) | ||
557 | q->angle = q->angle - 6; | ||
558 | else if (line_check (newx, newy, MOVE_UP)) | ||
559 | q->angle = 17 - q->angle; /* 17=540/(360/12)-1 */ | ||
560 | else | ||
561 | q->angle = 11 - q->angle; /* 11=360/(360/12)-1 */ | ||
562 | break; | ||
563 | case MOVE_DLL: | ||
564 | case MOVE_DDL: | ||
565 | case MOVE_DL: /* down-left (can hit floor or left wall) */ | ||
566 | if (line_check (newx, newy, MOVE_DN) | ||
567 | && line_check (newx, newy, MOVE_LT)) | ||
568 | q->angle = q->angle - 6; | ||
569 | else if (line_check (newx, newy, MOVE_DN)) | ||
570 | q->angle = 17 - q->angle; /* 17=540/(360/12)-1 */ | ||
571 | else | ||
572 | q->angle = 11 - q->angle; /* 11=360/(360/12)-1 */ | ||
573 | break; | ||
574 | case MOVE_DRR: | ||
575 | case MOVE_DDR: | ||
576 | case MOVE_DR: /* down-right (can hit floor or right wall) */ | ||
577 | if (line_check (newx, newy, MOVE_DN) | ||
578 | && line_check (newx, newy, MOVE_RT)) | ||
579 | q->angle = q->angle + 6; | ||
580 | else if (line_check (newx, newy, MOVE_DN)) | ||
581 | q->angle = 5 - q->angle; /* 5=180/(360/12)-1 */ | ||
582 | else | ||
583 | q->angle = 11 - q->angle; /* 11=360/(360/12)-1 */ | ||
584 | break; | ||
585 | } | ||
586 | q->x = newx; | ||
587 | q->y = newy; | ||
588 | refresh_board (); | ||
589 | newx = get_newx (newx, q->velocity, q->angle); | ||
590 | newy = get_newy (newy, q->velocity, q->angle); | ||
591 | q->x = newx; | ||
592 | q->y = newy; | ||
593 | } else if (nexthit == TRAIL) { | ||
594 | player.lives--; | ||
595 | if (player.lives == 0) | ||
596 | player.gameover = true; | ||
597 | refresh_board (); | ||
598 | rb->splash (HZ, true, "Crash!"); | ||
599 | init_board (); | ||
600 | } | ||
601 | } | ||
602 | |||
603 | /* move the board forward timewise */ | ||
604 | static inline void move_board (void) | ||
605 | { | ||
606 | int j, newi, newj; | ||
607 | |||
608 | for (j = 0; j < player.level + STARTING_QIXES; j++) | ||
609 | move_qix (&qixes[j]); | ||
610 | /* move_qix(&black_qix,true); */ | ||
611 | if (player.move) { | ||
612 | newi = player.i; | ||
613 | newj = player.j; | ||
614 | switch (player.move) { | ||
615 | case MOVE_UP: | ||
616 | if (player.j > 1) | ||
617 | newj--; | ||
618 | break; | ||
619 | case MOVE_DN: | ||
620 | if (player.j < BOARD_H - 2) | ||
621 | newj++; | ||
622 | break; | ||
623 | case MOVE_LT: | ||
624 | if (player.i > 0) | ||
625 | newi--; | ||
626 | break; | ||
627 | case MOVE_RT: | ||
628 | if (player.i < BOARD_W - 1) | ||
629 | newi++; | ||
630 | break; | ||
631 | default: | ||
632 | break; | ||
633 | } | ||
634 | |||
635 | if ((player.drawing) && (board[newj][newi] == EMPTIED)) /* continue drawing */ | ||
636 | board[newj][newi] = TRAIL; | ||
637 | else if ((player.drawing) && (board[newj][newi] == FILLED)) { /* finish drawing */ | ||
638 | player.move = MOVE_NO; /* stop moving */ | ||
639 | player.drawing = false; | ||
640 | complete_trail (); | ||
641 | } else if ((board[player.j][player.i] == FILLED) | ||
642 | && (board[newj][newi] == EMPTIED)) { | ||
643 | /* start drawing */ | ||
644 | player.drawing = true; | ||
645 | board[newj][newi] = TRAIL; | ||
646 | } | ||
647 | player.i = newi; | ||
648 | player.j = newj; | ||
649 | } | ||
650 | j = percentage (); | ||
651 | if (j > 75) { /* finished level */ | ||
652 | rb->splash (HZ * 2, true, "Level %d finished", player.level+1); | ||
653 | player.score += j; | ||
654 | if (player.level < MAX_LEVEL) | ||
655 | player.level++; | ||
656 | init_board (); | ||
657 | refresh_board (); | ||
658 | rb->splash (HZ * 2, true, "READY?"); | ||
659 | rb->lcd_update (); | ||
660 | refresh_board (); | ||
661 | } | ||
662 | } | ||
663 | |||
664 | /* the main menu */ | ||
665 | #define MAIN_MENU_SIZE 2 | ||
666 | #define MENU_START 0 | ||
667 | #define MENU_QUIT 1 | ||
668 | static int game_menu (void) | ||
669 | { | ||
670 | static char menu[MAIN_MENU_SIZE][15] = { | ||
671 | "Start New Game", | ||
672 | "Quit" | ||
673 | }; | ||
674 | |||
675 | int button, selection = 0, sw, sh, i; | ||
676 | bool quit = false; | ||
677 | rb->lcd_getstringsize ("A", NULL, &sh); | ||
678 | rb->lcd_getstringsize ("XOBOX", &sw, NULL); | ||
679 | sh++; | ||
680 | rb->lcd_set_background (LCD_WHITE); | ||
681 | rb->lcd_clear_display (); | ||
682 | rb->lcd_putsxy (LCD_WIDTH / 2 - sw / 2, 2, "XOBOX"); | ||
683 | while (!quit) { | ||
684 | for (i = 0; i < MAIN_MENU_SIZE; i++) { | ||
685 | rb->lcd_set_foreground ((i == selection ? LCD_WHITE : LCD_BLACK)); | ||
686 | rb->lcd_set_background ((i == selection ? CLR_BLUE : LCD_WHITE)); | ||
687 | rb->lcd_putsxy (9, sh + 4 + i * sh, menu[i]); | ||
688 | } | ||
689 | rb->lcd_update (); | ||
690 | button = rb->button_get (true); | ||
691 | switch (button) { | ||
692 | case UP: | ||
693 | selection = (selection + MAIN_MENU_SIZE - 1) % MAIN_MENU_SIZE; | ||
694 | break; | ||
695 | case DOWN: | ||
696 | selection = (selection + 1) % MAIN_MENU_SIZE; | ||
697 | break; | ||
698 | case SELECT: | ||
699 | case RIGHT: | ||
700 | quit = true; | ||
701 | break; | ||
702 | case QUIT: | ||
703 | selection = MENU_QUIT; | ||
704 | quit = true; | ||
705 | break; | ||
706 | } | ||
707 | } | ||
708 | return selection; | ||
709 | } | ||
710 | |||
711 | /* init game's variables */ | ||
712 | static void init_game (void) | ||
713 | { | ||
714 | player.level = 0; | ||
715 | player.score = 0; | ||
716 | player.lives = 3; | ||
717 | player.gameover = false; | ||
718 | player.drawing = false; | ||
719 | init_board (); | ||
720 | refresh_board (); | ||
721 | rb->splash (HZ * 2, true, "READY?"); | ||
722 | rb->lcd_update (); | ||
723 | refresh_board (); | ||
724 | } | ||
725 | |||
726 | /* general keypad handler loop */ | ||
727 | static int xobox_loop (void) | ||
728 | { | ||
729 | int button = 0, ret; | ||
730 | bool pause = false; | ||
731 | int end; | ||
732 | |||
733 | while (!quit) { | ||
734 | end = *rb->current_tick + (CYCLETIME * HZ) / 1000; | ||
735 | button = rb->button_get_w_tmo (true); | ||
736 | switch (button) { | ||
737 | case UP: | ||
738 | player.move = MOVE_UP; | ||
739 | break; | ||
740 | case DOWN: | ||
741 | player.move = MOVE_DN; | ||
742 | break; | ||
743 | case LEFT: | ||
744 | player.move = MOVE_LT; | ||
745 | break; | ||
746 | case RIGHT: | ||
747 | player.move = MOVE_RT; | ||
748 | break; | ||
749 | case PAUSE: | ||
750 | pause = !pause; | ||
751 | if (pause) | ||
752 | rb->splash (HZ, true, "PAUSED"); | ||
753 | break; | ||
754 | case QUIT: | ||
755 | quit = true; | ||
756 | return PLUGIN_OK; | ||
757 | break; | ||
758 | default: | ||
759 | if (rb->default_event_handler (button) == SYS_USB_CONNECTED) | ||
760 | return PLUGIN_USB_CONNECTED; | ||
761 | break; | ||
762 | } | ||
763 | if (!pause) { | ||
764 | move_board (); | ||
765 | refresh_board (); | ||
766 | } | ||
767 | if (player.gameover) { | ||
768 | rb->splash (HZ, true, "GAME OVER"); | ||
769 | ret = game_menu (); | ||
770 | if (ret == MENU_START) | ||
771 | init_game (); | ||
772 | else | ||
773 | quit = true; | ||
774 | } | ||
775 | |||
776 | if (end > *rb->current_tick) | ||
777 | rb->sleep (end - *rb->current_tick); | ||
778 | else | ||
779 | rb->yield (); | ||
780 | |||
781 | } /* end while */ | ||
782 | return PLUGIN_OK; /* for no warnings on compiling */ | ||
783 | } | ||
784 | |||
785 | /* plugin main procedure */ | ||
786 | enum plugin_status plugin_start (struct plugin_api *api, void *parameter) | ||
787 | { | ||
788 | int ret; | ||
789 | |||
790 | |||
791 | (void) parameter; | ||
792 | rb = api; | ||
793 | |||
794 | rb->lcd_setfont (FONT_SYSFIXED); | ||
795 | |||
796 | /* Permanently enable the backlight (unless the user has turned it off) */ | ||
797 | if (rb->global_settings->backlight_timeout > 0) | ||
798 | rb->backlight_set_timeout (1); | ||
799 | |||
800 | randomize (); | ||
801 | ret = game_menu (); | ||
802 | if (ret == MENU_START) { | ||
803 | init_game (); | ||
804 | ret = xobox_loop (); | ||
805 | return ret; | ||
806 | } | ||
807 | |||
808 | rb->backlight_set_timeout (rb->global_settings->backlight_timeout); | ||
809 | rb->lcd_setfont (FONT_UI); | ||
810 | |||
811 | |||
812 | return PLUGIN_OK; | ||
813 | } | ||