UNPKG

advanced-games-library

Version:

Advanced Gaming Library for React Native - Four Complete Games with iOS Compatibility Fixes

634 lines (548 loc) 17.9 kB
import React, { useState, useEffect, useCallback, useRef } from 'react'; import { GameManager } from '../services/GameManager'; import { GameResult, PlayerData, GameLibraryError } from '../core/types'; // AudioService was deleted, removing import /** * Hook for managing games library with initialization and game launching */ export const useGamesLibrary = () => { const [isInitialized, setIsInitialized] = useState(false); const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState<string | null>(null); const [availableGames, setAvailableGames] = useState<any[]>([]); const gameManager = GameManager.getInstance(); // Initialize library const initialize = useCallback(async (config: any) => { setIsLoading(true); setError(null); try { await gameManager.initialize(config); const games = gameManager.getAvailableGames(); setAvailableGames(games); setIsInitialized(true); } catch (err) { const errorMessage = err instanceof GameLibraryError ? err.message : 'Failed to initialize library'; setError(errorMessage); throw err; } finally { setIsLoading(false); } }, []); // Launch game const launchGame = useCallback(async (gameId: string, config?: any) => { setIsLoading(true); setError(null); try { const game = await gameManager.launchGame(gameId, config); return game; } catch (err) { const errorMessage = err instanceof GameLibraryError ? err.message : 'Failed to launch game'; setError(errorMessage); throw err; } finally { setIsLoading(false); } }, []); return { // State isInitialized, isLoading, error, availableGames, // Methods initialize, launchGame, // Utils clearError: () => setError(null), }; }; /** * Hook for managing game history and statistics */ export const useGameHistory = () => { const [gameHistory, setGameHistory] = useState<GameResult[]>([]); const [isLoading, setIsLoading] = useState(false); const gameManager = GameManager.getInstance(); // Load game history const loadGameHistory = useCallback(async (playerId?: string) => { setIsLoading(true); try { const history = await gameManager.getGameHistory(playerId); setGameHistory(history); } catch (err) { console.warn('Failed to load game history:', err); } finally { setIsLoading(false); } }, []); // Calculate improvement trend const calculateImprovement = useCallback((results: GameResult[]) => { if (results.length < 2) return 0; const recent = results.slice(-3); const earlier = results.slice(0, 3); if (earlier.length === 0) return 0; const recentAvg = recent.reduce((sum, r) => sum + r.score, 0) / recent.length; const earlierAvg = earlier.reduce((sum, r) => sum + r.score, 0) / earlier.length; return Math.round(((recentAvg - earlierAvg) / earlierAvg) * 100); }, []); // Get statistics for a specific game const getGameSpecificStats = useCallback((gameId: string) => { const gameResults = gameHistory.filter(result => result.gameId === gameId); if (gameResults.length === 0) { return null; } const totalPlays = gameResults.length; const completedGames = gameResults.filter(r => r.completed); const bestScore = Math.max(...gameResults.map(r => r.score)); const averageScore = gameResults.reduce((sum, r) => sum + r.score, 0) / totalPlays; const completionRate = (completedGames.length / totalPlays) * 100; const averageTime = gameResults.reduce((sum, r) => sum + r.timeSpent, 0) / totalPlays; return { gameId, totalPlays, bestScore, averageScore: Math.round(averageScore), completionRate: Math.round(completionRate), averageTime: Math.round(averageTime), lastPlayed: gameResults[gameResults.length - 1]?.timestamp || null, improvement: calculateImprovement(gameResults) }; }, [gameHistory, calculateImprovement]); // Calculate general statistics const stats = React.useMemo(() => { const totalPlays = gameHistory.length; const completedGames = gameHistory.filter(r => r.completed); const bestScore = gameHistory.length > 0 ? Math.max(...gameHistory.map(r => r.score)) : 0; const averageScore = totalPlays > 0 ? gameHistory.reduce((sum, r) => sum + r.score, 0) / totalPlays : 0; const completionRate = totalPlays > 0 ? (completedGames.length / totalPlays) * 100 : 0; const averageTime = totalPlays > 0 ? gameHistory.reduce((sum, r) => sum + r.timeSpent, 0) / totalPlays : 0; return { totalPlays, bestScore, averageScore: Math.round(averageScore), completionRate: Math.round(completionRate), averageTime: Math.round(averageTime), lastPlayed: gameHistory[gameHistory.length - 1]?.timestamp || null, improvement: calculateImprovement(gameHistory) }; }, [gameHistory, calculateImprovement]); // Load history on mount useEffect(() => { loadGameHistory(); }, [loadGameHistory]); return { // State gameHistory, stats, isLoading, // Methods loadGameHistory, getGameSpecificStats, }; }; /** * Hook for managing player data */ export const usePlayer = () => { const [player, setPlayerState] = useState<PlayerData | null>(null); const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState<string | null>(null); const gameManager = GameManager.getInstance(); // Load player data const loadPlayer = useCallback(() => { try { const playerData = gameManager.getPlayer(); setPlayerState(playerData); } catch (err) { setError('Failed to load player data'); } }, []); // Update player data const updatePlayer = useCallback(async (playerData: PlayerData) => { setIsLoading(true); setError(null); try { gameManager.setPlayer(playerData); setPlayerState(playerData); } catch (err) { const errorMessage = err instanceof GameLibraryError ? err.message : 'Failed to update player data'; setError(errorMessage); throw err; } finally { setIsLoading(false); } }, []); // Get player statistics const getPlayerStats = useCallback(() => { if (!player) return null; return { totalGames: player.totalGamesPlayed, achievements: player.achievements.length, memberSince: player.createdAt, lastActive: player.lastActive }; }, [player]); // Load player on mount useEffect(() => { loadPlayer(); }, [loadPlayer]); return { // State player, isLoading, error, // Methods updatePlayer, loadPlayer, getPlayerStats, // Utils clearError: () => setError(null), }; }; /** * Hook for managing game timer */ export const useGameTimer = (initialTime: number, onTimeUp?: () => void) => { const [timeLeft, setTimeLeft] = useState(initialTime); const [isRunning, setIsRunning] = useState(false); const [isPaused, setIsPaused] = useState(false); const intervalRef = useRef<NodeJS.Timeout>(); // Start timer const start = useCallback(() => { setIsRunning(true); setIsPaused(false); }, []); // Pause timer const pause = useCallback(() => { setIsRunning(false); setIsPaused(true); }, []); // Resume timer const resume = useCallback(() => { setIsRunning(true); setIsPaused(false); }, []); // Reset timer const reset = useCallback((newTime?: number) => { setTimeLeft(newTime || initialTime); setIsRunning(false); setIsPaused(false); }, [initialTime]); // Stop timer const stop = useCallback(() => { setIsRunning(false); setIsPaused(false); setTimeLeft(0); }, []); // Timer logic useEffect(() => { if (isRunning && timeLeft > 0) { intervalRef.current = setInterval(() => { setTimeLeft(prev => { const newTime = prev - 1; if (newTime <= 0) { setIsRunning(false); onTimeUp?.(); return 0; } return newTime; }); }, 1000); } else { if (intervalRef.current) { clearInterval(intervalRef.current); } } return () => { if (intervalRef.current) { clearInterval(intervalRef.current); } }; }, [isRunning, timeLeft, onTimeUp]); // Format time for display const formatTime = useCallback((seconds: number) => { const minutes = Math.floor(seconds / 60); const remainingSeconds = seconds % 60; return `${minutes}:${remainingSeconds.toString().padStart(2, '0')}`; }, []); return { // State timeLeft, isRunning, isPaused, formattedTime: formatTime(timeLeft), // Methods start, pause, resume, reset, stop, formatTime, }; }; /** * Hook for managing sound effects */ export const useGameSounds = () => { const [isMuted, setIsMuted] = useState(false); const [volume, setVolume] = useState(1.0); // AudioService was deleted, using placeholder const audioService = null; // Initialize audio service useEffect(() => { // AudioService was deleted, skipping initialization }, []); // Update audio config when settings change useEffect(() => { // AudioService was deleted, skipping config update }, [isMuted, volume]); // Play sound effect const playSound = useCallback(async (soundType: 'success' | 'failure' | 'click' | 'countdown' | 'background' | 'notification' | 'achievement' | 'move' | 'card_flip' | 'match_found' | 'wrong_answer' | 'correct_answer' | 'level_complete') => { if (isMuted) return; // AudioService was deleted, skipping sound playback console.log(`Would play sound: ${soundType}`); }, [isMuted, volume]); // Play background music const playBackgroundMusic = useCallback(async (audioUrl?: string) => { if (isMuted) return; // AudioService was deleted, skipping background music console.log(`Would play background music: ${audioUrl}`); }, [isMuted, volume]); // Stop background music const stopBackgroundMusic = useCallback(() => { // AudioService was deleted, skipping stop console.log('Would stop background music'); }, []); // Stop all sounds const stopAllSounds = useCallback(() => { // AudioService was deleted, skipping stop console.log('Would stop all sounds'); }, []); // Toggle mute const toggleMute = useCallback(() => { setIsMuted(prev => !prev); }, []); // Set volume (0.0 to 1.0) const changeVolume = useCallback((newVolume: number) => { setVolume(Math.max(0, Math.min(1, newVolume))); }, []); // Preload custom sound const preloadSound = useCallback(async (soundId: string, audioUrl: string) => { // AudioService was deleted, skipping preload console.log(`Would preload sound: ${soundId}`); }, []); // Get available sounds const getAvailableSounds = useCallback(() => { // AudioService was deleted, returning empty array return []; }, []); return { // State isMuted, volume, isAudioSupported: false, // AudioService was deleted // Methods playSound, playBackgroundMusic, stopBackgroundMusic, stopAllSounds, toggleMute, changeVolume, preloadSound, getAvailableSounds, }; }; /** * Hook for managing game performance metrics */ export const useGamePerformance = () => { const [metrics, setMetrics] = useState({ fps: 60, memoryUsage: 0, renderTime: 0, lastUpdate: Date.now() }); // Update performance metrics const updateMetrics = useCallback(() => { // This would integrate with performance monitoring // For now, just mock data setMetrics({ fps: Math.floor(Math.random() * 10) + 55, memoryUsage: Math.floor(Math.random() * 50) + 50, renderTime: Math.floor(Math.random() * 5) + 10, lastUpdate: Date.now() }); }, []); // Start performance monitoring useEffect(() => { const interval = setInterval(updateMetrics, 1000); return () => clearInterval(interval); }, [updateMetrics]); return { metrics, updateMetrics, }; }; /** * Hook for managing game achievements */ export const useAchievements = () => { const [achievements, setAchievements] = useState<any[]>([]); const [unlockedAchievements, setUnlockedAchievements] = useState<any[]>([]); const [isLoading, setIsLoading] = useState(false); const gameManager = GameManager.getInstance(); // Load achievements const loadAchievements = useCallback(async () => { setIsLoading(true); try { const player = gameManager.getPlayer(); if (player) { setUnlockedAchievements(player.achievements); } // In a real implementation, this would load all possible achievements setAchievements([ { id: 'first_game', name: 'המשחק הראשון', description: 'השלם את המשחק הראשון שלך', icon: '🎮', category: 'milestone' }, { id: 'speed_demon', name: 'שד המהירות', description: 'השלם משחק תוך פחות מ-30 שניות', icon: '⚡', category: 'speed' }, { id: 'perfectionist', name: 'פרפקציוניסט', description: 'השג ציון מושלם', icon: '🏆', category: 'performance' } ]); } catch (err) { console.warn('Failed to load achievements:', err); } finally { setIsLoading(false); } }, []); // Check for new achievements const checkAchievements = useCallback((gameResult: GameResult) => { const newAchievements: any[] = []; // Check first game achievement if (!unlockedAchievements.find(a => a.id === 'first_game')) { newAchievements.push({ id: 'first_game', name: 'המשחק הראשון', description: 'השלם את המשחק הראשון שלך', icon: '🎮', unlockedAt: new Date() }); } // Check speed achievement if (gameResult.timeSpent < 30000 && !unlockedAchievements.find(a => a.id === 'speed_demon')) { newAchievements.push({ id: 'speed_demon', name: 'שד המהירות', description: 'השלם משחק תוך פחות מ-30 שניות', icon: '⚡', unlockedAt: new Date() }); } // Check perfect score achievement if (gameResult.score === gameResult.maxScore && !unlockedAchievements.find(a => a.id === 'perfectionist')) { newAchievements.push({ id: 'perfectionist', name: 'פרפקציוניסט', description: 'השג ציון מושלם', icon: '🏆', unlockedAt: new Date() }); } if (newAchievements.length > 0) { setUnlockedAchievements(prev => [...prev, ...newAchievements]); return newAchievements; } return []; }, [unlockedAchievements]); // Load achievements on mount useEffect(() => { loadAchievements(); }, [loadAchievements]); return { achievements, unlockedAchievements, isLoading, checkAchievements, loadAchievements, }; }; /** * Hook for managing game preferences and settings */ export const useGameSettings = () => { const [settings, setSettings] = useState({ soundEnabled: true, musicEnabled: true, vibrationEnabled: true, difficulty: 'medium' as 'easy' | 'medium' | 'hard', language: 'he', theme: 'light' as 'light' | 'dark', notifications: true, analytics: true }); // Load settings from storage const loadSettings = useCallback(async () => { try { // In a real implementation, this would load from AsyncStorage const savedSettings = localStorage.getItem('gameSettings'); if (savedSettings) { setSettings(JSON.parse(savedSettings)); } } catch (err) { console.warn('Failed to load settings:', err); } }, []); // Save settings to storage const saveSettings = useCallback(async (newSettings: Partial<typeof settings>) => { try { const updatedSettings = { ...settings, ...newSettings }; setSettings(updatedSettings); localStorage.setItem('gameSettings', JSON.stringify(updatedSettings)); } catch (err) { console.warn('Failed to save settings:', err); } }, [settings]); // Update specific setting const updateSetting = useCallback((key: keyof typeof settings, value: any) => { saveSettings({ [key]: value }); }, [saveSettings]); // Reset to defaults const resetSettings = useCallback(() => { const defaultSettings = { soundEnabled: true, musicEnabled: true, vibrationEnabled: true, difficulty: 'medium' as const, language: 'he', theme: 'light' as const, notifications: true, analytics: true }; saveSettings(defaultSettings); }, [saveSettings]); // Load settings on mount useEffect(() => { loadSettings(); }, [loadSettings]); return { settings, updateSetting, saveSettings, resetSettings, loadSettings, }; };