package dev.loki.config; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.World; import org.bukkit.configuration.file.FileConfiguration; import java.util.ArrayList; import java.util.List; public class LocationsConfig { private final FileConfiguration config; public LocationsConfig(FileConfiguration config) { this.config = config; setDefaults(); } private void setDefaults() { if (!config.contains("lobby")) { config.set("lobby.world", "world"); config.set("lobby.x", 0.0); config.set("lobby.y", 100.0); config.set("lobby.z", 0.0); config.set("lobby.yaw", 0.0); config.set("lobby.pitch", 0.0); } // Безопасная проверка: contains() может вернуть true для пустого списка, // поэтому также проверяем что getList() не null if (!config.contains("generators") || config.getList("generators") == null) { config.set("generators", new ArrayList<>()); } if (!config.contains("hooks") || config.getList("hooks") == null) { config.set("hooks", new ArrayList<>()); } if (!config.contains("windows") || config.getList("windows") == null) { config.set("windows", new ArrayList<>()); } if (!config.contains("chests") || config.getList("chests") == null) { config.set("chests", new ArrayList<>()); } if (!config.contains("exitgates") || config.getList("exitgates") == null) { config.set("exitgates", new ArrayList<>()); } if (!config.contains("killer-spawn") || config.getList("killer-spawn") == null) { config.set("killer-spawn", new ArrayList<>()); } if (!config.contains("survivor-spawns") || config.getList("survivor-spawns") == null) { config.set("survivor-spawns", new ArrayList<>()); } } public Location getLobby() { World world = Bukkit.getWorld(config.getString("lobby.world", "world")); if (world == null) return null; return new Location( world, config.getDouble("lobby.x", 0.0), config.getDouble("lobby.y", 100.0), config.getDouble("lobby.z", 0.0), (float) config.getDouble("lobby.yaw", 0.0), (float) config.getDouble("lobby.pitch", 0.0) ); } public void setLobby(Location loc) { config.set("lobby.world", loc.getWorld().getName()); config.set("lobby.x", loc.getX()); config.set("lobby.y", loc.getY()); config.set("lobby.z", loc.getZ()); config.set("lobby.yaw", loc.getYaw()); config.set("lobby.pitch", loc.getPitch()); } public List getGenerators() { return loadLocationList("generators"); } public List getHooks() { return loadLocationList("hooks"); } public List getWindows() { return loadLocationList("windows"); } public List getChests() { return loadLocationList("chests"); } public List getTotems() { return loadLocationList("totems"); } // getExitGates() — определён в addLocation/setGate блоке public List getKillerSpawns() { return loadLocationList("killer-spawn"); } public Location getKillerSpawn() { List spawns = loadLocationList("killer-spawn"); return spawns.isEmpty() ? null : spawns.get(0); } public List getSurvivorSpawns() { return loadLocationList("survivor-spawns"); } private List loadLocationList(String path) { List locations = new ArrayList<>(); List list = safeGetStringList(path); if (list.isEmpty()) return locations; for (String s : list) { Location loc = parseLocation(s); if (loc != null) locations.add(loc); } return locations; } private Location parseLocation(String str) { // Handle both old format (world,x,y,z,yaw,pitch) and new format (world|x|y|z|yaw|pitch) // New format uses | as separator to avoid issues with negative coordinates String[] parts = str.contains("|") ? str.split("\\|") : str.split(","); if (parts.length < 4) return null; World world = Bukkit.getWorld(parts[0].trim()); if (world == null) { // Try to find by first loaded world as fallback var loadedWorld = Bukkit.getWorlds().stream().findFirst(); if (loadedWorld.isEmpty()) return null; world = loadedWorld.get(); } try { double x = Double.parseDouble(parts[1].trim()); double y = Double.parseDouble(parts[2].trim()); double z = Double.parseDouble(parts[3].trim()); float yaw = parts.length > 4 ? Float.parseFloat(parts[4].trim()) : 0f; float pitch = parts.length > 5 ? Float.parseFloat(parts[5].trim()) : 0f; return new Location(world, x, y, z, yaw, pitch); } catch (NumberFormatException e) { return null; } } public void addLocation(String path, Location loc) { List list = safeGetStringList(path); list.add(locationToString(loc)); config.set(path, list); } /** * Sets a location at a specific index in the list. * If the index is beyond the current list size, pads with empty strings. */ public void setLocationAtIndex(String path, int index, Location loc) { List list = safeGetStringList(path); while (list.size() <= index) { list.add(""); } list.set(index, locationToString(loc)); config.set(path, list); } /** Безопасная загрузка StringList: использует getList с fallback на toString(). */ private List safeGetStringList(String path) { List result = new ArrayList<>(); List raw = config.getList(path); if (raw != null) { for (Object obj : raw) { if (obj instanceof String s) { result.add(s); } else if (obj != null) { result.add(obj.toString()); } } } return result; } /** Очистить список локаций (используется при загрузке карты). */ public void clearLocationList(String path) { config.set(path, new ArrayList<>()); } public void setSingleLocation(String path, Location loc) { List list = new ArrayList<>(); list.add(locationToString(loc)); config.set(path, list); } /** Устанавливает ворота A или B (хранятся как gate-a / gate-b). */ public void setGate(String id, Location loc) { String key = "gate-" + id.toLowerCase(); config.set(key, locationToString(loc)); } public Location getGate(String id) { String key = "gate-" + id.toLowerCase(); String str = config.getString(key); return str != null ? parseLocation(str) : null; } public List getExitGates() { // Пробуем новый формат gate-a / gate-b List gates = new ArrayList<>(); Location a = getGate("a"); Location b = getGate("b"); if (a != null) gates.add(a); if (b != null) gates.add(b); // Фолбэк: старый список exitgates if (gates.isEmpty()) { gates.addAll(loadLocationList("exitgates")); } return gates; } private String locationToString(Location loc) { // Use | as separator instead of , to avoid issues with negative coordinates in YAML return String.format("%s|%.2f|%.2f|%.2f|%.1f|%.1f", loc.getWorld().getName(), loc.getX(), loc.getY(), loc.getZ(), loc.getYaw(), loc.getPitch()); } public FileConfiguration getConfig() { return config; } /** * Reload locations from the given FileConfiguration (freshly loaded from disk). * This clears any stale in-memory state and re-applies defaults. */ /** Reload internal FileConfiguration reference (used after config reload from disk). */ public void reload(FileConfiguration newConfig) { // Replace defaults on new config if (!newConfig.contains("lobby")) { newConfig.set("lobby.world", "world"); newConfig.set("lobby.x", 0.0); newConfig.set("lobby.y", 100.0); newConfig.set("lobby.z", 0.0); newConfig.set("lobby.yaw", 0.0); newConfig.set("lobby.pitch", 0.0); } // Copy all data from new config to current config for (String key : newConfig.getKeys(true)) { config.set(key, newConfig.get(key)); } } }