User prompt
š ļø CorreccioĢn: Cobertura COMPLETA de Burbujas Iniciales en el Techo (Nivel 3) Problema Actual (como se ve en la imagen): Las burbujas generadas por self.createInitialBubbles() estaĢn agrupadas en el centro superior de la pantalla, dejando grandes espacios vaciĢos a los lados. Necesitan cubrir todo el ancho de la parte superior, desde el borde izquierdo al derecho. InstruccioĢn para la IA: Ajustar la FuncioĢn self.createInitialBubbles() para Cobertura Total: Re-calcular self.gridCols y self.bubbleRadius: Es fundamental que el self.gridCols (nuĢmero de columnas) en combinacioĢn con el self.bubbleRadius (radio de la burbuja) permitan que la grilla hexagonal se extienda completamente a lo largo de los 2048px de ancho de la pantalla. Si self.bubbleRadius es 65 (diaĢmetro 130), y self.gridCols es 8, entonces el ancho total de 8 burbujas es 8 * 130 = 1040px, maĢs el offset hexagonal. Esto es claramente insuficiente para 2048px. AccioĢn Requerida: Aumenta self.gridCols (por ejemplo, a 16 columnas o el valor que sea necesario) y/o reduce self.bubbleRadius para que el ancho total de la grilla hexagonal cubra los 2048px. Calcula el bubbleRadius necesario para gridCols = 16 para cubrir los 2048px. Por ejemplo, 2048 / (16 * 2) = 64px si es un patroĢn de cuadrados, pero para hexaĢgonos es maĢs complejo. Ajustar self.getHexPosition() para Centrado Correcto: Una vez que self.gridCols y self.bubbleRadius esteĢn optimizados para el ancho, ajusta self.getHexPosition(row, col) para centrar horizontalmente toda la grilla. Actualmente, var x = self.bubbleRadius + col * (self.bubbleRadius * 2) + offsetX; no tiene un offset de centrado para el ancho total. AccioĢn Requerida: Calcula el ancho total real de la grilla hexagonal y resta la mitad de ese ancho al valor x para centrarla en 2048 / 2 = 1024px. Bucle de GeneracioĢn: El bucle for (var col = 0; col < self.gridCols - row % 2; col++) es correcto para la forma hexagonal, pero debe aplicarse al nuevo y mayor self.gridCols para garantizar que la generacioĢn se extienda por toda la pantalla. En Resumen: La IA debe realizar un recaĢlculo de self.gridCols y self.bubbleRadius para llenar 2048px, y luego ajustar el x inicial en self.getHexPosition para centrar esa grilla extendida.
User prompt
š ļø Modificaciones Urgentes en la Pantalla de la Tienda de Mejoras 1. EliminacioĢn de la CondicioĢn de Victoria/LoĢgica de Ganar CRIĢTICO: La tienda debe ser una pausa temporal, no una condicioĢn de fin de juego o victoria. Es probable que un self.checkWin() o una funcioĢn de game over se esteĢ ejecutando al entrar a la tienda. InstruccioĢn para la IA: Desactiva o Elimina cualquier LoĢgica de Fin de Juego al entrar a la Tienda: AseguĢrate de que, cuando el gameState se establezca en 'shop', no se ejecute ninguna funcioĢn de chequeo de victoria (self.checkWin()) o de derrota (self.checkLose()). La tienda es una pausa. La uĢnica forma de salir de esta pantalla debe ser a traveĢs de los botones 'SALIR' o 'SALTAR'. 2. Ajustes de Interfaz de Usuario (UI) Necesitas corregir la posicioĢn y el texto de los botones. InstruccioĢn para la IA: Ajustar la PosicioĢn y el Texto de los Botones Inferiores: Cambiar Texto del BotoĢn: Modifica el texto del botoĢn 'CONTINUAR' para que ahora diga 'SALIR'. Bajar PosicioĢn: Mueve los dos botones inferiores ('SALIR' y 'SALTAR') hacia abajo, aproximadamente 150 a 200 piĢxeles en el eje 'Y' para que no esteĢn tan cerca del texto del temporizador. Ejemplo de ajuste de coordenadas (asumiendo que estaĢn alrededor de y=1800): botonSalir.y = 2000; botonSaltar.y = 2150; (Aumentar el valor de 'y' en 200 piĢxeles). Con estos cambios, la Tienda de Mejoras funcionaraĢ correctamente como una pausa sin causar errores de fin de juego, y su interfaz seraĢ maĢs clara.
User prompt
š ļø Instrucciones Urgentes para el Minijuego 3 (Bubble Shooter) 1. CorreccioĢn: Cobertura de la Grilla Inicial (Burbujas en el Techo) La grilla inicial estaĢ mal posicionada y no estaĢ cubriendo todo el ancho de la pantalla (2048px), como se ve en la imagen proporcionada. Problema: La loĢgica de coordenadas y la iteracioĢn por columna (col) no se ajustan correctamente a la grilla hexagonal de tu pantalla de 2048px. InstruccioĢn para la IA: Modificar la funcioĢn self.createInitialBubbles(): Asegurar el Centrado: Revisa la funcioĢn self.getHexPosition(row, col) y ajusta el self.bubbleRadius para que el espaciado hexagonal (self.bubbleRadius * 2 para columnas) permita que la grilla de 8 columnas cubra los 2048px. Puede que necesites reducir el bubbleRadius o ajustar un offset inicial. Corregir la IteracioĢn: AseguĢrate de que el bucle de la columna (col) itere correctamente sobre el nuĢmero maĢximo de columnas permitido para esa fila (8 columnas para filas pares, 7 para impares). JavaScript // COĢDIGO SUGERIDO PARA AJUSTAR LA ITERACIOĢN EN createInitialBubbles // (Basado en tu coĢdigo de 8 columnas) self.createInitialBubbles = function () { for (var row = 0; row < 6; row++) { // La grilla hexagonal tiene un patroĢn de N y N-1 columnas (ej. 8, 7, 8, 7...) var colsInRow = self.gridCols - (row % 2); // 8 o 7 columnas for (var col = 0; col < colsInRow; col++) { // ... (El resto de la loĢgica de creacioĢn de burbujas permanece igual) if (Math.random() < 0.8) { // ... } } } }; 2. CorreccioĢn: Sistema de PresioĢn (Movimiento Constante de la Grilla) Las burbujas iniciales no se mueven porque solo tienes implementado el movimiento por fallos (self.moveGridDown()). Para que la grilla "escale hacia abajo" constantemente, necesitas aplicar un sistema de presioĢn basado en el tiempo. InstruccioĢn para la IA: Implementar Descenso por Tiempo: AnĢadir un temporizador de presioĢn: Dentro de la clase BubbleShooter, inicializa una variable de tiempo, por ejemplo, self.pressureTimer = 0;. Actualizar en cada fotograma: En la funcioĢn self.update() del BubbleShooter, incrementa este temporizador. Ejecutar el descenso: Cuando el temporizador alcance un valor predefinido (ej. 5 segundos, o 300 fotogramas), llama a la funcioĢn self.moveGridDown() para mover la grilla y reinicia el temporizador. JavaScript // LOĢGICA A ANĢADIR DENTRO DE self.update() en BubbleShooter // (DespueĢs de toda la loĢgica de la burbuja que se estaĢ disparando) // 1. Aumentar el temporizador de presioĢn self.pressureTimer++; // 2. Definir el intervalo (ej. cada 5 segundos a 60 FPS seriĢa 300) var pressureInterval = 300; // Ajusta este valor para maĢs o menos presioĢn if (self.pressureTimer >= pressureInterval) { self.moveGridDown(); // Llama a la funcioĢn que baja toda la grilla self.pressureTimer = 0; // Reiniciar el temporizador // **IMPORTANTE:** DespueĢs de bajar la grilla, verifica inmediatamente si el jugador // ha perdido al cruzar la liĢnea roja, llamando a self.checkLose(). self.checkLose(); } ConclusioĢn: Con estos dos cambios, la grilla se generaraĢ cubriendo todo el techo y bajaraĢ constantemente hasta tocar la franja roja (dangerLine), momento en el que self.checkLose() detectaraĢ el fin del juego.
User prompt
intentalo otra ves , as lo que puedas siempre y cuando funcione
User prompt
as que funcione el juego 3 , modificalo asta que cumpla su funciones ,
User prompt
Please fix the bug: 'TypeError: Cannot read properties of undefined (reading '4')' in or related to this line: 'if (visited[row][col] || !self.bubbleGrid[row][col] || self.bubbleGrid[row][col].colorType !== color) {' Line Number: 198
User prompt
crea una linea invisible en el fondo de la pantalla , con ella indica a las esferas que se generan arriba que pueden abansar asta alla , siempre y cuando se ballan generando otras para aser que escale acia abajo
User prompt
busca y elimina la parte de el codigo de el minijuego 3 que ase que las eseras que lanzo al colicionar se pausa el juego , elimina ese bug , y luego optimisa el juego para que valla mas fluido
User prompt
Please fix the bug: 'TypeError: Cannot read properties of undefined (reading '6')' in or related to this line: 'if (visited[row][col] || !self.bubbleGrid[row][col] || self.bubbleGrid[row][col].colorType !== color) {' Line Number: 189
User prompt
busca y elimina la parte de el codigo que no deja que las esferas de el minijuego 3 avansen acia abajo ,arreglalo asiendo que puedan moverse asta el infinito , pero si llegan a tocar la franja roja pierdes
User prompt
identifica la parte de el codigo para que las esferas no se superpongan entre si , y as que solo se visualise la parte con las esferas de el techo y lo demas de abajo de ella , ademas de el canĢon y las esferas que tira y el boton de menu , solo en el nivel 3
User prompt
Ā”Excelente! Para que Upit IA entienda exactamente lo que necesita cambiar, aquiĢ estaĢ el texto que resume los problemas y proporciona la solucioĢn directamente, listo para que lo pegues o le digas que reemplace tu coĢdigo. SolucioĢn a Problemas de Bubble Shooter (Minijuego 3) He identificado tres problemas principales en la clase BubbleShooter de tu juego: Esferas Fantasma/Atravesables (P1): La deteccioĢn de colisioĢn era demasiado estricta. Esferas que Rellenan Espacios Lejanos (P2): La loĢgica de fijacioĢn buscaba la posicioĢn vaciĢa maĢs cercana en toda la grilla, no solo cerca del punto de impacto. Esferas que Aparecen en la Esquina Superior Izquierda (P3): El objeto cannon (canĢoĢn) no estaba inicializado con una posicioĢn dentro del contenedor BubbleShooter. La solucioĢn se enfoca en inicializar el canĢoĢn y ajustar la loĢgica de acoplamiento y la distancia de colisioĢn. Archivo de Clase Reemplazado InstruccioĢn para Upit: Por favor, reemplaza completamente la clase BubbleShooter y solo la clase BubbleShooter con el siguiente coĢdigo. Instrucciones Finales Estos cambios resuelven los problemas de lanzamiento en la esquina y el ghosting al hacer que la colisioĢn sea maĢs precisa. El problema de que las esferas rellenan el espacio vaciĢo con las esferas que lanzo (Problema 2) es un problema de disenĢo de loĢgica (findClosestGridPosition itera sobre todos los espacios vaciĢos). Con esta correccioĢn, el efecto seraĢ mucho menos visible. Si todaviĢa quieres que solo se pegue a un espacio vecino de la burbuja colisionada, la loĢgica de findClosestGridPosition tendriĢa que ser reescrita por completo para buscar solo entre los vecinos de la burbuja colisionada.
User prompt
busca y elimina la parte de el codigo que esta impidiendo abansar y espandirse por todo el techo de el minijuego 3 a las esferas de el minijuejo 3 ,
User prompt
arregla las coliciones a 100 x 100
User prompt
arregla las coliciones , as que las coliciones de las esferas de el minijuego 3 sean mas acorde con sus imagenes de 200 X 200
User prompt
un poco mas abajo y que las esferas empiesen mas arriva no tan abajo āŖš” Consider importing and using the following plugins: @upit/tween.v1
User prompt
la franja roja tiene que estar masomenos en el medio un poco para abajo āŖš” Consider importing and using the following plugins: @upit/tween.v1
User prompt
mejor intenta que las esferas puedan abansar asta abajo de todo , osea el borde de abajo , cuando la franja roja colicione con las esferas que se generan en el techo que salte el cartel de que perdiste .
User prompt
as que las esferas que se generan en el techo de el minijuego 3 puedan llegar asta la franja roja āŖš” Consider importing and using the following plugins: @upit/tween.v1
User prompt
ok intenta con el primer bug que te mencione ese de que se generan esferas solo en el lado izquierdo arriva mientras que tendria que ser en todo el techo
User prompt
as que la esfera que cambia de color sea una variante de las otras esferas , osea que sea como una de las esferas que ya estan pero que cambie de aspecto Ejemplo : es una esfera azul que cambia de imagen con la esfera roja amarilla verde y asi āŖš” Consider importing and using the following plugins: @upit/tween.v1
User prompt
elimina la cosa que ase que gane en el minijuego 3 , que no se pueda ganar alenos que todas las esferas de el techo sean eliminadas
User prompt
, no me entendiste vien , que esas esferas que cambian de color sean como una esfera que se me genere solo a mi , no a las de el techo , y suve la franja roja un poquito mas āŖš” Consider importing and using the following plugins: @upit/tween.v1
User prompt
agrega una franja roja serca de el vorde de abajo , como un delimitante para las esferas de arriva , si las esferas de arriva tocan la franja roja pierdes , y que no se pueda ganar asta que no hayga esferas arriva , agrega una esfera que sea como una avilidad , que aparesca en un 10% de probavilidad de aparecer , y que esa esfera sea un color cambiante , cambia de color , como si fuera un arcoiris en una esfera , que esa esfera pueda aser que todas las esferas que colicionen contra ella se caiga , pero no las que no coliciono contra ella āŖš” Consider importing and using the following plugins: @upit/tween.v1
User prompt
agranda las esferas para que sean mas grandes , no las agas enormes
/****
* 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 = 8; // Further reduce columns to properly fit 2048px screen
self.bubbleRadius = 100; // Match bubble image size (200/2 = 100) for accurate collisions
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;
// Initialize grid
self.initGrid = function () {
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
};
// Get hexagonal position
self.getHexPosition = function (row, col) {
var offsetX = row % 2 * self.bubbleRadius;
var x = self.bubbleRadius + col * (self.bubbleRadius * 2) + offsetX;
var y = self.startY + row * (self.bubbleRadius * 1.7); // Slightly tighter vertical spacing
return {
x: x,
y: y
};
};
// Create initial bubble layout
self.createInitialBubbles = function () {
for (var row = 0; row < self.gridRows; row++) {
// Use all available rows to allow bubbles to reach bottom
for (var col = 0; col < self.gridCols; col++) {
if (Math.random() < 0.85) {
// Increased probability from 0.8 to 0.85 for denser 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.gridRows && newCol >= 0 && newCol < self.gridCols - newRow % 2) {
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
for (var r = 0; r < self.gridRows; r++) {
visited[r] = [];
for (var c = 0; c < self.gridCols; c++) {
visited[r][c] = false;
}
}
// Depth-first search
function dfs(row, col) {
if (visited[row][col] || !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.gridRows; 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.gridRows || col < 0 || col >= self.gridCols || !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.gridRows; row++) {
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)
self.moveGridDown = function () {
// Move all 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][col] && row < self.gridRows - 1) {
// 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);
self.currentBubble.x = 1024;
self.currentBubble.y = 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);
self.nextBubble.x = 1024 + 100;
self.nextBubble.y = 2500;
self.nextBubble.alpha = 0.7;
self.addChild(self.nextBubble);
}
};
// Check win condition
self.checkWin = function () {
for (var row = 0; row < self.gridRows; row++) {
for (var col = 0; col < self.gridCols; col++) {
if (self.bubbleGrid[row][col]) {
return false;
}
}
}
return true;
};
// 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.7) {
// 70% chance to place bubble
// 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
bullet.x = self.cannon.x;
bullet.y = self.cannon.y;
// 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 = 1024;
self.currentBubble.y = 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 = 1024 + 100;
self.nextBubble.y = 2500;
self.nextBubble.alpha = 0.7;
self.addChild(self.nextBubble);
}
};
// Check collision between shooting bubble and grid
self.checkCollision = function (shootingBubble) {
for (var row = 0; row < self.gridRows; 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 distance = Math.sqrt(dx * dx + dy * dy);
// Increase collision distance to better match 200x200 bubble images
if (distance <= 160) {
// Approximately matches the visual size of bubbles
return {
row: row,
col: col,
bubble: gridBubble
};
}
}
}
}
return null;
};
// Find closest grid position for bubble attachment
self.findClosestGridPosition = function (x, y) {
var minDist = 999999;
var targetRow = -1;
var targetCol = -1;
for (var row = 0; row < self.gridRows; row++) {
for (var col = 0; col < self.gridCols; col++) {
if (!self.bubbleGrid[row][col]) {
var pos = self.getHexPosition(row, col);
var dx = x - pos.x;
var dy = y - pos.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist < minDist) {
minDist = dist;
targetRow = row;
targetCol = col;
}
}
}
}
return {
row: targetRow,
col: targetCol
};
};
// Check lose condition
self.checkLose = function () {
// Check if danger line collides with any bubble
for (var row = 0; row < self.gridRows; row++) {
for (var col = 0; col < self.gridCols; col++) {
var bubble = self.bubbleGrid[row][col];
if (bubble && bubble.intersects(self.dangerLine)) {
return true;
}
}
}
return false;
};
// Update method to handle shooting physics
self.update = function () {
// 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;
var shouldAttach = collision || hitTop;
if (shouldAttach) {
// Stop bullet movement immediately
bullet.velocityX = 0;
bullet.velocityY = 0;
// Find closest empty grid position
var closestPos = self.findClosestGridPosition(bullet.x, bullet.y);
if (closestPos.row >= 0 && closestPos.col >= 0) {
// 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;
}
}
// Clear shooting bubble
self.shootingBubble = null;
// Re-enable shooting
self.canShoot = true;
// Create new bubble for next shot
self.createNewBubble();
} else {
// No valid position found - remove bullet
bullet.destroy();
self.missedShots++;
if (self.missedShots >= self.maxMissedShots) {
self.moveGridDown();
self.missedShots = 0;
}
// Clear shooting bubble
self.shootingBubble = null;
// Re-enable shooting
self.canShoot = true;
// Create new bubble for next shot
self.createNewBubble();
}
// Update missed shots display
missedShotsText.setText('Fallos: ' + self.missedShots + '/' + self.maxMissedShots);
// Win condition removed - game continues until all bubbles eliminated
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;
});
/****
* 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: 80,
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: 60,
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: 35,
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: 30,
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: 30,
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;
}
// Continue button
var continueBtn = new Text2('CONTINUAR', {
size: 60,
fill: 0x00FF00
});
continueBtn.anchor.set(0.5, 0.5);
continueBtn.x = 1024;
continueBtn.y = 1200;
game.addChild(continueBtn);
shopItems.push(continueBtn);
continueBtn.isContinueBtn = true;
// Skip button
var skipBtn = new Text2('SALTAR', {
size: 60,
fill: 0x00FF00
});
skipBtn.anchor.set(0.5, 0.5);
skipBtn.x = 1024;
skipBtn.y = 1300;
game.addChild(skipBtn);
shopItems.push(skipBtn);
skipBtn.isSkipBtn = true;
// Shop timer display
shopTimerText = new Text2('Tiempo: 5', {
size: 60,
fill: 0xFF0000
});
shopTimerText.anchor.set(0.5, 0.5);
shopTimerText.x = 1024;
shopTimerText.y = 1000;
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 {
// 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();
bubbleShooter.createNewBubble();
game.addChild(bubbleShooter);
// Create cannon
var cannon = LK.getAsset('cannon', {
anchorX: 0.5,
anchorY: 1
});
cannon.x = 1024;
cannon.y = 2600;
bubbleShooter.cannon = cannon;
game.addChild(cannon);
} 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 {
startCharacterSelection();
}
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;
}
// 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);
}
}
}
}
};
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();
}
// Bubble shooter physics will be handled in the class update method
}
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;
}
}
// Check win condition (500 spheres)
if (totalSpheres >= 500) {
// Create large green "GANASTE" text
var winText = new Text2('GANASTE', {
size: 200,
fill: 0x00FF00
});
winText.anchor.set(0.5, 0.5);
winText.x = 1024;
winText.y = 1366;
game.addChild(winText);
// Stop character from following cursor
if (character) {
tween.stop(character);
}
// All levels are unlocked by default, no need to unlock
// Reset accumulated spheres to 0 when winning
totalSpheres = 0;
storage.totalSpheres = 0;
// Reset owned characters to default when winning
ownedCharacters = ['joy'];
storage.ownedCharacters = ownedCharacters;
// Show you win after displaying text
LK.setTimeout(function () {
LK.showYouWin();
}, 3000);
}
// Check win condition (score of 5000)
if (LK.getScore() >= 5000) {
// Stop character from following cursor
if (character) {
tween.stop(character);
}
// All levels are unlocked by default, no need to unlock
// 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;
// Reset points to 0 after winning
LK.setScore(0);
scoreText.setText('Puntos: 0');
// Also reset owned characters when winning with high score
ownedCharacters = ['joy'];
storage.ownedCharacters = ownedCharacters;
LK.showYouWin();
}
}; ===================================================================
--- original.js
+++ change.js
@@ -58,9 +58,9 @@
// Game grid - hexagonal arrangement
self.bubbleGrid = [];
self.gridRows = 12;
self.gridCols = 8; // Further reduce columns to properly fit 2048px screen
- self.bubbleRadius = 85; // Match bubble image size (200/2 = 100, but use 85 for better spacing)
+ self.bubbleRadius = 100; // Match bubble image size (200/2 = 100) for accurate collisions
self.startY = 20; // Start bubbles higher up
// Shooting mechanics
self.cannon = null;
self.currentBubble = null;
@@ -451,9 +451,11 @@
if (gridBubble) {
var dx = shootingBubble.x - gridBubble.x;
var dy = shootingBubble.y - gridBubble.y;
var distance = Math.sqrt(dx * dx + dy * dy);
- if (distance <= self.bubbleRadius * 1.8) {
+ // Increase collision distance to better match 200x200 bubble images
+ if (distance <= 160) {
+ // Approximately matches the visual size of bubbles
return {
row: row,
col: col,
bubble: gridBubble
@@ -689,24 +691,24 @@
/****
* Game Code
****/
-// All previously requested features have been successfully implemented:
-// - Invisible hitboxes for JUGAR button and character selection
-// - Jumpscare with 22.2% probability on game over
-// - 223-second countdown timer with speed increase
-// - Character pricing and storage system
-// - Points accumulation (15 per sphere)
-// - Win condition at 50000 points
-// - Spike damage of 25 after timer expires
-// If you need additional features, please specify what you'd like to add
-// Character assets - Joy, Anger, Disgust, Fear
-// Memory sphere assets
-// Sound effects
-// - Insufficient spheres error message ('No tienes esferas suficientes')
-// - Character reset every 5 attempts except free characters (attemptCount % 5 === 0)
-// - Jumpscare probability: 25% (line with Math.random() < 0.25)
// 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;