From b82bef56e523526193bcfbaa6f004781e5550a96 Mon Sep 17 00:00:00 2001 From: Andre Padez Date: Fri, 31 Oct 2025 00:28:29 +0000 Subject: [PATCH] feat: add maxLevel to GameOptions with default of 25 - New option: maxLevel (defaults to 25) - Once a player reaches maxLevel, levels no longer progress - Prevents unlimited level scaling and FPS increases - Example: useGame({ maxLevel: 10 }) --- example/src/App.tsx | 7 ++++--- src/types.ts | 1 + src/useGame/useGame.ts | 4 ++-- src/useGameStatus/useGameStatus.ts | 4 +++- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/example/src/App.tsx b/example/src/App.tsx index 7ba21fd..97cb735 100755 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -3,7 +3,8 @@ import "@vimazing/vim-snake/game.css"; import { useKeyBindings } from "./useKeyBindings"; function App() { - const { containerRef, gameStatus, scoreManager, keyLog, score, level } = useGame({}, useKeyBindings); + const gameManager = useGame({ startingLevel: 10 }, useKeyBindings); + const { containerRef, gameStatus, scoreManager, score, level } = gameManager; const formatTime = (ms: number) => { const seconds = Math.floor(ms / 1000); @@ -15,7 +16,7 @@ function App() { return (

VIMazing Snake

- +
Time: {formatTime(scoreManager.timeValue)} @@ -37,7 +38,7 @@ function App() {
- +
{gameStatus === "waiting" &&

Press space to start

} {gameStatus === "started" &&

Use hjkl to change direction • Press q to quit

} diff --git a/src/types.ts b/src/types.ts index 6270e93..88b39fe 100755 --- a/src/types.ts +++ b/src/types.ts @@ -34,6 +34,7 @@ export type GameOptions = { timeLimit?: number; startingLevel?: number; foodsPerLevel?: number; + maxLevel?: number; }; // ============================================================================ diff --git a/src/useGame/useGame.ts b/src/useGame/useGame.ts index d795183..0d3fa16 100755 --- a/src/useGame/useGame.ts +++ b/src/useGame/useGame.ts @@ -8,7 +8,7 @@ import { useKeyBindings, type UseKeyBindingsType } from "./useKeyBindings"; import { useScore } from "../useScore"; export function useGame(options?: GameOptions, platformHook?: unknown): GameManager { - const { cols = 30, rows = 20, startingLevel, foodsPerLevel } = options ?? {}; + const { cols = 30, rows = 20, startingLevel, foodsPerLevel, maxLevel } = options ?? {}; const boardManager = useBoard(); const { containerRef, renderBoard } = boardManager; @@ -18,7 +18,7 @@ export function useGame(options?: GameOptions, platformHook?: unknown): GameMana const foodManager = useFood(cols, rows, boardManager); - const gameManager = useGameStatus(boardManager, snakeManager, foodManager, { startingLevel, foodsPerLevel }); + const gameManager = useGameStatus(boardManager, snakeManager, foodManager, { startingLevel, foodsPerLevel, maxLevel }); const { gameStatus, setGameStatus, startGame, quitGame, togglePause, score, level } = gameManager; useEffect(() => { diff --git a/src/useGameStatus/useGameStatus.ts b/src/useGameStatus/useGameStatus.ts index c62a9c6..5f848c1 100755 --- a/src/useGameStatus/useGameStatus.ts +++ b/src/useGameStatus/useGameStatus.ts @@ -7,6 +7,7 @@ import type { UseFoodType } from "../useGame/useFood"; export type UseGameStatusParams = { startingLevel?: number; foodsPerLevel?: number; + maxLevel?: number; }; export function useGameStatus( @@ -17,6 +18,7 @@ export function useGameStatus( ) { const startingLevel = params?.startingLevel ?? 1; const foodsPerLevel = params?.foodsPerLevel ?? 10; + const maxLevel = params?.maxLevel ?? 25; const { containerRef } = boardManager; const { initSnake, clearSnake, snakeBodyRef } = snakeManager; const { spawnFood, clearFood } = foodManager; @@ -99,7 +101,7 @@ export function useGameStatus( const pointsToAdd = levelRef.current; // Update level OUTSIDE of setScore to avoid multiple triggers - if (willLevelUp) { + if (willLevelUp && levelRef.current < maxLevel) { const newLevel = levelRef.current + 1; levelRef.current = newLevel; setLevel(newLevel);