This commit is contained in:
loki5512344 2026-06-10 23:00:12 +02:00
parent 89dc9eb00d
commit 957ab4a5a2
149 changed files with 5379 additions and 3263 deletions

View file

@ -1,5 +1,6 @@
package dev.loki.config;
import dev.loki.core.GameSession;
import dev.loki.mdbd;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
@ -7,6 +8,8 @@ import org.bukkit.configuration.file.YamlConfiguration;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
public class ConfigManager {
@ -36,6 +39,22 @@ public class ConfigManager {
plugin.getLogger().info("Configs loaded successfully");
}
/**
* Reload all config files from disk and update all running game sessions with the new config.
*/
public void reloadConfigs() {
loadConfigs();
// Update all active GameSessions with the new GameConfig reference
if (plugin.getGameManager() != null) {
for (GameSession session : plugin.getGameManager().getAllSessions()) {
if (session != null) {
session.refreshConfig();
}
}
}
plugin.getLogger().info("Configs reloaded and applied to all running sessions");
}
public void saveAllConfigs() {
saveConfig("locations.yml", locationsConfig.getConfig());
saveConfig("game.yml", gameConfig.getConfig());
@ -48,7 +67,13 @@ public class ConfigManager {
if (!file.exists()) {
createDefaultConfig(fileName, file);
}
return YamlConfiguration.loadConfiguration(file);
try (var reader = new InputStreamReader(
Files.newInputStream(file.toPath()), StandardCharsets.UTF_8)) {
return YamlConfiguration.loadConfiguration(reader);
} catch (IOException e) {
plugin.getLogger().warning("Could not read config UTF-8, fallback: " + fileName);
return YamlConfiguration.loadConfiguration(file);
}
}
private void createDefaultConfig(String resourceName, File targetFile) {

View file

@ -17,7 +17,7 @@ public class GameConfig {
setDefault("lobby.countdown-4-players", 45);
setDefault("lobby.countdown-5-players", 15);
setDefault("lobby.role-assign-delay", 5);
setDefault("lobby.server-lobby-count", 3);
setDefault("lobby.server-lobby-count", 10);
setDefault("lobby.persist-lobbies", true);
// Players scale
@ -42,7 +42,7 @@ public class GameConfig {
setDefault("hooks.struggle-interval", 2);
setDefault("hooks.max-hooks", 3);
setDefault("hooks.interaction-range", 3.0);
setDefault("hooks.rescue-seconds", 3);
setDefault("hooks.rescue-seconds", 2);
setDefault("hooks.sabotage-seconds", 10);
setDefault("hooks.sabotage-recovery-seconds", 60);
@ -64,17 +64,14 @@ public class GameConfig {
setDefault("healing.heal-range", 3.0);
// Carry / escape
setDefault("carry.escape-jumps", 15);
setDefault("carry.escape-shift-presses", 50);
setDefault("carry.escape-speed-seconds", 3);
setDefault("carry.jump-fill-percent", 6.67);
// Killer
setDefault("killer.attack-cooldown", 2.5);
setDefault("killer.post-hit-cooldown", 4.0);
setDefault("killer.attack-range", 3.0);
setDefault("killer.pallet-stun-seconds", 2.0);
setDefault("killer.pallet-break-seconds", 3.0);
setDefault("killer.terror-radius", 32);
setDefault("killer.pallet-interaction-range", 3.0);
// BossBar
setDefault("bossbar.enabled", true);
@ -84,15 +81,8 @@ public class GameConfig {
setDefault("bossbar.appear-range", 32);
setDefault("bossbar.full-range", 8);
// Window vault
setDefault("window.vault-survivor-fast", 1.0);
setDefault("window.vault-survivor-slow", 1.5);
setDefault("window.vault-killer", 1.5);
setDefault("window.vault-block-seconds", 15);
// Endgame
setDefault("endgame.gate-open-seconds", 20);
setDefault("endgame.collapse-seconds", 120);
setDefault("endgame.chest-open-seconds", 3);
setDefault("endgame.gate-interaction-range", 3.0);
@ -115,6 +105,31 @@ public class GameConfig {
// Hatch
setDefault("hatch.min-survivors-for-open", 1);
setDefault("hatch.require-more-gens-than-survivors", true);
// Block types for spawning
setDefault("blocks.generator", "BLAST_FURNACE");
setDefault("blocks.chest", "CHEST");
setDefault("blocks.hook", "IRON_BARS");
// Holograms
setDefault("holograms.enabled", true);
setDefault("holograms.generator.enabled", true);
setDefault("holograms.generator.height-offset", -1.0);
setDefault("holograms.hook.enabled", true);
setDefault("holograms.hook.height-offset", 0.0);
setDefault("holograms.chest.enabled", true);
setDefault("holograms.chest.height-offset", 0.0);
// Skill check
setDefault("skillcheck.chance", 0.005);
setDefault("skillcheck.win-percent", 5.0);
setDefault("skillcheck.loss-percent", 10.0);
setDefault("skillcheck.success-block", "GREEN_STAINED_GLASS_PANE");
setDefault("skillcheck.fail-block", "RED_STAINED_GLASS_PANE");
setDefault("skillcheck.neutral-block", "GRAY_STAINED_GLASS_PANE");
// Chest loot
setDefault("chest.loot-reset-minutes", 3.0);
}
private void setDefault(String path, Object value) {
@ -193,19 +208,41 @@ public class GameConfig {
// -------------------------------------------------------------------------
// Carry / Escape
// -------------------------------------------------------------------------
public int getCarryEscapeJumps() { return config.getInt("carry.escape-jumps", 15); }
/** Number of Shift presses required to escape from killer's carry (default 50). */
public int getCarryEscapePresses() { return config.getInt("carry.escape-shift-presses", 50); }
public int getCarryEscapeSpeedSeconds() { return config.getInt("carry.escape-speed-seconds", 3); }
public double getCarryJumpFillPercent() { return config.getDouble("carry.jump-fill-percent", 6.67); }
// -------------------------------------------------------------------------
// Killer
// -------------------------------------------------------------------------
public int getAttackCooldown() { return (int)(config.getDouble("killer.attack-cooldown", 2.5) * 20); }
/** Кулдаун после ПОПАДАНИЯ по игроку (3–5 сек). Отличается от swing cooldown. */
public int getPostHitCooldown() { return (int)(config.getDouble("killer.post-hit-cooldown", 4.0) * 20); }
public double getAttackRange() { return config.getDouble("killer.attack-range", 3.0); }
public int getPalletStunTicks() { return (int)(config.getDouble("killer.pallet-stun-seconds", 2.0) * 20); }
public int getPalletBreakTicks() { return (int)(config.getDouble("killer.pallet-break-seconds", 3.0) * 20); }
public int getTerrorRadius() { return config.getInt("killer.terror-radius", 32); }
public double getPalletInteractionRange() { return config.getDouble("killer.pallet-interaction-range", 3.0); }
// -------------------------------------------------------------------------
// Block types (какие блоки спавнятся на местах объектов)
// -------------------------------------------------------------------------
public String getGeneratorBlock() { return config.getString("blocks.generator", "BLAST_FURNACE"); }
public String getChestBlock() { return config.getString("blocks.chest", "CHEST"); }
public String getHookBlock() { return config.getString("blocks.hook", "IRON_BARS"); }
// -------------------------------------------------------------------------
// Holograms
// -------------------------------------------------------------------------
public boolean isHologramsEnabled() { return config.getBoolean("holograms.enabled", true); }
public boolean isGeneratorHologramEnabled() { return config.getBoolean("holograms.generator.enabled", true); }
public double getGeneratorHologramHeightOffset() { return config.getDouble("holograms.generator.height-offset", -1.0); }
public boolean isHookHologramEnabled() { return config.getBoolean("holograms.hook.enabled", true); }
public double getHookHologramHeightOffset() { return config.getDouble("holograms.hook.height-offset", 0.0); }
public boolean isChestHologramEnabled() { return config.getBoolean("holograms.chest.enabled", true); }
public double getChestHologramHeightOffset() { return config.getDouble("holograms.chest.height-offset", 0.0); }
// -------------------------------------------------------------------------
// Chest loot
// -------------------------------------------------------------------------
public double getChestLootResetMinutes() { return config.getDouble("chest.loot-reset-minutes", 3.0); }
// -------------------------------------------------------------------------
// BossBar
@ -217,19 +254,10 @@ public class GameConfig {
public int getBossBarAppearRange() { return config.getInt("bossbar.appear-range", 32); }
public int getBossBarFullRange() { return config.getInt("bossbar.full-range", 8); }
// -------------------------------------------------------------------------
// Window
// -------------------------------------------------------------------------
public int getVaultSurvivorFastTicks() { return (int)(config.getDouble("window.vault-survivor-fast", 1.0) * 20); }
public int getVaultSurvivorSlowTicks() { return (int)(config.getDouble("window.vault-survivor-slow", 1.5) * 20); }
public int getVaultKillerTicks() { return (int)(config.getDouble("window.vault-killer", 1.5) * 20); }
public int getVaultBlockSeconds() { return config.getInt("window.vault-block-seconds", 15); }
// -------------------------------------------------------------------------
// Endgame
// -------------------------------------------------------------------------
public int getGateOpenSeconds() { return config.getInt("endgame.gate-open-seconds", 20); }
public int getCollapseSeconds() { return config.getInt("endgame.collapse-seconds", 120); }
public int getChestOpenSeconds() { return config.getInt("endgame.chest-open-seconds", 3); }
public double getGateInteractionRange() { return config.getDouble("endgame.gate-interaction-range", 3.0); }
@ -248,5 +276,15 @@ public class GameConfig {
public String getColorHealing() { return config.getString("scoreboard.color-healing", "GREEN"); }
public String getColorHooked() { return config.getString("scoreboard.color-hooked", "DARK_RED"); }
// -------------------------------------------------------------------------
// Skill check
// -------------------------------------------------------------------------
public double getSkillCheckChance() { return config.getDouble("skillcheck.chance", 0.005); }
public double getSkillCheckWinPercent() { return config.getDouble("skillcheck.win-percent", 5.0); }
public double getSkillCheckLossPercent() { return config.getDouble("skillcheck.loss-percent", 10.0); }
public String getSkillCheckSuccessBlock() { return config.getString("skillcheck.success-block", "GREEN_STAINED_GLASS_PANE"); }
public String getSkillCheckFailBlock() { return config.getString("skillcheck.fail-block", "RED_STAINED_GLASS_PANE"); }
public String getSkillCheckNeutralBlock() { return config.getString("skillcheck.neutral-block", "GRAY_STAINED_GLASS_PANE"); }
public FileConfiguration getConfig() { return config; }
}

View file

@ -25,31 +25,27 @@ public class LocationsConfig {
config.set("lobby.yaw", 0.0);
config.set("lobby.pitch", 0.0);
}
if (!config.contains("generators")) {
// Безопасная проверка: contains() может вернуть true для пустого списка,
// поэтому также проверяем что getList() не null
if (!config.contains("generators") || config.getList("generators") == null) {
config.set("generators", new ArrayList<>());
}
if (!config.contains("hooks")) {
if (!config.contains("hooks") || config.getList("hooks") == null) {
config.set("hooks", new ArrayList<>());
}
if (!config.contains("pallets")) {
config.set("pallets", new ArrayList<>());
}
if (!config.contains("windows")) {
if (!config.contains("windows") || config.getList("windows") == null) {
config.set("windows", new ArrayList<>());
}
if (!config.contains("chests")) {
if (!config.contains("chests") || config.getList("chests") == null) {
config.set("chests", new ArrayList<>());
}
if (!config.contains("totems")) {
config.set("totems", new ArrayList<>());
}
if (!config.contains("exitgates")) {
if (!config.contains("exitgates") || config.getList("exitgates") == null) {
config.set("exitgates", new ArrayList<>());
}
if (!config.contains("killer-spawn")) {
if (!config.contains("killer-spawn") || config.getList("killer-spawn") == null) {
config.set("killer-spawn", new ArrayList<>());
}
if (!config.contains("survivor-spawns")) {
if (!config.contains("survivor-spawns") || config.getList("survivor-spawns") == null) {
config.set("survivor-spawns", new ArrayList<>());
}
}
@ -84,10 +80,6 @@ public class LocationsConfig {
return loadLocationList("hooks");
}
public List<Location> getPallets() {
return loadLocationList("pallets");
}
public List<Location> getWindows() {
return loadLocationList("windows");
}
@ -100,8 +92,10 @@ public class LocationsConfig {
return loadLocationList("totems");
}
public List<Location> getExitGates() {
return loadLocationList("exitgates");
// getExitGates() — определён в addLocation/setGate блоке
public List<Location> getKillerSpawns() {
return loadLocationList("killer-spawn");
}
public Location getKillerSpawn() {
@ -109,39 +103,35 @@ public class LocationsConfig {
return spawns.isEmpty() ? null : spawns.get(0);
}
public void setKillerSpawn(Location loc) {
// killer-spawn is a single location stored as a list with one entry
List<String> list = new java.util.ArrayList<>();
list.add(String.format("%s,%.2f,%.2f,%.2f,%.1f,%.1f",
loc.getWorld().getName(),
loc.getX(), loc.getY(), loc.getZ(),
loc.getYaw(), loc.getPitch()));
config.set("killer-spawn", list);
}
public List<Location> getSurvivorSpawns() {
return loadLocationList("survivor-spawns");
}
private List<Location> loadLocationList(String path) {
List<Location> locations = new ArrayList<>();
List<?> list = config.getList(path);
if (list == null) return locations;
List<String> list = safeGetStringList(path);
if (list.isEmpty()) return locations;
for (Object obj : list) {
if (obj instanceof String) {
Location loc = parseLocation((String) obj);
if (loc != null) locations.add(loc);
}
for (String s : list) {
Location loc = parseLocation(s);
if (loc != null) locations.add(loc);
}
return locations;
}
private Location parseLocation(String str) {
String[] parts = str.split(",");
// 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) return null;
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());
@ -155,15 +145,106 @@ public class LocationsConfig {
}
public void addLocation(String path, Location loc) {
List<String> list = config.getStringList(path);
list.add(String.format("%s,%.2f,%.2f,%.2f,%.1f,%.1f",
List<String> 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<String> list = safeGetStringList(path);
while (list.size() <= index) {
list.add("");
}
list.set(index, locationToString(loc));
config.set(path, list);
}
/** Безопасная загрузка StringList: использует getList с fallback на toString(). */
private List<String> safeGetStringList(String path) {
List<String> 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<String> 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<Location> getExitGates() {
// Пробуем новый формат gate-a / gate-b
List<Location> 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()));
config.set(path, list);
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));
}
}
}

View file

@ -49,10 +49,6 @@ public class MessagesConfig {
setDefault("survivor.heal.complete", "&#00FF00Вылечено!");
setDefault("survivor.heal.interrupt", "&#FFD700Лечение прервано!");
setDefault("survivor.sprint.exhausted", "&#FF8C00Вы устали...");
setDefault("survivor.pallet.drop", "&#DEB887Вы бросили паллет!");
setDefault("survivor.pallet.stun", "&#FFD700Киллер оглушен!");
setDefault("survivor.vault.fast", "&#00CED1Быстрый прыжок!");
setDefault("survivor.vault.slow", "&#87CEEBМедленный прыжок...");
// Hook messages
setDefault("hook.stage1", "&#FFFF00Крюк: Фаза 1");
@ -66,16 +62,11 @@ public class MessagesConfig {
// Item messages
setDefault("item.found", "&#FFD700Найдено: %s");
setDefault("item.medkit.use", "&#00FA9AИспользуете аптечку...");
setDefault("item.flashlight.blind", "&#FFFF00Ослепили киллера!");
setDefault("item.key.hatch", "&#00CED1Ключ открывает люк!");
setDefault("item.map.reveal", "&#87CEEBОбъекты показаны на карте");
// Endgame
setDefault("endgame.gates.powered", "&#00FF00Ворота запитаны!");
setDefault("endgame.gates.opening", "&#FFD700Открываете ворота...");
setDefault("endgame.hatch.open", "&#4B0082Люк открыт!");
setDefault("endgame.collapse.warning", "&#FF0000⚠ КОЛЛАПС ЧЕРЕЗ %s СЕК!");
setDefault("endgame.collapse.start", "&#8B0000КОЛЛАПС НАЧАЛСЯ!");
setDefault("endgame.escape", "&#00FF00✓ ПОБЕГ!");
// Error messages