package com.alttd.hunger_games.services; import com.alttd.hunger_games.config.Messages; import com.alttd.hunger_games.data_objects.PLAYER_STATE; import com.alttd.hunger_games.data_objects.ROUND_STATE; import net.kyori.adventure.bossbar.BossBar; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.minimessage.MiniMessage; import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; import org.bukkit.Bukkit; import org.bukkit.entity.Player; import java.time.Duration; import java.util.HashSet; import java.util.Objects; import java.util.Set; import java.util.UUID; import java.util.stream.Collectors; public class BossBarService implements RoundListener { private static BossBarService instance = null; private final RoundService roundService; private final BossBar bossBar; private final Set viewers = new HashSet<>(); private int maxPlayers = 0; public BossBarService(Round round, RoundService roundService) { this.roundService = roundService; this.bossBar = BossBar.bossBar(Component.empty(), 1.0f, BossBar.Color.YELLOW, BossBar.Overlay.PROGRESS); round.register(this); } public static BossBarService createSingletonInstance(Round round, RoundService roundService) { if (instance != null) { throw new IllegalStateException("BossBarService is already initialized."); } instance = new BossBarService(round, roundService); return instance; } public void updateCountdown(Duration timeLeft, Duration totalTime, ROUND_STATE state) { String message = switch (state) { case COUNTDOWN -> Messages.GAME.BOSSBAR_COUNTDOWN; case SAFE_PHASE -> Messages.GAME.BOSSBAR_PVP_COUNTDOWN; case KILL_PHASE -> Messages.GAME.BOSSBAR_SAFE_PHASE; default -> { if (state.ordinal() > ROUND_STATE.KILL_PHASE.ordinal()) { yield Messages.GAME.BOSSBAR_KILL_PHASE; } throw new IllegalArgumentException("Invalid round state: " + state); } }; if (message.isEmpty()) { bossBar.name(Component.empty()); bossBar.progress(0.0f); return; } bossBar.name(MiniMessage.miniMessage().deserialize(message, Placeholder.unparsed("time", formatTime(timeLeft)))); bossBar.progress(Math.clamp((float) timeLeft.toMillis() / totalTime.toMillis(), 0.0f, 1.0f)); updateBossBarColor(state); updateViewers(); } private void updateBossBarColor(ROUND_STATE state) { switch (state) { case COUNTDOWN -> bossBar.color(BossBar.Color.YELLOW); case SAFE_PHASE -> bossBar.color(BossBar.Color.GREEN); case KILL_PHASE -> bossBar.color(BossBar.Color.RED); case FINALE -> bossBar.color(BossBar.Color.PURPLE); default -> bossBar.color(BossBar.Color.WHITE); } } public void updateFinale(int remainingPlayers) { if (maxPlayers == 0) { maxPlayers = remainingPlayers; } if (remainingPlayers <= 1) { hideAll(); return; } bossBar.name(MiniMessage.miniMessage().deserialize(Messages.GAME.BOSSBAR_FINALE, Placeholder.unparsed("remaining", String.valueOf(remainingPlayers)))); bossBar.progress(Math.clamp((float) remainingPlayers / maxPlayers, 0.0f, 1.0f)); bossBar.color(BossBar.Color.PURPLE); updateViewers(); } private void updateViewers() { Set registeredPlayers = roundService.getPlayers(PLAYER_STATE.REGISTERED); // Add new viewers registeredPlayers.stream() .filter(viewers::add) .map(Bukkit::getPlayer) .filter(Objects::nonNull) .forEach(player -> player.showBossBar(bossBar)); // Remove old viewers Set toRemove = viewers.stream() .filter(uuid -> !registeredPlayers.contains(uuid)) .collect(Collectors.toSet()); for (UUID uuid : toRemove) { viewers.remove(uuid); Player player = Bukkit.getPlayer(uuid); if (player != null) { player.hideBossBar(bossBar); } } } private void hideAll() { viewers.stream() .map(Bukkit::getPlayer) .filter(Objects::nonNull) .forEach(player -> player.hideBossBar(bossBar)); viewers.clear(); } private String formatTime(Duration duration) { if (duration.isNegative()) { return "0s"; } long minutes = duration.toMinutesPart(); int seconds = duration.toSecondsPart(); if (minutes > 0) { return "%dm %ds".formatted(minutes, seconds); } return "%ds".formatted(seconds); } @Override public void stateChange(ROUND_STATE roundState) { if (roundState == ROUND_STATE.PLAYER_REGISTRATION || roundState == ROUND_STATE.ENDED) { hideAll(); maxPlayers = 0; } else if (roundState == ROUND_STATE.FINALE) { updateFinale(roundService.getPlayers(PLAYER_STATE.REGISTERED).size()); } } }