Java tutorial
/* * Copyright 2009 Hilbrand Bouwkamp, hs@bouwkamp.com * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package org.cobogw.pongy.client; import java.util.HashMap; import org.cobogw.gwt.waveapi.gadget.client.NeedsWave; import org.cobogw.gwt.waveapi.gadget.client.Participant; import org.cobogw.gwt.waveapi.gadget.client.ParticipantUpdateEvent; import org.cobogw.gwt.waveapi.gadget.client.ParticipantUpdateEventHandler; import org.cobogw.gwt.waveapi.gadget.client.State; import org.cobogw.gwt.waveapi.gadget.client.StateUpdateEvent; import org.cobogw.gwt.waveapi.gadget.client.StateUpdateEventHandler; import org.cobogw.gwt.waveapi.gadget.client.WaveFeature; import org.cobogw.pongy.client.Bar.BAR_POSITION; import com.google.gwt.core.client.JsArray; import com.google.gwt.event.dom.client.KeyDownEvent; import com.google.gwt.event.dom.client.KeyDownHandler; import com.google.gwt.event.dom.client.KeyUpEvent; import com.google.gwt.event.dom.client.KeyUpHandler; import com.google.gwt.gadgets.client.Gadget; import com.google.gwt.gadgets.client.UserPreferences; import com.google.gwt.user.client.Random; import com.google.gwt.user.client.Timer; @Gadget.ModulePrefs(title = "Pongy", description = "Play Pong in Google Wave with Pongy", height = 400, author = "Hilbrand Bouwkamp", author_email = "hs@bouwkamp.com") public class Pongy extends Gadget<UserPreferences> implements NeedsWave { public static enum PLAYER { PLAYER_LEFT, PLAYER_RIGHT, COMPUTER; public boolean equals(String anObject) { return this.toString().equals(anObject); } }; public static enum STATE_KEYS { PLAYER_LEFT_POSY, PLAYER_RIGHT_POSY, PLAYER_LEFT_READY, PLAYER_RIGHT_READY, LAST_WINNER, BALL_START, BALL_WAIT_LEFT_PLAYER, BALL_WAIT_RIGHT_PLAYER, PLAYER_LEFT_POINTS, PLAYER_RIGHT_POINTS, GAME_STATE; public boolean equals(String anObject) { return this.toString().equals(anObject); } }; public static enum GAME_STATE { NEW, INIT, NEW_MATCH, ON, PAUZE, END; public boolean equals(String anObject) { return this.toString().equals(anObject); } } private static String BALL_WAIT = "W"; private final static String instructionsText = "Instructions:<br><br>" + "[A] Move up<br>" + "[Z] Move down<br>" //+ "[P] Pause Game<br>" + "[S] Game starts when you both hit S key<br>" //+ "[N] New Game<br><br>" + "(Click with the mouse on the gamefield<br>if the keys don't react!)"; // private final static String textPause = "Press [P] to resume the game"; private final static String textInviteSomeone = "Invite someone to play a game with"; //HSB: TESTING // private final static String textInviteSomeone = // "Sorry this game is under construction, try again later..."; private final static String textWaitingForBothPlayers = "Waiting for both players to press [S] to start the game."; private final static String texWaitingForOtherPlayer = "Waiting for other player to press [S]"; private final static String texWaitingForThisPlayer = "The other player is waiting for you to hit [S]"; private final int width = 520; private final int height = 400; private final int barwidth = 10; private final int ballheight = 10; private final Ball ball; private final Player playerLeft; private final Player playerRight; private Player thisPlayer; private Player otherPlayer; // private final BarComputer barComputer; private final Timer runner; private final Timer playerTimer; private final PongyView view; // Game play private final int gamespeed = 20; private final int winnertimeout = 3000; private PLAYER lastwinner; //private boolean pauze = false; private boolean matchStarted = false; private String lastWLPStatus; private String lastWRPStatus; // Wave interaction private WaveFeature wave; private State state; private HashMap<String, String> delta = new HashMap<String, String>(); private Participant thisParticipant; private boolean stopBallLeft; private boolean stopBallRight; public Pongy() { view = new PongyViewImpl(width, height); ball = new Ball(height, width, barwidth, ballheight); ball.setVisible(false); view.setBall(ball); playerLeft = new Player(height, barwidth, BAR_POSITION.LEFT); view.setBarPlayerLeft(playerLeft.getBar()); playerRight = new Player(height, barwidth, BAR_POSITION.RIGHT); view.setBarPlayerRight(playerRight.getBar()); runner = new Timer() { @Override public void run() { if (!detectCollision()) { ball.move(); if (thisPlayer != null) { thisPlayer.move(); } handleBallSync(); } } }; playerTimer = new Timer() { private int lastTopY = -1; public void run() { if (wave != null) { final String gstate = getGameState(STATE_KEYS.GAME_STATE); if (state != null && GAME_STATE.ON.equals(gstate)) { // Handle this player state if (!thisPlayer.isKeyDown() && lastTopY != thisPlayer.getBar().getTopY()) { lastTopY = thisPlayer.getBar().getTopY(); setStateDelta(thisPlayer.getPosYKey(), "" + lastTopY); if (!wave.isPlayback()) { submitStateDelta(); } } // Handle other player state final Integer y = state != null ? state.getInt(otherPlayer.getPosYKey()) : null; if (y != null) { otherPlayer.getBar().setTopY(y); } } } } }; } public void initializeFeature(WaveFeature feature) { wave = feature; state = wave.getState(); view.addKeyDownHandler(new KeyDownHandler() { public void onKeyDown(KeyDownEvent event) { if (wave == null || wave.isPlayback()) { return; } final char keyCode = Character.toUpperCase((char) event.getNativeKeyCode()); // if ('N' == keyCode && playerLeft == thisPlayer) { // setStateDelta(STATE_KEYS.PLAYER_LEFT_POINTS, "0"); // setStateDelta(STATE_KEYS.PLAYER_RIGHT_POINTS, "0"); // //submitStateDelta(STATE_KEYS.GAME_STATE, GAME_STATE.NEW_MATCH); // } else if ('S' == keyCode && GAME_STATE.NEW_MATCH.equals(getGameState(STATE_KEYS.GAME_STATE))) { if (playerLeft == thisPlayer) { setStateDelta(STATE_KEYS.PLAYER_LEFT_READY, "1"); submitStateDelta(STATE_KEYS.GAME_STATE, GAME_STATE.NEW_MATCH); } else { submitStateDelta(STATE_KEYS.PLAYER_RIGHT_READY, "1"); } // } else if ('P' == keyCode) { // togglePauze(); } else { if (thisPlayer != null) { thisPlayer.setKeyState(true, keyCode); } } } }); view.addKeyUpHandler(new KeyUpHandler() { public void onKeyUp(KeyUpEvent event) { if (wave != null && !wave.isPlayback() && thisPlayer != null) { thisPlayer.setKeyState(false, (char) event.getNativeKeyCode()); } } }); wave.addParticipantUpdateEventHandler(new ParticipantUpdateEventHandler() { public void onUpdate(ParticipantUpdateEvent event) { initParticipants(); } }); wave.addStateUpdateEventHandler(new StateUpdateEventHandler() { public void onUpdate(StateUpdateEvent event) { if (state == null) { state = wave.getState(); } final String gs = getGameState(STATE_KEYS.GAME_STATE, GAME_STATE.NEW); switch (GAME_STATE.valueOf(gs)) { case NEW: break; case INIT: view.showSingleLine(textInviteSomeone); break; case NEW_MATCH: final boolean thisPlayerReady = (thisPlayer == playerLeft && getGameState(STATE_KEYS.PLAYER_LEFT_READY) != null) || (thisPlayer == playerRight && getGameState(STATE_KEYS.PLAYER_RIGHT_READY) != null); final boolean otherPlayerReady = (otherPlayer == playerLeft && getGameState(STATE_KEYS.PLAYER_LEFT_READY) != null) || (otherPlayer == playerRight && getGameState(STATE_KEYS.PLAYER_RIGHT_READY) != null); if (thisPlayerReady && otherPlayerReady) { setStateDelta(STATE_KEYS.PLAYER_LEFT_READY, (String) null); setStateDelta(STATE_KEYS.PLAYER_RIGHT_READY, (String) null); setStateDelta(STATE_KEYS.BALL_WAIT_LEFT_PLAYER, ""); setStateDelta(STATE_KEYS.BALL_WAIT_RIGHT_PLAYER, ""); setStateDelta(STATE_KEYS.BALL_START, String.valueOf(Random.nextInt(1000) > 500)); submitStateOnlyLeftDelta(STATE_KEYS.GAME_STATE, GAME_STATE.ON); } else if (thisPlayerReady) { view.showSingleLine(texWaitingForOtherPlayer); } else if (otherPlayerReady) { view.showSingleLine(texWaitingForThisPlayer); } else if ("0".equals(getGameState(STATE_KEYS.PLAYER_LEFT_POINTS, "0")) && "0".equals(getGameState(STATE_KEYS.PLAYER_RIGHT_POINTS, "0"))) { view.showInstructions(instructionsText); } else { view.showSingleLine(textWaitingForBothPlayers); } break; case ON: if (!matchStarted) { newMatch(); } final String bwl = getGameState(STATE_KEYS.BALL_WAIT_LEFT_PLAYER, ""); final String bwr = getGameState(STATE_KEYS.BALL_WAIT_RIGHT_PLAYER, ""); if ("".equals(bwl) && "".equals(bwr) && (!bwl.equals(lastWLPStatus) || !bwl.equals(lastWRPStatus))) { ball.resumeBall(); } lastWLPStatus = bwl; lastWRPStatus = bwr; break; // case PAUZE: // TODO implement pause // Game paused by on of the players, wait for both to acknowledge // continue // break; case END: matchStarted = false; showPoints(); ball.setVisible(false); lastwinner = PLAYER.PLAYER_LEFT.equals(getGameState(STATE_KEYS.LAST_WINNER)) ? PLAYER.PLAYER_LEFT : PLAYER.PLAYER_RIGHT; // Game ended, update score and send game on after timeout. final boolean youWin = (PLAYER.PLAYER_LEFT.equals(lastwinner) && playerLeft == thisPlayer) || (PLAYER.PLAYER_RIGHT.equals(lastwinner) && playerRight == thisPlayer); if (youWin) { handleMatchOver(); } view.showWinner("YOU " + (youWin ? "WIN!!" : "LOSE!!")); if (!wave.isPlayback() && youWin) { new Timer() { @Override public void run() { submitStateDelta(STATE_KEYS.GAME_STATE, GAME_STATE.NEW_MATCH); } }.schedule(winnertimeout); } break; } } }); } protected void init(UserPreferences preferences) { } // private void togglePauze() { // pauze = !pauze; // if (pauze) { // runner.cancel(); // playerTimer.cancel(); // view.showSingleLine(pauzeText); // } else { // view.setLineVisible(true); // playerTimer.scheduleRepeating(1000); // runner.scheduleRepeating(gamespeed); // } // } /** * Detect if the player missed the ball, only the player itself can report is * has missed the ball, this to avoid collision detection. * * @return returns true if the game is over */ private boolean detectCollision() { boolean gameOver = false; if (ball.atLeftBorder()) { if (stopBallLeft) { ball.waitBall(); } // player1 (left player) if (stopBallLeft && playerLeft == thisPlayer) { setStateDelta(STATE_KEYS.BALL_WAIT_RIGHT_PLAYER, BALL_WAIT); stopBallLeft = false; if (ball.getBottomY() < playerLeft.getBar().getTopY() || ball.getTopY() > playerLeft.getBar().getBottomY()) { matchOver(PLAYER.PLAYER_RIGHT); // right wins gameOver = true; } else { submitStateDelta(); } } } else { stopBallLeft = true; } if (ball.atRightBorder()) { if (stopBallRight) { ball.waitBall(); } // player2 (right player) if (stopBallRight && playerRight == thisPlayer) { setStateDelta(STATE_KEYS.BALL_WAIT_LEFT_PLAYER, BALL_WAIT); stopBallRight = false; if (ball.getBottomY() < playerRight.getBar().getTopY() || ball.getTopY() > playerRight.getBar().getBottomY()) { gameOver = true; matchOver(PLAYER.PLAYER_LEFT); //left wins } else { submitStateDelta(); } } } else { stopBallRight = true; } return gameOver; } private String getGameState(STATE_KEYS key) { return getGameState(key, (String) null); } private String getGameState(STATE_KEYS key, GAME_STATE defaultValue) { return getGameState(key, defaultValue.toString()); } private String getGameState(STATE_KEYS key, String defaultValue) { final String value = state != null ? state.get(key.toString(), null) : null; return value == null ? defaultValue : value; } /** * Processes ball synchronization between the 2 players to make sure they * remain in sync. */ private void handleBallSync() { if (GAME_STATE.ON.equals(getGameState(STATE_KEYS.GAME_STATE))) { // game in progress final String bwl = getGameState(STATE_KEYS.BALL_WAIT_LEFT_PLAYER, ""); final String bwr = getGameState(STATE_KEYS.BALL_WAIT_RIGHT_PLAYER, ""); if (playerLeft == thisPlayer && BALL_WAIT.equals(bwl) && ball.atRightBorder()) { stopBallRight = false; submitStateDelta(STATE_KEYS.BALL_WAIT_LEFT_PLAYER, ""); } if (playerRight == thisPlayer && BALL_WAIT.equals(bwr) && ball.atLeftBorder()) { stopBallLeft = false; submitStateDelta(STATE_KEYS.BALL_WAIT_RIGHT_PLAYER, ""); } } } private void handleMatchOver() { ball.ballOut(); runner.cancel(); playerTimer.cancel(); } private void increasePoints(STATE_KEYS playerPoints) { setStateDelta(playerPoints.toString(), String.valueOf(Integer.parseInt(getGameState(playerPoints, "0")) + 1)); } private void initParticipants() { if (wave != null && thisParticipant == null) { thisParticipant = wave.getViewer(); } if (wave == null || state == null) { return; } if (thisPlayer == null || otherPlayer == null) { final JsArray<Participant> participants = wave.getParticipants(); String idLeft = state.get(PLAYER.PLAYER_LEFT.toString()); if (idLeft == null) { idLeft = wave.getHost().getId(); if (idLeft.equals(thisParticipant.getId())) { setStateDelta(PLAYER.PLAYER_LEFT.toString(), "" + idLeft); submitStateDelta(); } } final boolean thisPlayerLeftPlayer = idLeft.equals(thisParticipant.getId()); initLeftPlayer(wave.getParticipantById(idLeft), thisPlayerLeftPlayer); // we have 2 players... if (participants.length() > 1) { String idRight = state != null ? state.get(PLAYER.PLAYER_RIGHT.toString()) : null; if (idRight == null) { //right is not the left... idRight = idLeft.equals(participants.get(0).getId()) ? participants.get(1).getId() : participants.get(0).getId(); if (!thisPlayerLeftPlayer) { setStateDelta(PLAYER.PLAYER_RIGHT.toString(), "" + idRight); submitStateDelta(); } } initRightPlayer(wave.getParticipantById(idRight), !thisPlayerLeftPlayer); showPoints(); if (getGameState(STATE_KEYS.GAME_STATE) == null || GAME_STATE.INIT.equals(getGameState(STATE_KEYS.GAME_STATE))) { submitStateOnlyLeftDelta(STATE_KEYS.GAME_STATE, GAME_STATE.NEW_MATCH); } } else if (!GAME_STATE.INIT.equals(getGameState(STATE_KEYS.GAME_STATE))) { submitStateOnlyLeftDelta(STATE_KEYS.GAME_STATE, GAME_STATE.INIT); } } } private void initLeftPlayer(Participant p, boolean amILeftPlayer) { if (amILeftPlayer) { thisPlayer = playerLeft; } else { otherPlayer = playerLeft; } view.setPlayerNameLeft(p.getThumbnailUrl(), p.getDisplayName()); playerLeft.setPlayerName(p.getDisplayName()); } private void initRightPlayer(Participant p, boolean amIRightPlayer) { if (amIRightPlayer) { thisPlayer = playerRight; } else { otherPlayer = playerRight; } view.setPlayerNameRight(p.getThumbnailUrl(), p.getDisplayName()); playerRight.setPlayerName(p.getDisplayName()); } /** * Match is over, increase points and set winner state. * * @param whowins Player that wins */ private void matchOver(PLAYER whowins) { handleMatchOver(); if (PLAYER.PLAYER_LEFT.equals(whowins)) { increasePoints(STATE_KEYS.PLAYER_LEFT_POINTS); setStateDelta(STATE_KEYS.LAST_WINNER, PLAYER.PLAYER_LEFT.toString()); } else { increasePoints(STATE_KEYS.PLAYER_RIGHT_POINTS); setStateDelta(STATE_KEYS.LAST_WINNER, PLAYER.PLAYER_RIGHT.toString()); } submitStateDelta(STATE_KEYS.GAME_STATE, GAME_STATE.END); } private void newMatch() { showPoints(); ball.setVisible(true); stopBallLeft = true; stopBallRight = true; thisPlayer.start(); final String balStart = getGameState(STATE_KEYS.BALL_START); ball.start(lastwinner, balStart == null ? true : Boolean.valueOf(balStart)); runner.scheduleRepeating(gamespeed); if (!wave.isPlayback()) { playerTimer.scheduleRepeating(1000); } matchStarted = true; } private void showPoints() { view.showGame(getGameState(STATE_KEYS.PLAYER_LEFT_POINTS, "0"), getGameState(STATE_KEYS.PLAYER_RIGHT_POINTS, "0")); } private void setStateDelta(String key, String value) { delta.put(key, value); } private void setStateDelta(STATE_KEYS key, String value) { setStateDelta(key.toString(), value); } private void submitStateDelta() { if (!wave.isPlayback() && state != null) { state.submitDelta(delta); delta.clear(); } } private void submitStateDelta(STATE_KEYS key, GAME_STATE value) { submitStateDelta(key, value.toString()); } private void submitStateDelta(STATE_KEYS key, String value) { if (!wave.isPlayback() && state != null) { setStateDelta(key, value); submitStateDelta(); } } /** * This submit should only be done by one player. This is set ot the left * player. * * @param key * @param value */ private void submitStateOnlyLeftDelta(STATE_KEYS key, GAME_STATE value) { if (!wave.isPlayback() && thisPlayer == playerLeft) { submitStateDelta(key, value.toString()); } } }