Files
slop-game-opus5.5/app/static/js/logic/format.js
T
2026-09-24 17:57:54 +02:00

95 lines
2.7 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Formatierung und Eingabeprüfung für Anzeige und Highscore-Formular (ohne Phaser).
* @module logic/format
*/
import { DIRECTION_SYMBOLS, HIGHSCORE } from '../config.js';
const scoreFormatter = new Intl.NumberFormat('de-DE');
/**
* Punktestand mit Tausendertrennzeichen („12.345“).
* @param {number} score
* @returns {string}
*/
export function formatScore(score) {
return scoreFormatter.format(Math.max(0, Math.floor(score)));
}
/**
* Dauer als „m:ss“.
* @param {number} ms
* @returns {string}
*/
export function formatDuration(ms) {
const totalSeconds = Math.max(0, Math.floor(ms / 1000));
const minutes = Math.floor(totalSeconds / 60);
const seconds = String(totalSeconds % 60).padStart(2, '0');
return `${minutes}:${seconds}`;
}
/**
* Multiplikator mit deutschem Dezimalkomma („×1,5“).
* @param {number} multiplier
* @returns {string}
*/
export function formatMultiplier(multiplier) {
return `×${String(Math.round(multiplier * 10) / 10).replace('.', ',')}`;
}
/**
* Abklingzeit in Sekunden für das HUD („12“, „0,8“).
* @param {number} ms
* @returns {string}
*/
export function formatCooldown(ms) {
if (ms <= 0) return '';
const seconds = ms / 1000;
return seconds >= 1 ? String(Math.ceil(seconds)) : seconds.toFixed(1).replace('.', ',');
}
/**
* Richtungsfolge als Pfeile („↑ → ↓ ↓“).
* @param {readonly string[]} sequence
* @param {string} [separator]
* @returns {string}
*/
export function formatSequence(sequence, separator = ' ') {
return sequence.map((dir) => DIRECTION_SYMBOLS[dir] ?? '?').join(separator);
}
/** Wie auf dem Server (app/validation.py): alles außer Buchstaben, Ziffern und wenigen Zeichen. */
const DISALLOWED_NAME_CHARS = /[^\p{L}\p{N}_ .\-'!?]/gu;
/** Steuer-, Format-, Surrogat- und private Zeichen (Unicode-Kategorie C). */
const CONTROL_CHARS = /\p{C}/gu;
/**
* Bereinigt einen Namen genauso wie das Backend, damit der Spieler vor dem Absenden
* sieht, was gespeichert wird. Der Server prüft trotzdem selbst.
* @param {string} raw
* @returns {string}
*/
export function sanitizeName(raw) {
return String(raw)
.normalize('NFKC')
.replace(CONTROL_CHARS, '')
.replace(DISALLOWED_NAME_CHARS, '')
.split(/\s+/u)
.filter(Boolean)
.join(' ');
}
/**
* Prüft einen Namen für das Highscore-Formular.
* @param {string} raw
* @returns {{ok: true, name: string} | {ok: false, error: string}}
*/
export function validateName(raw) {
const name = sanitizeName(raw);
if (!name) return { ok: false, error: 'Bitte einen Namen mit Buchstaben oder Ziffern eingeben.' };
if ([...name].length > HIGHSCORE.nameMaxLength) {
return { ok: false, error: `Höchstens ${HIGHSCORE.nameMaxLength} Zeichen.` };
}
return { ok: true, name };
}