UNPKG

advanced-games-library

Version:

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

381 lines (334 loc) 9.66 kB
import React from 'react'; import { GameConfig, GameState, GameResult, PlayerAction, GameDifficulty, GameStatus, GameEventType, AnalyticsEvent } from '../../core/types'; export interface GameComponent { gameId: string; render(): any; cleanup(): void; } export interface GameEngine { initialize(config: GameConfig): Promise<void>; start(): void; pause(): void; resume(): void; restart(): void; end(): GameResult; getState(): GameState; onPlayerAction(action: PlayerAction): void; } /** * Abstract base class for all games * Provides common functionality and enforces game structure */ export abstract class BaseGame implements GameEngine { protected config!: GameConfig; protected state!: GameState; protected startTime!: number; protected endTime?: number; protected sessionId!: string; protected eventListeners: Map<string, Function[]> = new Map(); // Abstract properties that each game must define abstract readonly gameId: string; abstract readonly name: string; abstract readonly description: string; abstract readonly category: string; abstract readonly version: string; abstract readonly minDifficulty: GameDifficulty; abstract readonly maxDifficulty: GameDifficulty; abstract readonly estimatedDuration: number; // minutes constructor() { this.sessionId = this.generateSessionId(); } // Abstract methods that each game must implement abstract initializeGameLogic(): Promise<void>; abstract startGameLogic(): void; abstract pauseGameLogic(): void; abstract resumeGameLogic(): void; abstract restartGameLogic(): void; abstract endGameLogic(): GameResult; abstract processPlayerAction(action: PlayerAction): void; abstract validateGameConfig(config: GameConfig): boolean; /** * Initialize the game with configuration */ async initialize(config: GameConfig): Promise<void> { if (!this.validateGameConfig(config)) { throw new Error(`Invalid configuration for game ${this.gameId}`); } this.config = config; this.state = this.createInitialState(); try { await this.initializeGameLogic(); this.emit(GameEventType.GAME_STARTED, { gameId: this.gameId }); } catch (error) { this.trackError('initialization_failed', error); throw error; } } /** * Start the game */ start(): void { if (this.state.status !== GameStatus.NOT_STARTED && this.state.status !== GameStatus.PAUSED) { throw new Error('Game cannot be started in current state'); } this.startTime = Date.now(); this.state.status = GameStatus.PLAYING; try { this.startGameLogic(); this.emit(GameEventType.GAME_STARTED, { gameId: this.gameId, difficulty: this.config.difficulty, timestamp: new Date() }); } catch (error) { this.trackError('start_failed', error); throw error; } } /** * Pause the game */ pause(): void { if (this.state.status !== GameStatus.PLAYING) { return; } this.state.status = GameStatus.PAUSED; try { this.pauseGameLogic(); this.emit(GameEventType.GAME_PAUSED, { gameId: this.gameId }); } catch (error) { this.trackError('pause_failed', error); } } /** * Resume the game */ resume(): void { if (this.state.status !== GameStatus.PAUSED) { return; } this.state.status = GameStatus.PLAYING; try { this.resumeGameLogic(); this.emit(GameEventType.GAME_RESUMED, { gameId: this.gameId }); } catch (error) { this.trackError('resume_failed', error); } } /** * Restart the game */ restart(): void { try { this.restartGameLogic(); this.state = this.createInitialState(); this.sessionId = this.generateSessionId(); this.startTime = Date.now(); this.endTime = undefined; } catch (error) { this.trackError('restart_failed', error); throw error; } } /** * End the game and return results */ end(): GameResult { this.endTime = Date.now(); this.state.status = this.state.currentScore > 0 ? GameStatus.COMPLETED : GameStatus.FAILED; try { const result = this.endGameLogic(); this.emit(GameEventType.GAME_COMPLETED, { gameId: this.gameId, result }); return result; } catch (error) { this.trackError('end_failed', error); throw error; } } /** * Get current game state */ getState(): GameState { return { ...this.state }; } /** * Handle player action */ onPlayerAction(action: PlayerAction): void { if (this.state.status !== GameStatus.PLAYING) { return; } try { this.processPlayerAction(action); this.emit(GameEventType.PLAYER_ACTION, { gameId: this.gameId, action }); } catch (error) { this.trackError('action_failed', error); } } /** * Update game score */ protected updateScore(points: number): void { const previousScore = this.state.currentScore; this.state.currentScore = Math.max(0, this.state.currentScore + points); this.emit(GameEventType.SCORE_UPDATED, { gameId: this.gameId, previousScore, newScore: this.state.currentScore, change: points }); } /** * Update elapsed time */ protected updateTime(): void { if (this.state.status === GameStatus.PLAYING) { this.state.timeElapsed = Date.now() - this.startTime; if (this.config.timeLimit) { const remaining = (this.config.timeLimit * 1000) - this.state.timeElapsed; this.state.timeRemaining = Math.max(0, remaining); if (remaining <= 0) { this.end(); } } } } /** * Create initial game state */ protected createInitialState(): GameState { return { status: GameStatus.NOT_STARTED, currentScore: 0, timeElapsed: 0, timeRemaining: this.config.timeLimit ? this.config.timeLimit * 1000 : undefined, level: 1, lives: 3, moves: 0, gameSpecificData: {} }; } /** * Generate unique session ID */ protected generateSessionId(): string { return `${this.gameId}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; } /** * Event system methods */ protected emit(eventType: GameEventType, data: any): void { const listeners = this.eventListeners.get(eventType) || []; listeners.forEach(listener => { try { listener(data); } catch (error) { console.warn('Event listener error:', error); } }); } public on(eventType: GameEventType, listener: Function): void { if (!this.eventListeners.has(eventType)) { this.eventListeners.set(eventType, []); } this.eventListeners.get(eventType)!.push(listener); } public off(eventType: GameEventType, listener: Function): void { const listeners = this.eventListeners.get(eventType) || []; const index = listeners.indexOf(listener); if (index > -1) { listeners.splice(index, 1); } } /** * Analytics and error tracking */ protected trackError(type: string, error: any): void { const event: AnalyticsEvent = { event: 'game_error', gameId: this.gameId, properties: { errorType: type, errorMessage: error.message || error, gameState: this.state, timestamp: new Date() }, timestamp: new Date() }; this.emit('analytics_event' as GameEventType, event); } protected trackCustomEvent(eventName: string, properties: Record<string, any>): void { const event: AnalyticsEvent = { event: eventName, gameId: this.gameId, properties: { ...properties, gameState: this.state }, timestamp: new Date() }; this.emit('analytics_event' as GameEventType, event); } /** * Utility methods */ protected getElapsedTime(): number { return this.endTime ? this.endTime - this.startTime : Date.now() - this.startTime; } protected calculateScore(baseScore: number): number { let finalScore = baseScore; // Time bonus (if completed quickly) if (this.config.rewards?.bonusForSpeed && this.state.status === GameStatus.COMPLETED) { const timeBonus = Math.max(0, this.config.rewards.bonusForSpeed - Math.floor(this.getElapsedTime() / 1000)); finalScore += timeBonus; } // Difficulty multiplier const difficultyMultipliers = { [GameDifficulty.EASY]: 1, [GameDifficulty.MEDIUM]: 1.5, [GameDifficulty.HARD]: 2, [GameDifficulty.EXPERT]: 3 }; finalScore *= difficultyMultipliers[this.config.difficulty] || 1; return Math.round(finalScore); } /** * Cleanup resources */ public cleanup(): void { this.eventListeners.clear(); // Subclasses should override this to cleanup specific resources } } /** * Factory interface for creating game instances */ export interface GameFactory { createGame(): BaseGame; getGameInfo(): { id: string; name: string; description: string; category: string; thumbnail: string; version: string; }; }