зеркало из https://github.com/mozilla/pluotsorbet.git
338 строки
11 KiB
Java
338 строки
11 KiB
Java
/**
|
|
* Copyright 2001 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.lcdui.*;
|
|
import java.util.*;
|
|
|
|
/**
|
|
* Class to implement an asteroid. Asteroids come in three sizes.
|
|
* @author Jean-Francois Doue
|
|
* @version 1.2, 2001/10/24
|
|
*/
|
|
public class Asteroid extends Mobile {
|
|
/**
|
|
* A size constant for small asteroids.
|
|
*/
|
|
public static final byte SIZE_SMALL = 0;
|
|
|
|
/**
|
|
* A size constant for medium asteroids.
|
|
*/
|
|
public static final byte SIZE_MEDIUM = 1;
|
|
|
|
/**
|
|
* A size constant for large asteroids.
|
|
*/
|
|
public static final byte SIZE_LARGE = 2;
|
|
|
|
/**
|
|
* The x coordinates of the asteroid's vertices
|
|
*/
|
|
public static byte[][] xcoords;
|
|
|
|
/**
|
|
* The y coordinates of the asteroid's vertices
|
|
*/
|
|
public static byte[][] ycoords;
|
|
|
|
/**
|
|
* The radius of the asteroid.
|
|
*/
|
|
public static byte[] radii;
|
|
|
|
/**
|
|
* The diameter of the asteroid.
|
|
*/
|
|
public static int[] diameters;
|
|
|
|
|
|
/**
|
|
* Scan converted asteroids used for collision detection.
|
|
*/
|
|
public static boolean[][][] masks;
|
|
|
|
/**
|
|
* The asteroids currently existing in the game.
|
|
*/
|
|
public static Pool asteroids;
|
|
|
|
private byte _angle; // Orientation of the asteroid.
|
|
private byte _size; // Small, medium or large.
|
|
private static byte[] _value = {5, 2, 1}; // Points earned when shot.
|
|
private static final byte _FIELD_TOP = 0;
|
|
private static final byte _FIELD_BOTTOM = 1;
|
|
private static final byte _FIELD_LEFT = 2;
|
|
private static final byte _FIELD_RIGHT = 3;
|
|
|
|
|
|
static {
|
|
// Create and populate the asteroid pool.
|
|
Asteroid[] array = new Asteroid[20];
|
|
for (int i = array.length - 1; i >= 0; i--) {
|
|
array[i] = new Asteroid();
|
|
}
|
|
asteroids = new Pool(array);
|
|
|
|
final int[] allRadii = {3, 5, 8};
|
|
final byte[][] allAngles = {
|
|
{3, 11, 19, 25, 3}, // small
|
|
{2, 7, 17, 19, 26, 2}, // medium
|
|
{1, 4, 9, 15, 21, 23, 29, 1} // large
|
|
};
|
|
radii = new byte[3];
|
|
diameters = new int[3];
|
|
xcoords = new byte[3][];
|
|
ycoords = new byte[3][];
|
|
masks = new boolean[3][][];
|
|
for (int i = SIZE_SMALL; i <= SIZE_LARGE; i++) {
|
|
// Precompute the asteroid vertices.
|
|
radii[i] = (byte)(allRadii[i] * ratioNum / ratioDenom);
|
|
byte[] angles = allAngles[i];
|
|
byte[] pointx = new byte[angles.length];
|
|
xcoords[i] = pointx;
|
|
byte[] pointy = new byte[angles.length];
|
|
ycoords[i] = pointy;
|
|
for (int j = angles.length - 1; j >= 0; j--) {
|
|
pointx[j] = (byte)((radii[i] * Mobile.cos[angles[j]]) >> 6);
|
|
pointy[j] = (byte)((radii[i] * Mobile.sin[angles[j]]) >> 6);
|
|
}
|
|
|
|
// Scan convert the asteroid polygon.
|
|
diameters[i] = radii[i] << 1;
|
|
masks[i] = new boolean[diameters[i]][];
|
|
for (int j = 0; j < diameters[i]; j++) {
|
|
masks[i][j] = new boolean[diameters[i]];
|
|
}
|
|
Geometry.scanConvertPolygon(masks[i], pointx, pointy);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Initializes an Asteroid instance to make it large, positionned
|
|
* on a screen boundary and going in the direction of opposite boundary.
|
|
*/
|
|
public static void randomInit(Asteroid asteroid) {
|
|
int x = 0, y = 0;
|
|
byte angle = 0;
|
|
switch(Math.abs(Game.random.nextInt()) % 4) {
|
|
case _FIELD_TOP:
|
|
y = 1;
|
|
x = Math.abs(Game.random.nextInt()) % width;
|
|
angle = (byte)(15 + Math.abs(Game.random.nextInt()) % 15);
|
|
break;
|
|
case _FIELD_LEFT:
|
|
x = 1;
|
|
y = Math.abs(Game.random.nextInt()) % height;
|
|
angle = (byte)((23 + Math.abs(Game.random.nextInt()) % 15) % 32);
|
|
break;
|
|
case _FIELD_RIGHT:
|
|
x = width - 1;
|
|
y = Math.abs(Game.random.nextInt()) % height;
|
|
angle = (byte)(7 + Math.abs(Game.random.nextInt()) % 15);
|
|
break;
|
|
case _FIELD_BOTTOM:
|
|
y = height - 1;
|
|
x = Math.abs(Game.random.nextInt()) % width;
|
|
angle = (byte)(Math.abs(Game.random.nextInt()) % 15);
|
|
break;
|
|
}
|
|
asteroid.init(x, y, angle, SIZE_LARGE);
|
|
}
|
|
|
|
/**
|
|
* Initializes a Asteroid instance by setting its position, angle
|
|
* and size.
|
|
*/
|
|
public final void init(int x, int y, byte angle, byte size) {
|
|
_angle = angle;
|
|
_size = size;
|
|
moveTo(x, y);
|
|
setVelocity(cos[angle] << 2, sin[angle] << 2);
|
|
}
|
|
|
|
public Asteroid() {
|
|
}
|
|
|
|
/**
|
|
* Move the asteroid to its next position.
|
|
*/
|
|
public static final void move() {
|
|
for (int i = 0; i < asteroids.count; i++) {
|
|
Asteroid a = (Asteroid)asteroids.pool[i];
|
|
a._x += a.vx;
|
|
a._y += a.vy;
|
|
a.x = a._x >> 8;
|
|
a.y = a._y >> 8;
|
|
|
|
// If a border has been hit, wrap the trajectory around
|
|
// the screen. The new origin is the projection of the
|
|
// intersection point on the opposite border.
|
|
if (a.x <= 0) {
|
|
a.moveTo(width - 2, a.y);
|
|
} else if (a.x >= width - 1) {
|
|
a.moveTo(1, a.y);
|
|
} else if (a.y <= 0) {
|
|
a.moveTo(a.x, height - 2);
|
|
} else if (a.y >= height - 1) {
|
|
a.moveTo(a.x, 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Compute collisions between the asteroids and
|
|
* the ship and the rockets.
|
|
*/
|
|
public static final void collisionDetection() {
|
|
Ship ship = Ship.ship;
|
|
Field field = Game.field;
|
|
|
|
// Collision detection makes sense only while the game is
|
|
// being played and the ship is alive.
|
|
if ((field != null) && (field.getState() == Field.GAME_STATE) && (ship.isAlive)) {
|
|
|
|
int score = field.getScore();
|
|
int newScore = score;
|
|
Pool rockets = Rocket.rockets;
|
|
Pool explosions = Explosion.explosions;
|
|
|
|
for (asteroids.current = asteroids.count - 1; asteroids.current >= 0;) {
|
|
Asteroid a = (Asteroid)asteroids.pool[asteroids.current--];
|
|
|
|
// Detect ship - asteroid collisions.
|
|
int offset = ship.angle << 2;
|
|
for (int i = 0; i < 4; i++) {
|
|
int sx = ship.x + Ship.xcoords[offset + i] - a.x + radii[a._size];
|
|
if ((sx >= 0) && (sx < diameters[a._size])) {
|
|
int sy = ship.y + Ship.ycoords[offset + i] - a.y + radii[a._size];
|
|
if ((sy >= 0) && (sy < diameters[a._size])) {
|
|
|
|
// Use the mask for collision detection
|
|
if (masks[a._size][sx][sy]) {
|
|
int lives = field.getLives();
|
|
if ((lives > 0) && (ship.isAlive)) {
|
|
ship.explode();
|
|
field.setLives(lives - 1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// Detect asteroid - rocket collisions.
|
|
for (rockets.current = rockets.count - 1; rockets.current >= 0;) {
|
|
Rocket r = (Rocket)rockets.pool[rockets.current--];
|
|
|
|
// Transform the r into asteroid coordinates.
|
|
int rx = r.x - a.x + radii[a._size];
|
|
if ((rx >= 0) && (rx < diameters[a._size])) {
|
|
int ry = r.y - a.y + radii[a._size];
|
|
if ((ry >= 0) && (ry < diameters[a._size])) {
|
|
|
|
// Use the mask for collision detection
|
|
if (masks[a._size][rx][ry]) {
|
|
asteroids.removeCurrent();
|
|
rockets.removeCurrent();
|
|
newScore += _value[a._size];
|
|
a.split(r);
|
|
Explosion explosion = (Explosion)explosions.addNewObject();
|
|
if (explosion != null) {
|
|
explosion.init(r.x, r.y);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (newScore != score) {
|
|
field.setScore(newScore);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Draws an asteroid of the specified size and location
|
|
* in the specified graphic context.
|
|
*/
|
|
public static final void draw(byte size, int xpos, int ypos, Graphics g) {
|
|
byte[] xcoord = xcoords[size];
|
|
byte[] ycoord = ycoords[size];
|
|
for (int i = 0; i < xcoord.length - 1; i++) {
|
|
g.drawLine(xcoord[i] + xpos, ycoord[i] + ypos, xcoord[i + 1] + xpos, ycoord[i + 1] + ypos);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Draws all the asteroids of the supplied object pool using the
|
|
* specified graphic context.
|
|
*/
|
|
static public final void draw(Graphics g) {
|
|
for (int i = 0; i < asteroids.count; i++) {
|
|
Asteroid a = (Asteroid)asteroids.pool[i];
|
|
byte[] xcoord = xcoords[a._size];
|
|
byte[] ycoord = ycoords[a._size];
|
|
for (int j = 0; j < xcoord.length - 1; j++) {
|
|
g.drawLine(
|
|
xcoord[j] + a.x,
|
|
ycoord[j] + a.y,
|
|
xcoord[j + 1] + a.x,
|
|
ycoord[j + 1] + a.y);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Splits the asteroid in two smaller pieces and set their
|
|
* direction depending on the asteroid direction and the hitting
|
|
* rocket direction. The pieces are added to the supplied collection.
|
|
*/
|
|
public final void split(Rocket rocket) {
|
|
if (_size >= SIZE_MEDIUM) {
|
|
byte newSize = (byte)(_size - 1);
|
|
int angle = (cos[_angle] * sin[rocket.angle] - cos[rocket.angle] * sin[_angle]) >> 10;
|
|
byte angle1 = (byte)(_angle + angle + Math.abs(Game.random.nextInt()) % 5);
|
|
if (angle1 < 0) {
|
|
angle1 += 32;
|
|
}
|
|
if (angle1 >= 32) {
|
|
angle1 -= 32;
|
|
}
|
|
byte angle2 = (byte)(_angle + angle - Math.abs(Game.random.nextInt()) % 5);
|
|
if (angle2 < 0) {
|
|
angle2 += 32;
|
|
}
|
|
if (angle2 >= 32) {
|
|
angle2 -= 32;
|
|
}
|
|
Asteroid a = (Asteroid)asteroids.addNewObject();
|
|
if (a != null) {
|
|
a.init(x, y, angle1, newSize);
|
|
}
|
|
a = (Asteroid)asteroids.addNewObject();
|
|
if (a != null) {
|
|
a.init(x, y, angle2, newSize);
|
|
}
|
|
}
|
|
}
|
|
}
|