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
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
10 changes: 5 additions & 5 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ target/

### IntelliJ IDEA ###
.idea/
!.idea/misc.xml
*.iws
*.iml
*.ipr
Expand Down Expand Up @@ -43,14 +44,13 @@ bin/

### FXGL
.fxgl/
system/
save/
log.txt
/save/
/logs/
/system/
*.sav
*.dat
*.bin
**/cache/
**/prefs/

### Workspace
/logs/
### Workspace
2 changes: 1 addition & 1 deletion .run/run.run.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<option name="executionName" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="externalSystemIdString" value="GRADLE" />
<option name="scriptParameters" value="" />
<option name="scriptParameters" value="--info" />
<option name="taskDescriptions">
<list />
</option>
Expand Down
77 changes: 73 additions & 4 deletions src/main/java/com/github/codestorm/bounceverse/Bounceverse.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
package com.github.codestorm.bounceverse;

import com.almasb.fxgl.app.ApplicationMode;
import com.almasb.fxgl.app.GameApplication;
import com.almasb.fxgl.app.GameSettings;
import com.almasb.fxgl.dsl.FXGL;
import com.github.codestorm.bounceverse.factory.BrickFactory;
import com.github.codestorm.bounceverse.factory.SceneFactory;
import com.github.codestorm.bounceverse.systems.LaunchOption;
import com.github.codestorm.bounceverse.systems.physics.CollisionSystem;
import java.io.IOException;
import java.util.Properties;
import javafx.scene.paint.Color;

/**
Expand All @@ -18,13 +24,70 @@
* 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>
*/
public final class Bounceverse extends GameApplication {
public static final String name = "Bounceverse";
private static LaunchOption launchOption;

/**
* Cấu hình game.
*
* <p>Sử dụng {@link #loadConfigs()} để load các config.
*/
private static final class Configs {
private static final String ROOT = "/configs/";

/** Cấu hình game bên trong hệ thống game. */
private static final class System {
public static Properties settings;

private System() {}
}

/** Cấu hình game bên ngoài hệ thống game. */
private static final class Options {
public static Properties DEFAULT; // Cấu hình mặc định của trò chơi

private Options() {}
}

/**
* Load game configs.
*
* @throws IOException if an error occurred when reading from the input stream.
*/
public static void loadConfigs() throws IOException {
Options.DEFAULT = Utils.IO.loadProperties(ROOT + "default.properties");
System.settings = Utils.IO.loadProperties(ROOT + "system/settings.properties");
}

private Configs() {}
}

@Override
protected void initSettings(GameSettings settings) {
settings.setWidth(900);
settings.setHeight(600);
settings.setTitle(name);
try {
Configs.loadConfigs();
} catch (IOException e) {
throw new RuntimeException(e);
}
// Basic
settings.setTitle(Configs.System.settings.getProperty("settings.name"));
settings.setVersion(Configs.System.settings.getProperty("settings.version"));
settings.setCredits(Utils.IO.readTextFile("credits.txt"));
Copy link

Copilot AI Oct 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will fail because readTextFile has a bug where it passes the path string directly to Scanner constructor. The path should be prefixed with '/' to read from resources: Utils.IO.readTextFile(\"/credits.txt\").

Suggested change
settings.setCredits(Utils.IO.readTextFile("credits.txt"));
settings.setCredits(Utils.IO.readTextFile("/credits.txt"));

Copilot uses AI. Check for mistakes.
settings.setApplicationMode(
Boolean.parseBoolean(Configs.System.settings.getProperty("settings.devMode"))
? ApplicationMode.DEVELOPER
: (launchOption.isDebug())
? ApplicationMode.DEBUG
: ApplicationMode.RELEASE);

// Display
settings.setWidth(Integer.parseInt(Configs.Options.DEFAULT.getProperty("width")));
settings.setHeight(Integer.parseInt(Configs.Options.DEFAULT.getProperty("height")));
Comment on lines +83 to +84
Copy link

Copilot AI Oct 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These lines will throw NullPointerException because Configs.Options.DEFAULT is never loaded in loadConfigs() method. The default.properties file is loaded but the file doesn't exist in the diff.

Copilot uses AI. Check for mistakes.
settings.setFullScreenAllowed(true);

// In-app
settings.setSceneFactory(new SceneFactory());
settings.setMainMenuEnabled(true);
settings.setIntroEnabled(true);
}

@Override
Expand All @@ -35,12 +98,18 @@ protected void initGame() {
var brick2 = FXGL.spawn("normalBrick", 200, 200);
}

@Override
protected void initPhysics() {
CollisionSystem.getInstance().apply();
}

@Override
protected void initUI() {
FXGL.getGameScene().setBackgroundColor(Color.web("#2B2B2B"));
}

public static void main(String[] args) {
launchOption = new LaunchOption(args);
launch(args);
}
}
87 changes: 87 additions & 0 deletions src/main/java/com/github/codestorm/bounceverse/Utils.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package com.github.codestorm.bounceverse;

import java.io.IOException;
import java.io.InputStream;
import java.util.*;

/** Utilities. */
public final class Utils {
/** Input/Output utilities. */
public static final class 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 {
InputStream fileStream = IO.class.getResourceAsStream(path);
if (fileStream == null) {
throw new IOException("Cannot open InputStream in" + path);
}

Properties prop = new Properties();
prop.load(fileStream);
fileStream.close();
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.
*
* <p>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
HashMap<String, String> res = new HashMap<>();
if (defaults != null) {
for (Map.Entry<String, String> e : defaults.entrySet()) {
if (e.getValue() != null) {
res.put(e.getKey(), e.getValue());
}
}
}
for (String s : args) {
String[] kv = s.split("=", 2);
if (whiteList != null && !whiteList.containsKey(kv[0])) {
continue;
}
res.put(kv[0], kv.length < 2 ? null : kv[1]);
}
return res;
}

/**
* Read text file (txt) and put all lines into {@link List}.
*
* @param path File path
* @return All lines in text file
*/
public static List<String> readTextFile(String path) {
var res = new ArrayList<String>();
var scanner = new Scanner(path);
while (scanner.hasNext()) {
res.add(scanner.next());
}
Comment on lines +75 to +78
Copy link

Copilot AI Oct 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Scanner constructor is being passed a String path instead of an InputStream. This will cause the Scanner to interpret the path as literal text to scan rather than opening a file. Use Utils.class.getResourceAsStream(path) to get an InputStream.

Suggested change
var scanner = new Scanner(path);
while (scanner.hasNext()) {
res.add(scanner.next());
}
InputStream fileStream = IO.class.getResourceAsStream(path);
if (fileStream == null) {
throw new RuntimeException("Cannot open InputStream in " + path);
}
var scanner = new Scanner(fileStream);
while (scanner.hasNextLine()) {
res.add(scanner.nextLine());
}

Copilot uses AI. Check for mistakes.
scanner.close();
return res;
}

private IO() {}
}

private Utils() {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.github.codestorm.bounceverse.factory;

import com.almasb.fxgl.app.scene.FXGLMenu;
import com.github.codestorm.bounceverse.scenes.MainMenu;
import org.jetbrains.annotations.NotNull;

public class SceneFactory extends com.almasb.fxgl.app.scene.SceneFactory {
@NotNull @Override
public FXGLMenu newMainMenu() {
return new MainMenu();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.github.codestorm.bounceverse.scenes;

import com.almasb.fxgl.app.scene.FXGLDefaultMenu;
import com.almasb.fxgl.app.scene.MenuType;

public class MainMenu extends FXGLDefaultMenu {
public MainMenu() {
super(MenuType.MAIN_MENU);
// TODO: Customize Main Menu
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.github.codestorm.bounceverse.systems;

import com.github.codestorm.bounceverse.Utils;

/**
*
*
* <h1>{@link LaunchOption}</h1>
*
* Các tùy chọn khởi động được áp dụng trong game.
*/
public final class LaunchOption {
private boolean debug = false;

public boolean isDebug() {
return debug;
}

public LaunchOption(String... args) {
var map = Utils.IO.parseArgs(args, null, null);
debug = Boolean.parseBoolean(map.getOrDefault("debug", "false"));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.github.codestorm.bounceverse.systems;

/**
*
*
* <h1>{@link System}</h1>
*
* Hệ thống logic trong game. <b>Các lớp kế thừa nên thiết kế dựa trên (lazy-loaded) Singleton.</b>
*
* <p>Tất cả logic của hệ thống được áp dụng thông qua {@link #apply()}.
*/
public abstract class System {
/**
* Áp dụng logic của hệ thống vào game.
*
* <p>Sử dụng trên {@link com.github.codestorm.bounceverse.Bounceverse}
*/
public abstract void apply();

protected System() {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package com.github.codestorm.bounceverse.systems.physics;

import com.github.codestorm.bounceverse.systems.System;

/**
*
*
* <h1>{@link CollisionSystem}</h1>
*
* Hệ thống xử lý va chạm.
*
* <p><i>Đây là một Singleton, cần lấy instance thông qua {@link #getInstance()}</i>.
*
* @see System
*/
public final class CollisionSystem extends System {
/**
* Lazy-loaded singleton holder.
*
* <p>Follow <a href="https://en.wikipedia.org/wiki/Initialization-on-demand_holder_idiom">
* Initialization-on-demand holder idiom</a>.
*/
private static final class Holder {
static final CollisionSystem INSTANCE = new CollisionSystem();
}

public static CollisionSystem getInstance() {
return Holder.INSTANCE;
}

// ? Tạo các Group CollisionHandler ở đây (dùng composition)

@Override
public void apply() {
// ? Viết các logic CollisionHandler ở đây. eg:
// getPhysicsWorld().addCollisionHandler(new CollisionHandler(EntityType.PLAYER,
// EntityType.COIN) {
// @Override
// protected void onCollisionBegin(Entity player, Entity coin) {
// coin.removeFromWorld();
// }
// });
}

private CollisionSystem() {}
}
2 changes: 2 additions & 0 deletions src/main/resources/configs/default.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
width=1024
height=768
4 changes: 4 additions & 0 deletions src/main/resources/configs/system/settings.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# settings
settings.name=Bounceverse
settings.version=1.0.0
settings.devMode=true
13 changes: 13 additions & 0 deletions src/main/resources/credits.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
- Game -

CodeStorm team

Leader:
Mai Thành (@thnhmai06)

Members:
Mạnh Tân (@ManhTanTran)
Minh Ngọc (@minngoc1213)
Anh Tuấn (@huynhtuan372)

-o0o-
Loading