package games.fighter.billMuenstermanJouster.util;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.geom.Point2D;
import util.Location;
import util.Pixmap;
import util.Vector;
/**
* This class represents a shape that moves on its own.
*
* Note, Sprite is a technical term:
* http://en.wikipedia.org/wiki/Sprite_(computer_graphics)
*
* @author Robert C. Duvall
*/
public abstract class Sprite {
// canonical directions for a collision
public static final int RIGHT_DIRECTION = 0;
public static final int UP_DIRECTION = 270;
public static final int LEFT_DIRECTION = 180;
public static final int DOWN_DIRECTION = 90;
// state
private Location myCenter;
private Vector myVelocity;
private Dimension mySize;
private Pixmap myView;
// keep copies of the original state so shape can be reset as needed
private Location myOriginalCenter;
private Vector myOriginalVelocity;
private Dimension myOriginalSize;
private Pixmap myOriginalView;
// cached for efficiency
private Rectangle myBounds;
/**
* Create a shape at the given position, with the given size.
*/
public Sprite (Pixmap image, Location center, Dimension size) {
this(image, center, size, new Vector());
}
/**
* Create a shape at the given position, with the given size, velocity, and color.
*/
public Sprite (Pixmap image, Location center, Dimension size, Vector velocity) {
// make copies just to be sure no one else has access
myOriginalCenter = new Location(center);
myOriginalSize = new Dimension(size);
myOriginalVelocity = new Vector(velocity);
myOriginalView = new Pixmap(image);
reset();
resetBounds();
}
/**
* Describes how to "animate" the shape by changing its state.
*
* Currently, moves by the current velocity.
*/
public void update (double elapsedTime, Dimension bounds) {
// do not change original velocity
Vector v = new Vector(myVelocity);
v.scale(elapsedTime);
translate(v);
}
/**
* Moves shape's center by given vector.
*/
public void translate (Vector v) {
myCenter.translate(v);
resetBounds();
}
/**
* Resets shape's center.
*/
public void setCenter (double x, double y) {
myCenter.setLocation(x, y);
resetBounds();
}
/**
* Returns shape's x coordinate in pixels.
*/
public double getX () {
return myCenter.getX();
}
/**
* Returns shape's y-coordinate in pixels.
*/
public double getY () {
return myCenter.getY();
}
/**
* Returns shape's left-most coordinate in pixels.
*/
public double getLeft () {
return myCenter.getX() - mySize.width / 2;
}
/**
* Returns shape's top-most coordinate in pixels.
*/
public double getTop () {
return myCenter.getY() - mySize.height / 2;
}
/**
* Returns shape's right-most coordinate in pixels.
*/
public double getRight () {
return myCenter.getX() + mySize.width / 2;
}
/**
* Returns shape's bottom-most coordinate in pixels.
*/
public double getBottom () {
return myCenter.getY() + mySize.height / 2;
}
/**
* Returns shape's width in pixels.
*/
public double getWidth () {
return mySize.getWidth();
}
/**
* Returns shape's height in pixels.
*/
public double getHeight () {
return mySize.getHeight();
}
/**
* Scales shape's size by the given factors.
*/
public void scale (double widthFactor, double heightFactor) {
mySize.setSize(mySize.width * widthFactor, mySize.height * heightFactor);
resetBounds();
}
/**
* Resets shape's size.
*/
public void setSize (int width, int height) {
mySize.setSize(width, height);
resetBounds();
}
/**
* Returns shape's velocity.
*/
public Vector getVelocity () {
return myVelocity;
}
/**
* Resets shape's velocity.
*/
public void setVelocity (double angle, double magnitude) {
myVelocity = new Vector(angle, magnitude);
}
/**
* Resets shape's image.
*/
public void setView (Pixmap image) {
if (image != null) {
myView = image;
}
}
/**
* Returns rectangle that encloses this shape.
*/
public Rectangle getBounds () {
return myBounds;
}
/**
* Returns true if the given point is within a rectangle representing this shape.
*/
public boolean intersects (Sprite other) {
return getBounds().intersects(other.getBounds());
}
/**
* Returns true if the given point is within a rectangle representing this shape.
*/
public boolean intersects (Point2D pt) {
return getBounds().contains(pt);
}
/**
* Reset shape back to its original values.
*/
public void reset () {
myCenter = new Location(myOriginalCenter);
mySize = new Dimension(myOriginalSize);
myVelocity = new Vector(myOriginalVelocity);
myView = new Pixmap(myOriginalView);
}
public Location getOriginalCenterLocation() {
return new Location(myOriginalCenter);
}
/**
* Display this shape on the screen.
*/
public void paint (Graphics2D pen)
{
myView.paint(pen, myCenter, mySize);
}
/**
* Returns rectangle that encloses this shape.
*/
protected void resetBounds () {
myBounds = new Rectangle((int)getLeft(), (int)getTop(), mySize.width, mySize.height);
}
/**
* Returns approximate direction from center of rectangle to side which was hit or
* NaN if no hit took place. I updated this function in order to work properly, using vectors
* from the center of the sprite to both its contacting edge and the contacting edge of the bounds
*/
protected double getHitDirection (Rectangle bounds) {
// double angle = Vector.angleBetween(myCenter, new Location(bounds.getCenterX(), bounds.getCenterY()));
// BUGBUG: FIX ME --- this is very imperfect, but sort of works for now
Location BottomRightCorner = new Location(getRight(), getBottom());
Location BottomLeftCorner = new Location(getLeft(), getBottom());
Location TopRightCorner = new Location(getRight(), getTop());
Location TopLeftCorner = new Location(getLeft(), getTop());
if (bounds.contains(BottomRightCorner)) {
if(DifferenceInAnglesFromCenterToCorners(BottomRightCorner, new Location(bounds.getMinX(), bounds.getMinY())) >= 0
&& DifferenceInAnglesFromCenterToCorners(BottomRightCorner, new Location(bounds.getMinX(), bounds.getMinY())) <= -180)
{
return LEFT_DIRECTION;
}
else
{
return UP_DIRECTION;
}
}
else if (bounds.contains(BottomLeftCorner)) {
if(DifferenceInAnglesFromCenterToCorners(BottomLeftCorner,new Location(bounds.getMaxX(), bounds.getMinY()))
>= 0)
{
return UP_DIRECTION;
}
else
{
return RIGHT_DIRECTION;
}
}
else if (bounds.contains(TopLeftCorner)) {
if(DifferenceInAnglesFromCenterToCorners(TopLeftCorner,
new Location(bounds.getMinX(), bounds.getMaxY())) >= 0)
{
return DOWN_DIRECTION;
}
else
{
return RIGHT_DIRECTION;
}
}
else if (bounds.contains(TopRightCorner)) {
if(DifferenceInAnglesFromCenterToCorners(TopRightCorner,
new Location(bounds.getMaxX(), bounds.getMaxY())) >= 0
&& DifferenceInAnglesFromCenterToCorners(TopRightCorner,
new Location(bounds.getMaxX(), bounds.getMaxY())) <= 180)
{
return LEFT_DIRECTION;
}
else
{
return DOWN_DIRECTION;
}
}
return 0;
//return Double.NaN;
}
/**
* Determines the difference of the angles of the vector from the center of the sprite to its
* relevant corner and the vector from the center to the other corner's intersecting corner
*/
private double DifferenceInAnglesFromCenterToCorners(Location RelevantCorner,
Location IntersectingCorner)
{
double AngleFromCenterToRelevantCorner;
double AngleFromCenterToIntersectingCorner;
AngleFromCenterToRelevantCorner = Vector.angleBetween(myCenter,
RelevantCorner);
AngleFromCenterToIntersectingCorner = Vector.angleBetween(myCenter,
IntersectingCorner);
return Vector.angleBetween(AngleFromCenterToRelevantCorner,
AngleFromCenterToIntersectingCorner);
}
public boolean HitTop(Rectangle bounds){
return UP_DIRECTION == getHitDirection(bounds);
}
public boolean HitRight(Rectangle bounds){
return RIGHT_DIRECTION == getHitDirection(bounds);
}
public boolean HitLeft(Rectangle bounds){
return LEFT_DIRECTION == getHitDirection(bounds);
}
public boolean HitBottom(Rectangle bounds){
return DOWN_DIRECTION == getHitDirection(bounds);
}
}