private void init() {
    initInput();
    initCharacters();

    soundPlayer = new SoundPlayer();
    if (soundPlayer.getSoundSystem() == null) soundPlayer = new NoSoundPlayer();

    soundPlayer.startTitleMusic();

    try {
      emptyCursor =
          Toolkit.getDefaultToolkit()
              .createCustomCursor(
                  new BufferedImage(16, 16, BufferedImage.TYPE_INT_ARGB), new Point(0, 0), "empty");
    } catch (RuntimeException e) {
      e.printStackTrace();
    }
    setFocusTraversalKeysEnabled(false);
    requestFocus();

    // hide cursor, since we're drawing our own one
    setCursor(emptyCursor);
  }
  private synchronized void render(Graphics g) {
    if (level != null) {
      int xScroll = (int) (player.pos.x - screen.w / 2);
      int yScroll = (int) (player.pos.y - (screen.h - 24) / 2);
      soundPlayer.setListenerPosition((float) player.pos.x, (float) player.pos.y);
      level.render(screen, xScroll, yScroll);
    }
    if (!menuStack.isEmpty()) {
      menuStack.peek().render(screen);
    }

    if (Options.getAsBoolean(Options.DRAW_FPS, Options.VALUE_FALSE)) {
      Font.defaultFont().draw(screen, texts.FPS(fps), 10, 10);
    }

    if (player != null && menuStack.size() == 0) {
      addHealthBar(screen);
      addXpBar(screen);
      addScore(screen);

      Font font = Font.defaultFont();
      if (isMultiplayer) {
        font.draw(screen, texts.latency(latencyCacheReady() ? "" + avgLatency() : "-"), 10, 20);
      }
    }

    if (isMultiplayer && menuStack.isEmpty()) {
      chat.render(screen);
    }

    g.setColor(Color.BLACK);

    g.fillRect(0, 0, getWidth(), getHeight());
    g.translate((getWidth() - GAME_WIDTH * SCALE) / 2, (getHeight() - GAME_HEIGHT * SCALE) / 2);
    g.clipRect(0, 0, GAME_WIDTH * SCALE, GAME_HEIGHT * SCALE);

    if (!menuStack.isEmpty() || level != null) {

      // render mouse
      renderMouse(screen, mouseButtons);

      g.drawImage(screen.image, 0, 0, GAME_WIDTH * SCALE, GAME_HEIGHT * SCALE, null);
    }
  }
  public void handleAction(int id) {
    switch (id) {
      case TitleMenu.RETURN_TO_TITLESCREEN:
        clearMenus();
        level = null;
        TitleMenu menu = new TitleMenu(GAME_WIDTH, GAME_HEIGHT);
        addMenu(menu);
        this.nextMusicInterval = 0;
        soundPlayer.stopBackgroundMusic();
        soundPlayer.startTitleMusic();
        break;

      case TitleMenu.START_GAME_ID:
        clearMenus();
        isMultiplayer = false;
        chat.clear();

        localId = 0;
        MojamComponent.localTeam = Team.Team1;
        synchronizer = new TurnSynchronizer(this, null, 0, 1);
        synchronizer.setStarted(true);

        createLevel(TitleMenu.level, TitleMenu.defaultGameMode);
        soundPlayer.stopBackgroundMusic();
        break;

      case TitleMenu.SELECT_LEVEL_ID:
        addMenu(new LevelSelect(false));
        break;

      case TitleMenu.SELECT_HOST_LEVEL_ID:
        addMenu(new LevelSelect(true));
        break;
        /*
         * case TitleMenu.UPDATE_LEVELS:
         * GuiMenu menu = menuStack.pop();
         * if (menu instanceof LevelSelect) { addMenu(new
         * LevelSelect(((LevelSelect) menu).bHosting)); } else { addMenu(new
         * LevelSelect(false)); } }
         */
      case TitleMenu.HOST_GAME_ID:
        addMenu(new HostingWaitMenu());
        isMultiplayer = true;
        isServer = true;
        chat.clear();
        try {
          if (isServer) {
            localId = 0;
            MojamComponent.localTeam = Team.Team1;
            serverSocket = new ServerSocket(Options.getAsInteger(Options.MP_PORT, 3000));
            serverSocket.setSoTimeout(1000);

            hostThread =
                new Thread() {

                  @Override
                  public void run() {
                    boolean fail = true;
                    try {
                      while (!isInterrupted()) {
                        Socket socket = null;
                        try {
                          socket = serverSocket.accept();
                        } catch (SocketTimeoutException e) {
                        }
                        if (socket == null) {
                          System.out.println("Waiting for player to connect");
                          continue;
                        }
                        fail = false;
                        packetLink = new NetworkPacketLink(socket);
                        createServerState = 1;
                        break;
                      }
                    } catch (Exception e) {
                      e.printStackTrace();
                    }
                    if (fail) {
                      try {
                        serverSocket.close();
                      } catch (IOException e) {
                      }
                    }
                  };
                };
            hostThread.start();
          }
        } catch (Exception e) {
          e.printStackTrace();
        }
        break;

      case TitleMenu.JOIN_GAME_ID:
        addMenu(new JoinGameMenu());
        break;

      case TitleMenu.CANCEL_JOIN_ID:
        popMenu();
        if (hostThread != null) {
          hostThread.interrupt();
          hostThread = null;
        }
        break;

      case TitleMenu.PERFORM_JOIN_ID:
        menuStack.clear();
        isMultiplayer = true;
        isServer = false;
        chat.clear();

        String[] data = TitleMenu.ip.trim().split(":");
        String ip = data[0];
        Integer port =
            (data.length > 1)
                ? Integer.parseInt(data[1])
                : Options.getAsInteger(Options.MP_PORT, 3000);

        try {
          localId = 1;
          MojamComponent.localTeam = Team.Team2;
          packetLink = new ClientSidePacketLink(ip, port);
          synchronizer = new TurnSynchronizer(this, packetLink, localId, 2);
          packetLink.setPacketListener(this);
        } catch (Exception e) {
          e.printStackTrace();
          // System.exit(1);
          addMenu(new TitleMenu(GAME_WIDTH, GAME_HEIGHT));
        }
        break;

      case TitleMenu.HOW_TO_PLAY:
        addMenu(new HowToPlayMenu(level != null));
        break;

      case TitleMenu.OPTIONS_ID:
        addMenu(new OptionsMenu(level != null));
        break;

      case TitleMenu.SELECT_DIFFICULTY_ID:
        addMenu(new DifficultySelect(false));
        break;

      case TitleMenu.SELECT_DIFFICULTY_HOSTING_ID:
        addMenu(new DifficultySelect(true));
        break;

      case TitleMenu.KEY_BINDINGS_ID:
        addMenu(new KeyBindingsMenu(keys, inputHandler));
        break;

      case TitleMenu.LEVEL_EDITOR_ID:
        addMenu(new LevelEditorMenu());
        break;

      case TitleMenu.EXIT_GAME_ID:
        System.exit(0);
        break;

      case TitleMenu.RETURN_ID:
        synchronizer.addCommand(new PauseCommand(false));
        keys.tick();
        break;

      case TitleMenu.BACK_ID:
        popMenu();
        break;

      case TitleMenu.CREDITS_ID:
        addMenu(new CreditsScreen(GAME_WIDTH, GAME_HEIGHT));
        break;

      case TitleMenu.CHARACTER_ID:
        addMenu(new CharacterSelectionMenu());
        break;
    }
  }
  private void tick() {
    // Not-In-Focus-Pause
    if (level != null && !isMultiplayer && !paused && !this.isFocusOwner()) {
      keys.release();
      mouseButtons.releaseAll();
      PauseCommand pauseCommand = new PauseCommand(true);
      synchronizer.addCommand(pauseCommand);
      paused = true;
    }

    if (requestToggleFullscreen || keys.fullscreen.wasPressed()) {
      requestToggleFullscreen = false;
      setFullscreen(!fullscreen);
    }

    if (level != null && level.victoryConditions != null) {
      if (level.victoryConditions.isVictoryConditionAchieved()) {
        int winner = level.victoryConditions.playerVictorious();
        int characterID = winner == MojamComponent.localTeam ? playerCharacter : opponentCharacter;
        addMenu(new WinMenu(GAME_WIDTH, GAME_HEIGHT, winner, characterID));
        level = null;
        return;
      }
    }

    if (packetLink != null) {
      packetLink.tick();
    }

    mouseButtons.setPosition(getMousePosition());
    if (!menuStack.isEmpty()) {
      menuStack.peek().tick(mouseButtons);
    }
    if (mouseMoved) {
      mouseMoved = false;
      mouseHideTime = 0;
      if (mouseButtons.mouseHidden) {
        mouseButtons.mouseHidden = false;
      }
    }
    if (mouseHideTime < 60) {
      mouseHideTime++;
      if (mouseHideTime == 60) {
        mouseButtons.mouseHidden = true;
      }
    }

    if (level == null) {
      mouseButtons.tick();
    } else if (level != null) {
      if (synchronizer.preTurn()) {
        synchronizer.postTurn();

        for (int index = 0; index < mouseButtons.currentState.length; index++) {
          boolean nextState = mouseButtons.nextState[index];
          if (mouseButtons.isDown(index) != nextState) {
            synchronizer.addCommand(new ChangeMouseButtonCommand(index, nextState));
          }
        }

        synchronizer.addCommand(
            new ChangeMouseCoordinateCommand(
                mouseButtons.getX(), mouseButtons.getY(), mouseButtons.mouseHidden));

        mouseButtons.tick();
        for (MouseButtons sMouseButtons : synchedMouseButtons) {
          sMouseButtons.tick();
        }

        if (!paused) {
          for (int index = 0; index < keys.getAll().size(); index++) {
            Keys.Key key = keys.getAll().get(index);
            boolean nextState = key.nextState;
            if (key.isDown != nextState) {
              synchronizer.addCommand(new ChangeKeyCommand(index, nextState));
            }
          }

          keys.tick();
          for (Keys skeys : synchedKeys) {
            skeys.tick();
          }

          if (keys.pause.wasPressed()) {
            keys.release();
            mouseButtons.releaseAll();
            synchronizer.addCommand(new PauseCommand(true));
          }

          level.tick();
          if (isMultiplayer) {
            tickChat();
          }
        }

        // every 4 minutes, start new background music :)
        if (System.currentTimeMillis() / 1000 > nextMusicInterval) {
          nextMusicInterval = (System.currentTimeMillis() / 1000) + 4 * 60;
          soundPlayer.startBackgroundMusic();
        }

        if (keys.screenShot.isDown) {
          takeScreenShot();
        }
      }
    }

    if (createServerState == 1) {
      createServerState = 2;

      synchronizer = new TurnSynchronizer(MojamComponent.this, packetLink, localId, 2);

      clearMenus();
      createLevel(TitleMenu.level, TitleMenu.defaultGameMode);

      synchronizer.setStarted(true);
      if (TitleMenu.level.vanilla) {
        packetLink.sendPacket(
            new StartGamePacket(
                TurnSynchronizer.synchedSeed,
                TitleMenu.level.getUniversalPath(),
                DifficultyList.getDifficultyID(TitleMenu.difficulty)));
      } else {
        packetLink.sendPacket(
            new StartGamePacketCustom(
                TurnSynchronizer.synchedSeed,
                level,
                DifficultyList.getDifficultyID(TitleMenu.difficulty),
                playerCharacter,
                opponentCharacter));
      }
      packetLink.setPacketListener(MojamComponent.this);
    }
  }
 public void stop() {
   running = false;
   soundPlayer.stopBackgroundMusic();
   soundPlayer.shutdown();
 }