2022-05-30 19:46:53 +01:00
/ * GamePanel class acts as the main "game loop" - continuously runs the game and calls whatever needs to be called
2022-05-30 19:36:18 +01:00
Child of JPanel because JPanel contains methods for drawing to the screen
Implements KeyListener interface to listen for keyboard input
Implements Runnable interface to use "threading" - let the game do two things at once
* /
import java.awt.* ;
import java.awt.event.* ;
2022-05-31 18:19:23 +01:00
import java.awt.image.BufferedImage ;
import java.io.File ;
import java.io.IOException ;
import javax.imageio.ImageIO ;
2022-06-14 19:54:55 +01:00
import java.io.Serializable ;
2022-06-16 18:31:44 +01:00
import java.nio.file.Files ;
import java.nio.file.Path ;
2022-05-31 18:19:41 +01:00
import java.util.ArrayList ;
2022-06-16 22:28:59 +01:00
import java.util.ConcurrentModificationException ;
2022-06-03 18:50:13 +01:00
import javax.sound.sampled.LineUnavailableException ;
import javax.sound.sampled.UnsupportedAudioFileException ;
2022-05-30 19:36:18 +01:00
import javax.swing.* ;
2022-06-16 18:31:44 +01:00
import static java.nio.file.StandardCopyOption.ATOMIC_MOVE ;
2022-06-14 19:54:55 +01:00
public class GamePanel extends JPanel implements Runnable , KeyListener , Serializable {
2022-05-30 19:36:18 +01:00
2022-05-30 19:46:53 +01:00
//dimensions of window
2022-06-02 18:33:04 +01:00
public static final int GAME_WIDTH = 1225 ;
public static final int GAME_HEIGHT = 630 ;
2022-06-16 18:31:44 +01:00
public int bombCount ;
2022-05-30 19:46:53 +01:00
2022-06-15 02:34:35 +01:00
public transient JPanel gameFrame ;
2022-06-06 18:32:13 +01:00
2022-06-15 02:34:35 +01:00
public transient Thread gameThread ;
public transient Image image ;
public transient Graphics graphics ;
2022-06-16 03:18:19 +01:00
public transient boolean isNewStart = false ;
2022-06-15 02:34:35 +01:00
public Player player ;
2022-06-15 16:24:45 +01:00
public BackgroundImage background , cloudOneBackground , cloudTwoBackground , cloudThreeBackground ;
2022-06-03 22:33:21 +01:00
public int playerFrame , enemyFrame ;
2022-05-31 18:19:23 +01:00
// keeps track of how many ticks has elapsed since last frame change
2022-06-03 22:33:21 +01:00
public int playerFrameCounter = 0 ;
2022-06-17 18:09:50 +01:00
public int timeSinceLastSave = 0 ;
2022-06-16 22:28:59 +01:00
public boolean isPaused , isDialogue , waitForDialogue , mouseAlreadyTranslated ;
2022-06-08 18:36:47 +01:00
public PauseMenu pauseMenu ;
2022-06-15 16:24:45 +01:00
public DialogueMenu dialogueMenu ;
2022-06-16 18:31:44 +01:00
public ArrayList < String > dialogueArray = new ArrayList < String > ( ) ;
2022-05-31 18:19:23 +01:00
2022-06-12 01:02:41 +01:00
2022-06-15 02:34:35 +01:00
public BufferedImageWrapper [ ] [ ] [ ] playerSpriteArray = new BufferedImageWrapper [ 2 ] [ 2 ] [ 11 ] ;
public BufferedImageWrapper [ ] [ ] [ ] slimeSpriteArray = new BufferedImageWrapper [ 2 ] [ 2 ] [ 3 ] ;
public BufferedImageWrapper [ ] explosionArray = new BufferedImageWrapper [ 9 ] ;
2022-05-30 19:46:53 +01:00
2022-06-08 03:12:40 +01:00
//public static ArrayList<Tile>map = new ArrayList<Tile>();
2022-06-15 16:25:38 +01:00
public Tile [ ] [ ] map = new Tile [ 1000 ] [ 18 ] ;
2022-06-15 02:34:35 +01:00
public ArrayList < Middleware > middlewareArray = new ArrayList < Middleware > ( ) ;
2022-06-08 19:32:31 +01:00
2022-06-15 02:34:35 +01:00
public ArrayList < Tile > particleTiles = new ArrayList < Tile > ( ) ;
2022-06-17 18:50:22 +01:00
public ArrayList < Tile > shootingTiles = new ArrayList < Tile > ( ) ;
2022-06-18 02:39:14 +01:00
public ArrayList < FireBall > fireballs = new ArrayList < FireBall > ( ) ;
2022-06-15 02:34:35 +01:00
public ArrayList < NonPlayer > enemy = new ArrayList < NonPlayer > ( ) ;
public ArrayList < StickyBomb > bombs = new ArrayList < > ( ) ;
2022-06-12 01:02:41 +01:00
public BombDirectionShow bombDir = null ;
2022-06-15 02:34:35 +01:00
public ArrayList < Particle > particles = new ArrayList < Particle > ( ) ;
public Camera camera ;
2022-06-02 18:24:01 +01:00
2022-06-02 18:22:17 +01:00
// image imports begin here
2022-06-16 03:08:35 +01:00
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" ) ) ;
2022-06-15 02:34:35 +01:00
public BufferedImageWrapper bomb ;
2022-06-17 18:32:53 +01:00
public BufferedImageWrapper narratorPortrait = new BufferedImageWrapper ( ( "img/dialogue/Gunther.png" ) ) ;
public BufferedImageWrapper villainPortrait = new BufferedImageWrapper ( ( "img/dialogue/Bouncer.png" ) ) ;
2022-06-16 18:31:44 +01:00
public String lastText ;
2022-06-17 18:55:23 +01:00
public boolean isContinue ;
2022-06-18 02:38:25 +01:00
public boolean isRunning ;
2022-06-07 18:19:54 +01:00
2022-06-02 18:22:17 +01:00
2022-06-06 18:32:13 +01:00
public GamePanel ( JPanel gameFrame ) throws IOException , SpriteException , UnsupportedAudioFileException , LineUnavailableException {
this . gameFrame = gameFrame ;
2022-06-02 18:21:29 +01:00
camera = new Camera ( 0 ) ;
2022-06-07 21:40:28 +01:00
background = new BackgroundImage ( 0 , 0 , backgroundImage , GAME_WIDTH , GAME_HEIGHT , 10 , camera ) ;
2022-06-15 16:24:45 +01:00
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 ) ;
2022-06-08 18:36:47 +01:00
pauseMenu = new PauseMenu ( GAME_HEIGHT / 2 , 100 , 400 , 400 , GAME_WIDTH , new Font ( Font . MONOSPACED , Font . BOLD , 60 ) , "Paused" ) ;
2022-06-17 18:32:53 +01:00
dialogueMenu = new DialogueMenu ( GAME_HEIGHT - 100 , 200 , new Font ( Font . MONOSPACED , Font . BOLD , 20 ) , narratorPortrait , true ) ;
dialogueArray . add ( "Did you ever hear the tragedy of Darth Plagueis The Wise?" ) ;
dialogueArray . add ( "I thought not." ) ;
dialogueArray . add ( "It’ s not a story the Jedi would tell you." ) ;
dialogueArray . add ( "Darth Plagueis was a Dark Lord of the Sith, " +
"so powerful and so wise he could use the Force to influence " +
"the midichlorians to create life… " +
"He had such a knowledge of the dark side that " +
"he could even keep the ones he cared about from dying. " +
"The dark side of the Force is a pathway to many abilities some consider to be unnatural. " +
"He became so powerful… the only thing he was afraid of was losing his power, " +
"which eventually, of course, he did." ) ;
dialogueArray . add ( "Unfortunately, he taught his apprentice everything he knew, then his apprentice killed him in his sleep." ) ;
dialogueArray . add ( "Ironic." ) ;
dialogueArray . add ( "He could save others from death, but not himself." ) ;
2022-06-03 22:33:21 +01:00
try {
// load player sprites from disk here
for ( int i = 0 ; i < 11 ; i + + ) {
2022-06-16 03:08:35 +01:00
String sprite = ( String . format ( "img/walk/p1_walk%s.png" , String . format ( "%1$2s" , i + 1 ) . replace ( ' ' , '0' ) ) ) ;
2022-05-31 18:19:23 +01:00
2022-06-15 02:34:35 +01:00
playerSpriteArray [ 1 ] [ 0 ] [ i ] = new BufferedImageWrapper ( sprite ) ;
playerSpriteArray [ 1 ] [ 1 ] [ i ] = new BufferedImageWrapper ( sprite ) ;
2022-06-16 03:08:35 +01:00
playerSpriteArray [ 0 ] [ 0 ] [ i ] = new BufferedImageWrapper ( sprite , true ) ;
playerSpriteArray [ 0 ] [ 1 ] [ i ] = new BufferedImageWrapper ( sprite , true ) ;
2022-05-31 18:19:23 +01:00
}
2022-06-08 18:36:47 +01:00
for ( int i = 0 ; i < 9 ; i + + ) {
2022-06-16 03:08:35 +01:00
explosionArray [ i ] = new BufferedImageWrapper ( ( "img/misc/bomb/sonicExplosion0" + i + ".png" ) ) ;
2022-06-08 18:36:47 +01:00
}
2022-06-03 22:33:21 +01:00
// load slime sprites from disk here
// these variables were not defined above because they are temporary variables
2022-06-16 03:08:35 +01:00
BufferedImageWrapper [ ] temporarySlimeArray = { new BufferedImageWrapper ( ( "img/enemy/slime/slimeWalk1.png" ) ) ,
new BufferedImageWrapper ( ( "img/enemy/slime/slimeWalk2.png" ) ) ,
new BufferedImageWrapper ( ( "img/enemy/slime/slimeDead.png" ) ) } ;
BufferedImageWrapper [ ] flippedTemporarySlimeArray = { new BufferedImageWrapper ( ( ( "img/enemy/slime/slimeWalk1.png" ) ) , true ) ,
new BufferedImageWrapper ( ( "img/enemy/slime/slimeWalk2.png" ) , true ) ,
new BufferedImageWrapper ( ( "img/enemy/slime/slimeDead.png" ) , true ) } ;
2022-06-03 22:33:21 +01:00
// please note that these sprites are reversed compared to the player sprites
slimeSpriteArray [ 0 ] [ 0 ] = temporarySlimeArray ;
slimeSpriteArray [ 0 ] [ 1 ] = temporarySlimeArray ;
slimeSpriteArray [ 1 ] [ 0 ] = flippedTemporarySlimeArray ;
slimeSpriteArray [ 1 ] [ 1 ] = flippedTemporarySlimeArray ;
2022-06-15 02:34:35 +01:00
// load bomb sprites
2022-06-16 03:08:35 +01:00
bomb = new BufferedImageWrapper ( ( "img/misc/bomb.png" ) ) ;
2022-06-03 22:33:21 +01:00
} catch ( IOException e ) {
e . printStackTrace ( ) ;
2022-05-31 18:19:23 +01:00
}
2022-06-15 02:34:35 +01:00
player = new Player ( GAME_WIDTH / 2 , GAME_HEIGHT / 2 , playerSpriteArray ) ; //create a player controlled player, set start location to middle of screenk
2022-06-02 18:22:17 +01:00
// the height of 35 is set because it is half of the original tile height (i.e., 70px)
2022-06-15 03:00:27 +01:00
addUserInterface ( ) ;
this . setPreferredSize ( new Dimension ( GAME_WIDTH , GAME_HEIGHT ) ) ;
2022-06-16 03:18:19 +01:00
isNewStart = true ;
2022-06-18 02:38:25 +01:00
isRunning = true ;
2022-06-15 03:00:27 +01:00
}
public void addUserInterface ( ) {
2022-05-30 19:46:53 +01:00
this . setFocusable ( true ) ; //make everything in this class appear on the screen
this . addKeyListener ( this ) ; //start listening for keyboard input
2022-06-06 18:32:13 +01:00
// request focus when the CardLayout selects this game
this . addComponentListener ( new ComponentAdapter ( ) {
@Override
public void componentShown ( ComponentEvent cEvt ) {
Component src = ( Component ) cEvt . getSource ( ) ;
src . requestFocusInWindow ( ) ;
}
2022-05-30 19:46:53 +01:00
2022-06-06 18:32:13 +01:00
} ) ;
2022-05-30 19:46:53 +01:00
//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
addMouseListener ( new MouseAdapter ( ) {
public void mousePressed ( MouseEvent e ) {
2022-06-12 22:28:27 +01:00
try {
2022-06-16 22:28:59 +01:00
if ( isDialogue | | isPaused ) {
mouseAlreadyTranslated = true ;
keyPressed ( new KeyEvent ( new Component ( ) { } , 0 , 0 , 0 , KeyEvent . VK_ENTER ) ) ;
} else {
player . mousePressed ( e ) ;
}
2022-06-15 02:34:35 +01:00
} catch ( SpriteException | IOException ex ) {
2022-06-12 22:28:27 +01:00
throw new RuntimeException ( ex ) ;
}
}
2022-06-09 19:19:23 +01:00
public void mouseReleased ( MouseEvent e ) {
2022-06-16 22:28:59 +01:00
if ( mouseAlreadyTranslated ) {
mouseAlreadyTranslated = false ;
} else if ( ! isDialogue & & ! isPaused ) {
player . mouseReleased ( e ) ;
}
2022-06-09 19:19:23 +01:00
}
2022-06-12 01:02:41 +01:00
} ) ;
addMouseMotionListener ( new MouseAdapter ( ) {
public void mouseDragged ( MouseEvent e ) {
player . mouseDragged ( e ) ;
}
2022-06-13 04:55:19 +01:00
public void mouseMoved ( MouseEvent e ) {
player . mouseMoved ( e ) ;
}
2022-06-12 01:02:41 +01:00
} ) ;
2022-06-15 02:34:35 +01:00
}
2022-05-30 19:46:53 +01:00
2022-06-15 02:34:35 +01:00
// startThread is to be called after the game has started to avoid any issues TODO: better explanation
public void startThread ( ) {
2022-05-30 19:46:53 +01:00
//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 ( ) ;
2022-06-16 18:32:58 +01:00
//System.out.println(gameThread);
2022-05-30 19:46:53 +01:00
}
//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
public void paint ( Graphics g ) {
//we are using "double buffering here" - if we draw images directly onto the screen, it takes time and the human eye can actually notice flashes of lag as each pixel on the screen is drawn one at a time. Instead, we are going to draw images OFF the screen, then simply move the image on screen as needed.
image = createImage ( GAME_WIDTH , GAME_HEIGHT ) ; //draw off screen
graphics = image . getGraphics ( ) ;
2022-06-13 04:55:19 +01:00
try {
draw ( graphics , playerFrame , enemyFrame ) ; //update the positions of everything on the screen
} catch ( IOException e ) {
throw new RuntimeException ( e ) ;
}
2022-05-30 19:46:53 +01:00
g . drawImage ( image , 0 , 0 , this ) ; //move the image on the screen
}
//call the draw methods in each class to update positions as things move
2022-06-13 04:55:19 +01:00
public void draw ( Graphics g , int playerFrame , int enemyFrame ) throws IOException {
2022-06-02 18:22:17 +01:00
background . draw ( g ) ;
2022-06-15 16:24:45 +01:00
cloudOneBackground . draw ( g ) ;
cloudTwoBackground . draw ( g ) ;
cloudThreeBackground . draw ( g ) ;
2022-06-15 19:55:39 +01:00
if ( isPaused | | isDialogue ) {
2022-06-08 18:36:47 +01:00
// set player frame to 0 to prevent frame from changing when player moves
playerFrame = 0 ;
Graphics2D g2d = ( Graphics2D ) ( g ) ;
// remove extraneous details from game when paused
g2d . setPaint ( Color . white ) ;
}
2022-06-08 03:12:40 +01:00
//Don't want to draw off screen items
2022-06-15 02:34:35 +01:00
int xMin = Math . max ( 0 , ( ( this . camera . x + GAME_WIDTH ) / Tile . length ) - ( GAME_WIDTH / ( 2 * Tile . length ) ) - 5 ) ;
2022-06-08 03:12:40 +01:00
int xMax = Math . min ( map . length , 7 + xMin + GAME_WIDTH / Tile . length ) ;
for ( int i = xMin ; i < xMax ; i + + ) {
for ( int j = 0 ; j < map [ 0 ] . length ; j + + ) {
if ( map [ i ] [ j ] ! = null ) {
map [ i ] [ j ] . draw ( g ) ;
}
}
2022-05-31 18:19:41 +01:00
}
2022-06-05 22:37:09 +01:00
for ( int i = 0 ; i < enemy . size ( ) ; i + + ) {
enemy . get ( i ) . draw ( g , enemyFrame ) ;
2022-06-03 22:33:21 +01:00
}
2022-06-05 17:01:21 +01:00
playerFrameCounter + = player . draw ( g , playerFrame ) ;
2022-06-09 05:07:44 +01:00
for ( int i = 0 ; i < bombs . size ( ) ; i + + ) {
if ( i < bombs . size ( ) ) {
if ( bombs . get ( i ) . erase ) {
bombs . remove ( i ) ;
} else {
2022-06-12 01:02:41 +01:00
bombs . get ( i ) . draw ( g ) ;
2022-06-09 05:07:44 +01:00
}
}
}
2022-06-18 04:36:52 +01:00
for ( int i = 0 ; i < fireballs . size ( ) ; i + + ) {
fireballs . get ( i ) . draw ( g ) ;
}
2022-06-08 16:23:57 +01:00
for ( int i = 0 ; i < particles . size ( ) ; i + + ) {
2022-06-15 03:00:27 +01:00
// todo: find cause of particles being null
2022-06-15 16:25:38 +01:00
if ( i < particles . size ( ) & & particles . get ( i ) ! = null ) {
2022-06-08 19:32:31 +01:00
particles . get ( i ) . draw ( g ) ;
particles . get ( i ) . lifeSpan - - ;
if ( particles . get ( i ) . lifeSpan < = 0 ) {
particles . remove ( i ) ;
}
2022-06-08 16:23:57 +01:00
}
}
2022-06-12 01:02:41 +01:00
if ( player . leftMouseDown ) {
2022-06-15 02:34:35 +01:00
bombDir = new BombDirectionShow ( this . player . x + this . camera . x + WIDTH / 2 , this . player . y + HEIGHT / 2 ,
( player . mouseX - this . player . x ) / 20 , ( player . mouseY - this . player . y ) / 10 ) ;
2022-06-12 01:02:41 +01:00
bombDir . draw ( g ) ;
}
2022-06-13 04:55:19 +01:00
if ( player . holdingSteel ) {
String filePath = "" ;
if ( player . canPlaceSteel ) {
filePath = "img/tiles/boxes/greenSteel.png" ;
} else {
filePath = "img/tiles/boxes/redSteel.png" ;
}
2022-06-13 18:53:58 +01:00
int x = ( player . mouseX + camera . x + GAME_WIDTH / 2 ) / Tile . length ;
2022-06-13 04:55:19 +01:00
double y = ( ( player . mouseY / Tile . length ) ) * Tile . length ;
2022-06-13 18:53:58 +01:00
g . drawImage ( getImage ( filePath ) , x * Tile . length - ( GamePanel . GAME_WIDTH / 2 ) - camera . x , ( int ) y , Tile . length , Tile . length , null ) ;
2022-06-13 04:55:19 +01:00
}
// g.drawString(camera.x+" "+((camera.x+GAME_WIDTH)/Tile.length)+" "+player.leftMouseDown,100,100);
// g.drawString(camera.x+" "+((player.mouseX+camera.x+GAME_WIDTH/2)/Tile.length)+" "+player.leftMouseDown,100,200);
2022-06-15 02:34:35 +01:00
g . drawImage ( bomb . image , 20 , 20 , 35 , 35 , null ) ;
2022-06-12 19:11:09 +01:00
g . drawString ( "X" + LevelManager . bombs , 60 , 40 ) ;
2022-06-08 18:36:47 +01:00
if ( isPaused ) {
g . setColor ( new Color ( 255 , 255 , 255 , 100 ) ) ;
g . fillRect ( 0 , 0 , GAME_WIDTH , GAME_HEIGHT ) ;
pauseMenu . draw ( g , Color . white , Color . black ) ;
2022-06-15 16:24:45 +01:00
} else if ( isDialogue ) {
g . setColor ( new Color ( 255 , 255 , 255 , 100 ) ) ;
g . fillRect ( 0 , 0 , GAME_WIDTH , GAME_HEIGHT ) ;
2022-06-16 18:31:44 +01:00
try {
if ( waitForDialogue ) {
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 ) ) {
waitForDialogue = true ;
}
} catch ( IndexOutOfBoundsException e ) {
isDialogue = false ;
2022-06-16 22:28:59 +01:00
// throw new RuntimeException(e);
2022-06-16 18:31:44 +01:00
}
2022-06-08 18:36:47 +01:00
}
2022-06-18 04:36:52 +01:00
g . drawString ( player . xVelocity + "" , 400 , 400 ) ;
2022-05-30 19:46:53 +01:00
}
//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
2022-06-08 19:32:31 +01:00
public void move ( ) throws IOException {
2022-05-31 18:19:23 +01:00
player . move ( ) ;
2022-06-03 22:33:21 +01:00
for ( NonPlayer n : enemy ) {
n . move ( ) ;
}
2022-06-09 05:07:44 +01:00
for ( int i = 0 ; i < bombs . size ( ) ; i + + ) {
bombs . get ( i ) . move ( ) ;
}
2022-06-08 19:32:31 +01:00
for ( int i = 0 ; i < particles . size ( ) ; i + + ) {
2022-06-15 02:35:05 +01:00
if ( particles . get ( i ) ! = null ) {
particles . get ( i ) . move ( ) ;
}
2022-06-08 16:23:57 +01:00
}
2022-06-18 02:39:14 +01:00
for ( int i = 0 ; i < fireballs . size ( ) ; i + + ) {
if ( fireballs . get ( i ) ! = null ) {
fireballs . get ( i ) . move ( ) ;
}
}
2022-05-30 19:46:53 +01:00
}
//handles all collision detection and responds accordingly
2022-05-31 18:31:58 +01:00
public void checkCollision ( ) {
2022-05-31 18:27:15 +01:00
player . isGrounded = false ;
2022-06-08 03:12:40 +01:00
for ( int i = 0 ; i < map . length ; i + + ) {
for ( int j = 0 ; j < map [ 0 ] . length ; j + + ) {
if ( map [ i ] [ j ] ! = null ) {
map [ i ] [ j ] . update ( ) ;
}
}
2022-05-31 18:19:41 +01:00
}
2022-06-06 03:18:10 +01:00
for ( NonPlayer n : enemy ) {
n . update ( ) ;
n . isGrounded = false ;
2022-06-12 04:48:57 +01:00
if ( n . collidePlayer ( player ) & & ! n . isDead ) {
2022-06-06 03:18:10 +01:00
player . alive = false ;
}
}
2022-06-05 22:33:24 +01:00
//force player to remain on screen (For the most part)
2022-05-31 18:31:58 +01:00
if ( player . y > = GAME_HEIGHT - Player . PLAYER_HEIGHT ) {
2022-06-05 17:05:49 +01:00
player . y = GAME_HEIGHT - Player . PLAYER_HEIGHT ;
player . yVelocity = 0 ;
player . isGrounded = true ;
2022-06-03 22:33:21 +01:00
}
if ( player . x < = 0 ) {
player . x = 0 ;
}
if ( player . x + Player . PLAYER_WIDTH > = GAME_WIDTH ) {
player . x = GAME_WIDTH - Player . PLAYER_WIDTH ;
}
2022-06-05 17:05:49 +01:00
for ( NonPlayer n : enemy ) {
2022-06-03 22:33:21 +01:00
if ( n . y > = GAME_HEIGHT - n . npcHeight ) {
n . y = GAME_HEIGHT - n . npcHeight ;
n . yVelocity = 0 ;
n . isGrounded = true ;
2022-05-31 18:31:58 +01:00
}
2022-05-30 19:36:18 +01:00
}
2022-06-05 17:05:49 +01:00
}
2022-05-30 19:46:53 +01:00
//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
2022-06-16 03:18:19 +01:00
public void run ( ) {
if ( isNewStart ) {
LevelManager . setLevel ( 1 ) ;
isDialogue = true ;
2022-06-16 18:31:44 +01:00
} else {
LevelManager . bombs = bombCount ;
2022-06-16 03:18:19 +01:00
}
2022-05-30 19:46:53 +01:00
//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 ;
2022-06-18 02:39:14 +01:00
int fireballCounter = 0 ;
2022-05-30 19:46:53 +01:00
while ( true ) { //this is the infinite game loop
now = System . nanoTime ( ) ;
delta = delta + ( now - lastTime ) / ns ;
lastTime = now ;
2022-06-18 04:36:52 +01:00
// if(fireballCounter<0){fireballCounter = 20;}
// fireballCounter--;
2022-05-30 19:46:53 +01:00
//only move objects around and update screen if enough time has passed
if ( delta > = 1 ) {
2022-06-15 19:55:39 +01:00
if ( ( ! isPaused & & ! isDialogue ) & & MenuPanel . gameStart ) {
// only perform game functions if game is not paused or in dialogue
2022-06-08 19:35:13 +01:00
try {
move ( ) ;
} catch ( IOException e ) {
throw new RuntimeException ( e ) ;
}
2022-06-08 18:36:47 +01:00
checkCollision ( ) ;
2022-06-12 04:48:57 +01:00
updateEnemy ( ) ;
2022-06-18 04:36:52 +01:00
//wwwwwwww
2022-06-08 19:50:45 +01:00
try {
updateParticle ( ) ;
} catch ( IOException e ) {
throw new RuntimeException ( e ) ;
}
2022-06-08 18:36:47 +01:00
if ( playerFrameCounter > 5 ) {
// increment sprite image to be used and keeps it below 12
playerFrame = ( playerFrame + 1 ) % 11 ;
playerFrameCounter - = 5 ;
}
2022-06-08 19:32:31 +01:00
}
2022-06-17 18:09:50 +01:00
// a new save is made every 100 ticks
timeSinceLastSave + + ;
if ( timeSinceLastSave > = 100 ) {
timeSinceLastSave - = 100 ;
bombCount = LevelManager . bombs ;
try {
// atomic save to prevent EOF errors
FileManager . writeObjectToFile ( "local\\temp_state.dat" , this ) ;
Files . move ( Path . of ( "local" , "temp_state.dat" ) , Path . of ( "local" , "game_state.dat" ) , ATOMIC_MOVE ) ;
} catch ( IOException | ConcurrentModificationException e ) {
e . printStackTrace ( ) ;
}
2022-06-16 18:31:44 +01:00
}
2022-05-30 19:36:18 +01:00
repaint ( ) ;
2022-05-30 19:46:53 +01:00
delta - - ;
}
2022-06-18 04:36:52 +01:00
//System.out.println(Thread.currentThread());
2022-05-30 19:36:18 +01:00
}
2022-06-18 02:41:42 +01:00
2022-05-30 19:46:53 +01:00
}
2022-05-30 19:36:18 +01:00
2022-06-12 04:48:57 +01:00
public void updateEnemy ( ) {
for ( int i = 0 ; i < enemy . size ( ) ; i + + ) {
if ( enemy . get ( i ) . isDead ) {
2022-06-13 18:42:05 +01:00
// enemy.get(i).fadeCounter--;
2022-06-12 04:48:57 +01:00
if ( enemy . get ( i ) . fadeCounter < = 0 ) {
enemy . remove ( i ) ;
}
}
}
}
2022-06-18 02:39:14 +01:00
public void updateShootingBlock ( ) {
for ( Tile i : shootingTiles ) {
if ( i . shootingDir . equals ( "left" ) ) {
2022-06-18 04:36:52 +01:00
//fireballs.add(new FireBall(i.x,i.y,80,41,-1,0,"left"));
2022-06-18 02:39:14 +01:00
}
}
}
2022-06-08 19:32:31 +01:00
public void updateParticle ( ) throws IOException {
2022-06-15 16:25:38 +01:00
if ( particles . size ( ) < 10 ) {
for ( int i = 0 ; i < particleTiles . size ( ) ; i + + ) {
if ( GlobalState . randInt ( 1 , 20 ) = = 1 ) {
particles . add ( new Particle ( particleTiles . get ( i ) . x + GlobalState . randInt ( 0 , Tile . length ) , particleTiles . get ( i ) . y + GlobalState . randInt ( 0 , Tile . length / 2 ) ,
GlobalState . randInt ( - 3 , 3 ) , GlobalState . randInt ( - 5 , 2 ) , GlobalState . randInt ( 5 , 9 ) , "img/particles/LavaParticle.png" ) ) ;
}
2022-06-08 19:50:45 +01:00
}
2022-06-15 16:25:38 +01:00
}
2022-06-08 19:32:31 +01:00
}
2022-05-31 18:19:23 +01:00
//if a key is pressed, we'll send it over to the Player class for processing
2022-05-30 19:46:53 +01:00
public void keyPressed ( KeyEvent e ) {
2022-06-10 16:01:23 +01:00
e = UtilityFunction . intercept ( e , middlewareArray ) ;
2022-06-08 18:36:47 +01:00
if ( e . getKeyCode ( ) = = KeyEvent . VK_ESCAPE ) {
isPaused = ! isPaused ;
2022-06-15 19:55:39 +01:00
} else if ( e . getKeyCode ( ) = = KeyEvent . VK_ENTER ) {
2022-06-16 22:28:59 +01:00
if ( ! waitForDialogue ) {
waitForDialogue = true ;
} else {
dialogueMenu . currentFrame = 0 ;
dialogueMenu . frameCounter = 0 ;
dialogueArray . remove ( 0 ) ;
waitForDialogue = false ;
}
2022-06-08 18:36:47 +01:00
} else {
player . keyPressed ( e ) ;
}
2022-05-30 19:46:53 +01:00
}
2022-05-30 19:36:18 +01:00
2022-05-31 18:19:23 +01:00
//if a key is released, we'll send it over to the Player class for processing
2022-05-30 19:46:53 +01:00
public void keyReleased ( KeyEvent e ) {
2022-06-10 16:01:23 +01:00
e = UtilityFunction . intercept ( e , middlewareArray ) ;
2022-05-31 18:19:23 +01:00
player . keyReleased ( e ) ;
2022-06-06 03:18:10 +01:00
if ( e . getKeyChar ( ) = = 'p' ) {
LevelManager . nextLevel ( ) ;
}
2022-05-30 19:46:53 +01:00
}
2022-05-30 19:36:18 +01:00
2022-05-30 19:46:53 +01:00
//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 ) {
2022-05-30 19:36:18 +01:00
2022-05-30 19:46:53 +01:00
}
2022-05-31 18:19:23 +01:00
2022-06-03 18:17:03 +01:00
public static BufferedImage getImage ( String imageLocation ) throws IOException {
2022-05-31 18:19:23 +01:00
return ImageIO . read ( new File ( imageLocation ) ) ;
}
2022-06-03 18:09:59 +01:00
public static BufferedImage flipImageHorizontally ( BufferedImage originalImage ) {
2022-06-01 19:39:57 +01:00
BufferedImage flippedImage = new BufferedImage ( originalImage . getWidth ( ) , originalImage . getHeight ( ) , BufferedImage . TYPE_INT_ARGB ) ;
for ( int x = 0 ; x < originalImage . getWidth ( ) ; x + + ) {
for ( int y = 0 ; y < originalImage . getHeight ( ) ; y + + ) {
// -1 is added to prevent off-by-one errors
flippedImage . setRGB ( x , y , originalImage . getRGB ( originalImage . getWidth ( ) - x - 1 , y ) ) ;
}
}
return flippedImage ;
}
2022-05-30 19:36:18 +01:00
}