Introduce TeamScoreboard and highest stat tracking functionality.
This commit refactors scoreboard management into a dedicated TeamScoreboard class, improving modularity and readability. It also adds a new command to track and display the highest player stats across all game metrics, enhancing gameplay insights.
This commit is contained in:
parent
14158f73b8
commit
3550d75634
|
|
@ -147,10 +147,11 @@ public class Main extends JavaPlugin {
|
||||||
gameManager.setPlayerStats(playerStats);
|
gameManager.setPlayerStats(playerStats);
|
||||||
final JsonConfigManager<PlayerStat> jsonConfigManager = new JsonConfigManager<>(JacksonConfig.configureMapper());
|
final JsonConfigManager<PlayerStat> jsonConfigManager = new JsonConfigManager<>(JacksonConfig.configureMapper());
|
||||||
Runnable runnable = () -> {
|
Runnable runnable = () -> {
|
||||||
|
log.info("Saving player stats");
|
||||||
File playerStatsDirectory = new File(getDataFolder(), "player_stats");
|
File playerStatsDirectory = new File(getDataFolder(), "player_stats");
|
||||||
gameManager.getPlayerStats().stream().filter(PlayerStat::isTouched).forEach(playerStat -> {
|
gameManager.getPlayerStats().stream().filter(PlayerStat::isTouched).forEach(playerStat -> {
|
||||||
try {
|
try {
|
||||||
jsonConfigManager.saveConfig(playerStat, playerStatsDirectory, String.format("%s.%s", playerStat.getUuid(), "json"));
|
jsonConfigManager.saveConfig(playerStat, playerStatsDirectory, playerStat.getUuid().toString());
|
||||||
playerStat.setUnTouched();
|
playerStat.setUnTouched();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
log.error("Failed to save player stats for [{}].", playerStat.getUuid(), e);
|
log.error("Failed to save player stats for [{}].", playerStat.getUuid(), e);
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,7 @@ public class CommandManager implements CommandExecutor, TabExecutor {
|
||||||
new SkipPhase(gameManager),
|
new SkipPhase(gameManager),
|
||||||
new Start(gameManager, flag),
|
new Start(gameManager, flag),
|
||||||
new CreateTeam(main, gameManager),
|
new CreateTeam(main, gameManager),
|
||||||
|
new HighestStat(gameManager),
|
||||||
new SelectClass(gameManager, worldBorderApi),
|
new SelectClass(gameManager, worldBorderApi),
|
||||||
new Reload(main)
|
new Reload(main)
|
||||||
);
|
);
|
||||||
|
|
@ -120,4 +121,4 @@ public class CommandManager implements CommandExecutor, TabExecutor {
|
||||||
.findFirst()
|
.findFirst()
|
||||||
.orElse(null);
|
.orElse(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,79 @@
|
||||||
|
package com.alttd.ctf.commands.subcommands;
|
||||||
|
|
||||||
|
import com.alttd.ctf.commands.SubCommand;
|
||||||
|
import com.alttd.ctf.config.Messages;
|
||||||
|
import com.alttd.ctf.game.GameManager;
|
||||||
|
import com.alttd.ctf.stats.PlayerStat;
|
||||||
|
import com.alttd.ctf.stats.Stat;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import net.kyori.adventure.text.Component;
|
||||||
|
import net.kyori.adventure.text.TextComponent;
|
||||||
|
import net.kyori.adventure.text.minimessage.MiniMessage;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.OfflinePlayer;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
public class HighestStat extends SubCommand {
|
||||||
|
|
||||||
|
private final GameManager gameManager;
|
||||||
|
private final MiniMessage miniMessage = MiniMessage.miniMessage();
|
||||||
|
|
||||||
|
public HighestStat(GameManager gameManager) {
|
||||||
|
this.gameManager = gameManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
private record HighestStatValue(Stat stat, UUID uuid, double value) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int onCommand(CommandSender commandSender, String[] args) {
|
||||||
|
ArrayList<HighestStatValue> highestStatValues = new ArrayList<>();
|
||||||
|
Collection<PlayerStat> playerStats = gameManager.getPlayerStats();
|
||||||
|
for (Stat stat : Stat.values()) {
|
||||||
|
Optional<PlayerStat> max = playerStats.stream().max(Comparator.comparingDouble(playerStat -> playerStat.getStat(stat)));
|
||||||
|
if (max.isEmpty()) {
|
||||||
|
log.warn("No max stat found for {}", stat.toString());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
PlayerStat playerStat = max.get();
|
||||||
|
highestStatValues.add(new HighestStatValue(stat, playerStat.getUuid(), playerStat.getStat(stat)));
|
||||||
|
}
|
||||||
|
Component message = highestStatValues.stream().map(highestStatValue -> {
|
||||||
|
TextComponent messageBuilder = Component.empty();
|
||||||
|
OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayer(highestStatValue.uuid);
|
||||||
|
Player player = offlinePlayer.getPlayer();
|
||||||
|
if (!offlinePlayer.isOnline() || player == null) {
|
||||||
|
messageBuilder = messageBuilder.append(miniMessage.deserialize(offlinePlayer.getName() == null ? highestStatValue.uuid.toString() : offlinePlayer.getName()));
|
||||||
|
} else {
|
||||||
|
messageBuilder = messageBuilder.append(player.name());
|
||||||
|
}
|
||||||
|
messageBuilder = messageBuilder.append(Component.text(": "));
|
||||||
|
messageBuilder = messageBuilder.append(Component.text(highestStatValue.stat.toString()));
|
||||||
|
messageBuilder = messageBuilder.append(Component.text(": "));
|
||||||
|
messageBuilder = messageBuilder.append(Component.text(highestStatValue.value()));
|
||||||
|
return messageBuilder;
|
||||||
|
}).reduce(Component.empty(), (a, b) -> a.append(Component.newline()).append(b));
|
||||||
|
commandSender.sendMessage(message);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "higheststat";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> getTabComplete(CommandSender commandSender, String[] args) {
|
||||||
|
return List.of();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getHelpMessage() {
|
||||||
|
return Messages.HELP.HIGHEST_STAT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -28,6 +28,7 @@ public class Messages extends AbstractConfig {
|
||||||
public static String START = "<green>Start a new game: <gold>/ctf start <time_in_minutes></gold></green>";
|
public static String START = "<green>Start a new game: <gold>/ctf start <time_in_minutes></gold></green>";
|
||||||
public static String SELECT_CLASS = "<green>Open class selection: <gold>/ctf selectclass</gold></green>";
|
public static String SELECT_CLASS = "<green>Open class selection: <gold>/ctf selectclass</gold></green>";
|
||||||
public static String SKIP_PHASE = "<green>Skip the current phase: <gold>/ctf skipphase</gold></green>";
|
public static String SKIP_PHASE = "<green>Skip the current phase: <gold>/ctf skipphase</gold></green>";
|
||||||
|
public static String HIGHEST_STAT = "<green>Display the highest stat and the player: <gold>/ctf higheststat</gold></green>";
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
private static void load() {
|
private static void load() {
|
||||||
|
|
@ -39,6 +40,7 @@ public class Messages extends AbstractConfig {
|
||||||
START = config.getString(prefix, "start", START);
|
START = config.getString(prefix, "start", START);
|
||||||
SELECT_CLASS = config.getString(prefix, "select-class", SELECT_CLASS);
|
SELECT_CLASS = config.getString(prefix, "select-class", SELECT_CLASS);
|
||||||
SKIP_PHASE = config.getString(prefix, "skip-phase", SKIP_PHASE);
|
SKIP_PHASE = config.getString(prefix, "skip-phase", SKIP_PHASE);
|
||||||
|
HIGHEST_STAT = config.getString(prefix, "highest-stat", HIGHEST_STAT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -121,4 +121,11 @@ public class GameManager {
|
||||||
public Collection<PlayerStat> getPlayerStats() {
|
public Collection<PlayerStat> getPlayerStats() {
|
||||||
return this.playerStats.values();
|
return this.playerStats.values();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Optional<Duration> getRemainingTime() {
|
||||||
|
if (runningGame == null || runningGame.getCurrentPhase().equals(GamePhase.ENDED)) {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
return Optional.of(runningGame.getRemainingTime());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ package com.alttd.ctf.game;
|
||||||
|
|
||||||
import com.alttd.ctf.config.GameConfig;
|
import com.alttd.ctf.config.GameConfig;
|
||||||
import com.alttd.ctf.flag.Flag;
|
import com.alttd.ctf.flag.Flag;
|
||||||
|
import com.alttd.ctf.team.TeamScoreboard;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import net.kyori.adventure.text.minimessage.MiniMessage;
|
import net.kyori.adventure.text.minimessage.MiniMessage;
|
||||||
|
|
@ -14,6 +15,7 @@ import java.time.Duration;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.concurrent.ScheduledExecutorService;
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
|
|
@ -51,6 +53,7 @@ public class RunningGame implements Runnable {
|
||||||
} else {
|
} else {
|
||||||
executorService.shutdown();
|
executorService.shutdown();
|
||||||
}
|
}
|
||||||
|
TeamScoreboard.refreshScoreboard(gameManager);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("Unexpected error in running game", e);
|
log.error("Unexpected error in running game", e);
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
|
|
@ -115,4 +118,9 @@ public class RunningGame implements Runnable {
|
||||||
//TODO say the phase ended early?
|
//TODO say the phase ended early?
|
||||||
nextPhaseActions(currentPhase, GamePhase.ENDED);
|
nextPhaseActions(currentPhase, GamePhase.ENDED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Duration getRemainingTime() {
|
||||||
|
Duration duration = phaseDurations.get(currentPhase);
|
||||||
|
return duration.minus(Duration.between(phaseStartTime, Instant.now()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,21 @@
|
||||||
package com.alttd.ctf.stats;
|
package com.alttd.ctf.stats;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import net.kyori.adventure.text.minimessage.MiniMessage;
|
||||||
|
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.OfflinePlayer;
|
||||||
import org.bukkit.command.CommandSender;
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@NoArgsConstructor
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
@Getter
|
@Getter
|
||||||
public final class PlayerStat {
|
public final class PlayerStat {
|
||||||
|
|
@ -16,11 +23,15 @@ public final class PlayerStat {
|
||||||
private boolean touched;
|
private boolean touched;
|
||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
private static final CommandSender commandSender = Bukkit.getConsoleSender();
|
private static final CommandSender commandSender = Bukkit.getConsoleSender();
|
||||||
|
@JsonIgnore
|
||||||
|
private static boolean someoneDiedInPowderedSnow = false;
|
||||||
|
|
||||||
|
@JsonProperty
|
||||||
@NotNull
|
@NotNull
|
||||||
private final UUID uuid;
|
private UUID uuid;
|
||||||
|
@JsonProperty
|
||||||
@NotNull
|
@NotNull
|
||||||
private final String inGameName;
|
private String inGameName;
|
||||||
|
|
||||||
private boolean completedGame = false;
|
private boolean completedGame = false;
|
||||||
private int flagsCaptured = 0;
|
private int flagsCaptured = 0;
|
||||||
|
|
@ -37,7 +48,7 @@ public final class PlayerStat {
|
||||||
switch (stat) {
|
switch (stat) {
|
||||||
case COMPLETED_GAME -> {
|
case COMPLETED_GAME -> {
|
||||||
if (!completedGame) {
|
if (!completedGame) {
|
||||||
Bukkit.dispatchCommand(commandSender, String.format("lp user %s permission set ctf.game.completed", inGameName));
|
Bukkit.dispatchCommand(commandSender, String.format("lp user %s permission settemp ctf.game.completed true 14d", inGameName));
|
||||||
}
|
}
|
||||||
completedGame = true;
|
completedGame = true;
|
||||||
}
|
}
|
||||||
|
|
@ -47,7 +58,24 @@ public final class PlayerStat {
|
||||||
case BLOCKS_PLACED -> blocksPlaced++;
|
case BLOCKS_PLACED -> blocksPlaced++;
|
||||||
case SNOWBALLS_THROWN -> snowballsThrown++;
|
case SNOWBALLS_THROWN -> snowballsThrown++;
|
||||||
case TIME_SPEND_CAPTURING_FLAG -> timeSpendCapturingFlag++;
|
case TIME_SPEND_CAPTURING_FLAG -> timeSpendCapturingFlag++;
|
||||||
case DEATHS_IN_POWDERED_SNOW -> deathsInPowderedSnow++; //TODO announce if they are the first person to do this and save they are the first
|
case DEATHS_IN_POWDERED_SNOW -> {
|
||||||
|
if (someoneDiedInPowderedSnow) {
|
||||||
|
deathsInPowderedSnow++; //TODO announce if they are the first person to do this and save they are the first
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayer(uuid);
|
||||||
|
if (!offlinePlayer.isOnline()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Player player = offlinePlayer.getPlayer();
|
||||||
|
if (player == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Bukkit.broadcast(MiniMessage.miniMessage().deserialize("<red><player> will receive a consolation prize for being the first to die in powdered snow.</red>",
|
||||||
|
Placeholder.component("player",player.displayName())));
|
||||||
|
Bukkit.dispatchCommand(commandSender, String.format("lp user %s permission settemp ctf.game.first_powdered_snow_death true 14d", inGameName));
|
||||||
|
someoneDiedInPowderedSnow = true;
|
||||||
|
}
|
||||||
case DAMAGE_DONE, DAMAGE_HEALED -> throw new IllegalArgumentException(String.format("%s requires a number", stat.name()));
|
case DAMAGE_DONE, DAMAGE_HEALED -> throw new IllegalArgumentException(String.format("%s requires a number", stat.name()));
|
||||||
}
|
}
|
||||||
touched = true;
|
touched = true;
|
||||||
|
|
@ -66,4 +94,18 @@ public final class PlayerStat {
|
||||||
touched = false;
|
touched = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public double getStat(Stat stat) {
|
||||||
|
return switch (stat) {
|
||||||
|
case COMPLETED_GAME -> completedGame ? 1 : 0;
|
||||||
|
case FLAGS_CAPTURED -> flagsCaptured;
|
||||||
|
case KILLS -> kills;
|
||||||
|
case DAMAGE_DONE -> damageDone;
|
||||||
|
case DAMAGE_HEALED -> damageHealed;
|
||||||
|
case SNOW_MINED -> snowMined;
|
||||||
|
case BLOCKS_PLACED -> blocksPlaced;
|
||||||
|
case SNOWBALLS_THROWN -> snowballsThrown;
|
||||||
|
case TIME_SPEND_CAPTURING_FLAG -> timeSpendCapturingFlag;
|
||||||
|
case DEATHS_IN_POWDERED_SNOW -> deathsInPowderedSnow;
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,15 +9,9 @@ import lombok.NoArgsConstructor;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import net.kyori.adventure.text.Component;
|
import net.kyori.adventure.text.Component;
|
||||||
import net.kyori.adventure.text.format.NamedTextColor;
|
|
||||||
import net.kyori.adventure.text.format.TextColor;
|
|
||||||
import net.kyori.adventure.text.minimessage.MiniMessage;
|
|
||||||
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
|
|
||||||
import org.bukkit.Bukkit;
|
|
||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
import org.bukkit.Material;
|
import org.bukkit.Material;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.scoreboard.*;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
@ -28,7 +22,7 @@ import java.util.*;
|
||||||
public class Team {
|
public class Team {
|
||||||
|
|
||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
private final static Scoreboard scoreboard = Bukkit.getScoreboardManager().getNewScoreboard();
|
private final TeamScoreboard scoreboard = new TeamScoreboard(this);
|
||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
private final HashMap<UUID, TeamPlayer> players = new HashMap<>();
|
private final HashMap<UUID, TeamPlayer> players = new HashMap<>();
|
||||||
@JsonProperty("name")
|
@JsonProperty("name")
|
||||||
|
|
@ -75,7 +69,7 @@ public class Team {
|
||||||
}
|
}
|
||||||
|
|
||||||
public TeamPlayer addPlayer(Player player, PlayerStat playerStat) {
|
public TeamPlayer addPlayer(Player player, PlayerStat playerStat) {
|
||||||
removeFromScoreBoard(player);
|
removeFromScoreboard(player);
|
||||||
UUID uuid = player.getUniqueId();
|
UUID uuid = player.getUniqueId();
|
||||||
TeamPlayer teamPlayer = new TeamPlayer(player, this, playerStat);
|
TeamPlayer teamPlayer = new TeamPlayer(player, this, playerStat);
|
||||||
players.put(uuid, teamPlayer);
|
players.put(uuid, teamPlayer);
|
||||||
|
|
@ -101,7 +95,7 @@ public class Team {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removePlayer(@NotNull Player player) {
|
public void removePlayer(@NotNull Player player) {
|
||||||
removeFromScoreBoard(player);
|
removeFromScoreboard(player);
|
||||||
TeamPlayer remove = players.remove(player.getUniqueId());
|
TeamPlayer remove = players.remove(player.getUniqueId());
|
||||||
if (remove == null) {
|
if (remove == null) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -115,38 +109,15 @@ public class Team {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addToScoreboard(Player player) {
|
public void addToScoreboard(Player player) {
|
||||||
org.bukkit.scoreboard.Team team = scoreboard.getTeam("ctf_" + id);
|
scoreboard.addToScoreboard(player);
|
||||||
if (team == null) {
|
|
||||||
team = scoreboard.registerNewTeam("ctf_" + id);
|
|
||||||
team.displayName(name);
|
|
||||||
NamedTextColor namedTextColor = NamedTextColor.nearestTo(TextColor.color(color.r(), color.g(), color.b()));
|
|
||||||
team.color(namedTextColor);
|
|
||||||
}
|
|
||||||
team.addPlayer(player);
|
|
||||||
player.setScoreboard(scoreboard);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void removeFromScoreBoard(Player player) {
|
private void removeFromScoreboard(Player player) {
|
||||||
scoreboard.getTeams().stream()
|
scoreboard.removeFromScoreboard(player);
|
||||||
.filter(team -> team.getName().startsWith("ctf_"))
|
|
||||||
.filter(team -> team.hasPlayer(player))
|
|
||||||
.forEach(team -> team.removePlayer(player));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setScore(int newScore) {
|
public void setScore(int newScore) {
|
||||||
Objective objective = getOrCreateObjective();
|
scoreboard.setScore(newScore);
|
||||||
Score score = objective.getScore(legacyTeamColor + PlainTextComponentSerializer.plainText().serialize(name));
|
|
||||||
score.setScore(newScore);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Objective getOrCreateObjective() {
|
|
||||||
Objective objective = scoreboard.getObjective("teamScores");
|
|
||||||
if (objective == null) {
|
|
||||||
objective = scoreboard.registerNewObjective("teamScores", Criteria.DUMMY,
|
|
||||||
MiniMessage.miniMessage().deserialize("<gold>CTF score</gold>"));
|
|
||||||
objective.setDisplaySlot(DisplaySlot.SIDEBAR);
|
|
||||||
}
|
|
||||||
return objective;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
86
src/main/java/com/alttd/ctf/team/TeamScoreboard.java
Normal file
86
src/main/java/com/alttd/ctf/team/TeamScoreboard.java
Normal file
|
|
@ -0,0 +1,86 @@
|
||||||
|
package com.alttd.ctf.team;
|
||||||
|
|
||||||
|
import com.alttd.ctf.game.GameManager;
|
||||||
|
import com.alttd.ctf.game.GamePhase;
|
||||||
|
import io.papermc.paper.scoreboard.numbers.NumberFormat;
|
||||||
|
import net.kyori.adventure.text.event.ClickEvent;
|
||||||
|
import net.kyori.adventure.text.format.*;
|
||||||
|
import net.kyori.adventure.text.minimessage.MiniMessage;
|
||||||
|
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.ChatColor;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.scoreboard.*;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
|
||||||
|
public class TeamScoreboard {
|
||||||
|
|
||||||
|
private final static Scoreboard scoreboard = Bukkit.getScoreboardManager().getNewScoreboard();
|
||||||
|
private final Team team;
|
||||||
|
|
||||||
|
public TeamScoreboard(Team team) {
|
||||||
|
this.team = team;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addToScoreboard(Player player) {
|
||||||
|
org.bukkit.scoreboard.Team scoreboardTeam = scoreboard.getTeam("ctf_" + team.getId());
|
||||||
|
if (scoreboardTeam == null) {
|
||||||
|
scoreboardTeam = scoreboard.registerNewTeam("ctf_" + team.getId());
|
||||||
|
scoreboardTeam.displayName(team.getName());
|
||||||
|
TeamColor color = team.getColor();
|
||||||
|
NamedTextColor namedTextColor = NamedTextColor.nearestTo(TextColor.color(color.r(), color.g(), color.b()));
|
||||||
|
scoreboardTeam.color(namedTextColor);
|
||||||
|
}
|
||||||
|
scoreboardTeam.addPlayer(player);
|
||||||
|
player.setScoreboard(scoreboard);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void removeFromScoreboard(Player player) {
|
||||||
|
scoreboard.getTeams().stream()
|
||||||
|
.filter(team -> team.getName().startsWith("ctf_"))
|
||||||
|
.filter(team -> team.hasPlayer(player))
|
||||||
|
.forEach(team -> team.removePlayer(player));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setScore(int newScore) {
|
||||||
|
Objective objective = getOrCreateObjective("teamScores", "<gold>CTF score</gold>");
|
||||||
|
Score score = objective.getScore(team.getLegacyTeamColor() +
|
||||||
|
PlainTextComponentSerializer.plainText().serialize(team.getName()));
|
||||||
|
score.setScore(newScore);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Objective getOrCreateObjective(String internalName, String displayName) {
|
||||||
|
Objective objective = scoreboard.getObjective(internalName);
|
||||||
|
if (objective == null) {
|
||||||
|
objective = scoreboard.registerNewObjective(internalName, Criteria.DUMMY,
|
||||||
|
MiniMessage.miniMessage().deserialize(displayName));
|
||||||
|
objective.setDisplaySlot(DisplaySlot.SIDEBAR);
|
||||||
|
}
|
||||||
|
return objective;
|
||||||
|
}
|
||||||
|
|
||||||
|
private record PhaseScore(GamePhase gamePhase, Score score) {}
|
||||||
|
private static PhaseScore phaseScore = null;
|
||||||
|
private static void updateTime(GamePhase gamePhase, Duration duration) {
|
||||||
|
if (phaseScore == null) {
|
||||||
|
phaseScore = new PhaseScore(gamePhase, getOrCreateObjective("teamScores", "<gold>CTF score</gold>")
|
||||||
|
.getScore(ChatColor.GREEN + PlainTextComponentSerializer.plainText().serialize(gamePhase.getDisplayName())));
|
||||||
|
} else if (phaseScore.gamePhase() != gamePhase) {
|
||||||
|
phaseScore.score.resetScore();
|
||||||
|
phaseScore = new PhaseScore(gamePhase, getOrCreateObjective("teamScores", "<gold>CTF score</gold>")
|
||||||
|
.getScore(ChatColor.GREEN + PlainTextComponentSerializer.plainText().serialize(gamePhase.getDisplayName())));
|
||||||
|
}
|
||||||
|
phaseScore.score.setScore(duration.toMinutesPart() == 0 ? duration.toSecondsPart() : duration.toMinutesPart());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void refreshScoreboard(GameManager gameManager) {
|
||||||
|
gameManager.getGamePhase().ifPresent(gamePhase ->
|
||||||
|
gameManager.getRemainingTime().ifPresent(duration ->
|
||||||
|
updateTime(gamePhase, duration)));
|
||||||
|
// TODO if game is active show time + game phase
|
||||||
|
// if someone has flag show that
|
||||||
|
// show how many of each team in circle?
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
#Sun Feb 23 01:14:21 CET 2025
|
#Fri Feb 28 22:11:49 CET 2025
|
||||||
buildNumber=70
|
buildNumber=80
|
||||||
version=0.1
|
version=0.1
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user