pluotsorbet/tests/asteroids/Asteroid.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);
}
}
}
}