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 Albert Veli
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/* Improvised creds goes to:
23 *
24 * - Anders Clausen for ingeniously inventing the name Invadrox.
25 * - Linus Nielsen-Feltzing for patiently answering n00b questions.
26 */
27
28#include "plugin.h"
29#include "lib/highscore.h"
30#include "lib/helper.h"
31
32/* bitmaps */
33#include "pluginbitmaps/invadrox_background.h"
34
35/* get dimensions for later use from the bitmaps */
36#include "pluginbitmaps/invadrox_aliens.h"
37#include "pluginbitmaps/invadrox_ships.h"
38#include "pluginbitmaps/invadrox_bombs.h"
39#include "pluginbitmaps/invadrox_alien_explode.h"
40#include "pluginbitmaps/invadrox_shield.h"
41#include "pluginbitmaps/invadrox_ufo.h"
42#include "pluginbitmaps/invadrox_ufo_explode.h"
43#include "pluginbitmaps/invadrox_numbers.h"
44#include "pluginbitmaps/invadrox_fire.h"
45#define ALIEN_WIDTH (BMPWIDTH_invadrox_aliens/2)
46#define ALIEN_HEIGHT (BMPHEIGHT_invadrox_aliens/3)
47#define SHIP_WIDTH BMPWIDTH_invadrox_ships
48#define SHIP_HEIGHT (BMPHEIGHT_invadrox_ships/3)
49#define BOMB_WIDTH (BMPWIDTH_invadrox_bombs/3)
50#define BOMB_HEIGHT (BMPHEIGHT_invadrox_bombs/6)
51#define ALIEN_EXPLODE_WIDTH BMPWIDTH_invadrox_alien_explode
52#define ALIEN_EXPLODE_HEIGHT BMPHEIGHT_invadrox_alien_explode
53#define SHIELD_WIDTH BMPWIDTH_invadrox_shield
54#define SHIELD_HEIGHT BMPHEIGHT_invadrox_shield
55#define UFO_WIDTH BMPWIDTH_invadrox_ufo
56#define UFO_HEIGHT BMPHEIGHT_invadrox_ufo
57#define UFO_EXPLODE_WIDTH BMPWIDTH_invadrox_ufo_explode
58#define UFO_EXPLODE_HEIGHT BMPHEIGHT_invadrox_ufo_explode
59#define NUMBERS_WIDTH (BMPWIDTH_invadrox_numbers/10)
60#define FONT_HEIGHT BMPHEIGHT_invadrox_numbers
61#define FIRE_WIDTH BMPWIDTH_invadrox_fire
62#define FIRE_HEIGHT BMPHEIGHT_invadrox_fire
63
64
65
66/* Original graphics is only 1bpp so it should be portable
67 * to most targets. But for now, only support the simple ones.
68 */
69#if (LCD_DEPTH < 2)
70 #error INVADROX: Unsupported LCD
71#endif
72
73/* #define DEBUG */
74#ifdef DEBUG
75#define DBG(format, arg...) { DEBUGF("%s: " format, __FUNCTION__, ## arg); }
76#else
77#define DBG(format, arg...) {}
78#endif
79
80#ifndef ABS
81#define ABS(a) (((a) < 0) ? -(a) : (a))
82#endif
83
84#if CONFIG_KEYPAD == IRIVER_H100_PAD
85
86#define QUIT BUTTON_OFF
87#define LEFT BUTTON_LEFT
88#define RIGHT BUTTON_RIGHT
89#define FIRE BUTTON_ON
90
91#elif CONFIG_KEYPAD == IRIVER_H300_PAD
92
93#define QUIT BUTTON_OFF
94#define LEFT BUTTON_LEFT
95#define RIGHT BUTTON_RIGHT
96#define FIRE BUTTON_SELECT
97
98#elif (CONFIG_KEYPAD == IRIVER_H10_PAD)
99
100#define QUIT BUTTON_POWER
101#define LEFT BUTTON_LEFT
102#define RIGHT BUTTON_RIGHT
103#define FIRE BUTTON_PLAY
104
105#elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
106 (CONFIG_KEYPAD == IPOD_3G_PAD) || \
107 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
108
109#define QUIT BUTTON_MENU
110#define LEFT BUTTON_LEFT
111#define RIGHT BUTTON_RIGHT
112#define FIRE BUTTON_SELECT
113
114#elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD
115
116#define QUIT BUTTON_POWER
117#define LEFT BUTTON_LEFT
118#define RIGHT BUTTON_RIGHT
119#define FIRE BUTTON_SELECT
120
121#elif CONFIG_KEYPAD == GIGABEAT_PAD \
122 || CONFIG_KEYPAD == SAMSUNG_YPR0_PAD
123
124#define QUIT BUTTON_POWER
125#define LEFT BUTTON_LEFT
126#define RIGHT BUTTON_RIGHT
127#define FIRE BUTTON_SELECT
128
129#elif (CONFIG_KEYPAD == SANSA_E200_PAD) || \
130 (CONFIG_KEYPAD == SANSA_CONNECT_PAD)
131
132#define QUIT BUTTON_POWER
133#define LEFT BUTTON_LEFT
134#define RIGHT BUTTON_RIGHT
135#define FIRE BUTTON_SELECT
136
137#elif CONFIG_KEYPAD == SANSA_FUZE_PAD
138
139#define QUIT (BUTTON_HOME|BUTTON_REPEAT)
140#define LEFT BUTTON_LEFT
141#define RIGHT BUTTON_RIGHT
142#define FIRE BUTTON_SELECT
143
144#elif CONFIG_KEYPAD == GIGABEAT_S_PAD
145
146#define QUIT BUTTON_BACK
147#define LEFT BUTTON_LEFT
148#define RIGHT BUTTON_RIGHT
149#define FIRE BUTTON_SELECT
150
151#elif CONFIG_KEYPAD == COWON_D2_PAD
152
153#define QUIT BUTTON_POWER
154#define LEFT BUTTON_MINUS
155#define RIGHT BUTTON_PLUS
156#define FIRE BUTTON_MENU
157
158#elif CONFIG_KEYPAD == CREATIVEZVM_PAD
159
160#define QUIT BUTTON_BACK
161#define LEFT BUTTON_LEFT
162#define RIGHT BUTTON_RIGHT
163#define FIRE BUTTON_SELECT
164
165#elif CONFIG_KEYPAD == CREATIVE_ZENXFI3_PAD
166
167#define QUIT BUTTON_POWER
168#define LEFT BUTTON_BACK
169#define RIGHT BUTTON_MENU
170#define FIRE BUTTON_PLAY
171
172#elif CONFIG_KEYPAD == PHILIPS_HDD6330_PAD
173
174#define QUIT BUTTON_POWER
175#define LEFT BUTTON_LEFT
176#define RIGHT BUTTON_RIGHT
177#define FIRE BUTTON_PLAY
178
179#elif CONFIG_KEYPAD == PHILIPS_SA9200_PAD
180
181#define QUIT BUTTON_POWER
182#define LEFT BUTTON_PREV
183#define RIGHT BUTTON_NEXT
184#define FIRE BUTTON_PLAY
185
186#elif CONFIG_KEYPAD == ONDAVX747_PAD || \
187CONFIG_KEYPAD == ONDAVX777_PAD || \
188CONFIG_KEYPAD == MROBE500_PAD
189
190#define QUIT BUTTON_POWER
191
192#elif (CONFIG_KEYPAD == SAMSUNG_YH820_PAD) || \
193 (CONFIG_KEYPAD == SAMSUNG_YH92X_PAD)
194
195#define QUIT BUTTON_REW
196#define LEFT BUTTON_LEFT
197#define RIGHT BUTTON_RIGHT
198#define FIRE BUTTON_PLAY
199#define FIRE2 BUTTON_UP
200
201#elif CONFIG_KEYPAD == PBELL_VIBE500_PAD
202
203#define QUIT BUTTON_REC
204#define LEFT BUTTON_PREV
205#define RIGHT BUTTON_NEXT
206#define FIRE BUTTON_OK
207
208#elif CONFIG_KEYPAD == MPIO_HD300_PAD
209
210#define QUIT BUTTON_MENU
211#define LEFT BUTTON_REW
212#define RIGHT BUTTON_FF
213#define FIRE BUTTON_ENTER
214
215#elif CONFIG_KEYPAD == SANSA_FUZEPLUS_PAD
216
217#define QUIT BUTTON_POWER
218#define LEFT BUTTON_LEFT
219#define RIGHT BUTTON_RIGHT
220#define FIRE BUTTON_SELECT
221
222#elif CONFIG_KEYPAD == SONY_NWZ_PAD
223
224#define QUIT BUTTON_BACK
225#define LEFT BUTTON_LEFT
226#define RIGHT BUTTON_RIGHT
227#define FIRE BUTTON_PLAY
228
229#elif CONFIG_KEYPAD == CREATIVE_ZEN_PAD
230
231#define QUIT BUTTON_BACK
232#define LEFT BUTTON_LEFT
233#define RIGHT BUTTON_RIGHT
234#define FIRE BUTTON_SELECT
235
236#elif (CONFIG_KEYPAD == HM60X_PAD) || \
237 (CONFIG_KEYPAD == HM801_PAD)
238
239#define QUIT BUTTON_POWER
240#define LEFT BUTTON_LEFT
241#define RIGHT BUTTON_RIGHT
242#define FIRE BUTTON_SELECT
243
244#elif CONFIG_KEYPAD == DX50_PAD
245
246#define QUIT (BUTTON_POWER|BUTTON_REL)
247#define LEFT BUTTON_LEFT
248#define RIGHT BUTTON_PLAY
249#define FIRE BUTTON_RIGHT
250
251#elif CONFIG_KEYPAD == CREATIVE_ZENXFI2_PAD
252
253#define QUIT BUTTON_POWER
254#define FIRE BUTTON_MENU
255
256#elif CONFIG_KEYPAD == XDUOO_X3II_PAD || CONFIG_KEYPAD == XDUOO_X20_PAD
257
258#define QUIT BUTTON_POWER
259#define LEFT BUTTON_HOME
260#define RIGHT BUTTON_VOL_DOWN
261#define FIRE BUTTON_VOL_UP
262
263#elif CONFIG_KEYPAD == FIIO_M3K_LINUX_PAD
264
265#define QUIT BUTTON_POWER
266#define LEFT BUTTON_HOME
267#define RIGHT BUTTON_VOL_DOWN
268#define FIRE BUTTON_VOL_UP
269
270#elif CONFIG_KEYPAD == IHIFI_770_PAD || CONFIG_KEYPAD == IHIFI_800_PAD
271
272#define QUIT BUTTON_POWER
273#define LEFT BUTTON_HOME
274#define RIGHT BUTTON_VOL_DOWN
275#define FIRE BUTTON_VOL_UP
276
277#elif CONFIG_KEYPAD == EROSQ_PAD
278
279#define QUIT BUTTON_POWER
280#define LEFT BUTTON_SCROLL_BACK
281#define RIGHT BUTTON_SCROLL_FWD
282#define FIRE BUTTON_PLAY
283
284#elif CONFIG_KEYPAD == FIIO_M3K_PAD
285
286#define QUIT BUTTON_POWER
287#define LEFT BUTTON_LEFT
288#define RIGHT BUTTON_RIGHT
289#define FIRE BUTTON_SELECT
290
291#elif CONFIG_KEYPAD == SHANLING_Q1_PAD
292/* use touchscreen */
293
294#elif CONFIG_KEYPAD == MA_PAD
295
296#define QUIT BUTTON_BACK
297#define LEFT BUTTON_LEFT
298#define RIGHT BUTTON_RIGHT
299#define FIRE BUTTON_PLAY
300
301#elif CONFIG_KEYPAD == RG_NANO_PAD
302
303#define QUIT BUTTON_START
304#define LEFT BUTTON_LEFT
305#define RIGHT BUTTON_RIGHT
306#define FIRE BUTTON_A
307
308#else
309 #error INVADROX: Unsupported keypad
310#endif
311
312#ifndef RC_QUIT
313#define RC_QUIT 0
314#endif
315
316#ifdef HAVE_TOUCHSCREEN
317
318#ifndef QUIT
319#define QUIT 0
320#endif
321#ifndef LEFT
322#define LEFT 0
323#endif
324#ifndef RIGHT
325#define RIGHT 0
326#endif
327#ifndef FIRE
328#define FIRE 0
329#endif
330
331#define TOUCHSCREEN_QUIT BUTTON_TOPLEFT
332#define TOUCHSCREEN_LEFT (BUTTON_MIDLEFT | BUTTON_BOTTOMLEFT)
333#define TOUCHSCREEN_RIGHT (BUTTON_MIDRIGHT | BUTTON_BOTTOMRIGHT)
334#define TOUCHSCREEN_FIRE (BUTTON_CENTER | BUTTON_BOTTOMMIDDLE)
335
336#define ACTION_QUIT (QUIT | TOUCHSCREEN_QUIT | RC_QUIT)
337#define ACTION_LEFT (LEFT | TOUCHSCREEN_LEFT)
338#define ACTION_RIGHT (RIGHT | TOUCHSCREEN_RIGHT)
339#define ACTION_FIRE (FIRE | TOUCHSCREEN_FIRE)
340
341#else /* HAVE_TOUCHSCREEN */
342
343#define ACTION_QUIT (QUIT | RC_QUIT)
344#define ACTION_LEFT LEFT
345#define ACTION_RIGHT RIGHT
346#ifndef FIRE2
347#define ACTION_FIRE FIRE
348#else
349#define ACTION_FIRE (FIRE | FIRE2)
350#endif
351
352#endif
353
354#ifndef UNUSED
355#define UNUSED __attribute__ ((unused))
356#endif
357
358/* Defines common to all models */
359#define UFO_Y (SCORENUM_Y + FONT_HEIGHT + ALIEN_HEIGHT)
360#define PLAYFIELD_Y (LCD_HEIGHT - SHIP_HEIGHT - 2)
361#define PLAYFIELD_WIDTH (LCD_WIDTH - 2 * PLAYFIELD_X)
362#define LEVEL_X (LCD_WIDTH - PLAYFIELD_X - LIVES_X - 2 * NUMBERS_WIDTH - 3 * NUM_SPACING)
363#define SHIP_MIN_X (PLAYFIELD_X + PLAYFIELD_WIDTH / 5 - SHIELD_WIDTH / 2 - SHIP_WIDTH)
364#define SHIP_MAX_X (PLAYFIELD_X + 4 * PLAYFIELD_WIDTH / 5 + SHIELD_WIDTH / 2)
365/* SCORE_Y = 0 for most targets. Gigabeat redefines it later. */
366#define SCORE_Y 0
367#define MAX_LIVES 8
368
369
370/* m:robe 500 defines */
371#if ((LCD_WIDTH == 640) && (LCD_HEIGHT == 480)) || \
372 ((LCD_WIDTH == 480) && (LCD_HEIGHT == 640))
373
374/* Original arcade game size 224x240, 1bpp with
375 * red overlay at top and green overlay at bottom.
376 *
377 * M:Robe 500: 640x480x16
378 * ======================
379 */
380
381#define ARCADISH_GRAPHICS
382#define PLAYFIELD_X 48
383#define SHIP_Y (PLAYFIELD_Y - 2 * SHIP_HEIGHT)
384#define ALIEN_START_Y (UFO_Y + ALIEN_HEIGHT)
385#define SCORENUM_X (PLAYFIELD_X + NUMBERS_WIDTH)
386#define SCORENUM_Y (SCORE_Y + FONT_HEIGHT + 2)
387#define HISCORENUM_X (LCD_WIDTH - PLAYFIELD_X - 1 - 6 * NUMBERS_WIDTH - 5 * NUM_SPACING)
388#define SHIELD_Y (PLAYFIELD_Y - 5 * SHIP_HEIGHT)
389#define LIVES_X 10
390#define MAX_Y 18
391
392/* iPod Video defines */
393#elif (LCD_WIDTH == 320) && (LCD_HEIGHT == 240)
394
395/* Original arcade game size 224x240, 1bpp with
396 * red overlay at top and green overlay at bottom.
397 *
398 * iPod Video: 320x240x16
399 * ======================
400 * X: 48p padding at left/right gives 224p playfield in middle.
401 * 10p "border" gives 204p actual playfield. UFO use full 224p.
402 * Y: Use full 240p.
403 *
404 * MAX_X = (204 - 12) / 2 - 1 = 95
405 *
406 * Y: Score text 7 0
407 * Space 10 7
408 * Score 7 17
409 * Space 8 24
410 * 3 Ufo 7 32
411 * 2 Space Aliens start at 32 + 3 * 8 = 56
412 * 0 aliens 9*8 56 -
413 * space ~7*8 128 | 18.75 aliens space between
414 * shield 2*8 182 | first alien and ship.
415 * space 8 198 | MAX_Y = 18
416 * ship 8 206 -
417 * space 2*8 214
418 * hline 1 230 - PLAYFIELD_Y
419 * bottom border 10 240
420 * Lives and Level goes inside bottom border
421 */
422
423#define ARCADISH_GRAPHICS
424#define PLAYFIELD_X 48
425#define SHIP_Y (PLAYFIELD_Y - 3 * SHIP_HEIGHT)
426#define ALIEN_START_Y (UFO_Y + 3 * ALIEN_HEIGHT)
427#define SCORENUM_X (PLAYFIELD_X + NUMBERS_WIDTH)
428#define SCORENUM_Y SCORE_Y + (2 * (FONT_HEIGHT + 1) + 1)
429#define HISCORENUM_X (LCD_WIDTH - PLAYFIELD_X - 1 - 6 * NUMBERS_WIDTH - 5 * NUM_SPACING)
430#define SHIELD_Y (PLAYFIELD_Y - 6 * SHIP_HEIGHT)
431#define LIVES_X 10
432#define MAX_Y 18
433
434/* Anbernic RG Nano defines */
435#elif (LCD_WIDTH == 240) && (LCD_HEIGHT == 240)
436
437/* Original arcade game size 224x240, 1bpp with
438 * red overlay at top and green overlay at bottom.
439 *
440 * Anbernic RG Nano: 240x240x16
441 * ======================
442 * X: 8p padding at left/right gives 224p playfield in middle.
443 * 10p "border" gives 204p actual playfield. UFO use full 224p.
444 * Y: Use full 240p.
445*/
446
447#define ARCADISH_GRAPHICS
448#define PLAYFIELD_X 8
449#define SHIP_Y (PLAYFIELD_Y - 3 * SHIP_HEIGHT)
450#define ALIEN_START_Y (UFO_Y + 3 * ALIEN_HEIGHT)
451#define SCORENUM_X (PLAYFIELD_X + NUMBERS_WIDTH) + 10
452#define SCORENUM_Y SCORE_Y + (2 * (FONT_HEIGHT + 1) + 1)
453#define HISCORENUM_X (LCD_WIDTH - PLAYFIELD_X - 1 - 6 * NUMBERS_WIDTH - 8 * NUM_SPACING)
454#define SHIELD_Y (PLAYFIELD_Y - 6 * SHIP_HEIGHT)
455#define LIVES_X 10
456#define MAX_Y 18
457
458#elif (LCD_WIDTH == 176) && (LCD_HEIGHT == 220)
459
460/* Sandisk Sansa e200: 176x220x16
461 * ==============================
462 * X: No padding. 8p border -> 160p playfield.
463 *
464 * 8p Aliens with 3p spacing -> 88 + 30 = 118p aliens block.
465 * (160 - 118) / 2 = 21 rounds for whole block (more than original)
466 * MAX_X = (160 - 8) / 2 - 1 = 75 rounds for single alien (less than original)
467 *
468 * LOGO 70 0
469 * Score text 5 70
470 * Space 5 75
471 * Y Score 5 80
472 * Space 10 85
473 * 2 Ufo 5 95
474 * 2 Space 10 100
475 * 0 aliens 9*5 110 -
476 * space ~7*5 155 | 18.6 aliens space between
477 * shield 2*5 188 | first alien and ship.
478 * space 5 198 | MAX_Y = 18
479 * ship 5 203 -
480 * space 5 208
481 * hline 1 213 PLAYFIELD_Y
482 * bottom border 6
483 * LCD_HEIGHT 220
484 * Lives and Level goes inside bottom border
485 */
486
487#define SMALL_GRAPHICS
488#define PLAYFIELD_X 0
489#define SHIP_Y (PLAYFIELD_Y - 2 * SHIP_HEIGHT)
490#define SHIELD_Y (SHIP_Y - SHIP_HEIGHT - SHIELD_HEIGHT)
491#define ALIEN_START_Y (UFO_Y + 3 * SHIP_HEIGHT)
492/* Redefine SCORE_Y */
493#undef SCORE_Y
494#define SCORE_Y 70
495#define SCORENUM_X (PLAYFIELD_X + NUMBERS_WIDTH)
496#define SCORENUM_Y (SCORE_Y + 2 * FONT_HEIGHT)
497#define HISCORENUM_X (LCD_WIDTH - PLAYFIELD_X - 1 - 6 * NUMBERS_WIDTH - 5 * NUM_SPACING)
498#define LIVES_X 8
499#define MAX_Y 18
500
501
502#elif (LCD_WIDTH == 176) && (LCD_HEIGHT == 132)
503
504/* iPod Nano: 176x132x16
505 * ======================
506 * X: No padding. 8p border -> 160p playfield.
507 *
508 * LIVES_X 8
509 * ALIEN_WIDTH 8
510 * ALIEN_HEIGHT 5
511 * ALIEN_SPACING 3
512 * SHIP_WIDTH 10
513 * SHIP_HEIGHT 5
514 * FONT_HEIGHT 5
515 * UFO_WIDTH 10
516 * UFO_HEIGHT 5
517 * SHIELD_WIDTH 15
518 * SHIELD_HEIGHT 10
519 * MAX_X 75
520 * MAX_Y = 18
521 * ALIEN_START_Y (UFO_Y + 12)
522 *
523 * 8p Aliens with 3p spacing -> 88 + 30 = 118p aliens block.
524 * (160 - 118) / 2 = 21 rounds for whole block (more than original)
525 * MAX_X = (160 - 8) / 2 - 1 = 75 rounds for single alien (less than original)
526 *
527 * Y: Scoreline 5 0 (combine scoretext and numbers on same line)
528 * Space 5 5
529 * 1 Ufo 5 10
530 * 3 Space 7 15
531 * 2 aliens 9*5 22 -
532 * space ~7*5 67 | Just above 18 aliens space between
533 * shield 2*5 100 | first alien and ship.
534 * space 5 110 | MAX_Y = 18
535 * ship 5 115 -
536 * space 5 120
537 * hline 1 125 PLAYFIELD_Y
538 * bottom border 6 126
539 * LCD_HEIGHT 131
540 * Lives and Level goes inside bottom border
541 */
542
543#define SMALL_GRAPHICS
544#define PLAYFIELD_X 0
545#define SHIP_Y (PLAYFIELD_Y - 2 * SHIP_HEIGHT)
546#define ALIEN_START_Y (UFO_Y + 12)
547#define SCORENUM_X (PLAYFIELD_X + 6 * NUMBERS_WIDTH + 5 * NUM_SPACING)
548#define SCORENUM_Y SCORE_Y
549#define HISCORENUM_X (LCD_WIDTH - PLAYFIELD_X - 4 * NUMBERS_WIDTH - 3 * NUM_SPACING)
550#define SHIELD_Y (SHIP_Y - SHIP_HEIGHT - SHIELD_HEIGHT)
551#define LIVES_X 8
552#define MAX_Y 18
553
554#elif (LCD_WIDTH == 160) && (LCD_HEIGHT == 128)
555
556/* iAudio X5, iRiver H10 20Gb, iPod 3g/4g, H100, M5: 160x128
557 * =========================================================
558 * X: No padding. No border -> 160p playfield.
559 *
560 * LIVES_X 0
561 * ALIEN_WIDTH 8
562 * ALIEN_HEIGHT 5
563 * ALIEN_SPACING 3
564 * SHIP_WIDTH 10
565 * SHIP_HEIGHT 5
566 * FONT_HEIGHT 5
567 * UFO_WIDTH 10
568 * UFO_HEIGHT 5
569 * SHIELD_WIDTH 15
570 * SHIELD_HEIGHT 10
571 * MAX_X 75
572 * MAX_Y = 18
573 * ALIEN_START_Y (UFO_Y + 10)
574 *
575 * 8p Aliens with 3p spacing -> 88 + 30 = 118p aliens block.
576 * (160 - 118) / 2 = 21 rounds for whole block (more than original)
577 * MAX_X = (160 - 8) / 2 - 1 = 75 rounds for single alien (less than original)
578 *
579 * Y: Scoreline 5 0 (combine scoretext and numbers on same line)
580 * Space 5 5
581 * 1 Ufo 5 10
582 * 2 Space 5 15
583 * 8 aliens 9*5 20 -
584 * space ~6*5 65 | Just above 18 aliens space between
585 * shield 2*5 96 | first alien and ship.
586 * space 5 106 | MAX_Y = 18
587 * ship 5 111 -
588 * space 5 116
589 * hline 1 121 PLAYFIELD_Y
590 * bottom border 6 122
591 * LCD_HEIGHT 128
592 * Lives and Level goes inside bottom border
593 */
594
595#define SMALL_GRAPHICS
596#define PLAYFIELD_X 0
597#define SHIP_Y (PLAYFIELD_Y - 2 * SHIP_HEIGHT)
598#define ALIEN_START_Y (UFO_Y + 10)
599#define SCORENUM_X (PLAYFIELD_X + 6 * NUMBERS_WIDTH + 5 * NUM_SPACING)
600#define SCORENUM_Y SCORE_Y
601#define HISCORENUM_X (LCD_WIDTH - PLAYFIELD_X - 4 * NUMBERS_WIDTH - 3 * NUM_SPACING)
602#define SHIELD_Y (SHIP_Y - SHIP_HEIGHT - SHIELD_HEIGHT)
603#define LIVES_X 0
604#define MAX_Y 18
605
606
607#elif (LCD_WIDTH == 240) && ((LCD_HEIGHT == 320) || (LCD_HEIGHT == 400))
608
609/* Gigabeat: 240x320x16
610 * ======================
611 * X: 8p padding at left/right gives 224p playfield in middle.
612 * 10p "border" gives 204p actual playfield. UFO use full 224p.
613 * Y: Use bottom 240p for playfield and top 80 pixels for logo.
614 *
615 * MAX_X = (204 - 12) / 2 - 1 = 95
616 *
617 * Y: Score text 7 0 + 80
618 * Space 10 7 + 80
619 * Score 7 17 + 80
620 * Space 8 24 + 80
621 * 3 Ufo 7 32 + 80
622 * 2 Space Aliens start at 32 + 3 * 8 = 56
623 * 0 aliens 9*8 56 -
624 * space ~7*8 128 | 18.75 aliens space between
625 * shield 2*8 182 | first alien and ship.
626 * space 8 198 | MAX_Y = 18
627 * ship 8 206 -
628 * space 2*8 214
629 * hline 1 230 310 - PLAYFIELD_Y
630 * bottom border 10 240 320
631 * Lives and Level goes inside bottom border
632 */
633
634#define ARCADISH_GRAPHICS
635#define PLAYFIELD_X 8
636#define SHIP_Y (PLAYFIELD_Y - 3 * SHIP_HEIGHT)
637#define ALIEN_START_Y (UFO_Y + 3 * ALIEN_HEIGHT)
638/* Redefine SCORE_Y */
639#undef SCORE_Y
640#define SCORE_Y 80
641#define SCORENUM_X (PLAYFIELD_X + NUMBERS_WIDTH)
642#define SCORENUM_Y SCORE_Y + (2 * (FONT_HEIGHT + 1) + 1)
643#define HISCORENUM_X (LCD_WIDTH - PLAYFIELD_X - 1 - 6 * NUMBERS_WIDTH - 5 * NUM_SPACING)
644#define SHIELD_Y (PLAYFIELD_Y - 6 * SHIP_HEIGHT)
645#define LIVES_X 10
646#define MAX_Y 18
647
648#elif (LCD_WIDTH == 220) && (LCD_HEIGHT == 176)
649
650/* H300, iPod Color: 220x176x16
651 * ============================
652 * X: 0p padding at left/right gives 220p playfield in middle.
653 * 8p "border" gives 204p actual playfield. UFO use full 220p.
654 * Y: Use full 176p for playfield.
655 *
656 * MAX_X = (204 - 12) / 2 - 1 = 95
657 *
658 * Y: Score text 7 0
659 * Space 8 7
660 * 1 Ufo 7 15
661 * 7 Space Aliens start at 15 + 3 * 8 = 56
662 * 6 aliens 9*8 25 -
663 * space ~7*8 103 | 15.6 aliens space between
664 * shield 2*8 126 | first alien and ship.
665 * space 8 142 | MAX_Y = 15
666 * ship 8 150 -
667 * space 8 158
668 * hline 1 166 - PLAYFIELD_Y
669 * bottom border 10 176
670 * Lives and Level goes inside bottom border
671 */
672
673#define ARCADISH_GRAPHICS
674#define PLAYFIELD_X 0
675#define SHIP_Y (PLAYFIELD_Y - 2 * SHIP_HEIGHT)
676#define ALIEN_START_Y (UFO_Y + 10)
677#define SCORENUM_Y SCORE_Y
678#define SCORENUM_X (PLAYFIELD_X + 6 * NUMBERS_WIDTH + 6 * NUM_SPACING)
679#define HISCORENUM_X (LCD_WIDTH - PLAYFIELD_X - 4 * NUMBERS_WIDTH - 3 * NUM_SPACING)
680#define SHIELD_Y (PLAYFIELD_Y - 5 * SHIP_HEIGHT)
681#define LIVES_X 8
682#define MAX_Y 15
683
684#elif (LCD_WIDTH == 360) && (LCD_HEIGHT == 400)
685
686/* Shanling Q1
687 */
688#define ARCADISH_GRAPHICS
689#define PLAYFIELD_X 32
690#define SHIP_Y (PLAYFIELD_Y - 3 * SHIP_HEIGHT)
691#define ALIEN_START_Y (UFO_Y + 3 * ALIEN_HEIGHT)
692/* Redefine SCORE_Y */
693#undef SCORE_Y
694#define SCORE_Y 80
695#define SCORENUM_X (PLAYFIELD_X + NUMBERS_WIDTH)
696#define SCORENUM_Y SCORE_Y + (2 * (FONT_HEIGHT + 1) + 1)
697#define HISCORENUM_X (LCD_WIDTH - PLAYFIELD_X - 1 - 6 * NUMBERS_WIDTH - 5 * NUM_SPACING)
698#define SHIELD_Y (PLAYFIELD_Y - 6 * SHIP_HEIGHT)
699#define LIVES_X 10
700#define MAX_Y 18
701
702#else
703 #error INVADROX: Unsupported LCD type
704#endif
705
706#define MAX_X ((LCD_WIDTH-LIVES_X*2-PLAYFIELD_X*2 - ALIEN_WIDTH)/2 - 1)
707
708/* Defines common to each "graphic type" */
709#ifdef ARCADISH_GRAPHICS
710
711#define SHOT_HEIGHT 5
712#define ALIEN_SPACING 4
713#define ALIEN_SPEED 2
714#define UFO_SPEED 1
715#define NUM_SPACING 3
716#define FIRE_SPEED 8
717#define BOMB_SPEED 3
718#define ALIENS 11
719
720#elif defined SMALL_GRAPHICS
721
722#define SHOT_HEIGHT 4
723#define ALIEN_SPACING 3
724#define ALIEN_SPEED 2
725#define UFO_SPEED 1
726#define NUM_SPACING 2
727#define FIRE_SPEED 6
728#define BOMB_SPEED 2
729#define ALIENS 11
730
731#else
732 #error Graphic type not defined
733#endif
734
735
736/* Colors */
737#if (LCD_DEPTH >= 8)
738#define SLIME_GREEN LCD_RGBPACK(31, 254, 31)
739#define UFO_RED LCD_RGBPACK(254, 31, 31)
740#elif (LCD_DEPTH == 2)
741#define SLIME_GREEN LCD_LIGHTGRAY
742#define UFO_RED LCD_LIGHTGRAY
743#else
744#error LCD type not implemented yet
745#endif
746
747/* Alien states */
748#define DEAD 0
749#define ALIVE 1
750#define BOMBER 2
751
752/* Fire/bomb/ufo states */
753#define S_IDLE 0
754#define S_ACTIVE 1
755#define S_SHOWSCORE 2
756#define S_EXPLODE -9
757
758/* Fire/bomb targets */
759#define TARGET_TOP 0
760#define TARGET_SHIELD 1
761#define TARGET_SHIP 2
762#define TARGET_BOTTOM 3
763#define TARGET_UFO 4
764
765#define HISCOREFILE PLUGIN_GAMES_DATA_DIR "/invadrox.high"
766
767
768/* The time (in ms) for one iteration through the game loop - decrease this
769 * to speed up the game - note that current_tick is (currently) only accurate
770 * to 10ms.
771 */
772#define CYCLETIME 40
773
774
775/* Physical x is at PLAYFIELD_X + LIVES_X + x * ALIEN_SPEED
776 * Physical y is at y * ALIEN_HEIGHT
777 */
778struct alien {
779 int x; /* x-coordinate (0 - 95) */
780 int y; /* y-coordinate (0 - 18) */
781 unsigned char type; /* 0 (Kang), 1 (Kodos), 2 (Serak) */
782 unsigned char state; /* Dead, alive or bomber */
783};
784
785/* Aliens box 5 rows * ALIENS aliens in each row */
786struct alien aliens[5 * ALIENS];
787
788#define MAX_BOMBS 4
789struct bomb {
790 int x, y;
791 unsigned char type;
792 unsigned char frame; /* Current animation frame */
793 unsigned char frames; /* Number of frames in animation */
794 unsigned char target; /* Remember target during explosion frames */
795 int state; /* 0 (IDLE) = inactive, 1 (FIRE) or negative, exploding */
796};
797struct bomb bombs[MAX_BOMBS];
798/* Increase max_bombs at higher levels */
799int max_bombs;
800
801/* Raw framebuffer value of shield/ship green color */
802fb_data screen_green, screen_white;
803
804/* For optimization, precalculate startoffset of each scanline */
805unsigned int ytab[LCD_HEIGHT];
806
807int lives = 2;
808int score = 0;
809int scores[3] = { 30, 20, 10 };
810int level = 0;
811struct highscore hiscore;
812bool game_over = false;
813int ship_x, old_ship_x, ship_dir, ship_acc, max_ship_speed;
814int ship_frame, ship_frame_counter;
815bool ship_hit;
816int fire, fire_target, fire_x, fire_y;
817int curr_alien, aliens_paralyzed, gamespeed;
818int ufo_state, ufo_x;
819bool level_finished;
820bool aliens_down, aliens_right, hit_left_border, hit_right_border;
821static fb_data *lcd_fb;
822
823/* No standard get_pixel function yet, use this hack instead */
824#if (LCD_DEPTH >= 8)
825
826#if LCD_STRIDEFORMAT == VERTICAL_STRIDE
827static inline fb_data get_pixel(int x, int y)
828{
829 return lcd_fb[x*LCD_HEIGHT+y];
830}
831#else
832static inline fb_data get_pixel(int x, int y)
833{
834 return lcd_fb[ytab[y] + x];
835}
836#endif
837
838#elif (LCD_DEPTH == 2)
839
840#if (LCD_PIXELFORMAT == HORIZONTAL_PACKING)
841static const unsigned char shifts[4] = {
842 6, 4, 2, 0
843};
844/* Horizontal packing */
845static inline fb_data get_pixel(int x, int y)
846{
847 return (lcd_fb[ytab[y] + (x >> 2)] >> shifts[x & 3]) & 3;
848}
849#else
850/* Vertical packing */
851static const unsigned char shifts[4] = {
852 0, 2, 4, 6
853};
854static inline fb_data get_pixel(int x, int y)
855{
856 return (lcd_fb[ytab[y] + x] >> shifts[y & 3]) & 3;
857}
858#endif /* Horizontal/Vertical packing */
859
860#else
861 #error get_pixel: pixelformat not implemented yet
862#endif
863
864
865/* Draw "digits" least significant digits of num at (x,y) */
866static void draw_number(int x, int y, int num, int digits)
867{
868 int i;
869 int d;
870
871 for (i = digits - 1; i >= 0; i--) {
872 d = num % 10;
873 num = num / 10;
874 rb->lcd_bitmap_part(invadrox_numbers, d * NUMBERS_WIDTH, 0,
875 STRIDE( SCREEN_MAIN,
876 BMPWIDTH_invadrox_numbers,
877 BMPHEIGHT_invadrox_numbers),
878 x + i * (NUMBERS_WIDTH + NUM_SPACING), y,
879 NUMBERS_WIDTH, FONT_HEIGHT);
880 }
881 /* Update lcd */
882 rb->lcd_update_rect(x, y, 4 * NUMBERS_WIDTH + 3 * NUM_SPACING, FONT_HEIGHT);
883}
884
885
886static inline void draw_score(void)
887{
888 draw_number(SCORENUM_X, SCORENUM_Y, score, 4);
889 if (score > hiscore.score) {
890 /* Draw new hiscore (same as score) */
891 draw_number(HISCORENUM_X, SCORENUM_Y, score, 4);
892 }
893}
894
895
896static void draw_level(void)
897{
898 draw_number(LEVEL_X + 2 * NUM_SPACING, PLAYFIELD_Y + 2, level, 2);
899}
900
901
902static void draw_lives(void)
903{
904 int i;
905 /* Lives num */
906 rb->lcd_bitmap_part(invadrox_numbers, lives * NUMBERS_WIDTH, 0,
907 STRIDE( SCREEN_MAIN,
908 BMPWIDTH_invadrox_numbers,
909 BMPHEIGHT_invadrox_numbers),
910 PLAYFIELD_X + LIVES_X, PLAYFIELD_Y + 2,
911 NUMBERS_WIDTH, FONT_HEIGHT);
912
913 /* Ships */
914 for (i = 0; i < (lives - 1); i++) {
915 rb->lcd_bitmap_part(invadrox_ships, 0, 0,
916 STRIDE( SCREEN_MAIN,
917 BMPWIDTH_invadrox_ships,
918 BMPHEIGHT_invadrox_ships),
919 PLAYFIELD_X + LIVES_X + SHIP_WIDTH + i * (SHIP_WIDTH + NUM_SPACING),
920 PLAYFIELD_Y + 1, SHIP_WIDTH, SHIP_HEIGHT);
921 }
922
923 /* Erase ship to the right (if less than MAX_LIVES) */
924 if (lives < MAX_LIVES) {
925 rb->lcd_fillrect(PLAYFIELD_X + LIVES_X + SHIP_WIDTH + i * (SHIP_WIDTH + NUM_SPACING),
926 PLAYFIELD_Y + 1, SHIP_WIDTH, SHIP_HEIGHT);
927 }
928 /* Update lives (and level) part of screen */
929 rb->lcd_update_rect(PLAYFIELD_X + LIVES_X, PLAYFIELD_Y + 1,
930 PLAYFIELD_WIDTH - 2 * LIVES_X, MAX(FONT_HEIGHT + 1, SHIP_HEIGHT + 1));
931}
932
933
934static inline void draw_aliens(void)
935{
936 int i;
937
938 for (i = 0; i < 5 * ALIENS; i++) {
939 rb->lcd_bitmap_part(invadrox_aliens, aliens[i].x & 1 ? ALIEN_WIDTH : 0,
940 aliens[i].type * ALIEN_HEIGHT,
941 STRIDE( SCREEN_MAIN,
942 BMPWIDTH_invadrox_aliens,
943 BMPHEIGHT_invadrox_aliens),
944 PLAYFIELD_X + LIVES_X + aliens[i].x * ALIEN_SPEED,
945 ALIEN_START_Y + aliens[i].y * ALIEN_HEIGHT,
946 ALIEN_WIDTH, ALIEN_HEIGHT);
947 }
948}
949
950
951/* Return false if there is no next alive alien (round is over) */
952static inline bool next_alien(void)
953{
954 bool ret = true;
955
956 do {
957 curr_alien++;
958 if (curr_alien % ALIENS == 0) {
959 /* End of this row. Move up one row. */
960 curr_alien -= 2 * ALIENS;
961 if (curr_alien < 0) {
962 /* No more aliens in this round. */
963 curr_alien = 4 * ALIENS;
964 ret = false;
965 }
966 }
967 } while (aliens[curr_alien].state == DEAD && ret);
968
969 if (!ret) {
970 /* No more alive aliens. Round finished. */
971 if (hit_right_border) {
972 if (hit_left_border) {
973 DBG("ERROR: both left and right borders are set (%d)\n", curr_alien);
974 }
975 /* Move down-left next round */
976 aliens_right = false;
977 aliens_down = true;
978 hit_right_border = false;
979 } else if (hit_left_border) {
980 /* Move down-right next round */
981 aliens_right = true;
982 aliens_down = true;
983 hit_left_border = false;
984 } else {
985 /* Not left nor right. Set down to false. */
986 aliens_down = false;
987 }
988 }
989
990 return ret;
991}
992
993
994/* All aliens have been moved.
995 * Set curr_alien to first alive.
996 * Return false if no-one is left alive.
997 */
998static bool first_alien(void)
999{
1000 int i, y;
1001
1002 for (y = 4; y >= 0; y--) {
1003 for (i = y * ALIENS; i < (y + 1) * ALIENS; i++) {
1004 if (aliens[i].state != DEAD) {
1005 curr_alien = i;
1006 return true;
1007 }
1008 }
1009 }
1010
1011 /* All aliens dead. */
1012 level_finished = true;
1013
1014 return false;
1015}
1016
1017
1018static bool move_aliens(void)
1019{
1020 int x, y, old_x, old_y;
1021
1022 /* Move current alien (curr_alien is pointing to a living alien) */
1023
1024 old_x = aliens[curr_alien].x;
1025 old_y = aliens[curr_alien].y;
1026
1027 if (aliens_down) {
1028 aliens[curr_alien].y++;
1029 if (aliens[curr_alien].y == MAX_Y) {
1030 /* Alien is at bottom. Game Over. */
1031 DBG("Alien %d is at bottom. Game Over.\n", curr_alien);
1032 game_over = true;
1033 return false;
1034 }
1035 }
1036
1037 if (aliens_right) {
1038 /* Moving right */
1039 if (aliens[curr_alien].x < MAX_X) {
1040 aliens[curr_alien].x++;
1041 }
1042
1043 /* Now, after move, check if we hit the right border. */
1044 if (aliens[curr_alien].x == MAX_X) {
1045 hit_right_border = true;
1046 }
1047
1048 } else {
1049 /* Moving left */
1050 if (aliens[curr_alien].x > 0) {
1051 aliens[curr_alien].x--;
1052 }
1053
1054 /* Now, after move, check if we hit the left border. */
1055 if (aliens[curr_alien].x == 0) {
1056 hit_left_border = true;
1057 }
1058 }
1059
1060 /* Erase old position */
1061 x = PLAYFIELD_X + LIVES_X + old_x * ALIEN_SPEED;
1062 y = ALIEN_START_Y + old_y * ALIEN_HEIGHT;
1063 if (aliens[curr_alien].y != old_y) {
1064 /* Moved in y-dir. Erase whole alien. */
1065 rb->lcd_fillrect(x, y, ALIEN_WIDTH, ALIEN_HEIGHT);
1066 } else {
1067 if (aliens_right) {
1068 /* Erase left edge */
1069 rb->lcd_fillrect(x, y, ALIEN_SPEED, ALIEN_HEIGHT);
1070 } else {
1071 /* Erase right edge */
1072 x += ALIEN_WIDTH - ALIEN_SPEED;
1073 rb->lcd_fillrect(x, y, ALIEN_SPEED, ALIEN_HEIGHT);
1074 }
1075 }
1076
1077 /* Draw alien at new pos */
1078 x = PLAYFIELD_X + LIVES_X + aliens[curr_alien].x * ALIEN_SPEED;
1079 y = ALIEN_START_Y + aliens[curr_alien].y * ALIEN_HEIGHT;
1080 rb->lcd_bitmap_part(invadrox_aliens,
1081 aliens[curr_alien].x & 1 ? ALIEN_WIDTH : 0,
1082 aliens[curr_alien].type * ALIEN_HEIGHT,
1083 STRIDE( SCREEN_MAIN,
1084 BMPWIDTH_invadrox_aliens,
1085 BMPHEIGHT_invadrox_aliens),
1086 x, y, ALIEN_WIDTH, ALIEN_HEIGHT);
1087
1088 if (!next_alien()) {
1089 /* Round finished. Set curr_alien to first alive from bottom. */
1090 if (!first_alien()) {
1091 /* Should never happen. Taken care of in move_fire(). */
1092 return false;
1093 }
1094 /* TODO: Play next background sound */
1095 }
1096
1097 return true;
1098}
1099
1100
1101static inline void draw_ship(void)
1102{
1103 /* Erase old ship */
1104 if (old_ship_x < ship_x) {
1105 /* Move right. Erase leftmost part of ship. */
1106 rb->lcd_fillrect(old_ship_x, SHIP_Y, ship_x - old_ship_x, SHIP_HEIGHT);
1107 } else if (old_ship_x > ship_x) {
1108 /* Move left. Erase rightmost part of ship. */
1109 rb->lcd_fillrect(ship_x + SHIP_WIDTH, SHIP_Y, old_ship_x - ship_x, SHIP_HEIGHT);
1110 }
1111
1112 /* Draw ship */
1113 rb->lcd_bitmap_part(invadrox_ships, 0, ship_frame * SHIP_HEIGHT,
1114 STRIDE( SCREEN_MAIN,
1115 BMPWIDTH_invadrox_ships,
1116 BMPHEIGHT_invadrox_ships),
1117 ship_x, SHIP_Y, SHIP_WIDTH, SHIP_HEIGHT);
1118 if (ship_hit) {
1119 /* Alternate between frame 1 and 2 during hit */
1120 ship_frame_counter++;
1121 if (ship_frame_counter > 2) {
1122 ship_frame_counter = 0;
1123 ship_frame++;
1124 if (ship_frame > 2) {
1125 ship_frame = 1;
1126 }
1127 }
1128 }
1129
1130 /* Save ship_x for next time */
1131 old_ship_x = ship_x;
1132}
1133
1134
1135static inline void fire_alpha(int xc, int yc, unsigned color)
1136{
1137 int oldmode = rb->lcd_get_drawmode();
1138
1139 rb->lcd_set_foreground(color);
1140 rb->lcd_set_drawmode(DRMODE_FG);
1141
1142 rb->lcd_mono_bitmap(invadrox_fire, xc - (FIRE_WIDTH/2), yc, FIRE_WIDTH, FIRE_HEIGHT);
1143
1144 rb->lcd_set_foreground(LCD_BLACK);
1145 rb->lcd_set_drawmode(oldmode);
1146}
1147
1148
1149static void move_fire(void)
1150{
1151 bool hit_green = false;
1152 bool hit_white = false;
1153 int i, j;
1154 static int exploding_alien = -1;
1155 fb_data pix;
1156
1157 if (fire == S_IDLE) {
1158 return;
1159 }
1160
1161 /* Alien hit. Wait until explosion is finished. */
1162 if (aliens_paralyzed < 0) {
1163 aliens_paralyzed++;
1164 if (aliens_paralyzed == 0) {
1165 /* Erase exploding_alien */
1166 rb->lcd_fillrect(PLAYFIELD_X + LIVES_X + aliens[exploding_alien].x * ALIEN_SPEED,
1167 ALIEN_START_Y + aliens[exploding_alien].y * ALIEN_HEIGHT,
1168 ALIEN_EXPLODE_WIDTH, ALIEN_HEIGHT);
1169 fire = S_IDLE;
1170 /* Special case. We killed curr_alien. */
1171 if (exploding_alien == curr_alien) {
1172 if (!next_alien()) {
1173 /* Round finished. Set curr_alien to first alive from bottom. */
1174 first_alien();
1175 }
1176 }
1177 }
1178 return;
1179 }
1180
1181 if (fire == S_ACTIVE) {
1182
1183 /* Erase */
1184 rb->lcd_vline(fire_x, fire_y, fire_y + SHOT_HEIGHT);
1185
1186 /* Check top */
1187 if (fire_y <= SCORENUM_Y + FONT_HEIGHT + 4) {
1188
1189 /* TODO: Play explode sound */
1190
1191 fire = S_EXPLODE;
1192 fire_target = TARGET_TOP;
1193 fire_alpha(fire_x, fire_y, UFO_RED);
1194 return;
1195 }
1196
1197 /* Move */
1198 fire_y -= FIRE_SPEED;
1199
1200 /* Hit UFO? */
1201 if (ufo_state == S_ACTIVE) {
1202 if ((ABS(ufo_x + UFO_WIDTH / 2 - fire_x) <= UFO_WIDTH / 2) &&
1203 (fire_y <= UFO_Y + UFO_HEIGHT)) {
1204 ufo_state = S_EXPLODE;
1205 fire = S_EXPLODE;
1206 fire_target = TARGET_UFO;
1207 /* Center explosion */
1208 ufo_x -= (UFO_EXPLODE_WIDTH - UFO_WIDTH) / 2;
1209 rb->lcd_bitmap(invadrox_ufo_explode, ufo_x, UFO_Y - 1,
1210 UFO_EXPLODE_WIDTH, UFO_EXPLODE_HEIGHT);
1211 return;
1212 }
1213 }
1214
1215 /* Hit bomb? (check position, not pixel value) */
1216 for (i = 0; i < max_bombs; i++) {
1217 if (bombs[i].state == S_ACTIVE) {
1218 /* Count as hit if within BOMB_WIDTH pixels */
1219 if ((ABS(bombs[i].x - fire_x) < BOMB_WIDTH) &&
1220 (fire_y - bombs[i].y < BOMB_HEIGHT)) {
1221 /* Erase bomb */
1222 rb->lcd_fillrect(bombs[i].x, bombs[i].y, BOMB_WIDTH, BOMB_HEIGHT);
1223 bombs[i].state = S_IDLE;
1224 /* Explode ship fire */
1225 fire = S_EXPLODE;
1226 fire_target = TARGET_SHIELD;
1227 fire_alpha(fire_x, fire_y, LCD_WHITE);
1228 return;
1229 }
1230 }
1231 }
1232
1233 /* Check for hit*/
1234 for (i = FIRE_SPEED; i >= 0; i--) {
1235 pix = get_pixel(fire_x, fire_y + i);
1236 if(!memcmp(&pix, &screen_white, sizeof(fb_data))) {
1237 hit_white = true;
1238 fire_y += i;
1239 break;
1240 }
1241 if(!memcmp(&pix, &screen_green, sizeof(fb_data))) {
1242 hit_green = true;
1243 fire_y += i;
1244 break;
1245 }
1246 }
1247
1248 if (hit_green) {
1249 /* Hit shield */
1250
1251 /* TODO: Play explode sound */
1252
1253 fire = S_EXPLODE;
1254 fire_target = TARGET_SHIELD;
1255 /* Center explosion around hit pixel */
1256 fire_y -= FIRE_HEIGHT / 2;
1257 fire_alpha(fire_x, fire_y, SLIME_GREEN);
1258 return;
1259 }
1260
1261 if (hit_white) {
1262
1263 /* Hit alien? */
1264 for (i = 0; i < 5 * ALIENS; i++) {
1265 if (aliens[i].state != DEAD &&
1266 (ABS(fire_x - (PLAYFIELD_X + LIVES_X + aliens[i].x * ALIEN_SPEED +
1267 ALIEN_WIDTH / 2)) <= ALIEN_WIDTH / 2) &&
1268 (ABS(fire_y - (ALIEN_START_Y + aliens[i].y * ALIEN_HEIGHT +
1269 ALIEN_HEIGHT / 2)) <= ALIEN_HEIGHT / 2)) {
1270
1271 /* TODO: play alien hit sound */
1272
1273 if (aliens[i].state == BOMBER) {
1274 /* Set (possible) alien above to bomber */
1275 for (j = i - ALIENS; j >= 0; j -= ALIENS) {
1276 if (aliens[j].state != DEAD) {
1277 /* printf("New bomber (%d, %d)\n", j % ALIENS, j / ALIENS); */
1278 aliens[j].state = BOMBER;
1279 break;
1280 }
1281 }
1282 }
1283 aliens[i].state = DEAD;
1284 exploding_alien = i;
1285 score += scores[aliens[i].type];
1286 draw_score();
1287 /* Update score part of screen */
1288 rb->lcd_update_rect(SCORENUM_X, SCORENUM_Y,
1289 PLAYFIELD_WIDTH - 2 * NUMBERS_WIDTH, FONT_HEIGHT);
1290
1291 /* Paralyze aliens S_EXPLODE frames */
1292 aliens_paralyzed = S_EXPLODE;
1293 rb->lcd_bitmap(invadrox_alien_explode,
1294 PLAYFIELD_X + LIVES_X + aliens[i].x * ALIEN_SPEED,
1295 ALIEN_START_Y + aliens[i].y * ALIEN_HEIGHT,
1296 ALIEN_EXPLODE_WIDTH, ALIEN_EXPLODE_HEIGHT);
1297 /* Since alien is 1 pixel taller than explosion sprite, erase bottom line */
1298 rb->lcd_hline(PLAYFIELD_X + LIVES_X + aliens[i].x * ALIEN_SPEED,
1299 PLAYFIELD_X + LIVES_X + aliens[i].x * ALIEN_SPEED + ALIEN_WIDTH,
1300 ALIEN_START_Y + (aliens[i].y + 1) * ALIEN_HEIGHT - 1);
1301 return;
1302 }
1303 }
1304 }
1305
1306 /* Draw shot */
1307 rb->lcd_set_foreground(LCD_WHITE);
1308 rb->lcd_vline(fire_x, fire_y, fire_y + SHOT_HEIGHT);
1309 rb->lcd_set_foreground(LCD_BLACK);
1310 } else if (fire < S_IDLE) {
1311 /* Count up towards S_IDLE, then erase explosion */
1312 fire++;
1313 if (fire == S_IDLE) {
1314 /* Erase explosion */
1315 if (fire_target == TARGET_TOP) {
1316 rb->lcd_fillrect(fire_x - (FIRE_WIDTH / 2), fire_y, FIRE_WIDTH, FIRE_HEIGHT);
1317 } else if (fire_target == TARGET_SHIELD) {
1318 /* Draw explosion with black pixels */
1319 fire_alpha(fire_x, fire_y, LCD_BLACK);
1320 }
1321 }
1322 }
1323}
1324
1325
1326/* Return a BOMBER alien */
1327static inline int random_bomber(void)
1328{
1329 int i, col;
1330
1331 /* TODO: Weigh higher probability near ship */
1332 col = rb->rand() % ALIENS;
1333 for (i = col + 4 * ALIENS; i >= 0; i -= ALIENS) {
1334 if (aliens[i].state == BOMBER) {
1335 return i;
1336 }
1337 }
1338
1339 /* No BOMBER found in this col */
1340
1341 for (i = 0; i < 5 * ALIENS; i++) {
1342 if (aliens[i].state == BOMBER) {
1343 return i;
1344 }
1345 }
1346
1347 /* No BOMBER found at all (error?) */
1348
1349 return -1;
1350}
1351
1352
1353static inline void draw_bomb(int i)
1354{
1355 rb->lcd_bitmap_part(invadrox_bombs, bombs[i].type * BOMB_WIDTH,
1356 bombs[i].frame * BOMB_HEIGHT,
1357 STRIDE( SCREEN_MAIN,
1358 BMPWIDTH_invadrox_bombs,
1359 BMPHEIGHT_invadrox_bombs),
1360 bombs[i].x, bombs[i].y,
1361 BOMB_WIDTH, BOMB_HEIGHT);
1362 /* Advance frame */
1363 bombs[i].frame++;
1364 if (bombs[i].frame == bombs[i].frames) {
1365 bombs[i].frame = 0;
1366 }
1367}
1368
1369
1370static void move_bombs(void)
1371{
1372 int i, j, bomber;
1373 bool abort;
1374
1375 for (i = 0; i < max_bombs; i++) {
1376
1377 switch (bombs[i].state) {
1378
1379 case S_IDLE:
1380 if (ship_hit) {
1381 continue;
1382 }
1383 bomber = random_bomber();
1384 if (bomber < 0) {
1385 DBG("ERROR: No bomber available\n");
1386 continue;
1387 }
1388 /* x, y */
1389 bombs[i].x = PLAYFIELD_X + LIVES_X + aliens[bomber].x * ALIEN_SPEED + ALIEN_WIDTH / 2;
1390 bombs[i].y = ALIEN_START_Y + (aliens[bomber].y + 1) * ALIEN_HEIGHT;
1391
1392 /* Check for duplets in x and y direction */
1393 abort = false;
1394 for (j = i - 1; j >= 0; j--) {
1395 if ((bombs[j].state == S_ACTIVE) &&
1396 ((bombs[i].x == bombs[j].x) || (bombs[i].y == bombs[j].y))) {
1397 abort = true;
1398 break;
1399 }
1400 }
1401 if (abort) {
1402 /* Skip this one, continue with next bomb */
1403 /* printf("Bomb %d duplet of %d\n", i, j); */
1404 continue;
1405 }
1406
1407 /* Passed, set type */
1408 bombs[i].type = rb->rand() % 3;
1409 bombs[i].frame = 0;
1410 if (bombs[i].type == 0) {
1411 bombs[i].frames = 3;
1412 } else if (bombs[i].type == 1) {
1413 bombs[i].frames = 4;
1414 } else {
1415 bombs[i].frames = 6;
1416 }
1417
1418 /* Bombs away */
1419 bombs[i].state = S_ACTIVE;
1420 draw_bomb(i);
1421 continue;
1422
1423 break;
1424
1425 case S_ACTIVE:
1426 /* Erase old position */
1427 rb->lcd_fillrect(bombs[i].x, bombs[i].y, BOMB_WIDTH, BOMB_HEIGHT);
1428
1429 /* Move */
1430 bombs[i].y += BOMB_SPEED;
1431
1432 /* Check if bottom hit */
1433 if (bombs[i].y + BOMB_HEIGHT >= PLAYFIELD_Y) {
1434 bombs[i].y = PLAYFIELD_Y - FIRE_HEIGHT + 1;
1435 fire_alpha(bombs[i].x, bombs[i].y, LCD_WHITE);
1436 bombs[i].state = S_EXPLODE;
1437 bombs[i].target = TARGET_BOTTOM;
1438 break;
1439 }
1440
1441 /* Check for green (ship or shield) */
1442 for (j = BOMB_HEIGHT; j >= BOMB_HEIGHT - BOMB_SPEED; j--) {
1443 bombs[i].target = 0;
1444 fb_data pix = get_pixel(bombs[i].x + BOMB_WIDTH / 2, bombs[i].y + j);
1445 if(!memcmp(&pix, &screen_green, sizeof(fb_data))) {
1446 /* Move to hit pixel */
1447 bombs[i].x += BOMB_WIDTH / 2;
1448 bombs[i].y += j;
1449
1450 /* Check if ship is hit */
1451 if (bombs[i].y > SHIELD_Y + SHIELD_HEIGHT && bombs[i].y < PLAYFIELD_Y) {
1452
1453 /* TODO: play ship hit sound */
1454
1455 ship_hit = true;
1456 ship_frame = 1;
1457 ship_frame_counter = 0;
1458 bombs[i].state = S_EXPLODE * 4;
1459 bombs[i].target = TARGET_SHIP;
1460 rb->lcd_bitmap_part(invadrox_ships, 0, 1 * SHIP_HEIGHT,
1461 STRIDE( SCREEN_MAIN,
1462 BMPWIDTH_invadrox_ships,
1463 BMPHEIGHT_invadrox_ships),
1464 ship_x, SHIP_Y,
1465 SHIP_WIDTH, SHIP_HEIGHT);
1466 break;
1467 }
1468 /* Shield hit */
1469 bombs[i].state = S_EXPLODE;
1470 bombs[i].target = TARGET_SHIELD;
1471 /* Center explosion around hit pixel in shield */
1472 bombs[i].y -= FIRE_HEIGHT / 2;
1473 fire_alpha(bombs[i].x, bombs[i].y, SLIME_GREEN);
1474 break;
1475 }
1476 }
1477
1478 if (bombs[i].target != 0) {
1479 /* Hit ship or shield, continue */
1480 continue;
1481 }
1482
1483 draw_bomb(i);
1484 break;
1485
1486 default:
1487 /* If we get here state should be < 0, exploding */
1488 bombs[i].state++;
1489 if (bombs[i].state == S_IDLE) {
1490 if (ship_hit) {
1491 /* Erase explosion */
1492 rb->lcd_fillrect(ship_x, SHIP_Y, SHIP_WIDTH, SHIP_HEIGHT);
1493 rb->lcd_update_rect(ship_x, SHIP_Y, SHIP_WIDTH, SHIP_HEIGHT);
1494 ship_hit = false;
1495 ship_frame = 0;
1496 ship_x = PLAYFIELD_X + 2 * LIVES_X;
1497 lives--;
1498 if (lives == 0) {
1499 game_over = true;
1500 return;
1501 }
1502 draw_lives();
1503 /* Sleep 1s to give player time to examine lives left */
1504 rb->sleep(HZ);
1505 }
1506 /* Erase explosion (even if ship hit, might be another bomb) */
1507 fire_alpha(bombs[i].x, bombs[i].y, LCD_BLACK);
1508 }
1509 break;
1510 }
1511 }
1512}
1513
1514
1515static inline void move_ship(void)
1516{
1517 ship_dir += ship_acc;
1518 if (ship_dir > max_ship_speed) {
1519 ship_dir = max_ship_speed;
1520 }
1521 if (ship_dir < -max_ship_speed) {
1522 ship_dir = -max_ship_speed;
1523 }
1524 ship_x += ship_dir;
1525 if (ship_x < SHIP_MIN_X) {
1526 ship_x = SHIP_MIN_X;
1527 }
1528 if (ship_x > SHIP_MAX_X) {
1529 ship_x = SHIP_MAX_X;
1530 }
1531
1532 draw_ship();
1533}
1534
1535
1536/* Unidentified Flying Object */
1537static void move_ufo(void)
1538{
1539 static int ufo_speed;
1540 static int counter;
1541 int mystery_score;
1542
1543 switch (ufo_state) {
1544
1545 case S_IDLE:
1546
1547 if (rb->rand() % 500 == 0) {
1548 /* Uh-oh, it's time to launch a mystery UFO */
1549
1550 /* TODO: Play UFO sound */
1551
1552 if (rb->rand() % 2) {
1553 ufo_speed = UFO_SPEED;
1554 ufo_x = PLAYFIELD_X;
1555 } else {
1556 ufo_speed = -UFO_SPEED;
1557 ufo_x = LCD_WIDTH - PLAYFIELD_X - UFO_WIDTH;
1558 }
1559 ufo_state = S_ACTIVE;
1560 /* UFO will be drawn next frame */
1561 }
1562 break;
1563
1564 case S_ACTIVE:
1565 /* Erase old pos */
1566 rb->lcd_fillrect(ufo_x, UFO_Y, UFO_WIDTH, UFO_HEIGHT);
1567 /* Move */
1568 ufo_x += ufo_speed;
1569 /* Check bounds */
1570 if (ufo_x < PLAYFIELD_X || ufo_x > LCD_WIDTH - PLAYFIELD_X - UFO_WIDTH) {
1571 ufo_state = S_IDLE;
1572 break;
1573 }
1574 /* Draw new pos */
1575 rb->lcd_bitmap(invadrox_ufo, ufo_x, UFO_Y, UFO_WIDTH, UFO_HEIGHT);
1576 break;
1577
1578 case S_SHOWSCORE:
1579 counter++;
1580 if (counter == S_IDLE) {
1581 /* Erase mystery number */
1582 rb->lcd_fillrect(ufo_x, UFO_Y, 3 * NUMBERS_WIDTH + 2 * NUM_SPACING, FONT_HEIGHT);
1583 ufo_state = S_IDLE;
1584 }
1585 break;
1586
1587 default:
1588 /* Exploding */
1589 ufo_state++;
1590 if (ufo_state == S_IDLE) {
1591 /* Erase explosion */
1592 rb->lcd_fillrect(ufo_x, UFO_Y - 1, UFO_EXPLODE_WIDTH, UFO_EXPLODE_HEIGHT);
1593 ufo_state = S_SHOWSCORE;
1594 counter = S_EXPLODE * 4;
1595 /* Draw mystery_score, sleep, increase score and continue */
1596 mystery_score = 50 + (rb->rand() % 6) * 50;
1597 if (mystery_score < 100) {
1598 draw_number(ufo_x, UFO_Y, mystery_score, 2);
1599 } else {
1600 draw_number(ufo_x, UFO_Y, mystery_score, 3);
1601 }
1602 score += mystery_score;
1603 draw_score();
1604 }
1605 break;
1606 }
1607}
1608
1609
1610static void draw_background(void)
1611{
1612
1613 rb->lcd_bitmap(invadrox_background, 0, 0, LCD_WIDTH, LCD_HEIGHT);
1614 rb->lcd_update();
1615}
1616
1617
1618static void new_level(void)
1619{
1620 int i;
1621
1622 draw_background();
1623 /* Give an extra life for each new level */
1624 if (lives < MAX_LIVES) {
1625 lives++;
1626 }
1627 draw_lives();
1628
1629 /* Score */
1630 draw_score();
1631 draw_number(HISCORENUM_X, SCORENUM_Y, hiscore.score, 4);
1632
1633 level++;
1634 draw_level();
1635 level_finished = false;
1636
1637 ufo_state = S_IDLE;
1638
1639 /* Init alien positions and states */
1640 for (i = 0; i < 4 * ALIENS; i++) {
1641 aliens[i].x = 0 + (i % ALIENS) * ((ALIEN_WIDTH + ALIEN_SPACING) / ALIEN_SPEED);
1642 aliens[i].y = 2 * (i / ALIENS);
1643 aliens[i].state = ALIVE;
1644 }
1645 /* Last row, bombers */
1646 for (i = 4 * ALIENS; i < 5 * ALIENS; i++) {
1647 aliens[i].x = 0 + (i % ALIENS) * ((ALIEN_WIDTH + ALIEN_SPACING) / ALIEN_SPEED);
1648 aliens[i].y = 2 * (i / ALIENS);
1649 aliens[i].state = BOMBER;
1650 }
1651
1652 /* Init bombs to inactive (S_IDLE) */
1653 for (i = 0; i < MAX_BOMBS; i++) {
1654 bombs[i].state = S_IDLE;
1655 }
1656
1657 /* Start aliens closer to earth from level 2 */
1658 for (i = 0; i < 5 * ALIENS; i++) {
1659 if (level < 6) {
1660 aliens[i].y += level - 1;
1661 } else {
1662 aliens[i].y += 5;
1663 }
1664 }
1665
1666 /* Max concurrent bombs */
1667 max_bombs = 1;
1668
1669 gamespeed = 2;
1670
1671 if (level > 1) {
1672 max_bombs++;
1673 }
1674
1675 /* Increase speed */
1676 if (level > 2) {
1677 gamespeed++;
1678 }
1679
1680 if (level > 3) {
1681 max_bombs++;
1682 }
1683
1684 /* Increase speed more */
1685 if (level > 4) {
1686 gamespeed++;
1687 }
1688
1689 if (level > 5) {
1690 max_bombs++;
1691 }
1692
1693 /* 4 shields */
1694 for (i = 1; i <= 4; i++) {
1695 rb->lcd_bitmap(invadrox_shield,
1696 PLAYFIELD_X + i * PLAYFIELD_WIDTH / 5 - SHIELD_WIDTH / 2,
1697 SHIELD_Y, SHIELD_WIDTH, SHIELD_HEIGHT);
1698 }
1699
1700 /* Bottom line */
1701 rb->lcd_set_foreground(SLIME_GREEN);
1702 rb->lcd_hline(PLAYFIELD_X, LCD_WIDTH - PLAYFIELD_X, PLAYFIELD_Y);
1703 /* Restore foreground to black (for fast erase later). */
1704 rb->lcd_set_foreground(LCD_BLACK);
1705
1706 ship_x = PLAYFIELD_X + 2 * LIVES_X;
1707 if (level == 1) {
1708 old_ship_x = ship_x;
1709 }
1710 ship_dir = 0;
1711 ship_acc = 0;
1712 ship_frame = 0;
1713 ship_hit = false;
1714 fire = S_IDLE;
1715 /* Start moving the bottom row left to right */
1716 curr_alien = 4 * ALIENS;
1717 aliens_paralyzed = 0;
1718 aliens_right = true;
1719 aliens_down = false;
1720 hit_left_border = false;
1721 hit_right_border = false;
1722 /* TODO: Change max_ship_speed to 3 at higher levels */
1723 max_ship_speed = 2;
1724
1725 draw_aliens();
1726
1727 rb->lcd_update();
1728}
1729
1730
1731static void init_invadrox(void)
1732{
1733 int i;
1734
1735 /* Seed random number generator with a "random" number */
1736 rb->srand(rb->get_time()->tm_sec + rb->get_time()->tm_min * 60);
1737
1738 /* Precalculate start of each scanline */
1739 for (i = 0; i < LCD_HEIGHT; i++) {
1740#if (LCD_DEPTH >= 8)
1741 ytab[i] = i * LCD_WIDTH;
1742#elif (LCD_DEPTH == 2) && (LCD_PIXELFORMAT == HORIZONTAL_PACKING)
1743 ytab[i] = i * (LCD_WIDTH / 4);
1744#elif (LCD_DEPTH == 2) && (LCD_PIXELFORMAT == VERTICAL_PACKING)
1745 ytab[i] = (i / 4) * LCD_WIDTH;
1746#else
1747 #error pixelformat not implemented yet
1748#endif
1749 }
1750
1751 rb->lcd_set_background(LCD_BLACK);
1752 rb->lcd_set_foreground(LCD_BLACK);
1753
1754 if (highscore_load(HISCOREFILE, &hiscore, 1) < 0) {
1755 /* Init hiscore to 0 */
1756 rb->strlcpy(hiscore.name, "Invader", sizeof(hiscore.name));
1757 hiscore.score = 0;
1758 hiscore.level = 1;
1759 }
1760
1761 /* Init alien types in aliens array */
1762 for (i = 0; i < 1 * ALIENS; i++) {
1763 aliens[i].type = 0; /* Kang */
1764 }
1765 for (; i < 3 * ALIENS; i++) {
1766 aliens[i].type = 1; /* Kodos */
1767 }
1768 for (; i < 5 * ALIENS; i++) {
1769 aliens[i].type = 2; /* Serak */
1770 }
1771
1772
1773 /* Save screen white color */
1774 rb->lcd_set_foreground(LCD_WHITE);
1775 rb->lcd_drawpixel(0, 0);
1776 rb->lcd_update_rect(0, 0, 1, 1);
1777 screen_white = get_pixel(0, 0);
1778
1779 /* Save screen green color */
1780 rb->lcd_set_foreground(SLIME_GREEN);
1781 rb->lcd_drawpixel(0, 0);
1782 rb->lcd_update_rect(0, 0, 1, 1);
1783 screen_green = get_pixel(0, 0);
1784
1785 /* Restore black foreground */
1786 rb->lcd_set_foreground(LCD_BLACK);
1787
1788 new_level();
1789
1790 /* Flash score at start */
1791 for (i = 0; i < 5; i++) {
1792 rb->lcd_fillrect(SCORENUM_X, SCORENUM_Y,
1793 4 * NUMBERS_WIDTH + 3 * NUM_SPACING,
1794 FONT_HEIGHT);
1795 rb->lcd_update_rect(SCORENUM_X, SCORENUM_Y,
1796 4 * NUMBERS_WIDTH + 3 * NUM_SPACING,
1797 FONT_HEIGHT);
1798 rb->sleep(HZ / 10);
1799 draw_number(SCORENUM_X, SCORENUM_Y, score, 4);
1800 rb->sleep(HZ / 10);
1801 }
1802}
1803
1804
1805static inline bool handle_buttons(void)
1806{
1807 static unsigned int oldbuttonstate = 0;
1808
1809 unsigned int released, pressed, newbuttonstate;
1810
1811 if (ship_hit) {
1812 /* Don't allow ship movement during explosion */
1813 newbuttonstate = 0;
1814 } else {
1815 newbuttonstate = rb->button_status();
1816 }
1817 if(newbuttonstate == oldbuttonstate) {
1818 if (newbuttonstate == 0) {
1819 /* No button pressed. Stop ship. */
1820 ship_acc = 0;
1821 if (ship_dir > 0) {
1822 ship_dir--;
1823 }
1824 if (ship_dir < 0) {
1825 ship_dir++;
1826 }
1827 }
1828 /* return false; */
1829 goto check_usb;
1830 }
1831 released = ~newbuttonstate & oldbuttonstate;
1832 pressed = newbuttonstate & ~oldbuttonstate;
1833 oldbuttonstate = newbuttonstate;
1834 if (pressed) {
1835 if (pressed & ACTION_LEFT) {
1836 if (ship_acc > -1) {
1837 ship_acc--;
1838 }
1839 }
1840 if (pressed & ACTION_RIGHT) {
1841 if (ship_acc < 1) {
1842 ship_acc++;
1843 }
1844 }
1845 if (pressed & ACTION_FIRE) {
1846 if (fire == S_IDLE) {
1847 /* Fire shot */
1848 fire_x = ship_x + SHIP_WIDTH / 2;
1849 fire_y = SHIP_Y - SHOT_HEIGHT;
1850 fire = S_ACTIVE;
1851 /* TODO: play fire sound */
1852 }
1853 }
1854 if (pressed & ACTION_QUIT) {
1855 rb->splash(HZ * 1, "Quit");
1856 return true;
1857 }
1858 }
1859 if (released) {
1860 if ((released & ACTION_LEFT)) {
1861 if (ship_acc < 1) {
1862 ship_acc++;
1863 }
1864 }
1865 if ((released & ACTION_RIGHT)) {
1866 if (ship_acc > -1) {
1867 ship_acc--;
1868 }
1869 }
1870 }
1871
1872check_usb:
1873
1874 /* Quit if USB is connected */
1875 if (rb->button_get(false) == SYS_USB_CONNECTED) {
1876 return true;
1877 }
1878
1879 return false;
1880}
1881
1882
1883static void game_loop(void)
1884{
1885 int i, end;
1886
1887 /* Print dimensions (just for debugging) */
1888 DBG("%03dx%03dx%02d\n", LCD_WIDTH, LCD_HEIGHT, LCD_DEPTH);
1889
1890 /* Init */
1891 init_invadrox();
1892
1893 while (1) {
1894 /* Convert CYCLETIME (in ms) to HZ */
1895 end = *rb->current_tick + (CYCLETIME * HZ) / 1000;
1896
1897 if (handle_buttons()) {
1898 return;
1899 }
1900
1901 /* Animate */
1902 move_ship();
1903 move_fire();
1904
1905 /* Check if level is finished (marked by move_fire) */
1906 if (level_finished) {
1907 /* TODO: Play level finished sound */
1908 new_level();
1909 }
1910
1911 move_ufo();
1912
1913 /* Move aliens */
1914 if (!aliens_paralyzed && !ship_hit) {
1915 for (i = 0; i < gamespeed; i++) {
1916 if (!move_aliens()) {
1917 if (game_over) {
1918 return;
1919 }
1920 }
1921 }
1922 }
1923
1924 /* Move alien bombs */
1925 move_bombs();
1926 if (game_over) {
1927 return;
1928 }
1929
1930 /* Update "playfield" rect */
1931 rb->lcd_update_rect(PLAYFIELD_X, SCORENUM_Y + FONT_HEIGHT,
1932 PLAYFIELD_WIDTH,
1933 PLAYFIELD_Y + 1 - SCORENUM_Y - FONT_HEIGHT);
1934
1935 /* Wait until next frame */
1936 DBG("%ld (%d)\n", end - *rb->current_tick, (CYCLETIME * HZ) / 1000);
1937 if (TIME_BEFORE(*rb->current_tick, end)) {
1938 rb->sleep(end - *rb->current_tick);
1939 } else {
1940 rb->yield();
1941 }
1942
1943 } /* end while */
1944}
1945
1946
1947/* this is the plugin entry point */
1948enum plugin_status plugin_start(UNUSED const void* parameter)
1949{
1950 rb->lcd_setfont(FONT_SYSFIXED);
1951 /* Turn off backlight timeout */
1952#ifdef HAVE_BACKLIGHT
1953 backlight_ignore_timeout();
1954#endif
1955 struct viewport *vp_main = rb->lcd_set_viewport(NULL);
1956 lcd_fb = vp_main->buffer->fb_ptr;
1957 /* now go ahead and have fun! */
1958 game_loop();
1959
1960 /* Game Over. */
1961 /* TODO: Play game over sound */
1962 rb->splash(HZ * 2, "Game Over");
1963 if (score > hiscore.score) {
1964 /* Save new hiscore */
1965 highscore_update(score, level, "Invader", &hiscore, 1);
1966 highscore_save(HISCOREFILE, &hiscore, 1);
1967 }
1968
1969 /* Restore user's original backlight setting */
1970 rb->lcd_setfont(FONT_UI);
1971 /* Turn on backlight timeout (revert to settings) */
1972#ifdef HAVE_BACKLIGHT
1973 backlight_use_settings();
1974#endif
1975 return PLUGIN_OK;
1976}
1977
1978
1979
1980/**
1981 * GNU Emacs settings: Kernighan & Richie coding style with
1982 * 4 spaces indent and no tabs.
1983 * Local Variables:
1984 * c-file-style: "k&r"
1985 * c-basic-offset: 4
1986 * indent-tabs-mode: nil
1987 * End:
1988 */