diff --git a/doc/index.md b/doc/index.md deleted file mode 100644 index 9256125..0000000 --- a/doc/index.md +++ /dev/null @@ -1,25 +0,0 @@ -# Function Overview - -This document intended to assist developers who seek to extend the game by modifying source code in src/. - -## Classes - -### BackgroundImage - -Its constructor takes the following arguments: - -`int x, int y, BufferedImage backgroundImage, int width, int height, int parallaxRatio` - -Draws a background image that moves one pixel every `parallaxRatio` pixels when `BackgroundImage.draw()` is called. - -### Camera - -placeholder - -## Utility Functions - -These functions are located in the `public final` class UtilityFunction; please note that the UtilityFunction constructor is private to prevent initialization. - -### readFromFile - -placeholder \ No newline at end of file diff --git a/sound/OOF.wav b/sound/OOF.wav index c7f7e8f..5d7e6b8 100644 Binary files a/sound/OOF.wav and b/sound/OOF.wav differ diff --git a/src/FileManager.java b/src/FileManager.java index 6a3127f..9d1d0c0 100644 --- a/src/FileManager.java +++ b/src/FileManager.java @@ -1,20 +1,17 @@ +// Eric Li, Charlie Zhao, ICS4U, Finished 2022-06-17 //This class allows us to input and output flies useful for -//Inputing levels and making save data. +// inputting levels and making save data. import java.io.*; import java.util.ArrayList; import java.util.List; import java.util.Scanner; // TODO: close scanner etc after use -public class FileManager { - public static void writeFile(String fileLocation, String writeString) throws IOException { - File newFile = new File(fileLocation); - FileWriter fileWriter = new FileWriter(newFile); - fileWriter.write(writeString); - fileWriter.close(); - } +public final class FileManager { // will create file if it doesn't exist + // does not include robust file closing as readFile is simpler and less likely to fail during reading + // additionally, even failing won't affect readFile as reading is a non-exclusive action (a lock will not prevent it) public static String readFile(String fileLocation) throws IOException { File newFile = new File(fileLocation); if (newFile.createNewFile()) { @@ -28,35 +25,48 @@ public class FileManager { } } + // includes robust file closing public static Object readObjectFromFile(String fileLocation, List allowedObject) throws IOException, ClassNotFoundException { ObjectInputStream objectStream; Object o; FileInputStream fileStream = new FileInputStream(fileLocation); + // if the allowedObject list is not "Any", ensure that the objects loaded are in the list + // this can help mitigate the security risk with deserializing untrusted files if (!allowedObject.contains("Any")) { objectStream = new SafeObjectInputStream(fileStream, allowedObject); } else { + // otherwise, use the unsafe class objectStream = new ObjectInputStream(fileStream); } try { o = objectStream.readObject(); + return o; } catch (Exception e) { // please note that the broad exception Exception was used here // as in the event of any exception, the object should still be closed - // additionally, the exception is reraised, so no information is lost from being too coarse + // additionally, the exception is re-raised, so no information is lost from being too coarse objectStream.close(); fileStream.close(); throw e; + } finally { + objectStream.close(); + fileStream.close(); } - objectStream.close(); - fileStream.close(); - return o; } + // includes robust file closing public static void writeObjectToFile(String fileLocation, Object o) throws IOException { FileOutputStream fileStream = new FileOutputStream(fileLocation); ObjectOutputStream objectStream = new ObjectOutputStream(fileStream); - objectStream.writeObject(o); - objectStream.close(); - fileStream.close(); + try { + objectStream.writeObject(o); + // please note that a less broad exception was used here compared to readObject, as the only "safe" exception to raise in this circumstance is IOException + } catch (IOException e) { + throw new RuntimeException(e); + } finally { + // close resources used even if writing caused an error + objectStream.close(); + fileStream.close(); + } } } diff --git a/src/GamePanel.java b/src/GamePanel.java index e75c789..e9bbdde 100644 --- a/src/GamePanel.java +++ b/src/GamePanel.java @@ -1,4 +1,6 @@ -/* GamePanel class acts as the main "game loop" - continuously runs the game and calls whatever needs to be called +/* Eric Li, Charlie Zhao, ICS4U, Finished 2022-06-20 + +GamePanel class acts as the main "game loop" - continuously runs the game and calls whatever needs to be called Child of JPanel because JPanel contains methods for drawing to the screen @@ -37,6 +39,7 @@ public class GamePanel extends JPanel implements Runnable, KeyListener, Serializ public static Font loreFont = new Font(Font.MONOSPACED, Font.ITALIC + Font.BOLD, 36); public static Color tutorialColor = Color.darkGray; public static Color loreColor = Color.lightGray; + public PauseMenu loadingMenu; public int bombCount; @@ -45,7 +48,7 @@ public class GamePanel extends JPanel implements Runnable, KeyListener, Serializ public transient Thread gameThread; public transient Image image; public transient Graphics graphics; - public transient boolean isNewStart = false; + public transient boolean isNewStart; public Player player; public BackgroundImage background, cloudOneBackground, cloudTwoBackground, cloudThreeBackground; public int playerFrame, enemyFrame; @@ -58,22 +61,19 @@ public class GamePanel extends JPanel implements Runnable, KeyListener, Serializ public DialogueMenu dialogueMenu; public ArrayList dialogueArray = new ArrayList(); - public BufferedImageWrapper[][][] playerSpriteArray = new BufferedImageWrapper[2][2][11]; public BufferedImageWrapper[][][] slimeSpriteArray = new BufferedImageWrapper[2][2][3]; public BufferedImageWrapper[] explosionArray = new BufferedImageWrapper[9]; - //public static ArrayListmap = new ArrayList(); - public Tile[][]map = new Tile[1000][18]; - public ArrayList middlewareArray = new ArrayList(); + public ArrayList middlewareArray = new ArrayList<>(); - public ArrayListparticleTiles = new ArrayList(); + public ArrayListparticleTiles = new ArrayList<>(); - public ArrayListshootingTiles = new ArrayList(); + public ArrayListshootingTiles = new ArrayList<>(); - public ArrayListfireballs = new ArrayList(); - public ArrayListenemy = new ArrayList(); + public ArrayListfireballs = new ArrayList<>(); + public ArrayListenemy = new ArrayList<>(); public ArrayListbombs = new ArrayList<>(); public BombDirectionShow bombDir = null; public ArrayListparticles = new ArrayList(); @@ -81,32 +81,35 @@ public class GamePanel extends JPanel implements Runnable, KeyListener, Serializ // image imports begin here public BufferedImageWrapper backgroundImage = new BufferedImageWrapper(("img/backgrounds/pointyMountains.png")); - public BufferedImageWrapper box = new BufferedImageWrapper(("img/tiles/boxes/box.png")); - public BufferedImageWrapper boxCoin = new BufferedImageWrapper(("img/tiles/boxes/boxCoin.png")); public BufferedImageWrapper cloud1 = new BufferedImageWrapper(("img/backgrounds/cloud1.png")); public BufferedImageWrapper cloud2 = new BufferedImageWrapper(("img/backgrounds/cloud2.png")); public BufferedImageWrapper cloud3 = new BufferedImageWrapper(("img/backgrounds/cloud3.png")); public BufferedImageWrapper bomb; public BufferedImageWrapper narratorPortrait = new BufferedImageWrapper(("img/dialogue/Gunther.png")); - public BufferedImageWrapper villainPortrait = new BufferedImageWrapper(("img/dialogue/Bouncer.png")); - public String lastText; public boolean isContinue; public boolean isRunning; public ArrayList tutorialSign = new ArrayList(); public ArrayList loreSign = new ArrayList(); + public boolean isLoaded; public GamePanel(JPanel gameFrame) throws IOException, SpriteException, UnsupportedAudioFileException, LineUnavailableException { + // set gameFrame to enable switching between different panels (in the gameFrame) this.gameFrame = gameFrame; camera = new Camera(0); + // create background mountains and images background = new BackgroundImage(0, 0, backgroundImage, GAME_WIDTH, GAME_HEIGHT, 10, camera); cloudOneBackground = new BackgroundImage(200, 200, cloud1, cloud1.image.getWidth(), cloud1.image.getHeight(), 5, camera); cloudTwoBackground = new BackgroundImage(600, 250, cloud2, cloud2.image.getWidth(), cloud3.image.getHeight(), 5, camera); cloudThreeBackground = new BackgroundImage(1000, 200, cloud3, cloud2.image.getWidth(), cloud3.image.getHeight(), 5, camera); + // create pause menu and text in pause menu pauseMenu = new PauseMenu(GAME_HEIGHT/2, 100, 400, 400, GAME_WIDTH, new Font(Font.MONOSPACED, Font.BOLD, 60), "Paused", true); pauseMenuExitOne = new PauseMenu(GAME_HEIGHT/2, 0, 400, 400, GAME_WIDTH, new Font(Font.MONOSPACED, Font.BOLD, 24), "Press ENTER to return", true); pauseMenuExitTwo = new PauseMenu(GAME_HEIGHT/2, -20, 400, 400, GAME_WIDTH, new Font(Font.MONOSPACED, Font.BOLD, 24), "to the main menu", true); pauseMenuResume = new PauseMenu(GAME_HEIGHT/2, -50, 400, 400, GAME_WIDTH, new Font(Font.MONOSPACED, Font.BOLD, 18), "(or press ESC to resume)", true); + // create menu shown when loading game + loadingMenu = new PauseMenu(GAME_HEIGHT/2, 0, GAME_WIDTH, GAME_HEIGHT, GAME_WIDTH, new Font(Font.MONOSPACED, Font.BOLD, 60), "Loading...", true); + // create text box shown during dialogue dialogueMenu = new DialogueMenu(GAME_HEIGHT-100, 200, new Font(Font.MONOSPACED, Font.BOLD, 20), narratorPortrait, true); try { // load player sprites from disk here @@ -138,13 +141,15 @@ public class GamePanel extends JPanel implements Runnable, KeyListener, Serializ // load bomb sprites bomb = new BufferedImageWrapper(("img/misc/bomb.png")); } catch (IOException e) { - e.printStackTrace(); + e.printStackTrace(); // TODO: remove stack trace } player = new Player(GAME_WIDTH/2, GAME_HEIGHT/2, playerSpriteArray); //create a player controlled player, set start location to middle of screenk - // the height of 35 is set because it is half of the original tile height (i.e., 70px) + // add mouse and keyboard listeners addUserInterface(); this.setPreferredSize(new Dimension(GAME_WIDTH, GAME_HEIGHT)); + // indicate that dialogue should be shown on load isNewStart = true; + // allow while look in run() to run isRunning = true; } @@ -153,7 +158,7 @@ public class GamePanel extends JPanel implements Runnable, KeyListener, Serializ this.addKeyListener(this); //start listening for keyboard input // request focus when the CardLayout selects this game requestFocusable(); - //add the MousePressed method from the MouseAdapter - by doing this we can listen for mouse input. We do this differently from the KeyListener because MouseAdapter has SEVEN mandatory methods - we only need one of them, and we don't want to make 6 empty methods + // add the mouse adapter methods addMouseListener(); } @@ -177,10 +182,8 @@ public class GamePanel extends JPanel implements Runnable, KeyListener, Serializ } else if (!isDialogue && !isPaused) { try { player.mouseReleased(e); - } catch (IOException ex) { - throw new RuntimeException(ex); - } catch (SpriteException ex) { - throw new RuntimeException(ex); + } catch (IOException | SpriteException ex) { + throw new RuntimeException(ex); // TODO: remove stack trace } } } @@ -192,10 +195,8 @@ public class GamePanel extends JPanel implements Runnable, KeyListener, Serializ public void mouseDragged(MouseEvent e) { try { player.mouseDragged(e); - } catch (IOException ex) { - throw new RuntimeException(ex); - } catch (SpriteException ex) { - throw new RuntimeException(ex); + } catch (IOException | SpriteException ex) { + throw new RuntimeException(ex); // TODO: remove stack } } public void mouseMoved(MouseEvent e) { @@ -216,12 +217,12 @@ public class GamePanel extends JPanel implements Runnable, KeyListener, Serializ }); } - // startThread is to be called after the game has started to avoid any issues TODO: better explanation + // startThread is to be called after the game has started to avoid any issues + // this allows serialization to work without having to implement a custom readObject method public void startThread() { //make this class run at the same time as other classes (without this each class would "pause" while another class runs). By using threading we can remove lag, and also allows us to do features like display timers in real time! gameThread = new Thread(this); gameThread.start(); - //System.out.println(gameThread); } //paint is a method in java.awt library that we are overriding. It is a special method - it is called automatically in the background in order to update what appears in the window. You NEVER call paint() yourself @@ -230,12 +231,8 @@ public class GamePanel extends JPanel implements Runnable, KeyListener, Serializ image = createImage(GAME_WIDTH, GAME_HEIGHT); //draw off screen graphics = image.getGraphics(); try { - draw(graphics, playerFrame, enemyFrame);//update the positions of everything on the screen - } catch (IOException e) { - throw new RuntimeException(e); - } catch (UnsupportedAudioFileException e) { - throw new RuntimeException(e); - } catch (LineUnavailableException e) { + draw(graphics, playerFrame);//update the positions of everything on the screen + } catch (IOException | LineUnavailableException | UnsupportedAudioFileException e) { throw new RuntimeException(e); } g.drawImage(image, 0, 0, this); //move the image on the screen @@ -243,7 +240,8 @@ public class GamePanel extends JPanel implements Runnable, KeyListener, Serializ } //call the draw methods in each class to update positions as things move - public void draw(Graphics g, int playerFrame, int enemyFrame) throws IOException, UnsupportedAudioFileException, LineUnavailableException { + public void draw(Graphics g, int playerFrame) throws IOException, UnsupportedAudioFileException, LineUnavailableException { + // save old color and font to enable resetting to these after drawing the tutorial Color oldColor = g.getColor(); Font oldFont = g.getFont(); background.draw(g); @@ -260,10 +258,10 @@ public class GamePanel extends JPanel implements Runnable, KeyListener, Serializ g.setColor(oldColor); g.setFont(oldFont); if (isPaused || isDialogue) { - // set player frame to 0 to prevent frame from changing when player moves - playerFrame = 0; + // set player frame to 7 to prevent frame from changing when player inputs key presses + playerFrame = 7; Graphics2D g2d = (Graphics2D)(g); - // remove extraneous details from game when paused + // remove extraneous details (like bomb counter) from game when paused g2d.setPaint(Color.white); } //Don't want to draw off screen items @@ -276,11 +274,15 @@ public class GamePanel extends JPanel implements Runnable, KeyListener, Serializ } } } + // this is not an enhanced for loop to prevent concurrent modification exception for(int i=0; i0&&!player.holdingSteel){ - bombDir = new BombDirectionShow(this.player.x + this.camera.x + player.WIDTH/2, this.player.y+player.HEIGHT/2, - (player.mouseX - this.player.x) / 20, (player.mouseY - this.player.y) / 10); + (Player.mouseX - this.player.x) / 20, (Player.mouseY - this.player.y) / 10); bombDir.draw(g); } - + // render steel being held based on mouse position + // additionally, change color of steel depending on whether the player can place the steel or not if(player.holdingSteel){ String filePath = ""; if(player.canPlaceSteel){ @@ -328,50 +335,66 @@ public class GamePanel extends JPanel implements Runnable, KeyListener, Serializ double y = ((player.mouseY / Tile.length))*Tile.length; g.drawImage(getImage(filePath),x*Tile.length - (GamePanel.GAME_WIDTH/2)-camera.x,(int)y,Tile.length,Tile.length,null); } - - + // draw bomb counter (bomb image and amount of bombs remaining) g.drawImage(bomb.image,20,20,35,35,null); g.drawString("X"+LevelManager.bombs,60,40); if (isPaused) { + // cover background with translucent rectangle g.setColor(new Color(255, 255, 255, 100)); g.fillRect(0, 0, GAME_WIDTH, GAME_HEIGHT); + // draw pauseMenu rectangle and "Paused" text pauseMenu.draw(g, Color.white, Color.black); + // draw instructions in pause menu pauseMenuExitOne.draw(g, new Color(0,0, 0, 0), Color.gray); pauseMenuExitTwo.draw(g, new Color(0,0, 0, 0), Color.gray); pauseMenuResume.draw(g, new Color(0,0, 0, 0), Color.gray); } else if (isDialogue) { + // cover background with translucent rectangle g.setColor(new Color(255, 255, 255, 100)); g.fillRect(0, 0, GAME_WIDTH, GAME_HEIGHT); try { if (waitForDialogue) { + // draws complete dialogue sentence dialogueMenu.currentFrame = dialogueArray.get(0).length(); dialogueMenu.frameCounter = 0; dialogueMenu.draw(g, dialogueArray.get(0), Color.white, Color.black); - } else if (dialogueMenu.draw(g, dialogueArray.get(0), Color.white, Color.black)) { + } else if (dialogueMenu.draw(g, dialogueArray.get(0), Color.white, Color.black)) { // draws partial sentence + // if the partial sentence is the same length as the complete sentence + // indicate that the game should draw the complete sentence in the future waitForDialogue = true; } } catch (IndexOutOfBoundsException e) { + // if there is no more dialogue to draw, this means that all the dialogue has already been iterated over and removed + // therefore, stop drawing the partially translucent rectangle and resume the game isDialogue = false; - // throw new RuntimeException(e); } } + if (!isLoaded) { + // if the game is not loaded, draw the loading screen + loadingMenu.draw(g, Color.white, Color.black); + } } //call the move methods in other classes to update positions //this method is constantly called from run(). By doing this, movements appear fluid and natural. If we take this out the movements appear sluggish and laggy public void move() throws IOException, UnsupportedAudioFileException, LineUnavailableException { + // move player character player.move(); + // move all enemies for (NonPlayer n: enemy) { n.move(); } + // move bombs flying through the air for(int i=0; i= GAME_HEIGHT - Player.PLAYER_HEIGHT) { player.y = GAME_HEIGHT - Player.PLAYER_HEIGHT; player.yVelocity = 0; player.isGrounded = true; } - if (player.x <= 0) { - player.x = 0; - } - if (player.x + Player.PLAYER_WIDTH >= GAME_WIDTH) { - player.x = GAME_WIDTH - Player.PLAYER_WIDTH; - } + // prevent enemies from falling through the floor + // also added to help level editing for (NonPlayer n: enemy) { if (n.y >= GAME_HEIGHT - n.npcHeight) { n.y = GAME_HEIGHT - n.npcHeight; @@ -422,6 +447,8 @@ public class GamePanel extends JPanel implements Runnable, KeyListener, Serializ //run() method is what makes the game continue running without end. It calls other methods to move objects, check for collision, and update the screen public void run() { + // if the game was not serialized but rather started, initialize the map through LevelManager + // also enable dialogue if (isNewStart) { LevelManager.setLevel(1); try { @@ -433,15 +460,16 @@ public class GamePanel extends JPanel implements Runnable, KeyListener, Serializ } else { LevelManager.bombs = bombCount; } - //System.out.println("done111"); - //the CPU runs our game code too quickly - we need to slow it down! The following lines of code "force" the computer to get stuck in a loop for short intervals between calling other methods to update the screen. + // let draw() know that the game is done loading so it can stop drawing the loading screen + isLoaded = true; + //the CPU runs our game code too quickly - we need to slow it down! The following lines of code "force" the computer to get stuck in a loop for short intervals between calling other methods to update the screen. long lastTime = System.nanoTime(); double amountOfTicks = 60; double ns = 1000000000/amountOfTicks; double delta = 0; long now; int fireballCounter = 0; - while(isRunning){ //this is the game loop, terminates on game restart to prevent race conditions + while(isRunning){ //this is the game loop, terminates on game restart to prevent race conditions. Also reduces lag now = System.nanoTime(); delta = delta + (now-lastTime)/ns; lastTime = now; @@ -452,15 +480,14 @@ public class GamePanel extends JPanel implements Runnable, KeyListener, Serializ // only perform game functions if game is not paused or in dialogue try { move(); - } catch (IOException e) { - throw new RuntimeException(e); - } catch (UnsupportedAudioFileException e) { - throw new RuntimeException(e); - } catch (LineUnavailableException e) { + } catch (IOException | UnsupportedAudioFileException | LineUnavailableException e) { throw new RuntimeException(e); } + // check collisions checkCollision(); + // update enemy positions updateEnemy(); + // shoot fireballs every 100 ticks if(fireballCounter<0){fireballCounter = 100;} fireballCounter--; if(fireballCounter == 0){ @@ -472,7 +499,7 @@ public class GamePanel extends JPanel implements Runnable, KeyListener, Serializ throw new RuntimeException(e); } if (playerFrameCounter > 5) { - // increment sprite image to be used and keeps it below 12 + // increment player sprite image to be used and keeps it below 12 playerFrame = (playerFrame + 1) % 11; playerFrameCounter -= 5; } @@ -493,11 +520,11 @@ public class GamePanel extends JPanel implements Runnable, KeyListener, Serializ repaint(); delta--; } - //System.out.println(Thread.currentThread()); } } + // remove dead enemies that have fully faded public void updateEnemy(){ for(int i=0; i fireballs.add(new FireBall(i.x - 20, i.y + Tile.length / 2 - 4, -fireballSpeed, 0, "left", 8, 16)); + case "up" -> fireballs.add(new FireBall(i.x + Tile.length / 2, i.y - 20, 0, -fireballSpeed, "up", 16, 8)); + case "right" -> fireballs.add(new FireBall(i.x + Tile.length + 20, i.y + Tile.length / 2 - 4, fireballSpeed, 0, "right", 8, 16)); + case "down" -> fireballs.add(new FireBall(i.x + Tile.length / 2, i.y + Tile.length + 10, 0, fireballSpeed, "down", 16, 8)); } } } + // create new particles randomly if amount of particles in the particle array is less than 10 public void updateParticle() throws IOException { if(particles.size()<10) { for (int i = 0; i < particleTiles.size(); i++) { if (UtilityFunction.randInt(1, 20) == 1) { + // create particle with the lava particle image particles.add(new Particle(particleTiles.get(i).x + UtilityFunction.randInt(0, Tile.length), particleTiles.get(i).y + UtilityFunction.randInt(0, Tile.length / 2), UtilityFunction.randInt(-3, 3), UtilityFunction.randInt(-5, 2), UtilityFunction.randInt(5, 9), "img/particles/LavaParticle.png")); } @@ -535,25 +564,36 @@ public class GamePanel extends JPanel implements Runnable, KeyListener, Serializ //if a key is pressed, we'll send it over to the Player class for processing public void keyPressed(KeyEvent e){ + // intercept keys through the Middleware class if these are created through the settings e = UtilityFunction.intercept(e, middlewareArray); + // unpause the game if the game is paused, and vice versa if (e.getKeyCode() == KeyEvent.VK_ESCAPE) { isPaused = !isPaused; - System.out.println(isPaused); } else if (e.getKeyCode() == KeyEvent.VK_ENTER) { + // leave the game if the game was already paused + // explicitly ignores MouseEvents that were converted to KeyEvents + // this is to prevent unintentionally quitting the game if (isPaused && (e.getWhen() != -1)) { ((CardLayout)gameFrame.getLayout()).show(gameFrame, "menu"); - } else if (!waitForDialogue) { + } else if (!waitForDialogue) { // fast-forward the dialogue animation if it is still playing waitForDialogue = true; } else { + // play the next dialogue item dialogueMenu.currentFrame = 0; dialogueMenu.frameCounter = 0; - dialogueArray.remove(0); + // try removing the current dialogue item + // the exception is ignored if it fails because waitForDialogue is still set to false + // so it does not matter + try { + dialogueArray.remove(0); + } catch (IndexOutOfBoundsException ignored) {} waitForDialogue = false; } } else { try { + // pass key event to player player.keyPressed(e); - } catch (IOException ex) { + } catch (IOException ex) { // TODO: why is IOException raised throw new RuntimeException(ex); } } @@ -561,23 +601,29 @@ public class GamePanel extends JPanel implements Runnable, KeyListener, Serializ //if a key is released, we'll send it over to the Player class for processing public void keyReleased(KeyEvent e){ + // intercept key with new key defined in settings e = UtilityFunction.intercept(e, middlewareArray); player.keyReleased(e); - if(e.getKeyChar() == 'p'){ + // pressing the P key skips to the next level + if(e.getKeyCode() == KeyEvent.VK_P){ LevelManager.nextLevel(); - + try { + player.resetNoSound(); + } catch (IOException ex) { + ex.printStackTrace(); + } } } //left empty because we don't need it; must be here because it is required to be overridded by the KeyListener interface - public void keyTyped(KeyEvent e){ - - } + public void keyTyped(KeyEvent e){} + // read new image with getImage public static BufferedImage getImage(String imageLocation) throws IOException { return ImageIO.read(new File(imageLocation)); } + // flip image horizontally public static BufferedImage flipImageHorizontally(BufferedImage originalImage) { BufferedImage flippedImage = new BufferedImage(originalImage.getWidth(), originalImage.getHeight(), BufferedImage.TYPE_INT_ARGB); for (int x = 0; x < originalImage.getWidth(); x++) { diff --git a/src/Player.java b/src/Player.java index b23dfe3..2b04203 100644 --- a/src/Player.java +++ b/src/Player.java @@ -108,7 +108,7 @@ public class Player extends GenericSprite { // calls parent public boolean canUpdate(double x, double y) throws UnsupportedAudioFileException, LineUnavailableException, IOException { - if(this.y+y<=-HEIGHT){ + if(this.y+y<=-(HEIGHT+Tile.length*2)){ return false; } boolean canUpdate = true; @@ -446,8 +446,8 @@ public class Player extends GenericSprite { if(rightMouseDown){ g.drawOval((int)(x+WIDTH/2-(reach*steelReachRange)),(int)(y+HEIGHT/2-(reach*steelReachRange)), (int)(reach*steelReachRange*2),(int)(reach*steelReachRange*2)); } - if (!upPressed && !downPressed && !leftPressed && !rightPressed) { - g.drawImage(spriteArray[lastXDirection][lastYDirection][0].image, x-10, y, null); + if (!upPressed && !leftPressed && !rightPressed) { + g.drawImage(spriteArray[lastXDirection][lastYDirection][7].image, x-10, y, null); return 0; } else { lastXDirection = (int)(Math.signum(xVelocity) + 1) / 2;