User prompt
paso 3
User prompt
haz el paso 2
User prompt
paso 3
User prompt
haz el paso 2
User prompt
paso 1
User prompt
cuando toco las zonas 2 y 3 el personaje no se mueve y las 3 zonas se quedan en rojo sabrias decirme porque pasa esto?
User prompt
pon 1 2 y 3 mas arriba
User prompt
haz que 2 y 3 esten mas arriba que las cartas
User prompt
haz que la zona 2 y 3 esten en las esquinas de la pantalla
User prompt
ahora separa las zonas de movimiento
User prompt
haz que cada lugar de movimiento tenga un numero para identificarlas y pon una en el medio una a la derecha y la otra a la izquierda
User prompt
implementa las soluciones
User prompt
Please fix the bug: 'ReferenceError: updateManaDisplay is not defined' in or related to this line: 'updateManaDisplay();' Line Number: 1834
Code edit (2 edits merged)
Please save this source code
User prompt
Please fix the bug: 'Uncaught ReferenceError: updateManaDisplay is not defined' in or related to this line: 'updateManaDisplay();' Line Number: 1824
Code edit (1 edits merged)
Please save this source code
User prompt
Please fix the bug: 'currentMana is not defined' in or related to this line: 'var originalMana = currentMana;' Line Number: 5691
Code edit (1 edits merged)
Please save this source code
User prompt
Please fix the bug: 'currentMana is not defined' in or related to this line: 'console.log(' - currentMana:', currentMana, '(should be 9999)');' Line Number: 5673
User prompt
Please fix the bug: 'maxMana is not defined' in or related to this line: 'console.log('maxMana:', maxMana);' Line Number: 3868
User prompt
Please fix the bug: 'currentMana is not defined' in or related to this line: 'console.log('currentMana:', currentMana);' Line Number: 3867
User prompt
Please fix the bug: 'currentMana is not defined' in or related to this line: 'console.log('currentMana:', currentMana);' Line Number: 3867
User prompt
Please fix the bug: 'currentMana is not defined' in or related to this line: 'console.log('currentMana:', currentMana);' Line Number: 3867
User prompt
Please fix the bug: 'currentMana is not defined' in or related to this line: 'console.log('currentMana:', currentMana);' Line Number: 3877
User prompt
elimina el sistema de mana completamente
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ var Coin = Container.expand(function () { var self = Container.call(this); var coinGraphics = self.attachAsset('coin', { anchorX: 0.5, anchorY: 0.5 }); self.bobOffset = Math.random() * Math.PI * 2; self.initialY = 0; self.update = function () { if (self.initialY === 0) { self.initialY = self.y; } // Only do bobbing animation and collection if not animating to coin counter if (!self.isAnimating) { // Bobbing animation self.y = self.initialY + Math.sin(LK.ticks * 0.1 + self.bobOffset) * 10; // Check collection by wizard if (wizard && self.intersects(wizard)) { self.collect(); } } }; self.collect = function () { LK.getSound('coinCollect').play(); LK.setScore(LK.getScore() + 5); coinCounter++; coinText.setText('Coins: ' + coinCounter); // Remove from coins array for (var i = coins.length - 1; i >= 0; i--) { if (coins[i] === self) { coins.splice(i, 1); break; } } // Destroy coin directly without object pool self.destroy(); }; return self; }); // Unified Enemy class that handles all enemy types var Enemy = Container.expand(function (type) { var self = Container.call(this); // Set enemy type and configure based on it self.enemyType = type || 'skeleton'; self.currentFrame = 1; self.animationTimer = 0; self.animationState = 'walking'; self.frozen = false; self.frozenTimer = 0; self.isDying = false; self.lastX = 0; self.speedTweenStarted = false; self.lastIntersecting = false; // Configure enemy based on type var config = { skeleton: { health: 100, speed: 3, damage: 20, animationSpeed: 15, assetPrefix: 'esqueleto', scale: 3.0, vibration: [100], tint: null }, ogre: { health: 200, speed: 2.5, damage: 30, animationSpeed: 18, assetPrefix: 'ogre', scale: 3.5, vibration: [200], tint: null }, knight: { health: 300, speed: 2, damage: 40, animationSpeed: 20, assetPrefix: 'knight', scale: 3.0, vibration: [150], tint: null }, miniBoss: { health: 3000, speed: 4, damage: 75, animationSpeed: 12, assetPrefix: 'ogre', scale: 5.0, vibration: [300, 100, 300], tint: 0x8B0000 } }; var enemyConfig = config[self.enemyType] || config.skeleton; self.health = enemyConfig.health; self.maxHealth = self.health; self.speed = enemyConfig.speed; self.damage = enemyConfig.damage; self.animationSpeed = enemyConfig.animationSpeed; // Create animation frames self.animationFrames = []; for (var i = 1; i <= 4; i++) { var frameGraphics = self.attachAsset(enemyConfig.assetPrefix + i, { anchorX: 0.5, anchorY: 1.0, scaleX: enemyConfig.scale, scaleY: enemyConfig.scale }); frameGraphics.visible = i === 1; if (enemyConfig.tint) { frameGraphics.tint = enemyConfig.tint; } self.animationFrames.push(frameGraphics); } // Create health bar for mini boss if (self.enemyType === 'miniBoss') { self.healthBarBg = game.addChild(LK.getAsset('miniBossHealthBarBg', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 300, scaleX: 8, scaleY: 2 })); self.healthBarFg = game.addChild(LK.getAsset('miniBossHealthBar', { anchorX: 0.0, anchorY: 0.5, x: 2048 / 2 - 200, y: 300, scaleX: 8, scaleY: 2 })); self.healthText = new Text2('Boss Health: ' + self.health + '/' + self.maxHealth, { size: 80, fill: 0xFFFFFF, font: "monospace" }); self.healthText.anchor.set(0.5, 0.5); self.healthText.x = 2048 / 2; self.healthText.y = 250; game.addChild(self.healthText); } // Update health bar for mini boss self.updateHealthBar = function () { if (self.enemyType !== 'miniBoss' || !self.healthBarFg) { return; } var healthPercent = self.health / self.maxHealth; self.healthBarFg.scaleX = healthPercent * 8; self.healthText.setText('Boss Health: ' + self.health + '/' + self.maxHealth); if (healthPercent > 0.6) { self.healthBarFg.tint = 0xff0000; } else if (healthPercent > 0.3) { self.healthBarFg.tint = 0xff4500; } else { self.healthBarFg.tint = 0x8B0000; } }; // Optimized animation system self.updateAnimation = function () { self.animationTimer++; var frameSpeed = self.animationSpeed; if (self.animationState === 'attacking') { frameSpeed = Math.floor(frameSpeed * 0.5); } else if (self.animationState === 'dying') { frameSpeed = Math.floor(frameSpeed * 1.3); } else if (self.animationState === 'idle') { frameSpeed = Math.floor(frameSpeed * 1.7); } if (self.animationTimer >= frameSpeed) { self.animationTimer = 0; self.animationFrames[self.currentFrame - 1].visible = false; if (self.animationState === 'walking') { self.currentFrame++; if (self.currentFrame > 4) { self.currentFrame = 1; } } else if (self.animationState === 'attacking') { self.currentFrame++; if (self.currentFrame > 4) { self.currentFrame = 2; } } else if (self.animationState === 'dying') { if (self.currentFrame < 4) { self.currentFrame++; } } else if (self.animationState === 'idle') { self.currentFrame = self.currentFrame === 1 ? 2 : 1; } self.animationFrames[self.currentFrame - 1].visible = true; } }; self.update = function () { if (tutorial && tutorial.isActive || self.isDying) { return; } // Animation and speed progression self.updateAnimation(); if (!self.speedTweenStarted) { self.speedTweenStarted = true; tween(self, { speed: self.speed * 1.5 }, { duration: 10000, easing: tween.easeOut }); } // Handle frozen state if (self.frozen) { self.frozenTimer--; if (self.frozenTimer <= 0) { self.frozen = false; } return; } // Enhanced movement toward wizard if (wizard) { var dx = wizard.x - self.x; var dy = wizard.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 0) { var speedMult = self.timeSlowed ? self.timeSlowAmount : 1.0; self.x += dx / distance * self.speed * speedMult; self.y += dy / distance * self.speed * speedMult; var flipScale = dx < 0 ? -enemyConfig.scale : enemyConfig.scale; for (var frameIdx = 0; frameIdx < self.animationFrames.length; frameIdx++) { self.animationFrames[frameIdx].scaleX = flipScale; } } else { self.y += self.speed; } } }; self.takeDamage = function (damage) { self.health -= damage; self.animationState = 'attacking'; // Create damage text var damageText = new Text2('-' + damage, { size: 120, fill: 0xFF4444, font: "monospace" }); damageText.anchor.set(0.5, 0.5); damageText.x = self.x + (Math.random() - 0.5) * 60; damageText.y = self.y - 40; game.addChild(damageText); var startY = damageText.y; tween(damageText, { y: startY - 120, alpha: 0, scaleX: 1.5, scaleY: 1.5 }, { duration: 1000, easing: tween.easeOut, onFinish: function onFinish() { damageText.destroy(); } }); // Flash effect LK.effects.flashObject(self, 0xFF0000, 300); // Return to walking state after brief animation tween({}, {}, { duration: 300, onFinish: function onFinish() { if (self.animationState === 'attacking') { self.animationState = 'walking'; } } }); if (self.enemyType === 'miniBoss') { self.updateHealthBar(); } if (self.health <= 0) { self.die(); } }; self.down = function (x, y, obj) { if (tutorial && tutorial.isActive || self.isDying) { return; } if (wizard && wizard.attackCooldown > 0) { LK.effects.flashObject(self, 0xFF0000, 200); return; } // Vibration feedback if (typeof LK.vibrate === 'function') { LK.vibrate(enemyConfig.vibration); } selectedEnemy = self; LK.effects.flashObject(self, 0xFFFF00, 500); if (wizard && projectiles.length < 10) { var projectile = ProjectileFactory.createBasicAttack(wizard, self); projectile.targetEnemy = self; projectiles.push(projectile); LK.getSound('spellCast').play(); wizard.attackCooldown = 30; } }; self.die = function () { if (globalDeathHandler) { // Get appropriate array based on type var enemyArray = enemies; if (self.enemyType === 'ogre') { enemyArray = ogres; } else if (self.enemyType === 'knight') { enemyArray = knights; } else if (self.enemyType === 'miniBoss') { enemyArray = miniBosses; } globalDeathHandler.executeEnemyDeath(self, enemyArray); } }; return self; }); // Unified EnemyManager for streamlined enemy lifecycle management var EnemyManager = Container.expand(function () { var self = Container.call(this); // Use global arrays directly - no separate collections needed // Unified enemy configuration templates self.enemyTemplates = { skeleton: { baseHealth: 100, baseSpeed: 3, damage: 20, coinReward: 1 }, ogre: { baseHealth: 200, baseSpeed: 2.5, damage: 30, coinReward: 1 }, knight: { baseHealth: 300, baseSpeed: 2, damage: 40, coinReward: 1 }, miniBoss: { baseHealth: 3000, baseSpeed: 4, damage: 75, coinReward: 5 } }; // Streamlined enemy creation with unified Enemy class self.createEnemy = function (type, difficulty, level, pathOverride) { var template = self.enemyTemplates[type]; if (!template) { return null; } // Create enemy using unified Enemy class var enemy = new Enemy(type); // Apply difficulty scaling enemy.speed *= 1 + level * 0.3; enemy.pathIndex = pathOverride || self.selectOptimalPath(type); self.positionEnemy(enemy, type); self.applyDifficultyModifications(enemy, type, difficulty); return enemy; }; // Unified enemy positioning system self.positionEnemy = function (enemy, type) { // Use direct spawn position values var spawnPositions = [{ x: 2048 / 2, y: -100 }, { x: 2048 + 50, y: -50 }, { x: -50, y: -50 }, { x: -100, y: 2732 / 2 + 400 }, { x: 2048 + 100, y: 2732 / 2 + 400 }]; var spawnPos = spawnPositions[enemy.pathIndex]; if (spawnPos) { enemy.x = Math.max(50, Math.min(1998, spawnPos.x)); enemy.y = type === 'miniBoss' ? -200 : Math.max(-200, Math.min(2732 + 100, spawnPos.y)); enemy.lastX = enemy.x; } }; // Streamlined difficulty modifications self.applyDifficultyModifications = function (enemy, type, difficulty) { if (type === 'miniBoss') { enemy.updateHealthBar(); LK.effects.flashScreen(0x8B0000, 1000); } else if (type === 'skeleton' && Math.random() < 0.3) { LK.getSound('enemyGrowl').play(); } // Elite enemy system for hard difficulty if (difficulty === 'DIFICIL' && enemyKillCounter >= 20 && Math.random() < 0.15) { self.createEliteEnemy(enemy, type); } }; // Unified elite enemy creation self.createEliteEnemy = function (enemy, type) { var eliteMultipliers = { health: 1.8, speed: 1.3, color: 0xFF6600 }; if (type === 'ogre') { eliteMultipliers.color = 0xFF0000; } if (type === 'knight') { eliteMultipliers.color = 0xFFD700; } if (type === 'miniBoss') { eliteMultipliers.color = 0x8B0000; } enemy.health *= eliteMultipliers.health; enemy.speed *= eliteMultipliers.speed; enemy.maxHealth = enemy.health; enemy.isElite = true; for (var i = 0; i < enemy.animationFrames.length; i++) { enemy.animationFrames[i].tint = eliteMultipliers.color; } }; // Optimized path selection system self.selectOptimalPath = function (type) { if (type === 'skeleton' && enemyKillCounter < 5) { return 0; } if (type === 'miniBoss') { return 0; } var available = []; for (var i = 0; i < 5; i++) { if (pathConsecutiveSpawns[i] < 2) { available.push(i); } } if (available.length === 0) { for (var i = 0; i < 5; i++) { pathConsecutiveSpawns[i] = 0; } available = [0, 1, 2, 3, 4]; } return available[Math.floor(Math.random() * available.length)]; }; // Unified enemy collection management self.addToCollection = function (enemy, type) { if (type === 'skeleton') { enemies.push(enemy); } else if (type === 'ogre') { ogres.push(enemy); } else if (type === 'knight') { knights.push(enemy); } else if (type === 'miniBoss') { miniBosses.push(enemy); } }; // Streamlined enemy removal system self.removeFromCollection = function (enemy, type) { var collection = []; if (type === 'skeleton') { collection = enemies; } else if (type === 'ogre') { collection = ogres; } else if (type === 'knight') { collection = knights; } else if (type === 'miniBoss') { collection = miniBosses; } if (collection.length > 0) { for (var i = collection.length - 1; i >= 0; i--) { if (collection[i] === enemy) { collection.splice(i, 1); break; } } } }; // Get all enemies as unified array self.getAllEnemies = function () { var allEnemies = []; allEnemies = allEnemies.concat(enemies); allEnemies = allEnemies.concat(ogres); allEnemies = allEnemies.concat(knights); allEnemies = allEnemies.concat(miniBosses); return allEnemies; }; return self; }); var EnergyOrb = Container.expand(function () { var self = Container.call(this); // Create energy sphere visual var sphereGraphics = self.attachAsset('energySphere', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.5, scaleY: 1.5 }); // Add glowing effect sphereGraphics.alpha = 0.8; self.attackTimer = 0; self.attackInterval = 180; // 3 seconds at 60fps self.orbitalAngle = 0; self.orbitalRadius = 120; self.update = function () { // Pause energy orb when tutorial is active if (tutorial && tutorial.isActive) { return; } // Keep sphere at wizard's position (stationary relative to wizard) if (wizard) { self.x = wizard.x + 140; // Position further to the right side of wizard self.y = wizard.y - 20; // Position slightly lower relative to wizard } // Pulsing glow effect var pulse = 1 + Math.sin(LK.ticks * 0.2) * 0.3; sphereGraphics.scaleX = 1.5 * pulse; sphereGraphics.scaleY = 1.5 * pulse; // Attack timer - keep original interval regardless of upgrades self.attackTimer++; if (self.attackTimer >= 180) { // Fixed at 3 seconds self.attackTimer = 0; self.attackClosestEnemy(); } }; self.attackClosestEnemy = function () { var closestEnemy = null; var closestDistance = Infinity; // Check all enemy types for closest one var allEnemies = collisionArrayPool.getAllEnemies(); for (var i = 0; i < allEnemies.length; i++) { var enemy = allEnemies[i]; var dx = enemy.x - self.x; var dy = enemy.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < closestDistance) { closestDistance = distance; closestEnemy = enemy; } } // Attack closest enemy if found if (closestEnemy) { // Create energy beam projectile using unified factory var energyBeam = ProjectileFactory.createProjectile('energyBeam', self.x, self.y, closestEnemy.x, closestEnemy.y, { targetEnemy: closestEnemy }); // Flash effect on sphere when attacking globalTween(sphereGraphics, { scaleX: 2.5, scaleY: 2.5, alpha: 1.0 }, { duration: 200, easing: tween.easeOut, onFinish: function onFinish() { globalTween(sphereGraphics, { scaleX: 1.5, scaleY: 1.5, alpha: 0.8 }, { duration: 200, easing: tween.easeIn }); } }); LK.getSound('spellCast').play(); } }; return self; }); var GameMenu = Container.expand(function () { var self = Container.call(this); // Simple spell deck - load from storage or use defaults var currentDeck = storage.spellDeck || ['fireball', 'heal', 'lightning']; storage.spellDeck = currentDeck.slice(); // Menu background image instead of cave background var menuBg = self.attachAsset('menuBackground', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2, scaleX: 25.0, scaleY: 35.0 }); menuBg.alpha = 1.0; // Title text var titleText = new Text2('WIZARD DEFENDER', { size: 150, fill: 0xFFD700, font: "monospace" }); titleText.anchor.set(0.5, 0.5); titleText.x = 2048 / 2; titleText.y = 800; self.addChild(titleText); // Instructions text var instructionsText = new Text2('TAP ENEMIES TO ATTACK\nDEFEND YOUR CASTLE!', { size: 80, fill: 0xFFFFFF, font: "monospace" }); instructionsText.anchor.set(0.5, 0.5); instructionsText.x = 2048 / 2; instructionsText.y = 1200; self.addChild(instructionsText); // Start button var startButton = self.attachAsset('wizard1', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 1500, scaleX: 2, scaleY: 2 }); var startButtonText = new Text2('START GAME', { size: 100, fill: 0x000000, font: "monospace" }); startButtonText.anchor.set(0.5, 0.5); startButtonText.x = 2048 / 2; startButtonText.y = 1600; self.addChild(startButtonText); // Configuration button var configButton = self.attachAsset('pathSelector', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 1800, scaleX: 4, scaleY: 2 }); configButton.tint = 0x4169E1; var configButtonText = new Text2('CONFIGURACION', { size: 80, fill: 0xFFFFFF, font: "monospace" }); configButtonText.anchor.set(0.5, 0.5); configButtonText.x = 2048 / 2; configButtonText.y = 1800; self.addChild(configButtonText); // Shop button var shopButton = self.attachAsset('pathSelector', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 1950, scaleX: 4, scaleY: 2 }); shopButton.tint = 0xFF6B35; var shopButtonText = new Text2('TIENDA', { size: 80, fill: 0xFFFFFF, font: "monospace" }); shopButtonText.anchor.set(0.5, 0.5); shopButtonText.x = 2048 / 2; shopButtonText.y = 1950; self.addChild(shopButtonText); // Deck button var deckButton = self.attachAsset('pathSelector', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2150, scaleX: 4, scaleY: 2 }); deckButton.tint = 0x8A2BE2; var deckButtonText = new Text2('DECK HECHIZOS', { size: 80, fill: 0xFFFFFF, font: "monospace" }); deckButtonText.anchor.set(0.5, 0.5); deckButtonText.x = 2048 / 2; deckButtonText.y = 2150; self.addChild(deckButtonText); // Tutorial button (only show if tutorial was completed before) if (storage.tutorialCompleted) { var tutorialButton = self.attachAsset('pathSelector', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2350, scaleX: 4, scaleY: 2 }); tutorialButton.tint = 0x2E8B57; var tutorialButtonText = new Text2('TUTORIAL', { size: 80, fill: 0xFFFFFF, font: "monospace" }); tutorialButtonText.anchor.set(0.5, 0.5); tutorialButtonText.x = 2048 / 2; tutorialButtonText.y = 2350; self.addChild(tutorialButtonText); } // Simplified button interaction router self.down = function (x, y, obj) { if (self.configMode) { self.handleConfigInteraction(x, y); } else if (self.deckMode) { self.handleDeckInteraction(x, y); } else if (self.shopMode) { self.handleShopInteraction(x, y); } else { self.handleMainMenuInteraction(x, y); } }; // Simplified configuration interaction handler self.handleConfigInteraction = function (x, y) { // Define interaction zones var zones = [{ yMin: 1150, yMax: 1250, action: 'music' }, { yMin: 1350, yMax: 1450, action: 'sound' }, { yMin: 1550, yMax: 1650, action: 'difficulty' }, { yMin: 1950, yMax: 2050, action: 'back' }]; for (var i = 0; i < zones.length; i++) { var zone = zones[i]; if (y >= zone.yMin && y <= zone.yMax) { self.handleConfigAction(zone.action); return; } } }; // Handle specific config actions self.handleConfigAction = function (action) { if (action === 'music') { var vol = storage.musicVolume || 0.7; vol = vol >= 1.0 ? 0.0 : Math.min(1.0, vol + 0.1); storage.musicVolume = vol; self.musicVolumeText.setText('VOLUMEN MUSICA: ' + Math.round(vol * 100) + '%'); } else if (action === 'sound') { var vol = storage.soundVolume || 1.0; vol = vol >= 1.0 ? 0.0 : Math.min(1.0, vol + 0.1); storage.soundVolume = vol; self.soundVolumeText.setText('VOLUMEN SONIDO: ' + Math.round(vol * 100) + '%'); } else if (action === 'difficulty') { var difficulties = ['FACIL', 'NORMAL', 'DIFICIL']; var current = storage.difficulty || 'NORMAL'; var index = (difficulties.indexOf(current) + 1) % difficulties.length; storage.difficulty = difficulties[index]; self.difficultyText.setText('DIFICULTAD: ' + difficulties[index]); } else if (action === 'back') { self.hideConfigMenu(); } }; // Simplified deck interaction handler self.handleDeckInteraction = function (x, y) { // Check back button first (fastest check) if (y >= 2350 && y <= 2650) { self.hideDeck(); return; } // Check deck cards var clickedCard = self.findClickedCard(x, y, self.deckElements, true); if (clickedCard) { self.handleDeckCardClick(clickedCard, x, y); return; } // Check available cards var availableCard = self.findClickedCard(x, y, self.availableElements, false); if (availableCard) { self.handleAvailableCardClick(availableCard); } }; // Find clicked card in collection self.findClickedCard = function (x, y, collection, isDeckCard) { for (var i = 0; i < collection.length; i++) { var element = collection[i]; if (element.spellId && element.isDeckCard === isDeckCard && self.isCardClicked(element, x, y)) { return element; } } return null; }; // Helper to check if card was clicked self.isCardClicked = function (element, x, y) { var cardX = element.x; var cardY = element.y; return x >= cardX - 175 && x <= cardX + 175 && y >= cardY - 225 && y <= cardY + 225; }; // Handle deck card clicks (remove from deck) self.handleDeckCardClick = function (element, x, y) { // Visual feedback LK.effects.flashObject(element, 0xFF4444, 200); var cardY = element.y; // Remove card from deck when touched self.removeFromDeck(element); }; // Handle available card clicks (add to deck) self.handleAvailableCardClick = function (element) { LK.effects.flashObject(element, 0x00FF00, 300); // Add card to deck when touched self.addToDeck(element); }; // Add spell to deck self.addToDeck = function (element) { if (self.spellDeck.addToDeck(element.spellId)) { self.refreshDeckDisplay(); LK.effects.flashScreen(0x00FF00, 200); self.showMessage('HECHIZO AÑADIDO', 0x00FF88); } else { LK.effects.flashScreen(0xFF0000, 200); self.showMessage('DECK LLENO O YA TIENES ESTA CARTA', 0xFF4444); } }; // Attempt to cast spell with validation self.attemptSpellCast = function (element) { var canCast = _canCastSpell(element.spellId); if (canCast) { self.executeSpellCast(element); } else { self.showSpellCastError(element); } }; // Execute successful spell cast self.executeSpellCast = function (element) { var success = _castSpell(element.spellId); if (success) { self.showMessage('HECHIZO LANZADO!', 0x00FF00); // Close deck menu after successful spell cast setTimeout(function () { if (self.deckMode) { self.hideDeck(); } }, 500); // Small delay to show the success message } else { self.showMessage('FALLO AL LANZAR HECHIZO', 0xFF4444); } }; // Show spell cast error with cooldown-only feedback (mana system completely neutralized) self.showSpellCastError = function (element) { var spell = _getSpell(element.spellId); var currentTick = LK.ticks || 0; if (cardCooldowns[element.spellId] && currentTick < cardCooldowns[element.spellId]) { var timeRemaining = Math.ceil((cardCooldowns[element.spellId] - currentTick) / 60); self.showMessage('EN RECARGA!\nEspera: ' + timeRemaining + ' segundos', 0x4169E1); } else { self.showMessage('ERROR DE HECHIZO', 0xFF6666); } LK.effects.flashObject(element, 0xFF0000, 200); }; // Remove spell from deck self.removeFromDeck = function (element) { if (self.spellDeck.removeFromDeck(element.spellId)) { self.refreshDeckDisplay(); LK.effects.flashScreen(0xFF8800, 200); self.showMessage('HECHIZO REMOVIDO', 0xFF6666); } else { LK.effects.flashScreen(0xFF0000, 200); } }; // Simplified shop interaction handler self.handleShopInteraction = function (x, y) { // Check back button if (y >= 1950 && y <= 2050) { self.hideShop(); return; } // Check purchase buttons var buttonX = 2048 / 2 + 300; if (x >= buttonX - 100 && x <= buttonX + 100) { for (var i = 0; i < 3; i++) { var itemY = 1100 + i * 200; if (y >= itemY - 50 && y <= itemY + 50) { self.purchaseShopItem(i); return; } } } }; // Handle shop item purchase self.purchaseShopItem = function (itemIndex) { var shopItems = [{ name: 'POCION SALUD', cost: 10 }, { name: 'ESCUDO MAGICO', cost: 15 }, { name: 'ESPADA MALDITA', cost: 20 }]; var item = shopItems[itemIndex]; if (coinCounter >= item.cost) { coinCounter -= item.cost; self.applyShopItemEffect(itemIndex); LK.effects.flashScreen(0x00FF00, 300); } else { LK.effects.flashScreen(0xFF0000, 300); } }; // Apply shop item effects self.applyShopItemEffect = function (itemIndex) { if (itemIndex === 0 && wizard) { // Health potion wizard.health = Math.min(wizard.health + 50, wizard.maxHealth); updateHealthBar(); } else if (itemIndex === 1 && wizard) { // Magic shield wizard.shieldActive = true; wizard.maxShieldHits = 3; wizard.currentShieldHits = 0; } else if (itemIndex === 2 && wizard) { // Cursed sword wizard.tempDamageBoost = true; wizard.tempDamageTimer = 1800; } }; // Simplified main menu interaction handler self.handleMainMenuInteraction = function (x, y) { var centerX = 2048 / 2; var buttonWidth = 400; // Define menu buttons with their zones var buttons = [{ yMin: 1450, yMax: 1650, action: 'start', needsX: false }, { yMin: 1700, yMax: 1900, action: 'config', needsX: true }, { yMin: 1850, yMax: 2050, action: 'shop', needsX: true }, { yMin: 2050, yMax: 2250, action: 'deck', needsX: true }, { yMin: 2250, yMax: 2450, action: 'tutorial', needsX: true }]; for (var i = 0; i < buttons.length; i++) { var btn = buttons[i]; if (y >= btn.yMin && y <= btn.yMax) { if (!btn.needsX || x >= centerX - buttonWidth / 2 && x <= centerX + buttonWidth / 2) { self.handleMenuAction(btn.action); return; } } } }; // Handle specific menu actions self.handleMenuAction = function (action) { if (action === 'start') { self.startGame(); } else if (action === 'config') { self.showConfigMenu(); } else if (action === 'shop') { self.showShop(); } else if (action === 'deck') { self.showDeck(); } else if (action === 'tutorial' && storage.tutorialCompleted && tutorial) { self.visible = false; tutorial.startTutorial(); } }; // Simplified message display self.showMessage = function (text, color) { var message = self.createMenuText(text, 2048 / 2, 2200, 50, color); tween(message, { alpha: 0, y: message.y - 100 }, { duration: 1500, easing: tween.easeOut, onFinish: function onFinish() { if (message.parent) { message.destroy(); } } }); }; self.showConfigMenu = function () { if (!self.configOverlay) { self.configOverlay = self.createMenuOverlay(0x000000); self.configTitle = self.createMenuText('CONFIGURACION', 2048 / 2, 800, 120, 0xFFD700); self.musicVolumeText = self.createMenuText('VOLUMEN MUSICA: ' + Math.round((storage.musicVolume || 0.7) * 100) + '%', 2048 / 2, 1200, 80, 0xFFFFFF); self.soundVolumeText = self.createMenuText('VOLUMEN SONIDO: ' + Math.round((storage.soundVolume || 1.0) * 100) + '%', 2048 / 2, 1400, 80, 0xFFFFFF); self.difficultyText = self.createMenuText('DIFICULTAD: ' + (storage.difficulty || 'NORMAL'), 2048 / 2, 1600, 80, 0xFFFFFF); self.backButton = self.createMenuButton('coin', 2048 / 2, 2000, 0x00FF00); self.backText = self.createMenuText('VOLVER', 2048 / 2, 2000, 80, 0xFFFFFF); } self.configOverlay.visible = true; self.configMode = true; }; self.hideConfigMenu = function () { if (self.configOverlay) { self.configOverlay.destroy(); self.configOverlay = null; } // Remove all configuration text elements if (self.musicVolumeText) { self.musicVolumeText.destroy(); self.musicVolumeText = null; } if (self.soundVolumeText) { self.soundVolumeText.destroy(); self.soundVolumeText = null; } if (self.difficultyText) { self.difficultyText.destroy(); self.difficultyText = null; } // Remove back button elements if (self.backButton) { self.backButton.destroy(); self.backButton = null; } if (self.backText) { self.backText.destroy(); self.backText = null; } // Remove configuration title if (self.configTitle) { self.configTitle.destroy(); self.configTitle = null; } // Remove all configuration children that were added for (var i = self.children.length - 1; i >= 0; i--) { var child = self.children[i]; // Remove config-related elements (title, texts, buttons created in showConfigMenu) if (child.setText && child.text && (child.text.includes('CONFIGURACION') || child.text.includes('VOLUMEN') || child.text.includes('DIFICULTAD') || child.text.includes('VOLVER'))) { child.destroy(); } } self.configMode = false; // Reset to show main menu elements self.visible = true; }; self.showShop = function () { if (!self.shopOverlay) { self.shopOverlay = self.createMenuOverlay(0x000033); self.shopTitle = self.createMenuText('TIENDA', 2048 / 2, 800, 120, 0xFFD700); var shopItems = self.getShopItemsData(); self.initializeShopArrays(); self.createShopItems(shopItems); self.shopBackButton = self.createMenuButton('coin', 2048 / 2, 2000, 0x00FF00); self.shopBackText = self.createMenuText('VOLVER', 2048 / 2, 2000, 80, 0xFFFFFF); } self.shopOverlay.visible = true; self.shopMode = true; }; self.hideShop = function () { if (self.shopOverlay) { self.shopOverlay.destroy(); self.shopOverlay = null; } // Remove shop title if (self.shopTitle) { self.shopTitle.destroy(); self.shopTitle = null; } // Remove shop back button elements if (self.shopBackButton) { self.shopBackButton.destroy(); self.shopBackButton = null; } if (self.shopBackText) { self.shopBackText.destroy(); self.shopBackText = null; } // Remove all shop icons if (self.shopIcons) { for (var i = 0; i < self.shopIcons.length; i++) { if (self.shopIcons[i]) { self.shopIcons[i].destroy(); } } self.shopIcons = []; } // Remove all shop texts if (self.shopTexts) { for (var i = 0; i < self.shopTexts.length; i++) { if (self.shopTexts[i]) { self.shopTexts[i].destroy(); } } self.shopTexts = []; } // Remove all shop buy buttons if (self.shopBuyButtons) { for (var i = 0; i < self.shopBuyButtons.length; i++) { if (self.shopBuyButtons[i]) { self.shopBuyButtons[i].destroy(); } } self.shopBuyButtons = []; } // Remove all shop buy texts if (self.shopBuyTexts) { for (var i = 0; i < self.shopBuyTexts.length; i++) { if (self.shopBuyTexts[i]) { self.shopBuyTexts[i].destroy(); } } self.shopBuyTexts = []; } // Remove all shop children that were added for (var i = self.children.length - 1; i >= 0; i--) { var child = self.children[i]; // Remove shop-related elements if (child.setText && child.text && (child.text.includes('TIENDA') || child.text.includes('POCION') || child.text.includes('ESCUDO') || child.text.includes('ESPADA') || child.text.includes('COMPRAR'))) { child.destroy(); } } self.shopMode = false; // Reset to show main menu elements self.visible = true; }; self.showDeck = function () { if (!self.deckOverlay) { // Ensure spellDeck exists before creating overlay if (!self.spellDeck) { self.spellDeck = new SpellDeck(); } self.deckOverlay = self.createMenuOverlay(0x1a0a2e); self.deckTitle = self.createMenuText('DECK DE HECHIZOS', 2048 / 2, 600, 100, 0xFFD700); self.initializeDeckArrays(); self.refreshDeckDisplay(); self.deckBackButton = self.createMenuButton('coin', 2048 / 2, 2500, 0x00FF00); self.deckBackText = self.createMenuText('VOLVER', 2048 / 2, 2500, 80, 0xFFFFFF); } self.deckOverlay.visible = true; self.deckMode = true; self.refreshDeckDisplay(); }; self.initializeDeckArrays = function () { if (!self.deckElements) { self.deckElements = []; } if (!self.availableElements) { self.availableElements = []; } }; self.refreshDeckDisplay = function () { if (!self.spellDeck) { self.spellDeck = new SpellDeck(); } // Clear existing deck elements for (var i = 0; i < self.deckElements.length; i++) { if (self.deckElements[i] && self.deckElements[i].parent) { self.deckElements[i].destroy(); } } self.deckElements = []; // Clear existing available elements for (var i = 0; i < self.availableElements.length; i++) { if (self.availableElements[i] && self.availableElements[i].parent) { self.availableElements[i].destroy(); } } self.availableElements = []; // Add helpful instructions at the top var instructionText = new Text2('SELECCIONA CARTAS PARA TU DECK', { size: 50, fill: 0x00FF00, font: "monospace" }); instructionText.anchor.set(0.5, 0.5); instructionText.x = 2048 / 2; instructionText.y = 700; self.addChild(instructionText); self.deckElements.push(instructionText); // Display current deck (top section) var deckLabel = new Text2('MI DECK ACTUAL (' + self.spellDeck.currentDeck.length + '/5):', { size: 70, fill: 0xFFD700, font: "monospace" }); deckLabel.anchor.set(0.5, 0.5); deckLabel.x = 2048 / 2; deckLabel.y = 800; self.addChild(deckLabel); self.deckElements.push(deckLabel); // Add deck instruction var deckInstruction = new Text2('TOCA CARTAS PARA REMOVER', { size: 40, fill: 0xFF6666, font: "monospace" }); deckInstruction.anchor.set(0.5, 0.5); deckInstruction.x = 2048 / 2; deckInstruction.y = 850; self.addChild(deckInstruction); self.deckElements.push(deckInstruction); // Display deck cards with better spacing for (var i = 0; i < 5; i++) { var cardX = 200 + i * 350; var cardY = 1050; if (i < self.spellDeck.currentDeck.length) { var spell = self.spellDeck.getSpell(self.spellDeck.currentDeck[i]); if (spell) { // Card background with dynamic state visualization var cardBg = self.attachAsset('spellCard', { anchorX: 0.5, anchorY: 0.5, x: cardX, y: cardY, scaleX: 3.5, scaleY: 4.5 }); // Set normal card appearance for deck selection cardBg.tint = self.spellDeck.getRarityColor(spell.rarity); cardBg.alpha = 0.9; cardBg.spellId = spell.id; cardBg.isDeckCard = true; self.deckElements.push(cardBg); // Add border glow var glowBorder = self.attachAsset('spellCardBg', { anchorX: 0.5, anchorY: 0.5, x: cardX, y: cardY, scaleX: 4, scaleY: 5 }); glowBorder.tint = 0x00FF00; glowBorder.alpha = 0.3; self.deckElements.push(glowBorder); // Card name var cardName = new Text2(spell.name, { size: 35, fill: 0xFFFFFF, font: "monospace" }); cardName.anchor.set(0.5, 0.5); cardName.x = cardX; cardName.y = cardY - 60; self.addChild(cardName); self.deckElements.push(cardName); // Card description var description = spell.description || 'Hechizo magico'; var cardDesc = new Text2(description, { size: 25, fill: 0xCCCCCC, font: "monospace", wordWrap: true, wordWrapWidth: 250 }); cardDesc.anchor.set(0.5, 0.5); cardDesc.x = cardX; cardDesc.y = cardY + 20; self.addChild(cardDesc); self.deckElements.push(cardDesc); // Show basic card stats for deck selection var statsText = ''; if (spell.damage) { statsText += 'Daño: ' + spell.damage + '\n'; } if (spell.healing) { statsText += 'Cura: ' + spell.healing + '\n'; } if (statsText) { var cardStats = new Text2(statsText, { size: 20, fill: 0xFFD700, font: "monospace" }); cardStats.anchor.set(0.5, 0.5); cardStats.x = cardX; cardStats.y = cardY + 80; self.addChild(cardStats); self.deckElements.push(cardStats); } } } else { // Empty slot var emptySlot = self.attachAsset('spellCardBg', { anchorX: 0.5, anchorY: 0.5, x: cardX, y: cardY, scaleX: 3.5, scaleY: 4.5 }); emptySlot.tint = 0x444444; emptySlot.alpha = 0.5; self.deckElements.push(emptySlot); var emptyText = new Text2('VACIO', { size: 40, fill: 0x666666, font: "monospace" }); emptyText.anchor.set(0.5, 0.5); emptyText.x = cardX; emptyText.y = cardY; self.addChild(emptyText); self.deckElements.push(emptyText); } } // Display available spells (bottom section) var availableLabel = new Text2('HECHIZOS DISPONIBLES PARA AÑADIR:', { size: 60, fill: 0xFFD700, font: "monospace" }); availableLabel.anchor.set(0.5, 0.5); availableLabel.x = 2048 / 2; availableLabel.y = 1350; self.addChild(availableLabel); self.availableElements.push(availableLabel); // Add available instruction var availableInstruction = new Text2('TOCA PARA AÑADIR A TU DECK', { size: 40, fill: 0x66FF66, font: "monospace" }); availableInstruction.anchor.set(0.5, 0.5); availableInstruction.x = 2048 / 2; availableInstruction.y = 1400; self.addChild(availableInstruction); self.availableElements.push(availableInstruction); // Display available spells var availableSpells = []; for (var i = 0; i < self.spellDeck.availableSpells.length; i++) { var spell = self.spellDeck.availableSpells[i]; if (self.spellDeck.currentDeck.indexOf(spell.id) === -1) { availableSpells.push(spell); } } if (availableSpells.length === 0) { var noSpellsText = new Text2('NO HAY HECHIZOS DISPONIBLES\nDESBLOQUEA MAS JUGANDO', { size: 50, fill: 0x888888, font: "monospace" }); noSpellsText.anchor.set(0.5, 0.5); noSpellsText.x = 2048 / 2; noSpellsText.y = 1600; self.addChild(noSpellsText); self.availableElements.push(noSpellsText); } for (var i = 0; i < availableSpells.length && i < 8; i++) { var spell = availableSpells[i]; var cardX = 150 + i % 4 * 450; var cardY = 1550 + Math.floor(i / 4) * 400; // Card background with hover effect var cardBg = self.attachAsset('spellCard', { anchorX: 0.5, anchorY: 0.5, x: cardX, y: cardY, scaleX: 3.2, scaleY: 4.2 }); cardBg.tint = self.spellDeck.getRarityColor(spell.rarity); cardBg.spellId = spell.id; cardBg.isDeckCard = false; cardBg.alpha = 0.8; self.availableElements.push(cardBg); // Add selection glow var selectionGlow = self.attachAsset('spellCardBg', { anchorX: 0.5, anchorY: 0.5, x: cardX, y: cardY, scaleX: 3.7, scaleY: 4.7 }); selectionGlow.tint = 0x00FFFF; selectionGlow.alpha = 0.2; self.availableElements.push(selectionGlow); // Card name var cardName = new Text2(spell.name, { size: 32, fill: 0xFFFFFF, font: "monospace" }); cardName.anchor.set(0.5, 0.5); cardName.x = cardX; cardName.y = cardY - 60; self.addChild(cardName); self.availableElements.push(cardName); // Card description var cardDesc = new Text2(spell.description, { size: 22, fill: 0xCCCCCC, font: "monospace", wordWrap: true, wordWrapWidth: 280 }); cardDesc.anchor.set(0.5, 0.5); cardDesc.x = cardX; cardDesc.y = cardY + 10; self.addChild(cardDesc); self.availableElements.push(cardDesc); // Card stats var statsText = ''; if (spell.damage) { statsText += 'Daño: ' + spell.damage + '\n'; } if (spell.healing) { statsText += 'Cura: ' + spell.healing + '\n'; } if (spell.manaCost) { statsText += 'Mana: ' + spell.manaCost; } if (statsText) { var cardStats = new Text2(statsText, { size: 18, fill: 0xFFD700, font: "monospace" }); cardStats.anchor.set(0.5, 0.5); cardStats.x = cardX; cardStats.y = cardY + 70; self.addChild(cardStats); self.availableElements.push(cardStats); } // Rarity indicator var rarityText = new Text2(spell.rarity.toUpperCase(), { size: 20, fill: self.spellDeck.getRarityColor(spell.rarity), font: "monospace" }); rarityText.anchor.set(0.5, 0.5); rarityText.x = cardX; rarityText.y = cardY + 100; self.addChild(rarityText); self.availableElements.push(rarityText); } }; self.hideDeck = function () { if (self.deckOverlay) { self.deckOverlay.destroy(); self.deckOverlay = null; } // Remove deck title if (self.deckTitle) { self.deckTitle.destroy(); self.deckTitle = null; } // Remove deck back button elements if (self.deckBackButton) { self.deckBackButton.destroy(); self.deckBackButton = null; } if (self.deckBackText) { self.deckBackText.destroy(); self.deckBackText = null; } // Clear deck elements for (var i = 0; i < self.deckElements.length; i++) { if (self.deckElements[i] && self.deckElements[i].parent) { self.deckElements[i].destroy(); } } self.deckElements = []; // Clear available elements for (var i = 0; i < self.availableElements.length; i++) { if (self.availableElements[i] && self.availableElements[i].parent) { self.availableElements[i].destroy(); } } self.availableElements = []; // Remove all deck-related children for (var i = self.children.length - 1; i >= 0; i--) { var child = self.children[i]; if (child.setText && child.text && (child.text.includes('DECK') || child.text.includes('HECHIZOS') || child.text.includes('ACTUAL') || child.text.includes('DISPONIBLES'))) { child.destroy(); } } self.deckMode = false; self.visible = true; }; // Unified UI factory for all menu elements // Menu overlay creation function self.createMenuOverlay = function (color) { var overlay = self.attachAsset('startMenuBackground', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2, scaleX: 25.0, scaleY: 35.0 }); overlay.alpha = 0.9; overlay.tint = color || 0x000000; overlay.interactive = true; overlay.zIndex = 1000; return overlay; }; // Menu text creation function self.createMenuText = function (text, x, y, size, color) { var textElement = new Text2(text, { size: size, fill: color, font: "monospace" }); textElement.anchor.set(0.5, 0.5); textElement.x = x; textElement.y = y; self.addChild(textElement); return textElement; }; // Menu button creation function self.createMenuButton = function (asset, x, y, color) { var button = self.attachAsset(asset, { anchorX: 0.5, anchorY: 0.5, x: x, y: y, scaleX: 2, scaleY: 2 }); button.tint = color; return button; }; // Removed redundant createUIElement - use specific creation methods instead self.getShopItemsData = function () { return [{ name: 'POCION SALUD', description: 'Restaura 50 HP', cost: 10, icon: 'energySphere' }, { name: 'ESCUDO MAGICO', description: 'Bloquea 3 ataques', cost: 15, icon: 'shield' }, { name: 'ESPADA MALDITA', description: 'Daño x2 por 30s', cost: 20, icon: 'spell' }]; }; self.initializeShopArrays = function () { if (!self.shopIcons) { self.shopIcons = []; } if (!self.shopTexts) { self.shopTexts = []; } if (!self.shopBuyButtons) { self.shopBuyButtons = []; } if (!self.shopBuyTexts) { self.shopBuyTexts = []; } }; self.createShopItems = function (shopItems) { for (var i = 0; i < shopItems.length; i++) { var item = shopItems[i]; var yPos = 1100 + i * 200; var itemIcon = self.attachAsset(item.icon, { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2 - 300, y: yPos, scaleX: 2, scaleY: 2 }); itemIcon.tint = 0xFFD700; self.shopIcons.push(itemIcon); var itemText = new Text2(item.name + '\n' + item.description + '\nCosto: ' + item.cost + ' monedas', { size: 60, fill: 0xFFFFFF, font: "monospace" }); itemText.anchor.set(0, 0.5); itemText.x = 2048 / 2 - 200; itemText.y = yPos; self.addChild(itemText); self.shopTexts.push(itemText); var buyButton = self.attachAsset('coin', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2 + 300, y: yPos, scaleX: 2, scaleY: 2 }); buyButton.tint = 0x00FF00; buyButton.itemIndex = i; self.shopBuyButtons.push(buyButton); var buyText = new Text2('COMPRAR', { size: 50, fill: 0xFFFFFF, font: "monospace" }); buyText.anchor.set(0.5, 0.5); buyText.x = 2048 / 2 + 300; buyText.y = yPos; self.addChild(buyText); self.shopBuyTexts.push(buyText); } }; self.startGame = function () { // Check if this is a new player (no tutorial completed) if (!storage.tutorialCompleted && tutorial) { // Show tutorial for new players self.visible = false; // Small delay to ensure menu is hidden before tutorial starts tween({}, {}, { duration: 100, onFinish: function onFinish() { tutorial.startTutorial(); } }); // Tutorial started successfully return; } // UNIFIED DECK SYNCHRONIZATION: Fix all deck system inconsistencies // Create unified deck reference that all systems will use var unifiedDeck = []; // Priority 1: Use spellDeck from menu if it exists and has content if (self.spellDeck && self.spellDeck.currentDeck && self.spellDeck.currentDeck.length > 0) { unifiedDeck = self.spellDeck.currentDeck.slice(); } // Priority 2: Use storage deck if available else if (storage.spellDeck && storage.spellDeck.length > 0) { unifiedDeck = storage.spellDeck.slice(); } // Priority 3: Use global currentDeck if available else if (currentDeck && currentDeck.length > 0) { unifiedDeck = currentDeck.slice(); } // Priority 4: Use activeSpellDeck if available else if (activeSpellDeck && activeSpellDeck.currentDeck && activeSpellDeck.currentDeck.length > 0) { unifiedDeck = activeSpellDeck.currentDeck.slice(); } // Priority 5: Default deck as last resort else { unifiedDeck = ['fireball', 'heal', 'lightning']; } // SYNCHRONIZE ALL DECK SYSTEMS with unified deck currentDeck = unifiedDeck.slice(); activeSpellDeck.currentDeck = unifiedDeck.slice(); storage.spellDeck = unifiedDeck.slice(); if (self.spellDeck) { self.spellDeck.currentDeck = unifiedDeck.slice(); } // Debug: Log deck synchronization with enhanced details console.log('=== DECK SYNCHRONIZATION ==='); console.log('Unified deck:', unifiedDeck); console.log('ActiveSpellDeck sync:', activeSpellDeck.currentDeck); console.log('Storage sync:', storage.spellDeck); console.log('CurrentDeck sync:', currentDeck); console.log('Available spells IDs:', availableSpells.map(function (s) { return s.id; })); console.log('Spell validation:'); for (var d = 0; d < unifiedDeck.length; d++) { var spellId = unifiedDeck[d]; var spell = _getSpell(spellId); console.log(' - ' + spellId + ':', spell ? spell.name : 'NOT FOUND'); } // Hide menu and start game normally self.visible = false; gameStarted = true; // Show cave background when game starts if (backgroundMap) { backgroundMap.visible = true; } // Show all game elements wizard.visible = true; for (var i = 0; i < paths.length; i++) { paths[i].visible = true; } // Show wizard position indicators with enhanced visibility for (var i = 0; i < positionIndicators.length; i++) { positionIndicators[i].visible = true; positionIndicators[i].interactive = true; // Ensure they can be touched } // Add pulsing animation to current position indicator var currentPositionIndicator = null; for (var i = 0; i < positionIndicators.length; i++) { if (positionIndicators[i].positionIndex === 0 && positionIndicators[i].tint === 0x00FF00) { currentPositionIndicator = positionIndicators[i]; break; } } if (currentPositionIndicator) { tween(currentPositionIndicator, { scaleX: 5.0, scaleY: 5.0, alpha: 1.0 }, { duration: 1000, easing: tween.easeInOut, onFinish: function onFinish() { tween(currentPositionIndicator, { scaleX: 4.0, scaleY: 4.0, alpha: 0.8 }, { duration: 1000, easing: tween.easeInOut }); } }); } // Show all stone path segments and make them visible for (var i = 0; i < game.children.length; i++) { var child = game.children[i]; if (child.pathIndex !== undefined && child !== paths[child.pathIndex]) { child.visible = true; // Check if it's a stone path segment or path number if (child.alpha !== undefined && child.setText === undefined) { child.alpha = 0; // Keep stone paths invisible } } } coinText.visible = true; killCountText.visible = true; tapText.visible = true; // Add wizard movement instructions var movementText = new Text2('TAP LOWER SCREEN TO MOVE WIZARD', { size: 60, fill: 0x00FFFF, font: "monospace" }); movementText.anchor.set(0.5, 0.5); LK.gui.center.addChild(movementText); movementText.y = -200; movementText.visible = true; // Auto-hide movement instructions after 8 seconds tween(movementText, { alpha: 0 }, { duration: 2000, delay: 6000, easing: tween.easeOut, onFinish: function onFinish() { if (movementText.parent) { movementText.destroy(); } } }); healthBarBg.visible = true; healthBar.visible = true; healthText.visible = true; // Mana UI removed - using cooldown-only spell system // Show card toggle button cardToggleButton.visible = true; cardToggleText.visible = true; // Open card panel automatically when game starts cardPanelVisible = true; showInGameCardPanel(); // Spell UI system removed - using deck menu only console.log('Spell UI handled through deck menu system'); // SPELL SLOT SYSTEM REMOVED - Using deck menu only for spell casting // Spells can now only be cast from the deck menu interface console.log('Spell slots eliminated - using deck menu system only'); // Mana system neutralized - values set to 9999 to bypass all mana checks currentMana = 9999; maxMana = 9999; if (activeSpellDeck) { activeSpellDeck.currentMana = 9999; activeSpellDeck.maxMana = 9999; } console.log('=== MANA SYSTEM SIMPLIFIED ==='); console.log('currentMana:', currentMana); console.log('activeSpellDeck.currentMana:', activeSpellDeck ? activeSpellDeck.currentMana : 'undefined'); console.log('maxMana:', maxMana); // Mana bar functionality moved to spell deck system console.log('Mana system managed by deck menu'); // DEBUG: Verify mana values after initialization console.log('=== MANA FORCE INITIALIZATION ==='); console.log('Mana system has been completely removed'); console.log('activeSpellDeck.currentMana:', activeSpellDeck.currentMana); console.log('Spells now use cooldown-only system'); console.log('Mana bar updated successfully'); // SPELL SLOT SYSTEM REMOVED - Using deck menu only for spell casting // Spells can now only be cast from the deck menu interface console.log('Spell slots eliminated - using deck menu system only'); // CRITICAL MANA SYSTEM INITIALIZATION AND REPAIR console.log('=== CRITICAL MANA INITIALIZATION ==='); // STEP 1: Force mana to 9999 immediately (NEUTRALIZED) currentMana = 9999; maxMana = 9999; // STEP 2: Force activeSpellDeck mana to 9999 (NEUTRALIZED) if (activeSpellDeck) { activeSpellDeck.currentMana = 9999; activeSpellDeck.maxMana = 9999; } // STEP 3: Clear any corrupted cooldowns cardCooldowns = {}; // STEP 4: CRITICAL - Validate deck data integrity before spell system validateDeckData(); // STEP 5: Force mana validation and repair validateManaSystem(); // STEP 5: Update mana display immediately // STEP 6: Additional verification with delay to ensure all systems sync tween({}, {}, { duration: 100, onFinish: function onFinish() { // FORCE MANA TO 9999 AGAIN after all initialization (NEUTRALIZED) currentMana = 9999; if (activeSpellDeck) { activeSpellDeck.currentMana = 9999; } // Force update display again updateManaDisplay(); // Final debug verification console.log('=== FINAL MANA VERIFICATION ==='); console.log('Mana system has been completely removed'); console.log('Spells now use cooldown-only system'); console.log('No mana variables are needed'); } }); // SPELL SLOTS SYSTEM REMOVED // All spell casting now happens through the deck menu system only console.log('=== SPELL SYSTEM SIMPLIFIED ==='); console.log('Deck menu is the only way to cast spells'); console.log('Spell slots have been eliminated'); // Start medieval music with user's volume setting var musicVolume = storage.musicVolume || 0.7; LK.playMusic('medievalTheme', { volume: musicVolume, fade: { start: 0, end: musicVolume, duration: 2000 } }); }; return self; }); var Orb = Container.expand(function () { var self = Container.call(this); // Create orb visual using energy sphere var orbGraphics = self.attachAsset('energySphere', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.4, scaleY: 0.4 }); orbGraphics.tint = 0xFFD700; // Golden color for orbs orbGraphics.alpha = 0.9; self.orbitalAngle = 0; // Starting angle for this orb self.orbitalRadius = 880; // Distance from wizard - doubled again for even more separation self.rotationSpeed = 0.025; // How fast orbs rotate - halved for slower movement // Category 3: Orb-enemy collisions (separate from projectile-enemy and enemy-wizard) self.processOrbEnemyCollisions = function () { var allEnemies = collisionArrayPool.getAllEnemies(); for (var i = 0; i < allEnemies.length; i++) { var enemy = allEnemies[i]; // Skip collision check with wizard if (enemy === wizard) { continue; } // Initialize collision tracking for this enemy if not exists if (!self.lastIntersecting) { self.lastIntersecting = {}; } if (self.lastIntersecting[i] === undefined) { self.lastIntersecting[i] = false; } // 1.1 Distance Culling: Quick distance check before expensive collision detection var dx = enemy.x - self.x; var dy = enemy.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); var maxOrbRange = 80; // Orb collision range var currentIntersecting = false; if (distance <= maxOrbRange) { // Only perform expensive intersection test if enemy is close enough currentIntersecting = self.intersects(enemy); } if (!self.lastIntersecting[i] && currentIntersecting) { // Deal damage to enemy on contact transition (first contact only) enemy.takeDamage(200); // Visual effect for orb hit LK.effects.flashObject(self, 0xFFFFFF, 200); // Create orb impact effect var orbImpact = game.addChild(LK.getAsset('energySphere', { anchorX: 0.5, anchorY: 0.5, x: enemy.x, y: enemy.y, scaleX: 0.3, scaleY: 0.3 })); orbImpact.tint = 0xFFD700; orbImpact.alpha = 0.8; // Animate orb impact globalTween(orbImpact, { scaleX: 1.5, scaleY: 1.5, alpha: 0 }, { duration: 250, easing: tween.easeOut, onFinish: function onFinish() { orbImpact.destroy(); } }); } // Update collision state for this enemy self.lastIntersecting[i] = currentIntersecting; } }; self.update = function () { // Pause orb when tutorial is active if (tutorial && tutorial.isActive) { return; } // Rotate around wizard if (wizard) { self.orbitalAngle += self.rotationSpeed; self.x = wizard.x + Math.cos(self.orbitalAngle) * self.orbitalRadius; self.y = wizard.y + Math.sin(self.orbitalAngle) * self.orbitalRadius - 240; // Position orb much higher up } // Add pulsing effect var pulse = 1 + Math.sin(LK.ticks * 0.3) * 0.2; orbGraphics.scaleX = 0.4 * pulse; orbGraphics.scaleY = 0.4 * pulse; // Category 3: Orb-enemy collisions (separate from projectile-enemy and enemy-wizard) self.processOrbEnemyCollisions(); }; return self; }); // Create global death handler instance var Projectile = Container.expand(function (type) { var self = Container.call(this); self.type = type || 'projectile'; self.targetEnemy = null; self.hitEnemy = false; // Unified projectile configuration var configs = { projectile: { assetId: 'projectile', scale: 1.5, speed: 50, damage: 100, tint: 0x44aaff, hasGlow: true, hasRotation: true }, energyBeam: { assetId: 'projectileGlow', scale: 1.0, speed: 60, damage: 100, tint: 0x00ffff, hasRotation: true }, fireBall: { assetId: 'projectileGlow', scale: 1.5, speed: 40, damage: 150, tint: 0xFF4500, hasRotation: true, hasFlicker: true, isAreaDamage: true } }; var config = configs[self.type] || configs.projectile; self.speed = config.speed; self.damage = config.damage; self.direction = { x: 0, y: 0 }; // Create unified graphics self.graphics = self.attachAsset(config.assetId, { anchorX: 0.5, anchorY: 0.5, scaleX: config.scale, scaleY: config.scale }); self.graphics.tint = config.tint; // Add glow effect if specified if (config.hasGlow) { var glow = self.attachAsset('projectileGlow', { anchorX: 0.5, anchorY: 0.5, scaleX: 2.0, scaleY: 2.0 }); glow.alpha = 0.3; glow.tint = config.tint; } self.update = function () { if (tutorial && tutorial.isActive) { return; } // Move projectile self.x += self.direction.x * self.speed; self.y += self.direction.y * self.speed; // Visual effects if (config.hasRotation) { var angle = Math.atan2(self.direction.y, self.direction.x); self.graphics.rotation = angle + Math.PI / 2; } if (config.hasFlicker) { var flicker = 1 + Math.sin(LK.ticks * 0.4) * 0.3; self.graphics.scaleX = config.scale * flicker; self.graphics.scaleY = config.scale * flicker; } // Remove if off screen if (self.x < -100 || self.x > 2148 || self.y < -100 || self.y > 2832) { ProjectileFactory.removeProjectile(self); return; } self.checkCollisions(); }; self.checkCollisions = function () { if (self.hitEnemy) { return; } if (config.isAreaDamage) { self.processAreaCollisions(); } else { self.processSingleTargetCollisions(); } }; // Unified collision processing self.processAreaCollisions = function () { var allEnemies = collisionArrayPool.getAllEnemies(); for (var i = 0; i < allEnemies.length; i++) { var enemy = allEnemies[i]; if (enemy === wizard || enemy.isDying) { continue; } var distance = Math.sqrt(Math.pow(enemy.x - self.x, 2) + Math.pow(enemy.y - self.y, 2)); if (distance <= 120 && self.intersects(enemy)) { enemy.takeDamage(self.damage); self.createExplosion(enemy); self.hitEnemy = true; ProjectileFactory.removeProjectile(self); return; } } }; self.processSingleTargetCollisions = function () { if (self.targetEnemy && self.targetEnemy.parent && !self.targetEnemy.isDying) { var distance = Math.sqrt(Math.pow(self.targetEnemy.x - self.x, 2) + Math.pow(self.targetEnemy.y - self.y, 2)); if (distance <= 100 && self.intersects(self.targetEnemy)) { self.targetEnemy.takeDamage(self.damage); self.hitEnemy = true; ProjectileFactory.removeProjectile(self); return; } } else { self.hitEnemy = true; ProjectileFactory.removeProjectile(self); } }; self.createExplosion = function (enemy) { LK.effects.flashObject(enemy, 0xFF4500, 400); var explosion = game.addChild(LK.getAsset('projectileGlow', { anchorX: 0.5, anchorY: 0.5, x: enemy.x, y: enemy.y, scaleX: 2, scaleY: 2 })); explosion.tint = 0xFF6600; explosion.alpha = 0.8; globalTween(explosion, { scaleX: 5, scaleY: 5, alpha: 0 }, { duration: 500, easing: tween.easeOut, onFinish: function onFinish() { explosion.destroy(); } }); }; return self; }); // Simple effects helper for basic visual feedback // Simple SpellDeck class to replace the complex system var SpellDeck = Container.expand(function () { var self = Container.call(this); // Use the existing simple spell system self.currentDeck = storage.spellDeck || ['fireball', 'heal', 'lightning']; self.availableSpells = availableSpells; // Reference to global availableSpells // Simple methods to match the interface expected by GameMenu self.addToDeck = function (spellId) { if (self.currentDeck.length >= 5) { return false; } if (self.currentDeck.indexOf(spellId) !== -1) { return false; } self.currentDeck.push(spellId); storage.spellDeck = self.currentDeck.slice(); return true; }; self.removeFromDeck = function (spellId) { var index = self.currentDeck.indexOf(spellId); if (index === -1) { return false; } self.currentDeck.splice(index, 1); storage.spellDeck = self.currentDeck.slice(); return true; }; self.getSpell = function (spellId) { return _getSpell(spellId); }; self.getRarityColor = function (rarity) { return _getRarityColor(rarity); }; self.unlockSpell = function (spellId) { // Simple unlock system - spells are always available return true; }; return self; }); // Simple spell system - no complex classes needed var Tutorial = Container.expand(function () { var self = Container.call(this); // Simple tutorial state self.isActive = false; // Tutorial overlay background var tutorialOverlay = self.attachAsset('startMenuBackground', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2, scaleX: 25.0, scaleY: 35.0 }); tutorialOverlay.alpha = 0.9; tutorialOverlay.tint = 0x000000; tutorialOverlay.visible = false; // Initially hidden tutorialOverlay.zIndex = 1999; // Ensure proper layering tutorialOverlay.interactive = true; // Always interactive to block clicks // Start the tutorial - simplified version self.startTutorial = function () { self.isActive = true; // Make tutorial visible self.visible = true; self.zIndex = 2000; tutorialOverlay.visible = true; // Hide game menu while tutorial is active if (gameMenu) { gameMenu.visible = false; } // Show simple tutorial text var titleText = new Text2('WIZARD DEFENDER', { size: 120, fill: 0xFFD700, font: "monospace" }); titleText.anchor.set(0.5, 0.5); titleText.x = 2048 / 2; titleText.y = 1000; self.addChild(titleText); var instructionsText = new Text2('TOCA ENEMIGOS PARA ATACAR\nUSA CARTAS PARA HECHIZOS\nCUIDA TU SALUD', { size: 80, fill: 0xFFFFFF, font: "monospace" }); instructionsText.anchor.set(0.5, 0.5); instructionsText.x = 2048 / 2; instructionsText.y = 1400; self.addChild(instructionsText); var continueText = new Text2('TOCA PARA EMPEZAR', { size: 60, fill: 0x00FF00, font: "monospace" }); continueText.anchor.set(0.5, 0.5); continueText.x = 2048 / 2; continueText.y = 1800; self.addChild(continueText); return true; // Tutorial started }; // Complete the tutorial - simplified self.completeTutorial = function () { // Mark tutorial as completed storage.tutorialCompleted = true; // Hide tutorial completely tutorialOverlay.visible = false; tutorialOverlay.interactive = false; self.visible = false; self.isActive = false; // Start game immediately if (gameMenu) { gameMenu.startGame(); } }; // Handle tutorial interactions - simplified self.down = function (x, y, obj) { if (!self.isActive) { return; } // Any tap completes tutorial self.completeTutorial(); }; return self; }); // Impact effect function now handled by BaseDamageHandler // Unified death handler for all enemy types using enemy configuration var UnifiedDeathHandler = Container.expand(function () { var self = Container.call(this); // Execute enemy death with consolidated coin and reward logic self.executeEnemyDeath = function (enemy, enemyArray) { enemy.animationState = 'dying'; enemy.currentFrame = 3; LK.getSound('painSound').play(); enemy.isDying = true; // Use direct enemy configuration values based on type var deathRotation = Math.PI * 0.5; var numCoins = 1; // Set specific values based on enemy type if (enemy.enemyType === 'miniBoss') { deathRotation = Math.PI * 0.8; numCoins = 5; } else if (enemy.enemyType === 'ogre') { deathRotation = Math.PI * 0.6; numCoins = 1; } else if (enemy.enemyType === 'knight') { deathRotation = Math.PI * 0.7; numCoins = 1; } // Special cleanup for mini boss UI elements if (enemy.enemyType === 'miniBoss') { self.cleanupMiniBossUI(enemy); } // Execute unified death animation globalTween(enemy, { alpha: 0, scaleX: 0.3, scaleY: 0.3, rotation: deathRotation }, { duration: 800, easing: tween.easeOut, onFinish: function onFinish() { self.handleDeathRewards(enemy, numCoins); self.updateProgression(enemy); self.cleanupEnemy(enemy, enemyArray); } }); }; // Clean up mini boss UI elements self.cleanupMiniBossUI = function (enemy) { if (enemy.healthBarBg && enemy.healthBarBg.parent) { enemy.healthBarBg.destroy(); } if (enemy.healthBarFg && enemy.healthBarFg.parent) { enemy.healthBarFg.destroy(); } if (enemy.healthText && enemy.healthText.parent) { enemy.healthText.destroy(); } }; // Handle coin drops and difficulty-based rewards using pooled coins self.handleDeathRewards = function (enemy, numCoins) { var selectedDifficulty = storage.difficulty || 'NORMAL'; for (var coinIdx = 0; coinIdx < numCoins; coinIdx++) { // Create coin directly without object pool var coin = new Coin(); coin.x = enemy.x + (enemy.enemyType === 'miniBoss' ? (Math.random() - 0.5) * 200 : 0); coin.y = enemy.y - 50 + (enemy.enemyType === 'miniBoss' ? (Math.random() - 0.5) * 100 : 0); coin.isAnimating = true; coin.bobOffset = Math.random() * Math.PI * 2; coin.initialY = 0; coin.visible = true; coin.alpha = 1.0; game.addChild(coin); coins.push(coin); var coinTargetX = 120 + coinText.width / 2; var coinTargetY = 90 + coinText.height / 2; globalTween(coin, { x: coinTargetX, y: coinTargetY, scaleX: 0.5, scaleY: 0.5 }, { duration: 1000 + (enemy.enemyType === 'miniBoss' ? coinIdx * 200 : 0), easing: tween.easeOut, onFinish: function onFinish() { self.processCoinReward(enemy, selectedDifficulty, coin); } }); } }; // Process coin rewards with difficulty modifiers self.processCoinReward = function (enemy, selectedDifficulty, coin) { var coinReward = enemy.enemyType === 'miniBoss' ? 10 : 1; if (selectedDifficulty === 'FACIL') { coinReward = Math.floor(coinReward * 1.5); } else if (selectedDifficulty === 'DIFICIL') { coinReward = Math.max(1, Math.floor(coinReward * 0.75)); } coinCounter += coinReward; coinText.setText('Coins: ' + coinCounter); // Easy difficulty healing bonus if (selectedDifficulty === 'FACIL' && Math.random() < 0.15) { wizard.health = Math.min(wizard.health + 5, wizard.maxHealth); updateHealthBar(); LK.effects.flashObject(wizard, 0x00FF00, 200); } // Remove coin from tracking array for (var i = coins.length - 1; i >= 0; i--) { if (coins[i] === coin) { coins.splice(i, 1); break; } } // Destroy coin directly without object pool coin.destroy(); }; // Update kill counter and experience progression self.updateProgression = function (enemy) { var killIncrement = enemy.enemyType === 'miniBoss' ? 10 : 1; enemyKillCounter += killIncrement; killCountText.setText('Puntuacion: ' + enemyKillCounter); wizard.gainExperience(enemy.enemyType === 'miniBoss' ? 250 : 25); if (selectedEnemy === enemy) { selectedEnemy = null; } }; // Clean up enemy from arrays and game self.cleanupEnemy = function (enemy, enemyArray) { // Remove from appropriate array for (var i = enemyArray.length - 1; i >= 0; i--) { if (enemyArray[i] === enemy) { enemyArray.splice(i, 1); break; } } // Also remove from global enemy manager collections globalEnemyManager.removeFromCollection(enemy, enemy.enemyType); // Remove from all legacy arrays to ensure proper cleanup var allArrays = [enemies, ogres, knights, miniBosses]; for (var arrayIdx = 0; arrayIdx < allArrays.length; arrayIdx++) { var array = allArrays[arrayIdx]; for (var i = array.length - 1; i >= 0; i--) { if (array[i] === enemy) { array.splice(i, 1); break; } } } enemy.destroy(); // Set score reward based on enemy type var scoreReward = 10; if (enemy.enemyType === 'miniBoss') { scoreReward = 100; } else if (enemy.enemyType === 'ogre') { scoreReward = 20; } else if (enemy.enemyType === 'knight') { scoreReward = 30; } LK.setScore(LK.getScore() + scoreReward); }; return self; }); // UpgradeMenu class removed - using spell deck system instead // Unified Projectile Factory using consolidated GAME_CONFIG.projectiles var Wizard = Container.expand(function () { var self = Container.call(this); // Animation system for wizard self.currentFrame = 1; self.animationTimer = 0; self.animationSpeed = 18; // Change frame every 18 ticks (300ms at 60fps) // Create all wizard graphics frames and store them self.wizardFrames = []; for (var i = 1; i <= 4; i++) { var frameGraphics = self.attachAsset('wizard' + i, { anchorX: 0.5, anchorY: 1.0, scaleX: 2.5, scaleY: 2.5 }); frameGraphics.visible = i === 1; // Only show first frame initially self.wizardFrames.push(frameGraphics); } // Create invisible hitbox with much smaller size for more precise collision var hitbox = self.attachAsset('wizard1', { anchorX: 0.3, anchorY: 1.0, scaleX: 0.25, // Much smaller size for very precise collision scaleY: 0.3 // Much smaller size for very precise collision }); hitbox.alpha = 0; // Make hitbox invisible // Position hitbox slightly to the right to reduce left side hitbox.x = 15; // Offset hitbox to the right // Create shield visual effect self.shieldGraphics = self.attachAsset('shield', { anchorX: 0.5, anchorY: 0.5, scaleX: 3, scaleY: 3 }); self.shieldGraphics.alpha = 0.7; self.shieldGraphics.visible = false; self.attackCooldown = 0; self.level = 1; self.health = 100; self.maxHealth = 100; self.shieldActive = false; // Track shield status // Upgrade system removed - simplified wizard properties // Override intersects method to use smaller hitbox self.intersects = function (other) { return hitbox.intersects(other); }; self.update = function () { // Pause wizard when tutorial is active if (tutorial && tutorial.isActive) { return; } if (self.attackCooldown > 0) { self.attackCooldown--; } // Update shield visibility based on shield status self.shieldGraphics.visible = self.shieldActive; if (self.shieldActive) { // Animate shield with pulsing effect var pulse = 1 + Math.sin(LK.ticks * 0.15) * 0.2; self.shieldGraphics.scaleX = 3 * pulse; self.shieldGraphics.scaleY = 3 * pulse; // Slowly rotate shield self.shieldGraphics.rotation += 0.03; // Add glowing effect self.shieldGraphics.alpha = 0.6 + Math.sin(LK.ticks * 0.1) * 0.2; } // Upgrade-based abilities removed - using spell deck system instead // Simplified animation system - instant frame switching only self.animationTimer++; if (self.animationTimer >= self.animationSpeed) { self.animationTimer = 0; // Hide current frame self.wizardFrames[self.currentFrame - 1].visible = false; // Move to next frame self.currentFrame++; if (self.currentFrame > 4) { self.currentFrame = 1; } // Show next frame self.wizardFrames[self.currentFrame - 1].visible = true; } }; self.attack = function (direction) { if (self.attackCooldown <= 0) { // Default direction if none specified if (direction === undefined) { direction = 0; // Default to center path } // Get attack angle based on path direction var attackAngle = pathAngles[direction]; var attackDistance = 100; // Calculate spell position based on attack direction var spellX = self.x + Math.cos(attackAngle) * attackDistance; var spellY = self.y + Math.sin(attackAngle) * attackDistance; // Create spell effect var spell = game.addChild(LK.getAsset('spell', { anchorX: 0.5, anchorY: 0.5, x: spellX, y: spellY, scaleX: 0.5, scaleY: 0.5 })); // Animate spell with magical effects globalTween(spell, { scaleX: 2, scaleY: 2, alpha: 0 }, { duration: 500, easing: tween.easeOut, onFinish: function onFinish() { spell.destroy(); } }); // Add rotation animation to spell globalTween(spell, { rotation: Math.PI * 2 }, { duration: 500, easing: tween.linear }); self.attackCooldown = 30; // 0.5 seconds at 60fps LK.getSound('spellCast').play(); // Base damage for wizard attack var totalDamage = 1; // Attack enemies in the specified direction/path for (var i = enemies.length - 1; i >= 0; i--) { var enemy = enemies[i]; if (enemy.pathIndex === direction) { // Only hit enemies on exact same path - no distance validation enemy.takeDamage(totalDamage); } } // Attack ogres in the specified direction/path for (var i = ogres.length - 1; i >= 0; i--) { var ogre = ogres[i]; if (ogre.pathIndex === direction) { // Only hit ogres on exact same path - no distance validation ogre.takeDamage(totalDamage); } } // Attack knights in the specified direction/path for (var i = knights.length - 1; i >= 0; i--) { var knight = knights[i]; if (knight.pathIndex === direction) { // Only hit knights on exact same path - no distance validation knight.takeDamage(totalDamage); } } return true; } return false; }; self.gainExperience = function (amount) { self.experience += amount; var expNeeded = self.level * 100; if (self.experience >= expNeeded) { self.levelUp(); } }; self.levelUp = function () { self.level++; self.experience = 0; // Visual level up effect LK.effects.flashObject(self, 0xFFD700, 500); }; self.takeDamage = function (damage) { // Check if teleport invulnerability is active if (self.teleportInvuln) { GameManager.createFlashEffect(self, 0x8000FF, 200); return; } // Check if shield is active if (self.shieldActive) { // Initialize shield properties if not set if (self.maxShieldHits === undefined) { self.maxShieldHits = 1; self.currentShieldHits = 0; } // Increment shield hits self.currentShieldHits++; // Visual feedback for shield use GameManager.createFlashEffect(self, 0x00BFFF, 300); // Check if shield is depleted if (self.currentShieldHits >= self.maxShieldHits) { self.shieldActive = false; // Start shield regeneration timer var regenTime = self.shieldRegen ? 5000 : 10000; // Faster regen if improved globalTween({}, {}, { duration: regenTime, onFinish: function onFinish() { // Regenerate shield self.shieldActive = true; self.currentShieldHits = 0; // Visual feedback for shield regeneration GameManager.createFlashEffect(self, 0x00BFFF, 500); // Add shield regeneration animation globalTween(self.shieldGraphics, { scaleX: 5, scaleY: 5, alpha: 1.0 }, { duration: 600, easing: tween.easeOut, onFinish: function onFinish() { globalTween(self.shieldGraphics, { scaleX: 3, scaleY: 3, alpha: 0.7 }, { duration: 400, easing: tween.easeIn }); } }); } }); } // No damage taken, shield absorbed it return; } // Use unified damage handler for core damage logic self.health -= damage; GameManager.createFlashEffect(self, 0xFF0000, 200); if (self.health <= 0) { self.health = 0; // 10% chance to revive when dying var reviveChance = Math.random(); if (reviveChance < 0.10) { // Revival successful! self.health = Math.floor(self.maxHealth * 0.5); // Revive with 50% health // Destroy ALL enemies when revival activates (no distance restriction) var allEnemies = collisionArrayPool.getAllEnemies(); for (var enemyIdx = allEnemies.length - 1; enemyIdx >= 0; enemyIdx--) { var enemy = allEnemies[enemyIdx]; // Create destruction effect for each enemy GameManager.createFlashEffect(enemy, 0xFFD700, 500); // Create golden explosion particles GameManager.createVisualEffect('explosion', enemy, { explosionColor: 0xFFD700, explosionScale: 4.0 }); // Kill ALL enemies instantly by calling die() method enemy.die(); } // Visual effects for revival LK.effects.flashScreen(0x00FF00, 1500); // Green flash for revival GameManager.createFlashEffect(self, 0xFFD700, 1000); // Golden flash on wizard // Create healing aura effect GameManager.createVisualEffect('explosion', self, { explosionColor: 0x00FF00, explosionScale: 8.0 }); // Play spell cast sound for revival LK.getSound('spellCast').play(); // Update health bar to show revival updateHealthBar(); } else { // Game over when health reaches 0 and no revival LK.effects.flashScreen(0xFF0000, 1000); LK.showGameOver(); } } // Update health bar updateHealthBar(); // Simplified screen shake for better performance var shakeIntensity = 8; var originalX = game.x; var originalY = game.y; // Simple single shake effect globalTween(game, { x: originalX + shakeIntensity, y: originalY + shakeIntensity * 0.5 }, { duration: 100, easing: tween.easeOut, onFinish: function onFinish() { globalTween(game, { x: originalX, y: originalY }, { duration: 100, easing: tween.easeIn }); } }); }; // Force push ability removed - not used in current spell system // Freeze pulse ability removed - not used in current spell system // Thorns ability removed - not used in current spell system // Fireball launch removed - using spell deck system instead // Frame transition config removed - using simple animation only // Advanced transition removed - using simple frame switching only // Basic transition removed - using instant frame switching only // Sparkle effects removed - using simplified animations only return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x000000 // Black background for pixel art }); /**** * Game Code ****/ // PASO 1: Verificar que el sistema de tween funciona básicamente // Simplified TweenManager for basic tween functionality function _typeof5(o) { "@babel/helpers - typeof"; return _typeof5 = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof5(o); } function _typeof4(o) { "@babel/helpers - typeof"; return _typeof4 = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof4(o); } console.log('=== PASO 1: VERIFICACIÓN DEL SISTEMA TWEEN ==='); // Test básico del plugin de tween if (tween && typeof tween === 'function') { console.log('✅ Plugin de tween cargado correctamente'); // Test de funciones de easing if (tween.easeOut && typeof tween.easeOut === 'function') { console.log('✅ Funciones de easing disponibles'); console.log(' - tween.easeOut:', _typeof4(tween.easeOut)); console.log(' - tween.easeIn:', _typeof4(tween.easeIn)); console.log(' - tween.linear:', _typeof4(tween.linear)); } else { console.log('⚠️ Funciones de easing no disponibles'); } // Test básico de tween (sin ejecutar, solo verificar estructura) try { var testObj = { x: 0, alpha: 1 }; var testResult = tween(testObj, { x: 100, alpha: 0.5 }, { duration: 100, easing: tween.linear || function (t) { return t; } }); console.log('✅ Estructura básica de tween funcional'); console.log(' - Test result type:', _typeof4(testResult)); // Test de stop function if (tween.stop && typeof tween.stop === 'function') { console.log('✅ Función tween.stop disponible'); tween.stop(testObj); // Test stop sin propiedades específicas } else { console.log('⚠️ Función tween.stop no disponible'); } } catch (error) { console.log('❌ Error en test básico de tween:', error); } } else { console.log('❌ Plugin de tween NO cargado correctamente'); console.log(' - tween type:', _typeof4(tween)); console.log(' - tween value:', tween); } // Verificar compatibilidad con el TweenManager existente if (typeof TweenManager !== 'undefined' && TweenManager.initialize) { console.log('✅ TweenManager detectado, verificando compatibilidad'); var isValid = TweenManager.isPluginValid(); console.log(' - TweenManager plugin válido:', isValid); } else { console.log('⚠️ TweenManager no detectado aún (se inicializará más tarde)'); } // Test de globalTween si está disponible if (typeof globalTween === 'function') { console.log('✅ globalTween function disponible'); } else { console.log('⚠️ globalTween function no disponible aún'); } console.log('=== FIN PASO 1: VERIFICACIÓN TWEEN ==='); console.log('Resultado: Plugin de tween ' + (tween ? 'FUNCIONANDO' : 'FALLANDO')); function _typeof3(o) { "@babel/helpers - typeof"; return _typeof3 = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof3(o); } var TweenManager = { // Core tween plugin reference tweenPlugin: null, // Initialize tween manager initialize: function initialize() { console.log('Initializing simplified TweenManager'); this.tweenPlugin = LK.import('@upit/tween.v1'); if (!this.tweenPlugin || typeof this.tweenPlugin !== 'function') { console.warn('Tween plugin failed, creating fallback'); this.createFallbackPlugin(); } else { console.log('Tween plugin loaded successfully'); } // Make tween globally available window.tween = this.createTweenProxy(); return this.isPluginValid(); }, // Check if plugin is valid isPluginValid: function isPluginValid() { return this.tweenPlugin && typeof this.tweenPlugin === 'function'; }, // Create simple fallback plugin createFallbackPlugin: function createFallbackPlugin() { var self = this; this.tweenPlugin = function (target, props, options) { // Immediate property changes for basic compatibility if (target && props) { for (var prop in props) { if (target.hasOwnProperty(prop)) { target[prop] = props[prop]; } } } // Execute onFinish callback if provided if (options && options.onFinish && typeof options.onFinish === 'function') { setTimeout(function () { try { options.onFinish(); } catch (error) { console.error('Fallback tween error:', error); } }, options.duration || 0); } return null; }; // Add basic easing functions var linear = function linear(t) { return t; }; this.tweenPlugin.easeOut = linear; this.tweenPlugin.easeIn = linear; this.tweenPlugin.easeInOut = linear; this.tweenPlugin.linear = linear; this.tweenPlugin.bounceOut = linear; }, // Create tween proxy function createTweenProxy: function createTweenProxy() { var self = this; var tweenProxy = function tweenProxy(target, props, options) { return self.executeTween(target, props, options); }; // Add easing functions to proxy tweenProxy.easeOut = this.tweenPlugin ? this.tweenPlugin.easeOut : function (t) { return t; }; tweenProxy.easeIn = this.tweenPlugin ? this.tweenPlugin.easeIn : function (t) { return t; }; tweenProxy.easeInOut = this.tweenPlugin ? this.tweenPlugin.easeInOut : function (t) { return t; }; tweenProxy.linear = this.tweenPlugin ? this.tweenPlugin.linear : function (t) { return t; }; tweenProxy.bounceOut = this.tweenPlugin ? this.tweenPlugin.bounceOut : function (t) { return t; }; tweenProxy.stop = function (target, properties) { return self.stopTween(target, properties); }; return tweenProxy; }, // Execute tween with basic error handling executeTween: function executeTween(target, props, options) { if (!target) { console.warn('No target provided for tween'); return null; } if (!this.isPluginValid()) { console.warn('Tween plugin not available, using fallback'); this.createFallbackPlugin(); } try { return this.tweenPlugin(target, props, options); } catch (error) { console.error('Tween execution failed:', error); // Use LK.effects as fallback if (props && props.alpha !== undefined) { LK.effects.flashObject(target, 0xFFFFFF, options ? options.duration || 500 : 500); } if (options && options.onFinish && typeof options.onFinish === 'function') { setTimeout(options.onFinish, options.duration || 0); } return null; } }, // Stop tween stopTween: function stopTween(target, properties) { if (!this.isPluginValid()) { return; } try { return this.tweenPlugin.stop(target, properties); } catch (error) { console.error('Stop tween failed:', error); } } }; // Initialize TweenManager TweenManager.initialize(); // Create global tween function function globalTween(target, props, options) { return TweenManager.executeTween(target, props, options); } // Legacy compatibility functions function safeTween(target, props, options) { return globalTween(target, props, options); } function validateTweenAvailability() { return TweenManager.isPluginValid(); } // Ensure tween is available globally if (!window.tween) { window.tween = TweenManager.createTweenProxy(); } console.log('Simplified TweenManager initialized'); function _typeof2(o) { "@babel/helpers - typeof"; return _typeof2 = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof2(o); } var availableSpells = [{ id: 'fireball', name: 'FIREBALL', damage: 150, rarity: 'common', description: 'Lanza bola de fuego explosiva' }, { id: 'heal', name: 'HEAL', healing: 50, rarity: 'common', description: 'Restaura puntos de salud' }, { id: 'lightning', name: 'LIGHTNING', damage: 200, rarity: 'rare', description: 'Cadena de rayos entre enemigos' }]; // STEP 5: Comprehensive deck and spell data validation with enhanced spell casting integration function validateDeckData() { console.log('=== STEP 5: COMPREHENSIVE DECK DATA VALIDATION ==='); var dataWasCorrupted = false; // CRITICAL: Validate availableSpells array integrity with extended spell set if (!availableSpells || !Array.isArray(availableSpells) || availableSpells.length === 0) { console.log('⚠️ availableSpells corrupted, reinitializing with full spell set'); availableSpells = [{ id: 'fireball', name: 'FIREBALL', damage: 150, manaCost: 30, rarity: 'common', description: 'Lanza bola de fuego explosiva' }, { id: 'heal', name: 'HEAL', healing: 50, manaCost: 25, rarity: 'common', description: 'Restaura puntos de salud' }, { id: 'lightning', name: 'LIGHTNING', damage: 200, manaCost: 40, rarity: 'rare', description: 'Cadena de rayos entre enemigos' }]; dataWasCorrupted = true; } // CRITICAL: Validate each spell in availableSpells has required properties for (var i = 0; i < availableSpells.length; i++) { var spell = availableSpells[i]; if (!spell.id || !spell.name || !spell.damage && !spell.healing) { console.log('⚠️ Corrupted spell data at index:', i, spell); // Repair corrupted spell with minimal data if (!spell.id) { spell.id = 'unknown' + i; } if (!spell.name) { spell.name = 'UNKNOWN SPELL'; } if (!spell.damage && !spell.healing) { spell.damage = 50; } if (!spell.description) { spell.description = 'Spell effect'; } dataWasCorrupted = true; } } // CRITICAL: Validate activeSpellDeck exists and has required structure if (!activeSpellDeck || _typeof3(activeSpellDeck) !== 'object') { console.log('⚠️ activeSpellDeck missing, recreating with full functionality'); activeSpellDeck = { currentDeck: ['fireball', 'heal', 'lightning'], currentMana: 9999, maxMana: 9999, availableSpells: availableSpells, getSpell: function getSpell(spellId) { return _getSpell(spellId); }, canCastSpell: function canCastSpell(spellId) { return _canCastSpell(spellId); }, castSpell: function castSpell(spellId) { return _castSpell(spellId); }, getRarityColor: function getRarityColor(rarity) { return _getRarityColor(rarity); } }; dataWasCorrupted = true; } // STEP 5: Enhanced deck validation with automatic repair if (!activeSpellDeck.currentDeck || !Array.isArray(activeSpellDeck.currentDeck)) { console.log('⚠️ activeSpellDeck.currentDeck corrupted, using intelligent fallback'); // Try multiple sources for deck data var repairDeck = null; if (storage.spellDeck && Array.isArray(storage.spellDeck) && storage.spellDeck.length > 0) { repairDeck = storage.spellDeck.slice(); } else if (currentDeck && Array.isArray(currentDeck) && currentDeck.length > 0) { repairDeck = currentDeck.slice(); } else { repairDeck = ['fireball', 'heal', 'lightning']; } activeSpellDeck.currentDeck = repairDeck; dataWasCorrupted = true; } // STEP 5: Enhanced currentDeck validation with spell ID verification if (!currentDeck || !Array.isArray(currentDeck)) { console.log('⚠️ currentDeck corrupted, syncing with activeSpellDeck'); currentDeck = activeSpellDeck.currentDeck.slice(); dataWasCorrupted = true; } else { // STEP 5: Validate each spell ID in currentDeck exists in availableSpells var validatedDeck = []; for (var deckIdx = 0; deckIdx < currentDeck.length; deckIdx++) { var spellId = currentDeck[deckIdx]; var spellExists = false; for (var spellIdx = 0; spellIdx < availableSpells.length; spellIdx++) { if (availableSpells[spellIdx].id === spellId) { spellExists = true; break; } } if (spellExists) { validatedDeck.push(spellId); } else { console.log('⚠️ Invalid spell ID in deck:', spellId, '- removing'); dataWasCorrupted = true; } } if (validatedDeck.length !== currentDeck.length) { currentDeck = validatedDeck; console.log('✓ Deck cleaned of invalid spell IDs'); } } // STEP 5: Enhanced storage validation with integrity checks if (!storage.spellDeck || !Array.isArray(storage.spellDeck)) { console.log('⚠️ storage.spellDeck corrupted, syncing with activeSpellDeck'); storage.spellDeck = activeSpellDeck.currentDeck.slice(); dataWasCorrupted = true; } // STEP 5: Comprehensive deck synchronization with validation var masterDeck = activeSpellDeck.currentDeck; var decksMatch = true; // Enhanced comparison with detailed logging if (!currentDeck || !masterDeck || currentDeck.length !== masterDeck.length) { console.log('⚠️ Deck length mismatch - currentDeck:', currentDeck ? currentDeck.length : 'null', 'masterDeck:', masterDeck ? masterDeck.length : 'null'); decksMatch = false; } else { for (var d = 0; d < currentDeck.length; d++) { if (currentDeck[d] !== masterDeck[d]) { console.log('⚠️ Deck content mismatch at position', d, '- currentDeck:', currentDeck[d], 'masterDeck:', masterDeck[d]); decksMatch = false; break; } } } if (!decksMatch) { console.log('✓ Synchronizing all deck systems'); currentDeck = masterDeck.slice(); storage.spellDeck = masterDeck.slice(); dataWasCorrupted = true; } // STEP 5: Enhanced cooldown validation with cleanup if (!cardCooldowns || _typeof3(cardCooldowns) !== 'object') { console.log('⚠️ cardCooldowns corrupted, reinitializing'); cardCooldowns = {}; dataWasCorrupted = true; } else { // STEP 5: Clean up expired cooldowns and validate current ones var currentTick = LK.ticks || 0; for (var spellId in cardCooldowns) { var cooldownValue = cardCooldowns[spellId]; if (typeof cooldownValue !== 'number' || isNaN(cooldownValue)) { console.log('⚠️ Corrupted cooldown entry removed:', spellId, cooldownValue); delete cardCooldowns[spellId]; dataWasCorrupted = true; } else if (cooldownValue <= currentTick) { // Remove expired cooldowns for cleaner state delete cardCooldowns[spellId]; console.log('✓ Expired cooldown cleaned up:', spellId); } } } // STEP 5: Enhanced spellConfigs validation with complete spell data if (!spellConfigs || _typeof3(spellConfigs) !== 'object') { console.log('⚠️ spellConfigs missing, creating comprehensive configs'); spellConfigs = { fireball: { manaCost: 30, damage: 150, color: 0xFF4500, sound: 'fireWhoosh', effect: 'projectile', targetType: 'enemy', description: 'Lanza bola de fuego explosiva' }, heal: { manaCost: 25, healing: 50, color: 0x00FF00, sound: 'spellCast', effect: 'heal', targetType: 'self', description: 'Restaura puntos de salud' }, lightning: { manaCost: 40, damage: 200, color: 0x00FFFF, sound: 'iceFreeze', effect: 'chain', targetType: 'enemy', maxTargets: 3, description: 'Cadena de rayos entre enemigos' } }; dataWasCorrupted = true; } else { // STEP 5: Validate existing spellConfigs have required properties for (var configId in spellConfigs) { var config = spellConfigs[configId]; if (!config.effect) { config.effect = 'projectile'; dataWasCorrupted = true; } if (!config.color) { config.color = 0xFFFFFF; dataWasCorrupted = true; } if (!config.sound) { config.sound = 'spellCast'; dataWasCorrupted = true; } } } // STEP 5: Validate spell casting functions are operational var criticalFunctionsWork = true; try { // Test _getSpell function var testSpell = _getSpell('fireball'); if (!testSpell) { console.log('⚠️ _getSpell function not working properly'); criticalFunctionsWork = false; } // Test _canCastSpell function var canCastTest = _canCastSpell('heal'); // Don't care about result, just that function doesn't crash // Test spell config access var testConfig = spellConfigs['lightning']; if (!testConfig) { console.log('⚠️ spellConfigs access not working properly'); criticalFunctionsWork = false; } } catch (error) { console.log('⚠️ Critical spell system functions have errors:', error); criticalFunctionsWork = false; } if (!criticalFunctionsWork) { console.log('⚠️ Spell system functions need repair - data corruption detected'); dataWasCorrupted = true; } // STEP 5: Final validation and reporting if (dataWasCorrupted) { console.log('✅ STEP 5: Data corruption detected and repaired'); console.log('✓ Final activeSpellDeck.currentDeck:', activeSpellDeck.currentDeck); console.log('✓ Final currentDeck:', currentDeck); console.log('✓ Final storage.spellDeck:', storage.spellDeck); console.log('✓ Final cardCooldowns:', Object.keys(cardCooldowns)); console.log('✓ Final spellConfigs:', Object.keys(spellConfigs)); console.log('✓ All spell systems synchronized and operational'); } else { console.log('✅ STEP 5: Deck data healthy - no corruption detected'); console.log('✓ All spell systems verified and operational'); } console.log('=== STEP 5: COMPREHENSIVE VALIDATION COMPLETE ==='); console.log('🎯 Spell casting should now work flawlessly from deck menu'); console.log('🎯 All data inconsistencies have been resolved'); console.log('🎯 Spell validation will pass for all valid spells'); return !dataWasCorrupted; } // PASO 2: Función de validación de maná mejorada function validateManaSystem() { var manaWasCorrupted = false; console.log('=== VALIDATING MANA SYSTEM ==='); console.log('currentMana (before):', currentMana, _typeof2(currentMana)); console.log('activeSpellDeck.currentMana (before):', activeSpellDeck ? activeSpellDeck.currentMana : 'undefined'); // CRÍTICO: Validar currentMana con detección de corrupción if (typeof currentMana !== 'number' || isNaN(currentMana) || currentMana < 0 || currentMana > 100) { console.log('⚠️ currentMana corrupted, resetting to 100'); currentMana = 100; manaWasCorrupted = true; } // CRÍTICO: Validar activeSpellDeck existe y tiene propiedades válidas if (!activeSpellDeck) { console.log('⚠️ activeSpellDeck missing, creating with full mana'); activeSpellDeck = { currentMana: 100, maxMana: 100 }; manaWasCorrupted = true; } // CRÍTICO: Validar activeSpellDeck.currentMana if (typeof activeSpellDeck.currentMana !== 'number' || isNaN(activeSpellDeck.currentMana) || activeSpellDeck.currentMana < 0 || activeSpellDeck.currentMana > 100) { console.log('⚠️ activeSpellDeck.currentMana corrupted, resetting to 100'); activeSpellDeck.currentMana = 100; activeSpellDeck.maxMana = 100; manaWasCorrupted = true; } // CRÍTICO: Sincronizar si hay diferencias entre las dos variables if (Math.abs(currentMana - activeSpellDeck.currentMana) > 1) { console.log('⚠️ Mana desync detected - currentMana:', currentMana, 'activeSpellDeck.currentMana:', activeSpellDeck.currentMana); // Usar el valor más alto pero válido var validMana = Math.max(currentMana, activeSpellDeck.currentMana); if (validMana < 0 || validMana > 100 || typeof validMana !== 'number' || isNaN(validMana)) { validMana = 100; } currentMana = validMana; activeSpellDeck.currentMana = validMana; manaWasCorrupted = true; console.log('✅ Mana synced to:', validMana); } // CRÍTICO: Actualizar UI si hubo corrupción if (manaWasCorrupted) { console.log('🔧 Mana corruption detected and fixed'); // Mana UI updates handled by deck menu system console.log('✅ Mana system repaired - currentMana:', currentMana, 'activeSpellDeck.currentMana:', activeSpellDeck.currentMana); } else { console.log('✅ Mana system healthy - no corruption detected'); } console.log('=== MANA VALIDATION COMPLETE ==='); return !manaWasCorrupted; } // Simplified spell visual effects system var SpellVisualEffects = { // Simplified pre-cast effects - basic flash only createPreCastEffects: function createPreCastEffects(spellType, wizard) { // Basic flash effect for all spells LK.effects.flashObject(wizard, this.getSpellColor(spellType), 300); }, // Get spell color for basic effects getSpellColor: function getSpellColor(spellType) { if (spellType === 'fireball') { return 0xFF4500; } if (spellType === 'lightning') { return 0x00FFFF; } if (spellType === 'heal') { return 0x00FF88; } return 0xFFFFFF; }, // Simplified casting aura - single flash effect createCastingAura: function createCastingAura(wizard, color) { LK.effects.flashObject(wizard, color, 500); }, // Simplified spell ring - basic visual feedback createSpellRing: function createSpellRing(wizard, color) { // Simple screen flash instead of complex ring LK.effects.flashScreen(color, 200); } }; // Simplified spell configuration system - manaCost ignored var spellConfigs = { fireball: { damage: 150, color: 0xFF4500, sound: 'fireWhoosh', effect: 'projectile', targetType: 'enemy', description: 'Daño: 150 al enemigo más cercano' }, lightning: { damage: 200, color: 0x00FFFF, sound: 'iceFreeze', effect: 'chain', targetType: 'enemy', maxTargets: 3, description: 'Cadena de rayos entre enemigos' }, heal: { healing: 50, color: 0x00FF00, sound: 'spellCast', effect: 'heal', targetType: 'self', description: 'Restaura puntos de salud' } }; // STEP 4: Greatly simplified spell validation - allow all valid spells to cast function _canCastSpell(spellId) { console.log('=== SPELL VALIDATION (Step 4) ==='); console.log('SpellId:', spellId); // STEP 4: Only check absolutely essential prerequisites if (!spellId || typeof spellId !== 'string') { console.log('⚠️ Invalid spell ID provided:', spellId); return false; } // STEP 4: Remove game state restrictions - allow casting even if game not fully started // This allows deck menu spell casting to work in all states if (!wizard) { console.log('⚠️ Wizard not available - creating wizard reference'); // Try to find wizard in game if reference is lost for (var i = 0; i < game.children.length; i++) { if (game.children[i].constructor.name === 'Wizard') { wizard = game.children[i]; break; } } if (!wizard) { console.log('⚠️ Wizard still not found - spells cannot be cast'); return false; } } // STEP 4: Simplified cooldown check only var currentTick = LK.ticks || 0; if (cardCooldowns && cardCooldowns[spellId] && currentTick < cardCooldowns[spellId]) { var timeRemaining = Math.ceil((cardCooldowns[spellId] - currentTick) / 60); console.log('_canCastSpell: Card on cooldown:', spellId, '- remaining:', timeRemaining, 'seconds'); return false; } // STEP 4: MAXIMUM PERMISSIVENESS - Allow all known spells to cast var allKnownSpells = ['fireball', 'heal', 'lightning', 'shield', 'teleport', 'timeSlow', 'meteor']; if (allKnownSpells.indexOf(spellId) !== -1) { console.log('✅ STEP 4: Known spell allowed:', spellId); return true; } // STEP 4: Check spell configs and availableSpells with maximum tolerance var config = spellConfigs[spellId]; var spellData = _getSpell(spellId); if (config || spellData) { console.log('✅ STEP 4: Spell found in systems:', spellId); return true; } // STEP 4: Even if spell not found in normal places, check current deck if (activeSpellDeck && activeSpellDeck.currentDeck && activeSpellDeck.currentDeck.indexOf(spellId) !== -1) { console.log('✅ STEP 4: Spell found in active deck:', spellId); return true; } // STEP 4: Check storage deck as last resort if (storage.spellDeck && storage.spellDeck.indexOf(spellId) !== -1) { console.log('✅ STEP 4: Spell found in storage deck:', spellId); return true; } console.log('⚠️ STEP 4: Spell not found anywhere:', spellId); return false; } // Simplified spell casting function - no mana consumption function _castSpell(spellId) { console.log('=== CASTING SPELL:', spellId, '==='); var config = spellConfigs[spellId]; if (!config) { console.log('Spell config not found:', spellId); return false; } // Basic casting effects LK.effects.flashObject(wizard, config.color, 300); LK.effects.flashScreen(config.color, 200); var success = false; var result = ''; // Execute spell based on effect type if (config.effect === 'projectile') { success = executeProjectileSpell(config); result = config.description; } else if (config.effect === 'chain') { var chainResult = executeChainSpell(config); success = chainResult.success; result = 'Cadena de ' + chainResult.targets + ' rayos - Daño: ' + config.damage; } else if (config.effect === 'heal') { var healResult = executeHealSpell(config); success = healResult.success; result = healResult.actualHealing > 0 ? 'Curado: ' + healResult.actualHealing + ' HP' : 'Salud completa'; } if (success) { // Set cooldown only - mana consumption removed cardCooldowns[spellId] = LK.ticks + cardCooldownDuration; LK.getSound(config.sound).play(); showSpellDescription(spellId.toUpperCase(), result, config.color); } return success; } // Projectile spell execution (fireball) function executeProjectileSpell(config) { var allEnemies = collisionArrayPool.getAllEnemies(); var closestEnemy = null; var closestDistance = Infinity; for (var i = 0; i < allEnemies.length; i++) { var enemy = allEnemies[i]; if (enemy.isDying) { continue; } var dx = enemy.x - wizard.x; var dy = enemy.y - wizard.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < closestDistance) { closestDistance = distance; closestEnemy = enemy; } } if (closestEnemy) { // Create projectile trail var simpleTrail = game.addChild(LK.getAsset('projectileGlow', { anchorX: 0.5, anchorY: 0.5, x: wizard.x, y: wizard.y, scaleX: 1.5, scaleY: 1.5 })); simpleTrail.tint = config.color; simpleTrail.alpha = 0.7; simpleTrail.zIndex = 1610; // Animate trail tween(simpleTrail, { x: closestEnemy.x, y: closestEnemy.y, alpha: 0, scaleX: 2.5, scaleY: 2.5 }, { duration: 800, easing: tween.easeOut, onFinish: function onFinish() { if (simpleTrail.parent) { simpleTrail.destroy(); } } }); // Create projectile var projectile = ProjectileFactory.createProjectile('fireBall', wizard.x, wizard.y, closestEnemy.x, closestEnemy.y, { targetEnemy: closestEnemy, damage: config.damage }); return true; } return false; } // Chain spell execution (lightning) function executeChainSpell(config) { var allEnemies = collisionArrayPool.getAllEnemies(); var livingEnemies = []; for (var e = 0; e < allEnemies.length; e++) { var enemy = allEnemies[e]; if (!enemy.isDying && enemy.health > 0 && enemy.parent) { var dx = enemy.x - wizard.x; var dy = enemy.y - wizard.y; enemy.distanceFromWizard = Math.sqrt(dx * dx + dy * dy); livingEnemies.push(enemy); } } // Sort by distance livingEnemies.sort(function (a, b) { return a.distanceFromWizard - b.distanceFromWizard; }); var targetsHit = Math.min(config.maxTargets, livingEnemies.length); if (targetsHit > 0) { // Apply damage and effects for (var i = 0; i < targetsHit; i++) { var enemy = livingEnemies[i]; if (enemy && enemy.parent && !enemy.isDying) { enemy.health -= config.damage; LK.effects.flashObject(enemy, config.color, 600); if (enemy.health <= 0) { enemy.die(); } // Create impact effect var impact = game.addChild(LK.getAsset('projectileGlow', { anchorX: 0.5, anchorY: 0.5, x: enemy.x, y: enemy.y, scaleX: 3, scaleY: 3 })); impact.tint = config.color; impact.alpha = 1.0; tween(impact, { scaleX: 7, scaleY: 7, alpha: 0, rotation: Math.PI * 3 }, { duration: 800, delay: i * 150, easing: tween.easeOut, onFinish: function onFinish() { if (impact.parent) { impact.destroy(); } } }); } } return { success: true, targets: targetsHit }; } return { success: false, targets: 0 }; } // Heal spell execution function executeHealSpell(config) { var healthBefore = wizard.health; wizard.health = Math.min(wizard.health + config.healing, wizard.maxHealth); var actualHealing = wizard.health - healthBefore; updateHealthBar(); // Create floating heal text var healText = new Text2('+' + actualHealing + ' HP', { size: 120, fill: config.color, font: "monospace" }); healText.anchor.set(0.5, 0.5); healText.x = wizard.x; healText.y = wizard.y - 150; game.addChild(healText); tween(healText, { y: healText.y - 300, alpha: 0, scaleX: 2.0, scaleY: 2.0 }, { duration: 2500, easing: tween.easeOut, onFinish: function onFinish() { if (healText.parent) { healText.destroy(); } } }); return { success: true, actualHealing: actualHealing }; } // Legacy compatibility functions function castFireball() { return _castSpell('fireball'); } function castLightning() { return _castSpell('lightning'); } function castHeal() { return _castSpell('heal'); } function _getSpell(spellId) { for (var i = 0; i < availableSpells.length; i++) { if (availableSpells[i].id === spellId) { return availableSpells[i]; } } return null; } function _getRarityColor(rarity) { return rarity === 'rare' ? 0x0080FF : 0xFFFFFF; } var GameManager = { updateEntity: function updateEntity(entity) { if (entity && entity.update && typeof entity.update === 'function') { entity.update(); } }, createDamageText: function createDamageText(x, y, damage) { var damageText = new Text2('-' + damage, { size: 120, fill: 0xFF4444, font: "monospace" }); damageText.anchor.set(0.5, 0.5); damageText.x = x; damageText.y = y - 40; game.addChild(damageText); tween(damageText, { y: y - 120, alpha: 0 }, { duration: 1000, easing: tween.easeOut, onFinish: function onFinish() { damageText.destroy(); } }); }, createFlashEffect: function createFlashEffect(target, color, duration) { LK.effects.flashObject(target, color, duration); }, createObject: function createObject(type, config) { if (type === 'coin') { return new Coin(); } if (type === 'enemy') { return new Enemy(config); } if (type === 'projectile') { return new Projectile(config); } return null; }, createVisualEffect: function createVisualEffect(type, target, config) { // Simple visual effect creation if (type === 'explosion') { var explosion = game.addChild(LK.getAsset('projectileGlow', { anchorX: 0.5, anchorY: 0.5, x: target.x, y: target.y, scaleX: config.explosionScale || 2, scaleY: config.explosionScale || 2 })); explosion.tint = config.explosionColor || 0xFFFFFF; explosion.alpha = 0.8; tween(explosion, { scaleX: (config.explosionScale || 2) * 1.5, scaleY: (config.explosionScale || 2) * 1.5, alpha: 0 }, { duration: 500, easing: tween.easeOut, onFinish: function onFinish() { explosion.destroy(); } }); } } }; function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); } function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; } function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; } function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) { return t; } var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) { return i; } throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } // Usar valores directos en lugar de configuraciones // Usar valores directos en lugar de configuraciones complejas // Usar valores directos en lugar de configuraciones complejas // Usar valores directos en lugar de configuraciones complejas // Usar valores directos en lugar de configuraciones complejas /**** * Global State Management ****/ // Simplified global state - no gameState object needed var gameStarted = false; var selectedEnemy = null; var coinCounter = 0; var enemyKillCounter = 0; var pathLastSpawnTime = [-1, -1, -1, -1, -1]; var pathConsecutiveSpawns = [0, 0, 0, 0, 0]; var lastSpawnedPath = -1; /**** * Global Systems ****/ // Unified game management system var gameManager = GameManager; // Simple Collision Array Pool var collisionArrayPool = { allEnemies: [], clearArray: function clearArray(arrayName) { if (this[arrayName]) { this[arrayName].length = 0; } return this[arrayName]; }, getAllEnemies: function getAllEnemies() { var array = this.clearArray('allEnemies'); for (var i = 0; i < enemies.length; i++) { array.push(enemies[i]); } for (var i = 0; i < ogres.length; i++) { array.push(ogres[i]); } for (var i = 0; i < knights.length; i++) { array.push(knights[i]); } for (var i = 0; i < miniBosses.length; i++) { array.push(miniBosses[i]); } return array; } }; /**** * Unified Projectile Manager ****/ var ProjectileFactory = { // Simplified projectile creation - all types use same Projectile class createProjectile: function createProjectile(type, startX, startY, targetX, targetY, config) { var projectile = new Projectile(type); projectile.x = startX; projectile.y = startY; if (config) { for (var key in config) { projectile[key] = config[key]; } } if (targetX !== undefined && targetY !== undefined) { var dx = targetX - startX; var dy = targetY - startY; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 0) { projectile.direction.x = dx / distance; projectile.direction.y = dy / distance; } } game.addChild(projectile); projectiles.push(projectile); return projectile; }, createBasicAttack: function createBasicAttack(wizard, enemy) { return this.createProjectile('projectile', wizard.x, wizard.y, enemy.x, enemy.y, { targetEnemy: enemy, damage: 100 }); }, createSpellProjectile: function createSpellProjectile(spellType, wizard, targetX, targetY) { return this.createProjectile(spellType, wizard.x, wizard.y, targetX, targetY, { damage: 150 }); }, removeProjectile: function removeProjectile(projectile) { for (var i = projectiles.length - 1; i >= 0; i--) { if (projectiles[i] === projectile) { projectiles.splice(i, 1); break; } } if (projectile.parent) { projectile.destroy(); } } }; // Removed globalDamageHandler and globalEffectManager aliases - use GameManager directly /**** * Game Objects (Legacy compatibility) ****/ // Direct global arrays - no gameState needed var enemies = []; var ogres = []; var knights = []; var miniBosses = []; var coins = []; var projectiles = []; // Single game menu object var gameMenu; // Create tutorial system first (initially hidden) var tutorial = game.addChild(new Tutorial()); tutorial.visible = false; // Create and show game menu gameMenu = game.addChild(new GameMenu()); // Create toggle button for in-game card panel var cardToggleButton = LK.getAsset('spellCard', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.0, scaleY: 1.0 }); LK.gui.topRight.addChild(cardToggleButton); cardToggleButton.x = -80; cardToggleButton.y = 80; cardToggleButton.tint = 0x4a0e4e; cardToggleButton.visible = false; var cardToggleText = new Text2('CARTAS', { size: 35, fill: 0xFFFFFF, font: "monospace" }); cardToggleText.anchor.set(0.5, 0.5); cardToggleText.x = -80; cardToggleText.y = 80; LK.gui.topRight.addChild(cardToggleText); cardToggleText.visible = false; // Add interaction to toggle button cardToggleButton.down = function (x, y, obj) { // Visual feedback for button press LK.effects.flashObject(obj, 0x4169E1, 200); tween(obj, { scaleX: 1.2, scaleY: 1.2 }, { duration: 100, easing: tween.easeOut, onFinish: function onFinish() { tween(obj, { scaleX: 1.0, scaleY: 1.0 }, { duration: 100, easing: tween.easeIn }); } }); toggleCardPanel(); }; // Simple spell system variables var currentDeck = storage.spellDeck || ['fireball', 'heal', 'lightning']; // Mana system completely removed - spells use cooldown-only system var currentMana = 9999; // Mana system neutralized - set to maximum value var maxMana = 9999; // Mana system neutralized - set to maximum value var spellCooldowns = {}; var cardCooldowns = {}; // Track cooldown for each card var cardCooldownDuration = 300; // 5 seconds at 60fps storage.spellDeck = currentDeck.slice(); // Basic system validation console.log('=== SYSTEM VALIDATION ==='); console.log('currentDeck:', currentDeck); console.log('Mana system removed - using cooldown-only spells'); console.log('Mana system removed - using cooldown-only spells'); console.log('Tween system available:', validateTweenAvailability()); var activeSpellDeck = { currentDeck: currentDeck.slice(), availableSpells: availableSpells, getSpell: function getSpell(spellId) { return _getSpell(spellId); }, canCastSpell: function canCastSpell(spellId) { return _canCastSpell(spellId); }, castSpell: function castSpell(spellId, targetX, targetY) { console.log('=== CASTING SPELL FROM ACTIVE DECK ==='); console.log('Spell ID:', spellId); return castSpell(spellId); }, getRarityColor: function getRarityColor(rarity) { return _getRarityColor(rarity); } }; // Simple tween validation at startup if (validateTweenAvailability()) { console.log('Tween system ready'); } else { console.warn('Tween system using fallback'); } // PASO 1: Verificar activeSpellDeck después de su creación console.log('activeSpellDeck created successfully:'); console.log('- currentDeck:', activeSpellDeck.currentDeck); console.log('- currentMana:', activeSpellDeck.currentMana); console.log('- maxMana:', activeSpellDeck.maxMana); console.log('- availableSpells count:', activeSpellDeck.availableSpells.length); // Initialize spell unlocking system var lastUnlockCheck = 0; function checkSpellUnlocks() { if (!gameMenu.spellDeck) { gameMenu.spellDeck = new SpellDeck(); } // Only check unlocks when kill counter changes if (enemyKillCounter === lastUnlockCheck) { return; } lastUnlockCheck = enemyKillCounter; // Unlock spells based on achievements with messages if (enemyKillCounter >= 10 && !storage.lightningUnlocked) { storage.lightningUnlocked = true; gameMenu.spellDeck.unlockSpell('lightning'); LK.effects.flashScreen(0x00FFFF, 500); showSpellUnlockMessage('LIGHTNING', 'Cadena de rayos entre enemigos'); } if (enemyKillCounter >= 25 && !storage.shieldUnlocked) { storage.shieldUnlocked = true; gameMenu.spellDeck.unlockSpell('shield'); LK.effects.flashScreen(0x0080FF, 500); showSpellUnlockMessage('MAGIC SHIELD', 'Inmunidad temporal al daño'); } if (enemyKillCounter >= 50 && !storage.teleportUnlocked) { storage.teleportUnlocked = true; gameMenu.spellDeck.unlockSpell('teleport'); LK.effects.flashScreen(0x8000FF, 500); showSpellUnlockMessage('TELEPORT', 'Mueve instantáneamente al mago'); } if (enemyKillCounter >= 75 && !storage.timeSlowUnlocked) { storage.timeSlowUnlocked = true; gameMenu.spellDeck.unlockSpell('timeSlow'); LK.effects.flashScreen(0xFF8000, 500); showSpellUnlockMessage('TIME SLOW', 'Ralentiza todos los enemigos'); } if (enemyKillCounter >= 100 && !storage.meteorUnlocked) { storage.meteorUnlocked = true; gameMenu.spellDeck.unlockSpell('meteor'); LK.effects.flashScreen(0xFF0000, 500); showSpellUnlockMessage('METEOR', 'Daño masivo en área'); } } function showSpellUnlockMessage(spellName, description) { var unlockText = new Text2('NUEVO HECHIZO DESBLOQUEADO!\n' + spellName + '\n' + description, { size: 60, fill: 0xFFD700, font: "monospace" }); unlockText.anchor.set(0.5, 0.5); unlockText.x = 2048 / 2; unlockText.y = 2732 / 2; game.addChild(unlockText); // Animate unlock message tween(unlockText, { scaleX: 1.2, scaleY: 1.2, alpha: 0.8 }, { duration: 2000, easing: tween.easeOut, onFinish: function onFinish() { tween(unlockText, { alpha: 0, y: unlockText.y - 200 }, { duration: 1000, easing: tween.easeIn, onFinish: function onFinish() { if (unlockText.parent) { unlockText.destroy(); } } }); } }); } // Function to show spell description when cast function showSpellDescription(spellName, description, color) { var descText = new Text2(spellName + '\n' + description, { size: 50, fill: color, font: "monospace" }); descText.anchor.set(0.5, 0.5); descText.x = wizard.x; descText.y = wizard.y - 200; game.addChild(descText); // Magical sparkles removed for simplification // Animate description tween(descText, { y: descText.y - 80, alpha: 0 }, { duration: 1500, easing: tween.easeOut, onFinish: function onFinish() { if (descText.parent) { descText.destroy(); } } }); } // Create unified path system - all 5 paths at once var paths = []; var knightX = 2048 / 2; var knightY = 2732 - 250; var pathAngles = [-Math.PI / 2, -Math.PI / 3, -2 * Math.PI / 3, Math.PI / 6, 5 * Math.PI / 6]; var wizardX = knightX; var wizardY = 2732 - 600; // Unified path creation function function createUnifiedPaths() { var spawnPositions = [{ x: 2048 / 2, y: -100 }, { x: 2048 + 50, y: -50 }, { x: -50, y: -50 }, { x: -100, y: 2732 / 2 + 400 }, { x: 2048 + 100, y: 2732 / 2 + 400 }]; var pathAngles = [-Math.PI / 2, -Math.PI / 3, -2 * Math.PI / 3, Math.PI / 6, 5 * Math.PI / 6]; for (var p = 0; p < 5; p++) { var angle = pathAngles[p]; var spawnPos = spawnPositions[p]; var actualPathLength = Math.sqrt((spawnPos.x - wizardX) * (spawnPos.x - wizardX) + (spawnPos.y - wizardY) * (spawnPos.y - wizardY)); // Create stone segments var segmentSize = 80; var numSegments = Math.floor(actualPathLength / segmentSize); for (var s = 0; s < numSegments; s++) { var segmentDistance = s * segmentSize + segmentSize / 2; var segmentX = spawnPos.x - Math.cos(angle) * segmentDistance; var segmentY = spawnPos.y - Math.sin(angle) * segmentDistance; if (segmentX >= -100 && segmentX <= 2148 && segmentY >= -100 && segmentY <= 2832) { var stoneSegment = game.addChild(LK.getAsset('stonePath', { anchorX: 0.5, anchorY: 0.5, x: segmentX, y: segmentY, scaleX: 2.0, scaleY: 2.0, rotation: angle + Math.PI / 2 })); stoneSegment.alpha = 0; stoneSegment.visible = false; stoneSegment.pathIndex = p; } } // Create collision area var centerX = (spawnPos.x + wizardX) / 2; var centerY = (spawnPos.y + wizardY) / 2; var path = game.addChild(LK.getAsset('pathSelector', { anchorX: 0.5, anchorY: 0.5, x: centerX, y: centerY, scaleX: 4, scaleY: actualPathLength / 60, rotation: angle + Math.PI / 2 })); path.alpha = 0; path.visible = false; path.pathIndex = p; // Add path number var pathNumber = new Text2((p + 1).toString(), { size: 120, fill: 0xFFD700, font: "monospace" }); pathNumber.anchor.set(0.5, 0.5); pathNumber.x = spawnPos.x; pathNumber.y = spawnPos.y - 80; pathNumber.visible = false; pathNumber.pathIndex = p; game.addChild(pathNumber); // Add touch handler path.down = function (x, y, obj) { wizard.attack(obj.pathIndex); }; paths.push(path); } } // Create all paths createUnifiedPaths(); // Pixel art scaling handled by engine automatically // Set fondodelacueva as the actual game background var backgroundMap = game.addChild(LK.getAsset('fondodelacueva', { anchorX: 0, anchorY: 0, scaleX: 19.5, scaleY: 26.0, x: 0, y: 0 })); // Send background to the back but use a less extreme z-index backgroundMap.zIndex = -100; // Hide background initially during menu backgroundMap.visible = false; backgroundMap.alpha = 1.0; // Create three wizard position points with clear numbering - all moved much higher up var wizardPositions = [{ x: 2048 / 2, y: 2732 - 800 }, // Position 1: Center - moved much higher up { x: 100, y: 2732 - 800 }, // Position 2: Left corner - moved much higher up { x: 2048 - 100, y: 2732 - 800 } // Position 3: Right corner - moved much higher up ]; var currentWizardPosition = 0; // Track current position (0, 1, or 2) // Create wizard at first position var wizard = game.addChild(new Wizard()); wizard.x = wizardPositions[currentWizardPosition].x; wizard.y = wizardPositions[currentWizardPosition].y; wizard.visible = false; // PASO 2: Simplified wizard movement function with minimal validation function moveWizardToNextPosition() { console.log('=== PASO 2: WIZARD MOVEMENT ATTEMPT ==='); console.log('Current wizard position index:', currentWizardPosition); console.log('Wizard exists:', !!wizard); console.log('Wizard has parent:', wizard && !!wizard.parent); console.log('Tween function available:', typeof tween === 'function'); console.log('GameStarted:', gameStarted); // PASO 2: Minimal validation - only check absolute essentials if (!wizard) { console.log('⚠️ PASO 2: Wizard object missing - cannot move'); return; } // PASO 2: Skip parent validation - wizard might be valid without parent check // PASO 2: Skip property type validation - assume wizard.x and wizard.y are valid // PASO 2: Calculate next position currentWizardPosition = (currentWizardPosition + 1) % 3; var targetPos = wizardPositions[currentWizardPosition]; console.log('Target position:', targetPos); // PASO 2: Skip target position validation - use fallback if needed if (!targetPos) { console.log('⚠️ PASO 2: Target position missing, using fallback'); targetPos = { x: 2048 / 2, y: 2732 - 400 }; // Default center position } // PASO 2: Simplified movement - try multiple methods console.log('✓ PASO 2: Attempting wizard movement to:', targetPos); // PASO 2: Method 1 - Direct position change (immediate) console.log('Method 1: Direct position change'); wizard.x = targetPos.x; wizard.y = targetPos.y; console.log('✓ Direct movement completed to:', { x: wizard.x, y: wizard.y }); // PASO 2: Method 2 - Tween animation (if available) if (typeof tween === 'function') { console.log('Method 2: Tween animation'); try { tween(wizard, { x: targetPos.x, y: targetPos.y }, { duration: 500, easing: tween.easeInOut || function (t) { return t; } }); console.log('✓ Tween animation started'); } catch (error) { console.log('⚠️ Tween animation failed:', error); } } else { console.log('⚠️ Tween function not available, using direct movement only'); } // PASO 3: Improved indicator color update with comprehensive logic console.log('Method 3: Update position indicators'); try { var indicatorsUpdated = 0; for (var j = 0; j < positionIndicators.length; j++) { var indicator = positionIndicators[j]; if (indicator && indicator.positionIndex !== undefined) { // This is a position indicator (not a label) if (indicator.positionIndex === currentWizardPosition) { indicator.tint = 0x00FF00; // Green for current position console.log('✓ PASO 3: Set indicator', indicator.positionIndex, 'to GREEN (current)'); } else { indicator.tint = 0x4169E1; // Blue for available positions console.log('✓ PASO 3: Set indicator', indicator.positionIndex, 'to BLUE (available)'); } indicatorsUpdated++; } } console.log('✓ PASO 3: Position indicators updated correctly:', indicatorsUpdated, 'indicators processed'); console.log('✓ PASO 3: Current position', currentWizardPosition, 'should be GREEN, others should be BLUE'); } catch (error) { console.log('⚠️ Position indicator update failed:', error); } // PASO 2: Visual feedback console.log('Method 4: Visual feedback'); try { LK.effects.flashObject(wizard, 0x00FF88, 300); console.log('✓ Visual feedback applied'); } catch (error) { console.log('⚠️ Visual feedback failed:', error); } console.log('=== PASO 2: WIZARD MOVEMENT COMPLETED ==='); console.log('New wizard position:', { x: wizard.x, y: wizard.y }); console.log('Current position index:', currentWizardPosition); } // Create position indicators with correct initial colors var positionIndicators = []; for (var i = 0; i < 3; i++) { var indicator = game.addChild(LK.getAsset('pathSelector', { anchorX: 0.5, anchorY: 0.5, x: wizardPositions[i].x, y: wizardPositions[i].y + 50, scaleX: 4.0, scaleY: 4.0 })); indicator.alpha = 0.8; // PASO 3: Fix initial color assignment based on actual wizard position indicator.tint = i === currentWizardPosition ? 0x00FF00 : 0x4169E1; // Green for current, blue for others indicator.visible = false; indicator.positionIndex = i; indicator.interactive = true; // Ensure indicators can receive touch events console.log('✓ PASO 3: Created indicator', i, 'with color:', i === currentWizardPosition ? 'GREEN' : 'BLUE'); // PASO 3: Simplified wizard movement with direct execution indicator.down = function (x, y, obj) { console.log('=== PASO 3: SIMPLIFIED WIZARD MOVEMENT ==='); console.log('Clicked position index:', obj.positionIndex); console.log('Current position index:', currentWizardPosition); console.log('Game started:', gameStarted); console.log('Wizard available:', !!wizard); // PASO 3: Only essential validation - game must be started and wizard must exist if (!gameStarted) { console.log('⚠️ Game not started, ignoring click'); return; } if (!wizard) { console.log('⚠️ No wizard available, ignoring click'); return; } // PASO 3: Direct movement execution - no validation blocking console.log('✓ PASO 3: Executing direct movement to position', obj.positionIndex); // STEP 1: Update position immediately currentWizardPosition = obj.positionIndex; var targetPos = wizardPositions[currentWizardPosition]; // STEP 2: Move wizard directly wizard.x = targetPos.x; wizard.y = targetPos.y; console.log('✓ PASO 3: Wizard moved to:', { x: wizard.x, y: wizard.y }); // STEP 3: Update all indicator colors in single pass for (var j = 0; j < positionIndicators.length; j++) { var indicator = positionIndicators[j]; if (indicator && indicator.positionIndex !== undefined) { if (indicator.positionIndex === currentWizardPosition) { indicator.tint = 0x00FF00; // Green for current position } else { indicator.tint = 0x4169E1; // Blue for available positions } } } // STEP 4: Visual feedback LK.effects.flashObject(obj, 0x00FF88, 300); console.log('=== PASO 3: MOVEMENT COMPLETED SUCCESSFULLY ==='); }; // Add position label text with clear numbering var positionNames = ['1 CENTRO', '2 IZQUIERDA', '3 DERECHA']; var positionLabel = new Text2(positionNames[i], { size: 40, fill: 0xFFFFFF, font: "monospace" }); positionLabel.anchor.set(0.5, 0.5); positionLabel.x = wizardPositions[i].x; positionLabel.y = wizardPositions[i].y + 120; positionLabel.visible = false; positionLabel.positionIndex = i; game.addChild(positionLabel); positionIndicators.push(indicator); positionIndicators.push(positionLabel); // Also track the label } // UI Elements // Removed scoreText and levelText to eliminate stray characters in top right var coinCounter = 0; var enemyKillCounter = 0; var coinText = new Text2('Coins: 0', { size: 60, fill: 0xFFD700, font: "monospace" }); coinText.anchor.set(0, 0); LK.gui.topLeft.addChild(coinText); coinText.x = 120; coinText.y = 90; coinText.visible = false; var killCountText = new Text2('Puntuacion: 0', { size: 60, fill: 0xFFFFFF, font: "monospace" }); killCountText.anchor.set(0, 0); LK.gui.topLeft.addChild(killCountText); killCountText.x = 120; killCountText.y = 50; killCountText.visible = false; var tapText = new Text2('TAP ENEMIES TO ATTACK!', { size: 80, fill: 0x00FF00, font: "monospace" }); tapText.anchor.set(0.5, 0.5); LK.gui.center.addChild(tapText); tapText.y = -400; tapText.visible = false; // Health bar UI var healthBarBg = LK.getAsset('healthBarBg', { anchorX: 0, anchorY: 0, scaleX: 2, scaleY: 1 }); LK.gui.topLeft.addChild(healthBarBg); healthBarBg.x = 120; healthBarBg.y = 20; healthBarBg.visible = false; var healthBar = LK.getAsset('healthBar', { anchorX: 0, anchorY: 0, scaleX: 2, scaleY: 1 }); LK.gui.topLeft.addChild(healthBar); healthBar.x = 120; healthBar.y = 20; healthBar.visible = false; var healthText = new Text2('Health: 100/100', { size: 60, fill: 0xFFFFFF, font: "monospace" }); healthText.anchor.set(0, 0); LK.gui.topLeft.addChild(healthText); healthText.x = 120; healthText.y = 65; healthText.visible = false; // Mana UI elements removed - using cooldown-only spell system // Mana bar removed - spells use cooldown system only // Mana text removed - spells use cooldown system only // updateManaDisplay function removed - mana system eliminated function updateHealthBar() { var healthPercent = wizard.health / wizard.maxHealth; healthBar.scaleX = healthPercent; healthText.setText('Health: ' + wizard.health + '/' + wizard.maxHealth); // Change color based on health if (healthPercent > 0.6) { healthBar.tint = 0x00ff00; // Green } else if (healthPercent > 0.3) { healthBar.tint = 0xffff00; // Yellow } else { healthBar.tint = 0xff0000; // Red } } // Mana display function - no-op since mana system is removed function updateManaDisplay() { // Mana system removed - using cooldown-only spell system // This function is kept for compatibility but does nothing console.log('updateManaDisplay called - mana system is disabled'); } // Enemy spawning variables var enemySpawnTimer = 0; var lastSpawnedPath = -1; // Track the last spawned path var consecutiveSpawns = 0; // Track consecutive spawns from same path // Cooldown system variables var pathLastSpawnTime = [-1, -1, -1, -1, -1]; // Track last spawn time for each path var pathConsecutiveSpawns = [0, 0, 0, 0, 0]; // Track consecutive spawns per path var pathCooldownDuration = 300; // 5 seconds at 60fps // Unified SpawnManager for streamlined spawn control and enemy lifecycle var SpawnManager = { // Consolidated spawn configuration spawnConfig: { skeleton: { interval: 90, maxCount: 15, startAt: 0 }, ogre: { interval: 180, maxCount: 4, startAt: 15 }, knight: { interval: 300, maxCount: 3, startAt: 30 }, miniBoss: { interval: 60, maxCount: 1, startAt: 80, endAt: 85, chance: 0.02 } }, // Unified spawn timers spawnTimers: { skeleton: 0, ogre: 0, knight: 0, miniBoss: 0 }, // Streamlined spawn validation canSpawnEnemy: function canSpawnEnemy(type, difficulty, totalEnemies) { var config = this.spawnConfig[type]; if (!config) { return false; } var collection = []; if (type === 'skeleton') { collection = enemies; } else if (type === 'ogre') { collection = ogres; } else if (type === 'knight') { collection = knights; } else if (type === 'miniBoss') { collection = miniBosses; } var timer = this.spawnTimers[type]; // Usar valores directos para thresholds var threshold = 0; if (type === 'ogre') { threshold = 15; } else if (type === 'knight') { threshold = 30; } else if (type === 'miniBoss') { threshold = 80; } // Universal spawn conditions with type-specific rules var baseConditions = timer >= config.interval && enemyKillCounter >= threshold && collection.length < config.maxCount && totalEnemies < 25; // Special conditions for specific types if (type === 'miniBoss') { return baseConditions && enemyKillCounter <= config.endAt && miniBosses.length === 0 && Math.random() < config.chance; } return baseConditions && miniBosses.length === 0; }, // Unified spawn execution with specific enemy classes executeSpawn: function executeSpawn(type, difficulty, level) { var totalEnemies = globalEnemyManager.getAllEnemies().length; if (!this.canSpawnEnemy(type, difficulty, totalEnemies)) { return null; } var enemy = globalEnemyManager.createEnemy(type, difficulty, level); if (enemy) { game.addChild(enemy); // Add directly to global arrays if (type === 'skeleton') { enemies.push(enemy); } else if (type === 'ogre') { ogres.push(enemy); } else if (type === 'knight') { knights.push(enemy); } else if (type === 'miniBoss') { miniBosses.push(enemy); } this.spawnTimers[type] = 0; // Update path tracking for skeleton spawns if (type === 'skeleton') { pathConsecutiveSpawns[enemy.pathIndex]++; pathLastSpawnTime[enemy.pathIndex] = LK.ticks; lastSpawnedPath = enemy.pathIndex; } } return enemy; }, // Streamlined interval updates updateSpawnIntervals: function updateSpawnIntervals(difficulty, level) { // Usar valores directos para skeleton if (difficulty === 'FACIL') { this.spawnConfig.skeleton.interval = Math.max(60, 120 - level * 5); this.spawnConfig.ogre.interval = 240; this.spawnConfig.knight.interval = 400; } else if (difficulty === 'NORMAL') { this.spawnConfig.skeleton.interval = Math.max(40, 90 - level * 6); this.spawnConfig.ogre.interval = 180; this.spawnConfig.knight.interval = 300; } else { // DIFICIL this.spawnConfig.skeleton.interval = Math.max(20, 60 - level * 4); this.spawnConfig.ogre.interval = 120; this.spawnConfig.knight.interval = 200; } }, // Unified timer updates updateTimers: function updateTimers() { for (var type in this.spawnTimers) { this.spawnTimers[type]++; } // Check if all paths are in cooldown and reset if needed var allPathsInCooldown = true; for (var pathIdx = 0; pathIdx < 5; pathIdx++) { if (pathConsecutiveSpawns[pathIdx] < 2) { allPathsInCooldown = false; break; } } if (allPathsInCooldown) { // Reset all path cooldowns to prevent spawn deadlock for (var pathIdx = 0; pathIdx < 5; pathIdx++) { pathConsecutiveSpawns[pathIdx] = 0; pathLastSpawnTime[pathIdx] = -1; } } }, // Streamlined spawn cycle for all enemy types processSpawnCycle: function processSpawnCycle(difficulty, level) { this.updateSpawnIntervals(difficulty, level); this.updateTimers(); // Clean up any null/destroyed enemies from collections first this.cleanupDestroyedEnemies(); var enemyTypes = ['skeleton', 'ogre', 'knight', 'miniBoss']; for (var i = 0; i < enemyTypes.length; i++) { this.executeSpawn(enemyTypes[i], difficulty, level); } }, // Add cleanup method for destroyed enemies cleanupDestroyedEnemies: function cleanupDestroyedEnemies() { // Clean up global arrays directly var allArrays = [enemies, ogres, knights, miniBosses]; for (var arrayIdx = 0; arrayIdx < allArrays.length; arrayIdx++) { var array = allArrays[arrayIdx]; for (var i = array.length - 1; i >= 0; i--) { if (!array[i] || !array[i].parent || array[i].isDying) { array.splice(i, 1); } } } } }; // Game input handling with spell casting support game.down = function (x, y, obj) { // If game hasn't started, ignore taps if (!gameStarted) { return; } // Check if tap is in lower third of screen (wizard area) for position cycling var lowerThirdY = 2732 * 0.66; // Lower third of screen if (y >= lowerThirdY && wizard) { console.log('=== TAP TO CYCLE WIZARD POSITION ==='); console.log('Tap in wizard area detected at Y:', y); // Cycle to next position moveWizardToNextPosition(); // Visual feedback LK.effects.flashScreen(0x00FF88, 200); return; } // Removed automatic fireball casting - spells are now manual only through spell slots // Tap-to-attack is now handled directly by individual enemies // No need for path-based attacks since enemies handle their own tap events }; // Initialize unified management systems for streamlined game architecture var globalEnemyManager = new EnemyManager(); var globalDeathHandler = new UnifiedDeathHandler(); // Unified death animation function for all enemy types function createEnemyDeathAnimation(enemy, enemyType, enemyArray) { globalDeathHandler.executeEnemyDeath(enemy, enemyArray); } // In-game spell card panel system var inGameCardPanel = null; var cardPanelVisible = false; var cardPanelElements = []; // Create in-game card panel function createInGameCardPanel() { if (inGameCardPanel) { return inGameCardPanel; } // Create simplified background panel with lower z-index to avoid event conflicts inGameCardPanel = game.addChild(LK.getAsset('spellCardBg', { anchorX: 0.5, anchorY: 1.0, x: 2048 / 2, y: 2732, scaleX: 20, scaleY: 4 })); inGameCardPanel.tint = 0x1a0a2e; inGameCardPanel.alpha = 0.7; // Reduced opacity to minimize interference inGameCardPanel.zIndex = 1500; // Lower than cards (1510+) to ensure cards are on top inGameCardPanel.interactive = false; // Prevent background from capturing events inGameCardPanel.visible = false; return inGameCardPanel; } // Toggle card panel visibility function toggleCardPanel() { if (!gameStarted) { return; } cardPanelVisible = !cardPanelVisible; if (cardPanelVisible) { showInGameCardPanel(); } else { hideInGameCardPanel(); } } // Show in-game card panel function showInGameCardPanel() { if (!inGameCardPanel) { createInGameCardPanel(); } // Clear existing card elements clearCardPanelElements(); inGameCardPanel.visible = true; // Get current deck from activeSpellDeck var currentDeck = activeSpellDeck ? activeSpellDeck.currentDeck : ['fireball', 'heal', 'lightning']; // Create cards in single layer with simplified z-index structure for (var i = 0; i < currentDeck.length && i < 5; i++) { var spellId = currentDeck[i]; var spell = _getSpell(spellId); if (!spell) { continue; } var cardX = 300 + i * 300; var cardY = 2732 - 150; // Check cooldown status first to avoid re-calculating var currentTick = LK.ticks || 0; var isOnCooldown = cardCooldowns[spellId] && currentTick < cardCooldowns[spellId]; // Create card background with much higher z-index than panel (1500) var cardBg = game.addChild(LK.getAsset('spellCard', { anchorX: 0.5, anchorY: 0.5, x: cardX, y: cardY, scaleX: 2.5, scaleY: 3.0 })); cardBg.spellId = spellId; cardBg.interactive = true; // Ensure interactivity is set before adding to game cardBg.zIndex = 1610; // Higher than background panel (1500) and other UI elements cardPanelElements.push(cardBg); // Simplified ready-to-cast state without glow conflicts if (!isOnCooldown) { cardBg.alpha = 0.95; cardBg.tint = 0x00FF88; // Green for ready // Simplified pulsing animation without overlays tween(cardBg, { alpha: 1.0, scaleX: 2.6, scaleY: 3.1 }, { duration: 1000, easing: tween.easeInOut, onFinish: function onFinish() { tween(cardBg, { alpha: 0.95, scaleX: 2.5, scaleY: 3.0 }, { duration: 1000, easing: tween.easeInOut }); } }); } else { // Simplified cooldown state cardBg.alpha = 0.4; cardBg.tint = 0x666666; // Gray out on cooldown // Add cooldown text directly on card var timeRemaining = Math.ceil((cardCooldowns[spellId] - currentTick) / 60); var cooldownText = new Text2(timeRemaining.toString(), { size: 60, fill: 0xFFFFFF, font: "monospace" }); cooldownText.anchor.set(0.5, 0.5); cooldownText.x = cardX; cooldownText.y = cardY; cooldownText.zIndex = 1620; // Higher than card background (1610) game.addChild(cooldownText); cardPanelElements.push(cooldownText); } // Event handler with improved touch detection cardBg.down = function (x, y, obj) { console.log('=== CARD TOUCHED (Step 4) ==='); console.log('✅ PASO 2B SUCCESS: Card touch event triggered!'); console.log('This means disabling z-index sorting FIXED the issue'); console.log('Card spellId:', obj.spellId); if (!obj || !obj.spellId) { console.log('⚠️ Invalid card object'); return; } // Enhanced cooldown check var currentTick = LK.ticks || 0; var isOnCooldown = cardCooldowns[obj.spellId] && currentTick < cardCooldowns[obj.spellId]; if (isOnCooldown) { console.log('⚠️ Spell on cooldown'); LK.effects.flashObject(obj, 0xFF0000, 300); return; } // Cast spell console.log('✓ Attempting spell cast'); LK.effects.flashObject(obj, 0x00FF88, 300); var success = _castSpell(obj.spellId); if (success) { console.log('✓ Spell cast successful'); // Auto-close panel after successful cast setTimeout(function () { if (cardPanelVisible) { toggleCardPanel(); } }, 600); } else { console.log('⚠️ Spell cast failed'); LK.effects.flashObject(obj, 0xFF0000, 300); } }; // Create simplified card text without z-index conflicts var nameText = new Text2(spell.name, { size: 28, fill: 0xFFFFFF, font: "monospace" }); nameText.anchor.set(0.5, 0.5); nameText.x = cardX; nameText.y = cardY - 50; nameText.zIndex = 1620; // Higher than card background (1610) game.addChild(nameText); cardPanelElements.push(nameText); // Effect text without mana cost var effectText = ''; if (spell.damage) { effectText = 'DMG: ' + spell.damage; } if (spell.healing) { effectText = 'HEAL: ' + spell.healing; } if (effectText) { var effectLabel = new Text2(effectText, { size: 24, fill: 0xFFD700, font: "monospace" }); effectLabel.anchor.set(0.5, 0.5); effectLabel.x = cardX; effectLabel.y = cardY + 50; effectLabel.zIndex = 1620; // Higher than card background (1610) game.addChild(effectLabel); cardPanelElements.push(effectLabel); } } // Simplified instruction text var panelInstruction = new Text2('TOCA CARTAS PARA LANZAR HECHIZOS', { size: 45, fill: 0x00FF88, font: "monospace" }); panelInstruction.anchor.set(0.5, 0.5); panelInstruction.x = 2048 / 2; panelInstruction.y = 2732 - 350; panelInstruction.zIndex = 1620; game.addChild(panelInstruction); cardPanelElements.push(panelInstruction); // Simplified hide button var hideButton = game.addChild(LK.getAsset('coin', { anchorX: 0.5, anchorY: 0.5, x: 2048 - 100, y: 2732 - 300, scaleX: 1.5, scaleY: 1.5 })); hideButton.tint = 0xFF4444; hideButton.zIndex = 1620; hideButton.interactive = true; hideButton.down = function (x, y, obj) { LK.effects.flashObject(obj, 0xFF6666, 200); toggleCardPanel(); }; cardPanelElements.push(hideButton); var hideText = new Text2('CERRAR', { size: 40, fill: 0xFFFFFF, font: "monospace" }); hideText.anchor.set(0.5, 0.5); hideText.x = 2048 - 100; hideText.y = 2732 - 300; hideText.zIndex = 1625; game.addChild(hideText); cardPanelElements.push(hideText); // STEP 1: COMPREHENSIVE CARD RENDERING DIAGNOSTICS console.log('=== CARD PANEL CREATION DIAGNOSTICS (Step 1) ==='); console.log('Panel visible:', inGameCardPanel ? inGameCardPanel.visible : 'panel missing'); console.log('Panel position:', inGameCardPanel ? { x: inGameCardPanel.x, y: inGameCardPanel.y } : 'N/A'); console.log('Panel z-index:', inGameCardPanel ? inGameCardPanel.zIndex : 'N/A'); console.log('Current deck:', currentDeck); console.log('Cards being created:', cardPanelElements.length); // STEP 1: VERIFY EACH CARD WAS CREATED PROPERLY for (var debugIdx = 0; debugIdx < cardPanelElements.length; debugIdx++) { var element = cardPanelElements[debugIdx]; if (element && element.spellId) { console.log('Card #' + debugIdx + ':'); console.log(' - SpellID:', element.spellId); console.log(' - Position:', { x: element.x, y: element.y }); console.log(' - Scale:', { x: element.scaleX, y: element.scaleY }); console.log(' - Visible:', element.visible); console.log(' - Interactive:', element.interactive); console.log(' - Parent exists:', element.parent ? 'yes' : 'no'); console.log(' - Z-index:', element.zIndex); console.log(' - Has down handler:', typeof element.down === 'function'); console.log(' - Alpha:', element.alpha); console.log(' - Tint:', element.tint); // STEP 1: TEST EVENT HANDLER DIRECTLY if (typeof element.down === 'function') { console.log(' - Testing event handler...'); try { // Simulate a touch event to see if the handler responds console.log(' - Handler test: Event handler exists and is callable'); } catch (error) { console.log(' - ⚠️ Handler test failed:', error); } } else { console.log(' - ⚠️ No down handler found!'); } } } // STEP 1: VERIFY Z-INDEX CONFLICTS console.log('=== Z-INDEX ANALYSIS ==='); console.log('Background panel z-index:', inGameCardPanel ? inGameCardPanel.zIndex : 'N/A'); var cardZIndexes = []; for (var zIdx = 0; zIdx < cardPanelElements.length; zIdx++) { var elem = cardPanelElements[zIdx]; if (elem && elem.zIndex !== undefined) { cardZIndexes.push({ type: elem.spellId ? 'card' : 'other', zIndex: elem.zIndex, spellId: elem.spellId || 'N/A' }); } } console.log('Card elements z-indexes:', cardZIndexes); // STEP 1: VERIFY POSITIONING IS WITHIN SCREEN BOUNDS console.log('=== POSITIONING ANALYSIS ==='); console.log('Screen dimensions: 2048x2732'); for (var posIdx = 0; posIdx < cardPanelElements.length; posIdx++) { var posElem = cardPanelElements[posIdx]; if (posElem && posElem.spellId) { var inBounds = posElem.x >= 0 && posElem.x <= 2048 && posElem.y >= 0 && posElem.y <= 2732; console.log('Card ' + posElem.spellId + ' in bounds:', inBounds, 'at', { x: posElem.x, y: posElem.y }); } } console.log('=== CARD PANEL DIAGNOSTICS COMPLETE ==='); console.log('✓ Cards created with simplified z-index structure'); console.log('✓ All interactive elements use single layer: 1510-1512'); console.log('✓ Event conflicts should be eliminated'); console.log('✓ Comprehensive diagnostics logged for debugging'); // PASO 2B: Additional logging for z-index experiment console.log('=== PASO 2B: Z-INDEX EXPERIMENT STATUS ==='); console.log('Dynamic z-index sorting: DISABLED'); console.log('Card rendering order: FIXED (by creation order)'); console.log('Expected result: Cards should now respond to touch'); console.log('If cards work now: Z-index sorting was the problem'); console.log('If cards still don\'t work: Problem is elsewhere'); console.log('=== TESTING CARD TOUCH RESPONSIVENESS ==='); } // Hide in-game card panel function hideInGameCardPanel() { if (inGameCardPanel) { inGameCardPanel.visible = false; } clearCardPanelElements(); } // Clear card panel elements function clearCardPanelElements() { for (var i = 0; i < cardPanelElements.length; i++) { if (cardPanelElements[i] && cardPanelElements[i].parent) { cardPanelElements[i].destroy(); } } cardPanelElements = []; } // Targeting system variables var targetingMode = false; var targetingSpell = null; var targetingCursor = null; var targetingRange = null; // Create targeting cursor function createTargetingCursor() { if (targetingCursor) { return targetingCursor; } targetingCursor = game.addChild(LK.getAsset('projectileGlow', { anchorX: 0.5, anchorY: 0.5, scaleX: 2.0, scaleY: 2.0 })); targetingCursor.tint = 0x00FFFF; targetingCursor.alpha = 0.8; targetingCursor.zIndex = 2000; targetingCursor.visible = false; // Add orbiting particles around cursor for enhanced feedback targetingCursor.particles = []; for (var p = 0; p < 4; p++) { var particle = game.addChild(LK.getAsset('projectileGlow', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.4, scaleY: 0.4 })); particle.tint = 0x00FFFF; particle.alpha = 0.6; particle.zIndex = 1999; particle.visible = false; particle.orbitAngle = p * Math.PI * 2 / 4; targetingCursor.particles.push(particle); } return targetingCursor; } // Create targeting range indicator function createTargetingRange() { if (targetingRange) { return targetingRange; } targetingRange = game.addChild(LK.getAsset('projectileGlow', { anchorX: 0.5, anchorY: 0.5, scaleX: 8.0, scaleY: 8.0 })); targetingRange.tint = 0x00FF00; targetingRange.alpha = 0.3; targetingRange.zIndex = 1999; targetingRange.visible = false; return targetingRange; } // Enter targeting mode function enterTargetingMode(spellId) { targetingMode = true; targetingSpell = spellId; // Create targeting visuals createTargetingCursor(); createTargetingRange(); targetingCursor.visible = true; targetingRange.visible = true; // Position range indicator around wizard targetingRange.x = wizard.x; targetingRange.y = wizard.y; // Add pulsing animation to cursor tween(targetingCursor, { scaleX: 2.5, scaleY: 2.5 }, { duration: 800, easing: tween.easeInOut, onFinish: function onFinish() { if (targetingCursor && targetingCursor.visible) { tween(targetingCursor, { scaleX: 2.0, scaleY: 2.0 }, { duration: 800, easing: tween.easeInOut }); } } }); // Show targeting instructions showTargetingInstructions(spellId); } // Exit targeting mode function exitTargetingMode() { targetingMode = false; targetingSpell = null; if (targetingCursor) { targetingCursor.visible = false; } if (targetingRange) { targetingRange.visible = false; } // Hide card panel after targeting hideInGameCardPanel(); cardPanelVisible = false; } // Show targeting instructions function showTargetingInstructions(spellId) { var spell = _getSpell(spellId); var instructionText = ''; if (spellId === 'fireball') { instructionText = 'TOCA UN ENEMIGO PARA LANZAR FIREBALL'; } else if (spellId === 'lightning') { instructionText = 'TOCA UN ENEMIGO PARA CADENA DE RAYOS'; } else if (spellId === 'heal') { instructionText = 'TOCA PARA CURARTE'; } else { instructionText = 'SELECCIONA OBJETIVO PARA ' + (spell ? spell.name : 'HECHIZO'); } var targetingInstructions = new Text2(instructionText, { size: 50, fill: 0x00FFFF, font: "monospace" }); targetingInstructions.anchor.set(0.5, 0.5); targetingInstructions.x = 2048 / 2; targetingInstructions.y = 400; targetingInstructions.zIndex = 2001; game.addChild(targetingInstructions); // Auto-remove instructions after 3 seconds tween(targetingInstructions, { alpha: 0 }, { duration: 3000, easing: tween.easeOut, onFinish: function onFinish() { if (targetingInstructions.parent) { targetingInstructions.destroy(); } } }); } // Execute spell at target location function executeSpellAtTarget(spellId, targetX, targetY, targetEnemy) { console.log('Executing spell', spellId, 'at', targetX, targetY); var success = false; if (spellId === 'fireball') { if (targetEnemy) { // Create targeted fireball var fireball = ProjectileFactory.createProjectile('fireBall', wizard.x, wizard.y, targetEnemy.x, targetEnemy.y, { targetEnemy: targetEnemy, damage: 150 }); LK.getSound('fireWhoosh').play(); showSpellDescription('FIREBALL', 'Daño: 150 dirigido', 0xFF4500); success = true; } } else if (spellId === 'lightning') { if (targetEnemy) { // Execute lightning with target as starting point LK.effects.flashScreen(0x00FFFF, 800); LK.getSound('iceFreeze').play(); var allEnemies = collisionArrayPool.getAllEnemies(); var livingEnemies = []; // Start chain from targeted enemy for (var e = 0; e < allEnemies.length; e++) { var enemy = allEnemies[e]; if (!enemy.isDying && enemy.health > 0 && enemy.parent) { var dx = enemy.x - targetEnemy.x; var dy = enemy.y - targetEnemy.y; enemy.distanceFromTarget = Math.sqrt(dx * dx + dy * dy); livingEnemies.push(enemy); } } // Sort by distance from target livingEnemies.sort(function (a, b) { return a.distanceFromTarget - b.distanceFromTarget; }); var targetsHit = Math.min(3, livingEnemies.length); if (targetsHit > 0) { // Apply lightning damage for (var i = 0; i < targetsHit; i++) { var enemy = livingEnemies[i]; if (enemy && enemy.parent && !enemy.isDying) { enemy.health -= 200; LK.effects.flashObject(enemy, 0x00FFFF, 600); if (enemy.health <= 0) { enemy.die(); } // Create lightning visual var lightningImpact = game.addChild(LK.getAsset('projectileGlow', { anchorX: 0.5, anchorY: 0.5, x: enemy.x, y: enemy.y, scaleX: 3, scaleY: 3 })); lightningImpact.tint = 0x00FFFF; lightningImpact.alpha = 1.0; tween(lightningImpact, { scaleX: 7, scaleY: 7, alpha: 0, rotation: Math.PI * 3 }, { duration: 800, delay: i * 150, easing: tween.easeOut, onFinish: function onFinish() { if (lightningImpact.parent) { lightningImpact.destroy(); } } }); } } showSpellDescription('LIGHTNING', 'Cadena dirigida: ' + targetsHit + ' rayos', 0x00FFFF); } success = true; } } else if (spellId === 'heal') { // Heal can be cast anywhere, always targets wizard var healthBefore = wizard.health; wizard.health = Math.min(wizard.health + 50, wizard.maxHealth); var actualHealing = wizard.health - healthBefore; updateHealthBar(); // Enhanced healing effects at target location LK.effects.flashScreen(0x00FF00, 500); LK.effects.flashObject(wizard, 0x00FF00, 1000); // Create healing aura at clicked location var healingAura = game.addChild(LK.getAsset('projectileGlow', { anchorX: 0.5, anchorY: 0.5, x: targetX, y: targetY, scaleX: 4, scaleY: 4 })); healingAura.tint = 0x00FF00; healingAura.alpha = 0.8; tween(healingAura, { scaleX: 12, scaleY: 12, alpha: 0 }, { duration: 2000, easing: tween.easeOut, onFinish: function onFinish() { if (healingAura.parent) { healingAura.destroy(); } } }); showSpellDescription('HEAL', 'Curado: ' + actualHealing + ' HP', 0x00FF00); success = true; } if (success) { LK.getSound('spellCast').play(); cardCooldowns[spellId] = LK.ticks + cardCooldownDuration; } return success; } // Universal spell casting function for all card interfaces function castSpellFromAnyCard(spellId, sourceInterface) { console.log('=== CASTING SPELL FROM', sourceInterface, '==='); console.log('Spell ID:', spellId); console.log('Current mana before cast:', currentMana); console.log('activeSpellDeck.currentMana before cast:', activeSpellDeck ? activeSpellDeck.currentMana : 'undefined'); // Check if spell can be cast with enhanced validation if (!_canCastSpell(spellId)) { console.log('Cannot cast spell:', spellId); LK.effects.flashScreen(0xFF0000, 200); return false; } // Get spell data for mana consumption var spell = _getSpell(spellId); if (!spell) { console.log('Spell not found for ID:', spellId); return false; } // Execute the spell using unified casting system console.log('Executing spell cast for:', spellId); var castSuccess = _castSpell(spellId); if (castSuccess) { console.log('Spell cast successful from', sourceInterface, ':', spellId); // Show success message showSpellDescription(spell.name.toUpperCase(), 'Lanzado desde ' + sourceInterface, spellConfigs[spellId].color); return true; } else { console.log('Unified spell cast failed for:', spellId); return false; } } // Cast spell from in-game card function castSpellFromCard(spellId) { console.log('=== CASTING SPELL FROM IN-GAME CARD ==='); console.log('Spell ID:', spellId); console.log('Current mana:', currentMana); console.log('Wizard exists:', !!wizard); // Validate prerequisites if (!spellId) { console.log('No spell ID provided'); return false; } if (!wizard || !wizard.parent) { console.log('Wizard not available'); return false; } // Check if spell can be cast var canCast = _canCastSpell(spellId); console.log('Can cast spell:', canCast); if (!canCast) { console.log('Spell cannot be cast - showing error feedback'); LK.effects.flashScreen(0xFF0000, 300); // Show specific error message var currentTick = LK.ticks || 0; var errorMessage = '¡NO SE PUEDE LANZAR!'; if (cardCooldowns[spellId] && currentTick < cardCooldowns[spellId]) { var timeRemaining = Math.ceil((cardCooldowns[spellId] - currentTick) / 60); errorMessage = '¡EN RECARGA!\nEspera: ' + timeRemaining + 's'; } var failureText = new Text2(errorMessage, { size: 60, fill: 0xFF4444, font: "monospace" }); failureText.anchor.set(0.5, 0.5); failureText.x = wizard.x; failureText.y = wizard.y - 150; failureText.zIndex = 1701; game.addChild(failureText); tween(failureText, { alpha: 0, y: failureText.y - 80 }, { duration: 800, easing: tween.easeOut, onFinish: function onFinish() { if (failureText.parent) { failureText.destroy(); } } }); return false; } // Execute spell casting console.log('Executing spell cast...'); var success = _castSpell(spellId); console.log('Spell cast result:', success); if (success) { console.log('Spell cast successful:', spellId); // Show success message var spell = _getSpell(spellId); var spellName = spell ? spell.name : spellId.toUpperCase(); var successText = new Text2('¡' + spellName + ' LANZADO!', { size: 80, fill: 0xFFD700, font: "monospace" }); successText.anchor.set(0.5, 0.5); successText.x = wizard.x; successText.y = wizard.y - 180; successText.zIndex = 1701; game.addChild(successText); tween(successText, { y: successText.y - 100, alpha: 0, scaleX: 1.5, scaleY: 1.5 }, { duration: 1200, easing: tween.easeOut, onFinish: function onFinish() { if (successText.parent) { successText.destroy(); } } }); // Hide card panel after successful cast setTimeout(function () { hideInGameCardPanel(); cardPanelVisible = false; }, 500); return true; } else { console.log('Spell cast failed:', spellId); LK.effects.flashScreen(0xFF0000, 300); var failureText = new Text2('¡FALLO AL LANZAR!', { size: 60, fill: 0xFF4444, font: "monospace" }); failureText.anchor.set(0.5, 0.5); failureText.x = wizard.x; failureText.y = wizard.y - 150; failureText.zIndex = 1701; game.addChild(failureText); tween(failureText, { alpha: 0, y: failureText.y - 80 }, { duration: 800, easing: tween.easeOut, onFinish: function onFinish() { if (failureText.parent) { failureText.destroy(); } } }); return false; } } // STEP 1: ADD GLOBAL EVENT DEBUGGING FOR CARD PANEL DIAGNOSTICS console.log('=== INSTALLING GLOBAL EVENT DEBUGGING ==='); // Override game.down to debug touch events reaching the game level var originalGameDown = game.down; game.down = function (x, y, obj) { // STEP 1: LOG ALL TOUCH EVENTS FOR DEBUGGING if (cardPanelVisible) { console.log('=== TOUCH EVENT DEBUG (Step 1) ==='); console.log('Touch at:', { x: x, y: y }); console.log('Card panel visible:', cardPanelVisible); console.log('Card panel elements count:', cardPanelElements.length); // STEP 1: CHECK IF TOUCH IS IN CARD AREA var cardAreaYMin = 2732 - 300; // Approximate card area var cardAreaYMax = 2732; var isInCardArea = y >= cardAreaYMin && y <= cardAreaYMax; console.log('Touch in card area:', isInCardArea, 'Y:', y, 'Card area:', cardAreaYMin, '-', cardAreaYMax); // STEP 1: CHECK WHICH CARD SHOULD BE TOUCHED if (isInCardArea) { for (var cardCheckIdx = 0; cardCheckIdx < cardPanelElements.length; cardCheckIdx++) { var cardElem = cardPanelElements[cardCheckIdx]; if (cardElem && cardElem.spellId) { var cardLeft = cardElem.x - 125; // Approximate card width/2 var cardRight = cardElem.x + 125; var cardTop = cardElem.y - 150; // Approximate card height/2 var cardBottom = cardElem.y + 150; var touchInCard = x >= cardLeft && x <= cardRight && y >= cardTop && y <= cardBottom; console.log('Card', cardElem.spellId, 'bounds check:', touchInCard, 'Touch XY:', { x: x, y: y }, 'Card bounds:', { left: cardLeft, right: cardRight, top: cardTop, bottom: cardBottom }); if (touchInCard) { console.log('⚠️ TOUCH SHOULD HAVE HIT CARD:', cardElem.spellId); console.log('Card interactive:', cardElem.interactive); console.log('Card visible:', cardElem.visible); console.log('Card parent exists:', cardElem.parent ? 'yes' : 'no'); console.log('Card has down handler:', typeof cardElem.down === 'function'); // STEP 1: MANUALLY TRIGGER CARD EVENT FOR TESTING if (typeof cardElem.down === 'function') { console.log('🔥 MANUALLY TRIGGERING CARD EVENT FOR TESTING'); try { cardElem.down(x, y, cardElem); console.log('✅ Manual trigger successful'); } catch (error) { console.log('❌ Manual trigger failed:', error); } } } } } } console.log('=== TOUCH EVENT DEBUG COMPLETE ==='); } // Call original game.down function if (originalGameDown) { return originalGameDown.call(this, x, y, obj); } }; // Add targeting system to game mouse/touch handling game.move = function (x, y, obj) { if (targetingMode && targetingCursor) { // Update cursor position to follow mouse/touch targetingCursor.x = x; targetingCursor.y = y; // Calculate if target is in range var dx = x - wizard.x; var dy = y - wizard.y; var distance = Math.sqrt(dx * dx + dy * dy); var maxRange = 400; // Maximum spell range // Change cursor color based on range if (distance <= maxRange) { targetingCursor.tint = 0x00FF00; // Green for in range targetingCursor.alpha = 0.8; } else { targetingCursor.tint = 0xFF0000; // Red for out of range targetingCursor.alpha = 0.5; } } }; // PASO 2: DEBUGGING OVERRIDE REMOVED TO FIX CARD TOUCH EVENTS // The debugging override was intercepting all touch events before they reached individual cards // This prevented card touch handlers from executing properly // Cards should now respond correctly to touch events // Main game update loop game.update = function () { // STEP 1 SOLUTION: Dynamic z-index sorting permanently removed to fix card touch detection // This was causing cards to be reordered every frame and interfering with touch events // Cards now maintain their creation order and should respond to touch properly console.log('=== STEP 1: DYNAMIC Z-INDEX SORTING PERMANENTLY DISABLED ==='); console.log('=== STEP 2: DEBUGGING OVERRIDE REMOVED FOR CARD TOUCH EVENTS ==='); console.log('Touch events should now work correctly on cards'); // Pause game when tutorial is active if (tutorial && tutorial.isActive) { return; } // Only update game logic if game has started if (!gameStarted) { return; } // Change music to epic battle theme at 10 enemies killed if (enemyKillCounter === 10) { LK.playMusic('epicBattle', { volume: 0.8, fade: { start: 0, end: 0.8, duration: 1500 } }); } // Change to mystical ambient music at 25 enemies killed if (enemyKillCounter === 25) { LK.playMusic('mysticalAmbient', { volume: 0.6, fade: { start: 0, end: 0.6, duration: 2000 } }); } // Upgrade menus removed - using spell deck system instead // Reset consecutive spawns for paths that have cooled down (runs every frame) for (var pathIdx = 0; pathIdx < 5; pathIdx++) { // Check if enough time has passed since last spawn (cooldown expired) if (pathLastSpawnTime[pathIdx] !== -1 && LK.ticks - pathLastSpawnTime[pathIdx] > pathCooldownDuration) { // Reset consecutive spawns for this path due to cooldown pathConsecutiveSpawns[pathIdx] = 0; } } // Get stored difficulty setting var selectedDifficulty = storage.difficulty || 'NORMAL'; var difficultyLevel = Math.floor(enemyKillCounter / 10); // Every 10 kills increases difficulty // Helper functions now integrated into EnemyFactory // Unified spawn management system SpawnManager.processSpawnCycle(selectedDifficulty, difficultyLevel); // Reset path cooldowns for optimized path management for (var pathIdx = 0; pathIdx < 5; pathIdx++) { if (pathLastSpawnTime[pathIdx] !== -1 && LK.ticks - pathLastSpawnTime[pathIdx] > pathCooldownDuration) { pathConsecutiveSpawns[pathIdx] = 0; } } // Unified CollisionManager for streamlined collision detection and response var CollisionManager = { // Consolidated collision configurations collisionConfig: { skeleton: { damage: 20, removeOnHit: true }, ogre: { damage: 30, removeOnHit: true }, knight: { damage: 40, removeOnHit: true }, miniBoss: { damage: 75, removeOnHit: false } }, // Streamlined enemy collision processing with categorized collision types processEnemyCollisions: function processEnemyCollisions() { var allEnemies = globalEnemyManager.getAllEnemies(); // Category 1: Off-screen cleanup (non-collision processing) this.processOffScreenCleanup(allEnemies); // Category 2: Enemy-wizard collisions this.processEnemyWizardCollisions(allEnemies); }, // Separate processing for off-screen enemy cleanup processOffScreenCleanup: function processOffScreenCleanup(allEnemies) { for (var i = allEnemies.length - 1; i >= 0; i--) { var enemy = allEnemies[i]; if (this.isOffScreen(enemy)) { this.removeEnemyFromGame(enemy); } } }, // Separate processing for enemy-wizard collisions processEnemyWizardCollisions: function processEnemyWizardCollisions(allEnemies) { for (var i = 0; i < allEnemies.length; i++) { var enemy = allEnemies[i]; if (!enemy.isDying && enemy.parent) { this.checkWizardCollision(enemy); } } }, // Efficient off-screen detection isOffScreen: function isOffScreen(enemy) { return enemy.y > 2732 + 100; }, // Unified enemy removal system removeEnemyFromGame: function removeEnemyFromGame(enemy) { // Remove from global arrays directly this.removeFromLegacyArrays(enemy); enemy.destroy(); }, // Legacy array compatibility cleanup removeFromLegacyArrays: function removeFromLegacyArrays(enemy) { var arrays = [enemies, ogres, knights, miniBosses]; for (var a = 0; a < arrays.length; a++) { var array = arrays[a]; for (var i = array.length - 1; i >= 0; i--) { if (array[i] === enemy) { array.splice(i, 1); break; } } } }, // 1.1 Distance Culling: Enhanced wizard collision detection with optimized distance-based culling checkWizardCollision: function checkWizardCollision(enemy) { // Initialize collision tracking if (enemy.lastIntersecting === undefined) { enemy.lastIntersecting = false; } // 1.1 Distance Culling: Skip expensive intersection test if objects are too far apart var dx = enemy.x - wizard.x; var dy = enemy.y - wizard.y; var distance = Math.sqrt(dx * dx + dy * dy); var maxCollisionDistance = 150; // Approximate maximum collision distance based on sprite sizes var currentIntersecting = false; if (distance <= maxCollisionDistance) { // Only perform expensive intersection test if objects are close enough currentIntersecting = wizard.intersects(enemy); } // Check collision transition if (!enemy.lastIntersecting && currentIntersecting && !enemy.isDying) { var config = this.getEnemyConfig(enemy); wizard.takeDamage(config.damage); // Handle enemy removal based on type if (config.removeOnHit) { this.removeEnemyFromGame(enemy); return; } } // Update collision state enemy.lastIntersecting = currentIntersecting; }, // Get enemy configuration by type getEnemyConfig: function getEnemyConfig(enemy) { return this.collisionConfig[enemy.enemyType] || this.collisionConfig.skeleton; } }; // Replace the old collision function call function checkAllEnemyCollisions() { CollisionManager.processEnemyCollisions(); } // Call the unified collision detection function checkAllEnemyCollisions(); // Check thorns spike collisions with all enemies continuously var allSpikes = []; for (var childIdx = 0; childIdx < game.children.length; childIdx++) { var child = game.children[childIdx]; // Check if this child is a spike (has hitEnemies array and brown tint) if (child.hitEnemies && child.tint === 0x8B4513) { allSpikes.push(child); } } for (var spikeIdx = 0; spikeIdx < allSpikes.length; spikeIdx++) { var spike = allSpikes[spikeIdx]; var allEnemies = collisionArrayPool.getAllEnemies(); for (var enemyIdx = 0; enemyIdx < allEnemies.length; enemyIdx++) { var enemy = allEnemies[enemyIdx]; // Only hit enemies that haven't been hit by this spike yet and are not dying if (spike.intersects(enemy) && spike.hitEnemies.indexOf(enemy) === -1 && !enemy.isDying) { var thornDamage = 100; // Always deal 100 damage enemy.takeDamage(thornDamage); LK.effects.flashObject(enemy, 0x8B4513, 300); // Mark this enemy as hit by this spike spike.hitEnemies.push(enemy); } } } // Mana system completely removed - spells use cooldown-only system // Simple time slow effects processing var allEnemies = collisionArrayPool.getAllEnemies(); for (var i = 0; i < allEnemies.length; i++) { var enemy = allEnemies[i]; if (enemy.timeSlowed) { enemy.timeSlowTimer--; if (enemy.timeSlowTimer <= 0) { enemy.timeSlowed = false; enemy.timeSlowAmount = 1.0; } } } // Make tap text pulse var pulse = 1 + Math.sin(LK.ticks * 0.1) * 0.2; tapText.scale.set(pulse, pulse); // Update targeting cursor animation if (targetingMode && targetingCursor && targetingCursor.visible) { // Rotate targeting cursor targetingCursor.rotation += 0.05; // Add secondary pulsing animation var targetPulse = 1 + Math.sin(LK.ticks * 0.2) * 0.3; if (targetingCursor.tint === 0x00FF00) { // In-range pulsing targetingCursor.alpha = 0.6 + targetPulse * 0.2; } else { // Out-of-range warning pulse targetingCursor.alpha = 0.3 + targetPulse * 0.4; } // Update orbiting particles around cursor if (targetingCursor.particles) { for (var p = 0; p < targetingCursor.particles.length; p++) { var particle = targetingCursor.particles[p]; if (particle && particle.parent) { particle.orbitAngle += 0.08; var orbitRadius = 40 + Math.sin(LK.ticks * 0.1) * 10; particle.x = targetingCursor.x + Math.cos(particle.orbitAngle) * orbitRadius; particle.y = targetingCursor.y + Math.sin(particle.orbitAngle) * orbitRadius; particle.visible = targetingCursor.visible; particle.tint = targetingCursor.tint; particle.alpha = targetingCursor.alpha * 0.7; } } } } // Update targeting range indicator if (targetingMode && targetingRange && targetingRange.visible) { // Gentle pulsing for range indicator var rangePulse = 1 + Math.sin(LK.ticks * 0.1) * 0.1; targetingRange.scaleX = 8.0 * rangePulse; targetingRange.scaleY = 8.0 * rangePulse; targetingRange.alpha = 0.2 + Math.sin(LK.ticks * 0.15) * 0.1; } // Check for spell unlocks checkSpellUnlocks(); // STEP 5: Periodic deck data validation (every 5 seconds) if (LK.ticks % 300 === 0) { validateDeckData(); } // Clean up any orphaned projectiles that may not have been properly removed for (var i = projectiles.length - 1; i >= 0; i--) { var projectile = projectiles[i]; if (!projectile || !projectile.parent || projectile.hitEnemy) { projectiles.splice(i, 1); } } // ProjectileFactory uses the global projectiles array, no separate activeProjectiles array needed }; // Remove tap text after 5 seconds tween({}, {}, { duration: 5000, onFinish: function onFinish() { if (tapText && tapText.parent) { tapText.destroy(); } } }); // TEST PHASE 1.3: Verify spell functionality works with neutralized mana system console.log('=== TESTING SPELL FUNCTIONALITY (Phase 1.3) ==='); // Test mana values are properly neutralized console.log('✓ Testing mana neutralization:'); console.log(' - activeSpellDeck.currentMana:', activeSpellDeck ? activeSpellDeck.currentMana : 'undefined', '(should be 9999)'); // Test spell validation functions console.log('✓ Testing spell validation:'); var testSpells = ['fireball', 'heal', 'lightning']; for (var t = 0; t < testSpells.length; t++) { var spellId = testSpells[t]; var canCast = _canCastSpell(spellId); console.log(' - ' + spellId + ' can cast:', canCast, '(should be true with neutralized mana)'); // Test spell configuration exists var config = spellConfigs[spellId]; if (config) { console.log(' - ' + spellId + ' config found - manaCost:', config.manaCost, 'damage/healing:', config.damage || config.healing); } else { console.log(' - ⚠️ ' + spellId + ' config missing'); } } // Test that mana checks are bypassed console.log('✓ Testing mana bypass:'); var originalMana = currentMana; currentMana = 0; // Temporarily set to 0 to test bypass var canCastWithZeroMana = _canCastSpell('fireball'); currentMana = originalMana; // Restore console.log(' - Can cast with 0 mana (should be false):', canCastWithZeroMana); console.log(' - Can cast with 9999 mana (should be true):', _canCastSpell('fireball')); // Test cooldown system works independently console.log('✓ Testing cooldown system:'); cardCooldowns['heal'] = LK.ticks + 300; // Set cooldown var canCastOnCooldown = _canCastSpell('heal'); console.log(' - Can cast heal on cooldown:', canCastOnCooldown, '(should be false)'); delete cardCooldowns['heal']; // Clear cooldown var canCastOffCooldown = _canCastSpell('heal'); console.log(' - Can cast heal off cooldown:', canCastOffCooldown, '(should be true)'); // Test actual spell casting works console.log('✓ Testing spell casting execution:'); if (gameStarted && wizard) { var originalHealth = wizard.health; var healSuccess = _castSpell('heal'); console.log(' - Heal spell cast success:', healSuccess); console.log(' - Health before/after:', originalHealth, '/', wizard.health); } else { console.log(' - Game not started, skipping live spell test'); } // Verify mana display shows neutralized values console.log(' - Mana display updated with neutralized values'); // Test error handling doesn't trigger mana errors console.log('✓ Testing error handling:'); var invalidSpellResult = _canCastSpell('nonexistent'); console.log(' - Invalid spell handled correctly:', !invalidSpellResult); console.log('=== PHASE 1.3 TESTING COMPLETE ==='); console.log('✓ Spells should now function using cooldowns only'); console.log('✓ Mana system is neutralized at 9999'); console.log('✓ No mana-related restrictions should block spell casting'); console.log('✓ Cards in deck menu should be ready to cast (green glow)'); // PASO 2A: INVESTIGAR Z-INDEX ACTUAL - Documentar todos los z-index que se están usando actualmente console.log('=== PASO 2A: INVESTIGACIÓN Z-INDEX ACTUAL ==='); // Recopilar todos los z-index en uso var zIndexInventory = { backgrounds: [], gameElements: [], ui: [], cards: [], effects: [], other: [] }; console.log('📊 Analizando z-index de todos los elementos del juego...'); // Analizar elementos del juego principal for (var i = 0; i < game.children.length; i++) { var child = game.children[i]; var zIndex = child.zIndex || 0; var elementInfo = { element: child.constructor.name || 'Unknown', zIndex: zIndex, position: { x: child.x, y: child.y }, visible: child.visible, interactive: child.interactive || false }; // Categorizar por z-index y tipo if (zIndex <= -50) { zIndexInventory.backgrounds.push(elementInfo); } else if (zIndex >= 1500 && zIndex <= 1600) { zIndexInventory.cards.push(elementInfo); } else if (zIndex >= 1000 && zIndex <= 1400) { zIndexInventory.ui.push(elementInfo); } else if (zIndex >= 1700) { zIndexInventory.effects.push(elementInfo); } else if (zIndex > 0) { zIndexInventory.gameElements.push(elementInfo); } else { zIndexInventory.other.push(elementInfo); } } // Reportar inventario de z-index console.log('🗂️ INVENTARIO DE Z-INDEX POR CATEGORÍA:'); console.log(''); console.log('📋 BACKGROUNDS (z-index <= -50):'); for (var b = 0; b < zIndexInventory.backgrounds.length; b++) { var bg = zIndexInventory.backgrounds[b]; console.log(' - ' + bg.element + ': z=' + bg.zIndex + ', visible=' + bg.visible); } console.log('📋 GAME ELEMENTS (0 < z-index < 1000):'); for (var g = 0; g < zIndexInventory.gameElements.length; g++) { var ge = zIndexInventory.gameElements[g]; console.log(' - ' + ge.element + ': z=' + ge.zIndex + ', visible=' + ge.visible + ', interactive=' + ge.interactive); } console.log('📋 UI ELEMENTS (1000 <= z-index < 1500):'); for (var u = 0; u < zIndexInventory.ui.length; u++) { var ui = zIndexInventory.ui[u]; console.log(' - ' + ui.element + ': z=' + ui.zIndex + ', visible=' + ui.visible + ', interactive=' + ui.interactive); } console.log('📋 CARD ELEMENTS (1500 <= z-index <= 1600):'); for (var c = 0; c < zIndexInventory.cards.length; c++) { var card = zIndexInventory.cards[c]; console.log(' - ' + card.element + ': z=' + card.zIndex + ', visible=' + card.visible + ', interactive=' + card.interactive); } console.log('📋 EFFECTS (z-index >= 1700):'); for (var e = 0; e < zIndexInventory.effects.length; e++) { var effect = zIndexInventory.effects[e]; console.log(' - ' + effect.element + ': z=' + effect.zIndex + ', visible=' + effect.visible); } console.log('📋 OTHER/UNKNOWN (z-index = 0 or uncategorized):'); for (var o = 0; o < zIndexInventory.other.length; o++) { var other = zIndexInventory.other[o]; console.log(' - ' + other.element + ': z=' + other.zIndex + ', visible=' + other.visible + ', interactive=' + other.interactive); } // Analizar específicamente el panel de cartas si está visible if (cardPanelVisible && inGameCardPanel) { console.log(''); console.log('🃏 ANÁLISIS ESPECÍFICO DEL PANEL DE CARTAS:'); console.log('Panel background z-index:', inGameCardPanel.zIndex); console.log('Panel visible:', inGameCardPanel.visible); console.log('Panel interactive:', inGameCardPanel.interactive); console.log('Panel position:', { x: inGameCardPanel.x, y: inGameCardPanel.y }); console.log('Elementos del panel de cartas:'); for (var cp = 0; cp < cardPanelElements.length; cp++) { var cardEl = cardPanelElements[cp]; if (cardEl) { console.log(' - Elemento #' + cp + ':'); console.log(' * Tipo:', cardEl.constructor.name || 'Unknown'); console.log(' * Z-index:', cardEl.zIndex || 'undefined'); console.log(' * SpellID:', cardEl.spellId || 'N/A'); console.log(' * Visible:', cardEl.visible); console.log(' * Interactive:', cardEl.interactive || false); console.log(' * Posición:', { x: cardEl.x, y: cardEl.y }); console.log(' * Tiene evento down:', typeof cardEl.down === 'function'); } } } // Verificar la línea problemática del ordenamiento dinámico console.log(''); console.log('⚠️ IDENTIFICANDO PROBLEMA DEL ORDENAMIENTO DINÁMICO:'); console.log('La línea problemática está en game.update():'); console.log('game.children.sort(function (a, b) { return (a.zIndex || 0) - (b.zIndex || 0); });'); console.log(''); console.log('📊 ESTADÍSTICAS DEL PROBLEMA:'); console.log('- Total elementos con z-index definido:', zIndexInventory.backgrounds.length + zIndexInventory.gameElements.length + zIndexInventory.ui.length + zIndexInventory.cards.length + zIndexInventory.effects.length); console.log('- Total elementos sin z-index (default 0):', zIndexInventory.other.length); console.log('- Elementos interactivos encontrados:', zIndexInventory.gameElements.filter(function (e) { return e.interactive; }).length + zIndexInventory.ui.filter(function (e) { return e.interactive; }).length + zIndexInventory.cards.filter(function (e) { return e.interactive; }).length); // Analizar conflictos potenciales console.log(''); console.log('⚠️ CONFLICTOS POTENCIALES IDENTIFICADOS:'); // Verificar si hay elementos interactivos con z-index similares var interactiveElements = []; var allCategories = [zIndexInventory.gameElements, zIndexInventory.ui, zIndexInventory.cards, zIndexInventory.effects]; for (var cat = 0; cat < allCategories.length; cat++) { var category = allCategories[cat]; for (var el = 0; el < category.length; el++) { if (category[el].interactive) { interactiveElements.push(category[el]); } } } // Buscar z-index duplicados entre elementos interactivos var zIndexConflicts = {}; for (var ie = 0; ie < interactiveElements.length; ie++) { var elem = interactiveElements[ie]; var zIdx = elem.zIndex; if (!zIndexConflicts[zIdx]) { zIndexConflicts[zIdx] = []; } zIndexConflicts[zIdx].push(elem.element); } for (var zIdx in zIndexConflicts) { if (zIndexConflicts[zIdx].length > 1) { console.log('⚠️ Conflicto en z-index ' + zIdx + ':', zIndexConflicts[zIdx].join(', ')); } } console.log(''); console.log('🎯 CONCLUSIONES DEL ANÁLISIS PASO 2A:'); console.log('1. El ordenamiento dinámico reordena ' + game.children.length + ' elementos cada frame'); console.log('2. Los elementos del panel de cartas compiten con otros elementos UI'); console.log('3. El ordenamiento puede cambiar el orden de renderizado constantemente'); console.log('4. Esto causa que las cartas queden "debajo" de otros elementos visualmente'); console.log('5. Los eventos táctiles pueden ser capturados por elementos mal ordenados'); console.log(''); console.log('✅ PASO 2A COMPLETADO - Datos recopilados para implementar solución'); console.log('=== FIN PASO 2A: INVESTIGACIÓN Z-INDEX ==='); // PASO 1: DIAGNÓSTICO DETALLADO DEL SISTEMA DE MOVIMIENTO DEL WIZARD console.log('=== PASO 1: DIAGNÓSTICO COMPLETO SISTEMA MOVIMIENTO WIZARD ==='); // PASO 1.1: Estado inicial del wizard console.log('1.1 ESTADO INICIAL DEL WIZARD:'); if (wizard) { console.log('✓ Wizard existe'); console.log(' - Posición actual:', { x: wizard.x, y: wizard.y }); console.log(' - Tiene parent:', !!wizard.parent); console.log(' - Es visible:', wizard.visible); console.log(' - Constructor:', wizard.constructor.name); console.log(' - Propiedades de posición definidas:', { x: typeof wizard.x === 'number' && !isNaN(wizard.x), y: typeof wizard.y === 'number' && !isNaN(wizard.y) }); } else { console.log('❌ Wizard NO existe'); } // PASO 1.2: Estado de las posiciones disponibles console.log('1.2 POSICIONES DE WIZARD DISPONIBLES:'); console.log(' - Posiciones definidas:', wizardPositions.length); console.log(' - Posición actual (índice):', currentWizardPosition); for (var pos = 0; pos < wizardPositions.length; pos++) { var position = wizardPositions[pos]; console.log(' - Posición ' + pos + ':', { x: position.x, y: position.y }); console.log(' * Está activa:', pos === currentWizardPosition); console.log(' * Válida:', typeof position.x === 'number' && typeof position.y === 'number'); } // PASO 1.3: Estado de los indicadores de posición console.log('1.3 INDICADORES DE POSICIÓN:'); console.log(' - Total indicadores creados:', positionIndicators.length); for (var ind = 0; ind < positionIndicators.length; ind++) { var indicator = positionIndicators[ind]; if (indicator && indicator.positionIndex !== undefined) { console.log(' - Indicador ' + indicator.positionIndex + ':'); console.log(' * Existe:', !!indicator); console.log(' * Visible:', indicator.visible); console.log(' * Interactive:', indicator.interactive); console.log(' * Tiene parent:', !!indicator.parent); console.log(' * Posición:', { x: indicator.x, y: indicator.y }); console.log(' * Color (tint):', '0x' + indicator.tint.toString(16).toUpperCase()); console.log(' * Tiene handler down:', typeof indicator.down === 'function'); console.log(' * Z-index:', indicator.zIndex || 'undefined'); } else if (indicator) { console.log(' - Elemento de texto ' + ind + ':'); console.log(' * Es texto:', !!indicator.setText); console.log(' * Visible:', indicator.visible); console.log(' * Texto:', indicator.text || 'N/A'); } } // PASO 1.4: Función de movimiento disponible console.log('1.4 FUNCIÓN DE MOVIMIENTO:'); console.log(' - moveWizardToNextPosition existe:', typeof moveWizardToNextPosition === 'function'); // PASO 1.5: Sistema de tween disponible console.log('1.5 SISTEMA DE TWEEN:'); console.log(' - Plugin tween cargado:', typeof tween === 'function'); console.log(' - TweenManager disponible:', _typeof5(TweenManager) === 'object' && TweenManager.isPluginValid); console.log(' - globalTween función disponible:', typeof globalTween === 'function'); // PASO 1.6: Estado del juego console.log('1.6 ESTADO DEL JUEGO:'); console.log(' - Juego iniciado (gameStarted):', gameStarted); console.log(' - Tutorial activo:', tutorial && tutorial.isActive); console.log(' - LK.ticks disponible:', typeof LK.ticks === 'number'); // PASO 1.7: Función de manejo de eventos global console.log('1.7 MANEJO DE EVENTOS:'); console.log(' - game.down definido:', typeof game.down === 'function'); console.log(' - game.move definido:', typeof game.move === 'function'); // PASO 1.8: Verificar configuración de área táctil console.log('1.8 CONFIGURACIÓN ÁREA TÁCTIL:'); var lowerThirdY = 2732 * 0.66; console.log(' - Límite inferior área wizard:', lowerThirdY); console.log(' - Altura total pantalla:', 2732); console.log(' - Área wizard comprende desde Y:', lowerThirdY, 'hasta Y:', 2732); // PASO 1.9: Test de función de movimiento (sin ejecutar) console.log('1.9 ANÁLISIS FUNCIÓN MOVIMIENTO:'); if (typeof moveWizardToNextPosition === 'function') { console.log('✓ Función moveWizardToNextPosition accesible'); // Simular los pasos críticos sin ejecutar var nextPos = (currentWizardPosition + 1) % 3; var nextPosition = wizardPositions[nextPos]; console.log(' - Siguiente posición sería índice:', nextPos); console.log(' - Siguiente coordenadas serían:', nextPosition); console.log(' - Posición válida:', !!nextPosition && typeof nextPosition.x === 'number'); } else { console.log('❌ Función moveWizardToNextPosition no accesible'); } // PASO 1.10: Análisis de posibles problemas console.log('1.10 ANÁLISIS DE PROBLEMAS POTENCIALES:'); var problemsFound = []; // Verificar wizard if (!wizard) { problemsFound.push('Wizard no existe'); } else if (!wizard.parent) { problemsFound.push('Wizard no tiene parent (no está en juego)'); } else if (typeof wizard.x !== 'number' || typeof wizard.y !== 'number') { problemsFound.push('Wizard tiene propiedades de posición inválidas'); } // Verificar posiciones if (wizardPositions.length !== 3) { problemsFound.push('No hay exactamente 3 posiciones de wizard definidas'); } for (var p = 0; p < wizardPositions.length; p++) { var pos = wizardPositions[p]; if (!pos || typeof pos.x !== 'number' || typeof pos.y !== 'number') { problemsFound.push('Posición ' + p + ' tiene datos inválidos'); } } // Verificar indicadores var actualIndicators = 0; for (var i = 0; i < positionIndicators.length; i++) { if (positionIndicators[i] && positionIndicators[i].positionIndex !== undefined) { actualIndicators++; if (!positionIndicators[i].interactive) { problemsFound.push('Indicador ' + positionIndicators[i].positionIndex + ' no es interactive'); } if (typeof positionIndicators[i].down !== 'function') { problemsFound.push('Indicador ' + positionIndicators[i].positionIndex + ' no tiene handler down'); } } } if (actualIndicators !== 3) { problemsFound.push('No hay exactamente 3 indicadores de posición interactive'); } // Verificar tween if (typeof tween !== 'function') { problemsFound.push('Sistema de tween no disponible'); } // Verificar estado del juego if (!gameStarted) { problemsFound.push('Juego no ha iniciado (gameStarted = false)'); } // Reportar problemas encontrados if (problemsFound.length === 0) { console.log('✅ No se encontraron problemas evidentes en el sistema'); } else { console.log('⚠️ PROBLEMAS DETECTADOS:'); for (var prob = 0; prob < problemsFound.length; prob++) { console.log(' - ' + problemsFound[prob]); } } console.log('=== FIN PASO 1: DIAGNÓSTICO COMPLETO SISTEMA MOVIMIENTO WIZARD ==='); console.log('📊 RESUMEN DIAGNÓSTICO:'); console.log(' - Wizard válido:', !!wizard && !!wizard.parent); console.log(' - Posiciones definidas:', wizardPositions.length + '/3'); console.log(' - Indicadores válidos:', actualIndicators + '/3'); console.log(' - Sistema tween:', typeof tween === 'function' ? 'OK' : 'FALLO'); console.log(' - Juego iniciado:', gameStarted); console.log(' - Problemas encontrados:', problemsFound.length); console.log(''); console.log('🎯 PRÓXIMO PASO: Según los resultados, implementar correcciones específicas');
===================================================================
--- original.js
+++ change.js
@@ -4243,92 +4243,50 @@
indicator.visible = false;
indicator.positionIndex = i;
indicator.interactive = true; // Ensure indicators can receive touch events
console.log('✓ PASO 3: Created indicator', i, 'with color:', i === currentWizardPosition ? 'GREEN' : 'BLUE');
- // PASO 2: Simplified position indicator click handler - validation removed
+ // PASO 3: Simplified wizard movement with direct execution
indicator.down = function (x, y, obj) {
- console.log('=== PASO 2: POSITION INDICATOR CLICKED ===');
+ console.log('=== PASO 3: SIMPLIFIED WIZARD MOVEMENT ===');
console.log('Clicked position index:', obj.positionIndex);
console.log('Current position index:', currentWizardPosition);
console.log('Game started:', gameStarted);
console.log('Wizard available:', !!wizard);
- // PASO 2: Minimal validation - only check game started
+ // PASO 3: Only essential validation - game must be started and wizard must exist
if (!gameStarted) {
console.log('⚠️ Game not started, ignoring click');
return;
}
if (!wizard) {
console.log('⚠️ No wizard available, ignoring click');
return;
}
- // PASO 2: Remove all blocking validations - allow any position click
- console.log('✓ PASO 2: All validations removed - executing movement to position', obj.positionIndex);
- // Execute position change
- console.log('✓ Executing position change to index:', obj.positionIndex);
+ // PASO 3: Direct movement execution - no validation blocking
+ console.log('✓ PASO 3: Executing direct movement to position', obj.positionIndex);
+ // STEP 1: Update position immediately
currentWizardPosition = obj.positionIndex;
var targetPos = wizardPositions[currentWizardPosition];
- // Use fallback position if needed
- if (!targetPos) {
- console.log('⚠️ Target position missing, using default');
- targetPos = {
- x: 2048 / 2,
- y: 2732 - 400
- };
- }
- console.log('Target position coordinates:', targetPos);
- // Move wizard immediately
+ // STEP 2: Move wizard directly
wizard.x = targetPos.x;
wizard.y = targetPos.y;
- console.log('✓ Direct movement completed');
- // FIXED: Update indicator colors correctly - check both indicators and labels
- var indicatorIndex = 0;
+ console.log('✓ PASO 3: Wizard moved to:', {
+ x: wizard.x,
+ y: wizard.y
+ });
+ // STEP 3: Update all indicator colors in single pass
for (var j = 0; j < positionIndicators.length; j++) {
var indicator = positionIndicators[j];
if (indicator && indicator.positionIndex !== undefined) {
- // This is a position indicator (not a label)
if (indicator.positionIndex === currentWizardPosition) {
indicator.tint = 0x00FF00; // Green for current position
- console.log('✓ Set indicator', indicator.positionIndex, 'to GREEN');
} else {
indicator.tint = 0x4169E1; // Blue for available positions
- console.log('✓ Set indicator', indicator.positionIndex, 'to BLUE');
}
}
}
- console.log('✓ Position indicators colors updated correctly');
- // Add smooth tween animation
- if (typeof tween === 'function') {
- console.log('Adding smooth tween animation');
- try {
- tween(wizard, {
- x: targetPos.x,
- y: targetPos.y
- }, {
- duration: 400,
- easing: tween.easeInOut || function (t) {
- return t;
- }
- });
- console.log('✓ Tween animation added');
- } catch (error) {
- console.log('⚠️ Tween animation failed:', error);
- }
- } else {
- console.log('⚠️ Tween not available, using direct movement only');
- }
- // Visual feedback
- try {
- LK.effects.flashObject(obj, 0x00FF88, 300);
- console.log('✓ Visual feedback applied');
- } catch (error) {
- console.log('⚠️ Visual feedback failed:', error);
- }
- console.log('=== POSITION CHANGE COMPLETED ===');
- console.log('New wizard position:', {
- x: wizard.x,
- y: wizard.y
- });
- console.log('New position index:', currentWizardPosition);
+ // STEP 4: Visual feedback
+ LK.effects.flashObject(obj, 0x00FF88, 300);
+ console.log('=== PASO 3: MOVEMENT COMPLETED SUCCESSFULLY ===');
};
// Add position label text with clear numbering
var positionNames = ['1 CENTRO', '2 IZQUIERDA', '3 DERECHA'];
var positionLabel = new Text2(positionNames[i], {