Update from Git Manager GUI
This commit is contained in:
@@ -281,7 +281,7 @@ public class FussballCommand implements CommandExecutor, TabCompleter {
|
||||
}
|
||||
|
||||
// ── Hologramm-Verwaltung ─────────────────────────────────────────
|
||||
// /fb hologram set <id> goals|wins – Hologramm erstellen
|
||||
// /fb hologram set <id> goals|wins|match – Hologramm erstellen
|
||||
// /fb hologram remove – Nächstes Hologramm (< 5 Blöcke) entfernen
|
||||
// /fb hologram delete <id> – Hologramm nach ID löschen
|
||||
// /fb hologram reload – Alle Hologramme neu spawnen
|
||||
@@ -291,7 +291,7 @@ public class FussballCommand implements CommandExecutor, TabCompleter {
|
||||
if (!(sender instanceof Player player)) { sender.sendMessage("Nur für Spieler!"); return true; }
|
||||
if (args.length < 2) {
|
||||
player.sendMessage(MessageUtil.header("Hologramm-Befehle"));
|
||||
player.sendMessage("§e/fb hologram set <id> goals|wins §7– Hologramm setzen");
|
||||
player.sendMessage("§e/fb hologram set <id> goals|wins|match §7– Hologramm setzen");
|
||||
player.sendMessage("§e/fb hologram remove §7– Nächstes entfernen (< 5 Blöcke)");
|
||||
player.sendMessage("§e/fb hologram delete <id> §7– Nach ID löschen");
|
||||
player.sendMessage("§e/fb hologram reload §7– Alle neu laden");
|
||||
@@ -303,18 +303,27 @@ public class FussballCommand implements CommandExecutor, TabCompleter {
|
||||
switch (args[1].toLowerCase()) {
|
||||
case "set" -> {
|
||||
if (args.length < 4) {
|
||||
player.sendMessage(MessageUtil.error("Benutze: /fb hologram set <id> goals|wins"));
|
||||
player.sendMessage(MessageUtil.error("Benutze: /fb hologram set <id> goals|wins|match"));
|
||||
return true;
|
||||
}
|
||||
String id = args[2];
|
||||
FussballHologram.HoloType type = switch (args[3].toLowerCase()) {
|
||||
case "wins", "siege" -> FussballHologram.HoloType.WINS;
|
||||
case "match", "live", "game" -> FussballHologram.HoloType.MATCH;
|
||||
default -> FussballHologram.HoloType.GOALS;
|
||||
};
|
||||
plugin.getHologramManager().createHologram(id, player.getLocation(), type);
|
||||
String holoLabel = type == FussballHologram.HoloType.WINS ? "Top-10-Siege" : "Top-10-Tore";
|
||||
String holoLabel = switch (type) {
|
||||
case WINS -> "Top-10-Siege";
|
||||
case MATCH -> "Live-Match";
|
||||
default -> "Top-10-Tore";
|
||||
};
|
||||
player.sendMessage(MessageUtil.success("§e" + id + " §a(" + holoLabel + ") Hologramm gesetzt!"));
|
||||
player.sendMessage("§7§oRechtsklick auf das Hologramm wechselt zwischen Tore und Siege.");
|
||||
if (type == FussballHologram.HoloType.MATCH) {
|
||||
player.sendMessage("§7§oLive-Match-Hologramm aktualisiert sich automatisch bei Toren und Nachspielzeit.");
|
||||
} else {
|
||||
player.sendMessage("§7§oRechtsklick auf das Hologramm wechselt zwischen Tore und Siege.");
|
||||
}
|
||||
}
|
||||
case "remove" -> {
|
||||
String removed = plugin.getHologramManager().removeNearest(player.getLocation());
|
||||
@@ -346,7 +355,7 @@ public class FussballCommand implements CommandExecutor, TabCompleter {
|
||||
}
|
||||
}
|
||||
}
|
||||
default -> player.sendMessage(MessageUtil.error("Gültig: set <id> goals|wins | remove | delete <id> | reload | list"));
|
||||
default -> player.sendMessage(MessageUtil.error("Gültig: set <id> goals|wins|match | remove | delete <id> | reload | list"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -485,7 +494,7 @@ public class FussballCommand implements CommandExecutor, TabCompleter {
|
||||
s.sendMessage("§e/fb history [n] §7- Letzte Spiele anzeigen");
|
||||
if (s.hasPermission("fussball.admin")) {
|
||||
s.sendMessage("§c§lAdmin: §ccreate / delete / setup / stop / debug / dropball");
|
||||
s.sendMessage("§c§lAdmin: §chologram set goals|wins / remove / reload");
|
||||
s.sendMessage("§c§lAdmin: §chologram set goals|wins|match / remove / reload");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -519,7 +528,7 @@ public class FussballCommand implements CommandExecutor, TabCompleter {
|
||||
} else if (args.length == 3 && args[0].equalsIgnoreCase("hologram") && args[1].equalsIgnoreCase("set")) {
|
||||
list.addAll(plugin.getArenaManager().getArenaNames()); // id-Vorschläge (frei wählbar, aber arena-namen passen)
|
||||
} else if (args.length == 4 && args[0].equalsIgnoreCase("hologram") && args[1].equalsIgnoreCase("set")) {
|
||||
list.addAll(List.of("goals", "wins"));
|
||||
list.addAll(List.of("goals", "wins", "match"));
|
||||
} else if (args.length == 3 && args[0].equalsIgnoreCase("hologram") && args[1].equalsIgnoreCase("delete")) {
|
||||
list.addAll(plugin.getHologramManager().getHologramIds());
|
||||
} else if (args.length == 3 && args[0].equalsIgnoreCase("setgk")) {
|
||||
|
||||
@@ -16,6 +16,8 @@ import org.bukkit.boss.BossBar;
|
||||
import org.bukkit.entity.Firework;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.FireworkEffect;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.BlockFace;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.FireworkMeta;
|
||||
import org.bukkit.potion.PotionEffect;
|
||||
@@ -94,6 +96,8 @@ public class Game {
|
||||
private int spawnCooldown = 0;
|
||||
private int ballMissingTicks = 0; // Ticks ohne lebenden Ball → Respawn
|
||||
private static final int BALL_MISSING_TIMEOUT = 80; // 4s
|
||||
private static final int GOAL_BEACON_SCAN_RADIUS = 10;
|
||||
private static final int GOAL_BEACON_MAX_PER_SIDE = 4;
|
||||
/** Ticks, in denen der Anstoß-Kreis erzwungen wird (Gegner müssen Abstand halten) */
|
||||
private int kickoffEnforceTicks = 0;
|
||||
|
||||
@@ -356,6 +360,7 @@ public class Game {
|
||||
secondHalf = false;
|
||||
timeLeft = arena.getGameDuration();
|
||||
secondsPlayed = 0;
|
||||
updateGoalBeaconColors();
|
||||
|
||||
bossBar = Bukkit.createBossBar("§e⚽ §c0 §7: §90 §8| §e05:00", BarColor.GREEN, BarStyle.SOLID);
|
||||
bossBar.setProgress(1.0);
|
||||
@@ -438,6 +443,7 @@ public class Game {
|
||||
state = GameState.RUNNING;
|
||||
secondHalf = true;
|
||||
timeLeft = arena.getGameDuration() / 2;
|
||||
updateGoalBeaconColors();
|
||||
// Nachspielzeit der 1. Halbzeit zurücksetzen
|
||||
injuryTimeBuffer = 0;
|
||||
inInjuryTime = false;
|
||||
@@ -487,6 +493,7 @@ public class Game {
|
||||
state = GameState.OVERTIME;
|
||||
overtimeDone = true;
|
||||
timeLeft = 600; // 2x5min = 10 Min Verlängerung
|
||||
updateGoalBeaconColors();
|
||||
|
||||
if (ball != null) ball.remove();
|
||||
spawnBallDelayed(arena.getBallSpawn());
|
||||
@@ -891,8 +898,7 @@ public class Game {
|
||||
|
||||
switch (side) {
|
||||
case "side" -> {
|
||||
resumeLocation = arena.clampToField(outLocation);
|
||||
resumeLocation.setY(outLocation.getY());
|
||||
resumeLocation = moveInsideField(arena.clampToField(outLocation), 1.25);
|
||||
if (touchTeam == null) {
|
||||
throwInTeam = null;
|
||||
message = "§e⚽ §7Ball im Aus! §7Einwurf!";
|
||||
@@ -905,28 +911,28 @@ public class Game {
|
||||
}
|
||||
case "redEnd" -> {
|
||||
if (touchTeam == Team.RED) {
|
||||
resumeLocation = getCornerLocation(outLocation);
|
||||
resumeLocation = moveInsideField(getCornerLocation(outLocation), 1.25);
|
||||
throwInTeam = Team.BLUE;
|
||||
message = "§e⚽ §7Ball im Aus! §9Ecke für Blaues Team§7!";
|
||||
} else {
|
||||
resumeLocation = arena.getRedSpawn() != null ? arena.getRedSpawn() : arena.getBallSpawn();
|
||||
resumeLocation = moveInsideField(arena.getRedSpawn() != null ? arena.getRedSpawn() : arena.getBallSpawn(), 1.25);
|
||||
throwInTeam = Team.RED;
|
||||
message = "§e⚽ §7Ball im Aus! §cAbstoß für Rotes Team§7!";
|
||||
}
|
||||
}
|
||||
case "blueEnd" -> {
|
||||
if (touchTeam == Team.BLUE) {
|
||||
resumeLocation = getCornerLocation(outLocation);
|
||||
resumeLocation = moveInsideField(getCornerLocation(outLocation), 1.25);
|
||||
throwInTeam = Team.RED;
|
||||
message = "§e⚽ §7Ball im Aus! §cEcke für Rotes Team§7!";
|
||||
} else {
|
||||
resumeLocation = arena.getBlueSpawn() != null ? arena.getBlueSpawn() : arena.getBallSpawn();
|
||||
resumeLocation = moveInsideField(arena.getBlueSpawn() != null ? arena.getBlueSpawn() : arena.getBallSpawn(), 1.25);
|
||||
throwInTeam = Team.BLUE;
|
||||
message = "§e⚽ §7Ball im Aus! §9Abstoß für Blaues Team§7!";
|
||||
}
|
||||
}
|
||||
default -> {
|
||||
resumeLocation = arena.getBallSpawn();
|
||||
resumeLocation = moveInsideField(arena.getBallSpawn(), 1.25);
|
||||
throwInTeam = null;
|
||||
message = "§e⚽ §7Ball im Aus!";
|
||||
}
|
||||
@@ -956,6 +962,25 @@ public class Game {
|
||||
return new Location(outLoc.getWorld(), cx, y, cz);
|
||||
}
|
||||
|
||||
private Location moveInsideField(Location source, double inset) {
|
||||
if (source == null) return arena.getBallSpawn();
|
||||
if (arena.getFieldMin() == null || arena.getFieldMax() == null) return source.clone();
|
||||
|
||||
double minX = Math.min(arena.getFieldMin().getX(), arena.getFieldMax().getX()) + inset;
|
||||
double maxX = Math.max(arena.getFieldMin().getX(), arena.getFieldMax().getX()) - inset;
|
||||
double minZ = Math.min(arena.getFieldMin().getZ(), arena.getFieldMax().getZ()) + inset;
|
||||
double maxZ = Math.max(arena.getFieldMin().getZ(), arena.getFieldMax().getZ()) - inset;
|
||||
|
||||
// Bei sehr kleinen Feldern: notfalls exakt klemmen statt invertierter Grenzen.
|
||||
if (minX > maxX) { minX = Math.min(arena.getFieldMin().getX(), arena.getFieldMax().getX()); maxX = Math.max(arena.getFieldMin().getX(), arena.getFieldMax().getX()); }
|
||||
if (minZ > maxZ) { minZ = Math.min(arena.getFieldMin().getZ(), arena.getFieldMax().getZ()); maxZ = Math.max(arena.getFieldMin().getZ(), arena.getFieldMax().getZ()); }
|
||||
|
||||
double x = Math.max(minX, Math.min(source.getX(), maxX));
|
||||
double z = Math.max(minZ, Math.min(source.getZ(), maxZ));
|
||||
double y = arena.getBallSpawn() != null ? arena.getBallSpawn().getY() : source.getY();
|
||||
return new Location(source.getWorld(), x, y, z, source.getYaw(), source.getPitch());
|
||||
}
|
||||
|
||||
private void checkPlayerBallInteraction() {
|
||||
if (ball == null || !ball.isActive() || ball.isHeld()) return;
|
||||
for (UUID uuid : allPlayers) {
|
||||
@@ -1744,6 +1769,14 @@ public class Game {
|
||||
if (bossBar != null) bossBar.removePlayer(player);
|
||||
scoreboard.remove(player);
|
||||
resetPlayer(player);
|
||||
|
||||
// Wenn ein Team komplett disqualifiziert wurde, gewinnt das andere Team sofort.
|
||||
if (state == GameState.RUNNING || state == GameState.OVERTIME || state == GameState.PENALTY || state == GameState.GOAL) {
|
||||
if (redTeam.isEmpty() && !blueTeam.isEmpty()) { endGame(Team.BLUE); return; }
|
||||
if (blueTeam.isEmpty() && !redTeam.isEmpty()) { endGame(Team.RED); return; }
|
||||
if (redTeam.isEmpty() && blueTeam.isEmpty()) { endGame(null); return; }
|
||||
}
|
||||
|
||||
if (allPlayers.size() < arena.getMinPlayers()) { endGame(getWinnerTeam()); return; }
|
||||
scoreboard.updateAll(); refreshSigns();
|
||||
}
|
||||
@@ -1810,6 +1843,8 @@ public class Game {
|
||||
lastKicker = null;
|
||||
secondLastKicker = null;
|
||||
lastKickWasHeader = false;
|
||||
secondHalf = false;
|
||||
updateGoalBeaconColors();
|
||||
|
||||
// Persistente Statistiken speichern
|
||||
// BUG FIX: nach Elfmeter kann redScore == blueScore true sein obwohl ein Gewinner existiert.
|
||||
@@ -1945,6 +1980,70 @@ public class Game {
|
||||
|
||||
private void refreshSigns() { plugin.getSignListener().refreshSignsForArena(arena.getName()); }
|
||||
|
||||
private void updateGoalBeaconColors() {
|
||||
setGoalBeaconColor(getGoalCenter(true), secondHalf ? Team.BLUE : Team.RED);
|
||||
setGoalBeaconColor(getGoalCenter(false), secondHalf ? Team.RED : Team.BLUE);
|
||||
}
|
||||
|
||||
private void setGoalBeaconColor(Location goalCenter, Team defendingTeam) {
|
||||
List<Block> beacons = findNearbyBeacons(goalCenter, GOAL_BEACON_SCAN_RADIUS, GOAL_BEACON_MAX_PER_SIDE);
|
||||
if (beacons.isEmpty()) return;
|
||||
|
||||
Material colorGlass = defendingTeam == Team.RED
|
||||
? Material.RED_STAINED_GLASS
|
||||
: Material.BLUE_STAINED_GLASS;
|
||||
|
||||
for (Block beacon : beacons) {
|
||||
Block above = beacon.getRelative(BlockFace.UP);
|
||||
Material current = above.getType();
|
||||
if (current == Material.AIR || isStainedGlass(current)) {
|
||||
above.setType(colorGlass, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private List<Block> findNearbyBeacons(Location center, int radius, int maxCount) {
|
||||
if (center == null || center.getWorld() == null) return Collections.emptyList();
|
||||
|
||||
List<Block> found = new ArrayList<>();
|
||||
int cx = center.getBlockX();
|
||||
int cy = center.getBlockY();
|
||||
int cz = center.getBlockZ();
|
||||
|
||||
for (int x = cx - radius; x <= cx + radius; x++) {
|
||||
for (int y = cy - 6; y <= cy + 6; y++) {
|
||||
for (int z = cz - radius; z <= cz + radius; z++) {
|
||||
Block block = center.getWorld().getBlockAt(x, y, z);
|
||||
if (block.getType() != Material.BEACON) continue;
|
||||
found.add(block);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
found.sort(Comparator.comparingDouble(b -> b.getLocation().distanceSquared(center)));
|
||||
if (found.size() > maxCount) {
|
||||
return new ArrayList<>(found.subList(0, maxCount));
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
private boolean isStainedGlass(Material material) {
|
||||
return material.name().endsWith("_STAINED_GLASS");
|
||||
}
|
||||
|
||||
private Location getGoalCenter(boolean redGoal) {
|
||||
Location min = redGoal ? arena.getRedGoalMin() : arena.getBlueGoalMin();
|
||||
Location max = redGoal ? arena.getRedGoalMax() : arena.getBlueGoalMax();
|
||||
if (min == null || max == null || min.getWorld() == null) return null;
|
||||
|
||||
return new Location(
|
||||
min.getWorld(),
|
||||
(min.getX() + max.getX()) / 2.0,
|
||||
(min.getY() + max.getY()) / 2.0,
|
||||
(min.getZ() + max.getZ()) / 2.0
|
||||
);
|
||||
}
|
||||
|
||||
/** Broadcast an alle Spieler UND Zuschauer */
|
||||
public void broadcastAll(String msg) {
|
||||
for (UUID uuid : getAllAndSpectators()) { Player p = Bukkit.getPlayer(uuid); if (p != null) p.sendMessage(msg); }
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package de.fussball.plugin.hologram;
|
||||
|
||||
import de.fussball.plugin.Fussball;
|
||||
import de.fussball.plugin.game.Game;
|
||||
import de.fussball.plugin.game.GameState;
|
||||
import de.fussball.plugin.stats.StatsManager;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Color;
|
||||
@@ -9,6 +11,9 @@ import org.bukkit.entity.Display;
|
||||
import org.bukkit.entity.Interaction;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.entity.TextDisplay;
|
||||
import org.bukkit.util.Transformation;
|
||||
import org.joml.AxisAngle4f;
|
||||
import org.joml.Vector3f;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
@@ -28,7 +33,7 @@ public class FussballHologram {
|
||||
/** Render-Radius: 48 Blöcke (2304 = 48²) */
|
||||
private static final double RENDER_RADIUS_SQ = 2304.0;
|
||||
|
||||
public enum HoloType { GOALS, WINS }
|
||||
public enum HoloType { GOALS, WINS, MATCH }
|
||||
|
||||
private final String id;
|
||||
private final Location location;
|
||||
@@ -53,6 +58,9 @@ public class FussballHologram {
|
||||
|
||||
/** Rechtsklick → zur nächsten Seite (GOALS ↔ WINS) */
|
||||
public void nextPage(Player player) {
|
||||
if (type == HoloType.MATCH) {
|
||||
return;
|
||||
}
|
||||
// Wir haben 2 Seiten: 0 = GOALS, 1 = WINS
|
||||
int next = (currentPage.getOrDefault(player.getUniqueId(), 0) + 1) % 2;
|
||||
currentPage.put(player.getUniqueId(), next);
|
||||
@@ -74,8 +82,13 @@ public class FussballHologram {
|
||||
}
|
||||
|
||||
// Seite bestimmen
|
||||
int pageIdx = currentPage.getOrDefault(player.getUniqueId(), type == HoloType.WINS ? 1 : 0);
|
||||
HoloType displayType = pageIdx == 1 ? HoloType.WINS : HoloType.GOALS;
|
||||
HoloType displayType;
|
||||
if (type == HoloType.MATCH) {
|
||||
displayType = HoloType.MATCH;
|
||||
} else {
|
||||
int pageIdx = currentPage.getOrDefault(player.getUniqueId(), type == HoloType.WINS ? 1 : 0);
|
||||
displayType = pageIdx == 1 ? HoloType.WINS : HoloType.GOALS;
|
||||
}
|
||||
|
||||
String text = buildText(displayType);
|
||||
|
||||
@@ -93,34 +106,47 @@ public class FussballHologram {
|
||||
entity.setText(text);
|
||||
entity.setInvulnerable(true);
|
||||
entity.setSeeThrough(false);
|
||||
});
|
||||
|
||||
// ── Interaction-Entity spawnen (Hitbox für Rechtsklick) ──────────
|
||||
Interaction interact = location.getWorld().spawn(location, Interaction.class, entity -> {
|
||||
entity.setInteractionWidth(2.5f);
|
||||
entity.setInteractionHeight(2.5f);
|
||||
entity.setCustomNameVisible(false);
|
||||
entity.setPersistent(false);
|
||||
applyVisualScale(entity, displayType);
|
||||
});
|
||||
|
||||
// ── Nur für diesen Spieler sichtbar machen ───────────────────────
|
||||
TextDisplay finalDisplay = display;
|
||||
Interaction interact = null;
|
||||
if (displayType != HoloType.MATCH) {
|
||||
// Match-Hologramme sind nur Anzeige, ohne Klick-Hitbox.
|
||||
interact = location.getWorld().spawn(location, Interaction.class, entity -> {
|
||||
entity.setInteractionWidth(2.5f);
|
||||
entity.setInteractionHeight(2.5f);
|
||||
entity.setCustomNameVisible(false);
|
||||
entity.setPersistent(false);
|
||||
});
|
||||
}
|
||||
|
||||
Interaction finalInteract = interact;
|
||||
for (Player other : Bukkit.getOnlinePlayers()) {
|
||||
if (!other.getUniqueId().equals(player.getUniqueId())) {
|
||||
other.hideEntity(plugin, finalDisplay);
|
||||
other.hideEntity(plugin, finalInteract);
|
||||
if (finalInteract != null) {
|
||||
other.hideEntity(plugin, finalInteract);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
playerEntities.put(player.getUniqueId(), display);
|
||||
playerInteractions.put(player.getUniqueId(), interact);
|
||||
if (interact != null) {
|
||||
playerInteractions.put(player.getUniqueId(), interact);
|
||||
}
|
||||
|
||||
} else {
|
||||
// ── Bestehende Entity nur aktualisieren (kein Re-Spawn) ──────────
|
||||
if (!display.getText().equals(text)) {
|
||||
display.setText(text);
|
||||
}
|
||||
applyVisualScale(display, displayType);
|
||||
if (displayType == HoloType.MATCH) {
|
||||
Interaction interact = playerInteractions.remove(player.getUniqueId());
|
||||
if (interact != null) interact.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,6 +185,10 @@ public class FussballHologram {
|
||||
|
||||
/** Baut den anzuzeigenden Text aus den aktuellen Top-10-Statistiken */
|
||||
private String buildText(HoloType showType) {
|
||||
if (showType == HoloType.MATCH) {
|
||||
return buildMatchText();
|
||||
}
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
if (showType == HoloType.GOALS) {
|
||||
@@ -202,6 +232,91 @@ public class FussballHologram {
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private String buildMatchText() {
|
||||
Game game = findRelevantGame();
|
||||
if (game == null) {
|
||||
return "§e§lLive Match\n§8§m────────────────\n§7Kein Spiel aktiv\n§8- : -\n§8--:--";
|
||||
}
|
||||
|
||||
String header = "§e§lLive Match";
|
||||
String separator = "§8§m────────────────";
|
||||
String phase = buildPhaseLabel(game);
|
||||
String score = "§c§l" + game.getRedScore() + " §r§7: §9§l" + game.getBlueScore();
|
||||
String timeLabel = game.isInInjuryTime()
|
||||
? "§c+" + formatInjury(game.getInjuryTimeBuffer()) + " §7(Nachspielzeit)"
|
||||
: "§e" + formatMainTime(game.getTimeLeft());
|
||||
|
||||
return header + "\n" + separator + "\n" + phase + "\n" + score + "\n" + timeLabel;
|
||||
}
|
||||
|
||||
private String buildPhaseLabel(Game game) {
|
||||
return switch (game.getState()) {
|
||||
case WAITING -> "§7⏳ Wartet auf Spieler";
|
||||
case STARTING -> "§aStartet...";
|
||||
case RUNNING -> game.isSecondHalf() ? "§72. Halbzeit" : "§71. Halbzeit";
|
||||
case HALFTIME -> "§e⏸ Halbzeit";
|
||||
case OVERTIME -> "§6⚡ Verlängerung";
|
||||
case GOAL -> "§6§l⚽ TOR!";
|
||||
case PENALTY -> "§c🥊 Elfmeterschießen";
|
||||
case ENDING -> "§8Spiel beendet";
|
||||
};
|
||||
}
|
||||
|
||||
private void applyVisualScale(TextDisplay display, HoloType showType) {
|
||||
float scale = showType == HoloType.MATCH ? 5.0f : 1.0f;
|
||||
display.setTransformation(new Transformation(
|
||||
new Vector3f(0f, 0f, 0f),
|
||||
new AxisAngle4f(),
|
||||
new Vector3f(scale, scale, scale),
|
||||
new AxisAngle4f()
|
||||
));
|
||||
}
|
||||
|
||||
private Game findRelevantGame() {
|
||||
Game best = null;
|
||||
int bestPrio = Integer.MAX_VALUE;
|
||||
double bestDist = Double.MAX_VALUE;
|
||||
|
||||
for (Game game : plugin.getGameManager().getAllGames()) {
|
||||
Location ref = game.getArena().getCenter();
|
||||
if (ref == null) ref = game.getArena().getBallSpawn();
|
||||
if (ref == null) ref = game.getArena().getLobby();
|
||||
if (ref == null || ref.getWorld() == null) continue;
|
||||
if (!ref.getWorld().equals(location.getWorld())) continue;
|
||||
|
||||
int prio = switch (game.getState()) {
|
||||
case RUNNING, OVERTIME, GOAL, PENALTY -> 0;
|
||||
case STARTING, HALFTIME -> 1;
|
||||
case WAITING -> 2;
|
||||
case ENDING -> 3;
|
||||
};
|
||||
double dist = ref.distanceSquared(location);
|
||||
|
||||
if (prio < bestPrio || (prio == bestPrio && dist < bestDist)) {
|
||||
best = game;
|
||||
bestPrio = prio;
|
||||
bestDist = dist;
|
||||
}
|
||||
}
|
||||
|
||||
return best;
|
||||
}
|
||||
|
||||
private String formatMainTime(int totalSeconds) {
|
||||
int safe = Math.max(0, totalSeconds);
|
||||
int m = safe / 60;
|
||||
int s = safe % 60;
|
||||
return String.format("%02d:%02d", m, s);
|
||||
}
|
||||
|
||||
private String formatInjury(int seconds) {
|
||||
int safe = Math.max(0, seconds);
|
||||
if (safe >= 60) {
|
||||
return "+" + (safe / 60) + ":" + String.format("%02d", safe % 60);
|
||||
}
|
||||
return "+" + safe + "s";
|
||||
}
|
||||
|
||||
private String medal(int rank) {
|
||||
return switch (rank) {
|
||||
case 1 -> "§6§l#1"; // Gold bleibt – hebt sich gut ab
|
||||
|
||||
@@ -106,10 +106,14 @@ public class HologramManager implements Listener {
|
||||
);
|
||||
|
||||
String typeStr = holoConfig.getString(path + ".type", "GOALS");
|
||||
FussballHologram.HoloType type =
|
||||
"WINS".equalsIgnoreCase(typeStr)
|
||||
? FussballHologram.HoloType.WINS
|
||||
: FussballHologram.HoloType.GOALS;
|
||||
FussballHologram.HoloType type;
|
||||
if ("WINS".equalsIgnoreCase(typeStr)) {
|
||||
type = FussballHologram.HoloType.WINS;
|
||||
} else if ("MATCH".equalsIgnoreCase(typeStr) || "LIVE".equalsIgnoreCase(typeStr)) {
|
||||
type = FussballHologram.HoloType.MATCH;
|
||||
} else {
|
||||
type = FussballHologram.HoloType.GOALS;
|
||||
}
|
||||
|
||||
holograms.put(id, new FussballHologram(id, loc, type, plugin));
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@ package de.fussball.plugin.listeners;
|
||||
import de.fussball.plugin.Fussball;
|
||||
import de.fussball.plugin.game.Game;
|
||||
import de.fussball.plugin.game.Team;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.HumanEntity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
@@ -10,7 +12,11 @@ import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.entity.EntityDamageEvent;
|
||||
import org.bukkit.event.entity.EntityPickupItemEvent;
|
||||
import org.bukkit.event.entity.FoodLevelChangeEvent;
|
||||
import org.bukkit.event.inventory.InventoryClickEvent;
|
||||
import org.bukkit.event.inventory.InventoryDragEvent;
|
||||
import org.bukkit.event.inventory.InventoryType;
|
||||
import org.bukkit.event.player.*;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
public class PlayerListener implements Listener {
|
||||
|
||||
@@ -63,6 +69,64 @@ public class PlayerListener implements Listener {
|
||||
if (plugin.getGameManager().isInAnyGame(player)) event.setCancelled(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Team-Rüstung darf im aktiven Spiel nicht ausgezogen oder verschoben werden.
|
||||
*/
|
||||
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
|
||||
public void onInventoryClick(InventoryClickEvent event) {
|
||||
if (!(event.getWhoClicked() instanceof Player player)) return;
|
||||
if (!plugin.getGameManager().isInGame(player)) return;
|
||||
|
||||
// Direkter Klick auf Helm/Brust/Beine/Schuhe-Slot
|
||||
if (event.getSlotType() == InventoryType.SlotType.ARMOR) {
|
||||
event.setCancelled(true);
|
||||
return;
|
||||
}
|
||||
|
||||
// Shift-Klick auf Rüstung im Inventar (würde in Rüstungsslot auto-equipen/verschieben)
|
||||
if (event.isShiftClick() && isArmorPiece(event.getCurrentItem())) {
|
||||
event.setCancelled(true);
|
||||
return;
|
||||
}
|
||||
|
||||
// Hotbar-Zahlen-Tausch mit Rüstungsteilen blockieren
|
||||
if (event.getClick().isKeyboardClick()) {
|
||||
int hotbar = event.getHotbarButton();
|
||||
if (hotbar >= 0 && hotbar <= 8) {
|
||||
ItemStack hotbarItem = player.getInventory().getItem(hotbar);
|
||||
if (isArmorPiece(hotbarItem) || event.getSlotType() == InventoryType.SlotType.ARMOR) {
|
||||
event.setCancelled(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Draggen auf Rüstungsslots blockieren.
|
||||
*/
|
||||
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
|
||||
public void onInventoryDrag(InventoryDragEvent event) {
|
||||
HumanEntity who = event.getWhoClicked();
|
||||
if (!(who instanceof Player player)) return;
|
||||
if (!plugin.getGameManager().isInGame(player)) return;
|
||||
|
||||
for (int rawSlot : event.getRawSlots()) {
|
||||
if (event.getView().getSlotType(rawSlot) == InventoryType.SlotType.ARMOR) {
|
||||
event.setCancelled(true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isArmorPiece(ItemStack item) {
|
||||
if (item == null) return false;
|
||||
Material t = item.getType();
|
||||
return t.name().endsWith("_HELMET")
|
||||
|| t.name().endsWith("_CHESTPLATE")
|
||||
|| t.name().endsWith("_LEGGINGS")
|
||||
|| t.name().endsWith("_BOOTS");
|
||||
}
|
||||
|
||||
/**
|
||||
* Zuschauer dürfen die Arena nicht verlassen (Weltenwechsel blockieren).
|
||||
* GM3 erlaubt Portale – das verhindert, dass ein Zuschauer durch ein Portal
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: Fussball
|
||||
version: 1.0.0
|
||||
version: 1.0.1
|
||||
main: de.fussball.plugin.Fussball
|
||||
api-version: 1.21
|
||||
author: M_Viper
|
||||
|
||||
Reference in New Issue
Block a user