A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita
audio
rust
zig
deno
mpris
rockbox
mpd
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2006 by Mat Holton
11 *
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.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
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"
27
28
29
30/* variable button definitions */
31#if (CONFIG_KEYPAD == IRIVER_H100_PAD) || \
32 (CONFIG_KEYPAD == IRIVER_H300_PAD)
33#define AST_PAUSE BUTTON_REC
34#define AST_QUIT BUTTON_OFF
35#define AST_THRUST BUTTON_UP
36#define AST_HYPERSPACE BUTTON_DOWN
37#define AST_LEFT BUTTON_LEFT
38#define AST_RIGHT BUTTON_RIGHT
39#define AST_FIRE BUTTON_SELECT
40
41#define AST_RC_QUIT BUTTON_RC_STOP
42
43#elif (CONFIG_KEYPAD == IAUDIO_X5M5_PAD)
44#define AST_PAUSE BUTTON_PLAY
45#define AST_QUIT BUTTON_POWER
46#define AST_THRUST BUTTON_UP
47#define AST_HYPERSPACE BUTTON_DOWN
48#define AST_LEFT BUTTON_LEFT
49#define AST_RIGHT BUTTON_RIGHT
50#define AST_FIRE BUTTON_SELECT
51
52#elif (CONFIG_KEYPAD == IPOD_4G_PAD) || (CONFIG_KEYPAD == IPOD_3G_PAD) || \
53 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
54#define AST_PAUSE BUTTON_PLAY
55#define AST_QUIT BUTTON_MENU
56#define AST_THRUST BUTTON_RIGHT
57#define AST_HYPERSPACE BUTTON_LEFT
58#define AST_LEFT BUTTON_SCROLL_BACK
59#define AST_RIGHT BUTTON_SCROLL_FWD
60#define AST_FIRE BUTTON_SELECT
61
62#elif (CONFIG_KEYPAD == GIGABEAT_PAD)
63#define AST_PAUSE BUTTON_A
64#define AST_QUIT BUTTON_POWER
65#define AST_THRUST BUTTON_UP
66#define AST_HYPERSPACE BUTTON_DOWN
67#define AST_LEFT BUTTON_LEFT
68#define AST_RIGHT BUTTON_RIGHT
69#define AST_FIRE BUTTON_SELECT
70
71#elif (CONFIG_KEYPAD == SANSA_E200_PAD)
72#define AST_PAUSE BUTTON_REC
73#define AST_QUIT BUTTON_POWER
74#define AST_THRUST BUTTON_UP
75#define AST_HYPERSPACE BUTTON_DOWN
76#define AST_LEFT BUTTON_SCROLL_BACK
77#define AST_RIGHT BUTTON_SCROLL_FWD
78#define AST_FIRE BUTTON_SELECT
79
80#elif (CONFIG_KEYPAD == SANSA_FUZE_PAD)
81#define AST_PAUSE (BUTTON_SELECT | BUTTON_UP)
82#define AST_QUIT (BUTTON_HOME|BUTTON_REPEAT)
83#define AST_THRUST BUTTON_UP
84#define AST_HYPERSPACE BUTTON_DOWN
85#define AST_LEFT BUTTON_SCROLL_BACK
86#define AST_RIGHT BUTTON_SCROLL_FWD
87#define AST_FIRE BUTTON_SELECT
88
89#elif (CONFIG_KEYPAD == SANSA_C200_PAD)
90#define AST_PAUSE BUTTON_REC
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
97
98#elif (CONFIG_KEYPAD == SANSA_CLIP_PAD)
99#define AST_PAUSE BUTTON_HOME
100#define AST_QUIT BUTTON_POWER
101#define AST_THRUST BUTTON_UP
102#define AST_HYPERSPACE BUTTON_DOWN
103#define AST_LEFT BUTTON_LEFT
104#define AST_RIGHT BUTTON_RIGHT
105#define AST_FIRE BUTTON_SELECT
106
107#elif (CONFIG_KEYPAD == SANSA_M200_PAD)
108#define AST_PAUSE (BUTTON_SELECT | BUTTON_UP)
109#define AST_QUIT BUTTON_POWER
110#define AST_THRUST BUTTON_UP
111#define AST_HYPERSPACE BUTTON_DOWN
112#define AST_LEFT BUTTON_LEFT
113#define AST_RIGHT BUTTON_RIGHT
114#define AST_FIRE (BUTTON_SELECT | BUTTON_REL)
115
116#elif (CONFIG_KEYPAD == IRIVER_H10_PAD)
117#define AST_PAUSE BUTTON_PLAY
118#define AST_QUIT BUTTON_POWER
119#define AST_THRUST BUTTON_SCROLL_UP
120#define AST_HYPERSPACE BUTTON_SCROLL_DOWN
121#define AST_LEFT BUTTON_LEFT
122#define AST_RIGHT BUTTON_RIGHT
123#define AST_FIRE BUTTON_REW
124
125#elif (CONFIG_KEYPAD == GIGABEAT_S_PAD)
126#define AST_PAUSE BUTTON_PLAY
127#define AST_QUIT BUTTON_BACK
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
133
134#elif (CONFIG_KEYPAD == MROBE100_PAD)
135#define AST_PAUSE BUTTON_DISPLAY
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
142
143#elif CONFIG_KEYPAD == IAUDIO_M3_PAD
144#define AST_PAUSE BUTTON_RC_PLAY
145#define AST_QUIT BUTTON_RC_REC
146#define AST_THRUST BUTTON_RC_VOL_UP
147#define AST_HYPERSPACE BUTTON_RC_VOL_DOWN
148#define AST_LEFT BUTTON_RC_REW
149#define AST_RIGHT BUTTON_RC_FF
150#define AST_FIRE BUTTON_RC_MODE
151
152#elif (CONFIG_KEYPAD == COWON_D2_PAD)
153#define AST_QUIT BUTTON_POWER
154
155#elif CONFIG_KEYPAD == CREATIVEZVM_PAD
156#define AST_PAUSE BUTTON_PLAY
157#define AST_QUIT BUTTON_BACK
158#define AST_THRUST BUTTON_UP
159#define AST_HYPERSPACE BUTTON_DOWN
160#define AST_LEFT BUTTON_LEFT
161#define AST_RIGHT BUTTON_RIGHT
162#define AST_FIRE BUTTON_SELECT
163
164#elif CONFIG_KEYPAD == CREATIVE_ZENXFI3_PAD
165#define AST_PAUSE (BUTTON_PLAY | BUTTON_REL)
166#define AST_QUIT BUTTON_POWER
167#define AST_THRUST BUTTON_UP
168#define AST_HYPERSPACE BUTTON_DOWN
169#define AST_LEFT BUTTON_BACK
170#define AST_RIGHT BUTTON_MENU
171#define AST_FIRE BUTTON_VOL_UP
172
173#elif CONFIG_KEYPAD == PHILIPS_HDD1630_PAD
174#define AST_PAUSE BUTTON_VIEW
175#define AST_QUIT BUTTON_POWER
176#define AST_THRUST BUTTON_UP
177#define AST_HYPERSPACE BUTTON_DOWN
178#define AST_LEFT BUTTON_LEFT
179#define AST_RIGHT BUTTON_RIGHT
180#define AST_FIRE BUTTON_PLAYLIST
181
182#elif CONFIG_KEYPAD == PHILIPS_HDD6330_PAD
183#define AST_PAUSE BUTTON_PLAY
184#define AST_QUIT BUTTON_POWER
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_VOL_DOWN
190
191#elif CONFIG_KEYPAD == PHILIPS_SA9200_PAD
192#define AST_PAUSE BUTTON_RIGHT
193#define AST_QUIT BUTTON_POWER
194#define AST_THRUST BUTTON_UP
195#define AST_HYPERSPACE BUTTON_DOWN
196#define AST_LEFT BUTTON_PREV
197#define AST_RIGHT BUTTON_NEXT
198#define AST_FIRE BUTTON_LEFT
199
200#elif (CONFIG_KEYPAD == ONDAVX747_PAD) || \
201 (CONFIG_KEYPAD == ONDAVX777_PAD) || \
202 (CONFIG_KEYPAD == MROBE500_PAD)
203#define AST_QUIT BUTTON_POWER
204
205#elif (CONFIG_KEYPAD == SAMSUNG_YH820_PAD) || \
206 (CONFIG_KEYPAD == SAMSUNG_YH92X_PAD)
207
208#define AST_PAUSE BUTTON_FFWD
209#define AST_QUIT BUTTON_REW
210#define AST_THRUST BUTTON_UP
211#define AST_HYPERSPACE BUTTON_DOWN
212#define AST_LEFT BUTTON_LEFT
213#define AST_RIGHT BUTTON_RIGHT
214#define AST_FIRE BUTTON_PLAY
215
216#elif (CONFIG_KEYPAD == PBELL_VIBE500_PAD)
217
218#define AST_PAUSE BUTTON_PLAY
219#define AST_QUIT BUTTON_REC
220#define AST_THRUST BUTTON_UP
221#define AST_HYPERSPACE BUTTON_DOWN
222#define AST_LEFT BUTTON_PREV
223#define AST_RIGHT BUTTON_NEXT
224#define AST_FIRE BUTTON_OK
225
226#elif (CONFIG_KEYPAD == MPIO_HD200_PAD)
227
228#define AST_PAUSE (BUTTON_PLAY|BUTTON_FUNC)
229#define AST_QUIT (BUTTON_REC|BUTTON_PLAY)
230#define AST_THRUST BUTTON_REC
231#define AST_HYPERSPACE BUTTON_PLAY
232#define AST_LEFT BUTTON_REW
233#define AST_RIGHT BUTTON_FF
234#define AST_FIRE BUTTON_FUNC
235
236#elif (CONFIG_KEYPAD == MPIO_HD300_PAD)
237
238#define AST_PAUSE (BUTTON_PLAY|BUTTON_REL)
239#define AST_QUIT (BUTTON_MENU|BUTTON_REPEAT)
240#define AST_THRUST BUTTON_REC
241#define AST_HYPERSPACE (BUTTON_PLAY|BUTTON_REPEAT)
242#define AST_LEFT BUTTON_UP
243#define AST_RIGHT BUTTON_DOWN
244#define AST_FIRE BUTTON_ENTER
245
246#elif (CONFIG_KEYPAD == SANSA_FUZEPLUS_PAD)
247
248#define AST_PAUSE BUTTON_PLAYPAUSE
249#define AST_QUIT BUTTON_POWER
250#define AST_THRUST BUTTON_UP
251#define AST_HYPERSPACE BUTTON_BACK
252#define AST_LEFT BUTTON_LEFT
253#define AST_RIGHT BUTTON_RIGHT
254#define AST_FIRE BUTTON_SELECT
255
256#elif (CONFIG_KEYPAD == SANSA_CONNECT_PAD)
257
258#define ALT_PAUSE BUTTON_VOL_DOWN
259#define AST_QUIT BUTTON_POWER
260#define AST_THRUST BUTTON_UP
261#define AST_HYPERSPACE BUTTON_DOWN
262#define AST_LEFT BUTTON_SCROLL_BACK
263#define AST_RIGHT BUTTON_SCROLL_FWD
264#define AST_FIRE BUTTON_SELECT
265
266#elif (CONFIG_KEYPAD == SAMSUNG_YPR0_PAD)
267#define AST_PAUSE BUTTON_MENU
268#define AST_QUIT BUTTON_BACK
269#define AST_THRUST BUTTON_UP
270#define AST_HYPERSPACE BUTTON_DOWN
271#define AST_LEFT BUTTON_LEFT
272#define AST_RIGHT BUTTON_RIGHT
273#define AST_FIRE BUTTON_SELECT
274
275#elif (CONFIG_KEYPAD == HM60X_PAD)
276#define AST_PAUSE (BUTTON_SELECT|BUTTON_POWER)
277#define AST_QUIT BUTTON_POWER
278#define AST_THRUST BUTTON_UP
279#define AST_HYPERSPACE BUTTON_DOWN
280#define AST_LEFT BUTTON_LEFT
281#define AST_RIGHT BUTTON_RIGHT
282#define AST_FIRE BUTTON_SELECT
283
284#elif (CONFIG_KEYPAD == HM801_PAD)
285#define AST_PAUSE BUTTON_PLAY
286#define AST_QUIT BUTTON_POWER
287#define AST_THRUST BUTTON_UP
288#define AST_HYPERSPACE BUTTON_DOWN
289#define AST_LEFT BUTTON_LEFT
290#define AST_RIGHT BUTTON_RIGHT
291#define AST_FIRE BUTTON_SELECT
292
293#elif CONFIG_KEYPAD == SONY_NWZ_PAD
294#define AST_PAUSE BUTTON_POWER
295#define AST_QUIT BUTTON_BACK
296#define AST_THRUST BUTTON_UP
297#define AST_HYPERSPACE BUTTON_DOWN
298#define AST_LEFT BUTTON_LEFT
299#define AST_RIGHT BUTTON_RIGHT
300#define AST_FIRE BUTTON_PLAY
301
302#elif CONFIG_KEYPAD == CREATIVE_ZEN_PAD
303#define AST_PAUSE BUTTON_PLAYPAUSE
304#define AST_QUIT BUTTON_BACK
305#define AST_THRUST BUTTON_UP
306#define AST_HYPERSPACE BUTTON_DOWN
307#define AST_LEFT BUTTON_LEFT
308#define AST_RIGHT BUTTON_RIGHT
309#define AST_FIRE BUTTON_SELECT
310
311#elif (CONFIG_KEYPAD == DX50_PAD)
312#define AST_QUIT BUTTON_POWER
313#define AST_THRUST BUTTON_VOL_UP
314#define AST_HYPERSPACE BUTTON_VOL_DOWN
315#define AST_LEFT BUTTON_LEFT
316#define AST_RIGHT BUTTON_PLAY
317#define AST_FIRE BUTTON_RIGHT
318
319#elif CONFIG_KEYPAD == CREATIVE_ZENXFI2_PAD
320#define AST_QUIT BUTTON_POWER
321
322#elif (CONFIG_KEYPAD == AGPTEK_ROCKER_PAD)
323#define AST_QUIT BUTTON_POWER
324#define AST_THRUST BUTTON_UP
325#define AST_HYPERSPACE BUTTON_VOLUP
326#define AST_LEFT BUTTON_LEFT
327#define AST_RIGHT BUTTON_RIGHT
328#define AST_FIRE BUTTON_SELECT
329
330#elif (CONFIG_KEYPAD == XDUOO_X3_PAD) || (CONFIG_KEYPAD == XDUOO_X3II_PAD) || (CONFIG_KEYPAD == XDUOO_X20_PAD)
331#define AST_PAUSE BUTTON_VOL_UP
332#define AST_QUIT BUTTON_POWER
333#define AST_THRUST BUTTON_HOME
334#define AST_HYPERSPACE BUTTON_OPTION
335#define AST_LEFT BUTTON_PREV
336#define AST_RIGHT BUTTON_NEXT
337#define AST_FIRE BUTTON_PLAY
338
339#elif (CONFIG_KEYPAD == FIIO_M3K_LINUX_PAD)
340#define AST_PAUSE BUTTON_VOL_UP
341#define AST_QUIT BUTTON_POWER
342#define AST_THRUST BUTTON_HOME
343#define AST_HYPERSPACE BUTTON_OPTION
344#define AST_LEFT BUTTON_PREV
345#define AST_RIGHT BUTTON_NEXT
346#define AST_FIRE BUTTON_PLAY
347
348#elif (CONFIG_KEYPAD == IHIFI_770_PAD) || (CONFIG_KEYPAD == IHIFI_800_PAD)
349#define AST_PAUSE BUTTON_PLAY
350#define AST_QUIT BUTTON_POWER
351#define AST_THRUST BUTTON_NEXT
352#define AST_HYPERSPACE BUTTON_PREV
353#define AST_LEFT BUTTON_HOME
354#define AST_RIGHT BUTTON_VOL_DOWN
355#define AST_FIRE BUTTON_VOL_UP
356
357#elif (CONFIG_KEYPAD == EROSQ_PAD)
358#define AST_PAUSE BUTTON_PREV
359#define AST_QUIT BUTTON_POWER
360#define AST_THRUST BUTTON_PLAY
361#define AST_HYPERSPACE BUTTON_MENU
362#define AST_LEFT BUTTON_SCROLL_BACK
363#define AST_RIGHT BUTTON_SCROLL_FWD
364#define AST_FIRE BUTTON_BACK
365
366#elif CONFIG_KEYPAD == FIIO_M3K_PAD
367#define AST_PAUSE BUTTON_MENU
368#define AST_QUIT BUTTON_POWER
369#define AST_THRUST BUTTON_SELECT
370#define AST_HYPERSPACE BUTTON_BACK
371#define AST_LEFT BUTTON_LEFT
372#define AST_RIGHT BUTTON_RIGHT
373#define AST_FIRE BUTTON_PLAY
374
375#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
376/* use touchscreen */
377
378#elif CONFIG_KEYPAD == SDL_PAD
379/* use touchscreen */
380#elif CONFIG_KEYPAD == MA_PAD
381#define AST_PAUSE BUTTON_MENU
382#define AST_QUIT BUTTON_BACK
383#define AST_THRUST BUTTON_PLAY
384#define AST_HYPERSPACE BUTTON_UP
385#define AST_LEFT BUTTON_LEFT
386#define AST_RIGHT BUTTON_RIGHT
387#define AST_FIRE BUTTON_DOWN
388
389#elif CONFIG_KEYPAD == RG_NANO_PAD
390#define AST_PAUSE BUTTON_X
391#define AST_QUIT BUTTON_START
392#define AST_THRUST BUTTON_UP
393#define AST_HYPERSPACE BUTTON_DOWN
394#define AST_LEFT BUTTON_LEFT
395#define AST_RIGHT BUTTON_RIGHT
396#define AST_FIRE BUTTON_A
397
398#else
399#error No keymap defined!
400#endif
401
402#ifdef HAVE_TOUCHSCREEN
403#ifndef AST_PAUSE
404#define AST_PAUSE BUTTON_CENTER
405#endif
406#ifndef AST_QUIT
407#define AST_QUIT BUTTON_TOPLEFT
408#endif
409#ifndef AST_THRUST
410#define AST_THRUST BUTTON_TOPMIDDLE
411#endif
412#ifndef AST_HYPERSPACE
413#define AST_HYPERSPACE BUTTON_TOPRIGHT
414#endif
415#ifndef AST_LEFT
416#define AST_LEFT BUTTON_MIDLEFT
417#endif
418#ifndef AST_RIGHT
419#define AST_RIGHT BUTTON_MIDRIGHT
420#endif
421#ifndef AST_FIRE
422#define AST_FIRE BUTTON_BOTTOMMIDDLE
423#endif
424#endif
425
426#define RES MAX(LCD_WIDTH, LCD_HEIGHT)
427#define LARGE_LCD (RES >= 200)
428
429#define CYCLETIME 30
430
431#define SHOW_COL 0
432#define SCALE 5000
433#define WRAP_GAP (LARGE*SCALE*3)
434#define POINT_SIZE 2
435#define START_LEVEL 1
436#define SHOW_LEVEL_TIME 50
437#define EXPLOSION_LENGTH 20
438
439#define MAX_NUM_ASTEROIDS 25
440#define MAX_NUM_MISSILES 6
441#define NUM_STARS 50
442#define NUM_TRAIL_POINTS 70
443#define MAX_LEVEL MAX_NUM_ASTEROIDS
444
445#define NUM_ASTEROID_VERTICES 10
446#define NUM_SHIP_VERTICES 4
447#define NUM_ENEMY_VERTICES 8
448
449#define INVULNERABLE_TIME 30
450#define BLINK_TIME 10
451#define EXTRA_LIFE 250
452#define START_LIVES 3
453#define MISSILE_LIFE_LENGTH 40
454
455#define ASTEROID_SPEED (RES/20)
456#define SPACE_CHECK_SIZE 30*SCALE
457
458#if (LARGE_LCD)
459#define SIZE_SHIP_COLLISION 8*SCALE
460#else
461#define SIZE_SHIP_COLLISION 6*SCALE
462#endif
463
464#define LITTLE_SHIP 1
465#define BIG_SHIP 2
466#define ENEMY_BIG_PROBABILITY_START 10
467#define ENEMY_APPEAR_PROBABILITY_START 35
468#define ENEMY_APPEAR_TIMING_START 600
469#define ENEMY_SPEED 4
470#define ENEMY_MISSILE_LIFE_LENGTH (RES/2)
471#if (LARGE_LCD)
472#define SIZE_ENEMY_COLLISION 7*SCALE
473#else
474#define SIZE_ENEMY_COLLISION 5*SCALE
475#endif
476
477#define SIN_COS_SCALE 10000
478
479#define FAST_ROT_CW_SIN 873
480#define FAST_ROT_CW_COS 9963
481#define FAST_ROT_ACW_SIN -873
482#define FAST_ROT_ACW_COS 9963
483
484#define MEDIUM_ROT_CW_SIN 350
485#define MEDIUM_ROT_CW_COS 9994
486#define MEDIUM_ROT_ACW_SIN -350
487#define MEDIUM_ROT_ACW_COS 9994
488
489#define SLOW_ROT_CW_SIN 350
490#define SLOW_ROT_CW_COS 9994
491#define SLOW_ROT_ACW_SIN -350
492#define SLOW_ROT_ACW_COS 9994
493
494#ifdef HAVE_LCD_COLOR
495#define SHIP_ROT_CW_SIN 2419
496#define SHIP_ROT_CW_COS 9702
497#define SHIP_ROT_ACW_SIN -2419
498#define SHIP_ROT_ACW_COS 9702
499#else
500#define SHIP_ROT_CW_SIN 3827
501#define SHIP_ROT_CW_COS 9239
502#define SHIP_ROT_ACW_SIN -3827
503#define SHIP_ROT_ACW_COS 9239
504#endif
505
506
507#define SCALED_WIDTH (LCD_WIDTH*SCALE)
508#define SCALED_HEIGHT (LCD_HEIGHT*SCALE)
509#define CENTER_LCD_X (LCD_WIDTH/2)
510#define CENTER_LCD_Y (LCD_HEIGHT/2)
511
512#ifdef HAVE_LCD_COLOR
513#define ASTEROID_R 230
514#define ASTEROID_G 200
515#define ASTEROID_B 100
516#define SHIP_R 255
517#define SHIP_G 255
518#define SHIP_B 255
519#define ENEMY_R 50
520#define ENEMY_G 220
521#define ENEMY_B 50
522#define THRUST_R 200
523#define THRUST_G 200
524#define THRUST_B 0
525
526#define COL_MISSILE LCD_RGBPACK(200,0,0)
527#define COL_PLAYER LCD_RGBPACK(200,200,200)
528#define COL_INVULN LCD_RGBPACK(100,100,200)
529#define COL_STARS LCD_WHITE
530#define COL_ASTEROID LCD_RGBPACK(ASTEROID_R,ASTEROID_G,ASTEROID_B)
531#define COL_TEXT LCD_RGBPACK(200,200,255)
532#define COL_ENEMY LCD_RGBPACK(ENEMY_R,ENEMY_G,ENEMY_B)
533#define SET_FG rb->lcd_set_foreground
534#define SET_BG rb->lcd_set_background
535#else
536#define SET_FG(x)
537#define SET_BG(x)
538#endif
539
540#define SCORE_FILE PLUGIN_GAMES_DATA_DIR "/spacerocks.score"
541#define NUM_SCORES 5
542
543static struct highscore highscores[NUM_SCORES];
544
545/* The array of points that make up an asteroid */
546static const short asteroid_one[NUM_ASTEROID_VERTICES*2] =
547{
548 -2, -12,
549 4, -8,
550 8, -14,
551 16, -5,
552 14, 0,
553 20, 2,
554 12, 14,
555 -4, 14,
556 -10, 6,
557 -10, -8,
558};
559
560/* The array of points that make up an asteroid */
561static const short asteroid_two[NUM_ASTEROID_VERTICES*2] =
562{
563 -2, -12,
564 4, -16,
565 6, -14,
566 16, -8,
567 14, 0,
568 20, 2,
569 12, 14,
570 -4, 14,
571 -10, 6,
572 -10, -8,
573};
574
575/* The array of points that make up an asteroid */
576static const short asteroid_three[NUM_ASTEROID_VERTICES*2] =
577{
578 -2, -12,
579 4, -16,
580 6, -14,
581 2, -8,
582 14, 0,
583 20, 2,
584 12, 14,
585 -4, 14,
586 -16, 6,
587 -10, -8,
588};
589
590/* The array of points the make up the ship */
591static const short ship_vertices[NUM_SHIP_VERTICES*2] =
592{
593#if (LARGE_LCD)
594 0, -6,
595 4, 6,
596 0, 2,
597 -4, 6,
598#else
599 0, -4,
600 3, 4,
601 0, 1,
602 -3, 4,
603#endif
604};
605
606/* The array of points the make up the bad spaceship */
607static const short enemy_vertices[NUM_ENEMY_VERTICES*2] =
608{
609#if (LARGE_LCD)
610 -8, 0,
611 -4, 4,
612 4, 4,
613 8, 0,
614 -8, 0,
615 8, 0,
616 4, -4,
617 -4, -4,
618#else
619 -5, 0,
620 -2, 2,
621 2, 2,
622 5, 0,
623 -5, 0,
624 5, 0,
625 2, -2,
626 -2, -2,
627#endif
628};
629
630enum asteroid_type
631{
632#if (LARGE_LCD)
633 SMALL = 2,
634 MEDIUM = 4,
635 LARGE = 6,
636#else
637 SMALL = 1,
638 MEDIUM = 2,
639 LARGE = 3,
640#endif
641};
642
643enum explosion_type
644{
645 EXPLOSION_SHIP,
646 EXPLOSION_ASTEROID,
647 EXPLOSION_ENEMY,
648 EXPLOSION_THRUST,
649};
650
651enum game_state
652{
653 GAME_OVER,
654 SHOW_LEVEL,
655 PLAY_MODE,
656 PAUSE_MODE,
657};
658
659struct Point
660{
661 int x;
662 int y;
663 int dx;
664 int dy;
665};
666
667struct TrailPoint
668{
669 struct Point position;
670 int alive;
671#ifdef HAVE_LCD_COLOR
672 short r;
673 short g;
674 short b;
675 short dec;
676#endif
677};
678
679/* Asteroid structure, contains an array of points */
680struct Asteroid
681{
682 struct Point position;
683 struct Point rotation;
684 struct Point vertices[NUM_ASTEROID_VERTICES];
685 bool exists;
686 int explode_countdown;
687 enum asteroid_type type;
688 int radius;
689 long speed_cos;
690 long speed_sin;
691};
692
693struct Ship
694{
695 struct Point position;
696 struct Point rotation;
697 struct Point vertices[NUM_SHIP_VERTICES];
698 bool exists;
699 int explode_countdown;
700 int invulnerable_time;
701};
702
703struct Enemy
704{
705 struct Point position;
706 struct Point vertices[NUM_ENEMY_VERTICES];
707 bool exists;
708 int explode_countdown;
709 int appear_countdown;
710 short size_probability;
711 short appear_probability;
712 short appear_timing;
713};
714
715struct Missile
716{
717 struct Point position;
718 struct Point oldpoint;
719 int alive;
720};
721
722static enum game_state game_state;
723static int asteroid_count;
724static int next_missile_count;
725static int next_thrust_count;
726static int num_lives;
727static int extra_life;
728static int show_level_timeout;
729static int current_level;
730static int current_score;
731
732static struct Ship ship;
733static struct Point stars[NUM_STARS];
734static struct Asteroid asteroids_array[MAX_NUM_ASTEROIDS];
735static struct Missile missiles_array[MAX_NUM_MISSILES];
736static struct Missile enemy_missile;
737static struct Enemy enemy;
738static struct Point lives_points[NUM_SHIP_VERTICES];
739static struct TrailPoint trail_points[NUM_TRAIL_POINTS];
740
741/*************************************************
742** Handle polygon and point
743*************************************************/
744
745/* Check if point is in a polygon */
746static bool is_point_in_polygon(struct Point* vertices, int num_vertices,
747 int x, int y)
748{
749 struct Point* pi;
750 struct Point* pj;
751 int n;
752 bool c = false;
753
754 if (x < -SCALED_WIDTH/2) x += SCALED_WIDTH;
755 else if (x > SCALED_WIDTH/2) x -= SCALED_WIDTH;
756 if (y < -SCALED_HEIGHT/2) y += SCALED_HEIGHT;
757 else if (y > SCALED_HEIGHT/2) y -= SCALED_HEIGHT;
758
759 pi = vertices;
760 pj = vertices + num_vertices-1;
761
762 n = num_vertices;
763 while (n--)
764 {
765 if ((((pi->y <= y) && (y < pj->y)) || ((pj->y <= y) && (y < pi->y))) &&
766 (x < (pj->x - pi->x) * (y - pi->y) / (pj->y - pi->y) + pi->x))
767 c = !c;
768
769 pj = pi;
770 pi++;
771 }
772
773 return c;
774}
775
776/* Check if point is within a rectangle */
777static bool is_point_within_rectangle(struct Point* rect, struct Point* p,
778 int size)
779{
780 int dx = p->x - rect->x;
781 int dy = p->y - rect->y;
782#if SHOW_COL
783 rb->lcd_drawrect((rect->x - size)/SCALE, (rect->y - size)/SCALE,
784 (size*2+1)/SCALE, (size*2+1)/SCALE);
785#endif
786 if (dx < -SCALED_WIDTH/2) dx += SCALED_WIDTH;
787 else if (dx > SCALED_WIDTH/2) dx -= SCALED_WIDTH;
788 if (dy < -SCALED_HEIGHT/2) dy += SCALED_HEIGHT;
789 else if (dy > SCALED_HEIGHT/2) dy -= SCALED_HEIGHT;
790 return (dx > -size && dx < size && dy > -size && dy < size);
791}
792
793/* Rotate polygon */
794static void rotate_polygon(struct Point* vertices, int num_vertices,
795 struct Point* rotation, int cos, int sin)
796{
797 struct Point* point;
798 int n;
799 long temp_x, temp_y;
800
801 temp_x = rotation->x;
802 temp_y = rotation->y;
803 rotation->x = (temp_x*cos - temp_y*sin)/SIN_COS_SCALE;
804 rotation->y = (temp_y*cos + temp_x*sin)/SIN_COS_SCALE;
805#define MIN_SCALE (SIN_COS_SCALE-10)
806#define MAX_SCALE (SIN_COS_SCALE+10)
807 /* normalize vector. this is not accurate but would be enough. */
808 temp_x = rotation->x*rotation->x + rotation->y*rotation->y;
809 if (temp_x <= MIN_SCALE*MIN_SCALE)
810 {
811 rotation->x = rotation->x*SIN_COS_SCALE/MIN_SCALE;
812 rotation->y = rotation->y*SIN_COS_SCALE/MIN_SCALE;
813 }
814 else if (temp_x >= MAX_SCALE*MAX_SCALE)
815 {
816 rotation->x = rotation->x*SIN_COS_SCALE/MAX_SCALE;
817 rotation->y = rotation->y*SIN_COS_SCALE/MAX_SCALE;
818 }
819#undef MIN_SCALE
820#undef MAX_SCALE
821
822 point = vertices;
823 n = num_vertices;
824 while (n--)
825 {
826 point->x = (point->dx*rotation->x - point->dy*rotation->y)/SIN_COS_SCALE;
827 point->y = (point->dy*rotation->x + point->dx*rotation->y)/SIN_COS_SCALE;
828 point++;
829 }
830}
831
832/* Draw polygon */
833static void draw_polygon(struct Point* vertices, int num_vertices,
834 int px, int py)
835{
836 int n, new_x, new_y, old_x, old_y;
837 struct Point *p;
838 bool draw_wrap;
839
840 if (px > SCALED_WIDTH - WRAP_GAP)
841 px -= SCALED_WIDTH;
842 if (py > SCALED_HEIGHT - WRAP_GAP)
843 py -= SCALED_HEIGHT;
844
845 draw_wrap = (px < WRAP_GAP || py < WRAP_GAP);
846
847 p = vertices + num_vertices - 1;
848 old_x = (p->x + px)/SCALE;
849 old_y = (p->y + py)/SCALE;
850 p = vertices;
851 n = num_vertices;
852 while (n--)
853 {
854 new_x = (p->x + px)/SCALE;
855 new_y = (p->y + py)/SCALE;
856
857 rb->lcd_drawline(old_x, old_y, new_x, new_y);
858 if (draw_wrap)
859 {
860 rb->lcd_drawline(old_x + LCD_WIDTH, old_y, new_x + LCD_WIDTH, new_y);
861 rb->lcd_drawline(old_x, old_y + LCD_HEIGHT, new_x, new_y + LCD_HEIGHT);
862 rb->lcd_drawline(old_x + LCD_WIDTH, old_y + LCD_HEIGHT,
863 new_x + LCD_WIDTH, new_y + LCD_HEIGHT);
864 }
865 old_x = new_x;
866 old_y = new_y;
867 p++;
868 }
869}
870
871static void move_point(struct Point* point)
872{
873 point->x += point->dx;
874 point->y += point->dy;
875
876 /* Check bounds on the x-axis: */
877 point->x %= SCALED_WIDTH;
878 if (point->x < 0)
879 point->x += SCALED_WIDTH;
880
881 /* Check bounds on the y-axis: */
882 point->y %= SCALED_HEIGHT;
883 if (point->y < 0)
884 point->y += SCALED_HEIGHT;
885}
886
887/*************************************************
888** Handle trail blaiz.
889*************************************************/
890
891static void create_ship_trail(struct TrailPoint* tpoint)
892{
893 tpoint->position.x += ship.vertices[2].x;
894 tpoint->position.y += ship.vertices[2].y;
895 tpoint->position.dx = -( ship.vertices[0].x - ship.vertices[2].x )/10;
896 tpoint->position.dy = -( ship.vertices[0].y - ship.vertices[2].y )/10;
897}
898
899static void create_explosion_trail(struct TrailPoint* tpoint)
900{
901 tpoint->position.dx = (rb->rand()%5001)-2500;
902 tpoint->position.dy = (rb->rand()%5001)-2500;
903}
904
905static void create_trail_blaze(int colour, struct Point* position)
906{
907 int numtoadd;
908 struct TrailPoint* tpoint;
909 int n;
910
911 if (colour != EXPLOSION_SHIP)
912 {
913 numtoadd = NUM_TRAIL_POINTS/5;
914 }
915 else
916 {
917 numtoadd = NUM_TRAIL_POINTS/8;
918 }
919
920 /* give the point a random countdown timer, so they dissapears at different
921 times */
922 tpoint = trail_points;
923 n = NUM_TRAIL_POINTS;
924 while (n--)
925 {
926 /* find a space in the array of trail_points that is NULL or DEAD or
927 whatever and place this one here. */
928 if (tpoint->alive <= 0)
929 {
930 /* take a random point near the position. */
931 tpoint->position.x = (rb->rand()%18000)-9000 + position->x;
932 tpoint->position.y = (rb->rand()%18000)-9000 + position->y;
933
934 switch(colour)
935 {
936 case EXPLOSION_SHIP:
937 create_explosion_trail(tpoint);
938 tpoint->alive = 51;
939#ifdef HAVE_LCD_COLOR
940 tpoint->r = SHIP_R;
941 tpoint->g = SHIP_G;
942 tpoint->b = SHIP_B;
943 tpoint->dec = 2;
944#endif
945 break;
946 case EXPLOSION_ASTEROID:
947 create_explosion_trail(tpoint);
948 tpoint->alive = 51;
949#ifdef HAVE_LCD_COLOR
950 tpoint->r = ASTEROID_R;
951 tpoint->g = ASTEROID_G;
952 tpoint->b = ASTEROID_B;
953 tpoint->dec = 2;
954#endif
955 break;
956 case EXPLOSION_ENEMY:
957 create_explosion_trail(tpoint);
958 tpoint->alive = 51;
959#ifdef HAVE_LCD_COLOR
960 tpoint->r = ENEMY_R;
961 tpoint->g = ENEMY_G;
962 tpoint->b = ENEMY_B;
963 tpoint->dec = 2;
964#endif
965 break;
966 case EXPLOSION_THRUST:
967 create_ship_trail(tpoint);
968 tpoint->alive = 17;
969#ifdef HAVE_LCD_COLOR
970 tpoint->r = THRUST_R;
971 tpoint->g = THRUST_G;
972 tpoint->b = THRUST_B;
973 tpoint->dec = 4;
974#endif
975 break;
976 }
977
978 /* give the points a speed based on direction of travel
979 - i.e. opposite */
980 tpoint->position.dx += position->dx;
981 tpoint->position.dy += position->dy;
982
983 numtoadd--;
984 if (numtoadd <= 0)
985 break;
986 }
987 tpoint++;
988 }
989}
990
991static void draw_and_move_trail_blaze(void)
992{
993 struct TrailPoint* tpoint;
994 int n;
995
996 /* loop through, if alive then move and draw.
997 when drawn, countdown it's timer.
998 if zero kill it! */
999
1000 tpoint = trail_points;
1001 n = NUM_TRAIL_POINTS;
1002 while (n--)
1003 {
1004 if (tpoint->alive > 0)
1005 {
1006 if (game_state != PAUSE_MODE)
1007 {
1008 tpoint->alive--;
1009 move_point(&(tpoint->position));
1010#ifdef HAVE_LCD_COLOR
1011 /* intensity = tpoint->alive/2; */
1012 if (tpoint->r >= tpoint->dec) tpoint->r -= tpoint->dec;
1013 if (tpoint->g >= tpoint->dec) tpoint->g -= tpoint->dec;
1014 if (tpoint->b >= tpoint->dec) tpoint->b -= tpoint->dec;
1015#endif
1016 }
1017 SET_FG(LCD_RGBPACK(tpoint->r, tpoint->g, tpoint->b));
1018 rb->lcd_drawpixel(tpoint->position.x/SCALE, tpoint->position.y/SCALE);
1019 }
1020 tpoint++;
1021 }
1022}
1023
1024/*************************************************
1025** Handle asteroid.
1026*************************************************/
1027
1028static void rotate_asteroid(struct Asteroid* asteroid)
1029{
1030 rotate_polygon(asteroid->vertices, NUM_ASTEROID_VERTICES,
1031 &asteroid->rotation,
1032 asteroid->speed_cos, asteroid->speed_sin);
1033}
1034
1035/* Initialise the passed Asteroid.
1036 * if position is NULL, place it at the random loacation
1037 * where ship doesn't exist
1038 */
1039static void initialise_asteroid(struct Asteroid* asteroid,
1040 enum asteroid_type type, struct Point *position)
1041{
1042 const short *asteroid_vertices;
1043 struct Point* point;
1044 int n;
1045
1046 asteroid->exists = true;
1047 asteroid->explode_countdown = 0;
1048 asteroid->type = type;
1049
1050 /* Set the radius of the asteroid: */
1051 asteroid->radius = (int)type*SCALE*3;
1052
1053 /* shall we move Clockwise and Fast */
1054 n = rb->rand()%100;
1055 if (n < 25)
1056 {
1057 asteroid->speed_cos = FAST_ROT_CW_COS;
1058 asteroid->speed_sin = FAST_ROT_CW_SIN;
1059 }
1060 else if (n < 50)
1061 {
1062 asteroid->speed_cos = FAST_ROT_ACW_COS;
1063 asteroid->speed_sin = FAST_ROT_ACW_SIN;
1064 }
1065 else if (n < 75)
1066 {
1067 asteroid->speed_cos = SLOW_ROT_ACW_COS;
1068 asteroid->speed_sin = SLOW_ROT_ACW_SIN;
1069 }
1070 else
1071 {
1072 asteroid->speed_cos = SLOW_ROT_CW_COS;
1073 asteroid->speed_sin = SLOW_ROT_CW_SIN;
1074 }
1075
1076 n = rb->rand()%99;
1077 if (n < 33)
1078 asteroid_vertices = asteroid_one;
1079 else if (n < 66)
1080 asteroid_vertices = asteroid_two;
1081 else
1082 asteroid_vertices = asteroid_three;
1083
1084 point = asteroid->vertices;
1085 for(n = 0; n < NUM_ASTEROID_VERTICES*2; n += 2)
1086 {
1087 point->x = asteroid_vertices[n];
1088 point->y = asteroid_vertices[n+1];
1089 point->x *= asteroid->radius/20;
1090 point->y *= asteroid->radius/20;
1091 /* dx and dy are used when rotate polygon */
1092 point->dx = point->x;
1093 point->dy = point->y;
1094 point++;
1095 }
1096
1097 if (!position)
1098 {
1099 do {
1100 /* Set the position randomly: */
1101 asteroid->position.x = (rb->rand()%SCALED_WIDTH);
1102 asteroid->position.y = (rb->rand()%SCALED_HEIGHT);
1103 } while (is_point_within_rectangle(&ship.position, &asteroid->position,
1104 SPACE_CHECK_SIZE));
1105 }
1106 else
1107 {
1108 asteroid->position.x = position->x;
1109 asteroid->position.y = position->y;
1110 }
1111
1112 do {
1113 asteroid->position.dx = (rb->rand()%ASTEROID_SPEED)-ASTEROID_SPEED/2;
1114 } while (asteroid->position.dx == 0);
1115
1116 do {
1117 asteroid->position.dy = (rb->rand()%ASTEROID_SPEED)-ASTEROID_SPEED/2;
1118 } while (asteroid->position.dy == 0);
1119
1120 asteroid->position.dx *= SCALE/10;
1121 asteroid->position.dy *= SCALE/10;
1122
1123 asteroid->rotation.x = SIN_COS_SCALE;
1124 asteroid->rotation.y = 0;
1125
1126 /* Now rotate the asteroid a bit, so they all look a bit different */
1127 for(n = (rb->rand()%30)+2; n--; )
1128 rotate_asteroid(asteroid);
1129
1130 /* great, we've created an asteroid, don't forget to increment the total: */
1131 asteroid_count++;
1132}
1133
1134/*
1135 * Creates a new asteroid of the given 4type (size) and at the given location.
1136 */
1137static void create_asteroid(enum asteroid_type type, struct Point *position)
1138{
1139 struct Asteroid* asteroid;
1140 int n;
1141
1142 asteroid = asteroids_array;
1143 n = MAX_NUM_ASTEROIDS;
1144 while (n--)
1145 {
1146 if (!asteroid->exists && asteroid->explode_countdown <= 0)
1147 {
1148 initialise_asteroid(asteroid, type, position);
1149 break;
1150 }
1151 asteroid++;
1152 }
1153}
1154
1155/* Draw and move all asteroids */
1156static void draw_and_move_asteroids(void)
1157{
1158 struct Asteroid* asteroid;
1159 int n;
1160
1161 SET_FG(COL_ASTEROID);
1162
1163 asteroid = asteroids_array;
1164 n = MAX_NUM_ASTEROIDS;
1165 while (n--)
1166 {
1167 if (asteroid->exists)
1168 {
1169 draw_polygon(asteroid->vertices, NUM_ASTEROID_VERTICES,
1170 asteroid->position.x, asteroid->position.y);
1171 }
1172 if (game_state != PAUSE_MODE)
1173 {
1174 if (asteroid->exists)
1175 {
1176 move_point(&asteroid->position);
1177 rotate_asteroid(asteroid);
1178 }
1179 else if (asteroid->explode_countdown > 0)
1180 {
1181 asteroid->explode_countdown--;
1182 }
1183 }
1184 asteroid++;
1185 }
1186}
1187
1188static void explode_asteroid(struct Asteroid* asteroid)
1189{
1190 struct Point p;
1191 p.dx = asteroid->position.dx;
1192 p.dy = asteroid->position.dy;
1193 p.x = asteroid->position.x;
1194 p.y = asteroid->position.y;
1195
1196 asteroid_count--;
1197 asteroid->exists = false;
1198
1199 switch(asteroid->type)
1200 {
1201 case SMALL:
1202 asteroid->explode_countdown = EXPLOSION_LENGTH;
1203 create_trail_blaze(EXPLOSION_ASTEROID, &p);
1204 break;
1205
1206 case MEDIUM:
1207 create_asteroid(SMALL, &p);
1208 create_asteroid(SMALL, &p);
1209 break;
1210
1211 case LARGE:
1212 create_asteroid(MEDIUM, &p);
1213 create_asteroid(MEDIUM, &p);
1214 break;
1215 }
1216}
1217
1218/*************************************************
1219** Handle ship.
1220*************************************************/
1221
1222/* Initialise the ship */
1223static void initialise_ship(void)
1224{
1225 struct Point* point;
1226 struct Point* lives_point;
1227 int n;
1228
1229 ship.position.x = CENTER_LCD_X * SCALE;
1230 ship.position.y = CENTER_LCD_Y * SCALE;
1231 ship.position.dx = 0;
1232 ship.position.dy = 0;
1233 ship.rotation.x = SIN_COS_SCALE;
1234 ship.rotation.y = 0;
1235 ship.exists = true;
1236 ship.explode_countdown = 0;
1237 ship.invulnerable_time = INVULNERABLE_TIME;
1238
1239 point = ship.vertices;
1240 lives_point = lives_points;
1241 for(n = 0; n < NUM_SHIP_VERTICES*2; n += 2)
1242 {
1243 point->x = ship_vertices[n];
1244 point->y = ship_vertices[n+1];
1245 point->x *= SCALE;
1246 point->y *= SCALE;
1247 /* dx and dy are used when rotate polygon */
1248 point->dx = point->x;
1249 point->dy = point->y;
1250 /* grab a copy of the ships points for the lives display: */
1251 lives_point->x = point->x;
1252 lives_point->y = point->y;
1253
1254 point++;
1255 lives_point++;
1256 }
1257}
1258
1259/*
1260 * Draws the ship, moves the ship and creates a new
1261 * one if it's finished exploding.
1262 */
1263static void draw_and_move_ship(void)
1264{
1265 if (ship.invulnerable_time > BLINK_TIME || ship.invulnerable_time % 2 != 0)
1266 {
1267 SET_FG(COL_INVULN);
1268 }
1269 else
1270 {
1271 SET_FG(COL_PLAYER);
1272 }
1273
1274 if (ship.exists)
1275 {
1276 draw_polygon(ship.vertices, NUM_SHIP_VERTICES,
1277 ship.position.x, ship.position.y);
1278 }
1279
1280 if (game_state != PAUSE_MODE)
1281 {
1282 if (ship.exists)
1283 {
1284 if (ship.invulnerable_time > 0)
1285 ship.invulnerable_time--;
1286 move_point(&ship.position);
1287 }
1288 else if (ship.explode_countdown > 0)
1289 {
1290 ship.explode_countdown--;
1291 if (ship.explode_countdown <= 0)
1292 {
1293 num_lives--;
1294 if (num_lives <= 0)
1295 {
1296 game_state = GAME_OVER;
1297 }
1298 else
1299 {
1300 initialise_ship();
1301 }
1302 }
1303 }
1304 }
1305}
1306
1307static void explode_ship(void)
1308{
1309 if (!ship.invulnerable_time)
1310 {
1311 /* if not invulnerable, blow up ship */
1312 ship.explode_countdown = EXPLOSION_LENGTH;
1313 ship.exists = false;
1314 create_trail_blaze(EXPLOSION_SHIP, &ship.position);
1315 }
1316}
1317
1318/* Rotate the ship using the passed sin & cos values */
1319static void rotate_ship(int cos, int sin)
1320{
1321 if (ship.exists)
1322 {
1323 rotate_polygon(ship.vertices, NUM_SHIP_VERTICES,
1324 &ship.rotation, cos, sin);
1325 }
1326}
1327
1328static void thrust_ship(void)
1329{
1330 if (ship.exists)
1331 {
1332 ship.position.dx += ( ship.vertices[0].x - ship.vertices[2].x )/20;
1333 ship.position.dy += ( ship.vertices[0].y - ship.vertices[2].y )/20;
1334
1335 /* if dx and dy are below a certain threshold, then set 'em to 0
1336 but to do this we need to ascertain if the spacehip as moved on
1337 screen for more than a certain amount. */
1338
1339 create_trail_blaze(EXPLOSION_THRUST, &ship.position);
1340 }
1341}
1342
1343/* stop movement of ship, 'cos that's what happens when you go into hyperspace. */
1344static void hyperspace(void)
1345{
1346 if (ship.exists)
1347 {
1348 ship.position.dx = ship.position.dy = 0;
1349 ship.position.x = (rb->rand()%SCALED_WIDTH);
1350 ship.position.y = (rb->rand()%SCALED_HEIGHT);
1351 }
1352}
1353
1354static void draw_lives(void)
1355{
1356 int n;
1357#if (LARGE_LCD)
1358 int px = (LCD_WIDTH-1 - 4)*SCALE;
1359 int py = (LCD_HEIGHT-1 - 6)*SCALE;
1360#else
1361 int px = (LCD_WIDTH-1 - 3)*SCALE;
1362 int py = (LCD_HEIGHT-1 - 4)*SCALE;
1363#endif
1364
1365 SET_FG(COL_PLAYER);
1366
1367 n = num_lives-1;
1368 while (n--)
1369 {
1370 draw_polygon(lives_points, NUM_SHIP_VERTICES, px, py);
1371#if (LARGE_LCD)
1372 px -= 8*SCALE;
1373#else
1374 px -= 6*SCALE;
1375#endif
1376 }
1377}
1378
1379/*
1380 * missile
1381 */
1382
1383/* Initialise a missile */
1384static void initialise_missile(struct Missile* missile)
1385{
1386 missile->position.x = ship.position.x + ship.vertices[0].x;
1387 missile->position.y = ship.position.y + ship.vertices[0].y;
1388 missile->position.dx = (ship.vertices[0].x - ship.vertices[2].x)/2;
1389 missile->position.dy = (ship.vertices[0].y - ship.vertices[2].y)/2;
1390 missile->alive = MISSILE_LIFE_LENGTH;
1391 missile->oldpoint.x = missile->position.x;
1392 missile->oldpoint.y = missile->position.y;
1393}
1394
1395/* Fire the next missile */
1396static void fire_missile(void)
1397{
1398 struct Missile* missile;
1399 int n;
1400
1401 if (ship.exists)
1402 {
1403 missile = missiles_array;
1404 n = MAX_NUM_MISSILES;
1405 while (n--)
1406 {
1407 if (missile->alive <= 0)
1408 {
1409 initialise_missile(missile);
1410 break;
1411 }
1412 missile++;
1413 }
1414 }
1415}
1416
1417/* Draw and Move all the missiles */
1418static void draw_and_move_missiles(void)
1419{
1420 struct Missile* missile;
1421 struct Point vertices[2];
1422 int n;
1423
1424 SET_FG(COL_MISSILE);
1425
1426 missile = missiles_array;
1427 n = MAX_NUM_MISSILES;
1428 while (n--)
1429 {
1430 if (missile->alive > 0)
1431 {
1432 vertices[0].x = 0;
1433 vertices[0].y = 0;
1434 vertices[1].x = -missile->position.dx;
1435 vertices[1].y = -missile->position.dy;
1436 draw_polygon(vertices, 2, missile->position.x, missile->position.y);
1437
1438 if (game_state != PAUSE_MODE)
1439 {
1440 missile->oldpoint.x = missile->position.x;
1441 missile->oldpoint.y = missile->position.y;
1442 move_point(&missile->position);
1443 missile->alive--;
1444 }
1445 }
1446 missile++;
1447 }
1448}
1449
1450/*************************************************
1451** Handle enemy.
1452*************************************************/
1453
1454static void initialise_enemy(void)
1455{
1456 struct Point* point;
1457 int n;
1458 int size;
1459
1460 if (rb->rand()%100 > enemy.size_probability)
1461 {
1462 size = BIG_SHIP;
1463 enemy.size_probability++;
1464 if (enemy.size_probability > 90)
1465 {
1466 enemy.size_probability = ENEMY_BIG_PROBABILITY_START;
1467 }
1468 }
1469 else
1470 {
1471 size = LITTLE_SHIP;
1472 enemy.size_probability = ENEMY_BIG_PROBABILITY_START;
1473 }
1474
1475 enemy.exists = true;
1476 enemy.explode_countdown = 0;
1477 enemy.appear_countdown = enemy.appear_timing;
1478
1479 point = enemy.vertices;
1480 for(n = 0; n < NUM_ENEMY_VERTICES*2; n += 2)
1481 {
1482 point->x = enemy_vertices[n];
1483 point->y = enemy_vertices[n+1];
1484 point->x *= size*SCALE/2;
1485 point->y *= size*SCALE/2;
1486 point++;
1487 }
1488
1489 if (ship.position.x >= SCALED_WIDTH/2)
1490 {
1491 enemy.position.dx = ENEMY_SPEED;
1492 enemy.position.x = 0;
1493 }
1494 else
1495 {
1496 enemy.position.dx = -ENEMY_SPEED;
1497 enemy.position.x = SCALED_WIDTH;
1498 }
1499
1500 if (ship.position.y >= SCALED_HEIGHT/2)
1501 {
1502 enemy.position.dy = ENEMY_SPEED;
1503 enemy.position.y = 0;
1504 }
1505 else
1506 {
1507 enemy.position.dy = -ENEMY_SPEED;
1508 enemy.position.y = SCALED_HEIGHT;
1509 }
1510
1511 enemy.position.dx *= SCALE/10;
1512 enemy.position.dy *= SCALE/10;
1513}
1514
1515static void draw_and_move_enemy(void)
1516{
1517 SET_FG(COL_ENEMY);
1518
1519 if (enemy.exists)
1520 {
1521 draw_polygon(enemy.vertices, NUM_ENEMY_VERTICES,
1522 enemy.position.x, enemy.position.y);
1523 }
1524
1525 if (game_state != PAUSE_MODE)
1526 {
1527 if (enemy.exists)
1528 {
1529 enemy.position.x += enemy.position.dx;
1530 enemy.position.y += enemy.position.dy;
1531
1532 if (enemy.position.x > SCALED_WIDTH || enemy.position.x < 0)
1533 enemy.exists = false;
1534
1535 enemy.position.y %= SCALED_HEIGHT;
1536 if (enemy.position.y < 0)
1537 enemy.position.y += SCALED_HEIGHT;
1538
1539 if ((rb->rand()%1000) < 10)
1540 enemy.position.dy = -enemy.position.dy;
1541 }
1542 else if (enemy.explode_countdown > 0)
1543 {
1544 enemy.explode_countdown--;
1545 }
1546 else
1547 {
1548 if (enemy.appear_countdown > 0)
1549 enemy.appear_countdown--;
1550 else if (rb->rand()%100 >= enemy.appear_probability)
1551 initialise_enemy();
1552 }
1553 }
1554
1555 if (enemy_missile.alive <= 0)
1556 {
1557 /* if no missile and the enemy is here and not exploding..
1558 then shoot baby! */
1559 if (enemy.exists && ship.exists &&
1560 game_state == PLAY_MODE && (rb->rand()%10) >= 5 )
1561 {
1562 int dx = ship.position.x - enemy.position.x;
1563 int dy = ship.position.y - enemy.position.y;
1564
1565 if (dx < -SCALED_WIDTH/2) dx += SCALED_WIDTH;
1566 else if (dx > SCALED_WIDTH/2) dx -= SCALED_WIDTH;
1567 if (dy < -SCALED_HEIGHT/2) dy += SCALED_HEIGHT;
1568 else if (dy > SCALED_HEIGHT/2) dy -= SCALED_HEIGHT;
1569
1570 enemy_missile.position.x = enemy.position.x;
1571 enemy_missile.position.y = enemy.position.y;
1572
1573 /* lame, needs to be sorted - it's trying to shoot at the ship */
1574 if (dx < -5*SCALE)
1575 enemy_missile.position.dx = -1;
1576 else if (dx > 5*SCALE)
1577 enemy_missile.position.dx = 1;
1578 else
1579 enemy_missile.position.dx = 0;
1580
1581 if (dy < -5*SCALE)
1582 enemy_missile.position.dy = -1;
1583 else if (dy > 5*SCALE)
1584 enemy_missile.position.dy = 1;
1585 else
1586 enemy_missile.position.dy = 0;
1587
1588 while (enemy_missile.position.dx == 0 &&
1589 enemy_missile.position.dy == 0)
1590 {
1591 enemy_missile.position.dx = rb->rand()%2-1;
1592 enemy_missile.position.dy = rb->rand()%2-1;
1593 }
1594
1595 enemy_missile.position.dx *= SCALE;
1596 enemy_missile.position.dy *= SCALE;
1597 enemy_missile.alive = ENEMY_MISSILE_LIFE_LENGTH;
1598 }
1599 }
1600 else
1601 {
1602 rb->lcd_fillrect( enemy_missile.position.x/SCALE,
1603 enemy_missile.position.y/SCALE,
1604 POINT_SIZE, POINT_SIZE );
1605 if (game_state != PAUSE_MODE)
1606 {
1607 move_point(&enemy_missile.position);
1608 enemy_missile.alive--;
1609 }
1610 }
1611}
1612
1613/*************************************************
1614** Check collisions.
1615*************************************************/
1616
1617/* Add score if missile hit asteroid or enemy */
1618static void add_score(int val)
1619{
1620 current_score += val;
1621 if (current_score >= extra_life)
1622 {
1623 num_lives++;
1624 extra_life += EXTRA_LIFE;
1625 }
1626}
1627
1628static bool is_point_within_asteroid(struct Asteroid* asteroid,
1629 struct Point* point)
1630{
1631 if (is_point_within_rectangle(&asteroid->position, point, asteroid->radius)
1632 && is_point_in_polygon(asteroid->vertices, NUM_ASTEROID_VERTICES,
1633 point->x - asteroid->position.x,
1634 point->y - asteroid->position.y))
1635 {
1636 explode_asteroid(asteroid);
1637 return true;
1638 }
1639 else
1640 return false;
1641}
1642
1643static bool is_point_within_ship(struct Point* point)
1644{
1645 if (is_point_within_rectangle(&ship.position, point, SIZE_SHIP_COLLISION)
1646 && is_point_in_polygon(ship.vertices, NUM_SHIP_VERTICES,
1647 point->x - ship.position.x,
1648 point->y - ship.position.y))
1649 {
1650 return true;
1651 }
1652 else
1653 return false;
1654}
1655
1656static bool is_point_within_enemy(struct Point* point)
1657{
1658 if (is_point_within_rectangle(&enemy.position, point, SIZE_ENEMY_COLLISION))
1659 {
1660 add_score(5);
1661 enemy.explode_countdown = EXPLOSION_LENGTH;
1662 enemy.exists = false;
1663 create_trail_blaze(EXPLOSION_ENEMY, &enemy.position);
1664 return true;
1665 }
1666 else
1667 return false;
1668}
1669
1670static bool is_ship_within_asteroid(struct Asteroid* asteroid)
1671{
1672 struct Point p;
1673
1674 if (!is_point_within_rectangle(&asteroid->position, &ship.position,
1675 asteroid->radius+SIZE_SHIP_COLLISION))
1676 return false;
1677
1678 p.x = ship.position.x + ship.vertices[0].x;
1679 p.y = ship.position.y + ship.vertices[0].y;
1680 if (is_point_within_asteroid(asteroid, &p))
1681 return true;
1682
1683 p.x = ship.position.x + ship.vertices[1].x;
1684 p.y = ship.position.y + ship.vertices[1].y;
1685 if (is_point_within_asteroid(asteroid, &p))
1686 return true;
1687
1688 p.x = ship.position.x + ship.vertices[3].x;
1689 p.y = ship.position.y + ship.vertices[3].y;
1690 if (is_point_within_asteroid(asteroid, &p))
1691 return true;
1692
1693 return false;
1694}
1695
1696/* Check for collsions between the missiles and the asteroids and the ship */
1697static void check_collisions(void)
1698{
1699 struct Missile* missile;
1700 struct Asteroid* asteroid;
1701 int m, n;
1702 bool asteroids_onscreen = false;
1703
1704 asteroid = asteroids_array;
1705 m = MAX_NUM_ASTEROIDS;
1706 while (m--)
1707 {
1708 /* if the asteroids exists then test missile collision: */
1709 if (asteroid->exists)
1710 {
1711 missile = missiles_array;
1712 n = MAX_NUM_MISSILES;
1713 while (n--)
1714 {
1715 /* if the missiles exists: */
1716 if (missile->alive > 0)
1717 {
1718 /* has the missile hit the asteroid? */
1719 if (is_point_within_asteroid(asteroid, &missile->position) ||
1720 is_point_within_asteroid(asteroid, &missile->oldpoint))
1721 {
1722 add_score(1);
1723 missile->alive = 0;
1724 break;
1725 }
1726 }
1727 missile++;
1728 }
1729
1730 /* now check collision with ship: */
1731 if (asteroid->exists && ship.exists)
1732 {
1733 if (is_ship_within_asteroid(asteroid))
1734 {
1735 add_score(1);
1736 explode_ship();
1737 }
1738 }
1739
1740 /* has the enemy missile blown something up? */
1741 if (asteroid->exists && enemy_missile.alive > 0)
1742 {
1743 if (is_point_within_asteroid(asteroid, &enemy_missile.position))
1744 {
1745 enemy_missile.alive = 0;
1746 }
1747 }
1748 }
1749
1750 /* is an asteroid still exploding? */
1751 if (asteroid->explode_countdown > 0)
1752 asteroids_onscreen = true;
1753
1754 asteroid++;
1755 }
1756
1757 /* now check collision between ship and enemy */
1758 if (enemy.exists && ship.exists)
1759 {
1760 /* has the enemy collided with the ship? */
1761 if (is_point_within_enemy(&ship.position))
1762 {
1763 explode_ship();
1764 create_trail_blaze(EXPLOSION_ENEMY, &enemy.position);
1765 }
1766
1767 if (enemy.exists)
1768 {
1769 /* Now see if the enemy has been shot at by the ships missiles: */
1770 missile = missiles_array;
1771 n = MAX_NUM_MISSILES;
1772 while (n--)
1773 {
1774 if (missile->alive > 0 &&
1775 is_point_within_enemy(&missile->position))
1776 {
1777 missile->alive = 0;
1778 break;
1779 }
1780 missile++;
1781 }
1782 }
1783 }
1784
1785 /* test collision with enemy missile and ship: */
1786 if (enemy_missile.alive > 0 && is_point_within_ship(&enemy_missile.position))
1787 {
1788 explode_ship();
1789 enemy_missile.alive = 0;
1790 enemy_missile.position.x = enemy_missile.position.y = 0;
1791 }
1792
1793 /* if all asteroids cleared then start again: */
1794 if (asteroid_count == 0 && !asteroids_onscreen
1795 && !enemy.exists && enemy.explode_countdown <= 0)
1796 {
1797 current_level++;
1798 if (current_level > MAX_LEVEL)
1799 current_level = START_LEVEL;
1800 enemy.appear_probability += 5;
1801 if (enemy.appear_probability >= 100)
1802 enemy.appear_probability = ENEMY_APPEAR_PROBABILITY_START;
1803 enemy.appear_timing -= 30;
1804 if (enemy.appear_timing < 30)
1805 enemy.appear_timing = 30;
1806 game_state = SHOW_LEVEL;
1807 show_level_timeout = SHOW_LEVEL_TIME;
1808 }
1809}
1810
1811/*
1812 * stars
1813 */
1814
1815static void create_stars(void)
1816{
1817 struct Point* p;
1818 int n;
1819
1820 p = stars;
1821 n = NUM_STARS;
1822 while (n--)
1823 {
1824 p->x = (rb->rand()%LCD_WIDTH);
1825 p->y = (rb->rand()%LCD_HEIGHT);
1826 p++;
1827 }
1828}
1829
1830static void drawstars(void)
1831{
1832 struct Point* p;
1833 int n;
1834
1835 SET_FG(COL_STARS);
1836
1837 p = stars;
1838 n = NUM_STARS;
1839 while (n--)
1840 {
1841 rb->lcd_drawpixel(p->x , p->y);
1842 p++;
1843 }
1844}
1845
1846/*************************************************
1847** Creates start_num number of new asteroids of
1848** full size.
1849**************************************************/
1850static void initialise_level(int start_num)
1851{
1852 struct Asteroid* asteroid;
1853 struct Missile* missile;
1854 struct TrailPoint* tpoint;
1855 int n;
1856 asteroid_count = next_missile_count = next_thrust_count = 0;
1857
1858 /* no enemy */
1859 enemy.exists = 0;
1860 enemy.explode_countdown = 0;
1861 enemy_missile.alive = 0;
1862
1863 /* clear asteroids */
1864 asteroid = asteroids_array;
1865 n = MAX_NUM_ASTEROIDS;
1866 while (n--)
1867 {
1868 asteroid->exists = false;
1869 asteroid++;
1870 }
1871
1872 /* make some LARGE asteroids */
1873 for(n = 0; n < start_num; n++)
1874 initialise_asteroid(&asteroids_array[n], LARGE, NULL);
1875
1876 /* ensure all missiles are out of action: */
1877 missile = missiles_array;
1878 n = MAX_NUM_MISSILES;
1879 while (n--)
1880 {
1881 missile->alive = 0;
1882 missile++;
1883 }
1884
1885 tpoint = trail_points;
1886 n = NUM_TRAIL_POINTS;
1887 while (n--)
1888 {
1889 tpoint->alive = 0;
1890 tpoint++;
1891 }
1892}
1893
1894static void initialise_game(void)
1895{
1896 enemy.appear_probability = ENEMY_APPEAR_PROBABILITY_START;
1897 enemy.appear_timing = ENEMY_APPEAR_TIMING_START;
1898 enemy.appear_countdown = enemy.appear_timing;
1899 enemy.size_probability = ENEMY_BIG_PROBABILITY_START;
1900 current_level = START_LEVEL;
1901 num_lives = START_LIVES;
1902 extra_life = EXTRA_LIFE;
1903 current_score = 0;
1904 initialise_ship();
1905 initialise_level(0);
1906 game_state = SHOW_LEVEL;
1907 show_level_timeout = SHOW_LEVEL_TIME;
1908}
1909
1910/* menu stuff */
1911static bool spacerocks_help(void)
1912{
1913 static char *help_text[] = {
1914 "Spacerocks", "", "Aim", "",
1915 "The", "goal", "of", "the", "game", "is", "to", "blow", "up",
1916 "the", "asteroids", "and", "avoid", "being", "hit", "by", "them.",
1917 "Also", "you'd", "better", "watch", "out", "for", "the", "UFOs!"
1918 };
1919 static struct style_text formation[]={
1920 { 0, TEXT_CENTER|TEXT_UNDERLINE },
1921 { 2, C_RED },
1922 LAST_STYLE_ITEM
1923 };
1924
1925 rb->lcd_setfont(FONT_UI);
1926 SET_BG(LCD_BLACK);
1927 SET_FG(LCD_WHITE);
1928 if (display_text(ARRAYLEN(help_text), help_text, formation, NULL, true))
1929 return true;
1930 rb->lcd_setfont(FONT_SYSFIXED);
1931
1932 return false;
1933}
1934
1935#define PLUGIN_OTHER 10
1936static bool ingame;
1937static int spacerocks_menu_cb(int action,
1938 const struct menu_item_ex *this_item,
1939 struct gui_synclist *this_list)
1940{
1941 (void)this_list;
1942 if (action == ACTION_REQUEST_MENUITEM
1943 && !ingame && ((intptr_t)this_item)==0)
1944 return ACTION_EXIT_MENUITEM;
1945 return action;
1946}
1947
1948static int spacerocks_menu(void)
1949{
1950 int selection = 0;
1951 MENUITEM_STRINGLIST(main_menu, "Spacerocks Menu", spacerocks_menu_cb,
1952 "Resume Game", "Start New Game",
1953 "Help", "High Scores",
1954 "Playback Control", "Quit");
1955 rb->button_clear_queue();
1956
1957 while (1)
1958 {
1959 switch (rb->do_menu(&main_menu, &selection, NULL, false))
1960 {
1961 case 0:
1962 return PLUGIN_OTHER;
1963 case 1:
1964 initialise_game();
1965 return PLUGIN_OTHER;
1966 case 2:
1967 if (spacerocks_help())
1968 return PLUGIN_USB_CONNECTED;
1969 break;
1970 case 3:
1971 highscore_show(-1, highscores, NUM_SCORES, true);
1972 break;
1973 case 4:
1974 playback_control(NULL);
1975 break;
1976 case 5:
1977 return PLUGIN_OK;
1978 case MENU_ATTACHED_USB:
1979 return PLUGIN_USB_CONNECTED;
1980 default:
1981 break;
1982 }
1983 }
1984}
1985
1986static int spacerocks_game_loop(void)
1987{
1988 int button;
1989 int end;
1990 int position;
1991 int ret;
1992
1993 if ((ret = spacerocks_menu()) != PLUGIN_OTHER)
1994 return ret;
1995
1996 SET_BG(LCD_BLACK);
1997
1998 ingame = true;
1999 while (true)
2000 {
2001 end = *rb->current_tick + (CYCLETIME * HZ) / 1000;
2002 rb->lcd_clear_display();
2003 SET_FG(COL_TEXT);
2004 switch(game_state)
2005 {
2006 case GAME_OVER:
2007 ingame = false;
2008 rb->splash (HZ * 2, "Game Over");
2009 rb->lcd_clear_display();
2010 position = highscore_update(current_score, current_level, "",
2011 highscores, NUM_SCORES);
2012 if (position != -1)
2013 {
2014 if (position == 0)
2015 rb->splash(HZ*2, "New High Score");
2016 highscore_show(position, highscores, NUM_SCORES, true);
2017 }
2018 return PLUGIN_OTHER;
2019 break;
2020
2021 case PAUSE_MODE:
2022 rb->lcd_putsxyf(1,LCD_HEIGHT-8, "score %d ", current_score);
2023 rb->lcd_putsxy(CENTER_LCD_X - 15,
2024 CENTER_LCD_Y + CENTER_LCD_Y/2 - 4, "pause");
2025 draw_and_move_missiles();
2026 draw_lives();
2027 draw_and_move_ship();
2028 break;
2029
2030 case PLAY_MODE:
2031 rb->lcd_putsxyf(1, LCD_HEIGHT-8, "score %d ", current_score);
2032 draw_and_move_missiles();
2033 draw_lives();
2034 check_collisions();
2035 draw_and_move_ship();
2036 break;
2037
2038 case SHOW_LEVEL:
2039 rb->lcd_putsxyf(1, LCD_HEIGHT-8, "score %d ", current_score);
2040 rb->lcd_putsxyf(CENTER_LCD_X - 20,
2041 CENTER_LCD_Y + CENTER_LCD_Y/2 - 4,
2042 "stage %d ", current_level);
2043 draw_lives();
2044 draw_and_move_ship();
2045 show_level_timeout--;
2046 if (show_level_timeout <= 0)
2047 {
2048 initialise_level(current_level);
2049 game_state = PLAY_MODE;
2050 }
2051 break;
2052 }
2053 draw_and_move_trail_blaze();
2054 drawstars();
2055 draw_and_move_asteroids();
2056 draw_and_move_enemy();
2057
2058 rb->lcd_update();
2059
2060#ifdef HAS_BUTTON_HOLD
2061 if (rb->button_hold() && game_state == PLAY_MODE)
2062 game_state = PAUSE_MODE;
2063#endif
2064 button = rb->button_get(false);
2065 switch(button)
2066 {
2067 case(AST_QUIT):
2068 return PLUGIN_OTHER;
2069 break;
2070#ifdef AST_PAUSE
2071 case(AST_PAUSE):
2072 if (game_state == PAUSE_MODE)
2073 game_state = PLAY_MODE;
2074 else if (game_state == PLAY_MODE)
2075 game_state = PAUSE_MODE;
2076 break;
2077#endif
2078 case (AST_LEFT):
2079 case (AST_LEFT | BUTTON_REPEAT):
2080 if (game_state == PLAY_MODE || game_state == SHOW_LEVEL)
2081 rotate_ship(SHIP_ROT_ACW_COS, SHIP_ROT_ACW_SIN);
2082 break;
2083
2084 case (AST_RIGHT):
2085 case (AST_RIGHT | BUTTON_REPEAT):
2086 if (game_state == PLAY_MODE || game_state == SHOW_LEVEL)
2087 rotate_ship(SHIP_ROT_CW_COS, SHIP_ROT_CW_SIN);
2088 break;
2089
2090 case (AST_THRUST):
2091 case (AST_THRUST | BUTTON_REPEAT):
2092 if (game_state == PLAY_MODE || game_state == SHOW_LEVEL)
2093 {
2094 if (next_thrust_count <= 0)
2095 {
2096 next_thrust_count = 5;
2097 thrust_ship();
2098 }
2099 }
2100 break;
2101
2102 case (AST_HYPERSPACE):
2103 if (game_state == PLAY_MODE)
2104 hyperspace();
2105 /* maybe shield if it gets too hard */
2106 break;
2107
2108 case (AST_FIRE):
2109 case (AST_FIRE | BUTTON_REPEAT):
2110 if (game_state == PLAY_MODE)
2111 {
2112 if (next_missile_count <= 0)
2113 {
2114 fire_missile();
2115 next_missile_count = 10;
2116 }
2117 }
2118 else if(game_state == PAUSE_MODE)
2119 game_state = PLAY_MODE;
2120 break;
2121
2122 default:
2123 if (rb->default_event_handler(button)==SYS_USB_CONNECTED)
2124 return PLUGIN_USB_CONNECTED;
2125 break;
2126 }
2127
2128 if (next_missile_count > 0)
2129 next_missile_count--;
2130
2131 if (next_thrust_count > 0)
2132 next_thrust_count--;
2133
2134 if (TIME_BEFORE(*rb->current_tick, end))
2135 rb->sleep(end-*rb->current_tick);
2136 else
2137 rb->yield();
2138 }
2139}
2140
2141enum plugin_status plugin_start(const void* parameter)
2142{
2143 (void)parameter;
2144 int ret = PLUGIN_OTHER;
2145
2146#if LCD_DEPTH > 1
2147 rb->lcd_set_backdrop(NULL);
2148#endif
2149 /* universal font */
2150 rb->lcd_setfont(FONT_SYSFIXED);
2151
2152 /* Turn off backlight timeout */
2153 backlight_ignore_timeout();
2154
2155 highscore_load(SCORE_FILE, highscores, NUM_SCORES);
2156 rb->srand(*rb->current_tick);
2157
2158 /* create stars once, and once only: */
2159 create_stars();
2160
2161 while (ret == PLUGIN_OTHER)
2162 ret = spacerocks_game_loop();
2163
2164 rb->lcd_setfont(FONT_UI);
2165 highscore_save(SCORE_FILE, highscores, NUM_SCORES);
2166
2167 /* Turn on backlight timeout (revert to settings) */
2168 backlight_use_settings();
2169
2170 return ret;
2171}