Skip to content
This repository was archived by the owner on Dec 24, 2025. It is now read-only.

Commit f762f72

Browse files
committed
feat(powerup): introduce PowerUp and these components
Added PowerUpContainer to store components within an entity without activating their logic. Implemented PowerUpFactory to create power-up entities in the game. This enhances the game's functionality by allowing dynamic power-up management.
1 parent 64fce02 commit f762f72

11 files changed

Lines changed: 209 additions & 38 deletions

File tree

build.gradle

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,7 @@ dependencies {
2323
implementation 'com.github.almasb:fxgl:21.1'
2424
implementation 'com.google.guava:guava:33.5.0-jre'
2525
implementation 'com.moandjiezana.toml:toml4j:0.7.2'
26-
implementation 'com.google.auto.service:auto-service-annotations:1.1.1'
27-
annotationProcessor 'com.google.auto.service:auto-service:1.1.1'
26+
implementation 'com.google.guava:guava:33.5.0-jre'
2827
}
2928

3029
application {

src/main/java/com/github/codestorm/bounceverse/Utilities.java

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -291,16 +291,17 @@ public static final class Compatibility {
291291
public static void throwIfNotCompatible(EntityType onlyFor, Component... params) {
292292
for (var param : params) {
293293
final var annotation = param.getClass().getAnnotation(ForEntity.class);
294-
if (annotation != null) {
295-
final var paramSet = EnumSet.copyOf(Arrays.asList(annotation.value()));
296-
if (paramSet.isEmpty() || paramSet.contains(onlyFor)) {
297-
continue;
298-
}
294+
if (annotation == null) {
295+
continue;
296+
}
297+
298+
final var paramEntityTypeSet = EnumSet.copyOf(Arrays.asList(annotation.value()));
299+
if (!paramEntityTypeSet.contains(onlyFor)) {
300+
throw new IllegalArgumentException(
301+
String.format(
302+
"Class '%s' does not compatible for entity has '%s' type.",
303+
param.getClass().getSimpleName(), onlyFor.name()));
299304
}
300-
throw new IllegalArgumentException(
301-
String.format(
302-
"Class '%s' does not compatible for entity has '%s' type.",
303-
param.getClass().getSimpleName(), onlyFor.name()));
304305
}
305306
}
306307

src/main/java/com/github/codestorm/bounceverse/components/behaviors/ScaleChange.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import com.github.codestorm.bounceverse.typing.annotations.ForEntity;
66
import java.util.ArrayList;
77
import java.util.List;
8+
import javafx.util.Duration;
89

910
/**
1011
*
@@ -61,4 +62,8 @@ public double getScaleHeight() {
6162
public void setScaleHeight(double scaleHeight) {
6263
this.scaleHeight = scaleHeight;
6364
}
65+
66+
public ScaleChange(Duration duration) {
67+
super(duration, true);
68+
}
6469
}

src/main/java/com/github/codestorm/bounceverse/components/behaviors/UndoableBehavior.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
* @see CanUndo
1717
*/
1818
public abstract class UndoableBehavior extends Behavior implements CanUndo {
19+
protected boolean removeWhenUndo = true;
1920
protected Duration duration = Duration.INDEFINITE;
2021
private List<Object> modified = null;
2122
private TimerAction current;
@@ -67,14 +68,39 @@ public final void undo() {
6768
if (undoLogic(modified)) {
6869
current = null;
6970
modified = null;
71+
if (removeWhenUndo) {
72+
entity.removeComponent(this.getClass());
73+
}
7074
}
7175
}
7276

77+
@Override
78+
public void onRemoved() {
79+
undo();
80+
}
81+
7382
public Duration getDuration() {
7483
return duration;
7584
}
7685

7786
public void setDuration(Duration duration) {
7887
this.duration = duration;
7988
}
89+
90+
public boolean isRemoveWhenUndo() {
91+
return removeWhenUndo;
92+
}
93+
94+
public void setRemoveWhenUndo(boolean removeWhenUndo) {
95+
this.removeWhenUndo = removeWhenUndo;
96+
}
97+
98+
public UndoableBehavior(Duration duration) {
99+
this.duration = duration;
100+
}
101+
102+
public UndoableBehavior(Duration duration, boolean removeWhenUndo) {
103+
this(duration);
104+
this.removeWhenUndo = removeWhenUndo;
105+
}
80106
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package com.github.codestorm.bounceverse.components.properties.powerup;
2+
3+
import com.almasb.fxgl.entity.Entity;
4+
import com.almasb.fxgl.entity.component.Component;
5+
import com.almasb.fxgl.entity.component.CoreComponent;
6+
import com.github.codestorm.bounceverse.Utilities;
7+
import com.github.codestorm.bounceverse.components.properties.Property;
8+
import com.github.codestorm.bounceverse.typing.annotations.ForEntity;
9+
import com.github.codestorm.bounceverse.typing.enums.EntityType;
10+
import com.google.common.collect.MutableClassToInstanceMap;
11+
import java.util.Map;
12+
import org.jetbrains.annotations.NotNull;
13+
14+
/**
15+
*
16+
*
17+
* <h1>{@link PowerUpContainer}</h1>
18+
*
19+
* Giống như một cái túi, nơi lưu trữ {@link Component} bên trong {@link Entity} và không kích hoạt
20+
* logic của các component đó.
21+
*/
22+
@ForEntity({EntityType.POWER_UP})
23+
@CoreComponent
24+
public final class PowerUpContainer extends Property {
25+
private MutableClassToInstanceMap<Component> container = MutableClassToInstanceMap.create();
26+
27+
/**
28+
* Gán trực tiếp các {@link Component} lên trên {@link Entity}.
29+
*
30+
* @param entity Entity.
31+
*/
32+
public void addTo(Entity entity) {
33+
for (var entry : container.entrySet()) {
34+
final var component = entry.getValue();
35+
// TODO: Gán việc kiểm tra trên chính Entity/Component thay vì thủ tục ntn
36+
Utilities.Compatibility.throwIfNotCompatible((EntityType) entity.getType(), component);
37+
entity.addComponent(entry.getValue());
38+
}
39+
}
40+
41+
/**
42+
* Thêm các {@link Component} vào {@link #container}.
43+
*
44+
* @param components Các component
45+
* @see MutableClassToInstanceMap#putAll(Map)
46+
*/
47+
public void put(@NotNull Component... components) {
48+
for (var component : components) {
49+
container.put(component.getClass(), component);
50+
}
51+
}
52+
53+
public PowerUpContainer(Component... components) {
54+
put(components);
55+
}
56+
57+
public MutableClassToInstanceMap<Component> getContainer() {
58+
return container;
59+
}
60+
61+
public void setContainer(MutableClassToInstanceMap<Component> container) {
62+
this.container = container;
63+
}
64+
}

src/main/java/com/github/codestorm/bounceverse/core/systems/GameSystem.java

Lines changed: 24 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import com.almasb.fxgl.dsl.FXGL;
44
import com.almasb.fxgl.entity.EntityFactory;
55
import com.github.codestorm.bounceverse.factory.entities.*;
6+
import org.jetbrains.annotations.NotNull;
67

78
public final class GameSystem extends System {
89
private GameSystem() {}
@@ -11,7 +12,7 @@ public static GameSystem getInstance() {
1112
return GameSystem.Holder.INSTANCE;
1213
}
1314

14-
private static void addFactory(EntityFactory... factories) {
15+
private static void addFactory(@NotNull EntityFactory... factories) {
1516
for (var factory : factories) {
1617
FXGL.getGameWorld().addEntityFactory(factory);
1718
}
@@ -24,34 +25,41 @@ private static void spawnWalls() {
2425
FXGL.spawn("wallRight");
2526
}
2627

27-
@Override
28-
public void apply() {
29-
addFactory(
30-
new WallFactory(),
31-
new BrickFactory(),
32-
new BulletFactory(),
33-
new PaddleFactory(),
34-
new BallFactory());
35-
36-
// Wall
37-
spawnWalls();
38-
39-
// Brick
28+
private static void spawnBrick() {
4029
for (int y = 1; y <= 6; y++) {
4130
for (int x = 1; x <= 10; x++) {
4231
FXGL.spawn("normalBrick", 85 * x, 35 * y);
4332
}
4433
}
34+
}
4535

46-
// Paddle
36+
private static void spawnPaddle() {
4737
double px = FXGL.getAppWidth() / 2.0 - 60;
4838
double py = FXGL.getAppHeight() - 40;
4939
FXGL.spawn("paddle", px, py);
40+
}
5041

51-
// Ball
42+
private static void spawnBall() {
5243
FXGL.spawn("ball");
5344
}
5445

46+
@Override
47+
public void apply() {
48+
final var gameWorld = FXGL.getGameWorld();
49+
50+
addFactory(
51+
new WallFactory(),
52+
new BrickFactory(),
53+
new BulletFactory(),
54+
new PaddleFactory(),
55+
new BallFactory());
56+
57+
spawnWalls();
58+
spawnBrick();
59+
spawnPaddle();
60+
spawnBall();
61+
}
62+
5563
/**
5664
* Lazy-loaded singleton holder. <br>
5765
* Follow <a href= "https://en.wikipedia.org/wiki/Initialization-on-demand_holder_idiom">

src/main/java/com/github/codestorm/bounceverse/core/systems/InputSystem.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
* {@link System} quản lý Input trong game. <br>
1515
* <i>Đây là một Singleton, cần lấy instance thông qua {@link #getInstance()}</i>.
1616
*/
17-
public class InputSystem extends System {
17+
public final class InputSystem extends System {
1818
public static InputSystem getInstance() {
1919
return InputSystem.Holder.INSTANCE;
2020
}

src/main/java/com/github/codestorm/bounceverse/factory/entities/BrickFactory.java

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,10 @@
1212
import com.almasb.fxgl.physics.box2d.dynamics.FixtureDef;
1313
import com.github.codestorm.bounceverse.Utilities;
1414
import com.github.codestorm.bounceverse.components.behaviors.HealthDeath;
15-
import com.github.codestorm.bounceverse.components.behaviors.paddle.PaddleShooting;
1615
import com.github.codestorm.bounceverse.typing.enums.EntityType;
1716
import javafx.geometry.Point2D;
1817
import javafx.scene.paint.Color;
1918
import javafx.scene.shape.Rectangle;
20-
import javafx.util.Duration;
2119
import org.jetbrains.annotations.NotNull;
2220

2321
/**
@@ -87,7 +85,6 @@ public final class BrickFactory implements EntityFactory {
8785
*/
8886
@NotNull @Spawns("normalBrick")
8987
public static Entity newNormalBrick(SpawnData pos) {
90-
return newBrick(
91-
new Point2D(pos.getX(), pos.getY()), DEFAULT_HP, new PaddleShooting(Duration.ONE));
88+
return newBrick(new Point2D(pos.getX(), pos.getY()), DEFAULT_HP);
9289
}
9390
}

src/main/java/com/github/codestorm/bounceverse/factory/entities/PaddleFactory.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
import com.almasb.fxgl.entity.EntityFactory;
66
import com.almasb.fxgl.entity.SpawnData;
77
import com.almasb.fxgl.entity.Spawns;
8-
import com.github.codestorm.bounceverse.components.behaviors.ScaleChange;
98
import com.github.codestorm.bounceverse.components.behaviors.paddle.PaddleShooting;
109
import com.github.codestorm.bounceverse.typing.enums.EntityType;
1110
import javafx.scene.paint.Color;
@@ -38,8 +37,8 @@ public Entity newPaddle(SpawnData data) {
3837
.type(EntityType.PADDLE)
3938
.viewWithBBox(view)
4039
.collidable()
41-
.with(new ScaleChange(), new PaddleShooting(DEFAULT_SHOOT_COOLDOWN))
40+
.with(new PaddleShooting(DEFAULT_SHOOT_COOLDOWN))
4241
.build();
43-
// TODO: Thêm điều chỉnh tốc độ
42+
// TODO: Thêm Dashing
4443
}
4544
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package com.github.codestorm.bounceverse.factory.entities;
2+
3+
import com.almasb.fxgl.dsl.FXGL;
4+
import com.almasb.fxgl.entity.Entity;
5+
import com.almasb.fxgl.entity.EntityFactory;
6+
import com.almasb.fxgl.entity.component.Component;
7+
import com.almasb.fxgl.physics.BoundingShape;
8+
import com.almasb.fxgl.physics.HitBox;
9+
import com.almasb.fxgl.physics.PhysicsComponent;
10+
import com.almasb.fxgl.texture.Texture;
11+
import com.github.codestorm.bounceverse.components.properties.powerup.PowerUpContainer;
12+
import com.github.codestorm.bounceverse.typing.enums.DirectionUnit;
13+
import com.github.codestorm.bounceverse.typing.enums.EntityType;
14+
import javafx.geometry.Point2D;
15+
import org.jetbrains.annotations.NotNull;
16+
17+
/**
18+
*
19+
*
20+
* <h1>{@link PowerUpFactory}</h1>
21+
*
22+
* Factory để tạo các entity loại {@link EntityType#POWER_UP} trong trò chơi.
23+
*
24+
* @see EntityFactory
25+
*/
26+
public final class PowerUpFactory implements EntityFactory {
27+
public static final double DEFAULT_RADIUS = 10;
28+
public static final double DEFAULT_SPEED = 10;
29+
30+
/**
31+
* Tạo mới một PowerUp. Đây là một "abstract" method.
32+
*
33+
* @param pos Vị trí
34+
* @param has Những gì PowerUp này sẽ cung cấp
35+
* @return Entity PowerUp
36+
*/
37+
private Entity newPowerUp(Point2D pos, Texture texture, Component... has) {
38+
final var hitbox = new HitBox(BoundingShape.circle(DEFAULT_RADIUS));
39+
40+
return FXGL.entityBuilder()
41+
.type(EntityType.POWER_UP)
42+
.bbox(hitbox)
43+
.at(pos)
44+
.view(texture)
45+
.collidable()
46+
.with(getPhysicsComponent(), new PowerUpContainer(has))
47+
.buildAndAttach();
48+
}
49+
50+
@NotNull private static PhysicsComponent getPhysicsComponent() {
51+
final var velocity = DirectionUnit.DOWN.getVector().mul(DEFAULT_SPEED);
52+
53+
var physics = new PhysicsComponent();
54+
physics.setOnPhysicsInitialized(
55+
() -> {
56+
physics.setLinearVelocity(velocity.toPoint2D());
57+
physics.setAngularVelocity(0);
58+
physics.getBody().setFixedRotation(true);
59+
physics.getBody().setLinearDamping(0f);
60+
physics.getBody().setAngularDamping(0f);
61+
});
62+
return physics;
63+
}
64+
}

0 commit comments

Comments
 (0)