From 50cc8e2a7b8ff8721ae6d6db45673bbab0baa248 Mon Sep 17 00:00:00 2001 From: naomi Date: Sun, 11 May 2025 05:09:50 +0000 Subject: [PATCH] Implementing state objects --- README.md | 25 +- pom.xml | 18 +- .../quajra/vectorbreakout/Ball.java | 89 ++--- .../quajra/vectorbreakout/Brick.java | 50 +-- .../quajra/vectorbreakout/GameDisplay.java | 349 +++++++++++------- .../quajra/vectorbreakout/GameState.java | 283 ++++++++++++++ .../quajra/vectorbreakout/Level.java | 165 +++++++-- .../quajra/vectorbreakout/Menu.java | 14 + .../quajra/vectorbreakout/Paddle.java | 7 +- .../quajra/vectorbreakout/Particle.java | 7 +- .../quajra/vectorbreakout/TextElement.java | 50 ++- .../quajra/vectorbreakout/VectorBreakout.java | 255 ++----------- 12 files changed, 813 insertions(+), 499 deletions(-) create mode 100644 src/main/java/dev/boyfailure/quajra/vectorbreakout/GameState.java create mode 100644 src/main/java/dev/boyfailure/quajra/vectorbreakout/Menu.java diff --git a/README.md b/README.md index ad20a06..a76f567 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,13 @@ # Vector Breakout -A clone of Breakout built using swing/awt in Java +A clone of Breakout built in pure Java, using AWT/Swing Very early proof of concept for a "vector graphics engine" for arcade style games. Eventual plans to create a real vector graphics game engine, potentially using LWJGL, once I have enough knowledge and experience. **Caveat emptor: I am a very novice programmer, and you might gag a bit when you see my code...** -The repository as is is built with JDK 21, but I am testing the code as far back as JDK 1.5. +This repository is built with JDK 21 (Maven), but I am testing the code as far back as JDK 1.5 (hurrah for people archiving old JDKs and NetBeans installers!). + +Special thanks to my friend [Ada](https://github.com/ProphetofAtnu) for getting it through my head that I need to move game code out of the main class lal ___ @@ -20,17 +22,8 @@ These controls may change, and I may add a controls config in the future. ___ ## TODO -- Improve ball collision - - "Path-casting" (for lack of a better term) for ball collision - - The idea is to change ball collision to instead check if a brick/paddle/etc will be in its path in the next frame, and if so, execute the collision instructions - - Bouncing horizontally off the sides of bricks -- Implement levels as their own class - - Each level will have unique properties, like varying playfield width, brick count/layouts, etc - - Will be much better than just the single hardcoded level and each individual brick being put into an array - -## ISSUES -- Ball sometimes passes through paddle, or gets stuck in right wall - - This should be fixed with the aforementioned collision changes once I get to that - -## I AM AWARE THAT: -- I could use a switch statement for the score/lives/level display. However, I want backwards compatability because I'm a silly nerd :3, and JDK 1.5 doesn't support passing a split string through a switch statement. If there is a better way to get the individual digits, please let me know. \ No newline at end of file +- Update hitboxes for bricks (the plan is to have individual sections of the brick make the ball move differently upon impact, sorta like how I did it in [the QuickBASIC version of the game](https://codeberg.org/boyfailure/quickout/src/branch/main/QUICKOUT.BAS)) +- Add complete javadoc +- Implement menus +- Look into sound effects +- Clean up/optimize rendering code \ No newline at end of file diff --git a/pom.xml b/pom.xml index 66b53da..d8dea36 100644 --- a/pom.xml +++ b/pom.xml @@ -1,15 +1,26 @@ - + 4.0.0 + dev.boyfailure.quajra VectorBreakout - 1.0-SNAPSHOT + 20250411_test1 + jar + + Vector Breakout + A clone of Breakout with a QuadraScan-inspired vector graphics artstyle. + - UTF-8 + 21 + 21 21 + UTF-8 dev.boyfailure.quajra.vectorbreakout.VectorBreakout + @@ -26,4 +37,5 @@ + \ No newline at end of file diff --git a/src/main/java/dev/boyfailure/quajra/vectorbreakout/Ball.java b/src/main/java/dev/boyfailure/quajra/vectorbreakout/Ball.java index c3f3ae5..3af9e84 100644 --- a/src/main/java/dev/boyfailure/quajra/vectorbreakout/Ball.java +++ b/src/main/java/dev/boyfailure/quajra/vectorbreakout/Ball.java @@ -13,7 +13,10 @@ public class Ball { byte speedX = 5; byte speedY = 5; - public Ball(int x, int y, int width, int height, int color, boolean active) { + private GameState gameState; + + public Ball(GameState gameState, int x, int y, int width, int height, int color, boolean active) { + this.gameState = gameState; this.ballX = (short) x; this.ballY = (short) y; this.ballWidth = (short) width; @@ -25,17 +28,17 @@ public class Ball { public void spawnBall(boolean useChancey) { if (useChancey) { - this.ballX += (short) (VectorBreakout.chancey * 3); - this.ballY += (short) VectorBreakout.chancey; + this.ballX += (short) (gameState.chancey * 3); + this.ballY += (short) gameState.chancey; } - VectorBreakout.ballsOnScreen++; + gameState.ballsOnScreen++; this.isActive = true; } public void spawnBall(int x, int y) { this.ballX = (short) x; this.ballY = (short) y; - VectorBreakout.ballsOnScreen++; + gameState.ballsOnScreen++; this.isActive = true; } @@ -52,7 +55,7 @@ public class Ball { switch((byte) bounceType) { case 0: this.speedY = (byte) -(this.speedY); - switch (VectorBreakout.chancey) { + switch (gameState.chancey) { case 0: break; case 1: this.speedX++; break; case 2: break; @@ -105,21 +108,21 @@ public class Ball { this.isActive = false; this.ballX = 32767; this.ballY = 32767; - VectorBreakout.ballsOnScreen--; - if (VectorBreakout.ballsOnScreen <= 0) { - VectorBreakout.lives--; + gameState.ballsOnScreen--; + if (gameState.ballsOnScreen <= 0) { + gameState.lives--; } - if (VectorBreakout.lives <= 0) { - VectorBreakout.gameLose(); + if (gameState.lives <= 0) { + gameState.gameLose(); } - VectorBreakout.currentTextElements[1].setText(String.valueOf(VectorBreakout.lives)); + gameState.currentTextElements[1].setText(String.valueOf(gameState.lives)); } // Checks to see if the ball is in contact with bricks, paddles, or walls public void checkCollision() { // Check floor - if (this.ballY >= VectorBreakout.gameResY) { + if (this.ballY >= gameState.gameResY) { this.destroyBall(); return; } @@ -138,19 +141,19 @@ public class Ball { this.wallBounceAnimation(); return; } - else if ((this.ballX + this.ballWidth) >= (VectorBreakout.gameResX - 20 - this.ballWidth)) { + else if ((this.ballX + this.ballWidth) >= (gameState.gameResX - 20 - this.ballWidth)) { this.bounce((byte) 1); - this.ballX = (short) (VectorBreakout.gameResX - 25 - this.ballWidth); + this.ballX = (short) (gameState.gameResX - 25 - this.ballWidth); this.wallBounceAnimation(); return; } // Check bricks - if (VectorBreakout.currentLevel != null) { - for (short i = 0; i < VectorBreakout.currentLevel.levelBricks.length; i++) { - if (VectorBreakout.currentLevel.levelBricks[i] != null && !VectorBreakout.currentLevel.levelBricks[i].isBroken) { - if (this.hitBox.collides(VectorBreakout.currentLevel.levelBricks[i].hitBox.bounds)) { - VectorBreakout.currentLevel.levelBricks[i].breakBrick(); + if (gameState.currentLevel != null) { + for (short i = 0; i < gameState.currentLevel.levelBricks.length; i++) { + if (gameState.currentLevel.levelBricks[i] != null && !gameState.currentLevel.levelBricks[i].isBroken) { + if (this.hitBox.collides(gameState.currentLevel.levelBricks[i].hitBox.bounds)) { + gameState.currentLevel.levelBricks[i].breakBrick(); this.bounce((byte) 0); return; } @@ -159,14 +162,14 @@ public class Ball { } // Check paddles - for (byte i = 0; i < VectorBreakout.currentPaddles.length; i++) { - if (VectorBreakout.currentPaddles[i] != null) { - if (this.hitBox.collides(VectorBreakout.currentPaddles[i].hitBox.bounds)) { + for (byte i = 0; i < gameState.currentPaddles.length; i++) { + if (gameState.currentPaddles[i] != null) { + if (this.hitBox.collides(gameState.currentPaddles[i].hitBox.bounds)) { - if (this.hitBox.collides(VectorBreakout.currentPaddles[i].hitBoxLeft.bounds)) { + if (this.hitBox.collides(gameState.currentPaddles[i].hitBoxLeft.bounds)) { this.bounce(2); } - else if (this.hitBox.collides(VectorBreakout.currentPaddles[i].hitBoxRight.bounds)) { + else if (this.hitBox.collides(gameState.currentPaddles[i].hitBoxRight.bounds)) { this.bounce(3); } else { @@ -177,21 +180,21 @@ public class Ball { // Draw particles byte ballParticleGen = 0; - for (byte ii = 0; ii < VectorBreakout.currentParticles.length; ii++) { - if (VectorBreakout.currentParticles[ii] == null) { + for (byte ii = 0; ii < gameState.currentParticles.length; ii++) { + if (gameState.currentParticles[ii] == null) { ballParticleGen++; switch(ballParticleGen) { case 1: - VectorBreakout.currentParticles[ii] = new Particle(0, this.ballX, this.ballY - 10, 0, -2, 1, 2, 7, 3); - VectorBreakout.currentParticles[ii].spawn(5); + gameState.currentParticles[ii] = new Particle(gameState, 0, this.ballX, this.ballY - 10, 0, -2, 1, 2, 7, 3); + gameState.currentParticles[ii].spawn(5); break; case 2: - VectorBreakout.currentParticles[ii] = new Particle(0, this.ballX - 3, this.ballY - 10, -2, -4, 1, 2, 6, 3); - VectorBreakout.currentParticles[ii].spawn(5); + gameState.currentParticles[ii] = new Particle(gameState, 0, this.ballX - 3, this.ballY - 10, -2, -4, 1, 2, 6, 3); + gameState.currentParticles[ii].spawn(5); break; case 3: - VectorBreakout.currentParticles[ii] = new Particle(0, this.ballX + 10, this.ballY - 10, 2, -4, 1, 2, 8, 3); - VectorBreakout.currentParticles[ii].spawn(5); + gameState.currentParticles[ii] = new Particle(gameState, 0, this.ballX + 10, this.ballY - 10, 2, -4, 1, 2, 8, 3); + gameState.currentParticles[ii].spawn(5); break; } if (ballParticleGen < 3) {continue;} @@ -207,25 +210,25 @@ public class Ball { private void wallBounceAnimation() { byte ballParticleGen = 0; - for (byte ii = 0; ii < VectorBreakout.currentParticles.length; ii++) { - if (VectorBreakout.currentParticles[ii] == null) { + for (byte ii = 0; ii < gameState.currentParticles.length; ii++) { + if (gameState.currentParticles[ii] == null) { ballParticleGen++; switch(ballParticleGen) { case 1: // Up left - VectorBreakout.currentParticles[ii] = new Particle(0, this.ballX - 3, this.ballY - 10, -2, -2, 1, 2, 6, 3); - VectorBreakout.currentParticles[ii].spawn(5); + gameState.currentParticles[ii] = new Particle(gameState, 0, this.ballX - 3, this.ballY - 10, -2, -2, 1, 2, 6, 3); + gameState.currentParticles[ii].spawn(5); break; case 2: // Up right - VectorBreakout.currentParticles[ii] = new Particle(0, this.ballX + 10, this.ballY - 10, 2, -2, 1, 2, 8, 3); - VectorBreakout.currentParticles[ii].spawn(5); + gameState.currentParticles[ii] = new Particle(gameState, 0, this.ballX + 10, this.ballY - 10, 2, -2, 1, 2, 8, 3); + gameState.currentParticles[ii].spawn(5); break; case 3: // Down right - VectorBreakout.currentParticles[ii] = new Particle(0, this.ballX - 3, this.ballY + 3, 2, 2, 1, 2, 2, 3); - VectorBreakout.currentParticles[ii].spawn(5); + gameState.currentParticles[ii] = new Particle(gameState, 0, this.ballX - 3, this.ballY + 3, 2, 2, 1, 2, 2, 3); + gameState.currentParticles[ii].spawn(5); break; case 4: // Down left - VectorBreakout.currentParticles[ii] = new Particle(0, this.ballX + 10, this.ballY + 3, -2, 2, 1, 2, 4, 3); - VectorBreakout.currentParticles[ii].spawn(5); + gameState.currentParticles[ii] = new Particle(gameState, 0, this.ballX + 10, this.ballY + 3, -2, 2, 1, 2, 4, 3); + gameState.currentParticles[ii].spawn(5); break; } if (ballParticleGen < 4) {continue;} diff --git a/src/main/java/dev/boyfailure/quajra/vectorbreakout/Brick.java b/src/main/java/dev/boyfailure/quajra/vectorbreakout/Brick.java index 52061d6..5eb4923 100644 --- a/src/main/java/dev/boyfailure/quajra/vectorbreakout/Brick.java +++ b/src/main/java/dev/boyfailure/quajra/vectorbreakout/Brick.java @@ -12,16 +12,20 @@ public class Brick { byte specialAbility = 0; Hitbox hitBox; + private GameState gameState; + /** * Bricks. - * @param x The x coordinate - * @param y The y coordinate - * @param isBroken Is it broken - * @param brickColor The color of the brick - * @param score The score the player receives upon breaking the brick - * @param specialAbility A special event that occurs upon breaking the brick + * @param gameState The GameState in which the Brick will reside. + * @param x The x coordinate of the Brick. + * @param y The y coordinate of the Brick. + * @param isBroken Determines whether the Brick is broken. + * @param brickColor The color code of the Brick. + * @param score The score the Player earns upon breaking the Brick. + * @param specialAbility A special event that occurs upon breaking the Brick. */ - public Brick(int x, int y, boolean isBroken, int brickColor, int score, int specialAbility) { + public Brick(GameState gameState, int x, int y, boolean isBroken, int brickColor, int score, int specialAbility) { + this.gameState = gameState; this.brickX = (short) x; this.brickY = (short) y; this.isBroken = isBroken; @@ -34,18 +38,18 @@ public class Brick { public void breakBrick() { if (!isBroken) { isBroken = true; - VectorBreakout.score += this.scoreOnBreak + (VectorBreakout.level - 1); - VectorBreakout.currentTextElements[0].setText(String.valueOf(VectorBreakout.score)); - VectorBreakout.bricksOnScreen--; + gameState.score += this.scoreOnBreak + (gameState.level - 1); + gameState.currentTextElements[0].setText(String.valueOf(gameState.score)); + gameState.bricksOnScreen--; switch(this.specialAbility) { case 1: - VectorBreakout.lives++; + gameState.lives++; break; case 2: - for (byte i = 0; i < VectorBreakout.currentBalls.length; i++) { - if (VectorBreakout.currentBalls[i] == null) { - VectorBreakout.currentBalls[i] = new Ball(300, 300, 7, 7, 0, true); - VectorBreakout.currentBalls[i].spawnBall((this.brickX + (this.brickWidth / 2)), (this.brickY + (this.brickHeight / 2))); + for (byte i = 0; i < gameState.currentBalls.length; i++) { + if (gameState.currentBalls[i] == null) { + gameState.currentBalls[i] = new Ball(gameState, 300, 300, 7, 7, 0, true); + gameState.currentBalls[i].spawnBall((this.brickX + (this.brickWidth / 2)), (this.brickY + (this.brickHeight / 2))); return; } } @@ -57,21 +61,21 @@ public class Brick { // Draw particles byte brickParticleGen = 0; - for (byte ii = 0; ii < VectorBreakout.currentParticles.length; ii++) { - if (VectorBreakout.currentParticles[ii] == null) { + for (byte ii = 0; ii < gameState.currentParticles.length; ii++) { + if (gameState.currentParticles[ii] == null) { brickParticleGen++; switch(brickParticleGen) { case 1: - VectorBreakout.currentParticles[ii] = new Particle(0, this.brickX + 12, this.brickY + 3, 1, 1, this.brickColor, 2, 4, 1); - VectorBreakout.currentParticles[ii].spawn(5); + gameState.currentParticles[ii] = new Particle(gameState, 0, this.brickX + 12, this.brickY + 3, 1, 1, this.brickColor, 2, 4, 1); + gameState.currentParticles[ii].spawn(5); break; case 2: - VectorBreakout.currentParticles[ii] = new Particle(0, this.brickX + 20, this.brickY + 13, 1, 1, this.brickColor, 2, 3, 1); - VectorBreakout.currentParticles[ii].spawn(5); + gameState.currentParticles[ii] = new Particle(gameState, 0, this.brickX + 20, this.brickY + 13, 1, 1, this.brickColor, 2, 3, 1); + gameState.currentParticles[ii].spawn(5); break; case 3: - VectorBreakout.currentParticles[ii] = new Particle(0, this.brickX + 32, this.brickY + 7, 1, 1, this.brickColor, 2, 2, 1); - VectorBreakout.currentParticles[ii].spawn(5); + gameState.currentParticles[ii] = new Particle(gameState, 0, this.brickX + 32, this.brickY + 7, 1, 1, this.brickColor, 2, 2, 1); + gameState.currentParticles[ii].spawn(5); break; } if (brickParticleGen < 3) {continue;} diff --git a/src/main/java/dev/boyfailure/quajra/vectorbreakout/GameDisplay.java b/src/main/java/dev/boyfailure/quajra/vectorbreakout/GameDisplay.java index 3844e08..fc268ef 100644 --- a/src/main/java/dev/boyfailure/quajra/vectorbreakout/GameDisplay.java +++ b/src/main/java/dev/boyfailure/quajra/vectorbreakout/GameDisplay.java @@ -9,49 +9,55 @@ import java.awt.geom.Line2D; public class GameDisplay extends JComponent { - float beamX = 0; // Horizontal location of "vector beam" - float beamY = 0; // Vertical location of "vector beam" - // Change this to 1 if you build for older versions of Java. Older versions // will render a leading zero otherwise. (tested on JDK 1.5) byte iStart = 0; + float beamX = 0; // Horizontal location of "vector beam" + float beamY = 0; // Vertical location of "vector beam" + public static float beamScaleX = 1; // Horizontal drawing scale public static float beamScaleY = 1; // Vertical drawing scale public static float beamThicknessScale = 1; // Thickness of lines + private GameState gameState; + + public GameDisplay(GameState gameState) { + this.gameState = gameState; + } + public void paint(Graphics gameGraphics) { super.paint(gameGraphics); Graphics2D g2 = (Graphics2D) gameGraphics; - g2.setColor(Color.white); - // Walls - g2.setColor(Color.gray); - g2.setStroke(bt(10)); - resetBeam(); - moveBeam(15, 0); - g2.draw(drawVec(0, 600)); - moveBeam(770, 0); - g2.draw(drawVec(0, -600)); - moveBeam(15, -5); - g2.draw(drawVec(-800, 0)); + if (gameState.isGameStarted) { + g2.setColor(Color.gray); + g2.setStroke(bt(10)); + resetBeam(); + moveBeam(15, 0); + g2.draw(drawVec(0, 600)); + moveBeam(770, 0); + g2.draw(drawVec(0, -600)); + moveBeam(15, -5); + g2.draw(drawVec(-800, 0)); + } // Bricks - if (VectorBreakout.currentLevel != null) { - for (short i = 0; i < VectorBreakout.currentLevel.levelBricks.length; i++) { - if (VectorBreakout.currentLevel.levelBricks[i] != null) { - if (VectorBreakout.currentLevel.levelBricks[i].isBroken == false) { + if (gameState.currentLevel != null) { + for (short i = 0; i < gameState.currentLevel.levelBricks.length; i++) { + if (gameState.currentLevel.levelBricks[i] != null) { + if (gameState.currentLevel.levelBricks[i].isBroken == false) { resetBeam(); g2.setStroke(bt(3)); - moveBeam(VectorBreakout.currentLevel.levelBricks[i].brickX + 1, VectorBreakout.currentLevel.levelBricks[i].brickY + 1); - g2.setColor(vc(VectorBreakout.currentLevel.levelBricks[i].brickColor)); - g2.draw(drawVec(VectorBreakout.currentLevel.levelBricks[i].brickWidth - 2, 0)); - g2.draw(drawVec(0, -(VectorBreakout.currentLevel.levelBricks[i].brickHeight - 2))); - g2.draw(drawVec(-(VectorBreakout.currentLevel.levelBricks[i].brickWidth - 2), 0)); - g2.draw(drawVec(0, VectorBreakout.currentLevel.levelBricks[i].brickHeight - 2)); + moveBeam(gameState.currentLevel.levelBricks[i].brickX + 1, gameState.currentLevel.levelBricks[i].brickY + 1); + g2.setColor(vc(gameState.currentLevel.levelBricks[i].brickColor)); + g2.draw(drawVec(gameState.currentLevel.levelBricks[i].brickWidth - 2, 0)); + g2.draw(drawVec(0, -(gameState.currentLevel.levelBricks[i].brickHeight - 2))); + g2.draw(drawVec(-(gameState.currentLevel.levelBricks[i].brickWidth - 2), 0)); + g2.draw(drawVec(0, gameState.currentLevel.levelBricks[i].brickHeight - 2)); } } } @@ -59,16 +65,16 @@ public class GameDisplay extends JComponent { // Paddles g2.setColor(Color.white); - for (byte i = 0; i < VectorBreakout.currentPaddles.length; i++) { - if (VectorBreakout.currentPaddles[i] != null) { - if (VectorBreakout.currentPaddles[i].isActive) { + for (byte i = 0; i < gameState.currentPaddles.length; i++) { + if (gameState.currentPaddles[i] != null) { + if (gameState.currentPaddles[i].isActive) { resetBeam(); g2.setStroke(bt(3)); - moveBeam(VectorBreakout.currentPaddles[i].paddleX, VectorBreakout.currentPaddles[i].paddleY); - g2.draw(drawVec(VectorBreakout.currentPaddles[i].paddleWidth, 0)); - g2.draw(drawVec(0, -(VectorBreakout.currentPaddles[i].paddleHeight))); - g2.draw(drawVec(-(VectorBreakout.currentPaddles[i].paddleWidth), 0)); - g2.draw(drawVec(0, VectorBreakout.currentPaddles[i].paddleHeight)); + moveBeam(gameState.currentPaddles[i].paddleX, gameState.currentPaddles[i].paddleY); + g2.draw(drawVec(gameState.currentPaddles[i].paddleWidth, 0)); + g2.draw(drawVec(0, -(gameState.currentPaddles[i].paddleHeight))); + g2.draw(drawVec(-(gameState.currentPaddles[i].paddleWidth), 0)); + g2.draw(drawVec(0, gameState.currentPaddles[i].paddleHeight)); } } } @@ -76,28 +82,28 @@ public class GameDisplay extends JComponent { // Balls g2.setStroke(bt(3)); g2.setColor(Color.yellow); - for (byte i = 0; i < VectorBreakout.currentBalls.length; i++) { - if (VectorBreakout.currentBalls[i] != null) { - if (VectorBreakout.currentBalls[i].isActive) { + for (byte i = 0; i < gameState.currentBalls.length; i++) { + if (gameState.currentBalls[i] != null) { + if (gameState.currentBalls[i].isActive) { resetBeam(); - moveBeam(VectorBreakout.currentBalls[i].ballX, VectorBreakout.currentBalls[i].ballY); - g2.draw(drawVec(VectorBreakout.currentBalls[i].ballWidth, 0)); - g2.draw(drawVec(0, -VectorBreakout.currentBalls[i].ballHeight)); - g2.draw(drawVec(-VectorBreakout.currentBalls[i].ballWidth, 0)); - g2.draw(drawVec(0, VectorBreakout.currentBalls[i].ballHeight)); + moveBeam(gameState.currentBalls[i].ballX, gameState.currentBalls[i].ballY); + g2.draw(drawVec(gameState.currentBalls[i].ballWidth, 0)); + g2.draw(drawVec(0, -gameState.currentBalls[i].ballHeight)); + g2.draw(drawVec(-gameState.currentBalls[i].ballWidth, 0)); + g2.draw(drawVec(0, gameState.currentBalls[i].ballHeight)); } } } // Particles - for (byte i = 0; i < VectorBreakout.currentParticles.length; i++) { - if (VectorBreakout.currentParticles[i] != null) { - if (VectorBreakout.currentParticles[i].isActive) { + for (byte i = 0; i < gameState.currentParticles.length; i++) { + if (gameState.currentParticles[i] != null) { + if (gameState.currentParticles[i].isActive) { resetBeam(); - moveBeam(VectorBreakout.currentParticles[i].particleX, VectorBreakout.currentParticles[i].particleY); - g2.setStroke(bt(VectorBreakout.currentParticles[i].vectorScale)); - g2.setColor(vc(VectorBreakout.currentParticles[i].particleColor)); - g2.draw(drawVec(VectorBreakout.currentParticles[i].particleX2, VectorBreakout.currentParticles[i].particleY2)); + moveBeam(gameState.currentParticles[i].particleX, gameState.currentParticles[i].particleY); + g2.setStroke(bt(gameState.currentParticles[i].vectorScale)); + g2.setColor(vc(gameState.currentParticles[i].particleColor)); + g2.draw(drawVec(gameState.currentParticles[i].particleX2, gameState.currentParticles[i].particleY2)); } } } @@ -113,31 +119,110 @@ public class GameDisplay extends JComponent { * systems, I used these goofy if/else statements so I could compile * on the older JDKs and play on my older systems... */ - for (byte i = 0; i < VectorBreakout.currentTextElements.length; i++) { - if (VectorBreakout.currentTextElements[i] != null) { - if (VectorBreakout.currentTextElements[i].active) { + for (byte i = 0; i < gameState.currentTextElements.length; i++) { + if (gameState.currentTextElements[i] != null) { + if (gameState.currentTextElements[i].active) { resetBeam(); - moveBeam(VectorBreakout.currentTextElements[i].x, VectorBreakout.currentTextElements[i].y); + moveBeam(gameState.currentTextElements[i].x, gameState.currentTextElements[i].y); + + // Save the current beam scale to revert once TextElement is done rendering float oldBeamScaleX = beamScaleX; float oldBeamScaleY = beamScaleY; - beamScaleX = beamScaleX * VectorBreakout.currentTextElements[i].textScale; - beamScaleY = beamScaleY * VectorBreakout.currentTextElements[i].textScale; - g2.setColor(vc(VectorBreakout.currentTextElements[i].textColor)); - g2.setStroke(bt(VectorBreakout.currentTextElements[i].vectorScale)); - String stringToSplit = String.valueOf(VectorBreakout.currentTextElements[i].text); + + // Adjust the beam scale to accomodate the scale of the TextElement + beamScaleX = beamScaleX * gameState.currentTextElements[i].textScale; + beamScaleY = beamScaleY * gameState.currentTextElements[i].textScale; + + // Render the TextElement + g2.setColor(vc(gameState.currentTextElements[i].textColor)); + g2.setStroke(bt(gameState.currentTextElements[i].vectorScale)); + String stringToSplit = String.valueOf(gameState.currentTextElements[i].text); String[] stringToRender = stringToSplit.split(""); + // for (short ii = iStart; ii < stringToRender.length; ii++) { - if ("1".equals(stringToRender[ii])) {moveBeam(7, 0);g2.draw(drawVec(0, -24));moveBeam(11, 24);} - else if ("2".equals(stringToRender[ii])) {moveBeam(0, -24);g2.draw(drawVec(14, 0));g2.draw(drawVec(0, 12));g2.draw(drawVec(-14, 0));g2.draw(drawVec(0, 12));g2.draw(drawVec(14, 0));moveBeam(4, 0);} - else if ("3".equals(stringToRender[ii])) {moveBeam(0, -24);g2.draw(drawVec(14, 0));g2.draw(drawVec(0, 12));g2.draw(drawVec(-14, 0));moveBeam(14, 0);g2.draw(drawVec(0, 12));g2.draw(drawVec(-14, 0));moveBeam(18, 0);} - else if ("4".equals(stringToRender[ii])) {moveBeam(0, -24);g2.draw(drawVec(0, 12));g2.draw(drawVec(14, 0));moveBeam(0, -12);g2.draw(drawVec(0, 24));moveBeam(4, 0);} - else if ("5".equals(stringToRender[ii]) || "S".equals(stringToRender[ii])) {moveBeam(14, -24);g2.draw(drawVec(-14, 0));g2.draw(drawVec(0, 12));g2.draw(drawVec(14, 0));g2.draw(drawVec(0, 12));g2.draw(drawVec(-14, 0));moveBeam(18, 0);} - else if ("6".equals(stringToRender[ii])) {moveBeam(0, -24);g2.draw(drawVec(0, 24));g2.draw(drawVec(14, 0));g2.draw(drawVec(0, -12));g2.draw(drawVec(-14, 0));moveBeam(18, 12);} - else if ("7".equals(stringToRender[ii])) {moveBeam(0, -24);g2.draw(drawVec(14, 0));g2.draw(drawVec(0, 24));moveBeam(4, 0);} - else if ("8".equals(stringToRender[ii])) {g2.draw(drawVec(0, -24));g2.draw(drawVec(14, 0));g2.draw(drawVec(0, 24));g2.draw(drawVec(-14, 0));moveBeam(0, -12);g2.draw(drawVec(14, 0));moveBeam(4, 12);} - else if ("9".equals(stringToRender[ii])) {moveBeam(14, 0);g2.draw(drawVec(0, -24));g2.draw(drawVec(-14, 0));g2.draw(drawVec(0, 12));g2.draw(drawVec(14, 0));moveBeam(4, 12);} - else if ("0".equals(stringToRender[ii]) || "O".equals(stringToRender[ii])) {g2.draw(drawVec(14, 0));g2.draw(drawVec(0, -24));g2.draw(drawVec(-14, 0));g2.draw(drawVec(0, 24));moveBeam(18, 0);} - else if ("\\".equals(stringToRender[ii])) {moveBeam(-(beamX - VectorBreakout.currentTextElements[i].x), 30);} // Newline + if ("1".equals(stringToRender[ii])) { + moveBeam(7, 0); + g2.draw(drawVec(0, -24)); + moveBeam(11, 24); + } + else if ("2".equals(stringToRender[ii])) { + moveBeam(0, -24); + g2.draw(drawVec(14, 0)); + g2.draw(drawVec(0, 12)); + g2.draw(drawVec(-14, 0)); + g2.draw(drawVec(0, 12)); + g2.draw(drawVec(14, 0)); + moveBeam(4, 0); + } + else if ("3".equals(stringToRender[ii])) { + moveBeam(0, -24); + g2.draw(drawVec(14, 0)); + g2.draw(drawVec(0, 12)); + g2.draw(drawVec(-14, 0)); + moveBeam(14, 0); + g2.draw(drawVec(0, 12)); + g2.draw(drawVec(-14, 0)); + moveBeam(18, 0); + } + else if ("4".equals(stringToRender[ii])) { + moveBeam(0, -24); + g2.draw(drawVec(0, 12)); + g2.draw(drawVec(14, 0)); + moveBeam(0, -12); + g2.draw(drawVec(0, 24)); + moveBeam(4, 0); + } + else if ("5".equals(stringToRender[ii]) || "S".equals(stringToRender[ii])) { + moveBeam(14, -24); + g2.draw(drawVec(-14, 0)); + g2.draw(drawVec(0, 12)); + g2.draw(drawVec(14, 0)); + g2.draw(drawVec(0, 12)); + g2.draw(drawVec(-14, 0)); + moveBeam(18, 0); + } + else if ("6".equals(stringToRender[ii])) { + moveBeam(0, -24); + g2.draw(drawVec(0, 24)); + g2.draw(drawVec(14, 0)); + g2.draw(drawVec(0, -12)); + g2.draw(drawVec(-14, 0)); + moveBeam(18, 12); + } + else if ("7".equals(stringToRender[ii])) { + moveBeam(0, -24); + g2.draw(drawVec(14, 0)); + g2.draw(drawVec(0, 24)); + moveBeam(4, 0); + } + else if ("8".equals(stringToRender[ii])) { + g2.draw(drawVec(0, -24)); + g2.draw(drawVec(14, 0)); + g2.draw(drawVec(0, 24)); + g2.draw(drawVec(-14, 0)); + moveBeam(0, -12); + g2.draw(drawVec(14, 0)); + moveBeam(4, 12); + } + else if ("9".equals(stringToRender[ii])) { + moveBeam(14, 0); + g2.draw(drawVec(0, -24)); + g2.draw(drawVec(-14, 0)); + g2.draw(drawVec(0, 12)); + g2.draw(drawVec(14, 0)); + moveBeam(4, 12); + } + else if ("0".equals(stringToRender[ii]) || "O".equals(stringToRender[ii])) { + g2.draw(drawVec(14, 0)); + g2.draw(drawVec(0, -24)); + g2.draw(drawVec(-14, 0)); + g2.draw(drawVec(0, 24)); + moveBeam(18, 0); + } + // Simulates a newline in a TextElement + else if ("\\".equals(stringToRender[ii])) { + moveBeam(-(beamX - gameState.currentTextElements[i].x), 30); + } else if ("A".equals(stringToRender[ii])) { g2.draw(drawVec(0, -17)); g2.draw(drawVec(7, -7)); @@ -359,100 +444,91 @@ public class GameDisplay extends JComponent { g2.draw(drawVec(0, -12)); moveBeam(11, 24); } + else if ("(".equals(stringToRender[ii])) { + moveBeam(10, 0); + g2.draw(drawVec(-6, -6)); + g2.draw(drawVec(0, -12)); + g2.draw(drawVec(6, -6)); + moveBeam(8, 24); + } + else if (")".equals(stringToRender[ii])) { + moveBeam(10, 0); + g2.draw(drawVec(6, -6)); + g2.draw(drawVec(0, -12)); + g2.draw(drawVec(-6, -6)); + moveBeam(8, 24); + } else { // Blank space moveBeam(18, 0); } } + // + + // Revert back to the previous beam scale beamScaleX = oldBeamScaleX; beamScaleY = oldBeamScaleY; } } } - // Paused - g2.setStroke(bt(3)); - if (VectorBreakout.isPaused) { - g2.setColor(Color.white); - resetBeam(); - moveBeam(110, 260); - // P - g2.draw(drawVec(0, -110));g2.draw(drawVec(80, 0));g2.draw(drawVec(0, 50));g2.draw(drawVec(-80, 0)); - moveBeam(100, 60); - // A - g2.draw(drawVec(0, -70));g2.draw(drawVec(40, -40));g2.draw(drawVec(40, 40));g2.draw(drawVec(0, 70));g2.draw(drawVec(0, -40));g2.draw(drawVec(-80, 0)); - moveBeam(100, 40); - // U - moveBeam(0, -110);g2.draw(drawVec(0, 110));g2.draw(drawVec(80, 0));g2.draw(drawVec(0, -110)); - moveBeam(20, 110); - // S - g2.draw(drawVec(80, 0));g2.draw(drawVec(0, -55));g2.draw(drawVec(-80, 0));g2.draw(drawVec(0, -55));g2.draw(drawVec(80, 0)); - moveBeam(20, 110); - // E - moveBeam(80, 0);g2.draw(drawVec(-80, 0));g2.draw(drawVec(0, -55));g2.draw(drawVec(80, 0));moveBeam(-80, 0);g2.draw(drawVec(0, -55));g2.draw(drawVec(80, 0)); - moveBeam(20, 110); - // D - g2.draw(drawVec(40, 0));g2.draw(drawVec(40, -40));g2.draw(drawVec(0, -30));g2.draw(drawVec(-40, -40));g2.draw(drawVec(-40, 0));g2.draw(drawVec(0, 110)); - moveBeam(100, 110); - } - - VectorBreakout.actualFrames++; // Count up actual frames rendered + gameState.actualFrames++; // Count up actual frames rendered // Debug Menu - if (VectorBreakout.debugMenuEnabled) { + if (gameState.debugMenuEnabled) { g2.setColor(Color.white); - g2.drawString("Quajra Debug", 1, 12); - g2.drawString("frame: " + VectorBreakout.frameCounter, 1, 24); + g2.drawString(gameState.gameName + " v" + gameState.gameVersion, 1, 12); + g2.drawString("frame: " + gameState.frameCounter, 1, 24); g2.drawString("beamScaleX: " + beamScaleX, 1, 36); g2.drawString("beamScaleY: " + beamScaleY, 1, 48); - g2.drawString("isPaused: " + VectorBreakout.isPaused, 1, 60); - g2.drawString("targetFrameRate: " + VectorBreakout.targetFrameRate, 150, 24); - g2.drawString("actualFrameRate: " + VectorBreakout.actualFrameRate, 150, 36); - g2.drawString("actualFrames: " + VectorBreakout.actualFrames, 150, 48); - g2.drawString("chancey: " + VectorBreakout.chancey, 150, 60); - g2.drawString("ballsOnScreen: " + VectorBreakout.ballsOnScreen, 300, 24); + g2.drawString("isPaused: " + gameState.isPaused, 1, 60); + g2.drawString("targetFrameRate: " + gameState.targetFrameRate, 150, 24); + g2.drawString("actualFrameRate: " + gameState.actualFrameRate, 150, 36); + g2.drawString("actualFrames: " + gameState.actualFrames, 150, 48); + g2.drawString("chancey: " + gameState.chancey, 150, 60); + g2.drawString("ballsOnScreen: " + gameState.ballsOnScreen, 300, 24); short padBoxX, padBoxY, padBoxX2, padBoxY2; g2.setStroke(new BasicStroke(1)); // Paddles g2.setColor(vc(10, 0.5f)); - for (byte i = 0; i < VectorBreakout.currentPaddles.length; i++) { - if (VectorBreakout.currentPaddles[i] != null) { - padBoxX = (short) VectorBreakout.currentPaddles[i].hitBox.bounds.getBounds2D().getMinX(); - padBoxY = (short) VectorBreakout.currentPaddles[i].hitBox.bounds.getBounds2D().getMinY(); - padBoxX2 = (short) VectorBreakout.currentPaddles[i].hitBox.bounds.getBounds2D().getWidth(); - padBoxY2 = (short) VectorBreakout.currentPaddles[i].hitBox.bounds.getBounds2D().getHeight(); - g2.fillRect(padBoxX, padBoxY + (40 - VectorBreakout.currentPaddles[i].paddleHeight), padBoxX2, padBoxY2); - padBoxX = (short) VectorBreakout.currentPaddles[i].hitBoxLeft.bounds.getBounds2D().getMinX(); - padBoxY = (short) VectorBreakout.currentPaddles[i].hitBoxLeft.bounds.getBounds2D().getMinY(); - padBoxX2 = (short) VectorBreakout.currentPaddles[i].hitBoxLeft.bounds.getBounds2D().getWidth(); - padBoxY2 = (short) VectorBreakout.currentPaddles[i].hitBoxLeft.bounds.getBounds2D().getHeight(); - g2.fillRect(padBoxX, padBoxY + (40 - VectorBreakout.currentPaddles[i].paddleHeight), padBoxX2, padBoxY2); - padBoxX = (short) VectorBreakout.currentPaddles[i].hitBoxRight.bounds.getBounds2D().getMinX(); - padBoxY = (short) VectorBreakout.currentPaddles[i].hitBoxRight.bounds.getBounds2D().getMinY(); - padBoxX2 = (short) VectorBreakout.currentPaddles[i].hitBoxRight.bounds.getBounds2D().getWidth(); - padBoxY2 = (short) VectorBreakout.currentPaddles[i].hitBoxRight.bounds.getBounds2D().getHeight(); - g2.fillRect(padBoxX, padBoxY + (40 - VectorBreakout.currentPaddles[i].paddleHeight), padBoxX2, padBoxY2); + for (byte i = 0; i < gameState.currentPaddles.length; i++) { + if (gameState.currentPaddles[i] != null) { + padBoxX = (short) gameState.currentPaddles[i].hitBox.bounds.getBounds2D().getMinX(); + padBoxY = (short) gameState.currentPaddles[i].hitBox.bounds.getBounds2D().getMinY(); + padBoxX2 = (short) gameState.currentPaddles[i].hitBox.bounds.getBounds2D().getWidth(); + padBoxY2 = (short) gameState.currentPaddles[i].hitBox.bounds.getBounds2D().getHeight(); + g2.fillRect(padBoxX, padBoxY + (40 - gameState.currentPaddles[i].paddleHeight), padBoxX2, padBoxY2); + padBoxX = (short) gameState.currentPaddles[i].hitBoxLeft.bounds.getBounds2D().getMinX(); + padBoxY = (short) gameState.currentPaddles[i].hitBoxLeft.bounds.getBounds2D().getMinY(); + padBoxX2 = (short) gameState.currentPaddles[i].hitBoxLeft.bounds.getBounds2D().getWidth(); + padBoxY2 = (short) gameState.currentPaddles[i].hitBoxLeft.bounds.getBounds2D().getHeight(); + g2.fillRect(padBoxX, padBoxY + (40 - gameState.currentPaddles[i].paddleHeight), padBoxX2, padBoxY2); + padBoxX = (short) gameState.currentPaddles[i].hitBoxRight.bounds.getBounds2D().getMinX(); + padBoxY = (short) gameState.currentPaddles[i].hitBoxRight.bounds.getBounds2D().getMinY(); + padBoxX2 = (short) gameState.currentPaddles[i].hitBoxRight.bounds.getBounds2D().getWidth(); + padBoxY2 = (short) gameState.currentPaddles[i].hitBoxRight.bounds.getBounds2D().getHeight(); + g2.fillRect(padBoxX, padBoxY + (40 - gameState.currentPaddles[i].paddleHeight), padBoxX2, padBoxY2); } } // Balls g2.setColor(vc(11, 1.0f)); - for (byte i = 0; i < VectorBreakout.currentBalls.length; i++) { - if (VectorBreakout.currentBalls[i] != null) { - padBoxX = (short) VectorBreakout.currentBalls[i].hitBox.bounds.getBounds2D().getMinX(); - padBoxY = (short) VectorBreakout.currentBalls[i].hitBox.bounds.getBounds2D().getMinY(); - padBoxX2 = (short) VectorBreakout.currentBalls[i].hitBox.bounds.getBounds2D().getWidth(); - padBoxY2 = (short) VectorBreakout.currentBalls[i].hitBox.bounds.getBounds2D().getHeight(); - g2.fillRect(padBoxX, padBoxY + (40 - VectorBreakout.currentBalls[i].ballHeight), padBoxX2, padBoxY2); + for (byte i = 0; i < gameState.currentBalls.length; i++) { + if (gameState.currentBalls[i] != null) { + padBoxX = (short) gameState.currentBalls[i].hitBox.bounds.getBounds2D().getMinX(); + padBoxY = (short) gameState.currentBalls[i].hitBox.bounds.getBounds2D().getMinY(); + padBoxX2 = (short) gameState.currentBalls[i].hitBox.bounds.getBounds2D().getWidth(); + padBoxY2 = (short) gameState.currentBalls[i].hitBox.bounds.getBounds2D().getHeight(); + g2.fillRect(padBoxX, padBoxY + (40 - gameState.currentBalls[i].ballHeight), padBoxX2, padBoxY2); } } // Bricks g2.setColor(vc(12, 0.5f)); - for (short i = 0; i < VectorBreakout.currentLevel.levelBricks.length; i++) { - if (VectorBreakout.currentLevel.levelBricks[i] != null) { - padBoxX = (short) VectorBreakout.currentLevel.levelBricks[i].hitBox.bounds.getBounds2D().getMinX(); - padBoxY = (short) VectorBreakout.currentLevel.levelBricks[i].hitBox.bounds.getBounds2D().getMinY(); - padBoxX2 = (short) VectorBreakout.currentLevel.levelBricks[i].hitBox.bounds.getBounds2D().getWidth(); - padBoxY2 = (short) VectorBreakout.currentLevel.levelBricks[i].hitBox.bounds.getBounds2D().getHeight(); - g2.fillRect(padBoxX, padBoxY + (40 - VectorBreakout.currentLevel.levelBricks[i].brickHeight), padBoxX2, padBoxY2); + for (short i = 0; i < gameState.currentLevel.levelBricks.length; i++) { + if (gameState.currentLevel.levelBricks[i] != null) { + padBoxX = (short) gameState.currentLevel.levelBricks[i].hitBox.bounds.getBounds2D().getMinX(); + padBoxY = (short) gameState.currentLevel.levelBricks[i].hitBox.bounds.getBounds2D().getMinY(); + padBoxX2 = (short) gameState.currentLevel.levelBricks[i].hitBox.bounds.getBounds2D().getWidth(); + padBoxY2 = (short) gameState.currentLevel.levelBricks[i].hitBox.bounds.getBounds2D().getHeight(); + g2.fillRect(padBoxX, padBoxY + (40 - gameState.currentLevel.levelBricks[i].brickHeight), padBoxX2, padBoxY2); } } } @@ -492,6 +568,8 @@ public class GameDisplay extends JComponent { case 16: return new Color(0, 1, 1, alpha); // aqua case 17: return new Color(0.66f, 0, 0.66f, alpha); // purple case 18: return new Color(1, 0, 1, alpha); // magenta + case 19: return new Color(1, 0.5f, 0.75f, alpha); // pastel pink + case 20: return new Color(0.5f, 0.66f, 1, alpha); // pastel blue default: return new Color(1, 1, 1, alpha); // white } } @@ -510,7 +588,4 @@ public class GameDisplay extends JComponent { beamY = 40 * beamScaleY; } - public GameDisplay() { - } - } diff --git a/src/main/java/dev/boyfailure/quajra/vectorbreakout/GameState.java b/src/main/java/dev/boyfailure/quajra/vectorbreakout/GameState.java new file mode 100644 index 0000000..dec3d40 --- /dev/null +++ b/src/main/java/dev/boyfailure/quajra/vectorbreakout/GameState.java @@ -0,0 +1,283 @@ +package dev.boyfailure.quajra.vectorbreakout; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; + +public class GameState { + + public String gameName = "Vector Breakout"; + public String gameVersion = "20250511"; + public boolean isGameStarted = false; + public boolean isPaused = false; + public boolean debugMenuEnabled = false; + public boolean isLost = false; + public boolean movingLeft = false; + public boolean movingRight = false; + public boolean confettiMode = false; + public byte lives = 5; + public byte chancey = 0; + public byte ballsOnScreen = 0; + public byte targetFrameRate = 60; + public byte actualFrames = 0; + public byte actualFrameRate = 0; + public byte gameTickRate = 60; + public short gameResX = 800; + public short gameResY = 600; + public short level = 1; + public short bricksOnScreen = 0; + public int score = 0; + public long frameCounter = 0; + public Ball[] currentBalls = new Ball[10]; + public Particle[] currentParticles = new Particle[127]; + public Paddle[] currentPaddles = new Paddle[2]; + public TextElement[] currentTextElements = new TextElement[63]; + public Level currentLevel; + + // Debug stuffs + public boolean debugSpawnAllBalls = false; + public boolean debugUseGiantPaddle = false; + public short debugStartLevel = 1; + + public GameState() { + this.showTitleScreen(); + } + + public KeyListener getKeyListener() { + return new GameKeyListener(); + } + + public byte getGameTickRate() { + return gameTickRate; + } + + public byte getTargetFrameRate() { + return targetFrameRate; + } + + public ActionListener getGameStateUpdateActionListener() { + return new GameStateUpdateListener(); + } + + public ActionListener getGameTickActionListener() { + return new GameTickActionListener(); + } + class GameTickActionListener implements ActionListener { + public void actionPerformed(ActionEvent e) { + onGameTick(); + } + } + + class GameKeyListener implements KeyListener { + public void keyPressed(KeyEvent e) { + switch(e.getKeyCode()) { + case 27: // Escape + debugMenuEnabled = !debugMenuEnabled; + break; + case 10: // Enter + if (isGameStarted) { + isPaused = !isPaused; + if (!isPaused) { + for (byte i = 0; i < currentTextElements.length; i++) { + if (currentTextElements[i] != null && "PAUSED".equals(currentTextElements[i].text)) { + currentTextElements[i].cull(); + return; + } + } + } + else { + for (byte i = 0; i < currentTextElements.length; i++) { + if (currentTextElements[i] == null) { + currentTextElements[i] = new TextElement(GameState.this, "PAUSED", 5, 142, 310, 1, 4); + currentTextElements[i].activate(); + return; + } + } + } + } + break; + case 35: // End + if (isLost || !isGameStarted) {isGameStarted = true;newGame(true);return;} + break; + case 32: // Space + if (isLost || !isGameStarted) {isGameStarted = true;newGame();return;} + if (!isPaused) { + if (ballsOnScreen < 1) { + for (byte i = 0; i < currentBalls.length; i++) { + if (currentBalls[i] == null) { + currentBalls[i] = new Ball(GameState.this, 200, 300, 7, 7, 0, true); + currentBalls[i].spawnBall(true); + if (!debugSpawnAllBalls) {return;} + } + } + } + } + break; + case 37: // Left + if (isGameStarted) {movingLeft = true;} + break; + case 39: // Right + if (isGameStarted) {movingRight = true;} + break; + } + } + public void keyReleased(KeyEvent e) { + switch(e.getKeyCode()) { + case 37: // Left + movingLeft = false; + break; + case 39: // Right + movingRight = false; + break; + } + } + public void keyTyped(KeyEvent e) {} + } + + class GameStateUpdateListener implements ActionListener { + public void actionPerformed(ActionEvent e) { + bricksOnScreen = 0; + actualFrameRate = actualFrames; + actualFrames = 0; + if (currentLevel != null) { + for (short i = 0; i < currentLevel.levelBricks.length; i++) { + if (currentLevel.levelBricks[i] != null) { + if (currentLevel.levelBricks[i].isBroken) { + currentLevel.levelBricks[i] = null; + } + else {bricksOnScreen++;} + } + } + } + + for (byte i = 0; i < currentBalls.length; i++) { + if (currentBalls[i] != null) { + if (currentBalls[i].isActive == false) { + currentBalls[i] = null; + } + } + } + for (byte i = 0; i < currentParticles.length; i++) { + if (currentParticles[i] != null) { + if (currentParticles[i].isActive == false) { + currentParticles[i] = null; + } + } + } + for (byte i = 0; i < currentTextElements.length; i++) { + if (currentTextElements[i] != null) { + if (currentTextElements[i].active == false) { + currentTextElements[i] = null; + } + } + } + + if (bricksOnScreen <= 0 && isGameStarted && !isLost) { + level++; + currentLevel = new Level(GameState.this, level); + currentLevel.startLevel(); + } + } + } + + public void incrementFrameCounter() { + this.frameCounter++; + } + + public void onGameTick() { + // i will implement something better than "chancey" eventually + chancey++; + if (chancey >= 16) { + chancey = 0; + } + + if (isPaused) {return;} + + // Move player if keys are held + for (byte i = 0; i < currentPaddles.length; i++) { + if (currentPaddles[i] != null) { + if (movingLeft) { + currentPaddles[i].movePaddle(false); + } + else if (movingRight) { + currentPaddles[i].movePaddle(true); + } + else { + currentPaddles[i].paddleSpeed = 0; + } + } + } + + // Ball logic (movement, collision checks) + for (byte i = 0; i < currentBalls.length; i++) { + if (currentBalls[i] != null) { + if (currentBalls[i].isActive) { + currentBalls[i].moveBall(); + } + } + } + + // Particles + for (byte i = 0; i < currentParticles.length; i++) { + if (currentParticles[i] != null) { + if (currentParticles[i].isActive) { + currentParticles[i].update(); + } + } + } + } + + public void showTitleScreen() { + currentTextElements[0] = new TextElement(this, "VECTOR BREAKOUT", 2.5f, 67, 280, 15, 2); + currentTextElements[1] = new TextElement(this, "MMXXV BOYFAILURE.DEV", 0.75f, 259, 315, 15, 1); + currentTextElements[2] = new TextElement(this, "PRESS SPACE TO BEGIN", 1, 215, 345, 15, 2); + currentTextElements[0].activate(); + currentTextElements[1].activate(); + currentTextElements[2].activate(); + } + + public void gameLose() { + if (currentLevel != null) { + for (short i = 0; i < currentLevel.levelBricks.length; i++) { + currentLevel.levelBricks[i] = null; + } + } + for (byte i = 0; i < currentBalls.length; i++) { + currentBalls[i] = null; + } + isLost = true; + ballsOnScreen = 0; + } + + public void newGame() { + newGame(false); + } + + public void newGame(boolean debugLevel) { + for (byte i = 0; i < currentTextElements.length; i++) { + currentTextElements[i] = null; + } + lives = 5; + isLost = false; + score = 0; + if (debugLevel) {level = 32767;} + else {level = 1;} + frameCounter = 0; + ballsOnScreen = 0; + movingLeft = false; + movingRight = false; + if (debugUseGiantPaddle) {currentPaddles[0] = new Paddle(this, 0, 500, 800, 60, 0);} + else {currentPaddles[0] = new Paddle(this, 350, 500, 100, 15, 0);} + currentTextElements[0] = new TextElement(this, String.valueOf(score), 1, 20, -13, 1, 2); + currentTextElements[1] = new TextElement(this, String.valueOf(lives), 1, 200, -13, 1, 2); + currentTextElements[2] = new TextElement(this, String.valueOf(level), 1, 380, -13, 1, 2); + currentTextElements[0].activate(); + currentTextElements[1].activate(); + currentTextElements[2].activate(); + if (debugStartLevel > 1) {level = debugStartLevel;} + currentLevel = new Level(this, level); + currentLevel.startLevel(); + } + +} diff --git a/src/main/java/dev/boyfailure/quajra/vectorbreakout/Level.java b/src/main/java/dev/boyfailure/quajra/vectorbreakout/Level.java index 1b48c68..746ad91 100644 --- a/src/main/java/dev/boyfailure/quajra/vectorbreakout/Level.java +++ b/src/main/java/dev/boyfailure/quajra/vectorbreakout/Level.java @@ -1,8 +1,5 @@ package dev.boyfailure.quajra.vectorbreakout; -import java.util.Timer; -import java.util.TimerTask; - public class Level { short levelID; @@ -12,7 +9,10 @@ public class Level { byte[] colorMap; Brick[] levelBricks; - public Level(int id) { + private GameState gameState; + + public Level(GameState gameState, int id) { + this.gameState = gameState; this.levelID = (short) id; switch(id) { @@ -33,39 +33,125 @@ public class Level { this.levelName = "DOUBLE TROUBLE"; this.levelLayout = new byte[] {4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, - 3,3,3,3,3,5,3,3,3,3,3,3,3,5,3,3,3,3,3, + 3,3,3,3,3,16,3,3,3,3,3,3,3,16,3,3,3,3,3, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}; + this.colorMap = new byte[] + {10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 13,13,13,13,13,18,13,13,13,13,13,13,13,18,13,13,13,13,13, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12}; break; case 3: - this.levelName = "THINKY THINKY..."; + this.levelName = "THE GAP"; this.levelLayout = new byte[] - {4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2, - 3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1, - 2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4, - 1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3}; + {5,5,5,5,5,5,5,5,0,0,0,5,5,5,5,5,5,5,5, + 4,4,4,4,4,4,4,4,0,0,0,4,4,4,4,4,4,4,4, + 3,3,3,3,3,16,3,3,0,0,0,3,3,16,3,3,3,3,3, + 2,2,2,2,2,2,2,2,0,0,0,2,2,2,2,2,2,2,2, + 1,1,1,1,1,1,1,1,0,0,0,1,1,1,1,1,1,1,1}; + this.colorMap = new byte[] + {14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 13,13,13,13,13,18,13,13,13,13,13,13,13,18,13,13,13,13,13, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12}; break; case 4: - this.levelName = "HOLES!!"; + this.levelName = "HOLE IN THE WALL"; this.levelLayout = new byte[] {4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, 3,3,0,0,3,3,3,3,0,0,0,3,3,3,3,0,0,3,3, 2,2,0,0,2,2,2,2,0,0,0,2,2,2,2,0,0,2,2, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}; + this.colorMap = new byte[] + {10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12}; + break; + case 5: + this.levelName = "A SECOND OPINION"; + this.levelLayout = new byte[] + {0,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,0, + 0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0, + 0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0, + 0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,0, + 0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0, + 0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0, + 0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0}; + this.colorMap = new byte[] + {10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12}; + break; + case 6: + this.levelName = "SLICES"; + this.levelLayout = new byte[] + {5,5,0,5,5,0,5,5,0,5,5,0,5,5,0,5,5,0,5, + 4,0,4,4,0,4,4,0,4,4,0,4,4,0,4,4,0,4,4, + 0,3,3,0,3,3,0,3,3,0,3,3,0,3,3,0,3,3,0, + 2,2,0,2,2,0,2,2,0,2,2,0,2,2,0,2,2,0,2, + 1,0,1,1,0,1,1,0,1,1,0,1,1,0,1,1,0,1,1, + 0,4,4,0,4,4,0,4,4,0,4,4,0,4,4,0,4,4,0, + 3,3,0,3,3,0,3,3,0,3,3,0,3,3,0,3,3,0,3, + 2,0,2,2,0,2,2,0,2,2,0,2,2,0,2,2,0,2,2, + 0,1,1,0,1,1,0,1,1,0,1,1,0,1,1,0,1,1,0}; + this.colorMap = new byte[] + {14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12}; break; case 1001: this.levelName = "TRANS RIGHTS"; this.levelLayout = new byte[] - {2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + {5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, + 3,3,3,3,3,3,3,3,3,3,3,3,3,5,3,3,3,3,3, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, - 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, - 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, - 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, - 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, - 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2}; + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}; + this.colorMap = new byte[] + {20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20, + 19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20}; + break; + case 1002: + this.levelName = "I USE ARCH BTW"; + this.levelLayout = new byte[] + //1 2 3 4 5 6 7 8 9 0 + {0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,6,6,6,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,5,5,5,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,4,4,0,4,4,0,0,0,0,0,0,0, + 0,0,0,0,0,0,3,3,0,0,0,3,3,0,0,0,0,0,0, + 0,0,0,0,0,2,2,2,0,0,0,2,2,2,0,0,0,0,0, + 0,0,0,1,1,1,1,1,0,0,0,1,1,1,1,1,0,0,0}; + this.colorMap = new byte[] + {16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, + 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, + 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, + 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, + 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, + 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, + 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, + 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16}; break; case 32767: this.levelName = "DEBUG.LEVEL;"; @@ -89,12 +175,17 @@ public class Level { 7,8,9,10,11,12,13,14,15,16,17,18,0,1,2,3,4,5,6}; break; default: - this.levelName = "LEVEL " + levelID; + this.levelName = "LEVEL 404: NOT FOUND (" + levelID + ")"; this.levelLayout = new byte[] {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}; + this.colorMap = new byte[] + {2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2}; break; } @@ -108,25 +199,28 @@ public class Level { for (int i = 0; i < this.levelLayout.length; i++) { switch (this.levelLayout[i]) { case 1: - this.levelBricks[i] = new Brick(brickLeftPlacement, (((int) (i / 19)) * this.rowHeight) + 50, false, this.colorMap[i], 4, 0); + this.levelBricks[i] = new Brick(gameState, brickLeftPlacement, (((int) (i / 19)) * this.rowHeight) + 50, false, this.colorMap[i], 4, 0); break; case 2: - this.levelBricks[i] = new Brick(brickLeftPlacement, (((int) (i / 19)) * this.rowHeight) + 50, false, this.colorMap[i], 8, 0); + this.levelBricks[i] = new Brick(gameState, brickLeftPlacement, (((int) (i / 19)) * this.rowHeight) + 50, false, this.colorMap[i], 8, 0); break; case 3: - this.levelBricks[i] = new Brick(brickLeftPlacement, (((int) (i / 19)) * this.rowHeight) + 50, false, this.colorMap[i], 16, 0); + this.levelBricks[i] = new Brick(gameState, brickLeftPlacement, (((int) (i / 19)) * this.rowHeight) + 50, false, this.colorMap[i], 16, 0); break; case 4: - this.levelBricks[i] = new Brick(brickLeftPlacement, (((int) (i / 19)) * this.rowHeight) + 50, false, this.colorMap[i], 32, 0); + this.levelBricks[i] = new Brick(gameState, brickLeftPlacement, (((int) (i / 19)) * this.rowHeight) + 50, false, this.colorMap[i], 32, 0); break; case 5: - this.levelBricks[i] = new Brick(brickLeftPlacement, (((int) (i / 19)) * this.rowHeight) + 50, false, this.colorMap[i], 8, 2); + this.levelBricks[i] = new Brick(gameState, brickLeftPlacement, (((int) (i / 19)) * this.rowHeight) + 50, false, this.colorMap[i], 64, 0); break; case 6: - this.levelBricks[i] = new Brick(brickLeftPlacement, (((int) (i / 19)) * this.rowHeight) + 50, false, this.colorMap[i], 8, 0); + this.levelBricks[i] = new Brick(gameState, brickLeftPlacement, (((int) (i / 19)) * this.rowHeight) + 50, false, this.colorMap[i], 8, 0); break; case 7: - this.levelBricks[i] = new Brick(brickLeftPlacement, (((int) (i / 19)) * this.rowHeight) + 50, false, this.colorMap[i], 8, 0); + this.levelBricks[i] = new Brick(gameState, brickLeftPlacement, (((int) (i / 19)) * this.rowHeight) + 50, false, this.colorMap[i], 8, 0); + break; + case 16: // Spawns a ball + this.levelBricks[i] = new Brick(gameState, brickLeftPlacement, (((int) (i / 19)) * this.rowHeight) + 50, false, this.colorMap[i], 8, 2); break; default: this.levelBricks[i] = null; @@ -136,17 +230,10 @@ public class Level { if (brickLeftPlacement >= 760) {brickLeftPlacement = 20;} } } - VectorBreakout.bricksOnScreen = (short) this.levelBricks.length; - VectorBreakout.currentTextElements[2].setText(String.valueOf(this.levelID)); - VectorBreakout.currentTextElements[3] = new TextElement("", 1, 24, 480, 15, 2); - VectorBreakout.currentTextElements[3].setText("LEVEL " + String.valueOf(this.levelID) + ": " + this.levelName); - VectorBreakout.currentTextElements[3].activate(); - Timer levelTitleGTFO = new Timer(); - levelTitleGTFO.schedule(new TimerTask() { - public void run() { - VectorBreakout.currentTextElements[3].cull(); - } - }, 3000); + gameState.bricksOnScreen = (short) this.levelBricks.length; + gameState.currentTextElements[2].setText(String.valueOf(this.levelID)); + gameState.currentTextElements[3] = new TextElement(gameState, this.levelName, 1, 24, 480, 15, 2, 3000); + gameState.currentTextElements[3].activate(); } } diff --git a/src/main/java/dev/boyfailure/quajra/vectorbreakout/Menu.java b/src/main/java/dev/boyfailure/quajra/vectorbreakout/Menu.java new file mode 100644 index 0000000..42a2448 --- /dev/null +++ b/src/main/java/dev/boyfailure/quajra/vectorbreakout/Menu.java @@ -0,0 +1,14 @@ +package dev.boyfailure.quajra.vectorbreakout; + +public class Menu { + + public String menuTitle = "Menu"; + public byte index = 0; + + private GameState gameState; + + public Menu(GameState gameState) { + this.gameState = gameState; + } + +} diff --git a/src/main/java/dev/boyfailure/quajra/vectorbreakout/Paddle.java b/src/main/java/dev/boyfailure/quajra/vectorbreakout/Paddle.java index ee29202..56b4e0e 100644 --- a/src/main/java/dev/boyfailure/quajra/vectorbreakout/Paddle.java +++ b/src/main/java/dev/boyfailure/quajra/vectorbreakout/Paddle.java @@ -14,7 +14,10 @@ public class Paddle { Hitbox hitBoxLeft; Hitbox hitBoxRight; - public Paddle(int x, int y, int width, int height, int paddleColor) { + private GameState gameState; + + public Paddle(GameState gameState, int x, int y, int width, int height, int paddleColor) { + this.gameState = gameState; this.paddleX = (short) x; this.paddleY = (short) y; this.paddleWidth = (short) width; @@ -30,7 +33,7 @@ public class Paddle { if (direction) {this.paddleX += (this.paddleSpeed * this.speedMultiplier);} else {this.paddleX -= (this.paddleSpeed * this.speedMultiplier);} if (this.paddleX <= 20) {this.paddleX = 20;} - else if (this.paddleX >= (VectorBreakout.gameResX - this.paddleWidth - 20)) {this.paddleX = (short) (VectorBreakout.gameResX - this.paddleWidth - 20);} + else if (this.paddleX >= (gameState.gameResX - this.paddleWidth - 20)) {this.paddleX = (short) (gameState.gameResX - this.paddleWidth - 20);} this.hitBox.moveTo(this.paddleX, this.paddleY, this.paddleWidth, this.paddleHeight); this.hitBoxLeft.moveTo(this.paddleX, this.paddleY, this.paddleWidth / 5, this.paddleHeight); diff --git a/src/main/java/dev/boyfailure/quajra/vectorbreakout/Particle.java b/src/main/java/dev/boyfailure/quajra/vectorbreakout/Particle.java index b7d92de..dd97a73 100644 --- a/src/main/java/dev/boyfailure/quajra/vectorbreakout/Particle.java +++ b/src/main/java/dev/boyfailure/quajra/vectorbreakout/Particle.java @@ -15,7 +15,10 @@ public class Particle { byte particleDirection; byte particleSpeed = 2; - public Particle(int particleID, int x, int y, int x2, int y2, int particleColor, float vectorScale, int direction, int speed) { + private GameState gameState; + + public Particle(GameState gameState, int particleID, int x, int y, int x2, int y2, int particleColor, float vectorScale, int direction, int speed) { + this.gameState = gameState; this.particleID = (byte) particleID; this.particleX = (short) x; this.particleY = (short) y; @@ -29,7 +32,7 @@ public class Particle { public void spawn(int lifetime) { this.isActive = true; - if (VectorBreakout.confettiMode) {this.particleLifetime = 400;} + if (gameState.confettiMode) {this.particleLifetime = 400;} else {this.particleLifetime = (short) lifetime;} } diff --git a/src/main/java/dev/boyfailure/quajra/vectorbreakout/TextElement.java b/src/main/java/dev/boyfailure/quajra/vectorbreakout/TextElement.java index 4c5b35a..d41d897 100644 --- a/src/main/java/dev/boyfailure/quajra/vectorbreakout/TextElement.java +++ b/src/main/java/dev/boyfailure/quajra/vectorbreakout/TextElement.java @@ -1,5 +1,8 @@ package dev.boyfailure.quajra.vectorbreakout; +import java.util.Timer; +import java.util.TimerTask; + public class TextElement { String text; @@ -9,16 +12,22 @@ public class TextElement { byte textColor; short x; short y; + short duration = 0; + long creationTime; + + private GameState gameState; /** * Text elements. + * @param gameState The GameState in which the TextElement will reside. * @param text The text that will be displayed. * @param textScale The size of the text on screen. * @param x The x coordinate of the text's origin point. * @param y The y coordinate of the text's origin point. * @param textColor The color of the text. */ - public TextElement(String text, float textScale, int x, int y, int textColor, float vectorScale) { + public TextElement(GameState gameState, String text, float textScale, int x, int y, int textColor, float vectorScale) { + this.gameState = gameState; this.text = text; this.textScale = textScale; this.x = (short) x; @@ -27,11 +36,48 @@ public class TextElement { this.vectorScale = vectorScale; } + /** + * Text elements with a fadeout timer. + * @param gameState The GameState in which the TextElement will reside. + * @param text The text that will be displayed. + * @param textScale The size of the text on screen. + * @param x The x coordinate of the text's origin point. + * @param y The y coordinate of the text's origin point. + * @param textColor The color of the text. + * @param duration How long the text will stay on screen, in milliseconds. + */ + public TextElement(GameState gameState, String text, float textScale, int x, int y, int textColor, float vectorScale, int duration) { + this.gameState = gameState; + this.text = text; + this.textScale = textScale; + this.x = (short) x; + this.y = (short) y; + this.textColor = (byte) textColor; + this.vectorScale = vectorScale; + this.duration = (short) duration; + this.creationTime = System.currentTimeMillis(); + } + /** * Prepares the TextElement for rendering. */ public void activate() { this.active = true; + long testLong = this.creationTime; + if (this.duration > 0) { + Timer levelTitleGTFO = new Timer(); + levelTitleGTFO.schedule(new TimerTask() { + public void run() { + for (byte i = 0; i < gameState.currentTextElements.length; i++) { + if (gameState.currentTextElements[i] != null) { + if (gameState.currentTextElements[i].creationTime == testLong) { + gameState.currentTextElements[i].cull(); + } + } + } + } + }, this.duration); + } } /** @@ -63,7 +109,7 @@ public class TextElement { /** * Changes the color of the text. - * @param textColor The color of the text. + * @param textColor The color code of the text. */ public void setColor(int textColor) { this.textColor = (byte) textColor; diff --git a/src/main/java/dev/boyfailure/quajra/vectorbreakout/VectorBreakout.java b/src/main/java/dev/boyfailure/quajra/vectorbreakout/VectorBreakout.java index 0a9d05f..29cc889 100644 --- a/src/main/java/dev/boyfailure/quajra/vectorbreakout/VectorBreakout.java +++ b/src/main/java/dev/boyfailure/quajra/vectorbreakout/VectorBreakout.java @@ -3,8 +3,6 @@ package dev.boyfailure.quajra.vectorbreakout; import java.awt.Color; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; -import java.awt.event.KeyEvent; -import java.awt.event.KeyListener; import java.awt.event.ComponentAdapter; import java.awt.event.ComponentEvent; import javax.swing.JFrame; @@ -12,260 +10,53 @@ import javax.swing.Timer; public class VectorBreakout { - public static String gameName = "Vector Breakout"; - public static boolean isGameStarted = false; - public static boolean isPaused = false; - public static boolean debugMenuEnabled = false; - public static boolean isLost = false; - public static boolean movingLeft = false; - public static boolean movingRight = false; - public static boolean confettiMode = false; - public static byte lives = 5; - public static byte chancey = 0; - public static byte ballsOnScreen = 0; - public static byte targetFrameRate = 60; - public static byte actualFrames = 0; - public static byte actualFrameRate = 0; - public static byte gameTickRate = 60; - public static short gameResX = 800; - public static short gameResY = 600; - public static short level = 1; - public static short bricksOnScreen = 0; - public static int score = 0; - public static long frameCounter = 0; - public static Ball[] currentBalls = new Ball[10]; - public static Particle[] currentParticles = new Particle[127]; - public static Paddle[] currentPaddles = new Paddle[2]; - public static TextElement[] currentTextElements = new TextElement[63]; - public static Level currentLevel; + // The embeddable game + public static GameState gameState = new GameState(); + + // The window for the game + public static JFrame gameFrame = new JFrame(); + public static GameDisplay gameCanvas = new GameDisplay(gameState); public VectorBreakout() { } public static void main(String[] args) { + + // Enable OpenGL for hardware acceleration System.setProperty("sun.java2d.opengl", "true"); - final JFrame gameFrame = new JFrame(); - + + // Set the game window's properties gameFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - gameFrame.setTitle(gameName); - gameFrame.setSize(gameResX, gameResY); + gameFrame.setTitle(gameState.gameName); + gameFrame.setSize(gameState.gameResX, gameState.gameResY); gameFrame.getContentPane().setBackground(Color.black); - - final GameDisplay gameCanvas = new GameDisplay(); gameFrame.getContentPane().add(gameCanvas); - gameFrame.setVisible(true); - gameFrame.addKeyListener(new KeyListener() { - public void keyPressed(KeyEvent e) { - switch(e.getKeyCode()) { - case 27: // Escape - debugMenuEnabled = !debugMenuEnabled; - break; - case 10: // Enter - if (isGameStarted) {isPaused = !isPaused;} - break; - case 35: // End - if (isLost || !isGameStarted) {isGameStarted = true;newGame(true);return;} - break; - case 32: // Space - if (isLost || !isGameStarted) {isGameStarted = true;newGame();return;} - if (!isPaused) { - if (ballsOnScreen < 1) { - for (byte i = 0; i < currentBalls.length; i++) { - if (currentBalls[i] == null) { - currentBalls[i] = new Ball(200, 300, 7, 7, 0, true); - currentBalls[i].spawnBall(true); - return; - } - } - } - } - break; - case 37: // Left - if (isGameStarted) {movingLeft = true;} - break; - case 39: // Right - if (isGameStarted) {movingRight = true;} - break; - } - } - public void keyReleased(KeyEvent e) { - switch(e.getKeyCode()) { - case 37: // Left - movingLeft = false; - break; - case 39: // Right - movingRight = false; - break; - } - } - public void keyTyped(KeyEvent e) {} - }); - + gameFrame.addKeyListener(gameState.getKeyListener()); + // Adapts vector drawing scale when the window is resized gameFrame.addComponentListener(new ComponentAdapter() { public void componentResized(ComponentEvent componentEvent) { - // Experiment for ensuring scaling at a 4:3 ratio gameFrame.setSize((int) Math.abs(gameFrame.getSize().height * 1.333), gameFrame.getSize().height); - gameCanvas.beamScaleX = gameFrame.getBounds().width / 800f; - gameCanvas.beamScaleY = gameFrame.getBounds().height / 600f; - if (gameCanvas.beamScaleX <= gameCanvas.beamScaleY) {gameCanvas.beamThicknessScale = gameCanvas.beamScaleX;} - else {gameCanvas.beamThicknessScale = gameCanvas.beamScaleY;} - } - }); - - Timer gameTick = new Timer((1000 / gameTickRate), new ActionListener(){ - public void actionPerformed(ActionEvent e) { - onGameTick(); + GameDisplay.beamScaleX = gameFrame.getBounds().width / 800f; + GameDisplay.beamScaleY = gameFrame.getBounds().height / 600f; } }); + + Timer gameTick = new Timer((1000 / gameState.getGameTickRate()), gameState.getGameTickActionListener()); gameTick.start(); - - Timer frameDisplay = new Timer((1000 / targetFrameRate), new ActionListener(){ + + Timer frameDisplay = new Timer((1000 / gameState.getTargetFrameRate()), new ActionListener() { public void actionPerformed(ActionEvent e) { - frameCounter++; + gameState.incrementFrameCounter(); gameFrame.repaint(); } }); frameDisplay.start(); - Timer gameCuller = new Timer(1000, new ActionListener(){ - public void actionPerformed(ActionEvent e) { - actualFrameRate = actualFrames; - actualFrames = 0; - if (currentLevel != null) { - for (short i = 0; i < currentLevel.levelBricks.length; i++) { - if (currentLevel.levelBricks[i] != null) { - if (currentLevel.levelBricks[i].isBroken) { - currentLevel.levelBricks[i] = null; - } - } - } - } - - for (byte i = 0; i < currentBalls.length; i++) { - if (currentBalls[i] != null) { - if (currentBalls[i].isActive == false) { - currentBalls[i] = null; - } - } - } - for (byte i = 0; i < currentParticles.length; i++) { - if (currentParticles[i] != null) { - if (currentParticles[i].isActive == false) { - currentParticles[i] = null; - } - } - } - for (byte i = 0; i < currentTextElements.length; i++) { - if (currentTextElements[i] != null) { - if (currentTextElements[i].active == false) { - currentTextElements[i] = null; - } - } - } - - if (bricksOnScreen <= 0 && isGameStarted) { - level++; - currentLevel = new Level(level); - currentLevel.startLevel(); - } - } - }); + Timer gameCuller = new Timer(1000, gameState.getGameStateUpdateActionListener()); gameCuller.start(); - - currentTextElements[0] = new TextElement("VECTOR BREAKOUT", 2.5f, 67, 280, 15, 2); - currentTextElements[1] = new TextElement("MMXXV BOYFAILURE.DEV", 0.75f, 259, 315, 15, 1); - currentTextElements[2] = new TextElement("PRESS SPACE TO BEGIN", 1, 215, 345, 15, 2); - currentTextElements[0].activate(); - currentTextElements[1].activate(); - currentTextElements[2].activate(); + } - - public static void onGameTick() { - // i will implement something better than "chancey" eventually - chancey++; - if (chancey >= 16) { - chancey = 0; - } - - if (isPaused) {return;} - - // Move player if keys are held - for (byte i = 0; i < currentPaddles.length; i++) { - if (currentPaddles[i] != null) { - if (movingLeft) { - currentPaddles[i].movePaddle(false); - } - else if (movingRight) { - currentPaddles[i].movePaddle(true); - } - else { - currentPaddles[i].paddleSpeed = 0; - } - } - } - - // Ball logic (movement, collision checks) - for (byte i = 0; i < currentBalls.length; i++) { - if (currentBalls[i] != null) { - if (currentBalls[i].isActive) { - currentBalls[i].moveBall(); - } - } - } - - // Particles - for (byte i = 0; i < currentParticles.length; i++) { - if (currentParticles[i] != null) { - if (currentParticles[i].isActive) { - currentParticles[i].update(); - } - } - } - } - - - public static void gameLose() { - if (currentLevel != null) { - for (short i = 0; i < currentLevel.levelBricks.length; i++) { - currentLevel.levelBricks[i] = null; - } - } - for (byte i = 0; i < currentBalls.length; i++) { - currentBalls[i] = null; - } - isLost = true; - ballsOnScreen = 0; - } - - public static void newGame() { - newGame(false); - } - - public static void newGame(boolean debugLevel) { - for (byte i = 0; i < currentTextElements.length; i++) { - currentTextElements[i] = null; - } - lives = 5; - isLost = false; - score = 0; - if (debugLevel) {level = 32767;} - else {level = 1;} - frameCounter = 0; - ballsOnScreen = 0; - movingLeft = false; - movingRight = false; - currentPaddles[0] = new Paddle(350, 500, 100, 15, 0); - //test currentPaddles[0] = new Paddle(0, 500, 800, 60, 0); - currentTextElements[0] = new TextElement(String.valueOf(score), 1, 20, -13, 1, 2); - currentTextElements[1] = new TextElement(String.valueOf(lives), 1, 200, -13, 1, 2); - currentTextElements[2] = new TextElement(String.valueOf(level), 1, 380, -13, 1, 2); - currentTextElements[0].activate(); - currentTextElements[1].activate(); - currentTextElements[2].activate(); - currentLevel = new Level(level); - currentLevel.startLevel(); - } }