import java.applet.*; import java.awt.*; import java.util.*; import java.awt.image.*; /** * JoustPong by Kirk Israel * * A simple game based on a conversation in the Usenet group * rec.games.video.classic * * Joust is copyright Williams. * * Permission is given to copy, post, use, and modify this * code so long as notification to the above e-mail address is made. * * This applet uses my GameRunner and SquareSprite classes. * Come to http://alienbill.com for more info * **/ public class JoustPong extends GameRunner { //physics defaults (that can be overridden by applet parameters) static final double DEFAULT_GRAVITY = .05; static final double DEFAULT_BALL_X_SPEED = 1.5; static final int DEFAULT_WIN_SCORE = 10; static final double DEFAULT_FLAP_STRENGTH = 2.5; //set constants to defaults double constGravity = DEFAULT_GRAVITY; double constBallXSpeed = DEFAULT_BALL_X_SPEED; int constWinScore = DEFAULT_WIN_SCORE ; double constFlapStrength = DEFAULT_FLAP_STRENGTH; int p1Score, p2Score, p1wincount,p2wincount; //current scores, and count of wins boolean gameOn = false; //game is happening vs. showing titlescreen boolean cpuPlay = false; //cpu controls player two, true/false boolean P2won = false; //did P2 win last game? (changes title graphic) //main Sprite references Player playerOne = null; Player playerTwo = null; Ball ball = null; //lettering for fancy titlescreen int numChars = 9; //9 letters in the string "JOUSTPONG" AlphaFlapper titleChars[] = new AlphaFlapper[numChars]; //center message of Title Screen String mainMessage = "Avoid Missing Ball for High Score"; //images for titlescreen Image imgP1Stand = null; Image imgP2Stand = null; /** * reset ballspeed and position, scores */ void resetMatch(){ ball.setXSpeed(constBallXSpeed); ball.setYSpeed(Math.random()*2-1); ball.setXPos(screenWidth/2); p1Score = 0; p2Score = 0; } /** * general startyp routine */ void setupGame(){ checkParams(); //check for override of physics and game defaults screenColor = Color.black; //set up players, size and location are hardcoded with fudge factors playerOne=new Player(16,26,12,screenHeight/2,constFlapStrength); playerTwo=new Player(16,26,screenWidth-28,screenHeight/2,constFlapStrength); //right facing sprites are drawn differently relative to their 'bounding box' playerOne.setFaceRight(true); //Load Images MediaTracker t = new MediaTracker(this); Image imgRightup = getImage(getCodeBase(),"p1up.gif"); t.addImage(imgRightup,0); Image imgRightdown = getImage(getCodeBase(),"p1down.gif"); t.addImage(imgRightdown,0); Image imgLeftup = getImage(getCodeBase(),"p2up.gif"); t.addImage(imgLeftup,0); Image imgLeftdown = getImage(getCodeBase(),"p2down.gif"); t.addImage(imgLeftdown,0); Image imgBall = getImage(getCodeBase(),"ball.gif"); t.addImage(imgBall,0); imgP1Stand = getImage(getCodeBase(),"p1stand.gif"); t.addImage(imgP1Stand,0); imgP2Stand = getImage(getCodeBase(),"p2stand.gif"); t.addImage(imgP2Stand,0); // wait for all images to finish loading // try { t.waitForAll(); } catch (InterruptedException e) { } //give players their images playerOne.addUpImage(imgRightup); playerOne.addDownImage(imgRightdown); playerTwo.addUpImage(imgLeftup); playerTwo.addDownImage(imgLeftdown); //set up ball, size is hardcoded ball = new Ball(12,14,screenWidth/2,screenWidth/2,imgBall); //Players bounce of ceilings and floors, losing some speed playerOne.setTopCollide(.8,0); playerOne.setBottomCollide(.4,screenHeight); playerTwo.setTopCollide(.8,0); playerTwo.setBottomCollide(.4,screenHeight); //ball bounces of ceiling and floors losing no velocity ball.setTopCollide(1,0); ball.setBottomCollide(1,screenHeight); //set letters to JOUSTPONG, give each a random starting location, and an appropriate goal location titleChars[0] = new AlphaFlapper(10,10,(int)(Math.random()*screenWidth),(int)(Math.random()*screenHeight),"J",screenWidth*1/10, screenHeight/4); titleChars[1] = new AlphaFlapper(10,10,(int)(Math.random()*screenWidth),(int)(Math.random()*screenHeight),"O",screenWidth*2/10, screenHeight/4); titleChars[2] = new AlphaFlapper(10,10,(int)(Math.random()*screenWidth),(int)(Math.random()*screenHeight),"U",screenWidth*3/10, screenHeight/4); titleChars[3] = new AlphaFlapper(10,10,(int)(Math.random()*screenWidth),(int)(Math.random()*screenHeight),"S",screenWidth*4/10, screenHeight/4); titleChars[4] = new AlphaFlapper(10,10,(int)(Math.random()*screenWidth),(int)(Math.random()*screenHeight),"T",screenWidth*5/10, screenHeight/4); titleChars[5] = new AlphaFlapper(10,10,(int)(Math.random()*screenWidth),(int)(Math.random()*screenHeight),"P",screenWidth*6/10, screenHeight/4); titleChars[6] = new AlphaFlapper(10,10,(int)(Math.random()*screenWidth),(int)(Math.random()*screenHeight),"O",screenWidth*7/10, screenHeight/4); titleChars[7] = new AlphaFlapper(10,10,(int)(Math.random()*screenWidth),(int)(Math.random()*screenHeight),"N",screenWidth*8/10, screenHeight/4); titleChars[8] = new AlphaFlapper(10,10,(int)(Math.random()*screenWidth),(int)(Math.random()*screenHeight),"G",screenWidth*9/10, screenHeight/4); } /** * what happens every 'tick'-- we repaint entire screen */ void runGame(Graphics offG){ if(gameOn){ //play game (as opposed to title screen) //draw scores offG.setColor(Color.white); offG.drawString(""+p1Score, screenWidth/4, screenHeight-20); offG.drawString(""+p2Score, screenWidth*3/4, screenHeight-20); //add gravity to Players speed, move and paint them playerOne.changeYSpeed(constGravity); playerOne.updatePosition(); playerOne.paint(offG, this); playerTwo.changeYSpeed(constGravity); //if the cpu is in control, see if we want to flap //(the AI is 'if below ball, flap') if(cpuPlay){ if(ball.getXSpeed() > 0) { if(playerTwo.getYPos() > ball.getYPos() ) { playerTwo.flap(); } } else { if(playerTwo.getYPos() > screenHeight/2 ) { playerTwo.flap(); } } } playerTwo.updatePosition(); playerTwo.paint(offG, this); //check for collisions, reverse ball and add player //vertical velocity to ball if(playerOne.overlap(ball)){ ball.setXSpeed(constBallXSpeed); ball.changeYSpeed(playerOne.getYSpeed()); } if(playerTwo.overlap(ball)){ ball.setXSpeed(constBallXSpeed * -1); ball.changeYSpeed(playerTwo.getYSpeed()); } //keep track if either jsut scored boolean hadScore = false; //point for one if(ball.getXPos() > screenWidth){ p1Score++; ball.setXPos(screenWidth/2); hadScore = true; } //point for two if(ball.getXPos() + ball.getXSize() < 0){ p2Score++; ball.setXPos(screenWidth/2); hadScore = true; } //check for victories if(hadScore) { if(p1Score >= constWinScore) { gameOn = false; P2won = false; mainMessage = "Player 1 Wins!"; p1wincount++; p1Score = 0; p2Score = 0; } if(p2Score >= constWinScore) { gameOn = false; P2won = true; mainMessage = "Player 2 Wins!"; p2wincount++; p1Score = 0; p2Score = 0; } } //move ball ball.updatePosition(); ball.paint(offG,this); } else { //game not on, show title screen offG.setColor(Color.white); //if player 2 jsut one show player 2 standing //otherwise show player one Image imgWinner = null; if(P2won){ imgWinner = imgP2Stand; } else { imgWinner = imgP1Stand; } offG.drawImage(imgWinner,screenWidth/2-14,screenHeight/2-50,this); centerString(offG, mainMessage, screenWidth/2, screenHeight*8/16); centerString(offG, "(Play to "+constWinScore+" Points)", screenWidth/2, screenHeight*9/16); centerString(offG, "To Flap:", screenWidth/2, screenHeight*5/8); centerString(offG, "Player 1: [space]", screenWidth/4, screenHeight*11/16); centerString(offG, "Player 2: [enter]", screenWidth*3/4, screenHeight*11/16); //show count of wins if(p1wincount > 0 || p2wincount > 0){ String wins; if(p1wincount == 1) wins = "win"; else wins = "wins"; centerString(offG, p1wincount+" "+wins, screenWidth/4, screenHeight*12/16); if(p2wincount == 1) wins = "win"; else wins = "wins"; centerString(offG, p2wincount+" "+wins, screenWidth*3/4, screenHeight*12/16); } centerString(offG, "Press 1 for 1 Player Game (vs CPU)", screenWidth/2, screenHeight*13/16); centerString(offG, "Press 2 for 2 Player Game", screenWidth/2, screenHeight*14/16); offG.drawString("alienbill.com",screenWidth-offG.getFontMetrics().stringWidth("alienbill.com"),screenHeight-1); // clear buffer //move JOUSTPONG letters for(int i=0; i < numChars;i++){ titleChars[i].changeYSpeed(constGravity); titleChars[i].steer(); titleChars[i].updatePosition(); titleChars[i].paint(offG); } } } public boolean keyDown(Event e,int key) { // System.out.println(key); if(gameOn) { if(key == KEY_ESCAPE){ gameOn = false; mainMessage = "Game Cancelled"; } if(key == KEY_SPACE){ //flap player one playerOne.flap(); } if(key == KEY_ENTER && cpuPlay == false){ //flap player two if cpu not playing playerTwo.flap(); } } else { //on title screen if(key == 49 || key==50) { //'1' or '2' resetMatch(); if(key == 49) {//1 player game cpuPlay = true; } else { //2 playe game cpuPlay = false; } gameOn = true; } } return true; } /* public boolean mouseMove(Event e,int x,int y) { return true; } public boolean mouseDrag(Event e,int x,int y) { return true; } public boolean mouseDown(Event e,int x,int y) { playerTwo.flap(); return true; } */ void checkParams(){ //see if we override the physical constants of the game try { constGravity = Double.valueOf(getParameter("constGravity")).doubleValue(); } catch(NumberFormatException e){} catch(NullPointerException e) {} try { constBallXSpeed = Double.valueOf(getParameter("constBallXSpeed")).doubleValue(); } catch(NumberFormatException e){} catch(NullPointerException e) {} try { constWinScore = Integer.valueOf(getParameter("constWinScore")).intValue(); } catch(NumberFormatException e){} catch(NullPointerException e) {} try { constFlapStrength = Double.valueOf(getParameter("constFlapStrength")).doubleValue(); } catch(NumberFormatException e){} catch(NullPointerException e) {} } } /** * general player class */ class Player extends SquareSprite { Image imgUp; //wings up Image imgDown; //wings down boolean faceRight = false; //right facing player has different bounding box final int FLAP_TICK_LENGTH = 5; //how long wings stay down when flap is pressed int flapDownCount = 0; //counter for how long wings stay down double flapstrength = 0.0; //constructor public Player(int width, int height,int left, int top, double flap){ xSize=width; ySize=height; setMaxXSpeed(10.0); setMaxYSpeed(10.0); xPos = left; yPos = top; flapstrength = flap; } //record fact facing right public void setFaceRight(boolean b){ faceRight = b; } //flap: add flap power to velocity, //start graphic timer going public void flap() { changeYSpeed(-flapstrength); flapDownCount = FLAP_TICK_LENGTH ; } void paint (Graphics g, ImageObserver a){ Image img = null; g.setColor(Color.white); //get int values rather than the doubles we usually use int xPosInt = new Double(xPos).intValue(); int yPosInt = new Double(yPos).intValue(); if(flapDownCount > 0){ //display wings down for now img = imgDown; flapDownCount--; } else { img = imgUp; //default is up } if(faceRight) { //need to compensate for wider GIF g.drawImage(img,xPosInt+xSize-imgUp.getWidth(a)+6,yPosInt,a); } else { g.drawImage(img,xPosInt-6,yPosInt,a); } } public void addUpImage(Image i){ //record image imgUp = i; } public void addDownImage(Image i){ //record image imgDown = i; } } class Ball extends SquareSprite { Image img; //Constructor public Ball(int width, int height,int left, int top, Image ball){ xSize=width; ySize=height; setMaxXSpeed(5.0); setMaxYSpeed(5.0); xPos = left; yPos = top; img = ball; } void paint (Graphics g, ImageObserver a){ //display image g.setColor(Color.white); int xPosInt = new Double(xPos).intValue(); int yPosInt = new Double(yPos).intValue(); // g.fillRect(xPosInt,yPosInt,xSize,ySize); g.drawImage(img,xPosInt,yPosInt,a); } } /** * prints a string, moves around like a player towards a specified x,y goal */ class AlphaFlapper extends Player { String letter; int goalX,goalY; public AlphaFlapper(int width, int height,int left, int top, String letter, int goalX, int goalY){ super(width, height,left, top, 1.5); this.letter = letter; this.goalX=goalX; this.goalY=goalY; setMaxXSpeed(1.5); setMaxYSpeed(1.5); } public void steer(){ //move towards goal if(getXPos() < goalX){ changeXSpeed(.3); } if(getXPos() > goalX){ changeXSpeed(-.3); } if(getYPos() > goalY && (int)(Math.random()*3)==1){ flap(); } } public void flap() { changeYSpeed(-flapstrength); } void paint (Graphics g){ g.setColor(Color.white); int xPosInt = new Double(xPos).intValue(); int yPosInt = new Double(yPos).intValue(); g.drawString(letter,xPosInt,yPosInt); } }