import React, { useState, useEffect, useRef } from 'react'; import { GameState } from '../types'; import { GAME_CONFIG, COLORS } from '../constants'; import { HardHat, Mountain } from 'lucide-react'; interface GameSceneProps { gameState: GameState; feedback?: { type: 'correct' | 'wrong', id: number } | null; } interface FloatingText { id: number; x: number; y: number; text: string; color: string; } export const GameScene: React.FC = ({ gameState, feedback }) => { const { characterX, bridgeEndX } = gameState; const containerRef = useRef(null); const [containerWidth, setContainerWidth] = useState(GAME_CONFIG.CANVAS_WIDTH); const [containerHeight, setContainerHeight] = useState(GAME_CONFIG.CANVAS_HEIGHT); const [isShaking, setIsShaking] = useState(false); const [floatingTexts, setFloatingTexts] = useState([]); useEffect(() => { if (!containerRef.current) return; const observer = new ResizeObserver((entries) => { for (const entry of entries) { setContainerWidth(entry.contentRect.width); setContainerHeight(entry.contentRect.height); } }); observer.observe(containerRef.current); return () => observer.disconnect(); }, []); // Handle Feedback useEffect(() => { if (!feedback) return; // Shake on error if (feedback.type === 'wrong') { setIsShaking(true); setTimeout(() => setIsShaking(false), 500); } // Add Floating Text const newText: FloatingText = { id: feedback.id, x: characterX, y: (containerHeight * 0.65) - 60, // Above character text: feedback.type === 'correct' ? '+10' : '❌', color: feedback.type === 'correct' ? 'text-green-500' : 'text-red-500' }; setFloatingTexts(prev => [...prev, newText]); // Cleanup text after animation setTimeout(() => { setFloatingTexts(prev => prev.filter(ft => ft.id !== feedback.id)); }, 1000); // eslint-disable-next-line react-hooks/exhaustive-deps }, [feedback]); // Dynamic positioning const viewOffset = Math.max(0, characterX - containerWidth * 0.3); // Place bridge at ~65% of screen height, but clamp it reasonable const bridgeY = containerHeight * 0.65; return (
{/* Background Decor */}
{/* Game World Container with transform for scrolling */}
{/* Ground (Starting Platform) */}
{/* The Abyss (Visual only, behind bridge) */}
{/* Bridge Segments */}
{/* Character */}
bridgeEndX ? '200px' : '0px'}) rotate(${gameState.status === 'GAME_OVER' && characterX > bridgeEndX ? '90deg' : '0deg'})`, transition: 'transform 0.5s ease-in' }} >
{/* Legs animation simulation */}
{/* Next Platform Marker (Visual Goal) */}
GOAL →
{/* Floating Texts */} {floatingTexts.map(ft => (
{ft.text}
))}
); };