Add AI to the pong plugin, to allow single-player operation.
[kugel-rb.git] / apps / plugins / spacerocks.c
blob59463b3c97e487a4334849ad4bc7e9569d7975b7
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
254 #define AST_QUIT (BUTTON_REC|BUTTON_REPEAT)
255 #define AST_THRUST BUTTON_MENU
256 #define AST_HYPERSPACE (BUTTON_PLAY|BUTTON_REPEAT)
257 #define AST_LEFT BUTTON_REW
258 #define AST_RIGHT BUTTON_FF
259 #define AST_FIRE BUTTON_ENTER
261 #else
262 #error No keymap defined!
263 #endif
265 #ifdef HAVE_TOUCHSCREEN
266 #ifndef AST_PAUSE
267 #define AST_PAUSE BUTTON_CENTER
268 #endif
269 #ifndef AST_QUIT
270 #define AST_QUIT BUTTON_TOPLEFT
271 #endif
272 #ifndef AST_THRUST
273 #define AST_THRUST BUTTON_TOPMIDDLE
274 #endif
275 #ifndef AST_HYPERSPACE
276 #define AST_HYPERSPACE BUTTON_TOPRIGHT
277 #endif
278 #ifndef AST_LEFT
279 #define AST_LEFT BUTTON_MIDLEFT
280 #endif
281 #ifndef AST_RIGHT
282 #define AST_RIGHT BUTTON_MIDRIGHT
283 #endif
284 #ifndef AST_FIRE
285 #define AST_FIRE BUTTON_BOTTOMMIDDLE
286 #endif
287 #endif
289 #define RES MAX(LCD_WIDTH, LCD_HEIGHT)
290 #define LARGE_LCD (RES >= 200)
292 #define CYCLETIME 30
294 #define SHOW_COL 0
295 #define SCALE 5000
296 #define WRAP_GAP (LARGE*SCALE*3)
297 #define POINT_SIZE 2
298 #define START_LEVEL 1
299 #define SHOW_LEVEL_TIME 50
300 #define EXPLOSION_LENGTH 20
302 #define MAX_NUM_ASTEROIDS 25
303 #define MAX_NUM_MISSILES 6
304 #define NUM_STARS 50
305 #define NUM_TRAIL_POINTS 70
306 #define MAX_LEVEL MAX_NUM_ASTEROIDS
308 #define NUM_ASTEROID_VERTICES 10
309 #define NUM_SHIP_VERTICES 4
310 #define NUM_ENEMY_VERTICES 8
312 #define INVULNERABLE_TIME 30
313 #define BLINK_TIME 10
314 #define EXTRA_LIFE 250
315 #define START_LIVES 3
316 #define MISSILE_LIFE_LENGTH 40
318 #define ASTEROID_SPEED (RES/20)
319 #define SPACE_CHECK_SIZE 30*SCALE
321 #if (LARGE_LCD)
322 #define SIZE_SHIP_COLLISION 8*SCALE
323 #else
324 #define SIZE_SHIP_COLLISION 6*SCALE
325 #endif
327 #define LITTLE_SHIP 1
328 #define BIG_SHIP 2
329 #define ENEMY_BIG_PROBABILITY_START 10
330 #define ENEMY_APPEAR_PROBABILITY_START 35
331 #define ENEMY_APPEAR_TIMING_START 600
332 #define ENEMY_SPEED 4
333 #define ENEMY_MISSILE_LIFE_LENGTH (RES/2)
334 #if (LARGE_LCD)
335 #define SIZE_ENEMY_COLLISION 7*SCALE
336 #else
337 #define SIZE_ENEMY_COLLISION 5*SCALE
338 #endif
340 #define SIN_COS_SCALE 10000
342 #define FAST_ROT_CW_SIN 873
343 #define FAST_ROT_CW_COS 9963
344 #define FAST_ROT_ACW_SIN -873
345 #define FAST_ROT_ACW_COS 9963
347 #define MEDIUM_ROT_CW_SIN 350
348 #define MEDIUM_ROT_CW_COS 9994
349 #define MEDIUM_ROT_ACW_SIN -350
350 #define MEDIUM_ROT_ACW_COS 9994
352 #define SLOW_ROT_CW_SIN 350
353 #define SLOW_ROT_CW_COS 9994
354 #define SLOW_ROT_ACW_SIN -350
355 #define SLOW_ROT_ACW_COS 9994
357 #ifdef HAVE_LCD_COLOR
358 #define SHIP_ROT_CW_SIN 2419
359 #define SHIP_ROT_CW_COS 9702
360 #define SHIP_ROT_ACW_SIN -2419
361 #define SHIP_ROT_ACW_COS 9702
362 #else
363 #define SHIP_ROT_CW_SIN 3827
364 #define SHIP_ROT_CW_COS 9239
365 #define SHIP_ROT_ACW_SIN -3827
366 #define SHIP_ROT_ACW_COS 9239
367 #endif
370 #define SCALED_WIDTH (LCD_WIDTH*SCALE)
371 #define SCALED_HEIGHT (LCD_HEIGHT*SCALE)
372 #define CENTER_LCD_X (LCD_WIDTH/2)
373 #define CENTER_LCD_Y (LCD_HEIGHT/2)
375 #ifdef HAVE_LCD_COLOR
376 #define ASTEROID_R 230
377 #define ASTEROID_G 200
378 #define ASTEROID_B 100
379 #define SHIP_R 255
380 #define SHIP_G 255
381 #define SHIP_B 255
382 #define ENEMY_R 50
383 #define ENEMY_G 220
384 #define ENEMY_B 50
385 #define THRUST_R 200
386 #define THRUST_G 200
387 #define THRUST_B 0
389 #define COL_MISSILE LCD_RGBPACK(200,0,0)
390 #define COL_PLAYER LCD_RGBPACK(200,200,200)
391 #define COL_INVULN LCD_RGBPACK(100,100,200)
392 #define COL_STARS LCD_WHITE
393 #define COL_ASTEROID LCD_RGBPACK(ASTEROID_R,ASTEROID_G,ASTEROID_B)
394 #define COL_TEXT LCD_RGBPACK(200,200,255)
395 #define COL_ENEMY LCD_RGBPACK(ENEMY_R,ENEMY_G,ENEMY_B)
396 #define SET_FG rb->lcd_set_foreground
397 #define SET_BG rb->lcd_set_background
398 #else
399 #define SET_FG(x)
400 #define SET_BG(x)
401 #endif
403 #define SCORE_FILE PLUGIN_GAMES_DIR "/spacerocks.score"
404 #define NUM_SCORES 5
406 static struct highscore highscores[NUM_SCORES];
408 /* The array of points that make up an asteroid */
409 static const short asteroid_one[NUM_ASTEROID_VERTICES*2] =
411 -2, -12,
412 4, -8,
413 8, -14,
414 16, -5,
415 14, 0,
416 20, 2,
417 12, 14,
418 -4, 14,
419 -10, 6,
420 -10, -8,
423 /* The array of points that make up an asteroid */
424 static const short asteroid_two[NUM_ASTEROID_VERTICES*2] =
426 -2, -12,
427 4, -16,
428 6, -14,
429 16, -8,
430 14, 0,
431 20, 2,
432 12, 14,
433 -4, 14,
434 -10, 6,
435 -10, -8,
438 /* The array of points that make up an asteroid */
439 static const short asteroid_three[NUM_ASTEROID_VERTICES*2] =
441 -2, -12,
442 4, -16,
443 6, -14,
444 2, -8,
445 14, 0,
446 20, 2,
447 12, 14,
448 -4, 14,
449 -16, 6,
450 -10, -8,
453 /* The array of points the make up the ship */
454 static const short ship_vertices[NUM_SHIP_VERTICES*2] =
456 #if (LARGE_LCD)
457 0, -6,
458 4, 6,
459 0, 2,
460 -4, 6,
461 #else
462 0, -4,
463 3, 4,
464 0, 1,
465 -3, 4,
466 #endif
469 /* The array of points the make up the bad spaceship */
470 static const short enemy_vertices[NUM_ENEMY_VERTICES*2] =
472 #if (LARGE_LCD)
473 -8, 0,
474 -4, 4,
475 4, 4,
476 8, 0,
477 -8, 0,
478 8, 0,
479 4, -4,
480 -4, -4,
481 #else
482 -5, 0,
483 -2, 2,
484 2, 2,
485 5, 0,
486 -5, 0,
487 5, 0,
488 2, -2,
489 -2, -2,
490 #endif
493 enum asteroid_type
495 #if (LARGE_LCD)
496 SMALL = 2,
497 MEDIUM = 4,
498 LARGE = 6,
499 #else
500 SMALL = 1,
501 MEDIUM = 2,
502 LARGE = 3,
503 #endif
506 enum explosion_type
508 EXPLOSION_SHIP,
509 EXPLOSION_ASTEROID,
510 EXPLOSION_ENEMY,
511 EXPLOSION_THRUST,
514 enum game_state
516 GAME_OVER,
517 SHOW_LEVEL,
518 PLAY_MODE,
519 PAUSE_MODE,
522 struct Point
524 int x;
525 int y;
526 int dx;
527 int dy;
530 struct TrailPoint
532 struct Point position;
533 int alive;
534 #ifdef HAVE_LCD_COLOR
535 short r;
536 short g;
537 short b;
538 short dec;
539 #endif
542 /* Asteroid structure, contains an array of points */
543 struct Asteroid
545 struct Point position;
546 struct Point rotation;
547 struct Point vertices[NUM_ASTEROID_VERTICES];
548 bool exists;
549 int explode_countdown;
550 enum asteroid_type type;
551 int radius;
552 long speed_cos;
553 long speed_sin;
556 struct Ship
558 struct Point position;
559 struct Point rotation;
560 struct Point vertices[NUM_SHIP_VERTICES];
561 bool exists;
562 int explode_countdown;
563 int invulnerable_time;
566 struct Enemy
568 struct Point position;
569 struct Point vertices[NUM_ENEMY_VERTICES];
570 bool exists;
571 int explode_countdown;
572 int appear_countdown;
573 short size_probability;
574 short appear_probability;
575 short appear_timing;
578 struct Missile
580 struct Point position;
581 struct Point oldpoint;
582 int alive;
585 static enum game_state game_state;
586 static int asteroid_count;
587 static int next_missile_count;
588 static int next_thrust_count;
589 static int num_lives;
590 static int extra_life;
591 static int show_level_timeout;
592 static int current_level;
593 static int current_score;
595 static struct Ship ship;
596 static struct Point stars[NUM_STARS];
597 static struct Asteroid asteroids_array[MAX_NUM_ASTEROIDS];
598 static struct Missile missiles_array[MAX_NUM_MISSILES];
599 static struct Missile enemy_missile;
600 static struct Enemy enemy;
601 static struct Point lives_points[NUM_SHIP_VERTICES];
602 static struct TrailPoint trail_points[NUM_TRAIL_POINTS];
604 /*************************************************
605 ** Handle polygon and point
606 *************************************************/
608 /* Check if point is in a polygon */
609 static bool is_point_in_polygon(struct Point* vertices, int num_vertices,
610 int x, int y)
612 struct Point* pi;
613 struct Point* pj;
614 int n;
615 bool c = false;
617 if (x < -SCALED_WIDTH/2) x += SCALED_WIDTH;
618 else if (x > SCALED_WIDTH/2) x -= SCALED_WIDTH;
619 if (y < -SCALED_HEIGHT/2) y += SCALED_HEIGHT;
620 else if (y > SCALED_HEIGHT/2) y -= SCALED_HEIGHT;
622 pi = vertices;
623 pj = vertices + num_vertices-1;
625 n = num_vertices;
626 while (n--)
628 if ((((pi->y <= y) && (y < pj->y)) || ((pj->y <= y) && (y < pi->y))) &&
629 (x < (pj->x - pi->x) * (y - pi->y) / (pj->y - pi->y) + pi->x))
630 c = !c;
632 pj = pi;
633 pi++;
636 return c;
639 /* Check if point is within a rectangle */
640 static bool is_point_within_rectangle(struct Point* rect, struct Point* p,
641 int size)
643 int dx = p->x - rect->x;
644 int dy = p->y - rect->y;
645 #if SHOW_COL
646 rb->lcd_drawrect((rect->x - size)/SCALE, (rect->y - size)/SCALE,
647 (size*2+1)/SCALE, (size*2+1)/SCALE);
648 #endif
649 if (dx < -SCALED_WIDTH/2) dx += SCALED_WIDTH;
650 else if (dx > SCALED_WIDTH/2) dx -= SCALED_WIDTH;
651 if (dy < -SCALED_HEIGHT/2) dy += SCALED_HEIGHT;
652 else if (dy > SCALED_HEIGHT/2) dy -= SCALED_HEIGHT;
653 return (dx > -size && dx < size && dy > -size && dy < size);
656 /* Rotate polygon */
657 static void rotate_polygon(struct Point* vertices, int num_vertices,
658 struct Point* rotation, int cos, int sin)
660 struct Point* point;
661 int n;
662 long temp_x, temp_y;
664 temp_x = rotation->x;
665 temp_y = rotation->y;
666 rotation->x = (temp_x*cos - temp_y*sin)/SIN_COS_SCALE;
667 rotation->y = (temp_y*cos + temp_x*sin)/SIN_COS_SCALE;
668 #define MIN_SCALE (SIN_COS_SCALE-10)
669 #define MAX_SCALE (SIN_COS_SCALE+10)
670 /* normalize vector. this is not accurate but would be enough. */
671 temp_x = rotation->x*rotation->x + rotation->y*rotation->y;
672 if (temp_x <= MIN_SCALE*MIN_SCALE)
674 rotation->x = rotation->x*SIN_COS_SCALE/MIN_SCALE;
675 rotation->y = rotation->y*SIN_COS_SCALE/MIN_SCALE;
677 else if (temp_x >= MAX_SCALE*MAX_SCALE)
679 rotation->x = rotation->x*SIN_COS_SCALE/MAX_SCALE;
680 rotation->y = rotation->y*SIN_COS_SCALE/MAX_SCALE;
682 #undef MIN_SCALE
683 #undef MAX_SCALE
685 point = vertices;
686 n = num_vertices;
687 while (n--)
689 point->x = (point->dx*rotation->x - point->dy*rotation->y)/SIN_COS_SCALE;
690 point->y = (point->dy*rotation->x + point->dx*rotation->y)/SIN_COS_SCALE;
691 point++;
695 /* Draw polygon */
696 static void draw_polygon(struct Point* vertices, int num_vertices,
697 int px, int py)
699 int n, new_x, new_y, old_x, old_y;
700 struct Point *p;
701 bool draw_wrap;
703 if (px > SCALED_WIDTH - WRAP_GAP)
704 px -= SCALED_WIDTH;
705 if (py > SCALED_HEIGHT - WRAP_GAP)
706 py -= SCALED_HEIGHT;
708 draw_wrap = (px < WRAP_GAP || py < WRAP_GAP);
710 p = vertices + num_vertices - 1;
711 old_x = (p->x + px)/SCALE;
712 old_y = (p->y + py)/SCALE;
713 p = vertices;
714 n = num_vertices;
715 while (n--)
717 new_x = (p->x + px)/SCALE;
718 new_y = (p->y + py)/SCALE;
720 rb->lcd_drawline(old_x, old_y, new_x, new_y);
721 if (draw_wrap)
723 rb->lcd_drawline(old_x + LCD_WIDTH, old_y, new_x + LCD_WIDTH, new_y);
724 rb->lcd_drawline(old_x, old_y + LCD_HEIGHT, new_x, new_y + LCD_HEIGHT);
725 rb->lcd_drawline(old_x + LCD_WIDTH, old_y + LCD_HEIGHT,
726 new_x + LCD_WIDTH, new_y + LCD_HEIGHT);
728 old_x = new_x;
729 old_y = new_y;
730 p++;
734 static void move_point(struct Point* point)
736 point->x += point->dx;
737 point->y += point->dy;
739 /* Check bounds on the x-axis: */
740 point->x %= SCALED_WIDTH;
741 if (point->x < 0)
742 point->x += SCALED_WIDTH;
744 /* Check bounds on the y-axis: */
745 point->y %= SCALED_HEIGHT;
746 if (point->y < 0)
747 point->y += SCALED_HEIGHT;
750 /*************************************************
751 ** Handle trail blaiz.
752 *************************************************/
754 static void create_ship_trail(struct TrailPoint* tpoint)
756 tpoint->position.x += ship.vertices[2].x;
757 tpoint->position.y += ship.vertices[2].y;
758 tpoint->position.dx = -( ship.vertices[0].x - ship.vertices[2].x )/10;
759 tpoint->position.dy = -( ship.vertices[0].y - ship.vertices[2].y )/10;
762 static void create_explosion_trail(struct TrailPoint* tpoint)
764 tpoint->position.dx = (rb->rand()%5001)-2500;
765 tpoint->position.dy = (rb->rand()%5001)-2500;
768 static void create_trail_blaze(int colour, struct Point* position)
770 int numtoadd;
771 struct TrailPoint* tpoint;
772 int n;
774 if (colour != EXPLOSION_SHIP)
776 numtoadd = NUM_TRAIL_POINTS/5;
778 else
780 numtoadd = NUM_TRAIL_POINTS/8;
783 /* give the point a random countdown timer, so they dissapears at different
784 times */
785 tpoint = trail_points;
786 n = NUM_TRAIL_POINTS;
787 while (n--)
789 /* find a space in the array of trail_points that is NULL or DEAD or
790 whatever and place this one here. */
791 if (tpoint->alive <= 0)
793 /* take a random point near the position. */
794 tpoint->position.x = (rb->rand()%18000)-9000 + position->x;
795 tpoint->position.y = (rb->rand()%18000)-9000 + position->y;
797 switch(colour)
799 case EXPLOSION_SHIP:
800 create_explosion_trail(tpoint);
801 tpoint->alive = 51;
802 #ifdef HAVE_LCD_COLOR
803 tpoint->r = SHIP_R;
804 tpoint->g = SHIP_G;
805 tpoint->b = SHIP_B;
806 tpoint->dec = 2;
807 #endif
808 break;
809 case EXPLOSION_ASTEROID:
810 create_explosion_trail(tpoint);
811 tpoint->alive = 51;
812 #ifdef HAVE_LCD_COLOR
813 tpoint->r = ASTEROID_R;
814 tpoint->g = ASTEROID_G;
815 tpoint->b = ASTEROID_B;
816 tpoint->dec = 2;
817 #endif
818 break;
819 case EXPLOSION_ENEMY:
820 create_explosion_trail(tpoint);
821 tpoint->alive = 51;
822 #ifdef HAVE_LCD_COLOR
823 tpoint->r = ENEMY_R;
824 tpoint->g = ENEMY_G;
825 tpoint->b = ENEMY_B;
826 tpoint->dec = 2;
827 #endif
828 break;
829 case EXPLOSION_THRUST:
830 create_ship_trail(tpoint);
831 tpoint->alive = 17;
832 #ifdef HAVE_LCD_COLOR
833 tpoint->r = THRUST_R;
834 tpoint->g = THRUST_G;
835 tpoint->b = THRUST_B;
836 tpoint->dec = 4;
837 #endif
838 break;
841 /* give the points a speed based on direction of travel
842 - i.e. opposite */
843 tpoint->position.dx += position->dx;
844 tpoint->position.dy += position->dy;
846 numtoadd--;
847 if (numtoadd <= 0)
848 break;
850 tpoint++;
854 static void draw_and_move_trail_blaze(void)
856 struct TrailPoint* tpoint;
857 int n;
859 /* loop through, if alive then move and draw.
860 when drawn, countdown it's timer.
861 if zero kill it! */
863 tpoint = trail_points;
864 n = NUM_TRAIL_POINTS;
865 while (n--)
867 if (tpoint->alive > 0)
869 if (game_state != PAUSE_MODE)
871 tpoint->alive--;
872 move_point(&(tpoint->position));
873 #ifdef HAVE_LCD_COLOR
874 /* intensity = tpoint->alive/2; */
875 if (tpoint->r >= tpoint->dec) tpoint->r -= tpoint->dec;
876 if (tpoint->g >= tpoint->dec) tpoint->g -= tpoint->dec;
877 if (tpoint->b >= tpoint->dec) tpoint->b -= tpoint->dec;
878 #endif
880 SET_FG(LCD_RGBPACK(tpoint->r, tpoint->g, tpoint->b));
881 rb->lcd_drawpixel(tpoint->position.x/SCALE, tpoint->position.y/SCALE);
883 tpoint++;
887 /*************************************************
888 ** Handle asteroid.
889 *************************************************/
891 static void rotate_asteroid(struct Asteroid* asteroid)
893 rotate_polygon(asteroid->vertices, NUM_ASTEROID_VERTICES,
894 &asteroid->rotation,
895 asteroid->speed_cos, asteroid->speed_sin);
898 /* Initialise the passed Asteroid.
899 * if position is NULL, place it at the random loacation
900 * where ship doesn't exist
902 static void initialise_asteroid(struct Asteroid* asteroid,
903 enum asteroid_type type, struct Point *position)
905 const short *asteroid_vertices;
906 struct Point* point;
907 int n;
909 asteroid->exists = true;
910 asteroid->explode_countdown = 0;
911 asteroid->type = type;
913 /* Set the radius of the asteroid: */
914 asteroid->radius = (int)type*SCALE*3;
916 /* shall we move Clockwise and Fast */
917 n = rb->rand()%100;
918 if (n < 25)
920 asteroid->speed_cos = FAST_ROT_CW_COS;
921 asteroid->speed_sin = FAST_ROT_CW_SIN;
923 else if (n < 50)
925 asteroid->speed_cos = FAST_ROT_ACW_COS;
926 asteroid->speed_sin = FAST_ROT_ACW_SIN;
928 else if (n < 75)
930 asteroid->speed_cos = SLOW_ROT_ACW_COS;
931 asteroid->speed_sin = SLOW_ROT_ACW_SIN;
933 else
935 asteroid->speed_cos = SLOW_ROT_CW_COS;
936 asteroid->speed_sin = SLOW_ROT_CW_SIN;
939 n = rb->rand()%99;
940 if (n < 33)
941 asteroid_vertices = asteroid_one;
942 else if (n < 66)
943 asteroid_vertices = asteroid_two;
944 else
945 asteroid_vertices = asteroid_three;
947 point = asteroid->vertices;
948 for(n = 0; n < NUM_ASTEROID_VERTICES*2; n += 2)
950 point->x = asteroid_vertices[n];
951 point->y = asteroid_vertices[n+1];
952 point->x *= asteroid->radius/20;
953 point->y *= asteroid->radius/20;
954 /* dx and dy are used when rotate polygon */
955 point->dx = point->x;
956 point->dy = point->y;
957 point++;
960 if (!position)
962 do {
963 /* Set the position randomly: */
964 asteroid->position.x = (rb->rand()%SCALED_WIDTH);
965 asteroid->position.y = (rb->rand()%SCALED_HEIGHT);
966 } while (is_point_within_rectangle(&ship.position, &asteroid->position,
967 SPACE_CHECK_SIZE));
969 else
971 asteroid->position.x = position->x;
972 asteroid->position.y = position->y;
975 do {
976 asteroid->position.dx = (rb->rand()%ASTEROID_SPEED)-ASTEROID_SPEED/2;
977 } while (asteroid->position.dx == 0);
979 do {
980 asteroid->position.dy = (rb->rand()%ASTEROID_SPEED)-ASTEROID_SPEED/2;
981 } while (asteroid->position.dy == 0);
983 asteroid->position.dx *= SCALE/10;
984 asteroid->position.dy *= SCALE/10;
986 asteroid->rotation.x = SIN_COS_SCALE;
987 asteroid->rotation.y = 0;
989 /* Now rotate the asteroid a bit, so they all look a bit different */
990 for(n = (rb->rand()%30)+2; n--; )
991 rotate_asteroid(asteroid);
993 /* great, we've created an asteroid, don't forget to increment the total: */
994 asteroid_count++;
998 * Creates a new asteroid of the given 4type (size) and at the given location.
1000 static void create_asteroid(enum asteroid_type type, struct Point *position)
1002 struct Asteroid* asteroid;
1003 int n;
1005 asteroid = asteroids_array;
1006 n = MAX_NUM_ASTEROIDS;
1007 while (n--)
1009 if (!asteroid->exists && asteroid->explode_countdown <= 0)
1011 initialise_asteroid(asteroid, type, position);
1012 break;
1014 asteroid++;
1018 /* Draw and move all asteroids */
1019 static void draw_and_move_asteroids(void)
1021 struct Asteroid* asteroid;
1022 int n;
1024 SET_FG(COL_ASTEROID);
1026 asteroid = asteroids_array;
1027 n = MAX_NUM_ASTEROIDS;
1028 while (n--)
1030 if (asteroid->exists)
1032 draw_polygon(asteroid->vertices, NUM_ASTEROID_VERTICES,
1033 asteroid->position.x, asteroid->position.y);
1035 if (game_state != PAUSE_MODE)
1037 if (asteroid->exists)
1039 move_point(&asteroid->position);
1040 rotate_asteroid(asteroid);
1042 else if (asteroid->explode_countdown > 0)
1044 asteroid->explode_countdown--;
1047 asteroid++;
1051 static void explode_asteroid(struct Asteroid* asteroid)
1053 struct Point p;
1054 p.dx = asteroid->position.dx;
1055 p.dy = asteroid->position.dy;
1056 p.x = asteroid->position.x;
1057 p.y = asteroid->position.y;
1059 asteroid_count--;
1060 asteroid->exists = false;
1062 switch(asteroid->type)
1064 case SMALL:
1065 asteroid->explode_countdown = EXPLOSION_LENGTH;
1066 create_trail_blaze(EXPLOSION_ASTEROID, &p);
1067 break;
1069 case MEDIUM:
1070 create_asteroid(SMALL, &p);
1071 create_asteroid(SMALL, &p);
1072 break;
1074 case LARGE:
1075 create_asteroid(MEDIUM, &p);
1076 create_asteroid(MEDIUM, &p);
1077 break;
1081 /*************************************************
1082 ** Handle ship.
1083 *************************************************/
1085 /* Initialise the ship */
1086 static void initialise_ship(void)
1088 struct Point* point;
1089 struct Point* lives_point;
1090 int n;
1092 ship.position.x = CENTER_LCD_X * SCALE;
1093 ship.position.y = CENTER_LCD_Y * SCALE;
1094 ship.position.dx = 0;
1095 ship.position.dy = 0;
1096 ship.rotation.x = SIN_COS_SCALE;
1097 ship.rotation.y = 0;
1098 ship.exists = true;
1099 ship.explode_countdown = 0;
1100 ship.invulnerable_time = INVULNERABLE_TIME;
1102 point = ship.vertices;
1103 lives_point = lives_points;
1104 for(n = 0; n < NUM_SHIP_VERTICES*2; n += 2)
1106 point->x = ship_vertices[n];
1107 point->y = ship_vertices[n+1];
1108 point->x *= SCALE;
1109 point->y *= SCALE;
1110 /* dx and dy are used when rotate polygon */
1111 point->dx = point->x;
1112 point->dy = point->y;
1113 /* grab a copy of the ships points for the lives display: */
1114 lives_point->x = point->x;
1115 lives_point->y = point->y;
1117 point++;
1118 lives_point++;
1123 * Draws the ship, moves the ship and creates a new
1124 * one if it's finished exploding.
1126 static void draw_and_move_ship(void)
1128 if (ship.invulnerable_time > BLINK_TIME || ship.invulnerable_time % 2 != 0)
1130 SET_FG(COL_INVULN);
1132 else
1134 SET_FG(COL_PLAYER);
1137 if (ship.exists)
1139 draw_polygon(ship.vertices, NUM_SHIP_VERTICES,
1140 ship.position.x, ship.position.y);
1143 if (game_state != PAUSE_MODE)
1145 if (ship.exists)
1147 if (ship.invulnerable_time > 0)
1148 ship.invulnerable_time--;
1149 move_point(&ship.position);
1151 else if (ship.explode_countdown > 0)
1153 ship.explode_countdown--;
1154 if (ship.explode_countdown <= 0)
1156 num_lives--;
1157 if (num_lives <= 0)
1159 game_state = GAME_OVER;
1161 else
1163 initialise_ship();
1170 static void explode_ship(void)
1172 if (!ship.invulnerable_time)
1174 /* if not invulnerable, blow up ship */
1175 ship.explode_countdown = EXPLOSION_LENGTH;
1176 ship.exists = false;
1177 create_trail_blaze(EXPLOSION_SHIP, &ship.position);
1181 /* Rotate the ship using the passed sin & cos values */
1182 static void rotate_ship(int cos, int sin)
1184 if (ship.exists)
1186 rotate_polygon(ship.vertices, NUM_SHIP_VERTICES,
1187 &ship.rotation, cos, sin);
1191 static void thrust_ship(void)
1193 if (ship.exists)
1195 ship.position.dx += ( ship.vertices[0].x - ship.vertices[2].x )/20;
1196 ship.position.dy += ( ship.vertices[0].y - ship.vertices[2].y )/20;
1198 /* if dx and dy are below a certain threshold, then set 'em to 0
1199 but to do this we need to ascertain if the spacehip as moved on
1200 screen for more than a certain amount. */
1202 create_trail_blaze(EXPLOSION_THRUST, &ship.position);
1206 /* stop movement of ship, 'cos that's what happens when you go into hyperspace. */
1207 static void hyperspace(void)
1209 if (ship.exists)
1211 ship.position.dx = ship.position.dy = 0;
1212 ship.position.x = (rb->rand()%SCALED_WIDTH);
1213 ship.position.y = (rb->rand()%SCALED_HEIGHT);
1217 static void draw_lives(void)
1219 int n;
1220 #if (LARGE_LCD)
1221 int px = (LCD_WIDTH-1 - 4)*SCALE;
1222 int py = (LCD_HEIGHT-1 - 6)*SCALE;
1223 #else
1224 int px = (LCD_WIDTH-1 - 3)*SCALE;
1225 int py = (LCD_HEIGHT-1 - 4)*SCALE;
1226 #endif
1228 SET_FG(COL_PLAYER);
1230 n = num_lives-1;
1231 while (n--)
1233 draw_polygon(lives_points, NUM_SHIP_VERTICES, px, py);
1234 #if (LARGE_LCD)
1235 px -= 8*SCALE;
1236 #else
1237 px -= 6*SCALE;
1238 #endif
1243 * missile
1246 /* Initialise a missile */
1247 static void initialise_missile(struct Missile* missile)
1249 missile->position.x = ship.position.x + ship.vertices[0].x;
1250 missile->position.y = ship.position.y + ship.vertices[0].y;
1251 missile->position.dx = (ship.vertices[0].x - ship.vertices[2].x)/2;
1252 missile->position.dy = (ship.vertices[0].y - ship.vertices[2].y)/2;
1253 missile->alive = MISSILE_LIFE_LENGTH;
1254 missile->oldpoint.x = missile->position.x;
1255 missile->oldpoint.y = missile->position.y;
1258 /* Fire the next missile */
1259 static void fire_missile(void)
1261 struct Missile* missile;
1262 int n;
1264 if (ship.exists)
1266 missile = missiles_array;
1267 n = MAX_NUM_MISSILES;
1268 while (n--)
1270 if (missile->alive <= 0)
1272 initialise_missile(missile);
1273 break;
1275 missile++;
1280 /* Draw and Move all the missiles */
1281 static void draw_and_move_missiles(void)
1283 struct Missile* missile;
1284 struct Point vertices[2];
1285 int n;
1287 SET_FG(COL_MISSILE);
1289 missile = missiles_array;
1290 n = MAX_NUM_MISSILES;
1291 while (n--)
1293 if (missile->alive > 0)
1295 vertices[0].x = 0;
1296 vertices[0].y = 0;
1297 vertices[1].x = -missile->position.dx;
1298 vertices[1].y = -missile->position.dy;
1299 draw_polygon(vertices, 2, missile->position.x, missile->position.y);
1301 if (game_state != PAUSE_MODE)
1303 missile->oldpoint.x = missile->position.x;
1304 missile->oldpoint.y = missile->position.y;
1305 move_point(&missile->position);
1306 missile->alive--;
1309 missile++;
1313 /*************************************************
1314 ** Handle enemy.
1315 *************************************************/
1317 static void initialise_enemy(void)
1319 struct Point* point;
1320 int n;
1321 int size;
1323 if (rb->rand()%100 > enemy.size_probability)
1325 size = BIG_SHIP;
1326 enemy.size_probability++;
1327 if (enemy.size_probability > 90)
1329 enemy.size_probability = ENEMY_BIG_PROBABILITY_START;
1332 else
1334 size = LITTLE_SHIP;
1335 enemy.size_probability = ENEMY_BIG_PROBABILITY_START;
1338 enemy.exists = true;
1339 enemy.explode_countdown = 0;
1340 enemy.appear_countdown = enemy.appear_timing;
1342 point = enemy.vertices;
1343 for(n = 0; n < NUM_ENEMY_VERTICES*2; n += 2)
1345 point->x = enemy_vertices[n];
1346 point->y = enemy_vertices[n+1];
1347 point->x *= size*SCALE/2;
1348 point->y *= size*SCALE/2;
1349 point++;
1352 if (ship.position.x >= SCALED_WIDTH/2)
1354 enemy.position.dx = ENEMY_SPEED;
1355 enemy.position.x = 0;
1357 else
1359 enemy.position.dx = -ENEMY_SPEED;
1360 enemy.position.x = SCALED_WIDTH;
1363 if (ship.position.y >= SCALED_HEIGHT/2)
1365 enemy.position.dy = ENEMY_SPEED;
1366 enemy.position.y = 0;
1368 else
1370 enemy.position.dy = -ENEMY_SPEED;
1371 enemy.position.y = SCALED_HEIGHT;
1374 enemy.position.dx *= SCALE/10;
1375 enemy.position.dy *= SCALE/10;
1378 static void draw_and_move_enemy(void)
1380 SET_FG(COL_ENEMY);
1382 if (enemy.exists)
1384 draw_polygon(enemy.vertices, NUM_ENEMY_VERTICES,
1385 enemy.position.x, enemy.position.y);
1388 if (game_state != PAUSE_MODE)
1390 if (enemy.exists)
1392 enemy.position.x += enemy.position.dx;
1393 enemy.position.y += enemy.position.dy;
1395 if (enemy.position.x > SCALED_WIDTH || enemy.position.x < 0)
1396 enemy.exists = false;
1398 enemy.position.y %= SCALED_HEIGHT;
1399 if (enemy.position.y < 0)
1400 enemy.position.y += SCALED_HEIGHT;
1402 if ((rb->rand()%1000) < 10)
1403 enemy.position.dy = -enemy.position.dy;
1405 else if (enemy.explode_countdown > 0)
1407 enemy.explode_countdown--;
1409 else
1411 if (enemy.appear_countdown > 0)
1412 enemy.appear_countdown--;
1413 else if (rb->rand()%100 >= enemy.appear_probability)
1414 initialise_enemy();
1418 if (enemy_missile.alive <= 0)
1420 /* if no missile and the enemy is here and not exploding..
1421 then shoot baby! */
1422 if (enemy.exists && ship.exists &&
1423 game_state == PLAY_MODE && (rb->rand()%10) >= 5 )
1425 int dx = ship.position.x - enemy.position.x;
1426 int dy = ship.position.y - enemy.position.y;
1428 if (dx < -SCALED_WIDTH/2) dx += SCALED_WIDTH;
1429 else if (dx > SCALED_WIDTH/2) dx -= SCALED_WIDTH;
1430 if (dy < -SCALED_HEIGHT/2) dy += SCALED_HEIGHT;
1431 else if (dy > SCALED_HEIGHT/2) dy -= SCALED_HEIGHT;
1433 enemy_missile.position.x = enemy.position.x;
1434 enemy_missile.position.y = enemy.position.y;
1436 /* lame, needs to be sorted - it's trying to shoot at the ship */
1437 if (dx < -5*SCALE)
1438 enemy_missile.position.dx = -1;
1439 else if (dx > 5*SCALE)
1440 enemy_missile.position.dx = 1;
1441 else
1442 enemy_missile.position.dx = 0;
1444 if (dy < -5*SCALE)
1445 enemy_missile.position.dy = -1;
1446 else if (dy > 5*SCALE)
1447 enemy_missile.position.dy = 1;
1448 else
1449 enemy_missile.position.dy = 0;
1451 while (enemy_missile.position.dx == 0 &&
1452 enemy_missile.position.dy == 0)
1454 enemy_missile.position.dx = rb->rand()%2-1;
1455 enemy_missile.position.dy = rb->rand()%2-1;
1458 enemy_missile.position.dx *= SCALE;
1459 enemy_missile.position.dy *= SCALE;
1460 enemy_missile.alive = ENEMY_MISSILE_LIFE_LENGTH;
1463 else
1465 rb->lcd_fillrect( enemy_missile.position.x/SCALE,
1466 enemy_missile.position.y/SCALE,
1467 POINT_SIZE, POINT_SIZE );
1468 if (game_state != PAUSE_MODE)
1470 move_point(&enemy_missile.position);
1471 enemy_missile.alive--;
1476 /*************************************************
1477 ** Check collisions.
1478 *************************************************/
1480 /* Add score if missile hit asteroid or enemy */
1481 static void add_score(int val)
1483 current_score += val;
1484 if (current_score >= extra_life)
1486 num_lives++;
1487 extra_life += EXTRA_LIFE;
1491 static bool is_point_within_asteroid(struct Asteroid* asteroid,
1492 struct Point* point)
1494 if (is_point_within_rectangle(&asteroid->position, point, asteroid->radius)
1495 && is_point_in_polygon(asteroid->vertices, NUM_ASTEROID_VERTICES,
1496 point->x - asteroid->position.x,
1497 point->y - asteroid->position.y))
1499 explode_asteroid(asteroid);
1500 return true;
1502 else
1503 return false;
1506 static bool is_point_within_ship(struct Point* point)
1508 if (is_point_within_rectangle(&ship.position, point, SIZE_SHIP_COLLISION)
1509 && is_point_in_polygon(ship.vertices, NUM_SHIP_VERTICES,
1510 point->x - ship.position.x,
1511 point->y - ship.position.y))
1513 return true;
1515 else
1516 return false;
1519 static bool is_point_within_enemy(struct Point* point)
1521 if (is_point_within_rectangle(&enemy.position, point, SIZE_ENEMY_COLLISION))
1523 add_score(5);
1524 enemy.explode_countdown = EXPLOSION_LENGTH;
1525 enemy.exists = false;
1526 create_trail_blaze(EXPLOSION_ENEMY, &enemy.position);
1527 return true;
1529 else
1530 return false;
1533 static bool is_ship_within_asteroid(struct Asteroid* asteroid)
1535 struct Point p;
1537 if (!is_point_within_rectangle(&asteroid->position, &ship.position,
1538 asteroid->radius+SIZE_SHIP_COLLISION))
1539 return false;
1541 p.x = ship.position.x + ship.vertices[0].x;
1542 p.y = ship.position.y + ship.vertices[0].y;
1543 if (is_point_within_asteroid(asteroid, &p))
1544 return true;
1546 p.x = ship.position.x + ship.vertices[1].x;
1547 p.y = ship.position.y + ship.vertices[1].y;
1548 if (is_point_within_asteroid(asteroid, &p))
1549 return true;
1551 p.x = ship.position.x + ship.vertices[3].x;
1552 p.y = ship.position.y + ship.vertices[3].y;
1553 if (is_point_within_asteroid(asteroid, &p))
1554 return true;
1556 return false;
1559 /* Check for collsions between the missiles and the asteroids and the ship */
1560 static void check_collisions(void)
1562 struct Missile* missile;
1563 struct Asteroid* asteroid;
1564 int m, n;
1565 bool asteroids_onscreen = false;
1567 asteroid = asteroids_array;
1568 m = MAX_NUM_ASTEROIDS;
1569 while (m--)
1571 /* if the asteroids exists then test missile collision: */
1572 if (asteroid->exists)
1574 missile = missiles_array;
1575 n = MAX_NUM_MISSILES;
1576 while (n--)
1578 /* if the missiles exists: */
1579 if (missile->alive > 0)
1581 /* has the missile hit the asteroid? */
1582 if (is_point_within_asteroid(asteroid, &missile->position) ||
1583 is_point_within_asteroid(asteroid, &missile->oldpoint))
1585 add_score(1);
1586 missile->alive = 0;
1587 break;
1590 missile++;
1593 /* now check collision with ship: */
1594 if (asteroid->exists && ship.exists)
1596 if (is_ship_within_asteroid(asteroid))
1598 add_score(1);
1599 explode_ship();
1603 /* has the enemy missile blown something up? */
1604 if (asteroid->exists && enemy_missile.alive > 0)
1606 if (is_point_within_asteroid(asteroid, &enemy_missile.position))
1608 enemy_missile.alive = 0;
1613 /* is an asteroid still exploding? */
1614 if (asteroid->explode_countdown > 0)
1615 asteroids_onscreen = true;
1617 asteroid++;
1620 /* now check collision between ship and enemy */
1621 if (enemy.exists && ship.exists)
1623 /* has the enemy collided with the ship? */
1624 if (is_point_within_enemy(&ship.position))
1626 explode_ship();
1627 create_trail_blaze(EXPLOSION_ENEMY, &enemy.position);
1630 if (enemy.exists)
1632 /* Now see if the enemy has been shot at by the ships missiles: */
1633 missile = missiles_array;
1634 n = MAX_NUM_MISSILES;
1635 while (n--)
1637 if (missile->alive > 0 &&
1638 is_point_within_enemy(&missile->position))
1640 missile->alive = 0;
1641 break;
1643 missile++;
1648 /* test collision with enemy missile and ship: */
1649 if (enemy_missile.alive > 0 && is_point_within_ship(&enemy_missile.position))
1651 explode_ship();
1652 enemy_missile.alive = 0;
1653 enemy_missile.position.x = enemy_missile.position.y = 0;
1656 /* if all asteroids cleared then start again: */
1657 if (asteroid_count == 0 && !asteroids_onscreen
1658 && !enemy.exists && enemy.explode_countdown <= 0)
1660 current_level++;
1661 if (current_level > MAX_LEVEL)
1662 current_level = START_LEVEL;
1663 enemy.appear_probability += 5;
1664 if (enemy.appear_probability >= 100)
1665 enemy.appear_probability = ENEMY_APPEAR_PROBABILITY_START;
1666 enemy.appear_timing -= 30;
1667 if (enemy.appear_timing < 30)
1668 enemy.appear_timing = 30;
1669 game_state = SHOW_LEVEL;
1670 show_level_timeout = SHOW_LEVEL_TIME;
1675 * stars
1678 static void create_stars(void)
1680 struct Point* p;
1681 int n;
1683 p = stars;
1684 n = NUM_STARS;
1685 while (n--)
1687 p->x = (rb->rand()%LCD_WIDTH);
1688 p->y = (rb->rand()%LCD_HEIGHT);
1689 p++;
1693 static void drawstars(void)
1695 struct Point* p;
1696 int n;
1698 SET_FG(COL_STARS);
1700 p = stars;
1701 n = NUM_STARS;
1702 while (n--)
1704 rb->lcd_drawpixel(p->x , p->y);
1705 p++;
1709 /*************************************************
1710 ** Creates start_num number of new asteroids of
1711 ** full size.
1712 **************************************************/
1713 static void initialise_level(int start_num)
1715 struct Asteroid* asteroid;
1716 struct Missile* missile;
1717 struct TrailPoint* tpoint;
1718 int n;
1719 asteroid_count = next_missile_count = next_thrust_count = 0;
1721 /* no enemy */
1722 enemy.exists = 0;
1723 enemy.explode_countdown = 0;
1724 enemy_missile.alive = 0;
1726 /* clear asteroids */
1727 asteroid = asteroids_array;
1728 n = MAX_NUM_ASTEROIDS;
1729 while (n--)
1731 asteroid->exists = false;
1732 asteroid++;
1735 /* make some LARGE asteroids */
1736 for(n = 0; n < start_num; n++)
1737 initialise_asteroid(&asteroids_array[n], LARGE, NULL);
1739 /* ensure all missiles are out of action: */
1740 missile = missiles_array;
1741 n = MAX_NUM_MISSILES;
1742 while (n--)
1744 missile->alive = 0;
1745 missile++;
1748 tpoint = trail_points;
1749 n = NUM_TRAIL_POINTS;
1750 while (n--)
1752 tpoint->alive = 0;
1753 tpoint++;
1757 static void initialise_game(void)
1759 enemy.appear_probability = ENEMY_APPEAR_PROBABILITY_START;
1760 enemy.appear_timing = ENEMY_APPEAR_TIMING_START;
1761 enemy.appear_countdown = enemy.appear_timing;
1762 enemy.size_probability = ENEMY_BIG_PROBABILITY_START;
1763 current_level = START_LEVEL;
1764 num_lives = START_LIVES;
1765 extra_life = EXTRA_LIFE;
1766 current_score = 0;
1767 initialise_ship();
1768 initialise_level(0);
1769 game_state = SHOW_LEVEL;
1770 show_level_timeout = SHOW_LEVEL_TIME;
1773 /* menu stuff */
1774 static bool spacerocks_help(void)
1776 static char *help_text[] = {
1777 "Spacerocks", "", "Aim", "",
1778 "The", "goal", "of", "the", "game", "is", "to", "blow", "up",
1779 "the", "asteroids", "and", "avoid", "being", "hit", "by", "them.",
1780 "Also", "you'd", "better", "watch", "out", "for", "the", "UFOs!"
1782 static struct style_text formation[]={
1783 { 0, TEXT_CENTER|TEXT_UNDERLINE },
1784 { 2, C_RED },
1785 LAST_STYLE_ITEM
1788 rb->lcd_setfont(FONT_UI);
1789 SET_BG(LCD_BLACK);
1790 SET_FG(LCD_WHITE);
1791 if (display_text(ARRAYLEN(help_text), help_text, formation, NULL, true))
1792 return true;
1793 rb->lcd_setfont(FONT_SYSFIXED);
1795 return false;
1798 #define PLUGIN_OTHER 10
1799 static bool ingame;
1800 static int spacerocks_menu_cb(int action, const struct menu_item_ex *this_item)
1802 if (action == ACTION_REQUEST_MENUITEM
1803 && !ingame && ((intptr_t)this_item)==0)
1804 return ACTION_EXIT_MENUITEM;
1805 return action;
1808 static int spacerocks_menu(void)
1810 int selection = 0;
1811 MENUITEM_STRINGLIST(main_menu, "Spacerocks Menu", spacerocks_menu_cb,
1812 "Resume Game", "Start New Game",
1813 "Help", "High Scores",
1814 "Playback Control", "Quit");
1815 rb->button_clear_queue();
1817 while (1)
1819 switch (rb->do_menu(&main_menu, &selection, NULL, false))
1821 case 0:
1822 return PLUGIN_OTHER;
1823 case 1:
1824 initialise_game();
1825 return PLUGIN_OTHER;
1826 case 2:
1827 if (spacerocks_help())
1828 return PLUGIN_USB_CONNECTED;
1829 break;
1830 case 3:
1831 highscore_show(-1, highscores, NUM_SCORES, true);
1832 break;
1833 case 4:
1834 playback_control(NULL);
1835 break;
1836 case 5:
1837 return PLUGIN_OK;
1838 case MENU_ATTACHED_USB:
1839 return PLUGIN_USB_CONNECTED;
1840 default:
1841 break;
1846 static int spacerocks_game_loop(void)
1848 int button;
1849 int end;
1850 int position;
1851 int ret;
1853 if ((ret = spacerocks_menu()) != PLUGIN_OTHER)
1854 return ret;
1856 SET_BG(LCD_BLACK);
1858 ingame = true;
1859 while (true)
1861 end = *rb->current_tick + (CYCLETIME * HZ) / 1000;
1862 rb->lcd_clear_display();
1863 SET_FG(COL_TEXT);
1864 switch(game_state)
1866 case GAME_OVER:
1867 ingame = false;
1868 rb->splash (HZ * 2, "Game Over");
1869 rb->lcd_clear_display();
1870 position = highscore_update(current_score, current_level, "",
1871 highscores, NUM_SCORES);
1872 if (position != -1)
1874 if (position == 0)
1875 rb->splash(HZ*2, "New High Score");
1876 highscore_show(position, highscores, NUM_SCORES, true);
1878 return PLUGIN_OTHER;
1879 break;
1881 case PAUSE_MODE:
1882 rb->lcd_putsxyf(1,LCD_HEIGHT-8, "score %d ", current_score);
1883 rb->lcd_putsxy(CENTER_LCD_X - 15,
1884 CENTER_LCD_Y + CENTER_LCD_Y/2 - 4, "pause");
1885 draw_and_move_missiles();
1886 draw_lives();
1887 draw_and_move_ship();
1888 break;
1890 case PLAY_MODE:
1891 rb->lcd_putsxyf(1, LCD_HEIGHT-8, "score %d ", current_score);
1892 draw_and_move_missiles();
1893 draw_lives();
1894 check_collisions();
1895 draw_and_move_ship();
1896 break;
1898 case SHOW_LEVEL:
1899 rb->lcd_putsxyf(1, LCD_HEIGHT-8, "score %d ", current_score);
1900 rb->lcd_putsxyf(CENTER_LCD_X - 20,
1901 CENTER_LCD_Y + CENTER_LCD_Y/2 - 4,
1902 "stage %d ", current_level);
1903 draw_lives();
1904 draw_and_move_ship();
1905 show_level_timeout--;
1906 if (show_level_timeout <= 0)
1908 initialise_level(current_level);
1909 game_state = PLAY_MODE;
1911 break;
1913 draw_and_move_trail_blaze();
1914 drawstars();
1915 draw_and_move_asteroids();
1916 draw_and_move_enemy();
1918 rb->lcd_update();
1920 #ifdef HAS_BUTTON_HOLD
1921 if (rb->button_hold() && game_state == PLAY_MODE)
1922 game_state = PAUSE_MODE;
1923 #endif
1924 button = rb->button_get(false);
1925 switch(button)
1927 case(AST_QUIT):
1928 return PLUGIN_OTHER;
1929 break;
1930 #ifdef AST_PAUSE
1931 case(AST_PAUSE):
1932 if (game_state == PAUSE_MODE)
1933 game_state = PLAY_MODE;
1934 else if (game_state == PLAY_MODE)
1935 game_state = PAUSE_MODE;
1936 break;
1937 #endif
1938 case (AST_LEFT):
1939 case (AST_LEFT | BUTTON_REPEAT):
1940 if (game_state == PLAY_MODE || game_state == SHOW_LEVEL)
1941 rotate_ship(SHIP_ROT_ACW_COS, SHIP_ROT_ACW_SIN);
1942 break;
1944 case (AST_RIGHT):
1945 case (AST_RIGHT | BUTTON_REPEAT):
1946 if (game_state == PLAY_MODE || game_state == SHOW_LEVEL)
1947 rotate_ship(SHIP_ROT_CW_COS, SHIP_ROT_CW_SIN);
1948 break;
1950 case (AST_THRUST):
1951 case (AST_THRUST | BUTTON_REPEAT):
1952 if (game_state == PLAY_MODE || game_state == SHOW_LEVEL)
1954 if (next_thrust_count <= 0)
1956 next_thrust_count = 5;
1957 thrust_ship();
1960 break;
1962 case (AST_HYPERSPACE):
1963 if (game_state == PLAY_MODE)
1964 hyperspace();
1965 /* maybe shield if it gets too hard */
1966 break;
1968 case (AST_FIRE):
1969 case (AST_FIRE | BUTTON_REPEAT):
1970 if (game_state == PLAY_MODE)
1972 if (next_missile_count <= 0)
1974 fire_missile();
1975 next_missile_count = 10;
1978 else if(game_state == PAUSE_MODE)
1979 game_state = PLAY_MODE;
1980 break;
1982 default:
1983 if (rb->default_event_handler(button)==SYS_USB_CONNECTED)
1984 return PLUGIN_USB_CONNECTED;
1985 break;
1988 if (next_missile_count > 0)
1989 next_missile_count--;
1991 if (next_thrust_count > 0)
1992 next_thrust_count--;
1994 if (TIME_BEFORE(*rb->current_tick, end))
1995 rb->sleep(end-*rb->current_tick);
1996 else
1997 rb->yield();
2001 enum plugin_status plugin_start(const void* parameter)
2003 (void)parameter;
2004 int ret = PLUGIN_OTHER;
2006 #if LCD_DEPTH > 1
2007 rb->lcd_set_backdrop(NULL);
2008 #endif
2009 /* universal font */
2010 rb->lcd_setfont(FONT_SYSFIXED);
2011 /* Turn off backlight timeout */
2012 backlight_force_on(); /* backlight control in lib/helper.c */
2013 highscore_load(SCORE_FILE, highscores, NUM_SCORES);
2014 rb->srand(*rb->current_tick);
2016 /* create stars once, and once only: */
2017 create_stars();
2019 while (ret == PLUGIN_OTHER)
2020 ret = spacerocks_game_loop();
2022 rb->lcd_setfont(FONT_UI);
2023 highscore_save(SCORE_FILE, highscores, NUM_SCORES);
2024 /* Turn on backlight timeout (revert to settings) */
2025 backlight_use_settings(); /* backlight control in lib/helper.c */
2027 return ret;