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