User prompt
🖱️ Instrucción: Reversión a Control por Clic del Ratón Debemos reactivar la lógica de entrada del ratón y asegurar que la serpiente se mueva una sola vez por clic en la dirección apuntada. 1. Desactivación de Controles de Teclado (CRÍTICO) Instrucción para la IA: Eliminar la Lógica de keyDown: En la clase SnakeGame, elimina o comenta todo el código relacionado con la detección de LK.on('keyDown') o la asignación de direcciones ('up', 'down', etc.) a las teclas W, A, S, D. El Gusano solo debe moverse en respuesta a un evento de clic. 2. Implementación de Control por Clic La lógica debe tomar la posición en píxeles del clic, convertirla a la coordenada de grilla y establecer la dirección de la Serpiente. Instrucción para la IA: Reinstalar LK.on('click') o LK.on('mouseDown'): Conversión de Coordenadas: Dentro del handler del clic, convierte las coordenadas de píxel del ratón (clickX, clickY) a coordenadas de celda de la grilla (targetGridX, targetGridY) usando Math.floor(clickX / self.gridSize). Determinación de Dirección (El Movimiento de Un Paso): La serpiente solo debe cambiar self.direction hacia la celda clicada, manteniendo la restricción de no girar 180 grados. Calcular Distancia: Determina si el clic está más lejos horizontal (dx) o vertical (dy) y prioriza ese movimiento. Aplicar Dirección: Si el clic está, por ejemplo, a la derecha y la serpiente no va a la izquierda (self.direction !== 'left'), establece self.direction = 'right'. Ejecución Única: El clic solo establece la dirección (self.direction). El movimiento en sí debe seguir ejecutándose una vez por cada intervalo de tiempo (self.moveInterval) dentro de self.update(). El clic simplemente cambia la dirección para el siguiente "tick" del Gusano.
User prompt
pero que el gusano siga siendo esfera
User prompt
cambiale el color al gusano por uno verde y cambia el color de la mansana a rojo
User prompt
🖱️ Instrucción: Reversión a Control por Clic del Ratón Debemos reactivar la lógica de entrada del ratón y asegurar que la serpiente se mueva una sola vez por clic en la dirección apuntada. 1. Desactivación de Controles de Teclado (CRÍTICO) Instrucción para la IA: Eliminar la Lógica de keyDown: En la clase SnakeGame, elimina o comenta todo el código relacionado con la detección de LK.on('keyDown') o la asignación de direcciones ('up', 'down', etc.) a las teclas W, A, S, D. El Gusano solo debe moverse en respuesta a un evento de clic. 2. Implementación de Control por Clic La lógica debe tomar la posición en píxeles del clic, convertirla a la coordenada de grilla y establecer la dirección de la Serpiente. Instrucción para la IA: Reinstalar LK.on('click') o LK.on('mouseDown'): Conversión de Coordenadas: Dentro del handler del clic, convierte las coordenadas de píxel del ratón (clickX, clickY) a coordenadas de celda de la grilla (targetGridX, targetGridY) usando Math.floor(clickX / self.gridSize). Determinación de Dirección (El Movimiento de Un Paso): La serpiente solo debe cambiar self.direction hacia la celda clicada, manteniendo la restricción de no girar 180 grados. Calcular Distancia: Determina si el clic está más lejos horizontal (dx) o vertical (dy) y prioriza ese movimiento. Aplicar Dirección: Si el clic está, por ejemplo, a la derecha y la serpiente no va a la izquierda (self.direction !== 'left'), establece self.direction = 'right'. Ejecución Única: El clic solo establece la dirección (self.direction). El movimiento en sí debe seguir ejecutándose una vez por cada intervalo de tiempo (self.moveInterval) dentro de self.update(). El clic simplemente cambia la dirección para el siguiente "tick" del Gusano.
User prompt
as los textos de la tienda mas grandes
User prompt
¡Comprendido\! Si la solución de **destruir y recrear** sprites en cada fotograma **no logró hacer visible** el Gusano y la Comida, significa que el problema es más fundamental que un simple fallo en la capa de renderizado. La única explicación lógica es que la IA no está pudiendo acceder correctamente al asset (`gusano_minijuego4`) o a las funciones de dibujo (`LK.Sprite`). Debemos darle una instrucción que **verifique y aísle** el problema, obligándola a usar una primitiva de dibujo simple (como un círculo) para confirmar si el problema está en la lógica o en el asset. ----- ## 🔍 Arreglo Final: Diagnóstico y Solución de Visibilidad Extrema Vamos a obligar a la IA a verificar el problema dibujando formas que no dependen de la carga de assets. ### 1\. Diagnóstico: Uso de Dibujo Primitivo (CRÍTICO) La IA debe reemplazar temporalmente el asset (`gusano_minijuego4`) por una forma simple que el motor gráfico sí pueda dibujar. **Instrucción para la IA:** > **Reemplazar el Dibujo del Gusano con un Círculo de Prueba:** > > 1. En la función de renderizado (`self.renderGame()`), comenta la línea que crea el `LK.Sprite('gusano_minijuego4')`. > 2. Reemplázala con la creación de una forma primitiva del motor, como un **círculo relleno de color blanco o rojo brillante**. > > > ```javascript > // Reemplaza la creación del sprite por: > var sprite = new LK.Circle(self.gridSize / 2, 0xFF0000); // Radio y color (Rojo Brillante) > sprite.x = segmentPosPix.x; > sprite.y = segmentPosPix.y; > // Sigue asegurando que se añade a la lista self.gusanoSprites > self.gusanoSprites.push(sprite); > ``` > > **PRUEBA A:** Si el círculo **aparece** visible y se mueve correctamente, el problema es que el asset **`gusano_minijuego4` no se está cargando o el nombre es incorrecto**. > > **PRUEBA B:** Si el círculo **tampoco aparece**, el problema es que la función `self.renderGame()` **no se está llamando** o la lógica de `self.gusanoSprites.destroy()` está fallando. ----- ### 2\. Solución Basada en el Diagnóstico La IA debe actuar basándose en el resultado de la prueba: **Instrucción para la IA:** > **Acción Requerida (Después de la Prueba con el Círculo):** > > **CASO A: Si el Círculo FUNCIONÓ (El problema es el Asset):** > > * La IA debe **verificar dos veces el nombre exacto** del asset (`gusano_minijuego4`) en la lista de recursos cargados. > * Una vez confirmado el nombre, la IA debe asegurarse de que la imagen del Gusano no sea una imagen transparente o blanca que se confunda con el fondo. > > **CASO B: Si el Círculo NO FUNCIONÓ (El problema es el Código):** > > * La IA debe **verificar que `self.renderGame()` se llama como la ÚLTIMA línea dentro de `self.update()`**. > * La IA debe asegurarse de que el objeto Gusano (`self.snake`) **tenga al menos un segmento** al inicio del juego. > * Debe confirmar que la función `destroy()` para la limpieza (`sprite.destroy()`) se está ejecutando correctamente en cada fotograma. Con este método de prueba y error forzado, la IA debería poder aislar finalmente la causa raíz de la invisibilidad.
User prompt
¡Entendido! Haremos que el Gusano y la Manzana (Esfera de Comida) sean **aún más grandes** para mejorar la visibilidad y jugabilidad en el Minijuego 4. Esto implica simplemente aumentar el valor de **`self.gridSize`** una vez más. --- ## 📏 Instrucción: Aumento de Tamaño (Gusano y Comida) La IA debe incrementar el valor que define el tamaño de la celda de la grilla (`self.gridSize`). **Instrucción para la IA:** > **Aumentar el Tamaño de `self.gridSize`:** > > 1. En la clase `SnakeGame`, incrementa el valor de **`self.gridSize`** a un nuevo valor aún mayor (por ejemplo, si era 80, ajústalo a **`self.gridSize = 120;`**). > 2. **Mantenimiento:** Confirma que el Gusano (`gusano_minijuego4`) y la Comida (Esfera) sigan utilizando este **`self.gridSize`** como su `width` y `height` en la función de renderizado (`self.renderGame()`) para que crezcan visualmente. > > 3. **Ajuste de Límites:** Vuelve a verificar que los límites de la grilla (`self.gridWidth` y `self.gridHeight`) se ajusten dividiendo el área del `suelo` por este nuevo y mayor **`self.gridSize`**. Con este ajuste, el Gusano y la Comida serán mucho más prominentes en la pantalla.
User prompt
¡Absolutamente\! Entendido. Vamos a cambiar el método de control del Gusano del teclado (`W, A, S, D` o Flechas) a un **control por clic del ratón** (mouse click), con la restricción de **un movimiento por clic**. Aquí tienes la instrucción clara y específica para Upit IA: ----- ## 🖱️ Instrucción: Control del Gusano por Clic del Ratón Debemos **eliminar** la lógica de control por teclas y reemplazarla con la lógica de entrada por el ratón. ### 1\. Desactivación de Controles de Teclado (CRÍTICO) **Instrucción para la IA:** > **Eliminar la Lógica de `keyDown`:** > > 1. En la clase `SnakeGame`, elimina o comenta todo el código relacionado con la detección de `LK.on('keyDown')` o cualquier función que asigne direcciones de movimiento (`'up'`, `'down'`, etc.) a las teclas. > 2. El Gusano **solo** debe moverse en respuesta a un clic del ratón. ----- ### 2\. Implementación de Control por Clic La lógica debe tomar la posición en **píxeles** del clic, convertirla a una **coordenada de grilla** y determinar la dirección de movimiento de la Serpiente hacia esa celda. **Instrucción para la IA:** > **Implementar `LK.on('click')` o `LK.on('mouseDown')`:** > > 1. **Detección de Clic:** Usa el evento que detecta un clic izquierdo. > 2. **Conversión de Coordenadas:** Cuando ocurre un clic en las coordenadas de píxel `(clickX, clickY)`, usa la función auxiliar de la grilla para convertirlas a coordenadas de celda `(gridX, gridY)`. > * `targetGridX = Math.floor(clickX / self.gridSize);` > * `targetGridY = Math.floor(clickY / self.gridSize);` > 3. **Determinación de Dirección (El Movimiento Inteligente):** > * La Serpiente (`self.snake[0]`) solo puede moverse un paso (`+1` o `-1` en `x` o `y`) por clic. La IA debe determinar la dirección más cercana (`'up'`, `'down'`, `'left'`, o `'right'`) **hacia la celda clicada**. > * **Prioridad:** Si la celda clicada está a la izquierda/derecha, la serpiente se mueve en `x`. Si está arriba/abajo, se mueve en `y`. Se debe priorizar el movimiento que acerque más el Gusano a la celda clicada, manteniendo la restricción de **no girar 180 grados**. > > > ```javascript > // Lógica simplificada dentro del handler del clic: > var currentHead = self.snake[0]; > var dx = targetGridX - currentHead.x; > var dy = targetGridY - currentHead.y; > ``` > // 1. Intentar movimiento horizontal (si la distancia X es mayor o igual a Y) > if (Math.abs(dx) \>= Math.abs(dy) && dx \!== 0) { > if (dx \> 0 && self.direction \!== 'left') { > self.direction = 'right'; > } else if (dx \< 0 && self.direction \!== 'right') { > self.direction = 'left'; > } > } > // 2. Intentar movimiento vertical (si el movimiento horizontal no fue válido o la distancia Y es mayor) > else if (dy \!== 0) { > if (dy \> 0 && self.direction \!== 'up') { > self.direction = 'down'; > } else if (dy \< 0 && self.direction \!== 'down') { > self.direction = 'up'; > } > } > // NOTA: La lógica de movimiento real se ejecuta en self.update() > > ``` > > 4. **Ejecución Única:** El clic solo establece la **dirección (`self.direction`)** y el movimiento en sí se ejecutará una sola vez en el próximo ciclo `self.update()` que alcance el `self.moveInterval`. Si el Gusano ya se movió en ese fotograma, el clic solo debe cambiar la dirección para el siguiente movimiento cronometrado. > ```
User prompt
mejor cambia a lo de antes de mver el gucsano con el mause
User prompt
🐍 Instrucción: Control de Movimiento con W, A, S, D Debemos reinstalar la lógica de control por teclado (W, A, S, D) y eliminar la lógica de clic del ratón. 1. Desactivación de Control por Clic Instrucción para la IA: Eliminar o Comentar la Lógica de Clic: En la clase SnakeGame, elimina o comenta todo el código relacionado con la detección de LK.on('click'), LK.on('mouseDown') o cualquier lógica que calcule la dirección de movimiento basándose en la posición del ratón. 2. Implementación de Controles W, A, S, D Instrucción para la IA: Implementar la Detección de keyDown para W, A, S, D: La Serpiente (self.direction) debe cambiar inmediatamente cuando se presiona la tecla, pero solo si el nuevo movimiento no es el opuesto al actual (restricción anti-180°). Utiliza la variable self.direction para actualizar la dirección de movimiento de la serpiente: Tecla Código de Tecla Nueva Dirección Restricción (Anti-180°) W 'w' 'up' Si self.direction NO es 'down'. S 's' 'down' Si self.direction NO es 'up'. A 'a' 'left' Si self.direction NO es 'right'. D 'd' 'right' Si self.direction NO es 'left'. Exportar a Hojas de cálculo Nota CRÍTICA: Confirma que el movimiento real (la actualización de las coordenadas x e y) de la serpiente sigue siendo controlado por el temporizador (self.moveTimer y self.moveInterval) dentro de self.update(). Las teclas solo deben establecer la nueva dirección.
User prompt
as que el gusano de el minijuego 4 se mueba con las flechas a s d w
User prompt
¡Absolutamente\! Entendido. Vamos a ajustar tres parámetros cruciales para que el Minijuego 4 (La Serpiente) sea más jugable y visible: **tamaño del Gusano**, **tamaño de las Manzanas/Esferas** y **velocidad del Gusano**. Aquí tienes la instrucción clara y directa para Upit IA: ----- ## 📐 Instrucción: Ajuste de Tamaño y Velocidad (Minijuego 4) La jugabilidad depende del tamaño de la celda (`gridSize`) y de la frecuencia de movimiento (`moveInterval`). ### 1\. Aumento del Tamaño del Gusano y la Comida Para hacer el Gusano y las Esferas de Comida más grandes, debemos aumentar el valor de **`self.gridSize`**. Este valor es el que dicta el tamaño visual de cada elemento en la grilla. **Instrucción para la IA:** > **Aumentar `self.gridSize` (Tamaño Visual):** > > 1. En la clase `SnakeGame`, cambia el valor de **`self.gridSize`** a un número significativamente mayor que el actual (ejemplo: si antes era 40, ajústalo a **`self.gridSize = 80;`**). > 2. **Verificación:** Confirma que tanto el asset **`gusano_minijuego4`** como el asset de la esfera de Comida se sigan escalando a este nuevo `self.gridSize` en la función de renderizado (`self.renderGame()`). > ```javascript > // Ejemplo de ajuste en la recreación del sprite: > sprite.width = self.gridSize; > sprite.height = self.gridSize; > ``` > 3. **Ajuste de Límites:** Si cambias `self.gridSize`, asegúrate de que **`self.gridWidth`** y **`self.gridHeight`** se recalcu len correctamente para que la grilla aún quepa dentro del asset `suelo`. ----- ### 2\. Disminución de la Velocidad del Gusano Para hacer el Gusano más lento, debemos aumentar el **intervalo de tiempo** entre cada paso. **Instrucción para la IA:** > **Aumentar `self.moveInterval` (Disminuir Velocidad):** > > 1. En la clase `SnakeGame`, incrementa el valor de **`self.moveInterval`** para que la serpiente se mueva con menos frecuencia. > 2. Si el valor actual era `8` (movimiento cada 8 fotogramas), ajústalo a un valor mayor, como **`self.moveInterval = 15;`**. > > *Explicación:* Un valor más alto significa que la variable `self.moveTimer` tardará más en alcanzar el límite, ralentizando la velocidad del juego.
User prompt
¡Absolutamente\! Entendido. Vamos a cambiar el método de control del Gusano del teclado (`W, A, S, D` o Flechas) a un **control por clic del ratón** (mouse click), con la restricción de **un movimiento por clic**. Aquí tienes la instrucción clara y específica para Upit IA: ----- ## 🖱️ Instrucción: Control del Gusano por Clic del Ratón Debemos **eliminar** la lógica de control por teclas y reemplazarla con la lógica de entrada por el ratón. ### 1\. Desactivación de Controles de Teclado (CRÍTICO) **Instrucción para la IA:** > **Eliminar la Lógica de `keyDown`:** > > 1. En la clase `SnakeGame`, elimina o comenta todo el código relacionado con la detección de `LK.on('keyDown')` o cualquier función que asigne direcciones de movimiento (`'up'`, `'down'`, etc.) a las teclas. > 2. El Gusano **solo** debe moverse en respuesta a un clic del ratón. ----- ### 2\. Implementación de Control por Clic La lógica debe tomar la posición en **píxeles** del clic, convertirla a una **coordenada de grilla** y determinar la dirección de movimiento de la Serpiente hacia esa celda. **Instrucción para la IA:** > **Implementar `LK.on('click')` o `LK.on('mouseDown')`:** > > 1. **Detección de Clic:** Usa el evento que detecta un clic izquierdo. > 2. **Conversión de Coordenadas:** Cuando ocurre un clic en las coordenadas de píxel `(clickX, clickY)`, usa la función auxiliar de la grilla para convertirlas a coordenadas de celda `(gridX, gridY)`. > * `targetGridX = Math.floor(clickX / self.gridSize);` > * `targetGridY = Math.floor(clickY / self.gridSize);` > 3. **Determinación de Dirección (El Movimiento Inteligente):** > * La Serpiente (`self.snake[0]`) solo puede moverse un paso (`+1` o `-1` en `x` o `y`) por clic. La IA debe determinar la dirección más cercana (`'up'`, `'down'`, `'left'`, o `'right'`) **hacia la celda clicada**. > * **Prioridad:** Si la celda clicada está a la izquierda/derecha, la serpiente se mueve en `x`. Si está arriba/abajo, se mueve en `y`. Se debe priorizar el movimiento que acerque más el Gusano a la celda clicada, manteniendo la restricción de **no girar 180 grados**. > > > ```javascript > // Lógica simplificada dentro del handler del clic: > var currentHead = self.snake[0]; > var dx = targetGridX - currentHead.x; > var dy = targetGridY - currentHead.y; > ``` > // 1. Intentar movimiento horizontal (si la distancia X es mayor o igual a Y) > if (Math.abs(dx) \>= Math.abs(dy) && dx \!== 0) { > if (dx \> 0 && self.direction \!== 'left') { > self.direction = 'right'; > } else if (dx \< 0 && self.direction \!== 'right') { > self.direction = 'left'; > } > } > // 2. Intentar movimiento vertical (si el movimiento horizontal no fue válido o la distancia Y es mayor) > else if (dy \!== 0) { > if (dy \> 0 && self.direction \!== 'up') { > self.direction = 'down'; > } else if (dy \< 0 && self.direction \!== 'down') { > self.direction = 'up'; > } > } > // NOTA: La lógica de movimiento real se ejecuta en self.update() > > ``` > > 4. **Ejecución Única:** El clic solo establece la **dirección (`self.direction`)** y el movimiento en sí se ejecutará una sola vez en el próximo ciclo `self.update()` que alcance el `self.moveInterval`. Si el Gusano ya se movió en ese fotograma, el clic solo debe cambiar la dirección para el siguiente movimiento cronometrado. > ```
User prompt
¡De acuerdo\! Si la IA no puede usar ni el `z-index` ni cambiar el orden de creación, la única forma de garantizar que el Gusano y la Comida sean visibles es forzar al código a **destruir y recrear** esos objetos en cada fotograma. Esto asegura que sean lo último que se dibuje, colocándolos automáticamente en la capa superior. Es una solución de emergencia robusta, aunque menos eficiente. Aquí está la instrucción final y definitiva para Upit IA: ----- ## 🔝 Solución Definitiva: Forzar Renderizado Superior (Destruir y Recrear) La IA debe implementar una función de renderizado que se llame en cada `update`. Esta función **eliminará** los sprites del Gusano y la Comida del fotograma anterior y los **volverá a dibujar** en sus nuevas posiciones, garantizando que siempre estén arriba de todo. ### 1\. Preparación de Variables Globales **Instrucción para la IA:** > **Asegurar la Limpieza de Sprites:** > > 1. Crea una lista en la clase `SnakeGame` para almacenar los objetos visuales del Gusano. > * `self.gusanoSprites = [];` > 2. Crea una variable para el objeto visual de la Comida. > * `self.foodSprite = null;` ----- ### 2\. Implementación de `self.renderGame()` (CRÍTICO) La IA debe mover la lógica de dibujo (creación de sprites) a esta nueva función y llamarla en cada ciclo. **Instrucción para la IA:** > **Añadir la Función de Renderizado Forzado `self.renderGame()`:** > > ```javascript > self.renderGame = function() { > // PASO 1: ELIMINAR LOS SPRITES ANTIGUOS (Destruir para limpiar la escena) > if (self.foodSprite) { > self.foodSprite.destroy(); > self.foodSprite = null; // Reiniciar > } > self.gusanoSprites.forEach(sprite => sprite.destroy()); > self.gusanoSprites = []; > ``` > ``` > // PASO 2: DIBUJAR LA COMIDA (Se dibuja antes del Gusano) > // Utilizar self.getPixelPosition(x, y) para convertir coordenadas de grilla a píxeles > var foodPosPix = self.getPixelPosition(self.foodPosition.x, self.foodPosition.y); > self.foodSprite = new LK.Sprite('nombre_de_la_esfera_de_comida'); // Usar la esfera de comida > self.foodSprite.x = foodPosPix.x; > self.foodSprite.y = foodPosPix.y; > self.foodSprite.width = self.gridSize; > self.foodSprite.height = self.gridSize; > ``` > ``` > // PASO 3: DIBUJAR EL GUSANO (Se dibuja último, queda en la capa superior) > self.snake.forEach(segment => { > var segmentPosPix = self.getPixelPosition(segment.x, segment.y); > var sprite = new LK.Sprite('gusano_minijuego4'); > sprite.x = segmentPosPix.x; > sprite.y = segmentPosPix.y; > sprite.width = self.gridSize; > sprite.height = self.gridSize; > self.gusanoSprites.push(sprite); > }); > ``` > > }; > > ``` > ``` ----- ### 3\. Llamada Final en `self.update()` **Instrucción para la IA:** > **Añadir la Llamada de Renderizado:** > > En el método **`self.update()`** del `SnakeGame`, la llamada a `self.renderGame()` debe ser el **último paso** de la lógica, después de la verificación de movimiento, colisión y fin del juego. Con esta solución, la visibilidad del Gusano y la Comida será garantizada en cada fotograma, superando la limitación del motor.
User prompt
arregla el gusano de el minijuego 4 para que se pueda ver y para controlarlo
User prompt
arregla que el gusano de el minijuego 4 no desaparesca
User prompt
solo cambia las capas de el gusano de el nivel 4 a arriva de la de el suelo de el minijuego 4
User prompt
🐛 Instrucción: Uso del Asset para el Cuerpo del Gusano Instrucción para la IA: Utilizar el Asset gusano_minijuego4 para el Cuerpo de la Serpiente: Reemplazo de Dibujo: En la función de renderizado (self.drawSnake() o dentro de self.update()) donde actualmente se dibuja cada segmento de la serpiente como una esfera de color o un punto genérico, reemplázalo con el asset gusano_minijuego4. Escala y Posición: Para cada segmento (self.snake[i]), crea o actualiza una instancia del objeto visual usando la imagen gusano_minijuego4. Asegura que el ancho y alto de este asset sean exactamente iguales al self.gridSize para que cada pieza encaje perfectamente en su celda de la grilla. Variación (Opcional pero Recomendado): Si tienes un asset separado para la cabeza del gusano (o para la cola), úsalos para mejorar la apariencia. Si no, usa gusano_minijuego4 para todos los segmentos. CRÍTICO: Asegúrate de que el asset gusano_minijuego4 se posicione correctamente en el centro de la celda de la grilla, usando la misma lógica de coordenadas de píxeles (x * gridSize + gridSize / 2, etc.) que hemos definido.
User prompt
¡Entendido\! La imagen muestra que el Minijuego 4 (La Serpiente) tiene problemas críticos con la **disposición de las paredes**, el **tamaño y posicionamiento de la serpiente/comida** y un **texto de la tienda** que no debería estar ahí. Aquí tienes las instrucciones **extremadamente específicas y detalladas** para Upit IA para solucionar estos problemas y hacer que el juego funcione como un Snake clásico. ----- ## 🛠️ Corrección y Configuración Detallada: Minijuego 4 (La Serpiente) ### 1\. Eliminación de Elementos Incorrectos (CRÍTICO) Lo primero es eliminar cualquier UI o texto que no pertenezca al Minijuego 4. **Instrucción para la IA:** > **Eliminar Texto "TIENDA DE MEJORAS" y "Esferas: 42":** > > 1. Cuando el `gameState` se establece en `'minigame4'` (o el estado que inicie la Serpiente), asegúrate de que **TODOS** los objetos de texto (`text`, `label`) relacionados con la "TIENDA DE MEJORAS" o el contador de esferas no estén activos (`alpha: 0`, `visible: false`, o `destroy()`). > 2. Este texto no debe aparecer bajo ninguna circunstancia en la pantalla de la serpiente. --- ### 2\. Rediseño del Escenario: `suelo` y `pared_de_inijuego_4` (CRÍTICO) La disposición actual de las paredes está incorrecta. Necesitamos un **contorno cerrado** y un **área de juego visible**. **Instrucción para la IA:** > **Reconfigurar la Disposición del `suelo` y `pared_de_inijuego_4`:** > > 1. **Asset `suelo`:** > * Posiciona el asset `suelo` para que sea el **fondo principal y el área jugable**. > * Asegúrate de que su **ancho y alto** (o su escala) coincidan con el área real donde la serpiente se va a mover (ej., 1800px de ancho por 2400px de alto, o ajusta a las dimensiones que permitan un buen juego con un `gridSize` adecuado). > * **Establece su `x` e `y`** para que esté **centrado** en la pantalla. > 2. **Assets `pared_de_inijuego_4` (CUATRO INSTANCIAS):** > * Crea **CUATRO objetos distintos** usando el asset `pared_de_inijuego_4`. > * **Rotación y Escala:** Ajusta su rotación y escala para que cada una forme un lado del contorno alrededor del asset `suelo`. > * **Dos Horizontales:** Para la parte superior e inferior. > * **Dos Verticales:** Rótadas 90 grados (`rotation: Math.PI / 2` o `rotation: 90` si usa grados) para la izquierda y la derecha. > * **Posicionamiento Exacto:** Colócalas de forma que el borde exterior del `suelo` coincida perfectamente con el borde interior de las paredes, formando un cuadrado o rectángulo cerrado. > * **Ejemplo de Posición (Ajustar según dimensiones de `suelo` y pared):** > * `paredSuperior.x = suelo.x; paredSuperior.y = suelo.y - (paredHeight / 2) - (suelo.height / 2);` > * `paredInferior.x = suelo.x; paredInferior.y = suelo.y + (paredHeight / 2) + (suelo.height / 2);` > * `paredIzquierda.x = suelo.x - (paredWidth / 2) - (suelo.width / 2); paredIzquierda.y = suelo.y;` (con rotación) > * `paredDerecha.x = suelo.x + (paredWidth / 2) + (suelo.width / 2); paredDerecha.y = suelo.y;` (con rotación) > > * **Grosor de las paredes:** El `gridSize` (tamaño de celda) de la serpiente debe ser lo suficientemente grande para que la serpiente sea visible dentro de las paredes. --- ### 3\. Escala y Visibilidad de la Serpiente y la Comida (CRÍTICO) Actualmente, la serpiente (el punto rojo) es minúscula. La comida también debe ser visible. **Instrucción para la IA:** > **Ajustar el `gridSize` y el Dibujo de la Serpiente/Comida:** > > 1. **`self.gridSize`:** Aumenta drásticamente `self.gridSize` (ej., **`self.gridSize = 60;`** o **`80;`**). Este es el tamaño de cada "celda" en el juego y, por lo tanto, el tamaño de cada segmento de la serpiente y la comida. > 2. **Dibujo de la Serpiente:** > * Cuando se dibuja cada segmento de `self.snake` (que son objetos `{x: celdaX, y: celdaY}`), el segmento debe ser dibujado en la posición de píxeles: > * `pixelX = self.snake[i].x * self.gridSize + (self.gridSize / 2);` > * `pixelY = self.snake[i].y * self.gridSize + (self.gridSize / 2);` > * Utiliza un asset de esfera (ej., `bubble_red` o el que sea para el cuerpo) y escala su tamaño (`width`, `height`) al `self.gridSize`. > 3. **Dibujo de la Comida:** > * La esfera de comida (`self.foodPosition`) debe ser dibujada de la misma manera, usando su `x` e `y` de grilla multiplicados por `self.gridSize` para obtener la posición en píxeles. > * Escala el asset de la esfera de comida (ej., `goldenMemory`) al `self.gridSize`. --- ### 4\. Ajuste de Límites de la Grilla Los `gridWidth` y `gridHeight` deben corresponder al número de celdas que caben dentro del `suelo`. **Instrucción para la IA:** > **Sincronizar `gridWidth` y `gridHeight` con el `suelo`:** > > 1. Calcula `self.gridWidth` y `self.gridHeight` dividiendo el ancho y alto en píxeles del asset `suelo` por `self.gridSize`. > * `self.gridWidth = Math.floor(suelo.width / self.gridSize);` > * `self.gridHeight = Math.floor(suelo.height / self.gridSize);` > 2. Ajusta el `x` e `y` iniciales de la serpiente (`self.snake[0].x`, `self.snake[0].y`) para que comience dentro de estos nuevos límites. Con estas instrucciones detalladas, la IA debería poder configurar correctamente el Minijuego 4 para que se parezca y funcione como un juego de la Serpiente.
User prompt
🏗️ Configuración de Escenario: Minijuego 4 (La Serpiente) El escenario debe estar formado por tu asset suelo y enmarcado por las cuatro instancias de pared_de_inijuego_4. 1. El Suelo (Área de Movimiento) El asset suelo define el área jugable. Instrucción para la IA: Configuración del Fondo (suelo): Al iniciar la clase SnakeGame, crea una instancia del asset suelo y utilízala como fondo principal. Ajusta su tamaño/escala para que llene la mayor parte de la pantalla, dejando espacio suficiente en los bordes para el grosor de las paredes. Define los límites de la Grilla: Los límites de self.gridWidth y self.gridHeight de la grilla de celdas deben coincidir con las dimensiones internas del asset suelo. 2. Las Paredes (Contorno y Colisión) El asset pared_de_inijuego_4 debe formar un marco cuadrado o rectangular alrededor del área del suelo. Instrucción para la IA: Crear y Posicionar los 4 Límites (pared_de_inijuego_4): Crea cuatro instancias del asset pared_de_inijuego_4 para formar un contorno cerrado. Ajusta la rotación y el tamaño (escala): Dos deben ser largas y horizontales (superior e inferior), y dos deben ser rotadas 90 grados y ajustadas en altura (izquierdo y derecho). Colisión con Paredes (Lógica): La condición de Game Over debe seguir basándose en que la cabeza de la serpiente salga de los límites de la grilla interna (los límites del asset suelo). No es necesario programar una colisión física compleja, solo verificar que: JavaScript if (newHead.x < 0 || newHead.x >= self.gridWidth || newHead.y < 0 || newHead.y >= self.gridHeight) { // GAME OVER: Colisión con pared self.gameOver(); } 🍎 Lógica Central: Consumo y Crecimiento (Como el Juego Original) Esto finaliza la programación del ciclo de juego, enfocándose en cómo la serpiente interactúa con la comida. 3. Colisión y Crecimiento Instrucción para la IA: Modificar la Lógica de Consumo en self.update() (o self.moveSnake()): La lógica ya creada para el movimiento simple y el consumo debe ser precisa. La clave es el método self.snake.pop(). Chequeo de Colisión: Verifica si la nueva posición de la cabeza (newHead) coincide con la posición de la esfera de comida (self.foodPosition). JavaScript if (newHead.x === self.foodPosition.x && newHead.y === self.foodPosition.y) { // Se consume la esfera. self.score++; // **NO** se ejecuta self.snake.pop(), la serpiente crece. // Elimina la esfera visualmente (si existe) y... // ... genera una nueva esfera de comida: self.foodPosition = self.placeFood(); } else { // Si no come, simplemente mueve el cuerpo (elimina la cola). self.snake.pop(); } Eliminación Visual de la Esfera: La esfera de comida que la serpiente acaba de "comer" debe ser eliminada del escenario antes de que se dibuje la nueva comida en otra parte. Si la esfera es un objeto LK/Upit, usa su método destroy() (o establece alpha: 0) antes de llamar a self.placeFood(). 4. Función self.placeFood() Instrucción para la IA: Reforzar la Colocación de la Comida: La función self.placeFood() debe generar coordenadas (x, y) totalmente al azar dentro de los límites de la grilla y debe asegurarse de que la posición esté VACÍA. Verificación Requerida: La posición elegida al azar para la nueva comida NO puede estar ocupada por ningún segmento del cuerpo de self.snake. Si lo está, la función debe intentar colocar la comida en otra celda aleatoria.
User prompt
🐍 Implementación: Minijuego 4 - La Serpiente de Esferas 1. Inicialización y Estructura de la Grilla El juego debe basarse en una grilla discreta (celdas) para manejar el movimiento. Instrucción para la IA: Crear la Clase SnakeGame Definición de Celdas: Establece el tamaño de la grilla y de la celda. Todas las posiciones de la serpiente y la comida deben ser coordenadas de grilla (números enteros), no coordenadas de píxeles. self.gridSize = 40; (Píxeles por celda, ajusta según el tamaño de pantalla). self.gridWidth = 50; (Ejemplo: 50 celdas horizontales). self.gridHeight = 35; (Ejemplo: 35 celdas verticales). Estado Inicial: self.snake = [{x: 10, y: 10}]; (La serpiente es una lista de objetos {x, y} que representan su cuerpo). self.direction = 'right'; (Dirección inicial de movimiento). self.moveTimer = 0; (Contador para controlar la velocidad). self.moveInterval = 8; (La serpiente se mueve cada 8 fotogramas). Comida (Esfera): Llama a una función self.placeFood() al inicio. self.foodPosition = self.placeFood(); (Debe ser una posición aleatoria vacía dentro de los límites de la grilla). Assets: Usa tus esferas existentes (bubble_color o similar) para dibujar tanto la comida como los segmentos de la serpiente, escalando su tamaño a self.gridSize. 2. Manejo de Controles (W, A, S, D) El control debe ser instantáneo y debe prohibir el movimiento en la dirección opuesta para evitar auto-colisiones inmediatas. Instrucción para la IA: Implementar LK.on('keyDown') o la lógica de entrada: La nueva dirección solo debe registrarse si no es directamente opuesta a la dirección actual (self.direction). JavaScript LK.on('keyDown', function(event) { var key = event.key.toLowerCase(); if (key === 'w' && self.direction !== 'down') { self.nextDirection = 'up'; } else if (key === 's' && self.direction !== 'up') { self.nextDirection = 'down'; } else if (key === 'a' && self.direction !== 'right') { self.nextDirection = 'left'; } else if (key === 'd' && self.direction !== 'left') { self.nextDirection = 'right'; } // Opcional: Actualizar self.direction al final de update() para evitar movimientos en un mismo fotograma. }); 3. Lógica del Juego en el Bucle update Este bucle es el corazón del juego y controla el movimiento, la velocidad, el consumo y el fin del juego. Instrucción para la IA: Implementar la Lógica de Movimiento en self.update(): Control de Velocidad: JavaScript self.moveTimer++; if (self.moveTimer < self.moveInterval) { return; // Esperar al siguiente intervalo de movimiento } self.moveTimer = 0; // Si se usa self.nextDirection: self.direction = self.nextDirection; Calcular Nueva Cabeza (newHead): Determinar la nueva posición x e y de la cabeza basándose en self.direction. Chequear Fin de Juego: Llama a self.checkGameOver(newHead). Si devuelve true, detén el juego. Movimiento y Consumo: Añadir newHead al frente de self.snake: self.snake.unshift(newHead); CONDICIÓN DE CRECIMIENTO: Si newHead.x === self.foodPosition.x y newHead.y === self.foodPosition.y: Aumenta la puntuación. Llama a self.placeFood() para generar una nueva esfera. NO se elimina el último segmento (la serpiente crece). CONDICIÓN DE MOVIMIENTO SIMPLE: Si no se consume comida: Eliminar el último segmento: self.snake.pop(); 4. Condiciones de Derrota (Game Over) Instrucción para la IA: Implementar la Función self.checkGameOver(headPosition): El juego debe terminar (gameState = 'gameOver') si se cumple alguna de estas condiciones: Colisión con el Borde: headPosition está fuera de self.gridWidth o self.gridHeight. Auto-Colisión: headPosition choca con cualquier segmento existente en self.snake (se puede iterar el cuerpo, ignorando el primer segmento, que es la cabeza). Si se detecta el fin del juego, la IA debe mostrar la pantalla de Game Over.
User prompt
📐 Instrucción: Fórmula Definitiva para Grilla Hexagonal y Centrado El problema de los huecos y el centrado incorrecto es un error matemático en la función que calcula las coordenadas. La IA debe reemplazar el código de self.getHexPosition() con la fórmula estándar y precisa para la cuadrícula hexagonal compensada (offset grid). 1. Variables Requeridas y Precisión La IA debe establecer estas variables de forma estricta dentro de la clase BubbleShooter para que la fórmula funcione correctamente en la pantalla de 2048px: Variable Valor Sugerido (Ejemplo) Propósito self.bubbleRadius 64 La mitad del diámetro. self.gridCols 16 Número de columnas para cubrir 2048px. Exportar a Hojas de cálculo 2. Instrucción Clave: Reemplazo de self.getHexPosition() Instrucción para la IA: Reemplaza COMPLETAMENTE el contenido de la función self.getHexPosition(row, col) por el siguiente código. Esta fórmula garantiza el espaciado compacto y centrado, eliminando los huecos. JavaScript self.getHexPosition = function (row, col) { var radius = self.bubbleRadius; var diameter = radius * 2; var rowHeight = radius * 1.732; // Factor de compresión vertical (sqrt(3)) // 1. CÁLCULO DE CENTRADO (Para colocar la grilla completa en el centro de 2048px) // Se calcula el ancho total teórico de la grilla más el radio final. var totalGridWidth = self.gridCols * diameter + radius; var centerOffset = (2048 - totalGridWidth) / 2; // 2. DESPLAZAMIENTO HEXAGONAL (Mueve las filas impares medio diámetro) var offsetX = (row % 2) * radius; // POSICIÓN X: (Offset de Centrado) + (Posición por columna) + (Offset hexagonal) var x = centerOffset + (col * diameter) + offsetX; // POSICIÓN Y: (Inicio del Nivel) + (Posición por fila) var y = self.startY + row * rowHeight; return { x: x, y: y }; }; Verificación Final: Una vez aplicada esta corrección de espaciado, la IA debe confirmar que self.createInitialBubbles() no tenga ninguna lógica de omisión de columnas, sino que solo genere burbujas basándose en la probabilidad aleatoria (Math.random()).
User prompt
¡Absolutamente! Aquí tienes la instrucción final y definitiva para Upit IA, con la fórmula matemática precisa que corregirá el espaciado y centrado de la grilla hexagonal. Este es un problema de precisión, por lo que el código debe ser reemplazado. 📐 Corrección Crítica: Centrado y Espaciado Hexagonal de la Grilla (Nivel 3) El problema de los huecos (espacios vacíos) se debe a un error en el cálculo de la posición horizontal (x) en la función que genera las coordenadas de las burbujas. La siguiente fórmula es el estándar para grillas hexagonales centradas y debe resolver los espacios. 1. Requisitos Previos de las Variables Asegúrate de que estas variables estén definidas correctamente en BubbleShooter para que la fórmula funcione en la pantalla de 2048px: Variable Valor Sugerido (Ejemplo) self.bubbleRadius 64 (Asumiendo que el diámetro es 128) self.gridCols 16 (Para cubrir los 2048px) Exportar a Hojas de cálculo 2. Instrucción Clave: Reemplazo de self.getHexPosition() Instrucción para la IA: Reemplaza COMPLETAMENTE el contenido de la función self.getHexPosition(row, col) por la siguiente fórmula precisa. Esto eliminará los huecos al calcular la posición exacta y compacta de cada burbuja.
User prompt
¡Entendido! Si después de aumentar el número de burbujas para cubrir el ancho de la pantalla, ahora ves huecos consistentes entre grupos de burbujas, el problema es que la fórmula de espaciado hexagonal (self.getHexPosition) no está funcionando correctamente con el nuevo radio y/o el offset horizontal. Aquí tienes la instrucción para que Upit IA solucione los espacios vacíos y asegure una grilla compacta: 🛠️ Corrección: Eliminación de Espacios Vacíos en la Generación de la Grilla El problema de los espacios se debe a un cálculo incorrecto del espaciado horizontal o del offsetX entre burbujas. 1. Instrucción Clave: Ajuste de Coordenada X Hexagonal La IA debe enfocarse en la función self.getHexPosition(row, col), que es la que dicta dónde se coloca cada burbuja. Modificar self.getHexPosition() para un Espaciado Compacto y Centrado: La fórmula para la coordenada x de una grilla hexagonal compensada (offset grid) debe ser muy precisa. Se deben usar los siguientes componentes: columnWidth: El ancho de la burbuja que determina el movimiento por columna. initialCenterOffset: Un valor constante para centrar la grilla completa en la pantalla (2048px). offsetX: La mitad del ancho de la burbuja, aplicado solo a las filas impares. Instrucción de Fórmula Específica: JavaScript // Ajusta self.getHexPosition en BubbleShooter self.getHexPosition = function (row, col) { var columnWidth = self.bubbleRadius * 2; // El ancho de una burbuja (Diámetro) var halfWidth = self.bubbleRadius; // 1. Calcular el desplazamiento horizontal para filas impares var offsetX = row % 2 * halfWidth; // 2. Calcular el X, incluyendo el offset inicial para centrar la grilla completa. // (El '1024' es la mitad de 2048, ajusta el 'shift' para centrar mejor.) var initialCenterShift = 1024 - (self.gridCols * halfWidth); var x = initialCenterShift + halfWidth + col * columnWidth + offsetX; // 3. La Y se mantiene igual: var y = self.startY + row * (self.bubbleRadius * 1.7); return { x: x, y: y }; }; 2. Verificar Parámetros de la Grilla Para que el espaciado funcione, la IA debe asegurarse de que estas variables estén definidas correctamente para llenar el ancho de 2048px: self.bubbleRadius: Debe ser un valor pequeño y consistente (ej., 64px o 65px). self.gridCols: Debe ser un número grande (ej., 16) para que, junto con el radio pequeño, el ancho se extienda a 2048px. Si el bubbleRadius es demasiado grande para el gridCols establecido, siempre habrá huecos. La IA debe verificar que: (bubbleRadius×2)×gridCols≈2048 p ı ˊ xeles Con esta corrección en self.getHexPosition, la grilla hexagonal debe calcular las posiciones con precisión, eliminando los espacios vacíos y asegurando una cobertura total en el techo.
User prompt
agrega que no se pueda ganar en ningun minijuego , si es nesesario elimina parte de el codigo para aser que no se pueda ganar
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Bubble = Container.expand(function (color) {
var self = Container.call(this);
// Color mapping - only 5 colors available, plus rainbow
var colorAssets = {
0: 'bubble_red',
1: 'bubble_blue',
2: 'bubble_green',
3: 'bubble_yellow',
4: 'bubble_purple',
5: 'bubble_red' // Start rainbow with red texture
};
self.colorType = color;
var bubbleGraphics = self.attachAsset(colorAssets[color], {
anchorX: 0.5,
anchorY: 0.5
});
// Grid position
self.gridRow = -1;
self.gridCol = -1;
self.isConnectedToTop = false;
self.visited = false;
self.isRainbow = color === 5;
// Movement properties for shooting
self.velocityX = 0;
self.velocityY = 0;
// Rainbow texture cycling
self.rainbowTimer = 0;
self.currentTextureIndex = 0;
self.rainbowTextures = ['bubble_red', 'bubble_yellow', 'bubble_green', 'bubble_blue', 'bubble_purple'];
self.update = function () {
if (self.isRainbow) {
self.rainbowTimer += 0.1;
// Change texture every 0.5 seconds (30 ticks at 60fps)
if (LK.ticks % 30 === 0) {
self.currentTextureIndex = (self.currentTextureIndex + 1) % self.rainbowTextures.length;
// Remove current texture and add new one
self.removeChild(bubbleGraphics);
bubbleGraphics = self.attachAsset(self.rainbowTextures[self.currentTextureIndex], {
anchorX: 0.5,
anchorY: 0.5
});
}
}
};
return self;
});
var BubbleShooter = Container.expand(function () {
var self = Container.call(this);
// Game grid - hexagonal arrangement
self.bubbleGrid = [];
self.gridRows = 12;
self.gridCols = 16; // Increase columns to cover full 2048px width
self.bubbleRadius = 64; // Adjust radius for proper spacing across 2048px
self.startY = 20; // Start bubbles higher up
// Shooting mechanics
self.cannon = null;
self.currentBubble = null;
self.nextBubble = null;
self.aimLine = [];
self.shootingBubble = null;
self.canShoot = true; // Flag to control when shooting is allowed
// Pressure system
self.missedShots = 0;
self.maxMissedShots = 5;
// Pressure timer system for automatic grid descent
self.pressureTimer = 0;
self.pressureInterval = 300; // Every 5 seconds at 60 FPS (adjustable)
// Initialize grid
self.initGrid = function () {
self.bubbleGrid = []; // Reset grid completely
for (var row = 0; row < self.gridRows; row++) {
self.bubbleGrid[row] = [];
for (var col = 0; col < self.gridCols; col++) {
self.bubbleGrid[row][col] = null;
}
}
// Create danger line
self.dangerLine = self.attachAsset('danger_line', {
anchorX: 0.5,
anchorY: 0.5
});
self.dangerLine.x = 1024;
self.dangerLine.y = 1800; // Position lower than middle
// Create invisible bottom boundary line for bubble expansion
self.bottomBoundary = LK.getAsset('danger_line', {
anchorX: 0.5,
anchorY: 0.5
});
self.bottomBoundary.x = 1024;
self.bottomBoundary.y = 2600; // Near bottom of screen
self.bottomBoundary.alpha = 0; // Make it invisible
self.addChild(self.bottomBoundary);
};
// Get hexagonal position - support infinite rows
self.getHexPosition = function (row, col) {
var radius = self.bubbleRadius;
var diameter = radius * 2;
var rowHeight = radius * 1.732; // Factor de compresión vertical (sqrt(3))
// 1. CÁLCULO DE CENTRADO (Para colocar la grilla completa en el centro de 2048px)
// Se calcula el ancho total teórico de la grilla más el radio final.
var totalGridWidth = self.gridCols * diameter + radius;
var centerOffset = (2048 - totalGridWidth) / 2;
// 2. DESPLAZAMIENTO HEXAGONAL (Mueve las filas impares medio diámetro)
var offsetX = row % 2 * radius;
// POSICIÓN X: (Offset de Centrado) + (Posición por columna) + (Offset hexagonal)
var x = centerOffset + col * diameter + offsetX;
// POSICIÓN Y: (Inicio del Nivel) + (Posición por fila)
var y = self.startY + row * rowHeight;
return {
x: x,
y: y
};
};
// Create initial bubble layout
self.createInitialBubbles = function () {
// Only create bubbles in first 6 rows to leave space for gameplay
var initialRows = Math.min(6, self.gridRows);
for (var row = 0; row < initialRows; row++) {
// The hexagonal grid has N and N-1 columns pattern (16, 15, 16, 15...)
var colsInRow = self.gridCols - row % 2; // 16 or 15 columns
for (var col = 0; col < colsInRow; col++) {
if (Math.random() < 0.8) {
// 80% probability for balanced coverage
// Only normal colors for ceiling bubbles - no rainbow bubbles
var color = Math.floor(Math.random() * 5); // 5 normal colors available
var bubble = new Bubble(color);
var pos = self.getHexPosition(row, col);
bubble.x = pos.x;
bubble.y = pos.y;
bubble.gridRow = row;
bubble.gridCol = col;
self.bubbleGrid[row][col] = bubble;
self.addChild(bubble);
}
}
}
};
// Get neighbors in hexagonal grid
self.getNeighbors = function (row, col) {
var neighbors = [];
var isOddRow = row % 2 === 1;
// Define neighbor offsets for hex grid
var offsets = isOddRow ? [[-1, 0], [-1, 1], [0, -1], [0, 1], [1, 0], [1, 1]] : [[-1, -1], [-1, 0], [0, -1], [0, 1], [1, -1], [1, 0]];
for (var i = 0; i < offsets.length; i++) {
var newRow = row + offsets[i][0];
var newCol = col + offsets[i][1];
if (newRow >= 0 && newRow < self.bubbleGrid.length && newCol >= 0 && newCol < self.gridCols) {
// Ensure the row exists before checking
if (!self.bubbleGrid[newRow]) {
self.bubbleGrid[newRow] = new Array(self.gridCols).fill(null);
}
neighbors.push({
row: newRow,
col: newCol
});
}
}
return neighbors;
};
// Find connected bubbles of same color
self.findConnectedBubbles = function (startRow, startCol, color) {
var connected = [];
var visited = [];
// Initialize visited array to match current grid size
for (var r = 0; r < self.bubbleGrid.length; r++) {
visited[r] = [];
for (var c = 0; c < self.gridCols; c++) {
visited[r][c] = false;
}
}
// Depth-first search
function dfs(row, col) {
if (row < 0 || row >= self.bubbleGrid.length || col < 0 || col >= self.gridCols || !visited[row] || visited[row][col]) {
return;
}
// Check if row exists and has bubbles
if (!self.bubbleGrid[row] || !self.bubbleGrid[row][col] || self.bubbleGrid[row][col].colorType !== color) {
return;
}
visited[row][col] = true;
connected.push({
row: row,
col: col
});
var neighbors = self.getNeighbors(row, col);
for (var i = 0; i < neighbors.length; i++) {
dfs(neighbors[i].row, neighbors[i].col);
}
}
dfs(startRow, startCol);
return connected;
};
// Check which bubbles are connected to top
self.markConnectedToTop = function () {
// Reset all connection flags
for (var row = 0; row < self.bubbleGrid.length; row++) {
if (self.bubbleGrid[row]) {
for (var col = 0; col < self.gridCols; col++) {
if (self.bubbleGrid[row][col]) {
self.bubbleGrid[row][col].isConnectedToTop = false;
self.bubbleGrid[row][col].visited = false;
}
}
}
}
// Mark bubbles connected to top row
function markConnected(row, col) {
if (row < 0 || row >= self.bubbleGrid.length || col < 0 || col >= self.gridCols) {
return;
}
// Ensure row exists
if (!self.bubbleGrid[row]) {
self.bubbleGrid[row] = new Array(self.gridCols).fill(null);
}
if (!self.bubbleGrid[row][col] || self.bubbleGrid[row][col].visited) {
return;
}
self.bubbleGrid[row][col].isConnectedToTop = true;
self.bubbleGrid[row][col].visited = true;
var neighbors = self.getNeighbors(row, col);
for (var i = 0; i < neighbors.length; i++) {
markConnected(neighbors[i].row, neighbors[i].col);
}
}
// Start from top row
for (var col = 0; col < self.gridCols; col++) {
if (self.bubbleGrid[0][col]) {
markConnected(0, col);
}
}
};
// Remove floating bubbles
self.removeFloatingBubbles = function () {
var removed = [];
for (var row = 0; row < self.bubbleGrid.length; row++) {
if (!self.bubbleGrid[row]) continue;
for (var col = 0; col < self.gridCols; col++) {
var bubble = self.bubbleGrid[row][col];
if (bubble && !bubble.isConnectedToTop) {
removed.push(bubble);
self.bubbleGrid[row][col] = null;
// Animate falling
tween(bubble, {
y: 2800,
alpha: 0
}, {
duration: 1000,
onFinish: function onFinish(bubbleRef) {
return function () {
if (bubbleRef && bubbleRef.destroy) {
bubbleRef.destroy();
}
};
}(bubble)
});
}
}
}
return removed.length;
};
// Process bubble elimination
self.processBubbleElimination = function (row, col) {
var bubble = self.bubbleGrid[row][col];
if (!bubble) return;
// Special rainbow bubble behavior
if (bubble.isRainbow) {
// Rainbow bubble makes ALL touching bubbles fall (regardless of color)
var neighbors = self.getNeighbors(row, col);
var bubblesAffected = [];
// Add the rainbow bubble itself
bubblesAffected.push({
row: row,
col: col
});
// Add all neighboring bubbles
for (var n = 0; n < neighbors.length; n++) {
var neighborRow = neighbors[n].row;
var neighborCol = neighbors[n].col;
if (self.bubbleGrid[neighborRow] && self.bubbleGrid[neighborRow][neighborCol]) {
bubblesAffected.push({
row: neighborRow,
col: neighborCol
});
}
}
// Remove all affected bubbles
for (var i = 0; i < bubblesAffected.length; i++) {
var pos = bubblesAffected[i];
var bubbleToRemove = self.bubbleGrid[pos.row][pos.col];
if (bubbleToRemove) {
self.bubbleGrid[pos.row][pos.col] = null;
LK.effects.flashObject(bubbleToRemove, 0xffffff, 300);
bubbleToRemove.destroy();
}
}
// Update score for rainbow elimination
LK.setScore(LK.getScore() + bubblesAffected.length * 20);
LK.getSound('collect').play();
// Check for floating bubbles
self.markConnectedToTop();
var floatingCount = self.removeFloatingBubbles();
if (floatingCount > 0) {
LK.setScore(LK.getScore() + floatingCount * 5);
}
self.missedShots = 0;
return true;
}
var connected = self.findConnectedBubbles(row, col, bubble.colorType);
if (connected.length >= 3) {
// Remove connected bubbles
for (var i = 0; i < connected.length; i++) {
var pos = connected[i];
var bubbleToRemove = self.bubbleGrid[pos.row][pos.col];
if (bubbleToRemove) {
self.bubbleGrid[pos.row][pos.col] = null;
LK.effects.flashObject(bubbleToRemove, 0xffffff, 300);
bubbleToRemove.destroy();
}
}
// Update score
LK.setScore(LK.getScore() + connected.length * 10);
LK.getSound('collect').play();
// Check for floating bubbles
self.markConnectedToTop();
var floatingCount = self.removeFloatingBubbles();
if (floatingCount > 0) {
LK.setScore(LK.getScore() + floatingCount * 5);
}
self.missedShots = 0; // Reset pressure counter on successful match
return true;
}
return false;
};
// Move grid down (pressure system) - allow infinite expansion
self.moveGridDown = function () {
// Expand grid if needed
var maxUsedRow = -1;
for (var row = 0; row < self.bubbleGrid.length; row++) {
for (var col = 0; col < self.gridCols; col++) {
if (self.bubbleGrid[row] && self.bubbleGrid[row][col]) {
maxUsedRow = Math.max(maxUsedRow, row);
}
}
}
// Add more rows if bubbles are near the bottom
if (maxUsedRow >= self.bubbleGrid.length - 3) {
for (var i = 0; i < 5; i++) {
// Add 5 more rows
self.bubbleGrid.push(new Array(self.gridCols).fill(null));
}
}
// Move all bubbles down one row
for (var row = self.bubbleGrid.length - 1; row >= 0; row--) {
for (var col = 0; col < self.gridCols; col++) {
if (self.bubbleGrid[row] && self.bubbleGrid[row][col] && row < self.bubbleGrid.length - 1) {
// Ensure next row exists
if (!self.bubbleGrid[row + 1]) {
self.bubbleGrid[row + 1] = new Array(self.gridCols).fill(null);
}
// Move bubble to next row
var bubble = self.bubbleGrid[row][col];
self.bubbleGrid[row + 1][col] = bubble;
self.bubbleGrid[row][col] = null;
bubble.gridRow = row + 1;
var newPos = self.getHexPosition(row + 1, col);
tween(bubble, {
x: newPos.x,
y: newPos.y
}, {
duration: 300
});
}
}
}
};
// Create new shooting bubble
self.createNewBubble = function () {
if (!self.currentBubble) {
var color;
if (Math.random() < 0.1) {
// 10% chance for rainbow bubble
color = 5;
} else {
// 90% chance for normal colors
color = Math.floor(Math.random() * 5);
}
self.currentBubble = new Bubble(color);
// Position bubble properly within BubbleShooter container
self.currentBubble.x = self.cannon ? self.cannon.x : 1024;
self.currentBubble.y = self.cannon ? self.cannon.y - 60 : 2500;
self.addChild(self.currentBubble);
}
if (!self.nextBubble) {
var nextColor;
if (Math.random() < 0.1) {
// 10% chance for rainbow bubble
nextColor = 5;
} else {
// 90% chance for normal colors
nextColor = Math.floor(Math.random() * 5);
}
self.nextBubble = new Bubble(nextColor);
// Position next bubble properly within BubbleShooter container
self.nextBubble.x = self.cannon ? self.cannon.x + 100 : 1124;
self.nextBubble.y = self.cannon ? self.cannon.y - 60 : 2500;
self.nextBubble.alpha = 0.7;
self.addChild(self.nextBubble);
}
};
// Check win condition - always return false to prevent winning
self.checkWin = function () {
return false;
};
// Add new row from top
self.addNewRowFromTop = function () {
// Shift all existing bubbles down one row
for (var row = self.gridRows - 1; row > 0; row--) {
for (var col = 0; col < self.gridCols; col++) {
if (self.bubbleGrid[row - 1][col]) {
var bubble = self.bubbleGrid[row - 1][col];
self.bubbleGrid[row][col] = bubble;
self.bubbleGrid[row - 1][col] = null;
bubble.gridRow = row;
var newPos = self.getHexPosition(row, col);
tween(bubble, {
x: newPos.x,
y: newPos.y
}, {
duration: 300
});
}
}
}
// Add new bubbles to top row
for (var col = 0; col < self.gridCols; col++) {
if (Math.random() < 0.85) {
// 85% chance to place bubble for fuller coverage
// Only normal colors for ceiling bubbles - no rainbow bubbles
var color = Math.floor(Math.random() * 5); // 5 colors available
var bubble = new Bubble(color);
var pos = self.getHexPosition(0, col);
bubble.x = pos.x;
bubble.y = pos.y - 100; // Start above screen
bubble.gridRow = 0;
bubble.gridCol = col;
self.bubbleGrid[0][col] = bubble;
self.addChild(bubble);
// Animate drop into position
tween(bubble, {
y: pos.y
}, {
duration: 500,
easing: tween.bounceOut
});
}
}
};
// Shoot bubble with physics
self.shootBubble = function (angle) {
if (self.currentBubble && self.canShoot) {
var bullet = self.currentBubble;
// Use shootPower from global scope (defined as 15)
var power = 15;
// Calculate velocity components
bullet.velocityX = Math.cos(angle) * power;
bullet.velocityY = Math.sin(angle) * power;
// Set bullet to cannon position - ensure proper positioning within container
bullet.x = self.cannon ? self.cannon.x : 1024;
bullet.y = self.cannon ? self.cannon.y : 2600;
// Make it the shooting bubble
self.shootingBubble = bullet;
// Disable shooting until collision
self.canShoot = false;
// Prepare next bubble
self.currentBubble = self.nextBubble;
if (self.currentBubble) {
self.currentBubble.x = self.cannon ? self.cannon.x : 1024;
self.currentBubble.y = self.cannon ? self.cannon.y - 60 : 2500;
self.currentBubble.alpha = 1;
}
var nextColor;
if (Math.random() < 0.1) {
// 10% chance for rainbow bubble
nextColor = 5;
} else {
// 90% chance for normal colors
nextColor = Math.floor(Math.random() * 5);
}
self.nextBubble = new Bubble(nextColor);
self.nextBubble.x = self.cannon ? self.cannon.x + 100 : 1124;
self.nextBubble.y = self.cannon ? self.cannon.y - 60 : 2500;
self.nextBubble.alpha = 0.7;
self.addChild(self.nextBubble);
}
};
// Check collision between shooting bubble and grid - optimized collision detection
self.checkCollision = function (shootingBubble) {
// Only check rows that the shooting bubble could actually hit
var minRow = Math.max(0, Math.floor((shootingBubble.y - self.bubbleRadius * 2) / (self.bubbleRadius * 1.7)));
var maxRow = Math.min(self.bubbleGrid.length - 1, Math.floor((shootingBubble.y + self.bubbleRadius * 2) / (self.bubbleRadius * 1.7)));
for (var row = minRow; row <= maxRow; row++) {
if (self.bubbleGrid[row]) {
for (var col = 0; col < self.gridCols; col++) {
var gridBubble = self.bubbleGrid[row][col];
if (gridBubble) {
var dx = shootingBubble.x - gridBubble.x;
var dy = shootingBubble.y - gridBubble.y;
var distanceSquared = dx * dx + dy * dy;
// Use squared distance to avoid expensive sqrt calculation
// 85^2 = 7225 for better collision detection
if (distanceSquared <= 7225) {
return {
row: row,
col: col,
bubble: gridBubble
};
}
}
}
}
}
return null;
};
// Find closest grid position for bubble attachment - optimized and simplified
self.findClosestGridPosition = function (x, y, collisionBubble) {
var minDist = 999999;
var targetRow = -1;
var targetCol = -1;
// If we have a collision bubble, only check its immediate neighbors
if (collisionBubble) {
var neighbors = self.getNeighbors(collisionBubble.row, collisionBubble.col);
for (var n = 0; n < neighbors.length; n++) {
var nRow = neighbors[n].row;
var nCol = neighbors[n].col;
// Ensure valid bounds
if (nRow >= 0 && nCol >= 0 && nCol < self.gridCols) {
// Ensure row exists
while (self.bubbleGrid.length <= nRow) {
self.bubbleGrid.push(new Array(self.gridCols).fill(null));
}
if (!self.bubbleGrid[nRow]) {
self.bubbleGrid[nRow] = new Array(self.gridCols).fill(null);
}
if (!self.bubbleGrid[nRow][nCol]) {
var pos = self.getHexPosition(nRow, nCol);
var dx = x - pos.x;
var dy = y - pos.y;
var distSquared = dx * dx + dy * dy;
if (distSquared < minDist) {
minDist = distSquared;
targetRow = nRow;
targetCol = nCol;
}
}
}
}
if (targetRow >= 0 && targetCol >= 0) {
return {
row: targetRow,
col: targetCol
};
}
}
// Fallback: find position in first few rows
var searchRow = Math.max(0, Math.min(5, Math.floor((y - self.startY) / (self.bubbleRadius * 1.7))));
// Ensure the row exists
while (self.bubbleGrid.length <= searchRow + 2) {
self.bubbleGrid.push(new Array(self.gridCols).fill(null));
}
// Search a small area around the calculated position
for (var rowOffset = 0; rowOffset < 3; rowOffset++) {
var row = searchRow + rowOffset;
if (row >= 0 && row < self.bubbleGrid.length && self.bubbleGrid[row]) {
for (var col = 0; col < self.gridCols; col++) {
if (!self.bubbleGrid[row][col]) {
return {
row: row,
col: col
};
}
}
}
}
// If no position found, return first available position in first row
for (var col = 0; col < self.gridCols; col++) {
if (!self.bubbleGrid[0][col]) {
return {
row: 0,
col: col
};
}
}
// Last resort: return valid position
return {
row: 0,
col: 0
};
};
// Check lose condition - bubbles touching red danger line
self.checkLose = function () {
// Check if any bubble touches the danger line
for (var row = 0; row < self.bubbleGrid.length; row++) {
if (self.bubbleGrid[row]) {
for (var col = 0; col < self.gridCols; col++) {
var bubble = self.bubbleGrid[row][col];
if (bubble) {
// Check if bubble position is at or below danger line
if (bubble.y + self.bubbleRadius >= self.dangerLine.y - 10) {
return true;
}
}
}
}
}
return false;
};
// Update method to handle shooting physics - optimized
self.update = function () {
// Pressure system - increment timer and check for automatic descent
self.pressureTimer++;
if (self.pressureTimer >= self.pressureInterval) {
self.moveGridDown(); // Move grid down automatically
self.pressureTimer = 0; // Reset timer
// Check lose condition immediately after moving grid down
if (self.checkLose()) {
LK.showGameOver();
return;
}
}
// Check if bubbles are approaching bottom boundary and generate new rows
var maxBubbleY = -1;
for (var row = 0; row < self.bubbleGrid.length; row++) {
if (self.bubbleGrid[row]) {
for (var col = 0; col < self.gridCols; col++) {
if (self.bubbleGrid[row][col]) {
maxBubbleY = Math.max(maxBubbleY, self.bubbleGrid[row][col].y);
}
}
}
}
// If bubbles are getting close to bottom boundary, add new rows from top
if (maxBubbleY > self.bottomBoundary.y - 200) {
self.addNewRowFromTop();
}
// Update shooting bubble physics
if (self.shootingBubble) {
var bullet = self.shootingBubble;
// Move bullet with velocity
bullet.x += bullet.velocityX;
bullet.y += bullet.velocityY;
// Wall bouncing - reverse X velocity when hitting side walls
if (bullet.x <= self.bubbleRadius) {
bullet.x = self.bubbleRadius;
bullet.velocityX = Math.abs(bullet.velocityX); // Bounce right
} else if (bullet.x >= 2048 - self.bubbleRadius) {
bullet.x = 2048 - self.bubbleRadius;
bullet.velocityX = -Math.abs(bullet.velocityX); // Bounce left
}
// Check collision with existing bubbles
var collision = self.checkCollision(bullet);
var hitTop = bullet.y <= self.startY + self.bubbleRadius;
var shouldAttach = collision || hitTop;
if (shouldAttach) {
// Find closest empty grid position
var closestPos = self.findClosestGridPosition(bullet.x, bullet.y, collision);
if (closestPos && closestPos.row >= 0 && closestPos.col >= 0 && closestPos.col < self.gridCols) {
// Ensure row exists
while (self.bubbleGrid.length <= closestPos.row) {
self.bubbleGrid.push(new Array(self.gridCols).fill(null));
}
if (!self.bubbleGrid[closestPos.row]) {
self.bubbleGrid[closestPos.row] = new Array(self.gridCols).fill(null);
}
// Attach bubble to grid
var gridPos = self.getHexPosition(closestPos.row, closestPos.col);
bullet.x = gridPos.x;
bullet.y = gridPos.y;
bullet.gridRow = closestPos.row;
bullet.gridCol = closestPos.col;
// Add to grid
self.bubbleGrid[closestPos.row][closestPos.col] = bullet;
// Process bubble elimination
var eliminated = self.processBubbleElimination(closestPos.row, closestPos.col);
if (!eliminated) {
// No match found - increment missed shots
self.missedShots++;
if (self.missedShots >= self.maxMissedShots) {
self.moveGridDown();
self.missedShots = 0;
}
}
} else {
// No valid position found - remove bullet
bullet.destroy();
self.missedShots++;
if (self.missedShots >= self.maxMissedShots) {
self.moveGridDown();
self.missedShots = 0;
}
}
// Clear shooting bubble and re-enable shooting
self.shootingBubble = null;
self.canShoot = true;
// Create new bubble for next shot
self.createNewBubble();
// Update missed shots display
if (missedShotsText) {
missedShotsText.setText('Fallos: ' + self.missedShots + '/' + self.maxMissedShots);
}
// Check lose condition
if (self.checkLose()) {
LK.showGameOver();
return;
}
}
}
};
return self;
});
// Add spikes around the corrupted memory
var CorruptedMemory = Container.expand(function () {
var self = Container.call(this);
var memoryGraphics = self.attachAsset('corruptedMemory', {
anchorX: 0.5,
anchorY: 0.5
});
// Add spikes around the corrupted memory
var spike1 = self.attachAsset('spike', {
anchorX: 0.5,
anchorY: 0.5
});
spike1.x = -30;
spike1.y = -30;
spike1.rotation = Math.PI / 4;
var spike2 = self.attachAsset('spike', {
anchorX: 0.5,
anchorY: 0.5
});
spike2.x = 30;
spike2.y = -30;
spike2.rotation = -Math.PI / 4;
var spike3 = self.attachAsset('spike', {
anchorX: 0.5,
anchorY: 0.5
});
spike3.x = 30;
spike3.y = 30;
spike3.rotation = Math.PI / 4;
var spike4 = self.attachAsset('spike', {
anchorX: 0.5,
anchorY: 0.5
});
spike4.x = -30;
spike4.y = 30;
spike4.rotation = -Math.PI / 4;
self.speed = 4;
self.lastY = undefined;
self.update = function () {
self.y += self.speed;
};
return self;
});
var EmotionCharacter = Container.expand(function (emotionType) {
var self = Container.call(this);
var characterGraphics = self.attachAsset(emotionType, {
anchorX: 0.5,
anchorY: 0.5
});
self.emotionType = emotionType;
self.health = 100;
self.maxHealth = 100;
return self;
});
var GoldenMemory = Container.expand(function () {
var self = Container.call(this);
var memoryGraphics = self.attachAsset('goldenMemory', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 3;
self.lastY = undefined;
self.update = function () {
self.y += self.speed;
};
return self;
});
var Pipe = Container.expand(function (isTop, gapY) {
var self = Container.call(this);
var pipeGraphics = self.attachAsset('spike', {
anchorX: 0.5,
anchorY: isTop ? 1 : 0
});
pipeGraphics.tint = 0x4CAF50;
pipeGraphics.width = pipeWidth;
pipeGraphics.height = isTop ? gapY - pipeGap / 2 : 2732 - (gapY + pipeGap / 2);
self.speed = 4;
self.isTop = isTop;
self.scored = false;
self.lastX = undefined;
self.update = function () {
self.x -= self.speed;
};
return self;
});
var SideSpike = Container.expand(function (fromLeft) {
var self = Container.call(this);
var spikeGraphics = self.attachAsset('espinas', {
anchorX: 0.5,
anchorY: 0.5
});
self.fromLeft = fromLeft;
self.speed = fromLeft ? 8 : -8;
self.lastX = undefined;
self.update = function () {
self.x += self.speed;
};
return self;
});
var SnakeGame = Container.expand(function () {
var self = Container.call(this);
// Grid configuration
self.gridSize = 40; // Pixels per cell
self.gridWidth = 50; // Cells horizontally
self.gridHeight = 35; // Cells vertically
// Snake state
self.snake = [{
x: 10,
y: 10
}]; // Start position
self.direction = 'right';
self.nextDirection = 'right';
self.moveTimer = 0;
self.moveInterval = 15; // Move every 15 frames
// Food
self.foodPosition = null;
// Visual elements for destroy/recreate pattern
self.gusanoSprites = [];
self.foodSprite = null;
// Convert grid coordinates to pixel coordinates
self.getPixelPosition = function (gridX, gridY) {
var floorLeft = self.floor.x - 1800 / 2;
var floorTop = self.floor.y - 2400 / 2;
return {
x: floorLeft + gridX * self.gridSize + self.gridSize / 2,
y: floorTop + gridY * self.gridSize + self.gridSize / 2
};
};
// Place food at random empty position
self.placeFood = function () {
var validPositions = [];
// Find all empty positions
for (var x = 0; x < self.gridWidth; x++) {
for (var y = 0; y < self.gridHeight; y++) {
var isEmpty = true;
// Check if position is occupied by snake
for (var i = 0; i < self.snake.length; i++) {
if (self.snake[i].x === x && self.snake[i].y === y) {
isEmpty = false;
break;
}
}
if (isEmpty) {
validPositions.push({
x: x,
y: y
});
}
}
}
if (validPositions.length > 0) {
var randomIndex = Math.floor(Math.random() * validPositions.length);
return validPositions[randomIndex];
}
// Fallback if no valid positions
return {
x: 0,
y: 0
};
};
// Initialize game
self.initGame = function () {
// Increase gridSize dramatically for better visibility
self.gridSize = 120;
// Recalculate grid dimensions based on floor size
var floorWidth = 1800; // Actual playable width
var floorHeight = 2400; // Actual playable height
self.gridWidth = Math.floor(floorWidth / self.gridSize); // ~22 cells wide
self.gridHeight = Math.floor(floorHeight / self.gridSize); // ~30 cells tall
// Create floor background - the actual playable area
self.floor = self.attachAsset('suelo', {
anchorX: 0.5,
anchorY: 0.5
});
self.floor.x = 1024; // Center horizontally
self.floor.y = 1366; // Center vertically
// Scale floor to exact playable dimensions
self.floor.scaleX = floorWidth / 500; // Scale to desired width
self.floor.scaleY = floorHeight / 500; // Scale to desired height
// Ensure floor appears below snake segments
self.floor.zIndex = -1;
// Calculate floor boundaries for proper wall placement
var floorLeft = self.floor.x - floorWidth / 2;
var floorRight = self.floor.x + floorWidth / 2;
var floorTop = self.floor.y - floorHeight / 2;
var floorBottom = self.floor.y + floorHeight / 2;
// Create four walls using pared_de_minijuego_4 - positioned exactly at floor edges
// Top wall
self.topWall = self.attachAsset('pared_de_minijuego_4', {
anchorX: 0.5,
anchorY: 0.5
});
self.topWall.x = 1024;
self.topWall.y = floorTop - 50; // Position above floor
self.topWall.scaleX = (floorWidth + 200) / 73; // Scale to cover full width plus wall thickness
self.topWall.scaleY = 1;
// Bottom wall
self.bottomWall = self.attachAsset('pared_de_minijuego_4', {
anchorX: 0.5,
anchorY: 0.5
});
self.bottomWall.x = 1024;
self.bottomWall.y = floorBottom + 50; // Position below floor
self.bottomWall.scaleX = (floorWidth + 200) / 73; // Scale to cover full width plus wall thickness
self.bottomWall.scaleY = 1;
// Left wall
self.leftWall = self.attachAsset('pared_de_minijuego_4', {
anchorX: 0.5,
anchorY: 0.5
});
self.leftWall.x = floorLeft - 50; // Position left of floor
self.leftWall.y = 1366;
self.leftWall.rotation = Math.PI / 2; // Rotate 90 degrees
self.leftWall.scaleX = (floorHeight + 200) / 73; // Scale to cover full height plus wall thickness
self.leftWall.scaleY = 1;
// Right wall
self.rightWall = self.attachAsset('pared_de_minijuego_4', {
anchorX: 0.5,
anchorY: 0.5
});
self.rightWall.x = floorRight + 50; // Position right of floor
self.rightWall.y = 1366;
self.rightWall.rotation = Math.PI / 2; // Rotate 90 degrees
self.rightWall.scaleX = (floorHeight + 200) / 73; // Scale to cover full height plus wall thickness
self.rightWall.scaleY = 1;
// Place food and create visuals
self.foodPosition = self.placeFood();
self.createVisuals();
};
// Render game - destroys and recreates all visuals to ensure top layer
self.renderGame = function () {
// STEP 1: DESTROY OLD SPRITES
if (self.foodSprite) {
self.foodSprite.destroy();
self.foodSprite = null;
}
for (var i = 0; i < self.gusanoSprites.length; i++) {
self.gusanoSprites[i].destroy();
}
self.gusanoSprites = [];
// STEP 2: DRAW FOOD (drawn first)
if (self.foodPosition) {
var foodPosPix = self.getPixelPosition(self.foodPosition.x, self.foodPosition.y);
self.foodSprite = LK.getAsset('goldenMemory', {
anchorX: 0.5,
anchorY: 0.5
});
self.foodSprite.x = foodPosPix.x;
self.foodSprite.y = foodPosPix.y;
self.foodSprite.width = self.gridSize;
self.foodSprite.height = self.gridSize;
self.foodSprite.tint = 0xFF0000; // Red apple/food
self.addChild(self.foodSprite);
}
// STEP 3: DRAW SNAKE (drawn last, appears on top)
for (var i = 0; i < self.snake.length; i++) {
var segmentPosPix = self.getPixelPosition(self.snake[i].x, self.snake[i].y);
// Use green snake segments
var sprite = LK.getAsset('gusano_minijuego4', {
anchorX: 0.5,
anchorY: 0.5
});
sprite.x = segmentPosPix.x;
sprite.y = segmentPosPix.y;
sprite.width = self.gridSize;
sprite.height = self.gridSize;
sprite.tint = 0x00FF00; // Bright green for snake
self.addChild(sprite);
self.gusanoSprites.push(sprite);
}
};
// Create visual elements
self.createVisuals = function () {
// Use renderGame method for consistent rendering
self.renderGame();
};
// Update visuals
self.updateVisuals = function () {
// Use renderGame method for consistent rendering
self.renderGame();
};
// Check game over conditions
self.checkGameOver = function (headPosition) {
// Check wall collision
if (headPosition.x < 0 || headPosition.x >= self.gridWidth || headPosition.y < 0 || headPosition.y >= self.gridHeight) {
return true;
}
// Check self collision
for (var i = 0; i < self.snake.length; i++) {
if (self.snake[i].x === headPosition.x && self.snake[i].y === headPosition.y) {
return true;
}
}
return false;
};
// Main update loop
self.update = function () {
// Control movement speed
self.moveTimer++;
if (self.moveTimer < self.moveInterval) {
return;
}
self.moveTimer = 0;
// Update direction
self.direction = self.nextDirection;
// Calculate new head position
var head = self.snake[0];
var newHead = {
x: head.x,
y: head.y
};
if (self.direction === 'up') {
newHead.y -= 1;
} else if (self.direction === 'down') {
newHead.y += 1;
} else if (self.direction === 'left') {
newHead.x -= 1;
} else if (self.direction === 'right') {
newHead.x += 1;
}
// Check game over
if (self.checkGameOver(newHead)) {
LK.showGameOver();
return;
}
// Add new head
self.snake.unshift(newHead);
// Check food consumption
if (newHead.x === self.foodPosition.x && newHead.y === self.foodPosition.y) {
// Eat food - snake grows, place new food
LK.setScore(LK.getScore() + 10);
// Place new food (visuals will be handled by renderGame)
self.foodPosition = self.placeFood();
LK.getSound('collect').play();
} else {
// Normal movement - remove tail
self.snake.pop();
}
// Render all visuals (final step - ensures snake appears on top)
self.renderGame();
};
// Keyboard controls are handled by the global keyDown event listener
// No mouse click handling needed for Snake Game
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x87CEEB
});
/****
* Game Code
****/
// All requested features are already properly implemented:
// - Jumpscare probability: 25% (line with Math.random() < 0.25)
// - Character reset every 5 attempts except free characters (attemptCount % 5 === 0)
// - Insufficient spheres error message ('No tienes esferas suficientes')
// Sound effects
// Memory sphere assets
// Character assets - Joy, Anger, Disgust, Fear
// If you need additional features, please specify what you'd like to add
// - Spike damage of 25 after timer expires
// - Win condition at 50000 points
// - Points accumulation (15 per sphere)
// - Character pricing and storage system
// - 223-second countdown timer with speed increase
// - Jumpscare with 22.2% probability on game over
// - Invisible hitboxes for JUGAR button and character selection
// All previously requested features have been successfully implemented:
var selectedEmotion = storage.selectedEmotion || 'joy';
var gameStarted = false;
var countdownActive = false;
var countdownValue = 3;
var character = null;
var goldenMemories = [];
var corruptedMemories = [];
var spawnTimer = 0;
var difficultyLevel = 1;
var health = 100;
var maxHealth = 100;
// Game state management
var gameState = 'menu'; // 'menu', 'levelSelection', 'characterSelection', 'countdown', 'playing'
var currentLevel = storage.currentLevel || 1;
var levelBoxes = [];
var characterSelectionActive = false;
var selectionButtons = [];
// Timer variables
var gameTimer = 223; // 223 seconds
var timerActive = false;
var speedIncreased = false;
// Character pricing and spheres
var totalSpheres = storage.totalSpheres || 0;
var attemptCount = storage.attemptCount || 0;
var characterPrices = {
joy: 0,
// Free
anger: 60,
disgust: 120,
fear: 180,
tristesa: 240
};
var ownedCharacters = storage.ownedCharacters || ['joy']; // Joy is free by default
// Level-specific attempt tracking
var levelAttempts = storage.levelAttempts || {};
// All levels are unlocked by default, no need to track unlocked levels
// Speed boost variables for level 1
var speedBoostActive = false;
var speedBoostTimer = 0;
var isLeftMouseDown = false;
// Upgrade system variables
var sphereMultiplier = storage.sphereMultiplier || 1;
var speedMultiplier = storage.speedMultiplier || 1;
var maxHealthBonus = storage.maxHealthBonus || 0;
var shopItems = [];
// Shop timer variables
var shopTimer = 5;
var shopTimerActive = false;
var shopTimerText = null;
var shopTimerInterval = null;
// Level 2 specific variables - Flappy Bird style
var sideSpikes = [];
var birdVelocity = 0;
var gravity = 0.8;
var flapStrength = -12;
var pipes = [];
var pipeGap = 400;
var pipeWidth = 100;
var distanceTraveled = 0;
var scoreCounter = 0;
// Level 3 specific variables - Bubble Shooter
var bubbleShooter = null;
var aimAngle = 0;
var aimGuide = [];
var shootPower = 15;
// Missed shots counter for Level 3
var missedShotsText = new Text2('Fallos: 0/5', {
size: 50,
fill: 0xFFFFFF
});
missedShotsText.anchor.set(1, 1);
missedShotsText.alpha = 0; // Hide initially
LK.gui.bottomRight.addChild(missedShotsText);
var heightText = new Text2('Distancia: 0m', {
size: 50,
fill: 0xFFFFFF
});
heightText.anchor.set(0, 0);
LK.gui.bottomLeft.addChild(heightText);
heightText.y = 100;
// Add invisible collision box to heightText
var heightTextBox = LK.getAsset('level1', {
anchorX: 0,
anchorY: 0,
scaleX: 1.5,
scaleY: 0.5
});
heightTextBox.alpha = 0;
heightTextBox.x = 0;
heightTextBox.y = 100;
heightTextBox.textElement = heightText;
LK.gui.bottomLeft.addChild(heightTextBox);
// heightText no longer auto-hides
// UI Elements
var scoreText = new Text2('Puntos: 0', {
size: 60,
fill: 0xFFFFFF
});
scoreText.anchor.set(0.5, 0);
scoreText.alpha = 0; // Hide initially
LK.gui.top.addChild(scoreText);
// Add invisible collision box to scoreText
var scoreTextBox = LK.getAsset('level1', {
anchorX: 0.5,
anchorY: 0,
scaleX: 1.5,
scaleY: 0.6
});
scoreTextBox.alpha = 0;
scoreTextBox.x = 0;
scoreTextBox.y = 0;
scoreTextBox.textElement = scoreText;
LK.gui.top.addChild(scoreTextBox);
// scoreText no longer auto-hides
var healthText = new Text2('Vida: 100/100', {
size: 50,
fill: 0xFF0000
});
healthText.anchor.set(1, 0);
LK.gui.topRight.addChild(healthText);
// Add invisible collision box to healthText
var healthTextBox = LK.getAsset('level1', {
anchorX: 1,
anchorY: 0,
scaleX: 1.2,
scaleY: 0.5
});
healthTextBox.alpha = 0;
healthTextBox.x = 0;
healthTextBox.y = 0;
healthTextBox.textElement = healthText;
LK.gui.topRight.addChild(healthTextBox);
// healthText no longer auto-hides
// Timer display
var timerText = new Text2('Tiempo: 223', {
size: 50,
fill: 0xFFFFFF
});
timerText.anchor.set(0, 1);
LK.gui.bottomLeft.addChild(timerText);
// Add invisible collision box to timerText
var timerTextBox = LK.getAsset('level1', {
anchorX: 0,
anchorY: 1,
scaleX: 1.2,
scaleY: 0.5
});
timerTextBox.alpha = 0;
timerTextBox.x = 0;
timerTextBox.y = 0;
timerTextBox.textElement = timerText;
LK.gui.bottomLeft.addChild(timerTextBox);
// timerText no longer auto-hides
// Sphere counter text (moved to where energy bar was)
var sphereCountText = new Text2('Esferas: ' + totalSpheres, {
size: 40,
fill: 0xFFD700
});
sphereCountText.anchor.set(1, 0);
sphereCountText.x = -20;
sphereCountText.y = 80;
LK.gui.topRight.addChild(sphereCountText);
// Add invisible collision box to sphereCountText
var sphereCountTextBox = LK.getAsset('level1', {
anchorX: 1,
anchorY: 0,
scaleX: 1.2,
scaleY: 0.5
});
sphereCountTextBox.alpha = 0;
sphereCountTextBox.x = -20;
sphereCountTextBox.y = 80;
sphereCountTextBox.textElement = sphereCountText;
LK.gui.topRight.addChild(sphereCountTextBox);
// sphereCountText no longer auto-hides
// Menu button - rectangular button to return to level selection (moved to bottom right)
var menuButton = LK.getAsset('level1', {
anchorX: 1,
anchorY: 1,
scaleX: 0.8,
scaleY: 0.6
});
menuButton.x = 2048 - 50;
menuButton.y = 2732 - 50;
menuButton.tint = 0x333333;
menuButton.alpha = 0; // Hide initially
game.addChild(menuButton);
var menuButtonText = new Text2('MENU', {
size: 50,
fill: 0xFFFFFF
});
menuButtonText.anchor.set(0.5, 0.5);
menuButtonText.x = 2048 - 50 - 240 / 2; // Center horizontally within button
menuButtonText.y = 2732 - 50 - 120 / 2; // Center vertically within button
menuButtonText.alpha = 0; // Hide initially
game.addChild(menuButtonText);
// Main Menu UI
var titleText = new Text2('INTENSAMENTE', {
size: 120,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 1024;
titleText.y = 800;
game.addChild(titleText);
// Add invisible collision box to titleText
var titleTextBox = LK.getAsset('level1', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2,
scaleY: 0.8
});
titleTextBox.alpha = 0;
titleTextBox.x = titleText.x;
titleTextBox.y = titleText.y;
titleTextBox.textElement = titleText;
game.addChild(titleTextBox);
// titleText no longer auto-hides
var playButton = new Text2('JUGAR', {
size: 80,
fill: 0x00FF00
});
playButton.anchor.set(0.5, 0.5);
playButton.x = 1024;
playButton.y = 1200;
game.addChild(playButton);
// Add invisible collision box to playButton
var playButtonBox = LK.getAsset('level1', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 0.8
});
playButtonBox.alpha = 0;
playButtonBox.x = playButton.x;
playButtonBox.y = playButton.y;
playButtonBox.textElement = playButton;
game.addChild(playButtonBox);
// playButton no longer auto-hides
var instructionText = new Text2('Selecciona tu personaje', {
size: 80,
fill: 0xFFFFFF
});
instructionText.anchor.set(0.5, 0.5);
instructionText.x = 1024;
instructionText.y = 600;
instructionText.alpha = 0;
instructionText.visible = false;
game.addChild(instructionText);
// Add invisible collision box to instructionText
var instructionTextBox = LK.getAsset('level1', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.5,
scaleY: 0.8
});
instructionTextBox.alpha = 0;
instructionTextBox.x = instructionText.x;
instructionTextBox.y = instructionText.y;
instructionTextBox.textElement = instructionText;
game.addChild(instructionTextBox);
// instructionText no longer auto-hides
instructionText.showWithTimeout = function () {
instructionText.alpha = 1;
instructionText.visible = true;
};
var countdownText = new Text2('3', {
size: 200,
fill: 0xFFFFFF
});
countdownText.anchor.set(0.5, 0.5);
countdownText.x = 1024;
countdownText.y = 1366;
countdownText.alpha = 0;
countdownText.visible = false;
game.addChild(countdownText);
// Add invisible collision box to countdownText
var countdownTextBox = LK.getAsset('level1', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 1.2
});
countdownTextBox.alpha = 0;
countdownTextBox.x = countdownText.x;
countdownTextBox.y = countdownText.y;
countdownTextBox.textElement = countdownText;
game.addChild(countdownTextBox);
// countdownText no longer auto-hides
countdownText.showWithTimeout = function () {
countdownText.alpha = 1;
countdownText.visible = true;
};
// Spawn static character selection spheres in center
function spawnCharacterSelectionSpheres() {
// Only spawn once
if (goldenMemories.length === 0) {
var emotions = [{
type: 'joy',
name: 'ALEGRÍA',
color: 0xffeb3b,
x: 524,
y: 800
}, {
type: 'anger',
name: 'ENOJO',
color: 0xf44336,
x: 1524,
y: 800
}, {
type: 'disgust',
name: 'DESAGRADO',
color: 0x4caf50,
x: 524,
y: 1200
}, {
type: 'fear',
name: 'MIEDO',
color: 0x9c27b0,
x: 1024,
y: 1200
}, {
type: 'tristesa',
name: 'TRISTEZA',
color: 0x2196f3,
x: 1524,
y: 1200
}];
// Hide score text in character selection
scoreText.alpha = 0;
// Add spheres counter display only if not lvl1
if (currentLevel !== 1) {
var spheresText = new Text2('Esferas: ' + totalSpheres, {
size: 60,
fill: 0xFFD700
});
spheresText.anchor.set(0.5, 0.5);
spheresText.x = 1024;
spheresText.y = 500;
game.addChild(spheresText);
}
for (var i = 0; i < emotions.length; i++) {
var emotionData = emotions[i];
// Create character image instead of sphere
var characterImage = LK.getAsset(emotionData.type, {
anchorX: 0.5,
anchorY: 0.5
});
characterImage.x = emotionData.x;
characterImage.y = emotionData.y;
characterImage.characterType = emotionData.type;
game.addChild(characterImage);
goldenMemories.push(characterImage);
// Check if character is owned or affordable
var isOwned = ownedCharacters.indexOf(emotionData.type) !== -1;
var canAfford = totalSpheres >= characterPrices[emotionData.type];
var price = characterPrices[emotionData.type];
// Dim character if not owned and can't afford
if (!isOwned && !canAfford) {
characterImage.alpha = 0.3;
}
// Add text label with price
var labelText = emotionData.name;
if (!isOwned) {
labelText += '\nPrecio: ' + price + ' esferas';
} else {
labelText += '\nDISPONIBLE';
}
var nameText = new Text2(labelText, {
size: 35,
fill: isOwned ? 0x00FF00 : canAfford ? 0xFFFFFF : 0xFF6666
});
nameText.anchor.set(0.5, 0.5);
nameText.x = emotionData.x;
nameText.y = emotionData.y + 150;
game.addChild(nameText);
characterImage.nameText = nameText;
characterImage.isOwned = isOwned;
characterImage.canAfford = canAfford;
}
}
}
// Handle sphere selection for character choice
function handleSphereSelection(sphere) {
if (sphere.characterType) {
var characterType = sphere.characterType;
var isOwned = ownedCharacters.indexOf(characterType) !== -1;
var canAfford = totalSpheres >= characterPrices[characterType];
var price = characterPrices[characterType];
// Check if character can be selected
if (!isOwned && !canAfford) {
// Flash red to indicate can't select
LK.effects.flashObject(sphere, 0xFF0000, 500);
// Play error sound
LK.getSound('error').play();
// Show insufficient spheres message
var errorText = new Text2('No tienes esferas suficientes', {
size: 60,
fill: 0xFF0000
});
errorText.anchor.set(0.5, 0.5);
errorText.x = 1024;
errorText.y = 2200;
game.addChild(errorText);
// Remove error message after 2 seconds
LK.setTimeout(function () {
errorText.destroy();
}, 2000);
return;
}
// If not owned but can afford, purchase the character
if (!isOwned && canAfford) {
totalSpheres -= price;
ownedCharacters.push(characterType);
storage.totalSpheres = totalSpheres;
storage.ownedCharacters = ownedCharacters;
// Update spheres display after purchase
var spheresTextElements = [];
for (var k = 0; k < game.children.length; k++) {
if (game.children[k].text && game.children[k].text.indexOf('Esferas:') === 0) {
game.children[k].setText('Esferas: ' + totalSpheres);
break;
}
}
} else if (isOwned) {
// Character is already owned, no need to purchase again
} else {
// Character not owned and can't afford - deduct price anyway and add to owned
totalSpheres -= price;
if (totalSpheres < 0) totalSpheres = 0; // Prevent negative spheres
ownedCharacters.push(characterType);
storage.totalSpheres = totalSpheres;
storage.ownedCharacters = ownedCharacters;
// Update spheres display after purchase
for (var k = 0; k < game.children.length; k++) {
if (game.children[k].text && game.children[k].text.indexOf('Esferas:') === 0) {
game.children[k].setText('Esferas: ' + totalSpheres);
break;
}
}
}
selectedEmotion = characterType;
storage.selectedEmotion = selectedEmotion;
// Flash selected sphere
LK.effects.flashObject(sphere, 0xFFFFFF, 500);
// Clear all spheres and their labels
for (var i = goldenMemories.length - 1; i >= 0; i--) {
if (goldenMemories[i].nameText) {
goldenMemories[i].nameText.destroy();
}
goldenMemories[i].destroy();
goldenMemories.splice(i, 1);
}
// Instruction text already removed, no hiding needed
// Go to shop before starting game
LK.setTimeout(function () {
createShop();
}, 600);
}
}
// Create level selection UI
function createLevelSelection() {
gameState = 'levelSelection';
// Hide menu elements
titleText.alpha = 0;
playButton.alpha = 0;
// Level selection title removed to avoid interference
// Create level boxes in a grid (2 columns, 5 rows)
var levelNames = ['MINIJUEGO 1', 'MINIJUEGO 2', 'MINIJUEGO 3', 'MINIJUEGO 4', 'MINIJUEGO 5', 'MINIJUEGO 6', 'MINIJUEGO 7', 'MINIJUEGO 8', 'MINIJUEGO 9', 'MINIJUEGO 10'];
var levelAssets = ['level1', 'level2', 'level3', 'level4', 'level5', 'level6', 'level7', 'level8', 'level9', 'level10'];
for (var i = 0; i < 10; i++) {
var col = i % 2;
var row = Math.floor(i / 2);
var levelBox = LK.getAsset(levelAssets[i], {
anchorX: 0.5,
anchorY: 0.5
});
levelBox.x = 600 + col * 848; // 600 and 1448 for two columns
levelBox.y = 700 + row * 280; // Spacing between rows
levelBox.levelNumber = i + 1;
levelBox.levelName = levelNames[i];
game.addChild(levelBox);
levelBoxes.push(levelBox);
// All levels are unlocked
var isUnlocked = true;
// No dimming since all levels are unlocked
// Add level text without lock status
var levelText = new Text2(levelNames[i], {
size: 50,
fill: 0xFFFFFF
});
levelText.anchor.set(0.5, 0.5);
levelText.x = levelBox.x;
levelText.y = levelBox.y;
game.addChild(levelText);
levelBox.levelText = levelText;
levelBox.isUnlocked = isUnlocked;
}
}
// Create shop screen
function createShop() {
gameState = 'shop';
// Hide other UI elements
titleText.alpha = 0;
playButton.alpha = 0;
instructionText.alpha = 0;
// Shop title
var shopTitle = new Text2('TIENDA DE MEJORAS', {
size: 120,
fill: 0xFFD700
});
shopTitle.anchor.set(0.5, 0.5);
shopTitle.x = 1024;
shopTitle.y = 400;
game.addChild(shopTitle);
// Show current spheres
var spheresDisplay = new Text2('Esferas: ' + totalSpheres, {
size: 80,
fill: 0xFFFFFF
});
spheresDisplay.anchor.set(0.5, 0.5);
spheresDisplay.x = 1024;
spheresDisplay.y = 500;
game.addChild(spheresDisplay);
// Create shop items
var shopItemsData = [{
name: 'Multiplicador\nde Esferas',
description: 'x' + (sphereMultiplier + 0.2).toFixed(1),
price: Math.floor(50 * Math.pow(sphereMultiplier, 2)),
type: 'sphere_multiplier',
x: 524,
y: 800
}, {
name: 'Multiplicador\nde Velocidad',
description: '+5% Velocidad',
price: Math.floor(30 * Math.pow(speedMultiplier, 1.5)),
type: 'speed_multiplier',
x: 1024,
y: 800
}, {
name: 'Aumento\nde Vida',
description: '+5 Vida Max',
price: Math.floor(40 * Math.pow(maxHealthBonus / 5 + 1, 1.2)),
type: 'health_bonus',
x: 1524,
y: 800
}];
for (var i = 0; i < shopItemsData.length; i++) {
var itemData = shopItemsData[i];
// Create item box
var itemBox = LK.getAsset('level1', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 1.2
});
itemBox.x = itemData.x;
itemBox.y = itemData.y;
itemBox.itemType = itemData.type;
itemBox.price = itemData.price;
itemBox.tint = 0x00FF00;
game.addChild(itemBox);
shopItems.push(itemBox);
// Item name
var nameText = new Text2(itemData.name, {
size: 50,
fill: 0xFFFFFF
});
nameText.anchor.set(0.5, 0.5);
nameText.x = itemData.x;
nameText.y = itemData.y - 40;
game.addChild(nameText);
itemBox.nameText = nameText;
// Item description
var descText = new Text2(itemData.description, {
size: 45,
fill: 0xFFD700
});
descText.anchor.set(0.5, 0.5);
descText.x = itemData.x;
descText.y = itemData.y;
game.addChild(descText);
itemBox.descText = descText;
// Item price
var priceText = new Text2('Precio: ' + itemData.price, {
size: 45,
fill: totalSpheres >= itemData.price ? 0x00FF00 : 0xFF0000
});
priceText.anchor.set(0.5, 0.5);
priceText.x = itemData.x;
priceText.y = itemData.y + 40;
game.addChild(priceText);
itemBox.priceText = priceText;
}
// Exit button (changed from Continue)
var exitBtn = new Text2('SALIR', {
size: 80,
fill: 0x00FF00
});
exitBtn.anchor.set(0.5, 0.5);
exitBtn.x = 1024;
exitBtn.y = 1400; // Moved down by 200px
game.addChild(exitBtn);
shopItems.push(exitBtn);
exitBtn.isContinueBtn = true;
// Skip button
var skipBtn = new Text2('SALTAR', {
size: 80,
fill: 0x00FF00
});
skipBtn.anchor.set(0.5, 0.5);
skipBtn.x = 1024;
skipBtn.y = 1500; // Moved down by 200px
game.addChild(skipBtn);
shopItems.push(skipBtn);
skipBtn.isSkipBtn = true;
// Shop timer display
shopTimerText = new Text2('Tiempo: 5', {
size: 80,
fill: 0xFF0000
});
shopTimerText.anchor.set(0.5, 0.5);
shopTimerText.x = 1024;
shopTimerText.y = 1200; // Moved up to make room for buttons
game.addChild(shopTimerText);
shopItems.push(shopTimerText);
// Start shop timer
shopTimer = 5;
shopTimerActive = true;
shopTimerInterval = LK.setInterval(function () {
shopTimer--;
shopTimerText.setText('Tiempo: ' + shopTimer);
if (shopTimer <= 0) {
LK.clearInterval(shopTimerInterval);
shopTimerActive = false;
// Auto-proceed when timer expires
// Clear shop UI
for (var j = shopItems.length - 1; j >= 0; j--) {
if (shopItems[j].nameText) shopItems[j].nameText.destroy();
if (shopItems[j].descText) shopItems[j].descText.destroy();
if (shopItems[j].priceText) shopItems[j].priceText.destroy();
shopItems[j].destroy();
}
shopItems = [];
// Clear shop title and spheres display
for (var k = game.children.length - 1; k >= 0; k--) {
if (game.children[k].text && (game.children[k].text.indexOf('TIENDA') === 0 || game.children[k].text.indexOf('Esferas:') === 0)) {
tween(game.children[k], {
alpha: 0
}, {
duration: 1000
});
LK.setTimeout(function (element) {
return function () {
if (element && element.destroy) {
element.destroy();
}
};
}(game.children[k]), 1000);
}
}
// Go to countdown
startCountdown();
}
}, 1000);
}
// Start character selection
function startCharacterSelection() {
gameState = 'characterSelection';
// Hide menu elements
titleText.alpha = 0;
playButton.alpha = 0;
// Character selection instruction text removed to avoid interference
// Spawn character selection spheres
spawnCharacterSelectionSpheres();
}
// Start countdown before game
function startCountdown() {
gameState = 'countdown';
countdownActive = true;
countdownValue = 3;
countdownText.showWithTimeout();
countdownText.setText('3');
var countdownInterval = LK.setInterval(function () {
countdownValue--;
if (countdownValue > 0) {
countdownText.setText(countdownValue.toString());
} else if (countdownValue === 0) {
countdownText.setText('¡Juega!');
} else {
LK.clearInterval(countdownInterval);
countdownText.alpha = 0;
countdownText.visible = false;
startActualGame();
}
}, 1000);
}
// Start the actual game
function startActualGame() {
gameState = 'playing';
countdownActive = false;
gameStarted = true;
// Initialize timer
gameTimer = 223;
timerActive = true;
speedIncreased = false;
// Show menu button during gameplay
menuButton.alpha = 1;
menuButtonText.alpha = 1;
// Hide all level selection boxes during gameplay
for (var i = 0; i < levelBoxes.length; i++) {
levelBoxes[i].alpha = 0;
if (levelBoxes[i].levelText) {
levelBoxes[i].levelText.alpha = 0;
}
}
// Show/hide UI elements based on level
if (currentLevel === 1) {
// Level 1: Show health, score, timer, sphere counter
scoreText.alpha = 1;
healthText.alpha = 1;
timerText.alpha = 1;
heightText.alpha = 0;
sphereCountText.alpha = 1;
} else if (currentLevel === 2) {
// Level 2: Show only distance and score
scoreText.alpha = 1;
healthText.alpha = 0;
timerText.alpha = 0;
heightText.alpha = 1;
sphereCountText.alpha = 0;
} else if (currentLevel === 3) {
// Level 3: Show score and missed shots counter
scoreText.alpha = 1;
healthText.alpha = 0;
timerText.alpha = 0;
heightText.alpha = 0;
sphereCountText.alpha = 0;
missedShotsText.alpha = 1;
} else if (currentLevel === 4) {
// Level 4: Show only score
scoreText.alpha = 1;
healthText.alpha = 0;
timerText.alpha = 0;
heightText.alpha = 0;
sphereCountText.alpha = 0;
missedShotsText.alpha = 0;
} else {
// Other levels: Show score and sphere counter
scoreText.alpha = 1;
healthText.alpha = 0;
timerText.alpha = 0;
heightText.alpha = 0;
sphereCountText.alpha = 1;
}
// Create player character
if (currentLevel === 3) {
// Level 3: Create bubble shooter instead of character
bubbleShooter = new BubbleShooter();
bubbleShooter.initGrid();
bubbleShooter.createInitialBubbles();
game.addChild(bubbleShooter);
// Create cannon and add to bubbleShooter container
var cannon = LK.getAsset('cannon', {
anchorX: 0.5,
anchorY: 1
});
cannon.x = 1024;
cannon.y = 2600;
bubbleShooter.cannon = cannon;
bubbleShooter.addChild(cannon); // Add cannon to bubbleShooter instead of game
// Create initial bubbles after setting up cannon
bubbleShooter.createNewBubble();
} else if (currentLevel === 4) {
// Level 4: Create Snake Game
var snakeGame = new SnakeGame();
snakeGame.initGame();
game.addChild(snakeGame);
// Store reference for input handling
game.snakeGame = snakeGame;
} else {
character = new EmotionCharacter(selectedEmotion);
if (currentLevel === 2) {
// Spawn in middle of screen for Flappy Bird level
character.x = 1024;
character.y = 1366; // Middle of screen vertically
// Reset level 2 specific variables
birdVelocity = 0;
distanceTraveled = 0;
scoreCounter = 0;
// Clear any existing pipes
for (var p = pipes.length - 1; p >= 0; p--) {
pipes[p].destroy();
}
pipes = [];
} else {
character.x = 1024;
character.y = 2400;
// Reset level 1 specific variables with upgrades
health = 100 + maxHealthBonus;
maxHealth = 100 + maxHealthBonus;
healthText.setText('Vida: ' + health + '/' + maxHealth);
// Clear any existing memories
for (var g = goldenMemories.length - 1; g >= 0; g--) {
goldenMemories[g].destroy();
}
goldenMemories = [];
for (var c = corruptedMemories.length - 1; c >= 0; c--) {
corruptedMemories[c].destroy();
}
corruptedMemories = [];
}
game.addChild(character);
}
// Start level-specific background music
if (currentLevel === 2) {
LK.playMusic('musica_de_nivel_2');
} else {
LK.playMusic('gameMusic');
}
}
// Spawn memory function
function spawnMemory() {
if (gameState === 'characterSelection') {
// Character selection phase - spawn static character spheres in center
spawnCharacterSelectionSpheres();
} else if (gameState === 'playing') {
// Game phase - spawn colored spheres and corrupted ones
var spawnX = Math.random() * (2048 - 160) + 80;
var randomType = Math.random();
// Define sphere colors for optimization
var sphereColors = [0xffeb3b, 0x4caf50, 0x2196f3, 0xf44336];
var colorThresholds = [0.15, 0.3, 0.45, 0.6];
// Calculate time-based spine multiplier - more spines as time progresses
var initialTimer = 223;
var timeElapsed = initialTimer - gameTimer;
var timeProgressRatio = Math.min(timeElapsed / initialTimer, 1); // 0 to 1 progression
// Base spine spawn probability increases with time (starts at 0.33, goes up to 0.85)
var baseSpineProbability = 0.33 + timeProgressRatio * 0.52;
// Check for golden memory creation - probability decreases over time to allow for more spines
var goldenProbability = 0.67 * (1 - timeProgressRatio * 0.6); // Decreases from 0.67 to ~0.27
var createGolden = false;
var tintColor = 0xffeb3b;
for (var i = 0; i < colorThresholds.length; i++) {
if (randomType < colorThresholds[i] * goldenProbability) {
createGolden = true;
tintColor = sphereColors[i];
break;
}
}
if (createGolden) {
var memory = new GoldenMemory();
memory.x = spawnX;
memory.y = -100;
memory.lastY = memory.y;
memory.children[0].tint = tintColor;
goldenMemories.push(memory);
game.addChild(memory);
} else {
// Corrupted memory with spikes - spawn rate increases dramatically over time
var corrupted = new CorruptedMemory();
corrupted.x = spawnX;
corrupted.y = -100;
corrupted.lastY = corrupted.y;
corruptedMemories.push(corrupted);
game.addChild(corrupted);
}
}
}
// Game event handlers
game.down = function (x, y, obj) {
if (gameState === 'menu') {
// Check if JUGAR button was clicked with invisible hitbox
var playButtonX = playButton.x - playButton.width / 2;
var playButtonY = playButton.y - playButton.height / 2;
var playButtonWidth = playButton.width;
var playButtonHeight = playButton.height;
if (x >= playButtonX && x <= playButtonX + playButtonWidth && y >= playButtonY && y <= playButtonY + playButtonHeight) {
createLevelSelection();
}
} else if (gameState === 'levelSelection') {
// Check if any level box was clicked
for (var i = 0; i < levelBoxes.length; i++) {
var levelBox = levelBoxes[i];
var boxX = levelBox.x - 150; // Half of width (300/2)
var boxY = levelBox.y - 100; // Half of height (200/2)
var boxWidth = 300;
var boxHeight = 200;
if (x >= boxX && x <= boxX + boxWidth && y >= boxY && y <= boxY + boxHeight) {
// All levels are unlocked - no need to check
// Flash selected level
LK.effects.flashObject(levelBox, 0xFFFFFF, 500);
// Set current level
currentLevel = levelBox.levelNumber;
storage.currentLevel = currentLevel;
// Clear level selection UI
for (var j = levelBoxes.length - 1; j >= 0; j--) {
if (levelBoxes[j].levelText) {
levelBoxes[j].levelText.destroy();
}
levelBoxes[j].destroy();
}
levelBoxes = [];
// Level selection title already removed, no cleanup needed
// Start character selection after short delay
LK.setTimeout(function () {
startCharacterSelection();
}, 600);
break;
}
}
} else if (gameState === 'shop') {
// Handle shop item clicks
for (var i = 0; i < shopItems.length; i++) {
var item = shopItems[i];
var itemX = item.x - 150;
var itemY = item.y - 100;
var itemWidth = 300;
var itemHeight = 200;
if (x >= itemX && x <= itemX + itemWidth && y >= itemY && y <= itemY + itemHeight) {
if (item.isContinueBtn || item.isSkipBtn) {
// Clear shop timer
if (shopTimerInterval) {
LK.clearInterval(shopTimerInterval);
shopTimerActive = false;
}
// Clear shop UI
for (var j = shopItems.length - 1; j >= 0; j--) {
if (shopItems[j].nameText) shopItems[j].nameText.destroy();
if (shopItems[j].descText) shopItems[j].descText.destroy();
if (shopItems[j].priceText) shopItems[j].priceText.destroy();
shopItems[j].destroy();
}
shopItems = [];
// Clear shop title and spheres display
for (var k = game.children.length - 1; k >= 0; k--) {
if (game.children[k].text && (game.children[k].text.indexOf('TIENDA') === 0 || game.children[k].text.indexOf('Esferas:') === 0)) {
game.children[k].destroy();
}
}
if (item.isSkipBtn) {
// Skip shop timer and go directly to countdown
startCountdown();
} else {
// Exit button - go directly to countdown
startCountdown();
}
break;
} else if (totalSpheres >= item.price) {
// Purchase item
totalSpheres -= item.price;
storage.totalSpheres = totalSpheres;
if (item.itemType === 'sphere_multiplier') {
sphereMultiplier += 0.2;
storage.sphereMultiplier = sphereMultiplier;
} else if (item.itemType === 'speed_multiplier') {
speedMultiplier += 0.05;
storage.speedMultiplier = speedMultiplier;
} else if (item.itemType === 'health_bonus') {
maxHealthBonus += 5;
storage.maxHealthBonus = maxHealthBonus;
}
LK.effects.flashObject(item, 0x00FF00, 300);
LK.getSound('collect').play();
// Clear shop timer before recreating
if (shopTimerInterval) {
LK.clearInterval(shopTimerInterval);
shopTimerActive = false;
}
// Recreate shop to update prices
for (var j = shopItems.length - 1; j >= 0; j--) {
if (shopItems[j].nameText) shopItems[j].nameText.destroy();
if (shopItems[j].descText) shopItems[j].descText.destroy();
if (shopItems[j].priceText) shopItems[j].priceText.destroy();
shopItems[j].destroy();
}
shopItems = [];
for (var k = game.children.length - 1; k >= 0; k--) {
if (game.children[k].text && (game.children[k].text.indexOf('TIENDA') === 0 || game.children[k].text.indexOf('Esferas:') === 0)) {
game.children[k].destroy();
}
}
createShop();
break;
} else {
LK.effects.flashObject(item, 0xFF0000, 300);
LK.getSound('error').play();
}
}
}
} else if (gameState === 'characterSelection') {
// Check if any character sphere was clicked with invisible hitboxes
for (var i = 0; i < goldenMemories.length; i++) {
var sphere = goldenMemories[i];
var sphereX = sphere.x - 60; // Sphere width is 120, so half is 60
var sphereY = sphere.y - 60; // Sphere height is 120, so half is 60
var sphereWidth = 120;
var sphereHeight = 120;
if (x >= sphereX && x <= sphereX + sphereWidth && y >= sphereY && y <= sphereY + sphereHeight) {
handleSphereSelection(sphere);
break;
}
}
} else if (gameState === 'playing') {
// Check if menu button was clicked (adjusted for bottom right anchor)
var menuButtonX = menuButton.x - 240; // Full scaled width (anchored to right)
var menuButtonY = menuButton.y - 120; // Full scaled height (anchored to bottom)
var menuButtonWidth = 240; // Scaled width
var menuButtonHeight = 120; // Scaled height
if (x >= menuButtonX && x <= menuButtonX + menuButtonWidth && y >= menuButtonY && y <= menuButtonY + menuButtonHeight) {
// Stop any music
LK.stopMusic();
// Hide menu button
menuButton.alpha = 0;
menuButtonText.alpha = 0;
// Hide all UI elements
scoreText.alpha = 0;
healthText.alpha = 0;
timerText.alpha = 0;
heightText.alpha = 0;
sphereCountText.alpha = 0;
// Destroy character if exists
if (character) {
character.destroy();
character = null;
}
// Clear all game objects
for (var i = goldenMemories.length - 1; i >= 0; i--) {
goldenMemories[i].destroy();
}
goldenMemories = [];
for (var i = corruptedMemories.length - 1; i >= 0; i--) {
corruptedMemories[i].destroy();
}
corruptedMemories = [];
for (var i = pipes.length - 1; i >= 0; i--) {
pipes[i].destroy();
}
pipes = [];
for (var i = sideSpikes.length - 1; i >= 0; i--) {
sideSpikes[i].destroy();
}
sideSpikes = [];
// Clean up bubble shooter
if (bubbleShooter) {
bubbleShooter.destroy();
bubbleShooter = null;
}
// Clean up snake game
if (game.snakeGame) {
game.snakeGame.destroy();
game.snakeGame = null;
}
// Hide missed shots counter
missedShotsText.alpha = 0;
// Clean up aim guide
for (var i = aimGuide.length - 1; i >= 0; i--) {
aimGuide[i].destroy();
}
aimGuide = [];
// Reset game variables
gameStarted = false;
timerActive = false;
speedIncreased = false;
// Show main menu elements
titleText.alpha = 1;
playButton.alpha = 1;
// Go to level selection
createLevelSelection();
}
if (currentLevel === 2) {
// Handle flapping in lvl2 - Flappy Bird style
if (character) {
birdVelocity = flapStrength;
}
} else if (currentLevel === 3 && bubbleShooter) {
// Level 3: Bubble shooting
if (bubbleShooter.currentBubble && bubbleShooter.canShoot) {
// Get fresh aim angle at moment of shooting
var cannonX = bubbleShooter.cannon.x;
var cannonY = bubbleShooter.cannon.y;
var freshAimAngle = Math.atan2(y - cannonY, x - cannonX);
// Limit aiming angle to upward directions only (allow wider range for better shooting)
if (freshAimAngle > -Math.PI * 0.9 && freshAimAngle < -Math.PI * 0.1) {
// Use shootBubble method for proper physics
bubbleShooter.shootBubble(freshAimAngle);
}
}
} else if (currentLevel === 4 && game.snakeGame) {
// Level 4: Snake Game mouse click control
// Convert click position to grid coordinates
var floorLeft = game.snakeGame.floor.x - 1800 / 2;
var floorTop = game.snakeGame.floor.y - 2400 / 2;
var targetGridX = Math.floor((x - floorLeft) / game.snakeGame.gridSize);
var targetGridY = Math.floor((y - floorTop) / game.snakeGame.gridSize);
// Ensure target is within grid bounds
if (targetGridX >= 0 && targetGridX < game.snakeGame.gridWidth && targetGridY >= 0 && targetGridY < game.snakeGame.gridHeight) {
var currentHead = game.snakeGame.snake[0];
var dx = targetGridX - currentHead.x;
var dy = targetGridY - currentHead.y;
// Determine movement direction based on click position
// Prioritize the direction with the larger distance
if (Math.abs(dx) >= Math.abs(dy) && dx !== 0) {
// Horizontal movement has priority
if (dx > 0 && game.snakeGame.direction !== 'left') {
game.snakeGame.nextDirection = 'right';
} else if (dx < 0 && game.snakeGame.direction !== 'right') {
game.snakeGame.nextDirection = 'left';
}
} else if (dy !== 0) {
// Vertical movement
if (dy > 0 && game.snakeGame.direction !== 'up') {
game.snakeGame.nextDirection = 'down';
} else if (dy < 0 && game.snakeGame.direction !== 'down') {
game.snakeGame.nextDirection = 'up';
}
}
}
}
}
};
game.up = function (x, y, obj) {
// No special handling needed for mouse up events
};
game.move = function (x, y, obj) {
if (gameStarted) {
if (currentLevel === 2 && character) {
// In lvl2, character stays in middle horizontal position for Flappy Bird
character.x = 1024;
// Y position is handled by gravity and flapping in update
} else if (currentLevel === 3 && bubbleShooter) {
// Level 3: Update aiming
if (bubbleShooter.cannon) {
var cannonX = bubbleShooter.cannon.x;
var cannonY = bubbleShooter.cannon.y;
aimAngle = Math.atan2(y - cannonY, x - cannonX);
// Limit aiming angle to upward directions only (allow wider range for better aiming)
if (aimAngle > -Math.PI * 0.9 && aimAngle < -Math.PI * 0.1) {
bubbleShooter.cannon.rotation = aimAngle + Math.PI / 2;
// Move current bubble to follow cannon aiming
if (bubbleShooter.currentBubble && bubbleShooter.canShoot) {
var aimDistance = 80; // Distance from cannon tip
bubbleShooter.currentBubble.x = cannonX + Math.cos(aimAngle) * aimDistance;
bubbleShooter.currentBubble.y = cannonY + Math.sin(aimAngle) * aimDistance;
}
}
}
} else if (character) {
// Original behavior for lvl1
var targetX = Math.max(60, Math.min(1988, x));
var targetY = Math.max(60, Math.min(2672, y));
character.x = targetX;
character.y = targetY;
}
}
};
// Start spawning spheres for character selection
// Main game update loop
game.update = function () {
// Handle different game states
if (gameState === 'menu') {
// Stop any playing music in menu
LK.stopMusic();
return; // No updates needed in menu
}
if (gameState === 'levelSelection') {
// Static level boxes, no updates needed
return;
}
if (gameState === 'characterSelection') {
// Static spheres, no updates needed
return;
}
if (gameState === 'countdown') {
return; // Countdown handles itself
}
if (gameState !== 'playing') {
return;
}
// Update sphere counter display
sphereCountText.setText('Esferas: ' + totalSpheres);
// Update timer
if (timerActive && LK.ticks % 60 === 0) {
// Update every second (60 ticks = 1 second at 60 FPS)
gameTimer--;
timerText.setText('Tiempo: ' + gameTimer);
// When timer reaches 0, increase speed
if (gameTimer <= 0 && !speedIncreased) {
speedIncreased = true;
timerActive = false;
// Speed increase will be handled in the speed calculation section below
}
}
// Game playing state - spawn spheres or spikes based on level
spawnTimer++;
if (currentLevel === 2) {
// Level 2: Flappy Bird mechanics - completely separate from other levels
// Spawn pipes
var pipeSpawnRate = 120; // Spawn pipe every 2 seconds
if (spawnTimer >= pipeSpawnRate) {
var gapY = Math.random() * (2000 - 700) + 700; // Random gap position between y 700-2000
// Create top pipe
var topPipe = new Pipe(true, gapY);
topPipe.x = 2100;
topPipe.y = gapY - pipeGap / 2;
topPipe.lastX = topPipe.x;
pipes.push(topPipe);
game.addChild(topPipe);
// Create bottom pipe
var bottomPipe = new Pipe(false, gapY);
bottomPipe.x = 2100;
bottomPipe.y = gapY + pipeGap / 2;
bottomPipe.lastX = bottomPipe.x;
pipes.push(bottomPipe);
game.addChild(bottomPipe);
spawnTimer = 0;
}
// Handle Flappy Bird physics
if (character) {
// Apply gravity
birdVelocity += gravity;
character.y += birdVelocity;
// Check boundaries - only bounce off top, die on bottom and left
if (character.y < 50) {
character.y = 50;
birdVelocity = 0;
}
// No win condition for minigame 2 - play until you lose
// Check death conditions: left edge or bottom
if (character.x <= 0 || character.y > 2680) {
// Hit left edge or ground - game over
attemptCount++;
storage.attemptCount = attemptCount;
// Track level-specific attempts
if (!levelAttempts[currentLevel]) {
levelAttempts[currentLevel] = 0;
}
levelAttempts[currentLevel]++;
storage.levelAttempts = levelAttempts;
// Convert distance to spheres (divide by 10)
var spheresEarned = Math.floor(distanceTraveled / 10);
totalSpheres += spheresEarned;
storage.totalSpheres = totalSpheres;
LK.showGameOver();
return;
}
// Update distance
distanceTraveled += 4; // Distance increases as pipes move
heightText.setText('Distancia: ' + Math.floor(distanceTraveled) + 'm');
}
// Update pipes and check collisions
for (var p = pipes.length - 1; p >= 0; p--) {
var pipe = pipes[p];
if (pipe.lastX === undefined) pipe.lastX = pipe.x;
var shouldRemovePipe = false;
// Check if off screen
if (pipe.lastX >= -pipeWidth && pipe.x < -pipeWidth) {
shouldRemovePipe = true;
}
// Check collision with character - bounce instead of game over
if (character && pipe.intersects(character)) {
// Bounce character away from pipe
if (pipe.isTop) {
// Hit top pipe - bounce down
character.y = pipe.y + pipe.height + 20;
birdVelocity = 5; // Push down
} else {
// Hit bottom pipe - bounce up
character.y = pipe.y - 20;
birdVelocity = -5; // Push up
}
// Flash character red to indicate collision
LK.effects.flashObject(character, 0xFF0000, 300);
}
// Check if passed pipe for scoring (only check bottom pipes to avoid double scoring)
if (!pipe.isTop && !pipe.scored && pipe.lastX >= character.x && pipe.x < character.x) {
pipe.scored = true;
scoreCounter++;
LK.setScore(scoreCounter);
scoreText.setText('Puntos: ' + LK.getScore());
LK.getSound('collect').play();
}
if (shouldRemovePipe) {
pipe.destroy();
pipes.splice(p, 1);
} else {
pipe.lastX = pipe.x;
}
}
// Level 2 specific updates only
return; // Skip level 1 logic for level 2
} else if (currentLevel === 3) {
// Level 3: Bubble Shooter logic
if (bubbleShooter) {
// Add new rows periodically
if (LK.ticks % 1800 === 0) {
// Every 30 seconds
bubbleShooter.addNewRowFromTop();
}
// No win condition - game continues indefinitely
// Bubble shooter physics will be handled in the class update method
}
return; // Skip other level logic
} else if (currentLevel === 4) {
// Level 4: Snake Game logic
if (game.snakeGame) {
// Snake game handles its own updates
// No additional spawning or logic needed
}
return; // Skip other level logic
} else if (currentLevel === 1) {
// Level 1 logic - completely separate from other levels
var spawnRate = Math.max(30, 90 - difficultyLevel * 5);
if (spawnTimer >= spawnRate) {
spawnMemory();
spawnTimer = 0;
}
}
// Increase difficulty over time
if (LK.getScore() > 0 && LK.getScore() % 10 === 0) {
difficultyLevel = Math.floor(LK.getScore() / 10) + 1;
}
// Spawn rate already handled at top of update function
// Only update memories for level 1
if (currentLevel === 1) {
// Update golden memories
for (var i = goldenMemories.length - 1; i >= 0; i--) {
var golden = goldenMemories[i];
// Initialize lastY if needed
if (golden.lastY === undefined) golden.lastY = golden.y;
var shouldRemove = false;
// Check if off screen
if (golden.lastY <= 2732 && golden.y > 2732) {
shouldRemove = true;
} else if (character && gameStarted && golden.intersects(character)) {
// Check collision with character for game phase
var spherePoints = Math.floor(3 * sphereMultiplier);
// Disgust character gets 2x spheres
if (selectedEmotion === 'disgust') {
spherePoints = Math.floor(6 * sphereMultiplier);
}
LK.setScore(LK.getScore() + spherePoints);
scoreText.setText('Puntos: ' + LK.getScore());
LK.getSound('collect').play();
// Flash character gold
LK.effects.flashObject(character, 0xFFD700, 300);
shouldRemove = true;
} else if (!gameStarted && !countdownActive && golden.characterType && golden.y > 500 && golden.y < 2000) {
// Check collision for character selection phase
handleSphereSelection(golden);
shouldRemove = true;
}
if (shouldRemove) {
golden.destroy();
goldenMemories.splice(i, 1);
} else {
golden.lastY = golden.y;
}
}
// Update corrupted memories
for (var j = corruptedMemories.length - 1; j >= 0; j--) {
var corrupted = corruptedMemories[j];
// Initialize lastY if needed
if (corrupted.lastY === undefined) corrupted.lastY = corrupted.y;
var shouldRemove = false;
// Check if off screen
if (corrupted.lastY <= 2732 && corrupted.y > 2732) {
shouldRemove = true;
} else if (character && corrupted.intersects(character)) {
// Check collision with character
var damage = speedIncreased ? 25 : 20;
health -= damage;
healthText.setText('Vida: ' + health + '/' + maxHealth);
LK.getSound('damage').play();
// Flash character red
LK.effects.flashObject(character, 0xFF0000, 500);
shouldRemove = true;
// Check game over
if (health <= 0) {
// Stop character from following cursor
if (character) {
tween.stop(character);
}
// Increment attempt counter
attemptCount++;
storage.attemptCount = attemptCount;
// Track level-specific attempts
if (!levelAttempts[currentLevel]) {
levelAttempts[currentLevel] = 0;
}
levelAttempts[currentLevel]++;
storage.levelAttempts = levelAttempts;
// Add accumulated points to total spheres
totalSpheres += LK.getScore();
storage.totalSpheres = totalSpheres;
// Show game over immediately
LK.showGameOver();
return;
}
}
if (shouldRemove) {
corrupted.destroy();
corruptedMemories.splice(j, 1);
} else {
corrupted.lastY = corrupted.y;
}
}
}
// Calculate speed only for level 1
if (currentLevel === 1) {
var goldenBaseSpeed = (3 + difficultyLevel * 0.5) * speedMultiplier;
var corruptedBaseSpeed = (4 + difficultyLevel * 0.5) * speedMultiplier;
if (speedIncreased) {
goldenBaseSpeed += goldenBaseSpeed * 0.5;
corruptedBaseSpeed += corruptedBaseSpeed * 0.5;
}
// Double speed for lvl1
goldenBaseSpeed *= 2;
corruptedBaseSpeed *= 2;
// Apply speed boost multiplier if active
if (speedBoostActive) {
goldenBaseSpeed *= 2;
corruptedBaseSpeed *= 2;
}
// Apply calculated speeds
for (var k = 0; k < goldenMemories.length; k++) {
goldenMemories[k].speed = goldenBaseSpeed;
}
for (var l = 0; l < corruptedMemories.length; l++) {
corruptedMemories[l].speed = corruptedBaseSpeed;
}
}
// Win condition removed - game continues indefinitely
// Win condition removed - game continues indefinitely
}; ===================================================================
--- original.js
+++ change.js
@@ -2198,18 +2198,18 @@
var currentHead = game.snakeGame.snake[0];
var dx = targetGridX - currentHead.x;
var dy = targetGridY - currentHead.y;
// Determine movement direction based on click position
- // 1. Attempt horizontal movement first (if distance X is greater or equal to Y)
+ // Prioritize the direction with the larger distance
if (Math.abs(dx) >= Math.abs(dy) && dx !== 0) {
+ // Horizontal movement has priority
if (dx > 0 && game.snakeGame.direction !== 'left') {
game.snakeGame.nextDirection = 'right';
} else if (dx < 0 && game.snakeGame.direction !== 'right') {
game.snakeGame.nextDirection = 'left';
}
- }
- // 2. Attempt vertical movement (if horizontal movement was not valid or distance Y is greater)
- else if (dy !== 0) {
+ } else if (dy !== 0) {
+ // Vertical movement
if (dy > 0 && game.snakeGame.direction !== 'up') {
game.snakeGame.nextDirection = 'down';
} else if (dy < 0 && game.snakeGame.direction !== 'down') {
game.snakeGame.nextDirection = 'up';