diff --git a/src/main/java/de/lasertag/game/Game.java b/src/main/java/de/lasertag/game/Game.java deleted file mode 100644 index b8b1a2c..0000000 --- a/src/main/java/de/lasertag/game/Game.java +++ /dev/null @@ -1,736 +0,0 @@ -package de.lasertag.game; - -import de.lasertag.LasertagPlugin; -import de.lasertag.arena.Arena; -import de.lasertag.camp.AntiCampManager; -import de.lasertag.player.LaserPlayer; -import de.lasertag.protection.ModProtectionManager; -import de.lasertag.weapon.WeaponType; -import de.lasertag.weapon.WeaponUtil; -import net.md_5.bungee.api.ChatMessageType; -import net.md_5.bungee.api.chat.TextComponent; -import org.bukkit.*; -import org.bukkit.entity.Player; -import org.bukkit.inventory.ItemFlag; -import org.bukkit.inventory.ItemStack; -import org.bukkit.inventory.meta.ItemMeta; -import org.bukkit.inventory.meta.LeatherArmorMeta; -import org.bukkit.inventory.meta.SkullMeta; -import org.bukkit.potion.PotionEffect; -import org.bukkit.potion.PotionEffectType; -import org.bukkit.scheduler.BukkitTask; -import org.bukkit.util.RayTraceResult; -import org.bukkit.util.Vector; - -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; - -public class Game { - - private final LasertagPlugin plugin; - private final Arena arena; - private final AntiCampManager antiCamp; - private final ModProtectionManager modProtect; - - private GameState state = GameState.WAITING; - private int timeLeft; - private int countdown; - - private final Map players = new ConcurrentHashMap<>(); - private final Map teamScore = new LinkedHashMap<>(); - private final Map shotCooldown= new ConcurrentHashMap<>(); - - private BukkitTask countdownTask, gameTask, healTask; - - public Game(LasertagPlugin plugin, Arena arena) { - this.plugin = plugin; - this.arena = arena; - this.antiCamp = new AntiCampManager(plugin); - this.modProtect = new ModProtectionManager(plugin); - this.timeLeft = plugin.getConfigManager().getGameDuration(); - this.countdown = plugin.getConfigManager().getCountdown(); - for (Team t : Team.values()) teamScore.put(t, 0); - arena.initBaseHealth(plugin.getConfigManager().getBaseHealth()); - } - - // ────────────────────────────────────────────────────────────────────────── - // SPIELER-VERWALTUNG - // ────────────────────────────────────────────────────────────────────────── - - public boolean addPlayer(Player player) { - if (players.containsKey(player.getUniqueId())) return false; - if (!isJoinable()) return false; - int maxTotal = plugin.getConfigManager().getMaxPlayersPerTeam() * 4; - if (players.size() >= maxTotal) return false; - - LaserPlayer lp = new LaserPlayer(player.getUniqueId(), player.getName()); - lp.setTeam(pickBalancedTeam()); - lp.setSavedInventory(player.getInventory().getContents().clone()); - players.put(player.getUniqueId(), lp); - - setupPlayer(player, lp); - sendToSpawn(player, lp.getTeam()); - modProtect.applyProtection(player, this); - - broadcast(plugin.getConfigManager().getText("join", - "§e{player} §7hat Team {team} §7beigetreten.", - "player", player.getName(), "team", lp.getTeam().colored())); - - plugin.getScoreboardManager().update(player, this); - - if (state == GameState.WAITING - && players.size() >= plugin.getConfigManager().getMinPlayers()) { - startCountdown(); - } - return true; - } - - public void removePlayer(Player player) { - LaserPlayer lp = players.remove(player.getUniqueId()); - if (lp == null) return; - shotCooldown.remove(player.getUniqueId()); - antiCamp.remove(player.getUniqueId()); - modProtect.removeProtection(player); - - saveStats(lp, player); - restorePlayer(player, lp); - plugin.getScoreboardManager().remove(player); - - broadcast(plugin.getConfigManager().getText("leave", - "§c{player} §7hat das Spiel verlassen.", "player", player.getName())); - - if (state == GameState.RUNNING - && players.size() < plugin.getConfigManager().getMinPlayers()) { - endGame(); - } - } - - private void saveStats(LaserPlayer lp, Player player) { - var stats = plugin.getPlayerDataManager().getStats(lp.getUuid()); - stats.setName(player.getName()); - stats.apply(lp); - } - - // ────────────────────────────────────────────────────────────────────────── - // SPIEL-ABLAUF - // ────────────────────────────────────────────────────────────────────────── - - public void startCountdown() { - if (state != GameState.WAITING) return; - state = GameState.STARTING; - - final int[] cd = { plugin.getConfigManager().getCountdown() }; - countdownTask = Bukkit.getScheduler().runTaskTimer(plugin, () -> { - if (cd[0] <= 0) { countdownTask.cancel(); startGame(); return; } - if (cd[0] <= 5 || cd[0] == 10) { - broadcast("§eSpiel startet in §b" + cd[0] + " §eSekunden!"); - playAll(Sound.BLOCK_NOTE_BLOCK_PLING, 1f, 1f); - } - float prog = (float) cd[0] / plugin.getConfigManager().getCountdown(); - for (Player p : getOnline()) p.setExp(Math.max(0f, Math.min(1f, prog))); - cd[0]--; - }, 0L, 20L); - } - - public void startGame() { - state = GameState.RUNNING; - placeBaseBlocks(); - - for (Player p : getOnline()) { - p.sendTitle("§b§l⚡ LASERTAG", "§7Verteidige eure Basis!", 10, 50, 10); - p.setExp(1f); - } - playAll(plugin.getConfigManager().getSound("game-start", Sound.ENTITY_ENDER_DRAGON_GROWL), 0.5f, 1.2f); - broadcast(plugin.getConfigManager().getText("game-start", "§a§l⚡ DAS SPIEL BEGINNT!")); - - // Heal-Check + AntiCamp: jede Sekunde - healTask = Bukkit.getScheduler().runTaskTimer(plugin, () -> { - tickHeal(); - antiCamp.tick(this); - }, 0L, 20L); - - // Haupt-Timer - final int[] tl = { plugin.getConfigManager().getGameDuration() }; - gameTask = Bukkit.getScheduler().runTaskTimer(plugin, () -> { - if (tl[0] <= 0) { gameTask.cancel(); endGame(); return; } - if (tl[0] == 60) broadcast(plugin.getConfigManager().getText("time-60", "§e⏰ Noch §b60 §eSekunden!")); - if (tl[0] == 30) broadcast(plugin.getConfigManager().getText("time-30", "§c⏰ Noch §b30 §eSekunden!")); - if (tl[0] == 10) broadcast(plugin.getConfigManager().getText("time-10", "§4⏰ Noch §b10 §4Sekunden!")); - - // Basis regenerieren? - if (plugin.getConfigManager().isBaseRegenerate()) { - int interval = plugin.getConfigManager().getBaseRegenInterval(); - if (tl[0] % interval == 0) { - for (Team t : Team.values()) { - int hp = arena.getBaseHealth(t); - int max = plugin.getConfigManager().getBaseHealth(); - if (hp < max && hp > 0) { - arena.damageBase(t); // Missbrauche: erhöhe via setBaseHealth direkt - // Eigentlich: arena.regenBase(t); — wir ergänzen das - } - } - } - } - - timeLeft = tl[0]; - for (Player p : getOnline()) { - plugin.getScoreboardManager().update(p, this); - sendActionBar(p); - } - tl[0]--; - }, 0L, 20L); - } - - public void endGame() { - if (state == GameState.ENDING) return; - state = GameState.ENDING; - cancelTasks(); - removeBaseBlocks(); - modProtect.removeAll(this); - antiCamp.reset(); - - Team winner = getLeader(); - for (Player p : getOnline()) { - LaserPlayer lp = players.get(p.getUniqueId()); - boolean won = winner != null && lp != null && lp.getTeam() == winner; - p.sendTitle(won ? "§a§lSIEG!" : "§c§lNIEDERLAGE!", - winner != null ? "Team " + winner.colored() + " §7gewinnt!" : "§7Unentschieden!", - 10, 80, 20); - p.playSound(p.getLocation(), - won ? Sound.UI_TOAST_CHALLENGE_COMPLETE : Sound.ENTITY_VILLAGER_NO, 1f, 1f); - showEndStats(p, lp); - saveStats(lp, p); - } - plugin.getPlayerDataManager().saveAll(); - - String endMsg = winner != null - ? plugin.getConfigManager().getText("game-end", "§6§l🏆 Team {team} §6§lhat gewonnen!", - "team", winner.colored()) - : plugin.getConfigManager().getText("game-draw", "§7Unentschieden!"); - broadcast(endMsg); - - int delay = plugin.getConfigManager().getEndDisplayTime() * 20; - Bukkit.getScheduler().runTaskLater(plugin, this::reset, delay); - } - - private void reset() { - new ArrayList<>(getOnline()).forEach(this::removePlayer); - state = GameState.WAITING; - timeLeft = plugin.getConfigManager().getGameDuration(); - countdown = plugin.getConfigManager().getCountdown(); - players.clear(); - shotCooldown.clear(); - antiCamp.reset(); - for (Team t : Team.values()) teamScore.put(t, 0); - arena.initBaseHealth(plugin.getConfigManager().getBaseHealth()); - } - - // ────────────────────────────────────────────────────────────────────────── - // SCHUSS-MECHANIK - // ────────────────────────────────────────────────────────────────────────── - - public boolean handleShot(Player shooter, WeaponType weapon) { - LaserPlayer lp = players.get(shooter.getUniqueId()); - if (lp == null || state != GameState.RUNNING) return false; - - if (lp.isHit()) { - sendActionBarMsg(shooter, "§c☠ GETROFFEN – Gehe zur §e" - + lp.getTeam().colored() + " §cBasis!"); - playSound(shooter, Sound.BLOCK_NOTE_BLOCK_DIDGERIDOO, 0.5f, 0.5f); - return false; - } - - long now = System.currentTimeMillis(); - long last = shotCooldown.getOrDefault(shooter.getUniqueId(), 0L); - int cooldown = plugin.getConfigManager().getWeaponCooldown(weapon.getConfigKey()); - if (now - last < cooldown) { - double rem = (cooldown - (now - last)) / 1000.0; - sendActionBarMsg(shooter, "§c⏳ Nachladen... §7(" + String.format("%.1f", rem) + "s)"); - return false; - } - shotCooldown.put(shooter.getUniqueId(), now); - - int pellets = plugin.getConfigManager().getWeaponPellets(weapon.getConfigKey()); - if (pellets > 1) { - for (int i = 0; i < pellets; i++) shootRay(shooter, lp, weapon, spreadDir(shooter)); - } else { - shootRay(shooter, lp, weapon, shooter.getEyeLocation().getDirection()); - } - - Sound shootSnd = plugin.getConfigManager().getSound("shoot", Sound.ENTITY_FIREWORK_ROCKET_BLAST); - float shootPitch = plugin.getConfigManager().getSoundPitch("shoot-pitch", 1.8f); - float shootVol = plugin.getConfigManager().getSoundVolume("shoot-volume", 0.4f); - if (shootSnd != null) shooter.getWorld().playSound(shooter.getLocation(), shootSnd, shootVol, shootPitch); - return true; - } - - private void shootRay(Player shooter, LaserPlayer shooterLp, WeaponType weapon, Vector dir) { - Location eye = shooter.getEyeLocation(); - int range = plugin.getConfigManager().getWeaponRange(weapon.getConfigKey()); - - if (plugin.getConfigManager().isLaserTrailEnabled()) - drawLaser(eye, dir, weapon, range); - - RayTraceResult result = shooter.getWorld().rayTrace( - eye, dir, range, FluidCollisionMode.NEVER, true, 0.5, - e -> e instanceof Player && !e.equals(shooter)); - if (result == null) return; - - // ── Spieler getroffen ───────────────────────────────────────────────── - if (result.getHitEntity() instanceof Player victim) { - LaserPlayer vLp = players.get(victim.getUniqueId()); - if (vLp == null || vLp.isHit()) return; - if (vLp.getTeam() == shooterLp.getTeam()) return; - - vLp.applyHit(); - - // Partikel - if (plugin.getConfigManager().isHitEffectEnabled()) { - victim.getWorld().spawnParticle(Particle.CRIT, - victim.getLocation().add(0,1,0), - plugin.getConfigManager().getHitParticleCount(), - 0.4, 0.4, 0.4, 0.1); - } - - // Sounds - playSound(victim, plugin.getConfigManager().getSound("hit-victim", Sound.ENTITY_PLAYER_HURT), 1f, 0.8f); - playSound(shooter, plugin.getConfigManager().getSound("hit-shooter", Sound.BLOCK_NOTE_BLOCK_BELL), 1f, 2f); - - // Punkte - int pts = plugin.getConfigManager().getKillPoints(); - int streak = shooterLp.getKillStreak() + 1; - int bonus = 0; - if (streak == 3) bonus = plugin.getConfigManager().getStreak3Bonus(); - if (streak == 5) bonus = plugin.getConfigManager().getStreak5Bonus(); - if (streak == 10) bonus = plugin.getConfigManager().getStreak10Bonus(); - - shooterLp.registerKill(); - shooterLp.addScore(pts + bonus); - teamScore.merge(shooterLp.getTeam(), pts + bonus, Integer::sum); - - String streakInfo = streak >= 3 ? " §6[" + streak + "er-Serie!+"+bonus+"]" : ""; - shooter.sendMessage(plugin.getConfigManager().getText("hit-shooter", - "§aDu hast §e{victim} §agetroffen! §7(+{pts} Pkt){streak}", - "victim", victim.getName(), "pts", pts + bonus, "streak", streakInfo)); - victim.sendMessage(plugin.getConfigManager().getText("hit-victim", - "§cDu wurdest von §e{shooter} §cgetroffen!", - "shooter", shooter.getName())); - - // Streak-Broadcast - if (streak == 3) broadcastStreak(shooter, "streak-3", "§6TRIPLE KILL!", 3); - if (streak == 5) broadcastStreak(shooter, "streak-5", "§bPENTA KILL!", 5); - if (streak == 10) broadcastStreak(shooter, "streak-10", "§5§lGODLIKE!", 10); - - sendActionBar(victim); - sendActionBar(shooter); - plugin.getScoreboardManager().update(shooter, this); - plugin.getScoreboardManager().update(victim, this); - return; - } - - // ── Block getroffen (Basisangriff) ──────────────────────────────────── - if (result.getHitBlock() != null) { - Team baseTeam = arena.getBaseTeam(result.getHitBlock().getLocation()); - if (baseTeam != null && baseTeam != shooterLp.getTeam()) { - handleBaseAttack(shooter, shooterLp, baseTeam, - result.getHitBlock().getLocation()); - } - } - } - - // ────────────────────────────────────────────────────────────────────────── - // HEAL-TICK - // ────────────────────────────────────────────────────────────────────────── - - private void tickHeal() { - double baseRadiusSq = Math.pow(plugin.getConfigManager().getBaseRadius(), 2); - int healTimeSec = plugin.getConfigManager().getBaseHealTime(); - - for (Map.Entry e : players.entrySet()) { - Player p = Bukkit.getPlayer(e.getKey()); - LaserPlayer lp = e.getValue(); - if (p == null || !lp.isHit()) continue; - - Location base = arena.getBase(lp.getTeam()); - if (base == null) continue; - - boolean near = p.getLocation().distanceSquared(base) <= baseRadiusSq; - - if (near) { - if (!lp.isHealing()) { - lp.startHealing(); - p.sendMessage(plugin.getConfigManager().getText("heal-start", - "§aHeilung gestartet...", "secs", healTimeSec)); - playSound(p, plugin.getConfigManager().getSound("heal-start", - Sound.BLOCK_ENCHANTMENT_TABLE_USE), 1f, 1f); - } else if (lp.healElapsedMs() >= healTimeSec * 1000L) { - lp.completeHeal(); - p.sendMessage(plugin.getConfigManager().getText("heal-complete", - "§a✔ Du bist wieder einsatzbereit!")); - Sound hs = plugin.getConfigManager().getSound("heal-complete", - Sound.ENTITY_EXPERIENCE_ORB_PICKUP); - float hp = plugin.getConfigManager().getSoundPitch("heal-complete-pitch", 1.5f); - playSound(p, hs, 1f, hp); - p.sendTitle("§a§lBEREIT!", "§7Du kannst wieder schießen!", 5, 30, 5); - if (plugin.getConfigManager().isInvincibleAfterHeal()) { - p.addPotionEffect(new PotionEffect(PotionEffectType.DAMAGE_RESISTANCE, - plugin.getConfigManager().getInvincibleDuration(), 4, false, false)); - } - if (plugin.getConfigManager().isHealEffectEnabled()) { - p.getWorld().spawnParticle(Particle.VILLAGER_HAPPY, - p.getLocation().add(0,1,0), 20, 0.5, 0.5, 0.5, 0.05); - } - } else { - double prog = lp.healElapsedMs() / (healTimeSec * 1000.0); - sendActionBarMsg(p, "§a🛡 Heilung: " + buildBar(prog, 15) - + " §7" + String.format("%.1f", lp.healElapsedMs()/1000.0) - + "§8/" + healTimeSec + "s"); - } - } else { - if (lp.isHealing()) { - lp.stopHealing(); - p.sendMessage(plugin.getConfigManager().getText("heal-interrupted", - "§c⚠ Heilung abgebrochen!")); - } - Location bl = base; - double dist = p.getLocation().distance(bl); - sendActionBarMsg(p, "§c☠ GETROFFEN – Zur §e" + lp.getTeam().colored() - + " §cBasis! §8(" + String.format("%.0f",dist) + "m)"); - } - } - } - - // ────────────────────────────────────────────────────────────────────────── - // BASIS-ANGRIFF - // ────────────────────────────────────────────────────────────────────────── - - private void handleBaseAttack(Player shooter, LaserPlayer lp, Team baseTeam, Location blockLoc) { - if (arena.isBaseDestroyed(baseTeam)) return; - boolean damaged = arena.damageBase(baseTeam); - if (!damaged) return; - - int pts = plugin.getConfigManager().getBaseAttackPoints(); - int hp = arena.getBaseHealth(baseTeam); - int maxHp = plugin.getConfigManager().getBaseHealth(); - lp.addScore(pts); - lp.addBaseAttack(); - teamScore.merge(lp.getTeam(), pts, Integer::sum); - - updateBaseBlock(baseTeam, hp, maxHp); - - if (plugin.getConfigManager().isParticlesEnabled()) { - blockLoc.getWorld().spawnParticle(Particle.EXPLOSION_LARGE, - blockLoc.clone().add(0.5,0.5,0.5), 3, 0.2,0.2,0.2,0.05); - } - Sound bhs = plugin.getConfigManager().getSound("base-hit", Sound.ENTITY_GENERIC_EXPLODE); - if (bhs != null) blockLoc.getWorld().playSound(blockLoc, bhs, 0.5f, 1.5f); - - shooter.sendMessage(plugin.getConfigManager().getPrefix() + "§6⚔ Basis von Team " - + baseTeam.colored() + " §6angegriffen! §7(+" + pts + " Pkt) §8[HP: " + hp + "/" + maxHp + "]"); - - // Warn-HP aus Config - int warnHp = plugin.getConfigManager().getBaseWarnHp(); - - for (Map.Entry e : players.entrySet()) { - if (e.getValue().getTeam() != baseTeam) continue; - Player p = Bukkit.getPlayer(e.getKey()); - if (p == null) continue; - p.sendMessage(plugin.getConfigManager().getText("base-attacked", - "§c⚠ Eure Basis wird angegriffen! §8[HP: {hp}/{max}]", - "hp", hp, "max", maxHp)); - if (hp <= warnHp) - p.sendTitle("§c§l⚠ BASIS KRITISCH!", "§7HP: " + hp + "/" + maxHp, 5, 40, 10); - playSound(p, plugin.getConfigManager().getSound("warning", Sound.BLOCK_BELL_USE), 1f, 0.5f); - } - - broadcast(plugin.getConfigManager().getPrefix() + "§6⚔ §e" + shooter.getName() - + " §6hat die Basis von Team " + baseTeam.colored() - + " §6getroffen! §8(" + hp + "/" + maxHp + ")"); - - if (hp <= 0) { - broadcast(plugin.getConfigManager().getText("base-destroyed", - "§c§l💥 Die Basis von Team {team} §c§lwurde ZERSTÖRT!", - "team", baseTeam.colored())); - int bonus = plugin.getConfigManager().getBaseDestroyBonus(); - lp.addScore(bonus); - teamScore.merge(lp.getTeam(), bonus, Integer::sum); - shooter.sendMessage(plugin.getConfigManager().getPrefix() - + "§6§l+" + bonus + " Bonuspunkte für Basis-Zerstörung!"); - playAll(Sound.ENTITY_ENDER_DRAGON_GROWL, 0.5f, 0.7f); - } - - plugin.getScoreboardManager().update(shooter, this); - } - - // ────────────────────────────────────────────────────────────────────────── - // SPIELER SETUP - // ────────────────────────────────────────────────────────────────────────── - - private void setupPlayer(Player player, LaserPlayer lp) { - player.setGameMode(GameMode.ADVENTURE); - player.getInventory().clear(); - player.setHealth(20); player.setFoodLevel(20); player.setSaturation(20f); - for (PotionEffect e : player.getActivePotionEffects()) - player.removePotionEffect(e.getType()); - - int slot = 0; - for (WeaponType w : WeaponType.values()) { - if (plugin.getConfigManager().isWeaponEnabled(w.getConfigKey())) { - player.getInventory().setItem(slot++, WeaponUtil.create(plugin, w)); - } - } - player.getInventory().setHeldItemSlot(0); - giveArmor(player, lp.getTeam()); - } - - private void giveArmor(Player player, Team team) { - // Helm: farbiger Woll-Block als Kopfbedeckung — auf einen Blick erkennbar - player.getInventory().setHelmet (makeHelm(team)); - player.getInventory().setChestplate(coloredLeather(Material.LEATHER_CHESTPLATE, team)); - player.getInventory().setLeggings (coloredLeather(Material.LEATHER_LEGGINGS, team)); - player.getInventory().setBoots (coloredLeather(Material.LEATHER_BOOTS, team)); - } - - /** - * Helm: Leder-Helm mit kräftiger Teamfarbe (RGB). - * Enchantment-Glanz macht ihn zusätzlich im Dunkeln erkennbar. - */ - private ItemStack makeHelm(Team team) { - ItemStack item = new ItemStack(Material.LEATHER_HELMET); - LeatherArmorMeta meta = (LeatherArmorMeta) item.getItemMeta(); - meta.setColor(team.getArmorColor()); - meta.setDisplayName(team.getChatColor() + "§l" + team.getDisplayName().toUpperCase() - + " §r" + team.getChatColor() + "Helm"); - meta.setLore(java.util.List.of( - "§8Lasertag – " + team.colored() + " §8Team", - "§8Rüstung kann nicht abgelegt werden" - )); - // Glanz damit der Helm optisch hervorsticht - meta.addEnchant(org.bukkit.enchantments.Enchantment.PROTECTION_ENVIRONMENTAL, 1, true); - meta.setUnbreakable(true); - meta.addItemFlags(ItemFlag.HIDE_ATTRIBUTES, ItemFlag.HIDE_UNBREAKABLE, - ItemFlag.HIDE_ENCHANTS); - item.setItemMeta(meta); - return item; - } - - /** - * Lederrüstung mit kräftiger Teamfarbe (RGB) und Lore. - * Brustplatte, Hose und Stiefel sind farbig — zusammen mit dem - * Woll-Helm ergibt sich ein eindeutiges Team-Outfit. - */ - private ItemStack coloredLeather(Material mat, Team team) { - String partName = switch (mat) { - case LEATHER_CHESTPLATE -> "Brustplatte"; - case LEATHER_LEGGINGS -> "Hose"; - case LEATHER_BOOTS -> "Stiefel"; - default -> "Rüstung"; - }; - ItemStack item = new ItemStack(mat); - LeatherArmorMeta meta = (LeatherArmorMeta) item.getItemMeta(); - meta.setColor(team.getArmorColor()); - meta.setDisplayName(team.getChatColor() + "Team " + team.getDisplayName() - + " – " + partName); - meta.setLore(java.util.List.of( - "§8Lasertag – " + team.colored() + " §8Team", - "§8Rüstung kann nicht abgelegt werden" - )); - meta.setUnbreakable(true); - meta.addItemFlags(ItemFlag.HIDE_ATTRIBUTES, ItemFlag.HIDE_UNBREAKABLE, - ItemFlag.HIDE_ENCHANTS); - item.setItemMeta(meta); - return item; - } - - private void restorePlayer(Player player, LaserPlayer lp) { - player.getInventory().clear(); - if (lp.getSavedInventory() != null) - player.getInventory().setContents(lp.getSavedInventory()); - player.setGameMode(GameMode.SURVIVAL); - player.setHealth(20); player.setFoodLevel(20); - for (PotionEffect e : player.getActivePotionEffects()) - player.removePotionEffect(e.getType()); - Location lobby = plugin.getConfigManager().getLobbyLocation(); - if (lobby != null) player.teleport(lobby); - } - - private void sendToSpawn(Player player, Team team) { - Location spawn = arena.getRandomSpawn(team); - if (spawn != null) player.teleport(spawn); - } - - // ────────────────────────────────────────────────────────────────────────── - // BASIS-BLÖCKE - // ────────────────────────────────────────────────────────────────────────── - - private void placeBaseBlocks() { - for (Team t : Team.values()) { - Location loc = arena.getBase(t); - if (loc != null) loc.getBlock().setType(t.getGlassMat()); - } - } - - private void updateBaseBlock(Team team, int hp, int maxHp) { - Location loc = arena.getBase(team); - if (loc == null) return; - if (hp <= 0) loc.getBlock().setType(Material.OBSIDIAN); - else if (hp < maxHp / 2.0) loc.getBlock().setType(team.getWoolMat()); - if (plugin.getConfigManager().isParticlesEnabled()) - loc.getWorld().spawnParticle(Particle.SMOKE_LARGE, - loc.clone().add(0.5,1.2,0.5), 5, 0.3,0.3,0.3,0.02); - } - - private void removeBaseBlocks() { - for (Team t : Team.values()) { - Location loc = arena.getBase(t); - if (loc == null) continue; - Material m = loc.getBlock().getType(); - if (m == t.getGlassMat() || m == t.getWoolMat() || m == Material.OBSIDIAN) - loc.getBlock().setType(Material.AIR); - } - } - - // ────────────────────────────────────────────────────────────────────────── - // HILFSMETHODEN - // ────────────────────────────────────────────────────────────────────────── - - private void drawLaser(Location start, Vector dir, WeaponType weapon, int range) { - var particle = plugin.getConfigManager().getWeaponParticle(weapon.getConfigKey()); - Location cur = start.clone(); - Vector step = dir.clone().normalize().multiply(0.6); - for (int i = 0; i < range * 1.7; i++) { - cur.add(step); - if (!cur.getBlock().isPassable()) break; - cur.getWorld().spawnParticle(particle, cur.clone(), 1, 0,0,0,0); - } - } - - private Vector spreadDir(Player player) { - Vector base = player.getEyeLocation().getDirection(); - double s = 0.12; - return base.clone().add(new Vector( - (Math.random()-0.5)*s, (Math.random()-0.5)*s*0.7, (Math.random()-0.5)*s - )).normalize(); - } - - private Team pickBalancedTeam() { - Map counts = new LinkedHashMap<>(); - for (Team t : Team.values()) counts.put(t, 0); - for (LaserPlayer lp : players.values()) counts.merge(lp.getTeam(), 1, Integer::sum); - return counts.entrySet().stream().min(Map.Entry.comparingByValue()).map(Map.Entry::getKey).orElse(Team.RED); - } - - private Team getLeader() { - return teamScore.entrySet().stream().max(Map.Entry.comparingByValue()) - .map(Map.Entry::getKey).orElse(null); - } - - private void cancelTasks() { - if (countdownTask != null) countdownTask.cancel(); - if (gameTask != null) gameTask.cancel(); - if (healTask != null) healTask.cancel(); - } - - private void sendActionBar(Player player) { - LaserPlayer lp = players.get(player.getUniqueId()); - if (lp == null) return; - if (!lp.isHit()) { - int idle = antiCamp.getIdleSeconds(player.getUniqueId()); - int max = plugin.getConfigManager().getCampMaxIdleSecs(); - String campBar = idle > 0 - ? " §c| Camp: " + buildBar(1.0 - (double)idle/max, 6) - : ""; - sendActionBarMsg(player, "§7Kills: §a" + lp.getKills() - + " §7Punkte: §6" + lp.getScore() - + " §7Team: " + lp.getTeam().colored() - + " §7Zeit: §b" + formatTime(timeLeft) - + campBar); - } - // Wenn getroffen: wird in tickHeal() gesetzt - } - - private void sendActionBarMsg(Player player, String msg) { - player.spigot().sendMessage(ChatMessageType.ACTION_BAR, TextComponent.fromLegacyText(msg)); - } - - private void broadcastStreak(Player player, String textKey, String fallbackTitle, int streak) { - broadcast(plugin.getConfigManager().getText(textKey, - fallbackTitle + " §e{player} §7– " + streak + "er Serie!", - "player", player.getName())); - for (Player p : getOnline()) - p.sendTitle(fallbackTitle, "§e" + player.getName(), 5, 40, 10); - } - - private void showEndStats(Player p, LaserPlayer lp) { - if (lp == null) return; - p.sendMessage("§8§l══════════════════════════════"); - p.sendMessage("§b§l ⚡ LASERTAG ERGEBNIS"); - p.sendMessage("§8§l══════════════════════════════"); - p.sendMessage("§7Team: " + lp.getTeam().colored()); - p.sendMessage("§7Kills: §a" + lp.getKills()); - p.sendMessage("§7Tode: §c" + lp.getDeaths()); - p.sendMessage("§7Basis-Angriffe: §6" + lp.getBaseAttacks()); - p.sendMessage("§7Punkte: §6" + lp.getScore()); - p.sendMessage("§7Beste Serie: §b" + lp.getBestStreak()); - p.sendMessage("§8──────────────────────────────"); - p.sendMessage("§7Team-Punkte:"); - teamScore.entrySet().stream() - .sorted((a,b) -> Integer.compare(b.getValue(), a.getValue())) - .forEach(e -> p.sendMessage(" " + e.getKey().colored() - + " §7→ §6" + e.getValue() + " Punkte")); - p.sendMessage("§8§l══════════════════════════════"); - } - - private String buildBar(double progress, int length) { - int filled = Math.max(0, Math.min(length, (int)Math.round(progress * length))); - return "§a" + "█".repeat(filled) + "§8" + "░".repeat(length - filled); - } - - private String formatTime(int secs) { - return String.format("%d:%02d", secs/60, secs%60); - } - - private void broadcast(String msg) { - for (Player p : getOnline()) p.sendMessage(msg); - } - - private void playAll(Sound sound, float vol, float pitch) { - if (sound == null) return; - for (Player p : getOnline()) p.playSound(p.getLocation(), sound, vol, pitch); - } - - private void playSound(Player p, Sound s, float vol, float pitch) { - if (s != null) p.playSound(p.getLocation(), s, vol, pitch); - } - - // ────────────────────────────────────────────────────────────────────────── - // GETTERS - // ────────────────────────────────────────────────────────────────────────── - - public Arena getArena() { return arena; } - public GameState getState() { return state; } - public Map getPlayers() { return players; } - public Map getTeamScore() { return teamScore; } - public int getTimeLeft() { return timeLeft; } - public LaserPlayer getLP(UUID uid) { return players.get(uid); } - public boolean isRunning() { return state == GameState.RUNNING; } - public boolean isJoinable() { return state == GameState.WAITING || state == GameState.STARTING; } - public int getPlayerCount() { return players.size(); } - public int getMaxPlayers() { return plugin.getConfigManager().getMaxPlayersPerTeam() * 4; } - public AntiCampManager getAntiCamp() { return antiCamp; } - - public List getOnline() { - List list = new ArrayList<>(); - for (UUID uid : players.keySet()) { - Player p = Bukkit.getPlayer(uid); - if (p != null) list.add(p); - } - return list; - } - - public void forceEnd() { endGame(); } - public void forceStart() { - if (state == GameState.WAITING) startCountdown(); - else if (state == GameState.STARTING) startGame(); - } -}