This commit is contained in:
@@ -10,6 +10,7 @@ import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.scheduler.BukkitTask;
|
||||
import us.tss3.blockparty.BlockPartyPlugin;
|
||||
import us.tss3.blockparty.billboard.BillboardManager;
|
||||
import us.tss3.blockparty.config.ArenaConfig;
|
||||
import us.tss3.blockparty.floor.FloorManager;
|
||||
import us.tss3.blockparty.logic.ArenaPhase;
|
||||
@@ -35,6 +36,7 @@ public class Arena {
|
||||
private final BlockPartyPlugin plugin;
|
||||
private final ArenaConfig config;
|
||||
private final FloorManager floorManager;
|
||||
private final BillboardManager billboardManager;
|
||||
private final ArenaStateMachine stateMachine = new ArenaStateMachine(ArenaPhase.DISABLED);
|
||||
private final Random random = new Random();
|
||||
private final TargetColorSelector selector = new TargetColorSelector(random::nextInt);
|
||||
@@ -56,6 +58,7 @@ public class Arena {
|
||||
this.plugin = plugin;
|
||||
this.config = config;
|
||||
this.floorManager = new FloorManager(plugin, config);
|
||||
this.billboardManager = new BillboardManager(plugin, config);
|
||||
if (config.isEnabled()) {
|
||||
stateMachine.transition(ArenaPhase.WAITING);
|
||||
}
|
||||
@@ -109,6 +112,7 @@ public class Arena {
|
||||
|
||||
public void disable() {
|
||||
cancelAllTasks();
|
||||
billboardManager.despawn();
|
||||
// Force reset players out regardless of state
|
||||
List<UUID> all = new ArrayList<>();
|
||||
all.addAll(players);
|
||||
@@ -318,6 +322,7 @@ public class Arena {
|
||||
p.getInventory().setItem(8, display.clone());
|
||||
plugin.getSoundUtil().play(p, plugin.getConfigManager().getSound("target-select"), 1f, 1f);
|
||||
}
|
||||
billboardManager.show(currentTarget);
|
||||
}
|
||||
|
||||
private void setupBossBar() {
|
||||
@@ -493,6 +498,7 @@ public class Arena {
|
||||
floorManager.cancelActiveTask();
|
||||
cancelAllTasks();
|
||||
round = 0;
|
||||
billboardManager.clear();
|
||||
stateMachine.transition(ArenaPhase.WAITING);
|
||||
}
|
||||
|
||||
@@ -516,6 +522,7 @@ public class Arena {
|
||||
/** Called on plugin disable / world unload to safely stop everything and restore players. */
|
||||
public void shutdown() {
|
||||
cancelAllTasks();
|
||||
billboardManager.despawn();
|
||||
List<UUID> all = allParticipants();
|
||||
for (UUID uuid : all) {
|
||||
Player p = plugin.getServer().getPlayer(uuid);
|
||||
@@ -529,6 +536,10 @@ public class Arena {
|
||||
spectators.clear();
|
||||
}
|
||||
|
||||
public BillboardManager getBillboardManager() {
|
||||
return billboardManager;
|
||||
}
|
||||
|
||||
private String formatMaterial(Material material) {
|
||||
String name = material.name().replace("_CONCRETE", "").replace("_", " ").toLowerCase();
|
||||
return name.substring(0, 1).toUpperCase() + name.substring(1);
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
package us.tss3.blockparty.billboard;
|
||||
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.BlockDisplay;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.util.Transformation;
|
||||
import org.joml.AxisAngle4f;
|
||||
import org.joml.Vector3f;
|
||||
import us.tss3.blockparty.BlockPartyPlugin;
|
||||
import us.tss3.blockparty.config.ArenaConfig;
|
||||
|
||||
import java.util.UUID;
|
||||
import java.util.logging.Level;
|
||||
|
||||
/**
|
||||
* Owns an optional {@link BlockDisplay} entity that visually mirrors an arena's currently
|
||||
* selected target color. Purely cosmetic: fully optional (global toggle + per-arena
|
||||
* toggle/location), and every method safely no-ops when disabled, unset, or the arena's
|
||||
* world isn't loaded.
|
||||
*/
|
||||
public class BillboardManager {
|
||||
|
||||
private static final AxisAngle4f NO_ROTATION = new AxisAngle4f(0f, 0f, 0f, 1f);
|
||||
|
||||
private final BlockPartyPlugin plugin;
|
||||
private final ArenaConfig config;
|
||||
private UUID entityId;
|
||||
|
||||
public BillboardManager(BlockPartyPlugin plugin, ArenaConfig config) {
|
||||
this.plugin = plugin;
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
private boolean isActive() {
|
||||
return plugin.getConfigManager().isBillboardEnabled() && config.hasBillboard()
|
||||
&& config.getBillboard().getWorld() != null;
|
||||
}
|
||||
|
||||
/** Shows (spawning the display entity if needed) the given material as the current target. */
|
||||
public void show(Material material) {
|
||||
if (!isActive()) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
BlockDisplay display = resolve();
|
||||
if (display == null) {
|
||||
display = spawn();
|
||||
}
|
||||
if (display != null) {
|
||||
display.setBlock(material.createBlockData());
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
plugin.getLogger().log(Level.WARNING, "Failed to update billboard for arena '" + config.getName() + "'", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/** Blanks the display (e.g. between matches) without destroying the entity. */
|
||||
public void clear() {
|
||||
BlockDisplay display = resolve();
|
||||
if (display != null) {
|
||||
display.setBlock(Material.AIR.createBlockData());
|
||||
}
|
||||
}
|
||||
|
||||
/** Fully removes the display entity. Safe to call even if nothing was ever spawned. */
|
||||
public void despawn() {
|
||||
BlockDisplay display = resolve();
|
||||
if (display != null) {
|
||||
display.remove();
|
||||
}
|
||||
entityId = null;
|
||||
}
|
||||
|
||||
/** Forces the entity to be re-created at its (possibly updated) configured location next time it's shown. */
|
||||
public void invalidate() {
|
||||
despawn();
|
||||
}
|
||||
|
||||
private BlockDisplay resolve() {
|
||||
if (entityId == null) {
|
||||
return null;
|
||||
}
|
||||
Entity entity = plugin.getServer().getEntity(entityId);
|
||||
if (entity instanceof BlockDisplay display && !display.isDead()) {
|
||||
return display;
|
||||
}
|
||||
entityId = null;
|
||||
return null;
|
||||
}
|
||||
|
||||
private BlockDisplay spawn() {
|
||||
Location loc = config.getBillboard();
|
||||
if (loc == null || loc.getWorld() == null) {
|
||||
return null;
|
||||
}
|
||||
BlockDisplay display = loc.getWorld().spawn(loc, BlockDisplay.class, bd -> {
|
||||
bd.setPersistent(false);
|
||||
bd.setGlowing(true);
|
||||
bd.setTransformation(new Transformation(
|
||||
new Vector3f(-0.75f, 0f, -0.75f),
|
||||
NO_ROTATION,
|
||||
new Vector3f(1.5f, 1.5f, 1.5f),
|
||||
NO_ROTATION));
|
||||
});
|
||||
entityId = display.getUniqueId();
|
||||
return display;
|
||||
}
|
||||
}
|
||||
@@ -45,6 +45,8 @@ public class BlockPartyCommand implements CommandExecutor, TabCompleter {
|
||||
case "setlobby" -> setLobby(sender, args);
|
||||
case "setspawn" -> setSpawn(sender, args);
|
||||
case "setspectator" -> setSpectator(sender, args);
|
||||
case "setbillboard" -> setBillboard(sender, args);
|
||||
case "delbillboard" -> delBillboard(sender, args);
|
||||
case "pos1" -> pos1(sender, args);
|
||||
case "pos2" -> pos2(sender, args);
|
||||
case "setfloor" -> setFloor(sender, args);
|
||||
@@ -75,6 +77,8 @@ public class BlockPartyCommand implements CommandExecutor, TabCompleter {
|
||||
"/blockparty setlobby <arena>",
|
||||
"/blockparty setspawn <arena>",
|
||||
"/blockparty setspectator <arena>",
|
||||
"/blockparty setbillboard <arena>",
|
||||
"/blockparty delbillboard <arena>",
|
||||
"/blockparty pos1 <arena>",
|
||||
"/blockparty pos2 <arena>",
|
||||
"/blockparty setfloor <arena>",
|
||||
@@ -191,6 +195,27 @@ public class BlockPartyCommand implements CommandExecutor, TabCompleter {
|
||||
});
|
||||
}
|
||||
|
||||
private void setBillboard(CommandSender sender, String[] args) {
|
||||
if (!requireAdmin(sender, "blockparty.admin.setup") || !requirePlayer(sender)) return;
|
||||
withArena(sender, args, arena -> {
|
||||
arena.getConfig().setBillboard(((Player) sender).getLocation());
|
||||
arena.getConfig().setBillboardEnabled(true);
|
||||
arena.getBillboardManager().invalidate();
|
||||
plugin.getArenaManager().save(arena);
|
||||
sender.sendMessage(plugin.getMessages().get("admin.billboard-set", Map.of("arena", arena.getConfig().getName())));
|
||||
});
|
||||
}
|
||||
|
||||
private void delBillboard(CommandSender sender, String[] args) {
|
||||
if (!requireAdmin(sender, "blockparty.admin.setup")) return;
|
||||
withArena(sender, args, arena -> {
|
||||
arena.getBillboardManager().despawn();
|
||||
arena.getConfig().setBillboard(null);
|
||||
plugin.getArenaManager().save(arena);
|
||||
sender.sendMessage(plugin.getMessages().get("admin.billboard-removed", Map.of("arena", arena.getConfig().getName())));
|
||||
});
|
||||
}
|
||||
|
||||
private void pos1(CommandSender sender, String[] args) {
|
||||
if (!requireAdmin(sender, "blockparty.admin.setup") || !requirePlayer(sender)) return;
|
||||
withArena(sender, args, arena -> {
|
||||
@@ -277,6 +302,7 @@ public class BlockPartyCommand implements CommandExecutor, TabCompleter {
|
||||
sender.sendMessage("§7Lobby set: §f" + (cfg.getLobby() != null));
|
||||
sender.sendMessage("§7Spawn set: §f" + (cfg.getSpawn() != null));
|
||||
sender.sendMessage("§7Spectator set: §f" + (cfg.getSpectator() != null));
|
||||
sender.sendMessage("§7Billboard set: §f" + (cfg.getBillboard() != null) + (cfg.getBillboard() != null ? " (enabled: " + cfg.isBillboardEnabled() + ")" : ""));
|
||||
sender.sendMessage("§7Floor region set: §f" + cfg.hasFloorRegion());
|
||||
sender.sendMessage("§7Floor generated: §f" + cfg.hasGeneratedLayout());
|
||||
sender.sendMessage("§7Floor materials: §f" + cfg.getFloorMaterials().size());
|
||||
@@ -394,7 +420,7 @@ public class BlockPartyCommand implements CommandExecutor, TabCompleter {
|
||||
public List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args) {
|
||||
if (args.length == 1) {
|
||||
return List.of("help", "join", "leave", "arenas", "stats", "create", "delete", "enable", "disable",
|
||||
"setlobby", "setspawn", "setspectator", "pos1", "pos2", "setfloor", "generate", "info", "list", "reload")
|
||||
"setlobby", "setspawn", "setspectator", "setbillboard", "delbillboard", "pos1", "pos2", "setfloor", "generate", "info", "list", "reload")
|
||||
.stream().filter(s -> s.startsWith(args[0].toLowerCase())).collect(Collectors.toList());
|
||||
}
|
||||
if (args.length == 2) {
|
||||
|
||||
@@ -15,6 +15,8 @@ public class ArenaConfig {
|
||||
private Location lobby;
|
||||
private Location spawn;
|
||||
private Location spectator;
|
||||
private Location billboard;
|
||||
private boolean billboardEnabled = true;
|
||||
private int[] pos1; // x,y,z block coords
|
||||
private int[] pos2;
|
||||
|
||||
@@ -84,6 +86,26 @@ public class ArenaConfig {
|
||||
this.spectator = spectator;
|
||||
}
|
||||
|
||||
public Location getBillboard() {
|
||||
return billboard;
|
||||
}
|
||||
|
||||
public void setBillboard(Location billboard) {
|
||||
this.billboard = billboard;
|
||||
}
|
||||
|
||||
public boolean isBillboardEnabled() {
|
||||
return billboardEnabled;
|
||||
}
|
||||
|
||||
public void setBillboardEnabled(boolean billboardEnabled) {
|
||||
this.billboardEnabled = billboardEnabled;
|
||||
}
|
||||
|
||||
public boolean hasBillboard() {
|
||||
return billboard != null && billboardEnabled;
|
||||
}
|
||||
|
||||
public int[] getPos1() {
|
||||
return pos1;
|
||||
}
|
||||
|
||||
@@ -57,6 +57,8 @@ public class ArenaConfigLoader {
|
||||
cfg.setLobby(LocationUtil.fromSection(yml.getConfigurationSection("lobby")));
|
||||
cfg.setSpawn(LocationUtil.fromSection(yml.getConfigurationSection("spawn")));
|
||||
cfg.setSpectator(LocationUtil.fromSection(yml.getConfigurationSection("spectator")));
|
||||
cfg.setBillboard(LocationUtil.fromSection(yml.getConfigurationSection("billboard")));
|
||||
cfg.setBillboardEnabled(yml.getBoolean("billboard-enabled", true));
|
||||
if (yml.contains("pos1")) {
|
||||
cfg.setPos1(new int[]{yml.getInt("pos1.x"), yml.getInt("pos1.y"), yml.getInt("pos1.z")});
|
||||
}
|
||||
@@ -106,6 +108,10 @@ public class ArenaConfigLoader {
|
||||
if (cfg.getSpectator() != null) {
|
||||
LocationUtil.toSection(yml.createSection("spectator"), cfg.getSpectator());
|
||||
}
|
||||
if (cfg.getBillboard() != null) {
|
||||
LocationUtil.toSection(yml.createSection("billboard"), cfg.getBillboard());
|
||||
}
|
||||
yml.set("billboard-enabled", cfg.isBillboardEnabled());
|
||||
if (cfg.getPos1() != null) {
|
||||
yml.set("pos1.x", cfg.getPos1()[0]);
|
||||
yml.set("pos1.y", cfg.getPos1()[1]);
|
||||
|
||||
@@ -33,6 +33,10 @@ public class ConfigManager {
|
||||
return config.getBoolean("ui.bossbar", true);
|
||||
}
|
||||
|
||||
public boolean isBillboardEnabled() {
|
||||
return config.getBoolean("ui.billboard", true);
|
||||
}
|
||||
|
||||
public boolean isTitlesEnabled() {
|
||||
return config.getBoolean("ui.titles", true);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user