User prompt
Please fix the bug: 'Timeout.tick error: timeLeftText is not defined' in or related to this line: 'if (timeLeftText.parent) {' Line Number: 380 ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
cuando jugamos el primer nivel y terminamos de derrotar a las panteras abajo de la pantalla aparecen unas letras rojas, elimínalas, no te lo pedí.
User prompt
cuando pausemos el nivel que el tiempo se detenga, basicamente que no siga contando, y que cuando saquemos la pausa que el tiempo marque justo el minuto y segundo que se mostro cuando pausamos el nivel, okey?. por otro lado, que cuando estemos jugando en cualquier nivel que detras de el laberinto y de las letras se muestre el asset: "fondonegro" y que deje de verse cuando ganemos o perdamos el nivel.
User prompt
cuando comienze el nivel 1 elimina el asset "FONDOMENU" y que el fondo que se vea mientras jugamos sea una imagen negra.
User prompt
cuando comience el nivel 1 que el asset "FONDOMENU" sea solo color negro como te lo pedí. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
cuando recolectamos una pastafrola se suma un solo punto, pero hay otro bug este es que si recolectamos (por ejemplo) en un solo movimiento 2 pastafrolas se suman 4 puntos, esto esta mal, solo se deben suman en los puntos la cantidad real de pastafrolas que recolectamos. por otro lado, quiero que el fondo de el menu principal sea el asset: "FONDOMENU" y que la intensidad de su brillo sea solo de un 20% osea que es bastante oscuro, eso si, cuando comencemos a jugar un nivel que este asset fije su brillo a 0% (osea que sea negro) y que solo recupere su 20% de brillo cuando se muestre el menu. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
cuando seleccionamos el idioma español hay palabras que no aparecen en este idioma, corrige esto pues quiero que cuando elijamos el idioma español que TODAS las palabras aparezcan en este idioma, ademas en este idioma se bugueo el texto que sale cuando precionamos tutorial, arregla TODOS estos bugs porfavor. y la implementacion de los sonidos de victoria y derrota esta mal hecha pues n o se escuchan, ARREGLA ESTO.
User prompt
cuando seleccionamos el idioma ingles no todos los textos aparecen en ingles, quiero que cuando seleccionemos esa opcion que TODOS los textos aparezcan en ese idioma. por otro lado, cuando ganemos el primer nivel y se nos muestre la pantalla de victoria que suene el sonido: "victoria" pero en cambio si perdemos cuando se nos muestre la pantalla de derrota que sueñe el sonido: "derrota"
User prompt
Viste los botones en el menu? bueno, conserva el de jugar, y elimina los otros(el de lenguaje y el de tutorial) , que en su lugar haya uno llamado: "opciones" y que al presionarlo nos muestre las siguientes opciones: la de lenguaje llamada: "lenguaje" y otra opcion llamada niveles, y al presionar esta misma nos muestre los assets: "level1" y "level2" y que al presionar level1 volvamos al menu (el level1 es el nivel que estuvimos creando, por lo que ahora al darle a jugar comience ese nivel) y que al lado del asset "level 2" que este el asset: "candado" (este ultimo asset es para indicar que el level2 esta bloqueado por lo que aun no se puede jugar) que abajo del asset "level1" diga tutorial, al presionarlo que nos muestre un apartado con el siguiente texto: "Para moverte presiona el lugar donde quieras ir, para ganar derrota a los enemigos lanzándoles pastafrolas, recoléctalas pasando sobre ellas, y lánzaselas presionando al enemigo que quieras atacar! Tienes un contador que te indica cuantas pastafrolas tienes recolectadas. y un limite de tiempo de 2 minutos para completar el nivel!" y que abajo de "level2" este tambien un boton de tutorial, pero que al presionarlo salga el mensaje: "ups, este nivel esta bloqueado!" cuando comience el juego si no elegimos un nivel... pues cuando le demos a jugar que nos salga el siguiente mensaje: "primero selecciona el nivel que quieras jugar"
User prompt
hay un bug que a veces cuando tratamos de golpear a la pantera y esta no recibe daño, por favor arregla este bug, gracias., lo correcto sería que deberíamos podemos atacar a las pateras siempre y cuando no haya una barrera del laberinto entre nosotros y la pantera. (recuerda que su colisión es del tamaño de un bloque)
User prompt
HAS QUE LAS colisiones DE CADA PANTERA SEA EQUIVALENTE A UN BLOQUE
User prompt
arregla el codigo, ahora no podemos atacar a las panteras... quiero que podamos atacarlas pero estando como maximo a 3 bloques de distancia, pero que no podamos atacarla si entre la pantera y el raton hay un wall de por medio.
User prompt
has que tengas que estar muy cerca de las panteras para poder atacarlas, no puedes estar con una pared del laberinto separado de la pantera a la hora de atacar, para poder atacarla no puede haber ningun obstaculo entre nosotros (con obstaculo me refiero a las paredes del laberinto)
User prompt
elimina el sonido de menu del juego
User prompt
cuando golpeemos a las panteras que suene el sonido: "panterasgolpe"
User prompt
esta sonando como repetido... que solo suene una pista de audio a la vez
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1", { language: "spanish" }); /**** * Classes ****/ var Door = Container.expand(function () { var self = Container.call(this); var doorGraphics = self.attachAsset('door', { anchorX: 0.5, anchorY: 0.5 }); self.locked = true; self.update = function () { if (self.locked && panterasDefeated >= 2 && mouse.pastafrolas >= 10) { self.locked = false; doorGraphics.tint = 0x90EE90; // Light green when unlocked } if (!self.locked && self.intersects(mouse)) { // Show congratulations screen showCongratulationsScreen(); } }; return self; }); // Game constants var Mouse = Container.expand(function () { var self = Container.call(this); var mouseGraphics = self.attachAsset('mouse', { anchorX: 0.5, anchorY: 0.5 }); self.isMoving = false; self.targetX = 0; self.targetY = 0; self.speed = 8; self.pastafrolas = 0; self.moveTo = function (x, y) { var adjustedY = y - MAZE_OFFSET_Y; var gridX = Math.floor(x / CELL_SIZE); var gridY = Math.floor(adjustedY / CELL_SIZE); if (isWalkable(gridX, gridY)) { self.targetX = x; self.targetY = y; self.isMoving = true; } }; self.update = function () { if (self.isMoving) { var dx = self.targetX - self.x; var dy = self.targetY - self.y; var distance = Math.sqrt(dx * dx + dy * dy); // Calculate rotation angle based on movement direction var targetRotation = Math.atan2(dy, dx) + Math.PI / 2 + Math.PI; // Add PI/2 because mouse starts pointing down, plus PI to correct direction tween(mouseGraphics, { rotation: targetRotation }, { duration: 150, easing: tween.easeOut }); if (distance < self.speed) { self.x = self.targetX; self.y = self.targetY; self.isMoving = false; } else { var newX = self.x + dx / distance * self.speed; var newY = self.y + dy / distance * self.speed; var adjustedY = newY - MAZE_OFFSET_Y; var gridX = Math.floor(newX / CELL_SIZE); var gridY = Math.floor(adjustedY / CELL_SIZE); // Check collision during movement if (isWalkable(gridX, gridY)) { self.x = newX; self.y = newY; } else { // Stop movement if hitting wall self.isMoving = false; } } } }; return self; }); var Pantera = Container.expand(function () { var self = Container.call(this); var panteraGraphics = self.attachAsset('pantera', { anchorX: 0.5, anchorY: 0.5 }); self.health = 100; self.maxHealth = 100; self.defeated = false; // Override the width and height for collision detection to match block size self.width = 102; // CELL_SIZE self.height = 102; // CELL_SIZE self.takeDamage = function (damage) { if (!self.defeated) { self.health -= damage; LK.getSound('hit').play(); LK.getSound('panterasgolpe').play(); if (self.health <= 0) { self.health = 0; self.defeated = true; self.visible = false; panterasDefeated++; // Spawn new pastafrola when pantera is defeated spawnPastafrola(); } // Visual feedback LK.effects.flashObject(self, 0xFF0000, 300); } }; self.down = function (x, y, obj) { if (!self.defeated && mouse.pastafrolas > 0) { // Calculate distance between mouse and pantera centers var dx = mouse.x - self.x; var dy = mouse.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); var maxAttackDistance = CELL_SIZE * 3; // Must be within 3 cells // Convert positions to grid coordinates for more reliable line of sight check var mouseGridX = Math.floor(mouse.x / CELL_SIZE); var mouseGridY = Math.floor((mouse.y - MAZE_OFFSET_Y) / CELL_SIZE); var panteraGridX = Math.floor(self.x / CELL_SIZE); var panteraGridY = Math.floor((self.y - MAZE_OFFSET_Y) / CELL_SIZE); // Calculate grid distance (Manhattan distance for more lenient attack range) var gridDistance = Math.abs(mouseGridX - panteraGridX) + Math.abs(panteraGridY - mouseGridY); // Check if mouse is close enough (within 3 grid cells) and has line of sight if (gridDistance <= 3 && hasLineOfSight(mouse.x, mouse.y, self.x, self.y)) { mouse.pastafrolas--; self.takeDamage(20); LK.getSound('attack').play(); updateUI(); } else { // Show notification if attack failed due to distance or obstacles if (gridDistance > 3) { showNotification(currentLanguage === 'spanish' ? '¡Muy lejos para atacar!' : 'Too far to attack!'); } else { showNotification(currentLanguage === 'spanish' ? '¡Hay obstáculos en el camino!' : 'Obstacles are blocking the way!'); } } } }; return self; }); var Pastafrola = Container.expand(function () { var self = Container.call(this); var pastaGraphics = self.attachAsset('pastafrola', { anchorX: 0.5, anchorY: 0.5 }); self.collected = false; self.update = function () { if (!self.collected && self.intersects(mouse)) { self.collected = true; mouse.pastafrolas++; pastafrolarCollas++; LK.getSound('pastafrolasound').play(); self.visible = false; updateUI(); } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x8B4513 }); /**** * Game Code ****/ // Game state management var gameStarted = false; var startButton; var languageButton; var tutorialButton; var showingLanguageMenu = false; var currentLanguage = storage.language || 'spanish'; // Timer variables var gameTimer = 120000; // 2 minutes in milliseconds var gameStartTime = 0; var timeRemainingText; // Translations object var translations = { spanish: { play: 'JUGAR', language: 'LENGUAJE', tutorial: 'TUTORIAL', selectLanguage: 'SELECCIONA IDIOMA', spanish: 'ESPAÑOL', english: 'INGLÉS', congratulations: 'FELICIDADES', youWon: '¡GANASTE!', pastafrolas: 'Pastafrolas', panteras: 'Panteras', instructionCollect: 'Haz clic para moverte. ¡Recoge pastafrolas y derrota panteras!', instructionDefeat: '¡Derrota todas las panteras primero!', instructionNeed10: '¡Necesitas 10 pastafrolas en tu inventario!', instructionComplete: '¡Todos los objetivos completados! ¡Ve a la puerta verde!', allDefeated: '¡Todas las panteras derrotadas! Necesitas tener 10 pastafrolas en tu inventario para abrir la puerta', defeatEnemy: 'Derrota al enemigo de esta zona para continuar', tutorialText: 'Para moverte presiona el lugar donde quieras ir, para ganar derrota a los enemigos lanzándoles pastafrolas, recoléctalas pasando sobre ellas, y lánzaselas presionando al enemigo que quieras atacar! Tienes un contador que te indica cuantas pastafrolas tienes recolectadas.', defeat: 'PERDISTE!', betterNextTime: 'mejor la próxima vez', timeRemaining: 'Tiempo restante' }, english: { play: 'PLAY', language: 'LANGUAGE', tutorial: 'TUTORIAL', selectLanguage: 'SELECT LANGUAGE', spanish: 'SPANISH', english: 'ENGLISH', congratulations: 'CONGRATULATIONS', youWon: 'YOU WON!', pastafrolas: 'Pastafrolas', panteras: 'Panthers', instructionCollect: 'Click to move. Collect pastafrolas and defeat panthers!', instructionDefeat: 'Defeat all panthers first!', instructionNeed10: 'You need 10 pastafrolas in your inventory!', instructionComplete: 'All objectives complete! Head to the green door!', allDefeated: 'All panthers defeated! You need to have 10 pastafrolas in your inventory to open the door', defeatEnemy: 'Defeat the enemy in this area to continue', tutorialText: 'To move press the place where you want to go, to win defeat the enemies by throwing pastafrolas at them, collect them by passing over them, and throw them by pressing the enemy you want to attack! You have a counter that indicates how many pastafrolas you have collected.', defeat: 'YOU LOST!', betterNextTime: 'better luck next time', timeRemaining: 'Time remaining' } }; function getText(key) { return translations[currentLanguage][key] || key; } // Game constants var CELL_SIZE = 102; var MAZE_WIDTH = 20; var MAZE_HEIGHT = 22; var MAZE_OFFSET_Y = 300; // Vertical offset to move maze down // Game variables var mouse; var door; var pastafrolas = []; var panteras = []; var pastafrolarCollas = 0; var panterasDefeated = 0; var lastPanterasDefeated = 0; // Track last pantera count to detect changes // UI elements var pastafrolarText; var panteraText; var instructionText; var notificationText; // Simple maze layout (1 = wall, 0 = walkable) var mazeData = [[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], [1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1], [1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1], [1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1], [1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], [1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1], [1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1], [1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1], [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], [1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1], [1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1], [1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1], [1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1], [1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1], [1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], [1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1], [1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1], [1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1], [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], [1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1], [1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1], [1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1], [1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], [1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1], [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]]; function showCongratulationsScreen() { // Stop the game timer by setting gameStarted to false gameStarted = false; // Hide UI counters immediately if (pastafrolarText && pastafrolarText.parent) { pastafrolarText.visible = false; } if (panteraText && panteraText.parent) { panteraText.visible = false; } if (instructionText && instructionText.parent) { instructionText.visible = false; } if (notificationText && notificationText.parent) { notificationText.visible = false; } if (timeRemainingText && timeRemainingText.parent) { timeRemainingText.visible = false; } // Create congratulations background var congratsBackground = LK.getAsset('wall', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 1366, scaleX: 25, scaleY: 35, tint: 0x000000, alpha: 0.8 }); game.addChild(congratsBackground); // Create congratulations title var congratsTitle = new Text2(getText('congratulations'), { size: 120, fill: 0xFFD700 }); congratsTitle.anchor.set(0.5, 0.5); congratsTitle.x = 1024; congratsTitle.y = 1200; game.addChild(congratsTitle); // Create you won text var youWonText = new Text2(getText('youWon'), { size: 100, fill: 0x00FF00 }); youWonText.anchor.set(0.5, 0.5); youWonText.x = 1024; youWonText.y = 1400; game.addChild(youWonText); // Calculate and display remaining time var currentTime = Date.now(); var elapsedTime = currentTime - gameStartTime; var timeLeft = Math.max(0, gameTimer - elapsedTime); var secondsLeft = Math.ceil(timeLeft / 1000); var minutes = Math.floor(secondsLeft / 60); var seconds = secondsLeft % 60; var timeLeftStr = minutes + ':' + (seconds < 10 ? '0' : '') + seconds; // Create time remaining text var timeLeftText = new Text2(getText('timeRemaining') + ': ' + timeLeftStr, { size: 80, fill: 0xFFD700 }); timeLeftText.anchor.set(0.5, 0.5); timeLeftText.x = 1024; timeLeftText.y = 1550; game.addChild(timeLeftText); // Automatically return to menu after 3 seconds LK.setTimeout(function () { // Remove congratulations screen elements if (congratsBackground.parent) game.removeChild(congratsBackground); if (congratsTitle.parent) game.removeChild(congratsTitle); if (youWonText.parent) game.removeChild(youWonText); if (timeLeftText.parent) game.removeChild(timeLeftText); // Reset game state and return to main menu resetGameState(); showStartScreen(); }, 3000); } function showDefeatScreen() { // Hide UI counters immediately if (pastafrolarText && pastafrolarText.parent) { pastafrolarText.visible = false; } if (panteraText && panteraText.parent) { panteraText.visible = false; } if (instructionText && instructionText.parent) { instructionText.visible = false; } if (notificationText && notificationText.parent) { notificationText.visible = false; } if (timeRemainingText && timeRemainingText.parent) { timeRemainingText.visible = false; } // Create defeat background var defeatBackground = LK.getAsset('wall', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 1366, scaleX: 25, scaleY: 35, tint: 0x000000, alpha: 0.8 }); game.addChild(defeatBackground); // Create defeat title var defeatTitle = new Text2(getText('defeat'), { size: 120, fill: 0xFF4444 }); defeatTitle.anchor.set(0.5, 0.5); defeatTitle.x = 1024; defeatTitle.y = 1200; game.addChild(defeatTitle); // Create better next time text var betterNextTimeText = new Text2(getText('betterNextTime'), { size: 80, fill: 0xFFFFFF }); betterNextTimeText.anchor.set(0.5, 0.5); betterNextTimeText.x = 1024; betterNextTimeText.y = 1400; game.addChild(betterNextTimeText); // Automatically return to menu after 3 seconds LK.setTimeout(function () { // Remove defeat screen elements if (defeatBackground.parent) game.removeChild(defeatBackground); if (defeatTitle.parent) game.removeChild(defeatTitle); if (betterNextTimeText.parent) game.removeChild(betterNextTimeText); // Reset game state and return to main menu resetGameState(); showStartScreen(); }, 3000); } function showStartScreen() { // Create start screen title using titulo asset var titleText = LK.getAsset('titulo', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 800, scaleX: 1.3, scaleY: 1.3 }); game.addChild(titleText); // Add pulsing animation to title function animateTitle() { tween(titleText, { scaleX: 1.5, scaleY: 1.5 }, { duration: 1000, easing: tween.easeInOut, onFinish: function onFinish() { tween(titleText, { scaleX: 1.3, scaleY: 1.3 }, { duration: 1000, easing: tween.easeInOut, onFinish: animateTitle }); } }); } animateTitle(); // Create JUGAR button startButton = LK.getAsset('wall', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 1200, scaleX: 4.5, scaleY: 1.5, tint: 0x4CAF50 }); game.addChild(startButton); // Create button text var buttonText = new Text2(getText('play'), { size: 80, fill: 0xFFFFFF }); buttonText.anchor.set(0.5, 0.5); buttonText.x = 1024; buttonText.y = 1200; game.addChild(buttonText); // Create LANGUAGE button languageButton = LK.getAsset('wall', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 1400, scaleX: 5, scaleY: 1.5, tint: 0x2196F3 }); game.addChild(languageButton); // Create language button text var languageButtonText = new Text2(getText('language'), { size: 80, fill: 0xFFFFFF }); languageButtonText.anchor.set(0.5, 0.5); languageButtonText.x = 1024; languageButtonText.y = 1400; game.addChild(languageButtonText); // Create TUTORIAL button tutorialButton = LK.getAsset('wall', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 1600, scaleX: 4.5, scaleY: 1.5, tint: 0xFF9800 }); game.addChild(tutorialButton); // Create tutorial button text var tutorialButtonText = new Text2(getText('tutorial'), { size: 80, fill: 0xFFFFFF }); tutorialButtonText.anchor.set(0.5, 0.5); tutorialButtonText.x = 1024; tutorialButtonText.y = 1600; game.addChild(tutorialButtonText); // Add click handler to start button startButton.down = function (x, y, obj) { gameStarted = true; // gameStarted flag will stop menumusical sound from looping // Remove start screen elements game.removeChild(titleText); game.removeChild(startButton); game.removeChild(buttonText); game.removeChild(languageButton); game.removeChild(languageButtonText); game.removeChild(tutorialButton); game.removeChild(tutorialButtonText); // Initialize the actual game initializeGame(); }; // Add click handler to language button languageButton.down = function (x, y, obj) { // Remove start screen elements game.removeChild(titleText); game.removeChild(startButton); game.removeChild(buttonText); game.removeChild(languageButton); game.removeChild(languageButtonText); game.removeChild(tutorialButton); game.removeChild(tutorialButtonText); // Show language selection screen showLanguageScreen(); }; // Add click handler to tutorial button tutorialButton.down = function (x, y, obj) { // Remove start screen elements game.removeChild(titleText); game.removeChild(startButton); game.removeChild(buttonText); game.removeChild(languageButton); game.removeChild(languageButtonText); game.removeChild(tutorialButton); game.removeChild(tutorialButtonText); // Show tutorial screen showTutorialScreen(); }; } function showLanguageScreen() { showingLanguageMenu = true; // Create title var languageTitle = new Text2(getText('selectLanguage'), { size: 100, fill: 0xFFFFFF }); languageTitle.anchor.set(0.5, 0.5); languageTitle.x = 1024; languageTitle.y = 800; game.addChild(languageTitle); // Create Spanish button var spanishButton = LK.getAsset('wall', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 1100, scaleX: 3, scaleY: 1.5, tint: currentLanguage === 'spanish' ? 0x4CAF50 : 0x757575 }); game.addChild(spanishButton); var spanishButtonText = new Text2(getText('spanish'), { size: 70, fill: 0xFFFFFF }); spanishButtonText.anchor.set(0.5, 0.5); spanishButtonText.x = 1024; spanishButtonText.y = 1100; game.addChild(spanishButtonText); // Create English button var englishButton = LK.getAsset('wall', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 1300, scaleX: 3, scaleY: 1.5, tint: currentLanguage === 'english' ? 0x4CAF50 : 0x757575 }); game.addChild(englishButton); var englishButtonText = new Text2(getText('english'), { size: 70, fill: 0xFFFFFF }); englishButtonText.anchor.set(0.5, 0.5); englishButtonText.x = 1024; englishButtonText.y = 1300; game.addChild(englishButtonText); // Spanish button click handler spanishButton.down = function (x, y, obj) { currentLanguage = 'spanish'; storage.language = 'spanish'; // Remove language screen elements game.removeChild(languageTitle); game.removeChild(spanishButton); game.removeChild(spanishButtonText); game.removeChild(englishButton); game.removeChild(englishButtonText); showingLanguageMenu = false; // Return to start screen showStartScreen(); }; // English button click handler englishButton.down = function (x, y, obj) { currentLanguage = 'english'; storage.language = 'english'; // Remove language screen elements game.removeChild(languageTitle); game.removeChild(spanishButton); game.removeChild(spanishButtonText); game.removeChild(englishButton); game.removeChild(englishButtonText); showingLanguageMenu = false; // Return to start screen showStartScreen(); }; } function showTutorialScreen() { // Create tutorial background var tutorialBackground = LK.getAsset('wall', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 1366, scaleX: 24, scaleY: 32, tint: 0x1A1A1A, alpha: 0.9 }); game.addChild(tutorialBackground); // Create tutorial title var tutorialTitle = new Text2(getText('tutorial'), { size: 100, fill: 0xFFD700 }); tutorialTitle.anchor.set(0.5, 0.5); tutorialTitle.x = 1024; tutorialTitle.y = 400; game.addChild(tutorialTitle); // Create tutorial text - break into multiple lines for better readability var tutorialText = new Text2(getText('tutorialText'), { size: 50, fill: 0xFFFFFF, wordWrap: true, wordWrapWidth: 1800 }); tutorialText.anchor.set(0.5, 0.5); tutorialText.x = 1024; tutorialText.y = 1000; game.addChild(tutorialText); // Create back button var backButton = LK.getAsset('wall', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 1700, scaleX: 2.5, scaleY: 1.2, tint: 0x4CAF50 }); game.addChild(backButton); // Create back button text var backButtonText = new Text2(currentLanguage === 'spanish' ? 'VOLVER' : 'BACK', { size: 60, fill: 0xFFFFFF }); backButtonText.anchor.set(0.5, 0.5); backButtonText.x = 1024; backButtonText.y = 1700; game.addChild(backButtonText); // Add click handler to back button backButton.down = function (x, y, obj) { // Remove tutorial screen elements game.removeChild(tutorialBackground); game.removeChild(tutorialTitle); game.removeChild(tutorialText); game.removeChild(backButton); game.removeChild(backButtonText); // Return to start screen showStartScreen(); }; } function initializeGame() { // Build the maze for (var y = 0; y < MAZE_HEIGHT; y++) { for (var x = 0; x < MAZE_WIDTH; x++) { if (y < mazeData.length && mazeData[y][x] === 1) { var wall = LK.getAsset('wall', { anchorX: 0.5, anchorY: 0.5, x: x * CELL_SIZE + CELL_SIZE / 2, y: y * CELL_SIZE + CELL_SIZE / 2 + MAZE_OFFSET_Y }); game.addChild(wall); } else if (y < mazeData.length) { var floor = LK.getAsset('floor', { anchorX: 0.5, anchorY: 0.5, x: x * CELL_SIZE + CELL_SIZE / 2, y: y * CELL_SIZE + CELL_SIZE / 2 + MAZE_OFFSET_Y }); game.addChild(floor); } } } // Create mouse mouse = new Mouse(); mouse.x = 1.5 * CELL_SIZE; mouse.y = 1.5 * CELL_SIZE + MAZE_OFFSET_Y; game.addChild(mouse); // Create door at the bottom right door = new Door(); door.x = 18 * CELL_SIZE + CELL_SIZE / 2; door.y = (MAZE_HEIGHT - 2) * CELL_SIZE + CELL_SIZE / 2 + MAZE_OFFSET_Y; game.addChild(door); // Spawn initial pastafrolas for (var i = 0; i < 20; i++) { spawnPastafrola(); } // Spawn 2 panteras in strategic positions var panteraPositions = [{ x: 5, y: 5 }, { x: 15, y: 8 }]; for (var i = 0; i < panteraPositions.length; i++) { var pos = panteraPositions[i]; if (isWalkable(pos.x, pos.y)) { var pantera = new Pantera(); pantera.x = pos.x * CELL_SIZE + CELL_SIZE / 2; pantera.y = pos.y * CELL_SIZE + CELL_SIZE / 2 + MAZE_OFFSET_Y; panteras.push(pantera); game.addChild(pantera); } } // Setup UI - all elements in top area to avoid click interference pastafrolarText = new Text2(getText('pastafrolas') + ': 0', { size: 60, fill: 0xFFFFFF }); pastafrolarText.anchor.set(0.5, 0); pastafrolarText.y = 0; // At very top LK.gui.top.addChild(pastafrolarText); // Add panteras defeated text below pastafrolas text panteraText = new Text2(getText('panteras') + ': 0/2', { size: 50, fill: 0xFFFFFF }); panteraText.anchor.set(0.5, 0); panteraText.y = 70; // Below pastafrola text LK.gui.top.addChild(panteraText); instructionText = new Text2(getText('instructionCollect'), { size: 40, fill: 0xFFFFFF }); instructionText.anchor.set(0.5, 0); instructionText.y = 130; // Below pantera text LK.gui.top.addChild(instructionText); notificationText = new Text2('', { size: 45, fill: 0xFF4444 }); notificationText.anchor.set(0.5, 1); notificationText.visible = false; LK.gui.bottom.addChild(notificationText); // Initialize game timer gameStartTime = Date.now(); // Create time remaining display timeRemainingText = new Text2(getText('timeRemaining') + ': 2:00', { size: 50, fill: 0xFFFFFF }); timeRemainingText.anchor.set(0.5, 0); timeRemainingText.y = 180; // Below instruction text LK.gui.top.addChild(timeRemainingText); // Update UI updateUI(); } // Show start screen initially showStartScreen(); function isWalkable(gridX, gridY) { if (gridX < 0 || gridX >= MAZE_WIDTH || gridY < 0 || gridY >= MAZE_HEIGHT) { return false; } return mazeData[gridY][gridX] === 0; } function hasLineOfSight(x1, y1, x2, y2) { // Convert world coordinates to grid coordinates more accurately var gridX1 = Math.floor(x1 / CELL_SIZE); var gridY1 = Math.floor((y1 - MAZE_OFFSET_Y) / CELL_SIZE); var gridX2 = Math.floor(x2 / CELL_SIZE); var gridY2 = Math.floor((y2 - MAZE_OFFSET_Y) / CELL_SIZE); // Ensure coordinates are within bounds if (gridX1 < 0 || gridX1 >= MAZE_WIDTH || gridY1 < 0 || gridY1 >= MAZE_HEIGHT || gridX2 < 0 || gridX2 >= MAZE_WIDTH || gridY2 < 0 || gridY2 >= MAZE_HEIGHT) { return false; } // Use Bresenham's line algorithm to check each grid cell between the two points var dx = Math.abs(gridX2 - gridX1); var dy = Math.abs(gridY2 - gridY1); var x = gridX1; var y = gridY1; var xInc = gridX1 < gridX2 ? 1 : -1; var yInc = gridY1 < gridY2 ? 1 : -1; var error = dx - dy; // Don't check the starting position (mouse position) as it's always walkable var stepCount = 0; while (true) { // If we reached the target, we have clear line of sight if (x === gridX2 && y === gridY2) { break; } var error2 = 2 * error; if (error2 > -dy) { error -= dy; x += xInc; } if (error2 < dx) { error += dx; y += yInc; } stepCount++; // Check if current position is a wall (not walkable), but skip first and last positions if (stepCount > 0 && (x !== gridX2 || y !== gridY2) && !isWalkable(x, y)) { return false; // Wall blocks line of sight } // Prevent infinite loops if (stepCount > 50) { return false; } } return true; // Clear line of sight } var barriers = []; // Global array to track barriers function canAccessArea(gridX, gridY) { // Define restricted areas that unlock as panteras are defeated var restrictedAreas = [{ minX: 0, maxX: 6, minY: 0, maxY: 8, requiredDefeats: 0 }, // Starting area { minX: 7, maxX: 13, minY: 0, maxY: 8, requiredDefeats: 1 }, // Second area { minX: 14, maxX: 19, minY: 0, maxY: 8, requiredDefeats: 2 }, // Third area { minX: 0, maxX: 6, minY: 9, maxY: 17, requiredDefeats: 2 }, // Fourth area { minX: 7, maxX: 13, minY: 9, maxY: 17, requiredDefeats: 3 }, // Fifth area { minX: 14, maxX: 19, minY: 9, maxY: 17, requiredDefeats: 4 }, // Sixth area { minX: 0, maxX: 19, minY: 18, maxY: 26, requiredDefeats: 5 } // Final area ]; for (var i = 0; i < restrictedAreas.length; i++) { var area = restrictedAreas[i]; if (gridX >= area.minX && gridX <= area.maxX && gridY >= area.minY && gridY <= area.maxY) { if (panterasDefeated < area.requiredDefeats) { showNotification(getText('defeatEnemy')); return false; } return true; } } return true; // Default allow access } function createBarriers() { // Create barriers at area boundaries var barrierPositions = [ // Vertical barrier between areas 1 and 2 { x: 6, y: 1 }, { x: 6, y: 2 }, { x: 6, y: 3 }, { x: 6, y: 4 }, { x: 6, y: 5 }, { x: 6, y: 6 }, { x: 6, y: 7 }, // Vertical barrier between areas 2 and 3 { x: 13, y: 1 }, { x: 13, y: 2 }, { x: 13, y: 3 }, { x: 13, y: 4 }, { x: 13, y: 5 }, { x: 13, y: 6 }, { x: 13, y: 7 }, // Horizontal barrier between upper and middle sections { x: 1, y: 8 }, { x: 2, y: 8 }, { x: 3, y: 8 }, { x: 4, y: 8 }, { x: 5, y: 8 }, { x: 7, y: 8 }, { x: 8, y: 8 }, { x: 9, y: 8 }, { x: 10, y: 8 }, { x: 11, y: 8 }, { x: 12, y: 8 }, { x: 14, y: 8 }, { x: 15, y: 8 }, { x: 16, y: 8 }, { x: 17, y: 8 }, { x: 18, y: 8 }, // Vertical barriers in middle section { x: 6, y: 10 }, { x: 6, y: 11 }, { x: 6, y: 13 }, { x: 6, y: 14 }, { x: 6, y: 15 }, { x: 6, y: 16 }, { x: 13, y: 10 }, { x: 13, y: 11 }, { x: 13, y: 13 }, { x: 13, y: 14 }, { x: 13, y: 15 }, { x: 13, y: 16 }, // Horizontal barrier between middle and bottom sections { x: 1, y: 17 }, { x: 2, y: 17 }, { x: 3, y: 17 }, { x: 4, y: 17 }, { x: 5, y: 17 }, { x: 6, y: 17 }, { x: 7, y: 17 }, { x: 8, y: 17 }, { x: 9, y: 17 }, { x: 10, y: 17 }, { x: 11, y: 17 }, { x: 12, y: 17 }, { x: 13, y: 17 }, { x: 14, y: 17 }, { x: 15, y: 17 }, { x: 16, y: 17 }, { x: 17, y: 17 }, { x: 18, y: 17 }]; for (var i = 0; i < barrierPositions.length; i++) { var pos = barrierPositions[i]; if (isWalkable(pos.x, pos.y)) { var barrier = LK.getAsset('barrier', { anchorX: 0.5, anchorY: 0.5, x: pos.x * CELL_SIZE + CELL_SIZE / 2, y: pos.y * CELL_SIZE + CELL_SIZE / 2 + MAZE_OFFSET_Y, alpha: 0.7 }); barriers.push(barrier); game.addChild(barrier); } } } function updateBarrierVisibility() { // Hide barriers in unlocked areas for (var i = 0; i < barriers.length; i++) { var barrier = barriers[i]; var gridX = Math.floor((barrier.x - CELL_SIZE / 2) / CELL_SIZE); var gridY = Math.floor((barrier.y - CELL_SIZE / 2 - MAZE_OFFSET_Y) / CELL_SIZE); // Check if this barrier should be visible based on area access var shouldShow = !canAccessAreaSilent(gridX, gridY); barrier.visible = shouldShow; } } function canAccessAreaSilent(gridX, gridY) { // Same as canAccessArea but without showing notification var restrictedAreas = [{ minX: 0, maxX: 6, minY: 0, maxY: 8, requiredDefeats: 0 }, { minX: 7, maxX: 13, minY: 0, maxY: 8, requiredDefeats: 1 }, { minX: 14, maxX: 19, minY: 0, maxY: 8, requiredDefeats: 2 }, { minX: 0, maxX: 6, minY: 9, maxY: 17, requiredDefeats: 2 }, { minX: 7, maxX: 13, minY: 9, maxY: 17, requiredDefeats: 3 }, { minX: 14, maxX: 19, minY: 9, maxY: 17, requiredDefeats: 4 }, { minX: 0, maxX: 19, minY: 18, maxY: 26, requiredDefeats: 5 }]; for (var i = 0; i < restrictedAreas.length; i++) { var area = restrictedAreas[i]; if (gridX >= area.minX && gridX <= area.maxX && gridY >= area.minY && gridY <= area.maxY) { return panterasDefeated >= area.requiredDefeats; } } return true; } function getRandomWalkablePosition() { var attempts = 0; while (attempts < 100) { var x = Math.floor(Math.random() * MAZE_WIDTH); var y = Math.floor(Math.random() * MAZE_HEIGHT); if (isWalkable(x, y)) { return { x: x * CELL_SIZE + CELL_SIZE / 2, y: y * CELL_SIZE + CELL_SIZE / 2 + MAZE_OFFSET_Y }; } attempts++; } return { x: 1.5 * CELL_SIZE, y: 1.5 * CELL_SIZE + MAZE_OFFSET_Y }; // Fallback position } function getRandomWalkablePositionIncludingRestricted() { var attempts = 0; while (attempts < 100) { var x = Math.floor(Math.random() * MAZE_WIDTH); var y = Math.floor(Math.random() * MAZE_HEIGHT); // Allow spawning in any walkable area, including restricted ones if (isWalkable(x, y)) { return { x: x * CELL_SIZE + CELL_SIZE / 2, y: y * CELL_SIZE + CELL_SIZE / 2 + MAZE_OFFSET_Y }; } attempts++; } return { x: 1.5 * CELL_SIZE, y: 1.5 * CELL_SIZE + MAZE_OFFSET_Y }; // Fallback position } function spawnPastafrola() { var position = getRandomWalkablePositionIncludingRestricted(); var pastafrola = new Pastafrola(); pastafrola.x = position.x; pastafrola.y = position.y; pastafrolas.push(pastafrola); game.addChild(pastafrola); } function resetGameState() { // Reset game state gameStarted = false; pastafrolarCollas = 0; panterasDefeated = 0; lastPanterasDefeated = 0; gameTimer = 120000; gameStartTime = 0; // Clear all game objects if (mouse) { game.removeChild(mouse); mouse = null; } if (door) { game.removeChild(door); door = null; } // Clear arrays and remove objects for (var i = 0; i < pastafrolas.length; i++) { if (pastafrolas[i].parent) { game.removeChild(pastafrolas[i]); } } pastafrolas = []; for (var i = 0; i < panteras.length; i++) { if (panteras[i].parent) { game.removeChild(panteras[i]); } } panteras = []; for (var i = 0; i < barriers.length; i++) { if (barriers[i].parent) { game.removeChild(barriers[i]); } } barriers = []; // Clear UI elements if (pastafrolarText && pastafrolarText.parent) { LK.gui.top.removeChild(pastafrolarText); pastafrolarText = null; } if (panteraText && panteraText.parent) { LK.gui.top.removeChild(panteraText); panteraText = null; } if (instructionText && instructionText.parent) { LK.gui.top.removeChild(instructionText); instructionText = null; } if (notificationText && notificationText.parent) { LK.gui.bottom.removeChild(notificationText); notificationText = null; } if (timeRemainingText && timeRemainingText.parent) { LK.gui.top.removeChild(timeRemainingText); timeRemainingText = null; } // Clear all remaining children from game - create a copy of the array to avoid modification during iteration var childrenToRemove = []; for (var i = 0; i < game.children.length; i++) { childrenToRemove.push(game.children[i]); } for (var i = 0; i < childrenToRemove.length; i++) { if (childrenToRemove[i].parent === game) { game.removeChild(childrenToRemove[i]); } } } function showNotification(message) { if (notificationText) { notificationText.setText(message); notificationText.visible = true; LK.setTimeout(function () { if (notificationText) { notificationText.visible = false; } }, 2000); } } function updateUI() { pastafrolarText.setText(getText('pastafrolas') + ': ' + mouse.pastafrolas); panteraText.setText(getText('panteras') + ': ' + panterasDefeated + '/2'); if (panterasDefeated < 2) { instructionText.setText(getText('instructionDefeat') + ' (' + panterasDefeated + '/2)'); } else if (mouse.pastafrolas < 10) { instructionText.setText(getText('instructionNeed10') + ' (' + mouse.pastafrolas + '/10)'); } else { instructionText.setText(getText('instructionComplete')); } } // Game initialization now handled by start screen // Game controls game.down = function (x, y, obj) { if (gameStarted && mouse) { mouse.moveTo(x, y); } }; // Main game loop game.update = function () { if (!gameStarted) return; // Update timer var currentTime = Date.now(); var elapsedTime = currentTime - gameStartTime; var timeLeft = Math.max(0, gameTimer - elapsedTime); // Update timer display if (timeRemainingText) { var secondsLeft = Math.ceil(timeLeft / 1000); var minutes = Math.floor(secondsLeft / 60); var seconds = secondsLeft % 60; var timeStr = minutes + ':' + (seconds < 10 ? '0' : '') + seconds; timeRemainingText.setText(getText('timeRemaining') + ': ' + timeStr); // Change color when time is running out if (timeLeft <= 30000) { // Last 30 seconds timeRemainingText.tint = 0xFF4444; } else if (timeLeft <= 60000) { // Last minute timeRemainingText.tint = 0xFFAA00; } else { timeRemainingText.tint = 0xFFFFFF; } } // Check if time is up if (timeLeft <= 0) { showDefeatScreen(); return; } // Check if we just defeated all panteras (transition from not all defeated to all defeated) if (lastPanterasDefeated < 2 && panterasDefeated >= 2) { showNotification(getText('allDefeated')); } lastPanterasDefeated = panterasDefeated; // Update mouse if (mouse) { mouse.update(); } // Update pastafrolas for (var i = 0; i < pastafrolas.length; i++) { pastafrolas[i].update(); } // Update panteras for (var i = 0; i < panteras.length; i++) { if (!panteras[i].defeated) { // Simple AI: panteras don't move but can be clicked for combat } } // Update door if (door) { door.update(); } // Barrier visibility removed - no restrictions // Spawn more pastafrolas if needed and not all collected if (pastafrolarCollas < 10 && pastafrolas.filter(function (p) { return !p.collected; }).length < 15) { if (Math.random() < 0.05) { // 5% chance per frame spawnPastafrola(); } } };
===================================================================
--- original.js
+++ change.js
@@ -116,22 +116,29 @@
}
};
self.down = function (x, y, obj) {
if (!self.defeated && mouse.pastafrolas > 0) {
- // Calculate distance between mouse and pantera
+ // Calculate distance between mouse and pantera centers
var dx = mouse.x - self.x;
var dy = mouse.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
var maxAttackDistance = CELL_SIZE * 3; // Must be within 3 cells
- // Check if mouse is close enough and has line of sight
- if (distance <= maxAttackDistance && hasLineOfSight(mouse.x, mouse.y, self.x, self.y)) {
+ // Convert positions to grid coordinates for more reliable line of sight check
+ var mouseGridX = Math.floor(mouse.x / CELL_SIZE);
+ var mouseGridY = Math.floor((mouse.y - MAZE_OFFSET_Y) / CELL_SIZE);
+ var panteraGridX = Math.floor(self.x / CELL_SIZE);
+ var panteraGridY = Math.floor((self.y - MAZE_OFFSET_Y) / CELL_SIZE);
+ // Calculate grid distance (Manhattan distance for more lenient attack range)
+ var gridDistance = Math.abs(mouseGridX - panteraGridX) + Math.abs(panteraGridY - mouseGridY);
+ // Check if mouse is close enough (within 3 grid cells) and has line of sight
+ if (gridDistance <= 3 && hasLineOfSight(mouse.x, mouse.y, self.x, self.y)) {
mouse.pastafrolas--;
self.takeDamage(20);
LK.getSound('attack').play();
updateUI();
} else {
// Show notification if attack failed due to distance or obstacles
- if (distance > maxAttackDistance) {
+ if (gridDistance > 3) {
showNotification(currentLanguage === 'spanish' ? '¡Muy lejos para atacar!' : 'Too far to attack!');
} else {
showNotification(currentLanguage === 'spanish' ? '¡Hay obstáculos en el camino!' : 'Obstacles are blocking the way!');
}
@@ -770,26 +777,28 @@
}
return mazeData[gridY][gridX] === 0;
}
function hasLineOfSight(x1, y1, x2, y2) {
- // Convert world coordinates to grid coordinates
- var gridX1 = Math.floor((x1 - CELL_SIZE / 2) / CELL_SIZE);
- var gridY1 = Math.floor((y1 - MAZE_OFFSET_Y - CELL_SIZE / 2) / CELL_SIZE);
- var gridX2 = Math.floor((x2 - CELL_SIZE / 2) / CELL_SIZE);
- var gridY2 = Math.floor((y2 - MAZE_OFFSET_Y - CELL_SIZE / 2) / CELL_SIZE);
+ // Convert world coordinates to grid coordinates more accurately
+ var gridX1 = Math.floor(x1 / CELL_SIZE);
+ var gridY1 = Math.floor((y1 - MAZE_OFFSET_Y) / CELL_SIZE);
+ var gridX2 = Math.floor(x2 / CELL_SIZE);
+ var gridY2 = Math.floor((y2 - MAZE_OFFSET_Y) / CELL_SIZE);
+ // Ensure coordinates are within bounds
+ if (gridX1 < 0 || gridX1 >= MAZE_WIDTH || gridY1 < 0 || gridY1 >= MAZE_HEIGHT || gridX2 < 0 || gridX2 >= MAZE_WIDTH || gridY2 < 0 || gridY2 >= MAZE_HEIGHT) {
+ return false;
+ }
// Use Bresenham's line algorithm to check each grid cell between the two points
var dx = Math.abs(gridX2 - gridX1);
var dy = Math.abs(gridY2 - gridY1);
var x = gridX1;
var y = gridY1;
var xInc = gridX1 < gridX2 ? 1 : -1;
var yInc = gridY1 < gridY2 ? 1 : -1;
var error = dx - dy;
+ // Don't check the starting position (mouse position) as it's always walkable
+ var stepCount = 0;
while (true) {
- // Check if current position is a wall (not walkable)
- if (!isWalkable(x, y)) {
- return false; // Wall blocks line of sight
- }
// If we reached the target, we have clear line of sight
if (x === gridX2 && y === gridY2) {
break;
}
@@ -801,8 +810,17 @@
if (error2 < dx) {
error += dx;
y += yInc;
}
+ stepCount++;
+ // Check if current position is a wall (not walkable), but skip first and last positions
+ if (stepCount > 0 && (x !== gridX2 || y !== gridY2) && !isWalkable(x, y)) {
+ return false; // Wall blocks line of sight
+ }
+ // Prevent infinite loops
+ if (stepCount > 50) {
+ return false;
+ }
}
return true; // Clear line of sight
}
var barriers = []; // Global array to track barriers