1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
10 * Copyright (C) 2006 Albert Veli
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
22 /* Improvised creds goes to:
24 * - Anders Clausen for ingeniously inventing the name Invadrox.
25 * - Linus Nielsen-Feltzing for patiently answering n00b questions.
29 #include "lib/highscore.h"
30 #include "lib/helper.h"
33 #include "pluginbitmaps/invadrox_background.h"
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
66 /* Original graphics is only 1bpp so it should be portable
67 * to most targets. But for now, only support the simple ones.
69 #ifndef HAVE_LCD_BITMAP
70 #error INVADROX: Unsupported LCD
74 #error INVADROX: Unsupported LCD
79 #define DBG(format, arg...) { DEBUGF("%s: " format, __FUNCTION__, ## arg); }
81 #define DBG(format, arg...) {}
85 #define ABS(a) (((a) < 0) ? -(a) : (a))
88 #if CONFIG_KEYPAD == IRIVER_H100_PAD
90 #define QUIT BUTTON_OFF
91 #define LEFT BUTTON_LEFT
92 #define RIGHT BUTTON_RIGHT
93 #define FIRE BUTTON_ON
95 #elif CONFIG_KEYPAD == IRIVER_H300_PAD
97 #define QUIT BUTTON_OFF
98 #define LEFT BUTTON_LEFT
99 #define RIGHT BUTTON_RIGHT
100 #define FIRE BUTTON_SELECT
102 #elif (CONFIG_KEYPAD == IRIVER_H10_PAD)
104 #define QUIT BUTTON_POWER
105 #define LEFT BUTTON_LEFT
106 #define RIGHT BUTTON_RIGHT
107 #define FIRE BUTTON_PLAY
109 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
110 (CONFIG_KEYPAD == IPOD_3G_PAD) || \
111 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
113 #define QUIT BUTTON_MENU
114 #define LEFT BUTTON_LEFT
115 #define RIGHT BUTTON_RIGHT
116 #define FIRE BUTTON_SELECT
118 #elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD
120 #define QUIT BUTTON_POWER
121 #define LEFT BUTTON_LEFT
122 #define RIGHT BUTTON_RIGHT
123 #define FIRE BUTTON_SELECT
125 #elif CONFIG_KEYPAD == GIGABEAT_PAD
127 #define QUIT BUTTON_POWER
128 #define LEFT BUTTON_LEFT
129 #define RIGHT BUTTON_RIGHT
130 #define FIRE BUTTON_SELECT
132 #elif CONFIG_KEYPAD == SANSA_E200_PAD
134 #define QUIT BUTTON_POWER
135 #define LEFT BUTTON_LEFT
136 #define RIGHT BUTTON_RIGHT
137 #define FIRE BUTTON_SELECT
139 #elif CONFIG_KEYPAD == SANSA_FUZE_PAD
141 #define QUIT (BUTTON_HOME|BUTTON_REPEAT)
142 #define LEFT BUTTON_LEFT
143 #define RIGHT BUTTON_RIGHT
144 #define FIRE BUTTON_SELECT
146 #elif CONFIG_KEYPAD == TATUNG_TPJ1022_PAD
148 /* TODO: Figure out which buttons to use for Tatung Elio TPJ-1022 */
149 #define QUIT BUTTON_AB
150 #define LEFT BUTTON_LEFT
151 #define RIGHT BUTTON_RIGHT
152 #define FIRE BUTTON_MENU
154 #elif CONFIG_KEYPAD == GIGABEAT_S_PAD
156 #define QUIT BUTTON_BACK
157 #define LEFT BUTTON_LEFT
158 #define RIGHT BUTTON_RIGHT
159 #define FIRE BUTTON_SELECT
161 #elif CONFIG_KEYPAD == COWON_D2_PAD
163 #define QUIT BUTTON_POWER
164 #define LEFT BUTTON_MINUS
165 #define RIGHT BUTTON_PLUS
166 #define FIRE BUTTON_MENU
168 #elif CONFIG_KEYPAD == IAUDIO67_PAD
170 #define QUIT BUTTON_POWER
171 #define LEFT BUTTON_LEFT
172 #define RIGHT BUTTON_RIGHT
173 #define FIRE BUTTON_PLAY
175 #elif CONFIG_KEYPAD == CREATIVEZVM_PAD
177 #define QUIT BUTTON_BACK
178 #define LEFT BUTTON_LEFT
179 #define RIGHT BUTTON_RIGHT
180 #define FIRE BUTTON_SELECT
182 #elif CONFIG_KEYPAD == PHILIPS_HDD6330_PAD
184 #define QUIT BUTTON_POWER
185 #define LEFT BUTTON_LEFT
186 #define RIGHT BUTTON_RIGHT
187 #define FIRE BUTTON_PLAY
189 #elif CONFIG_KEYPAD == PHILIPS_SA9200_PAD
191 #define QUIT BUTTON_POWER
192 #define LEFT BUTTON_PREV
193 #define RIGHT BUTTON_NEXT
194 #define FIRE BUTTON_PLAY
196 #elif CONFIG_KEYPAD == ONDAVX747_PAD || \
197 CONFIG_KEYPAD == ONDAVX777_PAD || \
198 CONFIG_KEYPAD == MROBE500_PAD
200 #define QUIT BUTTON_POWER
202 #elif CONFIG_KEYPAD == SAMSUNG_YH_PAD
204 #define QUIT BUTTON_REC
205 #define LEFT BUTTON_LEFT
206 #define RIGHT BUTTON_RIGHT
207 #define FIRE BUTTON_PLAY
209 #elif CONFIG_KEYPAD == PBELL_VIBE500_PAD
211 #define QUIT BUTTON_REC
212 #define LEFT BUTTON_PREV
213 #define RIGHT BUTTON_NEXT
214 #define FIRE BUTTON_OK
217 #error INVADROX: Unsupported keypad
224 #ifdef HAVE_TOUCHSCREEN
239 #define TOUCHSCREEN_QUIT BUTTON_TOPLEFT
240 #define TOUCHSCREEN_LEFT (BUTTON_MIDLEFT | BUTTON_BOTTOMLEFT)
241 #define TOUCHSCREEN_RIGHT (BUTTON_MIDRIGHT | BUTTON_BOTTOMRIGHT)
242 #define TOUCHSCREEN_FIRE (BUTTON_CENTER | BUTTON_BOTTOMMIDDLE)
244 #define ACTION_QUIT (QUIT | TOUCHSCREEN_QUIT | RC_QUIT)
245 #define ACTION_LEFT (LEFT | TOUCHSCREEN_LEFT)
246 #define ACTION_RIGHT (RIGHT | TOUCHSCREEN_RIGHT)
247 #define ACTION_FIRE (FIRE | TOUCHSCREEN_FIRE)
249 #else /* HAVE_TOUCHSCREEN */
251 #define ACTION_QUIT (QUIT | RC_QUIT)
252 #define ACTION_LEFT LEFT
253 #define ACTION_RIGHT RIGHT
254 #define ACTION_FIRE FIRE
259 #define UNUSED __attribute__ ((unused))
262 /* Defines common to all models */
263 #define UFO_Y (SCORENUM_Y + FONT_HEIGHT + ALIEN_HEIGHT)
264 #define PLAYFIELD_Y (LCD_HEIGHT - SHIP_HEIGHT - 2)
265 #define PLAYFIELD_WIDTH (LCD_WIDTH - 2 * PLAYFIELD_X)
266 #define LEVEL_X (LCD_WIDTH - PLAYFIELD_X - LIVES_X - 2 * NUMBERS_WIDTH - 3 * NUM_SPACING)
267 #define SHIP_MIN_X (PLAYFIELD_X + PLAYFIELD_WIDTH / 5 - SHIELD_WIDTH / 2 - SHIP_WIDTH)
268 #define SHIP_MAX_X (PLAYFIELD_X + 4 * PLAYFIELD_WIDTH / 5 + SHIELD_WIDTH / 2)
269 /* SCORE_Y = 0 for most targets. Gigabeat redefines it later. */
274 /* m:robe 500 defines */
275 #if ((LCD_WIDTH == 640) && (LCD_HEIGHT == 480)) || \
276 ((LCD_WIDTH == 480) && (LCD_HEIGHT == 640))
278 /* Original arcade game size 224x240, 1bpp with
279 * red overlay at top and green overlay at bottom.
281 * M:Robe 500: 640x480x16
282 * ======================
285 #define ARCADISH_GRAPHICS
286 #define PLAYFIELD_X 48
287 #define SHIP_Y (PLAYFIELD_Y - 2 * SHIP_HEIGHT)
288 #define ALIEN_START_Y (UFO_Y + ALIEN_HEIGHT)
289 #define SCORENUM_X (PLAYFIELD_X + NUMBERS_WIDTH)
290 #define SCORENUM_Y (SCORE_Y + FONT_HEIGHT + 2)
291 #define HISCORENUM_X (LCD_WIDTH - PLAYFIELD_X - 1 - 6 * NUMBERS_WIDTH - 5 * NUM_SPACING)
292 #define SHIELD_Y (PLAYFIELD_Y - 5 * SHIP_HEIGHT)
296 /* iPod Video defines */
297 #elif (LCD_WIDTH == 320) && (LCD_HEIGHT == 240)
299 /* Original arcade game size 224x240, 1bpp with
300 * red overlay at top and green overlay at bottom.
302 * iPod Video: 320x240x16
303 * ======================
304 * X: 48p padding at left/right gives 224p playfield in middle.
305 * 10p "border" gives 204p actual playfield. UFO use full 224p.
308 * MAX_X = (204 - 12) / 2 - 1 = 95
315 * 2 Space Aliens start at 32 + 3 * 8 = 56
317 * space ~7*8 128 | 18.75 aliens space between
318 * shield 2*8 182 | first alien and ship.
319 * space 8 198 | MAX_Y = 18
322 * hline 1 230 - PLAYFIELD_Y
323 * bottom border 10 240
324 * Lives and Level goes inside bottom border
327 #define ARCADISH_GRAPHICS
328 #define PLAYFIELD_X 48
329 #define SHIP_Y (PLAYFIELD_Y - 3 * SHIP_HEIGHT)
330 #define ALIEN_START_Y (UFO_Y + 3 * ALIEN_HEIGHT)
331 #define SCORENUM_X (PLAYFIELD_X + NUMBERS_WIDTH)
332 #define SCORENUM_Y SCORE_Y + (2 * (FONT_HEIGHT + 1) + 1)
333 #define HISCORENUM_X (LCD_WIDTH - PLAYFIELD_X - 1 - 6 * NUMBERS_WIDTH - 5 * NUM_SPACING)
334 #define SHIELD_Y (PLAYFIELD_Y - 6 * SHIP_HEIGHT)
338 #elif (LCD_WIDTH == 176) && (LCD_HEIGHT == 220)
340 /* Sandisk Sansa e200: 176x220x16
341 * ==============================
342 * X: No padding. 8p border -> 160p playfield.
344 * 8p Aliens with 3p spacing -> 88 + 30 = 118p aliens block.
345 * (160 - 118) / 2 = 21 rounds for whole block (more than original)
346 * MAX_X = (160 - 8) / 2 - 1 = 75 rounds for single alien (less than original)
356 * space ~7*5 155 | 18.6 aliens space between
357 * shield 2*5 188 | first alien and ship.
358 * space 5 198 | MAX_Y = 18
361 * hline 1 213 PLAYFIELD_Y
364 * Lives and Level goes inside bottom border
367 #define SMALL_GRAPHICS
368 #define PLAYFIELD_X 0
369 #define SHIP_Y (PLAYFIELD_Y - 2 * SHIP_HEIGHT)
370 #define SHIELD_Y (SHIP_Y - SHIP_HEIGHT - SHIELD_HEIGHT)
371 #define ALIEN_START_Y (UFO_Y + 3 * SHIP_HEIGHT)
372 /* Redefine SCORE_Y */
375 #define SCORENUM_X (PLAYFIELD_X + NUMBERS_WIDTH)
376 #define SCORENUM_Y (SCORE_Y + 2 * FONT_HEIGHT)
377 #define HISCORENUM_X (LCD_WIDTH - PLAYFIELD_X - 1 - 6 * NUMBERS_WIDTH - 5 * NUM_SPACING)
382 #elif (LCD_WIDTH == 176) && (LCD_HEIGHT == 132)
384 /* iPod Nano: 176x132x16
385 * ======================
386 * X: No padding. 8p border -> 160p playfield.
401 * ALIEN_START_Y (UFO_Y + 12)
403 * 8p Aliens with 3p spacing -> 88 + 30 = 118p aliens block.
404 * (160 - 118) / 2 = 21 rounds for whole block (more than original)
405 * MAX_X = (160 - 8) / 2 - 1 = 75 rounds for single alien (less than original)
407 * Y: Scoreline 5 0 (combine scoretext and numbers on same line)
412 * space ~7*5 67 | Just above 18 aliens space between
413 * shield 2*5 100 | first alien and ship.
414 * space 5 110 | MAX_Y = 18
417 * hline 1 125 PLAYFIELD_Y
418 * bottom border 6 126
420 * Lives and Level goes inside bottom border
423 #define SMALL_GRAPHICS
424 #define PLAYFIELD_X 0
425 #define SHIP_Y (PLAYFIELD_Y - 2 * SHIP_HEIGHT)
426 #define ALIEN_START_Y (UFO_Y + 12)
427 #define SCORENUM_X (PLAYFIELD_X + 6 * NUMBERS_WIDTH + 5 * NUM_SPACING)
428 #define SCORENUM_Y SCORE_Y
429 #define HISCORENUM_X (LCD_WIDTH - PLAYFIELD_X - 4 * NUMBERS_WIDTH - 3 * NUM_SPACING)
430 #define SHIELD_Y (SHIP_Y - SHIP_HEIGHT - SHIELD_HEIGHT)
434 #elif (LCD_WIDTH == 160) && (LCD_HEIGHT == 128)
436 /* iAudio X5, iRiver H10 20Gb, iPod 3g/4g, H100, M5: 160x128
437 * =========================================================
438 * X: No padding. No border -> 160p playfield.
453 * ALIEN_START_Y (UFO_Y + 10)
455 * 8p Aliens with 3p spacing -> 88 + 30 = 118p aliens block.
456 * (160 - 118) / 2 = 21 rounds for whole block (more than original)
457 * MAX_X = (160 - 8) / 2 - 1 = 75 rounds for single alien (less than original)
459 * Y: Scoreline 5 0 (combine scoretext and numbers on same line)
464 * space ~6*5 65 | Just above 18 aliens space between
465 * shield 2*5 96 | first alien and ship.
466 * space 5 106 | MAX_Y = 18
469 * hline 1 121 PLAYFIELD_Y
470 * bottom border 6 122
472 * Lives and Level goes inside bottom border
475 #define SMALL_GRAPHICS
476 #define PLAYFIELD_X 0
477 #define SHIP_Y (PLAYFIELD_Y - 2 * SHIP_HEIGHT)
478 #define ALIEN_START_Y (UFO_Y + 10)
479 #define SCORENUM_X (PLAYFIELD_X + 6 * NUMBERS_WIDTH + 5 * NUM_SPACING)
480 #define SCORENUM_Y SCORE_Y
481 #define HISCORENUM_X (LCD_WIDTH - PLAYFIELD_X - 4 * NUMBERS_WIDTH - 3 * NUM_SPACING)
482 #define SHIELD_Y (SHIP_Y - SHIP_HEIGHT - SHIELD_HEIGHT)
487 #elif (LCD_WIDTH == 240) && ((LCD_HEIGHT == 320) || (LCD_HEIGHT == 400))
489 /* Gigabeat: 240x320x16
490 * ======================
491 * X: 8p padding at left/right gives 224p playfield in middle.
492 * 10p "border" gives 204p actual playfield. UFO use full 224p.
493 * Y: Use bottom 240p for playfield and top 80 pixels for logo.
495 * MAX_X = (204 - 12) / 2 - 1 = 95
497 * Y: Score text 7 0 + 80
502 * 2 Space Aliens start at 32 + 3 * 8 = 56
504 * space ~7*8 128 | 18.75 aliens space between
505 * shield 2*8 182 | first alien and ship.
506 * space 8 198 | MAX_Y = 18
509 * hline 1 230 310 - PLAYFIELD_Y
510 * bottom border 10 240 320
511 * Lives and Level goes inside bottom border
514 #define ARCADISH_GRAPHICS
515 #define PLAYFIELD_X 8
516 #define SHIP_Y (PLAYFIELD_Y - 3 * SHIP_HEIGHT)
517 #define ALIEN_START_Y (UFO_Y + 3 * ALIEN_HEIGHT)
518 /* Redefine SCORE_Y */
521 #define SCORENUM_X (PLAYFIELD_X + NUMBERS_WIDTH)
522 #define SCORENUM_Y SCORE_Y + (2 * (FONT_HEIGHT + 1) + 1)
523 #define HISCORENUM_X (LCD_WIDTH - PLAYFIELD_X - 1 - 6 * NUMBERS_WIDTH - 5 * NUM_SPACING)
524 #define SHIELD_Y (PLAYFIELD_Y - 6 * SHIP_HEIGHT)
528 #elif (LCD_WIDTH == 220) && (LCD_HEIGHT == 176)
530 /* TPJ1022, H300, iPod Color: 220x176x16
531 * ============================
532 * X: 0p padding at left/right gives 220p playfield in middle.
533 * 8p "border" gives 204p actual playfield. UFO use full 220p.
534 * Y: Use full 176p for playfield.
536 * MAX_X = (204 - 12) / 2 - 1 = 95
541 * 7 Space Aliens start at 15 + 3 * 8 = 56
543 * space ~7*8 103 | 15.6 aliens space between
544 * shield 2*8 126 | first alien and ship.
545 * space 8 142 | MAX_Y = 15
548 * hline 1 166 - PLAYFIELD_Y
549 * bottom border 10 176
550 * Lives and Level goes inside bottom border
553 #define ARCADISH_GRAPHICS
554 #define PLAYFIELD_X 0
555 #define SHIP_Y (PLAYFIELD_Y - 2 * SHIP_HEIGHT)
556 #define ALIEN_START_Y (UFO_Y + 10)
557 #define SCORENUM_Y SCORE_Y
558 #define SCORENUM_X (PLAYFIELD_X + 6 * NUMBERS_WIDTH + 6 * NUM_SPACING)
559 #define HISCORENUM_X (LCD_WIDTH - PLAYFIELD_X - 4 * NUMBERS_WIDTH - 3 * NUM_SPACING)
560 #define SHIELD_Y (PLAYFIELD_Y - 5 * SHIP_HEIGHT)
566 #error INVADROX: Unsupported LCD type
569 #define MAX_X ((LCD_WIDTH-LIVES_X*2-PLAYFIELD_X*2 - ALIEN_WIDTH)/2 - 1)
571 /* Defines common to each "graphic type" */
572 #ifdef ARCADISH_GRAPHICS
574 #define SHOT_HEIGHT 5
575 #define ALIEN_SPACING 4
576 #define ALIEN_SPEED 2
578 #define NUM_SPACING 3
583 #elif defined SMALL_GRAPHICS
585 #define SHOT_HEIGHT 4
586 #define ALIEN_SPACING 3
587 #define ALIEN_SPEED 2
589 #define NUM_SPACING 2
595 #error Graphic type not defined
601 #define SLIME_GREEN LCD_RGBPACK(31, 254, 31)
602 #define UFO_RED LCD_RGBPACK(254, 31, 31)
603 #elif (LCD_DEPTH == 2)
604 #define SLIME_GREEN LCD_LIGHTGRAY
605 #define UFO_RED LCD_LIGHTGRAY
607 #error LCD type not implemented yet
615 /* Fire/bomb/ufo states */
618 #define S_SHOWSCORE 2
621 /* Fire/bomb targets */
623 #define TARGET_SHIELD 1
624 #define TARGET_SHIP 2
625 #define TARGET_BOTTOM 3
628 #define HISCOREFILE PLUGIN_GAMES_DIR "/invadrox.high"
631 /* The time (in ms) for one iteration through the game loop - decrease this
632 * to speed up the game - note that current_tick is (currently) only accurate
638 /* Physical x is at PLAYFIELD_X + LIVES_X + x * ALIEN_SPEED
639 * Physical y is at y * ALIEN_HEIGHT
642 int x
; /* x-coordinate (0 - 95) */
643 int y
; /* y-coordinate (0 - 18) */
644 unsigned char type
; /* 0 (Kang), 1 (Kodos), 2 (Serak) */
645 unsigned char state
; /* Dead, alive or bomber */
648 /* Aliens box 5 rows * ALIENS aliens in each row */
649 struct alien aliens
[5 * ALIENS
];
655 unsigned char frame
; /* Current animation frame */
656 unsigned char frames
; /* Number of frames in animation */
657 unsigned char target
; /* Remember target during explosion frames */
658 int state
; /* 0 (IDLE) = inactive, 1 (FIRE) or negative, exploding */
660 struct bomb bombs
[MAX_BOMBS
];
661 /* Increase max_bombs at higher levels */
664 /* Raw framebuffer value of shield/ship green color */
665 fb_data screen_green
, screen_white
;
667 /* For optimization, precalculate startoffset of each scanline */
668 unsigned int ytab
[LCD_HEIGHT
];
672 int scores
[3] = { 30, 20, 10 };
674 struct highscore hiscore
;
675 bool game_over
= false;
676 int ship_x
, old_ship_x
, ship_dir
, ship_acc
, max_ship_speed
;
677 int ship_frame
, ship_frame_counter
;
679 int fire
, fire_target
, fire_x
, fire_y
;
680 int curr_alien
, aliens_paralyzed
, gamespeed
;
681 int ufo_state
, ufo_x
;
683 bool aliens_down
, aliens_right
, hit_left_border
, hit_right_border
;
686 /* No standard get_pixel function yet, use this hack instead */
689 #if defined(LCD_STRIDEFORMAT) && LCD_STRIDEFORMAT == VERTICAL_STRIDE
690 static inline fb_data
get_pixel(int x
, int y
)
692 return rb
->lcd_framebuffer
[x
*LCD_HEIGHT
+y
];
695 static inline fb_data
get_pixel(int x
, int y
)
697 return rb
->lcd_framebuffer
[ytab
[y
] + x
];
701 #elif (LCD_DEPTH == 2)
703 #if (LCD_PIXELFORMAT == HORIZONTAL_PACKING)
704 static const unsigned char shifts
[4] = {
707 /* Horizontal packing */
708 static inline fb_data
get_pixel(int x
, int y
)
710 return (rb
->lcd_framebuffer
[ytab
[y
] + (x
>> 2)] >> shifts
[x
& 3]) & 3;
713 /* Vertical packing */
714 static const unsigned char shifts
[4] = {
717 static inline fb_data
get_pixel(int x
, int y
)
719 return (rb
->lcd_framebuffer
[ytab
[y
] + x
] >> shifts
[y
& 3]) & 3;
721 #endif /* Horizontal/Vertical packing */
724 #error get_pixel: pixelformat not implemented yet
728 /* Draw "digits" least significant digits of num at (x,y) */
729 void draw_number(int x
, int y
, int num
, int digits
)
734 for (i
= digits
- 1; i
>= 0; i
--) {
737 rb
->lcd_bitmap_part(invadrox_numbers
, d
* NUMBERS_WIDTH
, 0,
739 BMPWIDTH_invadrox_numbers
,
740 BMPHEIGHT_invadrox_numbers
),
741 x
+ i
* (NUMBERS_WIDTH
+ NUM_SPACING
), y
,
742 NUMBERS_WIDTH
, FONT_HEIGHT
);
745 rb
->lcd_update_rect(x
, y
, 4 * NUMBERS_WIDTH
+ 3 * NUM_SPACING
, FONT_HEIGHT
);
749 static inline void draw_score(void)
751 draw_number(SCORENUM_X
, SCORENUM_Y
, score
, 4);
752 if (score
> hiscore
.score
) {
753 /* Draw new hiscore (same as score) */
754 draw_number(HISCORENUM_X
, SCORENUM_Y
, score
, 4);
759 void draw_level(void)
761 draw_number(LEVEL_X
+ 2 * NUM_SPACING
, PLAYFIELD_Y
+ 2, level
, 2);
765 void draw_lives(void)
769 rb
->lcd_bitmap_part(invadrox_numbers
, lives
* NUMBERS_WIDTH
, 0,
771 BMPWIDTH_invadrox_numbers
,
772 BMPHEIGHT_invadrox_numbers
),
773 PLAYFIELD_X
+ LIVES_X
, PLAYFIELD_Y
+ 2,
774 NUMBERS_WIDTH
, FONT_HEIGHT
);
777 for (i
= 0; i
< (lives
- 1); i
++) {
778 rb
->lcd_bitmap_part(invadrox_ships
, 0, 0,
780 BMPWIDTH_invadrox_ships
,
781 BMPHEIGHT_invadrox_ships
),
782 PLAYFIELD_X
+ LIVES_X
+ SHIP_WIDTH
+ i
* (SHIP_WIDTH
+ NUM_SPACING
),
783 PLAYFIELD_Y
+ 1, SHIP_WIDTH
, SHIP_HEIGHT
);
786 /* Erase ship to the right (if less than MAX_LIVES) */
787 if (lives
< MAX_LIVES
) {
788 rb
->lcd_fillrect(PLAYFIELD_X
+ LIVES_X
+ SHIP_WIDTH
+ i
* (SHIP_WIDTH
+ NUM_SPACING
),
789 PLAYFIELD_Y
+ 1, SHIP_WIDTH
, SHIP_HEIGHT
);
791 /* Update lives (and level) part of screen */
792 rb
->lcd_update_rect(PLAYFIELD_X
+ LIVES_X
, PLAYFIELD_Y
+ 1,
793 PLAYFIELD_WIDTH
- 2 * LIVES_X
, MAX(FONT_HEIGHT
+ 1, SHIP_HEIGHT
+ 1));
797 static inline void draw_aliens(void)
801 for (i
= 0; i
< 5 * ALIENS
; i
++) {
802 rb
->lcd_bitmap_part(invadrox_aliens
, aliens
[i
].x
& 1 ? ALIEN_WIDTH
: 0,
803 aliens
[i
].type
* ALIEN_HEIGHT
,
805 BMPWIDTH_invadrox_aliens
,
806 BMPHEIGHT_invadrox_aliens
),
807 PLAYFIELD_X
+ LIVES_X
+ aliens
[i
].x
* ALIEN_SPEED
,
808 ALIEN_START_Y
+ aliens
[i
].y
* ALIEN_HEIGHT
,
809 ALIEN_WIDTH
, ALIEN_HEIGHT
);
814 /* Return false if there is no next alive alien (round is over) */
815 static inline bool next_alien(void)
821 if (curr_alien
% ALIENS
== 0) {
822 /* End of this row. Move up one row. */
823 curr_alien
-= 2 * ALIENS
;
824 if (curr_alien
< 0) {
825 /* No more aliens in this round. */
826 curr_alien
= 4 * ALIENS
;
830 } while (aliens
[curr_alien
].state
== DEAD
&& ret
);
833 /* No more alive aliens. Round finished. */
834 if (hit_right_border
) {
835 if (hit_left_border
) {
836 DBG("ERROR: both left and right borders are set (%d)\n", curr_alien
);
838 /* Move down-left next round */
839 aliens_right
= false;
841 hit_right_border
= false;
842 } else if (hit_left_border
) {
843 /* Move down-right next round */
846 hit_left_border
= false;
848 /* Not left nor right. Set down to false. */
857 /* All aliens have been moved.
858 * Set curr_alien to first alive.
859 * Return false if no-one is left alive.
861 bool first_alien(void)
865 for (y
= 4; y
>= 0; y
--) {
866 for (i
= y
* ALIENS
; i
< (y
+ 1) * ALIENS
; i
++) {
867 if (aliens
[i
].state
!= DEAD
) {
874 /* All aliens dead. */
875 level_finished
= true;
881 bool move_aliens(void)
883 int x
, y
, old_x
, old_y
;
885 /* Move current alien (curr_alien is pointing to a living alien) */
887 old_x
= aliens
[curr_alien
].x
;
888 old_y
= aliens
[curr_alien
].y
;
891 aliens
[curr_alien
].y
++;
892 if (aliens
[curr_alien
].y
== MAX_Y
) {
893 /* Alien is at bottom. Game Over. */
894 DBG("Alien %d is at bottom. Game Over.\n", curr_alien
);
902 if (aliens
[curr_alien
].x
< MAX_X
) {
903 aliens
[curr_alien
].x
++;
906 /* Now, after move, check if we hit the right border. */
907 if (aliens
[curr_alien
].x
== MAX_X
) {
908 hit_right_border
= true;
913 if (aliens
[curr_alien
].x
> 0) {
914 aliens
[curr_alien
].x
--;
917 /* Now, after move, check if we hit the left border. */
918 if (aliens
[curr_alien
].x
== 0) {
919 hit_left_border
= true;
923 /* Erase old position */
924 x
= PLAYFIELD_X
+ LIVES_X
+ old_x
* ALIEN_SPEED
;
925 y
= ALIEN_START_Y
+ old_y
* ALIEN_HEIGHT
;
926 if (aliens
[curr_alien
].y
!= old_y
) {
927 /* Moved in y-dir. Erase whole alien. */
928 rb
->lcd_fillrect(x
, y
, ALIEN_WIDTH
, ALIEN_HEIGHT
);
931 /* Erase left edge */
932 rb
->lcd_fillrect(x
, y
, ALIEN_SPEED
, ALIEN_HEIGHT
);
934 /* Erase right edge */
935 x
+= ALIEN_WIDTH
- ALIEN_SPEED
;
936 rb
->lcd_fillrect(x
, y
, ALIEN_SPEED
, ALIEN_HEIGHT
);
940 /* Draw alien at new pos */
941 x
= PLAYFIELD_X
+ LIVES_X
+ aliens
[curr_alien
].x
* ALIEN_SPEED
;
942 y
= ALIEN_START_Y
+ aliens
[curr_alien
].y
* ALIEN_HEIGHT
;
943 rb
->lcd_bitmap_part(invadrox_aliens
,
944 aliens
[curr_alien
].x
& 1 ? ALIEN_WIDTH
: 0,
945 aliens
[curr_alien
].type
* ALIEN_HEIGHT
,
947 BMPWIDTH_invadrox_aliens
,
948 BMPHEIGHT_invadrox_aliens
),
949 x
, y
, ALIEN_WIDTH
, ALIEN_HEIGHT
);
952 /* Round finished. Set curr_alien to first alive from bottom. */
953 if (!first_alien()) {
954 /* Should never happen. Taken care of in move_fire(). */
957 /* TODO: Play next background sound */
964 static inline void draw_ship(void)
967 if (old_ship_x
< ship_x
) {
968 /* Move right. Erase leftmost part of ship. */
969 rb
->lcd_fillrect(old_ship_x
, SHIP_Y
, ship_x
- old_ship_x
, SHIP_HEIGHT
);
970 } else if (old_ship_x
> ship_x
) {
971 /* Move left. Erase rightmost part of ship. */
972 rb
->lcd_fillrect(ship_x
+ SHIP_WIDTH
, SHIP_Y
, old_ship_x
- ship_x
, SHIP_HEIGHT
);
976 rb
->lcd_bitmap_part(invadrox_ships
, 0, ship_frame
* SHIP_HEIGHT
,
978 BMPWIDTH_invadrox_ships
,
979 BMPHEIGHT_invadrox_ships
),
980 ship_x
, SHIP_Y
, SHIP_WIDTH
, SHIP_HEIGHT
);
982 /* Alternate between frame 1 and 2 during hit */
983 ship_frame_counter
++;
984 if (ship_frame_counter
> 2) {
985 ship_frame_counter
= 0;
987 if (ship_frame
> 2) {
993 /* Save ship_x for next time */
998 static inline void fire_alpha(int xc
, int yc
, fb_data color
)
1000 int oldmode
= rb
->lcd_get_drawmode();
1002 rb
->lcd_set_foreground(color
);
1003 rb
->lcd_set_drawmode(DRMODE_FG
);
1005 rb
->lcd_mono_bitmap(invadrox_fire
, xc
- (FIRE_WIDTH
/2), yc
, FIRE_WIDTH
, FIRE_HEIGHT
);
1007 rb
->lcd_set_foreground(LCD_BLACK
);
1008 rb
->lcd_set_drawmode(oldmode
);
1012 void move_fire(void)
1014 bool hit_green
= false;
1015 bool hit_white
= false;
1017 static int exploding_alien
= -1;
1020 if (fire
== S_IDLE
) {
1024 /* Alien hit. Wait until explosion is finished. */
1025 if (aliens_paralyzed
< 0) {
1027 if (aliens_paralyzed
== 0) {
1028 /* Erase exploding_alien */
1029 rb
->lcd_fillrect(PLAYFIELD_X
+ LIVES_X
+ aliens
[exploding_alien
].x
* ALIEN_SPEED
,
1030 ALIEN_START_Y
+ aliens
[exploding_alien
].y
* ALIEN_HEIGHT
,
1031 ALIEN_EXPLODE_WIDTH
, ALIEN_HEIGHT
);
1033 /* Special case. We killed curr_alien. */
1034 if (exploding_alien
== curr_alien
) {
1035 if (!next_alien()) {
1036 /* Round finished. Set curr_alien to first alive from bottom. */
1044 if (fire
== S_ACTIVE
) {
1047 rb
->lcd_vline(fire_x
, fire_y
, fire_y
+ SHOT_HEIGHT
);
1050 if (fire_y
<= SCORENUM_Y
+ FONT_HEIGHT
+ 4) {
1052 /* TODO: Play explode sound */
1055 fire_target
= TARGET_TOP
;
1056 fire_alpha(fire_x
, fire_y
, UFO_RED
);
1061 fire_y
-= FIRE_SPEED
;
1064 if (ufo_state
== S_ACTIVE
) {
1065 if ((ABS(ufo_x
+ UFO_WIDTH
/ 2 - fire_x
) <= UFO_WIDTH
/ 2) &&
1066 (fire_y
<= UFO_Y
+ UFO_HEIGHT
)) {
1067 ufo_state
= S_EXPLODE
;
1069 fire_target
= TARGET_UFO
;
1070 /* Center explosion */
1071 ufo_x
-= (UFO_EXPLODE_WIDTH
- UFO_WIDTH
) / 2;
1072 rb
->lcd_bitmap(invadrox_ufo_explode
, ufo_x
, UFO_Y
- 1,
1073 UFO_EXPLODE_WIDTH
, UFO_EXPLODE_HEIGHT
);
1078 /* Hit bomb? (check position, not pixel value) */
1079 for (i
= 0; i
< max_bombs
; i
++) {
1080 if (bombs
[i
].state
== S_ACTIVE
) {
1081 /* Count as hit if within BOMB_WIDTH pixels */
1082 if ((ABS(bombs
[i
].x
- fire_x
) < BOMB_WIDTH
) &&
1083 (fire_y
- bombs
[i
].y
< BOMB_HEIGHT
)) {
1085 rb
->lcd_fillrect(bombs
[i
].x
, bombs
[i
].y
, BOMB_WIDTH
, BOMB_HEIGHT
);
1086 bombs
[i
].state
= S_IDLE
;
1087 /* Explode ship fire */
1089 fire_target
= TARGET_SHIELD
;
1090 fire_alpha(fire_x
, fire_y
, LCD_WHITE
);
1097 for (i
= FIRE_SPEED
; i
>= 0; i
--) {
1098 pix
= get_pixel(fire_x
, fire_y
+ i
);
1099 if(pix
== screen_white
) {
1104 if(pix
== screen_green
) {
1114 /* TODO: Play explode sound */
1117 fire_target
= TARGET_SHIELD
;
1118 /* Center explosion around hit pixel */
1119 fire_y
-= FIRE_HEIGHT
/ 2;
1120 fire_alpha(fire_x
, fire_y
, SLIME_GREEN
);
1127 for (i
= 0; i
< 5 * ALIENS
; i
++) {
1128 if (aliens
[i
].state
!= DEAD
&&
1129 (ABS(fire_x
- (PLAYFIELD_X
+ LIVES_X
+ aliens
[i
].x
* ALIEN_SPEED
+
1130 ALIEN_WIDTH
/ 2)) <= ALIEN_WIDTH
/ 2) &&
1131 (ABS(fire_y
- (ALIEN_START_Y
+ aliens
[i
].y
* ALIEN_HEIGHT
+
1132 ALIEN_HEIGHT
/ 2)) <= ALIEN_HEIGHT
/ 2)) {
1134 /* TODO: play alien hit sound */
1136 if (aliens
[i
].state
== BOMBER
) {
1137 /* Set (possible) alien above to bomber */
1138 for (j
= i
- ALIENS
; j
>= 0; j
-= ALIENS
) {
1139 if (aliens
[j
].state
!= DEAD
) {
1140 /* printf("New bomber (%d, %d)\n", j % ALIENS, j / ALIENS); */
1141 aliens
[j
].state
= BOMBER
;
1146 aliens
[i
].state
= DEAD
;
1147 exploding_alien
= i
;
1148 score
+= scores
[aliens
[i
].type
];
1150 /* Update score part of screen */
1151 rb
->lcd_update_rect(SCORENUM_X
, SCORENUM_Y
,
1152 PLAYFIELD_WIDTH
- 2 * NUMBERS_WIDTH
, FONT_HEIGHT
);
1154 /* Paralyze aliens S_EXPLODE frames */
1155 aliens_paralyzed
= S_EXPLODE
;
1156 rb
->lcd_bitmap(invadrox_alien_explode
,
1157 PLAYFIELD_X
+ LIVES_X
+ aliens
[i
].x
* ALIEN_SPEED
,
1158 ALIEN_START_Y
+ aliens
[i
].y
* ALIEN_HEIGHT
,
1159 ALIEN_EXPLODE_WIDTH
, ALIEN_EXPLODE_HEIGHT
);
1160 /* Since alien is 1 pixel taller than explosion sprite, erase bottom line */
1161 rb
->lcd_hline(PLAYFIELD_X
+ LIVES_X
+ aliens
[i
].x
* ALIEN_SPEED
,
1162 PLAYFIELD_X
+ LIVES_X
+ aliens
[i
].x
* ALIEN_SPEED
+ ALIEN_WIDTH
,
1163 ALIEN_START_Y
+ (aliens
[i
].y
+ 1) * ALIEN_HEIGHT
- 1);
1170 rb
->lcd_set_foreground(LCD_WHITE
);
1171 rb
->lcd_vline(fire_x
, fire_y
, fire_y
+ SHOT_HEIGHT
);
1172 rb
->lcd_set_foreground(LCD_BLACK
);
1173 } else if (fire
< S_IDLE
) {
1174 /* Count up towards S_IDLE, then erase explosion */
1176 if (fire
== S_IDLE
) {
1177 /* Erase explosion */
1178 if (fire_target
== TARGET_TOP
) {
1179 rb
->lcd_fillrect(fire_x
- (FIRE_WIDTH
/ 2), fire_y
, FIRE_WIDTH
, FIRE_HEIGHT
);
1180 } else if (fire_target
== TARGET_SHIELD
) {
1181 /* Draw explosion with black pixels */
1182 fire_alpha(fire_x
, fire_y
, LCD_BLACK
);
1189 /* Return a BOMBER alien */
1190 static inline int random_bomber(void)
1194 /* TODO: Weigh higher probability near ship */
1195 col
= rb
->rand() % ALIENS
;
1196 for (i
= col
+ 4 * ALIENS
; i
>= 0; i
-= ALIENS
) {
1197 if (aliens
[i
].state
== BOMBER
) {
1202 /* No BOMBER found in this col */
1204 for (i
= 0; i
< 5 * ALIENS
; i
++) {
1205 if (aliens
[i
].state
== BOMBER
) {
1210 /* No BOMBER found at all (error?) */
1216 static inline void draw_bomb(int i
)
1218 rb
->lcd_bitmap_part(invadrox_bombs
, bombs
[i
].type
* BOMB_WIDTH
,
1219 bombs
[i
].frame
* BOMB_HEIGHT
,
1220 STRIDE( SCREEN_MAIN
,
1221 BMPWIDTH_invadrox_bombs
,
1222 BMPHEIGHT_invadrox_bombs
),
1223 bombs
[i
].x
, bombs
[i
].y
,
1224 BOMB_WIDTH
, BOMB_HEIGHT
);
1227 if (bombs
[i
].frame
== bombs
[i
].frames
) {
1233 void move_bombs(void)
1238 for (i
= 0; i
< max_bombs
; i
++) {
1240 switch (bombs
[i
].state
) {
1246 bomber
= random_bomber();
1248 DBG("ERROR: No bomber available\n");
1252 bombs
[i
].x
= PLAYFIELD_X
+ LIVES_X
+ aliens
[bomber
].x
* ALIEN_SPEED
+ ALIEN_WIDTH
/ 2;
1253 bombs
[i
].y
= ALIEN_START_Y
+ (aliens
[bomber
].y
+ 1) * ALIEN_HEIGHT
;
1255 /* Check for duplets in x and y direction */
1257 for (j
= i
- 1; j
>= 0; j
--) {
1258 if ((bombs
[j
].state
== S_ACTIVE
) &&
1259 ((bombs
[i
].x
== bombs
[j
].x
) || (bombs
[i
].y
== bombs
[j
].y
))) {
1265 /* Skip this one, continue with next bomb */
1266 /* printf("Bomb %d duplet of %d\n", i, j); */
1270 /* Passed, set type */
1271 bombs
[i
].type
= rb
->rand() % 3;
1273 if (bombs
[i
].type
== 0) {
1274 bombs
[i
].frames
= 3;
1275 } else if (bombs
[i
].type
== 1) {
1276 bombs
[i
].frames
= 4;
1278 bombs
[i
].frames
= 6;
1282 bombs
[i
].state
= S_ACTIVE
;
1289 /* Erase old position */
1290 rb
->lcd_fillrect(bombs
[i
].x
, bombs
[i
].y
, BOMB_WIDTH
, BOMB_HEIGHT
);
1293 bombs
[i
].y
+= BOMB_SPEED
;
1295 /* Check if bottom hit */
1296 if (bombs
[i
].y
+ BOMB_HEIGHT
>= PLAYFIELD_Y
) {
1297 bombs
[i
].y
= PLAYFIELD_Y
- FIRE_HEIGHT
+ 1;
1298 fire_alpha(bombs
[i
].x
, bombs
[i
].y
, LCD_WHITE
);
1299 bombs
[i
].state
= S_EXPLODE
;
1300 bombs
[i
].target
= TARGET_BOTTOM
;
1304 /* Check for green (ship or shield) */
1305 for (j
= BOMB_HEIGHT
; j
>= BOMB_HEIGHT
- BOMB_SPEED
; j
--) {
1306 bombs
[i
].target
= 0;
1307 if(get_pixel(bombs
[i
].x
+ BOMB_WIDTH
/ 2, bombs
[i
].y
+ j
) == screen_green
) {
1308 /* Move to hit pixel */
1309 bombs
[i
].x
+= BOMB_WIDTH
/ 2;
1312 /* Check if ship is hit */
1313 if (bombs
[i
].y
> SHIELD_Y
+ SHIELD_HEIGHT
&& bombs
[i
].y
< PLAYFIELD_Y
) {
1315 /* TODO: play ship hit sound */
1319 ship_frame_counter
= 0;
1320 bombs
[i
].state
= S_EXPLODE
* 4;
1321 bombs
[i
].target
= TARGET_SHIP
;
1322 rb
->lcd_bitmap_part(invadrox_ships
, 0, 1 * SHIP_HEIGHT
,
1323 STRIDE( SCREEN_MAIN
,
1324 BMPWIDTH_invadrox_ships
,
1325 BMPHEIGHT_invadrox_ships
),
1327 SHIP_WIDTH
, SHIP_HEIGHT
);
1331 bombs
[i
].state
= S_EXPLODE
;
1332 bombs
[i
].target
= TARGET_SHIELD
;
1333 /* Center explosion around hit pixel in shield */
1334 bombs
[i
].y
-= FIRE_HEIGHT
/ 2;
1335 fire_alpha(bombs
[i
].x
, bombs
[i
].y
, SLIME_GREEN
);
1340 if (bombs
[i
].target
!= 0) {
1341 /* Hit ship or shield, continue */
1349 /* If we get here state should be < 0, exploding */
1351 if (bombs
[i
].state
== S_IDLE
) {
1353 /* Erase explosion */
1354 rb
->lcd_fillrect(ship_x
, SHIP_Y
, SHIP_WIDTH
, SHIP_HEIGHT
);
1355 rb
->lcd_update_rect(ship_x
, SHIP_Y
, SHIP_WIDTH
, SHIP_HEIGHT
);
1358 ship_x
= PLAYFIELD_X
+ 2 * LIVES_X
;
1365 /* Sleep 1s to give player time to examine lives left */
1368 /* Erase explosion (even if ship hit, might be another bomb) */
1369 fire_alpha(bombs
[i
].x
, bombs
[i
].y
, LCD_BLACK
);
1377 static inline void move_ship(void)
1379 ship_dir
+= ship_acc
;
1380 if (ship_dir
> max_ship_speed
) {
1381 ship_dir
= max_ship_speed
;
1383 if (ship_dir
< -max_ship_speed
) {
1384 ship_dir
= -max_ship_speed
;
1387 if (ship_x
< SHIP_MIN_X
) {
1388 ship_x
= SHIP_MIN_X
;
1390 if (ship_x
> SHIP_MAX_X
) {
1391 ship_x
= SHIP_MAX_X
;
1398 /* Unidentified Flying Object */
1401 static int ufo_speed
;
1405 switch (ufo_state
) {
1409 if (rb
->rand() % 500 == 0) {
1410 /* Uh-oh, it's time to launch a mystery UFO */
1412 /* TODO: Play UFO sound */
1414 if (rb
->rand() % 2) {
1415 ufo_speed
= UFO_SPEED
;
1416 ufo_x
= PLAYFIELD_X
;
1418 ufo_speed
= -UFO_SPEED
;
1419 ufo_x
= LCD_WIDTH
- PLAYFIELD_X
- UFO_WIDTH
;
1421 ufo_state
= S_ACTIVE
;
1422 /* UFO will be drawn next frame */
1428 rb
->lcd_fillrect(ufo_x
, UFO_Y
, UFO_WIDTH
, UFO_HEIGHT
);
1432 if (ufo_x
< PLAYFIELD_X
|| ufo_x
> LCD_WIDTH
- PLAYFIELD_X
- UFO_WIDTH
) {
1437 rb
->lcd_bitmap(invadrox_ufo
, ufo_x
, UFO_Y
, UFO_WIDTH
, UFO_HEIGHT
);
1442 if (counter
== S_IDLE
) {
1443 /* Erase mystery number */
1444 rb
->lcd_fillrect(ufo_x
, UFO_Y
, 3 * NUMBERS_WIDTH
+ 2 * NUM_SPACING
, FONT_HEIGHT
);
1452 if (ufo_state
== S_IDLE
) {
1453 /* Erase explosion */
1454 rb
->lcd_fillrect(ufo_x
, UFO_Y
- 1, UFO_EXPLODE_WIDTH
, UFO_EXPLODE_HEIGHT
);
1455 ufo_state
= S_SHOWSCORE
;
1456 counter
= S_EXPLODE
* 4;
1457 /* Draw mystery_score, sleep, increase score and continue */
1458 mystery_score
= 50 + (rb
->rand() % 6) * 50;
1459 if (mystery_score
< 100) {
1460 draw_number(ufo_x
, UFO_Y
, mystery_score
, 2);
1462 draw_number(ufo_x
, UFO_Y
, mystery_score
, 3);
1464 score
+= mystery_score
;
1472 void draw_background(void)
1475 rb
->lcd_bitmap(invadrox_background
, 0, 0, LCD_WIDTH
, LCD_HEIGHT
);
1480 void new_level(void)
1485 /* Give an extra life for each new level */
1486 if (lives
< MAX_LIVES
) {
1493 draw_number(HISCORENUM_X
, SCORENUM_Y
, hiscore
.score
, 4);
1497 level_finished
= false;
1501 /* Init alien positions and states */
1502 for (i
= 0; i
< 4 * ALIENS
; i
++) {
1503 aliens
[i
].x
= 0 + (i
% ALIENS
) * ((ALIEN_WIDTH
+ ALIEN_SPACING
) / ALIEN_SPEED
);
1504 aliens
[i
].y
= 2 * (i
/ ALIENS
);
1505 aliens
[i
].state
= ALIVE
;
1507 /* Last row, bombers */
1508 for (i
= 4 * ALIENS
; i
< 5 * ALIENS
; i
++) {
1509 aliens
[i
].x
= 0 + (i
% ALIENS
) * ((ALIEN_WIDTH
+ ALIEN_SPACING
) / ALIEN_SPEED
);
1510 aliens
[i
].y
= 2 * (i
/ ALIENS
);
1511 aliens
[i
].state
= BOMBER
;
1514 /* Init bombs to inactive (S_IDLE) */
1515 for (i
= 0; i
< MAX_BOMBS
; i
++) {
1516 bombs
[i
].state
= S_IDLE
;
1519 /* Start aliens closer to earth from level 2 */
1520 for (i
= 0; i
< 5 * ALIENS
; i
++) {
1522 aliens
[i
].y
+= level
- 1;
1528 /* Max concurrent bombs */
1537 /* Increase speed */
1546 /* Increase speed more */
1556 for (i
= 1; i
<= 4; i
++) {
1557 rb
->lcd_bitmap(invadrox_shield
,
1558 PLAYFIELD_X
+ i
* PLAYFIELD_WIDTH
/ 5 - SHIELD_WIDTH
/ 2,
1559 SHIELD_Y
, SHIELD_WIDTH
, SHIELD_HEIGHT
);
1563 rb
->lcd_set_foreground(SLIME_GREEN
);
1564 rb
->lcd_hline(PLAYFIELD_X
, LCD_WIDTH
- PLAYFIELD_X
, PLAYFIELD_Y
);
1565 /* Restore foreground to black (for fast erase later). */
1566 rb
->lcd_set_foreground(LCD_BLACK
);
1568 ship_x
= PLAYFIELD_X
+ 2 * LIVES_X
;
1570 old_ship_x
= ship_x
;
1577 /* Start moving the bottom row left to right */
1578 curr_alien
= 4 * ALIENS
;
1579 aliens_paralyzed
= 0;
1580 aliens_right
= true;
1581 aliens_down
= false;
1582 hit_left_border
= false;
1583 hit_right_border
= false;
1584 /* TODO: Change max_ship_speed to 3 at higher levels */
1593 void init_invadrox(void)
1597 /* Seed random number generator with a "random" number */
1598 rb
->srand(rb
->get_time()->tm_sec
+ rb
->get_time()->tm_min
* 60);
1600 /* Precalculate start of each scanline */
1601 for (i
= 0; i
< LCD_HEIGHT
; i
++) {
1602 #if (LCD_DEPTH >= 8)
1603 ytab
[i
] = i
* LCD_WIDTH
;
1604 #elif (LCD_DEPTH == 2) && (LCD_PIXELFORMAT == HORIZONTAL_PACKING)
1605 ytab
[i
] = i
* (LCD_WIDTH
/ 4);
1606 #elif (LCD_DEPTH == 2) && (LCD_PIXELFORMAT == VERTICAL_PACKING)
1607 ytab
[i
] = (i
/ 4) * LCD_WIDTH
;
1609 #error pixelformat not implemented yet
1613 rb
->lcd_set_background(LCD_BLACK
);
1614 rb
->lcd_set_foreground(LCD_BLACK
);
1616 if (highscore_load(HISCOREFILE
, &hiscore
, 1) < 0) {
1617 /* Init hiscore to 0 */
1618 rb
->strlcpy(hiscore
.name
, "Invader", sizeof(hiscore
.name
));
1623 /* Init alien types in aliens array */
1624 for (i
= 0; i
< 1 * ALIENS
; i
++) {
1625 aliens
[i
].type
= 0; /* Kang */
1627 for (; i
< 3 * ALIENS
; i
++) {
1628 aliens
[i
].type
= 1; /* Kodos */
1630 for (; i
< 5 * ALIENS
; i
++) {
1631 aliens
[i
].type
= 2; /* Serak */
1635 /* Save screen white color */
1636 rb
->lcd_set_foreground(LCD_WHITE
);
1637 rb
->lcd_drawpixel(0, 0);
1638 rb
->lcd_update_rect(0, 0, 1, 1);
1639 screen_white
= get_pixel(0, 0);
1641 /* Save screen green color */
1642 rb
->lcd_set_foreground(SLIME_GREEN
);
1643 rb
->lcd_drawpixel(0, 0);
1644 rb
->lcd_update_rect(0, 0, 1, 1);
1645 screen_green
= get_pixel(0, 0);
1647 /* Restore black foreground */
1648 rb
->lcd_set_foreground(LCD_BLACK
);
1652 /* Flash score at start */
1653 for (i
= 0; i
< 5; i
++) {
1654 rb
->lcd_fillrect(SCORENUM_X
, SCORENUM_Y
,
1655 4 * NUMBERS_WIDTH
+ 3 * NUM_SPACING
,
1657 rb
->lcd_update_rect(SCORENUM_X
, SCORENUM_Y
,
1658 4 * NUMBERS_WIDTH
+ 3 * NUM_SPACING
,
1661 draw_number(SCORENUM_X
, SCORENUM_Y
, score
, 4);
1667 static inline bool handle_buttons(void)
1669 static unsigned int oldbuttonstate
= 0;
1671 unsigned int released
, pressed
, newbuttonstate
;
1674 /* Don't allow ship movement during explosion */
1677 newbuttonstate
= rb
->button_status();
1679 if(newbuttonstate
== oldbuttonstate
) {
1680 if (newbuttonstate
== 0) {
1681 /* No button pressed. Stop ship. */
1693 released
= ~newbuttonstate
& oldbuttonstate
;
1694 pressed
= newbuttonstate
& ~oldbuttonstate
;
1695 oldbuttonstate
= newbuttonstate
;
1697 if (pressed
& ACTION_LEFT
) {
1698 if (ship_acc
> -1) {
1702 if (pressed
& ACTION_RIGHT
) {
1707 if (pressed
& ACTION_FIRE
) {
1708 if (fire
== S_IDLE
) {
1710 fire_x
= ship_x
+ SHIP_WIDTH
/ 2;
1711 fire_y
= SHIP_Y
- SHOT_HEIGHT
;
1713 /* TODO: play fire sound */
1716 if (pressed
& ACTION_QUIT
) {
1717 rb
->splash(HZ
* 1, "Quit");
1722 if ((released
& ACTION_LEFT
)) {
1727 if ((released
& ACTION_RIGHT
)) {
1728 if (ship_acc
> -1) {
1736 /* Quit if USB is connected */
1737 if (rb
->button_get(false) == SYS_USB_CONNECTED
) {
1745 void game_loop(void)
1749 /* Print dimensions (just for debugging) */
1750 DBG("%03dx%03dx%02d\n", LCD_WIDTH
, LCD_HEIGHT
, LCD_DEPTH
);
1756 /* Convert CYCLETIME (in ms) to HZ */
1757 end
= *rb
->current_tick
+ (CYCLETIME
* HZ
) / 1000;
1759 if (handle_buttons()) {
1767 /* Check if level is finished (marked by move_fire) */
1768 if (level_finished
) {
1769 /* TODO: Play level finished sound */
1776 if (!aliens_paralyzed
&& !ship_hit
) {
1777 for (i
= 0; i
< gamespeed
; i
++) {
1778 if (!move_aliens()) {
1786 /* Move alien bombs */
1792 /* Update "playfield" rect */
1793 rb
->lcd_update_rect(PLAYFIELD_X
, SCORENUM_Y
+ FONT_HEIGHT
,
1795 PLAYFIELD_Y
+ 1 - SCORENUM_Y
- FONT_HEIGHT
);
1797 /* Wait until next frame */
1798 DBG("%ld (%d)\n", end
- *rb
->current_tick
, (CYCLETIME
* HZ
) / 1000);
1799 if (TIME_BEFORE(*rb
->current_tick
, end
)) {
1800 rb
->sleep(end
- *rb
->current_tick
);
1809 /* this is the plugin entry point */
1810 enum plugin_status
plugin_start(UNUSED
const void* parameter
)
1812 rb
->lcd_setfont(FONT_SYSFIXED
);
1813 /* Turn off backlight timeout */
1814 backlight_force_on(); /* backlight control in lib/helper.c */
1816 /* now go ahead and have fun! */
1820 /* TODO: Play game over sound */
1821 rb
->splash(HZ
* 2, "Game Over");
1822 if (score
> hiscore
.score
) {
1823 /* Save new hiscore */
1824 highscore_update(score
, level
, "Invader", &hiscore
, 1);
1825 highscore_save(HISCOREFILE
, &hiscore
, 1);
1828 /* Restore user's original backlight setting */
1829 rb
->lcd_setfont(FONT_UI
);
1830 /* Turn on backlight timeout (revert to settings) */
1831 backlight_use_settings(); /* backlight control in lib/helper.c */
1839 * GNU Emacs settings: Kernighan & Richie coding style with
1840 * 4 spaces indent and no tabs.
1842 * c-file-style: "k&r"
1844 * indent-tabs-mode: nil