package client; import client.audio.*; import client.gui.*; import common.*; import common.messages.*; import java.awt.Color; import java.awt.Component; import java.awt.event.*; import java.awt.geom.Rectangle2D; import java.awt.Window; import java.util.logging.Logger; import java.util.logging.Level; import java.util.Vector; import javax.swing.Timer; /** * This class describes the game loop for this game */ public class GameEngine implements Constants, ActionListener, ActionCallback { // SINGLETONS public static Logger logger = Logger.getLogger(CLIENT_LOGGER_NAME); public static GameEngine gameEngine; public boolean gameOver; public Map gameMap; public ClientViewArea gameViewArea; public LocalPlayer localPlayer; public InputListener localInputListener; // Actor lists and sub-lists public Vector<Actor> actorList; // This contains all actors in the game public Vector<Stone> stoneList; // This only contains stones public Vector<Player> playerList; // This only contains players public Vector<Projectile> bulletList; // This only contains bullets public Vector<Actor> miscList; // Anything else not in the above lists public long lastTime; public float currentTime; public Timer timer; public Timer frameRate; public Vector<MapChangeListener> mapListeners; // Sound stuff public GameSoundSystem soundSystem; public SoundEffect soundBump, soundDeath, soundFire; // CONSTRUCTORS public GameEngine(Map m, byte playerID, String name, boolean bot) { preSetup(m); localPlayer = bot ? new ComputerPlayer(playerID, name, this) : new HumanPlayer(localInputListener, playerID, name); postSetup(true); } public GameEngine(Map m) { preSetup(m); localPlayer = new HumanPlayer(localInputListener); postSetup(false); } // end GameEngine() constructor private void preSetup(Map m) { gameEngine = this; gameOver = false; gameMap = m; mapListeners = new Vector<MapChangeListener>(); actorList = new Vector<Actor>(); stoneList = new Vector<Stone>(); playerList = new Vector<Player>(); bulletList = new Vector<Projectile>(); miscList = new Vector<Actor>(); gameViewArea = new ClientViewArea(this); addButton(-5, -5, 45, 15, "Quit", Color.red); localInputListener = new InputListener(); localInputListener.attachListeners(gameViewArea); triggerMapListeners(); // Sound engine stuff: soundSystem = new GameSoundSystem(); soundBump = soundSystem.loadSoundEffect(SOUND_BUMP); soundDeath = soundSystem.loadSoundEffect(SOUND_DEATH); soundFire = soundSystem.loadSoundEffect(SOUND_FIRE); } private void postSetup(boolean fixed) { // if (fixed) // gameMap.placePlayer(localPlayer, null); // else gameMap.placePlayer(localPlayer); MouseTracker mouseTracker = localPlayer instanceof ComputerPlayer ? new RandomMouseTracker(localInputListener, gameViewArea) : new MouseTracker(localInputListener, gameViewArea); localPlayer.setAimingTarget(mouseTracker); DoubleTracker doubleTracker = new DoubleTracker(mouseTracker, localPlayer); TrackingObject playerTracker = new TrackingObject(doubleTracker); gameViewArea.viewTracker = playerTracker; gameViewArea.setLocalPlayer(localPlayer); addActor(localPlayer); addActor(mouseTracker); addActor(doubleTracker); addActor(playerTracker); } // GETTERS public LocalPlayer getLocalPlayer() { return this.localPlayer; } public Map getGameMap() { return this.gameMap; } public boolean getGameOver() { return this.gameOver; } public ClientViewArea getGameViewArea() { return this.gameViewArea; } public InputListener getInputListener() { return this.localInputListener; } // SETTERS public void setLocalPlayer(LocalPlayer p) { this.localPlayer = p; } public void setGameMap(Map m) { this.gameMap = m; triggerMapListeners(); } // OPERATIONS public void addActor(Actor a) { synchronized(actorList) { actorList.add(a); if (a instanceof Stone) synchronized(stoneList) { stoneList.add((Stone) a); } else if (a instanceof Projectile) synchronized(bulletList) { bulletList.add((Projectile) a); } else if (a instanceof Player) synchronized(playerList) { playerList.add((Player) a); } else synchronized(miscList) { miscList.add(a); } } } public void removeActor(Actor a) { if(a == null) return; synchronized(actorList) { actorList.remove(a); if (a instanceof Projectile) synchronized(bulletList) { bulletList.remove((Projectile) a); } else if (a instanceof Player) synchronized(playerList) { playerList.remove((Player) a); } else synchronized(miscList) { miscList.remove(a); } } } public boolean isGameOver() { return this.gameOver; } public void gameOver() { gameOver = true; if (timer != null) timer.stop(); // This code finds the Window that contains the gameViewArea and tells // it to disapear Component c = gameViewArea.getParent(); while (!(c instanceof Window) && c != null) c = c.getParent(); if (c != null) ((Window) c).setVisible(false); } public void gameStep() { checkCollisions(); updateWorld(); Thread.yield(); } public void play() { initialize(); timer = new Timer(TIMER_TICK, this); timer.start(); timer.setCoalesce(true); // while (!isGameOver()) { // gameStep(); // // try { Thread.sleep(10); } // catch (InterruptedException er) { } // } } protected void triggerMapListeners() { for (MapChangeListener mcl : mapListeners) mcl.mapChanged(gameMap); } public void addMapListener(MapChangeListener listener) { mapListeners.add(listener); } public void initialize() { // Copy the map as a bunch of Stones for (int x = 0; x < gameMap.getWidth(); x++) for (int y = 0; y < gameMap.getHeight(); y++) { if (gameMap.isWall(x, y)) { stoneList.add(new Stone(x, y)); actorList.add(stoneList.get(stoneList.size() - 1)); } } lastTime = System.currentTimeMillis(); currentTime = 0; } // end initialize() public void checkCollisions() { // Environment, Player and projectile collision code goes here Rectangle2D bounds1, bounds2; Actor actor1, actor2; // Check players against stones Player p; for (int i = 0; i < playerList.size(); i++) { p = playerList.get(i); if (!p.isAlive()) { removeActor(p); continue; } float px = p.getX(), py = p.getY(); int ix = (int) px, iy = (int) py; if (px < 0 || py < 0) continue; if (px >= gameMap.getWidth() || py >= gameMap.getHeight()) continue; px = px - ix; py = py - iy; if (px < 0.25 && gameMap.isWall(ix - 1, iy)) p.collideLeft(); else if (px > 0.75 && gameMap.isWall(ix + 1, iy)) p.collideRight(); if (py < 0.25 && gameMap.isWall(ix, iy - 1)) p.collideUp(); else if (py > 0.72 && gameMap.isWall(ix, iy + 1)) p.collideDown(); } // for (int i=0; i < stoneList.size(); i ++) // { // actor1 = stoneList.get(i); // bounds1 = actor1.getBounds(); // // for (int j=0; j < playerList.size(); j ++) // { // actor2 = playerList.get(j); // bounds2 = actor2.getBounds(); // // if (bounds1.intersects(bounds2)) // { // actor1.collision(actor2); // actor2.collision(actor1); // } // } // } // end check players against stones // Check bullets against players and stones for (int i = 0; i < bulletList.size(); i++) { actor1 = bulletList.get(i); if (!actor1.isAlive()) { removeActor(actor1); continue; } bounds1 = actor1.getBounds(); for (int j = 0; j < playerList.size(); j++) { actor2 = playerList.get(j); bounds2 = actor2.getBounds(); /* * /<<<<<<< HEAD:client/GameEngine.java if (fixed) { * for(byte i = 0; i < 6; i++) { if(i != * localPlayer.getPlayerID()) { processPlayerJoin( new * PlayerJoinMessage(i,new * java.net.InetSocketAddress(MCAST_ADDRESS,MCAST_PORT),"User" + * i)); } } } //======= */ if (bounds1.intersects(bounds2)) { actor1.collision(actor2); actor2.collision(actor1); } } // for (int j=0; j < stoneList.size(); j ++) // { // actor2 = stoneList.get(j); // bounds2 = actor2.getBounds(); // // if (bounds1.intersects(bounds2)) // { // actor1.collision(actor2); // actor2.collision(actor1); // } // } // Simpler check: if (gameMap.isWall((int) actor1.getX(), (int) actor1.getY())) { actor1.collision(null); } } // end check bullets against players and stones // //Vector actorList = this.gameViewArea.actorList; // Rectangle2D playerBounds = this.localPlayer.getBounds(); // for (int i = 0; i < actorList.size(); i = i + 1) { // Actor actor1 = actorList.get(i); // if (actor1 instanceof TrackingObject) continue; // // Rectangle2D bound1 = actor1.getBounds(); // if (bound1.intersects(playerBounds)) { // this.localPlayer.collision(actor1); // actor1.collision(this.localPlayer); // } // for (int j = i + 1; j < actorList.size(); j = j + 1) { // Actor actor2 = actorList.get(j); // if (actor2 instanceof TrackingObject) continue; // // Rectangle2D bound2 = actor2.getBounds(); // if (bound1.intersects(bound2)) { // actor1.collision(actor2); // actor2.collision(actor1); // } // } // } } // end checkCollisions() public void updateWorld() { long thisTime = System.currentTimeMillis(); float dTime = 0.001f * (thisTime - lastTime); boolean repaint = false; // for (Actor a : actorList) // { // if (a.animate(dTime)) // repaint = true; // } synchronized(playerList) { for (Actor a : playerList) { if (a.animate(dTime, currentTime)) repaint = true; } } synchronized(bulletList) { for (Actor a : bulletList) { if (a.animate(dTime, currentTime)) repaint = true; } } synchronized(miscList) { for (Actor a : miscList) { if (a.animate(dTime, currentTime)) repaint = true; } } lastTime = thisTime; currentTime += dTime; if (repaint) { /* * This may not actually be desireable. If you bump or fire then * stop moving, it won't repaint */ } gameViewArea.repaint(); } // end updateWorld() public void actionPerformed(ActionEvent e) { gameStep(); } protected void addButton(int x, int y, int width, int height, String label) { addButton(x, y, width, height, label, Color.green); } protected void addButton(int x, int y, int width, int height, String label, Color c) { SimpleButton b = new SimpleButton(x, y, width, height, label, c); b.addCallback(this); gameViewArea.addWidget(b); } public void actionCallback(InteractiveWidget source, int buttons) { if (source.getLabel().equalsIgnoreCase("Quit")) { gameOver(); } } /* ****************************************** * * These method are for playing sound effects * * ****************************************** */ /** * Plays a bump sound effect at the specified volume. If the sound is * already playing at a lower volume, it is stopped and restarted at the * louder volume, otherwise nothing happens. * * @param volume * The volume at which to play. */ public void playBump(float volume) { logger.info(String.format("Playing bump sound at a volume of %.2f.", volume)); playSound(volume, soundBump); } /** * Plays a player death sound effect at the specified volume * * @param volume * The volume at which to play */ public void playDeath(float volume) { logger.info(String.format("Playing death sound at a volume of %.2f.", volume)); playSound(volume, soundDeath); } /** * Plays a gun fire sound effect at the specified volume * * @param volume * The volume at which to play */ public void playFire(float volume) { logger.info(String.format("Playing fire sound at a volume of %.2f.", volume)); playSound(volume, soundFire); } /** * A slightly more generic sound playing method so we don't have to * duplicate code all over the place. This actually handles playing or not * playing the sound. * * @param volume * @param sound */ private void playSound(float volume, SoundEffect sound) { // If we failed to find any audio lines, all sounds will be null if (sound == null) return; if (sound.isPlaying()) { if (sound.getVolume() <= volume) sound.stop(); else return; } sound.setVolume(volume); sound.play(); } public void registerActionListeners(Component c) { localInputListener.attachListeners(c); c.addKeyListener(gameViewArea); } public void registerActionListeners(Window w) { localInputListener.attachListeners(w); } public void unregisterActionListeners(Component c) { localInputListener.detachListeners(c); c.removeKeyListener(gameViewArea); } public void unregisterActionListenerst(Window w) { localInputListener.detachListeners(w); } /* ===================================== * Methods for processing messages * ===================================== */ public synchronized void processPlayerMotion(PlayerMotionMessage message) { // Get the index of the player int playerIndex = getPlayerIndex(message.getPlayerId()); // Do not process a player if they have not been added if(playerIndex == -1) { SpawnPoint sp = new SpawnPoint(message.getPosition()); processPlayerJoin(new PlayerJoinMessage(message.getPlayerId(), RESOLVING_NAME, null, sp)); } if (playerIndex < 0) return; try { // Update the co-ordinates of the player Player player = playerList.get(playerIndex); if (player instanceof RemotePlayer) ((RemotePlayer) player).addMotionPacket(message); } catch (Exception ex) { ex.printStackTrace(); } } public synchronized void processPlayerJoin(PlayerJoinMessage message) { // Get the index of the player int playerIndex = getPlayerIndex(message.getPlayerId()); Player player; // Creating a new player if(playerIndex == -1) { player = new RemotePlayer(message.getPlayerId(),message.getName()); logger.log(Level.FINE,"Player Added: " + player.getPlayerID()); gameMap.placePlayer(player,message.getSpawnPoint()); addActor(player); } // Updating information about the player else { player = playerList.get(playerIndex); logger.log(Level.FINE,"Player Info Updated: " + player.getPlayerID()); player.setPlayerName(message.getName()); } } public void processProjectile(ProjectileMessage message) { // Get the index of the player int playerIndex = getPlayerIndex(message.getPlayerId()); // Do not process this message if the player has not joined the game if (playerIndex == -1) return; Player player = playerList.get(playerIndex); if (player instanceof RemotePlayer) { // Add the projectile to the list addActor(new Projectile(message.getStartPosition(), message.getDirection(), player.getCurrentTime(), player.getCurrentTime(), player.getPlayerID(), player.getTeam())); // Signal that the remote player has fired player.fire(); logger.log(Level.FINE,"Player " + player.getPlayerID() + " fired!"); } } /** * Retrieve a player given their ID. */ public int getPlayerIndex(int playerId) { int index = -1; for (int i = 0; i < playerList.size(); i++) { if (playerList.get(i).getPlayerID() == playerId) { index = i; break; } } return index; } public Player getPlayer(byte playerId) { synchronized(playerList) { for(Player player : playerList) { if(player.getPlayerID() == playerId) return player; } } return null; } } // end class GameEngine