spacerocks: Fix FS#9380: spacerocks shows funny rocks.
[kugel-rb.git] / apps / plugins / spacerocks.c
blob636aeddde70838fc5411a0bbcc1ed892179f27fb
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"
28 PLUGIN_HEADER
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 == COWOND2_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_SA9200_PAD
201 #define AST_PAUSE BUTTON_RIGHT
202 #define AST_QUIT BUTTON_POWER
203 #define AST_THRUST BUTTON_UP
204 #define AST_HYPERSPACE BUTTON_DOWN
205 #define AST_LEFT BUTTON_PREV
206 #define AST_RIGHT BUTTON_NEXT
207 #define AST_FIRE BUTTON_LEFT
209 #elif (CONFIG_KEYPAD == ONDAVX747_PAD) || \
210 (CONFIG_KEYPAD == ONDAVX777_PAD) || \
211 (CONFIG_KEYPAD == MROBE500_PAD)
212 #define AST_QUIT BUTTON_POWER
214 #elif (CONFIG_KEYPAD == SAMSUNG_YH_PAD)
215 #define AST_PAUSE BUTTON_FFWD
216 #define AST_QUIT BUTTON_REC
217 #define AST_THRUST BUTTON_UP
218 #define AST_HYPERSPACE BUTTON_DOWN
219 #define AST_LEFT BUTTON_LEFT
220 #define AST_RIGHT BUTTON_RIGHT
221 #define AST_FIRE BUTTON_PLAY
223 #else
224 #error No keymap defined!
225 #endif
227 #ifdef HAVE_TOUCHSCREEN
228 #ifndef AST_PAUSE
229 #define AST_PAUSE BUTTON_CENTER
230 #endif
231 #ifndef AST_QUIT
232 #define AST_QUIT BUTTON_TOPLEFT
233 #endif
234 #ifndef AST_THRUST
235 #define AST_THRUST BUTTON_TOPMIDDLE
236 #endif
237 #ifndef AST_HYPERSPACE
238 #define AST_HYPERSPACE BUTTON_TOPRIGHT
239 #endif
240 #ifndef AST_LEFT
241 #define AST_LEFT BUTTON_MIDLEFT
242 #endif
243 #ifndef AST_RIGHT
244 #define AST_RIGHT BUTTON_MIDRIGHT
245 #endif
246 #ifndef AST_FIRE
247 #define AST_FIRE BUTTON_BOTTOMMIDDLE
248 #endif
249 #endif
251 #define RES MAX(LCD_WIDTH, LCD_HEIGHT)
252 #define LARGE_LCD (RES >= 200)
254 #define CYCLETIME 30
256 #define SHOW_COL 0
257 #define SCALE 5000
258 #define WRAP_GAP (LARGE*SCALE*3)
259 #define POINT_SIZE 2
260 #define START_LEVEL 1
261 #define SHOW_LEVEL_TIME 50
262 #define EXPLOSION_LENGTH 20
264 #define MAX_NUM_ASTEROIDS 25
265 #define MAX_NUM_MISSILES 6
266 #define NUM_STARS 50
267 #define NUM_TRAIL_POINTS 70
268 #define MAX_LEVEL MAX_NUM_ASTEROIDS
270 #define NUM_ASTEROID_VERTICES 10
271 #define NUM_SHIP_VERTICES 4
272 #define NUM_ENEMY_VERTICES 8
274 #define INVULNERABLE_TIME 30
275 #define BLINK_TIME 10
276 #define EXTRA_LIFE 250
277 #define START_LIVES 3
278 #define MISSILE_LIFE_LENGTH 40
280 #define ASTEROID_SPEED (RES/20)
281 #define SPACE_CHECK_SIZE 30*SCALE
283 #if (LARGE_LCD)
284 #define SIZE_SHIP_COLLISION 8*SCALE
285 #else
286 #define SIZE_SHIP_COLLISION 6*SCALE
287 #endif
289 #define LITTLE_SHIP 1
290 #define BIG_SHIP 2
291 #define ENEMY_BIG_PROBABILITY_START 10
292 #define ENEMY_APPEAR_PROBABILITY_START 35
293 #define ENEMY_APPEAR_TIMING_START 600
294 #define ENEMY_SPEED 4
295 #define ENEMY_MISSILE_LIFE_LENGTH (RES/2)
296 #if (LARGE_LCD)
297 #define SIZE_ENEMY_COLLISION 7*SCALE
298 #else
299 #define SIZE_ENEMY_COLLISION 5*SCALE
300 #endif
302 #define SIN_COS_SCALE 10000
304 #define FAST_ROT_CW_SIN 873
305 #define FAST_ROT_CW_COS 9963
306 #define FAST_ROT_ACW_SIN -873
307 #define FAST_ROT_ACW_COS 9963
309 #define MEDIUM_ROT_CW_SIN 350
310 #define MEDIUM_ROT_CW_COS 9994
311 #define MEDIUM_ROT_ACW_SIN -350
312 #define MEDIUM_ROT_ACW_COS 9994
314 #define SLOW_ROT_CW_SIN 350
315 #define SLOW_ROT_CW_COS 9994
316 #define SLOW_ROT_ACW_SIN -350
317 #define SLOW_ROT_ACW_COS 9994
319 #ifdef HAVE_LCD_COLOR
320 #define SHIP_ROT_CW_SIN 2419
321 #define SHIP_ROT_CW_COS 9702
322 #define SHIP_ROT_ACW_SIN -2419
323 #define SHIP_ROT_ACW_COS 9702
324 #else
325 #define SHIP_ROT_CW_SIN 3827
326 #define SHIP_ROT_CW_COS 9239
327 #define SHIP_ROT_ACW_SIN -3827
328 #define SHIP_ROT_ACW_COS 9239
329 #endif
332 #define SCALED_WIDTH (LCD_WIDTH*SCALE)
333 #define SCALED_HEIGHT (LCD_HEIGHT*SCALE)
334 #define CENTER_LCD_X (LCD_WIDTH/2)
335 #define CENTER_LCD_Y (LCD_HEIGHT/2)
337 #ifdef HAVE_LCD_COLOR
338 #define ASTEROID_R 230
339 #define ASTEROID_G 200
340 #define ASTEROID_B 100
341 #define SHIP_R 255
342 #define SHIP_G 255
343 #define SHIP_B 255
344 #define ENEMY_R 50
345 #define ENEMY_G 220
346 #define ENEMY_B 50
347 #define THRUST_R 200
348 #define THRUST_G 200
349 #define THRUST_B 0
351 #define COL_MISSILE LCD_RGBPACK(200,0,0)
352 #define COL_PLAYER LCD_RGBPACK(200,200,200)
353 #define COL_INVULN LCD_RGBPACK(100,100,200)
354 #define COL_STARS LCD_WHITE
355 #define COL_ASTEROID LCD_RGBPACK(ASTEROID_R,ASTEROID_G,ASTEROID_B)
356 #define COL_TEXT LCD_RGBPACK(200,200,255)
357 #define COL_ENEMY LCD_RGBPACK(ENEMY_R,ENEMY_G,ENEMY_B)
358 #define SET_FG rb->lcd_set_foreground
359 #define SET_BG rb->lcd_set_background
360 #else
361 #define SET_FG(x)
362 #define SET_BG(x)
363 #endif
365 #define HIGH_SCORE PLUGIN_GAMES_DIR "/spacerocks.score"
366 #define NUM_SCORES 5
368 static struct highscore highscores[NUM_SCORES];
370 /* The array of points that make up an asteroid */
371 static const short asteroid_one[NUM_ASTEROID_VERTICES*2] =
373 -2, -12,
374 4, -8,
375 8, -14,
376 16, -5,
377 14, 0,
378 20, 2,
379 12, 14,
380 -4, 14,
381 -10, 6,
382 -10, -8,
385 /* The array of points that make up an asteroid */
386 static const short asteroid_two[NUM_ASTEROID_VERTICES*2] =
388 -2, -12,
389 4, -16,
390 6, -14,
391 16, -8,
392 14, 0,
393 20, 2,
394 12, 14,
395 -4, 14,
396 -10, 6,
397 -10, -8,
400 /* The array of points that make up an asteroid */
401 static const short asteroid_three[NUM_ASTEROID_VERTICES*2] =
403 -2, -12,
404 4, -16,
405 6, -14,
406 2, -8,
407 14, 0,
408 20, 2,
409 12, 14,
410 -4, 14,
411 -16, 6,
412 -10, -8,
415 /* The array of points the make up the ship */
416 static const short ship_vertices[NUM_SHIP_VERTICES*2] =
418 #if (LARGE_LCD)
419 0, -6,
420 4, 6,
421 0, 2,
422 -4, 6,
423 #else
424 0, -4,
425 3, 4,
426 0, 1,
427 -3, 4,
428 #endif
431 /* The array of points the make up the bad spaceship */
432 static const short enemy_vertices[NUM_ENEMY_VERTICES*2] =
434 #if (LARGE_LCD)
435 -8, 0,
436 -4, 4,
437 4, 4,
438 8, 0,
439 -8, 0,
440 8, 0,
441 4, -4,
442 -4, -4,
443 #else
444 -5, 0,
445 -2, 2,
446 2, 2,
447 5, 0,
448 -5, 0,
449 5, 0,
450 2, -2,
451 -2, -2,
452 #endif
455 enum asteroid_type
457 #if (LARGE_LCD)
458 SMALL = 2,
459 MEDIUM = 4,
460 LARGE = 6,
461 #else
462 SMALL = 1,
463 MEDIUM = 2,
464 LARGE = 3,
465 #endif
468 enum explosion_type
470 EXPLOSION_SHIP,
471 EXPLOSION_ASTEROID,
472 EXPLOSION_ENEMY,
473 EXPLOSION_THRUST,
476 enum game_state
478 GAME_OVER,
479 SHOW_LEVEL,
480 PLAY_MODE,
481 PAUSE_MODE,
484 struct Point
486 int x;
487 int y;
488 int dx;
489 int dy;
492 struct TrailPoint
494 struct Point position;
495 int alive;
496 #ifdef HAVE_LCD_COLOR
497 short r;
498 short g;
499 short b;
500 short dec;
501 #endif
504 /* Asteroid structure, contains an array of points */
505 struct Asteroid
507 struct Point position;
508 struct Point rotation;
509 struct Point vertices[NUM_ASTEROID_VERTICES];
510 bool exists;
511 int explode_countdown;
512 enum asteroid_type type;
513 int radius;
514 long speed_cos;
515 long speed_sin;
518 struct Ship
520 struct Point position;
521 struct Point rotation;
522 struct Point vertices[NUM_SHIP_VERTICES];
523 bool exists;
524 int explode_countdown;
525 int invulnerable_time;
528 struct Enemy
530 struct Point position;
531 struct Point vertices[NUM_ENEMY_VERTICES];
532 bool exists;
533 int explode_countdown;
534 int appear_countdown;
535 short size_probability;
536 short appear_probability;
537 short appear_timing;
540 struct Missile
542 struct Point position;
543 struct Point oldpoint;
544 int alive;
547 static enum game_state game_state;
548 static int asteroid_count;
549 static int next_missile_count;
550 static int next_thrust_count;
551 static int num_lives;
552 static int extra_life;
553 static int show_level_timeout;
554 static int current_level;
555 static int current_score;
557 static struct Ship ship;
558 static struct Point stars[NUM_STARS];
559 static struct Asteroid asteroids_array[MAX_NUM_ASTEROIDS];
560 static struct Missile missiles_array[MAX_NUM_MISSILES];
561 static struct Missile enemy_missile;
562 static struct Enemy enemy;
563 static struct Point lives_points[NUM_SHIP_VERTICES];
564 static struct TrailPoint trail_points[NUM_TRAIL_POINTS];
566 /*************************************************
567 ** Handle polygon and point
568 *************************************************/
570 /* Check if point is in a polygon */
571 static bool is_point_in_polygon(struct Point* vertices, int num_vertices,
572 int x, int y)
574 struct Point* pi;
575 struct Point* pj;
576 int n;
577 bool c = false;
579 if (x < -SCALED_WIDTH/2) x += SCALED_WIDTH;
580 else if (x > SCALED_WIDTH/2) x -= SCALED_WIDTH;
581 if (y < -SCALED_HEIGHT/2) y += SCALED_HEIGHT;
582 else if (y > SCALED_HEIGHT/2) y -= SCALED_HEIGHT;
584 pi = vertices;
585 pj = vertices + num_vertices-1;
587 n = num_vertices;
588 while (n--)
590 if ((((pi->y <= y) && (y < pj->y)) || ((pj->y <= y) && (y < pi->y))) &&
591 (x < (pj->x - pi->x) * (y - pi->y) / (pj->y - pi->y) + pi->x))
592 c = !c;
594 pj = pi;
595 pi++;
598 return c;
601 /* Check if point is within a rectangle */
602 static bool is_point_within_rectangle(struct Point* rect, struct Point* p,
603 int size)
605 int dx = p->x - rect->x;
606 int dy = p->y - rect->y;
607 #if SHOW_COL
608 rb->lcd_drawrect((rect->x - size)/SCALE, (rect->y - size)/SCALE,
609 (size*2+1)/SCALE, (size*2+1)/SCALE);
610 #endif
611 if (dx < -SCALED_WIDTH/2) dx += SCALED_WIDTH;
612 else if (dx > SCALED_WIDTH/2) dx -= SCALED_WIDTH;
613 if (dy < -SCALED_HEIGHT/2) dy += SCALED_HEIGHT;
614 else if (dy > SCALED_HEIGHT/2) dy -= SCALED_HEIGHT;
615 return (dx > -size && dx < size && dy > -size && dy < size);
618 /* Rotate polygon */
619 static void rotate_polygon(struct Point* vertices, int num_vertices,
620 struct Point* rotation, int cos, int sin)
622 struct Point* point;
623 int n;
624 long temp_x, temp_y;
626 temp_x = rotation->x;
627 temp_y = rotation->y;
628 rotation->x = (temp_x*cos - temp_y*sin)/SIN_COS_SCALE;
629 rotation->y = (temp_y*cos + temp_x*sin)/SIN_COS_SCALE;
630 #define MIN_SCALE (SIN_COS_SCALE-10)
631 #define MAX_SCALE (SIN_COS_SCALE+10)
632 /* normalize vector. this is not accurate but would be enough. */
633 temp_x = rotation->x*rotation->x + rotation->y*rotation->y;
634 if (temp_x <= MIN_SCALE*MIN_SCALE)
636 rotation->x = rotation->x*SIN_COS_SCALE/MIN_SCALE;
637 rotation->y = rotation->y*SIN_COS_SCALE/MIN_SCALE;
639 else if (temp_x >= MAX_SCALE*MAX_SCALE)
641 rotation->x = rotation->x*SIN_COS_SCALE/MAX_SCALE;
642 rotation->y = rotation->y*SIN_COS_SCALE/MAX_SCALE;
644 #undef MIN_SCALE
645 #undef MAX_SCALE
647 point = vertices;
648 n = num_vertices;
649 while (n--)
651 point->x = (point->dx*rotation->x - point->dy*rotation->y)/SIN_COS_SCALE;
652 point->y = (point->dy*rotation->x + point->dx*rotation->y)/SIN_COS_SCALE;
653 point++;
657 /* Draw polygon */
658 static void draw_polygon(struct Point* vertices, int num_vertices,
659 int px, int py)
661 int n, new_x, new_y, old_x, old_y;
662 struct Point *p;
663 bool draw_wrap;
665 if (px > SCALED_WIDTH - WRAP_GAP)
666 px -= SCALED_WIDTH;
667 if (py > SCALED_HEIGHT - WRAP_GAP)
668 py -= SCALED_HEIGHT;
670 draw_wrap = (px < WRAP_GAP || py < WRAP_GAP);
672 p = vertices + num_vertices - 1;
673 old_x = (p->x + px)/SCALE;
674 old_y = (p->y + py)/SCALE;
675 p = vertices;
676 n = num_vertices;
677 while (n--)
679 new_x = (p->x + px)/SCALE;
680 new_y = (p->y + py)/SCALE;
682 rb->lcd_drawline(old_x, old_y, new_x, new_y);
683 if (draw_wrap)
685 rb->lcd_drawline(old_x + LCD_WIDTH, old_y, new_x + LCD_WIDTH, new_y);
686 rb->lcd_drawline(old_x, old_y + LCD_HEIGHT, new_x, new_y + LCD_HEIGHT);
687 rb->lcd_drawline(old_x + LCD_WIDTH, old_y + LCD_HEIGHT,
688 new_x + LCD_WIDTH, new_y + LCD_HEIGHT);
690 old_x = new_x;
691 old_y = new_y;
692 p++;
696 static void move_point(struct Point* point)
698 point->x += point->dx;
699 point->y += point->dy;
701 /* Check bounds on the x-axis: */
702 point->x %= SCALED_WIDTH;
703 if (point->x < 0)
704 point->x += SCALED_WIDTH;
706 /* Check bounds on the y-axis: */
707 point->y %= SCALED_HEIGHT;
708 if (point->y < 0)
709 point->y += SCALED_HEIGHT;
712 /*************************************************
713 ** Handle trail blaiz.
714 *************************************************/
716 static void create_ship_trail(struct TrailPoint* tpoint)
718 tpoint->position.x += ship.vertices[2].x;
719 tpoint->position.y += ship.vertices[2].y;
720 tpoint->position.dx = -( ship.vertices[0].x - ship.vertices[2].x )/10;
721 tpoint->position.dy = -( ship.vertices[0].y - ship.vertices[2].y )/10;
724 static void create_explosion_trail(struct TrailPoint* tpoint)
726 tpoint->position.dx = (rb->rand()%5001)-2500;
727 tpoint->position.dy = (rb->rand()%5001)-2500;
730 static void create_trail_blaze(int colour, struct Point* position)
732 int numtoadd;
733 struct TrailPoint* tpoint;
734 int n;
736 if (colour != EXPLOSION_SHIP)
738 numtoadd = NUM_TRAIL_POINTS/5;
740 else
742 numtoadd = NUM_TRAIL_POINTS/8;
745 /* give the point a random countdown timer, so they dissapears at different
746 times */
747 tpoint = trail_points;
748 n = NUM_TRAIL_POINTS;
749 while (n--)
751 /* find a space in the array of trail_points that is NULL or DEAD or
752 whatever and place this one here. */
753 if (tpoint->alive <= 0)
755 /* take a random point near the position. */
756 tpoint->position.x = (rb->rand()%18000)-9000 + position->x;
757 tpoint->position.y = (rb->rand()%18000)-9000 + position->y;
759 switch(colour)
761 case EXPLOSION_SHIP:
762 create_explosion_trail(tpoint);
763 tpoint->alive = 51;
764 #ifdef HAVE_LCD_COLOR
765 tpoint->r = SHIP_R;
766 tpoint->g = SHIP_G;
767 tpoint->b = SHIP_B;
768 tpoint->dec = 2;
769 #endif
770 break;
771 case EXPLOSION_ASTEROID:
772 create_explosion_trail(tpoint);
773 tpoint->alive = 51;
774 #ifdef HAVE_LCD_COLOR
775 tpoint->r = ASTEROID_R;
776 tpoint->g = ASTEROID_G;
777 tpoint->b = ASTEROID_B;
778 tpoint->dec = 2;
779 #endif
780 break;
781 case EXPLOSION_ENEMY:
782 create_explosion_trail(tpoint);
783 tpoint->alive = 51;
784 #ifdef HAVE_LCD_COLOR
785 tpoint->r = ENEMY_R;
786 tpoint->g = ENEMY_G;
787 tpoint->b = ENEMY_B;
788 tpoint->dec = 2;
789 #endif
790 break;
791 case EXPLOSION_THRUST:
792 create_ship_trail(tpoint);
793 tpoint->alive = 17;
794 #ifdef HAVE_LCD_COLOR
795 tpoint->r = THRUST_R;
796 tpoint->g = THRUST_G;
797 tpoint->b = THRUST_B;
798 tpoint->dec = 4;
799 #endif
800 break;
803 /* give the points a speed based on direction of travel
804 - i.e. opposite */
805 tpoint->position.dx += position->dx;
806 tpoint->position.dy += position->dy;
808 numtoadd--;
809 if (numtoadd <= 0)
810 break;
812 tpoint++;
816 static void draw_and_move_trail_blaze(void)
818 struct TrailPoint* tpoint;
819 int n;
821 /* loop through, if alive then move and draw.
822 when drawn, countdown it's timer.
823 if zero kill it! */
825 tpoint = trail_points;
826 n = NUM_TRAIL_POINTS;
827 while (n--)
829 if (tpoint->alive > 0)
831 if (game_state != PAUSE_MODE)
833 tpoint->alive--;
834 move_point(&(tpoint->position));
835 #ifdef HAVE_LCD_COLOR
836 /* intensity = tpoint->alive/2; */
837 if (tpoint->r >= tpoint->dec) tpoint->r -= tpoint->dec;
838 if (tpoint->g >= tpoint->dec) tpoint->g -= tpoint->dec;
839 if (tpoint->b >= tpoint->dec) tpoint->b -= tpoint->dec;
840 #endif
842 SET_FG(LCD_RGBPACK(tpoint->r, tpoint->g, tpoint->b));
843 rb->lcd_drawpixel(tpoint->position.x/SCALE, tpoint->position.y/SCALE);
845 tpoint++;
849 /*************************************************
850 ** Handle asteroid.
851 *************************************************/
853 static void rotate_asteroid(struct Asteroid* asteroid)
855 rotate_polygon(asteroid->vertices, NUM_ASTEROID_VERTICES,
856 &asteroid->rotation,
857 asteroid->speed_cos, asteroid->speed_sin);
860 /* Initialise the passed Asteroid.
861 * if position is NULL, place it at the random loacation
862 * where ship doesn't exist
864 static void initialise_asteroid(struct Asteroid* asteroid,
865 enum asteroid_type type, struct Point *position)
867 const short *asteroid_vertices;
868 struct Point* point;
869 int n;
871 asteroid->exists = true;
872 asteroid->explode_countdown = 0;
873 asteroid->type = type;
875 /* Set the radius of the asteroid: */
876 asteroid->radius = (int)type*SCALE*3;
878 /* shall we move Clockwise and Fast */
879 n = rb->rand()%100;
880 if (n < 25)
882 asteroid->speed_cos = FAST_ROT_CW_COS;
883 asteroid->speed_sin = FAST_ROT_CW_SIN;
885 else if (n < 50)
887 asteroid->speed_cos = FAST_ROT_ACW_COS;
888 asteroid->speed_sin = FAST_ROT_ACW_SIN;
890 else if (n < 75)
892 asteroid->speed_cos = SLOW_ROT_ACW_COS;
893 asteroid->speed_sin = SLOW_ROT_ACW_SIN;
895 else
897 asteroid->speed_cos = SLOW_ROT_CW_COS;
898 asteroid->speed_sin = SLOW_ROT_CW_SIN;
901 n = rb->rand()%99;
902 if (n < 33)
903 asteroid_vertices = asteroid_one;
904 else if (n < 66)
905 asteroid_vertices = asteroid_two;
906 else
907 asteroid_vertices = asteroid_three;
909 point = asteroid->vertices;
910 for(n = 0; n < NUM_ASTEROID_VERTICES*2; n += 2)
912 point->x = asteroid_vertices[n];
913 point->y = asteroid_vertices[n+1];
914 point->x *= asteroid->radius/20;
915 point->y *= asteroid->radius/20;
916 /* dx and dy are used when rotate polygon */
917 point->dx = point->x;
918 point->dy = point->y;
919 point++;
922 if (!position)
924 do {
925 /* Set the position randomly: */
926 asteroid->position.x = (rb->rand()%SCALED_WIDTH);
927 asteroid->position.y = (rb->rand()%SCALED_HEIGHT);
928 } while (is_point_within_rectangle(&ship.position, &asteroid->position,
929 SPACE_CHECK_SIZE));
931 else
933 asteroid->position.x = position->x;
934 asteroid->position.y = position->y;
937 do {
938 asteroid->position.dx = (rb->rand()%ASTEROID_SPEED)-ASTEROID_SPEED/2;
939 } while (asteroid->position.dx == 0);
941 do {
942 asteroid->position.dy = (rb->rand()%ASTEROID_SPEED)-ASTEROID_SPEED/2;
943 } while (asteroid->position.dy == 0);
945 asteroid->position.dx *= SCALE/10;
946 asteroid->position.dy *= SCALE/10;
948 asteroid->rotation.x = SIN_COS_SCALE;
949 asteroid->rotation.y = 0;
951 /* Now rotate the asteroid a bit, so they all look a bit different */
952 for(n = (rb->rand()%30)+2; n--; )
953 rotate_asteroid(asteroid);
955 /* great, we've created an asteroid, don't forget to increment the total: */
956 asteroid_count++;
960 * Creates a new asteroid of the given 4type (size) and at the given location.
962 static void create_asteroid(enum asteroid_type type, struct Point *position)
964 struct Asteroid* asteroid;
965 int n;
967 asteroid = asteroids_array;
968 n = MAX_NUM_ASTEROIDS;
969 while (n--)
971 if (!asteroid->exists && asteroid->explode_countdown <= 0)
973 initialise_asteroid(asteroid, type, position);
974 break;
976 asteroid++;
980 /* Draw and move all asteroids */
981 static void draw_and_move_asteroids(void)
983 struct Asteroid* asteroid;
984 int n;
986 SET_FG(COL_ASTEROID);
988 asteroid = asteroids_array;
989 n = MAX_NUM_ASTEROIDS;
990 while (n--)
992 if (asteroid->exists)
994 draw_polygon(asteroid->vertices, NUM_ASTEROID_VERTICES,
995 asteroid->position.x, asteroid->position.y);
997 if (game_state != PAUSE_MODE)
999 if (asteroid->exists)
1001 move_point(&asteroid->position);
1002 rotate_asteroid(asteroid);
1004 else if (asteroid->explode_countdown > 0)
1006 asteroid->explode_countdown--;
1009 asteroid++;
1013 static void explode_asteroid(struct Asteroid* asteroid)
1015 struct Point p;
1016 p.dx = asteroid->position.dx;
1017 p.dy = asteroid->position.dy;
1018 p.x = asteroid->position.x;
1019 p.y = asteroid->position.y;
1021 asteroid_count--;
1022 asteroid->exists = false;
1024 switch(asteroid->type)
1026 case SMALL:
1027 asteroid->explode_countdown = EXPLOSION_LENGTH;
1028 create_trail_blaze(EXPLOSION_ASTEROID, &p);
1029 break;
1031 case MEDIUM:
1032 create_asteroid(SMALL, &p);
1033 create_asteroid(SMALL, &p);
1034 break;
1036 case LARGE:
1037 create_asteroid(MEDIUM, &p);
1038 create_asteroid(MEDIUM, &p);
1039 break;
1043 /*************************************************
1044 ** Handle ship.
1045 *************************************************/
1047 /* Initialise the ship */
1048 static void initialise_ship(void)
1050 struct Point* point;
1051 struct Point* lives_point;
1052 int n;
1054 ship.position.x = CENTER_LCD_X * SCALE;
1055 ship.position.y = CENTER_LCD_Y * SCALE;
1056 ship.position.dx = 0;
1057 ship.position.dy = 0;
1058 ship.rotation.x = SIN_COS_SCALE;
1059 ship.rotation.y = 0;
1060 ship.exists = true;
1061 ship.explode_countdown = 0;
1062 ship.invulnerable_time = INVULNERABLE_TIME;
1064 point = ship.vertices;
1065 lives_point = lives_points;
1066 for(n = 0; n < NUM_SHIP_VERTICES*2; n += 2)
1068 point->x = ship_vertices[n];
1069 point->y = ship_vertices[n+1];
1070 point->x *= SCALE;
1071 point->y *= SCALE;
1072 /* dx and dy are used when rotate polygon */
1073 point->dx = point->x;
1074 point->dy = point->y;
1075 /* grab a copy of the ships points for the lives display: */
1076 lives_point->x = point->x;
1077 lives_point->y = point->y;
1079 point++;
1080 lives_point++;
1085 * Draws the ship, moves the ship and creates a new
1086 * one if it's finished exploding.
1088 static void draw_and_move_ship(void)
1090 if (ship.invulnerable_time > BLINK_TIME || ship.invulnerable_time % 2 != 0)
1092 SET_FG(COL_INVULN);
1094 else
1096 SET_FG(COL_PLAYER);
1099 if (ship.exists)
1101 draw_polygon(ship.vertices, NUM_SHIP_VERTICES,
1102 ship.position.x, ship.position.y);
1105 if (game_state != PAUSE_MODE)
1107 if (ship.exists)
1109 if (ship.invulnerable_time > 0)
1110 ship.invulnerable_time--;
1111 move_point(&ship.position);
1113 else if (ship.explode_countdown > 0)
1115 ship.explode_countdown--;
1116 if (ship.explode_countdown <= 0)
1118 num_lives--;
1119 if (num_lives <= 0)
1121 game_state = GAME_OVER;
1123 else
1125 initialise_ship();
1132 static void explode_ship(void)
1134 if (!ship.invulnerable_time)
1136 /* if not invulnerable, blow up ship */
1137 ship.explode_countdown = EXPLOSION_LENGTH;
1138 ship.exists = false;
1139 create_trail_blaze(EXPLOSION_SHIP, &ship.position);
1143 /* Rotate the ship using the passed sin & cos values */
1144 static void rotate_ship(int cos, int sin)
1146 if (ship.exists)
1148 rotate_polygon(ship.vertices, NUM_SHIP_VERTICES,
1149 &ship.rotation, cos, sin);
1153 static void thrust_ship(void)
1155 if (ship.exists)
1157 ship.position.dx += ( ship.vertices[0].x - ship.vertices[2].x )/20;
1158 ship.position.dy += ( ship.vertices[0].y - ship.vertices[2].y )/20;
1160 /* if dx and dy are below a certain threshold, then set 'em to 0
1161 but to do this we need to ascertain if the spacehip as moved on
1162 screen for more than a certain amount. */
1164 create_trail_blaze(EXPLOSION_THRUST, &ship.position);
1168 /* stop movement of ship, 'cos that's what happens when you go into hyperspace. */
1169 static void hyperspace(void)
1171 if (ship.exists)
1173 ship.position.dx = ship.position.dy = 0;
1174 ship.position.x = (rb->rand()%SCALED_WIDTH);
1175 ship.position.y = (rb->rand()%SCALED_HEIGHT);
1179 static void draw_lives(void)
1181 int n;
1182 #if (LARGE_LCD)
1183 int px = (LCD_WIDTH-1 - 4)*SCALE;
1184 int py = (LCD_HEIGHT-1 - 6)*SCALE;
1185 #else
1186 int px = (LCD_WIDTH-1 - 3)*SCALE;
1187 int py = (LCD_HEIGHT-1 - 4)*SCALE;
1188 #endif
1190 SET_FG(COL_PLAYER);
1192 n = num_lives-1;
1193 while (n--)
1195 draw_polygon(lives_points, NUM_SHIP_VERTICES, px, py);
1196 #if (LARGE_LCD)
1197 px -= 8*SCALE;
1198 #else
1199 px -= 6*SCALE;
1200 #endif
1205 * missile
1208 /* Initialise a missile */
1209 static void initialise_missile(struct Missile* missile)
1211 missile->position.x = ship.position.x + ship.vertices[0].x;
1212 missile->position.y = ship.position.y + ship.vertices[0].y;
1213 missile->position.dx = (ship.vertices[0].x - ship.vertices[2].x)/2;
1214 missile->position.dy = (ship.vertices[0].y - ship.vertices[2].y)/2;
1215 missile->alive = MISSILE_LIFE_LENGTH;
1216 missile->oldpoint.x = missile->position.x;
1217 missile->oldpoint.y = missile->position.y;
1220 /* Fire the next missile */
1221 static void fire_missile(void)
1223 struct Missile* missile;
1224 int n;
1226 if (ship.exists)
1228 missile = missiles_array;
1229 n = MAX_NUM_MISSILES;
1230 while (n--)
1232 if (missile->alive <= 0)
1234 initialise_missile(missile);
1235 break;
1237 missile++;
1242 /* Draw and Move all the missiles */
1243 static void draw_and_move_missiles(void)
1245 struct Missile* missile;
1246 struct Point vertices[2];
1247 int n;
1249 SET_FG(COL_MISSILE);
1251 missile = missiles_array;
1252 n = MAX_NUM_MISSILES;
1253 while (n--)
1255 if (missile->alive > 0)
1257 vertices[0].x = 0;
1258 vertices[0].y = 0;
1259 vertices[1].x = -missile->position.dx;
1260 vertices[1].y = -missile->position.dy;
1261 draw_polygon(vertices, 2, missile->position.x, missile->position.y);
1263 if (game_state != PAUSE_MODE)
1265 missile->oldpoint.x = missile->position.x;
1266 missile->oldpoint.y = missile->position.y;
1267 move_point(&missile->position);
1268 missile->alive--;
1271 missile++;
1275 /*************************************************
1276 ** Handle enemy.
1277 *************************************************/
1279 static void initialise_enemy(void)
1281 struct Point* point;
1282 int n;
1283 int size;
1285 if (rb->rand()%100 > enemy.size_probability)
1287 size = BIG_SHIP;
1288 enemy.size_probability++;
1289 if (enemy.size_probability > 90)
1291 enemy.size_probability = ENEMY_BIG_PROBABILITY_START;
1294 else
1296 size = LITTLE_SHIP;
1297 enemy.size_probability = ENEMY_BIG_PROBABILITY_START;
1300 enemy.exists = true;
1301 enemy.explode_countdown = 0;
1302 enemy.appear_countdown = enemy.appear_timing;
1304 point = enemy.vertices;
1305 for(n = 0; n < NUM_ENEMY_VERTICES*2; n += 2)
1307 point->x = enemy_vertices[n];
1308 point->y = enemy_vertices[n+1];
1309 point->x *= size*SCALE/2;
1310 point->y *= size*SCALE/2;
1311 point++;
1314 if (ship.position.x >= SCALED_WIDTH/2)
1316 enemy.position.dx = ENEMY_SPEED;
1317 enemy.position.x = 0;
1319 else
1321 enemy.position.dx = -ENEMY_SPEED;
1322 enemy.position.x = SCALED_WIDTH;
1325 if (ship.position.y >= SCALED_HEIGHT/2)
1327 enemy.position.dy = ENEMY_SPEED;
1328 enemy.position.y = 0;
1330 else
1332 enemy.position.dy = -ENEMY_SPEED;
1333 enemy.position.y = SCALED_HEIGHT;
1336 enemy.position.dx *= SCALE/10;
1337 enemy.position.dy *= SCALE/10;
1340 static void draw_and_move_enemy(void)
1342 SET_FG(COL_ENEMY);
1344 if (enemy.exists)
1346 draw_polygon(enemy.vertices, NUM_ENEMY_VERTICES,
1347 enemy.position.x, enemy.position.y);
1350 if (game_state != PAUSE_MODE)
1352 if (enemy.exists)
1354 enemy.position.x += enemy.position.dx;
1355 enemy.position.y += enemy.position.dy;
1357 if (enemy.position.x > SCALED_WIDTH || enemy.position.x < 0)
1358 enemy.exists = false;
1360 enemy.position.y %= SCALED_HEIGHT;
1361 if (enemy.position.y < 0)
1362 enemy.position.y += SCALED_HEIGHT;
1364 if ((rb->rand()%1000) < 10)
1365 enemy.position.dy = -enemy.position.dy;
1367 else if (enemy.explode_countdown > 0)
1369 enemy.explode_countdown--;
1371 else
1373 if (enemy.appear_countdown > 0)
1374 enemy.appear_countdown--;
1375 else if (rb->rand()%100 >= enemy.appear_probability)
1376 initialise_enemy();
1380 if (enemy_missile.alive <= 0)
1382 /* if no missile and the enemy is here and not exploding..
1383 then shoot baby! */
1384 if (enemy.exists && ship.exists &&
1385 game_state == PLAY_MODE && (rb->rand()%10) >= 5 )
1387 int dx = ship.position.x - enemy.position.x;
1388 int dy = ship.position.y - enemy.position.y;
1390 if (dx < -SCALED_WIDTH/2) dx += SCALED_WIDTH;
1391 else if (dx > SCALED_WIDTH/2) dx -= SCALED_WIDTH;
1392 if (dy < -SCALED_HEIGHT/2) dy += SCALED_HEIGHT;
1393 else if (dy > SCALED_HEIGHT/2) dy -= SCALED_HEIGHT;
1395 enemy_missile.position.x = enemy.position.x;
1396 enemy_missile.position.y = enemy.position.y;
1398 /* lame, needs to be sorted - it's trying to shoot at the ship */
1399 if (dx < -5*SCALE)
1400 enemy_missile.position.dx = -1;
1401 else if (dx > 5*SCALE)
1402 enemy_missile.position.dx = 1;
1403 else
1404 enemy_missile.position.dx = 0;
1406 if (dy < -5*SCALE)
1407 enemy_missile.position.dy = -1;
1408 else if (dy > 5*SCALE)
1409 enemy_missile.position.dy = 1;
1410 else
1411 enemy_missile.position.dy = 0;
1413 while (enemy_missile.position.dx == 0 &&
1414 enemy_missile.position.dy == 0)
1416 enemy_missile.position.dx = rb->rand()%2-1;
1417 enemy_missile.position.dy = rb->rand()%2-1;
1420 enemy_missile.position.dx *= SCALE;
1421 enemy_missile.position.dy *= SCALE;
1422 enemy_missile.alive = ENEMY_MISSILE_LIFE_LENGTH;
1425 else
1427 rb->lcd_fillrect( enemy_missile.position.x/SCALE,
1428 enemy_missile.position.y/SCALE,
1429 POINT_SIZE, POINT_SIZE );
1430 if (game_state != PAUSE_MODE)
1432 move_point(&enemy_missile.position);
1433 enemy_missile.alive--;
1438 /*************************************************
1439 ** Check collisions.
1440 *************************************************/
1442 /* Add score if missile hit asteroid or enemy */
1443 static void add_score(int val)
1445 current_score += val;
1446 if (current_score >= extra_life)
1448 num_lives++;
1449 extra_life += EXTRA_LIFE;
1453 static bool is_point_within_asteroid(struct Asteroid* asteroid,
1454 struct Point* point)
1456 if (is_point_within_rectangle(&asteroid->position, point, asteroid->radius)
1457 && is_point_in_polygon(asteroid->vertices, NUM_ASTEROID_VERTICES,
1458 point->x - asteroid->position.x,
1459 point->y - asteroid->position.y))
1461 explode_asteroid(asteroid);
1462 return true;
1464 else
1465 return false;
1468 static bool is_point_within_ship(struct Point* point)
1470 if (is_point_within_rectangle(&ship.position, point, SIZE_SHIP_COLLISION)
1471 && is_point_in_polygon(ship.vertices, NUM_SHIP_VERTICES,
1472 point->x - ship.position.x,
1473 point->y - ship.position.y))
1475 return true;
1477 else
1478 return false;
1481 static bool is_point_within_enemy(struct Point* point)
1483 if (is_point_within_rectangle(&enemy.position, point, SIZE_ENEMY_COLLISION))
1485 add_score(5);
1486 enemy.explode_countdown = EXPLOSION_LENGTH;
1487 enemy.exists = false;
1488 create_trail_blaze(EXPLOSION_ENEMY, &enemy.position);
1489 return true;
1491 else
1492 return false;
1495 static bool is_ship_within_asteroid(struct Asteroid* asteroid)
1497 struct Point p;
1499 if (!is_point_within_rectangle(&asteroid->position, &ship.position,
1500 asteroid->radius+SIZE_SHIP_COLLISION))
1501 return false;
1503 p.x = ship.position.x + ship.vertices[0].x;
1504 p.y = ship.position.y + ship.vertices[0].y;
1505 if (is_point_within_asteroid(asteroid, &p))
1506 return true;
1508 p.x = ship.position.x + ship.vertices[1].x;
1509 p.y = ship.position.y + ship.vertices[1].y;
1510 if (is_point_within_asteroid(asteroid, &p))
1511 return true;
1513 p.x = ship.position.x + ship.vertices[3].x;
1514 p.y = ship.position.y + ship.vertices[3].y;
1515 if (is_point_within_asteroid(asteroid, &p))
1516 return true;
1518 return false;
1521 /* Check for collsions between the missiles and the asteroids and the ship */
1522 static void check_collisions(void)
1524 struct Missile* missile;
1525 struct Asteroid* asteroid;
1526 int m, n;
1527 bool asteroids_onscreen = false;
1529 asteroid = asteroids_array;
1530 m = MAX_NUM_ASTEROIDS;
1531 while (m--)
1533 /* if the asteroids exists then test missile collision: */
1534 if (asteroid->exists)
1536 missile = missiles_array;
1537 n = MAX_NUM_MISSILES;
1538 while (n--)
1540 /* if the missiles exists: */
1541 if (missile->alive > 0)
1543 /* has the missile hit the asteroid? */
1544 if (is_point_within_asteroid(asteroid, &missile->position) ||
1545 is_point_within_asteroid(asteroid, &missile->oldpoint))
1547 add_score(1);
1548 missile->alive = 0;
1549 break;
1552 missile++;
1555 /* now check collision with ship: */
1556 if (asteroid->exists && ship.exists)
1558 if (is_ship_within_asteroid(asteroid))
1560 add_score(1);
1561 explode_ship();
1565 /* has the enemy missile blown something up? */
1566 if (asteroid->exists && enemy_missile.alive > 0)
1568 if (is_point_within_asteroid(asteroid, &enemy_missile.position))
1570 enemy_missile.alive = 0;
1575 /* is an asteroid still exploding? */
1576 if (asteroid->explode_countdown > 0)
1577 asteroids_onscreen = true;
1579 asteroid++;
1582 /* now check collision between ship and enemy */
1583 if (enemy.exists && ship.exists)
1585 /* has the enemy collided with the ship? */
1586 if (is_point_within_enemy(&ship.position))
1588 explode_ship();
1589 create_trail_blaze(EXPLOSION_ENEMY, &enemy.position);
1592 if (enemy.exists)
1594 /* Now see if the enemy has been shot at by the ships missiles: */
1595 missile = missiles_array;
1596 n = MAX_NUM_MISSILES;
1597 while (n--)
1599 if (missile->alive > 0 &&
1600 is_point_within_enemy(&missile->position))
1602 missile->alive = 0;
1603 break;
1605 missile++;
1610 /* test collision with enemy missile and ship: */
1611 if (enemy_missile.alive > 0 && is_point_within_ship(&enemy_missile.position))
1613 explode_ship();
1614 enemy_missile.alive = 0;
1615 enemy_missile.position.x = enemy_missile.position.y = 0;
1618 /* if all asteroids cleared then start again: */
1619 if (asteroid_count == 0 && !asteroids_onscreen
1620 && !enemy.exists && enemy.explode_countdown <= 0)
1622 current_level++;
1623 if (current_level > MAX_LEVEL)
1624 current_level = START_LEVEL;
1625 enemy.appear_probability += 5;
1626 if (enemy.appear_probability >= 100)
1627 enemy.appear_probability = ENEMY_APPEAR_PROBABILITY_START;
1628 enemy.appear_timing -= 30;
1629 if (enemy.appear_timing < 30)
1630 enemy.appear_timing = 30;
1631 game_state = SHOW_LEVEL;
1632 show_level_timeout = SHOW_LEVEL_TIME;
1637 * stars
1640 static void create_stars(void)
1642 struct Point* p;
1643 int n;
1645 p = stars;
1646 n = NUM_STARS;
1647 while (n--)
1649 p->x = (rb->rand()%LCD_WIDTH);
1650 p->y = (rb->rand()%LCD_HEIGHT);
1651 p++;
1655 static void drawstars(void)
1657 struct Point* p;
1658 int n;
1660 SET_FG(COL_STARS);
1662 p = stars;
1663 n = NUM_STARS;
1664 while (n--)
1666 rb->lcd_drawpixel(p->x , p->y);
1667 p++;
1671 /*************************************************
1672 ** Creates start_num number of new asteroids of
1673 ** full size.
1674 **************************************************/
1675 static void initialise_level(int start_num)
1677 struct Asteroid* asteroid;
1678 struct Missile* missile;
1679 struct TrailPoint* tpoint;
1680 int n;
1681 asteroid_count = next_missile_count = next_thrust_count = 0;
1683 /* no enemy */
1684 enemy.exists = 0;
1685 enemy.explode_countdown = 0;
1686 enemy_missile.alive = 0;
1688 /* clear asteroids */
1689 asteroid = asteroids_array;
1690 n = MAX_NUM_ASTEROIDS;
1691 while (n--)
1693 asteroid->exists = false;
1694 asteroid++;
1697 /* make some LARGE asteroids */
1698 for(n = 0; n < start_num; n++)
1699 initialise_asteroid(&asteroids_array[n], LARGE, NULL);
1701 /* ensure all missiles are out of action: */
1702 missile = missiles_array;
1703 n = MAX_NUM_MISSILES;
1704 while (n--)
1706 missile->alive = 0;
1707 missile++;
1710 tpoint = trail_points;
1711 n = NUM_TRAIL_POINTS;
1712 while (n--)
1714 tpoint->alive = 0;
1715 tpoint++;
1719 static void initialise_game(void)
1721 enemy.appear_probability = ENEMY_APPEAR_PROBABILITY_START;
1722 enemy.appear_timing = ENEMY_APPEAR_TIMING_START;
1723 enemy.appear_countdown = enemy.appear_timing;
1724 enemy.size_probability = ENEMY_BIG_PROBABILITY_START;
1725 current_level = START_LEVEL;
1726 num_lives = START_LIVES;
1727 extra_life = EXTRA_LIFE;
1728 current_score = 0;
1729 initialise_ship();
1730 initialise_level(0);
1731 game_state = SHOW_LEVEL;
1732 show_level_timeout = SHOW_LEVEL_TIME;
1735 /* menu stuff */
1736 static bool spacerocks_help(void)
1738 static char *help_text[] = {
1739 "Spacerocks", "", "Aim", "", "The", "goal", "of", "the", "game", "is",
1740 "to", "blow", "up", "the", "asteroids", "and", "avoid", "being", "hit", "by",
1741 "them.", "Also", "you'd", "better", "watch", "out", "for", "the", "UFOs!"
1743 static struct style_text formation[]={
1744 { 0, TEXT_CENTER|TEXT_UNDERLINE },
1745 { 2, C_RED },
1746 { -1, 0 }
1748 int button;
1750 rb->lcd_setfont(FONT_UI);
1751 #ifdef HAVE_LCD_COLOR
1752 rb->lcd_set_background(LCD_BLACK);
1753 rb->lcd_set_foreground(LCD_WHITE);
1754 #endif
1755 if (display_text(ARRAYLEN(help_text), help_text, formation, NULL)
1756 == PLUGIN_USB_CONNECTED)
1757 return true;
1758 do {
1759 button = rb->button_get(true);
1760 if (button == SYS_USB_CONNECTED)
1761 return true;
1762 } while( ( button == BUTTON_NONE )
1763 || ( button & (BUTTON_REL|BUTTON_REPEAT) ) );
1764 rb->lcd_setfont(FONT_SYSFIXED);
1766 return false;
1769 #define PLUGIN_OTHER 10
1770 static bool ingame;
1771 static int spacerocks_menu_cb(int action, const struct menu_item_ex *this_item)
1773 if (action == ACTION_REQUEST_MENUITEM
1774 && !ingame && ((intptr_t)this_item)==0)
1775 return ACTION_EXIT_MENUITEM;
1776 return action;
1779 static int spacerocks_menu(void)
1781 int selection = 0;
1782 MENUITEM_STRINGLIST(main_menu, "Spacerocks Menu", spacerocks_menu_cb,
1783 "Resume Game", "Start New Game",
1784 "Help", "High Scores",
1785 "Playback Control", "Quit");
1786 rb->button_clear_queue();
1788 while (1)
1790 switch (rb->do_menu(&main_menu, &selection, NULL, false))
1792 case 0:
1793 return PLUGIN_OTHER;
1794 case 1:
1795 initialise_game();
1796 return PLUGIN_OTHER;
1797 case 2:
1798 if (spacerocks_help())
1799 return PLUGIN_USB_CONNECTED;
1800 break;
1801 case 3:
1802 highscore_show(NUM_SCORES, highscores, NUM_SCORES, true);
1803 break;
1804 case 4:
1805 playback_control(NULL);
1806 break;
1807 case 5:
1808 return PLUGIN_OK;
1809 case MENU_ATTACHED_USB:
1810 return PLUGIN_USB_CONNECTED;
1811 default:
1812 break;
1817 static int spacerocks_game_loop(void)
1819 char str[20];
1820 int button;
1821 int end;
1822 int position;
1823 int ret;
1825 if ((ret = spacerocks_menu()) != PLUGIN_OTHER)
1826 return ret;
1828 SET_BG(LCD_BLACK);
1830 ingame = true;
1831 while (true)
1833 end = *rb->current_tick + (CYCLETIME * HZ) / 1000;
1834 rb->lcd_clear_display();
1835 SET_FG(COL_TEXT);
1836 switch(game_state)
1838 case GAME_OVER:
1839 ingame = false;
1840 rb->splash (HZ * 2, "Game Over");
1841 rb->lcd_clear_display();
1842 position = highscore_update(current_score, current_level, "",
1843 highscores, NUM_SCORES);
1844 if (position != -1)
1846 if (position == 0)
1847 rb->splash(HZ*2, "New High Score");
1848 highscore_show(position, highscores, NUM_SCORES, true);
1850 return PLUGIN_OTHER;
1851 break;
1853 case PAUSE_MODE:
1854 rb->snprintf(str, sizeof(str), "score %d ", current_score);
1855 rb->lcd_putsxy(1,LCD_HEIGHT-8, str);
1856 rb->lcd_putsxy(CENTER_LCD_X - 15,
1857 CENTER_LCD_Y + CENTER_LCD_Y/2 - 4, "pause");
1858 draw_and_move_missiles();
1859 draw_lives();
1860 draw_and_move_ship();
1861 break;
1863 case PLAY_MODE:
1864 rb->snprintf(str, sizeof(str), "score %d ", current_score);
1865 rb->lcd_putsxy(1, LCD_HEIGHT-8, str);
1866 draw_and_move_missiles();
1867 draw_lives();
1868 check_collisions();
1869 draw_and_move_ship();
1870 break;
1872 case SHOW_LEVEL:
1873 rb->snprintf(str, sizeof(str), "score %d ", current_score);
1874 rb->lcd_putsxy(1, LCD_HEIGHT-8, str);
1875 rb->snprintf(str, sizeof(str), "stage %d ", current_level);
1876 rb->lcd_putsxy(CENTER_LCD_X - 20,
1877 CENTER_LCD_Y + CENTER_LCD_Y/2 - 4, str);
1878 draw_lives();
1879 draw_and_move_ship();
1880 show_level_timeout--;
1881 if (show_level_timeout <= 0)
1883 initialise_level(current_level);
1884 game_state = PLAY_MODE;
1886 break;
1888 draw_and_move_trail_blaze();
1889 drawstars();
1890 draw_and_move_asteroids();
1891 draw_and_move_enemy();
1893 rb->lcd_update();
1895 #ifdef HAS_BUTTON_HOLD
1896 if (rb->button_hold() && game_state == PLAY_MODE)
1897 game_state = PAUSE_MODE;
1898 #endif
1899 button = rb->button_get(false);
1900 switch(button)
1902 case(AST_QUIT):
1903 return PLUGIN_OTHER;
1904 break;
1905 #ifdef AST_PAUSE
1906 case(AST_PAUSE):
1907 if (game_state == PAUSE_MODE)
1908 game_state = PLAY_MODE;
1909 else if (game_state == PLAY_MODE)
1910 game_state = PAUSE_MODE;
1911 break;
1912 #endif
1913 case (AST_LEFT):
1914 case (AST_LEFT | BUTTON_REPEAT):
1915 if (game_state == PLAY_MODE || game_state == SHOW_LEVEL)
1916 rotate_ship(SHIP_ROT_ACW_COS, SHIP_ROT_ACW_SIN);
1917 break;
1919 case (AST_RIGHT):
1920 case (AST_RIGHT | BUTTON_REPEAT):
1921 if (game_state == PLAY_MODE || game_state == SHOW_LEVEL)
1922 rotate_ship(SHIP_ROT_CW_COS, SHIP_ROT_CW_SIN);
1923 break;
1925 case (AST_THRUST):
1926 case (AST_THRUST | BUTTON_REPEAT):
1927 if (game_state == PLAY_MODE || game_state == SHOW_LEVEL)
1929 if (next_thrust_count <= 0)
1931 next_thrust_count = 5;
1932 thrust_ship();
1935 break;
1937 case (AST_HYPERSPACE):
1938 if (game_state == PLAY_MODE)
1939 hyperspace();
1940 /* maybe shield if it gets too hard */
1941 break;
1943 case (AST_FIRE):
1944 case (AST_FIRE | BUTTON_REPEAT):
1945 if (game_state == PLAY_MODE)
1947 if (next_missile_count <= 0)
1949 fire_missile();
1950 next_missile_count = 10;
1953 else if(game_state == PAUSE_MODE)
1954 game_state = PLAY_MODE;
1955 break;
1957 default:
1958 if (rb->default_event_handler(button)==SYS_USB_CONNECTED)
1959 return PLUGIN_USB_CONNECTED;
1960 break;
1963 if (next_missile_count > 0)
1964 next_missile_count--;
1966 if (next_thrust_count > 0)
1967 next_thrust_count--;
1969 if (TIME_BEFORE(*rb->current_tick, end))
1970 rb->sleep(end-*rb->current_tick);
1971 else
1972 rb->yield();
1976 enum plugin_status plugin_start(const void* parameter)
1978 (void)parameter;
1979 int ret = PLUGIN_OTHER;
1981 #if LCD_DEPTH > 1
1982 rb->lcd_set_backdrop(NULL);
1983 #endif
1984 /* universal font */
1985 rb->lcd_setfont(FONT_SYSFIXED);
1986 /* Turn off backlight timeout */
1987 backlight_force_on(); /* backlight control in lib/helper.c */
1988 highscore_load(HIGH_SCORE, highscores, NUM_SCORES);
1989 rb->srand(*rb->current_tick);
1991 /* create stars once, and once only: */
1992 create_stars();
1994 while (ret == PLUGIN_OTHER)
1995 ret = spacerocks_game_loop();
1997 rb->lcd_setfont(FONT_UI);
1998 highscore_save(HIGH_SCORE, highscores, NUM_SCORES);
1999 /* Turn on backlight timeout (revert to settings) */
2000 backlight_use_settings(); /* backlight control in lib/helper.c */
2002 return ret;