User prompt
The paddle keeps getting bigger fix that āŖš” Consider importing and using the following plugins: @upit/tween.v1
User prompt
Fix the error āŖš” Consider importing and using the following plugins: @upit/tween.v1
User prompt
Please fix the bug: 'Timeout.tick error: Invalid end type for property fill: undefined' in or related to this line: 'tween(scoreTxt, {' Line Number: 1828 āŖš” Consider importing and using the following plugins: @upit/tween.v1
User prompt
Add better animations āŖš” Consider importing and using the following plugins: @upit/tween.v1
User prompt
Add animations to everything āŖš” Consider importing and using the following plugins: @upit/tween.v1
User prompt
add volume sliders rather than presets āŖš” Consider importing and using the following plugins: @upit/tween.v1
User prompt
now add an extra page of settings for sounds in game and have it on another page and to get to the sound settings you click a down arrow in the settings that pulls up the sound settings and does a little page flip animation āŖš” Consider importing and using the following plugins: @upit/tween.v1
User prompt
add an animation when you enter and exit the settings āŖš” Consider importing and using the following plugins: @upit/tween.v1
User prompt
make the settings better to make every part of the game customizable like visualy āŖš” Consider importing and using the following plugins: @upit/storage.v1
User prompt
center the 'new high score' text
User prompt
animate the lava āŖš” Consider importing and using the following plugins: @upit/tween.v1
User prompt
put the select game mode text just a bit higher
User prompt
make the box corners in the select game mode screen rounded
User prompt
add some different game modes that the player can play
User prompt
add a back button so you can go to the start screen
User prompt
when ever the ball hits the paddle or a wall add particles
User prompt
make the radius of where the particles go smaller
User prompt
make them smaller
User prompt
make them smaller
User prompt
make the particles a bit smaller
User prompt
add particles when the cursor is clicked make them big āŖš” Consider importing and using the following plugins: @upit/tween.v1
User prompt
add a click sound when ever the cursor is clicked
User prompt
make it so when you go past level 3 the music changes to heavy metal but before that is relaxing music
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
highScore: 0
});
/****
* Classes
****/
var Ball = Container.expand(function () {
var self = Container.call(this);
var ballGraphic = self.attachAsset('ball', {
anchorX: 0.5,
anchorY: 0.5
});
self.speedX = 0;
self.speedY = 0;
self.active = true;
self.trailCounter = 0;
self.gravity = 0.18; // Further reduced gravity for slower falling
self.airResistance = 0.995; // Slightly increased air resistance for slower movement
self.bounciness = 0.92; // Lower bounciness for less energetic bounces
self.spin = 0; // Ball spin (affects horizontal movement)
self.lastHitPos = 0; // Last hit position on paddle (for spin calculation)
self.reset = function (speedMultiplier) {
// Set position to center of screen
self.x = 2048 / 2;
self.y = 2732 / 2;
// Generate a random angle between 0 and 2Ļ (full 360 degrees)
var randomAngle = Math.random() * Math.PI * 2;
// Set initial speed magnitude
var speedMagnitude = 15 * speedMultiplier;
// Calculate velocity components based on random angle
self.speedX = Math.cos(randomAngle) * speedMagnitude;
self.speedY = Math.sin(randomAngle) * speedMagnitude;
self.active = true;
self.spin = 0; // Reset spin
self.lastHitPos = 0; // Reset last hit position
};
self.update = function () {
if (!self.active) {
return;
}
// Store last positions for collision detection
var lastX = self.x;
var lastY = self.y;
// Apply gravity - increases vertical speed over time
self.speedY += self.gravity;
// Apply air resistance
self.speedX *= self.airResistance;
self.speedY *= self.airResistance;
// Apply spin effect to horizontal movement
self.speedX += self.spin * 0.1;
// Gradually reduce spin over time
self.spin *= 0.98;
// Apply velocity
self.x += self.speedX;
self.y += self.speedY;
// Add trail effect based on speed
self.trailCounter++;
// Adjust trail frequency based on speed - faster speed = more frequent trails
var trailFrequency = Math.max(1, 5 - Math.floor((Math.abs(self.speedX) + Math.abs(self.speedY)) / 10));
// Create more trails at higher speeds
if (self.trailCounter > trailFrequency) {
self.trailCounter = 0;
var trail = LK.getAsset('ball', {
anchorX: 0.5,
anchorY: 0.5,
x: lastX,
y: lastY
});
// Only create trail if trails are enabled
if (!visualSettings.trailsEnabled) {
self.trailCounter = 0;
return;
}
// Adjust trail size based on ball speed and settings
var speedFactor = Math.min(1, (Math.abs(self.speedX) + Math.abs(self.speedY)) / 40);
var trailSize = (0.7 - speedFactor * 0.2) * visualSettings.ballSize * visualSettings.trailIntensity;
trail.scale.set(self.scale.x * trailSize, self.scale.y * trailSize);
trail.tint = visualSettings.ballColor;
// Higher alpha for faster speeds, adjusted by trail intensity
trail.alpha = (0.5 + speedFactor * 0.3) * visualSettings.trailIntensity;
game.addChildAt(trail, game.getChildIndex(self));
// Fade out and remove trail - faster trails disappear quicker, adjusted by animation speed
var trailDuration = (300 - speedFactor * 150) / visualSettings.animationSpeed;
tween(trail, {
alpha: 0,
scaleX: trail.scale.x * 0.5,
scaleY: trail.scale.y * 0.5
}, {
duration: trailDuration,
onFinish: function onFinish() {
trail.destroy();
}
});
}
// Rotate the ball based on horizontal speed to show rolling effect
ballGraphic.rotation += self.speedX * 0.05;
// Bounce off sides with more random physics
if (self.x < 20 || self.x > 2028) {
// Create particles at wall collision point
createCollisionParticles(self.x, self.y);
// Flip the horizontal direction
self.speedX = -self.speedX * self.bounciness * 1.2;
// Add significant random variation to both speed components
self.speedX += Math.random() * 6 - 3; // Major randomness on x-axis
self.speedY += Math.random() * 4 - 2; // Add randomness to y-axis on wall bounce too
// Chance for a very wild bounce (20% chance)
if (Math.random() < 0.2) {
self.speedX *= 0.5 + Math.random();
self.speedY *= 0.5 + Math.random();
}
// Keep the ball within the game boundaries
self.x = Math.max(20, Math.min(2028, self.x));
// Play bounce sound if soundEnabled is true
if (soundEnabled) {
LK.getSound('bounce').play();
}
}
// Check if ball hits the top of the screen with more random bounces
if (self.y < 20) {
// Create particles at ceiling collision point
createCollisionParticles(self.x, self.y);
// Flip the vertical direction
self.speedY = -self.speedY * self.bounciness * 1.25;
// Add significant random variation to both speed components
self.speedX += Math.random() * 5 - 2.5; // Add randomness to x-axis on ceiling bounce
self.speedY += Math.random() * 5 - 2.5; // Major randomness on y-axis
// Chance for a very wild bounce (20% chance)
if (Math.random() < 0.2) {
// Completely random direction after ceiling hit
var randomAngle = Math.random() * Math.PI + Math.PI; // Angle in bottom half
var currentSpeed = Math.sqrt(self.speedX * self.speedX + self.speedY * self.speedY);
self.speedX = Math.cos(randomAngle) * currentSpeed;
self.speedY = Math.sin(randomAngle) * currentSpeed;
}
self.y = 20;
// Play bounce sound if soundEnabled is true
if (soundEnabled) {
LK.getSound('bounce').play();
}
}
};
return self;
});
var DiagonalStripe = Container.expand(function () {
var self = Container.call(this);
// Create a shape for the diagonal stripe
var stripeGraphic = self.attachAsset('background', {
anchorX: 0,
anchorY: 0
});
// Configure the stripe appearance
stripeGraphic.width = 3000; // Increased width to extend past screen edges
stripeGraphic.height = 100;
stripeGraphic.tint = 0xffffff; // White
// Initial position and rotation
stripeGraphic.rotation = Math.PI / 4; // 45 degrees in radians
// Position it to extend past screen edges
stripeGraphic.x = -500; // Start before the left edge
// Empty update method (stripe will be still)
self.update = function () {
// No animation - stripe remains still
};
return self;
});
var Paddle = Container.expand(function () {
var self = Container.call(this);
// Create the main paddle base - middle rectangle section
var paddleGraphic = self.attachAsset('paddle', {
anchorX: 0.5,
anchorY: 0.5
});
paddleGraphic.tint = 0xFFB612; // Set paddle color to #FFB612
// Create the left rounded end (circle shape)
var leftEnd = LK.getAsset('ball', {
// Using the ball asset as it's a circle
anchorX: 0.5,
anchorY: 0.5,
width: paddleGraphic.height,
height: paddleGraphic.height,
tint: 0xFFB612
});
leftEnd.x = -paddleGraphic.width / 2 + leftEnd.width / 2;
self.addChild(leftEnd);
// Create the right rounded end (circle shape)
var rightEnd = LK.getAsset('ball', {
// Using the ball asset as it's a circle
anchorX: 0.5,
anchorY: 0.5,
width: paddleGraphic.height,
height: paddleGraphic.height,
tint: 0xFFB612
});
rightEnd.x = paddleGraphic.width / 2 - rightEnd.width / 2;
// Make sure the right end is the correct color
rightEnd.tint = 0xFFB612;
self.addChild(rightEnd);
// Trim the main paddle to accommodate rounded ends
paddleGraphic.width = paddleGraphic.width - paddleGraphic.height;
self.width = paddleGraphic.width + paddleGraphic.height; // Total width includes the circles
self.height = paddleGraphic.height;
self.update = function () {
// Keep paddle within screen bounds
self.x = Math.max(self.width / 2, Math.min(2048 - self.width / 2, self.x));
};
return self;
});
var Particle = Container.expand(function () {
var self = Container.call(this);
var particleGraphic = self.attachAsset('ball', {
anchorX: 0.5,
anchorY: 0.5
});
// Initial size scale based on settings
var particleScale = 0.5 * visualSettings.particleSize * visualSettings.particleIntensity;
self.scale.set(particleScale, particleScale);
// Particle colors from settings
particleGraphic.tint = visualSettings.particleColors[Math.floor(Math.random() * visualSettings.particleColors.length)];
// Random speed and direction
var angle = Math.random() * Math.PI * 2;
var speed = 2 + Math.random() * 8;
self.vx = Math.cos(angle) * speed;
self.vy = Math.sin(angle) * speed;
// Random rotation
self.rotationSpeed = (Math.random() - 0.5) * 0.2;
// Particle lifespan tracking
self.lifetime = 0;
self.maxLifetime = 30 + Math.random() * 30;
// Update function
self.update = function () {
// Update position with reduced movement (smaller radius)
self.x += self.vx * 0.5; // Reduce horizontal movement by 50%
self.vy += 0.1; // Gravity effect
self.y += self.vy * 0.5; // Reduce vertical movement by 50%
// Rotate particle
particleGraphic.rotation += self.rotationSpeed;
// Update lifetime
self.lifetime++;
// Fade out based on lifetime
if (self.lifetime > self.maxLifetime * 0.7) {
self.alpha = 1 - (self.lifetime - self.maxLifetime * 0.7) / (self.maxLifetime * 0.3);
}
// Remove when lifetime is over
if (self.lifetime >= self.maxLifetime) {
self.active = false;
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0xffffff // Light sky blue for a calmer atmosphere
});
/****
* Game Code
****/
// Game state management
function _typeof(o) {
"@babel/helpers - typeof";
return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) {
return typeof o;
} : function (o) {
return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o;
}, _typeof(o);
}
function _defineProperty(e, r, t) {
return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
value: t,
enumerable: !0,
configurable: !0,
writable: !0
}) : e[r] = t, e;
}
function _toPropertyKey(t) {
var i = _toPrimitive(t, "string");
return "symbol" == _typeof(i) ? i : i + "";
}
function _toPrimitive(t, r) {
if ("object" != _typeof(t) || !t) return t;
var e = t[Symbol.toPrimitive];
if (void 0 !== e) {
var i = e.call(t, r || "default");
if ("object" != _typeof(i)) return i;
throw new TypeError("@@toPrimitive must return a primitive value.");
}
return ("string" === r ? String : Number)(t);
}
var GAME_STATE = {
MENU: 0,
PLAYING: 1,
SETTINGS: 2,
MODE_SELECT: 3
};
var currentState = GAME_STATE.MENU;
// Game modes
var GAME_MODES = {
CLASSIC: 0,
SPEED_DEMON: 1,
MULTI_BALL: 2,
SURVIVAL: 3
};
var currentGameMode = GAME_MODES.CLASSIC;
// Game mode configurations
var gameModeConfigs = _defineProperty(_defineProperty(_defineProperty(_defineProperty({}, GAME_MODES.CLASSIC, {
name: "Classic Mode",
description: "The original Lava Bounce experience.\nBounce balls and level up gradually.",
initialSpeed: 1.5,
speedIncrease: 0.4,
maxBalls: 1,
hitsPerLevel: 25
}), GAME_MODES.SPEED_DEMON, {
name: "Speed Demon",
description: "Fast-paced action from the start!\nHigh speed, quick reflexes required.",
initialSpeed: 3.0,
speedIncrease: 0.8,
maxBalls: 1,
hitsPerLevel: 15
}), GAME_MODES.MULTI_BALL, {
name: "Multi-Ball Madness",
description: "Multiple balls create chaos!\nManage several balls at once.",
initialSpeed: 1.2,
speedIncrease: 0.3,
maxBalls: 3,
hitsPerLevel: 35
}), GAME_MODES.SURVIVAL, {
name: "Survival Mode",
description: "How long can you last?\nIncreasing difficulty, one life only.",
initialSpeed: 1.0,
speedIncrease: 0.6,
maxBalls: 1,
hitsPerLevel: 20
});
// Visual customization settings with defaults
var visualSettings = {
// Colors
paddleColor: storage.paddleColor || 0xFFB612,
ballColor: storage.ballColor || 0xFFB612,
lavaColor: storage.lavaColor || 0xC60C30,
backgroundColor: storage.backgroundColor || 0xFFFFFF,
textColor: storage.textColor || 0x101820,
uiColor: storage.uiColor || 0xFFFFFF,
particleColors: storage.particleColors || [0xFFB612, 0xC60C30, 0x003087, 0xFFFFFF],
// Effects
trailsEnabled: storage.trailsEnabled !== undefined ? storage.trailsEnabled : true,
particlesEnabled: storage.particlesEnabled !== undefined ? storage.particlesEnabled : true,
screenShakeEnabled: storage.screenShakeEnabled !== undefined ? storage.screenShakeEnabled : true,
lavaAnimationEnabled: storage.lavaAnimationEnabled !== undefined ? storage.lavaAnimationEnabled : true,
// Sizes
ballSize: storage.ballSize || 1.0,
paddleSize: storage.paddleSize || 1.0,
textSize: storage.textSize || 1.0,
particleSize: storage.particleSize || 1.0,
// Visual intensity
trailIntensity: storage.trailIntensity || 1.0,
particleIntensity: storage.particleIntensity || 1.0,
animationSpeed: storage.animationSpeed || 1.0
};
// Default colors for game elements - using customizable settings
var gameColors = {
paddle: visualSettings.paddleColor,
ball: visualSettings.ballColor,
lava: visualSettings.lavaColor
};
// Function to save visual settings
function saveVisualSettings() {
storage.paddleColor = visualSettings.paddleColor;
storage.ballColor = visualSettings.ballColor;
storage.lavaColor = visualSettings.lavaColor;
storage.backgroundColor = visualSettings.backgroundColor;
storage.textColor = visualSettings.textColor;
storage.uiColor = visualSettings.uiColor;
storage.particleColors = visualSettings.particleColors;
storage.trailsEnabled = visualSettings.trailsEnabled;
storage.particlesEnabled = visualSettings.particlesEnabled;
storage.screenShakeEnabled = visualSettings.screenShakeEnabled;
storage.lavaAnimationEnabled = visualSettings.lavaAnimationEnabled;
storage.ballSize = visualSettings.ballSize;
storage.paddleSize = visualSettings.paddleSize;
storage.textSize = visualSettings.textSize;
storage.particleSize = visualSettings.particleSize;
storage.trailIntensity = visualSettings.trailIntensity;
storage.particleIntensity = visualSettings.particleIntensity;
storage.animationSpeed = visualSettings.animationSpeed;
}
// Function to apply visual settings to game elements
function applyVisualSettings() {
// Update game colors
gameColors.paddle = visualSettings.paddleColor;
gameColors.ball = visualSettings.ballColor;
gameColors.lava = visualSettings.lavaColor;
// Update background color
if (game) {
game.setBackgroundColor(visualSettings.backgroundColor);
}
if (background) {
background.tint = visualSettings.backgroundColor;
}
if (menuBackground) {
menuBackground.getChildAt(1).tint = visualSettings.backgroundColor;
}
}
// Make game accessible to other functions
var gameInstance = game;
// Game variables
var background;
var paddle;
var lava;
var balls = [];
var particles = []; // Array to track active particles
var score = 0;
var highScore = storage.highScore || 0;
var level = 1;
var combo = 0;
var lastBallHit = 0;
var gameActive = false;
var speedMultiplier = 1.0;
var maxBalls = 1;
var ballsInPlay = 0;
var spawnInterval;
var hitsToNextLevel = 25;
var currentHits = 0;
var hitCounterText;
// UI elements
var scoreTxt;
var levelTxt;
var comboTxt;
var highScoreTxt;
var speedTxt;
var backButton;
// Default sound settings
var soundEnabled = storage.soundEnabled !== undefined ? storage.soundEnabled : true;
var musicEnabled = storage.musicEnabled !== undefined ? storage.musicEnabled : true;
// Initialize game elements (called when starting game)
function initializeGameElements() {
if (!background) {
// Create background
background = LK.getAsset('background', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0
});
game.addChild(background);
// Initialize lava
lava = LK.getAsset('lava', {
anchorX: 0.5,
anchorY: 0,
x: 2048 / 2,
y: 2732 - 200
});
lava.tint = visualSettings.lavaColor;
game.addChild(lava);
// Start lava animation
animateLava();
// Initialize paddle
paddle = new Paddle();
paddle.x = 2048 / 2;
paddle.y = 2732 - 250;
paddle.getChildAt(0).tint = visualSettings.paddleColor;
// Apply paddle size scaling
paddle.scale.set(visualSettings.paddleSize, visualSettings.paddleSize);
game.addChild(paddle);
// Diagonal stripe removed from here and placed in menu
// Create hit counter text
scoreTxt = new Text2('0', {
size: 180,
fill: 0x101820
});
scoreTxt.anchor.set(0.5, 0.5);
// Position score text precisely in the center of the screen
scoreTxt.x = 2048 / 2; // True center horizontally
scoreTxt.y = 2732 / 2; // True center vertically
game.addChild(scoreTxt);
// Create level text
levelTxt = new Text2('Level: 1', {
size: 70,
fill: 0xFFFFFF
});
levelTxt.anchor.set(1, 0);
levelTxt.x = 2000;
levelTxt.y = 50;
LK.gui.addChild(levelTxt);
// Create combo text
comboTxt = new Text2('', {
size: 60,
fill: 0xFFFFFF
});
comboTxt.anchor.set(0.5, 0);
comboTxt.x = 1024;
comboTxt.y = 50;
comboTxt.alpha = 0;
LK.gui.addChild(comboTxt);
// Create high score text
highScoreTxt = new Text2('High Score: ' + highScore, {
size: 60,
fill: 0xffb612
});
highScoreTxt.anchor.set(1, 0);
highScoreTxt.x = 2000;
highScoreTxt.y = 130; // Position below hit counter
game.addChild(highScoreTxt); // Add directly to game to ensure visibility
// Create speed indicator text
speedTxt = new Text2('Speed: x' + speedMultiplier.toFixed(1), {
size: 60,
fill: 0xffb612
});
speedTxt.anchor.set(0, 0);
speedTxt.x = 48;
speedTxt.y = 50;
game.addChild(speedTxt);
// Create game mode indicator
var modeIndicator = new Text2(gameModeConfigs[currentGameMode].name, {
size: 50,
fill: 0x003087
});
modeIndicator.anchor.set(0, 0);
modeIndicator.x = 48;
modeIndicator.y = 130;
game.addChild(modeIndicator);
// Create hit counter text
hitCounterText = new Text2(currentHits + '/25', {
size: 70,
fill: 0x003087
});
hitCounterText.anchor.set(0.5, 0);
hitCounterText.x = 1024;
hitCounterText.y = 150; // More visible position at top of screen
game.addChild(hitCounterText); // Add directly to game to ensure visibility
// Create back button
backButton = new Text2('ā Menu', {
size: 50,
fill: 0xFFB612
});
backButton.anchor.set(0, 0);
backButton.x = 120; // Position to avoid platform menu icon
backButton.y = 120;
backButton.interactive = true;
backButton.down = function () {
if (soundEnabled) {
LK.getSound('click').play();
}
// Stop current game
gameActive = false;
// Clear balls
for (var i = 0; i < balls.length; i++) {
balls[i].destroy();
}
balls = [];
ballsInPlay = 0;
// Hide game elements
hideGameElements();
// Show menu
showMenu();
};
game.addChild(backButton);
}
// Show game elements
background.visible = true;
lava.visible = true;
paddle.visible = true;
scoreTxt.visible = true;
levelTxt.visible = true;
comboTxt.visible = true;
highScoreTxt.visible = true;
hitCounterText.visible = true;
backButton.visible = true;
}
// Create menu elements
var titleText;
var startButton;
var settingsButton;
var settingsPanel;
var menuBackground;
// Initialize menu
initializeMenu();
function initializeMenu() {
// Play menu music if enabled
if (musicEnabled) {
LK.playMusic('menuMusic', {
fade: {
start: 0,
end: 0.6,
duration: 1500
}
});
}
// Create menu background
menuBackground = new Container();
var menuBg = LK.getAsset('background', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0,
tint: 0xFFFFFF // White color for menu background
});
// Create a border by adding a slightly larger background behind it
var menuBorder = LK.getAsset('background', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0,
tint: 0xA5ACAF // Border color set to #A5ACAF
});
menuBorder.width = 2048 + 10 * 2; // Adding 10px on each side
menuBorder.height = 2732 + 10 * 2; // Adding 10px on each side
menuBorder.x = -10; // Position it 10px to the left
menuBorder.y = -10; // Position it 10px to the top
menuBackground.addChild(menuBorder);
menuBackground.addChild(menuBg);
game.addChild(menuBackground);
// Diagonal stripe removed from menu
// Create game title
titleText = new Text2('Lava Bounce', {
size: 150,
fill: 0x101820
});
titleText.anchor.set(0.5, 0);
titleText.x = 1024;
titleText.y = 200;
game.addChild(titleText);
// Animate the title to rotate back and forth
function animateTitleRotation() {
// Rotate to one side
tween(titleText, {
rotation: 0.1 // Slight rotation to the right (in radians)
}, {
duration: 1000,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Rotate to the other side
tween(titleText, {
rotation: -0.1 // Slight rotation to the left (in radians)
}, {
duration: 1000,
easing: tween.easeInOut,
onFinish: animateTitleRotation // Loop the animation
});
}
});
}
// Start the title rotation animation
animateTitleRotation();
// Create the "2" as a separate, larger text element
var titleNumber = new Text2('2', {
size: 200,
// Bigger than the main title text
fill: 0xFFB612 // Gold color
});
titleNumber.anchor.set(0.5, 0);
titleNumber.x = 1024; // Centered horizontally
titleNumber.y = 350; // Positioned below the main title
game.addChild(titleNumber);
// Animate the "2" with a continuous bounce effect
function animateTitle2() {
// Bounce up animation
tween(titleNumber, {
y: 320,
// Move up slightly
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 700,
onFinish: function onFinish() {
// Bounce down animation
tween(titleNumber, {
y: 350,
// Back to original position
scaleX: 1,
scaleY: 1
}, {
duration: 700,
onFinish: animateTitle2 // Loop the animation
});
}
});
}
// Start the animation
animateTitle2();
// Create start button
startButton = new Text2('Start Game', {
size: 90,
fill: 0xFFB612
});
startButton.anchor.set(0.5, 0.5);
startButton.x = 1024;
startButton.y = 1200;
startButton.interactive = true;
startButton.isHovered = false; // Track hover state
startButton.move = function (x, y, obj) {
// Check if cursor is over the button
if (x >= startButton.x - startButton.width / 2 && x <= startButton.x + startButton.width / 2 && y >= startButton.y - startButton.height / 2 && y <= startButton.y + startButton.height / 2) {
// Start animation only if not already hovering
if (!startButton.isHovered) {
startButton.isHovered = true;
// Apply a slight scale increase when hovered
tween(startButton, {
scaleX: 1.05,
scaleY: 1.05
}, {
duration: 200,
easing: tween.easeOut
});
animateStartButton();
}
} else {
// Stop animation when cursor moves away
if (startButton.isHovered) {
startButton.isHovered = false;
tween.stop(startButton, {
rotation: true
});
tween(startButton, {
rotation: 0,
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 200
});
}
}
};
// Function to animate the start button
function animateStartButton() {
if (!startButton.isHovered) {
return;
}
// Rotate to one side
tween(startButton, {
rotation: 0.08 // Slight rotation (in radians)
}, {
duration: 50,
// Very quick rotation for more responsive feel
easing: tween.easeInOut,
onFinish: function onFinish() {
if (!startButton.isHovered) {
startButton.rotation = 0;
return;
}
// Rotate to the other side
tween(startButton, {
rotation: -0.08 // Slight rotation in opposite direction
}, {
duration: 50,
// Very quick rotation for more responsive feel
easing: tween.easeInOut,
onFinish: animateStartButton // Continue the animation
});
}
});
}
game.addChild(startButton);
// Create settings button
settingsButton = new Text2('Settings', {
size: 90,
fill: 0x101820
});
settingsButton.anchor.set(0.5, 0.5);
settingsButton.x = 1024;
settingsButton.y = 1400;
settingsButton.interactive = true;
game.addChild(settingsButton);
// Set up event handlers for menu
startButton.down = function () {
if (soundEnabled) {
LK.getSound('click').play();
}
hideMenu();
showModeSelect();
};
settingsButton.down = function () {
if (soundEnabled) {
LK.getSound('click').play();
}
hideMenu();
showSettings();
};
}
function hideMenu() {
if (menuBackground) {
menuBackground.visible = false;
}
if (titleText) {
titleText.visible = false;
}
if (startButton) {
startButton.visible = false;
}
if (settingsButton) {
settingsButton.visible = false;
}
// Make sure high score is visible in game
if (highScoreTxt) {
highScoreTxt.visible = true;
}
}
function hideGameElements() {
if (background) {
background.visible = false;
}
if (lava) {
lava.visible = false;
}
if (paddle) {
paddle.visible = false;
}
if (scoreTxt) {
scoreTxt.visible = false;
}
if (levelTxt) {
levelTxt.visible = false;
}
if (comboTxt) {
comboTxt.visible = false;
}
if (highScoreTxt) {
highScoreTxt.visible = false;
}
if (hitCounterText) {
hitCounterText.visible = false;
}
if (speedTxt) {
speedTxt.visible = false;
}
if (backButton) {
backButton.visible = false;
}
}
function showMenu() {
currentState = GAME_STATE.MENU;
if (menuBackground) {
menuBackground.visible = true;
}
if (titleText) {
titleText.visible = true;
}
if (startButton) {
startButton.visible = true;
}
if (settingsButton) {
settingsButton.visible = true;
}
if (settingsPanel) {
settingsPanel.visible = false;
}
// Play menu music when returning to menu from game over
if (musicEnabled && currentState === GAME_STATE.MENU) {
LK.playMusic('menuMusic', {
fade: {
start: 0,
end: 0.6,
duration: 1000
}
});
}
}
// Mode selection screen
var modeSelectPanel;
var modeButtons = [];
function showModeSelect() {
currentState = GAME_STATE.MODE_SELECT;
// Create mode select panel if it doesn't exist
if (!modeSelectPanel) {
modeSelectPanel = new Container();
game.addChild(modeSelectPanel);
// Mode select background
var modeBg = LK.getAsset('background', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0,
tint: 0xFFFFFF
});
modeSelectPanel.addChild(modeBg);
// Mode select title
var modeTitle = new Text2('Select Game Mode', {
size: 120,
fill: 0x101820
});
modeTitle.anchor.set(0.5, 0);
modeTitle.x = 1024;
modeTitle.y = 100;
modeSelectPanel.addChild(modeTitle);
// Create mode buttons
var modeKeys = Object.keys(GAME_MODES);
for (var i = 0; i < modeKeys.length; i++) {
var modeKey = modeKeys[i];
var modeId = GAME_MODES[modeKey];
var config = gameModeConfigs[modeId];
var yPos = 400 + i * 320;
// Mode button container
var modeContainer = new Container();
modeContainer.x = 1024;
modeContainer.y = yPos;
modeSelectPanel.addChild(modeContainer);
// Mode button background with rounded corners
var buttonBg = new Container();
// Main rectangle body
var mainRect = LK.getAsset('background', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: 0
});
mainRect.width = 1700; // Slightly smaller for rounded ends
mainRect.height = 280;
mainRect.tint = 0x101820;
buttonBg.addChild(mainRect);
// Left rounded corner (circle)
var leftCorner = LK.getAsset('ball', {
anchorX: 0.5,
anchorY: 0.5,
width: 280,
height: 280,
tint: 0x101820
});
leftCorner.x = -850; // Position at left edge
leftCorner.y = 0;
buttonBg.addChild(leftCorner);
// Right rounded corner (circle)
var rightCorner = LK.getAsset('ball', {
anchorX: 0.5,
anchorY: 0.5,
width: 280,
height: 280,
tint: 0x101820
});
rightCorner.x = 850; // Position at right edge
rightCorner.y = 0;
buttonBg.addChild(rightCorner);
modeContainer.addChild(buttonBg);
// Mode name
var modeName = new Text2(config.name, {
size: 80,
fill: 0xFFB612
});
modeName.anchor.set(0.5, 0);
modeName.x = 0;
modeName.y = -80;
modeContainer.addChild(modeName);
// Mode description
var modeDesc = new Text2(config.description, {
size: 50,
fill: 0xFFFFFF
});
modeDesc.anchor.set(0.5, 0);
modeDesc.x = 0;
modeDesc.y = -20;
modeContainer.addChild(modeDesc);
// Make button interactive
modeContainer.interactive = true;
modeContainer.modeId = modeId;
modeContainer.down = function () {
if (soundEnabled) {
LK.getSound('click').play();
}
currentGameMode = this.modeId;
hideModeSelect();
// Stop menu music with fade out
if (musicEnabled) {
LK.playMusic('menuMusic', {
fade: {
start: 0.6,
end: 0,
duration: 500
}
});
}
initializeGameElements();
startGame();
};
modeButtons.push(modeContainer);
}
// Back to menu button
var backToMenuBtn = new Text2('ā Back to Menu', {
size: 70,
fill: 0x101820
});
backToMenuBtn.anchor.set(0.5, 0.5);
backToMenuBtn.x = 1024;
backToMenuBtn.y = 2500;
backToMenuBtn.interactive = true;
backToMenuBtn.down = function () {
if (soundEnabled) {
LK.getSound('click').play();
}
hideModeSelect();
showMenu();
};
modeSelectPanel.addChild(backToMenuBtn);
} else {
modeSelectPanel.visible = true;
}
}
function hideModeSelect() {
if (modeSelectPanel) {
modeSelectPanel.visible = false;
}
}
function showSettings() {
currentState = GAME_STATE.SETTINGS;
// Create settings panel if it doesn't exist
if (!settingsPanel) {
settingsPanel = new Container();
game.addChild(settingsPanel);
// Settings panel background
var panelBg = LK.getAsset('background', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0
});
panelBg.width = 2048;
panelBg.height = 2732;
panelBg.tint = 0x101820;
settingsPanel.addChild(panelBg);
// Settings title
var settingsTitle = new Text2('Visual Settings', {
size: 100 * visualSettings.textSize,
fill: visualSettings.uiColor
});
settingsTitle.anchor.set(0.5, 0);
settingsTitle.x = 1024;
settingsTitle.y = 100;
settingsPanel.addChild(settingsTitle);
// Create scrollable settings content
var settingsContent = new Container();
settingsContent.y = 0;
settingsPanel.addChild(settingsContent);
var currentY = 250;
var spacing = 120;
// Color settings section
var colorTitle = new Text2('Colors', {
size: 80 * visualSettings.textSize,
fill: 0xFFB612
});
colorTitle.anchor.set(0, 0);
colorTitle.x = 100;
colorTitle.y = currentY;
settingsContent.addChild(colorTitle);
currentY += spacing;
// Color presets
var colorPresets = [{
name: 'Classic Gold',
paddle: 0xFFB612,
ball: 0xFFB612,
lava: 0xC60C30,
bg: 0xFFFFFF,
text: 0x101820,
ui: 0xFFFFFF
}, {
name: 'Ocean Blue',
paddle: 0x3498DB,
ball: 0x2980B9,
lava: 0xE74C3C,
bg: 0xECF0F1,
text: 0x2C3E50,
ui: 0xFFFFFF
}, {
name: 'Forest Green',
paddle: 0x27AE60,
ball: 0x2ECC71,
lava: 0xE67E22,
bg: 0xF8F9FA,
text: 0x1E3A8A,
ui: 0xFFFFFF
}, {
name: 'Purple Power',
paddle: 0x8E44AD,
ball: 0x9B59B6,
lava: 0xE74C3C,
bg: 0xF4F3FF,
text: 0x4C1D95,
ui: 0xFFFFFF
}, {
name: 'Sunset',
paddle: 0xFF6B35,
ball: 0xFF8C42,
lava: 0xC70025,
bg: 0xFFF8DC,
text: 0x8B0000,
ui: 0xFFFFFF
}];
for (var i = 0; i < colorPresets.length; i++) {
var preset = colorPresets[i];
var presetButton = new Text2(preset.name, {
size: 60 * visualSettings.textSize,
fill: preset.paddle
});
presetButton.anchor.set(0, 0);
presetButton.x = 150;
presetButton.y = currentY;
presetButton.interactive = true;
presetButton.preset = preset;
presetButton.down = function () {
if (soundEnabled) LK.getSound('click').play();
// Apply preset colors
visualSettings.paddleColor = this.preset.paddle;
visualSettings.ballColor = this.preset.ball;
visualSettings.lavaColor = this.preset.lava;
visualSettings.backgroundColor = this.preset.bg;
visualSettings.textColor = this.preset.text;
visualSettings.uiColor = this.preset.ui;
saveVisualSettings();
applyVisualSettings();
// Refresh settings panel
settingsPanel.destroy();
settingsPanel = null;
showSettings();
};
settingsContent.addChild(presetButton);
currentY += 80;
}
currentY += 40;
// Effects section
var effectsTitle = new Text2('Effects', {
size: 80 * visualSettings.textSize,
fill: 0xFFB612
});
effectsTitle.anchor.set(0, 0);
effectsTitle.x = 100;
effectsTitle.y = currentY;
settingsContent.addChild(effectsTitle);
currentY += spacing;
// Effects toggles
var effectsSettings = [{
key: 'trailsEnabled',
name: 'Ball Trails'
}, {
key: 'particlesEnabled',
name: 'Particles'
}, {
key: 'screenShakeEnabled',
name: 'Screen Shake'
}, {
key: 'lavaAnimationEnabled',
name: 'Lava Animation'
}];
for (var i = 0; i < effectsSettings.length; i++) {
var setting = effectsSettings[i];
var toggleText = new Text2(setting.name + ': ' + (visualSettings[setting.key] ? 'ON' : 'OFF'), {
size: 60 * visualSettings.textSize,
fill: visualSettings[setting.key] ? 0x00FF00 : 0xFF0000
});
toggleText.anchor.set(0, 0);
toggleText.x = 150;
toggleText.y = currentY;
toggleText.interactive = true;
toggleText.settingKey = setting.key;
toggleText.settingName = setting.name;
toggleText.down = function () {
if (soundEnabled) LK.getSound('click').play();
visualSettings[this.settingKey] = !visualSettings[this.settingKey];
saveVisualSettings();
this.setText(this.settingName + ': ' + (visualSettings[this.settingKey] ? 'ON' : 'OFF'));
this.fill = visualSettings[this.settingKey] ? 0x00FF00 : 0xFF0000;
};
settingsContent.addChild(toggleText);
currentY += 80;
}
currentY += 40;
// Size settings section
var sizeTitle = new Text2('Sizes', {
size: 80 * visualSettings.textSize,
fill: 0xFFB612
});
sizeTitle.anchor.set(0, 0);
sizeTitle.x = 100;
sizeTitle.y = currentY;
settingsContent.addChild(sizeTitle);
currentY += spacing;
// Size presets
var sizePresets = [{
name: 'Small',
ball: 0.8,
paddle: 0.8,
text: 0.8,
particle: 0.8
}, {
name: 'Normal',
ball: 1.0,
paddle: 1.0,
text: 1.0,
particle: 1.0
}, {
name: 'Large',
ball: 1.3,
paddle: 1.2,
text: 1.2,
particle: 1.2
}, {
name: 'Extra Large',
ball: 1.6,
paddle: 1.4,
text: 1.4,
particle: 1.4
}];
for (var i = 0; i < sizePresets.length; i++) {
var preset = sizePresets[i];
var sizeButton = new Text2(preset.name, {
size: 60 * visualSettings.textSize,
fill: 0xFFFFFF
});
sizeButton.anchor.set(0, 0);
sizeButton.x = 150;
sizeButton.y = currentY;
sizeButton.interactive = true;
sizeButton.preset = preset;
sizeButton.down = function () {
if (soundEnabled) LK.getSound('click').play();
// Apply size preset
visualSettings.ballSize = this.preset.ball;
visualSettings.paddleSize = this.preset.paddle;
visualSettings.textSize = this.preset.text;
visualSettings.particleSize = this.preset.particle;
saveVisualSettings();
// Refresh settings panel
settingsPanel.destroy();
settingsPanel = null;
showSettings();
};
settingsContent.addChild(sizeButton);
currentY += 80;
}
currentY += 40;
// Reset all settings button
var resetAllButton = new Text2('Reset All Settings', {
size: 70 * visualSettings.textSize,
fill: 0xFF6B6B
});
resetAllButton.anchor.set(0.5, 0.5);
resetAllButton.x = 1024;
resetAllButton.y = currentY;
resetAllButton.interactive = true;
resetAllButton.down = function () {
if (soundEnabled) LK.getSound('click').play();
// Reset to defaults
visualSettings.paddleColor = 0xFFB612;
visualSettings.ballColor = 0xFFB612;
visualSettings.lavaColor = 0xC60C30;
visualSettings.backgroundColor = 0xFFFFFF;
visualSettings.textColor = 0x101820;
visualSettings.uiColor = 0xFFFFFF;
visualSettings.trailsEnabled = true;
visualSettings.particlesEnabled = true;
visualSettings.screenShakeEnabled = true;
visualSettings.lavaAnimationEnabled = true;
visualSettings.ballSize = 1.0;
visualSettings.paddleSize = 1.0;
visualSettings.textSize = 1.0;
visualSettings.particleSize = 1.0;
visualSettings.trailIntensity = 1.0;
visualSettings.particleIntensity = 1.0;
visualSettings.animationSpeed = 1.0;
saveVisualSettings();
applyVisualSettings();
// Refresh settings panel
settingsPanel.destroy();
settingsPanel = null;
showSettings();
};
settingsContent.addChild(resetAllButton);
currentY += 120;
// High score display
var highScoreDisplay = new Text2('High Score: ' + highScore, {
size: 60 * visualSettings.textSize,
fill: visualSettings.uiColor
});
highScoreDisplay.anchor.set(0.5, 0.5);
highScoreDisplay.x = 1024;
highScoreDisplay.y = currentY;
settingsContent.addChild(highScoreDisplay);
currentY += 100;
// Reset high score button
var resetScoreButton = new Text2('Reset High Score', {
size: 60 * visualSettings.textSize,
fill: 0xFF6B6B
});
resetScoreButton.anchor.set(0.5, 0.5);
resetScoreButton.x = 1024;
resetScoreButton.y = currentY;
resetScoreButton.interactive = true;
resetScoreButton.down = function () {
if (soundEnabled) LK.getSound('click').play();
showResetConfirmation();
};
settingsContent.addChild(resetScoreButton);
currentY += 100;
// Down arrow for sound settings
var soundSettingsArrow = new Text2('Sound Settings ā', {
size: 70 * visualSettings.textSize,
fill: 0xFFB612
});
soundSettingsArrow.anchor.set(0.5, 0.5);
soundSettingsArrow.x = 1024;
soundSettingsArrow.y = currentY;
soundSettingsArrow.interactive = true;
soundSettingsArrow.down = function () {
if (soundEnabled) LK.getSound('click').play();
showSoundSettings();
};
settingsContent.addChild(soundSettingsArrow);
currentY += 100;
// Back to menu button
var backButton = new Text2('ā Back to Menu', {
size: 70 * visualSettings.textSize,
fill: visualSettings.uiColor
});
backButton.anchor.set(0.5, 0.5);
backButton.x = 1024;
backButton.y = currentY;
backButton.interactive = true;
backButton.down = function () {
if (soundEnabled) LK.getSound('click').play();
// Animate settings panel exit
tween(settingsPanel, {
alpha: 0,
scaleX: 0.8,
scaleY: 0.8
}, {
duration: 300,
easing: tween.easeIn,
onFinish: function onFinish() {
settingsPanel.visible = false;
showMenu();
}
});
if (musicEnabled) {
LK.playMusic('menuMusic', {
fade: {
start: 0,
end: 0.6,
duration: 1000
}
});
}
};
settingsContent.addChild(backButton);
// Make settings panel scrollable
settingsPanel.interactive = true;
settingsPanel.scrollY = 0;
settingsPanel.move = function (x, y, obj) {
// Simple scroll implementation
if (obj && obj.event && obj.event.movementY) {
settingsContent.y += obj.event.movementY * 2;
settingsContent.y = Math.max(Math.min(0, settingsContent.y), -(currentY - 2732 + 200));
}
};
} else {
settingsPanel.visible = true;
}
// Settings panel enter animation
settingsPanel.alpha = 0;
settingsPanel.scale.set(0.8, 0.8);
tween(settingsPanel, {
alpha: 1,
scaleX: 1,
scaleY: 1
}, {
duration: 400,
easing: tween.easeOut
});
}
// Function to create particles at click location
function createClickParticles(x, y) {
// Only create particles if enabled
if (!visualSettings.particlesEnabled) {
return;
}
// Create 15-20 particles for a visually impressive effect, adjusted by intensity
var baseCount = 15 + Math.floor(Math.random() * 6);
var particleCount = Math.floor(baseCount * visualSettings.particleIntensity);
for (var i = 0; i < particleCount; i++) {
var particle = new Particle();
particle.x = x;
particle.y = y;
particle.active = true;
// Add to particles array and game display
particles.push(particle);
game.addChild(particle);
}
}
// Function to create particles at collision location
function createCollisionParticles(x, y) {
// Only create particles if enabled
if (!visualSettings.particlesEnabled) {
return;
}
// Create 8-12 particles for collision effect - fewer than click particles, adjusted by intensity
var baseCount = 8 + Math.floor(Math.random() * 5);
var particleCount = Math.floor(baseCount * visualSettings.particleIntensity);
for (var i = 0; i < particleCount; i++) {
var particle = new Particle();
particle.x = x;
particle.y = y;
particle.active = true;
var collisionScale = 0.3 * visualSettings.particleSize * visualSettings.particleIntensity;
particle.scale.set(collisionScale, collisionScale); // Smaller particles for collisions
// Add to particles array and game display
particles.push(particle);
game.addChild(particle);
}
}
// Initialize balls array
function createBall() {
if (ballsInPlay >= maxBalls || !gameActive) {
return;
}
var ball = new Ball();
ball.reset(speedMultiplier);
ball.getChildAt(0).tint = visualSettings.ballColor;
// Visual indicator of speed - make ball slightly smaller as it gets faster, adjusted by ball size setting
var scale = Math.max(0.6, 1 - (speedMultiplier - 1) * 0.15) * visualSettings.ballSize;
ball.scale.set(scale, scale);
balls.push(ball);
game.addChild(ball);
ballsInPlay++;
}
// Handle input events based on current state
game.down = function (x, y, obj) {
// Play click sound whenever cursor is clicked
if (soundEnabled) {
LK.getSound('click').play();
}
// Create particles at click location
createClickParticles(x, y);
if (currentState === GAME_STATE.PLAYING) {
paddle.x = x;
}
// Check if we hit any interactive elements
if (obj && obj.event && obj.event.target && obj.event.target.down) {
obj.event.target.down(x, y, obj);
}
};
game.up = function (x, y, obj) {
// Handle volume slider up events
if (game.sliderUpHandler) {
game.sliderUpHandler(x, y, obj);
}
};
game.move = function (x, y, obj) {
if (currentState === GAME_STATE.PLAYING) {
paddle.x = x;
}
// Handle volume slider movements
if (game.sliderMoveHandler) {
game.sliderMoveHandler(x, y, obj);
}
};
// Update function
game.update = function () {
// Update any diagonal stripes in the game (always update even when not playing)
for (var i = 0; i < game.children.length; i++) {
if (game.children[i] instanceof DiagonalStripe) {
game.children[i].update();
}
}
// Update all particles
for (var i = particles.length - 1; i >= 0; i--) {
var particle = particles[i];
if (particle.active === false) {
particle.destroy();
particles.splice(i, 1);
continue;
}
particle.update();
}
// Check if in playing state
if (currentState !== GAME_STATE.PLAYING) {
return;
}
// Game play state
if (!gameActive) {
return;
}
// Update speed indicator if it exists
if (speedTxt) {
speedTxt.setText('Speed: x' + speedMultiplier.toFixed(1));
}
// Create balls based on game mode
if (currentGameMode === GAME_MODES.MULTI_BALL) {
// Multi-ball mode: maintain multiple balls
while (ballsInPlay < maxBalls) {
createBall();
}
} else {
// Other modes: only create a ball if none exists
if (ballsInPlay === 0) {
createBall();
}
}
// Update paddle and track position for physics calculations
game.lastPaddleX = paddle.x; // Store current position for next frame
paddle.update();
// Update all balls
for (var i = balls.length - 1; i >= 0; i--) {
var ball = balls[i];
if (!ball.active) {
continue;
}
ball.update();
// Check if ball hits paddle with improved collision detection
if (ball.speedY > 0 && ball.y + 20 >= paddle.y - paddle.height / 2 && ball.y - 20 <= paddle.y + paddle.height / 2 && ball.x + 20 >= paddle.x - paddle.width / 2 && ball.x - 20 <= paddle.x + paddle.width / 2) {
// Create particles at collision point
createCollisionParticles(ball.x, ball.y);
// Calculate hit position from -1 (left edge) to 1 (right edge)
var hitPos = (ball.x - paddle.x) / (paddle.width / 2);
// Calculate spin based on the difference between current and last hit position
// This simulates the effect of a moving paddle hitting the ball
var paddleMovementEffect = 0;
if (game.lastPaddleX !== undefined) {
paddleMovementEffect = (paddle.x - game.lastPaddleX) * 0.1;
}
// Apply reduced spin based on hit position and paddle movement
ball.spin = hitPos * 0.3 + paddleMovementEffect * 0.6; // Reduced spin effect
ball.lastHitPos = hitPos;
// Calculate angle based on where the ball hits the paddle with more randomness
// Wider angle range - full 180 degrees (0 to 180) instead of 120 degrees
var angle = Math.PI * Math.random() + Math.PI / 2; // Random angle in upper half (90 to 270 degrees)
// Add influence from hit position
angle = angle * 0.7 + (Math.PI / 3 * hitPos + Math.PI / 2) * 0.3;
// Calculate current speed with an adjustment
var currentSpeed = Math.sqrt(ball.speedX * ball.speedX + ball.speedY * ball.speedY);
var speed = Math.max(currentSpeed * 1.25, 12 * speedMultiplier);
// Adjust ball direction with more random bounce physics
ball.speedX = Math.cos(angle) * speed + paddleMovementEffect * 1.5;
ball.speedY = -Math.sin(angle) * speed * 1.4; // Increased vertical multiplier for higher bounce
// Add larger random variations for more unpredictable bouncing
ball.speedX += (Math.random() * 4 - 2) * speedMultiplier; // Increased randomness
ball.speedY += (Math.random() * 4 - 2) * speedMultiplier; // Increased randomness
// Create a bounce effect with the paddle - make it visually respond to the hit
tween(paddle, {
scaleY: 0.85,
y: paddle.y + 5
}, {
duration: 100,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(paddle, {
scaleY: 1.0,
y: paddle.y - 5
}, {
duration: 100,
easing: tween.elasticOut
});
}
});
// Move ball above paddle to prevent multiple collisions
ball.y = paddle.y - paddle.height / 2 - 20;
// Add a visual impact effect
var impactEffect = LK.getAsset('ball', {
anchorX: 0.5,
anchorY: 0.5,
x: ball.x,
y: ball.y + 10,
tint: 0xFFFFFF
});
impactEffect.scale.set(1.5, 0.5);
impactEffect.alpha = 0.7;
game.addChild(impactEffect);
// Animate and remove the impact effect
tween(impactEffect, {
alpha: 0,
scaleX: 2.5,
scaleY: 0.2
}, {
duration: 200,
onFinish: function onFinish() {
impactEffect.destroy();
}
});
// Simple scoring - always add 1 point
score += 1;
// Hide combo text
comboTxt.alpha = 0;
// Update hit counter
scoreTxt.setText('' + score);
// Center the score text
scoreTxt.x = 2048 / 2; // True center horizontally
scoreTxt.y = 2732 / 2; // True center vertically
// Make it fade and gently grow when updated
tween(scoreTxt, {
scaleX: 1.1,
// Smaller scale change
scaleY: 1.1,
// Smaller scale change
alpha: 0.8 // Slight fade out
}, {
duration: 300,
// Longer duration for smoother animation
easing: tween.easeInOut,
// Smoother easing
onFinish: function onFinish() {
tween(scoreTxt, {
scaleX: 1,
scaleY: 1,
alpha: 1 // Back to full opacity
}, {
duration: 300,
// Longer duration
easing: tween.easeInOut // Smoother easing
});
}
});
LK.setScore(score);
// Play bounce sound if enabled
if (soundEnabled) {
LK.getSound('bounce').play();
}
// Update hit counter for level system
currentHits++;
hitCounterText.setText(currentHits + '/' + hitsToNextLevel);
// Level up based on hits
if (currentHits >= hitsToNextLevel) {
currentHits = 0;
levelUp();
hitCounterText.setText('Hits to Next Level: ' + hitsToNextLevel);
// Animate hit counter text
tween(hitCounterText, {
scaleX: 1.2,
scaleY: 1.2,
alpha: 0.8
}, {
duration: 300,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(hitCounterText, {
scaleX: 1,
scaleY: 1,
alpha: 1
}, {
duration: 300,
easing: tween.easeInOut
});
}
});
}
}
// Check if ball falls into lava
if (ball.y > lava.y) {
// Play lava sound if enabled
if (soundEnabled) {
LK.getSound('lava').play();
}
// Flash the lava
tween(lava, {
tint: 0xffffff
}, {
duration: 200,
onFinish: function onFinish() {
tween(lava, {
tint: 0xe74c3c
}, {
duration: 200
});
}
});
// Remove ball
ball.active = false;
ball.destroy();
balls.splice(i, 1);
ballsInPlay--;
// Check game over
if (balls.length === 0 && ballsInPlay === 0) {
gameOver();
}
}
}
};
// Reset confirmation popup
function showResetConfirmation() {
// Create popup container
var popup = new Container();
game.addChild(popup);
// Popup background
var popupBg = LK.getAsset('background', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1366
});
popupBg.width = 1200;
popupBg.height = 700;
popupBg.tint = 0x101820;
popup.addChild(popupBg);
// Border for popup
var popupBorder = LK.getAsset('background', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1366
});
popupBorder.width = 1210;
popupBorder.height = 710;
popupBorder.tint = 0xFFB612;
popup.addChildAt(popupBorder, 0);
// Confirmation text
var confirmText = new Text2('Reset High Score?', {
size: 70,
fill: 0xFFFFFF
});
confirmText.anchor.set(0.5, 0);
confirmText.x = 1024;
confirmText.y = 1200;
popup.addChild(confirmText);
// Yes button
var yesButton = new Text2('Yes', {
size: 60,
fill: 0xFF6B6B
});
yesButton.anchor.set(0.5, 0.5);
yesButton.x = 824;
yesButton.y = 1450;
yesButton.interactive = true;
yesButton.down = function () {
if (soundEnabled) {
LK.getSound('click').play();
}
// Reset high score
highScore = 0;
storage.highScore = 0;
// Update high score display
if (highScoreTxt) {
highScoreTxt.setText('High Score: 0');
}
// Update settings display
settingsPanel.children.forEach(function (child) {
if (child instanceof Text2 && child.text && child.text.startsWith('High Score:')) {
child.setText('High Score: 0');
}
});
// Remove popup with animation
tween(popup, {
alpha: 0,
scaleX: 0.8,
scaleY: 0.8
}, {
duration: 300,
onFinish: function onFinish() {
popup.destroy();
}
});
};
popup.addChild(yesButton);
// No button
var noButton = new Text2('No', {
size: 60,
fill: 0xFFFFFF
});
noButton.anchor.set(0.5, 0.5);
noButton.x = 1224;
noButton.y = 1450;
noButton.interactive = true;
noButton.down = function () {
if (soundEnabled) {
LK.getSound('click').play();
}
// Remove popup with animation
tween(popup, {
alpha: 0,
scaleX: 0.8,
scaleY: 0.8
}, {
duration: 300,
onFinish: function onFinish() {
popup.destroy();
}
});
};
popup.addChild(noButton);
// Animate popup appearing
popup.scale.set(0.8, 0.8);
popup.alpha = 0;
tween(popup, {
alpha: 1,
scaleX: 1,
scaleY: 1
}, {
duration: 300,
easing: tween.easeOut
});
}
function levelUp() {
level++;
levelTxt.setText('Level: ' + level);
// Show level up message with speed information
var levelUpTxt = new Text2('LEVEL UP!\nSpeed x' + speedMultiplier.toFixed(1), {
size: 80,
fill: 0xFFFFFF
});
levelUpTxt.anchor.set(0.5, 0.5);
levelUpTxt.x = 1024;
levelUpTxt.y = 1366;
LK.gui.addChild(levelUpTxt);
// Animate level up message
tween(levelUpTxt, {
alpha: 0,
scaleX: 2,
scaleY: 2
}, {
duration: 1000,
onFinish: function onFinish() {
levelUpTxt.destroy();
}
});
// Change music to heavy metal if level is 4 or above
if (level === 4 && musicEnabled) {
// Transition to heavy metal music with fade effects
LK.playMusic('heavyMetalMusic', {
fade: {
start: 0,
end: 0.7,
duration: 1500
}
});
}
// Get current mode configuration
var modeConfig = gameModeConfigs[currentGameMode];
// Increase difficulty based on game mode
speedMultiplier += modeConfig.speedIncrease + level * 0.08;
maxBalls = modeConfig.maxBalls;
// Reset hit counter for next level
currentHits = 0;
// Set hits required for next level based on mode
hitsToNextLevel = modeConfig.hitsPerLevel + (level - 1) * 5;
hitCounterText.setText('0/' + hitsToNextLevel);
}
function gameOver() {
gameActive = false;
// Check if we have a new high score
if (score > highScore) {
highScore = score;
storage.highScore = highScore;
// Show new high score message
var newHighScoreTxt = new Text2('NEW HIGH SCORE!', {
size: 80,
fill: 0xFFB612
});
newHighScoreTxt.anchor.set(0.5, 0.5);
newHighScoreTxt.x = 2048 / 2; // True center horizontally
newHighScoreTxt.y = 1000;
LK.gui.addChild(newHighScoreTxt);
// Animate high score message
tween(newHighScoreTxt, {
alpha: 0,
scaleX: 2,
scaleY: 2
}, {
duration: 1500,
onFinish: function onFinish() {
newHighScoreTxt.destroy();
}
});
// Update high score display
if (highScoreTxt) {
highScoreTxt.setText('High Score: ' + highScore);
}
}
// Flash the screen red to indicate death
LK.effects.flashScreen(0xff0000, 500);
// If music enabled and player was at level 4 or above, prepare to reset music
var shouldResetMusic = level >= 4 && musicEnabled;
// Restart the game after a short delay instead of showing menu
LK.setTimeout(function () {
// Keep the game elements visible, just restart game logic
// Clear any remaining balls
for (var i = 0; i < balls.length; i++) {
balls[i].destroy();
}
balls = [];
ballsInPlay = 0;
// Restart the game immediately
startGame();
// Ensure we're in playing state
currentState = GAME_STATE.PLAYING;
}, 500);
}
// Start the game
function startGame() {
// Get current mode configuration
var modeConfig = gameModeConfigs[currentGameMode];
// Reset variables
score = 0;
level = 1;
combo = 0;
lastBallHit = 0;
gameActive = true;
speedMultiplier = modeConfig.initialSpeed;
maxBalls = modeConfig.maxBalls;
ballsInPlay = 0;
currentHits = 0;
hitsToNextLevel = modeConfig.hitsPerLevel;
// Update UI
scoreTxt.setText('0');
scoreTxt.scale.set(1, 1); // Reset any scaling from animations
scoreTxt.x = 2048 / 2; // True center horizontally
scoreTxt.y = 2732 / 2; // True center vertically
levelTxt.setText('Level: 1');
comboTxt.setText('');
comboTxt.alpha = 0;
hitCounterText.setText('0/' + hitsToNextLevel);
// Update high score display
if (highScoreTxt) {
highScoreTxt.setText('High Score: ' + highScore);
}
// Clear any existing balls
for (var i = 0; i < balls.length; i++) {
balls[i].destroy();
}
balls = [];
// Start with one ball
createBall();
// Play relaxing game music at start (level 1-3)
if (musicEnabled) {
LK.playMusic('gameMusic', {
fade: {
start: 0,
end: 0.4,
// Lower volume
duration: 2000 // Longer fade in
}
});
}
// Ensure the current state is set to playing
currentState = GAME_STATE.PLAYING;
}
// Apply visual settings on initialization
applyVisualSettings();
// Sound settings panel
var soundSettingsPanel;
function showSoundSettings() {
// Create sound settings panel if it doesn't exist
if (!soundSettingsPanel) {
soundSettingsPanel = new Container();
game.addChild(soundSettingsPanel);
// Sound settings panel background
var soundPanelBg = LK.getAsset('background', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0
});
soundPanelBg.width = 2048;
soundPanelBg.height = 2732;
soundPanelBg.tint = 0x2C3E50;
soundSettingsPanel.addChild(soundPanelBg);
// Sound settings title
var soundTitle = new Text2('Sound Settings', {
size: 100 * visualSettings.textSize,
fill: 0xFFB612
});
soundTitle.anchor.set(0.5, 0);
soundTitle.x = 1024;
soundTitle.y = 200;
soundSettingsPanel.addChild(soundTitle);
var soundCurrentY = 400;
var soundSpacing = 150;
// Sound Effects Toggle
var soundEffectsText = new Text2('Sound Effects: ' + (soundEnabled ? 'ON' : 'OFF'), {
size: 80 * visualSettings.textSize,
fill: soundEnabled ? 0x00FF00 : 0xFF0000
});
soundEffectsText.anchor.set(0.5, 0.5);
soundEffectsText.x = 1024;
soundEffectsText.y = soundCurrentY;
soundEffectsText.interactive = true;
soundEffectsText.down = function () {
if (soundEnabled) LK.getSound('click').play();
soundEnabled = !soundEnabled;
storage.soundEnabled = soundEnabled;
this.setText('Sound Effects: ' + (soundEnabled ? 'ON' : 'OFF'));
this.fill = soundEnabled ? 0x00FF00 : 0xFF0000;
};
soundSettingsPanel.addChild(soundEffectsText);
soundCurrentY += soundSpacing;
// Music Toggle
var musicText = new Text2('Music: ' + (musicEnabled ? 'ON' : 'OFF'), {
size: 80 * visualSettings.textSize,
fill: musicEnabled ? 0x00FF00 : 0xFF0000
});
musicText.anchor.set(0.5, 0.5);
musicText.x = 1024;
musicText.y = soundCurrentY;
musicText.interactive = true;
musicText.down = function () {
if (soundEnabled) LK.getSound('click').play();
musicEnabled = !musicEnabled;
storage.musicEnabled = musicEnabled;
this.setText('Music: ' + (musicEnabled ? 'ON' : 'OFF'));
this.fill = musicEnabled ? 0x00FF00 : 0xFF0000;
if (!musicEnabled) {
LK.stopMusic();
} else if (currentState === GAME_STATE.MENU) {
LK.playMusic('menuMusic', {
fade: {
start: 0,
end: 0.6,
duration: 1000
}
});
} else if (currentState === GAME_STATE.PLAYING && level >= 4) {
LK.playMusic('heavyMetalMusic', {
fade: {
start: 0,
end: 0.7,
duration: 1000
}
});
} else if (currentState === GAME_STATE.PLAYING) {
LK.playMusic('gameMusic', {
fade: {
start: 0,
end: 0.4,
duration: 1000
}
});
}
};
soundSettingsPanel.addChild(musicText);
soundCurrentY += soundSpacing;
// Volume controls section
var volumeTitle = new Text2('Volume Controls', {
size: 70 * visualSettings.textSize,
fill: 0xFFB612
});
volumeTitle.anchor.set(0.5, 0);
volumeTitle.x = 1024;
volumeTitle.y = soundCurrentY;
soundSettingsPanel.addChild(volumeTitle);
soundCurrentY += 100;
// Initialize volume settings if not present
if (storage.masterVolume === undefined) storage.masterVolume = 0.7;
if (storage.soundVolume === undefined) storage.soundVolume = 1.0;
if (storage.musicVolume === undefined) storage.musicVolume = 0.6;
// Master Volume Slider
var masterVolumeLabel = new Text2('Master Volume: ' + Math.round(storage.masterVolume * 100) + '%', {
size: 60 * visualSettings.textSize,
fill: 0xFFFFFF
});
masterVolumeLabel.anchor.set(0.5, 0.5);
masterVolumeLabel.x = 1024;
masterVolumeLabel.y = soundCurrentY;
soundSettingsPanel.addChild(masterVolumeLabel);
soundCurrentY += 80;
// Master volume slider track
var masterTrack = LK.getAsset('background', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: soundCurrentY
});
masterTrack.width = 800;
masterTrack.height = 20;
masterTrack.tint = 0x333333;
soundSettingsPanel.addChild(masterTrack);
// Master volume slider handle
var masterHandle = LK.getAsset('ball', {
anchorX: 0.5,
anchorY: 0.5,
width: 50,
height: 50,
tint: 0xFFB612
});
masterHandle.x = 624 + storage.masterVolume * 800; // Position based on volume
masterHandle.y = soundCurrentY;
masterHandle.interactive = true;
masterHandle.isDragging = false;
masterHandle.volumeType = 'master';
masterHandle.label = masterVolumeLabel;
masterHandle.down = function () {
this.isDragging = true;
if (soundEnabled) LK.getSound('click').play();
};
masterHandle.move = function (x, y, obj) {
if (this.isDragging) {
var trackLeft = 624;
var trackRight = 1424;
this.x = Math.max(trackLeft, Math.min(trackRight, x));
var volume = (this.x - trackLeft) / 800;
storage.masterVolume = volume;
this.label.setText('Master Volume: ' + Math.round(volume * 100) + '%');
}
};
masterHandle.up = function () {
this.isDragging = false;
};
soundSettingsPanel.addChild(masterHandle);
soundCurrentY += 100;
// Sound Effects Volume Slider
var soundVolumeLabel = new Text2('Sound Effects: ' + Math.round(storage.soundVolume * 100) + '%', {
size: 60 * visualSettings.textSize,
fill: 0xFFFFFF
});
soundVolumeLabel.anchor.set(0.5, 0.5);
soundVolumeLabel.x = 1024;
soundVolumeLabel.y = soundCurrentY;
soundSettingsPanel.addChild(soundVolumeLabel);
soundCurrentY += 80;
// Sound effects volume slider track
var soundTrack = LK.getAsset('background', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: soundCurrentY
});
soundTrack.width = 800;
soundTrack.height = 20;
soundTrack.tint = 0x333333;
soundSettingsPanel.addChild(soundTrack);
// Sound effects volume slider handle
var soundHandle = LK.getAsset('ball', {
anchorX: 0.5,
anchorY: 0.5,
width: 50,
height: 50,
tint: 0x3498DB
});
soundHandle.x = 624 + storage.soundVolume * 800;
soundHandle.y = soundCurrentY;
soundHandle.interactive = true;
soundHandle.isDragging = false;
soundHandle.volumeType = 'sound';
soundHandle.label = soundVolumeLabel;
soundHandle.down = function () {
this.isDragging = true;
if (soundEnabled) LK.getSound('click').play();
};
soundHandle.move = function (x, y, obj) {
if (this.isDragging) {
var trackLeft = 624;
var trackRight = 1424;
this.x = Math.max(trackLeft, Math.min(trackRight, x));
var volume = (this.x - trackLeft) / 800;
storage.soundVolume = volume;
this.label.setText('Sound Effects: ' + Math.round(volume * 100) + '%');
}
};
soundHandle.up = function () {
this.isDragging = false;
};
soundSettingsPanel.addChild(soundHandle);
soundCurrentY += 100;
// Music Volume Slider
var musicVolumeLabel = new Text2('Music Volume: ' + Math.round(storage.musicVolume * 100) + '%', {
size: 60 * visualSettings.textSize,
fill: 0xFFFFFF
});
musicVolumeLabel.anchor.set(0.5, 0.5);
musicVolumeLabel.x = 1024;
musicVolumeLabel.y = soundCurrentY;
soundSettingsPanel.addChild(musicVolumeLabel);
soundCurrentY += 80;
// Music volume slider track
var musicTrack = LK.getAsset('background', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: soundCurrentY
});
musicTrack.width = 800;
musicTrack.height = 20;
musicTrack.tint = 0x333333;
soundSettingsPanel.addChild(musicTrack);
// Music volume slider handle
var musicHandle = LK.getAsset('ball', {
anchorX: 0.5,
anchorY: 0.5,
width: 50,
height: 50,
tint: 0xE74C3C
});
musicHandle.x = 624 + storage.musicVolume * 800;
musicHandle.y = soundCurrentY;
musicHandle.interactive = true;
musicHandle.isDragging = false;
musicHandle.volumeType = 'music';
musicHandle.label = musicVolumeLabel;
musicHandle.down = function () {
this.isDragging = true;
if (soundEnabled) LK.getSound('click').play();
};
musicHandle.move = function (x, y, obj) {
if (this.isDragging) {
var trackLeft = 624;
var trackRight = 1424;
this.x = Math.max(trackLeft, Math.min(trackRight, x));
var volume = (this.x - trackLeft) / 800;
storage.musicVolume = volume;
this.label.setText('Music Volume: ' + Math.round(volume * 100) + '%');
// Apply volume change immediately to current music
if (musicEnabled && currentState === GAME_STATE.MENU) {
LK.playMusic('menuMusic', {
fade: {
start: volume,
end: volume,
duration: 100
}
});
} else if (musicEnabled && currentState === GAME_STATE.PLAYING && level >= 4) {
LK.playMusic('heavyMetalMusic', {
fade: {
start: volume,
end: volume,
duration: 100
}
});
} else if (musicEnabled && currentState === GAME_STATE.PLAYING) {
LK.playMusic('gameMusic', {
fade: {
start: volume,
end: volume,
duration: 100
}
});
}
}
};
musicHandle.up = function () {
this.isDragging = false;
};
soundSettingsPanel.addChild(musicHandle);
soundCurrentY += 120;
// Add global move handler to catch slider movements
game.sliderMoveHandler = function (x, y, obj) {
// Find all slider handles and update them
var children = soundSettingsPanel.children;
for (var i = 0; i < children.length; i++) {
var child = children[i];
if (child.isDragging && child.move) {
child.move(x, y, obj);
}
}
};
// Add global up handler to stop dragging
game.sliderUpHandler = function (x, y, obj) {
var children = soundSettingsPanel.children;
for (var i = 0; i < children.length; i++) {
var child = children[i];
if (child.isDragging && child.up) {
child.up(x, y, obj);
}
}
};
// Back to visual settings button
var backToVisualBtn = new Text2('ā Back to Visual Settings', {
size: 70 * visualSettings.textSize,
fill: 0xFFB612
});
backToVisualBtn.anchor.set(0.5, 0.5);
backToVisualBtn.x = 1024;
backToVisualBtn.y = soundCurrentY + 100;
backToVisualBtn.interactive = true;
backToVisualBtn.down = function () {
if (soundEnabled) LK.getSound('click').play();
hideSoundSettings();
};
soundSettingsPanel.addChild(backToVisualBtn);
} else {
soundSettingsPanel.visible = true;
}
// Page flip animation - slide from right to left
soundSettingsPanel.x = 2048;
soundSettingsPanel.alpha = 1;
tween(soundSettingsPanel, {
x: 0
}, {
duration: 500,
easing: tween.easeInOut
});
// Hide visual settings with slide animation
tween(settingsPanel, {
x: -2048
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: function onFinish() {
settingsPanel.visible = false;
settingsPanel.x = 0; // Reset position for next time
}
});
}
function hideSoundSettings() {
if (soundSettingsPanel) {
// Page flip animation back - slide from left to right
tween(soundSettingsPanel, {
x: 2048
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: function onFinish() {
soundSettingsPanel.visible = false;
soundSettingsPanel.x = 0; // Reset position
}
});
// Show visual settings with slide animation
settingsPanel.visible = true;
settingsPanel.x = -2048;
tween(settingsPanel, {
x: 0
}, {
duration: 500,
easing: tween.easeInOut
});
}
}
// Game will start when player presses the Play button in the menu
// Function to animate the lava with multiple effects
function animateLava() {
if (!visualSettings.lavaAnimationEnabled) {
// Just apply the static lava color if animations are disabled
if (lava) {
lava.tint = visualSettings.lavaColor;
}
return;
}
// Pulsing scale animation
function pulseLava() {
if (!visualSettings.lavaAnimationEnabled) return;
tween(lava, {
scaleY: 1.05,
scaleX: 1.02
}, {
duration: (800 + Math.random() * 400) / visualSettings.animationSpeed,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (!visualSettings.lavaAnimationEnabled) return;
tween(lava, {
scaleY: 0.98,
scaleX: 1.01
}, {
duration: (600 + Math.random() * 400) / visualSettings.animationSpeed,
easing: tween.easeInOut,
onFinish: pulseLava
});
}
});
}
// Color shifting animation - based on the selected lava color
function shiftLavaColor() {
if (!visualSettings.lavaAnimationEnabled) return;
// Create variations of the selected lava color
var baseLava = visualSettings.lavaColor;
var colors = [baseLava, baseLava + 0x111111, baseLava - 0x111111, baseLava + 0x220000, baseLava - 0x110011];
var randomColor = colors[Math.floor(Math.random() * colors.length)];
tween(lava, {
tint: randomColor
}, {
duration: (1500 + Math.random() * 1000) / visualSettings.animationSpeed,
easing: tween.easeInOut,
onFinish: shiftLavaColor
});
}
// Subtle vertical movement to simulate bubbling
function bubbleLava() {
if (!visualSettings.lavaAnimationEnabled) return;
var originalY = lava.y;
tween(lava, {
y: originalY - 5 - Math.random() * 8
}, {
duration: (400 + Math.random() * 300) / visualSettings.animationSpeed,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (!visualSettings.lavaAnimationEnabled) return;
tween(lava, {
y: originalY + 3 + Math.random() * 4
}, {
duration: (500 + Math.random() * 200) / visualSettings.animationSpeed,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (!visualSettings.lavaAnimationEnabled) return;
tween(lava, {
y: originalY
}, {
duration: (300 + Math.random() * 200) / visualSettings.animationSpeed,
easing: tween.easeOut,
onFinish: function onFinish() {
// Wait a bit before next bubble cycle
LK.setTimeout(bubbleLava, (500 + Math.random() * 1500) / visualSettings.animationSpeed);
}
});
}
});
}
});
}
// Start all animations
pulseLava();
shiftLavaColor();
LK.setTimeout(bubbleLava, Math.random() * 1000 / visualSettings.animationSpeed); // Start bubbling with random delay
} ===================================================================
--- original.js
+++ change.js
@@ -1438,12 +1438,22 @@
if (obj && obj.event && obj.event.target && obj.event.target.down) {
obj.event.target.down(x, y, obj);
}
};
+game.up = function (x, y, obj) {
+ // Handle volume slider up events
+ if (game.sliderUpHandler) {
+ game.sliderUpHandler(x, y, obj);
+ }
+};
game.move = function (x, y, obj) {
if (currentState === GAME_STATE.PLAYING) {
paddle.x = x;
}
+ // Handle volume slider movements
+ if (game.sliderMoveHandler) {
+ game.sliderMoveHandler(x, y, obj);
+ }
};
// Update function
game.update = function () {
// Update any diagonal stripes in the game (always update even when not playing)
@@ -2014,50 +2024,231 @@
}
};
soundSettingsPanel.addChild(musicText);
soundCurrentY += soundSpacing;
- // Volume presets section
- var volumeTitle = new Text2('Volume Presets', {
+ // Volume controls section
+ var volumeTitle = new Text2('Volume Controls', {
size: 70 * visualSettings.textSize,
fill: 0xFFB612
});
volumeTitle.anchor.set(0.5, 0);
volumeTitle.x = 1024;
volumeTitle.y = soundCurrentY;
soundSettingsPanel.addChild(volumeTitle);
soundCurrentY += 100;
- // Volume preset buttons
- var volumePresets = [{
- name: 'Quiet',
- value: 0.3
- }, {
- name: 'Normal',
- value: 0.7
- }, {
- name: 'Loud',
- value: 1.0
- }];
- for (var i = 0; i < volumePresets.length; i++) {
- var preset = volumePresets[i];
- var volumeButton = new Text2(preset.name + ' (' + Math.round(preset.value * 100) + '%)', {
- size: 60 * visualSettings.textSize,
- fill: 0xFFFFFF
- });
- volumeButton.anchor.set(0.5, 0.5);
- volumeButton.x = 1024;
- volumeButton.y = soundCurrentY;
- volumeButton.interactive = true;
- volumeButton.preset = preset;
- volumeButton.down = function () {
- if (soundEnabled) LK.getSound('click').play();
- // Apply volume to all sounds and music
- // Note: In LK engine, volume is typically set at initialization
- // This is a conceptual implementation
- storage.volumeLevel = this.preset.value;
- };
- soundSettingsPanel.addChild(volumeButton);
- soundCurrentY += 80;
- }
+ // Initialize volume settings if not present
+ if (storage.masterVolume === undefined) storage.masterVolume = 0.7;
+ if (storage.soundVolume === undefined) storage.soundVolume = 1.0;
+ if (storage.musicVolume === undefined) storage.musicVolume = 0.6;
+ // Master Volume Slider
+ var masterVolumeLabel = new Text2('Master Volume: ' + Math.round(storage.masterVolume * 100) + '%', {
+ size: 60 * visualSettings.textSize,
+ fill: 0xFFFFFF
+ });
+ masterVolumeLabel.anchor.set(0.5, 0.5);
+ masterVolumeLabel.x = 1024;
+ masterVolumeLabel.y = soundCurrentY;
+ soundSettingsPanel.addChild(masterVolumeLabel);
+ soundCurrentY += 80;
+ // Master volume slider track
+ var masterTrack = LK.getAsset('background', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ x: 1024,
+ y: soundCurrentY
+ });
+ masterTrack.width = 800;
+ masterTrack.height = 20;
+ masterTrack.tint = 0x333333;
+ soundSettingsPanel.addChild(masterTrack);
+ // Master volume slider handle
+ var masterHandle = LK.getAsset('ball', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ width: 50,
+ height: 50,
+ tint: 0xFFB612
+ });
+ masterHandle.x = 624 + storage.masterVolume * 800; // Position based on volume
+ masterHandle.y = soundCurrentY;
+ masterHandle.interactive = true;
+ masterHandle.isDragging = false;
+ masterHandle.volumeType = 'master';
+ masterHandle.label = masterVolumeLabel;
+ masterHandle.down = function () {
+ this.isDragging = true;
+ if (soundEnabled) LK.getSound('click').play();
+ };
+ masterHandle.move = function (x, y, obj) {
+ if (this.isDragging) {
+ var trackLeft = 624;
+ var trackRight = 1424;
+ this.x = Math.max(trackLeft, Math.min(trackRight, x));
+ var volume = (this.x - trackLeft) / 800;
+ storage.masterVolume = volume;
+ this.label.setText('Master Volume: ' + Math.round(volume * 100) + '%');
+ }
+ };
+ masterHandle.up = function () {
+ this.isDragging = false;
+ };
+ soundSettingsPanel.addChild(masterHandle);
+ soundCurrentY += 100;
+ // Sound Effects Volume Slider
+ var soundVolumeLabel = new Text2('Sound Effects: ' + Math.round(storage.soundVolume * 100) + '%', {
+ size: 60 * visualSettings.textSize,
+ fill: 0xFFFFFF
+ });
+ soundVolumeLabel.anchor.set(0.5, 0.5);
+ soundVolumeLabel.x = 1024;
+ soundVolumeLabel.y = soundCurrentY;
+ soundSettingsPanel.addChild(soundVolumeLabel);
+ soundCurrentY += 80;
+ // Sound effects volume slider track
+ var soundTrack = LK.getAsset('background', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ x: 1024,
+ y: soundCurrentY
+ });
+ soundTrack.width = 800;
+ soundTrack.height = 20;
+ soundTrack.tint = 0x333333;
+ soundSettingsPanel.addChild(soundTrack);
+ // Sound effects volume slider handle
+ var soundHandle = LK.getAsset('ball', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ width: 50,
+ height: 50,
+ tint: 0x3498DB
+ });
+ soundHandle.x = 624 + storage.soundVolume * 800;
+ soundHandle.y = soundCurrentY;
+ soundHandle.interactive = true;
+ soundHandle.isDragging = false;
+ soundHandle.volumeType = 'sound';
+ soundHandle.label = soundVolumeLabel;
+ soundHandle.down = function () {
+ this.isDragging = true;
+ if (soundEnabled) LK.getSound('click').play();
+ };
+ soundHandle.move = function (x, y, obj) {
+ if (this.isDragging) {
+ var trackLeft = 624;
+ var trackRight = 1424;
+ this.x = Math.max(trackLeft, Math.min(trackRight, x));
+ var volume = (this.x - trackLeft) / 800;
+ storage.soundVolume = volume;
+ this.label.setText('Sound Effects: ' + Math.round(volume * 100) + '%');
+ }
+ };
+ soundHandle.up = function () {
+ this.isDragging = false;
+ };
+ soundSettingsPanel.addChild(soundHandle);
+ soundCurrentY += 100;
+ // Music Volume Slider
+ var musicVolumeLabel = new Text2('Music Volume: ' + Math.round(storage.musicVolume * 100) + '%', {
+ size: 60 * visualSettings.textSize,
+ fill: 0xFFFFFF
+ });
+ musicVolumeLabel.anchor.set(0.5, 0.5);
+ musicVolumeLabel.x = 1024;
+ musicVolumeLabel.y = soundCurrentY;
+ soundSettingsPanel.addChild(musicVolumeLabel);
+ soundCurrentY += 80;
+ // Music volume slider track
+ var musicTrack = LK.getAsset('background', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ x: 1024,
+ y: soundCurrentY
+ });
+ musicTrack.width = 800;
+ musicTrack.height = 20;
+ musicTrack.tint = 0x333333;
+ soundSettingsPanel.addChild(musicTrack);
+ // Music volume slider handle
+ var musicHandle = LK.getAsset('ball', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ width: 50,
+ height: 50,
+ tint: 0xE74C3C
+ });
+ musicHandle.x = 624 + storage.musicVolume * 800;
+ musicHandle.y = soundCurrentY;
+ musicHandle.interactive = true;
+ musicHandle.isDragging = false;
+ musicHandle.volumeType = 'music';
+ musicHandle.label = musicVolumeLabel;
+ musicHandle.down = function () {
+ this.isDragging = true;
+ if (soundEnabled) LK.getSound('click').play();
+ };
+ musicHandle.move = function (x, y, obj) {
+ if (this.isDragging) {
+ var trackLeft = 624;
+ var trackRight = 1424;
+ this.x = Math.max(trackLeft, Math.min(trackRight, x));
+ var volume = (this.x - trackLeft) / 800;
+ storage.musicVolume = volume;
+ this.label.setText('Music Volume: ' + Math.round(volume * 100) + '%');
+ // Apply volume change immediately to current music
+ if (musicEnabled && currentState === GAME_STATE.MENU) {
+ LK.playMusic('menuMusic', {
+ fade: {
+ start: volume,
+ end: volume,
+ duration: 100
+ }
+ });
+ } else if (musicEnabled && currentState === GAME_STATE.PLAYING && level >= 4) {
+ LK.playMusic('heavyMetalMusic', {
+ fade: {
+ start: volume,
+ end: volume,
+ duration: 100
+ }
+ });
+ } else if (musicEnabled && currentState === GAME_STATE.PLAYING) {
+ LK.playMusic('gameMusic', {
+ fade: {
+ start: volume,
+ end: volume,
+ duration: 100
+ }
+ });
+ }
+ }
+ };
+ musicHandle.up = function () {
+ this.isDragging = false;
+ };
+ soundSettingsPanel.addChild(musicHandle);
+ soundCurrentY += 120;
+ // Add global move handler to catch slider movements
+ game.sliderMoveHandler = function (x, y, obj) {
+ // Find all slider handles and update them
+ var children = soundSettingsPanel.children;
+ for (var i = 0; i < children.length; i++) {
+ var child = children[i];
+ if (child.isDragging && child.move) {
+ child.move(x, y, obj);
+ }
+ }
+ };
+ // Add global up handler to stop dragging
+ game.sliderUpHandler = function (x, y, obj) {
+ var children = soundSettingsPanel.children;
+ for (var i = 0; i < children.length; i++) {
+ var child = children[i];
+ if (child.isDragging && child.up) {
+ child.up(x, y, obj);
+ }
+ }
+ };
// Back to visual settings button
var backToVisualBtn = new Text2('ā Back to Visual Settings', {
size: 70 * visualSettings.textSize,
fill: 0xFFB612