diff --git a/pom.xml b/pom.xml index 34d32e6..7022d61 100644 --- a/pom.xml +++ b/pom.xml @@ -77,6 +77,10 @@ com.github.stefvanschie.inventoryframework ${project.groupId}.${project.artifactId}.inventoryframework + + net.megavex.scoreboardlibrary + ${project.groupId}.${project.artifactId}.scoreboardlibrary + @@ -169,6 +173,25 @@ IF 0.10.15 + + net.megavex + scoreboard-library-api + 2.1.10 + + + net.megavex + scoreboard-library-implementation + 2.1.10 + runtime + + + net.megavex + scoreboard-library-modern + 2.1.10 + runtime + + + diff --git a/src/main/java/xyz/twovb/sgm/SGM.java b/src/main/java/xyz/twovb/sgm/SGM.java index b2b21cc..46bcbee 100644 --- a/src/main/java/xyz/twovb/sgm/SGM.java +++ b/src/main/java/xyz/twovb/sgm/SGM.java @@ -3,6 +3,10 @@ package xyz.twovb.sgm; import dev.rollczi.litecommands.bukkit.LiteBukkitMessages; import dev.rollczi.litecommands.bukkit.LiteCommandsBukkit; import lombok.Getter; +import net.megavex.scoreboardlibrary.api.ScoreboardLibrary; +import net.megavex.scoreboardlibrary.api.exception.NoPacketAdapterAvailableException; +import net.megavex.scoreboardlibrary.api.noop.NoopScoreboardLibrary; +import net.megavex.scoreboardlibrary.api.team.TeamManager; import org.bukkit.configuration.InvalidConfigurationException; import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.YamlConfiguration; @@ -40,6 +44,11 @@ public final class SGM extends JavaPlugin { @Getter private LevelManager levelManager; + @Getter + private ScoreboardLibrary scoreboardLibrary; + @Getter + private TeamManager teamManager; + @Override public void onEnable() { instance = this; @@ -54,6 +63,7 @@ public final class SGM extends JavaPlugin { BuildInfo.load(Objects.requireNonNull(this.getTextResource("build-info.yml"))); } catch (IOException | InvalidConfigurationException ignored) { } + loadScoreboard(); loadGuis(); registerCommands(); registerGames(); @@ -61,9 +71,20 @@ public final class SGM extends JavaPlugin { levelManager.loadLevels(); } + private void loadScoreboard() { + try { + scoreboardLibrary = ScoreboardLibrary.loadScoreboardLibrary(this); + } catch (NoPacketAdapterAvailableException e) { + // If no packet adapter was found, you can fallback to the no-op implementation: + scoreboardLibrary = new NoopScoreboardLibrary(); + this.getCLogger().error("No scoreboard packet adapter found."); + } + } + private void loadGuis() { saveResource("guis/initgame.xml", false); saveResource("guis/mapgui.xml", false); + saveResource("guis/teampicker.xml", false); } private void registerPlaceholders() { diff --git a/src/main/java/xyz/twovb/sgm/commands/impl/GameCommand.java b/src/main/java/xyz/twovb/sgm/commands/impl/GameCommand.java index d4658f7..d707f3c 100644 --- a/src/main/java/xyz/twovb/sgm/commands/impl/GameCommand.java +++ b/src/main/java/xyz/twovb/sgm/commands/impl/GameCommand.java @@ -16,8 +16,11 @@ import dev.rollczi.litecommands.annotations.execute.Execute; import dev.rollczi.litecommands.annotations.permission.Permission; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; +import org.bukkit.scoreboard.Scoreboard; +import org.bukkit.scoreboard.Team; import xyz.twovb.sgm.SGM; import xyz.twovb.sgm.games.Minigame; +import xyz.twovb.sgm.games.impl.capturethebrick.guis.TeamGui; import xyz.twovb.sgm.guis.InitGameGui; import xyz.twovb.toolbox.api.CustomPlayer; import xyz.twovb.toolbox.utils.ChatUtils; @@ -59,6 +62,14 @@ public class GameCommand { game.start(); } + @Execute(name = "team join") + @Permission("sgm.games.team") + void teamJoin(@Context Player player, @Arg("player") Optional target) { +// String name = LevelName.orElse(UUID.randomUUID().toString()); + Player targetPlayer = target.orElse(player); + new TeamGui(targetPlayer).getGui().show(player); + } + // @Execute(name = "join") // void join(@Context Player player) { // diff --git a/src/main/java/xyz/twovb/sgm/games/Minigame.java b/src/main/java/xyz/twovb/sgm/games/Minigame.java index d4b6cf4..a767969 100644 --- a/src/main/java/xyz/twovb/sgm/games/Minigame.java +++ b/src/main/java/xyz/twovb/sgm/games/Minigame.java @@ -63,7 +63,6 @@ public interface Minigame extends Listener { } } - enum GameState { PRESTART, READY, STARTED, ENDING } diff --git a/src/main/java/xyz/twovb/sgm/games/impl/capturethebrick/CTB.java b/src/main/java/xyz/twovb/sgm/games/impl/capturethebrick/CTB.java index 0f7b701..6eda805 100644 --- a/src/main/java/xyz/twovb/sgm/games/impl/capturethebrick/CTB.java +++ b/src/main/java/xyz/twovb/sgm/games/impl/capturethebrick/CTB.java @@ -5,10 +5,21 @@ package xyz.twovb.sgm.games.impl.capturethebrick; * Created by 2vb - 5/7/2024 */ -/* - * Created by 2vb - 3/7/2024 - */ - +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.format.Style; +import net.kyori.adventure.text.format.TextDecoration; +import net.kyori.adventure.text.minimessage.MiniMessage; +import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; +import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver; +import net.megavex.scoreboardlibrary.api.sidebar.Sidebar; +import net.megavex.scoreboardlibrary.api.sidebar.component.ComponentSidebarLayout; +import net.megavex.scoreboardlibrary.api.sidebar.component.SidebarComponent; +import net.megavex.scoreboardlibrary.api.sidebar.component.animation.CollectionSidebarAnimation; +import net.megavex.scoreboardlibrary.api.sidebar.component.animation.SidebarAnimation; +import net.megavex.scoreboardlibrary.api.team.TeamDisplay; +import net.megavex.scoreboardlibrary.api.team.TeamManager; +import net.megavex.scoreboardlibrary.api.team.ScoreboardTeam; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.Material; @@ -23,6 +34,8 @@ import org.bukkit.event.EventHandler; import org.bukkit.event.HandlerList; import org.bukkit.event.entity.PlayerDeathEvent; import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.scoreboard.Team; +import org.jetbrains.annotations.NotNull; import xyz.twovb.sgm.SGM; import xyz.twovb.sgm.games.GameManager; import xyz.twovb.sgm.games.Minigame; @@ -38,8 +51,6 @@ public class CTB implements Minigame { final static String name = "CaptureTheBrick"; private final UUID uuid; private final List players; - private List redTeam; - private List blueTeam; private final ArrayList redBrickSpawns = new ArrayList(); private final ArrayList blueBrickSpawns = new ArrayList(); private final ArrayList redSpawnArea = new ArrayList(); @@ -51,6 +62,15 @@ public class CTB implements Minigame { private int boundsLevel; private Location spawnLoc; private FileConfiguration messages; + private TeamManager teamManager; + private ScoreboardTeam redTeam; + private ScoreboardTeam blueTeam; + private Sidebar sb; + + +// private List redTeam = new ArrayList(); +// private List blueTeam = new ArrayList(); +// private Sidebar sb; public CTB() { @@ -65,15 +85,56 @@ public class CTB implements Minigame { gameWorld = createGameWorld(world, uuid); gameWorld.setAutoSave(false); if (applyOptions(world)) { + teamManager = SGM.getInstance().getScoreboardLibrary().createTeamManager(); + sb = SGM.getInstance().getScoreboardLibrary().createSidebar(); Bukkit.getPluginManager().registerEvents(this, SGM.getInstance()); + + this.redTeam = teamManager.createIfAbsent("red_team"); + this.blueTeam = teamManager.createIfAbsent("blue_team"); + + setupTeamDisplay(redTeam, "Red Team", NamedTextColor.RED); + setupTeamDisplay(blueTeam, "Blue Team", NamedTextColor.BLUE); + + buildScoreboard(); + // Ready state = GameState.READY; owner.sendMessage(ChatUtils.translate(messages.getString("system.game.ready").replace("%game%", name))); } else { owner.sendMessage(ChatUtils.translate(messages.getString("system.game.failed").replace("%cause%", "to load options."))); - Bukkit.getServer().unloadWorld(gameWorld, false); + this.stop(); } } + private void setupTeamDisplay(ScoreboardTeam team, String displayName, NamedTextColor color) { + TeamDisplay display = team.defaultDisplay(); + display.displayName(Component.text(displayName)); + display.playerColor(color); + } + + private void buildScoreboard() { + @NotNull SidebarAnimation titleAnimation = createGradientAnimation(Component.text("Capture The Brick", Style.style(TextDecoration.BOLD))); + var title = SidebarComponent.animatedLine(titleAnimation); + SidebarComponent lines = SidebarComponent.builder() + .addDynamicLine(() -> Component.text("Red Team: " + redTeam.teamManager().players().size(), NamedTextColor.RED)) + .addDynamicLine(() -> Component.text("Blue Team: " + blueTeam.teamManager().players().size(), NamedTextColor.BLUE)) + .addDynamicLine(() -> Component.text("Bricks Remaining: " + bricks, NamedTextColor.GREEN)) + .build(); + ComponentSidebarLayout layout = new ComponentSidebarLayout(title, lines); + layout.apply(sb); + } + + private @NotNull SidebarAnimation createGradientAnimation(@NotNull Component text) { + float step = 1f / 8f; + TagResolver.Single textPlaceholder = Placeholder.component("text", text); + List frames = new ArrayList<>((int) (2f / step)); + float phase = -1f; + while (phase < 1) { + frames.add(MiniMessage.miniMessage().deserialize("", textPlaceholder)); + phase += step; + } + return new CollectionSidebarAnimation<>(frames); + } + private Location parseString(String string) { String[] coords = string.trim().split(","); if (coords.length == 3) { @@ -169,6 +230,29 @@ public class CTB implements Minigame { } } + public void addPlayerToTeam(Player player, boolean isRedTeam) { + if (isRedTeam) { + redTeam.defaultDisplay().addEntry(player.getName()); + } else { + blueTeam.defaultDisplay().addEntry(player.getName()); + } + teamManager.addPlayer(player); + } + + public void removePlayerFromTeams(Player player) { + redTeam.defaultDisplay().removeEntry(player.getName()); + blueTeam.defaultDisplay().removeEntry(player.getName()); + teamManager.removePlayer(player); + } + + public int getAlivePlayersCount() { + return (int) players.stream().filter(Player::isOnline).count(); + } + + public int getTeamSize(ScoreboardTeam team) { + return team.defaultDisplay().entries().size(); + } + private void placeBrick(Location location, String team) { Block brick = location.getBlock(); if (Objects.equals(team, "red")) { @@ -197,11 +281,16 @@ public class CTB implements Minigame { player.teleport(Bukkit.getServer().getWorlds().get(0).getSpawnLocation()); } Bukkit.unloadWorld(gameWorld, false); + if (teamManager != null) { + teamManager.close(); + } + sb.close();; } @Override public void addPlayer(Player player) { players.add(player); + sb.addPlayer(player); player.teleport(this.spawnLoc); sendMessageToAllPlayers(messages.getString("system.player.join").replace("%player%", player.getName())); } @@ -244,6 +333,10 @@ public class CTB implements Minigame { return uuid; } + public ScoreboardTeam getRedTeam() { + return redTeam; + } + @Override public GameState getState() { return state; @@ -251,6 +344,7 @@ public class CTB implements Minigame { @Override public void onTick() { + buildScoreboard(); if (state == GameState.STARTED && players.size() <= 1) { this.stop(); } diff --git a/src/main/java/xyz/twovb/sgm/games/impl/capturethebrick/guis/TeamGui.java b/src/main/java/xyz/twovb/sgm/games/impl/capturethebrick/guis/TeamGui.java new file mode 100644 index 0000000..fdad501 --- /dev/null +++ b/src/main/java/xyz/twovb/sgm/games/impl/capturethebrick/guis/TeamGui.java @@ -0,0 +1,57 @@ +package xyz.twovb.sgm.games.impl.capturethebrick.guis; + +import com.github.stefvanschie.inventoryframework.gui.type.ChestGui; +import lombok.Getter; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.inventory.ItemStack; +import xyz.twovb.sgm.SGM; +import xyz.twovb.sgm.games.Minigame; +import xyz.twovb.sgm.guis.MapGui; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.InputStream; +import java.nio.file.Files; +import java.util.logging.Level; + +public class TeamGui { + @Getter + private final ChestGui gui; + + private Player target; + + public TeamGui(Player player) { + ChestGui loadedGui = null; + try { + File guiFile = new File(SGM.getInstance().getDataFolder(), "guis/teampicker.xml"); + if (guiFile.exists()) { + byte[] fileContent = Files.readAllBytes(guiFile.toPath()); + InputStream inputStream = new ByteArrayInputStream(fileContent); + loadedGui = ChestGui.load(this, inputStream); + } else { + SGM.getInstance().getLogger().log(Level.WARNING, "Could not find teampicker.xml file."); + } + } catch (Exception e) { + SGM.getInstance().getLogger().log(Level.SEVERE, "Error loading teampicker.xml", e); + } + gui = loadedGui; + target = player; + } + + public void globalClick(InventoryClickEvent event) { + event.setCancelled(true); + } + + public void pickTeam(InventoryClickEvent event) { + Player player = (Player) event.getWhoClicked(); + ItemStack clickedItem = event.getCurrentItem(); + Minigame game = SGM.getInstance().getGameManager().findGame(player); + if (clickedItem.getType().equals(Material.RED_WOOL)) { + game.sendMessageToAllPlayers(target.getName() + " RED"); + } else if (clickedItem.getType().equals(Material.BLUE_WOOL)) { + game.sendMessageToAllPlayers(target.getName() + " BLUE"); + } + } +} diff --git a/src/main/java/xyz/twovb/sgm/games/impl/capturethebrick/scoreboards/CTBSidebar.java b/src/main/java/xyz/twovb/sgm/games/impl/capturethebrick/scoreboards/CTBSidebar.java new file mode 100644 index 0000000..b07dca1 --- /dev/null +++ b/src/main/java/xyz/twovb/sgm/games/impl/capturethebrick/scoreboards/CTBSidebar.java @@ -0,0 +1,70 @@ +//package xyz.twovb.sgm.games.impl.capturethebrick.scoreboards; +// +//import net.kyori.adventure.text.Component; +//import net.kyori.adventure.text.format.NamedTextColor; +//import net.kyori.adventure.text.format.Style; +//import net.kyori.adventure.text.format.TextDecoration; +//import net.kyori.adventure.text.minimessage.MiniMessage; +//import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; +//import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver; +//import net.megavex.scoreboardlibrary.api.sidebar.Sidebar; +//import net.megavex.scoreboardlibrary.api.sidebar.component.ComponentSidebarLayout; +//import net.megavex.scoreboardlibrary.api.sidebar.component.SidebarComponent; +//import net.megavex.scoreboardlibrary.api.sidebar.component.animation.CollectionSidebarAnimation; +//import net.megavex.scoreboardlibrary.api.sidebar.component.animation.SidebarAnimation; +//import org.bukkit.plugin.Plugin; +//import org.jetbrains.annotations.NotNull; +//import xyz.twovb.sgm.games.impl.capturethebrick.CTB; +// +//import java.util.ArrayList; +//import java.util.List; +// +//public class CTBSidebar { +// private final Sidebar sidebar; +// private final ComponentSidebarLayout componentSidebar; +// private final SidebarAnimation titleAnimation; +// private final CTB minigame; +// +// public CTBSidebar(@NotNull Plugin plugin, @NotNull Sidebar sidebar, @NotNull CTB minigame) { +// this.sidebar = sidebar; +// this.minigame = minigame; +// +// this.titleAnimation = createGradientAnimation(Component.text("Capture The Brick", Style.style(TextDecoration.BOLD))); +// var title = SidebarComponent.animatedLine(titleAnimation); +// +// SidebarComponent gameInfo = SidebarComponent.builder() +// .addDynamicLine(() -> Component.text("Players Alive: " + getAlivePlayersCount(), NamedTextColor.YELLOW)) +// .addDynamicLine(() -> Component.text("Red Team: " + getTeamSize(minigame.g), NamedTextColor.RED)) +// .addDynamicLine(() -> Component.text("Blue Team: " + getTeamSize(minigame.blueTeam), NamedTextColor.BLUE)) +// .addDynamicLine(() -> Component.text("Bricks Remaining: " + minigame.bricks, NamedTextColor.GREEN)) +// .build(); +// +// this.componentSidebar = new ComponentSidebarLayout(title, gameInfo); +// } +// +// public void onTick() { +// titleAnimation.nextFrame(); +// componentSidebar.apply(sidebar); +// } +// +// private int getAlivePlayersCount() { +// return (int) minigame.players.stream().filter(Player::isOnline).count(); +// } +// +// private int getTeamSize(ScoreboardTeam team) { +// return team.defaultDisplay().entries().size(); +// } +// +// private @NotNull SidebarAnimation createGradientAnimation(@NotNull Component text) { +// float step = 1f / 8f; +// TagResolver.Single textPlaceholder = Placeholder.component("text", text); +// List frames = new ArrayList<>((int) (2f / step)); +// float phase = -1f; +// while (phase < 1) { +// frames.add(MiniMessage.miniMessage().deserialize("", textPlaceholder)); +// phase += step; +// } +// return new CollectionSidebarAnimation<>(frames); +// } +//} +// diff --git a/src/main/java/xyz/twovb/sgm/levels/LevelManager.java b/src/main/java/xyz/twovb/sgm/levels/LevelManager.java index f1340a1..669575e 100644 --- a/src/main/java/xyz/twovb/sgm/levels/LevelManager.java +++ b/src/main/java/xyz/twovb/sgm/levels/LevelManager.java @@ -200,6 +200,7 @@ public class LevelManager { // Load levels from levelPath loadWorldDir(levelPath, LevelType.LEVEL); + loadWorldDir(mapPath, LevelType.MAP); } private void loadWorldDir(String path, LevelType levelType) { @@ -218,8 +219,10 @@ public class LevelManager { if (enabledMaps.containsKey(game) && levelType == LevelType.MAP) { enabledMaps.get(game).add(name); } - // Load the world if sgm.yml exists - loadWorld(file.getName(), levelType); + if (levelType == LevelType.LEVEL) { + // Load the world if sgm.yml exists + loadWorld(file.getName(), levelType); + } } else { SGM.getInstance().getCLogger().log("Skipping directory " + file.getName() + ": sgm.yml not found."); } diff --git a/src/main/resources/guis/teampicker.xml b/src/main/resources/guis/teampicker.xml new file mode 100644 index 0000000..dbb4207 --- /dev/null +++ b/src/main/resources/guis/teampicker.xml @@ -0,0 +1,17 @@ + + + + + + + + + Red Team + + + Blue Team + + + \ No newline at end of file