first commmit
This commit is contained in:
@@ -0,0 +1,105 @@
|
||||
import { WeaponType } from '../data/Interfaces';
|
||||
|
||||
/**
|
||||
* Score system (task 9.3, req 12.1-12.8).
|
||||
*
|
||||
* Scoring table:
|
||||
* - Ninja sword kill ×2.0 base
|
||||
* - Shuriken kill ×1.0 base
|
||||
* - Perfect parry counterkill ×3.0 base
|
||||
* - 5-combo "刃接触" bonus +1500
|
||||
* - Flawless level (no damage) ×3.0 total
|
||||
* - Remaining time bonus +10 pts / remaining sec
|
||||
*
|
||||
* Everything is deterministic and `Math.random`-free so QA can reproduce
|
||||
* every score calculation in unit tests.
|
||||
*/
|
||||
|
||||
export const BASE_ENEMY_SCORE = 100;
|
||||
export const COMBO_BONUS = 1500;
|
||||
export const COMBO_THRESHOLD = 5;
|
||||
export const TIME_BONUS_PER_SEC = 10;
|
||||
|
||||
export interface IScoreSnapshot {
|
||||
baseScore: number;
|
||||
comboBonus: number;
|
||||
timeBonus: number;
|
||||
flawlessMultiplier: number;
|
||||
finalScore: number;
|
||||
killCount: number;
|
||||
comboCount: number;
|
||||
consecutiveBladeHits: number;
|
||||
}
|
||||
|
||||
export class ScoreSystem {
|
||||
private baseScore = 0;
|
||||
private comboBonus = 0;
|
||||
private killCount = 0;
|
||||
private consecutiveBladeHits = 0;
|
||||
private comboCount = 0;
|
||||
private flawless = true;
|
||||
private timeBonus = 0;
|
||||
|
||||
public reset(): void {
|
||||
this.baseScore = 0;
|
||||
this.comboBonus = 0;
|
||||
this.killCount = 0;
|
||||
this.consecutiveBladeHits = 0;
|
||||
this.comboCount = 0;
|
||||
this.flawless = true;
|
||||
this.timeBonus = 0;
|
||||
}
|
||||
|
||||
/** Record a kill weighted by weapon type (req 12.1-12.2). */
|
||||
public recordEnemyKill(weapon: WeaponType): void {
|
||||
this.killCount++;
|
||||
const multiplier = weapon === WeaponType.NinjaSword ? 2 : 1;
|
||||
this.baseScore += BASE_ENEMY_SCORE * multiplier;
|
||||
}
|
||||
|
||||
/** Perfect parry followed by a counter-kill (req 12.3). */
|
||||
public recordParryKill(): void {
|
||||
this.killCount++;
|
||||
this.baseScore += BASE_ENEMY_SCORE * 3;
|
||||
}
|
||||
|
||||
/** Record a single blade contact; every 5 contacts award a combo bonus (req 12.4). */
|
||||
public recordBladeContact(): void {
|
||||
this.consecutiveBladeHits++;
|
||||
if (this.consecutiveBladeHits >= COMBO_THRESHOLD) {
|
||||
this.comboCount++;
|
||||
this.comboBonus += COMBO_BONUS;
|
||||
this.consecutiveBladeHits = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/** Resets the consecutive blade counter if the combo is broken. */
|
||||
public breakBladeChain(): void {
|
||||
this.consecutiveBladeHits = 0;
|
||||
}
|
||||
|
||||
/** Player took damage → flawless multiplier lost (req 12.5). */
|
||||
public markTaken(): void {
|
||||
this.flawless = false;
|
||||
}
|
||||
|
||||
/** Stage-end timing bonus (req 12.6). */
|
||||
public setRemainingTimeBonus(remainingSec: number): void {
|
||||
this.timeBonus = Math.max(0, Math.floor(remainingSec)) * TIME_BONUS_PER_SEC;
|
||||
}
|
||||
|
||||
public snapshot(): IScoreSnapshot {
|
||||
const flawlessMultiplier = this.flawless ? 3 : 1;
|
||||
const finalScore = (this.baseScore + this.comboBonus + this.timeBonus) * flawlessMultiplier;
|
||||
return {
|
||||
baseScore: this.baseScore,
|
||||
comboBonus: this.comboBonus,
|
||||
timeBonus: this.timeBonus,
|
||||
flawlessMultiplier,
|
||||
finalScore,
|
||||
killCount: this.killCount,
|
||||
comboCount: this.comboCount,
|
||||
consecutiveBladeHits: this.consecutiveBladeHits,
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user