/* * Copyright or © or Copr. Amaury Carrade (2014 - 2016) * * http://amaury.carrade.eu * * This software is governed by the CeCILL-B license under French law and * abiding by the rules of distribution of free software. You can use, * modify and/ or redistribute the software under the terms of the CeCILL-B * license as circulated by CEA, CNRS and INRIA at the following URL * "http://www.cecill.info". * * As a counterpart to the access to the source code and rights to copy, * modify and redistribute granted by the license, users are provided only * with a limited warranty and the software's author, the holder of the * economic rights, and the successive licensors have only limited * liability. * * In this respect, the user's attention is drawn to the risks associated * with loading, using, modifying and/or developing or reproducing the * software by the user in light of its specific status of free software, * that may mean that it is complicated to manipulate, and that also * therefore means that it is reserved for developers and experienced * professionals having in-depth computer knowledge. Users are therefore * encouraged to load and test the software's suitability as regards their * requirements in conditions enabling the security of their systems and/or * data to be ensured and, more generally, to use and operate it in the * same conditions as regards security. * * The fact that you are presently reading this means that you have had * knowledge of the CeCILL-B license and that you accept its terms. */ package eu.carrade.amaury.UHCReloaded.teams; import eu.carrade.amaury.UHCReloaded.UHCReloaded; import eu.carrade.amaury.UHCReloaded.UHConfig; import eu.carrade.amaury.UHCReloaded.gui.teams.TeamsSelectorGUI; import eu.carrade.amaury.UHCReloaded.gui.teams.editor.TeamEditDeleteGUI; import eu.carrade.amaury.UHCReloaded.gui.teams.editor.TeamEditGUI; import eu.carrade.amaury.UHCReloaded.gui.teams.editor.TeamEditMembersGUI; import fr.zcraft.zlib.components.configuration.ConfigurationParseException; import fr.zcraft.zlib.components.configuration.ConfigurationValueHandler; import fr.zcraft.zlib.components.gui.Gui; import fr.zcraft.zlib.components.i18n.I; import fr.zcraft.zlib.components.rawtext.RawText; import fr.zcraft.zlib.tools.PluginLogger; import fr.zcraft.zlib.tools.text.ActionBar; import fr.zcraft.zlib.tools.text.RawMessage; import org.bukkit.ChatColor; import org.bukkit.OfflinePlayer; import org.bukkit.entity.Player; import org.bukkit.inventory.meta.BannerMeta; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Random; import java.util.Set; public class TeamManager { private final int MAX_PLAYERS_PER_TEAM; private final UHCReloaded p; private final HashSet<UHTeam> teams = new HashSet<>(); public TeamManager(UHCReloaded plugin) { p = plugin; MAX_PLAYERS_PER_TEAM = UHConfig.TEAMS_OPTIONS.MAX_PLAYERS_PER_TEAM.get(); } /** * Is the given team registered? * * @param team The team. * @return {@code true} if the team is registered. */ public boolean isTeamRegistered(UHTeam team) { return teams.contains(team); } /** * Is the given team registered? * * @param name The name of the team. * @return {@code true} if the team is registered. */ public boolean isTeamRegistered(String name) { return getTeam(name) != null; } /** * Adds a team. * * @param color The color. * @param name The name of the team. * * @return The new team. * * @throws IllegalArgumentException if a team with the same name already exists. */ public UHTeam addTeam(TeamColor color, String name) { if (isTeamRegistered(name)) { throw new IllegalArgumentException("There is already a team named " + name + " registered!"); } UHTeam team = new UHTeam(name, generateColor(color)); teams.add(team); updateGUIs(); return team; } /** * Adds a team. A name is generated based on the color. * * @param color The color. * * @return The new team. * * @throws IllegalArgumentException if a team with the same name already exists. */ public UHTeam addTeam(TeamColor color) { color = generateColor(color); String teamName = color.toString().toLowerCase(); if (isTeamRegistered(teamName)) // Taken! { Random rand = new Random(); do { teamName = color.toString().toLowerCase() + rand.nextInt(1000); } while (isTeamRegistered(teamName)); } UHTeam team = new UHTeam(teamName, color); teams.add(team); updateGUIs(); return team; } /** * Adds a team from an UHTeam object. * * @param team The team. * @return The new team. * * @throws IllegalArgumentException if a team with the same name already exists. */ public UHTeam addTeam(UHTeam team) { if (isTeamRegistered(team)) { throw new IllegalArgumentException("There is already a team named " + team.getName() + " registered!"); } teams.add(team); updateGUIs(); return team; } /** * Deletes a team. * * @param team The team to delete. * @param dontNotify If true, the player will not be notified about the leave. * @return boolean True if a team was removed. */ public boolean removeTeam(UHTeam team, boolean dontNotify) { if (team != null) { if (dontNotify) { for (OfflinePlayer player : team.getPlayers()) { this.removePlayerFromTeam(player, true); } } team.deleteTeam(); } final boolean removed = teams.remove(team); updateGUIs(); return removed; } /** * Deletes a team. * * @param team The team to delete. * @return boolean True if a team was removed. */ public boolean removeTeam(UHTeam team) { return removeTeam(team, false); } /** * Deletes a team. * * @param name The name of the team to delete. * @return boolean True if a team was removed. */ public boolean removeTeam(String name) { return removeTeam(getTeam(name), false); } /** * Deletes a team. * * @param name The name of the team to delete. * @param dontNotify If true, the player will not be notified about the leave. * @return boolean True if a team was removed. */ public boolean removeTeam(String name, boolean dontNotify) { return removeTeam(getTeam(name), dontNotify); } /** * Adds a player to a team. * * @param teamName The team in which we adds the player. * @param player The player to add. * @throws IllegalArgumentException if the team does not exists. */ public void addPlayerToTeam(String teamName, OfflinePlayer player) { UHTeam team = getTeam(teamName); if (team == null) { throw new IllegalArgumentException("There isn't any team named" + teamName + " registered!"); } team.addPlayer(player); } /** * Removes a player from his team. * * @param player The player to remove. * @param dontNotify If true, the player will not be notified about the leave. */ public void removePlayerFromTeam(OfflinePlayer player, boolean dontNotify) { UHTeam team = getTeamForPlayer(player); if (team != null) { team.removePlayer(player, dontNotify); } } /** * Removes a player from his team. * * @param player The player to remove. */ public void removePlayerFromTeam(OfflinePlayer player) { removePlayerFromTeam(player, false); } /** * Removes all teams. * * @param dontNotify If true, the player will not be notified when they leave the destroyed team. */ public void reset(boolean dontNotify) { // 1: scoreboard reset for (UHTeam team : new HashSet<>(teams)) { this.removeTeam(team, dontNotify); } // 2: internal list reset teams.clear(); updateGUIs(); } /** * Removes all teams. */ public void reset() { reset(false); } /** * Sets the correct display name of a player, according to his team. * * @param offlinePlayer The player to colorize. */ public void colorizePlayer(OfflinePlayer offlinePlayer) { if (!UHConfig.COLORIZE_CHAT.get()) { return; } if (!offlinePlayer.isOnline()) { return; } Player player = (Player) offlinePlayer; UHTeam team = getTeamForPlayer(player); if (team == null) { player.setDisplayName(player.getName()); } else { player.setDisplayName(team.getColorOrWhite().toChatColor() + player.getName() + ChatColor.RESET); } } /** * Returns all the teams. * * @return The teams. */ public Set<UHTeam> getTeams() { return teams; } /** * Returns the maximal number of players in each team. * * @return The max. */ public int getMaxPlayersPerTeam() { return MAX_PLAYERS_PER_TEAM; } /** * Returns the UHTeam object of the team with the given name. * * @param name The name of the team. * @return The team, or null if the team does not exists. */ public UHTeam getTeam(String name) { for (UHTeam t : teams) { if (t.getName().equalsIgnoreCase(name)) { return t; } } return null; } /** * Gets a player's team. * * @param player The player. * @return The team of this player. */ public UHTeam getTeamForPlayer(OfflinePlayer player) { for (UHTeam t : teams) { if (t.containsPlayer(player.getUniqueId())) return t; } return null; } /** * Checks if two players are in the same team. * * @param player1 The first player. * @param player2 The second player * @return True if the players are in the same team, false else. */ public boolean inSameTeam(Player player1, Player player2) { return (getTeamForPlayer(player1).equals(getTeamForPlayer(player2))); } /** * Generates a color from the given color. * <p> * If the color is neither {@link TeamColor#RANDOM} nor {@code null}, returns the given color.<br /> * Else, generates a random unused (if possible) color. * @param color * @return */ public TeamColor generateColor(TeamColor color) { if (color != null && color != TeamColor.RANDOM) { return color; } // A list of the currently used colors. HashSet<TeamColor> availableColors = new HashSet<TeamColor>(Arrays.asList(TeamColor.values())); availableColors.remove(TeamColor.RANDOM); for (UHTeam team : getTeams()) { availableColors.remove(team.getColorOrWhite()); } if (availableColors.size() != 0) { return (TeamColor) availableColors.toArray()[(new Random()).nextInt(availableColors.size())]; } else { // length-1 so the RANDOM option is never selected. return TeamColor.values()[(new Random()).nextInt(TeamColor.values().length - 1)]; } } /** * Imports the teams from the configuration. * * @return The number of teams imported. */ public int importTeamsFromConfig() { int teamsCount = 0; for (String teamDescription : UHConfig.TEAMS) { try { //TODO: Sanitize UHTeam to allow it being loaded directly by the configuration. addTeam(handleTeamValue(teamDescription)); } catch (ConfigurationParseException ex) { PluginLogger.warning("Invalid team value : ''{0}''.", ex.getValue()); PluginLogger.warning("\tReason : {0}", ex.getMessage()); } } for(Entry<String, BannerMeta> bannerEntry : UHConfig.TEAM_BANNERS) { UHTeam team = getTeam(bannerEntry.getKey()); if(team == null) continue; team.setBanner(bannerEntry.getValue()); } return teamsCount; } /** * Displays a chat-based GUI (using tellraw formatting) to player to select a team. * <p> * Nothing is displayed if the player cannot use the /join command. * * @param player The receiver of the chat-GUI. */ public void displayTeamChooserChatGUI(Player player) { if (!player.hasPermission("uh.player.join.self")) return; if (p.getGameManager().isGameRunning()) { if (!p.getGameManager().isGameWithTeams()) { return; } } player.sendMessage(ChatColor.GRAY + "⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅"); if (p.getTeamManager().getTeams().size() != 0) { /// Invite displayed in the chat team selector player.sendMessage(I.t("{gold}Click on the names below to join a team")); boolean displayPlayers = UHConfig.TEAMS_OPTIONS.GUI.DISPLAY_PLAYERS_IN_TEAMS.get(); for (UHTeam team : p.getTeamManager().getTeams()) { String players = ""; if (displayPlayers) { String bullet = "\n - "; for (OfflinePlayer opl : team.getPlayers()) { if (!p.getGameManager().isGameRunning()) { players += bullet + opl.getName(); } else { if (p.getGameManager().isPlayerDead(opl.getUniqueId())) { /// Displayed in team tooltip of the chat team selector for a dead player players += bullet + I.t("{0} ({red}dead{reset})", opl.getName()); } else { /// Displayed in team tooltip of the chat team selector for an alive player players += bullet + I.t("{0} ({green}alive{reset})", opl.getName()); } } } } RawMessage.send(player, new RawText("") .then( MAX_PLAYERS_PER_TEAM != 0 /// Team count with max players (ex. [3/5]) followed in-game by the team name. {0} = current count, {1} = max. ? I.t("{gray}[{white}{0}{gray}/{white}{1}{gray}]", String.valueOf(team.getSize()), String.valueOf(MAX_PLAYERS_PER_TEAM)) /// Team count without max players (ex. [3]) followed in-game by the team name. {0} = current count. : I.t("{gray}[{white}{0}{gray}]", String.valueOf(team.getSize())) ) .hover(new RawText(I.tn("{0} player in this team", "{0} players in this team", team.getPlayers().size(), team.getPlayers().size()) + players)) .then(" ") .then(team.getName()) .color(team.getColorOrWhite().toChatColor()) .command("/join " + team.getName()) .style(team.containsPlayer(player) ? ChatColor.BOLD : null) .hover(new RawText( team.containsPlayer(player) /// Tooltip on the chat team selector GUI when the player is in the team. {0} = team display name. ? I.t("You are in the team {0}", team.getDisplayName()) /// Tooltip on the chat team selector GUI when the player is not in the team. {0} = team display name. : I.t("Click here to join the team {0}", team.getDisplayName()) )) .build() ); } if (p.getTeamManager().getTeamForPlayer(player) != null && player.hasPermission("uh.player.leave.self")) { RawMessage.send(player, new RawText(I.t("{darkred}[×] {red}Click here to leave your team")).command("/leave") ); } else { player.sendMessage(I.t("{gray}Run /join to display this again")); } } else { // No teams. player.sendMessage(I.t("{ce}There isn't any team available.")); } player.sendMessage(ChatColor.GRAY + "⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⋅"); } /** * Displays the team of the given player in his action bar. * If the player is not in a team, or the game is started, does nothing. * * @param player The player. */ public void displayTeamInActionBar(Player player) { UHTeam team = getTeamForPlayer(player); if (team == null) return; displayTeamInActionBar(player, team); } /** * Displays the team of the given player in his action bar. * If the player is not in a team, or the game is started, does nothing. * * Internal use when an instance of the team is accessible (avoids lookup). * * @param player The player. * @param team The team. */ void displayTeamInActionBar(Player player, UHTeam team) { if (!UHCReloaded.get().getGameManager().isGameStarted()) ActionBar.sendPermanentMessage(player, I.t("{gold}Your team: {0}", team.getDisplayName())); } /** * Updates the teams GUIs. Called when a team is created, deleted, or updated, * so the GUIs update in real time. */ void updateGUIs() { Gui.update(TeamsSelectorGUI.class); Gui.update(TeamEditGUI.class); Gui.update(TeamEditMembersGUI.class); Gui.update(TeamEditDeleteGUI.class); } @ConfigurationValueHandler static public TeamColor handleTeamColorValue(String value) throws ConfigurationParseException { TeamColor color = TeamColor.fromString(value); if(color == null) throw new ConfigurationParseException("Invalid team color name.", value); return color; } @ConfigurationValueHandler static public UHTeam handleTeamValue(String str) throws ConfigurationParseException { return handleTeamValue(Arrays.asList(str.split(","))); } @ConfigurationValueHandler static public UHTeam handleTeamValue(List list) throws ConfigurationParseException { if(list.size() < 1) throw new ConfigurationParseException("Not enough values, at least 1 required.", list); if(list.size() > 2) throw new ConfigurationParseException("Too many values, at most 2 (color, name) can be used.", list); if(list.size() == 1) { return new UHTeam(list.get(0).toString().trim(), handleTeamColorValue(list.get(0).toString())); } else { return new UHTeam(list.get(1).toString().trim(), handleTeamColorValue(list.get(0).toString())); } } @ConfigurationValueHandler static public UHTeam handleTeamValue(Map map) throws ConfigurationParseException { TeamColor color = map.containsKey("color") ? handleTeamColorValue(map.get("color").toString()) : TeamColor.RANDOM; Object name = map.containsKey("name") ? map.get("name") : map.get("color"); if(name == null) throw new ConfigurationParseException("Either color or name must be specified", map); return new UHTeam(name.toString(), color); } }