slop game oder so
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
/**
|
||||
* 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 };
|
||||
}
|
||||
Reference in New Issue
Block a user