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); + + } + +}