diff --git a/nb-configuration.xml b/nb-configuration.xml
new file mode 100644
index 0000000..e767c48
--- /dev/null
+++ b/nb-configuration.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+ apache20
+
+
diff --git a/pom.xml b/pom.xml
index d8dea36..fddf6fe 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,9 +4,9 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4.0.0
- dev.boyfailure.quajra
+ dev.boyfailure
VectorBreakout
- 20250411_test1
+ 1.0
jar
@@ -18,7 +18,7 @@
21
21
UTF-8
- dev.boyfailure.quajra.vectorbreakout.VectorBreakout
+ dev.boyfailure.vectorbreakout.VectorBreakout
@@ -30,7 +30,7 @@
- dev.boyfailure.quajra.vectorbreakout.VectorBreakout
+ dev.boyfailure.vectorbreakout.VectorBreakout
diff --git a/src/main/java/dev/boyfailure/quajra/vectorbreakout/Ball.java b/src/main/java/dev/boyfailure/quajra/vectorbreakout/Ball.java
deleted file mode 100644
index 0501215..0000000
--- a/src/main/java/dev/boyfailure/quajra/vectorbreakout/Ball.java
+++ /dev/null
@@ -1,235 +0,0 @@
-package dev.boyfailure.quajra.vectorbreakout;
-
-import java.util.Arrays;
-
-public class Ball {
-
- short ballX = 250;
- short ballY = 300;
- short ballWidth = 7;
- short ballHeight = 7;
- private boolean isActive = false;
- private boolean isInvincible = false;
- byte ballColor = 0;
- Hitbox hitBox;
-
- byte speedX = 5;
- byte speedY = 5;
-
- 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;
- this.ballHeight = (short) height;
- this.ballColor = (byte) color;
- this.hitBox = new Hitbox(x, y, width, height, 1);
- this.isActive = active;
- }
-
- public void spawnBall(boolean useChancey) {
- if (useChancey) {
- this.ballX += (short) (gameState.getChancey() * 3);
- this.ballY += (short) gameState.getChancey();
- }
- gameState.ballsOnScreen++;
- this.isActive = true;
- gameState.lives--;
- gameState.textElementList.get(4).setText(String.valueOf(gameState.lives));
- }
-
- public void spawnBall(int x, int y) {
- this.ballX = (short) x;
- this.ballY = (short) y;
- gameState.ballsOnScreen++;
- this.isActive = true;
- gameState.lives--;
- gameState.textElementList.get(4).setText(String.valueOf(gameState.lives));
- }
-
- public void moveBall() {
- this.ballX += this.speedX;
- this.ballY += this.speedY;
-
- this.hitBox.moveTo(this.ballX, this.ballY, this.ballWidth, this.ballHeight);
-
- this.checkCollision();
- }
-
- public void bounce(int bounceType) {
- switch((byte) bounceType) {
- case 0:
- this.speedY = (byte) -(this.speedY);
- switch (gameState.getChancey()) {
- case 0: break;
- case 1: this.speedX++; break;
- case 2: break;
- case 3: this.speedX--; break;
- case 4: break;
- case 5: this.speedY++;break;
- case 6: break;
- case 7: this.speedY--;break;
- case 8: break;
- case 9: this.speedY++;speedX++;break;
- case 10: break;
- case 11: this.speedY--;speedX--;break;
- case 12: break;
- case 13: this.speedY++;speedY++;break;
- case 14: break;
- case 15: this.speedY--;speedY--;break;
- }
- break;
- case 1:
- this.speedX = (byte) -(this.speedX);
- break;
- case 2:
- if (this.speedX > 0) {
- this.speedX = (byte) -(this.speedX);
- }
- else {
- this.speedX -= 4;
- }
- this.speedY = (byte) -(this.speedY);
- break;
- case 3:
- if (this.speedX < 0) {
- this.speedX = (byte) -(this.speedX);
- }
- else {
- this.speedX += 4;
- }
- this.speedY = (byte) -(this.speedY);
- break;
- }
- if (this.speedX < 2 && this.speedX >= 0) {this.speedX = 2;}
- else if (this.speedX > -2 && this.speedX < 0) {this.speedX = -2;}
- else if (this.speedX > 11) {this.speedX -= 3;} // Slow down if too fast
- if (this.speedY < 4 && this.speedY >= 0) {this.speedY = 4;}
- else if (this.speedY > -4 && this.speedY < 0) {this.speedY = -4;}
- else if (this.speedY > 11) {this.speedY -= 3;} // Slow down if too fast
- }
-
- public void destroyBall() {
- this.isActive = false;
- this.ballX = 32767;
- this.ballY = 32767;
- gameState.ballsOnScreen--;
- if (gameState.ballsOnScreen <= 0) {
- gameState.ballsOnScreen = 0;
- if (gameState.lives <= 0) {
- gameState.gameLose();
- }
- }
- }
-
- // Checks to see if the ball is in contact with bricks, paddles, or walls
- public void checkCollision() {
-
- this.setInvincibilityState(true); // Delay bouncing again for a frame
-
- // Check floor
- if (this.ballY >= gameState.getInternalResY()) {
- this.destroyBall();
- return;
- }
- // Check ceiling
- else if (this.ballY <= 0) {
- this.bounce(0);
- this.ballY = 0;
- this.wallBounceAnimation();
- return;
- }
-
- // Check walls
- if (this.ballX <= 20) {
- this.bounce(1);
- this.ballX = 22;
- this.wallBounceAnimation();
- return;
- }
- else if ((this.ballX + this.ballWidth) >= (gameState.getInternalResX() - 20 - this.ballWidth)) {
- this.bounce((byte) 1);
- this.ballX = (short) (gameState.getInternalResX() - 25 - this.ballWidth);
- this.wallBounceAnimation();
- return;
- }
-
- // Check bricks
- if (gameState.currentLevel != null) {
- if (gameState.currentLevel.brickList != null) {
- for (Brick brick : gameState.currentLevel.brickList) {
- if (this.hitBox.collides(brick.hitBox.getBounds())) {
- brick.breakBrick();
- this.bounce(0);
- gameState.brickCollector.add(brick);
- return;
- }
- }
- }
- }
-
- // Check paddles
- for (Paddle paddle : gameState.paddleList) {
- if (this.hitBox.collides(paddle.hitBox.getBounds())) {
- if (this.hitBox.collides(paddle.hitBoxLeft.getBounds())) {
- this.bounce(2);
- }
- else if (this.hitBox.collides(paddle.hitBoxRight.getBounds())) {
- this.bounce(3);
- }
- else {
- this.bounce(0);
- }
-
- this.ballY -= 2;
-
- // Draw particles
- Particle particle1, particle2, particle3;
- particle1 = new Particle(gameState, 0, this.ballX, this.ballY - 10, 0, -2, 1, 2, 7, 3);
- particle2 = new Particle(gameState, 0, this.ballX - 3, this.ballY - 10, -2, -4, 1, 2, 6, 3);
- particle3 = new Particle(gameState, 0, this.ballX + 10, this.ballY - 10, 2, -4, 1, 2, 8, 3);
- gameState.particleList.add(particle1);
- gameState.particleList.add(particle2);
- gameState.particleList.add(particle3);
- particle1.spawn(5);
- particle2.spawn(5);
- particle3.spawn(5);
-
- return; // Prevents bouncing multiple times
- }
- }
- }
-
- private void wallBounceAnimation() {
- Particle particle1, particle2, particle3, particle4;
- particle1 = new Particle(gameState, 0, this.ballX - 3, this.ballY - 10, -2, -2, 1, 2, 6, 3);
- particle2 = new Particle(gameState, 0, this.ballX + 10, this.ballY - 10, 2, -2, 1, 2, 8, 3);
- particle3 = new Particle(gameState, 0, this.ballX - 3, this.ballY + 3, 2, 2, 1, 2, 2, 3);
- particle4 = new Particle(gameState, 0, this.ballX + 10, this.ballY + 3, -2, 2, 1, 2, 4, 3);
- gameState.particleList.add(particle1);
- gameState.particleList.add(particle2);
- gameState.particleList.add(particle3);
- gameState.particleList.add(particle4);
- particle1.spawn(5);
- particle2.spawn(5);
- particle3.spawn(5);
- particle4.spawn(5);
- }
-
- public boolean getActiveState() {
- return this.isActive;
- }
- public void setActiveState(boolean activeState) {
- this.isActive = activeState;
- }
-
- public boolean getInvincibilityState() {
- return this.isInvincible;
- }
- public void setInvincibilityState(boolean invincibilityState) {
- this.isInvincible = invincibilityState;
- }
-
-}
diff --git a/src/main/java/dev/boyfailure/quajra/vectorbreakout/Brick.java b/src/main/java/dev/boyfailure/quajra/vectorbreakout/Brick.java
deleted file mode 100644
index 92d0d2e..0000000
--- a/src/main/java/dev/boyfailure/quajra/vectorbreakout/Brick.java
+++ /dev/null
@@ -1,81 +0,0 @@
-package dev.boyfailure.quajra.vectorbreakout;
-
-public class Brick {
-
- short brickX;
- short brickY;
- short brickWidth = 40;
- short brickHeight = 17;
- private boolean isBroken = false;
- byte brickColor = 0;
- byte scoreOnBreak = 10;
- byte specialAbility = 0;
- Hitbox hitBox;
-
- private GameState gameState;
-
- /**
- * Bricks.
- * @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(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;
- this.brickColor = (byte) brickColor;
- this.scoreOnBreak = (byte) score;
- this.hitBox = new Hitbox(x, y, this.brickWidth, this.brickHeight, 0);
- this.specialAbility = (byte) specialAbility;
- }
-
- public void breakBrick() {
- if (!isBroken) {
- isBroken = true;
- gameState.score += this.scoreOnBreak + (gameState.level - 1);
- gameState.textElementList.get(3).setText(String.valueOf(gameState.score));
- gameState.bricksOnScreen--;
- switch(this.specialAbility) {
- case 1:
- gameState.lives++;
- break;
- case 2:
- Ball ball = new Ball(gameState, 300, 300, 7, 7, 0, true);
- ball.spawnBall((this.brickX + (this.brickWidth / 2)), (this.brickY + (this.brickHeight / 2)));
- break;
- default:
- break;
- }
- }
-
- // Draw particles
- Particle particle1, particle2, particle3;
- particle1 = new Particle(gameState, 0, this.brickX + 12, this.brickY + 3, 1, 1, this.brickColor, 2, 4, 1);
- particle2 = new Particle(gameState, 0, this.brickX + 20, this.brickY + 13, 1, 1, this.brickColor, 2, 3, 1);
- particle3 = new Particle(gameState, 0, this.brickX + 32, this.brickY + 7, 1, 1, this.brickColor, 2, 2, 1);
- gameState.particleList.add(particle1);
- gameState.particleList.add(particle2);
- gameState.particleList.add(particle3);
- particle1.spawn(5);
- particle2.spawn(5);
- particle3.spawn(5);
-
- this.brickX = 32767;
- this.brickY = 32767;
- this.hitBox.moveTo(32760, 32760, 2, 2);
- }
-
- public boolean getBrokenState() {
- return this.isBroken;
- }
- public void setBrokenState(boolean brokenState) {
- this.isBroken = brokenState;
- }
-
-}
diff --git a/src/main/java/dev/boyfailure/quajra/vectorbreakout/Paddle.java b/src/main/java/dev/boyfailure/quajra/vectorbreakout/Paddle.java
deleted file mode 100644
index 0e7f76f..0000000
--- a/src/main/java/dev/boyfailure/quajra/vectorbreakout/Paddle.java
+++ /dev/null
@@ -1,56 +0,0 @@
-package dev.boyfailure.quajra.vectorbreakout;
-
-public class Paddle {
-
- short paddleX;
- short paddleY;
- short paddleWidth = 100;
- short paddleHeight = 7;
- byte paddleColor;
- byte paddleSpeed = 0;
- boolean isActive = true;
- float speedMultiplier = 1;
- Hitbox hitBox;
- Hitbox hitBoxLeft;
- Hitbox hitBoxRight;
-
- 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;
- this.paddleHeight = (short) height;
- this.paddleColor = (byte) paddleColor;
- this.hitBox = new Hitbox(x, y, width, height, 4);
- this.hitBoxLeft = new Hitbox(x, y, width / 5, height, 4);
- this.hitBoxRight = new Hitbox(x + ((width / 5) * 4), y, width / 5, height, 4);
- }
-
- public void movePaddle(boolean direction) {
- if (this.paddleSpeed <= 15) {this.paddleSpeed += 3;}
- 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 >= (gameState.getInternalResX() - this.paddleWidth - 20)) {this.paddleX = (short) (gameState.getInternalResX() - 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);
- this.hitBoxRight.moveTo(this.paddleX + ((this.paddleWidth / 5) * 4), this.paddleY, this.paddleWidth / 5, this.paddleHeight);
- }
-
- public void cullPaddle() {
- isActive = false;
- this.paddleX = 32767;
- this.paddleY = 32767;
- }
-
- public boolean getActiveState() {
- return this.isActive;
- }
- public void setActiveState(boolean activeState) {
- this.isActive = activeState;
- }
-
-}
diff --git a/src/main/java/dev/boyfailure/quajra/vectorbreakout/TextElement.java b/src/main/java/dev/boyfailure/quajra/vectorbreakout/TextElement.java
deleted file mode 100644
index 7ae74ec..0000000
--- a/src/main/java/dev/boyfailure/quajra/vectorbreakout/TextElement.java
+++ /dev/null
@@ -1,126 +0,0 @@
-package dev.boyfailure.quajra.vectorbreakout;
-
-import java.util.Timer;
-import java.util.TimerTask;
-
-public class TextElement {
-
- String text;
- boolean active = true;
- private boolean isVisible = false;
- float textScale = 1;
- float vectorScale = 1;
- 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.
- * @param vectorScale The scale of the text.
- */
- 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;
- this.y = (short) y;
- this.textColor = (byte) textColor;
- 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 vectorScale The scale 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();
- }
-
- public boolean getActiveState() {
- return this.active;
- }
- /**
- * Prepares the TextElement for rendering.
- */
- public void activateTimer() {
- this.active = true;
- long testLong = this.creationTime;
- if (this.duration > 0) {
- Timer levelTitleGTFO = new Timer();
- levelTitleGTFO.schedule(new TimerTask() {
- public void run() {
- TextElement.this.setVisibility(false);
- }
- }, this.duration);
- }
- this.setVisibility(true);
- }
-
- public boolean getVisibility() {
- return this.isVisible;
- }
- public void setVisibility(boolean visibility) {
- this.isVisible = visibility;
- }
-
- /**
- * Prepares the TextElement for culling.
- */
- public void cull() {
- this.active = false;
- this.x = 32767;
- this.y = 32767;
- }
-
- /**
- * Changes the text that the TextElement will display.
- * @param text The text that the TextElement will display.
- */
- public void setText(String text) {
- this.text = text;
- }
-
- /**
- * Changes the text that the TextElement will display.
- * @param x The x coordinate of the text's origin point.
- * @param y The y coordinate of the text's origin point.
- */
- public void moveTo(int x, int y) {
- this.x = (short) x;
- this.y = (short) y;
- }
-
- /**
- * Changes 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
deleted file mode 100644
index 3790396..0000000
--- a/src/main/java/dev/boyfailure/quajra/vectorbreakout/VectorBreakout.java
+++ /dev/null
@@ -1,64 +0,0 @@
-package dev.boyfailure.quajra.vectorbreakout;
-
-import java.awt.Color;
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
-import java.awt.event.ComponentAdapter;
-import java.awt.event.ComponentEvent;
-import javax.swing.JFrame;
-import javax.swing.Timer;
-
-public class VectorBreakout {
-
- // 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");
-
- // Set the game window's properties
- gameFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
- gameFrame.setTitle(gameState.gameName);
- gameFrame.setSize(gameState.getInternalResX(), gameState.getInternalResY());
- gameFrame.getContentPane().setBackground(Color.black);
- gameFrame.getContentPane().add(gameCanvas);
- gameFrame.setVisible(true);
-
- gameFrame.addKeyListener(gameState.getKeyListener());
-
- // Adapts vector drawing scale when the window is resized
- gameFrame.addComponentListener(new ComponentAdapter() {
- public void componentResized(ComponentEvent componentEvent) {
- gameState.setWindowResX((short) gameFrame.getBounds().width);
- gameState.setWindowResY((short) gameFrame.getBounds().height);
- gameCanvas.setBeamScaleX(gameState.getWindowResX() / 800f);
- gameCanvas.setBeamScaleY(gameState.getWindowResY() / 600f);
- }
- });
-
- Timer gameTick = new Timer((1000 / gameState.getGameTickRate()), gameState.getGameTickActionListener());
- gameTick.start();
-
- Timer frameDisplay = new Timer((1000 / gameState.getTargetFrameRate()), new ActionListener() {
- public void actionPerformed(ActionEvent e) {
- gameState.incrementFrameCounter();
- gameFrame.repaint();
- }
- });
- frameDisplay.start();
-
- Timer gameCuller = new Timer(1000, gameState.getGameStateUpdateActionListener());
- gameCuller.start();
-
- }
-
-}
diff --git a/src/main/java/dev/boyfailure/vectorbreakout/Ball.java b/src/main/java/dev/boyfailure/vectorbreakout/Ball.java
new file mode 100644
index 0000000..287b4de
--- /dev/null
+++ b/src/main/java/dev/boyfailure/vectorbreakout/Ball.java
@@ -0,0 +1,283 @@
+/*
+ * Copyright 2025 Naomi (boyfailure.dev).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dev.boyfailure.vectorbreakout;
+
+/**
+ * The balls that move around the playfield and hit bricks.
+ * @author Naomi (boyfailure.dev)
+ * @since 20250323
+ */
+public class Ball extends Entity {
+
+ private boolean isActive = false;
+ private boolean isInvincible = false;
+ byte ballColor = 0;
+
+ byte speedX = 5;
+ byte speedY = 5;
+
+ private GameState gameState;
+
+ public Ball(GameState gameState, int x, int y, int width, int height, int color, boolean active) {
+ this.gameState = gameState;
+ this.posX = (short) x;
+ this.posY = (short) y;
+ this.width = (short) width;
+ this.height = (short) height;
+ this.ballColor = (byte) color;
+ this.hitBox = new Hitbox(x, y, width, height, 1);
+ this.isActive = active;
+ }
+
+ public void spawnBall(boolean useLFSR) {
+ if (useLFSR) {this.spawnBall(((gameState.getPokey().getDecimalValue() / 2) + gameState.getPokey().getShiftedDecimalValue() + 30), (this.posY + (gameState.getPokey().getDecimalValue() / 4)) - 40);}
+ else {this.spawnBall(this.posX, this.posY);}
+ }
+
+ public void spawnBall(int x, int y) {
+ this.posX = (short) x;
+ this.posY = (short) y;
+ this.isActive = true;
+ if (gameState.getBalls().size() <= 1) {
+ gameState.removeLife();
+ gameState.getTextElements().get(4).setText(String.valueOf(gameState.getLives()));
+ }
+ }
+
+ public void moveBall() {
+ this.posX += this.speedX;
+ this.posY += this.speedY;
+
+ this.hitBox.moveTo(this.posX, this.posY, this.width, this.height);
+
+ this.checkCollision();
+ }
+
+ /**
+ * Richochets the ball.
+ * @param bounceType The code for which direction the ball bounces
+ */
+ public void bounce(int bounceType) {
+ switch((byte) bounceType) {
+ case 0:
+ this.speedY = (byte) -(this.speedY);
+ byte randByte = (byte) (gameState.getPokey().getDecimalValue() / 16);
+ if (randByte < 0) {randByte = 0;}
+ else if (randByte > 15) {randByte = 15;}
+ switch (randByte) {
+ case 0: break;
+ case 1: this.speedX++; break;
+ case 2: break;
+ case 3: this.speedX--; break;
+ case 4: break;
+ case 5: this.speedY++;break;
+ case 6: break;
+ case 7: this.speedY--;break;
+ case 8: break;
+ case 9: this.speedY++;speedX++;break;
+ case 10: break;
+ case 11: this.speedY--;speedX--;break;
+ case 12: break;
+ case 13: this.speedY++;speedY++;break;
+ case 14: break;
+ case 15: this.speedY--;speedY--;break;
+ }
+ break;
+ case 1:
+ this.speedX = (byte) -(this.speedX);
+ break;
+ case 2:
+ if (this.speedX > 0) {
+ this.speedX = (byte) -(this.speedX);
+ }
+ else {
+ this.speedX -= 4;
+ }
+ this.speedY = (byte) -(this.speedY);
+ break;
+ case 3:
+ if (this.speedX < 0) {
+ this.speedX = (byte) -(this.speedX);
+ }
+ else {
+ this.speedX += 4;
+ }
+ this.speedY = (byte) -(this.speedY);
+ break;
+ }
+ if (this.speedX < 2 && this.speedX >= 0) {this.speedX = 2;}
+ else if (this.speedX > -2 && this.speedX < 0) {this.speedX = -2;}
+ else if (this.speedX > 11) {this.speedX -= 3;} // Slow down if too fast
+ if (this.speedY < 4 && this.speedY >= 0) {this.speedY = 4;}
+ else if (this.speedY > -4 && this.speedY < 0) {this.speedY = -4;}
+ else if (this.speedY > 11) {this.speedY -= 3;} // Slow down if too fast
+ }
+
+ public void destroyBall() {
+ this.isActive = false;
+ this.posX = 32767;
+ this.posY = 32767;
+ this.hitBox.setBounds(32766, 32766, 32767, 32767);
+ if (gameState.getBalls().size() <= 1) {
+ if (gameState.getLives() <= 0) {
+ gameState.gameLose();
+ }
+ }
+ }
+
+ public void cull() {
+ this.isActive = false;
+ this.posX = 32767;
+ this.posY = 32767;
+ this.hitBox.setBounds(32766, 32766, 32767, 32767);
+ }
+
+ /**
+ * Checks to see if the ball is in contact with other Entities and walls.
+ */
+ public synchronized void checkCollision() {
+
+ this.setInvincibilityState(true); // Delay bouncing again for a frame
+
+ // Check floor
+ if (this.posY >= gameState.getInternalResY()) {
+ this.destroyBall();
+ return;
+ }
+ // Check ceiling
+ else if (this.posY <= 0) {
+ this.bounce(0);
+ this.posY = 0;
+ this.wallBounceAnimation();
+ return;
+ }
+
+ // Check walls
+ if (this.posX <= 20) {
+ this.bounce(1);
+ this.posX = 22;
+ this.wallBounceAnimation();
+ return;
+ }
+ else if ((this.posX + this.width) >= (gameState.getInternalResX() - 20 - this.width)) {
+ this.bounce(1);
+ this.posX = (short) (gameState.getInternalResX() - 25 - this.width);
+ this.wallBounceAnimation();
+ return;
+ }
+
+ // Check bricks
+ if (gameState.getCurrentLevel() != null) {
+ if (gameState.getCurrentLevel().brickList != null) {
+ for (Brick brick : gameState.getCurrentLevel().brickList) {
+ // Check for left bounce
+ if (this.hitBox.collides(brick.hitBoxLeft.getBounds())) {
+ if (this.hitBox.collides(brick.hitBox.getBounds())) {
+ this.bounce(0);
+ this.bounce(1);
+ }
+ else {
+ this.bounce(1);
+ }
+ brick.breakBrick();
+ return;
+ }
+ // Check for right bounce
+ else if (this.hitBox.collides(brick.hitBoxRight.getBounds())) {
+ if (this.hitBox.collides(brick.hitBox.getBounds())) {
+ this.bounce(0);
+ this.bounce(1);
+ }
+ else {
+ this.bounce(1);
+ }
+ brick.breakBrick();
+ return;
+ }
+ // Check for vertical bounce
+ else if (this.hitBox.collides(brick.hitBox.getBounds())) {
+ brick.breakBrick();
+ this.bounce(0);
+ return;
+ }
+ }
+ }
+ }
+
+ // Check paddles
+ for (Paddle paddle : gameState.getPaddles()) {
+ if (this.hitBox.collides(paddle.hitBox.getBounds())) {
+ if (this.hitBox.collides(paddle.hitBoxLeft.getBounds())) {
+ this.bounce(2);
+ }
+ else if (this.hitBox.collides(paddle.hitBoxRight.getBounds())) {
+ this.bounce(3);
+ }
+ else {
+ this.bounce(0);
+ }
+
+ this.posY -= 2;
+
+ // Draw particles
+ Particle particle1, particle2, particle3;
+ particle1 = new Particle(gameState, 0, this.posX, this.posY - 10, 0, -2, 1, 2, 7, 3);
+ particle2 = new Particle(gameState, 0, this.posX - 3, this.posY - 10, -2, -4, 1, 2, 6, 3);
+ particle3 = new Particle(gameState, 0, this.posX + 10, this.posY - 10, 2, -4, 1, 2, 8, 3);
+ gameState.getParticles().add(particle1);
+ gameState.getParticles().add(particle2);
+ gameState.getParticles().add(particle3);
+ particle1.spawn(5);
+ particle2.spawn(5);
+ particle3.spawn(5);
+
+ return; // Prevents bouncing multiple times
+ }
+ }
+ }
+
+ private void wallBounceAnimation() {
+ Particle particle1, particle2, particle3, particle4;
+ particle1 = new Particle(gameState, 0, this.posX - 3, this.posY - 10, -2, -2, 1, 2, 6, 3);
+ particle2 = new Particle(gameState, 0, this.posX + 10, this.posY - 10, 2, -2, 1, 2, 8, 3);
+ particle3 = new Particle(gameState, 0, this.posX - 3, this.posY + 3, 2, 2, 1, 2, 2, 3);
+ particle4 = new Particle(gameState, 0, this.posX + 10, this.posY + 3, -2, 2, 1, 2, 4, 3);
+ gameState.getParticles().add(particle1);
+ gameState.getParticles().add(particle2);
+ gameState.getParticles().add(particle3);
+ gameState.getParticles().add(particle4);
+ particle1.spawn(5);
+ particle2.spawn(5);
+ particle3.spawn(5);
+ particle4.spawn(5);
+ }
+
+ public boolean getActiveState() {
+ return this.isActive;
+ }
+ public void setActiveState(boolean activeState) {
+ this.isActive = activeState;
+ }
+
+ public boolean getInvincibilityState() {
+ return this.isInvincible;
+ }
+ public void setInvincibilityState(boolean invincibilityState) {
+ this.isInvincible = invincibilityState;
+ }
+
+}
diff --git a/src/main/java/dev/boyfailure/vectorbreakout/Brick.java b/src/main/java/dev/boyfailure/vectorbreakout/Brick.java
new file mode 100644
index 0000000..0c9fbe0
--- /dev/null
+++ b/src/main/java/dev/boyfailure/vectorbreakout/Brick.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2025 Naomi (boyfailure.dev).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dev.boyfailure.vectorbreakout;
+
+/**
+ * The bricks that appear in each level.
+ * @author Naomi (boyfailure.dev)
+ * @since 20250323
+ */
+public class Brick extends Entity {
+
+ private boolean isBroken = false;
+ byte brickColor = 0;
+ byte scoreOnBreak = 10;
+ byte specialAbility = 0;
+ Hitbox hitBoxLeft;
+ Hitbox hitBoxRight;
+
+ private GameState gameState;
+
+ /**
+ * Constructs a 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(GameState gameState, int x, int y, boolean isBroken, int brickColor, int score, int specialAbility) {
+ this.width = 40;
+ this.height = 17;
+ this.gameState = gameState;
+ this.posX = (short) x;
+ this.posY = (short) y;
+ this.isBroken = isBroken;
+ this.brickColor = (byte) brickColor;
+ this.scoreOnBreak = (byte) score;
+ this.hitBox = new Hitbox(x + 2, y, this.width - 4, this.height, 0);
+ this.hitBoxLeft = new Hitbox(x, y + 2, 6, this.height - 4, 0);
+ this.hitBoxRight = new Hitbox(x + this.width - 6, y + 2, 6, this.height - 4, 0);
+ this.specialAbility = (byte) specialAbility;
+ }
+
+ public void breakBrick() {
+ if (!isBroken) {
+ isBroken = true;
+ gameState.addScore(this.scoreOnBreak + (gameState.getCurrentLevelID() - 1));
+ gameState.getTextElements().get(3).setText(String.valueOf(gameState.getScore()));
+ switch(this.specialAbility) {
+ case 1:
+ gameState.addLife();
+ break;
+ case 2:
+ while (gameState.getCollectorActiveState()) {}
+ Ball newBall = new Ball(gameState, 300, 300, 7, 7, 0, true);
+ this.gameState.getBalls().add(newBall);
+ newBall.spawnBall((this.posX + (this.width / 2)), (this.posY + (this.height / 2)));
+ break;
+ default:
+ break;
+ }
+
+ // Show score on break
+ TextElement bruh = new TextElement(this.gameState,
+ String.valueOf(this.scoreOnBreak),
+ 0.25f,
+ this.posX + (this.width / 3),
+ this.posY - 3,
+ this.brickColor,
+ 1,
+ 300);
+ bruh.activateTimer();
+ bruh.setActiveState(true);
+ //bruh.moveUpOnFrame();
+ this.gameState.getTextElements().add(bruh);
+ }
+
+ // Draw particles
+ Particle particle1, particle2, particle3;
+ particle1 = new Particle(gameState, 0, this.posX + 12, this.posY + 3, 1, 1, this.brickColor, 2, 4, 1);
+ particle2 = new Particle(gameState, 0, this.posX + 20, this.posY + 13, 1, 1, this.brickColor, 2, 3, 1);
+ particle3 = new Particle(gameState, 0, this.posX + 32, this.posY + 7, 1, 1, this.brickColor, 2, 2, 1);
+ gameState.getParticles().add(particle1);
+ gameState.getParticles().add(particle2);
+ gameState.getParticles().add(particle3);
+ particle1.spawn(5);
+ particle2.spawn(5);
+ particle3.spawn(5);
+
+ this.posX = 32767;
+ this.posY = 32767;
+ this.hitBox.moveTo(32760, 32760, 2, 2);
+ this.hitBoxLeft.moveTo(32760, 32760, 2, 2);
+ this.hitBoxRight.moveTo(32760, 32760, 2, 2);
+ }
+
+ public boolean getBrokenState() {
+ return this.isBroken;
+ }
+ public void setBrokenState(boolean brokenState) {
+ this.isBroken = brokenState;
+ }
+
+}
diff --git a/src/main/java/dev/boyfailure/vectorbreakout/Entity.java b/src/main/java/dev/boyfailure/vectorbreakout/Entity.java
new file mode 100644
index 0000000..aa2a013
--- /dev/null
+++ b/src/main/java/dev/boyfailure/vectorbreakout/Entity.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2025 naomi.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dev.boyfailure.vectorbreakout;
+
+import java.awt.Point;
+
+/**
+ * Base class for all entities in the game.
+ * @author Naomi (boyfailure.dev)
+ * @since 1.0
+ */
+public class Entity {
+
+ /** The horizontal position of the entity, in pixels. */
+ short posX;
+ /** The vertical position of the entity, in pixels. */
+ short posY;
+ /** The width of the entity, in pixels. */
+ short width;
+ /** The height of the entity, in pixels. */
+ short height;
+ /** The default hitbox of the entity. */
+ Hitbox hitBox;
+
+ //
+
+ /**
+ * Returns the position of the entity as a Point.
+ * @return the position of the entity
+ */
+ public Point getPosition() {
+ return new Point(this.posX, this.posY);
+ }
+ /**
+ * Returns the horizontal position of the entity.
+ * @return the horizontal position of the entity
+ */
+ public short getPositionX() {
+ return this.posX;
+ }
+ /**
+ * Returns the vertical position of the entity.
+ * @return the vertical position of the entity
+ */
+ public short getPositionY() {
+ return this.posY;
+ }
+
+ //
+
+}
diff --git a/src/main/java/dev/boyfailure/quajra/vectorbreakout/GameDisplay.java b/src/main/java/dev/boyfailure/vectorbreakout/GameDisplay.java
similarity index 79%
rename from src/main/java/dev/boyfailure/quajra/vectorbreakout/GameDisplay.java
rename to src/main/java/dev/boyfailure/vectorbreakout/GameDisplay.java
index 88287a7..d622de1 100644
--- a/src/main/java/dev/boyfailure/quajra/vectorbreakout/GameDisplay.java
+++ b/src/main/java/dev/boyfailure/vectorbreakout/GameDisplay.java
@@ -1,4 +1,20 @@
-package dev.boyfailure.quajra.vectorbreakout;
+/*
+ * Copyright 2025 Naomi (boyfailure.dev).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dev.boyfailure.vectorbreakout;
import java.awt.BasicStroke;
import java.awt.Color;
@@ -7,6 +23,11 @@ import java.awt.Graphics2D;
import javax.swing.JComponent;
import java.awt.geom.Line2D;
+/**
+ * The logic to draw the game on the screen.
+ * @author Naomi (boyfailure.dev)
+ * @since 20250323
+ */
public class GameDisplay extends JComponent {
// Change this to 1 if you build for older versions of Java. Older versions
@@ -45,7 +66,7 @@ public class GameDisplay extends JComponent {
this.beamThicknessScale = beamScale;
}
- public void paint(Graphics gameGraphics) {
+ public synchronized void paint(Graphics gameGraphics) {
super.paint(gameGraphics);
@@ -65,18 +86,18 @@ public class GameDisplay extends JComponent {
}
// Bricks
- if (gameState.currentLevel != null) {
- if (gameState.currentLevel.brickList != null) {
- for (Brick brick : gameState.currentLevel.brickList) {
+ if (gameState.getCurrentLevel() != null) {
+ if (gameState.getCurrentLevel().brickList != null) {
+ for (Brick brick : gameState.getCurrentLevel().brickList) {
if (!brick.getBrokenState()) {
resetBeam();
g2.setStroke(bt(3));
- moveBeam(brick.brickX + 1, brick.brickY + 1);
+ moveBeam(brick.posX + 1, brick.posY + 1);
g2.setColor(vc(brick.brickColor));
- g2.draw(drawVec(brick.brickWidth - 2, 0));
- g2.draw(drawVec(0, -(brick.brickHeight - 2)));
- g2.draw(drawVec(-(brick.brickWidth - 2), 0));
- g2.draw(drawVec(0, brick.brickHeight - 2));
+ g2.draw(drawVec(brick.width - 2, 0));
+ g2.draw(drawVec(0, -(brick.height - 2)));
+ g2.draw(drawVec(-(brick.width - 2), 0));
+ g2.draw(drawVec(0, brick.height - 2));
}
}
}
@@ -84,34 +105,34 @@ public class GameDisplay extends JComponent {
// Paddles
g2.setColor(Color.white);
- for (Paddle paddle : gameState.paddleList) {
+ for (Paddle paddle : gameState.getPaddles()) {
if (paddle.getActiveState()) {
resetBeam();
g2.setStroke(bt(3));
- moveBeam(paddle.paddleX, paddle.paddleY);
- g2.draw(drawVec(paddle.paddleWidth, 0));
- g2.draw(drawVec(0, -(paddle.paddleHeight)));
- g2.draw(drawVec(-(paddle.paddleWidth), 0));
- g2.draw(drawVec(0, paddle.paddleHeight));
+ moveBeam(paddle.posX, paddle.posY);
+ g2.draw(drawVec(paddle.width, 0));
+ g2.draw(drawVec(0, -(paddle.height)));
+ g2.draw(drawVec(-(paddle.width), 0));
+ g2.draw(drawVec(0, paddle.height));
}
}
// Balls
g2.setStroke(bt(3));
g2.setColor(Color.yellow);
- for (Ball ball : gameState.ballList) {
+ for (Ball ball : gameState.getBalls()) {
if (ball.getActiveState()) {
resetBeam();
- moveBeam(ball.ballX, ball.ballY);
- g2.draw(drawVec(ball.ballWidth, 0));
- g2.draw(drawVec(0, -ball.ballHeight));
- g2.draw(drawVec(-ball.ballWidth, 0));
- g2.draw(drawVec(0, ball.ballHeight));
+ moveBeam(ball.posX, ball.posY);
+ g2.draw(drawVec(ball.width, 0));
+ g2.draw(drawVec(0, -ball.height));
+ g2.draw(drawVec(-ball.width, 0));
+ g2.draw(drawVec(0, ball.height));
}
}
// Particles
- for (Particle particle : gameState.particleList) {
+ for (Particle particle : gameState.getParticles()) {
if (particle.getActiveState()) {
resetBeam();
moveBeam(particle.particleX, particle.particleY);
@@ -121,10 +142,11 @@ public class GameDisplay extends JComponent {
}
}
- /* TextElements
+ /*
+ * TextElements
*
* Note on rendering numbers:
- *
+ *
* I am aware that I could be using a switch statement for these.
* However, this did not compile when testing in JDK 1.5, as it would
* refuse to accept anything other than numbers in a switch statement.
@@ -132,24 +154,25 @@ 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 (TextElement element : gameState.textElementList) {
- if (element.getActiveState() && element.getVisibility()) {
+ for (TextElement element : gameState.getTextElements()) {
+ if (element.getActiveState() && element.getVisibleState()) {
+ if (element.movesUpOnFrame()) {element.moveTo(element.getPositionX(), element.getPositionY() - 1);}
resetBeam();
- moveBeam(element.x, element.y);
+ moveBeam(element.getPositionX(), element.getPositionY());
// Save the current beam scale to revert once TextElement is done rendering
- float oldBeamScaleX = beamScaleX;
- float oldBeamScaleY = beamScaleY;
+ float oldBeamScaleX = this.beamScaleX;
+ float oldBeamScaleY = this.beamScaleY;
// Adjust the beam scale to accomodate the scale of the TextElement
- beamScaleX = beamScaleX * element.textScale;
- beamScaleY = beamScaleY * element.textScale;
+ this.beamScaleX *= element.getTextScale();
+ this.beamScaleY *= element.getTextScale();
// Render the TextElement
- g2.setColor(vc(element.textColor));
- g2.setStroke(bt(element.vectorScale));
- String stringToSplit = String.valueOf(element.text);
- String[] stringToRender = stringToSplit.split("");
+ g2.setColor(vc(element.getColor()));
+ g2.setStroke(bt(element.getVectorThickness()));
+ String[] stringToRender = element.getText().split("");
+
//
for (short i = iStart; i < stringToRender.length; i++) {
if ("1".equals(stringToRender[i])) {
@@ -233,7 +256,7 @@ public class GameDisplay extends JComponent {
}
// Simulates a newline in a TextElement
else if ("\\".equals(stringToRender[i])) {
- moveBeam(-(beamX - element.x), 30);
+ moveBeam(-(beamX - element.getPositionX()), 30);
}
else if ("A".equals(stringToRender[i])) {
g2.draw(drawVec(0, -17));
@@ -477,67 +500,81 @@ public class GameDisplay extends JComponent {
//
// Revert back to the previous beam scale
- beamScaleX = oldBeamScaleX;
- beamScaleY = oldBeamScaleY;
+ this.beamScaleX = oldBeamScaleX;
+ this.beamScaleY = oldBeamScaleY;
}
}
- gameState.actualFrames++; // Count up actual frames rendered
+ gameState.incrementActualFrames(); // Count up actual frames rendered
// Debug Menu
if (gameState.getDebugMenuState()) {
g2.setColor(Color.white);
- g2.drawString(gameState.gameName + " v" + gameState.gameVersion, 1, 12);
- g2.drawString("frame: " + gameState.getFrameCounter(), 1, 24);
- g2.drawString("beamScaleX: " + beamScaleX, 1, 36);
- g2.drawString("beamScaleY: " + beamScaleY, 1, 48);
- g2.drawString("isPaused: " + gameState.getPausedState(), 1, 60);
- g2.drawString("targetFrameRate: " + gameState.getTargetFrameRate(), 150, 24);
- g2.drawString("actualFrameRate: " + gameState.getActualFrameRate(), 150, 36);
- g2.drawString("actualFrames: " + gameState.actualFrames, 150, 48);
- g2.drawString("chancey: " + gameState.getChancey(), 150, 60);
- g2.drawString("ballsOnScreen: " + gameState.ballsOnScreen, 300, 24);
+ g2.drawString(gameState.getGameName() + " v" + gameState.getGameVersion(), 1, 12);
+ g2.drawString("Frame: " + gameState.getFrameCounter(), 1, 24);
+ g2.drawString("Horizontal beamscale: " + beamScaleX, 1, 36);
+ g2.drawString("Vertical beamscale: " + beamScaleY, 1, 48);
+ g2.drawString("Paused: " + gameState.getPausedState(), 1, 60);
+ g2.drawString("Target framerate: " + gameState.getTargetFrameRate(), 150, 24);
+ g2.drawString("Actual framerate: " + gameState.getActualFrameRate(), 150, 36);
+ g2.drawString("actualFrames: " + gameState.getActualFrames(), 150, 48);
+ g2.drawString("LFSR: " + gameState.getPokey().getDecimalValue(), 150, 60);
+ g2.drawString("Ball count: " + gameState.getBalls().size(), 300, 24);
+ g2.drawString("Particle count: " + gameState.getParticles().size(), 300, 36);
+ g2.drawString("Paddle count: " + gameState.getPaddles().size(), 300, 48);
+ g2.drawString("Text count: " + gameState.getTextElements().size(), 300, 60);
+ g2.drawString("Brick count: " + gameState.getCurrentLevel().brickList.size(), 300, 72);
short padBoxX, padBoxY, padBoxX2, padBoxY2;
g2.setStroke(new BasicStroke(1));
// Paddles
g2.setColor(vc(10, 0.5f));
- for (Paddle paddle : gameState.paddleList) {
+ for (Paddle paddle : gameState.getPaddles()) {
padBoxX = (short) paddle.hitBox.getBounds().getBounds2D().getMinX();
padBoxY = (short) paddle.hitBox.getBounds().getBounds2D().getMinY();
padBoxX2 = (short) paddle.hitBox.getBounds().getBounds2D().getWidth();
padBoxY2 = (short) paddle.hitBox.getBounds().getBounds2D().getHeight();
- g2.fillRect(padBoxX, padBoxY + (40 - paddle.paddleHeight), padBoxX2, padBoxY2);
+ g2.fillRect(padBoxX, padBoxY + (40 - paddle.height), padBoxX2, padBoxY2);
padBoxX = (short) paddle.hitBoxLeft.getBounds().getBounds2D().getMinX();
padBoxY = (short) paddle.hitBoxLeft.getBounds().getBounds2D().getMinY();
padBoxX2 = (short) paddle.hitBoxLeft.getBounds().getBounds2D().getWidth();
padBoxY2 = (short) paddle.hitBoxLeft.getBounds().getBounds2D().getHeight();
- g2.fillRect(padBoxX, padBoxY + (40 - paddle.paddleHeight), padBoxX2, padBoxY2);
+ g2.fillRect(padBoxX, padBoxY + (40 - paddle.height), padBoxX2, padBoxY2);
padBoxX = (short) paddle.hitBoxRight.getBounds().getBounds2D().getMinX();
padBoxY = (short) paddle.hitBoxRight.getBounds().getBounds2D().getMinY();
padBoxX2 = (short) paddle.hitBoxRight.getBounds().getBounds2D().getWidth();
padBoxY2 = (short) paddle.hitBoxRight.getBounds().getBounds2D().getHeight();
- g2.fillRect(padBoxX, padBoxY + (40 - paddle.paddleHeight), padBoxX2, padBoxY2);
+ g2.fillRect(padBoxX, padBoxY + (40 - paddle.height), padBoxX2, padBoxY2);
}
// Balls
g2.setColor(vc(11, 1.0f));
- for (Ball ball : gameState.ballList) {
+ for (Ball ball : gameState.getBalls()) {
padBoxX = (short) ball.hitBox.getBounds().getBounds2D().getMinX();
padBoxY = (short) ball.hitBox.getBounds().getBounds2D().getMinY();
padBoxX2 = (short) ball.hitBox.getBounds().getBounds2D().getWidth();
padBoxY2 = (short) ball.hitBox.getBounds().getBounds2D().getHeight();
- g2.fillRect(padBoxX, padBoxY + (40 - ball.ballHeight), padBoxX2, padBoxY2);
+ g2.fillRect(padBoxX, padBoxY + (40 - ball.height), padBoxX2, padBoxY2);
}
// Bricks
g2.setColor(vc(12, 0.5f));
- for (Brick brick : gameState.currentLevel.brickList) {
+ for (Brick brick : gameState.getCurrentLevel().brickList) {
padBoxX = (short) brick.hitBox.getBounds().getBounds2D().getMinX();
padBoxY = (short) brick.hitBox.getBounds().getBounds2D().getMinY();
padBoxX2 = (short) brick.hitBox.getBounds().getBounds2D().getWidth();
padBoxY2 = (short) brick.hitBox.getBounds().getBounds2D().getHeight();
- g2.fillRect(padBoxX, padBoxY + (40 - brick.brickHeight), padBoxX2, padBoxY2);
+ g2.fillRect(padBoxX, padBoxY + (40 - brick.height), padBoxX2, padBoxY2);
+ padBoxX = (short) brick.hitBoxLeft.getBounds().getBounds2D().getMinX();
+ padBoxY = (short) brick.hitBoxLeft.getBounds().getBounds2D().getMinY();
+ padBoxX2 = (short) brick.hitBoxLeft.getBounds().getBounds2D().getWidth();
+ padBoxY2 = (short) brick.hitBoxLeft.getBounds().getBounds2D().getHeight();
+ g2.fillRect(padBoxX, padBoxY + (40 - brick.height), padBoxX2, padBoxY2);
+ padBoxX = (short) brick.hitBoxRight.getBounds().getBounds2D().getMinX();
+ padBoxY = (short) brick.hitBoxRight.getBounds().getBounds2D().getMinY();
+ padBoxX2 = (short) brick.hitBoxRight.getBounds().getBounds2D().getWidth();
+ padBoxY2 = (short) brick.hitBoxRight.getBounds().getBounds2D().getHeight();
+ g2.fillRect(padBoxX, padBoxY + (40 - brick.height), padBoxX2, padBoxY2);
}
}
diff --git a/src/main/java/dev/boyfailure/vectorbreakout/GameFrame.java b/src/main/java/dev/boyfailure/vectorbreakout/GameFrame.java
new file mode 100644
index 0000000..939030f
--- /dev/null
+++ b/src/main/java/dev/boyfailure/vectorbreakout/GameFrame.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2025 Naomi (boyfailure.dev).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dev.boyfailure.vectorbreakout;
+
+import java.awt.Color;
+import java.awt.event.ComponentAdapter;
+import java.awt.event.ComponentEvent;
+import javax.swing.JFrame;
+
+/**
+ * The window in which the game is displayed.
+ * @author Naomi (boyfailure.dev)
+ * @since 1.0
+ */
+public class GameFrame extends JFrame {
+
+ private GameState gameState;
+ private GameDisplay gameDisplay;
+
+ public GameFrame(GameState gameState, GameDisplay gameDisplay) {
+ this.gameState = gameState;
+ this.gameDisplay = gameDisplay;
+
+ // Set the game window's properties
+ this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+ this.setTitle(this.gameState.getGameName());
+ this.setSize(this.gameState.getWindowResX(), this.gameState.getWindowResY());
+ this.getContentPane().setBackground(Color.black);
+ this.getContentPane().add(this.gameDisplay);
+
+ // Add the component adapter to change vector drawing scale when the window is resized
+ this.addComponentListener(this.getComponentAdapter());
+
+ // Add the key listener to get player input
+ this.addKeyListener(this.gameState.getKeyListener());
+ }
+
+ //
+
+ public ComponentAdapter getComponentAdapter() {
+ return new GameComponentAdapter();
+ }
+ class GameComponentAdapter extends ComponentAdapter {
+ public void componentResized(ComponentEvent componentEvent) {
+ GameFrame.this.gameState.setWindowResX((short) GameFrame.this.getBounds().width);
+ GameFrame.this.gameState.setWindowResY((short) GameFrame.this.getBounds().height);
+ GameFrame.this.gameDisplay.setBeamScaleX(GameFrame.this.gameState.getWindowResX() / 800f);
+ GameFrame.this.gameDisplay.setBeamScaleY(GameFrame.this.gameState.getWindowResY() / 600f);
+ if (GameFrame.this.gameDisplay.getBeamScaleX() <= GameFrame.this.gameDisplay.getBeamScaleY()) {GameFrame.this.gameDisplay.setBeamThicknessScale(GameFrame.this.gameDisplay.getBeamScaleX());}
+ else {GameFrame.this.gameDisplay.setBeamThicknessScale(GameFrame.this.gameDisplay.getBeamScaleY());}
+ }
+ }
+
+ //
+
+}
diff --git a/src/main/java/dev/boyfailure/quajra/vectorbreakout/GameState.java b/src/main/java/dev/boyfailure/vectorbreakout/GameState.java
similarity index 51%
rename from src/main/java/dev/boyfailure/quajra/vectorbreakout/GameState.java
rename to src/main/java/dev/boyfailure/vectorbreakout/GameState.java
index c9264f5..db735dc 100644
--- a/src/main/java/dev/boyfailure/quajra/vectorbreakout/GameState.java
+++ b/src/main/java/dev/boyfailure/vectorbreakout/GameState.java
@@ -1,4 +1,20 @@
-package dev.boyfailure.quajra.vectorbreakout;
+/*
+ * Copyright 2025 Naomi (boyfailure.dev).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dev.boyfailure.vectorbreakout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
@@ -6,58 +22,70 @@ import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.ArrayList;
+/**
+ * The embeddable game object which contains the main loop and logic.
+ * @author Naomi (boyfailure.dev)
+ * @since 20250511
+ */
public class GameState {
-
- public String gameName = "Vector Breakout";
- public String gameVersion = "20250511";
+
+ /** The name of the game. */
+ private String gameName = "Vector Breakout";
+ /** The version of the game. */
+ private String gameVersion = "1.0";
+ /** Determines whether the game has been started. */
private boolean isGameStarted = false;
+ /** Determines whether the game has been paused. */
private boolean isPaused = false;
+ /** Determines whether the debug menu has been enabled. */
private boolean debugMenuEnabled = false;
- public boolean isLost = false;
- public boolean movingLeft = false;
- public boolean movingRight = false;
- public boolean confettiMode = false;
- public boolean collectorActive = false;
- public byte lives = 5;
- private byte chancey = 0;
- private final byte maxChanceyValue = 16;
- public byte ballsOnScreen = 0;
+ /** Determines whether the game has been lost. */
+ private boolean isLost = false;
+ /** Determines whether the player is moving left. */
+ private boolean movingLeft = false;
+ /** Determines whether the player is moving right. */
+ private boolean movingRight = false;
+ /** Determines whether "Confetti Mode" (long-lasting particles) has been enabled. */
+ private boolean confettiMode = false;
+ /** Determines whether the collector function is running. */
+ private boolean collectorActive = false;
+ /** The number of lives the player has. */
+ private byte lives = 5;
+ /** The target frame rate for the game. */
private short targetFrameRate = 60;
- public short actualFrames = 0;
+ private short actualFrames = 0;
private short actualFrameRate = 0;
- public byte gameTickRate = 60;
+ private byte gameTickRate = 60;
private short internalResX = 800;
private short internalResY = 600;
- private short windowResX = 1024;
- private short windowResY = 768;
- public short level = 1;
- public short bricksOnScreen = 0;
- public int score = 0;
+ private short windowResX = 1280;
+ private short windowResY = 960;
+ private short level = 1;
+ private short bricksOnScreen = 0;
+ private int score = 0;
private long frameCounter = 0;
- public ArrayList ballList;
- public ArrayList particleList;
- public ArrayList paddleList;
- public ArrayList textElementList;
+ private Level currentLevel;
- public ArrayList ballCollector;
- public ArrayList particleCollector;
- public ArrayList paddleCollector;
- public ArrayList textElementCollector;
- public ArrayList brickCollector;
+ private ArrayList ballList;
+ private ArrayList particleList;
+ private ArrayList paddleList;
+ private ArrayList textElementList;
+
+ private ArrayList ballCollector;
+ private ArrayList particleCollector;
+ private ArrayList paddleCollector;
+ private ArrayList textElementCollector;
+ private ArrayList brickCollector;
+
+ private LFSR pokey;
- public Level currentLevel;
-
- // Debug stuffs
- public boolean debugSpawnAllBalls = false;
- public boolean debugUseGiantPaddle = false;
- public short debugStartLevel = 1;
-
public GameState() {
this.initComponents();
- this.showTitleScreen();
}
-
+
+ //
+
public KeyListener getKeyListener() {
return new GameKeyListener();
}
@@ -79,37 +107,29 @@ public class GameState {
class GameKeyListener implements KeyListener {
public void keyPressed(KeyEvent e) {
switch(e.getKeyCode()) {
- case 27: // Escape
+ case 27: // Escape - Toggle debug menu
toggleDebugMenu();
break;
- case 10: // Enter
- if (GameState.this.getGameStartedState()) {
- GameState.this.togglePausedState();
- if (!GameState.this.getPausedState()) {
- GameState.this.textElementList.get(6).setVisibility(false);
- }
- else {
- GameState.this.textElementList.get(6).setVisibility(true);
- }
- }
+ case 10: // Enter - Pause/unpause
+ if (GameState.this.getGameStartedState()) {GameState.this.togglePausedState();}
break;
- case 35: // End
+ case 35: // End - Start debug level
if (isLost || !GameState.this.getGameStartedState()) {GameState.this.setGameStartedState(true);GameState.this.newGame(true);}
break;
- case 32: // Space
+ case 32: // Space - Start normal game, spawn ball
if (isLost || !GameState.this.getGameStartedState()) {GameState.this.setGameStartedState(true);GameState.this.newGame();}
else if (!GameState.this.getPausedState()) {
- if (ballsOnScreen < 1) {
+ if (GameState.this.getBalls().size() < 1) {
Ball newBall = new Ball(GameState.this, 200, 300, 7, 7, 0, true);
GameState.this.ballList.add(newBall);
newBall.spawnBall(true);
}
}
break;
- case 37: // Left
+ case 37: // Left - move left
if (GameState.this.getGameStartedState()) {movingLeft = true;}
break;
- case 39: // Right
+ case 39: // Right - move right
if (GameState.this.getGameStartedState()) {movingRight = true;}
break;
}
@@ -126,9 +146,9 @@ public class GameState {
}
public void keyTyped(KeyEvent e) {}
}
-
+
class GameStateUpdateListener implements ActionListener {
- public void actionPerformed(ActionEvent e) {
+ public synchronized void actionPerformed(ActionEvent e) {
GameState.this.collectorActive = true;
GameState.this.ballCollector = new ArrayList<>();
@@ -145,7 +165,6 @@ public class GameState {
for (Brick brick : GameState.this.currentLevel.brickList) {
if (brick.getBrokenState()) {
GameState.this.brickCollector.add(brick);
- //GameState.this.currentLevel.brickList.remove(brick);
}
else {
bricksOnScreen++;
@@ -156,14 +175,12 @@ public class GameState {
for (Ball ball : GameState.this.ballList) {
if (!ball.getActiveState()) {
- //GameState.this.ballList.remove(ball);
GameState.this.ballCollector.add(ball);
}
}
for (Particle particle: GameState.this.particleList) {
if (!particle.getActiveState()) {
- //GameState.this.particleList.remove(particle);
GameState.this.particleCollector.add(particle);
}
}
@@ -186,56 +203,129 @@ public class GameState {
}
}
- public void incrementFrameCounter() {
- this.frameCounter++;
- }
- public long getFrameCounter() {
- return this.frameCounter;
- }
+ //
- public void incrementChancey() {
- this.chancey++;
- if (this.chancey >= this.maxChanceyValue) {
- this.chancey = 0;
- }
- }
- public byte getChancey() {
- return this.chancey;
- }
+ //
+ //
+ /**
+ * Gets the name of the game.
+ * @return the name of the game
+ */
+ public String getGameName() {
+ return this.gameName;
+ }
+ /**
+ * Gets the version number of the game.
+ * @return the version number of the game
+ */
+ public String getGameVersion() {
+ return this.gameVersion;
+ }
+ //
+
+ //
+ /**
+ * Gets the current instance of "Pokey", the game's LFSR.
+ * @return pokey
+ */
+ public LFSR getPokey() {
+ return this.pokey;
+ }
+ //
+
+ //
+ /**
+ * Gets the current game tick rate, in Hz.
+ * @return the current game tick rate (in Hz)
+ */
public byte getGameTickRate() {
return this.gameTickRate;
}
+ /**
+ * Sets the current game tick rate, in Hz.
+ * @param newTickRate the game tick rate, in Hz
+ */
+ public void setGameTickRate(byte newTickRate) {
+ this.gameTickRate = (byte) newTickRate;
+ }
+ //
+
+ //
+ /**
+ * Gets the target game frame rate, in Hz.
+ * @return the target game frame rate (in Hz)
+ */
public short getTargetFrameRate() {
return this.targetFrameRate;
}
+ /**
+ * Gets the actual game frame rate, in Hz.
+ * @return the actual game frame rate (in Hz)
+ */
public short getActualFrameRate() {
return this.actualFrameRate;
}
+ /**
+ * Increments the frame counter.
+ */
+ public void incrementFrameCounter() {
+ this.frameCounter++;
+ }
+
+ /**
+ * Gets the value of the frame counter.
+ * @return the value of the frame counter
+ */
+ public long getFrameCounter() {
+ return this.frameCounter;
+ }
+
+ /**
+ * Gets the actual frames drawn during the last second.
+ * @return the actual frames drawn during the last second.
+ */
+ public short getActualFrames() {
+ return this.actualFrames;
+ }
+ public void incrementActualFrames() {
+ this.actualFrames++;
+ }
+ //
+
+ //
public boolean getPausedState() {
return this.isPaused;
}
public void togglePausedState() {
this.isPaused = !this.isPaused;
+ if (this.getPausedState()) {this.textElementList.get(6).getVisibleState(true);}
+ else {this.textElementList.get(6).getVisibleState(false);}
}
+ //
+ //
public boolean getDebugMenuState() {
return this.debugMenuEnabled;
}
public void toggleDebugMenu() {
this.debugMenuEnabled = !this.debugMenuEnabled;
}
+ //
+ //
public boolean getGameStartedState() {
return this.isGameStarted;
}
public void setGameStartedState(boolean startState) {
this.isGameStarted = startState;
}
+ //
+ //
public short getInternalResX() {
return this.internalResX;
}
@@ -254,25 +344,94 @@ public class GameState {
public void setWindowResY(short resolution) {
this.windowResY = resolution;
}
+ //
+
+ //
+ public byte getLives() {
+ return this.lives;
+ }
+ public void addLives(int livesAdded) {
+ if (livesAdded >= 127) {livesAdded = 127;}
+ else if (livesAdded < 0) {livesAdded = 0;}
+ this.lives += (byte) livesAdded;
+ }
+ public void addLife() {
+ this.addLives(1);
+ }
+ public void removeLife() {
+ this.lives--;
+ if (this.lives < 0) {this.lives = 0;}
+ }
+ //
+
+ //
+ public int getScore() {
+ return this.score;
+ }
+ public void addScore(int scoreAdded) {
+ this.score += scoreAdded;
+ }
+ public void resetScore() {
+ this.score = 0;
+ }
+ //
+
+ //
+ public short getCurrentLevelID() {
+ return this.level;
+ }
+ public void setCurrentLevelID(short levelID) {
+ this.level = levelID;
+ }
+ public Level getCurrentLevel() {
+ return this.currentLevel;
+ }
+ //
+
+ //
+ public ArrayList getBalls() {
+ return this.ballList;
+ }
+ public ArrayList getParticles() {
+ return this.particleList;
+ }
+ public ArrayList getPaddles() {
+ return this.paddleList;
+ }
+ public ArrayList getTextElements() {
+ return this.textElementList;
+ }
+ //
+
+ //
+ public boolean getCollectorActiveState() {
+ return this.collectorActive;
+ }
+ //
+
+ //
+
+ public synchronized void onGameTick() {
- public void onGameTick() {
- this.incrementChancey();
-
if (this.getPausedState()) {return;}
-
+
// Move player if keys are held
- for (Paddle paddle : this.paddleList) {
- if (this.movingLeft) {
- paddle.movePaddle(false);
- }
- else if (this.movingRight) {
- paddle.movePaddle(true);
- }
- else {
- paddle.paddleSpeed = 0;
+ try {
+ for (Paddle paddle : this.paddleList) {
+ if (this.movingLeft) {
+ paddle.movePaddle(false);
+ }
+ else if (this.movingRight) {
+ paddle.movePaddle(true);
+ }
+ else {
+ paddle.paddleSpeed = 0;
+ }
}
+ } catch(java.util.ConcurrentModificationException except) {
+ System.err.println("ConcurrentModificationException, onGameTick() Paddle iteration " + System.currentTimeMillis());
}
-
+
// Ball logic (movement, collision checks)
try {
for (Ball ball : this.ballList) {
@@ -281,27 +440,28 @@ public class GameState {
}
}
} catch(java.util.ConcurrentModificationException except) {
- System.err.println("ConcurrentModificationException, onGameTick() Ball iteration\n"
- + "Resetting ballList ArrayList.");
- // except.printStackTrace();
- this.ballList = new ArrayList<>();
+ System.err.println("ConcurrentModificationException, onGameTick() Ball iteration " + System.currentTimeMillis());
}
-
+
// Particles
- for (Particle particle : this.particleList) {
- if (particle.getActiveState()) {
- particle.update();
+ try {
+ for (Particle particle : this.particleList) {
+ if (particle.getActiveState()) {
+ particle.update();
+ }
}
+ } catch(java.util.ConcurrentModificationException except) {
+ System.err.println("ConcurrentModificationException, onGameTick() Particle iteration " + System.currentTimeMillis());
}
}
-
+
public void showTitleScreen() {
textElementList.get(0).activateTimer();
textElementList.get(1).activateTimer();
textElementList.get(2).activateTimer();
}
-
- public void gameLose() {
+
+ public synchronized void gameLose() {
if (this.currentLevel != null) {
for (Brick brick : this.currentLevel.brickList) {
brick.setBrokenState(true);
@@ -314,22 +474,22 @@ public class GameState {
}
for (Paddle paddle : this.paddleList) {
paddle.setActiveState(false);
+ paddle.cullPaddle();
this.paddleCollector.add(paddle);
}
this.isLost = true;
- this.ballsOnScreen = 0;
- this.textElementList.get(3).setVisibility(false);
- this.textElementList.get(4).setVisibility(false);
- this.textElementList.get(5).setVisibility(false);
- this.textElementList.get(8).setVisibility(true);
- this.textElementList.get(9).setVisibility(true);
+ this.textElementList.get(3).getVisibleState(false);
+ this.textElementList.get(4).getVisibleState(false);
+ this.textElementList.get(5).getVisibleState(false);
+ this.textElementList.get(8).getVisibleState(true);
+ this.textElementList.get(9).getVisibleState(true);
this.getGameStateUpdateActionListener().actionPerformed(new ActionEvent(this, 0, ""));
}
-
+
public void newGame() {
newGame(false);
}
-
+
public void newGame(boolean debugLevel) {
lives = 5;
isLost = false;
@@ -337,39 +497,33 @@ public class GameState {
if (debugLevel) {level = 32767;}
else {level = 1;}
frameCounter = 0;
- ballsOnScreen = 0;
movingLeft = false;
movingRight = false;
- if (debugUseGiantPaddle) {this.paddleList.add(new Paddle(this, 0, 500, 800, 60, 0));}
- else {this.paddleList.add(new Paddle(this, 350, 500, 100, 15, 0));}
- if (this.debugUseGiantPaddle) {
- this.paddleList.add(new Paddle(this, 0, 500, 800, 60, 0));
- }
- else {
- this.paddleList.add(new Paddle(this, 350, 500, 100, 15, 0));
- }
- this.textElementList.get(0).setVisibility(false);
- this.textElementList.get(1).setVisibility(false);
- this.textElementList.get(2).setVisibility(false);
- this.textElementList.get(3).setVisibility(true);
- this.textElementList.get(4).setVisibility(true);
- this.textElementList.get(5).setVisibility(true);
+ this.paddleList.add(new Paddle(this, 350, 500, 100, 15, 0));
+ this.textElementList.get(0).getVisibleState(false);
+ this.textElementList.get(1).getVisibleState(false);
+ this.textElementList.get(2).getVisibleState(false);
+ this.textElementList.get(3).getVisibleState(true);
+ this.textElementList.get(4).getVisibleState(true);
+ this.textElementList.get(5).getVisibleState(true);
textElementList.get(3).setText(String.valueOf(this.score));
textElementList.get(4).setText(String.valueOf(this.lives));
textElementList.get(5).setText(String.valueOf(this.level));
- this.textElementList.get(8).setVisibility(false);
- this.textElementList.get(9).setVisibility(false);
- if (debugStartLevel > 1) {level = debugStartLevel;}
+ this.textElementList.get(8).getVisibleState(false);
+ this.textElementList.get(9).getVisibleState(false);
this.currentLevel = new Level(this, level);
this.currentLevel.constructLevel();
this.updateStaticTextElements();
}
- public void initComponents() {
+ public synchronized void initComponents() {
this.ballList = new ArrayList<>();
this.particleList = new ArrayList<>();
this.paddleList = new ArrayList<>();
this.textElementList = new ArrayList<>();
+
+ this.pokey = new LFSR();
+
// List of indices
textElementList.add(new TextElement(this, "VECTOR BREAKOUT", 2.5f, 67, 280, 15, 2)); // 0 - Title
textElementList.add(new TextElement(this, "MMXXV BOYFAILURE.DEV", 0.75f, 259, 315, 15, 1)); // 1 - Copyright
diff --git a/src/main/java/dev/boyfailure/quajra/vectorbreakout/Hitbox.java b/src/main/java/dev/boyfailure/vectorbreakout/Hitbox.java
similarity index 54%
rename from src/main/java/dev/boyfailure/quajra/vectorbreakout/Hitbox.java
rename to src/main/java/dev/boyfailure/vectorbreakout/Hitbox.java
index 483851d..4c15178 100644
--- a/src/main/java/dev/boyfailure/quajra/vectorbreakout/Hitbox.java
+++ b/src/main/java/dev/boyfailure/vectorbreakout/Hitbox.java
@@ -1,7 +1,28 @@
-package dev.boyfailure.quajra.vectorbreakout;
+/*
+ * Copyright 2025 Naomi (boyfailure.dev).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dev.boyfailure.vectorbreakout;
import java.awt.geom.Rectangle2D;
+/**
+ * Invisible regions in each object that dictate when it collides with other objects.
+ * @author Naomi (boyfailure.dev)
+ * @since 20250327
+ */
public class Hitbox {
private Rectangle2D bounds;
diff --git a/src/main/java/dev/boyfailure/vectorbreakout/LFSR.java b/src/main/java/dev/boyfailure/vectorbreakout/LFSR.java
new file mode 100644
index 0000000..06cd338
--- /dev/null
+++ b/src/main/java/dev/boyfailure/vectorbreakout/LFSR.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2025 Naomi (boyfailure.dev).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dev.boyfailure.vectorbreakout;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import javax.swing.Timer;
+
+/**
+ * A software implementation of a LFSR-based (linear frequency shift register) pseudo-random number generator.
+ * @author Naomi (boyfailure.dev)
+ * @since 1.0
+ */
+public class LFSR {
+
+ private static boolean[] originalArray = new boolean[] {true,false,true,true,true,true,true,true,false,true,false,false,false,true,true,false,false};
+
+ private boolean[] bitArray;
+ private int bitCount;
+ private int bitsReturned;
+ private int firstXorIndex;
+ private int secondXorIndex;
+
+ private Timer lfsrTimer;
+
+ public LFSR(int bitCount, int bitsReturned, int firstXorIndex, int secondXorIndex) {
+ this.bitCount = bitCount;
+ this.bitsReturned = bitsReturned;
+ this.firstXorIndex = firstXorIndex;
+ this.secondXorIndex = secondXorIndex;
+ if (bitsReturned > bitCount) {bitsReturned = bitCount;}
+ bitArray = new boolean[bitCount];
+ for (int i = 0; i < bitCount; i++) {
+ if (i < 17) {bitArray[i] = LFSR.originalArray[i];}
+ else {
+ bitArray[i] = bitArray[i - 3] ^ bitArray[i - 8];
+ }
+ }
+ this.lfsrTimer = new Timer(1, new ActionListener(){public void actionPerformed(ActionEvent e) {LFSR.this.shift();}});
+ this.lfsrTimer.start();
+ }
+ public LFSR() {
+ this(17, 8, 11, 16);
+ }
+
+ public int getDecimalValue() {
+ int regValue = 0;
+ int bitIndex = this.bitCount - 1;
+
+ for (int i = 0; i < this.bitsReturned; i++) {
+ if (this.bitArray[bitIndex]) {
+ regValue += Math.pow(2, i);
+ }
+ bitIndex--;
+ }
+
+ return regValue;
+ }
+ public int getShiftedDecimalValue() {
+ this.shift();
+ return this.getDecimalValue();
+ }
+
+ public void shift() {
+ for (int i = (this.bitCount - 1); i > 0; i--) {
+ this.bitArray[i] = this.bitArray[i - 1];
+ }
+ this.bitArray[0] = this.bitArray[this.firstXorIndex] ^ this.bitArray[this.secondXorIndex];
+ }
+
+ public String getBits() {
+ String result = "";
+ for (int i = 0; i < this.bitCount; i++) {
+ if (this.bitArray[i] == true) {
+ result += "1";
+ }
+ else {
+ result += "0";
+ }
+ }
+ return result;
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/dev/boyfailure/quajra/vectorbreakout/Level.java b/src/main/java/dev/boyfailure/vectorbreakout/Level.java
similarity index 78%
rename from src/main/java/dev/boyfailure/quajra/vectorbreakout/Level.java
rename to src/main/java/dev/boyfailure/vectorbreakout/Level.java
index 636caa7..59d7179 100644
--- a/src/main/java/dev/boyfailure/quajra/vectorbreakout/Level.java
+++ b/src/main/java/dev/boyfailure/vectorbreakout/Level.java
@@ -1,7 +1,28 @@
-package dev.boyfailure.quajra.vectorbreakout;
+/*
+ * Copyright 2025 Naomi (boyfailure.dev).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dev.boyfailure.vectorbreakout;
import java.util.ArrayList;
+/**
+ * The progressive levels that contain different brick layouts.
+ * @author Naomi (boyfailure.dev)
+ * @since 20250325
+ */
public class Level {
short levelID;
@@ -17,8 +38,8 @@ public class Level {
public Level(GameState gameState, int id) {
this.gameState = gameState;
this.levelID = (short) id;
-
- switch(id) {
+
+ switch(this.levelID) {
case 1:
this.levelName = "HELLO WORLD";
this.levelLayout = new byte[]
@@ -29,35 +50,35 @@ public class Level {
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,
+ 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 2:
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,
+ {4,4,4,4,4, 4,4,4,4,4,4,4,4, 4,4,4,4,4,4,
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};
+ 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,
+ 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 = "THE GAP";
this.levelLayout = new byte[]
- {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,
+ {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};
+ 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,
+ 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:
@@ -70,7 +91,7 @@ public class Level {
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,
+ 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:
@@ -88,12 +109,12 @@ public class Level {
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,
+ 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,
+ 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,
+ 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:
@@ -112,11 +133,11 @@ public class Level {
{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,
+ 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,
+ 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:
@@ -130,14 +151,13 @@ public class Level {
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,
+ 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,
@@ -168,17 +188,17 @@ public class Level {
7,1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,
1,2,3,4,5,6,7,1,2,3,4,5,6,7,1,2,3,4,5};
this.colorMap = new byte[]
- {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,
- 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,0,
- 2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,0,1,
- 3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,0,1,2,
- 4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,0,1,2,3,
- 5,6,7,8,9,10,11,12,13,14,15,16,17,18,0,1,2,3,4,
- 6,7,8,9,10,11,12,13,14,15,16,17,18,0,1,2,3,4,5,
- 7,8,9,10,11,12,13,14,15,16,17,18,0,1,2,3,4,5,6};
+ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,16,17,18,
+ 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,16,17,18, 0,
+ 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,16,17,18, 0, 1,
+ 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,16,17,18, 0, 1, 2,
+ 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,16,17,18, 0, 1, 2, 3,
+ 5, 6, 7, 8, 9,10,11,12,13,14,15,16,17,18, 0, 1, 2, 3, 4,
+ 6, 7, 8, 9,10,11,12,13,14,15,16,17,18, 0, 1, 2, 3, 4, 5,
+ 7, 8, 9,10,11,12,13,14,15,16,17,18, 0, 1, 2, 3, 4, 5, 6};
break;
default:
- this.levelName = "LEVEL 404: NOT FOUND (" + levelID + ")";
+ this.levelName = "LEVEL 404: NOT FOUND (" + this.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,
@@ -191,7 +211,7 @@ public class Level {
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2};
break;
}
-
+
}
public void constructLevel() {
@@ -234,9 +254,8 @@ public class Level {
}
public void startLevel() {
- gameState.bricksOnScreen = (short) this.brickList.size();
- gameState.textElementList.set(7, new TextElement(gameState, this.levelName, 1, 24, 480, 15, 2, 3000));
- gameState.textElementList.get(7).activateTimer();
+ gameState.getTextElements().set(7, new TextElement(gameState, this.levelName, 1, 24, 480, 15, 2, 3000));
+ gameState.getTextElements().get(7).activateTimer();
}
-}
+}
\ No newline at end of file
diff --git a/src/main/java/dev/boyfailure/quajra/vectorbreakout/Menu.java b/src/main/java/dev/boyfailure/vectorbreakout/Menu.java
similarity index 62%
rename from src/main/java/dev/boyfailure/quajra/vectorbreakout/Menu.java
rename to src/main/java/dev/boyfailure/vectorbreakout/Menu.java
index 87ed432..5fdcc09 100644
--- a/src/main/java/dev/boyfailure/quajra/vectorbreakout/Menu.java
+++ b/src/main/java/dev/boyfailure/vectorbreakout/Menu.java
@@ -1,7 +1,28 @@
-package dev.boyfailure.quajra.vectorbreakout;
+/*
+ * Copyright 2025 Naomi (boyfailure.dev).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dev.boyfailure.vectorbreakout;
import java.util.ArrayList;
+/**
+ * Customizable game menus.
+ * @author Naomi (boyfailure.dev)
+ * @since 1.0
+ */
public class Menu {
public String menuTitle = "Menu";
diff --git a/src/main/java/dev/boyfailure/vectorbreakout/Paddle.java b/src/main/java/dev/boyfailure/vectorbreakout/Paddle.java
new file mode 100644
index 0000000..a9a0b0c
--- /dev/null
+++ b/src/main/java/dev/boyfailure/vectorbreakout/Paddle.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2025 Naomi (boyfailure.dev).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dev.boyfailure.vectorbreakout;
+
+/**
+ * The player-controlled paddle that guides balls into bricks.
+ * @author Naomi (boyfailure.dev)
+ * @since 20250323
+ */
+public class Paddle extends Entity {
+
+ byte paddleColor;
+ byte paddleSpeed = 0;
+ boolean isActive = true;
+ float speedMultiplier = 1;
+ Hitbox hitBoxLeft;
+ Hitbox hitBoxRight;
+
+ private GameState gameState;
+
+ public Paddle(GameState gameState, int x, int y, int width, int height, int paddleColor) {
+ this.gameState = gameState;
+ this.posX = (short) x;
+ this.posY = (short) y;
+ this.width = (short) width;
+ this.height = (short) height;
+ this.paddleColor = (byte) paddleColor;
+ this.hitBox = new Hitbox(x, y, width, height, 4);
+ this.hitBoxLeft = new Hitbox(x, y, width / 5, height, 4);
+ this.hitBoxRight = new Hitbox(x + ((width / 5) * 4), y, width / 5, height, 4);
+ }
+
+ public void movePaddle(boolean direction) {
+ if (this.paddleSpeed <= 15) {this.paddleSpeed += 3;}
+ if (direction) {this.posX += (this.paddleSpeed * this.speedMultiplier);}
+ else {this.posX -= (this.paddleSpeed * this.speedMultiplier);}
+ if (this.posX <= 20) {this.posX = 20;}
+ else if (this.posX >= (gameState.getInternalResX() - this.width - 20)) {this.posX = (short) (gameState.getInternalResX() - this.width - 20);}
+
+ this.hitBox.moveTo(this.posX, this.posY, this.width, this.height);
+ this.hitBoxLeft.moveTo(this.posX, this.posY, this.width / 5, this.height);
+ this.hitBoxRight.moveTo(this.posX + ((this.width / 5) * 4), this.posY, this.width / 5, this.height);
+ }
+
+ public void cullPaddle() {
+ isActive = false;
+ this.posX = 32767;
+ this.posY = 32767;
+ this.hitBox.setBounds(32766, 32766, 32767, 32767);
+ this.hitBoxLeft.setBounds(32766, 32766, 32767, 32767);
+ this.hitBoxRight.setBounds(32766, 32766, 32767, 32767);
+ }
+
+ public boolean getActiveState() {
+ return this.isActive;
+ }
+ public void setActiveState(boolean activeState) {
+ this.isActive = activeState;
+ }
+
+}
diff --git a/src/main/java/dev/boyfailure/quajra/vectorbreakout/Particle.java b/src/main/java/dev/boyfailure/vectorbreakout/Particle.java
similarity index 69%
rename from src/main/java/dev/boyfailure/quajra/vectorbreakout/Particle.java
rename to src/main/java/dev/boyfailure/vectorbreakout/Particle.java
index 55b650c..b12223c 100644
--- a/src/main/java/dev/boyfailure/quajra/vectorbreakout/Particle.java
+++ b/src/main/java/dev/boyfailure/vectorbreakout/Particle.java
@@ -1,5 +1,26 @@
-package dev.boyfailure.quajra.vectorbreakout;
+/*
+ * Copyright 2025 Naomi (boyfailure.dev).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package dev.boyfailure.vectorbreakout;
+
+/**
+ * Small graphical effects that appear on screen.
+ * @author Naomi (boyfailure.dev)
+ * @since 20250325
+ */
public class Particle {
byte particleID;
@@ -32,8 +53,9 @@ public class Particle {
public void spawn(int lifetime) {
this.isActive = true;
- if (gameState.confettiMode) {this.particleLifetime = 400;}
- else {this.particleLifetime = (short) lifetime;}
+ // if (gameState.getConfettiMode()) {this.particleLifetime = 400;}
+ // else {this.particleLifetime = (short) lifetime;}
+ this.particleLifetime = (short) lifetime;
}
public void update() {
diff --git a/src/main/java/dev/boyfailure/vectorbreakout/TextElement.java b/src/main/java/dev/boyfailure/vectorbreakout/TextElement.java
new file mode 100644
index 0000000..a6a4945
--- /dev/null
+++ b/src/main/java/dev/boyfailure/vectorbreakout/TextElement.java
@@ -0,0 +1,247 @@
+/*
+ * Copyright 2025 Naomi (boyfailure.dev).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dev.boyfailure.vectorbreakout;
+
+import java.awt.Point;
+import java.util.Timer;
+import java.util.TimerTask;
+
+/**
+ * Fields of text that display on screen.
+ * @author Naomi (boyfailure.dev)
+ * @since 20250326
+ */
+public class TextElement {
+
+ private String text;
+ private boolean active = true;
+ private boolean isVisible = false;
+ private boolean moveUpOnFrame = false;
+ private float textScale = 1;
+ private float vectorThickness = 1;
+ private byte textColor;
+ private short x;
+ private short y;
+ private short duration = 0;
+ private 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
+ * @param vectorThickness the thickness of the vector lines
+ */
+ public TextElement(GameState gameState, String text, float textScale, int x, int y, int textColor, float vectorThickness) {
+ this.gameState = gameState;
+ this.text = text;
+ this.textScale = textScale;
+ this.x = (short) x;
+ this.y = (short) y;
+ this.textColor = (byte) textColor;
+ this.vectorThickness = vectorThickness;
+ }
+
+ /**
+ * 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 vectorThickness the thickness of the vector lines
+ * @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 vectorThickness, int duration) {
+ this.gameState = gameState;
+ this.text = text;
+ this.textScale = textScale;
+ this.x = (short) x;
+ this.y = (short) y;
+ this.textColor = (byte) textColor;
+ this.vectorThickness = vectorThickness;
+ this.duration = (short) duration;
+ this.creationTime = System.currentTimeMillis();
+ }
+
+ //
+
+ //
+ /**
+ * Gets the active status of the TextElement.
+ * @return the active status of the TextElement
+ */
+ public boolean getActiveState() {
+ return this.active;
+ }
+ /**
+ * Sets the active status of the TextElement.
+ * @param activeState the active status of the TextElement
+ */
+ public void setActiveState(boolean activeState) {
+ this.active = activeState;
+ }
+ //
+
+ //
+ /**
+ * Gets the visibility state of the TextElement.
+ * @return the visibility state of the TextElement
+ */
+ public boolean getVisibleState() {
+ return this.isVisible;
+ }
+ /**
+ * Sets the visible state of the TextElement.
+ * @param visibleState the visible state of the TextElement
+ */
+ public void getVisibleState(boolean visibleState) {
+ this.isVisible = visibleState;
+ }
+ //
+
+ //
+ /**
+ * Gets the color code of the TextElement.
+ * @return the color code of the TextElement
+ */
+ public byte getColor() {
+ return this.textColor;
+ }
+ /**
+ * Sets the color code of the TextElement.
+ * @param textColor the color code of the TextElement
+ */
+ public void setColor(int textColor) {
+ this.textColor = (byte) textColor;
+ }
+ //
+
+ //
+ /**
+ * Returns the thickness of the TextElement's vector lines.
+ * @return the thickness of the TextElement's vector lines
+ */
+ public float getVectorThickness() {
+ return this.vectorThickness;
+ }
+ /**
+ * Sets the thickness of the TextElement's vector lines.
+ * @param vectorThickness the new thickness of the TextElement's vector lines
+ */
+ public void setVectorThickness(float vectorThickness) {
+ this.vectorThickness = vectorThickness;
+ }
+ /**
+ * Returns the scale of the text.
+ * @return the scale of the text
+ */
+ public float getTextScale() {
+ return this.textScale;
+ }
+ /**
+ * Sets the scale of the text.
+ * @param textScale the new scale of the text
+ */
+ public void setTextScale(float textScale) {
+ this.textScale = textScale;
+ }
+ //
+
+ //
+ /**
+ * Gets the text in the TextElement.
+ * @return the text in the TextElement
+ */
+ public String getText() {
+ return this.text;
+ }
+ /**
+ * Changes the text that the TextElement will display.
+ * @param text the text that the TextElement will display
+ */
+ public void setText(String text) {
+ this.text = text;
+ }
+ //
+
+ //
+ public boolean movesUpOnFrame() {
+ return this.moveUpOnFrame;
+ }
+ public void moveUpOnFrame() {
+ this.moveUpOnFrame = true;
+ }
+ //
+
+ //
+ public Point getPosition() {
+ return new Point(this.x, this.y);
+ }
+ public short getPositionX() {
+ return this.x;
+ }
+ public short getPositionY() {
+ return this.y;
+ }
+ //
+
+ //
+
+ /**
+ * Prepares the TextElement for rendering.
+ */
+ public void activateTimer() {
+ this.setActiveState(true);
+ long testLong = this.creationTime;
+ if (this.duration > 0) {
+ Timer levelTitleGTFO = new Timer();
+ levelTitleGTFO.schedule(new TimerTask() {
+ public void run() {
+ TextElement.this.getVisibleState(false);
+ }
+ }, this.duration);
+ }
+ this.getVisibleState(true);
+ }
+
+ /**
+ * Prepares the TextElement for culling.
+ */
+ public void cull() {
+ this.active = false;
+ this.x = 32767;
+ this.y = 32767;
+ }
+
+ /**
+ * Changes the text that the TextElement will display.
+ * @param x The x coordinate of the text's origin point.
+ * @param y The y coordinate of the text's origin point.
+ */
+ public void moveTo(int x, int y) {
+ this.x = (short) x;
+ this.y = (short) y;
+ }
+
+}
diff --git a/src/main/java/dev/boyfailure/vectorbreakout/VectorBreakout.java b/src/main/java/dev/boyfailure/vectorbreakout/VectorBreakout.java
new file mode 100644
index 0000000..be998e5
--- /dev/null
+++ b/src/main/java/dev/boyfailure/vectorbreakout/VectorBreakout.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2025 Naomi (boyfailure.dev).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dev.boyfailure.vectorbreakout;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import javax.swing.Timer;
+
+/**
+ * The initial code to launch the game.
+ * @author Naomi (boyfailure.dev)
+ * @since 20250323
+ */
+public class VectorBreakout {
+
+ public static GameState gameState;
+ public static GameDisplay gameDisplay;
+ public static GameFrame gameFrame;
+
+ /**
+ * This empty constructor is currently only in here because the main class
+ * constructor generated in new projects on NetBeans 4. Will investigate to
+ * see if this is necessary in JDK 1.5 next time I am using those tools.
+ */
+ public VectorBreakout() {}
+
+ public static void main(String[] args) {
+
+ // Enable OpenGL for hardware acceleration
+ System.setProperty("sun.java2d.opengl", "true");
+
+ // Initialize the components
+ gameState = new GameState();
+ gameDisplay = new GameDisplay(gameState);
+ gameFrame = new GameFrame(gameState, gameDisplay);
+
+ Timer gameTick = new Timer((1000 / gameState.getGameTickRate()), gameState.getGameTickActionListener());
+ gameTick.start();
+
+ Timer frameDisplay = new Timer((1000 / gameState.getTargetFrameRate()), new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ gameState.incrementFrameCounter();
+ gameFrame.repaint();
+ }
+ });
+ frameDisplay.start();
+
+ Timer gameCuller = new Timer(1000, gameState.getGameStateUpdateActionListener());
+ gameCuller.start();
+
+ // Show the game window
+ gameState.showTitleScreen();
+ gameFrame.setVisible(true);
+
+ }
+
+}