/****
* 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();
};