Rename variables sectorbuf and verbose to avoid clashes in rbutil. Cleanup exports...
[Rockbox.git] / apps / plugins / invadrox.c
blob1294ed35c3010287802fa81978cf75ff726fb8e9
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 #elif CONFIG_KEYPAD == COWOND2_PAD
119 #define QUIT BUTTON_POWER
121 #else
122 #error INVADROX: Unsupported keypad
123 #endif
125 #ifdef HAVE_TOUCHPAD
126 #ifndef QUIT
127 #define QUIT BUTTON_TOPLEFT
128 #endif
129 #ifndef LEFT
130 #define LEFT BUTTON_MIDLEFT
131 #endif
132 #ifndef RIGHT
133 #define RIGHT BUTTON_MIDRIGHT
134 #endif
135 #ifndef FIRE
136 #define FIRE BUTTON_CENTER
137 #endif
138 #endif
140 #ifndef UNUSED
141 #define UNUSED __attribute__ ((unused))
142 #endif
144 #ifndef ABS
145 #define ABS(x) (((x) < 0) ? (-(x)) : (x))
146 #endif
149 /* Defines common to all models */
150 #define UFO_Y (SCORENUM_Y + FONT_HEIGHT + ALIEN_HEIGHT)
151 #define PLAYFIELD_Y (LCD_HEIGHT - SHIP_HEIGHT - 2)
152 #define PLAYFIELD_WIDTH (LCD_WIDTH - 2 * PLAYFIELD_X)
153 #define LEVEL_X (LCD_WIDTH - PLAYFIELD_X - LIVES_X - LEVEL_WIDTH - 2 * NUMBERS_WIDTH - 3 * NUM_SPACING)
154 #define SHIP_MIN_X (PLAYFIELD_X + PLAYFIELD_WIDTH / 5 - SHIELD_WIDTH / 2 - SHIP_WIDTH)
155 #define SHIP_MAX_X (PLAYFIELD_X + 4 * PLAYFIELD_WIDTH / 5 + SHIELD_WIDTH / 2)
156 /* SCORE_Y = 0 for most targets. Gigabeat redefines it later. */
157 #define SCORE_Y 0
158 #define MAX_LIVES 8
161 /* iPod Video defines */
162 #if (LCD_WIDTH == 320) && (LCD_HEIGHT == 240)
164 /* Original arcade game size 224x240, 1bpp with
165 * red overlay at top and green overlay at bottom.
167 * iPod Video: 320x240x16
168 * ======================
169 * X: 48p padding at left/right gives 224p playfield in middle.
170 * 10p "border" gives 204p actual playfield. UFO use full 224p.
171 * Y: Use full 240p.
173 * MAX_X = (204 - 12) / 2 - 1 = 95
175 * Y: Score text 7 0
176 * Space 10 7
177 * Score 7 17
178 * Space 8 24
179 * 3 Ufo 7 32
180 * 2 Space Aliens start at 32 + 3 * 8 = 56
181 * 0 aliens 9*8 56 -
182 * space ~7*8 128 | 18.75 aliens space between
183 * shield 2*8 182 | first alien and ship.
184 * space 8 198 | MAX_Y = 18
185 * ship 8 206 -
186 * space 2*8 214
187 * hline 1 230 - PLAYFIELD_Y
188 * bottom border 10 240
189 * Lives and Level goes inside bottom border
192 #define ARCADISH_GRAPHICS
193 #define PLAYFIELD_X 48
194 #define SHIP_Y (PLAYFIELD_Y - 3 * SHIP_HEIGHT)
195 #define ALIEN_START_Y (UFO_Y + 3 * ALIEN_HEIGHT)
196 #define SCORENUM_X (PLAYFIELD_X + NUMBERS_WIDTH)
197 #define SCORENUM_Y SCORE_Y + (2 * (FONT_HEIGHT + 1) + 1)
198 #define HISCORE_X (LCD_WIDTH - PLAYFIELD_X - HISCORE_WIDTH)
199 #define HISCORENUM_X (LCD_WIDTH - PLAYFIELD_X - 1 - 6 * NUMBERS_WIDTH - 5 * NUM_SPACING)
200 #define SHIELD_Y (PLAYFIELD_Y - 6 * SHIP_HEIGHT)
201 #define LIVES_X 10
202 #define MAX_X 95
203 #define MAX_Y 18
206 #elif (LCD_WIDTH == 176) && (LCD_HEIGHT == 220)
208 /* Sandisk Sansa e200: 176x220x16
209 * ==============================
210 * X: No padding. 8p border -> 160p playfield.
212 * 8p Aliens with 3p spacing -> 88 + 30 = 118p aliens block.
213 * (160 - 118) / 2 = 21 rounds for whole block (more than original)
214 * MAX_X = (160 - 8) / 2 - 1 = 75 rounds for single alien (less than original)
216 * LOGO 70 0
217 * Score text 5 70
218 * Space 5 75
219 * Y Score 5 80
220 * Space 10 85
221 * 2 Ufo 5 95
222 * 2 Space 10 100
223 * 0 aliens 9*5 110 -
224 * space ~7*5 155 | 18.6 aliens space between
225 * shield 2*5 188 | first alien and ship.
226 * space 5 198 | MAX_Y = 18
227 * ship 5 203 -
228 * space 5 208
229 * hline 1 213 PLAYFIELD_Y
230 * bottom border 6
231 * LCD_HEIGHT 220
232 * Lives and Level goes inside bottom border
235 #define TINY_GRAPHICS
236 #define PLAYFIELD_X 0
237 #define SHIP_Y (PLAYFIELD_Y - 2 * SHIP_HEIGHT)
238 #define SHIELD_Y (SHIP_Y - SHIP_HEIGHT - SHIELD_HEIGHT)
239 #define ALIEN_START_Y (UFO_Y + 3 * SHIP_HEIGHT)
240 /* Redefine SCORE_Y */
241 #undef SCORE_Y
242 #define SCORE_Y 70
243 #define SCORENUM_X (PLAYFIELD_X + NUMBERS_WIDTH)
244 #define SCORENUM_Y (SCORE_Y + 2 * FONT_HEIGHT)
245 #define HISCORE_X (LCD_WIDTH - PLAYFIELD_X - HISCORE_WIDTH)
246 #define HISCORENUM_X (LCD_WIDTH - PLAYFIELD_X - 1 - 6 * NUMBERS_WIDTH - 5 * NUM_SPACING)
247 #define LIVES_X 8
248 #define MAX_X 75
249 #define MAX_Y 18
252 #elif (LCD_WIDTH == 176) && (LCD_HEIGHT == 132)
254 /* iPod Nano: 176x132x16
255 * ======================
256 * X: No padding. 8p border -> 160p playfield.
258 * LIVES_X 8
259 * ALIEN_WIDTH 8
260 * ALIEN_HEIGHT 5
261 * ALIEN_SPACING 3
262 * SHIP_WIDTH 10
263 * SHIP_HEIGHT 5
264 * FONT_HEIGHT 5
265 * UFO_WIDTH 10
266 * UFO_HEIGHT 5
267 * SHIELD_WIDTH 15
268 * SHIELD_HEIGHT 10
269 * MAX_X 75
270 * MAX_Y = 18
271 * ALIEN_START_Y (UFO_Y + 12)
273 * 8p Aliens with 3p spacing -> 88 + 30 = 118p aliens block.
274 * (160 - 118) / 2 = 21 rounds for whole block (more than original)
275 * MAX_X = (160 - 8) / 2 - 1 = 75 rounds for single alien (less than original)
277 * Y: Scoreline 5 0 (combine scoretext and numbers on same line)
278 * Space 5 5
279 * 1 Ufo 5 10
280 * 3 Space 7 15
281 * 2 aliens 9*5 22 -
282 * space ~7*5 67 | Just above 18 aliens space between
283 * shield 2*5 100 | first alien and ship.
284 * space 5 110 | MAX_Y = 18
285 * ship 5 115 -
286 * space 5 120
287 * hline 1 125 PLAYFIELD_Y
288 * bottom border 6 126
289 * LCD_HEIGHT 131
290 * Lives and Level goes inside bottom border
293 #define TINY_GRAPHICS
294 #define PLAYFIELD_X 0
295 #define SHIP_Y (PLAYFIELD_Y - 2 * SHIP_HEIGHT)
296 #define ALIEN_START_Y (UFO_Y + 12)
297 #define SCORENUM_X (PLAYFIELD_X + SCORE_WIDTH + NUMBERS_WIDTH + NUM_SPACING)
298 #define SCORENUM_Y SCORE_Y
299 #define HISCORENUM_X (LCD_WIDTH - PLAYFIELD_X - 4 * NUMBERS_WIDTH - 3 * NUM_SPACING)
300 #define HISCORE_X (HISCORENUM_X - NUMBERS_WIDTH - NUM_SPACING - HISCORE_WIDTH)
301 #define SHIELD_Y (SHIP_Y - SHIP_HEIGHT - SHIELD_HEIGHT)
302 #define LIVES_X 8
303 #define MAX_X 75
304 #define MAX_Y 18
306 #elif (LCD_WIDTH == 160) && (LCD_HEIGHT == 128)
308 /* iAudio X5, iRiver H10 20Gb, iPod 3g/4g: 160x128x16
309 * ======================================
310 * X: No padding. No border -> 160p playfield.
312 * LIVES_X 0
313 * ALIEN_WIDTH 8
314 * ALIEN_HEIGHT 5
315 * ALIEN_SPACING 3
316 * SHIP_WIDTH 10
317 * SHIP_HEIGHT 5
318 * FONT_HEIGHT 5
319 * UFO_WIDTH 10
320 * UFO_HEIGHT 5
321 * SHIELD_WIDTH 15
322 * SHIELD_HEIGHT 10
323 * MAX_X 75
324 * MAX_Y = 18
325 * ALIEN_START_Y (UFO_Y + 10)
327 * 8p Aliens with 3p spacing -> 88 + 30 = 118p aliens block.
328 * (160 - 118) / 2 = 21 rounds for whole block (more than original)
329 * MAX_X = (160 - 8) / 2 - 1 = 75 rounds for single alien (less than original)
331 * Y: Scoreline 5 0 (combine scoretext and numbers on same line)
332 * Space 5 5
333 * 1 Ufo 5 10
334 * 2 Space 5 15
335 * 8 aliens 9*5 20 -
336 * space ~6*5 65 | Just above 18 aliens space between
337 * shield 2*5 96 | first alien and ship.
338 * space 5 106 | MAX_Y = 18
339 * ship 5 111 -
340 * space 5 116
341 * hline 1 121 PLAYFIELD_Y
342 * bottom border 6 122
343 * LCD_HEIGHT 128
344 * Lives and Level goes inside bottom border
347 #define TINY_GRAPHICS
348 #define PLAYFIELD_X 0
349 #define SHIP_Y (PLAYFIELD_Y - 2 * SHIP_HEIGHT)
350 #define ALIEN_START_Y (UFO_Y + 10)
351 #define SCORENUM_X (PLAYFIELD_X + SCORE_WIDTH + NUMBERS_WIDTH + NUM_SPACING)
352 #define SCORENUM_Y SCORE_Y
353 #define HISCORENUM_X (LCD_WIDTH - PLAYFIELD_X - 4 * NUMBERS_WIDTH - 3 * NUM_SPACING)
354 #define HISCORE_X (HISCORENUM_X - NUMBERS_WIDTH - NUM_SPACING - HISCORE_WIDTH)
355 #define SHIELD_Y (SHIP_Y - SHIP_HEIGHT - SHIELD_HEIGHT)
356 #define LIVES_X 0
357 #define MAX_X 75
358 #define MAX_Y 18
361 #elif (LCD_WIDTH == 240) && (LCD_HEIGHT == 320)
363 /* Gigabeat: 240x320x16
364 * ======================
365 * X: 8p padding at left/right gives 224p playfield in middle.
366 * 10p "border" gives 204p actual playfield. UFO use full 224p.
367 * Y: Use bottom 240p for playfield and top 80 pixels for logo.
369 * MAX_X = (204 - 12) / 2 - 1 = 95
371 * Y: Score text 7 0 + 80
372 * Space 10 7 + 80
373 * Score 7 17 + 80
374 * Space 8 24 + 80
375 * 3 Ufo 7 32 + 80
376 * 2 Space Aliens start at 32 + 3 * 8 = 56
377 * 0 aliens 9*8 56 -
378 * space ~7*8 128 | 18.75 aliens space between
379 * shield 2*8 182 | first alien and ship.
380 * space 8 198 | MAX_Y = 18
381 * ship 8 206 -
382 * space 2*8 214
383 * hline 1 230 310 - PLAYFIELD_Y
384 * bottom border 10 240 320
385 * Lives and Level goes inside bottom border
388 #define ARCADISH_GRAPHICS
389 #define PLAYFIELD_X 8
390 #define SHIP_Y (PLAYFIELD_Y - 3 * SHIP_HEIGHT)
391 #define ALIEN_START_Y (UFO_Y + 3 * ALIEN_HEIGHT)
392 /* Redefine SCORE_Y */
393 #undef SCORE_Y
394 #define SCORE_Y 80
395 #define SCORENUM_X (PLAYFIELD_X + NUMBERS_WIDTH)
396 #define SCORENUM_Y SCORE_Y + (2 * (FONT_HEIGHT + 1) + 1)
397 #define HISCORE_X (LCD_WIDTH - PLAYFIELD_X - HISCORE_WIDTH)
398 #define HISCORENUM_X (LCD_WIDTH - PLAYFIELD_X - 1 - 6 * NUMBERS_WIDTH - 5 * NUM_SPACING)
399 #define SHIELD_Y (PLAYFIELD_Y - 6 * SHIP_HEIGHT)
400 #define LIVES_X 10
401 #define MAX_X 95
402 #define MAX_Y 18
404 #elif (LCD_WIDTH == 220) && (LCD_HEIGHT == 176)
406 /* TPJ1022, H300, iPod Color: 220x176x16
407 * ============================
408 * X: 0p padding at left/right gives 220p playfield in middle.
409 * 8p "border" gives 204p actual playfield. UFO use full 220p.
410 * Y: Use full 176p for playfield.
412 * MAX_X = (204 - 12) / 2 - 1 = 95
414 * Y: Score text 7 0
415 * Space 8 7
416 * 1 Ufo 7 15
417 * 7 Space Aliens start at 15 + 3 * 8 = 56
418 * 6 aliens 9*8 25 -
419 * space ~7*8 103 | 15.6 aliens space between
420 * shield 2*8 126 | first alien and ship.
421 * space 8 142 | MAX_Y = 15
422 * ship 8 150 -
423 * space 8 158
424 * hline 1 166 - PLAYFIELD_Y
425 * bottom border 10 176
426 * Lives and Level goes inside bottom border
429 #define ARCADISH_GRAPHICS
430 #define PLAYFIELD_X 0
431 #define SHIP_Y (PLAYFIELD_Y - 2 * SHIP_HEIGHT)
432 #define ALIEN_START_Y (UFO_Y + 10)
433 #define SCORENUM_Y SCORE_Y
434 #define SCORENUM_X (PLAYFIELD_X + SCORE_WIDTH + NUMBERS_WIDTH + NUM_SPACING)
435 #define HISCORENUM_X (LCD_WIDTH - PLAYFIELD_X - 4 * NUMBERS_WIDTH - 3 * NUM_SPACING)
436 #define HISCORE_X (HISCORENUM_X - NUMBERS_WIDTH - NUM_SPACING - HISCORE_WIDTH)
437 #define SHIELD_Y (PLAYFIELD_Y - 5 * SHIP_HEIGHT)
438 #define LIVES_X 8
439 #define MAX_X 95
440 #define MAX_Y 15
442 #else
443 #error INVADROX: Unsupported LCD type
444 #endif
447 /* Defines common to each "graphic type" */
448 #ifdef ARCADISH_GRAPHICS
450 #define STRIDE 71
451 #define SHIP_SRC_X 24
452 #define SHIP_WIDTH 16
453 #define SHIP_HEIGHT 8
454 #define SHOT_HEIGHT 5
455 #define ALIEN_WIDTH 12
456 #define ALIEN_EXPLODE_SRC_X 52
457 #define ALIEN_EXPLODE_SRC_Y 39
458 #define ALIEN_EXPLODE_WIDTH 13
459 #define ALIEN_EXPLODE_HEIGHT 7
460 #define ALIEN_HEIGHT 8
461 #define ALIEN_SPACING 4
462 #define ALIEN_SPEED 2
463 #define UFO_SRC_X 40
464 #define UFO_WIDTH 16
465 #define UFO_HEIGHT 7
466 #define UFO_EXPLODE_WIDTH 21
467 #define UFO_EXPLODE_HEIGHT 8
468 #define UFO_SPEED 1
469 #define FONT_HEIGHT 7
470 #define LEVEL_SRC_Y 24
471 #define LEVEL_WIDTH 37
472 #define SCORE_SRC_X 24
473 #define SCORE_SRC_Y 31
474 #define SCORE_WIDTH 37
475 #define HISCORE_WIDTH 61
476 #define NUM_SPACING 3
477 #define NUMBERS_SRC_Y 38
478 #define NUMBERS_WIDTH 5
479 #define SHIELD_SRC_X 40
480 #define SHIELD_SRC_Y 15
481 #define SHIELD_WIDTH 22
482 #define SHIELD_HEIGHT 16
483 #define FIRE_WIDTH 8
484 #define FIRE_HEIGHT 8
485 #define FIRE_SPEED 8
486 #define BOMB_SRC_X 62
487 #define BOMB_WIDTH 3
488 #define BOMB_HEIGHT 7
489 #define BOMB_SPEED 3
490 #define ALIENS 11
491 unsigned char fire_sprite[FIRE_HEIGHT] = {
492 (1 << 7) | (0 << 6) | (0 << 5) | (0 << 4) | (1 << 3) | (0 << 2) | (0 << 1) | 1,
493 (0 << 7) | (0 << 6) | (1 << 5) | (0 << 4) | (0 << 3) | (0 << 2) | (1 << 1) | 0,
494 (0 << 7) | (1 << 6) | (1 << 5) | (1 << 4) | (1 << 3) | (1 << 2) | (1 << 1) | 0,
495 (1 << 7) | (1 << 6) | (1 << 5) | (1 << 4) | (1 << 3) | (1 << 2) | (1 << 1) | 1,
496 (0 << 7) | (1 << 6) | (1 << 5) | (1 << 4) | (1 << 3) | (1 << 2) | (1 << 1) | 1,
497 (0 << 7) | (1 << 6) | (1 << 5) | (1 << 4) | (1 << 3) | (1 << 2) | (1 << 1) | 0,
498 (0 << 7) | (0 << 6) | (1 << 5) | (0 << 4) | (0 << 3) | (1 << 2) | (0 << 1) | 0,
499 (1 << 7) | (0 << 6) | (0 << 5) | (1 << 4) | (0 << 3) | (0 << 2) | (0 << 1) | 1
502 #elif defined TINY_GRAPHICS
504 #define STRIDE 53
505 #define SHIP_SRC_X 16
506 #define SHIP_WIDTH 10
507 #define SHIP_HEIGHT 5
508 #define SHOT_HEIGHT 4
509 #define ALIEN_WIDTH 8
510 #define ALIEN_HEIGHT 5
511 #define ALIEN_EXPLODE_SRC_X 40
512 #define ALIEN_EXPLODE_SRC_Y 26
513 #define ALIEN_EXPLODE_WIDTH 10
514 #define ALIEN_EXPLODE_HEIGHT 5
515 #define ALIEN_SPACING 3
516 #define ALIEN_SPEED 2
517 #define UFO_SRC_X 26
518 #define UFO_WIDTH 11
519 #define UFO_HEIGHT 5
520 #define UFO_EXPLODE_WIDTH 14
521 #define UFO_EXPLODE_HEIGHT 5
522 #define UFO_SPEED 1
523 #define FONT_HEIGHT 5
524 #define LEVEL_SRC_Y 15
525 #define LEVEL_WIDTH 29
526 #define NUMBERS_WIDTH 4
527 #define NUM_SPACING 2
528 #define SCORE_SRC_X 17
529 #define SCORE_SRC_Y 20
530 #define SCORE_WIDTH 28
531 #define HISCORE_WIDTH 45
532 #define NUMBERS_SRC_Y 25
533 #define SHIELD_SRC_X 29
534 #define SHIELD_SRC_Y 10
535 #define SHIELD_WIDTH 15
536 #define SHIELD_HEIGHT 10
537 #define FIRE_WIDTH 6
538 #define FIRE_HEIGHT 6
539 #define FIRE_SPEED 6
540 #define BOMB_SRC_X 44
541 #define BOMB_WIDTH 3
542 #define BOMB_HEIGHT 5
543 #define BOMB_SPEED 2
544 #define ALIENS 11
545 unsigned char fire_sprite[FIRE_HEIGHT] = {
546 (1 << 5) | (0 << 4) | (0 << 3) | (1 << 2) | (0 << 1) | 1,
547 (0 << 5) | (1 << 4) | (0 << 3) | (0 << 2) | (0 << 1) | 0,
548 (0 << 5) | (1 << 4) | (1 << 3) | (1 << 2) | (1 << 1) | 0,
549 (0 << 5) | (1 << 4) | (1 << 3) | (1 << 2) | (1 << 1) | 1,
550 (0 << 5) | (1 << 4) | (0 << 3) | (0 << 2) | (1 << 1) | 0,
551 (1 << 5) | (0 << 4) | (1 << 3) | (0 << 2) | (0 << 1) | 1
554 #else
555 #error Graphic type not defined
556 #endif
559 /* Colors */
560 #if (LCD_DEPTH >= 8)
561 #define SLIME_GREEN LCD_RGBPACK(31, 254, 31)
562 #define UFO_RED LCD_RGBPACK(254, 31, 31)
563 #elif (LCD_DEPTH == 2)
564 #define SLIME_GREEN LCD_LIGHTGRAY
565 #define UFO_RED LCD_LIGHTGRAY
566 #else
567 #error LCD type not implemented yet
568 #endif
570 /* Alien states */
571 #define DEAD 0
572 #define ALIVE 1
573 #define BOMBER 2
575 /* Fire/bomb/ufo states */
576 #define S_IDLE 0
577 #define S_ACTIVE 1
578 #define S_SHOWSCORE 2
579 #define S_EXPLODE -9
581 /* Fire/bomb targets */
582 #define TARGET_TOP 0
583 #define TARGET_SHIELD 1
584 #define TARGET_SHIP 2
585 #define TARGET_BOTTOM 3
586 #define TARGET_UFO 4
588 #define HISCOREFILE PLUGIN_GAMES_DIR "/invadrox.high"
591 /* The time (in ms) for one iteration through the game loop - decrease this
592 * to speed up the game - note that current_tick is (currently) only accurate
593 * to 10ms.
595 #define CYCLETIME 40
598 static const struct plugin_api* rb;
600 /* Physical x is at PLAYFIELD_X + LIVES_X + x * ALIEN_SPEED
601 * Physical y is at y * ALIEN_HEIGHT
603 struct alien {
604 unsigned char x; /* x-coordinate (0 - 95) */
605 unsigned char y; /* y-coordinate (0 - 18) */
606 unsigned char type; /* 0 (Kang), 1 (Kodos), 2 (Serak) */
607 unsigned char state; /* Dead, alive or bomber */
610 /* Aliens box 5 rows * ALIENS aliens in each row */
611 struct alien aliens[5 * ALIENS];
613 #define MAX_BOMBS 4
614 struct bomb {
615 int x, y;
616 unsigned char type;
617 unsigned char frame; /* Current animation frame */
618 unsigned char frames; /* Number of frames in animation */
619 unsigned char target; /* Remember target during explosion frames */
620 int state; /* 0 (IDLE) = inactive, 1 (FIRE) or negative, exploding */
622 struct bomb bombs[MAX_BOMBS];
623 /* Increase max_bombs at higher levels */
624 int max_bombs;
626 /* Raw framebuffer value of shield/ship green color */
627 fb_data screen_green, screen_white;
629 /* For optimization, precalculate startoffset of each scanline */
630 unsigned int ytab[LCD_HEIGHT];
632 /* external bitmaps */
633 extern const fb_data invadrox[];
634 #if (LCD_WIDTH == 320) && (LCD_HEIGHT == 240)
635 /* iPod Video only */
636 extern const fb_data invadrox_left[];
637 extern const fb_data invadrox_right[];
638 #endif
639 #if ((LCD_WIDTH == 240) && (LCD_HEIGHT == 320)) || ((LCD_WIDTH == 176) && (LCD_HEIGHT == 220))
640 /* Gigabeat F, Sansa e200 */
641 extern const fb_data invadrox_logo[];
642 #endif
645 int lives = 2;
646 int score = 0;
647 int scores[3] = { 30, 20, 10 };
648 int level = 0;
649 struct highscore hiscore;
650 bool game_over = false;
651 int ship_x, old_ship_x, ship_dir, ship_acc, max_ship_speed;
652 int ship_frame, ship_frame_counter;
653 bool ship_hit;
654 int fire, fire_target, fire_x, fire_y;
655 int curr_alien, aliens_paralyzed, gamespeed;
656 int ufo_state, ufo_x;
657 bool level_finished;
658 bool aliens_down, aliens_right, hit_left_border, hit_right_border;
661 /* No standard get_pixel function yet, use this hack instead */
662 #if (LCD_DEPTH >= 8)
664 inline fb_data get_pixel(int x, int y)
666 return rb->lcd_framebuffer[ytab[y] + x];
669 #elif (LCD_DEPTH == 2)
671 #if (LCD_PIXELFORMAT == HORIZONTAL_PACKING)
672 static const unsigned char shifts[4] = {
673 6, 4, 2, 0
675 /* Horizontal packing */
676 inline fb_data get_pixel(int x, int y)
678 return (rb->lcd_framebuffer[ytab[y] + (x >> 2)] >> shifts[x & 3]) & 3;
680 #else
681 /* Vertical packing */
682 static const unsigned char shifts[4] = {
683 0, 2, 4, 6
685 inline fb_data get_pixel(int x, int y)
687 return (rb->lcd_framebuffer[ytab[y] + x] >> shifts[y & 3]) & 3;
689 #endif /* Horizontal/Vertical packing */
691 #else
692 #error get_pixel: pixelformat not implemented yet
693 #endif
696 /* Draw "digits" least significant digits of num at (x,y) */
697 void draw_number(int x, int y, int num, int digits)
699 int i;
700 int d;
702 for (i = digits - 1; i >= 0; i--) {
703 d = num % 10;
704 num = num / 10;
705 rb->lcd_bitmap_part(invadrox, d * NUMBERS_WIDTH, NUMBERS_SRC_Y,
706 STRIDE, x + i * (NUMBERS_WIDTH + NUM_SPACING), y,
707 NUMBERS_WIDTH, FONT_HEIGHT);
709 /* Update lcd */
710 rb->lcd_update_rect(x, y, 4 * NUMBERS_WIDTH + 3 * NUM_SPACING, FONT_HEIGHT);
714 inline void draw_score(void)
716 draw_number(SCORENUM_X, SCORENUM_Y, score, 4);
717 if (score > hiscore.score) {
718 /* Draw new hiscore (same as score) */
719 draw_number(HISCORENUM_X, SCORENUM_Y, score, 4);
724 void draw_level(void)
726 rb->lcd_bitmap_part(invadrox, 0, LEVEL_SRC_Y,
727 STRIDE, LEVEL_X, PLAYFIELD_Y + 2,
728 LEVEL_WIDTH, FONT_HEIGHT);
729 draw_number(LEVEL_X + LEVEL_WIDTH + 2 * NUM_SPACING, PLAYFIELD_Y + 2, level, 2);
733 void draw_lives(void)
735 int i;
736 /* Lives num */
737 rb->lcd_bitmap_part(invadrox, lives * NUMBERS_WIDTH, NUMBERS_SRC_Y,
738 STRIDE, PLAYFIELD_X + LIVES_X, PLAYFIELD_Y + 2,
739 NUMBERS_WIDTH, FONT_HEIGHT);
741 /* Ships */
742 for (i = 0; i < (lives - 1); i++) {
743 rb->lcd_bitmap_part(invadrox, SHIP_SRC_X, 0, STRIDE,
744 PLAYFIELD_X + LIVES_X + SHIP_WIDTH + i * (SHIP_WIDTH + NUM_SPACING),
745 PLAYFIELD_Y + 1, SHIP_WIDTH, SHIP_HEIGHT);
748 /* Erase ship to the righ (if less than MAX_LIVES) */
749 if (lives < MAX_LIVES) {
750 rb->lcd_fillrect(PLAYFIELD_X + LIVES_X + SHIP_WIDTH + i * (SHIP_WIDTH + NUM_SPACING),
751 PLAYFIELD_Y + 1, SHIP_WIDTH, SHIP_HEIGHT);
753 /* Update lives (and level) part of screen */
754 rb->lcd_update_rect(PLAYFIELD_X + LIVES_X, PLAYFIELD_Y + 1,
755 PLAYFIELD_WIDTH - 2 * LIVES_X, MAX(FONT_HEIGHT + 1, SHIP_HEIGHT + 1));
759 inline void draw_aliens(void)
761 int i;
763 for (i = 0; i < 5 * ALIENS; i++) {
764 rb->lcd_bitmap_part(invadrox, aliens[i].x & 1 ? ALIEN_WIDTH : 0, aliens[i].type * ALIEN_HEIGHT,
765 STRIDE, PLAYFIELD_X + LIVES_X + aliens[i].x * ALIEN_SPEED,
766 ALIEN_START_Y + aliens[i].y * ALIEN_HEIGHT,
767 ALIEN_WIDTH, ALIEN_HEIGHT);
772 /* Return false if there is no next alive alien (round is over) */
773 inline bool next_alien(void)
775 bool ret = true;
777 do {
778 curr_alien++;
779 if (curr_alien % ALIENS == 0) {
780 /* End of this row. Move up one row. */
781 curr_alien -= 2 * ALIENS;
782 if (curr_alien < 0) {
783 /* No more aliens in this round. */
784 curr_alien = 4 * ALIENS;
785 ret = false;
788 } while (aliens[curr_alien].state == DEAD && ret);
790 if (!ret) {
791 /* No more alive aliens. Round finished. */
792 if (hit_right_border) {
793 if (hit_left_border) {
794 DBG("ERROR: both left and right borders are set (%d)\n", curr_alien);
796 /* Move down-left next round */
797 aliens_right = false;
798 aliens_down = true;
799 hit_right_border = false;
800 } else if (hit_left_border) {
801 /* Move down-right next round */
802 aliens_right = true;
803 aliens_down = true;
804 hit_left_border = false;
805 } else {
806 /* Not left nor right. Set down to false. */
807 aliens_down = false;
811 return ret;
815 /* All aliens have been moved.
816 * Set curr_alien to first alive.
817 * Return false if no-one is left alive.
819 bool first_alien(void)
821 int i, y;
823 for (y = 4; y >= 0; y--) {
824 for (i = y * ALIENS; i < (y + 1) * ALIENS; i++) {
825 if (aliens[i].state != DEAD) {
826 curr_alien = i;
827 return true;
832 /* All aliens dead. */
833 level_finished = true;
835 return false;
839 bool move_aliens(void)
841 int x, y, old_x, old_y;
843 /* Move current alien (curr_alien is pointing to a living alien) */
845 old_x = aliens[curr_alien].x;
846 old_y = aliens[curr_alien].y;
848 if (aliens_down) {
849 aliens[curr_alien].y++;
850 if (aliens[curr_alien].y == MAX_Y) {
851 /* Alien is at bottom. Game Over. */
852 DBG("Alien %d is at bottom. Game Over.\n", curr_alien);
853 game_over = true;
854 return false;
858 if (aliens_right) {
859 /* Moving right */
860 if (aliens[curr_alien].x < MAX_X) {
861 aliens[curr_alien].x++;
864 /* Now, after move, check if we hit the right border. */
865 if (aliens[curr_alien].x == MAX_X) {
866 hit_right_border = true;
869 } else {
870 /* Moving left */
871 if (aliens[curr_alien].x > 0) {
872 aliens[curr_alien].x--;
875 /* Now, after move, check if we hit the left border. */
876 if (aliens[curr_alien].x == 0) {
877 hit_left_border = true;
881 /* Erase old position */
882 x = PLAYFIELD_X + LIVES_X + old_x * ALIEN_SPEED;
883 y = ALIEN_START_Y + old_y * ALIEN_HEIGHT;
884 if (aliens[curr_alien].y != old_y) {
885 /* Moved in y-dir. Erase whole alien. */
886 rb->lcd_fillrect(x, y, ALIEN_WIDTH, ALIEN_HEIGHT);
887 } else {
888 if (aliens_right) {
889 /* Erase left edge */
890 rb->lcd_fillrect(x, y, ALIEN_SPEED, ALIEN_HEIGHT);
891 } else {
892 /* Erase right edge */
893 x += ALIEN_WIDTH - ALIEN_SPEED;
894 rb->lcd_fillrect(x, y, ALIEN_SPEED, ALIEN_HEIGHT);
898 /* Draw alien at new pos */
899 x = PLAYFIELD_X + LIVES_X + aliens[curr_alien].x * ALIEN_SPEED;
900 y = ALIEN_START_Y + aliens[curr_alien].y * ALIEN_HEIGHT;
901 rb->lcd_bitmap_part(invadrox,
902 aliens[curr_alien].x & 1 ? ALIEN_WIDTH : 0, aliens[curr_alien].type * ALIEN_HEIGHT,
903 STRIDE, x, y, ALIEN_WIDTH, ALIEN_HEIGHT);
905 if (!next_alien()) {
906 /* Round finished. Set curr_alien to first alive from bottom. */
907 if (!first_alien()) {
908 /* Should never happen. Taken care of in move_fire(). */
909 return false;
911 /* TODO: Play next background sound */
914 return true;
918 inline void draw_ship(void)
920 /* Erase old ship */
921 if (old_ship_x < ship_x) {
922 /* Move right. Erase leftmost part of ship. */
923 rb->lcd_fillrect(old_ship_x, SHIP_Y, ship_x - old_ship_x, SHIP_HEIGHT);
924 } else if (old_ship_x > ship_x) {
925 /* Move left. Erase rightmost part of ship. */
926 rb->lcd_fillrect(ship_x + SHIP_WIDTH, SHIP_Y, old_ship_x - ship_x, SHIP_HEIGHT);
929 /* Draw ship */
930 rb->lcd_bitmap_part(invadrox, SHIP_SRC_X, ship_frame * SHIP_HEIGHT,
931 STRIDE, ship_x, SHIP_Y, SHIP_WIDTH, SHIP_HEIGHT);
932 if (ship_hit) {
933 /* Alternate between frame 1 and 2 during hit */
934 ship_frame_counter++;
935 if (ship_frame_counter > 2) {
936 ship_frame_counter = 0;
937 ship_frame++;
938 if (ship_frame > 2) {
939 ship_frame = 1;
944 /* Save ship_x for next time */
945 old_ship_x = ship_x;
949 inline void fire_alpha(int xc, int yc, fb_data color)
951 int x, y;
952 unsigned char mask;
954 rb->lcd_set_foreground(color);
956 for (y = 0; y < FIRE_HEIGHT; y++) {
957 mask = 1 << (FIRE_WIDTH - 1);
958 for (x = -(FIRE_WIDTH / 2); x < (FIRE_WIDTH / 2); x++) {
959 if (fire_sprite[y] & mask) {
960 rb->lcd_drawpixel(xc + x, yc + y);
962 mask >>= 1;
966 rb->lcd_set_foreground(LCD_BLACK);
970 void move_fire(void)
972 bool hit_green = false;
973 bool hit_white = false;
974 int i, j;
975 static int exploding_alien = -1;
976 fb_data pix;
978 if (fire == S_IDLE) {
979 return;
982 /* Alien hit. Wait until explosion is finished. */
983 if (aliens_paralyzed < 0) {
984 aliens_paralyzed++;
985 if (aliens_paralyzed == 0) {
986 /* Erase exploding_alien */
987 rb->lcd_fillrect(PLAYFIELD_X + LIVES_X + aliens[exploding_alien].x * ALIEN_SPEED,
988 ALIEN_START_Y + aliens[exploding_alien].y * ALIEN_HEIGHT,
989 ALIEN_EXPLODE_WIDTH, ALIEN_HEIGHT);
990 fire = S_IDLE;
991 /* Special case. We killed curr_alien. */
992 if (exploding_alien == curr_alien) {
993 if (!next_alien()) {
994 /* Round finished. Set curr_alien to first alive from bottom. */
995 first_alien();
999 return;
1002 if (fire == S_ACTIVE) {
1004 /* Erase */
1005 rb->lcd_vline(fire_x, fire_y, fire_y + SHOT_HEIGHT);
1007 /* Check top */
1008 if (fire_y <= SCORENUM_Y + FONT_HEIGHT + 4) {
1010 /* TODO: Play explode sound */
1012 fire = S_EXPLODE;
1013 fire_target = TARGET_TOP;
1014 fire_alpha(fire_x, fire_y, UFO_RED);
1015 return;
1018 /* Move */
1019 fire_y -= FIRE_SPEED;
1021 /* Hit UFO? */
1022 if (ufo_state == S_ACTIVE) {
1023 if ((ABS(ufo_x + UFO_WIDTH / 2 - fire_x) <= UFO_WIDTH / 2) &&
1024 (fire_y <= UFO_Y + UFO_HEIGHT)) {
1025 ufo_state = S_EXPLODE;
1026 fire = S_EXPLODE;
1027 fire_target = TARGET_UFO;
1028 /* Center explosion */
1029 ufo_x -= (UFO_EXPLODE_WIDTH - UFO_WIDTH) / 2;
1030 rb->lcd_bitmap_part(invadrox, UFO_SRC_X, UFO_HEIGHT,
1031 STRIDE, ufo_x, UFO_Y - 1, UFO_EXPLODE_WIDTH, UFO_EXPLODE_HEIGHT);
1032 return;
1036 /* Hit bomb? (check position, not pixel value) */
1037 for (i = 0; i < max_bombs; i++) {
1038 if (bombs[i].state == S_ACTIVE) {
1039 /* Count as hit if within BOMB_WIDTH pixels */
1040 if ((ABS(bombs[i].x - fire_x) < BOMB_WIDTH) &&
1041 (fire_y - bombs[i].y < BOMB_HEIGHT)) {
1042 /* Erase bomb */
1043 rb->lcd_fillrect(bombs[i].x, bombs[i].y, BOMB_WIDTH, BOMB_HEIGHT);
1044 bombs[i].state = S_IDLE;
1045 /* Explode ship fire */
1046 fire = S_EXPLODE;
1047 fire_target = TARGET_SHIELD;
1048 fire_alpha(fire_x, fire_y, LCD_WHITE);
1049 return;
1054 /* Check for hit*/
1055 for (i = FIRE_SPEED; i >= 0; i--) {
1056 pix = get_pixel(fire_x, fire_y + i);
1057 if(pix == screen_white) {
1058 hit_white = true;
1059 fire_y += i;
1060 break;
1062 if(pix == screen_green) {
1063 hit_green = true;
1064 fire_y += i;
1065 break;
1069 if (hit_green) {
1070 /* Hit shield */
1072 /* TODO: Play explode sound */
1074 fire = S_EXPLODE;
1075 fire_target = TARGET_SHIELD;
1076 /* Center explosion around hit pixel */
1077 fire_y -= FIRE_HEIGHT / 2;
1078 fire_alpha(fire_x, fire_y, SLIME_GREEN);
1079 return;
1082 if (hit_white) {
1084 /* Hit alien? */
1085 for (i = 0; i < 5 * ALIENS; i++) {
1086 if (aliens[i].state != DEAD &&
1087 (ABS(fire_x - (PLAYFIELD_X + LIVES_X + aliens[i].x * ALIEN_SPEED +
1088 ALIEN_WIDTH / 2)) <= ALIEN_WIDTH / 2) &&
1089 (ABS(fire_y - (ALIEN_START_Y + aliens[i].y * ALIEN_HEIGHT +
1090 ALIEN_HEIGHT / 2)) <= ALIEN_HEIGHT / 2)) {
1092 /* TODO: play alien hit sound */
1094 if (aliens[i].state == BOMBER) {
1095 /* Set (possible) alien above to bomber */
1096 for (j = i - ALIENS; j >= 0; j -= ALIENS) {
1097 if (aliens[j].state != DEAD) {
1098 /* printf("New bomber (%d, %d)\n", j % ALIENS, j / ALIENS); */
1099 aliens[j].state = BOMBER;
1100 break;
1104 aliens[i].state = DEAD;
1105 exploding_alien = i;
1106 score += scores[aliens[i].type];
1107 draw_score();
1108 /* Update score part of screen */
1109 rb->lcd_update_rect(SCORENUM_X, SCORENUM_Y,
1110 PLAYFIELD_WIDTH - 2 * NUMBERS_WIDTH, FONT_HEIGHT);
1112 /* Paralyze aliens S_EXPLODE frames */
1113 aliens_paralyzed = S_EXPLODE;
1114 rb->lcd_bitmap_part(invadrox, ALIEN_EXPLODE_SRC_X, ALIEN_EXPLODE_SRC_Y,
1115 STRIDE, PLAYFIELD_X + LIVES_X + aliens[i].x * ALIEN_SPEED,
1116 ALIEN_START_Y + aliens[i].y * ALIEN_HEIGHT,
1117 ALIEN_EXPLODE_WIDTH, ALIEN_EXPLODE_HEIGHT);
1118 /* Since alien is 1 pixel taller than explosion sprite, erase bottom line */
1119 rb->lcd_hline(PLAYFIELD_X + LIVES_X + aliens[i].x * ALIEN_SPEED,
1120 PLAYFIELD_X + LIVES_X + aliens[i].x * ALIEN_SPEED + ALIEN_WIDTH,
1121 ALIEN_START_Y + (aliens[i].y + 1) * ALIEN_HEIGHT - 1);
1122 return;
1127 /* Draw shot */
1128 rb->lcd_set_foreground(LCD_WHITE);
1129 rb->lcd_vline(fire_x, fire_y, fire_y + SHOT_HEIGHT);
1130 rb->lcd_set_foreground(LCD_BLACK);
1131 } else if (fire < S_IDLE) {
1132 /* Count up towards S_IDLE, then erase explosion */
1133 fire++;
1134 if (fire == S_IDLE) {
1135 /* Erase explosion */
1136 if (fire_target == TARGET_TOP) {
1137 rb->lcd_fillrect(fire_x - (FIRE_WIDTH / 2), fire_y, FIRE_WIDTH, FIRE_HEIGHT);
1138 } else if (fire_target == TARGET_SHIELD) {
1139 /* Draw explosion with black pixels */
1140 fire_alpha(fire_x, fire_y, LCD_BLACK);
1147 /* Return a BOMBER alien */
1148 inline int random_bomber(void)
1150 int i, col;
1152 /* TODO: Weigh higher probability near ship */
1153 col = rb->rand() % ALIENS;
1154 for (i = col + 4 * ALIENS; i >= 0; i -= ALIENS) {
1155 if (aliens[i].state == BOMBER) {
1156 return i;
1160 /* No BOMBER found in this col */
1162 for (i = 0; i < 5 * ALIENS; i++) {
1163 if (aliens[i].state == BOMBER) {
1164 return i;
1168 /* No BOMBER found at all (error?) */
1170 return -1;
1174 inline void draw_bomb(int i)
1176 rb->lcd_bitmap_part(invadrox, BOMB_SRC_X + bombs[i].type * BOMB_WIDTH,
1177 bombs[i].frame * (BOMB_HEIGHT + 1),
1178 STRIDE, bombs[i].x, bombs[i].y,
1179 BOMB_WIDTH, BOMB_HEIGHT);
1180 /* Advance frame */
1181 bombs[i].frame++;
1182 if (bombs[i].frame == bombs[i].frames) {
1183 bombs[i].frame = 0;
1188 void move_bombs(void)
1190 int i, j, bomber;
1191 bool abort;
1193 for (i = 0; i < max_bombs; i++) {
1195 switch (bombs[i].state) {
1197 case S_IDLE:
1198 if (ship_hit) {
1199 continue;
1201 bomber = random_bomber();
1202 if (bomber < 0) {
1203 DBG("ERROR: No bomber available\n");
1204 continue;
1206 /* x, y */
1207 bombs[i].x = PLAYFIELD_X + LIVES_X + aliens[bomber].x * ALIEN_SPEED + ALIEN_WIDTH / 2;
1208 bombs[i].y = ALIEN_START_Y + (aliens[bomber].y + 1) * ALIEN_HEIGHT;
1210 /* Check for duplets in x and y direction */
1211 abort = false;
1212 for (j = i - 1; j >= 0; j--) {
1213 if ((bombs[j].state == S_ACTIVE) &&
1214 ((bombs[i].x == bombs[j].x) || (bombs[i].y == bombs[j].y))) {
1215 abort = true;
1216 break;
1219 if (abort) {
1220 /* Skip this one, continue with next bomb */
1221 /* printf("Bomb %d duplet of %d\n", i, j); */
1222 continue;
1225 /* Passed, set type */
1226 bombs[i].type = rb->rand() % 3;
1227 bombs[i].frame = 0;
1228 if (bombs[i].type == 0) {
1229 bombs[i].frames = 3;
1230 } else if (bombs[i].type == 1) {
1231 bombs[i].frames = 4;
1232 } else {
1233 bombs[i].frames = 6;
1236 /* Bombs away */
1237 bombs[i].state = S_ACTIVE;
1238 draw_bomb(i);
1239 continue;
1241 break;
1243 case S_ACTIVE:
1244 /* Erase old position */
1245 rb->lcd_fillrect(bombs[i].x, bombs[i].y, BOMB_WIDTH, BOMB_HEIGHT);
1247 /* Move */
1248 bombs[i].y += BOMB_SPEED;
1250 /* Check if bottom hit */
1251 if (bombs[i].y + BOMB_HEIGHT >= PLAYFIELD_Y) {
1252 bombs[i].y = PLAYFIELD_Y - FIRE_HEIGHT + 1;
1253 fire_alpha(bombs[i].x, bombs[i].y, LCD_WHITE);
1254 bombs[i].state = S_EXPLODE;
1255 bombs[i].target = TARGET_BOTTOM;
1256 break;
1259 /* Check for green (ship or shield) */
1260 for (j = BOMB_HEIGHT; j >= BOMB_HEIGHT - BOMB_SPEED; j--) {
1261 bombs[i].target = 0;
1262 if(get_pixel(bombs[i].x + BOMB_WIDTH / 2, bombs[i].y + j) == screen_green) {
1263 /* Move to hit pixel */
1264 bombs[i].x += BOMB_WIDTH / 2;
1265 bombs[i].y += j;
1267 /* Check if ship is hit */
1268 if (bombs[i].y > SHIELD_Y + SHIELD_HEIGHT && bombs[i].y < PLAYFIELD_Y) {
1270 /* TODO: play ship hit sound */
1272 ship_hit = true;
1273 ship_frame = 1;
1274 ship_frame_counter = 0;
1275 bombs[i].state = S_EXPLODE * 4;
1276 bombs[i].target = TARGET_SHIP;
1277 rb->lcd_bitmap_part(invadrox, SHIP_SRC_X, 1 * SHIP_HEIGHT, STRIDE,
1278 ship_x, SHIP_Y, SHIP_WIDTH, SHIP_HEIGHT);
1279 break;
1281 /* Shield hit */
1282 bombs[i].state = S_EXPLODE;
1283 bombs[i].target = TARGET_SHIELD;
1284 /* Center explosion around hit pixel in shield */
1285 bombs[i].y -= FIRE_HEIGHT / 2;
1286 fire_alpha(bombs[i].x, bombs[i].y, SLIME_GREEN);
1287 break;
1291 if (bombs[i].target != 0) {
1292 /* Hit ship or shield, continue */
1293 continue;
1296 draw_bomb(i);
1297 break;
1299 default:
1300 /* If we get here state should be < 0, exploding */
1301 bombs[i].state++;
1302 if (bombs[i].state == S_IDLE) {
1303 if (ship_hit) {
1304 /* Erase explosion */
1305 rb->lcd_fillrect(ship_x, SHIP_Y, SHIP_WIDTH, SHIP_HEIGHT);
1306 rb->lcd_update_rect(ship_x, SHIP_Y, SHIP_WIDTH, SHIP_HEIGHT);
1307 ship_hit = false;
1308 ship_frame = 0;
1309 ship_x = PLAYFIELD_X + 2 * LIVES_X;
1310 lives--;
1311 if (lives == 0) {
1312 game_over = true;
1313 return;
1315 draw_lives();
1316 /* Sleep 1s to give player time to examine lives left */
1317 rb->sleep(HZ);
1319 /* Erase explosion (even if ship hit, might be another bomb) */
1320 fire_alpha(bombs[i].x, bombs[i].y, LCD_BLACK);
1322 break;
1328 inline void move_ship(void)
1330 ship_dir += ship_acc;
1331 if (ship_dir > max_ship_speed) {
1332 ship_dir = max_ship_speed;
1334 if (ship_dir < -max_ship_speed) {
1335 ship_dir = -max_ship_speed;
1337 ship_x += ship_dir;
1338 if (ship_x < SHIP_MIN_X) {
1339 ship_x = SHIP_MIN_X;
1341 if (ship_x > SHIP_MAX_X) {
1342 ship_x = SHIP_MAX_X;
1345 draw_ship();
1349 /* Unidentified Flying Object */
1350 void move_ufo(void)
1352 static int ufo_speed;
1353 static int counter;
1354 int mystery_score;
1356 switch (ufo_state) {
1358 case S_IDLE:
1360 if (rb->rand() % 500 == 0) {
1361 /* Uh-oh, it's time to launch a mystery UFO */
1363 /* TODO: Play UFO sound */
1365 if (rb->rand() % 2) {
1366 ufo_speed = UFO_SPEED;
1367 ufo_x = PLAYFIELD_X;
1368 } else {
1369 ufo_speed = -UFO_SPEED;
1370 ufo_x = LCD_WIDTH - PLAYFIELD_X - UFO_WIDTH;
1372 ufo_state = S_ACTIVE;
1373 /* UFO will be drawn next frame */
1375 break;
1377 case S_ACTIVE:
1378 /* Erase old pos */
1379 rb->lcd_fillrect(ufo_x, UFO_Y, UFO_WIDTH, UFO_HEIGHT);
1380 /* Move */
1381 ufo_x += ufo_speed;
1382 /* Check bounds */
1383 if (ufo_x < PLAYFIELD_X || ufo_x > LCD_WIDTH - PLAYFIELD_X - UFO_WIDTH) {
1384 ufo_state = S_IDLE;
1385 break;
1387 /* Draw new pos */
1388 rb->lcd_bitmap_part(invadrox, UFO_SRC_X, 0,
1389 STRIDE, ufo_x, UFO_Y, UFO_WIDTH, UFO_HEIGHT);
1390 break;
1392 case S_SHOWSCORE:
1393 counter++;
1394 if (counter == S_IDLE) {
1395 /* Erase mystery number */
1396 rb->lcd_fillrect(ufo_x, UFO_Y, 3 * NUMBERS_WIDTH + 2 * NUM_SPACING, FONT_HEIGHT);
1397 ufo_state = S_IDLE;
1399 break;
1401 default:
1402 /* Exploding */
1403 ufo_state++;
1404 if (ufo_state == S_IDLE) {
1405 /* Erase explosion */
1406 rb->lcd_fillrect(ufo_x, UFO_Y - 1, UFO_EXPLODE_WIDTH, UFO_EXPLODE_HEIGHT);
1407 ufo_state = S_SHOWSCORE;
1408 counter = S_EXPLODE * 4;
1409 /* Draw mystery_score, sleep, increase score and continue */
1410 mystery_score = 50 + (rb->rand() % 6) * 50;
1411 if (mystery_score < 100) {
1412 draw_number(ufo_x, UFO_Y, mystery_score, 2);
1413 } else {
1414 draw_number(ufo_x, UFO_Y, mystery_score, 3);
1416 score += mystery_score;
1417 draw_score();
1419 break;
1424 void draw_background(void)
1427 #if (LCD_WIDTH == 320) && (LCD_HEIGHT == 240)
1428 /* Erase background to black */
1429 rb->lcd_fillrect(PLAYFIELD_X, 0, PLAYFIELD_WIDTH, LCD_HEIGHT);
1430 /* Left and right bitmaps */
1431 rb->lcd_bitmap(invadrox_left, 0, 0, PLAYFIELD_X, LCD_HEIGHT);
1432 rb->lcd_bitmap(invadrox_right, LCD_WIDTH - PLAYFIELD_X, 0, PLAYFIELD_X, LCD_HEIGHT);
1433 #else
1434 rb->lcd_fillrect(0, 0, LCD_WIDTH, LCD_HEIGHT);
1435 #endif
1437 #if ((LCD_WIDTH == 240) && (LCD_HEIGHT == 320)) || ((LCD_WIDTH == 176) && (LCD_HEIGHT == 220))
1438 rb->lcd_bitmap(invadrox_logo, 0, 0, LCD_WIDTH, SCORE_Y);
1439 #endif
1441 rb->lcd_update();
1445 void new_level(void)
1447 int i;
1449 draw_background();
1450 /* Give an extra life for each new level */
1451 if (lives < MAX_LIVES) {
1452 lives++;
1454 draw_lives();
1456 /* Score */
1457 rb->lcd_bitmap_part(invadrox, SCORE_SRC_X, SCORE_SRC_Y,
1458 STRIDE, PLAYFIELD_X, SCORE_Y, SCORE_WIDTH, FONT_HEIGHT);
1459 /* Hi-score */
1460 rb->lcd_bitmap_part(invadrox, 0, SCORE_SRC_Y,
1461 STRIDE, HISCORE_X, SCORE_Y,
1462 HISCORE_WIDTH, FONT_HEIGHT);
1463 draw_score();
1464 draw_number(HISCORENUM_X, SCORENUM_Y, hiscore.score, 4);
1466 level++;
1467 draw_level();
1468 level_finished = false;
1470 ufo_state = S_IDLE;
1472 /* Init alien positions and states */
1473 for (i = 0; i < 4 * ALIENS; i++) {
1474 aliens[i].x = 0 + (i % ALIENS) * ((ALIEN_WIDTH + ALIEN_SPACING) / ALIEN_SPEED);
1475 aliens[i].y = 2 * (i / ALIENS);
1476 aliens[i].state = ALIVE;
1478 /* Last row, bombers */
1479 for (i = 4 * ALIENS; i < 5 * ALIENS; i++) {
1480 aliens[i].x = 0 + (i % ALIENS) * ((ALIEN_WIDTH + ALIEN_SPACING) / ALIEN_SPEED);
1481 aliens[i].y = 2 * (i / ALIENS);
1482 aliens[i].state = BOMBER;
1485 /* Init bombs to inactive (S_IDLE) */
1486 for (i = 0; i < MAX_BOMBS; i++) {
1487 bombs[i].state = S_IDLE;
1490 /* Start aliens closer to earth from level 2 */
1491 for (i = 0; i < 5 * ALIENS; i++) {
1492 if (level < 6) {
1493 aliens[i].y += level - 1;
1494 } else {
1495 aliens[i].y += 5;
1499 /* Max concurrent bombs */
1500 max_bombs = 1;
1502 gamespeed = 2;
1504 if (level > 1) {
1505 max_bombs++;
1508 /* Increase speed */
1509 if (level > 2) {
1510 gamespeed++;
1513 if (level > 3) {
1514 max_bombs++;
1517 /* Increase speed more */
1518 if (level > 4) {
1519 gamespeed++;
1522 if (level > 5) {
1523 max_bombs++;
1526 /* 4 shields */
1527 for (i = 1; i <= 4; i++) {
1528 rb->lcd_bitmap_part(invadrox, SHIELD_SRC_X, SHIELD_SRC_Y, STRIDE,
1529 PLAYFIELD_X + i * PLAYFIELD_WIDTH / 5 - SHIELD_WIDTH / 2,
1530 SHIELD_Y, SHIELD_WIDTH, SHIELD_HEIGHT);
1533 /* Bottom line */
1534 rb->lcd_set_foreground(SLIME_GREEN);
1535 rb->lcd_hline(PLAYFIELD_X, LCD_WIDTH - PLAYFIELD_X, PLAYFIELD_Y);
1536 /* Restore foreground to black (for fast erase later). */
1537 rb->lcd_set_foreground(LCD_BLACK);
1539 ship_x = PLAYFIELD_X + 2 * LIVES_X;
1540 if (level == 1) {
1541 old_ship_x = ship_x;
1543 ship_dir = 0;
1544 ship_acc = 0;
1545 ship_frame = 0;
1546 ship_hit = false;
1547 fire = S_IDLE;
1548 /* Start moving the bottom row left to right */
1549 curr_alien = 4 * ALIENS;
1550 aliens_paralyzed = 0;
1551 aliens_right = true;
1552 aliens_down = false;
1553 hit_left_border = false;
1554 hit_right_border = false;
1555 /* TODO: Change max_ship_speed to 3 at higher levels */
1556 max_ship_speed = 2;
1558 draw_aliens();
1560 rb->lcd_update();
1564 void init_invadrox(void)
1566 int i;
1568 /* Seed random number generator with a "random" number */
1569 rb->srand(rb->get_time()->tm_sec + rb->get_time()->tm_min * 60);
1571 /* Precalculate start of each scanline */
1572 for (i = 0; i < LCD_HEIGHT; i++) {
1573 #if (LCD_DEPTH >= 8)
1574 ytab[i] = i * LCD_WIDTH;
1575 #elif (LCD_DEPTH == 2) && (LCD_PIXELFORMAT == HORIZONTAL_PACKING)
1576 ytab[i] = i * (LCD_WIDTH / 4);
1577 #elif (LCD_DEPTH == 2) && (LCD_PIXELFORMAT == VERTICAL_PACKING)
1578 ytab[i] = (i / 4) * LCD_WIDTH;
1579 #else
1580 #error pixelformat not implemented yet
1581 #endif
1584 rb->lcd_set_background(LCD_BLACK);
1585 rb->lcd_set_foreground(LCD_BLACK);
1587 highscore_init(rb);
1588 if (highscore_load(HISCOREFILE, &hiscore, 1) < 0) {
1589 /* Init hiscore to 0 */
1590 rb->strncpy(hiscore.name, "Invader", sizeof(hiscore.name));
1591 hiscore.score = 0;
1592 hiscore.level = 1;
1595 /* Init alien types in aliens array */
1596 for (i = 0; i < 1 * ALIENS; i++) {
1597 aliens[i].type = 0; /* Kang */
1599 for (; i < 3 * ALIENS; i++) {
1600 aliens[i].type = 1; /* Kodos */
1602 for (; i < 5 * ALIENS; i++) {
1603 aliens[i].type = 2; /* Serak */
1607 /* Save screen white color */
1608 rb->lcd_set_foreground(LCD_WHITE);
1609 rb->lcd_drawpixel(0, 0);
1610 rb->lcd_update_rect(0, 0, 1, 1);
1611 screen_white = get_pixel(0, 0);
1613 /* Save screen green color */
1614 rb->lcd_set_foreground(SLIME_GREEN);
1615 rb->lcd_drawpixel(0, 0);
1616 rb->lcd_update_rect(0, 0, 1, 1);
1617 screen_green = get_pixel(0, 0);
1619 /* Restore black foreground */
1620 rb->lcd_set_foreground(LCD_BLACK);
1622 new_level();
1624 /* Flash score at start */
1625 for (i = 0; i < 5; i++) {
1626 rb->lcd_fillrect(SCORENUM_X, SCORENUM_Y,
1627 4 * NUMBERS_WIDTH + 3 * NUM_SPACING,
1628 FONT_HEIGHT);
1629 rb->lcd_update_rect(SCORENUM_X, SCORENUM_Y,
1630 4 * NUMBERS_WIDTH + 3 * NUM_SPACING,
1631 FONT_HEIGHT);
1632 rb->sleep(HZ / 10);
1633 draw_number(SCORENUM_X, SCORENUM_Y, score, 4);
1634 rb->sleep(HZ / 10);
1639 inline bool handle_buttons(void)
1641 static unsigned int oldbuttonstate = 0;
1643 unsigned int released, pressed, newbuttonstate;
1645 if (ship_hit) {
1646 /* Don't allow ship movement during explosion */
1647 newbuttonstate = 0;
1648 } else {
1649 newbuttonstate = rb->button_status();
1651 if(newbuttonstate == oldbuttonstate) {
1652 if (newbuttonstate == 0) {
1653 /* No button pressed. Stop ship. */
1654 ship_acc = 0;
1655 if (ship_dir > 0) {
1656 ship_dir--;
1658 if (ship_dir < 0) {
1659 ship_dir++;
1662 /* return false; */
1663 goto check_usb;
1665 released = ~newbuttonstate & oldbuttonstate;
1666 pressed = newbuttonstate & ~oldbuttonstate;
1667 oldbuttonstate = newbuttonstate;
1668 if (pressed) {
1669 if (pressed & LEFT) {
1670 if (ship_acc > -1) {
1671 ship_acc--;
1674 if (pressed & RIGHT) {
1675 if (ship_acc < 1) {
1676 ship_acc++;
1679 if (pressed & FIRE) {
1680 if (fire == S_IDLE) {
1681 /* Fire shot */
1682 fire_x = ship_x + SHIP_WIDTH / 2;
1683 fire_y = SHIP_Y - SHOT_HEIGHT;
1684 fire = S_ACTIVE;
1685 /* TODO: play fire sound */
1688 #ifdef RC_QUIT
1689 if (pressed & RC_QUIT) {
1690 rb->splash(HZ * 1, "Quit");
1691 return true;
1693 #endif
1694 if (pressed & QUIT) {
1695 rb->splash(HZ * 1, "Quit");
1696 return true;
1699 if (released) {
1700 if ((released & LEFT)) {
1701 if (ship_acc < 1) {
1702 ship_acc++;
1705 if ((released & RIGHT)) {
1706 if (ship_acc > -1) {
1707 ship_acc--;
1712 check_usb:
1714 /* Quit if USB is connected */
1715 if (rb->button_get(false) == SYS_USB_CONNECTED) {
1716 return true;
1719 return false;
1723 void game_loop(void)
1725 int i, end;
1727 /* Print dimensions (just for debugging) */
1728 DBG("%03dx%03dx%02d\n", LCD_WIDTH, LCD_HEIGHT, LCD_DEPTH);
1730 /* Init */
1731 init_invadrox();
1733 while (1) {
1734 /* Convert CYCLETIME (in ms) to HZ */
1735 end = *rb->current_tick + (CYCLETIME * HZ) / 1000;
1737 if (handle_buttons()) {
1738 return;
1741 /* Animate */
1742 move_ship();
1743 move_fire();
1745 /* Check if level is finished (marked by move_fire) */
1746 if (level_finished) {
1747 /* TODO: Play level finished sound */
1748 new_level();
1751 move_ufo();
1753 /* Move aliens */
1754 if (!aliens_paralyzed && !ship_hit) {
1755 for (i = 0; i < gamespeed; i++) {
1756 if (!move_aliens()) {
1757 if (game_over) {
1758 return;
1764 /* Move alien bombs */
1765 move_bombs();
1766 if (game_over) {
1767 return;
1770 /* Update "playfield" rect */
1771 rb->lcd_update_rect(PLAYFIELD_X, SCORENUM_Y + FONT_HEIGHT,
1772 PLAYFIELD_WIDTH,
1773 PLAYFIELD_Y + 1 - SCORENUM_Y - FONT_HEIGHT);
1775 /* Wait until next frame */
1776 DBG("%d (%d)\n", end - *rb->current_tick, (CYCLETIME * HZ) / 1000);
1777 if (end > *rb->current_tick) {
1778 rb->sleep(end - *rb->current_tick);
1779 } else {
1780 rb->yield();
1783 } /* end while */
1787 /* this is the plugin entry point */
1788 enum plugin_status plugin_start(const struct plugin_api* api, UNUSED const void* parameter)
1790 rb = api;
1792 rb->lcd_setfont(FONT_SYSFIXED);
1793 /* Turn off backlight timeout */
1794 backlight_force_on(rb); /* backlight control in lib/helper.c */
1796 /* now go ahead and have fun! */
1797 game_loop();
1799 /* Game Over. */
1800 /* TODO: Play game over sound */
1801 rb->splash(HZ * 2, "Game Over");
1802 if (score > hiscore.score) {
1803 /* Save new hiscore */
1804 hiscore.score = score;
1805 hiscore.level = level;
1806 highscore_save(HISCOREFILE, &hiscore, 1);
1809 /* Restore user's original backlight setting */
1810 rb->lcd_setfont(FONT_UI);
1811 /* Turn on backlight timeout (revert to settings) */
1812 backlight_use_settings(rb); /* backlight control in lib/helper.c */
1814 return PLUGIN_OK;
1820 * GNU Emacs settings: Kernighan & Richie coding style with
1821 * 4 spaces indent and no tabs.
1822 * Local Variables:
1823 * c-file-style: "k&r"
1824 * c-basic-offset: 4
1825 * indent-tabs-mode: nil
1826 * End: