Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 54414cbc2e | |||
| 885fd9792d | |||
| a6d17f4d64 | |||
| 22c5837455 | |||
| 266268bd0b | |||
| c837c963af | |||
| d7ed7b00c9 |
114
README.md
114
README.md
@@ -23,6 +23,7 @@ Ein professionelles Fußball-Plugin für Spigot/Paper 1.21+ mit echtem Ball-Phys
|
||||
| 🙋 Teamwahl | Spieler können per `/fb team rot|blau` ihr Wunsch-Team wählen (vor Spielstart) |
|
||||
| 👕 Team-Rüstung | Farbige Leder-Rüstung mit **Trikot-Nummer** (#1, #2, …) auf dem Brustpanzer |
|
||||
| 🥅 Tor-Erkennung | Region-basierte Tore mit Trajectory-Check (Schritt-für-Schritt Bahnverfolgung) |
|
||||
| 💠 Tor-Beacons | Beacon-Farbe am linken/rechten Tor zeigt das verteidigende Team (Rot/Blau) und wechselt automatisch zur Halbzeit mit den Seiten |
|
||||
| 🎉 Tor-Effekte | Feuerwerk, Titel, Action-Bar Tor-Replay, Sound |
|
||||
| 🎶 Stadionatmosphäre | Jubel-Sounds & mehrfache Feuerwerke beim Tor; Enttäuschungs-Sound für das andere Team |
|
||||
| 📊 Scoreboard | Live-Spielstand, Zeit, Halbzeit-Anzeige, Ballbesitz, farbige Spielernamen über Kopf |
|
||||
@@ -48,7 +49,8 @@ Ein professionelles Fußball-Plugin für Spigot/Paper 1.21+ mit echtem Ball-Phys
|
||||
| 🏟 Strafraum | Auto-berechnet aus Tor-Koordinaten oder manuell setzbar |
|
||||
| 📋 Matchbericht | Tore, Karten, Fouls, Abseits, Ballbesitz und MVP am Spielende |
|
||||
| 🏆 MVP-System | Bester Torschütze wird nach dem Spiel bekannt gegeben |
|
||||
| 📈 Persistente Stats | Tore, Eigentore, Vorlagen, Schüsse, Siege, Niederlagen, Siegquote |
|
||||
| 📈 Persistente Stats | Tore, Eigentore, Vorlagen, Schüsse, Siege, Niederlagen, Siegquote; per Admin-Befehl resetbar |
|
||||
| 🪄 Hologramme | Platzierbare Ingame-Hologramme für Top-Tore, Top-Siege und Live-Matchstand; Texte ingame bearbeitbar, Standard-Farben über `config.yml` anpassbar |
|
||||
| 📋 Match-History | Letzte 50 Spiele dauerhaft gespeichert, abrufbar per `/fb history` |
|
||||
| 🔢 Warteschlange | Automatische Queue wenn Arena voll; nächster Spieler rückt nach |
|
||||
| 👀 Zuschauer-Modus | Sichtbarer Zuschauer-Bereich außerhalb des Feldes mit BossBar und Scoreboard |
|
||||
@@ -164,11 +166,93 @@ Alle Pflichtfelder müssen **§a✔** zeigen. Erst dann ist die Arena spielberei
|
||||
| `/fb setgk <arena> <spieler>` | Torwart eines laufenden Spiels manuell neu zuweisen |
|
||||
| `/fb dropball <arena>` | Schiedsrichterball – Ball neutral spawnen, beide Teams dürfen spielen |
|
||||
| `/fb debug <arena>` | Tor-/Feld-Regionen, Ball-Position und Aus-Seite debuggen |
|
||||
| `/fb stats reset <spieler\|all>` | Einzelne Spieler-Stats oder alle Statistiken zurücksetzen |
|
||||
| `/fb hologram set <arena> goals\|wins\|match` | Hologramm für eine Arena an deiner aktuellen Position erstellen |
|
||||
| `/fb hologram remove` | Nächstes Hologramm im 5-Block-Radius entfernen |
|
||||
| `/fb hologram delete <arena> goals\|wins\|match` | Hologramm einer Arena gezielt löschen |
|
||||
| `/fb hologram text <arena> goals\|wins\|match <zeile> <text>` | Einzelne Hologramm-Zeilen bearbeiten |
|
||||
| `/fb hologram textpreview <arena> goals\|wins\|match` | Aktuell gespeicherte Hologramm-Zeilen anzeigen |
|
||||
| `/fb hologram textreset <arena> goals\|wins\|match` | Standard-Text eines Hologramms wiederherstellen |
|
||||
| `/fb hologram list` | Alle Hologramme als `arena → typ` anzeigen |
|
||||
| `/fb hologram reload` | Hologramme sowie Hologramm-Farben aus `holograms.yml`/`config.yml` neu laden |
|
||||
|
||||
Alle Commands unterstützen **Tab-Completion** für Arena-Namen und Optionen.
|
||||
|
||||
---
|
||||
|
||||
## 🪄 Hologramme
|
||||
|
||||
Hologramme kannst du **direkt ingame** setzen: Stelle dich an die gewünschte Position und nutze den Befehl.
|
||||
|
||||
```
|
||||
/fb hologram set <arena> goals → Top-10 Torschützen
|
||||
/fb hologram set <arena> wins → Top-10 Gewinner
|
||||
/fb hologram set <arena> match → Großes Live-Hologramm (nur Spielstand + Zeit/Nachspielzeit)
|
||||
```
|
||||
|
||||
Beispiel für deine Arena:
|
||||
|
||||
```
|
||||
/fb hologram set allianz goals
|
||||
/fb hologram set allianz wins
|
||||
/fb hologram set allianz match
|
||||
```
|
||||
|
||||
Intern speichert das Plugin diese Hologramme getrennt pro Arena und Typ, also z. B. `allianz_goals`, `allianz_wins` und `allianz_match`.
|
||||
|
||||
Weitere Verwaltung:
|
||||
|
||||
```
|
||||
/fb hologram remove
|
||||
/fb hologram delete <arena> goals|wins|match
|
||||
/fb hologram text <arena> goals|wins|match <zeile> <text>
|
||||
/fb hologram textpreview <arena> goals|wins|match
|
||||
/fb hologram textreset <arena> goals|wins|match
|
||||
/fb hologram list
|
||||
/fb hologram reload
|
||||
```
|
||||
|
||||
**Hinweise:**
|
||||
- `<arena>` ist der Arena-Name, nicht die Hologramm-ID.
|
||||
- `match` zeigt bewusst nur Spielstand + Zeit und aktualisiert sich automatisch bei Toren, Zeitlauf und Nachspielzeit.
|
||||
- `goals`/`wins` lassen sich per Rechtsklick direkt am Hologramm umschalten.
|
||||
- Farben und Formatierungen für einzelne Zeilen kannst du über `&`-Codes im `text`-Befehl setzen, z. B. `&6&lTitel`, `&cRot`, `&oKursiv`.
|
||||
- Standard-Farbschema (Titel, Werte, Trennlinie, Match-Header usw.) stellst du zentral in `config.yml` unter `holograms:` ein und übernimmst es mit `/fb hologram reload`.
|
||||
- `text` bearbeitet immer genau eine Zeile. Fehlende Zeilen werden bei Bedarf automatisch ergänzt.
|
||||
- Mit `textreset` wird nur das gewählte Hologramm auf den Standardtext zurückgesetzt.
|
||||
|
||||
### Hologramm-Platzhalter
|
||||
|
||||
Für `goals` und `wins` stehen in bearbeitbaren Texten diese Platzhalter zur Verfügung:
|
||||
|
||||
```
|
||||
{title} → Standard-Überschrift des Hologramms
|
||||
{separator} → Trennlinie
|
||||
{entries} → Dynamische Top-10-Liste
|
||||
{toggle} → Hinweis zum Umschalten per Rechtsklick
|
||||
```
|
||||
|
||||
Für `match` stehen diese Platzhalter zur Verfügung:
|
||||
|
||||
```
|
||||
{header} → Live-Match Überschrift
|
||||
{separator} → Trennlinie
|
||||
{phase} → Aktuelle Spielphase
|
||||
{score} → Aktueller Spielstand
|
||||
{time} → Spielzeit oder Nachspielzeit
|
||||
```
|
||||
|
||||
Beispiel:
|
||||
|
||||
```
|
||||
/fb hologram text allianz goals 1 &6&l⚽ Allianz Topscorer
|
||||
/fb hologram text allianz goals 2 &8{separator}
|
||||
/fb hologram text allianz goals 3 {entries}
|
||||
/fb hologram text allianz goals 4 &7{toggle}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎮 Spielablauf
|
||||
|
||||
```
|
||||
@@ -392,6 +476,8 @@ Das Schild wird automatisch formatiert und mit Live-Status aktualisiert:
|
||||
```
|
||||
/fb stats → Eigene Statistiken
|
||||
/fb stats Notch → Statistiken von "Notch"
|
||||
/fb stats reset Notch → Statistiken von "Notch" zurücksetzen (Admin)
|
||||
/fb stats reset all → Alle Statistiken zurücksetzen (Admin)
|
||||
/fb top goals → Top 10 Torschützen
|
||||
/fb top wins → Top 10 nach Siegen (inkl. Siegquote)
|
||||
/fb top kicks → Top 10 nach Anzahl Schüssen
|
||||
@@ -412,7 +498,7 @@ Das Schild wird automatisch formatiert und mit Live-Status aktualisiert:
|
||||
| 📊 Gespielte Spiele | Gesamtanzahl Spiele |
|
||||
| 📈 Siegquote | Siege / Spiele × 100 % |
|
||||
|
||||
Alle Daten werden in `plugins/Fussball/stats.yml` dauerhaft gespeichert.
|
||||
Alle Daten werden in `plugins/Fussball/stats.yml` dauerhaft gespeichert und können über `/fb stats reset <spieler|all>` gezielt zurückgesetzt werden.
|
||||
|
||||
---
|
||||
|
||||
@@ -443,7 +529,7 @@ Wenn PlaceholderAPI installiert ist, sind folgende Platzhalter verfügbar:
|
||||
|
||||
| Permission | Beschreibung | Standard |
|
||||
|---|---|---|
|
||||
| `fussball.admin` | Alle Admin-Commands (create, delete, setup, stop, setgk, dropball, debug, Schilder, Global-Chat) | OP |
|
||||
| `fussball.admin` | Alle Admin-Commands inklusive Stats-Reset, Hologramm-Verwaltung, Schilder und Global-Chat | OP |
|
||||
| `fussball.play` | Spielen, Zuschauen, Stats anzeigen | Alle |
|
||||
|
||||
---
|
||||
@@ -494,6 +580,21 @@ gameplay:
|
||||
atmosphere:
|
||||
enabled: true
|
||||
goal-fireworks: 5 # Feuerwerke pro Tor (0 = deaktiviert)
|
||||
|
||||
holograms:
|
||||
goals-title-color: "&6&l" # Titel-Farbe Top-Tore
|
||||
goals-value-color: "&4" # Werte-Farbe Tore
|
||||
wins-title-color: "&2&l" # Titel-Farbe Top-Siege
|
||||
wins-value-color: "&2" # Werte-Farbe Siege
|
||||
name-color: "&0" # Spielername-Farbe
|
||||
label-color: "&8" # Labels (Tore/Siege/Trenner)
|
||||
separator-color: "&8&m" # Trennlinien-Stil
|
||||
toggle-color: "&8&o" # Umschalt-Hinweis
|
||||
match-header-color: "&e&l" # Match-Header
|
||||
match-score-red: "&c&l" # Score Rot
|
||||
match-score-blue: "&9&l" # Score Blau
|
||||
match-time-color: "&e" # Normale Spielzeit
|
||||
match-injury-color: "&c" # Nachspielzeit
|
||||
```
|
||||
|
||||
Alle `messages`-Einträge sind ebenfalls vollständig anpassbar. Platzhalter: `{player}`, `{team}`, `{score}`, `{time}`, `{reason}`, `{n}`, `{max}`.
|
||||
@@ -509,7 +610,7 @@ plugins/Fussball/
|
||||
├── signs.yml → Gespeicherte Join-/Zuschauer-Schilder (auto-generiert)
|
||||
├── stats.yml → Spieler-Statistiken (auto-generiert)
|
||||
├── matchhistory.yml → Letzte 50 Spiele (auto-generiert)
|
||||
└── holograms.yml → Statistik-Hologramme (auto-generiert)
|
||||
└── holograms.yml → Statistik-Hologramme inkl. benutzerdefinierter Texte (auto-generiert)
|
||||
```
|
||||
|
||||
---
|
||||
@@ -519,6 +620,7 @@ plugins/Fussball/
|
||||
- **Feldgröße:** Mindestens **30×20 Blöcke** empfohlen (größer = mehr Spaß)
|
||||
- **Tore:** Ca. **5 Blöcke breit, 3 Blöcke hoch** – mindestens **2 Blöcke breit** da der Ball sonst stecken bleiben kann
|
||||
- **Tortiefe:** Mindestens **2 Blöcke tief** damit Tor-Erkennung sauber funktioniert
|
||||
- **Tor-Beacons (optional):** Platziere je einen Beacon nahe jedem Tor. Das Plugin setzt automatisch rotes/blaues Glas darüber und tauscht die Farben nach der Halbzeit beim Seitenwechsel.
|
||||
- **`fieldmin/fieldmax` immer setzen!** Sonst gibt es weder Aus-Erkennung noch Spieler-Feldgrenzenkontrolle
|
||||
- **`center` genau in die Feldmitte setzen** – wird für den Anstoß-Kreis verwendet
|
||||
- **`ballspawn` genau in die Mitte** – er ist gleichzeitig der Anstoßpunkt und der Spawn nach einem Tor
|
||||
@@ -549,6 +651,4 @@ Nach jedem Spiel wird automatisch ein Matchbericht ausgegeben:
|
||||
|
||||
---
|
||||
|
||||
**Copyright © 2026 - M_Viper - Alle Rechte vorbehalten**
|
||||
|
||||
Die unbefugte Vervielfältigung, Verbreitung oder Weitergabe dieses Plugins ist strafbar und wird rechtlich verfolgt.
|
||||
*Erstellt mit ❤️ von M_Viper — viel Spaß beim Fußballspielen! 🏆*
|
||||
2
pom.xml
2
pom.xml
@@ -5,7 +5,7 @@
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>de.fussball</groupId>
|
||||
<artifactId>Fussball</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<version>1.0.3</version>
|
||||
<packaging>jar</packaging>
|
||||
<name>Fussball</name>
|
||||
<description>Ein vollständiges Fußball-Minigame für Minecraft</description>
|
||||
|
||||
@@ -11,6 +11,7 @@ import de.fussball.plugin.hologram.HologramManager;
|
||||
import de.fussball.plugin.stats.MatchHistory;
|
||||
import de.fussball.plugin.stats.StatsManager;
|
||||
import de.fussball.plugin.utils.MessageUtil;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.command.*;
|
||||
@@ -81,6 +82,36 @@ public class FussballCommand implements CommandExecutor, TabCompleter {
|
||||
}
|
||||
|
||||
case "stats" -> {
|
||||
if (args.length >= 2 && args[1].equalsIgnoreCase("reset")) {
|
||||
if (!sender.hasPermission("fussball.admin")) { sender.sendMessage(MessageUtil.error("Keine Berechtigung!")); return true; }
|
||||
if (args.length < 3) { sender.sendMessage(MessageUtil.error("Benutze: /fb stats reset <spieler|all>")); return true; }
|
||||
|
||||
if (args[2].equalsIgnoreCase("all") || args[2].equalsIgnoreCase("*")) {
|
||||
int removed = plugin.getStatsManager().resetAllStats();
|
||||
plugin.getHologramManager().refreshAll();
|
||||
sender.sendMessage(MessageUtil.success("Alle Statistiken zurückgesetzt! §7(" + removed + " Einträge)"));
|
||||
return true;
|
||||
}
|
||||
|
||||
Player onlineTarget = Bukkit.getPlayerExact(args[2]);
|
||||
UUID targetUuid = onlineTarget != null
|
||||
? onlineTarget.getUniqueId()
|
||||
: plugin.getStatsManager().findPlayerUuidByName(args[2]);
|
||||
if (targetUuid == null) {
|
||||
sender.sendMessage(MessageUtil.error("Keine gespeicherten Statistiken für §e" + args[2] + " §cgefunden!"));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!plugin.getStatsManager().resetStats(targetUuid)) {
|
||||
sender.sendMessage(MessageUtil.error("Statistiken von §e" + args[2] + " §ckonnten nicht zurückgesetzt werden."));
|
||||
return true;
|
||||
}
|
||||
|
||||
plugin.getHologramManager().refreshAll();
|
||||
sender.sendMessage(MessageUtil.success("Statistiken von §e" + args[2] + " §azurückgesetzt!"));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!(sender instanceof Player player)) { sender.sendMessage("Nur für Spieler!"); return true; }
|
||||
Player target = args.length >= 2 ? Bukkit.getPlayer(args[1]) : player;
|
||||
if (target == null) { player.sendMessage(MessageUtil.error("Spieler §e" + args[1] + " §cnicht gefunden!")); return true; }
|
||||
@@ -281,9 +312,9 @@ public class FussballCommand implements CommandExecutor, TabCompleter {
|
||||
}
|
||||
|
||||
// ── Hologramm-Verwaltung ─────────────────────────────────────────
|
||||
// /fb hologram set <id> goals|wins|match – Hologramm erstellen
|
||||
// /fb hologram set <arena> goals|wins|match – Hologramm erstellen/verschieben
|
||||
// /fb hologram remove – Nächstes Hologramm (< 5 Blöcke) entfernen
|
||||
// /fb hologram delete <id> – Hologramm nach ID löschen
|
||||
// /fb hologram delete <arena> goals|wins|match – Hologramm gezielt löschen
|
||||
// /fb hologram reload – Alle Hologramme neu spawnen
|
||||
// /fb hologram list – Alle Hologramme anzeigen
|
||||
case "hologram", "holo" -> {
|
||||
@@ -291,9 +322,12 @@ 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|match §7– Hologramm setzen");
|
||||
player.sendMessage("§e/fb hologram set <arena> 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 delete <arena> goals|wins|match §7– Gezielt löschen");
|
||||
player.sendMessage("§e/fb hologram text <arena> goals|wins|match <zeile> <text> §7– Textzeile anpassen");
|
||||
player.sendMessage("§e/fb hologram textpreview <arena> goals|wins|match §7– Aktuelles Textlayout ansehen");
|
||||
player.sendMessage("§e/fb hologram textreset <arena> goals|wins|match §7– Standardtext wiederherstellen");
|
||||
player.sendMessage("§e/fb hologram reload §7– Alle neu laden");
|
||||
player.sendMessage("§e/fb hologram list §7– Alle anzeigen");
|
||||
player.sendMessage("§7Gesamt: §e" + plugin.getHologramManager().getCount() + " §7Hologramme");
|
||||
@@ -303,22 +337,26 @@ 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|match"));
|
||||
player.sendMessage(MessageUtil.error("Benutze: /fb hologram set <arena> goals|wins|match"));
|
||||
return true;
|
||||
}
|
||||
Arena arena = plugin.getArenaManager().getArena(args[2]);
|
||||
if (arena == null) {
|
||||
player.sendMessage(MessageUtil.error("Arena §e" + args[2] + " §cnicht gefunden!"));
|
||||
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 = 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!"));
|
||||
String id = buildHologramId(arena.getName(), type);
|
||||
plugin.getHologramManager().removeHologram(id);
|
||||
if (!plugin.getHologramManager().createHologram(id, player.getLocation(), type)) {
|
||||
player.sendMessage(MessageUtil.error("Konnte das Hologramm für Arena §e" + arena.getName() + " §cund Typ §e" + hologramTypeName(type) + " §cnicht setzen."));
|
||||
return true;
|
||||
}
|
||||
player.sendMessage(MessageUtil.success("Hologramm §e" + hologramTypeName(type) + " §afür Arena §e" + arena.getName() + " §agesetzt!"));
|
||||
if (type == FussballHologram.HoloType.MATCH) {
|
||||
player.sendMessage("§7§oLive-Match-Hologramm aktualisiert sich automatisch bei Toren und Nachspielzeit.");
|
||||
} else {
|
||||
@@ -334,13 +372,124 @@ public class FussballCommand implements CommandExecutor, TabCompleter {
|
||||
}
|
||||
}
|
||||
case "delete" -> {
|
||||
if (args.length < 3) { player.sendMessage(MessageUtil.error("Benutze: /fb hologram delete <id>")); return true; }
|
||||
if (args.length >= 4) {
|
||||
Arena arena = plugin.getArenaManager().getArena(args[2]);
|
||||
if (arena == null) {
|
||||
player.sendMessage(MessageUtil.error("Arena §e" + args[2] + " §cnicht gefunden!"));
|
||||
return true;
|
||||
}
|
||||
FussballHologram.HoloType type = switch (args[3].toLowerCase()) {
|
||||
case "wins", "siege" -> FussballHologram.HoloType.WINS;
|
||||
case "match", "live", "game" -> FussballHologram.HoloType.MATCH;
|
||||
default -> FussballHologram.HoloType.GOALS;
|
||||
};
|
||||
String id = buildHologramId(arena.getName(), type);
|
||||
if (plugin.getHologramManager().removeHologram(id)) {
|
||||
player.sendMessage(MessageUtil.success("Hologramm §e" + hologramTypeName(type) + " §ader Arena §e" + arena.getName() + " §agelöscht!"));
|
||||
} else {
|
||||
player.sendMessage(MessageUtil.error("Kein Hologramm vom Typ §e" + hologramTypeName(type) + " §cfür Arena §e" + arena.getName() + " §cgefunden!"));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (args.length < 3) { player.sendMessage(MessageUtil.error("Benutze: /fb hologram delete <arena> goals|wins|match")); return true; }
|
||||
if (plugin.getHologramManager().removeHologram(args[2])) {
|
||||
player.sendMessage(MessageUtil.success("Hologramm §e" + args[2] + " §agelöscht!"));
|
||||
player.sendMessage(MessageUtil.success("Legacy-Hologramm §e" + args[2] + " §agelöscht!"));
|
||||
} else {
|
||||
player.sendMessage(MessageUtil.error("Kein Hologramm mit ID §e" + args[2] + "§c gefunden!"));
|
||||
}
|
||||
}
|
||||
case "text" -> {
|
||||
if (args.length < 6) {
|
||||
player.sendMessage(MessageUtil.error("Benutze: /fb hologram text <arena> goals|wins|match <zeile> <text>"));
|
||||
return true;
|
||||
}
|
||||
Arena arena = plugin.getArenaManager().getArena(args[2]);
|
||||
if (arena == null) {
|
||||
player.sendMessage(MessageUtil.error("Arena §e" + args[2] + " §cnicht gefunden!"));
|
||||
return true;
|
||||
}
|
||||
FussballHologram.HoloType type = parseHologramType(args[3]);
|
||||
int line;
|
||||
try {
|
||||
line = Integer.parseInt(args[4]);
|
||||
} catch (NumberFormatException ex) {
|
||||
player.sendMessage(MessageUtil.error("§e" + args[4] + " §cist keine gültige Zeilennummer!"));
|
||||
return true;
|
||||
}
|
||||
if (line < 1) {
|
||||
player.sendMessage(MessageUtil.error("Zeilennummer muss mindestens §e1 §csein!"));
|
||||
return true;
|
||||
}
|
||||
|
||||
String id = buildHologramId(arena.getName(), type);
|
||||
HologramManager hologramManager = plugin.getHologramManager();
|
||||
if (hologramManager.getHologram(id) == null) {
|
||||
player.sendMessage(MessageUtil.error("Hologramm §e" + hologramTypeName(type) + " §cfür Arena §e" + arena.getName() + " §cexistiert nicht!"));
|
||||
return true;
|
||||
}
|
||||
|
||||
List<String> lines = new ArrayList<>(getEditableHologramTemplate(hologramManager, id, type));
|
||||
while (lines.size() < line) {
|
||||
lines.add(" ");
|
||||
}
|
||||
lines.set(line - 1, ChatColor.translateAlternateColorCodes('&', joinArgs(args, 5)));
|
||||
|
||||
if (!hologramManager.setCustomText(id, type, lines)) {
|
||||
player.sendMessage(MessageUtil.error("Konnte den Hologramm-Text nicht speichern!"));
|
||||
return true;
|
||||
}
|
||||
player.sendMessage(MessageUtil.success("Hologramm-Text für §e" + arena.getName() + " §a(" + hologramTypeName(type) + ") aktualisiert."));
|
||||
player.sendMessage("§7Zeile §e" + line + "§7: " + lines.get(line - 1));
|
||||
}
|
||||
case "textpreview" -> {
|
||||
if (args.length < 4) {
|
||||
player.sendMessage(MessageUtil.error("Benutze: /fb hologram textpreview <arena> goals|wins|match"));
|
||||
return true;
|
||||
}
|
||||
Arena arena = plugin.getArenaManager().getArena(args[2]);
|
||||
if (arena == null) {
|
||||
player.sendMessage(MessageUtil.error("Arena §e" + args[2] + " §cnicht gefunden!"));
|
||||
return true;
|
||||
}
|
||||
FussballHologram.HoloType type = parseHologramType(args[3]);
|
||||
String id = buildHologramId(arena.getName(), type);
|
||||
HologramManager hologramManager = plugin.getHologramManager();
|
||||
if (hologramManager.getHologram(id) == null) {
|
||||
player.sendMessage(MessageUtil.error("Hologramm §e" + hologramTypeName(type) + " §cfür Arena §e" + arena.getName() + " §cexistiert nicht!"));
|
||||
return true;
|
||||
}
|
||||
|
||||
List<String> lines = getEditableHologramTemplate(hologramManager, id, type);
|
||||
player.sendMessage(MessageUtil.header("Holo-Text: " + arena.getName() + " / " + hologramTypeName(type)));
|
||||
for (int i = 0; i < lines.size(); i++) {
|
||||
player.sendMessage("§e" + (i + 1) + "§7: " + lines.get(i));
|
||||
}
|
||||
if (type == FussballHologram.HoloType.MATCH) {
|
||||
player.sendMessage("§8Platzhalter: §7{header} {separator} {phase} {score} {time}");
|
||||
} else {
|
||||
player.sendMessage("§8Platzhalter: §7{title} {separator} {entries} {toggle}");
|
||||
}
|
||||
}
|
||||
case "textreset" -> {
|
||||
if (args.length < 4) {
|
||||
player.sendMessage(MessageUtil.error("Benutze: /fb hologram textreset <arena> goals|wins|match"));
|
||||
return true;
|
||||
}
|
||||
Arena arena = plugin.getArenaManager().getArena(args[2]);
|
||||
if (arena == null) {
|
||||
player.sendMessage(MessageUtil.error("Arena §e" + args[2] + " §cnicht gefunden!"));
|
||||
return true;
|
||||
}
|
||||
FussballHologram.HoloType type = parseHologramType(args[3]);
|
||||
String id = buildHologramId(arena.getName(), type);
|
||||
HologramManager hologramManager = plugin.getHologramManager();
|
||||
if (hologramManager.getHologram(id) == null) {
|
||||
player.sendMessage(MessageUtil.error("Hologramm §e" + hologramTypeName(type) + " §cfür Arena §e" + arena.getName() + " §cexistiert nicht!"));
|
||||
return true;
|
||||
}
|
||||
hologramManager.resetCustomText(id, type);
|
||||
player.sendMessage(MessageUtil.success("Standardtext für §e" + arena.getName() + " §a(" + hologramTypeName(type) + ") wiederhergestellt."));
|
||||
}
|
||||
case "reload" -> {
|
||||
plugin.getHologramManager().reload();
|
||||
player.sendMessage(MessageUtil.success("Hologramme neu geladen! §7(" + plugin.getHologramManager().getCount() + " gesamt)"));
|
||||
@@ -351,11 +500,11 @@ public class FussballCommand implements CommandExecutor, TabCompleter {
|
||||
player.sendMessage(MessageUtil.warn("Keine Hologramme vorhanden."));
|
||||
} else {
|
||||
for (String id : plugin.getHologramManager().getHologramIds()) {
|
||||
player.sendMessage("§7 • §e" + id);
|
||||
player.sendMessage("§7 • §e" + formatHologramDisplay(id));
|
||||
}
|
||||
}
|
||||
}
|
||||
default -> player.sendMessage(MessageUtil.error("Gültig: set <id> goals|wins|match | remove | delete <id> | reload | list"));
|
||||
default -> player.sendMessage(MessageUtil.error("Gültig: set | remove | delete | text | textpreview | textreset | reload | list"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -494,7 +643,8 @@ 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|match / remove / reload");
|
||||
s.sendMessage("§c§lAdmin: §cstats reset <spieler|all>");
|
||||
s.sendMessage("§c§lAdmin: §chologram set|delete|text|textpreview|textreset <arena> <typ>");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -512,6 +662,43 @@ public class FussballCommand implements CommandExecutor, TabCompleter {
|
||||
private String yn(boolean b) { return b ? "§aJA" : "§cNEIN"; }
|
||||
private String locStr(Location l) { return fmt(l.getX()) + " / " + fmt(l.getY()) + " / " + fmt(l.getZ()); }
|
||||
private String fmt(double d) { return String.format("%.1f", d); }
|
||||
private String buildHologramId(String arenaName, FussballHologram.HoloType type) { return arenaName.toLowerCase() + "_" + hologramTypeName(type); }
|
||||
private String joinArgs(String[] args, int start) { return String.join(" ", Arrays.copyOfRange(args, start, args.length)); }
|
||||
private List<String> getEditableHologramTemplate(HologramManager hologramManager, String id, FussballHologram.HoloType type) {
|
||||
List<String> custom = hologramManager.getCustomText(id, type);
|
||||
return custom.isEmpty() ? getDefaultHologramTemplate(type) : custom;
|
||||
}
|
||||
private List<String> getDefaultHologramTemplate(FussballHologram.HoloType type) {
|
||||
if (type == FussballHologram.HoloType.MATCH) {
|
||||
return List.of("{header}", "{separator}", "{phase}", "{score}", "{time}");
|
||||
}
|
||||
return List.of("{title}", "{separator}", "{entries}", "{separator}", "{toggle}");
|
||||
}
|
||||
private FussballHologram.HoloType parseHologramType(String rawType) {
|
||||
return switch (rawType.toLowerCase()) {
|
||||
case "wins", "siege" -> FussballHologram.HoloType.WINS;
|
||||
case "match", "live", "game" -> FussballHologram.HoloType.MATCH;
|
||||
default -> FussballHologram.HoloType.GOALS;
|
||||
};
|
||||
}
|
||||
private String hologramTypeName(FussballHologram.HoloType type) {
|
||||
return switch (type) {
|
||||
case WINS -> "wins";
|
||||
case MATCH -> "match";
|
||||
default -> "goals";
|
||||
};
|
||||
}
|
||||
private String formatHologramDisplay(String id) {
|
||||
int split = id.lastIndexOf('_');
|
||||
if (split > 0 && split < id.length() - 1) {
|
||||
String arena = id.substring(0, split);
|
||||
String type = id.substring(split + 1);
|
||||
if (type.equals("goals") || type.equals("wins") || type.equals("match")) {
|
||||
return arena + " §7→ §e" + type;
|
||||
}
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
// ── Tab-Completion ───────────────────────────────────────────────────────
|
||||
|
||||
@@ -523,14 +710,21 @@ public class FussballCommand implements CommandExecutor, TabCompleter {
|
||||
if (sender.hasPermission("fussball.admin")) list.addAll(List.of("create", "delete", "setup", "stop", "setgk", "debug", "hologram", "dropball"));
|
||||
} else if (args.length == 2 && List.of("join","delete","setup","stop","setgk","debug","spectate","dropball").contains(args[0].toLowerCase())) {
|
||||
list.addAll(plugin.getArenaManager().getArenaNames());
|
||||
} else if (args.length == 2 && args[0].equalsIgnoreCase("stats") && sender.hasPermission("fussball.admin")) {
|
||||
list.addAll(List.of("reset"));
|
||||
} else if (args.length == 3 && args[0].equalsIgnoreCase("stats") && args[1].equalsIgnoreCase("reset") && sender.hasPermission("fussball.admin")) {
|
||||
list.add("all");
|
||||
for (Player onlinePlayer : Bukkit.getOnlinePlayers()) {
|
||||
list.add(onlinePlayer.getName());
|
||||
}
|
||||
} else if (args.length == 2 && args[0].equalsIgnoreCase("hologram")) {
|
||||
list.addAll(List.of("set", "remove", "delete", "reload", "list"));
|
||||
} 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("set", "remove", "delete", "text", "textpreview", "textreset", "reload", "list"));
|
||||
} else if (args.length == 3 && args[0].equalsIgnoreCase("hologram") && List.of("set", "delete", "text", "textpreview", "textreset").contains(args[1].toLowerCase())) {
|
||||
list.addAll(plugin.getArenaManager().getArenaNames());
|
||||
} else if (args.length == 4 && args[0].equalsIgnoreCase("hologram") && List.of("set", "delete", "text", "textpreview", "textreset").contains(args[1].toLowerCase())) {
|
||||
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 == 5 && args[0].equalsIgnoreCase("hologram") && args[1].equalsIgnoreCase("text")) {
|
||||
list.addAll(List.of("1", "2", "3", "4", "5"));
|
||||
} else if (args.length == 3 && args[0].equalsIgnoreCase("setgk")) {
|
||||
// Spielernamen aus dem aktiven Spiel vorschlagen
|
||||
Game gkGame = plugin.getGameManager().getGame(args[1]);
|
||||
|
||||
@@ -4,6 +4,7 @@ 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.ChatColor;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Color;
|
||||
import org.bukkit.Location;
|
||||
@@ -15,6 +16,10 @@ import org.bukkit.util.Transformation;
|
||||
import org.joml.AxisAngle4f;
|
||||
import org.joml.Vector3f;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
@@ -44,14 +49,22 @@ public class FussballHologram {
|
||||
private final Map<UUID, Interaction> playerInteractions = new ConcurrentHashMap<>();
|
||||
/** Aktuell angezeigte Seite (0 = GOALS, 1 = WINS) pro Spieler */
|
||||
private final Map<UUID, Integer> currentPage = new ConcurrentHashMap<>();
|
||||
private final Map<HoloType, List<String>> customTextTemplates = new EnumMap<>(HoloType.class);
|
||||
|
||||
private final Fussball plugin;
|
||||
|
||||
public FussballHologram(String id, Location location, HoloType type, Fussball plugin) {
|
||||
this(id, location, type, plugin, Collections.emptyMap());
|
||||
}
|
||||
|
||||
public FussballHologram(String id, Location location, HoloType type, Fussball plugin, Map<HoloType, List<String>> customTextTemplates) {
|
||||
this.id = id;
|
||||
this.location = location.clone();
|
||||
this.type = type;
|
||||
this.plugin = plugin;
|
||||
for (Map.Entry<HoloType, List<String>> entry : customTextTemplates.entrySet()) {
|
||||
setCustomText(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
// ── Seiten-Wechsel ───────────────────────────────────────────────────────
|
||||
@@ -183,70 +196,228 @@ public class FussballHologram {
|
||||
.anyMatch(i -> i.getUniqueId().equals(entityId));
|
||||
}
|
||||
|
||||
public void setCustomText(HoloType targetType, List<String> lines) {
|
||||
if (lines == null || lines.isEmpty()) {
|
||||
customTextTemplates.remove(targetType);
|
||||
return;
|
||||
}
|
||||
List<String> sanitized = new ArrayList<>();
|
||||
for (String line : lines) {
|
||||
sanitized.add(ChatColor.translateAlternateColorCodes('&', line));
|
||||
}
|
||||
customTextTemplates.put(targetType, List.copyOf(sanitized));
|
||||
}
|
||||
|
||||
public void clearCustomText(HoloType targetType) {
|
||||
customTextTemplates.remove(targetType);
|
||||
}
|
||||
|
||||
public List<String> getCustomText(HoloType targetType) {
|
||||
return customTextTemplates.getOrDefault(targetType, Collections.emptyList());
|
||||
}
|
||||
|
||||
/** 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();
|
||||
String customText = buildCustomStatsText(showType);
|
||||
if (customText != null) {
|
||||
return customText;
|
||||
}
|
||||
|
||||
return buildDefaultStatsText(showType);
|
||||
}
|
||||
|
||||
private String buildDefaultStatsText(HoloType showType) {
|
||||
String nameColor = holoColor("name-color", "&0");
|
||||
String labelColor = holoColor("label-color", "&8");
|
||||
String sep = holoColor("separator-color", "&8&m") + "══════════════════════" + ChatColor.RESET;
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
if (showType == HoloType.GOALS) {
|
||||
sb.append("§6§l⚽ TOP 10 TORSCHÜTZEN ⚽\n");
|
||||
sb.append("§8§m══════════════════════§r\n");
|
||||
String title = holoColor("goals-title-color", "&6&l") + "⚽ TOP 10 TORSCHÜTZEN ⚽";
|
||||
String valColor = holoColor("goals-value-color", "&4");
|
||||
String toggle = holoColor("toggle-color", "&8&o") + "[Rechtsklick → Siege anzeigen]";
|
||||
sb.append(title).append("\n");
|
||||
sb.append(sep).append("\n");
|
||||
var list = plugin.getStatsManager().getTopScorers(10);
|
||||
if (list.isEmpty()) {
|
||||
sb.append("§8Noch keine Statistiken vorhanden.");
|
||||
sb.append(labelColor).append("Noch keine Statistiken vorhanden.");
|
||||
} else {
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
StatsManager.PlayerStats s = list.get(i).getValue();
|
||||
sb.append(medal(i + 1))
|
||||
.append(" §0").append(s.name)
|
||||
.append(" §4").append(s.goals).append(" §8Tore");
|
||||
.append(" ").append(nameColor).append(s.name)
|
||||
.append(" ").append(valColor).append(s.goals).append(" ").append(labelColor).append("Tore");
|
||||
if (i < list.size() - 1) sb.append("\n");
|
||||
}
|
||||
}
|
||||
sb.append("\n§8§m══════════════════════§r");
|
||||
sb.append("\n§8§o[Rechtsklick → Siege anzeigen]");
|
||||
sb.append("\n").append(sep);
|
||||
sb.append("\n").append(toggle);
|
||||
|
||||
} else {
|
||||
sb.append("§2§l🏆 TOP 10 GEWINNER 🏆\n");
|
||||
sb.append("§8§m══════════════════════§r\n");
|
||||
String title = holoColor("wins-title-color", "&2&l") + "🏆 TOP 10 GEWINNER 🏆";
|
||||
String valColor = holoColor("wins-value-color", "&2");
|
||||
String toggle = holoColor("toggle-color", "&8&o") + "[Rechtsklick → Tore anzeigen]";
|
||||
sb.append(title).append("\n");
|
||||
sb.append(sep).append("\n");
|
||||
var list = plugin.getStatsManager().getTopWins(10);
|
||||
if (list.isEmpty()) {
|
||||
sb.append("§8Noch keine Statistiken vorhanden.");
|
||||
sb.append(labelColor).append("Noch keine Statistiken vorhanden.");
|
||||
} else {
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
StatsManager.PlayerStats s = list.get(i).getValue();
|
||||
sb.append(medal(i + 1))
|
||||
.append(" §0").append(s.name)
|
||||
.append(" §2").append(s.wins).append(" §8Siege")
|
||||
.append(" §8(").append(String.format("%.0f", s.getWinRate())).append("%)");
|
||||
.append(" ").append(nameColor).append(s.name)
|
||||
.append(" ").append(valColor).append(s.wins).append(" ").append(labelColor).append("Siege")
|
||||
.append(" ").append(labelColor).append("(").append(String.format("%.0f", s.getWinRate())).append("%)");
|
||||
if (i < list.size() - 1) sb.append("\n");
|
||||
}
|
||||
}
|
||||
sb.append("\n§8§m══════════════════════§r");
|
||||
sb.append("\n§8§o[Rechtsklick → Tore anzeigen]");
|
||||
sb.append("\n").append(sep);
|
||||
sb.append("\n").append(toggle);
|
||||
}
|
||||
|
||||
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--:--";
|
||||
private String buildCustomStatsText(HoloType showType) {
|
||||
List<String> template = customTextTemplates.get(showType);
|
||||
if (template == null || template.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
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());
|
||||
String sep = holoColor("separator-color", "&8&m") + "══════════════════════" + ChatColor.RESET;
|
||||
String title, toggle;
|
||||
if (showType == HoloType.GOALS) {
|
||||
title = holoColor("goals-title-color", "&6&l") + "⚽ TOP 10 TORSCHÜTZEN ⚽";
|
||||
toggle = holoColor("toggle-color", "&8&o") + "[Rechtsklick → Siege anzeigen]";
|
||||
} else {
|
||||
title = holoColor("wins-title-color", "&2&l") + "🏆 TOP 10 GEWINNER 🏆";
|
||||
toggle = holoColor("toggle-color", "&8&o") + "[Rechtsklick → Tore anzeigen]";
|
||||
}
|
||||
String entries = buildStatsEntries(showType);
|
||||
|
||||
return header + "\n" + separator + "\n" + phase + "\n" + score + "\n" + timeLabel;
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < template.size(); i++) {
|
||||
String line = template.get(i)
|
||||
.replace("{title}", title)
|
||||
.replace("{separator}", sep)
|
||||
.replace("{entries}", entries)
|
||||
.replace("{toggle}", toggle);
|
||||
sb.append(line);
|
||||
if (i < template.size() - 1) {
|
||||
sb.append("\n");
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private String buildStatsEntries(HoloType showType) {
|
||||
String nameColor = holoColor("name-color", "&0");
|
||||
String labelColor = holoColor("label-color", "&8");
|
||||
StringBuilder entries = new StringBuilder();
|
||||
if (showType == HoloType.GOALS) {
|
||||
String valColor = holoColor("goals-value-color", "&4");
|
||||
var list = plugin.getStatsManager().getTopScorers(10);
|
||||
if (list.isEmpty()) {
|
||||
return labelColor + "Noch keine Statistiken vorhanden.";
|
||||
}
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
StatsManager.PlayerStats s = list.get(i).getValue();
|
||||
entries.append(medal(i + 1))
|
||||
.append(" ").append(nameColor).append(s.name)
|
||||
.append(" ").append(valColor).append(s.goals).append(" ").append(labelColor).append("Tore");
|
||||
if (i < list.size() - 1) {
|
||||
entries.append("\n");
|
||||
}
|
||||
}
|
||||
return entries.toString();
|
||||
}
|
||||
|
||||
String valColor = holoColor("wins-value-color", "&2");
|
||||
var list = plugin.getStatsManager().getTopWins(10);
|
||||
if (list.isEmpty()) {
|
||||
return labelColor + "Noch keine Statistiken vorhanden.";
|
||||
}
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
StatsManager.PlayerStats s = list.get(i).getValue();
|
||||
entries.append(medal(i + 1))
|
||||
.append(" ").append(nameColor).append(s.name)
|
||||
.append(" ").append(valColor).append(s.wins).append(" ").append(labelColor).append("Siege")
|
||||
.append(" ").append(labelColor).append("(").append(String.format("%.0f", s.getWinRate())).append("%)");
|
||||
if (i < list.size() - 1) {
|
||||
entries.append("\n");
|
||||
}
|
||||
}
|
||||
return entries.toString();
|
||||
}
|
||||
|
||||
private String buildMatchText() {
|
||||
String customText = buildCustomMatchText();
|
||||
if (customText != null) {
|
||||
return customText;
|
||||
}
|
||||
|
||||
Game game = findRelevantGame();
|
||||
String header = holoColor("match-header-color", "&e&l") + "Live Match";
|
||||
String sep = holoColor("separator-color", "&8&m") + "────────────────";
|
||||
if (game == null) {
|
||||
String lc = holoColor("label-color", "&8");
|
||||
return header + "\n" + sep + "\n§7Kein Spiel aktiv\n" + lc + "- : -\n" + lc + "--:--";
|
||||
}
|
||||
|
||||
String phase = buildPhaseLabel(game);
|
||||
String score = holoColor("match-score-red", "&c&l") + game.getRedScore()
|
||||
+ " §r§7: "
|
||||
+ holoColor("match-score-blue", "&9&l") + game.getBlueScore();
|
||||
String timeLabel = game.isInInjuryTime()
|
||||
? holoColor("match-injury-color", "&c") + "+" + formatInjury(game.getInjuryTimeBuffer()) + " §7(Nachspielzeit)"
|
||||
: holoColor("match-time-color", "&e") + formatMainTime(game.getTimeLeft());
|
||||
|
||||
return header + "\n" + sep + "\n" + phase + "\n" + score + "\n" + timeLabel;
|
||||
}
|
||||
|
||||
private String buildCustomMatchText() {
|
||||
List<String> template = customTextTemplates.get(HoloType.MATCH);
|
||||
if (template == null || template.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Game game = findRelevantGame();
|
||||
String header = holoColor("match-header-color", "&e&l") + "Live Match";
|
||||
String separator = holoColor("separator-color", "&8&m") + "────────────────";
|
||||
String phase = game == null ? "§7Kein Spiel aktiv" : buildPhaseLabel(game);
|
||||
String score, time;
|
||||
if (game == null) {
|
||||
String lc = holoColor("label-color", "&8");
|
||||
score = lc + "- : -";
|
||||
time = lc + "--:--";
|
||||
} else {
|
||||
score = holoColor("match-score-red", "&c&l") + game.getRedScore()
|
||||
+ " §r§7: "
|
||||
+ holoColor("match-score-blue", "&9&l") + game.getBlueScore();
|
||||
time = game.isInInjuryTime()
|
||||
? holoColor("match-injury-color", "&c") + "+" + formatInjury(game.getInjuryTimeBuffer()) + " §7(Nachspielzeit)"
|
||||
: holoColor("match-time-color", "&e") + formatMainTime(game.getTimeLeft());
|
||||
}
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < template.size(); i++) {
|
||||
String line = template.get(i)
|
||||
.replace("{header}", header)
|
||||
.replace("{separator}", separator)
|
||||
.replace("{phase}", phase)
|
||||
.replace("{score}", score)
|
||||
.replace("{time}", time);
|
||||
sb.append(line);
|
||||
if (i < template.size() - 1) {
|
||||
sb.append("\n");
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private String buildPhaseLabel(Game game) {
|
||||
@@ -317,6 +488,11 @@ public class FussballHologram {
|
||||
return "+" + safe + "s";
|
||||
}
|
||||
|
||||
private String holoColor(String key, String def) {
|
||||
String val = plugin.getConfig().getString("holograms." + key, def);
|
||||
return ChatColor.translateAlternateColorCodes('&', val != null ? val : def);
|
||||
}
|
||||
|
||||
private String medal(int rank) {
|
||||
return switch (rank) {
|
||||
case 1 -> "§6§l#1"; // Gold bleibt – hebt sich gut ab
|
||||
|
||||
@@ -21,6 +21,10 @@ import org.bukkit.scheduler.BukkitTask;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
@@ -115,7 +119,7 @@ public class HologramManager implements Listener {
|
||||
type = FussballHologram.HoloType.GOALS;
|
||||
}
|
||||
|
||||
holograms.put(id, new FussballHologram(id, loc, type, plugin));
|
||||
holograms.put(id, new FussballHologram(id, loc, type, plugin, loadCustomTextTemplates(path)));
|
||||
}
|
||||
|
||||
plugin.getLogger().info("[Hologram] " + holograms.size() + " Hologramme geladen.");
|
||||
@@ -123,10 +127,12 @@ public class HologramManager implements Listener {
|
||||
|
||||
/**
|
||||
* Erstellt ein neues Hologramm und speichert es in holograms.yml.
|
||||
* Falls die ID bereits existiert, wird das alte sauber entfernt.
|
||||
* Falls die ID bereits existiert, wird kein Hologramm ersetzt.
|
||||
*/
|
||||
public boolean createHologram(String id, Location loc, FussballHologram.HoloType type) {
|
||||
if (holograms.containsKey(id)) removeHologram(id);
|
||||
if (holograms.containsKey(id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String path = "holograms." + id;
|
||||
holoConfig.set(path + ".world", loc.getWorld().getName());
|
||||
@@ -187,6 +193,7 @@ public class HologramManager implements Listener {
|
||||
|
||||
/** Alle Hologramme neu laden (z.B. nach /fb hologram reload) */
|
||||
public void reload() {
|
||||
plugin.reloadConfig();
|
||||
loadConfig();
|
||||
loadHolograms();
|
||||
// Für alle Online-Spieler sofort rendern
|
||||
@@ -195,6 +202,51 @@ public class HologramManager implements Listener {
|
||||
}
|
||||
}
|
||||
|
||||
public FussballHologram getHologram(String id) {
|
||||
return holograms.get(id);
|
||||
}
|
||||
|
||||
public boolean setCustomText(String id, FussballHologram.HoloType type, List<String> lines) {
|
||||
FussballHologram holo = holograms.get(id);
|
||||
if (holo == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
List<String> sanitized = new ArrayList<>(lines);
|
||||
holo.setCustomText(type, sanitized);
|
||||
holoConfig.set(getTextPath(id, type), sanitized);
|
||||
saveConfig();
|
||||
rerenderHologram(holo);
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean resetCustomText(String id, FussballHologram.HoloType type) {
|
||||
FussballHologram holo = holograms.get(id);
|
||||
if (holo == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
holo.clearCustomText(type);
|
||||
holoConfig.set(getTextPath(id, type), null);
|
||||
saveConfig();
|
||||
rerenderHologram(holo);
|
||||
return true;
|
||||
}
|
||||
|
||||
public List<String> getCustomText(String id, FussballHologram.HoloType type) {
|
||||
FussballHologram holo = holograms.get(id);
|
||||
if (holo == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return holo.getCustomText(type);
|
||||
}
|
||||
|
||||
public void refreshAll() {
|
||||
for (Player player : Bukkit.getOnlinePlayers()) {
|
||||
holograms.values().forEach(h -> h.renderForPlayer(player));
|
||||
}
|
||||
}
|
||||
|
||||
// ── Render-Task ──────────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
@@ -294,4 +346,25 @@ public class HologramManager implements Listener {
|
||||
|
||||
/** @return Set aller Hologramm-IDs (für Tab-Completion) */
|
||||
public Set<String> getHologramIds() { return holograms.keySet(); }
|
||||
|
||||
private Map<FussballHologram.HoloType, List<String>> loadCustomTextTemplates(String path) {
|
||||
Map<FussballHologram.HoloType, List<String>> templates = new EnumMap<>(FussballHologram.HoloType.class);
|
||||
for (FussballHologram.HoloType holoType : FussballHologram.HoloType.values()) {
|
||||
List<String> lines = holoConfig.getStringList(path + ".text." + holoType.name().toLowerCase());
|
||||
if (!lines.isEmpty()) {
|
||||
templates.put(holoType, lines);
|
||||
}
|
||||
}
|
||||
return templates;
|
||||
}
|
||||
|
||||
private String getTextPath(String id, FussballHologram.HoloType type) {
|
||||
return "holograms." + id + ".text." + type.name().toLowerCase();
|
||||
}
|
||||
|
||||
private void rerenderHologram(FussballHologram holo) {
|
||||
for (Player player : Bukkit.getOnlinePlayers()) {
|
||||
holo.renderForPlayer(player);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -124,6 +124,34 @@ public class StatsManager {
|
||||
save();
|
||||
}
|
||||
|
||||
public boolean resetStats(UUID uuid) {
|
||||
boolean removed = cache.remove(uuid) != null;
|
||||
if (!removed) {
|
||||
return false;
|
||||
}
|
||||
save();
|
||||
return true;
|
||||
}
|
||||
|
||||
public int resetAllStats() {
|
||||
int count = cache.size();
|
||||
if (count == 0) {
|
||||
return 0;
|
||||
}
|
||||
cache.clear();
|
||||
save();
|
||||
return count;
|
||||
}
|
||||
|
||||
public UUID findPlayerUuidByName(String playerName) {
|
||||
for (Map.Entry<UUID, PlayerStats> entry : cache.entrySet()) {
|
||||
if (entry.getValue().name != null && entry.getValue().name.equalsIgnoreCase(playerName)) {
|
||||
return entry.getKey();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/** Gibt die Top-N-Torschützen zurück, sortiert nach Toren */
|
||||
public List<Map.Entry<UUID, PlayerStats>> getTopScorers(int limit) {
|
||||
List<Map.Entry<UUID, PlayerStats>> list = new ArrayList<>(cache.entrySet());
|
||||
|
||||
@@ -61,6 +61,24 @@ atmosphere:
|
||||
enabled: true
|
||||
goal-fireworks: 5 # Anzahl Feuerwerke bei einem Tor (0 = deaktiviert)
|
||||
|
||||
# ── Hologramm-Farben ────────────────────────────────────────────────────────
|
||||
# Verwende &-Codes (z.B. &6 = Gold, &c = Rot, &9 = Blau, &l = Fett, &o = Kursiv)
|
||||
# Änderungen werden nach /fb hologram reload wirksam.
|
||||
holograms:
|
||||
goals-title-color: "&6&l" # Titel Tore-Hologramm (Standard: Gold + Fett)
|
||||
goals-value-color: "&4" # Tor-Anzahl in der Liste (Standard: Dunkelrot)
|
||||
wins-title-color: "&2&l" # Titel Siege-Hologramm (Standard: Dunkelgrün + Fett)
|
||||
wins-value-color: "&2" # Siege-Anzahl in der Liste (Standard: Dunkelgrün)
|
||||
name-color: "&0" # Spielername in der Liste (Standard: Schwarz)
|
||||
label-color: "&8" # Beschriftungen (Tore/Siege) (Standard: Dunkelgrau)
|
||||
separator-color: "&8&m" # Trennlinie (Standard: Durchgestrichen)
|
||||
toggle-color: "&8&o" # Umschalte-Hinweis (Standard: Kursiv Dunkelgrau)
|
||||
match-header-color: "&e&l" # Match-Header (Standard: Gelb + Fett)
|
||||
match-score-red: "&c&l" # Rot-Team Spielstand (Standard: Rot + Fett)
|
||||
match-score-blue: "&9&l" # Blau-Team Spielstand (Standard: Blau + Fett)
|
||||
match-time-color: "&e" # Spielzeit (Standard: Gelb)
|
||||
match-injury-color: "&c" # Nachspielzeit (Standard: Rot)
|
||||
|
||||
# ── Nachrichten (alle editierbar) ─────────────────────────────────────────────
|
||||
# Verfügbare Platzhalter je nach Kontext:
|
||||
# {player} = Spielername
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: Fussball
|
||||
version: 1.0.1
|
||||
version: 1.0.3
|
||||
main: de.fussball.plugin.Fussball
|
||||
api-version: 1.21
|
||||
author: M_Viper
|
||||
@@ -19,5 +19,5 @@ permissions:
|
||||
commands:
|
||||
fussball:
|
||||
description: Hauptbefehl des Fußball-Plugins
|
||||
usage: /fussball <join|leave|spectate|team|list|stats|top|history|create|delete|setup|stop|setgk|dropball|debug|hologram>
|
||||
usage: "/fussball <join|leave|spectate|team|list|stats|top|history|create|delete|setup|stop|setgk|dropball|debug|hologram> | Admin: stats reset, hologram text/textpreview/textreset"
|
||||
aliases: [fb, soccer]
|
||||
Reference in New Issue
Block a user