/**
*
*/
package de.unisiegen.tpml.graphics.pong;
import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Point;
import java.awt.Toolkit;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.util.Random;
import java.util.Timer;
import java.util.TimerTask;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JWindow;
/**
* no comments for this stuff
* @author marcell
* @author Michael
*
*/
@SuppressWarnings("all")
public class Pong extends JComponent {
/**
*
*/
private static final long serialVersionUID = 6688203129068640177L;
private static final int WINPOINTS = 5;
private class Bat {
public float height;
public float width;
public float position;
public float oldPosition;
public float movementOldPos;
public int points;
public Bat (float height, float width, float position) {
this.height = height;
this.width = width;
this.position = position;
this.oldPosition = position;
this.points = 0;
}
public void save () {
this.oldPosition = this.position;
}
public void saveMovement() {
this.movementOldPos = this.position;
}
public void setPosition(float position) {
if (position < (this.height / 2f)) {
position = this.height / 2f;
}
else if (position > (1f - this.height / 2f)) {
position = (1f - this.height / 2f);
}
this.position = position;
}
}
private class Ball {
public float posX;
public float posY;
public float oldPosX;
public float oldPosY;
public float directionX;
public float directionY;
public float speed;
public float radius;
public Ball () {
this.directionX = -1.0f / (float)Math.sqrt (2);
this.radius = 0.02f;
}
public void move () {
this.posX += this.directionX * this.speed;
this.posY += this.directionY * this.speed;
}
public void save () {
this.oldPosX = this.posX;
this.oldPosY = this.posY;
}
public void restart(Bat pcBat) {
this.posX = 0.9f - this.radius;
this.posY = pcBat.position;
this.oldPosX = this.posX;
this.oldPosY = this.posY;
this.speed = 0.015f;
this.directionX = -this.directionX;
this.directionY = 1.0f / (float)Math.sqrt (2);
}
}
private class NPC {
public float maxSpeed;
public float speed;
public float dest;
public Bat bat;
public void act () {
if (this.bat.position > this.dest) {
this.bat.setPosition(this.bat.position - this.speed);
if (this.bat.position < this.dest) {
this.bat.setPosition(this.dest);
}
}
if (this.bat.position < this.dest) {
this.bat.setPosition(this.bat.position + this.speed);
if (this.bat.position > this.dest) {
this.bat.setPosition(this.dest);
}
}
}
}
private Bat pcBat;
private Bat npcBat;
private Ball ball;
private NPC npc;
private boolean renderAll;
private Timer timer;
private Random random;
private Font font;
private boolean onTheRun;
private boolean paused;
private enum GameState {
GameStateHold,
GameStateFlow,
}
private GameState gameState;
private JWindow parent;
public Pong (JWindow p) {
parent = p;
this.pcBat = new Bat (0.1f, 0.01f, 0.5f);
this.npcBat = new Bat (0.1f, 0.01f, 0.5f);
this.ball = new Ball ();
this.ball.restart(this.pcBat);
this.npc = new NPC ();
this.npc.maxSpeed = this.ball.speed;
this.npc.bat = this.npcBat;
this.npc.dest = this.npc.bat.position;
this.random = new Random ();
this.onTheRun = false;
this.gameState = GameState.GameStateHold;
this.font = new JLabel().getFont();
this.font = this.font.deriveFont(this.font.getSize2D() * 3.0f);
this.addMouseMotionListener(new MouseMotionAdapter () {
@Override
public void mouseMoved (MouseEvent event) {
Pong.this.handleMouseMoved (event);
}
});
this.addComponentListener(new ComponentAdapter () {
@Override
public void componentResized (ComponentEvent event) {
Pong.this.handleResize ();
}
});
this.timer = new Timer ();
this.timer.schedule(new TimerTask() {
@Override
public void run () {
if (!Pong.this.paused) {
Pong.this.handleTimerEvent ();
}
}
}, 0, 20);
// setup an invisible cursor
Image cursorImage = Toolkit.getDefaultToolkit().createImage(getClass().getResource("/de/unisiegen/tpml/graphics/pong/empty.gif"));
setCursor(Toolkit.getDefaultToolkit().createCustomCursor(cursorImage, new Point(0,0), "empty"));
// pause the game if the mouse is outside the component
this.addMouseListener(new MouseAdapter() {
@Override
public void mouseEntered(MouseEvent e) {
Pong.this.paused = false;
}
@Override
public void mouseExited(MouseEvent e) {
//Pong.this.paused = true;
}
@Override
public void mouseClicked(MouseEvent e) {
Pong.this.handleMouseClicked ();
}
});
setDoubleBuffered(true);
setOpaque(true);
this.renderAll = true;
}
private void handleResize () {
this.renderAll = true;
}
private void handleMouseMoved (MouseEvent event) {
float height = (float)event.getY() / (float)getHeight ();
// save the old state
this.pcBat.save();
// and set the new one
this.pcBat.setPosition(height);
if (this.gameState == GameState.GameStateHold) {
this.ball.posX = 0.9f - this.ball.radius;
this.ball.posY = this.pcBat.position;
}
repaint ();
}
private void handleMouseClicked () {
if (this.gameState == GameState.GameStateFlow) {
return;
}
this.ball.restart(this.pcBat);
this.ball.directionX = -0.4f;
this.ball.directionY = 0.5f - this.pcBat.position;
float length = (float)Math.sqrt (this.ball.directionX*this.ball.directionX + this.ball.directionY*this.ball.directionY);
this.ball.directionX /= length;
this.ball.directionY /= length;
this.gameState = GameState.GameStateFlow;
this.onTheRun = false;
recalcNPCDest();
}
private void handleTimerEvent () {
if (this.gameState == GameState.GameStateHold) {
return;
}
// do the npc movement
this.npc.act();
//this.ball.speed += 0.00001;
this.ball.speed += 0.000005;
// check the bat movement of the player
float pcBatMovement = this.pcBat.position - this.pcBat.movementOldPos;
this.pcBat.saveMovement();
float npcBatMovement = this.npcBat.position - this.npcBat.movementOldPos;
this.npcBat.saveMovement();
// save the old state
this.ball.save ();
// and set the new one
this.ball.move ();
if (this.ball.posY >= 1.0f || this.ball.posY <= 0.0f) {
this.ball.directionY *= -1.0f;
}
if (this.ball.posX >= 1.0f) {
this.gameState = GameState.GameStateHold;
this.npcBat.points++;
if (this.npcBat.points >= WINPOINTS && this.npcBat.points >= this.pcBat.points + 2 )
{
String message = "Computer wins!";
String title = "Computer wins!";
parent.setAlwaysOnTop(false);
parent.setVisible(false);
JOptionPane.showMessageDialog(parent, message, title, JOptionPane.INFORMATION_MESSAGE);
parent.dispose();
}
this.onTheRun = false;
this.ball.restart(this.pcBat);
}
if (this.ball.posX <= 0.0f) {
this.gameState = GameState.GameStateHold;
this.pcBat.points++;
if (this.pcBat.points >= WINPOINTS && this.pcBat.points >= this.npcBat.points + 2 )
{
String message = "Congratulations! You win!";
String title = "You win!";
parent.setAlwaysOnTop(false);
parent.setVisible(false);
JOptionPane.showMessageDialog(parent, message, title, JOptionPane.INFORMATION_MESSAGE);
parent.dispose();
}
this.onTheRun = false;
this.ball.restart(this.pcBat);
}
recalcNPCDest();
if (this.ball.posX >= (0.9f - this.pcBat.width / 2) &&
this.ball.oldPosX <= (0.9f - this.pcBat.width / 2)) {
// check whether the pc bat hits the ball
if (this.ball.posY >= (this.pcBat.position - this.pcBat.height / 2) &&
this.ball.posY <= (this.pcBat.position + this.pcBat.height / 2)) {
if (this.ball.directionX > pcBatMovement) {
this.ball.directionX -= pcBatMovement;
this.ball.directionY -= pcBatMovement * 3;
}
float length = (float)Math.sqrt (this.ball.directionX * this.ball.directionX + this.ball.directionY * this.ball.directionY);
this.ball.directionX /= length;
this.ball.directionY /= length;
this.ball.directionX *= -1.0f;
this.onTheRun = false;
}
}
if (this.ball.posX <= (0.1f + this.npcBat.width / 2) &&
this.ball.oldPosX >= (0.1f + this.npcBat.width / 2)) {
// check whether the npc bat hits the ball
if (this.ball.posY >= (this.npcBat.position - this.npcBat.height / 2) &&
this.ball.posY <= (this.npcBat.position + this.npcBat.height / 2)) {
if (this.ball.directionX < npcBatMovement) {
this.ball.directionX += npcBatMovement;
this.ball.directionY -= npcBatMovement * 3;
}
float length = (float)Math.sqrt (this.ball.directionX * this.ball.directionX + this.ball.directionY * this.ball.directionY);
this.ball.directionX /= length;
this.ball.directionY /= length;
this.ball.directionX *= -1.0f;
this.onTheRun = false;
}
}
repaint ();
}
private void recalcNPCDest () {
float Px = this.ball.posX;
float Py = this.ball.posY;
float Qx = this.ball.directionX;
float Qy = this.ball.directionY;
if (Qx > 0.0f) {
return;
}
if (Qy == 0.0f) {
return;
}
float topX = Px - (Py / Qy) * Qx;
float bottomX = Px + ((1.0f - Py) / Qy) * Qx;
if (topX < 0.1 || bottomX < 0.1) {
if (this.onTheRun == false) {
this.onTheRun = true;
this.npc.dest = Py + ((0.1f - Px) / Qx) * Qy;
float add = (this.random.nextFloat() - 0.5f) * this.npcBat.height * this.ball.speed * 70;
this.npc.dest += add;
float dist = this.npc.dest - this.npc.bat.position;
float time = (this.ball.posX - 0.1f) / (this.ball.speed * this.ball.directionX);
this.npc.speed = dist / time;
this.npc.speed *= Math.signum(this.npc.speed);
if (this.npc.speed > this.npc.maxSpeed) {
this.npc.speed = this.npc.maxSpeed;
}
}
return;
}
this.onTheRun = false;
if (this.ball.directionX < 0.0f) {
this.npc.speed = this.npc.maxSpeed;
this.npc.dest = this.ball.posY;
}
}
@Override
public void paint (Graphics gc) {
gc.setColor (Color.BLACK);
if (this.renderAll || true) {
// clear the background
gc.fillRect (0, 0, getWidth (), getHeight ());
this.renderAll= false;
}
gc.setColor(Color.GREEN);
// draw the old pc and the old npc bat
// npc old
int centerX = (int)(getWidth () * 0.1f);
int centerY = (int)(getHeight() * this.npcBat.position);
int width = (int)(this.npcBat.width * getWidth());
int height = (int)(this.npcBat.height * getHeight ());
gc.fillRect(centerX - width/2, centerY - height/2, width, height);
// pc old
centerX = (int)(getWidth () * 0.9f);
centerY = (int)(getHeight() * this.pcBat.position);
width = (int)(this.pcBat.width * getWidth());
height = (int)(this.pcBat.height * getHeight ());
gc.fillRect(centerX - width/2, centerY - height/2, width, height);
// draw the old ball
centerX = (int)(this.ball.posX * getWidth());
centerY = (int)(this.ball.posY * getHeight ());
width = (int)(this.ball.radius * getWidth ());
height = (int)(this.ball.radius * getHeight ());
gc.fillOval(centerX - width/2, centerY - height/2, width, height);
height = (int)(0.9f * getHeight ());
width = (int)(this.pcBat.width * getWidth ());
centerX = (int)(0.5f * getWidth ());
centerY = (int)(0.5f * getHeight ());
gc.fillRect(centerX - width / 2, centerY - height / 2, width, height);
String points = "" + this.npcBat.points;
FontMetrics fm = getFontMetrics (this.font);
int posX = getWidth () / 4 - fm.stringWidth(points) / 2;
int posY = fm.getHeight();
gc.setFont (this.font);
gc.drawString(points, posX, posY);
points = "" + this.pcBat.points;
posX = getWidth () * 3 / 4 - fm.stringWidth(points) / 2;
gc.drawString(points, posX, posY);
}
}