// Hinweise für den Ingame-Chat (RCON "say"). Absender ist im Spiel "Console". // Reine Textbausteine ohne Server-Zugriff, damit sie sich mit Beispielen testen lassen. const MAX_LENGTH = 120; /** * Text für "say", ohne Zeichen, mit denen sich weitere Konsolenbefehle anhängen ließen * (Anführungszeichen, Semikolon, Zeilenumbrüche). Doppelpunkt und Komma bleiben für Spielstände erlaubt. */ export function chatText(text) { return String(text ?? '') .replace(/["';\\\r\n\t]/g, '') .replace(/[^\p{L}\p{N} .,:_\-+#!?()[\]|/&]/gu, '') .replace(/\s+/g, ' ') .trim() .slice(0, MAX_LENGTH); } /** Konsolenbefehle für eine Liste von Hinweisen. Leere Zeilen fallen weg. */ export function sayCommands(lines) { return lines.map(chatText).filter(Boolean).map((line) => `say ${line}`); } const other = (team) => (team === 'A' ? 'B' : 'A'); /** * Hinweise nach dem Laden einer Map: welche Map, wer sie gewählt hat, wer welche Seite startet, * Serienstand und dass die Aufwärmphase bis zur Freigabe läuft. * @param {object} p * @param {{A: string, B: string}} p.teams Teamnamen * @param {{number: number, mapName: string, pickedBy: 'A'|'B'|null, sideChosenBy: 'A'|'B'|null, ctTeam: 'A'|'B'|null}} p.map * @param {number} p.total Maps in der Serie * @param {{A: number, B: number}} p.series Serienstand vor dieser Map */ export function mapLoadedLines({ teams, map, total, series }) { const lines = []; const head = total > 1 ? `Map ${map.number} von ${total}: ${map.mapName}` : `Map: ${map.mapName}`; lines.push(map.pickedBy ? `${head} - gewählt von ${teams[map.pickedBy]}` : `${head} - Entscheidungsmap`); if (map.ctTeam) { const t = other(map.ctTeam); const chooser = map.sideChosenBy ? `${teams[map.sideChosenBy]} hat die Seite gewählt: ` : ''; lines.push(`${chooser}${teams[map.ctTeam]} startet als CT, ${teams[t]} als T`); } if (total > 1) lines.push(`Serienstand: ${teams.A} ${series.A}:${series.B} ${teams.B}`); lines.push('Aufwärmphase - das Match startet, sobald der Organisator es freigibt'); return lines; } /** Hinweis beim Start des Matches (Aufwärmphase beendet). */ export function matchStartLines({ teams, ctTeam }) { if (!ctTeam) return [`Match live! ${teams.A} gegen ${teams.B} - viel Glück, have fun`]; return [`Match live! ${teams[ctTeam]} (CT) gegen ${teams[other(ctTeam)]} (T) - viel Glück, have fun`]; } /** * Hinweise am Ende einer Map: Sieger mit Stand, Serienstand und was als Nächstes passiert. * @param {object} p * @param {{mapName: string, number: number, winner: 'A'|'B'|null, score: {A: number, B: number}}} p.map * @param {{A: number, B: number}} p.series Serienstand nach dieser Map * @param {'A'|'B'|null} p.decided Sieger der Serie, wenn entschieden * @param {string|null} p.nextMapName nächste Map, wenn es eine gibt * @param {number} p.autoAdvanceSeconds Wartezeit bis zum automatischen Laden (< 0 = aus) * @param {number} p.total Maps in der Serie */ export function mapEndLines({ teams, map, series, decided, nextMapName, autoAdvanceSeconds, total }) { const lines = []; const score = `${map.score.A}:${map.score.B}`; lines.push(map.winner ? `Map ${map.number} (${map.mapName}) geht an ${teams[map.winner]} mit ${score}` : `Map ${map.number} (${map.mapName}) endet ${score}`); if (decided) { const s = decided === 'A' ? `${series.A}:${series.B}` : `${series.B}:${series.A}`; lines.push(total > 1 ? `${teams[decided]} gewinnt die Serie ${s} - danke fürs Spielen!` : 'Danke fürs Spielen!'); } else if (nextMapName) { lines.push(`Serienstand: ${teams.A} ${series.A}:${series.B} ${teams.B}`); lines.push(autoAdvanceSeconds >= 0 ? `Nächste Map: ${nextMapName}, wird in ${autoAdvanceSeconds} s geladen - bleibt auf dem Server` : `Nächste Map: ${nextMapName} - bleibt auf dem Server`); } return lines; }