diff --git a/example/bun.lock b/example/bun.lock
old mode 100755
new mode 100644
index 3d521d2..79fc665
--- a/example/bun.lock
+++ b/example/bun.lock
@@ -5,7 +5,7 @@
"name": "vimazing-vim-snake-example",
"dependencies": {
"@tailwindcss/vite": "^4.1.14",
- "@vimazing/vim-snake": "file:../",
+ "@vimazing/typing-chud": "file:../",
"react": "^19.1.1",
"react-dom": "^19.1.1",
},
@@ -211,7 +211,7 @@
"@types/react-dom": ["@types/react-dom@19.2.2", "", { "peerDependencies": { "@types/react": "^19.2.0" } }, "sha512-9KQPoO6mZCi7jcIStSnlOWn2nEF3mNmyr3rIAsGnAbQKYbRLyqmeSc39EVgtxXVia+LMT8j3knZLAZAh+xLmrw=="],
- "@vimazing/vim-snake": ["@vimazing/vim-snake@file:..", { "devDependencies": { "@types/node": "^24.7.2", "@types/react": "^19.2.2", "typescript": "~5.9.3" }, "peerDependencies": { "react": "^19.1.1" } }],
+ "@vimazing/typing-chud": ["@vimazing/typing-chud@file:..", { "devDependencies": { "@types/node": "^24.7.2", "@types/react": "^19.2.2", "typescript": "~5.9.3" }, "peerDependencies": { "react": "^19.1.1" } }],
"@vitejs/plugin-react": ["@vitejs/plugin-react@5.0.4", "", { "dependencies": { "@babel/core": "^7.28.4", "@babel/plugin-transform-react-jsx-self": "^7.27.1", "@babel/plugin-transform-react-jsx-source": "^7.27.1", "@rolldown/pluginutils": "1.0.0-beta.38", "@types/babel__core": "^7.20.5", "react-refresh": "^0.17.0" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, "sha512-La0KD0vGkVkSk6K+piWDKRUyg8Rl5iAIKRMH0vMJI0Eg47bq1eOxmoObAaQG37WMW9MSyk7Cs8EIWwJC1PtzKA=="],
diff --git a/example/src/App.tsx b/example/src/App.tsx
index 72efe07..f1b7839 100755
--- a/example/src/App.tsx
+++ b/example/src/App.tsx
@@ -17,12 +17,30 @@ function App() {
Typing Chud
-
+
- Time: {formatTime(scoreManager.timeValue)}
+
Time
+
{formatTime(scoreManager.timeValue)}
- Keystrokes: {scoreManager.totalKeystrokes}
+
Score
+
{scoreManager.score}
+
+
+
Correct
+
{scoreManager.correct}
+
+
+
Missed
+
{scoreManager.missed}
+
+
+
Deaths
+
{scoreManager.deaths}
+
+
+
Keystrokes
+
{scoreManager.totalKeystrokes}
diff --git a/package.json b/package.json
index bb52ee1..ff357a0 100755
--- a/package.json
+++ b/package.json
@@ -46,6 +46,7 @@
"dist"
],
"scripts": {
+ "dev": "cd example && bun run dev",
"build": "tsc --outDir dist --declaration --emitDeclarationOnly false && node -e \"const fs = require('node:fs'); fs.mkdirSync('dist', { recursive: true }); fs.copyFileSync('src/game.css', 'dist/game.css'); fs.copyFileSync('package.json', 'dist/package.json');\"",
"prepublishOnly": "npm run build"
},
diff --git a/src/types.ts b/src/types.ts
index 49c3608..6513c55 100755
--- a/src/types.ts
+++ b/src/types.ts
@@ -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;
};
// ============================================================================
diff --git a/src/useGameStatus/useGameStatus.ts b/src/useGameStatus/useGameStatus.ts
index 26ce4e3..bfcf06e 100755
--- a/src/useGameStatus/useGameStatus.ts
+++ b/src/useGameStatus/useGameStatus.ts
@@ -14,6 +14,10 @@ export function useGameStatus(onGameLoopTick?: (deltaTime: number, letters: Fall
const [gameStatus, setGameStatus] = useState
("waiting");
const [shipState, setShipState] = useState("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(null);
const lastFrameTimeRef = useRef(0);
const lettersRef = useRef([]);
@@ -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,
};
}
diff --git a/src/useScore/useScore.ts b/src/useScore/useScore.ts
index d1b71e0..753acdc 100755
--- a/src/useScore/useScore.ts
+++ b/src/useScore/useScore.ts
@@ -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,
};
}