first commmit

This commit is contained in:
jakciehan
2026-05-06 08:17:32 +08:00
commit 427a33c55b
269 changed files with 20906 additions and 0 deletions
+105
View File
@@ -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,
};
}
}