Add even more documentation, fix keybind bug
parent
a94409dd12
commit
f83b5c41c8
|
@ -1 +0,0 @@
|
|||
Press P to skip to the next level.
|
|
@ -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",
|
||||
|
|
|
@ -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){
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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<TextBox> textBoxArray = new ArrayList<TextBox>();
|
||||
public ArrayList<TextBox> 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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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.");
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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<Middleware> 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<TextBox> 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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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.
|
Loading…
Reference in New Issue