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"
34 /* Original graphics is only 1bpp so it should be portable
35 * to most targets. But for now, only support the simple ones.
37 #ifndef HAVE_LCD_BITMAP
38 #error INVADROX: Unsupported LCD
42 #error INVADROX: Unsupported LCD
47 #define DBG(format, arg...) { DEBUGF("%s: " format, __FUNCTION__, ## arg); }
49 #define DBG(format, arg...) {}
52 #if CONFIG_KEYPAD == IRIVER_H100_PAD
54 #define QUIT BUTTON_OFF
55 #define LEFT BUTTON_LEFT
56 #define RIGHT BUTTON_RIGHT
57 #define FIRE BUTTON_ON
59 #elif CONFIG_KEYPAD == IRIVER_H300_PAD
61 #define QUIT BUTTON_OFF
62 #define LEFT BUTTON_LEFT
63 #define RIGHT BUTTON_RIGHT
64 #define FIRE BUTTON_SELECT
66 #elif (CONFIG_KEYPAD == IRIVER_H10_PAD)
68 #define QUIT BUTTON_POWER
69 #define LEFT BUTTON_LEFT
70 #define RIGHT BUTTON_RIGHT
71 #define FIRE BUTTON_PLAY
73 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
74 (CONFIG_KEYPAD == IPOD_3G_PAD) || \
75 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
77 #define QUIT BUTTON_MENU
78 #define LEFT BUTTON_LEFT
79 #define RIGHT BUTTON_RIGHT
80 #define FIRE BUTTON_SELECT
82 #elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD
84 #define QUIT BUTTON_POWER
85 #define LEFT BUTTON_LEFT
86 #define RIGHT BUTTON_RIGHT
87 #define FIRE BUTTON_SELECT
89 #elif CONFIG_KEYPAD == GIGABEAT_PAD
91 #define QUIT BUTTON_POWER
92 #define LEFT BUTTON_LEFT
93 #define RIGHT BUTTON_RIGHT
94 #define FIRE BUTTON_SELECT
96 #elif CONFIG_KEYPAD == SANSA_E200_PAD || \
97 CONFIG_KEYPAD == SANSA_FUZE_PAD
99 #define QUIT BUTTON_POWER
100 #define LEFT BUTTON_LEFT
101 #define RIGHT BUTTON_RIGHT
102 #define FIRE BUTTON_SELECT
104 #elif CONFIG_KEYPAD == ELIO_TPJ1022_PAD
106 /* TODO: Figure out which buttons to use for Tatung Elio TPJ-1022 */
107 #define QUIT BUTTON_AB
108 #define LEFT BUTTON_LEFT
109 #define RIGHT BUTTON_RIGHT
110 #define FIRE BUTTON_MENU
112 #elif CONFIG_KEYPAD == GIGABEAT_S_PAD
114 #define QUIT BUTTON_BACK
115 #define LEFT BUTTON_LEFT
116 #define RIGHT BUTTON_RIGHT
117 #define FIRE BUTTON_SELECT
119 #elif CONFIG_KEYPAD == COWOND2_PAD
121 #define QUIT BUTTON_POWER
123 #elif CONFIG_KEYPAD == IAUDIO67_PAD
125 #define QUIT BUTTON_POWER
126 #define LEFT BUTTON_LEFT
127 #define RIGHT BUTTON_RIGHT
128 #define FIRE BUTTON_PLAY
130 #elif CONFIG_KEYPAD == CREATIVEZVM_PAD
132 #define QUIT BUTTON_BACK
133 #define LEFT BUTTON_LEFT
134 #define RIGHT BUTTON_RIGHT
135 #define FIRE BUTTON_SELECT
138 #error INVADROX: Unsupported keypad
141 #ifdef HAVE_TOUCHSCREEN
143 #define QUIT BUTTON_TOPLEFT
146 #define LEFT BUTTON_MIDLEFT
149 #define RIGHT BUTTON_MIDRIGHT
152 #define FIRE BUTTON_CENTER
157 #define UNUSED __attribute__ ((unused))
161 #define ABS(x) (((x) < 0) ? (-(x)) : (x))
165 /* Defines common to all models */
166 #define UFO_Y (SCORENUM_Y + FONT_HEIGHT + ALIEN_HEIGHT)
167 #define PLAYFIELD_Y (LCD_HEIGHT - SHIP_HEIGHT - 2)
168 #define PLAYFIELD_WIDTH (LCD_WIDTH - 2 * PLAYFIELD_X)
169 #define LEVEL_X (LCD_WIDTH - PLAYFIELD_X - LIVES_X - LEVEL_WIDTH - 2 * NUMBERS_WIDTH - 3 * NUM_SPACING)
170 #define SHIP_MIN_X (PLAYFIELD_X + PLAYFIELD_WIDTH / 5 - SHIELD_WIDTH / 2 - SHIP_WIDTH)
171 #define SHIP_MAX_X (PLAYFIELD_X + 4 * PLAYFIELD_WIDTH / 5 + SHIELD_WIDTH / 2)
172 /* SCORE_Y = 0 for most targets. Gigabeat redefines it later. */
177 /* iPod Video defines */
178 #if (LCD_WIDTH == 320) && (LCD_HEIGHT == 240)
180 /* Original arcade game size 224x240, 1bpp with
181 * red overlay at top and green overlay at bottom.
183 * iPod Video: 320x240x16
184 * ======================
185 * X: 48p padding at left/right gives 224p playfield in middle.
186 * 10p "border" gives 204p actual playfield. UFO use full 224p.
189 * MAX_X = (204 - 12) / 2 - 1 = 95
196 * 2 Space Aliens start at 32 + 3 * 8 = 56
198 * space ~7*8 128 | 18.75 aliens space between
199 * shield 2*8 182 | first alien and ship.
200 * space 8 198 | MAX_Y = 18
203 * hline 1 230 - PLAYFIELD_Y
204 * bottom border 10 240
205 * Lives and Level goes inside bottom border
208 #define ARCADISH_GRAPHICS
209 #define PLAYFIELD_X 48
210 #define SHIP_Y (PLAYFIELD_Y - 3 * SHIP_HEIGHT)
211 #define ALIEN_START_Y (UFO_Y + 3 * ALIEN_HEIGHT)
212 #define SCORENUM_X (PLAYFIELD_X + NUMBERS_WIDTH)
213 #define SCORENUM_Y SCORE_Y + (2 * (FONT_HEIGHT + 1) + 1)
214 #define HISCORE_X (LCD_WIDTH - PLAYFIELD_X - HISCORE_WIDTH)
215 #define HISCORENUM_X (LCD_WIDTH - PLAYFIELD_X - 1 - 6 * NUMBERS_WIDTH - 5 * NUM_SPACING)
216 #define SHIELD_Y (PLAYFIELD_Y - 6 * SHIP_HEIGHT)
222 #elif (LCD_WIDTH == 176) && (LCD_HEIGHT == 220)
224 /* Sandisk Sansa e200: 176x220x16
225 * ==============================
226 * X: No padding. 8p border -> 160p playfield.
228 * 8p Aliens with 3p spacing -> 88 + 30 = 118p aliens block.
229 * (160 - 118) / 2 = 21 rounds for whole block (more than original)
230 * MAX_X = (160 - 8) / 2 - 1 = 75 rounds for single alien (less than original)
240 * space ~7*5 155 | 18.6 aliens space between
241 * shield 2*5 188 | first alien and ship.
242 * space 5 198 | MAX_Y = 18
245 * hline 1 213 PLAYFIELD_Y
248 * Lives and Level goes inside bottom border
251 #define TINY_GRAPHICS
252 #define PLAYFIELD_X 0
253 #define SHIP_Y (PLAYFIELD_Y - 2 * SHIP_HEIGHT)
254 #define SHIELD_Y (SHIP_Y - SHIP_HEIGHT - SHIELD_HEIGHT)
255 #define ALIEN_START_Y (UFO_Y + 3 * SHIP_HEIGHT)
256 /* Redefine SCORE_Y */
259 #define SCORENUM_X (PLAYFIELD_X + NUMBERS_WIDTH)
260 #define SCORENUM_Y (SCORE_Y + 2 * FONT_HEIGHT)
261 #define HISCORE_X (LCD_WIDTH - PLAYFIELD_X - HISCORE_WIDTH)
262 #define HISCORENUM_X (LCD_WIDTH - PLAYFIELD_X - 1 - 6 * NUMBERS_WIDTH - 5 * NUM_SPACING)
268 #elif (LCD_WIDTH == 176) && (LCD_HEIGHT == 132)
270 /* iPod Nano: 176x132x16
271 * ======================
272 * X: No padding. 8p border -> 160p playfield.
287 * ALIEN_START_Y (UFO_Y + 12)
289 * 8p Aliens with 3p spacing -> 88 + 30 = 118p aliens block.
290 * (160 - 118) / 2 = 21 rounds for whole block (more than original)
291 * MAX_X = (160 - 8) / 2 - 1 = 75 rounds for single alien (less than original)
293 * Y: Scoreline 5 0 (combine scoretext and numbers on same line)
298 * space ~7*5 67 | Just above 18 aliens space between
299 * shield 2*5 100 | first alien and ship.
300 * space 5 110 | MAX_Y = 18
303 * hline 1 125 PLAYFIELD_Y
304 * bottom border 6 126
306 * Lives and Level goes inside bottom border
309 #define TINY_GRAPHICS
310 #define PLAYFIELD_X 0
311 #define SHIP_Y (PLAYFIELD_Y - 2 * SHIP_HEIGHT)
312 #define ALIEN_START_Y (UFO_Y + 12)
313 #define SCORENUM_X (PLAYFIELD_X + SCORE_WIDTH + NUMBERS_WIDTH + NUM_SPACING)
314 #define SCORENUM_Y SCORE_Y
315 #define HISCORENUM_X (LCD_WIDTH - PLAYFIELD_X - 4 * NUMBERS_WIDTH - 3 * NUM_SPACING)
316 #define HISCORE_X (HISCORENUM_X - NUMBERS_WIDTH - NUM_SPACING - HISCORE_WIDTH)
317 #define SHIELD_Y (SHIP_Y - SHIP_HEIGHT - SHIELD_HEIGHT)
322 #elif (LCD_WIDTH == 160) && (LCD_HEIGHT == 128)
324 /* iAudio X5, iRiver H10 20Gb, iPod 3g/4g: 160x128x16
325 * ======================================
326 * X: No padding. No border -> 160p playfield.
341 * ALIEN_START_Y (UFO_Y + 10)
343 * 8p Aliens with 3p spacing -> 88 + 30 = 118p aliens block.
344 * (160 - 118) / 2 = 21 rounds for whole block (more than original)
345 * MAX_X = (160 - 8) / 2 - 1 = 75 rounds for single alien (less than original)
347 * Y: Scoreline 5 0 (combine scoretext and numbers on same line)
352 * space ~6*5 65 | Just above 18 aliens space between
353 * shield 2*5 96 | first alien and ship.
354 * space 5 106 | MAX_Y = 18
357 * hline 1 121 PLAYFIELD_Y
358 * bottom border 6 122
360 * Lives and Level goes inside bottom border
363 #define TINY_GRAPHICS
364 #define PLAYFIELD_X 0
365 #define SHIP_Y (PLAYFIELD_Y - 2 * SHIP_HEIGHT)
366 #define ALIEN_START_Y (UFO_Y + 10)
367 #define SCORENUM_X (PLAYFIELD_X + SCORE_WIDTH + NUMBERS_WIDTH + NUM_SPACING)
368 #define SCORENUM_Y SCORE_Y
369 #define HISCORENUM_X (LCD_WIDTH - PLAYFIELD_X - 4 * NUMBERS_WIDTH - 3 * NUM_SPACING)
370 #define HISCORE_X (HISCORENUM_X - NUMBERS_WIDTH - NUM_SPACING - HISCORE_WIDTH)
371 #define SHIELD_Y (SHIP_Y - SHIP_HEIGHT - SHIELD_HEIGHT)
377 #elif (LCD_WIDTH == 240) && (LCD_HEIGHT == 320)
379 /* Gigabeat: 240x320x16
380 * ======================
381 * X: 8p padding at left/right gives 224p playfield in middle.
382 * 10p "border" gives 204p actual playfield. UFO use full 224p.
383 * Y: Use bottom 240p for playfield and top 80 pixels for logo.
385 * MAX_X = (204 - 12) / 2 - 1 = 95
387 * Y: Score text 7 0 + 80
392 * 2 Space Aliens start at 32 + 3 * 8 = 56
394 * space ~7*8 128 | 18.75 aliens space between
395 * shield 2*8 182 | first alien and ship.
396 * space 8 198 | MAX_Y = 18
399 * hline 1 230 310 - PLAYFIELD_Y
400 * bottom border 10 240 320
401 * Lives and Level goes inside bottom border
404 #define ARCADISH_GRAPHICS
405 #define PLAYFIELD_X 8
406 #define SHIP_Y (PLAYFIELD_Y - 3 * SHIP_HEIGHT)
407 #define ALIEN_START_Y (UFO_Y + 3 * ALIEN_HEIGHT)
408 /* Redefine SCORE_Y */
411 #define SCORENUM_X (PLAYFIELD_X + NUMBERS_WIDTH)
412 #define SCORENUM_Y SCORE_Y + (2 * (FONT_HEIGHT + 1) + 1)
413 #define HISCORE_X (LCD_WIDTH - PLAYFIELD_X - HISCORE_WIDTH)
414 #define HISCORENUM_X (LCD_WIDTH - PLAYFIELD_X - 1 - 6 * NUMBERS_WIDTH - 5 * NUM_SPACING)
415 #define SHIELD_Y (PLAYFIELD_Y - 6 * SHIP_HEIGHT)
420 #elif (LCD_WIDTH == 220) && (LCD_HEIGHT == 176)
422 /* TPJ1022, H300, iPod Color: 220x176x16
423 * ============================
424 * X: 0p padding at left/right gives 220p playfield in middle.
425 * 8p "border" gives 204p actual playfield. UFO use full 220p.
426 * Y: Use full 176p for playfield.
428 * MAX_X = (204 - 12) / 2 - 1 = 95
433 * 7 Space Aliens start at 15 + 3 * 8 = 56
435 * space ~7*8 103 | 15.6 aliens space between
436 * shield 2*8 126 | first alien and ship.
437 * space 8 142 | MAX_Y = 15
440 * hline 1 166 - PLAYFIELD_Y
441 * bottom border 10 176
442 * Lives and Level goes inside bottom border
445 #define ARCADISH_GRAPHICS
446 #define PLAYFIELD_X 0
447 #define SHIP_Y (PLAYFIELD_Y - 2 * SHIP_HEIGHT)
448 #define ALIEN_START_Y (UFO_Y + 10)
449 #define SCORENUM_Y SCORE_Y
450 #define SCORENUM_X (PLAYFIELD_X + SCORE_WIDTH + NUMBERS_WIDTH + NUM_SPACING)
451 #define HISCORENUM_X (LCD_WIDTH - PLAYFIELD_X - 4 * NUMBERS_WIDTH - 3 * NUM_SPACING)
452 #define HISCORE_X (HISCORENUM_X - NUMBERS_WIDTH - NUM_SPACING - HISCORE_WIDTH)
453 #define SHIELD_Y (PLAYFIELD_Y - 5 * SHIP_HEIGHT)
459 #error INVADROX: Unsupported LCD type
463 /* Defines common to each "graphic type" */
464 #ifdef ARCADISH_GRAPHICS
467 #define SHIP_SRC_X 24
468 #define SHIP_WIDTH 16
469 #define SHIP_HEIGHT 8
470 #define SHOT_HEIGHT 5
471 #define ALIEN_WIDTH 12
472 #define ALIEN_EXPLODE_SRC_X 52
473 #define ALIEN_EXPLODE_SRC_Y 39
474 #define ALIEN_EXPLODE_WIDTH 13
475 #define ALIEN_EXPLODE_HEIGHT 7
476 #define ALIEN_HEIGHT 8
477 #define ALIEN_SPACING 4
478 #define ALIEN_SPEED 2
482 #define UFO_EXPLODE_WIDTH 21
483 #define UFO_EXPLODE_HEIGHT 8
485 #define FONT_HEIGHT 7
486 #define LEVEL_SRC_Y 24
487 #define LEVEL_WIDTH 37
488 #define SCORE_SRC_X 24
489 #define SCORE_SRC_Y 31
490 #define SCORE_WIDTH 37
491 #define HISCORE_WIDTH 61
492 #define NUM_SPACING 3
493 #define NUMBERS_SRC_Y 38
494 #define NUMBERS_WIDTH 5
495 #define SHIELD_SRC_X 40
496 #define SHIELD_SRC_Y 15
497 #define SHIELD_WIDTH 22
498 #define SHIELD_HEIGHT 16
500 #define FIRE_HEIGHT 8
502 #define BOMB_SRC_X 62
504 #define BOMB_HEIGHT 7
507 unsigned char fire_sprite
[FIRE_HEIGHT
] = {
508 (1 << 7) | (0 << 6) | (0 << 5) | (0 << 4) | (1 << 3) | (0 << 2) | (0 << 1) | 1,
509 (0 << 7) | (0 << 6) | (1 << 5) | (0 << 4) | (0 << 3) | (0 << 2) | (1 << 1) | 0,
510 (0 << 7) | (1 << 6) | (1 << 5) | (1 << 4) | (1 << 3) | (1 << 2) | (1 << 1) | 0,
511 (1 << 7) | (1 << 6) | (1 << 5) | (1 << 4) | (1 << 3) | (1 << 2) | (1 << 1) | 1,
512 (0 << 7) | (1 << 6) | (1 << 5) | (1 << 4) | (1 << 3) | (1 << 2) | (1 << 1) | 1,
513 (0 << 7) | (1 << 6) | (1 << 5) | (1 << 4) | (1 << 3) | (1 << 2) | (1 << 1) | 0,
514 (0 << 7) | (0 << 6) | (1 << 5) | (0 << 4) | (0 << 3) | (1 << 2) | (0 << 1) | 0,
515 (1 << 7) | (0 << 6) | (0 << 5) | (1 << 4) | (0 << 3) | (0 << 2) | (0 << 1) | 1
518 #elif defined TINY_GRAPHICS
521 #define SHIP_SRC_X 16
522 #define SHIP_WIDTH 10
523 #define SHIP_HEIGHT 5
524 #define SHOT_HEIGHT 4
525 #define ALIEN_WIDTH 8
526 #define ALIEN_HEIGHT 5
527 #define ALIEN_EXPLODE_SRC_X 40
528 #define ALIEN_EXPLODE_SRC_Y 26
529 #define ALIEN_EXPLODE_WIDTH 10
530 #define ALIEN_EXPLODE_HEIGHT 5
531 #define ALIEN_SPACING 3
532 #define ALIEN_SPEED 2
536 #define UFO_EXPLODE_WIDTH 14
537 #define UFO_EXPLODE_HEIGHT 5
539 #define FONT_HEIGHT 5
540 #define LEVEL_SRC_Y 15
541 #define LEVEL_WIDTH 29
542 #define NUMBERS_WIDTH 4
543 #define NUM_SPACING 2
544 #define SCORE_SRC_X 17
545 #define SCORE_SRC_Y 20
546 #define SCORE_WIDTH 28
547 #define HISCORE_WIDTH 45
548 #define NUMBERS_SRC_Y 25
549 #define SHIELD_SRC_X 29
550 #define SHIELD_SRC_Y 10
551 #define SHIELD_WIDTH 15
552 #define SHIELD_HEIGHT 10
554 #define FIRE_HEIGHT 6
556 #define BOMB_SRC_X 44
558 #define BOMB_HEIGHT 5
561 unsigned char fire_sprite
[FIRE_HEIGHT
] = {
562 (1 << 5) | (0 << 4) | (0 << 3) | (1 << 2) | (0 << 1) | 1,
563 (0 << 5) | (1 << 4) | (0 << 3) | (0 << 2) | (0 << 1) | 0,
564 (0 << 5) | (1 << 4) | (1 << 3) | (1 << 2) | (1 << 1) | 0,
565 (0 << 5) | (1 << 4) | (1 << 3) | (1 << 2) | (1 << 1) | 1,
566 (0 << 5) | (1 << 4) | (0 << 3) | (0 << 2) | (1 << 1) | 0,
567 (1 << 5) | (0 << 4) | (1 << 3) | (0 << 2) | (0 << 1) | 1
571 #error Graphic type not defined
577 #define SLIME_GREEN LCD_RGBPACK(31, 254, 31)
578 #define UFO_RED LCD_RGBPACK(254, 31, 31)
579 #elif (LCD_DEPTH == 2)
580 #define SLIME_GREEN LCD_LIGHTGRAY
581 #define UFO_RED LCD_LIGHTGRAY
583 #error LCD type not implemented yet
591 /* Fire/bomb/ufo states */
594 #define S_SHOWSCORE 2
597 /* Fire/bomb targets */
599 #define TARGET_SHIELD 1
600 #define TARGET_SHIP 2
601 #define TARGET_BOTTOM 3
604 #define HISCOREFILE PLUGIN_GAMES_DIR "/invadrox.high"
607 /* The time (in ms) for one iteration through the game loop - decrease this
608 * to speed up the game - note that current_tick is (currently) only accurate
614 /* Physical x is at PLAYFIELD_X + LIVES_X + x * ALIEN_SPEED
615 * Physical y is at y * ALIEN_HEIGHT
618 unsigned char x
; /* x-coordinate (0 - 95) */
619 unsigned char y
; /* y-coordinate (0 - 18) */
620 unsigned char type
; /* 0 (Kang), 1 (Kodos), 2 (Serak) */
621 unsigned char state
; /* Dead, alive or bomber */
624 /* Aliens box 5 rows * ALIENS aliens in each row */
625 struct alien aliens
[5 * ALIENS
];
631 unsigned char frame
; /* Current animation frame */
632 unsigned char frames
; /* Number of frames in animation */
633 unsigned char target
; /* Remember target during explosion frames */
634 int state
; /* 0 (IDLE) = inactive, 1 (FIRE) or negative, exploding */
636 struct bomb bombs
[MAX_BOMBS
];
637 /* Increase max_bombs at higher levels */
640 /* Raw framebuffer value of shield/ship green color */
641 fb_data screen_green
, screen_white
;
643 /* For optimization, precalculate startoffset of each scanline */
644 unsigned int ytab
[LCD_HEIGHT
];
646 /* external bitmaps */
647 extern const fb_data invadrox
[];
648 #if (LCD_WIDTH == 320) && (LCD_HEIGHT == 240)
649 /* iPod Video only */
650 extern const fb_data invadrox_left
[];
651 extern const fb_data invadrox_right
[];
653 #if ((LCD_WIDTH == 240) && (LCD_HEIGHT == 320)) || ((LCD_WIDTH == 176) && (LCD_HEIGHT == 220))
654 /* Gigabeat F, Sansa e200 */
655 extern const fb_data invadrox_logo
[];
661 int scores
[3] = { 30, 20, 10 };
663 struct highscore hiscore
;
664 bool game_over
= false;
665 int ship_x
, old_ship_x
, ship_dir
, ship_acc
, max_ship_speed
;
666 int ship_frame
, ship_frame_counter
;
668 int fire
, fire_target
, fire_x
, fire_y
;
669 int curr_alien
, aliens_paralyzed
, gamespeed
;
670 int ufo_state
, ufo_x
;
672 bool aliens_down
, aliens_right
, hit_left_border
, hit_right_border
;
675 /* No standard get_pixel function yet, use this hack instead */
678 inline fb_data
get_pixel(int x
, int y
)
680 return rb
->lcd_framebuffer
[ytab
[y
] + x
];
683 #elif (LCD_DEPTH == 2)
685 #if (LCD_PIXELFORMAT == HORIZONTAL_PACKING)
686 static const unsigned char shifts
[4] = {
689 /* Horizontal packing */
690 inline fb_data
get_pixel(int x
, int y
)
692 return (rb
->lcd_framebuffer
[ytab
[y
] + (x
>> 2)] >> shifts
[x
& 3]) & 3;
695 /* Vertical packing */
696 static const unsigned char shifts
[4] = {
699 inline fb_data
get_pixel(int x
, int y
)
701 return (rb
->lcd_framebuffer
[ytab
[y
] + x
] >> shifts
[y
& 3]) & 3;
703 #endif /* Horizontal/Vertical packing */
706 #error get_pixel: pixelformat not implemented yet
710 /* Draw "digits" least significant digits of num at (x,y) */
711 void draw_number(int x
, int y
, int num
, int digits
)
716 for (i
= digits
- 1; i
>= 0; i
--) {
719 rb
->lcd_bitmap_part(invadrox
, d
* NUMBERS_WIDTH
, NUMBERS_SRC_Y
,
720 STRIDE
, x
+ i
* (NUMBERS_WIDTH
+ NUM_SPACING
), y
,
721 NUMBERS_WIDTH
, FONT_HEIGHT
);
724 rb
->lcd_update_rect(x
, y
, 4 * NUMBERS_WIDTH
+ 3 * NUM_SPACING
, FONT_HEIGHT
);
728 inline void draw_score(void)
730 draw_number(SCORENUM_X
, SCORENUM_Y
, score
, 4);
731 if (score
> hiscore
.score
) {
732 /* Draw new hiscore (same as score) */
733 draw_number(HISCORENUM_X
, SCORENUM_Y
, score
, 4);
738 void draw_level(void)
740 rb
->lcd_bitmap_part(invadrox
, 0, LEVEL_SRC_Y
,
741 STRIDE
, LEVEL_X
, PLAYFIELD_Y
+ 2,
742 LEVEL_WIDTH
, FONT_HEIGHT
);
743 draw_number(LEVEL_X
+ LEVEL_WIDTH
+ 2 * NUM_SPACING
, PLAYFIELD_Y
+ 2, level
, 2);
747 void draw_lives(void)
751 rb
->lcd_bitmap_part(invadrox
, lives
* NUMBERS_WIDTH
, NUMBERS_SRC_Y
,
752 STRIDE
, PLAYFIELD_X
+ LIVES_X
, PLAYFIELD_Y
+ 2,
753 NUMBERS_WIDTH
, FONT_HEIGHT
);
756 for (i
= 0; i
< (lives
- 1); i
++) {
757 rb
->lcd_bitmap_part(invadrox
, SHIP_SRC_X
, 0, STRIDE
,
758 PLAYFIELD_X
+ LIVES_X
+ SHIP_WIDTH
+ i
* (SHIP_WIDTH
+ NUM_SPACING
),
759 PLAYFIELD_Y
+ 1, SHIP_WIDTH
, SHIP_HEIGHT
);
762 /* Erase ship to the righ (if less than MAX_LIVES) */
763 if (lives
< MAX_LIVES
) {
764 rb
->lcd_fillrect(PLAYFIELD_X
+ LIVES_X
+ SHIP_WIDTH
+ i
* (SHIP_WIDTH
+ NUM_SPACING
),
765 PLAYFIELD_Y
+ 1, SHIP_WIDTH
, SHIP_HEIGHT
);
767 /* Update lives (and level) part of screen */
768 rb
->lcd_update_rect(PLAYFIELD_X
+ LIVES_X
, PLAYFIELD_Y
+ 1,
769 PLAYFIELD_WIDTH
- 2 * LIVES_X
, MAX(FONT_HEIGHT
+ 1, SHIP_HEIGHT
+ 1));
773 inline void draw_aliens(void)
777 for (i
= 0; i
< 5 * ALIENS
; i
++) {
778 rb
->lcd_bitmap_part(invadrox
, aliens
[i
].x
& 1 ? ALIEN_WIDTH
: 0, aliens
[i
].type
* ALIEN_HEIGHT
,
779 STRIDE
, PLAYFIELD_X
+ LIVES_X
+ aliens
[i
].x
* ALIEN_SPEED
,
780 ALIEN_START_Y
+ aliens
[i
].y
* ALIEN_HEIGHT
,
781 ALIEN_WIDTH
, ALIEN_HEIGHT
);
786 /* Return false if there is no next alive alien (round is over) */
787 inline bool next_alien(void)
793 if (curr_alien
% ALIENS
== 0) {
794 /* End of this row. Move up one row. */
795 curr_alien
-= 2 * ALIENS
;
796 if (curr_alien
< 0) {
797 /* No more aliens in this round. */
798 curr_alien
= 4 * ALIENS
;
802 } while (aliens
[curr_alien
].state
== DEAD
&& ret
);
805 /* No more alive aliens. Round finished. */
806 if (hit_right_border
) {
807 if (hit_left_border
) {
808 DBG("ERROR: both left and right borders are set (%d)\n", curr_alien
);
810 /* Move down-left next round */
811 aliens_right
= false;
813 hit_right_border
= false;
814 } else if (hit_left_border
) {
815 /* Move down-right next round */
818 hit_left_border
= false;
820 /* Not left nor right. Set down to false. */
829 /* All aliens have been moved.
830 * Set curr_alien to first alive.
831 * Return false if no-one is left alive.
833 bool first_alien(void)
837 for (y
= 4; y
>= 0; y
--) {
838 for (i
= y
* ALIENS
; i
< (y
+ 1) * ALIENS
; i
++) {
839 if (aliens
[i
].state
!= DEAD
) {
846 /* All aliens dead. */
847 level_finished
= true;
853 bool move_aliens(void)
855 int x
, y
, old_x
, old_y
;
857 /* Move current alien (curr_alien is pointing to a living alien) */
859 old_x
= aliens
[curr_alien
].x
;
860 old_y
= aliens
[curr_alien
].y
;
863 aliens
[curr_alien
].y
++;
864 if (aliens
[curr_alien
].y
== MAX_Y
) {
865 /* Alien is at bottom. Game Over. */
866 DBG("Alien %d is at bottom. Game Over.\n", curr_alien
);
874 if (aliens
[curr_alien
].x
< MAX_X
) {
875 aliens
[curr_alien
].x
++;
878 /* Now, after move, check if we hit the right border. */
879 if (aliens
[curr_alien
].x
== MAX_X
) {
880 hit_right_border
= true;
885 if (aliens
[curr_alien
].x
> 0) {
886 aliens
[curr_alien
].x
--;
889 /* Now, after move, check if we hit the left border. */
890 if (aliens
[curr_alien
].x
== 0) {
891 hit_left_border
= true;
895 /* Erase old position */
896 x
= PLAYFIELD_X
+ LIVES_X
+ old_x
* ALIEN_SPEED
;
897 y
= ALIEN_START_Y
+ old_y
* ALIEN_HEIGHT
;
898 if (aliens
[curr_alien
].y
!= old_y
) {
899 /* Moved in y-dir. Erase whole alien. */
900 rb
->lcd_fillrect(x
, y
, ALIEN_WIDTH
, ALIEN_HEIGHT
);
903 /* Erase left edge */
904 rb
->lcd_fillrect(x
, y
, ALIEN_SPEED
, ALIEN_HEIGHT
);
906 /* Erase right edge */
907 x
+= ALIEN_WIDTH
- ALIEN_SPEED
;
908 rb
->lcd_fillrect(x
, y
, ALIEN_SPEED
, ALIEN_HEIGHT
);
912 /* Draw alien at new pos */
913 x
= PLAYFIELD_X
+ LIVES_X
+ aliens
[curr_alien
].x
* ALIEN_SPEED
;
914 y
= ALIEN_START_Y
+ aliens
[curr_alien
].y
* ALIEN_HEIGHT
;
915 rb
->lcd_bitmap_part(invadrox
,
916 aliens
[curr_alien
].x
& 1 ? ALIEN_WIDTH
: 0, aliens
[curr_alien
].type
* ALIEN_HEIGHT
,
917 STRIDE
, x
, y
, ALIEN_WIDTH
, ALIEN_HEIGHT
);
920 /* Round finished. Set curr_alien to first alive from bottom. */
921 if (!first_alien()) {
922 /* Should never happen. Taken care of in move_fire(). */
925 /* TODO: Play next background sound */
932 inline void draw_ship(void)
935 if (old_ship_x
< ship_x
) {
936 /* Move right. Erase leftmost part of ship. */
937 rb
->lcd_fillrect(old_ship_x
, SHIP_Y
, ship_x
- old_ship_x
, SHIP_HEIGHT
);
938 } else if (old_ship_x
> ship_x
) {
939 /* Move left. Erase rightmost part of ship. */
940 rb
->lcd_fillrect(ship_x
+ SHIP_WIDTH
, SHIP_Y
, old_ship_x
- ship_x
, SHIP_HEIGHT
);
944 rb
->lcd_bitmap_part(invadrox
, SHIP_SRC_X
, ship_frame
* SHIP_HEIGHT
,
945 STRIDE
, ship_x
, SHIP_Y
, SHIP_WIDTH
, SHIP_HEIGHT
);
947 /* Alternate between frame 1 and 2 during hit */
948 ship_frame_counter
++;
949 if (ship_frame_counter
> 2) {
950 ship_frame_counter
= 0;
952 if (ship_frame
> 2) {
958 /* Save ship_x for next time */
963 inline void fire_alpha(int xc
, int yc
, fb_data color
)
968 rb
->lcd_set_foreground(color
);
970 for (y
= 0; y
< FIRE_HEIGHT
; y
++) {
971 mask
= 1 << (FIRE_WIDTH
- 1);
972 for (x
= -(FIRE_WIDTH
/ 2); x
< (FIRE_WIDTH
/ 2); x
++) {
973 if (fire_sprite
[y
] & mask
) {
974 rb
->lcd_drawpixel(xc
+ x
, yc
+ y
);
980 rb
->lcd_set_foreground(LCD_BLACK
);
986 bool hit_green
= false;
987 bool hit_white
= false;
989 static int exploding_alien
= -1;
992 if (fire
== S_IDLE
) {
996 /* Alien hit. Wait until explosion is finished. */
997 if (aliens_paralyzed
< 0) {
999 if (aliens_paralyzed
== 0) {
1000 /* Erase exploding_alien */
1001 rb
->lcd_fillrect(PLAYFIELD_X
+ LIVES_X
+ aliens
[exploding_alien
].x
* ALIEN_SPEED
,
1002 ALIEN_START_Y
+ aliens
[exploding_alien
].y
* ALIEN_HEIGHT
,
1003 ALIEN_EXPLODE_WIDTH
, ALIEN_HEIGHT
);
1005 /* Special case. We killed curr_alien. */
1006 if (exploding_alien
== curr_alien
) {
1007 if (!next_alien()) {
1008 /* Round finished. Set curr_alien to first alive from bottom. */
1016 if (fire
== S_ACTIVE
) {
1019 rb
->lcd_vline(fire_x
, fire_y
, fire_y
+ SHOT_HEIGHT
);
1022 if (fire_y
<= SCORENUM_Y
+ FONT_HEIGHT
+ 4) {
1024 /* TODO: Play explode sound */
1027 fire_target
= TARGET_TOP
;
1028 fire_alpha(fire_x
, fire_y
, UFO_RED
);
1033 fire_y
-= FIRE_SPEED
;
1036 if (ufo_state
== S_ACTIVE
) {
1037 if ((ABS(ufo_x
+ UFO_WIDTH
/ 2 - fire_x
) <= UFO_WIDTH
/ 2) &&
1038 (fire_y
<= UFO_Y
+ UFO_HEIGHT
)) {
1039 ufo_state
= S_EXPLODE
;
1041 fire_target
= TARGET_UFO
;
1042 /* Center explosion */
1043 ufo_x
-= (UFO_EXPLODE_WIDTH
- UFO_WIDTH
) / 2;
1044 rb
->lcd_bitmap_part(invadrox
, UFO_SRC_X
, UFO_HEIGHT
,
1045 STRIDE
, ufo_x
, UFO_Y
- 1, UFO_EXPLODE_WIDTH
, UFO_EXPLODE_HEIGHT
);
1050 /* Hit bomb? (check position, not pixel value) */
1051 for (i
= 0; i
< max_bombs
; i
++) {
1052 if (bombs
[i
].state
== S_ACTIVE
) {
1053 /* Count as hit if within BOMB_WIDTH pixels */
1054 if ((ABS(bombs
[i
].x
- fire_x
) < BOMB_WIDTH
) &&
1055 (fire_y
- bombs
[i
].y
< BOMB_HEIGHT
)) {
1057 rb
->lcd_fillrect(bombs
[i
].x
, bombs
[i
].y
, BOMB_WIDTH
, BOMB_HEIGHT
);
1058 bombs
[i
].state
= S_IDLE
;
1059 /* Explode ship fire */
1061 fire_target
= TARGET_SHIELD
;
1062 fire_alpha(fire_x
, fire_y
, LCD_WHITE
);
1069 for (i
= FIRE_SPEED
; i
>= 0; i
--) {
1070 pix
= get_pixel(fire_x
, fire_y
+ i
);
1071 if(pix
== screen_white
) {
1076 if(pix
== screen_green
) {
1086 /* TODO: Play explode sound */
1089 fire_target
= TARGET_SHIELD
;
1090 /* Center explosion around hit pixel */
1091 fire_y
-= FIRE_HEIGHT
/ 2;
1092 fire_alpha(fire_x
, fire_y
, SLIME_GREEN
);
1099 for (i
= 0; i
< 5 * ALIENS
; i
++) {
1100 if (aliens
[i
].state
!= DEAD
&&
1101 (ABS(fire_x
- (PLAYFIELD_X
+ LIVES_X
+ aliens
[i
].x
* ALIEN_SPEED
+
1102 ALIEN_WIDTH
/ 2)) <= ALIEN_WIDTH
/ 2) &&
1103 (ABS(fire_y
- (ALIEN_START_Y
+ aliens
[i
].y
* ALIEN_HEIGHT
+
1104 ALIEN_HEIGHT
/ 2)) <= ALIEN_HEIGHT
/ 2)) {
1106 /* TODO: play alien hit sound */
1108 if (aliens
[i
].state
== BOMBER
) {
1109 /* Set (possible) alien above to bomber */
1110 for (j
= i
- ALIENS
; j
>= 0; j
-= ALIENS
) {
1111 if (aliens
[j
].state
!= DEAD
) {
1112 /* printf("New bomber (%d, %d)\n", j % ALIENS, j / ALIENS); */
1113 aliens
[j
].state
= BOMBER
;
1118 aliens
[i
].state
= DEAD
;
1119 exploding_alien
= i
;
1120 score
+= scores
[aliens
[i
].type
];
1122 /* Update score part of screen */
1123 rb
->lcd_update_rect(SCORENUM_X
, SCORENUM_Y
,
1124 PLAYFIELD_WIDTH
- 2 * NUMBERS_WIDTH
, FONT_HEIGHT
);
1126 /* Paralyze aliens S_EXPLODE frames */
1127 aliens_paralyzed
= S_EXPLODE
;
1128 rb
->lcd_bitmap_part(invadrox
, ALIEN_EXPLODE_SRC_X
, ALIEN_EXPLODE_SRC_Y
,
1129 STRIDE
, PLAYFIELD_X
+ LIVES_X
+ aliens
[i
].x
* ALIEN_SPEED
,
1130 ALIEN_START_Y
+ aliens
[i
].y
* ALIEN_HEIGHT
,
1131 ALIEN_EXPLODE_WIDTH
, ALIEN_EXPLODE_HEIGHT
);
1132 /* Since alien is 1 pixel taller than explosion sprite, erase bottom line */
1133 rb
->lcd_hline(PLAYFIELD_X
+ LIVES_X
+ aliens
[i
].x
* ALIEN_SPEED
,
1134 PLAYFIELD_X
+ LIVES_X
+ aliens
[i
].x
* ALIEN_SPEED
+ ALIEN_WIDTH
,
1135 ALIEN_START_Y
+ (aliens
[i
].y
+ 1) * ALIEN_HEIGHT
- 1);
1142 rb
->lcd_set_foreground(LCD_WHITE
);
1143 rb
->lcd_vline(fire_x
, fire_y
, fire_y
+ SHOT_HEIGHT
);
1144 rb
->lcd_set_foreground(LCD_BLACK
);
1145 } else if (fire
< S_IDLE
) {
1146 /* Count up towards S_IDLE, then erase explosion */
1148 if (fire
== S_IDLE
) {
1149 /* Erase explosion */
1150 if (fire_target
== TARGET_TOP
) {
1151 rb
->lcd_fillrect(fire_x
- (FIRE_WIDTH
/ 2), fire_y
, FIRE_WIDTH
, FIRE_HEIGHT
);
1152 } else if (fire_target
== TARGET_SHIELD
) {
1153 /* Draw explosion with black pixels */
1154 fire_alpha(fire_x
, fire_y
, LCD_BLACK
);
1161 /* Return a BOMBER alien */
1162 inline int random_bomber(void)
1166 /* TODO: Weigh higher probability near ship */
1167 col
= rb
->rand() % ALIENS
;
1168 for (i
= col
+ 4 * ALIENS
; i
>= 0; i
-= ALIENS
) {
1169 if (aliens
[i
].state
== BOMBER
) {
1174 /* No BOMBER found in this col */
1176 for (i
= 0; i
< 5 * ALIENS
; i
++) {
1177 if (aliens
[i
].state
== BOMBER
) {
1182 /* No BOMBER found at all (error?) */
1188 inline void draw_bomb(int i
)
1190 rb
->lcd_bitmap_part(invadrox
, BOMB_SRC_X
+ bombs
[i
].type
* BOMB_WIDTH
,
1191 bombs
[i
].frame
* (BOMB_HEIGHT
+ 1),
1192 STRIDE
, bombs
[i
].x
, bombs
[i
].y
,
1193 BOMB_WIDTH
, BOMB_HEIGHT
);
1196 if (bombs
[i
].frame
== bombs
[i
].frames
) {
1202 void move_bombs(void)
1207 for (i
= 0; i
< max_bombs
; i
++) {
1209 switch (bombs
[i
].state
) {
1215 bomber
= random_bomber();
1217 DBG("ERROR: No bomber available\n");
1221 bombs
[i
].x
= PLAYFIELD_X
+ LIVES_X
+ aliens
[bomber
].x
* ALIEN_SPEED
+ ALIEN_WIDTH
/ 2;
1222 bombs
[i
].y
= ALIEN_START_Y
+ (aliens
[bomber
].y
+ 1) * ALIEN_HEIGHT
;
1224 /* Check for duplets in x and y direction */
1226 for (j
= i
- 1; j
>= 0; j
--) {
1227 if ((bombs
[j
].state
== S_ACTIVE
) &&
1228 ((bombs
[i
].x
== bombs
[j
].x
) || (bombs
[i
].y
== bombs
[j
].y
))) {
1234 /* Skip this one, continue with next bomb */
1235 /* printf("Bomb %d duplet of %d\n", i, j); */
1239 /* Passed, set type */
1240 bombs
[i
].type
= rb
->rand() % 3;
1242 if (bombs
[i
].type
== 0) {
1243 bombs
[i
].frames
= 3;
1244 } else if (bombs
[i
].type
== 1) {
1245 bombs
[i
].frames
= 4;
1247 bombs
[i
].frames
= 6;
1251 bombs
[i
].state
= S_ACTIVE
;
1258 /* Erase old position */
1259 rb
->lcd_fillrect(bombs
[i
].x
, bombs
[i
].y
, BOMB_WIDTH
, BOMB_HEIGHT
);
1262 bombs
[i
].y
+= BOMB_SPEED
;
1264 /* Check if bottom hit */
1265 if (bombs
[i
].y
+ BOMB_HEIGHT
>= PLAYFIELD_Y
) {
1266 bombs
[i
].y
= PLAYFIELD_Y
- FIRE_HEIGHT
+ 1;
1267 fire_alpha(bombs
[i
].x
, bombs
[i
].y
, LCD_WHITE
);
1268 bombs
[i
].state
= S_EXPLODE
;
1269 bombs
[i
].target
= TARGET_BOTTOM
;
1273 /* Check for green (ship or shield) */
1274 for (j
= BOMB_HEIGHT
; j
>= BOMB_HEIGHT
- BOMB_SPEED
; j
--) {
1275 bombs
[i
].target
= 0;
1276 if(get_pixel(bombs
[i
].x
+ BOMB_WIDTH
/ 2, bombs
[i
].y
+ j
) == screen_green
) {
1277 /* Move to hit pixel */
1278 bombs
[i
].x
+= BOMB_WIDTH
/ 2;
1281 /* Check if ship is hit */
1282 if (bombs
[i
].y
> SHIELD_Y
+ SHIELD_HEIGHT
&& bombs
[i
].y
< PLAYFIELD_Y
) {
1284 /* TODO: play ship hit sound */
1288 ship_frame_counter
= 0;
1289 bombs
[i
].state
= S_EXPLODE
* 4;
1290 bombs
[i
].target
= TARGET_SHIP
;
1291 rb
->lcd_bitmap_part(invadrox
, SHIP_SRC_X
, 1 * SHIP_HEIGHT
, STRIDE
,
1292 ship_x
, SHIP_Y
, SHIP_WIDTH
, SHIP_HEIGHT
);
1296 bombs
[i
].state
= S_EXPLODE
;
1297 bombs
[i
].target
= TARGET_SHIELD
;
1298 /* Center explosion around hit pixel in shield */
1299 bombs
[i
].y
-= FIRE_HEIGHT
/ 2;
1300 fire_alpha(bombs
[i
].x
, bombs
[i
].y
, SLIME_GREEN
);
1305 if (bombs
[i
].target
!= 0) {
1306 /* Hit ship or shield, continue */
1314 /* If we get here state should be < 0, exploding */
1316 if (bombs
[i
].state
== S_IDLE
) {
1318 /* Erase explosion */
1319 rb
->lcd_fillrect(ship_x
, SHIP_Y
, SHIP_WIDTH
, SHIP_HEIGHT
);
1320 rb
->lcd_update_rect(ship_x
, SHIP_Y
, SHIP_WIDTH
, SHIP_HEIGHT
);
1323 ship_x
= PLAYFIELD_X
+ 2 * LIVES_X
;
1330 /* Sleep 1s to give player time to examine lives left */
1333 /* Erase explosion (even if ship hit, might be another bomb) */
1334 fire_alpha(bombs
[i
].x
, bombs
[i
].y
, LCD_BLACK
);
1342 inline void move_ship(void)
1344 ship_dir
+= ship_acc
;
1345 if (ship_dir
> max_ship_speed
) {
1346 ship_dir
= max_ship_speed
;
1348 if (ship_dir
< -max_ship_speed
) {
1349 ship_dir
= -max_ship_speed
;
1352 if (ship_x
< SHIP_MIN_X
) {
1353 ship_x
= SHIP_MIN_X
;
1355 if (ship_x
> SHIP_MAX_X
) {
1356 ship_x
= SHIP_MAX_X
;
1363 /* Unidentified Flying Object */
1366 static int ufo_speed
;
1370 switch (ufo_state
) {
1374 if (rb
->rand() % 500 == 0) {
1375 /* Uh-oh, it's time to launch a mystery UFO */
1377 /* TODO: Play UFO sound */
1379 if (rb
->rand() % 2) {
1380 ufo_speed
= UFO_SPEED
;
1381 ufo_x
= PLAYFIELD_X
;
1383 ufo_speed
= -UFO_SPEED
;
1384 ufo_x
= LCD_WIDTH
- PLAYFIELD_X
- UFO_WIDTH
;
1386 ufo_state
= S_ACTIVE
;
1387 /* UFO will be drawn next frame */
1393 rb
->lcd_fillrect(ufo_x
, UFO_Y
, UFO_WIDTH
, UFO_HEIGHT
);
1397 if (ufo_x
< PLAYFIELD_X
|| ufo_x
> LCD_WIDTH
- PLAYFIELD_X
- UFO_WIDTH
) {
1402 rb
->lcd_bitmap_part(invadrox
, UFO_SRC_X
, 0,
1403 STRIDE
, ufo_x
, UFO_Y
, UFO_WIDTH
, UFO_HEIGHT
);
1408 if (counter
== S_IDLE
) {
1409 /* Erase mystery number */
1410 rb
->lcd_fillrect(ufo_x
, UFO_Y
, 3 * NUMBERS_WIDTH
+ 2 * NUM_SPACING
, FONT_HEIGHT
);
1418 if (ufo_state
== S_IDLE
) {
1419 /* Erase explosion */
1420 rb
->lcd_fillrect(ufo_x
, UFO_Y
- 1, UFO_EXPLODE_WIDTH
, UFO_EXPLODE_HEIGHT
);
1421 ufo_state
= S_SHOWSCORE
;
1422 counter
= S_EXPLODE
* 4;
1423 /* Draw mystery_score, sleep, increase score and continue */
1424 mystery_score
= 50 + (rb
->rand() % 6) * 50;
1425 if (mystery_score
< 100) {
1426 draw_number(ufo_x
, UFO_Y
, mystery_score
, 2);
1428 draw_number(ufo_x
, UFO_Y
, mystery_score
, 3);
1430 score
+= mystery_score
;
1438 void draw_background(void)
1441 #if (LCD_WIDTH == 320) && (LCD_HEIGHT == 240)
1442 /* Erase background to black */
1443 rb
->lcd_fillrect(PLAYFIELD_X
, 0, PLAYFIELD_WIDTH
, LCD_HEIGHT
);
1444 /* Left and right bitmaps */
1445 rb
->lcd_bitmap(invadrox_left
, 0, 0, PLAYFIELD_X
, LCD_HEIGHT
);
1446 rb
->lcd_bitmap(invadrox_right
, LCD_WIDTH
- PLAYFIELD_X
, 0, PLAYFIELD_X
, LCD_HEIGHT
);
1448 rb
->lcd_fillrect(0, 0, LCD_WIDTH
, LCD_HEIGHT
);
1451 #if ((LCD_WIDTH == 240) && (LCD_HEIGHT == 320)) || ((LCD_WIDTH == 176) && (LCD_HEIGHT == 220))
1452 rb
->lcd_bitmap(invadrox_logo
, 0, 0, LCD_WIDTH
, SCORE_Y
);
1459 void new_level(void)
1464 /* Give an extra life for each new level */
1465 if (lives
< MAX_LIVES
) {
1471 rb
->lcd_bitmap_part(invadrox
, SCORE_SRC_X
, SCORE_SRC_Y
,
1472 STRIDE
, PLAYFIELD_X
, SCORE_Y
, SCORE_WIDTH
, FONT_HEIGHT
);
1474 rb
->lcd_bitmap_part(invadrox
, 0, SCORE_SRC_Y
,
1475 STRIDE
, HISCORE_X
, SCORE_Y
,
1476 HISCORE_WIDTH
, FONT_HEIGHT
);
1478 draw_number(HISCORENUM_X
, SCORENUM_Y
, hiscore
.score
, 4);
1482 level_finished
= false;
1486 /* Init alien positions and states */
1487 for (i
= 0; i
< 4 * ALIENS
; i
++) {
1488 aliens
[i
].x
= 0 + (i
% ALIENS
) * ((ALIEN_WIDTH
+ ALIEN_SPACING
) / ALIEN_SPEED
);
1489 aliens
[i
].y
= 2 * (i
/ ALIENS
);
1490 aliens
[i
].state
= ALIVE
;
1492 /* Last row, bombers */
1493 for (i
= 4 * ALIENS
; i
< 5 * ALIENS
; i
++) {
1494 aliens
[i
].x
= 0 + (i
% ALIENS
) * ((ALIEN_WIDTH
+ ALIEN_SPACING
) / ALIEN_SPEED
);
1495 aliens
[i
].y
= 2 * (i
/ ALIENS
);
1496 aliens
[i
].state
= BOMBER
;
1499 /* Init bombs to inactive (S_IDLE) */
1500 for (i
= 0; i
< MAX_BOMBS
; i
++) {
1501 bombs
[i
].state
= S_IDLE
;
1504 /* Start aliens closer to earth from level 2 */
1505 for (i
= 0; i
< 5 * ALIENS
; i
++) {
1507 aliens
[i
].y
+= level
- 1;
1513 /* Max concurrent bombs */
1522 /* Increase speed */
1531 /* Increase speed more */
1541 for (i
= 1; i
<= 4; i
++) {
1542 rb
->lcd_bitmap_part(invadrox
, SHIELD_SRC_X
, SHIELD_SRC_Y
, STRIDE
,
1543 PLAYFIELD_X
+ i
* PLAYFIELD_WIDTH
/ 5 - SHIELD_WIDTH
/ 2,
1544 SHIELD_Y
, SHIELD_WIDTH
, SHIELD_HEIGHT
);
1548 rb
->lcd_set_foreground(SLIME_GREEN
);
1549 rb
->lcd_hline(PLAYFIELD_X
, LCD_WIDTH
- PLAYFIELD_X
, PLAYFIELD_Y
);
1550 /* Restore foreground to black (for fast erase later). */
1551 rb
->lcd_set_foreground(LCD_BLACK
);
1553 ship_x
= PLAYFIELD_X
+ 2 * LIVES_X
;
1555 old_ship_x
= ship_x
;
1562 /* Start moving the bottom row left to right */
1563 curr_alien
= 4 * ALIENS
;
1564 aliens_paralyzed
= 0;
1565 aliens_right
= true;
1566 aliens_down
= false;
1567 hit_left_border
= false;
1568 hit_right_border
= false;
1569 /* TODO: Change max_ship_speed to 3 at higher levels */
1578 void init_invadrox(void)
1582 /* Seed random number generator with a "random" number */
1583 rb
->srand(rb
->get_time()->tm_sec
+ rb
->get_time()->tm_min
* 60);
1585 /* Precalculate start of each scanline */
1586 for (i
= 0; i
< LCD_HEIGHT
; i
++) {
1587 #if (LCD_DEPTH >= 8)
1588 ytab
[i
] = i
* LCD_WIDTH
;
1589 #elif (LCD_DEPTH == 2) && (LCD_PIXELFORMAT == HORIZONTAL_PACKING)
1590 ytab
[i
] = i
* (LCD_WIDTH
/ 4);
1591 #elif (LCD_DEPTH == 2) && (LCD_PIXELFORMAT == VERTICAL_PACKING)
1592 ytab
[i
] = (i
/ 4) * LCD_WIDTH
;
1594 #error pixelformat not implemented yet
1598 rb
->lcd_set_background(LCD_BLACK
);
1599 rb
->lcd_set_foreground(LCD_BLACK
);
1601 if (highscore_load(HISCOREFILE
, &hiscore
, 1) < 0) {
1602 /* Init hiscore to 0 */
1603 rb
->strncpy(hiscore
.name
, "Invader", sizeof(hiscore
.name
));
1608 /* Init alien types in aliens array */
1609 for (i
= 0; i
< 1 * ALIENS
; i
++) {
1610 aliens
[i
].type
= 0; /* Kang */
1612 for (; i
< 3 * ALIENS
; i
++) {
1613 aliens
[i
].type
= 1; /* Kodos */
1615 for (; i
< 5 * ALIENS
; i
++) {
1616 aliens
[i
].type
= 2; /* Serak */
1620 /* Save screen white color */
1621 rb
->lcd_set_foreground(LCD_WHITE
);
1622 rb
->lcd_drawpixel(0, 0);
1623 rb
->lcd_update_rect(0, 0, 1, 1);
1624 screen_white
= get_pixel(0, 0);
1626 /* Save screen green color */
1627 rb
->lcd_set_foreground(SLIME_GREEN
);
1628 rb
->lcd_drawpixel(0, 0);
1629 rb
->lcd_update_rect(0, 0, 1, 1);
1630 screen_green
= get_pixel(0, 0);
1632 /* Restore black foreground */
1633 rb
->lcd_set_foreground(LCD_BLACK
);
1637 /* Flash score at start */
1638 for (i
= 0; i
< 5; i
++) {
1639 rb
->lcd_fillrect(SCORENUM_X
, SCORENUM_Y
,
1640 4 * NUMBERS_WIDTH
+ 3 * NUM_SPACING
,
1642 rb
->lcd_update_rect(SCORENUM_X
, SCORENUM_Y
,
1643 4 * NUMBERS_WIDTH
+ 3 * NUM_SPACING
,
1646 draw_number(SCORENUM_X
, SCORENUM_Y
, score
, 4);
1652 inline bool handle_buttons(void)
1654 static unsigned int oldbuttonstate
= 0;
1656 unsigned int released
, pressed
, newbuttonstate
;
1659 /* Don't allow ship movement during explosion */
1662 newbuttonstate
= rb
->button_status();
1664 if(newbuttonstate
== oldbuttonstate
) {
1665 if (newbuttonstate
== 0) {
1666 /* No button pressed. Stop ship. */
1678 released
= ~newbuttonstate
& oldbuttonstate
;
1679 pressed
= newbuttonstate
& ~oldbuttonstate
;
1680 oldbuttonstate
= newbuttonstate
;
1682 if (pressed
& LEFT
) {
1683 if (ship_acc
> -1) {
1687 if (pressed
& RIGHT
) {
1692 if (pressed
& FIRE
) {
1693 if (fire
== S_IDLE
) {
1695 fire_x
= ship_x
+ SHIP_WIDTH
/ 2;
1696 fire_y
= SHIP_Y
- SHOT_HEIGHT
;
1698 /* TODO: play fire sound */
1702 if (pressed
& RC_QUIT
) {
1703 rb
->splash(HZ
* 1, "Quit");
1707 if (pressed
& QUIT
) {
1708 rb
->splash(HZ
* 1, "Quit");
1713 if ((released
& LEFT
)) {
1718 if ((released
& RIGHT
)) {
1719 if (ship_acc
> -1) {
1727 /* Quit if USB is connected */
1728 if (rb
->button_get(false) == SYS_USB_CONNECTED
) {
1736 void game_loop(void)
1740 /* Print dimensions (just for debugging) */
1741 DBG("%03dx%03dx%02d\n", LCD_WIDTH
, LCD_HEIGHT
, LCD_DEPTH
);
1747 /* Convert CYCLETIME (in ms) to HZ */
1748 end
= *rb
->current_tick
+ (CYCLETIME
* HZ
) / 1000;
1750 if (handle_buttons()) {
1758 /* Check if level is finished (marked by move_fire) */
1759 if (level_finished
) {
1760 /* TODO: Play level finished sound */
1767 if (!aliens_paralyzed
&& !ship_hit
) {
1768 for (i
= 0; i
< gamespeed
; i
++) {
1769 if (!move_aliens()) {
1777 /* Move alien bombs */
1783 /* Update "playfield" rect */
1784 rb
->lcd_update_rect(PLAYFIELD_X
, SCORENUM_Y
+ FONT_HEIGHT
,
1786 PLAYFIELD_Y
+ 1 - SCORENUM_Y
- FONT_HEIGHT
);
1788 /* Wait until next frame */
1789 DBG("%ld (%d)\n", end
- *rb
->current_tick
, (CYCLETIME
* HZ
) / 1000);
1790 if (end
> *rb
->current_tick
) {
1791 rb
->sleep(end
- *rb
->current_tick
);
1800 /* this is the plugin entry point */
1801 enum plugin_status
plugin_start(UNUSED
const void* parameter
)
1803 rb
->lcd_setfont(FONT_SYSFIXED
);
1804 /* Turn off backlight timeout */
1805 backlight_force_on(); /* backlight control in lib/helper.c */
1807 /* now go ahead and have fun! */
1811 /* TODO: Play game over sound */
1812 rb
->splash(HZ
* 2, "Game Over");
1813 if (score
> hiscore
.score
) {
1814 /* Save new hiscore */
1815 hiscore
.score
= score
;
1816 hiscore
.level
= level
;
1817 highscore_save(HISCOREFILE
, &hiscore
, 1);
1820 /* Restore user's original backlight setting */
1821 rb
->lcd_setfont(FONT_UI
);
1822 /* Turn on backlight timeout (revert to settings) */
1823 backlight_use_settings(); /* backlight control in lib/helper.c */
1831 * GNU Emacs settings: Kernighan & Richie coding style with
1832 * 4 spaces indent and no tabs.
1834 * c-file-style: "k&r"
1836 * indent-tabs-mode: nil