User prompt
Undo the lava waves
User prompt
Make the waves on the lava bigger
User prompt
Make the lava have waves and move
User prompt
Make the score count in the center of screen
User prompt
Make the ball bounce in a random direction when it hits the paddle
User prompt
Make it so when the ball touches the paddle particles come out of the paddle
User prompt
make the ball bounce higher when it hits the paddle ↪💡 Consider importing and using the following plugins: @upit/tween.v1
Code edit (1 edits merged)
Please save this source code
User prompt
Lava Bounce
Initial prompt
hey, can you make a game where there is a ball and a paddle and the ball falls down and if it hits the paddle the ball bounces up and you get one point and there lava at the bottom of the screen and if the ball touches the lava your points get reset
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1", { highScore: 0, volume: 1, musicVolume: 1, sfxVolume: 1 }); /**** * Classes ****/ var Ball = Container.expand(function () { var self = Container.call(this); var ballGraphics = self.attachAsset('ball', { anchorX: 0.5, anchorY: 0.5 }); self.width = ballGraphics.width; self.height = ballGraphics.height; self.speedY = 3; self.speedX = 0; self.gravity = 0.1; self.maxSpeed = 10; self.reset = function () { self.x = 2048 / 2; self.y = 500; self.speedY = 3; self.speedX = Math.random() * 4 - 2; }; self.update = function () { // Apply gravity self.speedY += self.gravity; // Cap max speed if (self.speedY > self.maxSpeed) { self.speedY = self.maxSpeed; } // Update position self.x += self.speedX; self.y += self.speedY; // Bounce off walls if (self.x < self.width / 2) { self.x = self.width / 2; self.speedX = -self.speedX; // Play wall hit sound var masterVolume = storage.volume !== undefined ? storage.volume : 1.0; var sfxVolume = storage.sfxVolume !== undefined ? storage.sfxVolume : 1.0; LK.getSound('bounce').play({ volume: masterVolume * sfxVolume * 0.7 }); // Emit wall hit particles if (self.onWallHit) { self.onWallHit(self.x, self.y, 'left'); } } else if (self.x > 2048 - self.width / 2) { self.x = 2048 - self.width / 2; self.speedX = -self.speedX; // Play wall hit sound var masterVolume = storage.volume !== undefined ? storage.volume : 1.0; var sfxVolume = storage.sfxVolume !== undefined ? storage.sfxVolume : 1.0; LK.getSound('bounce').play({ volume: masterVolume * sfxVolume * 0.7 }); // Emit wall hit particles if (self.onWallHit) { self.onWallHit(self.x, self.y, 'right'); } } }; return self; }); var Confetti = Container.expand(function () { var self = Container.call(this); self.particles = []; self.colors = [0xFF0000, 0x00FF00, 0x0000FF, 0xFFFF00, 0xFF00FF, 0x00FFFF, 0xFFFFFF]; self.emit = function (x, y, count) { for (var i = 0; i < count; i++) { var particle = new Container(); var size = Math.random() * 20 + 10; var color = self.colors[Math.floor(Math.random() * self.colors.length)]; var particleGraphic = particle.attachAsset('ball', { anchorX: 0.5, anchorY: 0.5, scaleX: size / 40, scaleY: size / 40 }); particleGraphic.tint = color; particle.x = x; particle.y = y; particle.speedX = Math.random() * 20 - 10; particle.speedY = -Math.random() * 15 - 5; particle.rotationSpeed = (Math.random() - 0.5) * 0.2; particle.life = 60 + Math.random() * 60; particle.maxLife = particle.life; self.addChild(particle); self.particles.push(particle); } }; self.update = function () { for (var i = self.particles.length - 1; i >= 0; i--) { var particle = self.particles[i]; particle.x += particle.speedX; particle.y += particle.speedY; particle.speedY += 0.2; // Gravity particle.rotation += particle.rotationSpeed; particle.life--; particle.alpha = particle.life / particle.maxLife; if (particle.life <= 0) { self.removeChild(particle); self.particles.splice(i, 1); } } }; return self; }); var ConfirmDialog = Container.expand(function () { var self = Container.call(this); // Create semi-transparent background var bg = new Container(); var bgAsset = bg.attachAsset('lava', { anchorX: 0.5, anchorY: 0.5, scaleX: 3, scaleY: 2 }); bgAsset.alpha = 0.8; bgAsset.tint = 0x000000; self.addChild(bg); // Create message text var messageText = new Text2('Are you sure you want\nto reset your high score?', { size: 80, fill: 0xFFFFFF }); messageText.anchor.set(0.5, 0.5); messageText.y = -100; self.addChild(messageText); // Create yes button var yesButton = new Container(); var yesText = new Text2('YES', { size: 80, fill: 0xFF0000 }); yesText.anchor.set(0.5, 0.5); yesButton.addChild(yesText); yesButton.x = -150; yesButton.y = 100; yesButton.interactive = true; self.addChild(yesButton); // Create no button var noButton = new Container(); var noText = new Text2('NO', { size: 80, fill: 0xFFFFFF }); noText.anchor.set(0.5, 0.5); noButton.addChild(noText); noButton.x = 150; noButton.y = 100; noButton.interactive = true; self.addChild(noButton); // Button press handlers yesButton.down = function () { tween(this, { scaleX: 0.9, scaleY: 0.9 }, { duration: 100 }); }; yesButton.up = function () { var btn = this; tween(btn, { scaleX: 1, scaleY: 1 }, { duration: 200, easing: tween.elasticOut, onFinish: function onFinish() { if (self.onConfirm) { self.onConfirm(); } } }); }; noButton.down = function () { tween(this, { scaleX: 0.9, scaleY: 0.9 }, { duration: 100 }); }; noButton.up = function () { var btn = this; tween(btn, { scaleX: 1, scaleY: 1 }, { duration: 200, easing: tween.elasticOut, onFinish: function onFinish() { if (self.onCancel) { self.onCancel(); } } }); }; return self; }); var Lava = Container.expand(function () { var self = Container.call(this); var lavaGraphics = self.attachAsset('lava', { anchorX: 0.5, anchorY: 0.5 }); // Set initial wave properties self.waveOffset = Math.random() * Math.PI * 2; // Random starting position for wave self.baseY = 0; // Will store the initial y position self.rising = false; // Track if lava is rising or falling self.moveRange = 300; // How far the lava will move up and down self.moveSpeed = 5000; // Duration of rise/fall movement in ms // The paddle reference will be set when the lava is added to the game // Start the rise and fall cycle self.startRiseFall = function () { // Make the lava rise a little bit from its base position tween(self, { y: self.baseY - 100 }, { duration: 2000, easing: tween.easeInOut, onFinish: function onFinish() { tween(self, { y: self.baseY }, { duration: 2000, easing: tween.easeInOut, onFinish: self.startRiseFall }); } }); }; // Create a pulsing effect for the lava self.pulse = function () { tween(lavaGraphics, { alpha: 0.7 }, { duration: 800, easing: tween.easeInOut, onFinish: function onFinish() { tween(lavaGraphics, { alpha: 1 }, { duration: 800, easing: tween.easeInOut, onFinish: self.pulse }); } }); }; // Create animated wave effect self.update = function () { // Create a wave effect based on time var waveTime = LK.ticks / 30; // Adjust speed of wave var waveScale = Math.sin(waveTime) * 0.05 + 1; // Scale between 0.95 and 1.05 // Apply different wave scales to left and right sides var leftWaveScale = Math.sin(waveTime + 1) * 0.05 + 1; var rightWaveScale = Math.sin(waveTime + 2) * 0.05 + 1; // Apply the scale distortion to create wave effect lavaGraphics.scaleX = waveScale; // Small vertical oscillation to add to the tween movement var oscillation = Math.sin(waveTime * 0.5) * 0.5; // The y position is controlled by the tween, we just add small oscillation // (Not setting self.y directly here since tweens are handling the major movement) }; self.pulse(); return self; }); var MouseParticleSystem = Container.expand(function () { var self = Container.call(this); self.particles = []; self.maxParticles = 100; self.createParticle = function (x, y) { var particle = new Container(); var particleGraphic = particle.attachAsset('ball', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.4, scaleY: 0.4 }); // Use bright colors for better visibility var colors = [0xFFFF00, 0xFF00FF, 0x00FFFF, 0xFF5500, 0x88FF00]; particleGraphic.tint = colors[Math.floor(Math.random() * colors.length)]; particle.x = x; particle.y = y; particle.alpha = 1; particle.speedX = Math.random() * 12 - 6; particle.speedY = Math.random() * 12 - 6; particle.life = 30 + Math.random() * 30; particle.maxLife = particle.life; self.addChild(particle); self.particles.push(particle); return particle; }; self.update = function () { for (var i = self.particles.length - 1; i >= 0; i--) { var particle = self.particles[i]; particle.x += particle.speedX; particle.y += particle.speedY; particle.speedX *= 0.96; particle.speedY *= 0.96; particle.life--; particle.alpha = particle.life / particle.maxLife; // Smoother size transition with tween-like effect particle.scaleX = particle.life / particle.maxLife * 0.4 + 0.1; particle.scaleY = particle.life / particle.maxLife * 0.4 + 0.1; // Add rotation for more visual interest particle.rotation += 0.05; if (particle.life <= 0) { self.removeChild(particle); self.particles.splice(i, 1); } } }; self.emitAt = function (x, y, count) { // Create more particles for better visual effect for (var i = 0; i < count; i++) { // Slightly offset each particle for a more natural burst effect var offsetX = x + (Math.random() * 20 - 10); var offsetY = y + (Math.random() * 20 - 10); self.createParticle(offsetX, offsetY); } }; return self; }); var Paddle = Container.expand(function () { var self = Container.call(this); // Create left rounded edge var paddleLeftGraphics = self.attachAsset('paddleLeft', { anchorX: 1.0, anchorY: 0.5, x: -170 }); // Create middle section var paddleMiddleGraphics = self.attachAsset('paddleMiddle', { anchorX: 0.5, anchorY: 0.5 }); // Create right rounded edge var paddleRightGraphics = self.attachAsset('paddleRight', { anchorX: 0.0, anchorY: 0.5, x: 170 }); // Adjust positions to eliminate gaps // The middle section's width is 340, and each end cap is 30 // We need to ensure they connect perfectly paddleLeftGraphics.x = -170 + 15; // Move right by half of end cap width paddleRightGraphics.x = 170 - 15; // Move left by half of end cap width // Set the overall width based on the combined elements self.width = 400; // total width (30 + 340 + 30) self.height = 30; return self; }); var ParticleSystem = Container.expand(function () { var self = Container.call(this); self.particles = []; self.maxParticles = 20; self.createParticle = function (x, y, color) { var particle = new Container(); var particleGraphic = particle.attachAsset('ball', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.3, scaleY: 0.3 }); particleGraphic.tint = color; particle.x = x; particle.y = y; particle.alpha = 1; particle.speedX = Math.random() * 10 - 5; particle.speedY = -Math.random() * 5 - 2; particle.life = 30; self.addChild(particle); self.particles.push(particle); return particle; }; self.update = function () { for (var i = self.particles.length - 1; i >= 0; i--) { var particle = self.particles[i]; particle.x += particle.speedX; particle.y += particle.speedY; particle.speedY += 0.1; particle.life--; particle.alpha = particle.life / 30; if (particle.life <= 0) { self.removeChild(particle); self.particles.splice(i, 1); } } }; self.emitAt = function (x, y, color, count) { for (var i = 0; i < count; i++) { self.createParticle(x, y, color); } }; return self; }); var SettingsPanel = Container.expand(function () { var self = Container.call(this); // Create semi-transparent background var bg = new Container(); var bgAsset = bg.attachAsset('lava', { anchorX: 0.5, anchorY: 0.5, scaleX: 5, scaleY: 5 }); bgAsset.alpha = 0.7; bgAsset.tint = 0x000000; self.addChild(bg); // Create panel title var titleText = new Text2('SETTINGS', { size: 120, fill: 0xFFFFFF }); titleText.anchor.set(0.5, 0.5); titleText.y = -500; self.addChild(titleText); // Create close button var closeButton = new Container(); var closeText = new Text2('X', { size: 80, fill: 0xFFFFFF }); closeText.anchor.set(0.5, 0.5); closeButton.addChild(closeText); closeButton.x = 800; closeButton.y = -500; closeButton.interactive = true; self.addChild(closeButton); // Paddle color selection section var paddleLabel = new Text2('PADDLE COLOR', { size: 80, fill: 0xFFFFFF }); paddleLabel.anchor.set(0.5, 0.5); paddleLabel.y = -300; self.addChild(paddleLabel); // Color options for paddle var paddleColors = [0xffbd00, 0xff0000, 0x00ff00, 0x0000ff, 0xff00ff, 0x00ffff, 0xffffff]; var paddleColorButtons = []; for (var i = 0; i < paddleColors.length; i++) { var colorBtn = new Container(); var colorAsset = colorBtn.attachAsset('ball', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.5, scaleY: 1.5 }); colorAsset.tint = paddleColors[i]; colorBtn.x = (i - 3) * 150; colorBtn.y = -200; colorBtn.interactive = true; colorBtn.colorValue = paddleColors[i]; colorBtn.colorType = 'paddle'; paddleColorButtons.push(colorBtn); self.addChild(colorBtn); } // Sound control section var soundLabel = new Text2('SOUND SETTINGS', { size: 80, fill: 0xFFFFFF }); soundLabel.anchor.set(0.5, 0.5); soundLabel.y = 300; self.addChild(soundLabel); // Master volume controls var masterVolumeLabel = new Text2('Master Volume', { size: 50, fill: 0xFFFFFF }); masterVolumeLabel.anchor.set(0.5, 0.5); masterVolumeLabel.y = 360; self.addChild(masterVolumeLabel); // Add master slider background var masterSliderBg = new Container(); var masterSliderBgAsset = masterSliderBg.attachAsset('paddleMiddle', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.5, scaleY: 0.5 }); masterSliderBgAsset.tint = 0x555555; masterSliderBg.x = 0; masterSliderBg.y = 400; self.addChild(masterSliderBg); // Add master slider handle var masterSliderHandle = new Container(); var masterSliderHandleAsset = masterSliderHandle.attachAsset('ball', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.2, scaleY: 1.2 }); masterSliderHandleAsset.tint = 0xFFFFFF; masterSliderHandle.interactive = true; masterSliderHandle.y = 400; self.addChild(masterSliderHandle); // Music volume controls var musicVolumeLabel = new Text2('Music Volume', { size: 50, fill: 0xFFFFFF }); musicVolumeLabel.anchor.set(0.5, 0.5); musicVolumeLabel.y = 460; self.addChild(musicVolumeLabel); // Add music slider background var musicSliderBg = new Container(); var musicSliderBgAsset = musicSliderBg.attachAsset('paddleMiddle', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.5, scaleY: 0.5 }); musicSliderBgAsset.tint = 0x555555; musicSliderBg.x = 0; musicSliderBg.y = 500; self.addChild(musicSliderBg); // Add music slider handle var musicSliderHandle = new Container(); var musicSliderHandleAsset = musicSliderHandle.attachAsset('ball', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.2, scaleY: 1.2 }); musicSliderHandleAsset.tint = 0x00FFFF; musicSliderHandle.interactive = true; musicSliderHandle.y = 500; self.addChild(musicSliderHandle); // SFX volume controls var sfxVolumeLabel = new Text2('SFX Volume', { size: 50, fill: 0xFFFFFF }); sfxVolumeLabel.anchor.set(0.5, 0.5); sfxVolumeLabel.y = 560; self.addChild(sfxVolumeLabel); // Add SFX slider background var sfxSliderBg = new Container(); var sfxSliderBgAsset = sfxSliderBg.attachAsset('paddleMiddle', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.5, scaleY: 0.5 }); sfxSliderBgAsset.tint = 0x555555; sfxSliderBg.x = 0; sfxSliderBg.y = 600; self.addChild(sfxSliderBg); // Add SFX slider handle var sfxSliderHandle = new Container(); var sfxSliderHandleAsset = sfxSliderHandle.attachAsset('ball', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.2, scaleY: 1.2 }); sfxSliderHandleAsset.tint = 0xFF5500; sfxSliderHandle.interactive = true; sfxSliderHandle.y = 600; self.addChild(sfxSliderHandle); // Set initial slider positions based on stored volumes var volume = storage.volume !== undefined ? storage.volume : 1.0; var musicVolume = storage.musicVolume !== undefined ? storage.musicVolume : 1.0; var sfxVolume = storage.sfxVolume !== undefined ? storage.sfxVolume : 1.0; masterSliderHandle.x = (volume - 0.5) * 300; // Map volume 0-1 to position -150 to 150 musicSliderHandle.x = (musicVolume - 0.5) * 300; sfxSliderHandle.x = (sfxVolume - 0.5) * 300; // Ball color selection section var ballLabel = new Text2('BALL COLOR', { size: 80, fill: 0xFFFFFF }); ballLabel.anchor.set(0.5, 0.5); ballLabel.y = 200; self.addChild(ballLabel); // Color options for ball var ballColors = [0xffffff, 0xff0000, 0x00ff00, 0x0000ff, 0xffff00, 0xff00ff, 0x00ffff]; var ballColorButtons = []; for (var j = 0; j < ballColors.length; j++) { var ballBtn = new Container(); var ballAsset = ballBtn.attachAsset('ball', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.5, scaleY: 1.5 }); ballAsset.tint = ballColors[j]; ballBtn.x = (j - 3) * 150; ballBtn.y = 100; ballBtn.interactive = true; ballBtn.colorValue = ballColors[j]; ballBtn.colorType = 'ball'; ballColorButtons.push(ballBtn); self.addChild(ballBtn); } // Update UI to show selected colors self.updateSelections = function () { var paddleColor = storage.paddleColor || 0xffbd00; var ballColor = storage.ballColor || 0xffffff; // Update paddle color buttons for (var i = 0; i < paddleColorButtons.length; i++) { var btn = paddleColorButtons[i]; if (btn.colorValue === paddleColor) { tween(btn, { scaleX: 1.8, scaleY: 1.8 }, { duration: 200 }); } else { tween(btn, { scaleX: 1, scaleY: 1 }, { duration: 200 }); } } // Update ball color buttons for (var j = 0; j < ballColorButtons.length; j++) { var bBtn = ballColorButtons[j]; if (bBtn.colorValue === ballColor) { tween(bBtn, { scaleX: 1.8, scaleY: 1.8 }, { duration: 200 }); } else { tween(bBtn, { scaleX: 1, scaleY: 1 }, { duration: 200 }); } } }; // Register color button events for all buttons function registerColorButtons() { var isDragging = false; // Volume slider handlers var currentSlider = null; // Master volume slider handlers masterSliderHandle.down = function (x, y, obj) { isDragging = true; currentSlider = this; tween(this, { scaleX: 1.2, scaleY: 1.2 }, { duration: 100 }); }; masterSliderHandle.move = function (x, y, obj) { if (this.parent && isDragging && currentSlider === this) { var localX = x; // Clamp to slider bounds (-150 to 150) if (localX < -150) { localX = -150; } if (localX > 150) { localX = 150; } this.x = localX; // Convert position to volume (0 to 1) var newVolume = (localX + 150) / 300; // Store volume setting in storage for persistence storage.volume = newVolume; // Test sound for immediate feedback with updated volume if (LK.ticks % 15 === 0) { LK.getSound('bounce').play({ volume: newVolume }); } } }; masterSliderHandle.up = function () { if (currentSlider === this) { isDragging = false; currentSlider = null; } tween(this, { scaleX: 1, scaleY: 1 }, { duration: 200, easing: tween.elasticOut }); }; // Music volume slider handlers musicSliderHandle.down = function (x, y, obj) { isDragging = true; currentSlider = this; tween(this, { scaleX: 1.2, scaleY: 1.2 }, { duration: 100 }); }; musicSliderHandle.move = function (x, y, obj) { if (this.parent && isDragging && currentSlider === this) { var localX = x; // Clamp to slider bounds if (localX < -150) { localX = -150; } if (localX > 150) { localX = 150; } this.x = localX; // Convert position to volume (0 to 1) var newMusicVolume = (localX + 150) / 300; // Store music volume setting storage.musicVolume = newMusicVolume; // Give immediate feedback by adjusting current music if (LK.ticks % 15 === 0) { // Apply music volume change var masterVolume = storage.volume !== undefined ? storage.volume : 1.0; LK.playMusic('bgmusic', { volume: masterVolume * newMusicVolume }); } } }; musicSliderHandle.up = function () { if (currentSlider === this) { isDragging = false; currentSlider = null; } tween(this, { scaleX: 1, scaleY: 1 }, { duration: 200, easing: tween.elasticOut }); }; // SFX volume slider handlers sfxSliderHandle.down = function (x, y, obj) { isDragging = true; currentSlider = this; tween(this, { scaleX: 1.2, scaleY: 1.2 }, { duration: 100 }); }; sfxSliderHandle.move = function (x, y, obj) { if (this.parent && isDragging && currentSlider === this) { var localX = x; // Clamp to slider bounds if (localX < -150) { localX = -150; } if (localX > 150) { localX = 150; } this.x = localX; // Convert position to volume (0 to 1) var newSfxVolume = (localX + 150) / 300; // Store sfx volume setting storage.sfxVolume = newSfxVolume; // Test sound for immediate feedback with updated volume if (LK.ticks % 15 === 0) { var masterVolume = storage.volume !== undefined ? storage.volume : 1.0; LK.getSound('click').play({ volume: masterVolume * newSfxVolume }); } } }; sfxSliderHandle.up = function () { if (currentSlider === this) { isDragging = false; currentSlider = null; } tween(this, { scaleX: 1, scaleY: 1 }, { duration: 200, easing: tween.elasticOut }); }; // Paddle color buttons for (var i = 0; i < paddleColorButtons.length; i++) { var pBtn = paddleColorButtons[i]; pBtn.down = function () { tween(this, { scaleX: 0.9, scaleY: 0.9 }, { duration: 100 }); }; pBtn.up = function () { var btn = this; tween(btn, { scaleX: 1, scaleY: 1 }, { duration: 200, easing: tween.elasticOut, onFinish: function onFinish() { storage.paddleColor = btn.colorValue; self.updateSelections(); // Update game paddle colors if (self.onColorChange) { self.onColorChange(); } } }); }; } // Ball color buttons for (var j = 0; j < ballColorButtons.length; j++) { var bBtn = ballColorButtons[j]; bBtn.down = function () { tween(this, { scaleX: 0.9, scaleY: 0.9 }, { duration: 100 }); }; bBtn.up = function () { var btn = this; tween(btn, { scaleX: 1, scaleY: 1 }, { duration: 200, easing: tween.elasticOut, onFinish: function onFinish() { storage.ballColor = btn.colorValue; self.updateSelections(); // Update game ball colors if (self.onColorChange) { self.onColorChange(); } } }); }; } } // Close button event closeButton.down = function () { tween(this, { scaleX: 0.9, scaleY: 0.9 }, { duration: 100 }); }; closeButton.up = function () { tween(this, { scaleX: 1, scaleY: 1 }, { duration: 200, easing: tween.elasticOut, onFinish: function onFinish() { // Call close callback if exists if (self.onClose) { self.onClose(); } } }); }; // Initialize registerColorButtons(); self.updateSelections(); return self; }); var StartMenu = Container.expand(function () { var self = Container.call(this); // Create title text var titleText = new Text2('Lava Bounce', { size: 200, fill: 0xFFBD00 }); titleText.anchor.set(0.5, 0.5); titleText.y = -300; self.addChild(titleText); // Create title animation function animateTitle() { tween(titleText, { y: -300 - 30 }, { duration: 1000, easing: tween.easeInOut, onFinish: function onFinish() { tween(titleText, { y: -300 + 30 }, { duration: 1000, easing: tween.easeInOut, onFinish: function onFinish() { animateTitle(); } }); } }); } // Start the title animation animateTitle(); // Create start button var startButton = new Container(); // Remove background asset but keep the text var buttonText = new Text2('PLAY', { size: 100, fill: 0xFFFFFF }); buttonText.anchor.set(0.5, 0.5); startButton.addChild(buttonText); startButton.interactive = true; startButton.y = 0; self.addChild(startButton); // High score display var highScoreText = new Text2('HIGH SCORE: 0', { size: 60, fill: 0xFFFFFF }); highScoreText.anchor.set(0.5, 0.5); highScoreText.y = 200; self.addChild(highScoreText); // Update high score display self.updateHighScore = function (score) { highScoreText.setText('HIGH SCORE: ' + score); }; // Handle button press startButton.down = function () { // Play click sound with combined volume settings var masterVolume = storage.volume !== undefined ? storage.volume : 1.0; var sfxVolume = storage.sfxVolume !== undefined ? storage.sfxVolume : 1.0; LK.getSound('click').play({ volume: masterVolume * sfxVolume }); // Scale effect tween(startButton, { scaleX: 0.95, scaleY: 0.95 }, { duration: 100 }); }; // Handle button release startButton.up = function () { // Return to original scale with bounce tween(startButton, { scaleX: 1, scaleY: 1 }, { duration: 200, easing: tween.elasticOut, onFinish: function onFinish() { // Notify game that start was pressed if (self.onStart) { self.onStart(); } } }); }; return self; }); var WallParticles = Container.expand(function () { var self = Container.call(this); self.particles = []; self.maxParticles = 15; self.createParticle = function (x, y, side) { var particle = new Container(); var particleGraphic = particle.attachAsset('ball', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.2 + Math.random() * 0.3, scaleY: 0.2 + Math.random() * 0.3 }); // Use white or slightly colored particles var colors = [0xFFFFFF, 0xFFFFAA, 0xAAFFFF]; particleGraphic.tint = colors[Math.floor(Math.random() * colors.length)]; particle.x = x; particle.y = y; particle.alpha = 1; // Particles fly away from the wall var speed = 2 + Math.random() * 3; if (side === 'left') { particle.speedX = speed; } else { particle.speedX = -speed; } particle.speedY = Math.random() * 6 - 3; particle.rotationSpeed = (Math.random() - 0.5) * 0.2; particle.life = 20 + Math.random() * 15; particle.maxLife = particle.life; self.addChild(particle); self.particles.push(particle); return particle; }; self.update = function () { for (var i = self.particles.length - 1; i >= 0; i--) { var particle = self.particles[i]; particle.x += particle.speedX; particle.y += particle.speedY; particle.rotation += particle.rotationSpeed; particle.speedX *= 0.95; particle.speedY *= 0.95; particle.life--; particle.alpha = particle.life / particle.maxLife; if (particle.life <= 0) { self.removeChild(particle); self.particles.splice(i, 1); } } }; self.emitWallHit = function (x, y, side, count) { for (var i = 0; i < count; i++) { self.createParticle(x, y, side); } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x220000 }); /**** * Game Code ****/ // Game state // Sounds removed var gameActive = false; var settingsPanelActive = false; var confirmDialogActive = false; // Game variables var score = 0; var highScore = storage.highScore || 0; var level = 1; var hitsToNextLevel = 20; var difficulty = 1; var difficultyIncreaseTimer = null; var levelRequirement = 20; // Hits needed to complete each level var mouseParticles = new MouseParticleSystem(); // Create back button var backButton = new Container(); // Remove yellow box background, keeping just the arrow var backText = new Text2('←', { size: 60, fill: 0xFFFFFF }); backText.anchor.set(0.5, 0.5); backButton.addChild(backText); backButton.x = 2048 - 120; backButton.y = 80; backButton.interactive = true; backButton.visible = false; // Function to check if a display object is a child of this container function containerContains(container, child) { if (!container.children) { return false; } for (var i = 0; i < container.children.length; i++) { if (container.children[i] === child) { return true; } } return false; } ; // Load stored colors or use defaults var paddleColor = storage.paddleColor || 0xffbd00; var ballColor = storage.ballColor || 0xffffff; // Create game objects var paddle = new Paddle(); var ball = new Ball(); var lava = new Lava(); var particles = new ParticleSystem(); var wallParticles = new WallParticles(); var startMenu = new StartMenu(); var settingsPanel = new SettingsPanel(); var confirmDialog = new ConfirmDialog(); // Initialize settings panel settingsPanel.x = 2048 / 2; settingsPanel.y = 2732 / 2; settingsPanel.visible = false; settingsPanel.alpha = 0; // Apply stored colors and volume to game objects function applyStoredColors() { // Apply paddle color paddle.children.forEach(function (child) { if (child.tint !== undefined) { child.tint = paddleColor; } }); // Apply ball color if (ball.children && ball.children.length > 0) { ball.children[0].tint = ballColor; } // Apply stored volume settings - note: we don't need to set volume here // as sounds will use the stored volume when played var volume = storage.volume !== undefined ? storage.volume : 1.0; // Don't need to set volume directly on sound objects since we'll use // the volume parameter when playing sounds via LK.getSound().play({volume: x}) } // Settings panel callbacks settingsPanel.onColorChange = function () { // Update color variables paddleColor = storage.paddleColor || 0xffbd00; ballColor = storage.ballColor || 0xffffff; // Apply colors applyStoredColors(); }; settingsPanel.onClose = function () { // Hide settings panel with smoother, slower animation tween(settingsPanel, { alpha: 0, scaleX: 0.5, scaleY: 0.5 }, { duration: 700, easing: tween.easeOut, onFinish: function onFinish() { settingsPanel.visible = false; settingsPanelActive = false; } }); }; // Start menu settings callback startMenu.onSettings = function () { // Show settings panel with animation settingsPanel.visible = true; settingsPanel.alpha = 0; settingsPanelActive = true; // Set initial scale for a gentler opening animation settingsPanel.scale.set(0.5); // Make sure we're not adding it multiple times if (!containerContains(game, settingsPanel)) { game.addChild(settingsPanel); } // Make sure settings panel is at the top layer if (containerContains(game, settingsPanel)) { game.removeChild(settingsPanel); game.addChild(settingsPanel); } // Animate both alpha and scale with a smoother, slower animation tween(settingsPanel, { alpha: 1, scaleX: 1, scaleY: 1 }, { duration: 800, easing: tween.easeOut }); }; // Initialize paddle position paddle.x = 2048 / 2; paddle.y = 2732 - 300; // Initialize ball position ball.reset(); // Initialize lava position to be at the bottom of the screen lava.x = 2048 / 2; lava.y = 2732 + lava.height / 2 - 100; // Position lava so only top part is visible lava.baseY = lava.y; // Store the initial Y position for wave animation lava.minY = lava.height / 2; // Minimum Y position to prevent running off top edge lava.maxY = 2732 - lava.height / 2; // Maximum Y position to prevent running off bottom edge // Create confetti system for level celebrations var confetti = new Confetti(); // Position start menu in center of screen startMenu.x = 2048 / 2; startMenu.y = 2732 / 2; startMenu.updateHighScore(highScore); // Start menu callback startMenu.onStart = function () { startGame(); }; // Create settings button var settingsButton = new Container(); var settingsText = new Text2('SETTINGS', { size: 60, fill: 0xFFFFFF }); settingsText.anchor.set(0.5, 0.5); settingsButton.addChild(settingsText); settingsButton.interactive = true; settingsButton.y = 300; settingsButton.x = 0; startMenu.addChild(settingsButton); // Handle settings button press settingsButton.down = function () { var masterVolume = storage.volume !== undefined ? storage.volume : 1.0; var sfxVolume = storage.sfxVolume !== undefined ? storage.sfxVolume : 1.0; LK.getSound('click').play({ volume: masterVolume * sfxVolume }); tween(settingsButton, { scaleX: 0.9, scaleY: 0.9 }, { duration: 100 }); }; // Handle settings button release settingsButton.up = function () { tween(settingsButton, { scaleX: 1, scaleY: 1 }, { duration: 200, easing: tween.elasticOut, onFinish: function onFinish() { // Show settings panel if (startMenu.onSettings) { startMenu.onSettings(); } } }); }; // Create reset high score button to start menu var resetButton = new Container(); var resetText = new Text2('RESET HIGH SCORE', { size: 40, fill: 0xFFFFFF }); resetText.anchor.set(0.5, 0.5); resetButton.addChild(resetText); resetButton.interactive = true; resetButton.y = 370; resetButton.x = 0; startMenu.addChild(resetButton); // Handle reset button press resetButton.down = function () { var masterVolume = storage.volume !== undefined ? storage.volume : 1.0; var sfxVolume = storage.sfxVolume !== undefined ? storage.sfxVolume : 1.0; LK.getSound('click').play({ volume: masterVolume * sfxVolume }); tween(resetButton, { scaleX: 0.9, scaleY: 0.9 }, { duration: 100 }); }; // Handle reset button release resetButton.up = function () { tween(resetButton, { scaleX: 1, scaleY: 1 }, { duration: 200, easing: tween.elasticOut, onFinish: function onFinish() { // Show confirm dialog showConfirmDialog(); } }); }; // Add start menu to game initially game.addChild(startMenu); // Add settings panel to game (initially hidden) game.addChild(settingsPanel); // Add back button to game game.addChild(backButton); // Setup back button event handlers backButton.down = function () { var masterVolume = storage.volume !== undefined ? storage.volume : 1.0; var sfxVolume = storage.sfxVolume !== undefined ? storage.sfxVolume : 1.0; LK.getSound('click').play({ volume: masterVolume * sfxVolume }); tween(this, { scaleX: 0.9, scaleY: 0.9 }, { duration: 100 }); }; backButton.up = function () { var btn = this; tween(btn, { scaleX: 1, scaleY: 1 }, { duration: 200, easing: tween.elasticOut, onFinish: function onFinish() { returnToMenu(); } }); }; // Initialize confirm dialog confirmDialog.x = 2048 / 2; confirmDialog.y = 2732 / 2; confirmDialog.visible = false; confirmDialog.alpha = 0; // Setup confirm dialog callbacks confirmDialog.onConfirm = function () { // Reset high score to 0 highScore = 0; storage.highScore = 0; startMenu.updateHighScore(0); highScoreTxt.setText('High Score: 0'); // Hide dialog hideConfirmDialog(); }; confirmDialog.onCancel = function () { // Just hide dialog hideConfirmDialog(); }; // Function to show confirm dialog function showConfirmDialog() { confirmDialog.visible = true; confirmDialogActive = true; // Set initial state for a gentler animation confirmDialog.scale.set(0.5); confirmDialog.alpha = 0; // Make sure it's not already added multiple times if (containerContains(game, confirmDialog)) { game.removeChild(confirmDialog); } game.addChild(confirmDialog); // Make sure confirmDialog is at the top layer if (containerContains(game, confirmDialog)) { game.removeChild(confirmDialog); game.addChild(confirmDialog); } // Add smoother, less aggressive animation when dialog opens tween(confirmDialog, { alpha: 1, scaleX: 1.05, scaleY: 1.05 }, { duration: 700, easing: tween.easeOut, onFinish: function onFinish() { // Scale back to normal size with a gentler effect tween(confirmDialog, { scaleX: 1, scaleY: 1 }, { duration: 300, easing: tween.easeOut }); } }); } // Function to hide confirm dialog function hideConfirmDialog() { tween(confirmDialog, { alpha: 0, scaleX: 0.5, scaleY: 0.5 }, { duration: 600, easing: tween.easeOut, onFinish: function onFinish() { confirmDialog.visible = false; confirmDialogActive = false; } }); } // Function to start the game function startGame() { // Remove start menu game.removeChild(startMenu); // Add game objects game.addChild(paddle); game.addChild(ball); game.addChild(lava); game.addChild(particles); game.addChild(confetti); game.addChild(mouseParticles); game.addChild(wallParticles); // Setup wall hit callback for ball ball.onWallHit = function (x, y, side) { wallParticles.emitWallHit(x, y, side, 10); }; // Add back button backButton.visible = true; // Apply color settings applyStoredColors(); // Reset game state score = 0; level = 1; difficulty = 1; ball.reset(); // Play background music with fade in and proper combined volume var masterVolume = storage.volume !== undefined ? storage.volume : 1.0; var musicVolume = storage.musicVolume !== undefined ? storage.musicVolume : 1.0; var combinedVolume = masterVolume * musicVolume; LK.playMusic('bgmusic', { fade: { start: 0, end: combinedVolume, duration: 1000 } }); // Start difficulty timer if (difficultyIncreaseTimer) { LK.clearInterval(difficultyIncreaseTimer); } difficultyIncreaseTimer = LK.setInterval(increaseDifficulty, 10000); // Reset lava to its proper starting position at the bottom of the screen lava.y = 2732 + lava.height / 2 - 100; // Position lava so only top part is visible lava.baseY = lava.y; // Store the initial Y position for wave animation // Stop any existing animations tween.stop(lava, { y: true }); // Start the rise animation lava.startRiseFall(); // Set game as active gameActive = true; // Make UI elements visible scoreTxt.visible = true; levelTxt.visible = true; progressTxt.visible = true; highScoreTxt.visible = true; // Show high score when game starts // Update UI updateScore(); levelTxt.setText('Level: ' + level); progressTxt.setText('0/' + levelRequirement); } // Function to return to menu function returnToMenu() { // Remove game objects game.removeChild(paddle); game.removeChild(ball); game.removeChild(lava); game.removeChild(particles); game.removeChild(confetti); game.removeChild(wallParticles); // Hide back button backButton.visible = false; // Update high score on menu startMenu.updateHighScore(highScore); // Add menu back if (!containerContains(game, startMenu)) { game.addChild(startMenu); } // Make sure settings button and reset button events are properly attached if (settingsButton && !containerContains(startMenu, settingsButton)) { startMenu.addChild(settingsButton); } if (resetButton && !containerContains(startMenu, resetButton)) { startMenu.addChild(resetButton); } // Set game as inactive gameActive = false; // Hide UI elements scoreTxt.visible = false; levelTxt.visible = false; progressTxt.visible = false; // Fade out and stop music var masterVolume = storage.volume !== undefined ? storage.volume : 1.0; var musicVolume = storage.musicVolume !== undefined ? storage.musicVolume : 1.0; var combinedVolume = masterVolume * musicVolume; LK.playMusic('bgmusic', { fade: { start: combinedVolume, end: 0, duration: 800 } }); LK.setTimeout(function () { LK.stopMusic(); }, 800); } // Create score display var scoreTxt = new Text2('Score: 0', { size: 100, fill: 0xFFFFFF }); scoreTxt.anchor.set(0.5, 0.5); scoreTxt.y = 50; scoreTxt.visible = false; // Hide score initially // Create level display var levelTxt = new Text2('Level: 1', { size: 80, fill: 0x00FFFF }); levelTxt.anchor.set(0.5, 0); levelTxt.y = 150; levelTxt.visible = false; // Hide level initially // Create level progress display var progressTxt = new Text2('', { size: 50, fill: 0xFFFFFF }); progressTxt.anchor.set(0.5, 0); progressTxt.y = 30; progressTxt.visible = false; // Hide progress initially // Create high score display var highScoreTxt = new Text2('High Score: 0', { size: 80, fill: 0xFFBD00 }); highScoreTxt.anchor.set(1, 0); highScoreTxt.setText('High Score: ' + highScore); highScoreTxt.y = 70; highScoreTxt.visible = false; // Hide high score initially // Description text removed LK.gui.topRight.addChild(highScoreTxt); LK.gui.center.addChild(scoreTxt); LK.gui.top.addChild(levelTxt); LK.gui.top.addChild(progressTxt); // Touch/drag handling for paddle movement var isDragging = false; game.down = function (x, y, obj) { // Play click sound with combined volume settings var masterVolume = storage.volume !== undefined ? storage.volume : 1.0; var sfxVolume = storage.sfxVolume !== undefined ? storage.sfxVolume : 1.0; LK.getSound('click').play({ volume: masterVolume * sfxVolume }); isDragging = true; paddle.x = x; // Emit more particles at mouse/touch position for improved visibility mouseParticles.emitAt(x, y, 35); }; game.move = function (x, y, obj) { if (isDragging) { paddle.x = x; // Clamp paddle position to keep it within screen bounds if (paddle.x < paddle.width / 2) { paddle.x = paddle.width / 2; } else if (paddle.x > 2048 - paddle.width / 2) { paddle.x = 2048 - paddle.width / 2; } } }; game.up = function (x, y, obj) { isDragging = false; }; // Function to update score display function updateScore() { scoreTxt.setText(score.toString()); // Update progress to next level var progress = score % levelRequirement; var remaining = levelRequirement - progress; progressTxt.setText(progress + '/' + levelRequirement); // Check for level completion if (progress === 0 && score > 0) { // Level up! level = Math.floor(score / levelRequirement) + 1; levelTxt.setText('Level: ' + level); // Celebrate level completion celebrateNewLevel(); // Increase difficulty with each level difficulty = 1 + (level - 1) * 0.3; if (difficulty > 3) { difficulty = 3; } // Update game properties based on new difficulty ball.gravity = 0.2 * difficulty; ball.maxSpeed = 15 * difficulty; } // Update high score if current score is higher if (score > highScore) { highScore = score; storage.highScore = highScore; highScoreTxt.setText('High Score: ' + highScore); } } // Function to celebrate new level function celebrateNewLevel() { // Play level up sound with combined volume settings var masterVolume = storage.volume !== undefined ? storage.volume : 1.0; var sfxVolume = storage.sfxVolume !== undefined ? storage.sfxVolume : 1.0; LK.getSound('levelup').play({ volume: masterVolume * sfxVolume }); // Show celebratory text var celebrationText = new Text2('LEVEL ' + level + '!', { size: 150, fill: 0xFFFF00 }); celebrationText.anchor.set(0.5, 0.5); LK.gui.center.addChild(celebrationText); // Make text appear with scale animation celebrationText.scale.set(0.1); tween(celebrationText, { scaleX: 1.2, scaleY: 1.2 }, { duration: 500, easing: tween.elasticOut, onFinish: function onFinish() { tween(celebrationText, { scaleX: 1, scaleY: 1 }, { duration: 200, onFinish: function onFinish() { // Fade out after a moment LK.setTimeout(function () { tween(celebrationText, { alpha: 0 }, { duration: 500, easing: tween.easeOut, onFinish: function onFinish() { LK.gui.center.removeChild(celebrationText); } }); }, 1000); } }); } }); // Create confetti bursts for (var i = 0; i < 5; i++) { LK.setTimeout(function () { // Create multiple confetti bursts across screen confetti.emit(Math.random() * 2048, 500, 30); }, i * 200); } // Flash screen for celebration LK.effects.flashScreen(0x00FFFF, 300); // Sounds and music removed } // Function to increase difficulty over time function increaseDifficulty() { difficulty += 0.1; // Cap maximum difficulty if (difficulty > 2) { difficulty = 2; LK.clearInterval(difficultyIncreaseTimer); } // Update ball properties based on difficulty ball.gravity = 0.1 * difficulty; ball.maxSpeed = 10 * difficulty; // Music adjustment removed } // Start difficulty increase timer difficultyIncreaseTimer = LK.setInterval(increaseDifficulty, 10000); // Music will be played when game starts // Not playing background music initially to allow menu to be silent // Game update loop game.update = function () { // If game is not active or UI panels are active, don't update game if (!gameActive || settingsPanelActive || confirmDialogActive) { return; } // Update ball position ball.update(); // Update lava waves lava.update(); // Check for collision with paddle if (ball.speedY > 0 && ball.y + ball.height / 2 >= paddle.y - paddle.height / 2 && ball.y - ball.height / 2 <= paddle.y + paddle.height / 2 && ball.x + ball.width / 2 >= paddle.x - paddle.width / 2 && ball.x - ball.width / 2 <= paddle.x + paddle.width / 2) { // Bounce the ball in a random direction when it hits the paddle var angle = Math.random() * Math.PI * 0.7 - Math.PI * 0.35; // Random angle between -35 and +35 degrees var speed = Math.sqrt(ball.speedX * ball.speedX + ball.speedY * ball.speedY) * 1.5; // Keep overall speed but increase by 1.5x ball.speedY = -Math.cos(angle) * speed; // Vertical component (mostly upward) ball.speedX = Math.sin(angle) * speed; // Horizontal component // Add additional horizontal velocity based on where the ball hit the paddle var hitPosition = (ball.x - paddle.x) / (paddle.width / 2); ball.speedX += hitPosition * 5; // Add some influence from hit position, but less than before // Flash paddle to indicate hit LK.effects.flashObject(paddle, 0xffffff, 200); // Emit particles at collision point particles.emitAt(ball.x, paddle.y - paddle.height / 2, 0xffbd00, 15); // Add scale animation to paddle for bounce feedback tween(paddle, { scaleY: 0.7, y: paddle.y + 10 }, { duration: 100, easing: tween.easeOut, onFinish: function onFinish() { tween(paddle, { scaleY: 1, y: 2732 - 300 }, { duration: 150, easing: tween.elasticOut }); } }); // Play bounce sound when ball hits paddle var masterVolume = storage.volume !== undefined ? storage.volume : 1.0; var sfxVolume = storage.sfxVolume !== undefined ? storage.sfxVolume : 1.0; LK.getSound('bounce').play({ volume: masterVolume * sfxVolume }); // Increment score score++; updateScore(); } // Check if ball touches lava if (ball.y + ball.height / 2 >= lava.y - lava.height / 2) { // Play lava sound with combined volume settings var masterVolume = storage.volume !== undefined ? storage.volume : 1.0; var sfxVolume = storage.sfxVolume !== undefined ? storage.sfxVolume : 1.0; LK.getSound('lava').play({ volume: masterVolume * sfxVolume }); LK.getSound('gameover').play({ volume: masterVolume * sfxVolume }); // Flash screen red LK.effects.flashScreen(0xff0000, 500); // Create lava splash effect particles.emitAt(ball.x, lava.y - lava.height / 2, 0xff3300, 30); // Stop any current lava animations tween.stop(lava, { y: true }); // Make lava "jump" without moving up or down tween(lava, { scaleY: 1.2 }, { duration: 200, easing: tween.easeOut, onFinish: function onFinish() { tween(lava, { scaleY: 1 }, { duration: 300, easing: tween.elasticOut }); } }); // Just reset the game without returning to menu // Reset score and level score = 0; level = 1; levelTxt.setText('Level: ' + level); progressTxt.setText('0/' + levelRequirement); updateScore(); // Reset ball ball.reset(); // Reset difficulty difficulty = 1; ball.gravity = 0.2; ball.maxSpeed = 15; // Restart difficulty increase timer LK.clearInterval(difficultyIncreaseTimer); difficultyIncreaseTimer = LK.setInterval(increaseDifficulty, 10000); // Music removed } // If ball goes too high, change direction if (ball.y < 100) { ball.speedY = Math.abs(ball.speedY) * 0.5; } // Update particles particles.update(); // Update confetti confetti.update(); // Update mouse particles mouseParticles.update(); // Update wall particles wallParticles.update(); };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
highScore: 0,
volume: 1,
musicVolume: 1,
sfxVolume: 1
});
/****
* Classes
****/
var Ball = Container.expand(function () {
var self = Container.call(this);
var ballGraphics = self.attachAsset('ball', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = ballGraphics.width;
self.height = ballGraphics.height;
self.speedY = 3;
self.speedX = 0;
self.gravity = 0.1;
self.maxSpeed = 10;
self.reset = function () {
self.x = 2048 / 2;
self.y = 500;
self.speedY = 3;
self.speedX = Math.random() * 4 - 2;
};
self.update = function () {
// Apply gravity
self.speedY += self.gravity;
// Cap max speed
if (self.speedY > self.maxSpeed) {
self.speedY = self.maxSpeed;
}
// Update position
self.x += self.speedX;
self.y += self.speedY;
// Bounce off walls
if (self.x < self.width / 2) {
self.x = self.width / 2;
self.speedX = -self.speedX;
// Play wall hit sound
var masterVolume = storage.volume !== undefined ? storage.volume : 1.0;
var sfxVolume = storage.sfxVolume !== undefined ? storage.sfxVolume : 1.0;
LK.getSound('bounce').play({
volume: masterVolume * sfxVolume * 0.7
});
// Emit wall hit particles
if (self.onWallHit) {
self.onWallHit(self.x, self.y, 'left');
}
} else if (self.x > 2048 - self.width / 2) {
self.x = 2048 - self.width / 2;
self.speedX = -self.speedX;
// Play wall hit sound
var masterVolume = storage.volume !== undefined ? storage.volume : 1.0;
var sfxVolume = storage.sfxVolume !== undefined ? storage.sfxVolume : 1.0;
LK.getSound('bounce').play({
volume: masterVolume * sfxVolume * 0.7
});
// Emit wall hit particles
if (self.onWallHit) {
self.onWallHit(self.x, self.y, 'right');
}
}
};
return self;
});
var Confetti = Container.expand(function () {
var self = Container.call(this);
self.particles = [];
self.colors = [0xFF0000, 0x00FF00, 0x0000FF, 0xFFFF00, 0xFF00FF, 0x00FFFF, 0xFFFFFF];
self.emit = function (x, y, count) {
for (var i = 0; i < count; i++) {
var particle = new Container();
var size = Math.random() * 20 + 10;
var color = self.colors[Math.floor(Math.random() * self.colors.length)];
var particleGraphic = particle.attachAsset('ball', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: size / 40,
scaleY: size / 40
});
particleGraphic.tint = color;
particle.x = x;
particle.y = y;
particle.speedX = Math.random() * 20 - 10;
particle.speedY = -Math.random() * 15 - 5;
particle.rotationSpeed = (Math.random() - 0.5) * 0.2;
particle.life = 60 + Math.random() * 60;
particle.maxLife = particle.life;
self.addChild(particle);
self.particles.push(particle);
}
};
self.update = function () {
for (var i = self.particles.length - 1; i >= 0; i--) {
var particle = self.particles[i];
particle.x += particle.speedX;
particle.y += particle.speedY;
particle.speedY += 0.2; // Gravity
particle.rotation += particle.rotationSpeed;
particle.life--;
particle.alpha = particle.life / particle.maxLife;
if (particle.life <= 0) {
self.removeChild(particle);
self.particles.splice(i, 1);
}
}
};
return self;
});
var ConfirmDialog = Container.expand(function () {
var self = Container.call(this);
// Create semi-transparent background
var bg = new Container();
var bgAsset = bg.attachAsset('lava', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 3,
scaleY: 2
});
bgAsset.alpha = 0.8;
bgAsset.tint = 0x000000;
self.addChild(bg);
// Create message text
var messageText = new Text2('Are you sure you want\nto reset your high score?', {
size: 80,
fill: 0xFFFFFF
});
messageText.anchor.set(0.5, 0.5);
messageText.y = -100;
self.addChild(messageText);
// Create yes button
var yesButton = new Container();
var yesText = new Text2('YES', {
size: 80,
fill: 0xFF0000
});
yesText.anchor.set(0.5, 0.5);
yesButton.addChild(yesText);
yesButton.x = -150;
yesButton.y = 100;
yesButton.interactive = true;
self.addChild(yesButton);
// Create no button
var noButton = new Container();
var noText = new Text2('NO', {
size: 80,
fill: 0xFFFFFF
});
noText.anchor.set(0.5, 0.5);
noButton.addChild(noText);
noButton.x = 150;
noButton.y = 100;
noButton.interactive = true;
self.addChild(noButton);
// Button press handlers
yesButton.down = function () {
tween(this, {
scaleX: 0.9,
scaleY: 0.9
}, {
duration: 100
});
};
yesButton.up = function () {
var btn = this;
tween(btn, {
scaleX: 1,
scaleY: 1
}, {
duration: 200,
easing: tween.elasticOut,
onFinish: function onFinish() {
if (self.onConfirm) {
self.onConfirm();
}
}
});
};
noButton.down = function () {
tween(this, {
scaleX: 0.9,
scaleY: 0.9
}, {
duration: 100
});
};
noButton.up = function () {
var btn = this;
tween(btn, {
scaleX: 1,
scaleY: 1
}, {
duration: 200,
easing: tween.elasticOut,
onFinish: function onFinish() {
if (self.onCancel) {
self.onCancel();
}
}
});
};
return self;
});
var Lava = Container.expand(function () {
var self = Container.call(this);
var lavaGraphics = self.attachAsset('lava', {
anchorX: 0.5,
anchorY: 0.5
});
// Set initial wave properties
self.waveOffset = Math.random() * Math.PI * 2; // Random starting position for wave
self.baseY = 0; // Will store the initial y position
self.rising = false; // Track if lava is rising or falling
self.moveRange = 300; // How far the lava will move up and down
self.moveSpeed = 5000; // Duration of rise/fall movement in ms
// The paddle reference will be set when the lava is added to the game
// Start the rise and fall cycle
self.startRiseFall = function () {
// Make the lava rise a little bit from its base position
tween(self, {
y: self.baseY - 100
}, {
duration: 2000,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(self, {
y: self.baseY
}, {
duration: 2000,
easing: tween.easeInOut,
onFinish: self.startRiseFall
});
}
});
};
// Create a pulsing effect for the lava
self.pulse = function () {
tween(lavaGraphics, {
alpha: 0.7
}, {
duration: 800,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(lavaGraphics, {
alpha: 1
}, {
duration: 800,
easing: tween.easeInOut,
onFinish: self.pulse
});
}
});
};
// Create animated wave effect
self.update = function () {
// Create a wave effect based on time
var waveTime = LK.ticks / 30; // Adjust speed of wave
var waveScale = Math.sin(waveTime) * 0.05 + 1; // Scale between 0.95 and 1.05
// Apply different wave scales to left and right sides
var leftWaveScale = Math.sin(waveTime + 1) * 0.05 + 1;
var rightWaveScale = Math.sin(waveTime + 2) * 0.05 + 1;
// Apply the scale distortion to create wave effect
lavaGraphics.scaleX = waveScale;
// Small vertical oscillation to add to the tween movement
var oscillation = Math.sin(waveTime * 0.5) * 0.5;
// The y position is controlled by the tween, we just add small oscillation
// (Not setting self.y directly here since tweens are handling the major movement)
};
self.pulse();
return self;
});
var MouseParticleSystem = Container.expand(function () {
var self = Container.call(this);
self.particles = [];
self.maxParticles = 100;
self.createParticle = function (x, y) {
var particle = new Container();
var particleGraphic = particle.attachAsset('ball', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.4,
scaleY: 0.4
});
// Use bright colors for better visibility
var colors = [0xFFFF00, 0xFF00FF, 0x00FFFF, 0xFF5500, 0x88FF00];
particleGraphic.tint = colors[Math.floor(Math.random() * colors.length)];
particle.x = x;
particle.y = y;
particle.alpha = 1;
particle.speedX = Math.random() * 12 - 6;
particle.speedY = Math.random() * 12 - 6;
particle.life = 30 + Math.random() * 30;
particle.maxLife = particle.life;
self.addChild(particle);
self.particles.push(particle);
return particle;
};
self.update = function () {
for (var i = self.particles.length - 1; i >= 0; i--) {
var particle = self.particles[i];
particle.x += particle.speedX;
particle.y += particle.speedY;
particle.speedX *= 0.96;
particle.speedY *= 0.96;
particle.life--;
particle.alpha = particle.life / particle.maxLife;
// Smoother size transition with tween-like effect
particle.scaleX = particle.life / particle.maxLife * 0.4 + 0.1;
particle.scaleY = particle.life / particle.maxLife * 0.4 + 0.1;
// Add rotation for more visual interest
particle.rotation += 0.05;
if (particle.life <= 0) {
self.removeChild(particle);
self.particles.splice(i, 1);
}
}
};
self.emitAt = function (x, y, count) {
// Create more particles for better visual effect
for (var i = 0; i < count; i++) {
// Slightly offset each particle for a more natural burst effect
var offsetX = x + (Math.random() * 20 - 10);
var offsetY = y + (Math.random() * 20 - 10);
self.createParticle(offsetX, offsetY);
}
};
return self;
});
var Paddle = Container.expand(function () {
var self = Container.call(this);
// Create left rounded edge
var paddleLeftGraphics = self.attachAsset('paddleLeft', {
anchorX: 1.0,
anchorY: 0.5,
x: -170
});
// Create middle section
var paddleMiddleGraphics = self.attachAsset('paddleMiddle', {
anchorX: 0.5,
anchorY: 0.5
});
// Create right rounded edge
var paddleRightGraphics = self.attachAsset('paddleRight', {
anchorX: 0.0,
anchorY: 0.5,
x: 170
});
// Adjust positions to eliminate gaps
// The middle section's width is 340, and each end cap is 30
// We need to ensure they connect perfectly
paddleLeftGraphics.x = -170 + 15; // Move right by half of end cap width
paddleRightGraphics.x = 170 - 15; // Move left by half of end cap width
// Set the overall width based on the combined elements
self.width = 400; // total width (30 + 340 + 30)
self.height = 30;
return self;
});
var ParticleSystem = Container.expand(function () {
var self = Container.call(this);
self.particles = [];
self.maxParticles = 20;
self.createParticle = function (x, y, color) {
var particle = new Container();
var particleGraphic = particle.attachAsset('ball', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.3,
scaleY: 0.3
});
particleGraphic.tint = color;
particle.x = x;
particle.y = y;
particle.alpha = 1;
particle.speedX = Math.random() * 10 - 5;
particle.speedY = -Math.random() * 5 - 2;
particle.life = 30;
self.addChild(particle);
self.particles.push(particle);
return particle;
};
self.update = function () {
for (var i = self.particles.length - 1; i >= 0; i--) {
var particle = self.particles[i];
particle.x += particle.speedX;
particle.y += particle.speedY;
particle.speedY += 0.1;
particle.life--;
particle.alpha = particle.life / 30;
if (particle.life <= 0) {
self.removeChild(particle);
self.particles.splice(i, 1);
}
}
};
self.emitAt = function (x, y, color, count) {
for (var i = 0; i < count; i++) {
self.createParticle(x, y, color);
}
};
return self;
});
var SettingsPanel = Container.expand(function () {
var self = Container.call(this);
// Create semi-transparent background
var bg = new Container();
var bgAsset = bg.attachAsset('lava', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 5,
scaleY: 5
});
bgAsset.alpha = 0.7;
bgAsset.tint = 0x000000;
self.addChild(bg);
// Create panel title
var titleText = new Text2('SETTINGS', {
size: 120,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0.5);
titleText.y = -500;
self.addChild(titleText);
// Create close button
var closeButton = new Container();
var closeText = new Text2('X', {
size: 80,
fill: 0xFFFFFF
});
closeText.anchor.set(0.5, 0.5);
closeButton.addChild(closeText);
closeButton.x = 800;
closeButton.y = -500;
closeButton.interactive = true;
self.addChild(closeButton);
// Paddle color selection section
var paddleLabel = new Text2('PADDLE COLOR', {
size: 80,
fill: 0xFFFFFF
});
paddleLabel.anchor.set(0.5, 0.5);
paddleLabel.y = -300;
self.addChild(paddleLabel);
// Color options for paddle
var paddleColors = [0xffbd00, 0xff0000, 0x00ff00, 0x0000ff, 0xff00ff, 0x00ffff, 0xffffff];
var paddleColorButtons = [];
for (var i = 0; i < paddleColors.length; i++) {
var colorBtn = new Container();
var colorAsset = colorBtn.attachAsset('ball', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 1.5
});
colorAsset.tint = paddleColors[i];
colorBtn.x = (i - 3) * 150;
colorBtn.y = -200;
colorBtn.interactive = true;
colorBtn.colorValue = paddleColors[i];
colorBtn.colorType = 'paddle';
paddleColorButtons.push(colorBtn);
self.addChild(colorBtn);
}
// Sound control section
var soundLabel = new Text2('SOUND SETTINGS', {
size: 80,
fill: 0xFFFFFF
});
soundLabel.anchor.set(0.5, 0.5);
soundLabel.y = 300;
self.addChild(soundLabel);
// Master volume controls
var masterVolumeLabel = new Text2('Master Volume', {
size: 50,
fill: 0xFFFFFF
});
masterVolumeLabel.anchor.set(0.5, 0.5);
masterVolumeLabel.y = 360;
self.addChild(masterVolumeLabel);
// Add master slider background
var masterSliderBg = new Container();
var masterSliderBgAsset = masterSliderBg.attachAsset('paddleMiddle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 0.5
});
masterSliderBgAsset.tint = 0x555555;
masterSliderBg.x = 0;
masterSliderBg.y = 400;
self.addChild(masterSliderBg);
// Add master slider handle
var masterSliderHandle = new Container();
var masterSliderHandleAsset = masterSliderHandle.attachAsset('ball', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 1.2
});
masterSliderHandleAsset.tint = 0xFFFFFF;
masterSliderHandle.interactive = true;
masterSliderHandle.y = 400;
self.addChild(masterSliderHandle);
// Music volume controls
var musicVolumeLabel = new Text2('Music Volume', {
size: 50,
fill: 0xFFFFFF
});
musicVolumeLabel.anchor.set(0.5, 0.5);
musicVolumeLabel.y = 460;
self.addChild(musicVolumeLabel);
// Add music slider background
var musicSliderBg = new Container();
var musicSliderBgAsset = musicSliderBg.attachAsset('paddleMiddle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 0.5
});
musicSliderBgAsset.tint = 0x555555;
musicSliderBg.x = 0;
musicSliderBg.y = 500;
self.addChild(musicSliderBg);
// Add music slider handle
var musicSliderHandle = new Container();
var musicSliderHandleAsset = musicSliderHandle.attachAsset('ball', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 1.2
});
musicSliderHandleAsset.tint = 0x00FFFF;
musicSliderHandle.interactive = true;
musicSliderHandle.y = 500;
self.addChild(musicSliderHandle);
// SFX volume controls
var sfxVolumeLabel = new Text2('SFX Volume', {
size: 50,
fill: 0xFFFFFF
});
sfxVolumeLabel.anchor.set(0.5, 0.5);
sfxVolumeLabel.y = 560;
self.addChild(sfxVolumeLabel);
// Add SFX slider background
var sfxSliderBg = new Container();
var sfxSliderBgAsset = sfxSliderBg.attachAsset('paddleMiddle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 0.5
});
sfxSliderBgAsset.tint = 0x555555;
sfxSliderBg.x = 0;
sfxSliderBg.y = 600;
self.addChild(sfxSliderBg);
// Add SFX slider handle
var sfxSliderHandle = new Container();
var sfxSliderHandleAsset = sfxSliderHandle.attachAsset('ball', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 1.2
});
sfxSliderHandleAsset.tint = 0xFF5500;
sfxSliderHandle.interactive = true;
sfxSliderHandle.y = 600;
self.addChild(sfxSliderHandle);
// Set initial slider positions based on stored volumes
var volume = storage.volume !== undefined ? storage.volume : 1.0;
var musicVolume = storage.musicVolume !== undefined ? storage.musicVolume : 1.0;
var sfxVolume = storage.sfxVolume !== undefined ? storage.sfxVolume : 1.0;
masterSliderHandle.x = (volume - 0.5) * 300; // Map volume 0-1 to position -150 to 150
musicSliderHandle.x = (musicVolume - 0.5) * 300;
sfxSliderHandle.x = (sfxVolume - 0.5) * 300;
// Ball color selection section
var ballLabel = new Text2('BALL COLOR', {
size: 80,
fill: 0xFFFFFF
});
ballLabel.anchor.set(0.5, 0.5);
ballLabel.y = 200;
self.addChild(ballLabel);
// Color options for ball
var ballColors = [0xffffff, 0xff0000, 0x00ff00, 0x0000ff, 0xffff00, 0xff00ff, 0x00ffff];
var ballColorButtons = [];
for (var j = 0; j < ballColors.length; j++) {
var ballBtn = new Container();
var ballAsset = ballBtn.attachAsset('ball', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 1.5
});
ballAsset.tint = ballColors[j];
ballBtn.x = (j - 3) * 150;
ballBtn.y = 100;
ballBtn.interactive = true;
ballBtn.colorValue = ballColors[j];
ballBtn.colorType = 'ball';
ballColorButtons.push(ballBtn);
self.addChild(ballBtn);
}
// Update UI to show selected colors
self.updateSelections = function () {
var paddleColor = storage.paddleColor || 0xffbd00;
var ballColor = storage.ballColor || 0xffffff;
// Update paddle color buttons
for (var i = 0; i < paddleColorButtons.length; i++) {
var btn = paddleColorButtons[i];
if (btn.colorValue === paddleColor) {
tween(btn, {
scaleX: 1.8,
scaleY: 1.8
}, {
duration: 200
});
} else {
tween(btn, {
scaleX: 1,
scaleY: 1
}, {
duration: 200
});
}
}
// Update ball color buttons
for (var j = 0; j < ballColorButtons.length; j++) {
var bBtn = ballColorButtons[j];
if (bBtn.colorValue === ballColor) {
tween(bBtn, {
scaleX: 1.8,
scaleY: 1.8
}, {
duration: 200
});
} else {
tween(bBtn, {
scaleX: 1,
scaleY: 1
}, {
duration: 200
});
}
}
};
// Register color button events for all buttons
function registerColorButtons() {
var isDragging = false;
// Volume slider handlers
var currentSlider = null;
// Master volume slider handlers
masterSliderHandle.down = function (x, y, obj) {
isDragging = true;
currentSlider = this;
tween(this, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 100
});
};
masterSliderHandle.move = function (x, y, obj) {
if (this.parent && isDragging && currentSlider === this) {
var localX = x;
// Clamp to slider bounds (-150 to 150)
if (localX < -150) {
localX = -150;
}
if (localX > 150) {
localX = 150;
}
this.x = localX;
// Convert position to volume (0 to 1)
var newVolume = (localX + 150) / 300;
// Store volume setting in storage for persistence
storage.volume = newVolume;
// Test sound for immediate feedback with updated volume
if (LK.ticks % 15 === 0) {
LK.getSound('bounce').play({
volume: newVolume
});
}
}
};
masterSliderHandle.up = function () {
if (currentSlider === this) {
isDragging = false;
currentSlider = null;
}
tween(this, {
scaleX: 1,
scaleY: 1
}, {
duration: 200,
easing: tween.elasticOut
});
};
// Music volume slider handlers
musicSliderHandle.down = function (x, y, obj) {
isDragging = true;
currentSlider = this;
tween(this, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 100
});
};
musicSliderHandle.move = function (x, y, obj) {
if (this.parent && isDragging && currentSlider === this) {
var localX = x;
// Clamp to slider bounds
if (localX < -150) {
localX = -150;
}
if (localX > 150) {
localX = 150;
}
this.x = localX;
// Convert position to volume (0 to 1)
var newMusicVolume = (localX + 150) / 300;
// Store music volume setting
storage.musicVolume = newMusicVolume;
// Give immediate feedback by adjusting current music
if (LK.ticks % 15 === 0) {
// Apply music volume change
var masterVolume = storage.volume !== undefined ? storage.volume : 1.0;
LK.playMusic('bgmusic', {
volume: masterVolume * newMusicVolume
});
}
}
};
musicSliderHandle.up = function () {
if (currentSlider === this) {
isDragging = false;
currentSlider = null;
}
tween(this, {
scaleX: 1,
scaleY: 1
}, {
duration: 200,
easing: tween.elasticOut
});
};
// SFX volume slider handlers
sfxSliderHandle.down = function (x, y, obj) {
isDragging = true;
currentSlider = this;
tween(this, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 100
});
};
sfxSliderHandle.move = function (x, y, obj) {
if (this.parent && isDragging && currentSlider === this) {
var localX = x;
// Clamp to slider bounds
if (localX < -150) {
localX = -150;
}
if (localX > 150) {
localX = 150;
}
this.x = localX;
// Convert position to volume (0 to 1)
var newSfxVolume = (localX + 150) / 300;
// Store sfx volume setting
storage.sfxVolume = newSfxVolume;
// Test sound for immediate feedback with updated volume
if (LK.ticks % 15 === 0) {
var masterVolume = storage.volume !== undefined ? storage.volume : 1.0;
LK.getSound('click').play({
volume: masterVolume * newSfxVolume
});
}
}
};
sfxSliderHandle.up = function () {
if (currentSlider === this) {
isDragging = false;
currentSlider = null;
}
tween(this, {
scaleX: 1,
scaleY: 1
}, {
duration: 200,
easing: tween.elasticOut
});
};
// Paddle color buttons
for (var i = 0; i < paddleColorButtons.length; i++) {
var pBtn = paddleColorButtons[i];
pBtn.down = function () {
tween(this, {
scaleX: 0.9,
scaleY: 0.9
}, {
duration: 100
});
};
pBtn.up = function () {
var btn = this;
tween(btn, {
scaleX: 1,
scaleY: 1
}, {
duration: 200,
easing: tween.elasticOut,
onFinish: function onFinish() {
storage.paddleColor = btn.colorValue;
self.updateSelections();
// Update game paddle colors
if (self.onColorChange) {
self.onColorChange();
}
}
});
};
}
// Ball color buttons
for (var j = 0; j < ballColorButtons.length; j++) {
var bBtn = ballColorButtons[j];
bBtn.down = function () {
tween(this, {
scaleX: 0.9,
scaleY: 0.9
}, {
duration: 100
});
};
bBtn.up = function () {
var btn = this;
tween(btn, {
scaleX: 1,
scaleY: 1
}, {
duration: 200,
easing: tween.elasticOut,
onFinish: function onFinish() {
storage.ballColor = btn.colorValue;
self.updateSelections();
// Update game ball colors
if (self.onColorChange) {
self.onColorChange();
}
}
});
};
}
}
// Close button event
closeButton.down = function () {
tween(this, {
scaleX: 0.9,
scaleY: 0.9
}, {
duration: 100
});
};
closeButton.up = function () {
tween(this, {
scaleX: 1,
scaleY: 1
}, {
duration: 200,
easing: tween.elasticOut,
onFinish: function onFinish() {
// Call close callback if exists
if (self.onClose) {
self.onClose();
}
}
});
};
// Initialize
registerColorButtons();
self.updateSelections();
return self;
});
var StartMenu = Container.expand(function () {
var self = Container.call(this);
// Create title text
var titleText = new Text2('Lava Bounce', {
size: 200,
fill: 0xFFBD00
});
titleText.anchor.set(0.5, 0.5);
titleText.y = -300;
self.addChild(titleText);
// Create title animation
function animateTitle() {
tween(titleText, {
y: -300 - 30
}, {
duration: 1000,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(titleText, {
y: -300 + 30
}, {
duration: 1000,
easing: tween.easeInOut,
onFinish: function onFinish() {
animateTitle();
}
});
}
});
}
// Start the title animation
animateTitle();
// Create start button
var startButton = new Container();
// Remove background asset but keep the text
var buttonText = new Text2('PLAY', {
size: 100,
fill: 0xFFFFFF
});
buttonText.anchor.set(0.5, 0.5);
startButton.addChild(buttonText);
startButton.interactive = true;
startButton.y = 0;
self.addChild(startButton);
// High score display
var highScoreText = new Text2('HIGH SCORE: 0', {
size: 60,
fill: 0xFFFFFF
});
highScoreText.anchor.set(0.5, 0.5);
highScoreText.y = 200;
self.addChild(highScoreText);
// Update high score display
self.updateHighScore = function (score) {
highScoreText.setText('HIGH SCORE: ' + score);
};
// Handle button press
startButton.down = function () {
// Play click sound with combined volume settings
var masterVolume = storage.volume !== undefined ? storage.volume : 1.0;
var sfxVolume = storage.sfxVolume !== undefined ? storage.sfxVolume : 1.0;
LK.getSound('click').play({
volume: masterVolume * sfxVolume
});
// Scale effect
tween(startButton, {
scaleX: 0.95,
scaleY: 0.95
}, {
duration: 100
});
};
// Handle button release
startButton.up = function () {
// Return to original scale with bounce
tween(startButton, {
scaleX: 1,
scaleY: 1
}, {
duration: 200,
easing: tween.elasticOut,
onFinish: function onFinish() {
// Notify game that start was pressed
if (self.onStart) {
self.onStart();
}
}
});
};
return self;
});
var WallParticles = Container.expand(function () {
var self = Container.call(this);
self.particles = [];
self.maxParticles = 15;
self.createParticle = function (x, y, side) {
var particle = new Container();
var particleGraphic = particle.attachAsset('ball', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.2 + Math.random() * 0.3,
scaleY: 0.2 + Math.random() * 0.3
});
// Use white or slightly colored particles
var colors = [0xFFFFFF, 0xFFFFAA, 0xAAFFFF];
particleGraphic.tint = colors[Math.floor(Math.random() * colors.length)];
particle.x = x;
particle.y = y;
particle.alpha = 1;
// Particles fly away from the wall
var speed = 2 + Math.random() * 3;
if (side === 'left') {
particle.speedX = speed;
} else {
particle.speedX = -speed;
}
particle.speedY = Math.random() * 6 - 3;
particle.rotationSpeed = (Math.random() - 0.5) * 0.2;
particle.life = 20 + Math.random() * 15;
particle.maxLife = particle.life;
self.addChild(particle);
self.particles.push(particle);
return particle;
};
self.update = function () {
for (var i = self.particles.length - 1; i >= 0; i--) {
var particle = self.particles[i];
particle.x += particle.speedX;
particle.y += particle.speedY;
particle.rotation += particle.rotationSpeed;
particle.speedX *= 0.95;
particle.speedY *= 0.95;
particle.life--;
particle.alpha = particle.life / particle.maxLife;
if (particle.life <= 0) {
self.removeChild(particle);
self.particles.splice(i, 1);
}
}
};
self.emitWallHit = function (x, y, side, count) {
for (var i = 0; i < count; i++) {
self.createParticle(x, y, side);
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x220000
});
/****
* Game Code
****/
// Game state
// Sounds removed
var gameActive = false;
var settingsPanelActive = false;
var confirmDialogActive = false;
// Game variables
var score = 0;
var highScore = storage.highScore || 0;
var level = 1;
var hitsToNextLevel = 20;
var difficulty = 1;
var difficultyIncreaseTimer = null;
var levelRequirement = 20; // Hits needed to complete each level
var mouseParticles = new MouseParticleSystem();
// Create back button
var backButton = new Container();
// Remove yellow box background, keeping just the arrow
var backText = new Text2('←', {
size: 60,
fill: 0xFFFFFF
});
backText.anchor.set(0.5, 0.5);
backButton.addChild(backText);
backButton.x = 2048 - 120;
backButton.y = 80;
backButton.interactive = true;
backButton.visible = false;
// Function to check if a display object is a child of this container
function containerContains(container, child) {
if (!container.children) {
return false;
}
for (var i = 0; i < container.children.length; i++) {
if (container.children[i] === child) {
return true;
}
}
return false;
}
;
// Load stored colors or use defaults
var paddleColor = storage.paddleColor || 0xffbd00;
var ballColor = storage.ballColor || 0xffffff;
// Create game objects
var paddle = new Paddle();
var ball = new Ball();
var lava = new Lava();
var particles = new ParticleSystem();
var wallParticles = new WallParticles();
var startMenu = new StartMenu();
var settingsPanel = new SettingsPanel();
var confirmDialog = new ConfirmDialog();
// Initialize settings panel
settingsPanel.x = 2048 / 2;
settingsPanel.y = 2732 / 2;
settingsPanel.visible = false;
settingsPanel.alpha = 0;
// Apply stored colors and volume to game objects
function applyStoredColors() {
// Apply paddle color
paddle.children.forEach(function (child) {
if (child.tint !== undefined) {
child.tint = paddleColor;
}
});
// Apply ball color
if (ball.children && ball.children.length > 0) {
ball.children[0].tint = ballColor;
}
// Apply stored volume settings - note: we don't need to set volume here
// as sounds will use the stored volume when played
var volume = storage.volume !== undefined ? storage.volume : 1.0;
// Don't need to set volume directly on sound objects since we'll use
// the volume parameter when playing sounds via LK.getSound().play({volume: x})
}
// Settings panel callbacks
settingsPanel.onColorChange = function () {
// Update color variables
paddleColor = storage.paddleColor || 0xffbd00;
ballColor = storage.ballColor || 0xffffff;
// Apply colors
applyStoredColors();
};
settingsPanel.onClose = function () {
// Hide settings panel with smoother, slower animation
tween(settingsPanel, {
alpha: 0,
scaleX: 0.5,
scaleY: 0.5
}, {
duration: 700,
easing: tween.easeOut,
onFinish: function onFinish() {
settingsPanel.visible = false;
settingsPanelActive = false;
}
});
};
// Start menu settings callback
startMenu.onSettings = function () {
// Show settings panel with animation
settingsPanel.visible = true;
settingsPanel.alpha = 0;
settingsPanelActive = true;
// Set initial scale for a gentler opening animation
settingsPanel.scale.set(0.5);
// Make sure we're not adding it multiple times
if (!containerContains(game, settingsPanel)) {
game.addChild(settingsPanel);
}
// Make sure settings panel is at the top layer
if (containerContains(game, settingsPanel)) {
game.removeChild(settingsPanel);
game.addChild(settingsPanel);
}
// Animate both alpha and scale with a smoother, slower animation
tween(settingsPanel, {
alpha: 1,
scaleX: 1,
scaleY: 1
}, {
duration: 800,
easing: tween.easeOut
});
};
// Initialize paddle position
paddle.x = 2048 / 2;
paddle.y = 2732 - 300;
// Initialize ball position
ball.reset();
// Initialize lava position to be at the bottom of the screen
lava.x = 2048 / 2;
lava.y = 2732 + lava.height / 2 - 100; // Position lava so only top part is visible
lava.baseY = lava.y; // Store the initial Y position for wave animation
lava.minY = lava.height / 2; // Minimum Y position to prevent running off top edge
lava.maxY = 2732 - lava.height / 2; // Maximum Y position to prevent running off bottom edge
// Create confetti system for level celebrations
var confetti = new Confetti();
// Position start menu in center of screen
startMenu.x = 2048 / 2;
startMenu.y = 2732 / 2;
startMenu.updateHighScore(highScore);
// Start menu callback
startMenu.onStart = function () {
startGame();
};
// Create settings button
var settingsButton = new Container();
var settingsText = new Text2('SETTINGS', {
size: 60,
fill: 0xFFFFFF
});
settingsText.anchor.set(0.5, 0.5);
settingsButton.addChild(settingsText);
settingsButton.interactive = true;
settingsButton.y = 300;
settingsButton.x = 0;
startMenu.addChild(settingsButton);
// Handle settings button press
settingsButton.down = function () {
var masterVolume = storage.volume !== undefined ? storage.volume : 1.0;
var sfxVolume = storage.sfxVolume !== undefined ? storage.sfxVolume : 1.0;
LK.getSound('click').play({
volume: masterVolume * sfxVolume
});
tween(settingsButton, {
scaleX: 0.9,
scaleY: 0.9
}, {
duration: 100
});
};
// Handle settings button release
settingsButton.up = function () {
tween(settingsButton, {
scaleX: 1,
scaleY: 1
}, {
duration: 200,
easing: tween.elasticOut,
onFinish: function onFinish() {
// Show settings panel
if (startMenu.onSettings) {
startMenu.onSettings();
}
}
});
};
// Create reset high score button to start menu
var resetButton = new Container();
var resetText = new Text2('RESET HIGH SCORE', {
size: 40,
fill: 0xFFFFFF
});
resetText.anchor.set(0.5, 0.5);
resetButton.addChild(resetText);
resetButton.interactive = true;
resetButton.y = 370;
resetButton.x = 0;
startMenu.addChild(resetButton);
// Handle reset button press
resetButton.down = function () {
var masterVolume = storage.volume !== undefined ? storage.volume : 1.0;
var sfxVolume = storage.sfxVolume !== undefined ? storage.sfxVolume : 1.0;
LK.getSound('click').play({
volume: masterVolume * sfxVolume
});
tween(resetButton, {
scaleX: 0.9,
scaleY: 0.9
}, {
duration: 100
});
};
// Handle reset button release
resetButton.up = function () {
tween(resetButton, {
scaleX: 1,
scaleY: 1
}, {
duration: 200,
easing: tween.elasticOut,
onFinish: function onFinish() {
// Show confirm dialog
showConfirmDialog();
}
});
};
// Add start menu to game initially
game.addChild(startMenu);
// Add settings panel to game (initially hidden)
game.addChild(settingsPanel);
// Add back button to game
game.addChild(backButton);
// Setup back button event handlers
backButton.down = function () {
var masterVolume = storage.volume !== undefined ? storage.volume : 1.0;
var sfxVolume = storage.sfxVolume !== undefined ? storage.sfxVolume : 1.0;
LK.getSound('click').play({
volume: masterVolume * sfxVolume
});
tween(this, {
scaleX: 0.9,
scaleY: 0.9
}, {
duration: 100
});
};
backButton.up = function () {
var btn = this;
tween(btn, {
scaleX: 1,
scaleY: 1
}, {
duration: 200,
easing: tween.elasticOut,
onFinish: function onFinish() {
returnToMenu();
}
});
};
// Initialize confirm dialog
confirmDialog.x = 2048 / 2;
confirmDialog.y = 2732 / 2;
confirmDialog.visible = false;
confirmDialog.alpha = 0;
// Setup confirm dialog callbacks
confirmDialog.onConfirm = function () {
// Reset high score to 0
highScore = 0;
storage.highScore = 0;
startMenu.updateHighScore(0);
highScoreTxt.setText('High Score: 0');
// Hide dialog
hideConfirmDialog();
};
confirmDialog.onCancel = function () {
// Just hide dialog
hideConfirmDialog();
};
// Function to show confirm dialog
function showConfirmDialog() {
confirmDialog.visible = true;
confirmDialogActive = true;
// Set initial state for a gentler animation
confirmDialog.scale.set(0.5);
confirmDialog.alpha = 0;
// Make sure it's not already added multiple times
if (containerContains(game, confirmDialog)) {
game.removeChild(confirmDialog);
}
game.addChild(confirmDialog);
// Make sure confirmDialog is at the top layer
if (containerContains(game, confirmDialog)) {
game.removeChild(confirmDialog);
game.addChild(confirmDialog);
}
// Add smoother, less aggressive animation when dialog opens
tween(confirmDialog, {
alpha: 1,
scaleX: 1.05,
scaleY: 1.05
}, {
duration: 700,
easing: tween.easeOut,
onFinish: function onFinish() {
// Scale back to normal size with a gentler effect
tween(confirmDialog, {
scaleX: 1,
scaleY: 1
}, {
duration: 300,
easing: tween.easeOut
});
}
});
}
// Function to hide confirm dialog
function hideConfirmDialog() {
tween(confirmDialog, {
alpha: 0,
scaleX: 0.5,
scaleY: 0.5
}, {
duration: 600,
easing: tween.easeOut,
onFinish: function onFinish() {
confirmDialog.visible = false;
confirmDialogActive = false;
}
});
}
// Function to start the game
function startGame() {
// Remove start menu
game.removeChild(startMenu);
// Add game objects
game.addChild(paddle);
game.addChild(ball);
game.addChild(lava);
game.addChild(particles);
game.addChild(confetti);
game.addChild(mouseParticles);
game.addChild(wallParticles);
// Setup wall hit callback for ball
ball.onWallHit = function (x, y, side) {
wallParticles.emitWallHit(x, y, side, 10);
};
// Add back button
backButton.visible = true;
// Apply color settings
applyStoredColors();
// Reset game state
score = 0;
level = 1;
difficulty = 1;
ball.reset();
// Play background music with fade in and proper combined volume
var masterVolume = storage.volume !== undefined ? storage.volume : 1.0;
var musicVolume = storage.musicVolume !== undefined ? storage.musicVolume : 1.0;
var combinedVolume = masterVolume * musicVolume;
LK.playMusic('bgmusic', {
fade: {
start: 0,
end: combinedVolume,
duration: 1000
}
});
// Start difficulty timer
if (difficultyIncreaseTimer) {
LK.clearInterval(difficultyIncreaseTimer);
}
difficultyIncreaseTimer = LK.setInterval(increaseDifficulty, 10000);
// Reset lava to its proper starting position at the bottom of the screen
lava.y = 2732 + lava.height / 2 - 100; // Position lava so only top part is visible
lava.baseY = lava.y; // Store the initial Y position for wave animation
// Stop any existing animations
tween.stop(lava, {
y: true
});
// Start the rise animation
lava.startRiseFall();
// Set game as active
gameActive = true;
// Make UI elements visible
scoreTxt.visible = true;
levelTxt.visible = true;
progressTxt.visible = true;
highScoreTxt.visible = true; // Show high score when game starts
// Update UI
updateScore();
levelTxt.setText('Level: ' + level);
progressTxt.setText('0/' + levelRequirement);
}
// Function to return to menu
function returnToMenu() {
// Remove game objects
game.removeChild(paddle);
game.removeChild(ball);
game.removeChild(lava);
game.removeChild(particles);
game.removeChild(confetti);
game.removeChild(wallParticles);
// Hide back button
backButton.visible = false;
// Update high score on menu
startMenu.updateHighScore(highScore);
// Add menu back
if (!containerContains(game, startMenu)) {
game.addChild(startMenu);
}
// Make sure settings button and reset button events are properly attached
if (settingsButton && !containerContains(startMenu, settingsButton)) {
startMenu.addChild(settingsButton);
}
if (resetButton && !containerContains(startMenu, resetButton)) {
startMenu.addChild(resetButton);
}
// Set game as inactive
gameActive = false;
// Hide UI elements
scoreTxt.visible = false;
levelTxt.visible = false;
progressTxt.visible = false;
// Fade out and stop music
var masterVolume = storage.volume !== undefined ? storage.volume : 1.0;
var musicVolume = storage.musicVolume !== undefined ? storage.musicVolume : 1.0;
var combinedVolume = masterVolume * musicVolume;
LK.playMusic('bgmusic', {
fade: {
start: combinedVolume,
end: 0,
duration: 800
}
});
LK.setTimeout(function () {
LK.stopMusic();
}, 800);
}
// Create score display
var scoreTxt = new Text2('Score: 0', {
size: 100,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0.5);
scoreTxt.y = 50;
scoreTxt.visible = false; // Hide score initially
// Create level display
var levelTxt = new Text2('Level: 1', {
size: 80,
fill: 0x00FFFF
});
levelTxt.anchor.set(0.5, 0);
levelTxt.y = 150;
levelTxt.visible = false; // Hide level initially
// Create level progress display
var progressTxt = new Text2('', {
size: 50,
fill: 0xFFFFFF
});
progressTxt.anchor.set(0.5, 0);
progressTxt.y = 30;
progressTxt.visible = false; // Hide progress initially
// Create high score display
var highScoreTxt = new Text2('High Score: 0', {
size: 80,
fill: 0xFFBD00
});
highScoreTxt.anchor.set(1, 0);
highScoreTxt.setText('High Score: ' + highScore);
highScoreTxt.y = 70;
highScoreTxt.visible = false; // Hide high score initially
// Description text removed
LK.gui.topRight.addChild(highScoreTxt);
LK.gui.center.addChild(scoreTxt);
LK.gui.top.addChild(levelTxt);
LK.gui.top.addChild(progressTxt);
// Touch/drag handling for paddle movement
var isDragging = false;
game.down = function (x, y, obj) {
// Play click sound with combined volume settings
var masterVolume = storage.volume !== undefined ? storage.volume : 1.0;
var sfxVolume = storage.sfxVolume !== undefined ? storage.sfxVolume : 1.0;
LK.getSound('click').play({
volume: masterVolume * sfxVolume
});
isDragging = true;
paddle.x = x;
// Emit more particles at mouse/touch position for improved visibility
mouseParticles.emitAt(x, y, 35);
};
game.move = function (x, y, obj) {
if (isDragging) {
paddle.x = x;
// Clamp paddle position to keep it within screen bounds
if (paddle.x < paddle.width / 2) {
paddle.x = paddle.width / 2;
} else if (paddle.x > 2048 - paddle.width / 2) {
paddle.x = 2048 - paddle.width / 2;
}
}
};
game.up = function (x, y, obj) {
isDragging = false;
};
// Function to update score display
function updateScore() {
scoreTxt.setText(score.toString());
// Update progress to next level
var progress = score % levelRequirement;
var remaining = levelRequirement - progress;
progressTxt.setText(progress + '/' + levelRequirement);
// Check for level completion
if (progress === 0 && score > 0) {
// Level up!
level = Math.floor(score / levelRequirement) + 1;
levelTxt.setText('Level: ' + level);
// Celebrate level completion
celebrateNewLevel();
// Increase difficulty with each level
difficulty = 1 + (level - 1) * 0.3;
if (difficulty > 3) {
difficulty = 3;
}
// Update game properties based on new difficulty
ball.gravity = 0.2 * difficulty;
ball.maxSpeed = 15 * difficulty;
}
// Update high score if current score is higher
if (score > highScore) {
highScore = score;
storage.highScore = highScore;
highScoreTxt.setText('High Score: ' + highScore);
}
}
// Function to celebrate new level
function celebrateNewLevel() {
// Play level up sound with combined volume settings
var masterVolume = storage.volume !== undefined ? storage.volume : 1.0;
var sfxVolume = storage.sfxVolume !== undefined ? storage.sfxVolume : 1.0;
LK.getSound('levelup').play({
volume: masterVolume * sfxVolume
});
// Show celebratory text
var celebrationText = new Text2('LEVEL ' + level + '!', {
size: 150,
fill: 0xFFFF00
});
celebrationText.anchor.set(0.5, 0.5);
LK.gui.center.addChild(celebrationText);
// Make text appear with scale animation
celebrationText.scale.set(0.1);
tween(celebrationText, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 500,
easing: tween.elasticOut,
onFinish: function onFinish() {
tween(celebrationText, {
scaleX: 1,
scaleY: 1
}, {
duration: 200,
onFinish: function onFinish() {
// Fade out after a moment
LK.setTimeout(function () {
tween(celebrationText, {
alpha: 0
}, {
duration: 500,
easing: tween.easeOut,
onFinish: function onFinish() {
LK.gui.center.removeChild(celebrationText);
}
});
}, 1000);
}
});
}
});
// Create confetti bursts
for (var i = 0; i < 5; i++) {
LK.setTimeout(function () {
// Create multiple confetti bursts across screen
confetti.emit(Math.random() * 2048, 500, 30);
}, i * 200);
}
// Flash screen for celebration
LK.effects.flashScreen(0x00FFFF, 300);
// Sounds and music removed
}
// Function to increase difficulty over time
function increaseDifficulty() {
difficulty += 0.1;
// Cap maximum difficulty
if (difficulty > 2) {
difficulty = 2;
LK.clearInterval(difficultyIncreaseTimer);
}
// Update ball properties based on difficulty
ball.gravity = 0.1 * difficulty;
ball.maxSpeed = 10 * difficulty;
// Music adjustment removed
}
// Start difficulty increase timer
difficultyIncreaseTimer = LK.setInterval(increaseDifficulty, 10000);
// Music will be played when game starts
// Not playing background music initially to allow menu to be silent
// Game update loop
game.update = function () {
// If game is not active or UI panels are active, don't update game
if (!gameActive || settingsPanelActive || confirmDialogActive) {
return;
}
// Update ball position
ball.update();
// Update lava waves
lava.update();
// Check for collision with paddle
if (ball.speedY > 0 && ball.y + ball.height / 2 >= paddle.y - paddle.height / 2 && ball.y - ball.height / 2 <= paddle.y + paddle.height / 2 && ball.x + ball.width / 2 >= paddle.x - paddle.width / 2 && ball.x - ball.width / 2 <= paddle.x + paddle.width / 2) {
// Bounce the ball in a random direction when it hits the paddle
var angle = Math.random() * Math.PI * 0.7 - Math.PI * 0.35; // Random angle between -35 and +35 degrees
var speed = Math.sqrt(ball.speedX * ball.speedX + ball.speedY * ball.speedY) * 1.5; // Keep overall speed but increase by 1.5x
ball.speedY = -Math.cos(angle) * speed; // Vertical component (mostly upward)
ball.speedX = Math.sin(angle) * speed; // Horizontal component
// Add additional horizontal velocity based on where the ball hit the paddle
var hitPosition = (ball.x - paddle.x) / (paddle.width / 2);
ball.speedX += hitPosition * 5; // Add some influence from hit position, but less than before
// Flash paddle to indicate hit
LK.effects.flashObject(paddle, 0xffffff, 200);
// Emit particles at collision point
particles.emitAt(ball.x, paddle.y - paddle.height / 2, 0xffbd00, 15);
// Add scale animation to paddle for bounce feedback
tween(paddle, {
scaleY: 0.7,
y: paddle.y + 10
}, {
duration: 100,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(paddle, {
scaleY: 1,
y: 2732 - 300
}, {
duration: 150,
easing: tween.elasticOut
});
}
});
// Play bounce sound when ball hits paddle
var masterVolume = storage.volume !== undefined ? storage.volume : 1.0;
var sfxVolume = storage.sfxVolume !== undefined ? storage.sfxVolume : 1.0;
LK.getSound('bounce').play({
volume: masterVolume * sfxVolume
});
// Increment score
score++;
updateScore();
}
// Check if ball touches lava
if (ball.y + ball.height / 2 >= lava.y - lava.height / 2) {
// Play lava sound with combined volume settings
var masterVolume = storage.volume !== undefined ? storage.volume : 1.0;
var sfxVolume = storage.sfxVolume !== undefined ? storage.sfxVolume : 1.0;
LK.getSound('lava').play({
volume: masterVolume * sfxVolume
});
LK.getSound('gameover').play({
volume: masterVolume * sfxVolume
});
// Flash screen red
LK.effects.flashScreen(0xff0000, 500);
// Create lava splash effect
particles.emitAt(ball.x, lava.y - lava.height / 2, 0xff3300, 30);
// Stop any current lava animations
tween.stop(lava, {
y: true
});
// Make lava "jump" without moving up or down
tween(lava, {
scaleY: 1.2
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(lava, {
scaleY: 1
}, {
duration: 300,
easing: tween.elasticOut
});
}
});
// Just reset the game without returning to menu
// Reset score and level
score = 0;
level = 1;
levelTxt.setText('Level: ' + level);
progressTxt.setText('0/' + levelRequirement);
updateScore();
// Reset ball
ball.reset();
// Reset difficulty
difficulty = 1;
ball.gravity = 0.2;
ball.maxSpeed = 15;
// Restart difficulty increase timer
LK.clearInterval(difficultyIncreaseTimer);
difficultyIncreaseTimer = LK.setInterval(increaseDifficulty, 10000);
// Music removed
}
// If ball goes too high, change direction
if (ball.y < 100) {
ball.speedY = Math.abs(ball.speedY) * 0.5;
}
// Update particles
particles.update();
// Update confetti
confetti.update();
// Update mouse particles
mouseParticles.update();
// Update wall particles
wallParticles.update();
};