commit dfd880b935c184f0b3d3a85f350d363af485b329 Author: Matheus Date: Tue Jun 11 23:40:44 2024 -0400 upload diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..dcf923a --- /dev/null +++ b/.gitignore @@ -0,0 +1,36 @@ +# Eclipse stuff +.classpath +.project +.settings/ + +# netbeans +nbproject/ +nbactions.xml + +# we use maven! +build.xml + +# maven +target/ +dependency-reduced-pom.xml + +# vim +.*.sw[a-p] + +# various other potential build files +build/ +bin/ +dist/ +manifest.mf + +# Mac filesystem dust +.DS_Store/ +.DS_Store + +# intellij +*.iml +*.ipr +*.iws +.idea/ + +working-dir/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..e54544d --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +# Practice +Practice core - forked from Zonix Practice diff --git a/lib/Citizens-2.0.29-b2490.jar b/lib/Citizens-2.0.29-b2490.jar new file mode 100644 index 0000000..95800d7 Binary files /dev/null and b/lib/Citizens-2.0.29-b2490.jar differ diff --git a/lib/FastAsyncWorldEdit-bukkit-21.03.26-5ff3a9b-1286-22.3.9.jar b/lib/FastAsyncWorldEdit-bukkit-21.03.26-5ff3a9b-1286-22.3.9.jar new file mode 100644 index 0000000..53a3860 Binary files /dev/null and b/lib/FastAsyncWorldEdit-bukkit-21.03.26-5ff3a9b-1286-22.3.9.jar differ diff --git a/lib/Scandium.jar b/lib/Scandium.jar new file mode 100644 index 0000000..a1f159c Binary files /dev/null and b/lib/Scandium.jar differ diff --git a/lib/Shop.jar b/lib/Shop.jar new file mode 100644 index 0000000..d449bbb Binary files /dev/null and b/lib/Shop.jar differ diff --git a/lib/commonslibs-bukkit_1.jar b/lib/commonslibs-bukkit_1.jar new file mode 100644 index 0000000..c95dcd5 Binary files /dev/null and b/lib/commonslibs-bukkit_1.jar differ diff --git a/lib/lunar.jar b/lib/lunar.jar new file mode 100644 index 0000000..feb278e Binary files /dev/null and b/lib/lunar.jar differ diff --git a/lib/scala-spigot-server.jar b/lib/scala-spigot-server.jar new file mode 100644 index 0000000..70dff06 Binary files /dev/null and b/lib/scala-spigot-server.jar differ diff --git a/lib/spigot.jar b/lib/spigot.jar new file mode 100644 index 0000000..56a5475 Binary files /dev/null and b/lib/spigot.jar differ diff --git a/lib/worldedit-bukkit-6.1.7.2.jar b/lib/worldedit-bukkit-6.1.7.2.jar new file mode 100644 index 0000000..9411332 Binary files /dev/null and b/lib/worldedit-bukkit-6.1.7.2.jar differ diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..f04881d --- /dev/null +++ b/pom.xml @@ -0,0 +1,167 @@ + + + 4.0.0 + + com.solexgames.practice + practice + LATEST + + + + papermc + https://papermc.io/repo/repository/maven-public/ + + + jitpack.io + https://jitpack.io + + + dmulloy2-repo + https://repo.dmulloy2.net/repository/public/ + + + everything + https://repo.citizensnpcs.co/ + + + + + + org.projectlombok + lombok + 1.18.20 + provided + + + + com.solexgames.spigot + spigot-server + 1.8.8-R0.1-SNAPSHOT + system + ${basedir}/lib/spigot.jar + + + com.solexgames.core + scandium-bukkit + LATEST + system + ${basedir}/lib/Scandium.jar + + + com.solexgames.shop + shop-bukkit + LATEST + system + ${basedir}/lib/Shop.jar + + + io.papermc + paperlib + 1.0.6 + compile + + + com.moonsworth.api + lunar-client-api + LATEST + system + ${basedir}/lib/lunar.jar + + + + com.github.cryptomorin + XSeries + 8.2.0 + + + xyz.xenondevs + particle + 1.6.4 + + + + + com.google.code.gson + gson + 2.8.7 + + + com.github.NoSequel + ScoreboardAPI + d86c2c6d87 + + + com.sk89q + worldedit + 6.0.0-SNAPSHOT + system + ${basedir}/lib/worldedit-bukkit-6.1.7.2.jar + + + com.boydti + fawe-api + latest + system + ${basedir}/lib/FastAsyncWorldEdit-bukkit-21.03.26-5ff3a9b-1286-22.3.9.jar + + + com.comphenix.protocol + ProtocolLib + 4.6.0 + provided + + + org.mongodb + mongodb-driver + 3.12.9 + + + net.citizne + citizens + 2.0.25-SNAPSHOT + system + ${basedir}/lib/Citizens-2.0.29-b2490.jar + + + net.23 + qwoiufjwsodnmwso + 2.0.25-SNAPSHOT + system + ${basedir}/lib/commonslibs-bukkit_1.jar + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.8 + 1.8 + + + + org.apache.maven.plugins + maven-shade-plugin + 2.1 + + ${project.build.directory}/dependency-reduced-pom.xml + + + + package + + shade + + + + + + + + Practice + + diff --git a/src/main/java/com/solexgames/practice/Practice.java b/src/main/java/com/solexgames/practice/Practice.java new file mode 100644 index 0000000..1ae0ebc --- /dev/null +++ b/src/main/java/com/solexgames/practice/Practice.java @@ -0,0 +1,319 @@ +package com.solexgames.practice; + +import com.solexgames.core.CorePlugin; +import com.solexgames.lib.acf.ConditionFailedException; +import com.solexgames.lib.acf.PaperCommandManager; +import com.solexgames.lib.commons.command.context.CommonsPlayer; +import com.solexgames.practice.arena.Arena; +import com.solexgames.practice.board.BoardAdapter; +import com.solexgames.practice.commands.*; +import com.solexgames.practice.commands.administration.*; +import com.solexgames.practice.commands.duel.AcceptCommand; +import com.solexgames.practice.commands.duel.DuelCommand; +import com.solexgames.practice.commands.duel.SpectateCommand; +import com.solexgames.practice.commands.spawn.SpawnCommand; +import com.solexgames.practice.commands.time.TimeCommand; +import com.solexgames.practice.comphenix.EntityHider; +import com.solexgames.practice.ffa.FFAManager; +import com.solexgames.practice.kit.Kit; +import com.solexgames.practice.listeners.*; +import com.solexgames.practice.managers.*; +import com.solexgames.practice.runnable.ExpBarRunnable; +import com.solexgames.practice.runnable.LeaderboardUpdateRunnable; +import com.solexgames.practice.runnable.SaveDataRunnable; +import com.solexgames.practice.runnable.cache.StatusCache; +import com.solexgames.practice.settings.PracticeSettingsImpl; +import com.solexgames.practice.shop.category.KillMessageShopCategory; +import com.solexgames.practice.shop.category.MiscellaneousShopCategory; +import com.solexgames.practice.shop.data.KillMessageDataType; +import com.solexgames.practice.shop.data.NameColorDataType; +import com.solexgames.practice.shop.data.StatsResetDataType; +import com.solexgames.practice.shop.item.EffectShopItem; +import com.solexgames.practice.shop.data.EffectDataType; +import com.solexgames.practice.shop.item.message.KillMessageItem; +import com.solexgames.practice.shop.item.statreset.NameColorItem; +import com.solexgames.practice.shop.item.statreset.StatisticResetItem; +import com.solexgames.practice.util.CC; +import com.solexgames.practice.util.timer.TimerManager; +import com.solexgames.practice.util.timer.impl.EnderpearlTimer; +import com.solexgames.shop.Shop; +import com.solexgames.shop.category.impl.configurable.KillEffectShopCategory; +import io.github.nosequel.scoreboard.ScoreboardHandler; +import lombok.Getter; +import lombok.Setter; +import me.lucko.helper.Commands; +import me.lucko.helper.plugin.ExtendedJavaPlugin; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.entity.Player; + +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.logging.Logger; + +@Getter +@Setter +public class Practice extends ExtendedJavaPlugin { + + @Getter + private static Practice instance; + + private final Set builderSet = new HashSet<>(); + + private PracticeServerType currentType = PracticeServerType.DEVELOPMENT; + + private FollowManager followManager; + private SnapshotManager inventoryManager; + private EditorManager editorManager; + private PlayerManager playerManager; + private ArenaManager arenaManager; + private MatchManager matchManager; + private PartyManager partyManager; + private QueueManager queueManager; + private FunManager effectManager; + private ItemManager itemManager; + private KitManager kitManager; + private SpawnManager spawnManager; + private TournamentManager tournamentManager; + private ChunkManager chunkManager; + private TimerManager timerManager; + private FFAManager ffaManager; + + private EntityHider entityHider; + + private boolean rankedEnabled = true; + private boolean eventsEnabled = true; + + private String loadingString = "."; + + @Override + public void enable() { + instance = this; + + new MongoManager(); + + this.registerCommands(); + this.registerListeners(); + this.registerManagers(); + + // an alternative to bukkit's default entity hider system + this.entityHider = new EntityHider(this, EntityHider.Policy.BLACKLIST); + + this.getServer().getScheduler().runTaskTimerAsynchronously(this, () -> this.loadingString = + this.loadingString.equals(".") ? ".." : + this.loadingString.equals("..") ? "..." : ".", 10L, 10L); + + new ScoreboardHandler(this, new BoardAdapter(), 10L); + + CorePlugin.getInstance().addNewSettingHandler(new PracticeSettingsImpl()); + + this.getServer().getScheduler().runTaskTimerAsynchronously(this, new SaveDataRunnable(), 300L * 20L, 300L * 20L); + this.getServer().getScheduler().runTaskTimerAsynchronously(this, new ExpBarRunnable(), 2L, 2L); + + new StatusCache().start(); + + this.setupShopStuff(); + this.setupChallengeStuff(); + + final Runnable leaderboardRunnable = new LeaderboardUpdateRunnable(); + + Commands.create().assertPlayer().assertOp().handler(c -> { + leaderboardRunnable.run(); + + c.reply("Updated"); + }).register("updatelb"); + + CorePlugin.getInstance().getJedisManager().runCommand(jedis -> { + final String type = jedis.hget(PracticeConstants.JEDIS_PRACTICE_SETTING_CATEGORY, CorePlugin.getInstance().getServerName() + ":type"); + + if (type != null) { + this.currentType = PracticeServerType.valueOf(type); + + Logger.getGlobal().info("Fetched server type " + this.currentType.name()); + } + }); + + this.getServer().getScheduler().runTaskTimerAsynchronously(this, leaderboardRunnable, 0L, TimeUnit.SECONDS.toMillis(5L)); + } + + @Override + public void disable() { + this.arenaManager.saveArenas(); + this.kitManager.saveKits(); + + this.spawnManager.saveAsyncLocationsToConfig(); + } + + public boolean isKitPairedWithArena(Kit kit, Arena arena) { + return arena.isEnabled() && kit.isEnabled() && kit.getFlag().equals(arena.getFlag()); + } + + private void setupChallengeStuff() { + /*BarChallengesPlugin.getInstance().getChallengeHandler().getRegisteredChallenges().add(new TwentyFiveKillsChallenge()); + BarChallengesPlugin.getInstance().getDataTrackerHandler().getDataTrackerClasses().add(KillStreakDataTracker.class);*/ + + this.getServer().getPluginManager().registerEvents(new KillStreakDataTrackerListener(), this); + } + + private void setupShopStuff() { + // data types + Shop.getInstance().getDataTypeHandler().getDataTypes().add(new EffectDataType()); + Shop.getInstance().getDataTypeHandler().getDataTypes().add(new KillMessageDataType()); + Shop.getInstance().getDataTypeHandler().getDataTypes().add(new StatsResetDataType()); + Shop.getInstance().getDataTypeHandler().getDataTypes().add(new NameColorDataType()); + + // categories + Shop.getInstance().getCategoryHandler().getCategories().add(new KillEffectShopCategory()); + Shop.getInstance().getCategoryHandler().getCategories().add(new KillMessageShopCategory()); + Shop.getInstance().getCategoryHandler().getCategories().add(new MiscellaneousShopCategory()); + + // single items + Shop.getInstance().getItemHandler().getItems().add(new NameColorItem()); + Shop.getInstance().getItemHandler().getItems().add(new StatisticResetItem()); + + // effects + this.effectManager.getEffectList().stream().filter(effect -> effect.getPermission() != null).forEach(effect -> { + Shop.getInstance().getItemHandler().getItems().add(new EffectShopItem(effect, 250)); + }); + + // kill messages + this.effectManager.getMessageList().stream().filter(message -> !message.getName().equals("Normal")).forEach(message -> { + Shop.getInstance().getItemHandler().getItems().add(new KillMessageItem(message)); + }); + } + + private void registerCommands() { + new ArenaCommand(); + new KitCommand(); + + final PaperCommandManager acfCommandProcessor = new PaperCommandManager(this); + + acfCommandProcessor.enableUnstableAPI("help"); + + acfCommandProcessor.getCommandContexts().registerContext(CommonsPlayer.class, context -> { + final String nameRaw = context.popFirstArg(); + final String username = nameRaw.replace(":confirm", ""); + final Player bukkitPlayer = Bukkit.getPlayer(username); + + if (bukkitPlayer != null && bukkitPlayer.hasMetadata("vanished")) { + if (!context.getSender().hasPermission("scandium.staff")) { + throw new ConditionFailedException("No player with the name matching " + ChatColor.YELLOW + username + ChatColor.RED + " is online."); + } else { + if (!nameRaw.endsWith(":confirm")) { + throw new ConditionFailedException("That player's vanished, please add :confirm to the end of the user's name to confirm this action."); + } else { + return new CommonsPlayer(bukkitPlayer); + } + } + } else if (bukkitPlayer != null) { + return new CommonsPlayer(bukkitPlayer); + } else { + throw new ConditionFailedException("No player with the name matching " + ChatColor.YELLOW + username + ChatColor.RED + " is online."); + } + }); + + acfCommandProcessor.getCommandContexts().registerContext(UUID.class, c -> { + final String firstArg = c.popFirstArg(); + + try { + return UUID.fromString(firstArg); + } catch (Exception ignored) { + throw new ConditionFailedException(CC.YELLOW + firstArg + CC.RED + " is not a valid input."); + } + }); + + acfCommandProcessor.getCommandContexts().registerContext(Kit.class, c -> { + final String firstArg = c.popFirstArg(); + final Kit kit = this.kitManager.getKit(firstArg); + + if (kit == null) { + throw new ConditionFailedException("The kit by the name " + CC.YELLOW + firstArg + CC.RED + " does not exist."); + } + + return kit; + }); + + acfCommandProcessor.getCommandCompletions().registerAsyncCompletion("unvanished", c -> { + final List playerList = new ArrayList<>(); + + Bukkit.getOnlinePlayers().forEach(player -> { + if (!player.hasMetadata("vanished")) { + playerList.add(player.getName()); + } + }); + + return playerList; + }); + + Arrays.asList( + new ResetStatsCommand(), + new AcceptCommand(), + new PartyCommand(), + new DuelCommand(), + new SpectateCommand(), + new TimeCommand(), + new SpawnCommand(), + new InventoryCommand(), + new StatisticCommands(), + new LeaveFFACommand(), + new FollowCommand(), + new TournamentCommand(), + new ServerSettingsCommand(), + new SetSpawnCommand() + ).forEach(acfCommandProcessor::registerCommand); + } + + private void registerListeners() { + Arrays.asList( + new EntityListener(), + new PlayerListener(), + new MatchListener(), + new WorldListener(), + new MovementListener(), + new InventoryListener() + ).forEach(listener -> this.getServer().getPluginManager().registerEvents(listener, this)); + } + + public CompletableFuture fetchNitroReward(Player player) { + return CompletableFuture.supplyAsync(() -> { + final AtomicBoolean atomicBoolean = new AtomicBoolean(); + + CorePlugin.getInstance().getJedisManager().runCommand(jedis -> { + final String reward = jedis.hget(PracticeConstants.JEDIS_DISCORD_REWARD_CATEGORY, player.getUniqueId().toString()); + + atomicBoolean.set(reward != null); + }); + + return atomicBoolean.get(); + }); + } + + private void registerManagers() { + this.spawnManager = new SpawnManager(); + this.arenaManager = new ArenaManager(); + this.followManager = new FollowManager(); + this.chunkManager = new ChunkManager(); + this.editorManager = new EditorManager(); + this.itemManager = new ItemManager(); + this.kitManager = new KitManager(); + this.matchManager = new MatchManager(); + + this.effectManager = new FunManager(); + this.effectManager.setup(); + + this.partyManager = new PartyManager(); + this.playerManager = new PlayerManager(); + this.queueManager = new QueueManager(); + this.inventoryManager = new SnapshotManager(); + this.tournamentManager = new TournamentManager(); + this.timerManager = new TimerManager(this); + + if (this.timerManager.getTimer(EnderpearlTimer.class) == null) { + this.timerManager.registerTimer(new EnderpearlTimer()); + } + + this.ffaManager = new FFAManager(this.getSpawnManager().fetchSingleLocation("ffa").orElse(null), this.kitManager.getKit("NoDebuff")); + } +} diff --git a/src/main/java/com/solexgames/practice/PracticeConstants.java b/src/main/java/com/solexgames/practice/PracticeConstants.java new file mode 100644 index 0000000..82f339d --- /dev/null +++ b/src/main/java/com/solexgames/practice/PracticeConstants.java @@ -0,0 +1,72 @@ +package com.solexgames.practice; + +import com.google.gson.reflect.TypeToken; +import com.solexgames.practice.party.Party; +import com.solexgames.practice.player.PlayerData; +import com.solexgames.practice.state.PlayerStateAdapter; +import com.solexgames.practice.state.impl.*; +import com.solexgames.practice.util.CC; +import io.github.nosequel.scoreboard.element.ScoreboardElement; +import lombok.experimental.UtilityClass; +import org.bukkit.ChatColor; +import org.bukkit.entity.Player; + +import java.lang.reflect.Type; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * @author GrowlyX + * @since 7/29/2021 + */ + +@UtilityClass +public class PracticeConstants { + + public static final Type STRING_INTEGER_MAP_TYPE = new TypeToken>(){}.getType(); + public static final Type STRING_BOOLEAN_MAP_TYPE = new TypeToken>(){}.getType(); + + public static final String PARTY_PREFIX = ChatColor.AQUA + ChatColor.BOLD.toString() + "Party " + ChatColor.GRAY + ChatColor.BOLD + "»" + " "; + public static final String KIT_PREFIX = ChatColor.AQUA + ChatColor.BOLD.toString() + "Kit " + ChatColor.GRAY + ChatColor.BOLD + "»" + " "; + public static final String ARENA_PREFIX = ChatColor.AQUA + ChatColor.BOLD.toString() + "Arena " + ChatColor.GRAY + ChatColor.BOLD + "»" + " "; + public static final String TOURNAMENT_PREFIX = ChatColor.AQUA + ChatColor.BOLD.toString() + "Tournament " + ChatColor.GRAY + ChatColor.BOLD + "»" + " "; + + public static final String JEDIS_PRACTICE_SETTING_CATEGORY = "practice_settings"; + public static final String JEDIS_DISCORD_REWARD_CATEGORY = "discord_rewards"; + + public static final int DEFAULT_ELO = 750; + + public static final List CHANGELOG = Arrays.asList( + CC.GRAY + "Leaderboards " + CC.AQUA + "(/leaderboards)", + CC.GRAY + "Statistics " + CC.AQUA + "(/stats)", + CC.GRAY + "Guilds " + CC.AQUA + "(/guilds)", + CC.GRAY + "Friends " + CC.AQUA + "(/friends)", + CC.GRAY + "Win Streaks " + CC.AQUA + "(Top 10 win streaks viewable via /lb)", + CC.GRAY + "Ranked Divisions " + CC.AQUA + "(Learn more - discord.gg/nodebuff)" + ); + + public static final Map BOARD_ADAPTERS = new HashMap<>(); + + static { + BOARD_ADAPTERS.put("match", new MatchBoardAdapter()); + BOARD_ADAPTERS.put("party", new PartyBoardAdapter()); + BOARD_ADAPTERS.put("queue", new QueueBoardAdapter()); + BOARD_ADAPTERS.put("spawn", new SpawnBoardAdapter()); + BOARD_ADAPTERS.put("spectator", new SpectatorBoardAdapter()); + BOARD_ADAPTERS.put("editor", new EditorBoardAdapter()); + } + + public static void handleAdapter(String id, ScoreboardElement element, Player player, PlayerData playerData, Party party) { + final PlayerStateAdapter adapter = PracticeConstants.BOARD_ADAPTERS.get(id); + + if (adapter != null) { + final List stringList = adapter.handleBoard(player, playerData, party); + + for (final String line : stringList) { + element.add(CC.translate(line)); + } + } + } +} diff --git a/src/main/java/com/solexgames/practice/PracticeServerType.java b/src/main/java/com/solexgames/practice/PracticeServerType.java new file mode 100644 index 0000000..5b269b2 --- /dev/null +++ b/src/main/java/com/solexgames/practice/PracticeServerType.java @@ -0,0 +1,16 @@ +package com.solexgames.practice; + +/** + * @author GrowlyX + * @since 8/2/2021 + */ + +public enum PracticeServerType { + + DEVELOPMENT, + + PROD_WHITELISTED, + PROD_RELEASE_DAY, + PROD_PUBLIC + +} diff --git a/src/main/java/com/solexgames/practice/arena/Arena.java b/src/main/java/com/solexgames/practice/arena/Arena.java new file mode 100644 index 0000000..a2e9c7a --- /dev/null +++ b/src/main/java/com/solexgames/practice/arena/Arena.java @@ -0,0 +1,58 @@ +package com.solexgames.practice.arena; + +import com.solexgames.practice.arena.type.StandaloneArena; +import com.solexgames.practice.flags.Flag; +import com.solexgames.practice.location.impl.impl.AsyncLocation; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.Setter; +import org.bukkit.Material; + +import java.util.List; +import java.util.concurrent.ThreadLocalRandom; + +@Getter +@Setter +@AllArgsConstructor +@RequiredArgsConstructor +public class Arena { + + private final String name; + + private List standaloneArenas; + private List availableArenas; + + private AsyncLocation positionOne; + private AsyncLocation positionTwo; + + private AsyncLocation min; + private AsyncLocation max; + + private boolean enabled = false; + + private Flag flag = Flag.DEFAULT; + + private Material icon; + + public StandaloneArena getAvailableArena() { + if (!this.availableArenas.isEmpty()) { + final int randomInteger = ThreadLocalRandom.current().nextInt(this.availableArenas.size()); + final StandaloneArena arena = this.availableArenas.get(randomInteger); + + if (arena != null) { + return this.availableArenas.remove(randomInteger); + } + } + + return null; + } + + public void addStandaloneArena(StandaloneArena arena) { + this.standaloneArenas.add(arena); + } + + public void addAvailableArena(StandaloneArena arena) { + this.availableArenas.add(arena); + } +} diff --git a/src/main/java/com/solexgames/practice/arena/type/StandaloneArena.java b/src/main/java/com/solexgames/practice/arena/type/StandaloneArena.java new file mode 100644 index 0000000..afdd3ff --- /dev/null +++ b/src/main/java/com/solexgames/practice/arena/type/StandaloneArena.java @@ -0,0 +1,21 @@ +package com.solexgames.practice.arena.type; + +import com.solexgames.practice.location.impl.impl.AsyncLocation; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@AllArgsConstructor +@RequiredArgsConstructor +public class StandaloneArena { + + private AsyncLocation positionOne; + private AsyncLocation positionTwo; + + private AsyncLocation min; + private AsyncLocation max; + +} diff --git a/src/main/java/com/solexgames/practice/board/BoardAdapter.java b/src/main/java/com/solexgames/practice/board/BoardAdapter.java new file mode 100644 index 0000000..2485d1e --- /dev/null +++ b/src/main/java/com/solexgames/practice/board/BoardAdapter.java @@ -0,0 +1,70 @@ +package com.solexgames.practice.board; + +import com.solexgames.core.CorePlugin; +import com.solexgames.core.player.PotPlayer; +import com.solexgames.core.util.Color; +import com.solexgames.practice.Practice; +import com.solexgames.practice.PracticeConstants; +import com.solexgames.practice.party.Party; +import com.solexgames.practice.player.PlayerData; +import com.solexgames.practice.player.PlayerState; +import com.solexgames.practice.util.CC; +import io.github.nosequel.scoreboard.element.ScoreboardElement; +import io.github.nosequel.scoreboard.element.ScoreboardElementHandler; +import org.bukkit.entity.Player; + +/** + * @author GrowlyX + * @since 8/14/2021 + */ + +public class BoardAdapter implements ScoreboardElementHandler { + + @Override + public ScoreboardElement getElement(Player player) { + final ScoreboardElement element = new ScoreboardElement(); + + final PlayerData playerData = Practice.getInstance().getPlayerManager() + .getPlayerData(player.getUniqueId()); + + element.setTitle(CC.GOLD + CC.BOLD + "Practice"); + + if (playerData == null || !playerData.getOptions().isScoreboardEnabled()) { + return element; + } + + final Party party = Practice.getInstance().getPartyManager().getParty(player.getUniqueId()); + + element.add(CC.SB_LINE); + + final boolean queued = this.isState(playerData, PlayerState.QUEUE); + + if (this.isState(playerData, PlayerState.SPAWN) || queued) { + PracticeConstants.handleAdapter("spawn", element, player, playerData, party); + + if (queued) { + PracticeConstants.handleAdapter("queue", element, player, playerData, party); + } + + if (party != null) { + PracticeConstants.handleAdapter("party", element, player, playerData, party); + } + } else if (this.isState(playerData, PlayerState.FIGHTING)) { + PracticeConstants.handleAdapter("match", element, player, playerData, party); + } else if (this.isState(playerData, PlayerState.SPECTATING)) { + PracticeConstants.handleAdapter("spectator", element, player, playerData, party); + } else if (this.isState(playerData, PlayerState.EDITING)) { + PracticeConstants.handleAdapter("editor", element, player, playerData, party); + } + + element.add(" "); + element.add(CC.GOLD + "battle.land"); + element.add(CC.SB_LINE); + + return element; + } + + public boolean isState(PlayerData playerData, PlayerState playerState) { + return playerData.getPlayerState().equals(playerState); + } +} diff --git a/src/main/java/com/solexgames/practice/challenges/TenRankedWinsChallenge.java b/src/main/java/com/solexgames/practice/challenges/TenRankedWinsChallenge.java new file mode 100644 index 0000000..49b9668 --- /dev/null +++ b/src/main/java/com/solexgames/practice/challenges/TenRankedWinsChallenge.java @@ -0,0 +1,9 @@ +package com.solexgames.practice.challenges; + +/** + * @author GrowlyX + * @since 6/25/2021 + */ + +public class TenRankedWinsChallenge { +} diff --git a/src/main/java/com/solexgames/practice/challenges/TwentyFiveKillsChallenge.java b/src/main/java/com/solexgames/practice/challenges/TwentyFiveKillsChallenge.java new file mode 100644 index 0000000..c196298 --- /dev/null +++ b/src/main/java/com/solexgames/practice/challenges/TwentyFiveKillsChallenge.java @@ -0,0 +1,51 @@ +package com.solexgames.practice.challenges; + +import org.bukkit.ChatColor; + +import java.util.Collections; +import java.util.List; + +/** + * @author GrowlyX + * @since 6/25/2021 + */ + +public class TwentyFiveKillsChallenge /*implements DailyChallenge*/ {/* + + @Override + public String getName() { + return "25 Kills"; + } + + @Override + public String getGoal() { + return "Get another 15 kills"; + } + + @Override + public String[] getDescription() { + return new String[]{ + ChatColor.GRAY + "Get 25 kills on any ladders and any queues.", + ChatColor.GRAY + " ", + ChatColor.GRAY + "Note: Boxing, Archer, and Sumo kills may not", + ChatColor.GRAY + "be marked as a kill for challenges.", + }; + } + + @Override + public Integer getCompletionRequirement() { + return 25; + } + + @Override + public List getRewards() { + return Collections.singletonList( + new ExperienceReward(200) + ); + } + + @Override + public Class getTrackerType() { + return KillsDataTracker.class; + }*/ +} diff --git a/src/main/java/com/solexgames/practice/challenges/tracker/KillStreakDataTracker.java b/src/main/java/com/solexgames/practice/challenges/tracker/KillStreakDataTracker.java new file mode 100644 index 0000000..755737f --- /dev/null +++ b/src/main/java/com/solexgames/practice/challenges/tracker/KillStreakDataTracker.java @@ -0,0 +1,43 @@ +package com.solexgames.practice.challenges.tracker; + +import com.solexgames.practice.Practice; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import java.util.HashMap; +import java.util.Map; + +/** + * @author GrowlyX + * @since 7/30/2021 + */ + +@Getter +public class KillStreakDataTracker /*implements DataTracker*/ {/* + + private final Map kitKillStreaks = new HashMap<>(); + + public KillStreakDataTracker() { + Practice.getInstance().getKitManager().getKits().forEach(kit -> this.kitKillStreaks.putIfAbsent(kit.getName(), 0)); + } + + @Override + public String getName() { + return "KillStreaks"; + } + + @Override + public boolean canAchieve(Object o) { + return false; + } + + @Override + public boolean hasCompleted(Object o) { + return false; + } + + @Override + public String getStatusMessage(Object o) { + return ""; + }*/ +} diff --git a/src/main/java/com/solexgames/practice/commands/FollowCommand.java b/src/main/java/com/solexgames/practice/commands/FollowCommand.java new file mode 100644 index 0000000..dad5425 --- /dev/null +++ b/src/main/java/com/solexgames/practice/commands/FollowCommand.java @@ -0,0 +1,30 @@ +package com.solexgames.practice.commands; + +import com.solexgames.lib.acf.BaseCommand; +import com.solexgames.lib.acf.annotation.*; +import com.solexgames.lib.acf.bukkit.contexts.OnlinePlayer; +import com.solexgames.lib.commons.command.context.CommonsPlayer; +import com.solexgames.practice.Practice; +import org.bukkit.entity.Player; + +/** + * @author GrowlyX + * @since 7/10/2021 + */ + +@CommandAlias("follow") +@CommandPermission("practice.command.follow") +public class FollowCommand extends BaseCommand { + + @Default + @Syntax("") + @CommandCompletion("@unvanished") + public void onDefault(Player player, @Optional CommonsPlayer target) { + if (target == null) { + Practice.getInstance().getFollowManager().stopFollowing(player); + return; + } + + Practice.getInstance().getFollowManager().followPlayer(player, target.getPlayer()); + } +} diff --git a/src/main/java/com/solexgames/practice/commands/InventoryCommand.java b/src/main/java/com/solexgames/practice/commands/InventoryCommand.java new file mode 100644 index 0000000..d94f32b --- /dev/null +++ b/src/main/java/com/solexgames/practice/commands/InventoryCommand.java @@ -0,0 +1,33 @@ +package com.solexgames.practice.commands; + +import com.solexgames.lib.acf.BaseCommand; +import com.solexgames.lib.acf.ConditionFailedException; +import com.solexgames.lib.acf.annotation.CommandAlias; +import com.solexgames.lib.acf.annotation.Default; +import com.solexgames.lib.acf.annotation.Syntax; +import com.solexgames.practice.Practice; +import com.solexgames.practice.match.snapshot.InventorySnapshot; +import org.bukkit.entity.Player; + +import java.util.UUID; + +/** + * @author GrowlyX + * @since 5/17/2021 + */ + +@CommandAlias("inventory") +public class InventoryCommand extends BaseCommand { + + @Default + @Syntax("") + public void onDefault(Player player, UUID uuid) { + final InventorySnapshot snapshot = Practice.getInstance().getInventoryManager().getSnapshot(uuid); + + if (snapshot == null) { + throw new ConditionFailedException("That inventory does not exist, it probably expired."); + } else { + snapshot.openMenu(player); + } + } +} diff --git a/src/main/java/com/solexgames/practice/commands/LeaveFFACommand.java b/src/main/java/com/solexgames/practice/commands/LeaveFFACommand.java new file mode 100644 index 0000000..15db899 --- /dev/null +++ b/src/main/java/com/solexgames/practice/commands/LeaveFFACommand.java @@ -0,0 +1,32 @@ +package com.solexgames.practice.commands; + +import com.solexgames.lib.acf.BaseCommand; +import com.solexgames.lib.acf.ConditionFailedException; +import com.solexgames.lib.acf.annotation.CommandAlias; +import com.solexgames.lib.acf.annotation.Default; +import com.solexgames.practice.Practice; +import com.solexgames.practice.player.PlayerData; +import com.solexgames.practice.player.PlayerState; +import org.bukkit.entity.Player; + +import java.util.UUID; + +/** + * @author GrowlyX + * @since 6/9/2021 + */ + +@CommandAlias("leaveffa") +public class LeaveFFACommand extends BaseCommand { + + @Default + public void onDefault(Player player) { + final PlayerData playerData = Practice.getInstance().getPlayerManager().getPlayerData(player.getUniqueId()); + + if (playerData.getPlayerState() != PlayerState.FFA) { + throw new ConditionFailedException("You're not in FFA right now."); + } + + Practice.getInstance().getFfaManager().removePlayer(player); + } +} diff --git a/src/main/java/com/solexgames/practice/commands/PartyCommand.java b/src/main/java/com/solexgames/practice/commands/PartyCommand.java new file mode 100644 index 0000000..7ce5aa4 --- /dev/null +++ b/src/main/java/com/solexgames/practice/commands/PartyCommand.java @@ -0,0 +1,291 @@ +package com.solexgames.practice.commands; + +import com.solexgames.core.util.Color; +import com.solexgames.core.util.clickable.Clickable; +import com.solexgames.lib.acf.BaseCommand; +import com.solexgames.lib.acf.CommandHelp; +import com.solexgames.lib.acf.ConditionFailedException; +import com.solexgames.lib.acf.annotation.*; +import com.solexgames.lib.acf.bukkit.contexts.OnlinePlayer; +import com.solexgames.lib.commons.command.context.CommonsPlayer; +import com.solexgames.practice.Practice; +import com.solexgames.practice.PracticeConstants; +import com.solexgames.practice.party.Party; +import com.solexgames.practice.player.PlayerData; +import com.solexgames.practice.player.PlayerState; +import com.solexgames.practice.util.CC; +import net.md_5.bungee.api.chat.ClickEvent; +import org.apache.commons.lang3.time.DurationFormatUtils; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.entity.Player; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +@CommandAlias("party|p") +public class PartyCommand extends BaseCommand { + + private final Practice plugin = Practice.getInstance(); + + @Default + @HelpCommand + @Syntax("[page]") + public void onHelp(CommandHelp help) { + help.showHelp(); + } + + @Subcommand("create") + @Description("Creates a party.") + public void onCreate(Player player) { + final Party party = this.plugin.getPartyManager().getParty(player.getUniqueId()); + final PlayerData playerData = this.plugin.getPlayerManager().getPlayerData(player.getUniqueId()); + + if (party != null) { + throw new ConditionFailedException("You're already in a party."); + } else if (playerData.getPlayerState() != PlayerState.SPAWN) { + throw new ConditionFailedException("You must be at spawn to create a party."); + } else { + this.plugin.getPartyManager().createParty(player); + } + } + + @Subcommand("leave") + @Description("Leaves your party.") + public void onLeave(Player player) { + final Party party = this.plugin.getPartyManager().getParty(player.getUniqueId()); + final PlayerData playerData = this.plugin.getPlayerManager().getPlayerData(player.getUniqueId()); + + if (party == null) { + throw new ConditionFailedException("You're not in a party."); + } else if (playerData.getPlayerState() != PlayerState.SPAWN) { + throw new ConditionFailedException("You must be at spawn to leave your party."); + } else { + this.plugin.getPartyManager().leaveParty(player); + } + } + + @Syntax("") + @Subcommand("invite|inv") + @CommandCompletion("@unvanished") + @Description("Invites a player to your party.") + public void onInvite(Player player, CommonsPlayer target) { + final Party party = this.plugin.getPartyManager().getParty(player.getUniqueId()); + + if (party == null) { + throw new ConditionFailedException("You're not in a party."); + } else if (!this.plugin.getPartyManager().isLeader(player.getUniqueId())) { + throw new ConditionFailedException("You're not the leader of your party."); + } else if (this.plugin.getTournamentManager().getTournament(player.getUniqueId()) != null) { + throw new ConditionFailedException("You're currently in a tournament."); + } else if (party.isOpen()) { + throw new ConditionFailedException("The party's public! Tell " + CC.YELLOW + target.getPlayer().getName() + CC.RED + " to use /party join " + CC.YELLOW + Bukkit.getPlayer(party.getLeader()).getName() + CC.RED + " to join the party."); + } else if (party.getMembers().size() >= party.getLimit()) { + throw new ConditionFailedException("The party's full, no more players are able to join/be invited."); + } else { + if (party.getLeader() != player.getUniqueId()) { + throw new ConditionFailedException("You're not the leader of your party."); + } + + final PlayerData targetData = this.plugin.getPlayerManager().getPlayerData(player.getUniqueId()); + + if (target.getPlayer().getUniqueId() == player.getUniqueId()) { + throw new ConditionFailedException("You cannot invite yourself to a party."); + } else if (this.plugin.getPartyManager().getParty(target.getPlayer().getUniqueId()) != null) { + throw new ConditionFailedException(CC.YELLOW + target.getPlayer().getName() + CC.RED + " is already in a party."); + } else if (targetData.getPlayerState() != PlayerState.SPAWN) { + throw new ConditionFailedException(CC.YELLOW + target.getPlayer().getName() + CC.RED + " is not at spawn."); + } else if (this.plugin.getPartyManager().hasPartyInvite(target.getPlayer().getUniqueId(), player.getUniqueId())) { + throw new ConditionFailedException(CC.YELLOW + target.getPlayer().getName() + CC.RED + " has already received your invitation, please be patient."); + } else { + this.plugin.getPartyManager().createPartyInvite(player.getUniqueId(), target.getPlayer().getUniqueId()); + + final Clickable partyInvite = new Clickable(PracticeConstants.PARTY_PREFIX + player.getDisplayName() + CC.YELLOW + " has invited you to their party! " + CC.GREEN + CC.BOLD + "[Click to Accept]", + CC.GREEN + "Click to accept this party invite!", + "/party accept " + player.getName()); + + target.getPlayer().spigot().sendMessage(partyInvite.asComponents()); + party.broadcast(PracticeConstants.PARTY_PREFIX + target.getPlayer().getDisplayName() + Color.SECONDARY_COLOR + " has been invited to the party."); + } + } + } + + @Syntax("") + @Subcommand("join") + @CommandCompletion("@unvanished") + @Description("Joins a player's party.") + public void onJoin(Player player, CommonsPlayer target) { + final Party party = this.plugin.getPartyManager().getParty(player.getUniqueId()); + final PlayerData playerData = this.plugin.getPlayerManager().getPlayerData(player.getUniqueId()); + + if (party != null) { + throw new ConditionFailedException("You're already in a party."); + } else if (playerData.getPlayerState() != PlayerState.SPAWN) { + throw new ConditionFailedException("You must be at spawn to join a party."); + } else { + final Party targetParty = this.plugin.getPartyManager().getParty(target.getPlayer().getUniqueId()); + + if (targetParty == null || !targetParty.isOpen() || targetParty.getMembers().size() >= targetParty.getLimit()) { + throw new ConditionFailedException("You cannot join " + CC.YELLOW + target.getPlayer().getName() + "'s " + CC.RED + "party."); + } else { + this.plugin.getPartyManager().joinParty(targetParty.getLeader(), player); + } + } + } + + @Private + @Syntax("") + @Subcommand("accept") + @CommandCompletion("@unvanished") + public void onAccept(Player player, CommonsPlayer target) { + final Party party = this.plugin.getPartyManager().getParty(player.getUniqueId()); + final PlayerData playerData = this.plugin.getPlayerManager().getPlayerData(player.getUniqueId()); + + if (party != null) { + throw new ConditionFailedException("You're already in a party."); + } else if (playerData.getPlayerState() != PlayerState.SPAWN) { + throw new ConditionFailedException("You must be at spawn to join a party."); + } else { + Party targetParty = this.plugin.getPartyManager().getParty(target.getPlayer().getUniqueId()); + + if (targetParty == null) { + throw new ConditionFailedException(CC.YELLOW + target.getPlayer().getName() + CC.RED + " is not in a party."); + } else if (targetParty.getMembers().size() >= targetParty.getLimit()) { + throw new ConditionFailedException(CC.YELLOW + target.getPlayer().getName() + "'s" + CC.RED + " has reached its limit."); + } else if (!this.plugin.getPartyManager().hasPartyInvite(player.getUniqueId(), targetParty.getLeader())) { + throw new ConditionFailedException("You don't have any pending requests from " + CC.YELLOW + target.getPlayer().getName() + CC.RED + "."); + } else { + this.plugin.getPartyManager().joinParty(targetParty.getLeader(), player); + } + } + } + + @Syntax("") + @Subcommand("kick") + @CommandCompletion("@unvanished") + @Description("Kicks a player from your party.") + public void onKick(Player player, CommonsPlayer target) { + final Party party = this.plugin.getPartyManager().getParty(player.getUniqueId()); + + if (party == null) { + throw new ConditionFailedException("You're already in a party."); + } else { + if (party.getLeader() != player.getUniqueId()) { + throw new ConditionFailedException("You're not the leader of your party."); + } + + final Party targetParty = this.plugin.getPartyManager().getParty(target.getPlayer().getUniqueId()); + + if (targetParty == null || targetParty.getLeader() != party.getLeader()) { + throw new ConditionFailedException(CC.YELLOW + target.getPlayer().getName() + CC.RED + " is not in your party."); + } else { + this.plugin.getPartyManager().leaveParty(target.getPlayer()); + } + } + } + + @Syntax("[limit]") + @Subcommand("setlimit") + @Description("Sets your party's player limit.") + public void onSetLimit(Player player, Integer limit) { + final Party party = this.plugin.getPartyManager().getParty(player.getUniqueId()); + + if (party == null) { + throw new ConditionFailedException("You're already in a party."); + } else { + if (party.getLeader() != player.getUniqueId()) { + throw new ConditionFailedException("You're not the leader of your party."); + } + + if (limit > 30) { + throw new ConditionFailedException("You cannot set a limit higher than " + CC.YELLOW + "30" + CC.RED + "."); + } else { + party.setLimit(limit); + + player.sendMessage(PracticeConstants.PARTY_PREFIX + CC.YELLOW + "You've set the party limit to " + CC.AQUA + limit + CC.YELLOW + "."); + } + } + } + + @Subcommand("open|private|close") + @Description("Opens/closes your party.") + public void onPartyStatus(Player player) { + final Party party = this.plugin.getPartyManager().getParty(player.getUniqueId()); + + if (party == null) { + throw new ConditionFailedException("You're already in a party."); + } else { + if (party.getLeader() != player.getUniqueId()) { + throw new ConditionFailedException("You're not the leader of your party."); + } + + party.setOpen(!party.isOpen()); + party.broadcast(PracticeConstants.PARTY_PREFIX + (party.isOpen() ? + CC.GREEN + "Everyone can now join your party." : + CC.RED + "Only people who are invited can join your party now" + )); + } + } + + @Subcommand("announce|broadcast") + @Description("Broadcasts your party to the server.") + public void onPartyBroadcast(Player player) { + final Party party = this.plugin.getPartyManager().getParty(player.getUniqueId()); + final PlayerData playerData = this.plugin.getPlayerManager().getPlayerData(player.getUniqueId()); + + if (party == null) { + throw new ConditionFailedException("You're already in a party."); + } else { + if (party.getLeader() != player.getUniqueId()) { + throw new ConditionFailedException("You're not the leader of your party."); + } + + if (!player.hasPermission("practice.party.broadcast")) { + player.sendMessage(CC.RED + "You don't have permission to announce parties yet!"); + player.sendMessage(CC.RED + "Purchase " + CC.AQUA + "Platinum" + CC.RED + " to gain access to this command."); + return; + } + + if (playerData.getLastAnnouncement() + TimeUnit.SECONDS.toMillis(15L) > System.currentTimeMillis()) { + throw new ConditionFailedException("You must wait " + DurationFormatUtils.formatDurationWords(playerData.getLastAnnouncement() + TimeUnit.SECONDS.toMillis(15L) - System.currentTimeMillis(), true, true) + " as you have already announced the party."); + } + + playerData.setLastAnnouncement(System.currentTimeMillis()); + player.sendMessage(PracticeConstants.PARTY_PREFIX + CC.GREEN + "The party has been announced."); + + final Clickable clickable = new Clickable(PracticeConstants.PARTY_PREFIX + player.getDisplayName() + Color.SECONDARY_COLOR + " wants you to join their party! "); + clickable.add(CC.GREEN + CC.BOLD + "[Join]", CC.GREEN + "Click to join " + player.getDisplayName() + "'s" + CC.GREEN + " party.", "/p join " + player.getName(), ClickEvent.Action.RUN_COMMAND); + + Bukkit.getOnlinePlayers().forEach(player2 -> player2.spigot().sendMessage(clickable.asComponents())); + } + } + + @Syntax("[optional: player]") + @Subcommand("view|information|info") + @Description("View party information.") + public void onPartyBroadcast(Player player, @Optional CommonsPlayer target) { + final Party party = this.plugin.getPartyManager().getParty(target != null ? target.getPlayer().getUniqueId() : player.getUniqueId()); + + if (party == null) { + throw new ConditionFailedException(target == null ? "You're not in a party" : CC.YELLOW + target.getPlayer().getName() + CC.RED + " is not in a party."); + } else { + final List members = new ArrayList<>(party.getMembers()); + final String[] information = new String[]{ + CC.GRAY + CC.STRIKE_THROUGH + "----------------------------------------------------", + CC.DARK_AQUA + CC.BOLD + "Party Information:", + "", + CC.GRAY + "Members: " + members.stream() + .map(this.plugin.getServer()::getPlayer) + .map(player1 -> (player1.getUniqueId().equals(party.getLeader()) ? CC.GRAY + "*" : "") + player1.getDisplayName()) + .collect(Collectors.joining(ChatColor.WHITE + ", ")), + CC.GRAY + "Status: " + CC.AQUA + (party.isOpen() ? CC.GREEN + "Public" : CC.PINK + "Private"), + CC.GRAY + CC.STRIKE_THROUGH + "----------------------------------------------------" + }; + + player.sendMessage(information); + } + } +} diff --git a/src/main/java/com/solexgames/practice/commands/StatisticCommands.java b/src/main/java/com/solexgames/practice/commands/StatisticCommands.java new file mode 100644 index 0000000..b54778a --- /dev/null +++ b/src/main/java/com/solexgames/practice/commands/StatisticCommands.java @@ -0,0 +1,32 @@ +package com.solexgames.practice.commands; + +import com.solexgames.lib.acf.BaseCommand; +import com.solexgames.lib.acf.annotation.CommandAlias; +import com.solexgames.lib.acf.annotation.CommandCompletion; +import com.solexgames.lib.acf.annotation.Optional; +import com.solexgames.lib.acf.annotation.Syntax; +import com.solexgames.lib.acf.bukkit.contexts.OnlinePlayer; +import com.solexgames.lib.commons.command.context.CommonsPlayer; +import com.solexgames.practice.menu.LeaderboardsMenu; +import com.solexgames.practice.menu.StatisticsMenu; +import org.bukkit.entity.Player; + +/** + * @author GrowlyX + * @since 5/22/2021 + */ + +public class StatisticCommands extends BaseCommand { + + @CommandAlias("leaderboards|lb|lbs") + public void onLeaderboards(Player player) { + new LeaderboardsMenu().openMenu(player); + } + + @Syntax("[optional: ]") + @CommandAlias("statistics|stats") + @CommandCompletion("@unvanished") + public void onStats(Player player, @Optional CommonsPlayer target) { + new StatisticsMenu(target == null ? player : target.getPlayer()).openMenu(player); + } +} diff --git a/src/main/java/com/solexgames/practice/commands/administration/ArenaCommand.java b/src/main/java/com/solexgames/practice/commands/administration/ArenaCommand.java new file mode 100644 index 0000000..64e769b --- /dev/null +++ b/src/main/java/com/solexgames/practice/commands/administration/ArenaCommand.java @@ -0,0 +1,250 @@ +package com.solexgames.practice.commands.administration; + +import com.solexgames.core.command.BaseCommand; +import com.solexgames.core.command.annotation.Command; +import com.solexgames.core.util.Color; +import com.solexgames.core.util.builder.PageListBuilder; +import com.solexgames.practice.Practice; +import com.solexgames.practice.arena.Arena; +import com.solexgames.practice.flags.Flag; +import com.solexgames.practice.location.impl.impl.AsyncLocation; +import com.solexgames.practice.menu.management.ArenaManagementMainMenu; +import com.solexgames.practice.runnable.ArenaCommandRunnable; +import com.solexgames.practice.util.CC; +import org.bukkit.ChatColor; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +@Command(label = "arena") +public class ArenaCommand extends BaseCommand { + + private static final String NO_ARENA = CC.RED + "That arena doesn't exist!"; + private final Practice plugin = Practice.getInstance(); + + public ArenaCommand() { + super(Practice.getInstance()); + } + + @Override + public boolean command(CommandSender sender, String alias, String[] args) { + if (!(sender instanceof Player)) { + return true; + } + + Player player = (Player) sender; + + if (!player.hasPermission("practice.admin")) { + player.sendMessage(this.NO_PERMISSION); + return true; + } + + if (args.length == 0) { + this.getHelpMessage(1, sender, + "/arena list", + "/arena save", + "/arena menu", + "/arena icon ", + "/arena create ", + "/arena a ", + "/arena b ", + "/arena min ", + "/arena max ", + "/arena toggle ", + "/arena flag ", + "/arena reload" + ); + + return false; + } + + if (args[0].equalsIgnoreCase("list")) { + final PageListBuilder listBuilder = new PageListBuilder(50, "Available arenas"); + final List fancyList = this.plugin.getArenaManager().getArenas().values().stream() + .map(arena -> CC.AQUA + arena.getName() + CC.GRAY + "(" + (arena.isEnabled() ? CC.GREEN + "Enabled" : CC.RED + "Disabled") + CC.GRAY + ")").collect(Collectors.toList()); + + listBuilder.display(sender, 1, fancyList); + return true; + } + + if (args[0].equalsIgnoreCase("menu")) { + new ArenaManagementMainMenu().openMenu(player); + return true; + } + + if (args.length < 2) { + this.getHelpMessage(1, sender, + "/arena list", + "/arena save", + "/arena menu", + "/arena create ", + "/arena a ", + "/arena b ", + "/arena min ", + "/arena max ", + "/arena toggle ", + "/arena flag ", + "/arena reload" + ); + return true; + } + + Arena arena = this.plugin.getArenaManager().getArena(args[1]); + + switch (args[0].toLowerCase()) { + case "create": + if (arena == null) { + this.plugin.getArenaManager().createArena(args[1]); + sender.sendMessage(CC.GREEN + "Successfully created arena " + args[1] + "."); + } else { + sender.sendMessage(CC.RED + "That arena already exists!"); + } + break; + case "delete": + if (arena != null) { + this.plugin.getArenaManager().deleteArena(args[1]); + sender.sendMessage(CC.GREEN + "Successfully deleted arena " + args[1] + "."); + } else { + sender.sendMessage(ArenaCommand.NO_ARENA); + } + break; + case "icon": + if (arena != null) { + if (player.getItemInHand().getType() != Material.AIR) { + arena.setIcon(player.getItemInHand().getType()); + + sender.sendMessage(CC.GREEN + "Successfully set icon for kit " + args[1] + "."); + } else { + player.sendMessage(CC.RED + "You must be holding an item to set the kit icon!"); + } + } else { + sender.sendMessage(ArenaCommand.NO_ARENA); + } + break; + case "a": + if (arena != null) { + Location location = player.getLocation(); + + if (args.length < 3 || !args[2].equalsIgnoreCase("-e")) { + location.setX(location.getBlockX() + 0.5D); + location.setY(location.getBlockY() + 3.0D); + location.setZ(location.getBlockZ() + 0.5D); + } + + arena.setPositionOne(AsyncLocation.of(location)); + sender.sendMessage(CC.GREEN + "Successfully set position A for arena " + args[1] + "."); + } else { + sender.sendMessage(ArenaCommand.NO_ARENA); + } + break; + case "b": + if (arena != null) { + Location location = player.getLocation(); + + if (args.length < 3 || !args[2].equalsIgnoreCase("-e")) { + location.setX(location.getBlockX() + 0.5D); + location.setY(location.getBlockY() + 3.0D); + location.setZ(location.getBlockZ() + 0.5D); + } + arena.setPositionTwo(AsyncLocation.of(location)); + sender.sendMessage(CC.GREEN + "Successfully set position B for arena " + args[1] + "."); + } else { + sender.sendMessage(ArenaCommand.NO_ARENA); + } + break; + case "min": + if (arena != null) { + arena.setMin(AsyncLocation.of(player.getLocation())); + sender.sendMessage(CC.GREEN + "Successfully set minimum position for arena " + args[1] + "."); + } else { + sender.sendMessage(ArenaCommand.NO_ARENA); + } + break; + case "max": + if (arena != null) { + arena.setMax(AsyncLocation.of(player.getLocation())); + sender.sendMessage(CC.GREEN + "Successfully set maximum position for arena " + args[1] + "."); + } else { + sender.sendMessage(ArenaCommand.NO_ARENA); + } + break; + case "toggle": + if (arena != null) { + arena.setEnabled(!arena.isEnabled()); + sender.sendMessage(arena.isEnabled() ? CC.GREEN + "Successfully enabled arena " + args[1] + "." : + CC.RED + "Successfully disabled arena " + args[1] + "."); + } else { + sender.sendMessage(ArenaCommand.NO_ARENA); + } + break; + case "generate": + if (arena != null) { + if (args.length < 3) { + sender.sendMessage(this.getUsageMessage("flag", " ")); + } + if (args.length == 3) { + int arenas = Integer.parseInt(args[2]); + + this.plugin.getServer().getScheduler().runTask(this.plugin, new ArenaCommandRunnable(this.plugin, arena, arenas)); + } + } else { + sender.sendMessage(ArenaCommand.NO_ARENA); + } + break; + case "flag": + if (arena != null) { + if (args.length < 3) { + sender.sendMessage(this.getUsageMessage("flag", " ")); + } + if (args.length == 3) { + final String flagString = args[2]; + + try { + final Flag flag = Flag.valueOf(flagString); + + arena.setFlag(flag); + + sender.sendMessage(CC.GREEN + "Updated the flag to " + flag.name()); + } catch (Exception ignored) { + sender.sendMessage(ChatColor.RED + "Invalid flag, here're some below:"); + sender.sendMessage(Color.SECONDARY_COLOR + Arrays.stream(Flag.values()).map(Flag::name).collect(Collectors.joining(ChatColor.WHITE + ", " + Color.SECONDARY_COLOR))); + } + } + } else { + sender.sendMessage(ArenaCommand.NO_ARENA); + } + break; + case "save": + this.plugin.getArenaManager().reloadArenas(); + sender.sendMessage(CC.GREEN + "Successfully reloaded the arenas."); + break; + default: + try { + int page = Integer.parseInt(args[0]); + + this.getHelpMessage(page, sender, + "/arena list", + "/arena save", + "/arena create ", + "/arena a ", + "/arena b ", + "/arena min ", + "/arena max ", + "/arena toggle ", + "/arena flag ", + "/arena reload" + ); + } catch (Exception ignored) { + sender.sendMessage(this.usageMessage); + } + break; + } + + return true; + } +} diff --git a/src/main/java/com/solexgames/practice/commands/administration/KitCommand.java b/src/main/java/com/solexgames/practice/commands/administration/KitCommand.java new file mode 100644 index 0000000..11f04ba --- /dev/null +++ b/src/main/java/com/solexgames/practice/commands/administration/KitCommand.java @@ -0,0 +1,265 @@ +package com.solexgames.practice.commands.administration; + +import com.solexgames.core.command.BaseCommand; +import com.solexgames.core.command.annotation.Command; +import com.solexgames.core.util.Color; +import com.solexgames.core.util.builder.PageListBuilder; +import com.solexgames.practice.Practice; +import com.solexgames.practice.flags.Flag; +import com.solexgames.practice.kit.Kit; +import com.solexgames.practice.util.CC; +import com.solexgames.practice.util.ItemUtil; +import org.bukkit.ChatColor; +import org.bukkit.GameMode; +import org.bukkit.Material; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +@Command(label = "kit") +public class KitCommand extends BaseCommand { + + private static final String NO_KIT = CC.RED + "That kit doesn't exist!"; + + private final Practice plugin = Practice.getInstance(); + + public KitCommand() { + super(Practice.getInstance()); + } + + @Override + public boolean command(CommandSender sender, String alias, String[] args) { + if (!(sender instanceof Player)) { + return true; + } + + Player player = (Player) sender; + + if (!player.hasPermission("practice.admin")) { + player.sendMessage(CC.RED + "You do not have permission to use that command."); + return true; + } + + if (args.length == 0) { + this.getHelpMessage(1, sender, + "/kit list", + "/kit create ", + "/kit delete ", + "/kit enable ", + "/kit disable ", + "/kit setinv ", + "/kit getinv ", + "/kit seteditinv ", + "/kit geteditinv ", + "/kit ranked ", + "/kit icon ", + "/kit flag ", + "/kit queue " + ); + + return true; + } + + if (args.length == 1) { + try { + final int page = Integer.parseInt(args[0]); + + this.getHelpMessage(page, sender, + "/kit list", + "/kit create ", + "/kit delete ", + "/kit enable ", + "/kit disable ", + "/kit setinv ", + "/kit getinv ", + "/kit seteditinv ", + "/kit geteditinv ", + "/kit ranked ", + "/kit icon ", + "/kit flag " + ); + } catch (Exception ignored) { + if (args[0].equals("list")) { + final PageListBuilder listBuilder = new PageListBuilder(50, "Available kits"); + final List fancyList = this.plugin.getKitManager().getKits().stream() + .map(kit -> CC.AQUA + kit.getName() + CC.GRAY + "(" + (kit.isEnabled() ? CC.GREEN + "Enabled" : CC.RED + "Disabled") + CC.GRAY + ")").collect(Collectors.toList()); + + listBuilder.display(sender, 1, fancyList); + } + } + + return true; + } + + Kit kit = this.plugin.getKitManager().getKit(args[1]); + + switch (args[0].toLowerCase()) { + case "create": + if (kit == null) { + this.plugin.getKitManager().createKit(args[1]); + sender.sendMessage(CC.GREEN + "Successfully created kit " + args[1] + "."); + } else { + sender.sendMessage(CC.RED + "That kit already exists!"); + } + break; + case "delete": + if (kit != null) { + this.plugin.getKitManager().deleteKit(args[1]); + sender.sendMessage(CC.GREEN + "Successfully deleted kit " + args[1] + "."); + } else { + sender.sendMessage(KitCommand.NO_KIT); + } + break; + case "disable": + case "enable": + if (kit != null) { + kit.setEnabled(!kit.isEnabled()); + sender.sendMessage(kit.isEnabled() ? CC.GREEN + "Successfully enabled kit " + args[1] + "." : + CC.RED + "Successfully disabled kit " + args[1] + "."); + } else { + sender.sendMessage(KitCommand.NO_KIT); + } + break; + case "flag": + if (kit != null) { + if (args.length < 3) { + sender.sendMessage(this.getUsageMessage("flag", " ")); + } + if (args.length == 3) { + final String flagString = args[2]; + + try { + final Flag flag = Flag.valueOf(flagString); + + kit.setFlag(flag); + + sender.sendMessage(CC.GREEN + "Updated the flag to " + flag.name()); + } catch (Exception ignored) { + sender.sendMessage(ChatColor.RED + "Invalid flag, here're some below:"); + sender.sendMessage(Color.SECONDARY_COLOR + Arrays.stream(Flag.values()).map(Flag::name).collect(Collectors.joining(ChatColor.WHITE + ", " + Color.SECONDARY_COLOR))); + } + } + } else { + sender.sendMessage(NO_KIT); + } + break; + case "queue": + if (kit != null) { + if (args.length < 3) { + sender.sendMessage(this.getUsageMessage("queue", " ")); + } + if (args.length == 3) { + final String flagString = args[2]; + + try { + final int integer = Integer.parseInt(flagString); + kit.setQueueMenu(integer); + + sender.sendMessage(CC.GREEN + "Updated the queue pos to " + integer); + } catch (Exception ignored) { + sender.sendMessage(ChatColor.RED + "Invalid int"); + } + } + } else { + sender.sendMessage(NO_KIT); + } + break; + case "ranked": + if (kit != null) { + kit.setRanked(!kit.isRanked()); + sender.sendMessage( + kit.isRanked() ? CC.GREEN + "Successfully enabled ranked mode for kit " + args[1] + "." + : CC.RED + "Successfully disabled ranked mode for kit " + args[1] + "."); + } else { + sender.sendMessage(KitCommand.NO_KIT); + } + break; + case "icon": + if (kit != null) { + if (player.getItemInHand().getType() != Material.AIR) { + ItemStack icon = ItemUtil.renameItem(player.getItemInHand().clone(), CC.GREEN + kit.getName()); + + kit.setIcon(icon); + + sender.sendMessage(CC.GREEN + "Successfully set icon for kit " + args[1] + "."); + } else { + player.sendMessage(CC.RED + "You must be holding an item to set the kit icon!"); + } + } else { + sender.sendMessage(KitCommand.NO_KIT); + } + break; + case "setinv": + if (kit != null) { + player.updateInventory(); + + kit.setContents(player.getInventory().getContents()); + kit.setArmor(player.getInventory().getArmorContents()); + + sender.sendMessage(CC.GREEN + "Successfully set kit contents for " + args[1] + "."); + } else { + sender.sendMessage(KitCommand.NO_KIT); + } + break; + case "getinv": + if (kit != null) { + player.getInventory().setContents(kit.getContents()); + player.getInventory().setArmorContents(kit.getArmor()); + player.updateInventory(); + + sender.sendMessage(CC.GREEN + "Successfully retrieved kit contents from " + args[1] + "."); + } else { + sender.sendMessage(KitCommand.NO_KIT); + } + break; + case "seteditinv": + if (kit != null) { + if (player.getGameMode() == GameMode.CREATIVE) { + sender.sendMessage(CC.RED + "You can't set item contents in creative mode!"); + } else { + player.updateInventory(); + + kit.setKitEditContents(player.getInventory().getContents()); + + sender.sendMessage(CC.GREEN + "Successfully set edit kit contents for " + args[1] + "."); + } + } else { + sender.sendMessage(KitCommand.NO_KIT); + } + break; + case "geteditinv": + if (kit != null) { + player.getInventory().setContents(kit.getKitEditContents()); + player.updateInventory(); + sender.sendMessage(CC.GREEN + "Successfully retrieved edit kit contents from " + args[1] + "."); + } else { + sender.sendMessage(KitCommand.NO_KIT); + } + break; + default: + this.getHelpMessage(1, sender, + "/kit list", + "/kit create ", + "/kit delete ", + "/kit enable ", + "/kit disable ", + "/kit setinv ", + "/kit getinv ", + "/kit seteditinv ", + "/kit geteditinv ", + "/kit ranked ", + "/kit icon ", + "/kit flag " + ); + break; + } + if (kit != null) { + plugin.getKitManager().saveKits(); + } + return true; + } +} diff --git a/src/main/java/com/solexgames/practice/commands/administration/ResetStatsCommand.java b/src/main/java/com/solexgames/practice/commands/administration/ResetStatsCommand.java new file mode 100644 index 0000000..9b8159a --- /dev/null +++ b/src/main/java/com/solexgames/practice/commands/administration/ResetStatsCommand.java @@ -0,0 +1,51 @@ +package com.solexgames.practice.commands.administration; + +import com.solexgames.core.util.Color; +import com.solexgames.lib.acf.BaseCommand; +import com.solexgames.lib.acf.annotation.*; +import com.solexgames.lib.acf.bukkit.contexts.OnlinePlayer; +import com.solexgames.lib.commons.command.context.CommonsPlayer; +import com.solexgames.practice.Practice; +import com.solexgames.practice.PracticeConstants; +import com.solexgames.practice.kit.Kit; +import com.solexgames.practice.player.PlayerData; +import com.solexgames.practice.util.CC; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +/** + * @author GrowlyX + * @since 5/17/2021 + */ + +@CommandAlias("resetstats|statsreset") +@CommandPermission("practice.command.resetstats") +public class ResetStatsCommand extends BaseCommand { + + @Default + @Syntax("") + @CommandCompletion("@unvanished") + public void onDefault(CommandSender sender, CommonsPlayer target, @Optional Kit optionalKit) { + final PlayerData playerData = Practice.getInstance().getPlayerManager().getPlayerData(target.getPlayer().getUniqueId()); + + if (optionalKit != null) { + this.wipeStatisticsFor(playerData, optionalKit); + return; + } + + for (final Kit kit : Practice.getInstance().getKitManager().getKits()) { + this.wipeStatisticsFor(playerData, kit); + } + + sender.sendMessage(Color.SECONDARY_COLOR + "You've wiped " + CC.AQUA + target.getPlayer().getName() + "'s" + Color.SECONDARY_COLOR + " statistics."); + } + + public void wipeStatisticsFor(PlayerData playerData, Kit kit) { + playerData.setElo(kit.getName(), PracticeConstants.DEFAULT_ELO); + + playerData.setLosses(kit.getName(), 0); + playerData.setWins(kit.getName(), 0); + + playerData.setHighestKillStreak(kit.getName(), 0); + } +} diff --git a/src/main/java/com/solexgames/practice/commands/administration/ServerSettingsCommand.java b/src/main/java/com/solexgames/practice/commands/administration/ServerSettingsCommand.java new file mode 100644 index 0000000..6fefa0d --- /dev/null +++ b/src/main/java/com/solexgames/practice/commands/administration/ServerSettingsCommand.java @@ -0,0 +1,70 @@ +package com.solexgames.practice.commands.administration; + +import com.solexgames.core.CorePlugin; +import com.solexgames.lib.acf.BaseCommand; +import com.solexgames.lib.acf.annotation.CommandAlias; +import com.solexgames.lib.acf.annotation.CommandPermission; +import com.solexgames.lib.acf.annotation.Syntax; +import com.solexgames.practice.Practice; +import com.solexgames.practice.PracticeConstants; +import com.solexgames.practice.PracticeServerType; +import com.solexgames.practice.util.CC; +import org.bukkit.ChatColor; +import org.bukkit.entity.Player; + +/** + * @author GrowlyX + * @since 5/17/2021 + */ + +public class ServerSettingsCommand extends BaseCommand { + + @CommandAlias("toggleranked") + @CommandPermission("practice.command.ranked.toggle") + public void onRankedToggle(Player player) { + Practice.getInstance().setRankedEnabled(!Practice.getInstance().isRankedEnabled()); + + player.sendMessage(Practice.getInstance().isRankedEnabled() ? + CC.GREEN + "You've enabled the ranked queue." : + CC.RED + "You've disabled the ranked queue." + ); + } + + @CommandAlias("toggleevents") + @CommandPermission("practice.command.event.toggle") + public void onEventToggle(Player player) { + Practice.getInstance().setEventsEnabled(!Practice.getInstance().isEventsEnabled()); + + player.sendMessage(Practice.getInstance().isEventsEnabled() ? + CC.GREEN + "You've enabled event hosting." : + CC.RED + "You've disabled event hosting." + ); + } + + @Syntax("") + @CommandAlias("practicetype") + @CommandPermission("practice.command.practice.type") + public void onPracticeType(Player player, PracticeServerType serverType) { + Practice.getInstance().setCurrentType(serverType); + + CorePlugin.getInstance().getJedisManager().runCommand(jedis -> jedis.hset(PracticeConstants.JEDIS_PRACTICE_SETTING_CATEGORY, CorePlugin.getInstance().getServerName() + ":type", serverType.name())); + + player.sendMessage(CC.GREEN + "You've updated this server's type to " + CC.YELLOW + serverType.name() + CC.GREEN + "."); + } + + @CommandAlias("build") + @CommandPermission("practice.command.build") + public void onBuildToggle(Player player) { + final boolean builder = Practice.getInstance().getBuilderSet().contains(player); + + if (builder) { + player.sendMessage(ChatColor.RED + "You've been removed from build mode."); + + Practice.getInstance().getBuilderSet().remove(player); + } else { + player.sendMessage(ChatColor.GREEN + "You've been added to build mode."); + + Practice.getInstance().getBuilderSet().add(player); + } + } +} diff --git a/src/main/java/com/solexgames/practice/commands/administration/SetSpawnCommand.java b/src/main/java/com/solexgames/practice/commands/administration/SetSpawnCommand.java new file mode 100644 index 0000000..3784bac --- /dev/null +++ b/src/main/java/com/solexgames/practice/commands/administration/SetSpawnCommand.java @@ -0,0 +1,116 @@ +package com.solexgames.practice.commands.administration; + +import com.solexgames.core.util.clickable.Clickable; +import com.solexgames.lib.acf.BaseCommand; +import com.solexgames.lib.acf.CommandHelp; +import com.solexgames.lib.acf.ConditionFailedException; +import com.solexgames.lib.acf.annotation.*; +import com.solexgames.practice.Practice; +import com.solexgames.practice.location.impl.impl.AsyncLocation; +import com.solexgames.practice.managers.SpawnManager; +import com.solexgames.practice.util.CC; +import io.papermc.lib.PaperLib; +import net.md_5.bungee.api.chat.ClickEvent; +import org.bukkit.Location; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * @author GrowlyX + * @since 8/6/2021 + */ + +@CommandAlias("location") +@CommandPermission("practice.command.location") +public class SetSpawnCommand extends BaseCommand { + + @Default + @HelpCommand + @Syntax("[page]") + public void onHelp(CommandHelp help) { + help.showHelp(); + } + + @Subcommand("set|s") + @Syntax(" ") + @Description("Set a location.") + public void onSet(Player player, String id, @Optional SaveType saveType) { + final SpawnManager manager = Practice.getInstance().getSpawnManager(); + final AsyncLocation asyncLocation = AsyncLocation.of(player.getLocation()); + + if (saveType != null) { + switch (saveType) { + case SINGLE: + manager.getLocationMap().put(id, Collections.singletonList(asyncLocation)); + break; + case MULTIPLE: + final List locations = manager.fetchMultipleLocations(id) + .orElse(new ArrayList<>()); + locations.add(asyncLocation); + + manager.getLocationMap().put(id, locations); + break; + } + } else { + manager.getLocationMap().put(id, Collections.singletonList(asyncLocation)); + } + + manager.saveAsyncLocationsToConfig(); + + player.sendMessage(CC.YELLOW + "You've saved the location by the id " + CC.AQUA + id + CC.YELLOW + " with the save type " + CC.AQUA + (saveType == null ? SaveType.SINGLE.name() : saveType.name()) + CC.YELLOW + "."); + } + + @Subcommand("list|show") + @Description("List all available locations.") + public void onList(Player player) { + final SpawnManager manager = Practice.getInstance().getSpawnManager(); + + final int parent = manager.getLocationMap().size(); + final int total = manager.getLocationMap().values() + .stream().mapToInt(List::size).sum(); + + player.sendMessage(CC.AQUA + CC.BOLD + "Practice Locations:"); + player.sendMessage(CC.GRAY + "Available (Parent): " + CC.WHITE + parent); + player.sendMessage(CC.GRAY + "Available (Total): " + CC.WHITE + total); + player.sendMessage(" "); + + manager.getLocationMap().forEach((s, asyncLocations) -> { + player.sendMessage(CC.AQUA + CC.BOLD + s); + + asyncLocations.forEach(asyncLocation -> { + final String formattedLocation = CC.YELLOW + "" + String.format("%.2f", asyncLocation.getX()) + ", " + String.format("%.2f", asyncLocation.getY()) + ", " + String.format("%.2f", asyncLocation.getZ()); + final Clickable clickable = new Clickable(CC.GRAY + " - " + formattedLocation); + + clickable.add( + CC.GREEN + " (Teleport)", + CC.GREEN + "Click to teleport to " + formattedLocation + CC.GREEN + ".", + "/location tp-async " + ((int) asyncLocation.getX()) + " " + ((int) asyncLocation.getY()) + " " + ((int) asyncLocation.getZ()), + ClickEvent.Action.RUN_COMMAND + ); + + player.spigot().sendMessage(clickable.asComponents()); + }); + + player.sendMessage(" "); + }); + } + + @Syntax(" ") + @Subcommand("tp-async") + @Description("Teleport to a location async.") + public void onTpAsync(Player player, Integer x, Integer y, Integer z) { + PaperLib.teleportAsync(player, new Location(player.getWorld(), x, y, z)).whenComplete((aBoolean, throwable) -> { + if (aBoolean) { + player.sendMessage(CC.GREEN + "You've teleported to " + x + ", " + y + ", " + z + "."); + } else { + player.sendMessage(CC.RED + "Sorry, we couldn't teleport you to that location."); + } + }); + } + + private enum SaveType { SINGLE, MULTIPLE } +} diff --git a/src/main/java/com/solexgames/practice/commands/administration/TournamentCommand.java b/src/main/java/com/solexgames/practice/commands/administration/TournamentCommand.java new file mode 100644 index 0000000..abde8e7 --- /dev/null +++ b/src/main/java/com/solexgames/practice/commands/administration/TournamentCommand.java @@ -0,0 +1,137 @@ +package com.solexgames.practice.commands.administration; + +import com.solexgames.core.util.Color; +import com.solexgames.core.util.clickable.Clickable; +import com.solexgames.lib.acf.BaseCommand; +import com.solexgames.lib.acf.CommandHelp; +import com.solexgames.lib.acf.ConditionFailedException; +import com.solexgames.lib.acf.annotation.*; +import com.solexgames.practice.Practice; +import com.solexgames.practice.PracticeConstants; +import com.solexgames.practice.kit.Kit; +import com.solexgames.practice.tournament.Tournament; +import com.solexgames.practice.tournament.TournamentState; +import com.solexgames.practice.util.CC; +import org.bukkit.Bukkit; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +@CommandAlias("tournament|tourny") +public class TournamentCommand extends BaseCommand { + + private final Practice plugin = Practice.getInstance(); + + @Default + @HelpCommand + @Syntax("[page]") + public void onHelp(CommandHelp help) { + help.showHelp(); + } + + @Subcommand("start|create") + @Description("Schedule a new tournament.") + @CommandPermission("practice.command.tournament") + @Syntax(" ") + public void onStart(CommandSender sender, Kit kit, int id, int teamSize, int maxSize) { + if (maxSize % teamSize != 0) { + throw new ConditionFailedException("The team size and max size do not match."); + } + + if (this.plugin.getTournamentManager().getTournament(id) != null) { + throw new ConditionFailedException("Tournament ID " + CC.YELLOW + id + CC.RED + " already exists."); + } + + this.plugin.getTournamentManager().createNewTournament(sender, id, teamSize, maxSize, kit.getName()); + } + + @Syntax("") + @Subcommand("end|stop") + @Description("Force end a running tournament.") + @CommandPermission("practice.command.tournament") + public void onStop(CommandSender sender, int id) { + final Tournament tournament = this.plugin.getTournamentManager().getTournament(id); + + if (tournament == null) { + throw new ConditionFailedException("Tournament ID " + CC.YELLOW + id + CC.RED + " does not exist."); + } + + sender.sendMessage(PracticeConstants.TOURNAMENT_PREFIX + CC.GREEN + "You've ended tournament " + CC.YELLOW + id + CC.GREEN + "."); + + this.plugin.getTournamentManager().forceEndTournament(id, true); + } + + @Syntax("") + @Subcommand("alert|bc|broadcast") + @Description("Broadcast a tournament.") + @CommandPermission("practice.command.tournament") + public void onBroadcast(CommandSender sender, int id) { + final Tournament tournament = this.plugin.getTournamentManager().getTournament(id); + + if (tournament == null) { + throw new ConditionFailedException("Tournament ID " + CC.YELLOW + id + CC.RED + " does not exist."); + } + + final String toSend = Color.SECONDARY_COLOR + "A " + CC.AQUA + tournament.getKitName() + CC.GRAY + " (" + tournament.getTeamSize() + "v" + tournament.getTeamSize() + ")" + Color.SECONDARY_COLOR + " tournament is starting soon. " + CC.GREEN + CC.BOLD + "[Click to Join]"; + + final Clickable message = new Clickable(toSend, + CC.GREEN + "Click to join tournament number " + CC.YELLOW + id + CC.GREEN + ".", + "/tournament join " + id); + + Bukkit.getOnlinePlayers().forEach(player -> player.spigot().sendMessage(message.asComponents())); + + sender.sendMessage(PracticeConstants.TOURNAMENT_PREFIX + CC.GREEN + "You've broadcasted tournament " + CC.YELLOW + id + CC.GREEN + "."); + } + + @Syntax("") + @Subcommand("forcestart") + @Description("Force start a tournament.") + @CommandPermission("practice.command.tournament") + public void onForceStart(CommandSender sender, int id) { + final Tournament tournament = this.plugin.getTournamentManager().getTournament(id); + + if (tournament == null) { + throw new ConditionFailedException("Tournament ID " + CC.YELLOW + id + CC.RED + " does not exist."); + } + + if (!tournament.getTournamentState().equals(TournamentState.WAITING)) { + throw new ConditionFailedException("Tournament " + CC.YELLOW + id + CC.RED + " is not in the lobby state."); + } + + tournament.setTournamentState(TournamentState.STARTING); + + sender.sendMessage(PracticeConstants.TOURNAMENT_PREFIX + CC.GREEN + "You've force started tournament " + CC.YELLOW + id + CC.GREEN + "."); + } + + @Syntax("") + @Subcommand("join") + @Description("Join a tournament.") + public void onJoin(Player player, int id) { + final Tournament tournament = this.plugin.getTournamentManager().getTournament(id); + + if (tournament == null) { + throw new ConditionFailedException("Tournament ID " + CC.YELLOW + id + CC.RED + " does not exist."); + } + + if (this.plugin.getTournamentManager().isInTournament(player.getUniqueId())) { + throw new ConditionFailedException("You're already in a tournament!"); + } + + if (tournament.getPlayers().size() + 1 > tournament.getSize() && !player.hasPermission("practice.tournament.max.bypass")) { + throw new ConditionFailedException("The tournament's currently full."); + } + + this.plugin.getTournamentManager().joinTournament(id, player); + } + + @Subcommand("leave") + @Description("Leave your tournament.") + public void onLeave(Player player) { + final Tournament tournament = this.plugin.getTournamentManager().getTournament(player.getUniqueId()); + + if (tournament == null) { + throw new ConditionFailedException("You're not in a tournament."); + } + + this.plugin.getTournamentManager().leaveTournament(player); + } +} diff --git a/src/main/java/com/solexgames/practice/commands/duel/AcceptCommand.java b/src/main/java/com/solexgames/practice/commands/duel/AcceptCommand.java new file mode 100644 index 0000000..97821c8 --- /dev/null +++ b/src/main/java/com/solexgames/practice/commands/duel/AcceptCommand.java @@ -0,0 +1,103 @@ +package com.solexgames.practice.commands.duel; + +import com.solexgames.core.util.Color; +import com.solexgames.lib.acf.BaseCommand; +import com.solexgames.lib.acf.ConditionFailedException; +import com.solexgames.lib.acf.annotation.*; +import com.solexgames.lib.acf.bukkit.contexts.OnlinePlayer; +import com.solexgames.lib.commons.command.context.CommonsPlayer; +import com.solexgames.practice.Practice; +import com.solexgames.practice.kit.Kit; +import com.solexgames.practice.managers.PartyManager; +import com.solexgames.practice.match.Match; +import com.solexgames.practice.match.MatchRequest; +import com.solexgames.practice.match.team.impl.MatchTeam; +import com.solexgames.practice.party.Party; +import com.solexgames.practice.player.PlayerData; +import com.solexgames.practice.player.PlayerState; +import com.solexgames.practice.queue.QueueType; +import com.solexgames.practice.util.CC; +import org.bukkit.entity.Player; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +@CommandAlias("accept") +public class AcceptCommand extends BaseCommand { + + private final Practice plugin = Practice.getInstance(); + + @Default + @Syntax("") + @CommandCompletion("@unvanished") + public void onDefault(Player player, CommonsPlayer targetOnlinePlayer, @Optional String kitName) { + final Player target = targetOnlinePlayer.getPlayer(); + + if (target == null || player.getName().equals(target.getName())) { + throw new ConditionFailedException(CC.RED + "You cannot send duel requests to yourself."); + } + + final PlayerData targetData = this.plugin.getPlayerManager().getPlayerData(target.getUniqueId()); + + if (targetData.getPlayerState() != PlayerState.SPAWN) { + throw new ConditionFailedException(CC.YELLOW + target.getName() + CC.RED + " is not at spawn."); + } + + MatchRequest request = this.plugin.getMatchManager().getMatchRequest(target.getUniqueId(), player.getUniqueId()); + + if (kitName != null) { + final Kit kit = this.plugin.getKitManager().getKit(kitName); + + if (kit != null) { + request = this.plugin.getMatchManager().getMatchRequest(target.getUniqueId(), player.getUniqueId(), kit.getName()); + } + } + + if (request == null) { + throw new ConditionFailedException(CC.RED + "You don't have any pending requests from anyone."); + } + + if (request.getRequester().equals(target.getUniqueId())) { + final List playersA = new ArrayList<>(); + final List playersB = new ArrayList<>(); + + final PartyManager partyManager = this.plugin.getPartyManager(); + + final Party party = partyManager.getParty(player.getUniqueId()); + final Party targetParty = partyManager.getParty(target.getUniqueId()); + + if (request.isParty()) { + if (party != null && targetParty != null && partyManager.isLeader(target.getUniqueId()) && partyManager.isLeader(target.getUniqueId())) { + playersA.addAll(party.getMembers()); + playersB.addAll(targetParty.getMembers()); + + } else { + throw new ConditionFailedException(CC.YELLOW + target.getName() + CC.RED + " is not the leader of their party."); + } + } else { + if (party == null && targetParty == null) { + playersA.add(player.getUniqueId()); + playersB.add(target.getUniqueId()); + } else { + throw new ConditionFailedException(CC.YELLOW + target.getName() + CC.RED + " is in a party."); + } + } + + final Kit kit = this.plugin.getKitManager().getKit(request.getKitName()); + + final MatchTeam teamA = new MatchTeam(target.getUniqueId(), playersB, 0); + final MatchTeam teamB = new MatchTeam(player.getUniqueId(), playersA, 1); + + final Match match = new Match(request.getArena(), kit, QueueType.UNRANKED, teamA, teamB); + + final Player leaderA = this.plugin.getServer().getPlayer(teamA.getLeader()); + final Player leaderB = this.plugin.getServer().getPlayer(teamB.getLeader()); + + match.broadcast(Color.SECONDARY_COLOR + "Starting a match with kit " + Color.MAIN_COLOR + request.getKitName().replace("_", " ") + Color.SECONDARY_COLOR + " between " + Color.MAIN_COLOR + leaderA.getName() + Color.SECONDARY_COLOR + " and " + Color.MAIN_COLOR + leaderB.getName() + Color.SECONDARY_COLOR + "."); + match.broadcast(Color.SECONDARY_COLOR + "You're playing on the arena " + Color.MAIN_COLOR + match.getArena().getName() + Color.SECONDARY_COLOR + "."); + + this.plugin.getMatchManager().createMatch(match, true); + } + } +} diff --git a/src/main/java/com/solexgames/practice/commands/duel/DuelCommand.java b/src/main/java/com/solexgames/practice/commands/duel/DuelCommand.java new file mode 100644 index 0000000..b8ab665 --- /dev/null +++ b/src/main/java/com/solexgames/practice/commands/duel/DuelCommand.java @@ -0,0 +1,75 @@ +package com.solexgames.practice.commands.duel; + +import com.solexgames.lib.acf.BaseCommand; +import com.solexgames.lib.acf.ConditionFailedException; +import com.solexgames.lib.acf.annotation.CommandAlias; +import com.solexgames.lib.acf.annotation.CommandCompletion; +import com.solexgames.lib.acf.annotation.Default; +import com.solexgames.lib.acf.annotation.Syntax; +import com.solexgames.lib.acf.bukkit.contexts.OnlinePlayer; +import com.solexgames.lib.commons.command.context.CommonsPlayer; +import com.solexgames.practice.Practice; +import com.solexgames.practice.menu.duel.DuelMenu; +import com.solexgames.practice.party.Party; +import com.solexgames.practice.player.PlayerData; +import com.solexgames.practice.player.PlayerState; +import com.solexgames.practice.util.CC; +import org.bukkit.entity.Player; + +@CommandAlias("duel|fight") +public class DuelCommand extends BaseCommand { + + private final Practice plugin = Practice.getInstance(); + + @Default + @Syntax("") + @CommandCompletion("@unvanished") + public void onDefault(Player player, CommonsPlayer target) { + final PlayerData playerData = this.plugin.getPlayerManager().getPlayerData(player.getUniqueId()); + + if (playerData.getPlayerState() != PlayerState.SPAWN) { + throw new ConditionFailedException(CC.RED + "You must be at spawn to send duel requests."); + } + + if (this.plugin.getTournamentManager().getTournament(target.getPlayer().getUniqueId()) != null) { + throw new ConditionFailedException(CC.YELLOW + target.getPlayer().getName() + CC.RED + " is in a tournament."); + } + + final Party party = this.plugin.getPartyManager().getParty(player.getUniqueId()); + final Party targetParty = this.plugin.getPartyManager().getParty(target.getPlayer().getUniqueId()); + + if (player.getName().equals(target.getPlayer().getName())) { + throw new ConditionFailedException(CC.RED + "You cannot send duel requests to yourself."); + } + + if (targetParty != null && party == targetParty) { + throw new ConditionFailedException(CC.RED + "You cannot send duel requests to yourself."); + } + + if (party != null && !this.plugin.getPartyManager().isLeader(player.getUniqueId())) { + throw new ConditionFailedException(CC.RED + "You're not the leader of your party."); + } + + final PlayerData targetData = this.plugin.getPlayerManager().getPlayerData(target.getPlayer().getUniqueId()); + + if (targetData.getPlayerState() != PlayerState.SPAWN) { + throw new ConditionFailedException(CC.YELLOW + target.getPlayer().getName() + CC.RED + " is not at spawn."); + } + + if (!targetData.getOptions().isDuelRequests()) { + throw new ConditionFailedException(CC.YELLOW + target.getPlayer().getName() + CC.RED + " is ignoring duel requests."); + } + + if (party == null && targetParty != null) { + throw new ConditionFailedException(CC.YELLOW + target.getPlayer().getName() + CC.RED + " is currently in a party."); + } + + if (party != null && targetParty == null) { + throw new ConditionFailedException("You're currently in a party."); + } + + playerData.setDuelSelecting(target.getPlayer().getUniqueId()); + + new DuelMenu(target.getPlayer()).openMenu(player); + } +} diff --git a/src/main/java/com/solexgames/practice/commands/duel/SpectateCommand.java b/src/main/java/com/solexgames/practice/commands/duel/SpectateCommand.java new file mode 100644 index 0000000..a98f5e8 --- /dev/null +++ b/src/main/java/com/solexgames/practice/commands/duel/SpectateCommand.java @@ -0,0 +1,79 @@ +package com.solexgames.practice.commands.duel; + +import com.solexgames.core.util.Color; +import com.solexgames.lib.acf.BaseCommand; +import com.solexgames.lib.acf.ConditionFailedException; +import com.solexgames.lib.acf.annotation.CommandAlias; +import com.solexgames.lib.acf.annotation.CommandCompletion; +import com.solexgames.lib.acf.annotation.Default; +import com.solexgames.lib.acf.annotation.Syntax; +import com.solexgames.lib.acf.bukkit.contexts.OnlinePlayer; +import com.solexgames.lib.commons.command.context.CommonsPlayer; +import com.solexgames.practice.Practice; +import com.solexgames.practice.match.Match; +import com.solexgames.practice.match.team.impl.MatchTeam; +import com.solexgames.practice.menu.SpectateMenu; +import com.solexgames.practice.party.Party; +import com.solexgames.practice.player.PlayerData; +import com.solexgames.practice.player.PlayerState; +import com.solexgames.practice.util.CC; +import org.bukkit.entity.Player; + +@CommandAlias("spectate|spec") +public class SpectateCommand extends BaseCommand { + + private final Practice plugin = Practice.getInstance(); + + @Default + public void onDefaultOne(Player player) { + new SpectateMenu().openMenu(player); + } + + @Default + @Syntax("") + @CommandCompletion("@unvanished") + public void onDefaultTwo(Player player, CommonsPlayer target) { + final PlayerData playerData = this.plugin.getPlayerManager().getPlayerData(player.getUniqueId()); + final Party party = this.plugin.getPartyManager().getParty(playerData.getUniqueId()); + + if (party != null || (playerData.getPlayerState() != PlayerState.SPAWN && playerData.getPlayerState() != PlayerState.SPECTATING)) { + throw new ConditionFailedException("You cannot execute this command right now."); + } + + final PlayerData targetData = this.plugin.getPlayerManager().getPlayerData(target.getPlayer().getUniqueId()); + + if (targetData.getPlayerState() != PlayerState.FIGHTING) { + throw new ConditionFailedException(CC.YELLOW + target.getPlayer().getName() + CC.RED + " is not in a fight."); + } + + final Match targetMatch = this.plugin.getMatchManager().getMatch(targetData); + + if (!targetMatch.isParty()) { + if (!targetData.getOptions().isSpectators() && !player.hasPermission("practice.staff")) { + throw new ConditionFailedException(CC.YELLOW + target.getPlayer().getName() + CC.RED + " is ignoring spectators."); + } + + final MatchTeam team = targetMatch.getTeams().get(0); + final MatchTeam team2 = targetMatch.getTeams().get(1); + + final PlayerData otherPlayerData = this.plugin.getPlayerManager().getPlayerData(team.getPlayers().get(0) == target.getPlayer().getUniqueId() ? team2.getPlayers().get(0) : team.getPlayers().get(0)); + + if (otherPlayerData != null && !otherPlayerData.getOptions().isSpectators() && !player.hasPermission("practice.staff")) { + throw new ConditionFailedException(CC.YELLOW + target.getPlayer().getName() + CC.RED + " is ignoring spectators."); + } + } + + if (playerData.getPlayerState() == PlayerState.SPECTATING) { + Match match = this.plugin.getMatchManager().getSpectatingMatch(player.getUniqueId()); + + if (match.equals(targetMatch)) { + throw new ConditionFailedException("You're already spectating " + CC.YELLOW + target.getPlayer().getName() + "'s " + CC.RED + "match."); + } + match.removeSpectator(player.getUniqueId()); + } + + player.sendMessage(Color.SECONDARY_COLOR + "You're now spectating " + target.getPlayer().getDisplayName() + Color.SECONDARY_COLOR + ". " + CC.GRAY + "(" + targetMatch.getType().getName() + " " + targetMatch.getKit().getName() + ")"); + + this.plugin.getMatchManager().addSpectator(player, playerData, target.getPlayer(), targetMatch); + } +} diff --git a/src/main/java/com/solexgames/practice/commands/spawn/SpawnCommand.java b/src/main/java/com/solexgames/practice/commands/spawn/SpawnCommand.java new file mode 100644 index 0000000..28555bd --- /dev/null +++ b/src/main/java/com/solexgames/practice/commands/spawn/SpawnCommand.java @@ -0,0 +1,35 @@ +package com.solexgames.practice.commands.spawn; + +import com.solexgames.lib.acf.BaseCommand; +import com.solexgames.lib.acf.annotation.CommandAlias; +import com.solexgames.lib.acf.annotation.CommandPermission; +import com.solexgames.lib.acf.annotation.Default; +import com.solexgames.practice.Practice; +import com.solexgames.practice.player.PlayerData; +import com.solexgames.practice.player.PlayerState; +import com.solexgames.practice.util.CC; +import org.bukkit.entity.Player; + +/** + * @author GrowlyX + * @since 5/17/2021 + */ + +@CommandAlias("spawn") +@CommandPermission("practice.command.spawn") +public class SpawnCommand extends BaseCommand { + + @Default + public void onDefault(Player player) { + final PlayerData playerData = Practice.getInstance().getPlayerManager().getPlayerData(player.getUniqueId()); + + if (playerData.getPlayerState() != PlayerState.SPAWN && playerData.getPlayerState() != PlayerState.FFA) { + player.sendMessage(CC.RED + "You cannot execute this command right now."); + return; + } + + player.sendMessage(CC.GREEN + "You've been teleported to spawn!"); + + Practice.getInstance().getPlayerManager().sendToSpawnAndReset(player); + } +} diff --git a/src/main/java/com/solexgames/practice/commands/time/TimeCommand.java b/src/main/java/com/solexgames/practice/commands/time/TimeCommand.java new file mode 100644 index 0000000..22e41b0 --- /dev/null +++ b/src/main/java/com/solexgames/practice/commands/time/TimeCommand.java @@ -0,0 +1,27 @@ +package com.solexgames.practice.commands.time; + +import com.solexgames.lib.acf.BaseCommand; +import com.solexgames.lib.acf.annotation.CommandAlias; +import com.solexgames.practice.util.CC; +import org.bukkit.entity.Player; + +public class TimeCommand extends BaseCommand { + + @CommandAlias("day") + public void onDay(Player player) { + player.setPlayerTime(6000L, true); + player.sendMessage(CC.YELLOW + "You've set your in-game time to " + CC.GREEN + "Day" + CC.YELLOW + "!"); + } + + @CommandAlias("night") + public void onNight(Player player) { + player.setPlayerTime(18000L, true); + player.sendMessage(CC.YELLOW + "You've set your in-game time to " + CC.BLUE + "Night" + CC.YELLOW + "!"); + } + + @CommandAlias("sunset") + public void onSunset(Player player) { + player.setPlayerTime(12000L, true); + player.sendMessage(CC.YELLOW + "You've set your in-game time to " + CC.AQUA + "Sunset" + CC.YELLOW + "!"); + } +} diff --git a/src/main/java/com/solexgames/practice/comphenix/EntityHider.java b/src/main/java/com/solexgames/practice/comphenix/EntityHider.java new file mode 100644 index 0000000..15f11fe --- /dev/null +++ b/src/main/java/com/solexgames/practice/comphenix/EntityHider.java @@ -0,0 +1,316 @@ +package com.solexgames.practice.comphenix; + +import static com.comphenix.protocol.PacketType.Play.Server.*; + +import java.lang.reflect.InvocationTargetException; +import java.util.Arrays; +import java.util.Map; + +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.HandlerList; +import org.bukkit.event.Listener; +import org.bukkit.event.entity.EntityDeathEvent; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.event.world.ChunkUnloadEvent; +import org.bukkit.plugin.Plugin; + +import com.comphenix.protocol.PacketType; +import com.comphenix.protocol.ProtocolLibrary; +import com.comphenix.protocol.ProtocolManager; +import com.comphenix.protocol.events.PacketAdapter; +import com.comphenix.protocol.events.PacketContainer; +import com.comphenix.protocol.events.PacketEvent; +import com.google.common.base.Preconditions; +import com.google.common.collect.HashBasedTable; +import com.google.common.collect.Table; + +/** + * @author Comphenix + */ + +public class EntityHider implements Listener { + + protected Table observerEntityMap = HashBasedTable.create(); + + // Packets that update remote player entities + private static final PacketType[] ENTITY_PACKETS = { + ENTITY_EQUIPMENT, BED, ANIMATION, NAMED_ENTITY_SPAWN, + COLLECT, SPAWN_ENTITY, SPAWN_ENTITY_LIVING, SPAWN_ENTITY_PAINTING, SPAWN_ENTITY_EXPERIENCE_ORB, + ENTITY_VELOCITY, REL_ENTITY_MOVE, ENTITY_LOOK, ENTITY_MOVE_LOOK, ENTITY_MOVE_LOOK, + ENTITY_TELEPORT, ENTITY_HEAD_ROTATION, ENTITY_STATUS, ATTACH_ENTITY, ENTITY_METADATA, + ENTITY_EFFECT, REMOVE_ENTITY_EFFECT, BLOCK_BREAK_ANIMATION, WORLD_PARTICLES, REMOVE_ENTITY_EFFECT + + // We don't handle DESTROY_ENTITY though + }; + + /** + * The current entity visibility policy. + * @author Kristian + */ + public enum Policy { + /** + * All entities are invisible by default. Only entities specifically made visible may be seen. + */ + WHITELIST, + + /** + * All entities are visible by default. An entity can only be hidden explicitly. + */ + BLACKLIST, + } + + private ProtocolManager manager; + + // Listeners + private Listener bukkitListener; + private PacketAdapter protocolListener; + + // Current policy + protected final Policy policy; + + /** + * Construct a new entity hider. + * @param plugin - the plugin that controls this entity hider. + * @param policy - the default visibility policy. + */ + public EntityHider(Plugin plugin, Policy policy) { + Preconditions.checkNotNull(plugin, "plugin cannot be NULL."); + + // Save policy + this.policy = policy; + this.manager = ProtocolLibrary.getProtocolManager(); + + // Register events and packet listener + plugin.getServer().getPluginManager().registerEvents( + bukkitListener = constructBukkit(), plugin); + manager.addPacketListener( + protocolListener = constructProtocol(plugin)); + } + + /** + * Set the visibility status of a given entity for a particular observer. + * @param observer - the observer player. + * @param entity - ID of the entity that will be hidden or made visible. + * @param visible - TRUE if the entity should be made visible, FALSE if not. + * @return TRUE if the entity was visible before this method call, FALSE otherwise. + */ + protected boolean setVisibility(Player observer, int entityID, boolean visible) { + switch (policy) { + case BLACKLIST: + // Non-membership means they are visible + return !setMembership(observer, entityID, !visible); + case WHITELIST: + return setMembership(observer, entityID, visible); + default : + throw new IllegalArgumentException("Unknown policy: " + policy); + } + } + + /** + * Add or remove the given entity and observer entry from the table. + * @param observer - the player observer. + * @param entityID - ID of the entity. + * @param member - TRUE if they should be present in the table, FALSE otherwise. + * @return TRUE if they already were present, FALSE otherwise. + */ + // Helper method + protected boolean setMembership(Player observer, int entityID, boolean member) { + if (member) { + return observerEntityMap.put(observer.getEntityId(), entityID, true) != null; + } else { + return observerEntityMap.remove(observer.getEntityId(), entityID) != null; + } + } + + /** + * Determine if the given entity and observer is present in the table. + * @param observer - the player observer. + * @param entityID - ID of the entity. + * @return TRUE if they are present, FALSE otherwise. + */ + protected boolean getMembership(Player observer, int entityID) { + return observerEntityMap.contains(observer.getEntityId(), entityID); + } + + /** + * Determine if a given entity is visible for a particular observer. + * @param observer - the observer player. + * @param entityID - ID of the entity that we are testing for visibility. + * @return TRUE if the entity is visible, FALSE otherwise. + */ + protected boolean isVisible(Player observer, int entityID) { + // If we are using a whitelist, presence means visibility - if not, the opposite is the case + boolean presence = getMembership(observer, entityID); + + return policy == Policy.WHITELIST ? presence : !presence; + } + + /** + * Remove the given entity from the underlying map. + * @param entity - the entity to remove. + * @param destroyed - TRUE if the entity was killed, FALSE if it is merely unloading. + */ + protected void removeEntity(Entity entity, boolean destroyed) { + int entityID = entity.getEntityId(); + + for (Map maps : observerEntityMap.rowMap().values()) { + maps.remove(entityID); + } + } + + /** + * Invoked when a player logs out. + * @param player - the player that jused logged out. + */ + protected void removePlayer(Player player) { + // Cleanup + observerEntityMap.rowMap().remove(player.getEntityId()); + } + + /** + * Construct the Bukkit event listener. + * @return Our listener. + */ + private Listener constructBukkit() { + return new Listener() { + @EventHandler + public void onEntityDeath(EntityDeathEvent e) { + removeEntity(e.getEntity(), true); + } + + @EventHandler + public void onChunkUnload(ChunkUnloadEvent e) { + for (Entity entity : e.getChunk().getEntities()) { + removeEntity(entity, false); + } + } + + @EventHandler + public void onPlayerQuit(PlayerQuitEvent e) { + removePlayer(e.getPlayer()); + } + }; + } + + /** + * Construct the packet listener that will be used to intercept every entity-related packet. + * @param plugin - the parent plugin. + * @return The packet listener. + */ + private PacketAdapter constructProtocol(Plugin plugin) { + return new PacketAdapter(plugin, ENTITY_PACKETS) { + @Override + public void onPacketSending(PacketEvent event) { + int entityID = event.getPacket().getIntegers().read(0); + + // See if this packet should be cancelled + if (!isVisible(event.getPlayer(), entityID)) { + event.setCancelled(true); + } + } + }; + } + +// /** +// * Toggle the visibility status of an entity for a player. +// *

+// * If the entity is visible, it will be hidden. If it is hidden, it will become visible. +// * @param observer - the player observer. +// * @param entity - the entity to toggle. +// * @return TRUE if the entity was visible before, FALSE otherwise. +// */ +// public final boolean toggleEntity(Player observer, Entity entity) { +// if (isVisible(observer, entity.getEntityId())) { +// return hideEntity(observer, entity); +// } else { +// return !showEntity(observer, entity); +// } +// } + + /** + * Allow the observer to see an entity that was previously hidden. + * @param observer - the observer. + * @param entity - the entity to show. + * @return TRUE if the entity was hidden before, FALSE otherwise. + */ + public final boolean showEntity(Player observer, Player entity) { + observer.showPlayer(entity); + +// validate(observer, entity); +// boolean hiddenBefore = !setVisibility(observer, entity.getEntityId(), true); +// +// // Resend packets +// if (manager != null && hiddenBefore) { +// manager.updateEntity(entity, Arrays.asList(observer)); +// } + + return true; + } + + /** + * Prevent the observer from seeing a given entity. + * @param observer - the player observer. + * @param entity - the entity to hide. + * @return TRUE if the entity was previously visible, FALSE otherwise. + */ + public final boolean hideEntity(Player observer, Player entity) { + observer.hidePlayer(entity); + +// validate(observer, entity); +// boolean visibleBefore = setVisibility(observer, entity.getEntityId(), false); +// +// if (visibleBefore) { +// PacketContainer destroyEntity = new PacketContainer(ENTITY_DESTROY); +// destroyEntity.getIntegerArrays().write(0, new int[] { entity.getEntityId() }); +// +// // Make the entity disappear +// try { +// manager.sendServerPacket(observer, destroyEntity); +// } catch (InvocationTargetException e) { +// throw new RuntimeException("Cannot send server packet.", e); +// } +// } + + return true; + } + + /** + * Determine if the given entity has been hidden from an observer. + *

+ * Note that the entity may very well be occluded or out of range from the perspective + * of the observer. This method simply checks if an entity has been completely hidden + * for that observer. + * @param observer - the observer. + * @param entity - the entity that may be hidden. + * @return TRUE if the player may see the entity, FALSE if the entity has been hidden. + */ + public final boolean canSee(Player observer, Entity entity) { + validate(observer, entity); + + return isVisible(observer, entity.getEntityId()); + } + + // For valdiating the input parameters + private void validate(Player observer, Entity entity) { + Preconditions.checkNotNull(observer, "observer cannot be NULL."); + Preconditions.checkNotNull(entity, "entity cannot be NULL."); + } + + /** + * Retrieve the current visibility policy. + * @return The current visibility policy. + */ + public Policy getPolicy() { + return policy; + } + + public void close() { + if (manager != null) { + HandlerList.unregisterAll(bukkitListener); + manager.removePacketListener(protocolListener); + manager = null; + } + } +} diff --git a/src/main/java/com/solexgames/practice/completion/AbstractDivisionPrizeHandler.java b/src/main/java/com/solexgames/practice/completion/AbstractDivisionPrizeHandler.java new file mode 100644 index 0000000..52b43bc --- /dev/null +++ b/src/main/java/com/solexgames/practice/completion/AbstractDivisionPrizeHandler.java @@ -0,0 +1,17 @@ +package com.solexgames.practice.completion; + +import com.solexgames.practice.player.PlayerData; +import org.bukkit.entity.Player; + +import java.util.function.BiConsumer; + +/** + * @author GrowlyX + * @since 8/12/2021 + */ + +public abstract class AbstractDivisionPrizeHandler { + + public abstract BiConsumer getPrize(); + +} diff --git a/src/main/java/com/solexgames/practice/completion/impl/BronzeDivisionPrizeHandler.java b/src/main/java/com/solexgames/practice/completion/impl/BronzeDivisionPrizeHandler.java new file mode 100644 index 0000000..9a6ea51 --- /dev/null +++ b/src/main/java/com/solexgames/practice/completion/impl/BronzeDivisionPrizeHandler.java @@ -0,0 +1,22 @@ +package com.solexgames.practice.completion.impl; + +import com.solexgames.practice.completion.AbstractDivisionPrizeHandler; +import com.solexgames.practice.player.PlayerData; +import org.bukkit.entity.Player; + +import java.util.function.BiConsumer; + +/** + * @author GrowlyX + * @since 8/12/2021 + */ + +public class BronzeDivisionPrizeHandler extends AbstractDivisionPrizeHandler { + + @Override + public BiConsumer getPrize() { + return (player, playerData) -> { + + }; + } +} diff --git a/src/main/java/com/solexgames/practice/completion/impl/DiamondDivisionPrizeHandler.java b/src/main/java/com/solexgames/practice/completion/impl/DiamondDivisionPrizeHandler.java new file mode 100644 index 0000000..2c4fc52 --- /dev/null +++ b/src/main/java/com/solexgames/practice/completion/impl/DiamondDivisionPrizeHandler.java @@ -0,0 +1,22 @@ +package com.solexgames.practice.completion.impl; + +import com.solexgames.practice.completion.AbstractDivisionPrizeHandler; +import com.solexgames.practice.player.PlayerData; +import org.bukkit.entity.Player; + +import java.util.function.BiConsumer; + +/** + * @author GrowlyX + * @since 8/12/2021 + */ + +public class DiamondDivisionPrizeHandler extends AbstractDivisionPrizeHandler { + + @Override + public BiConsumer getPrize() { + return (player, playerData) -> { + + }; + } +} diff --git a/src/main/java/com/solexgames/practice/completion/impl/GoldDivisionPrizeHandler.java b/src/main/java/com/solexgames/practice/completion/impl/GoldDivisionPrizeHandler.java new file mode 100644 index 0000000..a41f0fd --- /dev/null +++ b/src/main/java/com/solexgames/practice/completion/impl/GoldDivisionPrizeHandler.java @@ -0,0 +1,22 @@ +package com.solexgames.practice.completion.impl; + +import com.solexgames.practice.completion.AbstractDivisionPrizeHandler; +import com.solexgames.practice.player.PlayerData; +import org.bukkit.entity.Player; + +import java.util.function.BiConsumer; + +/** + * @author GrowlyX + * @since 8/12/2021 + */ + +public class GoldDivisionPrizeHandler extends AbstractDivisionPrizeHandler { + + @Override + public BiConsumer getPrize() { + return (player, playerData) -> { + + }; + } +} diff --git a/src/main/java/com/solexgames/practice/completion/impl/IronDivisionPrizeHandler.java b/src/main/java/com/solexgames/practice/completion/impl/IronDivisionPrizeHandler.java new file mode 100644 index 0000000..0ad856c --- /dev/null +++ b/src/main/java/com/solexgames/practice/completion/impl/IronDivisionPrizeHandler.java @@ -0,0 +1,22 @@ +package com.solexgames.practice.completion.impl; + +import com.solexgames.practice.completion.AbstractDivisionPrizeHandler; +import com.solexgames.practice.player.PlayerData; +import org.bukkit.entity.Player; + +import java.util.function.BiConsumer; + +/** + * @author GrowlyX + * @since 8/12/2021 + */ + +public class IronDivisionPrizeHandler extends AbstractDivisionPrizeHandler { + + @Override + public BiConsumer getPrize() { + return (player, playerData) -> { + + }; + } +} diff --git a/src/main/java/com/solexgames/practice/completion/impl/SilverDivisionPrizeHandler.java b/src/main/java/com/solexgames/practice/completion/impl/SilverDivisionPrizeHandler.java new file mode 100644 index 0000000..7295198 --- /dev/null +++ b/src/main/java/com/solexgames/practice/completion/impl/SilverDivisionPrizeHandler.java @@ -0,0 +1,22 @@ +package com.solexgames.practice.completion.impl; + +import com.solexgames.practice.completion.AbstractDivisionPrizeHandler; +import com.solexgames.practice.player.PlayerData; +import org.bukkit.entity.Player; + +import java.util.function.BiConsumer; + +/** + * @author GrowlyX + * @since 8/12/2021 + */ + +public class SilverDivisionPrizeHandler extends AbstractDivisionPrizeHandler { + + @Override + public BiConsumer getPrize() { + return (player, playerData) -> { + + }; + } +} diff --git a/src/main/java/com/solexgames/practice/effects/DeathEffect.java b/src/main/java/com/solexgames/practice/effects/DeathEffect.java new file mode 100644 index 0000000..d13ea32 --- /dev/null +++ b/src/main/java/com/solexgames/practice/effects/DeathEffect.java @@ -0,0 +1,21 @@ +package com.solexgames.practice.effects; + +import org.bukkit.Material; +import org.bukkit.entity.Player; + +/** + * @author GrowlyX + * @since 5/23/2021 + */ + +public interface DeathEffect { + + String getName(); + + String getPermission(); + + Material getIcon(); + + void applyEffect(Player player, Player killer); + +} diff --git a/src/main/java/com/solexgames/practice/effects/DeathEffects.java b/src/main/java/com/solexgames/practice/effects/DeathEffects.java new file mode 100644 index 0000000..aeb9f61 --- /dev/null +++ b/src/main/java/com/solexgames/practice/effects/DeathEffects.java @@ -0,0 +1,14 @@ +package com.solexgames.practice.effects; + +import com.solexgames.practice.effects.impl.LightningDeathEffect; + +/** + * @author GrowlyX + * @since 7/7/2021 + */ + +public class DeathEffects { + + public static final DeathEffect LIGHTNING = new LightningDeathEffect(); + +} diff --git a/src/main/java/com/solexgames/practice/effects/impl/BloodDeathEffect.java b/src/main/java/com/solexgames/practice/effects/impl/BloodDeathEffect.java new file mode 100644 index 0000000..4f6b0fd --- /dev/null +++ b/src/main/java/com/solexgames/practice/effects/impl/BloodDeathEffect.java @@ -0,0 +1,62 @@ +package com.solexgames.practice.effects.impl; + +import com.solexgames.practice.Practice; +import com.solexgames.practice.effects.DeathEffect; +import lombok.RequiredArgsConstructor; +import org.bukkit.Effect; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitRunnable; + +import java.util.Arrays; + +/** + * @author GrowlyX + * @since 7/9/2021 + */ + +public class BloodDeathEffect implements DeathEffect { + + @Override + public String getName() { + return "Blood"; + } + + @Override + public String getPermission() { + return "practice.effects.blood"; + } + + @Override + public Material getIcon() { + return Material.REDSTONE; + } + + @Override + public void applyEffect(Player player, Player killer) { + new BloodEffectTask(player.getLocation()).runTaskTimer(Practice.getInstance(), 0L, 10L); + } + + @RequiredArgsConstructor + public static class BloodEffectTask extends BukkitRunnable { + + private final Location location; + + private int ticks = 0; + + @Override + public void run() { + this.ticks++; + + if (this.ticks >= 5) { + this.cancel(); + return; + } + + Arrays.asList(1.0D, 1.25D, 1.5D, 1.75D, 2.0D).forEach(aDouble -> { + this.location.getWorld().playEffect(this.location.clone().add(0.0D, aDouble, 0.0D), Effect.STEP_SOUND, Material.REDSTONE_BLOCK); + }); + } + } +} diff --git a/src/main/java/com/solexgames/practice/effects/impl/ExplosionDeathEffect.java b/src/main/java/com/solexgames/practice/effects/impl/ExplosionDeathEffect.java new file mode 100644 index 0000000..a10c5c2 --- /dev/null +++ b/src/main/java/com/solexgames/practice/effects/impl/ExplosionDeathEffect.java @@ -0,0 +1,38 @@ +package com.solexgames.practice.effects.impl; + +import com.solexgames.practice.effects.DeathEffect; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.util.Vector; +import xyz.xenondevs.particle.ParticleEffect; + +/** + * @author GrowlyX + * @since 5/26/2021 + */ + +public class ExplosionDeathEffect implements DeathEffect { + + @Override + public String getName() { + return "Explosion"; + } + + @Override + public String getPermission() { + return "practice.effects.explosion"; + } + + @Override + public Material getIcon() { + return Material.TNT; + } + + @Override + public void applyEffect(Player player, Player killer) { + final Vector finalLoc = killer.getLocation().getDirection().multiply(2.5D).setY(1.1D); + player.setVelocity(finalLoc); + + ParticleEffect.EXPLOSION_LARGE.display(player.getLocation()); + } +} diff --git a/src/main/java/com/solexgames/practice/effects/impl/LightningDeathEffect.java b/src/main/java/com/solexgames/practice/effects/impl/LightningDeathEffect.java new file mode 100644 index 0000000..2b4989c --- /dev/null +++ b/src/main/java/com/solexgames/practice/effects/impl/LightningDeathEffect.java @@ -0,0 +1,43 @@ +package com.solexgames.practice.effects.impl; + +import com.solexgames.practice.effects.DeathEffect; +import net.minecraft.server.v1_8_R3.EntityLightning; +import net.minecraft.server.v1_8_R3.Packet; +import net.minecraft.server.v1_8_R3.PacketPlayOutSpawnEntityWeather; +import org.bukkit.Material; +import org.bukkit.craftbukkit.v1_8_R3.entity.CraftPlayer; +import org.bukkit.entity.Player; + +/** + * @author GrowlyX + * @since 5/23/2021 + */ + +public class LightningDeathEffect implements DeathEffect { + + @Override + public String getName() { + return "Lightning"; + } + + @Override + public String getPermission() { + return "practice.effects.lightning"; + } + + @Override + public Material getIcon() { + return Material.BLAZE_ROD; + } + + @Override + public void applyEffect(Player player, Player killer) { + final Packet packet = new PacketPlayOutSpawnEntityWeather(new EntityLightning(((CraftPlayer) player).getHandle().getWorld(), (player).getLocation().getX(), (player).getLocation().getY(), (player).getLocation().getZ(), false, false)); + + ((CraftPlayer) player).getHandle().playerConnection.sendPacket(packet); + ((CraftPlayer) killer).getHandle().playerConnection.sendPacket(packet); + + player.setAllowFlight(true); + player.setFlying(true); + } +} diff --git a/src/main/java/com/solexgames/practice/event/BaseEvent.java b/src/main/java/com/solexgames/practice/event/BaseEvent.java new file mode 100644 index 0000000..75d20a8 --- /dev/null +++ b/src/main/java/com/solexgames/practice/event/BaseEvent.java @@ -0,0 +1,24 @@ +package com.solexgames.practice.event; + +import org.bukkit.Bukkit; +import org.bukkit.event.Cancellable; +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; + +public class BaseEvent extends Event { + + private static final HandlerList HANDLER_LIST = new HandlerList(); + + @Override + public HandlerList getHandlers() { + return HANDLER_LIST; + } + + public void call() { + Bukkit.getServer().getPluginManager().callEvent(this); + + if (this instanceof Cancellable) { + ((Cancellable) this).isCancelled(); + } + } +} diff --git a/src/main/java/com/solexgames/practice/event/match/MatchEndEvent.java b/src/main/java/com/solexgames/practice/event/match/MatchEndEvent.java new file mode 100644 index 0000000..03d49e4 --- /dev/null +++ b/src/main/java/com/solexgames/practice/event/match/MatchEndEvent.java @@ -0,0 +1,20 @@ +package com.solexgames.practice.event.match; + +import com.solexgames.practice.match.Match; +import com.solexgames.practice.match.team.impl.MatchTeam; +import lombok.Getter; + +@Getter +public class MatchEndEvent extends MatchEvent { + + private final MatchTeam winningTeam; + private final MatchTeam losingTeam; + + public MatchEndEvent(Match match, MatchTeam winningTeam, MatchTeam losingTeam) { + super(match); + + this.winningTeam = winningTeam; + this.losingTeam = losingTeam; + } + +} diff --git a/src/main/java/com/solexgames/practice/event/match/MatchEvent.java b/src/main/java/com/solexgames/practice/event/match/MatchEvent.java new file mode 100644 index 0000000..2541650 --- /dev/null +++ b/src/main/java/com/solexgames/practice/event/match/MatchEvent.java @@ -0,0 +1,24 @@ +package com.solexgames.practice.event.match; + +import com.solexgames.practice.match.Match; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; + +@Getter +@RequiredArgsConstructor +public class MatchEvent extends Event { + private static final HandlerList HANDLERS = new HandlerList(); + + private final Match match; + + public static HandlerList getHandlerList() { + return HANDLERS; + } + + @Override + public HandlerList getHandlers() { + return HANDLERS; + } +} diff --git a/src/main/java/com/solexgames/practice/event/match/MatchStartEvent.java b/src/main/java/com/solexgames/practice/event/match/MatchStartEvent.java new file mode 100644 index 0000000..54c409f --- /dev/null +++ b/src/main/java/com/solexgames/practice/event/match/MatchStartEvent.java @@ -0,0 +1,9 @@ +package com.solexgames.practice.event.match; + +import com.solexgames.practice.match.Match; + +public class MatchStartEvent extends MatchEvent { + public MatchStartEvent(Match match) { + super(match); + } +} diff --git a/src/main/java/com/solexgames/practice/events/Event.java b/src/main/java/com/solexgames/practice/events/Event.java new file mode 100644 index 0000000..1ed1062 --- /dev/null +++ b/src/main/java/com/solexgames/practice/events/Event.java @@ -0,0 +1,183 @@ +package com.solexgames.practice.events; + +import com.solexgames.core.util.Color; +import com.solexgames.practice.Practice; +import com.solexgames.practice.location.impl.impl.AsyncLocation; +import com.solexgames.practice.player.PlayerData; +import com.solexgames.practice.player.PlayerState; +import com.solexgames.practice.util.CC; +import com.solexgames.practice.util.PlayerUtil; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.Setter; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; + +import java.util.*; +import java.util.concurrent.ThreadLocalRandom; +import java.util.function.Consumer; +import java.util.stream.Collectors; + +/** + * @author GrowlyX + * @since 8/14/2021 + */ + +@Getter +@Setter +@RequiredArgsConstructor +public abstract class Event { +// +// private final String name; +// +// private Player host; +// private EventState state = EventState.UNANNOUNCED; +// +// private int limit = 64; +// +// public void startCountdown() { +// if (this.getCountdownTask().isEnded()) { +// this.getCountdownTask().setTimeUntilStart(this.getCountdownTask().getCountdownTime()); +// this.getCountdownTask().setEnded(false); +// } else { +// this.getCountdownTask().runTaskTimer(Practice.getInstance(), 20L, 20L); +// } +// } +// +// public void sendMessage(String message) { +// this.getBukkitPlayers().forEach(player -> player.sendMessage(Color.translate(message))); +// } +// +// public Set getBukkitPlayers() { +// return getPlayers().keySet().stream() +// .filter(uuid -> Practice.getInstance().getServer().getPlayer(uuid) != null) +// .map(Practice.getInstance().getServer()::getPlayer) +// .collect(Collectors.toSet()); +// } +// +// public void join(Player player) { +// if (this.getPlayers().size() >= this.limit && !player.hasPermission("practice.event.full.bypass")) { +// return; +// } +// +// Practice.getInstance().getQueueManager().removePlayerFromQueue(player); +// +// final PlayerData playerData = Practice.getInstance().getPlayerManager().getPlayerData(player.getUniqueId()); +// playerData.setPlayerState(PlayerState.EVENT); +// +// PlayerUtil.clearPlayer(player); +// +// if (this.onJoin() != null) { +// this.onJoin().accept(player); +// } +// +// if (this.getSpawnLocations().size() == 1) { +// player.teleport(this.getSpawnLocations().get(0).toBukkitLocation()); +// } else { +// final List asyncLocations = new ArrayList<>(this.getSpawnLocations()); +// +// asyncLocations.get(ThreadLocalRandom.current().nextInt(asyncLocations.size())).teleport(player); +// } +// +// Practice.getInstance().getPlayerManager().giveLobbyItems(player); +// +// this.getBukkitPlayers().forEach(other -> other.showPlayer(player)); +// this.getBukkitPlayers().forEach(player::showPlayer); +// +// this.sendMessage(CC.AQUA + player.getName() + CC.YELLOW + " has joined. " + CC.GREEN + "(" + this.getPlayers().size() + "/" + this.limit + ")"); +// +// player.sendMessage(CC.YELLOW + "You've joined the " + CC.AQUA + this.name + CC.YELLOW + " event."); +// } +// +// public void leave(Player player) { +// if (this.onDeath() != null) { +// this.onDeath().accept(player); +// } +// +// this.getPlayers().remove(player.getUniqueId()); +// +// player.sendMessage(CC.RED + "You've left the " + this.name + " event."); +// +// Practice.getInstance().getPlayerManager().sendToSpawnAndReset(player); +// } +// +//// public void start() { +// new EventStartEvent(this).call(); +// +// this.setState(EventState.STARTED); +// this.onStart(); +// +// Practice.getInstance().getEventManager().setCooldown(0L); +// } +// +// public void handleWin(Player winner) { +// Bukkit.broadcastMessage(""); +// Bukkit.broadcastMessage(winner.getDisplayName() + CC.YELLOW + " has won the " + CC.AQUA + this.name + CC.YELLOW + " event!"); +// Bukkit.broadcastMessage(""); +// } +// +// public void end() { +// Practice.getInstance().getEventManager().setCooldown(System.currentTimeMillis() + (60 * 3) * 1000L); +// +// this.getBukkitPlayers().forEach(player -> { +// Practice.getInstance().getPlayerManager().sendToSpawnAndReset(player); +// }); +// +// if (this instanceof SumoEvent) { +// SumoEvent sumoEvent = (SumoEvent) this; +// sumoEvent.setRound(0); +// +// for (SumoPlayer sumoPlayer : sumoEvent.getPlayers().values()) { +// if (sumoPlayer.getFightTask() != null) { +// sumoPlayer.getFightTask().cancel(); +// } +// } +// +// if (sumoEvent.getWaterCheckTask() != null) { +// sumoEvent.getWaterCheckTask().cancel(); +// } +// } +// +// this.getPlayers().clear(); +// this.setState(EventState.UNANNOUNCED); +//// +//// final Iterator iterator = Practice.getInstance().getEventManager() +//// .getSpectators().keySet().iterator(); +//// +//// while (iterator.hasNext()) { +//// final UUID spectatorUUID = iterator.next(); +//// final Player spectator = Bukkit.getPlayer(spectatorUUID); +//// +//// if (spectator != null) { +//// Practice.getInstance().getServer().getScheduler().runTask(Practice.getInstance(), () -> Practice.getInstance().getPlayerManager().sendToSpawnAndReset(spectator)); +//// +//// iterator.remove(); +//// } +//// } +// +//// Practice.getInstance().getEventManager().getSpectators().clear(); +// +// this.getCountdownTask().setEnded(true); +// } +// +// public K getPlayer(Player player) { +// return this.getPlayer(player.getUniqueId()); +// } +// +// public K getPlayer(UUID uuid) { +// return this.getPlayers().get(uuid); +// } +// +// public abstract Map getPlayers(); +// +// public abstract EventCountdownTask getCountdownTask(); +// +// public abstract List getSpawnLocations(); +// +// public abstract void onStart(); +// +// public abstract Consumer onJoin(); +// +// public abstract Consumer onDeath(); + +} diff --git a/src/main/java/com/solexgames/practice/events/EventCountdownTask.java b/src/main/java/com/solexgames/practice/events/EventCountdownTask.java new file mode 100644 index 0000000..95b2db4 --- /dev/null +++ b/src/main/java/com/solexgames/practice/events/EventCountdownTask.java @@ -0,0 +1,62 @@ +package com.solexgames.practice.events; + +import com.solexgames.core.util.clickable.Clickable; +import com.solexgames.practice.util.CC; +import lombok.Getter; +import lombok.Setter; +import net.md_5.bungee.api.chat.ClickEvent; +import org.bukkit.Bukkit; +import org.bukkit.scheduler.BukkitRunnable; + +@Setter +@Getter +public abstract class EventCountdownTask extends BukkitRunnable { + + private final Event event; + private final int countdownTime; + + private int timeUntilStart; + private boolean ended; + + public EventCountdownTask(Event event, int countdownTime) { + this.event = event; + this.countdownTime = countdownTime; + this.timeUntilStart = countdownTime; + } + + @Override + public void run() { + if (this.isEnded()) { + return; + } + + if (this.timeUntilStart <= 0) { + if (this.canStart()) { +// this.event.start(); + } else { + this.onCancel(); + } + + this.ended = true; + return; + } + + if (shouldAnnounce(timeUntilStart)) { +// final Clickable clickable = new Clickable(CC.YELLOW + "A " + CC.AQUA + this.event.getName() + CC.GRAY + " (1v1)" + CC.YELLOW + " event is starting soon. "); +// clickable.add(CC.GREEN + CC.BOLD + "[Join]", CC.GREEN + "Click to join the " + CC.YELLOW + this.event.getName() + CC.GREEN + " event.", "/joinevent " + this.event.getName(), ClickEvent.Action.RUN_COMMAND); +// +// Bukkit.getOnlinePlayers().forEach(player -> { +// player.spigot().sendMessage(clickable.asComponents()); +// }); + } + + this.timeUntilStart--; + } + + public abstract boolean shouldAnnounce(int timeUntilStart); + + public abstract boolean canStart(); + + public abstract void onCancel(); + +} diff --git a/src/main/java/com/solexgames/practice/events/EventPlayer.java b/src/main/java/com/solexgames/practice/events/EventPlayer.java new file mode 100644 index 0000000..bfb2f03 --- /dev/null +++ b/src/main/java/com/solexgames/practice/events/EventPlayer.java @@ -0,0 +1,15 @@ +package com.solexgames.practice.events; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +import java.util.UUID; + +@Getter +@RequiredArgsConstructor +public class EventPlayer { + + private final UUID uuid; + private final Event event; + +} diff --git a/src/main/java/com/solexgames/practice/events/EventState.java b/src/main/java/com/solexgames/practice/events/EventState.java new file mode 100644 index 0000000..7ec16a2 --- /dev/null +++ b/src/main/java/com/solexgames/practice/events/EventState.java @@ -0,0 +1,9 @@ +package com.solexgames.practice.events; + +public enum EventState { + + UNANNOUNCED, // The event hasn't even been announced yet + WAITING, // Waiting for players to join while the event counts down to start + STARTED // The event has started and is in progress + +} diff --git a/src/main/java/com/solexgames/practice/ffa/FFAManager.java b/src/main/java/com/solexgames/practice/ffa/FFAManager.java new file mode 100644 index 0000000..8a42e3f --- /dev/null +++ b/src/main/java/com/solexgames/practice/ffa/FFAManager.java @@ -0,0 +1,137 @@ +package com.solexgames.practice.ffa; + +import com.solexgames.core.util.Color; +import com.solexgames.practice.Practice; +import com.solexgames.practice.comphenix.EntityHider; +import com.solexgames.practice.ffa.killstreak.KillStreak; +import com.solexgames.practice.ffa.killstreak.impl.DebuffKillStreak; +import com.solexgames.practice.ffa.killstreak.impl.GappleKillStreak; +import com.solexgames.practice.ffa.killstreak.impl.GodAppleKillStreak; +import com.solexgames.practice.kit.Kit; +import com.solexgames.practice.location.impl.impl.AsyncLocation; +import com.solexgames.practice.player.PlayerData; +import com.solexgames.practice.player.PlayerState; +import com.solexgames.practice.util.CC; +import com.solexgames.practice.util.PlayerUtil; +import io.papermc.lib.PaperLib; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import org.bukkit.Bukkit; +import org.bukkit.entity.Item; +import org.bukkit.entity.Player; +import org.bukkit.metadata.FixedMetadataValue; + +import java.util.*; + +@RequiredArgsConstructor +public class FFAManager { + + @Getter + private final Map itemTracker = new HashMap<>(); + @Getter + private final Map killStreakTracker = new HashMap<>(); + + @Getter + private final Set killStreaks = new HashSet<>(); + + private final Practice plugin = Practice.getInstance(); + + private final AsyncLocation spawnPoint; + private final Kit kit; + + public void addPlayer(Player player) { + if (this.spawnPoint == null || this.kit == null) { + player.sendMessage(CC.RED + "FFA is currently disabled."); + return; + } + + if (this.killStreaks.isEmpty()) { + this.killStreaks.add(new GappleKillStreak()); + this.killStreaks.add(new DebuffKillStreak()); + this.killStreaks.add(new GodAppleKillStreak()); + } + + PlayerUtil.clearPlayer(player); + + final PlayerData playerData = this.plugin.getPlayerManager().getPlayerData(player.getUniqueId()); + playerData.setPlayerState(PlayerState.FFA); + + PaperLib.teleportAsync(player, this.spawnPoint.toBukkitLocation()); + player.sendMessage(CC.GREEN + "Welcome to the FFA Arena!"); + + this.kit.applyToPlayer(player); + + for (PlayerData data : this.plugin.getPlayerManager().getAllData()) { + final Player otherPlayer = this.plugin.getServer().getPlayer(data.getUniqueId()); + + if (data.getPlayerState() == PlayerState.FFA) { + Practice.getInstance().getEntityHider().showEntity(player, otherPlayer); + Practice.getInstance().getEntityHider().showEntity(otherPlayer, player); + } else { + Practice.getInstance().getEntityHider().hideEntity(player, otherPlayer); + Practice.getInstance().getEntityHider().hideEntity(otherPlayer, player); + } + } + + player.sendMessage(Color.SECONDARY_COLOR + "Players will be able to attack you in " + Color.MAIN_COLOR + "5 seconds" + Color.SECONDARY_COLOR + "..."); + player.setMetadata("non-attackable", new FixedMetadataValue(this.plugin, true)); + + Bukkit.getScheduler().runTaskLater(this.plugin, () -> { + player.sendMessage(CC.GRAY + "Your invincibility has ran out."); + player.removeMetadata("non-attackable", this.plugin); + }, 100L); + } + + public void resetAndAddBack(Player player) { + if (this.spawnPoint == null || this.kit == null) { + return; + } + + if (this.killStreaks.isEmpty()) { + this.killStreaks.add(new GappleKillStreak()); + this.killStreaks.add(new DebuffKillStreak()); + this.killStreaks.add(new GodAppleKillStreak()); + } + + PaperLib.teleportAsync(player, this.spawnPoint.toBukkitLocation()); + + PlayerUtil.clearPlayer(player); + + this.kit.applyToPlayer(player); + + for (PlayerData data : this.plugin.getPlayerManager().getAllData()) { + final Player otherPlayer = this.plugin.getServer().getPlayer(data.getUniqueId()); + + if (data.getPlayerState() == PlayerState.FFA) { + Practice.getInstance().getEntityHider().showEntity(player, otherPlayer); + Practice.getInstance().getEntityHider().showEntity(otherPlayer, player); + } else { + Practice.getInstance().getEntityHider().hideEntity(player, otherPlayer); + Practice.getInstance().getEntityHider().hideEntity(otherPlayer, player); + } + } + + player.sendMessage(Color.SECONDARY_COLOR + "Players will be able to attack you in " + Color.MAIN_COLOR + "5 seconds" + Color.SECONDARY_COLOR + "..."); + player.setMetadata("non-attackable", new FixedMetadataValue(this.plugin, true)); + + Bukkit.getScheduler().runTaskLater(this.plugin, () -> { + player.sendMessage(CC.GRAY + "Your non attackable cooldown has expired."); + player.removeMetadata("non-attackable", this.plugin); + }, 100L); + } + + public void removePlayer(Player player) { + for (PlayerData data : this.plugin.getPlayerManager().getAllData()) { + final Player player1 = this.plugin.getServer().getPlayer(data.getUniqueId()); + + if (data.getPlayerState() == PlayerState.FFA) { + Practice.getInstance().getEntityHider().hideEntity(player, player1); + Practice.getInstance().getEntityHider().hideEntity(player1, player); + } + } + + player.sendMessage(CC.GREEN + "You've been sent to spawn."); + + this.plugin.getPlayerManager().sendToSpawnAndReset(player); + } +} diff --git a/src/main/java/com/solexgames/practice/ffa/killstreak/KillStreak.java b/src/main/java/com/solexgames/practice/ffa/killstreak/KillStreak.java new file mode 100644 index 0000000..c7b6588 --- /dev/null +++ b/src/main/java/com/solexgames/practice/ffa/killstreak/KillStreak.java @@ -0,0 +1,13 @@ +package com.solexgames.practice.ffa.killstreak; + +import org.bukkit.entity.Player; + +import java.util.List; + +public interface KillStreak { + + void giveKillStreak(Player player); + + List getStreaks(); + +} diff --git a/src/main/java/com/solexgames/practice/ffa/killstreak/impl/DebuffKillStreak.java b/src/main/java/com/solexgames/practice/ffa/killstreak/impl/DebuffKillStreak.java new file mode 100644 index 0000000..397667d --- /dev/null +++ b/src/main/java/com/solexgames/practice/ffa/killstreak/impl/DebuffKillStreak.java @@ -0,0 +1,30 @@ +package com.solexgames.practice.ffa.killstreak.impl; + +import com.solexgames.practice.ffa.killstreak.KillStreak; +import com.solexgames.practice.util.CC; +import com.solexgames.practice.util.PlayerUtil; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +import java.util.Arrays; +import java.util.List; + +public class DebuffKillStreak implements KillStreak { + + private static final ItemStack SLOWNESS = new ItemStack(Material.POTION, 1, (short) 16394); + private static final ItemStack POISON = new ItemStack(Material.POTION, 1, (short) 16388); + + @Override + public void giveKillStreak(Player player) { + PlayerUtil.setFirstSlotOfType(player, Material.POTION, SLOWNESS.clone()); + PlayerUtil.setFirstSlotOfType(player, Material.POTION, POISON.clone()); + + player.sendMessage(CC.GRAY + "You've received the " + CC.DARK_GREEN + "Debuff" + CC.GRAY + " killstreak!"); + } + + @Override + public List getStreaks() { + return Arrays.asList(7, 25); + } +} diff --git a/src/main/java/com/solexgames/practice/ffa/killstreak/impl/GappleKillStreak.java b/src/main/java/com/solexgames/practice/ffa/killstreak/impl/GappleKillStreak.java new file mode 100644 index 0000000..18790b5 --- /dev/null +++ b/src/main/java/com/solexgames/practice/ffa/killstreak/impl/GappleKillStreak.java @@ -0,0 +1,26 @@ +package com.solexgames.practice.ffa.killstreak.impl; + +import com.solexgames.practice.ffa.killstreak.KillStreak; +import com.solexgames.practice.util.CC; +import com.solexgames.practice.util.PlayerUtil; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +import java.util.Arrays; +import java.util.List; + +public class GappleKillStreak implements KillStreak { + + @Override + public void giveKillStreak(Player player) { + PlayerUtil.setFirstSlotOfType(player, Material.POTION, new ItemStack(Material.GOLDEN_APPLE, 3)); + + player.sendMessage(CC.GRAY + "You've received the " + CC.YELLOW + "GApple" + CC.GRAY + " killstreak!"); + } + + @Override + public List getStreaks() { + return Arrays.asList(3, 15); + } +} diff --git a/src/main/java/com/solexgames/practice/ffa/killstreak/impl/GodAppleKillStreak.java b/src/main/java/com/solexgames/practice/ffa/killstreak/impl/GodAppleKillStreak.java new file mode 100644 index 0000000..1aa5619 --- /dev/null +++ b/src/main/java/com/solexgames/practice/ffa/killstreak/impl/GodAppleKillStreak.java @@ -0,0 +1,26 @@ +package com.solexgames.practice.ffa.killstreak.impl; + +import com.solexgames.practice.ffa.killstreak.KillStreak; +import com.solexgames.practice.util.CC; +import com.solexgames.practice.util.PlayerUtil; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +import java.util.Arrays; +import java.util.List; + +public class GodAppleKillStreak implements KillStreak { + + @Override + public void giveKillStreak(Player player) { + PlayerUtil.setFirstSlotOfType(player, Material.POTION, new ItemStack(Material.GOLDEN_APPLE, 1, (short) 1)); + + player.sendMessage(CC.GRAY + "You've received the " + CC.AQUA + "GodApple" + CC.GRAY + " killstreak!"); + } + + @Override + public List getStreaks() { + return Arrays.asList(30, 40, 60, 75, 100); + } +} diff --git a/src/main/java/com/solexgames/practice/flags/Flag.java b/src/main/java/com/solexgames/practice/flags/Flag.java new file mode 100644 index 0000000..2e0d5a2 --- /dev/null +++ b/src/main/java/com/solexgames/practice/flags/Flag.java @@ -0,0 +1,21 @@ +package com.solexgames.practice.flags; + +/** + * @author GrowlyX + * @since 6/9/2021 + */ + +public enum Flag { + + DEFAULT, + HCF, + + SUMO, + BUILD, + SPLEEF, + PARKOUR, + BEDWARS, + BRIDGES, + STICK_FIGHT, + +} diff --git a/src/main/java/com/solexgames/practice/kit/Kit.java b/src/main/java/com/solexgames/practice/kit/Kit.java new file mode 100644 index 0000000..0fed4ad --- /dev/null +++ b/src/main/java/com/solexgames/practice/kit/Kit.java @@ -0,0 +1,40 @@ +package com.solexgames.practice.kit; + +import com.solexgames.practice.flags.Flag; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.Setter; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +@Getter +@Setter +@AllArgsConstructor +@RequiredArgsConstructor +public class Kit { + + private final String name; + + private ItemStack[] contents = new ItemStack[36]; + private ItemStack[] armor = new ItemStack[4]; + private ItemStack[] kitEditContents = new ItemStack[36]; + + private ItemStack icon; + private ItemStack leaderboardIcon; + + private boolean enabled = true; + private boolean ranked = false; + private boolean newKit = false; + + private int queueMenu = 0; + + private Flag flag = Flag.DEFAULT; + + public void applyToPlayer(Player player) { + player.getInventory().setContents(this.contents); + player.getInventory().setArmorContents(this.armor); + + player.updateInventory(); + } +} diff --git a/src/main/java/com/solexgames/practice/kit/PlayerKit.java b/src/main/java/com/solexgames/practice/kit/PlayerKit.java new file mode 100644 index 0000000..576f543 --- /dev/null +++ b/src/main/java/com/solexgames/practice/kit/PlayerKit.java @@ -0,0 +1,35 @@ +package com.solexgames.practice.kit; + +import com.solexgames.practice.Practice; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.Setter; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +@Getter +@Setter +@AllArgsConstructor +public class PlayerKit { + + private final String name; + private final int index; + + private ItemStack[] contents; + private String displayName; + + public void applyToPlayer(Player player) { + for (ItemStack itemStack : this.contents) { + if (itemStack != null) { + if (itemStack.getAmount() <= 0) { + itemStack.setAmount(1); + } + } + } + + player.getInventory().setContents(this.contents); + player.getInventory().setArmorContents(Practice.getInstance().getKitManager().getKit(this.name).getArmor()); + + player.updateInventory(); + } +} diff --git a/src/main/java/com/solexgames/practice/listeners/EntityListener.java b/src/main/java/com/solexgames/practice/listeners/EntityListener.java new file mode 100644 index 0000000..de99c9b --- /dev/null +++ b/src/main/java/com/solexgames/practice/listeners/EntityListener.java @@ -0,0 +1,210 @@ +package com.solexgames.practice.listeners; + +import com.solexgames.core.util.Color; +import com.solexgames.practice.Practice; +import com.solexgames.practice.flags.Flag; +import com.solexgames.practice.location.impl.impl.AsyncLocation; +import com.solexgames.practice.match.Match; +import com.solexgames.practice.match.MatchState; +import com.solexgames.practice.player.PlayerData; +import com.solexgames.practice.player.PlayerState; +import com.solexgames.practice.runnable.NewRoundRunnable; +import com.solexgames.practice.tournament.Tournament; +import com.solexgames.practice.util.CC; +import io.papermc.lib.PaperLib; +import org.bukkit.GameMode; +import org.bukkit.entity.Arrow; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.bukkit.entity.Projectile; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.event.entity.EntityDamageEvent; +import org.bukkit.event.entity.PotionSplashEvent; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; +import org.bukkit.projectiles.ProjectileSource; + +public class EntityListener implements Listener { + + private final Practice plugin = Practice.getInstance(); + + @EventHandler + public void onEntityDamage(EntityDamageEvent event) { + final Entity entity = event.getEntity(); + + if (entity instanceof Player) { + final Player player = (Player) entity; + final PlayerData playerData = this.plugin.getPlayerManager() + .getPlayerData(player.getUniqueId()); + + if (playerData != null) { + final Match match = this.plugin.getMatchManager().getMatch(playerData.getUniqueId()); + + if (match != null) { + switch (match.getMatchState()) { + case FIGHTING: + if (match.getKit().getName().equals("Boxing")) { + if (event.getCause().equals(EntityDamageEvent.DamageCause.FALL)) { + event.setCancelled(true); + return; + } + + if (!event.getCause().equals(EntityDamageEvent.DamageCause.CUSTOM)) { + event.setDamage(0.0D); + } + return; + } + break; + case ENDING: + case SWITCHING: + case STARTING: + event.setCancelled(true); + break; + } + } else { + if (event.getCause() == EntityDamageEvent.DamageCause.VOID) { + this.plugin.getSpawnManager().fetchSingleLocation("spawn").ifPresent(asyncLocation -> { + asyncLocation.teleport(player); + }); + } + + event.setCancelled(true); + } + } + } + } + + @EventHandler + public void onEntityDamageByEntity(EntityDamageByEntityEvent event) { + if (!(event.getEntity() instanceof Player)) { + event.setCancelled(true); + return; + } + + Player entity = (Player) event.getEntity(); + Player damager; + + if (event.getDamager() instanceof Player) { + damager = (Player) event.getDamager(); + } else if (event.getDamager() instanceof Projectile) { + final ProjectileSource projectileSource = ((Projectile) event.getDamager()).getShooter(); + + if (projectileSource instanceof Player) { + damager = (Player) ((Projectile) event.getDamager()).getShooter(); + } else { + return; + } + } else { + return; + } + + PlayerData entityData = this.plugin.getPlayerManager().getPlayerData(entity.getUniqueId()); + PlayerData damagerData = this.plugin.getPlayerManager().getPlayerData(damager.getUniqueId()); + + if (entityData == null || damagerData == null) { + event.setDamage(0.0D); + return; + } + + + if (damagerData.getPlayerState() == PlayerState.SPECTATING) { + event.setCancelled(true); + return; + } + + if ((!entity.canSee(damager) && damager.canSee(entity)) || damager.getGameMode() == GameMode.SPECTATOR) { + event.setCancelled(true); + return; + } + + final Match match = this.plugin.getMatchManager().getMatch(entityData); + + if (match == null || match.getMatchState().equals(MatchState.STARTING)) { + event.setCancelled(true); + return; + } + + if (damagerData.getTeamID() == entityData.getTeamID() && !match.isFFA()) { + event.setCancelled(true); + return; + } + + if (match.getKit().getFlag().equals(Flag.PARKOUR)) { + event.setCancelled(true); + return; + } + + if (match.getKit().getName().equals("Sumo")) { + event.setDamage(0.0D); + return; + } + + if (match.getKit().getFlag().equals(Flag.SPLEEF) || match.getKit().getFlag().equals(Flag.SUMO)) { + event.setDamage(0.0D); + } + + if (event.getDamager() instanceof Player) { + if (!match.getMatchState().equals(MatchState.FIGHTING)) { + return; + } + + damagerData.setCombo(damagerData.getCombo() + 1); + damagerData.setHits(damagerData.getHits() + 1); + + if (damagerData.getCombo() > damagerData.getLongestCombo()) { + damagerData.setLongestCombo(damagerData.getCombo()); + } + + entityData.setCombo(0); + + if (match.getKit().getName().contains("Boxing")) { + if (damagerData.getHits() >= 100) { + this.plugin.getMatchManager().removeFighter(entity, entityData, false); + } + + event.setDamage(0.0D); + } else if (match.getKit().getFlag().equals(Flag.STICK_FIGHT)) { + event.setDamage(0.0D); + } else if (match.getKit().getFlag().equals(Flag.SPLEEF)) { + event.setCancelled(true); + } + } else if (event.getDamager() instanceof Arrow) { + Arrow arrow = (Arrow) event.getDamager(); + + if (arrow.getShooter() instanceof Player) { + Player shooter = (Player) arrow.getShooter(); + + if (!entity.getName().equals(shooter.getName())) { + double health = Math.ceil(entity.getHealth() - event.getFinalDamage()) / 2.0D; + + if (health > 0.0D) { + shooter.sendMessage(entity.getDisplayName() + Color.SECONDARY_COLOR + " is now at " + CC.RED + health + " ❤" + Color.SECONDARY_COLOR + "."); + } + } + } + } + } + + @EventHandler + public void onPotionSplash(PotionSplashEvent e) { + if (!(e.getEntity().getShooter() instanceof Player)) { + return; + } + for (PotionEffect effect : e.getEntity().getEffects()) { + if (effect.getType().equals(PotionEffectType.HEAL)) { + Player shooter = (Player) e.getEntity().getShooter(); + + if (e.getIntensity(shooter) <= 0.5D) { + PlayerData shooterData = this.plugin.getPlayerManager().getPlayerData(shooter.getUniqueId()); + + if (shooterData != null) { + shooterData.setMissedPots(shooterData.getMissedPots() + 1); + } + } + break; + } + } + } +} diff --git a/src/main/java/com/solexgames/practice/listeners/InventoryListener.java b/src/main/java/com/solexgames/practice/listeners/InventoryListener.java new file mode 100644 index 0000000..b1747eb --- /dev/null +++ b/src/main/java/com/solexgames/practice/listeners/InventoryListener.java @@ -0,0 +1,29 @@ +package com.solexgames.practice.listeners; + +import com.solexgames.practice.Practice; +import com.solexgames.practice.player.PlayerData; +import com.solexgames.practice.player.PlayerState; +import org.bukkit.GameMode; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.inventory.InventoryClickEvent; + +public class InventoryListener implements Listener { + + private final Practice plugin = Practice.getInstance(); + + @EventHandler + public void onInventoryClick(InventoryClickEvent event) { + final Player player = (Player) event.getWhoClicked(); + + if (!player.getGameMode().equals(GameMode.CREATIVE)) { + final PlayerData playerData = this.plugin.getPlayerManager().getPlayerData(player.getUniqueId()); + + if (playerData.getPlayerState() == PlayerState.SPAWN || (playerData.getPlayerState() == PlayerState.EVENT && player.getItemInHand() != null && player.getItemInHand().getType() == Material.COMPASS)) { + event.setCancelled(true); + } + } + } +} diff --git a/src/main/java/com/solexgames/practice/listeners/KillStreakDataTrackerListener.java b/src/main/java/com/solexgames/practice/listeners/KillStreakDataTrackerListener.java new file mode 100644 index 0000000..54ac2cd --- /dev/null +++ b/src/main/java/com/solexgames/practice/listeners/KillStreakDataTrackerListener.java @@ -0,0 +1,67 @@ +package com.solexgames.practice.listeners; + +import com.solexgames.practice.Practice; +import com.solexgames.practice.event.match.MatchEndEvent; +import com.solexgames.practice.player.PlayerData; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; + +/** + * @author GrowlyX + * @since 7/30/2021 + */ + +public class KillStreakDataTrackerListener implements Listener { + + @EventHandler + public void onMatchEnd(MatchEndEvent event) { + if (event.getLosingTeam() != null) { + event.getLosingTeam().getPlayers().forEach(uuid -> { + final Player player = Bukkit.getPlayer(uuid); + + if (player != null) { + final PlayerData challengePlayer = Practice.getInstance().getPlayerManager().getPlayerData(player.getUniqueId()); + + if (challengePlayer != null) { + challengePlayer.setKillstreak(event.getMatch().getKit(), 0); + Practice.getInstance().getPlayerManager().saveData(challengePlayer); + } + + } + }); + } + + event.getWinningTeam().getPlayers().forEach(uuid -> { + final Player player = Bukkit.getPlayer(uuid); + + if (player != null) { + final PlayerData challengePlayer = Practice.getInstance().getPlayerManager().getPlayerData(player.getUniqueId()); + + if (challengePlayer != null) { + final String kitName = event.getMatch().getKit().getName(); + final int current = challengePlayer.getKitKillStreaks().getOrDefault(kitName, -1); + + if (current == -1) { + challengePlayer.getKitKillStreaks().put(kitName, 1); + } else { + final PlayerData playerData = Practice.getInstance().getPlayerManager().getPlayerData(uuid); + final int newKillStreak = current + 1; + + challengePlayer.getKitKillStreaks().put(kitName, newKillStreak); + + final int highest = playerData.getHighestKillStreak(kitName); + + if (highest == 0 || newKillStreak > highest) { + playerData.setHighestKillStreak(kitName, newKillStreak); + } + + Practice.getInstance().getPlayerManager().saveData(challengePlayer); + } + + } + } + }); + } +} diff --git a/src/main/java/com/solexgames/practice/listeners/MatchListener.java b/src/main/java/com/solexgames/practice/listeners/MatchListener.java new file mode 100644 index 0000000..ef36a82 --- /dev/null +++ b/src/main/java/com/solexgames/practice/listeners/MatchListener.java @@ -0,0 +1,431 @@ +package com.solexgames.practice.listeners; + +import com.solexgames.core.listener.custom.PlayerFreezeEvent; +import com.solexgames.core.menu.impl.player.PlayerInfoMenu; +import com.solexgames.core.util.Color; +import com.solexgames.core.util.clickable.Clickable; +import com.solexgames.practice.Practice; +import com.solexgames.practice.arena.type.StandaloneArena; +import com.solexgames.practice.event.match.MatchEndEvent; +import com.solexgames.practice.event.match.MatchStartEvent; +import com.solexgames.practice.flags.Flag; +import com.solexgames.practice.kit.Kit; +import com.solexgames.practice.location.impl.impl.AsyncLocation; +import com.solexgames.practice.match.Match; +import com.solexgames.practice.match.MatchState; +import com.solexgames.practice.player.PlayerData; +import com.solexgames.practice.player.PlayerState; +import com.solexgames.practice.queue.QueueType; +import com.solexgames.practice.runnable.MatchRunnable; +import com.solexgames.practice.util.*; +import com.solexgames.practice.util.calc.EloCalculator; +import net.md_5.bungee.api.chat.BaseComponent; +import net.md_5.bungee.api.chat.ClickEvent; +import net.md_5.bungee.api.chat.HoverEvent; +import org.apache.commons.lang.ArrayUtils; +import org.bukkit.Bukkit; +import org.bukkit.entity.ArmorStand; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.entity.CreatureSpawnEvent; +import org.bukkit.event.entity.EntitySpawnEvent; +import org.bukkit.event.entity.FoodLevelChangeEvent; +import org.bukkit.event.player.PlayerInteractAtEntityEvent; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; + +import java.util.*; +import java.util.concurrent.CompletableFuture; + +public class MatchListener implements Listener { + + private final Practice plugin = Practice.getInstance(); + + @EventHandler + public void onFreeze(PlayerFreezeEvent event) { + final Player player = event.getPlayer(); + final PlayerData playerData = this.plugin.getPlayerManager().getPlayerData(player.getUniqueId()); + + if (playerData.getCurrentMatchID() == null) { + return; + } + + final Match match = this.plugin.getMatchManager().getMatch(playerData); + + if (match != null) { + if (match.isParty()) { + this.plugin.getMatchManager().removeFighter(player, playerData, false); + this.plugin.getPlayerManager().sendToSpawnAndReset(player); + } else { + this.plugin.getServer().getPluginManager().callEvent(new MatchEndEvent(match, null, null)); + match.broadcast(CC.RED + "The match has been canceled due to a player being frozen."); + } + } + + this.plugin.getPartyManager().removePartyInvites(player.getUniqueId()); + this.plugin.getMatchManager().removeMatchRequests(player.getUniqueId()); + + this.plugin.getPartyManager().leaveParty(player); + } + + @EventHandler + public void onEntitySpawn(EntitySpawnEvent event) { + if (event.getEntityType().equals(EntityType.ARMOR_STAND)) { + event.setCancelled(false); + return; + } + + event.setCancelled(true); + } + + @EventHandler + public void onMobSpawn(CreatureSpawnEvent event) { + if (event.getEntity() instanceof ArmorStand) { + event.setCancelled(false); + return; + } + + event.setCancelled(true); + } + + @EventHandler + public void onHunger(FoodLevelChangeEvent event) { + final Match match = Practice.getInstance().getMatchManager().getMatch(event.getEntity().getUniqueId()); + + if (match != null) { + if (match.getKit().getName().contains("Soup")) { + event.setCancelled(true); + } + } + } + + @EventHandler + public void onMatchStart(MatchStartEvent event) { + final Match match = event.getMatch(); + final Kit kit = match.getKit(); + + match.setMatchState(MatchState.STARTING); + + if (!kit.isEnabled()) { + match.broadcast(CC.RED + "This kit is currently disabled."); + this.plugin.getMatchManager().removeMatch(match); + return; + } + + if (match.getKit().getFlag().equals(Flag.BUILD) || match.getKit().getFlag().equals(Flag.SPLEEF)) { + final StandaloneArena arena = match.getArena().getAvailableArena(); + + if (arena != null) { + match.setStandaloneArena(arena); + this.plugin.getArenaManager().setArenaMatchUUID(arena, match.getMatchId()); + } else { + match.broadcast(CC.RED + "There are no arenas available at this moment."); + this.plugin.getMatchManager().removeMatch(match); + return; + } + } + + final Set matchPlayers = new HashSet<>(); + + match.getTeams().forEach(team -> team.streamAlivePlayers().forEach(player -> { + matchPlayers.add(player); + + this.plugin.getMatchManager().removeMatchRequests(player.getUniqueId()); + + final PlayerData playerData = this.plugin.getPlayerManager().getPlayerData(player.getUniqueId()); + + if (playerData != null) { + PlayerUtil.clearPlayer(player); + + playerData.setCurrentMatchID(match.getMatchId()); + playerData.setTeamID(team.getTeamID()); + + playerData.setMissedPots(0); + playerData.setLongestCombo(0); + playerData.setCombo(0); + playerData.setHits(0); + + PlayerUtil.updateNametag(player, kit.getFlag().equals(Flag.BUILD)); + + final AsyncLocation locationA = match.getStandaloneArena() != null ? match.getStandaloneArena().getPositionOne() : match.getArena().getPositionOne(); + final AsyncLocation locationB = match.getStandaloneArena() != null ? match.getStandaloneArena().getPositionTwo() : match.getArena().getPositionTwo(); + + final CompletableFuture completableFuture = team.getTeamID() == 1 ? locationA.teleport(player) : locationB.teleport(player); + completableFuture.whenComplete((unused, throwable) -> { + this.plugin.getMatchManager().giveKits(player, kit); + + //BridgePlugin.INSTANCE.applyKnockbackProfile(player, kit.getName().toLowerCase()); + + if (kit.getName().equals("Combo")) { + player.setMaximumNoDamageTicks(0); + } + + if (kit.getName().contains("Boxing")) { + player.getInventory().setContents(this.plugin.getItemManager().getBoxingItems()); + player.addPotionEffect(new PotionEffect(PotionEffectType.SPEED, 99999, 1)); + + player.sendMessage(CC.GREEN + "First to 100 hits wins the match!"); + } + + if (match.getKit().getFlag().equals(Flag.STICK_FIGHT)) { + match.getLives().put(player.getUniqueId(), 5); + + player.getInventory().setContents(this.plugin.getItemManager().getStickFightItems()); + } + + playerData.setPlayerState(PlayerState.FIGHTING); + }); + } + })); + + for (Player player : matchPlayers) { + for (Player online : this.plugin.getServer().getOnlinePlayers()) { + Practice.getInstance().getEntityHider().hideEntity(player, online); + Practice.getInstance().getEntityHider().hideEntity(online, player); + } + } + + for (Player player : matchPlayers) { + for (Player other : matchPlayers) { + Practice.getInstance().getEntityHider().showEntity(player, other); + } + } + + new MatchRunnable(match).runTaskTimer(this.plugin, 20L, 20L); + } + + + @EventHandler + public void onMatchEnd(MatchEndEvent event) { + final Match match = event.getMatch(); + + if (event.getWinningTeam() == null) { + match.setMatchState(MatchState.ENDING); + match.setCountdown(1); + return; + } + + match.broadcast(CC.GRAY + CC.STRIKE_THROUGH + "-----------------------------------------------------"); + match.broadcast(Color.MAIN_COLOR + "Match Results" + CC.GRAY + " (Click names to view inventories)"); + + final ChatComponentBuilder wInventories = new ChatComponentBuilder(""); + final ChatComponentBuilder lInventories = new ChatComponentBuilder(""); + + match.setMatchState(MatchState.ENDING); + match.setWinningTeamId(event.getWinningTeam().getTeamID()); + match.setCountdown(4); + + if (match.isFFA()) { + final Player winner = Bukkit.getPlayer(event.getWinningTeam().getAlivePlayers().get(0)); + + if (winner == null) { + return; + } + + lInventories.append(CC.RED + "Participants: " + CC.YELLOW); + + event.getWinningTeam().getPlayers().forEach(uuid -> { + final Player player = Bukkit.getPlayer(uuid); + + if (player == null) { + return; + } + + if (!match.hasSnapshot(player.getUniqueId())) { + match.addSnapshot(player); + } + + if (!player.getUniqueId().equals(winner.getUniqueId())) { + final HoverEvent hover = new HoverEvent(HoverEvent.Action.SHOW_TEXT, (new ChatComponentBuilder(CC.GREEN + "Click to view this player's inventory.")).create()); + final ClickEvent click = new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/inventory " + match.getSnapshot(player.getUniqueId()).getSnapshotId()); + + lInventories.append(CC.YELLOW + player.getName()); + lInventories.setCurrentHoverEvent(hover).setCurrentClickEvent(click).append(CC.YELLOW + ", "); + } + }); + + match.getSnapshots().values().forEach(snapshot -> this.plugin.getInventoryManager().addSnapshot(snapshot)); + + final Clickable winnerClickable = new Clickable(CC.GREEN + "Winner: " + CC.YELLOW); + + winnerClickable.add(CC.YELLOW + winner.getName(), CC.GREEN + "Click to view this player's inventory.", "/inventory " + match.getSnapshot(winner.getUniqueId()).getSnapshotId(), ClickEvent.Action.RUN_COMMAND); + + match.broadcast(winnerClickable); + match.broadcast(lInventories.create()); + } else { + wInventories.append(match.isParty() ? (CC.GREEN + "Winning Team: ") : (CC.GREEN + "Winner: ")); + lInventories.append(match.isParty() ? (CC.RED + "Losing Team: ") : (CC.GRAY + "┃ " + CC.RED + "Loser: ")); + + match.getTeams().forEach(team -> team.getPlayers().forEach(uuid -> { + final Player player = Bukkit.getOfflinePlayer(uuid).getPlayer(); + + if (player == null) { + return; + } + + if (!match.hasSnapshot(player.getUniqueId())) { + match.addSnapshot(player); + } + + final boolean onWinningTeam = this.plugin.getPlayerManager().getPlayerData(player.getUniqueId()).getTeamID() == event.getWinningTeam().getTeamID(); + + final HoverEvent hover = new HoverEvent(HoverEvent.Action.SHOW_TEXT, (new ChatComponentBuilder(CC.GREEN + "Click to view this player's inventory.")).create()); + final ClickEvent click = new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/inventory " + match.getSnapshot(player.getUniqueId()).getSnapshotId()); + + if (onWinningTeam) { + wInventories.append(CC.YELLOW + player.getName()); + wInventories.setCurrentHoverEvent(hover).setCurrentClickEvent(click).append(CC.WHITE + ", "); + } else { + lInventories.append(CC.YELLOW + player.getName()); + lInventories.setCurrentHoverEvent(hover).setCurrentClickEvent(click).append(CC.WHITE + ", "); + } + })); + + match.getSnapshots().values().forEach(snapshot -> this.plugin.getInventoryManager().addSnapshot(snapshot)); + + wInventories.getCurrent().setText(wInventories.getCurrent().getText().substring(0, wInventories.getCurrent().getText().length() - 2)); + lInventories.getCurrent().setText(lInventories.getCurrent().getText().substring(0, lInventories.getCurrent().getText().length() - 2)); + + if (match.isParty()) { + match.broadcast(wInventories.create()); + match.broadcast(lInventories.create()); + } else { + final BaseComponent[] winnerComponents = wInventories.create(); + final BaseComponent[] loserComponents = lInventories.create(); + final BaseComponent[] separatorComponents = new ChatComponentBuilder(CC.GRAY + " " + " ").create(); + + match.broadcast((BaseComponent[]) ArrayUtils.addAll(winnerComponents, ArrayUtils.addAll(separatorComponents, loserComponents))); + } + + if (match.getType().isRanked()) { + final Player winnerLeader = Bukkit.getPlayer(event.getWinningTeam().getPlayers().get(0)); + final PlayerData winnerLeaderData = plugin.getPlayerManager().getPlayerData(winnerLeader.getUniqueId()); + + final Player loserLeader = Bukkit.getPlayer(event.getLosingTeam().getPlayers().get(0)); + final PlayerData loserLeaderData = plugin.getPlayerManager().getPlayerData(loserLeader.getUniqueId()); + + String eloMessage = null; + + int winnerElo; + int loserElo; + + final String kitName = match.getKit().getName(); + + if (event.getWinningTeam().getPlayers().size() == 1) { + winnerElo = winnerLeaderData.getElo(kitName); + loserElo = loserLeaderData.getElo(kitName); + + final int[] rankings = EloCalculator.getNewRankings(winnerElo, loserElo, true); + + eloMessage = Color.SECONDARY_COLOR + "Elo updates: " + CC.GREEN + winnerLeader.getName() + " +" + (rankings[0] - winnerElo) + + " (" + rankings[0] + ")" + Color.SECONDARY_COLOR + ", " + + CC.RED + loserLeader.getName() + " -" + (loserElo - rankings[1]) + " (" + + rankings[1] + ")"; + + if (match.getType() == QueueType.RANKED) { + winnerLeaderData.setElo(kitName, rankings[0]); + loserLeaderData.setElo(kitName, rankings[1]); + + winnerLeaderData.setWins(kitName, winnerLeaderData.getWins(kitName) + 1); + loserLeaderData.setLosses(kitName, loserLeaderData.getLosses(kitName) + 1); + } + } + + match.broadcast(" "); + + if (eloMessage != null) { + match.broadcast(eloMessage); + } + } + + this.plugin.getMatchManager().saveRematches(match); + } + + if (match.getSpectators() != null) { + int spectatorAmount = 0; + int spectatorOverflow = 0; + + final ArrayList specArrayList = new ArrayList<>(); + final Player loserLeader = Bukkit.getPlayer(event.getLosingTeam().getPlayers().get(0)); + + for (UUID playerUUID : match.getSpectators()) { + final String playersName = Bukkit.getPlayer(playerUUID).getName(); + final Player player = Bukkit.getPlayer(playersName); + + if (player != loserLeader) { + if (spectatorAmount >= 10) { + spectatorOverflow++; + continue; + } + + if (!player.hasPermission("practice.staff")) { + specArrayList.add(playersName); + spectatorAmount++; + } + } + } + + if (spectatorAmount != 0) { + final String spectatorList = String.join(", ", specArrayList); + + match.broadcast(" "); + match.broadcast(CC.AQUA + "Spectators (" + spectatorAmount + "): " + CC.YELLOW + spectatorList); + + if (spectatorOverflow != 0) { + match.broadcast(CC.GRAY + "(+" + spectatorOverflow + " more)"); + } + } + } + + match.broadcast(CC.GRAY + CC.STRIKE_THROUGH + "-----------------------------------------------------"); + + match.getTeams().forEach(team -> team.getPlayers().forEach(uuid -> { + final Player player = Bukkit.getPlayer(uuid); + + if (player == null) { + return; + } + + final PlayerData playerData = Practice.getInstance().getPlayerManager().getPlayerData(uuid); + + if (match.isShouldGiveXp()) { + if (team.getTeamID() == match.getWinningTeamId()) { + if (match.getType().isRanked()) { + playerData.setRankedWinCount(playerData.getRankedWinCount() + 1); + + EXPUtil.addExperienceToPlayer(player, 35, "Ranked match win"); + } else { + playerData.setUnrankedWinCount(playerData.getUnrankedWinCount() + 1); + + EXPUtil.addExperienceToPlayer(player, 10, "Unranked match win"); + } + PlayerUtil.clearPlayer(player); + } else if (match.getType().isRanked()) { + playerData.setRankedLossCount(playerData.getRankedLossCount() + 1); + + EXPUtil.addExperienceToPlayer(player, 10, "Competing in ranked"); + } else { + playerData.setUnrankedLossCount(playerData.getUnrankedLossCount() + 1); + + EXPUtil.addExperienceToPlayer(player, 10, "Participating in unranked"); + } + } + + playerData.setLastPlayedArena(match.getArena()); + + if (!playerData.canPlayRanked() && !player.hasPermission("practice.ranked.bypass")) { + player.sendMessage(Color.SECONDARY_COLOR + "You need to win " + Color.MAIN_COLOR + playerData.winsUntilRankedUnlocked() + Color.SECONDARY_COLOR + " more matches to play ranked."); + } + + playerData.recalculateGlobalElo(); + + if (match.getKit().isRanked()) { + playerData.recalculateDivision(); + } + + //BridgePlugin.INSTANCE.applyKnockbackProfile(player, "Default"); + })); + } +} diff --git a/src/main/java/com/solexgames/practice/listeners/MovementListener.java b/src/main/java/com/solexgames/practice/listeners/MovementListener.java new file mode 100644 index 0000000..c23764e --- /dev/null +++ b/src/main/java/com/solexgames/practice/listeners/MovementListener.java @@ -0,0 +1,72 @@ +package com.solexgames.practice.listeners; + +import com.solexgames.practice.Practice; +import com.solexgames.practice.flags.Flag; +import com.solexgames.practice.match.Match; +import com.solexgames.practice.match.MatchState; +import com.solexgames.practice.player.PlayerData; +import com.solexgames.practice.player.PlayerState; +import com.solexgames.practice.util.BlockUtil; +import io.papermc.lib.PaperLib; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerMoveEvent; + +/** + * @author GrowlyX + * @since 5/20/2021 + */ + +public class MovementListener implements Listener { + + private final Practice plugin = Practice.getInstance(); + + @EventHandler + public void onMove(PlayerMoveEvent event) { + final Player player = event.getPlayer(); + final PlayerData playerData = this.plugin.getPlayerManager().getPlayerData(player.getUniqueId()); + + if (playerData == null) { + return; + } + + if (playerData.getPlayerState() == PlayerState.FIGHTING) { + final Match match = this.plugin.getMatchManager().getMatch(player.getUniqueId()); + + if (player.hasMetadata("frozen")) { + PaperLib.teleportAsync(player, event.getFrom()); + return; + } + + if (match == null) { + return; + } + + if (match.getKit().getFlag().equals(Flag.PARKOUR)) { + if (BlockUtil.isStandingOn(player, Material.GOLD_PLATE)) { + final Player startingOpponent = match.getTeams().get(0).getPlayers().get(0) == player.getUniqueId() + ? this.plugin.getServer().getPlayer(match.getTeams().get(1).getPlayers().get(0)) + : this.plugin.getServer().getPlayer(match.getTeams().get(0).getPlayers().get(0)); + + this.plugin.getMatchManager().removeFighter(startingOpponent, playerData, false); + } + } + + if (match.getKit().getFlag().equals(Flag.SPLEEF) || match.getKit().getFlag().equals(Flag.SUMO)) { + final Location location = player.getLocation(); + location.setY(location.getY() - 0.6D); + + if (location.getBlock().getType().equals(Material.WATER) || location.getBlock().getType().equals(Material.STATIONARY_WATER) || location.getBlock().getType().equals(Material.LAVA) || location.getBlock().getType().equals(Material.STATIONARY_LAVA)) { + this.plugin.getMatchManager().removeFighter(player, playerData, true); + } + + if ((event.getTo().getX() != event.getFrom().getX() || event.getTo().getZ() != event.getFrom().getZ()) && match.getMatchState() == MatchState.STARTING) { + PaperLib.teleportAsync(player, event.getFrom()); + } + } + } + } +} diff --git a/src/main/java/com/solexgames/practice/listeners/PlayerListener.java b/src/main/java/com/solexgames/practice/listeners/PlayerListener.java new file mode 100644 index 0000000..67c9877 --- /dev/null +++ b/src/main/java/com/solexgames/practice/listeners/PlayerListener.java @@ -0,0 +1,672 @@ +package com.solexgames.practice.listeners; + +import com.solexgames.core.CorePlugin; +import com.solexgames.core.menu.impl.SettingsMenu; +import com.solexgames.core.player.PotPlayer; +import com.solexgames.core.player.prefixes.Prefix; +import com.solexgames.core.util.Color; +import com.solexgames.core.util.StringUtil; +import com.solexgames.practice.Practice; +import com.solexgames.practice.PracticeConstants; +import com.solexgames.practice.PracticeServerType; +import com.solexgames.practice.ffa.killstreak.KillStreak; +import com.solexgames.practice.flags.Flag; +import com.solexgames.practice.kit.Kit; +import com.solexgames.practice.kit.PlayerKit; +import com.solexgames.practice.match.Match; +import com.solexgames.practice.match.MatchState; +import com.solexgames.practice.menu.EventHostMenu; +import com.solexgames.practice.menu.JoinQueueMenu; +import com.solexgames.practice.menu.SpectateMenu; +import com.solexgames.practice.menu.editor.KitEditorMainMenu; +import com.solexgames.practice.menu.editor.KitEditorMenu; +import com.solexgames.practice.menu.party.PartyEventsMenu; +import com.solexgames.practice.party.Party; +import com.solexgames.practice.player.PlayerData; +import com.solexgames.practice.player.PlayerState; +import com.solexgames.practice.queue.QueueType; +import com.solexgames.practice.util.CC; +import com.solexgames.practice.util.PlayerUtil; +import com.solexgames.shop.menu.ShopCategoryMenu; +import io.papermc.lib.PaperLib; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.GameMode; +import org.bukkit.Material; +import org.bukkit.block.Sign; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.entity.*; +import org.bukkit.event.inventory.CraftItemEvent; +import org.bukkit.event.player.*; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.PlayerInventory; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; + +import java.util.Map; +import java.util.UUID; + +public class PlayerListener implements Listener { + + private final Practice plugin = Practice.getInstance(); + + @EventHandler + public void onPlayerInteractSoup(final PlayerInteractEvent event) { + final Player player = event.getPlayer(); + + if (!player.isDead() && player.getItemInHand().getType() == Material.MUSHROOM_SOUP && player.getHealth() < 19.0) { + final double newHealth = Math.min(player.getHealth() + 7.0, 20.0); + + player.setHealth(newHealth); + player.getItemInHand().setType(Material.BOWL); + player.updateInventory(); + } + } + + private final PotionEffect regenerationEffect = new PotionEffect(PotionEffectType.REGENERATION, 200, 1); + private final PotionEffect absorbtionEffect = new PotionEffect(PotionEffectType.ABSORPTION, 2400, 0); + + @EventHandler + public void onPlayerItemConsume(PlayerItemConsumeEvent event) { + if (event.getItem().getType() == Material.GOLDEN_APPLE) { + if (!event.getItem().hasItemMeta() || !event.getItem().getItemMeta().getDisplayName().contains("Golden Head")) { + return; + } + + final PlayerData playerData = this.plugin.getPlayerManager().getPlayerData(event.getPlayer().getUniqueId()); + + if (playerData.getPlayerState() == PlayerState.FIGHTING) { + final Player player = event.getPlayer(); + + event.getPlayer().addPotionEffect(this.regenerationEffect); + event.getPlayer().addPotionEffect(this.absorbtionEffect); + + player.setFoodLevel(Math.min(player.getFoodLevel() + 6, 20)); + } + } + } + + @EventHandler + public void onRegenerate(EntityRegainHealthEvent event) { + if (!(event.getEntity() instanceof Player)) { + return; + } + if (event.getRegainReason() != EntityRegainHealthEvent.RegainReason.SATIATED) { + return; + } + + final Player player = (Player) event.getEntity(); + final PlayerData playerData = this.plugin.getPlayerManager().getPlayerData(player.getUniqueId()); + + if (playerData.getPlayerState() == PlayerState.FIGHTING) { + final Match match = this.plugin.getMatchManager().getMatch(player.getUniqueId()); + + if (match.getKit().getFlag().equals(Flag.BUILD)) { + event.setCancelled(true); + } + } + } + + @EventHandler + public void onAsyncPreLogin(AsyncPlayerPreLoginEvent event) { + this.plugin.getPlayerManager().createPlayerData(event); + } + + private final String centeredTitle = StringUtil.getCentered(CC.GOLD + CC.BOLD + "NA Practice"); + private final String centeredSubTitle = StringUtil.getCentered(CC.GRAY + "Click one of the swords to start fighting!"); + + @EventHandler + public void onPlayerJoin(PlayerJoinEvent event) { + final Player player = event.getPlayer(); + final PracticeServerType serverType = Practice.getInstance().getCurrentType(); + + this.plugin.getPlayerManager().sendToSpawnAndReset(player); + + player.sendMessage(new String[]{ + " ", + this.centeredTitle, + this.centeredSubTitle, + " " + }); + + switch (serverType) { + case PROD_WHITELISTED: + Practice.getInstance().fetchNitroReward(player).whenComplete((aBoolean, throwable) -> { + if (throwable != null) { + throwable.printStackTrace(); + } + + final PotPlayer potPlayer = CorePlugin.getInstance() + .getPlayerManager().getPlayer(player); + + if (aBoolean && !potPlayer.getAllPrefixes().contains("Booster")) { + final Prefix prefix = Prefix.getByName("Booster"); + + if (prefix != null) { + potPlayer.getAllPrefixes().add(prefix.getName()); + potPlayer.saveWithoutRemove(); + + player.sendMessage(ChatColor.GREEN + "You've been given access to the " + Color.translate(prefix.getDisplayName()) + ChatColor.GREEN + " prefix."); + } + + CorePlugin.getInstance().getJedisManager().runCommand(jedis -> { + jedis.hdel(PracticeConstants.JEDIS_DISCORD_REWARD_CATEGORY, player.getUniqueId().toString()); + }); + } + }); + case PROD_RELEASE_DAY: + if (!PracticeConstants.CHANGELOG.isEmpty()) { + player.sendMessage(CC.DARK_AQUA + CC.BOLD + " NEW FEATURES:"); + + PracticeConstants.CHANGELOG.forEach(s -> { + player.sendMessage(CC.GRAY + CC.BOLD + " ■ " + s); + }); + + player.sendMessage(" "); + } + break; + } + + event.setJoinMessage(null); + } + + @EventHandler + public void onPlayerQuit(PlayerQuitEvent event) { + final Player player = event.getPlayer(); + final Party party = this.plugin.getPartyManager().getParty(player.getUniqueId()); + final PlayerData playerData = this.plugin.getPlayerManager().getPlayerData(player.getUniqueId()); + + event.setQuitMessage(null); + + if (playerData == null) { + return; + } + + switch (playerData.getPlayerState()) { + case FIGHTING: + this.plugin.getMatchManager().removeFighter(player, playerData, false); + break; + case SPECTATING: + this.plugin.getMatchManager().removeSpectator(player, false); + break; + case EDITING: + this.plugin.getEditorManager().removeEditor(player.getUniqueId()); + break; + case QUEUE: + if (party == null) { + this.plugin.getQueueManager().removePlayerFromQueue(player); + } else if (this.plugin.getPartyManager().isLeader(player.getUniqueId())) { + this.plugin.getQueueManager().removePartyFromQueue(party); + } + break; + case FFA: + this.plugin.getFfaManager().removePlayer(player); + break; + } + + this.plugin.getTournamentManager().leaveTournament(player); + this.plugin.getPartyManager().leaveParty(player); + + this.plugin.getMatchManager().removeMatchRequests(player.getUniqueId()); + this.plugin.getPartyManager().removePartyInvites(player.getUniqueId()); + this.plugin.getPlayerManager().removePlayerData(player.getUniqueId()); + } + + @EventHandler + public void onPlayerKick(PlayerKickEvent event) { + final Player player = event.getPlayer(); + final Party party = this.plugin.getPartyManager().getParty(player.getUniqueId()); + final PlayerData playerData = this.plugin.getPlayerManager().getPlayerData(player.getUniqueId()); + + if (playerData == null) { + return; + } + + switch (playerData.getPlayerState()) { + case FIGHTING: + this.plugin.getMatchManager().removeFighter(player, playerData, false); + break; + case SPECTATING: + this.plugin.getMatchManager().removeSpectator(player, false); + break; + case EDITING: + this.plugin.getEditorManager().removeEditor(player.getUniqueId()); + break; + case QUEUE: + if (party == null) { + this.plugin.getQueueManager().removePlayerFromQueue(player); + } else if (this.plugin.getPartyManager().isLeader(player.getUniqueId())) { + this.plugin.getQueueManager().removePartyFromQueue(party); + } + break; + case FFA: + this.plugin.getFfaManager().removePlayer(player); + break; + } + + this.plugin.getTournamentManager().leaveTournament(player); + this.plugin.getPartyManager().leaveParty(player); + + this.plugin.getMatchManager().removeMatchRequests(player.getUniqueId()); + this.plugin.getPartyManager().removePartyInvites(player.getUniqueId()); + this.plugin.getPlayerManager().removePlayerData(player.getUniqueId()); + } + + @EventHandler + public void onPlayerInteract(PlayerInteractEvent event) { + final Player player = event.getPlayer(); + final PotPlayer potPlayer = CorePlugin.getInstance().getPlayerManager().getPlayer(player); + + if (potPlayer.isStaffMode()) { + return; + } + + if (player.getGameMode() == GameMode.CREATIVE) { + return; + } + + final PlayerData playerData = this.plugin.getPlayerManager().getPlayerData(player.getUniqueId()); + + if (event.getClickedBlock() != null && playerData.getPlayerState() == PlayerState.SPAWN) { + event.setCancelled(true); + return; + } + + if (playerData.getPlayerState() == PlayerState.SPECTATING) { + event.setCancelled(true); + } + + if (event.getAction().name().endsWith("_BLOCK")) { + if (event.getClickedBlock().getType().name().contains("SIGN") && event.getClickedBlock().getState() instanceof Sign) { + final Sign sign = (Sign) event.getClickedBlock().getState(); + + if (ChatColor.stripColor(sign.getLine(1)).equals("[Soup]")) { + event.setCancelled(true); + + final Inventory inventory = this.plugin.getServer().createInventory(null, 54, CC.DARK_GRAY + "Soup Refill"); + + for (int i = 0; i < 54; i++) { + inventory.setItem(i, new ItemStack(Material.MUSHROOM_SOUP)); + } + + event.getPlayer().openInventory(inventory); + } + } + if (event.getClickedBlock().getType() == Material.CHEST + || event.getClickedBlock().getType() == Material.ENDER_CHEST) { + event.setCancelled(true); + } + } + + if (event.getAction().name().startsWith("RIGHT_")) { + final ItemStack item = event.getItem(); + final Party party = this.plugin.getPartyManager().getParty(player.getUniqueId()); + + switch (playerData.getPlayerState()) { + case LOADING: + player.sendMessage(CC.RED + "Please wait until your player data is loaded."); + break; + case FIGHTING: + if (item == null) { + return; + } + Match match = this.plugin.getMatchManager().getMatch(playerData); + + switch (item.getType()) { + case ENDER_PEARL: + if (match.getMatchState() == MatchState.STARTING) { + event.setCancelled(true); + player.sendMessage(CC.RED + "You cannot throw enderpearls right now."); + player.updateInventory(); + } + break; + case ENCHANTED_BOOK: + Kit kit = match.getKit(); + PlayerInventory inventory = player.getInventory(); + + int kitIndex = inventory.getHeldItemSlot(); + + if (kitIndex == 8) { + kit.applyToPlayer(player); + player.sendMessage(CC.GREEN + "You've equipped the default " + CC.YELLOW + kit.getName() + CC.GREEN + " loadout."); + } else { + Map kits = playerData.getPlayerKits(kit.getName()); + PlayerKit playerKit = kits.get(kitIndex + 1); + + if (playerKit != null) { + playerKit.applyToPlayer(player); + player.sendMessage(CC.GREEN + "You've equipped the default " + CC.YELLOW + playerKit.getDisplayName() + CC.GREEN + " loadout."); + } + } + break; + } + break; + case SPAWN: + if (item == null) { + return; + } + + switch (item.getType()) { + case DIAMOND_SWORD: + if (!Practice.getInstance().isRankedEnabled()) { + player.sendMessage(CC.RED + "Ranked queues are currently disabled."); + return; + } + + if (!playerData.canPlayRanked() && !player.hasPermission("practice.ranked.bypass")) { + player.sendMessage(ChatColor.RED + "You need " + playerData.winsUntilRankedUnlocked() + " more wins to join ranked queues."); + return; + } + + new JoinQueueMenu(QueueType.RANKED).openMenu(player); + break; + case IRON_SWORD: + new JoinQueueMenu(QueueType.UNRANKED).openMenu(player); + break; + case BLAZE_POWDER: + final UUID rematching = this.plugin.getMatchManager().getRematcher(player.getUniqueId()); + + if (rematching == null) { + player.sendMessage(CC.RED + "The rematch request has expired."); + break; + } + + final Player toRematch = this.plugin.getServer().getPlayer(rematching); + + if (toRematch != null) { + if (this.plugin.getMatchManager().getMatchRequest(toRematch.getUniqueId(), player.getUniqueId()) != null) { + this.plugin.getServer().dispatchCommand(player, "accept " + toRematch.getName()); + } else { + this.plugin.getServer().dispatchCommand(player, "duel " + toRematch.getName()); + } + } else { + player.sendMessage(CC.RED + "That player is now offline."); + } + break; + case GOLD_INGOT: + new ShopCategoryMenu().openMenu(player); + break; + case EYE_OF_ENDER: + player.sendMessage(CC.RED + "This feature is currently unavailable."); + event.setCancelled(true); + break; + case SKULL_ITEM: + player.performCommand("party info"); + break; + case BOOK: + new KitEditorMainMenu().openMenu(player); + break; + case EMERALD: + new SettingsMenu(player).openMenu(player); + break; + case GOLD_AXE: + if (party != null && !this.plugin.getPartyManager().isLeader(player.getUniqueId())) { + player.sendMessage(CC.RED + "You're not the leader of this party."); + return; + } + + new PartyEventsMenu().openMenu(player); + break; + case NETHER_STAR: + if (this.plugin.getPartyManager().getParty(player.getUniqueId()) == null) { + this.plugin.getPartyManager().createParty(player); + } else { + this.plugin.getPartyManager().leaveParty(player); + } + + this.plugin.getFollowManager().stopFollowing(player); + this.plugin.getTournamentManager().leaveTournament(player); + break; + } + break; + case QUEUE: + if (item == null) { + return; + } + + if (item.getType() == Material.INK_SACK) { + if (party == null) { + this.plugin.getQueueManager().removePlayerFromQueue(player); + } else { + this.plugin.getQueueManager().removePartyFromQueue(party); + } + } + break; + case SPECTATING: + if (item == null) { + return; + } + + if (item.getType() == Material.NETHER_STAR) { + if (party == null) { + this.plugin.getMatchManager().removeSpectator(player, false); + } else { + this.plugin.getPartyManager().leaveParty(player); + } + } + + if (item.getType() == Material.COMPASS) { + new SpectateMenu().openMenu(player); + } + + if (item.getType() == Material.EMERALD) { + final Match spectating = this.plugin.getMatchManager() + .getSpectatingMatch(player.getUniqueId()); + + if (spectating != null) { + final Player pvp = Bukkit.getPlayer(spectating.getTeams().get(0).getLeader()); + + if (pvp != null) { + PaperLib.teleportAsync(player, pvp.getLocation()); + } + } + } + case EDITING: + if (event.getClickedBlock() == null) { + return; + } + + switch (event.getClickedBlock().getType()) { + case WALL_SIGN: + case SIGN: + case SIGN_POST: + this.plugin.getEditorManager().removeEditor(player.getUniqueId()); + this.plugin.getPlayerManager().sendToSpawnAndReset(player); + break; + case CHEST: + final Kit kit = this.plugin.getEditorManager().getEditingKit(player.getUniqueId()); + + if (kit.getKitEditContents()[0] != null) { + final Inventory editorInventory = this.plugin.getServer().createInventory(null, 36); + + editorInventory.setContents(kit.getContents()); + player.openInventory(editorInventory); + + event.setCancelled(true); + } + break; + case ANVIL: + new KitEditorMenu(Practice.getInstance().getEditorManager().getEditingKit(player.getUniqueId())).openMenu(player); + event.setCancelled(true); + break; + } + + break; + } + } + } + + @EventHandler + public void onCraft(CraftItemEvent event) { + event.setCancelled(true); + } + + @EventHandler + public void onPlayerDropItem(PlayerDropItemEvent event) { + final Player player = event.getPlayer(); + final PlayerData playerData = this.plugin.getPlayerManager().getPlayerData(player.getUniqueId()); + final Material drop = event.getItemDrop().getItemStack().getType(); + if (playerData.getPlayerState() == PlayerState.FIGHTING) { + switch (drop) { + case ENCHANTED_BOOK: + case DIAMOND_SWORD: + case IRON_AXE: + case DIAMOND_SPADE: + case BOW: + event.setCancelled(true); + } + + if (!event.isCancelled()) { + final Match match = this.plugin.getMatchManager().getMatch(event.getPlayer().getUniqueId()); + + this.plugin.getMatchManager().addDroppedItem(match, event.getItemDrop()); + } + } else { + event.setCancelled(true); + } + } + + + @EventHandler + public void onPlayerConsumeItem(PlayerItemConsumeEvent event) { + final Player player = event.getPlayer(); + final PlayerData playerData = this.plugin.getPlayerManager().getPlayerData(player.getUniqueId()); + final Material drop = event.getItem().getType(); + + switch (playerData.getPlayerState()) { + case EVENT: + case FIGHTING: + if (drop.getId() == 373) { + this.plugin.getServer().getScheduler().runTaskLaterAsynchronously(this.plugin, () -> { + player.setItemInHand(new ItemStack(Material.AIR)); + player.updateInventory(); + }, 1L); + } + break; + } + } + + @EventHandler + public void onPlayerPickupItem(PlayerPickupItemEvent event) { + final Player player = event.getPlayer(); + final PlayerData playerData = this.plugin.getPlayerManager().getPlayerData(player.getUniqueId()); + + if (playerData.getPlayerState() == PlayerState.SPECTATING) { + event.setCancelled(true); + } + } + + @EventHandler + public void onPlayerDeath(PlayerDeathEvent event) { + final Player player = event.getEntity(); + final PlayerData playerData = this.plugin.getPlayerManager().getPlayerData(player.getUniqueId()); + PlayerUtil.animateDeath(player); + switch (playerData.getPlayerState()) { + case FIGHTING: + this.plugin.getMatchManager().removeFighter(player, playerData, true); + break; + case FFA: + for (ItemStack item : player.getInventory().getContents()) { + if (item != null && item.getType() == Material.MUSHROOM_SOUP) { + this.plugin.getFfaManager().getItemTracker().put(player.getWorld().dropItemNaturally(player.getLocation(), item), System.currentTimeMillis()); + } + } + + this.plugin.getFfaManager().getKillStreakTracker().put(player.getUniqueId(), 0); + + final StringBuilder deathMessage = new StringBuilder(CC.RED + player.getName() + CC.GRAY + " has been killed" + (player.getKiller() == null ? "." : " by " + CC.GREEN + player.getKiller().getName() + CC.GRAY + ".")); + + if (player.getKiller() != null) { + final int ks = this.plugin.getFfaManager().getKillStreakTracker().compute(player.getKiller().getUniqueId(), (k, v) -> (v == null ? 0 : v) + 1); + + for (final KillStreak killStreak : this.plugin.getFfaManager().getKillStreaks()) { + if (killStreak.getStreaks().contains(ks)) { + killStreak.giveKillStreak(player.getKiller()); + + deathMessage.append("\n") + .append(CC.AQUA).append(player.getKiller().getName()) + .append(CC.GRAY).append(" is on a ") + .append(CC.YELLOW).append(ks) + .append(CC.GRAY).append(" kill streak!"); + + break; + } + } + } + + for (PlayerData data : this.plugin.getPlayerManager().getAllData()) { + if (data.getPlayerState() == PlayerState.FFA) { + final Player ffaPlayer = this.plugin.getServer().getPlayer(data.getUniqueId()); + + ffaPlayer.sendMessage(deathMessage.toString()); + } + } + + player.getInventory().clear(); + + Bukkit.getScheduler().runTaskLater(this.plugin, () -> { + Practice.getInstance().getFfaManager().resetAndAddBack(player); + }, 10L); + break; + } + + event.getDrops().clear(); + event.setDroppedExp(0); + event.setDeathMessage(null); + PlayerUtil.respawnPlayer(player); + event.getEntity().setHealth(20); + } + + @EventHandler + public void onFoodLevelChange(FoodLevelChangeEvent event) { + final Player player = (Player) event.getEntity(); + final PlayerData playerData = this.plugin.getPlayerManager().getPlayerData(player.getUniqueId()); + + if (playerData.getPlayerState() == PlayerState.FIGHTING) { + final Match match = this.plugin.getMatchManager().getMatch(player.getUniqueId()); + + if (match.getKit().getFlag().equals(Flag.PARKOUR) || match.getKit().getFlag().equals(Flag.SUMO) || match.getKit().getName().contains("Boxing")) { + event.setCancelled(true); + } + } else { + event.setCancelled(true); + } + } + + @EventHandler + public void onProjectileLaunch(ProjectileLaunchEvent event) { + if (event.getEntity().getShooter() instanceof Player) { + final Player shooter = (Player) event.getEntity().getShooter(); + final PlayerData shooterData = this.plugin.getPlayerManager().getPlayerData(shooter.getUniqueId()); + + if (shooterData.getPlayerState() == PlayerState.FIGHTING) { + final Match match = this.plugin.getMatchManager().getMatch(shooter.getUniqueId()); + match.addEntityToRemove(event.getEntity()); + } + } + } + + @EventHandler + public void onProjectileHit(ProjectileHitEvent event) { + if (event.getEntity().getShooter() instanceof Player) { + final Player shooter = (Player) event.getEntity().getShooter(); + final PlayerData shooterData = this.plugin.getPlayerManager().getPlayerData(shooter.getUniqueId()); + + if (shooterData != null) { + if (shooterData.getPlayerState() == PlayerState.FIGHTING) { + final Match match = this.plugin.getMatchManager().getMatch(shooter.getUniqueId()); + + match.removeEntityToRemove(event.getEntity()); + + if (event.getEntityType() == EntityType.ARROW) { + event.getEntity().remove(); + } + } + } + } + } +} diff --git a/src/main/java/com/solexgames/practice/listeners/WorldListener.java b/src/main/java/com/solexgames/practice/listeners/WorldListener.java new file mode 100644 index 0000000..ebffecb --- /dev/null +++ b/src/main/java/com/solexgames/practice/listeners/WorldListener.java @@ -0,0 +1,267 @@ +package com.solexgames.practice.listeners; + +import com.solexgames.practice.Practice; +import com.solexgames.practice.arena.type.StandaloneArena; +import com.solexgames.practice.flags.Flag; +import com.solexgames.practice.match.Match; +import com.solexgames.practice.match.MatchState; +import com.solexgames.practice.player.PlayerData; +import com.solexgames.practice.player.PlayerState; +import com.solexgames.practice.runnable.BlockRemoveRunnable; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.entity.Item; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.block.*; +import org.bukkit.event.hanging.HangingBreakEvent; +import org.bukkit.event.player.PlayerBucketEmptyEvent; +import org.bukkit.event.weather.WeatherChangeEvent; + +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + +public class WorldListener implements Listener { + + private final Practice plugin = Practice.getInstance(); + + @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGH) + public void onBlockBreak(BlockBreakEvent event) { + Player player = event.getPlayer(); + PlayerData playerData = this.plugin.getPlayerManager().getPlayerData(player.getUniqueId()); + + if (playerData == null) { + this.plugin.getLogger().warning(player.getName() + "'s player data is null"); + event.setCancelled(true); + return; + } + + if (playerData.getPlayerState() == PlayerState.FIGHTING) { + Match match = this.plugin.getMatchManager().getMatch(player.getUniqueId()); + + if (match.getKit().getFlag().equals(Flag.BUILD)) { + if (!match.getPlacedBlockLocations().contains(event.getBlock().getLocation())) { + event.setCancelled(true); + } + } else if (match.getKit().getFlag().equals(Flag.SPLEEF)) { + double minX = match.getStandaloneArena().getMin().getX(); + double minZ = match.getStandaloneArena().getMin().getZ(); + double maxX = match.getStandaloneArena().getMax().getX(); + double maxZ = match.getStandaloneArena().getMax().getZ(); + if (minX > maxX) { + double lastMinX = minX; + minX = maxX; + maxX = lastMinX; + } + + if (minZ > maxZ) { + double lastMinZ = minZ; + minZ = maxZ; + maxZ = lastMinZ; + } + if (match.getMatchState() == MatchState.STARTING) { + event.setCancelled(true); + return; + } + if (player.getLocation().getX() >= minX && player.getLocation().getX() <= maxX + && player.getLocation().getZ() >= minZ && player.getLocation().getZ() <= maxZ) { + if (event.getBlock().getType() == Material.SNOW_BLOCK && player.getItemInHand().getType() == Material.DIAMOND_SPADE) { + Location blockLocation = event.getBlock().getLocation(); + event.setCancelled(true); + match.addOriginalBlockChange(event.getBlock().getState()); + Set items = new HashSet<>(); + event.getBlock().getDrops().forEach(itemStack -> items.add(player.getWorld().dropItemNaturally(blockLocation.add(0.0D, 0.25D, 0.0D), itemStack))); + this.plugin.getMatchManager().addDroppedItems(match, items); + event.getBlock().setType(Material.AIR); + } else { + event.setCancelled(true); + } + } else { + event.setCancelled(true); + } + } else { + event.setCancelled(true); + } + } else { + if (!Practice.getInstance().getBuilderSet().contains(player)) { + event.setCancelled(true); + } + } + } + + @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGH) + public void onBlockPlace(BlockPlaceEvent event) { + final Player player = event.getPlayer(); + final PlayerData playerData = this.plugin.getPlayerManager().getPlayerData(player.getUniqueId()); + + if (playerData == null) { + event.setCancelled(true); + return; + } + + if (playerData.getPlayerState() == PlayerState.FIGHTING) { + final Match match = this.plugin.getMatchManager().getMatch(player.getUniqueId()); + + if (match == null) { + event.setCancelled(true); + return; + } + + if (!match.getKit().getFlag().equals(Flag.BUILD)) { + if (match.getKit().getFlag().equals(Flag.STICK_FIGHT)) { + event.setCancelled(false); + + new BlockRemoveRunnable(event.getBlock().getLocation()); + return; + } + + event.setCancelled(true); + } else { + double minX = match.getStandaloneArena().getMin().getX(); + double minZ = match.getStandaloneArena().getMin().getZ(); + double maxX = match.getStandaloneArena().getMax().getX(); + double maxZ = match.getStandaloneArena().getMax().getZ(); + if (minX > maxX) { + double lastMinX = minX; + minX = maxX; + maxX = lastMinX; + } + + if (minZ > maxZ) { + double lastMinZ = minZ; + minZ = maxZ; + maxZ = lastMinZ; + } + if (player.getLocation().getX() >= minX && player.getLocation().getX() <= maxX + && player.getLocation().getZ() >= minZ && player.getLocation().getZ() <= maxZ) { + if ((player.getLocation().getY() - match.getStandaloneArena().getPositionOne().getY()) < 5.0D && event.getBlockPlaced() != null) { + match.addPlacedBlockLocation(event.getBlockPlaced().getLocation()); + } else { + event.setCancelled(true); + } + } else { + event.setCancelled(true); + } + } + return; + } + + if (!this.plugin.getBuilderSet().contains(player)) { + event.setCancelled(true); + } + } + + @EventHandler + public void onBucketEmpty(PlayerBucketEmptyEvent event) { + Player player = event.getPlayer(); + + PlayerData playerData = this.plugin.getPlayerManager().getPlayerData(player.getUniqueId()); + if (playerData == null) { + this.plugin.getLogger().warning(player.getName() + "'s player data is null"); + event.setCancelled(true); + return; + } + if (playerData.getPlayerState() == PlayerState.FIGHTING) { + Match match = this.plugin.getMatchManager().getMatch(player.getUniqueId()); + if (!match.getKit().getFlag().equals(Flag.BUILD)) { + event.setCancelled(true); + } else { + double minX = match.getStandaloneArena().getMin().getX(); + double minZ = match.getStandaloneArena().getMin().getZ(); + double maxX = match.getStandaloneArena().getMax().getX(); + double maxZ = match.getStandaloneArena().getMax().getZ(); + if (minX > maxX) { + double lastMinX = minX; + minX = maxX; + maxX = lastMinX; + } + + if (minZ > maxZ) { + double lastMinZ = minZ; + minZ = maxZ; + maxZ = lastMinZ; + } + if (player.getLocation().getX() >= minX && player.getLocation().getX() <= maxX + && player.getLocation().getZ() >= minZ && player.getLocation().getZ() <= maxZ) { + if ((player.getLocation().getY() - match.getStandaloneArena().getPositionOne().getY()) < 5.0D) { + Block block = event.getBlockClicked().getRelative(event.getBlockFace()); + match.addPlacedBlockLocation(block.getLocation()); + } else { + event.setCancelled(true); + } + } else { + event.setCancelled(true); + } + } + return; + } + + if (!this.plugin.getBuilderSet().contains(player)) { + event.setCancelled(true); + } + } + + @EventHandler + public void onBlockFromTo(BlockFromToEvent event) { + if (event.getToBlock() == null) { + return; + } + for (StandaloneArena arena : this.plugin.getArenaManager().getArenaMatchUUIDs().keySet()) { + double minX = arena.getMin().getX(); + double minZ = arena.getMin().getZ(); + double maxX = arena.getMax().getX(); + double maxZ = arena.getMax().getZ(); + if (minX > maxX) { + double lastMinX = minX; + minX = maxX; + maxX = lastMinX; + } + + if (minZ > maxZ) { + double lastMinZ = minZ; + minZ = maxZ; + maxZ = lastMinZ; + } + if (event.getToBlock().getX() >= minX && event.getToBlock().getZ() >= minZ + && event.getToBlock().getX() <= maxX && event.getToBlock().getZ() <= maxZ) { + UUID matchUUID = this.plugin.getArenaManager().getArenaMatchUUID(arena); + Match match = this.plugin.getMatchManager().getMatchFromUUID(matchUUID); + + match.addPlacedBlockLocation(event.getToBlock().getLocation()); + break; + } + } + } + + @EventHandler + public void onWeatherChange(WeatherChangeEvent event) { + if (event.toWeatherState()) { + event.setCancelled(true); + } + } + + @EventHandler + public void onLeavesDecay(LeavesDecayEvent event) { + event.setCancelled(true); + } + + @EventHandler + public void onHangingBreak(HangingBreakEvent event) { + event.setCancelled(true); + } + + @EventHandler + public void onBlockBurn(BlockBurnEvent event) { + event.setCancelled(true); + } + + @EventHandler + public void onBlockSpread(BlockSpreadEvent event) { + event.setCancelled(true); + } + +} diff --git a/src/main/java/com/solexgames/practice/location/WrappedLocation.java b/src/main/java/com/solexgames/practice/location/WrappedLocation.java new file mode 100644 index 0000000..f44cc26 --- /dev/null +++ b/src/main/java/com/solexgames/practice/location/WrappedLocation.java @@ -0,0 +1,39 @@ +package com.solexgames.practice.location; + +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.entity.Player; + +import java.util.concurrent.CompletableFuture; + +/** + * @author GrowlyX + * @since 8/6/2021 + */ + +public interface WrappedLocation { + + double getX(); + + double getY(); + + double getZ(); + + float getYaw(); + + float getPitch(); + + String getWorldName(); + + CompletableFuture teleport(Player player); + + default World getWorld() { + return Bukkit.getWorld(getWorldName()); + } + + default Location toBukkitLocation() { + return new Location(getWorld(), getX(), getY(), getZ(), getYaw(), getPitch()); + } + +} diff --git a/src/main/java/com/solexgames/practice/location/impl/LocationImpl.java b/src/main/java/com/solexgames/practice/location/impl/LocationImpl.java new file mode 100644 index 0000000..7bc9a43 --- /dev/null +++ b/src/main/java/com/solexgames/practice/location/impl/LocationImpl.java @@ -0,0 +1,18 @@ +package com.solexgames.practice.location.impl; + +import com.solexgames.practice.location.WrappedLocation; +import lombok.Data; + +/** + * @author GrowlyX + * @since 8/6/2021 + */ + +@Data +public abstract class LocationImpl implements WrappedLocation { + + private final double x, y, z; + private final float yaw, pitch; + private final String worldName; + +} diff --git a/src/main/java/com/solexgames/practice/location/impl/impl/AsyncLocation.java b/src/main/java/com/solexgames/practice/location/impl/impl/AsyncLocation.java new file mode 100644 index 0000000..2ace8d1 --- /dev/null +++ b/src/main/java/com/solexgames/practice/location/impl/impl/AsyncLocation.java @@ -0,0 +1,54 @@ +package com.solexgames.practice.location.impl.impl; + +import com.solexgames.core.CorePlugin; +import com.solexgames.practice.location.impl.LocationImpl; +import com.solexgames.practice.util.old.DeprecatedLocation; +import io.papermc.lib.PaperLib; +import org.bukkit.Location; +import org.bukkit.entity.Player; + +import java.util.concurrent.CompletableFuture; + +public class AsyncLocation extends LocationImpl { + + public AsyncLocation(double x, double y, double z, float yaw, float pitch, String worldName) { + super(x, y, z, yaw, pitch, worldName); + } + + @Override + public CompletableFuture teleport(Player player) { + final CompletableFuture future = new CompletableFuture<>(); + final Location location = toBukkitLocation(); + + PaperLib.teleportAsync(player, location).whenComplete((aBoolean, throwable) -> future.complete(null)); + + return future; + } + + public String toJson() { + return CorePlugin.GSON.toJson(this); + } + + public static AsyncLocation of(Location location) { + return new AsyncLocation(location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch(), location.getWorld().getName()); + } + + public static AsyncLocation of(String string) { + try { + final AsyncLocation asyncLocation = CorePlugin.GSON.fromJson(string, AsyncLocation.class); + + if (asyncLocation == null) { + final Location location = DeprecatedLocation.stringToLocation(string).toBukkitLocation(); + + return AsyncLocation.of(location); + } else { + return asyncLocation; + } + } catch (Exception ignored) { + final Location location = DeprecatedLocation.stringToLocation(string).toBukkitLocation(); + + return AsyncLocation.of(location); + } + } + +} diff --git a/src/main/java/com/solexgames/practice/managers/ArenaManager.java b/src/main/java/com/solexgames/practice/managers/ArenaManager.java new file mode 100644 index 0000000..b5f9151 --- /dev/null +++ b/src/main/java/com/solexgames/practice/managers/ArenaManager.java @@ -0,0 +1,187 @@ +package com.solexgames.practice.managers; + +import com.solexgames.practice.Practice; +import com.solexgames.practice.arena.Arena; +import com.solexgames.practice.arena.type.StandaloneArena; +import com.solexgames.practice.flags.Flag; +import com.solexgames.practice.kit.Kit; +import com.solexgames.practice.location.impl.impl.AsyncLocation; +import com.solexgames.practice.util.FlatFile; +import lombok.Getter; +import lombok.Setter; +import org.bukkit.Material; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.file.FileConfiguration; + +import java.util.*; +import java.util.concurrent.ThreadLocalRandom; + +@Getter +@Setter +public class ArenaManager { + + private final Practice plugin = Practice.getInstance(); + private final FlatFile config = new FlatFile("arenas", this.plugin); + + private final Map arenas = new HashMap<>(); + private final Map arenaMatchUUIDs = new HashMap<>(); + + private int generatingArenaTask; + + public ArenaManager() { + this.loadArenas(); + } + + private void loadArenas() { + FileConfiguration fileConfig = config.getConfig(); + ConfigurationSection arenaSection = fileConfig.getConfigurationSection("arenas"); + + if (arenaSection == null) { + return; + } + + arenaSection.getKeys(false).forEach(name -> { + String a = arenaSection.getString(name + ".a"); + String b = arenaSection.getString(name + ".b"); + String min = arenaSection.getString(name + ".min"); + String max = arenaSection.getString(name + ".max"); + Material icon = arenaSection.getString(name + ".icon") == null ? Material.PAPER : Material.valueOf(arenaSection.getString(name + ".icon")); + + AsyncLocation locA = AsyncLocation.of(a); + AsyncLocation locB = AsyncLocation.of(b); + AsyncLocation locMin = AsyncLocation.of(min); + AsyncLocation locMax = AsyncLocation.of(max); + + List standaloneArenas = new ArrayList<>(); + + ConfigurationSection saSection = arenaSection.getConfigurationSection(name + ".standaloneArenas"); + + if (saSection != null) { + saSection.getKeys(false).forEach(id -> { + String saA = saSection.getString(id + ".a"); + String saB = saSection.getString(id + ".b"); + String saMin = saSection.getString(id + ".min"); + String saMax = saSection.getString(id + ".max"); + + AsyncLocation locSaA = AsyncLocation.of(saA); + AsyncLocation locSaB = AsyncLocation.of(saB); + AsyncLocation locSaMin = AsyncLocation.of(saMin); + AsyncLocation locSaMax = AsyncLocation.of(saMax); + + standaloneArenas.add(new StandaloneArena(locSaA, locSaB, locSaMin, locSaMax)); + }); + } + + final boolean enabled = arenaSection.getBoolean(name + ".enabled", false); + final Flag arenaFlag = Flag.valueOf(arenaSection.getString(name + ".flag", "DEFAULT")); + + final Arena arena = new Arena( + name, + standaloneArenas, + new ArrayList<>(standaloneArenas), + locA, locB, + locMin, locMax, + enabled, + arenaFlag, + icon + ); + + this.arenas.put(name, arena); + }); + } + + public void saveArenas() { + final FileConfiguration fileConfig = this.config.getConfig(); + + this.arenas.forEach((arenaName, arena) -> { + try { + final String a = arena.getPositionOne().toJson(); + final String b = arena.getPositionTwo().toJson(); + final String min = arena.getMin().toJson(); + final String max = arena.getMax().toJson(); + + final String arenaRoot = "arenas." + arenaName; + + fileConfig.set(arenaRoot + ".a", a); + fileConfig.set(arenaRoot + ".b", b); + fileConfig.set(arenaRoot + ".min", min); + fileConfig.set(arenaRoot + ".max", max); + fileConfig.set(arenaRoot + ".icon", arena.getIcon() != null ? arena.getIcon().name() : Material.PAPER.name()); + fileConfig.set(arenaRoot + ".enabled", arena.isEnabled()); + fileConfig.set(arenaRoot + ".flag", arena.getFlag().name()); + fileConfig.set(arenaRoot + ".standaloneArenas", null); + + int i = 0; + if (arena.getStandaloneArenas() != null) { + for (StandaloneArena saArena : arena.getStandaloneArenas()) { + String saA = saArena.getPositionOne().toJson(); + String saB = saArena.getPositionTwo().toJson(); + String saMin = saArena.getMin().toJson(); + String saMax = saArena.getMax().toJson(); + + String standAloneRoot = arenaRoot + ".standaloneArenas." + i; + + fileConfig.set(standAloneRoot + ".a", saA); + fileConfig.set(standAloneRoot + ".b", saB); + fileConfig.set(standAloneRoot + ".min", saMin); + fileConfig.set(standAloneRoot + ".max", saMax); + + i++; + } + } + } catch (Exception ignored) { + System.out.println("[Practice] [Arena] Failed to save arena: " + arenaName); + } + }); + + this.config.save(); + } + + public void reloadArenas() { + this.saveArenas(); + + this.arenas.clear(); + + this.loadArenas(); + } + + public void createArena(String name) { + this.arenas.put(name, new Arena(name)); + } + + public void deleteArena(String name) { + this.arenas.remove(name); + } + + public Arena getArena(String name) { + return this.arenas.get(name); + } + + public Arena getRandomArena(Kit kit) { + final List pairedArenas = new ArrayList<>(); + + for (final Arena arena : this.arenas.values()) { + if (Practice.getInstance().isKitPairedWithArena(kit, arena)) { + pairedArenas.add(arena); + } + } + + if (pairedArenas.size() == 0) { + return null; + } + + return pairedArenas.get(ThreadLocalRandom.current().nextInt(pairedArenas.size())); + } + + public void removeArenaMatchUUID(StandaloneArena arena) { + this.arenaMatchUUIDs.remove(arena); + } + + public UUID getArenaMatchUUID(StandaloneArena arena) { + return this.arenaMatchUUIDs.get(arena); + } + + public void setArenaMatchUUID(StandaloneArena arena, UUID matchUUID) { + this.arenaMatchUUIDs.put(arena, matchUUID); + } +} diff --git a/src/main/java/com/solexgames/practice/managers/ChunkManager.java b/src/main/java/com/solexgames/practice/managers/ChunkManager.java new file mode 100644 index 0000000..5e4c6a9 --- /dev/null +++ b/src/main/java/com/solexgames/practice/managers/ChunkManager.java @@ -0,0 +1,78 @@ +package com.solexgames.practice.managers; + +import com.solexgames.practice.Practice; +import com.solexgames.practice.arena.Arena; +import com.solexgames.practice.arena.type.StandaloneArena; +import com.solexgames.practice.location.impl.impl.AsyncLocation; +import io.papermc.lib.PaperLib; +import lombok.Getter; + +public class ChunkManager { + + private final Practice plugin = Practice.getInstance(); + + @Getter + private boolean chunksLoaded; + + public ChunkManager() { + this.plugin.getServer().getScheduler().runTaskLater(this.plugin, this::loadChunks, 1L); + } + + private void loadChunks() { + this.plugin.getLogger().info("Loading chunks for important locations..."); + + final AsyncLocation spawnMin = this.plugin.getSpawnManager().fetchSingleLocation("spawn-min").orElse(null); + final AsyncLocation spawnMax = this.plugin.getSpawnManager().fetchSingleLocation("spawn-max").orElse(null); + + this.loadChunkFromCoordinates(spawnMin, spawnMax); + + final AsyncLocation editorMin = this.plugin.getSpawnManager().fetchSingleLocation("editor-min").orElse(null); + final AsyncLocation editorMax = this.plugin.getSpawnManager().fetchSingleLocation("editor-max").orElse(null); + + this.loadChunkFromCoordinates(editorMin, editorMax); + + final AsyncLocation sumoMin = this.plugin.getSpawnManager().fetchSingleLocation("sumo-min").orElse(null); + final AsyncLocation sumoMax = this.plugin.getSpawnManager().fetchSingleLocation("sumo-max").orElse(null); + + this.loadChunkFromCoordinates(sumoMin, sumoMax); + + for (final Arena arena : this.plugin.getArenaManager().getArenas().values()) { + this.loadChunkFromCoordinates(arena.getMin(), arena.getMax()); + + for (final StandaloneArena saArena : arena.getStandaloneArenas()) { + this.loadChunkFromCoordinates(saArena.getMin(), saArena.getMax()); + } + } + + this.chunksLoaded = true; + + this.plugin.getLogger().info("Finished loading all the chunks!"); + } + + private void loadChunkFromCoordinates(AsyncLocation spawnMin, AsyncLocation spawnMax) { + if (spawnMin != null && spawnMax != null) { + int spawnMinX = spawnMin.toBukkitLocation().getBlockX() >> 4; + int spawnMinZ = spawnMin.toBukkitLocation().getBlockZ() >> 4; + int spawnMaxX = spawnMax.toBukkitLocation().getBlockX() >> 4; + int spawnMaxZ = spawnMax.toBukkitLocation().getBlockZ() >> 4; + + if (spawnMinX > spawnMaxX) { + int lastSpawnMinX = spawnMinX; + spawnMinX = spawnMaxX; + spawnMaxX = lastSpawnMinX; + } + + if (spawnMinZ > spawnMaxZ) { + int lastSpawnMinZ = spawnMinZ; + spawnMinZ = spawnMaxZ; + spawnMaxZ = lastSpawnMinZ; + } + + for (int x = spawnMinX; x <= spawnMaxX; x++) { + for (int z = spawnMinZ; z <= spawnMaxZ; z++) { + PaperLib.getChunkAtAsync(spawnMin.getWorld(), x, z); + } + } + } + } +} diff --git a/src/main/java/com/solexgames/practice/managers/EditorManager.java b/src/main/java/com/solexgames/practice/managers/EditorManager.java new file mode 100644 index 0000000..6b5b571 --- /dev/null +++ b/src/main/java/com/solexgames/practice/managers/EditorManager.java @@ -0,0 +1,48 @@ +package com.solexgames.practice.managers; + +import com.solexgames.core.util.Color; +import com.solexgames.practice.Practice; +import com.solexgames.practice.kit.Kit; +import com.solexgames.practice.location.impl.impl.AsyncLocation; +import com.solexgames.practice.util.CC; +import com.solexgames.practice.util.PlayerUtil; +import io.papermc.lib.PaperLib; +import org.bukkit.entity.Player; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.UUID; + +public class EditorManager { + + private final Practice plugin = Practice.getInstance(); + private final Map editing = new HashMap<>(); + + public void addEditor(Player player, Kit kit) { + final Optional asyncLocationOptional = this.plugin + .getSpawnManager().fetchSingleLocation("editor"); + + asyncLocationOptional.ifPresent(asyncLocation -> { + this.editing.put(player.getUniqueId(), kit); + + PlayerUtil.clearPlayer(player); + asyncLocation.teleport(player); + + player.getInventory().setContents(kit.getContents()); + + player.sendMessage(new String[]{ + Color.SECONDARY_COLOR + "You're currently editing the " + Color.MAIN_COLOR + kit.getName() + Color.SECONDARY_COLOR + " kit!", + CC.GRAY + CC.ITALIC + "Armor is applied automatically. Abuse of glitches automatically result in a ban." + }); + }); + } + + public void removeEditor(UUID editor) { + this.editing.remove(editor); + } + + public Kit getEditingKit(UUID editor) { + return this.editing.get(editor); + } +} diff --git a/src/main/java/com/solexgames/practice/managers/EventManager.java b/src/main/java/com/solexgames/practice/managers/EventManager.java new file mode 100644 index 0000000..defebd5 --- /dev/null +++ b/src/main/java/com/solexgames/practice/managers/EventManager.java @@ -0,0 +1,87 @@ +package com.solexgames.practice.managers; + +import com.solexgames.practice.Practice; +import com.solexgames.practice.events.Event; +import com.solexgames.practice.events.EventState; +import com.solexgames.practice.player.PlayerData; +import com.solexgames.practice.player.PlayerState; +import lombok.Setter; +import org.bukkit.Bukkit; +import org.bukkit.GameMode; +import org.bukkit.entity.Player; + +import java.util.*; +import java.util.concurrent.ThreadLocalRandom; + +/** + * @author GrowlyX + * @since 8/14/2021 + */ + +@Setter +public class EventManager { + + private final List> events = Arrays.asList( +// new SumoEvent() + ); + + private final HashMap> spectators = new HashMap<>(); + + private long cooldown; + +// public Event getByName(String name) { +// return this.events.stream() +// .filter(event -> event.getName().equalsIgnoreCase(name)) +// .findFirst().orElse(null); +// } +// +// public void hostEvent(Event event, Player host) { +// event.setState(EventState.WAITING); +// event.setHost(host); +// +// event.startCountdown(); +// } + + public void addSpectatorSumo(Player player, PlayerData playerData/*, SumoEvent event*/) { +// this.addSpectator(player, playerData, event); + +// if (event.getSpawnLocations().size() == 1) { +// player.teleport(event.getSpawnLocations().get(0).toBukkitLocation()); +// } else { +// List spawnLocations = new ArrayList<>(event.getSpawnLocations()); +// player.teleport(spawnLocations.remove(ThreadLocalRandom.current().nextInt(spawnLocations.size())).toBukkitLocation()); +// } +// +// for (Player eventPlayer : event.getBukkitPlayers()) { +// player.showPlayer(eventPlayer); +// } + + player.setGameMode(GameMode.ADVENTURE); + + player.setAllowFlight(true); + player.setFlying(true); + } + + private void addSpectator(Player player, PlayerData playerData, Event event) { + playerData.setPlayerState(PlayerState.SPECTATING); + this.spectators.put(player.getUniqueId(), event); + + player.getInventory().setContents(Practice.getInstance().getItemManager().getSpecItems()); + player.updateInventory(); + + Bukkit.getOnlinePlayers().forEach(online -> { + online.hidePlayer(player); + player.hidePlayer(online); + }); + } + + public void removeSpectator(Player player) { + this.spectators.remove(player.getUniqueId()); + +// if (Practice.getInstance().getEventManager().getEventPlaying(player) != null) { +// Practice.getInstance().getEventManager().getEventPlaying(player).getPlayers().remove(player.getUniqueId()); +// } + + Practice.getInstance().getPlayerManager().sendToSpawnAndReset(player); + } +} diff --git a/src/main/java/com/solexgames/practice/managers/FollowManager.java b/src/main/java/com/solexgames/practice/managers/FollowManager.java new file mode 100644 index 0000000..730a623 --- /dev/null +++ b/src/main/java/com/solexgames/practice/managers/FollowManager.java @@ -0,0 +1,79 @@ +package com.solexgames.practice.managers; + +import com.solexgames.core.CorePlugin; +import com.solexgames.core.player.PotPlayer; +import com.solexgames.practice.Practice; +import com.solexgames.practice.player.PlayerData; +import com.solexgames.practice.util.CC; +import io.papermc.lib.PaperLib; +import org.bukkit.entity.Player; + +import java.util.List; +import java.util.stream.Collectors; + +/** + * @author GrowlyX + * @since 7/10/2021 + */ + +public class FollowManager { + + public List getPlayerWhoIsFollowingPlayer(Player player) { + return Practice.getInstance().getPlayerManager().getAllData().stream() + .filter(playerData -> playerData.getFollowing() != null && playerData.getFollowing().equals(player)) + .collect(Collectors.toList()); + } + + public void stopFollowing(Player player) { + final PlayerData playerData = Practice.getInstance().getPlayerManager().getPlayerData(player.getUniqueId()); + + if (playerData.getFollowing() != null) { + Practice.getInstance().getPlayerManager().sendToSpawnAndReset(player); + player.sendMessage(CC.YELLOW + "You've stopped following " + playerData.getFollowing().getDisplayName() + CC.YELLOW + "."); + + playerData.setFollowing(null); + } + } + + public void followPlayer(Player player, Player target) { + final PlayerData playerData = Practice.getInstance().getPlayerManager().getPlayerData(player.getUniqueId()); + final PlayerData targetData = Practice.getInstance().getPlayerManager().getPlayerData(target.getUniqueId()); + + if (this.getPlayerWhoIsFollowingPlayer(target).contains(playerData)) { + player.sendMessage(CC.RED + "Error: You're already following " + CC.YELLOW + target.getName() + CC.RED + "."); + return; + } + + if (playerData.getFollowing() != null) { + player.sendMessage(CC.RED + "Error: You're already following " + CC.YELLOW + playerData.getFollowing().getName() + CC.RED + "."); + return; + } + + player.setAllowFlight(true); + player.setFlying(true); + + switch (targetData.getPlayerState()) { + case SPAWN: + final PotPlayer potPlayer = CorePlugin.getInstance().getPlayerManager().getPlayer(player); + + if (potPlayer.isStaffMode()) { + player.getInventory().setItem(4, ItemManager.LEAVE_FOLLOW); + } else { + player.getInventory().setContents(Practice.getInstance().getItemManager().getSpecItems()); + } + + player.updateInventory(); + + Practice.getInstance().getEntityHider().showEntity(player, target); + PaperLib.teleportAsync(player, target.getLocation()); + break; + case FIGHTING: + Practice.getInstance().getMatchManager().addSpectator(player, playerData, target, Practice.getInstance().getMatchManager().getMatch(targetData)); + break; + } + + playerData.setFollowing(target); + + player.sendMessage(CC.YELLOW + "You're now following " + target.getDisplayName() + CC.YELLOW + "."); + } +} diff --git a/src/main/java/com/solexgames/practice/managers/FunManager.java b/src/main/java/com/solexgames/practice/managers/FunManager.java new file mode 100644 index 0000000..2b5e610 --- /dev/null +++ b/src/main/java/com/solexgames/practice/managers/FunManager.java @@ -0,0 +1,53 @@ +package com.solexgames.practice.managers; + +import com.solexgames.practice.effects.DeathEffect; +import com.solexgames.practice.effects.impl.BloodDeathEffect; +import com.solexgames.practice.effects.impl.ExplosionDeathEffect; +import com.solexgames.practice.effects.impl.LightningDeathEffect; +import com.solexgames.practice.message.KillMessage; +import com.solexgames.practice.message.impl.DefaultDeathMessage; +import com.solexgames.practice.message.impl.GamerKillMessages; +import com.solexgames.practice.message.impl.KitchenKillMessages; +import com.solexgames.practice.message.impl.NerdyKillMessages; +import lombok.Getter; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author GrowlyX + * @since 5/26/2021 + */ + +@Getter +public class FunManager { + + private final List effectList = new ArrayList<>(); + private final List messageList = new ArrayList<>(); + + private final KillMessage defKillMessage = new DefaultDeathMessage(); + private final DeathEffect defDeathEffect = new LightningDeathEffect(); + + public void setup() { + this.effectList.add(new LightningDeathEffect()); + this.effectList.add(new ExplosionDeathEffect()); + this.effectList.add(new BloodDeathEffect()); + + this.messageList.add(new GamerKillMessages()); + this.messageList.add(new NerdyKillMessages()); + this.messageList.add(new KitchenKillMessages()); + this.messageList.add(new DefaultDeathMessage()); + } + + public DeathEffect getByName(String name) { + return this.effectList.stream() + .filter(deathEffect -> deathEffect.getName().equalsIgnoreCase(name)) + .findFirst().orElse(this.defDeathEffect); + } + + public KillMessage getMessageByName(String name) { + return this.messageList.stream() + .filter(deathEffect -> deathEffect.getName().equalsIgnoreCase(name)) + .findFirst().orElse(this.defKillMessage); + } +} diff --git a/src/main/java/com/solexgames/practice/managers/ItemManager.java b/src/main/java/com/solexgames/practice/managers/ItemManager.java new file mode 100644 index 0000000..42514ec --- /dev/null +++ b/src/main/java/com/solexgames/practice/managers/ItemManager.java @@ -0,0 +1,161 @@ +package com.solexgames.practice.managers; + +import com.solexgames.core.util.Color; +import com.solexgames.core.util.builder.ItemBuilder; +import com.solexgames.practice.util.CC; +import com.solexgames.practice.util.ItemUtil; +import lombok.Getter; +import org.bukkit.Material; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.inventory.ItemStack; + +@Getter +public class ItemManager { + + public static final ItemStack LEAVE_SPEC = ItemUtil.createItem(Material.NETHER_STAR, CC.RED + "Leave Spectator" + CC.GRAY + " (Right Click)"); + public static final ItemStack LEAVE_FOLLOW = ItemUtil.createItem(Material.NETHER_STAR, CC.RED + "Stop Following" + CC.GRAY + " (Right Click)"); + public static final ItemStack SHOP_ITEM = ItemUtil.createItem(Material.GOLD_INGOT, CC.GOLD + "Shop" + CC.GRAY + " (Right Click)"); + + private final ItemStack[] spawnItems; + private final ItemStack[] queueItems; + private final ItemStack[] partyItems; + private final ItemStack[] partyNonLeaderItems; + private final ItemStack[] tournamentItems; + private final ItemStack[] eventItems; + private final ItemStack[] specItems; + private final ItemStack[] boxingItems; + private final ItemStack[] stickFightItems; + private final ItemStack[] partySpecItems; + + private final ItemStack defaultBook; + + public ItemManager() { + this.spawnItems = new ItemStack[]{ + ItemUtil.createItem(Material.IRON_SWORD, CC.GOLD + "Join Unranked Matchmaking" + CC.GRAY + " (Right Click)"), + ItemUtil.createItem(Material.DIAMOND_SWORD, CC.GOLD + "Join Ranked Matchmaking" + CC.GRAY + " (Right Click)"), + ItemUtil.createItem(Material.NETHER_STAR, CC.GOLD + "Create a Party" + CC.GRAY + " (Right Click)"), + null, + ItemUtil.createItem(Material.GOLD_INGOT, CC.GOLD + "Shop" + CC.GRAY + " (Right Click)"), + null, + ItemUtil.createItem(Material.EYE_OF_ENDER, CC.GOLD + "Daily Challenges" + CC.GRAY + " (Right Click)"), + ItemUtil.createItem(Material.BOOK, CC.GOLD + "Kit Editor" + CC.GRAY + " (Right Click)"), + ItemUtil.createItem(Material.EMERALD, CC.GOLD + "Settings" + CC.GRAY + " (Right Click)") + }; + this.queueItems = new ItemStack[]{ + null, + null, + null, + null, + null, + null, + null, + null, + new ItemBuilder(Material.INK_SACK) + .setDurability(1) + .setDisplayName(CC.RED + "Leave Queue" + CC.GRAY + " (Right Click)") + .create(), + }; + this.specItems = new ItemStack[]{ + ItemUtil.createItem(Material.COMPASS, CC.RED + "Spectate a Match" + CC.GRAY + " (Right Click)"), + null, + null, + null, + ItemUtil.createItem(Material.EMERALD, CC.RED + "Teleport to last PvP" + CC.GRAY + " (Right Click)"), + null, + null, + null, + ItemUtil.createItem(Material.NETHER_STAR, CC.RED + "Leave Spectator" + CC.GRAY + " (Right Click)"), + }; + this.partySpecItems = new ItemStack[]{ + null, + null, + null, + null, + null, + null, + null, + null, + ItemUtil.createItem(Material.NETHER_STAR, CC.RED + "Leave Party" + CC.GRAY + " (Right Click)"), + }; + this.tournamentItems = new ItemStack[]{ + null, + null, + null, + null, + null, + null, + null, + null, + ItemUtil.createItem(Material.NETHER_STAR, CC.RED + "Leave Tournament" + CC.GRAY + " (Right Click)"), + }; + this.eventItems = new ItemStack[]{ + null, + null, + null, + null, + null, + null, + null, + null, + ItemUtil.createItem(Material.NETHER_STAR, CC.RED + "Leave Event" + CC.GRAY + " (Right Click)"), + }; + this.partyItems = new ItemStack[]{ + ItemUtil.createItem(Material.GOLD_AXE, CC.GOLD + "Party Events" + CC.GRAY + " (Right Click)"), + ItemUtil.createItem(Material.SKULL_ITEM, CC.GOLD + "Party Information" + CC.GRAY + " (Right Click)"), + null, + null, + null, + null, + null, + ItemUtil.createItem(Material.BOOK, CC.GOLD + "Edit Kits" + CC.GRAY + " (Right Click)"), + ItemUtil.createItem(Material.NETHER_STAR, CC.RED + "Leave Party" + CC.GRAY + " (Right Click)") + }; + this.partyNonLeaderItems = new ItemStack[]{ + ItemUtil.createItem(Material.SKULL_ITEM, CC.GOLD + "Party Information" + CC.GRAY + " (Right Click)"), + ItemUtil.createItem(Material.BOOK, CC.GOLD + "Edit Kits" + CC.GRAY + " (Right Click)"), + null, + null, + null, + null, + null, + null, + ItemUtil.createItem(Material.NETHER_STAR, CC.RED + "Leave Party" + CC.GRAY + " (Right Click)") + }; + this.boxingItems = new ItemStack[]{ + new ItemBuilder(Material.DIAMOND_SWORD) + .setDisplayName(Color.MAIN_COLOR + CC.GOLD + "Boxing Sword") + .setEnchant(Enchantment.DAMAGE_ALL, 2) + .setUnbreakable(true) + .create(), + null, + null, + null, + null, + null, + null, + null, + null, + }; + this.stickFightItems = new ItemStack[]{ + new com.solexgames.practice.util.ItemBuilder(Material.STICK) + .enchantment(Enchantment.KNOCKBACK, 1) + .unbreakable() + .build(), + new ItemBuilder(Material.SHEARS) + .setUnbreakable(true) + .create(), + new ItemBuilder(Material.WOOL) + .setDurability(1) + .setAmount(5) + .create(), + null, + null, + null, + null, + null, + null, + }; + + this.defaultBook = ItemUtil.createItem(Material.ENCHANTED_BOOK, CC.GOLD + "Default Kit " + CC.GRAY + "(Click to Equip)"); + } +} diff --git a/src/main/java/com/solexgames/practice/managers/KitManager.java b/src/main/java/com/solexgames/practice/managers/KitManager.java new file mode 100644 index 0000000..fdded6f --- /dev/null +++ b/src/main/java/com/solexgames/practice/managers/KitManager.java @@ -0,0 +1,99 @@ +package com.solexgames.practice.managers; + +import com.solexgames.practice.Practice; +import com.solexgames.practice.flags.Flag; +import com.solexgames.practice.kit.Kit; +import com.solexgames.practice.util.FlatFile; +import lombok.Getter; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.inventory.ItemStack; + +import java.util.*; + +@Getter +public class KitManager { + + private final Practice plugin = Practice.getInstance(); + + private final Map kits = new HashMap<>(); + private final List rankedKits = new ArrayList<>(); + + private final FlatFile config = new FlatFile("kits", this.plugin); + + public KitManager() { + this.loadKits(); + + this.kits.entrySet().stream() + .filter(kit -> kit.getValue().isEnabled() && kit.getValue().isRanked()) + .forEach(kit -> this.rankedKits.add(kit.getKey())); + } + + private void loadKits() { + final ConfigurationSection kitSection = this.config.getConfig().getConfigurationSection("kits"); + + if (kitSection == null) { + return; + } + + kitSection.getKeys(false).forEach(name -> { + final ItemStack[] contents = ((List) kitSection.getList(name + ".contents")).toArray(new ItemStack[0]); + final ItemStack[] armor = ((List) kitSection.get(name + ".armor")).toArray(new ItemStack[0]); + final ItemStack[] kitEditContents = ((List) kitSection.get(name + ".kitEditContents")).toArray(new ItemStack[0]); + + final ItemStack icon = (ItemStack) kitSection.get(name + ".icon"); + + final boolean enabled = kitSection.getBoolean(name + ".enabled", false); + final boolean ranked = kitSection.getBoolean(name + ".ranked", false); + final int q = kitSection.getInt(name + ".queue", 0); + + final Flag kitFlag = Flag.valueOf(kitSection.getString(name + ".flag", "DEFAULT")); + final Kit kit = new Kit(name, contents, armor, kitEditContents, icon, icon, enabled, ranked, false, q, kitFlag); + + this.kits.put(name, kit); + }); + } + + public void saveKits() { + final FileConfiguration fileConfig = this.config.getConfig(); + + fileConfig.set("kits", null); + + this.kits.forEach((kitName, kit) -> { + if (kit.getIcon() != null && kit.getContents() != null && kit.getArmor() != null) { + fileConfig.set("kits." + kitName + ".contents", kit.getContents()); + fileConfig.set("kits." + kitName + ".armor", kit.getArmor()); + fileConfig.set("kits." + kitName + ".kitEditContents", kit.getKitEditContents()); + fileConfig.set("kits." + kitName + ".icon", kit.getIcon()); + fileConfig.set("kits." + kitName + ".enabled", kit.isEnabled()); + fileConfig.set("kits." + kitName + ".ranked", kit.isRanked()); + fileConfig.set("kits." + kitName + ".flag", kit.getFlag().name()); + fileConfig.set("kits." + kitName + ".queue", kit.getQueueMenu()); + } + }); + + this.config.save(); + } + + public void deleteKit(String name) { + this.kits.remove(name); + } + + public void createKit(String name) { + final Kit kit = new Kit(name); + + kit.setEnabled(true); + kit.setRanked(true); + + this.kits.put(name, new Kit(name)); + } + + public Collection getKits() { + return this.kits.values(); + } + + public Kit getKit(String name) { + return this.kits.get(name); + } + +} diff --git a/src/main/java/com/solexgames/practice/managers/MatchManager.java b/src/main/java/com/solexgames/practice/managers/MatchManager.java new file mode 100644 index 0000000..14d950b --- /dev/null +++ b/src/main/java/com/solexgames/practice/managers/MatchManager.java @@ -0,0 +1,573 @@ +package com.solexgames.practice.managers; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import com.solexgames.core.CorePlugin; +import com.solexgames.core.player.PotPlayer; +import com.solexgames.core.util.Color; +import com.solexgames.practice.Practice; +import com.solexgames.practice.arena.Arena; +import com.solexgames.practice.event.match.MatchEndEvent; +import com.solexgames.practice.event.match.MatchStartEvent; +import com.solexgames.practice.kit.Kit; +import com.solexgames.practice.kit.PlayerKit; +import com.solexgames.practice.match.Match; +import com.solexgames.practice.match.MatchRequest; +import com.solexgames.practice.match.MatchState; +import com.solexgames.practice.match.snapshot.InventorySnapshot; +import com.solexgames.practice.match.team.impl.MatchTeam; +import com.solexgames.practice.message.KillMessage; +import com.solexgames.practice.player.PlayerData; +import com.solexgames.practice.player.PlayerState; +import com.solexgames.practice.queue.QueueType; +import com.solexgames.practice.runnable.RematchRunnable; +import com.solexgames.practice.util.CC; +import com.solexgames.practice.util.ItemUtil; +import com.solexgames.practice.util.PlayerUtil; +import com.solexgames.practice.util.TtlHashMap; +import io.papermc.lib.PaperLib; +import lombok.Getter; +import net.minecraft.server.v1_8_R3.EntityPlayer; +import org.bson.Document; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.Material; +import org.bukkit.craftbukkit.v1_8_R3.entity.CraftPlayer; +import org.bukkit.entity.Item; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; + +import java.lang.reflect.Constructor; +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; +import java.util.function.Consumer; + +public class MatchManager { + + private final Map> matchRequests = new TtlHashMap<>(TimeUnit.SECONDS, 30); + private final Map rematchUUIDs = new TtlHashMap<>(TimeUnit.SECONDS, 30); + private final Map rematchInventories = new TtlHashMap<>(TimeUnit.SECONDS, 30); + private final Map spectators = new ConcurrentHashMap<>(); + + @Getter + private final Map matches = new ConcurrentHashMap<>(); + + private final Practice plugin = Practice.getInstance(); + + public void createMatchRequest(Player requester, Player requested, Arena arena, String kitName, boolean party) { + final MatchRequest request = new MatchRequest(requester.getUniqueId(), requested.getUniqueId(), arena, kitName, party); + + this.matchRequests.computeIfAbsent(requested.getUniqueId(), k -> new HashSet<>()).add(request); + } + + public MatchRequest getMatchRequest(UUID requester, UUID requested) { + final Set requests = this.matchRequests.get(requested); + + if (requests == null) { + return null; + } + + return requests.stream().filter(req -> req.getRequester().equals(requester)).findAny().orElse(null); + } + + public MatchRequest getMatchRequest(UUID requester, UUID requested, String kitName) { + final Set requests = this.matchRequests.get(requested); + + if (requests == null) { + return null; + } + return requests.stream().filter(req -> req.getRequester().equals(requester) && req.getKitName().equals(kitName)) + .findAny().orElse(null); + } + + public Match getMatch(PlayerData playerData) { + if (playerData == null || playerData.getCurrentMatchID() == null) { + return null; + } + + return this.matches.get(playerData.getCurrentMatchID()); + } + + public Match getMatch(UUID uuid) { + final PlayerData playerData = this.plugin.getPlayerManager().getPlayerData(uuid); + + return this.getMatch(playerData); + } + + public Match getMatchFromUUID(UUID uuid) { + return this.matches.get(uuid); + } + + public Match getSpectatingMatch(UUID uuid) { + return this.matches.get(this.spectators.get(uuid)); + } + + public void removeMatchRequests(UUID uuid) { + this.matchRequests.remove(uuid); + } + + public void createMatch(Match match, boolean noXpMatch) { + match.setShouldGiveXp(!noXpMatch); + + this.matches.put(match.getMatchId(), match); + this.plugin.getServer().getPluginManager().callEvent(new MatchStartEvent(match)); + } + + public void storeMatchInDatabase(Match match, HashMap elo, Consumer idConsumer) { + CompletableFuture.supplyAsync(() -> { + final JsonObject matchObject = new JsonObject(); + final JsonArray teamsArray = new JsonArray(); + + match.getTeams().forEach(team -> { + final JsonObject teamObject = new JsonObject(); + final JsonArray playersArray = new JsonArray(); + + team.getPlayers().forEach((uuid) -> { + final JsonObject playerObject = new JsonObject(); + final PlayerData data = Practice.getInstance().getPlayerManager().getPlayerData(uuid); + final InventorySnapshot snapshot = Practice.getInstance().getInventoryManager().getByPlayer(uuid); + + playerObject.addProperty("uuid", uuid.toString()); + playerObject.addProperty("name", Bukkit.getOfflinePlayer(uuid).getName()); + playerObject.addProperty("changedElo", (match.getType().isRanked() ? (data.getElo(match.getKit().getName()) - elo.get(uuid)) : 0)); + + playerObject.addProperty("health", snapshot.getHealth()); + playerObject.addProperty("hunger", snapshot.getFood()); + playerObject.addProperty("longestCombo", snapshot.getLongestCombo()); + playerObject.addProperty("potionCount", snapshot.getPotCount()); + playerObject.addProperty("soupCount", snapshot.getSoupCount()); + playerObject.addProperty("totalHits", snapshot.getTotalHits()); + + playersArray.add(playerObject); + }); + + teamObject.addProperty("id", team.getTeamID()); + teamObject.add("players", playersArray); + teamsArray.add(teamObject); + }); + + matchObject.addProperty("_id", UUID.randomUUID().toString()); + matchObject.addProperty("matchId", match.getShortMatchId()); + + matchObject.addProperty("ladder", match.getKit().getName()); + matchObject.addProperty("queueType", match.getType().name()); + matchObject.addProperty("ladderIcon", match.getKit().getIcon().getType().name().toLowerCase()); + + matchObject.addProperty("arena", match.getArena().getName()); + matchObject.addProperty("time", System.currentTimeMillis()); + matchObject.addProperty("ffa", match.isFFA()); + matchObject.add("teams", teamsArray); + matchObject.addProperty("winningTeam", match.getWinningTeamId()); + + if (match.isFFA()) { + final JsonObject winnerObject = new JsonObject(); + final Player winner = Bukkit.getPlayer(match.getTeams().get(0).getAlivePlayers().get(0)); + + winnerObject.addProperty("name", winner.getName()); + winnerObject.addProperty("uuid", winner.getUniqueId().toString()); + matchObject.add("winningPlayer", winnerObject); + } + + MongoManager.getInstance().getMatchOverviews().insertOne(Document.parse(matchObject.toString())); + + return match.getShortMatchId(); + }).thenAccept(idConsumer); + } + + public void removeFighter(Player player, PlayerData playerData, boolean spectateDeath) { + final Match match = this.matches.get(playerData.getCurrentMatchID()); + final Player killer = player.getKiller(); + + final MatchTeam entityTeam = match.getTeams().get(playerData.getTeamID()); + final MatchTeam winningTeam = match.isFFA() ? entityTeam : match.getTeams().get(entityTeam.getTeamID() == 0 ? 1 : 0); + + if (match.getMatchState() == MatchState.ENDING) { + return; + } + + match.addSnapshot(player); + + if (killer != null) { + final PlayerData data = Practice.getInstance().getPlayerManager() + .getPlayerData(killer.getUniqueId()); + + data.getEffect().applyEffect(player, killer); + + final KillMessage killMessage = Practice.getInstance().getEffectManager() + .getMessageByName(data.getDeathMessageType()); + + match.broadcast(killMessage.getFormatted(player, killer, true)); + } else { + match.broadcast(CC.RED + player.getName() + CC.GRAY + " has been killed."); + } + + player.setHealth(20); + player.setFoodLevel(20); + player.setSaturation(20); + + entityTeam.killPlayer(player.getUniqueId()); + + final int remaining = entityTeam.getAlivePlayers().size(); + + if (remaining != 0) { + final Set items = new HashSet<>(); + + for (ItemStack item : player.getInventory().getContents()) { + if (item != null && item.getType() != Material.AIR) { + items.add(player.getWorld().dropItemNaturally(player.getLocation(), item)); + } + } + for (ItemStack item : player.getInventory().getArmorContents()) { + if (item != null && item.getType() != Material.AIR) { + items.add(player.getWorld().dropItemNaturally(player.getLocation(), item)); + } + } + + this.plugin.getMatchManager().addDroppedItems(match, items); + } + + if (spectateDeath) { + this.addDeathSpectator(player, playerData, match); + } + + if (player.isDead()) { + Bukkit.getScheduler().runTask(plugin, () -> player.spigot().respawn()); + } + + if ((match.isFFA() && remaining == 1) || remaining == 0) { + this.plugin.getServer().getPluginManager().callEvent(new MatchEndEvent(match, winningTeam, entityTeam)); + } + } + + public void removeMatch(Match match) { + this.matches.remove(match.getMatchId()); + } + + public void giveKits(Player player, Kit kit) { + final PlayerData playerData = this.plugin.getPlayerManager().getPlayerData(player.getUniqueId()); + final Collection playerKits = playerData.getPlayerKits(kit.getName()).values(); + + if (playerKits.size() == 0) { + kit.applyToPlayer(player); + } else { + player.getInventory().setItem(8, this.plugin.getItemManager().getDefaultBook()); + + int slot = -1; + for (PlayerKit playerKit : playerKits) { + player.getInventory().setItem(++slot, ItemUtil.createItem(Material.ENCHANTED_BOOK, Color.MAIN_COLOR + playerKit.getDisplayName() + CC.GRAY + " (Click to equip)")); + } + + player.updateInventory(); + } + } + + private void addDeathSpectator(Player player, PlayerData playerData, Match match) { + this.spectators.put(player.getUniqueId(), match.getMatchId()); + + playerData.setPlayerState(PlayerState.SPECTATING); + + PlayerUtil.clearPlayer(player); + + final CraftPlayer playerCp = (CraftPlayer) player; + final EntityPlayer playerEp = playerCp.getHandle(); + + playerEp.getDataWatcher().watch(6, 0.0F); + + match.addRunnable(this.plugin.getServer().getScheduler().scheduleSyncDelayedTask(this.plugin, () -> { + match.getTeams().forEach(team -> team.streamAlivePlayers().forEach(member -> Practice.getInstance().getEntityHider().hideEntity(member, player))); + match.streamSpectators().forEach(member -> Practice.getInstance().getEntityHider().hideEntity(member, player)); + + player.getActivePotionEffects().stream().map(PotionEffect::getType).forEach(player::removePotionEffect); + player.setWalkSpeed(0.2F); + player.setAllowFlight(true); + }, 20L)); + + if (match.isRedRover()) { + for (MatchTeam team : match.getTeams()) { + for (UUID alivePlayerUUID : team.getAlivePlayers()) { + final Player alivePlayer = this.plugin.getServer().getPlayer(alivePlayerUUID); + + if (alivePlayer != null) { + Practice.getInstance().getEntityHider().showEntity(player, alivePlayer); + } + } + } + } + + player.setWalkSpeed(0.0F); + player.addPotionEffect(new PotionEffect(PotionEffectType.JUMP, 10000, -5)); + + if (match.isParty() || match.isFFA()) { + this.plugin.getServer().getScheduler().runTaskLater(this.plugin, () -> + player.getInventory().setContents(this.plugin.getItemManager().getPartySpecItems()), 1L); + } + + player.updateInventory(); + } + + public void addSpectator(Player player, PlayerData playerData, Player target, Match targetMatch) { + this.spectators.put(player.getUniqueId(), targetMatch.getMatchId()); + + if (targetMatch.getMatchState() != MatchState.ENDING) { + if (!targetMatch.haveSpectated(player.getUniqueId())) { + if (!player.hasPermission("practice.staff")) { + targetMatch.broadcast(player.getDisplayName() + Color.SECONDARY_COLOR + " is now spectating your match."); + } + } + } + + targetMatch.addSpectator(player.getUniqueId()); + + playerData.setPlayerState(PlayerState.SPECTATING); + + PaperLib.teleportAsync(player, target.getLocation()); + + player.setAllowFlight(true); + player.setFlying(true); + + final PotPlayer potPlayer = CorePlugin.getInstance().getPlayerManager().getPlayer(player); + + if (potPlayer.isStaffMode()) { + player.getInventory().setItem(4, ItemManager.LEAVE_SPEC); + } else { + player.getInventory().setContents(this.plugin.getItemManager().getSpecItems()); + } + + player.updateInventory(); + + this.plugin.getServer().getOnlinePlayers().forEach(online -> { + Practice.getInstance().getEntityHider().hideEntity(player, online); + Practice.getInstance().getEntityHider().hideEntity(online, player); + }); + + targetMatch.getTeams().forEach(team -> team.streamAlivePlayers().forEach(player1 -> { + Practice.getInstance().getEntityHider().showEntity(player, player1); + })); + } + + public void addDroppedItem(Match match, Item item) { + match.addEntityToRemove(item); + + match.addRunnable(this.plugin.getServer().getScheduler().runTaskLater(this.plugin, () -> { + match.removeEntityToRemove(item); + }, 200L).getTaskId()); + } + + public void addDroppedItems(Match match, Set items) { + for (Item item : items) { + match.addEntityToRemove(item); + } + + match.addRunnable(this.plugin.getServer().getScheduler().runTaskLater(this.plugin, () -> { + for (Item item : items) { + match.removeEntityToRemove(item); + item.remove(); + } + }, 100L).getTaskId()); + } + + public void removeSpectator(Player player, boolean silent) { + final Match match = this.matches.get(this.spectators.get(player.getUniqueId())); + + match.removeSpectator(player.getUniqueId()); + + final PlayerData playerData = this.plugin.getPlayerManager().getPlayerData(player.getUniqueId()); + + if (match.getTeams().size() > playerData.getTeamID() && playerData.getTeamID() >= 0) { + final MatchTeam entityTeam = match.getTeams().get(playerData.getTeamID()); + + if (entityTeam != null) { + entityTeam.killPlayer(player.getUniqueId()); + } + } + + if (match.getMatchState() != MatchState.ENDING) { + if (!match.haveSpectated(player.getUniqueId())) { + match.addHaveSpectated(player.getUniqueId()); + } + } + + if (!player.hasPermission("practice.staff") && !silent) { + match.broadcast(player.getDisplayName() + Color.SECONDARY_COLOR + " is no longer spectating your match."); + } + + this.spectators.remove(player.getUniqueId()); + this.plugin.getPlayerManager().sendToSpawnAndReset(player); + } + + public void pickPlayer(Match match) { + final Player playerA = this.plugin.getServer().getPlayer(match.getTeams().get(0).getAlivePlayers().get(0)); + final PlayerData playerDataA = this.plugin.getPlayerManager().getPlayerData(playerA.getUniqueId()); + + if (playerDataA.getPlayerState() != PlayerState.FIGHTING) { + playerA.teleport(match.getArena().getPositionOne().toBukkitLocation()); + PlayerUtil.clearPlayer(playerA); + + if (match.getKit().getName().contains("Combo")) { + playerA.setMaximumNoDamageTicks(3); + } + + this.plugin.getMatchManager().giveKits(playerA, match.getKit()); + playerDataA.setPlayerState(PlayerState.FIGHTING); + } + + final Player playerB = this.plugin.getServer().getPlayer(match.getTeams().get(1).getAlivePlayers().get(0)); + final PlayerData playerDataB = this.plugin.getPlayerManager().getPlayerData(playerB.getUniqueId()); + + if (playerDataB.getPlayerState() != PlayerState.FIGHTING) { + playerB.teleport(match.getArena().getPositionTwo().toBukkitLocation()); + PlayerUtil.clearPlayer(playerB); + + if (match.getKit().getName().contains("Combo")) { + playerB.setMaximumNoDamageTicks(3); + } + + this.plugin.getMatchManager().giveKits(playerB, match.getKit()); + playerDataB.setPlayerState(PlayerState.FIGHTING); + } + + for (MatchTeam team : match.getTeams()) { + for (UUID uuid : team.getAlivePlayers()) { + final Player player = this.plugin.getServer().getPlayer(uuid); + + if (player != null) { + if (!playerA.equals(player) && !playerB.equals(player)) { + Practice.getInstance().getEntityHider().hideEntity(playerA, player); + Practice.getInstance().getEntityHider().hideEntity(playerB, player); + } + } + } + } + + Practice.getInstance().getEntityHider().showEntity(playerA, playerB); + Practice.getInstance().getEntityHider().showEntity(playerB, playerA); + + match.broadcast(Color.SECONDARY_COLOR + "Starting a 1v1 match. " + Color.MAIN_COLOR + playerA.getName() + " vs " + playerB.getName()); + } + + public void saveRematches(Match match) { + if (match.isParty() || match.isFFA()) { + return; + } + + final UUID playerOne = match.getTeams().get(0).getLeader(); + final UUID playerTwo = match.getTeams().get(1).getLeader(); + + final PlayerData dataOne = this.plugin.getPlayerManager().getPlayerData(playerOne); + final PlayerData dataTwo = this.plugin.getPlayerManager().getPlayerData(playerTwo); + + this.saveRematchFor(match, playerOne, playerTwo, dataOne); + this.saveRematchFor(match, playerTwo, playerOne, dataTwo); + } + + private void saveRematchFor(Match match, UUID playerOne, UUID playerTwo, PlayerData dataOne) { + InventorySnapshot snapshot; + + if (dataOne != null) { + this.rematchUUIDs.put(playerOne, playerTwo); + + snapshot = match.getSnapshot(playerTwo); + if (snapshot != null) { + this.rematchInventories.put(playerOne, snapshot.getSnapshotId()); + } + if (dataOne.getRematchID() > -1) { + this.plugin.getServer().getScheduler().cancelTask(dataOne.getRematchID()); + } + + dataOne.setRematchID(this.plugin.getServer().getScheduler().scheduleSyncDelayedTask(this.plugin, new RematchRunnable(playerOne), 600L)); + } + } + + public void removeRematch(UUID uuid) { + this.rematchUUIDs.remove(uuid); + this.rematchInventories.remove(uuid); + } + + public UUID getRematcher(UUID uuid) { + return this.rematchUUIDs.get(uuid); + } + + public UUID getRematcherInventory(UUID uuid) { + return this.rematchInventories.get(uuid); + } + + public boolean isRematching(UUID uuid) { + return this.rematchUUIDs.containsKey(uuid); + } + + /** + * Send a title to player + * + * @param player Player to send the title to + * @param text The text displayed in the title + * @param fadeInTime The time the title takes to fade in + * @param showTime The time the title is displayed + * @param fadeOutTime The time the title takes to fade out + * @param color The color of the title + */ + public void sendTitle(Player player, String text, int fadeInTime, int showTime, int fadeOutTime, ChatColor color) { + try { + Object chatTitle = getNMSClass("IChatBaseComponent").getDeclaredClasses()[0].getMethod("a", String.class).invoke(null, "{\"text\": \"" + text + "\",color:" + color.name().toLowerCase() + "}"); + + Constructor titleConstructor = getNMSClass("PacketPlayOutTitle").getConstructor(getNMSClass("PacketPlayOutTitle").getDeclaredClasses()[0], getNMSClass("IChatBaseComponent"), int.class, int.class, int.class); + Object packet = titleConstructor.newInstance(getNMSClass("PacketPlayOutTitle").getDeclaredClasses()[0].getField("TITLE").get(null), chatTitle, fadeInTime, showTime, fadeOutTime); + + sendPacket(player, packet); + } catch (Exception ex) { + //Do something + } + } + + /** + * Send a subtitle to player + * + * @param player Player to send the title to + * @param text The text displayed in the title + * @param fadeInTime The time the title takes to fade in + * @param showTime The time the title is displayed + * @param fadeOutTime The time the title takes to fade out + * @param color The color of the title + */ + public void sendSubtitle(Player player, String text, int fadeInTime, int showTime, int fadeOutTime, ChatColor color) { + try { + Object chatTitle = getNMSClass("IChatBaseComponent").getDeclaredClasses()[0].getMethod("a", String.class).invoke(null, "{\"text\": \"" + text + "\",color:" + color.name().toLowerCase() + "}"); + + Constructor titleConstructor = getNMSClass("PacketPlayOutTitle").getConstructor(getNMSClass("PacketPlayOutTitle").getDeclaredClasses()[0], getNMSClass("IChatBaseComponent"), int.class, int.class, int.class); + Object packet = titleConstructor.newInstance(getNMSClass("PacketPlayOutTitle").getDeclaredClasses()[0].getField("SUBTITLE").get(null), chatTitle, fadeInTime, showTime, fadeOutTime); + + sendPacket(player, packet); + } catch (Exception ex) { + //Do something + } + } + + private void sendPacket(Player player, Object packet) { + try { + Object handle = player.getClass().getMethod("getHandle").invoke(player); + Object playerConnection = handle.getClass().getField("playerConnection").get(handle); + playerConnection.getClass().getMethod("sendPacket", getNMSClass("Packet")).invoke(playerConnection, packet); + } catch (Exception ex) { + //Do something + } + } + + /** + * Get NMS class using reflection + * + * @param name Name of the class + * + * @return Class + */ + private Class getNMSClass(String name) { + try { + return Class.forName("net.minecraft.server" + Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3] + "." + name); + } catch (ClassNotFoundException ex) { + //Do something + } + return null; + } +} diff --git a/src/main/java/com/solexgames/practice/managers/MongoManager.java b/src/main/java/com/solexgames/practice/managers/MongoManager.java new file mode 100644 index 0000000..9577a3e --- /dev/null +++ b/src/main/java/com/solexgames/practice/managers/MongoManager.java @@ -0,0 +1,37 @@ +package com.solexgames.practice.managers; + +import com.mongodb.MongoClient; +import com.mongodb.client.MongoCollection; +import com.mongodb.client.MongoDatabase; +import com.solexgames.core.CorePlugin; +import lombok.Getter; +import org.bson.Document; + +@Getter +public class MongoManager { + + @Getter + private static MongoManager instance; + + private final MongoClient client; + private final MongoDatabase database; + + private final MongoCollection players; + private final MongoCollection matchOverviews; + private final MongoCollection arenaRatings; + + public MongoManager() { + if (instance != null) { + throw new RuntimeException("The mongo database has already been instantiated."); + } + + instance = this; + + this.client = CorePlugin.getInstance().getCoreDatabase().getClient(); + this.database = this.client.getDatabase("SGSoftware"); + + this.players = this.database.getCollection("practice_profiles"); + this.matchOverviews = this.database.getCollection("practice_matches"); + this.arenaRatings = this.database.getCollection("practice_ratings"); + } +} diff --git a/src/main/java/com/solexgames/practice/managers/PartyManager.java b/src/main/java/com/solexgames/practice/managers/PartyManager.java new file mode 100644 index 0000000..5c08a5a --- /dev/null +++ b/src/main/java/com/solexgames/practice/managers/PartyManager.java @@ -0,0 +1,147 @@ +package com.solexgames.practice.managers; + +import com.solexgames.core.util.Color; +import com.solexgames.practice.Practice; +import com.solexgames.practice.PracticeConstants; +import com.solexgames.practice.party.Party; +import com.solexgames.practice.player.PlayerData; +import com.solexgames.practice.player.PlayerState; +import com.solexgames.practice.util.CC; +import com.solexgames.practice.util.TtlHashMap; +import lombok.Getter; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; + +import java.util.*; +import java.util.concurrent.TimeUnit; + +@Getter +public class PartyManager { + + private final Practice plugin = Practice.getInstance(); + + private final Map> partyInvites = new TtlHashMap<>(TimeUnit.SECONDS, 15); + private final Map parties = new HashMap<>(); + private final Map partyLeaders = new HashMap<>(); + + public boolean isLeader(UUID uuid) { + return this.parties.containsKey(uuid); + } + + public void removePartyInvites(UUID uuid) { + this.partyInvites.remove(uuid); + } + + public boolean hasPartyInvite(UUID player, UUID other) { + return this.partyInvites.get(player) != null && this.partyInvites.get(player).contains(other); + } + + public void createPartyInvite(UUID requester, UUID requested) { + this.partyInvites.computeIfAbsent(requested, k -> new ArrayList<>()).add(requester); + } + + public boolean isInParty(UUID player, Party party) { + final Party targetParty = this.getParty(player); + return targetParty != null && targetParty.getLeader() == party.getLeader(); + } + + public Party getParty(UUID player) { + if (this.parties.containsKey(player)) { + return this.parties.get(player); + } + + if (this.partyLeaders.containsKey(player)) { + final UUID leader = this.partyLeaders.get(player); + return this.parties.get(leader); + } + + return null; + } + + public void createParty(Player player) { + final Party party = new Party(player.getUniqueId()); + + this.parties.put(player.getUniqueId(), party); + this.plugin.getPlayerManager().reset(Bukkit.getPlayer(player.getUniqueId())); + + player.sendMessage(PracticeConstants.PARTY_PREFIX + CC.GREEN + (player.getName().equals("threazy") ? "您創建了一個新派對" : "You've created a new party.")); + } + + private void disbandParty(Party party, boolean tournament) { + this.parties.remove(party.getLeader()); + + party.broadcast(PracticeConstants.PARTY_PREFIX + CC.RED + "Your party has been disbanded" + (tournament ? " as one of the party members left during the tournament." : ".")); + party.streamPartyMembers().forEach(member -> { + final PlayerData memberData = this.plugin.getPlayerManager().getPlayerData(member.getUniqueId()); + + if (this.partyLeaders.get(memberData.getUniqueId()) != null) { + this.partyLeaders.remove(memberData.getUniqueId()); + } + if (memberData.getPlayerState() == PlayerState.SPAWN) { + this.plugin.getPlayerManager().reset(Bukkit.getPlayer(memberData.getUniqueId())); + } + }); + } + + public void leaveParty(Player player) { + final Party party = this.getParty(player.getUniqueId()); + + if (party == null) { + return; + } + + final PlayerData playerData = this.plugin.getPlayerManager().getPlayerData(player.getUniqueId()); + + if (this.parties.containsKey(player.getUniqueId())) { + this.disbandParty(party, false); + } else if (this.plugin.getTournamentManager().getTournament(player.getUniqueId()) != null) { + this.disbandParty(party, true); + } else { + party.broadcast(PracticeConstants.PARTY_PREFIX + player.getDisplayName() + CC.YELLOW + " has left the party."); + party.removeMember(player.getUniqueId()); + + this.partyLeaders.remove(player.getUniqueId()); + } + + switch (playerData.getPlayerState()) { + case FIGHTING: + this.plugin.getMatchManager().removeFighter(player, playerData, false); + break; + case SPECTATING: + this.plugin.getMatchManager().removeSpectator(player, false); + break; + } + + party.getMembers().forEach(uuid -> { + final Player other = Bukkit.getPlayer(uuid); + + Practice.getInstance().getEntityHider().hideEntity(player, other); + Practice.getInstance().getEntityHider().hideEntity(other, player); + }); + + this.plugin.getPlayerManager().reset(player); + } + + public void joinParty(UUID leader, Player player) { + final Party party = this.getParty(leader); + + if (this.plugin.getTournamentManager().getTournament(leader) != null) { + player.sendMessage(PracticeConstants.PARTY_PREFIX + CC.RED + "The leader of the party you tried to join is currently in a party."); + return; + } + + this.partyLeaders.put(player.getUniqueId(), leader); + party.addMember(player.getUniqueId()); + + this.plugin.getPlayerManager().sendToSpawnAndReset(player); + + party.getMembers().forEach(uuid -> { + final Player other = Bukkit.getPlayer(uuid); + + Practice.getInstance().getEntityHider().showEntity(player, other); + Practice.getInstance().getEntityHider().showEntity(other, player); + }); + + party.broadcast(PracticeConstants.PARTY_PREFIX + player.getDisplayName() + CC.YELLOW + " has joined the party."); + } +} diff --git a/src/main/java/com/solexgames/practice/managers/PlayerManager.java b/src/main/java/com/solexgames/practice/managers/PlayerManager.java new file mode 100644 index 0000000..c93b88b --- /dev/null +++ b/src/main/java/com/solexgames/practice/managers/PlayerManager.java @@ -0,0 +1,290 @@ +package com.solexgames.practice.managers; + +import com.mongodb.client.model.Filters; +import com.mongodb.client.model.ReplaceOptions; +import com.solexgames.core.CorePlugin; +import com.solexgames.core.player.PotPlayer; +import com.solexgames.core.util.builder.ItemBuilder; +import com.solexgames.practice.Practice; +import com.solexgames.practice.PracticeConstants; +import com.solexgames.practice.kit.Kit; +import com.solexgames.practice.kit.PlayerKit; +import com.solexgames.practice.party.Party; +import com.solexgames.practice.player.PlayerData; +import com.solexgames.practice.player.PlayerState; +import com.solexgames.practice.settings.item.ProfileOptionsItemState; +import com.solexgames.practice.util.CC; +import com.solexgames.practice.util.ItemUtil; +import com.solexgames.practice.util.PlayerUtil; +import com.solexgames.practice.util.division.RankedDivision; +import com.solexgames.practice.util.timer.impl.EnderpearlTimer; +import lombok.Getter; +import me.lucko.helper.Schedulers; +import org.bson.Document; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.event.player.AsyncPlayerPreLoginEvent; +import org.bukkit.inventory.ItemStack; + +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; + +@Getter +public class PlayerManager { + + private static final ItemStack REMATCH_ITEM = new ItemBuilder(Material.BLAZE_POWDER) + .setDisplayName(CC.AQUA + "Rematch " + CC.GRAY + "(Right Click)") + .addLore( + CC.GRAY + "Click to rematch your opponent." + ) + .create(); + + private final Practice plugin = Practice.getInstance(); + private final Map playerData = new ConcurrentHashMap<>(); + + public void createPlayerData(AsyncPlayerPreLoginEvent event) { + final PlayerData data = new PlayerData(event.getUniqueId(), event.getName()); + this.playerData.put(data.getUniqueId(), data); + + this.loadData(data); + } + + private void loadData(PlayerData playerData) { + playerData.setPlayerState(PlayerState.SPAWN); + + CompletableFuture.supplyAsync(() -> MongoManager.getInstance().getPlayers().find(Filters.eq("_id", playerData.getUniqueId())).first()) + .thenAcceptAsync(document -> { + if (document == null) { + this.saveData(playerData); + return; + } + + playerData.getOptions().setSpectators(document.getBoolean("allowingSpectators")); + playerData.getOptions().setDuelRequests(document.getBoolean("acceptingDuels")); + playerData.getOptions().setAutoGg(document.getBoolean("autoGg")); + + playerData.getOptions().setScoreboardEnabled(document.getBoolean("scoreboardEnabled")); + playerData.getOptions().setScoreboardState(ProfileOptionsItemState.valueOf(document.getString("scoreboardStyle"))); + + playerData.setUnrankedWinCount(document.getInteger("unrankedWins")); + playerData.setRankedWinCount(document.getInteger("rankedWins")); + playerData.setUnrankedLossCount(document.getInteger("unrankedLosses")); + playerData.setRankedLossCount(document.getInteger("rankedLosses")); + playerData.setRankedDivision(RankedDivision.valueOf(document.getString("rankedDivisionCurrent"))); + playerData.setEffect(Practice.getInstance().getEffectManager().getByName(document.getString("effect"))); + playerData.getMatchIds().addAll(document.getList("matches", String.class)); + + final String deathMessage = document.getString("deathMessage"); + playerData.setDeathMessageType(deathMessage == null ? "Normal" : deathMessage); + + final Map eloMap = CorePlugin.GSON.fromJson(document.getString("elo"), PracticeConstants.STRING_INTEGER_MAP_TYPE); + final Map winMap = CorePlugin.GSON.fromJson(document.getString("wins"), PracticeConstants.STRING_INTEGER_MAP_TYPE); + final Map lossMap = CorePlugin.GSON.fromJson(document.getString("losses"), PracticeConstants.STRING_INTEGER_MAP_TYPE); + final Map highestStreakMap = CorePlugin.GSON.fromJson(document.getString("highestKs"), PracticeConstants.STRING_INTEGER_MAP_TYPE); + final Map killstreakMap = CorePlugin.GSON.fromJson(document.getString("currentKs"), PracticeConstants.STRING_INTEGER_MAP_TYPE); + + final Map divisions = CorePlugin.GSON.fromJson(document.getString("divisionClaims"), PracticeConstants.STRING_BOOLEAN_MAP_TYPE); + + if (eloMap != null && winMap != null && lossMap != null && divisions != null) { + eloMap.forEach(playerData::setElo); + winMap.forEach(playerData::setWins); + lossMap.forEach(playerData::setLosses); + highestStreakMap.forEach(playerData::setHighestKillStreak); + killstreakMap.forEach(playerData::setKillstreak); + divisions.forEach(playerData.getRankedDivisionBooleanMap()::put); + } + + final List kitList = document.getList("playerKits", Document.class); + + if (kitList != null && !kitList.isEmpty()) { + kitList.forEach(kit -> { + final List specificKits = kit.getList("kits", Document.class); + + specificKits.forEach(specificKit -> { + final Integer index = specificKit.getInteger("key"); + final PlayerKit playerKit = new PlayerKit(specificKit.getString("name"), specificKit.getInteger("key"), ItemUtil.itemStackArrayFromBase64(specificKit.getString("contents")), specificKit.getString("displayName")); + + playerData.addPlayerKit(index, playerKit); + }); + }); + } + }); + } + + public void removePlayerData(UUID uuid) { + CompletableFuture.runAsync(() -> { + PlayerManager.this.saveData(PlayerManager.this.playerData.get(uuid)); + PlayerManager.this.playerData.remove(uuid); + }); + } + + public void saveData(PlayerData playerData) { + if (playerData == null) { + return; + } + + final Document document = new Document("_id", playerData.getUniqueId()); + + document.put("scoreboardEnabled", playerData.getOptions().isScoreboardEnabled()); + document.put("scoreboardStyle", playerData.getOptions().getScoreboardState().name()); + document.put("acceptingDuels", playerData.getOptions().isDuelRequests()); + document.put("allowingSpectators", playerData.getOptions().isSpectators()); + document.put("autoGg", playerData.getOptions().isAutoGg()); + + document.put("pingRange", playerData.getPingRange()); + document.put("eloRange", playerData.getEloRange()); + document.put("unrankedWins", playerData.getUnrankedWinCount()); + document.put("rankedWins", playerData.getRankedWinCount()); + document.put("unrankedLosses", playerData.getUnrankedLossCount()); + document.put("rankedLosses", playerData.getRankedLossCount()); + document.put("effect", playerData.getEffect().getName()); + document.put("rankedDivisionCurrent", playerData.getRankedDivision().name()); + document.put("deathMessage", playerData.getDeathMessageType() == null ? "Normal" : playerData.getDeathMessageType()); + + final Map eloMap = new HashMap<>(); + final Map winMap = new HashMap<>(); + final Map lossMap = new HashMap<>(); + final Map highestStreakMap = new HashMap<>(); + final Map killstreakMap = new HashMap<>(); + + for (Kit kit : this.plugin.getKitManager().getKits()) { + winMap.put(kit.getName(), playerData.getWins(kit.getName())); + lossMap.put(kit.getName(), playerData.getLosses(kit.getName())); + + if (kit.isRanked()) { + eloMap.put(kit.getName(), playerData.getElo(kit.getName())); + } + + highestStreakMap.put(kit.getName(), playerData.getHighestKillStreak(kit.getName())); + killstreakMap.put(kit.getName(), playerData.getKillstreak(kit)); + } + + document.put("elo", CorePlugin.GSON.toJson(eloMap)); + document.put("wins", CorePlugin.GSON.toJson(winMap)); + document.put("losses", CorePlugin.GSON.toJson(lossMap)); + document.put("divisionClaims", CorePlugin.GSON.toJson(playerData.getRankedDivisionBooleanMap())); + + document.put("highestKs", CorePlugin.GSON.toJson(highestStreakMap)); + document.put("currentKs", CorePlugin.GSON.toJson(killstreakMap)); + document.put("globalElo", playerData.recalculateGlobalElo()); + + final List playerKitList = new ArrayList<>(); + + this.plugin.getKitManager().getKits().forEach(kit -> { + final Map playerKits = playerData.getPlayerKits(kit.getName()); + + if (playerKits != null) { + final List documentList = new ArrayList<>(); + + playerKits.forEach((key, value) -> { + final Document specificKit = new Document("key", key); + + specificKit.put("name", value.getName()); + specificKit.put("displayName", value.getDisplayName()); + specificKit.put("contents", ItemUtil.itemStackArrayToBase64(value.getContents())); + + documentList.add(specificKit); + }); + + final Document document1 = new Document("kits", documentList); + + document1.put("kitName", kit.getName()); + + playerKitList.add(document1); + } + }); + + document.put("uuid", playerData.getUniqueId().toString()); + document.put("username", playerData.getName()); + + document.put("matches", playerData.getMatchIds()); + document.put("playerKits", playerKitList); + + MongoManager.getInstance().getPlayers().replaceOne(Filters.eq("_id", playerData.getUniqueId()), document, new ReplaceOptions().upsert(true)); + } + + public Collection getAllData() { + return this.playerData.values(); + } + + public PlayerData getPlayerData(UUID uuid) { + return this.playerData.get(uuid); + } + + public void giveLobbyItems(Player player) { + final Party party = this.plugin.getPartyManager().getParty(player.getUniqueId()); + final boolean inParty = party != null; + final boolean inTournament = this.plugin.getTournamentManager().getTournament(player.getUniqueId()) != null; + final boolean isRematching = this.plugin.getMatchManager().isRematching(player.getUniqueId()); + + final PotPlayer potPlayer = CorePlugin.getInstance().getPlayerManager().getPlayer(player); + final boolean isModMode = potPlayer != null && potPlayer.isStaffMode(); + + ItemStack[] items = this.plugin.getItemManager().getSpawnItems(); + + if (inTournament) { + items = this.plugin.getItemManager().getTournamentItems(); + } else if (inParty) { + if (party.getLeader().equals(player.getUniqueId())) { + items = this.plugin.getItemManager().getPartyItems(); + } else { + items = this.plugin.getItemManager().getPartyNonLeaderItems(); + } + } + + if (isModMode) { + CorePlugin.getInstance().getPlayerManager().applyItems(player); + } else { + player.getInventory().setContents(items); + } + + if (isRematching && !inParty && !inTournament) { + player.getInventory().setItem(4, PlayerManager.REMATCH_ITEM); + } + + player.updateInventory(); + } + + public void sendToSpawnAndUpdateTag(Player player) { + this.sendToSpawnAndReset(player); + + final PotPlayer potPlayer = CorePlugin.getInstance().getPlayerManager().getPlayer(player); + potPlayer.setupPlayerTag(); + } + + public void reset(Player player) { + final PlayerData playerData = this.getPlayerData(player.getUniqueId()); + if (player.isDead()) { + Bukkit.getScheduler().runTask(plugin, () -> player.spigot().respawn()); + } + + playerData.setPlayerState(PlayerState.SPAWN); + PlayerUtil.clearPlayer(player); + + this.plugin.getTimerManager().getTimer(EnderpearlTimer.class).clearCooldown(player.getUniqueId()); + + this.giveLobbyItems(player); + + if (player.hasPermission("practice.spawn.fly")) { + player.setAllowFlight(true); + player.setFlying(true); + } + + Schedulers.sync().runLater(() -> { + this.plugin.getServer().getOnlinePlayers().forEach(other -> { + Practice.getInstance().getEntityHider().hideEntity(player, other); + Practice.getInstance().getEntityHider().hideEntity(other, player); + }); + }, 1L); + } + + public void sendToSpawnAndReset(Player player) { + this.reset(player); + + this.plugin.getSpawnManager().fetchSingleLocation("spawn").ifPresent(asyncLocation -> { + asyncLocation.teleport(player); + }); + } +} diff --git a/src/main/java/com/solexgames/practice/managers/QueueManager.java b/src/main/java/com/solexgames/practice/managers/QueueManager.java new file mode 100644 index 0000000..fdd1cae --- /dev/null +++ b/src/main/java/com/solexgames/practice/managers/QueueManager.java @@ -0,0 +1,329 @@ +package com.solexgames.practice.managers; + +import com.solexgames.core.util.Color; +import com.solexgames.core.util.PlayerUtil; +import com.solexgames.practice.Practice; +import com.solexgames.practice.arena.Arena; +import com.solexgames.practice.kit.Kit; +import com.solexgames.practice.match.Match; +import com.solexgames.practice.match.team.impl.MatchTeam; +import com.solexgames.practice.party.Party; +import com.solexgames.practice.player.PlayerData; +import com.solexgames.practice.player.PlayerState; +import com.solexgames.practice.queue.QueueEntry; +import com.solexgames.practice.queue.QueueType; +import com.solexgames.practice.util.CC; +import lombok.Getter; +import lombok.Setter; +import org.bukkit.entity.Player; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; + +@Getter +@Setter +public class QueueManager { + + private final Map queued = new ConcurrentHashMap<>(); + private final Map playerQueueTime = new HashMap<>(); + + private final Practice plugin = Practice.getInstance(); + + private boolean rankedEnabled = true; + + public QueueManager() { + this.plugin.getServer().getScheduler().runTaskTimer(this.plugin, () -> this.queued.forEach((key, value) -> { + if (key == null || value == null) { + return; + } + + if (value.isParty()) { + this.findMatch(this.plugin.getPartyManager().getParty(key), value.getKitName(), + value.getElo(), value.getQueueType()); + } else { + this.findMatch(this.plugin.getServer().getPlayer(key), value.getKitName(), + value.getElo(), value.getQueueType()); + } + }), 20L, 20L); + } + + public void addPlayerToQueue(Player player, PlayerData playerData, String kitName, QueueType type) { + if (type.isRanked() && !this.rankedEnabled) { + player.sendMessage(CC.RED + "I'm sorry, but ranked is currently unavailable."); + player.closeInventory(); + return; + } + + playerData.setPlayerState(PlayerState.QUEUE); + + final int elo = type.isRanked() ? playerData.getElo(kitName) : 0; + final QueueEntry entry = new QueueEntry(type, kitName, elo, PlayerUtil.getPing(player), false); + + this.queued.put(playerData.getUniqueId(), entry); + + this.giveQueueItems(player); + + final String message = CC.YELLOW + "You've been added to the " + CC.AQUA + type.getName() + " " + kitName + CC.YELLOW + " queue." + (type.isRanked() ? CC.GRAY + " (" + elo + " elo)" : ""); + player.sendMessage(message); + + this.playerQueueTime.put(player.getUniqueId(), System.currentTimeMillis()); + } + + private void giveQueueItems(Player player) { + player.closeInventory(); + player.getInventory().setContents(this.plugin.getItemManager().getQueueItems()); + player.updateInventory(); + } + + public QueueEntry getQueueEntry(UUID uuid) { + return this.queued.getOrDefault(uuid, null); + } + + public long getPlayerQueueTime(UUID uuid) { + return this.playerQueueTime.getOrDefault(uuid, null); + } + + public int getQueueSize(String ladder, QueueType type) { + return (int) this.queued.entrySet().stream() + .filter(uuidQueueEntryEntry -> uuidQueueEntryEntry != null && uuidQueueEntryEntry.getValue() != null && uuidQueueEntryEntry.getValue().getQueueType() == type && uuidQueueEntryEntry.getValue().getKitName().equals(ladder)) + .count(); + } + + private boolean findMatch(Player player, String kitName, int elo, QueueType type) { + if (this.playerQueueTime.containsKey(player.getUniqueId()) && this.playerQueueTime.get(player.getUniqueId()) == null) { + return false; + } + + final long queueTime = System.currentTimeMillis() - this.playerQueueTime.get(player.getUniqueId()); + final PlayerData playerData = this.plugin.getPlayerManager().getPlayerData(player.getUniqueId()); + + if (playerData == null) { + return false; + } + + if (playerData.getPlayerState() != PlayerState.QUEUE) + return false; + + final QueueEntry queueEntry = this.queued.get(player.getUniqueId()); + + int eloRange = playerData.getEloRange(); + int pingRange = playerData.getPingRange(); + int seconds = Math.round((float) (queueTime / 1000L)); + + if (seconds > 5) { + if (seconds % 15 == 0) { + pingRange += 20; + + if (pingRange > 70) { + pingRange = 70; + } else { + int ping = queueEntry.getPing(); + player.sendMessage(Color.SECONDARY_COLOR + "Expanding your ping range by " + Color.MAIN_COLOR + "20ms" + Color.SECONDARY_COLOR + "..." + CC.GRAY + "[" + Math.max(0, ping - pingRange) + "ms - " + (ping + pingRange) + "ms]"); + } + + playerData.setPingRange(pingRange); + } + if (type.isRanked() && seconds % 10 == 0) { + eloRange += 10; + + if (eloRange >= 3000) + eloRange = 3000; + + playerData.setEloRange(eloRange); + } + } + + for (UUID opponent : this.queued.keySet()) { + String playerFoundMatchMessage, matchedFoundMatchMessage; + if (opponent.equals(player.getUniqueId())) + continue; + QueueEntry opponentQueueEntry = this.queued.get(opponent); + if (!opponentQueueEntry.getKitName().equals(kitName)) + continue; + if (opponentQueueEntry.getQueueType() != type) + continue; + if (opponentQueueEntry.isParty()) + continue; + Player opponentPlayer = this.plugin.getServer().getPlayer(opponent); + PlayerData opponentData = this.plugin.getPlayerManager().getPlayerData(opponent); + if (opponentData.getPlayerState() != PlayerState.QUEUE) + continue; + if (type.isRanked()) { + int eloDiff = Math.abs(opponentQueueEntry.getElo() - elo); + if (eloDiff > eloRange) + continue; + if (eloDiff > opponentData.getEloRange()) + continue; + int pingDiff = Math.abs(opponentQueueEntry.getPing() - queueEntry.getPing()); + + if (playerData.getPingRange() != -1 && pingDiff > playerData.getPingRange()) + continue; + } + + Kit kit = this.plugin.getKitManager().getKit(kitName); + Arena arena = this.plugin.getArenaManager().getRandomArena(kit); + + if (arena == null || kit == null) { + return false; + } + + if (type.isRanked()) { + playerFoundMatchMessage = Color.SECONDARY_COLOR + "Found " + type.getName().toLowerCase() + " match: " + CC.GREEN + player.getName() + CC.GRAY + " (" + elo + " elo)" + Color.SECONDARY_COLOR + " vs. " + CC.RED + opponentPlayer.getName() + CC.GRAY + " (" + this.queued.get(opponentPlayer.getUniqueId()).getElo() + " elo)"; + matchedFoundMatchMessage = Color.SECONDARY_COLOR + "Found " + type.getName().toLowerCase() + " match: " + CC.GREEN + opponentPlayer.getName() + CC.GRAY + " (" + this.queued.get(opponentPlayer.getUniqueId()).getElo() + " elo)" + Color.SECONDARY_COLOR + " vs. " + CC.RED + player.getName() + CC.GRAY + " (" + elo + " elo)"; + } else { + playerFoundMatchMessage = Color.SECONDARY_COLOR + "Found " + type.getName().toLowerCase() + " match: " + CC.GREEN + player.getName() + Color.SECONDARY_COLOR + " vs. " + CC.RED + opponentPlayer.getName(); + matchedFoundMatchMessage = Color.SECONDARY_COLOR + "Found " + type.getName().toLowerCase() + " match: " + CC.GREEN + opponentPlayer.getName() + Color.SECONDARY_COLOR + " vs. " + CC.RED + player.getName(); + } + + player.sendMessage(playerFoundMatchMessage); + opponentPlayer.sendMessage(matchedFoundMatchMessage); + + opponentPlayer.sendMessage(Color.SECONDARY_COLOR + "You're playing on the arena " + Color.MAIN_COLOR + arena.getName() + Color.SECONDARY_COLOR + "."); + player.sendMessage(Color.SECONDARY_COLOR + "You're playing on the arena " + Color.MAIN_COLOR + arena.getName() + Color.SECONDARY_COLOR + "."); + + final MatchTeam teamA = new MatchTeam(player.getUniqueId(), Collections.singletonList(player.getUniqueId()), 0); + final MatchTeam teamB = new MatchTeam(opponentPlayer.getUniqueId(), Collections.singletonList(opponentPlayer.getUniqueId()), 1); + final Match match = new Match(arena, kit, type, teamA, teamB); + + this.plugin.getMatchManager().createMatch(match, false); + + this.queued.remove(player.getUniqueId()); + this.queued.remove(opponentPlayer.getUniqueId()); + + this.playerQueueTime.remove(player.getUniqueId()); + + return true; + } + + return false; + } + + public void removePlayerFromQueue(Player player) { + final QueueEntry entry = this.queued.get(player.getUniqueId()); + + if (entry == null) { + return; + } + + this.queued.remove(player.getUniqueId()); + this.plugin.getPlayerManager().reset(player); + + player.sendMessage(CC.RED + "You've left the " + entry.getQueueType().getName() + " " + entry.getKitName() + " queue."); + } + + public void addPartyToQueue(Player leader, Party party, String kitName, QueueType type) { + if (type.isRanked() && !this.rankedEnabled) { + leader.closeInventory(); + } else if (party.getMembers().size() != 2) { + leader.sendMessage(CC.RED + "There must be at least 2 players in your party to do this."); + leader.closeInventory(); + } else { + party.getMembers().stream() + .map(this.plugin.getPlayerManager()::getPlayerData) + .forEach(member -> member.setPlayerState(PlayerState.QUEUE)); + + final PlayerData playerData = this.plugin.getPlayerManager().getPlayerData(leader.getUniqueId()); + final int elo = type.isRanked() ? playerData.getElo(kitName) : -1; + + this.queued.put(playerData.getUniqueId(), new QueueEntry(type, kitName, elo, PlayerUtil.getPing(leader), true)); + + this.giveQueueItems(leader); + + final String unrankedMessage = Color.SECONDARY_COLOR + "Your party has been added to the " + Color.MAIN_COLOR + "Unranked 2v2 " + kitName + Color.SECONDARY_COLOR + " queue."; + final String rankedMessage = Color.SECONDARY_COLOR + "Your party has been added to the " + Color.MAIN_COLOR + "Ranked 2v2 " + kitName + Color.SECONDARY_COLOR + " queue with " + CC.GREEN + elo + " elo" + Color.SECONDARY_COLOR + "."; + + party.broadcast(type.isRanked() ? rankedMessage : unrankedMessage); + + this.playerQueueTime.put(party.getLeader(), System.currentTimeMillis()); + + this.findMatch(party, kitName, elo, type); + } + } + + private void findMatch(Party partyA, String kitName, int elo, QueueType type) { + if (!this.playerQueueTime.containsKey(partyA.getLeader())) { + return; + } + + final long queueTime = System.currentTimeMillis() - this.playerQueueTime.get(partyA.getLeader()); + final PlayerData playerData = this.plugin.getPlayerManager().getPlayerData(partyA.getLeader()); + + if (playerData == null) { + return; + } + + int eloRange = playerData.getEloRange(); + final int seconds = Math.round(queueTime / 1000L); + + if (seconds > 5 && type.isRanked()) { + eloRange += seconds * 50; + + if (eloRange >= 1000) { + eloRange = 1000; + } + + partyA.broadcast(Color.SECONDARY_COLOR + "Searching in ELO range " + Color.MAIN_COLOR + "[" + (elo - eloRange / 2) + " -> " + (elo + eloRange / 2) + "]"); + } + + final int finalEloRange = eloRange; + final UUID opponent = this.queued.entrySet().stream() + .filter(entry -> entry.getKey() != partyA.getLeader() && this.plugin.getPlayerManager().getPlayerData(entry.getKey()).getPlayerState() == PlayerState.QUEUE) + .filter(entry -> entry.getValue().isParty() && entry.getValue().getQueueType() == type) + .filter(entry -> !type.isRanked() || Math.abs(entry.getValue().getElo() - elo) < finalEloRange && entry.getValue().getKitName().equals(kitName)) + .map(Map.Entry::getKey).findFirst().orElse(null); + + if (opponent == null) { + return; + } + + final PlayerData opponentData = this.plugin.getPlayerManager().getPlayerData(opponent); + + if (opponentData.getPlayerState() == PlayerState.FIGHTING || playerData.getPlayerState() == PlayerState.FIGHTING) { + return; + } + + final Player leaderA = this.plugin.getServer().getPlayer(partyA.getLeader()); + final Player leaderB = this.plugin.getServer().getPlayer(opponent); + + final Party partyB = this.plugin.getPartyManager().getParty(opponent); + + final Kit kit = this.plugin.getKitManager().getKit(kitName); + final Arena arena = this.plugin.getArenaManager().getRandomArena(kit); + + String partyAFoundMatchMessage; + String partyBFoundMatchMessage; + + if (type.isRanked()) { + partyAFoundMatchMessage = Color.MAIN_COLOR + leaderB.getName() + Color.SECONDARY_COLOR + "'s Party with " + Color.MAIN_COLOR + this.queued.get(leaderB.getUniqueId()).getElo() + " elo" + Color.SECONDARY_COLOR + "."; + partyBFoundMatchMessage = Color.MAIN_COLOR + leaderA.getName() + Color.SECONDARY_COLOR + "'s Party with " + Color.MAIN_COLOR + this.queued.get(leaderA.getUniqueId()).getElo() + " elo" + Color.SECONDARY_COLOR + "."; + } else { + partyAFoundMatchMessage = Color.MAIN_COLOR + leaderB.getName() + Color.SECONDARY_COLOR + "'s Party."; + partyBFoundMatchMessage = Color.MAIN_COLOR + leaderA.getName() + Color.SECONDARY_COLOR + "'s Party."; + } + + partyA.broadcast(Color.SECONDARY_COLOR + "Starting a " + partyA.getMembers().size() + "v" + partyB.getMembers().size() + " match against " + partyAFoundMatchMessage); + partyB.broadcast(Color.SECONDARY_COLOR + "Starting a " + partyB.getMembers().size() + "v" + partyA.getMembers().size() + " match against " + partyBFoundMatchMessage); + + final List playersA = new ArrayList<>(partyA.getMembers()); + final List playersB = new ArrayList<>(partyB.getMembers()); + + final MatchTeam teamA = new MatchTeam(leaderA.getUniqueId(), playersA, 0); + final MatchTeam teamB = new MatchTeam(leaderB.getUniqueId(), playersB, 1); + + final Match match = new Match(arena, kit, type, teamA, teamB); + + this.plugin.getMatchManager().createMatch(match, false); + + this.queued.remove(partyA.getLeader()); + this.queued.remove(partyB.getLeader()); + } + + public void removePartyFromQueue(Party party) { + final QueueEntry entry = this.queued.get(party.getLeader()); + + this.queued.remove(party.getLeader()); + + party.streamPartyMembers().forEach(this.plugin.getPlayerManager()::reset); + party.broadcast(Color.SECONDARY_COLOR + "Your party has left the " + Color.MAIN_COLOR + entry.getQueueType().getName() + " " + entry.getKitName() + Color.SECONDARY_COLOR + " queue."); + } +} diff --git a/src/main/java/com/solexgames/practice/managers/SnapshotManager.java b/src/main/java/com/solexgames/practice/managers/SnapshotManager.java new file mode 100644 index 0000000..9b2d297 --- /dev/null +++ b/src/main/java/com/solexgames/practice/managers/SnapshotManager.java @@ -0,0 +1,29 @@ +package com.solexgames.practice.managers; + +import com.solexgames.practice.match.snapshot.InventorySnapshot; +import com.solexgames.practice.util.TtlHashMap; +import lombok.Getter; + +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.TimeUnit; + +@Getter +public class SnapshotManager { + + private final Map snapshots = new TtlHashMap<>(TimeUnit.SECONDS, 30); + + public InventorySnapshot getByPlayer(UUID uuid) { + return this.snapshots.values().stream() + .filter(inventorySnapshot -> inventorySnapshot.getBoundPlayer().equals(uuid)) + .findFirst().orElse(null); + } + + public void addSnapshot(InventorySnapshot snapshot) { + this.snapshots.put(snapshot.getSnapshotId(), snapshot); + } + + public InventorySnapshot getSnapshot(UUID snapshotId) { + return this.snapshots.get(snapshotId); + } +} diff --git a/src/main/java/com/solexgames/practice/managers/SpawnManager.java b/src/main/java/com/solexgames/practice/managers/SpawnManager.java new file mode 100644 index 0000000..0497ac7 --- /dev/null +++ b/src/main/java/com/solexgames/practice/managers/SpawnManager.java @@ -0,0 +1,62 @@ +package com.solexgames.practice.managers; + +import com.solexgames.practice.Practice; +import com.solexgames.practice.location.impl.impl.AsyncLocation; +import lombok.Getter; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.file.FileConfiguration; + +import java.util.*; +import java.util.concurrent.atomic.AtomicInteger; + +public class SpawnManager { + + @Getter + private final Map> locationMap = new HashMap<>(); + + public SpawnManager() { + this.loadAsyncLocationsFromConfig(); + } + + public Optional fetchSingleLocation(String id) { + return Optional.ofNullable(this.locationMap.getOrDefault(id, null) == null || this.locationMap.getOrDefault(id, null).size() == 0 ? null : this.locationMap.getOrDefault(id, null).get(0)); + } + + public Optional> fetchMultipleLocations(String id) { + return Optional.of(this.locationMap.getOrDefault(id, new ArrayList<>())); + } + + private void loadAsyncLocationsFromConfig() { + final FileConfiguration config = Practice.getInstance().getConfig(); + + config.getKeys(false).forEach(sectionId -> { + final ConfigurationSection section = config.getConfigurationSection(sectionId); + final List asyncLocations = new ArrayList<>(); + + final Set keys = section.getKeys(false); + + if (keys != null) { + keys.forEach(number -> { + final AsyncLocation asyncLocation = AsyncLocation.of(section.getString(number)); + asyncLocations.add(asyncLocation); + }); + } + + this.locationMap.put(sectionId, asyncLocations); + }); + } + + public void saveAsyncLocationsToConfig() { + final FileConfiguration config = Practice.getInstance().getConfig(); + + this.locationMap.forEach((id, asyncLocations) -> { + final AtomicInteger atomicInteger = new AtomicInteger(); + + asyncLocations.forEach(asyncLocation -> { + config.set(id + "." + atomicInteger.get(), asyncLocation.toJson()); + }); + }); + + Practice.getInstance().saveConfig(); + } +} diff --git a/src/main/java/com/solexgames/practice/managers/TournamentManager.java b/src/main/java/com/solexgames/practice/managers/TournamentManager.java new file mode 100644 index 0000000..3a19409 --- /dev/null +++ b/src/main/java/com/solexgames/practice/managers/TournamentManager.java @@ -0,0 +1,298 @@ +package com.solexgames.practice.managers; + +import com.solexgames.core.util.Color; +import com.solexgames.practice.Practice; +import com.solexgames.practice.match.Match; +import com.solexgames.practice.match.team.impl.MatchTeam; +import com.solexgames.practice.party.Party; +import com.solexgames.practice.player.PlayerData; +import com.solexgames.practice.runnable.TournamentBroadcastRunnable; +import com.solexgames.practice.runnable.TournamentRunnable; +import com.solexgames.practice.tournament.Tournament; +import com.solexgames.practice.tournament.TournamentState; +import com.solexgames.practice.tournament.TournamentTeam; +import com.solexgames.practice.util.CC; +import com.solexgames.practice.util.EXPUtil; +import com.solexgames.practice.util.TeamUtil; +import lombok.Getter; +import org.bukkit.Bukkit; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitRunnable; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.UUID; + +@Getter +public class TournamentManager { + + private final Practice plugin = Practice.getInstance(); + + private final Map players = new HashMap<>(); + private final Map matches = new HashMap<>(); + private final Map tournaments = new HashMap<>(); + private final Map taskIds = new HashMap<>(); + + public boolean isInTournament(UUID uuid) { + return this.players.containsKey(uuid); + } + + public Tournament getTournament(UUID uuid) { + final Integer id = this.players.get(uuid); + + if (id == null) { + return null; + } + + return this.tournaments.get(id); + } + + public Tournament getTournamentFromMatch(UUID uuid) { + final Integer id = this.matches.get(uuid); + + if (id == null) { + return null; + } + + return this.tournaments.get(id); + } + + public void createNewTournament(CommandSender sender, int id, int teamSize, int size, String kitName) { + final Tournament tournament = new Tournament(id, teamSize, size, kitName); + + this.tournaments.put(id, tournament); + + final BukkitRunnable bukkitRunnable = new TournamentRunnable(tournament); + bukkitRunnable.runTaskTimerAsynchronously(this.plugin, 0L, 20L); + + final BukkitRunnable broadcastRunnable = new TournamentBroadcastRunnable(tournament); + broadcastRunnable.runTaskTimerAsynchronously(this.plugin, 0L, 200L); + + this.taskIds.put(tournament, bukkitRunnable); + + sender.sendMessage(new String[]{ + Color.SECONDARY_COLOR + "You've created a new tournament with the id: " + CC.GREEN + id, + Color.SECONDARY_COLOR + "The team size is currently: " + Color.MAIN_COLOR + teamSize, + Color.SECONDARY_COLOR + "The maximum capacity is currently: " + Color.MAIN_COLOR + size, + Color.SECONDARY_COLOR + "The tournament will be running with the kit: " + Color.MAIN_COLOR + kitName, + "", + CC.GREEN + "The tournament will start broadcasting in 5 seconds..." + }); + } + + private void removeFromTournament(Tournament tournament, Player player) { + final TournamentTeam team = tournament.getPlayerTeam(player.getUniqueId()); + + tournament.removePlayer(player.getUniqueId()); + + player.sendMessage(CC.GREEN + "You've left the tournament."); + + this.players.remove(player.getUniqueId()); + this.plugin.getPlayerManager().sendToSpawnAndReset(player); + + tournament.broadcast(Color.MAIN_COLOR + player.getName() + Color.SECONDARY_COLOR + " left the tournament." + CC.GREEN + " (" + tournament.getPlayers().size() + "/" + tournament.getSize() + ")"); + + if (team != null) { + team.killPlayer(player.getUniqueId()); + + if (team.getAlivePlayers().size() == 0) { + tournament.killTeam(team); + + if (tournament.getAliveTeams().size() == 1) { + final TournamentTeam tournamentTeam = tournament.getAliveTeams().get(0); + + this.notifyTeamOfRemoval(tournament, tournamentTeam); + } + } else { + if (team.getLeader().equals(player.getUniqueId())) { + team.setLeader(team.getAlivePlayers().get(0)); + } + } + } + } + + private void notifyTeamOfRemoval(Tournament tournament, TournamentTeam tournamentTeam) { + final String names = TeamUtil.getNames(tournamentTeam); + + Bukkit.broadcastMessage(Color.SECONDARY_COLOR + "The tournament has ended and the players " + CC.AQUA + names + Color.SECONDARY_COLOR + " has won."); + + for (UUID playerUUID : tournamentTeam.getAlivePlayers()) { + this.players.remove(playerUUID); + final Player tournamentPlayer = this.plugin.getServer().getPlayer(playerUUID); + + this.plugin.getPlayerManager().sendToSpawnAndReset(tournamentPlayer); + + EXPUtil.addExperienceToPlayer(Bukkit.getPlayer(playerUUID), 25, "Playing a Tournament"); + } + + this.plugin.getTournamentManager().forceEndTournament(tournament.getId(), false); + } + + private void eliminateTeamFromTournament(Tournament tournament, TournamentTeam winnerTeam, TournamentTeam losingTeam) { + for (UUID playerUUID : losingTeam.getAlivePlayers()) { + final Player player = this.plugin.getServer().getPlayer(playerUUID); + + tournament.removePlayer(player.getUniqueId()); + this.players.remove(player.getUniqueId()); + + player.sendMessage(CC.RED + "You've been eliminated from the tournament."); + } + + final String word = losingTeam.getAlivePlayers().size() > 1 ? "have" : "has"; + final boolean isParty = tournament.getTeamSize() > 1; + + final String announce = CC.RED + (isParty ? losingTeam.getLeaderName() + "'s Party" : losingTeam.getLeaderName()) + CC.GRAY + " " + word + " been eliminated by " + CC.GREEN + (isParty ? winnerTeam.getLeaderName() + "'s Party" : winnerTeam.getLeaderName()) + CC.GRAY + "."; + final String alive = CC.YELLOW + "There are now " + CC.AQUA + tournament.getPlayers().size() + "/" + tournament.getSize() + CC.YELLOW + " players remaining in the tournament."; + + tournament.broadcast(announce); + tournament.broadcast(alive); + } + + public void leaveTournament(Player player) { + final Tournament tournament = this.getTournament(player.getUniqueId()); + + if (tournament == null) { + return; + } + + final Party party = this.plugin.getPartyManager().getParty(player.getUniqueId()); + + if (party != null && tournament.getTournamentState() != TournamentState.FIGHTING) { + if (this.plugin.getPartyManager().isLeader(player.getUniqueId())) { + for (UUID memberUUID : party.getMembers()) { + final Player member = this.plugin.getServer().getPlayer(memberUUID); + + this.removeFromTournament(tournament, member); + } + } else { + player.sendMessage(CC.RED + "You're not the leader of the party."); + } + } else { + this.removeFromTournament(tournament, player); + } + } + + private void addToTournament(Tournament tournament, Player player) { + tournament.addPlayer(player.getUniqueId()); + + this.players.put(player.getUniqueId(), tournament.getId()); + this.plugin.getPlayerManager().sendToSpawnAndReset(player); + + tournament.broadcast(Color.MAIN_COLOR + player.getName() + Color.SECONDARY_COLOR + " joined the tournament." + CC.GREEN + " (" + tournament.getPlayers().size() + "/" + tournament.getSize() + ")"); + } + + public void joinTournament(Integer id, Player player) { + final Tournament tournament = this.tournaments.get(id); + + if (tournament == null) { + return; + } + + final Party party = this.plugin.getPartyManager().getParty(player.getUniqueId()); + + if (party != null) { + if (this.plugin.getPartyManager().isLeader(player.getUniqueId())) { + if ((party.getMembers().size() + tournament.getPlayers().size()) <= tournament.getSize()) { + if (party.getMembers().size() != tournament.getTeamSize() || party.getMembers().size() == 1) { + player.sendMessage(CC.RED + "Error: Your party must have " + CC.YELLOW + tournament.getTeamSize() + CC.RED + " players max."); + } else { + for (UUID memberUUID : party.getMembers()) { + final Player member = this.plugin.getServer().getPlayer(memberUUID); + + this.addToTournament(tournament, member); + } + } + } else { + player.sendMessage(CC.RED + "Error: The tournament's currently full."); + } + } else { + player.sendMessage(CC.RED + "Error: You're not the leader of your party."); + } + } else { + this.addToTournament(tournament, player); + } + + if (tournament.getPlayers().size() == tournament.getSize()) { + tournament.setTournamentState(TournamentState.STARTING); + } + } + + public Tournament getTournament(Integer id) { + return this.tournaments.getOrDefault(id, null); + } + + public void forceEndTournament(Integer id, boolean force) { + final Tournament tournament = this.tournaments.get(id); + + if (tournament == null) { + return; + } + + if (force) { + final Iterator players = this.players.keySet().iterator(); + + while (players.hasNext()) { + final Player player = Bukkit.getPlayer(players.next()); + + if (player != null) { + player.sendMessage(CC.RED + "The tournament you were previously in has been ended."); + + this.plugin.getServer().getScheduler().runTaskLater(this.plugin, () -> { + final PlayerData playerData = this.plugin.getPlayerManager().getPlayerData(player.getUniqueId()); + + if (playerData != null && tournament.getTournamentState().equals(TournamentState.FIGHTING)) { + this.plugin.getMatchManager().removeFighter(player, playerData, false); + } + + this.plugin.getPlayerManager().sendToSpawnAndReset(player); + }, 2L); + } + + players.remove(); + } + } + + if (this.taskIds.containsKey(tournament)) { + this.taskIds.get(tournament).cancel(); + } + + this.tournaments.remove(id); + } + + public void addMatchToTournament(UUID matchId, Integer tournamentId) { + this.matches.put(matchId, tournamentId); + } + + public void removeMatchFromTournament(Match match) { + final Tournament tournament = this.getTournamentFromMatch(match.getMatchId()); + + if (tournament == null) { + return; + } + + tournament.removeMatch(match.getMatchId()); + this.matches.remove(match.getMatchId()); + + final MatchTeam losingTeam = match.getWinningTeamId() == 0 ? match.getTeams().get(1) : match.getTeams().get(0); + final TournamentTeam losingTournamentTeam = tournament.getPlayerTeam(losingTeam.getPlayers().get(0)); + + tournament.killTeam(losingTournamentTeam); + + final MatchTeam winningTeam = match.getTeams().get(match.getWinningTeamId()); + final TournamentTeam winningTournamentTeam = tournament.getPlayerTeam(winningTeam.getAlivePlayers().get(0)); + + this.eliminateTeamFromTournament(tournament, winningTournamentTeam, losingTournamentTeam); + + if (tournament.getMatches().size() == 0) { + if (tournament.getAliveTeams().size() > 1) { + tournament.setTournamentState(TournamentState.STARTING); + tournament.setCurrentRound(tournament.getCurrentRound() + 1); + tournament.setCountdown(16); + } else { + this.notifyTeamOfRemoval(tournament, winningTournamentTeam); + } + } + } +} diff --git a/src/main/java/com/solexgames/practice/match/Match.java b/src/main/java/com/solexgames/practice/match/Match.java new file mode 100644 index 0000000..981b2d9 --- /dev/null +++ b/src/main/java/com/solexgames/practice/match/Match.java @@ -0,0 +1,228 @@ +package com.solexgames.practice.match; + +import com.google.common.collect.Sets; +import com.solexgames.core.util.SaltUtil; +import com.solexgames.core.util.clickable.Clickable; +import com.solexgames.practice.Practice; +import com.solexgames.practice.arena.Arena; +import com.solexgames.practice.arena.type.StandaloneArena; +import com.solexgames.practice.kit.Kit; +import com.solexgames.practice.match.snapshot.InventorySnapshot; +import com.solexgames.practice.match.team.impl.MatchTeam; +import com.solexgames.practice.queue.QueueType; +import io.netty.util.internal.ConcurrentSet; +import lombok.Getter; +import lombok.Setter; +import net.md_5.bungee.api.chat.BaseComponent; +import org.apache.commons.lang.time.DurationFormatUtils; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Sound; +import org.bukkit.block.BlockState; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Stream; + +@Getter +@Setter +public class Match { + + private final Practice plugin = Practice.getInstance(); + + private final Map snapshots = new HashMap<>(); + private final Map lives = new HashMap<>(); + + private final UUID matchId = UUID.randomUUID(); + private final String shortMatchId = SaltUtil.getRandomSaltedString(7); + + private final Set entitiesToRemove = new HashSet<>(); + + private final Set originalBlockChanges = Sets.newConcurrentHashSet(); + private final Set placedBlockLocations = Sets.newConcurrentHashSet(); + + private final Set spectators = new ConcurrentSet<>(); + private final Set haveSpectated = new HashSet<>(); + + private final Set taskIds = new HashSet<>(); + + private final List teams; + + private final QueueType type; + private final Arena arena; + private final Kit kit; + + private final boolean redRover; + private final long start; + private final int rounds; + + private boolean shouldGiveXp = true; + + @Getter + private StandaloneArena standaloneArena; + + private MatchState matchState = MatchState.STARTING; + + private int winningTeamId; + private int countdown = 6; + + private Map betPool = new HashMap<>(); + + public Match(Arena arena, Kit kit, QueueType type, MatchTeam... teams) { + this(arena, kit, type, false, 1, teams); + } + + public Match(Arena arena, Kit kit, QueueType type, int rounds, MatchTeam... teams) { + this(arena, kit, type, false, rounds, teams); + } + + public Match(Arena arena, Kit kit, QueueType type, boolean redRover, int rounds, MatchTeam... teams) { + this.arena = arena; + this.kit = kit; + this.type = type; + this.redRover = redRover; + this.rounds = rounds; + this.start = System.currentTimeMillis(); + this.teams = Arrays.asList(teams); + } + + public void addSpectator(UUID uuid) { + this.spectators.add(uuid); + } + + public void removeSpectator(UUID uuid) { + this.spectators.remove(uuid); + } + + public void addHaveSpectated(UUID uuid) { + this.haveSpectated.add(uuid); + } + + public boolean haveSpectated(UUID uuid) { + return this.haveSpectated.contains(uuid); + } + + public void addSnapshot(Player player) { + this.snapshots.put(player.getUniqueId(), new InventorySnapshot(player, this)); + } + + public boolean hasSnapshot(UUID uuid) { + return this.snapshots.containsKey(uuid); + } + + public InventorySnapshot getSnapshot(UUID uuid) { + return this.snapshots.get(uuid); + } + + public void addEntityToRemove(Entity entity) { + this.entitiesToRemove.add(entity); + } + + public void removeEntityToRemove(Entity entity) { + this.entitiesToRemove.remove(entity); + } + + public void clearEntitiesToRemove() { + this.entitiesToRemove.clear(); + } + + public void addRunnable(int id) { + this.taskIds.add(id); + } + + public void addOriginalBlockChange(BlockState blockState) { + this.originalBlockChanges.add(blockState); + } + + public void addPlacedBlockLocation(Location location) { + this.placedBlockLocations.add(location); + } + + public void broadcast(String message) { + this.teams.forEach(team -> team.streamAlivePlayers().forEach(player -> player.sendMessage(message))); + this.streamSpectators().forEach(spectator -> spectator.sendMessage(message)); + } + + public void broadcast(String... message) { + this.teams.forEach(team -> team.streamAlivePlayers().forEach(player -> player.sendMessage(message))); + this.streamSpectators().forEach(spectator -> spectator.sendMessage(message)); + } + + public void broadcast(Clickable message) { + this.teams.forEach(team -> team.streamAlivePlayers().forEach(player -> player.spigot().sendMessage(message.asComponents()))); + this.streamSpectators().forEach(player -> player.spigot().sendMessage(message.asComponents())); + } + + public void broadcast(BaseComponent[] message) { + this.teams.forEach(team -> team.streamAlivePlayers().forEach(player -> player.spigot().sendMessage(message))); + this.streamSpectators().forEach(player -> player.spigot().sendMessage(message)); + } + + public void broadcast(String message, Sound sound) { + this.teams.forEach(team -> team.getPlayers().forEach(uuid -> { + final Player player = Bukkit.getPlayer(uuid); + + player.sendMessage(message); + player.playSound(player.getLocation(), sound, 10.0F, 1.0F); + })); + this.spectators.forEach(spectator -> { + final Player player = Bukkit.getPlayer(spectator); + + player.sendMessage(message); + player.playSound(player.getLocation(), sound, 10.0F, 1.0F); + }); + } + + public MatchTeam getTeamById(int id) { + return this.teams.stream().filter(team -> team.getTeamID() == id).findFirst().orElse(null); + } + + public MatchTeam getTeamByPlayer(UUID uuid) { + return this.teams.stream().filter(team -> team.getPlayers().contains(uuid)).findFirst().orElse(null); + } + + public Stream streamSpectators() { + return this.spectators.stream() + .map(this.plugin.getServer()::getPlayer) + .filter(Objects::nonNull); + } + + public int decrementCountdown() { + return --this.countdown; + } + + public boolean isParty() { + return this.isFFA() || this.teams.get(0).getPlayers().size() != 1 && this.teams.get(1).getPlayers().size() != 1; + } + + public boolean isPartyMatch() { + return this.isFFA() || (this.teams.get(0).getPlayers().size() >= 2 || this.teams.get(1).getPlayers().size() >= 2); + } + + public boolean isFFA() { + return this.teams.size() == 1; + } + + public String getDuration() { + final long duration = System.currentTimeMillis() - this.start; + return DurationFormatUtils.formatDuration(duration, "mm:ss"); + } + + // Surely there is a better way to do this??? + public int getTotalBets() { + return this.betPool.values() + .stream() + .mapToInt(value -> value) + .sum(); + } + + public void addBet(UUID uuid, int amount) { + + } + + public void removeBet(UUID uuid) { + + } +} diff --git a/src/main/java/com/solexgames/practice/match/MatchRequest.java b/src/main/java/com/solexgames/practice/match/MatchRequest.java new file mode 100644 index 0000000..08884a4 --- /dev/null +++ b/src/main/java/com/solexgames/practice/match/MatchRequest.java @@ -0,0 +1,21 @@ +package com.solexgames.practice.match; + +import com.solexgames.practice.arena.Arena; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +import java.util.UUID; + +@Getter +@RequiredArgsConstructor +public class MatchRequest { + + private final UUID requester; + private final UUID requested; + + private final Arena arena; + private final String kitName; + + private final boolean party; + +} diff --git a/src/main/java/com/solexgames/practice/match/MatchState.java b/src/main/java/com/solexgames/practice/match/MatchState.java new file mode 100644 index 0000000..d0c43d5 --- /dev/null +++ b/src/main/java/com/solexgames/practice/match/MatchState.java @@ -0,0 +1,10 @@ +package com.solexgames.practice.match; + +public enum MatchState { + + STARTING, + FIGHTING, + SWITCHING, + ENDING + +} diff --git a/src/main/java/com/solexgames/practice/match/snapshot/InventorySnapshot.java b/src/main/java/com/solexgames/practice/match/snapshot/InventorySnapshot.java new file mode 100644 index 0000000..5c80dbb --- /dev/null +++ b/src/main/java/com/solexgames/practice/match/snapshot/InventorySnapshot.java @@ -0,0 +1,208 @@ +package com.solexgames.practice.match.snapshot; + +import com.solexgames.core.util.Color; +import com.solexgames.core.util.PlayerUtil; +import com.solexgames.core.util.builder.ItemBuilder; +import com.solexgames.core.util.external.Button; +import com.solexgames.core.util.external.Menu; +import com.solexgames.practice.Practice; +import com.solexgames.practice.match.Match; +import com.solexgames.practice.player.PlayerData; +import com.solexgames.practice.util.CC; +import com.solexgames.practice.util.MathUtil; +import com.solexgames.practice.util.StringUtil; +import lombok.Getter; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.potion.PotionEffect; + +import java.util.*; + +@Getter +public class InventorySnapshot extends Menu { + + private static final List NO_EFFECT_LIST = Collections.singletonList( + CC.RED + "No potion effects found!" + ); + + private static final ItemStack AIR = new ItemStack(Material.AIR); + + private final UUID boundPlayer; + + private final ItemStack[] originalInventory; + private final ItemStack[] originalArmor; + + private final UUID snapshotId = UUID.randomUUID(); + + private final double health; + private final double food; + + private final int longestCombo; + private final int totalHits; + + private final Player target; + private final Map buttonMap = new HashMap<>(); + + private int potCount = 0; + private int soupCount = 0; + + private boolean potionMatch; + private boolean soupMatch; + + public InventorySnapshot(Player player, Match match) { + final ItemStack[] contents = player.getInventory().getContents(); + final ItemStack[] armor = player.getInventory().getArmorContents(); + + this.boundPlayer = player.getUniqueId(); + + this.originalInventory = contents; + this.originalArmor = armor; + + this.target = player; + + final PlayerData playerData = Practice.getInstance().getPlayerManager().getPlayerData(player.getUniqueId()); + + this.health = player.getHealth(); + this.food = player.getFoodLevel(); + + final List potionEffectStrings = new ArrayList<>(); + + for (PotionEffect potionEffect : player.getActivePotionEffects()) { + final String romanNumeral = MathUtil.convertToRomanNumeral(potionEffect.getAmplifier() + 1); + final String effectName = StringUtil.toNiceString(potionEffect.getType().getName().toLowerCase()); + final String duration = MathUtil.convertTicksToMinutes(potionEffect.getDuration()); + + potionEffectStrings.add(CC.GRAY + " - " + CC.WHITE + effectName + " " + romanNumeral + CC.GRAY + " (" + duration + ")"); + } + + for (int i = 0; i < 9; i++) { + this.buttonMap.put(i + 27, new ItemBuilder(contents[i] == null ? InventorySnapshot.AIR : contents[i]).toButton()); + this.buttonMap.put(i + 18, new ItemBuilder(contents[i + 27] == null ? InventorySnapshot.AIR : contents[i + 27]).toButton()); + this.buttonMap.put(i + 9, new ItemBuilder(contents[i + 18] == null ? InventorySnapshot.AIR : contents[i + 18]).toButton()); + this.buttonMap.put(i, new ItemBuilder(contents[i + 9] == null ? InventorySnapshot.AIR : contents[i + 9]).toButton()); + } + + potionMatch = false; + soupMatch = false; + + for (final ItemStack item : match.getKit().getContents()) { + if (item == null) { + continue; + } + + if (item.getType() == Material.MUSHROOM_SOUP) { + soupMatch = true; + break; + } else if (item.getType() == Material.POTION && item.getDurability() == (short) 16421) { + potionMatch = true; + break; + } + } + + if (potionMatch) { + this.potCount = (int) Arrays.stream(contents).filter(Objects::nonNull).map(ItemStack::getDurability).filter(d -> d == 16421).count(); + + this.buttonMap.put(48, new ItemBuilder(Material.POTION) + .setDisplayName(CC.AQUA + "Potion Statistics") + .setDurability(16421) + .setAmount(this.potCount) + .addLore( + CC.GRAY + "Health Pots: " + CC.WHITE + this.potCount + " potion" + (this.potCount > 1 ? "s" : ""), + CC.GRAY + "Missed Pots: " + CC.WHITE + playerData.getMissedPots() + " potion" + (playerData.getMissedPots() > 1 ? "s" : "") + ) + .toButton() + ); + } else if (soupMatch) { + this.soupCount = (int) Arrays.stream(contents).filter(Objects::nonNull).map(ItemStack::getType).filter(d -> d == Material.MUSHROOM_SOUP).count(); + + this.buttonMap.put(48, new ItemBuilder(Material.MUSHROOM_SOUP) + .setDisplayName(CC.GRAY + "Soups Left: " + CC.WHITE + this.soupCount) + .setAmount(this.soupCount) + .toButton() + ); + } + + final double roundedHealth = Math.round(health / 2.0 * 2.0) / 2.0; + + this.buttonMap.put(45, new ItemBuilder(Material.SPECKLED_MELON) + .setDisplayName(CC.GRAY + "Health: " + CC.AQUA + roundedHealth) + .setAmount((int) roundedHealth) + .toButton() + ); + + final double roundedFood = Math.round(health / 2.0 * 2.0) / 2.0; + + this.buttonMap.put(46, new ItemBuilder(Material.COOKED_BEEF) + .setDisplayName(CC.GRAY + "Hunger: " + CC.AQUA + roundedFood) + .setAmount((int) roundedFood) + .toButton() + ); + + this.buttonMap.put(47, new ItemBuilder(Material.POTION) + .setDisplayName(CC.AQUA + "Potion Effects") + .setAmount(potionEffectStrings.size()) + .addLore( + potionEffectStrings.isEmpty() ? InventorySnapshot.NO_EFFECT_LIST : potionEffectStrings + ) + .toButton() + ); + + this.longestCombo = playerData.getLongestCombo(); + this.totalHits = playerData.getHits(); + + final int ping = PlayerUtil.getPing(player); + + this.buttonMap.put(49, new ItemBuilder(Material.PAPER) + .setDisplayName(CC.AQUA + "Match Statistics") + .addLore( + CC.GRAY + "Longest Combo: " + CC.WHITE + playerData.getLongestCombo() + " hit" + (playerData.getLongestCombo() > 1 ? "s" : ""), + CC.GRAY + "Total Hits: " + CC.WHITE + playerData.getHits() + " hit" + (playerData.getHits() > 1 ? "s" : ""), + CC.GRAY + "Average Ping: " + CC.WHITE + (ping == 1 ? "Unknown" : ping + " ms") + ) + .toButton() + ); + + if (!match.isParty()) { + this.buttonMap.put(53, new ItemBuilder(Material.LEVER) + .setDisplayName(Color.MAIN_COLOR + "Opponent's Inventory") + .addLore( + CC.GRAY + "See your opponent's inventory", + CC.GRAY + "and combat details." + ) + .toButton((player1, clickType) -> { + final UUID uuid = Practice.getInstance().getMatchManager().getRematcherInventory(player.getUniqueId()); + + if (uuid == null) { + player1.sendMessage(CC.RED + "The inventory you're currently viewing has expired."); + player1.closeInventory(); + return; + } + + player1.performCommand("inventory " + uuid); + }) + ); + } + + for (int i = 36; i < 40; i++) { + this.buttonMap.put(i, new ItemBuilder(armor[39 - i] == null ? InventorySnapshot.AIR : armor[39 - i]) + .toButton() + ); + } + } + + @Override + public String getTitle(Player player) { + return "Inventory » " + this.target.getDisplayName(); + } + + @Override + public Map getButtons(Player player) { + return this.buttonMap; + } + + @Override + public int getSize() { + return 54; + } +} diff --git a/src/main/java/com/solexgames/practice/match/team/KillableTeam.java b/src/main/java/com/solexgames/practice/match/team/KillableTeam.java new file mode 100644 index 0000000..830a980 --- /dev/null +++ b/src/main/java/com/solexgames/practice/match/team/KillableTeam.java @@ -0,0 +1,42 @@ +package com.solexgames.practice.match.team; + +import com.solexgames.practice.Practice; +import lombok.Getter; +import lombok.Setter; +import org.bukkit.entity.Player; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.UUID; +import java.util.stream.Stream; + +@Getter +@Setter +public class KillableTeam { + + protected final Practice plugin = Practice.getInstance(); + + private final List players; + private final List alivePlayers = new ArrayList<>(); + private final String leaderName; + + private UUID leader; + + public KillableTeam(UUID leader, List players) { + this.leader = leader; + this.leaderName = this.plugin.getServer().getPlayer(leader).getName(); + this.players = players; + this.alivePlayers.addAll(players); + } + + public void killPlayer(UUID playerUUID) { + this.alivePlayers.remove(playerUUID); + } + + public Stream streamAlivePlayers() { + return this.players.stream() + .map(this.plugin.getServer()::getPlayer) + .filter(Objects::nonNull); + } +} diff --git a/src/main/java/com/solexgames/practice/match/team/impl/MatchTeam.java b/src/main/java/com/solexgames/practice/match/team/impl/MatchTeam.java new file mode 100644 index 0000000..b352d50 --- /dev/null +++ b/src/main/java/com/solexgames/practice/match/team/impl/MatchTeam.java @@ -0,0 +1,18 @@ +package com.solexgames.practice.match.team.impl; + +import com.solexgames.practice.match.team.KillableTeam; +import lombok.Getter; + +import java.util.List; +import java.util.UUID; + +@Getter +public class MatchTeam extends KillableTeam { + + private final int teamID; + + public MatchTeam(UUID leader, List players, int teamID) { + super(leader, players); + this.teamID = teamID; + } +} diff --git a/src/main/java/com/solexgames/practice/menu/DeathEffectMenu.java b/src/main/java/com/solexgames/practice/menu/DeathEffectMenu.java new file mode 100644 index 0000000..3727a32 --- /dev/null +++ b/src/main/java/com/solexgames/practice/menu/DeathEffectMenu.java @@ -0,0 +1,62 @@ +package com.solexgames.practice.menu; + +import com.solexgames.core.util.builder.ItemBuilder; +import com.solexgames.core.util.external.Button; +import com.solexgames.core.util.external.Menu; +import com.solexgames.practice.Practice; +import com.solexgames.practice.player.PlayerData; +import com.solexgames.practice.util.CC; +import org.bukkit.entity.Player; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * @author GrowlyX + * @since 7/7/2021 + */ + +public class DeathEffectMenu extends Menu { + + @Override + public String getTitle(Player player) { + return "Death Effects"; + } + + @Override + public Map getButtons(Player player) { + final Map buttonMap = new HashMap<>(); + final AtomicInteger atomicInteger = new AtomicInteger(); + final PlayerData data = Practice.getInstance().getPlayerManager().getPlayerData(player.getUniqueId()); + + Practice.getInstance().getEffectManager().getEffectList().forEach(effect -> { + final boolean equipped = data.getEffect().getName().equals(effect.getName()); + + buttonMap.put(atomicInteger.getAndIncrement(), new ItemBuilder(effect.getIcon()) + .setDisplayName(equipped ? CC.GREEN + effect.getName() : CC.RED + effect.getName()) + .addLore( + equipped ? CC.GRAY + "You have this equipped." : CC.GRAY + "You don't have this equipped.", + "", + equipped ? CC.RED + "[Already have equipped]" : CC.YELLOW + "[Click to equip]" + ) + .toUpdatingButton((player1, clickType) -> { + if (equipped) { + player.sendMessage(CC.RED + "You already have this effect equipped."); + return; + } + + if (effect.getPermission() != null && !player.hasPermission(effect.getPermission())) { + player.sendMessage(CC.RED + "You don't have permission to apply this cosmetic."); + return; + } + + data.setEffect(effect); + player.sendMessage(CC.YELLOW + "You've equipped the " + CC.AQUA + effect.getName() + CC.YELLOW + " effect."); + }) + ); + }); + + return buttonMap; + } +} diff --git a/src/main/java/com/solexgames/practice/menu/DivisionViewMenu.java b/src/main/java/com/solexgames/practice/menu/DivisionViewMenu.java new file mode 100644 index 0000000..f0b1d5e --- /dev/null +++ b/src/main/java/com/solexgames/practice/menu/DivisionViewMenu.java @@ -0,0 +1,71 @@ +package com.solexgames.practice.menu; + +import com.solexgames.core.util.builder.ItemBuilder; +import com.solexgames.core.util.external.Button; +import com.solexgames.core.util.external.pagination.PaginatedMenu; +import com.solexgames.practice.util.CC; +import com.solexgames.practice.util.division.RankedDivision; +import org.bukkit.Material; +import org.bukkit.entity.Player; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * @author GrowlyX + * @since 8/12/2021 + */ + +public class DivisionViewMenu extends PaginatedMenu { + + private final Player target; + + public DivisionViewMenu(Player target) { + super(9); + + this.target = target; + } + + @Override + public Map getGlobalButtons(Player player) { + final Map buttonMap = new HashMap<>(); + + buttonMap.put(4, new ItemBuilder(Material.BED) + .setDisplayName(CC.RED + "Click to return") + .addLore( + CC.GRAY + "Return to the main", + CC.GRAY + "statistics menu." + ) + .toButton((player1, clickType) -> { + new StatisticsMenu(this.target).openMenu(player1); + }) + ); + + return buttonMap; + } + + @Override + public String getPrePaginatedTitle(Player player) { + return "Divisions"; + } + + @Override + public Map getAllPagesButtons(Player player) { + final Map buttonMap = new HashMap<>(); + final AtomicInteger atomicInteger = new AtomicInteger(); + + RankedDivision.DIVISIONS.forEach(division -> { + buttonMap.put(atomicInteger.getAndIncrement(), new ItemBuilder(Material.GLOWSTONE_DUST) + .setDisplayName(division.getFancyName()) + .addLore( + CC.GRAY + "Minimum ELO: " + CC.WHITE + division.getMinimum(), + CC.GRAY + "Maximum ELO: " + CC.WHITE + division.getMaximum() + ) + .toButton() + ); + }); + + return buttonMap; + } +} diff --git a/src/main/java/com/solexgames/practice/menu/EventHostMenu.java b/src/main/java/com/solexgames/practice/menu/EventHostMenu.java new file mode 100644 index 0000000..24d9d53 --- /dev/null +++ b/src/main/java/com/solexgames/practice/menu/EventHostMenu.java @@ -0,0 +1,137 @@ +package com.solexgames.practice.menu; + +import com.solexgames.core.util.Color; +import com.solexgames.core.util.builder.ItemBuilder; +import com.solexgames.core.util.external.Button; +import com.solexgames.core.util.external.Menu; +import com.solexgames.practice.util.CC; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.entity.Player; + +import java.util.HashMap; +import java.util.Map; + +/** + * @author GrowlyX + * @since 5/15/2021 + */ + +public class EventHostMenu extends Menu { + + @Override + public String getTitle(Player player) { + return "Host an Event"; + } + + @Override + public Map getButtons(Player player) { + final Map buttonMap = new HashMap<>(); + + buttonMap.put(1, new ItemBuilder(Material.GOLD_BOOTS) + .setDisplayName(Color.MAIN_COLOR + "TNT Run") + .addLore( + "&7Run around the arena", + "&7while tnt disappears", + "&7behind you. Try not to", + "&7fall to your death!", + "", + "&7You need the &6Gold&7", + "&7rank to host this event.", + "", + "&e[Click to host TNT Run]" + ) + .toButton((player1, clickType) -> { + Bukkit.dispatchCommand(player1, "host TNTRun"); + player.closeInventory(); + }) + ); + + buttonMap.put(2, new ItemBuilder(Material.WOOL) + .setDurability(3) + .setDisplayName(Color.MAIN_COLOR + "4Corners") + .addLore( + "&7Players choose a platform", + "&7out of four and hope their ", + "&7choice lets them live.", + "", + "&7You need the &aBasic&7", + "&7rank to host this event.", + "", + "&e[Click to host 4Corners]" + ) + .toButton((player1, clickType) -> { + Bukkit.dispatchCommand(player1, "host 4Corners"); + player.closeInventory(); + }) + ); + buttonMap.put(3, new ItemBuilder(Material.BOW) + .setDisplayName(Color.MAIN_COLOR + "One in the Chamber") + .addLore( + "&7Players kill each other to", + "&7become the first to " + Color.SECONDARY_COLOR + CC.BOLD + "20 kills&7.", + "", + "&7You need the &aBasic&7", + "&7rank to host this event.", + "", + "&e[Click to host OITC]" + ) + .toButton((player1, clickType) -> { + Bukkit.dispatchCommand(player1, "host OITC"); + player.closeInventory(); + }) + ); + buttonMap.put(5, new ItemBuilder(Material.STICK) + .setDisplayName(Color.MAIN_COLOR + "Sumo") + .addLore( + "&7Players attempt to hit their", + "&7opponent of the round platform.", + "", + "&7You need the &7Silver&7", + "&7rank to host this event.", + "", + "&e[Click to host Sumo]" + ) + .toButton((player1, clickType) -> { + Bukkit.dispatchCommand(player1, "host Sumo"); + player.closeInventory(); + }) + ); + buttonMap.put(6, new ItemBuilder(Material.GOLD_PLATE) + .setDisplayName(Color.MAIN_COLOR + "Parkour") + .addLore( + "&7Jump across the map and try", + "&7to beat your opponent to the", + "&7end of the arena.", + "", + "&7You need the &7Silver&7", + "&7rank to host this event.", + "", + "&e[Click to host Parkour]" + ) + .toButton((player1, clickType) -> { + Bukkit.dispatchCommand(player1, "host Parkour"); + player.closeInventory(); + }) + ); + buttonMap.put(7, new ItemBuilder(Material.DIAMOND_AXE) + .setDisplayName(Color.MAIN_COLOR + "Last Man Standing") + .addLore( + "&7Play with 75 other players in", + "&7a large arena and try to stay", + "&7alive until the end.", + "", + "&7You need the &bPlatinum&7", + "&7rank to host this event.", + "", + "&c[This event is coming soon]" + ) + .toButton((player1, clickType) -> { + Bukkit.dispatchCommand(player1, "host LMS"); + player.closeInventory(); + }) + ); + + return buttonMap; + } +} diff --git a/src/main/java/com/solexgames/practice/menu/JoinQueueMenu.java b/src/main/java/com/solexgames/practice/menu/JoinQueueMenu.java new file mode 100644 index 0000000..72a7526 --- /dev/null +++ b/src/main/java/com/solexgames/practice/menu/JoinQueueMenu.java @@ -0,0 +1,126 @@ +package com.solexgames.practice.menu; + +import com.solexgames.core.util.Color; +import com.solexgames.core.util.builder.ItemBuilder; +import com.solexgames.core.util.external.Button; +import com.solexgames.core.util.external.Menu; +import com.solexgames.practice.Practice; +import com.solexgames.practice.kit.Kit; +import com.solexgames.practice.party.Party; +import com.solexgames.practice.player.PlayerData; +import com.solexgames.practice.player.PlayerState; +import com.solexgames.practice.queue.QueueType; +import com.solexgames.practice.runnable.LeaderboardUpdateRunnable; +import com.solexgames.practice.runnable.cache.StatusCache; +import com.solexgames.practice.util.CC; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import org.bukkit.ChatColor; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.event.inventory.ClickType; +import org.bukkit.inventory.ItemFlag; +import org.bukkit.inventory.ItemStack; + +import java.util.*; + +/** + * @author GrowlyX + * @since 5/15/2021 + */ + +@Getter +@RequiredArgsConstructor +public class JoinQueueMenu extends Menu { + + private final QueueType queueType; + + @Override + public String getTitle(Player player) { + return "Join an " + this.queueType.getName() + " queue"; + } + + @Override + public Map getButtons(Player player) { + final Map buttonMap = new HashMap<>(); + final PlayerData data = Practice.getInstance().getPlayerManager().getPlayerData(player.getUniqueId()); + + if (this.queueType.isRanked()) { + Practice.getInstance().getKitManager().getKits().stream() + .filter(Objects::nonNull) + .filter(Kit::isRanked) + .filter(Kit::isEnabled) + .forEachOrdered(kit -> buttonMap.put(kit.getQueueMenu() + 10, new KitButton(kit, data))); + } else { + Practice.getInstance().getKitManager().getKits().stream() + .filter(Objects::nonNull) + .filter(Kit::isEnabled) + .forEachOrdered(kit -> buttonMap.put(kit.getQueueMenu() + 10, new KitButton(kit, data))); + } + + return buttonMap; + } + + @RequiredArgsConstructor + private class KitButton extends Button { + + private final Kit queue; + private final PlayerData data; + + @Override + public ItemStack getButtonItem(final Player player) { + final List lore = new ArrayList<>(); + final int playing = StatusCache.getInstance().getFightingKits().get(this.queue); + + lore.add("&7In Queue: &f" + Practice.getInstance().getQueueManager().getQueueSize(this.queue.getName(), JoinQueueMenu.this.queueType)); + lore.add("&7In Fights: &f" + playing); + lore.add(" "); + lore.add("&6&lDaily Winstreaks:"); + lore.addAll(LeaderboardUpdateRunnable.KIT_SPECIFIC_WIN_STREAK_LORE_SHORT.getOrDefault(this.queue, Collections.emptyList())); + lore.add(" "); + lore.add(CC.YELLOW + "[Click to join queue]"); + + return new ItemBuilder(this.queue.getIcon().clone()) + .setDisplayName(Color.MAIN_COLOR + CC.BOLD + this.queue.getName()) + .addLore(lore) + .setAmount(playing > 64 ? 64 : playing == 0 ? 1 : playing) + .addItemFlags(ItemFlag.HIDE_UNBREAKABLE, ItemFlag.HIDE_ATTRIBUTES, ItemFlag.HIDE_POTION_EFFECTS, ItemFlag.HIDE_ENCHANTS) + .create(); + } + + @Override + public void clicked(Player player, ClickType clickType) { + final Party party = Practice.getInstance().getPartyManager().getParty(player.getUniqueId()); + + if (this.data.getPlayerState() != PlayerState.SPAWN) { + player.sendMessage(CC.RED + "You must be at spawn to queue for a ladder."); + return; + } + + player.closeInventory(); + + if (this.queue != null) { + if (party == null) { + Practice.getInstance().getQueueManager().addPlayerToQueue(player, this.data, this.queue.getName(), JoinQueueMenu.this.queueType); + } else if (Practice.getInstance().getPartyManager().isLeader(player.getUniqueId())) { + Practice.getInstance().getQueueManager().addPartyToQueue(player, party, this.queue.getName(), JoinQueueMenu.this.queueType); + } + } + } + } + + @Override + public int getSize() { + return 36; + } + + @Override + public boolean isFillBorders() { + return true; + } + + @Override + public boolean isAutoUpdate() { + return true; + } +} diff --git a/src/main/java/com/solexgames/practice/menu/KillMessageMenu.java b/src/main/java/com/solexgames/practice/menu/KillMessageMenu.java new file mode 100644 index 0000000..8a6dd09 --- /dev/null +++ b/src/main/java/com/solexgames/practice/menu/KillMessageMenu.java @@ -0,0 +1,64 @@ +package com.solexgames.practice.menu; + +import com.solexgames.core.util.builder.ItemBuilder; +import com.solexgames.core.util.external.Button; +import com.solexgames.core.util.external.Menu; +import com.solexgames.practice.Practice; +import com.solexgames.practice.player.PlayerData; +import com.solexgames.practice.util.CC; +import org.bukkit.Material; +import org.bukkit.entity.Player; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * @author GrowlyX + * @since 7/7/2021 + */ + +public class KillMessageMenu extends Menu { + + @Override + public String getTitle(Player player) { + return "Kill Messages"; + } + + @Override + public Map getButtons(Player player) { + final Map buttonMap = new HashMap<>(); + final AtomicInteger atomicInteger = new AtomicInteger(); + final PlayerData data = Practice.getInstance().getPlayerManager().getPlayerData(player.getUniqueId()); + + Practice.getInstance().getEffectManager().getMessageList().forEach(message -> { + final boolean equipped = data.getDeathMessageType().equals(message.getName()); + + buttonMap.put(atomicInteger.getAndIncrement(), new ItemBuilder(Material.BOOK_AND_QUILL) + .setDisplayName(equipped ? CC.GREEN + message.getName() : CC.RED + message.getName()) + .addLore( + equipped ? CC.GRAY + "You have this equipped." : CC.GRAY + "You don't have this equipped.", + "", + equipped ? CC.RED + "[Already have equipped]" : CC.YELLOW + "[Click to equip]" + ) + .toUpdatingButton((player1, clickType) -> { + if (equipped) { + player.sendMessage(CC.RED + "You already have this effect equipped."); + return; + } + + if (!message.getName().equals("Normal") && !player.hasPermission("practice.killmessages." + message.getName().toLowerCase())) { + player.sendMessage(CC.RED + "You don't have permission to apply this cosmetic."); + return; + } + + data.setDeathMessageType(message.getName()); + + player.sendMessage(CC.YELLOW + "You've equipped the " + CC.AQUA + message.getName() + CC.YELLOW + " kill message bundle."); + }) + ); + }); + + return buttonMap; + } +} diff --git a/src/main/java/com/solexgames/practice/menu/KyroJoinQueueMenu.java b/src/main/java/com/solexgames/practice/menu/KyroJoinQueueMenu.java new file mode 100644 index 0000000..7d4aac9 --- /dev/null +++ b/src/main/java/com/solexgames/practice/menu/KyroJoinQueueMenu.java @@ -0,0 +1,228 @@ +package com.solexgames.practice.menu; + +import com.cryptomorin.xseries.XMaterial; +import com.solexgames.core.menu.impl.SettingsMenu; +import com.solexgames.core.util.Color; +import com.solexgames.core.util.builder.ItemBuilder; +import com.solexgames.core.util.external.Button; +import com.solexgames.core.util.external.Menu; +import com.solexgames.practice.Practice; +import com.solexgames.practice.kit.Kit; +import com.solexgames.practice.party.Party; +import com.solexgames.practice.player.PlayerData; +import com.solexgames.practice.player.PlayerState; +import com.solexgames.practice.queue.QueueType; +import com.solexgames.practice.util.CC; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import org.apache.commons.lang3.StringUtils; +import org.bukkit.ChatColor; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.event.inventory.ClickType; +import org.bukkit.inventory.ItemFlag; +import org.bukkit.inventory.ItemStack; + +import java.util.*; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * @author GrowlyX + * @since 5/15/2021 + */ + +@Getter +@RequiredArgsConstructor +public class KyroJoinQueueMenu extends Menu { + + private final QueueType queueType; + + @Override + public String getTitle(Player player) { + return this.queueType.getName() + " Queue"; + } + + @Override + public Map getButtons(Player player) { + final Map buttonMap = new HashMap<>(); + final AtomicInteger atomicInteger = new AtomicInteger(10); + final PlayerData data = Practice.getInstance().getPlayerManager().getPlayerData(player.getUniqueId()); + if (this.queueType.isRanked()) { + for (int i = 0; i <= 53; ++i) { + buttonMap.put(i, (new ItemBuilder(XMaterial.GRAY_STAINED_GLASS_PANE).setDurability(7).setDisplayName(" ").toButton())); + } + buttonMap.put(4, new ItemBuilder(Material.WATCH) + .setDisplayName(CC.GREEN + "Queuing") + .addLore( + ChatColor.GRAY.toString() + ChatColor.STRIKETHROUGH + StringUtils.repeat("-", 38), + CC.GRAY + "Select a kit below to join a " + CC.AQUA + this.queueType.getName() + CC.GRAY + " queue", + ChatColor.GRAY.toString() + ChatColor.STRIKETHROUGH + StringUtils.repeat("-", 38) + ) + .toButton()); + + buttonMap.put(45, new ItemBuilder(Material.STONE_BUTTON) + .setDisplayName(ChatColor.YELLOW + "Open Unranked Queues Menu " + CC.GRAY + "(" + CC.WHITE + "Right Click" + CC.GRAY + ")") + .addLore("&7Right Click to open the next queue menu.") + .toButton((player1, clickType) -> new JoinQueueMenu(QueueType.UNRANKED).openMenu(player))); + + buttonMap.put(53, new ItemBuilder(Material.REDSTONE) + .setDisplayName(ChatColor.RED + "Close Ranked Queues Menu") + .addLore("&7Right Click to close this menu.") + .toButton((player1, clickType) -> player.closeInventory())); + + Practice.getInstance().getKitManager().getKits().stream() + .filter(Objects::nonNull) + .filter(Kit::isRanked) + .filter(Kit::isEnabled) + .forEachOrdered(kit -> { + buttonMap.put(atomicInteger.get(), new KitButton(kit, data)); + + if (atomicInteger.get() == 16) { + atomicInteger.set(19); + } else if (atomicInteger.get() == 25) { + atomicInteger.set(28); + } else if (atomicInteger.get() == 34) { + atomicInteger.set(37); + } else { + atomicInteger.getAndIncrement(); + } + }); + } else { + for (int i = 0; i <= 53; ++i) { + buttonMap.put(i, (new ItemBuilder(XMaterial.GRAY_STAINED_GLASS_PANE).setDurability(7).setDisplayName(" ").toButton())); + } + buttonMap.put(4, new ItemBuilder(Material.WATCH) + .setDisplayName(CC.GREEN + "Queuing") + .addLore( + ChatColor.GRAY.toString() + ChatColor.STRIKETHROUGH + StringUtils.repeat("-", 38), + CC.GRAY + "Select a kit below to join a " + CC.AQUA + this.queueType.getName() + CC.GRAY + " queue", + ChatColor.GRAY.toString() + ChatColor.STRIKETHROUGH + StringUtils.repeat("-", 38) + ) + .toButton()); + + buttonMap.put(53, new ItemBuilder(Material.STONE_BUTTON) + .setDisplayName(ChatColor.AQUA + "Open Ranked Queues menu " + CC.GRAY + "(" + CC.WHITE + "Right Click" + CC.GRAY + ")") + .addLore("&7Right Click to open the next queue menu.") + .toButton((player1, clickType) -> new JoinQueueMenu(QueueType.RANKED).openMenu(player))); + + buttonMap.put(45, new ItemBuilder(Material.REDSTONE) + .setDisplayName(ChatColor.RED + "Close Unranked Queues Menu") + .addLore("&7Right Click to close this menu.") + .toButton((player1, clickType) -> player.closeInventory())); + + Practice.getInstance().getKitManager().getKits().stream() + .filter(Objects::nonNull) + .filter(Kit::isEnabled) + .forEachOrdered(kit -> { + buttonMap.put(atomicInteger.get(), new KitButton(kit, data)); + + if (atomicInteger.get() == 16) { + atomicInteger.set(19); + } else if (atomicInteger.get() == 25) { + atomicInteger.set(28); + } else if (atomicInteger.get() == 34) { + atomicInteger.set(37); + } else { + atomicInteger.getAndIncrement(); + } + }); + } + + buttonMap.put(0, new ItemBuilder(Material.REDSTONE) + .setDisplayName(ChatColor.RED + "Close " + this.getQueueType().getName() + " Queue Menu") + .addLore("&7Right Click to close this menu.") + .toButton((player1, clickType) -> player.closeInventory())); + + buttonMap.put(1, new ItemBuilder(Material.ANVIL) + .setDisplayName(ChatColor.LIGHT_PURPLE + "Open Settings " + CC.GRAY + "(" + CC.WHITE + "Right Click" + CC.GRAY + ")") + .addLore("&7Right Click to open the Settings menu.") + .toButton((player1, clickType) -> new SettingsMenu(player).openMenu(player))); + + buttonMap.put(7, new ItemBuilder(Material.ITEM_FRAME) + .setDisplayName(ChatColor.AQUA + "Open Leaderboards " + CC.GRAY + "(" + CC.WHITE + "Right Click" + CC.GRAY + ")") + .addLore("&7Right Click to open the Leaderboards menu.") + .toButton((player1, clickType) -> player.sendMessage(CC.RED + "This feature is currently unavailable."))); + + buttonMap.put(8, new ItemBuilder(Material.REDSTONE) + .setDisplayName(ChatColor.RED + "Close " + this.getQueueType().getName() + " Queue Menu") + .addLore("&7Right Click to close this menu.") + .toButton((player1, clickType) -> player.closeInventory())); + + buttonMap.put(47, new ItemBuilder(Material.CARPET) + .setDurability(5) + .setDisplayName(ChatColor.GREEN + "1v1") + .addLore("&7You're currently viewing 1v1.") + .toButton((player1, clickType) -> player.sendMessage(CC.RED + "This mode is currently unavailable."))); + + buttonMap.put(49, new ItemBuilder(Material.CARPET) + .setDurability(7) + .setDisplayName(ChatColor.RED + "2v2") + .addLore("&7Click to switch to 2v2.") + .toButton((player1, clickType) -> player.sendMessage(CC.RED + "This mode is currently unavailable."))); + + buttonMap.put(51, new ItemBuilder(Material.CARPET) + .setDurability(7) + .setDisplayName(ChatColor.RED + "3v3") + .addLore("&7Click to switch to 3v3.") + .toButton((player1, clickType) -> player.sendMessage(CC.RED + "This mode is currently unavailable."))); + + return buttonMap; + } + + @RequiredArgsConstructor + private class KitButton extends Button { + + private final Kit queue; + private final PlayerData data; + + @Override + public ItemStack getButtonItem(final Player player) { + final List lore = new ArrayList<>(); + final PlayerData data = Practice.getInstance().getPlayerManager().getPlayerData(player.getUniqueId()); + final int playing = (int) Practice.getInstance().getPlayerManager().getAllData().stream() + .filter(Objects::nonNull) + .filter(playerData -> playerData.getPlayerState().equals(PlayerState.FIGHTING) && Practice.getInstance().getMatchManager().getMatch(playerData.getCurrentMatchID()) != null && Practice.getInstance().getMatchManager().getMatch(playerData.getCurrentMatchID()).getKit().getName().equals((this.queue != null ? this.queue.getName() : ""))) + .count(); + + lore.add("&7In Queue: " + Color.MAIN_COLOR + Practice.getInstance().getQueueManager().getQueueSize(this.queue.getName(), KyroJoinQueueMenu.this.queueType)); + lore.add("&7In Fights: " + Color.MAIN_COLOR + playing); + lore.add(" "); + lore.add("&7Wins: " + Color.MAIN_COLOR + data.getWins(this.queue.getName())); + lore.add("&7Losses: " + Color.MAIN_COLOR + data.getLosses(this.queue.getName())); + + if (this.queue.isRanked() && KyroJoinQueueMenu.this.queueType.isRanked()) { + lore.add(" "); + lore.add("&7Elo: &f" + this.data.getElo(this.queue.getName())); + } + + lore.add(" "); + lore.add(CC.YELLOW + "[Click to join the queue]"); + + return new ItemBuilder(this.queue.getIcon().clone()) + .setDisplayName(Color.MAIN_COLOR + CC.BOLD + this.queue.getName()) + .addLore(lore) + .addItemFlags(ItemFlag.HIDE_UNBREAKABLE, ItemFlag.HIDE_ATTRIBUTES, ItemFlag.HIDE_POTION_EFFECTS, ItemFlag.HIDE_ENCHANTS) + .create(); + } + + @Override + public void clicked(Player player, ClickType clickType) { + final Party party = Practice.getInstance().getPartyManager().getParty(player.getUniqueId()); + + if (this.data.getPlayerState() != PlayerState.SPAWN) { + player.sendMessage(CC.RED + "You must be at spawn to queue for a ladder."); + return; + } + + player.closeInventory(); + + if (this.queue != null) { + if (party == null) { + Practice.getInstance().getQueueManager().addPlayerToQueue(player, this.data, this.queue.getName(), KyroJoinQueueMenu.this.queueType); + } else if (Practice.getInstance().getPartyManager().isLeader(player.getUniqueId())) { + Practice.getInstance().getQueueManager().addPartyToQueue(player, party, this.queue.getName(), KyroJoinQueueMenu.this.queueType); + } + } + } + } +} diff --git a/src/main/java/com/solexgames/practice/menu/LeaderboardsMenu.java b/src/main/java/com/solexgames/practice/menu/LeaderboardsMenu.java new file mode 100644 index 0000000..9175b5c --- /dev/null +++ b/src/main/java/com/solexgames/practice/menu/LeaderboardsMenu.java @@ -0,0 +1,126 @@ +package com.solexgames.practice.menu; + +import com.solexgames.core.player.ranks.Rank; +import com.solexgames.core.util.builder.ItemBuilder; +import com.solexgames.core.util.external.Button; +import com.solexgames.core.util.external.Menu; +import com.solexgames.practice.Practice; +import com.solexgames.practice.kit.Kit; +import com.solexgames.practice.player.PlayerData; +import com.solexgames.practice.runnable.LeaderboardUpdateRunnable; +import com.solexgames.practice.util.CC; +import com.solexgames.practice.util.division.RankedDivision; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import org.bukkit.ChatColor; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemFlag; +import org.bukkit.inventory.ItemStack; + +import java.util.*; + +/** + * @author GrowlyX + * @since 5/15/2021 + */ + +@Getter +public class LeaderboardsMenu extends Menu { + + private boolean winStreakLb = false; + + @Override + public String getTitle(Player player) { + return "Leaderboards"; + } + + @Override + public Map getButtons(Player player) { + final Map buttonMap = new HashMap<>(); + final PlayerData playerData = Practice.getInstance().getPlayerManager().getPlayerData(player.getUniqueId()); + + if (!this.winStreakLb) { + final List globalEloLore = new ArrayList<>(); + + globalEloLore.add("&7Showing top 10 results."); + globalEloLore.add(" "); + globalEloLore.addAll(LeaderboardUpdateRunnable.GLOBAL_ELO_LEADERBOARD_LORE); + + final RankedDivision rankedDivision = RankedDivision.getByGlobalElo(playerData.getGlobalElo()); + + if (rankedDivision != null) { + globalEloLore.add(" "); + globalEloLore.add(CC.GRAY + "Your current division is " + rankedDivision.getFancyName()); + globalEloLore.add(CC.GRAY + "and you have " + CC.WHITE + playerData.getGlobalElo() + CC.GRAY + " elo."); + } + + buttonMap.put(4, new ItemBuilder(Material.NETHER_STAR) + .setDisplayName(CC.GOLD + CC.BOLD + "Global Elo") + .addLore(globalEloLore) + .toButton()); + } + + Practice.getInstance().getKitManager().getKits().stream() + .filter(Objects::nonNull) + .filter(Kit::isRanked) + .filter(Kit::isEnabled) + .forEachOrdered(kit -> { + buttonMap.put(kit.getQueueMenu() + 10, new KitButton(kit)); + }); + + buttonMap.put(39, new ItemBuilder(Material.INK_SACK) + .setDurability(this.winStreakLb ? 8 : 10) + .setDisplayName((!this.winStreakLb ? ChatColor.GREEN : ChatColor.RED) + "ELO Leaderboards") + .addLore(CC.GRAY + (!this.winStreakLb ? + "You're currently viewing this menu." : + "Click to select this menu." + )) + .toUpdatingButton((player1, clickType) -> this.winStreakLb = !this.winStreakLb) + ); + + buttonMap.put(41, new ItemBuilder(Material.INK_SACK) + .setDurability(this.winStreakLb ? 10 : 8) + .setDisplayName((this.winStreakLb ? ChatColor.GREEN : ChatColor.RED) + "Win Streak Leaderboards") + .addLore(CC.GRAY + (this.winStreakLb ? + "You're currently viewing this menu." : + "Click to select this menu." + )) + .toUpdatingButton((player1, clickType) -> this.winStreakLb = !this.winStreakLb) + ); + + return buttonMap; + } + + @Override + public int getSize() { + return 45; + } + + @Override + public boolean isFillBorders() { + return true; + } + + @RequiredArgsConstructor + private class KitButton extends Button { + + private final Kit queue; + + @Override + public ItemStack getButtonItem(final Player player) { + final List globalEloLore = new ArrayList<>(); + final Map> map = !LeaderboardsMenu.this.winStreakLb ? LeaderboardUpdateRunnable.KIT_SPECIFIC_LEADERBOARD_LORE : LeaderboardUpdateRunnable.KIT_SPECIFIC_WIN_STREAK_LORE; + + globalEloLore.add("&7Showing top 10 results."); + globalEloLore.add(" "); + globalEloLore.addAll(map.get(this.queue) == null ? Collections.singletonList(CC.RED + "Failed to load results") : map.get(this.queue)); + + return new ItemBuilder(this.queue.getIcon().clone()) + .setDisplayName(CC.GOLD + CC.BOLD + this.queue.getName() + (winStreakLb ? " Win Streaks" : " Elo")) + .addLore(globalEloLore) + .addItemFlags(ItemFlag.HIDE_UNBREAKABLE, ItemFlag.HIDE_ATTRIBUTES, ItemFlag.HIDE_POTION_EFFECTS, ItemFlag.HIDE_ENCHANTS) + .create(); + } + } +} diff --git a/src/main/java/com/solexgames/practice/menu/MatchmakingSettingsMenu.java b/src/main/java/com/solexgames/practice/menu/MatchmakingSettingsMenu.java new file mode 100644 index 0000000..65ffe65 --- /dev/null +++ b/src/main/java/com/solexgames/practice/menu/MatchmakingSettingsMenu.java @@ -0,0 +1,61 @@ +package com.solexgames.practice.menu; + +import com.solexgames.core.util.builder.ItemBuilder; +import com.solexgames.core.util.external.Button; +import com.solexgames.core.util.external.Menu; +import com.solexgames.practice.Practice; +import com.solexgames.practice.player.PlayerData; +import com.solexgames.practice.util.CC; +import org.bukkit.Material; +import org.bukkit.entity.Player; + +import java.util.HashMap; +import java.util.Map; + +/** + * @author GrowlyX + * @since 7/29/2021 + */ + +public class MatchmakingSettingsMenu extends Menu { + + @Override + public String getTitle(Player player) { + return "Matchmaking Settings"; + } + + @Override + public Map getButtons(Player player) { + final Map buttonMap = new HashMap<>(); + final PlayerData playerData = Practice.getInstance().getPlayerManager() + .getPlayerData(player.getUniqueId()); + + buttonMap.put(0, new ItemBuilder(Material.STICK) + .setDisplayName(CC.YELLOW + "Ping Range: " + CC.AQUA + (playerData.getPingRange() == -1 ? "Unrestricted" : playerData.getPingRange())) + .addLore( + "", + "&7&oRight-click to decrease by 50.", + "&7&oLeft-click to increase by 50." + ) + .toUpdatingButton((player1, clickType) -> { + final int currentPingRange = playerData.getPingRange(); + + if (clickType.name().contains("RIGHT")) { + if (currentPingRange == 50) { + playerData.setPingRange(-1); + } else { + playerData.setPingRange(currentPingRange == -1 ? 300 : currentPingRange - 50); + } + } else if (clickType.name().contains("LEFT")) { + if (currentPingRange == 300) { + playerData.setPingRange(-1); + } else { + playerData.setPingRange(currentPingRange == -1 ? 50 : currentPingRange + 50); + } + } + }) + ); + + return buttonMap; + } +} diff --git a/src/main/java/com/solexgames/practice/menu/OtherPartiesMenu.java b/src/main/java/com/solexgames/practice/menu/OtherPartiesMenu.java new file mode 100644 index 0000000..6e2f290 --- /dev/null +++ b/src/main/java/com/solexgames/practice/menu/OtherPartiesMenu.java @@ -0,0 +1,85 @@ +package com.solexgames.practice.menu; + +import com.solexgames.core.util.Color; +import com.solexgames.core.util.builder.ItemBuilder; +import com.solexgames.core.util.external.Button; +import com.solexgames.core.util.external.pagination.PaginatedMenu; +import com.solexgames.practice.Practice; +import com.solexgames.practice.party.Party; +import com.solexgames.practice.util.CC; +import lombok.RequiredArgsConstructor; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.event.inventory.ClickType; +import org.bukkit.inventory.ItemStack; + +import java.util.*; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * @author GrowlyX + * @since 5/17/2021 + */ + +public class OtherPartiesMenu extends PaginatedMenu { + + public OtherPartiesMenu() { + super(27); + } + + @Override + public Map getGlobalButtons(Player player) { + return null; + } + + @Override + public String getPrePaginatedTitle(Player player) { + return "Select a Game"; + } + + @Override + public Map getAllPagesButtons(Player player) { + final Map buttonMap = new HashMap<>(); + final AtomicInteger atomicInteger = new AtomicInteger(); + + Practice.getInstance().getPartyManager().getParties().values().forEach(party -> buttonMap.put(atomicInteger.getAndIncrement(), new PartyButton(party))); + + return buttonMap; + } + + @RequiredArgsConstructor + private static class PartyButton extends Button { + + private final Party party; + + @Override + public ItemStack getButtonItem(Player player) { + final Player leader = Bukkit.getPlayer(this.party.getLeader()); + final List stringList = new ArrayList<>(Arrays.asList( + "", + Color.MAIN_COLOR + "Participants:" + )); + + this.party.getMembers().forEach(uuid -> stringList.add(CC.GRAY + " - " + Bukkit.getPlayer(uuid).getDisplayName())); + + stringList.add(""); + stringList.add(CC.YELLOW + "[Click to fight this party]"); + + return new ItemBuilder(Material.SKULL_ITEM) + .setDurability(3) + .setOwner(leader.getName()) + .setDisplayName(Color.MAIN_COLOR + CC.BOLD + leader.getName() + "'s Party") + .addLore(stringList) + .create(); + } + + @Override + public void clicked(Player player, ClickType clickType) { + final Player leader = Bukkit.getPlayer(this.party.getLeader()); + + Bukkit.dispatchCommand(player, "duel " + leader.getName()); + player.closeInventory(); + } + } +} diff --git a/src/main/java/com/solexgames/practice/menu/SpectateMenu.java b/src/main/java/com/solexgames/practice/menu/SpectateMenu.java new file mode 100644 index 0000000..7a6f066 --- /dev/null +++ b/src/main/java/com/solexgames/practice/menu/SpectateMenu.java @@ -0,0 +1,78 @@ +package com.solexgames.practice.menu; + +import com.solexgames.core.util.builder.ItemBuilder; +import com.solexgames.core.util.external.Button; +import com.solexgames.core.util.external.pagination.PaginatedMenu; +import com.solexgames.practice.Practice; +import com.solexgames.practice.util.CC; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.entity.Player; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * @author GrowlyX + * @since 8/16/2021 + */ + +public class SpectateMenu extends PaginatedMenu { + + public SpectateMenu() { + super(45); + } + + @Override + public String getPrePaginatedTitle(Player player) { + return "Spectate a match"; + } + + @Override + public Map getGlobalButtons(Player player) { + final Map buttonMap = new HashMap<>(); + + buttonMap.put(4, new ItemBuilder(Material.NETHER_STAR) + .setDisplayName(CC.GREEN + "Click to update") + .addLore( + CC.GRAY + "Update matches on your", + CC.GRAY + "spectate menu." + ) + .toButton((player1, clickType) -> { + new SpectateMenu().openMenu(player1); + }) + ); + + return buttonMap; + } + + @Override + public Map getAllPagesButtons(Player player) { + final Map buttonMap = new HashMap<>(); + final AtomicInteger atomicInteger = new AtomicInteger(); + + Practice.getInstance().getMatchManager().getMatches().forEach((uuid, match) -> { + final String[] strings = new String[]{ + Bukkit.getPlayer(match.getTeams().get(0).getLeader()).getName(), + Bukkit.getPlayer(match.getTeams().get(1).getLeader()).getName(), + }; + + buttonMap.put(atomicInteger.getAndIncrement(), new ItemBuilder(match.getKit().getIcon().clone()) + .setDisplayName(CC.AQUA + CC.BOLD + strings[0] + " vs. " + strings[1]) + .addLore( + CC.GRAY + "Kit: " + CC.WHITE + match.getKit().getName(), + CC.GRAY + "Arena: " + CC.WHITE + match.getArena().getName(), + CC.GRAY + "Spectators: " + CC.WHITE + match.getSpectators().size(), + "", + CC.YELLOW + "[Click to spectate]" + ) + .toButton((player1, clickType) -> { + player1.performCommand("spec " + strings[0]); + }) + ); + }); + + return buttonMap; + } +} diff --git a/src/main/java/com/solexgames/practice/menu/StatisticsMenu.java b/src/main/java/com/solexgames/practice/menu/StatisticsMenu.java new file mode 100644 index 0000000..0a87bd1 --- /dev/null +++ b/src/main/java/com/solexgames/practice/menu/StatisticsMenu.java @@ -0,0 +1,149 @@ +package com.solexgames.practice.menu; + +import com.solexgames.core.util.builder.ItemBuilder; +import com.solexgames.core.util.external.Button; +import com.solexgames.core.util.external.Menu; +import com.solexgames.practice.Practice; +import com.solexgames.practice.challenges.tracker.KillStreakDataTracker; +import com.solexgames.practice.kit.Kit; +import com.solexgames.practice.player.PlayerData; +import com.solexgames.practice.util.CC; +import com.solexgames.practice.util.division.RankedDivision; +import lombok.RequiredArgsConstructor; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemFlag; +import org.bukkit.inventory.ItemStack; + +import java.util.*; + +/** + * @author GrowlyX + * @since 5/15/2021 + */ + +@RequiredArgsConstructor +public class StatisticsMenu extends Menu { + + private final Player target; + + @Override + public String getTitle(Player player) { + return "Statistics » " + this.target.getDisplayName(); + } + + @Override + public Map getButtons(Player player) { + final Map buttonMap = new HashMap<>(); + final List globalStats = new ArrayList<>(); + final PlayerData playerData = Practice.getInstance().getPlayerManager().getPlayerData(this.target.getUniqueId()); + + globalStats.add(CC.GRAY + "ELO: " + CC.WHITE + playerData.getGlobalElo()); + + final RankedDivision rankedDivision = RankedDivision.getByGlobalElo(playerData.getGlobalElo()); + + if (rankedDivision != null) { + globalStats.add(CC.GRAY + "Division: " + CC.WHITE + rankedDivision.getFancyName()); + } + + globalStats.add(" "); + + final double wins = playerData.getGlobalWins(); + final double losses = playerData.getGlobalLosses(); + final double wlr = wins == 0 || losses == 0 ? 0.0 : wins / losses; + + globalStats.add(CC.GRAY + "Wins: " + CC.WHITE + wins); + globalStats.add(CC.GRAY + "Losses: " + CC.WHITE + losses); + globalStats.add(CC.GRAY + "W/L Ratio: " + CC.WHITE + wlr); + + buttonMap.put(rankedDivision == null ? 4 : 3, new ItemBuilder(Material.NETHER_STAR) + .setDisplayName(CC.AQUA + CC.BOLD + "Global Statistics") + .addLore(globalStats) + .toButton()); + + if (rankedDivision != null) { + final RankedDivision next = rankedDivision.getNext(); + + final String nextDivision = next == null ? rankedDivision.getFancyName() : next.getFancyName(); + final int amountNeeded = next == null ? 0 : next.getMinimum() - rankedDivision.getMinimum(); + final int current = next == null ? 0 : playerData.getGlobalElo() - rankedDivision.getMinimum(); + final int currentReality = Math.max(current, 0); + + final String percentage = String.format("%.2f", (currentReality * 100.0f) / amountNeeded); + + buttonMap.put(5, new ItemBuilder(Material.GLOWSTONE_DUST) + .setDisplayName(CC.AQUA + CC.BOLD + "Division Statistics") + .addLore( + CC.GRAY + "Current Division: " + CC.WHITE + rankedDivision.getFancyName(), + CC.GRAY + "Current Elo: " + CC.WHITE + playerData.getGlobalElo(), + " ", + CC.GRAY + "Next Division: " + CC.WHITE + nextDivision, + CC.GRAY + "Progress: " + CC.AQUA + current + "/" + amountNeeded + CC.WHITE + " (" + percentage + "%)", + " ", + CC.YELLOW + "[Click to view all divisions]" + ) + .toButton((player1, clickType) -> { + new DivisionViewMenu(this.target).openMenu(player1); + })); + } + + /*final ChallengePlayer challengePlayer = BarChallengesPlugin.getInstance() + .getPlayerHandler().getByPlayer(playerData.getBukkitPlayer()); + + final KillStreakDataTracker killStreakDataTracker = challengePlayer + .getTrackerByClass(KillStreakDataTracker.class);*/ + + Practice.getInstance().getKitManager().getKits().stream() + .filter(Objects::nonNull) + .filter(Kit::isRanked) + .filter(Kit::isEnabled) + .forEachOrdered(kit -> { + buttonMap.put(kit.getQueueMenu() + 10, new KitButton(kit, playerData)); + }); + + return buttonMap; + } + + @Override + public int getSize() { + return 45; + } + + @Override + public boolean isFillBorders() { + return true; + } + + @RequiredArgsConstructor + private static class KitButton extends Button { + + private final Kit queue; + + private final PlayerData playerData; + + @Override + public ItemStack getButtonItem(final Player player) { + final List kitStats = new ArrayList<>(); + + kitStats.add(CC.GRAY + "ELO: " + CC.WHITE + this.playerData.getElo(this.queue.getName())); + kitStats.add(" "); + + final double wins = this.playerData.getWins(this.queue.getName()); + final double losses = this.playerData.getLosses(this.queue.getName()); + final double wlr = wins == 0 || losses == 0 ? 0.0 : wins / losses; + + kitStats.add(CC.GRAY + "Wins: " + CC.WHITE + wins); + kitStats.add(CC.GRAY + "Losses: " + CC.WHITE + losses); + kitStats.add(CC.GRAY + "W/L Ratio: " + CC.WHITE + wlr); + kitStats.add(" "); + kitStats.add(CC.GRAY + "Streak: " + CC.WHITE + this.playerData.getKitKillStreaks().getOrDefault(this.queue.getName(), 0)); + kitStats.add(CC.GRAY + "Best Streak: " + CC.WHITE + this.playerData.getHighestKillStreak(this.queue.getName())); + + return new ItemBuilder(this.queue.getIcon().clone()) + .setDisplayName(CC.AQUA + CC.BOLD + this.queue.getName() + " Statistics") + .addLore(kitStats) + .addItemFlags(ItemFlag.HIDE_UNBREAKABLE, ItemFlag.HIDE_ATTRIBUTES, ItemFlag.HIDE_POTION_EFFECTS, ItemFlag.HIDE_ENCHANTS) + .create(); + } + } +} diff --git a/src/main/java/com/solexgames/practice/menu/duel/DuelMenu.java b/src/main/java/com/solexgames/practice/menu/duel/DuelMenu.java new file mode 100644 index 0000000..e1cb6e4 --- /dev/null +++ b/src/main/java/com/solexgames/practice/menu/duel/DuelMenu.java @@ -0,0 +1,270 @@ +package com.solexgames.practice.menu.duel; + +import com.solexgames.core.util.Color; +import com.solexgames.core.util.PlayerUtil; +import com.solexgames.core.util.builder.ItemBuilder; +import com.solexgames.core.util.clickable.Clickable; +import com.solexgames.core.util.external.Button; +import com.solexgames.core.util.external.Menu; +import com.solexgames.practice.Practice; +import com.solexgames.practice.arena.Arena; +import com.solexgames.practice.kit.Kit; +import com.solexgames.practice.party.Party; +import com.solexgames.practice.player.PlayerData; +import com.solexgames.practice.player.PlayerState; +import com.solexgames.practice.util.CC; +import com.solexgames.practice.util.StringUtil; +import lombok.RequiredArgsConstructor; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.entity.Player; +import org.bukkit.event.inventory.ClickType; +import org.bukkit.inventory.ItemFlag; +import org.bukkit.inventory.ItemStack; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * @author GrowlyX + * @since 7/29/2021 + */ + +@RequiredArgsConstructor +public class DuelMenu extends Menu { + + private final Player target; + + private Kit kit; + private Arena arena; + + private boolean arenaSelecting = false; + + @Override + public String getTitle(Player player) { + return "Duel Panel » " + this.target.getDisplayName(); + } + + @Override + public int getSize() { + return 54; + } + + @Override + public Map getButtons(Player player) { + final Map buttonMap = new HashMap<>(); + final AtomicInteger integer = new AtomicInteger(10); + final PlayerData data = Practice.getInstance().getPlayerManager().getPlayerData(player.getUniqueId()); + + if (this.arenaSelecting) { + Practice.getInstance().getArenaManager().getArenas().values().stream() + .filter(arena -> Practice.getInstance().isKitPairedWithArena(kit, arena)) + .forEach(arena -> { + if (integer.get() > 43) { + return; + } + + buttonMap.put(integer.get(), new ArenaButton(arena)); + + if (integer.get() == 16) { + integer.set(19); + } else if (integer.get() == 25) { + integer.set(28); + } else if (integer.get() == 34) { + integer.set(37); + } else { + integer.getAndIncrement(); + } + }); + } else { + Practice.getInstance().getKitManager().getKits().stream() + .filter(Kit::isEnabled) + .forEach(arena -> { + if (integer.get() > 43) { + return; + } + + buttonMap.put(integer.get(), new KitButton(arena)); + + if (integer.get() == 16) { + integer.set(19); + } else if (integer.get() == 25) { + integer.set(28); + } else if (integer.get() == 34) { + integer.set(37); + } else { + integer.getAndIncrement(); + } + }); + } + + buttonMap.put(48, new ItemBuilder(this.arenaSelecting ? Material.CHEST : Material.PAPER) + .setDisplayName(this.arenaSelecting ? CC.AQUA + CC.BOLD + "Kit Selection" : CC.AQUA + CC.BOLD + "Arena Selection") + .addLore( + CC.GRAY + "Click to switch inventories." + ) + .toUpdatingButton((player1, clickType) -> { + if (this.kit == null) { + player.sendMessage(CC.RED + "You must select a kit before switching to the arena inventory"); + return; + } + + this.arenaSelecting = !this.arenaSelecting; + }) + ); + + buttonMap.put(50, new ItemBuilder(Material.INK_SACK) + .setDisplayName(CC.AQUA + CC.BOLD + "Confirm Duel") + .addLore( + CC.GRAY + "Confirm your settings and send", + CC.GRAY + "the duel to out.", + "", + (this.arena == null || this.kit == null ? CC.RED + "[Cannot send duel right now]" : CC.YELLOW + "[Click to send duel to player]") + ) + .setDurability(this.arena == null || this.kit == null ? 8 : 10) + .toButton((player1, clickType) -> { + if (this.arena == null) { + player.sendMessage(CC.RED + "You must select an arena before confirming the duel."); + return; + } + + if (this.kit == null) { + player.sendMessage(CC.RED + "You must select a kit before confirming the duel."); + return; + } + + final Player selected = Bukkit.getPlayer(data.getDuelSelecting()); + + if (selected == null) { + player.sendMessage(String.format(StringUtil.PLAYER_NOT_FOUND, data.getDuelSelecting())); + return; + } + + final PlayerData targetData = Practice.getInstance().getPlayerManager().getPlayerData(selected.getUniqueId()); + + if (targetData.getPlayerState() != PlayerState.SPAWN) { + player.sendMessage(CC.RED + "That player is currently busy."); + return; + } + + final Party targetParty = Practice.getInstance().getPartyManager().getParty(selected.getUniqueId()); + final Party party = Practice.getInstance().getPartyManager().getParty(player.getUniqueId()); + + final boolean partyDuel = party != null; + + if (partyDuel && targetParty == null) { + player.sendMessage(CC.RED + "That player is not in a party."); + return; + } + + if (Practice.getInstance().getMatchManager().getMatchRequest(player.getUniqueId(), selected.getUniqueId()) != null) { + player.sendMessage(CC.RED + "You have already sent a duel request to this player, please wait."); + return; + } + + Practice.getInstance().getMatchManager().createMatchRequest(player, selected, this.arena, DuelMenu.this.kit.getName(), partyDuel); + + final Clickable requestMessage = new Clickable(Color.MAIN_COLOR + player.getDisplayName() + CC.GRAY + " (" + PlayerUtil.getPing(player) + "ms)" + Color.SECONDARY_COLOR + " has sent you a " + (partyDuel ? "party" : "") + "duel request" + ((DuelMenu.this.kit.getName() != null) ? (" with kit " + Color.MAIN_COLOR + DuelMenu.this.kit.getName().replace("_", " ") + Color.SECONDARY_COLOR) : "") + ((this.arena == null) ? "" : (" on arena " + Color.MAIN_COLOR + this.arena.getName())) + Color.SECONDARY_COLOR + ". " + CC.GREEN + "[Click Here]", CC.GREEN + "Click to accept this duel.", "/accept " + player.getName() + " " + DuelMenu.this.kit.getName()); + + if (partyDuel) { + targetParty.getMembers().forEach(uuid -> Bukkit.getPlayer(uuid).spigot().sendMessage(requestMessage.asComponents())); + party.broadcast(Color.SECONDARY_COLOR + "Sent " + Color.MAIN_COLOR + selected.getDisplayName() + Color.SECONDARY_COLOR + " a " + Color.MAIN_COLOR + kit.getName().replace("_", " ") + Color.SECONDARY_COLOR + " duel request" + ((arena == null) ? "." : (" on " + Color.MAIN_COLOR + arena.getName() + Color.SECONDARY_COLOR + "."))); + } else { + selected.spigot().sendMessage(requestMessage.asComponents()); + player.sendMessage(Color.SECONDARY_COLOR + "Sent " + Color.MAIN_COLOR + selected.getDisplayName() + Color.SECONDARY_COLOR + " a " + Color.MAIN_COLOR + kit.getName().replace("_", " ") + Color.SECONDARY_COLOR + " duel request" + ((arena == null) ? "." : (" on " + Color.MAIN_COLOR + arena.getName() + Color.SECONDARY_COLOR + "."))); + } + + player.closeInventory(); + })); + + return buttonMap; + } + + @RequiredArgsConstructor + private class ArenaButton extends Button { + + private final Arena newArena; + + @Override + public ItemStack getButtonItem(final Player player) { + final List lore = new ArrayList<>(); + + if (arena != null && arena.getName().equals(newArena.getName())) { + lore.add(CC.GRAY + "You have this arena selected."); + lore.add(" "); + } + + lore.add(CC.YELLOW + "[Click to duel with this arena]"); + + final ItemBuilder itemBuilder = new ItemBuilder(this.newArena.getIcon() == null ? Material.PAPER : this.newArena.getIcon()) + .setDisplayName(Color.MAIN_COLOR + CC.BOLD + this.newArena.getName()) + .addLore(lore); + + if (arena == newArena) { + itemBuilder.setEnchant(Enchantment.DAMAGE_ALL, 1); + } + + return itemBuilder.addItemFlags(ItemFlag.HIDE_UNBREAKABLE, ItemFlag.HIDE_ATTRIBUTES, ItemFlag.HIDE_POTION_EFFECTS, ItemFlag.HIDE_ENCHANTS).create(); + } + + @Override + public boolean shouldUpdate(Player player, ClickType clickType) { + if (arena == this.newArena) { + arena = null; + return true; + } + + arena = this.newArena; + return true; + } + } + + @RequiredArgsConstructor + private class KitButton extends Button { + + private final Kit queue; + + @Override + public ItemStack getButtonItem(final Player player) { + final List lore = new ArrayList<>(); + + if (kit != null && kit.getName().equals(queue.getName())) { + lore.add(CC.GRAY + "You have this kit selected."); + lore.add(" "); + } + + lore.add(CC.YELLOW + "[Click to duel with this kit]"); + + final ItemBuilder itemBuilder = new ItemBuilder(this.queue.getIcon().clone()) + .setDisplayName(Color.MAIN_COLOR + CC.BOLD + this.queue.getName()) + .addLore(lore) + .addItemFlags(ItemFlag.HIDE_UNBREAKABLE, ItemFlag.HIDE_ATTRIBUTES, ItemFlag.HIDE_POTION_EFFECTS, ItemFlag.HIDE_ENCHANTS); + + if (kit == queue) { + itemBuilder.setEnchant(Enchantment.DAMAGE_ALL, 1); + } + + return itemBuilder.create(); + } + + @Override + public boolean shouldUpdate(Player player, ClickType clickType) { + if (kit == this.queue) { + kit = null; + return true; + } + + kit = this.queue; + return true; + } + } + + @Override + public boolean isFillBorders() { + return true; + } +} diff --git a/src/main/java/com/solexgames/practice/menu/editor/KitEditorMainMenu.java b/src/main/java/com/solexgames/practice/menu/editor/KitEditorMainMenu.java new file mode 100644 index 0000000..36c14f8 --- /dev/null +++ b/src/main/java/com/solexgames/practice/menu/editor/KitEditorMainMenu.java @@ -0,0 +1,80 @@ +package com.solexgames.practice.menu.editor; + +import com.solexgames.core.util.Color; +import com.solexgames.core.util.builder.ItemBuilder; +import com.solexgames.core.util.external.Button; +import com.solexgames.core.util.external.pagination.PaginatedMenu; +import com.solexgames.practice.Practice; +import com.solexgames.practice.kit.Kit; +import com.solexgames.practice.player.PlayerState; +import com.solexgames.practice.util.CC; +import lombok.RequiredArgsConstructor; +import org.bukkit.entity.Player; +import org.bukkit.event.inventory.ClickType; +import org.bukkit.inventory.ItemFlag; +import org.bukkit.inventory.ItemStack; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * @author GrowlyX + * @since 5/17/2021 + */ + +public class KitEditorMainMenu extends PaginatedMenu { + + public KitEditorMainMenu() { + super(18); + } + + @Override + public Map getGlobalButtons(Player player) { + return null; + } + + @Override + public String getPrePaginatedTitle(Player player) { + return "Kit Editor"; + } + + @Override + public Map getAllPagesButtons(Player player) { + final Map buttonMap = new HashMap<>(); + final AtomicInteger integer = new AtomicInteger(); + + Practice.getInstance().getKitManager().getKits().stream() + .filter(Kit::isEnabled) + .filter(kit -> kit.getContents() != null) + .forEach(kit -> buttonMap.put(integer.getAndIncrement(), new KitButton(kit))); + + return buttonMap; + } + + @RequiredArgsConstructor + private static class KitButton extends Button { + + private final Kit kit; + + @Override + public ItemStack getButtonItem(Player player) { + return new ItemBuilder(this.kit.getIcon()) + .setDisplayName(Color.MAIN_COLOR + CC.BOLD + this.kit.getName()) + .addItemFlags(ItemFlag.HIDE_UNBREAKABLE, ItemFlag.HIDE_ATTRIBUTES, ItemFlag.HIDE_POTION_EFFECTS, ItemFlag.HIDE_ENCHANTS) + .addLore( + CC.GRAY + "Edit your match layout for", + CC.GRAY + "the " + this.kit.getName() + " kit.", + "", + CC.YELLOW + "[Click to edit kit]" + ) + .create(); + } + + @Override + public void clicked(Player player, ClickType clickType) { + Practice.getInstance().getEditorManager().addEditor(player, this.kit); + Practice.getInstance().getPlayerManager().getPlayerData(player.getUniqueId()).setPlayerState(PlayerState.EDITING); + } + } +} diff --git a/src/main/java/com/solexgames/practice/menu/editor/KitEditorMenu.java b/src/main/java/com/solexgames/practice/menu/editor/KitEditorMenu.java new file mode 100644 index 0000000..5fecc28 --- /dev/null +++ b/src/main/java/com/solexgames/practice/menu/editor/KitEditorMenu.java @@ -0,0 +1,136 @@ +package com.solexgames.practice.menu.editor; + +import com.solexgames.core.CorePlugin; +import com.solexgames.core.util.builder.ItemBuilder; +import com.solexgames.core.util.external.Button; +import com.solexgames.core.util.external.Menu; +import com.solexgames.practice.Practice; +import com.solexgames.practice.kit.Kit; +import com.solexgames.practice.kit.PlayerKit; +import com.solexgames.practice.player.PlayerData; +import com.solexgames.practice.util.CC; +import com.solexgames.practice.util.prompt.RenameKitLoadoutPrompt; +import lombok.RequiredArgsConstructor; +import org.bukkit.ChatColor; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +import java.util.HashMap; +import java.util.Map; +import java.util.stream.IntStream; + +/** + * @author GrowlyX + * @since 7/15/2021 + */ + +@RequiredArgsConstructor +public class KitEditorMenu extends Menu { + + private final Kit kit; + + @Override + public String getTitle(Player player) { + return "Kit Editor"; + } + + @Override + public int getSize() { + return 36; + } + + @Override + public Map getButtons(Player player) { + final Map buttonMap = new HashMap<>(); + final PlayerData data = Practice.getInstance().getPlayerManager().getPlayerData(player.getUniqueId()); + final Map playerKitMap = data.getPlayerKits(this.kit.getName()); + + IntStream.range(1, 8).forEach(integer -> { + final PlayerKit playerKit = playerKitMap.get(integer); + + buttonMap.put(integer, new ItemBuilder(Material.BOOK) + .setDisplayName(playerKit == null ? CC.YELLOW + "Save kit " + CC.AQUA + "#" + integer : CC.AQUA + CC.BOLD + "Loadout " + playerKit.getDisplayName()) + .addLore(CC.GRAY + "Click to create or save a kit.") + .toUpdatingButton((player1, clickType) -> { + if (playerKit != null) { + playerKit.setContents(player.getInventory().getContents().clone()); + player.sendMessage(CC.YELLOW + "You've saved the " + CC.AQUA + playerKit.getDisplayName() + CC.YELLOW + " loadout."); + return; + } + + if (integer > 3 && !player.hasPermission("practice.editor.more")) { + player.sendMessage(new String[]{ + ChatColor.RED + "You do not have permission to create kit #" + integer + ".", + ChatColor.RED + "Purchase the " + ChatColor.AQUA + "Basic" + ChatColor.RED + " rank at " + ChatColor.YELLOW + "store.nodebuff.com" + ChatColor.RED + " to unlock this feature." + }); + return; + } + + final PlayerKit newPlayerKit = new PlayerKit(this.kit.getName(), integer, player.getInventory().getContents().clone(), this.kit.getName() + " #" + integer); + data.addPlayerKit(integer, newPlayerKit); + + player.sendMessage(CC.YELLOW + "You've created the " + CC.AQUA + newPlayerKit.getDisplayName() + CC.YELLOW + " loadout."); + }) + ); + + if (playerKit != null) { + buttonMap.put(integer + 9, new ItemBuilder(Material.CHEST) + .setDisplayName(CC.AQUA + "Get Loadout") + .addLore( + CC.GRAY + "Click to load this loadout's inventory", + CC.GRAY + "into your inventory." + ) + .toUpdatingButton((player1, clickType) -> { + final ItemStack[] contents = playerKit.getContents(); + + for (final ItemStack itemStack : contents) { + if (itemStack != null) { + if (itemStack.getAmount() <= 0) { + itemStack.setAmount(1); + } + } + } + + player.getInventory().setContents(contents); + player.updateInventory(); + + player.sendMessage(CC.YELLOW + "You've loaded the " + CC.AQUA + playerKit.getDisplayName() + CC.YELLOW + " loadout."); + }) + ); + + buttonMap.put(integer + 18, new ItemBuilder(Material.NAME_TAG) + .setDisplayName(CC.AQUA + "Rename Loadout") + .addLore( + CC.GRAY + "Click to change this loadout's", + CC.GRAY + "display name." + ) + .toButton((player1, clickType) -> { + player.closeInventory(); + + CorePlugin.getInstance().getConversationFactory() + .withFirstPrompt(new RenameKitLoadoutPrompt(playerKit)) + .withLocalEcho(false) + .buildConversation(player).begin(); + }) + ); + + buttonMap.put(integer + 27, new ItemBuilder(Material.INK_SACK) + .setDurability(1) + .setDisplayName(CC.RED + "Delete Loadout") + .addLore( + CC.GRAY + "Click to delete this loadout", + CC.GRAY + "from your loadouts." + ) + .toUpdatingButton((player1, clickType) -> { + playerKitMap.remove(integer); + + player.sendMessage(CC.YELLOW + "You've deleted the kit with the name " + CC.AQUA + playerKit.getDisplayName() + CC.YELLOW + "."); + }) + ); + } + }); + + return buttonMap; + } +} diff --git a/src/main/java/com/solexgames/practice/menu/management/ArenaManagementMainMenu.java b/src/main/java/com/solexgames/practice/menu/management/ArenaManagementMainMenu.java new file mode 100644 index 0000000..f131fe2 --- /dev/null +++ b/src/main/java/com/solexgames/practice/menu/management/ArenaManagementMainMenu.java @@ -0,0 +1,66 @@ +package com.solexgames.practice.menu.management; + +import com.solexgames.core.util.builder.ItemBuilder; +import com.solexgames.core.util.external.Button; +import com.solexgames.core.util.external.pagination.PaginatedMenu; +import com.solexgames.practice.Practice; +import com.solexgames.practice.arena.Arena; +import com.solexgames.practice.menu.management.generation.ArenaManagementGenerationMenu; +import com.solexgames.practice.util.CC; +import io.papermc.lib.PaperLib; +import org.bukkit.entity.Player; +import org.bukkit.event.inventory.ClickType; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * @author GrowlyX + * @since 8/8/2021 + */ + +public class ArenaManagementMainMenu extends PaginatedMenu { + + public ArenaManagementMainMenu() { + super(27); + } + + @Override + public Map getGlobalButtons(Player player) { + return null; + } + + @Override + public String getPrePaginatedTitle(Player player) { + return "Arena Manager » Main"; + } + + @Override + public Map getAllPagesButtons(Player player) { + final Map buttonMap = new HashMap<>(); + final AtomicInteger atomicInteger = new AtomicInteger(); + + for (final Arena arena : Practice.getInstance().getArenaManager().getArenas().values()) { + buttonMap.put(atomicInteger.getAndIncrement(), new ItemBuilder(arena.getIcon()) + .setDisplayName(CC.AQUA + arena.getName()) + .addLore( + CC.GRAY + "Enabled: " + CC.WHITE + arena.isEnabled(), + CC.GRAY + "Arenas: " + CC.WHITE + (arena.getStandaloneArenas() == null || arena.getStandaloneArenas().size() == 0 ? "Single Arena (Invisible Players)" : arena.getStandaloneArenas().size() + " Arenas"), + "", + CC.YELLOW + "[Left-click to teleport]", + CC.YELLOW + "[Right-click to generate]" + ) + .toButton((player1, clickType) -> { + if (clickType == ClickType.LEFT) { + PaperLib.teleportAsync(player, arena.getPositionOne().toBukkitLocation()); + } else { + new ArenaManagementGenerationMenu(arena).openMenu(player1); + } + }) + ); + } + + return buttonMap; + } +} diff --git a/src/main/java/com/solexgames/practice/menu/management/generation/ArenaManagementGenerationMenu.java b/src/main/java/com/solexgames/practice/menu/management/generation/ArenaManagementGenerationMenu.java new file mode 100644 index 0000000..c1709df --- /dev/null +++ b/src/main/java/com/solexgames/practice/menu/management/generation/ArenaManagementGenerationMenu.java @@ -0,0 +1,68 @@ +package com.solexgames.practice.menu.management.generation; + +import com.solexgames.core.util.builder.ItemBuilder; +import com.solexgames.core.util.external.Button; +import com.solexgames.core.util.external.pagination.PaginatedMenu; +import com.solexgames.practice.arena.Arena; +import com.solexgames.practice.util.CC; +import org.bukkit.Material; +import org.bukkit.entity.Player; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * @author GrowlyX + * @since 8/8/2021 + */ + +public class ArenaManagementGenerationMenu extends PaginatedMenu { + + private static final int[] BATCHES = { 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 200 }; + + private final Arena arena; + + public ArenaManagementGenerationMenu(Arena arena) { + super(9); + + this.arena = arena; + } + + @Override + public String getPrePaginatedTitle(Player player) { + return "Arena Manager » " + this.arena.getName() + " » Generation"; + } + + @Override + public Map getGlobalButtons(Player player) { + return null; + } + + @Override + public Map getAllPagesButtons(Player player) { + final Map buttonMap = new HashMap<>(); + final AtomicInteger atomicInteger = new AtomicInteger(); + + for (int batchAmount : ArenaManagementGenerationMenu.BATCHES) { + buttonMap.put(atomicInteger.getAndIncrement(), new ItemBuilder(Material.EMERALD) + .setDisplayName(CC.AQUA + "" + batchAmount) + .addLore( + "&7Selecting this will generate", + "&e" + batchAmount + "&7 standalone", + "&7arenas. This process may cause", + "&7lag!", + "", + "&e[Click to start generating]" + ) + .toButton((player1, clickType) -> { + player.performCommand("arena generate " + this.arena.getName() + " " + batchAmount); + + player.sendMessage(CC.GREEN + "You're now generating " + CC.YELLOW + batchAmount + CC.GREEN + " standalone arenas for " + CC.YELLOW + this.arena.getName() + CC.GREEN + "."); + }) + ); + } + + return buttonMap; + } +} diff --git a/src/main/java/com/solexgames/practice/menu/party/PartyEventsMenu.java b/src/main/java/com/solexgames/practice/menu/party/PartyEventsMenu.java new file mode 100644 index 0000000..49d35e3 --- /dev/null +++ b/src/main/java/com/solexgames/practice/menu/party/PartyEventsMenu.java @@ -0,0 +1,76 @@ +package com.solexgames.practice.menu.party; + +import com.solexgames.core.util.Color; +import com.solexgames.core.util.builder.ItemBuilder; +import com.solexgames.core.util.external.Button; +import com.solexgames.core.util.external.Menu; +import com.solexgames.practice.menu.OtherPartiesMenu; +import com.solexgames.practice.menu.party.events.PartyFFAMenu; +import com.solexgames.practice.menu.party.events.PartySplitMenu; +import com.solexgames.practice.util.CC; +import org.bukkit.Material; +import org.bukkit.entity.Player; + +import java.util.HashMap; +import java.util.Map; + +/** + * @author GrowlyX + * @since 5/22/2021 + */ + +public class PartyEventsMenu extends Menu { + + @Override + public String getTitle(Player player) { + return "Select a Game"; + } + + @Override + public Map getButtons(Player player) { + final Map buttonMap = new HashMap<>(); + + buttonMap.put(1, new ItemBuilder(Material.DIAMOND_SWORD) + .setDisplayName(Color.MAIN_COLOR + CC.BOLD + "Party Queues") + .addLore( + "&7Queue your party for an", + "&7unranked or ranked match.", + "", + "&e[Click to queue party]" + ) + .toButton((player1, clickType) -> new PartyQueueMenu().openMenu(player))); + + buttonMap.put(3, new ItemBuilder(Material.BLAZE_POWDER) + .setDisplayName(Color.MAIN_COLOR + CC.BOLD + "Split Party") + .addLore( + "&7Split your party into", + "&7two teams and fight with", + "&7the selected kit!", + "", + "&e[Click to split party]" + ) + .toButton((player1, clickType) -> new PartySplitMenu().openMenu(player))); + + buttonMap.put(5, new ItemBuilder(Material.PISTON_BASE) + .setDisplayName(Color.MAIN_COLOR + CC.BOLD + "Free for All") + .addLore( + "&7Start a party free-for-all", + "&7match and find out who wins!", + "", + "&e[Click to start an ffa match]" + ) + .toButton((player1, clickType) -> new PartyFFAMenu().openMenu(player))); + + buttonMap.put(7, new ItemBuilder(Material.SKULL_ITEM) + .setDisplayName(Color.MAIN_COLOR + CC.BOLD + "Fight other party") + .addLore( + "&7Start a duel match between", + "&7another party!", + "", + "&e[Click to fight party]" + ) + .toButton((player1, clickType) -> new OtherPartiesMenu().openMenu(player))); + + return buttonMap; + } +} diff --git a/src/main/java/com/solexgames/practice/menu/party/PartyQueueMenu.java b/src/main/java/com/solexgames/practice/menu/party/PartyQueueMenu.java new file mode 100644 index 0000000..bc8a9e4 --- /dev/null +++ b/src/main/java/com/solexgames/practice/menu/party/PartyQueueMenu.java @@ -0,0 +1,64 @@ +package com.solexgames.practice.menu.party; + +import com.solexgames.core.util.Color; +import com.solexgames.core.util.builder.ItemBuilder; +import com.solexgames.core.util.external.Button; +import com.solexgames.core.util.external.Menu; +import com.solexgames.practice.menu.JoinQueueMenu; +import com.solexgames.practice.queue.QueueType; +import com.solexgames.practice.util.CC; +import org.bukkit.Material; +import org.bukkit.entity.Player; + +import java.util.HashMap; +import java.util.Map; + +/** + * @author GrowlyX + * @since 6/30/2021 + */ + +public class PartyQueueMenu extends Menu { + + @Override + public String getTitle(Player player) { + return "Select a Game"; + } + + @Override + public Map getButtons(Player player) { + final Map buttonMap = new HashMap<>(); + + buttonMap.put(3, new ItemBuilder(Material.IRON_SWORD) + .setDisplayName(Color.MAIN_COLOR + CC.BOLD + "Unranked") + .addLore( + "&7Join a queue for an unranked", + "&7ladder!", + "", + "&e[Click to join unranked]" + ) + .toButton((player1, clickType) -> new JoinQueueMenu(QueueType.UNRANKED).openMenu(player))); + + buttonMap.put(5, new ItemBuilder(Material.DIAMOND_SWORD) + .setDisplayName(Color.MAIN_COLOR + CC.BOLD + "Ranked") + .addLore( + "&7Join a queue for an ranked", + "&7ladder!", + "", + "&e[Click to join ranked]" + ) + .toButton((player1, clickType) -> new JoinQueueMenu(QueueType.RANKED).openMenu(player))); + + buttonMap.put(8, new ItemBuilder(Material.BED) + .setDisplayName(CC.RED + "Return") + .addLore( + "&7Return to the main events", + "&7menu!", + "", + "&e[Click to return home]" + ) + .toButton((player1, clickType) -> new PartyEventsMenu().openMenu(player))); + + return buttonMap; + } +} diff --git a/src/main/java/com/solexgames/practice/menu/party/events/PartyFFAMenu.java b/src/main/java/com/solexgames/practice/menu/party/events/PartyFFAMenu.java new file mode 100644 index 0000000..d4318ec --- /dev/null +++ b/src/main/java/com/solexgames/practice/menu/party/events/PartyFFAMenu.java @@ -0,0 +1,89 @@ +package com.solexgames.practice.menu.party.events; + +import com.solexgames.core.util.Color; +import com.solexgames.core.util.builder.ItemBuilder; +import com.solexgames.core.util.external.Button; +import com.solexgames.core.util.external.Menu; +import com.solexgames.practice.Practice; +import com.solexgames.practice.kit.Kit; +import com.solexgames.practice.menu.party.events.arena.PartyFFAArenaMenu; +import com.solexgames.practice.party.Party; +import com.solexgames.practice.player.PlayerData; +import com.solexgames.practice.util.CC; +import lombok.RequiredArgsConstructor; +import org.bukkit.entity.Player; +import org.bukkit.event.inventory.ClickType; +import org.bukkit.inventory.ItemFlag; +import org.bukkit.inventory.ItemStack; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * @author GrowlyX + * @since 5/22/2021 + */ + +public class PartyFFAMenu extends Menu { + + @Override + public String getTitle(Player player) { + return "Select a Kit"; + } + + @Override + public Map getButtons(Player player) { + final Map buttonMap = new HashMap<>(); + final AtomicInteger atomicInteger = new AtomicInteger(); + final PlayerData data = Practice.getInstance().getPlayerManager().getPlayerData(player.getUniqueId()); + + Practice.getInstance().getKitManager().getKits().stream() + .filter(Kit::isEnabled) + .forEach(kit -> buttonMap.put(atomicInteger.getAndIncrement(), new KitButton(kit, data))); + + return buttonMap; + } + + @RequiredArgsConstructor + private static class KitButton extends Button { + + private final Kit queue; + private final PlayerData data; + + @Override + public ItemStack getButtonItem(final Player player) { + final List lore = new ArrayList<>(); + + lore.add(CC.GRAY + "Elo: " + CC.WHITE + this.data.getElo(this.queue.getName())); + lore.add(" "); + lore.add(CC.YELLOW + "[Click to play with this kit]"); + + return new ItemBuilder(this.queue.getIcon().clone()) + .setDisplayName(Color.MAIN_COLOR + CC.BOLD + this.queue.getName()) + .addLore(lore) + .addItemFlags(ItemFlag.HIDE_UNBREAKABLE, ItemFlag.HIDE_ATTRIBUTES, ItemFlag.HIDE_POTION_EFFECTS, ItemFlag.HIDE_ENCHANTS) + .create(); + } + + @Override + public void clicked(Player player, ClickType clickType) { + final Party party = Practice.getInstance().getPartyManager().getParty(player.getUniqueId()); + + if (party == null || this.queue == null || !Practice.getInstance().getPartyManager().isLeader(player.getUniqueId())) { + player.sendMessage(CC.RED + "Something went terribly wrong while performing this action."); + return; + } + + player.closeInventory(); + + if (party.getMembers().size() < 2) { + player.sendMessage(CC.RED + "You need at least 2 players to start a party split event!"); + } else { + new PartyFFAArenaMenu(this.queue).openMenu(player); + } + } + } +} diff --git a/src/main/java/com/solexgames/practice/menu/party/events/PartySplitMenu.java b/src/main/java/com/solexgames/practice/menu/party/events/PartySplitMenu.java new file mode 100644 index 0000000..713504a --- /dev/null +++ b/src/main/java/com/solexgames/practice/menu/party/events/PartySplitMenu.java @@ -0,0 +1,90 @@ +package com.solexgames.practice.menu.party.events; + +import com.solexgames.core.util.Color; +import com.solexgames.core.util.builder.ItemBuilder; +import com.solexgames.core.util.external.Button; +import com.solexgames.core.util.external.Menu; +import com.solexgames.practice.Practice; +import com.solexgames.practice.kit.Kit; +import com.solexgames.practice.menu.party.events.arena.PartyFFAArenaMenu; +import com.solexgames.practice.menu.party.events.arena.PartySplitArenaMenu; +import com.solexgames.practice.party.Party; +import com.solexgames.practice.player.PlayerData; +import com.solexgames.practice.util.CC; +import lombok.RequiredArgsConstructor; +import org.bukkit.entity.Player; +import org.bukkit.event.inventory.ClickType; +import org.bukkit.inventory.ItemFlag; +import org.bukkit.inventory.ItemStack; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * @author GrowlyX + * @since 5/22/2021 + */ + +public class PartySplitMenu extends Menu { + + @Override + public String getTitle(Player player) { + return "Select a Kit"; + } + + @Override + public Map getButtons(Player player) { + final Map buttonMap = new HashMap<>(); + final AtomicInteger atomicInteger = new AtomicInteger(); + final PlayerData data = Practice.getInstance().getPlayerManager().getPlayerData(player.getUniqueId()); + + Practice.getInstance().getKitManager().getKits().stream() + .filter(Kit::isEnabled) + .forEach(kit -> buttonMap.put(atomicInteger.getAndIncrement(), new KitButton(kit, data))); + + return buttonMap; + } + + @RequiredArgsConstructor + private static class KitButton extends Button { + + private final Kit queue; + private final PlayerData data; + + @Override + public ItemStack getButtonItem(final Player player) { + final List lore = new ArrayList<>(); + + lore.add(CC.GRAY + "Elo: " + CC.WHITE + this.data.getElo(this.queue.getName())); + lore.add(" "); + lore.add(CC.YELLOW + "[Click to play with this kit]"); + + return new ItemBuilder(this.queue.getIcon().clone()) + .setDisplayName(Color.MAIN_COLOR + CC.BOLD + this.queue.getName()) + .addLore(lore) + .addItemFlags(ItemFlag.HIDE_UNBREAKABLE, ItemFlag.HIDE_ATTRIBUTES, ItemFlag.HIDE_POTION_EFFECTS, ItemFlag.HIDE_ENCHANTS) + .create(); + } + + @Override + public void clicked(Player player, ClickType clickType) { + final Party party = Practice.getInstance().getPartyManager().getParty(player.getUniqueId()); + + if (party == null || this.queue == null || !Practice.getInstance().getPartyManager().isLeader(player.getUniqueId())) { + player.sendMessage(CC.RED + "Something went terribly wrong while performing this action."); + return; + } + + player.closeInventory(); + + if (party.getMembers().size() < 2) { + player.sendMessage(CC.RED + "You need at least 2 players to start a party split event!"); + } else { + new PartySplitArenaMenu(this.queue).openMenu(player); + } + } + } +} diff --git a/src/main/java/com/solexgames/practice/menu/party/events/arena/PartyFFAArenaMenu.java b/src/main/java/com/solexgames/practice/menu/party/events/arena/PartyFFAArenaMenu.java new file mode 100644 index 0000000..058dfe5 --- /dev/null +++ b/src/main/java/com/solexgames/practice/menu/party/events/arena/PartyFFAArenaMenu.java @@ -0,0 +1,101 @@ +package com.solexgames.practice.menu.party.events.arena; + +import com.google.common.collect.Lists; +import com.solexgames.core.util.Color; +import com.solexgames.core.util.builder.ItemBuilder; +import com.solexgames.core.util.external.Button; +import com.solexgames.core.util.external.Menu; +import com.solexgames.practice.Practice; +import com.solexgames.practice.arena.Arena; +import com.solexgames.practice.kit.Kit; +import com.solexgames.practice.match.Match; +import com.solexgames.practice.match.team.impl.MatchTeam; +import com.solexgames.practice.party.Party; +import com.solexgames.practice.queue.QueueType; +import com.solexgames.practice.util.CC; +import lombok.RequiredArgsConstructor; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.event.inventory.ClickType; +import org.bukkit.inventory.ItemFlag; +import org.bukkit.inventory.ItemStack; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * @author GrowlyX + * @since 5/22/2021 + */ + +@RequiredArgsConstructor +public class PartyFFAArenaMenu extends Menu { + + private final Kit kit; + + @Override + public String getTitle(Player player) { + return "Select an Arena"; + } + + @Override + public Map getButtons(Player player) { + final Map buttonMap = new HashMap<>(); + final AtomicInteger atomicInteger = new AtomicInteger(); + + Practice.getInstance().getArenaManager().getArenas().values().stream() + .filter(arena -> Practice.getInstance().isKitPairedWithArena(this.kit, arena)) + .forEach(arena -> { + buttonMap.put(atomicInteger.getAndIncrement(), new ArenaButton(arena)); + }); + + return buttonMap; + } + + @RequiredArgsConstructor + private class ArenaButton extends Button { + + private final Arena arena; + + @Override + public ItemStack getButtonItem(final Player player) { + final List lore = new ArrayList<>(); + + lore.add(" "); + lore.add(CC.YELLOW + "[Click to play with this arena]"); + + return new ItemBuilder(this.arena.getIcon() == null ? Material.PAPER : this.arena.getIcon()) + .setDisplayName(Color.MAIN_COLOR + CC.BOLD + this.arena.getName()) + .addLore(lore) + .addItemFlags(ItemFlag.HIDE_UNBREAKABLE, ItemFlag.HIDE_ATTRIBUTES, ItemFlag.HIDE_POTION_EFFECTS, ItemFlag.HIDE_ENCHANTS) + .create(); + } + + @Override + public void clicked(Player player, ClickType clickType) { + final Party party = Practice.getInstance().getPartyManager().getParty(player.getUniqueId()); + + if (party == null || !Practice.getInstance().getPartyManager().isLeader(player.getUniqueId())) { + player.sendMessage(CC.RED + "Something went terribly wrong while performing this action."); + return; + } + + player.closeInventory(); + + if (party.getMembers().size() < 2) { + player.sendMessage(CC.RED + "You need at least 2 players to start a party split event!"); + return; + } + + final MatchTeam team = new MatchTeam(party.getLeader(), Lists.newArrayList(party.getMembers()), 0); + final Match match = new Match(this.arena, kit, QueueType.UNRANKED, team); + + match.broadcast(CC.YELLOW + "Starting a new party " + CC.AQUA + "FFA" + CC.YELLOW + " match with the ladder " + CC.AQUA + kit.getName() + CC.YELLOW + "."); + + Practice.getInstance().getMatchManager().createMatch(match, true); + } + } +} diff --git a/src/main/java/com/solexgames/practice/menu/party/events/arena/PartySplitArenaMenu.java b/src/main/java/com/solexgames/practice/menu/party/events/arena/PartySplitArenaMenu.java new file mode 100644 index 0000000..d3da172 --- /dev/null +++ b/src/main/java/com/solexgames/practice/menu/party/events/arena/PartySplitArenaMenu.java @@ -0,0 +1,100 @@ +package com.solexgames.practice.menu.party.events.arena; + +import com.solexgames.core.util.Color; +import com.solexgames.core.util.builder.ItemBuilder; +import com.solexgames.core.util.external.Button; +import com.solexgames.core.util.external.Menu; +import com.solexgames.practice.Practice; +import com.solexgames.practice.arena.Arena; +import com.solexgames.practice.kit.Kit; +import com.solexgames.practice.match.Match; +import com.solexgames.practice.match.team.impl.MatchTeam; +import com.solexgames.practice.party.Party; +import com.solexgames.practice.queue.QueueType; +import com.solexgames.practice.util.CC; +import lombok.RequiredArgsConstructor; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.event.inventory.ClickType; +import org.bukkit.inventory.ItemFlag; +import org.bukkit.inventory.ItemStack; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * @author GrowlyX + * @since 5/22/2021 + */ + +@RequiredArgsConstructor +public class PartySplitArenaMenu extends Menu { + + private final Kit kit; + + @Override + public String getTitle(Player player) { + return "Select an Arena"; + } + + @Override + public Map getButtons(Player player) { + final Map buttonMap = new HashMap<>(); + final AtomicInteger atomicInteger = new AtomicInteger(); + + Practice.getInstance().getArenaManager().getArenas().values().stream() + .filter(arena -> Practice.getInstance().isKitPairedWithArena(this.kit, arena)) + .forEach(arena -> { + buttonMap.put(atomicInteger.getAndIncrement(), new ArenaButton(arena)); + }); + + return buttonMap; + } + + @RequiredArgsConstructor + private class ArenaButton extends Button { + + private final Arena arena; + + @Override + public ItemStack getButtonItem(final Player player) { + final List lore = new ArrayList<>(); + + lore.add(" "); + lore.add(CC.YELLOW + "[Click to play with this arena]"); + + return new ItemBuilder(this.arena.getIcon() == null ? Material.PAPER : this.arena.getIcon()) + .setDisplayName(Color.MAIN_COLOR + CC.BOLD + this.arena.getName()) + .addLore(lore) + .addItemFlags(ItemFlag.HIDE_UNBREAKABLE, ItemFlag.HIDE_ATTRIBUTES, ItemFlag.HIDE_POTION_EFFECTS, ItemFlag.HIDE_ENCHANTS) + .create(); + } + + @Override + public void clicked(Player player, ClickType clickType) { + final Party party = Practice.getInstance().getPartyManager().getParty(player.getUniqueId()); + + if (party == null || !Practice.getInstance().getPartyManager().isLeader(player.getUniqueId())) { + player.sendMessage(CC.RED + "Something went terribly wrong while performing this action."); + return; + } + + player.closeInventory(); + + if (party.getMembers().size() < 2) { + player.sendMessage(CC.RED + "You need at least 2 players to start a party split event!"); + return; + } + + final MatchTeam[] teams = party.split(); + final Match match = new Match(this.arena, kit, QueueType.UNRANKED, teams); + + match.broadcast(CC.YELLOW + "Starting a new party " + CC.AQUA + "Split " + CC.GRAY + "(" + teams[0].getPlayers().size() + "v" + teams[1].getPlayers().size() + ")" + CC.YELLOW + " match with the ladder " + CC.AQUA + kit.getName() + CC.YELLOW + "."); + + Practice.getInstance().getMatchManager().createMatch(match, true); + } + } +} diff --git a/src/main/java/com/solexgames/practice/message/KillMessage.java b/src/main/java/com/solexgames/practice/message/KillMessage.java new file mode 100644 index 0000000..3319a54 --- /dev/null +++ b/src/main/java/com/solexgames/practice/message/KillMessage.java @@ -0,0 +1,40 @@ +package com.solexgames.practice.message; + +import com.solexgames.practice.util.CC; +import org.bukkit.entity.Player; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author GrowlyX + * @since 8/4/2021 + */ + +public interface KillMessage { + + String getName(); + + String getFormatted(Player player, Player other, boolean otherPlayerExists); + + List getMessages(); + + List getDescription(); + + default List getFormattedLore() { + final List stringList = new ArrayList<>(this.getDescription()); + + stringList.add(" "); + + this.getMessages().forEach(message -> { + stringList.add(CC.GRAY + "... was " + CC.YELLOW + message + CC.GRAY + " by ..."); + }); + + stringList.add(" "); + stringList.add(CC.WHITE + CC.ITALIC + "One of these messages will"); + stringList.add(CC.WHITE + CC.ITALIC + "appear when you kill someone."); + + return stringList; + } + +} diff --git a/src/main/java/com/solexgames/practice/message/impl/DefaultDeathMessage.java b/src/main/java/com/solexgames/practice/message/impl/DefaultDeathMessage.java new file mode 100644 index 0000000..f4b6527 --- /dev/null +++ b/src/main/java/com/solexgames/practice/message/impl/DefaultDeathMessage.java @@ -0,0 +1,42 @@ +package com.solexgames.practice.message.impl; + +import com.solexgames.practice.message.KillMessage; +import com.solexgames.practice.util.CC; +import org.bukkit.entity.Player; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +/** + * @author GrowlyX + * @since 8/4/2021 + */ + +public class DefaultDeathMessage implements KillMessage { + + @Override + public String getName() { + return "Normal"; + } + + @Override + public String getFormatted(Player player, Player other, boolean otherPlayerExists) { + if (!otherPlayerExists) { + return CC.RED + player.getName() + CC.GRAY + " was killed."; + } + + return CC.RED + player.getName() + CC.GRAY + " was killed by " + CC.GREEN + other.getName() + CC.GRAY + "."; + } + + @Override + public List getMessages() { + return Collections.singletonList("killed"); + } + + @Override + public List getDescription() { + return new ArrayList<>(); + } +} diff --git a/src/main/java/com/solexgames/practice/message/impl/GamerKillMessages.java b/src/main/java/com/solexgames/practice/message/impl/GamerKillMessages.java new file mode 100644 index 0000000..ae211b5 --- /dev/null +++ b/src/main/java/com/solexgames/practice/message/impl/GamerKillMessages.java @@ -0,0 +1,49 @@ +package com.solexgames.practice.message.impl; + +import com.solexgames.practice.message.KillMessage; +import com.solexgames.practice.util.CC; +import org.bukkit.entity.Player; + +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.ThreadLocalRandom; + +/** + * @author GrowlyX + * @since 8/4/2021 + */ + +public class GamerKillMessages implements KillMessage { + + @Override + public String getName() { + return "Gamer"; + } + + @Override + public String getFormatted(Player player, Player other, boolean otherPlayerExists) { + final String randomMessage = CC.YELLOW + this.getMessages().get(ThreadLocalRandom.current().nextInt(this.getMessages().size())) + CC.GRAY; + + if (!otherPlayerExists) { + return CC.RED + player.getName() + CC.GRAY + " was " + randomMessage + "."; + } + + return CC.RED + player.getName() + CC.GRAY + " was " + randomMessage + " by " + CC.GREEN + other.getName() + CC.GRAY + "."; + } + + @Override + public List getMessages() { + return Arrays.asList( + "destroyed", "rolled", + "shoved into a locker", "clapped" + ); + } + + @Override + public List getDescription() { + return Arrays.asList( + CC.GRAY + "Trigger your opponents", + CC.GRAY + "using slightly toxic phrases." + ); + } +} diff --git a/src/main/java/com/solexgames/practice/message/impl/KitchenKillMessages.java b/src/main/java/com/solexgames/practice/message/impl/KitchenKillMessages.java new file mode 100644 index 0000000..bf8535e --- /dev/null +++ b/src/main/java/com/solexgames/practice/message/impl/KitchenKillMessages.java @@ -0,0 +1,49 @@ +package com.solexgames.practice.message.impl; + +import com.solexgames.practice.message.KillMessage; +import com.solexgames.practice.util.CC; +import org.bukkit.entity.Player; + +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.ThreadLocalRandom; + +/** + * @author GrowlyX + * @since 8/4/2021 + */ + +public class KitchenKillMessages implements KillMessage { + + @Override + public String getName() { + return "Kitchen"; + } + + @Override + public String getFormatted(Player player, Player other, boolean otherPlayerExists) { + final String randomMessage = CC.YELLOW + this.getMessages().get(ThreadLocalRandom.current().nextInt(this.getMessages().size())) + CC.GRAY; + + if (!otherPlayerExists) { + return CC.RED + player.getName() + CC.GRAY + " was " + randomMessage + "."; + } + + return CC.RED + player.getName() + CC.GRAY + " was " + randomMessage + " by " + CC.GREEN + other.getName() + CC.GRAY + "."; + } + + @Override + public List getMessages() { + return Arrays.asList( + "slow roasted", "marinated", + "steamed", "deep fried" + ); + } + + @Override + public List getDescription() { + return Arrays.asList( + CC.GRAY + "Common phrases and terms", + CC.GRAY + "used in the kitchen." + ); + } +} diff --git a/src/main/java/com/solexgames/practice/message/impl/NerdyKillMessages.java b/src/main/java/com/solexgames/practice/message/impl/NerdyKillMessages.java new file mode 100644 index 0000000..45deaeb --- /dev/null +++ b/src/main/java/com/solexgames/practice/message/impl/NerdyKillMessages.java @@ -0,0 +1,54 @@ +package com.solexgames.practice.message.impl; + +import com.solexgames.practice.message.KillMessage; +import com.solexgames.practice.util.CC; +import org.bukkit.entity.Player; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.ThreadLocalRandom; + +/** + * @author GrowlyX + * @since 8/4/2021 + */ + +public class NerdyKillMessages implements KillMessage { + + @Override + public String getName() { + return "Nerdy"; + } + + @Override + public String getFormatted(Player player, Player other, boolean otherPlayerExists) { + final String randomMessage = CC.YELLOW + this.getMessages().get(ThreadLocalRandom.current().nextInt(this.getMessages().size())) + CC.GRAY; + + if (!otherPlayerExists) { + return CC.RED + player.getName() + CC.GRAY + " was " + randomMessage + "."; + } + + return CC.RED + player.getName() + CC.GRAY + " was " + randomMessage + " by " + CC.GREEN + other.getName() + CC.GRAY + "."; + } + + @Override + public List getMessages() { + return Arrays.asList( + "ALT+F4'd", + "deleted", + "crashed", + "ratted", + "hacked", + "over-heated" + ); + } + + @Override + public List getDescription() { + return Arrays.asList( + CC.GRAY + "Nerdy computer terms", + CC.GRAY + "to trigger your opponents." + ); + } +} diff --git a/src/main/java/com/solexgames/practice/party/Party.java b/src/main/java/com/solexgames/practice/party/Party.java new file mode 100644 index 0000000..c474cbf --- /dev/null +++ b/src/main/java/com/solexgames/practice/party/Party.java @@ -0,0 +1,73 @@ +package com.solexgames.practice.party; + +import com.solexgames.practice.Practice; +import com.solexgames.practice.match.team.impl.MatchTeam; +import lombok.Getter; +import lombok.Setter; +import org.bukkit.entity.Player; + +import java.util.*; +import java.util.stream.Stream; + +@Getter +@Setter +public class Party { + + private final Practice plugin = Practice.getInstance(); + + private final UUID leader; + private final Set members = new HashSet<>(); + + private int limit = 50; + private boolean open; + + public Party(UUID leader) { + this.leader = leader; + this.members.add(leader); + } + + public void addMember(UUID uuid) { + this.members.add(uuid); + } + + public void removeMember(UUID uuid) { + this.members.remove(uuid); + } + + public void broadcast(String message) { + this.streamPartyMembers() + .forEach(member -> member.sendMessage(message)); + } + + public void broadcast(String... message) { + this.streamPartyMembers() + .forEach(member -> member.sendMessage(message)); + } + + public MatchTeam[] split() { + final List teamA = new ArrayList<>(); + final List teamB = new ArrayList<>(); + + final List shuffled = new ArrayList<>(this.members); + Collections.shuffle(shuffled); + + for (final UUID member : shuffled) { + if (teamA.size() == teamB.size()) { + teamA.add(member); + } else { + teamB.add(member); + } + } + + return new MatchTeam[]{ + new MatchTeam(teamA.get(0), teamA, 0), + new MatchTeam(teamB.get(0), teamB, 1) + }; + } + + public Stream streamPartyMembers() { + return this.members.stream() + .map(this.plugin.getServer()::getPlayer) + .filter(Objects::nonNull); + } +} diff --git a/src/main/java/com/solexgames/practice/player/PlayerData.java b/src/main/java/com/solexgames/practice/player/PlayerData.java new file mode 100644 index 0000000..33148a8 --- /dev/null +++ b/src/main/java/com/solexgames/practice/player/PlayerData.java @@ -0,0 +1,244 @@ +package com.solexgames.practice.player; + +import com.solexgames.practice.Practice; +import com.solexgames.practice.PracticeConstants; +import com.solexgames.practice.arena.Arena; +import com.solexgames.practice.completion.AbstractDivisionPrizeHandler; +import com.solexgames.practice.util.DivisionPrizes; +import com.solexgames.practice.effects.DeathEffect; +import com.solexgames.practice.effects.DeathEffects; +import com.solexgames.practice.kit.Kit; +import com.solexgames.practice.kit.PlayerKit; +import com.solexgames.practice.settings.ProfileOptions; +import com.solexgames.practice.util.CC; +import com.solexgames.practice.util.division.RankedDivision; +import io.papermc.lib.PaperLib; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.Setter; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.checkerframework.checker.units.qual.K; + +import java.util.*; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; + +@Getter +@Setter +@RequiredArgsConstructor +public class PlayerData { + + private final Map> playerKits = new HashMap<>(); + + private final Map rankedLosses = new HashMap<>(); + private final Map rankedWins = new HashMap<>(); + private final Map rankedElo = new HashMap<>(); + private final Map highestKillStreaks = new HashMap<>(); + private final Map kitKillStreaks = new HashMap<>(); + + private final Map rankedDivisionBooleanMap = new HashMap<>(); + + private final List matchIds = new ArrayList<>(); + + private final UUID uniqueId; + private final String name; + + private PlayerState playerState = PlayerState.LOADING; + + private UUID currentMatchID; + private UUID duelSelecting; + + private ProfileOptions options = new ProfileOptions(); + + private String deathMessageType = "Normal"; + + private int eloRange = -1; + private int pingRange = -1; + private int teamID = -1; + private int rematchID = -1; + private int missedPots; + private int longestCombo; + private int combo; + private int hits; + private int globalElo = 750; + private int unrankedWinCount; + private int rankedWinCount; + private int unrankedLossCount; + private int rankedLossCount; + + private long lastAnnouncement; + + private Player following; + private Arena lastPlayedArena; + + private RankedDivision rankedDivision = RankedDivision.UNRATED; + private DeathEffect effect = DeathEffects.LIGHTNING; + + { + for (final RankedDivision value : RankedDivision.values()) { + this.rankedDivisionBooleanMap.putIfAbsent(value.name(), false); + } + } + + public void setPlayerState(PlayerState playerState) { + this.playerState = playerState; + + if (this.getBukkitPlayer() != null) { + Practice.getInstance().getFollowManager().getPlayerWhoIsFollowingPlayer(this.getBukkitPlayer()).forEach(follower -> { + if (follower != null) { + switch (playerState) { + case SPAWN: + follower.getBukkitPlayer().getInventory().setContents(Practice.getInstance().getItemManager().getSpecItems()); + follower.getBukkitPlayer().updateInventory(); + + Practice.getInstance().getEntityHider().showEntity(follower.getBukkitPlayer(), this.getBukkitPlayer()); + PaperLib.teleportAsync(follower.getBukkitPlayer(), this.getBukkitPlayer().getLocation()); + break; + case FIGHTING: + Practice.getInstance().getMatchManager().addSpectator(follower.getBukkitPlayer(), follower, this.getBukkitPlayer(), Practice.getInstance().getMatchManager().getMatch(this)); + break; + } + } + }); + } + } + + public Player getBukkitPlayer() { + return Bukkit.getPlayer(this.uniqueId); + } + + public boolean canPlayRanked() { + return this.unrankedWinCount >= 10; + } + + public int winsUntilRankedUnlocked() { + if (this.canPlayRanked()) { + return 0; + } + + return 10 - this.unrankedWinCount; + } + + public int getGlobalWins() { + final AtomicInteger integer = new AtomicInteger(); + final List kits = new ArrayList<>(Practice.getInstance().getKitManager().getKits()); + + kits.forEach(kit -> { + integer.addAndGet(this.getWins(kit.getName())); + }); + + return integer.get(); + } + + public int getGlobalLosses() { + final AtomicInteger integer = new AtomicInteger(); + final List kits = new ArrayList<>(Practice.getInstance().getKitManager().getKits()); + + kits.forEach(kit -> { + integer.addAndGet(this.getLosses(kit.getName())); + }); + + return integer.get(); + } + + public RankedDivision getDivision() { + return RankedDivision.getByGlobalElo(this.globalElo); + } + + public int recalculateGlobalElo() { + try { + final AtomicInteger integer = new AtomicInteger(); + final List rankedKits = Practice.getInstance().getKitManager().getKits() + .stream().filter(Kit::isRanked).collect(Collectors.toList()); + + rankedKits.forEach(kit -> integer.addAndGet(this.getElo(kit.getName()))); + + return this.globalElo = (integer.get() / rankedKits.size()); + } catch (Exception ex) { + return this.globalElo = 750; + } + } + + public void setKillstreak(Kit kit, int killstreak) { + setKillstreak(kit.getName(), killstreak); + } + + public void setKillstreak(String name, int killstreak) { + kitKillStreaks.put(name, killstreak); + } + + public int getKillstreak(Kit kit) { + return this.kitKillStreaks.getOrDefault(kit.getName(), 0); + } + + public int getWins(String kitName) { + return this.rankedWins.computeIfAbsent(kitName, k -> 0); + } + + public void setWins(String kitName, int wins) { + this.rankedWins.put(kitName, wins); + } + + public int getLosses(String kitName) { + return this.rankedLosses.computeIfAbsent(kitName, k -> 0); + } + + public int getHighestKillStreak(String kitName) { + return this.highestKillStreaks.computeIfAbsent(kitName, k -> 0); + } + + public void setHighestKillStreak(String kitName, Integer killStreak) { + this.highestKillStreaks.put(kitName, killStreak); + } + + public void setLosses(String kitName, int losses) { + this.rankedLosses.put(kitName, losses); + } + + public int getElo(String kitName) { + return this.rankedElo.computeIfAbsent(kitName, k -> PracticeConstants.DEFAULT_ELO); + } + + public void setElo(String kitName, int elo) { + this.rankedElo.put(kitName, elo); + } + + public void addPlayerKit(int index, PlayerKit playerKit) { + this.getPlayerKits(playerKit.getName()).put(index, playerKit); + } + + public Map getPlayerKits(String kitName) { + return this.playerKits.computeIfAbsent(kitName, k -> new HashMap<>()); + } + + public void recalculateDivision() { + final RankedDivision currentDivision = this.rankedDivision; + final RankedDivision nextDivision = this.rankedDivision = RankedDivision.getByGlobalElo(this.globalElo); + + if (nextDivision == null || currentDivision.isEqualTo(nextDivision)) { + return; + } + + if (nextDivision.isLowerThan(currentDivision)) { + this.getBukkitPlayer().sendMessage(CC.YELLOW + "You've de-ranked from " + currentDivision.getFancyName() + CC.YELLOW + " to " + CC.AQUA + nextDivision.getFancyName() + CC.YELLOW + "."); + } else if (nextDivision.isHigherThan(currentDivision)) { + if (!this.rankedDivisionBooleanMap.get(nextDivision.name()) && !nextDivision.equals(RankedDivision.UNRATED)) { + final AbstractDivisionPrizeHandler prizeHandler = DivisionPrizes.REACHED_PRIZE_MAP.get(nextDivision); + + if (prizeHandler != null) { + prizeHandler.getPrize().accept(this.getBukkitPlayer(), this); + + this.getBukkitPlayer().sendMessage(new String[]{ + CC.YELLOW + "You've ranked up from " + currentDivision.getFancyName() + CC.YELLOW + " to " + CC.AQUA + nextDivision.getFancyName() + CC.YELLOW + "!", + CC.GREEN + "You've also been given your respective division's rewards." + }); + + this.rankedDivisionBooleanMap.put(nextDivision.name(), true); + } + } else { + this.getBukkitPlayer().sendMessage(CC.YELLOW + "You've ranked up from " + currentDivision.getFancyName() + CC.YELLOW + " to " + CC.AQUA + nextDivision.getFancyName() + CC.YELLOW + "!"); + } + } + } +} diff --git a/src/main/java/com/solexgames/practice/player/PlayerState.java b/src/main/java/com/solexgames/practice/player/PlayerState.java new file mode 100644 index 0000000..18994ea --- /dev/null +++ b/src/main/java/com/solexgames/practice/player/PlayerState.java @@ -0,0 +1,14 @@ +package com.solexgames.practice.player; + +public enum PlayerState { + + LOADING, + SPAWN, + EDITING, + SPECTATING, + QUEUE, + FIGHTING, + FFA, + EVENT + +} diff --git a/src/main/java/com/solexgames/practice/queue/QueueEntry.java b/src/main/java/com/solexgames/practice/queue/QueueEntry.java new file mode 100644 index 0000000..ad9a17e --- /dev/null +++ b/src/main/java/com/solexgames/practice/queue/QueueEntry.java @@ -0,0 +1,18 @@ +package com.solexgames.practice.queue; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +public class QueueEntry { + + private final QueueType queueType; + private final String kitName; + + private final int elo; + private final int ping; + + private final boolean party; + +} diff --git a/src/main/java/com/solexgames/practice/queue/QueueType.java b/src/main/java/com/solexgames/practice/queue/QueueType.java new file mode 100644 index 0000000..1d785c9 --- /dev/null +++ b/src/main/java/com/solexgames/practice/queue/QueueType.java @@ -0,0 +1,17 @@ +package com.solexgames.practice.queue; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +public enum QueueType { + + UNRANKED("Unranked", false), + PREMIUM("Premium", false), + RANKED("Ranked", true); + + private final String name; + private final boolean ranked; + +} diff --git a/src/main/java/com/solexgames/practice/runnable/ArenaCommandRunnable.java b/src/main/java/com/solexgames/practice/runnable/ArenaCommandRunnable.java new file mode 100644 index 0000000..0828fcd --- /dev/null +++ b/src/main/java/com/solexgames/practice/runnable/ArenaCommandRunnable.java @@ -0,0 +1,68 @@ +package com.solexgames.practice.runnable; + +import com.solexgames.practice.Practice; +import com.solexgames.practice.arena.Arena; +import com.solexgames.practice.arena.type.StandaloneArena; +import com.solexgames.practice.location.impl.impl.AsyncLocation; +import com.solexgames.practice.util.old.DeprecatedLocation; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.ArrayList; + +@Getter +@AllArgsConstructor +public class ArenaCommandRunnable implements Runnable { + + private final Practice plugin; + private final Arena copiedArena; + + private int times; + + @Override + public void run() { + this.duplicateArena(this.copiedArena, 10000, 10000); + } + + private void duplicateArena(Arena arena, int offsetX, int offsetZ) { + new DuplicateArenaRunnable(this.plugin, arena, offsetX, offsetZ, 500, 500) { + @Override + public void onComplete() { + final double minX = arena.getMin().getX() + this.getOffsetX(); + final double minZ = arena.getMin().getZ() + this.getOffsetZ(); + final double maxX = arena.getMax().getX() + this.getOffsetX(); + final double maxZ = arena.getMax().getZ() + this.getOffsetZ(); + + final double aX = arena.getPositionOne().getX() + this.getOffsetX(); + final double aZ = arena.getPositionOne().getZ() + this.getOffsetZ(); + final double bX = arena.getPositionTwo().getX() + this.getOffsetX(); + final double bZ = arena.getPositionTwo().getZ() + this.getOffsetZ(); + + final AsyncLocation min = AsyncLocation.of(new DeprecatedLocation(minX, arena.getMin().getY(), minZ, arena.getMin().getYaw(), arena.getMin().getPitch()).toBukkitLocation()); + final AsyncLocation max = AsyncLocation.of(new DeprecatedLocation(maxX, arena.getMax().getY(), maxZ, arena.getMax().getYaw(), arena.getMax().getPitch()).toBukkitLocation()); + final AsyncLocation a = AsyncLocation.of(new DeprecatedLocation(aX, arena.getPositionOne().getY(), aZ, arena.getPositionOne().getYaw(), arena.getPositionOne().getPitch()).toBukkitLocation()); + final AsyncLocation b = AsyncLocation.of(new DeprecatedLocation(bX, arena.getPositionTwo().getY(), bZ, arena.getPositionOne().getYaw(), arena.getPositionOne().getPitch()).toBukkitLocation()); + + final StandaloneArena standaloneArena = new StandaloneArena(a, b, min, max); + + if (arena.getStandaloneArenas() == null) { + arena.setStandaloneArenas(new ArrayList<>()); + } + + arena.addStandaloneArena(standaloneArena); + arena.addAvailableArena(standaloneArena); + + if (--ArenaCommandRunnable.this.times > 0) { + ArenaCommandRunnable.this.plugin.getServer().getLogger().info("Placed a standalone arena of " + arena.getName() + " at " + (int) minX + ", " + (int) minZ + + ". " + ArenaCommandRunnable.this.times + " arenas remaining."); + ArenaCommandRunnable.this.duplicateArena(arena, (int) Math.round(maxX), (int) Math.round(maxZ)); + } else { + ArenaCommandRunnable.this.plugin.getServer().getLogger().info("Finished pasting " + ArenaCommandRunnable.this.copiedArena.getName() + "'s standalone arenas."); + ArenaCommandRunnable.this.plugin.getArenaManager().setGeneratingArenaTask(ArenaCommandRunnable.this.plugin.getArenaManager().getGeneratingArenaTask() - 1); + + this.getPlugin().getArenaManager().reloadArenas(); + } + } + }.run(); + } +} diff --git a/src/main/java/com/solexgames/practice/runnable/AttackableRunnable.java b/src/main/java/com/solexgames/practice/runnable/AttackableRunnable.java new file mode 100644 index 0000000..463e3f2 --- /dev/null +++ b/src/main/java/com/solexgames/practice/runnable/AttackableRunnable.java @@ -0,0 +1,49 @@ +package com.solexgames.practice.runnable; + +import com.solexgames.practice.Practice; +import org.bukkit.entity.Player; +import org.bukkit.metadata.FixedMetadataValue; +import org.bukkit.scheduler.BukkitRunnable; + +/** + * @author GrowlyX + * @since 5/25/2021 + */ + +public class AttackableRunnable extends BukkitRunnable { + + private final Player player; + + private int ticks; + + public AttackableRunnable(Player player) { + this.player = player; + this.ticks = 2; + + if (!this.player.hasMetadata("non-attackable")) { + this.player.setMetadata("non-attackable", new FixedMetadataValue(Practice.getInstance(), true)); + } + + this.runTaskTimer(Practice.getInstance(), 20L, 20L); + } + + @Override + public void run() { + this.ticks--; + + if (this.player == null) { + this.cancel(); + return; + } + + switch (this.ticks) { + case 1: + break; + case 0: + if (this.player.hasMetadata("non-attackable")) { + this.player.removeMetadata("non-attackable", Practice.getInstance()); + } + break; + } + } +} diff --git a/src/main/java/com/solexgames/practice/runnable/BlockPlaceRunnable.java b/src/main/java/com/solexgames/practice/runnable/BlockPlaceRunnable.java new file mode 100644 index 0000000..bd7eab0 --- /dev/null +++ b/src/main/java/com/solexgames/practice/runnable/BlockPlaceRunnable.java @@ -0,0 +1,71 @@ +package com.solexgames.practice.runnable; + +import com.boydti.fawe.util.EditSessionBuilder; +import com.boydti.fawe.util.TaskManager; +import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.blocks.BaseBlock; +import lombok.Getter; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.scheduler.BukkitRunnable; + +import java.util.Iterator; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +@Getter +public abstract class BlockPlaceRunnable extends BukkitRunnable { + + private final ConcurrentMap blocks = new ConcurrentHashMap<>(); + + private final World world; + private final Iterator iterator; + + private final int totalBlocks; + private final int blockIndex = 0; + private final int blocksPlaced = 0; + + private boolean completed = false; + + public BlockPlaceRunnable(World world, Map blocks) { + this.world = world; + this.blocks.putAll(blocks); + + this.totalBlocks = blocks.keySet().size(); + this.iterator = blocks.keySet().iterator(); + } + + @Override + public void run() { + if (this.blocks.isEmpty() || !this.iterator.hasNext()) { + this.finish(); + + this.completed = true; + + this.cancel(); + return; + } + + CompletableFuture.runAsync(() -> { + final EditSession editSession = new EditSessionBuilder(this.world.getName()).fastmode(true).allowedRegionsEverywhere().autoQueue(false).limitUnlimited().build(); + + for (Map.Entry entry : this.blocks.entrySet()) { + try { + editSession.setBlock(new Vector(entry.getKey().getBlockX(), entry.getKey().getBlockY(), entry.getKey().getZ()), new BaseBlock(entry.getValue().getTypeId(), entry.getValue().getData())); + } catch (Exception ignored) { + } + } + + editSession.flushQueue(); + + TaskManager.IMP.task(this.blocks::clear); + }); + } + + public abstract void finish(); + +} diff --git a/src/main/java/com/solexgames/practice/runnable/BlockRemoveRunnable.java b/src/main/java/com/solexgames/practice/runnable/BlockRemoveRunnable.java new file mode 100644 index 0000000..87c3779 --- /dev/null +++ b/src/main/java/com/solexgames/practice/runnable/BlockRemoveRunnable.java @@ -0,0 +1,41 @@ +package com.solexgames.practice.runnable; + +import com.solexgames.practice.Practice; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.scheduler.BukkitRunnable; + +/** + * @author GrowlyX + * @since 5/25/2021 + */ + +public class BlockRemoveRunnable extends BukkitRunnable { + + private final Location location; + + private int ticks; + + public BlockRemoveRunnable(Location location) { + this.location = location; + this.ticks = 14; + + this.runTaskTimer(Practice.getInstance(), 20L, 20L); + } + + @Override + public void run() { + this.ticks--; + + if (this.location == null) { + this.cancel(); + return; + } + + if (this.ticks == 0) { + if (this.location.getBlock() != null) { + this.location.getBlock().setType(Material.AIR); + } + } + } +} diff --git a/src/main/java/com/solexgames/practice/runnable/DuplicateArenaRunnable.java b/src/main/java/com/solexgames/practice/runnable/DuplicateArenaRunnable.java new file mode 100644 index 0000000..6eb296b --- /dev/null +++ b/src/main/java/com/solexgames/practice/runnable/DuplicateArenaRunnable.java @@ -0,0 +1,121 @@ +package com.solexgames.practice.runnable; + +import com.solexgames.practice.Practice; +import com.solexgames.practice.arena.Arena; +import lombok.Getter; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.scheduler.BukkitRunnable; + +import java.util.HashMap; +import java.util.Map; + +/** + * @since 11/25/2017 + */ +@Getter +public abstract class DuplicateArenaRunnable extends BukkitRunnable { + + private final Practice plugin; + private final Arena copiedArena; + + private final int incrementX; + private final int incrementZ; + + private int offsetX; + private int offsetZ; + + private Map paste; + + public DuplicateArenaRunnable(Practice plugin, Arena copiedArena, int offsetX, int offsetZ, int incrementX, int incrementZ) { + this.plugin = plugin; + this.copiedArena = copiedArena; + this.offsetX = offsetX; + this.offsetZ = offsetZ; + this.incrementX = incrementX; + this.incrementZ = incrementZ; + } + + @Override + public void run() { + if (this.paste == null) { + final Map copy = this.blocksFromTwoPoints(this.copiedArena.getMin().toBukkitLocation(), + this.copiedArena.getMax().toBukkitLocation()); + + this.paste = new HashMap<>(); + for (Location loc : copy.keySet()) { + if (copy.get(loc).getType() != Material.AIR) { + this.paste.put(loc.clone().add(this.offsetX, 0, this.offsetZ), copy.get(loc)); + } + } + + copy.clear(); + } else { + final Map newPaste = new HashMap<>(); + + for (Location loc : this.paste.keySet()) { + if (this.paste.get(loc).getType() != Material.AIR) { + newPaste.put(loc.clone().add(this.incrementX, 0, this.incrementZ), this.paste.get(loc)); + } + } + + this.paste.clear(); + this.paste.putAll(newPaste); + } + + boolean safe = true; + for (Location loc : this.paste.keySet()) { + final Block block = loc.getBlock(); + + if (block.getType() != Material.AIR) { + safe = false; + break; + } + } + + if (!safe) { + this.offsetX += this.incrementX; + this.offsetZ += this.incrementZ; + + this.run(); + return; + } + + new BlockPlaceRunnable(this.copiedArena.getPositionOne().toBukkitLocation().getWorld(), this.paste) { + @Override + public void finish() { + DuplicateArenaRunnable.this.onComplete(); + } + }.runTaskTimer(this.plugin, 0L, 5L); + } + + public Map blocksFromTwoPoints(Location loc1, Location loc2) { + final Map blocks = new HashMap<>(); + + final int topBlockX = (Math.max(loc1.getBlockX(), loc2.getBlockX())); + final int bottomBlockX = (Math.min(loc1.getBlockX(), loc2.getBlockX())); + + final int topBlockY = (Math.max(loc1.getBlockY(), loc2.getBlockY())); + final int bottomBlockY = (Math.min(loc1.getBlockY(), loc2.getBlockY())); + + final int topBlockZ = (Math.max(loc1.getBlockZ(), loc2.getBlockZ())); + final int bottomBlockZ = (Math.min(loc1.getBlockZ(), loc2.getBlockZ())); + + for (int x = bottomBlockX; x <= topBlockX; x++) { + for (int z = bottomBlockZ; z <= topBlockZ; z++) { + for (int y = bottomBlockY; y <= topBlockY; y++) { + final Block block = loc1.getWorld().getBlockAt(x, y, z); + + if (block.getType() != Material.AIR) { + blocks.put(new Location(loc1.getWorld(), x, y, z), block); + } + } + } + } + + return blocks; + } + + public abstract void onComplete(); +} diff --git a/src/main/java/com/solexgames/practice/runnable/ExpBarRunnable.java b/src/main/java/com/solexgames/practice/runnable/ExpBarRunnable.java new file mode 100644 index 0000000..d23b282 --- /dev/null +++ b/src/main/java/com/solexgames/practice/runnable/ExpBarRunnable.java @@ -0,0 +1,31 @@ +package com.solexgames.practice.runnable; + +import com.solexgames.practice.Practice; +import com.solexgames.practice.util.timer.impl.EnderpearlTimer; +import lombok.RequiredArgsConstructor; +import org.bukkit.entity.Player; + +import java.util.UUID; + +@RequiredArgsConstructor +public class ExpBarRunnable implements Runnable { + + private final Practice plugin = Practice.getInstance(); + + @Override + public void run() { + final EnderpearlTimer timer = Practice.getInstance().getTimerManager().getTimer(EnderpearlTimer.class); + + for (UUID uuid : timer.getCooldowns().keySet()) { + final Player player = this.plugin.getServer().getPlayer(uuid); + + if (player != null) { + final long time = timer.getRemaining(player); + final int seconds = (int) Math.round((double) time / 1000.0D); + + player.setLevel(seconds); + player.setExp((float) time / 15000.0F); + } + } + } +} diff --git a/src/main/java/com/solexgames/practice/runnable/LeaderboardUpdateRunnable.java b/src/main/java/com/solexgames/practice/runnable/LeaderboardUpdateRunnable.java new file mode 100644 index 0000000..bf71d47 --- /dev/null +++ b/src/main/java/com/solexgames/practice/runnable/LeaderboardUpdateRunnable.java @@ -0,0 +1,157 @@ +package com.solexgames.practice.runnable; + +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.mongodb.Block; +import com.mongodb.client.model.Filters; +import com.solexgames.core.CorePlugin; +import com.solexgames.core.player.ranks.Rank; +import com.solexgames.practice.Practice; +import com.solexgames.practice.PracticeConstants; +import com.solexgames.practice.challenges.tracker.KillStreakDataTracker; +import com.solexgames.practice.kit.Kit; +import com.solexgames.practice.managers.MongoManager; +import com.solexgames.practice.player.PlayerData; +import com.solexgames.practice.util.CC; +import com.solexgames.practice.util.division.RankedDivision; +import org.bson.Document; + +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +/** + * @author GrowlyX + * @since 7/29/2021 + */ + +public class LeaderboardUpdateRunnable implements Runnable { + + public static final List GLOBAL_ELO_LEADERBOARD_LORE = new ArrayList<>(); + + public static final Map> KIT_SPECIFIC_LEADERBOARD_LORE = new HashMap<>(); + public static final Map> KIT_SPECIFIC_WIN_STREAK_LORE = new HashMap<>(); + public static final Map> KIT_SPECIFIC_WIN_STREAK_LORE_SHORT = new HashMap<>(); + + @Override + public void run() { + CompletableFuture.runAsync(() -> { + Practice.getInstance().getKitManager().getKits().stream().filter(Kit::isRanked).forEach(kit -> { + final Map allEloMap = new HashMap<>(); + final Map allKillStreaksMap = new HashMap<>(); + + final List lore = new ArrayList<>(); + + MongoManager.getInstance().getPlayers().find().forEach((Block) document -> { + final Map eloMap = CorePlugin.GSON.fromJson(document.getString("elo"), PracticeConstants.STRING_INTEGER_MAP_TYPE); + final UUID playerUuid = UUID.fromString(document.getString("uuid")); + + if (eloMap != null) { + allEloMap.put(playerUuid, eloMap.get(kit.getName())); + } + + PlayerData data = Practice.getInstance().getPlayerManager().getPlayerData(playerUuid); + if (data != null) { + int value = 0; + if (data.getKitKillStreaks().containsKey(kit.getName())) { + value = data.getKillstreak(kit); + } else { + data.getKitKillStreaks().put(kit.getName(), 0); + } + allKillStreaksMap.put(playerUuid, value); + } + + }); + + final List sortedUuids = new HashSet<>(allEloMap.keySet()).stream() + .sorted(Comparator.comparing(uuid -> -(allEloMap.get(uuid) == null ? 0 : allEloMap.get(uuid)))) + .collect(Collectors.toList()); + + final AtomicInteger integer = new AtomicInteger(); + + sortedUuids.forEach(uuid -> { + if (integer.get() != 10) { + final Document playerDocument = CorePlugin.getInstance().getCoreDatabase().getPlayerCollection().find(Filters.eq("uuid", uuid.toString())).first(); + + if (playerDocument != null) { + lore.add(CC.GOLD + "#" + integer.incrementAndGet() + " " + CC.WHITE + playerDocument.getString("name") + CC.GRAY + " - " + CC.YELLOW + allEloMap.getOrDefault(uuid, 1000)); + } + } + }); + + LeaderboardUpdateRunnable.KIT_SPECIFIC_LEADERBOARD_LORE.put(kit, lore); + + final List loreForKillStreaks = new ArrayList<>(); + final List loreForKillStreaksShort = new ArrayList<>(); + + final List sortedUuidsForKillStreaks = new HashSet<>(allKillStreaksMap.keySet()).stream() + .sorted(Comparator.comparing(uuid -> -(allKillStreaksMap.get(uuid) == null ? 0 : allKillStreaksMap.get(uuid)))) + .collect(Collectors.toList()); + + final AtomicInteger integerForKillStreaks = new AtomicInteger(); + + sortedUuidsForKillStreaks.forEach(uuid -> { + if (integerForKillStreaks.get() != 10) { + final Document playerDocument = CorePlugin.getInstance().getCoreDatabase().getPlayerCollection().find(Filters.eq("uuid", uuid.toString())).first(); + + if (playerDocument != null) { + loreForKillStreaks.add(CC.GOLD + "#" + integerForKillStreaks.incrementAndGet() + " " + CC.WHITE + playerDocument.getString("name") + CC.GRAY + " - " + CC.YELLOW + allKillStreaksMap.get(uuid)); + } + } + }); + + if (loreForKillStreaks.size() >= 3) { + IntStream.range(0, 3).forEach(i -> { + loreForKillStreaksShort.add(" " + loreForKillStreaks.get(i)); + }); + } + + LeaderboardUpdateRunnable.KIT_SPECIFIC_WIN_STREAK_LORE.put(kit, loreForKillStreaks); + LeaderboardUpdateRunnable.KIT_SPECIFIC_WIN_STREAK_LORE_SHORT.put(kit, loreForKillStreaksShort); + }); + + final Map globalEloMap = new HashMap<>(); + final List lore = new ArrayList<>(); + + MongoManager.getInstance().getPlayers().find().forEach((Block) document -> { + final Map eloMap = CorePlugin.GSON.fromJson(document.getString("elo"), PracticeConstants.STRING_INTEGER_MAP_TYPE); + + if (eloMap != null) { + globalEloMap.put(UUID.fromString(document.getString("uuid")), document.getInteger("globalElo")); + } + }); + + final List sortedUuids = new HashSet<>(globalEloMap.keySet()).stream() + .sorted(Comparator.comparing(uuid -> -(globalEloMap.get(uuid) == null ? 0 : globalEloMap.get(uuid)))) + .collect(Collectors.toList()); + + final AtomicInteger integer = new AtomicInteger(); + + sortedUuids.forEach(uuid -> { + if (integer.get() != 11) { + final Document playerDocument = CorePlugin.getInstance().getCoreDatabase().getPlayerCollection() + .find(Filters.eq("uuid", uuid.toString())).first(); + + if (playerDocument != null) { + final int globalElo = globalEloMap.get(uuid); + final RankedDivision rankedDivision = RankedDivision.getByGlobalElo(globalElo); + + if (rankedDivision != null) { + lore.add(CC.GOLD + "#" + integer.incrementAndGet() + " " + CC.WHITE + playerDocument.getString("name") + CC.GRAY + " - " + CC.YELLOW + globalElo + CC.GRAY + " - " + rankedDivision.getFancyName()); + } + } + } + }); + + LeaderboardUpdateRunnable.GLOBAL_ELO_LEADERBOARD_LORE.clear(); + LeaderboardUpdateRunnable.GLOBAL_ELO_LEADERBOARD_LORE.addAll(lore); + }).whenComplete((unused, throwable) -> { + if (throwable != null) { + throwable.printStackTrace(); + } + }); + } +} diff --git a/src/main/java/com/solexgames/practice/runnable/MatchResetRunnable.java b/src/main/java/com/solexgames/practice/runnable/MatchResetRunnable.java new file mode 100644 index 0000000..a7cca35 --- /dev/null +++ b/src/main/java/com/solexgames/practice/runnable/MatchResetRunnable.java @@ -0,0 +1,69 @@ +package com.solexgames.practice.runnable; + +import com.boydti.fawe.util.EditSessionBuilder; +import com.boydti.fawe.util.TaskManager; +import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.blocks.BaseBlock; +import com.solexgames.practice.Practice; +import com.solexgames.practice.flags.Flag; +import com.solexgames.practice.match.Match; +import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; +import org.bukkit.Location; +import org.bukkit.block.BlockState; +import org.bukkit.scheduler.BukkitRunnable; + +@RequiredArgsConstructor +public class MatchResetRunnable extends BukkitRunnable { + + private final Practice plugin = Practice.getInstance(); + private final Match match; + + @Override + @SneakyThrows + public void run() { + if (this.match.getKit().getFlag().equals(Flag.BUILD) && this.match.getPlacedBlockLocations().size() > 0) { + final EditSession editSession = new EditSessionBuilder(this.match.getArena().getPositionOne().getWorldName()) + .fastmode(true).allowedRegionsEverywhere().autoQueue(false).limitUnlimited().build(); + + for (final Location location : this.match.getPlacedBlockLocations()) { + editSession.setBlock(new Vector(location.getBlockX(), location.getBlockY(), location.getZ()), new BaseBlock(0)); + } + + editSession.flushQueue(); + + TaskManager.IMP.task(() -> { + this.match.getPlacedBlockLocations().clear(); + this.match.getArena().addAvailableArena(this.match.getStandaloneArena()); + this.plugin.getArenaManager().removeArenaMatchUUID(this.match.getStandaloneArena()); + + this.cancel(); + }); + } else if (this.match.getOriginalBlockChanges().size() > 0) { + final EditSession editSession = new EditSessionBuilder(this.match.getArena().getPositionOne().getWorldName()) + .fastmode(true).allowedRegionsEverywhere().autoQueue(false).limitUnlimited().build(); + + for (BlockState blockState : this.match.getOriginalBlockChanges()) { + try { + editSession.setBlock(new Vector(blockState.getLocation().getBlockX(), blockState.getLocation().getBlockY(), blockState.getLocation().getZ()), new BaseBlock(blockState.getTypeId(), blockState.getRawData())); + } catch (Exception ignored) { + } + } + + editSession.flushQueue(); + + TaskManager.IMP.task(() -> { + if (match.getKit().getFlag().equals(Flag.SPLEEF)) { + this.match.getOriginalBlockChanges().clear(); + this.match.getArena().addAvailableArena(this.match.getStandaloneArena()); + this.plugin.getArenaManager().removeArenaMatchUUID(this.match.getStandaloneArena()); + } + + this.cancel(); + }); + } else { + this.cancel(); + } + } +} diff --git a/src/main/java/com/solexgames/practice/runnable/MatchRunnable.java b/src/main/java/com/solexgames/practice/runnable/MatchRunnable.java new file mode 100644 index 0000000..a749e5f --- /dev/null +++ b/src/main/java/com/solexgames/practice/runnable/MatchRunnable.java @@ -0,0 +1,114 @@ +package com.solexgames.practice.runnable; + +import com.solexgames.core.util.Color; +import com.solexgames.practice.Practice; +import com.solexgames.practice.flags.Flag; +import com.solexgames.practice.match.Match; +import com.solexgames.practice.match.MatchState; +import com.solexgames.practice.util.CC; +import lombok.RequiredArgsConstructor; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.Sound; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.bukkit.metadata.FixedMetadataValue; +import org.bukkit.scheduler.BukkitRunnable; + +@RequiredArgsConstructor +public class MatchRunnable extends BukkitRunnable { + + private final Practice plugin = Practice.getInstance(); + private final Match match; + + @Override + public void run() { + switch (this.match.getMatchState()) { + case STARTING: + if (this.match.decrementCountdown() == 0) { + this.match.setMatchState(MatchState.FIGHTING); + this.match.broadcast(CC.GREEN + "The match has started, good luck!", Sound.FIREWORK_BLAST); + + if (this.match.getKit().getFlag().equals(Flag.BEDWARS) || this.match.getKit().getFlag().equals(Flag.PARKOUR) || this.match.getKit().getFlag().equals(Flag.BRIDGES) || this.match.getKit().getFlag().equals(Flag.STICK_FIGHT)) { + this.match.getTeams().forEach(team -> team.getPlayers().forEach(uuid -> { + final Player player = Bukkit.getPlayer(uuid); + + if (player != null && player.hasMetadata("frozen")) { + player.removeMetadata("frozen", this.plugin); + } + })); + } + + this.match.getTeams().forEach(team -> team.getPlayers().forEach(uuid -> { + final Player player = Bukkit.getPlayer(uuid); + + if (player != null) { + this.plugin.getMatchManager().sendTitle(player, CC.BOLD + "Match started", 1, 1, 1, CC.GREEN); + this.plugin.getMatchManager().sendSubtitle(player, "Good luck, and have fun!", 1, 1, 1, CC.GRAY); + } + })); + + if (this.match.isRedRover()) { + this.plugin.getMatchManager().pickPlayer(this.match); + } + } else { + if (this.match.getKit().getFlag().equals(Flag.BEDWARS) || this.match.getKit().getFlag().equals(Flag.PARKOUR) || this.match.getKit().getFlag().equals(Flag.BRIDGES) || this.match.getKit().getFlag().equals(Flag.STICK_FIGHT)) { + this.match.getTeams().forEach(team -> team.getPlayers().forEach(uuid -> { + final Player player = Bukkit.getPlayer(uuid); + + if (player != null && !player.hasMetadata("frozen")) { + player.setMetadata("frozen", new FixedMetadataValue(this.plugin, true)); + } + })); + } + + this.match.getTeams().forEach(team -> team.getPlayers().forEach(uuid -> { + final Player player = Bukkit.getPlayer(uuid); + + if (player != null) { + this.plugin.getMatchManager().sendTitle(player, CC.BOLD + this.match.getCountdown(), 1, 1, 1, this.match.getCountdown() > 4 ? CC.GREEN : this.match.getCountdown() > 2 ? CC.AQUA : CC.RED); + } + })); + + this.match.broadcast(Color.SECONDARY_COLOR + "The match starts in " + Color.MAIN_COLOR + this.match.getCountdown() + Color.SECONDARY_COLOR + " " + (this.match.getCountdown() == 1 ? "second" : "seconds") + "!", Sound.CLICK); + } + break; + case SWITCHING: + if (this.match.decrementCountdown() == 0) { + this.match.getEntitiesToRemove().forEach(Entity::remove); + this.match.clearEntitiesToRemove(); + this.match.setMatchState(MatchState.FIGHTING); + + this.plugin.getMatchManager().pickPlayer(this.match); + } + break; + case ENDING: + if (this.match.decrementCountdown() == 0) { + this.plugin.getTournamentManager().removeMatchFromTournament(this.match); + + this.match.getTaskIds().forEach(id -> this.plugin.getServer().getScheduler().cancelTask(id)); + this.match.getEntitiesToRemove().forEach(Entity::remove); + this.match.getTeams().forEach(team -> + team.streamAlivePlayers().forEach(this.plugin.getPlayerManager()::sendToSpawnAndUpdateTag)); + this.match.streamSpectators().forEach(player -> { + this.plugin.getMatchManager().removeSpectator(player, true); + }); + + this.match.getPlacedBlockLocations().forEach(location -> location.getBlock().setType(Material.AIR)); + this.match.getOriginalBlockChanges().forEach(blockState -> blockState.getLocation().getBlock().setType(blockState.getType())); + + if (this.match.getKit().getFlag().equals(Flag.BUILD) || this.match.getKit().getFlag().equals(Flag.SPLEEF)) { + this.match.getArena().addAvailableArena(this.match.getStandaloneArena()); + this.plugin.getArenaManager().removeArenaMatchUUID(this.match.getStandaloneArena()); + } + + this.plugin.getMatchManager().removeMatch(this.match); + + new MatchResetRunnable(this.match).runTaskTimer(this.plugin, 20L, 20L); + + this.cancel(); + } + break; + } + } +} diff --git a/src/main/java/com/solexgames/practice/runnable/NewRoundRunnable.java b/src/main/java/com/solexgames/practice/runnable/NewRoundRunnable.java new file mode 100644 index 0000000..35a8078 --- /dev/null +++ b/src/main/java/com/solexgames/practice/runnable/NewRoundRunnable.java @@ -0,0 +1,68 @@ +package com.solexgames.practice.runnable; + +import com.solexgames.core.util.Color; +import com.solexgames.practice.Practice; +import com.solexgames.practice.location.impl.impl.AsyncLocation; +import com.solexgames.practice.match.Match; +import com.solexgames.practice.util.PlayerUtil; +import io.papermc.lib.PaperLib; +import org.bukkit.entity.Player; +import org.bukkit.metadata.FixedMetadataValue; +import org.bukkit.scheduler.BukkitRunnable; + +/** + * @author GrowlyX + * @since 5/25/2021 + */ + +public class NewRoundRunnable extends BukkitRunnable { + + private final Match match; + private final Player player; + + private int ticks; + + public NewRoundRunnable(Match match, Player player) { + this.match = match; + this.player = player; + this.ticks = 4; + + this.runTaskTimer(Practice.getInstance(), 20L, 20L); + } + + @Override + public void run() { + this.ticks--; + + if (this.player == null) { + this.cancel(); + return; + } + + switch (this.ticks) { + case 3: + this.player.setAllowFlight(true); + this.player.setFlying(true); + this.player.setMetadata("e", new FixedMetadataValue(Practice.getInstance(), "")); + case 2: case 1: + this.player.sendMessage(Color.SECONDARY_COLOR + "You will respawn in " + Color.MAIN_COLOR + this.ticks + Color.SECONDARY_COLOR + " second" + (this.ticks == 1 ? "" : "s") + "."); + break; + case 0: + final AsyncLocation locationA = this.match.getStandaloneArena() != null ? this.match.getStandaloneArena().getPositionOne() : this.match.getArena().getPositionOne(); + final AsyncLocation locationB = this.match.getStandaloneArena() != null ? this.match.getStandaloneArena().getPositionTwo() : this.match.getArena().getPositionTwo(); + + PlayerUtil.clearPlayer(player); + PaperLib.teleportAsync(player, this.match.getTeamByPlayer(this.player.getUniqueId()).getTeamID() == 1 ? locationA.toBukkitLocation() : locationB.toBukkitLocation()) + .whenComplete((aBoolean, throwable) -> { + this.player.removeMetadata("e", Practice.getInstance()); + }); + + this.player.sendMessage(Color.MAIN_COLOR + "You've been respawned."); + + new AttackableRunnable(this.player); + + this.cancel(); + break; + } + } +} diff --git a/src/main/java/com/solexgames/practice/runnable/RematchRunnable.java b/src/main/java/com/solexgames/practice/runnable/RematchRunnable.java new file mode 100644 index 0000000..54753b1 --- /dev/null +++ b/src/main/java/com/solexgames/practice/runnable/RematchRunnable.java @@ -0,0 +1,35 @@ +package com.solexgames.practice.runnable; + +import com.solexgames.practice.Practice; +import com.solexgames.practice.managers.ItemManager; +import com.solexgames.practice.player.PlayerData; +import com.solexgames.practice.player.PlayerState; +import lombok.RequiredArgsConstructor; +import org.bukkit.entity.Player; + +import java.util.UUID; + +@RequiredArgsConstructor +public class RematchRunnable implements Runnable { + + private final Practice plugin = Practice.getInstance(); + private final UUID playerUUID; + + @Override + public void run() { + final Player player = this.plugin.getServer().getPlayer(this.playerUUID); + + if (player != null) { + final PlayerData playerData = this.plugin.getPlayerManager().getPlayerData(player.getUniqueId()); + + if (playerData != null && playerData.getPlayerState() == PlayerState.SPAWN && this.plugin.getMatchManager().isRematching(player.getUniqueId()) && this.plugin.getPartyManager().getParty(player.getUniqueId()) == null) { + player.getInventory().setItem(4, ItemManager.SHOP_ITEM); + + player.updateInventory(); + playerData.setRematchID(-1); + } + + this.plugin.getMatchManager().removeRematch(this.playerUUID); + } + } +} diff --git a/src/main/java/com/solexgames/practice/runnable/SaveDataRunnable.java b/src/main/java/com/solexgames/practice/runnable/SaveDataRunnable.java new file mode 100644 index 0000000..81380c8 --- /dev/null +++ b/src/main/java/com/solexgames/practice/runnable/SaveDataRunnable.java @@ -0,0 +1,18 @@ +package com.solexgames.practice.runnable; + +import com.solexgames.practice.Practice; +import com.solexgames.practice.player.PlayerData; +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +public class SaveDataRunnable implements Runnable { + + private final Practice plugin = Practice.getInstance(); + + @Override + public void run() { + for (PlayerData playerData : this.plugin.getPlayerManager().getAllData()) { + this.plugin.getPlayerManager().saveData(playerData); + } + } +} diff --git a/src/main/java/com/solexgames/practice/runnable/TournamentBroadcastRunnable.java b/src/main/java/com/solexgames/practice/runnable/TournamentBroadcastRunnable.java new file mode 100644 index 0000000..e55342e --- /dev/null +++ b/src/main/java/com/solexgames/practice/runnable/TournamentBroadcastRunnable.java @@ -0,0 +1,41 @@ +package com.solexgames.practice.runnable; + +import com.solexgames.core.util.clickable.Clickable; +import com.solexgames.practice.Practice; +import com.solexgames.practice.PracticeConstants; +import com.solexgames.practice.tournament.Tournament; +import com.solexgames.practice.tournament.TournamentState; +import com.solexgames.practice.util.CC; +import lombok.RequiredArgsConstructor; +import org.bukkit.Bukkit; +import org.bukkit.scheduler.BukkitRunnable; + +/** + * @author GrowlyX + * @since 8/8/2021 + */ + +@RequiredArgsConstructor +public class TournamentBroadcastRunnable extends BukkitRunnable { + + private final Tournament tournament; + + @Override + public void run() { + if (this.tournament.getTournamentState().equals(TournamentState.STARTING) || Practice.getInstance().getTournamentManager().getTournament(this.tournament.getId()) == null) { + this.cancel(); + return; + } + + final String size = CC.GRAY + " (" + tournament.getTeamSize() + "v" + tournament.getTeamSize() + ")"; + final String broadcast = CC.YELLOW + "A " + CC.AQUA + tournament.getKitName() + size + CC.YELLOW + " tournament is starting soon. " + CC.GREEN + CC.BOLD + "[Join]"; + + final Clickable message = new Clickable(broadcast, + CC.GREEN + "Click to join tournament number " + CC.YELLOW + this.tournament.getId() + CC.GREEN + ".", + "/tournament join " + this.tournament.getId()); + + Bukkit.getOnlinePlayers().forEach(player -> + player.spigot().sendMessage(message.asComponents()) + ); + } +} diff --git a/src/main/java/com/solexgames/practice/runnable/TournamentRunnable.java b/src/main/java/com/solexgames/practice/runnable/TournamentRunnable.java new file mode 100644 index 0000000..70eb0db --- /dev/null +++ b/src/main/java/com/solexgames/practice/runnable/TournamentRunnable.java @@ -0,0 +1,145 @@ +package com.solexgames.practice.runnable; + +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; +import com.solexgames.core.util.Color; +import com.solexgames.practice.Practice; +import com.solexgames.practice.kit.Kit; +import com.solexgames.practice.match.Match; +import com.solexgames.practice.match.team.impl.MatchTeam; +import com.solexgames.practice.party.Party; +import com.solexgames.practice.player.PlayerData; +import com.solexgames.practice.player.PlayerState; +import com.solexgames.practice.queue.QueueType; +import com.solexgames.practice.tournament.Tournament; +import com.solexgames.practice.tournament.TournamentState; +import com.solexgames.practice.tournament.TournamentTeam; +import com.solexgames.practice.util.CC; +import lombok.RequiredArgsConstructor; +import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitRunnable; + +import java.util.*; + +@RequiredArgsConstructor +public class TournamentRunnable extends BukkitRunnable { + + private final Practice plugin = Practice.getInstance(); + private final Tournament tournament; + + @Override + public void run() { + if (this.tournament.getTournamentState().equals(TournamentState.STARTING)) { + final int countdown = this.tournament.decrementCountdown(); + + if (countdown == 0) { + if (this.tournament.getCurrentRound() == 1) { + final Set players = Sets.newConcurrentHashSet(this.tournament.getPlayers()); + + for (UUID player : players) { + final Party party = this.plugin.getPartyManager().getParty(player); + + if (party != null) { + final TournamentTeam team = new TournamentTeam(party.getLeader(), Lists.newArrayList(party.getMembers())); + + this.tournament.addAliveTeam(team); + + for (UUID member : party.getMembers()) { + this.tournament.setPlayerTeam(member, team); + + players.remove(member); + } + } + } + + List currentTeam = new ArrayList<>(); + + for (UUID player : players) { + if (currentTeam == null) { + currentTeam = new ArrayList<>(); + } + + currentTeam.add(player); + + if (currentTeam.size() == this.tournament.getTeamSize()) { + final TournamentTeam team = new TournamentTeam(currentTeam.get(0), currentTeam); + + this.tournament.addAliveTeam(team); + + for (UUID teammate : team.getPlayers()) { + tournament.setPlayerTeam(teammate, team); + } + + currentTeam = null; + } + } + } + + final List teams = this.tournament.getAliveTeams(); + + Collections.shuffle(teams); + + for (int i = 0; i < teams.size(); i += 2) { + final TournamentTeam teamA = teams.get(i); + + if (teams.size() > i + 1) { + final TournamentTeam teamB = teams.get(i + 1); + + for (UUID playerUUID : teamA.getAlivePlayers()) { + this.removeSpectator(playerUUID); + } + for (UUID playerUUID : teamB.getAlivePlayers()) { + this.removeSpectator(playerUUID); + } + + final MatchTeam matchTeamA = new MatchTeam(teamA.getLeader(), new ArrayList<>(teamA.getAlivePlayers()), 0); + final MatchTeam matchTeamB = new MatchTeam(teamB.getLeader(), new ArrayList<>(teamB.getAlivePlayers()), 1); + + final Kit kit = this.plugin.getKitManager().getKit(this.tournament.getKitName()); + final Match match = new Match(this.plugin.getArenaManager().getRandomArena(kit), kit, QueueType.UNRANKED, matchTeamA, matchTeamB); + + final Player leaderA = this.plugin.getServer().getPlayer(teamA.getLeader()); + final Player leaderB = this.plugin.getServer().getPlayer(teamB.getLeader()); + + match.broadcast(CC.YELLOW + "Now starting a " + CC.AQUA + match.getKit().getName() + CC.GRAY + " (" + leaderA.getName() + " v " + leaderB.getName() + ") " + CC.YELLOW + "match on the arena " + CC.AQUA + match.getArena().getName() + CC.YELLOW + "."); + + this.plugin.getServer().getScheduler().runTask(this.plugin, () -> { + this.plugin.getMatchManager().createMatch(match, true); + this.tournament.addMatch(match.getMatchId()); + + this.plugin.getTournamentManager().addMatchToTournament(match.getMatchId(), this.tournament.getId()); + }); + + } else { + for (UUID playerUUID : teamA.getAlivePlayers()) { + final Player player = this.plugin.getServer().getPlayer(playerUUID); + + player.sendMessage(new String[]{ + CC.RED + "I'm sorry, but there was no matching team available for you.", + CC.RED + "You've been skipped to the " + Color.SECONDARY_COLOR + "next round" + CC.RED + "!", + }); + } + } + } + + this.tournament.broadcast(Color.SECONDARY_COLOR + "Starting round number " + Color.MAIN_COLOR + this.tournament.getCurrentRound() + Color.SECONDARY_COLOR + "..."); + + this.tournament.setTournamentState(TournamentState.FIGHTING); + } else if (countdown <= 5) { + this.tournament.broadcast(Color.SECONDARY_COLOR + "Round " + Color.MAIN_COLOR + "#" + this.tournament.getCurrentRound() + Color.SECONDARY_COLOR + " is starting in " + Color.MAIN_COLOR + countdown + Color.SECONDARY_COLOR + " " + (countdown == 1 ? "second" : "seconds") + "!"); + } + } + } + + private void removeSpectator(UUID playerUUID) { + final Player player = this.plugin.getServer().getPlayer(playerUUID); + + if (player != null) { + final PlayerData playerData = this.plugin.getPlayerManager().getPlayerData(player.getUniqueId()); + + if (playerData.getPlayerState().equals(PlayerState.SPECTATING)) { + this.plugin.getMatchManager().removeSpectator(player, false); + } + } + } +} diff --git a/src/main/java/com/solexgames/practice/runnable/cache/StatusCache.java b/src/main/java/com/solexgames/practice/runnable/cache/StatusCache.java new file mode 100644 index 0000000..fa175f9 --- /dev/null +++ b/src/main/java/com/solexgames/practice/runnable/cache/StatusCache.java @@ -0,0 +1,82 @@ +package com.solexgames.practice.runnable.cache; + +import com.solexgames.core.util.external.Menu; +import com.solexgames.practice.Practice; +import com.solexgames.practice.kit.Kit; +import com.solexgames.practice.player.PlayerData; +import com.solexgames.practice.player.PlayerState; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; + +@Data +@EqualsAndHashCode(callSuper = true) +public class StatusCache extends Thread { + + @Getter + private static StatusCache instance; + + private Map fightingKits = new HashMap<>(); + + private int fighting; + private int queueing; + private int inFfa; + + public StatusCache() { + instance = this; + } + + @Override + public void run() { + while (true) { + int fighting = 0; + int queueing = 0; + int ffa = 0; + + for (PlayerData playerData : Practice.getInstance().getPlayerManager().getAllData()) { + if (playerData.getPlayerState() == PlayerState.FIGHTING) { + fighting++; + } + + if (playerData.getPlayerState() == PlayerState.QUEUE) { + queueing++; + } + + if (playerData.getPlayerState() == PlayerState.FFA) { + ffa++; + } + } + + for (Kit kit : Practice.getInstance().getKitManager().getKits()) { + final AtomicInteger integer = new AtomicInteger(); + + Practice.getInstance().getMatchManager().getMatches().forEach((uuid, match) -> { + if (match.getKit().getName().equals(kit.getName())) { + final int playing = match.getTeams().stream() + .mapToInt(m -> m.getAlivePlayers().size()).sum(); + + integer.addAndGet(playing); + } + }); + + this.fightingKits.put(kit, integer.get()); + } + + this.fighting = fighting; + this.queueing = queueing; + this.inFfa = ffa; + + try { + Thread.sleep(500L); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } +} diff --git a/src/main/java/com/solexgames/practice/settings/PracticeSettingsImpl.java b/src/main/java/com/solexgames/practice/settings/PracticeSettingsImpl.java new file mode 100644 index 0000000..41d7288 --- /dev/null +++ b/src/main/java/com/solexgames/practice/settings/PracticeSettingsImpl.java @@ -0,0 +1,163 @@ +package com.solexgames.practice.settings; + +import com.solexgames.core.settings.player.ISettings; +import com.solexgames.core.util.Color; +import com.solexgames.core.util.builder.ItemBuilder; +import com.solexgames.core.util.external.Button; +import com.solexgames.practice.Practice; +import com.solexgames.practice.menu.DeathEffectMenu; +import com.solexgames.practice.menu.KillMessageMenu; +import com.solexgames.practice.menu.MatchmakingSettingsMenu; +import com.solexgames.practice.player.PlayerData; +import com.solexgames.practice.settings.item.ProfileOptionsItemState; +import com.solexgames.practice.util.CC; +import org.bukkit.Material; +import org.bukkit.entity.Player; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author GrowlyX + * @since 5/22/2021 + */ + +public class PracticeSettingsImpl implements ISettings { + + @Override + public List