Delete src/main/java/de/lasertec/game/Game.java via Git Manager GUI

This commit is contained in:
2026-06-20 19:28:57 +00:00
parent c5c4e77578
commit c54dfddeb3

View File

@@ -1,733 +0,0 @@
package de.lasertec.game;
import de.lasertec.LasertecPlugin;
import de.lasertec.arena.Arena;
import de.lasertec.camp.AntiCampManager;
import de.lasertec.player.LaserPlayer;
import de.lasertec.protection.ModProtectionManager;
import de.lasertec.weapon.WeaponType;
import de.lasertec.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 LasertecPlugin 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<UUID, LaserPlayer> players = new ConcurrentHashMap<>();
private final Map<Team, Integer> teamScore = new LinkedHashMap<>();
private final Map<UUID, Long> shotCooldown= new ConcurrentHashMap<>();
private BukkitTask countdownTask, gameTask, healTask;
public Game(LasertecPlugin 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⚡ LASERTEC", "§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<UUID, LaserPlayer> 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<UUID, LaserPlayer> 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: Farbige Wolle als Kopfblock.
* Wolle ist auf dem Kopf viel auffälliger als ein Leder-Helm,
* weil die satte Blockfarbe aus jeder Entfernung sichtbar ist.
*/
private ItemStack makeHelm(Team team) {
ItemStack wool = new ItemStack(team.getWoolMat());
ItemMeta meta = wool.getItemMeta();
meta.setDisplayName(team.getChatColor() + "Team " + team.getDisplayName() + " Helm");
meta.setLore(java.util.List.of(
"§8Lasertec " + team.colored() + " §8Team",
"§8Rüstung kann nicht abgelegt werden"
));
meta.setUnbreakable(true);
meta.addItemFlags(ItemFlag.HIDE_ATTRIBUTES, ItemFlag.HIDE_UNBREAKABLE,
ItemFlag.HIDE_ENCHANTS);
wool.setItemMeta(meta);
return wool;
}
/**
* 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(
"§8Lasertec " + 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<Team,Integer> 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 ⚡ LASERTEC 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<UUID, LaserPlayer> getPlayers() { return players; }
public Map<Team, Integer> 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<Player> getOnline() {
List<Player> 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();
}
}