package airhockey;
import java.io.IOException;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.ConcurrentModificationException;
import java.util.Enumeration;
import java.util.ListIterator;
import java.util.logging.*;
import javax.swing.JOptionPane;
import org.jbox2d.common.*;
import org.jbox2d.dynamics.*;
import org.jbox2d.dynamics.joints.MouseJoint;
import org.jbox2d.dynamics.joints.MouseJointDef;
import org.lwjgl.input.*;
import org.lwjgl.opengl.*;
import org.newdawn.slick.*;
import org.newdawn.slick.font.effects.ColorEffect;
import org.newdawn.slick.util.FontUtils;
public class AirHockey extends BasicGame{
static World world;
static CollisionHandler cl;
static ArrayList<Puck> puck;
static Mallet mallet,mallet2;
static AppGameContainer appgc;
static Wall down1,down2,up1,up2,left,right;
static Goal goalu,goald;
static Image imgBackground,imgPuck,imgMallet,imgMallet2;
static UnicodeFont scoreFont,resultFont;
static float X,Y;
static int goali,tries,wins,puckDispenserQueue[],iMilestone,resi;
static boolean online,isWaiting,mouseGrabbed,end;
static NetworkThread networkThread;
static Thread dispenserThread;
static String ip,myip;
static Body ground;
static MouseJoint mj;
static ArrayList<Puck> puckRemovalQueue;
static float seconds;
static final int resX=320,resY=640,rad=20,border=10;
static final int XA=rad+border,XB=resX-rad-border,YA=rad+border,YB=resY-rad-border;
static final int port = 8999,framerate=120,scoreboardDuration[]={200,800,1000},resultDuration[]={500};
static final int dispensingBorder = 15;
static final Vec2 dispensingForce = new Vec2(5.0f,2.5f);
static final int matchDuration = 60;
static final int milestone[] = {45,30,15};
public AirHockey(String gamename) {
super(gamename);
}
@Override
public void init(GameContainer gc) throws SlickException {
X = resX/2/50.0f;
Y = resY*3/4/50.0f;
tries = wins = 0;
iMilestone = milestone.length;
end = false;
seconds = 0.0f;
puck = new ArrayList<>();
puckRemovalQueue = new ArrayList<>();
world = new World(new Vec2(0f,0f));
mallet = new Mallet(new Vec2(resX/2,resY*3/4),false);
mallet2 = new Mallet(new Vec2(resX/2,100),true);
down1 = new Wall(world,0,YB+rad+5,(resX-120)/2,5);
down2 = new Wall(world,resX,YB+rad+5,(resX-120)/2,5);
up1 = new Wall(world,0,YA-rad-5,(resX-120)/2,5);
up2 = new Wall(world,resX,YA-rad-5,(resX-120)/2,5);
left = new Wall(world,XA-rad,0,2,resY);
right = new Wall(world,XB+rad,0,2,resY);
goalu = new Goal(world,0,YA-rad-5-rad*2.5f,resX,0);
goald = new Goal(world,0,YB+rad+5+rad*2.5f,resX,0);
cl = new CollisionHandler(puck,mallet,mallet2,goalu,goald);
world.setContactListener(cl);
goali=0;
resi=0;
scoreFont = new UnicodeFont("airhockey/assets/digital-7.ttf", 72, false, false);
scoreFont.addAsciiGlyphs();
scoreFont.getEffects().add(new ColorEffect());
resultFont = new org.newdawn.slick.UnicodeFont(new java.awt.Font("Lucida Bright",0,35),35,false,false);
resultFont.addAsciiGlyphs();
resultFont.getEffects().add(new ColorEffect());
imgBackground = new Image("airhockey/assets/bg.png");
imgPuck = new Image("airhockey/assets/puck.png");
imgMallet = new Image("airhockey/assets/mallet.png");
imgMallet2 = new Image("airhockey/assets/mallet2.png");
isWaiting = false;
MouseJointDef md = new MouseJointDef();
md.bodyA = world.createBody(new BodyDef());
md.bodyB = mallet.body;
md.maxForce = 10000.0f * mallet.body.getMass();
md.dampingRatio = 0;
md.frequencyHz = 120;
md.target.set(mallet.body.getWorldCenter());
mj = (MouseJoint)world.createJoint(md);
puckDispenserQueue = new int[]{1,0};
dispenserThread = new Thread(new Runnable(){
@Override
public void run() {
try {
while(true){
while(world.isLocked()||(puckDispenserQueue[0]==0&&puckDispenserQueue[1]==0)) Thread.sleep(2);
if (online){
if (puckDispenserQueue[0]>0){
puckDispenserQueue[0]--;
dispensePuck(true);
}
if (puckDispenserQueue[1]>0){
puckDispenserQueue[1]--;
dispensePuck(false);
}
}else{
if (puckDispenserQueue[0]>0) {
puckDispenserQueue[0]--;
dispensePuck(true);
}else if (puckDispenserQueue[1]>0) {
puckDispenserQueue[1]--;
dispensePuck(true);
}
}
Thread.sleep(1000);
}
} catch (InterruptedException ex) {
Logger.getLogger(AirHockey.class.getName()).log(Level.SEVERE, null, ex);
}
}
});
dispenserThread.start();
}
@Override
public void update(GameContainer gc, int i) throws SlickException {
try {
Input in = gc.getInput();
if (in.isKeyPressed(Input.KEY_Q)) System.exit(0);
if (end) return;
if (in.isKeyPressed(Input.KEY_M)){
mouseGrabbed = !mouseGrabbed;
appgc.setMouseGrabbed(mouseGrabbed);
}
if (in.isKeyPressed(Input.KEY_W)){
if (!online){
if (isWaiting)
networkThread.s.close();
else {
myip = getIP();
networkThread = new NetworkThread(null);
cl.networkThread = networkThread;
cl.sa = cl.sb = 0;
networkThread.t.start();
}
}
}
if (in.isKeyPressed(Input.KEY_C)){
String ip = JOptionPane.showInputDialog("Please enter the IP address to connect to: ","127.0.0.1");
if (ip!=null){
networkThread = new NetworkThread(ip);
cl.networkThread = networkThread;
cl.sa = cl.sb = 0;
networkThread.t.start();
}
}
for (Puck p : puckRemovalQueue){
puck.remove(p);
world.destroyBody(p.body);
}
puckRemovalQueue.clear();
if (resi!=0 && resi <= resultDuration[0])resi+=i;
if (online) {
networkThread.oos.writeByte(0);
networkThread.oos.writeFloat(X);
networkThread.oos.writeFloat(Y);
networkThread.oos.writeFloat(mallet.body.getLinearVelocity().x);
networkThread.oos.writeFloat(mallet.body.getLinearVelocity().y);
networkThread.oos.flush();
} else {
for (Puck p : puck){
if (p.body.getLinearVelocity().length() < 5e-1f &&
p.body.getPosition().y*50.0f < resY/2-rad){
puckRemovalQueue.add(p);
puckDispenserQueue[0]++;
tries++;
}
}
}
if (goalu.active || goald.active){
goali+=i;
if (goali>scoreboardDuration[2]){
if (online) {
if (puck.size()-puckRemovalQueue.size()==0)
puckDispenserQueue[networkThread.client?(goalu.active?0:1):(goalu.active?1:0)]++;
}
else puckDispenserQueue[0]++;
if (!online&&goalu.active){
wins++;
tries++;
}
goalu.active = goald.active = false;
goali=0;
}
}else {
world.step(i/1000.0f, 6, 48);
seconds-=i/1000.0f;
}
mj.setTarget(new Vec2(X,Y));
mallet.update();
for (Puck tmp: puck){
if (tmp.isBeingDispensed) tmp.body.applyForceToCenter(tmp.dispenseForce);
tmp.update();
}
if (iMilestone != milestone.length){
if (seconds < milestone[iMilestone]){
iMilestone++;
puckDispenserQueue[0]+=iMilestone;
puckDispenserQueue[1]+=iMilestone;
}
}
reduceInputLag();
} catch (IOException ex) {
Logger.getLogger(AirHockey.class.getName()).log(Level.SEVERE, null, ex);
}
}
@Override
public void render(GameContainer gc, Graphics g) throws SlickException {
imgBackground.draw();
//if (end) return;
try{
ListIterator<Puck> it = puck.listIterator();
while(it.hasNext()) {
Puck p = it.next();
imgPuck.draw(p.gShape.getX(), p.gShape.getY());
}
}catch (ConcurrentModificationException ex){
}
/*for (Puck p: puck){
imgPuck.draw(p.gShape.getX(), p.gShape.getY());
}*/
imgMallet.draw(mallet.gShape.getX(), mallet.gShape.getY());
if (online){
imgMallet2.draw(mallet2.gShape.getX(), mallet2.gShape.getY());
if (goali<=scoreboardDuration[0]) {
g.setColor(new Color(0.0f,0.0f,0.0f,goali/((float)scoreboardDuration[0])*0.7f));
g.fillRect(0, 0, resX, resY);
}else if (goali>=scoreboardDuration[1]) {
g.setColor(new Color(0.0f,0.0f,0.0f,(scoreboardDuration[2]-goali)/((float)scoreboardDuration[0])*0.7f));
g.fillRect(0, 0, resX, resY);
}else {
g.setColor(new Color(0.0f,0.0f,0.0f,0.7f));
scoreFont.loadGlyphs();
g.fillRect(0, 0, resX, resY);
FontUtils.drawString(scoreFont, Integer.toString(goald.score), FontUtils.Alignment.CENTER, 0, 160-scoreFont.getLineHeight()/2, 320, new Color(1.0f,0.0f,0.0f));
FontUtils.drawString(scoreFont, Integer.toString(goalu.score), FontUtils.Alignment.CENTER, 0, 480-scoreFont.getLineHeight()/2, 320, new Color(1.0f,0.0f,0.0f));
}
}else goali=scoreboardDuration[2]+1;
g.setColor(Color.black);
g.fillRect(0,resY,resX,20);
g.setColor(online?Color.green:Color.red);
g.fillOval(resX-15, resY+5, 10, 10);
if (resi!=0){
g.setColor(new Color(0.0f,0.0f,0.0f,resi/((float)resultDuration[0])));
if (resi/((float)resultDuration[0]) > 1.0f) end=true;
g.fillRect(0, 0, resX, resY);
resultFont.loadGlyphs();
String s;
Color c;
if (goalu.score>goald.score) {
s = "Congratulations!";
c = new Color(0.0f,1.0f,0.0f);
}else if (goalu.score<goald.score){
s = "You lose!";
c = new Color(1.0f,0.0f,0.0f);
}else {
s = "Draw";
c = new Color(1.0f,1.0f,1.0f);
}
FontUtils.drawString(resultFont,s,FontUtils.Alignment.CENTER,0, (resY-resultFont.getLineHeight())/2,320,c);
}
if (online || end){
if (seconds>=1) FontUtils.drawString(g.getFont(),String.format("%d:%02d", ((int)seconds)/60,((int)seconds)%60),FontUtils.Alignment.CENTER,0,resY,320,new Color(0.0f,1.0f,0.0f));
else{
if (resi==0)resi=1;
}
}else{
g.setColor(isWaiting?Color.green:Color.red);
g.fillOval(resX-30, resY+5, 10, 10);
g.setColor(Color.white);
if (isWaiting){
g.drawString(myip,0,resY);
}else{
g.drawString("1P mode - "+tries+" tries | "+wins+" wins", 0, resY);
}
}
reduceInputLag();
}
@Override
public void mouseMoved(int oldx, int oldy, int newx, int newy) {
X += (newx-oldx)/50.0f;
Y += (newy-oldy)/50.0f;
if (Y<resY/2/50.0f) Y=resY/2/50.0f;
if (Y>YB/50.0f) Y=YB/50.0f;
if (X<XA/50.0f) X=XA/50.0f;
if (X>XB/50.0f) X=XB/50.0f;
}
/*public static void resetPuck(){
//puck.update(new Vec2(resX/2/50.0f,resY/2/50.0f));
//puck.body.setLinearVelocity(new Vec2(0.0f,0.0f));
}*/
/*public static void resetPuck(boolean m){
if (m){
mallet.update(new Vec2(resX/2/50.0f,3*(resY/4)/50.0f));
mallet.body.setLinearVelocity(new Vec2(0.0f,0.0f));
}
}*/
public static void dispensePuck(boolean side){
Puck p;
if (online && networkThread.client) side = !side;
if (side){
p = new Puck(new Vec2(0-dispensingBorder,resY/2));
p.dispenseForce = dispensingForce;
}else{
p = new Puck(new Vec2(resX+dispensingBorder,resY/2));
p.dispenseForce = dispensingForce.negate();
}
puck.add(p);
}
public static void reduceInputLag() {
GL11.glGetError(); // this call will burn the time between vsyncs
Display.processMessages(); // process new native messages since Display.update();
Mouse.poll(); // now update Mouse events
Keyboard.poll(); // and Keyboard too
}
public static String getIP(){
String ipAddress = null;
Enumeration<NetworkInterface> net = null;
try {
net = NetworkInterface.getNetworkInterfaces();
} catch (SocketException e) {
throw new RuntimeException(e);
}
while(net.hasMoreElements()){
NetworkInterface element = net.nextElement();
Enumeration<InetAddress> addresses = element.getInetAddresses();
while (addresses.hasMoreElements()){
InetAddress ip = addresses.nextElement();
if (ip instanceof Inet4Address){
if (ip.isSiteLocalAddress()){
ipAddress = ip.getHostAddress();
}
}
}
}
return ipAddress;
}
public static void main(String[] args) throws SlickException {
online = false;
isWaiting = false;
mouseGrabbed = true;
appgc = new AppGameContainer(new AirHockey("AirHockey v1"));
appgc.setTargetFrameRate(framerate);
appgc.setShowFPS(false);
appgc.setDisplayMode(resX, resY+20, false);
appgc.setAlwaysRender(true);
appgc.setMouseGrabbed(mouseGrabbed);
appgc.start();
}
}