1041 lines
41 KiB
Java
1041 lines
41 KiB
Java
package com.bedwars;
|
|
|
|
import org.bukkit.*;
|
|
import org.bukkit.configuration.ConfigurationSection;
|
|
import org.bukkit.entity.Firework;
|
|
import org.bukkit.entity.Player;
|
|
import org.bukkit.inventory.ItemStack;
|
|
import org.bukkit.inventory.meta.FireworkMeta;
|
|
import org.bukkit.potion.PotionEffect;
|
|
import org.bukkit.potion.PotionEffectType;
|
|
import org.bukkit.scheduler.BukkitRunnable;
|
|
import org.bukkit.scheduler.BukkitTask;
|
|
import org.bukkit.event.entity.EntityDamageEvent;
|
|
|
|
import java.util.*;
|
|
|
|
public class GameManager {
|
|
|
|
private final BedwarsPlugin plugin;
|
|
private GameState state = GameState.LOBBY;
|
|
|
|
private final Map<UUID, BedwarsTeam> playerTeams = new HashMap<>();
|
|
private final Map<BedwarsTeam, Boolean> teamBeds = new HashMap<>();
|
|
private final Set<UUID> spectators = new HashSet<>();
|
|
private final Set<BedwarsTeam> eliminatedTeams = new HashSet<>();
|
|
private final Set<UUID> eliminatedPlayers = new HashSet<>();
|
|
|
|
// Stats
|
|
private final Map<UUID, Integer> kills = new HashMap<>();
|
|
private final Map<UUID, Integer> bedsBroken = new HashMap<>();
|
|
|
|
// Combat Tracking
|
|
private final Map<UUID, UUID> lastAttacker = new HashMap<>();
|
|
private final Map<UUID, Long> lastAttackTime = new HashMap<>();
|
|
private final Map<UUID, LastDamageType> lastDamageType = new HashMap<>();
|
|
|
|
// Countdowns
|
|
private boolean countingDown = false;
|
|
private int countdownTime = 30;
|
|
private BukkitTask countdownTask;
|
|
private BukkitTask gameLoopTask;
|
|
private final List<BukkitTask> respawnTasks = new ArrayList<>();
|
|
private int gameTimeTicks = 0;
|
|
|
|
public GameManager(BedwarsPlugin plugin) {
|
|
this.plugin = plugin;
|
|
resetStats();
|
|
}
|
|
|
|
public void init() {
|
|
// Initialize bed status
|
|
for (BedwarsTeam team : BedwarsTeam.values()) {
|
|
teamBeds.put(team, true);
|
|
}
|
|
// Save pristine bed states from world
|
|
plugin.getArenaManager().saveBedStates();
|
|
}
|
|
|
|
public GameState getState() {
|
|
return state;
|
|
}
|
|
|
|
public void setState(GameState state) {
|
|
this.state = state;
|
|
plugin.getScoreboardManager().updateAll();
|
|
}
|
|
|
|
public boolean isCountingDown() {
|
|
return countingDown;
|
|
}
|
|
|
|
public int getCountdownTime() {
|
|
return countdownTime;
|
|
}
|
|
|
|
public BedwarsTeam getPlayerTeam(Player player) {
|
|
return playerTeams.get(player.getUniqueId());
|
|
}
|
|
|
|
public void setPlayerTeam(Player player, BedwarsTeam team) {
|
|
playerTeams.put(player.getUniqueId(), team);
|
|
updateTabName(player);
|
|
plugin.getScoreboardManager().updateAll();
|
|
}
|
|
|
|
public void updateTabName(Player player) {
|
|
player.setPlayerListHeaderFooter("", "");
|
|
BedwarsTeam team = getPlayerTeam(player);
|
|
if (team != null) {
|
|
if (isSpectator(player)) {
|
|
player.setPlayerListName("§7" + player.getName());
|
|
player.setDisplayName("§7" + player.getName() + "§r");
|
|
} else {
|
|
player.setPlayerListName(team.getColor() + player.getName());
|
|
player.setDisplayName(team.getColor() + player.getName() + "§r");
|
|
}
|
|
} else {
|
|
player.setPlayerListName("§7" + player.getName());
|
|
player.setDisplayName("§7" + player.getName() + "§r");
|
|
}
|
|
}
|
|
|
|
public boolean hasBed(BedwarsTeam team) {
|
|
return teamBeds.getOrDefault(team, false);
|
|
}
|
|
|
|
public void setBedBroken(BedwarsTeam team, boolean broken) {
|
|
teamBeds.put(team, !broken);
|
|
plugin.getScoreboardManager().updateAll();
|
|
}
|
|
|
|
public boolean isSpectator(Player player) {
|
|
return spectators.contains(player.getUniqueId());
|
|
}
|
|
|
|
public int getKills(Player player) {
|
|
return kills.getOrDefault(player.getUniqueId(), 0);
|
|
}
|
|
|
|
public void addKill(Player player) {
|
|
kills.put(player.getUniqueId(), getKills(player) + 1);
|
|
plugin.getScoreboardManager().updateScoreboard(player);
|
|
}
|
|
|
|
public int getBedsBroken(Player player) {
|
|
return bedsBroken.getOrDefault(player.getUniqueId(), 0);
|
|
}
|
|
|
|
public void addBedBroken(Player player) {
|
|
bedsBroken.put(player.getUniqueId(), getBedsBroken(player) + 1);
|
|
plugin.getScoreboardManager().updateScoreboard(player);
|
|
}
|
|
|
|
public void resetStats() {
|
|
playerTeams.clear();
|
|
spectators.clear();
|
|
eliminatedTeams.clear();
|
|
eliminatedPlayers.clear();
|
|
kills.clear();
|
|
bedsBroken.clear();
|
|
lastAttacker.clear();
|
|
lastAttackTime.clear();
|
|
lastDamageType.clear();
|
|
for (BedwarsTeam team : BedwarsTeam.values()) {
|
|
teamBeds.put(team, false); // Disabled until game starts
|
|
}
|
|
if (plugin.getUpgradesManager() != null) {
|
|
plugin.getUpgradesManager().resetTeamUpgrades();
|
|
}
|
|
if (plugin.getShopManager() != null) {
|
|
plugin.getShopManager().resetAllShopData();
|
|
}
|
|
gameTimeTicks = 0;
|
|
|
|
// Show all players to all players again
|
|
for (Player p1 : Bukkit.getOnlinePlayers()) {
|
|
for (Player p2 : Bukkit.getOnlinePlayers()) {
|
|
p1.showPlayer(plugin, p2);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Attempts to start the lobby countdown.
|
|
*/
|
|
public void checkStart() {
|
|
if (state != GameState.LOBBY || countingDown) return;
|
|
|
|
int minPlayers = plugin.getConfig().getInt("min-players", 2);
|
|
if (Bukkit.getOnlinePlayers().size() >= minPlayers) {
|
|
startCountdown();
|
|
}
|
|
}
|
|
|
|
private void startCountdown() {
|
|
countingDown = true;
|
|
countdownTime = plugin.getConfig().getInt("countdown-seconds", 30);
|
|
|
|
countdownTask = new BukkitRunnable() {
|
|
@Override
|
|
public void run() {
|
|
if (Bukkit.getOnlinePlayers().size() < plugin.getConfig().getInt("min-players", 2)) {
|
|
cancelCountdown();
|
|
Bukkit.broadcastMessage("§cNot enough players! Countdown cancelled.");
|
|
return;
|
|
}
|
|
|
|
if (countdownTime <= 0) {
|
|
startGame();
|
|
cancel();
|
|
return;
|
|
}
|
|
|
|
if (countdownTime == 30 || countdownTime == 15 || countdownTime == 10 || countdownTime <= 5) {
|
|
Bukkit.broadcastMessage("§eThe match will begin in §a" + countdownTime + " §eseconds!");
|
|
for (Player p : Bukkit.getOnlinePlayers()) {
|
|
p.playSound(p.getLocation(), Sound.BLOCK_NOTE_BLOCK_PLING, 1.0f, 1.0f);
|
|
}
|
|
}
|
|
|
|
countdownTime--;
|
|
plugin.getScoreboardManager().updateAll();
|
|
}
|
|
}.runTaskTimer(plugin, 0L, 20L);
|
|
}
|
|
|
|
public void cancelCountdown() {
|
|
countingDown = false;
|
|
if (countdownTask != null) {
|
|
countdownTask.cancel();
|
|
}
|
|
plugin.getScoreboardManager().updateAll();
|
|
}
|
|
|
|
/**
|
|
* Core Game Start logic.
|
|
*/
|
|
public void startGame() {
|
|
setState(GameState.PLAYING);
|
|
countingDown = false;
|
|
|
|
// Remove waiting area glass cage
|
|
plugin.getArenaManager().removeWaitingLobby();
|
|
|
|
// Purge residual mobs from world region files
|
|
plugin.getArenaManager().clearAllMobs();
|
|
|
|
// 1. Setup active teams in the game
|
|
List<BedwarsTeam> activeTeams = new ArrayList<>();
|
|
ConfigurationSection teamsSec = plugin.getConfig().getConfigurationSection("locations.teams");
|
|
if (teamsSec != null) {
|
|
for (String key : teamsSec.getKeys(false)) {
|
|
if (teamsSec.getBoolean(key + ".enabled", false)) {
|
|
try {
|
|
BedwarsTeam t = BedwarsTeam.valueOf(key.toUpperCase());
|
|
activeTeams.add(t);
|
|
teamBeds.put(t, true); // Bed active!
|
|
} catch (Exception ignored) {}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (activeTeams.isEmpty()) {
|
|
Bukkit.broadcastMessage("§cError: No teams have been configured in /bw setup! Force ending game.");
|
|
endGame(null);
|
|
return;
|
|
}
|
|
|
|
// 2. Distribute players into teams
|
|
List<Player> players = new ArrayList<>(Bukkit.getOnlinePlayers());
|
|
Collections.shuffle(players);
|
|
|
|
int teamIndex = 0;
|
|
for (Player player : players) {
|
|
BedwarsTeam team = activeTeams.get(teamIndex);
|
|
setPlayerTeam(player, team);
|
|
teamIndex = (teamIndex + 1) % activeTeams.size();
|
|
|
|
// Set up player
|
|
player.setGameMode(GameMode.SURVIVAL);
|
|
player.setHealth(20.0);
|
|
player.setFoodLevel(20);
|
|
player.setSaturation(20.0f);
|
|
player.getInventory().clear();
|
|
player.getEnderChest().clear();
|
|
|
|
// Clear potion effects
|
|
for (org.bukkit.potion.PotionEffect effect : player.getActivePotionEffects()) {
|
|
player.removePotionEffect(effect.getType());
|
|
}
|
|
|
|
// Hide spectators from this player
|
|
hideAllSpectatorsFrom(player);
|
|
|
|
// Apply starting gear
|
|
giveStartingGear(player);
|
|
|
|
// Teleport to Team Spawn
|
|
teleportToTeamSpawn(player, team);
|
|
|
|
// Setup tab and display name colors
|
|
updateTabName(player);
|
|
|
|
player.sendMessage("§eMatch started! You are in team: " + team.getColorizedName());
|
|
player.playSound(player.getLocation(), Sound.ENTITY_ENDER_DRAGON_GROWL, 0.8f, 1.0f);
|
|
}
|
|
|
|
// Mark empty teams as eliminated immediately so they don't block victory
|
|
for (BedwarsTeam team : activeTeams) {
|
|
int teamSize = 0;
|
|
for (Player p : Bukkit.getOnlinePlayers()) {
|
|
if (getPlayerTeam(p) == team) {
|
|
teamSize++;
|
|
}
|
|
}
|
|
if (teamSize == 0) {
|
|
teamBeds.put(team, false);
|
|
eliminatedTeams.add(team);
|
|
}
|
|
}
|
|
|
|
// 3. Spawn Shop Keepers & Upgrades Shop Keepers
|
|
spawnNPCs(activeTeams);
|
|
|
|
// Place enderchests for active teams asynchronously to prevent synchronous chunk loading locks
|
|
String worldName = plugin.getConfig().getString("world-name", "bedwars");
|
|
World world = Bukkit.getWorld(worldName);
|
|
if (world != null) {
|
|
for (BedwarsTeam team : activeTeams) {
|
|
String teamName = team.getName().toLowerCase();
|
|
String path = "locations.teams." + teamName + ".enderchest";
|
|
if (plugin.getConfig().contains(path)) {
|
|
final double x = plugin.getConfig().getDouble(path + ".x");
|
|
final double y = plugin.getConfig().getDouble(path + ".y");
|
|
final double z = plugin.getConfig().getDouble(path + ".z");
|
|
world.getChunkAtAsync((int) x >> 4, (int) z >> 4).thenAccept(chunk -> {
|
|
chunk.getWorld().getBlockAt((int) x, (int) y, (int) z).setType(Material.ENDER_CHEST);
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
// 4. Load generators and start game loop task
|
|
plugin.getGeneratorManager().loadGenerators();
|
|
startGameLoop();
|
|
|
|
Bukkit.broadcastMessage("§a§lThe game has begun! Protect your bed!");
|
|
checkWinner();
|
|
}
|
|
|
|
private void giveStartingGear(Player player) {
|
|
player.getInventory().setItem(0, new ItemStack(Material.WOODEN_SWORD));
|
|
// Give leather armor colored with team dye
|
|
BedwarsTeam team = getPlayerTeam(player);
|
|
if (team != null) {
|
|
player.getInventory().setHelmet(createColoredArmor(Material.LEATHER_HELMET, team.getColor()));
|
|
player.getInventory().setChestplate(createColoredArmor(Material.LEATHER_CHESTPLATE, team.getColor()));
|
|
|
|
// Retrieve and equip permanent bought armor tier, falling back to team colored leather
|
|
int tier = plugin.getShopManager().getArmorTier(player);
|
|
if (tier > 0) {
|
|
plugin.getShopManager().equipArmorTier(player, tier);
|
|
} else {
|
|
player.getInventory().setLeggings(createColoredArmor(Material.LEATHER_LEGGINGS, team.getColor()));
|
|
player.getInventory().setBoots(createColoredArmor(Material.LEATHER_BOOTS, team.getColor()));
|
|
}
|
|
|
|
// Retrieve and give permanent tools (Shears, Pickaxe, Axe)
|
|
plugin.getShopManager().giveTools(player);
|
|
}
|
|
}
|
|
|
|
private ItemStack createColoredArmor(Material material, ChatColor chatColor) {
|
|
ItemStack item = new ItemStack(material);
|
|
org.bukkit.inventory.meta.LeatherArmorMeta meta = (org.bukkit.inventory.meta.LeatherArmorMeta) item.getItemMeta();
|
|
if (meta != null) {
|
|
Color color = Color.WHITE;
|
|
if (chatColor == ChatColor.RED) color = Color.RED;
|
|
else if (chatColor == ChatColor.BLUE) color = Color.BLUE;
|
|
else if (chatColor == ChatColor.DARK_PURPLE) color = Color.PURPLE;
|
|
else if (chatColor == ChatColor.YELLOW) color = Color.YELLOW;
|
|
|
|
meta.setColor(color);
|
|
meta.setUnbreakable(true);
|
|
item.setItemMeta(meta);
|
|
}
|
|
return item;
|
|
}
|
|
|
|
private void teleportToTeamSpawn(Player player, BedwarsTeam team) {
|
|
String colorName = team.getName().toLowerCase();
|
|
String worldName = plugin.getConfig().getString("world-name", "bedwars");
|
|
World w = Bukkit.getWorld(worldName);
|
|
if (w != null && plugin.getConfig().contains("locations.teams." + colorName + ".spawn")) {
|
|
double x = plugin.getConfig().getDouble("locations.teams." + colorName + ".spawn.x");
|
|
double y = plugin.getConfig().getDouble("locations.teams." + colorName + ".spawn.y");
|
|
double z = plugin.getConfig().getDouble("locations.teams." + colorName + ".spawn.z");
|
|
float yaw = (float) plugin.getConfig().getDouble("locations.teams." + colorName + ".spawn.yaw");
|
|
float pitch = (float) plugin.getConfig().getDouble("locations.teams." + colorName + ".spawn.pitch");
|
|
player.teleport(new Location(w, x, y, z, yaw, pitch));
|
|
} else {
|
|
// Fallback
|
|
player.teleport(w != null ? w.getSpawnLocation() : player.getWorld().getSpawnLocation());
|
|
}
|
|
}
|
|
|
|
private void spawnNPCs(List<BedwarsTeam> activeTeams) {
|
|
String worldName = plugin.getConfig().getString("world-name", "bedwars");
|
|
World world = Bukkit.getWorld(worldName);
|
|
if (world == null) return;
|
|
|
|
// Clear existing villager entities in the bedwars world
|
|
world.getEntitiesByClass(org.bukkit.entity.Villager.class).forEach(org.bukkit.entity.Entity::remove);
|
|
|
|
for (BedwarsTeam team : activeTeams) {
|
|
String path = "locations.teams." + team.getName().toLowerCase();
|
|
|
|
// 1. Spawn Item Shop Villager
|
|
if (plugin.getConfig().contains(path + ".shop")) {
|
|
double x = plugin.getConfig().getDouble(path + ".shop.x");
|
|
double y = plugin.getConfig().getDouble(path + ".shop.y");
|
|
double z = plugin.getConfig().getDouble(path + ".shop.z");
|
|
float yaw = (float) plugin.getConfig().getDouble(path + ".shop.yaw");
|
|
float pitch = (float) plugin.getConfig().getDouble(path + ".shop.pitch");
|
|
|
|
org.bukkit.entity.Villager villager = world.spawn(new Location(world, x, y, z, yaw, pitch), org.bukkit.entity.Villager.class);
|
|
villager.setCustomName("§a§lITEM SHOP\n§7Right Click");
|
|
villager.setCustomNameVisible(true);
|
|
villager.setProfession(org.bukkit.entity.Villager.Profession.ARMORER);
|
|
villager.setAI(false);
|
|
villager.setInvulnerable(true);
|
|
villager.setSilent(true);
|
|
}
|
|
|
|
// 2. Spawn Upgrades Shop Villager
|
|
if (plugin.getConfig().contains(path + ".upgrades")) {
|
|
double x = plugin.getConfig().getDouble(path + ".upgrades.x");
|
|
double y = plugin.getConfig().getDouble(path + ".upgrades.y");
|
|
double z = plugin.getConfig().getDouble(path + ".upgrades.z");
|
|
float yaw = (float) plugin.getConfig().getDouble(path + ".upgrades.yaw");
|
|
float pitch = (float) plugin.getConfig().getDouble(path + ".upgrades.pitch");
|
|
|
|
org.bukkit.entity.Villager villager = world.spawn(new Location(world, x, y, z, yaw, pitch), org.bukkit.entity.Villager.class);
|
|
villager.setCustomName("§b§lTEAM UPGRADES\n§7Right Click");
|
|
villager.setCustomNameVisible(true);
|
|
villager.setProfession(org.bukkit.entity.Villager.Profession.WEAPONSMITH);
|
|
villager.setAI(false);
|
|
villager.setInvulnerable(true);
|
|
villager.setSilent(true);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void startGameLoop() {
|
|
gameTimeTicks = 0;
|
|
gameLoopTask = new BukkitRunnable() {
|
|
@Override
|
|
public void run() {
|
|
if (state != GameState.PLAYING) {
|
|
cancel();
|
|
return;
|
|
}
|
|
|
|
// Tick generators
|
|
plugin.getGeneratorManager().tick();
|
|
|
|
// Increment ticks and check game duration (20 minutes = 24000 ticks)
|
|
gameTimeTicks++;
|
|
|
|
// Custom regeneration every 5 seconds (100 ticks)
|
|
if (gameTimeTicks % 100 == 0) {
|
|
for (Player player : Bukkit.getOnlinePlayers()) {
|
|
if (!isSpectator(player) && !eliminatedPlayers.contains(player.getUniqueId())) {
|
|
if (!player.isDead() && player.getHealth() > 0) {
|
|
double maxHealth = player.getMaxHealth();
|
|
double currentHealth = player.getHealth();
|
|
if (currentHealth < maxHealth) {
|
|
player.setHealth(Math.min(maxHealth, currentHealth + 1.0));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int secondsLeft = (24000 - gameTimeTicks) / 20;
|
|
if (gameTimeTicks < 24000 && gameTimeTicks % 20 == 0) {
|
|
// This runs once every second
|
|
if (secondsLeft == 300) { // 5 minutes
|
|
Bukkit.broadcastMessage("§c§lWARNING! §eThe game will end in §a5 minutes§e!");
|
|
} else if (secondsLeft == 60) { // 1 minute
|
|
Bukkit.broadcastMessage("§c§lWARNING! §eThe game will end in §a1 minute§e!");
|
|
} else if (secondsLeft == 30) {
|
|
Bukkit.broadcastMessage("§c§lWARNING! §eThe game will end in §a30 seconds§e!");
|
|
} else if (secondsLeft == 10) {
|
|
Bukkit.broadcastMessage("§c§lWARNING! §eThe game will end in §a10 seconds§e!");
|
|
} else if (secondsLeft > 0 && secondsLeft <= 5) {
|
|
Bukkit.broadcastMessage("§c§lWARNING! §eThe game will end in §c" + secondsLeft + " §eseconds!");
|
|
for (Player p : Bukkit.getOnlinePlayers()) {
|
|
p.playSound(p.getLocation(), Sound.BLOCK_NOTE_BLOCK_PLING, 1.0f, 1.0f);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (gameTimeTicks >= 24000) {
|
|
Bukkit.broadcastMessage("§c§lTIME UP! §eThe game has ended in a draw.");
|
|
endGame(null);
|
|
cancel();
|
|
}
|
|
}
|
|
}.runTaskTimer(plugin, 0L, 1L);
|
|
}
|
|
|
|
/**
|
|
* Handles a player's death, respawn loop, or final elimination.
|
|
*/
|
|
public void handlePlayerDeath(Player player, Player killer) {
|
|
BedwarsTeam team = getPlayerTeam(player);
|
|
if (team == null) return;
|
|
|
|
// Check if player died in the void
|
|
boolean inVoid = player.getLocation().getY() <= -20.0 || (player.getLastDamageCause() != null && player.getLastDamageCause().getCause() == EntityDamageEvent.DamageCause.VOID);
|
|
|
|
// Handle tool levels and shears on death
|
|
plugin.getShopManager().handlePlayerDeath(player);
|
|
|
|
// Drop player resources or transfer to killer if in void
|
|
if (inVoid && killer != null) {
|
|
giveResourcesToKiller(player, killer);
|
|
} else {
|
|
dropPlayerResources(player);
|
|
}
|
|
|
|
// Determine death message
|
|
String deathMsg = getDeathMessage(player, killer, inVoid);
|
|
boolean isFinal = !hasBed(team);
|
|
String finalSuffix = isFinal ? " §c§lFINAL KILL!" : "";
|
|
Bukkit.broadcastMessage("§7" + deathMsg + finalSuffix);
|
|
|
|
// Clear combat tracking
|
|
clearCombatTag(player);
|
|
|
|
// Record stats
|
|
if (killer != null) {
|
|
addKill(killer);
|
|
killer.sendMessage("§eYou killed " + team.getColor() + player.getName() + "!");
|
|
killer.playSound(killer.getLocation(), Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 0.8f, 1.4f);
|
|
}
|
|
|
|
// Check if player has bed
|
|
if (hasBed(team)) {
|
|
// Can respawn!
|
|
startRespawnSequence(player, team);
|
|
} else {
|
|
// Eliminated!
|
|
eliminatePlayer(player);
|
|
checkWinner();
|
|
}
|
|
}
|
|
|
|
public void handlePlayerQuit(Player player) {
|
|
if (state != GameState.PLAYING) return;
|
|
if (isSpectator(player) || eliminatedPlayers.contains(player.getUniqueId())) return;
|
|
|
|
BedwarsTeam team = getPlayerTeam(player);
|
|
if (team == null) return;
|
|
|
|
// Eliminate player
|
|
eliminatedPlayers.add(player.getUniqueId());
|
|
spectators.add(player.getUniqueId());
|
|
|
|
// Broadcast quit final kill/elimination
|
|
Bukkit.broadcastMessage(team.getColor() + player.getName() + " §c§lquit and was eliminated!");
|
|
|
|
// Drop player resources at their last location
|
|
dropPlayerResources(player);
|
|
|
|
// Check if team is eliminated and check winner
|
|
checkWinner();
|
|
}
|
|
|
|
public String getFormattedName(Player player) {
|
|
BedwarsTeam team = getPlayerTeam(player);
|
|
if (team != null) {
|
|
return team.getColor() + player.getName() + "§7";
|
|
}
|
|
return "§7" + player.getName();
|
|
}
|
|
|
|
private String getDeathMessage(Player player, Player killer, boolean inVoid) {
|
|
String victimName = getFormattedName(player);
|
|
|
|
if (killer != null) {
|
|
String killerName = getFormattedName(killer);
|
|
LastDamageType type = lastDamageType.getOrDefault(player.getUniqueId(), LastDamageType.OTHER);
|
|
|
|
if (inVoid) {
|
|
switch (type) {
|
|
case FIREBALL:
|
|
return victimName + " was fireballed into the void by " + killerName + ".";
|
|
case TNT:
|
|
return victimName + " was blown into the void by " + killerName + ".";
|
|
case PROJECTILE:
|
|
return victimName + " was shot into the void by " + killerName + ".";
|
|
case MELEE:
|
|
default:
|
|
return victimName + " was knocked into the void by " + killerName + ".";
|
|
}
|
|
} else {
|
|
switch (type) {
|
|
case FIREBALL:
|
|
return victimName + " was fireballed by " + killerName + ".";
|
|
case TNT:
|
|
return victimName + " was blown up by " + killerName + ".";
|
|
case PROJECTILE:
|
|
return victimName + " was shot by " + killerName + ".";
|
|
case MELEE:
|
|
default:
|
|
return victimName + " was slain by " + killerName + ".";
|
|
}
|
|
}
|
|
} else {
|
|
if (inVoid) {
|
|
return victimName + " fell into the void.";
|
|
} else {
|
|
EntityDamageEvent lastDamage = player.getLastDamageCause();
|
|
if (lastDamage != null) {
|
|
EntityDamageEvent.DamageCause cause = lastDamage.getCause();
|
|
if (cause == EntityDamageEvent.DamageCause.FALL) {
|
|
return victimName + " fell to their death.";
|
|
} else if (cause == EntityDamageEvent.DamageCause.BLOCK_EXPLOSION || cause == EntityDamageEvent.DamageCause.ENTITY_EXPLOSION) {
|
|
return victimName + " was blown up.";
|
|
}
|
|
}
|
|
return victimName + " died.";
|
|
}
|
|
}
|
|
}
|
|
|
|
private void dropPlayerResources(Player player) {
|
|
Location loc = player.getLocation();
|
|
for (ItemStack item : player.getInventory().getContents()) {
|
|
if (item != null) {
|
|
Material type = item.getType();
|
|
if (type == Material.IRON_INGOT || type == Material.GOLD_INGOT ||
|
|
type == Material.DIAMOND || type == Material.EMERALD) {
|
|
loc.getWorld().dropItemNaturally(loc, item.clone());
|
|
}
|
|
}
|
|
}
|
|
player.getInventory().clear();
|
|
}
|
|
|
|
private void giveResourcesToKiller(Player player, Player killer) {
|
|
for (ItemStack item : player.getInventory().getContents()) {
|
|
if (item != null) {
|
|
Material type = item.getType();
|
|
if (type == Material.IRON_INGOT || type == Material.GOLD_INGOT ||
|
|
type == Material.DIAMOND || type == Material.EMERALD) {
|
|
// Give directly to killer's inventory
|
|
HashMap<Integer, ItemStack> remaining = killer.getInventory().addItem(item.clone());
|
|
if (!remaining.isEmpty()) {
|
|
// Drop any items that don't fit at killer's feet
|
|
for (ItemStack overflow : remaining.values()) {
|
|
killer.getWorld().dropItemNaturally(killer.getLocation(), overflow);
|
|
}
|
|
}
|
|
// Send informative message to killer
|
|
killer.sendMessage("§aReceived " + item.getAmount() + "x " + formatMaterialName(type) + " §afrom " + player.getName() + "'s void death!");
|
|
}
|
|
}
|
|
}
|
|
player.getInventory().clear();
|
|
}
|
|
|
|
private String formatMaterialName(Material material) {
|
|
switch (material) {
|
|
case IRON_INGOT:
|
|
return "§fIron";
|
|
case GOLD_INGOT:
|
|
return "§eGold";
|
|
case DIAMOND:
|
|
return "§bDiamond";
|
|
case EMERALD:
|
|
return "§aEmerald";
|
|
default:
|
|
return material.name();
|
|
}
|
|
}
|
|
|
|
public void setLastAttacker(Player victim, Player attacker, LastDamageType type) {
|
|
lastAttacker.put(victim.getUniqueId(), attacker.getUniqueId());
|
|
lastAttackTime.put(victim.getUniqueId(), System.currentTimeMillis());
|
|
lastDamageType.put(victim.getUniqueId(), type);
|
|
}
|
|
|
|
public Player getLastAttacker(Player player) {
|
|
UUID victimId = player.getUniqueId();
|
|
if (lastAttacker.containsKey(victimId)) {
|
|
long time = lastAttackTime.getOrDefault(victimId, 0L);
|
|
if (System.currentTimeMillis() - time <= 15000) { // 15 seconds combat window
|
|
UUID attackerId = lastAttacker.get(victimId);
|
|
return Bukkit.getPlayer(attackerId);
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public void clearCombatTag(Player player) {
|
|
lastAttacker.remove(player.getUniqueId());
|
|
lastAttackTime.remove(player.getUniqueId());
|
|
lastDamageType.remove(player.getUniqueId());
|
|
}
|
|
|
|
private void startRespawnSequence(Player player, BedwarsTeam team) {
|
|
spectators.add(player.getUniqueId());
|
|
player.setGameMode(GameMode.SPECTATOR);
|
|
player.teleport(player.getLocation().clone().add(0, 5, 0));
|
|
updateTabName(player);
|
|
|
|
BukkitTask task = new BukkitRunnable() {
|
|
int time = 5;
|
|
|
|
@Override
|
|
public void run() {
|
|
if (state != GameState.PLAYING || !player.isOnline()) {
|
|
cancel();
|
|
return;
|
|
}
|
|
|
|
if (time <= 0) {
|
|
respawnPlayer(player, team);
|
|
cancel();
|
|
return;
|
|
}
|
|
|
|
player.sendTitle("§cYOU DIED!", "§eRespawning in §a" + time + "s", 0, 22, 0);
|
|
player.playSound(player.getLocation(), Sound.UI_BUTTON_CLICK, 0.8f, 1.2f);
|
|
time--;
|
|
}
|
|
}.runTaskTimer(plugin, 0L, 20L);
|
|
|
|
respawnTasks.add(task);
|
|
}
|
|
|
|
private void respawnPlayer(Player player, BedwarsTeam team) {
|
|
spectators.remove(player.getUniqueId());
|
|
player.setGameMode(GameMode.SURVIVAL);
|
|
player.setHealth(20.0);
|
|
player.setFoodLevel(20);
|
|
player.setSaturation(20.0f);
|
|
player.getInventory().clear();
|
|
|
|
// Clear potion effects
|
|
for (org.bukkit.potion.PotionEffect effect : player.getActivePotionEffects()) {
|
|
player.removePotionEffect(effect.getType());
|
|
}
|
|
|
|
// Hide all active spectators from this player
|
|
hideAllSpectatorsFrom(player);
|
|
|
|
// Gear
|
|
giveStartingGear(player);
|
|
|
|
// Apply passive team upgrades (Haste, Sharpness, Protection)
|
|
plugin.getUpgradesManager().applyTeamUpgrades(player);
|
|
|
|
teleportToTeamSpawn(player, team);
|
|
updateTabName(player);
|
|
player.sendTitle("§a§lRESPAWNED!", "§fGo get them!", 5, 20, 5);
|
|
player.playSound(player.getLocation(), Sound.ENTITY_PLAYER_LEVELUP, 0.8f, 1.5f);
|
|
}
|
|
|
|
private void eliminatePlayer(Player player) {
|
|
spectators.add(player.getUniqueId());
|
|
eliminatedPlayers.add(player.getUniqueId());
|
|
player.setGameMode(GameMode.CREATIVE);
|
|
player.getInventory().clear();
|
|
giveSpectatorItems(player);
|
|
hideSpectator(player);
|
|
updateTabName(player);
|
|
player.sendTitle("§c§lELIMINATED!", "§7You have no respawns left!", 10, 40, 10);
|
|
player.playSound(player.getLocation(), Sound.ENTITY_LIGHTNING_BOLT_THUNDER, 0.8f, 0.8f);
|
|
}
|
|
|
|
public void addSpectator(Player player) {
|
|
spectators.add(player.getUniqueId());
|
|
player.setGameMode(GameMode.CREATIVE);
|
|
player.getInventory().clear();
|
|
giveSpectatorItems(player);
|
|
hideSpectator(player);
|
|
updateTabName(player);
|
|
plugin.getScoreboardManager().updateAll();
|
|
}
|
|
|
|
public void giveSpectatorItems(Player player) {
|
|
player.getInventory().clear();
|
|
|
|
ItemStack compass = new ItemStack(Material.COMPASS);
|
|
org.bukkit.inventory.meta.ItemMeta m1 = compass.getItemMeta();
|
|
if (m1 != null) {
|
|
m1.setDisplayName("§a§lTeleporter §7(Right Click)");
|
|
compass.setItemMeta(m1);
|
|
}
|
|
player.getInventory().setItem(0, compass);
|
|
|
|
ItemStack bed = new ItemStack(Material.RED_BED);
|
|
org.bukkit.inventory.meta.ItemMeta m2 = bed.getItemMeta();
|
|
if (m2 != null) {
|
|
m2.setDisplayName("§c§lReturn to Lobby §7(Right Click)");
|
|
bed.setItemMeta(m2);
|
|
}
|
|
player.getInventory().setItem(8, bed);
|
|
}
|
|
|
|
public void hideSpectator(Player player) {
|
|
for (Player p : Bukkit.getOnlinePlayers()) {
|
|
if (!isSpectator(p)) {
|
|
p.hidePlayer(plugin, player);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void hideAllSpectatorsFrom(Player alivePlayer) {
|
|
for (UUID specId : spectators) {
|
|
Player spec = Bukkit.getPlayer(specId);
|
|
if (spec != null && spec.isOnline()) {
|
|
alivePlayer.hidePlayer(plugin, spec);
|
|
}
|
|
}
|
|
}
|
|
|
|
public boolean isTeamEnabled(BedwarsTeam team) {
|
|
return plugin.getConfig().getBoolean("locations.teams." + team.getName().toLowerCase() + ".enabled", false);
|
|
}
|
|
|
|
public int getAliveTeamCount(BedwarsTeam team) {
|
|
int alive = 0;
|
|
for (Player p : Bukkit.getOnlinePlayers()) {
|
|
if (getPlayerTeam(p) == team && !eliminatedPlayers.contains(p.getUniqueId())) {
|
|
alive++;
|
|
}
|
|
}
|
|
return alive;
|
|
}
|
|
|
|
/**
|
|
* Checks if only one team is left alive.
|
|
*/
|
|
public void checkWinner() {
|
|
if (state != GameState.PLAYING) return;
|
|
|
|
List<BedwarsTeam> activeTeams = new ArrayList<>();
|
|
ConfigurationSection teamsSec = plugin.getConfig().getConfigurationSection("locations.teams");
|
|
if (teamsSec != null) {
|
|
for (String key : teamsSec.getKeys(false)) {
|
|
if (teamsSec.getBoolean(key + ".enabled", false)) {
|
|
activeTeams.add(BedwarsTeam.valueOf(key.toUpperCase()));
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check for team eliminations (if getAliveTeamCount(team) == 0, even if there is a bed, the team is eliminated!)
|
|
for (BedwarsTeam team : activeTeams) {
|
|
if (!eliminatedTeams.contains(team)) {
|
|
if (getAliveTeamCount(team) == 0) {
|
|
eliminatedTeams.add(team);
|
|
teamBeds.put(team, false); // No bed active if team has no alive players!
|
|
Bukkit.broadcastMessage("§r");
|
|
Bukkit.broadcastMessage("§d§lTEAM ELIMINATED! §fTeam " + team.getColor() + team.name() + " §fhas been §c§lELIMINATED§f!");
|
|
Bukkit.broadcastMessage("§r");
|
|
}
|
|
}
|
|
}
|
|
|
|
BedwarsTeam winningTeam = null;
|
|
int activeTeamCount = 0;
|
|
|
|
for (BedwarsTeam team : activeTeams) {
|
|
// A team is active if they have at least one alive player
|
|
if (getAliveTeamCount(team) > 0) {
|
|
activeTeamCount++;
|
|
winningTeam = team;
|
|
}
|
|
}
|
|
|
|
// If there is only one player online, do not end the game so they can test features alone!
|
|
if (Bukkit.getOnlinePlayers().size() > 1) {
|
|
if (activeTeamCount <= 1) {
|
|
endGame(winningTeam);
|
|
}
|
|
}
|
|
}
|
|
|
|
private BedwarsTeam winnerTeam = null;
|
|
|
|
public BedwarsTeam getWinnerTeam() {
|
|
return winnerTeam;
|
|
}
|
|
|
|
/**
|
|
* Game Over, show winner, celebrate, redirect to proxy lobby server, reset world.
|
|
*/
|
|
public void endGame(BedwarsTeam winner) {
|
|
setState(GameState.ENDING);
|
|
this.winnerTeam = winner;
|
|
|
|
if (winner != null) {
|
|
Bukkit.broadcastMessage(" §b§lVICTORY!");
|
|
Bukkit.broadcastMessage(" " + winner.getColorizedName() + " §fhas won the match!");
|
|
|
|
for (Player p : Bukkit.getOnlinePlayers()) {
|
|
p.sendTitle("§6§lVICTORY!", winner.getColorizedName() + " §fhas won!", 10, 80, 10);
|
|
p.playSound(p.getLocation(), Sound.UI_TOAST_CHALLENGE_COMPLETE, 0.8f, 1.0f);
|
|
}
|
|
|
|
// Spawn fireworks
|
|
String colorName = winner.getName().toLowerCase();
|
|
String worldName = plugin.getConfig().getString("world-name", "bedwars");
|
|
World w = Bukkit.getWorld(worldName);
|
|
if (w != null && plugin.getConfig().contains("locations.teams." + colorName + ".spawn")) {
|
|
double x = plugin.getConfig().getDouble("locations.teams." + colorName + ".spawn.x");
|
|
double y = plugin.getConfig().getDouble("locations.teams." + colorName + ".spawn.y");
|
|
double z = plugin.getConfig().getDouble("locations.teams." + colorName + ".spawn.z");
|
|
Location spawnLoc = new Location(w, x, y + 2, z);
|
|
|
|
new BukkitRunnable() {
|
|
int fireworkCount = 10;
|
|
@Override
|
|
public void run() {
|
|
if (fireworkCount <= 0 || state != GameState.ENDING) {
|
|
cancel();
|
|
return;
|
|
}
|
|
|
|
Firework fw = spawnLoc.getWorld().spawn(spawnLoc.clone().add(Math.random()*4-2, 0, Math.random()*4-2), Firework.class);
|
|
FireworkMeta meta = fw.getFireworkMeta();
|
|
meta.addEffect(FireworkEffect.builder()
|
|
.withColor(winner.getColor() == ChatColor.RED ? Color.RED : winner.getColor() == ChatColor.BLUE ? Color.BLUE : winner.getColor() == ChatColor.DARK_PURPLE ? Color.PURPLE : Color.YELLOW)
|
|
.with(FireworkEffect.Type.BALL_LARGE)
|
|
.flicker(true)
|
|
.trail(true)
|
|
.build());
|
|
meta.setPower(1);
|
|
fw.setFireworkMeta(meta);
|
|
fireworkCount--;
|
|
}
|
|
}.runTaskTimer(plugin, 0L, 10L);
|
|
}
|
|
} else {
|
|
Bukkit.broadcastMessage("§cThe game ended in a draw!");
|
|
}
|
|
|
|
// Start 10 seconds ending countdown before unloading and resetting world
|
|
new BukkitRunnable() {
|
|
int timeLeft = 10;
|
|
|
|
@Override
|
|
public void run() {
|
|
if (timeLeft <= 0) {
|
|
// Kick all players to trigger proxy redirection
|
|
for (Player p : Bukkit.getOnlinePlayers()) {
|
|
p.kickPlayer("§a§lGAME OVER!\n\n§fThe game has ended.\n§eWinner: " + (winner != null ? winner.getColorizedName() : "§cDraw") + "\n\n§bThank you for playing!");
|
|
}
|
|
|
|
// Schedule world reset shortly after kicking so connection is fully released
|
|
Bukkit.getScheduler().runTaskLater(plugin, () -> {
|
|
// Cleanup generators and NPCs
|
|
plugin.getGeneratorManager().cleanup();
|
|
|
|
// Reset the world map
|
|
plugin.getArenaManager().resetWorld();
|
|
|
|
// Cancel all tasks
|
|
for (BukkitTask task : respawnTasks) {
|
|
task.cancel();
|
|
}
|
|
respawnTasks.clear();
|
|
|
|
if (gameLoopTask != null) {
|
|
gameLoopTask.cancel();
|
|
}
|
|
|
|
// Reinitialize variables
|
|
resetStats();
|
|
winnerTeam = null;
|
|
eliminatedTeams.clear();
|
|
setState(GameState.LOBBY);
|
|
|
|
// Check if players are already inside waiting lobby (in case they joined during reset)
|
|
checkStart();
|
|
}, 40L);
|
|
|
|
cancel();
|
|
return;
|
|
}
|
|
|
|
if (timeLeft <= 5) {
|
|
Bukkit.broadcastMessage("§eGame closing and resetting in §a" + timeLeft + " §eseconds...");
|
|
for (Player p : Bukkit.getOnlinePlayers()) {
|
|
p.playSound(p.getLocation(), Sound.BLOCK_NOTE_BLOCK_PLING, 1.0f, 1.2f);
|
|
}
|
|
}
|
|
timeLeft--;
|
|
}
|
|
}.runTaskTimer(plugin, 0L, 20L);
|
|
}
|
|
|
|
/**
|
|
* Sends players back to the configured lobby proxy server.
|
|
*/
|
|
public void sendToLobby(Player player) {
|
|
String serverName = plugin.getConfig().getString("lobby-server", "lobby");
|
|
player.sendMessage("§b§lNEXORIA §fsending you back to the lobby...");
|
|
try {
|
|
// 1. Send standard BungeeCord packets
|
|
com.google.common.io.ByteArrayDataOutput bungeeOut = com.google.common.io.ByteStreams.newDataOutput();
|
|
bungeeOut.writeUTF("Connect");
|
|
bungeeOut.writeUTF(serverName);
|
|
player.sendPluginMessage(plugin, "BungeeCord", bungeeOut.toByteArray());
|
|
player.sendPluginMessage(plugin, "bungeecord:main", bungeeOut.toByteArray());
|
|
|
|
// 2. Send custom nexoria direct channel packet
|
|
com.google.common.io.ByteArrayDataOutput nexoriaOut = com.google.common.io.ByteStreams.newDataOutput();
|
|
nexoriaOut.writeUTF("Connect");
|
|
nexoriaOut.writeUTF(player.getName());
|
|
nexoriaOut.writeUTF(serverName);
|
|
player.sendPluginMessage(plugin, "nexoria:main", nexoriaOut.toByteArray());
|
|
} catch (Exception e) {
|
|
player.sendMessage("§cFailed to redirect to proxy lobby: " + e.getMessage());
|
|
}
|
|
}
|
|
|
|
public void resetGameImmediately() {
|
|
// Cleanup generators and NPCs
|
|
plugin.getGeneratorManager().cleanup();
|
|
|
|
// Reset the world map
|
|
plugin.getArenaManager().resetWorld();
|
|
|
|
// Cancel all tasks
|
|
for (BukkitTask task : respawnTasks) {
|
|
task.cancel();
|
|
}
|
|
respawnTasks.clear();
|
|
|
|
if (gameLoopTask != null) {
|
|
gameLoopTask.cancel();
|
|
}
|
|
|
|
// Reinitialize variables
|
|
resetStats();
|
|
winnerTeam = null;
|
|
eliminatedTeams.clear();
|
|
setState(GameState.LOBBY);
|
|
countingDown = false;
|
|
if (countdownTask != null) {
|
|
countdownTask.cancel();
|
|
}
|
|
}
|
|
}
|