532 lines
23 KiB
Java
532 lines
23 KiB
Java
package dev.loki.command;
|
||
|
||
import dev.loki.mdbd;
|
||
import dev.loki.util.SpawnUtil;
|
||
import dev.loki.util.TextUtil;
|
||
import dev.loki.config.LocationsConfig;
|
||
import dev.loki.config.MessagesConfig;
|
||
import org.bukkit.Location;
|
||
import org.bukkit.Material;
|
||
import org.bukkit.command.Command;
|
||
import org.bukkit.command.CommandSender;
|
||
import org.bukkit.command.TabExecutor;
|
||
import org.bukkit.entity.Player;
|
||
import org.bukkit.inventory.ItemStack;
|
||
import org.jetbrains.annotations.NotNull;
|
||
import org.jetbrains.annotations.Nullable;
|
||
|
||
import java.util.*;
|
||
|
||
/**
|
||
* /dbdsetup — настройка карты.
|
||
*
|
||
* Система:
|
||
* 1. Ванд: /dbdsetup wand (ЛКМ=pos1, ПКМ=pos2 — для ворот)
|
||
* 2. Обычные объекты: /dbdsetup set <тип> [номер] → сломай блок
|
||
* 3. Ворота: /dbdsetup set gate A|B (из pos1/pos2 ванда)
|
||
*
|
||
* Типы: hook, chest, generator, killer-spawn, survivor-spawn, gate
|
||
* Для спавнов можно указать номер: /dbdsetup set killer-spawn 0
|
||
*/
|
||
public class SetupCommand implements TabExecutor {
|
||
private final mdbd plugin;
|
||
|
||
// Wand positions for gates (LMB=pos1, RMB=pos2)
|
||
private final Map<UUID, Location> pos1 = new HashMap<>();
|
||
private final Map<UUID, Location> pos2 = new HashMap<>();
|
||
|
||
// Setup mode data: mode + optional spawn index
|
||
private static final class SetupData {
|
||
final String mode;
|
||
int spawnIndex; // -1 means "not specified, append"; resets to -1 after first use
|
||
|
||
SetupData(String mode, int spawnIndex) {
|
||
this.mode = mode;
|
||
this.spawnIndex = spawnIndex;
|
||
}
|
||
}
|
||
private final Map<UUID, SetupData> setupMode = new HashMap<>();
|
||
|
||
public SetupCommand(mdbd plugin) {
|
||
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(messages().get("cmd.no-permission"));
|
||
return true;
|
||
}
|
||
if (!(sender instanceof Player player)) {
|
||
sender.sendMessage(messages().get("cmd.players-only"));
|
||
return true;
|
||
}
|
||
if (args.length == 0) { sendUsage(sender); return true; }
|
||
|
||
return switch (args[0].toLowerCase()) {
|
||
case "wand" -> giveWand(player);
|
||
case "set" -> handleSet(player, args);
|
||
case "clear" -> handleClear(player, args);
|
||
case "delete" -> handleDelete(player, args);
|
||
case "clear-spawns" -> clearSpawns(player);
|
||
case "lobby" -> setLobby(player);
|
||
default -> { sendUsage(sender); yield true; }
|
||
};
|
||
}
|
||
|
||
// ── Wand ─────────────────────────────────────────────────────────────────
|
||
|
||
private boolean giveWand(Player player) {
|
||
ItemStack wand = new ItemStack(Material.WOODEN_AXE);
|
||
wand.editMeta(meta -> {
|
||
meta.displayName(TextUtil.color("<#FF6B00>DBD Setup Wand"));
|
||
meta.lore(List.of(
|
||
TextUtil.color("<#FFD700>ЛКМ по блоку — Точка 1 (для ворот)"),
|
||
TextUtil.color("<#FFD700>ПКМ по блоку — Точка 2 (для ворот)"),
|
||
TextUtil.color("<gray>Для остального: /dbdsetup set <тип> и ломай блок")
|
||
));
|
||
var key = new org.bukkit.NamespacedKey(plugin, "dbd_wand");
|
||
meta.getPersistentDataContainer().set(key,
|
||
org.bukkit.persistence.PersistentDataType.BYTE, (byte) 1);
|
||
});
|
||
player.getInventory().addItem(wand);
|
||
player.sendMessage(messages().get("setup.wand-given"));
|
||
return true;
|
||
}
|
||
|
||
// ── Set mode ──────────────────────────────────────────────────────────────
|
||
|
||
private boolean handleSet(Player player, String[] args) {
|
||
if (args.length < 2) {
|
||
player.sendMessage(messages().get("setup.set-usage"));
|
||
player.sendMessage(messages().get("setup.set-types"));
|
||
player.sendMessage(messages().get("setup.set-spawn-info"));
|
||
return true;
|
||
}
|
||
|
||
String type = args[1].toLowerCase();
|
||
UUID uuid = player.getUniqueId();
|
||
|
||
if (type.equals("gate")) {
|
||
if (args.length < 3) {
|
||
player.sendMessage(messages().get("setup.gate-usage"));
|
||
return true;
|
||
}
|
||
String gateId = args[2].toUpperCase();
|
||
if (!gateId.equals("A") && !gateId.equals("B")) {
|
||
player.sendMessage(messages().get("setup.gate-invalid"));
|
||
return true;
|
||
}
|
||
return placeGate(player, gateId);
|
||
}
|
||
|
||
String mode = switch (type) {
|
||
case "killer_spawn" -> "killer-spawn";
|
||
case "survivor_spawn" -> "survivor-spawn";
|
||
default -> type;
|
||
};
|
||
|
||
if (!List.of("hook", "chest", "generator", "killer-spawn", "survivor-spawn").contains(mode)) {
|
||
player.sendMessage(messages().get("setup.unknown-type", type));
|
||
return true;
|
||
}
|
||
|
||
// Parse optional spawn index
|
||
int spawnIndex = -1;
|
||
if (args.length >= 3 && (mode.equals("killer-spawn") || mode.equals("survivor-spawn"))) {
|
||
try {
|
||
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(messages().get("setup.invalid-number", args[2]));
|
||
return true;
|
||
}
|
||
}
|
||
|
||
// Toggle: если уже в этом режиме с тем же типом — выключаем
|
||
SetupData currentData = setupMode.get(uuid);
|
||
if (currentData != null && mode.equals(currentData.mode)) {
|
||
setupMode.remove(uuid);
|
||
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(messages().get("setup.mode-on-spawn", mode, indexMsg));
|
||
} else {
|
||
player.sendMessage(messages().get("setup.mode-on", mode, indexMsg));
|
||
}
|
||
return true;
|
||
}
|
||
|
||
private boolean placeGate(Player player, String gateId) {
|
||
Location p1 = pos1.get(player.getUniqueId());
|
||
Location p2 = pos2.get(player.getUniqueId());
|
||
|
||
if (p1 == null || p2 == null) {
|
||
player.sendMessage(messages().get("setup.gate-need-points"));
|
||
return true;
|
||
}
|
||
|
||
if (!p1.getWorld().equals(p2.getWorld())) {
|
||
player.sendMessage(messages().get("setup.gate-same-world"));
|
||
return true;
|
||
}
|
||
|
||
Location center = new Location(
|
||
p1.getWorld(),
|
||
(p1.getX() + p2.getX()) / 2.0,
|
||
Math.min(p1.getY(), p2.getY()),
|
||
(p1.getZ() + p2.getZ()) / 2.0,
|
||
player.getLocation().getYaw(),
|
||
0f
|
||
);
|
||
|
||
var locations = plugin.getConfigManager().getLocations();
|
||
locations.setGate(gateId, center);
|
||
locations.setGateZone(gateId, p1, p2);
|
||
plugin.getConfigManager().saveConfig("locations.yml", locations.getConfig());
|
||
|
||
player.sendMessage(messages().get("setup.gate-placed", gateId, formatLoc(center)));
|
||
return true;
|
||
}
|
||
|
||
// ── Block break callback ───────────────────────────────────────────────────
|
||
|
||
/**
|
||
* Called when a block is broken in setup mode.
|
||
* Saves the location and restores the original block.
|
||
*
|
||
* @param player The player who broke the block
|
||
* @param blockLoc Location of the broken block
|
||
* @param restoreBlock The material to restore the block to (null = don't restore)
|
||
*/
|
||
public void onBlockBroken(Player player, Location blockLoc, Material restoreBlock) {
|
||
UUID uuid = player.getUniqueId();
|
||
SetupData data = setupMode.get(uuid);
|
||
if (data == null) return;
|
||
|
||
String mode = data.mode;
|
||
var locations = plugin.getConfigManager().getLocations();
|
||
Location loc = SpawnUtil.blockBreakSpawn(blockLoc, player.getLocation().getYaw());
|
||
|
||
switch (mode) {
|
||
case "hook" -> {
|
||
Location hLoc = blockLoc.clone().add(0.5, 0, 0.5);
|
||
if (locations.hasLocation("hooks", hLoc)) {
|
||
player.sendMessage(messages().get("setup.duplicate"));
|
||
break;
|
||
}
|
||
locations.addLocation("hooks", hLoc);
|
||
player.sendMessage(messages().get("setup.hook-placed", formatLoc(loc)));
|
||
}
|
||
case "chest" -> {
|
||
Location cLoc = blockLoc.clone().add(0.5, 0, 0.5);
|
||
if (locations.hasLocation("chests", cLoc)) {
|
||
player.sendMessage(messages().get("setup.duplicate"));
|
||
break;
|
||
}
|
||
locations.addLocation("chests", cLoc);
|
||
player.sendMessage(messages().get("setup.chest-placed", formatLoc(loc)));
|
||
}
|
||
case "generator" -> {
|
||
Location gLoc = blockLoc.clone().add(0.5, 0, 0.5);
|
||
if (locations.hasLocation("generators", gLoc)) {
|
||
player.sendMessage(messages().get("setup.duplicate"));
|
||
break;
|
||
}
|
||
locations.addLocation("generators", gLoc);
|
||
plugin.getGeneratorData().addGenerator(gLoc);
|
||
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(messages().get("setup.killer-spawn-placed", formatLoc(loc)));
|
||
data.spawnIndex = -1;
|
||
}
|
||
case "survivor-spawn" -> {
|
||
int slot = data.spawnIndex >= 1 ? data.spawnIndex : locations.firstFreeSurvivorSlot();
|
||
if (slot == -1) {
|
||
player.sendMessage(messages().get("setup.survivor-spawn-limit"));
|
||
break;
|
||
}
|
||
locations.setSurvivorSpawn(slot, loc);
|
||
int newCount = locations.getSurvivorSpawns().size();
|
||
String label = switch (slot) {
|
||
case 1 -> "первого";
|
||
case 2 -> "второго";
|
||
case 3 -> "третьего";
|
||
case 4 -> "четвёртого";
|
||
default -> "#" + slot;
|
||
};
|
||
player.sendMessage(messages().get("setup.survivor-spawn-placed", label, newCount, formatLoc(loc)));
|
||
data.spawnIndex = -1;
|
||
}
|
||
}
|
||
|
||
plugin.getConfigManager().saveConfig("locations.yml", locations.getConfig());
|
||
|
||
// Restore the block to its original material + send block update to client
|
||
if (restoreBlock != null) {
|
||
Material finalRestore = restoreBlock;
|
||
plugin.getServer().getScheduler().runTask(plugin, () -> {
|
||
blockLoc.getBlock().setType(finalRestore, false);
|
||
player.sendBlockChange(blockLoc, finalRestore.createBlockData());
|
||
});
|
||
}
|
||
// НЕ выходим из режима — чтобы можно было установить несколько подряд
|
||
}
|
||
|
||
// ── 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(messages().get("setup.wand-point", num, formatLoc(loc)));
|
||
}
|
||
|
||
public boolean isInSetupMode(UUID uuid) {
|
||
return setupMode.containsKey(uuid);
|
||
}
|
||
|
||
public String getSetupMode(UUID uuid) {
|
||
SetupData data = setupMode.get(uuid);
|
||
return data != null ? data.mode : null;
|
||
}
|
||
|
||
/**
|
||
* Returns the spawn index for the current setup mode, or -1 if not specified.
|
||
*/
|
||
public int getSpawnIndex(UUID uuid) {
|
||
SetupData data = setupMode.get(uuid);
|
||
return data != null ? data.spawnIndex : -1;
|
||
}
|
||
|
||
// ── Clear / lobby ─────────────────────────────────────────────────
|
||
|
||
private boolean clearMap(Player player) {
|
||
plugin.getMapBuilder().clearMap(plugin.getConfigManager().getLocations());
|
||
player.sendMessage(messages().get("setup.map-cleared"));
|
||
return true;
|
||
}
|
||
|
||
// ── Clear ────────────────────────────────────────────────────────
|
||
|
||
private boolean handleClear(Player player, String[] args) {
|
||
var locations = plugin.getConfigManager().getLocations();
|
||
|
||
if (args.length >= 2) {
|
||
String type = args[1].toLowerCase();
|
||
return clearObjectsByType(player, locations, type);
|
||
}
|
||
|
||
// Clear ALL object data from config (no block destruction)
|
||
locations.clearLocationList("generators");
|
||
locations.clearLocationList("hooks");
|
||
locations.clearLocationList("chests");
|
||
locations.clearLocationList("killer-spawn");
|
||
for (String id : List.of("a", "b")) {
|
||
locations.getConfig().set("gate-" + id, null);
|
||
locations.getConfig().set("gate-" + id + "-corner1", null);
|
||
locations.getConfig().set("gate-" + id + "-corner2", null);
|
||
}
|
||
locations.clearSurvivorSpawns();
|
||
plugin.getGeneratorData().clearGenerators();
|
||
plugin.getConfigManager().saveConfig("locations.yml", locations.getConfig());
|
||
player.sendMessage(messages().get("setup.cleared"));
|
||
return true;
|
||
}
|
||
|
||
private boolean clearObjectsByType(Player player, LocationsConfig locations, String type) {
|
||
String path = switch (type) {
|
||
case "generator" -> "generators";
|
||
case "hook" -> "hooks";
|
||
case "chest" -> "chests";
|
||
case "gate" -> null;
|
||
case "killer-spawn" -> "killer-spawn";
|
||
case "survivor-spawn" -> null;
|
||
default -> { player.sendMessage(messages().get("setup.unknown-type", type)); yield null; }
|
||
};
|
||
if (path == null) {
|
||
if (type.equals("gate")) {
|
||
for (String id : List.of("a", "b")) {
|
||
locations.getConfig().set("gate-" + id, null);
|
||
locations.getConfig().set("gate-" + id + "-corner1", null);
|
||
locations.getConfig().set("gate-" + id + "-corner2", null);
|
||
}
|
||
} else if (type.equals("survivor-spawn")) {
|
||
locations.clearSurvivorSpawns();
|
||
} else {
|
||
return true;
|
||
}
|
||
} else {
|
||
locations.clearLocationList(path);
|
||
if (type.equals("generator")) {
|
||
plugin.getGeneratorData().clearGenerators();
|
||
}
|
||
}
|
||
plugin.getConfigManager().saveConfig("locations.yml", locations.getConfig());
|
||
player.sendMessage(messages().get("setup.cleared-type", type));
|
||
return true;
|
||
}
|
||
|
||
// ── Delete ───────────────────────────────────────────────────────
|
||
|
||
private boolean handleDelete(Player player, String[] args) {
|
||
if (args.length < 3) {
|
||
player.sendMessage(messages().get("setup.delete-usage"));
|
||
return true;
|
||
}
|
||
|
||
String type = args[1].toLowerCase();
|
||
int index;
|
||
try {
|
||
index = Integer.parseInt(args[2]);
|
||
} catch (NumberFormatException e) {
|
||
player.sendMessage(messages().get("setup.invalid-number", args[2]));
|
||
return true;
|
||
}
|
||
|
||
var locations = plugin.getConfigManager().getLocations();
|
||
|
||
switch (type) {
|
||
case "generator" -> {
|
||
var gens = locations.getGenerators();
|
||
if (index < 0 || index >= gens.size()) {
|
||
player.sendMessage(messages().get("setup.out-of-range", index));
|
||
return true;
|
||
}
|
||
locations.removeLocationAtIndex("generators", index);
|
||
plugin.getGeneratorData().removeGenerator(index);
|
||
player.sendMessage(messages().get("setup.deleted", "generator #" + index));
|
||
}
|
||
case "hook" -> {
|
||
var hooks = locations.getHooks();
|
||
if (index < 0 || index >= hooks.size()) {
|
||
player.sendMessage(messages().get("setup.out-of-range", index));
|
||
return true;
|
||
}
|
||
locations.removeLocationAtIndex("hooks", index);
|
||
player.sendMessage(messages().get("setup.deleted", "hook #" + index));
|
||
}
|
||
case "chest" -> {
|
||
var chests = locations.getChests();
|
||
if (index < 0 || index >= chests.size()) {
|
||
player.sendMessage(messages().get("setup.out-of-range", index));
|
||
return true;
|
||
}
|
||
locations.removeLocationAtIndex("chests", index);
|
||
player.sendMessage(messages().get("setup.deleted", "chest #" + index));
|
||
}
|
||
case "killer-spawn" -> {
|
||
locations.clearLocationList("killer-spawn");
|
||
player.sendMessage(messages().get("setup.deleted", "killer-spawn"));
|
||
}
|
||
case "survivor-spawn" -> {
|
||
if (index < 1 || index > 4) {
|
||
player.sendMessage(messages().get("setup.out-of-range", index));
|
||
return true;
|
||
}
|
||
locations.clearSurvivorSpawn(index);
|
||
player.sendMessage(messages().get("setup.deleted", "survivor-spawn #" + index));
|
||
}
|
||
case "gate" -> {
|
||
String id = index == 1 ? "a" : index == 2 ? "b" : null;
|
||
if (id == null) {
|
||
player.sendMessage(messages().get("setup.out-of-range", index));
|
||
return true;
|
||
}
|
||
locations.getConfig().set("gate-" + id, null);
|
||
locations.getConfig().set("gate-" + id + "-corner1", null);
|
||
locations.getConfig().set("gate-" + id + "-corner2", null);
|
||
player.sendMessage(messages().get("setup.deleted", "gate-" + id));
|
||
}
|
||
default -> {
|
||
player.sendMessage(messages().get("setup.unknown-type", type));
|
||
return true;
|
||
}
|
||
}
|
||
|
||
plugin.getConfigManager().saveConfig("locations.yml", locations.getConfig());
|
||
return true;
|
||
}
|
||
|
||
private boolean clearSpawns(Player player) {
|
||
var locations = plugin.getConfigManager().getLocations();
|
||
locations.clearLocationList("killer-spawn");
|
||
locations.clearSurvivorSpawns();
|
||
plugin.getConfigManager().saveConfig("locations.yml", locations.getConfig());
|
||
player.sendMessage(messages().get("setup.spawns-cleared"));
|
||
return true;
|
||
}
|
||
|
||
private boolean setLobby(Player player) {
|
||
plugin.getConfigManager().getLocations().setLobby(player.getLocation());
|
||
plugin.getConfigManager().saveConfig("locations.yml",
|
||
plugin.getConfigManager().getLocations().getConfig());
|
||
player.sendMessage(messages().get("setup.lobby-set", formatLoc(player.getLocation())));
|
||
return true;
|
||
}
|
||
|
||
// ── Usage / helpers ───────────────────────────────────────────────────────
|
||
|
||
private void sendUsage(CommandSender sender) {
|
||
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"));
|
||
sender.sendMessage(messages().get("usage.setup-delete"));
|
||
sender.sendMessage(messages().get("usage.setup-clear-spawns"));
|
||
sender.sendMessage(messages().get("usage.setup-lobby"));
|
||
}
|
||
|
||
private String formatLoc(Location loc) {
|
||
return String.format("[%s, %.1f, %.1f, %.1f]",
|
||
loc.getWorld().getName(), loc.getX(), loc.getY(), loc.getZ());
|
||
}
|
||
|
||
@Override
|
||
public @Nullable List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command,
|
||
@NotNull String label, @NotNull String[] args) {
|
||
if (!sender.hasPermission("mdbd.admin")) return List.of();
|
||
if (args.length == 1) {
|
||
return List.of("wand", "set", "clear", "delete", "clear-spawns", "lobby").stream()
|
||
.filter(s -> s.startsWith(args[0].toLowerCase())).toList();
|
||
}
|
||
if (args.length == 2) {
|
||
if (args[0].equalsIgnoreCase("set") || args[0].equalsIgnoreCase("clear") || args[0].equalsIgnoreCase("delete")) {
|
||
return List.of("generator", "hook", "chest", "killer-spawn", "survivor-spawn", "gate").stream()
|
||
.filter(s -> s.startsWith(args[1].toLowerCase())).toList();
|
||
}
|
||
}
|
||
return List.of();
|
||
}
|
||
}
|