diff --git a/src/main/java/eu/carrade/amaury/UHCReloaded/borders/BorderManager.java b/src/main/java/eu/carrade/amaury/UHCReloaded/borders/BorderManager.java index 55a2963..e41daa0 100644 --- a/src/main/java/eu/carrade/amaury/UHCReloaded/borders/BorderManager.java +++ b/src/main/java/eu/carrade/amaury/UHCReloaded/borders/BorderManager.java @@ -40,6 +40,8 @@ import eu.carrade.amaury.UHCReloaded.timers.UHTimer; import eu.carrade.amaury.UHCReloaded.utils.UHUtils; import fr.zcraft.zlib.tools.PluginLogger; +import fr.zcraft.zlib.tools.runners.RunTask; +import fr.zcraft.zlib.tools.text.Titles; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.Material; @@ -55,11 +57,15 @@ public class BorderManager { + private final boolean BORDER_SHRINKING; + private final long BORDER_SHRINKING_STARTS_AFTER; + private final long BORDER_SHRINKING_DURATION; + private final double BORDER_SHRINKING_FINAL_SIZE; + private UHCReloaded p = null; private I18n i = null; private WorldBorder border = null; - private Integer currentBorderDiameter = null; private Integer warningSize = 0; private BukkitRunnable warningTask = null; @@ -73,14 +79,12 @@ public class BorderManager public BorderManager(UHCReloaded plugin) { - this.p = plugin; - this.i = p.getI18n(); - - this.warningTimerName = i.t("borders.warning.nameTimer"); + p = plugin; + i = p.getI18n(); - this.currentBorderDiameter = p.getConfig().getInt("map.size"); - this.mapShape = MapShape.fromString(p.getConfig().getString("map.shape")); + warningTimerName = i.t("borders.warning.nameTimer"); + mapShape = MapShape.fromString(p.getConfig().getString("map.shape")); if (mapShape == null) { PluginLogger.warning("Invalid shape '" + p.getConfig().getString("map.shape") + "'; using 'squared' instead."); @@ -100,11 +104,17 @@ public BorderManager(UHCReloaded plugin) border.setShape(mapShape); border.setCenter(world.getSpawnLocation()); - border.setDiameter(currentBorderDiameter); + border.setDiameter(p.getConfig().getInt("map.size", 2000)); border.init(); PluginLogger.info("Using {0} to set the world border.", border.getClass().getSimpleName()); + + + BORDER_SHRINKING = p.getConfig().getBoolean("map.border.shrinking.enabled", false); + BORDER_SHRINKING_STARTS_AFTER = UHUtils.string2Time(p.getConfig().getString("map.border.shrinking.startsAfter"), 30*60); // Seconds + BORDER_SHRINKING_DURATION = UHUtils.string2Time(p.getConfig().getString("map.border.shrinking.shrinksDuring"), 60*60*2); // Same + BORDER_SHRINKING_FINAL_SIZE = p.getConfig().getDouble("map.border.shrinking.diameterAfterShrink", 200); } /** @@ -303,10 +313,7 @@ public void cancelWarning() { warningTask.cancel(); } - catch (IllegalStateException e) - { - - } + catch (IllegalStateException ignored) {} } UHTimer timer = getWarningTimer(); @@ -322,7 +329,7 @@ public void cancelWarning() */ public int getCurrentBorderDiameter() { - return this.currentBorderDiameter; + return (int) border.getDiameter(); } /** @@ -338,7 +345,6 @@ public void setCurrentBorderDiameter(int diameter) { cancelWarning(); - currentBorderDiameter = diameter; border.setDiameter(diameter); } @@ -399,4 +405,28 @@ public void generateWalls(World world) throws CannotGenerateWallsException mapShape.getWallGeneratorInstance(p, wallBlockAir, wallBlockSolid).build(world, getCurrentBorderDiameter(), wallHeight); } + + /** + * Schedules the automatic border reduction, if enabled in the configuration. + */ + public void scheduleBorderReduction() + { + if (BORDER_SHRINKING) + { + RunTask.later(new Runnable() { + @Override + public void run() + { + Integer secondsPerBlock = (int) Math.rint(BORDER_SHRINKING_DURATION / (border.getDiameter() - BORDER_SHRINKING_FINAL_SIZE)) * 2; + + border.setDiameter(BORDER_SHRINKING_FINAL_SIZE, BORDER_SHRINKING_DURATION); + + Titles.broadcastTitle(5, 30, 8, i.t("borders.shrinking.title.title"), i.t("borders.shrinking.title.subtitle")); + + Bukkit.broadcastMessage(i.t("borders.shrinking.message.title")); + Bukkit.broadcastMessage(i.t("borders.shrinking.message.times", secondsPerBlock, BORDER_SHRINKING_FINAL_SIZE)); + } + }, BORDER_SHRINKING_STARTS_AFTER * 20l); + } + } } diff --git a/src/main/java/eu/carrade/amaury/UHCReloaded/borders/worldborders/BrettflanWorldBorder.java b/src/main/java/eu/carrade/amaury/UHCReloaded/borders/worldborders/BrettflanWorldBorder.java index a76e74c..0412f2b 100644 --- a/src/main/java/eu/carrade/amaury/UHCReloaded/borders/worldborders/BrettflanWorldBorder.java +++ b/src/main/java/eu/carrade/amaury/UHCReloaded/borders/worldborders/BrettflanWorldBorder.java @@ -35,8 +35,10 @@ import com.wimbli.WorldBorder.Config; import eu.carrade.amaury.UHCReloaded.UHCReloaded; import eu.carrade.amaury.UHCReloaded.borders.MapShape; +import fr.zcraft.zlib.tools.runners.RunTask; import org.bukkit.Location; import org.bukkit.World; +import org.bukkit.scheduler.BukkitTask; /** @@ -58,6 +60,9 @@ public class BrettflanWorldBorder extends WorldBorder private Double diameter = 0d; + private BukkitTask slowReductionTask = null; + + public BrettflanWorldBorder(World world) { this.world = world; @@ -110,13 +115,47 @@ public double getDiameter() public void setDiameter(double diameter) { setDiameterInternal(diameter); + + if (slowReductionTask != null) + { + slowReductionTask.cancel(); + slowReductionTask = null; + } } @Override - public void setDiameter(double diameter, long time) + public void setDiameter(final double diameter, final long time) { - // TODO emulate the vanilla world border, to allow slowly shrinking circular borders - setDiameterInternal(diameter); + // The behavior of the vanilla reduction is emulated. + final double currentDiameter = getDiameter(); + + final long ticksPerBlockRemoved = (int) Math.rint(time / (currentDiameter - diameter)) * 20l; + final long movement = (diameter >= currentDiameter) ? 1 : -1; + + if (slowReductionTask != null) + { + slowReductionTask.cancel(); + slowReductionTask = null; + } + + slowReductionTask = RunTask.timer(new Runnable() { + @Override + public void run() + { + Double newDiameter = getDiameter() + movement; + + // If the final size is achieved, we set the exact requested size and we stop here. + // Calling setDiameter cancels this task. + if ((movement < 0 && newDiameter <= diameter) || (movement > 0 && newDiameter >= diameter)) + { + setDiameter(diameter); + } + else + { + setDiameterInternal(newDiameter); + } + } + }, ticksPerBlockRemoved, ticksPerBlockRemoved); } private void setDiameterInternal(double diameter) diff --git a/src/main/java/eu/carrade/amaury/UHCReloaded/listeners/GameListener.java b/src/main/java/eu/carrade/amaury/UHCReloaded/listeners/GameListener.java index 1052504..626cae3 100644 --- a/src/main/java/eu/carrade/amaury/UHCReloaded/listeners/GameListener.java +++ b/src/main/java/eu/carrade/amaury/UHCReloaded/listeners/GameListener.java @@ -704,6 +704,9 @@ public void onGameStarts(UHGameStartsEvent ev) // Commands p.getRuntimeCommandsExecutor().registerCommandsInScheduler(RuntimeCommandsExecutor.AFTER_GAME_START); + // Border shrinking + p.getBorderManager().scheduleBorderReduction(); + // MOTD p.getMOTDManager().updateMOTDDuringGame(); diff --git a/src/main/java/eu/carrade/amaury/UHCReloaded/utils/UHUtils.java b/src/main/java/eu/carrade/amaury/UHCReloaded/utils/UHUtils.java index 2922be0..091f403 100644 --- a/src/main/java/eu/carrade/amaury/UHCReloaded/utils/UHUtils.java +++ b/src/main/java/eu/carrade/amaury/UHCReloaded/utils/UHUtils.java @@ -33,6 +33,7 @@ package eu.carrade.amaury.UHCReloaded.utils; import fr.zcraft.zlib.tools.Callback; +import fr.zcraft.zlib.tools.PluginLogger; import org.bukkit.Bukkit; import org.bukkit.Color; import org.bukkit.FireworkEffect; @@ -125,6 +126,30 @@ else if (split.length == 2) // "mm:ss" } } + /** + * Converts a string to a number of seconds. + * + * Prints a warning if the format is invalid. + * + * @param text The text to be converted. + * @param defaultValue The default value returned if the format is invalid. + * + * @return The extracted seconds, or the default value if invalid. + * @see #string2Time(String) + */ + public static int string2Time(String text, Integer defaultValue) + { + try + { + return string2Time(text); + } + catch (IllegalArgumentException e) + { + PluginLogger.warning("Invalid duration '{0}', using {1} seconds instead.", text, defaultValue); + return defaultValue; + } + } + /** * Converts a string to a boolean. * diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index d966b34..89ef11a 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -76,7 +76,9 @@ map: # You can display the border size in the scoreboard; see `scoreboard.border`. - # Automatic and progressive border shrinking + # Automatic and progressive border shrinking. + # Notice: if the border is circular, the WorldBorder Bukkit plugin by Brettflan (see above) + # is REQUIRED. Without, the border shrinking will not work. shrinking: enabled: false diff --git a/src/main/resources/i18n/en_US.yml b/src/main/resources/i18n/en_US.yml index ffe3ced..3ad93b1 100644 --- a/src/main/resources/i18n/en_US.yml +++ b/src/main/resources/i18n/en_US.yml @@ -490,6 +490,14 @@ keys: itemPlayerClose: "{lightpurple} - {yellow}{0}{ci} (close to the border)" itemPlayerVeryClose: "{lightpurple} - {green}{0}{ci} (very close to the border)" + shrinking: + title: + title: "{red}Warning!" + subtitle: "{white}The border begins to shrink..." + message: + title: "{red}{bold}The border begins to shrink..." + times: "{gray}It will shrink by one block every {0} second(s) until {1} blocks in diameter." + timers: syntaxError: "{ce}Syntax error, see /uh timers." durationSyntaxError: "{ce}The duration' syntax is invalid; accepted formats are mm, mm:ss or hh:mm:ss." diff --git a/src/main/resources/i18n/fr_FR.yml b/src/main/resources/i18n/fr_FR.yml index 9e616ef..10177f9 100644 --- a/src/main/resources/i18n/fr_FR.yml +++ b/src/main/resources/i18n/fr_FR.yml @@ -491,6 +491,14 @@ keys: itemPlayerFar: "{lightpurple} - {red}{0}{ci} (loin de la bordure)" itemPlayerClose: "{lightpurple} - {yellow}{0}{ci} (proche de la bordure)" itemPlayerVeryClose: "{lightpurple} - {green}{0}{ci} (très proche de la bordure)" + + shrinking: + title: + title: "{red}Attention !" + subtitle: "{white}La bordure commence à se réduire..." + message: + title: "{red}{bold}La bordure commence à se réduire..." + times: "{gray}Elle réduira d'un bloc toutes les {0} seconde(s) jusqu'à atteindre {1} blocs de diamètre." timers: syntaxError: "{ce}Erreur de syntaxe, consultez /uh timers."