slop game oder so
This commit is contained in:
@@ -0,0 +1,127 @@
|
||||
/**
|
||||
* Punkte, Serien und Multiplikator (ohne Phaser).
|
||||
*
|
||||
* Regeln: Jeder Abschuss innerhalb von `streakWindowMs` nach dem letzten setzt die
|
||||
* Serie fort. Alle `stepKills` Abschüsse steigt der Multiplikator um `stepValue`
|
||||
* bis `maxMultiplier`. Ein Treffer am Präsidenten beendet die Serie – das belohnt
|
||||
* sauberes Spielen statt blindem Draufhalten.
|
||||
* @module logic/scoring
|
||||
*/
|
||||
|
||||
import { SCORING } from '../config.js';
|
||||
|
||||
/**
|
||||
* Multiplikator für eine Serienlänge.
|
||||
* @param {number} streak Anzahl Abschüsse in Folge (inkl. des aktuellen).
|
||||
* @param {typeof SCORING} [cfg]
|
||||
* @returns {number}
|
||||
*/
|
||||
export function multiplierForStreak(streak, cfg = SCORING) {
|
||||
if (streak <= 0) return 1;
|
||||
return Math.min(cfg.maxMultiplier, 1 + Math.floor(streak / cfg.stepKills) * cfg.stepValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Bonus für eine überstandene Welle (ohne Multiplikator).
|
||||
* @param {number} waveNumber
|
||||
* @param {typeof SCORING} [cfg]
|
||||
* @returns {number}
|
||||
*/
|
||||
export function waveBonus(waveNumber, cfg = SCORING) {
|
||||
return waveNumber * cfg.waveBonusPerWave;
|
||||
}
|
||||
|
||||
/**
|
||||
* Führt Punktestand, Serie und Statistik einer Spielrunde.
|
||||
*/
|
||||
export class ScoreKeeper {
|
||||
/** @param {typeof SCORING} [cfg] */
|
||||
constructor(cfg = SCORING) {
|
||||
this.cfg = cfg;
|
||||
this.score = 0;
|
||||
this.kills = 0;
|
||||
this.streak = 0;
|
||||
this.bestStreak = 0;
|
||||
this.lastKillAt = -Infinity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Läuft die Serie zum Zeitpunkt `now` noch?
|
||||
* @param {number} now Spielzeit in ms.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
isStreakActive(now) {
|
||||
return this.streak > 0 && now - this.lastKillAt <= this.cfg.streakWindowMs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Aktueller Multiplikator (1, wenn die Serie abgelaufen ist).
|
||||
* @param {number} now
|
||||
* @returns {number}
|
||||
*/
|
||||
multiplierAt(now) {
|
||||
return this.isStreakActive(now) ? multiplierForStreak(this.streak, this.cfg) : 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verbleibender Anteil des Serienfensters (1 = gerade getroffen, 0 = abgelaufen).
|
||||
* @param {number} now
|
||||
* @returns {number}
|
||||
*/
|
||||
streakTimeLeft(now) {
|
||||
if (!this.isStreakActive(now)) return 0;
|
||||
return 1 - (now - this.lastKillAt) / this.cfg.streakWindowMs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verbucht einen Abschuss.
|
||||
* @param {number} basePoints Punkte des Gegnertyps.
|
||||
* @param {number} now Spielzeit in ms.
|
||||
* @returns {{awarded: number, multiplier: number, streak: number}}
|
||||
*/
|
||||
registerKill(basePoints, now) {
|
||||
if (!this.isStreakActive(now)) {
|
||||
this.streak = 0;
|
||||
}
|
||||
this.streak += 1;
|
||||
this.kills += 1;
|
||||
this.bestStreak = Math.max(this.bestStreak, this.streak);
|
||||
this.lastKillAt = now;
|
||||
const multiplier = multiplierForStreak(this.streak, this.cfg);
|
||||
const awarded = Math.round(basePoints * multiplier);
|
||||
this.score += awarded;
|
||||
return { awarded, multiplier, streak: this.streak };
|
||||
}
|
||||
|
||||
/** Beendet die laufende Serie (z. B. wenn der Präsident getroffen wird). */
|
||||
breakStreak() {
|
||||
this.streak = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fügt Bonuspunkte ohne Multiplikator hinzu.
|
||||
* @param {number} points
|
||||
* @returns {number} Tatsächlich gutgeschriebene Punkte.
|
||||
*/
|
||||
addBonus(points) {
|
||||
const awarded = Math.max(0, Math.round(points));
|
||||
this.score += awarded;
|
||||
return awarded;
|
||||
}
|
||||
|
||||
/**
|
||||
* Momentaufnahme für HUD und Game-Over-Bildschirm.
|
||||
* @param {number} now
|
||||
*/
|
||||
snapshot(now) {
|
||||
const active = this.isStreakActive(now);
|
||||
return {
|
||||
score: this.score,
|
||||
kills: this.kills,
|
||||
streak: active ? this.streak : 0,
|
||||
multiplier: this.multiplierAt(now),
|
||||
streakTimeLeft: this.streakTimeLeft(now),
|
||||
bestStreak: this.bestStreak,
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user