| C:\__todo__\tecaj.www\src\Epsilon_05\GameCanvas.java |
1 /**
2 * GameCanvas contains the Canvas for the Epsilon game.
3 * Epsilon is a simple space-shooter-vertical-scroller game.
4 *
5 * Epsilon is available for non-commercial use only!
6 * You can learn from this sources and you can modify them
7 * for learning purposes.
8 *
9 * @author Ziga Hajdukovic
10 * @version 1.0
11 *
12 * CHANGES (01 --> 02)
13 * + new player sprite, p.png
14 * + added boss sprite boss1.png
15 * + added tileset t04.png
16 * + added enemy sprites e1.png, e2.png, e3.png
17 * + added fire.png, removed pf.png
18 * + added explosion sprite, exp.png
19 * + moved Image objects for sprites to spriteImage array
20 * + added tileImages
21 * + moved image loading to loadImages()
22 * + setClip in paintSprite
23 * + player sprite frames for left and right movement
24 * + multiple fire sprites + fire_cooldown_wait_frames
25 * + 20 FPS fixed frame rate
26 * + Vector explosionSprites
27 * + updateExplosionSprites()
28 * + doCollision() -> add explosion on collision
29 * + paint explosionSprites
30 * + Level class
31 * + loadLevel(), tile map, and sprite positions
32 * + space and tiles y and vy
33 * + load sprites positions
34 * + level paint
35 * + (scrolling background space layer)
36 * + (scrolling tile layer)
37 * + new (but still random) enemies
38 * + player game over explosion
39 *
40 *
41 * CHANGES (02 -> 03)
42 * + run() if(gameRunning)
43 * + run(), + midlet.display.callSerially(this);
44 * + midlet.startApp(), gameCanvas.run()
45 *
46 * + pause game screen
47 * + new menu scr paused game, with 2 softkey labels
48 * + resume on softkey1
49 * + main menu on softkey2
50 * + hidenotify()
51 * + player.vy += Level.tiles_scroll_vy
52 * + removed automatic enemy sprite generation
53 * + updated player screen edge collision detection
54 * + spriteData array
55 * + damage and score added to GameSprite, see doCollision
56 * + EXPLOSION_FRAME_SMOKE_START
57 * + move player with level scroll
58 * + enemy activation zone
59 * + remove out-of-screen enemies, updated!
60 * + doEnemyAI()
61 * + fire_cooldown_frames_counter moved to GameSprite
62 * + collision updated, vector loop inverted...
63 * + enemyFireSprites Vector, updateEnemyFireSprites() paint ...
64 * + doEnemyAI(), shooting enemies!
65 * + enemy fire sprites <-> player collision
66 * + boss big bada boom explosion, like players...
67 * + god_mode
68 * + boss, moving, shooting
69 * + view and enter hiscore screens
70 * + flow for score view and edit
71 * + import javax.microedition.rms.*;
72 * + import java.io.*;
73 * + loadHighscoreFromRMS(), add call to intro mode, when loading
74 * + isQualified(int score)
75 * + addHighscore(int score, String name)
76 * + getHighscoreData(), highscore helper function
77 * + saveHighscores(), highscore helper function
78 *
79 *
80 * CHANGES (03 -> 04)
81 *
82 * + added visible property to GameSprite, used for enemy paint and update optimization
83 * + BUGFIX> menu option after highscore
84 * + BUGFIX> player cannot be killed by enemy fire
85 * + FPS counter
86 * + 1,3,7 and 9 keys for diagonal movement
87 * + all level_scroll and x,y and vx,vy of sprites are now representations of floats, with factor SHIFT_FACTOR
88 * + level paint, used shifting instead of * and /
89 * + paint and run profiling!
90 * + LEVEL_DONE and GAME_DONE, new menu screens
91 * + new enemy3 types, sinusoidal movement, with shooting in players direction
92 * + new boss2 type
93 * + new enemy4 type, gun turret, shooting in players direction
94 * + new image for boss2 and gun turret enemy4
95 * + player_nukes
96 * + nuke status bar indicators
97 * + nuke background fade fx
98 * + playMusic(), loadMusic(), stopMusic()
99 * + hideNotify() added a call to stop music
100 * + showNotify() added to restart music
101 *
102 *
103 * CHANGES (03 -> 04)
104 *
105 * + icon i.png, 24x24 for nokia s60, added to res and link to .jad
106 * + boss2 movement
107 * + boss2 special paint
108 * + boss2 shoot toward player every x seconds
109 * + boss2 opening every now an then and shooting a lot when open
110 * + boss2 invincible when closed
111 *
112 */
113
114 import java.util.*;
115
116 import javax.microedition.lcdui.*;
117
118 import javax.microedition.rms.*;
119 import java.io.*;
120
121 import com.nokia.mid.ui.FullCanvas;
122
123 import javax.microedition.media.*;
124 import javax.microedition.media.control.*;
125
126 class GameCanvas extends FullCanvas implements Runnable
127 //class GameCanvas extends Canvas implements Runnable
128 {
129 private final Game midlet;
130
131 public static int canvasWidth;
132 public static int canvasHeight;
133
134 public static boolean gameRunning;
135
136 public static final int SOFTKEY_1 = -6;
137 public static final int SOFTKEY_2 = -7;
138
139 public static boolean menu_command_1;
140 public static boolean menu_command_2;
141
142 public static boolean command_up;
143 public static boolean command_down;
144 public static boolean command_left;
145 public static boolean command_right;
146 public static boolean command_fire;
147
148 public static byte mode;
149 public static final byte MODE_INTRO = 0;
150 public static final byte MODE_GAME = 1;
151 public static final byte MODE_MENU = 2;
152
153 public static int loadingCounter;
154
155 public static Image imgIntro;
156 public static Image imgBackground;
157
158 public static Image[] spriteImages;
159 public static final byte SPRITE_IMAGE_PLAYER = 0;
160 public static final byte SPRITE_IMAGE_FIRE = 1;
161 public static final byte SPRITE_IMAGE_BOSS1 = 2;
162 public static final byte SPRITE_IMAGE_BOSS2 = 3;
163 public static final byte SPRITE_IMAGE_ENEMY1 = 4;
164 public static final byte SPRITE_IMAGE_ENEMY2 = 5;
165 public static final byte SPRITE_IMAGE_ENEMY3 = 6;
166 public static final byte SPRITE_IMAGE_ENEMY4 = 7;
167 public static final byte SPRITE_IMAGE_EXPLOSION = 8;
168 public static final byte SPRITE_IMAGE_MAX = 9;
169
170 public static final byte SPRITE_FRAME_PLAYER_LEFT = 0;
171 public static final byte SPRITE_FRAME_PLAYER_CENTER = 1;
172 public static final byte SPRITE_FRAME_PLAYER_RIGHT = 2;
173
174 public static final byte PLAYER_FIRE_COOLDOWN_WAIT_FRAMES = 3;
175 public static final byte ENEMY_FIRE_COOLDOWN_WAIT_FRAMES = 40;
176
177 public static Image tileImage;
178
179 public static int menuScreen;
180 public static final byte MENU_SCR_MAIN = 0;
181 public static final byte MENU_SCR_TEXT_ABOUT = 1;
182 public static final byte MENU_SCR_TEXT_HELP = 2;
183 public static final byte MENU_SCR_TEXT_GAME_OVER = 3;
184 public static final byte MENU_SCR_PAUSE_GAME = 4;
185 public static final byte MENU_SCR_HISCORE_ENTER = 5;
186 public static final byte MENU_SCR_HISCORE_VIEW = 6;
187 public static final byte MENU_SCR_TEXT_LEVEL_DONE = 7;
188 public static final byte MENU_SCR_TEXT_GAME_DONE = 8;
189
190 public static int menuSelectedOption;
191 public static final byte MENU_OPTION_MAIN_NEW_GAME = 0;
192 public static final byte MENU_OPTION_MAIN_HISCORES = 1;
193 public static final byte MENU_OPTION_MAIN_MUSIC_ON_OFF = 2;
194 public static final byte MENU_OPTION_MAIN_ABOUT = 3;
195 public static final byte MENU_OPTION_MAIN_HELP = 4;
196 public static final byte MENU_OPTION_MAIN_MAX = 4;
197
198 public static final String TEXT_ON = " on";
199 public static final String TEXT_OFF = " off";
200
201 public static final String[] menuScreenMainOptions = { "New game", "Highscore", "Music", "About", "Help"};
202
203 public static final String[][] menuScreensTextLines = {
204 { "" },
205 { "About", "", "Epsilon v0.5", "(c) 2005 zigah.", "gfx by geci." },
206 { "Help", "", "Press LEFT, RIGHT, ", "UP or DOWN", "to move and", "FIRE to shoot." },
207 { "Game Over", "", "Try harder!"},
208 { "","Paused!"},
209 { },
210 { },
211 { "Stage clear!", "", "Well done!"},
212 { "Game over!", "", "Superb!"},
213 };
214
215
216 // Highscore
217 public static final byte MAX_SCORES = 5;
218 public static final String SCORE_DB_NAME = "EPS";
219 public static String hiNames[] = { "JOE", "DOE", "LAA", "BIG", "MAC" };
220 public static int hiScores[] = { 3000, 2000, 1000, 500, 250 };
221 private static RecordStore db;
222
223 public static final char[] HI_NAME_ENTER_DEFAULT = {"A","A","A"};
224 public static final byte MAX_HI_NAME_LENGTH = 3;
225 public static char[] hi_name_enter = new char[MAX_HI_NAME_LENGTH];
226 public static byte hi_name_enter_char_pos;
227
228
229 // for debug purposes
230 public boolean god_mode;
231
232 public static GameSprite player;
233 public static Vector playerFireSprites;
234 public static int player_score;
235 public static int player_gameover_explosion_counter;
236 public static final byte PLAYER_GAMEOVER_EXPLOSION_COUNTER_MAX = 40;
237
238 public static int player_nukes;
239 public static int player_nuke_counter = 0;
240 public static final byte PLAYER_NUKES_MAX = 3;
241 public static final byte PLAYER_NUKE_COUNTER_MAX = 16;
242
243 public static boolean activate_boss_energy_bar;
244
245 public static int boss_dead_explosion_counter;
246 public static final byte BOSS_DEAD_EXPLOSION_COUNTER_MAX = 80;
247
248 // boss2 vars
249 public static byte boss2_open_counter;
250 public static byte boss2_open_dir;
251 public static final byte BOSS2_OPEN_COUNTER_MAX = 7;
252 public static final byte BOSS2_WING_WIDTH = 11;
253
254 public static byte boss2_closed_counter;
255 public static final byte BOSS2_CLOSED_COUNTER_MAX = 90;
256 public static byte boss2_stay_open_counter;
257 public static final byte BOSS2_STAY_OPEN_COUNTER_MAX = 40;
258
259
260 public static final short FIRE_VELOCITY = (8 << GameSprite.SHIFT_FACTOR);
261 public static final byte MAX_ENEMY_FIRE = 6;
262
263 public static Vector enemyFireSprites;
264
265 public static final byte MAX_ENERGY_PLAYER = 100;
266 public static final byte MAX_ENERGY_PLAYER_FIRE = 10;
267 public static final byte MAX_ENERGY_ENEMY = 10;
268
269 public static Vector explosionSprites;
270
271 public static byte max_enemy_sprites = 3;
272
273 public static final short FIXED_FRAME_TIME = 10;
274
275 public static int global_frame_counter;
276
277 public static Random random = new Random();
278
279 public static boolean PROFILE_ON_SCREEN = false; // for all the profiling drawString calls
280
281 GameCanvas(Game midlet)
282 {
283 this.midlet = midlet;
284
285 canvasWidth = this.getWidth();
286 canvasHeight = this.getHeight();
287
288 loadingCounter = 0;
289 menuScreen = MENU_SCR_MAIN;
290 menuSelectedOption = MENU_OPTION_MAIN_NEW_GAME;
291 mode = MODE_INTRO;
292 gameRunning = true;
293 }
294
295 public static int FPS_frame_counter;
296 public static long FPS_time;
297 public static int FPS;
298
299 public synchronized void run()
300 {
301 if(gameRunning)
302 {
303 long frameStartTime = System.currentTimeMillis();
304 global_frame_counter++;
305
306 FPS_frame_counter++;
307 if (FPS_time + 1000 < frameStartTime)
308 {
309 FPS_time = frameStartTime;
310 FPS = FPS_frame_counter;
311 FPS_frame_counter = 0;
312 System.out.println("FPS:"+ FPS);
313 }
314
315 repaint();
316 //serviceRepaints();
317 midlet.display.callSerially(this);
318
319 switch (mode) {
320 case MODE_INTRO:
321
322 try
323 {
324 if (loadingCounter == 0)
325 {
326 imgIntro = Image.createImage("/intro.png");
327 }
328 else if (loadingCounter == 1)
329 {
330 loadImages();
331 loadMusic();
332 loadHighscoreFromRMS();
333 }
334 else
335 {
336 // view the intro splash image for a while
337 // it would be better to let user skip intro by pressing a key
338 // but, in a full game project, loading would take forever, anyway, so...
339 try {
340 Thread.sleep(1000);
341 } catch (Exception e) {};
342 // switch mode
343 mode = MODE_MENU;
344 playMusic();
345 }
346 loadingCounter++;
347 }
348 catch (Exception e) {
349 // ignore loading exception
350 };
351 break;
352
353 case MODE_MENU:
354
355 if (command_up) {
356 command_up = false;
357 menuSelectedOption--;
358 if (menuSelectedOption < 0)
359 menuSelectedOption = 0;
360
361 if (menuScreen == MENU_SCR_HISCORE_ENTER)
362 {
363 if ( hi_name_enter[hi_name_enter_char_pos] > "A")
364 hi_name_enter[hi_name_enter_char_pos]--;
365 }
366 }
367 if (command_down) {
368 command_down = false;
369 menuSelectedOption++;
370 if ((menuScreen == MENU_SCR_MAIN) &&
371 (menuSelectedOption > MENU_OPTION_MAIN_MAX))
372 menuSelectedOption = MENU_OPTION_MAIN_MAX;
373
374 if (menuScreen == MENU_SCR_HISCORE_ENTER)
375 {
376 if ( hi_name_enter[hi_name_enter_char_pos] < "Z")
377 hi_name_enter[hi_name_enter_char_pos]++;
378 }
379 }
380 if (command_left) {
381 command_left = false;
382
383 if (menuScreen == MENU_SCR_HISCORE_ENTER)
384 {
385 if (hi_name_enter_char_pos > 0)
386 hi_name_enter_char_pos--;
387 }
388 }
389 if (command_right) {
390 command_left = false;
391
392 if (menuScreen == MENU_SCR_HISCORE_ENTER)
393 {
394 if (hi_name_enter_char_pos < MAX_HI_NAME_LENGTH-1)
395 hi_name_enter_char_pos++;
396 }
397 }
398
399 if (menu_command_1)
400 {
401 menu_command_1 = false;
402 if (menuScreen == MENU_SCR_MAIN)
403 {
404 switch(menuSelectedOption)
405 {
406 case MENU_OPTION_MAIN_NEW_GAME:
407
408 // init number of nukes available to player
409 player_nukes = PLAYER_NUKES_MAX;
410
411 // init score
412 player_score = 0;
413
414 startGame((byte) 1);
415 break;
416
417 case MENU_OPTION_MAIN_HISCORES:
418 menuScreen = MENU_SCR_HISCORE_VIEW;
419 break;
420
421 case MENU_OPTION_MAIN_ABOUT:
422 menuScreen = MENU_SCR_TEXT_ABOUT;
423 break;
424
425 case MENU_OPTION_MAIN_MUSIC_ON_OFF:
426 musicOn = !musicOn;
427 if (musicOn)
428 playMusic();
429 else
430 stopMusic();
431 break;
432
433 case MENU_OPTION_MAIN_HELP:
434 menuScreen = MENU_SCR_TEXT_HELP;
435 break;
436 }
437 } else if ( menuScreen == MENU_SCR_PAUSE_GAME) {
438 // resume pressed
439 mode = MODE_GAME;
440 } else if ( menuScreen == MENU_SCR_TEXT_LEVEL_DONE) {
441 // load next level
442 startGame( (byte) (Level.levelno+1) );
443
444 } else if ( menuScreen == MENU_SCR_TEXT_GAME_OVER || menuScreen == MENU_SCR_TEXT_GAME_DONE) {
445 // ok pressed
446 if ( isQualified( player_score ) != -1 )
447 {
448 menuScreen = MENU_SCR_HISCORE_ENTER;
449 // init enter name character position
450 hi_name_enter_char_pos = 0;
451 hi_name_enter = HI_NAME_ENTER_DEFAULT;
452 } else {
453 menuScreen = MENU_SCR_HISCORE_VIEW;
454 }
455 } else if ( menuScreen == MENU_SCR_HISCORE_ENTER) {
456 // save highscore
457
458 String name = new String(hi_name_enter);
459 addHighscore(player_score, name);
460
461 menuScreen = MENU_SCR_HISCORE_VIEW;
462 } else {
463 // all other menu screens go to main menu when OK is selected
464 menuScreen = MENU_SCR_MAIN;
465 menuSelectedOption = MENU_OPTION_MAIN_NEW_GAME;
466 }
467 }
468 if (menu_command_2)
469 {
470 menu_command_2 = false;
471
472 // back to main menu, when end game selected
473 if ( menuScreen == MENU_SCR_PAUSE_GAME) {
474 menuScreen = MENU_SCR_MAIN;
475 } else if (menuScreen == MENU_SCR_MAIN)
476 {
477 gameRunning = false;
478 midlet.exitRequested();
479 }
480 }
481
482 // delay, so the keypress events get their turn...
483 try {
484 Thread.sleep(40);
485 } catch (Exception e) {};
486 break;
487
488 case MODE_GAME:
489
490 // scroll space and tile background
491 Level.space_scroll_y += Level.space_scroll_vy;
492 Level.tiles_scroll_y += Level.tiles_scroll_vy;
493
494 // top of the level (boss) reached, stop scrolling
495 if (Level.tiles_scroll_y < 0) {
496 Level.tiles_scroll_y = 0;
497 Level.tiles_scroll_vy = 0;
498 }
499
500 // int tiles_scroll_x_range = (Level.LEVEL_WIDTH * Level.TILE_SIZE - canvasWidth);
501 // int player_x_range = canvasWidth - player.w;
502 // Level.tiles_scroll_x = player.x * tiles_scroll_x_range / player_x_range;
503
504
505 // update scroll_x
506 Level.tiles_scroll_x = player.x + (player.w/2 << GameSprite.SHIFT_FACTOR) - (canvasWidth/2 << GameSprite.SHIFT_FACTOR);
507 if ((Level.tiles_scroll_x >> GameSprite.SHIFT_FACTOR) > (Level.LEVEL_WIDTH * Level.TILE_SIZE - canvasWidth)) {
508 Level.tiles_scroll_x = (Level.LEVEL_WIDTH * Level.TILE_SIZE - canvasWidth) << GameSprite.SHIFT_FACTOR;
509 }
510
511 if (Level.tiles_scroll_x < 0) {
512 Level.tiles_scroll_x = 0;
513 }
514
515 updatePlayerSprite();
516 updatePlayerFireSprites();
517
518 updateEnemySprites();
519 updateEnemyFireSprites();
520
521 updateExplosionSprites();
522
523 // check player <-> enemy <-> player fire collision
524 doCollision();
525
526 // check for game end
527 checkEndGame();
528
529 if (menu_command_2)
530 {
531 menu_command_2 = false;
532 // on softkey_2 activate pause game menu
533 mode = MODE_MENU;
534 menuScreen = MENU_SCR_PAUSE_GAME;
535 }
536
537 // calc. delay
538 long frameTime = System.currentTimeMillis() - frameStartTime;
539 if (frameTime < FIXED_FRAME_TIME)
540 {
541 try {
542 Thread.sleep( FIXED_FRAME_TIME - frameTime );
543 } catch (Exception e) {};
544 }
545 break;
546 }
547 }
548 }
549
550
551 public byte PAINT_PROFILE_TIMES_MAX = 4;
552 public long[] paint_profile_times = new long[PAINT_PROFILE_TIMES_MAX*2];
553 public String[] paint_profile_labels = { " ", " ", " ", " ", " " };
554 public int paint_profile_times_index_counter;
555
556 public void paint_profileStart(int idx, String label) {
557 paint_profile_times_index_counter = idx*2;
558 paint_profile_times_index_counter = idx*2;
559 paint_profile_labels[paint_profile_times_index_counter / 2] = label;
560 paint_profile_times[paint_profile_times_index_counter] = System
561 .currentTimeMillis();
562 paint_profile_times_index_counter++;
563 }
564
565 public void paint_profileEnd() {
566 paint_profile_times[paint_profile_times_index_counter] = paint_profile_times[paint_profile_times_index_counter]
567 + (System.currentTimeMillis() - paint_profile_times[paint_profile_times_index_counter - 1]);
568 }
569
570 public synchronized void paint(Graphics g)
571 {
572 paint_profile_times_index_counter = 0;
573 switch(mode)
574 {
575 case MODE_INTRO:
576
577 g.setColor(0x000000);
578 g.fillRect(0, 0, canvasWidth, canvasHeight);
579 if (imgIntro != null) {
580 g.drawImage(imgIntro, canvasWidth/2, canvasHeight/2, Graphics.VCENTER | Graphics.HCENTER);
581 }
582
583 // g.setColor(0xFFFFFF);
584 // g.drawString(""+loadingCounter,0,0,Graphics.LEFT | Graphics.TOP);
585
586 break;
587
588 case MODE_MENU:
589
590 Level.paintSpaceBackground(g);
591
592 switch(menuScreen)
593 {
594 case MENU_SCR_MAIN:
595 paintMainMenu(g);
596 break;
597
598 case MENU_SCR_TEXT_ABOUT:
599 case MENU_SCR_TEXT_HELP:
600 case MENU_SCR_TEXT_GAME_OVER:
601 case MENU_SCR_TEXT_GAME_DONE:
602 case MENU_SCR_TEXT_LEVEL_DONE:
603 case MENU_SCR_PAUSE_GAME:
604 paintTextMenu(g, menuScreen);
605 break;
606 case MENU_SCR_HISCORE_VIEW:
607 paintHiscoreView(g);
608 break;
609 case MENU_SCR_HISCORE_ENTER:
610 paintHiscoreEnter(g);
611 break;
612 }
613 break;
614
615 case MODE_GAME:
616
617 paint_profileStart(0,"BG");
618 Level.paintSpaceBackground(g);
619 paint_profileEnd();
620 paint_profileStart(1,"TL");
621 Level.paintTiles(g);
622 paint_profileEnd();
623
624 paint_profileStart(2,"SP");
625 paintSprite( g, player );
626
627 for (int i=0; i < Level.enemySprites.size(); i++)
628 {
629 GameSprite enemy = (GameSprite) Level.enemySprites.elementAt(i);
630 if (enemy.visible)
631 paintSprite( g, enemy );
632 }
633
634 for (int i=0; i < explosionSprites.size(); i++)
635 paintSprite( g, (GameSprite) explosionSprites.elementAt(i) );
636
637 for (int i=0; i < playerFireSprites.size(); i++)
638 paintSprite( g, (GameSprite) playerFireSprites.elementAt(i));
639
640 for (int i=0; i < enemyFireSprites.size(); i++)
641 paintSprite( g, (GameSprite) enemyFireSprites.elementAt(i));
642
643 paint_profileEnd();
644
645 paint_profileStart(3,"ST");
646 paintStatusBars(g);
647 paint_profileEnd();
648
649 if (GameCanvas.PROFILE_ON_SCREEN) {
650 for (int p = 0; p < PAINT_PROFILE_TIMES_MAX; p++) {
651 g.setColor(0x000000);
652 g.drawString(paint_profile_labels[p] + " "
653 + paint_profile_times[p * 2 + 1], 2, 20 + 20 * p,
654 Graphics.LEFT | Graphics.TOP);
655 g.setColor(0xFFFFFF);
656 g.drawString(paint_profile_labels[p] + " "
657 + paint_profile_times[p * 2 + 1], 2 - 1,
658 20 + 20 * p - 1, Graphics.LEFT | Graphics.TOP);
659 }
660 }
661
662
663 break;
664 }
665 }
666
667 public void keyPressed(int key)
668 {
669 switch(key) {
670 case SOFTKEY_1:
671 menu_command_1 = true;
672 break;
673
674 case SOFTKEY_2:
675 menu_command_2 = true;
676 break;
677
678 // TODO: debug, temp, remove
679 case KEY_POUND:
680 god_mode = !god_mode;
681
682 // TODO: debug, temp, remove
683 Level.tiles_scroll_vy *= 2;
684
685 break;
686 case KEY_STAR:
687 PROFILE_ON_SCREEN = !PROFILE_ON_SCREEN;
688 break;
689 case KEY_NUM1:
690 command_up = true;
691 command_left = true;
692 break;
693 case KEY_NUM3:
694 command_up = true;
695 command_right = true;
696 break;
697 case KEY_NUM7:
698 command_down = true;
699 command_left = true;
700 break;
701 case KEY_NUM9:
702 command_down = true;
703 command_right = true;
704 break;
705 case KEY_NUM0:
706 if (player_nuke_counter == 0 && player_nukes > 0)
707 {
708 player_nukes--;
709 player_nuke_counter = PLAYER_NUKE_COUNTER_MAX;
710 }
711 break;
712 }
713
714 int game_action = getGameAction(key);
715
716 switch(game_action)
717 {
718 case UP:
719 command_up = true;
720 break;
721
722 case DOWN:
723 command_down = true;
724 break;
725
726 case LEFT:
727 command_left = true;
728 break;
729
730 case RIGHT:
731 command_right = true;
732 break;
733
734 case FIRE:
735 command_fire = true;
736 menu_command_1 = true;
737 break;
738 }
739 }
740
741 public void keyReleased(int key)
742 {
743 int game_action = getGameAction(key);
744
745 switch(key)
746 {
747 case KEY_NUM1:
748 command_up = false;
749 command_left = false;
750 break;
751 case KEY_NUM3:
752 command_up = false;
753 command_right = false;
754 break;
755 case KEY_NUM7:
756 command_down = false;
757 command_left = false;
758 break;
759 case KEY_NUM9:
760 command_down = false;
761 command_right = false;
762 break;
763 }
764
765 switch(game_action)
766 {
767 case UP:
768 command_up = false;
769 break;
770
771 case DOWN:
772 command_down = false;
773 break;
774
775 case LEFT:
776 command_left = false;
777 break;
778
779 case RIGHT:
780 command_right = false;
781 break;
782
783 case FIRE:
784 command_fire = false;
785 menu_command_1 = false;
786 break;
787 }
788 }
789
790 public void paintMainMenu(Graphics g)
791 {
792 // init font
793 g.setFont(Font.getFont(Font.FACE_MONOSPACE, Font.STYLE_BOLD, Font.SIZE_LARGE));
794 int menu_y = canvasHeight/2 - 45;
795 for(int i=0; i <= MENU_OPTION_MAIN_MAX; i++)
796 {
797 // paint the main menu options
798 if (i == menuSelectedOption) {
799 g.setColor(0xFFCC00);
800 } else {
801 g.setColor(0xFFFFFF);
802 }
803
804 String option_text = menuScreenMainOptions[i];
805
806 if (i == MENU_OPTION_MAIN_MUSIC_ON_OFF)
807 {
808 option_text = option_text + (musicOn ? TEXT_ON : TEXT_OFF);
809 }
810 g.drawString(option_text, canvasWidth/2, menu_y, Graphics.HCENTER | Graphics.TOP);
811
812 menu_y += 20;
813 }
814 g.setFont(Font.getFont(Font.FACE_MONOSPACE, Font.STYLE_BOLD, Font.SIZE_MEDIUM));
815 g.setColor(0xFFFFFF);
816 // paint softkey_1 label
817 g.drawString("Select", 2, canvasHeight-2, Graphics.LEFT | Graphics.BOTTOM);
818 // paint softkey_2 label
819 g.drawString("Exit", canvasWidth-2, canvasHeight-2, Graphics.RIGHT | Graphics.BOTTOM);
820 }
821
822 public void paintTextMenu(Graphics g, int screen)
823 {
824 g.setFont(Font.getFont(Font.FACE_MONOSPACE, Font.STYLE_BOLD, Font.SIZE_MEDIUM));
825 g.setColor(0xFFFFFF);
826 int text_line_y = 10;
827 for(int line=0; line < menuScreensTextLines[screen].length; line++)
828 {
829 // paint all the lines of text in this menu screen
830 g.drawString(menuScreensTextLines[screen][line], canvasWidth/2, text_line_y, Graphics.HCENTER | Graphics.TOP);
831
832 text_line_y += 15;
833 }
834 if (screen == MENU_SCR_TEXT_GAME_OVER || screen == MENU_SCR_TEXT_LEVEL_DONE || screen == MENU_SCR_TEXT_GAME_DONE)
835 {
836 // print score in the game over screen
837 text_line_y += 15;
838 g.drawString("Score: "+ player_score, canvasWidth/2, text_line_y, Graphics.HCENTER | Graphics.TOP);
839 }
840
841 g.setFont(Font.getFont(Font.FACE_MONOSPACE, Font.STYLE_BOLD, Font.SIZE_MEDIUM));
842
843 if (menuScreen == MENU_SCR_PAUSE_GAME )
844 {
845 // paint softkey_1 label
846 g.drawString("Resume", 1, canvasHeight-1, Graphics.LEFT | Graphics.BOTTOM);
847 // paint softkey_2 label
848 g.drawString("End game", canvasWidth-1, canvasHeight-1, Graphics.RIGHT | Graphics.BOTTOM);
849 }
850 else
851 {
852 // paint softkey_1 label
853 g.drawString("Ok", 1, canvasHeight-1, Graphics.LEFT | Graphics.BOTTOM);
854 }
855 }
856
857 public static void paintHiscoreView(Graphics g)
858 {
859 g.setColor(0xFFFFFF);
860
861 g.drawString("Highscore!", canvasWidth/2, 2, Graphics.HCENTER | Graphics.TOP);
862
863 for (int i=0; i < MAX_SCORES; i++)
864 {
865 g.drawString(hiNames[i], canvasWidth*1/4, 20 + 20*i, Graphics.LEFT | Graphics.TOP);
866 g.drawString(""+hiScores[i], canvasWidth*3/4, 20 + 20*i, Graphics.RIGHT | Graphics.TOP);
867 }
868
869 // paint softkey_1 label
870 g.drawString("Ok", 1, canvasHeight-1, Graphics.LEFT | Graphics.BOTTOM);
871 }
872 public static void paintHiscoreEnter(Graphics g)
873 {
874 g.setColor(0xFFFFFF);
875
876 g.drawString("Enter name:", canvasWidth/2, canvasHeight*1/4, Graphics.HCENTER | Graphics.BOTTOM);
877
878 for (int i=0; i < MAX_HI_NAME_LENGTH; i++)
879 {
880 if (i == hi_name_enter_char_pos)
881 g.setColor(0xFFFF00);
882 else
883 g.setColor(0xFFFFFF);
884 g.drawChar(hi_name_enter[i], canvasWidth/2 - 12 + 12*i, canvasHeight*2/4, Graphics.HCENTER | Graphics.BOTTOM);
885 }
886
887 g.setColor(0xFFFFFF);
888 g.drawString("Score: "+ player_score, canvasWidth/2, canvasHeight*3/4, Graphics.HCENTER | Graphics.BOTTOM);
889
890 // paint softkey_1 label
891 g.drawString("Save", 1, canvasHeight-1, Graphics.LEFT | Graphics.BOTTOM);
892 }
893
894 public static void paintSprite(Graphics g, GameSprite spr)
895 {
896 // set the clip window
897 int dx = (spr.x - Level.tiles_scroll_x) >> GameSprite.SHIFT_FACTOR;
898 int dy = (spr.y - Level.tiles_scroll_y) >> GameSprite.SHIFT_FACTOR;
899 int cx = dx;
900 int cy = dy;
901 int cw = GameSprite.spriteImageFrameWidths[spr.imageId];
902 int ch = spr.h;
903
904 // check clip window coordinates, if out of screen
905 if (cx < 0)
906 {
907 cw += cx;
908 cx = 0;
909 }
910 if (cy < 0)
911 {
912 ch += cy;
913 cy = 0;
914 }
915
916 if (spr.spriteId == Level.SPRITE_ID_BOSS2)
917 {
918 // paint boss2 left wing
919 int dxw = dx + (BOSS2_OPEN_COUNTER_MAX - boss2_open_counter);
920 int cxw = dxw;
921 int cww = BOSS2_WING_WIDTH;
922
923 g.setClip(cxw, cy, cww, ch);
924 g.drawImage(spriteImages[spr.imageId], dxw, dy, Graphics.TOP | Graphics.LEFT);
925
926 // paint boss2 rightt wing
927 dxw = dx - (BOSS2_OPEN_COUNTER_MAX - boss2_open_counter);
928 cxw = dx + GameSprite.spriteImageFrameWidths[spr.imageId] - BOSS2_WING_WIDTH - (BOSS2_OPEN_COUNTER_MAX - boss2_open_counter);
929
930 g.setClip(cxw, cy, cww, ch);
931 g.drawImage(spriteImages[spr.imageId], dxw, dy, Graphics.TOP | Graphics.LEFT);
932
933 // paint boss2 body
934 int cwb = cw - BOSS2_WING_WIDTH*2;
935 int cxb = cx + BOSS2_WING_WIDTH;
936
937 g.setClip(cxb, cy, cwb, ch);
938 g.drawImage(spriteImages[spr.imageId], dx, dy, Graphics.TOP | Graphics.LEFT);
939
940 } else {
941
942 g.setClip(cx, cy, cw, ch);
943
944 // calculate the x coordinate of where to start the image paint, so our frame will be seen through the clip window
945 dx = dx - spr.frameId * GameSprite.spriteImageFrameWidths[spr.imageId];
946
947 // paint the sprites image at the calculated coordinates
948 g.drawImage(spriteImages[spr.imageId], dx, dy, Graphics.TOP | Graphics.LEFT);
949
950 }
951 // restore the clip window
952 g.setClip(0, 0, canvasWidth, canvasHeight);
953 }
954
955 public static void paintStatusBars(Graphics g)
956 {
957 int max_energy_bar_width = canvasWidth/2;
958
959 // paint the energy bar
960 g.setColor(0xFFFFFF);
961 g.drawRect(5, 5, max_energy_bar_width + 1, 4);
962 g.setColor(0xFFCC00);
963 g.fillRect(6, 6, player.energy * max_energy_bar_width / player.energyMax, 3);
964
965 if ( activate_boss_energy_bar)
966 {
967 // paint the energy bar
968 g.setColor(0xFFFFFF);
969 g.drawRect(5, 12, max_energy_bar_width + 1, 4);
970 g.setColor(0xFF9900);
971 g.fillRect(6, 13, Level.boss.energy * max_energy_bar_width / Level.boss.energyMax, 3);
972 }
973
974 // paint the score count
975 g.setColor(0xFFFFFF);
976 g.setFont(Font.getFont(Font.FACE_MONOSPACE, Font.STYLE_PLAIN, Font.SIZE_SMALL));
977 g.drawString(""+player_score, canvasWidth-4, 8, Graphics.TOP | Graphics.RIGHT);
978
979 // paint number of nukes
980 for (int i=0; i < PLAYER_NUKES_MAX; i++)
981 {
982 g.setColor(0xFFFF00);
983 int x = canvasWidth - (3-i) * (5+3);
984 int y = canvasHeight - 8;
985 if ((3-i) <= player_nukes)
986 {
987 g.fillRect(x,y,5,5);
988 }
989 g.setColor(0xFFFFFF);
990 g.drawRect(x,y,5,5);
991 }
992
993 if (PROFILE_ON_SCREEN)
994 {
995 // paint the FPS
996 g.setColor(0xFFFFFF);
997 g.setFont(Font.getFont(Font.FACE_MONOSPACE, Font.STYLE_PLAIN, Font.SIZE_SMALL));
998 g.drawString("FPS: "+FPS, 1, canvasHeight-2, Graphics.BOTTOM | Graphics.LEFT);
999 }
1000 }
1001
1002 public void startGame(byte load_level_no)
1003 {
1004 player_gameover_explosion_counter = -1;
1005 player_nuke_counter = 0;
1006
1007 activate_boss_energy_bar = false;
1008 boss_dead_explosion_counter = -1;
1009
1010 // initialize the explosion sprite list
1011 explosionSprites = new Vector();
1012
1013 // initialize the player fire list
1014 playerFireSprites = new Vector();
1015
1016 // initialize the enemy fire list
1017 enemyFireSprites = new Vector();
1018
1019 // init level tiles position to horizontal center and vertical bottom
1020 Level.tiles_scroll_x = (Level.LEVEL_WIDTH * Level.TILE_SIZE / 2 - canvasWidth /2) << GameSprite.SHIFT_FACTOR;
1021 Level.tiles_scroll_y = (Level.LEVEL_HEIGHT * Level.TILE_SIZE - canvasHeight - 1) << GameSprite.SHIFT_FACTOR;
1022
1023 Level.loadLevel( load_level_no );
1024
1025 // switch to game mode
1026 mode = MODE_GAME;
1027
1028 boss2_open_counter = 0;
1029 boss2_open_dir = 1;
1030 boss2_closed_counter = BOSS2_CLOSED_COUNTER_MAX;
1031 boss2_stay_open_counter = -1;
1032
1033 }
1034
1035 public static final byte GAME_END_NONE = 0;
1036 public static final byte GAME_END_LOSE = 1;
1037 public static final byte GAME_END_WIN = 2;
1038
1039 public void checkEndGame()
1040 {
1041 byte game_end = GAME_END_NONE;
1042 // check for game end
1043 if ( (player.energy <= 0) && (player_gameover_explosion_counter == 0) )
1044 {
1045 game_end = GAME_END_LOSE;
1046 } else if ( (Level.boss.energy <= 0) && (boss_dead_explosion_counter == 0) )
1047 {
1048 game_end = GAME_END_WIN;
1049 }
1050 if (game_end > GAME_END_NONE)
1051 {
1052 // back to menu mode, set the game or lever over text screen
1053 mode = MODE_MENU;
1054 if (game_end == GAME_END_LOSE)
1055 {
1056 menuScreen = MENU_SCR_TEXT_GAME_OVER;
1057 } else {
1058 if (Level.levelno == Level.MAX_LEVELS)
1059 menuScreen = MENU_SCR_TEXT_GAME_DONE;
1060 else
1061 menuScreen = MENU_SCR_TEXT_LEVEL_DONE;
1062 }
1063
1064 // clean up
1065 player = null;
1066 Level.enemySprites = null;
1067 }
1068 }
1069
1070 public void updateEnemySprites()
1071 {
1072 // move enemy sprites
1073 int i = Level.enemySprites.size()-1;
1074 while (i >= 0)
1075 {
1076 GameSprite enemy = (GameSprite) Level.enemySprites.elementAt(i);
1077
1078 if ( (enemy.y - Level.tiles_scroll_y > (canvasHeight << GameSprite.SHIFT_FACTOR)) )
1079 {
1080 // remove out-of-screen enemies
1081 Level.enemySprites.removeElementAt(i);
1082 i--;
1083 }
1084 else
1085 {
1086 //System.out.println(enemy.y +"-"+ Level.tiles_scroll_y +">"+ Level.ENEMY_AI_ACTIVATION_ZONE_Y);
1087 // do the AI move, shoot and other tricks
1088 if ( enemy.y - Level.tiles_scroll_y > (Level.ENEMY_AI_ACTIVATION_ZONE_Y << GameSprite.SHIFT_FACTOR) )
1089 {
1090 enemy.visible = true;
1091 doEnemyAI( enemy );
1092 i--;
1093 } else {
1094 enemy.visible = false;
1095 // optimization
1096 // but enemySprites must be ordered by the sprites" y coordinate !
1097 // they are in this case, because of loadLevel
1098 break;
1099 }
1100 }
1101 }
1102 if (boss_dead_explosion_counter > 0)
1103 {
1104 boss_dead_explosion_counter--;
1105 }
1106 }
1107
1108 public void doEnemyAI( GameSprite sprite )
1109 {
1110
1111 // System.out.println("doing enemy ai for "+sprite.spriteId);
1112
1113 switch (sprite.spriteId)
1114 {
1115 case Level.SPRITE_ID_ENEMY1:
1116 break;
1117 case Level.SPRITE_ID_ENEMY1_SHOOTING:
1118
1119 shootEnemyFire(sprite, Level.SPRITE_ID_FIRE2, (byte) 1);
1120
1121 break;
1122 case Level.SPRITE_ID_ENEMY2:
1123 break;
1124 case Level.SPRITE_ID_ENEMY2_SHOOTING:
1125
1126 shootEnemyFire(sprite, Level.SPRITE_ID_FIRE2, (byte) 1);
1127
1128 break;
1129 case Level.SPRITE_ID_ENEMY4_SHOOTING:
1130
1131 shootEnemyFireTowardPlayer(sprite, Level.SPRITE_ID_FIRE3, (byte) 2);
1132
1133 break;
1134 case Level.SPRITE_ID_ENEMY3:
1135 case Level.SPRITE_ID_ENEMY3_SINUS:
1136 case Level.SPRITE_ID_ENEMY3_SINUS_SHOOTING:
1137
1138 // animate sprite to next sprite frame every 3 frames
1139 if (global_frame_counter % 3 == 0)
1140 sprite.frameId = (byte) ( (sprite.frameId + 1) % (sprite.frameIdMax+1));
1141
1142 // sinusoidal movement
1143 if (sprite.spriteId == Level.SPRITE_ID_ENEMY3_SINUS || sprite.spriteId == Level.SPRITE_ID_ENEMY3_SINUS_SHOOTING)
1144 {
1145 int tx = Math.abs( (sprite.y >> 1) % 360);
1146 sprite.vx = sin[tx] >> 4;
1147
1148 // int ty = Math.abs(global_frame_counter % 360);
1149 // sprite.vy = - sin[ty] >> 5;
1150 }
1151 if (sprite.spriteId == Level.SPRITE_ID_ENEMY3_SINUS)
1152 {
1153 shootEnemyFireTowardPlayer(sprite, Level.SPRITE_ID_FIRE3, (byte) 2);
1154 }
1155
1156 break;
1157 case Level.SPRITE_ID_BOSS1:
1158
1159 activate_boss_energy_bar = true;
1160
1161 // boss fire
1162 if (sprite.fire_cooldown_frames_counter <= 0)
1163 {
1164 GameSprite new_fire;
1165
1166 new_fire = new GameSprite(Level.SPRITE_ID_FIRE2);
1167 new_fire.frameId = 1;
1168 new_fire.x = sprite.x + ((sprite.w - 5 - new_fire.w) << GameSprite.SHIFT_FACTOR);
1169 new_fire.y = sprite.y + (sprite.h << GameSprite.SHIFT_FACTOR);
1170 new_fire.vx = 0;
1171 new_fire.vy = sprite.vy + FIRE_VELOCITY/2;
1172 enemyFireSprites.addElement(new_fire);
1173
1174 new_fire = new GameSprite(Level.SPRITE_ID_FIRE2);
1175 new_fire.frameId = 1;
1176 new_fire.x = sprite.x + (5 << GameSprite.SHIFT_FACTOR);
1177 new_fire.y = sprite.y + (sprite.h << GameSprite.SHIFT_FACTOR);
1178 new_fire.vx = 0;
1179 new_fire.vy = sprite.vy + FIRE_VELOCITY/2;
1180 enemyFireSprites.addElement(new_fire);
1181
1182 new_fire = new GameSprite(Level.SPRITE_ID_FIRE3);
1183 new_fire.frameId = 2;
1184 new_fire.x = sprite.x + ((sprite.w / 2 - new_fire.w / 2) << GameSprite.SHIFT_FACTOR);
1185 new_fire.y = sprite.y + (sprite.h << GameSprite.SHIFT_FACTOR);
1186 new_fire.vx = 0;
1187 new_fire.vy = sprite.vy + FIRE_VELOCITY;
1188 enemyFireSprites.addElement(new_fire);
1189
1190 sprite.fire_cooldown_frames_counter = ENEMY_FIRE_COOLDOWN_WAIT_FRAMES;
1191 } else {
1192 // extra cooldown, boss only
1193 sprite.fire_cooldown_frames_counter--;
1194 }
1195
1196 // boss move
1197 if (sprite.x + (sprite.w << GameSprite.SHIFT_FACTOR) > (((Level.LEVEL_WIDTH * Level.TILE_SIZE) - (Level.TILE_SIZE/2)) << GameSprite.SHIFT_FACTOR) )
1198 sprite.vx = -(1 << GameSprite.SHIFT_FACTOR);
1199 if (sprite.x < (Level.TILE_SIZE/2 << GameSprite.SHIFT_FACTOR) )
1200 sprite.vx = 1 << GameSprite.SHIFT_FACTOR;
1201
1202 break;
1203
1204 case Level.SPRITE_ID_BOSS2:
1205
1206 activate_boss_energy_bar = true;
1207
1208 // boss move
1209 if (sprite.x + (sprite.w << GameSprite.SHIFT_FACTOR) > (((Level.LEVEL_WIDTH * Level.TILE_SIZE) - (Level.TILE_SIZE/2)) << GameSprite.SHIFT_FACTOR) )
1210 {
1211 sprite.x = (((Level.LEVEL_WIDTH * Level.TILE_SIZE) - (Level.TILE_SIZE/2)) << GameSprite.SHIFT_FACTOR) - (sprite.w << GameSprite.SHIFT_FACTOR);
1212 sprite.vx = -(1 << GameSprite.SHIFT_FACTOR);
1213 }
1214 if (sprite.x < (Level.TILE_SIZE/2 << GameSprite.SHIFT_FACTOR) )
1215 {
1216 sprite.x = (Level.TILE_SIZE/2 << GameSprite.SHIFT_FACTOR);
1217 sprite.vx = 1 << GameSprite.SHIFT_FACTOR;
1218 }
1219
1220 int ty = Math.abs(sprite.x % 360);
1221 sprite.vy = (- sin[ty]) >> 5;
1222
1223 if ( (sprite.y < ((canvasHeight-Level.TILE_SIZE*5) << GameSprite.SHIFT_FACTOR)) && ((sprite.vx < 0 && sprite.x > player.x) || (sprite.vx > 0 && sprite.x < player.x)) && (sprite.y < player.y) )
1224 sprite.y += 2 << GameSprite.SHIFT_FACTOR;
1225 if ( (sprite.y > (Level.TILE_SIZE << GameSprite.SHIFT_FACTOR)) && ((sprite.vx < 0 && sprite.x < player.x) || (sprite.vx > 0 && sprite.x > player.x) || sprite.y > player.y))
1226 sprite.y -= 1 << GameSprite.SHIFT_FACTOR;
1227
1228 if (global_frame_counter % 50 == 0)
1229 {
1230 shootEnemyFireTowardPlayer(sprite, Level.SPRITE_ID_FIRE3, (byte) 2);
1231 sprite.fire_cooldown_frames_counter = 0;
1232 }
1233
1234 // waiting closed
1235 if (boss2_closed_counter > 0)
1236 boss2_closed_counter--;
1237
1238 // start opening
1239 if (boss2_closed_counter == 0)
1240 {
1241 boss2_closed_counter--;
1242 boss2_open_counter = 0;
1243 boss2_open_dir = 1;
1244 }
1245
1246 // opening
1247 if ( (boss2_closed_counter == -1) && (boss2_open_dir > 0) && (boss2_open_counter < BOSS2_OPEN_COUNTER_MAX) &&
1248 boss2_stay_open_counter == -1)
1249 {
1250 boss2_open_counter = (byte) (boss2_open_counter + boss2_open_dir);
1251 }
1252
1253 // closing
1254 if ( (boss2_closed_counter == -1) && (boss2_open_dir < 0) && (boss2_open_counter > 0) &&
1255 boss2_stay_open_counter == -1)
1256 {
1257 boss2_open_counter = (byte) (boss2_open_counter + boss2_open_dir);
1258 }
1259
1260 // stay open for a while
1261 if (boss2_open_counter == BOSS2_OPEN_COUNTER_MAX && boss2_stay_open_counter == -1)
1262 {
1263 boss2_stay_open_counter = BOSS2_STAY_OPEN_COUNTER_MAX;
1264 }
1265 if ( boss2_stay_open_counter > 0)
1266 {
1267 boss2_stay_open_counter--;
1268 }
1269 // start closing
1270 if ( boss2_stay_open_counter == 0)
1271 {
1272 boss2_open_dir = -1;
1273 boss2_stay_open_counter--;
1274 }
1275
1276 if (boss2_open_dir == -1 && boss2_stay_open_counter == -1 && boss2_open_counter == 0)
1277 {
1278 boss2_open_counter = 0;
1279
1280 boss2_closed_counter = BOSS2_CLOSED_COUNTER_MAX;
1281 boss2_open_dir = 0;
1282 }
1283
1284 if (boss2_open_counter == BOSS2_OPEN_COUNTER_MAX
1285 && boss2_stay_open_counter < BOSS2_STAY_OPEN_COUNTER_MAX/2
1286 && sprite.fire_cooldown_frames_counter == 0)
1287 {
1288 GameSprite new_fire;
1289 new_fire = new GameSprite(Level.SPRITE_ID_FIRE2);
1290 new_fire.frameId = 1;
1291 new_fire.x = sprite.x + ((sprite.w - 5 - new_fire.w) << GameSprite.SHIFT_FACTOR);
1292 new_fire.y = sprite.y + (sprite.h << GameSprite.SHIFT_FACTOR);
1293 new_fire.vx = 0;
1294 new_fire.vy = sprite.vy + FIRE_VELOCITY/2;
1295 enemyFireSprites.addElement(new_fire);
1296
1297 new_fire = new GameSprite(Level.SPRITE_ID_FIRE2);
1298 new_fire.frameId = 1;
1299 new_fire.x = sprite.x + (5 << GameSprite.SHIFT_FACTOR);
1300 new_fire.y = sprite.y + (sprite.h << GameSprite.SHIFT_FACTOR);
1301 new_fire.vx = 0;
1302 new_fire.vy = sprite.vy + FIRE_VELOCITY/2;
1303 enemyFireSprites.addElement(new_fire);
1304
1305 sprite.fire_cooldown_frames_counter = 4;
1306 }
1307
1308 break;
1309 }
1310
1311 // move the sprite
1312 sprite.x += sprite.vx;
1313 sprite.y += sprite.vy;
1314
1315 if (sprite.fire_cooldown_frames_counter > 0)
1316 sprite.fire_cooldown_frames_counter--;
1317 }
1318
1319 public GameSprite shootEnemyFire(GameSprite sprite, byte fire_sprite_id, byte fire_sprite_frame_id)
1320 {
1321 GameSprite new_fire = null;
1322 if (enemyFireSprites.size() < MAX_ENEMY_FIRE && sprite.fire_cooldown_frames_counter <= 0)
1323 {
1324 // System.out.println("adding fire sprite ... ");
1325 new_fire = new GameSprite(fire_sprite_id);
1326 new_fire.frameId = fire_sprite_frame_id;
1327 new_fire.x = sprite.x + (sprite.w / 2 << GameSprite.SHIFT_FACTOR);
1328 new_fire.y = sprite.y + (sprite.h << GameSprite.SHIFT_FACTOR);
1329 new_fire.vx = 0;
1330 new_fire.vy = sprite.vy + FIRE_VELOCITY/2;
1331 enemyFireSprites.addElement(new_fire);
1332
1333 sprite.fire_cooldown_frames_counter = ENEMY_FIRE_COOLDOWN_WAIT_FRAMES;
1334
1335 }
1336 return new_fire;
1337 }
1338 public GameSprite shootEnemyFireTowardPlayer(GameSprite sprite, byte fire_sprite_id, byte fire_sprite_frame_id)
1339 {
1340 GameSprite new_fire = shootEnemyFire(sprite, fire_sprite_id, fire_sprite_frame_id);
1341 if (new_fire != null)
1342 {
1343 int pdx = player.x - new_fire.x;
1344 int pdy = player.y - new_fire.y;
1345
1346 // we dont have a tangens function (yet!), so for now this will do...
1347 int approx_dist = Math.abs(pdx) + Math.abs(pdy);
1348 int approx_velocity_step = approx_dist / 40;
1349 new_fire.vx = pdx / approx_velocity_step;
1350 new_fire.vy = pdy / approx_velocity_step;
1351 }
1352
1353 return new_fire;
1354 }
1355
1356 public void updatePlayerSprite()
1357 {
1358 // nuke
1359 if (player_nuke_counter == PLAYER_NUKE_COUNTER_MAX)
1360 {
1361 // activate nuke, destroy all visible enemy sprites
1362 int i = Level.enemySprites.size() - 1;
1363 while (i >= 0 )
1364 {
1365 GameSprite enemy = (GameSprite) Level.enemySprites.elementAt(i);
1366
1367 if ( enemy.visible )
1368 {
1369 System.out.println("destroying opponent: "+i+" / "+Level.enemySprites.size());
1370 enemy.energy = 0;
1371 addExplosion(enemy);
1372 }
1373 i--;
1374 }
1375 }
1376 if (player_nuke_counter > 0)
1377 {
1378 player_nuke_counter--;
1379 }
1380
1381 // move player with level scroll
1382 player.y += Level.tiles_scroll_vy;
1383
1384 if (player_gameover_explosion_counter > 0)
1385 {
1386 player_gameover_explosion_counter--;
1387 }
1388 else
1389 {
1390 if (player.energy <= 0)
1391 {
1392 player_gameover_explosion_counter = PLAYER_GAMEOVER_EXPLOSION_COUNTER_MAX;
1393 }
1394
1395 // move player sprite on command, and check player <-> screen edge collision
1396 if (command_up)
1397 {
1398 player.y -= player.vy;
1399 if (player.y < Level.tiles_scroll_y)
1400 player.y = Level.tiles_scroll_y;
1401 }
1402 if (command_down)
1403 {
1404 player.y += player.vy;
1405 // level edge collision
1406 if (player.y - Level.tiles_scroll_y > ((canvasHeight - player.h) << GameSprite.SHIFT_FACTOR))
1407 player.y = Level.tiles_scroll_y + ((canvasHeight - player.h) << GameSprite.SHIFT_FACTOR);
1408 }
1409 player.frameId = SPRITE_FRAME_PLAYER_CENTER;
1410 if (command_left)
1411 {
1412 player.x -= player.vx;
1413 if (player.x < Level.tiles_scroll_x)
1414 player.x = Level.tiles_scroll_x;
1415 player.frameId = SPRITE_FRAME_PLAYER_LEFT;
1416 }
1417 if (command_right)
1418 {
1419 player.x += player.vx;
1420 if (player.x - Level.tiles_scroll_x > ((canvasWidth - player.w) << GameSprite.SHIFT_FACTOR))
1421 player.x = Level.tiles_scroll_x + ((canvasWidth - player.w) << GameSprite.SHIFT_FACTOR);
1422 player.frameId = SPRITE_FRAME_PLAYER_RIGHT;
1423 }
1424 if (command_fire)
1425 {
1426 if (player.fire_cooldown_frames_counter <= 0)
1427 {
1428 GameSprite fire = new GameSprite(Level.SPRITE_ID_FIRE1);
1429 fire.x = player.x + ((player.w/2 - fire.w/2) << GameSprite.SHIFT_FACTOR);
1430 fire.y = player.y;
1431 fire.vy = -FIRE_VELOCITY + Level.tiles_scroll_vy;
1432 playerFireSprites.addElement(fire);
1433
1434 player.fire_cooldown_frames_counter = PLAYER_FIRE_COOLDOWN_WAIT_FRAMES;
1435 }
1436 }
1437 if (player.fire_cooldown_frames_counter > 0)
1438 player.fire_cooldown_frames_counter--;
1439 if ( god_mode )
1440 player.fire_cooldown_frames_counter -= 2;
1441 }
1442 }
1443
1444 public void doCollision()
1445 {
1446 int i = Level.enemySprites.size() - 1;
1447 while (i >= 0 )
1448 {
1449 GameSprite enemy = (GameSprite) Level.enemySprites.elementAt(i);
1450
1451 if ( enemy.visible )
1452 {
1453 // check player <-> enemy collision
1454 if (player.collidesWith(enemy))
1455 {
1456 // add explosion
1457 addExplosion(enemy);
1458 addExplosion(player);
1459
1460 // player loses energy
1461 if ( !god_mode )
1462 player.energy -= enemy.damage;
1463
1464 // level boss does not take damage from colliding with player
1465 if (enemy != Level.boss)
1466 enemy.energy -= player.damage;
1467
1468 }
1469
1470 // check the player fire sprites <-> enemy collision
1471 int j = playerFireSprites.size() - 1;
1472 while (j >= 0)
1473 {
1474 GameSprite fire = (GameSprite) playerFireSprites.elementAt(j);
1475 if (fire.collidesWith(enemy))
1476 {
1477 if (enemy.spriteId != Level.SPRITE_ID_BOSS2 || (boss2_open_counter > 0))
1478 {
1479 enemy.energy -= fire.damage;
1480
1481 // add explosions
1482 addExplosion(enemy);
1483 }
1484 // destroy player fire
1485 playerFireSprites.removeElementAt(j);
1486 j--;
1487 }
1488 j--;
1489 }
1490 // remove dead enemies
1491 if (enemy.energy <= 0)
1492 {
1493 Level.enemySprites.removeElementAt(i);
1494 i--;
1495 // increase player score
1496 player_score += enemy.score;
1497
1498 // if boss sprite
1499 if (enemy.spriteId == Level.SPRITE_ID_BOSS1 || enemy.spriteId == Level.SPRITE_ID_BOSS2)
1500 {
1501 boss_dead_explosion_counter = BOSS_DEAD_EXPLOSION_COUNTER_MAX;
1502 }
1503 }
1504 }
1505 i--;
1506 }
1507 // check the enemy fire sprites <-> player collision
1508 int j = enemyFireSprites.size() - 1;
1509 while (j >= 0)
1510 {
1511 GameSprite fire = (GameSprite) enemyFireSprites.elementAt(j);
1512 if (fire.collidesWith(player))
1513 {
1514 if ( !god_mode ) {
1515 player.energy -= fire.damage;
1516 System.out.println("player.energy "+ player.energy);
1517 }
1518
1519 // add explosions
1520 addExplosion(player);
1521
1522 // destroy enemy fire
1523 enemyFireSprites.removeElementAt(j);
1524 j--;
1525 }
1526 j--;
1527 }
1528 }
1529
1530 public void updatePlayerFireSprites()
1531 {
1532 for (int i=0; i < playerFireSprites.size(); i++)
1533 {
1534 GameSprite fire = (GameSprite) playerFireSprites.elementAt(i);
1535 fire.y += fire.vy;
1536
1537 // destroy player fire when out of screen
1538 if ( (fire.y + (fire.h << GameSprite.SHIFT_FACTOR)) < Level.tiles_scroll_y)
1539 {
1540 playerFireSprites.removeElementAt(i);
1541 // only increase counter, if not removed from list
1542 i--;
1543 }
1544 }
1545 }
1546
1547 public void updateEnemyFireSprites()
1548 {
1549 for (int i=0; i < enemyFireSprites.size(); i++)
1550 {
1551 // System.out.println("updating enemy fire sprite...");
1552
1553 GameSprite fire = (GameSprite) enemyFireSprites.elementAt(i);
1554 fire.x += fire.vx;
1555 fire.y += fire.vy;
1556
1557 // destroy enemy fire when out of screen
1558 if ( fire.y > Level.tiles_scroll_y + (canvasHeight << GameSprite.SHIFT_FACTOR))
1559 {
1560 enemyFireSprites.removeElementAt(i);
1561 // only increase counter, if not removed from list
1562 i--;
1563 }
1564 }
1565 }
1566
1567 public void updateExplosionSprites()
1568 {
1569 for (int i=0; i < explosionSprites.size(); i++)
1570 {
1571 GameSprite exp = (GameSprite) explosionSprites.elementAt(i);
1572
1573 // animate explosion!
1574 exp.frameId++;
1575
1576 if (exp.frameId > exp.frameIdMax)
1577 {
1578 explosionSprites.removeElementAt(i);
1579 // only increase counter, if not removed from list
1580 i--;
1581 }
1582 }
1583 // create a big bada boom player gameover explosion
1584 // 7 - max explosion frames
1585 // 2 - add an explosion every 2nd frame
1586 if (player_gameover_explosion_counter > 7 &&
1587 player_gameover_explosion_counter % 2 == 0
1588 )
1589 {
1590 GameSprite new_exp = addExplosion(player);
1591 new_exp.x += random.nextInt() % (player.w << GameSprite.SHIFT_FACTOR);
1592 new_exp.y += random.nextInt() % (player.h << GameSprite.SHIFT_FACTOR);
1593 }
1594 // create a big bada boom boss gameover explosion
1595 // 7 - max explosion frames
1596 // 2 - add an explosion every 2nd frame
1597 if (boss_dead_explosion_counter > 7 &&
1598 boss_dead_explosion_counter % 2 == 0
1599 )
1600 {
1601 GameSprite new_exp = addExplosion(Level.boss);
1602 new_exp.x += random.nextInt() % (Level.boss.w << GameSprite.SHIFT_FACTOR);
1603 new_exp.y += random.nextInt() % (Level.boss.h << GameSprite.SHIFT_FACTOR);
1604 }
1605 }
1606
1607 public static void loadImages()
1608 {
1609 try {
1610
1611 spriteImages = new Image[SPRITE_IMAGE_MAX];
1612 spriteImages[SPRITE_IMAGE_PLAYER] = Image.createImage("/p.png");
1613 spriteImages[SPRITE_IMAGE_FIRE] = Image.createImage("/fire.png");
1614 spriteImages[SPRITE_IMAGE_BOSS1] = Image.createImage("/boss1.png");
1615 spriteImages[SPRITE_IMAGE_BOSS2] = Image.createImage("/boss2.png");
1616 spriteImages[SPRITE_IMAGE_ENEMY1] = Image.createImage("/e1.png");
1617 spriteImages[SPRITE_IMAGE_ENEMY2] = Image.createImage("/e2.png");
1618 spriteImages[SPRITE_IMAGE_ENEMY3] = Image.createImage("/e3.png");
1619 spriteImages[SPRITE_IMAGE_ENEMY4] = Image.createImage("/e4.png");
1620 spriteImages[SPRITE_IMAGE_EXPLOSION] = Image.createImage("/exp.png");
1621
1622 tileImage = Image.createImage("/t04.png");
1623
1624 imgBackground = Image.createImage("/bg.png");
1625
1626 } catch (Exception e) {};
1627 }
1628
1629 public static GameSprite addExplosion(GameSprite exp_spr)
1630 {
1631 // add explosion
1632 GameSprite exp = new GameSprite(Level.SPRITE_ID_EXPLOSION);
1633 // center the explosion position on the exploded sprite
1634 exp.x = exp_spr.x + ((exp_spr.w/2 - exp.w/2) << GameSprite.SHIFT_FACTOR);
1635 exp.y = exp_spr.y + ((exp_spr.h/2 - exp.h/2) << GameSprite.SHIFT_FACTOR);
1636 exp.vx = exp_spr.vx;
1637 exp.vy = exp_spr.vy;
1638 explosionSprites.addElement(exp);
1639
1640 if (exp_spr.energy > 0)
1641 exp.frameId = GameSprite.EXPLOSION_FRAME_SMOKE_START;
1642
1643 return exp;
1644 }
1645
1646 public void hideNotify()
1647 {
1648 // hideNotify method of Canvas is called on external events, which hide the Canvas
1649 // this method can be used to handle such events (battery warnings, incoming phone calls...)
1650 if (mode == MODE_GAME)
1651 {
1652 mode = MODE_MENU;
1653 menuScreen = MENU_SCR_PAUSE_GAME;
1654 stopMusic();
1655 }
1656 }
1657
1658 public void showNotify()
1659 {
1660 if (musicOn)
1661 playMusic();
1662 }
1663
1664
1665 // loads the highscores from RMS - the internal phone database
1666 // also saves default scores, if no scores exist.
1667 public static void loadHighscoreFromRMS()
1668 {
1669 try
1670 {
1671 db = RecordStore.openRecordStore(SCORE_DB_NAME, true);
1672
1673 byte[] score_data = null;
1674 // initialize scores
1675 if (db.getNumRecords() == 0)
1676 {
1677 // add record with initial default scores
1678 score_data = getHighscoreData();
1679 db.addRecord(score_data, 0, score_data.length);
1680 }
1681 // load highscore
1682 score_data = db.getRecord(1);
1683 ByteArrayInputStream in = new ByteArrayInputStream(score_data);
1684 DataInputStream dis = new DataInputStream(in);
1685 for (byte i = 0; i < MAX_SCORES; i++)
1686 {
1687 hiScores[i] = dis.readInt();
1688 hiNames[i] = dis.readUTF();
1689 }
1690 dis.close();
1691 in.close();
1692 db.closeRecordStore();
1693 dis = null;
1694 in = null;
1695 db = null;
1696 // System.gc();
1697 } catch (Exception ex) {
1698 ex.printStackTrace();
1699 }
1700 }
1701
1702 // prepares data in a byte array, ready for saving to a RMS record
1703 public static byte[] getHighscoreData() throws IOException
1704 {
1705 ByteArrayOutputStream out = new ByteArrayOutputStream();
1706 DataOutputStream dos = new DataOutputStream(out);
1707 for (byte i = 0; i < MAX_SCORES; i++)
1708 {
1709 dos.writeInt(hiScores[i]);
1710 dos.writeUTF(hiNames[i]);
1711 }
1712 dos.close();
1713 out.close();
1714
1715 return out.toByteArray();
1716 }
1717
1718 // saves highscores to RMS
1719 public static void saveHighscores()
1720 {
1721 try
1722 {
1723 db = RecordStore.openRecordStore(SCORE_DB_NAME, true);
1724
1725 byte[] data = getHighscoreData();
1726
1727 db.setRecord(1, data, 0, data.length);
1728 db.closeRecordStore();
1729 } catch (Exception ex) {
1730 ex.printStackTrace();
1731 }
1732 }
1733
1734 // returns -1 if not qualified, highscore position otherwise
1735 public static int isQualified(int score)
1736 {
1737 for (byte i = 0; i < MAX_SCORES; i++)
1738 if (hiScores[i] < score)
1739 return i;
1740 return -1;
1741 }
1742
1743 // add a score to the scores array and to the rms
1744 public static void addHighscore(int score, String name)
1745 {
1746 int i = isQualified(score);
1747
1748 // move others down 1 place
1749 for (byte k = (byte) (MAX_SCORES - 1); k > i; k--) {
1750 hiScores[k] = hiScores[k - 1];
1751 hiNames[k] = hiNames[k - 1];
1752 }
1753 // insert score
1754 hiScores[i] = score;
1755 hiNames[i] = name;
1756
1757 saveHighscores();
1758 return;
1759 }
1760
1761 public static final short SIN_FACTOR = 1024;
1762 public static final short[] sin = {
1763 0, 18, 36, 54, 71, 89, 107, 125, 143, 160,
1764 178, 195, 213, 230, 248, 265, 282, 299, 316, 333,
1765 350, 367, 384, 400, 416, 433, 449, 465, 481, 496,
1766 512, 527, 543, 558, 573, 587, 602, 616, 630, 644,
1767 658, 672, 685, 698, 711, 724, 737, 749, 761, 773,
1768 784, 796, 807, 818, 828, 839, 849, 859, 868, 878,
1769 887, 896, 904, 912, 920, 928, 935, 943, 949, 956,
1770 962, 968, 974, 979, 984, 989, 994, 998, 1002, 1005,
1771 1008, 1011, 1014, 1016, 1018, 1020, 1022, 1023, 1023, 1024,
1772 1024, 1024, 1023, 1023, 1022, 1020, 1018, 1016, 1014, 1011,
1773 1008, 1005, 1002, 998, 994, 989, 984, 979, 974, 968,
1774 962, 956, 949, 943, 935, 928, 920, 912, 904, 896,
1775 887, 878, 868, 859, 849, 839, 828, 818, 807, 796,
1776 784, 773, 761, 749, 737, 724, 711, 698, 685, 672,
1777 658, 644, 630, 616, 602, 587, 573, 558, 543, 527,
1778 512, 496, 481, 465, 449, 433, 416, 400, 384, 367,
1779 350, 333, 316, 299, 282, 265, 248, 230, 213, 195,
1780 178, 160, 143, 125, 107, 89, 71, 54, 36, 18,
1781 0, -18, -36, -54, -71, -89, -107, -125, -143, -160,
1782 -178, -195, -213, -230, -248, -265, -282, -299, -316, -333,
1783 -350, -367, -384, -400, -416, -433, -449, -465, -481, -496,
1784 -512, -527, -543, -558, -573, -587, -602, -616, -630, -644,
1785 -658, -672, -685, -698, -711, -724, -737, -749, -761, -773,
1786 -784, -796, -807, -818, -828, -839, -849, -859, -868, -878,
1787 -887, -896, -904, -912, -920, -928, -935, -943, -949, -956,
1788 -962, -968, -974, -979, -984, -989, -994, -998, -1002, -1005,
1789 -1008, -1011, -1014, -1016, -1018, -1020, -1022, -1023, -1023, -1024,
1790 -1024, -1024, -1023, -1023, -1022, -1020, -1018, -1016, -1014, -1011,
1791 -1008, -1005, -1002, -998, -994, -989, -984, -979, -974, -968,
1792 -962, -956, -949, -943, -935, -928, -920, -912, -904, -896,
1793 -887, -878, -868, -859, -849, -839, -828, -818, -807, -796,
1794 -784, -773, -761, -749, -737, -724, -711, -698, -685, -672,
1795 -658, -644, -630, -616, -602, -587, -573, -558, -543, -527,
1796 -512, -496, -481, -465, -449, -433, -416, -400, -384, -367,
1797 -350, -333, -316, -299, -282, -265, -248, -230, -213, -195,
1798 -178, -160, -143, -125, -107, -89, -54, -36, -18, 0
1799 };
1800
1801
1802 public static boolean musicOn = true;
1803 // public static javax.microedition.media.Player musicPlayer;
1804
1805 public static void loadMusic()
1806 {
1807 // InputStream inputstream;
1808 //
1809 // try
1810 // {
1811 // inputstream = new Object().getClass().getResourceAsStream("/music.mid");
1812 // musicPlayer = Manager.createPlayer(inputstream, "audio/midi");
1813 // musicPlayer.realize();
1814 // musicPlayer.setLoopCount(-1);
1815 // }
1816 // catch (Exception e) {
1817 // }
1818 }
1819
1820 public static void playMusic()
1821 {
1822 // try
1823 // {
1824 // VolumeControl volume = (VolumeControl) musicPlayer
1825 // .getControl("javax.microedition.media.control.VolumeControl");
1826 // volume.setLevel(20);
1827 //
1828 // musicPlayer.realize();
1829 // musicPlayer.start();
1830 // }
1831 // catch (Exception e) {
1832 // }
1833 }
1834
1835 public static void stopMusic() {
1836 // try
1837 // {
1838 // if (musicPlayer != null) {
1839 // musicPlayer.stop();
1840 // musicPlayer.setMediaTime(-1);
1841 // }
1842 // }
1843 // catch (Exception e) {
1844 // }
1845 //
1846 }
1847
1848 }
1849