fix the last warning
[kugel-rb.git] / apps / plugins / invadrox.c
blob8f5c0bf5a32cbe85ba58de072bf1db42081dc4dc
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 #elif CONFIG_KEYPAD == GIGABEAT_S_PAD
112 #define QUIT BUTTON_BACK
113 #define LEFT BUTTON_LEFT
114 #define RIGHT BUTTON_RIGHT
115 #define FIRE BUTTON_SELECT
117 #else
118 #error INVADROX: Unsupported keypad
119 #endif
122 #ifndef UNUSED
123 #define UNUSED __attribute__ ((unused))
124 #endif
126 #ifndef ABS
127 #define ABS(x) (((x) < 0) ? (-(x)) : (x))
128 #endif
131 /* Defines common to all models */
132 #define UFO_Y (SCORENUM_Y + FONT_HEIGHT + ALIEN_HEIGHT)
133 #define PLAYFIELD_Y (LCD_HEIGHT - SHIP_HEIGHT - 2)
134 #define PLAYFIELD_WIDTH (LCD_WIDTH - 2 * PLAYFIELD_X)
135 #define LEVEL_X (LCD_WIDTH - PLAYFIELD_X - LIVES_X - LEVEL_WIDTH - 2 * NUMBERS_WIDTH - 3 * NUM_SPACING)
136 #define SHIP_MIN_X (PLAYFIELD_X + PLAYFIELD_WIDTH / 5 - SHIELD_WIDTH / 2 - SHIP_WIDTH)
137 #define SHIP_MAX_X (PLAYFIELD_X + 4 * PLAYFIELD_WIDTH / 5 + SHIELD_WIDTH / 2)
138 /* SCORE_Y = 0 for most targets. Gigabeat redefines it later. */
139 #define SCORE_Y 0
140 #define MAX_LIVES 8
143 /* iPod Video defines */
144 #if (LCD_WIDTH == 320) && (LCD_HEIGHT == 240)
146 /* Original arcade game size 224x240, 1bpp with
147 * red overlay at top and green overlay at bottom.
149 * iPod Video: 320x240x16
150 * ======================
151 * X: 48p padding at left/right gives 224p playfield in middle.
152 * 10p "border" gives 204p actual playfield. UFO use full 224p.
153 * Y: Use full 240p.
155 * MAX_X = (204 - 12) / 2 - 1 = 95
157 * Y: Score text 7 0
158 * Space 10 7
159 * Score 7 17
160 * Space 8 24
161 * 3 Ufo 7 32
162 * 2 Space Aliens start at 32 + 3 * 8 = 56
163 * 0 aliens 9*8 56 -
164 * space ~7*8 128 | 18.75 aliens space between
165 * shield 2*8 182 | first alien and ship.
166 * space 8 198 | MAX_Y = 18
167 * ship 8 206 -
168 * space 2*8 214
169 * hline 1 230 - PLAYFIELD_Y
170 * bottom border 10 240
171 * Lives and Level goes inside bottom border
174 #define ARCADISH_GRAPHICS
175 #define PLAYFIELD_X 48
176 #define SHIP_Y (PLAYFIELD_Y - 3 * SHIP_HEIGHT)
177 #define ALIEN_START_Y (UFO_Y + 3 * ALIEN_HEIGHT)
178 #define SCORENUM_X (PLAYFIELD_X + NUMBERS_WIDTH)
179 #define SCORENUM_Y SCORE_Y + (2 * (FONT_HEIGHT + 1) + 1)
180 #define HISCORE_X (LCD_WIDTH - PLAYFIELD_X - HISCORE_WIDTH)
181 #define HISCORENUM_X (LCD_WIDTH - PLAYFIELD_X - 1 - 6 * NUMBERS_WIDTH - 5 * NUM_SPACING)
182 #define SHIELD_Y (PLAYFIELD_Y - 6 * SHIP_HEIGHT)
183 #define LIVES_X 10
184 #define MAX_X 95
185 #define MAX_Y 18
188 #elif (LCD_WIDTH == 176) && (LCD_HEIGHT == 220)
190 /* Sandisk Sansa e200: 176x220x16
191 * ==============================
192 * X: No padding. 8p border -> 160p playfield.
194 * 8p Aliens with 3p spacing -> 88 + 30 = 118p aliens block.
195 * (160 - 118) / 2 = 21 rounds for whole block (more than original)
196 * MAX_X = (160 - 8) / 2 - 1 = 75 rounds for single alien (less than original)
198 * LOGO 70 0
199 * Score text 5 70
200 * Space 5 75
201 * Y Score 5 80
202 * Space 10 85
203 * 2 Ufo 5 95
204 * 2 Space 10 100
205 * 0 aliens 9*5 110 -
206 * space ~7*5 155 | 18.6 aliens space between
207 * shield 2*5 188 | first alien and ship.
208 * space 5 198 | MAX_Y = 18
209 * ship 5 203 -
210 * space 5 208
211 * hline 1 213 PLAYFIELD_Y
212 * bottom border 6
213 * LCD_HEIGHT 220
214 * Lives and Level goes inside bottom border
217 #define TINY_GRAPHICS
218 #define PLAYFIELD_X 0
219 #define SHIP_Y (PLAYFIELD_Y - 2 * SHIP_HEIGHT)
220 #define SHIELD_Y (SHIP_Y - SHIP_HEIGHT - SHIELD_HEIGHT)
221 #define ALIEN_START_Y (UFO_Y + 3 * SHIP_HEIGHT)
222 /* Redefine SCORE_Y */
223 #undef SCORE_Y
224 #define SCORE_Y 70
225 #define SCORENUM_X (PLAYFIELD_X + NUMBERS_WIDTH)
226 #define SCORENUM_Y (SCORE_Y + 2 * FONT_HEIGHT)
227 #define HISCORE_X (LCD_WIDTH - PLAYFIELD_X - HISCORE_WIDTH)
228 #define HISCORENUM_X (LCD_WIDTH - PLAYFIELD_X - 1 - 6 * NUMBERS_WIDTH - 5 * NUM_SPACING)
229 #define LIVES_X 8
230 #define MAX_X 75
231 #define MAX_Y 18
234 #elif (LCD_WIDTH == 176) && (LCD_HEIGHT == 132)
236 /* iPod Nano: 176x132x16
237 * ======================
238 * X: No padding. 8p border -> 160p playfield.
240 * LIVES_X 8
241 * ALIEN_WIDTH 8
242 * ALIEN_HEIGHT 5
243 * ALIEN_SPACING 3
244 * SHIP_WIDTH 10
245 * SHIP_HEIGHT 5
246 * FONT_HEIGHT 5
247 * UFO_WIDTH 10
248 * UFO_HEIGHT 5
249 * SHIELD_WIDTH 15
250 * SHIELD_HEIGHT 10
251 * MAX_X 75
252 * MAX_Y = 18
253 * ALIEN_START_Y (UFO_Y + 12)
255 * 8p Aliens with 3p spacing -> 88 + 30 = 118p aliens block.
256 * (160 - 118) / 2 = 21 rounds for whole block (more than original)
257 * MAX_X = (160 - 8) / 2 - 1 = 75 rounds for single alien (less than original)
259 * Y: Scoreline 5 0 (combine scoretext and numbers on same line)
260 * Space 5 5
261 * 1 Ufo 5 10
262 * 3 Space 7 15
263 * 2 aliens 9*5 22 -
264 * space ~7*5 67 | Just above 18 aliens space between
265 * shield 2*5 100 | first alien and ship.
266 * space 5 110 | MAX_Y = 18
267 * ship 5 115 -
268 * space 5 120
269 * hline 1 125 PLAYFIELD_Y
270 * bottom border 6 126
271 * LCD_HEIGHT 131
272 * Lives and Level goes inside bottom border
275 #define TINY_GRAPHICS
276 #define PLAYFIELD_X 0
277 #define SHIP_Y (PLAYFIELD_Y - 2 * SHIP_HEIGHT)
278 #define ALIEN_START_Y (UFO_Y + 12)
279 #define SCORENUM_X (PLAYFIELD_X + SCORE_WIDTH + NUMBERS_WIDTH + NUM_SPACING)
280 #define SCORENUM_Y SCORE_Y
281 #define HISCORENUM_X (LCD_WIDTH - PLAYFIELD_X - 4 * NUMBERS_WIDTH - 3 * NUM_SPACING)
282 #define HISCORE_X (HISCORENUM_X - NUMBERS_WIDTH - NUM_SPACING - HISCORE_WIDTH)
283 #define SHIELD_Y (SHIP_Y - SHIP_HEIGHT - SHIELD_HEIGHT)
284 #define LIVES_X 8
285 #define MAX_X 75
286 #define MAX_Y 18
288 #elif (LCD_WIDTH == 160) && (LCD_HEIGHT == 128)
290 /* iAudio X5, iRiver H10 20Gb, iPod 3g/4g: 160x128x16
291 * ======================================
292 * X: No padding. No border -> 160p playfield.
294 * LIVES_X 0
295 * ALIEN_WIDTH 8
296 * ALIEN_HEIGHT 5
297 * ALIEN_SPACING 3
298 * SHIP_WIDTH 10
299 * SHIP_HEIGHT 5
300 * FONT_HEIGHT 5
301 * UFO_WIDTH 10
302 * UFO_HEIGHT 5
303 * SHIELD_WIDTH 15
304 * SHIELD_HEIGHT 10
305 * MAX_X 75
306 * MAX_Y = 18
307 * ALIEN_START_Y (UFO_Y + 10)
309 * 8p Aliens with 3p spacing -> 88 + 30 = 118p aliens block.
310 * (160 - 118) / 2 = 21 rounds for whole block (more than original)
311 * MAX_X = (160 - 8) / 2 - 1 = 75 rounds for single alien (less than original)
313 * Y: Scoreline 5 0 (combine scoretext and numbers on same line)
314 * Space 5 5
315 * 1 Ufo 5 10
316 * 2 Space 5 15
317 * 8 aliens 9*5 20 -
318 * space ~6*5 65 | Just above 18 aliens space between
319 * shield 2*5 96 | first alien and ship.
320 * space 5 106 | MAX_Y = 18
321 * ship 5 111 -
322 * space 5 116
323 * hline 1 121 PLAYFIELD_Y
324 * bottom border 6 122
325 * LCD_HEIGHT 128
326 * Lives and Level goes inside bottom border
329 #define TINY_GRAPHICS
330 #define PLAYFIELD_X 0
331 #define SHIP_Y (PLAYFIELD_Y - 2 * SHIP_HEIGHT)
332 #define ALIEN_START_Y (UFO_Y + 10)
333 #define SCORENUM_X (PLAYFIELD_X + SCORE_WIDTH + NUMBERS_WIDTH + NUM_SPACING)
334 #define SCORENUM_Y SCORE_Y
335 #define HISCORENUM_X (LCD_WIDTH - PLAYFIELD_X - 4 * NUMBERS_WIDTH - 3 * NUM_SPACING)
336 #define HISCORE_X (HISCORENUM_X - NUMBERS_WIDTH - NUM_SPACING - HISCORE_WIDTH)
337 #define SHIELD_Y (SHIP_Y - SHIP_HEIGHT - SHIELD_HEIGHT)
338 #define LIVES_X 0
339 #define MAX_X 75
340 #define MAX_Y 18
343 #elif (LCD_WIDTH == 240) && (LCD_HEIGHT == 320)
345 /* Gigabeat: 240x320x16
346 * ======================
347 * X: 8p padding at left/right gives 224p playfield in middle.
348 * 10p "border" gives 204p actual playfield. UFO use full 224p.
349 * Y: Use bottom 240p for playfield and top 80 pixels for logo.
351 * MAX_X = (204 - 12) / 2 - 1 = 95
353 * Y: Score text 7 0 + 80
354 * Space 10 7 + 80
355 * Score 7 17 + 80
356 * Space 8 24 + 80
357 * 3 Ufo 7 32 + 80
358 * 2 Space Aliens start at 32 + 3 * 8 = 56
359 * 0 aliens 9*8 56 -
360 * space ~7*8 128 | 18.75 aliens space between
361 * shield 2*8 182 | first alien and ship.
362 * space 8 198 | MAX_Y = 18
363 * ship 8 206 -
364 * space 2*8 214
365 * hline 1 230 310 - PLAYFIELD_Y
366 * bottom border 10 240 320
367 * Lives and Level goes inside bottom border
370 #define ARCADISH_GRAPHICS
371 #define PLAYFIELD_X 8
372 #define SHIP_Y (PLAYFIELD_Y - 3 * SHIP_HEIGHT)
373 #define ALIEN_START_Y (UFO_Y + 3 * ALIEN_HEIGHT)
374 /* Redefine SCORE_Y */
375 #undef SCORE_Y
376 #define SCORE_Y 80
377 #define SCORENUM_X (PLAYFIELD_X + NUMBERS_WIDTH)
378 #define SCORENUM_Y SCORE_Y + (2 * (FONT_HEIGHT + 1) + 1)
379 #define HISCORE_X (LCD_WIDTH - PLAYFIELD_X - HISCORE_WIDTH)
380 #define HISCORENUM_X (LCD_WIDTH - PLAYFIELD_X - 1 - 6 * NUMBERS_WIDTH - 5 * NUM_SPACING)
381 #define SHIELD_Y (PLAYFIELD_Y - 6 * SHIP_HEIGHT)
382 #define LIVES_X 10
383 #define MAX_X 95
384 #define MAX_Y 18
386 #elif (LCD_WIDTH == 220) && (LCD_HEIGHT == 176)
388 /* TPJ1022, H300, iPod Color: 220x176x16
389 * ============================
390 * X: 0p padding at left/right gives 220p playfield in middle.
391 * 8p "border" gives 204p actual playfield. UFO use full 220p.
392 * Y: Use full 176p for playfield.
394 * MAX_X = (204 - 12) / 2 - 1 = 95
396 * Y: Score text 7 0
397 * Space 8 7
398 * 1 Ufo 7 15
399 * 7 Space Aliens start at 15 + 3 * 8 = 56
400 * 6 aliens 9*8 25 -
401 * space ~7*8 103 | 15.6 aliens space between
402 * shield 2*8 126 | first alien and ship.
403 * space 8 142 | MAX_Y = 15
404 * ship 8 150 -
405 * space 8 158
406 * hline 1 166 - PLAYFIELD_Y
407 * bottom border 10 176
408 * Lives and Level goes inside bottom border
411 #define ARCADISH_GRAPHICS
412 #define PLAYFIELD_X 0
413 #define SHIP_Y (PLAYFIELD_Y - 2 * SHIP_HEIGHT)
414 #define ALIEN_START_Y (UFO_Y + 10)
415 #define SCORENUM_Y SCORE_Y
416 #define SCORENUM_X (PLAYFIELD_X + SCORE_WIDTH + NUMBERS_WIDTH + NUM_SPACING)
417 #define HISCORENUM_X (LCD_WIDTH - PLAYFIELD_X - 4 * NUMBERS_WIDTH - 3 * NUM_SPACING)
418 #define HISCORE_X (HISCORENUM_X - NUMBERS_WIDTH - NUM_SPACING - HISCORE_WIDTH)
419 #define SHIELD_Y (PLAYFIELD_Y - 5 * SHIP_HEIGHT)
420 #define LIVES_X 8
421 #define MAX_X 95
422 #define MAX_Y 15
424 #else
425 #error INVADROX: Unsupported LCD type
426 #endif
429 /* Defines common to each "graphic type" */
430 #ifdef ARCADISH_GRAPHICS
432 #define STRIDE 71
433 #define SHIP_SRC_X 24
434 #define SHIP_WIDTH 16
435 #define SHIP_HEIGHT 8
436 #define SHOT_HEIGHT 5
437 #define ALIEN_WIDTH 12
438 #define ALIEN_EXPLODE_SRC_X 52
439 #define ALIEN_EXPLODE_SRC_Y 39
440 #define ALIEN_EXPLODE_WIDTH 13
441 #define ALIEN_EXPLODE_HEIGHT 7
442 #define ALIEN_HEIGHT 8
443 #define ALIEN_SPACING 4
444 #define ALIEN_SPEED 2
445 #define UFO_SRC_X 40
446 #define UFO_WIDTH 16
447 #define UFO_HEIGHT 7
448 #define UFO_EXPLODE_WIDTH 21
449 #define UFO_EXPLODE_HEIGHT 8
450 #define UFO_SPEED 1
451 #define FONT_HEIGHT 7
452 #define LEVEL_SRC_Y 24
453 #define LEVEL_WIDTH 37
454 #define SCORE_SRC_X 24
455 #define SCORE_SRC_Y 31
456 #define SCORE_WIDTH 37
457 #define HISCORE_WIDTH 61
458 #define NUM_SPACING 3
459 #define NUMBERS_SRC_Y 38
460 #define NUMBERS_WIDTH 5
461 #define SHIELD_SRC_X 40
462 #define SHIELD_SRC_Y 15
463 #define SHIELD_WIDTH 22
464 #define SHIELD_HEIGHT 16
465 #define FIRE_WIDTH 8
466 #define FIRE_HEIGHT 8
467 #define FIRE_SPEED 8
468 #define BOMB_SRC_X 62
469 #define BOMB_WIDTH 3
470 #define BOMB_HEIGHT 7
471 #define BOMB_SPEED 3
472 #define ALIENS 11
473 unsigned char fire_sprite[FIRE_HEIGHT] = {
474 (1 << 7) | (0 << 6) | (0 << 5) | (0 << 4) | (1 << 3) | (0 << 2) | (0 << 1) | 1,
475 (0 << 7) | (0 << 6) | (1 << 5) | (0 << 4) | (0 << 3) | (0 << 2) | (1 << 1) | 0,
476 (0 << 7) | (1 << 6) | (1 << 5) | (1 << 4) | (1 << 3) | (1 << 2) | (1 << 1) | 0,
477 (1 << 7) | (1 << 6) | (1 << 5) | (1 << 4) | (1 << 3) | (1 << 2) | (1 << 1) | 1,
478 (0 << 7) | (1 << 6) | (1 << 5) | (1 << 4) | (1 << 3) | (1 << 2) | (1 << 1) | 1,
479 (0 << 7) | (1 << 6) | (1 << 5) | (1 << 4) | (1 << 3) | (1 << 2) | (1 << 1) | 0,
480 (0 << 7) | (0 << 6) | (1 << 5) | (0 << 4) | (0 << 3) | (1 << 2) | (0 << 1) | 0,
481 (1 << 7) | (0 << 6) | (0 << 5) | (1 << 4) | (0 << 3) | (0 << 2) | (0 << 1) | 1
484 #elif defined TINY_GRAPHICS
486 #define STRIDE 53
487 #define SHIP_SRC_X 16
488 #define SHIP_WIDTH 10
489 #define SHIP_HEIGHT 5
490 #define SHOT_HEIGHT 4
491 #define ALIEN_WIDTH 8
492 #define ALIEN_HEIGHT 5
493 #define ALIEN_EXPLODE_SRC_X 40
494 #define ALIEN_EXPLODE_SRC_Y 26
495 #define ALIEN_EXPLODE_WIDTH 10
496 #define ALIEN_EXPLODE_HEIGHT 5
497 #define ALIEN_SPACING 3
498 #define ALIEN_SPEED 2
499 #define UFO_SRC_X 26
500 #define UFO_WIDTH 11
501 #define UFO_HEIGHT 5
502 #define UFO_EXPLODE_WIDTH 14
503 #define UFO_EXPLODE_HEIGHT 5
504 #define UFO_SPEED 1
505 #define FONT_HEIGHT 5
506 #define LEVEL_SRC_Y 15
507 #define LEVEL_WIDTH 29
508 #define NUMBERS_WIDTH 4
509 #define NUM_SPACING 2
510 #define SCORE_SRC_X 17
511 #define SCORE_SRC_Y 20
512 #define SCORE_WIDTH 28
513 #define HISCORE_WIDTH 45
514 #define NUMBERS_SRC_Y 25
515 #define SHIELD_SRC_X 29
516 #define SHIELD_SRC_Y 10
517 #define SHIELD_WIDTH 15
518 #define SHIELD_HEIGHT 10
519 #define FIRE_WIDTH 6
520 #define FIRE_HEIGHT 6
521 #define FIRE_SPEED 6
522 #define BOMB_SRC_X 44
523 #define BOMB_WIDTH 3
524 #define BOMB_HEIGHT 5
525 #define BOMB_SPEED 2
526 #define ALIENS 11
527 unsigned char fire_sprite[FIRE_HEIGHT] = {
528 (1 << 5) | (0 << 4) | (0 << 3) | (1 << 2) | (0 << 1) | 1,
529 (0 << 5) | (1 << 4) | (0 << 3) | (0 << 2) | (0 << 1) | 0,
530 (0 << 5) | (1 << 4) | (1 << 3) | (1 << 2) | (1 << 1) | 0,
531 (0 << 5) | (1 << 4) | (1 << 3) | (1 << 2) | (1 << 1) | 1,
532 (0 << 5) | (1 << 4) | (0 << 3) | (0 << 2) | (1 << 1) | 0,
533 (1 << 5) | (0 << 4) | (1 << 3) | (0 << 2) | (0 << 1) | 1
536 #else
537 #error Graphic type not defined
538 #endif
541 /* Colors */
542 #if (LCD_DEPTH >= 8)
543 #define SLIME_GREEN LCD_RGBPACK(31, 254, 31)
544 #define UFO_RED LCD_RGBPACK(254, 31, 31)
545 #elif (LCD_DEPTH == 2)
546 #define SLIME_GREEN LCD_LIGHTGRAY
547 #define UFO_RED LCD_LIGHTGRAY
548 #else
549 #error LCD type not implemented yet
550 #endif
552 /* Alien states */
553 #define DEAD 0
554 #define ALIVE 1
555 #define BOMBER 2
557 /* Fire/bomb/ufo states */
558 #define S_IDLE 0
559 #define S_ACTIVE 1
560 #define S_SHOWSCORE 2
561 #define S_EXPLODE -9
563 /* Fire/bomb targets */
564 #define TARGET_TOP 0
565 #define TARGET_SHIELD 1
566 #define TARGET_SHIP 2
567 #define TARGET_BOTTOM 3
568 #define TARGET_UFO 4
570 #define HISCOREFILE PLUGIN_GAMES_DIR "/invadrox.high"
573 /* The time (in ms) for one iteration through the game loop - decrease this
574 * to speed up the game - note that current_tick is (currently) only accurate
575 * to 10ms.
577 #define CYCLETIME 40
580 static struct plugin_api* rb;
582 /* Physical x is at PLAYFIELD_X + LIVES_X + x * ALIEN_SPEED
583 * Physical y is at y * ALIEN_HEIGHT
585 struct alien {
586 unsigned char x; /* x-coordinate (0 - 95) */
587 unsigned char y; /* y-coordinate (0 - 18) */
588 unsigned char type; /* 0 (Kang), 1 (Kodos), 2 (Serak) */
589 unsigned char state; /* Dead, alive or bomber */
592 /* Aliens box 5 rows * ALIENS aliens in each row */
593 struct alien aliens[5 * ALIENS];
595 #define MAX_BOMBS 4
596 struct bomb {
597 int x, y;
598 unsigned char type;
599 unsigned char frame; /* Current animation frame */
600 unsigned char frames; /* Number of frames in animation */
601 unsigned char target; /* Remember target during explosion frames */
602 int state; /* 0 (IDLE) = inactive, 1 (FIRE) or negative, exploding */
604 struct bomb bombs[MAX_BOMBS];
605 /* Increase max_bombs at higher levels */
606 int max_bombs;
608 /* Raw framebuffer value of shield/ship green color */
609 fb_data screen_green, screen_white;
611 /* For optimization, precalculate startoffset of each scanline */
612 unsigned int ytab[LCD_HEIGHT];
614 /* external bitmaps */
615 extern const fb_data invadrox[];
616 #if (LCD_WIDTH == 320) && (LCD_HEIGHT == 240)
617 /* iPod Video only */
618 extern const fb_data invadrox_left[];
619 extern const fb_data invadrox_right[];
620 #endif
621 #if ((LCD_WIDTH == 240) && (LCD_HEIGHT == 320)) || ((LCD_WIDTH == 176) && (LCD_HEIGHT == 220))
622 /* Gigabeat F, Sansa e200 */
623 extern const fb_data invadrox_logo[];
624 #endif
627 int lives = 2;
628 int score = 0;
629 int scores[3] = { 30, 20, 10 };
630 int level = 0;
631 struct highscore hiscore;
632 bool game_over = false;
633 int ship_x, old_ship_x, ship_dir, ship_acc, max_ship_speed;
634 int ship_frame, ship_frame_counter;
635 bool ship_hit;
636 int fire, fire_target, fire_x, fire_y;
637 int curr_alien, aliens_paralyzed, gamespeed;
638 int ufo_state, ufo_x;
639 bool level_finished;
640 bool aliens_down, aliens_right, hit_left_border, hit_right_border;
643 /* No standard get_pixel function yet, use this hack instead */
644 #if (LCD_DEPTH >= 8)
646 inline fb_data get_pixel(int x, int y)
648 return rb->lcd_framebuffer[ytab[y] + x];
651 #elif (LCD_DEPTH == 2)
653 #if (LCD_PIXELFORMAT == HORIZONTAL_PACKING)
654 static const unsigned char shifts[4] = {
655 6, 4, 2, 0
657 /* Horizontal packing */
658 inline fb_data get_pixel(int x, int y)
660 return (rb->lcd_framebuffer[ytab[y] + (x >> 2)] >> shifts[x & 3]) & 3;
662 #else
663 /* Vertical packing */
664 static const unsigned char shifts[4] = {
665 0, 2, 4, 6
667 inline fb_data get_pixel(int x, int y)
669 return (rb->lcd_framebuffer[ytab[y] + x] >> shifts[y & 3]) & 3;
671 #endif /* Horizontal/Vertical packing */
673 #else
674 #error get_pixel: pixelformat not implemented yet
675 #endif
678 /* Draw "digits" least significant digits of num at (x,y) */
679 void draw_number(int x, int y, int num, int digits)
681 int i;
682 int d;
684 for (i = digits - 1; i >= 0; i--) {
685 d = num % 10;
686 num = num / 10;
687 rb->lcd_bitmap_part(invadrox, d * NUMBERS_WIDTH, NUMBERS_SRC_Y,
688 STRIDE, x + i * (NUMBERS_WIDTH + NUM_SPACING), y,
689 NUMBERS_WIDTH, FONT_HEIGHT);
691 /* Update lcd */
692 rb->lcd_update_rect(x, y, 4 * NUMBERS_WIDTH + 3 * NUM_SPACING, FONT_HEIGHT);
696 inline void draw_score(void)
698 draw_number(SCORENUM_X, SCORENUM_Y, score, 4);
699 if (score > hiscore.score) {
700 /* Draw new hiscore (same as score) */
701 draw_number(HISCORENUM_X, SCORENUM_Y, score, 4);
706 void draw_level(void)
708 rb->lcd_bitmap_part(invadrox, 0, LEVEL_SRC_Y,
709 STRIDE, LEVEL_X, PLAYFIELD_Y + 2,
710 LEVEL_WIDTH, FONT_HEIGHT);
711 draw_number(LEVEL_X + LEVEL_WIDTH + 2 * NUM_SPACING, PLAYFIELD_Y + 2, level, 2);
715 void draw_lives(void)
717 int i;
718 /* Lives num */
719 rb->lcd_bitmap_part(invadrox, lives * NUMBERS_WIDTH, NUMBERS_SRC_Y,
720 STRIDE, PLAYFIELD_X + LIVES_X, PLAYFIELD_Y + 2,
721 NUMBERS_WIDTH, FONT_HEIGHT);
723 /* Ships */
724 for (i = 0; i < (lives - 1); i++) {
725 rb->lcd_bitmap_part(invadrox, SHIP_SRC_X, 0, STRIDE,
726 PLAYFIELD_X + LIVES_X + SHIP_WIDTH + i * (SHIP_WIDTH + NUM_SPACING),
727 PLAYFIELD_Y + 1, SHIP_WIDTH, SHIP_HEIGHT);
730 /* Erase ship to the righ (if less than MAX_LIVES) */
731 if (lives < MAX_LIVES) {
732 rb->lcd_fillrect(PLAYFIELD_X + LIVES_X + SHIP_WIDTH + i * (SHIP_WIDTH + NUM_SPACING),
733 PLAYFIELD_Y + 1, SHIP_WIDTH, SHIP_HEIGHT);
735 /* Update lives (and level) part of screen */
736 rb->lcd_update_rect(PLAYFIELD_X + LIVES_X, PLAYFIELD_Y + 1,
737 PLAYFIELD_WIDTH - 2 * LIVES_X, MAX(FONT_HEIGHT + 1, SHIP_HEIGHT + 1));
741 inline void draw_aliens(void)
743 int i;
745 for (i = 0; i < 5 * ALIENS; i++) {
746 rb->lcd_bitmap_part(invadrox, aliens[i].x & 1 ? ALIEN_WIDTH : 0, aliens[i].type * ALIEN_HEIGHT,
747 STRIDE, PLAYFIELD_X + LIVES_X + aliens[i].x * ALIEN_SPEED,
748 ALIEN_START_Y + aliens[i].y * ALIEN_HEIGHT,
749 ALIEN_WIDTH, ALIEN_HEIGHT);
754 /* Return false if there is no next alive alien (round is over) */
755 inline bool next_alien(void)
757 bool ret = true;
759 do {
760 curr_alien++;
761 if (curr_alien % ALIENS == 0) {
762 /* End of this row. Move up one row. */
763 curr_alien -= 2 * ALIENS;
764 if (curr_alien < 0) {
765 /* No more aliens in this round. */
766 curr_alien = 4 * ALIENS;
767 ret = false;
770 } while (aliens[curr_alien].state == DEAD && ret);
772 if (!ret) {
773 /* No more alive aliens. Round finished. */
774 if (hit_right_border) {
775 if (hit_left_border) {
776 DBG("ERROR: both left and right borders are set (%d)\n", curr_alien);
778 /* Move down-left next round */
779 aliens_right = false;
780 aliens_down = true;
781 hit_right_border = false;
782 } else if (hit_left_border) {
783 /* Move down-right next round */
784 aliens_right = true;
785 aliens_down = true;
786 hit_left_border = false;
787 } else {
788 /* Not left nor right. Set down to false. */
789 aliens_down = false;
793 return ret;
797 /* All aliens have been moved.
798 * Set curr_alien to first alive.
799 * Return false if no-one is left alive.
801 bool first_alien(void)
803 int i, y;
805 for (y = 4; y >= 0; y--) {
806 for (i = y * ALIENS; i < (y + 1) * ALIENS; i++) {
807 if (aliens[i].state != DEAD) {
808 curr_alien = i;
809 return true;
814 /* All aliens dead. */
815 level_finished = true;
817 return false;
821 bool move_aliens(void)
823 int x, y, old_x, old_y;
825 /* Move current alien (curr_alien is pointing to a living alien) */
827 old_x = aliens[curr_alien].x;
828 old_y = aliens[curr_alien].y;
830 if (aliens_down) {
831 aliens[curr_alien].y++;
832 if (aliens[curr_alien].y == MAX_Y) {
833 /* Alien is at bottom. Game Over. */
834 DBG("Alien %d is at bottom. Game Over.\n", curr_alien);
835 game_over = true;
836 return false;
840 if (aliens_right) {
841 /* Moving right */
842 if (aliens[curr_alien].x < MAX_X) {
843 aliens[curr_alien].x++;
846 /* Now, after move, check if we hit the right border. */
847 if (aliens[curr_alien].x == MAX_X) {
848 hit_right_border = true;
851 } else {
852 /* Moving left */
853 if (aliens[curr_alien].x > 0) {
854 aliens[curr_alien].x--;
857 /* Now, after move, check if we hit the left border. */
858 if (aliens[curr_alien].x == 0) {
859 hit_left_border = true;
863 /* Erase old position */
864 x = PLAYFIELD_X + LIVES_X + old_x * ALIEN_SPEED;
865 y = ALIEN_START_Y + old_y * ALIEN_HEIGHT;
866 if (aliens[curr_alien].y != old_y) {
867 /* Moved in y-dir. Erase whole alien. */
868 rb->lcd_fillrect(x, y, ALIEN_WIDTH, ALIEN_HEIGHT);
869 } else {
870 if (aliens_right) {
871 /* Erase left edge */
872 rb->lcd_fillrect(x, y, ALIEN_SPEED, ALIEN_HEIGHT);
873 } else {
874 /* Erase right edge */
875 x += ALIEN_WIDTH - ALIEN_SPEED;
876 rb->lcd_fillrect(x, y, ALIEN_SPEED, ALIEN_HEIGHT);
880 /* Draw alien at new pos */
881 x = PLAYFIELD_X + LIVES_X + aliens[curr_alien].x * ALIEN_SPEED;
882 y = ALIEN_START_Y + aliens[curr_alien].y * ALIEN_HEIGHT;
883 rb->lcd_bitmap_part(invadrox,
884 aliens[curr_alien].x & 1 ? ALIEN_WIDTH : 0, aliens[curr_alien].type * ALIEN_HEIGHT,
885 STRIDE, x, y, ALIEN_WIDTH, ALIEN_HEIGHT);
887 if (!next_alien()) {
888 /* Round finished. Set curr_alien to first alive from bottom. */
889 if (!first_alien()) {
890 /* Should never happen. Taken care of in move_fire(). */
891 return false;
893 /* TODO: Play next background sound */
896 return true;
900 inline void draw_ship(void)
902 /* Erase old ship */
903 if (old_ship_x < ship_x) {
904 /* Move right. Erase leftmost part of ship. */
905 rb->lcd_fillrect(old_ship_x, SHIP_Y, ship_x - old_ship_x, SHIP_HEIGHT);
906 } else if (old_ship_x > ship_x) {
907 /* Move left. Erase rightmost part of ship. */
908 rb->lcd_fillrect(ship_x + SHIP_WIDTH, SHIP_Y, old_ship_x - ship_x, SHIP_HEIGHT);
911 /* Draw ship */
912 rb->lcd_bitmap_part(invadrox, SHIP_SRC_X, ship_frame * SHIP_HEIGHT,
913 STRIDE, ship_x, SHIP_Y, SHIP_WIDTH, SHIP_HEIGHT);
914 if (ship_hit) {
915 /* Alternate between frame 1 and 2 during hit */
916 ship_frame_counter++;
917 if (ship_frame_counter > 2) {
918 ship_frame_counter = 0;
919 ship_frame++;
920 if (ship_frame > 2) {
921 ship_frame = 1;
926 /* Save ship_x for next time */
927 old_ship_x = ship_x;
931 inline void fire_alpha(int xc, int yc, fb_data color)
933 int x, y;
934 unsigned char mask;
936 rb->lcd_set_foreground(color);
938 for (y = 0; y < FIRE_HEIGHT; y++) {
939 mask = 1 << (FIRE_WIDTH - 1);
940 for (x = -(FIRE_WIDTH / 2); x < (FIRE_WIDTH / 2); x++) {
941 if (fire_sprite[y] & mask) {
942 rb->lcd_drawpixel(xc + x, yc + y);
944 mask >>= 1;
948 rb->lcd_set_foreground(LCD_BLACK);
952 void move_fire(void)
954 bool hit_green = false;
955 bool hit_white = false;
956 int i, j;
957 static int exploding_alien = -1;
958 fb_data pix;
960 if (fire == S_IDLE) {
961 return;
964 /* Alien hit. Wait until explosion is finished. */
965 if (aliens_paralyzed < 0) {
966 aliens_paralyzed++;
967 if (aliens_paralyzed == 0) {
968 /* Erase exploding_alien */
969 rb->lcd_fillrect(PLAYFIELD_X + LIVES_X + aliens[exploding_alien].x * ALIEN_SPEED,
970 ALIEN_START_Y + aliens[exploding_alien].y * ALIEN_HEIGHT,
971 ALIEN_EXPLODE_WIDTH, ALIEN_HEIGHT);
972 fire = S_IDLE;
973 /* Special case. We killed curr_alien. */
974 if (exploding_alien == curr_alien) {
975 if (!next_alien()) {
976 /* Round finished. Set curr_alien to first alive from bottom. */
977 first_alien();
981 return;
984 if (fire == S_ACTIVE) {
986 /* Erase */
987 rb->lcd_vline(fire_x, fire_y, fire_y + SHOT_HEIGHT);
989 /* Check top */
990 if (fire_y <= SCORENUM_Y + FONT_HEIGHT + 4) {
992 /* TODO: Play explode sound */
994 fire = S_EXPLODE;
995 fire_target = TARGET_TOP;
996 fire_alpha(fire_x, fire_y, UFO_RED);
997 return;
1000 /* Move */
1001 fire_y -= FIRE_SPEED;
1003 /* Hit UFO? */
1004 if (ufo_state == S_ACTIVE) {
1005 if ((ABS(ufo_x + UFO_WIDTH / 2 - fire_x) <= UFO_WIDTH / 2) &&
1006 (fire_y <= UFO_Y + UFO_HEIGHT)) {
1007 ufo_state = S_EXPLODE;
1008 fire = S_EXPLODE;
1009 fire_target = TARGET_UFO;
1010 /* Center explosion */
1011 ufo_x -= (UFO_EXPLODE_WIDTH - UFO_WIDTH) / 2;
1012 rb->lcd_bitmap_part(invadrox, UFO_SRC_X, UFO_HEIGHT,
1013 STRIDE, ufo_x, UFO_Y - 1, UFO_EXPLODE_WIDTH, UFO_EXPLODE_HEIGHT);
1014 return;
1018 /* Hit bomb? (check position, not pixel value) */
1019 for (i = 0; i < max_bombs; i++) {
1020 if (bombs[i].state == S_ACTIVE) {
1021 /* Count as hit if within BOMB_WIDTH pixels */
1022 if ((ABS(bombs[i].x - fire_x) < BOMB_WIDTH) &&
1023 (fire_y - bombs[i].y < BOMB_HEIGHT)) {
1024 /* Erase bomb */
1025 rb->lcd_fillrect(bombs[i].x, bombs[i].y, BOMB_WIDTH, BOMB_HEIGHT);
1026 bombs[i].state = S_IDLE;
1027 /* Explode ship fire */
1028 fire = S_EXPLODE;
1029 fire_target = TARGET_SHIELD;
1030 fire_alpha(fire_x, fire_y, LCD_WHITE);
1031 return;
1036 /* Check for hit*/
1037 for (i = FIRE_SPEED; i >= 0; i--) {
1038 pix = get_pixel(fire_x, fire_y + i);
1039 if(pix == screen_white) {
1040 hit_white = true;
1041 fire_y += i;
1042 break;
1044 if(pix == screen_green) {
1045 hit_green = true;
1046 fire_y += i;
1047 break;
1051 if (hit_green) {
1052 /* Hit shield */
1054 /* TODO: Play explode sound */
1056 fire = S_EXPLODE;
1057 fire_target = TARGET_SHIELD;
1058 /* Center explosion around hit pixel */
1059 fire_y -= FIRE_HEIGHT / 2;
1060 fire_alpha(fire_x, fire_y, SLIME_GREEN);
1061 return;
1064 if (hit_white) {
1066 /* Hit alien? */
1067 for (i = 0; i < 5 * ALIENS; i++) {
1068 if (aliens[i].state != DEAD &&
1069 (ABS(fire_x - (PLAYFIELD_X + LIVES_X + aliens[i].x * ALIEN_SPEED +
1070 ALIEN_WIDTH / 2)) <= ALIEN_WIDTH / 2) &&
1071 (ABS(fire_y - (ALIEN_START_Y + aliens[i].y * ALIEN_HEIGHT +
1072 ALIEN_HEIGHT / 2)) <= ALIEN_HEIGHT / 2)) {
1074 /* TODO: play alien hit sound */
1076 if (aliens[i].state == BOMBER) {
1077 /* Set (possible) alien above to bomber */
1078 for (j = i - ALIENS; j >= 0; j -= ALIENS) {
1079 if (aliens[j].state != DEAD) {
1080 /* printf("New bomber (%d, %d)\n", j % ALIENS, j / ALIENS); */
1081 aliens[j].state = BOMBER;
1082 break;
1086 aliens[i].state = DEAD;
1087 exploding_alien = i;
1088 score += scores[aliens[i].type];
1089 draw_score();
1090 /* Update score part of screen */
1091 rb->lcd_update_rect(SCORENUM_X, SCORENUM_Y,
1092 PLAYFIELD_WIDTH - 2 * NUMBERS_WIDTH, FONT_HEIGHT);
1094 /* Paralyze aliens S_EXPLODE frames */
1095 aliens_paralyzed = S_EXPLODE;
1096 rb->lcd_bitmap_part(invadrox, ALIEN_EXPLODE_SRC_X, ALIEN_EXPLODE_SRC_Y,
1097 STRIDE, PLAYFIELD_X + LIVES_X + aliens[i].x * ALIEN_SPEED,
1098 ALIEN_START_Y + aliens[i].y * ALIEN_HEIGHT,
1099 ALIEN_EXPLODE_WIDTH, ALIEN_EXPLODE_HEIGHT);
1100 /* Since alien is 1 pixel taller than explosion sprite, erase bottom line */
1101 rb->lcd_hline(PLAYFIELD_X + LIVES_X + aliens[i].x * ALIEN_SPEED,
1102 PLAYFIELD_X + LIVES_X + aliens[i].x * ALIEN_SPEED + ALIEN_WIDTH,
1103 ALIEN_START_Y + (aliens[i].y + 1) * ALIEN_HEIGHT - 1);
1104 return;
1109 /* Draw shot */
1110 rb->lcd_set_foreground(LCD_WHITE);
1111 rb->lcd_vline(fire_x, fire_y, fire_y + SHOT_HEIGHT);
1112 rb->lcd_set_foreground(LCD_BLACK);
1113 } else if (fire < S_IDLE) {
1114 /* Count up towards S_IDLE, then erase explosion */
1115 fire++;
1116 if (fire == S_IDLE) {
1117 /* Erase explosion */
1118 if (fire_target == TARGET_TOP) {
1119 rb->lcd_fillrect(fire_x - (FIRE_WIDTH / 2), fire_y, FIRE_WIDTH, FIRE_HEIGHT);
1120 } else if (fire_target == TARGET_SHIELD) {
1121 /* Draw explosion with black pixels */
1122 fire_alpha(fire_x, fire_y, LCD_BLACK);
1129 /* Return a BOMBER alien */
1130 inline int random_bomber(void)
1132 int i, col;
1134 /* TODO: Weigh higher probability near ship */
1135 col = rb->rand() % ALIENS;
1136 for (i = col + 4 * ALIENS; i >= 0; i -= ALIENS) {
1137 if (aliens[i].state == BOMBER) {
1138 return i;
1142 /* No BOMBER found in this col */
1144 for (i = 0; i < 5 * ALIENS; i++) {
1145 if (aliens[i].state == BOMBER) {
1146 return i;
1150 /* No BOMBER found at all (error?) */
1152 return -1;
1156 inline void draw_bomb(int i)
1158 rb->lcd_bitmap_part(invadrox, BOMB_SRC_X + bombs[i].type * BOMB_WIDTH,
1159 bombs[i].frame * (BOMB_HEIGHT + 1),
1160 STRIDE, bombs[i].x, bombs[i].y,
1161 BOMB_WIDTH, BOMB_HEIGHT);
1162 /* Advance frame */
1163 bombs[i].frame++;
1164 if (bombs[i].frame == bombs[i].frames) {
1165 bombs[i].frame = 0;
1170 void move_bombs(void)
1172 int i, j, bomber;
1173 bool abort;
1175 for (i = 0; i < max_bombs; i++) {
1177 switch (bombs[i].state) {
1179 case S_IDLE:
1180 if (ship_hit) {
1181 continue;
1183 bomber = random_bomber();
1184 if (bomber < 0) {
1185 DBG("ERROR: No bomber available\n");
1186 continue;
1188 /* x, y */
1189 bombs[i].x = PLAYFIELD_X + LIVES_X + aliens[bomber].x * ALIEN_SPEED + ALIEN_WIDTH / 2;
1190 bombs[i].y = ALIEN_START_Y + (aliens[bomber].y + 1) * ALIEN_HEIGHT;
1192 /* Check for duplets in x and y direction */
1193 abort = false;
1194 for (j = i - 1; j >= 0; j--) {
1195 if ((bombs[j].state == S_ACTIVE) &&
1196 ((bombs[i].x == bombs[j].x) || (bombs[i].y == bombs[j].y))) {
1197 abort = true;
1198 break;
1201 if (abort) {
1202 /* Skip this one, continue with next bomb */
1203 /* printf("Bomb %d duplet of %d\n", i, j); */
1204 continue;
1207 /* Passed, set type */
1208 bombs[i].type = rb->rand() % 3;
1209 bombs[i].frame = 0;
1210 if (bombs[i].type == 0) {
1211 bombs[i].frames = 3;
1212 } else if (bombs[i].type == 1) {
1213 bombs[i].frames = 4;
1214 } else {
1215 bombs[i].frames = 6;
1218 /* Bombs away */
1219 bombs[i].state = S_ACTIVE;
1220 draw_bomb(i);
1221 continue;
1223 break;
1225 case S_ACTIVE:
1226 /* Erase old position */
1227 rb->lcd_fillrect(bombs[i].x, bombs[i].y, BOMB_WIDTH, BOMB_HEIGHT);
1229 /* Move */
1230 bombs[i].y += BOMB_SPEED;
1232 /* Check if bottom hit */
1233 if (bombs[i].y + BOMB_HEIGHT >= PLAYFIELD_Y) {
1234 bombs[i].y = PLAYFIELD_Y - FIRE_HEIGHT + 1;
1235 fire_alpha(bombs[i].x, bombs[i].y, LCD_WHITE);
1236 bombs[i].state = S_EXPLODE;
1237 bombs[i].target = TARGET_BOTTOM;
1238 break;
1241 /* Check for green (ship or shield) */
1242 for (j = BOMB_HEIGHT; j >= BOMB_HEIGHT - BOMB_SPEED; j--) {
1243 bombs[i].target = 0;
1244 if(get_pixel(bombs[i].x + BOMB_WIDTH / 2, bombs[i].y + j) == screen_green) {
1245 /* Move to hit pixel */
1246 bombs[i].x += BOMB_WIDTH / 2;
1247 bombs[i].y += j;
1249 /* Check if ship is hit */
1250 if (bombs[i].y > SHIELD_Y + SHIELD_HEIGHT && bombs[i].y < PLAYFIELD_Y) {
1252 /* TODO: play ship hit sound */
1254 ship_hit = true;
1255 ship_frame = 1;
1256 ship_frame_counter = 0;
1257 bombs[i].state = S_EXPLODE * 4;
1258 bombs[i].target = TARGET_SHIP;
1259 rb->lcd_bitmap_part(invadrox, SHIP_SRC_X, 1 * SHIP_HEIGHT, STRIDE,
1260 ship_x, SHIP_Y, SHIP_WIDTH, SHIP_HEIGHT);
1261 break;
1263 /* Shield hit */
1264 bombs[i].state = S_EXPLODE;
1265 bombs[i].target = TARGET_SHIELD;
1266 /* Center explosion around hit pixel in shield */
1267 bombs[i].y -= FIRE_HEIGHT / 2;
1268 fire_alpha(bombs[i].x, bombs[i].y, SLIME_GREEN);
1269 break;
1273 if (bombs[i].target != 0) {
1274 /* Hit ship or shield, continue */
1275 continue;
1278 draw_bomb(i);
1279 break;
1281 default:
1282 /* If we get here state should be < 0, exploding */
1283 bombs[i].state++;
1284 if (bombs[i].state == S_IDLE) {
1285 if (ship_hit) {
1286 /* Erase explosion */
1287 rb->lcd_fillrect(ship_x, SHIP_Y, SHIP_WIDTH, SHIP_HEIGHT);
1288 rb->lcd_update_rect(ship_x, SHIP_Y, SHIP_WIDTH, SHIP_HEIGHT);
1289 ship_hit = false;
1290 ship_frame = 0;
1291 ship_x = PLAYFIELD_X + 2 * LIVES_X;
1292 lives--;
1293 if (lives == 0) {
1294 game_over = true;
1295 return;
1297 draw_lives();
1298 /* Sleep 1s to give player time to examine lives left */
1299 rb->sleep(HZ);
1301 /* Erase explosion (even if ship hit, might be another bomb) */
1302 fire_alpha(bombs[i].x, bombs[i].y, LCD_BLACK);
1304 break;
1310 inline void move_ship(void)
1312 ship_dir += ship_acc;
1313 if (ship_dir > max_ship_speed) {
1314 ship_dir = max_ship_speed;
1316 if (ship_dir < -max_ship_speed) {
1317 ship_dir = -max_ship_speed;
1319 ship_x += ship_dir;
1320 if (ship_x < SHIP_MIN_X) {
1321 ship_x = SHIP_MIN_X;
1323 if (ship_x > SHIP_MAX_X) {
1324 ship_x = SHIP_MAX_X;
1327 draw_ship();
1331 /* Unidentified Flying Object */
1332 void move_ufo(void)
1334 static int ufo_speed;
1335 static int counter;
1336 int mystery_score;
1338 switch (ufo_state) {
1340 case S_IDLE:
1342 if (rb->rand() % 500 == 0) {
1343 /* Uh-oh, it's time to launch a mystery UFO */
1345 /* TODO: Play UFO sound */
1347 if (rb->rand() % 2) {
1348 ufo_speed = UFO_SPEED;
1349 ufo_x = PLAYFIELD_X;
1350 } else {
1351 ufo_speed = -UFO_SPEED;
1352 ufo_x = LCD_WIDTH - PLAYFIELD_X - UFO_WIDTH;
1354 ufo_state = S_ACTIVE;
1355 /* UFO will be drawn next frame */
1357 break;
1359 case S_ACTIVE:
1360 /* Erase old pos */
1361 rb->lcd_fillrect(ufo_x, UFO_Y, UFO_WIDTH, UFO_HEIGHT);
1362 /* Move */
1363 ufo_x += ufo_speed;
1364 /* Check bounds */
1365 if (ufo_x < PLAYFIELD_X || ufo_x > LCD_WIDTH - PLAYFIELD_X - UFO_WIDTH) {
1366 ufo_state = S_IDLE;
1367 break;
1369 /* Draw new pos */
1370 rb->lcd_bitmap_part(invadrox, UFO_SRC_X, 0,
1371 STRIDE, ufo_x, UFO_Y, UFO_WIDTH, UFO_HEIGHT);
1372 break;
1374 case S_SHOWSCORE:
1375 counter++;
1376 if (counter == S_IDLE) {
1377 /* Erase mystery number */
1378 rb->lcd_fillrect(ufo_x, UFO_Y, 3 * NUMBERS_WIDTH + 2 * NUM_SPACING, FONT_HEIGHT);
1379 ufo_state = S_IDLE;
1381 break;
1383 default:
1384 /* Exploding */
1385 ufo_state++;
1386 if (ufo_state == S_IDLE) {
1387 /* Erase explosion */
1388 rb->lcd_fillrect(ufo_x, UFO_Y - 1, UFO_EXPLODE_WIDTH, UFO_EXPLODE_HEIGHT);
1389 ufo_state = S_SHOWSCORE;
1390 counter = S_EXPLODE * 4;
1391 /* Draw mystery_score, sleep, increase score and continue */
1392 mystery_score = 50 + (rb->rand() % 6) * 50;
1393 if (mystery_score < 100) {
1394 draw_number(ufo_x, UFO_Y, mystery_score, 2);
1395 } else {
1396 draw_number(ufo_x, UFO_Y, mystery_score, 3);
1398 score += mystery_score;
1399 draw_score();
1401 break;
1406 void draw_background(void)
1409 #if (LCD_WIDTH == 320) && (LCD_HEIGHT == 240)
1410 /* Erase background to black */
1411 rb->lcd_fillrect(PLAYFIELD_X, 0, PLAYFIELD_WIDTH, LCD_HEIGHT);
1412 /* Left and right bitmaps */
1413 rb->lcd_bitmap(invadrox_left, 0, 0, PLAYFIELD_X, LCD_HEIGHT);
1414 rb->lcd_bitmap(invadrox_right, LCD_WIDTH - PLAYFIELD_X, 0, PLAYFIELD_X, LCD_HEIGHT);
1415 #else
1416 rb->lcd_fillrect(0, 0, LCD_WIDTH, LCD_HEIGHT);
1417 #endif
1419 #if ((LCD_WIDTH == 240) && (LCD_HEIGHT == 320)) || ((LCD_WIDTH == 176) && (LCD_HEIGHT == 220))
1420 rb->lcd_bitmap(invadrox_logo, 0, 0, LCD_WIDTH, SCORE_Y);
1421 #endif
1423 rb->lcd_update();
1427 void new_level(void)
1429 int i;
1431 draw_background();
1432 /* Give an extra life for each new level */
1433 if (lives < MAX_LIVES) {
1434 lives++;
1436 draw_lives();
1438 /* Score */
1439 rb->lcd_bitmap_part(invadrox, SCORE_SRC_X, SCORE_SRC_Y,
1440 STRIDE, PLAYFIELD_X, SCORE_Y, SCORE_WIDTH, FONT_HEIGHT);
1441 /* Hi-score */
1442 rb->lcd_bitmap_part(invadrox, 0, SCORE_SRC_Y,
1443 STRIDE, HISCORE_X, SCORE_Y,
1444 HISCORE_WIDTH, FONT_HEIGHT);
1445 draw_score();
1446 draw_number(HISCORENUM_X, SCORENUM_Y, hiscore.score, 4);
1448 level++;
1449 draw_level();
1450 level_finished = false;
1452 ufo_state = S_IDLE;
1454 /* Init alien positions and states */
1455 for (i = 0; i < 4 * ALIENS; i++) {
1456 aliens[i].x = 0 + (i % ALIENS) * ((ALIEN_WIDTH + ALIEN_SPACING) / ALIEN_SPEED);
1457 aliens[i].y = 2 * (i / ALIENS);
1458 aliens[i].state = ALIVE;
1460 /* Last row, bombers */
1461 for (i = 4 * ALIENS; i < 5 * ALIENS; i++) {
1462 aliens[i].x = 0 + (i % ALIENS) * ((ALIEN_WIDTH + ALIEN_SPACING) / ALIEN_SPEED);
1463 aliens[i].y = 2 * (i / ALIENS);
1464 aliens[i].state = BOMBER;
1467 /* Init bombs to inactive (S_IDLE) */
1468 for (i = 0; i < MAX_BOMBS; i++) {
1469 bombs[i].state = S_IDLE;
1472 /* Start aliens closer to earth from level 2 */
1473 for (i = 0; i < 5 * ALIENS; i++) {
1474 if (level < 6) {
1475 aliens[i].y += level - 1;
1476 } else {
1477 aliens[i].y += 5;
1481 /* Max concurrent bombs */
1482 max_bombs = 1;
1484 gamespeed = 2;
1486 if (level > 1) {
1487 max_bombs++;
1490 /* Increase speed */
1491 if (level > 2) {
1492 gamespeed++;
1495 if (level > 3) {
1496 max_bombs++;
1499 /* Increase speed more */
1500 if (level > 4) {
1501 gamespeed++;
1504 if (level > 5) {
1505 max_bombs++;
1508 /* 4 shields */
1509 for (i = 1; i <= 4; i++) {
1510 rb->lcd_bitmap_part(invadrox, SHIELD_SRC_X, SHIELD_SRC_Y, STRIDE,
1511 PLAYFIELD_X + i * PLAYFIELD_WIDTH / 5 - SHIELD_WIDTH / 2,
1512 SHIELD_Y, SHIELD_WIDTH, SHIELD_HEIGHT);
1515 /* Bottom line */
1516 rb->lcd_set_foreground(SLIME_GREEN);
1517 rb->lcd_hline(PLAYFIELD_X, LCD_WIDTH - PLAYFIELD_X, PLAYFIELD_Y);
1518 /* Restore foreground to black (for fast erase later). */
1519 rb->lcd_set_foreground(LCD_BLACK);
1521 ship_x = PLAYFIELD_X + 2 * LIVES_X;
1522 if (level == 1) {
1523 old_ship_x = ship_x;
1525 ship_dir = 0;
1526 ship_acc = 0;
1527 ship_frame = 0;
1528 ship_hit = false;
1529 fire = S_IDLE;
1530 /* Start moving the bottom row left to right */
1531 curr_alien = 4 * ALIENS;
1532 aliens_paralyzed = 0;
1533 aliens_right = true;
1534 aliens_down = false;
1535 hit_left_border = false;
1536 hit_right_border = false;
1537 /* TODO: Change max_ship_speed to 3 at higher levels */
1538 max_ship_speed = 2;
1540 draw_aliens();
1542 rb->lcd_update();
1546 void init_invadrox(void)
1548 int i;
1550 /* Seed random number generator with a "random" number */
1551 rb->srand(rb->get_time()->tm_sec + rb->get_time()->tm_min * 60);
1553 /* Precalculate start of each scanline */
1554 for (i = 0; i < LCD_HEIGHT; i++) {
1555 #if (LCD_DEPTH >= 8)
1556 ytab[i] = i * LCD_WIDTH;
1557 #elif (LCD_DEPTH == 2) && (LCD_PIXELFORMAT == HORIZONTAL_PACKING)
1558 ytab[i] = i * (LCD_WIDTH / 4);
1559 #elif (LCD_DEPTH == 2) && (LCD_PIXELFORMAT == VERTICAL_PACKING)
1560 ytab[i] = (i / 4) * LCD_WIDTH;
1561 #else
1562 #error pixelformat not implemented yet
1563 #endif
1566 rb->lcd_set_background(LCD_BLACK);
1567 rb->lcd_set_foreground(LCD_BLACK);
1569 highscore_init(rb);
1570 if (highscore_load(HISCOREFILE, &hiscore, 1) < 0) {
1571 /* Init hiscore to 0 */
1572 rb->strncpy(hiscore.name, "Invader", sizeof(hiscore.name));
1573 hiscore.score = 0;
1574 hiscore.level = 1;
1577 /* Init alien types in aliens array */
1578 for (i = 0; i < 1 * ALIENS; i++) {
1579 aliens[i].type = 0; /* Kang */
1581 for (; i < 3 * ALIENS; i++) {
1582 aliens[i].type = 1; /* Kodos */
1584 for (; i < 5 * ALIENS; i++) {
1585 aliens[i].type = 2; /* Serak */
1589 /* Save screen white color */
1590 rb->lcd_set_foreground(LCD_WHITE);
1591 rb->lcd_drawpixel(0, 0);
1592 rb->lcd_update_rect(0, 0, 1, 1);
1593 screen_white = get_pixel(0, 0);
1595 /* Save screen green color */
1596 rb->lcd_set_foreground(SLIME_GREEN);
1597 rb->lcd_drawpixel(0, 0);
1598 rb->lcd_update_rect(0, 0, 1, 1);
1599 screen_green = get_pixel(0, 0);
1601 /* Restore black foreground */
1602 rb->lcd_set_foreground(LCD_BLACK);
1604 new_level();
1606 /* Flash score at start */
1607 for (i = 0; i < 5; i++) {
1608 rb->lcd_fillrect(SCORENUM_X, SCORENUM_Y,
1609 4 * NUMBERS_WIDTH + 3 * NUM_SPACING,
1610 FONT_HEIGHT);
1611 rb->lcd_update_rect(SCORENUM_X, SCORENUM_Y,
1612 4 * NUMBERS_WIDTH + 3 * NUM_SPACING,
1613 FONT_HEIGHT);
1614 rb->sleep(HZ / 10);
1615 draw_number(SCORENUM_X, SCORENUM_Y, score, 4);
1616 rb->sleep(HZ / 10);
1621 inline bool handle_buttons(void)
1623 static unsigned int oldbuttonstate = 0;
1625 unsigned int released, pressed, newbuttonstate;
1627 if (ship_hit) {
1628 /* Don't allow ship movement during explosion */
1629 newbuttonstate = 0;
1630 } else {
1631 newbuttonstate = rb->button_status();
1633 if(newbuttonstate == oldbuttonstate) {
1634 if (newbuttonstate == 0) {
1635 /* No button pressed. Stop ship. */
1636 ship_acc = 0;
1637 if (ship_dir > 0) {
1638 ship_dir--;
1640 if (ship_dir < 0) {
1641 ship_dir++;
1644 /* return false; */
1645 goto check_usb;
1647 released = ~newbuttonstate & oldbuttonstate;
1648 pressed = newbuttonstate & ~oldbuttonstate;
1649 oldbuttonstate = newbuttonstate;
1650 if (pressed) {
1651 if (pressed & LEFT) {
1652 if (ship_acc > -1) {
1653 ship_acc--;
1656 if (pressed & RIGHT) {
1657 if (ship_acc < 1) {
1658 ship_acc++;
1661 if (pressed & FIRE) {
1662 if (fire == S_IDLE) {
1663 /* Fire shot */
1664 fire_x = ship_x + SHIP_WIDTH / 2;
1665 fire_y = SHIP_Y - SHOT_HEIGHT;
1666 fire = S_ACTIVE;
1667 /* TODO: play fire sound */
1670 #ifdef RC_QUIT
1671 if (pressed & RC_QUIT) {
1672 rb->splash(HZ * 1, "Quit");
1673 return true;
1675 #endif
1676 if (pressed & QUIT) {
1677 rb->splash(HZ * 1, "Quit");
1678 return true;
1681 if (released) {
1682 if ((released & LEFT)) {
1683 if (ship_acc < 1) {
1684 ship_acc++;
1687 if ((released & RIGHT)) {
1688 if (ship_acc > -1) {
1689 ship_acc--;
1694 check_usb:
1696 /* Quit if USB is connected */
1697 if (rb->button_get(false) == SYS_USB_CONNECTED) {
1698 return true;
1701 return false;
1705 void game_loop(void)
1707 int i, end;
1709 /* Print dimensions (just for debugging) */
1710 DBG("%03dx%03dx%02d\n", LCD_WIDTH, LCD_HEIGHT, LCD_DEPTH);
1712 /* Init */
1713 init_invadrox();
1715 while (1) {
1716 /* Convert CYCLETIME (in ms) to HZ */
1717 end = *rb->current_tick + (CYCLETIME * HZ) / 1000;
1719 if (handle_buttons()) {
1720 return;
1723 /* Animate */
1724 move_ship();
1725 move_fire();
1727 /* Check if level is finished (marked by move_fire) */
1728 if (level_finished) {
1729 /* TODO: Play level finished sound */
1730 new_level();
1733 move_ufo();
1735 /* Move aliens */
1736 if (!aliens_paralyzed && !ship_hit) {
1737 for (i = 0; i < gamespeed; i++) {
1738 if (!move_aliens()) {
1739 if (game_over) {
1740 return;
1746 /* Move alien bombs */
1747 move_bombs();
1748 if (game_over) {
1749 return;
1752 /* Update "playfield" rect */
1753 rb->lcd_update_rect(PLAYFIELD_X, SCORENUM_Y + FONT_HEIGHT,
1754 PLAYFIELD_WIDTH,
1755 PLAYFIELD_Y + 1 - SCORENUM_Y - FONT_HEIGHT);
1757 /* Wait until next frame */
1758 DBG("%d (%d)\n", end - *rb->current_tick, (CYCLETIME * HZ) / 1000);
1759 if (end > *rb->current_tick) {
1760 rb->sleep(end - *rb->current_tick);
1761 } else {
1762 rb->yield();
1765 } /* end while */
1769 /* this is the plugin entry point */
1770 enum plugin_status plugin_start(struct plugin_api* api, UNUSED void* parameter)
1772 rb = api;
1774 rb->lcd_setfont(FONT_SYSFIXED);
1775 /* Turn off backlight timeout */
1776 backlight_force_on(rb); /* backlight control in lib/helper.c */
1778 /* now go ahead and have fun! */
1779 game_loop();
1781 /* Game Over. */
1782 /* TODO: Play game over sound */
1783 rb->splash(HZ * 2, "Game Over");
1784 if (score > hiscore.score) {
1785 /* Save new hiscore */
1786 hiscore.score = score;
1787 hiscore.level = level;
1788 highscore_save(HISCOREFILE, &hiscore, 1);
1791 /* Restore user's original backlight setting */
1792 rb->lcd_setfont(FONT_UI);
1793 /* Turn on backlight timeout (revert to settings) */
1794 backlight_use_settings(rb); /* backlight control in lib/helper.c */
1796 return PLUGIN_OK;
1802 * GNU Emacs settings: Kernighan & Richie coding style with
1803 * 4 spaces indent and no tabs.
1804 * Local Variables:
1805 * c-file-style: "k&r"
1806 * c-basic-offset: 4
1807 * indent-tabs-mode: nil
1808 * End: