diff --git a/.idea/ant.xml b/.idea/ant.xml
new file mode 100644
index 0000000..a2a4769
--- /dev/null
+++ b/.idea/ant.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/compiler.xml b/.idea/compiler.xml
index b589d56..b86273d 100644
--- a/.idea/compiler.xml
+++ b/.idea/compiler.xml
@@ -1,6 +1,6 @@
-
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
index 5b99cf8..5cd9a10 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -1,7 +1,7 @@
-
+
\ No newline at end of file
diff --git a/.idea/saveactions_settings.xml b/.idea/saveactions_settings.xml
new file mode 100644
index 0000000..7a3a79c
--- /dev/null
+++ b/.idea/saveactions_settings.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..94a25f7
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
index 07160dc..f1c12b7 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,7 +1,6 @@
plugins {
id 'java'
id("xyz.jpenilla.run-paper") version "2.3.1"
- id 'com.rikonardo.papermake' version '1.0.6'
}
group = 'hdvtdev'
@@ -10,6 +9,7 @@ version = '0.0.1-a'
repositories {
mavenCentral()
maven { url 'https://repo.md-5.net/content/groups/public/' }
+ maven { url 'https://jitpack.io' }
maven {
name = "papermc-repo"
url = "https://repo.papermc.io/repository/maven-public/"
@@ -22,17 +22,10 @@ repositories {
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'
}
-tasks {
- runServer {
- // Configure the Minecraft version for our task.
- // This is the only required configuration besides applying the plugin.
- // Your plugin's jar (or shadowJar if present) will be used automatically.
- minecraftVersion("1.20")
- }
-}
def targetJavaVersion = 21
java {
@@ -62,6 +55,10 @@ processResources {
}
jar {
+
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) } }
+ duplicatesStrategy = DuplicatesStrategy.EXCLUDE
}
diff --git a/build.xml b/build.xml
new file mode 100644
index 0000000..44efce6
--- /dev/null
+++ b/build.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/java/hdvtdev/blockAndSeek/BlockAndSeek.java b/src/main/java/hdvtdev/blockAndSeek/BlockAndSeek.java
index de5db00..de409f0 100644
--- a/src/main/java/hdvtdev/blockAndSeek/BlockAndSeek.java
+++ b/src/main/java/hdvtdev/blockAndSeek/BlockAndSeek.java
@@ -3,34 +3,63 @@ package hdvtdev.blockAndSeek;
import me.libraryaddict.disguise.LibsDisguises;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandExecutor;
+import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.java.JavaPlugin;
import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
import java.util.Objects;
+import java.util.logging.Logger;
public class BlockAndSeek extends JavaPlugin implements CommandExecutor {
- private static File dataFolder;
+ private static Plugin javaPlugin;
+
+ public static Plugin getInstance() {
+ return javaPlugin;
+ }
public static File getPluginDataFolder() {
- return dataFolder;
+ return javaPlugin.getDataFolder();
+ }
+
+ public static InputStream getPluginResource(String resource) {
+ return javaPlugin.getResource(resource);
+ }
+
+ public static void saveResource(File file) {
+ saveResource(file.getAbsolutePath());
+ }
+
+ public static void saveResource(String file) {
+ javaPlugin.saveResource(file, false);
+ }
+
+ public static Logger getPluginLogger() {
+ return javaPlugin.getLogger();
}
@Override
public void onEnable() {
- dataFolder = getDataFolder();
+ javaPlugin = this;
+
LibsDisguises libsDisguises = (LibsDisguises) Bukkit.getPluginManager().getPlugin("LibsDisguises");
if (libsDisguises == null) {
- getLogger().severe("LibsDisguises не найден!");
+ getLogger().severe("LibsDisguises not found! It's required for the plugin to work!");
super.onDisable();
}
- Localization.load(this);
- ConfigManager.load();
+ try {
+ ConfigManager.loadAll();
+ } catch (IOException e) {
+ getLogger().severe("Failed to save some .yml configs!");
+ }
Objects.requireNonNull(getCommand("blockandseek")).setExecutor(new CommandListener());
+ getServer().getPluginManager().registerEvents(new EventListener(), this);
}
diff --git a/src/main/java/hdvtdev/blockAndSeek/BlockAndSeekContainer.java b/src/main/java/hdvtdev/blockAndSeek/BlockAndSeekContainer.java
new file mode 100644
index 0000000..0cd54f5
--- /dev/null
+++ b/src/main/java/hdvtdev/blockAndSeek/BlockAndSeekContainer.java
@@ -0,0 +1,24 @@
+package hdvtdev.blockAndSeek;
+
+import org.bukkit.Bukkit;
+import org.bukkit.NamespacedKey;
+import org.bukkit.scoreboard.Scoreboard;
+import org.bukkit.scoreboard.Team;
+
+public class BlockAndSeekContainer {
+
+ 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 FROZEN_PLAYER = new NamespacedKey(BlockAndSeek.getInstance(), "BlockAndSeekFrozenPlayer");
+ public static final NamespacedKey PLAYER = new NamespacedKey(BlockAndSeek.getInstance(), "BlockAndSeekPlayer");
+
+ public static final Team NO_COLLIDE_TEAM;
+
+ static {
+ Scoreboard scoreboard = Bukkit.getScoreboardManager().getMainScoreboard();
+ Team team = scoreboard.getTeam("BlockAndSeekNoCollide");
+ if (team == null) team = scoreboard.registerNewTeam("BlockAndSeekNoCollide");
+ team.setOption(Team.Option.COLLISION_RULE, Team.OptionStatus.NEVER);
+ NO_COLLIDE_TEAM = team;
+ }
+}
diff --git a/src/main/java/hdvtdev/blockAndSeek/BlockAndSeekGame.java b/src/main/java/hdvtdev/blockAndSeek/BlockAndSeekGame.java
index 2bf70e5..a9beda5 100644
--- a/src/main/java/hdvtdev/blockAndSeek/BlockAndSeekGame.java
+++ b/src/main/java/hdvtdev/blockAndSeek/BlockAndSeekGame.java
@@ -1,29 +1,94 @@
package hdvtdev.blockAndSeek;
-import org.bukkit.Bukkit;
-import org.bukkit.Location;
-import org.bukkit.World;
+import me.libraryaddict.disguise.DisguiseAPI;
+import org.bukkit.Material;
import org.bukkit.entity.Player;
-import org.jetbrains.annotations.NotNull;
+import org.bukkit.persistence.PersistentDataType;
-import java.util.*;
+import java.security.SecureRandom;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicBoolean;
public class BlockAndSeekGame {
- private final Set players = Collections.synchronizedSet(new HashSet<>());
+ private final ConcurrentHashMap players = new ConcurrentHashMap<>();
- public BlockAndSeekGame(String name, List players) {
- for (String player : players) {
- this.addPlayer(player);
+ private AtomicBoolean started = new AtomicBoolean(false);
+ private final String name;
+
+ public BlockAndSeekGame(String name) {
+ this.name = name;
+ }
+
+ public void end() {
+ for (Player player : players.keySet()) {
+ EventListener.unfreezePlayer(player);
+ player.getPersistentDataContainer().remove(BlockAndSeekContainer.PLAYER);
+ player.sendBlockChange(player.getLocation(), Material.AIR.createBlockData());
+ DisguiseAPI.undisguiseToAll(player);
}
}
- public void addPlayer(String name) {
- Player p = Bukkit.getPlayerExact(name);
- if (p != null) players.add(p);
+ public boolean addPlayer(Player player) {
+ if (started.get()) {
+ return false;
+ }
+ player.getPersistentDataContainer().set(BlockAndSeekContainer.PLAYER, PersistentDataType.STRING, name);
+ players.put(player, PlayerType.HIDER);
+ return true;
+ }
+
+ public void removePlayer(Player player) {
+ players.remove(player);
+ }
+
+ public Set getPlayers() {
+ return players.keySet();
+ }
+
+ public Player getLastHider() {
+ for (Map.Entry entry : players.entrySet()) {
+ if (entry.getValue() == PlayerType.HIDER) return entry.getKey();
+ }
+ return null;
+ }
+
+ public int seekersCount() {
+ return (int) players.values().stream().filter(f -> f.equals(PlayerType.SEEKER)).count();
+ }
+
+ public int hidersCound() {
+ return (int) players.values().stream().filter(f -> f.equals(PlayerType.HIDER)).count();
+ }
+
+ public int playerCount() {
+ return players.size();
+ }
+
+ public void start() {
+ started.set(true);
+ }
+
+ public boolean isStarted() {
+ return started.get();
+ }
+
+ 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) return player;
+ }
+ return null;
+ }
+
+ private enum PlayerType {
+ SEEKER,
+ HIDER
}
-
-
}
diff --git a/src/main/java/hdvtdev/blockAndSeek/BlockAndSeekMap.java b/src/main/java/hdvtdev/blockAndSeek/BlockAndSeekMap.java
new file mode 100644
index 0000000..c78844b
--- /dev/null
+++ b/src/main/java/hdvtdev/blockAndSeek/BlockAndSeekMap.java
@@ -0,0 +1,13 @@
+package hdvtdev.blockAndSeek;
+
+import org.bukkit.inventory.ItemStack;
+
+import java.util.List;
+
+public record BlockAndSeekMap(List spawn, List lobby, int duration, List blocks) {
+
+
+ public record Block(ItemStack block, int chance) {
+ }
+
+}
diff --git a/src/main/java/hdvtdev/blockAndSeek/CommandListener.java b/src/main/java/hdvtdev/blockAndSeek/CommandListener.java
index 1d45ab3..cf72e20 100644
--- a/src/main/java/hdvtdev/blockAndSeek/CommandListener.java
+++ b/src/main/java/hdvtdev/blockAndSeek/CommandListener.java
@@ -1,38 +1,32 @@
package hdvtdev.blockAndSeek;
-import me.libraryaddict.disguise.DisguiseAPI;
-import me.libraryaddict.disguise.disguisetypes.Disguise;
-import me.libraryaddict.disguise.disguisetypes.DisguiseType;
-import me.libraryaddict.disguise.disguisetypes.MiscDisguise;
+import hdvtdev.blockAndSeek.roulette.RouletteCreator;
import net.kyori.adventure.text.minimessage.MiniMessage;
-import org.bukkit.Bukkit;
-import org.bukkit.Location;
import org.bukkit.Material;
-import org.bukkit.World;
-import org.bukkit.command.*;
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandExecutor;
+import org.bukkit.command.CommandSender;
+import org.bukkit.command.TabCompleter;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.meta.ItemMeta;
+import org.bukkit.persistence.PersistentDataType;
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.Map;
+import java.util.Set;
public class CommandListener implements CommandExecutor, TabCompleter {
- public static List getAllCommands() {
- return List.of("tpp", "morph");
- }
-
-
+ private static final MiniMessage miniMessage = MiniMessage.miniMessage();
@Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
-
int argsLen = args.length;
if (argsLen == 0) {
@@ -41,54 +35,77 @@ public class CommandListener implements CommandExecutor, TabCompleter {
}
switch (args[0]) {
+ case "test" -> {
+ if (sender instanceof Player player) {
+ RouletteCreator.createRoulette(player, null, true);
+ }
+ }
+ case "freeze" -> {
+ 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);
+ }
+ }
+ 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);
+ foo.setItemMeta(fooMeta);
+ player.getInventory().addItem(foo);
+ }
+ }
case "map" -> {
- if (argsLen > 1) {
- switch (args[1]) {
- case "list" -> sender.sendMessage(Localization.get("maps-list", "{maps}", ConfigManager.createdMaps().toString()));
- case "create" -> {
- if (argsLen > 2) {
- String mapName = args[2];
- World world = Bukkit.getWorld(mapName);
- if (world == null) {
- sender.sendMessage(Localization.get("map-missing-world", "{world}", mapName));
- } else {
- try {
- if (ConfigManager.addMap(mapName)) sender.sendMessage(Localization.get("map-created", "{map}", mapName));
- else sender.sendMessage(Localization.get("map-already-exist", "{map}", mapName));
- } catch (IOException e) {
- sender.sendMessage(Localization.get("map-creation-failed", "{error}", e.getMessage()));
- }
+ if (sender.hasPermission("blockandseek.manage")) {
+ if (argsLen > 1) {
+ switch (args[1]) {
+ case "list" -> {
+ StringBuilder buffer = new StringBuilder(Localization.get("maps-available"));
+ String listElement = Localization.get("maps-available-element");
+ Set readyMaps = ConfigManager.getReadyMaps();
+ for (String map : ConfigManager.getAllMaps()) {
+ buffer.append("\n").append(listElement.replace("{map}", map).replace("{color-status}",
+ readyMaps.contains(map) ? "" : ""));
}
- } else sender.sendMessage(Localization.get("not-enough-arguments", "{usage}", "/blockandseek create [MAP NAME]"));
+ sender.sendMessage(miniMessage.deserialize(buffer.toString()));
+ }
}
- default -> {
- if (ConfigManager.createdMaps().contains(args[1])) {
- if (argsLen > 2) {
- switch (args[2]) {
- case "setspawn" -> {}
- case "setlobby" -> {}
- case "setduration" -> {}
- default -> sender.sendMessage(Localization.get("unknown-subcommand", "{subcommand}", args[2]));
- }
- } else sender.sendMessage(Localization.get("not-enough-arguments", "{usage}", String.format("/blockandseek create %s [SUBCOMMAND]", args[1])));
- } else sender.sendMessage(Localization.get("map-not-exist", "{map}", args[1]));
- }
- }
- } else sender.sendMessage(Localization.get("not-enough-arguments", "{usage}", "/blockandseek map [MAP NAME | SUBCOMMAND]"));
+ } else
+ sender.sendMessage(Localization.getComponent("not-enough-arguments", "{command}", "/blockandseek map", "{help}", "[MAP NAME | COMMANDS]"));
+
+ } else sender.sendMessage(Localization.getComponent("not-enough-permissions"));
}
case "reload" -> {
- if (argsLen > 1) {
- switch (args[1]) {
- case "messages" -> {
- Localization.reload();
- sender.sendMessage(Localization.get("messages-reload"));
+ if (sender.hasPermission("blockandseek.manage")) {
+ if (argsLen > 1) {
+ switch (args[1]) {
+ case "localization" -> {
+ try {
+ ConfigManager.load("localization.yml");
+ sender.sendMessage(Localization.getPrefix().append(Localization.getComponent("successful-reload", "{config}", "localization.yml")));
+ } catch (IOException e) {
+ sender.sendMessage(Localization.getComponent("failed-reload", "{config}", "localization.yml", "{e}", e.getMessage()));
+ }
+
+ }
+ case "maps" -> {
+ try {
+ ConfigManager.load(null);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ sender.sendMessage(Localization.get("maps-reload"));
+ }
}
- case "maps" -> {
- ConfigManager.load();
- sender.sendMessage(Localization.get("maps-reload"));
- }
- }
- }
+ } else
+ sender.sendMessage(Localization.get("not-enough-arguments", "{usage}", "/blockandseek reload [messages | maps]"));
+
+ } else sender.sendMessage(Localization.get("not-enough-permissions"));
}
}
@@ -98,7 +115,6 @@ public class CommandListener implements CommandExecutor, TabCompleter {
*/
-
return true;
}
@@ -106,23 +122,41 @@ public class CommandListener implements CommandExecutor, TabCompleter {
public @Nullable List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
sender.sendActionBar(MiniMessage.miniMessage().deserialize("" + args.length));
return switch (args.length) {
- case 1 -> List.of("reload", "help", "map", "join");
+ case 1 -> {
+ if (sender.hasPermission("blockandseek.manage")) {
+ yield List.of("reload", "help", "map", "join");
+ } else yield List.of("help", "join");
+ }
case 2 -> switch (args[0]) {
case "map" -> {
- List scmds = new ArrayList<>(List.of("create", "list"));
- scmds.addAll(ConfigManager.createdMaps());
+ List scmds = new ArrayList<>();
+ /*
+ if (sender.hasPermission("blockandseek.manage")) {
+ scmds.addAll(List.of("create", "list"));
+ scmds.addAll(ConfigManager.createdMaps());
+ }
+
+ */
yield scmds;
}
- case "join" -> List.of("test");
- case "reload" -> List.of("messages", "maps");
+ case "join" -> List.of("test"); //TODO
+ case "reload" -> {
+ if (sender.hasPermission("blockandseek.manage")) {
+ yield List.of("messages", "maps");
+ } else yield List.of();
+ }
default -> List.of();
};
case 3 -> switch (args[1]) {
case "create", "list" -> List.of();
default -> {
- if (ConfigManager.createdMaps().contains(args[1])) {
+ /*
+ if (ConfigManager.createdMaps().contains(args[1]) && sender.hasPermission("blockandseek.manage")) {
yield List.of("setspawn", "setlobby", "setduration");
} else yield List.of();
+
+ */
+ yield List.of();
}
};
default -> List.of();
diff --git a/src/main/java/hdvtdev/blockAndSeek/ConfigManager.java b/src/main/java/hdvtdev/blockAndSeek/ConfigManager.java
index f7c3539..dbd4884 100644
--- a/src/main/java/hdvtdev/blockAndSeek/ConfigManager.java
+++ b/src/main/java/hdvtdev/blockAndSeek/ConfigManager.java
@@ -1,57 +1,252 @@
package hdvtdev.blockAndSeek;
+import org.bukkit.Material;
+import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.YamlConfiguration;
-import org.jetbrains.annotations.NotNull;
+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.HashSet;
import java.util.List;
+import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
public class ConfigManager {
- private static final File mapsConfigFile = new File(BlockAndSeek.getPluginDataFolder(), "maps.yml");
- private static final ConcurrentHashMap mapsConfigs = new ConcurrentHashMap<>();
+ private static volatile ConcurrentHashMap localization = new ConcurrentHashMap<>();
+ private static volatile ConcurrentHashMap config = new ConcurrentHashMap<>();
+ private static volatile ConcurrentHashMap maps = new ConcurrentHashMap<>();
- public static Set createdMaps() {
- return mapsConfigs.keySet();
+ private static final File mapsFile = new File(BlockAndSeek.getPluginDataFolder(), "maps.yml");
+
+ public static Set getAllMaps() {
+ return YamlConfiguration.loadConfiguration(mapsFile).getKeys(false);
}
- public static MapConfig getMapConfig(@NotNull String name) {
- return mapsConfigs.get(name);
+ public enum MapStatus {
+ SPAWN_REQUIRED,
+ LOBBY_REQUIRED,
+ DURATION_REQUIRED,
+ BLOCKS_REQUIRED
}
- public static boolean addMap(String name) throws IOException {
- if (mapsConfigs.containsKey(name)) return false;
- mapsConfigs.put(name, MapConfig.defaults());
- YamlConfiguration config = YamlConfiguration.loadConfiguration(mapsConfigFile);
- config.set(name + ".spawn", "null");
- config.set(name + ".duration", "null");
- config.set(name + ".lobby", "null");
+ public static Set getReadyMaps() {
+ return maps.keySet();
+ }
- config.save(mapsConfigFile);
+ 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);
+ 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, 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("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);
- public static void load() {
- mapsConfigs.clear();
- YamlConfiguration config = YamlConfiguration.loadConfiguration(mapsConfigFile);
- for (String mapKey : config.getKeys(false)) {
- List spawn = config.getLongList(mapKey + ".spawn");
- List lobby = config.getLongList(mapKey + ".lobby");
- int duration = config.getInt(mapKey + ".duration");
- MapConfig mapConfig = new MapConfig(spawn, lobby, duration);
- mapsConfigs.put(mapKey, mapConfig);
+ if (section != null) {
+ section.set(property, value);
+ try {
+ configuration.save(mapsFile);
+ } catch (IOException e) {
+ return false;
+ }
}
+
+ return true;
}
- public record MapConfig(List spawn, List lobby, int duration) {
- public static MapConfig defaults() {
- return new MapConfig(null, null, -1);
+ public static void loadAll() throws IOException {
+ load("config.yml");
+ load("localization.yml");
+ load("maps.yml");
+ }
+
+ public static void load(String file) throws IOException {
+
+ File conf = new File(BlockAndSeek.getPluginDataFolder(), file);
+
+ YamlConfiguration defaultConfiguration = YamlConfiguration.loadConfiguration(
+ new InputStreamReader(BlockAndSeek.getPluginResource(file)));
+
+ if (!conf.exists()) {
+ BlockAndSeek.saveResource(file);
+
+ ConcurrentHashMap confMap = new ConcurrentHashMap<>();
+
+ switch (file) {
+ case "config.yml" -> {
+ for (String key : defaultConfiguration.getKeys(false)) {
+ confMap.put(key, defaultConfiguration.getString(key, "NULL"));
+ }
+ config = confMap;
+ }
+ case "localization.yml" -> {
+ for (String key : defaultConfiguration.getConfigurationSection("en_US").getKeys(false)) {
+ confMap.put(key, defaultConfiguration.getString(key, "NULL"));
+ }
+ localization = confMap;
+ Localization.update();
+ }
+ }
+
+
+ } else {
+ switch (file) {
+ case "config.yml" -> loadConfig(conf, defaultConfiguration);
+ case "localization.yml" -> loadLocalization(conf, defaultConfiguration);
+ case "maps.yml" -> loadMaps(conf);
+ }
}
+
+
+ }
+
+ private static void loadMaps(File configurationFile) throws IOException {
+ ConcurrentHashMap 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");
+ List blocks = section.getMapList("blocks").stream().map(
+ block -> {
+ try {
+ return new BlockAndSeekMap.Block(
+ new ItemStack(Material.valueOf(((String) block.get("name")).toUpperCase())),
+ (int) block.get("chance")
+ );
+ } catch (IllegalArgumentException | ClassCastException | NullPointerException ignored) {
+ return null;
+ }
+
+ }
+ ).toList();
+
+ if (!spawn.isEmpty() && !lobby.isEmpty() && duration != 0 && !blocks.isEmpty()) {
+ confMap.put(map, new BlockAndSeekMap(spawn, lobby, duration, blocks));
+ }
+ }
+ maps = confMap;
+ }
+
+
+ }
+
+ private static void loadConfig(File configurationFile, YamlConfiguration defaultConfiguration) throws IOException {
+ ConcurrentHashMap confMap = new ConcurrentHashMap<>();
+ YamlConfiguration configuration = YamlConfiguration.loadConfiguration(configurationFile);
+
+ for (String key : defaultConfiguration.getKeys(false)) {
+ if (configuration.isSet(key)) {
+ confMap.put(key, configuration.getString(key, defaultConfiguration.getString(key, "NULL")));
+ } else {
+ String value = defaultConfiguration.getString(key, "NULL");
+ configuration.set(key, value);
+ confMap.put(key, value);
+ }
+ }
+
+ configuration.save(configurationFile);
+
+ config = confMap;
+ }
+
+ private static void loadLocalization(File configurationFile, YamlConfiguration defaultConfiguration) throws IOException {
+
+ ConcurrentHashMap confMap = new ConcurrentHashMap<>();
+ YamlConfiguration configuration = YamlConfiguration.loadConfiguration(configurationFile);
+ String language = config.get("language");
+
+ ConfigurationSection defaultSection = defaultConfiguration.getConfigurationSection("en_US");
+ ConfigurationSection configSection = configuration.getConfigurationSection(language);
+
+ if (configSection != null) {
+ for (String key : defaultSection.getKeys(false)) {
+ if (configSection.contains(key)) {
+ confMap.put(key, configSection.getString(key, defaultSection.getString(key, "NULL")));
+ } else {
+ String value = defaultSection.getString(key, "NULL");
+ configSection.set(key, value);
+ confMap.put(key, value);
+ }
+
+ }
+ } else {
+ BlockAndSeek.getPluginLogger().warning(String.format("Language \"%s\" does not exist in localization.yml! Using default language en_US.", language));
+ for (String key : defaultSection.getKeys(false)) {
+ confMap.put(key, defaultSection.getString(key, "NULL"));
+ }
+ }
+
+ configuration.save(configurationFile);
+
+ localization = confMap;
+
+
+ }
+
+
+ public static ConcurrentHashMap getLocalization() {
+ return localization;
}
diff --git a/src/main/java/hdvtdev/blockAndSeek/EventListener.java b/src/main/java/hdvtdev/blockAndSeek/EventListener.java
new file mode 100644
index 0000000..7d7e77b
--- /dev/null
+++ b/src/main/java/hdvtdev/blockAndSeek/EventListener.java
@@ -0,0 +1,213 @@
+package hdvtdev.blockAndSeek;
+
+
+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.Bukkit;
+import org.bukkit.Location;
+import org.bukkit.Material;
+import org.bukkit.Sound;
+import org.bukkit.block.Block;
+import org.bukkit.entity.Player;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+import org.bukkit.event.entity.EntityDamageByEntityEvent;
+import org.bukkit.event.inventory.InventoryClickEvent;
+import org.bukkit.event.inventory.InventoryCloseEvent;
+import org.bukkit.event.player.PlayerInteractEvent;
+import org.bukkit.event.player.PlayerQuitEvent;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.meta.ItemMeta;
+import org.bukkit.persistence.PersistentDataContainer;
+import org.bukkit.persistence.PersistentDataType;
+import org.bukkit.potion.PotionEffect;
+import org.bukkit.potion.PotionEffectType;
+import org.bukkit.scheduler.BukkitTask;
+import org.bukkit.util.Vector;
+
+import java.util.concurrent.ConcurrentHashMap;
+
+
+public class EventListener implements Listener {
+
+ 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);
+
+ public static void createTask(Player player, BukkitTask bukkitTask) {
+ tasks.put(player, bukkitTask);
+ }
+
+ public static void stopTask(Player player) {
+ BukkitTask task = tasks.remove(player);
+ task.cancel();
+ }
+
+ @EventHandler
+ public void onPlayerQuit(PlayerQuitEvent event) {
+ Player player = event.getPlayer();
+ PersistentDataContainer container = player.getPersistentDataContainer();
+ if (frozenPlayers.remove(player) != null) {
+ unfreezePlayer(player);
+ }
+ String arena = container.get(BlockAndSeekContainer.PLAYER, PersistentDataType.STRING);
+ if (arena != null) {
+ container.remove(BlockAndSeekContainer.PLAYER);
+ GamesManager.get(arena).removePlayer(player);
+ }
+ }
+
+ @EventHandler
+ public void onDamage(EntityDamageByEntityEvent event) {
+
+ if (event.getEntity() instanceof Player victim) {
+ PersistentDataContainer container = victim.getPersistentDataContainer();
+ if (event.getDamager() instanceof Player && container.has(BlockAndSeekContainer.FROZEN_PLAYER)) {
+ BlockAndSeekGame game = GamesManager.get(container.get(BlockAndSeekContainer.PLAYER, PersistentDataType.STRING));
+ for (Player player : game.getPlayers()) {
+ player.sendBlockChange(frozenPlayers.get(victim), Material.AIR.createBlockData());
+ }
+ unfreezePlayer(victim);
+ }
+ }
+ }
+
+
+ @EventHandler
+ public void onRightClick(PlayerInteractEvent event) {
+ Player player = event.getPlayer();
+ ItemStack item = player.getInventory().getItemInMainHand();
+ ItemMeta itemMeta = item.getItemMeta();
+ if (itemMeta != null) {
+
+ PersistentDataContainer data = itemMeta.getPersistentDataContainer();
+ PersistentDataContainer playerData = player.getPersistentDataContainer();
+
+ if (data.has(BlockAndSeekContainer.SOUND_ITEM)) {
+ Long time = soundCoolDown.get(player);
+ if (time != null) {
+ long diff = System.currentTimeMillis() - time;
+ if (diff > 3000) {
+ soundCoolDown.remove(player);
+ } else {
+ player.sendActionBar(MiniMessage.miniMessage().deserialize("Подождите немного перед следующим использованием"));
+ return;
+ }
+ }
+ Location location = player.getLocation();
+ for (Player p : Bukkit.getOnlinePlayers()) {
+ p.playSound(location, Sound.ENTITY_CHICKEN_AMBIENT, 2f, 1.5f);
+ }
+ soundCoolDown.put(player, System.currentTimeMillis());
+ }
+
+ if (data.has(BlockAndSeekContainer.FREEZE_ITEM)) {
+
+ String arena = playerData.get(BlockAndSeekContainer.PLAYER, PersistentDataType.STRING);
+ if (arena != null) {
+ BlockAndSeekGame game = GamesManager.get(arena);
+ if (frozenPlayers.remove(player) != null) {
+ for (Player p : game.getPlayers()) {
+ p.sendBlockChange(player.getLocation(), Material.AIR.createBlockData());
+ }
+ unfreezePlayer(player);
+ } else {
+ if (game.isStarted()) {
+ Location playerLocation = player.getLocation();
+ Block block = playerLocation.getBlock();
+ Block upperBlock = player.getLocation().getBlock();
+ if (!upperBlock.isSolid()) {
+
+ Location blockLocation = block.getLocation();
+ blockLocation.setX(blockLocation.getX() + 0.5);
+ double y = Math.round(blockLocation.getY());
+ blockLocation.setY(block.isSolid() ? y + 1 : y);
+ blockLocation.setZ(blockLocation.getZ() + 0.5);
+ blockLocation.setPitch(playerLocation.getPitch());
+ blockLocation.setYaw(playerLocation.getYaw());
+
+ player.getPersistentDataContainer().set(BlockAndSeekContainer.FROZEN_PLAYER, PersistentDataType.BOOLEAN, true);
+
+ player.setWalkSpeed(0);
+ player.setFreezeTicks(40);
+ player.setVelocity(ZERO_VELOCITY);
+ player.setFlySpeed(0);
+ player.setAllowFlight(true);
+ player.setFlying(true);
+ player.setGravity(false);
+ player.addPotionEffect(INVISIBILITY);
+ BlockAndSeekContainer.NO_COLLIDE_TEAM.addEntry(player.getName());
+
+ for (Player p : Bukkit.getOnlinePlayers()) {
+ p.sendBlockChange(blockLocation, Material.CHORUS_PLANT.createBlockData());
+ }
+
+ player.teleport(blockLocation);
+ frozenPlayers.put(player, blockLocation);
+ } else
+ player.sendActionBar(MiniMessage.miniMessage().deserialize(" Could not freeze here!"));
+
+ } else
+ player.sendActionBar(MiniMessage.miniMessage().deserialize(" Could not freeze now!"));
+
+ }
+ } else player.sendActionBar(MiniMessage.miniMessage().deserialize(" Could not freeze now!"));
+ }
+
+
+ }
+ }
+
+ public static void unfreezePlayer(Player player) {
+ player.getPersistentDataContainer().remove(BlockAndSeekContainer.FROZEN_PLAYER);
+ player.setFlySpeed(0.2f);
+ player.setWalkSpeed(0.2f);
+ player.setAllowFlight(false);
+ player.setFlying(false);
+ BlockAndSeekContainer.NO_COLLIDE_TEAM.removeEntry(player.getName());
+ player.setGravity(true);
+ player.setFreezeTicks(0);
+ player.removePotionEffect(PotionEffectType.INVISIBILITY);
+ }
+
+ @EventHandler
+ public void onClick(InventoryClickEvent event) {
+ Player player = (Player) event.getWhoClicked();
+
+ 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));
+ DisguiseAPI.disguiseToAll(player, miscDisguise);
+ player.closeInventory(InventoryCloseEvent.Reason.UNKNOWN);
+ }
+ }
+ }
+ }
+
+ @EventHandler
+ public void onClose(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));
+ DisguiseAPI.disguiseToAll(player, miscDisguise);
+ }
+ } else {
+ Bukkit.getScheduler().runTaskLater(BlockAndSeek.getInstance(), () -> player.openInventory(event.getInventory()), 0L);
+ }
+
+ }
+ }
+
+}
diff --git a/src/main/java/hdvtdev/blockAndSeek/GamesManager.java b/src/main/java/hdvtdev/blockAndSeek/GamesManager.java
index 50a7e58..3e0b13d 100644
--- a/src/main/java/hdvtdev/blockAndSeek/GamesManager.java
+++ b/src/main/java/hdvtdev/blockAndSeek/GamesManager.java
@@ -1,25 +1,106 @@
package hdvtdev.blockAndSeek;
-import java.util.ArrayList;
-import java.util.List;
+import hdvtdev.blockAndSeek.roulette.RouletteCreator;
+import net.kyori.adventure.text.Component;
+import org.bukkit.Bukkit;
+import org.bukkit.entity.Player;
+import org.bukkit.scheduler.BukkitRunnable;
+
+import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
public class GamesManager {
private static final ConcurrentHashMap games = new ConcurrentHashMap<>();
- public static void createGame(String name, ArrayList players) {
- games.put(name, new BlockAndSeekGame(name, players));
+ public static boolean isExist(String name) {
+ return games.containsKey(name);
}
- public static boolean joinGame(String name) {
- return false;
+ public static Set getAvailableGames() {
+ return games.keySet();
}
- public static void endGame(String name) {
- games.remove(name);
+ public static int createGame(String name) {
+ if (games.containsKey(name)) return 1;
+ if (Bukkit.getWorld(name) == null) return 2;
+ BlockAndSeekGame game = new BlockAndSeekGame(name);
+ games.put(name, game);
+
+ new BukkitRunnable() {
+
+ int duration = 30;
+ int waitTime = 10;
+
+
+ @Override
+ public void run() {
+
+ if (!game.isStarted()) {
+ int playerCount = game.playerCount();
+
+ for (Player player : game.getPlayers()) {
+ player.sendActionBar(Component.text("Игроков " + playerCount + "/12"));
+ }
+
+ if (playerCount > 1) {
+
+ if (waitTime == 0) {
+ game.start();
+ for (Player player : game.getPlayers()) {
+ RouletteCreator.createRoulette(player, null, true);
+ }
+ } else {
+ for (Player player : game.getPlayers()) {
+ player.sendMessage(Component.text("Осталось: " + waitTime));
+ }
+ waitTime--;
+ }
+
+ } else waitTime = 10;
+
+ } else {
+
+ if (game.hidersCound() == 0) {
+
+ for (Player player : game.getPlayers()) {
+ player.sendActionBar(Component.text("Сикеры победили!"));
+ //TODO
+ }
+
+ game.end();
+ games.remove(name);
+ this.cancel();
+ }
+
+ for (Player player : game.getPlayers()) {
+ player.sendActionBar(Component.text("Осталось: " + duration));
+ }
+
+ if (duration == 0) {
+ for (Player player : game.getPlayers()) {
+ player.sendActionBar(Component.text("Хайдеры победили!"));
+ }
+ game.end();
+ games.remove(name);
+ this.cancel();
+ } else duration--;
+
+
+ }
+
+
+ }
+
+
+ }.runTaskTimer(BlockAndSeek.getInstance(), 0L, 20L);
+
+
+ return 0;
}
-
+ public static BlockAndSeekGame get(String name) {
+ return games.get(name);
+ }
}
diff --git a/src/main/java/hdvtdev/blockAndSeek/Localization.java b/src/main/java/hdvtdev/blockAndSeek/Localization.java
index fb496b2..5ea65f6 100644
--- a/src/main/java/hdvtdev/blockAndSeek/Localization.java
+++ b/src/main/java/hdvtdev/blockAndSeek/Localization.java
@@ -2,66 +2,35 @@ package hdvtdev.blockAndSeek;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.MiniMessage;
-import org.bukkit.ChatColor;
-import org.bukkit.configuration.file.YamlConfiguration;
-import org.bukkit.plugin.java.JavaPlugin;
-import org.jetbrains.annotations.Nullable;
-
-import java.io.*;
import java.util.Map;
-import java.util.Objects;
-import java.util.concurrent.ConcurrentHashMap;
public class Localization {
- private static final Map localization = new ConcurrentHashMap<>();
- private static JavaPlugin javaPlugin;
+ private static volatile Map localization = ConfigManager.getLocalization();
+ private static final MiniMessage miniMessage = MiniMessage.miniMessage();
+ private static final Component prefix = miniMessage.deserialize("[BlockAndSeek] ");
- public static void reload() {
- File localizationFile = new File(javaPlugin.getDataFolder(), "messages.yml");
- if (!localizationFile.exists()) javaPlugin.saveResource("messages.yml", false);
-
- YamlConfiguration locale = YamlConfiguration.loadConfiguration(localizationFile);
- YamlConfiguration defaultLocale = YamlConfiguration.loadConfiguration(
- new InputStreamReader(Objects.requireNonNull(javaPlugin.getResource("messages.yml"))));
-
- String prefix = locale.getString("prefix");
- prefix = prefix == null ? defaultLocale.getString("prefix") : prefix;
-
- for (String defaultElement : defaultLocale.getKeys(false)) {
- String element = locale.getString(defaultElement);
- localization.put(defaultElement, String.format("%s%s", prefix, element == null ? defaultLocale.getString(defaultElement) : element));
- }
+ public static Component getPrefix() {
+ return prefix;
}
- public static void load(JavaPlugin plugin) {
- javaPlugin = plugin;
- File localizationFile = new File(plugin.getDataFolder(), "messages.yml");
- if (!localizationFile.exists()) plugin.saveResource("messages.yml", false);
-
- YamlConfiguration locale = YamlConfiguration.loadConfiguration(localizationFile);
- YamlConfiguration defaultLocale = YamlConfiguration.loadConfiguration(
- new InputStreamReader(Objects.requireNonNull(plugin.getResource("messages.yml"))));
-
- String prefix = locale.getString("prefix");
- prefix = prefix == null ? defaultLocale.getString("prefix") : prefix;
-
- for (String defaultElement : defaultLocale.getKeys(false)) {
- String element = locale.getString(defaultElement);
- localization.put(defaultElement, String.format("%s%s", prefix, element == null ? defaultLocale.getString(defaultElement) : element));
- }
-
+ public static void update() {
+ localization = ConfigManager.getLocalization();
}
-
- public static Component get(String key, String... replacements) {
+ public static String get(String key, String... replacements) {
String s = localization.get(key);
- for (int i = 0; i < replacements.length; i+=2) {
- s = s.replace(replacements[i], replacements[i + 1]);
- }
+ if (s != null) {
+ for (int i = 0; i < replacements.length; i += 2) {
+ s = s.replace(replacements[i], replacements[i + 1]);
+ }
+ } else return "Unknown localization: " + key;
+ return s;
+ }
- return MiniMessage.miniMessage().deserialize(s);
+ public static Component getComponent(String key, String... replacements) {
+ return miniMessage.deserialize(get(key, replacements));
}
diff --git a/src/main/java/hdvtdev/blockAndSeek/roulette/RouletteCreator.java b/src/main/java/hdvtdev/blockAndSeek/roulette/RouletteCreator.java
new file mode 100644
index 0000000..347f836
--- /dev/null
+++ b/src/main/java/hdvtdev/blockAndSeek/roulette/RouletteCreator.java
@@ -0,0 +1,154 @@
+package hdvtdev.blockAndSeek.roulette;
+
+import hdvtdev.blockAndSeek.BlockAndSeek;
+import hdvtdev.blockAndSeek.EventListener;
+import me.libraryaddict.disguise.DisguiseAPI;
+import me.libraryaddict.disguise.disguisetypes.DisguiseType;
+import me.libraryaddict.disguise.disguisetypes.MiscDisguise;
+import net.kyori.adventure.text.Component;
+import org.bukkit.Bukkit;
+import org.bukkit.Material;
+import org.bukkit.Sound;
+import org.bukkit.entity.Player;
+import org.bukkit.inventory.Inventory;
+import org.bukkit.inventory.InventoryHolder;
+import org.bukkit.inventory.ItemFlag;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.meta.ItemMeta;
+import org.bukkit.metadata.FixedMetadataValue;
+import org.bukkit.scheduler.BukkitRunnable;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.List;
+
+public class RouletteCreator implements InventoryHolder {
+
+ private static final int[] filledSlots = {
+
+ };
+
+ public static void createRoulette(Player player, Inventory inventory, boolean openInventory) {
+
+ Inventory gui = inventory == null ? new RouletteCreator().getInventory() : inventory;
+
+
+ EventListener.createTask(player, new BukkitRunnable() {
+
+ int i = 0;
+ final RouletteGenerator rouletteGenerator = new RouletteGenerator(List.of(
+ new RouletteGenerator.Block(new ItemStack(Material.FLOWER_POT), 55),
+ new RouletteGenerator.Block(new ItemStack(Material.GOLD_BLOCK), 25),
+ new RouletteGenerator.Block(new ItemStack(Material.EMERALD_BLOCK), 10),
+ new RouletteGenerator.Block(new ItemStack(Material.DIAMOND_BLOCK), 6),
+ new RouletteGenerator.Block(new ItemStack(Material.NETHERITE_BLOCK), 4)
+ ));
+
+ final List> rows = List.of(
+ new RouletteList<>(rouletteGenerator.getRandomRow(15)),
+ new RouletteList<>(rouletteGenerator.getRandomRow(15)),
+ new RouletteList<>(rouletteGenerator.getRandomRow(15))
+ );
+
+ final List items = List.of(
+ new ItemStack[]{
+ rows.getFirst().next(),
+ rows.getFirst().next(),
+ rows.getFirst().next(),
+ rows.getFirst().next(),
+ rows.getFirst().next()
+ },
+ new ItemStack[]{
+ rows.get(1).next(),
+ rows.get(1).next(),
+ rows.get(1).next(),
+ rows.get(1).next(),
+ rows.get(1).next()
+
+ },
+ new ItemStack[]{
+ rows.get(2).next(),
+ rows.get(2).next(),
+ rows.get(2).next(),
+ rows.get(2).next(),
+ rows.get(2).next()
+ }
+ );
+
+ @Override
+ public void run() {
+
+ for (int j = 0; j < 5; j++) {
+ gui.setItem(3 + j * 9, items.getFirst()[j]);
+ gui.setItem(5 + j * 9, items.get(1)[j]);
+ gui.setItem(7 + j * 9, items.get(2)[j]);
+ }
+
+ player.playSound(player.getLocation(), Sound.UI_BUTTON_CLICK, 0.5f, 2f);
+
+
+ if (i == 30) {
+ player.playSound(player.getLocation(), Sound.ENTITY_PLAYER_LEVELUP, 0.5f, 0.5f);
+ EventListener.stopTask(player);
+ }
+
+ for (int j = 4; j >= 1; j--) {
+ items.getFirst()[j] = items.getFirst()[j - 1];
+ items.get(1)[j] = items.get(1)[j - 1];
+ items.get(2)[j] = items.get(2)[j - 1];
+ }
+
+
+ items.getFirst()[0] = rows.getFirst().next();
+ items.get(1)[0] = rows.get(1).next();
+ items.get(2)[0] = rows.get(2).next();
+
+ i++;
+ }
+
+
+ }.runTaskTimer(BlockAndSeek.getInstance(), 0, 3L));
+
+ new BukkitRunnable() {
+
+ @Override
+ public void run() {
+
+ Inventory inventory = player.getOpenInventory().getTopInventory();
+
+ if (inventory.getHolder() instanceof RouletteCreator) {
+ inventory.close();
+ MiscDisguise miscDisguise = new MiscDisguise(DisguiseType.FALLING_BLOCK, inventory.getItem(21));
+ DisguiseAPI.disguiseToAll(player, miscDisguise);
+ }
+
+ }
+
+
+ }.runTaskLater(BlockAndSeek.getInstance(), 300L);
+
+ if (openInventory) {
+ player.openInventory(gui);
+ player.setMetadata("RollingMenu", new FixedMetadataValue(BlockAndSeek.getInstance(), "RollingMenu"));
+ }
+
+
+ }
+
+
+ @Override
+ public @NotNull Inventory getInventory() {
+
+ Inventory gui = Bukkit.createInventory(this, 45, Component.text("РулетОЧКА"));
+
+ ItemStack filler = new ItemStack(Material.BLUE_STAINED_GLASS_PANE);
+ ItemMeta fillerMeta = filler.getItemMeta();
+ fillerMeta.displayName(Component.text(""));
+ fillerMeta.lore(null);
+ fillerMeta.addItemFlags(ItemFlag.HIDE_ATTRIBUTES);
+ filler.setItemMeta(fillerMeta);
+ for (int i = 0; i < 45; i++) {
+ gui.setItem(i, filler);
+ }
+ return gui;
+ }
+}
diff --git a/src/main/java/hdvtdev/blockAndSeek/roulette/RouletteGenerator.java b/src/main/java/hdvtdev/blockAndSeek/roulette/RouletteGenerator.java
new file mode 100644
index 0000000..d73c724
--- /dev/null
+++ b/src/main/java/hdvtdev/blockAndSeek/roulette/RouletteGenerator.java
@@ -0,0 +1,30 @@
+package hdvtdev.blockAndSeek.roulette;
+
+import com.lewdev.probabilitylib.ProbabilityCollection;
+import org.bukkit.inventory.ItemStack;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class RouletteGenerator {
+
+
+ private final ProbabilityCollection probabilityCollection = new ProbabilityCollection<>();
+
+ public RouletteGenerator(List blocks) {
+ for (Block block : blocks) {
+ probabilityCollection.add(block.itemStack, block.chance);
+ }
+ }
+
+ public List getRandomRow(int count) {
+ List items = new ArrayList<>(count);
+ for (; count > 0; --count) items.add(probabilityCollection.get());
+ return items;
+ }
+
+ public record Block(ItemStack itemStack, int chance) {
+ }
+
+
+}
diff --git a/src/main/java/hdvtdev/blockAndSeek/roulette/RouletteList.java b/src/main/java/hdvtdev/blockAndSeek/roulette/RouletteList.java
new file mode 100644
index 0000000..d1e3524
--- /dev/null
+++ b/src/main/java/hdvtdev/blockAndSeek/roulette/RouletteList.java
@@ -0,0 +1,23 @@
+package hdvtdev.blockAndSeek.roulette;
+
+import java.util.List;
+import java.util.Objects;
+
+public class RouletteList {
+ private final List items;
+ private int currentIndex;
+
+ public RouletteList(List items) {
+ this.items = Objects.requireNonNull(items);
+ this.currentIndex = 0;
+ if (items.isEmpty()) {
+ throw new IllegalArgumentException("List must not be empty");
+ }
+ }
+
+ public T next() {
+ T item = items.get(currentIndex);
+ currentIndex = (currentIndex + 1) % items.size();
+ return item;
+ }
+}
diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml
index 4287ca8..8e3b571 100644
--- a/src/main/resources/config.yml
+++ b/src/main/resources/config.yml
@@ -1 +1,3 @@
-#
\ No newline at end of file
+# Selected language in localization.yml (en_US is used by default)
+language: en_US
+
diff --git a/src/main/resources/localization.yml b/src/main/resources/localization.yml
new file mode 100644
index 0000000..73dba61
--- /dev/null
+++ b/src/main/resources/localization.yml
@@ -0,0 +1,10 @@
+en_US:
+ #Maps
+ maps-available: "Available maps: "
+ maps-available-element: "- {color-status}{map}"
+
+ not-enough-permissions: "You do not have permission to run this command."
+ not-enough-arguments: "Too few arguments to run command {command}. Arguments example: {help}"
+
+ successful-reload: "Successfully reloaded {config}."
+ failed-reload: "Failed to reload {config}. Error: {e}"
\ No newline at end of file
diff --git a/src/main/resources/maps.yml b/src/main/resources/maps.yml
index e69de29..10855ad 100644
--- a/src/main/resources/maps.yml
+++ b/src/main/resources/maps.yml
@@ -0,0 +1 @@
+# DO NOT edit this file manually! Use plugin commands to edit maps instead.
\ No newline at end of file
diff --git a/src/main/resources/messages.yml b/src/main/resources/messages.yml
deleted file mode 100644
index 8bab099..0000000
--- a/src/main/resources/messages.yml
+++ /dev/null
@@ -1,15 +0,0 @@
-prefix: "[BlockAndSeek] "
-bs-usage: "Usage: /blockandseek [SUBCOMMAND]"
-
-messages-reload: "Successfully reload messages.yml"
-not-enough-arguments: "Too few args. Usage: {usage}."
-unknown-subcommand: "Unknown subcommand {subcommand}."
-
-#Map
-maps-list: "Available maps: {maps}"
-maps-reload: "Successfully reload maps.yml"
-map-created: "Successfully created map {map}."
-map-missing-world: "Could not find world {world} in server folder. An existing world is required to create a map"
-map-creation-failed: "Failed to create map {map}. Error: {error}."
-map-already-exist: "Map {map} already exist!"
-map-not-exist: "Map {map} does not exist!"
\ No newline at end of file
diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml
index c6029b2..6e9a625 100644
--- a/src/main/resources/plugin.yml
+++ b/src/main/resources/plugin.yml
@@ -2,6 +2,12 @@ name: BlockAndSeek
version: '0.0.1-a'
main: hdvtdev.blockAndSeek.BlockAndSeek
api-version: '1.20'
+
+permissions:
+ blockandseek.manage:
+ description: "Permission to use this subcommands: reload, map"
+ default: op
+
commands:
blockandseek:
aliases: