diff --git a/game/src/main/java/net/onelitefeather/cygnus/Cygnus.java b/game/src/main/java/net/onelitefeather/cygnus/Cygnus.java
index d9370eef..5dd34382 100644
--- a/game/src/main/java/net/onelitefeather/cygnus/Cygnus.java
+++ b/game/src/main/java/net/onelitefeather/cygnus/Cygnus.java
@@ -92,14 +92,14 @@ public Cygnus() {
Path path = Paths.get("");
this.teamService = TeamService.of();
this.linearPhaseSeries = new LinearPhaseSeries<>("game");
- this.ambientProvider = new AmbientProvider();
+ this.ambientProvider = new AmbientProvider(this.teamService.getTeams().get(Helper.SLENDER_ID));
this.staminaService = new StaminaService();
this.gameConfig = new GameConfigReader(path).getConfig();
MinecraftServer.getConnectionManager().setPlayerProvider(CygnusPlayer::new);
this.pageProvider = new PageProvider();
this.mapProvider = new GameMapProvider(path);
this.view = new GameViewImpl();
- this.createTeams(this.gameConfig, this.teamService, this.ambientProvider);
+ this.createTeams(this.gameConfig, this.teamService);
this.initPhases();
this.initCommands();
this.initListener();
diff --git a/game/src/main/java/net/onelitefeather/cygnus/TeamCreator.java b/game/src/main/java/net/onelitefeather/cygnus/TeamCreator.java
index 50efdcbe..d18df769 100644
--- a/game/src/main/java/net/onelitefeather/cygnus/TeamCreator.java
+++ b/game/src/main/java/net/onelitefeather/cygnus/TeamCreator.java
@@ -6,7 +6,6 @@
import net.theevilreaper.xerus.api.component.team.ColorComponent;
import net.theevilreaper.xerus.api.team.Team;
import net.theevilreaper.xerus.api.team.TeamService;
-import net.onelitefeather.cygnus.ambient.AmbientProvider;
import net.onelitefeather.cygnus.common.config.GameConfig;
import org.jetbrains.annotations.NotNull;
@@ -26,12 +25,10 @@ public interface TeamCreator {
*
* @param gameConfig the configuration to get some values from it
* @param teamService the service to add the teams
- * @param ambientProvider the provider to set the ambient team
*/
default void createTeams(
@NotNull GameConfig gameConfig,
- @NotNull TeamService teamService,
- @NotNull AmbientProvider ambientProvider
+ @NotNull TeamService teamService
) {
Team slenderTeam = Team.of(
Key.key("cygnus", GameConfig.SLENDER_TEAM_NAME.toLowerCase(Locale.ROOT)),
@@ -49,7 +46,6 @@ default void createTeams(
survivorTeam.add(ColorComponent.class, new ColorComponent(ColorData.LIGHT_GREEN));
survivorTeam.add(TeamNameComponent.class, new TeamNameComponent(GameConfig.SURVIVOR_TEAM_NAME));
teamService.add(survivorTeam);
- ambientProvider.setTeam(survivorTeam);
teamService.add(survivorTeam);
}
}
diff --git a/game/src/main/java/net/onelitefeather/cygnus/ambient/AmbientProvider.java b/game/src/main/java/net/onelitefeather/cygnus/ambient/AmbientProvider.java
index 60e471b0..c760c0ff 100644
--- a/game/src/main/java/net/onelitefeather/cygnus/ambient/AmbientProvider.java
+++ b/game/src/main/java/net/onelitefeather/cygnus/ambient/AmbientProvider.java
@@ -9,80 +9,106 @@
import net.minestom.server.potion.TimedPotion;
import net.minestom.server.sound.SoundEvent;
import net.minestom.server.timer.Task;
-import net.minestom.server.utils.validate.Check;
import net.onelitefeather.cygnus.common.Messages;
-import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
import java.time.temporal.ChronoUnit;
+import java.util.List;
import static net.onelitefeather.cygnus.common.util.Helper.getRandomPitchValue;
/**
+ * Provides ambient effects for a {@link Team} during a game phase.
+ *
+ *
The provider runs a repeating task that plays cave sounds at fixed tick
+ * intervals and applies a blindness effect with additional sounds at tick 77,
+ * simulating a "lights out" scenario. One full cycle spans {@value #CYCLE_LENGTH} ticks.
+ *
+ * Usage:
+ * {@code
+ * AmbientProvider provider = new AmbientProvider();
+ * provider.setTeam(team);
+ * provider.startTask();
+ * // ...
+ * provider.stopTask();
+ * }
+ *
* @author theEvilReaper
- * @version 1.0.0
+ * @version 2.0.0
* @since 1.0.0
- **/
-@SuppressWarnings({"java:S3252"})
+ */
public final class AmbientProvider {
- private static final int MAX_TICKS = 80;
- private Task task;
- private int currentTicks;
- private final TimedPotion potionEffect;
- private final Sound[] sounds;
- private Team team;
+ private static final int CYCLE_LENGTH = 82;
+ private static final TimedPotion POTION_EFFECT =
+ new TimedPotion(new Potion(PotionEffect.BLINDNESS, (byte) 1, 200), 200);
+ private static final Sound[] SOUNDS = {
+ Sound.sound(SoundEvent.AMBIENT_CAVE, Sound.Source.MASTER, 1F, 1F),
+ Sound.sound(SoundEvent.ENTITY_GENERIC_EXPLODE, Sound.Source.MASTER, 1F, 0F),
+ Sound.sound(SoundEvent.BLOCK_FIRE_EXTINGUISH, Sound.Source.MASTER, 1F, 1F),
+ Sound.sound(SoundEvent.ENTITY_WITHER_SPAWN, Sound.Source.MASTER, 5F, 0.7F)
+ };
- public AmbientProvider() {
- this.potionEffect = new TimedPotion(new Potion(PotionEffect.BLINDNESS, (byte) 1 , 200), 200);
- this.sounds = new Sound[4];
- this.sounds[0] = Sound.sound(SoundEvent.AMBIENT_CAVE, Sound.Source.MASTER, 1F, 1F);
- this.sounds[1] = Sound.sound(SoundEvent.ENTITY_GENERIC_EXPLODE, Sound.Source.MASTER, 1F, 0F);
- this.sounds[2] = Sound.sound(SoundEvent.BLOCK_FIRE_EXTINGUISH, Sound.Source.MASTER, 1F, 1F);
- this.sounds[3] = Sound.sound(SoundEvent.ENTITY_WITHER_SPAWN, Sound.Source.MASTER, 5F, 0.7F);
- }
+ private final Team team;
+ private @Nullable Task task;
+ private int currentTicks;
- // TODO: I think we can use here only the set reference from the team instead of the whole team reference
- public void setTeam(@NotNull Team team) {
+ /**
+ * Creates a new instance of the {@link AmbientProvider}.
+ * @param team which is involved
+ */
+ public AmbientProvider(Team team) {
this.team = team;
}
+ /**
+ * Starts the ambient task.
+ */
public void startTask() {
if (task != null) return;
- Check.argCondition(team == null, "The team variable can't be null");
- task = MinecraftServer.getSchedulerManager().buildTask(this::tick).repeat(1, ChronoUnit.SECONDS).schedule();
+ task = MinecraftServer.getSchedulerManager()
+ .buildTask(this::tick)
+ .repeat(1, ChronoUnit.SECONDS)
+ .schedule();
}
+ /**
+ * Stops the ambient task.
+ */
public void stopTask() {
if (task == null) return;
- this.task.cancel();
- this.task = null;
+ task.cancel();
+ task = null;
}
- // TODO: The iteration over the players can be result into a race condition because the players from a team can be changed each time
+ /**
+ * Executes the ambient logic.
+ */
public void tick() {
+ List players = List.copyOf(this.team.getPlayers());
+
switch (currentTicks) {
- case 0, 15, 30, 45, 60:
- for (Player player : this.team.getPlayers()) {
- player.playSound(Sound.sound(SoundEvent.AMBIENT_CAVE, Sound.Source.MASTER, 1F, getRandomPitchValue()), player.getPosition());
+ case 0, 15, 30, 45, 60 -> {
+ Sound caveSound = Sound.sound(SoundEvent.AMBIENT_CAVE,
+ Sound.Source.MASTER, 1F, getRandomPitchValue());
+ for (Player player : players) {
+ player.playSound(caveSound, player.getPosition());
}
- break;
- case 77:
- for (Player player : this.team.getPlayers()) {
- player.addEffect(potionEffect.potion());
- player.playSound(sounds[1], player.getPosition());
- player.playSound(sounds[2], player.getPosition());
- player.playSound(sounds[3], player.getPosition());
+ }
+ case 77 -> {
+ for (Player player : players) {
+ player.addEffect(POTION_EFFECT.potion());
+ player.playSound(SOUNDS[1], player.getPosition());
+ player.playSound(SOUNDS[2], player.getPosition());
+ player.playSound(SOUNDS[3], player.getPosition());
player.sendMessage(Messages.LIGHT_WENT_OUT);
}
- break;
- default: {
- //NOTHING TODO here
+ }
+ default -> {
+ // Nothing to do here
}
}
- if (currentTicks > MAX_TICKS) {
- currentTicks = 0;
- } else {
- currentTicks++;
- }
+
+ currentTicks = (currentTicks + 1) % CYCLE_LENGTH;
}
-}
\ No newline at end of file
+}
diff --git a/game/src/main/java/net/onelitefeather/cygnus/ambient/package-info.java b/game/src/main/java/net/onelitefeather/cygnus/ambient/package-info.java
new file mode 100644
index 00000000..f6d32150
--- /dev/null
+++ b/game/src/main/java/net/onelitefeather/cygnus/ambient/package-info.java
@@ -0,0 +1,4 @@
+@NotNullByDefault
+package net.onelitefeather.cygnus.ambient;
+
+import org.jetbrains.annotations.NotNullByDefault;
\ No newline at end of file
diff --git a/game/src/test/java/net/onelitefeather/cygnus/TeamCreatorTest.java b/game/src/test/java/net/onelitefeather/cygnus/TeamCreatorTest.java
index 5fff453b..21ddde6d 100644
--- a/game/src/test/java/net/onelitefeather/cygnus/TeamCreatorTest.java
+++ b/game/src/test/java/net/onelitefeather/cygnus/TeamCreatorTest.java
@@ -16,11 +16,9 @@ class TeamCreatorTest {
void testTeamCreation() {
GameConfig gameConfig = new GameConfigReader(Paths.get("")).getConfig();
TeamService teamService = TeamService.of();
- AmbientProvider ambientProvider = new AmbientProvider();
-
TeamCreator teamCreator = new TeamCreator() {};
- teamCreator.createTeams(gameConfig, teamService, ambientProvider);
+ teamCreator.createTeams(gameConfig, teamService);
for (int i = 0; i < teamService.getTeams().size(); i++) {
assertNotNull(teamService.getTeams().get(i));
diff --git a/game/src/test/java/net/onelitefeather/cygnus/ambient/AmbientProviderIntegrationTest.java b/game/src/test/java/net/onelitefeather/cygnus/ambient/AmbientProviderIntegrationTest.java
new file mode 100644
index 00000000..668bc1db
--- /dev/null
+++ b/game/src/test/java/net/onelitefeather/cygnus/ambient/AmbientProviderIntegrationTest.java
@@ -0,0 +1,35 @@
+package net.onelitefeather.cygnus.ambient;
+
+import net.kyori.adventure.key.Key;
+import net.minestom.server.network.packet.server.play.SoundEffectPacket;
+import net.minestom.testing.Collector;
+import net.minestom.testing.TestConnection;
+import net.theevilreaper.xerus.api.team.Team;
+import net.minestom.server.entity.Player;
+import net.minestom.server.instance.Instance;
+import net.minestom.testing.Env;
+import net.minestom.testing.extension.MicrotusExtension;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+@ExtendWith(MicrotusExtension.class)
+class AmbientProviderIntegrationTest {
+
+ @Test
+ void testCaveSoundAtTickZero(Env env) {
+ Instance instance = env.createFlatInstance();
+ TestConnection connection = env.createConnection();
+ Player player = connection.connect(instance);
+ Team team = Team.of(Key.key("cygnus", "test"));
+ team.addPlayer(player);
+
+ AmbientProvider provider = new AmbientProvider(team);
+ Collector sounds = connection.trackIncoming(SoundEffectPacket.class);
+
+ provider.tick();
+ assertFalse(sounds.collect().isEmpty(), "Cave sound should have been played at tick 0");
+ env.destroyInstance(instance, true);
+ }
+}
\ No newline at end of file
diff --git a/game/src/test/java/net/onelitefeather/cygnus/ambient/AmbientProviderTest.java b/game/src/test/java/net/onelitefeather/cygnus/ambient/AmbientProviderTest.java
deleted file mode 100644
index 232786f9..00000000
--- a/game/src/test/java/net/onelitefeather/cygnus/ambient/AmbientProviderTest.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package net.onelitefeather.cygnus.ambient;
-
-import org.junit.jupiter.api.Test;
-
-import static org.junit.jupiter.api.Assertions.*;
-
-class AmbientProviderTest {
-
- @Test
- void testAmbientProviderWithoutATeam() {
- AmbientProvider provider = new AmbientProvider();
- IllegalArgumentException exception = assertThrows(
- IllegalArgumentException.class,
- provider::startTask
- );
- assertEquals("The team variable can't be null", exception.getMessage());
- }
-}
diff --git a/game/src/test/java/net/onelitefeather/cygnus/utils/StaminaHelperTest.java b/game/src/test/java/net/onelitefeather/cygnus/utils/StaminaHelperTest.java
index f0d2fcff..c67c8e01 100644
--- a/game/src/test/java/net/onelitefeather/cygnus/utils/StaminaHelperTest.java
+++ b/game/src/test/java/net/onelitefeather/cygnus/utils/StaminaHelperTest.java
@@ -26,10 +26,9 @@ class StaminaHelperTest {
void testStaminaObjectCreation(@NotNull Env env) {
Instance testInstance = env.createFlatInstance();
GameConfig gameConfig = new GameConfigReader(Paths.get("")).getConfig();
- AmbientProvider ambientProvider = new AmbientProvider();
TeamService teamService = TeamService.of();
TeamCreator teamCreator = new TeamCreator() {};
- teamCreator.createTeams(gameConfig, teamService, ambientProvider);
+ teamCreator.createTeams(gameConfig, teamService);
StaminaService staminaService = new StaminaService();
assertNotNull(staminaService);
diff --git a/game/src/test/java/net/onelitefeather/cygnus/utils/TeamHelperTest.java b/game/src/test/java/net/onelitefeather/cygnus/utils/TeamHelperTest.java
index 416598ac..ef8ff4e0 100644
--- a/game/src/test/java/net/onelitefeather/cygnus/utils/TeamHelperTest.java
+++ b/game/src/test/java/net/onelitefeather/cygnus/utils/TeamHelperTest.java
@@ -13,7 +13,6 @@
import net.minestom.testing.Env;
import net.minestom.testing.extension.MicrotusExtension;
import net.onelitefeather.cygnus.TeamCreator;
-import net.onelitefeather.cygnus.ambient.AmbientProvider;
import net.onelitefeather.cygnus.common.Tags;
import net.onelitefeather.cygnus.common.config.GameConfig;
import net.onelitefeather.cygnus.common.config.GameConfigReader;
@@ -107,11 +106,10 @@ void testTeamAllocation(@NotNull Env env) {
@Test
void testSlenderTeleport(@NotNull Env env) {
Instance testInstance = env.createFlatInstance();
- AmbientProvider ambientProvider = new AmbientProvider();
TeamService teamService = TeamService.of();
TeamCreator teamCreator = new TeamCreator() {
};
- teamCreator.createTeams(gameConfig, teamService, ambientProvider);
+ teamCreator.createTeams(gameConfig, teamService);
Pos slenderSpawn = new Pos(10, 10, 10);
GameMap gameMap = new GameMap("Test", Pos.ZERO, slenderSpawn, Set.of(), Set.of(), List.of());
assertNotNull(gameMap);
@@ -138,11 +136,10 @@ void testNoTabListUpdate() {
@Test
void testInvalidUpdateTabList() {
- AmbientProvider ambientProvider = new AmbientProvider();
TeamService teamService = TeamService.of();
TeamCreator teamCreator = new TeamCreator() {
};
- teamCreator.createTeams(gameConfig, teamService, ambientProvider);
+ teamCreator.createTeams(gameConfig, teamService);
IllegalStateException exception = assertThrows(
IllegalStateException.class,
@@ -154,11 +151,10 @@ void testInvalidUpdateTabList() {
@Test
void testUpdateTabList(@NotNull Env env) {
- AmbientProvider ambientProvider = new AmbientProvider();
TeamService teamService = TeamService.of();
TeamCreator teamCreator = new TeamCreator() {
};
- teamCreator.createTeams(gameConfig, teamService, ambientProvider);
+ teamCreator.createTeams(gameConfig, teamService);
Set survivors = new HashSet<>();