diff --git a/build.gradle b/build.gradle index f1c12b7..7af82b3 100644 --- a/build.gradle +++ b/build.gradle @@ -18,12 +18,14 @@ repositories { name = "sonatype" url = "https://oss.sonatype.org/content/groups/public/" } + maven { url 'https://libraries.minecraft.net/' } } dependencies { compileOnly("io.papermc.paper:paper-api:1.20.4-R0.1-SNAPSHOT") - implementation("com.github.lewysDavies:Java-Probability-Collection:v0.8") implementation group: 'me.libraryaddict.disguises', name: 'libsdisguises', version: '11.0.6' + //TODO implementation 'me.lucko:commodore:2.2' + } @@ -55,10 +57,13 @@ processResources { } jar { - - destinationDirectory.set(file("/home/hadvart/Documents/Minecraft/Pufferfish 1.20.4/plugins")) + //destinationDirectory.set(file("/home/hadvart/Documents/Minecraft/Pufferfish 1.20.4/plugins")) from sourceSets.main.output - from { configurations.runtimeClasspath.findAll { it.name.startsWith('Java-Probability-Collection') }.collect { zipTree(it) } } + from { + configurations.runtimeClasspath.findAll { + it.name.startsWith('Java-Probability-Collection') || it.name.startsWith('commodore') || it.name.startsWith('brigadier') + }.collect { zipTree(it) } + } duplicatesStrategy = DuplicatesStrategy.EXCLUDE } diff --git a/build.xml b/build.xml index 44efce6..546d259 100644 --- a/build.xml +++ b/build.xml @@ -2,17 +2,13 @@ - - + - + - - + \ No newline at end of file diff --git a/src/main/java/com/lewdev/probabilitylib/ProbabilityCollection.java b/src/main/java/com/lewdev/probabilitylib/ProbabilityCollection.java new file mode 100644 index 0000000..3055382 --- /dev/null +++ b/src/main/java/com/lewdev/probabilitylib/ProbabilityCollection.java @@ -0,0 +1,246 @@ +/* + * Copyright (c) 2020 Lewys Davies + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.lewdev.probabilitylib; + +import java.util.*; + +/** + * ProbabilityCollection for retrieving random elements based on probability. + *
+ *
+ * Selection Algorithm Implementation: + *

+ *

+ * + * @param Type of elements + * @author Lewys Davies + * @version 0.8 + */ +public class ProbabilityCollection { + + private final NavigableSet> collection; + private final SplittableRandom random = new SplittableRandom(); + + private int totalProbability; + + /** + * Construct a new Probability Collection + */ + public ProbabilityCollection() { + this.collection = new TreeSet<>(Comparator.comparingInt(ProbabilitySetElement::getIndex)); + this.totalProbability = 0; + } + + /** + * @return Number of objects inside the collection + */ + public int size() { + return this.collection.size(); + } + + /** + * @return True if collection contains no elements, else False + */ + public boolean isEmpty() { + return this.collection.isEmpty(); + } + + /** + * @param object + * @return True if collection contains the object, else False + * @throws IllegalArgumentException if object is null + */ + public boolean contains(E object) { + if (object == null) { + throw new IllegalArgumentException("Cannot check if null object is contained in this collection"); + } + + return this.collection.stream() + .anyMatch(entry -> entry.getObject().equals(object)); + } + + /** + * @return Iterator over this collection + */ + public Iterator> iterator() { + return this.collection.iterator(); + } + + /** + * Add an object to this collection + * + * @param object. Not null. + * @param probability share. Must be greater than 0. + * @throws IllegalArgumentException if object is null + * @throws IllegalArgumentException if probability <= 0 + */ + public void add(E object, int probability) { + if (object == null) { + throw new IllegalArgumentException("Cannot add null object"); + } + + if (probability <= 0) { + throw new IllegalArgumentException("Probability must be greater than 0"); + } + + ProbabilitySetElement entry = new ProbabilitySetElement(object, probability); + entry.setIndex(this.totalProbability + 1); + + this.collection.add(entry); + this.totalProbability += probability; + } + + /** + * Remove a object from this collection + * + * @param object + * @return True if object was removed, else False. + * @throws IllegalArgumentException if object is null + */ + public boolean remove(E object) { + if (object == null) { + throw new IllegalArgumentException("Cannot remove null object"); + } + + Iterator> it = this.iterator(); + boolean removed = false; + + while (it.hasNext()) { + ProbabilitySetElement entry = it.next(); + if (entry.getObject().equals(object)) { + this.totalProbability -= entry.getProbability(); + it.remove(); + removed = true; + } + } + + this.updateIndexes(); + + return removed; + } + + /** + * Remove all objects from this collection + */ + public void clear() { + this.collection.clear(); + this.totalProbability = 0; + } + + /** + * Get a random object from this collection, based on probability. + * + * @return Random object + * @throws IllegalStateException if this collection is empty + */ + public E get() { + if (this.isEmpty()) { + throw new IllegalStateException("Cannot get an object out of a empty collection"); + } + + ProbabilitySetElement toFind = new ProbabilitySetElement<>(null, 0); + toFind.setIndex(this.random.nextInt(1, this.totalProbability + 1)); + + return Objects.requireNonNull(this.collection.floor(toFind).getObject()); + } + + /** + * @return Sum of all element's probability + */ + public final int getTotalProbability() { + return this.totalProbability; + } + + /* + * Calculate the size of all element's "block" of space: + * i.e 1-5, 6-10, 11-14, 15, 16 + * + * We then only need to store the start index of each element, + * as we make use of the TreeSet#floor + */ + private final void updateIndexes() { + int previousIndex = 0; + + for (ProbabilitySetElement entry : this.collection) { + previousIndex = entry.setIndex(previousIndex + 1) + (entry.getProbability() - 1); + } + } + + /** + * Used internally to store information about a object's + * state in a collection. Specifically, the probability + * and index within the collection. + *

+ * Indexes refer to the start position of this element's "block" of space. + * The space between element "block"s represents their probability of being selected + * + * @param Type of element + * @author Lewys Davies + */ + final static class ProbabilitySetElement { + private final T object; + private final int probability; + private int index; + + /** + * @param object + * @param probability + */ + protected ProbabilitySetElement(T object, int probability) { + this.object = object; + this.probability = probability; + } + + /** + * @return The actual object + */ + public final T getObject() { + return this.object; + } + + /** + * @return Probability share in this collection + */ + public final int getProbability() { + return this.probability; + } + + // Used internally, see this class's documentation + private final int getIndex() { + return this.index; + } + + // Used Internally, see this class's documentation + private final int setIndex(int index) { + this.index = index; + return this.index; + } + } +} diff --git a/src/main/java/hdvtdev/blockAndSeek/BlockAndSeek.java b/src/main/java/hdvtdev/blockAndSeek/BlockAndSeek.java index f2cab4a..68df789 100644 --- a/src/main/java/hdvtdev/blockAndSeek/BlockAndSeek.java +++ b/src/main/java/hdvtdev/blockAndSeek/BlockAndSeek.java @@ -3,7 +3,8 @@ package hdvtdev.blockAndSeek; import hdvtdev.blockAndSeek.managers.ConfigManager; import me.libraryaddict.disguise.LibsDisguises; import org.bukkit.Bukkit; -import org.bukkit.command.CommandExecutor; +import org.bukkit.command.PluginCommand; +import org.bukkit.configuration.serialization.ConfigurationSerialization; import org.bukkit.plugin.Plugin; import org.bukkit.plugin.java.JavaPlugin; @@ -13,9 +14,9 @@ import java.io.InputStream; import java.util.Objects; import java.util.logging.Logger; -public class BlockAndSeek extends JavaPlugin implements CommandExecutor { +public class BlockAndSeek extends JavaPlugin { - private static Plugin javaPlugin; + private static JavaPlugin javaPlugin; public static Plugin getInstance() { return javaPlugin; @@ -45,11 +46,18 @@ public class BlockAndSeek extends JavaPlugin implements CommandExecutor { return javaPlugin.getLogger(); } + public static PluginCommand getCmd(String name) { + return javaPlugin.getCommand(name); + + } + @Override public void onEnable() { javaPlugin = this; + ConfigurationSerialization.registerClass(BlockAndSeekMap.class, "BlockAndSeekMap"); + LibsDisguises libsDisguises = (LibsDisguises) Bukkit.getPluginManager().getPlugin("LibsDisguises"); if (libsDisguises == null) { @@ -63,7 +71,19 @@ public class BlockAndSeek extends JavaPlugin implements CommandExecutor { getLogger().severe("Failed to save some .yml configs!"); } - Objects.requireNonNull(getCommand("blockandseek")).setExecutor(new CommandListener()); + PluginCommand command = Objects.requireNonNull(getCommand("blockandseek")); + command.setExecutor(new CommandListener()); + + + + /* TODO + if (CommodoreProvider.isSupported()) { + CommandListener.registerCompletions(CommodoreProvider.getCommodore(this), command); + } + + */ + + getServer().getPluginManager().registerEvents(new EventListener(), this); } diff --git a/src/main/java/hdvtdev/blockAndSeek/BlockAndSeekGame.java b/src/main/java/hdvtdev/blockAndSeek/BlockAndSeekGame.java index b32195f..4c5aa87 100644 --- a/src/main/java/hdvtdev/blockAndSeek/BlockAndSeekGame.java +++ b/src/main/java/hdvtdev/blockAndSeek/BlockAndSeekGame.java @@ -1,16 +1,14 @@ package hdvtdev.blockAndSeek; +import hdvtdev.blockAndSeek.managers.FreezeManager; import me.libraryaddict.disguise.DisguiseAPI; +import org.bukkit.Bukkit; import org.bukkit.GameMode; -import org.bukkit.Material; +import org.bukkit.Location; import org.bukkit.entity.Player; import org.bukkit.persistence.PersistentDataType; -import java.security.SecureRandom; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Set; +import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicBoolean; @@ -21,24 +19,50 @@ public class BlockAndSeekGame { private final AtomicBoolean started = new AtomicBoolean(false); private final String name; private final int maxPlayers; + private final Location lobby; - public BlockAndSeekGame(String name, int maxPlayers) { + public BlockAndSeekGame(String name, int maxPlayers, List lobby) { this.name = name; this.maxPlayers = maxPlayers; + this.lobby = new Location(Bukkit.getWorld(name), lobby.getFirst(), lobby.get(1), lobby.get(2)); } public List getHiders() { return players.entrySet().stream().filter(e -> Objects.equals(e.getValue(), PlayerType.HIDER)).map(Map.Entry::getKey).toList(); } + public void preEnd() { + + for (Player hider : getHiders()) { + FreezeManager.unfreezeIfFrozen(hider); + FreezeManager.removePlayerDisguise(hider); + DisguiseAPI.undisguiseToAll(hider); + hider.getInventory().clear(); + hider.setGlowing(true); + } + + for (Player player : getPlayers()) { + player.setInvulnerable(true); + player.setVisibleByDefault(true); + } + + + } + public void end() { + + for (Player hider : getHiders()) { + FreezeManager.removePlayerDisguise(hider); + DisguiseAPI.undisguiseToAll(hider); + } + for (Player player : players.keySet()) { - //EventListener.unfreezePlayer(player); TODO - player.getPersistentDataContainer().remove(BlockAndSeekContainer.PLAYER); - player.sendBlockChange(player.getLocation(), Material.AIR.createBlockData()); + player.teleport(lobby); + player.getPersistentDataContainer().remove(Keys.PLAYER); player.setGameMode(GameMode.SURVIVAL); - DisguiseAPI.undisguiseToAll(player); + player.setInvulnerable(false); + player.setGlowing(false); } } @@ -46,7 +70,8 @@ public class BlockAndSeekGame { if (started.get() || playerCount() + 1 > maxPlayers) { return false; } - player.getPersistentDataContainer().set(BlockAndSeekContainer.PLAYER, PersistentDataType.STRING, name); + player.getPersistentDataContainer().set(Keys.PLAYER, PersistentDataType.STRING, name); + player.teleport(lobby); players.put(player, PlayerType.HIDER); return true; } @@ -92,17 +117,15 @@ public class BlockAndSeekGame { } public Player selectRandomSeeker() { - SecureRandom random = new SecureRandom(); - int i = 0; - int randomNum = random.nextInt(0, players.size()); - for (Player player : players.keySet()) { - if (i == randomNum) { - players.put(player, PlayerType.SEEKER); - return player; - } - i++; - } - return null; + return selectRandomSeekers(1).getFirst(); + } + + public List selectRandomSeekers(int count) { + ArrayList playerList = new ArrayList<>(players.keySet()); + Collections.shuffle(playerList); + List seekers = playerList.subList(0, Math.min(count, playerList.size())); + for (Player seeker : seekers) players.put(seeker, PlayerType.SEEKER); + return seekers; } private enum PlayerType { diff --git a/src/main/java/hdvtdev/blockAndSeek/BlockAndSeekMap.java b/src/main/java/hdvtdev/blockAndSeek/BlockAndSeekMap.java index a2a2a41..e00fd54 100644 --- a/src/main/java/hdvtdev/blockAndSeek/BlockAndSeekMap.java +++ b/src/main/java/hdvtdev/blockAndSeek/BlockAndSeekMap.java @@ -1,17 +1,162 @@ package hdvtdev.blockAndSeek; +import org.bukkit.Material; +import org.bukkit.configuration.serialization.ConfigurationSerializable; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; -import java.util.List; +import java.util.*; -public record BlockAndSeekMap(List spawn, List lobby, int duration, int minPlayers, int maxPlayers, - List blocks) { +public class BlockAndSeekMap implements ConfigurationSerializable { - - public record Block(@NotNull ItemStack block, int chance) { + private List spawn; + private List lobby; + private int duration; + private int minPlayers; + private int maxPlayers; + private List blocks; + + public BlockAndSeekMap() { } + public BlockAndSeekMap(List spawn, List lobby, int duration, int minPlayers, int maxPlayers, + List blocks) { + this.spawn = spawn; + this.lobby = lobby; + this.duration = duration; + this.minPlayers = minPlayers; + this.maxPlayers = maxPlayers; + this.blocks = blocks; + } + + public List getSpawn() { + return spawn; + } + + public void setSpawn(List spawn) { + this.spawn = spawn; + } + + public List getLobby() { + return lobby; + } + + public void setLobby(List lobby) { + this.lobby = lobby; + } + + public int getDuration() { + return duration; + } + + public void setDuration(int duration) { + this.duration = duration; + } + + public int getMinPlayers() { + return minPlayers; + } + + public void setMinPlayers(int minPlayers) { + this.minPlayers = minPlayers; + } + + public int getMaxPlayers() { + return maxPlayers; + } + + public void setMaxPlayers(int maxPlayers) { + this.maxPlayers = maxPlayers; + } + + public List getBlocks() { + return blocks; + } + + public void setBlocks(List blocks) { + this.blocks = blocks; + } + + public boolean isReady() { + return !spawn.isEmpty() && !lobby.isEmpty() && duration > 0 && !blocks.isEmpty() && minPlayers > 0 && maxPlayers > 0; + } + + public static BlockAndSeekMap defaultMap() { + return new BlockAndSeekMap(List.of(), List.of(), 0, 0, 0, List.of()); + } + + public Set check() { + Set status = new HashSet<>(); + if (spawn.isEmpty()) status.add(MapStatus.SPAWN_REQUIRED); + if (lobby.isEmpty()) status.add(MapStatus.LOBBY_REQUIRED); + if (duration <= 0) status.add(MapStatus.DURATION_REQUIRED); + if (minPlayers <= 0) status.add(MapStatus.MIN_PLAYERS_REQUIRED); + if (maxPlayers <= 0) status.add(MapStatus.MAX_PLAYERS_REQUIRED); + if (blocks.isEmpty()) status.add(MapStatus.BLOCKS_REQUIRED); + return status; + } + + @Override + public @NotNull Map serialize() { + + Map map = new HashMap<>(); + map.put("spawn", spawn); + map.put("lobby", lobby); + map.put("duration", duration); + map.put("min-players", minPlayers); + map.put("max-players", maxPlayers); + List> serBlocks = new ArrayList<>(); + for (Block block : blocks) { + serBlocks.add(block.toMap()); + } + map.put("blocks", serBlocks); + + + return map; + } + + @NotNull + @SuppressWarnings("unchecked") + public static BlockAndSeekMap deserialize(@NotNull Map args) { + + BlockAndSeekMap map = new BlockAndSeekMap(); + map.setSpawn((List) args.get("spawn")); + map.setLobby((List) args.get("lobby")); + map.setDuration((int) args.get("duration")); + map.setMinPlayers((int) args.get("min-players")); + map.setMaxPlayers((int) args.get("max-players")); + map.setBlocks(((List>) args.get("blocks")).stream().map(Block::fromMap).toList()); + + return map; + } + + @Override + public String toString() { + return String.format("BlockAndSeekMap[spawn=%s, lobby=%s, duration=%s, minPlayers=%s, maxPlayers=%s, blocks=%s]", spawn, lobby, duration, minPlayers, maxPlayers, blocks); + } + + public enum MapStatus { + SPAWN_REQUIRED, + LOBBY_REQUIRED, + DURATION_REQUIRED, + BLOCKS_REQUIRED, + MIN_PLAYERS_REQUIRED, + MAX_PLAYERS_REQUIRED + } + + public record Block(@NotNull ItemStack block, int chance) { + public Map toMap() { + Map map = new HashMap<>(); + map.put("block", block.getType().name()); + map.put("chance", chance); + return map; + } + + public static Block fromMap(Map map) { + return new Block(new ItemStack(Material.valueOf(((String) map.get("block")).toUpperCase())), (int) map.get("chance")); + } + } + } diff --git a/src/main/java/hdvtdev/blockAndSeek/CommandListener.java b/src/main/java/hdvtdev/blockAndSeek/CommandListener.java index 0a2a51c..0cc37ff 100644 --- a/src/main/java/hdvtdev/blockAndSeek/CommandListener.java +++ b/src/main/java/hdvtdev/blockAndSeek/CommandListener.java @@ -1,15 +1,19 @@ package hdvtdev.blockAndSeek; -import hdvtdev.blockAndSeek.managers.ConfigManager; -import hdvtdev.blockAndSeek.managers.FreezeManager; -import hdvtdev.blockAndSeek.managers.GamesManager; +//import com.mojang.brigadier.arguments.StringArgumentType; +//import com.mojang.brigadier.builder.LiteralArgumentBuilder; +//import com.mojang.brigadier.builder.RequiredArgumentBuilder; + +import hdvtdev.blockAndSeek.managers.*; +import me.libraryaddict.disguise.DisguiseAPI; +import me.libraryaddict.disguise.disguisetypes.DisguiseType; +import me.libraryaddict.disguise.disguisetypes.MiscDisguise; import net.kyori.adventure.text.minimessage.MiniMessage; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.command.Command; -import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandSender; -import org.bukkit.command.TabCompleter; +import org.bukkit.command.TabExecutor; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.ItemMeta; @@ -18,11 +22,9 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.Set; +import java.util.*; -public class CommandListener implements CommandExecutor, TabCompleter { +public class CommandListener implements TabExecutor { private static final MiniMessage miniMessage = MiniMessage.miniMessage(); @@ -37,6 +39,7 @@ public class CommandListener implements CommandExecutor, TabCompleter { return true; } + switch (args[0]) { case "test" -> { switch (GamesManager.createGame("dust2")) { @@ -55,28 +58,32 @@ public class CommandListener implements CommandExecutor, TabCompleter { player.sendMessage("eq: " + player.locale().toLanguageTag().equals("en-US")); } } - case "freeze" -> { + case "menu" -> { if (sender instanceof Player player) { - ItemStack foo = new ItemStack(Material.HEART_OF_THE_SEA); - ItemMeta fooMeta = foo.getItemMeta(); - fooMeta.displayName(MiniMessage.miniMessage().deserialize("Freeze")); - fooMeta.getPersistentDataContainer().set(BlockAndSeekContainer.FREEZE_ITEM, PersistentDataType.BOOLEAN, true); - foo.setItemMeta(fooMeta); - player.getInventory().addItem(foo); + GuiManager.Menu.open(player); } } - case "f" -> { - if (sender instanceof Player player) { - FreezeManager.freeze(player, Material.TARGET); + + + case "morph" -> { + if (sender instanceof Player player && args.length == 2) { + Material material = Material.valueOf(args[1].toUpperCase()); + + FreezeManager.addPlayerDisguise(player, material.createBlockData()); + DisguiseAPI.disguiseToAll(player, new MiscDisguise(DisguiseType.FALLING_BLOCK, new ItemStack(material))); + } + } + case "armor" -> { + if (sender instanceof Player player) { + player.getInventory().setArmorContents(ItemManager.getSeekerArmor()); } } - case "foo" -> { if (sender instanceof Player player) { ItemStack foo = new ItemStack(Material.BROWN_DYE); ItemMeta fooMeta = foo.getItemMeta(); fooMeta.displayName(MiniMessage.miniMessage().deserialize("SoundMaker3000")); - fooMeta.getPersistentDataContainer().set(BlockAndSeekContainer.SOUND_ITEM, PersistentDataType.BOOLEAN, true); + fooMeta.getPersistentDataContainer().set(Keys.SOUND_ITEM, PersistentDataType.BOOLEAN, true); foo.setItemMeta(fooMeta); player.getInventory().addItem(foo); } @@ -88,13 +95,36 @@ public class CommandListener implements CommandExecutor, TabCompleter { case "list" -> { StringBuilder buffer = new StringBuilder(Localization.get("maps-available")); String listElement = Localization.get("maps-available-element"); - Set readyMaps = ConfigManager.getReadyMaps(); + Set readyMaps = null; //TODO ConfigManager.getReadyMaps(); for (String map : ConfigManager.getAllMaps()) { buffer.append("\n").append(listElement.replace("{map}", map).replace("{color-status}", readyMaps.contains(map) ? "" : "")); } sender.sendMessage(miniMessage.deserialize(buffer.toString())); } + default -> { + if (ConfigManager.getAllMaps().contains(args[1])) { + if (argsLen > 3) { + switch (args[2]) { + case "setduration" -> { + try { + int duration = Integer.parseInt(args[3]); + if (duration > 0) { + sender.sendMessage(args[1]); + var map = MapsManager.getMap(args[1]); + map.setDuration(duration); + MapsManager.saveMap(args[1], map); + sender.sendMessage("duration set"); + } else sender.sendMessage("idk"); + + } catch (NumberFormatException | IOException e) { + sender.sendMessage("idk"); + } + } + } + } + } + } } } else sender.sendMessage(Localization.getComponent("not-enough-arguments", "{command}", "/blockandseek map", "{help}", "[MAP NAME | COMMANDS]")); @@ -132,64 +162,64 @@ public class CommandListener implements CommandExecutor, TabCompleter { } - /* - - */ - return true; } + @Override public @Nullable List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { - boolean hasPermission = sender.hasPermission("blockandseek.manage"); - return switch (args.length) { - case 1 -> { - if (hasPermission) { - yield List.of("reload", "help", "map"); - } else yield List.of("help"); - } + return !sender.hasPermission("blockandseek.manage") ? List.of() : switch (args.length) { + case 1 -> List.of("info", "map", "reload"); case 2 -> switch (args[0]) { + case "reload" -> List.of("localization", "config"); case "map" -> { - List scmds = new ArrayList<>(); - - if (hasPermission) { - scmds.addAll(List.of("create", "list")); - scmds.addAll(ConfigManager.getAllMaps()); - } - - - yield scmds; - } - case "reload" -> { - if (hasPermission) { - yield List.of("localization", "config"); - } else yield List.of(); + List mapCommands = new ArrayList<>(MapsManager.getAllMaps()); + mapCommands.add("create"); + mapCommands.add("list"); + yield mapCommands; } default -> List.of(); }; - case 3 -> switch (args[1]) { - case "create", "list" -> List.of(); - default -> { - - if (ConfigManager.getAllMaps().contains(args[1]) && hasPermission) { - yield List.of("setspawn", "setlobby", "setduration", "setmaxplayers", "setminplayers", "status", "remove"); - } else yield List.of(); - + case 3 -> { + if (MapsManager.getAllMaps().contains(args[1])) { + yield List.of("status", "setspawn", "setlobby", "setduration", "setminplayers", "setmaxplayers", "addblock", "deleteblock"); } - }; + yield List.of(); + } case 4 -> switch (args[2]) { case "setspawn", "setlobby" -> { - if (hasPermission && sender instanceof Player player) { - Location location = player.getLocation(); - yield List.of(String.format("%.1f %.0f %.1f", location.getX(), location.getY(), location.getZ())); + if (sender instanceof Player player) { + Location location = player.getLocation().toCenterLocation(); + yield List.of(String.format("%s %s %s", location.getX(), location.getY(), location.getZ())); } else yield List.of(); } + case "setduration" -> List.of("[]"); + case "setminplayers", "setmaxplayers" -> List.of("[]"); + case "addblock", "removeblock" -> + Arrays.stream(Material.values()).map(Objects::toString).map(String::toLowerCase).toList(); default -> List.of(); }; - + case 5 -> args[2].equalsIgnoreCase("addblock") ? List.of("[]") : List.of(); default -> List.of(); }; } + + /* TODO + public static void registerCompletions(Commodore commodore, PluginCommand command) { + commodore.register(command, LiteralArgumentBuilder.literal("blockandseek") + .then(RequiredArgumentBuilder.argument("idk", StringArgumentType.greedyString()).suggests((a, b) -> { + b.suggest("sometextidk"); + + return b.buildFuture(); + }).executes(o -> { + System.out.println(23238923); + return 1; + }))); + } + + */ + + } diff --git a/src/main/java/hdvtdev/blockAndSeek/EventListener.java b/src/main/java/hdvtdev/blockAndSeek/EventListener.java index 2427dca..f14e7a2 100644 --- a/src/main/java/hdvtdev/blockAndSeek/EventListener.java +++ b/src/main/java/hdvtdev/blockAndSeek/EventListener.java @@ -3,42 +3,56 @@ package hdvtdev.blockAndSeek; import hdvtdev.blockAndSeek.managers.FreezeManager; import hdvtdev.blockAndSeek.managers.GamesManager; +import hdvtdev.blockAndSeek.managers.GuiManager; +import hdvtdev.blockAndSeek.managers.ItemManager; import me.libraryaddict.disguise.DisguiseAPI; import me.libraryaddict.disguise.disguisetypes.DisguiseType; import me.libraryaddict.disguise.disguisetypes.MiscDisguise; import org.bukkit.Bukkit; -import org.bukkit.Material; import org.bukkit.entity.ArmorStand; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.block.Action; -import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.block.BlockDamageEvent; +import org.bukkit.event.block.BlockPlaceEvent; +import org.bukkit.event.entity.EntityDismountEvent; import org.bukkit.event.entity.PlayerDeathEvent; import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.event.inventory.InventoryCloseEvent; +import org.bukkit.event.inventory.InventoryType; +import org.bukkit.event.player.PlayerDropItemEvent; import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.event.player.PlayerQuitEvent; import org.bukkit.inventory.EquipmentSlot; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; import org.bukkit.inventory.ItemStack; import org.bukkit.persistence.PersistentDataContainer; import org.bukkit.persistence.PersistentDataType; -import org.bukkit.potion.PotionEffect; -import org.bukkit.potion.PotionEffectType; +import org.bukkit.scheduler.BukkitScheduler; import org.bukkit.scheduler.BukkitTask; import org.bukkit.util.Vector; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; public class EventListener implements Listener { + private static final BukkitScheduler scheduler = Bukkit.getScheduler(); + + private static final Set coolDown = ConcurrentHashMap.newKeySet(); + private static final ConcurrentHashMap tasks = new ConcurrentHashMap<>(); private static final ConcurrentHashMap soundCoolDown = new ConcurrentHashMap<>(); - private static final ConcurrentHashMap frozenPlayers = new ConcurrentHashMap<>(); + private static final Vector ZERO_VELOCITY = new Vector(0, 0, 0); - private static final PotionEffect INVISIBILITY = new PotionEffect(PotionEffectType.INVISIBILITY, PotionEffect.INFINITE_DURATION, 2, false, false); + private static final ItemStack freezeItem = ItemManager.getFreezeItem(); + private static final ItemStack menuItem = ItemManager.getMenuItem(); public static void createTask(Player player, BukkitTask bukkitTask) { tasks.put(player, bukkitTask); @@ -49,15 +63,40 @@ public class EventListener implements Listener { task.cancel(); } + @EventHandler + public void onDrop(PlayerDropItemEvent event) { + event.setCancelled(true); + } + + @EventHandler + public void onBlockDamage(BlockDamageEvent event) { + + if (FreezeManager.unfreeze(event.getBlock().getBlockData())) event.setCancelled(true); + } + + @EventHandler + public void onPlayerJoin(PlayerJoinEvent event) { + Player player = event.getPlayer(); + if (player.getVehicle() instanceof ArmorStand armorStand) { + armorStand.getPersistentDataContainer().remove(Keys.FROZEN_PLAYER); + armorStand.remove(); + } + + Inventory inventory = player.getInventory(); + if (!inventory.contains(ItemManager.getMenuItem())) inventory.addItem(ItemManager.getMenuItem()); + + } + @EventHandler public void onPlayerQuit(PlayerQuitEvent event) { Player player = event.getPlayer(); PersistentDataContainer container = player.getPersistentDataContainer(); - String arena = container.get(BlockAndSeekContainer.PLAYER, PersistentDataType.STRING); + String arena = container.get(Keys.PLAYER, PersistentDataType.STRING); if (arena != null) { - container.remove(BlockAndSeekContainer.PLAYER); + container.remove(Keys.PLAYER); GamesManager.get(arena).removePlayer(player); + FreezeManager.removePlayerDisguise(player); } } @@ -66,11 +105,12 @@ public class EventListener implements Listener { } - @EventHandler - public void onDamage(EntityDamageByEntityEvent event) { - - + public void onEntityDismount(EntityDismountEvent event) { + Player player = (Player) event.getEntity(); + if (event.getDismounted() instanceof ArmorStand armorStand && armorStand.getPersistentDataContainer().has(Keys.FROZEN_PLAYER)) { + FreezeManager.freeze(player); + } } @@ -78,41 +118,96 @@ public class EventListener implements Listener { public void onRightClick(PlayerInteractEvent event) { if (event.getHand() != EquipmentSlot.HAND) return; Action action = event.getAction(); - if (action == Action.RIGHT_CLICK_BLOCK || action == Action.RIGHT_CLICK_AIR) { - Player player = event.getPlayer(); - if (player.getInventory().getItemInMainHand().equals(new ItemStack(Material.HEART_OF_THE_SEA))) { - FreezeManager.freeze(player, Material.TARGET); + Player player = event.getPlayer(); + if (action.isRightClick() && !coolDown.contains(player)) { + + + ItemStack itemInHand = player.getInventory().getItemInMainHand(); + + if (itemInHand.equals(freezeItem)) { + coolDown.add(player); + scheduler.runTaskLater(BlockAndSeek.getInstance(), () -> coolDown.remove(player), 3L); + FreezeManager.freeze(player); + event.setCancelled(true); + } else if (itemInHand.equals(menuItem)) { + GuiManager.Menu.open(player); } - } else event.getPlayer().sendMessage(action.toString()); + } + + } + + @EventHandler + public void onBlockPlacement(BlockPlaceEvent event) { + event.setCancelled(true); + } + + @EventHandler + public void onBlockBreak(BlockBreakEvent event) { + event.setCancelled(true); } @EventHandler - public void onClick(InventoryClickEvent event) { + public void onInventoryClick(InventoryClickEvent event) { Player player = (Player) event.getWhoClicked(); + Inventory inventory = event.getClickedInventory(); + + if (inventory != null) { + InventoryHolder holder = inventory.getHolder(); + switch (holder) { + case GuiManager.Menu ignored -> { + switch (event.getSlot()) { + case 13 -> { + GuiManager.Menu.Games.open(player); + } + default -> { + } + } + event.setCancelled(true); + } + case GuiManager.Menu.Games ignored -> { + ItemStack game = event.getCurrentItem(); + if (game != null) { + String gameName = game.getItemMeta().getPersistentDataContainer().get(Keys.GAME, PersistentDataType.STRING); + if (gameName != null) GamesManager.get(gameName).addPlayer(player); + } + event.setCancelled(true); + } + case null, default -> { + } + } + } if (player.hasMetadata("RollingMenu")) { event.setCancelled(true); int slot = event.getSlot(); if (slot == 21 || slot == 23 || slot == 25) { if (!tasks.containsKey(player)) { - MiscDisguise miscDisguise = new MiscDisguise(DisguiseType.FALLING_BLOCK, event.getInventory().getItem(slot)); + ItemStack prop = event.getInventory().getItem(slot); + FreezeManager.addPlayerDisguise(player, prop.getType().createBlockData()); + MiscDisguise miscDisguise = new MiscDisguise(DisguiseType.FALLING_BLOCK, prop); DisguiseAPI.disguiseToAll(player, miscDisguise); player.closeInventory(InventoryCloseEvent.Reason.UNKNOWN); } } + } else if (event.getSlotType().equals(InventoryType.SlotType.ARMOR)) { + event.setCancelled(true); } + + } @EventHandler - public void onClose(InventoryCloseEvent event) { + public void onInventoryClose(InventoryCloseEvent event) { Player player = (Player) event.getPlayer(); if (player.hasMetadata("RollingMenu")) { if (!tasks.containsKey(player)) { player.removeMetadata("RollingMenu", BlockAndSeek.getInstance()); if (!event.getReason().equals(InventoryCloseEvent.Reason.UNKNOWN)) { - MiscDisguise miscDisguise = new MiscDisguise(DisguiseType.FALLING_BLOCK, event.getInventory().getItem(21)); + ItemStack prop = event.getInventory().getItem(21); + FreezeManager.addPlayerDisguise(player, prop.getType().createBlockData()); + MiscDisguise miscDisguise = new MiscDisguise(DisguiseType.FALLING_BLOCK, prop); DisguiseAPI.disguiseToAll(player, miscDisguise); } } else { diff --git a/src/main/java/hdvtdev/blockAndSeek/BlockAndSeekContainer.java b/src/main/java/hdvtdev/blockAndSeek/Keys.java similarity index 80% rename from src/main/java/hdvtdev/blockAndSeek/BlockAndSeekContainer.java rename to src/main/java/hdvtdev/blockAndSeek/Keys.java index 0cd54f5..d1a7ce0 100644 --- a/src/main/java/hdvtdev/blockAndSeek/BlockAndSeekContainer.java +++ b/src/main/java/hdvtdev/blockAndSeek/Keys.java @@ -5,12 +5,15 @@ import org.bukkit.NamespacedKey; import org.bukkit.scoreboard.Scoreboard; import org.bukkit.scoreboard.Team; -public class BlockAndSeekContainer { +public class Keys { public static final NamespacedKey SOUND_ITEM = new NamespacedKey(BlockAndSeek.getInstance(), "BlockAndSeekSoundItem"); public static final NamespacedKey FREEZE_ITEM = new NamespacedKey(BlockAndSeek.getInstance(), "BlockAndSeekFreezeItem"); + public static final NamespacedKey MENU_ITEM = new NamespacedKey(BlockAndSeek.getInstance(), "BlockAndSeekMenuItem"); + public static final NamespacedKey FROZEN_PLAYER = new NamespacedKey(BlockAndSeek.getInstance(), "BlockAndSeekFrozenPlayer"); public static final NamespacedKey PLAYER = new NamespacedKey(BlockAndSeek.getInstance(), "BlockAndSeekPlayer"); + public static final NamespacedKey GAME = new NamespacedKey(BlockAndSeek.getInstance(), "BlockAndSeekGame"); public static final Team NO_COLLIDE_TEAM; diff --git a/src/main/java/hdvtdev/blockAndSeek/managers/ConfigManager.java b/src/main/java/hdvtdev/blockAndSeek/managers/ConfigManager.java index bc84c32..46ad566 100644 --- a/src/main/java/hdvtdev/blockAndSeek/managers/ConfigManager.java +++ b/src/main/java/hdvtdev/blockAndSeek/managers/ConfigManager.java @@ -3,16 +3,15 @@ package hdvtdev.blockAndSeek.managers; import hdvtdev.blockAndSeek.BlockAndSeek; import hdvtdev.blockAndSeek.BlockAndSeekMap; import hdvtdev.blockAndSeek.Localization; -import org.bukkit.Material; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.file.YamlConfiguration; -import org.bukkit.inventory.ItemStack; -import org.jetbrains.annotations.Nullable; import java.io.File; import java.io.IOException; import java.io.InputStreamReader; -import java.util.*; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; public class ConfigManager { @@ -31,99 +30,6 @@ public class ConfigManager { return YamlConfiguration.loadConfiguration(mapsFile).getKeys(false); } - public enum MapStatus { - SPAWN_REQUIRED, - LOBBY_REQUIRED, - DURATION_REQUIRED, - BLOCKS_REQUIRED, - MIN_PLAYERS_REQUIRED, - MAX_PLAYERS_REQUIRED - } - - public static Set getReadyMaps() { - return maps.keySet(); - } - - public static @Nullable Set checkMap(String name) { - - Set status = new HashSet<>(); - - YamlConfiguration configuration = YamlConfiguration.loadConfiguration(mapsFile); - ConfigurationSection section = configuration.getConfigurationSection(name); - - if (section != null) { - List spawn = section.getIntegerList("spawn"); - if (spawn.size() != 3) status.add(MapStatus.SPAWN_REQUIRED); - List lobby = section.getIntegerList("lobby"); - if (lobby.size() != 3) status.add(MapStatus.LOBBY_REQUIRED); - int duration = section.getInt("duration"); - if (duration <= 0) status.add(MapStatus.DURATION_REQUIRED); - int minPlayers = section.getInt("min-players"); - if (minPlayers <= 0) status.add(MapStatus.MIN_PLAYERS_REQUIRED); - int maxPlayers = section.getInt("max-players"); - if (maxPlayers <= 0) status.add(MapStatus.MAX_PLAYERS_REQUIRED); - List blocks = section.getMapList("blocks").stream().map( - block -> { - try { - return new BlockAndSeekMap.Block( - new ItemStack(Material.valueOf(((String) block.get("block")).toUpperCase())), - (int) block.get("chance") - ); - } catch (IllegalArgumentException | ClassCastException ignored) { - return null; - } - - } - ).toList(); - if (blocks.isEmpty() || new HashSet<>(blocks).size() == 1) status.add(MapStatus.BLOCKS_REQUIRED); - - if (status.isEmpty()) - maps.put(name, new BlockAndSeekMap(spawn, lobby, duration, minPlayers, maxPlayers, blocks)); - - } else return null; - - return status; - } - - public static boolean addDefaultMap(String name) { - - YamlConfiguration configuration = YamlConfiguration.loadConfiguration(mapsFile); - - if (configuration.get(name) != null) return false; - - ConfigurationSection section = configuration.createSection(name); - section.set("spawn", List.of()); - section.set("lobby", List.of()); - section.set("duration", 0); - section.set("min-players", 0); - section.set("max-players", 0); - section.set("blocks", List.of(Map.of())); - - try { - configuration.save(mapsFile); - } catch (IOException ignored) { - return false; - } - - return true; - } - - public static boolean setMapProperty(String map, String property, Object value) { - YamlConfiguration configuration = YamlConfiguration.loadConfiguration(mapsFile); - ConfigurationSection section = configuration.getConfigurationSection(map); - - if (section != null) { - section.set(property, value); - try { - configuration.save(mapsFile); - } catch (IOException e) { - return false; - } - } - - return true; - } - public static void loadAll() throws IOException { load("config.yml"); load("localization.yml"); @@ -166,50 +72,13 @@ public class ConfigManager { switch (file) { case "config.yml" -> loadConfig(conf, defaultConfiguration); case "localization.yml" -> loadLocalization(conf, defaultConfiguration); - case "maps.yml" -> loadMaps(conf); + case "maps.yml" -> MapsManager.loadMaps(); } } } - private static void loadMaps(File configurationFile) throws IOException { - Map confMap = new ConcurrentHashMap<>(); - YamlConfiguration configuration = YamlConfiguration.loadConfiguration(configurationFile); - - for (String map : configuration.getKeys(false)) { - ConfigurationSection section = configuration.getConfigurationSection(map); - if (section != null) { - List spawn = section.getIntegerList("spawn"); - List lobby = section.getIntegerList("lobby"); - int duration = section.getInt("duration"); - int minPlayers = section.getInt("min-players"); - int maxPlayers = section.getInt("max-players"); - List blocks = section.getMapList("blocks").stream().map( - block -> { - try { - return new BlockAndSeekMap.Block( - new ItemStack(Material.valueOf(((String) block.get("block")).toUpperCase())), - (int) block.get("chance") - ); - } catch (IllegalArgumentException | ClassCastException | NullPointerException ignored) { - return null; - - } - - } - ).filter(Objects::nonNull).toList(); - - if (!spawn.isEmpty() && !lobby.isEmpty() && duration > 0 && !blocks.isEmpty() && minPlayers > 0 && maxPlayers > 0) { - confMap.put(map, new BlockAndSeekMap(spawn, lobby, duration, minPlayers, maxPlayers, blocks)); - } - } - - maps = confMap; - } - - - } private static void loadConfig(File configurationFile, YamlConfiguration defaultConfiguration) throws IOException { ConcurrentHashMap confMap = new ConcurrentHashMap<>(); diff --git a/src/main/java/hdvtdev/blockAndSeek/managers/FreezeManager.java b/src/main/java/hdvtdev/blockAndSeek/managers/FreezeManager.java index cf67491..b6f0ec6 100644 --- a/src/main/java/hdvtdev/blockAndSeek/managers/FreezeManager.java +++ b/src/main/java/hdvtdev/blockAndSeek/managers/FreezeManager.java @@ -1,15 +1,16 @@ package hdvtdev.blockAndSeek.managers; -import hdvtdev.blockAndSeek.BlockAndSeekContainer; +import hdvtdev.blockAndSeek.Keys; import me.libraryaddict.disguise.DisguiseAPI; import me.libraryaddict.disguise.disguisetypes.Disguise; -import org.bukkit.Bukkit; +import me.libraryaddict.disguise.disguisetypes.DisguiseType; +import me.libraryaddict.disguise.disguisetypes.MiscDisguise; import org.bukkit.Location; -import org.bukkit.Material; import org.bukkit.block.Block; import org.bukkit.block.data.BlockData; import org.bukkit.entity.ArmorStand; import org.bukkit.entity.Player; +import org.bukkit.persistence.PersistentDataType; import org.bukkit.util.Vector; import java.util.Map; @@ -18,26 +19,58 @@ import java.util.concurrent.ConcurrentHashMap; public class FreezeManager { private static final Map frozenPlayers = new ConcurrentHashMap<>(); + private static final Map playerDisguise = new ConcurrentHashMap<>(); + private static final Map disguisePlayer = new ConcurrentHashMap<>(); - private static final Vector velocity = new Vector(0, 0, 0); + private static final Disguise hideDisguise = new MiscDisguise(DisguiseType.LLAMA_SPIT); + private static final Vector zeroVelocity = new Vector(0, 0, 0); - public static boolean freeze(Player player, Material material) { + public static void addPlayerDisguise(Player player, BlockData blockData) { + disguisePlayer.put(blockData, player); + playerDisguise.put(player, blockData); + } + + public static void removePlayerDisguise(Player player) { + disguisePlayer.remove(playerDisguise.remove(player)); + } + + public static void unfreezeIfFrozen(Player player) { Location location = player.getLocation(); - if (frozenPlayers.containsKey(player)) { - FreezeData data = frozenPlayers.remove(player); - Location blockLocation = location.getBlock().getLocation(); - for (Player p : Bukkit.getOnlinePlayers()) { - p.sendBlockChange(blockLocation, data.blockData); - } - ArmorStand armorStand = data.armorStand; - armorStand.removePassenger(player); - armorStand.remove(); - player.setInvulnerable(false); + FreezeData data = frozenPlayers.remove(player); + if (data != null) { + unfreeze(player, location, data); + } + } - BlockAndSeekContainer.NO_COLLIDE_TEAM.removeEntry(player.getName()); + public static boolean unfreeze(BlockData blockData) { + Player player = disguisePlayer.get(blockData); + if (player != null) { + freeze(player); + return true; + } else return false; + } - if (data.disguise() != null) DisguiseAPI.disguiseToAll(player, data.disguise); + private static void unfreeze(Player player, Location location, FreezeData data) { + Location blockLocation = location.getBlock().getLocation(); + location.getWorld().setBlockData(blockLocation, data.blockData); + player.getPersistentDataContainer().remove(Keys.FROZEN_PLAYER); + + + data.armorStand.remove(); + player.setInvulnerable(false); + + Keys.NO_COLLIDE_TEAM.removeEntry(player.getName()); + + if (data.disguise != null) DisguiseAPI.disguiseToAll(player, data.disguise); + + } + + public static boolean freeze(Player player) { + Location location = player.getLocation(); + FreezeData data = frozenPlayers.remove(player); + if (data != null) { + unfreeze(player, location, data); } else { Block block = location.getBlock(); @@ -45,35 +78,37 @@ public class FreezeManager { Location blockLocation = block.getLocation(); Location centerLocation = blockLocation.toCenterLocation(); Location upperBlockLocation = centerLocation.clone(); - upperBlockLocation.setY(upperBlockLocation.getY() + 1); + upperBlockLocation.setY(upperBlockLocation.getY() + 0.25); if (!upperBlockLocation.getBlock().isSolid() && !blockLocation.getBlock().isSolid()) { - for (Player p : Bukkit.getOnlinePlayers()) { - p.sendBlockChange(blockLocation, material.createBlockData()); - } + location.getWorld().setBlockData(blockLocation, playerDisguise.get(player)); - centerLocation.setY(centerLocation.getY() - 0.5); + centerLocation.setY(centerLocation.getY() - 0.85); - player.setVelocity(velocity); + player.setVelocity(zeroVelocity); player.setInvulnerable(true); - BlockAndSeekContainer.NO_COLLIDE_TEAM.addEntry(player.getName()); + player.getPersistentDataContainer().set(Keys.FROZEN_PLAYER, PersistentDataType.BOOLEAN, true); + Keys.NO_COLLIDE_TEAM.addEntry(player.getName()); - ArmorStand armorStand = location.getWorld().spawn(centerLocation, ArmorStand.class); - armorStand.setInvulnerable(true); - armorStand.setSmall(true); - armorStand.setGravity(true); - armorStand.setCanMove(false); - armorStand.setInvisible(true); - armorStand.setCollidable(false); - armorStand.addPassenger(player); + ArmorStand armorStand = location.getWorld().spawn(centerLocation, ArmorStand.class, stand -> { + stand.setVisible(false); + stand.setVisible(false); + stand.setCollidable(false); + stand.setGravity(true); + stand.setSmall(true); + stand.setCanMove(false); + stand.addPassenger(player); + stand.getPersistentDataContainer().set(Keys.FROZEN_PLAYER, PersistentDataType.BOOLEAN, true); + stand.setInvulnerable(true); + }); Disguise disguise = DisguiseAPI.getDisguise(player); - DisguiseAPI.undisguiseToAll(player); + DisguiseAPI.disguiseToAll(player, hideDisguise); frozenPlayers.put(player, new FreezeData(armorStand, blockData, disguise)); diff --git a/src/main/java/hdvtdev/blockAndSeek/managers/GamesManager.java b/src/main/java/hdvtdev/blockAndSeek/managers/GamesManager.java index b3c0a6d..84f13e5 100644 --- a/src/main/java/hdvtdev/blockAndSeek/managers/GamesManager.java +++ b/src/main/java/hdvtdev/blockAndSeek/managers/GamesManager.java @@ -11,6 +11,7 @@ import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.WorldCreator; import org.bukkit.entity.Player; +import org.bukkit.inventory.PlayerInventory; import org.bukkit.scheduler.BukkitRunnable; import org.bukkit.scoreboard.Criteria; import org.bukkit.scoreboard.DisplaySlot; @@ -43,19 +44,21 @@ public class GamesManager { } else return 2; } - BlockAndSeekMap map = ConfigManager.getMap(name); - BlockAndSeekGame game = new BlockAndSeekGame(name, map.maxPlayers()); - List spawnCords = map.spawn(); - List lobbyCords = map.lobby(); + BlockAndSeekMap map = MapsManager.getMap(name); + System.out.println(map); + BlockAndSeekGame game = new BlockAndSeekGame(name, map.getMaxPlayers(), map.getLobby()); + List spawnCords = map.getSpawn(); + List lobbyCords = map.getLobby(); games.put(name, game); new BukkitRunnable() { - int duration = map.duration(); + int duration = map.getDuration() + 10; + final int seekerSpawn = duration - 15; + int gameEnd = duration - 10; int waitTime = 30; final Location spawn = new Location(Bukkit.getWorld(name), spawnCords.getFirst(), spawnCords.get(1), spawnCords.get(2)); - final Location lobby = new Location(Bukkit.getWorld(name), lobbyCords.getFirst(), lobbyCords.get(1), lobbyCords.get(2)); - + Player seeker; @Override public void run() { @@ -67,15 +70,18 @@ public class GamesManager { player.sendActionBar(Component.text("Игроков " + playerCount + "/12")); //TODO! } - if (playerCount >= map.minPlayers()) { + if (playerCount >= map.getMinPlayers()) { - if (waitTime == 0 || playerCount == map.maxPlayers()) { + if (waitTime == 0 || playerCount == map.getMaxPlayers()) { game.start(); - Player seeker = game.selectRandomSeeker(); - seeker.teleport(spawn); + seeker = game.selectRandomSeeker(); + for (Player player : game.getHiders()) { player.teleport(spawn); - RouletteCreator.createRoulette(player, null, true, map.blocks()); + PlayerInventory inventory = player.getInventory(); + inventory.clear(); + inventory.addItem(ItemManager.getFreezeItem()); + RouletteCreator.createRoulette(player, null, true, map.getBlocks()); } } else { @@ -91,25 +97,31 @@ public class GamesManager { } else { + if (seekerSpawn == gameEnd) { + seeker.teleport(spawn); + PlayerInventory seekerInventory = seeker.getInventory(); + seekerInventory.setArmorContents(ItemManager.getSeekerArmor()); + } + if (game.hidersCount() == 0) { for (Player player : game.getPlayers()) { player.showTitle(Title.title(Localization.getComponent("seekers-won"), Component.text(""))); } - game.end(); + game.preEnd(); games.remove(name); - this.cancel(); + } for (Player player : game.getPlayers()) { - player.sendActionBar(Localization.getComponent("game-time-left", "{time}", String.valueOf(duration))); + player.sendActionBar(Localization.getComponent("game-time-left", "{time}", String.valueOf(gameEnd))); } - if (duration == 0) { + if (gameEnd == 0) { if (game.hidersCount() == 1) { for (Player player : game.getPlayers()) { - player.showTitle(Title.title(Localization.getComponent("hiders-solo-win", "{player}", player.getName()), Component.text(""))); + player.showTitle(Title.title(Localization.getComponent("hiders-solo-win", "{player}", game.getLastHider().getName()), Component.text(""))); } } else { for (Player player : game.getPlayers()) { @@ -117,11 +129,14 @@ public class GamesManager { } } - game.end(); + game.preEnd(); games.remove(name); - this.cancel(); - } else duration--; + } else gameEnd--; + + if (duration == 0) { + this.cancel(); + } } @@ -145,7 +160,7 @@ public class GamesManager { scoreboard.resetScores(o); } - + objective.getScore(" ").setScore(3); objective.getScore(Localization.getComponent("game-players-count", "{players}", String.valueOf(players), "{max-players}", String.valueOf(maxPlayers)).toString()).setScore(2); objective.getScore(" ").setScore(1); diff --git a/src/main/java/hdvtdev/blockAndSeek/managers/GuiManager.java b/src/main/java/hdvtdev/blockAndSeek/managers/GuiManager.java new file mode 100644 index 0000000..e29108e --- /dev/null +++ b/src/main/java/hdvtdev/blockAndSeek/managers/GuiManager.java @@ -0,0 +1,106 @@ +package hdvtdev.blockAndSeek.managers; + +import hdvtdev.blockAndSeek.Keys; +import hdvtdev.blockAndSeek.Localization; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.persistence.PersistentDataType; +import org.jetbrains.annotations.NotNull; + +public class GuiManager { + + private static final ItemStack filler1 = new ItemStack(Material.ORANGE_STAINED_GLASS_PANE); + private static final ItemStack filler2 = new ItemStack(Material.BLUE_STAINED_GLASS_PANE); + private static final ItemStack filler3 = new ItemStack(Material.GREEN_STAINED_GLASS_PANE); + + private GuiManager() { + } + + public static void fill(Inventory inventory) { + int size = inventory.getSize(); + for (int i = 0; i < size; i++) { + inventory.setItem(i, i % 2 == 0 ? filler1 : filler2); + } + } + + public static void fillAlt(Inventory inventory) { + int size = inventory.getSize(); + for (int i = 0; i < size; i++) { + inventory.setItem(i, i % 2 == 0 ? filler3 : filler2); + } + } + + + public static class Menu implements InventoryHolder { + + public static final Menu instance = new Menu(); + + private static final Inventory mainMenu = Bukkit.createInventory(instance, 27, Localization.getComponent("menu-item")); + + static { + fill(mainMenu); + mainMenu.setItem(13, ItemManager.getGamesPageItem()); + } + + private Menu() { + } + + public static void open(Player player) { + player.openInventory(mainMenu); + } + + + @Override + public @NotNull Inventory getInventory() { + return mainMenu; + } + + public static class Games implements InventoryHolder { + + private static final ItemStack defaultGameItem = new ItemStack(Material.CLOCK); + + private Games() { + } + + public static void open(Player player) { + + Inventory gamesMenu = Bukkit.createInventory(new Games(), 45, Localization.getComponent("menu-item")); + for (String game : GamesManager.getAvailableGames()) { + ItemStack gameItem = defaultGameItem.clone(); + ItemMeta meta = gameItem.getItemMeta(); + meta.getPersistentDataContainer().set(Keys.GAME, PersistentDataType.STRING, game); + meta.displayName(Localization.getComponent("game-name", "{name}", game)); + gameItem.setItemMeta(meta); + gamesMenu.addItem(gameItem); + } + + + if (player.hasPermission("blockandseek.manage")) { + gamesMenu.setItem(44, ItemManager.getCreateGameButton()); + } + + player.openInventory(gamesMenu); + } + + @Override + public @NotNull Inventory getInventory() { + return Bukkit.createInventory(null, 9); + } + + + public static abstract class Maps implements InventoryHolder { + + } + + } + + + } + + +} diff --git a/src/main/java/hdvtdev/blockAndSeek/managers/ItemManager.java b/src/main/java/hdvtdev/blockAndSeek/managers/ItemManager.java new file mode 100644 index 0000000..74a7b8c --- /dev/null +++ b/src/main/java/hdvtdev/blockAndSeek/managers/ItemManager.java @@ -0,0 +1,98 @@ +package hdvtdev.blockAndSeek.managers; + +import hdvtdev.blockAndSeek.Keys; +import hdvtdev.blockAndSeek.Localization; +import org.bukkit.Color; +import org.bukkit.Material; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemFlag; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.PlayerInventory; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.inventory.meta.LeatherArmorMeta; +import org.bukkit.persistence.PersistentDataType; + +public class ItemManager { + + private static final ItemStack freezeItem = new ItemStack(Material.HEART_OF_THE_SEA); + + private static final ItemStack menuItem = new ItemStack(Material.COMPASS); + private static final ItemStack games = new ItemStack(Material.BOOKSHELF); + private static final ItemStack createGameButton = new ItemStack(Material.SLIME_BALL); + + private static final ItemStack[] seekerSet; + + static { + + ItemMeta freezeMeta = freezeItem.getItemMeta(); + freezeMeta.displayName(Localization.getComponent("freeze-item")); + freezeMeta.getPersistentDataContainer().set(Keys.FREEZE_ITEM, PersistentDataType.BOOLEAN, true); + freezeItem.setItemMeta(freezeMeta); + + ItemMeta menuMeta = menuItem.getItemMeta(); + menuMeta.displayName(Localization.getComponent("menu-item")); + menuMeta.getPersistentDataContainer().set(Keys.MENU_ITEM, PersistentDataType.BOOLEAN, true); + menuMeta.addEnchant(Enchantment.ARROW_INFINITE, 1, true); + menuMeta.addItemFlags(ItemFlag.HIDE_ENCHANTS); + menuItem.setItemMeta(menuMeta); + + ItemMeta gamesMeta = games.getItemMeta(); + gamesMeta.displayName(Localization.getComponent("games-page-item")); + games.setItemMeta(gamesMeta); + + ItemMeta createGameButtonMeta = createGameButton.getItemMeta(); + createGameButtonMeta.displayName(Localization.getComponent("create-game-item")); + createGameButton.setItemMeta(createGameButtonMeta); + + ItemStack seekerHelmet = new ItemStack(Material.LEATHER_HELMET); + ItemStack seekerChestplate = new ItemStack(Material.LEATHER_CHESTPLATE); + ItemStack seekerLeggings = new ItemStack(Material.LEATHER_LEGGINGS); + ItemStack seekerBoots = new ItemStack(Material.LEATHER_BOOTS); + + seekerHelmet.setItemMeta(color(seekerHelmet)); + seekerBoots.setItemMeta(color(seekerBoots)); + seekerChestplate.setItemMeta(color(seekerChestplate)); + seekerLeggings.setItemMeta(color(seekerLeggings)); + seekerSet = new ItemStack[]{seekerBoots, seekerLeggings, seekerChestplate, seekerHelmet}; + } + + private static ItemMeta color(ItemStack item) { + + LeatherArmorMeta meta = (LeatherArmorMeta) item.getItemMeta(); + meta.setColor(Color.RED); + meta.setUnbreakable(true); + meta.addEnchant(Enchantment.PROTECTION_ENVIRONMENTAL, 10, true); + meta.displayName(Localization.getComponent("seeker-armor")); + + return meta; + } + + public static void defaultInventory(Player player) { + PlayerInventory inventory = player.getInventory(); + inventory.clear(); + inventory.addItem(menuItem); + } + + public static ItemStack getCreateGameButton() { + return createGameButton; + } + + public static ItemStack getGamesPageItem() { + return games; + } + + public static ItemStack getMenuItem() { + return menuItem; + } + + public static ItemStack[] getSeekerArmor() { + return seekerSet; + } + + public static ItemStack getFreezeItem() { + return freezeItem; + } + + +} diff --git a/src/main/java/hdvtdev/blockAndSeek/managers/MapsManager.java b/src/main/java/hdvtdev/blockAndSeek/managers/MapsManager.java index de1ba2e..6a464cd 100644 --- a/src/main/java/hdvtdev/blockAndSeek/managers/MapsManager.java +++ b/src/main/java/hdvtdev/blockAndSeek/managers/MapsManager.java @@ -1,11 +1,60 @@ package hdvtdev.blockAndSeek.managers; +import hdvtdev.blockAndSeek.BlockAndSeek; import hdvtdev.blockAndSeek.BlockAndSeekMap; +import org.bukkit.configuration.file.YamlConfiguration; +import java.io.File; +import java.io.IOException; import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; public class MapsManager { - private static final Map maps = null; + private static volatile Map allMaps = new ConcurrentHashMap<>(); + private static final Map readyMaps = new ConcurrentHashMap<>(); + + private static final File mapsFile = new File(BlockAndSeek.getPluginDataFolder(), "maps.yml"); + + public static Set getAllMaps() { + return allMaps.keySet(); + } + + public static void test() { + System.out.println("all maps"); + allMaps.forEach((k, v) -> System.out.println(k + ": " + v)); + System.out.println("ready maps"); + readyMaps.forEach((k, v) -> System.out.println(k + ": " + v)); + } + + public static BlockAndSeekMap getMap(String name) { + return allMaps.get(name); + } + + public static void saveMap(String name, BlockAndSeekMap map) throws IOException { + var conf = YamlConfiguration.loadConfiguration(mapsFile); + if (conf.contains(name)) conf.set(name, null); + conf.set(name, map); + conf.save(mapsFile); + } + + public static void loadMaps() { + Map confMap = new ConcurrentHashMap<>(); + YamlConfiguration configuration = YamlConfiguration.loadConfiguration(mapsFile); + + for (String map : configuration.getKeys(false)) { + + + BlockAndSeekMap blockAndSeekMap = configuration.getSerializable(map, BlockAndSeekMap.class, BlockAndSeekMap.defaultMap()); + if (blockAndSeekMap.isReady()) readyMaps.put(map, blockAndSeekMap); + confMap.put(map, blockAndSeekMap); + } + + allMaps = confMap; + } + } + + diff --git a/src/main/java/hdvtdev/blockAndSeek/roulette/RouletteGenerator.java b/src/main/java/hdvtdev/blockAndSeek/roulette/RouletteGenerator.java index 70063af..75e0b74 100644 --- a/src/main/java/hdvtdev/blockAndSeek/roulette/RouletteGenerator.java +++ b/src/main/java/hdvtdev/blockAndSeek/roulette/RouletteGenerator.java @@ -9,7 +9,6 @@ import java.util.List; public class RouletteGenerator { - private final ProbabilityCollection probabilityCollection = new ProbabilityCollection<>(); public RouletteGenerator(List blocks) { diff --git a/src/main/resources/localization.yml b/src/main/resources/localization.yml index 1f032f2..f09b1dd 100644 --- a/src/main/resources/localization.yml +++ b/src/main/resources/localization.yml @@ -18,4 +18,13 @@ en-US: game-players-count: "{players} of {max-players}" game-title: "{title}" - wait-time-left: "Game starts in: {time}s" \ No newline at end of file + wait-time-left: "Game starts in: {time}s" + + #items + freeze-item: "Freeze" + seeker-armor: "Seeker armor" + + menu-item: "BlockAndSeek Menu" + games-page-item: "Games" + create-game-item: "Create new game" + game-name: "{name}"