diff --git a/.gradle/buildOutputCleanup/buildOutputCleanup.lock b/.gradle/buildOutputCleanup/buildOutputCleanup.lock index b4911b7..34130c3 100644 Binary files a/.gradle/buildOutputCleanup/buildOutputCleanup.lock and b/.gradle/buildOutputCleanup/buildOutputCleanup.lock differ diff --git a/.idea/compiler.xml b/.idea/compiler.xml index b86273d..bc52ee8 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -1,6 +1,15 @@ - + + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index 5cd9a10..87a20fc 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,7 +1,7 @@ - + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml index 1b7e58d..ae6737f 100644 --- a/.idea/modules.xml +++ b/.idea/modules.xml @@ -4,6 +4,7 @@ + \ No newline at end of file diff --git a/.idea/modules/BlockAndSeek.main.iml b/.idea/modules/BlockAndSeek.main.iml index bbeeb3e..d329813 100644 --- a/.idea/modules/BlockAndSeek.main.iml +++ b/.idea/modules/BlockAndSeek.main.iml @@ -1,5 +1,10 @@ + + + + + diff --git a/build.gradle b/build.gradle index 7af82b3..090d6fd 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,9 @@ +import java.nio.ByteBuffer +import java.nio.ByteOrder + plugins { id 'java' - id("xyz.jpenilla.run-paper") version "2.3.1" + id "com.gradleup.shadow" version "9.2.2" } group = 'hdvtdev' @@ -8,6 +11,7 @@ version = '0.0.1-a' repositories { mavenCentral() + maven { url 'https://storehouse.okaeri.eu/repository/maven-releases/'} maven { url 'https://repo.md-5.net/content/groups/public/' } maven { url 'https://jitpack.io' } maven { @@ -21,15 +25,24 @@ repositories { maven { url 'https://libraries.minecraft.net/' } } +def okaeriConfigsVersion = '5.0.13' + dependencies { compileOnly("io.papermc.paper:paper-api:1.20.4-R0.1-SNAPSHOT") - implementation group: 'me.libraryaddict.disguises', name: 'libsdisguises', version: '11.0.6' - //TODO implementation 'me.lucko:commodore:2.2' + compileOnly group: 'me.libraryaddict.disguises', name: 'libsdisguises', version: '11.0.6' + compileOnly 'org.projectlombok:lombok:1.18.42' + annotationProcessor 'org.projectlombok:lombok:1.18.42' + implementation "eu.okaeri:okaeri-configs-validator-okaeri:$okaeriConfigsVersion" + implementation "eu.okaeri:okaeri-configs-yaml-bukkit:$okaeriConfigsVersion" + implementation "eu.okaeri:okaeri-configs-serdes-bukkit:$okaeriConfigsVersion" + implementation "eu.okaeri:okaeri-configs-serdes-commons:$okaeriConfigsVersion" + + implementation "org.incendo:cloud-paper:2.0.0-beta.13" } -def targetJavaVersion = 21 +def targetJavaVersion = 17 java { def javaVersion = JavaVersion.toVersion(targetJavaVersion) sourceCompatibility = javaVersion @@ -56,14 +69,83 @@ 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') || it.name.startsWith('commodore') || it.name.startsWith('brigadier') - }.collect { zipTree(it) } - } - duplicatesStrategy = DuplicatesStrategy.EXCLUDE +tasks.shadowJar { + archiveClassifier.set("") + relocate("eu.okaeri", "hdvtdev.blockandseek.libs.okaeri") + relocate("org.incendo", "hdvtdev.blockandseek.libs.cloud") } +tasks.build { + dependsOn(tasks.shadowJar) +} + +tasks.register('deploy') { + dependsOn build + + + doLast { + def targetDir = project.property('server.plugins.dir') + def rconHost = project.property('rcon.host') + def rconPort = project.property('rcon.port') as int + def rconPass = project.property('rcon.password') + + + def jarFile = tasks.jar.archiveFile.get().asFile + + if (!file(targetDir).exists()) { + println "Err folder not found : $targetDir" + return + } + + copy { + from jarFile + into targetDir + } + + try { + sendRconCommand(rconHost, rconPort, rconPass, "plugman reload BlockAndSeek") + sendRconCommand(rconHost, rconPort, rconPass, "reload") + println "Plugin reloaded" + } catch (Exception e) { + println "RCON: ${e.message}" + } + } +} + +def sendRconCommand(String host, int port, String password, String command) { + new Socket(host, port).withCloseable { socket -> + def out = socket.outputStream + def inp = socket.inputStream + + def sendPacket = { int id, int type, String body -> + byte[] bodyBytes = body.getBytes("UTF-8") + int length = 4 + 4 + bodyBytes.length + 2 // id + type + body + 2 nulls + ByteBuffer buffer = ByteBuffer.allocate(4 + length) + buffer.order(ByteOrder.LITTLE_ENDIAN) + + buffer.putInt(length) + buffer.putInt(id) + buffer.putInt(type) + buffer.put(bodyBytes) + buffer.put((byte) 0) + buffer.put((byte) 0) + + out.write(buffer.array()) + out.flush() + } + + + sendPacket(1, 3, password) + + inp.read(new byte[4096]) + + sendPacket(2, 2, command) + + byte[] buffer = new byte[4096] + int read = inp.read(buffer) + if (read > 12) { + String response = new String(buffer, 12, read - 12 - 2, "UTF-8") + println "Server response: $response" + } + } +} diff --git a/gradle.properties b/gradle.properties index e69de29..07be753 100644 --- a/gradle.properties +++ b/gradle.properties @@ -0,0 +1,7 @@ +plugin.name=MySuperPlugin + +server.plugins.dir=/home/hadvart/Documents/minecraft/Pufferfish/1.21.8/plugins + +rcon.host=127.0.0.1 +rcon.port=25575 +rcon.password=14881488 \ No newline at end of file diff --git a/src/main/java/hdvtdev/blockAndSeek/BlockAndSeekGame.java b/src/main/java/hdvtdev/blockAndSeek/BlockAndSeekGame.java deleted file mode 100644 index 071e9c9..0000000 --- a/src/main/java/hdvtdev/blockAndSeek/BlockAndSeekGame.java +++ /dev/null @@ -1,315 +0,0 @@ -package hdvtdev.blockAndSeek; - -import hdvtdev.blockAndSeek.managers.ConfigManager; -import hdvtdev.blockAndSeek.managers.GamesManager; -import hdvtdev.blockAndSeek.managers.ItemManager; -import hdvtdev.blockAndSeek.managers.PropManager; -import hdvtdev.blockAndSeek.roulette.RouletteCreator; -import me.libraryaddict.disguise.DisguiseAPI; -import org.bukkit.*; -import org.bukkit.entity.Player; -import org.bukkit.inventory.PlayerInventory; -import org.bukkit.persistence.PersistentDataContainer; -import org.bukkit.persistence.PersistentDataType; -import org.bukkit.scheduler.BukkitRunnable; -import org.jetbrains.annotations.Nullable; - -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; - -public class BlockAndSeekGame { - - private final Map players = new ConcurrentHashMap<>(); - private final Map hiderRoulette = new HashMap<>(); - - private volatile boolean started = false; - private final BlockAndSeekMap map; - private final Location lobby; - private final Location spawn; - private final String name; - - public BlockAndSeekGame(String name, BlockAndSeekMap map) { - this.map = map; - World world = Bukkit.getWorld(name); - world.setTime(1000); - world.setGameRule(GameRule.DO_DAYLIGHT_CYCLE, false); - world.setStorm(false); - world.setGameRule(GameRule.DO_WEATHER_CYCLE, false); - this.name = name; - this.lobby = map.getLobbyLocation(world); - this.spawn = map.getSpawnLocation(world); - this.startBukkitTask(); - } - - public int playerCount() { - return players.size(); - } - - public boolean isStarted() { - return started; - } - - public int maxPlayers() { - return map.getMaxPlayers(); - } - - public boolean addPlayer(Player player) { - if (!started) { - players.put(player, PlayerType.HIDER); - player.getPersistentDataContainer().set(Keys.GAME, PersistentDataType.STRING, name); - player.teleport(lobby); - PlayerInventory inventory = player.getInventory(); - inventory.clear(); - inventory.setItem(8, Localization.translateItem(player, ItemManager.getLeaveItem())); - player.setGameMode(GameMode.SURVIVAL); - player.setInvulnerable(true); - player.setHealth(20.0); - player.setFoodLevel(20); - Localization.sendMessage( - players.keySet(), - true, - "player-join", - "{player}", player.getName(), - "{players}", players.size() + "/" + map.getMaxPlayers() - ); - return true; - } else return false; - } - - public void removePlayer(Player player) { - players.remove(player); - DisguiseAPI.undisguiseToAll(player); - player.getInventory().clear(); - player.setGameMode(GameMode.SURVIVAL); - player.getPersistentDataContainer().remove(Keys.GAME); - Config config = ConfigManager.getConfig(); - if (config.forceControl()) ItemManager.defaultInventory(player); - player.teleport(config.defaultSpawn()); - - - Localization.sendMessage( - players.keySet(), - true, - "player-leave", - "{player}", player.getName(), - "{players}", started ? "" : players.size() + "/" + map.getMaxPlayers() - ); - } - - public void setSpectator(Player hider, @Nullable Player seeker) { - hider.spigot().respawn(); - hider.teleport(hider.getLastDeathLocation()); - players.put(hider, PlayerType.SPECTATOR); - hider.setGameMode(GameMode.SPECTATOR); - Localization.sendMessage( - players.keySet(), - true, - seeker == null ? "hider-died" : "hider-was-found", - "{hider}", hider.getName(), - "{seeker}", seeker == null ? "" : seeker.getName() - ); - } - - private long getHidersCount() { - return players.values().stream().filter(type -> type == PlayerType.HIDER).count(); - } - - private long getSeekersCount() { - return players.values().stream().filter(type -> type == PlayerType.SEEKER).count(); - } - - private List getSeekers() { - return players.entrySet().stream().filter(entry -> entry.getValue() == PlayerType.SEEKER).map(Map.Entry::getKey).toList(); - } - - private List getHiders() { - return players.entrySet().stream().filter(entry -> entry.getValue() == PlayerType.HIDER).map(Map.Entry::getKey).toList(); - } - - private Player getLastHider() { - return players.keySet().iterator().next(); - } - - private void start() { - started = true; - selectRandomSeekers((int) Math.round(players.size() * 0.25)); - List hiders = getHiders(); - for (Player hider : hiders) { - hider.getPersistentDataContainer().set(Keys.HIDER, PersistentDataType.BOOLEAN, true); - hider.teleport(spawn); - hider.setInvulnerable(false); - PlayerInventory inventory = hider.getInventory(); - inventory.clear(); - inventory.addItem(Localization.translateItem(hider, ItemManager.getFreezeItem())); - inventory.addItem(Localization.translateItem(hider, ItemManager.getFaceChangingItem())); - hiderRoulette.put(hider, new RouletteCreator(hider, map.getBlocks())); - } - } - - private void end(boolean force) { - GamesManager.remove(name); - if (!force) { - Config config = ConfigManager.getConfig(); - Location serverLobby = config.defaultSpawn(); - boolean defaultInventory = config.forceControl(); - for (Player player : players.keySet()) { - - - DisguiseAPI.undisguiseToAll(player); - if (defaultInventory) ItemManager.defaultInventory(player); - player.setGlowing(false); - player.setInvulnerable(false); - Utils.setLevelWithBar(player, 0); - player.setVisibleByDefault(true); - player.setHealth(20); - player.setGameMode(GameMode.SURVIVAL); - player.teleport(serverLobby); - - } - } - } - - private void preEnd() { - for (Player player : players.keySet()) { - PersistentDataContainer container = player.getPersistentDataContainer(); - player.setInvulnerable(true); - container.remove(Keys.HIDER); - container.remove(Keys.SEEKER); - container.remove(Keys.GAME); - } - for (Player hider : getHiders()) { - hider.getInventory().clear(); - PropManager.unfreezeIfFrozen(hider); - hider.setGlowing(true); - RouletteCreator rouletteCreator = hiderRoulette.get(hider); - rouletteCreator.getTask().cancelBoth(); - rouletteCreator.closeInventory(); - } - } - - private void selectRandomSeekers(int count) { - ArrayList rawSeekers = new ArrayList<>(); - Set playerSet = players.keySet(); - for (Player player : playerSet) { - if (!GamesManager.triggerSeekerImmune(player)) rawSeekers.add(player); - } - Collections.shuffle(rawSeekers); - - for (Player seeker : rawSeekers.subList(0, Math.min(count, playerSet.size()))) { - players.put(seeker, PlayerType.SEEKER); - ItemManager.setSeekerSet(seeker); - Utils.setLevelWithBar(seeker, 100); - seeker.setInvulnerable(false); - seeker.getPersistentDataContainer().set(Keys.SEEKER, PersistentDataType.BOOLEAN, true); - GamesManager.addSeekerImmune(seeker); - } - - } - - private void startBukkitTask() { - new BukkitRunnable() { - - int waitTime = 30; - final int defaultWaitTime = waitTime; - int duration = map.getDuration(); - final int seekerDeploy = duration - 15; - - - @Override - public void run() { - if (waitTime != 0) { - int playerSize = players.size(); - if (playerSize >= map.getMinPlayers()) { - if (waitTime % 5 == 0 || waitTime <= 5) { - Localization.sendMessage( - players.keySet(), - true, - "wait-time-left", - "{time}", String.valueOf(waitTime) - ); - } - waitTime--; - } else if (playerSize == 0) { - end(true); - this.cancel(); - } else if (waitTime != defaultWaitTime) { - waitTime = defaultWaitTime; - } - } else { - if (!started) { - start(); - } else { - - if (players.isEmpty()) { - end(false); - this.cancel(); - return; - } - - if (duration > 0 && getHidersCount() == 0) { - Localization.sendTitle( - players.keySet(), - false, - "seekers-won" - ); - preEnd(); - duration = -1; - } - - if (duration == seekerDeploy) { - for (Player seeker : getSeekers()) { - seeker.teleport(spawn); - } - } - - if (duration > 0 && getSeekersCount() == 0) duration = 0; - - if (duration == 0) { - - preEnd(); - if (getHidersCount() == 1) { - Localization.sendTitle( - players.keySet(), - false, - "hiders-solo-win", - "{hider}", getLastHider().getName() - ); - } else { - Localization.sendTitle( - players.keySet(), - false, - "hiders-won" - ); - } - duration--; // уменьшаем только один раз - } else if (duration > 0) { - Localization.sendActionBar( - players.keySet(), - false, - "game-time-left", - "{time}", String.valueOf(duration) - ); - duration--; - } else if (duration == -10) { - end(false); - this.cancel(); - } else { - duration--; - } - } - } - } - - - }.runTaskTimer(BlockAndSeek.getInstance(), 0L, 20L); - } - - - private enum PlayerType { - SEEKER, - HIDER, - SPECTATOR - } - - -} diff --git a/src/main/java/hdvtdev/blockAndSeek/BlockAndSeekMap.java b/src/main/java/hdvtdev/blockAndSeek/BlockAndSeekMap.java deleted file mode 100644 index a8bbfb7..0000000 --- a/src/main/java/hdvtdev/blockAndSeek/BlockAndSeekMap.java +++ /dev/null @@ -1,173 +0,0 @@ -package hdvtdev.blockAndSeek; - - -import org.bukkit.Location; -import org.bukkit.Material; -import org.bukkit.World; -import org.bukkit.configuration.serialization.ConfigurationSerializable; -import org.bukkit.inventory.ItemStack; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.*; - -public class BlockAndSeekMap implements ConfigurationSerializable { - - 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 Location getSpawnLocation(@Nullable World world) { - return new Location(world, spawn.getFirst(), spawn.get(1), spawn.get(2)); - } - - public void setSpawn(List spawn) { - this.spawn = spawn; - } - - public List getLobby() { - return lobby; - } - - public Location getLobbyLocation(@Nullable World world) { - return new Location(world, lobby.getFirst(), lobby.get(1), lobby.get(2)); - } - - 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/BlockAndSeek.java b/src/main/java/hdvtdev/blockandseek/BlockAndSeek.java index 12d32df..6d7bf5b 100644 --- a/src/main/java/hdvtdev/blockandseek/BlockAndSeek.java +++ b/src/main/java/hdvtdev/blockandseek/BlockAndSeek.java @@ -1,31 +1,39 @@ -package hdvtdev.blockAndSeek; +package hdvtdev.blockandseek; -import hdvtdev.blockAndSeek.eventListeners.RequiredEventListener; -import hdvtdev.blockAndSeek.eventListeners.EventListener; -import hdvtdev.blockAndSeek.eventListeners.ForceControlEventListener; +import hdvtdev.blockandseek.eventListeners.RequiredEventListener; +import hdvtdev.blockandseek.eventListeners.EventListener; +import hdvtdev.blockandseek.eventListeners.ForceControlEventListener; +import hdvtdev.blockandseek.managers.*; +import hdvtdev.blockandseek.objects.*; +import hdvtdev.blockandseek.roulette.RouletteCreator; import me.libraryaddict.disguise.LibsDisguises; -import org.bukkit.Bukkit; +import net.kyori.adventure.text.minimessage.MiniMessage; + +import org.bukkit.*; import org.bukkit.command.CommandSender; import org.bukkit.command.PluginCommand; import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; import org.bukkit.plugin.Plugin; import org.bukkit.plugin.PluginManager; import org.bukkit.plugin.java.JavaPlugin; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; +import org.bukkit.scheduler.BukkitRunnable; -import org.incendo.cloud.Command; import org.incendo.cloud.bukkit.CloudBukkitCapabilities; -import org.incendo.cloud.bukkit.parser.PlayerParser; +import org.incendo.cloud.bukkit.parser.location.LocationParser; import org.incendo.cloud.execution.ExecutionCoordinator; import org.incendo.cloud.paper.LegacyPaperCommandManager; -import org.incendo.cloud.parser.standard.IntegerParser; import org.incendo.cloud.parser.standard.StringParser; import java.io.File; -import java.util.Objects; +import java.util.*; import java.util.logging.Logger; +import java.util.stream.Collectors; public class BlockAndSeek extends JavaPlugin { @@ -66,17 +74,13 @@ public class BlockAndSeek extends JavaPlugin { this.init(); - if (!Config.loadConfig()) getPluginLogger().warning("Failed to load BlockAndSeek config.toml! Using default config..."); - - PluginCommand command = Objects.requireNonNull(getCommand("blockandseek")); - PluginManager manager = getServer().getPluginManager(); - manager.registerEvents(Config.forceControl() ? new ForceControlEventListener() : new EventListener(), this); + boolean forceControl = Config.forceControl(); + if (forceControl) getLogger().info("Using force control"); + manager.registerEvents(forceControl ? new ForceControlEventListener() : new EventListener(), this); manager.registerEvents(new RequiredEventListener(), this); - - } @@ -105,6 +109,9 @@ public class BlockAndSeek extends JavaPlugin { saveResource("languages/en_US.yml", false); } + MapsManager.loadMaps(); + TranslationManager.loadLanguages(); + commandManager = LegacyPaperCommandManager.createNative( this, ExecutionCoordinator.simpleCoordinator() @@ -130,53 +137,226 @@ public class BlockAndSeek extends JavaPlugin { } + private final PropManager propManager = new PropManager(); + private void registerCommands() { - Command.Builder root = commandManager.commandBuilder("blockandseek"); + var root = commandManager.commandBuilder("blockandseek"); + + var editBase = root + .literal("editMap") + .required("map_name", StringParser.stringParser(), MapsManager.mapSuggestions); + + //edit commands + + + commandManager.command( + editBase.literal("setLobby") // Это наш как литерал + .required("location", LocationParser.locationParser()) + .handler(ctx -> { + + }) + ); + + commandManager.command( + editBase.literal("setLocation") + .required("location", LocationParser.locationParser()) // Это + .handler(ctx -> { + String configName = ctx.get("config_name"); + Location location = ctx.get("location"); + + ctx.sender().sendMessage("В конфиге " + configName + " точка установлена: " + location.toString()); + }) + ); + + commandManager.command(root + .literal("maps") + .handler(ctx -> { + ctx.sender().sendMessage(MapsManager.getMaps().stream().map(BlockAndSeekMap::getWorld).collect(Collectors.joining(", "))); + }) + ); + + commandManager.command(root + .literal("testMessage") + .handler(ctx -> { + ctx.sender().sendMessage(MiniMessage.miniMessage().deserialize(" System » Ваш игровой режим изменен.\n")); + }) + ); + + commandManager.command(root + .literal("menuTest") + .handler(ctx -> { + if (ctx.sender() instanceof Player player) { + player.getInventory().addItem(ItemManager.getMenuItem()); + } + }) + ); commandManager.command(root - .literal("reload") + .literal("localeTest") + .handler(ctx -> { + if (ctx.sender() instanceof Player player) { + player.sendMessage("Locale is: " + player.locale()); + player.sendMessage(TranslationManager.get(player, TranslationKey.FREEZE_ITEM)); + } + }) + ); + + + commandManager.command(root + .literal("createGame") .permission(perm) + .required("map_name", StringParser.stringParser(), MapsManager.mapSuggestions) .handler(context -> { - + String name = context.get("map_name"); + GamesManager.createGame(name); + if (context.sender() instanceof Player player) { + GamesManager.get(name).addPlayer(player); + } }) ); - - commandManager.command(root - .literal("inttest") - .required("text", IntegerParser.integerParser(0, 64)) - - .handler(context -> { - String text = context.get("text"); - context.sender().sendMessage(text); - }) - ); - - - commandManager.command(root - .literal("map") + .literal("createMap") .permission(perm) - .required("map", StringParser.stringParser()) - .required("action", StringParser.stringParser()) + .required("map_name", StringParser.stringParser(), MapsManager.worldSuggestions) .handler(context -> { - Player target = context.get("target"); - int amount = context.getOrDefault("amount", 1); - - + String name = context.get("map_name"); + MapsManager.createMap(name); + context.sender().sendMessage("Map probably created"); }) ); + commandManager.command(root + .literal("joinGame") + .permission(perm) + .required("name", StringParser.stringParser()) + .handler(context -> { + String name = context.get("name"); + if (context.sender() instanceof Player player) { + BlockAndSeekGame game = GamesManager.get(name); + if (game != null) game.addPlayer(player); + } + }) + ); + + commandManager.command( + editBase.literal("generateBlocks") + .handler(ctx -> { + if (ctx.sender() instanceof Player player) { + + String mapName = ctx.get("map_name"); + BlockAndSeekMap map = MapsManager.getMap(mapName); + + var cached = BlocksGenerator.get(map.getSpawn().getWorld().getName()); + + if (cached != null) { + new RouletteCreator(player, cached); + } else { + BlocksGenerator.getSortedBlockStats(player.getLocation(), 32, (var list) -> { + + List> selectedBlocks = new ArrayList<>(list); + + // 2. Перемешиваем и берем 30 случайных (или меньше, если всего блоков меньше 30) + Collections.shuffle(selectedBlocks); + if (selectedBlocks.size() > 30) { + selectedBlocks = selectedBlocks.subList(0, 30); + } + + selectedBlocks.sort((a, b) -> Long.compare(b.getValue(), a.getValue())); + + List> result = new ArrayList<>(); + + // 4. Считаем общий вес редкостей (100 + 50 + 25 + ...) = 189 + int totalWeight = Arrays.stream(Rarity.values()).mapToInt(Rarity::getChance).sum(); + + // Шаг веса на один блок. + // Представь длинную полоску длиной 189 единиц. Мы режем её на 30 равных частей. + // Куда попадает разрез — такая и редкость. + double weightStep = (double) totalWeight / selectedBlocks.size(); + + double currentWeightPosition = 0; // Текущая позиция на шкале весов + + // Идем по отсортированному списку (от самых частых к самым редким) + for (Map.Entry entry : selectedBlocks) { + + // Определяем, в какой диапазон редкости попадает текущий блок + Rarity assignedRarity = Rarity.COMMON; // Дефолт + + int weightCursor = 0; + for (Rarity r : Rarity.values()) { + weightCursor += r.getChance(); + // Если текущая позиция веса меньше границы этой редкости — мы нашли её + // (Добавляем половину шага, чтобы брать "центр" диапазона блока для точности) + if ((currentWeightPosition + weightStep / 2) <= weightCursor) { + assignedRarity = r; + break; + } + } + + result.add(new AbstractMap.SimpleEntry<>(entry.getKey(), assignedRarity)); + + // Сдвигаемся дальше по шкале + currentWeightPosition += weightStep; + } + var r = result.stream().map(e -> new PropBlock(new ItemStack(e.getKey()), e.getValue())).toList(); + map.setBlocks(r); + + map.save(); + BlocksGenerator.addCache(player.getLocation().getWorld().getName(), r); + + + + }); + } + + + } + + }) + ); + } + + private void spawnSmokeCloud(Location center) { + + new BukkitRunnable() { + int ticksPassed = 0; + + final int maxIterations = (10 * 20) / 5; + + @Override + public void run() { + if (ticksPassed >= maxIterations) { + this.cancel(); + return; + } + + World world = center.getWorld(); + if (world == null) return; + + + world.spawnParticle(Particle.EXPLOSION_HUGE, center, 15, 1.2, 1.2, 1.2, 0.05); + + + + for (var entity : world.getNearbyEntities(center, 2.5, 2.5, 2.5)) { + if (entity instanceof Player player && player.getLocation().distanceSquared(center) <= 9) { + player.addPotionEffect(new PotionEffect(PotionEffectType.BLINDNESS, 40, 1)); + } + } + + ticksPassed++; + } + }.runTaskTimer(this, 0L, 5L); } } diff --git a/src/main/java/hdvtdev/blockandseek/BlocksGenerator.java b/src/main/java/hdvtdev/blockandseek/BlocksGenerator.java index 8b42902..f943831 100644 --- a/src/main/java/hdvtdev/blockandseek/BlocksGenerator.java +++ b/src/main/java/hdvtdev/blockandseek/BlocksGenerator.java @@ -1,50 +1,191 @@ -package hdvtdev.blockAndSeek; +package hdvtdev.blockandseek; +import hdvtdev.blockandseek.objects.PropBlock; import org.bukkit.*; -import org.bukkit.block.data.AnaloguePowerable; -import org.bukkit.block.data.BlockData; -import org.bukkit.block.data.Openable; -import org.bukkit.block.data.Powerable; +import org.bukkit.block.Banner; +import org.bukkit.block.Comparator; +import org.bukkit.block.data.*; +import org.bukkit.block.data.type.Observer; +import org.bukkit.block.data.type.Piston; +import org.bukkit.block.data.type.RedstoneWire; import org.bukkit.scheduler.BukkitRunnable; -import org.jetbrains.annotations.ApiStatus; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Nullable; + +import java.util.*; +import java.util.concurrent.ThreadLocalRandom; import java.util.function.Consumer; @ApiStatus.Experimental public class BlocksGenerator { - private static boolean isSupported(Material type, BlockData data) { + private static final Set BLACKLIST = EnumSet.noneOf(Material.class); + private static final Map> cached = new HashMap<>(); - if (type.isAir() || type.hasGravity() || !type.isSolid()) { + static { + // --- 1. ГРАВИТАЦИЯ (Сыпучие) --- + BLACKLIST.add(Material.SAND); + BLACKLIST.add(Material.RED_SAND); + BLACKLIST.add(Material.GRAVEL); + BLACKLIST.add(Material.DRAGON_EGG); + BLACKLIST.add(Material.ANVIL); + BLACKLIST.add(Material.CHIPPED_ANVIL); + BLACKLIST.add(Material.DAMAGED_ANVIL); + BLACKLIST.add(Material.POINTED_DRIPSTONE); // Сталактит (может упасть) + BLACKLIST.add(Material.SCAFFOLDING); // Строительные леса + + // --- 2. РЕДСТОУН И МЕХАНИЗМЫ --- + BLACKLIST.addAll(Tag.BUTTONS.getValues()); // Все кнопки + BLACKLIST.addAll(Tag.PRESSURE_PLATES.getValues()); // Все нажимные плиты + BLACKLIST.addAll(Tag.DOORS.getValues()); // Двери + BLACKLIST.addAll(Tag.TRAPDOORS.getValues()); // Люки + BLACKLIST.addAll(Tag.FENCE_GATES.getValues()); // Калитки + BLACKLIST.addAll(Tag.RAILS.getValues()); // Рельсы + + BLACKLIST.add(Material.REDSTONE_WIRE); + BLACKLIST.add(Material.REDSTONE_TORCH); + BLACKLIST.add(Material.REDSTONE_BLOCK); // Если не нужен блок редстоуна + BLACKLIST.add(Material.REPEATER); + BLACKLIST.add(Material.COMPARATOR); + BLACKLIST.add(Material.OBSERVER); + BLACKLIST.add(Material.PISTON); + BLACKLIST.add(Material.STICKY_PISTON); + BLACKLIST.add(Material.PISTON_HEAD); + BLACKLIST.add(Material.MOVING_PISTON); + BLACKLIST.add(Material.DISPENSER); + BLACKLIST.add(Material.DROPPER); + BLACKLIST.add(Material.HOPPER); + BLACKLIST.add(Material.DAYLIGHT_DETECTOR); + BLACKLIST.add(Material.TRIPWIRE); + BLACKLIST.add(Material.TRIPWIRE_HOOK); + BLACKLIST.add(Material.LEVER); + BLACKLIST.add(Material.TNT); + BLACKLIST.add(Material.NOTE_BLOCK); + BLACKLIST.add(Material.JUKEBOX); + BLACKLIST.add(Material.SCULK_SENSOR); + BLACKLIST.add(Material.SCULK_SHRIEKER); + BLACKLIST.add(Material.SCULK_CATALYST); + BLACKLIST.add(Material.COMMAND_BLOCK); + BLACKLIST.add(Material.REPEATING_COMMAND_BLOCK); + BLACKLIST.add(Material.CHAIN_COMMAND_BLOCK); + BLACKLIST.add(Material.LIGHTNING_ROD); + BLACKLIST.add(Material.BELL); // Колокол + + // --- 3. ХРУПКИЕ / ЗАВИСЯЩИЕ ОТ ОПОРЫ (Растения, декор) --- + BLACKLIST.addAll(Tag.SAPLINGS.getValues()); // Саженцы + BLACKLIST.addAll(Tag.FLOWERS.getValues()); // Цветы + BLACKLIST.addAll(Tag.CROPS.getValues()); // Пшеница, картошка и т.д. + BLACKLIST.addAll(Tag.BANNERS.getValues()); // Флаги (стоячие и настенные) + BLACKLIST.addAll(Tag.SIGNS.getValues()); // Таблички + BLACKLIST.addAll(Tag.BEDS.getValues()); // Кровати + BLACKLIST.addAll(Tag.CANDLES.getValues()); // Свечи + BLACKLIST.addAll(Tag.CAMPFIRES.getValues()); // Костры + BLACKLIST.addAll(Tag.CAULDRONS.getValues()); // Котлы + BLACKLIST.addAll(Tag.WOOL_CARPETS.getValues()); // Ковры + BLACKLIST.addAll(Tag.FLOWER_POTS.getValues()); // Горшки + + BLACKLIST.add(Material.BROWN_MUSHROOM); + BLACKLIST.add(Material.RED_MUSHROOM); + BLACKLIST.add(Material.CRIMSON_FUNGUS); + BLACKLIST.add(Material.WARPED_FUNGUS); + BLACKLIST.add(Material.SUGAR_CANE); + BLACKLIST.add(Material.BAMBOO); + BLACKLIST.add(Material.BAMBOO_SAPLING); + BLACKLIST.add(Material.CACTUS); + BLACKLIST.add(Material.DEAD_BUSH); + BLACKLIST.add(Material.SWEET_BERRY_BUSH); + BLACKLIST.add(Material.PUMPKIN_STEM); + BLACKLIST.add(Material.MELON_STEM); + BLACKLIST.add(Material.NETHER_WART); + BLACKLIST.add(Material.CHORUS_PLANT); + BLACKLIST.add(Material.CHORUS_FLOWER); + BLACKLIST.add(Material.KELP); + BLACKLIST.add(Material.KELP_PLANT); + BLACKLIST.add(Material.SEAGRASS); + BLACKLIST.add(Material.TALL_SEAGRASS); + BLACKLIST.add(Material.VINE); + BLACKLIST.add(Material.WEEPING_VINES); + BLACKLIST.add(Material.TWISTING_VINES); + BLACKLIST.add(Material.LILY_PAD); + BLACKLIST.add(Material.SMALL_DRIPLEAF); + BLACKLIST.add(Material.BIG_DRIPLEAF); + BLACKLIST.add(Material.SPORE_BLOSSOM); + BLACKLIST.add(Material.HANGING_ROOTS); + BLACKLIST.add(Material.GLOW_LICHEN); + BLACKLIST.add(Material.AMETHYST_CLUSTER); + BLACKLIST.add(Material.SMALL_AMETHYST_BUD); + BLACKLIST.add(Material.MEDIUM_AMETHYST_BUD); + BLACKLIST.add(Material.LARGE_AMETHYST_BUD); + BLACKLIST.add(Material.POINTED_DRIPSTONE); + BLACKLIST.add(Material.COCOA); + + BLACKLIST.add(Material.TORCH); + BLACKLIST.add(Material.SOUL_TORCH); + BLACKLIST.add(Material.LANTERN); + BLACKLIST.add(Material.SOUL_LANTERN); + BLACKLIST.add(Material.CHAIN); + BLACKLIST.add(Material.END_ROD); + BLACKLIST.add(Material.IRON_BARS); // Обычно некрасиво в топах + BLACKLIST.add(Material.LADDER); + BLACKLIST.add(Material.SNOW); // Снежный покров (тонкий) + BLACKLIST.add(Material.TURTLE_EGG); + BLACKLIST.add(Material.CAKE); + + // --- 4. КОНТЕЙНЕРЫ (Если не хочешь, чтобы выпадали сундуки) --- + BLACKLIST.add(Material.CHEST); + BLACKLIST.add(Material.TRAPPED_CHEST); + BLACKLIST.add(Material.ENDER_CHEST); + BLACKLIST.add(Material.BARREL); + BLACKLIST.add(Material.FURNACE); + BLACKLIST.add(Material.BLAST_FURNACE); + BLACKLIST.add(Material.SMOKER); + BLACKLIST.add(Material.BREWING_STAND); + BLACKLIST.add(Material.LECTERN); + BLACKLIST.add(Material.COMPOSTER); + BLACKLIST.add(Material.BEEHIVE); + BLACKLIST.add(Material.BEE_NEST); + + // --- 5. ТЕХНИЧЕСКИЕ И ПРОЗРАЧНЫЕ --- + BLACKLIST.add(Material.BARRIER); + BLACKLIST.add(Material.STRUCTURE_VOID); + BLACKLIST.add(Material.STRUCTURE_BLOCK); + BLACKLIST.add(Material.JIGSAW); + BLACKLIST.add(Material.LIGHT); + BLACKLIST.add(Material.SPAWNER); + BLACKLIST.add(Material.COBWEB); + BLACKLIST.add(Material.SLIME_BLOCK); // Спорно, но липкий + BLACKLIST.add(Material.HONEY_BLOCK); + + // --- 6. ЗАЧИСТКА ПО ИМЕНАМ (На всякий случай) --- + // Добавляем все, что мы могли забыть, но что имеет общие корни + for (Material m : Material.values()) { + String name = m.name(); + if (name.contains("POTTED") || // Горшки с цветами + name.contains("WALL_") || // Настенные варианты (факелы, знаки) + name.contains("HEAD") || // Головы + name.contains("SKULL") || // Черепа + name.contains("BANNER") || // Флаги (дублируем для надежности) + name.contains("CORAL") // Кораллы (дохнут без воды) + ) { + BLACKLIST.add(m); + } + } + } + + // Твой метод проверки стал очень простым + private static boolean isSupported(Material type) { + // 1. Воздух + if (type.isAir()) return false; + + // 2. Черный список (самая надежная проверка) + if (BLACKLIST.contains(type)) { return false; } - if (data instanceof Powerable - || data instanceof Openable - || data instanceof AnaloguePowerable) { - return false; - } - - return switch (type) { - case CACTUS, FARMLAND, - TNT, DISPENSER, DROPPER, HOPPER, - PISTON, STICKY_PISTON, OBSERVER, - COMMAND_BLOCK, REPEATING_COMMAND_BLOCK, - CHAIN_COMMAND_BLOCK, - SCULK_SENSOR, SCULK_SHRIEKER, - DAYLIGHT_DETECTOR, JUKEBOX, - TURTLE_EGG, DRAGON_EGG, - BAMBOO, BAMBOO_SAPLING, - CAKE, LECTERN, COMPOSTER, - CAMPFIRE, SOUL_CAMPFIRE, - BEEHIVE, BEE_NEST - -> false; - default -> true; - }; + // 3. Дополнительная защита: блок должен быть твердым + // (Это отсечет то, что мы могли случайно забыть в списке, типа нитки/String) + return type.isSolid(); } public static void getSortedBlockStats(Location center, int chunkRadius, Consumer>> callback) { @@ -81,11 +222,10 @@ public class BlocksGenerator { for (int z = 0; z < 16; z++) { for (int y = minH; y < maxH; y++) { - BlockData data = snap.getBlockData(x, y, z); Material type = data.getMaterial(); - if (isSupported(type, data)) { + if (isSupported(type)) { counts.put(type, counts.getOrDefault(type, 0L) + 1); } } @@ -93,12 +233,9 @@ public class BlocksGenerator { } } - List> sortedList = new ArrayList<>(counts.entrySet()); sortedList.sort(Map.Entry.comparingByValue()); - - new BukkitRunnable() { @Override public void run() { @@ -109,5 +246,21 @@ public class BlocksGenerator { }.runTaskAsynchronously(BlockAndSeek.getInstance()); } + public static void addCache(String key, List propBlocks) { + cached.put(key, propBlocks); + } + + @Nullable + public static List get(String key) { + return cached.get(key); + } + + private static final ThreadLocalRandom random = ThreadLocalRandom.current(); + + @Nullable + public static PropBlock getRandom(String key) { + List propBlocks = get(key); + return propBlocks == null ? null : propBlocks.get(random.nextInt(0, propBlocks.size())); + } } diff --git a/src/main/java/hdvtdev/blockandseek/CommandBuilder.java b/src/main/java/hdvtdev/blockandseek/CommandBuilder.java index faddced..47eb8ba 100644 --- a/src/main/java/hdvtdev/blockandseek/CommandBuilder.java +++ b/src/main/java/hdvtdev/blockandseek/CommandBuilder.java @@ -1,4 +1,4 @@ -package hdvtdev.blockAndSeek; +package hdvtdev.blockandseek; import org.jetbrains.annotations.Nullable; diff --git a/src/main/java/hdvtdev/blockandseek/CommandListener.java b/src/main/java/hdvtdev/blockandseek/CommandListener.java deleted file mode 100644 index b705253..0000000 --- a/src/main/java/hdvtdev/blockandseek/CommandListener.java +++ /dev/null @@ -1,21 +0,0 @@ -package hdvtdev.blockAndSeek; - -import org.bukkit.command.Command; -import org.bukkit.command.CommandSender; -import org.bukkit.command.TabExecutor; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.List; - -public class CommandListener implements TabExecutor { - @Override - public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { - return false; - } - - @Override - public @Nullable List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { - return List.of(); - } -} diff --git a/src/main/java/hdvtdev/blockandseek/Config.java b/src/main/java/hdvtdev/blockandseek/Config.java index c2ad2f6..95f6790 100644 --- a/src/main/java/hdvtdev/blockandseek/Config.java +++ b/src/main/java/hdvtdev/blockandseek/Config.java @@ -1,4 +1,4 @@ -package hdvtdev.blockAndSeek; +package hdvtdev.blockandseek; import eu.okaeri.configs.ConfigManager; import eu.okaeri.configs.OkaeriConfig; @@ -8,22 +8,43 @@ import eu.okaeri.configs.serdes.commons.SerdesCommons; import eu.okaeri.configs.yaml.bukkit.YamlBukkitConfigurer; import eu.okaeri.configs.yaml.bukkit.serdes.SerdesBukkit; - import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.WorldCreator; import java.io.File; import java.util.Objects; -import java.util.concurrent.atomic.AtomicReference; public class Config extends OkaeriConfig { @Exclude - private static final AtomicReference config = new AtomicReference<>(new Config()); + private static Config config; + + static { + try { + File configFile = new File(BlockAndSeek.getPluginDataFolder(), "config.yml"); + if (!configFile.exists()) configFile.createNewFile(); + config = ConfigManager.create(Config.class, (it) -> { + it.withConfigurer( + new YamlBukkitConfigurer(), + new SerdesBukkit(), + new SerdesCommons() + ); + it.withBindFile(configFile); + it.withLogger(BlockAndSeek.getPluginLogger()); + it.saveDefaults(); + }); + + config.load(); + config.save(); + } catch (Exception e) { + BlockAndSeek.getPluginLogger().severe("Failed to load config.yml: " + e.getMessage()); + e.printStackTrace(); + } + } @Comment("Server options.") - private Server server = new Server(false); + private ServerSettings serverSettings = new ServerSettings(); @Comment("Spawn location. Useless if the Server.forceControl is false.") private Location spawn = Objects.requireNonNull(Bukkit.createWorld(new WorldCreator("world"))).getSpawnLocation(); @Comment("Show hidden BlockAndSeek commands.") @@ -31,50 +52,23 @@ public class Config extends OkaeriConfig { public static boolean debugEnabled() { - return config.get().enableDebugCommands; + return config.enableDebugCommands; } public static boolean forceControl() { - return config.get().server.forceControl; + return config.serverSettings.forceControl; } public static Location spawn() { - return config.get().spawn; + return config.spawn; } - - public static boolean loadConfig() { - try { - Config conf = ConfigManager.create(Config.class, (it) -> { - it.withConfigurer( - new YamlBukkitConfigurer(), - new SerdesBukkit(), - new SerdesCommons() - ); - it.withBindFile(new File(BlockAndSeek.getPluginDataFolder(), "config.yml")); - it.withLogger(BlockAndSeek.getPluginLogger()); - it.saveDefaults(); - }); - - config.setRelease(conf); - } catch (Exception e) { - BlockAndSeek.getPluginLogger().severe("Failed to load config.yml: " + e.getMessage()); - return false; - } - return true; + public static String toStaticString() { + return String.format("BlockAndSeekConfig[forceControl = %s, spawn = %s, enableDebugCommands = %s]", forceControl(), spawn(), debugEnabled()); } - private static class Server { - + private static class ServerSettings extends OkaeriConfig { private boolean forceControl = false; - - public Server(boolean forceControl) { - this.forceControl = forceControl; - } - - public boolean isForceControl() { - return forceControl; - } } } diff --git a/src/main/java/hdvtdev/blockandseek/GuiHolder.java b/src/main/java/hdvtdev/blockandseek/GuiHolder.java index 0ed8a59..a8d3ca8 100644 --- a/src/main/java/hdvtdev/blockandseek/GuiHolder.java +++ b/src/main/java/hdvtdev/blockandseek/GuiHolder.java @@ -1,4 +1,4 @@ -package hdvtdev.blockAndSeek; +package hdvtdev.blockandseek; import org.bukkit.entity.Player; import org.bukkit.event.inventory.InventoryClickEvent; diff --git a/src/main/java/hdvtdev/blockandseek/Keys.java b/src/main/java/hdvtdev/blockandseek/Keys.java index 32748a7..ad60870 100644 --- a/src/main/java/hdvtdev/blockandseek/Keys.java +++ b/src/main/java/hdvtdev/blockandseek/Keys.java @@ -1,4 +1,4 @@ -package hdvtdev.blockAndSeek; +package hdvtdev.blockandseek; import org.bukkit.Bukkit; import org.bukkit.NamespacedKey; @@ -8,21 +8,11 @@ import org.bukkit.scoreboard.Team; public class Keys { public static final NamespacedKey SOUND_ITEM = new NamespacedKey(BlockAndSeek.getInstance(), "BlockAndSeekSoundItem"); + public static final NamespacedKey DASH_ITEM = new NamespacedKey(BlockAndSeek.getInstance(), "BlockAndSeekDashItem"); public static final NamespacedKey FREEZE_ITEM = new NamespacedKey(BlockAndSeek.getInstance(), "BlockAndSeekFreezeItem"); public static final NamespacedKey FACE_CHANGING_ITEM = new NamespacedKey(BlockAndSeek.getInstance(), "BlockAndSeekFaceChangingItem"); public static final NamespacedKey MENU_ITEM = new NamespacedKey(BlockAndSeek.getInstance(), "BlockAndSeekMenuItem"); public static final NamespacedKey LEAVE_ITEM = new NamespacedKey(BlockAndSeek.getInstance(), "BlockAndSeekLeaveItem"); - public static final NamespacedKey GAME_PAGE = new NamespacedKey(BlockAndSeek.getInstance(), "BlockAndSeekGamePage"); - - public static final NamespacedKey FROZEN_PLAYER = new NamespacedKey(BlockAndSeek.getInstance(), "BlockAndSeekFrozenPlayer"); - public static final NamespacedKey GAME = new NamespacedKey(BlockAndSeek.getInstance(), "BlockAndSeekGame"); - - public static final NamespacedKey HIDER = new NamespacedKey(BlockAndSeek.getInstance(), "BlockAndSeekHider"); - public static final NamespacedKey SEEKER = new NamespacedKey(BlockAndSeek.getInstance(), "BlockAndSeekSeeker"); - - public static final NamespacedKey LANG_KEY = new NamespacedKey(BlockAndSeek.getInstance(), "BlockAndSeekLangKey"); - - public static final Team NO_COLLIDE_TEAM; @@ -32,7 +22,5 @@ public class Keys { 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/Localization.java b/src/main/java/hdvtdev/blockandseek/Localization.java deleted file mode 100644 index b4a9b84..0000000 --- a/src/main/java/hdvtdev/blockandseek/Localization.java +++ /dev/null @@ -1,83 +0,0 @@ -package hdvtdev.blockAndSeek; - - - -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.minimessage.MiniMessage; - -import org.bukkit.entity.Player; -import org.bukkit.inventory.ItemStack; -import org.bukkit.inventory.meta.ItemMeta; - -import java.io.File; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.atomic.AtomicReference; - -public final class Localization { - - private Localization() {} - - private static final String defaultLanguage = "en_US"; - private static final MiniMessage mm = MiniMessage.miniMessage(); - - private static final AtomicReference>> translations = new AtomicReference<>(); - - public static Component get(Player player, String key, String... placeholders) { - return get(player.locale().toString(), key, placeholders); - } - - public static Component get(String lang, String key, String... placeholders) { - String raw = translations.get().get(lang).getOrDefault(key, "?" + key + "?"); - if (placeholders.length % 2 == 0) { - for (int i = 0; i < placeholders.length; i++) { - raw = raw.replace(placeholders[i], placeholders[++i]); - } - } else BlockAndSeek.getPluginLogger().warning("Wrong amount of placeholders for key: " + key); - - return mm.deserialize(raw); - } - - public static ItemStack translateItem(Player player, ItemStack itemStack, String key) { - ItemMeta itemMeta = itemStack.getItemMeta(); - itemMeta.displayName(get(player, key)); - itemStack.setItemMeta(itemMeta); - return itemStack; - } - - public static boolean loadTranslations() { - /* - Path path = BlockAndSeek.getPluginDataFolder().toPath().resolve("translations"); - if (Files.notExists(path)) { - try { - Files.createDirectories(path); - } catch (IOException e) { - BlockAndSeek.getPluginLogger().severe("Failed to create \"translations\" dir: " + e); - return false; - } - } - - File[] files = path.toFile().listFiles(); - if (files != null) { - for (File file : files) { - String lang = file.getName().split(".toml")[0]; - Map translation = new HashMap<>(); - for (Map.Entry entry : new Toml().read(file).entrySet()) { - translation.put(entry.getKey(), entry.getValue().toString()); - } - translations.get().put(lang, translation); - } - } else { - BlockAndSeek.getPluginLogger().severe("Failed to load translations! " + path + " returned null."); - } - - - */ - - return true; - } - -} diff --git a/src/main/java/hdvtdev/blockandseek/Utils.java b/src/main/java/hdvtdev/blockandseek/Utils.java index 88996a1..0073fad 100644 --- a/src/main/java/hdvtdev/blockandseek/Utils.java +++ b/src/main/java/hdvtdev/blockandseek/Utils.java @@ -1,20 +1,12 @@ -package hdvtdev.blockAndSeek; +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 me.libraryaddict.disguise.disguisetypes.watchers.FallingBlockWatcher; -import org.bukkit.Bukkit; -import org.bukkit.block.data.BlockData; +import org.bukkit.GameMode; +import org.bukkit.attribute.Attribute; import org.bukkit.entity.Player; -import org.bukkit.inventory.ItemStack; import org.bukkit.persistence.PersistentDataContainer; -import org.jetbrains.annotations.Nullable; - -import java.io.File; +import org.bukkit.potion.PotionEffect; public final class Utils { @@ -26,26 +18,38 @@ public final class Utils { p.setExp(0.9998f * ((float) level / 100)); } - public static boolean isInOneTeam(Player p1, Player p2) { - var c1 = p1.getPersistentDataContainer(); - var c2 = p2.getPersistentDataContainer(); - return (c1.has(Keys.HIDER) && c2.has(Keys.HIDER)) || (c1.has(Keys.SEEKER) && c2.has(Keys.SEEKER)); - } + public static void clearPlayer(Player player) { - public static boolean playerInGame(Player player) { - return player.getPersistentDataContainer().has(Keys.GAME); - } + DisguiseAPI.undisguiseToAll(player); - public static boolean hasPermsToDamage(Player p1, Player p2) { - PersistentDataContainer c1 = p1.getPersistentDataContainer(); - PersistentDataContainer c2 = p2.getPersistentDataContainer(); - return (c1.has(Keys.SEEKER) || c1.has(Keys.HIDER)) && (c2.has(Keys.SEEKER) || c2.has(Keys.HIDER)); - } + player.getInventory().clear(); + player.getInventory().setArmorContents(null); + player.getInventory().setExtraContents(null); + + + player.setGameMode(GameMode.SURVIVAL); + + if (player.getAttribute(Attribute.GENERIC_MAX_HEALTH) != null) { + player.getAttribute(Attribute.GENERIC_MAX_HEALTH).setBaseValue(20.0); + } + player.setHealth(20.0); + + player.setFoodLevel(20); + player.setSaturation(5.0f); + player.setExhaustion(0f); + player.setInvulnerable(false); + player.setLevel(0); + player.setExp(0f); + player.setFireTicks(0); + player.setFallDistance(0f); + player.setWalkSpeed(0.2f); + player.setFlySpeed(0.1f); + player.setAllowFlight(false); + player.setFlying(false); + player.setGliding(false); + player.setGlowing(false); + player.closeInventory(); - public static void clearPlayer(Player p) { - PersistentDataContainer container = p.getPersistentDataContainer(); - container.remove(Keys.SEEKER); - container.remove(Keys.HIDER); } diff --git a/src/main/java/hdvtdev/blockandseek/eventListeners/EventListener.java b/src/main/java/hdvtdev/blockandseek/eventListeners/EventListener.java index 3040b8c..04bab96 100644 --- a/src/main/java/hdvtdev/blockandseek/eventListeners/EventListener.java +++ b/src/main/java/hdvtdev/blockandseek/eventListeners/EventListener.java @@ -1,7 +1,7 @@ -package hdvtdev.blockAndSeek.eventListeners; +package hdvtdev.blockandseek.eventListeners; -import hdvtdev.blockAndSeek.Keys; -import hdvtdev.blockAndSeek.Utils; +import hdvtdev.blockandseek.Keys; +import hdvtdev.blockandseek.Utils; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; @@ -14,25 +14,22 @@ public class EventListener implements Listener { @EventHandler public void onDrop(PlayerDropItemEvent event) { - if (event.getPlayer().getPersistentDataContainer().has(Keys.GAME)) event.setCancelled(true); + } @EventHandler public void onFoodLevelChange(FoodLevelChangeEvent event) { - if (event.getEntity() instanceof Player player && Utils.playerInGame(player)) { - event.setCancelled(true); - event.getEntity().setFoodLevel(20); - } + } @EventHandler public void onBlockPlacement(BlockPlaceEvent event) { - if (event.getPlayer().getPersistentDataContainer().has(Keys.GAME)) event.setCancelled(true); + } @EventHandler public void onBlockBreak(BlockBreakEvent event) { - if (event.getPlayer().getPersistentDataContainer().has(Keys.GAME)) event.setCancelled(true); + } diff --git a/src/main/java/hdvtdev/blockandseek/eventListeners/ForceControlEventListener.java b/src/main/java/hdvtdev/blockandseek/eventListeners/ForceControlEventListener.java index ef550df..0eb6c8f 100644 --- a/src/main/java/hdvtdev/blockandseek/eventListeners/ForceControlEventListener.java +++ b/src/main/java/hdvtdev/blockandseek/eventListeners/ForceControlEventListener.java @@ -1,9 +1,11 @@ -package hdvtdev.blockAndSeek.eventListeners; +package hdvtdev.blockandseek.eventListeners; -import hdvtdev.blockAndSeek.Utils; -import hdvtdev.blockAndSeek.managers.ItemManager; +import hdvtdev.blockandseek.Config; +import hdvtdev.blockandseek.Utils; +import hdvtdev.blockandseek.managers.ItemManager; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; import org.bukkit.event.block.BlockBreakEvent; import org.bukkit.event.block.BlockPlaceEvent; @@ -21,6 +23,9 @@ public class ForceControlEventListener implements Listener { @EventHandler public void onPlayerJoin(PlayerJoinEvent event) { Player player = event.getPlayer(); + player.teleport(Config.spawn()); + Utils.clearPlayer(player); + player.setInvulnerable(true); ItemManager.defaultInventory(player); } @@ -34,17 +39,17 @@ public class ForceControlEventListener implements Listener { if (event.getSkipReason() != TimeSkipEvent.SkipReason.COMMAND) event.setCancelled(true); } - @EventHandler + @EventHandler(ignoreCancelled = true) public void onBlockPlacement(BlockPlaceEvent event) { event.setCancelled(true); } - @EventHandler + @EventHandler(ignoreCancelled = true) public void onBlockBreak(BlockBreakEvent event) { event.setCancelled(true); } - @EventHandler + @EventHandler(ignoreCancelled = true) public void onFoodLevelChange(FoodLevelChangeEvent event) { if (event.getEntity() instanceof Player player) { event.setCancelled(true); @@ -52,22 +57,12 @@ public class ForceControlEventListener implements Listener { } } - @EventHandler + @EventHandler(ignoreCancelled = true) public void onDrop(PlayerDropItemEvent event) { event.setCancelled(true); } - @EventHandler - public void onPlayerDamage(EntityDamageByEntityEvent event) { - - if (event.getDamager() instanceof Player damager && event.getEntity() instanceof Player victim) { - if (!Utils.hasPermsToDamage(damager, victim)) { - event.setCancelled(true); - } - } - } - - @EventHandler + @EventHandler(ignoreCancelled = true) public void onFallDamage(EntityDamageEvent e) { if (e.getEntity() instanceof Player) { if (e.getCause() == EntityDamageEvent.DamageCause.FALL) { diff --git a/src/main/java/hdvtdev/blockandseek/eventListeners/RequiredEventListener.java b/src/main/java/hdvtdev/blockandseek/eventListeners/RequiredEventListener.java index 7c36a9b..c8218f7 100644 --- a/src/main/java/hdvtdev/blockandseek/eventListeners/RequiredEventListener.java +++ b/src/main/java/hdvtdev/blockandseek/eventListeners/RequiredEventListener.java @@ -1,8 +1,15 @@ -package hdvtdev.blockAndSeek.eventListeners; +package hdvtdev.blockandseek.eventListeners; -import hdvtdev.blockAndSeek.Keys; -import hdvtdev.blockAndSeek.Utils; -import hdvtdev.blockAndSeek.GuiHolder; +import hdvtdev.blockandseek.BlockAndSeek; +import hdvtdev.blockandseek.Keys; +import hdvtdev.blockandseek.Utils; +import hdvtdev.blockandseek.GuiHolder; +import hdvtdev.blockandseek.managers.PropManager; + +import hdvtdev.blockandseek.menus.GamesMenu; +import net.kyori.adventure.text.Component; + +import org.bukkit.Material; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; @@ -16,10 +23,17 @@ import org.bukkit.event.inventory.InventoryCloseEvent; import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.event.server.ServerLoadEvent; import org.bukkit.inventory.EquipmentSlot; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.persistence.PersistentDataContainer; +import org.bukkit.scheduler.BukkitRunnable; +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; -import static hdvtdev.blockAndSeek.Utils.isInOneTeam; public class RequiredEventListener implements Listener { @@ -34,9 +48,36 @@ public class RequiredEventListener implements Listener { } + //todo remove + private final PropManager propManager = new PropManager(); + private final Set coolDown = new HashSet<>(); + @EventHandler public void onRightClick(PlayerInteractEvent event) { if (event.getHand() != EquipmentSlot.HAND) return; + if (event.getAction().isLeftClick()) return; + + Player player = event.getPlayer(); + UUID uuid = player.getUniqueId(); + + ItemStack item = event.getItem(); + if (item != null) { + ItemMeta meta = item.getItemMeta(); + if (meta != null) { + PersistentDataContainer dataContainer = meta.getPersistentDataContainer(); + if (dataContainer.has(Keys.MENU_ITEM)) { + new GamesMenu(player); + event.setCancelled(true); + } + + } + + } + + + + + } @@ -78,21 +119,7 @@ public class RequiredEventListener implements Listener { @EventHandler public void onPlayerDamage(EntityDamageByEntityEvent event) { - if (event.getDamager() instanceof Player damager && event.getEntity() instanceof Player victim) { - if (isInOneTeam(damager, victim)) { - event.setCancelled(true); - } else if (victim.getPersistentDataContainer().has(Keys.SEEKER)) { - event.setDamage(0); - } else if (damager.getPersistentDataContainer().has(Keys.SEEKER)) { - double maxHealth = 20.0; - double currentHealth = damager.getHealth(); - if (currentHealth < maxHealth) { - double newHealth = Math.min(currentHealth + event.getDamage(), maxHealth); - damager.setHealth(newHealth); - Utils.setLevelWithBar(damager, (int) Math.round(damager.getHealth() * 5)); - } - } - } + } } diff --git a/src/main/java/hdvtdev/blockandseek/managers/GamesManager.java b/src/main/java/hdvtdev/blockandseek/managers/GamesManager.java index fa700e8..301597d 100644 --- a/src/main/java/hdvtdev/blockandseek/managers/GamesManager.java +++ b/src/main/java/hdvtdev/blockandseek/managers/GamesManager.java @@ -1,35 +1,40 @@ -package hdvtdev.blockAndSeek.managers; +package hdvtdev.blockandseek.managers; -import hdvtdev.blockAndSeek.objects.BlockAndSeekGame; -import org.bukkit.Bukkit; -import org.bukkit.WorldCreator; +import hdvtdev.blockandseek.objects.BlockAndSeekGame; + +import hdvtdev.blockandseek.objects.BlockAndSeekMap; import org.bukkit.entity.Player; + import org.jetbrains.annotations.Nullable; -import java.io.File; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; +import java.util.*; + public class GamesManager { - private static final ConcurrentHashMap games = new ConcurrentHashMap<>(); - private static final Set seekerImmune = ConcurrentHashMap.newKeySet(); + private static final Map games = new HashMap<>(); + private static final Set seekerImmune = new HashSet<>(); - public static boolean triggerSeekerImmune(Player player) { - return seekerImmune.remove(player); + public static boolean triggerSeekerImmune(UUID uuid) { + return seekerImmune.remove(uuid); } public static void addSeekerImmune(Player player) { - seekerImmune.add(player); + seekerImmune.add(player.getUniqueId()); } public static Set getAvailableGames() { return games.keySet(); } - public static @Nullable String createGame(String name) { - if (games.containsKey(name)) return name; //TODO use copy or create copy + public static @Nullable BlockAndSeekGame createGame(String name) { + BlockAndSeekMap map = MapsManager.getMap(name); + if (map != null) { + BlockAndSeekGame game = new BlockAndSeekGame(name, map); + games.put(name, game); + return game; + } return null; } diff --git a/src/main/java/hdvtdev/blockandseek/managers/ItemManager.java b/src/main/java/hdvtdev/blockandseek/managers/ItemManager.java index 734177f..9b34aba 100644 --- a/src/main/java/hdvtdev/blockandseek/managers/ItemManager.java +++ b/src/main/java/hdvtdev/blockandseek/managers/ItemManager.java @@ -1,7 +1,8 @@ -package hdvtdev.blockAndSeek.managers; +package hdvtdev.blockandseek.managers; -import hdvtdev.blockAndSeek.Keys; -import hdvtdev.blockAndSeek.Localization; +import hdvtdev.blockandseek.Keys; +import hdvtdev.blockandseek.objects.TranslationKey; +import lombok.Getter; import net.kyori.adventure.text.Component; import org.bukkit.Color; import org.bukkit.Material; @@ -14,10 +15,19 @@ import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.inventory.meta.LeatherArmorMeta; import org.bukkit.persistence.PersistentDataType; +import static hdvtdev.blockandseek.objects.TranslationKey.*; + public class ItemManager { + @Getter private static final ItemStack freezeItem = new ItemStack(Material.HEART_OF_THE_SEA); + @Getter private static final ItemStack faceChangingItem = new ItemStack(Material.NETHER_STAR); + @Getter + private static final ItemStack dashItem = new ItemStack(Material.LAPIS_LAZULI); + @Getter + private static final ItemStack soundItem = new ItemStack(Material.BROWN_DYE); + private static final ItemStack seekerSword = new ItemStack(Material.WOODEN_SWORD); private static final ItemStack seekerHelmet = new ItemStack(Material.LEATHER_HELMET); @@ -25,9 +35,13 @@ public class ItemManager { private static final ItemStack seekerLeggings = new ItemStack(Material.LEATHER_LEGGINGS); private static final ItemStack seekerBoots = new ItemStack(Material.LEATHER_BOOTS); + @Getter private static final ItemStack menuItem = new ItemStack(Material.COMPASS); + @Getter private static final ItemStack games = new ItemStack(Material.BOOKSHELF); + @Getter private static final ItemStack createGameButton = new ItemStack(Material.SLIME_BALL); + @Getter private static final ItemStack leaveItem = new ItemStack(Material.RED_DYE); @@ -53,8 +67,19 @@ public class ItemManager { menuMeta.addItemFlags(ItemFlag.HIDE_ENCHANTS); menuItem.setItemMeta(menuMeta); + ItemMeta dashMeta = dashItem.getItemMeta(); + dashMeta.getPersistentDataContainer().set(Keys.DASH_ITEM, PersistentDataType.BOOLEAN, true); + dashMeta.addEnchant(Enchantment.ARROW_INFINITE, 1, true); + dashMeta.addItemFlags(ItemFlag.HIDE_ENCHANTS); + dashItem.setItemMeta(dashMeta); + + ItemMeta soundMeta = soundItem.getItemMeta(); + soundMeta.getPersistentDataContainer().set(Keys.SOUND_ITEM, PersistentDataType.BOOLEAN, true); + soundMeta.addEnchant(Enchantment.ARROW_INFINITE, 1, true); + soundMeta.addItemFlags(ItemFlag.HIDE_ENCHANTS); + soundItem.setItemMeta(soundMeta); + ItemMeta gamesMeta = games.getItemMeta(); - gamesMeta.getPersistentDataContainer().set(Keys.GAME_PAGE, PersistentDataType.BOOLEAN, true); games.setItemMeta(gamesMeta); ItemMeta leaveMeta = leaveItem.getItemMeta(); @@ -75,12 +100,12 @@ public class ItemManager { public static void setSeekerSet(Player seeker) { PlayerInventory inventory = seeker.getInventory(); inventory.clear(); - inventory.addItem(Localization.translateItem(seeker, seekerSword, "seeker_sword")); + inventory.addItem(TranslationManager.translateItem(seeker, seekerSword, TranslationKey.SEEKER_TEMPLATE)); ItemStack[] armor = new ItemStack[]{ - Localization.translateItem(seeker, seekerBoots, "seeker_boots"), - Localization.translateItem(seeker, seekerLeggings, "seeker_leggings"), - Localization.translateItem(seeker, seekerChestplate, "seeker_chestplate"), - Localization.translateItem(seeker, seekerHelmet, "seeker_helmet") + TranslationManager.translateItem(seeker, seekerBoots, TranslationKey.SEEKER_TEMPLATE), + TranslationManager.translateItem(seeker, seekerLeggings, TranslationKey.SEEKER_TEMPLATE), + TranslationManager.translateItem(seeker, seekerChestplate, TranslationKey.SEEKER_TEMPLATE), + TranslationManager.translateItem(seeker, seekerHelmet, TranslationKey.SEEKER_TEMPLATE) }; inventory.setArmorContents(armor); @@ -94,7 +119,6 @@ public class ItemManager { meta.setUnbreakable(true); meta.addEnchant(Enchantment.PROTECTION_ENVIRONMENTAL, 10, true); meta.addEnchant(Enchantment.THORNS, 3, true); - meta.displayName(Component.text("seeker-armor")); return meta; } @@ -102,32 +126,25 @@ public class ItemManager { public static void defaultInventory(Player player) { PlayerInventory inventory = player.getInventory(); inventory.clear(); - inventory.addItem(Localization.translateItem(player, menuItem, "menu_item")); + inventory.addItem(TranslationManager.translateItem(player, menuItem, TranslationKey.MENU)); + } + + public static void hiderInventory(Player player) { + PlayerInventory playerInventory = player.getInventory(); + playerInventory.clear(); + playerInventory.addItem( + TranslationManager.translateItem(player, freezeItem, FREEZE_ITEM), + TranslationManager.translateItem(player, faceChangingItem, FACE_CHANGING_ITEM), + TranslationManager.translateItem(player, soundItem, SOUND_ITEM), + TranslationManager.translateItem(player, dashItem, DASH_ITEM) + ); } public static void giveFaceChangingItem(Player player) { - player.getInventory().addItem(Localization.translateItem(player, faceChangingItem, "face_changing_item")); + player.getInventory().addItem(TranslationManager.translateItem(player, faceChangingItem, TranslationKey.SEEKER_TEMPLATE)); } - public static ItemStack getLeaveItem() { - return leaveItem; - } - public static ItemStack getCreateGameButton() { - return createGameButton; - } - - public static ItemStack getGamesPageItem() { - return games; - } - - public static ItemStack getMenuItem() { - return menuItem; - } - - 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 fb3bc4f..8b2dd98 100644 --- a/src/main/java/hdvtdev/blockandseek/managers/MapsManager.java +++ b/src/main/java/hdvtdev/blockandseek/managers/MapsManager.java @@ -1,54 +1,135 @@ -package hdvtdev.blockAndSeek.managers; +package hdvtdev.blockandseek.managers; +import eu.okaeri.configs.ConfigManager; +import eu.okaeri.configs.serdes.commons.SerdesCommons; +import eu.okaeri.configs.yaml.bukkit.YamlBukkitConfigurer; +import eu.okaeri.configs.yaml.bukkit.serdes.SerdesBukkit; +import hdvtdev.blockandseek.BlockAndSeek; +import hdvtdev.blockandseek.objects.BlockAndSeekMap; import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.WorldCreator; import org.bukkit.command.CommandSender; - import org.incendo.cloud.suggestion.Suggestion; import org.incendo.cloud.suggestion.SuggestionProvider; import java.io.File; -import java.util.ArrayList; -import java.util.List; +import java.util.*; import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; public final class MapsManager { - private MapsManager() { + private static final Map maps = new HashMap<>(); + private static final File MAPS_DIR = new File(BlockAndSeek.getPluginDataFolder(), "maps"); + + private MapsManager() {} + + public static void loadMaps() { + if (!MAPS_DIR.exists()) { + MAPS_DIR.mkdirs(); + } + + File[] files = MAPS_DIR.listFiles((dir, name) -> name.endsWith(".yml")); + if (files == null) return; + + for (File file : files) { + String name = file.getName().replace(".yml", ""); + try { + BlockAndSeekMap map = loadConfig(file); + maps.put(name, map); + } catch (Exception e) { + BlockAndSeek.getPluginLogger().severe("Error loading map " + name + ": " + e.getMessage()); + e.printStackTrace(); + } + } + BlockAndSeek.getPluginLogger().info("Loaded " + maps.size() + " maps."); } + public static BlockAndSeekMap getMap(String name) { + return maps.get(name); + } + + public static Collection getMaps() { + return maps.values(); + } + + public static void createMap(String name) { + File mapFile = new File(MAPS_DIR, name + ".yml"); + if (mapFile.exists()) { + throw new IllegalArgumentException("Map already exists!"); + } + + BlockAndSeekMap map = loadConfig(mapFile); + + map.setWorld(name); + + if (Bukkit.getWorld(name) != null) { + Location spawn = Bukkit.getWorld(name).getSpawnLocation(); + map.setLobby(spawn); + map.setSpawn(spawn); + } + + map.save(); + maps.put(name, map); + } + + private static BlockAndSeekMap loadConfig(File file) { + var map = ConfigManager.create(BlockAndSeekMap.class, (it) -> { + it.withConfigurer( + new YamlBukkitConfigurer(), + new SerdesBukkit(), + new SerdesCommons() + ); + it.withBindFile(file); + it.withLogger(BlockAndSeek.getPluginLogger()); + it.load(true); + }); + + String worldName = map.getWorld(); + if (worldName == null) { + BlockAndSeek.getPluginLogger().warning("Map " + file.getName() + " has no world defined!"); + return map; + } + + World world = Bukkit.getWorld(worldName); + + if (world == null) { + File worldFolder = new File(Bukkit.getWorldContainer(), worldName); + if (worldFolder.exists() && worldFolder.isDirectory()) { + BlockAndSeek.getPluginLogger().info("World '" + worldName + "' is not loaded. Loading now..."); + world = Bukkit.createWorld(new WorldCreator(worldName)); + } + } + + map.getLobby().setWorld(world); + map.getSpawn().setWorld(world); + + map.save(); + + return map; + } + + // --- Suggestions --- + public static SuggestionProvider worldSuggestions = (context, input) -> CompletableFuture.supplyAsync(() -> { - List suggestions = new ArrayList<>(); - + // Лучше брать загруженные миры Bukkit, если карта должна быть в активном мире + // Если нужны папки миров: File container = Bukkit.getWorldContainer(); + File[] files = container.listFiles((dir, name) -> new File(dir, name + "/level.dat").exists()); - File[] files = container.listFiles(); - if (files != null) { - for (File file : files) { - if (file.isDirectory() && new File(file, "level.dat").exists()) { - suggestions.add(Suggestion.suggestion(file.getName())); - } - } - } - return suggestions; + if (files == null) return Collections.emptyList(); + + return Arrays.stream(files) + .map(File::getName) + .map(Suggestion::suggestion) + .collect(Collectors.toList()); }); public static SuggestionProvider mapSuggestions = (context, input) -> - CompletableFuture.supplyAsync(() -> { - List suggestions = new ArrayList<>(); - - File container = new File(Bukkit.getPluginsFolder(), "maps"); - - File[] files = container.listFiles(); - if (files != null) { - for (File file : files) { - String name = file.getName(); - if (file.isFile() && name.endsWith(".toml")) { - suggestions.add(Suggestion.suggestion(name.replace(".toml", ""))); //baddd - } - } - } - - return suggestions; - }); -} + CompletableFuture.supplyAsync(() -> maps.keySet().stream() + .map(Suggestion::suggestion) + .collect(Collectors.toList())); +} \ No newline at end of file diff --git a/src/main/java/hdvtdev/blockandseek/managers/PropManager.java b/src/main/java/hdvtdev/blockandseek/managers/PropManager.java index dd3ac35..22fdc05 100644 --- a/src/main/java/hdvtdev/blockandseek/managers/PropManager.java +++ b/src/main/java/hdvtdev/blockandseek/managers/PropManager.java @@ -1,20 +1,23 @@ -package hdvtdev.blockAndSeek.managers; +package hdvtdev.blockandseek.managers; -import hdvtdev.blockAndSeek.BlockAndSeek; +import hdvtdev.blockandseek.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 me.libraryaddict.disguise.disguisetypes.watchers.FallingBlockWatcher; import org.bukkit.Bukkit; +import org.bukkit.GameMode; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.block.Block; import org.bukkit.block.data.BlockData; +import org.bukkit.block.data.Openable; import org.bukkit.entity.ArmorStand; import org.bukkit.entity.EntityType; -import org.bukkit.entity.FallingBlock; import org.bukkit.entity.Player; +import org.bukkit.event.entity.CreatureSpawnEvent; +import org.bukkit.scheduler.BukkitRunnable; import org.jetbrains.annotations.Nullable; import java.util.HashMap; @@ -24,30 +27,105 @@ import java.util.UUID; public final class PropManager { private final Map players = new HashMap<>(); + private final Map props = new HashMap<>(); + public void removePlayer(Player player) { + + } + + public boolean isProped(UUID uuid) { + return props.containsValue(uuid); + } + + public void forceUnfreeze(Player player) { + if (players.containsKey(player.getUniqueId())) { + freezeOrUnfreeze(player); + } + } + + public void forceUnfreeze(UUID uuid) { + if (players.containsKey(uuid)) { + freezeOrUnfreeze(Bukkit.getPlayer(uuid)); + } + } + + @Nullable + public UUID getProp(Location location) { + return props.remove(location); + } + + + //fixme: replace body with freezeOrUnfreeze(Player player) body + /* + public PropState freezeOrUnfreeze(UUID uuid) { + Player player = Bukkit.getPlayer(uuid); + if (player != null) { + return freezeOrUnfreeze(player); + } + return PropState.FAILED; + } + + */ + + //i dont know how dis works xd public PropState freezeOrUnfreeze(Player player) { UUID uuid = player.getUniqueId(); PropData propData = players.remove(uuid); - Location location = player.getLocation(); - - Block originalBlock = location.getBlock(); + Location location = player.getLocation().toCenterLocation(); if (propData != null) { + ArmorStand armorStand = propData.armorStand; + location.getWorld().setBlockData(location, propData.blockData); + location.setY(Math.floor(location.getY())); + player.setInvulnerable(false); + armorStand.removePassenger(player); + armorStand.remove(); - originalBlock.setBlockData(propData.blockData); + return PropState.UNFROZEN; } else { - BlockData originalBlockData = originalBlock.getBlockData(); - BlockData disguiseBlockData = getPlayerDisguiseData(player); + Block block = location.getBlock(); + BlockData blockData = block.getBlockData(); + + Location blockLocation = block.getLocation(); + Location centerLocation = blockLocation.toCenterLocation(); + Location upperBlockLocation = centerLocation.clone(); + upperBlockLocation.setY(upperBlockLocation.getY() + 1); + + if (!upperBlockLocation.getBlock().isSolid() && !blockLocation.getBlock().isSolid()) { + props.put(blockLocation, uuid); + + BlockData disguiseBlockData = getPlayerDisguiseData(player); + location.getWorld().setBlockData(blockLocation, disguiseBlockData == null ? Material.TARGET.createBlockData() : disguiseBlockData); + centerLocation.setY(centerLocation.getY() - 0.85); + + player.setInvulnerable(true); + player.setFreezeTicks(40); + + 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.setInvulnerable(true); + }); + + players.put(uuid, new PropData(armorStand, blockData)); + return PropState.FROZEN; + } - players.put(uuid, new PropData(null, originalBlockData, null)); } + + return PropState.FAILED; } @@ -68,7 +146,7 @@ public final class PropManager { FAILED } - private record PropData(ArmorStand armorStand, BlockData blockData, Disguise disguise) { + private record PropData(ArmorStand armorStand, BlockData blockData) { } diff --git a/src/main/java/hdvtdev/blockandseek/managers/StateManager.java b/src/main/java/hdvtdev/blockandseek/managers/StateManager.java index 7334176..ee0917a 100644 --- a/src/main/java/hdvtdev/blockandseek/managers/StateManager.java +++ b/src/main/java/hdvtdev/blockandseek/managers/StateManager.java @@ -1,6 +1,6 @@ -package hdvtdev.blockAndSeek.managers; +package hdvtdev.blockandseek.managers; -import hdvtdev.blockAndSeek.objects.BlockAndSeekGame; +import hdvtdev.blockandseek.objects.BlockAndSeekGame; import java.util.*; @@ -26,6 +26,14 @@ public class StateManager { stateToPlayers.computeIfAbsent(newState, k -> new HashSet<>()).add(uuid); } + public Set getPlayers() { + return playerToState.keySet(); + } + + public boolean hasPlayer(UUID uuid) { + return playerToState.containsKey(uuid); + } + public int playerCount() { return playerToState.size(); } diff --git a/src/main/java/hdvtdev/blockandseek/managers/TranslationManager.java b/src/main/java/hdvtdev/blockandseek/managers/TranslationManager.java new file mode 100644 index 0000000..b71dfc4 --- /dev/null +++ b/src/main/java/hdvtdev/blockandseek/managers/TranslationManager.java @@ -0,0 +1,94 @@ +package hdvtdev.blockandseek.managers; + + + +import eu.okaeri.configs.ConfigManager; +import eu.okaeri.configs.OkaeriConfig; +import eu.okaeri.configs.annotation.Exclude; +import eu.okaeri.configs.serdes.commons.SerdesCommons; +import eu.okaeri.configs.yaml.bukkit.YamlBukkitConfigurer; +import hdvtdev.blockandseek.BlockAndSeek; +import hdvtdev.blockandseek.objects.Translation; +import hdvtdev.blockandseek.objects.TranslationKey; +import lombok.Getter; +import lombok.Setter; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.minimessage.MiniMessage; + +import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; + +import java.io.File; +import java.util.*; +import java.util.concurrent.atomic.AtomicReference; + +public final class TranslationManager { + + private TranslationManager() {} + + public static final String defaultLanguage = "en_US"; + public static final MiniMessage mm = MiniMessage.miniMessage(); + public static final PlainTextComponentSerializer plaintText = PlainTextComponentSerializer.plainText(); + + public static final String prefix = "BlockAndSeek »"; + public static final String bracedPrefix = "[BlockAndSeek]"; + + private static final Map> translations = new HashMap<>(); + + public static Component get(Player player, TranslationKey key, String... placeholders) { + return get(player.locale().toString(), key, placeholders); + } + + public static Component get(String lang, TranslationKey key, String... placeholders) { + String raw = translations.getOrDefault(lang, translations.get(defaultLanguage)).getOrDefault(key, "?" + key.toString() + "?"); + if (placeholders.length % 2 == 0) { + for (int i = 0; i < placeholders.length; i++) { + raw = raw.replace(placeholders[i], placeholders[++i]); + } + } else BlockAndSeek.getPluginLogger().warning("Wrong amount of placeholders for key: " + key); + return mm.deserialize(raw); + } + + public static ItemStack translateItem(Player player, ItemStack itemStack, TranslationKey key, String... placeholders) { + ItemMeta itemMeta = itemStack.getItemMeta(); + itemMeta.displayName(get(player, key, placeholders)); + itemStack.setItemMeta(itemMeta); + return itemStack; + } + + public static void loadLanguages() { + File langDir = new File(BlockAndSeek.getPluginDataFolder(), "languages"); + if (!langDir.exists()) langDir.mkdirs(); + + if (!new File(langDir, "en_US.yml").exists()) BlockAndSeek.saveResource("languages/en_US.yml"); + if (!new File(langDir, "ru_RU.yml").exists()) BlockAndSeek.saveResource("languages/ru_RU.yml"); + BlockAndSeek.getInstance().saveResource("languages/README.txt", true); + + for (File lang : langDir.listFiles()) { + String name = lang.getName(); + if (!name.endsWith(".yml")) continue; + + Translation config = ConfigManager.create(Translation.class, (it) -> { + it.withConfigurer(new YamlBukkitConfigurer(), new SerdesCommons()); + it.withBindFile(new File(langDir, name)); + it.load(true); + }); + + EnumMap enumMap = new EnumMap<>(TranslationKey.class); + enumMap.putAll(config.getMessages()); + + translations.put(name.replace(".yml", ""), enumMap); + + BlockAndSeek.getPluginLogger().info("Loaded " + name + " translation"); + } + } + + + + + + +} diff --git a/src/main/java/hdvtdev/blockandseek/managers/WorldManager.java b/src/main/java/hdvtdev/blockandseek/managers/WorldManager.java index 4b72748..35c3586 100644 --- a/src/main/java/hdvtdev/blockandseek/managers/WorldManager.java +++ b/src/main/java/hdvtdev/blockandseek/managers/WorldManager.java @@ -1,7 +1,7 @@ -package hdvtdev.blockAndSeek.managers; +package hdvtdev.blockandseek.managers; -import hdvtdev.blockAndSeek.BlockAndSeek; +import hdvtdev.blockandseek.BlockAndSeek; import io.papermc.paper.threadedregions.scheduler.AsyncScheduler; diff --git a/src/main/java/hdvtdev/blockandseek/menus/GamesMenu.java b/src/main/java/hdvtdev/blockandseek/menus/GamesMenu.java index a2d08a6..98d4546 100644 --- a/src/main/java/hdvtdev/blockandseek/menus/GamesMenu.java +++ b/src/main/java/hdvtdev/blockandseek/menus/GamesMenu.java @@ -1,31 +1,73 @@ -package hdvtdev.blockAndSeek.menus; +package hdvtdev.blockandseek.menus; -import hdvtdev.blockAndSeek.Localization; -import hdvtdev.blockAndSeek.GuiHolder; +import hdvtdev.blockandseek.managers.TranslationManager; +import hdvtdev.blockandseek.GuiHolder; +import hdvtdev.blockandseek.managers.GamesManager; +import hdvtdev.blockandseek.managers.ItemManager; +import hdvtdev.blockandseek.objects.TranslationKey; import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.minimessage.MiniMessage; import org.bukkit.Bukkit; +import org.bukkit.Material; import org.bukkit.entity.Player; import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; import org.jetbrains.annotations.NotNull; +import java.util.List; + public class GamesMenu implements GuiHolder { private final Inventory inventory; public GamesMenu(Player player) { - Component title = Localization.get(player, "games_menu"); + Component title = TranslationManager.get(player, TranslationKey.GAMES_MENU); this.inventory = Bukkit.createInventory(this, 54, title); initInventory(); + showInventory(player); } private void initInventory() { - + inventory.setItem(53, ItemManager.getCreateGameButton()); + for (String gameName : GamesManager.getAvailableGames()) { + ItemStack gameItem = new ItemStack(Material.AMETHYST_BLOCK); + ItemMeta itemMeta = gameItem.getItemMeta(); + itemMeta.displayName(TranslationManager.get(TranslationManager.defaultLanguage, TranslationKey.GAME, "%name%", gameName)); + var game = GamesManager.get(gameName); + itemMeta.lore(List.of(Component.text(game.players()))); + gameItem.setItemMeta(itemMeta); + inventory.addItem(gameItem); + } } @Override public void onClick(InventoryClickEvent event) { + int slot = event.getSlot(); + Player player = (Player) event.getWhoClicked(); + + event.setCancelled(true); + + if (slot == 53) { + new MapsMenu(player); + } else { + ItemStack item = event.getCurrentItem(); + if (item != null) { + String gameName = TranslationManager.plaintText.serialize(item.displayName()).replaceAll("[\\[\\]]", ""); + var game = GamesManager.get(gameName); + if (game != null) { + if (!game.addPlayer(player)) { + player.sendMessage(TranslationManager.get(player, TranslationKey.GAME_IS_FULL, "%game%", gameName)); + } + } + event.getInventory().close(); + } + } + + + } diff --git a/src/main/java/hdvtdev/blockandseek/menus/MapsMenu.java b/src/main/java/hdvtdev/blockandseek/menus/MapsMenu.java new file mode 100644 index 0000000..4e2cd84 --- /dev/null +++ b/src/main/java/hdvtdev/blockandseek/menus/MapsMenu.java @@ -0,0 +1,66 @@ +package hdvtdev.blockandseek.menus; + +import hdvtdev.blockandseek.GuiHolder; +import hdvtdev.blockandseek.managers.GamesManager; +import hdvtdev.blockandseek.managers.ItemManager; +import hdvtdev.blockandseek.managers.MapsManager; +import hdvtdev.blockandseek.managers.TranslationManager; +import hdvtdev.blockandseek.objects.BlockAndSeekMap; +import hdvtdev.blockandseek.objects.TranslationKey; +import net.kyori.adventure.text.Component; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.jetbrains.annotations.NotNull; + +import java.util.stream.Collectors; + +import static hdvtdev.blockandseek.objects.TranslationKey.UNKNOWN_MAP; + +public class MapsMenu implements GuiHolder { + + private final Inventory inventory; + + public MapsMenu(Player player) { + Component title = TranslationManager.get(player, TranslationKey.GAMES_MENU); + this.inventory = Bukkit.createInventory(this, 54, title); + init(); + showInventory(player); + } + + private void init() { + for (BlockAndSeekMap map : MapsManager.getMaps()) { + ItemStack gameItem = new ItemStack(Material.MAP); + ItemMeta itemMeta = gameItem.getItemMeta(); + itemMeta.displayName(TranslationManager.get(TranslationManager.defaultLanguage, TranslationKey.MAP, "%name%", map.getWorld())); + gameItem.setItemMeta(itemMeta); + inventory.addItem(gameItem); + } + } + + @Override + public void onClick(InventoryClickEvent event) { + ItemStack item = event.getCurrentItem(); + event.setCancelled(true); + if (item != null) { + Player player = (Player) event.getWhoClicked(); + String mapName = TranslationManager.plaintText.serialize(item.displayName()).replaceAll("[\\[\\]]", ""); + var map = MapsManager.getMap(mapName); + if (map != null) { + GamesManager.createGame(mapName).addPlayer(player); + } else { + player.sendMessage(TranslationManager.get(player, UNKNOWN_MAP, "%map%", mapName, "%maps%", MapsManager.getMaps().stream().map(BlockAndSeekMap::getWorld).collect(Collectors.joining(", ")))); + } + player.closeInventory(); + } + } + + @Override + public @NotNull Inventory getInventory() { + return inventory; + } +} diff --git a/src/main/java/hdvtdev/blockandseek/objects/BlockAndSeekGame.java b/src/main/java/hdvtdev/blockandseek/objects/BlockAndSeekGame.java index 7a76057..00bfee5 100644 --- a/src/main/java/hdvtdev/blockandseek/objects/BlockAndSeekGame.java +++ b/src/main/java/hdvtdev/blockandseek/objects/BlockAndSeekGame.java @@ -1,33 +1,121 @@ -package hdvtdev.blockAndSeek.objects; +package hdvtdev.blockandseek.objects; + +import hdvtdev.blockandseek.BlockAndSeek; +import hdvtdev.blockandseek.Config; +import hdvtdev.blockandseek.Keys; +import hdvtdev.blockandseek.managers.TranslationManager; +import hdvtdev.blockandseek.Utils; +import hdvtdev.blockandseek.managers.GamesManager; +import hdvtdev.blockandseek.managers.ItemManager; +import hdvtdev.blockandseek.managers.PropManager; +import hdvtdev.blockandseek.managers.StateManager; + +import hdvtdev.blockandseek.menus.GamesMenu; +import hdvtdev.blockandseek.roulette.RouletteCreator; +import me.libraryaddict.disguise.DisguiseAPI; +import me.libraryaddict.disguise.disguisetypes.DisguiseType; +import me.libraryaddict.disguise.disguisetypes.MiscDisguise; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.title.Title; -import hdvtdev.blockAndSeek.managers.StateManager; import org.bukkit.Bukkit; -import org.bukkit.World; +import org.bukkit.GameMode; +import org.bukkit.entity.ArmorStand; import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.HandlerList; +import org.bukkit.event.Listener; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.block.BlockDamageEvent; +import org.bukkit.event.block.BlockPlaceEvent; +import org.bukkit.event.entity.*; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.inventory.InventoryCloseEvent; +import org.bukkit.event.player.PlayerDropItemEvent; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.inventory.EquipmentSlot; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.persistence.PersistentDataContainer; +import org.bukkit.plugin.Plugin; +import org.bukkit.scheduler.BukkitRunnable; import java.util.*; public class BlockAndSeekGame { private final BlockAndSeekMap map; - private final World world; private final StateManager stateManager = new StateManager(); + private final PropManager propManager = new PropManager(); + private final String name; - public BlockAndSeekGame(BlockAndSeekMap blockAndSeekMap) { + private GamePhase phase; + + public BlockAndSeekGame(String name, BlockAndSeekMap blockAndSeekMap) { + this.name = name; this.map = blockAndSeekMap; - this.world = Objects.requireNonNull(Bukkit.getWorld(blockAndSeekMap.world())); + start(); } public boolean addPlayer(Player player) { - if (stateManager.playerCount() < map.maxPlayers()) { + if (phase instanceof LobbyPhase && stateManager.playerCount() < map.getMaxPlayers()) { stateManager.setPlayerState(player.getUniqueId(), PlayerType.NONE); + player.teleport(map.getLobby()); + player.setInvulnerable(true); return true; } return false; } - public void removePlayer(Player player) { - stateManager.removePlayer(player.getUniqueId()); + public String players() { + return stateManager.getPlayers().size() + "/" + map.getMaxPlayers(); + } + + private void start() { + phase = new LobbyPhase(); + phase.onEnable(); + new BukkitRunnable() { + @Override + public void run() { + if (phase != null) { + phase.tick(); + } else this.cancel(); + } + }.runTaskTimer(BlockAndSeek.getInstance(), 0, 20); + } + + private void endGame() { + phase = null; + GamesManager.remove(name); + } + + private void sendToAll(TranslationKey key, String... placeholders) { + for (UUID uuid : stateManager.getPlayers()) { + Player player = Bukkit.getPlayer(uuid); + if (player != null) { + player.sendMessage(TranslationManager.get(player, key, placeholders)); + } + } + } + + private void broadcastToAll(TranslationKey key, String... placeholders) { + for (UUID uuid : stateManager.getPlayers()) { + Player player = Bukkit.getPlayer(uuid); + if (player != null) { + player.showTitle(Title.title(TranslationManager.get(player, key, placeholders), Component.empty())); + } + } + } + + private void actionBarToAll(TranslationKey key, String... placeholders) { + for (UUID uuid : stateManager.getPlayers()) { + Player player = Bukkit.getPlayer(uuid); + if (player != null) { + player.sendActionBar(TranslationManager.get(player, key, placeholders)); + } + } } public enum PlayerType { @@ -37,6 +125,343 @@ public class BlockAndSeekGame { SPECTATOR } + //fixme: player leave event: count == 0 -> phase = null -> delete game + private class LobbyPhase extends GamePhase { + + private static final int WAIT_TIME = 30; + + @Override + public void onTick() { + + if (map.getMinPlayers() > stateManager.playerCount()) { + super.tick = 0; + } else { + int timeLeft = WAIT_TIME - tick; + + if (timeLeft <= 0) { + this.onDisable(); + } + + if (timeLeft == 30 || timeLeft == 15 || timeLeft == 10 || timeLeft <= 5) { + sendToAll(TranslationKey.TIME_TO_START, "%time%", String.valueOf(timeLeft)); + } + } + } + + @Override + public void onEnable() { + sendToAll(TranslationKey.WAITING_FOR_PLAYERS); + } + + @Override + public void onDisable() { + phase = new StartedGamePhase(); + phase.onEnable(); + } + + } + + + + private class StartedGamePhase extends GamePhase implements Listener { + + private long gameDuration = map.getGameDuration().toSeconds(); + + @Override + public void onTick() { + if (stateManager.getPlayersInState(PlayerType.SEEKER).isEmpty() || gameDuration == 0) { + var props = stateManager.getPlayersInState(PlayerType.PROP); + if (props.size() == 1) { + Player player = Bukkit.getPlayer(props.iterator().next()); + if (player != null) { + broadcastToAll(TranslationKey.HIDER_SOLO_WIN, "%player%", player.getName()); + } else broadcastToAll(TranslationKey.HIDERS_WIN); + } else broadcastToAll(TranslationKey.HIDERS_WIN); + this.onDisable(); + } else if (stateManager.getPlayersInState(PlayerType.PROP).isEmpty()) { + broadcastToAll(TranslationKey.SEEKERS_WIN); + this.onDisable(); + } else { + actionBarToAll(TranslationKey.TIME_LEFT, "%time%", String.valueOf(gameDuration)); + } + gameDuration--; + } + + @Override + public void onEnable() { + Plugin instance = BlockAndSeek.getInstance(); + instance.getServer().getPluginManager().registerEvents(this, instance); + selectSeekers(1 + (stateManager.playerCount() / 7)); + for (UUID uuid : stateManager.getPlayersInState(PlayerType.NONE)) { + Player player = Bukkit.getPlayer(uuid); + if (player != null) { + stateManager.setPlayerState(uuid, PlayerType.PROP); + ItemManager.hiderInventory(player); + player.teleport(map.getSpawn()); + new RouletteCreator(player, map.getBlocks()); + player.setInvulnerable(false); + } else BlockAndSeek.getPluginLogger().warning("Player is null. "); + } + } + + private void selectSeekers(int count) { + ArrayList rawSeekers = new ArrayList<>(); + Set playerSet = stateManager.getPlayers(); + for (UUID uuid : playerSet) { + if (!GamesManager.triggerSeekerImmune(uuid)) rawSeekers.add(uuid); + } + Collections.shuffle(rawSeekers); + + var seekers = rawSeekers.subList(0, Math.min(count, playerSet.size())); + + for (UUID seeker : seekers) { + Player player = Bukkit.getPlayer(seeker); + if (player != null) { + stateManager.setPlayerState(seeker, PlayerType.SEEKER); + ItemManager.setSeekerSet(player); + Utils.setLevelWithBar(player, 100); + GamesManager.addSeekerImmune(player); + } + } + + new BukkitRunnable() { + @Override + public void run() { + for (UUID seeker : seekers) { + Player player = Bukkit.getPlayer(seeker); + if (player != null) { + player.teleport(map.getSpawn()); + player.setInvulnerable(false); + } + } + } + }.runTaskLater(BlockAndSeek.getInstance(), map.getSeekerSpawnDelay().toSeconds() * 20); + } + + @Override + public void onDisable() { + HandlerList.unregisterAll(this); + phase = new EndedGamePhase(); + phase.onEnable(); + } + + @EventHandler + private void onPlayerLeaveEvent(PlayerQuitEvent event) { + Player player = event.getPlayer(); + UUID uuid = player.getUniqueId(); + if (stateManager.hasPlayer(uuid)) { + stateManager.removePlayer(uuid); + Utils.clearPlayer(player); + } + } + + @EventHandler + private void onItemDrop(PlayerDropItemEvent event) { + if (stateManager.hasPlayer(event.getPlayer().getUniqueId())) { + event.setCancelled(true); + } + } + + @EventHandler + private void onPlayerDamage(EntityDamageEvent event) { + if (event.getEntity() instanceof Player player) { + if (stateManager.hasPlayer(player.getUniqueId())) { + if (event.getCause() == EntityDamageEvent.DamageCause.FALL) { + event.setCancelled(true); + } + } + } + } + + @EventHandler + private void onPlayerDeath(PlayerDeathEvent event) { + Player player = event.getPlayer(); + UUID uuid = player.getUniqueId(); + if (stateManager.hasPlayer(uuid)) { + stateManager.setPlayerState(uuid, PlayerType.SPECTATOR); + player.setGameMode(GameMode.SPECTATOR); + event.setCancelled(true); + } + } + + @EventHandler + private void onBlockBreak(BlockBreakEvent event) { + Player player = event.getPlayer(); + if (stateManager.hasPlayer(player.getUniqueId())) { + event.setCancelled(true); + } + } + + @EventHandler + public void onRegainHealth(EntityRegainHealthEvent event) { + if (event.getEntity() instanceof Player player) { + UUID uuid = player.getUniqueId(); + PlayerType playerType = stateManager.getState(uuid); + + if (playerType == PlayerType.SEEKER) { + event.setCancelled(true); + } else if (playerType == PlayerType.PROP) { + if (!propManager.isProped(uuid)) { + event.setCancelled(true); + } + } + } + } + + @EventHandler + public void onEntityDismount(EntityDismountEvent event) { + if (event.getEntity() instanceof Player player && event.getDismounted() instanceof ArmorStand) { + if (stateManager.hasPlayer(player.getUniqueId())) { + propManager.forceUnfreeze(player); + } + } + } + + + @EventHandler + private void onBlockPlace(BlockPlaceEvent event) { + Player player = event.getPlayer(); + if (stateManager.hasPlayer(player.getUniqueId())) { + event.setCancelled(true); + } + } + + @EventHandler + private void onPlayerDamageByPlayer(EntityDamageByEntityEvent event) { + if (event.getDamager() instanceof Player damager && event.getEntity() instanceof Player victim) { + PlayerType damagerType = stateManager.getState(damager.getUniqueId()); + PlayerType victimType = stateManager.getState(victim.getUniqueId()); + if (damagerType == victimType) { + event.setCancelled(true); + } else { + if (damagerType == PlayerType.SEEKER) { + double currentHealth = damager.getHealth(); + if (currentHealth < 20) { + double newHealth = Math.min(currentHealth + event.getDamage(), 20); + damager.setHealth(newHealth); + Utils.setLevelWithBar(damager, (int) Math.round(damager.getHealth() * 5)); + } + } + } + } + } + + @EventHandler + private void onInventoryClose(InventoryCloseEvent event) { + Inventory inventory = event.getInventory(); + if (inventory.getHolder() instanceof RouletteCreator rouletteCreator && event.getReason() != InventoryCloseEvent.Reason.PLUGIN) { + Player player = (Player) event.getPlayer(); + DisguiseAPI.disguiseToAll(player, new MiscDisguise(DisguiseType.FALLING_BLOCK, rouletteCreator.randomPropItem())); + DisguiseAPI.setActionBarShown(player, false); + } + } + + @EventHandler + public void onBlockDamage(BlockDamageEvent event) { + + Player player = event.getPlayer(); + UUID uuid = player.getUniqueId(); + if (PlayerType.SEEKER.equals(stateManager.getState(uuid))) { + UUID prop = propManager.getProp(event.getBlock().getLocation()); + if (prop != null) { + propManager.forceUnfreeze(prop); + } else { + player.damage(2); + Utils.setLevelWithBar(player, (int) Math.round(player.getHealth() * 5)); + } + } + } + + @EventHandler + private void onInventoryClick(InventoryClickEvent event) { + Inventory inventory = event.getInventory(); + if (inventory.getHolder() instanceof RouletteCreator) { + ItemStack itemStack = event.getCurrentItem(); + int slot = event.getSlot(); + if (itemStack != null && (slot == 21 || slot == 23 || slot == 25)) { + Player player = (Player) event.getWhoClicked(); + player.closeInventory(InventoryCloseEvent.Reason.PLUGIN); + DisguiseAPI.disguiseToAll(player, new MiscDisguise(DisguiseType.FALLING_BLOCK, itemStack)); + DisguiseAPI.setActionBarShown(player, false); + } + event.setCancelled(true); + } + } + + private final Set coolDown = new HashSet<>(); + + @EventHandler + public void onRightClick(PlayerInteractEvent event) { + if (event.getHand() != EquipmentSlot.HAND) return; + if (event.getAction().isLeftClick()) return; + + Player player = event.getPlayer(); + UUID uuid = player.getUniqueId(); + + ItemStack item = event.getItem(); + if (item != null) { + ItemMeta meta = item.getItemMeta(); + if (meta != null) { + PersistentDataContainer dataContainer = meta.getPersistentDataContainer(); + if (dataContainer.has(Keys.FREEZE_ITEM)) { + if (!coolDown.contains(uuid)) { + player.sendActionBar(Component.text(propManager.freezeOrUnfreeze(event.getPlayer()).toString())); + coolDown.add(uuid); + new BukkitRunnable() { + @Override + public void run() { + coolDown.remove(uuid); + } + }.runTaskLater(BlockAndSeek.getInstance(), 2); + } + } + } + } + } + + + + } + + private class EndedGamePhase extends GamePhase { + + private final long endTick = map.getDelayBeforeGameEnd().toSeconds(); + + @Override + public void onTick() { + if (super.tick == endTick) { + this.onDisable(); + } + } + + @Override + public void onEnable() { + for (UUID uuid : stateManager.getPlayers()) { + Player player = Bukkit.getPlayer(uuid); + if (player != null) { + propManager.forceUnfreeze(player); + player.setInvulnerable(true); + player.setGlowing(true); + } + } + } + + @Override + public void onDisable() { + for (UUID uuid : stateManager.getPlayers()) { + Player player = Bukkit.getPlayer(uuid); + if (player != null) { + //propManager.removePlayer(player); + Utils.clearPlayer(player); + player.setInvulnerable(true); + ItemManager.defaultInventory(player); + player.teleport(Config.spawn()); + } + } + endGame(); + } + + } diff --git a/src/main/java/hdvtdev/blockandseek/objects/BlockAndSeekMap.java b/src/main/java/hdvtdev/blockandseek/objects/BlockAndSeekMap.java index e2b7fbd..f26f684 100644 --- a/src/main/java/hdvtdev/blockandseek/objects/BlockAndSeekMap.java +++ b/src/main/java/hdvtdev/blockandseek/objects/BlockAndSeekMap.java @@ -1,17 +1,59 @@ -package hdvtdev.blockAndSeek.objects; +package hdvtdev.blockandseek.objects; -import org.bukkit.Bukkit; -import org.bukkit.World; +import eu.okaeri.configs.OkaeriConfig; +import eu.okaeri.configs.annotation.Comment; +import eu.okaeri.configs.annotation.CustomKey; +import eu.okaeri.validator.annotation.Min; -public record BlockAndSeekMap(String world, Cords spawn, Cords lobby, - int minPlayers, int maxPlayers, int duration, int seekerSpawnDelay, int delayBeforeEnd) { +import lombok.Getter; +import lombok.Setter; - public static void prepareWorld(World world) { - if (Integer.parseInt(Bukkit.getMinecraftVersion().replaceAll("[^0-9]", "")) >= 1215) { - String cmd = "execute in " + world.getKey() + " run gamerule locatorBar true"; - Bukkit.dispatchCommand(Bukkit.getConsoleSender(), cmd); - } - } +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.inventory.ItemStack; +import java.time.Duration; +import java.util.ArrayList; +import java.util.List; + +@Getter +@Setter +public class BlockAndSeekMap extends OkaeriConfig { + + @Comment("The name of the world where the game takes place") + private String world; + + @Comment("The main spawn point inside the arena") + private Location spawn; + + @Comment("The waiting lobby location where players join before the match") + private Location lobby; + + @Comment("Minimum number of players required to start the game") + @Min(2) + @CustomKey("min-players") + private int minPlayers = 2; + + @Comment("Maximum number of players allowed in the game") + @CustomKey("max-players") + private int maxPlayers = 12; + + @Comment("Total duration of the match. Format examples: 10m, 300s, 1h") + @CustomKey("game-duration") + private Duration gameDuration = Duration.ofMinutes(10); + + @Comment("How long seekers must wait before they are released") + @CustomKey("seeker-spawn-delay") + private Duration seekerSpawnDelay = Duration.ofSeconds(30); + + @Comment("Time to stay in the arena after the game ends (Post-game phase)") + @Comment("Allows players to look around, check positions and chat before being sent to lobby") + @CustomKey("delay-before-game-end") + private Duration delayBeforeGameEnd = Duration.ofSeconds(10); + + @Comment("Available blocks for hiders") + private List blocks = new ArrayList<>(List.of(new PropBlock(new ItemStack(Material.TARGET), Rarity.LEGENDARY))); } + + diff --git a/src/main/java/hdvtdev/blockandseek/objects/GamePhase.java b/src/main/java/hdvtdev/blockandseek/objects/GamePhase.java index eeb1ead..d83fac7 100644 --- a/src/main/java/hdvtdev/blockandseek/objects/GamePhase.java +++ b/src/main/java/hdvtdev/blockandseek/objects/GamePhase.java @@ -1,4 +1,18 @@ package hdvtdev.blockandseek.objects; -public interface GamePhase { +import org.jetbrains.annotations.Nullable; + +public abstract class GamePhase { + + protected int tick = 0; + + public final void tick() { + onTick(); + tick++; + } + + protected abstract void onTick(); + protected abstract void onEnable(); + protected abstract void onDisable(); + } diff --git a/src/main/java/hdvtdev/blockandseek/objects/PropBlock.java b/src/main/java/hdvtdev/blockandseek/objects/PropBlock.java index ac8cdd9..59b93c3 100644 --- a/src/main/java/hdvtdev/blockandseek/objects/PropBlock.java +++ b/src/main/java/hdvtdev/blockandseek/objects/PropBlock.java @@ -1,9 +1,25 @@ -package hdvtdev.blockAndSeek.objects; +package hdvtdev.blockandseek.objects; +import eu.okaeri.configs.OkaeriConfig; +import eu.okaeri.configs.annotation.Comment; import org.bukkit.inventory.ItemStack; -public record PropBlock(ItemStack block, int chance) { - public PropBlock(ItemStack block, Rarity rarity) { - this(block, rarity.toChance()); +public class PropBlock extends OkaeriConfig { + + private ItemStack block; + private Rarity rarity = Rarity.COMMON; + + public ItemStack getBlock() { + return block; } + + public Rarity getRarity() { + return rarity; + } + + public PropBlock(ItemStack itemStack, Rarity rarity) { + this.block = itemStack; + this.rarity = rarity; + } + } diff --git a/src/main/java/hdvtdev/blockandseek/objects/Rarity.java b/src/main/java/hdvtdev/blockandseek/objects/Rarity.java index 98655fd..837db2a 100644 --- a/src/main/java/hdvtdev/blockandseek/objects/Rarity.java +++ b/src/main/java/hdvtdev/blockandseek/objects/Rarity.java @@ -1,23 +1,20 @@ -package hdvtdev.blockAndSeek.objects; +package hdvtdev.blockandseek.objects; public enum Rarity { + COMMON(100), // Очень часто (как грязь) + UNCOMMON(50), // Часто (половина от обычного) + RARE(25), // Редко + EPIC(10), // Очень редко + MYTHIC(3), // Почти невозможно + LEGENDARY(1); // Чудо - COMMON,// 38% - UNCOMMON,// 27% - RARE,// 19% - EPIC,// 10% - MYTHIC,// 4% - LEGENDARY; // 2% + private final int weight; - public int toChance() { - return switch (this.ordinal()) { - case 1 -> 27; - case 2 -> 19; - case 3 -> 10; - case 4 -> 4; - case 5 -> 2; - default -> 38; - }; + Rarity(int weight) { + this.weight = weight; } -} + public int getChance() { + return weight; + } +} \ No newline at end of file diff --git a/src/main/java/hdvtdev/blockandseek/objects/Translation.java b/src/main/java/hdvtdev/blockandseek/objects/Translation.java new file mode 100644 index 0000000..66ac95b --- /dev/null +++ b/src/main/java/hdvtdev/blockandseek/objects/Translation.java @@ -0,0 +1,17 @@ +package hdvtdev.blockandseek.objects; + +import eu.okaeri.configs.OkaeriConfig; +import eu.okaeri.configs.annotation.Header; + +import lombok.Getter; + +import java.util.EnumMap; +import java.util.Map; + +@Header("Translations lives here") +@Getter +public class Translation extends OkaeriConfig { + + private Map messages = new EnumMap<>(TranslationKey.class); + +} \ No newline at end of file diff --git a/src/main/java/hdvtdev/blockandseek/objects/TranslationKey.java b/src/main/java/hdvtdev/blockandseek/objects/TranslationKey.java new file mode 100644 index 0000000..efe63c9 --- /dev/null +++ b/src/main/java/hdvtdev/blockandseek/objects/TranslationKey.java @@ -0,0 +1,40 @@ +package hdvtdev.blockandseek.objects; + +public enum TranslationKey { + // Misc + UNKNOWN_COMMAND, + SEEKER_TEMPLATE, + + // Maps management + UNKNOWN_MAP, + SUCCESSFUL_MAP_CREATION, + + // Menus + MENU, + GAMES_MENU, + MAPS_MENU, + + // Menus buttons + GAME, + CREATE_GAME, + MAP, + + // Game + TIME_LEFT, + TIME_TO_START, + PLAYER_JOINED, + PLAYER_LEFT, + SEEKERS_WIN, + HIDERS_WIN, + HIDER_SOLO_WIN, + ROULETTE, + GAME_IS_FULL, + WAITING_FOR_PLAYERS, + + // Items + FREEZE_ITEM, + FACE_CHANGING_ITEM, + SOUND_ITEM, + LEAVE_ITEM, + DASH_ITEM; +} \ No newline at end of file diff --git a/src/main/java/hdvtdev/blockandseek/roulette/RouletteCreator.java b/src/main/java/hdvtdev/blockandseek/roulette/RouletteCreator.java index fc24209..7c92c56 100644 --- a/src/main/java/hdvtdev/blockandseek/roulette/RouletteCreator.java +++ b/src/main/java/hdvtdev/blockandseek/roulette/RouletteCreator.java @@ -1,4 +1,11 @@ -package hdvtdev.blockAndSeek.roulette; +package hdvtdev.blockandseek.roulette; + + +import hdvtdev.blockandseek.BlockAndSeek; +import hdvtdev.blockandseek.managers.TranslationManager; +import hdvtdev.blockandseek.objects.PropBlock; + +import hdvtdev.blockandseek.objects.TranslationKey; import org.bukkit.Bukkit; import org.bukkit.Sound; @@ -7,15 +14,14 @@ import org.bukkit.inventory.Inventory; import org.bukkit.inventory.InventoryHolder; import org.bukkit.inventory.ItemStack; import org.bukkit.scheduler.BukkitRunnable; -import org.bukkit.scheduler.BukkitTask; + import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.List; import java.util.Random; -/* + public final class RouletteCreator implements InventoryHolder { @@ -24,146 +30,116 @@ public final class RouletteCreator implements InventoryHolder { private static final Random random = new Random(); private final Inventory roulette; - private Task task; - private volatile Boolean closedByPlayer = true; + private boolean isRunning = true; - public RouletteCreator(@NotNull Player player, List blocks) { - roulette = Bukkit.createInventory(this, 45, Localization.getComponent(player, "roulette-title")); - this.createUnoptimizedRoulette(roulette, player, blocks); + public RouletteCreator(@NotNull Player player, @NotNull List blocks) { + roulette = Bukkit.createInventory(this, 45, TranslationManager.get(player, TranslationKey.ROULETTE)); + this.createSmoothRoulette(roulette, player, blocks); } - public @NotNull Task getTask() { - return task; - } - - public @Nullable Boolean isClosedByPlayer() { - return closedByPlayer; - } @Override public @NotNull Inventory getInventory() { return roulette; } - public void closeInventoryBySystem() { - closedByPlayer = false; - roulette.close(); - } - - public void closeInventory() { - closedByPlayer = null; - roulette.close(); - } public ItemStack randomPropItem() { - return roulette.getItem(slots[random.nextInt(0, 15)]); - } - - public ItemStack randomMidPropItem() { - return roulette.getItem(midSlots[random.nextInt(0, 3)]); + return isRunning ? roulette.getItem(slots[random.nextInt(0, 15)]) : roulette.getItem(midSlots[random.nextInt(0, 3)]); } @ApiStatus.Experimental - private void createUnoptimizedRoulette(Inventory gui, Player player, List blocks) { - - BukkitTask rouletteTask = new BukkitRunnable() { + private void createSmoothRoulette(Inventory gui, Player player, List blocks) { + player.openInventory(gui); + new BukkitRunnable() { final RouletteGenerator rouletteGenerator = new RouletteGenerator(blocks); - final List> rows = List.of( + final List> sources = List.of( new RouletteList<>(rouletteGenerator.getRandomRow(15)), new RouletteList<>(rouletteGenerator.getRandomRow(15)), new RouletteList<>(rouletteGenerator.getRandomRow(15)) ); - - final List items; - + final List columns = new ArrayList<>(); { - List rawItems = new ArrayList<>(); - for (int j = 0; j < 3; j++) { - ItemStack[] itemStacks = new ItemStack[5]; - for (int l = 0; l < 5; l++) { - itemStacks[l] = rows.get(j).next(); + for (int i = 0; i < 3; i++) { + ItemStack[] col = new ItemStack[5]; + for (int row = 0; row < 5; row++) { + col[row] = sources.get(i).next(); } - rawItems.add(j, itemStacks); + columns.add(col); } - items = rawItems; } - final long startTime = System.currentTimeMillis(); - double currentSpeed = 0; - int i = 0; + final int[] colStartIndices = {3, 5, 7}; + int ticksAlive = 0; // Общее время работы анимации + int ticksUntilNextMove = 0; // Обратный отсчет до следующего кадра @Override public void run() { - - long now = System.currentTimeMillis(); - double elapsed = (now - startTime) / 1000.0; - - if (elapsed >= 5) { + // Безопасность + if (!player.isOnline() || !player.getOpenInventory().getTopInventory().equals(gui)) { this.cancel(); + return; } - double speed; - if (elapsed < 2) speed = 1.0; - else if (elapsed < 2.2) speed = 0.8; - else if (elapsed < 2.4) speed = 0.6; - else if (elapsed < 2.6) speed = 0.5; - else if (elapsed < 2.8) speed = 0.4; - else if (elapsed < 3) speed = 0.33; - else if (elapsed < 3.2) speed = 0.28; - else if (elapsed < 3.5) speed = 0.22; - else if (elapsed < 3.8) speed = 0.15; - else speed = 0.1; + // Увеличиваем общий счетчик жизни таймера + ticksAlive++; - task(speed); + // Если время ожидания еще не вышло — ждем + if (ticksUntilNextMove > 0) { + ticksUntilNextMove--; + return; + } - i++; + updateRoulette(); + + if (ticksAlive < 30) { // 0 - 1.5 сек: + ticksUntilNextMove = 1; // Каждые 2 тика (быстро, но не мыло) + } else if (ticksAlive < 50) { // 1.5 - 2.5 сек: + ticksUntilNextMove = 2; // Чуть медленнее (каждые 3 тика) + } else if (ticksAlive < 65) { // 2.5 - 3.2 сек: + ticksUntilNextMove = 3; // Еще медленнее (каждые 4 тика) + } else if (ticksAlive < 75) { // 3.2 - 3.7 сек: + ticksUntilNextMove = 5; // Заметное торможение + } else if (ticksAlive < 85) { // 3.7 - 4.2 сек: + ticksUntilNextMove = 8; // Почти конец + } else if (ticksAlive < 95) { // Последние вздохи + ticksUntilNextMove = 12; + } else if (ticksAlive < 110) { + ticksUntilNextMove = 20; // Финальный медленный шаг + } else { + // КОНЕЦ + isRunning = false; + player.playSound(player.getLocation(), Sound.ENTITY_PLAYER_LEVELUP, 1f, 2f); + this.cancel(); + } } - private void task(double speed) { - if (currentSpeed >= 1) { - currentSpeed = 0; - 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]); + private void updateRoulette() { + player.playSound(player.getLocation(), Sound.UI_BUTTON_CLICK, 0.5f, 1f); + + for (int colIndex = 0; colIndex < 3; colIndex++) { + ItemStack[] colItems = columns.get(colIndex); + + for (int row = 0; row < 5; row++) { + int slot = colStartIndices[colIndex] + (row * 9); + gui.setItem(slot, colItems[row]); } - player.playSound(player.getLocation(), Sound.UI_BUTTON_CLICK, 0.5f, 2f); - - 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(); - } else currentSpeed += speed; - + System.arraycopy(colItems, 0, colItems, 1, 4); + colItems[0] = sources.get(colIndex).next(); + } } - }.runTaskTimer(BlockAndSeek.getInstance(), 0, 1); - - - task = new Task(rouletteTask, Bukkit.getScheduler().runTaskLater(BlockAndSeek.getInstance(), this::closeInventoryBySystem, 300)); - - player.openInventory(gui); + }.runTaskTimer(BlockAndSeek.getInstance(), 0L, 1L); } - public record Task(BukkitTask rouletteTask, BukkitTask autoCloseTask) { - public void cancelBoth() { - rouletteTask.cancel(); - autoCloseTask.cancel(); - } } -} - */ \ No newline at end of file + diff --git a/src/main/java/hdvtdev/blockandseek/roulette/RouletteGenerator.java b/src/main/java/hdvtdev/blockandseek/roulette/RouletteGenerator.java index 6504d6e..80532b5 100644 --- a/src/main/java/hdvtdev/blockandseek/roulette/RouletteGenerator.java +++ b/src/main/java/hdvtdev/blockandseek/roulette/RouletteGenerator.java @@ -1,18 +1,22 @@ -package hdvtdev.blockAndSeek.roulette; +package hdvtdev.blockandseek.roulette; + import com.lewdev.probabilitylib.ProbabilityCollection; + +import hdvtdev.blockandseek.objects.PropBlock; + 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 (BlockAndSeekMap.Block block : blocks) { - probabilityCollection.add(block.block(), block.chance()); + public RouletteGenerator(List blocks) { + for (PropBlock block : blocks) { + probabilityCollection.add(block.getBlock(), block.getRarity().getChance()); } } @@ -22,7 +26,6 @@ public class RouletteGenerator { return items; } - } - */ + diff --git a/src/main/java/hdvtdev/blockandseek/roulette/RouletteList.java b/src/main/java/hdvtdev/blockandseek/roulette/RouletteList.java index 7aee9e8..7901e6b 100644 --- a/src/main/java/hdvtdev/blockandseek/roulette/RouletteList.java +++ b/src/main/java/hdvtdev/blockandseek/roulette/RouletteList.java @@ -1,4 +1,4 @@ -package hdvtdev.blockAndSeek.roulette; +package hdvtdev.blockandseek.roulette; import org.jetbrains.annotations.NotNull; diff --git a/src/main/resources/languages/README.txt b/src/main/resources/languages/README.txt new file mode 100644 index 0000000..0bcfc0e --- /dev/null +++ b/src/main/resources/languages/README.txt @@ -0,0 +1,2 @@ +Place your translations in language folder. Translation file name must be a language tag and file extension must be .yml. +Use en_US.yml as example. \ No newline at end of file diff --git a/src/main/resources/languages/en_US.yml b/src/main/resources/languages/en_US.yml index e69de29..7835ed0 100644 --- a/src/main/resources/languages/en_US.yml +++ b/src/main/resources/languages/en_US.yml @@ -0,0 +1,26 @@ +messages: + UNKNOWN_COMMAND: "Unknown command: %command%." + SEEKER_TEMPLATE: "%template%" + UNKNOWN_MAP: "Unknown map: %map%. Available maps: %maps%" + SUCCESSFUL_MAP_CREATION: "Map %map% was successfully created. Use /blockandseek map %map% to edit" + MENU: menu + GAMES_MENU: "games" + MAPS_MENU: "maps" + GAME: "%name%" + CREATE_GAME: "Create game" + MAP: "%name%" + TIME_LEFT: "Time left %time%" + PLAYER_JOINED: "%player% joined. %count%/%max%" + PLAYER_LEFT: "%player% left. %count%/%max%" + SEEKERS_WIN: "Seekers won this game!" + HIDERS_WIN: "Hiders won this game!" + HIDER_SOLO_WIN: "%player% won this game!" + FREEZE_ITEM: "Freezer 3000" + SOUND_ITEM: "Sounder 3000" + LEAVE_ITEM: "Leave" + DASH_ITEM: "Dash" + ROULETTE: "Blocks roulette" + TIME_TO_START: "Game starts in %time%" + GAME_IS_FULL: "failed> to join %game%. Game is full." + FACE_CHANGING_ITEM: "FACE_CHANGING_ITEM: todo" + WAITING_FOR_PLAYERS: "Waiting for players" \ No newline at end of file diff --git a/src/main/resources/languages/ru_RU.yml b/src/main/resources/languages/ru_RU.yml new file mode 100644 index 0000000..4946811 --- /dev/null +++ b/src/main/resources/languages/ru_RU.yml @@ -0,0 +1,23 @@ +messages: + UNKNOWN_COMMAND: "Неизвестная команда: %command%." + SEEKER_TEMPLATE: %template% + UNKNOWN_MAP: "Неизвестная карта: %map%. Доступные карты: %maps%" + SUCCESSFUL_MAP_CREATION: Карта %map% была успешно создана. Используйте /blockandseek map %map%, чтобы редактировать + MENU: меню + GAMES_MENU: игры + MAPS_MENU: карты + GAME: %name% + CREATE_GAME: Создать игру + MAP: %name% + TIME_LEFT: "Осталось времени: %time%" + PLAYER_JOINED: %player% присоединился. %count%/%max% + PLAYER_LEFT: %player% вышел. %count%/%max% + SEEKERS_WIN: Искатели победили! + HIDERS_WIN: Прячущиеся победили! + HIDER_SOLO_WIN: %player% победил! + FREEZE_ITEM: Замораживатель 3000 + SOUND_ITEM: Шумелка 3000 + LEAVE_ITEM: Выйти + DASH_ITEM: Рывок + ROULETTE: Рулетка блоков + TIME_TO_START: Игра начнется через %time% \ No newline at end of file diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 6e9a625..c268846 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -1,7 +1,11 @@ name: BlockAndSeek version: '0.0.1-a' -main: hdvtdev.blockAndSeek.BlockAndSeek +main: hdvtdev.blockandseek.BlockAndSeek api-version: '1.20' +load: POSTWORLD + +depend: + - LibsDisguises permissions: blockandseek.manage: @@ -12,4 +16,4 @@ commands: blockandseek: aliases: - bs - usage: "Usage: /blockandseek [subcommand]" \ No newline at end of file + usage: "Usage: /blockandseek [subcommand]"