feat: implement complete score tracking system

- Track score (starts at 0)
- +1 score and +1 correct for each correct character typed
- -1 score and +1 missed for each wrong character typed
- +1 deaths for each collision with ship
- Score resets when game starts
- All metrics exposed through ScoreManager
- Updated example app UI with colorful scoreboard showing:
  - Time (gray)
  - Score (neutral)
  - Correct (green)
  - Missed (yellow)
  - Deaths (red)
  - Keystrokes (neutral)
- Uses grid layout for organized metric display
- Score cannot go below 0 (clamped to minimum)
This commit is contained in:
2025-11-03 23:04:26 +00:00
parent 08c2d40d23
commit 7ee42a8dc1
6 changed files with 74 additions and 8 deletions
+11 -2
View File
@@ -82,6 +82,10 @@ export type CursorManager = {
export type ScoreManager = {
timeValue: number;
totalKeystrokes: number;
score: number;
correct: number;
missed: number;
deaths: number;
};
// ============================================================================
@@ -94,13 +98,18 @@ export type GameStatusManager = {
startGame: () => void;
quitGame: () => void;
level: number;
score: number;
togglePause: () => void;
shipState: ShipState;
shipExplosionTime: number;
updatePlayerX: (x: number) => void;
handleTypedLetter: (letter: string) => void;
setGameLoopCallback: (callback: (deltaTime: number, letters: FallingLetter[]) => void) => void;
addCorrect: () => void;
addMissed: () => void;
addDeath: () => void;
score: number;
correct: number;
missed: number;
deaths: number;
};
// ============================================================================
+34
View File
@@ -14,6 +14,10 @@ export function useGameStatus(onGameLoopTick?: (deltaTime: number, letters: Fall
const [gameStatus, setGameStatus] = useState<GameStatus>("waiting");
const [shipState, setShipState] = useState<ShipState>("normal");
const [shipExplosionTime, setShipExplosionTime] = useState(0);
const [score, setScore] = useState(0);
const [correct, setCorrect] = useState(0);
const [missed, setMissed] = useState(0);
const [deaths, setDeaths] = useState(0);
const gameLoopRef = useRef<number | null>(null);
const lastFrameTimeRef = useRef<number>(0);
const lettersRef = useRef<FallingLetter[]>([]);
@@ -61,6 +65,7 @@ export function useGameStatus(onGameLoopTick?: (deltaTime: number, letters: Fall
letter.stateStartTime = currentTime;
setShipState('exploding');
setShipExplosionTime(performance.now());
setDeaths(prev => prev + 1);
// Clear letters and reset spawn timer after explosion animation
setTimeout(() => {
@@ -88,6 +93,10 @@ export function useGameStatus(onGameLoopTick?: (deltaTime: number, letters: Fall
lastFrameTimeRef.current = performance.now();
lastSpawnRef.current = performance.now();
lettersRef.current = [];
setScore(0);
setCorrect(0);
setMissed(0);
setDeaths(0);
setGameStatus("started");
gameLoopRef.current = requestAnimationFrame(gameLoop);
}, [gameLoop]);
@@ -108,6 +117,8 @@ export function useGameStatus(onGameLoopTick?: (deltaTime: number, letters: Fall
if (typedLetter.toUpperCase() === currentLetter.letter) {
currentLetter.state = 'exploding';
currentLetter.stateStartTime = performance.now();
setScore(prev => prev + 1);
setCorrect(prev => prev + 1);
setTimeout(() => {
lettersRef.current = [];
@@ -116,6 +127,8 @@ export function useGameStatus(onGameLoopTick?: (deltaTime: number, letters: Fall
} else {
currentLetter.state = 'wrong';
currentLetter.stateStartTime = performance.now();
setScore(prev => Math.max(0, prev - 1));
setMissed(prev => prev + 1);
setTimeout(() => {
if (currentLetter.state === 'wrong') {
@@ -133,6 +146,20 @@ export function useGameStatus(onGameLoopTick?: (deltaTime: number, letters: Fall
onGameLoopTickRef.current = callback;
}, []);
const addCorrect = useCallback(() => {
setScore(prev => prev + 1);
setCorrect(prev => prev + 1);
}, []);
const addMissed = useCallback(() => {
setScore(prev => Math.max(0, prev - 1));
setMissed(prev => prev + 1);
}, []);
const addDeath = useCallback(() => {
setDeaths(prev => prev + 1);
}, []);
return {
gameStatus,
setGameStatus,
@@ -143,6 +170,13 @@ export function useGameStatus(onGameLoopTick?: (deltaTime: number, letters: Fall
shipExplosionTime,
updatePlayerX,
setGameLoopCallback,
addCorrect,
addMissed,
addDeath,
score,
correct,
missed,
deaths,
};
}
+5 -1
View File
@@ -16,7 +16,11 @@ export function useScore(gameStatusManager: any): ScoreManager {
return {
timeValue,
totalKeystrokes: 0, // TODO: Track keystrokes
totalKeystrokes: 0, // TODO: Track keystrokes from keyboard input
score: gameStatusManager.score,
correct: gameStatusManager.correct,
missed: gameStatusManager.missed,
deaths: gameStatusManager.deaths,
};
}