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<>();