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