Skip to content
This repository was archived by the owner on Dec 24, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
9dc0071
ci: remove ratchetFrom configuration in spotless (#2)
thnhmai06 Sep 29, 2025
90d06b7
Implement abstract class Ball (#3)
minngoc1213 Sep 30, 2025
b0844df
feat: Brick (#1)
ManhTanTran Sep 30, 2025
bfd5534
feat: Add PowerUp class (#5)
thnhmai06 Sep 30, 2025
9ebb12d
chore: Update .gitignore and add commit instructions
thnhmai06 Oct 1, 2025
f803b3c
Merge pull request #6
ManhTanTran Oct 1, 2025
5d7487b
docs: Update documentation links in PowerUp and Ball classes
thnhmai06 Oct 1, 2025
e5f75a4
Brick (#7)
ManhTanTran Oct 7, 2025
169ad53
refactor: remove duplicate `paddle` folder
thnhmai06 Oct 7, 2025
84c357f
feat/GameApplication (#8)
ManhTanTran Oct 8, 2025
7419cd4
ci: update workflow setup steps for clarity
thnhmai06 Oct 8, 2025
06eb0b7
chore: remove debug script parameters from run configurations
thnhmai06 Oct 8, 2025
f11276a
refactor: reorganize component structure and improve entity tagging (…
thnhmai06 Oct 9, 2025
c0e00af
chore: remove unused logging dependencies from build.gradle
thnhmai06 Oct 11, 2025
91e1aca
chore: update output directory for build artifacts
thnhmai06 Oct 11, 2025
0079733
Add game systems template (#13)
thnhmai06 Oct 13, 2025
a2d0010
chore: update project JDK language level to JDK_24_PREVIEW
thnhmai06 Oct 13, 2025
4ea8826
BREAKING CHANGE: Add Paddle Components, refactor codebase and ci (#10)
ManhTanTran Oct 21, 2025
06f6adc
[skip ci] chore: add CODEOWNERS
thnhmai06 Oct 21, 2025
4b9e256
Add Ball entity and Ball, Brick physic/collision components (#14)
minngoc1213 Oct 22, 2025
eba320e
Fix divergence: update game systems, factories, and new PauseMenu
ManhTanTran Oct 28, 2025
240a68d
Update physics and wall logic for Ball–Brick–Paddle collisions
ManhTanTran Oct 28, 2025
7d1ebdf
Merge branch 'dev' into fix/diverged-main
thnhmai06 Oct 28, 2025
bbbb929
[skip ci] chore: reformatting code
thnhmai06 Oct 28, 2025
12f0db1
Fix: Adjust Ball vs Wall collision logic and physics handling
ManhTanTran Oct 29, 2025
72d0790
Update: BrickFactory logic and new texture assets
ManhTanTran Oct 29, 2025
157661f
chore: temp commit before switching device (update brick logic & add …
ManhTanTran Oct 29, 2025
2218e3e
feat: add Special.java and update BrickFactory for strong & shield br…
ManhTanTran Oct 29, 2025
daed1e8
feat: implement strong shield brick system
ManhTanTran Nov 1, 2025
c8b7432
feat: update local changes before merge
ManhTanTran Nov 4, 2025
1338acc
feat(ui): update UISystem & integrate BackgroundColorManager
ManhTanTran Nov 4, 2025
f3239a4
merge: integrate feature/strong-shield-brick with latest power-up system
ManhTanTran Nov 4, 2025
2e4d181
feat(powerup): merge and restructure power-up system
ManhTanTran Nov 5, 2025
f44a859
feat(powerup): rework PowerUp system
ManhTanTran Nov 5, 2025
7bda65f
Merge branch 'dev' into feature/powerup-merge
ManhTanTran Nov 8, 2025
b024a20
wip: temporary commit before switching device [skip ci]
ManhTanTran Nov 9, 2025
ba79775
feat(powerup): add Magnet & Gun power-ups and paddle attachment compo…
ManhTanTran Nov 10, 2025
299026d
chore: commit tạm để chuyển máy (merge conflict PowerUpFactory)
ManhTanTran Nov 10, 2025
30a79ee
Resolve all merge conflicts in feature/powerup-merge
ManhTanTran Nov 11, 2025
7b7190e
feat(ui): add PowerUp HUD countdown display and CSS styling
ManhTanTran Nov 11, 2025
3bfdefe
chore(assets): update brick textures and new paddle/ball graphics
ManhTanTran Nov 11, 2025
066df51
merge: resolve all conflicts (PowerUpFactory, WallFactory, AppEventSy…
ManhTanTran Nov 11, 2025
997c3d5
refactor(assets): update paddle and brick textures; adjust related fa…
ManhTanTran Nov 11, 2025
2d10408
fix(brick): prevent NPE when hpPercent=0 and update strong brick text…
ManhTanTran Nov 11, 2025
c65bf77
fix(powerup): spawn multiple balls from original ball position instea…
ManhTanTran Nov 11, 2025
ae47152
fix(core): update gameplay components and system logic for power-up i…
ManhTanTran Nov 11, 2025
13c4eb9
feat(assets): update paddle textures
ManhTanTran Nov 12, 2025
18d8520
refactor(paddle): replace PaddleSizeManager & PaddleTextureManager wi…
ManhTanTran Nov 12, 2025
6e52770
refactor(powerup): integrate shield logic into ShieldPowerUp and remo…
ManhTanTran Nov 12, 2025
224cec4
merge: resolve conflicts in core systems (Bounceverse, AppEventSystem…
ManhTanTran Nov 12, 2025
235b80a
fix: update logic and UI behavior after merge (Attachment, BrickFacto…
ManhTanTran Nov 12, 2025
96d8187
refactor: Merging with new version of code
thnhmai06 Nov 12, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ dependencies {
implementation 'com.google.guava:guava:33.5.0-jre'
implementation 'com.moandjiezana.toml:toml4j:0.7.2'
implementation 'com.google.guava:guava:33.5.0-jre'
implementation 'com.google.auto.service:auto-service-annotations:1.1.1'
annotationProcessor 'com.google.auto.service:auto-service:1.1.1'
}

application {
Expand Down
52 changes: 31 additions & 21 deletions src/main/java/com/github/codestorm/bounceverse/AssetsPath.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,20 @@

import org.jspecify.annotations.NonNull;

import java.util.*;
import java.util.Map;
import java.util.NavigableMap;
import java.util.Objects;
import java.util.TreeMap;

/**
*
*
* <h1>{@link AssetsPath}</h1>
*
* Nơi lưu trữ các đường dẫn tới assets.
* Nơi lưu trữ các đường dẫn tới assets (texture, âm thanh, video, v.v.).
*/
public final class AssetsPath {

private AssetsPath() {}

private static final String ROOT = "/assets";
Expand All @@ -25,8 +29,7 @@ public static final class Video {
private Video() {}

private static final String ROOT = AssetsPath.ROOT + "/videos";

public static final String INTRO = "intro.mp4";
public static final String INTRO = "/intro.mp4";
}

public static final class Sounds {
Expand Down Expand Up @@ -81,33 +84,31 @@ public static final class Textures {
private Textures() {}

public static final class Bricks {

private Bricks() {}

private static final String ROOT = "bricks";

public static final Map<Color, ColorAssets> COLORS;
public static final Map<String, ColorAssets> COLORS;

static {
COLORS =
Map.of(
Color.BLUE,
new ColorAssets(Color.BLUE),
Color.GREEN,
new ColorAssets(Color.GREEN),
Color.ORANGE,
new ColorAssets(Color.ORANGE),
Color.PINK,
new ColorAssets(Color.PINK),
Color.RED,
new ColorAssets(Color.RED),
Color.YELLOW,
new ColorAssets(Color.YELLOW));
"blue", new ColorAssets(Color.BLUE),
"green", new ColorAssets(Color.GREEN),
"orange", new ColorAssets(Color.ORANGE),
"pink", new ColorAssets(Color.PINK),
"red", new ColorAssets(Color.RED),
"yellow", new ColorAssets(Color.YELLOW));
}

public static final class ColorAssets {

private static final NavigableMap<Double, String> NORMAL = new TreeMap<>();
private static final NavigableMap<Double, String> SHIELD = new TreeMap<>();
private static final NavigableMap<Double, String> STRONG = new TreeMap<>();
private static final NavigableMap<Double, String> KEY = new TreeMap<>();
private static final NavigableMap<Double, String> EXPLODING = new TreeMap<>();

private final Color color;

Expand All @@ -127,9 +128,16 @@ private ColorAssets(Color color) {
SHIELD.put(0.0, "/shield.png");

// Strong
STRONG.put(2.0 / 3, "/strong.png");
STRONG.put(1.0 / 3, "/strongFirstHit.png");
STRONG.put(0.0, "/strongSecondHit.png");
STRONG.put(1.0, "/strong.png");
STRONG.put(2.0 / 3, "/strongFirstHit.png");
STRONG.put(1.0 / 3, "/strongSecondHit.png");
STRONG.put(0.0, "/strongThirdHit.png");

// Key Brick
KEY.put(0.0, "/keybrick.png");

// Exploding
EXPLODING.put(0.0, "/explode.png");
}

public String getColorName() {
Expand Down Expand Up @@ -159,6 +167,8 @@ public String getTexture(BrickType brickType, double hpPercent) {
case NORMAL -> NORMAL;
case SHIELD -> SHIELD;
case STRONG -> STRONG;
case KEY -> KEY;
case EXPLODING -> EXPLODING; // fallback dùng texture thường
};
return getRoot() + map.floorEntry(hpPercent).getValue();
}
Expand All @@ -182,7 +192,7 @@ public int hashCode() {

@Override
public String toString() {
return "ColorAssets[" + "color=" + color + ']';
return "ColorAssets[color=" + color + ']';
}
}
}
Expand Down
25 changes: 18 additions & 7 deletions src/main/java/com/github/codestorm/bounceverse/Bounceverse.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import com.almasb.fxgl.app.GameApplication;
import com.almasb.fxgl.app.GameSettings;
import com.almasb.fxgl.dsl.FXGL;
import com.github.codestorm.bounceverse.components.properties.powerup.PowerUpManager;
import com.github.codestorm.bounceverse.systems.init.AppEventSystem;
import com.github.codestorm.bounceverse.systems.init.GameSystem;
import com.github.codestorm.bounceverse.systems.init.InputSystem;
Expand All @@ -12,6 +14,8 @@
import com.github.codestorm.bounceverse.systems.manager.settings.UserSettingsManager;
import com.github.codestorm.bounceverse.typing.exceptions.BounceverseException;

import javafx.util.Duration;

import java.io.IOException;
import java.util.Map;

Expand All @@ -20,13 +24,10 @@
*
* <h1>{@link Bounceverse}</h1>
*
* Phần Hệ thống Chương trình chính của game, nơi mà mọi thứ bắt đầu từ {@link #main(String[])}...
* <br>
* <i>Game {@link Bounceverse} được lấy cảm hứng từ game Arkanoid nổi tiếng, nơi người chơi điều
* khiển một thanh để đỡ bóng và phá vỡ các viên gạch. Mục tiêu của game là phá vỡ tất cả các viên
* gạch và dành được điểm số cao nhất. Nhưng liệu mọi thứ chỉ đơn giản như vậy?</i>
* Game chính — quản lý vòng đời khởi tạo và vòng lặp của trò chơi.
*/
public final class Bounceverse extends GameApplication {

public static void main(String[] args) {
LaunchOptionsManager.getInstance().load(args);
launch(args);
Expand Down Expand Up @@ -54,12 +55,14 @@ protected void onPreInit() {

@Override
protected void initGameVars(Map<String, Object> vars) {
GameSystem.Variables.loadDefault(vars);
GameSystem.Variables.loadDefault(FXGL.getWorldProperties());
}

@Override
protected void initGame() {
GameSystem.getInstance().apply();
GameSystem.UI.getInstance().dispose();
UISystem.getInstance().dispose();
FXGL.runOnce(() -> GameSystem.getInstance().apply(), Duration.seconds(0.1));
}

@Override
Expand All @@ -69,6 +72,14 @@ protected void initPhysics() {

@Override
protected void initUI() {
FXGL.getGameScene().setBackgroundColor(javafx.scene.paint.Color.web("#0d0b1a"));
UISystem.getInstance().apply();
FXGL.getGameScene().getRoot().getStylesheets().add("assets/ui/powerup.css");
}

@Override
protected void onUpdate(double tpf) {
PowerUpManager.getInstance().onUpdate(tpf);
GameSystem.UI.getInstance().onUpdate();
}
}
101 changes: 22 additions & 79 deletions src/main/java/com/github/codestorm/bounceverse/Utilities.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
import com.github.codestorm.bounceverse.typing.enums.EntityType;

import javafx.geometry.Rectangle2D;
import javafx.scene.shape.Circle;

import java.io.*;
import java.util.*;
Expand All @@ -20,15 +19,9 @@ private Utilities() {}

/** Input/Output utilities. */
public static final class IO {
// ... (Nội dung của lớp IO giữ nguyên, không cần thay đổi)
private IO() {}

/**
* Load .properties file.
*
* @param path Relative path
* @return Parsed properties
* @throws IOException if an error occurred when reading from the input stream.
*/
public static Properties loadProperties(String path) throws IOException {
var fileStream = IO.class.getResourceAsStream(path);
if (fileStream == null) {
Expand All @@ -41,24 +34,10 @@ public static Properties loadProperties(String path) throws IOException {
return prop;
}

/**
* Convert an array of key=value pairs into a hashmap. The string "key=" maps key onto "",
* while just "key" maps key onto null. The value may contain '=' characters, only the first
* "=" is a delimiter. <br>
* Source code from <a href="https://stackoverflow.com/a/52940215/16410937">here</a>.
*
* @param args command-line arguments in the key=value format (or just key= or key)
* @param defaults a map of default values, may be null. Mappings to null are not copied to
* the resulting map.
* @param whiteList if not null, the keys not present in this map cause an exception (and
* keys mapped to null are ok)
* @return a map that maps these keys onto the corresponding values.
*/
public static HashMap<String, String> parseArgs(
String[] args,
HashMap<String, String> defaults,
HashMap<String, String> whiteList) {
// HashMap allows null values
var res = new HashMap<String, String>();
if (defaults != null) {
for (var e : defaults.entrySet()) {
Expand All @@ -77,13 +56,6 @@ public static HashMap<String, String> parseArgs(
return res;
}

/**
* Read text file (txt) and put all lines into {@link List}.
*
* @param path File path
* @return All lines in text file
* @throws FileNotFoundException if an I/O error occurs.
*/
public static List<String> readTextFile(String path) throws IOException {
final var res = new ArrayList<String>();
final var stream = IO.class.getResourceAsStream(path);
Expand All @@ -103,20 +75,6 @@ public static List<String> readTextFile(String path) throws IOException {
public static final class Geometric {
private Geometric() {}

/**
* Lọc các Entity trong phạm vi Hình tròn.
*
* @param circle Hình tròn
* @return Các entity
*/
public static List<Entity> getEntityInCircle(Circle circle) {
final var cx = circle.getCenterX();
final var cy = circle.getCenterY();
final var radius = circle.getRadius();

return getEntityInCircle(cx, cy, radius);
}

/**
* Lọc các Entity trong phạm vi Hình tròn.
*
Expand All @@ -140,20 +98,35 @@ public static List<Entity> getEntityInCircle(double cx, double cy, double radius
})
.toList();
}

/**
* Lọc các Entity trong phạm vi Hình chữ nhật.
*
* @param centerX Tâm X của hình chữ nhật
* @param centerY Tâm Y của hình chữ nhật
* @param width Chiều rộng của hình chữ nhật
* @param height Chiều cao của hình chữ nhật
* @return Danh sách các entity nằm trong khu vực đó
*/
public static List<Entity> getEntitiesInRectangle(
double centerX, double centerY, double width, double height) {
var topLeftX = centerX - width / 2;
var topLeftY = centerY - height / 2;
var explosionArea = new Rectangle2D(topLeftX, topLeftY, width, height);
return FXGL.getGameWorld().getEntitiesInRange(explosionArea);
}
}

// ... (Các lớp Collision, Compatibility, Typing giữ nguyên, không cần thay đổi)
public static final class Collision {
private Collision() {}

public static DirectionUnit getCollisionDirection(Entity source, Entity target) {
var fromBox = source.getBoundingBoxComponent();
var toBox = target.getBoundingBoxComponent();

var fCenter = fromBox.getCenterWorld();
var tCenter = toBox.getCenterWorld();

var direction = tCenter.subtract(fCenter);

return Math.abs(direction.getX()) > Math.abs(direction.getY())
? direction.getX() > 0 ? DirectionUnit.RIGHT : DirectionUnit.LEFT
: direction.getY() > 0 ? DirectionUnit.DOWN : DirectionUnit.UP;
Expand All @@ -163,20 +136,13 @@ public static DirectionUnit getCollisionDirection(Entity source, Entity target)
public static final class Compatibility {
private Compatibility() {}

/**
* Throw {@link IllegalArgumentException} nếu như có component trong {@code params} không
* phù hợp với {@code entityType}.
*
* @param entityType {@link EntityType} muốn kiểm tra tương thích
* @param params Các component cần kiểm tra
*/
public static void throwIfNotCompatible(EntityType entityType, Component... params) {
for (var param : params) {
public static void throwIfNotCompatible(
EntityType entityType, com.almasb.fxgl.entity.component.Component... components) {
for (var param : components) {
final var annotation = param.getClass().getAnnotation(OnlyForEntity.class);
if (annotation == null) {
continue;
}

final var paramEntityTypeSet = EnumSet.copyOf(Arrays.asList(annotation.value()));
if (!paramEntityTypeSet.contains(entityType)) {
throw new IllegalArgumentException(
Expand All @@ -188,13 +154,6 @@ public static void throwIfNotCompatible(EntityType entityType, Component... para
}
}

/**
* {@link #throwIfNotCompatible(EntityType, Component...)} nhưng không throw exception.
*
* @param entityType {@link EntityType} muốn kiểm tra tương thích
* @param params Các component cần kiểm tra
* @return {@code true} nếu tất cả tương thích, ngược lại {@code false}.
*/
public static boolean isCompatible(EntityType entityType, Component... params) {
try {
throwIfNotCompatible(entityType, params);
Expand All @@ -208,29 +167,13 @@ public static boolean isCompatible(EntityType entityType, Component... params) {
public static final class Typing {
private Typing() {}

/**
* Xử lý nhanh lấy data từ {@link SpawnData}.
*
* @param data Dữ liệu cần lấy
* @param key Khóa cần lấy
* @param ifNot Nếu không có thì trả về cái này
* @return Giá trị
* @param <T> Kiểu của Giá trị
*/
public static <T> T getOr(SpawnData data, String key, T ifNot) {
if (data.hasKey(key)) {
return data.get(key);
}
return ifNot;
}

/**
* Hợp nhanh varargs sang Array.
*
* @param varargs Varargs
* @return Array tương ứng
* @param <T> Kiểu của Giá trị
*/
@SafeVarargs
public static <T> T[] toArray(T... varargs) {
return varargs;
Expand Down
Loading
Loading