From f83b5c41c872337eb68b380e57b9e831ea0ab971 Mon Sep 17 00:00:00 2001 From: John Date: Mon, 20 Jun 2022 17:19:49 -0700 Subject: [PATCH] Add even more documentation, fix keybind bug --- cheats.txt | 1 - src/GameFrame.java | 2 +- src/MenuPanel.java | 4 ++-- src/PauseMenu.java | 11 ++++++++- src/Player.java | 2 +- src/SafeObjectInputStream.java | 3 +++ src/SettingPanel.java | 44 +++++++++++++++++----------------- src/Sound.java | 16 ++++++++++--- src/SoundWrapper.java | 32 +++++-------------------- src/SpriteException.java | 3 +++ src/StickyBomb.java | 2 +- src/TextBox.java | 11 +++++++-- src/UtilityFunction.java | 19 ++++++++++++++- src/WallSign.java | 5 ++-- tips and tricks.txt | 3 +++ 15 files changed, 95 insertions(+), 63 deletions(-) delete mode 100644 cheats.txt create mode 100644 tips and tricks.txt diff --git a/cheats.txt b/cheats.txt deleted file mode 100644 index fecc29e..0000000 --- a/cheats.txt +++ /dev/null @@ -1 +0,0 @@ -Press P to skip to the next level. \ No newline at end of file diff --git a/src/GameFrame.java b/src/GameFrame.java index 1566cd3..42076e8 100644 --- a/src/GameFrame.java +++ b/src/GameFrame.java @@ -32,7 +32,7 @@ public class GameFrame extends JFrame{ // this ensures that attempting to execute malicious code by tampering with saves will be more difficult, as payloads would have to stick to these classes // please note that it is not a perfect mitigation though game = (GamePanel)FileManager.readObjectFromFile("local/game_state.dat", - Arrays.asList("GamePanel", "javax.swing.JPanel", "javax.swing.JComponent", "java.awt.Container", + Arrays.asList("FireBall", "GamePanel", "javax.swing.JPanel", "javax.swing.JComponent", "java.awt.Container", "java.awt.Component", "javax.swing.plaf.ColorUIResource", "java.awt.Color", "javax.swing.plaf.FontUIResource", "java.awt.Font", "java.util.Locale", "java.awt.Dimension", "java.awt.ComponentOrientation", "[Ljava.awt.Component;", "java.awt.FlowLayout", diff --git a/src/MenuPanel.java b/src/MenuPanel.java index b97e790..3864bb8 100644 --- a/src/MenuPanel.java +++ b/src/MenuPanel.java @@ -166,7 +166,7 @@ public class MenuPanel extends JPanel implements Runnable, KeyListener{ return false; } - //if a key is pressed, we'll send it over to the Player class for processing + //if a key is pressed, we'll process it public void keyPressed(KeyEvent e) { // intercept keypresses and replace them with previously defined keypresses through the Middleware class e = UtilityFunction.intercept(e, GameFrame.game.middlewareArray); @@ -224,7 +224,7 @@ public class MenuPanel extends JPanel implements Runnable, KeyListener{ } } } - //if a key is released, we'll send it over to the Player class for processing + // left empty public void keyReleased(KeyEvent e){ } diff --git a/src/PauseMenu.java b/src/PauseMenu.java index d428090..a55f6c0 100644 --- a/src/PauseMenu.java +++ b/src/PauseMenu.java @@ -1,3 +1,6 @@ +// Eric Li, Charlie Zhao, ICS4U, Finished 6/17/2022 +// Render the pause menu and offset text + import java.awt.*; import java.io.Serializable; @@ -5,21 +8,27 @@ public class PauseMenu extends TextBox implements Serializable { public boolean hasBorder; public PauseMenu(int y, int textYOffset, int xWidth, int yHeight, int totalWidth, Font font, String text, boolean hasBorder) { super(y, xWidth, yHeight, totalWidth, font, text, null); + // shift y up by the offset given this.y -= textYOffset; this.hasBorder = hasBorder; } public void drawCenteredTextBox(Graphics g, String text, Color backgroundColor, Color textColor) { + // draw background if backgroundColor is not null + // please note that later uses of this class (and TextBox) pass a fully transparent color to make debugging easier if (backgroundColor != null) { + // set color of border g.setColor(textColor); - // TODO: make drawn line widths consistent if (hasBorder) { ((Graphics2D) g).setStroke(new BasicStroke(4f)); } + // draw border g.drawRect(newX, newY, xWidth, yHeight); + // set color of rectangle and draw rectangle in border g.setColor(backgroundColor); g.fillRect(newX, newY, xWidth, yHeight); } + // set text color and draw centered string g.setColor(textColor); drawCenteredString(g, y, newX, xWidth, text); } diff --git a/src/Player.java b/src/Player.java index 785a174..42e5908 100644 --- a/src/Player.java +++ b/src/Player.java @@ -294,7 +294,7 @@ public class Player extends GenericSprite { } public void reset() throws UnsupportedAudioFileException, LineUnavailableException, IOException { - SoundWrapper.playSound("sound/OOF.wav"); + UtilityFunction.playSound("sound/OOF.wav"); holdingSteel = false; LevelManager.setLevel(GameFrame.game.level, true); GameFrame.game.camera.x = LevelManager.xSpawn; diff --git a/src/SafeObjectInputStream.java b/src/SafeObjectInputStream.java index d0e4715..3eb57b5 100644 --- a/src/SafeObjectInputStream.java +++ b/src/SafeObjectInputStream.java @@ -1,3 +1,6 @@ +// Eric Li, Charlie Zhao, ICS4U, Finished 6/15/2022 +// Ensures that no arbitrary classes are loaded from the save files + import java.io.IOException; import java.io.InputStream; import java.io.ObjectInputStream; diff --git a/src/SettingPanel.java b/src/SettingPanel.java index 5198958..b32cb7c 100644 --- a/src/SettingPanel.java +++ b/src/SettingPanel.java @@ -1,6 +1,7 @@ -/* GameFrame.game.class acts as the main "game loop" - continuously runs the game and calls whatever needs to be called +// Eric Li, Charlie Zhao, ICS4U, Finished 6/20/2022 +/* SettingPanel acts as the JPanel that controls the settings; it mostly creates new Middleware and adds that Middleware to GamePanel -Child of JPanel because JPanel contains methods for drawing to the screen +Child of MenuPanel because it shares most of the characteristics, and child of JPanel because JPanel contains methods for drawing to the screen Implements KeyListener interface to listen for keyboard input @@ -24,30 +25,30 @@ public class SettingPanel extends MenuPanel { public TextBox title; public TextBox up, down, left, right, tip; - public ArrayList textBoxArray = new ArrayList(); + public ArrayList textBoxArray = new ArrayList<>(); public Font standardFont = new Font(Font.MONOSPACED, Font.BOLD, 60); public Font smallFont = new Font(Font.MONOSPACED, Font.PLAIN, 40); - public Font smallerFont = new Font(Font.MONOSPACED, Font.ITALIC, 24); - public int playerFrame, enemyFrame; - public boolean waitForKey, notFirstWait; + public Font smallerFont = new Font(Font.MONOSPACED, Font.ITALIC + Font.BOLD, 24); + public boolean waitForKey; public int lastKeyCode = -1; public int currentBox = 0; public PauseMenu pauseMenu; public SettingPanel(CameraPanel gameFrame) throws IOException, SpriteException, UnsupportedAudioFileException, LineUnavailableException { super(gameFrame); - + // initialize new textboxes and add the textboxes to the selectable textbox array title = new TextBox(100, 400, 100, GAME_WIDTH, standardFont, "Settings", null); up = new TextBox(300, 600, 50, GAME_WIDTH, smallFont, "Up", Integer.toString(KeyEvent.VK_W)); - down = new TextBox(350, 600, 50, GAME_WIDTH, smallFont, "Down", Integer.toString(KeyEvent.VK_S)); - left = new TextBox(400, 600, 50, GAME_WIDTH, smallFont, "Left", Integer.toString(KeyEvent.VK_A)); + down = new TextBox(400, 600, 50, GAME_WIDTH, smallFont, "Down", Integer.toString(KeyEvent.VK_S)); + left = new TextBox(350, 600, 50, GAME_WIDTH, smallFont, "Left", Integer.toString(KeyEvent.VK_A)); right = new TextBox(450, 600, 50, GAME_WIDTH, smallFont, "Right", Integer.toString(KeyEvent.VK_D)); tip = new TextBox(500, 600, 50, GAME_WIDTH, smallerFont, "TIP: Press ESC to return to the main menu", null); textBoxArray.add(up); textBoxArray.add(left); textBoxArray.add(down); textBoxArray.add(right); - + // initialize new menu + // this menu is displayed when the player is rebinding a key pauseMenu = new PauseMenu(GAME_HEIGHT/2, 0, 400, 400, GAME_WIDTH, smallFont, "Enter your key", true); } @@ -83,14 +84,16 @@ public class SettingPanel extends MenuPanel { } } - // move is repurposed to change key bind + // doAction is used to change key bind public void doAction() { changeKeyBind(); } + // check if mouse is hovering over textbox public boolean hoverCheck(MouseEvent e) { for (TextBox t: textBoxArray) { if (t.isHover(e.getX(), e.getY())) { + // set currentBox to the one the mouse is hovering over currentBox = textBoxArray.indexOf(t); return true; } @@ -98,7 +101,9 @@ public class SettingPanel extends MenuPanel { return false; } + // change the keybind for a specific control (e.x., rebinding jump from W to another key) public void changeKeyBind() { + // the player can press escape to cancel rebinding keys if (lastKeyCode == KeyEvent.VK_ESCAPE) { lastKeyCode = -1; } else if (lastKeyCode != -1) { @@ -113,32 +118,27 @@ public class SettingPanel extends MenuPanel { GameFrame.game.middlewareArray.add(new Middleware(-1, Integer.parseInt(textBoxArray.get(currentBox).id))); // lastKeyCode is set to -1 to prevent endless execution lastKeyCode = -1; - - // save new key bind to file - try { - FileManager.writeObjectToFile("local/controls", GameFrame.game.middlewareArray); - } catch (IOException e) { - e.printStackTrace(); - } } } - //if a key is pressed, we'll send it over to the Player class for processing + //if a key is pressed, we'll process it public void keyPressed(KeyEvent e) { + // if the key is not being rebound right now, intercept it if (!waitForKey) { e = UtilityFunction.intercept(e, GameFrame.game.middlewareArray); } + // if the key is being rebound, process the rebinding if (waitForKey) { if (e.getKeyCode() != KeyEvent.VK_ENTER) { lastKeyCode = e.getKeyCode(); waitForKey = false; } - } else if (e.getKeyCode() == KeyEvent.VK_ESCAPE) { + } else if (e.getKeyCode() == KeyEvent.VK_ESCAPE) { // if the player presses ESC, return to the main menu ((CardLayout)gameFrame.getLayout()).show(gameFrame, "menu"); - } else if (e.getKeyCode() == KeyEvent.VK_ENTER) { + } else if (e.getKeyCode() == KeyEvent.VK_ENTER) { // if the player presses ENTER, rebind the key that is the id of the currently selected textbox // logic for changing keys starts here waitForKey = true; - } else { + } else { // otherwise, move the currently selected textbox up or down currentBox = UtilityFunction.processBox(e, currentBox, textBoxArray); } } diff --git a/src/Sound.java b/src/Sound.java index 9ad298e..c8fa995 100644 --- a/src/Sound.java +++ b/src/Sound.java @@ -1,20 +1,30 @@ +// Eric Li, Charlie Zhao, ICS4U, Finished 6/20/2022 +// Utility class to make interacting with sound files easier + import javax.sound.sampled.*; import java.io.File; import java.io.IOException; import java.io.Serializable; public class Sound implements Serializable { - private AudioInputStream audioInputStream; - private Clip clip; - private File file; + public AudioInputStream audioInputStream; + public Clip clip; + public File file; public Sound(String filePath) throws UnsupportedAudioFileException, IOException, LineUnavailableException { file = new File(filePath); audioInputStream = AudioSystem.getAudioInputStream(file); clip = AudioSystem.getClip(); clip.open(audioInputStream); } + // start playing sound public void start(){ clip.setFramePosition(0); clip.start(); } + + // while close() is not used because the same sounds are either constantly used or immediately dereferenced (and therefore collected by the GC) + // it is still good practice to enable closing sounds after finishing using them + public void close() { + clip.close(); + } } diff --git a/src/SoundWrapper.java b/src/SoundWrapper.java index f1e0c26..9788899 100644 --- a/src/SoundWrapper.java +++ b/src/SoundWrapper.java @@ -1,3 +1,7 @@ +// Eric Li, Charlie Zhao, ICS4U, Finished 6/17/2022 +// A wrapper that makes the Sound class serializable +// Please note that this is currently superseded by UtilityFunction.playSound, but is included for extensibility purposes + import javax.sound.sampled.LineUnavailableException; import javax.sound.sampled.UnsupportedAudioFileException; import java.io.*; @@ -6,20 +10,6 @@ public class SoundWrapper implements Serializable { transient public Sound sound; public String soundString; - public static Sound grass; - - static { - try { - grass = new Sound("sound/grass.wav"); - } catch (UnsupportedAudioFileException e) { - throw new RuntimeException(e); - } catch (IOException e) { - throw new RuntimeException(e); - } catch (LineUnavailableException e) { - throw new RuntimeException(e); - } - } - // please note that not as many constructors were implemented as BufferedImage, as this class was created before most sounds were added; // as such, backwards compatibility was not needed public SoundWrapper(String soundLocation) throws UnsupportedAudioFileException, LineUnavailableException, IOException { @@ -31,25 +21,15 @@ public class SoundWrapper implements Serializable { @Serial private void writeObject(ObjectOutputStream out) throws IOException { + // write location of .wav file (soundString) out.writeObject(soundString); } @Serial private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException, UnsupportedAudioFileException, LineUnavailableException { + // read .wav file located at soundString Object o; o = in.readObject(); sound = new Sound((String)o); } - public static void playSound(String filePath) throws UnsupportedAudioFileException, LineUnavailableException, IOException { - Sound sound = new Sound(filePath); - if (sound == null) { - try { - sound = new Sound(filePath); - } catch (UnsupportedAudioFileException | LineUnavailableException | IOException e) { - throw new RuntimeException(e); - } - } - sound.start(); - } - } diff --git a/src/SpriteException.java b/src/SpriteException.java index cedf600..9a01cfc 100644 --- a/src/SpriteException.java +++ b/src/SpriteException.java @@ -1,3 +1,6 @@ +// Eric Li, Charlie Zhao, ICS4U, Finished 5/30/2022 +// Exception that occurs when the sprite is not of the right dimensions + public class SpriteException extends Exception { public SpriteException() { super("Tile sprites must have equal lengths and heights."); diff --git a/src/StickyBomb.java b/src/StickyBomb.java index b5ee74e..93d2cff 100644 --- a/src/StickyBomb.java +++ b/src/StickyBomb.java @@ -54,7 +54,7 @@ public class StickyBomb extends GenericSprite implements Serializable { // } // } // explode.start(); - SoundWrapper.playSound("sound/explode.wav"); + UtilityFunction.playSound("sound/explode.wav"); double yDis = GameFrame.game.player.y+Player.PLAYER_HEIGHT/2-(y+(double)length/2); double xDis = GameFrame.game.player.x+Player.PLAYER_WIDTH/2-(realX+(double)length/2); double hypo = Math.sqrt(yDis*yDis+xDis*xDis); diff --git a/src/TextBox.java b/src/TextBox.java index 5148c25..f3b0142 100644 --- a/src/TextBox.java +++ b/src/TextBox.java @@ -1,3 +1,7 @@ +// Eric Li, Charlie Zhao, ICS4U, Finished 6/17/2022 +// Creates a centered textbox with centered text +// The textbox can also have a filled background + import java.awt.*; import java.io.Serializable; @@ -20,7 +24,7 @@ public class TextBox extends Rectangle implements Serializable { this.id = id; } - // isHover is not needed for now and is also probably implemented badly + // checks if a mouse is hovering over the textbox public boolean isHover(int x, int y) { if (x >= newX && x <= newX + xWidth) { return (y >= newY && y <= newY + yHeight); @@ -28,15 +32,19 @@ public class TextBox extends Rectangle implements Serializable { return false; } + // draws the centered textbox public void drawCenteredTextBox(Graphics g, String text, Color backgroundColor, Color textColor) { + // if the backgroundColor is not null, draw a rectangle filled with that color if (backgroundColor != null) { g.setColor(backgroundColor); g.fillRect(newX, newY, xWidth, yHeight); } + // set text color and draw string g.setColor(textColor); drawCenteredString(g, y, newX, xWidth, text); } + // draws the centered string public static void drawCenteredString(Graphics g, int y, int x, int xWidth, String text) { int newX, newY; // get font size @@ -49,7 +57,6 @@ public class TextBox extends Rectangle implements Serializable { g.drawString(text, newX, newY); } - // TODO: make this good public void draw(Graphics g, Color backgroundColor, Color textColor) { g.setFont(font); drawCenteredTextBox(g, text, backgroundColor, textColor); diff --git a/src/UtilityFunction.java b/src/UtilityFunction.java index e1edcf3..62655b9 100644 --- a/src/UtilityFunction.java +++ b/src/UtilityFunction.java @@ -1,12 +1,21 @@ +// Eric Li, Charlie Zhao, ICS4U, Finished 6/20/2022 +// Utility functions to help with common tasks + +import javax.sound.sampled.LineUnavailableException; +import javax.sound.sampled.UnsupportedAudioFileException; import java.awt.event.KeyEvent; +import java.io.IOException; import java.util.ArrayList; +// class is final because no objects will be made from it public final class UtilityFunction { - private UtilityFunction(){} + // intercept keystroke public static KeyEvent intercept(KeyEvent e, ArrayList middlewareArray) { + // iterate through each Middleware object in middlewareArray for (Middleware m: middlewareArray) { if (m.canIntercept(e)) { + // call interceptKey to intercept the key e = m.interceptKey(e); return e; } @@ -14,6 +23,7 @@ public final class UtilityFunction { return e; } + // process VK_UP or VK_DOWN inputs to change the currently selected box public static int processBox(KeyEvent e, int currentBox, ArrayList textBoxArray) { if (e.getKeyCode() == KeyEvent.VK_UP || e.getKeyCode() == KeyEvent.VK_W) { // if currentBox > 0, subtract one @@ -27,7 +37,14 @@ public final class UtilityFunction { return currentBox; } + // randomly generate an integer from low to high public static int randInt(int low, int high){ return (int)(Math.random()*(high-low+1))+low; } + + // start playing a sound that is located at filePath + public static void playSound(String filePath) throws UnsupportedAudioFileException, LineUnavailableException, IOException { + Sound sound = new Sound(filePath); + sound.start(); + } } diff --git a/src/WallSign.java b/src/WallSign.java index d013d44..de8bdaf 100644 --- a/src/WallSign.java +++ b/src/WallSign.java @@ -1,3 +1,6 @@ +// Eric Li, Charlie Zhao, ICS4U, Finished 6/17/2022 +// Draws signs on the walls of levels + import java.awt.*; import java.io.Serializable; @@ -8,8 +11,6 @@ public class WallSign extends TextBox implements Serializable { this.newX = x; this.y = y; } - - // TODO: flip public void draw(Graphics g, Color textColor) { int oldX = this.newX; newX -= GameFrame.game.camera.x; diff --git a/tips and tricks.txt b/tips and tricks.txt new file mode 100644 index 0000000..4dac2f5 --- /dev/null +++ b/tips and tricks.txt @@ -0,0 +1,3 @@ +Press P to skip to the next level. +Delete local/game_state if you bound the wrong controls and are not having a good time. +Use bombs sparingly. \ No newline at end of file