advanced-games-library
Version:
Advanced Gaming Library for React Native - Four Complete Games with iOS Compatibility Fixes
381 lines (334 loc) • 9.66 kB
text/typescript
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;
};
}