Prepare new maemo release
[maemo-rb.git] / apps / plugins / spacerocks.c
blobe303a1138d843244364828ccbec117e0a0ecbe9b
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2006 by Mat Holton
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
22 #include "plugin.h"
23 #include "lib/display_text.h"
24 #include "lib/helper.h"
25 #include "lib/highscore.h"
26 #include "lib/playback_control.h"
30 /* variable button definitions */
31 #if CONFIG_KEYPAD == RECORDER_PAD
32 #define AST_PAUSE BUTTON_ON
33 #define AST_QUIT BUTTON_OFF
34 #define AST_THRUST BUTTON_UP
35 #define AST_HYPERSPACE BUTTON_DOWN
36 #define AST_LEFT BUTTON_LEFT
37 #define AST_RIGHT BUTTON_RIGHT
38 #define AST_FIRE BUTTON_PLAY
40 #elif CONFIG_KEYPAD == ARCHOS_AV300_PAD
41 #define AST_PAUSE BUTTON_ON
42 #define AST_QUIT BUTTON_OFF
43 #define AST_THRUST BUTTON_UP
44 #define AST_HYPERSPACE BUTTON_DOWN
45 #define AST_LEFT BUTTON_LEFT
46 #define AST_RIGHT BUTTON_RIGHT
47 #define AST_FIRE BUTTON_SELECT
49 #elif CONFIG_KEYPAD == ONDIO_PAD
50 #define AST_PAUSE (BUTTON_MENU | BUTTON_OFF)
51 #define AST_QUIT BUTTON_OFF
52 #define AST_THRUST BUTTON_UP
53 #define AST_HYPERSPACE BUTTON_DOWN
54 #define AST_LEFT BUTTON_LEFT
55 #define AST_RIGHT BUTTON_RIGHT
56 #define AST_FIRE BUTTON_MENU
58 #elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || \
59 (CONFIG_KEYPAD == IRIVER_H300_PAD)
60 #define AST_PAUSE BUTTON_REC
61 #define AST_QUIT BUTTON_OFF
62 #define AST_THRUST BUTTON_UP
63 #define AST_HYPERSPACE BUTTON_DOWN
64 #define AST_LEFT BUTTON_LEFT
65 #define AST_RIGHT BUTTON_RIGHT
66 #define AST_FIRE BUTTON_SELECT
68 #define AST_RC_QUIT BUTTON_RC_STOP
70 #elif (CONFIG_KEYPAD == IAUDIO_X5M5_PAD)
71 #define AST_PAUSE BUTTON_PLAY
72 #define AST_QUIT BUTTON_POWER
73 #define AST_THRUST BUTTON_UP
74 #define AST_HYPERSPACE BUTTON_DOWN
75 #define AST_LEFT BUTTON_LEFT
76 #define AST_RIGHT BUTTON_RIGHT
77 #define AST_FIRE BUTTON_SELECT
79 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || (CONFIG_KEYPAD == IPOD_3G_PAD) || \
80 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
81 #define AST_PAUSE (BUTTON_SELECT | BUTTON_PLAY)
82 #define AST_QUIT (BUTTON_SELECT | BUTTON_MENU)
83 #define AST_THRUST BUTTON_MENU
84 #define AST_HYPERSPACE BUTTON_PLAY
85 #define AST_LEFT BUTTON_SCROLL_BACK
86 #define AST_RIGHT BUTTON_SCROLL_FWD
87 #define AST_FIRE BUTTON_SELECT
89 #elif (CONFIG_KEYPAD == GIGABEAT_PAD)
90 #define AST_PAUSE BUTTON_A
91 #define AST_QUIT BUTTON_POWER
92 #define AST_THRUST BUTTON_UP
93 #define AST_HYPERSPACE BUTTON_DOWN
94 #define AST_LEFT BUTTON_LEFT
95 #define AST_RIGHT BUTTON_RIGHT
96 #define AST_FIRE BUTTON_SELECT
98 #elif (CONFIG_KEYPAD == SANSA_E200_PAD)
99 #define AST_PAUSE BUTTON_REC
100 #define AST_QUIT BUTTON_POWER
101 #define AST_THRUST BUTTON_UP
102 #define AST_HYPERSPACE BUTTON_DOWN
103 #define AST_LEFT BUTTON_SCROLL_BACK
104 #define AST_RIGHT BUTTON_SCROLL_FWD
105 #define AST_FIRE BUTTON_SELECT
107 #elif (CONFIG_KEYPAD == SANSA_FUZE_PAD)
108 #define AST_PAUSE (BUTTON_SELECT | BUTTON_UP)
109 #define AST_QUIT (BUTTON_HOME|BUTTON_REPEAT)
110 #define AST_THRUST BUTTON_UP
111 #define AST_HYPERSPACE BUTTON_DOWN
112 #define AST_LEFT BUTTON_SCROLL_BACK
113 #define AST_RIGHT BUTTON_SCROLL_FWD
114 #define AST_FIRE BUTTON_SELECT
116 #elif (CONFIG_KEYPAD == SANSA_C200_PAD)
117 #define AST_PAUSE BUTTON_REC
118 #define AST_QUIT BUTTON_POWER
119 #define AST_THRUST BUTTON_UP
120 #define AST_HYPERSPACE BUTTON_DOWN
121 #define AST_LEFT BUTTON_LEFT
122 #define AST_RIGHT BUTTON_RIGHT
123 #define AST_FIRE BUTTON_SELECT
125 #elif (CONFIG_KEYPAD == SANSA_CLIP_PAD)
126 #define AST_PAUSE BUTTON_HOME
127 #define AST_QUIT BUTTON_POWER
128 #define AST_THRUST BUTTON_UP
129 #define AST_HYPERSPACE BUTTON_DOWN
130 #define AST_LEFT BUTTON_LEFT
131 #define AST_RIGHT BUTTON_RIGHT
132 #define AST_FIRE BUTTON_SELECT
134 #elif (CONFIG_KEYPAD == SANSA_M200_PAD)
135 #define AST_PAUSE (BUTTON_SELECT | BUTTON_UP)
136 #define AST_QUIT BUTTON_POWER
137 #define AST_THRUST BUTTON_UP
138 #define AST_HYPERSPACE BUTTON_DOWN
139 #define AST_LEFT BUTTON_LEFT
140 #define AST_RIGHT BUTTON_RIGHT
141 #define AST_FIRE (BUTTON_SELECT | BUTTON_REL)
143 #elif (CONFIG_KEYPAD == IRIVER_H10_PAD)
144 #define AST_PAUSE BUTTON_PLAY
145 #define AST_QUIT BUTTON_POWER
146 #define AST_THRUST BUTTON_SCROLL_UP
147 #define AST_HYPERSPACE BUTTON_SCROLL_DOWN
148 #define AST_LEFT BUTTON_LEFT
149 #define AST_RIGHT BUTTON_RIGHT
150 #define AST_FIRE BUTTON_REW
152 #elif (CONFIG_KEYPAD == GIGABEAT_S_PAD)
153 #define AST_PAUSE BUTTON_PLAY
154 #define AST_QUIT BUTTON_BACK
155 #define AST_THRUST BUTTON_UP
156 #define AST_HYPERSPACE BUTTON_DOWN
157 #define AST_LEFT BUTTON_LEFT
158 #define AST_RIGHT BUTTON_RIGHT
159 #define AST_FIRE BUTTON_SELECT
161 #elif (CONFIG_KEYPAD == MROBE100_PAD)
162 #define AST_PAUSE BUTTON_DISPLAY
163 #define AST_QUIT BUTTON_POWER
164 #define AST_THRUST BUTTON_UP
165 #define AST_HYPERSPACE BUTTON_DOWN
166 #define AST_LEFT BUTTON_LEFT
167 #define AST_RIGHT BUTTON_RIGHT
168 #define AST_FIRE BUTTON_SELECT
170 #elif CONFIG_KEYPAD == IAUDIO_M3_PAD
171 #define AST_PAUSE BUTTON_RC_PLAY
172 #define AST_QUIT BUTTON_RC_REC
173 #define AST_THRUST BUTTON_RC_VOL_UP
174 #define AST_HYPERSPACE BUTTON_RC_VOL_DOWN
175 #define AST_LEFT BUTTON_RC_REW
176 #define AST_RIGHT BUTTON_RC_FF
177 #define AST_FIRE BUTTON_RC_MODE
179 #elif (CONFIG_KEYPAD == COWON_D2_PAD)
180 #define AST_QUIT BUTTON_POWER
182 #elif CONFIG_KEYPAD == CREATIVEZVM_PAD
183 #define AST_PAUSE BUTTON_PLAY
184 #define AST_QUIT BUTTON_BACK
185 #define AST_THRUST BUTTON_UP
186 #define AST_HYPERSPACE BUTTON_DOWN
187 #define AST_LEFT BUTTON_LEFT
188 #define AST_RIGHT BUTTON_RIGHT
189 #define AST_FIRE BUTTON_SELECT
191 #elif CONFIG_KEYPAD == PHILIPS_HDD1630_PAD
192 #define AST_PAUSE BUTTON_VIEW
193 #define AST_QUIT BUTTON_POWER
194 #define AST_THRUST BUTTON_UP
195 #define AST_HYPERSPACE BUTTON_DOWN
196 #define AST_LEFT BUTTON_LEFT
197 #define AST_RIGHT BUTTON_RIGHT
198 #define AST_FIRE BUTTON_PLAYLIST
200 #elif CONFIG_KEYPAD == PHILIPS_HDD6330_PAD
201 #define AST_PAUSE BUTTON_PLAY
202 #define AST_QUIT BUTTON_POWER
203 #define AST_THRUST BUTTON_UP
204 #define AST_HYPERSPACE BUTTON_DOWN
205 #define AST_LEFT BUTTON_LEFT
206 #define AST_RIGHT BUTTON_RIGHT
207 #define AST_FIRE BUTTON_VOL_DOWN
209 #elif CONFIG_KEYPAD == PHILIPS_SA9200_PAD
210 #define AST_PAUSE BUTTON_RIGHT
211 #define AST_QUIT BUTTON_POWER
212 #define AST_THRUST BUTTON_UP
213 #define AST_HYPERSPACE BUTTON_DOWN
214 #define AST_LEFT BUTTON_PREV
215 #define AST_RIGHT BUTTON_NEXT
216 #define AST_FIRE BUTTON_LEFT
218 #elif (CONFIG_KEYPAD == ONDAVX747_PAD) || \
219 (CONFIG_KEYPAD == ONDAVX777_PAD) || \
220 (CONFIG_KEYPAD == MROBE500_PAD)
221 #define AST_QUIT BUTTON_POWER
223 #elif (CONFIG_KEYPAD == SAMSUNG_YH_PAD)
224 #define AST_PAUSE BUTTON_FFWD
225 #define AST_QUIT BUTTON_REC
226 #define AST_THRUST BUTTON_UP
227 #define AST_HYPERSPACE BUTTON_DOWN
228 #define AST_LEFT BUTTON_LEFT
229 #define AST_RIGHT BUTTON_RIGHT
230 #define AST_FIRE BUTTON_PLAY
232 #elif (CONFIG_KEYPAD == PBELL_VIBE500_PAD)
233 #define AST_PAUSE BUTTON_PLAY
234 #define AST_QUIT BUTTON_REC
235 #define AST_THRUST BUTTON_UP
236 #define AST_HYPERSPACE BUTTON_DOWN
237 #define AST_LEFT BUTTON_PREV
238 #define AST_RIGHT BUTTON_NEXT
239 #define AST_FIRE BUTTON_OK
241 #elif (CONFIG_KEYPAD == MPIO_HD200_PAD)
243 #define AST_PAUSE (BUTTON_PLAY|BUTTON_FUNC)
244 #define AST_QUIT (BUTTON_REC|BUTTON_PLAY)
245 #define AST_THRUST BUTTON_REC
246 #define AST_HYPERSPACE BUTTON_PLAY
247 #define AST_LEFT BUTTON_REW
248 #define AST_RIGHT BUTTON_FF
249 #define AST_FIRE BUTTON_FUNC
251 #elif (CONFIG_KEYPAD == MPIO_HD300_PAD)
253 #define AST_PAUSE (BUTTON_PLAY|BUTTON_REL)
254 #define AST_QUIT (BUTTON_MENU|BUTTON_REPEAT)
255 #define AST_THRUST BUTTON_REC
256 #define AST_HYPERSPACE (BUTTON_PLAY|BUTTON_REPEAT)
257 #define AST_LEFT BUTTON_UP
258 #define AST_RIGHT BUTTON_DOWN
259 #define AST_FIRE BUTTON_ENTER
261 #elif (CONFIG_KEYPAD == SANSA_FUZEPLUS_PAD)
263 #define AST_PAUSE BUTTON_PLAYPAUSE
264 #define AST_QUIT BUTTON_POWER
265 #define AST_THRUST BUTTON_UP
266 #define AST_HYPERSPACE BUTTON_BACK
267 #define AST_LEFT BUTTON_LEFT
268 #define AST_RIGHT BUTTON_RIGHT
269 #define AST_FIRE BUTTON_SELECT
271 #elif (CONFIG_KEYPAD == SANSA_CONNECT_PAD)
273 #define ALT_PAUSE BUTTON_VOL_DOWN
274 #define AST_QUIT BUTTON_POWER
275 #define AST_THRUST BUTTON_UP
276 #define AST_HYPERSPACE BUTTON_DOWN
277 #define AST_LEFT BUTTON_LEFT
278 #define AST_RIGHT BUTTON_RIGHT
279 #define AST_FIRE BUTTON_SELECT
281 #elif (CONFIG_KEYPAD == SAMSUNG_YPR0_PAD)
282 #define AST_PAUSE BUTTON_MENU
283 #define AST_QUIT BUTTON_BACK
284 #define AST_THRUST BUTTON_UP
285 #define AST_HYPERSPACE BUTTON_DOWN
286 #define AST_LEFT BUTTON_LEFT
287 #define AST_RIGHT BUTTON_RIGHT
288 #define AST_FIRE BUTTON_SELECT
290 #elif (CONFIG_KEYPAD == HM60X_PAD)
291 #define AST_PAUSE (BUTTON_SELECT|BUTTON_POWER)
292 #define AST_QUIT BUTTON_POWER
293 #define AST_THRUST BUTTON_UP
294 #define AST_HYPERSPACE BUTTON_DOWN
295 #define AST_LEFT BUTTON_LEFT
296 #define AST_RIGHT BUTTON_RIGHT
297 #define AST_FIRE BUTTON_SELECT
299 #elif (CONFIG_KEYPAD == HM801_PAD)
300 #define AST_PAUSE BUTTON_PLAY
301 #define AST_QUIT BUTTON_POWER
302 #define AST_THRUST BUTTON_UP
303 #define AST_HYPERSPACE BUTTON_DOWN
304 #define AST_LEFT BUTTON_LEFT
305 #define AST_RIGHT BUTTON_RIGHT
306 #define AST_FIRE BUTTON_SELECT
308 #else
309 #error No keymap defined!
310 #endif
312 #ifdef HAVE_TOUCHSCREEN
313 #ifndef AST_PAUSE
314 #define AST_PAUSE BUTTON_CENTER
315 #endif
316 #ifndef AST_QUIT
317 #define AST_QUIT BUTTON_TOPLEFT
318 #endif
319 #ifndef AST_THRUST
320 #define AST_THRUST BUTTON_TOPMIDDLE
321 #endif
322 #ifndef AST_HYPERSPACE
323 #define AST_HYPERSPACE BUTTON_TOPRIGHT
324 #endif
325 #ifndef AST_LEFT
326 #define AST_LEFT BUTTON_MIDLEFT
327 #endif
328 #ifndef AST_RIGHT
329 #define AST_RIGHT BUTTON_MIDRIGHT
330 #endif
331 #ifndef AST_FIRE
332 #define AST_FIRE BUTTON_BOTTOMMIDDLE
333 #endif
334 #endif
336 #define RES MAX(LCD_WIDTH, LCD_HEIGHT)
337 #define LARGE_LCD (RES >= 200)
339 #define CYCLETIME 30
341 #define SHOW_COL 0
342 #define SCALE 5000
343 #define WRAP_GAP (LARGE*SCALE*3)
344 #define POINT_SIZE 2
345 #define START_LEVEL 1
346 #define SHOW_LEVEL_TIME 50
347 #define EXPLOSION_LENGTH 20
349 #define MAX_NUM_ASTEROIDS 25
350 #define MAX_NUM_MISSILES 6
351 #define NUM_STARS 50
352 #define NUM_TRAIL_POINTS 70
353 #define MAX_LEVEL MAX_NUM_ASTEROIDS
355 #define NUM_ASTEROID_VERTICES 10
356 #define NUM_SHIP_VERTICES 4
357 #define NUM_ENEMY_VERTICES 8
359 #define INVULNERABLE_TIME 30
360 #define BLINK_TIME 10
361 #define EXTRA_LIFE 250
362 #define START_LIVES 3
363 #define MISSILE_LIFE_LENGTH 40
365 #define ASTEROID_SPEED (RES/20)
366 #define SPACE_CHECK_SIZE 30*SCALE
368 #if (LARGE_LCD)
369 #define SIZE_SHIP_COLLISION 8*SCALE
370 #else
371 #define SIZE_SHIP_COLLISION 6*SCALE
372 #endif
374 #define LITTLE_SHIP 1
375 #define BIG_SHIP 2
376 #define ENEMY_BIG_PROBABILITY_START 10
377 #define ENEMY_APPEAR_PROBABILITY_START 35
378 #define ENEMY_APPEAR_TIMING_START 600
379 #define ENEMY_SPEED 4
380 #define ENEMY_MISSILE_LIFE_LENGTH (RES/2)
381 #if (LARGE_LCD)
382 #define SIZE_ENEMY_COLLISION 7*SCALE
383 #else
384 #define SIZE_ENEMY_COLLISION 5*SCALE
385 #endif
387 #define SIN_COS_SCALE 10000
389 #define FAST_ROT_CW_SIN 873
390 #define FAST_ROT_CW_COS 9963
391 #define FAST_ROT_ACW_SIN -873
392 #define FAST_ROT_ACW_COS 9963
394 #define MEDIUM_ROT_CW_SIN 350
395 #define MEDIUM_ROT_CW_COS 9994
396 #define MEDIUM_ROT_ACW_SIN -350
397 #define MEDIUM_ROT_ACW_COS 9994
399 #define SLOW_ROT_CW_SIN 350
400 #define SLOW_ROT_CW_COS 9994
401 #define SLOW_ROT_ACW_SIN -350
402 #define SLOW_ROT_ACW_COS 9994
404 #ifdef HAVE_LCD_COLOR
405 #define SHIP_ROT_CW_SIN 2419
406 #define SHIP_ROT_CW_COS 9702
407 #define SHIP_ROT_ACW_SIN -2419
408 #define SHIP_ROT_ACW_COS 9702
409 #else
410 #define SHIP_ROT_CW_SIN 3827
411 #define SHIP_ROT_CW_COS 9239
412 #define SHIP_ROT_ACW_SIN -3827
413 #define SHIP_ROT_ACW_COS 9239
414 #endif
417 #define SCALED_WIDTH (LCD_WIDTH*SCALE)
418 #define SCALED_HEIGHT (LCD_HEIGHT*SCALE)
419 #define CENTER_LCD_X (LCD_WIDTH/2)
420 #define CENTER_LCD_Y (LCD_HEIGHT/2)
422 #ifdef HAVE_LCD_COLOR
423 #define ASTEROID_R 230
424 #define ASTEROID_G 200
425 #define ASTEROID_B 100
426 #define SHIP_R 255
427 #define SHIP_G 255
428 #define SHIP_B 255
429 #define ENEMY_R 50
430 #define ENEMY_G 220
431 #define ENEMY_B 50
432 #define THRUST_R 200
433 #define THRUST_G 200
434 #define THRUST_B 0
436 #define COL_MISSILE LCD_RGBPACK(200,0,0)
437 #define COL_PLAYER LCD_RGBPACK(200,200,200)
438 #define COL_INVULN LCD_RGBPACK(100,100,200)
439 #define COL_STARS LCD_WHITE
440 #define COL_ASTEROID LCD_RGBPACK(ASTEROID_R,ASTEROID_G,ASTEROID_B)
441 #define COL_TEXT LCD_RGBPACK(200,200,255)
442 #define COL_ENEMY LCD_RGBPACK(ENEMY_R,ENEMY_G,ENEMY_B)
443 #define SET_FG rb->lcd_set_foreground
444 #define SET_BG rb->lcd_set_background
445 #else
446 #define SET_FG(x)
447 #define SET_BG(x)
448 #endif
450 #define SCORE_FILE PLUGIN_GAMES_DATA_DIR "/spacerocks.score"
451 #define NUM_SCORES 5
453 static struct highscore highscores[NUM_SCORES];
455 /* The array of points that make up an asteroid */
456 static const short asteroid_one[NUM_ASTEROID_VERTICES*2] =
458 -2, -12,
459 4, -8,
460 8, -14,
461 16, -5,
462 14, 0,
463 20, 2,
464 12, 14,
465 -4, 14,
466 -10, 6,
467 -10, -8,
470 /* The array of points that make up an asteroid */
471 static const short asteroid_two[NUM_ASTEROID_VERTICES*2] =
473 -2, -12,
474 4, -16,
475 6, -14,
476 16, -8,
477 14, 0,
478 20, 2,
479 12, 14,
480 -4, 14,
481 -10, 6,
482 -10, -8,
485 /* The array of points that make up an asteroid */
486 static const short asteroid_three[NUM_ASTEROID_VERTICES*2] =
488 -2, -12,
489 4, -16,
490 6, -14,
491 2, -8,
492 14, 0,
493 20, 2,
494 12, 14,
495 -4, 14,
496 -16, 6,
497 -10, -8,
500 /* The array of points the make up the ship */
501 static const short ship_vertices[NUM_SHIP_VERTICES*2] =
503 #if (LARGE_LCD)
504 0, -6,
505 4, 6,
506 0, 2,
507 -4, 6,
508 #else
509 0, -4,
510 3, 4,
511 0, 1,
512 -3, 4,
513 #endif
516 /* The array of points the make up the bad spaceship */
517 static const short enemy_vertices[NUM_ENEMY_VERTICES*2] =
519 #if (LARGE_LCD)
520 -8, 0,
521 -4, 4,
522 4, 4,
523 8, 0,
524 -8, 0,
525 8, 0,
526 4, -4,
527 -4, -4,
528 #else
529 -5, 0,
530 -2, 2,
531 2, 2,
532 5, 0,
533 -5, 0,
534 5, 0,
535 2, -2,
536 -2, -2,
537 #endif
540 enum asteroid_type
542 #if (LARGE_LCD)
543 SMALL = 2,
544 MEDIUM = 4,
545 LARGE = 6,
546 #else
547 SMALL = 1,
548 MEDIUM = 2,
549 LARGE = 3,
550 #endif
553 enum explosion_type
555 EXPLOSION_SHIP,
556 EXPLOSION_ASTEROID,
557 EXPLOSION_ENEMY,
558 EXPLOSION_THRUST,
561 enum game_state
563 GAME_OVER,
564 SHOW_LEVEL,
565 PLAY_MODE,
566 PAUSE_MODE,
569 struct Point
571 int x;
572 int y;
573 int dx;
574 int dy;
577 struct TrailPoint
579 struct Point position;
580 int alive;
581 #ifdef HAVE_LCD_COLOR
582 short r;
583 short g;
584 short b;
585 short dec;
586 #endif
589 /* Asteroid structure, contains an array of points */
590 struct Asteroid
592 struct Point position;
593 struct Point rotation;
594 struct Point vertices[NUM_ASTEROID_VERTICES];
595 bool exists;
596 int explode_countdown;
597 enum asteroid_type type;
598 int radius;
599 long speed_cos;
600 long speed_sin;
603 struct Ship
605 struct Point position;
606 struct Point rotation;
607 struct Point vertices[NUM_SHIP_VERTICES];
608 bool exists;
609 int explode_countdown;
610 int invulnerable_time;
613 struct Enemy
615 struct Point position;
616 struct Point vertices[NUM_ENEMY_VERTICES];
617 bool exists;
618 int explode_countdown;
619 int appear_countdown;
620 short size_probability;
621 short appear_probability;
622 short appear_timing;
625 struct Missile
627 struct Point position;
628 struct Point oldpoint;
629 int alive;
632 static enum game_state game_state;
633 static int asteroid_count;
634 static int next_missile_count;
635 static int next_thrust_count;
636 static int num_lives;
637 static int extra_life;
638 static int show_level_timeout;
639 static int current_level;
640 static int current_score;
642 static struct Ship ship;
643 static struct Point stars[NUM_STARS];
644 static struct Asteroid asteroids_array[MAX_NUM_ASTEROIDS];
645 static struct Missile missiles_array[MAX_NUM_MISSILES];
646 static struct Missile enemy_missile;
647 static struct Enemy enemy;
648 static struct Point lives_points[NUM_SHIP_VERTICES];
649 static struct TrailPoint trail_points[NUM_TRAIL_POINTS];
651 /*************************************************
652 ** Handle polygon and point
653 *************************************************/
655 /* Check if point is in a polygon */
656 static bool is_point_in_polygon(struct Point* vertices, int num_vertices,
657 int x, int y)
659 struct Point* pi;
660 struct Point* pj;
661 int n;
662 bool c = false;
664 if (x < -SCALED_WIDTH/2) x += SCALED_WIDTH;
665 else if (x > SCALED_WIDTH/2) x -= SCALED_WIDTH;
666 if (y < -SCALED_HEIGHT/2) y += SCALED_HEIGHT;
667 else if (y > SCALED_HEIGHT/2) y -= SCALED_HEIGHT;
669 pi = vertices;
670 pj = vertices + num_vertices-1;
672 n = num_vertices;
673 while (n--)
675 if ((((pi->y <= y) && (y < pj->y)) || ((pj->y <= y) && (y < pi->y))) &&
676 (x < (pj->x - pi->x) * (y - pi->y) / (pj->y - pi->y) + pi->x))
677 c = !c;
679 pj = pi;
680 pi++;
683 return c;
686 /* Check if point is within a rectangle */
687 static bool is_point_within_rectangle(struct Point* rect, struct Point* p,
688 int size)
690 int dx = p->x - rect->x;
691 int dy = p->y - rect->y;
692 #if SHOW_COL
693 rb->lcd_drawrect((rect->x - size)/SCALE, (rect->y - size)/SCALE,
694 (size*2+1)/SCALE, (size*2+1)/SCALE);
695 #endif
696 if (dx < -SCALED_WIDTH/2) dx += SCALED_WIDTH;
697 else if (dx > SCALED_WIDTH/2) dx -= SCALED_WIDTH;
698 if (dy < -SCALED_HEIGHT/2) dy += SCALED_HEIGHT;
699 else if (dy > SCALED_HEIGHT/2) dy -= SCALED_HEIGHT;
700 return (dx > -size && dx < size && dy > -size && dy < size);
703 /* Rotate polygon */
704 static void rotate_polygon(struct Point* vertices, int num_vertices,
705 struct Point* rotation, int cos, int sin)
707 struct Point* point;
708 int n;
709 long temp_x, temp_y;
711 temp_x = rotation->x;
712 temp_y = rotation->y;
713 rotation->x = (temp_x*cos - temp_y*sin)/SIN_COS_SCALE;
714 rotation->y = (temp_y*cos + temp_x*sin)/SIN_COS_SCALE;
715 #define MIN_SCALE (SIN_COS_SCALE-10)
716 #define MAX_SCALE (SIN_COS_SCALE+10)
717 /* normalize vector. this is not accurate but would be enough. */
718 temp_x = rotation->x*rotation->x + rotation->y*rotation->y;
719 if (temp_x <= MIN_SCALE*MIN_SCALE)
721 rotation->x = rotation->x*SIN_COS_SCALE/MIN_SCALE;
722 rotation->y = rotation->y*SIN_COS_SCALE/MIN_SCALE;
724 else if (temp_x >= MAX_SCALE*MAX_SCALE)
726 rotation->x = rotation->x*SIN_COS_SCALE/MAX_SCALE;
727 rotation->y = rotation->y*SIN_COS_SCALE/MAX_SCALE;
729 #undef MIN_SCALE
730 #undef MAX_SCALE
732 point = vertices;
733 n = num_vertices;
734 while (n--)
736 point->x = (point->dx*rotation->x - point->dy*rotation->y)/SIN_COS_SCALE;
737 point->y = (point->dy*rotation->x + point->dx*rotation->y)/SIN_COS_SCALE;
738 point++;
742 /* Draw polygon */
743 static void draw_polygon(struct Point* vertices, int num_vertices,
744 int px, int py)
746 int n, new_x, new_y, old_x, old_y;
747 struct Point *p;
748 bool draw_wrap;
750 if (px > SCALED_WIDTH - WRAP_GAP)
751 px -= SCALED_WIDTH;
752 if (py > SCALED_HEIGHT - WRAP_GAP)
753 py -= SCALED_HEIGHT;
755 draw_wrap = (px < WRAP_GAP || py < WRAP_GAP);
757 p = vertices + num_vertices - 1;
758 old_x = (p->x + px)/SCALE;
759 old_y = (p->y + py)/SCALE;
760 p = vertices;
761 n = num_vertices;
762 while (n--)
764 new_x = (p->x + px)/SCALE;
765 new_y = (p->y + py)/SCALE;
767 rb->lcd_drawline(old_x, old_y, new_x, new_y);
768 if (draw_wrap)
770 rb->lcd_drawline(old_x + LCD_WIDTH, old_y, new_x + LCD_WIDTH, new_y);
771 rb->lcd_drawline(old_x, old_y + LCD_HEIGHT, new_x, new_y + LCD_HEIGHT);
772 rb->lcd_drawline(old_x + LCD_WIDTH, old_y + LCD_HEIGHT,
773 new_x + LCD_WIDTH, new_y + LCD_HEIGHT);
775 old_x = new_x;
776 old_y = new_y;
777 p++;
781 static void move_point(struct Point* point)
783 point->x += point->dx;
784 point->y += point->dy;
786 /* Check bounds on the x-axis: */
787 point->x %= SCALED_WIDTH;
788 if (point->x < 0)
789 point->x += SCALED_WIDTH;
791 /* Check bounds on the y-axis: */
792 point->y %= SCALED_HEIGHT;
793 if (point->y < 0)
794 point->y += SCALED_HEIGHT;
797 /*************************************************
798 ** Handle trail blaiz.
799 *************************************************/
801 static void create_ship_trail(struct TrailPoint* tpoint)
803 tpoint->position.x += ship.vertices[2].x;
804 tpoint->position.y += ship.vertices[2].y;
805 tpoint->position.dx = -( ship.vertices[0].x - ship.vertices[2].x )/10;
806 tpoint->position.dy = -( ship.vertices[0].y - ship.vertices[2].y )/10;
809 static void create_explosion_trail(struct TrailPoint* tpoint)
811 tpoint->position.dx = (rb->rand()%5001)-2500;
812 tpoint->position.dy = (rb->rand()%5001)-2500;
815 static void create_trail_blaze(int colour, struct Point* position)
817 int numtoadd;
818 struct TrailPoint* tpoint;
819 int n;
821 if (colour != EXPLOSION_SHIP)
823 numtoadd = NUM_TRAIL_POINTS/5;
825 else
827 numtoadd = NUM_TRAIL_POINTS/8;
830 /* give the point a random countdown timer, so they dissapears at different
831 times */
832 tpoint = trail_points;
833 n = NUM_TRAIL_POINTS;
834 while (n--)
836 /* find a space in the array of trail_points that is NULL or DEAD or
837 whatever and place this one here. */
838 if (tpoint->alive <= 0)
840 /* take a random point near the position. */
841 tpoint->position.x = (rb->rand()%18000)-9000 + position->x;
842 tpoint->position.y = (rb->rand()%18000)-9000 + position->y;
844 switch(colour)
846 case EXPLOSION_SHIP:
847 create_explosion_trail(tpoint);
848 tpoint->alive = 51;
849 #ifdef HAVE_LCD_COLOR
850 tpoint->r = SHIP_R;
851 tpoint->g = SHIP_G;
852 tpoint->b = SHIP_B;
853 tpoint->dec = 2;
854 #endif
855 break;
856 case EXPLOSION_ASTEROID:
857 create_explosion_trail(tpoint);
858 tpoint->alive = 51;
859 #ifdef HAVE_LCD_COLOR
860 tpoint->r = ASTEROID_R;
861 tpoint->g = ASTEROID_G;
862 tpoint->b = ASTEROID_B;
863 tpoint->dec = 2;
864 #endif
865 break;
866 case EXPLOSION_ENEMY:
867 create_explosion_trail(tpoint);
868 tpoint->alive = 51;
869 #ifdef HAVE_LCD_COLOR
870 tpoint->r = ENEMY_R;
871 tpoint->g = ENEMY_G;
872 tpoint->b = ENEMY_B;
873 tpoint->dec = 2;
874 #endif
875 break;
876 case EXPLOSION_THRUST:
877 create_ship_trail(tpoint);
878 tpoint->alive = 17;
879 #ifdef HAVE_LCD_COLOR
880 tpoint->r = THRUST_R;
881 tpoint->g = THRUST_G;
882 tpoint->b = THRUST_B;
883 tpoint->dec = 4;
884 #endif
885 break;
888 /* give the points a speed based on direction of travel
889 - i.e. opposite */
890 tpoint->position.dx += position->dx;
891 tpoint->position.dy += position->dy;
893 numtoadd--;
894 if (numtoadd <= 0)
895 break;
897 tpoint++;
901 static void draw_and_move_trail_blaze(void)
903 struct TrailPoint* tpoint;
904 int n;
906 /* loop through, if alive then move and draw.
907 when drawn, countdown it's timer.
908 if zero kill it! */
910 tpoint = trail_points;
911 n = NUM_TRAIL_POINTS;
912 while (n--)
914 if (tpoint->alive > 0)
916 if (game_state != PAUSE_MODE)
918 tpoint->alive--;
919 move_point(&(tpoint->position));
920 #ifdef HAVE_LCD_COLOR
921 /* intensity = tpoint->alive/2; */
922 if (tpoint->r >= tpoint->dec) tpoint->r -= tpoint->dec;
923 if (tpoint->g >= tpoint->dec) tpoint->g -= tpoint->dec;
924 if (tpoint->b >= tpoint->dec) tpoint->b -= tpoint->dec;
925 #endif
927 SET_FG(LCD_RGBPACK(tpoint->r, tpoint->g, tpoint->b));
928 rb->lcd_drawpixel(tpoint->position.x/SCALE, tpoint->position.y/SCALE);
930 tpoint++;
934 /*************************************************
935 ** Handle asteroid.
936 *************************************************/
938 static void rotate_asteroid(struct Asteroid* asteroid)
940 rotate_polygon(asteroid->vertices, NUM_ASTEROID_VERTICES,
941 &asteroid->rotation,
942 asteroid->speed_cos, asteroid->speed_sin);
945 /* Initialise the passed Asteroid.
946 * if position is NULL, place it at the random loacation
947 * where ship doesn't exist
949 static void initialise_asteroid(struct Asteroid* asteroid,
950 enum asteroid_type type, struct Point *position)
952 const short *asteroid_vertices;
953 struct Point* point;
954 int n;
956 asteroid->exists = true;
957 asteroid->explode_countdown = 0;
958 asteroid->type = type;
960 /* Set the radius of the asteroid: */
961 asteroid->radius = (int)type*SCALE*3;
963 /* shall we move Clockwise and Fast */
964 n = rb->rand()%100;
965 if (n < 25)
967 asteroid->speed_cos = FAST_ROT_CW_COS;
968 asteroid->speed_sin = FAST_ROT_CW_SIN;
970 else if (n < 50)
972 asteroid->speed_cos = FAST_ROT_ACW_COS;
973 asteroid->speed_sin = FAST_ROT_ACW_SIN;
975 else if (n < 75)
977 asteroid->speed_cos = SLOW_ROT_ACW_COS;
978 asteroid->speed_sin = SLOW_ROT_ACW_SIN;
980 else
982 asteroid->speed_cos = SLOW_ROT_CW_COS;
983 asteroid->speed_sin = SLOW_ROT_CW_SIN;
986 n = rb->rand()%99;
987 if (n < 33)
988 asteroid_vertices = asteroid_one;
989 else if (n < 66)
990 asteroid_vertices = asteroid_two;
991 else
992 asteroid_vertices = asteroid_three;
994 point = asteroid->vertices;
995 for(n = 0; n < NUM_ASTEROID_VERTICES*2; n += 2)
997 point->x = asteroid_vertices[n];
998 point->y = asteroid_vertices[n+1];
999 point->x *= asteroid->radius/20;
1000 point->y *= asteroid->radius/20;
1001 /* dx and dy are used when rotate polygon */
1002 point->dx = point->x;
1003 point->dy = point->y;
1004 point++;
1007 if (!position)
1009 do {
1010 /* Set the position randomly: */
1011 asteroid->position.x = (rb->rand()%SCALED_WIDTH);
1012 asteroid->position.y = (rb->rand()%SCALED_HEIGHT);
1013 } while (is_point_within_rectangle(&ship.position, &asteroid->position,
1014 SPACE_CHECK_SIZE));
1016 else
1018 asteroid->position.x = position->x;
1019 asteroid->position.y = position->y;
1022 do {
1023 asteroid->position.dx = (rb->rand()%ASTEROID_SPEED)-ASTEROID_SPEED/2;
1024 } while (asteroid->position.dx == 0);
1026 do {
1027 asteroid->position.dy = (rb->rand()%ASTEROID_SPEED)-ASTEROID_SPEED/2;
1028 } while (asteroid->position.dy == 0);
1030 asteroid->position.dx *= SCALE/10;
1031 asteroid->position.dy *= SCALE/10;
1033 asteroid->rotation.x = SIN_COS_SCALE;
1034 asteroid->rotation.y = 0;
1036 /* Now rotate the asteroid a bit, so they all look a bit different */
1037 for(n = (rb->rand()%30)+2; n--; )
1038 rotate_asteroid(asteroid);
1040 /* great, we've created an asteroid, don't forget to increment the total: */
1041 asteroid_count++;
1045 * Creates a new asteroid of the given 4type (size) and at the given location.
1047 static void create_asteroid(enum asteroid_type type, struct Point *position)
1049 struct Asteroid* asteroid;
1050 int n;
1052 asteroid = asteroids_array;
1053 n = MAX_NUM_ASTEROIDS;
1054 while (n--)
1056 if (!asteroid->exists && asteroid->explode_countdown <= 0)
1058 initialise_asteroid(asteroid, type, position);
1059 break;
1061 asteroid++;
1065 /* Draw and move all asteroids */
1066 static void draw_and_move_asteroids(void)
1068 struct Asteroid* asteroid;
1069 int n;
1071 SET_FG(COL_ASTEROID);
1073 asteroid = asteroids_array;
1074 n = MAX_NUM_ASTEROIDS;
1075 while (n--)
1077 if (asteroid->exists)
1079 draw_polygon(asteroid->vertices, NUM_ASTEROID_VERTICES,
1080 asteroid->position.x, asteroid->position.y);
1082 if (game_state != PAUSE_MODE)
1084 if (asteroid->exists)
1086 move_point(&asteroid->position);
1087 rotate_asteroid(asteroid);
1089 else if (asteroid->explode_countdown > 0)
1091 asteroid->explode_countdown--;
1094 asteroid++;
1098 static void explode_asteroid(struct Asteroid* asteroid)
1100 struct Point p;
1101 p.dx = asteroid->position.dx;
1102 p.dy = asteroid->position.dy;
1103 p.x = asteroid->position.x;
1104 p.y = asteroid->position.y;
1106 asteroid_count--;
1107 asteroid->exists = false;
1109 switch(asteroid->type)
1111 case SMALL:
1112 asteroid->explode_countdown = EXPLOSION_LENGTH;
1113 create_trail_blaze(EXPLOSION_ASTEROID, &p);
1114 break;
1116 case MEDIUM:
1117 create_asteroid(SMALL, &p);
1118 create_asteroid(SMALL, &p);
1119 break;
1121 case LARGE:
1122 create_asteroid(MEDIUM, &p);
1123 create_asteroid(MEDIUM, &p);
1124 break;
1128 /*************************************************
1129 ** Handle ship.
1130 *************************************************/
1132 /* Initialise the ship */
1133 static void initialise_ship(void)
1135 struct Point* point;
1136 struct Point* lives_point;
1137 int n;
1139 ship.position.x = CENTER_LCD_X * SCALE;
1140 ship.position.y = CENTER_LCD_Y * SCALE;
1141 ship.position.dx = 0;
1142 ship.position.dy = 0;
1143 ship.rotation.x = SIN_COS_SCALE;
1144 ship.rotation.y = 0;
1145 ship.exists = true;
1146 ship.explode_countdown = 0;
1147 ship.invulnerable_time = INVULNERABLE_TIME;
1149 point = ship.vertices;
1150 lives_point = lives_points;
1151 for(n = 0; n < NUM_SHIP_VERTICES*2; n += 2)
1153 point->x = ship_vertices[n];
1154 point->y = ship_vertices[n+1];
1155 point->x *= SCALE;
1156 point->y *= SCALE;
1157 /* dx and dy are used when rotate polygon */
1158 point->dx = point->x;
1159 point->dy = point->y;
1160 /* grab a copy of the ships points for the lives display: */
1161 lives_point->x = point->x;
1162 lives_point->y = point->y;
1164 point++;
1165 lives_point++;
1170 * Draws the ship, moves the ship and creates a new
1171 * one if it's finished exploding.
1173 static void draw_and_move_ship(void)
1175 if (ship.invulnerable_time > BLINK_TIME || ship.invulnerable_time % 2 != 0)
1177 SET_FG(COL_INVULN);
1179 else
1181 SET_FG(COL_PLAYER);
1184 if (ship.exists)
1186 draw_polygon(ship.vertices, NUM_SHIP_VERTICES,
1187 ship.position.x, ship.position.y);
1190 if (game_state != PAUSE_MODE)
1192 if (ship.exists)
1194 if (ship.invulnerable_time > 0)
1195 ship.invulnerable_time--;
1196 move_point(&ship.position);
1198 else if (ship.explode_countdown > 0)
1200 ship.explode_countdown--;
1201 if (ship.explode_countdown <= 0)
1203 num_lives--;
1204 if (num_lives <= 0)
1206 game_state = GAME_OVER;
1208 else
1210 initialise_ship();
1217 static void explode_ship(void)
1219 if (!ship.invulnerable_time)
1221 /* if not invulnerable, blow up ship */
1222 ship.explode_countdown = EXPLOSION_LENGTH;
1223 ship.exists = false;
1224 create_trail_blaze(EXPLOSION_SHIP, &ship.position);
1228 /* Rotate the ship using the passed sin & cos values */
1229 static void rotate_ship(int cos, int sin)
1231 if (ship.exists)
1233 rotate_polygon(ship.vertices, NUM_SHIP_VERTICES,
1234 &ship.rotation, cos, sin);
1238 static void thrust_ship(void)
1240 if (ship.exists)
1242 ship.position.dx += ( ship.vertices[0].x - ship.vertices[2].x )/20;
1243 ship.position.dy += ( ship.vertices[0].y - ship.vertices[2].y )/20;
1245 /* if dx and dy are below a certain threshold, then set 'em to 0
1246 but to do this we need to ascertain if the spacehip as moved on
1247 screen for more than a certain amount. */
1249 create_trail_blaze(EXPLOSION_THRUST, &ship.position);
1253 /* stop movement of ship, 'cos that's what happens when you go into hyperspace. */
1254 static void hyperspace(void)
1256 if (ship.exists)
1258 ship.position.dx = ship.position.dy = 0;
1259 ship.position.x = (rb->rand()%SCALED_WIDTH);
1260 ship.position.y = (rb->rand()%SCALED_HEIGHT);
1264 static void draw_lives(void)
1266 int n;
1267 #if (LARGE_LCD)
1268 int px = (LCD_WIDTH-1 - 4)*SCALE;
1269 int py = (LCD_HEIGHT-1 - 6)*SCALE;
1270 #else
1271 int px = (LCD_WIDTH-1 - 3)*SCALE;
1272 int py = (LCD_HEIGHT-1 - 4)*SCALE;
1273 #endif
1275 SET_FG(COL_PLAYER);
1277 n = num_lives-1;
1278 while (n--)
1280 draw_polygon(lives_points, NUM_SHIP_VERTICES, px, py);
1281 #if (LARGE_LCD)
1282 px -= 8*SCALE;
1283 #else
1284 px -= 6*SCALE;
1285 #endif
1290 * missile
1293 /* Initialise a missile */
1294 static void initialise_missile(struct Missile* missile)
1296 missile->position.x = ship.position.x + ship.vertices[0].x;
1297 missile->position.y = ship.position.y + ship.vertices[0].y;
1298 missile->position.dx = (ship.vertices[0].x - ship.vertices[2].x)/2;
1299 missile->position.dy = (ship.vertices[0].y - ship.vertices[2].y)/2;
1300 missile->alive = MISSILE_LIFE_LENGTH;
1301 missile->oldpoint.x = missile->position.x;
1302 missile->oldpoint.y = missile->position.y;
1305 /* Fire the next missile */
1306 static void fire_missile(void)
1308 struct Missile* missile;
1309 int n;
1311 if (ship.exists)
1313 missile = missiles_array;
1314 n = MAX_NUM_MISSILES;
1315 while (n--)
1317 if (missile->alive <= 0)
1319 initialise_missile(missile);
1320 break;
1322 missile++;
1327 /* Draw and Move all the missiles */
1328 static void draw_and_move_missiles(void)
1330 struct Missile* missile;
1331 struct Point vertices[2];
1332 int n;
1334 SET_FG(COL_MISSILE);
1336 missile = missiles_array;
1337 n = MAX_NUM_MISSILES;
1338 while (n--)
1340 if (missile->alive > 0)
1342 vertices[0].x = 0;
1343 vertices[0].y = 0;
1344 vertices[1].x = -missile->position.dx;
1345 vertices[1].y = -missile->position.dy;
1346 draw_polygon(vertices, 2, missile->position.x, missile->position.y);
1348 if (game_state != PAUSE_MODE)
1350 missile->oldpoint.x = missile->position.x;
1351 missile->oldpoint.y = missile->position.y;
1352 move_point(&missile->position);
1353 missile->alive--;
1356 missile++;
1360 /*************************************************
1361 ** Handle enemy.
1362 *************************************************/
1364 static void initialise_enemy(void)
1366 struct Point* point;
1367 int n;
1368 int size;
1370 if (rb->rand()%100 > enemy.size_probability)
1372 size = BIG_SHIP;
1373 enemy.size_probability++;
1374 if (enemy.size_probability > 90)
1376 enemy.size_probability = ENEMY_BIG_PROBABILITY_START;
1379 else
1381 size = LITTLE_SHIP;
1382 enemy.size_probability = ENEMY_BIG_PROBABILITY_START;
1385 enemy.exists = true;
1386 enemy.explode_countdown = 0;
1387 enemy.appear_countdown = enemy.appear_timing;
1389 point = enemy.vertices;
1390 for(n = 0; n < NUM_ENEMY_VERTICES*2; n += 2)
1392 point->x = enemy_vertices[n];
1393 point->y = enemy_vertices[n+1];
1394 point->x *= size*SCALE/2;
1395 point->y *= size*SCALE/2;
1396 point++;
1399 if (ship.position.x >= SCALED_WIDTH/2)
1401 enemy.position.dx = ENEMY_SPEED;
1402 enemy.position.x = 0;
1404 else
1406 enemy.position.dx = -ENEMY_SPEED;
1407 enemy.position.x = SCALED_WIDTH;
1410 if (ship.position.y >= SCALED_HEIGHT/2)
1412 enemy.position.dy = ENEMY_SPEED;
1413 enemy.position.y = 0;
1415 else
1417 enemy.position.dy = -ENEMY_SPEED;
1418 enemy.position.y = SCALED_HEIGHT;
1421 enemy.position.dx *= SCALE/10;
1422 enemy.position.dy *= SCALE/10;
1425 static void draw_and_move_enemy(void)
1427 SET_FG(COL_ENEMY);
1429 if (enemy.exists)
1431 draw_polygon(enemy.vertices, NUM_ENEMY_VERTICES,
1432 enemy.position.x, enemy.position.y);
1435 if (game_state != PAUSE_MODE)
1437 if (enemy.exists)
1439 enemy.position.x += enemy.position.dx;
1440 enemy.position.y += enemy.position.dy;
1442 if (enemy.position.x > SCALED_WIDTH || enemy.position.x < 0)
1443 enemy.exists = false;
1445 enemy.position.y %= SCALED_HEIGHT;
1446 if (enemy.position.y < 0)
1447 enemy.position.y += SCALED_HEIGHT;
1449 if ((rb->rand()%1000) < 10)
1450 enemy.position.dy = -enemy.position.dy;
1452 else if (enemy.explode_countdown > 0)
1454 enemy.explode_countdown--;
1456 else
1458 if (enemy.appear_countdown > 0)
1459 enemy.appear_countdown--;
1460 else if (rb->rand()%100 >= enemy.appear_probability)
1461 initialise_enemy();
1465 if (enemy_missile.alive <= 0)
1467 /* if no missile and the enemy is here and not exploding..
1468 then shoot baby! */
1469 if (enemy.exists && ship.exists &&
1470 game_state == PLAY_MODE && (rb->rand()%10) >= 5 )
1472 int dx = ship.position.x - enemy.position.x;
1473 int dy = ship.position.y - enemy.position.y;
1475 if (dx < -SCALED_WIDTH/2) dx += SCALED_WIDTH;
1476 else if (dx > SCALED_WIDTH/2) dx -= SCALED_WIDTH;
1477 if (dy < -SCALED_HEIGHT/2) dy += SCALED_HEIGHT;
1478 else if (dy > SCALED_HEIGHT/2) dy -= SCALED_HEIGHT;
1480 enemy_missile.position.x = enemy.position.x;
1481 enemy_missile.position.y = enemy.position.y;
1483 /* lame, needs to be sorted - it's trying to shoot at the ship */
1484 if (dx < -5*SCALE)
1485 enemy_missile.position.dx = -1;
1486 else if (dx > 5*SCALE)
1487 enemy_missile.position.dx = 1;
1488 else
1489 enemy_missile.position.dx = 0;
1491 if (dy < -5*SCALE)
1492 enemy_missile.position.dy = -1;
1493 else if (dy > 5*SCALE)
1494 enemy_missile.position.dy = 1;
1495 else
1496 enemy_missile.position.dy = 0;
1498 while (enemy_missile.position.dx == 0 &&
1499 enemy_missile.position.dy == 0)
1501 enemy_missile.position.dx = rb->rand()%2-1;
1502 enemy_missile.position.dy = rb->rand()%2-1;
1505 enemy_missile.position.dx *= SCALE;
1506 enemy_missile.position.dy *= SCALE;
1507 enemy_missile.alive = ENEMY_MISSILE_LIFE_LENGTH;
1510 else
1512 rb->lcd_fillrect( enemy_missile.position.x/SCALE,
1513 enemy_missile.position.y/SCALE,
1514 POINT_SIZE, POINT_SIZE );
1515 if (game_state != PAUSE_MODE)
1517 move_point(&enemy_missile.position);
1518 enemy_missile.alive--;
1523 /*************************************************
1524 ** Check collisions.
1525 *************************************************/
1527 /* Add score if missile hit asteroid or enemy */
1528 static void add_score(int val)
1530 current_score += val;
1531 if (current_score >= extra_life)
1533 num_lives++;
1534 extra_life += EXTRA_LIFE;
1538 static bool is_point_within_asteroid(struct Asteroid* asteroid,
1539 struct Point* point)
1541 if (is_point_within_rectangle(&asteroid->position, point, asteroid->radius)
1542 && is_point_in_polygon(asteroid->vertices, NUM_ASTEROID_VERTICES,
1543 point->x - asteroid->position.x,
1544 point->y - asteroid->position.y))
1546 explode_asteroid(asteroid);
1547 return true;
1549 else
1550 return false;
1553 static bool is_point_within_ship(struct Point* point)
1555 if (is_point_within_rectangle(&ship.position, point, SIZE_SHIP_COLLISION)
1556 && is_point_in_polygon(ship.vertices, NUM_SHIP_VERTICES,
1557 point->x - ship.position.x,
1558 point->y - ship.position.y))
1560 return true;
1562 else
1563 return false;
1566 static bool is_point_within_enemy(struct Point* point)
1568 if (is_point_within_rectangle(&enemy.position, point, SIZE_ENEMY_COLLISION))
1570 add_score(5);
1571 enemy.explode_countdown = EXPLOSION_LENGTH;
1572 enemy.exists = false;
1573 create_trail_blaze(EXPLOSION_ENEMY, &enemy.position);
1574 return true;
1576 else
1577 return false;
1580 static bool is_ship_within_asteroid(struct Asteroid* asteroid)
1582 struct Point p;
1584 if (!is_point_within_rectangle(&asteroid->position, &ship.position,
1585 asteroid->radius+SIZE_SHIP_COLLISION))
1586 return false;
1588 p.x = ship.position.x + ship.vertices[0].x;
1589 p.y = ship.position.y + ship.vertices[0].y;
1590 if (is_point_within_asteroid(asteroid, &p))
1591 return true;
1593 p.x = ship.position.x + ship.vertices[1].x;
1594 p.y = ship.position.y + ship.vertices[1].y;
1595 if (is_point_within_asteroid(asteroid, &p))
1596 return true;
1598 p.x = ship.position.x + ship.vertices[3].x;
1599 p.y = ship.position.y + ship.vertices[3].y;
1600 if (is_point_within_asteroid(asteroid, &p))
1601 return true;
1603 return false;
1606 /* Check for collsions between the missiles and the asteroids and the ship */
1607 static void check_collisions(void)
1609 struct Missile* missile;
1610 struct Asteroid* asteroid;
1611 int m, n;
1612 bool asteroids_onscreen = false;
1614 asteroid = asteroids_array;
1615 m = MAX_NUM_ASTEROIDS;
1616 while (m--)
1618 /* if the asteroids exists then test missile collision: */
1619 if (asteroid->exists)
1621 missile = missiles_array;
1622 n = MAX_NUM_MISSILES;
1623 while (n--)
1625 /* if the missiles exists: */
1626 if (missile->alive > 0)
1628 /* has the missile hit the asteroid? */
1629 if (is_point_within_asteroid(asteroid, &missile->position) ||
1630 is_point_within_asteroid(asteroid, &missile->oldpoint))
1632 add_score(1);
1633 missile->alive = 0;
1634 break;
1637 missile++;
1640 /* now check collision with ship: */
1641 if (asteroid->exists && ship.exists)
1643 if (is_ship_within_asteroid(asteroid))
1645 add_score(1);
1646 explode_ship();
1650 /* has the enemy missile blown something up? */
1651 if (asteroid->exists && enemy_missile.alive > 0)
1653 if (is_point_within_asteroid(asteroid, &enemy_missile.position))
1655 enemy_missile.alive = 0;
1660 /* is an asteroid still exploding? */
1661 if (asteroid->explode_countdown > 0)
1662 asteroids_onscreen = true;
1664 asteroid++;
1667 /* now check collision between ship and enemy */
1668 if (enemy.exists && ship.exists)
1670 /* has the enemy collided with the ship? */
1671 if (is_point_within_enemy(&ship.position))
1673 explode_ship();
1674 create_trail_blaze(EXPLOSION_ENEMY, &enemy.position);
1677 if (enemy.exists)
1679 /* Now see if the enemy has been shot at by the ships missiles: */
1680 missile = missiles_array;
1681 n = MAX_NUM_MISSILES;
1682 while (n--)
1684 if (missile->alive > 0 &&
1685 is_point_within_enemy(&missile->position))
1687 missile->alive = 0;
1688 break;
1690 missile++;
1695 /* test collision with enemy missile and ship: */
1696 if (enemy_missile.alive > 0 && is_point_within_ship(&enemy_missile.position))
1698 explode_ship();
1699 enemy_missile.alive = 0;
1700 enemy_missile.position.x = enemy_missile.position.y = 0;
1703 /* if all asteroids cleared then start again: */
1704 if (asteroid_count == 0 && !asteroids_onscreen
1705 && !enemy.exists && enemy.explode_countdown <= 0)
1707 current_level++;
1708 if (current_level > MAX_LEVEL)
1709 current_level = START_LEVEL;
1710 enemy.appear_probability += 5;
1711 if (enemy.appear_probability >= 100)
1712 enemy.appear_probability = ENEMY_APPEAR_PROBABILITY_START;
1713 enemy.appear_timing -= 30;
1714 if (enemy.appear_timing < 30)
1715 enemy.appear_timing = 30;
1716 game_state = SHOW_LEVEL;
1717 show_level_timeout = SHOW_LEVEL_TIME;
1722 * stars
1725 static void create_stars(void)
1727 struct Point* p;
1728 int n;
1730 p = stars;
1731 n = NUM_STARS;
1732 while (n--)
1734 p->x = (rb->rand()%LCD_WIDTH);
1735 p->y = (rb->rand()%LCD_HEIGHT);
1736 p++;
1740 static void drawstars(void)
1742 struct Point* p;
1743 int n;
1745 SET_FG(COL_STARS);
1747 p = stars;
1748 n = NUM_STARS;
1749 while (n--)
1751 rb->lcd_drawpixel(p->x , p->y);
1752 p++;
1756 /*************************************************
1757 ** Creates start_num number of new asteroids of
1758 ** full size.
1759 **************************************************/
1760 static void initialise_level(int start_num)
1762 struct Asteroid* asteroid;
1763 struct Missile* missile;
1764 struct TrailPoint* tpoint;
1765 int n;
1766 asteroid_count = next_missile_count = next_thrust_count = 0;
1768 /* no enemy */
1769 enemy.exists = 0;
1770 enemy.explode_countdown = 0;
1771 enemy_missile.alive = 0;
1773 /* clear asteroids */
1774 asteroid = asteroids_array;
1775 n = MAX_NUM_ASTEROIDS;
1776 while (n--)
1778 asteroid->exists = false;
1779 asteroid++;
1782 /* make some LARGE asteroids */
1783 for(n = 0; n < start_num; n++)
1784 initialise_asteroid(&asteroids_array[n], LARGE, NULL);
1786 /* ensure all missiles are out of action: */
1787 missile = missiles_array;
1788 n = MAX_NUM_MISSILES;
1789 while (n--)
1791 missile->alive = 0;
1792 missile++;
1795 tpoint = trail_points;
1796 n = NUM_TRAIL_POINTS;
1797 while (n--)
1799 tpoint->alive = 0;
1800 tpoint++;
1804 static void initialise_game(void)
1806 enemy.appear_probability = ENEMY_APPEAR_PROBABILITY_START;
1807 enemy.appear_timing = ENEMY_APPEAR_TIMING_START;
1808 enemy.appear_countdown = enemy.appear_timing;
1809 enemy.size_probability = ENEMY_BIG_PROBABILITY_START;
1810 current_level = START_LEVEL;
1811 num_lives = START_LIVES;
1812 extra_life = EXTRA_LIFE;
1813 current_score = 0;
1814 initialise_ship();
1815 initialise_level(0);
1816 game_state = SHOW_LEVEL;
1817 show_level_timeout = SHOW_LEVEL_TIME;
1820 /* menu stuff */
1821 static bool spacerocks_help(void)
1823 static char *help_text[] = {
1824 "Spacerocks", "", "Aim", "",
1825 "The", "goal", "of", "the", "game", "is", "to", "blow", "up",
1826 "the", "asteroids", "and", "avoid", "being", "hit", "by", "them.",
1827 "Also", "you'd", "better", "watch", "out", "for", "the", "UFOs!"
1829 static struct style_text formation[]={
1830 { 0, TEXT_CENTER|TEXT_UNDERLINE },
1831 { 2, C_RED },
1832 LAST_STYLE_ITEM
1835 rb->lcd_setfont(FONT_UI);
1836 SET_BG(LCD_BLACK);
1837 SET_FG(LCD_WHITE);
1838 if (display_text(ARRAYLEN(help_text), help_text, formation, NULL, true))
1839 return true;
1840 rb->lcd_setfont(FONT_SYSFIXED);
1842 return false;
1845 #define PLUGIN_OTHER 10
1846 static bool ingame;
1847 static int spacerocks_menu_cb(int action, const struct menu_item_ex *this_item)
1849 if (action == ACTION_REQUEST_MENUITEM
1850 && !ingame && ((intptr_t)this_item)==0)
1851 return ACTION_EXIT_MENUITEM;
1852 return action;
1855 static int spacerocks_menu(void)
1857 int selection = 0;
1858 MENUITEM_STRINGLIST(main_menu, "Spacerocks Menu", spacerocks_menu_cb,
1859 "Resume Game", "Start New Game",
1860 "Help", "High Scores",
1861 "Playback Control", "Quit");
1862 rb->button_clear_queue();
1864 while (1)
1866 switch (rb->do_menu(&main_menu, &selection, NULL, false))
1868 case 0:
1869 return PLUGIN_OTHER;
1870 case 1:
1871 initialise_game();
1872 return PLUGIN_OTHER;
1873 case 2:
1874 if (spacerocks_help())
1875 return PLUGIN_USB_CONNECTED;
1876 break;
1877 case 3:
1878 highscore_show(-1, highscores, NUM_SCORES, true);
1879 break;
1880 case 4:
1881 playback_control(NULL);
1882 break;
1883 case 5:
1884 return PLUGIN_OK;
1885 case MENU_ATTACHED_USB:
1886 return PLUGIN_USB_CONNECTED;
1887 default:
1888 break;
1893 static int spacerocks_game_loop(void)
1895 int button;
1896 int end;
1897 int position;
1898 int ret;
1900 if ((ret = spacerocks_menu()) != PLUGIN_OTHER)
1901 return ret;
1903 SET_BG(LCD_BLACK);
1905 ingame = true;
1906 while (true)
1908 end = *rb->current_tick + (CYCLETIME * HZ) / 1000;
1909 rb->lcd_clear_display();
1910 SET_FG(COL_TEXT);
1911 switch(game_state)
1913 case GAME_OVER:
1914 ingame = false;
1915 rb->splash (HZ * 2, "Game Over");
1916 rb->lcd_clear_display();
1917 position = highscore_update(current_score, current_level, "",
1918 highscores, NUM_SCORES);
1919 if (position != -1)
1921 if (position == 0)
1922 rb->splash(HZ*2, "New High Score");
1923 highscore_show(position, highscores, NUM_SCORES, true);
1925 return PLUGIN_OTHER;
1926 break;
1928 case PAUSE_MODE:
1929 rb->lcd_putsxyf(1,LCD_HEIGHT-8, "score %d ", current_score);
1930 rb->lcd_putsxy(CENTER_LCD_X - 15,
1931 CENTER_LCD_Y + CENTER_LCD_Y/2 - 4, "pause");
1932 draw_and_move_missiles();
1933 draw_lives();
1934 draw_and_move_ship();
1935 break;
1937 case PLAY_MODE:
1938 rb->lcd_putsxyf(1, LCD_HEIGHT-8, "score %d ", current_score);
1939 draw_and_move_missiles();
1940 draw_lives();
1941 check_collisions();
1942 draw_and_move_ship();
1943 break;
1945 case SHOW_LEVEL:
1946 rb->lcd_putsxyf(1, LCD_HEIGHT-8, "score %d ", current_score);
1947 rb->lcd_putsxyf(CENTER_LCD_X - 20,
1948 CENTER_LCD_Y + CENTER_LCD_Y/2 - 4,
1949 "stage %d ", current_level);
1950 draw_lives();
1951 draw_and_move_ship();
1952 show_level_timeout--;
1953 if (show_level_timeout <= 0)
1955 initialise_level(current_level);
1956 game_state = PLAY_MODE;
1958 break;
1960 draw_and_move_trail_blaze();
1961 drawstars();
1962 draw_and_move_asteroids();
1963 draw_and_move_enemy();
1965 rb->lcd_update();
1967 #ifdef HAS_BUTTON_HOLD
1968 if (rb->button_hold() && game_state == PLAY_MODE)
1969 game_state = PAUSE_MODE;
1970 #endif
1971 button = rb->button_get(false);
1972 switch(button)
1974 case(AST_QUIT):
1975 return PLUGIN_OTHER;
1976 break;
1977 #ifdef AST_PAUSE
1978 case(AST_PAUSE):
1979 if (game_state == PAUSE_MODE)
1980 game_state = PLAY_MODE;
1981 else if (game_state == PLAY_MODE)
1982 game_state = PAUSE_MODE;
1983 break;
1984 #endif
1985 case (AST_LEFT):
1986 case (AST_LEFT | BUTTON_REPEAT):
1987 if (game_state == PLAY_MODE || game_state == SHOW_LEVEL)
1988 rotate_ship(SHIP_ROT_ACW_COS, SHIP_ROT_ACW_SIN);
1989 break;
1991 case (AST_RIGHT):
1992 case (AST_RIGHT | BUTTON_REPEAT):
1993 if (game_state == PLAY_MODE || game_state == SHOW_LEVEL)
1994 rotate_ship(SHIP_ROT_CW_COS, SHIP_ROT_CW_SIN);
1995 break;
1997 case (AST_THRUST):
1998 case (AST_THRUST | BUTTON_REPEAT):
1999 if (game_state == PLAY_MODE || game_state == SHOW_LEVEL)
2001 if (next_thrust_count <= 0)
2003 next_thrust_count = 5;
2004 thrust_ship();
2007 break;
2009 case (AST_HYPERSPACE):
2010 if (game_state == PLAY_MODE)
2011 hyperspace();
2012 /* maybe shield if it gets too hard */
2013 break;
2015 case (AST_FIRE):
2016 case (AST_FIRE | BUTTON_REPEAT):
2017 if (game_state == PLAY_MODE)
2019 if (next_missile_count <= 0)
2021 fire_missile();
2022 next_missile_count = 10;
2025 else if(game_state == PAUSE_MODE)
2026 game_state = PLAY_MODE;
2027 break;
2029 default:
2030 if (rb->default_event_handler(button)==SYS_USB_CONNECTED)
2031 return PLUGIN_USB_CONNECTED;
2032 break;
2035 if (next_missile_count > 0)
2036 next_missile_count--;
2038 if (next_thrust_count > 0)
2039 next_thrust_count--;
2041 if (TIME_BEFORE(*rb->current_tick, end))
2042 rb->sleep(end-*rb->current_tick);
2043 else
2044 rb->yield();
2048 enum plugin_status plugin_start(const void* parameter)
2050 (void)parameter;
2051 int ret = PLUGIN_OTHER;
2053 #if LCD_DEPTH > 1
2054 rb->lcd_set_backdrop(NULL);
2055 #endif
2056 /* universal font */
2057 rb->lcd_setfont(FONT_SYSFIXED);
2058 /* Turn off backlight timeout */
2059 backlight_ignore_timeout();
2060 highscore_load(SCORE_FILE, highscores, NUM_SCORES);
2061 rb->srand(*rb->current_tick);
2063 /* create stars once, and once only: */
2064 create_stars();
2066 while (ret == PLUGIN_OTHER)
2067 ret = spacerocks_game_loop();
2069 rb->lcd_setfont(FONT_UI);
2070 highscore_save(SCORE_FILE, highscores, NUM_SCORES);
2071 /* Turn on backlight timeout (revert to settings) */
2072 backlight_use_settings();
2074 return ret;