pluotsorbet/tests/asteroids/Field.java

470 строки
15 KiB
Java

/**
* Copyright 2001-2002 Jean-Francois Doue
*
* This file is part of Asteroid Zone. Asteroid Zone is free software;
* you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation;
* either version 2 of the License, or (at your option) any later version.
* Asteroid Zone is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with Asteroid Zone; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307 USA
*/
package asteroids;
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
import java.util.*;
/**
* The game main screen. The class also implements the game's main loop.
* @author Jean-Francois Doue
* @author Pavel Machek
* @version 1.4, 2002/10/14
*/
public class Field extends Canvas implements Runnable {
/**
* The 'title' game state. Displays the game name and author.
*/
public static final byte TITLE_STATE = 0;
/**
* The 'control' game state. Displays the mapping between keys and game commands.
*/
public static final byte CONTROL_STATE = 1;
/**
* The 'points' game state. Displays the value of each screen items.
*/
public static final byte POINTS_STATE = 2;
/**
* The 'high score' game state. Displays the high score table.
*/
public static final byte HISCORE_STATE = 3;
/**
* The 'game over' game state. Displays the 'game over' message.
*/
public static final byte GAMEOVER_STATE = 4;
/**
* The 'new high score' game state. Lets players enter their name.
*/
public static final byte NEWHIGHSCORE_STATE = 5;
/**
* The 'game' game state. The player is actively playing the game.
*/
public static final byte GAME_STATE = 6;
/**
* When not in game mode, the game switches its state every TIMEOUT ms
*/
public static final long TIMEOUT = 8000L;
public static Font smallFont = Font.getFont(Font.FACE_PROPORTIONAL, Font.STYLE_PLAIN, Font.SIZE_SMALL);
public static Font bigFont = Font.getFont(Font.FACE_PROPORTIONAL, Font.STYLE_BOLD, Font.SIZE_LARGE);
/**
* Time spent moving objects and computing collisions.
*/
//public static long computeavg;
/**
* Time spent painting screens.
*/
//public static long paintavg;
/**
* Number of game frames since beginning.
*/
//public static long frames;
private byte _state; // The current game state
private Slideshow _nextSlide; // A TimeTask used to trigger the transition to the next state.
private int _lives; // Number of lives left.
private int _nextBonusLife; // Score above which next extra life will be awarded.
private int _score; // Current score.
private byte _level; // Current level.
private boolean _paused; // Set the true by the Game if it wants to enter its paused state
// This causes the main game thread to exit.
private boolean _frozen; // Set when another Displayable is being displayed by the game
// (nothing happens in the game in between, but the game
// must be able to resume).
private Image _buffer; // To implement double-buffering on platforms which do not have it.
private char[] _scoreString;
private char _liveString;
private char[] _levelString // The current level indicator
= {'L', 'e', 'v', 'e', 'l', ' ', '0'};
private byte _levelStringTimer; // The number of frames during which the level indicator is displayed
private int _liveStringWidth; // In pixels.
private int _lastKeyPressed; // The last key pressed by the player.
private boolean _isRepeatedKey; // True if _lastKeyPressed was obtained through keyRepeated().
public Field() {
//computeavg = 0;
//paintavg = 0;
//frames = 0;
// Determine the dimensions of the Canvas.
// The game has been developed using the j2mewtk's
// DefaultColorPhone, using a resolution of 100x96.
// Object are sized proportionaly on other platforms
// ratioDenom.
Mobile.width = getWidth();
Mobile.height = getHeight();
Mobile.ratioNum = Math.min(Mobile.width, Mobile.height);
// The score is stored as a char array for greater
// efficiency.
_scoreString = new char[4];
// For device which do not support double buffering,
// create an offscreen image for double buffering.
if (!isDoubleBuffered()) {
_buffer = Image.createImage(Mobile.width, Mobile.height);
}
}
/**
* Starts a new game.
*/
public final void newGame() {
_level = 0;
setLives(4);
_nextBonusLife = 500;
setScore(0);
Asteroid.asteroids.removeAll();
_lastKeyPressed = 0;
_isRepeatedKey = false;
Ship.ship.reset();
}
/**
* Returns the number of remaining ships
*/
public final int getLives() {
return _lives;
}
/**
* Sets the number of remaining ships
*/
public final void setLives(int lives) {
if (lives < 0) {
lives = 0;
}
_lives = lives;
_liveString = (char)('0' + _lives);
_liveStringWidth = smallFont.charWidth(_liveString);
}
/**
* Returns the current score.
*/
public final int getScore() {
return _score;
}
/**
* Updates the score.
*/
public final void setScore(int score) {
_score = score;
// Convert the score to an array of chars
// of the form: 0000
Scores.toCharArray(_score, _scoreString);
// Extra lives are awarded every 500 points.
if (_score >= _nextBonusLife) {
_nextBonusLife += 500;
setLives(_lives + 1);
}
}
/**
* Initializes a new level.
*/
private final void _nextLevel() {
// At most 7 large asteroids per screen.
if (_level < 5) {
_level++;
}
// Populate the game level with large asteroids.
for (byte i = 0; i < 3 + _level; i++) {
Asteroid asteroid = (Asteroid)Asteroid.asteroids.addNewObject();
if (asteroid != null) {
Asteroid.randomInit(asteroid);
}
}
// Clear all the existing rockets or explosions and reset
// the ship.
Rocket.rockets.removeAll();
Explosion.explosions.removeAll();
if (_level >= 1) {
_levelString[6] = (char)('0' + _level);
_levelStringTimer = 20;
}
}
/**
* Overriden from Canvas.
*/
protected void paint(Graphics g) {
Graphics gr = (_buffer != null) ? _buffer.getGraphics() : g;
gr.setColor(0x00FFFFFF);;
gr.fillRect(0, 0, getWidth() - 1, getHeight() - 1);
gr.setColor(0x00000000);
gr.drawRect(0, 0, getWidth() - 1, getHeight() - 1);
// Draw the asteroids
gr.setColor(0x000000FF);
Asteroid.draw(gr);
// Draw the explosions
gr.setColor(0x00FF00FF);
Explosion.draw(gr);
// Draw the rockets
gr.setColor(0x00FF0000);
Rocket.draw(gr);
switch(_state) {
case TITLE_STATE:
Slideshow.drawTitleScreen(gr);
break;
case CONTROL_STATE:
Slideshow.drawControlScreen(gr);
break;
case POINTS_STATE:
Slideshow.drawPointsScreen(gr);
break;
case HISCORE_STATE:
Slideshow.drawHighScoresScreen(gr);
break;
case GAME_STATE:
gr.setColor(0x00000000);
//gr.drawRect(10, 10, 16, 16);
gr.setFont(smallFont);
gr.drawChars(_scoreString, 0, 4, 2, getHeight() - 1, Graphics.BOTTOM|Graphics.LEFT);
gr.drawChar(_liveString, Mobile.width - _liveStringWidth - 1, Mobile.height, Graphics.BOTTOM|Graphics.LEFT);
Ship.draw(0, Mobile.width - Ship.radius - 2 - _liveStringWidth, Mobile.height - Ship.radius - 1, gr);
// Display the level indicator (only during the first few frames of the level)
if (_levelStringTimer > 0) {
_levelStringTimer--;
int x = (Mobile.width - Field.smallFont.charsWidth(_levelString, 0, _levelString.length)) >> 1;
int y = ((Mobile.height) >> 1) + Field.smallFont.getHeight();
gr.drawChars(_levelString, 0, _levelString.length, x, y, Graphics.BOTTOM|Graphics.LEFT);
}
Ship.ship.draw(gr);
break;
case GAMEOVER_STATE:
Slideshow.drawGameOverScreen(gr);
break;
}
if (_buffer != null) {
g.drawImage(_buffer, 0, 0, Graphics.TOP|Graphics.LEFT);
}
}
/**
* Overriden from Canvas.
*/
protected void keyPressed(int keyCode) {
_lastKeyPressed = keyCode;
}
protected void keyRepeated(int keyCode) {
_lastKeyPressed = keyCode;
_isRepeatedKey = true;
}
/**
* Returns the current state.
*/
public final byte getState() {
return _state;
}
/**
* Triggers a transition from one state of the game automaton
* to the other.
*/
public final void setState(byte newState) {
switch (newState) {
case TITLE_STATE:
_nextSlide = new Slideshow(CONTROL_STATE);
Game.timer.schedule(_nextSlide, TIMEOUT);
break;
case CONTROL_STATE:
_nextSlide = new Slideshow(POINTS_STATE);
Game.timer.schedule(_nextSlide, TIMEOUT);
break;
case POINTS_STATE:
_nextSlide = new Slideshow(HISCORE_STATE);
Game.timer.schedule(_nextSlide, TIMEOUT);
break;
case HISCORE_STATE:
_nextSlide = new Slideshow(TITLE_STATE);
Game.timer.schedule(_nextSlide, TIMEOUT);
break;
case GAMEOVER_STATE:
if (Game.scores.isHighScore(_score)) {
_nextSlide = new Slideshow(NEWHIGHSCORE_STATE);
Game.timer.schedule(_nextSlide, TIMEOUT / 2);
} else {
_nextSlide = new Slideshow(HISCORE_STATE);
Game.timer.schedule(_nextSlide, TIMEOUT);
}
break;
case NEWHIGHSCORE_STATE:
setFrozen(true);
if (Game.display.getCurrent() != Game.scoreForm) {
Game.enterHighScore();
}
break;
case GAME_STATE:
if (_nextSlide != null) {
_nextSlide.cancel();
_nextSlide = null;
}
break;
}
_state = newState;
}
/**
* Asks the game's main thread to exit.
*/
public final void pause() {
_paused = true;
}
/**
*
*/
public final void setFrozen(boolean frozen) {
_frozen = frozen;
if (_frozen) {
if (_nextSlide != null) {
_nextSlide.cancel();
_nextSlide = null;
}
}
}
/**
* Executes the game's main loop.
*/
public void run() {
try {
while (!_paused) {
long time1 = System.currentTimeMillis();
//long time2;
long time3;
long ellapsed = 0;
if (!_frozen) {
// If the field has been cleared, change the level.
if (Asteroid.asteroids.size() == 0) {
_nextLevel();
}
// Animate the explosion and remove those which are done.
Explosion.explode();
// Move the asteroids.
Asteroid.move();
// Move the rockets and remove those which have expired.
Rocket.move();
// Handle user events
// Move the ship (only while the game is actually proceeding)
if (_state == GAME_STATE) {
if (_lastKeyPressed != 0) {
int gameAction = getGameAction(_lastKeyPressed);
if (gameAction != 0) {
switch(gameAction) {
case LEFT:
Ship.ship.rotate(-2);
break;
case RIGHT:
Ship.ship.rotate(2);
break;
case FIRE:
Ship.ship.shoot(Rocket.rockets);
break;
case GAME_A:
case UP:
Ship.ship.burn();
break;
case GAME_B:
if (!_isRepeatedKey) {
Ship.ship.teleport();
}
break;
}
}
_lastKeyPressed = 0;
_isRepeatedKey = false;
}
Ship.ship.move();
}
// Compute collisions between the asteroids and
// the ship and the rockets.
Asteroid.collisionDetection();
// Detect game over
if ((_state == GAME_STATE) && (_lives <= 0) && (Ship.ship.isAlive)) {
setState(GAMEOVER_STATE);
}
// Determine the time spent to compute the frame.
//time2 = System.currentTimeMillis();
//computeavg += (time2 - time1);
// Force a screen refresh.
repaint();
serviceRepaints();
// Determine the time spent to draw the frame.
time3 = System.currentTimeMillis();
//paintavg += (time3 - time2);
//frames++;
// Determine the total time for the frame.
ellapsed = time3 - time1;
}
// Sleep for a while (at least 20ms)
try {
Thread.currentThread().sleep(Math.max(50 - ellapsed, 20));
} catch(java.lang.InterruptedException e) {
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}