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