Siegbedingungen vollstaendig abbilden, samt Race-Modus (#304)

Vorsprung und Deckelung je Spiel, dieselbe Regel eine Ebene tiefer fuer den
einzelnen Satz, eine Auswahl der Satzzahl (feste Zahl oder Gewinnsaetze) samt
optional abweichender Regel fuer den Entscheidungssatz, eine Auswahl
"Mehrere - mit Ergebnis je Satz" mit Satzzeilen im Begegnungsformular — und
eine Zaehlweise "Race-Modus", in der der Stand ueber die ganze Begegnung
durchlaeuft.

Die Regel beschreibt den Wettbewerb, sie lehnt keine Eingabe ab: ein 8:6 dort,
wo ein Spiel bei 6 endet, hat oft einen Grund, den keine Regel kennt, und ueber
die Richtigkeit wachen beide Mannschaften. Ihr Nutzen liegt darin, dass eine
auswertende Anwendung endlich erkennen kann, ob ein Spiel entschieden ist.

RACE-MODUS. Das Spiel N endet, wenn der LAUFENDE Stand einer Seite N mal den
Schritt erreicht; eingetragen wird wie bisher die DIFFERENZ dieses Spiels, und
der Begegnungsstand bleibt ihre Summe. Gemessen an 91 Bundesliga-Begegnungen:
6:5 5:7 7:5 4:7 8:2 6:9 6:5 summiert sich auf 42:40, und laufend gelesen trifft
jedes Spiel seine Etappe (6, 12, 18, 24, 30, 36, 42). Die Ergaenzung im Formular
rechnet deshalb mit laufenden Staenden und zieht ab, was schon auf dem Konto
steht.

Im Formular steht statt des gespeicherten "Zwischenergebnis" jetzt der
GESAMTSTAND, gross und in Echtzeit: die Spielpunkte, gerechnet aus den
eingetragenen Spielen mit derselben Arithmetik, die der Server beim Speichern
anwendet. Es ist DIE Zahl, die die Begegnung nach aussen hat — Liste,
Mannschaftsseite und die Kopfzeile der Begegnungsdetails zeigen alle
heim_spielpunkte:gast_spielpunkte. Gemessen an einem 5er-Spielplan mit "Sieg: 1
Punkt": ein eingetragenes 6:4 ergibt "1:0", genau wie die Details. Die Torsumme
gehoert dagegen in die Tabellenspalte "Tore" und nicht in den Kopf einer
Begegnung. Die Zeile steht als letzte des Kopfes, unter dem Kommentar, mit
demselben senkrechten Abstand, den die Spiele untereinander haben — und ohne
erklaerenden Zusatz: "Gesamtstand" samt Zahl sagt es selbst.

Das Ziel wird nicht eingegeben, sondern folgt aus der Spielfolge. Dort steht
deshalb die Rechenvorschrift ("Anzahl der Spiele aus der Spielfolge x Schritt je
Spiel") und die konkrete Zahl erst ab zwei Spielen ("derzeit 7 Spiele x 6 = 42
Tore"): bei einem einzigen — dem Anfangszustand — waere "1 Spiele x 6 = 6 Tore"
nicht nur ungrammatisch, sondern eine Auskunft ueber einen Zustand, den niemand
gemeint hat. Mindestvorsprung, Grenze und ein moegliches
Unentschieden gelten nur fuer das letzte Spiel. Die Zaehlweise setzt
"Spielergebnis als Spielpunkte" und einen Satz je Spiel voraus und setzt beides
mit; das Ziel wird zusaetzlich nach punkte_sieg_* gespiegelt (mit Vorzeichen
fuer das Unentschieden), damit eine aeltere Programmfassung dieselbe wahre
Aussage liest. Vorrunde und Hauptrunde sind zwei Mannschaftsspielplaene. Die Zaehlweise steht als
erste Zeile ihrer Gruppe: was sie ausblendet, liegt darunter — Felder ueber einer
Auswahl verschwinden zu lassen, liest sich als Fehler.

Damit die Einstellungen ueberhaupt auseinanderzuhalten sind, steht dem Editor
eine kurze Begriffsleiter voran: Begegnung, Spiel, Satz, Tor. Sie stecken
ineinander, und die Gruppen darunter folgen genau dieser Reihenfolge. Drei
weitere Hinweiszeilen sagen, was "Ergebnis je Satz" bedeutet, wozu die Angaben
gut sind und wie der Race-Modus zaehlt.

Die vierte Spielpunkte-Wertung hiess "Keine" und sagte damit das Gegenteil
dessen, was sie tut: gewertet wird alles, nur ohne Umrechnung — das Ergebnis
des Spiels IST sein Spielpunktestand. Sie heisst jetzt "Ohne Umrechnung - Tore
bzw. Saetze sind die Spielpunkte"; die drei uebrigen beginnen mit "Sieg:", also
sagt schon der Satzanfang, dass es hier nicht um einen Sieg-Wert geht.

Die Hinweiszeilen brechen ausdruecklich um und haben ein Mass (max-width): die
Zellen dieses Formulars tragen alle `nowrap`, was fuer Beschriftungen und
Auswahlfelder richtig ist und fuer Prosa falsch — die Hinweise zogen die Tabelle
sonst beliebig breit. Wo ein Hinweis steht, ist die Beschriftung oben
ausgerichtet: sonst stand sie auf halber Hoehe des Absatzes statt neben dem
Auswahlfeld, zu dem sie gehoert.

Die Satzebene fuehrt dasselbe Vorzeichen wie die Spielebene: der Betrag ist der
Siegwert, ein negatives Vorzeichen erlaubt zusaetzlich ein Unentschieden ein Tor
darunter. Ohne diese Faelle waere ein Satz, der remis endet, gar nicht
hinterlegbar gewesen.

Bestehende "Mehrere"-Modi werden in die Satzzahl UEBERFUEHRT, und zwar restlos: 1 wird ein fester Satz, ein positiver Wert N werden N
Gewinnsaetze, ein negativer wird die feste Satzzahl 2(|N|-1) — denn ein Remis
setzt voraus, dass alle Saetze gespielt werden. Auch Werte jenseits der neuen
Auswahl werden ueberfuehrt statt als "Beliebig" liegen zu bleiben, denn sie sind
nicht unsinnig, nur ungewoehnlich: ein gespeichertes "Spiel gewonnen bei 42
Saetzen" bleibt nach der Ueberfuehrung dieselbe wahre Aussage, nur an der
richtigen Stelle. Das Formular zeigt einen solchen Wert als EIGENEN,
vorgewaehlten Eintrag der Auswahl ("42 Gewinnsaetze"), speichert ihn unveraendert
wieder und bietet ihn, einmal weggestellt, nicht mehr an. Sonst haette dort eine
Zeile ohne Auswahlfeld gestanden — die Beschriftung "Spiel abgeschlossen bei:"
und rechts daneben nichts — oder die Zahl waere beim ersten Speichern
verschwunden. Gemessen an drei Bestandswerten: 42 wird -42 und uebersteht Anzeige
und Speichern unveraendert, -9 wird die feste Satzzahl 16, und nach dem Wechsel
auf "3 Gewinnsaetze" hat die Auswahl 18 statt 19 Eintraege. Der Race-Modus wird
NICHT automatisch erkannt; er ist aus Bestandsdaten nicht sicher abzuleiten und
wird einmal von Hand gewaehlt.

Dass die Ueberfuehrung nichts verschiebt, ist gemessen und nicht bloss
begruendet: dieselbe Datenbank einmal unter der alten Fassung mit altem Schema
und einmal unter dieser, je sieben Begegnungen unveraendert neu gespeichert und
dann Tabellen, Uebersicht, Spielerstatistik und drei Begegnungsdetails
verglichen — 7942 Zeilen HTML und 436 Datenzeilen aus begegnung und teamspiel,
kein einziger Unterschied. Darunter der Bestandsmodus mit punkte_sieg 42, den
die Ueberfuehrung anfasst.

Umgekehrt spiegelt der Editor die Satzzahl VOLLSTAENDIG nach punkte_sieg_*
zurueck, nicht nur bei Gewinnsaetzen: eine feste Satzzahl sagt genau, welche
Endstaende es gibt, und damit auch, bei welchem Stand das Spiel entschieden ist —
4 feste Saetze heissen "gewonnen bei 3, Unentschieden bei 2:2" (-3), 3 feste
Saetze "gewonnen bei 2" (2). Das ist die Umkehrung der Ueberfuehrung und deshalb
verlustfrei. Zuvor blieb punkte_sieg_* bei fester Satzzahl leer, mit der
Begruendung, sie sei keine Siegbedingung — richtig gedacht, aber praktisch falsch:
ein Client, der nur die alten Felder kennt, haette nach einmaligem Oeffnen und
Speichern eines Bestandsmodus "Beliebig" gelesen, wo vorher eine Zahl stand. Und
es gibt einen solchen Client, den wir nicht mitziehen koennen (Kickern). Gemessen:
feste Satzzahl 4 -> -3, 3 -> 2, 6 -> -4, 1 -> 1, und Gewinnsaetze unveraendert
3 -> 3, 2 -> 2.

Die Einheit der Spielebene folgt punktetyp: Tore bei "Einer", Saetze bei
"Mehrere". Bisher stand dort in beiden Faellen "Punkte" — und genau daran war
eine Fehlkonfiguration nicht zu erkennen. Am echten TFVB-Spielplan 2026 sah man
es doppelt: "Spiel abgeschlossen bei 5 Punkte" direkt ueber "Mindestvorsprung 2
Tore", dieselbe Einheit in zwei Woertern. Innerhalb eines Satzes wird ohnehin
durchgaengig von Toren gesprochen.

Und "Spiele in Spielerstatistik" waehlt "Alle Spiele" jetzt ausdruecklich vor,
statt sich darauf zu verlassen, dass es die erste Option ist. Aus "Vorsprung" wird "Mindestvorsprung" (und
der Wert 1 heisst "Keiner"), aus "Deckelung" wird "Spaetestens gewonnen bei".

Die Einstellungen sind in fuenf Gruppen geordnet, vom Grossen ins Kleine:
Allgemein, Begegnung, Spiel, Satz, Spieler und Statistik. Dafuer wechseln drei
bestehende Zeilen den Platz — "Aktiv" nach oben zu "Bezeichnung",
"Spielernamen" hinunter zu "Spiele in Spielerstatistik", und die Gruppe
"Begegnung" vor die Gruppe "Spiel"; sonst ist nichts verschoben. Der Knopf
"Doppel getrennt einstellen" bekommt eine eigene Zeile, weil er auch die
Satzregel schaltet. Eine Gruppe ohne sichtbare Zeile zeigt keine Ueberschrift.

Die Spielfolge zeigt nur noch die Zeilen, die es gibt: Spiel 1 steht fest, weitere
kommen ueber "Spiel hinzufuegen", jede weitere traegt ein Kreuz zum Entfernen — ohne
Rueckfrage, denn gespeichert ist bis zum Speichern nichts. Verknuepfungen fangen bei
null an. Alle Zeilen bleiben im Dokument und werden beim Entfernen nach oben geschoben,
weil das Speichern spiel_1, spiel_2 … der Reihe nach liest und beim ersten leeren Paar
abbricht. Das Ziel des Race-Modus rechnet dabei mit: es haengt an der Zahl der Spiele,
und die aendert sich hier.

Bei "Mehrere" beantwortet die Satzzahl die Frage vollstaendig — "Spiel abgeschlossen
bei" wird dort deshalb gar nicht mehr angeboten. Es geht dabei nichts verloren, weil
jeder Bestandswert in die Satzzahl uebergeht (siehe oben) und ein Wert jenseits der
Auswahl dort als eigener Eintrag steht.

Ohne geforderten Mindestvorsprung endet ein Spiel exakt beim Siegwert — eine Grenze
kann dann nie greifen. "Spaetestens gewonnen bei" wird deshalb ueberall nur noch
angeboten, wo ein Vorsprung verlangt ist, und beim Speichern auf null gesetzt.

Die Spielfolge steht schon in derselben Spalte unter den Einstellungen und bekommt
deshalb nur ihre Ueberschrift — als sechste Gruppe. Und "Spiele in Spielerstatistik"
stellte nackte Zahlen neben "Alle Spiele"; sie sagen jetzt, was sie zaehlen ("nur die
ersten 3 Spiele").

Beim WECHSEL der Satzart wird nichts umgerechnet — aus sechs Toren wuerden sechs
Saetze, und umgekehrt; aus "drei gewonnene Saetze" folgt keine Torzahl, es ist also gar
nicht rekonstruierbar. Stattdessen zwei Massnahmen. Erstens fragt der Editor nach, wenn
"Saetze pro Spiel" geaendert wird und unter dem Spielplan schon Ergebnisse stehen.
Zweitens ueberleben echte Satzergebnisse den Weg zurueck von "mit Ergebnis je Satz" auf
"nur der Satzstand": bringt ein Aufruf keine Satzfelder mit, bleibt der Stand
unveraendert und stehen im Bestand mehr als ein Paar, bleibt ergebnis_detailliert, wie
es ist — sonst haette blosses Oeffnen und Speichern "6:3 3:6 6:3 3:6 8:6" durch "3:2"
ersetzt. Wird der Stand geaendert, wird der Detailtext wie bisher neu geschrieben.

RUECKWAERTSKOMPATIBEL, und zwar geprueft und nicht bloss behauptet.

EINE Datenbankversion, 122, nicht sechs (Sven 2026-09-03): alle Spalten in einem
ALTER, die Ueberfuehrung dahinter, ein Zaehler hoch. 121 gehoert dem
Hall-of-Fame-Zweig (PR #303 / Issue #300), auf dem dieser Stand aufsetzt; dieser
Block muss deshalb der LETZTE der Kette bleiben — $datenbank_version wird einmal
gelesen, beide Bloecke laufen also im selben Aufruf, aber die Nummer des zuletzt
gelaufenen bleibt stehen. Gemessen gegen eine Datenbank, die #303 schon hinter sich
hat: 121 -> 122, unsere 24 Spalten dazu, die drei Hall-of-Fame-Spalten unberuehrt,
der Bestandsmodus mit punkte_sieg 42 als saetze_modus -42 ueberfuehrt. Die Zwischenstufen waren nur
die Reihenfolge, in der ich gebaut habe — kein Bestand hat sie je gesehen, und
sechs Schritte, die immer zusammen laufen, sind sechs Gelegenheiten, auf halbem
Weg stehenzubleiben. Gemessen an einer Datenbank auf Stand 120: ein Aufruf, 120
-> 122, 13 Spalten -> 37, der Bestandsmodus mit punkte_sieg 42 als saetze_modus
-42 ueberfuehrt; ein zweiter Lauf tut nichts (die Spaltenpruefung und das WHERE
der Ueberfuehrung sehen beide, dass es schon geschehen ist).

Ein Client, der nur die ueberlieferten Felder liest, soll weiterarbeiten wie
bisher — es gibt mindestens einen, den wir von hier aus nicht mitziehen koennen
(Kickern). Dafuer zaehlt nicht, was hinzukommt, sondern was sich an dem aendert,
was er schon liest. Neue Spalten sind harmlos: die Modus-Zeile geht als ganzes
Objekt in die JSON-Ansicht, unbekannte Schluessel ignoriert jeder Client. Die
Ueberfuehrung selbst fasst kein altes Feld an. Bleiben zwei Stellen, und beide
sind versorgt.

Erstens der Wertebereich von punktetyp. "Mehrere - mit Ergebnis je Satz" war
zuerst ein dritter Wert 2 — und punktetyp ist genau das Feld, an dem eine fremde
Anwendung ablesen muss, ob die Punkte eines Spiels Tore oder gewonnene Saetze
sind. Wer "1 = Saetze, sonst Tore" schreibt statt "0 = Tore, sonst Saetze",
haette bei einem Satzstand 3:2 "3:2 Tore" gelesen; beide Fassungen stehen im
Sports Manager selbst. Die Angabe bekommt deshalb eine EIGENE Spalte
(satzergebnisse), punktetyp behaelt seine zwei Werte und seine Bedeutung. Ein
Altclient liest weiter "Mehrere" und rechnet richtig — die Punkte SIND die
gewonnenen Saetze, daran aendert die feinere Erfassung nichts; er verpasst nur
das Zusaetzliche. Die Auswahl im Editor hat weiter drei Eintraege, der dritte
schreibt punktetyp 1 samt satzergebnisse 1.

Zweitens: der Server verlangt die Satzergebnisse NICHT, auch wo sie eingestellt
sind. Er fragt beim Speichern ueberhaupt nicht nach dem Modus — kommt kein
Satzfeld mit, gilt der Summenwert, wie immer. Gemessen an einem Spielplan mit
"3 Gewinnsaetze" und Satzerfassung, gemeldet wie ein Altclient nach
SetsMode.setsOnly (nur spiel_N_*_punkte, keine Satzfelder): Spiel 1 als 3:1
gespeichert, Spielpunkte 1:0, ergebnis_detailliert "3:1" — genau die Zeile, die
das eigene Formular unter "Mehrere" auch schreibt. Dieselbe Begegnung nimmt
gemischt beides auf: Spiel 1 als Satzstand, Spiel 2 als "6:3 3:6 6:4". Meldet der
Altclient danach beide Spiele erneut als Satzstand, ueberleben die Satzergebnisse
von Spiel 2 unveraendert; dasselbe beim Oeffnen und Speichern im Verbandsformular,
das fuer diesen Modus nur Satzzeilen zeigt — es belegt sie fuer Spiel 1 bewusst
nicht vor (aus "3:1" folgt kein erster Satz) und schickt den gespeicherten Stand
im versteckten Summenfeld mit. Nur wenn der Altclient das Spiel AENDERT, weicht
der Detailtext dem neuen Stand ("2:0") — er beschrieb dann ein anderes Ergebnis.

Und wo Satzergebnisse gefuehrt werden, bleibt ergebnis_detailliert bei einer Meldung
ohne Satzfelder LEER, statt wie sonst den Stand zu spiegeln. Dieses eine Paar bedeutet
dort naemlich etwas anderes als sonst: das Feld fuehrt Saetze, also liest man „3:1" als
einen Satz mit drei Toren und rechnet daraus 1:0 statt 3:1 — in unserer eigenen App
steckte genau dieser Fehler. Leer ist die Wahrheit: der Satzstand steht in
teamspiel_*_punkte, die Tore darin sind nicht bekannt, und die Anzeige laesst die
Detailspalte dann weg statt denselben Wert zweimal zu zeigen. Erfundene Platzhalter
(„1:0" je gewonnenen Satz) waeren die Alternative und sind verworfen: gespeichert liesse
sie niemand mehr von einer echten Meldung unterscheiden (Sven 2026-09-03). Es ist die
einzige Stelle, an der das Speichern nach dem Modus fragt. Gemessen: dieselbe
Altclient-Meldung schreibt unter „Mehrere" weiter „4:2" und unter „Mehrere - mit
Ergebnis je Satz" nichts; echte Satzergebnisse daneben bleiben unberuehrt, auch nach
erneuter Summenmeldung und nach Oeffnen und Speichern im Formular.

Der Race-Modus braucht dafuer nichts: fuer einen Altclient sieht ein
Race-Spielplan aus wie punktetyp 0, "Ergebnis als Spielpunkte" und punkte_sieg =
Ziel — also genau so, wie ein solcher Spielplan heute schon eingestellt ist. Er
verliert nichts und gewinnt nichts.

Die Spielerauswahl erscheint nur noch fuer eine Mannschaft, die auch Spieler
hinterlegt hat — sonst stand dort ein Feld mit nichts als dem Leereintrag und
behauptete eine Wahl, die es nicht gibt. Gespeichert wird dann genau wie bei
einer leeren Auswahl: der Server liest die fehlenden Felder mit Vorgabe 0, also
"kein Spieler". Die Absende-Pruefung wird SEITENWEISE erzeugt und liest nur, was
es gibt; ohne Kader auf beiden Seiten bleibt von ihr "return true" uebrig. Sonst
haette eine Forderung nach Namen, die niemand erfuellen kann, das Melden
verhindert statt es zu ordnen. Gemessen an drei Faellen: beide Kader (12 Felder
je Seite), nur der Heimkader (12 zu 0, und die Pruefung liest nur heim), keiner
(0 Felder, Pruefung ohne Feldzugriff, ein 5:3 speichert mit vier Spieler-IDs 0
und Begegnungsstand 5:3 bei Spielpunkten 1:0) — der letzte Fall ausdruecklich
mit "Spielernamen: Pflicht".

Nebenbei behoben:
- Durchweg leere Satzfelder gewannen gegen ein vorhandenes Summenergebnis; das
  blosse Oeffnen und Speichern loeschte damit Altbestand (gemessen: von sieben
  gespeicherten Spielen blieb eines uebrig).
- "Doppel separat" wurde allein aus punkte_sieg_* abgeleitet; abweichende
  Doppelwerte in den neuen Feldern waeren verborgen und beim naechsten
  Speichern ueberschrieben worden.
- Zwei hartkodierte deutsche Zeichenketten im uebersetzten Formular nutzen
  jetzt ihre vorhandenen Sprachschluessel.
- COM_SPORTSMANAGER_SETS fuehrte den Umlaut als HTML-Entitaet und wurde in zwei
  Auswahlfeldern erneut maskiert ("Sätze"); jetzt literal.
This commit is contained in:
svennickel
2026-09-06 21:23:49 +02:00
parent 5aee02830d
commit 09ae1c1609
6 changed files with 2108 additions and 126 deletions
@@ -4553,6 +4553,21 @@ function adminEditTeamspielmodus(): void
$row = $rows[0];
}
// Ob unter diesem Spielplan schon Ergebnisse stehen. „Saetze pro Spiel" zu aendern
// rechnet naemlich nichts um — aus sechs Toren werden sechs Saetze, und aus drei
// Saetzen drei Tore. Das laesst sich auch nicht umrechnen (aus „drei gewonnene
// Saetze" folgt keine Torzahl), also wird davor gewarnt statt gehandelt.
if ($row != null) {
$query = "SELECT COUNT(*) AS anzahl"
. "\n FROM #__sportsmanager_teamspiel AS t"
. "\n INNER JOIN #__sportsmanager_begegnung AS b ON b.begegnung_id = t.begegnung_id"
. "\n INNER JOIN #__sportsmanager_team AS m ON m.team_id = b.heim_team_id"
. "\n INNER JOIN #__sportsmanager_veranstaltung AS v ON v.veranstaltung_id = m.veranstaltung_id"
. "\n WHERE v.modus_id = " . (int)$row->teamspiel_modus_id;
$treffer = loadObjectList($db, $query);
$row->hat_ergebnisse = !empty($treffer) && (int)$treffer[0]->anzahl > 0;
}
HTML_sportsmanager_admin::adminEditTeamspielmodus($row);
}
@@ -4566,10 +4581,204 @@ function adminEditTeamspielmodus(): void
$id = $jInput->get('id', 0, 'INT');
$bezeichnung = $db->escape(trim($jInput->get('bezeichnung', '', 'RAW')));
// Die Auswahl „Saetze pro Spiel" hat drei Eintraege, die Datenbank zwei Felder: der
// dritte Eintrag ist „Mehrere" mit der zusaetzlichen Angabe, dass die Saetze einzeln
// eingetragen werden. punktetyp behaelt damit seine beiden ueberlieferten Werte — es
// ist das Feld, an dem jede fremde Anwendung ablesen muss, ob die Punkte eines Spiels
// Tore oder gewonnene Saetze sind (siehe Datenbankversion 126).
$punktetyp = $jInput->get('punktetyp', 0, 'INT');
$satzergebnisse = $punktetyp == 2 ? 1 : 0;
if ($punktetyp == 2)
$punktetyp = 1;
$punkte_sieg_einzel = $jInput->get('punkte_sieg_einzel', 0, 'INT');
$punkte_sieg_doppel_separat = $jInput->get('punkte_sieg_doppel_separat', 0, 'INT');
$punkte_sieg_doppel = $punkte_sieg_doppel_separat ? $jInput->get('punkte_sieg_doppel', 0, 'INT') : $punkte_sieg_einzel;
// Vorsprung (mindestens 1 Tor, sonst waere kein Spiel je entschieden) und Deckelung
// (0 = keine). Beide folgen derselben "Doppel separat"-Schaltung wie die Siegbedingung,
// zu der sie gehoeren.
$punkte_vorsprung_einzel = max(1, $jInput->get('punkte_vorsprung_einzel', 1, 'INT'));
$punkte_vorsprung_doppel = $punkte_sieg_doppel_separat
? max(1, $jInput->get('punkte_vorsprung_doppel', 1, 'INT'))
: $punkte_vorsprung_einzel;
$punkte_deckel_einzel = max(0, $jInput->get('punkte_deckel_einzel', 0, 'INT'));
$punkte_deckel_doppel = $punkte_sieg_doppel_separat
? max(0, $jInput->get('punkte_deckel_doppel', 0, 'INT'))
: $punkte_deckel_einzel;
// Eine Deckelung unterhalb der Siegbedingung wuerde das Spiel vor seinem eigenen Ziel
// beenden — das ist keine Konfiguration, sondern ein Zahlendreher. Dann gilt sie nicht.
if ($punkte_deckel_einzel != 0 && $punkte_deckel_einzel < abs($punkte_sieg_einzel))
$punkte_deckel_einzel = 0;
if ($punkte_deckel_doppel != 0 && $punkte_deckel_doppel < abs($punkte_sieg_doppel))
$punkte_deckel_doppel = 0;
// Vorsprung und Unentschieden schliessen sich aus: das negative Vorzeichen von
// punkte_sieg_* bedeutet „Remis bei Betrag - 1", und genau dieser Stand ist bei zwei
// Toren Vorsprung kein Endstand, sondern ein laufendes Spiel (5:5 bei Sieg 6). Wird ein
// Vorsprung gefordert, wird deshalb die Variante ohne Unentschieden gespeichert — sonst
// stuende in der Konfiguration zweierlei, das sich widerspricht.
if ($punkte_vorsprung_einzel > 1 && $punkte_sieg_einzel < 0)
$punkte_sieg_einzel = abs($punkte_sieg_einzel);
if ($punkte_vorsprung_doppel > 1 && $punkte_sieg_doppel < 0)
$punkte_sieg_doppel = abs($punkte_sieg_doppel);
// Ohne Siegbedingung („Beliebig") gibt es nichts, worauf sich Vorsprung oder Deckelung
// beziehen koennten. Dann bleiben beide auf ihren neutralen Werten, statt eine Regel zu
// behaupten, die nirgends greift.
// Ohne geforderten Vorsprung endet ein Spiel EXAKT beim Siegwert — dann kann eine
// Grenze nie greifen, und sie zu speichern hiesse, eine Regel zu behaupten, die nichts
// tut (Sven 2026-09-01). Gilt auf jeder Ebene gleich.
if ($punkte_vorsprung_einzel <= 1) $punkte_deckel_einzel = 0;
if ($punkte_vorsprung_doppel <= 1) $punkte_deckel_doppel = 0;
if ($punkte_sieg_einzel == 0) {
$punkte_vorsprung_einzel = 1;
$punkte_deckel_einzel = 0;
}
if ($punkte_sieg_doppel == 0) {
$punkte_vorsprung_doppel = 1;
$punkte_deckel_doppel = 0;
}
// Zweite Ebene: die Regel INNERHALB eines Satzes. Sie ergibt nur dort einen Sinn, wo ein
// Spiel ueberhaupt aus Saetzen besteht — bei „Einer" ist das Spiel der Satz, und die
// Regel steht bereits in punkte_sieg_*. 0 heisst „nicht hinterlegt".
// Vorzeichenbehaftet wie punkte_sieg_*: der Betrag ist der Siegwert, ein negatives
// Vorzeichen erlaubt zusaetzlich ein Unentschieden ein Tor darunter.
$satz_sieg_einzel = $jInput->get('satz_sieg_einzel', 0, 'INT');
$satz_sieg_doppel = $punkte_sieg_doppel_separat
? $jInput->get('satz_sieg_doppel', 0, 'INT')
: $satz_sieg_einzel;
$satz_vorsprung_einzel = max(1, $jInput->get('satz_vorsprung_einzel', 1, 'INT'));
$satz_vorsprung_doppel = $punkte_sieg_doppel_separat
? max(1, $jInput->get('satz_vorsprung_doppel', 1, 'INT'))
: $satz_vorsprung_einzel;
$satz_deckel_einzel = max(0, $jInput->get('satz_deckel_einzel', 0, 'INT'));
$satz_deckel_doppel = $punkte_sieg_doppel_separat
? max(0, $jInput->get('satz_deckel_doppel', 0, 'INT'))
: $satz_deckel_einzel;
if ($punktetyp == 0) {
$satzergebnisse = 0;
$satz_sieg_einzel = 0;
$satz_sieg_doppel = 0;
$satz_vorsprung_einzel = 1;
$satz_vorsprung_doppel = 1;
$satz_deckel_einzel = 0;
$satz_deckel_doppel = 0;
}
if ($satz_sieg_einzel == 0) {
$satz_vorsprung_einzel = 1;
$satz_deckel_einzel = 0;
}
if ($satz_sieg_doppel == 0) {
$satz_vorsprung_doppel = 1;
$satz_deckel_doppel = 0;
}
// Wie eine Ebene hoeher: eine Grenze unter dem Siegwert ist ein Zahlendreher, und ein
// gefordertes Mindestvorsprung schliesst das Unentschieden aus — bei zwei Toren
// Vorsprung ist ein 5:5 bei „Sieg 6, Remis 5" kein Endstand, sondern ein laufender Satz.
if ($satz_vorsprung_einzel > 1 && $satz_sieg_einzel < 0)
$satz_sieg_einzel = abs($satz_sieg_einzel);
if ($satz_vorsprung_doppel > 1 && $satz_sieg_doppel < 0)
$satz_sieg_doppel = abs($satz_sieg_doppel);
if ($satz_vorsprung_einzel <= 1) $satz_deckel_einzel = 0;
if ($satz_vorsprung_doppel <= 1) $satz_deckel_doppel = 0;
if ($satz_deckel_einzel != 0 && $satz_deckel_einzel < abs($satz_sieg_einzel))
$satz_deckel_einzel = 0;
if ($satz_deckel_doppel != 0 && $satz_deckel_doppel < abs($satz_sieg_doppel))
$satz_deckel_doppel = 0;
// Wie viele Saetze ein Spiel hat. Eine Auswahl, zwei Familien: eine feste Zahl (alle
// werden gespielt, positiv) oder Gewinnsaetze (best of 2N-1, negativ). 0 = wie bisher.
$saetze_modus_einzel = $jInput->get('saetze_modus_einzel', 0, 'INT');
$saetze_modus_doppel = $punkte_sieg_doppel_separat
? $jInput->get('saetze_modus_doppel', 0, 'INT')
: $saetze_modus_einzel;
// Abweichende Regel fuer den Entscheidungssatz. Den gibt es nur bei Gewinnsaetzen: er
// ist der Satz, der nach N-1 zu N-1 noch aussteht. Bei fester Satzzahl gaebe es keinen,
// und ohne Angabe zur Satzzahl waere nicht bestimmbar, welcher es waere.
$entscheidung_sieg_einzel = $jInput->get('entscheidung_sieg_einzel', 0, 'INT');
$entscheidung_sieg_doppel = $punkte_sieg_doppel_separat
? $jInput->get('entscheidung_sieg_doppel', 0, 'INT')
: $entscheidung_sieg_einzel;
$entscheidung_vorsprung_einzel = max(1, $jInput->get('entscheidung_vorsprung_einzel', 1, 'INT'));
$entscheidung_vorsprung_doppel = $punkte_sieg_doppel_separat
? max(1, $jInput->get('entscheidung_vorsprung_doppel', 1, 'INT'))
: $entscheidung_vorsprung_einzel;
$entscheidung_deckel_einzel = max(0, $jInput->get('entscheidung_deckel_einzel', 0, 'INT'));
$entscheidung_deckel_doppel = $punkte_sieg_doppel_separat
? max(0, $jInput->get('entscheidung_deckel_doppel', 0, 'INT'))
: $entscheidung_deckel_einzel;
if ($punktetyp == 0) {
$saetze_modus_einzel = 0;
$saetze_modus_doppel = 0;
}
// Die Satzzahl wird ZUSAETZLICH nach punkte_sieg_* gespiegelt — dorthin, wo seit jeher
// steht, bei wievielen Saetzen ein Spiel gewonnen ist. Eine aeltere Programmfassung
// liest damit weiter dieselbe wahre Aussage, obwohl sie die neue Spalte nicht kennt.
//
// Gewinnsaetze sind diese Aussage unmittelbar: N Gewinnsaetze = „gewonnen bei N".
// Aber auch eine feste Satzzahl laesst sich vollstaendig so sagen, denn sie legt fest,
// welche Endstaende es gibt: bei 4 festen Saetzen sind 4:0, 3:1 und 2:2 moeglich, also
// „gewonnen bei 3, Unentschieden bei 2:2" — genau die Bedeutung des negativen
// Vorzeichens. Bei ungerader Satzzahl gibt es kein Unentschieden: 3 feste Saetze heissen
// „gewonnen bei 2". Das ist die Umkehrung der Ueberfuehrung (Datenbankversion 124) und
// deshalb verlustfrei in beide Richtungen.
//
// Fuer punkte_sieg_* = 0 („keine Siegbedingung") bleibt damit nur, was es auch bisher
// meinte: „Beliebig" — keine Angabe zur Satzzahl.
$sieg_aus_satzzahl = function ($modus) {
if ($modus < 0) return -$modus; // N Gewinnsaetze
if ($modus == 0) return null; // keine Angabe
return $modus % 2 == 1 // feste Satzzahl
? intdiv($modus + 1, 2) // ungerade: kein Remis
: -(intdiv($modus, 2) + 1); // gerade: Remis in der Mitte
};
$gespiegelt = $sieg_aus_satzzahl($saetze_modus_einzel);
if ($gespiegelt !== null) {
$punkte_sieg_einzel = $gespiegelt;
$punkte_vorsprung_einzel = 1;
$punkte_deckel_einzel = 0;
}
$gespiegelt = $sieg_aus_satzzahl($saetze_modus_doppel);
if ($gespiegelt !== null) {
$punkte_sieg_doppel = $gespiegelt;
$punkte_vorsprung_doppel = 1;
$punkte_deckel_doppel = 0;
}
// Ohne Gewinnsaetze gibt es keinen Entscheidungssatz, und ohne eigene Siegbedingung
// keine Abweichung — dann bleiben die Felder auf ihren neutralen Werten.
if ($saetze_modus_einzel >= 0) $entscheidung_sieg_einzel = 0;
if ($saetze_modus_doppel >= 0) $entscheidung_sieg_doppel = 0;
if ($entscheidung_sieg_einzel == 0) {
$entscheidung_vorsprung_einzel = 1;
$entscheidung_deckel_einzel = 0;
}
if ($entscheidung_sieg_doppel == 0) {
$entscheidung_vorsprung_doppel = 1;
$entscheidung_deckel_doppel = 0;
}
if ($entscheidung_vorsprung_einzel > 1 && $entscheidung_sieg_einzel < 0)
$entscheidung_sieg_einzel = abs($entscheidung_sieg_einzel);
if ($entscheidung_vorsprung_doppel > 1 && $entscheidung_sieg_doppel < 0)
$entscheidung_sieg_doppel = abs($entscheidung_sieg_doppel);
if ($entscheidung_vorsprung_einzel <= 1) $entscheidung_deckel_einzel = 0;
if ($entscheidung_vorsprung_doppel <= 1) $entscheidung_deckel_doppel = 0;
if ($entscheidung_deckel_einzel != 0 && $entscheidung_deckel_einzel < abs($entscheidung_sieg_einzel))
$entscheidung_deckel_einzel = 0;
if ($entscheidung_deckel_doppel != 0 && $entscheidung_deckel_doppel < abs($entscheidung_sieg_doppel))
$entscheidung_deckel_doppel = 0;
// Race-Modus: der Stand laeuft ueber die Begegnung durch.
//
// Das Ziel wird NICHT eingegeben — es ergibt sich aus der Spielfolge (Anzahl Spiele mal
// Schritt) und stuende sonst als zweite Wahrheit neben ihr. Mindestvorsprung, Grenze
// und Unentschieden gelten allein fuer das letzte Spiel, also fuer das Ziel.
$zaehlweise = $jInput->get('zaehlweise', 0, 'INT') != 0 ? 1 : 0;
$race_schritt = max(0, $jInput->get('race_schritt', 0, 'INT'));
$race_vorsprung = max(1, $jInput->get('race_vorsprung', 1, 'INT'));
$race_deckel = max(0, $jInput->get('race_deckel', 0, 'INT'));
$race_remis = $jInput->get('race_remis', 0, 'INT') != 0 ? 1 : 0;
if ($zaehlweise == 0 || $race_schritt == 0) {
$zaehlweise = 0;
$race_schritt = 0;
$race_vorsprung = 1;
$race_deckel = 0;
$race_remis = 0;
}
$heimtausch = $jInput->get('heimtausch', 0, 'INT');
$spiele_in_spielerstatistik = $jInput->get('spiele_in_spielerstatistik', 0, 'INT');
$status = $jInput->get('status', 0, 'INT');
@@ -4616,13 +4825,59 @@ function adminEditTeamspielmodus(): void
if (strlen($verknuepfungen) > 0)
$modus .= "|" . $verknuepfungen;
// Erst hier, weil das Ziel die Anzahl der Spiele braucht: Race setzt „ein Satz je
// Spiel" und „Spielergebnis als Spielpunkte" voraus, und das Ziel wird in die
// Altfelder gespiegelt — dieselbe wahre Aussage, die dort seit jeher steht („das Spiel
// ist bei N Toren gewonnen"), damit eine aeltere Programmfassung nichts Falsches liest.
// Vorsprung und Grenze werden NICHT gespiegelt: sie gelten nur fuer das letzte Spiel,
// waehrend die Altfelder fuer alle gelten wuerden.
if ($zaehlweise == 1) {
$spiele_anzahl = count(array_filter(explode(",", explode("|", $modus)[0]), 'strlen'));
$race_ziel = $spiele_anzahl * $race_schritt;
$punktetyp = 0;
$satzergebnisse = 0;
$saetze_modus_einzel = 0;
$saetze_modus_doppel = 0;
$satz_sieg_einzel = 0;
$satz_sieg_doppel = 0;
$satz_vorsprung_einzel = 1;
$satz_vorsprung_doppel = 1;
$satz_deckel_einzel = 0;
$satz_deckel_doppel = 0;
$entscheidung_sieg_einzel = 0;
$entscheidung_sieg_doppel = 0;
$entscheidung_vorsprung_einzel = 1;
$entscheidung_vorsprung_doppel = 1;
$entscheidung_deckel_einzel = 0;
$entscheidung_deckel_doppel = 0;
$spielpunkte_wertung_einzel = 2;
$spielpunkte_wertung_doppel = 2;
$spielpunkte_bedingung = 0;
$punkte_sieg_einzel = $race_remis ? -$race_ziel : $race_ziel;
$punkte_sieg_doppel = $punkte_sieg_einzel;
$punkte_vorsprung_einzel = 1;
$punkte_vorsprung_doppel = 1;
$punkte_deckel_einzel = 0;
$punkte_deckel_doppel = 0;
// Eine Grenze unter dem Ziel waere ein Zahlendreher — und ohne geforderten
// Vorsprung endet das Rennen ohnehin exakt am Ziel.
if ($race_vorsprung <= 1 || ($race_deckel != 0 && $race_deckel < $race_ziel))
$race_deckel = 0;
// Vorsprung und Unentschieden schliessen sich aus, wie eine Ebene tiefer auch.
if ($race_vorsprung > 1 && $race_remis) {
$race_remis = 0;
$punkte_sieg_einzel = $race_ziel;
$punkte_sieg_doppel = $race_ziel;
}
}
$modus = $db->escape($modus);
if ($id == 0) {
$query = "INSERT INTO #__sportsmanager_teamspiel_modus"
. "\n (bezeichnung, punktetyp, punkte_sieg_einzel, punkte_sieg_doppel, spielpunkte_wertung_einzel, spielpunkte_wertung_doppel,"
. "\n (bezeichnung, punktetyp, satzergebnisse, punkte_sieg_einzel, punkte_sieg_doppel, punkte_vorsprung_einzel, punkte_vorsprung_doppel, punkte_deckel_einzel, punkte_deckel_doppel, satz_sieg_einzel, satz_sieg_doppel, satz_vorsprung_einzel, satz_vorsprung_doppel, satz_deckel_einzel, satz_deckel_doppel, saetze_modus_einzel, saetze_modus_doppel, entscheidung_sieg_einzel, entscheidung_sieg_doppel, entscheidung_vorsprung_einzel, entscheidung_vorsprung_doppel, entscheidung_deckel_einzel, entscheidung_deckel_doppel, zaehlweise, race_schritt, race_vorsprung, race_deckel, race_remis, spielpunkte_wertung_einzel, spielpunkte_wertung_doppel,"
. "\n spielpunkte_bedingung, spielernamen, heimtausch, modus, spiele_in_spielerstatistik, status)"
. "\n VALUES ('$bezeichnung', '$punktetyp', '$punkte_sieg_einzel', '$punkte_sieg_doppel', '$spielpunkte_wertung_einzel',"
. "\n VALUES ('$bezeichnung', '$punktetyp', '$satzergebnisse', '$punkte_sieg_einzel', '$punkte_sieg_doppel', '$punkte_vorsprung_einzel', '$punkte_vorsprung_doppel', '$punkte_deckel_einzel', '$punkte_deckel_doppel', '$satz_sieg_einzel', '$satz_sieg_doppel', '$satz_vorsprung_einzel', '$satz_vorsprung_doppel', '$satz_deckel_einzel', '$satz_deckel_doppel', '$saetze_modus_einzel', '$saetze_modus_doppel', '$entscheidung_sieg_einzel', '$entscheidung_sieg_doppel', '$entscheidung_vorsprung_einzel', '$entscheidung_vorsprung_doppel', '$entscheidung_deckel_einzel', '$entscheidung_deckel_doppel', '$zaehlweise', '$race_schritt', '$race_vorsprung', '$race_deckel', '$race_remis', '$spielpunkte_wertung_einzel',"
. "\n '$spielpunkte_wertung_doppel', '$spielpunkte_bedingung', '$spielernamen', '$heimtausch', '$modus', '$spiele_in_spielerstatistik', '$status');";
$db->setQuery($query);
if (!$db->execute()) {
@@ -4633,8 +4888,32 @@ function adminEditTeamspielmodus(): void
$query = "UPDATE #__sportsmanager_teamspiel_modus"
. "\n SET bezeichnung = '$bezeichnung',"
. "\n punktetyp = '$punktetyp',"
. "\n satzergebnisse = '$satzergebnisse',"
. "\n punkte_sieg_einzel = '$punkte_sieg_einzel',"
. "\n punkte_sieg_doppel = '$punkte_sieg_doppel',"
. "\n punkte_vorsprung_einzel = '$punkte_vorsprung_einzel',"
. "\n punkte_vorsprung_doppel = '$punkte_vorsprung_doppel',"
. "\n punkte_deckel_einzel = '$punkte_deckel_einzel',"
. "\n punkte_deckel_doppel = '$punkte_deckel_doppel',"
. "\n satz_sieg_einzel = '$satz_sieg_einzel',"
. "\n satz_sieg_doppel = '$satz_sieg_doppel',"
. "\n satz_vorsprung_einzel = '$satz_vorsprung_einzel',"
. "\n satz_vorsprung_doppel = '$satz_vorsprung_doppel',"
. "\n satz_deckel_einzel = '$satz_deckel_einzel',"
. "\n satz_deckel_doppel = '$satz_deckel_doppel',"
. "\n saetze_modus_einzel = '$saetze_modus_einzel',"
. "\n saetze_modus_doppel = '$saetze_modus_doppel',"
. "\n entscheidung_sieg_einzel = '$entscheidung_sieg_einzel',"
. "\n entscheidung_sieg_doppel = '$entscheidung_sieg_doppel',"
. "\n entscheidung_vorsprung_einzel = '$entscheidung_vorsprung_einzel',"
. "\n entscheidung_vorsprung_doppel = '$entscheidung_vorsprung_doppel',"
. "\n entscheidung_deckel_einzel = '$entscheidung_deckel_einzel',"
. "\n entscheidung_deckel_doppel = '$entscheidung_deckel_doppel',"
. "\n zaehlweise = '$zaehlweise',"
. "\n race_schritt = '$race_schritt',"
. "\n race_vorsprung = '$race_vorsprung',"
. "\n race_deckel = '$race_deckel',"
. "\n race_remis = '$race_remis',"
. "\n spielpunkte_wertung_einzel = '$spielpunkte_wertung_einzel',"
. "\n spielpunkte_wertung_doppel = '$spielpunkte_wertung_doppel',"
. "\n spielpunkte_bedingung = '$spielpunkte_bedingung',"
@@ -4695,7 +4974,7 @@ function adminEditTeamspielmodus(): void
if (count($rows) < 1) die ("Wrong id");
$teamspiel_modus = $rows[0];
$query = "INSERT INTO #__sportsmanager_teamspiel_modus (bezeichnung, punktetyp, punkte_sieg_einzel, punkte_sieg_doppel, spielpunkte_wertung_einzel, spielpunkte_wertung_doppel, spielpunkte_bedingung, spielernamen, heimtausch, modus, status) VALUES ('" . $db->escape($teamspiel_modus->bezeichnung) . " (Kopie)', '$teamspiel_modus->punktetyp', '$teamspiel_modus->punkte_sieg_einzel', '$teamspiel_modus->punkte_sieg_doppel', '$teamspiel_modus->spielpunkte_wertung_einzel', '$teamspiel_modus->spielpunkte_wertung_doppel', '$teamspiel_modus->spielpunkte_bedingung', '$teamspiel_modus->spielernamen', '$teamspiel_modus->heimtausch', '$teamspiel_modus->modus', '$teamspiel_modus->status');";
$query = "INSERT INTO #__sportsmanager_teamspiel_modus (bezeichnung, punktetyp, satzergebnisse, punkte_sieg_einzel, punkte_sieg_doppel, punkte_vorsprung_einzel, punkte_vorsprung_doppel, punkte_deckel_einzel, punkte_deckel_doppel, satz_sieg_einzel, satz_sieg_doppel, satz_vorsprung_einzel, satz_vorsprung_doppel, satz_deckel_einzel, satz_deckel_doppel, saetze_modus_einzel, saetze_modus_doppel, entscheidung_sieg_einzel, entscheidung_sieg_doppel, entscheidung_vorsprung_einzel, entscheidung_vorsprung_doppel, entscheidung_deckel_einzel, entscheidung_deckel_doppel, zaehlweise, race_schritt, race_vorsprung, race_deckel, race_remis, spielpunkte_wertung_einzel, spielpunkte_wertung_doppel, spielpunkte_bedingung, spielernamen, heimtausch, modus, status) VALUES ('" . $db->escape($teamspiel_modus->bezeichnung) . " (Kopie)', '$teamspiel_modus->punktetyp', '$teamspiel_modus->satzergebnisse', '$teamspiel_modus->punkte_sieg_einzel', '$teamspiel_modus->punkte_sieg_doppel', '$teamspiel_modus->punkte_vorsprung_einzel', '$teamspiel_modus->punkte_vorsprung_doppel', '$teamspiel_modus->punkte_deckel_einzel', '$teamspiel_modus->punkte_deckel_doppel', '$teamspiel_modus->satz_sieg_einzel', '$teamspiel_modus->satz_sieg_doppel', '$teamspiel_modus->satz_vorsprung_einzel', '$teamspiel_modus->satz_vorsprung_doppel', '$teamspiel_modus->satz_deckel_einzel', '$teamspiel_modus->satz_deckel_doppel', '$teamspiel_modus->saetze_modus_einzel', '$teamspiel_modus->saetze_modus_doppel', '$teamspiel_modus->entscheidung_sieg_einzel', '$teamspiel_modus->entscheidung_sieg_doppel', '$teamspiel_modus->entscheidung_vorsprung_einzel', '$teamspiel_modus->entscheidung_vorsprung_doppel', '$teamspiel_modus->entscheidung_deckel_einzel', '$teamspiel_modus->entscheidung_deckel_doppel', '$teamspiel_modus->zaehlweise', '$teamspiel_modus->race_schritt', '$teamspiel_modus->race_vorsprung', '$teamspiel_modus->race_deckel', '$teamspiel_modus->race_remis', '$teamspiel_modus->spielpunkte_wertung_einzel', '$teamspiel_modus->spielpunkte_wertung_doppel', '$teamspiel_modus->spielpunkte_bedingung', '$teamspiel_modus->spielernamen', '$teamspiel_modus->heimtausch', '$teamspiel_modus->modus', '$teamspiel_modus->status');";
$db->setQuery($query);
if (!$db->execute()) {
die($db->stderr(true));
@@ -10938,10 +11217,51 @@ function adminSaveBegegnungSpielplan(): void
} else {
$spiel_heim_punkte_detailliert = $begegnung_input->get('spiel_' . $spiel_nr . '_heim_punkte_detailliert', array(), 'ARRAY');
$spiel_gast_punkte_detailliert = $begegnung_input->get('spiel_' . $spiel_nr . '_gast_punkte_detailliert', array(), 'ARRAY');
// Ein Formular mit Satzfeldern schickt sie fuer JEDES Spiel mit, auch fuer die,
// in denen nichts steht. Durchweg leere Satzfelder sind aber keine Aussage ueber
// Saetze — sie sagen „hier wurde nichts eingetragen". Ohne diese Unterscheidung
// gewinnt die leere Satzangabe gegen ein vorhandenes Summenergebnis, und das
// blosse Oeffnen und Speichern einer alten Begegnung loescht sie: gemessen an
// einer Begegnung mit sieben gespeicherten Spielen, von denen nach dem Speichern
// eines uebrig war.
$satzfelder_leer = true;
foreach ($spiel_heim_punkte_detailliert as $wert) {
if ($wert !== '' && $wert !== null) {
$satzfelder_leer = false;
break;
}
}
if ($satzfelder_leer) {
foreach ($spiel_gast_punkte_detailliert as $wert) {
if ($wert !== '' && $wert !== null) {
$satzfelder_leer = false;
break;
}
}
}
if ($satzfelder_leer) {
$spiel_heim_punkte_detailliert = array();
$spiel_gast_punkte_detailliert = array();
}
if (empty($spiel_heim_punkte_detailliert)) {
$spiel_heim_punkte = $begegnung_input->get('spiel_' . $spiel_nr . '_heim_punkte', 0, 'INT');
$spiel_gast_punkte = $begegnung_input->get('spiel_' . $spiel_nr . '_gast_punkte', 0, 'INT');
$ergebnis_detailliert = $spiel_heim_punkte == 0 && $spiel_gast_punkte == 0 ? '' : ($spiel_heim_punkte . ":" . $spiel_gast_punkte);
// Der gespiegelte Stand („3:1") steht seit jeher in ergebnis_detailliert,
// wenn eine Meldung ohne Satzfelder kommt. Wo der Wettbewerb Ergebnisse JE
// SATZ fuehrt, ist dieses eine Paar aber irrefuehrend: das Feld bedeutet
// dort Saetze, und wer es so liest, sieht einen Satz mit drei Toren und
// rechnet daraus 1:0 statt 3:1 (genau der Fehler, der in unserer eigenen
// App steckte). Ein Altclient, der die Einstellung nicht kennt, meldet
// genau so — deshalb bleibt das Feld dann LEER: der Satzstand steht in
// teamspiel_*_punkte, und die Tore darin sind eben nicht bekannt.
//
// Erfundene Platzhalter („1:0" je gewonnenen Satz) waeren die Alternative
// und sind bewusst verworfen: gespeichert liesse sie niemand mehr von einer
// echten Meldung unterscheiden (Sven 2026-09-03).
$ergebnis_detailliert = ($spiel_heim_punkte == 0 && $spiel_gast_punkte == 0)
|| !empty($teamspiel_modus->satzergebnisse)
? ''
: ($spiel_heim_punkte . ":" . $spiel_gast_punkte);
} else {
$ergebnis_detailliert = "";
$spiel_heim_punkte = 0;
@@ -10997,6 +11317,26 @@ function adminSaveBegegnungSpielplan(): void
$spiel_gast_spieler_2_id = $t;
}
// Echte Satzergebnisse nicht durch ihren eigenen Spiegel ersetzen.
//
// Stellt jemand einen Wettbewerb von „Mehrere - mit Ergebnis je Satz" zurueck
// auf „nur der Satzstand", bietet das Formular danach nur noch die
// Summenfelder — und ein blosses Oeffnen und Speichern schriebe „3:1" ueber
// ein gespeichertes „6:3 3:6 6:3". Eng gefasst: nur wenn dieser Aufruf gar
// keine Satzfelder mitbringt, der Stand unveraendert bleibt und im Bestand
// mehr als ein Paar steht, bleibt der Detailtext, wie er ist (Sven
// 2026-09-01).
if (isset($spiel_heim_punkte_detailliert) && empty($spiel_heim_punkte_detailliert)
&& isset($spiele_nummern[$spiel_nr])) {
$bestand = $spiele_nummern[$spiel_nr];
if ((int)$bestand->teamspiel_heim_punkte === (int)$spiel_heim_punkte
&& (int)$bestand->teamspiel_gast_punkte === (int)$spiel_gast_punkte
&& !empty($bestand->ergebnis_detailliert)
&& count(explode(" ", trim($bestand->ergebnis_detailliert))) > 1) {
$ergebnis_detailliert = $bestand->ergebnis_detailliert;
}
}
if ($zwischenergebnis && $begegnung->verlegen_aktionen != $anzahl_historie && isset($spiele_nummern[$spiel_nr])) {
$spiel = $spiele_nummern[$spiel_nr];
if ($spiel_heim_punkte == 0 && $spiel_gast_punkte == 0) {
@@ -5787,6 +5787,156 @@ function updateDatabase(): void
}
}
if ($datenbank_version < 122) {
// SIEGBEDINGUNGEN vollstaendig abbilden (Issue #304).
//
// Datenbankversion 122, nicht 121: 121 gehoert dem Hall-of-Fame-Zweig (PR #303,
// Issue #300), der die Spalten teamspieler und nicht_ausgespielt bringt. Dieser
// Block muss der LETZTE der Kette bleiben. Die Reihenfolge ist nicht bloss Kosmetik:
// $datenbank_version wird einmal am Anfang gelesen, also laufen beide Bloecke im
// selben Aufruf — aber jeder schreibt am Ende seine eigene Nummer, und die des
// zuletzt gelaufenen bleibt stehen. Stuende dieser Block vor dem 121er, waere die
// gespeicherte Version danach 121, und beim naechsten Seitenaufruf liefe alles
// erneut.
//
// Bisher sagte ein Mannschaftsspielmodus mit einer einzigen Zahl, wann ein Spiel
// endet: punkte_sieg_einzel/_doppel, deren Vorzeichen zusaetzlich ein
// Unentschieden zulaesst. Verbreitete Modi sind damit nicht darstellbar — „6 Tore,
// aber zwei Vorsprung, spaetestens bei 8" —, und wer nach Saetzen spielt, kann die
// Regel des einzelnen Satzes nirgends hinterlegen. Diese Ueberfuehrung ergaenzt
// deshalb drei Ebenen derselben Regel, die Satzzahl, die Angabe ob Saetze einzeln
// eingetragen werden, und eine Zaehlweise.
//
// Alle Vorgabewerte beschreiben GENAU das heutige Verhalten, und keine bestehende
// Spalte wird angetastet. Eine aeltere Programmfassung liest die neuen Spalten
// nicht und rechnet unveraendert weiter; ein Client, der nur die ueberlieferten
// Felder kennt, findet dort dieselbe Auskunft wie vorher.
//
// SPIELEBENE — punkte_vorsprung_* (wie viele klar voraus, 1 = ein Tor genuegt, also
// das heutige Verhalten) und punkte_deckel_* (Wert, bei dem das Spiel unabhaengig
// vom Vorsprung endet, 0 = keine Grenze).
//
// SATZEBENE — satz_sieg_* (0 = nicht hinterlegt, also der heutige Zustand; sonst
// dasselbe Vorzeichen wie punkte_sieg_*: Betrag = Siegwert, negativ erlaubt ein
// Remis ein Tor darunter), satz_vorsprung_*, satz_deckel_*. Die Regel INNERHALB
// eines Satzes hatte bisher keinen Platz: punkte_sieg_* misst dort, je nach
// punktetyp, entweder Tore („Einer") oder gewonnene Saetze („Mehrere").
//
// SATZZAHL — saetze_modus_*: 0 = wie bisher (allein punkte_sieg_* zaehlt Saetze,
// keine feste Zahl, kein Entscheidungssatz), N > 0 = feste Satzzahl N (alle werden
// gespielt), -N = N Gewinnsaetze (hoechstens 2N-1 Saetze, der letzte ist der
// Entscheidungssatz). Der Editor schreibt die Satzzahl ZUSAETZLICH nach
// punkte_sieg_* zurueck, damit eine aeltere Programmfassung dieselbe — wahre —
// Aussage liest wie bisher: N Gewinnsaetze als N, eine feste Satzzahl als den
// Stand, bei dem sie entschieden ist (4 feste Saetze = „gewonnen bei 3,
// Unentschieden bei 2:2" = -3; 3 feste Saetze = „gewonnen bei 2" = 2). Diese
// Spiegelung ist die Umkehrung der Ueberfuehrung unten und damit verlustfrei.
//
// ENTSCHEIDUNGSSATZ — entscheidung_*: abweichende Regel fuer den Satz, der bei N
// Gewinnsaetzen nach N-1 zu N-1 noch aussteht; sonst gilt satz_*.
// entscheidung_sieg_* = 0 heisst „keine Abweichung" und ist der Bestandswert.
//
// SATZERGEBNISSE — satzergebnisse: werden die Saetze eines Spiels mit ihrem
// jeweiligen Ergebnis eingetragen? Eine EIGENE Spalte und ausdruecklich kein
// dritter Wert in punktetyp: punktetyp ist das einzige Feld, aus dem eine fremde
// Anwendung ablesen muss, ob die Punkte eines Spiels Tore oder gewonnene Saetze
// sind, und es kennt seit jeher genau zwei Werte. Ein dritter haette jede Anwendung
// getroffen, die „1 = Saetze, sonst Tore" schreibt statt „0 = Tore, sonst Saetze" —
// beide Fassungen stehen im Sports Manager selbst, und mindestens ein Client
// (Kickern) ist von hier aus nicht mitzuziehen. So behaelt punktetyp seine
// Bedeutung: die Punkte SIND weiter die gewonnenen Saetze, die feinere Erfassung
// aendert daran nichts, und wer die neue Spalte nicht liest, verpasst das
// Zusaetzliche statt Falsches zu behaupten.
//
// ZAEHLWEISE — zaehlweise/race_*: im Race-Modus laeuft der Stand durch die ganze
// Begegnung, Spiel N endet also, wenn eine Seite N mal race_schritt erreicht.
// Eingetragen wird wie bisher die Differenz des einzelnen Spiels. race_vorsprung,
// race_deckel und race_remis gehoeren zum ZIEL, gelten also nur fuer das letzte
// Spiel der Folge. zaehlweise = 0 ist jeder Wettbewerb, der nichts anderes sagt.
$columns = $db->getTableColumns('#__sportsmanager_teamspiel_modus');
if (!array_key_exists('satzergebnisse', $columns)) {
$query = "ALTER TABLE `#__sportsmanager_teamspiel_modus`"
. "\n ADD `satzergebnisse` TINYINT(4) NOT NULL DEFAULT '0' AFTER `punktetyp`,"
. "\n ADD `punkte_vorsprung_einzel` TINYINT(4) NOT NULL DEFAULT '1' AFTER `punkte_sieg_doppel`,"
. "\n ADD `punkte_vorsprung_doppel` TINYINT(4) NOT NULL DEFAULT '1' AFTER `punkte_vorsprung_einzel`,"
. "\n ADD `punkte_deckel_einzel` TINYINT(4) NOT NULL DEFAULT '0' AFTER `punkte_vorsprung_doppel`,"
. "\n ADD `punkte_deckel_doppel` TINYINT(4) NOT NULL DEFAULT '0' AFTER `punkte_deckel_einzel`,"
. "\n ADD `satz_sieg_einzel` TINYINT(4) NOT NULL DEFAULT '0' AFTER `punkte_deckel_doppel`,"
. "\n ADD `satz_sieg_doppel` TINYINT(4) NOT NULL DEFAULT '0' AFTER `satz_sieg_einzel`,"
. "\n ADD `satz_vorsprung_einzel` TINYINT(4) NOT NULL DEFAULT '1' AFTER `satz_sieg_doppel`,"
. "\n ADD `satz_vorsprung_doppel` TINYINT(4) NOT NULL DEFAULT '1' AFTER `satz_vorsprung_einzel`,"
. "\n ADD `satz_deckel_einzel` TINYINT(4) NOT NULL DEFAULT '0' AFTER `satz_vorsprung_doppel`,"
. "\n ADD `satz_deckel_doppel` TINYINT(4) NOT NULL DEFAULT '0' AFTER `satz_deckel_einzel`,"
. "\n ADD `saetze_modus_einzel` TINYINT(4) NOT NULL DEFAULT '0' AFTER `satz_deckel_doppel`,"
. "\n ADD `saetze_modus_doppel` TINYINT(4) NOT NULL DEFAULT '0' AFTER `saetze_modus_einzel`,"
. "\n ADD `entscheidung_sieg_einzel` TINYINT(4) NOT NULL DEFAULT '0' AFTER `saetze_modus_doppel`,"
. "\n ADD `entscheidung_sieg_doppel` TINYINT(4) NOT NULL DEFAULT '0' AFTER `entscheidung_sieg_einzel`,"
. "\n ADD `entscheidung_vorsprung_einzel` TINYINT(4) NOT NULL DEFAULT '1' AFTER `entscheidung_sieg_doppel`,"
. "\n ADD `entscheidung_vorsprung_doppel` TINYINT(4) NOT NULL DEFAULT '1' AFTER `entscheidung_vorsprung_einzel`,"
. "\n ADD `entscheidung_deckel_einzel` TINYINT(4) NOT NULL DEFAULT '0' AFTER `entscheidung_vorsprung_doppel`,"
. "\n ADD `entscheidung_deckel_doppel` TINYINT(4) NOT NULL DEFAULT '0' AFTER `entscheidung_deckel_einzel`,"
. "\n ADD `zaehlweise` TINYINT(4) NOT NULL DEFAULT '0' AFTER `entscheidung_deckel_doppel`,"
. "\n ADD `race_schritt` TINYINT(4) NOT NULL DEFAULT '0' AFTER `zaehlweise`,"
. "\n ADD `race_vorsprung` TINYINT(4) NOT NULL DEFAULT '1' AFTER `race_schritt`,"
. "\n ADD `race_deckel` TINYINT(4) NOT NULL DEFAULT '0' AFTER `race_vorsprung`,"
. "\n ADD `race_remis` TINYINT(4) NOT NULL DEFAULT '0' AFTER `race_deckel`;";
$db->setQuery($query);
if (!$db->execute()) {
die($db->stderr(true));
}
}
// Bestehende „Mehrere"-Modi in die Satzzahl ueberfuehren.
//
// * punkte_sieg_* = 1 → ein Satz, und der wird gespielt.
// * punkte_sieg_* = N (ab 2) → N GEWINNSAETZE. Von den beiden Lesarten, die diese
// Zahl zulaesst (best of 2N-1 oder feste 2N-1 Saetze), ist das die weitaus
// haeufigere; wessen Wettbewerb alle Saetze ausspielt, stellt eine Auswahl um.
// * punkte_sieg_* = -N → FESTE Satzzahl 2(N-1). Das Vorzeichen bedeutet „Remis bei
// N-1 zu N-1 moeglich", und ein Remis setzt voraus, dass alle Saetze gespielt
// werden: bei „Sieg 3, Remis 2:2" sind es vier.
// * 0 bleibt 0 und heisst „Beliebig": da war nie eine Regel.
//
// Auch ungewoehnliche Zahlen werden ueberfuehrt, nicht liegengelassen (Sven
// 2026-09-02). In „Mehrere" MISST punkte_sieg_* gewonnene Saetze — das ist die
// Bedeutung des Feldes, auch wenn ein Wettbewerb sie fuer Tore benutzt hat. Der
// Editor bietet solche Werte nicht zur Wahl an, zeigt den vorhandenen aber als
// eigenen Eintrag: sichtbar, unveraendert speicherbar, und einmal weggestellt
// nicht mehr einstellbar.
//
// Der Race-Modus wird NICHT automatisch erkannt; er ist aus Bestandsdaten nicht
// sicher abzuleiten und wird einmal von Hand gewaehlt.
//
// Nur dort, wo noch nichts gesetzt ist — ein zweiter Lauf aendert nichts.
$query = "UPDATE #__sportsmanager_teamspiel_modus"
. "\n SET saetze_modus_einzel = CASE"
. "\n WHEN punktetyp = 0 THEN 0"
. "\n WHEN punkte_sieg_einzel = 1 THEN 1"
. "\n WHEN punkte_sieg_einzel >= 2 THEN -punkte_sieg_einzel"
. "\n WHEN punkte_sieg_einzel <= -2 THEN 2 * (-punkte_sieg_einzel - 1)"
. "\n ELSE 0 END,"
. "\n saetze_modus_doppel = CASE"
. "\n WHEN punktetyp = 0 THEN 0"
. "\n WHEN punkte_sieg_doppel = 1 THEN 1"
. "\n WHEN punkte_sieg_doppel >= 2 THEN -punkte_sieg_doppel"
. "\n WHEN punkte_sieg_doppel <= -2 THEN 2 * (-punkte_sieg_doppel - 1)"
. "\n ELSE 0 END"
. "\n WHERE saetze_modus_einzel = 0 AND saetze_modus_doppel = 0";
$db->setQuery($query);
if (!$db->execute()) {
die($db->stderr(true));
}
$query = "UPDATE #__sportsmanager_einstellungen"
. "\n SET wert = '122'"
. "\n WHERE name = 'datenbank_version'";
$db->setQuery($query);
if (!$db->execute()) {
die($db->stderr(true));
}
}
if ($termin_aktionen_email_setzen) {
$query = "SELECT aktion_user_id, termin_aktion_id"
. "\n FROM #__sportsmanager_termin_aktion";
File diff suppressed because it is too large Load Diff
@@ -158,7 +158,7 @@ COM_SPORTSMANAGER_DEFEATS="Niederlagen"
COM_SPORTSMANAGER_DEFEATS_SHORTCUT="N"
COM_SPORTSMANAGER_GOALS="Tore"
COM_SPORTSMANAGER_GOALS_SHORTCUT="T"
COM_SPORTSMANAGER_SETS="S&auml;tze"
COM_SPORTSMANAGER_SETS="Sätze"
COM_SPORTSMANAGER_POINT="Punkt"
COM_SPORTSMANAGER_POINTS="Punkte"
COM_SPORTSMANAGER_POINTS_SHORTCUT="P"
@@ -490,7 +490,7 @@ COM_SPORTSMANAGER_GAME_DOUBLES2="Doppel-Spiel"
COM_SPORTSMANAGER_GAME_DOUBLES_IDENTICAL="Doppel-Spiel identisch"
COM_SPORTSMANAGER_GAME_DOUBLES_SEPARATELY="Doppel-Spiel separat"
COM_SPORTSMANAGER_ONE="Einer"
COM_SPORTSMANAGER_MULTIPLE="Mehrere"
COM_SPORTSMANAGER_MULTIPLE="Mehrere nur der Satzstand"
COM_SPORTSMANAGER_HOME_EXCHANGE="Heimtausch"
COM_SPORTSMANAGER_NOT_ALLOWED="Nicht zulässig"
COM_SPORTSMANAGER_ALLOWED="Zulässig"
@@ -1124,4 +1124,52 @@ COM_SPORTSMANAGER_MATCH_REPORT_CORRECTED="Spielberichtskorrektur"
COM_SPORTSMANAGER_MIN_MATCHES="Mindestzahl Spiele"
COM_SPORTSMANAGER_SELECT_ALL="Alle"
COM_SPORTSMANAGER_MAIL_SEND_ERROR="Die Benachrichtigungs-E-Mail zum Ergebnis konnte nicht versendet werden."
COM_SPORTSMANAGER_TERMIN_MAIL_SEND_ERROR="Mindestens eine Benachrichtigungs-E-Mail zum Termin konnte nicht versendet werden."
COM_SPORTSMANAGER_TERMIN_MAIL_SEND_ERROR="Mindestens eine Benachrichtigungs-E-Mail zum Termin konnte nicht versendet werden."
COM_SPORTSMANAGER_GAME_MARGIN="Mindestvorsprung"
COM_SPORTSMANAGER_GAME_CAP="Spätestens gewonnen bei"
COM_SPORTSMANAGER_GAME_CAP_NONE="Keine Begrenzung"
COM_SPORTSMANAGER_NO_MARGIN="Keiner"
COM_SPORTSMANAGER_ONE_GOAL="1 Tor"
COM_SPORTSMANAGER_NUMBER_GOALS="%d Tore"
COM_SPORTSMANAGER_MULTIPLE_WITH_SETS="Mehrere mit Ergebnis je Satz"
COM_SPORTSMANAGER_SET_NUMBER="Satz %d"
COM_SPORTSMANAGER_SET_COUNT="Sätze je Spiel"
COM_SPORTSMANAGER_GROUP_GENERAL="Allgemein"
COM_SPORTSMANAGER_GROUP_GAME="Spiel"
COM_SPORTSMANAGER_GROUP_SET="Satz"
COM_SPORTSMANAGER_GROUP_MATCH="Begegnung"
COM_SPORTSMANAGER_GROUP_PLAYERS="Spieler und Statistik"
COM_SPORTSMANAGER_DOUBLES_SETTINGS="Doppel-Spiele"
COM_SPORTSMANAGER_ONE_FIXED_SET="1 Satz (wird gespielt)"
COM_SPORTSMANAGER_NUMBER_FIXED_SETS="%d Sätze (alle werden gespielt)"
COM_SPORTSMANAGER_NUMBER_WINNING_SETS="%d Gewinnsätze"
COM_SPORTSMANAGER_DECIDING_SET="Entscheidungssatz"
COM_SPORTSMANAGER_DECIDING_SET_SAME="Wie die übrigen Sätze"
COM_SPORTSMANAGER_SETS_PER_GAME_HELP="Mit „Ergebnis je Satz" werden die Tore der einzelnen Sätze mit erfasst und stehen im Spielbericht; ohne bleibt nur der Satzstand."
COM_SPORTSMANAGER_SET_COUNT_HELP="Die Angaben beschreiben den Wettbewerb; sie lehnen keine Eingabe ab. Auswertungen und Apps erkennen daran, ob ein Spiel noch läuft oder schon entschieden ist."
COM_SPORTSMANAGER_MODE_GLOSSARY="Eine <strong>Begegnung</strong> ist die Partie zweier Mannschaften. Sie besteht aus mehreren <strong>Spielen</strong>: Einzel und Doppel, in der Reihenfolge der Spielfolge. Jedes Spiel bringt Spielpunkte für die Begegnung. Ein <strong>Spiel</strong> besteht aus einem Satz („Einer") oder aus mehreren. In einem <strong>Satz</strong> fallen <strong>Tore</strong>. Die Einstellungen unten sind in dieser Reihenfolge geordnet: von der Begegnung über das Spiel zum Satz."
COM_SPORTSMANAGER_RESULT_AS_GAME_POINTS="Ohne Umrechnung Tore bzw. Sätze sind die Spielpunkte"
COM_SPORTSMANAGER_COUNTING="Zählweise"
COM_SPORTSMANAGER_COUNTING_PER_GAME="Je Spiel jedes Spiel beginnt bei 0:0"
COM_SPORTSMANAGER_COUNTING_RACE="Race-Modus die Tore addieren sich über alle Spiele"
COM_SPORTSMANAGER_RACE_STEP="Schritt je Spiel"
COM_SPORTSMANAGER_RACE_TARGET="Ziel"
COM_SPORTSMANAGER_RACE_TARGET_FORMULA="Anzahl der Spiele aus der Spielfolge × Schritt je Spiel"
COM_SPORTSMANAGER_RACE_TARGET_CURRENT="derzeit %1$d Spiele × %2$d = %3$d Tore"
COM_SPORTSMANAGER_RACE_MARGIN="Mindestvorsprung am Ziel"
COM_SPORTSMANAGER_RACE_CAP="Spätestens gewonnen bei"
COM_SPORTSMANAGER_RACE_DRAW="Unentschieden am Ziel"
COM_SPORTSMANAGER_RACE_DRAW_NO="Nein"
COM_SPORTSMANAGER_RACE_DRAW_YES="Ja bei einem Tor unter dem Ziel"
COM_SPORTSMANAGER_RACE_HELP="Der Stand läuft über die ganze Begegnung durch: Spiel 1 endet beim Schritt, Spiel 2 beim doppelten Schritt, und so fort bis zum Ziel. Mindestvorsprung und Grenze gelten nur für das letzte Spiel. Der Race-Modus setzt „Spielergebnis als Spielpunkte" und einen Satz je Spiel voraus beides wird mitgesetzt."
COM_SPORTSMANAGER_ADD_GAME="Spiel hinzufügen"
COM_SPORTSMANAGER_ADD_LINK="Verknüpfung hinzufügen"
COM_SPORTSMANAGER_REMOVE_GAME="Dieses Spiel entfernen"
COM_SPORTSMANAGER_REMOVE_LINK="Diese Verknüpfung entfernen"
COM_SPORTSMANAGER_LINK_NUMBER="Verknüpfung Nr. %d"
COM_SPORTSMANAGER_GROUP_SEQUENCE="Spielfolge"
COM_SPORTSMANAGER_GAMES_IN_STATISTIK_FIRST_ONE="nur das erste Spiel"
COM_SPORTSMANAGER_GAMES_IN_STATISTIK_FIRST="nur die ersten %d Spiele"
COM_SPORTSMANAGER_POINT_TYPE_CHANGED_WARNING="Für diesen Mannschaftsspielplan sind bereits Ergebnisse eingetragen. Die gespeicherten Zahlen werden NICHT umgerechnet sie bedeuten danach etwas anderes (Tore statt Sätze bzw. umgekehrt). Trotzdem speichern?"
COM_SPORTSMANAGER_RACE_RUNNING="Stand"
COM_SPORTSMANAGER_TOTAL_SCORE="Gesamtstand"
@@ -490,7 +490,7 @@ COM_SPORTSMANAGER_GAME_DOUBLES2="Double games"
COM_SPORTSMANAGER_GAME_DOUBLES_IDENTICAL="Doubles game identical"
COM_SPORTSMANAGER_GAME_DOUBLES_SEPARATELY="Doubles game seperately"
COM_SPORTSMANAGER_ONE="One"
COM_SPORTSMANAGER_MULTIPLE="Multiple"
COM_SPORTSMANAGER_MULTIPLE="Multiple set score only"
COM_SPORTSMANAGER_HOME_EXCHANGE="Home exchange"
COM_SPORTSMANAGER_NOT_ALLOWED="Not allowed"
COM_SPORTSMANAGER_ALLOWED="Allowed"
@@ -1124,4 +1124,52 @@ COM_SPORTSMANAGER_MATCH_REPORT_CORRECTED="Match report corrected"
COM_SPORTSMANAGER_MIN_MATCHES="Min count matches"
COM_SPORTSMANAGER_SELECT_ALL="All"
COM_SPORTSMANAGER_MAIL_SEND_ERROR="The notification e-mail for the result could not be sent."
COM_SPORTSMANAGER_TERMIN_MAIL_SEND_ERROR="At least one notification e-mail for the appointment could not be sent."
COM_SPORTSMANAGER_TERMIN_MAIL_SEND_ERROR="At least one notification e-mail for the appointment could not be sent."
COM_SPORTSMANAGER_GAME_MARGIN="Minimum margin"
COM_SPORTSMANAGER_GAME_CAP="Won at the latest at"
COM_SPORTSMANAGER_GAME_CAP_NONE="No limit"
COM_SPORTSMANAGER_NO_MARGIN="None"
COM_SPORTSMANAGER_ONE_GOAL="1 goal"
COM_SPORTSMANAGER_NUMBER_GOALS="%d goals"
COM_SPORTSMANAGER_MULTIPLE_WITH_SETS="Multiple with each set's result"
COM_SPORTSMANAGER_SET_NUMBER="Set %d"
COM_SPORTSMANAGER_SET_COUNT="Sets per game"
COM_SPORTSMANAGER_GROUP_GENERAL="General"
COM_SPORTSMANAGER_GROUP_GAME="Game"
COM_SPORTSMANAGER_GROUP_SET="Set"
COM_SPORTSMANAGER_GROUP_MATCH="Match"
COM_SPORTSMANAGER_GROUP_PLAYERS="Players and statistics"
COM_SPORTSMANAGER_DOUBLES_SETTINGS="Doubles games"
COM_SPORTSMANAGER_ONE_FIXED_SET="1 set (played out)"
COM_SPORTSMANAGER_NUMBER_FIXED_SETS="%d sets (all played out)"
COM_SPORTSMANAGER_NUMBER_WINNING_SETS="Best of %d wins"
COM_SPORTSMANAGER_DECIDING_SET="Deciding set"
COM_SPORTSMANAGER_DECIDING_SET_SAME="As the other sets"
COM_SPORTSMANAGER_SETS_PER_GAME_HELP="With “each sets result” the goals of the individual sets are recorded too and appear in the match report; without it only the set score remains."
COM_SPORTSMANAGER_SET_COUNT_HELP="These settings describe the competition; they never reject an entry. Evaluations and apps use them to tell a game still running from one already decided."
COM_SPORTSMANAGER_MODE_GLOSSARY="A <strong>match</strong> is the fixture between two teams. It consists of several <strong>games</strong> singles and doubles in the order of the game sequence and each game yields game points for the match. A <strong>game</strong> consists of one set (“One”) or of several. Within a <strong>set</strong>, <strong>goals</strong> are scored. The settings below follow that order: from the match through the game down to the set."
COM_SPORTSMANAGER_RESULT_AS_GAME_POINTS="Without conversion goals or sets are the game points"
COM_SPORTSMANAGER_COUNTING="Counting"
COM_SPORTSMANAGER_COUNTING_PER_GAME="Per game every game starts at 0:0"
COM_SPORTSMANAGER_COUNTING_RACE="Race goals add up across all games"
COM_SPORTSMANAGER_RACE_STEP="Step per game"
COM_SPORTSMANAGER_RACE_TARGET="Target"
COM_SPORTSMANAGER_RACE_TARGET_FORMULA="Number of games in the sequence × step per game"
COM_SPORTSMANAGER_RACE_TARGET_CURRENT="currently %1$d games × %2$d = %3$d goals"
COM_SPORTSMANAGER_RACE_MARGIN="Minimum margin at the target"
COM_SPORTSMANAGER_RACE_CAP="Won at the latest at"
COM_SPORTSMANAGER_RACE_DRAW="Draw at the target"
COM_SPORTSMANAGER_RACE_DRAW_NO="No"
COM_SPORTSMANAGER_RACE_DRAW_YES="Yes one goal below the target"
COM_SPORTSMANAGER_RACE_HELP="The score runs through the whole match: game 1 ends at the step, game 2 at twice the step, and so on up to the target. Minimum margin and limit apply to the last game only. Race requires “game result as game points” and one set per game both are set along with it."
COM_SPORTSMANAGER_ADD_GAME="Add a game"
COM_SPORTSMANAGER_ADD_LINK="Add a link"
COM_SPORTSMANAGER_REMOVE_GAME="Remove this game"
COM_SPORTSMANAGER_REMOVE_LINK="Remove this link"
COM_SPORTSMANAGER_LINK_NUMBER="Link no. %d"
COM_SPORTSMANAGER_GROUP_SEQUENCE="Game sequence"
COM_SPORTSMANAGER_GAMES_IN_STATISTIK_FIRST_ONE="the first game only"
COM_SPORTSMANAGER_GAMES_IN_STATISTIK_FIRST="the first %d games only"
COM_SPORTSMANAGER_POINT_TYPE_CHANGED_WARNING="Results have already been entered for this team schedule. The stored numbers are NOT converted afterwards they mean something else (goals instead of sets, or the other way round). Save anyway?"
COM_SPORTSMANAGER_RACE_RUNNING="Score"
COM_SPORTSMANAGER_TOTAL_SCORE="Total score"
+25 -1
View File
@@ -790,8 +790,32 @@ return new class () implements InstallerScriptInterface
. "\n `teamspiel_modus_id` int(11) NOT NULL AUTO_INCREMENT,"
. "\n `bezeichnung` varchar(50) NOT NULL DEFAULT '',"
. "\n `punktetyp` tinyint(4) NOT NULL DEFAULT '0',"
. "\n `satzergebnisse` tinyint(4) NOT NULL DEFAULT '0',"
. "\n `punkte_sieg_einzel` tinyint(4) NOT NULL DEFAULT '0',"
. "\n `punkte_sieg_doppel` tinyint(4) NOT NULL DEFAULT '0',"
. "\n `punkte_vorsprung_einzel` tinyint(4) NOT NULL DEFAULT '1',"
. "\n `punkte_vorsprung_doppel` tinyint(4) NOT NULL DEFAULT '1',"
. "\n `punkte_deckel_einzel` tinyint(4) NOT NULL DEFAULT '0',"
. "\n `punkte_deckel_doppel` tinyint(4) NOT NULL DEFAULT '0',"
. "\n `satz_sieg_einzel` tinyint(4) NOT NULL DEFAULT '0',"
. "\n `satz_sieg_doppel` tinyint(4) NOT NULL DEFAULT '0',"
. "\n `satz_vorsprung_einzel` tinyint(4) NOT NULL DEFAULT '1',"
. "\n `satz_vorsprung_doppel` tinyint(4) NOT NULL DEFAULT '1',"
. "\n `satz_deckel_einzel` tinyint(4) NOT NULL DEFAULT '0',"
. "\n `satz_deckel_doppel` tinyint(4) NOT NULL DEFAULT '0',"
. "\n `saetze_modus_einzel` tinyint(4) NOT NULL DEFAULT '0',"
. "\n `saetze_modus_doppel` tinyint(4) NOT NULL DEFAULT '0',"
. "\n `entscheidung_sieg_einzel` tinyint(4) NOT NULL DEFAULT '0',"
. "\n `entscheidung_sieg_doppel` tinyint(4) NOT NULL DEFAULT '0',"
. "\n `entscheidung_vorsprung_einzel` tinyint(4) NOT NULL DEFAULT '1',"
. "\n `entscheidung_vorsprung_doppel` tinyint(4) NOT NULL DEFAULT '1',"
. "\n `entscheidung_deckel_einzel` tinyint(4) NOT NULL DEFAULT '0',"
. "\n `entscheidung_deckel_doppel` tinyint(4) NOT NULL DEFAULT '0',"
. "\n `zaehlweise` tinyint(4) NOT NULL DEFAULT '0',"
. "\n `race_schritt` tinyint(4) NOT NULL DEFAULT '0',"
. "\n `race_vorsprung` tinyint(4) NOT NULL DEFAULT '1',"
. "\n `race_deckel` tinyint(4) NOT NULL DEFAULT '0',"
. "\n `race_remis` tinyint(4) NOT NULL DEFAULT '0',"
. "\n `spielpunkte_wertung_einzel` tinyint(4) NOT NULL DEFAULT '0',"
. "\n `spielpunkte_wertung_doppel` tinyint(4) NOT NULL DEFAULT '0',"
. "\n `spielpunkte_bedingung` tinyint(4) NOT NULL DEFAULT '0',"
@@ -1283,7 +1307,7 @@ return new class () implements InstallerScriptInterface
$db->setQuery( $query );
if (!$db->execute()) { die($db->stderr(true)); }
$query = "INSERT IGNORE #__sportsmanager_einstellungen SET name = 'datenbank_version', wert = '121';";
$query = "INSERT IGNORE #__sportsmanager_einstellungen SET name = 'datenbank_version', wert = '122';";
$db->setQuery( $query );
if (!$db->execute()) { die($db->stderr(true)); }