Shuffle floor colors each round, keep eliminated players out of spectator mode, add optional match music
Build / build (push) Successful in 1m17s
Build / build (push) Successful in 1m17s
This commit is contained in:
@@ -117,6 +117,7 @@ public class Arena {
|
||||
public void disable() {
|
||||
cancelAllTasks();
|
||||
billboardManager.despawn();
|
||||
pauseMusic();
|
||||
// Force reset players out regardless of state
|
||||
List<UUID> all = new ArrayList<>();
|
||||
all.addAll(players);
|
||||
@@ -257,6 +258,9 @@ public class Arena {
|
||||
if (!floorManager.hasLayout()) {
|
||||
floorManager.generate(random);
|
||||
}
|
||||
if (plugin.getConfigManager().isShuffleFloorEachRound()) {
|
||||
floorManager.shuffle(random);
|
||||
}
|
||||
floorManager.restoreFull(plugin.getConfigManager().getFloorBatchBlocksPerTick(), () -> {
|
||||
Location safeSpawn = safeSpawnLocation();
|
||||
for (UUID uuid : players) {
|
||||
@@ -269,6 +273,7 @@ public class Arena {
|
||||
p.teleport(safeSpawn);
|
||||
}
|
||||
}
|
||||
startMusic();
|
||||
startNextRound();
|
||||
});
|
||||
}
|
||||
@@ -359,6 +364,7 @@ public class Arena {
|
||||
|
||||
private void processRoundEnd() {
|
||||
playToParticipants("floor-disappear");
|
||||
pauseMusic();
|
||||
floorManager.removeNonTarget(currentTarget, plugin.getConfigManager().getFloorBatchBlocksPerTick(), () -> {
|
||||
BukkitTask delay = plugin.getServer().getScheduler().runTaskLater(plugin, this::evaluateEliminations,
|
||||
config.getFloorRemoveDelaySeconds() * 20L);
|
||||
@@ -379,6 +385,36 @@ public class Arena {
|
||||
}
|
||||
}
|
||||
|
||||
/** Starts the configured background music (if enabled) for every current player/spectator,
|
||||
* from the beginning of the track — Bukkit has no true "resume from position" API, so
|
||||
* pause/resume below is implemented as stop/replay-from-start rather than a real seek. */
|
||||
private void startMusic() {
|
||||
if (!plugin.getConfigManager().isMusicEnabled()) {
|
||||
return;
|
||||
}
|
||||
String track = plugin.getConfigManager().getMusicTrack();
|
||||
float volume = plugin.getConfigManager().getMusicVolume();
|
||||
for (UUID uuid : allParticipants()) {
|
||||
Player p = plugin.getServer().getPlayer(uuid);
|
||||
if (p != null) {
|
||||
plugin.getSoundUtil().playMusic(p, track, volume);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void pauseMusic() {
|
||||
if (!plugin.getConfigManager().isMusicEnabled()) {
|
||||
return;
|
||||
}
|
||||
String track = plugin.getConfigManager().getMusicTrack();
|
||||
for (UUID uuid : allParticipants()) {
|
||||
Player p = plugin.getServer().getPlayer(uuid);
|
||||
if (p != null) {
|
||||
plugin.getSoundUtil().stopMusic(p, track);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void evaluateEliminations() {
|
||||
List<EliminationLogic.PlayerFloorState<UUID, Material>> states = new ArrayList<>();
|
||||
for (UUID uuid : players) {
|
||||
@@ -418,11 +454,16 @@ public class Arena {
|
||||
return;
|
||||
}
|
||||
|
||||
BukkitTask restoreDelay = plugin.getServer().getScheduler().runTaskLater(plugin, () ->
|
||||
floorManager.restoreFull(plugin.getConfigManager().getFloorBatchBlocksPerTick(), () -> {
|
||||
playToParticipants("round-complete");
|
||||
startNextRound();
|
||||
}),
|
||||
BukkitTask restoreDelay = plugin.getServer().getScheduler().runTaskLater(plugin, () -> {
|
||||
if (plugin.getConfigManager().isShuffleFloorEachRound()) {
|
||||
floorManager.shuffle(random);
|
||||
}
|
||||
floorManager.restoreFull(plugin.getConfigManager().getFloorBatchBlocksPerTick(), () -> {
|
||||
playToParticipants("round-complete");
|
||||
startMusic();
|
||||
startNextRound();
|
||||
});
|
||||
},
|
||||
config.getFloorRestoreDelaySeconds() * 20L);
|
||||
tasks.add(restoreDelay);
|
||||
}
|
||||
@@ -437,7 +478,11 @@ public class Arena {
|
||||
session.setEliminated(true);
|
||||
}
|
||||
if (p != null) {
|
||||
p.setGameMode(GameMode.SPECTATOR);
|
||||
// Stay in adventure mode rather than true spectator mode (no noclip/flight) -
|
||||
// being physically placed at the spectator location plus the gameplay
|
||||
// restriction listener's damage-cancel for the SPECTATOR role is enough for
|
||||
// them to safely watch without interacting.
|
||||
p.setGameMode(GameMode.ADVENTURE);
|
||||
if (config.getSpectator() != null) {
|
||||
p.teleport(config.getSpectator());
|
||||
}
|
||||
@@ -453,6 +498,7 @@ public class Arena {
|
||||
private void endMatch(UUID winner) {
|
||||
stateMachine.transition(ArenaPhase.ENDING);
|
||||
cancelAllTasks();
|
||||
pauseMusic();
|
||||
if (winner != null) {
|
||||
Player winnerPlayer = plugin.getServer().getPlayer(winner);
|
||||
String winnerName = winnerPlayer != null ? winnerPlayer.getName() : "Unknown";
|
||||
@@ -551,6 +597,7 @@ public class Arena {
|
||||
public void shutdown() {
|
||||
cancelAllTasks();
|
||||
billboardManager.despawn();
|
||||
pauseMusic();
|
||||
List<UUID> all = allParticipants();
|
||||
for (UUID uuid : all) {
|
||||
Player p = plugin.getServer().getPlayer(uuid);
|
||||
|
||||
@@ -93,6 +93,22 @@ public class ConfigManager {
|
||||
return config.getInt("performance.floor-blocks-per-tick", 200);
|
||||
}
|
||||
|
||||
public boolean isShuffleFloorEachRound() {
|
||||
return config.getBoolean("floor.shuffle-each-round", true);
|
||||
}
|
||||
|
||||
public boolean isMusicEnabled() {
|
||||
return config.getBoolean("music.enabled", false);
|
||||
}
|
||||
|
||||
public String getMusicTrack() {
|
||||
return config.getString("music.track", "MUSIC_DISC_PIGSTEP");
|
||||
}
|
||||
|
||||
public float getMusicVolume() {
|
||||
return (float) config.getDouble("music.volume", 1.0);
|
||||
}
|
||||
|
||||
public boolean isPlaceholderApiEnabled() {
|
||||
return config.getBoolean("integrations.placeholderapi", true);
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import us.tss3.blockparty.config.ArenaConfig;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Deque;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
@@ -89,6 +90,26 @@ public class FloorManager {
|
||||
return new ArrayList<>(layout.values());
|
||||
}
|
||||
|
||||
/**
|
||||
* Randomly permutes which material sits at which position, without changing the set of
|
||||
* positions or how many blocks of each material exist (so target-color counts/fairness
|
||||
* from {@link #generate} are preserved) — used so the board looks different each round
|
||||
* instead of always restoring to the exact same static pattern. Purely in-memory: the
|
||||
* arena's persisted {@code generated-layout} is left untouched, so a restart or
|
||||
* `/bp generate` still starts from the original saved layout.
|
||||
*/
|
||||
public void shuffle(Random random) {
|
||||
if (layout.size() < 2) {
|
||||
return;
|
||||
}
|
||||
List<Material> materials = new ArrayList<>(layout.values());
|
||||
Collections.shuffle(materials, random);
|
||||
List<String> keys = new ArrayList<>(layout.keySet());
|
||||
for (int i = 0; i < keys.size(); i++) {
|
||||
layout.put(keys.get(i), materials.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
|
||||
@@ -2,6 +2,7 @@ package us.tss3.blockparty.util;
|
||||
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Sound;
|
||||
import org.bukkit.SoundCategory;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
@@ -36,6 +37,25 @@ public final class SoundUtil {
|
||||
location.getWorld().playSound(location, sound, volume, pitch);
|
||||
}
|
||||
|
||||
/** Plays a configured track to a player on the MUSIC category (respects their music
|
||||
* volume slider independently of SFX), from the start. */
|
||||
public void playMusic(Player player, String soundName, float volume) {
|
||||
Sound sound = resolve(soundName);
|
||||
if (sound == null) {
|
||||
return;
|
||||
}
|
||||
player.playSound(player.getLocation(), sound, SoundCategory.MUSIC, volume, 1f);
|
||||
}
|
||||
|
||||
/** Stops a configured track for a player on the MUSIC category, if currently playing. */
|
||||
public void stopMusic(Player player, String soundName) {
|
||||
Sound sound = resolve(soundName);
|
||||
if (sound == null) {
|
||||
return;
|
||||
}
|
||||
player.stopSound(sound, SoundCategory.MUSIC);
|
||||
}
|
||||
|
||||
private Sound resolve(String name) {
|
||||
if (name == null || name.isBlank()) {
|
||||
return null;
|
||||
|
||||
Reference in New Issue
Block a user