Commit FS#9617 - Keymaps for Plugins fuze by Thomas Martitz.
[kugel-rb.git] / apps / plugins / invadrox.c
blob0d50c67e73bbcc88c4332bf33324444d764afe15
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2006 Albert Veli
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
22 /* Improvised creds goes to:
24 * - Anders Clausen for ingeniously inventing the name Invadrox.
25 * - Linus Nielsen-Feltzing for patiently answering n00b questions.
28 #include "plugin.h"
29 #include "lib/highscore.h"
30 #include "lib/helper.h"
32 PLUGIN_HEADER
34 /* Original graphics is only 1bpp so it should be portable
35 * to most targets. But for now, only support the simple ones.
37 #ifndef HAVE_LCD_BITMAP
38 #error INVADROX: Unsupported LCD
39 #endif
41 #if (LCD_DEPTH < 2)
42 #error INVADROX: Unsupported LCD
43 #endif
45 /* #define DEBUG */
46 #ifdef DEBUG
47 #define DBG(format, arg...) { DEBUGF("%s: " format, __FUNCTION__, ## arg); }
48 #else
49 #define DBG(format, arg...) {}
50 #endif
52 #if CONFIG_KEYPAD == IRIVER_H100_PAD
54 #define QUIT BUTTON_OFF
55 #define LEFT BUTTON_LEFT
56 #define RIGHT BUTTON_RIGHT
57 #define FIRE BUTTON_ON
59 #elif CONFIG_KEYPAD == IRIVER_H300_PAD
61 #define QUIT BUTTON_OFF
62 #define LEFT BUTTON_LEFT
63 #define RIGHT BUTTON_RIGHT
64 #define FIRE BUTTON_SELECT
66 #elif (CONFIG_KEYPAD == IRIVER_H10_PAD)
68 #define QUIT BUTTON_POWER
69 #define LEFT BUTTON_LEFT
70 #define RIGHT BUTTON_RIGHT
71 #define FIRE BUTTON_PLAY
73 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
74 (CONFIG_KEYPAD == IPOD_3G_PAD) || \
75 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
77 #define QUIT BUTTON_MENU
78 #define LEFT BUTTON_LEFT
79 #define RIGHT BUTTON_RIGHT
80 #define FIRE BUTTON_SELECT
82 #elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD
84 #define QUIT BUTTON_POWER
85 #define LEFT BUTTON_LEFT
86 #define RIGHT BUTTON_RIGHT
87 #define FIRE BUTTON_SELECT
89 #elif CONFIG_KEYPAD == GIGABEAT_PAD
91 #define QUIT BUTTON_POWER
92 #define LEFT BUTTON_LEFT
93 #define RIGHT BUTTON_RIGHT
94 #define FIRE BUTTON_SELECT
96 #elif CONFIG_KEYPAD == SANSA_E200_PAD || \
97 CONFIG_KEYPAD == SANSA_FUZE_PAD
99 #define QUIT BUTTON_POWER
100 #define LEFT BUTTON_LEFT
101 #define RIGHT BUTTON_RIGHT
102 #define FIRE BUTTON_SELECT
104 #elif CONFIG_KEYPAD == ELIO_TPJ1022_PAD
106 /* TODO: Figure out which buttons to use for Tatung Elio TPJ-1022 */
107 #define QUIT BUTTON_AB
108 #define LEFT BUTTON_LEFT
109 #define RIGHT BUTTON_RIGHT
110 #define FIRE BUTTON_MENU
112 #elif CONFIG_KEYPAD == GIGABEAT_S_PAD
114 #define QUIT BUTTON_BACK
115 #define LEFT BUTTON_LEFT
116 #define RIGHT BUTTON_RIGHT
117 #define FIRE BUTTON_SELECT
119 #elif CONFIG_KEYPAD == COWOND2_PAD
121 #define QUIT BUTTON_POWER
123 #elif CONFIG_KEYPAD == IAUDIO67_PAD
125 #define QUIT BUTTON_POWER
126 #define LEFT BUTTON_LEFT
127 #define RIGHT BUTTON_RIGHT
128 #define FIRE BUTTON_PLAY
130 #elif CONFIG_KEYPAD == CREATIVEZVM_PAD
132 #define QUIT BUTTON_BACK
133 #define LEFT BUTTON_LEFT
134 #define RIGHT BUTTON_RIGHT
135 #define FIRE BUTTON_SELECT
137 #else
138 #error INVADROX: Unsupported keypad
139 #endif
141 #ifdef HAVE_TOUCHSCREEN
142 #ifndef QUIT
143 #define QUIT BUTTON_TOPLEFT
144 #endif
145 #ifndef LEFT
146 #define LEFT BUTTON_MIDLEFT
147 #endif
148 #ifndef RIGHT
149 #define RIGHT BUTTON_MIDRIGHT
150 #endif
151 #ifndef FIRE
152 #define FIRE BUTTON_CENTER
153 #endif
154 #endif
156 #ifndef UNUSED
157 #define UNUSED __attribute__ ((unused))
158 #endif
160 #ifndef ABS
161 #define ABS(x) (((x) < 0) ? (-(x)) : (x))
162 #endif
165 /* Defines common to all models */
166 #define UFO_Y (SCORENUM_Y + FONT_HEIGHT + ALIEN_HEIGHT)
167 #define PLAYFIELD_Y (LCD_HEIGHT - SHIP_HEIGHT - 2)
168 #define PLAYFIELD_WIDTH (LCD_WIDTH - 2 * PLAYFIELD_X)
169 #define LEVEL_X (LCD_WIDTH - PLAYFIELD_X - LIVES_X - LEVEL_WIDTH - 2 * NUMBERS_WIDTH - 3 * NUM_SPACING)
170 #define SHIP_MIN_X (PLAYFIELD_X + PLAYFIELD_WIDTH / 5 - SHIELD_WIDTH / 2 - SHIP_WIDTH)
171 #define SHIP_MAX_X (PLAYFIELD_X + 4 * PLAYFIELD_WIDTH / 5 + SHIELD_WIDTH / 2)
172 /* SCORE_Y = 0 for most targets. Gigabeat redefines it later. */
173 #define SCORE_Y 0
174 #define MAX_LIVES 8
177 /* iPod Video defines */
178 #if (LCD_WIDTH == 320) && (LCD_HEIGHT == 240)
180 /* Original arcade game size 224x240, 1bpp with
181 * red overlay at top and green overlay at bottom.
183 * iPod Video: 320x240x16
184 * ======================
185 * X: 48p padding at left/right gives 224p playfield in middle.
186 * 10p "border" gives 204p actual playfield. UFO use full 224p.
187 * Y: Use full 240p.
189 * MAX_X = (204 - 12) / 2 - 1 = 95
191 * Y: Score text 7 0
192 * Space 10 7
193 * Score 7 17
194 * Space 8 24
195 * 3 Ufo 7 32
196 * 2 Space Aliens start at 32 + 3 * 8 = 56
197 * 0 aliens 9*8 56 -
198 * space ~7*8 128 | 18.75 aliens space between
199 * shield 2*8 182 | first alien and ship.
200 * space 8 198 | MAX_Y = 18
201 * ship 8 206 -
202 * space 2*8 214
203 * hline 1 230 - PLAYFIELD_Y
204 * bottom border 10 240
205 * Lives and Level goes inside bottom border
208 #define ARCADISH_GRAPHICS
209 #define PLAYFIELD_X 48
210 #define SHIP_Y (PLAYFIELD_Y - 3 * SHIP_HEIGHT)
211 #define ALIEN_START_Y (UFO_Y + 3 * ALIEN_HEIGHT)
212 #define SCORENUM_X (PLAYFIELD_X + NUMBERS_WIDTH)
213 #define SCORENUM_Y SCORE_Y + (2 * (FONT_HEIGHT + 1) + 1)
214 #define HISCORE_X (LCD_WIDTH - PLAYFIELD_X - HISCORE_WIDTH)
215 #define HISCORENUM_X (LCD_WIDTH - PLAYFIELD_X - 1 - 6 * NUMBERS_WIDTH - 5 * NUM_SPACING)
216 #define SHIELD_Y (PLAYFIELD_Y - 6 * SHIP_HEIGHT)
217 #define LIVES_X 10
218 #define MAX_X 95
219 #define MAX_Y 18
222 #elif (LCD_WIDTH == 176) && (LCD_HEIGHT == 220)
224 /* Sandisk Sansa e200: 176x220x16
225 * ==============================
226 * X: No padding. 8p border -> 160p playfield.
228 * 8p Aliens with 3p spacing -> 88 + 30 = 118p aliens block.
229 * (160 - 118) / 2 = 21 rounds for whole block (more than original)
230 * MAX_X = (160 - 8) / 2 - 1 = 75 rounds for single alien (less than original)
232 * LOGO 70 0
233 * Score text 5 70
234 * Space 5 75
235 * Y Score 5 80
236 * Space 10 85
237 * 2 Ufo 5 95
238 * 2 Space 10 100
239 * 0 aliens 9*5 110 -
240 * space ~7*5 155 | 18.6 aliens space between
241 * shield 2*5 188 | first alien and ship.
242 * space 5 198 | MAX_Y = 18
243 * ship 5 203 -
244 * space 5 208
245 * hline 1 213 PLAYFIELD_Y
246 * bottom border 6
247 * LCD_HEIGHT 220
248 * Lives and Level goes inside bottom border
251 #define TINY_GRAPHICS
252 #define PLAYFIELD_X 0
253 #define SHIP_Y (PLAYFIELD_Y - 2 * SHIP_HEIGHT)
254 #define SHIELD_Y (SHIP_Y - SHIP_HEIGHT - SHIELD_HEIGHT)
255 #define ALIEN_START_Y (UFO_Y + 3 * SHIP_HEIGHT)
256 /* Redefine SCORE_Y */
257 #undef SCORE_Y
258 #define SCORE_Y 70
259 #define SCORENUM_X (PLAYFIELD_X + NUMBERS_WIDTH)
260 #define SCORENUM_Y (SCORE_Y + 2 * FONT_HEIGHT)
261 #define HISCORE_X (LCD_WIDTH - PLAYFIELD_X - HISCORE_WIDTH)
262 #define HISCORENUM_X (LCD_WIDTH - PLAYFIELD_X - 1 - 6 * NUMBERS_WIDTH - 5 * NUM_SPACING)
263 #define LIVES_X 8
264 #define MAX_X 75
265 #define MAX_Y 18
268 #elif (LCD_WIDTH == 176) && (LCD_HEIGHT == 132)
270 /* iPod Nano: 176x132x16
271 * ======================
272 * X: No padding. 8p border -> 160p playfield.
274 * LIVES_X 8
275 * ALIEN_WIDTH 8
276 * ALIEN_HEIGHT 5
277 * ALIEN_SPACING 3
278 * SHIP_WIDTH 10
279 * SHIP_HEIGHT 5
280 * FONT_HEIGHT 5
281 * UFO_WIDTH 10
282 * UFO_HEIGHT 5
283 * SHIELD_WIDTH 15
284 * SHIELD_HEIGHT 10
285 * MAX_X 75
286 * MAX_Y = 18
287 * ALIEN_START_Y (UFO_Y + 12)
289 * 8p Aliens with 3p spacing -> 88 + 30 = 118p aliens block.
290 * (160 - 118) / 2 = 21 rounds for whole block (more than original)
291 * MAX_X = (160 - 8) / 2 - 1 = 75 rounds for single alien (less than original)
293 * Y: Scoreline 5 0 (combine scoretext and numbers on same line)
294 * Space 5 5
295 * 1 Ufo 5 10
296 * 3 Space 7 15
297 * 2 aliens 9*5 22 -
298 * space ~7*5 67 | Just above 18 aliens space between
299 * shield 2*5 100 | first alien and ship.
300 * space 5 110 | MAX_Y = 18
301 * ship 5 115 -
302 * space 5 120
303 * hline 1 125 PLAYFIELD_Y
304 * bottom border 6 126
305 * LCD_HEIGHT 131
306 * Lives and Level goes inside bottom border
309 #define TINY_GRAPHICS
310 #define PLAYFIELD_X 0
311 #define SHIP_Y (PLAYFIELD_Y - 2 * SHIP_HEIGHT)
312 #define ALIEN_START_Y (UFO_Y + 12)
313 #define SCORENUM_X (PLAYFIELD_X + SCORE_WIDTH + NUMBERS_WIDTH + NUM_SPACING)
314 #define SCORENUM_Y SCORE_Y
315 #define HISCORENUM_X (LCD_WIDTH - PLAYFIELD_X - 4 * NUMBERS_WIDTH - 3 * NUM_SPACING)
316 #define HISCORE_X (HISCORENUM_X - NUMBERS_WIDTH - NUM_SPACING - HISCORE_WIDTH)
317 #define SHIELD_Y (SHIP_Y - SHIP_HEIGHT - SHIELD_HEIGHT)
318 #define LIVES_X 8
319 #define MAX_X 75
320 #define MAX_Y 18
322 #elif (LCD_WIDTH == 160) && (LCD_HEIGHT == 128)
324 /* iAudio X5, iRiver H10 20Gb, iPod 3g/4g: 160x128x16
325 * ======================================
326 * X: No padding. No border -> 160p playfield.
328 * LIVES_X 0
329 * ALIEN_WIDTH 8
330 * ALIEN_HEIGHT 5
331 * ALIEN_SPACING 3
332 * SHIP_WIDTH 10
333 * SHIP_HEIGHT 5
334 * FONT_HEIGHT 5
335 * UFO_WIDTH 10
336 * UFO_HEIGHT 5
337 * SHIELD_WIDTH 15
338 * SHIELD_HEIGHT 10
339 * MAX_X 75
340 * MAX_Y = 18
341 * ALIEN_START_Y (UFO_Y + 10)
343 * 8p Aliens with 3p spacing -> 88 + 30 = 118p aliens block.
344 * (160 - 118) / 2 = 21 rounds for whole block (more than original)
345 * MAX_X = (160 - 8) / 2 - 1 = 75 rounds for single alien (less than original)
347 * Y: Scoreline 5 0 (combine scoretext and numbers on same line)
348 * Space 5 5
349 * 1 Ufo 5 10
350 * 2 Space 5 15
351 * 8 aliens 9*5 20 -
352 * space ~6*5 65 | Just above 18 aliens space between
353 * shield 2*5 96 | first alien and ship.
354 * space 5 106 | MAX_Y = 18
355 * ship 5 111 -
356 * space 5 116
357 * hline 1 121 PLAYFIELD_Y
358 * bottom border 6 122
359 * LCD_HEIGHT 128
360 * Lives and Level goes inside bottom border
363 #define TINY_GRAPHICS
364 #define PLAYFIELD_X 0
365 #define SHIP_Y (PLAYFIELD_Y - 2 * SHIP_HEIGHT)
366 #define ALIEN_START_Y (UFO_Y + 10)
367 #define SCORENUM_X (PLAYFIELD_X + SCORE_WIDTH + NUMBERS_WIDTH + NUM_SPACING)
368 #define SCORENUM_Y SCORE_Y
369 #define HISCORENUM_X (LCD_WIDTH - PLAYFIELD_X - 4 * NUMBERS_WIDTH - 3 * NUM_SPACING)
370 #define HISCORE_X (HISCORENUM_X - NUMBERS_WIDTH - NUM_SPACING - HISCORE_WIDTH)
371 #define SHIELD_Y (SHIP_Y - SHIP_HEIGHT - SHIELD_HEIGHT)
372 #define LIVES_X 0
373 #define MAX_X 75
374 #define MAX_Y 18
377 #elif (LCD_WIDTH == 240) && (LCD_HEIGHT == 320)
379 /* Gigabeat: 240x320x16
380 * ======================
381 * X: 8p padding at left/right gives 224p playfield in middle.
382 * 10p "border" gives 204p actual playfield. UFO use full 224p.
383 * Y: Use bottom 240p for playfield and top 80 pixels for logo.
385 * MAX_X = (204 - 12) / 2 - 1 = 95
387 * Y: Score text 7 0 + 80
388 * Space 10 7 + 80
389 * Score 7 17 + 80
390 * Space 8 24 + 80
391 * 3 Ufo 7 32 + 80
392 * 2 Space Aliens start at 32 + 3 * 8 = 56
393 * 0 aliens 9*8 56 -
394 * space ~7*8 128 | 18.75 aliens space between
395 * shield 2*8 182 | first alien and ship.
396 * space 8 198 | MAX_Y = 18
397 * ship 8 206 -
398 * space 2*8 214
399 * hline 1 230 310 - PLAYFIELD_Y
400 * bottom border 10 240 320
401 * Lives and Level goes inside bottom border
404 #define ARCADISH_GRAPHICS
405 #define PLAYFIELD_X 8
406 #define SHIP_Y (PLAYFIELD_Y - 3 * SHIP_HEIGHT)
407 #define ALIEN_START_Y (UFO_Y + 3 * ALIEN_HEIGHT)
408 /* Redefine SCORE_Y */
409 #undef SCORE_Y
410 #define SCORE_Y 80
411 #define SCORENUM_X (PLAYFIELD_X + NUMBERS_WIDTH)
412 #define SCORENUM_Y SCORE_Y + (2 * (FONT_HEIGHT + 1) + 1)
413 #define HISCORE_X (LCD_WIDTH - PLAYFIELD_X - HISCORE_WIDTH)
414 #define HISCORENUM_X (LCD_WIDTH - PLAYFIELD_X - 1 - 6 * NUMBERS_WIDTH - 5 * NUM_SPACING)
415 #define SHIELD_Y (PLAYFIELD_Y - 6 * SHIP_HEIGHT)
416 #define LIVES_X 10
417 #define MAX_X 95
418 #define MAX_Y 18
420 #elif (LCD_WIDTH == 220) && (LCD_HEIGHT == 176)
422 /* TPJ1022, H300, iPod Color: 220x176x16
423 * ============================
424 * X: 0p padding at left/right gives 220p playfield in middle.
425 * 8p "border" gives 204p actual playfield. UFO use full 220p.
426 * Y: Use full 176p for playfield.
428 * MAX_X = (204 - 12) / 2 - 1 = 95
430 * Y: Score text 7 0
431 * Space 8 7
432 * 1 Ufo 7 15
433 * 7 Space Aliens start at 15 + 3 * 8 = 56
434 * 6 aliens 9*8 25 -
435 * space ~7*8 103 | 15.6 aliens space between
436 * shield 2*8 126 | first alien and ship.
437 * space 8 142 | MAX_Y = 15
438 * ship 8 150 -
439 * space 8 158
440 * hline 1 166 - PLAYFIELD_Y
441 * bottom border 10 176
442 * Lives and Level goes inside bottom border
445 #define ARCADISH_GRAPHICS
446 #define PLAYFIELD_X 0
447 #define SHIP_Y (PLAYFIELD_Y - 2 * SHIP_HEIGHT)
448 #define ALIEN_START_Y (UFO_Y + 10)
449 #define SCORENUM_Y SCORE_Y
450 #define SCORENUM_X (PLAYFIELD_X + SCORE_WIDTH + NUMBERS_WIDTH + NUM_SPACING)
451 #define HISCORENUM_X (LCD_WIDTH - PLAYFIELD_X - 4 * NUMBERS_WIDTH - 3 * NUM_SPACING)
452 #define HISCORE_X (HISCORENUM_X - NUMBERS_WIDTH - NUM_SPACING - HISCORE_WIDTH)
453 #define SHIELD_Y (PLAYFIELD_Y - 5 * SHIP_HEIGHT)
454 #define LIVES_X 8
455 #define MAX_X 95
456 #define MAX_Y 15
458 #else
459 #error INVADROX: Unsupported LCD type
460 #endif
463 /* Defines common to each "graphic type" */
464 #ifdef ARCADISH_GRAPHICS
466 #define STRIDE 71
467 #define SHIP_SRC_X 24
468 #define SHIP_WIDTH 16
469 #define SHIP_HEIGHT 8
470 #define SHOT_HEIGHT 5
471 #define ALIEN_WIDTH 12
472 #define ALIEN_EXPLODE_SRC_X 52
473 #define ALIEN_EXPLODE_SRC_Y 39
474 #define ALIEN_EXPLODE_WIDTH 13
475 #define ALIEN_EXPLODE_HEIGHT 7
476 #define ALIEN_HEIGHT 8
477 #define ALIEN_SPACING 4
478 #define ALIEN_SPEED 2
479 #define UFO_SRC_X 40
480 #define UFO_WIDTH 16
481 #define UFO_HEIGHT 7
482 #define UFO_EXPLODE_WIDTH 21
483 #define UFO_EXPLODE_HEIGHT 8
484 #define UFO_SPEED 1
485 #define FONT_HEIGHT 7
486 #define LEVEL_SRC_Y 24
487 #define LEVEL_WIDTH 37
488 #define SCORE_SRC_X 24
489 #define SCORE_SRC_Y 31
490 #define SCORE_WIDTH 37
491 #define HISCORE_WIDTH 61
492 #define NUM_SPACING 3
493 #define NUMBERS_SRC_Y 38
494 #define NUMBERS_WIDTH 5
495 #define SHIELD_SRC_X 40
496 #define SHIELD_SRC_Y 15
497 #define SHIELD_WIDTH 22
498 #define SHIELD_HEIGHT 16
499 #define FIRE_WIDTH 8
500 #define FIRE_HEIGHT 8
501 #define FIRE_SPEED 8
502 #define BOMB_SRC_X 62
503 #define BOMB_WIDTH 3
504 #define BOMB_HEIGHT 7
505 #define BOMB_SPEED 3
506 #define ALIENS 11
507 unsigned char fire_sprite[FIRE_HEIGHT] = {
508 (1 << 7) | (0 << 6) | (0 << 5) | (0 << 4) | (1 << 3) | (0 << 2) | (0 << 1) | 1,
509 (0 << 7) | (0 << 6) | (1 << 5) | (0 << 4) | (0 << 3) | (0 << 2) | (1 << 1) | 0,
510 (0 << 7) | (1 << 6) | (1 << 5) | (1 << 4) | (1 << 3) | (1 << 2) | (1 << 1) | 0,
511 (1 << 7) | (1 << 6) | (1 << 5) | (1 << 4) | (1 << 3) | (1 << 2) | (1 << 1) | 1,
512 (0 << 7) | (1 << 6) | (1 << 5) | (1 << 4) | (1 << 3) | (1 << 2) | (1 << 1) | 1,
513 (0 << 7) | (1 << 6) | (1 << 5) | (1 << 4) | (1 << 3) | (1 << 2) | (1 << 1) | 0,
514 (0 << 7) | (0 << 6) | (1 << 5) | (0 << 4) | (0 << 3) | (1 << 2) | (0 << 1) | 0,
515 (1 << 7) | (0 << 6) | (0 << 5) | (1 << 4) | (0 << 3) | (0 << 2) | (0 << 1) | 1
518 #elif defined TINY_GRAPHICS
520 #define STRIDE 53
521 #define SHIP_SRC_X 16
522 #define SHIP_WIDTH 10
523 #define SHIP_HEIGHT 5
524 #define SHOT_HEIGHT 4
525 #define ALIEN_WIDTH 8
526 #define ALIEN_HEIGHT 5
527 #define ALIEN_EXPLODE_SRC_X 40
528 #define ALIEN_EXPLODE_SRC_Y 26
529 #define ALIEN_EXPLODE_WIDTH 10
530 #define ALIEN_EXPLODE_HEIGHT 5
531 #define ALIEN_SPACING 3
532 #define ALIEN_SPEED 2
533 #define UFO_SRC_X 26
534 #define UFO_WIDTH 11
535 #define UFO_HEIGHT 5
536 #define UFO_EXPLODE_WIDTH 14
537 #define UFO_EXPLODE_HEIGHT 5
538 #define UFO_SPEED 1
539 #define FONT_HEIGHT 5
540 #define LEVEL_SRC_Y 15
541 #define LEVEL_WIDTH 29
542 #define NUMBERS_WIDTH 4
543 #define NUM_SPACING 2
544 #define SCORE_SRC_X 17
545 #define SCORE_SRC_Y 20
546 #define SCORE_WIDTH 28
547 #define HISCORE_WIDTH 45
548 #define NUMBERS_SRC_Y 25
549 #define SHIELD_SRC_X 29
550 #define SHIELD_SRC_Y 10
551 #define SHIELD_WIDTH 15
552 #define SHIELD_HEIGHT 10
553 #define FIRE_WIDTH 6
554 #define FIRE_HEIGHT 6
555 #define FIRE_SPEED 6
556 #define BOMB_SRC_X 44
557 #define BOMB_WIDTH 3
558 #define BOMB_HEIGHT 5
559 #define BOMB_SPEED 2
560 #define ALIENS 11
561 unsigned char fire_sprite[FIRE_HEIGHT] = {
562 (1 << 5) | (0 << 4) | (0 << 3) | (1 << 2) | (0 << 1) | 1,
563 (0 << 5) | (1 << 4) | (0 << 3) | (0 << 2) | (0 << 1) | 0,
564 (0 << 5) | (1 << 4) | (1 << 3) | (1 << 2) | (1 << 1) | 0,
565 (0 << 5) | (1 << 4) | (1 << 3) | (1 << 2) | (1 << 1) | 1,
566 (0 << 5) | (1 << 4) | (0 << 3) | (0 << 2) | (1 << 1) | 0,
567 (1 << 5) | (0 << 4) | (1 << 3) | (0 << 2) | (0 << 1) | 1
570 #else
571 #error Graphic type not defined
572 #endif
575 /* Colors */
576 #if (LCD_DEPTH >= 8)
577 #define SLIME_GREEN LCD_RGBPACK(31, 254, 31)
578 #define UFO_RED LCD_RGBPACK(254, 31, 31)
579 #elif (LCD_DEPTH == 2)
580 #define SLIME_GREEN LCD_LIGHTGRAY
581 #define UFO_RED LCD_LIGHTGRAY
582 #else
583 #error LCD type not implemented yet
584 #endif
586 /* Alien states */
587 #define DEAD 0
588 #define ALIVE 1
589 #define BOMBER 2
591 /* Fire/bomb/ufo states */
592 #define S_IDLE 0
593 #define S_ACTIVE 1
594 #define S_SHOWSCORE 2
595 #define S_EXPLODE -9
597 /* Fire/bomb targets */
598 #define TARGET_TOP 0
599 #define TARGET_SHIELD 1
600 #define TARGET_SHIP 2
601 #define TARGET_BOTTOM 3
602 #define TARGET_UFO 4
604 #define HISCOREFILE PLUGIN_GAMES_DIR "/invadrox.high"
607 /* The time (in ms) for one iteration through the game loop - decrease this
608 * to speed up the game - note that current_tick is (currently) only accurate
609 * to 10ms.
611 #define CYCLETIME 40
614 static const struct plugin_api* rb;
616 /* Physical x is at PLAYFIELD_X + LIVES_X + x * ALIEN_SPEED
617 * Physical y is at y * ALIEN_HEIGHT
619 struct alien {
620 unsigned char x; /* x-coordinate (0 - 95) */
621 unsigned char y; /* y-coordinate (0 - 18) */
622 unsigned char type; /* 0 (Kang), 1 (Kodos), 2 (Serak) */
623 unsigned char state; /* Dead, alive or bomber */
626 /* Aliens box 5 rows * ALIENS aliens in each row */
627 struct alien aliens[5 * ALIENS];
629 #define MAX_BOMBS 4
630 struct bomb {
631 int x, y;
632 unsigned char type;
633 unsigned char frame; /* Current animation frame */
634 unsigned char frames; /* Number of frames in animation */
635 unsigned char target; /* Remember target during explosion frames */
636 int state; /* 0 (IDLE) = inactive, 1 (FIRE) or negative, exploding */
638 struct bomb bombs[MAX_BOMBS];
639 /* Increase max_bombs at higher levels */
640 int max_bombs;
642 /* Raw framebuffer value of shield/ship green color */
643 fb_data screen_green, screen_white;
645 /* For optimization, precalculate startoffset of each scanline */
646 unsigned int ytab[LCD_HEIGHT];
648 /* external bitmaps */
649 extern const fb_data invadrox[];
650 #if (LCD_WIDTH == 320) && (LCD_HEIGHT == 240)
651 /* iPod Video only */
652 extern const fb_data invadrox_left[];
653 extern const fb_data invadrox_right[];
654 #endif
655 #if ((LCD_WIDTH == 240) && (LCD_HEIGHT == 320)) || ((LCD_WIDTH == 176) && (LCD_HEIGHT == 220))
656 /* Gigabeat F, Sansa e200 */
657 extern const fb_data invadrox_logo[];
658 #endif
661 int lives = 2;
662 int score = 0;
663 int scores[3] = { 30, 20, 10 };
664 int level = 0;
665 struct highscore hiscore;
666 bool game_over = false;
667 int ship_x, old_ship_x, ship_dir, ship_acc, max_ship_speed;
668 int ship_frame, ship_frame_counter;
669 bool ship_hit;
670 int fire, fire_target, fire_x, fire_y;
671 int curr_alien, aliens_paralyzed, gamespeed;
672 int ufo_state, ufo_x;
673 bool level_finished;
674 bool aliens_down, aliens_right, hit_left_border, hit_right_border;
677 /* No standard get_pixel function yet, use this hack instead */
678 #if (LCD_DEPTH >= 8)
680 inline fb_data get_pixel(int x, int y)
682 return rb->lcd_framebuffer[ytab[y] + x];
685 #elif (LCD_DEPTH == 2)
687 #if (LCD_PIXELFORMAT == HORIZONTAL_PACKING)
688 static const unsigned char shifts[4] = {
689 6, 4, 2, 0
691 /* Horizontal packing */
692 inline fb_data get_pixel(int x, int y)
694 return (rb->lcd_framebuffer[ytab[y] + (x >> 2)] >> shifts[x & 3]) & 3;
696 #else
697 /* Vertical packing */
698 static const unsigned char shifts[4] = {
699 0, 2, 4, 6
701 inline fb_data get_pixel(int x, int y)
703 return (rb->lcd_framebuffer[ytab[y] + x] >> shifts[y & 3]) & 3;
705 #endif /* Horizontal/Vertical packing */
707 #else
708 #error get_pixel: pixelformat not implemented yet
709 #endif
712 /* Draw "digits" least significant digits of num at (x,y) */
713 void draw_number(int x, int y, int num, int digits)
715 int i;
716 int d;
718 for (i = digits - 1; i >= 0; i--) {
719 d = num % 10;
720 num = num / 10;
721 rb->lcd_bitmap_part(invadrox, d * NUMBERS_WIDTH, NUMBERS_SRC_Y,
722 STRIDE, x + i * (NUMBERS_WIDTH + NUM_SPACING), y,
723 NUMBERS_WIDTH, FONT_HEIGHT);
725 /* Update lcd */
726 rb->lcd_update_rect(x, y, 4 * NUMBERS_WIDTH + 3 * NUM_SPACING, FONT_HEIGHT);
730 inline void draw_score(void)
732 draw_number(SCORENUM_X, SCORENUM_Y, score, 4);
733 if (score > hiscore.score) {
734 /* Draw new hiscore (same as score) */
735 draw_number(HISCORENUM_X, SCORENUM_Y, score, 4);
740 void draw_level(void)
742 rb->lcd_bitmap_part(invadrox, 0, LEVEL_SRC_Y,
743 STRIDE, LEVEL_X, PLAYFIELD_Y + 2,
744 LEVEL_WIDTH, FONT_HEIGHT);
745 draw_number(LEVEL_X + LEVEL_WIDTH + 2 * NUM_SPACING, PLAYFIELD_Y + 2, level, 2);
749 void draw_lives(void)
751 int i;
752 /* Lives num */
753 rb->lcd_bitmap_part(invadrox, lives * NUMBERS_WIDTH, NUMBERS_SRC_Y,
754 STRIDE, PLAYFIELD_X + LIVES_X, PLAYFIELD_Y + 2,
755 NUMBERS_WIDTH, FONT_HEIGHT);
757 /* Ships */
758 for (i = 0; i < (lives - 1); i++) {
759 rb->lcd_bitmap_part(invadrox, SHIP_SRC_X, 0, STRIDE,
760 PLAYFIELD_X + LIVES_X + SHIP_WIDTH + i * (SHIP_WIDTH + NUM_SPACING),
761 PLAYFIELD_Y + 1, SHIP_WIDTH, SHIP_HEIGHT);
764 /* Erase ship to the righ (if less than MAX_LIVES) */
765 if (lives < MAX_LIVES) {
766 rb->lcd_fillrect(PLAYFIELD_X + LIVES_X + SHIP_WIDTH + i * (SHIP_WIDTH + NUM_SPACING),
767 PLAYFIELD_Y + 1, SHIP_WIDTH, SHIP_HEIGHT);
769 /* Update lives (and level) part of screen */
770 rb->lcd_update_rect(PLAYFIELD_X + LIVES_X, PLAYFIELD_Y + 1,
771 PLAYFIELD_WIDTH - 2 * LIVES_X, MAX(FONT_HEIGHT + 1, SHIP_HEIGHT + 1));
775 inline void draw_aliens(void)
777 int i;
779 for (i = 0; i < 5 * ALIENS; i++) {
780 rb->lcd_bitmap_part(invadrox, aliens[i].x & 1 ? ALIEN_WIDTH : 0, aliens[i].type * ALIEN_HEIGHT,
781 STRIDE, PLAYFIELD_X + LIVES_X + aliens[i].x * ALIEN_SPEED,
782 ALIEN_START_Y + aliens[i].y * ALIEN_HEIGHT,
783 ALIEN_WIDTH, ALIEN_HEIGHT);
788 /* Return false if there is no next alive alien (round is over) */
789 inline bool next_alien(void)
791 bool ret = true;
793 do {
794 curr_alien++;
795 if (curr_alien % ALIENS == 0) {
796 /* End of this row. Move up one row. */
797 curr_alien -= 2 * ALIENS;
798 if (curr_alien < 0) {
799 /* No more aliens in this round. */
800 curr_alien = 4 * ALIENS;
801 ret = false;
804 } while (aliens[curr_alien].state == DEAD && ret);
806 if (!ret) {
807 /* No more alive aliens. Round finished. */
808 if (hit_right_border) {
809 if (hit_left_border) {
810 DBG("ERROR: both left and right borders are set (%d)\n", curr_alien);
812 /* Move down-left next round */
813 aliens_right = false;
814 aliens_down = true;
815 hit_right_border = false;
816 } else if (hit_left_border) {
817 /* Move down-right next round */
818 aliens_right = true;
819 aliens_down = true;
820 hit_left_border = false;
821 } else {
822 /* Not left nor right. Set down to false. */
823 aliens_down = false;
827 return ret;
831 /* All aliens have been moved.
832 * Set curr_alien to first alive.
833 * Return false if no-one is left alive.
835 bool first_alien(void)
837 int i, y;
839 for (y = 4; y >= 0; y--) {
840 for (i = y * ALIENS; i < (y + 1) * ALIENS; i++) {
841 if (aliens[i].state != DEAD) {
842 curr_alien = i;
843 return true;
848 /* All aliens dead. */
849 level_finished = true;
851 return false;
855 bool move_aliens(void)
857 int x, y, old_x, old_y;
859 /* Move current alien (curr_alien is pointing to a living alien) */
861 old_x = aliens[curr_alien].x;
862 old_y = aliens[curr_alien].y;
864 if (aliens_down) {
865 aliens[curr_alien].y++;
866 if (aliens[curr_alien].y == MAX_Y) {
867 /* Alien is at bottom. Game Over. */
868 DBG("Alien %d is at bottom. Game Over.\n", curr_alien);
869 game_over = true;
870 return false;
874 if (aliens_right) {
875 /* Moving right */
876 if (aliens[curr_alien].x < MAX_X) {
877 aliens[curr_alien].x++;
880 /* Now, after move, check if we hit the right border. */
881 if (aliens[curr_alien].x == MAX_X) {
882 hit_right_border = true;
885 } else {
886 /* Moving left */
887 if (aliens[curr_alien].x > 0) {
888 aliens[curr_alien].x--;
891 /* Now, after move, check if we hit the left border. */
892 if (aliens[curr_alien].x == 0) {
893 hit_left_border = true;
897 /* Erase old position */
898 x = PLAYFIELD_X + LIVES_X + old_x * ALIEN_SPEED;
899 y = ALIEN_START_Y + old_y * ALIEN_HEIGHT;
900 if (aliens[curr_alien].y != old_y) {
901 /* Moved in y-dir. Erase whole alien. */
902 rb->lcd_fillrect(x, y, ALIEN_WIDTH, ALIEN_HEIGHT);
903 } else {
904 if (aliens_right) {
905 /* Erase left edge */
906 rb->lcd_fillrect(x, y, ALIEN_SPEED, ALIEN_HEIGHT);
907 } else {
908 /* Erase right edge */
909 x += ALIEN_WIDTH - ALIEN_SPEED;
910 rb->lcd_fillrect(x, y, ALIEN_SPEED, ALIEN_HEIGHT);
914 /* Draw alien at new pos */
915 x = PLAYFIELD_X + LIVES_X + aliens[curr_alien].x * ALIEN_SPEED;
916 y = ALIEN_START_Y + aliens[curr_alien].y * ALIEN_HEIGHT;
917 rb->lcd_bitmap_part(invadrox,
918 aliens[curr_alien].x & 1 ? ALIEN_WIDTH : 0, aliens[curr_alien].type * ALIEN_HEIGHT,
919 STRIDE, x, y, ALIEN_WIDTH, ALIEN_HEIGHT);
921 if (!next_alien()) {
922 /* Round finished. Set curr_alien to first alive from bottom. */
923 if (!first_alien()) {
924 /* Should never happen. Taken care of in move_fire(). */
925 return false;
927 /* TODO: Play next background sound */
930 return true;
934 inline void draw_ship(void)
936 /* Erase old ship */
937 if (old_ship_x < ship_x) {
938 /* Move right. Erase leftmost part of ship. */
939 rb->lcd_fillrect(old_ship_x, SHIP_Y, ship_x - old_ship_x, SHIP_HEIGHT);
940 } else if (old_ship_x > ship_x) {
941 /* Move left. Erase rightmost part of ship. */
942 rb->lcd_fillrect(ship_x + SHIP_WIDTH, SHIP_Y, old_ship_x - ship_x, SHIP_HEIGHT);
945 /* Draw ship */
946 rb->lcd_bitmap_part(invadrox, SHIP_SRC_X, ship_frame * SHIP_HEIGHT,
947 STRIDE, ship_x, SHIP_Y, SHIP_WIDTH, SHIP_HEIGHT);
948 if (ship_hit) {
949 /* Alternate between frame 1 and 2 during hit */
950 ship_frame_counter++;
951 if (ship_frame_counter > 2) {
952 ship_frame_counter = 0;
953 ship_frame++;
954 if (ship_frame > 2) {
955 ship_frame = 1;
960 /* Save ship_x for next time */
961 old_ship_x = ship_x;
965 inline void fire_alpha(int xc, int yc, fb_data color)
967 int x, y;
968 unsigned char mask;
970 rb->lcd_set_foreground(color);
972 for (y = 0; y < FIRE_HEIGHT; y++) {
973 mask = 1 << (FIRE_WIDTH - 1);
974 for (x = -(FIRE_WIDTH / 2); x < (FIRE_WIDTH / 2); x++) {
975 if (fire_sprite[y] & mask) {
976 rb->lcd_drawpixel(xc + x, yc + y);
978 mask >>= 1;
982 rb->lcd_set_foreground(LCD_BLACK);
986 void move_fire(void)
988 bool hit_green = false;
989 bool hit_white = false;
990 int i, j;
991 static int exploding_alien = -1;
992 fb_data pix;
994 if (fire == S_IDLE) {
995 return;
998 /* Alien hit. Wait until explosion is finished. */
999 if (aliens_paralyzed < 0) {
1000 aliens_paralyzed++;
1001 if (aliens_paralyzed == 0) {
1002 /* Erase exploding_alien */
1003 rb->lcd_fillrect(PLAYFIELD_X + LIVES_X + aliens[exploding_alien].x * ALIEN_SPEED,
1004 ALIEN_START_Y + aliens[exploding_alien].y * ALIEN_HEIGHT,
1005 ALIEN_EXPLODE_WIDTH, ALIEN_HEIGHT);
1006 fire = S_IDLE;
1007 /* Special case. We killed curr_alien. */
1008 if (exploding_alien == curr_alien) {
1009 if (!next_alien()) {
1010 /* Round finished. Set curr_alien to first alive from bottom. */
1011 first_alien();
1015 return;
1018 if (fire == S_ACTIVE) {
1020 /* Erase */
1021 rb->lcd_vline(fire_x, fire_y, fire_y + SHOT_HEIGHT);
1023 /* Check top */
1024 if (fire_y <= SCORENUM_Y + FONT_HEIGHT + 4) {
1026 /* TODO: Play explode sound */
1028 fire = S_EXPLODE;
1029 fire_target = TARGET_TOP;
1030 fire_alpha(fire_x, fire_y, UFO_RED);
1031 return;
1034 /* Move */
1035 fire_y -= FIRE_SPEED;
1037 /* Hit UFO? */
1038 if (ufo_state == S_ACTIVE) {
1039 if ((ABS(ufo_x + UFO_WIDTH / 2 - fire_x) <= UFO_WIDTH / 2) &&
1040 (fire_y <= UFO_Y + UFO_HEIGHT)) {
1041 ufo_state = S_EXPLODE;
1042 fire = S_EXPLODE;
1043 fire_target = TARGET_UFO;
1044 /* Center explosion */
1045 ufo_x -= (UFO_EXPLODE_WIDTH - UFO_WIDTH) / 2;
1046 rb->lcd_bitmap_part(invadrox, UFO_SRC_X, UFO_HEIGHT,
1047 STRIDE, ufo_x, UFO_Y - 1, UFO_EXPLODE_WIDTH, UFO_EXPLODE_HEIGHT);
1048 return;
1052 /* Hit bomb? (check position, not pixel value) */
1053 for (i = 0; i < max_bombs; i++) {
1054 if (bombs[i].state == S_ACTIVE) {
1055 /* Count as hit if within BOMB_WIDTH pixels */
1056 if ((ABS(bombs[i].x - fire_x) < BOMB_WIDTH) &&
1057 (fire_y - bombs[i].y < BOMB_HEIGHT)) {
1058 /* Erase bomb */
1059 rb->lcd_fillrect(bombs[i].x, bombs[i].y, BOMB_WIDTH, BOMB_HEIGHT);
1060 bombs[i].state = S_IDLE;
1061 /* Explode ship fire */
1062 fire = S_EXPLODE;
1063 fire_target = TARGET_SHIELD;
1064 fire_alpha(fire_x, fire_y, LCD_WHITE);
1065 return;
1070 /* Check for hit*/
1071 for (i = FIRE_SPEED; i >= 0; i--) {
1072 pix = get_pixel(fire_x, fire_y + i);
1073 if(pix == screen_white) {
1074 hit_white = true;
1075 fire_y += i;
1076 break;
1078 if(pix == screen_green) {
1079 hit_green = true;
1080 fire_y += i;
1081 break;
1085 if (hit_green) {
1086 /* Hit shield */
1088 /* TODO: Play explode sound */
1090 fire = S_EXPLODE;
1091 fire_target = TARGET_SHIELD;
1092 /* Center explosion around hit pixel */
1093 fire_y -= FIRE_HEIGHT / 2;
1094 fire_alpha(fire_x, fire_y, SLIME_GREEN);
1095 return;
1098 if (hit_white) {
1100 /* Hit alien? */
1101 for (i = 0; i < 5 * ALIENS; i++) {
1102 if (aliens[i].state != DEAD &&
1103 (ABS(fire_x - (PLAYFIELD_X + LIVES_X + aliens[i].x * ALIEN_SPEED +
1104 ALIEN_WIDTH / 2)) <= ALIEN_WIDTH / 2) &&
1105 (ABS(fire_y - (ALIEN_START_Y + aliens[i].y * ALIEN_HEIGHT +
1106 ALIEN_HEIGHT / 2)) <= ALIEN_HEIGHT / 2)) {
1108 /* TODO: play alien hit sound */
1110 if (aliens[i].state == BOMBER) {
1111 /* Set (possible) alien above to bomber */
1112 for (j = i - ALIENS; j >= 0; j -= ALIENS) {
1113 if (aliens[j].state != DEAD) {
1114 /* printf("New bomber (%d, %d)\n", j % ALIENS, j / ALIENS); */
1115 aliens[j].state = BOMBER;
1116 break;
1120 aliens[i].state = DEAD;
1121 exploding_alien = i;
1122 score += scores[aliens[i].type];
1123 draw_score();
1124 /* Update score part of screen */
1125 rb->lcd_update_rect(SCORENUM_X, SCORENUM_Y,
1126 PLAYFIELD_WIDTH - 2 * NUMBERS_WIDTH, FONT_HEIGHT);
1128 /* Paralyze aliens S_EXPLODE frames */
1129 aliens_paralyzed = S_EXPLODE;
1130 rb->lcd_bitmap_part(invadrox, ALIEN_EXPLODE_SRC_X, ALIEN_EXPLODE_SRC_Y,
1131 STRIDE, PLAYFIELD_X + LIVES_X + aliens[i].x * ALIEN_SPEED,
1132 ALIEN_START_Y + aliens[i].y * ALIEN_HEIGHT,
1133 ALIEN_EXPLODE_WIDTH, ALIEN_EXPLODE_HEIGHT);
1134 /* Since alien is 1 pixel taller than explosion sprite, erase bottom line */
1135 rb->lcd_hline(PLAYFIELD_X + LIVES_X + aliens[i].x * ALIEN_SPEED,
1136 PLAYFIELD_X + LIVES_X + aliens[i].x * ALIEN_SPEED + ALIEN_WIDTH,
1137 ALIEN_START_Y + (aliens[i].y + 1) * ALIEN_HEIGHT - 1);
1138 return;
1143 /* Draw shot */
1144 rb->lcd_set_foreground(LCD_WHITE);
1145 rb->lcd_vline(fire_x, fire_y, fire_y + SHOT_HEIGHT);
1146 rb->lcd_set_foreground(LCD_BLACK);
1147 } else if (fire < S_IDLE) {
1148 /* Count up towards S_IDLE, then erase explosion */
1149 fire++;
1150 if (fire == S_IDLE) {
1151 /* Erase explosion */
1152 if (fire_target == TARGET_TOP) {
1153 rb->lcd_fillrect(fire_x - (FIRE_WIDTH / 2), fire_y, FIRE_WIDTH, FIRE_HEIGHT);
1154 } else if (fire_target == TARGET_SHIELD) {
1155 /* Draw explosion with black pixels */
1156 fire_alpha(fire_x, fire_y, LCD_BLACK);
1163 /* Return a BOMBER alien */
1164 inline int random_bomber(void)
1166 int i, col;
1168 /* TODO: Weigh higher probability near ship */
1169 col = rb->rand() % ALIENS;
1170 for (i = col + 4 * ALIENS; i >= 0; i -= ALIENS) {
1171 if (aliens[i].state == BOMBER) {
1172 return i;
1176 /* No BOMBER found in this col */
1178 for (i = 0; i < 5 * ALIENS; i++) {
1179 if (aliens[i].state == BOMBER) {
1180 return i;
1184 /* No BOMBER found at all (error?) */
1186 return -1;
1190 inline void draw_bomb(int i)
1192 rb->lcd_bitmap_part(invadrox, BOMB_SRC_X + bombs[i].type * BOMB_WIDTH,
1193 bombs[i].frame * (BOMB_HEIGHT + 1),
1194 STRIDE, bombs[i].x, bombs[i].y,
1195 BOMB_WIDTH, BOMB_HEIGHT);
1196 /* Advance frame */
1197 bombs[i].frame++;
1198 if (bombs[i].frame == bombs[i].frames) {
1199 bombs[i].frame = 0;
1204 void move_bombs(void)
1206 int i, j, bomber;
1207 bool abort;
1209 for (i = 0; i < max_bombs; i++) {
1211 switch (bombs[i].state) {
1213 case S_IDLE:
1214 if (ship_hit) {
1215 continue;
1217 bomber = random_bomber();
1218 if (bomber < 0) {
1219 DBG("ERROR: No bomber available\n");
1220 continue;
1222 /* x, y */
1223 bombs[i].x = PLAYFIELD_X + LIVES_X + aliens[bomber].x * ALIEN_SPEED + ALIEN_WIDTH / 2;
1224 bombs[i].y = ALIEN_START_Y + (aliens[bomber].y + 1) * ALIEN_HEIGHT;
1226 /* Check for duplets in x and y direction */
1227 abort = false;
1228 for (j = i - 1; j >= 0; j--) {
1229 if ((bombs[j].state == S_ACTIVE) &&
1230 ((bombs[i].x == bombs[j].x) || (bombs[i].y == bombs[j].y))) {
1231 abort = true;
1232 break;
1235 if (abort) {
1236 /* Skip this one, continue with next bomb */
1237 /* printf("Bomb %d duplet of %d\n", i, j); */
1238 continue;
1241 /* Passed, set type */
1242 bombs[i].type = rb->rand() % 3;
1243 bombs[i].frame = 0;
1244 if (bombs[i].type == 0) {
1245 bombs[i].frames = 3;
1246 } else if (bombs[i].type == 1) {
1247 bombs[i].frames = 4;
1248 } else {
1249 bombs[i].frames = 6;
1252 /* Bombs away */
1253 bombs[i].state = S_ACTIVE;
1254 draw_bomb(i);
1255 continue;
1257 break;
1259 case S_ACTIVE:
1260 /* Erase old position */
1261 rb->lcd_fillrect(bombs[i].x, bombs[i].y, BOMB_WIDTH, BOMB_HEIGHT);
1263 /* Move */
1264 bombs[i].y += BOMB_SPEED;
1266 /* Check if bottom hit */
1267 if (bombs[i].y + BOMB_HEIGHT >= PLAYFIELD_Y) {
1268 bombs[i].y = PLAYFIELD_Y - FIRE_HEIGHT + 1;
1269 fire_alpha(bombs[i].x, bombs[i].y, LCD_WHITE);
1270 bombs[i].state = S_EXPLODE;
1271 bombs[i].target = TARGET_BOTTOM;
1272 break;
1275 /* Check for green (ship or shield) */
1276 for (j = BOMB_HEIGHT; j >= BOMB_HEIGHT - BOMB_SPEED; j--) {
1277 bombs[i].target = 0;
1278 if(get_pixel(bombs[i].x + BOMB_WIDTH / 2, bombs[i].y + j) == screen_green) {
1279 /* Move to hit pixel */
1280 bombs[i].x += BOMB_WIDTH / 2;
1281 bombs[i].y += j;
1283 /* Check if ship is hit */
1284 if (bombs[i].y > SHIELD_Y + SHIELD_HEIGHT && bombs[i].y < PLAYFIELD_Y) {
1286 /* TODO: play ship hit sound */
1288 ship_hit = true;
1289 ship_frame = 1;
1290 ship_frame_counter = 0;
1291 bombs[i].state = S_EXPLODE * 4;
1292 bombs[i].target = TARGET_SHIP;
1293 rb->lcd_bitmap_part(invadrox, SHIP_SRC_X, 1 * SHIP_HEIGHT, STRIDE,
1294 ship_x, SHIP_Y, SHIP_WIDTH, SHIP_HEIGHT);
1295 break;
1297 /* Shield hit */
1298 bombs[i].state = S_EXPLODE;
1299 bombs[i].target = TARGET_SHIELD;
1300 /* Center explosion around hit pixel in shield */
1301 bombs[i].y -= FIRE_HEIGHT / 2;
1302 fire_alpha(bombs[i].x, bombs[i].y, SLIME_GREEN);
1303 break;
1307 if (bombs[i].target != 0) {
1308 /* Hit ship or shield, continue */
1309 continue;
1312 draw_bomb(i);
1313 break;
1315 default:
1316 /* If we get here state should be < 0, exploding */
1317 bombs[i].state++;
1318 if (bombs[i].state == S_IDLE) {
1319 if (ship_hit) {
1320 /* Erase explosion */
1321 rb->lcd_fillrect(ship_x, SHIP_Y, SHIP_WIDTH, SHIP_HEIGHT);
1322 rb->lcd_update_rect(ship_x, SHIP_Y, SHIP_WIDTH, SHIP_HEIGHT);
1323 ship_hit = false;
1324 ship_frame = 0;
1325 ship_x = PLAYFIELD_X + 2 * LIVES_X;
1326 lives--;
1327 if (lives == 0) {
1328 game_over = true;
1329 return;
1331 draw_lives();
1332 /* Sleep 1s to give player time to examine lives left */
1333 rb->sleep(HZ);
1335 /* Erase explosion (even if ship hit, might be another bomb) */
1336 fire_alpha(bombs[i].x, bombs[i].y, LCD_BLACK);
1338 break;
1344 inline void move_ship(void)
1346 ship_dir += ship_acc;
1347 if (ship_dir > max_ship_speed) {
1348 ship_dir = max_ship_speed;
1350 if (ship_dir < -max_ship_speed) {
1351 ship_dir = -max_ship_speed;
1353 ship_x += ship_dir;
1354 if (ship_x < SHIP_MIN_X) {
1355 ship_x = SHIP_MIN_X;
1357 if (ship_x > SHIP_MAX_X) {
1358 ship_x = SHIP_MAX_X;
1361 draw_ship();
1365 /* Unidentified Flying Object */
1366 void move_ufo(void)
1368 static int ufo_speed;
1369 static int counter;
1370 int mystery_score;
1372 switch (ufo_state) {
1374 case S_IDLE:
1376 if (rb->rand() % 500 == 0) {
1377 /* Uh-oh, it's time to launch a mystery UFO */
1379 /* TODO: Play UFO sound */
1381 if (rb->rand() % 2) {
1382 ufo_speed = UFO_SPEED;
1383 ufo_x = PLAYFIELD_X;
1384 } else {
1385 ufo_speed = -UFO_SPEED;
1386 ufo_x = LCD_WIDTH - PLAYFIELD_X - UFO_WIDTH;
1388 ufo_state = S_ACTIVE;
1389 /* UFO will be drawn next frame */
1391 break;
1393 case S_ACTIVE:
1394 /* Erase old pos */
1395 rb->lcd_fillrect(ufo_x, UFO_Y, UFO_WIDTH, UFO_HEIGHT);
1396 /* Move */
1397 ufo_x += ufo_speed;
1398 /* Check bounds */
1399 if (ufo_x < PLAYFIELD_X || ufo_x > LCD_WIDTH - PLAYFIELD_X - UFO_WIDTH) {
1400 ufo_state = S_IDLE;
1401 break;
1403 /* Draw new pos */
1404 rb->lcd_bitmap_part(invadrox, UFO_SRC_X, 0,
1405 STRIDE, ufo_x, UFO_Y, UFO_WIDTH, UFO_HEIGHT);
1406 break;
1408 case S_SHOWSCORE:
1409 counter++;
1410 if (counter == S_IDLE) {
1411 /* Erase mystery number */
1412 rb->lcd_fillrect(ufo_x, UFO_Y, 3 * NUMBERS_WIDTH + 2 * NUM_SPACING, FONT_HEIGHT);
1413 ufo_state = S_IDLE;
1415 break;
1417 default:
1418 /* Exploding */
1419 ufo_state++;
1420 if (ufo_state == S_IDLE) {
1421 /* Erase explosion */
1422 rb->lcd_fillrect(ufo_x, UFO_Y - 1, UFO_EXPLODE_WIDTH, UFO_EXPLODE_HEIGHT);
1423 ufo_state = S_SHOWSCORE;
1424 counter = S_EXPLODE * 4;
1425 /* Draw mystery_score, sleep, increase score and continue */
1426 mystery_score = 50 + (rb->rand() % 6) * 50;
1427 if (mystery_score < 100) {
1428 draw_number(ufo_x, UFO_Y, mystery_score, 2);
1429 } else {
1430 draw_number(ufo_x, UFO_Y, mystery_score, 3);
1432 score += mystery_score;
1433 draw_score();
1435 break;
1440 void draw_background(void)
1443 #if (LCD_WIDTH == 320) && (LCD_HEIGHT == 240)
1444 /* Erase background to black */
1445 rb->lcd_fillrect(PLAYFIELD_X, 0, PLAYFIELD_WIDTH, LCD_HEIGHT);
1446 /* Left and right bitmaps */
1447 rb->lcd_bitmap(invadrox_left, 0, 0, PLAYFIELD_X, LCD_HEIGHT);
1448 rb->lcd_bitmap(invadrox_right, LCD_WIDTH - PLAYFIELD_X, 0, PLAYFIELD_X, LCD_HEIGHT);
1449 #else
1450 rb->lcd_fillrect(0, 0, LCD_WIDTH, LCD_HEIGHT);
1451 #endif
1453 #if ((LCD_WIDTH == 240) && (LCD_HEIGHT == 320)) || ((LCD_WIDTH == 176) && (LCD_HEIGHT == 220))
1454 rb->lcd_bitmap(invadrox_logo, 0, 0, LCD_WIDTH, SCORE_Y);
1455 #endif
1457 rb->lcd_update();
1461 void new_level(void)
1463 int i;
1465 draw_background();
1466 /* Give an extra life for each new level */
1467 if (lives < MAX_LIVES) {
1468 lives++;
1470 draw_lives();
1472 /* Score */
1473 rb->lcd_bitmap_part(invadrox, SCORE_SRC_X, SCORE_SRC_Y,
1474 STRIDE, PLAYFIELD_X, SCORE_Y, SCORE_WIDTH, FONT_HEIGHT);
1475 /* Hi-score */
1476 rb->lcd_bitmap_part(invadrox, 0, SCORE_SRC_Y,
1477 STRIDE, HISCORE_X, SCORE_Y,
1478 HISCORE_WIDTH, FONT_HEIGHT);
1479 draw_score();
1480 draw_number(HISCORENUM_X, SCORENUM_Y, hiscore.score, 4);
1482 level++;
1483 draw_level();
1484 level_finished = false;
1486 ufo_state = S_IDLE;
1488 /* Init alien positions and states */
1489 for (i = 0; i < 4 * ALIENS; i++) {
1490 aliens[i].x = 0 + (i % ALIENS) * ((ALIEN_WIDTH + ALIEN_SPACING) / ALIEN_SPEED);
1491 aliens[i].y = 2 * (i / ALIENS);
1492 aliens[i].state = ALIVE;
1494 /* Last row, bombers */
1495 for (i = 4 * ALIENS; i < 5 * ALIENS; i++) {
1496 aliens[i].x = 0 + (i % ALIENS) * ((ALIEN_WIDTH + ALIEN_SPACING) / ALIEN_SPEED);
1497 aliens[i].y = 2 * (i / ALIENS);
1498 aliens[i].state = BOMBER;
1501 /* Init bombs to inactive (S_IDLE) */
1502 for (i = 0; i < MAX_BOMBS; i++) {
1503 bombs[i].state = S_IDLE;
1506 /* Start aliens closer to earth from level 2 */
1507 for (i = 0; i < 5 * ALIENS; i++) {
1508 if (level < 6) {
1509 aliens[i].y += level - 1;
1510 } else {
1511 aliens[i].y += 5;
1515 /* Max concurrent bombs */
1516 max_bombs = 1;
1518 gamespeed = 2;
1520 if (level > 1) {
1521 max_bombs++;
1524 /* Increase speed */
1525 if (level > 2) {
1526 gamespeed++;
1529 if (level > 3) {
1530 max_bombs++;
1533 /* Increase speed more */
1534 if (level > 4) {
1535 gamespeed++;
1538 if (level > 5) {
1539 max_bombs++;
1542 /* 4 shields */
1543 for (i = 1; i <= 4; i++) {
1544 rb->lcd_bitmap_part(invadrox, SHIELD_SRC_X, SHIELD_SRC_Y, STRIDE,
1545 PLAYFIELD_X + i * PLAYFIELD_WIDTH / 5 - SHIELD_WIDTH / 2,
1546 SHIELD_Y, SHIELD_WIDTH, SHIELD_HEIGHT);
1549 /* Bottom line */
1550 rb->lcd_set_foreground(SLIME_GREEN);
1551 rb->lcd_hline(PLAYFIELD_X, LCD_WIDTH - PLAYFIELD_X, PLAYFIELD_Y);
1552 /* Restore foreground to black (for fast erase later). */
1553 rb->lcd_set_foreground(LCD_BLACK);
1555 ship_x = PLAYFIELD_X + 2 * LIVES_X;
1556 if (level == 1) {
1557 old_ship_x = ship_x;
1559 ship_dir = 0;
1560 ship_acc = 0;
1561 ship_frame = 0;
1562 ship_hit = false;
1563 fire = S_IDLE;
1564 /* Start moving the bottom row left to right */
1565 curr_alien = 4 * ALIENS;
1566 aliens_paralyzed = 0;
1567 aliens_right = true;
1568 aliens_down = false;
1569 hit_left_border = false;
1570 hit_right_border = false;
1571 /* TODO: Change max_ship_speed to 3 at higher levels */
1572 max_ship_speed = 2;
1574 draw_aliens();
1576 rb->lcd_update();
1580 void init_invadrox(void)
1582 int i;
1584 /* Seed random number generator with a "random" number */
1585 rb->srand(rb->get_time()->tm_sec + rb->get_time()->tm_min * 60);
1587 /* Precalculate start of each scanline */
1588 for (i = 0; i < LCD_HEIGHT; i++) {
1589 #if (LCD_DEPTH >= 8)
1590 ytab[i] = i * LCD_WIDTH;
1591 #elif (LCD_DEPTH == 2) && (LCD_PIXELFORMAT == HORIZONTAL_PACKING)
1592 ytab[i] = i * (LCD_WIDTH / 4);
1593 #elif (LCD_DEPTH == 2) && (LCD_PIXELFORMAT == VERTICAL_PACKING)
1594 ytab[i] = (i / 4) * LCD_WIDTH;
1595 #else
1596 #error pixelformat not implemented yet
1597 #endif
1600 rb->lcd_set_background(LCD_BLACK);
1601 rb->lcd_set_foreground(LCD_BLACK);
1603 highscore_init(rb);
1604 if (highscore_load(HISCOREFILE, &hiscore, 1) < 0) {
1605 /* Init hiscore to 0 */
1606 rb->strncpy(hiscore.name, "Invader", sizeof(hiscore.name));
1607 hiscore.score = 0;
1608 hiscore.level = 1;
1611 /* Init alien types in aliens array */
1612 for (i = 0; i < 1 * ALIENS; i++) {
1613 aliens[i].type = 0; /* Kang */
1615 for (; i < 3 * ALIENS; i++) {
1616 aliens[i].type = 1; /* Kodos */
1618 for (; i < 5 * ALIENS; i++) {
1619 aliens[i].type = 2; /* Serak */
1623 /* Save screen white color */
1624 rb->lcd_set_foreground(LCD_WHITE);
1625 rb->lcd_drawpixel(0, 0);
1626 rb->lcd_update_rect(0, 0, 1, 1);
1627 screen_white = get_pixel(0, 0);
1629 /* Save screen green color */
1630 rb->lcd_set_foreground(SLIME_GREEN);
1631 rb->lcd_drawpixel(0, 0);
1632 rb->lcd_update_rect(0, 0, 1, 1);
1633 screen_green = get_pixel(0, 0);
1635 /* Restore black foreground */
1636 rb->lcd_set_foreground(LCD_BLACK);
1638 new_level();
1640 /* Flash score at start */
1641 for (i = 0; i < 5; i++) {
1642 rb->lcd_fillrect(SCORENUM_X, SCORENUM_Y,
1643 4 * NUMBERS_WIDTH + 3 * NUM_SPACING,
1644 FONT_HEIGHT);
1645 rb->lcd_update_rect(SCORENUM_X, SCORENUM_Y,
1646 4 * NUMBERS_WIDTH + 3 * NUM_SPACING,
1647 FONT_HEIGHT);
1648 rb->sleep(HZ / 10);
1649 draw_number(SCORENUM_X, SCORENUM_Y, score, 4);
1650 rb->sleep(HZ / 10);
1655 inline bool handle_buttons(void)
1657 static unsigned int oldbuttonstate = 0;
1659 unsigned int released, pressed, newbuttonstate;
1661 if (ship_hit) {
1662 /* Don't allow ship movement during explosion */
1663 newbuttonstate = 0;
1664 } else {
1665 newbuttonstate = rb->button_status();
1667 if(newbuttonstate == oldbuttonstate) {
1668 if (newbuttonstate == 0) {
1669 /* No button pressed. Stop ship. */
1670 ship_acc = 0;
1671 if (ship_dir > 0) {
1672 ship_dir--;
1674 if (ship_dir < 0) {
1675 ship_dir++;
1678 /* return false; */
1679 goto check_usb;
1681 released = ~newbuttonstate & oldbuttonstate;
1682 pressed = newbuttonstate & ~oldbuttonstate;
1683 oldbuttonstate = newbuttonstate;
1684 if (pressed) {
1685 if (pressed & LEFT) {
1686 if (ship_acc > -1) {
1687 ship_acc--;
1690 if (pressed & RIGHT) {
1691 if (ship_acc < 1) {
1692 ship_acc++;
1695 if (pressed & FIRE) {
1696 if (fire == S_IDLE) {
1697 /* Fire shot */
1698 fire_x = ship_x + SHIP_WIDTH / 2;
1699 fire_y = SHIP_Y - SHOT_HEIGHT;
1700 fire = S_ACTIVE;
1701 /* TODO: play fire sound */
1704 #ifdef RC_QUIT
1705 if (pressed & RC_QUIT) {
1706 rb->splash(HZ * 1, "Quit");
1707 return true;
1709 #endif
1710 if (pressed & QUIT) {
1711 rb->splash(HZ * 1, "Quit");
1712 return true;
1715 if (released) {
1716 if ((released & LEFT)) {
1717 if (ship_acc < 1) {
1718 ship_acc++;
1721 if ((released & RIGHT)) {
1722 if (ship_acc > -1) {
1723 ship_acc--;
1728 check_usb:
1730 /* Quit if USB is connected */
1731 if (rb->button_get(false) == SYS_USB_CONNECTED) {
1732 return true;
1735 return false;
1739 void game_loop(void)
1741 int i, end;
1743 /* Print dimensions (just for debugging) */
1744 DBG("%03dx%03dx%02d\n", LCD_WIDTH, LCD_HEIGHT, LCD_DEPTH);
1746 /* Init */
1747 init_invadrox();
1749 while (1) {
1750 /* Convert CYCLETIME (in ms) to HZ */
1751 end = *rb->current_tick + (CYCLETIME * HZ) / 1000;
1753 if (handle_buttons()) {
1754 return;
1757 /* Animate */
1758 move_ship();
1759 move_fire();
1761 /* Check if level is finished (marked by move_fire) */
1762 if (level_finished) {
1763 /* TODO: Play level finished sound */
1764 new_level();
1767 move_ufo();
1769 /* Move aliens */
1770 if (!aliens_paralyzed && !ship_hit) {
1771 for (i = 0; i < gamespeed; i++) {
1772 if (!move_aliens()) {
1773 if (game_over) {
1774 return;
1780 /* Move alien bombs */
1781 move_bombs();
1782 if (game_over) {
1783 return;
1786 /* Update "playfield" rect */
1787 rb->lcd_update_rect(PLAYFIELD_X, SCORENUM_Y + FONT_HEIGHT,
1788 PLAYFIELD_WIDTH,
1789 PLAYFIELD_Y + 1 - SCORENUM_Y - FONT_HEIGHT);
1791 /* Wait until next frame */
1792 DBG("%ld (%d)\n", end - *rb->current_tick, (CYCLETIME * HZ) / 1000);
1793 if (end > *rb->current_tick) {
1794 rb->sleep(end - *rb->current_tick);
1795 } else {
1796 rb->yield();
1799 } /* end while */
1803 /* this is the plugin entry point */
1804 enum plugin_status plugin_start(const struct plugin_api* api, UNUSED const void* parameter)
1806 rb = api;
1808 rb->lcd_setfont(FONT_SYSFIXED);
1809 /* Turn off backlight timeout */
1810 backlight_force_on(rb); /* backlight control in lib/helper.c */
1812 /* now go ahead and have fun! */
1813 game_loop();
1815 /* Game Over. */
1816 /* TODO: Play game over sound */
1817 rb->splash(HZ * 2, "Game Over");
1818 if (score > hiscore.score) {
1819 /* Save new hiscore */
1820 hiscore.score = score;
1821 hiscore.level = level;
1822 highscore_save(HISCOREFILE, &hiscore, 1);
1825 /* Restore user's original backlight setting */
1826 rb->lcd_setfont(FONT_UI);
1827 /* Turn on backlight timeout (revert to settings) */
1828 backlight_use_settings(rb); /* backlight control in lib/helper.c */
1830 return PLUGIN_OK;
1836 * GNU Emacs settings: Kernighan & Richie coding style with
1837 * 4 spaces indent and no tabs.
1838 * Local Variables:
1839 * c-file-style: "k&r"
1840 * c-basic-offset: 4
1841 * indent-tabs-mode: nil
1842 * End: