Spaces:
Runtime error
Runtime error
| // =================================================================== | |
| // RETRO LEGENDS: Ultimate Edition - Core Engine Ultra Avanzado | |
| // Desarrollado con arquitectura modular profesional y optimización extrema | |
| // =================================================================== | |
| class RetroLegendsUltimateEngine { | |
| constructor() { | |
| // Configuración del canvas y contexto | |
| this.canvas = document.getElementById('gameCanvas'); | |
| this.ctx = this.canvas.getContext('2d'); | |
| this.setupCanvas(); | |
| // Estados del motor | |
| this.engineState = { | |
| isInitialized: false, | |
| isRunning: false, | |
| isPaused: false, | |
| debugMode: false, | |
| performanceMode: 'ultra', | |
| frameRate: 60, | |
| deltaTime: 0, | |
| lastFrameTime: 0, | |
| frameCount: 0, | |
| averageFPS: 60 | |
| }; | |
| // Estado del juego principal | |
| this.gameState = { | |
| currentLevel: 'caminito_matrix', | |
| score: 0, | |
| lives: 3, | |
| mateCount: 5, | |
| health: 100, | |
| maxHealth: 100, | |
| energy: 100, | |
| maxEnergy: 100, | |
| combo: 0, | |
| maxCombo: 0, | |
| time: 0, | |
| difficulty: 1, | |
| experience: 0, | |
| level: 1, | |
| achievements: new Set(), | |
| powerUps: new Map(), | |
| inventory: new Map() | |
| }; | |
| // Configuración del jugador ultra avanzada | |
| this.player = { | |
| // Posición y dimensiones | |
| x: 150, | |
| y: 400, | |
| width: 32, | |
| height: 32, | |
| centerX: 0, | |
| centerY: 0, | |
| // Velocidades y física | |
| velocityX: 0, | |
| velocityY: 0, | |
| acceleration: 0.8, | |
| deceleration: 0.85, | |
| maxSpeed: 8, | |
| jumpPower: 16, | |
| doubleJumpPower: 12, | |
| dashPower: 15, | |
| // Estados del jugador | |
| grounded: false, | |
| canDoubleJump: false, | |
| canDash: true, | |
| isDashing: false, | |
| isAttacking: false, | |
| isInvulnerable: false, | |
| isCharging: false, | |
| // Direcciones y orientación | |
| facing: 'right', | |
| lastDirection: 'right', | |
| // Timers y cooldowns | |
| invulnerabilityTimer: 0, | |
| dashCooldown: 0, | |
| attackCooldown: 0, | |
| chargeCooldown: 0, | |
| // Sprites y animaciones | |
| currentSprite: '🧉', | |
| spriteSet: { | |
| idle: ['🧉', '🧉'], | |
| running: ['🏃♂️', '🚶♂️'], | |
| jumping: ['🤸♂️'], | |
| attacking: ['⚔️', '🥊'], | |
| dashing: ['💨'], | |
| charging: ['⚡', '🔥'] | |
| }, | |
| animationFrame: 0, | |
| animationSpeed: 200, | |
| lastAnimationTime: 0, | |
| // Estadísticas y progresión | |
| stats: { | |
| strength: 10, | |
| agility: 10, | |
| defense: 10, | |
| luck: 10, | |
| charisma: 10 | |
| }, | |
| // Habilidades especiales | |
| abilities: { | |
| mateHealing: true, | |
| empanadasPower: false, | |
| choripanShield: false, | |
| fernetRage: false, | |
| tangoTime: false | |
| }, | |
| // Sistema de combos argentinos | |
| comboSystem: { | |
| currentCombo: [], | |
| comboTimer: 0, | |
| maxComboTime: 2000, | |
| availableCombos: [ | |
| { sequence: ['che', 'boludo', 'dale'], name: 'Combo Argento', power: 150 }, | |
| { sequence: ['mate', 'asado', 'futbol'], name: 'Combo Patriótico', power: 200 }, | |
| { sequence: ['tango', 'milonga', 'bandoneón'], name: 'Combo Porteño', power: 250 } | |
| ] | |
| } | |
| }; | |
| // Configuración de físicas avanzadas | |
| this.physics = { | |
| gravity: 0.8, | |
| terminalVelocity: 20, | |
| airResistance: 0.98, | |
| groundFriction: 0.85, | |
| bounceFactor: 0.3, | |
| collisionPadding: 2, | |
| // Configuraciones específicas por material | |
| materials: { | |
| concrete: { friction: 0.8, bounce: 0.1 }, | |
| grass: { friction: 0.9, bounce: 0.2 }, | |
| ice: { friction: 0.3, bounce: 0.1 }, | |
| rubber: { friction: 0.7, bounce: 0.8 }, | |
| metal: { friction: 0.6, bounce: 0.4 } | |
| } | |
| }; | |
| // Configuración de cámara avanzada | |
| this.camera = { | |
| x: 0, | |
| y: 0, | |
| targetX: 0, | |
| targetY: 0, | |
| smoothing: 0.1, | |
| shake: { x: 0, y: 0, intensity: 0, duration: 0 }, | |
| zoom: 1, | |
| targetZoom: 1, | |
| bounds: { left: 0, right: 2000, top: 0, bottom: 1000 }, | |
| // Efectos de cámara | |
| effects: { | |
| screenShake: false, | |
| slowMotion: false, | |
| colorFilter: null, | |
| vignette: false | |
| } | |
| }; | |
| // Configuración de niveles procedurales | |
| this.levelConfig = { | |
| currentLevel: null, | |
| levelData: new Map(), | |
| proceduralSeed: Date.now(), | |
| chunkSize: 800, | |
| loadedChunks: new Map(), | |
| activeChunks: new Set(), | |
| // Configuraciones por nivel | |
| levelTypes: { | |
| caminito_matrix: { | |
| theme: 'urban_argentina', | |
| difficulty: 1, | |
| enemies: ['mantero', 'bondi', 'taxi'], | |
| powerUps: ['mate', 'empanada', 'choripan'], | |
| music: 'tango_chiptune', | |
| background: 'la_boca_neon' | |
| }, | |
| obelisco_cyber: { | |
| theme: 'cyberpunk_bsas', | |
| difficulty: 2, | |
| enemies: ['drone_policia', 'hacker', 'cyborg_portero'], | |
| powerUps: ['fernet_digital', 'asado_hologram'], | |
| music: 'cumbia_synthwave', | |
| background: 'obelisco_matrix' | |
| } | |
| } | |
| }; | |
| // Sistema de partículas ultra avanzado | |
| this.particleSystem = { | |
| particles: [], | |
| maxParticles: 500, | |
| pools: { | |
| explosion: [], | |
| trail: [], | |
| sparkle: [], | |
| smoke: [], | |
| blood: [], | |
| energy: [] | |
| }, | |
| // Configuraciones de efectos | |
| effects: { | |
| playerTrail: { enabled: true, intensity: 0.7 }, | |
| impactSparks: { enabled: true, intensity: 1.0 }, | |
| ambientParticles: { enabled: true, intensity: 0.5 }, | |
| weatherEffects: { enabled: true, type: 'none' } | |
| } | |
| }; | |
| // Sistema de audio ultra avanzado | |
| this.audioSystem = { | |
| context: null, | |
| masterVolume: 0.7, | |
| sfxVolume: 0.8, | |
| musicVolume: 0.6, | |
| // Canales de audio | |
| channels: { | |
| master: null, | |
| music: null, | |
| sfx: null, | |
| ambient: null, | |
| voice: null | |
| }, | |
| // Biblioteca de sonidos | |
| sounds: new Map(), | |
| currentMusic: null, | |
| musicQueue: [], | |
| // Efectos de audio avanzados | |
| effects: { | |
| reverb: null, | |
| delay: null, | |
| distortion: null, | |
| filter: null | |
| } | |
| }; | |
| // Sistema de IA compañero ultra inteligente | |
| this.aiCompanion = { | |
| personality: { | |
| humor: 0.8, | |
| sarcasm: 0.6, | |
| encouragement: 0.9, | |
| knowledge: 0.95, | |
| patience: 0.7, | |
| argentineness: 1.0 | |
| }, | |
| // Estados emocionales | |
| mood: 'excited', | |
| relationship: 50, // 0-100 | |
| trust: 30, // 0-100 | |
| // Sistema de memoria | |
| memory: { | |
| playerActions: [], | |
| conversations: [], | |
| achievements: [], | |
| preferences: new Map(), | |
| personalData: new Map() | |
| }, | |
| // Sistema de respuestas contextuales | |
| responseSystem: { | |
| contextAnalyzer: null, | |
| emotionEngine: null, | |
| humorGenerator: null, | |
| knowledgeBase: new Map() | |
| }, | |
| // Configuración de chat | |
| chatConfig: { | |
| isActive: false, | |
| autoResponse: true, | |
| responseDelay: 1500, | |
| maxMessageLength: 200, | |
| currentConversation: [] | |
| } | |
| }; | |
| // Sistemas del motor | |
| this.systems = { | |
| input: null, | |
| physics: null, | |
| rendering: null, | |
| audio: null, | |
| ai: null, | |
| particles: null, | |
| entities: null, | |
| levels: null, | |
| save: null, | |
| achievements: null, | |
| analytics: null | |
| }; | |
| // Configuración de rendimiento | |
| this.performance = { | |
| targetFPS: 60, | |
| adaptiveQuality: true, | |
| cullingEnabled: true, | |
| lodEnabled: true, | |
| // Métricas de rendimiento | |
| metrics: { | |
| frameTime: 0, | |
| drawCalls: 0, | |
| particleCount: 0, | |
| entityCount: 0, | |
| memoryUsage: 0 | |
| }, | |
| // Configuraciones de calidad | |
| qualityLevels: { | |
| potato: { particles: 50, effects: false, shadows: false }, | |
| low: { particles: 100, effects: true, shadows: false }, | |
| medium: { particles: 250, effects: true, shadows: true }, | |
| high: { particles: 400, effects: true, shadows: true }, | |
| ultra: { particles: 500, effects: true, shadows: true } | |
| } | |
| }; | |
| // Configuración de red y multijugador (futuro) | |
| this.network = { | |
| isOnline: false, | |
| playerId: null, | |
| sessionId: null, | |
| peers: new Map(), | |
| latency: 0, | |
| // Configuración de sincronización | |
| sync: { | |
| enabled: false, | |
| rate: 20, // Hz | |
| interpolation: true, | |
| prediction: true | |
| } | |
| }; | |
| // Sistema de eventos avanzado | |
| this.eventSystem = { | |
| listeners: new Map(), | |
| queue: [], | |
| processing: false, | |
| // Tipos de eventos | |
| eventTypes: { | |
| PLAYER_MOVE: 'player_move', | |
| PLAYER_JUMP: 'player_jump', | |
| PLAYER_ATTACK: 'player_attack', | |
| ENEMY_SPAWN: 'enemy_spawn', | |
| ITEM_COLLECT: 'item_collect', | |
| LEVEL_COMPLETE: 'level_complete', | |
| ACHIEVEMENT_UNLOCK: 'achievement_unlock', | |
| AI_RESPONSE: 'ai_response' | |
| } | |
| }; | |
| // Inicializar el motor | |
| this.initialize(); | |
| } | |
| // =================================================================== | |
| // MÉTODOS DE INICIALIZACIÓN | |
| // =================================================================== | |
| async initialize() { | |
| console.log('🚀 Inicializando RETRO LEGENDS: Ultimate Engine...'); | |
| try { | |
| // Configurar canvas | |
| this.setupCanvas(); | |
| // Inicializar sistemas principales | |
| await this.initializeSystems(); | |
| // Cargar recursos | |
| await this.loadResources(); | |
| // Configurar nivel inicial | |
| await this.loadInitialLevel(); | |
| // Configurar IA | |
| this.initializeAI(); | |
| // Inicializar sistema de eventos | |
| this.initializeEventSystem(); | |
| // Configurar sistema de guardado | |
| this.initializeSaveSystem(); | |
| // Marcar como inicializado | |
| this.engineState.isInitialized = true; | |
| this.engineState.isRunning = true; | |
| // Iniciar loop principal | |
| this.startMainLoop(); | |
| console.log('✅ RETRO LEGENDS: Ultimate Engine inicializado correctamente'); | |
| // Disparar evento de inicialización | |
| this.dispatchEvent('ENGINE_INITIALIZED', { timestamp: Date.now() }); | |
| } catch (error) { | |
| console.error('❌ Error inicializando el motor:', error); | |
| throw error; | |
| } | |
| } | |
| setupCanvas() { | |
| // Configurar tamaño responsivo | |
| this.resizeCanvas(); | |
| window.addEventListener('resize', () => this.resizeCanvas()); | |
| // Configurar contexto para renderizado pixelado | |
| this.ctx.imageSmoothingEnabled = false; | |
| this.ctx.webkitImageSmoothingEnabled = false; | |
| this.ctx.mozImageSmoothingEnabled = false; | |
| this.ctx.msImageSmoothingEnabled = false; | |
| // Configurar filtros de renderizado | |
| this.ctx.filter = 'contrast(1.1) saturate(1.2) brightness(1.05)'; | |
| console.log('🖥️ Canvas configurado:', this.canvas.width, 'x', this.canvas.height); | |
| } | |
| resizeCanvas() { | |
| const container = this.canvas.parentElement; | |
| const rect = container.getBoundingClientRect(); | |
| // Configurar tamaño manteniendo aspect ratio 16:9 | |
| const aspectRatio = 16 / 9; | |
| let width = rect.width; | |
| let height = rect.height; | |
| if (width / height > aspectRatio) { | |
| width = height * aspectRatio; | |
| } else { | |
| height = width / aspectRatio; | |
| } | |
| this.canvas.width = width; | |
| this.canvas.height = height; | |
| // Actualizar bounds de cámara | |
| this.camera.bounds.right = width * 2; | |
| this.camera.bounds.bottom = height * 1.5; | |
| } | |
| async initializeSystems() { | |
| console.log('🔧 Inicializando sistemas del motor...'); | |
| // Sistema de input avanzado | |
| this.systems.input = new AdvancedInputHandler(this); | |
| // Sistema de físicas | |
| this.systems.physics = new AdvancedPhysicsEngine(this); | |
| // Sistema de renderizado | |
| this.systems.rendering = new AdvancedRenderingSystem(this); | |
| // Sistema de audio | |
| this.systems.audio = new UltraAudioSystem(this); | |
| await this.systems.audio.initialize(); | |
| // Sistema de partículas | |
| this.systems.particles = new AdvancedParticleSystem(this); | |
| // Sistema de entidades | |
| this.systems.entities = new UltraEntityManager(this); | |
| // Sistema de niveles | |
| this.systems.levels = new ProceduralLevelGenerator(this); | |
| // Sistema de IA | |
| this.systems.ai = new AdvancedAICompanion(this); | |
| // Sistema de guardado | |
| this.systems.save = new AdvancedSaveSystem(this); | |
| // Sistema de logros | |
| this.systems.achievements = new AchievementSystem(this); | |
| // Sistema de analíticas | |
| this.systems.analytics = new GameAnalytics(this); | |
| console.log('✅ Todos los sistemas inicializados'); | |
| } | |
| async loadResources() { | |
| console.log('📦 Cargando recursos del juego...'); | |
| // Cargar sprites y texturas | |
| await this.loadSprites(); | |
| // Cargar sonidos | |
| await this.loadAudio(); | |
| // Cargar datos de niveles | |
| await this.loadLevelData(); | |
| // Cargar configuraciones de IA | |
| await this.loadAIData(); | |
| console.log('✅ Recursos cargados correctamente'); | |
| } | |
| async loadSprites() { | |
| // Aquí cargaríamos sprites reales en una implementación completa | |
| console.log('🎨 Sprites cargados (modo texto para demo)'); | |
| } | |
| async loadAudio() { | |
| // Inicializar contexto de audio | |
| if (!this.audioSystem.context) { | |
| this.audioSystem.context = new (window.AudioContext || window.webkitAudioContext)(); | |
| } | |
| console.log('🎵 Sistema de audio inicializado'); | |
| } | |
| async loadLevelData() { | |
| // Cargar configuraciones de niveles | |
| this.levelConfig.levelData.set('caminito_matrix', { | |
| name: 'Caminito Matrix', | |
| theme: 'cyberpunk_argentina', | |
| size: { width: 2000, height: 800 }, | |
| spawnPoints: [{ x: 100, y: 400 }], | |
| enemies: ['mantero_cyber', 'bondi_hologram', 'taxi_drone'], | |
| collectibles: ['mate_digital', 'empanada_power', 'choripan_shield'] | |
| }); | |
| console.log('🗺️ Datos de niveles cargados'); | |
| } | |
| async loadAIData() { | |
| // Cargar base de conocimiento de la IA | |
| this.aiCompanion.responseSystem.knowledgeBase.set('argentina', { | |
| cultura: ['mate', 'asado', 'tango', 'futbol', 'Maradona'], | |
| lugares: ['Buenos Aires', 'Córdoba', 'Mendoza', 'Bariloche'], | |
| comidas: ['empanadas', 'choripan', 'milanesas', 'dulce de leche'], | |
| expresiones: ['che', 'boludo', 'dale', 'bárbaro', 'copado'] | |
| }); | |
| console.log('🤖 Base de conocimiento de IA cargada'); | |
| } | |
| async loadInitialLevel() { | |
| console.log('🏗️ Cargando nivel inicial...'); | |
| // Cargar Caminito Matrix | |
| await this.systems.levels.loadLevel('caminito_matrix'); | |
| // Posicionar jugador | |
| this.player.x = 150; | |
| this.player.y = 400; | |
| // Configurar cámara | |
| this.updateCamera(); | |
| console.log('✅ Nivel inicial cargado'); | |
| } | |
| initializeAI() { | |
| console.log('🧠 Inicializando IA compañero...'); | |
| // Configurar personalidad inicial | |
| this.aiCompanion.mood = 'excited'; | |
| this.aiCompanion.relationship = 50; | |
| // Mensaje de bienvenida | |
| setTimeout(() => { | |
| this.systems.ai.sendMessage( | |
| "¡Ehhhh, pibe! ¡Bienvenido a la experiencia más nostálgica y avanzada de tu vida! " + | |
| "Soy NEXUS, tu compañero argento con IA de última generación. " + | |
| "¿Listos para romperla en este universo retro-futurista? ¡Dale que arrancamos! 🚀🇦🇷" | |
| ); | |
| }, 2000); | |
| console.log('✅ IA compañero inicializada'); | |
| } | |
| initializeEventSystem() { | |
| console.log('📡 Inicializando sistema de eventos...'); | |
| // Configurar listeners principales | |
| this.addEventListener('PLAYER_MOVE', (data) => { | |
| this.systems.ai.onPlayerMove(data); | |
| this.systems.analytics.trackEvent('player_movement', data); | |
| }); | |
| this.addEventListener('PLAYER_JUMP', (data) => { | |
| this.systems.ai.onPlayerJump(data); | |
| this.systems.particles.createJumpEffect(data.x, data.y); | |
| }); | |
| this.addEventListener('ITEM_COLLECT', (data) => { | |
| this.systems.ai.onItemCollect(data); | |
| this.updateScore(data.points); | |
| }); | |
| console.log('✅ Sistema de eventos configurado'); | |
| } | |
| initializeSaveSystem() { | |
| console.log('💾 Inicializando sistema de guardado...'); | |
| // Cargar datos guardados si existen | |
| this.systems.save.loadGame(); | |
| // Configurar auto-guardado cada 30 segundos | |
| setInterval(() => { | |
| this.systems.save.autoSave(); | |
| }, 30000); | |
| console.log('✅ Sistema de guardado configurado'); | |
| } | |
| // =================================================================== | |
| // LOOP PRINCIPAL DEL JUEGO | |
| // =================================================================== | |
| startMainLoop() { | |
| console.log('🔄 Iniciando loop principal del juego...'); | |
| this.engineState.lastFrameTime = performance.now(); | |
| this.mainLoop(); | |
| } | |
| mainLoop() { | |
| if (!this.engineState.isRunning) return; | |
| // Calcular delta time | |
| const currentTime = performance.now(); | |
| this.engineState.deltaTime = currentTime - this.engineState.lastFrameTime; | |
| this.engineState.lastFrameTime = currentTime; | |
| // Actualizar FPS | |
| this.updateFPS(); | |
| // Verificar rendimiento y ajustar calidad si es necesario | |
| this.checkPerformance(); | |
| // Actualizar solo si no está pausado | |
| if (!this.engineState.isPaused) { | |
| this.update(this.engineState.deltaTime); | |
| } | |
| // Renderizar siempre | |
| this.render(); | |
| // Continuar el loop | |
| requestAnimationFrame(() => this.mainLoop()); | |
| } | |
| update(deltaTime) { | |
| // Actualizar tiempo de juego | |
| this.gameState.time += deltaTime; | |
| // Actualizar sistemas principales | |
| this.updatePlayer(deltaTime); | |
| this.updateCamera(deltaTime); | |
| this.updatePhysics(deltaTime); | |
| // Actualizar sistemas secundarios | |
| this.systems.entities.update(deltaTime); | |
| this.systems.particles.update(deltaTime); | |
| this.systems.levels.update(deltaTime); | |
| this.systems.ai.update(deltaTime); | |
| // Procesar eventos | |
| this.processEvents(); | |
| // Verificar colisiones | |
| this.checkCollisions(); | |
| // Actualizar UI | |
| this.updateUI(); | |
| // Verificar condiciones de juego | |
| this.checkGameConditions(); | |
| // Actualizar analíticas | |
| this.systems.analytics.update(deltaTime); | |
| } | |
| updatePlayer(deltaTime) { | |
| const player = this.player; | |
| // Actualizar timers | |
| this.updatePlayerTimers(deltaTime); | |
| // Procesar input del jugador | |
| this.systems.input.processPlayerInput(deltaTime); | |
| // Aplicar física del jugador | |
| this.applyPlayerPhysics(deltaTime); | |
| // Actualizar animaciones | |
| this.updatePlayerAnimation(deltaTime); | |
| // Actualizar centro del jugador | |
| player.centerX = player.x + player.width / 2; | |
| player.centerY = player.y + player.height / 2; | |
| // Verificar límites del mundo | |
| this.checkWorldBounds(); | |
| // Actualizar sistema de combos | |
| this.updateComboSystem(deltaTime); | |
| } | |
| updatePlayerTimers(deltaTime) { | |
| const player = this.player; | |
| // Timer de invulnerabilidad | |
| if (player.invulnerabilityTimer > 0) { | |
| player.invulnerabilityTimer -= deltaTime; | |
| if (player.invulnerabilityTimer <= 0) { | |
| player.isInvulnerable = false; | |
| } | |
| } | |
| // Cooldown de dash | |
| if (player.dashCooldown > 0) { | |
| player.dashCooldown -= deltaTime; | |
| if (player.dashCooldown <= 0) { | |
| player.canDash = true; | |
| } | |
| } | |
| // Cooldown de ataque | |
| if (player.attackCooldown > 0) { | |
| player.attackCooldown -= deltaTime; | |
| if (player.attackCooldown <= 0) { | |
| player.isAttacking = false; | |
| } | |
| } | |
| // Timer de combo | |
| if (player.comboSystem.comboTimer > 0) { | |
| player.comboSystem.comboTimer -= deltaTime; | |
| if (player.comboSystem.comboTimer <= 0) { | |
| player.comboSystem.currentCombo = []; | |
| } | |
| } | |
| } | |
| applyPlayerPhysics(deltaTime) { | |
| const player = this.player; | |
| const physics = this.physics; | |
| // Aplicar gravedad si no está en el suelo | |
| if (!player.grounded) { | |
| player.velocityY += physics.gravity; | |
| if (player.velocityY > physics.terminalVelocity) { | |
| player.velocityY = physics.terminalVelocity; | |
| } | |
| } | |
| // Aplicar fricción/resistencia | |
| if (player.grounded) { | |
| player.velocityX *= physics.groundFriction; | |
| } else { | |
| player.velocityX *= physics.airResistance; | |
| } | |
| // Aplicar velocidades | |
| player.x += player.velocityX; | |
| player.y += player.velocityY; | |
| // Verificar colisiones con terreno | |
| this.checkTerrainCollisions(); | |
| } | |
| updatePlayerAnimation(deltaTime) { | |
| const player = this.player; | |
| // Actualizar timer de animación | |
| player.lastAnimationTime += deltaTime; | |
| if (player.lastAnimationTime >= player.animationSpeed) { | |
| player.lastAnimationTime = 0; | |
| // Determinar sprite actual basado en estado | |
| let spriteSet; | |
| if (player.isDashing) { | |
| spriteSet = player.spriteSet.dashing; | |
| } else if (player.isAttacking) { | |
| spriteSet = player.spriteSet.attacking; | |
| } else if (player.isCharging) { | |
| spriteSet = player.spriteSet.charging; | |
| } else if (!player.grounded) { | |
| spriteSet = player.spriteSet.jumping; | |
| } else if (Math.abs(player.velocityX) > 0.5) { | |
| spriteSet = player.spriteSet.running; | |
| } else { | |
| spriteSet = player.spriteSet.idle; | |
| } | |
| // Avanzar frame de animación | |
| player.animationFrame = (player.animationFrame + 1) % spriteSet.length; | |
| player.currentSprite = spriteSet[player.animationFrame]; | |
| } | |
| } | |
| updateCamera(deltaTime) { | |
| const camera = this.camera; | |
| const player = this.player; | |
| // Calcular posición objetivo de la cámara | |
| camera.targetX = player.centerX - this.canvas.width / 2; | |
| camera.targetY = player.centerY - this.canvas.height / 2; | |
| // Aplicar límites de cámara | |
| camera.targetX = Math.max(camera.bounds.left, | |
| Math.min(camera.bounds.right - this.canvas.width, camera.targetX)); | |
| camera.targetY = Math.max(camera.bounds.top, | |
| Math.min(camera.bounds.bottom - this.canvas.height, camera.targetY)); | |
| // Interpolar suavemente hacia la posición objetivo | |
| camera.x += (camera.targetX - camera.x) * camera.smoothing; | |
| camera.y += (camera.targetY - camera.y) * camera.smoothing; | |
| // Aplicar shake de cámara si está activo | |
| if (camera.shake.duration > 0) { | |
| camera.shake.duration -= deltaTime; | |
| const intensity = camera.shake.intensity * (camera.shake.duration / 1000); | |
| camera.shake.x = (Math.random() - 0.5) * intensity; | |
| camera.shake.y = (Math.random() - 0.5) * intensity; | |
| } else { | |
| camera.shake.x = 0; | |
| camera.shake.y = 0; | |
| } | |
| // Aplicar zoom suave | |
| camera.zoom += (camera.targetZoom - camera.zoom) * 0.1; | |
| } | |
| updatePhysics(deltaTime) { | |
| // Actualizar física del mundo | |
| this.systems.physics.update(deltaTime); | |
| } | |
| checkCollisions() { | |
| // Verificar colisiones jugador-entidades | |
| const playerRect = { | |
| x: this.player.x, | |
| y: this.player.y, | |
| width: this.player.width, | |
| height: this.player.height | |
| }; | |
| const entities = this.systems.entities.getActiveEntities(); | |
| entities.forEach((entity, index) => { | |
| if (this.isColliding(playerRect, entity.getBounds())) { | |
| this.handleCollision(entity, index); | |
| } | |
| }); | |
| } | |
| isColliding(rect1, rect2) { | |
| return rect1.x < rect2.x + rect2.width && | |
| rect1.x + rect1.width > rect2.x && | |
| rect1.y < rect2.y + rect2.height && | |
| rect1.y + rect1.height > rect2.y; | |
| } | |
| handleCollision(entity, index) { | |
| if (this.player.isInvulnerable) return; | |
| switch(entity.type) { | |
| case 'enemy': | |
| this.playerHit(entity); | |
| break; | |
| case 'collectible': | |
| this.collectItem(entity, index); | |
| break; | |
| case 'powerup': | |
| this.collectPowerUp(entity, index); | |
| break; | |
| case 'checkpoint': | |
| this.activateCheckpoint(entity); | |
| break; | |
| } | |
| } | |
| playerHit(enemy) { | |
| // Reducir vida | |
| this.gameState.health -= enemy.damage || 25; | |
| // Activar invulnerabilidad | |
| this.player.isInvulnerable = true; | |
| this.player.invulnerabilityTimer = 2000; | |
| // Aplicar knockback | |
| const knockbackDirection = this.player.x < enemy.x ? -1 : 1; | |
| this.player.velocityX = knockbackDirection * 10; | |
| this.player.velocityY = -8; | |
| // Efectos visuales y sonoros | |
| this.systems.particles.createHitEffect(this.player.centerX, this.player.centerY); | |
| this.systems.audio.playSound('player_hit'); | |
| this.shakeCamera(300, 10); | |
| // Reacción de la IA | |
| this.systems.ai.onPlayerHit(enemy.type); | |
| // Disparar evento | |
| this.dispatchEvent('PLAYER_HIT', { | |
| enemy: enemy.type, | |
| damage: enemy.damage, | |
| health: this.gameState.health | |
| }); | |
| // Verificar muerte | |
| if (this.gameState.health <= 0) { | |
| this.playerDeath(); | |
| } | |
| } | |
| collectItem(item, index) { | |
| // Añadir puntos | |
| this.gameState.score += item.points || 100; | |
| // Procesar tipo de item | |
| switch(item.subtype) { | |
| case 'mate': | |
| this.gameState.mateCount++; | |
| this.systems.ai.onMateCollected(); | |
| break; | |
| case 'empanada': | |
| this.gameState.health = Math.min(this.gameState.maxHealth, this.gameState.health + 25); | |
| break; | |
| case 'choripan': | |
| this.gameState.energy = Math.min(this.gameState.maxEnergy, this.gameState.energy + 50); | |
| break; | |
| } | |
| // Efectos | |
| this.systems.particles.createCollectEffect(item.x, item.y, item.color); | |
| this.systems.audio.playSound('item_collect'); | |
| // Remover item | |
| this.systems.entities.removeEntity(index); | |
| // Disparar evento | |
| this.dispatchEvent('ITEM_COLLECT', { | |
| type: item.subtype, | |
| points: item.points, | |
| position: { x: item.x, y: item.y } | |
| }); | |
| } | |
| updateComboSystem(deltaTime) { | |
| const comboSystem = this.player.comboSystem; | |
| // Verificar si hay combo activo | |
| if (comboSystem.currentCombo.length > 0) { | |
| // Verificar si se completó algún combo | |
| comboSystem.availableCombos.forEach(combo => { | |
| if (this.arraysEqual(comboSystem.currentCombo, combo.sequence)) { | |
| this.executeCombo(combo); | |
| comboSystem.currentCombo = []; | |
| comboSystem.comboTimer = 0; | |
| } | |
| }); | |
| } | |
| } | |
| executeCombo(combo) { | |
| console.log(`🔥 Combo ejecutado: ${combo.name} (${combo.power} poder)`); | |
| // Aplicar efectos del combo | |
| this.gameState.score += combo.power; | |
| this.gameState.combo++; | |
| // Efectos visuales espectaculares | |
| this.systems.particles.createComboEffect(this.player.centerX, this.player.centerY, combo.name); | |
| this.systems.audio.playSound('combo_success'); | |
| this.shakeCamera(500, 15); | |
| // Reacción de la IA | |
| this.systems.ai.onComboExecuted(combo); | |
| // Disparar evento | |
| this.dispatchEvent('COMBO_EXECUTED', { combo: combo.name, power: combo.power }); | |
| } | |
| // =================================================================== | |
| // MÉTODOS DE RENDERIZADO | |
| // =================================================================== | |
| render() { | |
| // Limpiar canvas | |
| this.clearCanvas(); | |
| // Aplicar transformaciones de cámara | |
| this.ctx.save(); | |
| this.applyCamera(); | |
| // Renderizar fondo | |
| this.renderBackground(); | |
| // Renderizar nivel | |
| this.systems.levels.render(this.ctx); | |
| // Renderizar entidades | |
| this.systems.entities.render(this.ctx); | |
| // Renderizar jugador | |
| this.renderPlayer(); | |
| // Renderizar partículas | |
| this.systems.particles.render(this.ctx); | |
| // Renderizar efectos de primer plano | |
| this.renderForegroundEffects(); | |
| // Restaurar transformaciones | |
| this.ctx.restore(); | |
| // Renderizar UI (sin transformaciones de cámara) | |
| this.renderUI(); | |
| // Renderizar debug si está habilitado | |
| if (this.engineState.debugMode) { | |
| this.renderDebugInfo(); | |
| } | |
| // Actualizar métricas de rendimiento | |
| this.performance.metrics.drawCalls++; | |
| } | |
| clearCanvas() { | |
| // Fondo degradado dinámico | |
| const gradient = this.ctx.createLinearGradient(0, 0, this.canvas.width, this.canvas.height); | |
| gradient.addColorStop(0, '#0a0a0a'); | |
| gradient.addColorStop(0.5, '#1a1a2e'); | |
| gradient.addColorStop(1, '#16213e'); | |
| this.ctx.fillStyle = gradient; | |
| this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height); | |
| } | |
| applyCamera() { | |
| // Aplicar zoom | |
| this.ctx.scale(this.camera.zoom, this.camera.zoom); | |
| // Aplicar posición de cámara con shake | |
| this.ctx.translate( | |
| -this.camera.x + this.camera.shake.x, | |
| -this.camera.y + this.camera.shake.y | |
| ); | |
| } | |
| renderBackground() { | |
| // Renderizar fondo parallax del nivel actual | |
| this.systems.levels.renderBackground(this.ctx, this.camera); | |
| } | |
| renderPlayer() { | |
| const player = this.player; | |
| // Efecto de parpadeo si es invulnerable | |
| if (player.isInvulnerable && Math.floor(this.gameState.time / 100) % 2) { | |
| this.ctx.globalAlpha = 0.5; | |
| } | |
| // Configurar renderizado de texto | |
| this.ctx.font = '32px Arial'; | |
| this.ctx.textAlign = 'center'; | |
| this.ctx.textBaseline = 'bottom'; | |
| // Renderizar sprite del jugador | |
| this.ctx.fillStyle = '#ffffff'; | |
| this.ctx.fillText( | |
| player.currentSprite, | |
| player.centerX, | |
| player.y + player.height | |
| ); | |
| // Efectos especiales según estado | |
| if (player.isDashing) { | |
| this.renderDashEffect(); | |
| } | |
| if (player.isCharging) { | |
| this.renderChargeEffect(); | |
| } | |
| // Restaurar alpha | |
| this.ctx.globalAlpha = 1.0; | |
| // Renderizar barra de vida si está dañado | |
| if (this.gameState.health < this.gameState.maxHealth) { | |
| this.renderPlayerHealthBar(); | |
| } | |
| } | |
| renderDashEffect() { | |
| const player = this.player; | |
| // Crear trail de dash | |
| this.ctx.strokeStyle = '#00ffff'; | |
| this.ctx.lineWidth = 3; | |
| this.ctx.globalAlpha = 0.7; | |
| this.ctx.beginPath(); | |
| this.ctx.moveTo(player.x - 20, player.centerY); | |
| this.ctx.lineTo(player.x + player.width + 20, player.centerY); | |
| this.ctx.stroke(); | |
| this.ctx.globalAlpha = 1.0; | |
| } | |
| renderChargeEffect() { | |
| const player = this.player; | |
| const time = this.gameState.time; | |
| // Efecto de energía cargándose | |
| this.ctx.strokeStyle = '#ffff00'; | |
| this.ctx.lineWidth = 2; | |
| this.ctx.globalAlpha = 0.8; | |
| const radius = 40 + Math.sin(time * 0.01) * 10; | |
| this.ctx.beginPath(); | |
| this.ctx.arc(player.centerX, player.centerY, radius, 0, Math.PI * 2); | |
| this.ctx.stroke(); | |
| this.ctx.globalAlpha = 1.0; | |
| } | |
| renderPlayerHealthBar() { | |
| const player = this.player; | |
| const barWidth = 40; | |
| const barHeight = 6; | |
| const x = player.centerX - barWidth / 2; | |
| const y = player.y - 15; | |
| // Fondo de la barra | |
| this.ctx.fillStyle = '#ff0000'; | |
| this.ctx.fillRect(x, y, barWidth, barHeight); | |
| // Barra de vida | |
| const healthPercent = this.gameState.health / this.gameState.maxHealth; | |
| this.ctx.fillStyle = '#00ff00'; | |
| this.ctx.fillRect(x, y, barWidth * healthPercent, barHeight); | |
| // Borde | |
| this.ctx.strokeStyle = '#ffffff'; | |
| this.ctx.lineWidth = 1; | |
| this.ctx.strokeRect(x, y, barWidth, barHeight); | |
| } | |
| renderForegroundEffects() { | |
| // Efectos de primer plano como lluvia, nieve, etc. | |
| if (this.particleSystem.effects.weatherEffects.enabled) { | |
| this.renderWeatherEffects(); | |
| } | |
| // Efectos de pantalla como vignette | |
| if (this.camera.effects.vignette) { | |
| this.renderVignette(); | |
| } | |
| } | |
| renderWeatherEffects() { | |
| // Implementar efectos climáticos | |
| } | |
| renderVignette() { | |
| const gradient = this.ctx.createRadialGradient( | |
| this.canvas.width / 2, this.canvas.height / 2, 0, | |
| this.canvas.width / 2, this.canvas.height / 2, this.canvas.width / 2 | |
| ); | |
| gradient.addColorStop(0, 'rgba(0,0,0,0)'); | |
| gradient.addColorStop(1, 'rgba(0,0,0,0.5)'); | |
| this.ctx.fillStyle = gradient; | |
| this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height); | |
| } | |
| renderUI() { | |
| // La UI se renderiza en el HTML, pero aquí podríamos añadir overlays | |
| } | |
| renderDebugInfo() { | |
| this.ctx.fillStyle = '#00ff00'; | |
| this.ctx.font = '12px monospace'; | |
| this.ctx.textAlign = 'left'; | |
| const debugInfo = [ | |
| `FPS: ${this.engineState.averageFPS.toFixed(1)}`, | |
| `Delta: ${this.engineState.deltaTime.toFixed(2)}ms`, | |
| `Player: (${this.player.x.toFixed(0)}, ${this.player.y.toFixed(0)})`, | |
| `Velocity: (${this.player.velocityX.toFixed(2)}, ${this.player.velocityY.toFixed(2)})`, | |
| `Camera: (${this.camera.x.toFixed(0)}, ${this.camera.y.toFixed(0)})`, | |
| `Entities: ${this.systems.entities.getEntityCount()}`, | |
| `Particles: ${this.systems.particles.getParticleCount()}` | |
| ]; | |
| debugInfo.forEach((line, index) => { | |
| this.ctx.fillText(line, 10, 20 + index * 15); | |
| }); | |
| } | |
| // =================================================================== | |
| // MÉTODOS AUXILIARES | |
| // =================================================================== | |
| updateFPS() { | |
| this.engineState.frameCount++; | |
| if (this.engineState.frameCount % 60 === 0) { | |
| this.engineState.averageFPS = 1000 / this.engineState.deltaTime; | |
| } | |
| } | |
| checkPerformance() { | |
| // Ajustar calidad automáticamente si el rendimiento es bajo | |
| if (this.performance.adaptiveQuality && this.engineState.averageFPS < 45) { | |
| this.adjustQuality('down'); | |
| } else if (this.performance.adaptiveQuality && this.engineState.averageFPS > 55) { | |
| this.adjustQuality('up'); | |
| } | |
| } | |
| adjustQuality(direction) { | |
| // Implementar ajuste automático de calidad | |
| console.log(`🔧 Ajustando calidad: ${direction}`); | |
| } | |
| processEvents() { | |
| while (this.eventSystem.queue.length > 0) { | |
| const event = this.eventSystem.queue.shift(); | |
| this.handleEvent(event); | |
| } | |
| } | |
| handleEvent(event) { | |
| const listeners = this.eventSystem.listeners.get(event.type); | |
| if (listeners) { | |
| listeners.forEach(listener => listener(event.data)); | |
| } | |
| } | |
| addEventListener(type, listener) { | |
| if (!this.eventSystem.listeners.has(type)) { | |
| this.eventSystem.listeners.set(type, []); | |
| } | |
| this.eventSystem.listeners.get(type).push(listener); | |
| } | |
| dispatchEvent(type, data) { | |
| this.eventSystem.queue.push({ type, data, timestamp: Date.now() }); | |
| } | |
| shakeCamera(duration, intensity) { | |
| this.camera.shake.duration = duration; | |
| this.camera.shake.intensity = intensity; | |
| } | |
| updateUI() { | |
| // Actualizar elementos de UI | |
| document.getElementById('gameScore').textContent = this.gameState.score.toLocaleString(); | |
| document.getElementById('playerHealth').textContent = Math.max(0, this.gameState.health); | |
| document.getElementById('mateCount').textContent = this.gameState.mateCount; | |
| document.getElementById('currentLevel').textContent = this.levelConfig.currentLevel; | |
| document.getElementById('comboCount').textContent = this.gameState.combo; | |
| } | |
| checkGameConditions() { | |
| // Verificar condiciones de victoria/derrota | |
| if (this.gameState.health <= 0 && this.gameState.lives <= 0) { | |
| this.gameOver(); | |
| } | |
| // Verificar completado de nivel | |
| if (this.systems.levels.isLevelComplete()) { | |
| this.completeLevel(); | |
| } | |
| } | |
| checkWorldBounds() { | |
| const player = this.player; | |
| // Límites horizontales | |
| if (player.x < 0) { | |
| player.x = 0; | |
| player.velocityX = 0; | |
| } | |
| // Límite inferior (muerte por caída) | |
| if (player.y > this.canvas.height + 200) { | |
| this.playerDeath('fall'); | |
| } | |
| } | |
| checkTerrainCollisions() { | |
| const player = this.player; | |
| const groundY = this.canvas.height - 80; // Altura del suelo base | |
| // Colisión con el suelo | |
| if (player.y + player.height >= groundY) { | |
| player.y = groundY - player.height; | |
| player.velocityY = 0; | |
| if (!player.grounded) { | |
| player.grounded = true; | |
| player.canDoubleJump = true; | |
| this.systems.particles.createLandingEffect(player.centerX, player.y + player.height); | |
| this.systems.ai.onPlayerLanding(); | |
| } | |
| } else { | |
| player.grounded = false; | |
| } | |
| } | |
| playerDeath(cause = 'unknown') { | |
| console.log(`💀 Jugador murió por: ${cause}`); | |
| // Reducir vidas | |
| this.gameState.lives--; | |
| // Efectos de muerte | |
| this.systems.particles.createDeathEffect(this.player.centerX, this.player.centerY); | |
| this.systems.audio.playSound('player_death'); | |
| this.shakeCamera(1000, 20); | |
| // Reacción de la IA | |
| this.systems.ai.onPlayerDeath(cause); | |
| // Resetear jugador | |
| this.respawnPlayer(); | |
| // Disparar evento | |
| this.dispatchEvent('PLAYER_DEATH', { cause, lives: this.gameState.lives }); | |
| } | |
| respawnPlayer() { | |
| // Resetear posición y estado del jugador | |
| this.player.x = 150; | |
| this.player.y = 400; | |
| this.player.velocityX = 0; | |
| this.player.velocityY = 0; | |
| this.gameState.health = this.gameState.maxHealth; | |
| this.player.isInvulnerable = true; | |
| this.player.invulnerabilityTimer = 3000; | |
| console.log('🔄 Jugador respawneado'); | |
| } | |
| gameOver() { | |
| this.engineState.isRunning = false; | |
| console.log('💀 GAME OVER'); | |
| // Mostrar pantalla de game over | |
| this.systems.ai.onGameOver(); | |
| // Guardar estadísticas finales | |
| this.systems.save.saveGameOverStats(); | |
| } | |
| completeLevel() { | |
| console.log('🏆 Nivel completado'); | |
| // Bonificación por completar nivel | |
| this.gameState.score += 5000; | |
| // Reacción de la IA | |
| this.systems.ai.onLevelComplete(); | |
| // Cargar siguiente nivel | |
| this.loadNextLevel(); | |
| } | |
| loadNextLevel() { | |
| // Implementar carga del siguiente nivel | |
| console.log('🗺️ Cargando siguiente nivel...'); | |
| } | |
| arraysEqual(a, b) { | |
| return a.length === b.length && a.every((val, i) => val === b[i]); | |
| } | |
| // =================================================================== | |
| // MÉTODOS PÚBLICOS PARA CONTROLES | |
| // =================================================================== | |
| jump() { | |
| const player = this.player; | |
| if (player.grounded) { | |
| player.velocityY = -player.jumpPower; | |
| player.grounded = false; | |
| this.systems.audio.playSound('jump'); | |
| this.systems.particles.createJumpEffect(player.centerX, player.y + player.height); | |
| this.dispatchEvent('PLAYER_JUMP', { x: player.centerX, y: player.centerY }); | |
| } else if (player.canDoubleJump) { | |
| player.velocityY = -player.doubleJumpPower; | |
| player.canDoubleJump = false; | |
| this.systems.audio.playSound('double_jump'); | |
| this.systems.particles.createDoubleJumpEffect(player.centerX, player.centerY); | |
| } | |
| } | |
| dash() { | |
| const player = this.player; | |
| if (player.canDash && !player.isDashing) { | |
| const direction = player.facing === 'right' ? 1 : -1; | |
| player.velocityX = direction * player.dashPower; | |
| player.isDashing = true; | |
| player.canDash = false; | |
| player.dashCooldown = 1000; | |
| this.systems.audio.playSound('dash'); | |
| this.systems.particles.createDashEffect(player.centerX, player.centerY, direction); | |
| // El dash dura 200ms | |
| setTimeout(() => { | |
| player.isDashing = false; | |
| }, 200); | |
| } | |
| } | |
| attack() { | |
| const player = this.player; | |
| if (!player.isAttacking && player.attackCooldown <= 0) { | |
| player.isAttacking = true; | |
| player.attackCooldown = 500; | |
| this.systems.audio.playSound('attack'); | |
| this.systems.particles.createAttackEffect(player.centerX, player.centerY, player.facing); | |
| // Verificar enemigos en rango | |
| this.checkAttackHits(); | |
| } | |
| } | |
| checkAttackHits() { | |
| const player = this.player; | |
| const attackRange = 50; | |
| const attackRect = { | |
| x: player.facing === 'right' ? player.x + player.width : player.x - attackRange, | |
| y: player.y, | |
| width: attackRange, | |
| height: player.height | |
| }; | |
| const entities = this.systems.entities.getActiveEntities(); | |
| entities.forEach((entity, index) => { | |
| if (entity.type === 'enemy' && this.isColliding(attackRect, entity.getBounds())) { | |
| this.hitEnemy(entity, index); | |
| } | |
| }); | |
| } | |
| hitEnemy(enemy, index) { | |
| // Dañar enemigo | |
| enemy.takeDamage(player.stats.strength); | |
| // Efectos | |
| this.systems.particles.createEnemyHitEffect(enemy.x, enemy.y); | |
| this.systems.audio.playSound('enemy_hit'); | |
| // Puntos | |
| this.gameState.score += 50; | |
| // Si el enemigo muere | |
| if (enemy.health <= 0) { | |
| this.systems.entities.removeEntity(index); | |
| this.gameState.score += enemy.killScore || 100; | |
| this.systems.particles.createEnemyDeathEffect(enemy.x, enemy.y); | |
| } | |
| } | |
| useMate() { | |
| if (this.gameState.mateCount > 0) { | |
| this.gameState.mateCount--; | |
| this.gameState.health = Math.min(this.gameState.maxHealth, this.gameState.health + 50); | |
| this.gameState.energy = this.gameState.maxEnergy; | |
| this.systems.audio.playSound('mate_use'); | |
| this.systems.particles.createMateEffect(this.player.centerX, this.player.centerY); | |
| this.systems.ai.onMateUsed(); | |
| console.log('🧉 Mate usado - Vida y energía restauradas'); | |
| } | |
| } | |
| togglePause() { | |
| this.engineState.isPaused = !this.engineState.isPaused; | |
| if (this.engineState.isPaused) { | |
| this.systems.audio.pauseAll(); | |
| this.systems.ai.onGamePaused(); | |
| } else { | |
| this.systems.audio.resumeAll(); | |
| this.systems.ai.onGameResumed(); | |
| } | |
| console.log(`⏸️ Juego ${this.engineState.isPaused ? 'pausado' : 'reanudado'}`); | |
| } | |
| toggleDebug() { | |
| this.engineState.debugMode = !this.engineState.debugMode; | |
| console.log(`🐛 Modo debug: ${this.engineState.debugMode ? 'ON' : 'OFF'}`); | |
| } | |
| // =================================================================== | |
| // GETTERS PÚBLICOS | |
| // =================================================================== | |
| getPlayer() { return this.player; } | |
| getGameState() { return this.gameState; } | |
| getCamera() { return this.camera; } | |
| getCanvas() { return this.canvas; } | |
| getContext() { return this.ctx; } | |
| getSystems() { return this.systems; } | |
| getPerformanceMetrics() { return this.performance.metrics; } | |
| } | |
| // =================================================================== | |
| // EXPORTAR PARA USO GLOBAL | |
| // =================================================================== | |
| window.RetroLegendsUltimateEngine = RetroLegendsUltimateEngine; | |
| console.log('🚀 Core Engine Ultra cargado correctamente'); | |