Package de.creepsmash.client.game

Source Code of de.creepsmash.client.game.GameLoop

/**
   Creep Smash, a multiplayer towerdefence game
   created as a project at the Hochschule fuer
   Technik Stuttgart (University of Applied Science)
   http://www.hft-stuttgart.de
  
   Copyright (C) 2008 by     
    * Andreas Wittig
    * Bernd Hietler
    * Christoph Fritz
    * Fabian Kessel
    * Levin Fritz
    * Nikolaj Langner
    * Philipp Schulte-Hubbert
    * Robert Rapczynski
    * Ron Trautsch
    * Sven Supper
    http://creepsmash.sf.net/

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
**/
package de.creepsmash.client.game;

import java.awt.Graphics2D;
import java.util.ArrayList;
import java.util.TreeMap;
import java.util.logging.Logger;

import de.creepsmash.client.creep.Creep;
import de.creepsmash.client.creep.CreepFactory;
import de.creepsmash.client.game.GameContext.BoardLocation;
import de.creepsmash.client.network.MessageListener;
import de.creepsmash.client.network.Network;
import de.creepsmash.client.panel.GamePanel;
import de.creepsmash.client.sound.SoundManagement;
import de.creepsmash.client.tower.Tower;
import de.creepsmash.common.IConstants;
import de.creepsmash.common.messages.server.MessageMessage;
import de.creepsmash.common.messages.server.PlayerQuitMessage;
import de.creepsmash.common.messages.server.RoundMessage;
import de.creepsmash.common.messages.server.ServerMessage;
import de.creepsmash.common.messages.server.StartGameMessage;

/**
* Main GameLoop for updates and repaint. Sync with Server tick and framerate
* approximation.
*/
public class GameLoop extends Thread implements MessageListener, IConstants {

  private static Logger logger = Logger.getLogger(GameLoop.class.getName());
  private GameContext myContext;
  private ArrayList<GameContext> contexts = new ArrayList<GameContext>();
 
  //key PlayerID, value position
  private TreeMap<Integer, Integer> playersOrder
    = new TreeMap<Integer, Integer>();
  private TreeMap<Integer, String> players = new TreeMap<Integer, String>();
  private Network tdNetwork;

  private GamePanel gamePanel;

  private Map map;

  private boolean running = false;
  private static final int MAX_ROUNDS_BEHIND = IConstants.USER_ACTION_DELAY;

  // one loop-cycle lasts TICK_INTERVAL time
  public static final int TICK_INTERVAL = TICK_MS * 1000000; // Mil * 1000000
  // If we are MAX_DELAY_BEHIND_MAXROUND, we'll stop
  // sleeping every round and accelerate FPS *wuuhuuu*
  public static final int MAX_DELAY_BEHIND_MAXROUND = USER_ACTION_DELAY + 40;

  // we need this to measure the time
  private static long nextTime;
  private long roundID = 0;
  private long maxRound = 0;
  private int incomeCounter = 0;
 
  private boolean gameOver;

  private SoundManagement managementSound;

  /**
   * @return the gameOver
   */
  public boolean isGameOver() {
    return gameOver;
  }

  /**
   * @param gameOver the gameOver to set
   */
  public void setGameOver(boolean gameOver) {
    this.gameOver = gameOver;
  }

  /**
   * Creates a new GameLoop.
   *
   * @param gamePanel
   *            the gamePanel
   * @param network
   *            the network instance
   */
  public GameLoop(GamePanel gamePanel, Network network, SoundManagement soundM) {

    this.gamePanel = gamePanel;
    tdNetwork = network;
    tdNetwork.addListener(this);
    this.managementSound = soundM;

    network.makeContact();
  }

  /**
   * finds the PlayerContext.
   */
  private void findMyContext() {
    for (GameContext gc : contexts) {
      if (gc instanceof PlayerContext) {
        myContext = gc;
      }
    }
  }

  /**
   * HelperMethod. How much time has left compared to nextTime
   *
   * @return how much time is left
   */
  private long timeLeft() {
    long now;

    now = System.nanoTime();

    if (nextTime <= now) {
      return 0;
    } else {
      return nextTime - now;
    }
  }

 
  /**
   * some configurations before the gameloop starts.
   */
  public void init() {
    contextSetup();
    findMyContext();
    gamePanel.setSoundManagementObject(managementSound);
    gamePanel.getCreepPanel().setContext(myContext);
    gamePanel.getChatPanel().setContext(myContext);
    gamePanel.getTowerPanel().setContext(myContext);
    gamePanel.getBuildTowerInfoPanel().setContext(myContext);
    gamePanel.getSelectTowerInfoPanel().setContext(myContext);
    gamePanel.getCreepInfoPanel().setContext(myContext);
    gamePanel.getNoInfoPanel().setContext(myContext);
    gamePanel.setContext(myContext);
   
  }

  // 2nd GameLoop
  /**
   * Calls the update() and repaint() methods on GameContext.
   */

  @Override
  public void run() {
    logger.info("GameLoop running...");
    init();
    nextTime = System.nanoTime() + TICK_INTERVAL;
    running = true;

    gamePanel.getQuit().setEnabled(true);
    // The gameloop
    while (running) {

      if (roundID > maxRound) {

        while (roundID > maxRound) {
          try {
            // logger.info("DBG: waiting for \"bigger\" maxRound");
            Thread.sleep(10);
          } catch (InterruptedException e) {
            e.printStackTrace();
          }
        }
        nextTime = System.nanoTime() + TICK_INTERVAL;
      }

      gameUpdate(); // updates the gamestate
      gameRender(); // paints new screen in a buffer
      // draw buffer to screen
      gamePanel.getBoardPanel().getStrategy().show();
      logger.fine("update");
      roundID++;

      if (roundID + MAX_DELAY_BEHIND_MAXROUND < maxRound) {
        Thread.yield();
        nextTime = System.nanoTime();
      } else if (timeLeft() != 0) {
        logger.fine("NormalSleep " + timeLeft() / 1000000);
        try {
          Thread.sleep(timeLeft() / 1000000); // to millisec
        } catch (InterruptedException e) {
          e.printStackTrace();
        }

      }
      while (maxRound - roundID > MAX_ROUNDS_BEHIND) {
        // logger.info("Speeding up because we are to slow");
        gameUpdate();
        roundID++;
      }
      nextTime += TICK_INTERVAL;
    }
   
    // only painting when game is over
    while (gameOver) {
      gameRender();
      gamePanel.getBoardPanel().getStrategy().show();
      try {
        Thread.sleep(50);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }

    }

  }

  /**
   * Transfers the creeps from one Context to the next.
   */
  public void transferCreeps() {
    int size = contexts.size();

    for (int i = 0; i < size; i++) {
      ArrayList<Creep> transCopy = new ArrayList<Creep>(contexts.get(i)
          .getTransfer());

      GameContext copyTo = findNextContext(contexts.get(i));
     
      // If some Playaer is dead, find other living Player
      while (copyTo.isDead()) {
        copyTo = findNextContext(copyTo);
      }
     

      for (Creep c : transCopy) {
        Creep copy = CreepFactory.createCreep(copyTo, c.getType());
        copy.setHealth(c.getHealth());
        copy.setBuildTime(c.getBuildTime());
        copy.setSenderId(c.getSenderId());
        if (c.getSenderId() == copyTo.getPlayerId()) { // If sender and receiver the same Player
          copyTo.getTransfer().add(copy);
        } else {
          copyTo.getCreeps().add(copy);
         
          /********************************
           * Here is calculating of taked *
           * lives from each Player       *
           ********************************/
          for (int j = 0; j < size; j++) {
            if (contexts.get(j).getPlayerId() == c.getSenderId()) {
              contexts.get(j).takedlives++;
              break;
            }
          }
        }
        contexts.get(i).getTransfer().remove(c);
      }
    }
  }

 
  /**
   *
   * find the next context.
   * @param start startcontext
   * @return found context
   */
  private GameContext findNextContext(GameContext start) {
    GameContext found = null;
    BoardLocation loc = start.getLocation();
    while (found == null) {
      loc = getSuccessor(loc);
      found = findContextByLocation(loc);
    }
    return found;
  }
 
  /**
   * find a context by the location.
   * @param loc the location
   * @return found context
   */
  private GameContext findContextByLocation(BoardLocation loc) {
    for (GameContext gc : contexts) {
      if (gc.getLocation().equals(loc)) {
        return gc;
      }
    }
    return null;
  }
 
 
  /**
   * get the Successor.
   * @param loc location
   * @return location
   */
  private BoardLocation getSuccessor(BoardLocation loc) {
    switch(loc) {
      case TOPLEFT:
        return BoardLocation.TOPRIGHT;
      case TOPRIGHT:
        return BoardLocation.BOTTOMRIGHT;
      case BOTTOMRIGHT:
        return BoardLocation.BOTTOMLEFT;
      case BOTTOMLEFT:
        return BoardLocation.TOPLEFT;
      default:
        return null;
    }
  }
 
  /**
   * Instatiates the game context for all players.
   */
  private void contextSetup() {
    int cnt = 0;
    GameContext.BoardLocation loc = null;
    GameContext context = null;
    GameContext.setWinningPosition(0);
    for (Integer id : players.keySet()) {
      switch (playersOrder.get(id)) {
      case 0:
        loc = GameContext.BoardLocation.TOPLEFT;
        break;
      case 1:
        loc = GameContext.BoardLocation.TOPRIGHT;
        break;
      case 2:
        loc = GameContext.BoardLocation.BOTTOMRIGHT;
        break;
      case 3:
        loc = GameContext.BoardLocation.BOTTOMLEFT;
        break;
      default:
        logger.warning("Creating context without location");

      }
      cnt++;
      if (id == gamePanel.getCore().getPlayerId()) {
        logger.info("own context created");
        context = new PlayerContext(loc, tdNetwork, managementSound,
            map);
        gamePanel.getBoardPanel().addMouseMotionListener(
            context.getGameBoard());
        gamePanel.getBoardPanel().addMouseListener(
            context.getGameBoard());
        gamePanel.getGameInfoPanel().addPlayerContext(
            (PlayerContext) context);

      } else {
        context = new OpponentContext(loc, tdNetwork, map);

        gamePanel.getGameInfoPanel().addOpponentContext(
            (OpponentContext) context);
      }
      context.setPlayerId(id);
      context.setPlayerName(players.get(id));
      contexts.add(context);

      context.fireCreditsChangedEvent();
      context.fireIncomeChangedEvent();
      context.fireLivesChangedEvent();
    }
  }

  /**
   * Receive messages from the server.
   *
   * @param m
   *            the ServerMessage
   */
  public void update(ServerMessage m) {
    if (m instanceof StartGameMessage) {
      if (!this.isAlive()) {
        this.start();
      }
    } else if (m instanceof MessageMessage) {
      MessageMessage mMChat = (MessageMessage) m;
      gamePanel.getChatPanel().setMessage(mMChat.getPlayerName(),
          mMChat.getMessage());
    } else if (m instanceof PlayerQuitMessage) {
      PlayerQuitMessage pqm = (PlayerQuitMessage) m;

      for (GameContext con : contexts) {
        if (pqm.getPlayerName().equals(con.getPlayerName())) {
          con.setLives(0);
          con.sendDeathMessage();
          con.fireLivesChangedEvent();
        }
      }
    }

    if (m instanceof RoundMessage) {
      maxRound = ((RoundMessage) m).getRoundId();
    }
  }

  /**
   * updates the income every INCOME_TIME.
   */
  private void updateIncome() {

    for (GameContext gc : contexts) {
      gc.setCredits(gc.getCredits() + gc.getIncome());
    }

    logger.info("new income");
  }

  /**
   * updates the gamestate.
   */
  private void gameUpdate() {
   
    if (gameOver) {
      return;
    }
   
    int deadCount = 0;
    transferCreeps();

    for (GameContext gc : contexts) {
      gc.update(roundID);
      if (gc.isDead()) {
        gc.sendDeathMessage();
        deadCount++;
        for (Tower t : gc.getTowers()) {
          t.setCoolDownNow(t.getCoolDown() - 1);
        }
      }
    }
    // Game is finished at this point
    // stop the thread.
    if (deadCount >= contexts.size() - 1) {
      if (!myContext.isDead()) {
        // send gameover Message to the server
        ((PlayerContext) myContext).sendDeathMessage();
        // set the context to dead so that no more
        // user actions are allowed
        myContext.sendDeathMessage();
      }
      gameOver = true;
      this.running = false;

    }

    if (myContext.getStartCounter() >= 0) {
      if (roundID % (1000 / TICK_MS) == 0) {
        gameRender();
        gamePanel.getBoardPanel().getStrategy().show();
        myContext.setStartCounter(myContext.getStartCounter() - 1);
      }
    }
    if (myContext.getStartCounter() < 0) {
      // only do that if player is alive
      if (!myContext.isDead()) {
        // updates income everu INCOME_TIME millis
        if (roundID % (INCOME_TIME / TICK_MS) == 0) {
          updateIncome();
        }
        // countdown for next income
        if (roundID % (1000 / TICK_MS) == 0) {

          if (--incomeCounter < 1) {
            // milis>>secs
            incomeCounter = IConstants.INCOME_TIME / 1000;
          }

          gamePanel.getGameInfoPanel()
              .setIncomeCounter(incomeCounter);
        }
      }

    }
  }

  /**
   * prepares the game for painting.
   */
  private void gameRender() {
    Graphics2D g2 = gamePanel.getBoardPanel().getImgGraphics();
    for (GameContext gc : contexts) {
      gc.paint(g2);
    }
  }

  /**
   * @return the players
   */
  public TreeMap<Integer, String> getPlayers() {
    return players;
  }

  /**
   * @param players
   *            the players to set
   */
  public void setPlayers(TreeMap<Integer, String> players) {
    this.players = players;
  }

  /**
   * @return the map
   */
  public Map getMap() {
    return map;
  }

  /**
   * @param map
   *            the map to set
   */
  public void setMap(Map map) {
    this.map = map;
  }

  /**
   * @return the running
   */
  public boolean isRunning() {
    return running;
  }

  /**
   * @param running
   *            the running to set
   */
  public void setRunning(boolean running) {
    this.running = running;
  }

  /**
   * @return the playersOrder
   */
  public TreeMap<Integer, Integer> getPlayersOrder() {
    return playersOrder;
  }

  /**
   * @param playersOrder the playersOrder to set
   */
  public void setPlayersOrder(TreeMap<Integer, Integer> playersOrder) {
    this.playersOrder = playersOrder;
  }

}
TOP

Related Classes of de.creepsmash.client.game.GameLoop

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.