diff --git a/README.md b/README.md index 438c9df..ae79ecb 100644 --- a/README.md +++ b/README.md @@ -142,10 +142,11 @@ disabled, deleted, or the plugin shuts down, so it never leaks entities. ## Configuration overview - **`config.yml`** — global settings: `ui.*` toggles for scoreboard/bossbar/titles/actionbar, - `sounds.*` (Bukkit `Sound` enum names; invalid names are logged and skipped, never crash - the plugin), `performance.floor-blocks-per-tick` (batch size for floor *generation* and - *restoration*, spread across ticks — the round-end removal of non-target blocks is always - instantaneous, all at once, by design), + `sounds.*` (Bukkit `Sound` enum names, played directly to each participating player rather + than at one world location, so distance never mutes them; invalid names are logged and + skipped, never crash the plugin), `performance.floor-blocks-per-tick` (reserved for + future/optional batched floor operations — round-end removal and each round's floor reset + are always instantaneous, all blocks at once, by design), `defaults.*` (used only when `/bp create` seeds a new arena), `default-floor-materials` (the full 16-color concrete palette by default), `rewards.winner.commands`, and `integrations.placeholderapi`. diff --git a/src/main/java/us/tss3/blockparty/arena/Arena.java b/src/main/java/us/tss3/blockparty/arena/Arena.java index cca0a83..85fecad 100644 --- a/src/main/java/us/tss3/blockparty/arena/Arena.java +++ b/src/main/java/us/tss3/blockparty/arena/Arena.java @@ -358,7 +358,7 @@ public class Arena { } private void processRoundEnd() { - plugin.getSoundUtil().play(config.getSpawn(), plugin.getConfigManager().getSound("floor-disappear"), 1f, 1f); + playToParticipants("floor-disappear"); floorManager.removeNonTarget(currentTarget, plugin.getConfigManager().getFloorBatchBlocksPerTick(), () -> { BukkitTask delay = plugin.getServer().getScheduler().runTaskLater(plugin, this::evaluateEliminations, config.getFloorRemoveDelaySeconds() * 20L); @@ -366,6 +366,19 @@ public class Arena { }); } + /** Plays a configured sound directly to every current player/spectator (each at their own + * location), instead of once at a single world coordinate — Bukkit's location-based + * playSound falls off with distance, so anyone far from that one spot would hear nothing. */ + private void playToParticipants(String soundKey) { + String soundName = plugin.getConfigManager().getSound(soundKey); + for (UUID uuid : allParticipants()) { + Player p = plugin.getServer().getPlayer(uuid); + if (p != null) { + plugin.getSoundUtil().play(p, soundName, 1f, 1f); + } + } + } + private void evaluateEliminations() { List> states = new ArrayList<>(); for (UUID uuid : players) { @@ -406,7 +419,10 @@ public class Arena { } BukkitTask restoreDelay = plugin.getServer().getScheduler().runTaskLater(plugin, () -> - floorManager.restoreFull(plugin.getConfigManager().getFloorBatchBlocksPerTick(), this::startNextRound), + floorManager.restoreFull(plugin.getConfigManager().getFloorBatchBlocksPerTick(), () -> { + playToParticipants("round-complete"); + startNextRound(); + }), config.getFloorRestoreDelaySeconds() * 20L); tasks.add(restoreDelay); } diff --git a/src/main/java/us/tss3/blockparty/floor/FloorManager.java b/src/main/java/us/tss3/blockparty/floor/FloorManager.java index 6587c10..3332cf1 100644 --- a/src/main/java/us/tss3/blockparty/floor/FloorManager.java +++ b/src/main/java/us/tss3/blockparty/floor/FloorManager.java @@ -89,7 +89,11 @@ public class FloorManager { return new ArrayList<>(layout.values()); } - /** Places every block from the stored layout, batched across ticks. */ + /** + * Places every block from the stored layout, all within a single tick, so the floor + * visibly resets instantly at the start of each round instead of filling in gradually. + * See {@link #removeNonTarget} for the same instant-vs-batched rationale. + */ public void restoreFull(int blocksPerTick, Runnable onComplete) { World world = config.getWorld(); if (world == null) { @@ -99,26 +103,12 @@ public class FloorManager { return; } cancelActiveTask(); - Deque> queue = new ArrayDeque<>(layout.entrySet()); - org.bukkit.scheduler.BukkitRunnable runnable = new org.bukkit.scheduler.BukkitRunnable() { - @Override - public void run() { - int processed = 0; - while (processed < blocksPerTick && !queue.isEmpty()) { - Map.Entry entry = queue.poll(); - setBlock(world, entry.getKey(), entry.getValue()); - processed++; - } - if (queue.isEmpty()) { - cancel(); - activeTask = null; - if (onComplete != null) { - onComplete.run(); - } - } - } - }; - activeTask = runnable.runTaskTimer(plugin, 0L, 1L); + for (Map.Entry entry : layout.entrySet()) { + setBlock(world, entry.getKey(), entry.getValue()); + } + if (onComplete != null) { + onComplete.run(); + } } /**