示例#1
0
 public static Font createFont() {
   Font font2 = null; // The font we are going to returnb
   InputStream is =
       game.getClass().getResourceAsStream("/fonts/BOOTERZO.ttf"); // Load the font from the file
   try { // Attempt to...
     font2 = Font.createFont(Font.TRUETYPE_FONT, is); // set the font to the one from the file
   } catch (FontFormatException e) { // If there is an error, do nothing
   } catch (IOException e) { // If there is an error, do nothing
   }
   return font2.deriveFont(30f); // return the new font with size 30
 }
示例#2
0
public class Game extends Canvas
    implements Runnable,
        EventListener { // Set up game class as Canvas, and allow this class to be a thread and
                        // handle key events
  private static final long serialVersionUID = 1L; // Required by Canvas superclass
  private static int scale =
      3; // Image rendering factor, used to speed up rendering time by scaling the BufferedImage

  private static Dimension
      imgDim =
          new Dimension(
              ImageUtil.getImage("/ui/panels/background.png").getWidth(),
              ImageUtil.getImage("/ui/panels/background.png").getHeight()),
      boundary = new Dimension(900, 700),
      size =
          Game.getScaledDimension(
              imgDim, boundary); // Scales the background image to 900x700 pixels, with a ratio
  // the same as that of the screen resolution
  private static int width = size.width / scale,
      height = size.height / scale,
      absoluteWidth = width,
      absoluteHeight = height; // Scaled and unscaled window width/height

  /*
   * Enum state used to determinging what is currently being displayed to the user
   */
  public static enum State {
    START,
    GAME,
    OPTION,
    WAITING,
    PAUSE
  }

  public static State STATE =
      State.START; // Instance of the state, which is static, so is accessible to any other class.
  /*
   * Standard variable declerations.
   * Each variable is set to null to ensure that "floating" variables don't occur
   */
  private Thread thread = null; // Create main thread
  private JFrame frame = null; // Create frame object
  private static Keyboard key = null; // Create keyboard for handling key events
  private static Level level =
      null; // Create an instance of the level class, so we can access level-based variables
  private static Player player = null; // Create an instance of the player
  private boolean running = false; // Determines if the thread is running or not

  private static UIManager uiManager = null,
      minimapManager =
          null; // Create the required managers for the minimap and User Interface (UI)). These
                // control rendering and updating of the minimap and UI
  private StartScreen sc = null; // Create a startscreen object
  private Pause pause = null; // Create a pause menu object

  private Screen screen =
      null; // Create a screen object. Used for drawing all the pixels to the frame
  private BufferedImage image =
      new BufferedImage(
          width,
          height,
          BufferedImage
              .TYPE_INT_RGB); // Create the scaled down BufferedImage. I used a BufferedImage
                              // because it allows for transparency, and pixel-per-pixel
  // Control over each rendered object, further increasing rendering time.
  private int[] pixels =
      ((DataBufferInt) image.getRaster().getDataBuffer())
          .getData(); // Set up an array of pixels (integers) based on the number of pixels the
                      // BufferedImage can hold.

  private List<Layer> layerStack = new ArrayList<Layer>(); // Create a list of layers
  private List<UILayer> UILayerStack = new ArrayList<UILayer>(); // Create a list of UI layers

  private static Game game =
      null; // Create a static game object, so other classes can access non-static items in this
            // class
  public static boolean loaded = false,
      paused = false,
      initPause =
          false; // Booleans for if the game is paused, and if we have created a pause menu yet.
  private int time = 0; // Actual time since the game was running
  private static Console c =
      new Console(); // Output console for debuggibng, and displaying tips to the user
  public static Font font =
      new Font("Magneto", Font.BOLD, 24); // Set up the default font for all text in the game
  private static Graphics g; // Create a graphics object that other classes can access
  public static SoundManager s =
      new SoundManager() { // Create a soubnd manager, and set up all the required game sounds ready
                           // for playing.
        public void initSounds() {
          sounds.add(new Sound("INTRO", Sound.getURL("Intro.wav")));
          // sounds.add(new Sound("WALK", Sound.getURL("footstepDirt.wav")));
          /*sounds.add(new Sound("FIRESHOOT", Sound.getURL("SpellFire.wav")));
          sounds.add(new Sound("ICESHOOT", Sound.getURL("SpellIce.wav")));
          sounds.add(new Sound("ACHIEVE", Sound.getURL("Achievement.wav")));
          sounds.add(new Sound("DOORCLOSE", Sound.getURL("DoorClose.wav")));*/
          sounds.add(new Sound("GOBLIN", Sound.getURL("Goblin1.wav")));
          /*sounds.add(new Sound("ENEMY2", Sound.getURL("Goblin2.wav")));
          sounds.add(new Sound("ENEMY3", Sound.getURL("Goblin3.wav")));
          sounds.add(new Sound("ENEMY4", Sound.getURL("Goblin4.wav")));
          sounds.add(new Sound("ENEMY5", Sound.getURL("Goblin5.wav")));
          sounds.add(new Sound("LOSE", Sound.getURL("Lose.wav")));
          sounds.add(new Sound("SELECT", Sound.getURL("Select.wav")));
          sounds.add(new Sound("WIN", Sound.getURL("Win.wav")));*/
        }
      };

  /*
   * Game constructor
   * Sets up the canvas ready to be drawn to the frame
   */

  public Game() {
    Dimension size = new Dimension((width * scale), height * scale); // Create unscaled size
    setPreferredSize(size); // Set the size of the canvas to the unscaled size
    Game.game = this; // Initiate the game instance

    screen = new Screen(width, height); // Initiate a new screeen object ready for rendering.
    uiManager = new UIManager(); // Create the UI Manager for handling the UI
    UILayerStack.add(uiManager); // Add the UI Manager to the UI layer stack
    minimapManager = new UIManager(); // Instantiate the minimap Manager
    frame = new JFrame(); // Instantiate the main frame/window
    key = new Keyboard(); // Instantiate the keyboard variable
    init(STATE); // Initiate the current game state

    addKeyListener(key); // Add the keyboard as the frame's default keyboard listener

    Mouse mouse =
        new Mouse(this); // Create an instance of the Mouse class for handling mouse click events
    addMouseListener(mouse); // Set the default mouse listener for the frame to the mouse object
    addMouseMotionListener(mouse); // Set the default mouse movement listener to the mouse object
  }

  /*
   * Method used for initiating the main game frame, seetting up the close strategy and starting the game
   */

  public static void startGame() {
    game = new Game(); // Create an instance of the class
    game.frame.setResizable(false); // Prevent the window from being allowed to be resized
    game.frame.setUndecorated(
        true); // Removes the bar around the edge of the window. Change to false to add this back
    game.frame.setLayout(
        new GridBagLayout()); // Create a layout pane for items to be positioned in the frame
                              // correctly
    GridBagConstraints gbc = new GridBagConstraints(); // Initiate a callable object of this layout
    gbc.anchor = GridBagConstraints.CENTER; // Center the objects we add past this point
    game.frame.add(
        game, gbc); // Add the game object (Canvas) to the layout manager, and thus the frame.

    game.frame.pack(); // Tell the frame we are done adding items
    game.frame.setDefaultCloseOperation(
        JFrame.DO_NOTHING_ON_CLOSE); // Tell the frame not to close if the user tries to exit

    /*
     * Sets up a custom way of exiting the game
     */

    WindowListener exitListener =
        new WindowAdapter() { // Default event class that triggers when the window is tried to be
                              // closed
          @Override // Declare we are overriding a method in the WindowListener class
          public void windowClosing(WindowEvent e) { // If the window is trying to close
            State preState = STATE; // Store the game state
            if (player != null) STATE = State.PAUSE; // If the player exists, pause the game
            int confirm =
                JOptionPane.showOptionDialog(
                    null,
                    "Are You Sure to Close Application?",
                    "Exit Confirmation",
                    JOptionPane.YES_NO_OPTION,
                    JOptionPane.QUESTION_MESSAGE,
                    null,
                    null,
                    null); // Open a yes/no message bos, asking if the user wants to exit
            if (confirm == 0
                && player
                    != null) { // If they want to exit and the player exists (They are in game)...
              int confirm2 =
                  JOptionPane.showOptionDialog(
                      null,
                      "Save Progress?",
                      "Save Confirmation",
                      JOptionPane.YES_NO_OPTION,
                      JOptionPane.QUESTION_MESSAGE,
                      null,
                      null,
                      null); // Open a message box asking if they want to save their progress
              if (confirm2 == 0) { // If they do...
                FileHandler.save(player); // Save
                System.exit(0); // Close the window
              } else {
                System.exit(0); // Otherwise just exit.
              }
            } else if (confirm == 0) { // If they want to exit while not in game...
              System.exit(0); // Close the window
            } else { // If they say no...
              STATE = preState; // Set the state back to the previous state
              paused = false; // Unpause the game
            }
          }
        };
    game.frame.addWindowListener(exitListener); // Set the windows exit listener to our custom one
    game.frame.setLocationRelativeTo(null); // Position the frame in the center of the screen
    game.frame.setVisible(true); // Set the frame to be visible
    game.frame.requestFocus(); // Set the game to have input focus
    game.start(); // Start the game thread
    s.loopSound("INTRO"); // Start the instroduction music
    game.frame.requestFocus(); // Request focus to ensure we haven't lost it
    System.out.println("Press Enter to start..."); // Tell the user the game has started
  }

  /*
   * A method for creating a font object from a font file
   */

  public static Font createFont() {
    Font font2 = null; // The font we are going to returnb
    InputStream is =
        game.getClass().getResourceAsStream("/fonts/BOOTERZO.ttf"); // Load the font from the file
    try { // Attempt to...
      font2 = Font.createFont(Font.TRUETYPE_FONT, is); // set the font to the one from the file
    } catch (FontFormatException e) { // If there is an error, do nothing
    } catch (IOException e) { // If there is an error, do nothing
    }
    return font2.deriveFont(30f); // return the new font with size 30
  }

  /*
   * Returns the instance of the output console
   */

  public static Console getConsole() {
    return c;
  }

  /*
   * Create a message box, setting the title and message to the title and message passed into the parameters
   */

  public static void infoBox(String infoMessage, String titleBar) {
    JOptionPane.showMessageDialog(
        null, infoMessage, "InfoBox: " + titleBar, JOptionPane.INFORMATION_MESSAGE);
  }

  /*
   * Return the version value stored in Maven's pom.xml. This ensures the game name is savd witht the correct version in the name.
   */

  public static String getVersion(Object classRes) {
    return classRes.getClass().getPackage().getImplementationVersion();
  }

  /*
   * Initiate the game state. This loads the necessary game objects depending on the state
   */

  private void init(State state) {
    if (state == State.GAME) { // If the state is ingame...
      level = Level.createLevel(0); // Load the spawn level (level 0)
      TileCoordinate playerSpawn =
          new TileCoordinate(
              15,
              60); // Create player coodinates. This class creates coordinates from pixels and
                   // converts it to tile coordinates (16 pixels -> 1 tile)
      String playerName = ""; // Initiate the players name
      if (FileHandler.fileExists(FileHandler.localUserFile))
        playerName =
            FileHandler
                .getPlayerName(); // If the player already exists, set the players name to the
                                  // stored name
      else
        playerName =
            sc
                .getPlayerName(); // Otherwise retrieve the name from the start screen that the user
                                  // has typed into the name box
      player =
          new Player(
              playerName,
              playerSpawn.x(),
              playerSpawn.y(),
              key); // Create a new player. Pass in the name, spawn location (in tile coordinates),
                    // and the keyboard class.
      changeLevel(
          Level
              .activeLevel); // Set the level to the current level (0, as we set that in the
                             // beginning)
      FileHandler.save(player); // Save everything
      initPause = false; // Set pause to false, just in case it is true
      c.clearTextArea(); // Clear the console
      System.out.println(
          "Controls: \n W/up -> Player up \n A/left -> Player left \n S/down -> Player down \n D/right -> Player right \n "
              + "Q -> Inventory \n Shift -> Sprint \n HOME -> home level \n M -> Map \n numbers 1-6 -> Weapons"); // Output the controls so the user can see what to do
    } else if (state == State.START) { // If the state is the start screen...
      sc = new StartScreen(); // Initiate the start screen
      uiManager.addPanel(
          sc); // Add the startscreen to the UI manager, which will render and update it
    } else if (state == State.PAUSE) { // If the state is in pause...
      pause = new Pause(); // Instantiate a new pause menu
      uiManager.addPanel(
          pause); // Add the pause menu to the UI manager, ready for rendering and updating
      initPause = true; // Tell the game we have created a pause menu (prevent more than 1 opening)
      paused = true; // Tell the game we are paused
    }
    STATE =
        State
            .WAITING; // Once the correct state is set up, set the state wo WAITING, which tells the
                      // game we are waiting for the state to change.
  }

  /*
   * Return the current JFrame
   */

  public JFrame getWindow() {
    return frame;
  }

  /*
   * A method used for changing the game's level, and transfering the game objects (e.g. player) to the new level
   */

  public void changeLevel(Level lev) {
    if (level != null) { // If the level exists...
      layerStack.remove(
          level); // remove it from the layer stack (stops it being rendered, updated, and recieving
                  // key events)
      level = lev; // Set level to the new level we wish to change to
      level.add(player); // Add the player to the new level
      level.setPlayerLocation(); // Set the location to the levels chosen location
      addLayer(
          level); // Add the new level into the layerstack for updating, rendering and receiving key
                  // events
    }
  }

  /*
   * Returns the current UI manager so other classes can add items to it
   */

  public static UIManager getUIManager() {
    return uiManager;
  }

  /*
   *  //Return the current UI map manager
   */

  public static UIManager getMapManager() {
    return minimapManager;
  }

  /*
   * //Return the value of the window Width, unscaled
   */

  public static int getWindowWidth() {
    return (width * scale);
  }

  /*
   * Return the value of the window Height unscaled
   */

  public static int getWindowHeight() {
    return (height * scale);
  }

  /*
   * Return the absolute window width
   */

  public static int getAbsoluteWidth() {
    return (absoluteWidth * scale);
  }

  /*
   * Return the absolute window height
   */

  public static int getAbsoluteHeight() {
    return (absoluteHeight * scale);
  }

  /*
   * Return the scale variable
   */

  public static int getScale() {
    return scale;
  }

  /*
   * Return the non-sttic instance of game
   */

  public static synchronized Game getGame() {
    // synchronized means that this method can be accessed by more than 1 thread at once. If one
    // thread accesses the variable, he other threads have to wait for the first thread
    // to finish before it can access the value. Otherwise the value may be changed by one thread,
    // then changed again by another, and the value isn't what was intended.
    return game;
  }

  /*
   * Returnt he current level
   */

  public static Level getLevel() {
    return level;
  }

  /*
   * Returns the current keyboard controller
   */

  public static Keyboard getKeyboard() {
    return key;
  }

  /*
   * Allows other classes to add layers into the stack
   */

  public void addLayer(Layer layer) {
    layerStack.add(layer);
  }

  /*
   * Allows other classes to remove layers from the stack
   */

  public void removeLayer(Layer layer) {
    layerStack.remove(layer);
  }

  /*
   * Allows other classes to add UI ayers to the UI stack
   */

  public void addUILayer(UILayer layer) {
    UILayerStack.add(layer);
  }

  /*
   * Alows other classes to remove UI layers from the UI stack
   */

  public void removeUILayer(UILayer layer) {
    UILayerStack.remove(layer);
  }

  /*
   * //Returns the image used for drawing the game
   */

  public BufferedImage getImage() {
    return image;
  }

  /*
   * Scales the dimensions given to have the same ratio of the screen width to the screen height
   */

  public static Dimension getScaledDimension(Dimension imgSize, Dimension boundary) {
    // Set up all the necessary variables
    int originalWidth = imgSize.width;
    int originalHeight = imgSize.height;
    int boundWidth = boundary.width;
    int boundHeight = boundary.height;
    int newWidth = originalWidth;
    int newHeight = originalHeight;

    // first check if we need to scale width
    if (originalWidth > boundWidth) {
      // scale width to fit
      newWidth = boundWidth;
      // scale height to maintain aspect ratio
      newHeight = (newWidth * originalHeight) / originalWidth;
    }

    // then check if we need to scale even with the new height
    if (newHeight > boundHeight) {
      // scale height to fit instead
      newHeight = boundHeight;
      // scale width to maintain aspect ratio
      newWidth = (newHeight * originalWidth) / originalHeight;
    }
    return new Dimension(newWidth, newHeight);
  }

  /*
   * //synchronized method to start the main thread
   */

  public synchronized void start() { // Starts the Thread running
    running = true;
    thread = new Thread(this, "Display"); // Initialises the Thread
    thread.start(); // Runs the Thread=
  }

  /*
   * Synchronized method to stop the main thread
   */

  public synchronized void stop() { // Stops the Thread
    running = false;
    try {
      thread.join();
    } catch (InterruptedException e) {
      e.printStackTrace(); // Catches any errors if the Thread crashes
    }
  }

  /*
   * This method is defualt. It is overriden from the thread superclass, and is automatically called when the thread is started
   */

  @Override
  public void run() {
    long lastTime =
        System
            .nanoTime(); // Get the current system time that the thread has been awake in
                         // nanoseconds
    long timer =
        System
            .currentTimeMillis(); // Get the current time the thread has been running in miliseconds
    final double ns = 1000000000.0 / 60.0; // Set up a frame-per-second lock at 60
    double delta = 0; // initiate delta
    int frames = 0; // initiate frames couner
    int updates = 0; // initiate updates counter
    requestFocus(); // request focus to the window to ensue we are calculating live variables
    while (running) { // If the thread is running...
      long now =
          System.nanoTime(); // Get the current time the thread has been running in nanoseconds
      delta +=
          (now - lastTime)
              / ns; // Increase delta by the time the last frame took to draw/, and divide it by the
                    // fps lock
      lastTime =
          now; // Set the last frame time to the current frame time ready for the next frame time to
               // be calculated
      render(); // Call the render method
      frames++; // Increase the frame counter
      while (delta
          >= 1) { // While delta is greater than or equal to 1 (The render will be called 60 times a
                  // second. Each render cycle the update method will be called every other second)
        update(); // Update the game
        updates++; // Increase the update counter
        delta--; // Decrease delta
      }

      if (System.currentTimeMillis() - timer
          > 1000) { // Updates the fps and ups counter every time the thread has run for 1000
                    // miliseconds (1 second)
        timer += 1000;
        frame.setTitle(updates + "UPS, " + frames + "FPS");
        updates = 0;
        frames = 0;
      }
    }
    stop(); // If we are no longer running, stop the thread
  }

  /*
   * The event method for EventListener
   */

  public void onEvent(Event event) {
    /* Loop through each layer in both stacks and pass the events to them.
     *  Chech the Ui first as it is on top. If the UI returns false
     *  (it hasn't handled the event), pass it to the game layers.
     */
    for (int i = UILayerStack.size() - 1; i >= 0; i--) {
      UILayerStack.get(i).onEvent(event);
    }
    for (int i = layerStack.size() - 1; i >= 0; i--) {
      layerStack.get(i).onEvent(event);
    }
  }

  /*
   * The main game update method. Handles updating all game items
   */

  public void update() {
    time++; // Increase the time value by one updated 60 times per second, so if (time % 60) == 0,
            // one second has passed)
    if (key.paused && !initPause)
      STATE =
          State
              .PAUSE; // If the pause key is being pressed and pause menue hasn't been made, set the
                      // game state to pause
    else if (key.paused
        && initPause) // If we press the pause key and we have created pause, return to the game
                      // state
    STATE = State.GAME;
    if (time % 1800 == 0
        && player != null) // If 30 seconds has passed, and the player exists, then save the game
    FileHandler.save(player);
    init(STATE); // Call the initiate state method, in case it is changed during the game
    key.update(); // Update the key detector

    // Update layers
    for (int i = 0; i < UILayerStack.size(); i++) {
      UILayerStack.get(i).update();
    }
    for (int i = 0; i < layerStack.size(); i++) {
      layerStack.get(i).update();
    }
  }

  /*
   * Main game render method. Handles rendering of all game objects to the image
   */

  public void render() {
    BufferStrategy bs =
        getBufferStrategy(); // Create a buffer strategy (a way of drawing pixels to the image)
    if (bs == null) { // If it is null...
      createBufferStrategy(
          3); // Create a buffer strategy. Set it up for 3D as it has a more recise pixel resolution
              // and more control
      return; // Exit the method
    }

    screen.clear(); // Clear the screen every render, ready for re-drawing

    Graphics g =
        bs.getDrawGraphics(); // Get the buffer strategy's default graphics object, used for drawing
    this.g = g; // Set the global graphics variable to the one we just fetched
    if (STATE != State.WAITING) { // If the game state isn't waiting...
      List<UIPanel> panels =
          uiManager.getPanels(); // Get the list of panels that are currently being drawn
      for (int i = 0; i < panels.size(); i++) { // For each one...
        uiManager.removePanel(panels.get(i)); // remove it.
      }
    }
    init(
        STATE); // Call initiate state to set up the game for if the state has changed during
                // rendering.
    if (player != null) { // If the player exists...
      double xScroll = player.getX() - screen.width / 2; // Set the x scroll variable of the map
      double yScroll = player.getY() - screen.height / 2; // Set the y scroll variable of the map
      level.setScroll((int) xScroll, (int) yScroll); // Tell the level these values
    }

    // Loop through the stacks, rendering each layer
    for (int i = 0; i < layerStack.size(); i++) {
      layerStack.get(i).render(screen);
    }
    // Set the pixels in this class to all the pixels stored in the screen class, ready for
    // rendering to the BufferedImage
    for (int i = 0; i < pixels.length; i++) {
      pixels[i] = screen.pixels[i];
    }

    g.setColor(
        new Color(
            0xff00ff)); // Set the background to a bright pink, so we know if anything isn't
                        // rendering correctly
    g.fillRect(0, 0, getWidth(), getHeight()); // Draw the rectangle to fill thr screen
    /* scale the image's size to fill the window, scaling the objects inside to also fit. This ensures the game will scale depending on
     *  scree resolution
     */
    int newWidth = 0, newHeight = 0;
    if (image.getWidth() / image.getHeight() < getAbsoluteWidth() / getAbsoluteHeight()) {
      newWidth = getAbsoluteWidth();
      newHeight =
          (int)
              Math.floor(
                  (double) image.getHeight()
                      * (double) getAbsoluteWidth()
                      / (double) image.getWidth());
    } else {
      newHeight = getAbsoluteHeight();
      newWidth =
          (int)
              Math.floor(
                  (double) image.getWidth()
                      * (double) getAbsoluteHeight()
                      / (double) image.getHeight());
    }

    g.drawImage(
        image, 0, 0, newWidth, newHeight,
        null); // Draw the image over the rectangle, filling the window

    // Loop through the stacks, rendering each layer. Do the UI as the last thing to render, as it
    // is on top of everything else
    for (int i = 0; i < UILayerStack.size(); i++) {
      UILayerStack.get(i).render(g);
    }
    g.dispose(); // Dispose of the graphics object as we no longer need it
    bs.show(); // Show the buffer strategy in the canvas
  }

  /*
   * The main mehod, which is a default method that is called when the jar is first run.
   */

  public static void main(String[] args) {
    if (!Installer.installed) { // If the game isn't installed, run the installer
      new Installer();
    } else { // Otherwise create the game
      startGame();
    }
  }
}
示例#3
0
  public static void startGame() {
    game = new Game(); // Create an instance of the class
    game.frame.setResizable(false); // Prevent the window from being allowed to be resized
    game.frame.setUndecorated(
        true); // Removes the bar around the edge of the window. Change to false to add this back
    game.frame.setLayout(
        new GridBagLayout()); // Create a layout pane for items to be positioned in the frame
                              // correctly
    GridBagConstraints gbc = new GridBagConstraints(); // Initiate a callable object of this layout
    gbc.anchor = GridBagConstraints.CENTER; // Center the objects we add past this point
    game.frame.add(
        game, gbc); // Add the game object (Canvas) to the layout manager, and thus the frame.

    game.frame.pack(); // Tell the frame we are done adding items
    game.frame.setDefaultCloseOperation(
        JFrame.DO_NOTHING_ON_CLOSE); // Tell the frame not to close if the user tries to exit

    /*
     * Sets up a custom way of exiting the game
     */

    WindowListener exitListener =
        new WindowAdapter() { // Default event class that triggers when the window is tried to be
                              // closed
          @Override // Declare we are overriding a method in the WindowListener class
          public void windowClosing(WindowEvent e) { // If the window is trying to close
            State preState = STATE; // Store the game state
            if (player != null) STATE = State.PAUSE; // If the player exists, pause the game
            int confirm =
                JOptionPane.showOptionDialog(
                    null,
                    "Are You Sure to Close Application?",
                    "Exit Confirmation",
                    JOptionPane.YES_NO_OPTION,
                    JOptionPane.QUESTION_MESSAGE,
                    null,
                    null,
                    null); // Open a yes/no message bos, asking if the user wants to exit
            if (confirm == 0
                && player
                    != null) { // If they want to exit and the player exists (They are in game)...
              int confirm2 =
                  JOptionPane.showOptionDialog(
                      null,
                      "Save Progress?",
                      "Save Confirmation",
                      JOptionPane.YES_NO_OPTION,
                      JOptionPane.QUESTION_MESSAGE,
                      null,
                      null,
                      null); // Open a message box asking if they want to save their progress
              if (confirm2 == 0) { // If they do...
                FileHandler.save(player); // Save
                System.exit(0); // Close the window
              } else {
                System.exit(0); // Otherwise just exit.
              }
            } else if (confirm == 0) { // If they want to exit while not in game...
              System.exit(0); // Close the window
            } else { // If they say no...
              STATE = preState; // Set the state back to the previous state
              paused = false; // Unpause the game
            }
          }
        };
    game.frame.addWindowListener(exitListener); // Set the windows exit listener to our custom one
    game.frame.setLocationRelativeTo(null); // Position the frame in the center of the screen
    game.frame.setVisible(true); // Set the frame to be visible
    game.frame.requestFocus(); // Set the game to have input focus
    game.start(); // Start the game thread
    s.loopSound("INTRO"); // Start the instroduction music
    game.frame.requestFocus(); // Request focus to ensure we haven't lost it
    System.out.println("Press Enter to start..."); // Tell the user the game has started
  }