refactor: generator minigame system (gui + text), fix color codes, holograms, messages, & config

This commit is contained in:
loki5512344 2026-06-14 16:04:25 +02:00
parent 957ab4a5a2
commit 7c7562a8d4
48 changed files with 1628 additions and 733 deletions

View file

@ -1,11 +1,9 @@
package dev.loki.command;
import dev.loki.config.LocationsConfig;
import dev.loki.mdbd;
import dev.loki.util.SpawnUtil;
import dev.loki.util.TextUtil;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import dev.loki.config.MessagesConfig;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.command.Command;
@ -52,15 +50,19 @@ public class SetupCommand implements TabExecutor {
this.plugin = plugin;
}
private MessagesConfig messages() {
return plugin.getConfigManager().getMessages();
}
@Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command,
@NotNull String label, @NotNull String[] args) {
if (!sender.hasPermission("mdbd.admin")) {
sender.sendMessage(Component.text("Нет прав!", NamedTextColor.RED));
sender.sendMessage(messages().get("cmd.no-permission"));
return true;
}
if (!(sender instanceof Player player)) {
sender.sendMessage(Component.text("Только игроки!", NamedTextColor.RED));
sender.sendMessage(messages().get("cmd.players-only"));
return true;
}
if (args.length == 0) { sendUsage(sender); return true; }
@ -68,7 +70,6 @@ public class SetupCommand implements TabExecutor {
return switch (args[0].toLowerCase()) {
case "wand" -> giveWand(player);
case "set" -> handleSet(player, args);
case "build" -> buildMap(player);
case "clear" -> clearMap(player);
case "clear-spawns" -> clearSpawns(player);
case "lobby" -> setLobby(player);
@ -92,7 +93,7 @@ public class SetupCommand implements TabExecutor {
org.bukkit.persistence.PersistentDataType.BYTE, (byte) 1);
});
player.getInventory().addItem(wand);
player.sendMessage(TextUtil.color("<#00FF00>Получен DBD Setup Wand"));
player.sendMessage(messages().get("setup.wand-given"));
return true;
}
@ -100,11 +101,9 @@ public class SetupCommand implements TabExecutor {
private boolean handleSet(Player player, String[] args) {
if (args.length < 2) {
player.sendMessage(TextUtil.color("<#FF0000>Использование: /dbdsetup set <тип> [номер]"));
player.sendMessage(TextUtil.color(
"<#FFD700>Типы: hook, chest, generator, killer-spawn, survivor-spawn, gate"));
player.sendMessage(TextUtil.color(
"<#FFD700>Для спавнов можно указать номер: /dbdsetup set killer-spawn 0"));
player.sendMessage(messages().get("setup.set-usage"));
player.sendMessage(messages().get("setup.set-types"));
player.sendMessage(messages().get("setup.set-spawn-info"));
return true;
}
@ -113,12 +112,12 @@ public class SetupCommand implements TabExecutor {
if (type.equals("gate")) {
if (args.length < 3) {
player.sendMessage(TextUtil.color("<#FF0000>Укажи: /dbdsetup set gate A (или B)"));
player.sendMessage(messages().get("setup.gate-usage"));
return true;
}
String gateId = args[2].toUpperCase();
if (!gateId.equals("A") && !gateId.equals("B")) {
player.sendMessage(TextUtil.color("<#FF0000>Ворота могут быть только A или B"));
player.sendMessage(messages().get("setup.gate-invalid"));
return true;
}
return placeGate(player, gateId);
@ -131,7 +130,7 @@ public class SetupCommand implements TabExecutor {
};
if (!List.of("hook", "chest", "generator", "killer-spawn", "survivor-spawn").contains(mode)) {
player.sendMessage(TextUtil.color("<#FF0000>Неизвестный тип: " + type));
player.sendMessage(messages().get("setup.unknown-type", type));
return true;
}
@ -139,13 +138,22 @@ public class SetupCommand implements TabExecutor {
int spawnIndex = -1;
if (args.length >= 3 && (mode.equals("killer-spawn") || mode.equals("survivor-spawn"))) {
try {
spawnIndex = Integer.parseInt(args[2]);
if (spawnIndex < 0) {
player.sendMessage(TextUtil.color("<#FF0000>Номер спавна должен быть >= 0"));
return true;
int raw = Integer.parseInt(args[2]);
if (mode.equals("survivor-spawn")) {
if (raw < 1 || raw > 4) {
player.sendMessage(messages().get("setup.survivor-spawn-range"));
return true;
}
spawnIndex = raw; // 1-4, храним как есть
} else {
if (raw < 0) {
player.sendMessage(messages().get("setup.killer-spawn-range"));
return true;
}
spawnIndex = raw;
}
} catch (NumberFormatException e) {
player.sendMessage(TextUtil.color("<#FF0000>Номер спавна должен быть числом: " + args[2]));
player.sendMessage(messages().get("setup.invalid-number", args[2]));
return true;
}
}
@ -154,22 +162,16 @@ public class SetupCommand implements TabExecutor {
SetupData currentData = setupMode.get(uuid);
if (currentData != null && mode.equals(currentData.mode)) {
setupMode.remove(uuid);
player.sendMessage(TextUtil.color("<#FFA500>Режим установки <#FFD700>" + mode + " <#FFA500>выключен"));
player.sendMessage(messages().get("setup.mode-off", mode));
return true;
}
setupMode.put(uuid, new SetupData(mode, spawnIndex));
String indexMsg = spawnIndex >= 0 ? " #" + spawnIndex : "";
if (mode.contains("spawn")) {
player.sendMessage(TextUtil.color(
"<#00FF00>Режим установки: <#FFD700>" + mode + indexMsg +
"\n<#00FF00>Сломай блок чтобы установить спавн (блок восстановится)" +
"\n<#FFA500>Повторно введи команду для выключения"));
player.sendMessage(messages().get("setup.mode-on-spawn", mode, indexMsg));
} else {
player.sendMessage(TextUtil.color(
"<#00FF00>Режим установки: <#FFD700>" + mode + indexMsg +
"\n<#00FF00>Сломай блок чтобы установить (блок восстановится)" +
"\n<#FFA500>Введи команду снова чтобы выключить режим"));
player.sendMessage(messages().get("setup.mode-on", mode, indexMsg));
}
return true;
}
@ -179,13 +181,12 @@ public class SetupCommand implements TabExecutor {
Location p2 = pos2.get(player.getUniqueId());
if (p1 == null || p2 == null) {
player.sendMessage(TextUtil.color(
"<#FF0000>Сначала выбери оба угла топориком! (ЛКМ=точка 1, ПКМ=точка 2)"));
player.sendMessage(messages().get("setup.gate-need-points"));
return true;
}
if (!p1.getWorld().equals(p2.getWorld())) {
player.sendMessage(TextUtil.color("<#FF0000>Точки должны быть в одном мире!"));
player.sendMessage(messages().get("setup.gate-same-world"));
return true;
}
@ -202,8 +203,7 @@ public class SetupCommand implements TabExecutor {
locations.setGate(gateId, center);
plugin.getConfigManager().saveConfig("locations.yml", locations.getConfig());
player.sendMessage(TextUtil.color(
"<#00FF00>Ворота <#FFFFFF>" + gateId + " <#00FF00>установлены: " + formatLoc(center)));
player.sendMessage(messages().get("setup.gate-placed", gateId, formatLoc(center)));
return true;
}
@ -229,39 +229,42 @@ public class SetupCommand implements TabExecutor {
switch (mode) {
case "hook" -> {
locations.addLocation("hooks", blockLoc.clone().add(0.5, 0, 0.5));
player.sendMessage(TextUtil.color("<#00FF00>Крюк установлен: " + formatLoc(loc)));
player.sendMessage(messages().get("setup.hook-placed", formatLoc(loc)));
}
case "chest" -> {
locations.addLocation("chests", blockLoc.clone().add(0.5, 0, 0.5));
player.sendMessage(TextUtil.color("<#00FF00>Сундук добавлен: " + formatLoc(loc)));
player.sendMessage(messages().get("setup.chest-placed", formatLoc(loc)));
}
case "generator" -> {
locations.addLocation("generators", blockLoc.clone().add(0.5, 0, 0.5));
plugin.getGeneratorData().addGenerator(blockLoc.clone().add(0.5, 0, 0.5));
player.sendMessage(TextUtil.color("<#00FF00>Генератор добавлен: " + formatLoc(loc)));
player.sendMessage(messages().get("setup.generator-placed", formatLoc(loc)));
}
case "killer-spawn" -> {
if (locations.getKillerSpawn() != null) {
player.sendMessage(messages().get("setup.killer-spawn-exists"));
break;
}
locations.setSingleLocation("killer-spawn", loc);
player.sendMessage(TextUtil.color("<#FF4444>Спавн киллера установлен: " + formatLoc(loc)));
player.sendMessage(messages().get("setup.killer-spawn-placed", formatLoc(loc)));
data.spawnIndex = -1;
}
case "survivor-spawn" -> {
int currentCount = locations.getSurvivorSpawns().size();
if (currentCount >= 4) {
player.sendMessage(TextUtil.color("<#FF0000>Достигнут лимит спавнов выживших (4)! Используй /dbdsetup clear-spawns для сброса."));
int slot = data.spawnIndex >= 1 ? data.spawnIndex : locations.firstFreeSurvivorSlot();
if (slot == -1) {
player.sendMessage(messages().get("setup.survivor-spawn-limit"));
break;
}
saveSpawn(locations, "survivor-spawns", loc, data.spawnIndex);
locations.setSurvivorSpawn(slot, loc);
int newCount = locations.getSurvivorSpawns().size();
String label = switch (newCount) {
String label = switch (slot) {
case 1 -> "первого";
case 2 -> "второго";
case 3 -> "третьего";
case 4 -> "четвёртого";
default -> "#" + newCount;
default -> "#" + slot;
};
player.sendMessage(TextUtil.color("<#00FF00>Спавн для <#FFD700>" + label + " <#00FF00>выжившего установлен! (всего: " + newCount + "/4) " + formatLoc(loc)));
// Сбрасываем индекс после использования, чтобы следующие блоки добавлялись
player.sendMessage(messages().get("setup.survivor-spawn-placed", label, newCount, formatLoc(loc)));
data.spawnIndex = -1;
}
}
@ -279,23 +282,12 @@ public class SetupCommand implements TabExecutor {
// НЕ выходим из режима — чтобы можно было установить несколько подряд
}
/**
* Saves a spawn location at a specific index, or appends if index is -1.
*/
private void saveSpawn(LocationsConfig locations, String path, Location loc, int index) {
if (index >= 0) {
locations.setLocationAtIndex(path, index, loc);
} else {
locations.addLocation(path, loc);
}
}
// ── Wand position callbacks ────────────────────────────────────────────────
public void setWandPos(Player player, int num, Location loc) {
if (num == 1) pos1.put(player.getUniqueId(), loc);
else pos2.put(player.getUniqueId(), loc);
player.sendMessage(TextUtil.color("<#00FF00>[Wand] Точка " + num + ": " + formatLoc(loc)));
player.sendMessage(messages().get("setup.wand-point", num, formatLoc(loc)));
}
public boolean isInSetupMode(UUID uuid) {
@ -315,26 +307,20 @@ public class SetupCommand implements TabExecutor {
return data != null ? data.spawnIndex : -1;
}
// ── Build / clear / lobby ─────────────────────────────────────────────────
private boolean buildMap(Player player) {
plugin.getMapBuilder().buildMap(plugin.getConfigManager().getLocations());
player.sendMessage(TextUtil.color("<#00FF00>Карта построена!"));
return true;
}
// ── Clear / lobby ─────────────────────────────────────────────────
private boolean clearMap(Player player) {
plugin.getMapBuilder().clearMap(plugin.getConfigManager().getLocations());
player.sendMessage(TextUtil.color("<#00FF00>Карта очищена!"));
player.sendMessage(messages().get("setup.map-cleared"));
return true;
}
private boolean clearSpawns(Player player) {
var locations = plugin.getConfigManager().getLocations();
locations.clearLocationList("killer-spawn");
locations.clearLocationList("survivor-spawns");
locations.clearSurvivorSpawns();
plugin.getConfigManager().saveConfig("locations.yml", locations.getConfig());
player.sendMessage(TextUtil.color("<#00FF00>Все спавны (киллер + выжившие) очищены!"));
player.sendMessage(messages().get("setup.spawns-cleared"));
return true;
}
@ -342,23 +328,23 @@ public class SetupCommand implements TabExecutor {
plugin.getConfigManager().getLocations().setLobby(player.getLocation());
plugin.getConfigManager().saveConfig("locations.yml",
plugin.getConfigManager().getLocations().getConfig());
player.sendMessage(TextUtil.color("<#00FF00>Лобби установлено: " + formatLoc(player.getLocation())));
player.sendMessage(messages().get("setup.lobby-set", formatLoc(player.getLocation())));
return true;
}
// ── Usage / helpers ───────────────────────────────────────────────────────
private void sendUsage(CommandSender sender) {
sender.sendMessage(TextUtil.color("<#FF6B00>=== DBD Setup ==="));
sender.sendMessage(TextUtil.color("<#FFD700>/dbdsetup wand — Получить ванд (ЛКМ/ПКМ для ворот)"));
sender.sendMessage(TextUtil.color("<#FFD700>/dbdsetup set hook — Установить крюк (ломай блок)"));
sender.sendMessage(TextUtil.color("<#FFD700>/dbdsetup set chest — Установить сундук"));
sender.sendMessage(TextUtil.color("<#FFD700>/dbdsetup set generator — Установить генератор"));
sender.sendMessage(TextUtil.color("<#FFD700>/dbdsetup set killer-spawn — Спавн киллера (только 1)"));
sender.sendMessage(TextUtil.color("<#FFD700>/dbdsetup set survivor-spawn — Спавн выжившего (до 4)"));
sender.sendMessage(TextUtil.color("<#FFD700>/dbdsetup set gate A|B — Установить ворота (из точек ванда)"));
sender.sendMessage(TextUtil.color("<#FFD700>/dbdsetup clear-spawns — Очистить все спавны"));
sender.sendMessage(TextUtil.color("<#FFD700>/dbdsetup lobby — Установить точку лобби"));
sender.sendMessage(messages().get("usage.setup-header"));
sender.sendMessage(messages().get("usage.setup-wand"));
sender.sendMessage(messages().get("usage.setup-hook"));
sender.sendMessage(messages().get("usage.setup-chest"));
sender.sendMessage(messages().get("usage.setup-generator"));
sender.sendMessage(messages().get("usage.setup-killer-spawn"));
sender.sendMessage(messages().get("usage.setup-survivor-spawn"));
sender.sendMessage(messages().get("usage.setup-gate"));
sender.sendMessage(messages().get("usage.setup-clear-spawns"));
sender.sendMessage(messages().get("usage.setup-lobby"));
}
private String formatLoc(Location loc) {
@ -371,7 +357,7 @@ public class SetupCommand implements TabExecutor {
@NotNull String label, @NotNull String[] args) {
if (!sender.hasPermission("mdbd.admin")) return List.of();
if (args.length == 1) {
return List.of("wand", "set", "build", "clear", "clear-spawns", "lobby").stream()
return List.of("wand", "set", "clear", "clear-spawns", "lobby").stream()
.filter(s -> s.startsWith(args[0].toLowerCase())).toList();
}
if (args.length == 2 && args[0].equalsIgnoreCase("set")) {