Add platform file for Ipod 1G / 2G. Now only the front image is missing for building...
[Rockbox.git] / apps / plugins / invadrox.c
blob62a1c3e8059ff9db6cda07da419d3fccc35fb63b
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"
29 PLUGIN_HEADER
31 /* Original graphics is only 1bpp so it should be portable
32 * to most targets. But for now, only support the simple ones.
34 #ifndef HAVE_LCD_BITMAP
35 #error INVADROX: Unsupported LCD
36 #endif
38 #if (LCD_DEPTH < 2)
39 #error INVADROX: Unsupported LCD
40 #endif
42 /* #define DEBUG */
43 #ifdef DEBUG
44 #include <stdio.h>
45 #define DBG(format, arg...) { printf("%s: " format, __FUNCTION__, ## arg); }
46 #else
47 #define DBG(format, arg...) {}
48 #endif
50 #if CONFIG_KEYPAD == IRIVER_H100_PAD
52 #define QUIT BUTTON_OFF
53 #define LEFT BUTTON_LEFT
54 #define RIGHT BUTTON_RIGHT
55 #define FIRE BUTTON_ON
57 #elif CONFIG_KEYPAD == IRIVER_H300_PAD
59 #define QUIT BUTTON_OFF
60 #define LEFT BUTTON_LEFT
61 #define RIGHT BUTTON_RIGHT
62 #define FIRE BUTTON_SELECT
64 #elif (CONFIG_KEYPAD == IRIVER_H10_PAD)
66 #define QUIT BUTTON_POWER
67 #define LEFT BUTTON_LEFT
68 #define RIGHT BUTTON_RIGHT
69 #define FIRE BUTTON_PLAY
71 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
72 (CONFIG_KEYPAD == IPOD_3G_PAD) || \
73 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
75 #define QUIT BUTTON_MENU
76 #define LEFT BUTTON_LEFT
77 #define RIGHT BUTTON_RIGHT
78 #define FIRE BUTTON_SELECT
80 #elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD
82 #define QUIT BUTTON_POWER
83 #define LEFT BUTTON_LEFT
84 #define RIGHT BUTTON_RIGHT
85 #define FIRE BUTTON_SELECT
87 #elif CONFIG_KEYPAD == GIGABEAT_PAD
89 #define QUIT BUTTON_POWER
90 #define LEFT BUTTON_LEFT
91 #define RIGHT BUTTON_RIGHT
92 #define FIRE BUTTON_SELECT
94 #elif CONFIG_KEYPAD == SANSA_E200_PAD
96 #define QUIT BUTTON_POWER
97 #define LEFT BUTTON_LEFT
98 #define RIGHT BUTTON_RIGHT
99 #define FIRE BUTTON_SELECT
101 #elif CONFIG_KEYPAD == ELIO_TPJ1022_PAD
103 /* TODO: Figure out which buttons to use for Tatung Elio TPJ-1022 */
104 #define QUIT BUTTON_AB
105 #define LEFT BUTTON_LEFT
106 #define RIGHT BUTTON_RIGHT
107 #define FIRE BUTTON_MENU
109 #else
110 #error INVADROX: Unsupported keypad
111 #endif
114 #ifndef UNUSED
115 #define UNUSED __attribute__ ((unused))
116 #endif
118 #ifndef ABS
119 #define ABS(x) (((x) < 0) ? (-(x)) : (x))
120 #endif
123 /* Defines common to all models */
124 #define UFO_Y (SCORENUM_Y + FONT_HEIGHT + ALIEN_HEIGHT)
125 #define PLAYFIELD_Y (LCD_HEIGHT - SHIP_HEIGHT - 2)
126 #define PLAYFIELD_WIDTH (LCD_WIDTH - 2 * PLAYFIELD_X)
127 #define LEVEL_X (LCD_WIDTH - PLAYFIELD_X - LIVES_X - LEVEL_WIDTH - 2 * NUMBERS_WIDTH - 3 * NUM_SPACING)
128 #define SHIP_MIN_X (PLAYFIELD_X + PLAYFIELD_WIDTH / 5 - SHIELD_WIDTH / 2 - SHIP_WIDTH)
129 #define SHIP_MAX_X (PLAYFIELD_X + 4 * PLAYFIELD_WIDTH / 5 + SHIELD_WIDTH / 2)
130 /* SCORE_Y = 0 for most targets. Gigabeat redefines it later. */
131 #define SCORE_Y 0
132 #define MAX_LIVES 8
135 /* iPod Video defines */
136 #if (LCD_WIDTH == 320) && (LCD_HEIGHT == 240)
138 /* Original arcade game size 224x240, 1bpp with
139 * red overlay at top and green overlay at bottom.
141 * iPod Video: 320x240x16
142 * ======================
143 * X: 48p padding at left/right gives 224p playfield in middle.
144 * 10p "border" gives 204p actual playfield. UFO use full 224p.
145 * Y: Use full 240p.
147 * MAX_X = (204 - 12) / 2 - 1 = 95
149 * Y: Score text 7 0
150 * Space 10 7
151 * Score 7 17
152 * Space 8 24
153 * 3 Ufo 7 32
154 * 2 Space Aliens start at 32 + 3 * 8 = 56
155 * 0 aliens 9*8 56 -
156 * space ~7*8 128 | 18.75 aliens space between
157 * shield 2*8 182 | first alien and ship.
158 * space 8 198 | MAX_Y = 18
159 * ship 8 206 -
160 * space 2*8 214
161 * hline 1 230 - PLAYFIELD_Y
162 * bottom border 10 240
163 * Lives and Level goes inside bottom border
166 #define ARCADISH_GRAPHICS
167 #define PLAYFIELD_X 48
168 #define SHIP_Y (PLAYFIELD_Y - 3 * SHIP_HEIGHT)
169 #define ALIEN_START_Y (UFO_Y + 3 * ALIEN_HEIGHT)
170 #define SCORENUM_X (PLAYFIELD_X + NUMBERS_WIDTH)
171 #define SCORENUM_Y SCORE_Y + (2 * (FONT_HEIGHT + 1) + 1)
172 #define HISCORE_X (LCD_WIDTH - PLAYFIELD_X - HISCORE_WIDTH)
173 #define HISCORENUM_X (LCD_WIDTH - PLAYFIELD_X - 1 - 6 * NUMBERS_WIDTH - 5 * NUM_SPACING)
174 #define SHIELD_Y (PLAYFIELD_Y - 6 * SHIP_HEIGHT)
175 #define LIVES_X 10
176 #define MAX_X 95
177 #define MAX_Y 18
180 #elif (LCD_WIDTH == 176) && (LCD_HEIGHT == 220)
182 /* Sandisk Sansa e200: 176x220x16
183 * ==============================
184 * X: No padding. 8p border -> 160p playfield.
186 * 8p Aliens with 3p spacing -> 88 + 30 = 118p aliens block.
187 * (160 - 118) / 2 = 21 rounds for whole block (more than original)
188 * MAX_X = (160 - 8) / 2 - 1 = 75 rounds for single alien (less than original)
190 * LOGO 70 0
191 * Score text 5 70
192 * Space 5 75
193 * Y Score 5 80
194 * Space 10 85
195 * 2 Ufo 5 95
196 * 2 Space 10 100
197 * 0 aliens 9*5 110 -
198 * space ~7*5 155 | 18.6 aliens space between
199 * shield 2*5 188 | first alien and ship.
200 * space 5 198 | MAX_Y = 18
201 * ship 5 203 -
202 * space 5 208
203 * hline 1 213 PLAYFIELD_Y
204 * bottom border 6
205 * LCD_HEIGHT 220
206 * Lives and Level goes inside bottom border
209 #define TINY_GRAPHICS
210 #define PLAYFIELD_X 0
211 #define SHIP_Y (PLAYFIELD_Y - 2 * SHIP_HEIGHT)
212 #define SHIELD_Y (SHIP_Y - SHIP_HEIGHT - SHIELD_HEIGHT)
213 #define ALIEN_START_Y (UFO_Y + 3 * SHIP_HEIGHT)
214 /* Redefine SCORE_Y */
215 #undef SCORE_Y
216 #define SCORE_Y 70
217 #define SCORENUM_X (PLAYFIELD_X + NUMBERS_WIDTH)
218 #define SCORENUM_Y (SCORE_Y + 2 * FONT_HEIGHT)
219 #define HISCORE_X (LCD_WIDTH - PLAYFIELD_X - HISCORE_WIDTH)
220 #define HISCORENUM_X (LCD_WIDTH - PLAYFIELD_X - 1 - 6 * NUMBERS_WIDTH - 5 * NUM_SPACING)
221 #define LIVES_X 8
222 #define MAX_X 75
223 #define MAX_Y 18
226 #elif (LCD_WIDTH == 176) && (LCD_HEIGHT == 132)
228 /* iPod Nano: 176x132x16
229 * ======================
230 * X: No padding. 8p border -> 160p playfield.
232 * LIVES_X 8
233 * ALIEN_WIDTH 8
234 * ALIEN_HEIGHT 5
235 * ALIEN_SPACING 3
236 * SHIP_WIDTH 10
237 * SHIP_HEIGHT 5
238 * FONT_HEIGHT 5
239 * UFO_WIDTH 10
240 * UFO_HEIGHT 5
241 * SHIELD_WIDTH 15
242 * SHIELD_HEIGHT 10
243 * MAX_X 75
244 * MAX_Y = 18
245 * ALIEN_START_Y (UFO_Y + 12)
247 * 8p Aliens with 3p spacing -> 88 + 30 = 118p aliens block.
248 * (160 - 118) / 2 = 21 rounds for whole block (more than original)
249 * MAX_X = (160 - 8) / 2 - 1 = 75 rounds for single alien (less than original)
251 * Y: Scoreline 5 0 (combine scoretext and numbers on same line)
252 * Space 5 5
253 * 1 Ufo 5 10
254 * 3 Space 7 15
255 * 2 aliens 9*5 22 -
256 * space ~7*5 67 | Just above 18 aliens space between
257 * shield 2*5 100 | first alien and ship.
258 * space 5 110 | MAX_Y = 18
259 * ship 5 115 -
260 * space 5 120
261 * hline 1 125 PLAYFIELD_Y
262 * bottom border 6 126
263 * LCD_HEIGHT 131
264 * Lives and Level goes inside bottom border
267 #define TINY_GRAPHICS
268 #define PLAYFIELD_X 0
269 #define SHIP_Y (PLAYFIELD_Y - 2 * SHIP_HEIGHT)
270 #define ALIEN_START_Y (UFO_Y + 12)
271 #define SCORENUM_X (PLAYFIELD_X + SCORE_WIDTH + NUMBERS_WIDTH + NUM_SPACING)
272 #define SCORENUM_Y SCORE_Y
273 #define HISCORENUM_X (LCD_WIDTH - PLAYFIELD_X - 4 * NUMBERS_WIDTH - 3 * NUM_SPACING)
274 #define HISCORE_X (HISCORENUM_X - NUMBERS_WIDTH - NUM_SPACING - HISCORE_WIDTH)
275 #define SHIELD_Y (SHIP_Y - SHIP_HEIGHT - SHIELD_HEIGHT)
276 #define LIVES_X 8
277 #define MAX_X 75
278 #define MAX_Y 18
280 #elif (LCD_WIDTH == 160) && (LCD_HEIGHT == 128)
282 /* iAudio X5, iRiver H10 20Gb, iPod 3g/4g: 160x128x16
283 * ======================================
284 * X: No padding. No border -> 160p playfield.
286 * LIVES_X 0
287 * ALIEN_WIDTH 8
288 * ALIEN_HEIGHT 5
289 * ALIEN_SPACING 3
290 * SHIP_WIDTH 10
291 * SHIP_HEIGHT 5
292 * FONT_HEIGHT 5
293 * UFO_WIDTH 10
294 * UFO_HEIGHT 5
295 * SHIELD_WIDTH 15
296 * SHIELD_HEIGHT 10
297 * MAX_X 75
298 * MAX_Y = 18
299 * ALIEN_START_Y (UFO_Y + 10)
301 * 8p Aliens with 3p spacing -> 88 + 30 = 118p aliens block.
302 * (160 - 118) / 2 = 21 rounds for whole block (more than original)
303 * MAX_X = (160 - 8) / 2 - 1 = 75 rounds for single alien (less than original)
305 * Y: Scoreline 5 0 (combine scoretext and numbers on same line)
306 * Space 5 5
307 * 1 Ufo 5 10
308 * 2 Space 5 15
309 * 8 aliens 9*5 20 -
310 * space ~6*5 65 | Just above 18 aliens space between
311 * shield 2*5 96 | first alien and ship.
312 * space 5 106 | MAX_Y = 18
313 * ship 5 111 -
314 * space 5 116
315 * hline 1 121 PLAYFIELD_Y
316 * bottom border 6 122
317 * LCD_HEIGHT 128
318 * Lives and Level goes inside bottom border
321 #define TINY_GRAPHICS
322 #define PLAYFIELD_X 0
323 #define SHIP_Y (PLAYFIELD_Y - 2 * SHIP_HEIGHT)
324 #define ALIEN_START_Y (UFO_Y + 10)
325 #define SCORENUM_X (PLAYFIELD_X + SCORE_WIDTH + NUMBERS_WIDTH + NUM_SPACING)
326 #define SCORENUM_Y SCORE_Y
327 #define HISCORENUM_X (LCD_WIDTH - PLAYFIELD_X - 4 * NUMBERS_WIDTH - 3 * NUM_SPACING)
328 #define HISCORE_X (HISCORENUM_X - NUMBERS_WIDTH - NUM_SPACING - HISCORE_WIDTH)
329 #define SHIELD_Y (SHIP_Y - SHIP_HEIGHT - SHIELD_HEIGHT)
330 #define LIVES_X 0
331 #define MAX_X 75
332 #define MAX_Y 18
335 #elif (LCD_WIDTH == 240) && (LCD_HEIGHT == 320)
337 /* Gigabeat: 240x320x16
338 * ======================
339 * X: 8p padding at left/right gives 224p playfield in middle.
340 * 10p "border" gives 204p actual playfield. UFO use full 224p.
341 * Y: Use bottom 240p for playfield and top 80 pixels for logo.
343 * MAX_X = (204 - 12) / 2 - 1 = 95
345 * Y: Score text 7 0 + 80
346 * Space 10 7 + 80
347 * Score 7 17 + 80
348 * Space 8 24 + 80
349 * 3 Ufo 7 32 + 80
350 * 2 Space Aliens start at 32 + 3 * 8 = 56
351 * 0 aliens 9*8 56 -
352 * space ~7*8 128 | 18.75 aliens space between
353 * shield 2*8 182 | first alien and ship.
354 * space 8 198 | MAX_Y = 18
355 * ship 8 206 -
356 * space 2*8 214
357 * hline 1 230 310 - PLAYFIELD_Y
358 * bottom border 10 240 320
359 * Lives and Level goes inside bottom border
362 #define ARCADISH_GRAPHICS
363 #define PLAYFIELD_X 8
364 #define SHIP_Y (PLAYFIELD_Y - 3 * SHIP_HEIGHT)
365 #define ALIEN_START_Y (UFO_Y + 3 * ALIEN_HEIGHT)
366 /* Redefine SCORE_Y */
367 #undef SCORE_Y
368 #define SCORE_Y 80
369 #define SCORENUM_X (PLAYFIELD_X + NUMBERS_WIDTH)
370 #define SCORENUM_Y SCORE_Y + (2 * (FONT_HEIGHT + 1) + 1)
371 #define HISCORE_X (LCD_WIDTH - PLAYFIELD_X - HISCORE_WIDTH)
372 #define HISCORENUM_X (LCD_WIDTH - PLAYFIELD_X - 1 - 6 * NUMBERS_WIDTH - 5 * NUM_SPACING)
373 #define SHIELD_Y (PLAYFIELD_Y - 6 * SHIP_HEIGHT)
374 #define LIVES_X 10
375 #define MAX_X 95
376 #define MAX_Y 18
378 #elif (LCD_WIDTH == 220) && (LCD_HEIGHT == 176)
380 /* TPJ1022, H300, iPod Color: 220x176x16
381 * ============================
382 * X: 0p padding at left/right gives 220p playfield in middle.
383 * 8p "border" gives 204p actual playfield. UFO use full 220p.
384 * Y: Use full 176p for playfield.
386 * MAX_X = (204 - 12) / 2 - 1 = 95
388 * Y: Score text 7 0
389 * Space 8 7
390 * 1 Ufo 7 15
391 * 7 Space Aliens start at 15 + 3 * 8 = 56
392 * 6 aliens 9*8 25 -
393 * space ~7*8 103 | 15.6 aliens space between
394 * shield 2*8 126 | first alien and ship.
395 * space 8 142 | MAX_Y = 15
396 * ship 8 150 -
397 * space 8 158
398 * hline 1 166 - PLAYFIELD_Y
399 * bottom border 10 176
400 * Lives and Level goes inside bottom border
403 #define ARCADISH_GRAPHICS
404 #define PLAYFIELD_X 0
405 #define SHIP_Y (PLAYFIELD_Y - 2 * SHIP_HEIGHT)
406 #define ALIEN_START_Y (UFO_Y + 10)
407 #define SCORENUM_Y SCORE_Y
408 #define SCORENUM_X (PLAYFIELD_X + SCORE_WIDTH + NUMBERS_WIDTH + NUM_SPACING)
409 #define HISCORENUM_X (LCD_WIDTH - PLAYFIELD_X - 4 * NUMBERS_WIDTH - 3 * NUM_SPACING)
410 #define HISCORE_X (HISCORENUM_X - NUMBERS_WIDTH - NUM_SPACING - HISCORE_WIDTH)
411 #define SHIELD_Y (PLAYFIELD_Y - 5 * SHIP_HEIGHT)
412 #define LIVES_X 8
413 #define MAX_X 95
414 #define MAX_Y 15
416 #else
417 #error INVADROX: Unsupported LCD type
418 #endif
421 /* Defines common to each "graphic type" */
422 #ifdef ARCADISH_GRAPHICS
424 #define STRIDE 71
425 #define SHIP_SRC_X 24
426 #define SHIP_WIDTH 16
427 #define SHIP_HEIGHT 8
428 #define SHOT_HEIGHT 5
429 #define ALIEN_WIDTH 12
430 #define ALIEN_EXPLODE_SRC_X 52
431 #define ALIEN_EXPLODE_SRC_Y 39
432 #define ALIEN_EXPLODE_WIDTH 13
433 #define ALIEN_EXPLODE_HEIGHT 7
434 #define ALIEN_HEIGHT 8
435 #define ALIEN_SPACING 4
436 #define ALIEN_SPEED 2
437 #define UFO_SRC_X 40
438 #define UFO_WIDTH 16
439 #define UFO_HEIGHT 7
440 #define UFO_EXPLODE_WIDTH 21
441 #define UFO_EXPLODE_HEIGHT 8
442 #define UFO_SPEED 1
443 #define FONT_HEIGHT 7
444 #define LEVEL_SRC_Y 24
445 #define LEVEL_WIDTH 37
446 #define SCORE_SRC_X 24
447 #define SCORE_SRC_Y 31
448 #define SCORE_WIDTH 37
449 #define HISCORE_WIDTH 61
450 #define NUM_SPACING 3
451 #define NUMBERS_SRC_Y 38
452 #define NUMBERS_WIDTH 5
453 #define SHIELD_SRC_X 40
454 #define SHIELD_SRC_Y 15
455 #define SHIELD_WIDTH 22
456 #define SHIELD_HEIGHT 16
457 #define FIRE_WIDTH 8
458 #define FIRE_HEIGHT 8
459 #define FIRE_SPEED 8
460 #define BOMB_SRC_X 62
461 #define BOMB_WIDTH 3
462 #define BOMB_HEIGHT 7
463 #define BOMB_SPEED 3
464 #define ALIENS 11
465 unsigned char fire_sprite[FIRE_HEIGHT] = {
466 (1 << 7) | (0 << 6) | (0 << 5) | (0 << 4) | (1 << 3) | (0 << 2) | (0 << 1) | 1,
467 (0 << 7) | (0 << 6) | (1 << 5) | (0 << 4) | (0 << 3) | (0 << 2) | (1 << 1) | 0,
468 (0 << 7) | (1 << 6) | (1 << 5) | (1 << 4) | (1 << 3) | (1 << 2) | (1 << 1) | 0,
469 (1 << 7) | (1 << 6) | (1 << 5) | (1 << 4) | (1 << 3) | (1 << 2) | (1 << 1) | 1,
470 (0 << 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) | 0,
472 (0 << 7) | (0 << 6) | (1 << 5) | (0 << 4) | (0 << 3) | (1 << 2) | (0 << 1) | 0,
473 (1 << 7) | (0 << 6) | (0 << 5) | (1 << 4) | (0 << 3) | (0 << 2) | (0 << 1) | 1
476 #elif defined TINY_GRAPHICS
478 #define STRIDE 53
479 #define SHIP_SRC_X 16
480 #define SHIP_WIDTH 10
481 #define SHIP_HEIGHT 5
482 #define SHOT_HEIGHT 4
483 #define ALIEN_WIDTH 8
484 #define ALIEN_HEIGHT 5
485 #define ALIEN_EXPLODE_SRC_X 40
486 #define ALIEN_EXPLODE_SRC_Y 26
487 #define ALIEN_EXPLODE_WIDTH 10
488 #define ALIEN_EXPLODE_HEIGHT 5
489 #define ALIEN_SPACING 3
490 #define ALIEN_SPEED 2
491 #define UFO_SRC_X 26
492 #define UFO_WIDTH 11
493 #define UFO_HEIGHT 5
494 #define UFO_EXPLODE_WIDTH 14
495 #define UFO_EXPLODE_HEIGHT 5
496 #define UFO_SPEED 1
497 #define FONT_HEIGHT 5
498 #define LEVEL_SRC_Y 15
499 #define LEVEL_WIDTH 29
500 #define NUMBERS_WIDTH 4
501 #define NUM_SPACING 2
502 #define SCORE_SRC_X 17
503 #define SCORE_SRC_Y 20
504 #define SCORE_WIDTH 28
505 #define HISCORE_WIDTH 45
506 #define NUMBERS_SRC_Y 25
507 #define SHIELD_SRC_X 29
508 #define SHIELD_SRC_Y 10
509 #define SHIELD_WIDTH 15
510 #define SHIELD_HEIGHT 10
511 #define FIRE_WIDTH 6
512 #define FIRE_HEIGHT 6
513 #define FIRE_SPEED 6
514 #define BOMB_SRC_X 44
515 #define BOMB_WIDTH 3
516 #define BOMB_HEIGHT 5
517 #define BOMB_SPEED 2
518 #define ALIENS 11
519 unsigned char fire_sprite[FIRE_HEIGHT] = {
520 (1 << 5) | (0 << 4) | (0 << 3) | (1 << 2) | (0 << 1) | 1,
521 (0 << 5) | (1 << 4) | (0 << 3) | (0 << 2) | (0 << 1) | 0,
522 (0 << 5) | (1 << 4) | (1 << 3) | (1 << 2) | (1 << 1) | 0,
523 (0 << 5) | (1 << 4) | (1 << 3) | (1 << 2) | (1 << 1) | 1,
524 (0 << 5) | (1 << 4) | (0 << 3) | (0 << 2) | (1 << 1) | 0,
525 (1 << 5) | (0 << 4) | (1 << 3) | (0 << 2) | (0 << 1) | 1
528 #else
529 #error Graphic type not defined
530 #endif
533 /* Colors */
534 #if (LCD_DEPTH >= 8)
535 #define SLIME_GREEN LCD_RGBPACK(31, 254, 31)
536 #define UFO_RED LCD_RGBPACK(254, 31, 31)
537 #elif (LCD_DEPTH == 2)
538 #define SLIME_GREEN LCD_LIGHTGRAY
539 #define UFO_RED LCD_LIGHTGRAY
540 #else
541 #error LCD type not implemented yet
542 #endif
544 /* Alien states */
545 #define DEAD 0
546 #define ALIVE 1
547 #define BOMBER 2
549 /* Fire/bomb/ufo states */
550 #define S_IDLE 0
551 #define S_ACTIVE 1
552 #define S_SHOWSCORE 2
553 #define S_EXPLODE -9
555 /* Fire/bomb targets */
556 #define TARGET_TOP 0
557 #define TARGET_SHIELD 1
558 #define TARGET_SHIP 2
559 #define TARGET_BOTTOM 3
560 #define TARGET_UFO 4
562 #define HISCOREFILE "/.rockbox/rocks/invadrox.high"
565 /* The time (in ms) for one iteration through the game loop - decrease this
566 * to speed up the game - note that current_tick is (currently) only accurate
567 * to 10ms.
569 #define CYCLETIME 40
572 static struct plugin_api* rb;
574 /* Physical x is at PLAYFIELD_X + LIVES_X + x * ALIEN_SPEED
575 * Physical y is at y * ALIEN_HEIGHT
577 struct alien {
578 unsigned char x; /* x-coordinate (0 - 95) */
579 unsigned char y; /* y-coordinate (0 - 18) */
580 unsigned char type; /* 0 (Kang), 1 (Kodos), 2 (Serak) */
581 unsigned char state; /* Dead, alive or bomber */
584 /* Aliens box 5 rows * ALIENS aliens in each row */
585 struct alien aliens[5 * ALIENS];
587 #define MAX_BOMBS 4
588 struct bomb {
589 int x, y;
590 unsigned char type;
591 unsigned char frame; /* Current animation frame */
592 unsigned char frames; /* Number of frames in animation */
593 unsigned char target; /* Remember target during explosion frames */
594 int state; /* 0 (IDLE) = inactive, 1 (FIRE) or negative, exploding */
596 struct bomb bombs[MAX_BOMBS];
597 /* Increase max_bombs at higher levels */
598 int max_bombs;
600 /* Raw framebuffer value of shield/ship green color */
601 fb_data screen_green, screen_white;
603 /* For optimization, precalculate startoffset of each scanline */
604 unsigned int ytab[LCD_HEIGHT];
606 /* external bitmaps */
607 extern const fb_data invadrox[];
608 #if (LCD_WIDTH == 320) && (LCD_HEIGHT == 240)
609 /* iPod Video only */
610 extern const fb_data invadrox_left[];
611 extern const fb_data invadrox_right[];
612 #endif
613 #if ((LCD_WIDTH == 240) && (LCD_HEIGHT == 320)) || ((LCD_WIDTH == 176) && (LCD_HEIGHT == 220))
614 /* Gigabeat F, Sansa e200 */
615 extern const fb_data invadrox_logo[];
616 #endif
619 int lives = 2;
620 int score = 0;
621 int scores[3] = { 30, 20, 10 };
622 int level = 0;
623 struct highscore hiscore;
624 bool game_over = false;
625 int ship_x, old_ship_x, ship_dir, ship_acc, max_ship_speed;
626 int ship_frame, ship_frame_counter;
627 bool ship_hit;
628 int fire, fire_target, fire_x, fire_y;
629 int curr_alien, aliens_paralyzed, gamespeed;
630 int ufo_state, ufo_x;
631 bool level_finished;
632 bool aliens_down, aliens_right, hit_left_border, hit_right_border;
635 /* No standard get_pixel function yet, use this hack instead */
636 #if (LCD_DEPTH >= 8)
638 inline fb_data get_pixel(int x, int y)
640 return rb->lcd_framebuffer[ytab[y] + x];
643 #elif (LCD_DEPTH == 2)
645 #if (LCD_PIXELFORMAT == HORIZONTAL_PACKING)
646 static const unsigned char shifts[4] = {
647 6, 4, 2, 0
649 /* Horizontal packing */
650 inline fb_data get_pixel(int x, int y)
652 return (rb->lcd_framebuffer[ytab[y] + (x >> 2)] >> shifts[x & 3]) & 3;
654 #else
655 /* Vertical packing */
656 static const unsigned char shifts[4] = {
657 0, 2, 4, 6
659 inline fb_data get_pixel(int x, int y)
661 return (rb->lcd_framebuffer[ytab[y] + x] >> shifts[y & 3]) & 3;
663 #endif /* Horizontal/Vertical packing */
665 #else
666 #error get_pixel: pixelformat not implemented yet
667 #endif
670 /* Draw "digits" least significant digits of num at (x,y) */
671 void draw_number(int x, int y, int num, int digits)
673 int i;
674 int d;
676 for (i = digits - 1; i >= 0; i--) {
677 d = num % 10;
678 num = num / 10;
679 rb->lcd_bitmap_part(invadrox, d * NUMBERS_WIDTH, NUMBERS_SRC_Y,
680 STRIDE, x + i * (NUMBERS_WIDTH + NUM_SPACING), y,
681 NUMBERS_WIDTH, FONT_HEIGHT);
683 /* Update lcd */
684 rb->lcd_update_rect(x, y, 4 * NUMBERS_WIDTH + 3 * NUM_SPACING, FONT_HEIGHT);
688 inline void draw_score(void)
690 draw_number(SCORENUM_X, SCORENUM_Y, score, 4);
691 if (score > hiscore.score) {
692 /* Draw new hiscore (same as score) */
693 draw_number(HISCORENUM_X, SCORENUM_Y, score, 4);
698 void draw_level(void)
700 rb->lcd_bitmap_part(invadrox, 0, LEVEL_SRC_Y,
701 STRIDE, LEVEL_X, PLAYFIELD_Y + 2,
702 LEVEL_WIDTH, FONT_HEIGHT);
703 draw_number(LEVEL_X + LEVEL_WIDTH + 2 * NUM_SPACING, PLAYFIELD_Y + 2, level, 2);
707 void draw_lives(void)
709 int i;
710 /* Lives num */
711 rb->lcd_bitmap_part(invadrox, lives * NUMBERS_WIDTH, NUMBERS_SRC_Y,
712 STRIDE, PLAYFIELD_X + LIVES_X, PLAYFIELD_Y + 2,
713 NUMBERS_WIDTH, FONT_HEIGHT);
715 /* Ships */
716 for (i = 0; i < (lives - 1); i++) {
717 rb->lcd_bitmap_part(invadrox, SHIP_SRC_X, 0, STRIDE,
718 PLAYFIELD_X + LIVES_X + SHIP_WIDTH + i * (SHIP_WIDTH + NUM_SPACING),
719 PLAYFIELD_Y + 1, SHIP_WIDTH, SHIP_HEIGHT);
722 /* Erase ship to the righ (if less than MAX_LIVES) */
723 if (lives < MAX_LIVES) {
724 rb->lcd_fillrect(PLAYFIELD_X + LIVES_X + SHIP_WIDTH + i * (SHIP_WIDTH + NUM_SPACING),
725 PLAYFIELD_Y + 1, SHIP_WIDTH, SHIP_HEIGHT);
727 /* Update lives (and level) part of screen */
728 rb->lcd_update_rect(PLAYFIELD_X + LIVES_X, PLAYFIELD_Y + 1,
729 PLAYFIELD_WIDTH - 2 * LIVES_X, MAX(FONT_HEIGHT + 1, SHIP_HEIGHT + 1));
733 inline void draw_aliens(void)
735 int i;
737 for (i = 0; i < 5 * ALIENS; i++) {
738 rb->lcd_bitmap_part(invadrox, aliens[i].x & 1 ? ALIEN_WIDTH : 0, aliens[i].type * ALIEN_HEIGHT,
739 STRIDE, PLAYFIELD_X + LIVES_X + aliens[i].x * ALIEN_SPEED,
740 ALIEN_START_Y + aliens[i].y * ALIEN_HEIGHT,
741 ALIEN_WIDTH, ALIEN_HEIGHT);
746 /* Return false if there is no next alive alien (round is over) */
747 inline bool next_alien(void)
749 bool ret = true;
751 do {
752 curr_alien++;
753 if (curr_alien % ALIENS == 0) {
754 /* End of this row. Move up one row. */
755 curr_alien -= 2 * ALIENS;
756 if (curr_alien < 0) {
757 /* No more aliens in this round. */
758 curr_alien = 4 * ALIENS;
759 ret = false;
762 } while (aliens[curr_alien].state == DEAD && ret);
764 if (!ret) {
765 /* No more alive aliens. Round finished. */
766 if (hit_right_border) {
767 if (hit_left_border) {
768 DBG("ERROR: both left and right borders are set (%d)\n", curr_alien);
770 /* Move down-left next round */
771 aliens_right = false;
772 aliens_down = true;
773 hit_right_border = false;
774 } else if (hit_left_border) {
775 /* Move down-right next round */
776 aliens_right = true;
777 aliens_down = true;
778 hit_left_border = false;
779 } else {
780 /* Not left nor right. Set down to false. */
781 aliens_down = false;
785 return ret;
789 /* All aliens have been moved.
790 * Set curr_alien to first alive.
791 * Return false if no-one is left alive.
793 bool first_alien(void)
795 int i, y;
797 for (y = 4; y >= 0; y--) {
798 for (i = y * ALIENS; i < (y + 1) * ALIENS; i++) {
799 if (aliens[i].state != DEAD) {
800 curr_alien = i;
801 return true;
806 /* All aliens dead. */
807 level_finished = true;
809 return false;
813 bool move_aliens(void)
815 int x, y, old_x, old_y;
817 /* Move current alien (curr_alien is pointing to a living alien) */
819 old_x = aliens[curr_alien].x;
820 old_y = aliens[curr_alien].y;
822 if (aliens_down) {
823 aliens[curr_alien].y++;
824 if (aliens[curr_alien].y == MAX_Y) {
825 /* Alien is at bottom. Game Over. */
826 DBG("Alien %d is at bottom. Game Over.\n", curr_alien);
827 game_over = true;
828 return false;
832 if (aliens_right) {
833 /* Moving right */
834 if (aliens[curr_alien].x < MAX_X) {
835 aliens[curr_alien].x++;
838 /* Now, after move, check if we hit the right border. */
839 if (aliens[curr_alien].x == MAX_X) {
840 hit_right_border = true;
843 } else {
844 /* Moving left */
845 if (aliens[curr_alien].x > 0) {
846 aliens[curr_alien].x--;
849 /* Now, after move, check if we hit the left border. */
850 if (aliens[curr_alien].x == 0) {
851 hit_left_border = true;
855 /* Erase old position */
856 x = PLAYFIELD_X + LIVES_X + old_x * ALIEN_SPEED;
857 y = ALIEN_START_Y + old_y * ALIEN_HEIGHT;
858 if (aliens[curr_alien].y != old_y) {
859 /* Moved in y-dir. Erase whole alien. */
860 rb->lcd_fillrect(x, y, ALIEN_WIDTH, ALIEN_HEIGHT);
861 } else {
862 if (aliens_right) {
863 /* Erase left edge */
864 rb->lcd_fillrect(x, y, ALIEN_SPEED, ALIEN_HEIGHT);
865 } else {
866 /* Erase right edge */
867 x += ALIEN_WIDTH - ALIEN_SPEED;
868 rb->lcd_fillrect(x, y, ALIEN_SPEED, ALIEN_HEIGHT);
872 /* Draw alien at new pos */
873 x = PLAYFIELD_X + LIVES_X + aliens[curr_alien].x * ALIEN_SPEED;
874 y = ALIEN_START_Y + aliens[curr_alien].y * ALIEN_HEIGHT;
875 rb->lcd_bitmap_part(invadrox,
876 aliens[curr_alien].x & 1 ? ALIEN_WIDTH : 0, aliens[curr_alien].type * ALIEN_HEIGHT,
877 STRIDE, x, y, ALIEN_WIDTH, ALIEN_HEIGHT);
879 if (!next_alien()) {
880 /* Round finished. Set curr_alien to first alive from bottom. */
881 if (!first_alien()) {
882 /* Should never happen. Taken care of in move_fire(). */
883 return false;
885 /* TODO: Play next background sound */
888 return true;
892 inline void draw_ship(void)
894 /* Erase old ship */
895 if (old_ship_x < ship_x) {
896 /* Move right. Erase leftmost part of ship. */
897 rb->lcd_fillrect(old_ship_x, SHIP_Y, ship_x - old_ship_x, SHIP_HEIGHT);
898 } else if (old_ship_x > ship_x) {
899 /* Move left. Erase rightmost part of ship. */
900 rb->lcd_fillrect(ship_x + SHIP_WIDTH, SHIP_Y, old_ship_x - ship_x, SHIP_HEIGHT);
903 /* Draw ship */
904 rb->lcd_bitmap_part(invadrox, SHIP_SRC_X, ship_frame * SHIP_HEIGHT,
905 STRIDE, ship_x, SHIP_Y, SHIP_WIDTH, SHIP_HEIGHT);
906 if (ship_hit) {
907 /* Alternate between frame 1 and 2 during hit */
908 ship_frame_counter++;
909 if (ship_frame_counter > 2) {
910 ship_frame_counter = 0;
911 ship_frame++;
912 if (ship_frame > 2) {
913 ship_frame = 1;
918 /* Save ship_x for next time */
919 old_ship_x = ship_x;
923 inline void fire_alpha(int xc, int yc, fb_data color)
925 int x, y;
926 unsigned char mask;
928 rb->lcd_set_foreground(color);
930 for (y = 0; y < FIRE_HEIGHT; y++) {
931 mask = 1 << (FIRE_WIDTH - 1);
932 for (x = -(FIRE_WIDTH / 2); x < (FIRE_WIDTH / 2); x++) {
933 if (fire_sprite[y] & mask) {
934 rb->lcd_drawpixel(xc + x, yc + y);
936 mask >>= 1;
940 rb->lcd_set_foreground(LCD_BLACK);
944 void move_fire(void)
946 bool hit_green = false;
947 bool hit_white = false;
948 int i, j;
949 static int exploding_alien = -1;
950 fb_data pix;
952 if (fire == S_IDLE) {
953 return;
956 /* Alien hit. Wait until explosion is finished. */
957 if (aliens_paralyzed < 0) {
958 aliens_paralyzed++;
959 if (aliens_paralyzed == 0) {
960 /* Erase exploding_alien */
961 rb->lcd_fillrect(PLAYFIELD_X + LIVES_X + aliens[exploding_alien].x * ALIEN_SPEED,
962 ALIEN_START_Y + aliens[exploding_alien].y * ALIEN_HEIGHT,
963 ALIEN_EXPLODE_WIDTH, ALIEN_HEIGHT);
964 fire = S_IDLE;
965 /* Special case. We killed curr_alien. */
966 if (exploding_alien == curr_alien) {
967 if (!next_alien()) {
968 /* Round finished. Set curr_alien to first alive from bottom. */
969 first_alien();
973 return;
976 if (fire == S_ACTIVE) {
978 /* Erase */
979 rb->lcd_vline(fire_x, fire_y, fire_y + SHOT_HEIGHT);
981 /* Check top */
982 if (fire_y <= SCORENUM_Y + FONT_HEIGHT + 4) {
984 /* TODO: Play explode sound */
986 fire = S_EXPLODE;
987 fire_target = TARGET_TOP;
988 fire_alpha(fire_x, fire_y, UFO_RED);
989 return;
992 /* Move */
993 fire_y -= FIRE_SPEED;
995 /* Hit UFO? */
996 if (ufo_state == S_ACTIVE) {
997 if ((ABS(ufo_x + UFO_WIDTH / 2 - fire_x) <= UFO_WIDTH / 2) &&
998 (fire_y <= UFO_Y + UFO_HEIGHT)) {
999 ufo_state = S_EXPLODE;
1000 fire = S_EXPLODE;
1001 fire_target = TARGET_UFO;
1002 /* Center explosion */
1003 ufo_x -= (UFO_EXPLODE_WIDTH - UFO_WIDTH) / 2;
1004 rb->lcd_bitmap_part(invadrox, UFO_SRC_X, UFO_HEIGHT,
1005 STRIDE, ufo_x, UFO_Y - 1, UFO_EXPLODE_WIDTH, UFO_EXPLODE_HEIGHT);
1006 return;
1010 /* Hit bomb? (check position, not pixel value) */
1011 for (i = 0; i < max_bombs; i++) {
1012 if (bombs[i].state == S_ACTIVE) {
1013 /* Count as hit if within BOMB_WIDTH pixels */
1014 if ((ABS(bombs[i].x - fire_x) < BOMB_WIDTH) &&
1015 (fire_y - bombs[i].y < BOMB_HEIGHT)) {
1016 /* Erase bomb */
1017 rb->lcd_fillrect(bombs[i].x, bombs[i].y, BOMB_WIDTH, BOMB_HEIGHT);
1018 bombs[i].state = S_IDLE;
1019 /* Explode ship fire */
1020 fire = S_EXPLODE;
1021 fire_target = TARGET_SHIELD;
1022 fire_alpha(fire_x, fire_y, LCD_WHITE);
1023 return;
1028 /* Check for hit*/
1029 for (i = FIRE_SPEED; i >= 0; i--) {
1030 pix = get_pixel(fire_x, fire_y + i);
1031 if(pix == screen_white) {
1032 hit_white = true;
1033 fire_y += i;
1034 break;
1036 if(pix == screen_green) {
1037 hit_green = true;
1038 fire_y += i;
1039 break;
1043 if (hit_green) {
1044 /* Hit shield */
1046 /* TODO: Play explode sound */
1048 fire = S_EXPLODE;
1049 fire_target = TARGET_SHIELD;
1050 /* Center explosion around hit pixel */
1051 fire_y -= FIRE_HEIGHT / 2;
1052 fire_alpha(fire_x, fire_y, SLIME_GREEN);
1053 return;
1056 if (hit_white) {
1058 /* Hit alien? */
1059 for (i = 0; i < 5 * ALIENS; i++) {
1060 if (aliens[i].state != DEAD &&
1061 (ABS(fire_x - (PLAYFIELD_X + LIVES_X + aliens[i].x * ALIEN_SPEED +
1062 ALIEN_WIDTH / 2)) <= ALIEN_WIDTH / 2) &&
1063 (ABS(fire_y - (ALIEN_START_Y + aliens[i].y * ALIEN_HEIGHT +
1064 ALIEN_HEIGHT / 2)) <= ALIEN_HEIGHT / 2)) {
1066 /* TODO: play alien hit sound */
1068 if (aliens[i].state == BOMBER) {
1069 /* Set (possible) alien above to bomber */
1070 for (j = i - ALIENS; j >= 0; j -= ALIENS) {
1071 if (aliens[j].state != DEAD) {
1072 /* printf("New bomber (%d, %d)\n", j % ALIENS, j / ALIENS); */
1073 aliens[j].state = BOMBER;
1074 break;
1078 aliens[i].state = DEAD;
1079 exploding_alien = i;
1080 score += scores[aliens[i].type];
1081 draw_score();
1082 /* Update score part of screen */
1083 rb->lcd_update_rect(SCORENUM_X, SCORENUM_Y,
1084 PLAYFIELD_WIDTH - 2 * NUMBERS_WIDTH, FONT_HEIGHT);
1086 /* Paralyze aliens S_EXPLODE frames */
1087 aliens_paralyzed = S_EXPLODE;
1088 rb->lcd_bitmap_part(invadrox, ALIEN_EXPLODE_SRC_X, ALIEN_EXPLODE_SRC_Y,
1089 STRIDE, PLAYFIELD_X + LIVES_X + aliens[i].x * ALIEN_SPEED,
1090 ALIEN_START_Y + aliens[i].y * ALIEN_HEIGHT,
1091 ALIEN_EXPLODE_WIDTH, ALIEN_EXPLODE_HEIGHT);
1092 /* Since alien is 1 pixel taller than explosion sprite, erase bottom line */
1093 rb->lcd_hline(PLAYFIELD_X + LIVES_X + aliens[i].x * ALIEN_SPEED,
1094 PLAYFIELD_X + LIVES_X + aliens[i].x * ALIEN_SPEED + ALIEN_WIDTH,
1095 ALIEN_START_Y + (aliens[i].y + 1) * ALIEN_HEIGHT - 1);
1096 return;
1101 /* Draw shot */
1102 rb->lcd_set_foreground(LCD_WHITE);
1103 rb->lcd_vline(fire_x, fire_y, fire_y + SHOT_HEIGHT);
1104 rb->lcd_set_foreground(LCD_BLACK);
1105 } else if (fire < S_IDLE) {
1106 /* Count up towards S_IDLE, then erase explosion */
1107 fire++;
1108 if (fire == S_IDLE) {
1109 /* Erase explosion */
1110 if (fire_target == TARGET_TOP) {
1111 rb->lcd_fillrect(fire_x - (FIRE_WIDTH / 2), fire_y, FIRE_WIDTH, FIRE_HEIGHT);
1112 } else if (fire_target == TARGET_SHIELD) {
1113 /* Draw explosion with black pixels */
1114 fire_alpha(fire_x, fire_y, LCD_BLACK);
1121 /* Return a BOMBER alien */
1122 inline int random_bomber(void)
1124 int i, col;
1126 /* TODO: Weigh higher probability near ship */
1127 col = rb->rand() % ALIENS;
1128 for (i = col + 4 * ALIENS; i >= 0; i -= ALIENS) {
1129 if (aliens[i].state == BOMBER) {
1130 return i;
1134 /* No BOMBER found in this col */
1136 for (i = 0; i < 5 * ALIENS; i++) {
1137 if (aliens[i].state == BOMBER) {
1138 return i;
1142 /* No BOMBER found at all (error?) */
1144 return -1;
1148 inline void draw_bomb(int i)
1150 rb->lcd_bitmap_part(invadrox, BOMB_SRC_X + bombs[i].type * BOMB_WIDTH,
1151 bombs[i].frame * (BOMB_HEIGHT + 1),
1152 STRIDE, bombs[i].x, bombs[i].y,
1153 BOMB_WIDTH, BOMB_HEIGHT);
1154 /* Advance frame */
1155 bombs[i].frame++;
1156 if (bombs[i].frame == bombs[i].frames) {
1157 bombs[i].frame = 0;
1162 void move_bombs(void)
1164 int i, j, bomber;
1165 bool abort;
1167 for (i = 0; i < max_bombs; i++) {
1169 switch (bombs[i].state) {
1171 case S_IDLE:
1172 if (ship_hit) {
1173 continue;
1175 bomber = random_bomber();
1176 if (bomber < 0) {
1177 DBG("ERROR: No bomber available\n");
1178 continue;
1180 /* x, y */
1181 bombs[i].x = PLAYFIELD_X + LIVES_X + aliens[bomber].x * ALIEN_SPEED + ALIEN_WIDTH / 2;
1182 bombs[i].y = ALIEN_START_Y + (aliens[bomber].y + 1) * ALIEN_HEIGHT;
1184 /* Check for duplets in x and y direction */
1185 abort = false;
1186 for (j = i - 1; j >= 0; j--) {
1187 if ((bombs[j].state == S_ACTIVE) &&
1188 ((bombs[i].x == bombs[j].x) || (bombs[i].y == bombs[j].y))) {
1189 abort = true;
1190 break;
1193 if (abort) {
1194 /* Skip this one, continue with next bomb */
1195 /* printf("Bomb %d duplet of %d\n", i, j); */
1196 continue;
1199 /* Passed, set type */
1200 bombs[i].type = rb->rand() % 3;
1201 bombs[i].frame = 0;
1202 if (bombs[i].type == 0) {
1203 bombs[i].frames = 3;
1204 } else if (bombs[i].type == 1) {
1205 bombs[i].frames = 4;
1206 } else {
1207 bombs[i].frames = 6;
1210 /* Bombs away */
1211 bombs[i].state = S_ACTIVE;
1212 draw_bomb(i);
1213 continue;
1215 break;
1217 case S_ACTIVE:
1218 /* Erase old position */
1219 rb->lcd_fillrect(bombs[i].x, bombs[i].y, BOMB_WIDTH, BOMB_HEIGHT);
1221 /* Move */
1222 bombs[i].y += BOMB_SPEED;
1224 /* Check if bottom hit */
1225 if (bombs[i].y + BOMB_HEIGHT >= PLAYFIELD_Y) {
1226 bombs[i].y = PLAYFIELD_Y - FIRE_HEIGHT + 1;
1227 fire_alpha(bombs[i].x, bombs[i].y, LCD_WHITE);
1228 bombs[i].state = S_EXPLODE;
1229 bombs[i].target = TARGET_BOTTOM;
1230 break;
1233 /* Check for green (ship or shield) */
1234 for (j = BOMB_HEIGHT; j >= BOMB_HEIGHT - BOMB_SPEED; j--) {
1235 bombs[i].target = 0;
1236 if(get_pixel(bombs[i].x + BOMB_WIDTH / 2, bombs[i].y + j) == screen_green) {
1237 /* Move to hit pixel */
1238 bombs[i].x += BOMB_WIDTH / 2;
1239 bombs[i].y += j;
1241 /* Check if ship is hit */
1242 if (bombs[i].y > SHIELD_Y + SHIELD_HEIGHT && bombs[i].y < PLAYFIELD_Y) {
1244 /* TODO: play ship hit sound */
1246 ship_hit = true;
1247 ship_frame = 1;
1248 ship_frame_counter = 0;
1249 bombs[i].state = S_EXPLODE * 4;
1250 bombs[i].target = TARGET_SHIP;
1251 rb->lcd_bitmap_part(invadrox, SHIP_SRC_X, 1 * SHIP_HEIGHT, STRIDE,
1252 ship_x, SHIP_Y, SHIP_WIDTH, SHIP_HEIGHT);
1253 break;
1255 /* Shield hit */
1256 bombs[i].state = S_EXPLODE;
1257 bombs[i].target = TARGET_SHIELD;
1258 /* Center explosion around hit pixel in shield */
1259 bombs[i].y -= FIRE_HEIGHT / 2;
1260 fire_alpha(bombs[i].x, bombs[i].y, SLIME_GREEN);
1261 break;
1265 if (bombs[i].target != 0) {
1266 /* Hit ship or shield, continue */
1267 continue;
1270 draw_bomb(i);
1271 break;
1273 default:
1274 /* If we get here state should be < 0, exploding */
1275 bombs[i].state++;
1276 if (bombs[i].state == S_IDLE) {
1277 if (ship_hit) {
1278 /* Erase explosion */
1279 rb->lcd_fillrect(ship_x, SHIP_Y, SHIP_WIDTH, SHIP_HEIGHT);
1280 rb->lcd_update_rect(ship_x, SHIP_Y, SHIP_WIDTH, SHIP_HEIGHT);
1281 ship_hit = false;
1282 ship_frame = 0;
1283 ship_x = PLAYFIELD_X + 2 * LIVES_X;
1284 lives--;
1285 if (lives == 0) {
1286 game_over = true;
1287 return;
1289 draw_lives();
1290 /* Sleep 1s to give player time to examine lives left */
1291 rb->sleep(HZ);
1293 /* Erase explosion (even if ship hit, might be another bomb) */
1294 fire_alpha(bombs[i].x, bombs[i].y, LCD_BLACK);
1296 break;
1302 inline void move_ship(void)
1304 ship_dir += ship_acc;
1305 if (ship_dir > max_ship_speed) {
1306 ship_dir = max_ship_speed;
1308 if (ship_dir < -max_ship_speed) {
1309 ship_dir = -max_ship_speed;
1311 ship_x += ship_dir;
1312 if (ship_x < SHIP_MIN_X) {
1313 ship_x = SHIP_MIN_X;
1315 if (ship_x > SHIP_MAX_X) {
1316 ship_x = SHIP_MAX_X;
1319 draw_ship();
1323 /* Unidentified Flying Object */
1324 void move_ufo(void)
1326 static int ufo_speed;
1327 static int counter;
1328 int mystery_score;
1330 switch (ufo_state) {
1332 case S_IDLE:
1334 if (rb->rand() % 500 == 0) {
1335 /* Uh-oh, it's time to launch a mystery UFO */
1337 /* TODO: Play UFO sound */
1339 if (rb->rand() % 2) {
1340 ufo_speed = UFO_SPEED;
1341 ufo_x = PLAYFIELD_X;
1342 } else {
1343 ufo_speed = -UFO_SPEED;
1344 ufo_x = LCD_WIDTH - PLAYFIELD_X - UFO_WIDTH;
1346 ufo_state = S_ACTIVE;
1347 /* UFO will be drawn next frame */
1349 break;
1351 case S_ACTIVE:
1352 /* Erase old pos */
1353 rb->lcd_fillrect(ufo_x, UFO_Y, UFO_WIDTH, UFO_HEIGHT);
1354 /* Move */
1355 ufo_x += ufo_speed;
1356 /* Check bounds */
1357 if (ufo_x < PLAYFIELD_X || ufo_x > LCD_WIDTH - PLAYFIELD_X - UFO_WIDTH) {
1358 ufo_state = S_IDLE;
1359 break;
1361 /* Draw new pos */
1362 rb->lcd_bitmap_part(invadrox, UFO_SRC_X, 0,
1363 STRIDE, ufo_x, UFO_Y, UFO_WIDTH, UFO_HEIGHT);
1364 break;
1366 case S_SHOWSCORE:
1367 counter++;
1368 if (counter == S_IDLE) {
1369 /* Erase mystery number */
1370 rb->lcd_fillrect(ufo_x, UFO_Y, 3 * NUMBERS_WIDTH + 2 * NUM_SPACING, FONT_HEIGHT);
1371 ufo_state = S_IDLE;
1373 break;
1375 default:
1376 /* Exploding */
1377 ufo_state++;
1378 if (ufo_state == S_IDLE) {
1379 /* Erase explosion */
1380 rb->lcd_fillrect(ufo_x, UFO_Y - 1, UFO_EXPLODE_WIDTH, UFO_EXPLODE_HEIGHT);
1381 ufo_state = S_SHOWSCORE;
1382 counter = S_EXPLODE * 4;
1383 /* Draw mystery_score, sleep, increase score and continue */
1384 mystery_score = 50 + (rb->rand() % 6) * 50;
1385 if (mystery_score < 100) {
1386 draw_number(ufo_x, UFO_Y, mystery_score, 2);
1387 } else {
1388 draw_number(ufo_x, UFO_Y, mystery_score, 3);
1390 score += mystery_score;
1391 draw_score();
1393 break;
1398 void draw_background(void)
1401 #if (LCD_WIDTH == 320) && (LCD_HEIGHT == 240)
1402 /* Erase background to black */
1403 rb->lcd_fillrect(PLAYFIELD_X, 0, PLAYFIELD_WIDTH, LCD_HEIGHT);
1404 /* Left and right bitmaps */
1405 rb->lcd_bitmap(invadrox_left, 0, 0, PLAYFIELD_X, LCD_HEIGHT);
1406 rb->lcd_bitmap(invadrox_right, LCD_WIDTH - PLAYFIELD_X, 0, PLAYFIELD_X, LCD_HEIGHT);
1407 #else
1408 rb->lcd_fillrect(0, 0, LCD_WIDTH, LCD_HEIGHT);
1409 #endif
1411 #if ((LCD_WIDTH == 240) && (LCD_HEIGHT == 320)) || ((LCD_WIDTH == 176) && (LCD_HEIGHT == 220))
1412 rb->lcd_bitmap(invadrox_logo, 0, 0, LCD_WIDTH, SCORE_Y);
1413 #endif
1415 rb->lcd_update();
1419 void new_level(void)
1421 int i;
1423 draw_background();
1424 /* Give an extra life for each new level */
1425 if (lives < MAX_LIVES) {
1426 lives++;
1428 draw_lives();
1430 /* Score */
1431 rb->lcd_bitmap_part(invadrox, SCORE_SRC_X, SCORE_SRC_Y,
1432 STRIDE, PLAYFIELD_X, SCORE_Y, SCORE_WIDTH, FONT_HEIGHT);
1433 /* Hi-score */
1434 rb->lcd_bitmap_part(invadrox, 0, SCORE_SRC_Y,
1435 STRIDE, HISCORE_X, SCORE_Y,
1436 HISCORE_WIDTH, FONT_HEIGHT);
1437 draw_score();
1438 draw_number(HISCORENUM_X, SCORENUM_Y, hiscore.score, 4);
1440 level++;
1441 draw_level();
1442 level_finished = false;
1444 ufo_state = S_IDLE;
1446 /* Init alien positions and states */
1447 for (i = 0; i < 4 * ALIENS; i++) {
1448 aliens[i].x = 0 + (i % ALIENS) * ((ALIEN_WIDTH + ALIEN_SPACING) / ALIEN_SPEED);
1449 aliens[i].y = 2 * (i / ALIENS);
1450 aliens[i].state = ALIVE;
1452 /* Last row, bombers */
1453 for (i = 4 * ALIENS; i < 5 * ALIENS; i++) {
1454 aliens[i].x = 0 + (i % ALIENS) * ((ALIEN_WIDTH + ALIEN_SPACING) / ALIEN_SPEED);
1455 aliens[i].y = 2 * (i / ALIENS);
1456 aliens[i].state = BOMBER;
1459 /* Init bombs to inactive (S_IDLE) */
1460 for (i = 0; i < MAX_BOMBS; i++) {
1461 bombs[i].state = S_IDLE;
1464 /* Start aliens closer to earth from level 2 */
1465 for (i = 0; i < 5 * ALIENS; i++) {
1466 if (level < 6) {
1467 aliens[i].y += level - 1;
1468 } else {
1469 aliens[i].y += 5;
1473 /* Max concurrent bombs */
1474 max_bombs = 1;
1476 gamespeed = 2;
1478 if (level > 1) {
1479 max_bombs++;
1482 /* Increase speed */
1483 if (level > 2) {
1484 gamespeed++;
1487 if (level > 3) {
1488 max_bombs++;
1491 /* Increase speed more */
1492 if (level > 4) {
1493 gamespeed++;
1496 if (level > 5) {
1497 max_bombs++;
1500 /* 4 shields */
1501 for (i = 1; i <= 4; i++) {
1502 rb->lcd_bitmap_part(invadrox, SHIELD_SRC_X, SHIELD_SRC_Y, STRIDE,
1503 PLAYFIELD_X + i * PLAYFIELD_WIDTH / 5 - SHIELD_WIDTH / 2,
1504 SHIELD_Y, SHIELD_WIDTH, SHIELD_HEIGHT);
1507 /* Bottom line */
1508 rb->lcd_set_foreground(SLIME_GREEN);
1509 rb->lcd_hline(PLAYFIELD_X, LCD_WIDTH - PLAYFIELD_X, PLAYFIELD_Y);
1510 /* Restore foreground to black (for fast erase later). */
1511 rb->lcd_set_foreground(LCD_BLACK);
1513 ship_x = PLAYFIELD_X + 2 * LIVES_X;
1514 if (level == 1) {
1515 old_ship_x = ship_x;
1517 ship_dir = 0;
1518 ship_acc = 0;
1519 ship_frame = 0;
1520 ship_hit = false;
1521 fire = S_IDLE;
1522 /* Start moving the bottom row left to right */
1523 curr_alien = 4 * ALIENS;
1524 aliens_paralyzed = 0;
1525 aliens_right = true;
1526 aliens_down = false;
1527 hit_left_border = false;
1528 hit_right_border = false;
1529 /* TODO: Change max_ship_speed to 3 at higher levels */
1530 max_ship_speed = 2;
1532 draw_aliens();
1534 rb->lcd_update();
1538 void init_invadrox(void)
1540 int i;
1542 /* Seed random number generator with a "random" number */
1543 rb->srand(rb->get_time()->tm_sec + rb->get_time()->tm_min * 60);
1545 /* Precalculate start of each scanline */
1546 for (i = 0; i < LCD_HEIGHT; i++) {
1547 #if (LCD_DEPTH >= 8)
1548 ytab[i] = i * LCD_WIDTH;
1549 #elif (LCD_DEPTH == 2) && (LCD_PIXELFORMAT == HORIZONTAL_PACKING)
1550 ytab[i] = i * (LCD_WIDTH / 4);
1551 #elif (LCD_DEPTH == 2) && (LCD_PIXELFORMAT == VERTICAL_PACKING)
1552 ytab[i] = (i / 4) * LCD_WIDTH;
1553 #else
1554 #error pixelformat not implemented yet
1555 #endif
1558 rb->lcd_set_background(LCD_BLACK);
1559 rb->lcd_set_foreground(LCD_BLACK);
1561 highscore_init(rb);
1562 if (highscore_load(HISCOREFILE, &hiscore, 1) < 0) {
1563 /* Init hiscore to 0 */
1564 rb->strncpy(hiscore.name, "Invader", sizeof(hiscore.name));
1565 hiscore.score = 0;
1566 hiscore.level = 1;
1569 /* Init alien types in aliens array */
1570 for (i = 0; i < 1 * ALIENS; i++) {
1571 aliens[i].type = 0; /* Kang */
1573 for (; i < 3 * ALIENS; i++) {
1574 aliens[i].type = 1; /* Kodos */
1576 for (; i < 5 * ALIENS; i++) {
1577 aliens[i].type = 2; /* Serak */
1581 /* Save screen white color */
1582 rb->lcd_set_foreground(LCD_WHITE);
1583 rb->lcd_drawpixel(0, 0);
1584 rb->lcd_update_rect(0, 0, 1, 1);
1585 screen_white = get_pixel(0, 0);
1587 /* Save screen green color */
1588 rb->lcd_set_foreground(SLIME_GREEN);
1589 rb->lcd_drawpixel(0, 0);
1590 rb->lcd_update_rect(0, 0, 1, 1);
1591 screen_green = get_pixel(0, 0);
1593 /* Restore black foreground */
1594 rb->lcd_set_foreground(LCD_BLACK);
1596 new_level();
1598 /* Flash score at start */
1599 for (i = 0; i < 5; i++) {
1600 rb->lcd_fillrect(SCORENUM_X, SCORENUM_Y,
1601 4 * NUMBERS_WIDTH + 3 * NUM_SPACING,
1602 FONT_HEIGHT);
1603 rb->lcd_update_rect(SCORENUM_X, SCORENUM_Y,
1604 4 * NUMBERS_WIDTH + 3 * NUM_SPACING,
1605 FONT_HEIGHT);
1606 rb->sleep(HZ / 10);
1607 draw_number(SCORENUM_X, SCORENUM_Y, score, 4);
1608 rb->sleep(HZ / 10);
1613 inline bool handle_buttons(void)
1615 static unsigned int oldbuttonstate = 0;
1617 unsigned int released, pressed, newbuttonstate;
1619 if (ship_hit) {
1620 /* Don't allow ship movement during explosion */
1621 newbuttonstate = 0;
1622 } else {
1623 newbuttonstate = rb->button_status();
1625 if(newbuttonstate == oldbuttonstate) {
1626 if (newbuttonstate == 0) {
1627 /* No button pressed. Stop ship. */
1628 ship_acc = 0;
1629 if (ship_dir > 0) {
1630 ship_dir--;
1632 if (ship_dir < 0) {
1633 ship_dir++;
1636 /* return false; */
1637 goto check_usb;
1639 released = ~newbuttonstate & oldbuttonstate;
1640 pressed = newbuttonstate & ~oldbuttonstate;
1641 oldbuttonstate = newbuttonstate;
1642 if (pressed) {
1643 if (pressed & LEFT) {
1644 if (ship_acc > -1) {
1645 ship_acc--;
1648 if (pressed & RIGHT) {
1649 if (ship_acc < 1) {
1650 ship_acc++;
1653 if (pressed & FIRE) {
1654 if (fire == S_IDLE) {
1655 /* Fire shot */
1656 fire_x = ship_x + SHIP_WIDTH / 2;
1657 fire_y = SHIP_Y - SHOT_HEIGHT;
1658 fire = S_ACTIVE;
1659 /* TODO: play fire sound */
1662 #ifdef RC_QUIT
1663 if (pressed & RC_QUIT) {
1664 rb->splash(HZ * 1, "Quit");
1665 return true;
1667 #endif
1668 if (pressed & QUIT) {
1669 rb->splash(HZ * 1, "Quit");
1670 return true;
1673 if (released) {
1674 if ((released & LEFT)) {
1675 if (ship_acc < 1) {
1676 ship_acc++;
1679 if ((released & RIGHT)) {
1680 if (ship_acc > -1) {
1681 ship_acc--;
1686 check_usb:
1688 /* Quit if USB is connected */
1689 if (rb->button_get(false) == SYS_USB_CONNECTED) {
1690 return true;
1693 return false;
1697 void game_loop(void)
1699 int i, end;
1701 /* Print dimensions (just for debugging) */
1702 DBG("%03dx%03dx%02d\n", LCD_WIDTH, LCD_HEIGHT, LCD_DEPTH);
1704 /* Init */
1705 init_invadrox();
1707 while (1) {
1708 /* Convert CYCLETIME (in ms) to HZ */
1709 end = *rb->current_tick + (CYCLETIME * HZ) / 1000;
1711 if (handle_buttons()) {
1712 return;
1715 /* Animate */
1716 move_ship();
1717 move_fire();
1719 /* Check if level is finished (marked by move_fire) */
1720 if (level_finished) {
1721 /* TODO: Play level finished sound */
1722 new_level();
1725 move_ufo();
1727 /* Move aliens */
1728 if (!aliens_paralyzed && !ship_hit) {
1729 for (i = 0; i < gamespeed; i++) {
1730 if (!move_aliens()) {
1731 if (game_over) {
1732 return;
1738 /* Move alien bombs */
1739 move_bombs();
1740 if (game_over) {
1741 return;
1744 /* Update "playfield" rect */
1745 rb->lcd_update_rect(PLAYFIELD_X, SCORENUM_Y + FONT_HEIGHT,
1746 PLAYFIELD_WIDTH,
1747 PLAYFIELD_Y + 1 - SCORENUM_Y - FONT_HEIGHT);
1749 /* Wait until next frame */
1750 DBG("%d (%d)\n", end - *rb->current_tick, (CYCLETIME * HZ) / 1000);
1751 if (end > *rb->current_tick) {
1752 rb->sleep(end - *rb->current_tick);
1753 } else {
1754 rb->yield();
1757 } /* end while */
1761 /* this is the plugin entry point */
1762 enum plugin_status plugin_start(struct plugin_api* api, UNUSED void* parameter)
1764 rb = api;
1766 rb->lcd_setfont(FONT_SYSFIXED);
1767 /* Permanently enable the backlight (unless the user has turned it off) */
1768 if (rb->global_settings->backlight_timeout > 0)
1769 rb->backlight_set_timeout(1);
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 rb->backlight_set_timeout(rb->global_settings->backlight_timeout);
1788 return PLUGIN_OK;
1794 * GNU Emacs settings: Kernighan & Richie coding style with
1795 * 4 spaces indent and no tabs.
1796 * Local Variables:
1797 * c-file-style: "k&r"
1798 * c-basic-offset: 4
1799 * indent-tabs-mode: nil
1800 * End: