Code edit (1 edits merged)
Please save this source code
User prompt
write “Glaud” in small orange in the upper right corner.
User prompt
add all possible sounds.
User prompt
Please fix the bug: 'TypeError: Cannot use 'in' operator to search for 'x' in 0.5' in or related to this line: 'tween(particle.scale, {' Line Number: 661
User prompt
Please fix the bug: 'TypeError: Cannot use 'in' operator to search for 'x' in 0.5' in or related to this line: 'tween(particle.scale, {' Line Number: 663 ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Please fix the bug: 'TypeError: Cannot use 'in' operator to search for 'x' in 0.5' in or related to this line: 'tween(particle.scale, {' Line Number: 656
User prompt
Make the mouse tracking feature.
User prompt
Please fix the bug: 'TypeError: Cannot use 'in' operator to search for 'x' in 0.5' in or related to this line: 'tween(particle.scale, {' Line Number: 549
User prompt
reach out and fix all the bugs.
User prompt
Please fix the bug: 'TypeError: Cannot use 'in' operator to search for 'x' in 0.5' in or related to this line: 'tween(particle.scale, {' Line Number: 548
User prompt
identify the features needed to make it more beautiful and implement them smoothly. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
Code edit (1 edits merged)
Please save this source code
User prompt
Gravity Pong
Initial prompt
game
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ var Ball = Container.expand(function () { var self = Container.call(this); var ballGraphics = self.attachAsset('ball', { anchorX: 0.5, anchorY: 0.5, alpha: 0.9 }); var glowBall = self.attachAsset('ball', { anchorX: 0.5, anchorY: 0.5, alpha: 0.4, scaleX: 1.5, scaleY: 1.5 }); self.velocityX = 0; self.velocityY = 0; self.speed = 10; self.gravityAffected = true; self.lastX = 0; self.lastY = 0; self.trailElements = []; self.maxTrailLength = 5; self.createTrailElement = function () { var trailElement = LK.getAsset('ball', { anchorX: 0.5, anchorY: 0.5, alpha: 0.3, scaleX: 0.7, scaleY: 0.7, tint: 0x87CEFA }); return trailElement; }; self.updateTrail = function () { // Add new trail element if (Math.abs(self.x - self.lastX) > 5 || Math.abs(self.y - self.lastY) > 5) { var trail = self.createTrailElement(); trail.x = self.x; trail.y = self.y; game.addChildAt(trail, game.getChildIndex(self)); self.trailElements.push(trail); // Fade out and remove tween(trail, { alpha: 0 }, { duration: 400, easing: tween.linear, onFinish: function onFinish() { if (trail.parent) { trail.parent.removeChild(trail); } } }); // Remove old elements if too many if (self.trailElements.length > self.maxTrailLength) { var oldTrail = self.trailElements.shift(); if (oldTrail.parent) { oldTrail.parent.removeChild(oldTrail); } } } // Update last position self.lastX = self.x; self.lastY = self.y; }; self.reset = function () { self.x = 2048 / 2; self.y = 2732 / 2; self.velocityX = (Math.random() > 0.5 ? 1 : -1) * self.speed; self.velocityY = (Math.random() * 2 - 1) * self.speed * 0.5; self.lastX = self.x; self.lastY = self.y; // Clear any existing trail elements for (var i = 0; i < self.trailElements.length; i++) { if (self.trailElements[i].parent) { self.trailElements[i].parent.removeChild(self.trailElements[i]); } } self.trailElements = []; // Add pulsating glow effect self.startGlowAnimation(); }; self.startGlowAnimation = function () { tween(glowBall.scale, { x: 1.8, y: 1.8 }, { duration: 800, easing: tween.sinceOut, onFinish: function onFinish() { tween(glowBall.scale, { x: 1.5, y: 1.5 }, { duration: 800, easing: tween.sinceIn, onFinish: function onFinish() { self.startGlowAnimation(); } }); } }); }; self.update = function () { self.x += self.velocityX; self.y += self.velocityY; // Update the trail self.updateTrail(); // Handle wall collisions if (self.x < 0 + ballGraphics.width / 2) { self.x = ballGraphics.width / 2; self.velocityX = -self.velocityX; // Play wall bounce sound LK.getSound('bounce').play(); } else if (self.x > 2048 - ballGraphics.width / 2) { self.x = 2048 - ballGraphics.width / 2; self.velocityX = -self.velocityX; // Play wall bounce sound LK.getSound('bounce').play(); } }; return self; }); var GravityField = Container.expand(function () { var self = Container.call(this); var fieldGraphics = self.attachAsset('gravitationField', { anchorX: 0.5, anchorY: 0.5, alpha: 0.2 }); self.strength = 0.1; self.maxStrength = 0.5; self.setup = function () { self.x = 2048 / 2; self.y = 2732 / 2; self.strength = 0.1; self.scale.set(1); }; self.increaseStrength = function () { self.strength = Math.min(self.maxStrength, self.strength + 0.05); // Play powerup sound when gravity field increases strength LK.getSound('powerup').play(); tween(self.scale, { x: 1 + self.strength, y: 1 + self.strength }, { duration: 1000, easing: tween.elasticOut }); }; self.reverseGravity = function () { self.strength = -self.strength; // Play sound for gravity reversal LK.getSound('powerup').play(); tween(fieldGraphics, { tint: 0xFF5733 }, { duration: 300, easing: tween.linear, onFinish: function onFinish() { tween(fieldGraphics, { tint: 0x4287f5 }, { duration: 5000, easing: tween.linear, onFinish: function onFinish() { self.strength = Math.abs(self.strength); } }); } }); }; self.applyGravityTo = function (ball) { if (!ball.gravityAffected) { return; } var dx = self.x - ball.x; var dy = self.y - ball.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 10) { var forceX = dx / distance * self.strength; var forceY = dy / distance * self.strength; ball.velocityX += forceX; ball.velocityY += forceY; // Cap maximum velocity var speed = Math.sqrt(ball.velocityX * ball.velocityX + ball.velocityY * ball.velocityY); if (speed > ball.speed * 2) { ball.velocityX = ball.velocityX / speed * ball.speed * 2; ball.velocityY = ball.velocityY / speed * ball.speed * 2; } } }; return self; }); var Paddle = Container.expand(function () { var self = Container.call(this); // Create shadow effect var paddleShadow = self.attachAsset('paddle', { anchorX: 0.5, anchorY: 0.5, alpha: 0.3, scaleX: 1.05, scaleY: 1.8, y: 8 }); var paddleGraphics = self.attachAsset('paddle', { anchorX: 0.5, anchorY: 0.5 }); self.targetX = 0; self.speed = 15; self.isPlayer = false; self.score = 0; self.setup = function (isPlayer) { self.isPlayer = isPlayer; if (isPlayer) { self.y = 2732 - 200; paddleGraphics.tint = 0x4287f5; // Blue for player } else { self.y = 200; paddleGraphics.tint = 0xff5733; // Orange for AI } self.x = 2048 / 2; self.targetX = self.x; // Create entrance animation self.scale.set(0.1); // Play sound for paddle appearance LK.getSound('powerup').play(); tween(self.scale, { x: 1, y: 1 }, { duration: 800, easing: tween.elasticOut }); }; self.update = function () { // Move toward target position with easing if (Math.abs(self.x - self.targetX) > 1) { self.x += (self.targetX - self.x) * 0.2; } // Boundary check if (self.x < paddleGraphics.width / 2) { self.x = paddleGraphics.width / 2; } else if (self.x > 2048 - paddleGraphics.width / 2) { self.x = 2048 - paddleGraphics.width / 2; } }; return self; }); var PowerUp = Container.expand(function () { var self = Container.call(this); var type = 'gravity'; // Default type var powerUpGraphics; self.setup = function (powerType) { type = powerType; if (self.children.length > 0) { self.removeChildAt(0); } if (type === 'gravity') { powerUpGraphics = self.attachAsset('powerUpGravity', { anchorX: 0.5, anchorY: 0.5 }); } else if (type === 'multiball') { powerUpGraphics = self.attachAsset('powerUpMultiBall', { anchorX: 0.5, anchorY: 0.5 }); } self.x = Math.random() * (2048 - 200) + 100; self.y = Math.random() * (2732 - 600) + 300; self.alpha = 1; // Play sound on first appearance (not on pulse refresh) if (arguments.length > 0) { LK.getSound('powerup').play(); } // Pulsating animation tween(self, { alpha: 0.6 }, { duration: 800, easing: tween.sinceOut, onFinish: function onFinish() { tween(self, { alpha: 1 }, { duration: 800, easing: tween.sinceIn, onFinish: function onFinish() { if (self.parent) { self.setup(type); } } }); } }); }; self.getType = function () { return type; }; return self; }); var PredictionLine = Container.expand(function () { var self = Container.call(this); var lineSegments = []; var numSegments = 10; var segmentLength = 15; var segmentGap = 10; self.setup = function () { // Create line segments for (var i = 0; i < numSegments; i++) { var segment = LK.getAsset('paddle', { anchorX: 0.5, anchorY: 0.5, width: 5, height: segmentLength, alpha: 0.6 - i * 0.05, tint: 0x4287f5 }); lineSegments.push(segment); self.addChild(segment); } self.visible = false; }; self.updatePosition = function (startX, startY, targetX, targetY) { if (!self.visible) { self.visible = true; } var dx = targetX - startX; var dy = targetY - startY; var angle = Math.atan2(dy, dx); for (var i = 0; i < lineSegments.length; i++) { var distance = i * (segmentLength + segmentGap); lineSegments[i].x = startX + Math.cos(angle) * distance; lineSegments[i].y = startY + Math.sin(angle) * distance; lineSegments[i].rotation = angle; } }; self.hide = function () { self.visible = false; }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x000000 }); /**** * Game Code ****/ // Set a beautiful dark gradient background game.setBackgroundColor(0x0a0a2a); // Add "Glaud" text in small orange to the upper right corner var glaudText = new Text2('Glaud', { size: 40, fill: 0xff8c00 // Orange color }); glaudText.anchor.set(1, 0); // Anchor to top right glaudText.x = 2048 - 20; // 20px margin from right edge glaudText.y = 20; // 20px margin from top edge game.addChild(glaudText); // Create a star field background function createStarfield() { var starfield = new Container(); // Create stars with different sizes and opacities for (var i = 0; i < 100; i++) { var size = Math.random() * 4 + 1; var star = LK.getAsset('ball', { anchorX: 0.5, anchorY: 0.5, width: size, height: size, alpha: Math.random() * 0.7 + 0.3 }); star.x = Math.random() * 2048; star.y = Math.random() * 2732; // Create twinkling animation var duration = Math.random() * 3000 + 2000; // Create closure to keep reference to the current star (function (starElement) { function animateStar() { tween(starElement, { alpha: Math.random() * 0.5 + 0.1 }, { duration: duration, easing: tween.sinceInOut, onFinish: function onFinish() { tween(starElement, { alpha: Math.random() * 0.7 + 0.3 }, { duration: duration, easing: tween.sinceInOut, onFinish: animateStar }); } }); } animateStar(); })(star); starfield.addChild(star); } game.addChildAt(starfield, 0); // Add at the bottom layer return starfield; } // Create starfield var starfield = createStarfield(); // Create game elements var playerPaddle = new Paddle(); var aiPaddle = new Paddle(); var gravityField = new GravityField(); var balls = []; var powerUps = []; var centerLine; var playerScoreText; var aiScoreText; var dragTarget = null; var difficultyTimer; var powerUpTimer; var gameActive = false; var predictionLine; var mouseX = 0; var mouseY = 0; var isMouseOver = false; // Animate score text function function animateScoreText(textObject, newValue) { // Store original scale var originalScale = { x: textObject.scale.x, y: textObject.scale.y }; // Set the new text textObject.setText(newValue); // Animate scale up and back down tween.stop(textObject.scale); textObject.scale.set(originalScale.x * 1.5, originalScale.y * 1.5); tween(textObject.scale, { x: originalScale.x, y: originalScale.y }, { duration: 500, easing: tween.elasticOut }); } // Initialize game state function initGame() { // Setup players playerPaddle.setup(true); aiPaddle.setup(false); game.addChild(playerPaddle); game.addChild(aiPaddle); // Setup gravity field gravityField.setup(); game.addChild(gravityField); // Create center line with animation centerLine = new Container(); centerLine.alpha = 0; for (var i = 0; i < 20; i++) { var dash = LK.getAsset('paddle', { anchorX: 0.5, anchorY: 0.5, width: 50, height: 10, alpha: 0.5 }); dash.x = i * 110; // Add staggered animation to each dash (function (element, delay) { element.alpha = 0; element.scale.set(0.5); LK.setTimeout(function () { tween(element, { alpha: 0.5 }, { duration: 400, easing: tween.linear }); tween(element.scale, { x: 1, y: 1 }, { duration: 600, easing: tween.elasticOut }); }, delay); })(dash, i * 30); centerLine.addChild(dash); } centerLine.x = 50; centerLine.y = 2732 / 2; game.addChild(centerLine); tween(centerLine, { alpha: 1 }, { duration: 800, easing: tween.linear }); // Create score text with animated appearance playerScoreText = new Text2('0', { size: 150, fill: 0xFFFFFF }); playerScoreText.anchor.set(0.5, 1); playerScoreText.x = 2048 / 2; playerScoreText.y = 2732 - 50; playerScoreText.alpha = 0; playerScoreText.scale.set(0.5); game.addChild(playerScoreText); aiScoreText = new Text2('0', { size: 150, fill: 0xFFFFFF }); aiScoreText.anchor.set(0.5, 0); aiScoreText.x = 2048 / 2; aiScoreText.y = 50; aiScoreText.alpha = 0; aiScoreText.scale.set(0.5); game.addChild(aiScoreText); // Animate score text appearance tween(playerScoreText, { alpha: 1 }, { duration: 800, easing: tween.linear }); tween(playerScoreText.scale, { x: 1, y: 1 }, { duration: 1000, easing: tween.elasticOut }); tween(aiScoreText, { alpha: 1 }, { duration: 800, easing: tween.linear }); tween(aiScoreText.scale, { x: 1, y: 1 }, { duration: 1000, easing: tween.elasticOut }); // Create first ball with delay to allow UI elements to animate in LK.setTimeout(function () { createBall(); }, 1200); // Setup difficulty increases difficultyTimer = LK.setInterval(function () { if (gameActive) { gravityField.increaseStrength(); } }, 10000); // Setup power-up spawning powerUpTimer = LK.setInterval(function () { if (gameActive && powerUps.length < 1) { spawnPowerUp(); } }, 15000); // Create prediction line predictionLine = new PredictionLine(); predictionLine.setup(); game.addChild(predictionLine); // Start game gameActive = true; LK.playMusic('gameMusic'); } // Function to create a new ball function createBall() { var ball = new Ball(); ball.reset(); balls.push(ball); game.addChild(ball); // Play sound when creating a new ball LK.getSound('powerup').play(); return ball; } // Function to spawn a power-up function spawnPowerUp() { var powerUp = new PowerUp(); var type = Math.random() > 0.5 ? 'gravity' : 'multiball'; powerUp.setup(type); powerUps.push(powerUp); game.addChild(powerUp); // Play sound when spawning power-up (now handled in PowerUp.setup) } // Check for collisions between ball and paddle function checkPaddleCollision(ball, paddle) { if (ball.y + 25 >= paddle.y - 25 && ball.y - 25 <= paddle.y + 25) { if (ball.x + 25 >= paddle.x - 125 && ball.x - 25 <= paddle.x + 125) { // Calculate bounce angle based on where ball hit the paddle var relativeIntersectX = ball.x - paddle.x; var normalizedRelativeIntersectionX = relativeIntersectX / 125; var bounceAngle = normalizedRelativeIntersectionX * (Math.PI / 3); // Maximum angle: 60 degrees // Invert Y velocity and adjust X velocity based on bounce angle ball.velocityY = -ball.velocityY; ball.velocityX = ball.speed * Math.sin(bounceAngle); // Increase speed slightly ball.speed = Math.min(20, ball.speed * 1.05); // Play bounce sound LK.getSound('bounce').play(); // Enhanced paddle hit effect - scale and flash tween.stop(paddle.scale); paddle.scale.set(1.2, 0.8); tween(paddle.scale, { x: 1, y: 1 }, { duration: 300, easing: tween.elasticOut }); // Flash paddle with color based on player LK.effects.flashObject(paddle, paddle.isPlayer ? 0x4287f5 : 0xff5733, 300); return true; } } return false; } // Check for collisions between ball and power-up function checkPowerUpCollision(ball) { for (var i = powerUps.length - 1; i >= 0; i--) { var powerUp = powerUps[i]; if (ball.intersects(powerUp)) { // Create explosion effect at power-up location var explosionEffect = new Container(); explosionEffect.x = powerUp.x; explosionEffect.y = powerUp.y; game.addChild(explosionEffect); // Create multiple particles for explosion for (var p = 0; p < 10; p++) { var particle = LK.getAsset(powerUp.getType() === 'gravity' ? 'powerUpGravity' : 'powerUpMultiBall', { anchorX: 0.5, anchorY: 0.5, scale: 0.5, alpha: 0.8 }); // Randomize particle position and movement var angle = Math.random() * Math.PI * 2; var distance = Math.random() * 100 + 50; var duration = Math.random() * 500 + 500; particle.x = 0; particle.y = 0; explosionEffect.addChild(particle); // Animate particle movement and fade tween(particle, { x: Math.cos(angle) * distance, y: Math.sin(angle) * distance, alpha: 0 }, { duration: duration, easing: tween.cubicOut }); // Ensure particle has proper scale object particle.scale = particle.scale || { x: 0.5, y: 0.5 }; // Ensure particle has scale object before tweening if (!particle.scale || typeof particle.scale === 'number') { particle.scale = { x: 0.5, y: 0.5 }; } // Now safely tween the scale property tween(particle.scale, { x: 0.1, y: 0.1 }, { duration: duration, easing: tween.linear }); } // Remove explosion container after animation completes LK.setTimeout(function () { explosionEffect.destroy(); }, 1000); // Apply power-up effect with enhanced visuals if (powerUp.getType() === 'gravity') { // Shake gravity field for dramatic effect var originalX = gravityField.x; var originalY = gravityField.y; var shakeCount = 0; var shakeInterval = LK.setInterval(function () { gravityField.x = originalX + (Math.random() * 20 - 10); gravityField.y = originalY + (Math.random() * 20 - 10); shakeCount++; if (shakeCount >= 10) { LK.clearInterval(shakeInterval); gravityField.x = originalX; gravityField.y = originalY; gravityField.reverseGravity(); } }, 50); } else if (powerUp.getType() === 'multiball') { for (var j = 0; j < 2; j++) { var newBall = createBall(); // Add a scale animation to new balls newBall.scale.set(0.2); tween(newBall.scale, { x: 1, y: 1 }, { duration: 500, easing: tween.elasticOut }); } } // Play sound and remove power-up LK.getSound('powerup').play(); LK.effects.flashScreen(0x33FF57, 300); powerUp.destroy(); powerUps.splice(i, 1); } } } // Handle AI movement function updateAI() { // Find the closest ball var closestBall = null; var closestDistance = Infinity; for (var i = 0; i < balls.length; i++) { var ball = balls[i]; if (ball.velocityY < 0) { // Ball is moving towards AI var distance = Math.abs(ball.x - aiPaddle.x); if (distance < closestDistance) { closestDistance = distance; closestBall = ball; } } } if (closestBall) { // Add some prediction based on velocity and gravity var predictedX = closestBall.x + closestBall.velocityX * (aiPaddle.y - closestBall.y) / Math.abs(closestBall.velocityY); aiPaddle.targetX = predictedX; // Add some difficulty scaling var difficultyFactor = 0.3 + gravityField.strength / gravityField.maxStrength * 0.6; aiPaddle.targetX = aiPaddle.x + (predictedX - aiPaddle.x) * difficultyFactor; } } // Event handler for touch/mouse down game.down = function (x, y, obj) { // Only allow dragging the player paddle if (y > 2732 / 2) { dragTarget = playerPaddle; playerPaddle.targetX = x; isMouseOver = true; mouseX = x; mouseY = y; } }; // Event handler for touch/mouse move game.move = function (x, y, obj) { mouseX = x; mouseY = y; isMouseOver = true; if (dragTarget) { playerPaddle.targetX = x; } }; // Event handler for touch/mouse up game.up = function (x, y, obj) { dragTarget = null; }; // Game update loop game.update = function () { if (!gameActive) { return; } // Update AI updateAI(); // Update paddles playerPaddle.update(); aiPaddle.update(); // Update prediction line if (isMouseOver && balls.length > 0) { // Show prediction line from closest ball to mouse position var closestBall = null; var minDistance = Infinity; for (var i = 0; i < balls.length; i++) { var dist = Math.sqrt(Math.pow(balls[i].x - mouseX, 2) + Math.pow(balls[i].y - mouseY, 2)); if (dist < minDistance) { minDistance = dist; closestBall = balls[i]; } } if (closestBall) { predictionLine.updatePosition(closestBall.x, closestBall.y, mouseX, playerPaddle.y); } } else { predictionLine.hide(); } // Reset mouse tracking after update isMouseOver = false; // Update balls for (var i = balls.length - 1; i >= 0; i--) { var ball = balls[i]; // Apply gravity gravityField.applyGravityTo(ball); // Update ball position ball.update(); // Check for paddle collisions var hitPlayer = checkPaddleCollision(ball, playerPaddle); var hitAI = checkPaddleCollision(ball, aiPaddle); // Check if ball is out of bounds (scoring) if (ball.y > 2732 + 50) { // AI scores aiPaddle.score++; // Animated score update animateScoreText(aiScoreText, aiPaddle.score.toString()); LK.getSound('score').play(); // Create a visual effect at ball position before destroying var scoreEffect = LK.getAsset('ball', { anchorX: 0.5, anchorY: 0.5, x: ball.x, y: 2732, alpha: 0.8, scaleX: 1, scaleY: 1, tint: 0xff5733 }); game.addChild(scoreEffect); tween(scoreEffect.scale, { x: 3, y: 3 }, { duration: 500, easing: tween.cubicOut, onFinish: function onFinish() { tween(scoreEffect, { alpha: 0 }, { duration: 300, easing: tween.linear, onFinish: function onFinish() { scoreEffect.destroy(); } }); } }); ball.destroy(); balls.splice(i, 1); if (balls.length === 0) { createBall(); } // Check for game over if (aiPaddle.score >= 10) { gameActive = false; LK.showGameOver(); } } else if (ball.y < -50) { // Player scores playerPaddle.score++; // Animated score update animateScoreText(playerScoreText, playerPaddle.score.toString()); LK.getSound('score').play(); // Create a visual effect at ball position before destroying var scoreEffect = LK.getAsset('ball', { anchorX: 0.5, anchorY: 0.5, x: ball.x, y: 0, alpha: 0.8, scaleX: 1, scaleY: 1, tint: 0x4287f5 }); game.addChild(scoreEffect); tween(scoreEffect.scale, { x: 3, y: 3 }, { duration: 500, easing: tween.cubicOut, onFinish: function onFinish() { tween(scoreEffect, { alpha: 0 }, { duration: 300, easing: tween.linear, onFinish: function onFinish() { scoreEffect.destroy(); } }); } }); ball.destroy(); balls.splice(i, 1); if (balls.length === 0) { createBall(); } // Check for win if (playerPaddle.score >= 10) { gameActive = false; LK.showYouWin(); } } // Check for power-up collisions checkPowerUpCollision(ball); } }; // Initialize the game when code loads initGame();
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Ball = Container.expand(function () {
var self = Container.call(this);
var ballGraphics = self.attachAsset('ball', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.9
});
var glowBall = self.attachAsset('ball', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.4,
scaleX: 1.5,
scaleY: 1.5
});
self.velocityX = 0;
self.velocityY = 0;
self.speed = 10;
self.gravityAffected = true;
self.lastX = 0;
self.lastY = 0;
self.trailElements = [];
self.maxTrailLength = 5;
self.createTrailElement = function () {
var trailElement = LK.getAsset('ball', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.3,
scaleX: 0.7,
scaleY: 0.7,
tint: 0x87CEFA
});
return trailElement;
};
self.updateTrail = function () {
// Add new trail element
if (Math.abs(self.x - self.lastX) > 5 || Math.abs(self.y - self.lastY) > 5) {
var trail = self.createTrailElement();
trail.x = self.x;
trail.y = self.y;
game.addChildAt(trail, game.getChildIndex(self));
self.trailElements.push(trail);
// Fade out and remove
tween(trail, {
alpha: 0
}, {
duration: 400,
easing: tween.linear,
onFinish: function onFinish() {
if (trail.parent) {
trail.parent.removeChild(trail);
}
}
});
// Remove old elements if too many
if (self.trailElements.length > self.maxTrailLength) {
var oldTrail = self.trailElements.shift();
if (oldTrail.parent) {
oldTrail.parent.removeChild(oldTrail);
}
}
}
// Update last position
self.lastX = self.x;
self.lastY = self.y;
};
self.reset = function () {
self.x = 2048 / 2;
self.y = 2732 / 2;
self.velocityX = (Math.random() > 0.5 ? 1 : -1) * self.speed;
self.velocityY = (Math.random() * 2 - 1) * self.speed * 0.5;
self.lastX = self.x;
self.lastY = self.y;
// Clear any existing trail elements
for (var i = 0; i < self.trailElements.length; i++) {
if (self.trailElements[i].parent) {
self.trailElements[i].parent.removeChild(self.trailElements[i]);
}
}
self.trailElements = [];
// Add pulsating glow effect
self.startGlowAnimation();
};
self.startGlowAnimation = function () {
tween(glowBall.scale, {
x: 1.8,
y: 1.8
}, {
duration: 800,
easing: tween.sinceOut,
onFinish: function onFinish() {
tween(glowBall.scale, {
x: 1.5,
y: 1.5
}, {
duration: 800,
easing: tween.sinceIn,
onFinish: function onFinish() {
self.startGlowAnimation();
}
});
}
});
};
self.update = function () {
self.x += self.velocityX;
self.y += self.velocityY;
// Update the trail
self.updateTrail();
// Handle wall collisions
if (self.x < 0 + ballGraphics.width / 2) {
self.x = ballGraphics.width / 2;
self.velocityX = -self.velocityX;
// Play wall bounce sound
LK.getSound('bounce').play();
} else if (self.x > 2048 - ballGraphics.width / 2) {
self.x = 2048 - ballGraphics.width / 2;
self.velocityX = -self.velocityX;
// Play wall bounce sound
LK.getSound('bounce').play();
}
};
return self;
});
var GravityField = Container.expand(function () {
var self = Container.call(this);
var fieldGraphics = self.attachAsset('gravitationField', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.2
});
self.strength = 0.1;
self.maxStrength = 0.5;
self.setup = function () {
self.x = 2048 / 2;
self.y = 2732 / 2;
self.strength = 0.1;
self.scale.set(1);
};
self.increaseStrength = function () {
self.strength = Math.min(self.maxStrength, self.strength + 0.05);
// Play powerup sound when gravity field increases strength
LK.getSound('powerup').play();
tween(self.scale, {
x: 1 + self.strength,
y: 1 + self.strength
}, {
duration: 1000,
easing: tween.elasticOut
});
};
self.reverseGravity = function () {
self.strength = -self.strength;
// Play sound for gravity reversal
LK.getSound('powerup').play();
tween(fieldGraphics, {
tint: 0xFF5733
}, {
duration: 300,
easing: tween.linear,
onFinish: function onFinish() {
tween(fieldGraphics, {
tint: 0x4287f5
}, {
duration: 5000,
easing: tween.linear,
onFinish: function onFinish() {
self.strength = Math.abs(self.strength);
}
});
}
});
};
self.applyGravityTo = function (ball) {
if (!ball.gravityAffected) {
return;
}
var dx = self.x - ball.x;
var dy = self.y - ball.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 10) {
var forceX = dx / distance * self.strength;
var forceY = dy / distance * self.strength;
ball.velocityX += forceX;
ball.velocityY += forceY;
// Cap maximum velocity
var speed = Math.sqrt(ball.velocityX * ball.velocityX + ball.velocityY * ball.velocityY);
if (speed > ball.speed * 2) {
ball.velocityX = ball.velocityX / speed * ball.speed * 2;
ball.velocityY = ball.velocityY / speed * ball.speed * 2;
}
}
};
return self;
});
var Paddle = Container.expand(function () {
var self = Container.call(this);
// Create shadow effect
var paddleShadow = self.attachAsset('paddle', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.3,
scaleX: 1.05,
scaleY: 1.8,
y: 8
});
var paddleGraphics = self.attachAsset('paddle', {
anchorX: 0.5,
anchorY: 0.5
});
self.targetX = 0;
self.speed = 15;
self.isPlayer = false;
self.score = 0;
self.setup = function (isPlayer) {
self.isPlayer = isPlayer;
if (isPlayer) {
self.y = 2732 - 200;
paddleGraphics.tint = 0x4287f5; // Blue for player
} else {
self.y = 200;
paddleGraphics.tint = 0xff5733; // Orange for AI
}
self.x = 2048 / 2;
self.targetX = self.x;
// Create entrance animation
self.scale.set(0.1);
// Play sound for paddle appearance
LK.getSound('powerup').play();
tween(self.scale, {
x: 1,
y: 1
}, {
duration: 800,
easing: tween.elasticOut
});
};
self.update = function () {
// Move toward target position with easing
if (Math.abs(self.x - self.targetX) > 1) {
self.x += (self.targetX - self.x) * 0.2;
}
// Boundary check
if (self.x < paddleGraphics.width / 2) {
self.x = paddleGraphics.width / 2;
} else if (self.x > 2048 - paddleGraphics.width / 2) {
self.x = 2048 - paddleGraphics.width / 2;
}
};
return self;
});
var PowerUp = Container.expand(function () {
var self = Container.call(this);
var type = 'gravity'; // Default type
var powerUpGraphics;
self.setup = function (powerType) {
type = powerType;
if (self.children.length > 0) {
self.removeChildAt(0);
}
if (type === 'gravity') {
powerUpGraphics = self.attachAsset('powerUpGravity', {
anchorX: 0.5,
anchorY: 0.5
});
} else if (type === 'multiball') {
powerUpGraphics = self.attachAsset('powerUpMultiBall', {
anchorX: 0.5,
anchorY: 0.5
});
}
self.x = Math.random() * (2048 - 200) + 100;
self.y = Math.random() * (2732 - 600) + 300;
self.alpha = 1;
// Play sound on first appearance (not on pulse refresh)
if (arguments.length > 0) {
LK.getSound('powerup').play();
}
// Pulsating animation
tween(self, {
alpha: 0.6
}, {
duration: 800,
easing: tween.sinceOut,
onFinish: function onFinish() {
tween(self, {
alpha: 1
}, {
duration: 800,
easing: tween.sinceIn,
onFinish: function onFinish() {
if (self.parent) {
self.setup(type);
}
}
});
}
});
};
self.getType = function () {
return type;
};
return self;
});
var PredictionLine = Container.expand(function () {
var self = Container.call(this);
var lineSegments = [];
var numSegments = 10;
var segmentLength = 15;
var segmentGap = 10;
self.setup = function () {
// Create line segments
for (var i = 0; i < numSegments; i++) {
var segment = LK.getAsset('paddle', {
anchorX: 0.5,
anchorY: 0.5,
width: 5,
height: segmentLength,
alpha: 0.6 - i * 0.05,
tint: 0x4287f5
});
lineSegments.push(segment);
self.addChild(segment);
}
self.visible = false;
};
self.updatePosition = function (startX, startY, targetX, targetY) {
if (!self.visible) {
self.visible = true;
}
var dx = targetX - startX;
var dy = targetY - startY;
var angle = Math.atan2(dy, dx);
for (var i = 0; i < lineSegments.length; i++) {
var distance = i * (segmentLength + segmentGap);
lineSegments[i].x = startX + Math.cos(angle) * distance;
lineSegments[i].y = startY + Math.sin(angle) * distance;
lineSegments[i].rotation = angle;
}
};
self.hide = function () {
self.visible = false;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
// Set a beautiful dark gradient background
game.setBackgroundColor(0x0a0a2a);
// Add "Glaud" text in small orange to the upper right corner
var glaudText = new Text2('Glaud', {
size: 40,
fill: 0xff8c00 // Orange color
});
glaudText.anchor.set(1, 0); // Anchor to top right
glaudText.x = 2048 - 20; // 20px margin from right edge
glaudText.y = 20; // 20px margin from top edge
game.addChild(glaudText);
// Create a star field background
function createStarfield() {
var starfield = new Container();
// Create stars with different sizes and opacities
for (var i = 0; i < 100; i++) {
var size = Math.random() * 4 + 1;
var star = LK.getAsset('ball', {
anchorX: 0.5,
anchorY: 0.5,
width: size,
height: size,
alpha: Math.random() * 0.7 + 0.3
});
star.x = Math.random() * 2048;
star.y = Math.random() * 2732;
// Create twinkling animation
var duration = Math.random() * 3000 + 2000;
// Create closure to keep reference to the current star
(function (starElement) {
function animateStar() {
tween(starElement, {
alpha: Math.random() * 0.5 + 0.1
}, {
duration: duration,
easing: tween.sinceInOut,
onFinish: function onFinish() {
tween(starElement, {
alpha: Math.random() * 0.7 + 0.3
}, {
duration: duration,
easing: tween.sinceInOut,
onFinish: animateStar
});
}
});
}
animateStar();
})(star);
starfield.addChild(star);
}
game.addChildAt(starfield, 0); // Add at the bottom layer
return starfield;
}
// Create starfield
var starfield = createStarfield();
// Create game elements
var playerPaddle = new Paddle();
var aiPaddle = new Paddle();
var gravityField = new GravityField();
var balls = [];
var powerUps = [];
var centerLine;
var playerScoreText;
var aiScoreText;
var dragTarget = null;
var difficultyTimer;
var powerUpTimer;
var gameActive = false;
var predictionLine;
var mouseX = 0;
var mouseY = 0;
var isMouseOver = false;
// Animate score text function
function animateScoreText(textObject, newValue) {
// Store original scale
var originalScale = {
x: textObject.scale.x,
y: textObject.scale.y
};
// Set the new text
textObject.setText(newValue);
// Animate scale up and back down
tween.stop(textObject.scale);
textObject.scale.set(originalScale.x * 1.5, originalScale.y * 1.5);
tween(textObject.scale, {
x: originalScale.x,
y: originalScale.y
}, {
duration: 500,
easing: tween.elasticOut
});
}
// Initialize game state
function initGame() {
// Setup players
playerPaddle.setup(true);
aiPaddle.setup(false);
game.addChild(playerPaddle);
game.addChild(aiPaddle);
// Setup gravity field
gravityField.setup();
game.addChild(gravityField);
// Create center line with animation
centerLine = new Container();
centerLine.alpha = 0;
for (var i = 0; i < 20; i++) {
var dash = LK.getAsset('paddle', {
anchorX: 0.5,
anchorY: 0.5,
width: 50,
height: 10,
alpha: 0.5
});
dash.x = i * 110;
// Add staggered animation to each dash
(function (element, delay) {
element.alpha = 0;
element.scale.set(0.5);
LK.setTimeout(function () {
tween(element, {
alpha: 0.5
}, {
duration: 400,
easing: tween.linear
});
tween(element.scale, {
x: 1,
y: 1
}, {
duration: 600,
easing: tween.elasticOut
});
}, delay);
})(dash, i * 30);
centerLine.addChild(dash);
}
centerLine.x = 50;
centerLine.y = 2732 / 2;
game.addChild(centerLine);
tween(centerLine, {
alpha: 1
}, {
duration: 800,
easing: tween.linear
});
// Create score text with animated appearance
playerScoreText = new Text2('0', {
size: 150,
fill: 0xFFFFFF
});
playerScoreText.anchor.set(0.5, 1);
playerScoreText.x = 2048 / 2;
playerScoreText.y = 2732 - 50;
playerScoreText.alpha = 0;
playerScoreText.scale.set(0.5);
game.addChild(playerScoreText);
aiScoreText = new Text2('0', {
size: 150,
fill: 0xFFFFFF
});
aiScoreText.anchor.set(0.5, 0);
aiScoreText.x = 2048 / 2;
aiScoreText.y = 50;
aiScoreText.alpha = 0;
aiScoreText.scale.set(0.5);
game.addChild(aiScoreText);
// Animate score text appearance
tween(playerScoreText, {
alpha: 1
}, {
duration: 800,
easing: tween.linear
});
tween(playerScoreText.scale, {
x: 1,
y: 1
}, {
duration: 1000,
easing: tween.elasticOut
});
tween(aiScoreText, {
alpha: 1
}, {
duration: 800,
easing: tween.linear
});
tween(aiScoreText.scale, {
x: 1,
y: 1
}, {
duration: 1000,
easing: tween.elasticOut
});
// Create first ball with delay to allow UI elements to animate in
LK.setTimeout(function () {
createBall();
}, 1200);
// Setup difficulty increases
difficultyTimer = LK.setInterval(function () {
if (gameActive) {
gravityField.increaseStrength();
}
}, 10000);
// Setup power-up spawning
powerUpTimer = LK.setInterval(function () {
if (gameActive && powerUps.length < 1) {
spawnPowerUp();
}
}, 15000);
// Create prediction line
predictionLine = new PredictionLine();
predictionLine.setup();
game.addChild(predictionLine);
// Start game
gameActive = true;
LK.playMusic('gameMusic');
}
// Function to create a new ball
function createBall() {
var ball = new Ball();
ball.reset();
balls.push(ball);
game.addChild(ball);
// Play sound when creating a new ball
LK.getSound('powerup').play();
return ball;
}
// Function to spawn a power-up
function spawnPowerUp() {
var powerUp = new PowerUp();
var type = Math.random() > 0.5 ? 'gravity' : 'multiball';
powerUp.setup(type);
powerUps.push(powerUp);
game.addChild(powerUp);
// Play sound when spawning power-up (now handled in PowerUp.setup)
}
// Check for collisions between ball and paddle
function checkPaddleCollision(ball, paddle) {
if (ball.y + 25 >= paddle.y - 25 && ball.y - 25 <= paddle.y + 25) {
if (ball.x + 25 >= paddle.x - 125 && ball.x - 25 <= paddle.x + 125) {
// Calculate bounce angle based on where ball hit the paddle
var relativeIntersectX = ball.x - paddle.x;
var normalizedRelativeIntersectionX = relativeIntersectX / 125;
var bounceAngle = normalizedRelativeIntersectionX * (Math.PI / 3); // Maximum angle: 60 degrees
// Invert Y velocity and adjust X velocity based on bounce angle
ball.velocityY = -ball.velocityY;
ball.velocityX = ball.speed * Math.sin(bounceAngle);
// Increase speed slightly
ball.speed = Math.min(20, ball.speed * 1.05);
// Play bounce sound
LK.getSound('bounce').play();
// Enhanced paddle hit effect - scale and flash
tween.stop(paddle.scale);
paddle.scale.set(1.2, 0.8);
tween(paddle.scale, {
x: 1,
y: 1
}, {
duration: 300,
easing: tween.elasticOut
});
// Flash paddle with color based on player
LK.effects.flashObject(paddle, paddle.isPlayer ? 0x4287f5 : 0xff5733, 300);
return true;
}
}
return false;
}
// Check for collisions between ball and power-up
function checkPowerUpCollision(ball) {
for (var i = powerUps.length - 1; i >= 0; i--) {
var powerUp = powerUps[i];
if (ball.intersects(powerUp)) {
// Create explosion effect at power-up location
var explosionEffect = new Container();
explosionEffect.x = powerUp.x;
explosionEffect.y = powerUp.y;
game.addChild(explosionEffect);
// Create multiple particles for explosion
for (var p = 0; p < 10; p++) {
var particle = LK.getAsset(powerUp.getType() === 'gravity' ? 'powerUpGravity' : 'powerUpMultiBall', {
anchorX: 0.5,
anchorY: 0.5,
scale: 0.5,
alpha: 0.8
});
// Randomize particle position and movement
var angle = Math.random() * Math.PI * 2;
var distance = Math.random() * 100 + 50;
var duration = Math.random() * 500 + 500;
particle.x = 0;
particle.y = 0;
explosionEffect.addChild(particle);
// Animate particle movement and fade
tween(particle, {
x: Math.cos(angle) * distance,
y: Math.sin(angle) * distance,
alpha: 0
}, {
duration: duration,
easing: tween.cubicOut
});
// Ensure particle has proper scale object
particle.scale = particle.scale || {
x: 0.5,
y: 0.5
};
// Ensure particle has scale object before tweening
if (!particle.scale || typeof particle.scale === 'number') {
particle.scale = {
x: 0.5,
y: 0.5
};
}
// Now safely tween the scale property
tween(particle.scale, {
x: 0.1,
y: 0.1
}, {
duration: duration,
easing: tween.linear
});
}
// Remove explosion container after animation completes
LK.setTimeout(function () {
explosionEffect.destroy();
}, 1000);
// Apply power-up effect with enhanced visuals
if (powerUp.getType() === 'gravity') {
// Shake gravity field for dramatic effect
var originalX = gravityField.x;
var originalY = gravityField.y;
var shakeCount = 0;
var shakeInterval = LK.setInterval(function () {
gravityField.x = originalX + (Math.random() * 20 - 10);
gravityField.y = originalY + (Math.random() * 20 - 10);
shakeCount++;
if (shakeCount >= 10) {
LK.clearInterval(shakeInterval);
gravityField.x = originalX;
gravityField.y = originalY;
gravityField.reverseGravity();
}
}, 50);
} else if (powerUp.getType() === 'multiball') {
for (var j = 0; j < 2; j++) {
var newBall = createBall();
// Add a scale animation to new balls
newBall.scale.set(0.2);
tween(newBall.scale, {
x: 1,
y: 1
}, {
duration: 500,
easing: tween.elasticOut
});
}
}
// Play sound and remove power-up
LK.getSound('powerup').play();
LK.effects.flashScreen(0x33FF57, 300);
powerUp.destroy();
powerUps.splice(i, 1);
}
}
}
// Handle AI movement
function updateAI() {
// Find the closest ball
var closestBall = null;
var closestDistance = Infinity;
for (var i = 0; i < balls.length; i++) {
var ball = balls[i];
if (ball.velocityY < 0) {
// Ball is moving towards AI
var distance = Math.abs(ball.x - aiPaddle.x);
if (distance < closestDistance) {
closestDistance = distance;
closestBall = ball;
}
}
}
if (closestBall) {
// Add some prediction based on velocity and gravity
var predictedX = closestBall.x + closestBall.velocityX * (aiPaddle.y - closestBall.y) / Math.abs(closestBall.velocityY);
aiPaddle.targetX = predictedX;
// Add some difficulty scaling
var difficultyFactor = 0.3 + gravityField.strength / gravityField.maxStrength * 0.6;
aiPaddle.targetX = aiPaddle.x + (predictedX - aiPaddle.x) * difficultyFactor;
}
}
// Event handler for touch/mouse down
game.down = function (x, y, obj) {
// Only allow dragging the player paddle
if (y > 2732 / 2) {
dragTarget = playerPaddle;
playerPaddle.targetX = x;
isMouseOver = true;
mouseX = x;
mouseY = y;
}
};
// Event handler for touch/mouse move
game.move = function (x, y, obj) {
mouseX = x;
mouseY = y;
isMouseOver = true;
if (dragTarget) {
playerPaddle.targetX = x;
}
};
// Event handler for touch/mouse up
game.up = function (x, y, obj) {
dragTarget = null;
};
// Game update loop
game.update = function () {
if (!gameActive) {
return;
}
// Update AI
updateAI();
// Update paddles
playerPaddle.update();
aiPaddle.update();
// Update prediction line
if (isMouseOver && balls.length > 0) {
// Show prediction line from closest ball to mouse position
var closestBall = null;
var minDistance = Infinity;
for (var i = 0; i < balls.length; i++) {
var dist = Math.sqrt(Math.pow(balls[i].x - mouseX, 2) + Math.pow(balls[i].y - mouseY, 2));
if (dist < minDistance) {
minDistance = dist;
closestBall = balls[i];
}
}
if (closestBall) {
predictionLine.updatePosition(closestBall.x, closestBall.y, mouseX, playerPaddle.y);
}
} else {
predictionLine.hide();
}
// Reset mouse tracking after update
isMouseOver = false;
// Update balls
for (var i = balls.length - 1; i >= 0; i--) {
var ball = balls[i];
// Apply gravity
gravityField.applyGravityTo(ball);
// Update ball position
ball.update();
// Check for paddle collisions
var hitPlayer = checkPaddleCollision(ball, playerPaddle);
var hitAI = checkPaddleCollision(ball, aiPaddle);
// Check if ball is out of bounds (scoring)
if (ball.y > 2732 + 50) {
// AI scores
aiPaddle.score++;
// Animated score update
animateScoreText(aiScoreText, aiPaddle.score.toString());
LK.getSound('score').play();
// Create a visual effect at ball position before destroying
var scoreEffect = LK.getAsset('ball', {
anchorX: 0.5,
anchorY: 0.5,
x: ball.x,
y: 2732,
alpha: 0.8,
scaleX: 1,
scaleY: 1,
tint: 0xff5733
});
game.addChild(scoreEffect);
tween(scoreEffect.scale, {
x: 3,
y: 3
}, {
duration: 500,
easing: tween.cubicOut,
onFinish: function onFinish() {
tween(scoreEffect, {
alpha: 0
}, {
duration: 300,
easing: tween.linear,
onFinish: function onFinish() {
scoreEffect.destroy();
}
});
}
});
ball.destroy();
balls.splice(i, 1);
if (balls.length === 0) {
createBall();
}
// Check for game over
if (aiPaddle.score >= 10) {
gameActive = false;
LK.showGameOver();
}
} else if (ball.y < -50) {
// Player scores
playerPaddle.score++;
// Animated score update
animateScoreText(playerScoreText, playerPaddle.score.toString());
LK.getSound('score').play();
// Create a visual effect at ball position before destroying
var scoreEffect = LK.getAsset('ball', {
anchorX: 0.5,
anchorY: 0.5,
x: ball.x,
y: 0,
alpha: 0.8,
scaleX: 1,
scaleY: 1,
tint: 0x4287f5
});
game.addChild(scoreEffect);
tween(scoreEffect.scale, {
x: 3,
y: 3
}, {
duration: 500,
easing: tween.cubicOut,
onFinish: function onFinish() {
tween(scoreEffect, {
alpha: 0
}, {
duration: 300,
easing: tween.linear,
onFinish: function onFinish() {
scoreEffect.destroy();
}
});
}
});
ball.destroy();
balls.splice(i, 1);
if (balls.length === 0) {
createBall();
}
// Check for win
if (playerPaddle.score >= 10) {
gameActive = false;
LK.showYouWin();
}
}
// Check for power-up collisions
checkPowerUpCollision(ball);
}
};
// Initialize the game when code loads
initGame();