diff --git a/guidelines/Guidelines.md b/guidelines/Guidelines.md
new file mode 100644
index 0000000..110f117
--- /dev/null
+++ b/guidelines/Guidelines.md
@@ -0,0 +1,61 @@
+**Add your own guidelines here**
+
diff --git a/src/features/snake-game/SnakeGame.tsx b/src/features/snake-game/SnakeGame.tsx
index 9f93734..efdd855 100644
--- a/src/features/snake-game/SnakeGame.tsx
+++ b/src/features/snake-game/SnakeGame.tsx
@@ -9,7 +9,6 @@ import { ScoreDisplay } from './components/ScoreDisplay';
import { DecorativeBolt } from './components/DecorativeBolt';
import { GameTabs } from './components/GameTabs';
import { HighScoreBadge } from './components/HighScoreBadge';
-import { ComboBadge } from './components/ComboBadge';
import { Countdown } from './components/Countdown';
import { PauseOverlay } from './components/PauseOverlay';
import { useSwipe } from './hooks/useSwipe';
@@ -29,6 +28,7 @@ export function SnakeGame({ className = '' }: SnakeGameProps) {
mode,
leaderboard,
combo,
+ lastEatTime,
particles,
shakeKey,
actions,
@@ -89,12 +89,9 @@ export function SnakeGame({ className = '' }: SnakeGameProps) {
status={status}
score={score}
mode={mode}
- difficulty={difficulty}
leaderboard={leaderboard}
onStart={handleStartClick}
onSaveScore={actions.saveToLeaderboard}
- onSetDifficulty={actions.setDifficulty}
- onSetMode={actions.setMode}
/>
{status === 'countdown' && }
@@ -121,18 +118,13 @@ export function SnakeGame({ className = '' }: SnakeGameProps) {
>
)}
- {status === 'playing' && combo > 1 && (
-
-
-
- )}
-
+
- );
-}
diff --git a/src/features/snake-game/components/GameOverlay.tsx b/src/features/snake-game/components/GameOverlay.tsx
index 61ddafd..c37ee97 100644
--- a/src/features/snake-game/components/GameOverlay.tsx
+++ b/src/features/snake-game/components/GameOverlay.tsx
@@ -1,33 +1,25 @@
import { memo, useState } from 'react';
-import type { GameStatus, GameMode, LeaderboardEntry, Difficulty } from '../types';
-import { DIFFICULTY_LABELS, MODE_LABELS } from '../constants';
+import type { GameStatus, GameMode, LeaderboardEntry } from '../types';
+import { DIFFICULTY_LABELS } from '../constants';
const MEDALS = ['#FFD700', '#C0C0C0', '#CD7F32'] as const;
-const DIFFICULTIES: Difficulty[] = ['easy', 'normal', 'hard'];
-const MODES: GameMode[] = ['casual', 'competitive'];
interface GameOverlayProps {
status: GameStatus;
score: number;
mode: GameMode;
- difficulty: Difficulty;
leaderboard: LeaderboardEntry[];
onStart: () => void;
onSaveScore: (name: string) => void;
- onSetDifficulty: (d: Difficulty) => void;
- onSetMode: (m: GameMode) => void;
}
export const GameOverlay = memo(function GameOverlay({
status,
score,
mode,
- difficulty,
leaderboard,
onStart,
onSaveScore,
- onSetDifficulty,
- onSetMode,
}: GameOverlayProps) {
const [playerName, setPlayerName] = useState('');
const [saved, setSaved] = useState(false);
@@ -144,44 +136,12 @@ export const GameOverlay = memo(function GameOverlay({
)}
-
-
- {DIFFICULTIES.map((d) => (
-
- ))}
- |
- {MODES.map((m) => (
-
- ))}
-
-
-
-
+
);
diff --git a/src/features/snake-game/components/ScoreDisplay.tsx b/src/features/snake-game/components/ScoreDisplay.tsx
index ffa491d..db93433 100644
--- a/src/features/snake-game/components/ScoreDisplay.tsx
+++ b/src/features/snake-game/components/ScoreDisplay.tsx
@@ -1,14 +1,23 @@
import { memo } from 'react';
-import { FOOD_TO_WIN_CASUAL } from '../constants';
+import { FOOD_TO_WIN_CASUAL, COMBO_WINDOW_MS } from '../constants';
import type { GameMode } from '../types';
interface ScoreDisplayProps {
score: number;
mode: GameMode;
+ combo: number;
+ lastEatTime: number;
}
-export const ScoreDisplay = memo(function ScoreDisplay({ score, mode }: ScoreDisplayProps) {
+export const ScoreDisplay = memo(function ScoreDisplay({
+ score,
+ mode,
+ combo,
+ lastEatTime,
+}: ScoreDisplayProps) {
if (mode === 'competitive') {
+ const showCombo = combo > 1;
+ const multiplier = Math.min(combo, 5);
return (
@@ -17,6 +26,18 @@ export const ScoreDisplay = memo(function ScoreDisplay({ score, mode }: ScoreDis
{score}
+
+ {showCombo && (
+
+ combo x{multiplier}
+
+
+ )}
+
);
}
diff --git a/src/features/snake-game/hooks/useSnakeGame.ts b/src/features/snake-game/hooks/useSnakeGame.ts
index 29679cf..45b21d1 100644
--- a/src/features/snake-game/hooks/useSnakeGame.ts
+++ b/src/features/snake-game/hooks/useSnakeGame.ts
@@ -13,6 +13,7 @@ export function useSnakeGame() {
const mode = useGameStore((s) => s.mode);
const leaderboard = useGameStore((s) => s.leaderboard);
const combo = useGameStore((s) => s.combo);
+ const lastEatTime = useGameStore((s) => s.lastEatTime);
const particles = useGameStore((s) => s.particles);
const shakeKey = useGameStore((s) => s.shakeKey);
const startGame = useGameStore((s) => s.startGame);
@@ -90,6 +91,7 @@ export function useSnakeGame() {
mode,
leaderboard,
combo,
+ lastEatTime,
particles,
shakeKey,
remainingFood,
diff --git a/src/features/snake-game/store/useGameStore.ts b/src/features/snake-game/store/useGameStore.ts
index 7cc8f7f..557291e 100644
--- a/src/features/snake-game/store/useGameStore.ts
+++ b/src/features/snake-game/store/useGameStore.ts
@@ -276,9 +276,10 @@ export const useGameStore = create((set, get) => ({
if (food && newHead.x === food.x && newHead.y === food.y) {
const { score, highScore } = get();
const foodCfg = FOOD_TYPES[food.type];
+ const isCompetitive = mode === 'competitive';
const withinCombo = tickTime - lastEatTime < COMBO_WINDOW_MS && lastEatTime > 0;
- const newCombo = withinCombo ? combo + 1 : 1;
- const comboMultiplier = Math.min(newCombo, 5);
+ const newCombo = isCompetitive && withinCombo ? combo + 1 : 1;
+ const comboMultiplier = isCompetitive ? Math.min(newCombo, 5) : 1;
const newScore = score + foodCfg.points * comboMultiplier;
const newFood = generateRandomFood(newSnake);
const newParticles = [...particles, ...spawnParticles(food.x, food.y, foodCfg.color, 10)];
diff --git a/src/styles/theme.css b/src/styles/theme.css
index c7b5f6e..66b7846 100644
--- a/src/styles/theme.css
+++ b/src/styles/theme.css
@@ -105,6 +105,31 @@
animation: combo-pop 0.25s ease-out;
}
+@keyframes snake-combo-fade {
+ 0% { opacity: 1; }
+ 60% { opacity: 1; }
+ 100% { opacity: 0; }
+}
+
+.snake-combo {
+ animation: snake-combo-fade linear forwards;
+}
+
+@keyframes snake-combo-bar {
+ 0% { transform: scaleX(1); }
+ 100% { transform: scaleX(0); }
+}
+
+.snake-combo-bar {
+ display: inline-block;
+ width: 40px;
+ height: 3px;
+ background: #b14eff;
+ border-radius: 2px;
+ transform-origin: left center;
+ animation: snake-combo-bar linear forwards;
+}
+
@keyframes countdown-pop {
0% { transform: scale(0.4); opacity: 0; }
40% { transform: scale(1.1); opacity: 1; }