A bit of adapting.
[Rockbox.git] / apps / plugins / invadrox.c
blob527ec9587fc56b8c56e2bc25d0df5f66e4fa0045
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id: $
10 * Copyright (C) 2006 Albert Veli
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
18 ****************************************************************************/
20 /* Improvised creds goes to:
22 * - Anders Clausen for ingeniously inventing the name Invadrox.
23 * - Linus Nielsen-Feltzing for patiently answering n00b questions.
26 #include "plugin.h"
27 #include "highscore.h"
28 #include "helper.h"
30 PLUGIN_HEADER
32 /* Original graphics is only 1bpp so it should be portable
33 * to most targets. But for now, only support the simple ones.
35 #ifndef HAVE_LCD_BITMAP
36 #error INVADROX: Unsupported LCD
37 #endif
39 #if (LCD_DEPTH < 2)
40 #error INVADROX: Unsupported LCD
41 #endif
43 /* #define DEBUG */
44 #ifdef DEBUG
45 #include <stdio.h>
46 #define DBG(format, arg...) { printf("%s: " format, __FUNCTION__, ## arg); }
47 #else
48 #define DBG(format, arg...) {}
49 #endif
51 #if CONFIG_KEYPAD == IRIVER_H100_PAD
53 #define QUIT BUTTON_OFF
54 #define LEFT BUTTON_LEFT
55 #define RIGHT BUTTON_RIGHT
56 #define FIRE BUTTON_ON
58 #elif CONFIG_KEYPAD == IRIVER_H300_PAD
60 #define QUIT BUTTON_OFF
61 #define LEFT BUTTON_LEFT
62 #define RIGHT BUTTON_RIGHT
63 #define FIRE BUTTON_SELECT
65 #elif (CONFIG_KEYPAD == IRIVER_H10_PAD)
67 #define QUIT BUTTON_POWER
68 #define LEFT BUTTON_LEFT
69 #define RIGHT BUTTON_RIGHT
70 #define FIRE BUTTON_PLAY
72 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
73 (CONFIG_KEYPAD == IPOD_3G_PAD) || \
74 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
76 #define QUIT BUTTON_MENU
77 #define LEFT BUTTON_LEFT
78 #define RIGHT BUTTON_RIGHT
79 #define FIRE BUTTON_SELECT
81 #elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD
83 #define QUIT BUTTON_POWER
84 #define LEFT BUTTON_LEFT
85 #define RIGHT BUTTON_RIGHT
86 #define FIRE BUTTON_SELECT
88 #elif CONFIG_KEYPAD == GIGABEAT_PAD
90 #define QUIT BUTTON_POWER
91 #define LEFT BUTTON_LEFT
92 #define RIGHT BUTTON_RIGHT
93 #define FIRE BUTTON_SELECT
95 #elif CONFIG_KEYPAD == SANSA_E200_PAD
97 #define QUIT BUTTON_POWER
98 #define LEFT BUTTON_LEFT
99 #define RIGHT BUTTON_RIGHT
100 #define FIRE BUTTON_SELECT
102 #elif CONFIG_KEYPAD == ELIO_TPJ1022_PAD
104 /* TODO: Figure out which buttons to use for Tatung Elio TPJ-1022 */
105 #define QUIT BUTTON_AB
106 #define LEFT BUTTON_LEFT
107 #define RIGHT BUTTON_RIGHT
108 #define FIRE BUTTON_MENU
110 #else
111 #error INVADROX: Unsupported keypad
112 #endif
115 #ifndef UNUSED
116 #define UNUSED __attribute__ ((unused))
117 #endif
119 #ifndef ABS
120 #define ABS(x) (((x) < 0) ? (-(x)) : (x))
121 #endif
124 /* Defines common to all models */
125 #define UFO_Y (SCORENUM_Y + FONT_HEIGHT + ALIEN_HEIGHT)
126 #define PLAYFIELD_Y (LCD_HEIGHT - SHIP_HEIGHT - 2)
127 #define PLAYFIELD_WIDTH (LCD_WIDTH - 2 * PLAYFIELD_X)
128 #define LEVEL_X (LCD_WIDTH - PLAYFIELD_X - LIVES_X - LEVEL_WIDTH - 2 * NUMBERS_WIDTH - 3 * NUM_SPACING)
129 #define SHIP_MIN_X (PLAYFIELD_X + PLAYFIELD_WIDTH / 5 - SHIELD_WIDTH / 2 - SHIP_WIDTH)
130 #define SHIP_MAX_X (PLAYFIELD_X + 4 * PLAYFIELD_WIDTH / 5 + SHIELD_WIDTH / 2)
131 /* SCORE_Y = 0 for most targets. Gigabeat redefines it later. */
132 #define SCORE_Y 0
133 #define MAX_LIVES 8
136 /* iPod Video defines */
137 #if (LCD_WIDTH == 320) && (LCD_HEIGHT == 240)
139 /* Original arcade game size 224x240, 1bpp with
140 * red overlay at top and green overlay at bottom.
142 * iPod Video: 320x240x16
143 * ======================
144 * X: 48p padding at left/right gives 224p playfield in middle.
145 * 10p "border" gives 204p actual playfield. UFO use full 224p.
146 * Y: Use full 240p.
148 * MAX_X = (204 - 12) / 2 - 1 = 95
150 * Y: Score text 7 0
151 * Space 10 7
152 * Score 7 17
153 * Space 8 24
154 * 3 Ufo 7 32
155 * 2 Space Aliens start at 32 + 3 * 8 = 56
156 * 0 aliens 9*8 56 -
157 * space ~7*8 128 | 18.75 aliens space between
158 * shield 2*8 182 | first alien and ship.
159 * space 8 198 | MAX_Y = 18
160 * ship 8 206 -
161 * space 2*8 214
162 * hline 1 230 - PLAYFIELD_Y
163 * bottom border 10 240
164 * Lives and Level goes inside bottom border
167 #define ARCADISH_GRAPHICS
168 #define PLAYFIELD_X 48
169 #define SHIP_Y (PLAYFIELD_Y - 3 * SHIP_HEIGHT)
170 #define ALIEN_START_Y (UFO_Y + 3 * ALIEN_HEIGHT)
171 #define SCORENUM_X (PLAYFIELD_X + NUMBERS_WIDTH)
172 #define SCORENUM_Y SCORE_Y + (2 * (FONT_HEIGHT + 1) + 1)
173 #define HISCORE_X (LCD_WIDTH - PLAYFIELD_X - HISCORE_WIDTH)
174 #define HISCORENUM_X (LCD_WIDTH - PLAYFIELD_X - 1 - 6 * NUMBERS_WIDTH - 5 * NUM_SPACING)
175 #define SHIELD_Y (PLAYFIELD_Y - 6 * SHIP_HEIGHT)
176 #define LIVES_X 10
177 #define MAX_X 95
178 #define MAX_Y 18
181 #elif (LCD_WIDTH == 176) && (LCD_HEIGHT == 220)
183 /* Sandisk Sansa e200: 176x220x16
184 * ==============================
185 * X: No padding. 8p border -> 160p playfield.
187 * 8p Aliens with 3p spacing -> 88 + 30 = 118p aliens block.
188 * (160 - 118) / 2 = 21 rounds for whole block (more than original)
189 * MAX_X = (160 - 8) / 2 - 1 = 75 rounds for single alien (less than original)
191 * LOGO 70 0
192 * Score text 5 70
193 * Space 5 75
194 * Y Score 5 80
195 * Space 10 85
196 * 2 Ufo 5 95
197 * 2 Space 10 100
198 * 0 aliens 9*5 110 -
199 * space ~7*5 155 | 18.6 aliens space between
200 * shield 2*5 188 | first alien and ship.
201 * space 5 198 | MAX_Y = 18
202 * ship 5 203 -
203 * space 5 208
204 * hline 1 213 PLAYFIELD_Y
205 * bottom border 6
206 * LCD_HEIGHT 220
207 * Lives and Level goes inside bottom border
210 #define TINY_GRAPHICS
211 #define PLAYFIELD_X 0
212 #define SHIP_Y (PLAYFIELD_Y - 2 * SHIP_HEIGHT)
213 #define SHIELD_Y (SHIP_Y - SHIP_HEIGHT - SHIELD_HEIGHT)
214 #define ALIEN_START_Y (UFO_Y + 3 * SHIP_HEIGHT)
215 /* Redefine SCORE_Y */
216 #undef SCORE_Y
217 #define SCORE_Y 70
218 #define SCORENUM_X (PLAYFIELD_X + NUMBERS_WIDTH)
219 #define SCORENUM_Y (SCORE_Y + 2 * FONT_HEIGHT)
220 #define HISCORE_X (LCD_WIDTH - PLAYFIELD_X - HISCORE_WIDTH)
221 #define HISCORENUM_X (LCD_WIDTH - PLAYFIELD_X - 1 - 6 * NUMBERS_WIDTH - 5 * NUM_SPACING)
222 #define LIVES_X 8
223 #define MAX_X 75
224 #define MAX_Y 18
227 #elif (LCD_WIDTH == 176) && (LCD_HEIGHT == 132)
229 /* iPod Nano: 176x132x16
230 * ======================
231 * X: No padding. 8p border -> 160p playfield.
233 * LIVES_X 8
234 * ALIEN_WIDTH 8
235 * ALIEN_HEIGHT 5
236 * ALIEN_SPACING 3
237 * SHIP_WIDTH 10
238 * SHIP_HEIGHT 5
239 * FONT_HEIGHT 5
240 * UFO_WIDTH 10
241 * UFO_HEIGHT 5
242 * SHIELD_WIDTH 15
243 * SHIELD_HEIGHT 10
244 * MAX_X 75
245 * MAX_Y = 18
246 * ALIEN_START_Y (UFO_Y + 12)
248 * 8p Aliens with 3p spacing -> 88 + 30 = 118p aliens block.
249 * (160 - 118) / 2 = 21 rounds for whole block (more than original)
250 * MAX_X = (160 - 8) / 2 - 1 = 75 rounds for single alien (less than original)
252 * Y: Scoreline 5 0 (combine scoretext and numbers on same line)
253 * Space 5 5
254 * 1 Ufo 5 10
255 * 3 Space 7 15
256 * 2 aliens 9*5 22 -
257 * space ~7*5 67 | Just above 18 aliens space between
258 * shield 2*5 100 | first alien and ship.
259 * space 5 110 | MAX_Y = 18
260 * ship 5 115 -
261 * space 5 120
262 * hline 1 125 PLAYFIELD_Y
263 * bottom border 6 126
264 * LCD_HEIGHT 131
265 * Lives and Level goes inside bottom border
268 #define TINY_GRAPHICS
269 #define PLAYFIELD_X 0
270 #define SHIP_Y (PLAYFIELD_Y - 2 * SHIP_HEIGHT)
271 #define ALIEN_START_Y (UFO_Y + 12)
272 #define SCORENUM_X (PLAYFIELD_X + SCORE_WIDTH + NUMBERS_WIDTH + NUM_SPACING)
273 #define SCORENUM_Y SCORE_Y
274 #define HISCORENUM_X (LCD_WIDTH - PLAYFIELD_X - 4 * NUMBERS_WIDTH - 3 * NUM_SPACING)
275 #define HISCORE_X (HISCORENUM_X - NUMBERS_WIDTH - NUM_SPACING - HISCORE_WIDTH)
276 #define SHIELD_Y (SHIP_Y - SHIP_HEIGHT - SHIELD_HEIGHT)
277 #define LIVES_X 8
278 #define MAX_X 75
279 #define MAX_Y 18
281 #elif (LCD_WIDTH == 160) && (LCD_HEIGHT == 128)
283 /* iAudio X5, iRiver H10 20Gb, iPod 3g/4g: 160x128x16
284 * ======================================
285 * X: No padding. No border -> 160p playfield.
287 * LIVES_X 0
288 * ALIEN_WIDTH 8
289 * ALIEN_HEIGHT 5
290 * ALIEN_SPACING 3
291 * SHIP_WIDTH 10
292 * SHIP_HEIGHT 5
293 * FONT_HEIGHT 5
294 * UFO_WIDTH 10
295 * UFO_HEIGHT 5
296 * SHIELD_WIDTH 15
297 * SHIELD_HEIGHT 10
298 * MAX_X 75
299 * MAX_Y = 18
300 * ALIEN_START_Y (UFO_Y + 10)
302 * 8p Aliens with 3p spacing -> 88 + 30 = 118p aliens block.
303 * (160 - 118) / 2 = 21 rounds for whole block (more than original)
304 * MAX_X = (160 - 8) / 2 - 1 = 75 rounds for single alien (less than original)
306 * Y: Scoreline 5 0 (combine scoretext and numbers on same line)
307 * Space 5 5
308 * 1 Ufo 5 10
309 * 2 Space 5 15
310 * 8 aliens 9*5 20 -
311 * space ~6*5 65 | Just above 18 aliens space between
312 * shield 2*5 96 | first alien and ship.
313 * space 5 106 | MAX_Y = 18
314 * ship 5 111 -
315 * space 5 116
316 * hline 1 121 PLAYFIELD_Y
317 * bottom border 6 122
318 * LCD_HEIGHT 128
319 * Lives and Level goes inside bottom border
322 #define TINY_GRAPHICS
323 #define PLAYFIELD_X 0
324 #define SHIP_Y (PLAYFIELD_Y - 2 * SHIP_HEIGHT)
325 #define ALIEN_START_Y (UFO_Y + 10)
326 #define SCORENUM_X (PLAYFIELD_X + SCORE_WIDTH + NUMBERS_WIDTH + NUM_SPACING)
327 #define SCORENUM_Y SCORE_Y
328 #define HISCORENUM_X (LCD_WIDTH - PLAYFIELD_X - 4 * NUMBERS_WIDTH - 3 * NUM_SPACING)
329 #define HISCORE_X (HISCORENUM_X - NUMBERS_WIDTH - NUM_SPACING - HISCORE_WIDTH)
330 #define SHIELD_Y (SHIP_Y - SHIP_HEIGHT - SHIELD_HEIGHT)
331 #define LIVES_X 0
332 #define MAX_X 75
333 #define MAX_Y 18
336 #elif (LCD_WIDTH == 240) && (LCD_HEIGHT == 320)
338 /* Gigabeat: 240x320x16
339 * ======================
340 * X: 8p padding at left/right gives 224p playfield in middle.
341 * 10p "border" gives 204p actual playfield. UFO use full 224p.
342 * Y: Use bottom 240p for playfield and top 80 pixels for logo.
344 * MAX_X = (204 - 12) / 2 - 1 = 95
346 * Y: Score text 7 0 + 80
347 * Space 10 7 + 80
348 * Score 7 17 + 80
349 * Space 8 24 + 80
350 * 3 Ufo 7 32 + 80
351 * 2 Space Aliens start at 32 + 3 * 8 = 56
352 * 0 aliens 9*8 56 -
353 * space ~7*8 128 | 18.75 aliens space between
354 * shield 2*8 182 | first alien and ship.
355 * space 8 198 | MAX_Y = 18
356 * ship 8 206 -
357 * space 2*8 214
358 * hline 1 230 310 - PLAYFIELD_Y
359 * bottom border 10 240 320
360 * Lives and Level goes inside bottom border
363 #define ARCADISH_GRAPHICS
364 #define PLAYFIELD_X 8
365 #define SHIP_Y (PLAYFIELD_Y - 3 * SHIP_HEIGHT)
366 #define ALIEN_START_Y (UFO_Y + 3 * ALIEN_HEIGHT)
367 /* Redefine SCORE_Y */
368 #undef SCORE_Y
369 #define SCORE_Y 80
370 #define SCORENUM_X (PLAYFIELD_X + NUMBERS_WIDTH)
371 #define SCORENUM_Y SCORE_Y + (2 * (FONT_HEIGHT + 1) + 1)
372 #define HISCORE_X (LCD_WIDTH - PLAYFIELD_X - HISCORE_WIDTH)
373 #define HISCORENUM_X (LCD_WIDTH - PLAYFIELD_X - 1 - 6 * NUMBERS_WIDTH - 5 * NUM_SPACING)
374 #define SHIELD_Y (PLAYFIELD_Y - 6 * SHIP_HEIGHT)
375 #define LIVES_X 10
376 #define MAX_X 95
377 #define MAX_Y 18
379 #elif (LCD_WIDTH == 220) && (LCD_HEIGHT == 176)
381 /* TPJ1022, H300, iPod Color: 220x176x16
382 * ============================
383 * X: 0p padding at left/right gives 220p playfield in middle.
384 * 8p "border" gives 204p actual playfield. UFO use full 220p.
385 * Y: Use full 176p for playfield.
387 * MAX_X = (204 - 12) / 2 - 1 = 95
389 * Y: Score text 7 0
390 * Space 8 7
391 * 1 Ufo 7 15
392 * 7 Space Aliens start at 15 + 3 * 8 = 56
393 * 6 aliens 9*8 25 -
394 * space ~7*8 103 | 15.6 aliens space between
395 * shield 2*8 126 | first alien and ship.
396 * space 8 142 | MAX_Y = 15
397 * ship 8 150 -
398 * space 8 158
399 * hline 1 166 - PLAYFIELD_Y
400 * bottom border 10 176
401 * Lives and Level goes inside bottom border
404 #define ARCADISH_GRAPHICS
405 #define PLAYFIELD_X 0
406 #define SHIP_Y (PLAYFIELD_Y - 2 * SHIP_HEIGHT)
407 #define ALIEN_START_Y (UFO_Y + 10)
408 #define SCORENUM_Y SCORE_Y
409 #define SCORENUM_X (PLAYFIELD_X + SCORE_WIDTH + NUMBERS_WIDTH + NUM_SPACING)
410 #define HISCORENUM_X (LCD_WIDTH - PLAYFIELD_X - 4 * NUMBERS_WIDTH - 3 * NUM_SPACING)
411 #define HISCORE_X (HISCORENUM_X - NUMBERS_WIDTH - NUM_SPACING - HISCORE_WIDTH)
412 #define SHIELD_Y (PLAYFIELD_Y - 5 * SHIP_HEIGHT)
413 #define LIVES_X 8
414 #define MAX_X 95
415 #define MAX_Y 15
417 #else
418 #error INVADROX: Unsupported LCD type
419 #endif
422 /* Defines common to each "graphic type" */
423 #ifdef ARCADISH_GRAPHICS
425 #define STRIDE 71
426 #define SHIP_SRC_X 24
427 #define SHIP_WIDTH 16
428 #define SHIP_HEIGHT 8
429 #define SHOT_HEIGHT 5
430 #define ALIEN_WIDTH 12
431 #define ALIEN_EXPLODE_SRC_X 52
432 #define ALIEN_EXPLODE_SRC_Y 39
433 #define ALIEN_EXPLODE_WIDTH 13
434 #define ALIEN_EXPLODE_HEIGHT 7
435 #define ALIEN_HEIGHT 8
436 #define ALIEN_SPACING 4
437 #define ALIEN_SPEED 2
438 #define UFO_SRC_X 40
439 #define UFO_WIDTH 16
440 #define UFO_HEIGHT 7
441 #define UFO_EXPLODE_WIDTH 21
442 #define UFO_EXPLODE_HEIGHT 8
443 #define UFO_SPEED 1
444 #define FONT_HEIGHT 7
445 #define LEVEL_SRC_Y 24
446 #define LEVEL_WIDTH 37
447 #define SCORE_SRC_X 24
448 #define SCORE_SRC_Y 31
449 #define SCORE_WIDTH 37
450 #define HISCORE_WIDTH 61
451 #define NUM_SPACING 3
452 #define NUMBERS_SRC_Y 38
453 #define NUMBERS_WIDTH 5
454 #define SHIELD_SRC_X 40
455 #define SHIELD_SRC_Y 15
456 #define SHIELD_WIDTH 22
457 #define SHIELD_HEIGHT 16
458 #define FIRE_WIDTH 8
459 #define FIRE_HEIGHT 8
460 #define FIRE_SPEED 8
461 #define BOMB_SRC_X 62
462 #define BOMB_WIDTH 3
463 #define BOMB_HEIGHT 7
464 #define BOMB_SPEED 3
465 #define ALIENS 11
466 unsigned char fire_sprite[FIRE_HEIGHT] = {
467 (1 << 7) | (0 << 6) | (0 << 5) | (0 << 4) | (1 << 3) | (0 << 2) | (0 << 1) | 1,
468 (0 << 7) | (0 << 6) | (1 << 5) | (0 << 4) | (0 << 3) | (0 << 2) | (1 << 1) | 0,
469 (0 << 7) | (1 << 6) | (1 << 5) | (1 << 4) | (1 << 3) | (1 << 2) | (1 << 1) | 0,
470 (1 << 7) | (1 << 6) | (1 << 5) | (1 << 4) | (1 << 3) | (1 << 2) | (1 << 1) | 1,
471 (0 << 7) | (1 << 6) | (1 << 5) | (1 << 4) | (1 << 3) | (1 << 2) | (1 << 1) | 1,
472 (0 << 7) | (1 << 6) | (1 << 5) | (1 << 4) | (1 << 3) | (1 << 2) | (1 << 1) | 0,
473 (0 << 7) | (0 << 6) | (1 << 5) | (0 << 4) | (0 << 3) | (1 << 2) | (0 << 1) | 0,
474 (1 << 7) | (0 << 6) | (0 << 5) | (1 << 4) | (0 << 3) | (0 << 2) | (0 << 1) | 1
477 #elif defined TINY_GRAPHICS
479 #define STRIDE 53
480 #define SHIP_SRC_X 16
481 #define SHIP_WIDTH 10
482 #define SHIP_HEIGHT 5
483 #define SHOT_HEIGHT 4
484 #define ALIEN_WIDTH 8
485 #define ALIEN_HEIGHT 5
486 #define ALIEN_EXPLODE_SRC_X 40
487 #define ALIEN_EXPLODE_SRC_Y 26
488 #define ALIEN_EXPLODE_WIDTH 10
489 #define ALIEN_EXPLODE_HEIGHT 5
490 #define ALIEN_SPACING 3
491 #define ALIEN_SPEED 2
492 #define UFO_SRC_X 26
493 #define UFO_WIDTH 11
494 #define UFO_HEIGHT 5
495 #define UFO_EXPLODE_WIDTH 14
496 #define UFO_EXPLODE_HEIGHT 5
497 #define UFO_SPEED 1
498 #define FONT_HEIGHT 5
499 #define LEVEL_SRC_Y 15
500 #define LEVEL_WIDTH 29
501 #define NUMBERS_WIDTH 4
502 #define NUM_SPACING 2
503 #define SCORE_SRC_X 17
504 #define SCORE_SRC_Y 20
505 #define SCORE_WIDTH 28
506 #define HISCORE_WIDTH 45
507 #define NUMBERS_SRC_Y 25
508 #define SHIELD_SRC_X 29
509 #define SHIELD_SRC_Y 10
510 #define SHIELD_WIDTH 15
511 #define SHIELD_HEIGHT 10
512 #define FIRE_WIDTH 6
513 #define FIRE_HEIGHT 6
514 #define FIRE_SPEED 6
515 #define BOMB_SRC_X 44
516 #define BOMB_WIDTH 3
517 #define BOMB_HEIGHT 5
518 #define BOMB_SPEED 2
519 #define ALIENS 11
520 unsigned char fire_sprite[FIRE_HEIGHT] = {
521 (1 << 5) | (0 << 4) | (0 << 3) | (1 << 2) | (0 << 1) | 1,
522 (0 << 5) | (1 << 4) | (0 << 3) | (0 << 2) | (0 << 1) | 0,
523 (0 << 5) | (1 << 4) | (1 << 3) | (1 << 2) | (1 << 1) | 0,
524 (0 << 5) | (1 << 4) | (1 << 3) | (1 << 2) | (1 << 1) | 1,
525 (0 << 5) | (1 << 4) | (0 << 3) | (0 << 2) | (1 << 1) | 0,
526 (1 << 5) | (0 << 4) | (1 << 3) | (0 << 2) | (0 << 1) | 1
529 #else
530 #error Graphic type not defined
531 #endif
534 /* Colors */
535 #if (LCD_DEPTH >= 8)
536 #define SLIME_GREEN LCD_RGBPACK(31, 254, 31)
537 #define UFO_RED LCD_RGBPACK(254, 31, 31)
538 #elif (LCD_DEPTH == 2)
539 #define SLIME_GREEN LCD_LIGHTGRAY
540 #define UFO_RED LCD_LIGHTGRAY
541 #else
542 #error LCD type not implemented yet
543 #endif
545 /* Alien states */
546 #define DEAD 0
547 #define ALIVE 1
548 #define BOMBER 2
550 /* Fire/bomb/ufo states */
551 #define S_IDLE 0
552 #define S_ACTIVE 1
553 #define S_SHOWSCORE 2
554 #define S_EXPLODE -9
556 /* Fire/bomb targets */
557 #define TARGET_TOP 0
558 #define TARGET_SHIELD 1
559 #define TARGET_SHIP 2
560 #define TARGET_BOTTOM 3
561 #define TARGET_UFO 4
563 #define HISCOREFILE PLUGIN_GAMES_DIR "/invadrox.high"
566 /* The time (in ms) for one iteration through the game loop - decrease this
567 * to speed up the game - note that current_tick is (currently) only accurate
568 * to 10ms.
570 #define CYCLETIME 40
573 static struct plugin_api* rb;
575 /* Physical x is at PLAYFIELD_X + LIVES_X + x * ALIEN_SPEED
576 * Physical y is at y * ALIEN_HEIGHT
578 struct alien {
579 unsigned char x; /* x-coordinate (0 - 95) */
580 unsigned char y; /* y-coordinate (0 - 18) */
581 unsigned char type; /* 0 (Kang), 1 (Kodos), 2 (Serak) */
582 unsigned char state; /* Dead, alive or bomber */
585 /* Aliens box 5 rows * ALIENS aliens in each row */
586 struct alien aliens[5 * ALIENS];
588 #define MAX_BOMBS 4
589 struct bomb {
590 int x, y;
591 unsigned char type;
592 unsigned char frame; /* Current animation frame */
593 unsigned char frames; /* Number of frames in animation */
594 unsigned char target; /* Remember target during explosion frames */
595 int state; /* 0 (IDLE) = inactive, 1 (FIRE) or negative, exploding */
597 struct bomb bombs[MAX_BOMBS];
598 /* Increase max_bombs at higher levels */
599 int max_bombs;
601 /* Raw framebuffer value of shield/ship green color */
602 fb_data screen_green, screen_white;
604 /* For optimization, precalculate startoffset of each scanline */
605 unsigned int ytab[LCD_HEIGHT];
607 /* external bitmaps */
608 extern const fb_data invadrox[];
609 #if (LCD_WIDTH == 320) && (LCD_HEIGHT == 240)
610 /* iPod Video only */
611 extern const fb_data invadrox_left[];
612 extern const fb_data invadrox_right[];
613 #endif
614 #if ((LCD_WIDTH == 240) && (LCD_HEIGHT == 320)) || ((LCD_WIDTH == 176) && (LCD_HEIGHT == 220))
615 /* Gigabeat F, Sansa e200 */
616 extern const fb_data invadrox_logo[];
617 #endif
620 int lives = 2;
621 int score = 0;
622 int scores[3] = { 30, 20, 10 };
623 int level = 0;
624 struct highscore hiscore;
625 bool game_over = false;
626 int ship_x, old_ship_x, ship_dir, ship_acc, max_ship_speed;
627 int ship_frame, ship_frame_counter;
628 bool ship_hit;
629 int fire, fire_target, fire_x, fire_y;
630 int curr_alien, aliens_paralyzed, gamespeed;
631 int ufo_state, ufo_x;
632 bool level_finished;
633 bool aliens_down, aliens_right, hit_left_border, hit_right_border;
636 /* No standard get_pixel function yet, use this hack instead */
637 #if (LCD_DEPTH >= 8)
639 inline fb_data get_pixel(int x, int y)
641 return rb->lcd_framebuffer[ytab[y] + x];
644 #elif (LCD_DEPTH == 2)
646 #if (LCD_PIXELFORMAT == HORIZONTAL_PACKING)
647 static const unsigned char shifts[4] = {
648 6, 4, 2, 0
650 /* Horizontal packing */
651 inline fb_data get_pixel(int x, int y)
653 return (rb->lcd_framebuffer[ytab[y] + (x >> 2)] >> shifts[x & 3]) & 3;
655 #else
656 /* Vertical packing */
657 static const unsigned char shifts[4] = {
658 0, 2, 4, 6
660 inline fb_data get_pixel(int x, int y)
662 return (rb->lcd_framebuffer[ytab[y] + x] >> shifts[y & 3]) & 3;
664 #endif /* Horizontal/Vertical packing */
666 #else
667 #error get_pixel: pixelformat not implemented yet
668 #endif
671 /* Draw "digits" least significant digits of num at (x,y) */
672 void draw_number(int x, int y, int num, int digits)
674 int i;
675 int d;
677 for (i = digits - 1; i >= 0; i--) {
678 d = num % 10;
679 num = num / 10;
680 rb->lcd_bitmap_part(invadrox, d * NUMBERS_WIDTH, NUMBERS_SRC_Y,
681 STRIDE, x + i * (NUMBERS_WIDTH + NUM_SPACING), y,
682 NUMBERS_WIDTH, FONT_HEIGHT);
684 /* Update lcd */
685 rb->lcd_update_rect(x, y, 4 * NUMBERS_WIDTH + 3 * NUM_SPACING, FONT_HEIGHT);
689 inline void draw_score(void)
691 draw_number(SCORENUM_X, SCORENUM_Y, score, 4);
692 if (score > hiscore.score) {
693 /* Draw new hiscore (same as score) */
694 draw_number(HISCORENUM_X, SCORENUM_Y, score, 4);
699 void draw_level(void)
701 rb->lcd_bitmap_part(invadrox, 0, LEVEL_SRC_Y,
702 STRIDE, LEVEL_X, PLAYFIELD_Y + 2,
703 LEVEL_WIDTH, FONT_HEIGHT);
704 draw_number(LEVEL_X + LEVEL_WIDTH + 2 * NUM_SPACING, PLAYFIELD_Y + 2, level, 2);
708 void draw_lives(void)
710 int i;
711 /* Lives num */
712 rb->lcd_bitmap_part(invadrox, lives * NUMBERS_WIDTH, NUMBERS_SRC_Y,
713 STRIDE, PLAYFIELD_X + LIVES_X, PLAYFIELD_Y + 2,
714 NUMBERS_WIDTH, FONT_HEIGHT);
716 /* Ships */
717 for (i = 0; i < (lives - 1); i++) {
718 rb->lcd_bitmap_part(invadrox, SHIP_SRC_X, 0, STRIDE,
719 PLAYFIELD_X + LIVES_X + SHIP_WIDTH + i * (SHIP_WIDTH + NUM_SPACING),
720 PLAYFIELD_Y + 1, SHIP_WIDTH, SHIP_HEIGHT);
723 /* Erase ship to the righ (if less than MAX_LIVES) */
724 if (lives < MAX_LIVES) {
725 rb->lcd_fillrect(PLAYFIELD_X + LIVES_X + SHIP_WIDTH + i * (SHIP_WIDTH + NUM_SPACING),
726 PLAYFIELD_Y + 1, SHIP_WIDTH, SHIP_HEIGHT);
728 /* Update lives (and level) part of screen */
729 rb->lcd_update_rect(PLAYFIELD_X + LIVES_X, PLAYFIELD_Y + 1,
730 PLAYFIELD_WIDTH - 2 * LIVES_X, MAX(FONT_HEIGHT + 1, SHIP_HEIGHT + 1));
734 inline void draw_aliens(void)
736 int i;
738 for (i = 0; i < 5 * ALIENS; i++) {
739 rb->lcd_bitmap_part(invadrox, aliens[i].x & 1 ? ALIEN_WIDTH : 0, aliens[i].type * ALIEN_HEIGHT,
740 STRIDE, PLAYFIELD_X + LIVES_X + aliens[i].x * ALIEN_SPEED,
741 ALIEN_START_Y + aliens[i].y * ALIEN_HEIGHT,
742 ALIEN_WIDTH, ALIEN_HEIGHT);
747 /* Return false if there is no next alive alien (round is over) */
748 inline bool next_alien(void)
750 bool ret = true;
752 do {
753 curr_alien++;
754 if (curr_alien % ALIENS == 0) {
755 /* End of this row. Move up one row. */
756 curr_alien -= 2 * ALIENS;
757 if (curr_alien < 0) {
758 /* No more aliens in this round. */
759 curr_alien = 4 * ALIENS;
760 ret = false;
763 } while (aliens[curr_alien].state == DEAD && ret);
765 if (!ret) {
766 /* No more alive aliens. Round finished. */
767 if (hit_right_border) {
768 if (hit_left_border) {
769 DBG("ERROR: both left and right borders are set (%d)\n", curr_alien);
771 /* Move down-left next round */
772 aliens_right = false;
773 aliens_down = true;
774 hit_right_border = false;
775 } else if (hit_left_border) {
776 /* Move down-right next round */
777 aliens_right = true;
778 aliens_down = true;
779 hit_left_border = false;
780 } else {
781 /* Not left nor right. Set down to false. */
782 aliens_down = false;
786 return ret;
790 /* All aliens have been moved.
791 * Set curr_alien to first alive.
792 * Return false if no-one is left alive.
794 bool first_alien(void)
796 int i, y;
798 for (y = 4; y >= 0; y--) {
799 for (i = y * ALIENS; i < (y + 1) * ALIENS; i++) {
800 if (aliens[i].state != DEAD) {
801 curr_alien = i;
802 return true;
807 /* All aliens dead. */
808 level_finished = true;
810 return false;
814 bool move_aliens(void)
816 int x, y, old_x, old_y;
818 /* Move current alien (curr_alien is pointing to a living alien) */
820 old_x = aliens[curr_alien].x;
821 old_y = aliens[curr_alien].y;
823 if (aliens_down) {
824 aliens[curr_alien].y++;
825 if (aliens[curr_alien].y == MAX_Y) {
826 /* Alien is at bottom. Game Over. */
827 DBG("Alien %d is at bottom. Game Over.\n", curr_alien);
828 game_over = true;
829 return false;
833 if (aliens_right) {
834 /* Moving right */
835 if (aliens[curr_alien].x < MAX_X) {
836 aliens[curr_alien].x++;
839 /* Now, after move, check if we hit the right border. */
840 if (aliens[curr_alien].x == MAX_X) {
841 hit_right_border = true;
844 } else {
845 /* Moving left */
846 if (aliens[curr_alien].x > 0) {
847 aliens[curr_alien].x--;
850 /* Now, after move, check if we hit the left border. */
851 if (aliens[curr_alien].x == 0) {
852 hit_left_border = true;
856 /* Erase old position */
857 x = PLAYFIELD_X + LIVES_X + old_x * ALIEN_SPEED;
858 y = ALIEN_START_Y + old_y * ALIEN_HEIGHT;
859 if (aliens[curr_alien].y != old_y) {
860 /* Moved in y-dir. Erase whole alien. */
861 rb->lcd_fillrect(x, y, ALIEN_WIDTH, ALIEN_HEIGHT);
862 } else {
863 if (aliens_right) {
864 /* Erase left edge */
865 rb->lcd_fillrect(x, y, ALIEN_SPEED, ALIEN_HEIGHT);
866 } else {
867 /* Erase right edge */
868 x += ALIEN_WIDTH - ALIEN_SPEED;
869 rb->lcd_fillrect(x, y, ALIEN_SPEED, ALIEN_HEIGHT);
873 /* Draw alien at new pos */
874 x = PLAYFIELD_X + LIVES_X + aliens[curr_alien].x * ALIEN_SPEED;
875 y = ALIEN_START_Y + aliens[curr_alien].y * ALIEN_HEIGHT;
876 rb->lcd_bitmap_part(invadrox,
877 aliens[curr_alien].x & 1 ? ALIEN_WIDTH : 0, aliens[curr_alien].type * ALIEN_HEIGHT,
878 STRIDE, x, y, ALIEN_WIDTH, ALIEN_HEIGHT);
880 if (!next_alien()) {
881 /* Round finished. Set curr_alien to first alive from bottom. */
882 if (!first_alien()) {
883 /* Should never happen. Taken care of in move_fire(). */
884 return false;
886 /* TODO: Play next background sound */
889 return true;
893 inline void draw_ship(void)
895 /* Erase old ship */
896 if (old_ship_x < ship_x) {
897 /* Move right. Erase leftmost part of ship. */
898 rb->lcd_fillrect(old_ship_x, SHIP_Y, ship_x - old_ship_x, SHIP_HEIGHT);
899 } else if (old_ship_x > ship_x) {
900 /* Move left. Erase rightmost part of ship. */
901 rb->lcd_fillrect(ship_x + SHIP_WIDTH, SHIP_Y, old_ship_x - ship_x, SHIP_HEIGHT);
904 /* Draw ship */
905 rb->lcd_bitmap_part(invadrox, SHIP_SRC_X, ship_frame * SHIP_HEIGHT,
906 STRIDE, ship_x, SHIP_Y, SHIP_WIDTH, SHIP_HEIGHT);
907 if (ship_hit) {
908 /* Alternate between frame 1 and 2 during hit */
909 ship_frame_counter++;
910 if (ship_frame_counter > 2) {
911 ship_frame_counter = 0;
912 ship_frame++;
913 if (ship_frame > 2) {
914 ship_frame = 1;
919 /* Save ship_x for next time */
920 old_ship_x = ship_x;
924 inline void fire_alpha(int xc, int yc, fb_data color)
926 int x, y;
927 unsigned char mask;
929 rb->lcd_set_foreground(color);
931 for (y = 0; y < FIRE_HEIGHT; y++) {
932 mask = 1 << (FIRE_WIDTH - 1);
933 for (x = -(FIRE_WIDTH / 2); x < (FIRE_WIDTH / 2); x++) {
934 if (fire_sprite[y] & mask) {
935 rb->lcd_drawpixel(xc + x, yc + y);
937 mask >>= 1;
941 rb->lcd_set_foreground(LCD_BLACK);
945 void move_fire(void)
947 bool hit_green = false;
948 bool hit_white = false;
949 int i, j;
950 static int exploding_alien = -1;
951 fb_data pix;
953 if (fire == S_IDLE) {
954 return;
957 /* Alien hit. Wait until explosion is finished. */
958 if (aliens_paralyzed < 0) {
959 aliens_paralyzed++;
960 if (aliens_paralyzed == 0) {
961 /* Erase exploding_alien */
962 rb->lcd_fillrect(PLAYFIELD_X + LIVES_X + aliens[exploding_alien].x * ALIEN_SPEED,
963 ALIEN_START_Y + aliens[exploding_alien].y * ALIEN_HEIGHT,
964 ALIEN_EXPLODE_WIDTH, ALIEN_HEIGHT);
965 fire = S_IDLE;
966 /* Special case. We killed curr_alien. */
967 if (exploding_alien == curr_alien) {
968 if (!next_alien()) {
969 /* Round finished. Set curr_alien to first alive from bottom. */
970 first_alien();
974 return;
977 if (fire == S_ACTIVE) {
979 /* Erase */
980 rb->lcd_vline(fire_x, fire_y, fire_y + SHOT_HEIGHT);
982 /* Check top */
983 if (fire_y <= SCORENUM_Y + FONT_HEIGHT + 4) {
985 /* TODO: Play explode sound */
987 fire = S_EXPLODE;
988 fire_target = TARGET_TOP;
989 fire_alpha(fire_x, fire_y, UFO_RED);
990 return;
993 /* Move */
994 fire_y -= FIRE_SPEED;
996 /* Hit UFO? */
997 if (ufo_state == S_ACTIVE) {
998 if ((ABS(ufo_x + UFO_WIDTH / 2 - fire_x) <= UFO_WIDTH / 2) &&
999 (fire_y <= UFO_Y + UFO_HEIGHT)) {
1000 ufo_state = S_EXPLODE;
1001 fire = S_EXPLODE;
1002 fire_target = TARGET_UFO;
1003 /* Center explosion */
1004 ufo_x -= (UFO_EXPLODE_WIDTH - UFO_WIDTH) / 2;
1005 rb->lcd_bitmap_part(invadrox, UFO_SRC_X, UFO_HEIGHT,
1006 STRIDE, ufo_x, UFO_Y - 1, UFO_EXPLODE_WIDTH, UFO_EXPLODE_HEIGHT);
1007 return;
1011 /* Hit bomb? (check position, not pixel value) */
1012 for (i = 0; i < max_bombs; i++) {
1013 if (bombs[i].state == S_ACTIVE) {
1014 /* Count as hit if within BOMB_WIDTH pixels */
1015 if ((ABS(bombs[i].x - fire_x) < BOMB_WIDTH) &&
1016 (fire_y - bombs[i].y < BOMB_HEIGHT)) {
1017 /* Erase bomb */
1018 rb->lcd_fillrect(bombs[i].x, bombs[i].y, BOMB_WIDTH, BOMB_HEIGHT);
1019 bombs[i].state = S_IDLE;
1020 /* Explode ship fire */
1021 fire = S_EXPLODE;
1022 fire_target = TARGET_SHIELD;
1023 fire_alpha(fire_x, fire_y, LCD_WHITE);
1024 return;
1029 /* Check for hit*/
1030 for (i = FIRE_SPEED; i >= 0; i--) {
1031 pix = get_pixel(fire_x, fire_y + i);
1032 if(pix == screen_white) {
1033 hit_white = true;
1034 fire_y += i;
1035 break;
1037 if(pix == screen_green) {
1038 hit_green = true;
1039 fire_y += i;
1040 break;
1044 if (hit_green) {
1045 /* Hit shield */
1047 /* TODO: Play explode sound */
1049 fire = S_EXPLODE;
1050 fire_target = TARGET_SHIELD;
1051 /* Center explosion around hit pixel */
1052 fire_y -= FIRE_HEIGHT / 2;
1053 fire_alpha(fire_x, fire_y, SLIME_GREEN);
1054 return;
1057 if (hit_white) {
1059 /* Hit alien? */
1060 for (i = 0; i < 5 * ALIENS; i++) {
1061 if (aliens[i].state != DEAD &&
1062 (ABS(fire_x - (PLAYFIELD_X + LIVES_X + aliens[i].x * ALIEN_SPEED +
1063 ALIEN_WIDTH / 2)) <= ALIEN_WIDTH / 2) &&
1064 (ABS(fire_y - (ALIEN_START_Y + aliens[i].y * ALIEN_HEIGHT +
1065 ALIEN_HEIGHT / 2)) <= ALIEN_HEIGHT / 2)) {
1067 /* TODO: play alien hit sound */
1069 if (aliens[i].state == BOMBER) {
1070 /* Set (possible) alien above to bomber */
1071 for (j = i - ALIENS; j >= 0; j -= ALIENS) {
1072 if (aliens[j].state != DEAD) {
1073 /* printf("New bomber (%d, %d)\n", j % ALIENS, j / ALIENS); */
1074 aliens[j].state = BOMBER;
1075 break;
1079 aliens[i].state = DEAD;
1080 exploding_alien = i;
1081 score += scores[aliens[i].type];
1082 draw_score();
1083 /* Update score part of screen */
1084 rb->lcd_update_rect(SCORENUM_X, SCORENUM_Y,
1085 PLAYFIELD_WIDTH - 2 * NUMBERS_WIDTH, FONT_HEIGHT);
1087 /* Paralyze aliens S_EXPLODE frames */
1088 aliens_paralyzed = S_EXPLODE;
1089 rb->lcd_bitmap_part(invadrox, ALIEN_EXPLODE_SRC_X, ALIEN_EXPLODE_SRC_Y,
1090 STRIDE, PLAYFIELD_X + LIVES_X + aliens[i].x * ALIEN_SPEED,
1091 ALIEN_START_Y + aliens[i].y * ALIEN_HEIGHT,
1092 ALIEN_EXPLODE_WIDTH, ALIEN_EXPLODE_HEIGHT);
1093 /* Since alien is 1 pixel taller than explosion sprite, erase bottom line */
1094 rb->lcd_hline(PLAYFIELD_X + LIVES_X + aliens[i].x * ALIEN_SPEED,
1095 PLAYFIELD_X + LIVES_X + aliens[i].x * ALIEN_SPEED + ALIEN_WIDTH,
1096 ALIEN_START_Y + (aliens[i].y + 1) * ALIEN_HEIGHT - 1);
1097 return;
1102 /* Draw shot */
1103 rb->lcd_set_foreground(LCD_WHITE);
1104 rb->lcd_vline(fire_x, fire_y, fire_y + SHOT_HEIGHT);
1105 rb->lcd_set_foreground(LCD_BLACK);
1106 } else if (fire < S_IDLE) {
1107 /* Count up towards S_IDLE, then erase explosion */
1108 fire++;
1109 if (fire == S_IDLE) {
1110 /* Erase explosion */
1111 if (fire_target == TARGET_TOP) {
1112 rb->lcd_fillrect(fire_x - (FIRE_WIDTH / 2), fire_y, FIRE_WIDTH, FIRE_HEIGHT);
1113 } else if (fire_target == TARGET_SHIELD) {
1114 /* Draw explosion with black pixels */
1115 fire_alpha(fire_x, fire_y, LCD_BLACK);
1122 /* Return a BOMBER alien */
1123 inline int random_bomber(void)
1125 int i, col;
1127 /* TODO: Weigh higher probability near ship */
1128 col = rb->rand() % ALIENS;
1129 for (i = col + 4 * ALIENS; i >= 0; i -= ALIENS) {
1130 if (aliens[i].state == BOMBER) {
1131 return i;
1135 /* No BOMBER found in this col */
1137 for (i = 0; i < 5 * ALIENS; i++) {
1138 if (aliens[i].state == BOMBER) {
1139 return i;
1143 /* No BOMBER found at all (error?) */
1145 return -1;
1149 inline void draw_bomb(int i)
1151 rb->lcd_bitmap_part(invadrox, BOMB_SRC_X + bombs[i].type * BOMB_WIDTH,
1152 bombs[i].frame * (BOMB_HEIGHT + 1),
1153 STRIDE, bombs[i].x, bombs[i].y,
1154 BOMB_WIDTH, BOMB_HEIGHT);
1155 /* Advance frame */
1156 bombs[i].frame++;
1157 if (bombs[i].frame == bombs[i].frames) {
1158 bombs[i].frame = 0;
1163 void move_bombs(void)
1165 int i, j, bomber;
1166 bool abort;
1168 for (i = 0; i < max_bombs; i++) {
1170 switch (bombs[i].state) {
1172 case S_IDLE:
1173 if (ship_hit) {
1174 continue;
1176 bomber = random_bomber();
1177 if (bomber < 0) {
1178 DBG("ERROR: No bomber available\n");
1179 continue;
1181 /* x, y */
1182 bombs[i].x = PLAYFIELD_X + LIVES_X + aliens[bomber].x * ALIEN_SPEED + ALIEN_WIDTH / 2;
1183 bombs[i].y = ALIEN_START_Y + (aliens[bomber].y + 1) * ALIEN_HEIGHT;
1185 /* Check for duplets in x and y direction */
1186 abort = false;
1187 for (j = i - 1; j >= 0; j--) {
1188 if ((bombs[j].state == S_ACTIVE) &&
1189 ((bombs[i].x == bombs[j].x) || (bombs[i].y == bombs[j].y))) {
1190 abort = true;
1191 break;
1194 if (abort) {
1195 /* Skip this one, continue with next bomb */
1196 /* printf("Bomb %d duplet of %d\n", i, j); */
1197 continue;
1200 /* Passed, set type */
1201 bombs[i].type = rb->rand() % 3;
1202 bombs[i].frame = 0;
1203 if (bombs[i].type == 0) {
1204 bombs[i].frames = 3;
1205 } else if (bombs[i].type == 1) {
1206 bombs[i].frames = 4;
1207 } else {
1208 bombs[i].frames = 6;
1211 /* Bombs away */
1212 bombs[i].state = S_ACTIVE;
1213 draw_bomb(i);
1214 continue;
1216 break;
1218 case S_ACTIVE:
1219 /* Erase old position */
1220 rb->lcd_fillrect(bombs[i].x, bombs[i].y, BOMB_WIDTH, BOMB_HEIGHT);
1222 /* Move */
1223 bombs[i].y += BOMB_SPEED;
1225 /* Check if bottom hit */
1226 if (bombs[i].y + BOMB_HEIGHT >= PLAYFIELD_Y) {
1227 bombs[i].y = PLAYFIELD_Y - FIRE_HEIGHT + 1;
1228 fire_alpha(bombs[i].x, bombs[i].y, LCD_WHITE);
1229 bombs[i].state = S_EXPLODE;
1230 bombs[i].target = TARGET_BOTTOM;
1231 break;
1234 /* Check for green (ship or shield) */
1235 for (j = BOMB_HEIGHT; j >= BOMB_HEIGHT - BOMB_SPEED; j--) {
1236 bombs[i].target = 0;
1237 if(get_pixel(bombs[i].x + BOMB_WIDTH / 2, bombs[i].y + j) == screen_green) {
1238 /* Move to hit pixel */
1239 bombs[i].x += BOMB_WIDTH / 2;
1240 bombs[i].y += j;
1242 /* Check if ship is hit */
1243 if (bombs[i].y > SHIELD_Y + SHIELD_HEIGHT && bombs[i].y < PLAYFIELD_Y) {
1245 /* TODO: play ship hit sound */
1247 ship_hit = true;
1248 ship_frame = 1;
1249 ship_frame_counter = 0;
1250 bombs[i].state = S_EXPLODE * 4;
1251 bombs[i].target = TARGET_SHIP;
1252 rb->lcd_bitmap_part(invadrox, SHIP_SRC_X, 1 * SHIP_HEIGHT, STRIDE,
1253 ship_x, SHIP_Y, SHIP_WIDTH, SHIP_HEIGHT);
1254 break;
1256 /* Shield hit */
1257 bombs[i].state = S_EXPLODE;
1258 bombs[i].target = TARGET_SHIELD;
1259 /* Center explosion around hit pixel in shield */
1260 bombs[i].y -= FIRE_HEIGHT / 2;
1261 fire_alpha(bombs[i].x, bombs[i].y, SLIME_GREEN);
1262 break;
1266 if (bombs[i].target != 0) {
1267 /* Hit ship or shield, continue */
1268 continue;
1271 draw_bomb(i);
1272 break;
1274 default:
1275 /* If we get here state should be < 0, exploding */
1276 bombs[i].state++;
1277 if (bombs[i].state == S_IDLE) {
1278 if (ship_hit) {
1279 /* Erase explosion */
1280 rb->lcd_fillrect(ship_x, SHIP_Y, SHIP_WIDTH, SHIP_HEIGHT);
1281 rb->lcd_update_rect(ship_x, SHIP_Y, SHIP_WIDTH, SHIP_HEIGHT);
1282 ship_hit = false;
1283 ship_frame = 0;
1284 ship_x = PLAYFIELD_X + 2 * LIVES_X;
1285 lives--;
1286 if (lives == 0) {
1287 game_over = true;
1288 return;
1290 draw_lives();
1291 /* Sleep 1s to give player time to examine lives left */
1292 rb->sleep(HZ);
1294 /* Erase explosion (even if ship hit, might be another bomb) */
1295 fire_alpha(bombs[i].x, bombs[i].y, LCD_BLACK);
1297 break;
1303 inline void move_ship(void)
1305 ship_dir += ship_acc;
1306 if (ship_dir > max_ship_speed) {
1307 ship_dir = max_ship_speed;
1309 if (ship_dir < -max_ship_speed) {
1310 ship_dir = -max_ship_speed;
1312 ship_x += ship_dir;
1313 if (ship_x < SHIP_MIN_X) {
1314 ship_x = SHIP_MIN_X;
1316 if (ship_x > SHIP_MAX_X) {
1317 ship_x = SHIP_MAX_X;
1320 draw_ship();
1324 /* Unidentified Flying Object */
1325 void move_ufo(void)
1327 static int ufo_speed;
1328 static int counter;
1329 int mystery_score;
1331 switch (ufo_state) {
1333 case S_IDLE:
1335 if (rb->rand() % 500 == 0) {
1336 /* Uh-oh, it's time to launch a mystery UFO */
1338 /* TODO: Play UFO sound */
1340 if (rb->rand() % 2) {
1341 ufo_speed = UFO_SPEED;
1342 ufo_x = PLAYFIELD_X;
1343 } else {
1344 ufo_speed = -UFO_SPEED;
1345 ufo_x = LCD_WIDTH - PLAYFIELD_X - UFO_WIDTH;
1347 ufo_state = S_ACTIVE;
1348 /* UFO will be drawn next frame */
1350 break;
1352 case S_ACTIVE:
1353 /* Erase old pos */
1354 rb->lcd_fillrect(ufo_x, UFO_Y, UFO_WIDTH, UFO_HEIGHT);
1355 /* Move */
1356 ufo_x += ufo_speed;
1357 /* Check bounds */
1358 if (ufo_x < PLAYFIELD_X || ufo_x > LCD_WIDTH - PLAYFIELD_X - UFO_WIDTH) {
1359 ufo_state = S_IDLE;
1360 break;
1362 /* Draw new pos */
1363 rb->lcd_bitmap_part(invadrox, UFO_SRC_X, 0,
1364 STRIDE, ufo_x, UFO_Y, UFO_WIDTH, UFO_HEIGHT);
1365 break;
1367 case S_SHOWSCORE:
1368 counter++;
1369 if (counter == S_IDLE) {
1370 /* Erase mystery number */
1371 rb->lcd_fillrect(ufo_x, UFO_Y, 3 * NUMBERS_WIDTH + 2 * NUM_SPACING, FONT_HEIGHT);
1372 ufo_state = S_IDLE;
1374 break;
1376 default:
1377 /* Exploding */
1378 ufo_state++;
1379 if (ufo_state == S_IDLE) {
1380 /* Erase explosion */
1381 rb->lcd_fillrect(ufo_x, UFO_Y - 1, UFO_EXPLODE_WIDTH, UFO_EXPLODE_HEIGHT);
1382 ufo_state = S_SHOWSCORE;
1383 counter = S_EXPLODE * 4;
1384 /* Draw mystery_score, sleep, increase score and continue */
1385 mystery_score = 50 + (rb->rand() % 6) * 50;
1386 if (mystery_score < 100) {
1387 draw_number(ufo_x, UFO_Y, mystery_score, 2);
1388 } else {
1389 draw_number(ufo_x, UFO_Y, mystery_score, 3);
1391 score += mystery_score;
1392 draw_score();
1394 break;
1399 void draw_background(void)
1402 #if (LCD_WIDTH == 320) && (LCD_HEIGHT == 240)
1403 /* Erase background to black */
1404 rb->lcd_fillrect(PLAYFIELD_X, 0, PLAYFIELD_WIDTH, LCD_HEIGHT);
1405 /* Left and right bitmaps */
1406 rb->lcd_bitmap(invadrox_left, 0, 0, PLAYFIELD_X, LCD_HEIGHT);
1407 rb->lcd_bitmap(invadrox_right, LCD_WIDTH - PLAYFIELD_X, 0, PLAYFIELD_X, LCD_HEIGHT);
1408 #else
1409 rb->lcd_fillrect(0, 0, LCD_WIDTH, LCD_HEIGHT);
1410 #endif
1412 #if ((LCD_WIDTH == 240) && (LCD_HEIGHT == 320)) || ((LCD_WIDTH == 176) && (LCD_HEIGHT == 220))
1413 rb->lcd_bitmap(invadrox_logo, 0, 0, LCD_WIDTH, SCORE_Y);
1414 #endif
1416 rb->lcd_update();
1420 void new_level(void)
1422 int i;
1424 draw_background();
1425 /* Give an extra life for each new level */
1426 if (lives < MAX_LIVES) {
1427 lives++;
1429 draw_lives();
1431 /* Score */
1432 rb->lcd_bitmap_part(invadrox, SCORE_SRC_X, SCORE_SRC_Y,
1433 STRIDE, PLAYFIELD_X, SCORE_Y, SCORE_WIDTH, FONT_HEIGHT);
1434 /* Hi-score */
1435 rb->lcd_bitmap_part(invadrox, 0, SCORE_SRC_Y,
1436 STRIDE, HISCORE_X, SCORE_Y,
1437 HISCORE_WIDTH, FONT_HEIGHT);
1438 draw_score();
1439 draw_number(HISCORENUM_X, SCORENUM_Y, hiscore.score, 4);
1441 level++;
1442 draw_level();
1443 level_finished = false;
1445 ufo_state = S_IDLE;
1447 /* Init alien positions and states */
1448 for (i = 0; i < 4 * ALIENS; i++) {
1449 aliens[i].x = 0 + (i % ALIENS) * ((ALIEN_WIDTH + ALIEN_SPACING) / ALIEN_SPEED);
1450 aliens[i].y = 2 * (i / ALIENS);
1451 aliens[i].state = ALIVE;
1453 /* Last row, bombers */
1454 for (i = 4 * ALIENS; i < 5 * ALIENS; i++) {
1455 aliens[i].x = 0 + (i % ALIENS) * ((ALIEN_WIDTH + ALIEN_SPACING) / ALIEN_SPEED);
1456 aliens[i].y = 2 * (i / ALIENS);
1457 aliens[i].state = BOMBER;
1460 /* Init bombs to inactive (S_IDLE) */
1461 for (i = 0; i < MAX_BOMBS; i++) {
1462 bombs[i].state = S_IDLE;
1465 /* Start aliens closer to earth from level 2 */
1466 for (i = 0; i < 5 * ALIENS; i++) {
1467 if (level < 6) {
1468 aliens[i].y += level - 1;
1469 } else {
1470 aliens[i].y += 5;
1474 /* Max concurrent bombs */
1475 max_bombs = 1;
1477 gamespeed = 2;
1479 if (level > 1) {
1480 max_bombs++;
1483 /* Increase speed */
1484 if (level > 2) {
1485 gamespeed++;
1488 if (level > 3) {
1489 max_bombs++;
1492 /* Increase speed more */
1493 if (level > 4) {
1494 gamespeed++;
1497 if (level > 5) {
1498 max_bombs++;
1501 /* 4 shields */
1502 for (i = 1; i <= 4; i++) {
1503 rb->lcd_bitmap_part(invadrox, SHIELD_SRC_X, SHIELD_SRC_Y, STRIDE,
1504 PLAYFIELD_X + i * PLAYFIELD_WIDTH / 5 - SHIELD_WIDTH / 2,
1505 SHIELD_Y, SHIELD_WIDTH, SHIELD_HEIGHT);
1508 /* Bottom line */
1509 rb->lcd_set_foreground(SLIME_GREEN);
1510 rb->lcd_hline(PLAYFIELD_X, LCD_WIDTH - PLAYFIELD_X, PLAYFIELD_Y);
1511 /* Restore foreground to black (for fast erase later). */
1512 rb->lcd_set_foreground(LCD_BLACK);
1514 ship_x = PLAYFIELD_X + 2 * LIVES_X;
1515 if (level == 1) {
1516 old_ship_x = ship_x;
1518 ship_dir = 0;
1519 ship_acc = 0;
1520 ship_frame = 0;
1521 ship_hit = false;
1522 fire = S_IDLE;
1523 /* Start moving the bottom row left to right */
1524 curr_alien = 4 * ALIENS;
1525 aliens_paralyzed = 0;
1526 aliens_right = true;
1527 aliens_down = false;
1528 hit_left_border = false;
1529 hit_right_border = false;
1530 /* TODO: Change max_ship_speed to 3 at higher levels */
1531 max_ship_speed = 2;
1533 draw_aliens();
1535 rb->lcd_update();
1539 void init_invadrox(void)
1541 int i;
1543 /* Seed random number generator with a "random" number */
1544 rb->srand(rb->get_time()->tm_sec + rb->get_time()->tm_min * 60);
1546 /* Precalculate start of each scanline */
1547 for (i = 0; i < LCD_HEIGHT; i++) {
1548 #if (LCD_DEPTH >= 8)
1549 ytab[i] = i * LCD_WIDTH;
1550 #elif (LCD_DEPTH == 2) && (LCD_PIXELFORMAT == HORIZONTAL_PACKING)
1551 ytab[i] = i * (LCD_WIDTH / 4);
1552 #elif (LCD_DEPTH == 2) && (LCD_PIXELFORMAT == VERTICAL_PACKING)
1553 ytab[i] = (i / 4) * LCD_WIDTH;
1554 #else
1555 #error pixelformat not implemented yet
1556 #endif
1559 rb->lcd_set_background(LCD_BLACK);
1560 rb->lcd_set_foreground(LCD_BLACK);
1562 highscore_init(rb);
1563 if (highscore_load(HISCOREFILE, &hiscore, 1) < 0) {
1564 /* Init hiscore to 0 */
1565 rb->strncpy(hiscore.name, "Invader", sizeof(hiscore.name));
1566 hiscore.score = 0;
1567 hiscore.level = 1;
1570 /* Init alien types in aliens array */
1571 for (i = 0; i < 1 * ALIENS; i++) {
1572 aliens[i].type = 0; /* Kang */
1574 for (; i < 3 * ALIENS; i++) {
1575 aliens[i].type = 1; /* Kodos */
1577 for (; i < 5 * ALIENS; i++) {
1578 aliens[i].type = 2; /* Serak */
1582 /* Save screen white color */
1583 rb->lcd_set_foreground(LCD_WHITE);
1584 rb->lcd_drawpixel(0, 0);
1585 rb->lcd_update_rect(0, 0, 1, 1);
1586 screen_white = get_pixel(0, 0);
1588 /* Save screen green color */
1589 rb->lcd_set_foreground(SLIME_GREEN);
1590 rb->lcd_drawpixel(0, 0);
1591 rb->lcd_update_rect(0, 0, 1, 1);
1592 screen_green = get_pixel(0, 0);
1594 /* Restore black foreground */
1595 rb->lcd_set_foreground(LCD_BLACK);
1597 new_level();
1599 /* Flash score at start */
1600 for (i = 0; i < 5; i++) {
1601 rb->lcd_fillrect(SCORENUM_X, SCORENUM_Y,
1602 4 * NUMBERS_WIDTH + 3 * NUM_SPACING,
1603 FONT_HEIGHT);
1604 rb->lcd_update_rect(SCORENUM_X, SCORENUM_Y,
1605 4 * NUMBERS_WIDTH + 3 * NUM_SPACING,
1606 FONT_HEIGHT);
1607 rb->sleep(HZ / 10);
1608 draw_number(SCORENUM_X, SCORENUM_Y, score, 4);
1609 rb->sleep(HZ / 10);
1614 inline bool handle_buttons(void)
1616 static unsigned int oldbuttonstate = 0;
1618 unsigned int released, pressed, newbuttonstate;
1620 if (ship_hit) {
1621 /* Don't allow ship movement during explosion */
1622 newbuttonstate = 0;
1623 } else {
1624 newbuttonstate = rb->button_status();
1626 if(newbuttonstate == oldbuttonstate) {
1627 if (newbuttonstate == 0) {
1628 /* No button pressed. Stop ship. */
1629 ship_acc = 0;
1630 if (ship_dir > 0) {
1631 ship_dir--;
1633 if (ship_dir < 0) {
1634 ship_dir++;
1637 /* return false; */
1638 goto check_usb;
1640 released = ~newbuttonstate & oldbuttonstate;
1641 pressed = newbuttonstate & ~oldbuttonstate;
1642 oldbuttonstate = newbuttonstate;
1643 if (pressed) {
1644 if (pressed & LEFT) {
1645 if (ship_acc > -1) {
1646 ship_acc--;
1649 if (pressed & RIGHT) {
1650 if (ship_acc < 1) {
1651 ship_acc++;
1654 if (pressed & FIRE) {
1655 if (fire == S_IDLE) {
1656 /* Fire shot */
1657 fire_x = ship_x + SHIP_WIDTH / 2;
1658 fire_y = SHIP_Y - SHOT_HEIGHT;
1659 fire = S_ACTIVE;
1660 /* TODO: play fire sound */
1663 #ifdef RC_QUIT
1664 if (pressed & RC_QUIT) {
1665 rb->splash(HZ * 1, "Quit");
1666 return true;
1668 #endif
1669 if (pressed & QUIT) {
1670 rb->splash(HZ * 1, "Quit");
1671 return true;
1674 if (released) {
1675 if ((released & LEFT)) {
1676 if (ship_acc < 1) {
1677 ship_acc++;
1680 if ((released & RIGHT)) {
1681 if (ship_acc > -1) {
1682 ship_acc--;
1687 check_usb:
1689 /* Quit if USB is connected */
1690 if (rb->button_get(false) == SYS_USB_CONNECTED) {
1691 return true;
1694 return false;
1698 void game_loop(void)
1700 int i, end;
1702 /* Print dimensions (just for debugging) */
1703 DBG("%03dx%03dx%02d\n", LCD_WIDTH, LCD_HEIGHT, LCD_DEPTH);
1705 /* Init */
1706 init_invadrox();
1708 while (1) {
1709 /* Convert CYCLETIME (in ms) to HZ */
1710 end = *rb->current_tick + (CYCLETIME * HZ) / 1000;
1712 if (handle_buttons()) {
1713 return;
1716 /* Animate */
1717 move_ship();
1718 move_fire();
1720 /* Check if level is finished (marked by move_fire) */
1721 if (level_finished) {
1722 /* TODO: Play level finished sound */
1723 new_level();
1726 move_ufo();
1728 /* Move aliens */
1729 if (!aliens_paralyzed && !ship_hit) {
1730 for (i = 0; i < gamespeed; i++) {
1731 if (!move_aliens()) {
1732 if (game_over) {
1733 return;
1739 /* Move alien bombs */
1740 move_bombs();
1741 if (game_over) {
1742 return;
1745 /* Update "playfield" rect */
1746 rb->lcd_update_rect(PLAYFIELD_X, SCORENUM_Y + FONT_HEIGHT,
1747 PLAYFIELD_WIDTH,
1748 PLAYFIELD_Y + 1 - SCORENUM_Y - FONT_HEIGHT);
1750 /* Wait until next frame */
1751 DBG("%d (%d)\n", end - *rb->current_tick, (CYCLETIME * HZ) / 1000);
1752 if (end > *rb->current_tick) {
1753 rb->sleep(end - *rb->current_tick);
1754 } else {
1755 rb->yield();
1758 } /* end while */
1762 /* this is the plugin entry point */
1763 enum plugin_status plugin_start(struct plugin_api* api, UNUSED void* parameter)
1765 rb = api;
1767 rb->lcd_setfont(FONT_SYSFIXED);
1768 /* Turn off backlight timeout */
1769 backlight_force_on(rb); /* backlight control in lib/helper.c */
1771 /* now go ahead and have fun! */
1772 game_loop();
1774 /* Game Over. */
1775 /* TODO: Play game over sound */
1776 rb->splash(HZ * 2, "Game Over");
1777 if (score > hiscore.score) {
1778 /* Save new hiscore */
1779 hiscore.score = score;
1780 hiscore.level = level;
1781 highscore_save(HISCOREFILE, &hiscore, 1);
1784 /* Restore user's original backlight setting */
1785 rb->lcd_setfont(FONT_UI);
1786 /* Turn on backlight timeout (revert to settings) */
1787 backlight_use_settings(rb); /* backlight control in lib/helper.c */
1789 return PLUGIN_OK;
1795 * GNU Emacs settings: Kernighan & Richie coding style with
1796 * 4 spaces indent and no tabs.
1797 * Local Variables:
1798 * c-file-style: "k&r"
1799 * c-basic-offset: 4
1800 * indent-tabs-mode: nil
1801 * End: