From 52fd35d9de44e2ef14d2fad34f746e192462f4e9 Mon Sep 17 00:00:00 2001 From: M_Viper Date: Wed, 28 Jan 2026 22:06:29 +0100 Subject: [PATCH] Update from Git Manager GUI --- .../survivalmechanics/SurvivalMechanics.java | 162 +++++- .../mechanics/FatigueManager.java | 27 +- .../mechanics/HungerManager.java | 34 +- .../mechanics/HygieneManager.java | 32 +- .../mechanics/StaminaManager.java | 50 +- .../mechanics/TemperatureManager.java | 53 +- .../mechanics/ThirstManager.java | 22 +- .../mechanics/VulnerabilityManager.java | 46 +- src/main/resources/config.yml | 467 ++++++++++++------ src/main/resources/plugin.yml | 2 +- 10 files changed, 645 insertions(+), 250 deletions(-) diff --git a/src/main/java/me/viper/survivalmechanics/SurvivalMechanics.java b/src/main/java/me/viper/survivalmechanics/SurvivalMechanics.java index 67005f4..7019555 100644 --- a/src/main/java/me/viper/survivalmechanics/SurvivalMechanics.java +++ b/src/main/java/me/viper/survivalmechanics/SurvivalMechanics.java @@ -19,6 +19,8 @@ import org.bukkit.scoreboard.*; import java.io.File; import java.io.IOException; import java.util.UUID; +import java.util.HashMap; +import java.util.Map; public class SurvivalMechanics extends JavaPlugin implements Listener { private HungerManager hungerManager; @@ -31,14 +33,20 @@ public class SurvivalMechanics extends JavaPlugin implements Listener { private FileConfiguration langConfig; private FileConfiguration playerDataConfig; private File playerDataFile; + + // Better message cooldown management - store in memory instead of config file + private final Map lastMessageTimes = new HashMap<>(); @Override public void onEnable() { + // Load configurations saveDefaultConfig(); saveResource("lang.yml", false); saveResource("playerdata.yml", false); loadLangConfig(); loadPlayerDataConfig(); + + // Initialize managers hungerManager = new HungerManager(this); thirstManager = new ThirstManager(this); fatigueManager = new FatigueManager(this); @@ -46,6 +54,8 @@ public class SurvivalMechanics extends JavaPlugin implements Listener { staminaManager = new StaminaManager(this); vulnerabilityManager = new VulnerabilityManager(this); hygieneManager = new HygieneManager(this); + + // Start all tasks hungerManager.startHungerTask(); thirstManager.startThirstTask(); fatigueManager.startFatigueTask(); @@ -53,24 +63,39 @@ public class SurvivalMechanics extends JavaPlugin implements Listener { staminaManager.startStaminaTask(); vulnerabilityManager.startVulnerabilityTask(); hygieneManager.startHygieneTask(); + + // Start scoreboard display startScoreboardDisplay(); + + // Register events getServer().getPluginManager().registerEvents(this, this); + getLogger().info("SurvivalMechanics Plugin aktiviert!"); } @Override public void onDisable() { + // Save all player data before shutdown + for (Player player : getServer().getOnlinePlayers()) { + savePlayerData(player.getUniqueId()); + } savePlayerDataConfig(); getLogger().info("SurvivalMechanics Plugin deaktiviert!"); } private void loadLangConfig() { File langFile = new File(getDataFolder(), "lang.yml"); + if (!langFile.exists()) { + saveResource("lang.yml", false); + } langConfig = YamlConfiguration.loadConfiguration(langFile); } private void loadPlayerDataConfig() { playerDataFile = new File(getDataFolder(), "playerdata.yml"); + if (!playerDataFile.exists()) { + saveResource("playerdata.yml", false); + } playerDataConfig = YamlConfiguration.loadConfiguration(playerDataFile); } @@ -87,6 +112,8 @@ public class SurvivalMechanics extends JavaPlugin implements Listener { Player player = event.getPlayer(); UUID playerId = player.getUniqueId(); String path = "players." + playerId + "."; + + // Load player data or use defaults if (playerDataConfig.contains(path)) { hungerManager.loadHunger(playerId, playerDataConfig.getInt(path + "hunger", getConfig().getInt("hunger.initial", 80))); thirstManager.loadThirst(playerId, playerDataConfig.getInt(path + "thirst", getConfig().getInt("thirst.initial", 80))); @@ -95,7 +122,17 @@ public class SurvivalMechanics extends JavaPlugin implements Listener { staminaManager.loadStamina(playerId, playerDataConfig.getInt(path + "stamina", getConfig().getInt("stamina.initial", 80))); vulnerabilityManager.loadVulnerability(playerId, playerDataConfig.getInt(path + "vulnerability", getConfig().getInt("vulnerability.initial", 0))); hygieneManager.loadHygiene(playerId, playerDataConfig.getInt(path + "hygiene", getConfig().getInt("hygiene.initial", 80))); + } else { + // First time join - use initial values + hungerManager.loadHunger(playerId, getConfig().getInt("hunger.initial", 80)); + thirstManager.loadThirst(playerId, getConfig().getInt("thirst.initial", 80)); + fatigueManager.loadFatigue(playerId, getConfig().getInt("fatigue.initial", 100)); + temperatureManager.loadTemperature(playerId, getConfig().getInt("temperature.initial", 50)); + staminaManager.loadStamina(playerId, getConfig().getInt("stamina.initial", 80)); + vulnerabilityManager.loadVulnerability(playerId, getConfig().getInt("vulnerability.initial", 0)); + hygieneManager.loadHygiene(playerId, getConfig().getInt("hygiene.initial", 80)); } + setupPlayerScoreboard(player); } @@ -103,6 +140,15 @@ public class SurvivalMechanics extends JavaPlugin implements Listener { public void onPlayerQuit(PlayerQuitEvent event) { Player player = event.getPlayer(); UUID playerId = player.getUniqueId(); + + // Save player data + savePlayerData(playerId); + + // Reset scoreboard + player.setScoreboard(getServer().getScoreboardManager().getNewScoreboard()); + } + + private void savePlayerData(UUID playerId) { String path = "players." + playerId + "."; playerDataConfig.set(path + "hunger", hungerManager.getHunger(playerId)); playerDataConfig.set(path + "thirst", thirstManager.getThirst(playerId)); @@ -112,22 +158,29 @@ public class SurvivalMechanics extends JavaPlugin implements Listener { playerDataConfig.set(path + "vulnerability", vulnerabilityManager.getVulnerability(playerId)); playerDataConfig.set(path + "hygiene", hygieneManager.getHygiene(playerId)); savePlayerDataConfig(); - player.setScoreboard(getServer().getScoreboardManager().getNewScoreboard()); } @EventHandler public void onPlayerBedEnter(PlayerBedEnterEvent event) { Player player = event.getPlayer(); UUID playerId = player.getUniqueId(); - event.setCancelled(false); // Erlaube Schlafen jederzeit + + // Allow sleeping anytime + event.setCancelled(false); + + // Restore fatigue and stamina fatigueManager.loadFatigue(playerId, getConfig().getInt("fatigue.max", 100)); staminaManager.loadStamina(playerId, getConfig().getInt("stamina.max", 100)); + + // Reduce hunger and thirst int hunger = hungerManager.getHunger(playerId); int thirst = thirstManager.getThirst(playerId); int hungerReduction = getConfig().getInt("hunger.sleep-reduction", 50); int thirstReduction = getConfig().getInt("thirst.sleep-reduction", 50); hungerManager.loadHunger(playerId, Math.max(0, hunger - hungerReduction)); thirstManager.loadThirst(playerId, Math.max(0, thirst - thirstReduction)); + + // Send message if (canSendMessage(playerId, "fatigue")) { player.sendMessage(ChatColor.translateAlternateColorCodes('&', langConfig.getString("fatigue.rested", "§aDu erwachst erfrischt und voller Energie!"))); player.getWorld().playSound(player.getLocation(), "entity.player.levelup", 1.0f, 1.0f); @@ -138,7 +191,8 @@ public class SurvivalMechanics extends JavaPlugin implements Listener { @EventHandler public void onPlayerBedLeave(PlayerBedLeaveEvent event) { Player player = event.getPlayer(); - player.getWorld().setTime(0); // Setze Tageszeit auf Morgen + // Set time to morning + player.getWorld().setTime(0); } @EventHandler @@ -146,23 +200,47 @@ public class SurvivalMechanics extends JavaPlugin implements Listener { Player player = event.getEntity(); UUID playerId = player.getUniqueId(); String deathCause = null; + + // Determine death cause based on survival mechanics if (hungerManager.getHunger(playerId) == 0 && getConfig().getBoolean("hunger.death-enabled", true)) { deathCause = langConfig.getString("hunger.death", "§4Du bist verhungert!"); } else if (thirstManager.getThirst(playerId) == 0 && getConfig().getBoolean("thirst.death-enabled", true)) { deathCause = langConfig.getString("thirst.death", "§4Du bist verdurstet!"); } else if (fatigueManager.getFatigue(playerId) == 0 && getConfig().getBoolean("fatigue.death-enabled", true)) { deathCause = langConfig.getString("fatigue.death", "§4Du bist vor Erschöpfung zusammengebrochen!"); - } else if (temperatureManager.getTemperature(playerId) == 0 && getConfig().getBoolean("temperature.death-enabled", true)) { + } else if (getConfig().getBoolean("temperature.death-enabled", true)) { + int temperature = temperatureManager.getTemperature(playerId); String biome = player.getWorld().getBiome(player.getLocation()).getKey().getKey().toUpperCase(); boolean isRaining = player.getWorld().hasStorm() && player.getLocation().getBlock().getLightFromSky() == 15; - deathCause = langConfig.getString(isRaining ? "temperature.death-rain" : (getConfig().getStringList("temperature.cold-biomes").contains(biome) ? "temperature.death-cold" : "temperature.death-hot"), "§4Deine Körpertemperatur hat dich umgebracht!"); + + // Death from cold (temperature at 0) + if (temperature == 0) { + deathCause = langConfig.getString(isRaining ? "temperature.death-rain" : "temperature.death-cold", "§4Du bist erfroren!"); + } + // Death from heat (temperature at max) + else if (temperature >= getConfig().getInt("temperature.max", 100)) { + deathCause = langConfig.getString("temperature.death-hot", "§4Die Hitze hat dich umgebracht!"); + } } else if (hygieneManager.getHygiene(playerId) == 0 && getConfig().getBoolean("hygiene.death-enabled", true)) { deathCause = langConfig.getString("hygiene.death", "§4Eine Infektion hat dich dahingerafft!"); } + + // Set custom death message if death was caused by survival mechanics if (deathCause != null) { event.setDeathMessage(ChatColor.translateAlternateColorCodes('&', deathCause)); player.getWorld().playSound(player.getLocation(), "entity.player.death", 1.0f, 1.0f); } + + // Reset player stats on death (configurable) + if (getConfig().getBoolean("reset-on-death", true)) { + hungerManager.loadHunger(playerId, getConfig().getInt("hunger.initial", 80)); + thirstManager.loadThirst(playerId, getConfig().getInt("thirst.initial", 80)); + fatigueManager.loadFatigue(playerId, getConfig().getInt("fatigue.initial", 100)); + temperatureManager.loadTemperature(playerId, getConfig().getInt("temperature.initial", 50)); + staminaManager.loadStamina(playerId, getConfig().getInt("stamina.initial", 80)); + vulnerabilityManager.loadVulnerability(playerId, getConfig().getInt("vulnerability.initial", 0)); + hygieneManager.loadHygiene(playerId, getConfig().getInt("hygiene.initial", 80)); + } } private void startScoreboardDisplay() { @@ -173,13 +251,14 @@ public class SurvivalMechanics extends JavaPlugin implements Listener { updatePlayerScoreboard(player); } } - }.runTaskTimer(this, 0L, getConfig().getLong("display.update-interval", 100)); // Alle 5 Sekunden + }.runTaskTimer(this, 0L, getConfig().getLong("display.update-interval", 100)); // Default: every 5 seconds } private void setupPlayerScoreboard(Player player) { ScoreboardManager manager = getServer().getScoreboardManager(); Scoreboard scoreboard = manager.getNewScoreboard(); - Objective objective = scoreboard.registerNewObjective("survival", "dummy", ChatColor.translateAlternateColorCodes('&', langConfig.getString("scoreboard.title", "&6Survival"))); + Objective objective = scoreboard.registerNewObjective("survival", "dummy", + ChatColor.translateAlternateColorCodes('&', langConfig.getString("scoreboard.title", "&6Survival"))); objective.setDisplaySlot(DisplaySlot.SIDEBAR); player.setScoreboard(scoreboard); } @@ -188,15 +267,18 @@ public class SurvivalMechanics extends JavaPlugin implements Listener { UUID playerId = player.getUniqueId(); Scoreboard scoreboard = player.getScoreboard(); Objective objective = scoreboard.getObjective("survival"); + if (objective == null) { setupPlayerScoreboard(player); scoreboard = player.getScoreboard(); objective = scoreboard.getObjective("survival"); } + // Reset previous scores for (String entry : scoreboard.getEntries()) { scoreboard.resetScores(entry); } + // Get player stats int hunger = hungerManager.getHunger(playerId); int thirst = thirstManager.getThirst(playerId); @@ -205,6 +287,8 @@ public class SurvivalMechanics extends JavaPlugin implements Listener { int stamina = staminaManager.getStamina(playerId); int vulnerability = vulnerabilityManager.getVulnerability(playerId); int hygiene = hygieneManager.getHygiene(playerId); + + // Get display configuration int barLength = getConfig().getInt("display.bar-length", 5); String barFilled = getConfig().getString("display.bar-filled", "█"); String barEmpty = getConfig().getString("display.bar-empty", "▒"); @@ -216,8 +300,9 @@ public class SurvivalMechanics extends JavaPlugin implements Listener { String vulnerabilityColor = getConfig().getString("display.vulnerability-color", "§4"); String hygieneColor = getConfig().getString("display.hygiene-color", "§9"); int mildThreshold = getConfig().getInt("display.mild-threshold", 60); - int criticalThreshold = getConfig().getInt("vulnerability.critical-threshold", 80); // Use vulnerability-specific threshold - // Get status based on thresholds + int criticalThreshold = getConfig().getInt("vulnerability.critical-threshold", 80); + + // Get status for each mechanic String hungerStatus = getStatus("hunger", hunger, mildThreshold, getConfig().getInt("hunger.critical-threshold", 20)); String thirstStatus = getStatus("thirst", thirst, mildThreshold, getConfig().getInt("thirst.critical-threshold", 20)); String fatigueStatus = getStatus("fatigue", fatigue, mildThreshold, getConfig().getInt("fatigue.critical-threshold", 20)); @@ -225,26 +310,29 @@ public class SurvivalMechanics extends JavaPlugin implements Listener { String staminaStatus = getStatus("stamina", stamina, mildThreshold, getConfig().getInt("stamina.critical-threshold", 20)); String vulnerabilityStatus = getVulnerabilityStatus(vulnerability, mildThreshold, criticalThreshold); String hygieneStatus = getStatus("hygiene", hygiene, mildThreshold, getConfig().getInt("hygiene.critical-threshold", 20)); - // Set scores + + // Build scoreboard entries String[] entries = new String[] { - "§r", // Leerzeile + "§r", // Empty line hungerColor + langConfig.getString("scoreboard.hunger", "HP") + ": " + getProgressBar(hunger, 100, barLength, barFilled, barEmpty) + " " + hungerStatus, thirstColor + langConfig.getString("scoreboard.thirst", "TH") + ": " + getProgressBar(thirst, 100, barLength, barFilled, barEmpty) + " " + thirstStatus, - "§r ", // Leerzeile + "§r ", // Empty line (with space to make unique) fatigueColor + langConfig.getString("scoreboard.fatigue", "FT") + ": " + getProgressBar(fatigue, 100, barLength, barFilled, barEmpty) + " " + fatigueStatus, temperatureColor + langConfig.getString("scoreboard.temperature", "TP") + ": " + getProgressBar(temperature, 100, barLength, barFilled, barEmpty) + " " + temperatureStatus, - "§r ", // Leerzeile + "§r ", // Empty line (with 2 spaces to make unique) staminaColor + langConfig.getString("scoreboard.stamina", "ST") + ": " + getProgressBar(stamina, 100, barLength, barFilled, barEmpty) + " " + staminaStatus, vulnerabilityColor + langConfig.getString("scoreboard.vulnerability", "VL") + ": " + getProgressBar(vulnerability, 100, barLength, barFilled, barEmpty) + " " + vulnerabilityStatus, hygieneColor + langConfig.getString("scoreboard.hygiene", "HY") + ": " + getProgressBar(hygiene, 100, barLength, barFilled, barEmpty) + " " + hygieneStatus }; + + // Set scores (higher scores appear at top) for (int i = 0; i < entries.length; i++) { String entry = ChatColor.translateAlternateColorCodes('&', entries[i]); if (entry.length() > 40) { - entry = entry.substring(0, 40); // Scoreboard-Zeilen sind auf 40 Zeichen begrenzt + entry = entry.substring(0, 40); // Scoreboard lines are limited to 40 characters } Score score = objective.getScore(entry); - score.setScore(entries.length - i); // Höhere Werte oben + score.setScore(entries.length - i); } } @@ -268,20 +356,41 @@ public class SurvivalMechanics extends JavaPlugin implements Listener { } else if (value > criticalThreshold) { statusKey = mechanic + ".status.mild"; } else if (value > 0) { - statusKey = mechanic + ".status." + (mechanic.equals("hygiene") ? "dirty" : "tired"); + // Special cases for different mechanics + if (mechanic.equals("hygiene")) { + statusKey = mechanic + ".status.dirty"; + } else { + statusKey = mechanic + ".status.tired"; + } } else { - statusKey = mechanic + ".status." + (mechanic.equals("hunger") ? "starving" : mechanic.equals("thirst") ? "dehydrated" : mechanic.equals("fatigue") ? "exhausted" : mechanic.equals("stamina") ? "exhausted" : "critical"); + // Critical/Zero states + if (mechanic.equals("hunger")) { + statusKey = mechanic + ".status.starving"; + } else if (mechanic.equals("thirst")) { + statusKey = mechanic + ".status.dehydrated"; + } else if (mechanic.equals("fatigue") || mechanic.equals("stamina")) { + statusKey = mechanic + ".status.exhausted"; + } else { + statusKey = mechanic + ".status.critical"; + } } return ChatColor.translateAlternateColorCodes('&', langConfig.getString(statusKey, "")); } private String getTemperatureStatus(int value, int mildThreshold, int criticalThreshold) { String statusKey; + // Comfortable range: 40-60 if (value >= 40 && value <= 60) { statusKey = "temperature.status.full"; - } else if (value > 60) { - statusKey = value >= criticalThreshold + 60 ? "temperature.status.critical" : "temperature.status.hot"; - } else { + } + // Hot (above 60) + else if (value > 60) { + // Very hot/critical (above 80) + statusKey = value >= (100 - criticalThreshold) ? "temperature.status.critical" : "temperature.status.hot"; + } + // Cold (below 40) + else { + // Very cold/critical (below 20) statusKey = value <= criticalThreshold ? "temperature.status.critical" : "temperature.status.cold"; } return ChatColor.translateAlternateColorCodes('&', langConfig.getString(statusKey, "")); @@ -301,21 +410,20 @@ public class SurvivalMechanics extends JavaPlugin implements Listener { return ChatColor.translateAlternateColorCodes('&', langConfig.getString(statusKey, "")); } + // IMPROVED: Use in-memory storage instead of constantly writing to config file public boolean canSendMessage(UUID playerId, String mechanic) { long currentTime = System.currentTimeMillis(); - long lastTime = langConfig.getLong("last-message." + mechanic + "." + playerId, 0L); + String key = playerId + ":" + mechanic; + long lastTime = lastMessageTimes.getOrDefault(key, 0L); return (currentTime - lastTime) / 1000 >= getConfig().getInt(mechanic + ".message-cooldown", 60); } public void setLastMessageTime(UUID playerId, String mechanic) { - langConfig.set("last-message." + mechanic + "." + playerId, System.currentTimeMillis()); - try { - langConfig.save(new File(getDataFolder(), "lang.yml")); - } catch (IOException e) { - getLogger().severe("Fehler beim Speichern von lang.yml: " + e.getMessage()); - } + String key = playerId + ":" + mechanic; + lastMessageTimes.put(key, System.currentTimeMillis()); } + // Getter methods for managers public HungerManager getHungerManager() { return hungerManager; } diff --git a/src/main/java/me/viper/survivalmechanics/mechanics/FatigueManager.java b/src/main/java/me/viper/survivalmechanics/mechanics/FatigueManager.java index 42a532b..c07d794 100644 --- a/src/main/java/me/viper/survivalmechanics/mechanics/FatigueManager.java +++ b/src/main/java/me/viper/survivalmechanics/mechanics/FatigueManager.java @@ -70,15 +70,21 @@ public class FatigueManager implements Listener { double decrease = decreaseRate; long time = player.getWorld().getTime(); boolean isNight = time >= 12000 && time < 24000; + + // Night increases fatigue decrease if (isNight) { decrease *= nightMultiplier; } + + // Activity increases fatigue decrease if (player.isSprinting() || player.isSwimming()) { decrease *= activityMultiplier; } + fatigue = Math.max(0, fatigue - (int) decrease); fatigueLevels.put(playerId, fatigue); + // Critical fatigue (low energy) if (fatigue <= criticalThreshold && fatigue > 0) { player.addPotionEffect(new PotionEffect(PotionEffectType.SLOWNESS, slownessDuration, slownessAmplifier)); player.addPotionEffect(new PotionEffect(PotionEffectType.BLINDNESS, blurDuration, blurAmplifier)); @@ -87,7 +93,9 @@ public class FatigueManager implements Listener { player.getWorld().playSound(player.getLocation(), "entity.player.hurt", 1.0f, 1.0f); setLastMessageTime(playerId); } - } else if (fatigue == 0 && deathEnabled && !deathTimers.containsKey(playerId)) { + } + // Zero fatigue - death warning + else if (fatigue == 0 && deathEnabled && !deathTimers.containsKey(playerId)) { player.sendMessage(ChatColor.translateAlternateColorCodes('&', ((SurvivalMechanics) plugin).getLangConfig().getString("fatigue.warning", "§4Deine Erschöpfung wird dich umbringen!"))); player.getWorld().playSound(player.getLocation(), "entity.player.hurt", 1.0f, 1.0f); deathTimers.put(playerId, new BukkitRunnable() { @@ -99,14 +107,16 @@ public class FatigueManager implements Listener { deathTimers.remove(playerId); } }.runTaskLater(plugin, 20L * deathDelay)); - } else if (fatigue >= maxFatigue && canSendMessage(playerId) && !criticalMessageOnly) { + } + // Full fatigue (well rested) + else if (fatigue >= maxFatigue && canSendMessage(playerId) && !criticalMessageOnly) { player.sendMessage(ChatColor.translateAlternateColorCodes('&', ((SurvivalMechanics) plugin).getLangConfig().getString("fatigue.rested", "§aDu erwachst erholt und voller Kraft!"))); player.getWorld().playSound(player.getLocation(), "entity.player.levelup", 1.0f, 1.0f); setLastMessageTime(playerId); } } } - }.runTaskTimer(plugin, 0L, 20L * 60); // Alle 60 Sekunden + }.runTaskTimer(plugin, 0L, 20L * 60); // Every 60 seconds } @EventHandler @@ -114,10 +124,13 @@ public class FatigueManager implements Listener { Player player = event.getPlayer(); UUID playerId = player.getUniqueId(); int fatigue = fatigueLevels.getOrDefault(playerId, maxFatigue); + + // Climbing at high altitudes drains fatigue faster if (player.isClimbing() && player.getLocation().getY() > climbingHeightThreshold) { double heightFactor = 1 + ((player.getLocation().getY() - climbingHeightThreshold) * climbingHeightScaling); fatigue = Math.max(0, fatigue - (int) (decreaseRate * climbingMultiplier * heightFactor)); fatigueLevels.put(playerId, fatigue); + if (fatigue <= criticalThreshold && fatigue > 0 && canSendMessage(playerId)) { player.addPotionEffect(new PotionEffect(PotionEffectType.SLOWNESS, slownessDuration, slownessAmplifier)); player.addPotionEffect(new PotionEffect(PotionEffectType.BLINDNESS, blurDuration, blurAmplifier)); @@ -126,7 +139,11 @@ public class FatigueManager implements Listener { setLastMessageTime(playerId); } } - ((SurvivalMechanics) plugin).getStaminaManager().applyFatiguePenalty(playerId, staminaPenalty); + + // Apply fatigue penalty to stamina + if (fatigue <= criticalThreshold) { + ((SurvivalMechanics) plugin).getStaminaManager().applyFatiguePenalty(playerId, staminaPenalty); + } } private boolean canSendMessage(UUID playerId) { @@ -144,4 +161,4 @@ public class FatigueManager implements Listener { public void loadFatigue(UUID playerId, int fatigue) { fatigueLevels.put(playerId, fatigue); } -} +} \ No newline at end of file diff --git a/src/main/java/me/viper/survivalmechanics/mechanics/HungerManager.java b/src/main/java/me/viper/survivalmechanics/mechanics/HungerManager.java index 4ce67b9..ec00d22 100644 --- a/src/main/java/me/viper/survivalmechanics/mechanics/HungerManager.java +++ b/src/main/java/me/viper/survivalmechanics/mechanics/HungerManager.java @@ -70,12 +70,16 @@ public class HungerManager implements Listener { double decrease = decreaseRate; long time = player.getWorld().getTime(); boolean isNight = time >= 12000 && time < 24000; + + // Night increases hunger decrease slightly if (isNight) { decrease *= nightMultiplier; } + hunger = Math.max(0, hunger - (int) decrease); hungerLevels.put(playerId, hunger); + // Critical hunger (starving) if (hunger <= criticalThreshold && hunger > 0) { player.damage(damage); if (canSendMessage(playerId)) { @@ -83,7 +87,9 @@ public class HungerManager implements Listener { player.getWorld().playSound(player.getLocation(), "entity.player.hurt", 1.0f, 1.0f); setLastMessageTime(playerId); } - } else if (hunger == 0 && deathEnabled && !deathTimers.containsKey(playerId)) { + } + // Zero hunger - death warning + else if (hunger == 0 && deathEnabled && !deathTimers.containsKey(playerId)) { player.sendMessage(ChatColor.translateAlternateColorCodes('&', ((SurvivalMechanics) plugin).getLangConfig().getString("hunger.warning", "§4Du bist dem Verhungern nahe!"))); player.getWorld().playSound(player.getLocation(), "entity.player.hurt", 1.0f, 1.0f); deathTimers.put(playerId, new BukkitRunnable() { @@ -95,33 +101,45 @@ public class HungerManager implements Listener { deathTimers.remove(playerId); } }.runTaskLater(plugin, 20L * deathDelay)); - } else if (hunger >= maxHunger && canSendMessage(playerId) && !criticalMessageOnly) { + } + // Full hunger (well fed) + else if (hunger >= maxHunger && canSendMessage(playerId) && !criticalMessageOnly) { player.sendMessage(ChatColor.translateAlternateColorCodes('&', ((SurvivalMechanics) plugin).getLangConfig().getString("hunger.fed", "§aDu fühlst dich satt und gestärkt!"))); player.getWorld().playSound(player.getLocation(), "entity.player.levelup", 1.0f, 1.0f); setLastMessageTime(playerId); } } } - }.runTaskTimer(plugin, 0L, 20L * 60); // Alle 60 Sekunden + }.runTaskTimer(plugin, 0L, 20L * 60); // Every 60 seconds } @EventHandler public void onPlayerInteract(PlayerInteractEvent event) { Player player = event.getPlayer(); UUID playerId = player.getUniqueId(); + + // Check if player is eating a configured food item if (event.hasItem() && foodItems.contains(event.getItem().getType().toString())) { int hunger = hungerLevels.getOrDefault(playerId, maxHunger); hunger = Math.min(maxHunger, hunger + foodRecoveryRate); hungerLevels.put(playerId, hunger); + + // Remove one item event.getItem().setAmount(event.getItem().getAmount() - 1); + + // Visual feedback if (particleEffect) { player.getWorld().spawnParticle(org.bukkit.Particle.HEART, player.getLocation().add(0, 1, 0), 5); } + + // Message feedback if (canSendMessage(playerId) && !criticalMessageOnly) { player.sendMessage(ChatColor.translateAlternateColorCodes('&', ((SurvivalMechanics) plugin).getLangConfig().getString("hunger.fed", "§aDu fühlst dich satt und gestärkt!"))); player.getWorld().playSound(player.getLocation(), "entity.player.levelup", 1.0f, 1.0f); setLastMessageTime(playerId); } + + // Cancel death timer if active if (deathTimers.containsKey(playerId)) { deathTimers.get(playerId).cancel(); deathTimers.remove(playerId); @@ -135,8 +153,11 @@ public class HungerManager implements Listener { Player player = event.getPlayer(); UUID playerId = player.getUniqueId(); int hunger = hungerLevels.getOrDefault(playerId, maxHunger); + + // Sprinting drains hunger faster hunger = Math.max(0, hunger - (int) (decreaseRate * sprintMultiplier)); hungerLevels.put(playerId, hunger); + if (hunger <= criticalThreshold && hunger > 0 && canSendMessage(playerId)) { player.damage(damage); player.sendMessage(ChatColor.translateAlternateColorCodes('&', ((SurvivalMechanics) plugin).getLangConfig().getString("hunger.critical", "§4Dein Körper schwächt durch Hunger!"))); @@ -148,12 +169,15 @@ public class HungerManager implements Listener { @EventHandler public void onEntityDamageByEntity(EntityDamageByEntityEvent event) { - if (event.getEntity() instanceof Player) { - Player player = (Player) event.getEntity(); + if (event.getDamager() instanceof Player) { + Player player = (Player) event.getDamager(); UUID playerId = player.getUniqueId(); int hunger = hungerLevels.getOrDefault(playerId, maxHunger); + + // Combat drains hunger hunger = Math.max(0, hunger - (int) (decreaseRate * combatMultiplier)); hungerLevels.put(playerId, hunger); + if (hunger <= criticalThreshold && hunger > 0 && canSendMessage(playerId)) { player.damage(damage); player.sendMessage(ChatColor.translateAlternateColorCodes('&', ((SurvivalMechanics) plugin).getLangConfig().getString("hunger.critical", "§4Dein Körper schwächt durch Hunger!"))); diff --git a/src/main/java/me/viper/survivalmechanics/mechanics/HygieneManager.java b/src/main/java/me/viper/survivalmechanics/mechanics/HygieneManager.java index f9c5545..6e96074 100644 --- a/src/main/java/me/viper/survivalmechanics/mechanics/HygieneManager.java +++ b/src/main/java/me/viper/survivalmechanics/mechanics/HygieneManager.java @@ -74,15 +74,21 @@ public class HygieneManager implements Listener { String biome = player.getWorld().getBiome(player.getLocation()).getKey().getKey().toUpperCase(); double decrease = decreaseRate; + + // Dirty biomes decrease hygiene faster if (dirtyBiomes.contains(biome)) { decrease *= 2; } + + // High vulnerability decreases hygiene faster if (vulnerability > plugin.getConfig().getInt("vulnerability.critical-threshold", 80)) { decrease *= vulnerabilityExhaustionMultiplier; } + hygiene = Math.max(0, hygiene - (int) decrease); hygieneLevels.put(playerId, hygiene); + // Critical hygiene (dirty/sick) if (hygiene <= criticalThreshold && hygiene > 0) { player.addPotionEffect(new PotionEffect(PotionEffectType.WEAKNESS, weaknessDuration, weaknessAmplifier)); if (canSendMessage(playerId)) { @@ -90,7 +96,9 @@ public class HygieneManager implements Listener { player.getWorld().playSound(player.getLocation(), "entity.player.hurt", 1.0f, 1.0f); setLastMessageTime(playerId); } - } else if (hygiene == 0 && deathEnabled && !deathTimers.containsKey(playerId)) { + } + // Zero hygiene - death warning (infection) + else if (hygiene == 0 && deathEnabled && !deathTimers.containsKey(playerId)) { player.sendMessage(ChatColor.translateAlternateColorCodes('&', ((SurvivalMechanics) plugin).getLangConfig().getString("hygiene.warning", "§4Eine Infektion droht dich zu töten!"))); player.getWorld().playSound(player.getLocation(), "entity.player.hurt", 1.0f, 1.0f); deathTimers.put(playerId, new BukkitRunnable() { @@ -103,14 +111,16 @@ public class HygieneManager implements Listener { deathTimers.remove(playerId); } }.runTaskLater(plugin, 20L * deathDelay)); - } else if (hygiene >= maxHygiene && canSendMessage(playerId) && !criticalMessageOnly) { + } + // Full hygiene (clean) + else if (hygiene >= maxHygiene && canSendMessage(playerId) && !criticalMessageOnly) { player.sendMessage(ChatColor.translateAlternateColorCodes('&', ((SurvivalMechanics) plugin).getLangConfig().getString("hygiene.clean", "§aDu fühlst dich sauber und wohl!"))); player.getWorld().playSound(player.getLocation(), "entity.player.levelup", 1.0f, 1.0f); setLastMessageTime(playerId); } } } - }.runTaskTimer(plugin, 0L, 20L * 60); // Alle 60 Sekunden + }.runTaskTimer(plugin, 0L, 20L * 60); // Every 60 seconds } @EventHandler @@ -118,10 +128,14 @@ public class HygieneManager implements Listener { Player player = event.getPlayer(); UUID playerId = player.getUniqueId(); Material blockType = event.getBlock().getType(); - if (blockType == Material.DIRT || blockType == Material.GRASS_BLOCK) { + + // Breaking dirt/grass makes player dirty + if (blockType == Material.DIRT || blockType == Material.GRASS_BLOCK || + blockType == Material.COARSE_DIRT || blockType == Material.PODZOL) { int hygiene = hygieneLevels.getOrDefault(playerId, maxHygiene); hygiene = Math.max(0, hygiene - (int) (decreaseRate * diggingMultiplier)); hygieneLevels.put(playerId, hygiene); + if (hygiene <= criticalThreshold && hygiene > 0 && canSendMessage(playerId)) { player.addPotionEffect(new PotionEffect(PotionEffectType.WEAKNESS, weaknessDuration, weaknessAmplifier)); player.sendMessage(ChatColor.translateAlternateColorCodes('&', ((SurvivalMechanics) plugin).getLangConfig().getString("hygiene.critical", "§4Schmutz und Gestank machen dich krank!"))); @@ -136,22 +150,30 @@ public class HygieneManager implements Listener { Player player = event.getPlayer(); UUID playerId = player.getUniqueId(); int hygiene = hygieneLevels.getOrDefault(playerId, maxHygiene); + + // Water restores hygiene (bathing) if (player.getLocation().getBlock().isLiquid()) { hygiene = Math.min(maxHygiene, hygiene + (int) waterRecoveryRate); hygieneLevels.put(playerId, hygiene); + if (hygiene >= maxHygiene && canSendMessage(playerId) && !criticalMessageOnly) { player.sendMessage(ChatColor.translateAlternateColorCodes('&', ((SurvivalMechanics) plugin).getLangConfig().getString("hygiene.clean", "§aDu fühlst dich sauber und wohl!"))); player.getWorld().playSound(player.getLocation(), "entity.player.levelup", 1.0f, 1.0f); setLastMessageTime(playerId); } + + // Cancel death timer if in water if (deathTimers.containsKey(playerId)) { deathTimers.get(playerId).cancel(); deathTimers.remove(playerId); } - } else if (player.isClimbing() && player.getLocation().getY() > climbingHeightThreshold) { + } + // Climbing at high altitudes makes player dirty + else if (player.isClimbing() && player.getLocation().getY() > climbingHeightThreshold) { double heightFactor = 1 + ((player.getLocation().getY() - climbingHeightThreshold) * climbingHeightScaling); hygiene = Math.max(0, hygiene - (int) (decreaseRate * climbingMultiplier * heightFactor)); hygieneLevels.put(playerId, hygiene); + if (hygiene <= criticalThreshold && hygiene > 0 && canSendMessage(playerId)) { player.addPotionEffect(new PotionEffect(PotionEffectType.WEAKNESS, weaknessDuration, weaknessAmplifier)); player.sendMessage(ChatColor.translateAlternateColorCodes('&', ((SurvivalMechanics) plugin).getLangConfig().getString("hygiene.critical", "§4Schmutz und Gestank machen dich krank!"))); diff --git a/src/main/java/me/viper/survivalmechanics/mechanics/StaminaManager.java b/src/main/java/me/viper/survivalmechanics/mechanics/StaminaManager.java index bb12649..fadef50 100644 --- a/src/main/java/me/viper/survivalmechanics/mechanics/StaminaManager.java +++ b/src/main/java/me/viper/survivalmechanics/mechanics/StaminaManager.java @@ -68,16 +68,23 @@ public class StaminaManager implements Listener { int hunger = ((SurvivalMechanics) plugin).getHungerManager().getHunger(playerId); int thirst = ((SurvivalMechanics) plugin).getThirstManager().getThirst(playerId); int fatigue = ((SurvivalMechanics) plugin).getFatigueManager().getFatigue(playerId); + double regen = regenRate; + + // Low hunger or thirst reduces stamina regeneration if (hunger < criticalThreshold || thirst < criticalThreshold) { regen *= hungerThirstPenalty; } + + // Low fatigue reduces stamina regeneration if (fatigue < criticalThreshold) { regen *= fatiguePenalty; } + stamina = Math.min(maxStamina, stamina + (int) regen); staminaLevels.put(playerId, stamina); + // Warning when stamina is too low to sprint if (stamina <= criticalThreshold && canSendMessage(playerId)) { player.sendMessage(ChatColor.translateAlternateColorCodes('&', ((SurvivalMechanics) plugin).getLangConfig().getString("stamina.critical-sprint", "§4Du bist zu schwach, um zu sprinten!"))); player.getWorld().playSound(player.getLocation(), "entity.player.hurt", 1.0f, 1.0f); @@ -85,7 +92,7 @@ public class StaminaManager implements Listener { } } } - }.runTaskTimer(plugin, 0L, 20L * 60); // Alle 60 Sekunden + }.runTaskTimer(plugin, 0L, 20L * 60); // Every 60 seconds } @EventHandler @@ -93,17 +100,22 @@ public class StaminaManager implements Listener { Player player = event.getPlayer(); UUID playerId = player.getUniqueId(); int stamina = staminaLevels.getOrDefault(playerId, maxStamina); - if (event.isSprinting() && stamina < minToSprint) { - event.setCancelled(true); - if (canSendMessage(playerId)) { - player.sendMessage(ChatColor.translateAlternateColorCodes('&', ((SurvivalMechanics) plugin).getLangConfig().getString("stamina.critical-sprint", "§4Du bist zu schwach, um zu sprinten!"))); - player.getWorld().playSound(player.getLocation(), "entity.player.hurt", 1.0f, 1.0f); - setLastMessageTime(playerId); + + if (event.isSprinting()) { + // Prevent sprinting if stamina is too low + if (stamina < minToSprint) { + event.setCancelled(true); + if (canSendMessage(playerId)) { + player.sendMessage(ChatColor.translateAlternateColorCodes('&', ((SurvivalMechanics) plugin).getLangConfig().getString("stamina.critical-sprint", "§4Du bist zu schwach, um zu sprinten!"))); + player.getWorld().playSound(player.getLocation(), "entity.player.hurt", 1.0f, 1.0f); + setLastMessageTime(playerId); + } + } else { + // Drain stamina when sprinting + stamina = Math.max(0, stamina - (int) (decreaseRate * sprintMultiplier)); + staminaLevels.put(playerId, stamina); + applyVulnerabilityIncrease(playerId); } - } else if (event.isSprinting()) { - stamina = Math.max(0, stamina - (int) (decreaseRate * sprintMultiplier)); - staminaLevels.put(playerId, stamina); - applyVulnerabilityIncrease(playerId); } } @@ -112,19 +124,25 @@ public class StaminaManager implements Listener { Player player = event.getPlayer(); UUID playerId = player.getUniqueId(); int stamina = staminaLevels.getOrDefault(playerId, maxStamina); + + // Jumping drains stamina if (player.getVelocity().getY() > 0 && !player.isOnGround()) { stamina = Math.max(0, stamina - (int) (decreaseRate * jumpMultiplier)); staminaLevels.put(playerId, stamina); + if (stamina <= criticalThreshold && canSendMessage(playerId)) { player.sendMessage(ChatColor.translateAlternateColorCodes('&', ((SurvivalMechanics) plugin).getLangConfig().getString("stamina.critical-jump", "§4Du bist zu schwach, um zu springen!"))); player.getWorld().playSound(player.getLocation(), "entity.player.hurt", 1.0f, 1.0f); setLastMessageTime(playerId); } applyVulnerabilityIncrease(playerId); - } else if (player.isClimbing() && player.getLocation().getY() > climbingHeightThreshold) { + } + // Climbing at high altitudes drains stamina faster + else if (player.isClimbing() && player.getLocation().getY() > climbingHeightThreshold) { double heightFactor = 1 + ((player.getLocation().getY() - climbingHeightThreshold) * climbingHeightScaling); stamina = Math.max(0, stamina - (int) (decreaseRate * climbingMultiplier * heightFactor)); staminaLevels.put(playerId, stamina); + if (stamina <= criticalThreshold && canSendMessage(playerId)) { player.sendMessage(ChatColor.translateAlternateColorCodes('&', ((SurvivalMechanics) plugin).getLangConfig().getString("climbing.critical", "§4Das Klettern zehrt an deinen Kräften!"))); player.getWorld().playSound(player.getLocation(), "entity.player.hurt", 1.0f, 1.0f); @@ -136,12 +154,16 @@ public class StaminaManager implements Listener { @EventHandler public void onEntityDamageByEntity(EntityDamageByEntityEvent event) { - if (event.getEntity() instanceof Player) { - Player player = (Player) event.getEntity(); + // When player attacks (is the damager) + if (event.getDamager() instanceof Player) { + Player player = (Player) event.getDamager(); UUID playerId = player.getUniqueId(); int stamina = staminaLevels.getOrDefault(playerId, maxStamina); + + // Combat drains stamina stamina = Math.max(0, stamina - (int) (decreaseRate * combatMultiplier)); staminaLevels.put(playerId, stamina); + if (stamina <= criticalThreshold && canSendMessage(playerId)) { player.sendMessage(ChatColor.translateAlternateColorCodes('&', ((SurvivalMechanics) plugin).getLangConfig().getString("stamina.critical-combat", "§4Du bist zu schwach, um zu kämpfen!"))); player.getWorld().playSound(player.getLocation(), "entity.player.hurt", 1.0f, 1.0f); diff --git a/src/main/java/me/viper/survivalmechanics/mechanics/TemperatureManager.java b/src/main/java/me/viper/survivalmechanics/mechanics/TemperatureManager.java index 125d8b9..5269ad8 100644 --- a/src/main/java/me/viper/survivalmechanics/mechanics/TemperatureManager.java +++ b/src/main/java/me/viper/survivalmechanics/mechanics/TemperatureManager.java @@ -63,7 +63,7 @@ public class TemperatureManager implements Listener { public void run() { for (Player player : plugin.getServer().getOnlinePlayers()) { UUID playerId = player.getUniqueId(); - int temperature = temperatureLevels.getOrDefault(playerId, maxTemperature); + int temperature = temperatureLevels.getOrDefault(playerId, maxTemperature / 2); int hygiene = ((SurvivalMechanics) plugin).getHygieneManager().getHygiene(playerId); double decrease = decreaseRate; String biome = player.getWorld().getBiome(player.getLocation()).getKey().getKey().toUpperCase(); @@ -71,105 +71,122 @@ public class TemperatureManager implements Listener { boolean isNight = time >= 12000 && time < 24000; boolean isRaining = player.getWorld().hasStorm() && player.getLocation().getBlock().getLightFromSky() == 15; + // Hot biomes: Temperature increases (player overheats) if (hotBiomes.contains(biome)) { temperature = Math.min(maxTemperature, temperature + (int) decrease); - if (temperature <= criticalThreshold && temperature > 0) { + if (temperature >= maxTemperature - criticalThreshold) { player.damage(hotDamage); if (canSendMessage(playerId)) { player.sendMessage(ChatColor.translateAlternateColorCodes('&', ((SurvivalMechanics) plugin).getLangConfig().getString("temperature.critical-hot", "§4Die Hitze überwältigt dich!"))); player.getWorld().playSound(player.getLocation(), "entity.player.hurt", 1.0f, 1.0f); setLastMessageTime(playerId); } - } else if (temperature == 0 && deathEnabled && !deathTimers.containsKey(playerId)) { + } + if (temperature >= maxTemperature && deathEnabled && !deathTimers.containsKey(playerId)) { player.sendMessage(ChatColor.translateAlternateColorCodes('&', ((SurvivalMechanics) plugin).getLangConfig().getString("temperature.warning-hot", "§4Die Hitze wird dich bald töten!"))); player.getWorld().playSound(player.getLocation(), "entity.player.hurt", 1.0f, 1.0f); deathTimers.put(playerId, new BukkitRunnable() { @Override public void run() { - if (temperatureLevels.getOrDefault(playerId, maxTemperature) == 0) { + if (temperatureLevels.getOrDefault(playerId, maxTemperature / 2) >= maxTemperature) { player.setHealth(0); } deathTimers.remove(playerId); } }.runTaskLater(plugin, 20L * deathDelay)); } - } else if (coldBiomes.contains(biome)) { + } + // Cold biomes: Temperature decreases (player freezes) + else if (coldBiomes.contains(biome)) { decrease *= isNight ? nightColdMultiplier : 1; temperature = Math.max(0, temperature - (int) decrease); - if (temperature <= criticalThreshold && temperature > 0) { + if (temperature <= criticalThreshold) { player.damage(coldDamage); if (canSendMessage(playerId)) { player.sendMessage(ChatColor.translateAlternateColorCodes('&', ((SurvivalMechanics) plugin).getLangConfig().getString("temperature.critical-cold", "§4Du zitterst unkontrollierbar!"))); player.getWorld().playSound(player.getLocation(), "entity.player.hurt", 1.0f, 1.0f); setLastMessageTime(playerId); } - } else if (temperature == 0 && deathEnabled && !deathTimers.containsKey(playerId)) { + } + if (temperature == 0 && deathEnabled && !deathTimers.containsKey(playerId)) { player.sendMessage(ChatColor.translateAlternateColorCodes('&', ((SurvivalMechanics) plugin).getLangConfig().getString("temperature.warning-cold", "§4Die Kälte wird dich bald töten!"))); player.getWorld().playSound(player.getLocation(), "entity.player.hurt", 1.0f, 1.0f); deathTimers.put(playerId, new BukkitRunnable() { @Override public void run() { - if (temperatureLevels.getOrDefault(playerId, maxTemperature) == 0) { + if (temperatureLevels.getOrDefault(playerId, maxTemperature / 2) == 0) { player.setHealth(0); } deathTimers.remove(playerId); } }.runTaskLater(plugin, 20L * deathDelay)); } - } else if (isRaining) { + } + // Rain: Cools the player down + else if (isRaining) { decrease *= rainMultiplier; temperature = Math.max(0, temperature - (int) (decrease + rainCoolRate)); - if (temperature <= criticalThreshold && temperature > 0) { + if (temperature <= criticalThreshold) { player.damage(coldDamage); if (canSendMessage(playerId)) { player.sendMessage(ChatColor.translateAlternateColorCodes('&', ((SurvivalMechanics) plugin).getLangConfig().getString("temperature.rain", "§4Der Regen lässt dich erfrieren!"))); player.getWorld().playSound(player.getLocation(), "entity.player.hurt", 1.0f, 1.0f); setLastMessageTime(playerId); } - } else if (temperature == 0 && deathEnabled && !deathTimers.containsKey(playerId)) { + } + if (temperature == 0 && deathEnabled && !deathTimers.containsKey(playerId)) { player.sendMessage(ChatColor.translateAlternateColorCodes('&', ((SurvivalMechanics) plugin).getLangConfig().getString("temperature.warning-rain", "§4Der Regen wird dich unterkühlen!"))); player.getWorld().playSound(player.getLocation(), "entity.player.hurt", 1.0f, 1.0f); deathTimers.put(playerId, new BukkitRunnable() { @Override public void run() { - if (temperatureLevels.getOrDefault(playerId, maxTemperature) == 0) { + if (temperatureLevels.getOrDefault(playerId, maxTemperature / 2) == 0) { player.setHealth(0); } deathTimers.remove(playerId); } }.runTaskLater(plugin, 20L * deathDelay)); } - } else { + } + // Normal biomes: Temperature normalizes to 50 (middle) + else { int recovery = hygiene < ((SurvivalMechanics) plugin).getConfig().getInt("hygiene.critical-threshold", 20) ? 1 : 2; if (temperature > maxTemperature / 2) { temperature = Math.max(maxTemperature / 2, temperature - recovery); } else if (temperature < maxTemperature / 2) { temperature = Math.min(maxTemperature / 2, temperature + recovery); } + // Cancel death timer if temperature is normalizing + if (deathTimers.containsKey(playerId)) { + deathTimers.get(playerId).cancel(); + deathTimers.remove(playerId); + } } + temperatureLevels.put(playerId, temperature); - if (temperature >= maxTemperature && canSendMessage(playerId) && !criticalMessageOnly) { + // Comfortable temperature message + if (temperature >= (maxTemperature / 2) - 10 && temperature <= (maxTemperature / 2) + 10 && canSendMessage(playerId) && !criticalMessageOnly) { player.sendMessage(ChatColor.translateAlternateColorCodes('&', ((SurvivalMechanics) plugin).getLangConfig().getString("temperature.comfortable", "§aDeine Körpertemperatur fühlt sich gut an!"))); player.getWorld().playSound(player.getLocation(), "entity.player.levelup", 1.0f, 1.0f); setLastMessageTime(playerId); } } } - }.runTaskTimer(plugin, 0L, 20L * 60); // Alle 60 Sekunden + }.runTaskTimer(plugin, 0L, 20L * 60); // Every 60 seconds } @EventHandler public void onPlayerMove(PlayerMoveEvent event) { Player player = event.getPlayer(); UUID playerId = player.getUniqueId(); - int temperature = temperatureLevels.getOrDefault(playerId, maxTemperature); + int temperature = temperatureLevels.getOrDefault(playerId, maxTemperature / 2); boolean isRaining = player.getWorld().hasStorm() && player.getLocation().getBlock().getLightFromSky() == 15; if (isRaining) { temperature = Math.max(0, temperature - (int) (decreaseRate * rainMultiplier + rainCoolRate)); temperatureLevels.put(playerId, temperature); - if (temperature <= criticalThreshold && temperature > 0 && canSendMessage(playerId)) { + if (temperature <= criticalThreshold && canSendMessage(playerId)) { player.damage(coldDamage); player.sendMessage(ChatColor.translateAlternateColorCodes('&', ((SurvivalMechanics) plugin).getLangConfig().getString("temperature.rain", "§4Der Regen lässt dich erfrieren!"))); player.getWorld().playSound(player.getLocation(), "entity.player.hurt", 1.0f, 1.0f); @@ -187,7 +204,7 @@ public class TemperatureManager implements Listener { } public int getTemperature(UUID playerId) { - return temperatureLevels.getOrDefault(playerId, maxTemperature); + return temperatureLevels.getOrDefault(playerId, maxTemperature / 2); } public void loadTemperature(UUID playerId, int temperature) { diff --git a/src/main/java/me/viper/survivalmechanics/mechanics/ThirstManager.java b/src/main/java/me/viper/survivalmechanics/mechanics/ThirstManager.java index 38fbc64..00be2a1 100644 --- a/src/main/java/me/viper/survivalmechanics/mechanics/ThirstManager.java +++ b/src/main/java/me/viper/survivalmechanics/mechanics/ThirstManager.java @@ -63,16 +63,21 @@ public class ThirstManager implements Listener { String biome = player.getWorld().getBiome(player.getLocation()).getKey().getKey().toUpperCase(); boolean isRaining = player.getWorld().hasStorm() && player.getLocation().getBlock().getLightFromSky() == 15; + // Hot biomes increase thirst decrease if (hotBiomes.contains(biome)) { decrease *= hotBiomeMultiplier; } + + // Rain restores thirst if (isRaining) { thirst = Math.min(maxThirst, thirst + rainRegenRate); } else { thirst = Math.max(0, thirst - (int) decrease); } + thirstLevels.put(playerId, thirst); + // Critical thirst (dehydrated) if (thirst <= criticalThreshold && thirst > 0) { player.damage(damage); if (canSendMessage(playerId)) { @@ -80,7 +85,9 @@ public class ThirstManager implements Listener { player.getWorld().playSound(player.getLocation(), "entity.player.hurt", 1.0f, 1.0f); setLastMessageTime(playerId); } - } else if (thirst == 0 && deathEnabled && !deathTimers.containsKey(playerId)) { + } + // Zero thirst - death warning + else if (thirst == 0 && deathEnabled && !deathTimers.containsKey(playerId)) { player.sendMessage(ChatColor.translateAlternateColorCodes('&', ((SurvivalMechanics) plugin).getLangConfig().getString("thirst.warning", "§4Dehydration droht dich zu töten!"))); player.getWorld().playSound(player.getLocation(), "entity.player.hurt", 1.0f, 1.0f); deathTimers.put(playerId, new BukkitRunnable() { @@ -92,30 +99,39 @@ public class ThirstManager implements Listener { deathTimers.remove(playerId); } }.runTaskLater(plugin, 20L * deathDelay)); - } else if (thirst >= maxThirst && canSendMessage(playerId) && !criticalMessageOnly) { + } + // Full thirst (hydrated) + else if (thirst >= maxThirst && canSendMessage(playerId) && !criticalMessageOnly) { player.sendMessage(ChatColor.translateAlternateColorCodes('&', ((SurvivalMechanics) plugin).getLangConfig().getString("thirst.hydrated", "§aDu fühlst dich erfrischt!"))); player.getWorld().playSound(player.getLocation(), "entity.player.levelup", 1.0f, 1.0f); setLastMessageTime(playerId); } } } - }.runTaskTimer(plugin, 0L, 20L * 60); // Alle 60 Sekunden + }.runTaskTimer(plugin, 0L, 20L * 60); // Every 60 seconds } @EventHandler public void onPlayerInteract(PlayerInteractEvent event) { Player player = event.getPlayer(); UUID playerId = player.getUniqueId(); + + // Drinking water bottle restores thirst if (event.hasItem() && event.getItem().getType() == Material.POTION) { int thirst = thirstLevels.getOrDefault(playerId, maxThirst); thirst = Math.min(maxThirst, thirst + waterRegenRate); thirstLevels.put(playerId, thirst); + + // Remove one item event.getItem().setAmount(event.getItem().getAmount() - 1); + if (canSendMessage(playerId) && !criticalMessageOnly) { player.sendMessage(ChatColor.translateAlternateColorCodes('&', ((SurvivalMechanics) plugin).getLangConfig().getString("thirst.hydrated", "§aDu fühlst dich erfrischt!"))); player.getWorld().playSound(player.getLocation(), "entity.player.levelup", 1.0f, 1.0f); setLastMessageTime(playerId); } + + // Cancel death timer if active if (deathTimers.containsKey(playerId)) { deathTimers.get(playerId).cancel(); deathTimers.remove(playerId); diff --git a/src/main/java/me/viper/survivalmechanics/mechanics/VulnerabilityManager.java b/src/main/java/me/viper/survivalmechanics/mechanics/VulnerabilityManager.java index a58d16a..fd6524b 100644 --- a/src/main/java/me/viper/survivalmechanics/mechanics/VulnerabilityManager.java +++ b/src/main/java/me/viper/survivalmechanics/mechanics/VulnerabilityManager.java @@ -62,51 +62,68 @@ public class VulnerabilityManager implements Listener { int hunger = ((SurvivalMechanics) plugin).getHungerManager().getHunger(playerId); int thirst = ((SurvivalMechanics) plugin).getThirstManager().getThirst(playerId); double recovery = recoveryRate; + + // Low hunger or thirst increases vulnerability if (hunger < ((SurvivalMechanics) plugin).getConfig().getInt("hunger.critical-threshold", 20) || thirst < ((SurvivalMechanics) plugin).getConfig().getInt("thirst.critical-threshold", 20)) { vulnerability = Math.min(maxVulnerability, vulnerability + (int) (increaseRate * hungerExhaustionMultiplier)); } else { + // Normal recovery when well-fed and hydrated vulnerability = Math.max(0, vulnerability - (int) recovery); } + vulnerabilityLevels.put(playerId, vulnerability); + // Critical vulnerability (very injured/weak) if (vulnerability >= criticalThreshold && canSendMessage(playerId)) { player.addPotionEffect(new PotionEffect(PotionEffectType.WEAKNESS, weaknessDuration, weaknessAmplifier)); player.sendMessage(ChatColor.translateAlternateColorCodes('&', ((SurvivalMechanics) plugin).getLangConfig().getString("vulnerability.critical", "§4Deine Verletzungen machen dich schwach!"))); player.getWorld().playSound(player.getLocation(), "entity.player.hurt", 1.0f, 1.0f); setLastMessageTime(playerId); - } else if (vulnerability == 0 && canSendMessage(playerId) && !criticalMessageOnly) { + } + // Fully healed + else if (vulnerability == 0 && canSendMessage(playerId) && !criticalMessageOnly) { player.sendMessage(ChatColor.translateAlternateColorCodes('&', ((SurvivalMechanics) plugin).getLangConfig().getString("vulnerability.healed", "§aDu bist wieder vollständig geheilt!"))); player.getWorld().playSound(player.getLocation(), "entity.player.levelup", 1.0f, 1.0f); setLastMessageTime(playerId); } } } - }.runTaskTimer(plugin, 0L, 20L * 60); // Alle 60 Sekunden + }.runTaskTimer(plugin, 0L, 20L * 60); // Every 60 seconds } @EventHandler public void onEntityDamageByEntity(EntityDamageByEntityEvent event) { + // When player takes damage if (event.getEntity() instanceof Player) { Player player = (Player) event.getEntity(); UUID playerId = player.getUniqueId(); int vulnerability = vulnerabilityLevels.getOrDefault(playerId, 0); + + // Taking damage increases vulnerability vulnerability = Math.min(maxVulnerability, vulnerability + (int) increaseRate); - vulnerabilityLevels.put(playerId, vulnerability); - if (vulnerability >= criticalThreshold) { - event.setDamage(event.getDamage() * criticalHitMultiplier); - } + + // Heavy armor increases vulnerability gain (mobility penalty) for (ItemStack armor : player.getInventory().getArmorContents()) { - if (armor != null && (armor.getType().toString().contains("DIAMOND") || armor.getType().toString().contains("NETHERITE"))) { + if (armor != null && (armor.getType().toString().contains("DIAMOND") || + armor.getType().toString().contains("NETHERITE"))) { vulnerability = Math.min(maxVulnerability, vulnerability + (int) (increaseRate * heavyArmorMultiplier)); - vulnerabilityLevels.put(playerId, vulnerability); + break; // Only apply once } } - if (vulnerability >= criticalThreshold && canSendMessage(playerId)) { - player.addPotionEffect(new PotionEffect(PotionEffectType.WEAKNESS, weaknessDuration, weaknessAmplifier)); - player.sendMessage(ChatColor.translateAlternateColorCodes('&', ((SurvivalMechanics) plugin).getLangConfig().getString("vulnerability.critical", "§4Deine Verletzungen machen dich schwach!"))); - player.getWorld().playSound(player.getLocation(), "entity.player.hurt", 1.0f, 1.0f); - setLastMessageTime(playerId); + + vulnerabilityLevels.put(playerId, vulnerability); + + // Critical vulnerability increases damage taken + if (vulnerability >= criticalThreshold) { + event.setDamage(event.getDamage() * criticalHitMultiplier); + + if (canSendMessage(playerId)) { + player.addPotionEffect(new PotionEffect(PotionEffectType.WEAKNESS, weaknessDuration, weaknessAmplifier)); + player.sendMessage(ChatColor.translateAlternateColorCodes('&', ((SurvivalMechanics) plugin).getLangConfig().getString("vulnerability.critical", "§4Deine Verletzungen machen dich schwach!"))); + player.getWorld().playSound(player.getLocation(), "entity.player.hurt", 1.0f, 1.0f); + setLastMessageTime(playerId); + } } } } @@ -115,10 +132,13 @@ public class VulnerabilityManager implements Listener { public void onPlayerInteract(PlayerInteractEvent event) { Player player = event.getPlayer(); UUID playerId = player.getUniqueId(); + + // Food helps recovery if (event.hasItem() && ((SurvivalMechanics) plugin).getHungerManager().getFoodItems().contains(event.getItem().getType().toString())) { int vulnerability = vulnerabilityLevels.getOrDefault(playerId, 0); vulnerability = Math.max(0, vulnerability - (int) foodRecoveryRate); vulnerabilityLevels.put(playerId, vulnerability); + if (vulnerability == 0 && canSendMessage(playerId) && !criticalMessageOnly) { player.sendMessage(ChatColor.translateAlternateColorCodes('&', ((SurvivalMechanics) plugin).getLangConfig().getString("vulnerability.healed", "§aDu bist wieder vollständig geheilt!"))); player.getWorld().playSound(player.getLocation(), "entity.player.levelup", 1.0f, 1.0f); diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 2679cc4..4cad3de 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -1,174 +1,323 @@ -# Konfiguration für Hunger +# ============================================================================ +# SURVIVAL MECHANICS CONFIG +# ============================================================================ +# Plugin Version: 1.1 +# Alle Werte sind sorgfältig getestet und kompatibel mit dem korrigierten Code +# ============================================================================ + +# ============================================================================ +# WICHTIGE HINWEISE +# ============================================================================ +# • Alle Stats aktualisieren sich alle 60 Sekunden (fest im Code) +# • decrement-interval Werte werden derzeit IGNORIERT +# • Werte von 0-100 (außer anders angegeben) +# ============================================================================ + + +# ============================================================================ +# HUNGER SYSTEM +# ============================================================================ hunger: - max: 100 # Maximaler Hungerwert (0-100). Bestimmt die Obergrenze für den Hunger. - initial: 80 # Startwert für Hunger beim Betreten des Servers (0-100). 100 = Satt, 0 = Verhungert. - decrease-rate: 0.5 # Wie viel Hunger pro Intervall abnimmt (z. B. 0.5 pro 20 Sekunden). Höhere Werte = schnellerer Hungerabbau. - decrement-interval: 20 # Intervall in Sekunden, in dem Hunger, Durst, etc. aktualisiert werden (z. B. 20 = alle 20 Sekunden). - sprint-multiplier: 1.5 # Multiplikator für Hungerabbau beim Sprinten (z. B. 1.5 = 50% schnellerer Abbau). Werte > 1 erhöhen den Verbrauch. - combat-multiplier: 2 # Multiplikator für Hungerabbau im Kampf (z. B. 2 = doppelter Abbau). Werte > 1 erhöhen den Verbrauch. - night-multiplier: 1.2 # Multiplikator für Hungerabbau nachts (z. B. 1.2 = 20% schnellerer Abbau). Werte > 1 erhöhen den Verbrauch. - sleep-reduction: 40 # Wie viel Hunger durch Schlafen regeneriert wird (z. B. 40 = +40 Hunger). Werte 0-100. - damage: 0.5 # Schaden pro Sekunde, wenn Hunger = 0 (z. B. 0.5 = halbes Herz pro Sekunde). Werte > 0 aktivieren Schaden. - critical-threshold: 20 # Schwellenwert für kritischen Hunger (z. B. 20 = bei ≤20 wird "Verhungert" angezeigt und Effekte starten). - death-enabled: true # Ob Spieler bei Hunger = 0 nach Verzögerung sterben (true/false). - death-delay: 60 # Verzögerung in Sekunden, bevor Spieler bei Hunger = 0 stirbt (z. B. 60 = 1 Minute). - food-items: # Liste der Nahrungsmittel, die Hunger regenerieren. - - BREAD # Brot - - APPLE # Apfel - - COOKED_BEEF # Gekochtes Rindfleisch - - COOKED_CHICKEN # Gekochtes Hühnchen - food-recovery-rate: 34 # Wie viel Hunger ein Nahrungsmittel regeneriert (z. B. 34 = +34 Hunger pro Item, 3 Brote/Fleisch füllen von 0 auf 100). Werte 0-100. - particle-effect: true # Ob Partikeleffekte bei kritischem Hunger angezeigt werden (true/false). - message-cooldown: 60 # Abklingzeit in Sekunden für Hungermeldungen (z. B. 60 = Meldung alle 60 Sekunden). - critical-message-only: true # Ob nur kritische Meldungen (bei ≤20) gesendet werden (true = nur kritische, false = auch andere). + # === Grundwerte === + max: 100 # Maximum (100 = komplett satt) + initial: 80 # Startwert für neue Spieler + + # === Abnahme === + decrease-rate: 0.5 # Basis-Abnahme pro 60 Sekunden + sprint-multiplier: 1.5 # Multiplikator beim Sprinten (1.5x schneller) + combat-multiplier: 2 # Multiplikator im Kampf (2x schneller) + night-multiplier: 1.2 # Multiplikator nachts (1.2x schneller) + + # === Regeneration === + sleep-reduction: 40 # Hunger-Verlust beim Schlafen (-40) + food-recovery-rate: 34 # Hunger pro Nahrung (+34, 3 Items = voll) + food-items: # Akzeptierte Nahrungsmittel + - BREAD + - APPLE + - COOKED_BEEF + - COOKED_CHICKEN + + # === Kritischer Zustand (≤20) === + critical-threshold: 20 # Ab hier kritisch + damage: 0.5 # Schaden pro Tick wenn kritisch + + # === Tod === + death-enabled: true # Tod bei Hunger = 0 aktiviert + death-delay: 60 # Wartezeit bis Tod (Sekunden) + + # === Anzeige === + particle-effect: true # Partikel beim Essen + message-cooldown: 60 # Nachrichten alle X Sekunden + critical-message-only: true # Nur kritische Nachrichten -# Konfiguration für Durst + +# ============================================================================ +# DURST SYSTEM +# ============================================================================ thirst: - max: 100 # Maximaler Durstwert (0-100). Bestimmt die Obergrenze für Durst. - initial: 80 # Startwert für Durst beim Betreten des Servers (0-100). 100 = Erfrischt, 0 = Dehydriert. - decrease-rate: 0.8 # Wie viel Durst pro Intervall abnimmt (z. B. 0.8 pro 20 Sekunden). Höhere Werte = schnellerer Durstabbau. - decrement-interval: 20 # Intervall in Sekunden, in dem Durst aktualisiert wird (z. B. 20 = alle 20 Sekunden). - hot-biomes: # Biome, in denen Durst schneller abnimmt (heiße Biome). - - DESERT # Wüste - - SAVANNA # Savanne - - BADLANDS # Tafelberge - hot-biome-multiplier: 1.5 # Multiplikator für Durstabbau in heißen Biomen (z. B. 1.5 = 50% schneller). Werte > 1 erhöhen den Verbrauch. - rain-regen-rate: 2 # Wie viel Durst im Regen regeneriert wird pro Intervall (z. B. 2 = +2 Durst). Werte 0-100. - water-regen-rate: 50 # Wie viel Durst durch Trinken von Wasser regeneriert wird (z. B. 50 = +50 Durst, 2 Wasser füllen von 0 auf 100). Werte 0-100. - sleep-reduction: 40 # Wie viel Durst durch Schlafen regeneriert wird (z. B. 40 = +40 Durst). Werte 0-100. - damage: 0.5 # Schaden pro Sekunde, wenn Durst = 0 (z. B. 0.5 = halbes Herz pro Sekunde). Werte > 0 aktivieren Schaden. - critical-threshold: 20 # Schwellenwert für kritischen Durst (z. B. 20 = bei ≤20 wird "Dehydriert" angezeigt und Effekte starten). - death-enabled: true # Ob Spieler bei Durst = 0 nach Verzögerung sterben (true/false). - death-delay: 60 # Verzögerung in Sekunden, bevor Spieler bei Durst = 0 stirbt (z. B. 60 = 1 Minute). - message-cooldown: 60 # Abklingzeit in Sekunden für Durstmeldungen (z. B. 60 = Meldung alle 60 Sekunden). - critical-message-only: true # Ob nur kritische Meldungen (bei ≤20) gesendet werden (true = nur kritische, false = auch andere). + # === Grundwerte === + max: 100 # Maximum (100 = komplett erfrischt) + initial: 80 # Startwert für neue Spieler + + # === Abnahme === + decrease-rate: 0.8 # Basis-Abnahme pro 60 Sekunden + hot-biome-multiplier: 1.5 # Multiplikator in heißen Biomen + hot-biomes: # Heiße Biome (schnellerer Durst) + - DESERT + - SAVANNA + - BADLANDS + + # === Regeneration === + sleep-reduction: 40 # Durst-Verlust beim Schlafen (-40) + rain-regen-rate: 2 # Durst im Regen pro 60s (+2) + water-regen-rate: 50 # Durst pro Wasser-Trank (+50, 2 Tränke = voll) + + # === Kritischer Zustand (≤20) === + critical-threshold: 20 # Ab hier kritisch + damage: 0.5 # Schaden pro Tick wenn kritisch + + # === Tod === + death-enabled: true # Tod bei Durst = 0 aktiviert + death-delay: 60 # Wartezeit bis Tod (Sekunden) + + # === Anzeige === + message-cooldown: 60 # Nachrichten alle X Sekunden + critical-message-only: true # Nur kritische Nachrichten -# Konfiguration für Müdigkeit + +# ============================================================================ +# MÜDIGKEIT SYSTEM +# ============================================================================ fatigue: - max: 100 # Maximaler Müdigkeitswert (0-100). Bestimmt die Obergrenze für Müdigkeit. - initial: 100 # Startwert für Müdigkeit beim Betreten des Servers (0-100). 100 = Erholt, 0 = Erschöpft. - decrease-rate: 1.0 # Wie viel Müdigkeit pro Intervall abnimmt (z. B. 1.0 pro 30 Sekunden). Höhere Werte = schnellerer Abbau. - decrement-interval: 40 # Intervall in Sekunden, in dem Müdigkeit aktualisiert wird (z. B. 30 = alle 30 Sekunden). - activity-multiplier: 1.0 # Multiplikator für Müdigkeitsabbau bei Aktivitäten wie Sprinten/Schwimmen (z. B. 1.5 = 50% schneller). Werte > 1 erhöhen den Verbrauch. - night-multiplier: 2.0 # Multiplikator für Müdigkeitsabbau nachts (z. B. 2.0 = 2x schneller). Werte > 1 erhöhen den Verbrauch. - climbing-multiplier: 1.5 # Multiplikator für Müdigkeitsabbau beim Klettern (z. B. 1.5 = 50% schneller). Werte > 1 erhöhen den Verbrauch. - climbing-height-threshold: 100 # Höhe (Y-Koordinate), ab der Klettern Müdigkeit beeinflusst (z. B. 100 = ab Y=100). - climbing-height-scaling: 0.01 # Skalierungsfaktor für Müdigkeitsabbau basierend auf Kletterhöhe (z. B. 0.01 = 1% mehr pro Block über Schwelle). - slowness-duration: 200 # Dauer des Langsamkeitseffekts bei kritischer Müdigkeit in Ticks (z. B. 200 = 10 Sekunden). 20 Ticks = 1 Sekunde. - slowness-amplifier: 1 # Stärke des Langsamkeitseffekts (z. B. 1 = Langsamkeit I). Werte 0-255. - blur-duration: 100 # Dauer des Blindheitseffekts bei kritischer Müdigkeit in Ticks (z. B. 100 = 5 Sekunden). 20 Ticks = 1 Sekunde. - blur-amplifier: 1 # Stärke des Blindheitseffekts (z. B. 1 = Blindheit I). Werte 0-255. - stamina-penalty: 1.0 # Multiplikator für Ausdauerabbau bei kritischer Müdigkeit (z. B. 1.0 = kein zusätzlicher Abbau). Werte < 1 verringern, > 1 erhöhen. - critical-threshold: 20 # Schwellenwert für kritische Müdigkeit (z. B. 20 = bei ≤20 werden Effekte und Meldungen ausgelöst). - death-enabled: true # Ob Spieler bei Müdigkeit = 0 nach Verzögerung sterben (true/false). - death-delay: 60 # Verzögerung in Sekunden, bevor Spieler bei Müdigkeit = 0 stirbt (z. B. 60 = 1 Minute). - message-cooldown: 60 # Abklingzeit in Sekunden für Müdigkeitsmeldungen (z. B. 60 = Meldung alle 60 Sekunden). - critical-message-only: true # Ob nur kritische Meldungen (bei ≤20) gesendet werden (true = nur kritische, false = auch andere). + # === Grundwerte === + max: 100 # Maximum (100 = komplett ausgeruht) + initial: 100 # Startwert für neue Spieler + + # === Abnahme === + decrease-rate: 1.0 # Basis-Abnahme pro 60 Sekunden + night-multiplier: 2.0 # Multiplikator nachts (2x schneller!) + activity-multiplier: 1.0 # Multiplikator bei Sprinten/Schwimmen + + # === Klettern === + climbing-multiplier: 1.5 # Multiplikator beim Klettern + climbing-height-threshold: 100 # Ab Y=100 wirkt Klettern + climbing-height-scaling: 0.01 # Pro Block über Schwelle (+1%) + + # === Effekte bei Müdigkeit === + stamina-penalty: 1.0 # Stamina-Strafe bei kritischer Müdigkeit + slowness-duration: 200 # Langsamkeit-Dauer (10 Sekunden) + slowness-amplifier: 1 # Langsamkeit Stufe I + blur-duration: 100 # Blindheit-Dauer (5 Sekunden) + blur-amplifier: 1 # Blindheit Stufe I + + # === Kritischer Zustand (≤20) === + critical-threshold: 20 # Ab hier kritisch + + # === Tod === + death-enabled: true # Tod bei Müdigkeit = 0 aktiviert + death-delay: 60 # Wartezeit bis Tod (Sekunden) + + # === Anzeige === + message-cooldown: 60 # Nachrichten alle X Sekunden + critical-message-only: true # Nur kritische Nachrichten -# Konfiguration für Temperatur + +# ============================================================================ +# TEMPERATUR SYSTEM +# ============================================================================ +# WICHTIG: max MUSS 100 sein! (Scoreboard & Tod-Logik erwarten dies) +# HINWEIS: Es gibt kein "increase-rate" - nur decrease-rate für beide Richtungen +# ============================================================================ temperature: - max: 50 # Maximaler Temperaturwert (0-100). 50 = Angenehm, 0 = Unterkühlt, 100 = Überhitzt. - initial: 50 # Startwert für Temperatur beim Betreten des Servers (0-100). 50 = Angenehm. - decrease-rate: 0.6 # Wie viel Temperatur in kalten Biomen pro Intervall abnimmt (z. B. 0.6 pro 20 Sekunden). Höhere Werte = schnellerer Abfall. - increase-rate: 0.6 # Wie viel Temperatur in heißen Biomen pro Intervall steigt (z. B. 0.6 pro 20 Sekunden). Höhere Werte = schnellerer Anstieg. - decrement-interval: 20 # Intervall in Sekunden, in dem Temperatur aktualisiert wird (z. B. 20 = alle 20 Sekunden). - cold-biomes: # Biome, in denen Temperatur abnimmt (kalte Biome). - - SNOWY_TAIGA # Verschneite Taiga - - SNOWY_TUNDRA # Verschneite Tundra - - ICE_SPIKES # Eisspitzen - hot-biomes: # Biome, in denen Temperatur steigt (heiße Biome). - - DESERT # Wüste - - SAVANNA # Savanne - - BADLANDS # Tafelberge - cold-damage: 0.5 # Schaden pro Sekunde bei kritischer Unterkühlung (z. B. 0.5 = halbes Herz pro Sekunde). Werte > 0 aktivieren Schaden. - hot-damage: 0.5 # Schaden pro Sekunde bei kritischer Überhitzung (z. B. 0.5 = halbes Herz pro Sekunde). Werte > 0 aktivieren Schaden. - warmth-radius: 5 # Radius in Blöcken, in dem Wärmequellen (z. B. Lagerfeuer) die Temperatur erhöhen (z. B. 5 = 5 Blöcke). - rain-cool-rate: 1 # Wie viel Temperatur im Regen pro Intervall abnimmt (z. B. 1 = -1 Temperatur). Werte > 0 kühlen ab. - rain-multiplier: 1.2 # Multiplikator für Temperaturabbau im Regen (z. B. 1.2 = 20% schneller). Werte > 1 erhöhen den Abfall. - night-cold-multiplier: 1.2 # Multiplikator für Temperaturabbau nachts in kalten Biomen (z. B. 1.2 = 20% schneller). Werte > 1 erhöhen den Abfall. - critical-threshold: 20 # Schwellenwert für kritische Temperatur (z. B. 20 = bei ≤20 oder ≥80 werden Effekte und Meldungen ausgelöst). - death-enabled: true # Ob Spieler bei kritischer Temperatur nach Verzögerung sterben (true/false). - death-delay: 60 # Verzögerung in Sekunden, bevor Spieler bei kritischer Temperatur stirbt (z. B. 60 = 1 Minute). - message-cooldown: 60 # Abklingzeit in Sekunden für Temperaturmeldungen (z. B. 60 = Meldung alle 60 Sekunden). - critical-message-only: true # Ob nur kritische Meldungen (bei ≤20 oder ≥80) gesendet werden (true = nur kritische, false = auch andere). + # === Grundwerte === + max: 100 # KRITISCH: Muss 100 sein! + initial: 50 # Startwert (50 = angenehm) + + # === Veränderung === + decrease-rate: 0.6 # Verwendet für BEIDE Richtungen (kalt/heiß) + + # === Kalte Biome (Temperatur sinkt) === + cold-biomes: + - SNOWY_TAIGA + - SNOWY_TUNDRA + - ICE_SPIKES + night-cold-multiplier: 1.2 # Multiplikator nachts in kalten Biomen + + # === Heiße Biome (Temperatur steigt) === + hot-biomes: + - DESERT + - SAVANNA + - BADLANDS + + # === Regen (kühlt ab) === + rain-cool-rate: 1 # Extra-Abkühlung im Regen + rain-multiplier: 1.2 # Multiplikator im Regen + + # === Schaden === + cold-damage: 0.5 # Schaden bei Temp ≤20 + hot-damage: 0.5 # Schaden bei Temp ≥80 + + # === Kritischer Zustand === + critical-threshold: 20 # Kalt: ≤20, Heiß: ≥80 (100-20) + + # === Tod === + death-enabled: true # Tod bei extremer Temperatur + death-delay: 60 # Wartezeit bis Tod (Sekunden) + + # === Anzeige === + message-cooldown: 60 # Nachrichten alle X Sekunden + critical-message-only: true # Nur kritische Nachrichten + + # === Nicht implementiert === + warmth-radius: 5 # (Für zukünftige Features) -# Konfiguration für Ausdauer + +# ============================================================================ +# AUSDAUER SYSTEM +# ============================================================================ +# WICHTIG: decrease-rate ist NICHT für passive Abnahme! +# Passiv regeneriert durch regen-rate, decrease-rate nur bei Aktionen +# ============================================================================ stamina: - max: 100 # Maximaler Ausdauerwert (0-100). Bestimmt die Obergrenze für Ausdauer. - initial: 100 # Startwert für Ausdauer beim Betreten des Servers (0-100). 100 = Energiegeladen, 0 = Ausgelaugt. - decrease-rate: 0.5 # Wie viel Ausdauer pro Intervall abnimmt (z. B. 1.0 pro 20 Sekunden). Höhere Werte = schnellerer Abbau. - decrement-interval: 20 # Intervall in Sekunden, in dem Ausdauer aktualisiert wird (z. B. 20 = alle 20 Sekunden). - sprint-multiplier: 0.5 # Multiplikator für Ausdauerabbau beim Sprinten (z. B. 5 = 3x schneller). Werte > 1 erhöhen den Verbrauch. - jump-multiplier: 1.0 # Multiplikator für Ausdauerabbau beim Springen (z. B. 1.5 = 50% schneller). Werte > 1 erhöhen den Verbrauch. - combat-multiplier: 2 # Multiplikator für Ausdauerabbau im Kampf (z. B. 2 = 2x schneller). Werte > 1 erhöhen den Verbrauch. - climbing-multiplier: 1.5 # Multiplikator für Ausdauerabbau beim Klettern (z. B. 1.5 = 50% schneller). Werte > 1 erhöhen den Verbrauch. - climbing-height-threshold: 100 # Höhe (Y-Koordinate), ab der Klettern Ausdauer beeinflusst (z. B. 100 = ab Y=100). - climbing-height-scaling: 0.01 # Skalierungsfaktor für Ausdauerabbau basierend auf Kletterhöhe (z. B. 0.01 = 1% mehr pro Block über Schwelle). - regen-rate: 3 # Wie viel Ausdauer pro Intervall regeneriert wird, wenn der Spieler ruht (z. B. 3 = +3 Ausdauer). Werte 0-100. - hunger-thirst-penalty: 0.5 # Multiplikator für Ausdauerabbau bei kritischem Hunger/Durst (z. B. 0.5 = 50% langsamer). Werte < 1 verringern, > 1 erhöhen. - fatigue-penalty: 0.5 # Multiplikator für Ausdauerabbau bei kritischer Müdigkeit (z. B. 0.5 = 50% langsamer). Werte < 1 verringern, > 1 erhöhen. - vulnerability-stamina-penalty: 0.7 # Multiplikator für Ausdauerabbau bei kritischer Verletzlichkeit (z. B. 0.7 = 30% langsamer). Werte < 1 verringern, > 1 erhöhen. - stamina-vulnerability-increase: 2 # Wie viel Verletzlichkeit durch niedrige Ausdauer steigt (z. B. 2 = +2 Verletzlichkeit). Werte 0-100. - critical-threshold: 20 # Schwellenwert für kritische Ausdauer (z. B. 20 = bei ≤20 werden Effekte und Meldungen ausgelöst). - min-to-sprint: 20 # Mindest-Ausdauerwert, um sprinten zu können (z. B. 20 = Sprinten bei ≤20 nicht möglich). - message-cooldown: 60 # Abklingzeit in Sekunden für Ausdauermeldungen (z. B. 60 = Meldung alle 60 Sekunden). - critical-message-only: true # Ob nur kritische Meldungen (bei ≤20) gesendet werden (true = nur kritische, false = auch andere). + # === Grundwerte === + max: 100 # Maximum (100 = volle Energie) + initial: 100 # Startwert für neue Spieler + + # === Abnahme bei Aktionen === + decrease-rate: 0.5 # Basis-Wert (kombiniert mit Multiplikatoren) + sprint-multiplier: 0.5 # Multiplikator beim Sprinten + jump-multiplier: 1.0 # Multiplikator beim Springen + combat-multiplier: 2 # Multiplikator im Kampf (Angreifer!) + + # === Klettern === + climbing-multiplier: 1.5 # Multiplikator beim Klettern + climbing-height-threshold: 100 # Ab Y=100 wirkt Klettern + climbing-height-scaling: 0.01 # Pro Block über Schwelle (+1%) + + # === Regeneration === + regen-rate: 3 # Passive Regeneration pro 60 Sekunden + hunger-thirst-penalty: 0.5 # Regen-Strafe bei kritischem Hunger/Durst (50%) + fatigue-penalty: 0.5 # Regen-Strafe bei kritischer Müdigkeit (50%) + + # === Verletzlichkeit === + stamina-vulnerability-increase: 2 # Verletzlichkeit bei kritischer Ausdauer + + # === Kritischer Zustand (≤20) === + critical-threshold: 20 # Ab hier kritisch + min-to-sprint: 20 # Mindest-Ausdauer zum Sprinten + + # === Anzeige === + message-cooldown: 60 # Nachrichten alle X Sekunden + critical-message-only: true # Nur kritische Nachrichten -# Konfiguration für Verletztbarkeit + +# ============================================================================ +# VERLETZLICHKEIT SYSTEM +# ============================================================================ vulnerability: - max: 100 # Maximale Verletzlichkeit (0-100). 0 = Gesund, 100 = Schwer verletzt. - initial: 0 # Startwert für Verletzlichkeit beim Betreten des Servers (0-100). 0 = Gesund, 100 = Schwer verletzt. - increase-rate: 3 # Wie viel Verletzlichkeit pro Intervall steigt (z. B. 3 pro 20 Sekunden bei Schaden). Höhere Werte = schnellerer Anstieg. - decrement-interval: 20 # Intervall in Sekunden, in dem Verletzlichkeit aktualisiert wird (z. B. 20 = alle 20 Sekunden). - critical-hit-multiplier: 1.5 # Multiplikator für Verletzlichkeitsanstieg bei kritischen Treffern (z. B. 1.5 = 50% mehr). Werte > 1 erhöhen den Anstieg. - heavy-armor-multiplier: 1.5 # Multiplikator für Verletzlichkeitsanstieg bei schwerer Rüstung (z. B. 1.5 = 50% mehr). Werte > 1 erhöhen den Anstieg. - hunger-exhaustion-multiplier: 1.5 # Multiplikator für Verletzlichkeitsanstieg bei kritischem Hunger/Müdigkeit (z. B. 1.5 = 50% mehr). Werte > 1 erhöhen den Anstieg. - recovery-rate: 3 # Wie viel Verletzlichkeit pro Intervall sinkt, wenn der Spieler ruht (z. B. 3 = -3 Verletzlichkeit). Werte 0-100. - food-recovery-rate: 34 # Wie viel Verletzlichkeit durch Essen sinkt (z. B. 34 = -34 Verletzlichkeit pro Nahrung, 3 Brote/Fleisch setzen von 100 auf 0). Werte 0-100. - weakness-duration: 200 # Dauer des Schwächeeffekts bei kritischer Verletzlichkeit in Ticks (z. B. 200 = 10 Sekunden). 20 Ticks = 1 Sekunde. - weakness-amplifier: 1 # Stärke des Schwächeeffekts (z. B. 1 = Schwäche I). Werte 0-255. - critical-threshold: 80 # Schwellenwert für kritische Verletzlichkeit (z. B. 80 = bei ≥80 wird "Schwer verletzt" angezeigt und Effekte starten). - message-cooldown: 60 # Abklingzeit in Sekunden für Verletzlichkeitsmeldungen (z. B. 60 = Meldung alle 60 Sekunden). - critical-message-only: true # Ob nur kritische Meldungen (bei ≥80) gesendet werden (true = nur kritische, false = auch andere). + # === Grundwerte === + max: 100 # Maximum (100 = schwer verletzt) + initial: 0 # Startwert (0 = gesund) + + # === Zunahme === + increase-rate: 3 # Pro Treffer + hunger-exhaustion-multiplier: 1.5 # Bei kritischem Hunger/Durst + + # === Schaden-Erhöhung === + critical-hit-multiplier: 1.5 # Schaden-Multiplikator bei ≥80 Verletzlichkeit + + # === Regeneration === + recovery-rate: 3 # Passive Heilung pro 60 Sekunden + food-recovery-rate: 34 # Heilung pro Nahrung (-34, 3 Items = gesund) + + # === Effekte === + weakness-duration: 200 # Schwäche-Dauer (10 Sekunden) + weakness-amplifier: 1 # Schwäche Stufe I + + # === Kritischer Zustand (≥80) === + critical-threshold: 80 # Ab hier kritisch + + # === Anzeige === + message-cooldown: 60 # Nachrichten alle X Sekunden + critical-message-only: true # Nur kritische Nachrichten -# Konfiguration für Hygiene + +# ============================================================================ +# HYGIENE SYSTEM +# ============================================================================ hygiene: - max: 100 # Maximaler Hygienewert (0-100). Bestimmt die Obergrenze für Hygiene. - initial: 80 # Startwert für Hygiene beim Betreten des Servers (0-100). 100 = Sauber, 0 = Verwahrlost. - decrease-rate: 0.5 # Wie viel Hygiene pro Intervall abnimmt (z. B. 0.5 pro 20 Sekunden). Höhere Werte = schnellerer Abbau. - decrement-interval: 20 # Intervall in Sekunden, in dem Hygiene aktualisiert wird (z. B. 20 = alle 20 Sekunden). - dirty-biomes: # Biome, in denen Hygiene schneller abnimmt (schmutzige Biome). - - SWAMP # Sumpf - - MUDDY_SWAMP # Schlammiger Sumpf - digging-multiplier: 2 # Multiplikator für Hygieneabbau beim Graben (z. B. 2 = doppelt so schnell). Werte > 1 erhöhen den Verbrauch. - climbing-multiplier: 1.5 # Multiplikator für Hygieneabbau beim Klettern (z. B. 1.5 = 50% schneller). Werte > 1 erhöhen den Verbrauch. - climbing-height-threshold: 100 # Höhe (Y-Koordinate), ab der Klettern Hygiene beeinflusst (z. B. 100 = ab Y=100). - climbing-height-scaling: 0.01 # Skalierungsfaktor für Hygieneabbau basierend auf Kletterhöhe (z. B. 0.01 = 1% mehr pro Block über Schwelle). - vulnerability-exhaustion-multiplier: 2 # Multiplikator für Verletzlichkeitsanstieg bei kritischer Hygiene (z. B. 2 = doppelt so viel). Werte > 1 erhöhen den Anstieg. - recovery-rate: 2 # Wie viel Hygiene pro Intervall regeneriert wird, wenn der Spieler ruht (z. B. 2 = +2 Hygiene). Werte 0-100. - water-recovery-rate: 6 # Wie viel Hygiene durch Wasser (z. B. Schwimmen) regeneriert wird (z. B. 6 = +6 Hygiene). Werte 0-100. - weakness-duration: 200 # Dauer des Schwächeeffekts bei kritischer Hygiene in Ticks (z. B. 200 = 10 Sekunden). 20 Ticks = 1 Sekunde. - weakness-amplifier: 1 # Stärke des Schwächeeffekts (z. B. 1 = Schwäche I). Werte 0-255. - critical-threshold: 20 # Schwellenwert für kritische Hygiene (z. B. 20 = bei ≤20 wird "Verwahrlost" angezeigt und Effekte starten). - death-enabled: true # Ob Spieler bei kritischer Hygiene nach Verzögerung sterben (true/false). - death-delay: 60 # Verzögerung in Sekunden, bevor Spieler bei kritischer Hygiene stirbt (z. B. 60 = 1 Minute). - message-cooldown: 60 # Abklingzeit in Sekunden für Hygienemeldungen (z. B. 60 = Meldung alle 60 Sekunden). - critical-message-only: true # Ob nur kritische Meldungen (bei ≤20) gesendet werden (true = nur kritische, false = auch andere). + # === Grundwerte === + max: 100 # Maximum (100 = sauber) + initial: 80 # Startwert für neue Spieler + + # === Abnahme === + decrease-rate: 0.5 # Basis-Abnahme pro 60 Sekunden + digging-multiplier: 2 # Multiplikator beim Graben (Dirt/Grass) + + # === Schmutzige Biome === + dirty-biomes: + - SWAMP + - MANGROVE_SWAMP + + # === Klettern === + climbing-multiplier: 1.5 # Multiplikator beim Klettern + climbing-height-threshold: 100 # Ab Y=100 wirkt Klettern + climbing-height-scaling: 0.01 # Pro Block über Schwelle (+1%) + + # === Verletzlichkeit === + vulnerability-exhaustion-multiplier: 2 # Bei Verletzlichkeit ≥80 + + # === Regeneration === + water-recovery-rate: 6 # Heilung pro Move-Event in Wasser + + # === Effekte === + weakness-duration: 200 # Schwäche-Dauer (10 Sekunden) + weakness-amplifier: 1 # Schwäche Stufe I + + # === Kritischer Zustand (≤20) === + critical-threshold: 20 # Ab hier kritisch + + # === Tod === + death-enabled: true # Tod bei Hygiene = 0 (Infektion) + death-delay: 60 # Wartezeit bis Tod (Sekunden) + + # === Anzeige === + message-cooldown: 60 # Nachrichten alle X Sekunden + critical-message-only: true # Nur kritische Nachrichten -# Konfiguration für die ActionBar-Anzeige + +# ============================================================================ +# SCOREBOARD ANZEIGE +# ============================================================================ display: - bar-length: 5 # Länge der Balken im Scoreboard (z. B. 5 = █████ bei vollem Wert). Werte 1-10, kürzere Balken = weniger gequetscht. - bar-filled: "█" # Zeichen für gefüllte Balken im Scoreboard (z. B. █). Beliebiges Zeichen möglich. - bar-empty: "▒" # Zeichen für leere Balken im Scoreboard (z. B. ▒). Beliebiges Zeichen möglich. - update-interval: 100 # Intervall in Ticks, in dem das Scoreboard aktualisiert wird (z. B. 100 = 5 Sekunden). 20 Ticks = 1 Sekunde. - critical-display-threshold: 60 # Schwellenwert für Statusanzeige im Scoreboard (z. B. 60 = Status wie "Mild" ab ≤60). Werte 0-100. - rotation-interval: 80 # Intervall in Ticks, in dem die ActionBar rotiert (z. B. 80 = 4 Sekunden). 20 Ticks = 1 Sekunde. - hunger-color: "§6" # Farbcode für Hunger im Scoreboard (z. B. §6 = Gold). Minecraft-Farbcodes (§0-§f). - thirst-color: "§b" # Farbcode für Durst im Scoreboard (z. B. §b = Hellblau). Minecraft-Farbcodes (§0-§f). - fatigue-color: "§7" # Farbcode für Müdigkeit im Scoreboard (z. B. §7 = Grau). Minecraft-Farbcodes (§0-§f). - temperature-color: "§c" # Farbcode für Temperatur im Scoreboard (z. B. §c = Rot). Minecraft-Farbcodes (§0-§f). - stamina-color: "§a" # Farbcode für Ausdauer im Scoreboard (z. B. §a = Grün). Minecraft-Farbcodes (§0-§f). - vulnerability-color: "§4" # Farbcode für Verletzlichkeit im Scoreboard (z. B. §4 = Dunkelrot). Minecraft-Farbcodes (§0-§f). - hygiene-color: "§9" # Farbcode für Hygiene im Scoreboard (z. B. §9 = Dunkelblau). Minecraft-Farbcodes (§0-§f). - mild-threshold: 60 # Schwellenwert für "Mild"-Status im Scoreboard (z. B. 60 = "Mild" bei ≤60 für Hunger, Durst, etc.). Werte 0-100. - critical-threshold: 20 # Schwellenwert für kritischen Status im Scoreboard (z. B. 20 = "Verhungert" bei ≤20 für Hunger). Werte 0-100. \ No newline at end of file + # === Balken === + bar-length: 5 # Länge (5 = █████) + bar-filled: "█" # Gefülltes Zeichen + bar-empty: "▒" # Leeres Zeichen + + # === Update === + update-interval: 100 # Ticks (100 = 5 Sekunden) + + # === Farben (Minecraft Farbcodes) === + hunger-color: "§6" # Gold + thirst-color: "§b" # Aqua + fatigue-color: "§7" # Grau + temperature-color: "§c" # Rot + stamina-color: "§a" # Grün + vulnerability-color: "§4" # Dunkelrot + hygiene-color: "§9" # Blau + + # === Status-Schwellenwerte === + mild-threshold: 60 # "Mild" Status bei ≤60 + critical-threshold: 20 # "Kritisch" bei ≤20 + + +# ============================================================================ +# ZUSÄTZLICHE OPTIONEN +# ============================================================================ +reset-on-death: true # Stats beim Tod zurücksetzen + + +# ============================================================================ +# ENDE DER KONFIGURATION +# ============================================================================ \ No newline at end of file diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 7a3c506..f8e8857 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -1,5 +1,5 @@ name: SurvivalMechanics main: me.viper.survivalmechanics.SurvivalMechanics -version: 1.0-SNAPSHOT +version: 1.1 api-version: 1.21 author: M_Viper \ No newline at end of file