User prompt
If Player Wins the Game, Play P1won Sound
User prompt
Background_Music Always Play Never Stop And If The Player Wins The Game, P1won Seini Play
User prompt
If paddle_player wins the game, play p1won sound
User prompt
Play pAI sound when ball hits racket
User prompt
Play p1 sound when ball hits paddle_player
User prompt
Play Background_Music in All Game Modes
User prompt
Show the Pause Button Only While Playing the Game
User prompt
Also add a button to the main menu that explains the game and prevent power-ups from appearing in the main menu before the game starts.
User prompt
Background_Music should start playing when the game starts and stop when the game ends.
User prompt
When the ball hits the Power-Up, the Power-Up Name will appear on the screen for half a second.
User prompt
When the ball hits the Power-Up, the Power-Up's Attribute will appear on the screen for half a second
User prompt
Play powerup_collect when ball hits powerup
User prompt
When the Ball Hits the Power-Up, Give It to Everyone Special and No Sound Fix Sound
User prompt
Make the Power-Ups a Click Bigger Add the Named Sound to the Game and Let the Ball Hit the Power-Ups
User prompt
Please fix the bug: 'TypeError: Cannot read properties of undefined (reading 'x')' in or related to this line: 'return !(a.x + a.width / 2 < b.x - b.width / 2 || a.x - a.width / 2 > b.x + b.width / 2 || a.y + a.height / 2 < b.y - b.height / 2 || a.y - a.height / 2 > b.y + b.height / 2);' Line Number: 446
User prompt
The speed of the ball should change according to the difficulty of the game. In Easy, the ball should be slow. In Normal, it should be medium speed. In Hard, it should be fast.
User prompt
Add Button to Return to Main Menu When Stop Button is Pressed
User prompt
Still Broken Fix Power Up
User prompt
Power-Ups Are Buggy Fix Them
User prompt
Please fix the bug: 'ReferenceError: powerUps is not defined' in or related to this line: 'var p = new PowerUp(pType);' Line Number: 425
User prompt
Please fix the bug: 'ReferenceError: powerUps is not defined' in or related to this line: 'for (var i = powerUps.length - 1; i >= 0; i--) {' Line Number: 429
User prompt
Add Power-Ups to the Game
User prompt
The Ball Can Move to Random Places, But This Is Rare
User prompt
The ball may sometimes appear to go up and then go down, or vice versa.
User prompt
The ball may be thrown to us fake, meaning sometimes it may pretend to go left but go right, and sometimes it may pretend to go right but go left.
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // Ball class var Ball = Container.expand(function () { var self = Container.call(this); var ballSprite = self.attachAsset('ball', { anchorX: 0.5, anchorY: 0.5 }); // Ball velocity self.vx = 0; self.vy = 0; // Ball speed (pixels per tick) self.speed = 18; // Used for rhythm sync self.baseSpeed = 18; self.rhythmBoost = 0; // Used to prevent multiple scoring on one pass self.lastScored = false; // Ball update self.update = function () { self.x += self.vx * (self.speed + self.rhythmBoost); self.y += self.vy * (self.speed + self.rhythmBoost); }; // Reset ball to center, random direction self.reset = function (direction) { self.x = 2048 / 2; self.y = 2732 / 2; // Start ball slow, then accelerate after 1 second self.speed = 4; self.rhythmBoost = 0; LK.setTimeout(function () { // Only accelerate if this ball is still in play and hasn't been reset again if (self && typeof self.speed !== "undefined" && self.speed === 4) { self.speed = self.baseSpeed; } }, 1000); // --- Fakeout logic --- // direction: 1 = to player, -1 = to AI // Sometimes (30% chance) the ball will "fake" left or right before going the real direction var fakeoutChance = 0.3; var doFakeout = Math.random() < fakeoutChance; var fakeoutDuration = 220; // ms var realAngle, fakeAngle; if (doFakeout) { // Decide real direction (left or right) var goLeft = Math.random() < 0.5; // Real angle: aim slightly left or right, but always toward the correct side realAngle = (goLeft ? -0.35 : 0.35) + (direction === 1 ? Math.PI / 2 : -Math.PI / 2); // Fake angle: aim the opposite way, but still toward the correct side fakeAngle = (goLeft ? 0.35 : -0.35) + (direction === 1 ? Math.PI / 2 : -Math.PI / 2); // Start with fake direction self.vx = Math.sin(fakeAngle); self.vy = Math.cos(fakeAngle) * direction; // After a short delay, switch to real direction LK.setTimeout(function () { if (self && typeof self.vx !== "undefined" && typeof self.vy !== "undefined") { self.vx = Math.sin(realAngle); self.vy = Math.cos(realAngle) * direction; } }, fakeoutDuration); } else { // Normal random angle var angle = Math.random() * 0.5 - 0.25 + (direction === 1 ? Math.PI / 2 : -Math.PI / 2); self.vx = Math.sin(angle); self.vy = Math.cos(angle) * direction; } self.lastScored = false; }; return self; }); // Paddle class var Paddle = Container.expand(function () { var self = Container.call(this); // Set in init self.isPlayer = false; // Attach asset var paddleSprite = self.attachAsset('paddle_player', { anchorX: 0.5, anchorY: 0.5 }); // Set color for AI self.setAI = function () { paddleSprite.destroy(); self.attachAsset('paddle_ai', { anchorX: 0.5, anchorY: 0.5 }); self.isPlayer = false; }; // Set color for player self.setPlayer = function () { paddleSprite.destroy(); self.attachAsset('paddle_player', { anchorX: 0.5, anchorY: 0.5 }); self.isPlayer = true; }; // Clamp paddle inside table self.clamp = function () { var halfW = self.width / 2; if (self.x < halfW) self.x = halfW; if (self.x > 2048 - halfW) self.x = 2048 - halfW; }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x228B22 // Table green }); /**** * Game Code ****/ // --- Main Menu and Difficulty Selection --- var menuContainer = new Container(); var menuBg = LK.getAsset('table', { anchorX: 0, anchorY: 0, x: 0, y: 0 }); menuContainer.addChild(menuBg); var titleText = new Text2('PING PONG RHYTHM SMASH', { size: 120, fill: 0xffffff }); titleText.anchor.set(0.5, 0); titleText.x = 2048 / 2; titleText.y = 320; menuContainer.addChild(titleText); var easyBtn = new Text2('EASY', { size: 100, fill: 0x99ff99 }); easyBtn.anchor.set(0.5, 0.5); easyBtn.x = 2048 / 2; easyBtn.y = 800; menuContainer.addChild(easyBtn); var normalBtn = new Text2('NORMAL', { size: 100, fill: 0xffff99 }); normalBtn.anchor.set(0.5, 0.5); normalBtn.x = 2048 / 2; normalBtn.y = 1050; menuContainer.addChild(normalBtn); var hardBtn = new Text2('HARD', { size: 100, fill: 0xff9999 }); hardBtn.anchor.set(0.5, 0.5); hardBtn.x = 2048 / 2; hardBtn.y = 1300; menuContainer.addChild(hardBtn); var selectedDifficulty = null; var aiDifficulty = "normal"; // default function startGameWithDifficulty(diff) { selectedDifficulty = diff; if (diff === "easy") { aiDifficulty = "easy"; } else if (diff === "normal") { aiDifficulty = "normal"; } else { aiDifficulty = "hard"; } menuContainer.destroy(); // Only start the game (and thus pause logic) after menu is gone initGame(); } easyBtn.down = function (x, y, obj) { startGameWithDifficulty("easy"); }; normalBtn.down = function (x, y, obj) { startGameWithDifficulty("normal"); }; hardBtn.down = function (x, y, obj) { startGameWithDifficulty("hard"); }; game.addChild(menuContainer); // --- Game variables (initialized in initGame) --- var tableBg, net, playerPaddle, aiPaddle, ball, playerScore, aiScore, scoreText; var rhythmInterval, rhythmTimer, lastRhythmTick, rhythmBoostAmount, rhythmBoostDuration; var dragging; // --- Game initialization function --- function initGame() { // Table background tableBg = LK.getAsset('table', { anchorX: 0, anchorY: 0, x: 0, y: 0 }); game.addChild(tableBg); // Net net = LK.getAsset('net', { anchorX: 0, anchorY: 0.5, x: 0, y: 2732 / 2 }); game.addChild(net); // Player paddle playerPaddle = new Paddle(); playerPaddle.setPlayer(); playerPaddle.x = 2048 / 2; playerPaddle.y = 2732 - 180; game.addChild(playerPaddle); // AI paddle aiPaddle = new Paddle(); aiPaddle.setAI(); aiPaddle.x = 2048 / 2; aiPaddle.y = 180; game.addChild(aiPaddle); // Ball ball = new Ball(); ball.reset(Math.random() < 0.5 ? 1 : -1); game.addChild(ball); // Score playerScore = 0; aiScore = 0; // Score display scoreText = new Text2('0 : 0', { size: 120, fill: 0xFFFFFF }); scoreText.anchor.set(0.5, 0); LK.gui.top.addChild(scoreText); // Rhythm variables rhythmInterval = 600; // ms per beat (100 BPM) rhythmTimer = 0; lastRhythmTick = 0; rhythmBoostAmount = 8; rhythmBoostDuration = 180; // ms // Start music LK.playMusic('rhythmtrack'); // Dragging dragging = false; } // Move handler (player paddle) function handleMove(x, y, obj) { if (dragging) { // Clamp to table var newX = x; var halfW = playerPaddle.width / 2; if (newX < halfW) newX = halfW; if (newX > 2048 - halfW) newX = 2048 - halfW; playerPaddle.x = newX; } } game.move = handleMove; game.down = function (x, y, obj) { // Only start drag if touch is on/near player paddle var localY = y; if (typeof playerPaddle !== "undefined" && playerPaddle && typeof playerPaddle.y !== "undefined" && typeof playerPaddle.height !== "undefined" && localY > playerPaddle.y - playerPaddle.height / 2 - 80) { dragging = true; handleMove(x, y, obj); } }; game.up = function (x, y, obj) { dragging = false; }; // Helper: rectangle collision function rectsIntersect(a, b) { return !(a.x + a.width / 2 < b.x - b.width / 2 || a.x - a.width / 2 > b.x + b.width / 2 || a.y + a.height / 2 < b.y - b.height / 2 || a.y - a.height / 2 > b.y + b.height / 2); } // Update score display function updateScore() { if (typeof scoreText !== "undefined" && scoreText && typeof scoreText.setText === "function") { scoreText.setText(playerScore + ' : ' + aiScore); } } // Game update game.update = function () { // Rhythm sync: every rhythmInterval ms, boost ball speed and randomize direction slightly var now = LK.ticks * 1000 / 60; if (now - lastRhythmTick > rhythmInterval) { lastRhythmTick = now; // Boost ball.rhythmBoost = rhythmBoostAmount; // Add a small random angle to ball direction var angle = Math.atan2(ball.vy, ball.vx); var delta = (Math.random() - 0.5) * 0.3; // -0.15 to 0.15 radians angle += delta; var mag = Math.sqrt(ball.vx * ball.vx + ball.vy * ball.vy); ball.vx = Math.cos(angle) * (mag > 0 ? 1 : 1); ball.vy = Math.sin(angle) * (mag > 0 ? 1 : 1); // Animate ball (flash) LK.effects.flashObject(ball, 0xffff00, 120); } // Remove boost after duration if (typeof ball !== "undefined" && ball && ball.rhythmBoost > 0 && now - lastRhythmTick > rhythmBoostDuration) { ball.rhythmBoost = 0; } // Ball update if (typeof ball !== "undefined" && ball && typeof ball.update === "function") { ball.update(); } // Ball collision with left/right walls if (typeof ball !== "undefined" && ball && typeof ball.x !== "undefined" && typeof ball.width !== "undefined") { if (ball.x < ball.width / 2) { ball.x = ball.width / 2; ball.vx *= -1; } if (ball.x > 2048 - ball.width / 2) { ball.x = 2048 - ball.width / 2; ball.vx *= -1; } } // Ball collision with player paddle if (typeof ball !== "undefined" && ball && typeof ball.vy !== "undefined" && ball.vy > 0 && rectsIntersect(ball, playerPaddle)) { ball.y = playerPaddle.y - playerPaddle.height / 2 - ball.height / 2; ball.vy *= -1; // Add a bit of angle based on where it hit the paddle var offset = (ball.x - playerPaddle.x) / (playerPaddle.width / 2); var angle = offset * 0.5; // up to ~30deg var speed = Math.sqrt(ball.vx * ball.vx + ball.vy * ball.vy); var newAngle = Math.atan2(-ball.vy, ball.vx) + angle; ball.vx = Math.cos(newAngle); ball.vy = -Math.abs(Math.sin(newAngle)); // Increase speed ball.speed += 1.2; // Flash paddle LK.effects.flashObject(playerPaddle, 0x99ccff, 120); } // Ball collision with AI paddle if (typeof ball !== "undefined" && ball && typeof ball.vy !== "undefined" && ball.vy < 0 && rectsIntersect(ball, aiPaddle)) { ball.y = aiPaddle.y + aiPaddle.height / 2 + ball.height / 2; ball.vy *= -1; // Add a bit of angle based on where it hit the paddle var offset = (ball.x - aiPaddle.x) / (aiPaddle.width / 2); var angle = offset * 0.5; var speed = Math.sqrt(ball.vx * ball.vx + ball.vy * ball.vy); var newAngle = Math.atan2(-ball.vy, ball.vx) + angle; ball.vx = Math.cos(newAngle); ball.vy = Math.abs(Math.sin(newAngle)); // Increase speed ball.speed += 1.2; // Flash paddle LK.effects.flashObject(aiPaddle, 0xff99bb, 120); } // Ball out of bounds (top/bottom) if (typeof ball !== "undefined" && ball && typeof ball.y !== "undefined" && typeof ball.height !== "undefined" && !ball.lastScored && ball.y < -ball.height / 2) { // Player scores playerScore += 1; updateScore(); ball.lastScored = true; LK.effects.flashScreen(0x3399ff, 400); if (playerScore >= 7) { LK.showYouWin(); return; } ball.reset(-1); } if (typeof ball !== "undefined" && ball && typeof ball.y !== "undefined" && typeof ball.height !== "undefined" && !ball.lastScored && ball.y > 2732 + ball.height / 2) { // AI scores aiScore += 1; updateScore(); ball.lastScored = true; LK.effects.flashScreen(0xff3366, 400); if (aiScore >= 7) { LK.showGameOver(); return; } ball.reset(1); } // --- AI paddle movement: difficulty-based --- if (typeof aiMoveMode === "undefined") { var aiMoveMode = "random"; // "random" or "track" var aiMoveTimer = 0; var aiMoveTargetX = typeof aiPaddle !== "undefined" && aiPaddle && typeof aiPaddle.x !== "undefined" ? aiPaddle.x : 2048 / 2; var aiMoveDir = 0; } // Difficulty parameters var aiTrackChance, aiRandomOffset, aiSpeedBase, aiSpeedMax, aiStandStillChance, aiReactDelay; if (typeof aiDifficulty === "undefined") aiDifficulty = "normal"; if (aiDifficulty === "easy") { aiTrackChance = 0.10; aiRandomOffset = 700; aiSpeedBase = 9; aiSpeedMax = 18; aiStandStillChance = 0.40; aiReactDelay = 110; } else if (aiDifficulty === "hard") { aiTrackChance = 0.6; aiRandomOffset = 180; aiSpeedBase = 24; aiSpeedMax = 44; aiStandStillChance = 0.10; aiReactDelay = 40; } else { // normal aiTrackChance = 0.20; aiRandomOffset = 500; aiSpeedBase = 15; aiSpeedMax = 28; aiStandStillChance = 0.25; aiReactDelay = 80; } aiMoveTimer--; if (aiMoveTimer <= 0) { // Switch mode every 0.5-2 seconds depending on difficulty if (Math.random() < aiTrackChance) { aiMoveMode = "track"; if (typeof ball !== "undefined" && ball && typeof ball.x !== "undefined") { aiMoveTargetX = ball.x + (Math.random() - 0.5) * aiRandomOffset; } else { aiMoveTargetX = 2048 / 2; } aiMoveTimer = aiReactDelay + Math.floor(Math.random() * aiReactDelay); } else { aiMoveMode = "random"; if (typeof aiPaddle !== "undefined" && aiPaddle && typeof aiPaddle.width !== "undefined") { aiMoveTargetX = Math.random() * (2048 - aiPaddle.width) + aiPaddle.width / 2; } else { aiMoveTargetX = 2048 / 2; } aiMoveTimer = aiReactDelay + Math.floor(Math.random() * (aiReactDelay + 30)); } // Randomly sometimes just stand still if (Math.random() < aiStandStillChance) { if (typeof aiPaddle !== "undefined" && aiPaddle && typeof aiPaddle.x !== "undefined") { aiMoveTargetX = aiPaddle.x; } else { aiMoveTargetX = 2048 / 2; } } } var aiSpeed = aiSpeedBase; if (typeof ball !== "undefined" && ball && typeof ball.speed !== "undefined") { aiSpeed += Math.min(ball.speed * 1.2, aiSpeedMax); } if (aiMoveMode === "track") { if (typeof aiPaddle !== "undefined" && aiPaddle && typeof aiPaddle.x !== "undefined" && typeof aiMoveTargetX !== "undefined" && Math.abs(aiPaddle.x - aiMoveTargetX) > 8) { if (aiPaddle.x < aiMoveTargetX) { aiPaddle.x += aiSpeed; if (aiPaddle.x > aiMoveTargetX) aiPaddle.x = aiMoveTargetX; } else { aiPaddle.x -= aiSpeed; if (aiPaddle.x < aiMoveTargetX) aiPaddle.x = aiMoveTargetX; } aiPaddle.clamp(); } } else { // random mode: move toward random target if (typeof aiPaddle !== "undefined" && aiPaddle && typeof aiPaddle.x !== "undefined" && typeof aiMoveTargetX !== "undefined" && Math.abs(aiPaddle.x - aiMoveTargetX) > 8) { if (aiPaddle.x < aiMoveTargetX) { aiPaddle.x += aiSpeed * 0.7; if (aiPaddle.x > aiMoveTargetX) aiPaddle.x = aiMoveTargetX; } else { aiPaddle.x -= aiSpeed * 0.7; if (aiPaddle.x < aiMoveTargetX) aiPaddle.x = aiMoveTargetX; } aiPaddle.clamp(); } } // (Gun and bullet logic removed) }; // Initial score updateScore();
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Ball class
var Ball = Container.expand(function () {
var self = Container.call(this);
var ballSprite = self.attachAsset('ball', {
anchorX: 0.5,
anchorY: 0.5
});
// Ball velocity
self.vx = 0;
self.vy = 0;
// Ball speed (pixels per tick)
self.speed = 18;
// Used for rhythm sync
self.baseSpeed = 18;
self.rhythmBoost = 0;
// Used to prevent multiple scoring on one pass
self.lastScored = false;
// Ball update
self.update = function () {
self.x += self.vx * (self.speed + self.rhythmBoost);
self.y += self.vy * (self.speed + self.rhythmBoost);
};
// Reset ball to center, random direction
self.reset = function (direction) {
self.x = 2048 / 2;
self.y = 2732 / 2;
// Start ball slow, then accelerate after 1 second
self.speed = 4;
self.rhythmBoost = 0;
LK.setTimeout(function () {
// Only accelerate if this ball is still in play and hasn't been reset again
if (self && typeof self.speed !== "undefined" && self.speed === 4) {
self.speed = self.baseSpeed;
}
}, 1000);
// --- Fakeout logic ---
// direction: 1 = to player, -1 = to AI
// Sometimes (30% chance) the ball will "fake" left or right before going the real direction
var fakeoutChance = 0.3;
var doFakeout = Math.random() < fakeoutChance;
var fakeoutDuration = 220; // ms
var realAngle, fakeAngle;
if (doFakeout) {
// Decide real direction (left or right)
var goLeft = Math.random() < 0.5;
// Real angle: aim slightly left or right, but always toward the correct side
realAngle = (goLeft ? -0.35 : 0.35) + (direction === 1 ? Math.PI / 2 : -Math.PI / 2);
// Fake angle: aim the opposite way, but still toward the correct side
fakeAngle = (goLeft ? 0.35 : -0.35) + (direction === 1 ? Math.PI / 2 : -Math.PI / 2);
// Start with fake direction
self.vx = Math.sin(fakeAngle);
self.vy = Math.cos(fakeAngle) * direction;
// After a short delay, switch to real direction
LK.setTimeout(function () {
if (self && typeof self.vx !== "undefined" && typeof self.vy !== "undefined") {
self.vx = Math.sin(realAngle);
self.vy = Math.cos(realAngle) * direction;
}
}, fakeoutDuration);
} else {
// Normal random angle
var angle = Math.random() * 0.5 - 0.25 + (direction === 1 ? Math.PI / 2 : -Math.PI / 2);
self.vx = Math.sin(angle);
self.vy = Math.cos(angle) * direction;
}
self.lastScored = false;
};
return self;
});
// Paddle class
var Paddle = Container.expand(function () {
var self = Container.call(this);
// Set in init
self.isPlayer = false;
// Attach asset
var paddleSprite = self.attachAsset('paddle_player', {
anchorX: 0.5,
anchorY: 0.5
});
// Set color for AI
self.setAI = function () {
paddleSprite.destroy();
self.attachAsset('paddle_ai', {
anchorX: 0.5,
anchorY: 0.5
});
self.isPlayer = false;
};
// Set color for player
self.setPlayer = function () {
paddleSprite.destroy();
self.attachAsset('paddle_player', {
anchorX: 0.5,
anchorY: 0.5
});
self.isPlayer = true;
};
// Clamp paddle inside table
self.clamp = function () {
var halfW = self.width / 2;
if (self.x < halfW) self.x = halfW;
if (self.x > 2048 - halfW) self.x = 2048 - halfW;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x228B22 // Table green
});
/****
* Game Code
****/
// --- Main Menu and Difficulty Selection ---
var menuContainer = new Container();
var menuBg = LK.getAsset('table', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0
});
menuContainer.addChild(menuBg);
var titleText = new Text2('PING PONG RHYTHM SMASH', {
size: 120,
fill: 0xffffff
});
titleText.anchor.set(0.5, 0);
titleText.x = 2048 / 2;
titleText.y = 320;
menuContainer.addChild(titleText);
var easyBtn = new Text2('EASY', {
size: 100,
fill: 0x99ff99
});
easyBtn.anchor.set(0.5, 0.5);
easyBtn.x = 2048 / 2;
easyBtn.y = 800;
menuContainer.addChild(easyBtn);
var normalBtn = new Text2('NORMAL', {
size: 100,
fill: 0xffff99
});
normalBtn.anchor.set(0.5, 0.5);
normalBtn.x = 2048 / 2;
normalBtn.y = 1050;
menuContainer.addChild(normalBtn);
var hardBtn = new Text2('HARD', {
size: 100,
fill: 0xff9999
});
hardBtn.anchor.set(0.5, 0.5);
hardBtn.x = 2048 / 2;
hardBtn.y = 1300;
menuContainer.addChild(hardBtn);
var selectedDifficulty = null;
var aiDifficulty = "normal"; // default
function startGameWithDifficulty(diff) {
selectedDifficulty = diff;
if (diff === "easy") {
aiDifficulty = "easy";
} else if (diff === "normal") {
aiDifficulty = "normal";
} else {
aiDifficulty = "hard";
}
menuContainer.destroy();
// Only start the game (and thus pause logic) after menu is gone
initGame();
}
easyBtn.down = function (x, y, obj) {
startGameWithDifficulty("easy");
};
normalBtn.down = function (x, y, obj) {
startGameWithDifficulty("normal");
};
hardBtn.down = function (x, y, obj) {
startGameWithDifficulty("hard");
};
game.addChild(menuContainer);
// --- Game variables (initialized in initGame) ---
var tableBg, net, playerPaddle, aiPaddle, ball, playerScore, aiScore, scoreText;
var rhythmInterval, rhythmTimer, lastRhythmTick, rhythmBoostAmount, rhythmBoostDuration;
var dragging;
// --- Game initialization function ---
function initGame() {
// Table background
tableBg = LK.getAsset('table', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0
});
game.addChild(tableBg);
// Net
net = LK.getAsset('net', {
anchorX: 0,
anchorY: 0.5,
x: 0,
y: 2732 / 2
});
game.addChild(net);
// Player paddle
playerPaddle = new Paddle();
playerPaddle.setPlayer();
playerPaddle.x = 2048 / 2;
playerPaddle.y = 2732 - 180;
game.addChild(playerPaddle);
// AI paddle
aiPaddle = new Paddle();
aiPaddle.setAI();
aiPaddle.x = 2048 / 2;
aiPaddle.y = 180;
game.addChild(aiPaddle);
// Ball
ball = new Ball();
ball.reset(Math.random() < 0.5 ? 1 : -1);
game.addChild(ball);
// Score
playerScore = 0;
aiScore = 0;
// Score display
scoreText = new Text2('0 : 0', {
size: 120,
fill: 0xFFFFFF
});
scoreText.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreText);
// Rhythm variables
rhythmInterval = 600; // ms per beat (100 BPM)
rhythmTimer = 0;
lastRhythmTick = 0;
rhythmBoostAmount = 8;
rhythmBoostDuration = 180; // ms
// Start music
LK.playMusic('rhythmtrack');
// Dragging
dragging = false;
}
// Move handler (player paddle)
function handleMove(x, y, obj) {
if (dragging) {
// Clamp to table
var newX = x;
var halfW = playerPaddle.width / 2;
if (newX < halfW) newX = halfW;
if (newX > 2048 - halfW) newX = 2048 - halfW;
playerPaddle.x = newX;
}
}
game.move = handleMove;
game.down = function (x, y, obj) {
// Only start drag if touch is on/near player paddle
var localY = y;
if (typeof playerPaddle !== "undefined" && playerPaddle && typeof playerPaddle.y !== "undefined" && typeof playerPaddle.height !== "undefined" && localY > playerPaddle.y - playerPaddle.height / 2 - 80) {
dragging = true;
handleMove(x, y, obj);
}
};
game.up = function (x, y, obj) {
dragging = false;
};
// Helper: rectangle collision
function rectsIntersect(a, b) {
return !(a.x + a.width / 2 < b.x - b.width / 2 || a.x - a.width / 2 > b.x + b.width / 2 || a.y + a.height / 2 < b.y - b.height / 2 || a.y - a.height / 2 > b.y + b.height / 2);
}
// Update score display
function updateScore() {
if (typeof scoreText !== "undefined" && scoreText && typeof scoreText.setText === "function") {
scoreText.setText(playerScore + ' : ' + aiScore);
}
}
// Game update
game.update = function () {
// Rhythm sync: every rhythmInterval ms, boost ball speed and randomize direction slightly
var now = LK.ticks * 1000 / 60;
if (now - lastRhythmTick > rhythmInterval) {
lastRhythmTick = now;
// Boost
ball.rhythmBoost = rhythmBoostAmount;
// Add a small random angle to ball direction
var angle = Math.atan2(ball.vy, ball.vx);
var delta = (Math.random() - 0.5) * 0.3; // -0.15 to 0.15 radians
angle += delta;
var mag = Math.sqrt(ball.vx * ball.vx + ball.vy * ball.vy);
ball.vx = Math.cos(angle) * (mag > 0 ? 1 : 1);
ball.vy = Math.sin(angle) * (mag > 0 ? 1 : 1);
// Animate ball (flash)
LK.effects.flashObject(ball, 0xffff00, 120);
}
// Remove boost after duration
if (typeof ball !== "undefined" && ball && ball.rhythmBoost > 0 && now - lastRhythmTick > rhythmBoostDuration) {
ball.rhythmBoost = 0;
}
// Ball update
if (typeof ball !== "undefined" && ball && typeof ball.update === "function") {
ball.update();
}
// Ball collision with left/right walls
if (typeof ball !== "undefined" && ball && typeof ball.x !== "undefined" && typeof ball.width !== "undefined") {
if (ball.x < ball.width / 2) {
ball.x = ball.width / 2;
ball.vx *= -1;
}
if (ball.x > 2048 - ball.width / 2) {
ball.x = 2048 - ball.width / 2;
ball.vx *= -1;
}
}
// Ball collision with player paddle
if (typeof ball !== "undefined" && ball && typeof ball.vy !== "undefined" && ball.vy > 0 && rectsIntersect(ball, playerPaddle)) {
ball.y = playerPaddle.y - playerPaddle.height / 2 - ball.height / 2;
ball.vy *= -1;
// Add a bit of angle based on where it hit the paddle
var offset = (ball.x - playerPaddle.x) / (playerPaddle.width / 2);
var angle = offset * 0.5; // up to ~30deg
var speed = Math.sqrt(ball.vx * ball.vx + ball.vy * ball.vy);
var newAngle = Math.atan2(-ball.vy, ball.vx) + angle;
ball.vx = Math.cos(newAngle);
ball.vy = -Math.abs(Math.sin(newAngle));
// Increase speed
ball.speed += 1.2;
// Flash paddle
LK.effects.flashObject(playerPaddle, 0x99ccff, 120);
}
// Ball collision with AI paddle
if (typeof ball !== "undefined" && ball && typeof ball.vy !== "undefined" && ball.vy < 0 && rectsIntersect(ball, aiPaddle)) {
ball.y = aiPaddle.y + aiPaddle.height / 2 + ball.height / 2;
ball.vy *= -1;
// Add a bit of angle based on where it hit the paddle
var offset = (ball.x - aiPaddle.x) / (aiPaddle.width / 2);
var angle = offset * 0.5;
var speed = Math.sqrt(ball.vx * ball.vx + ball.vy * ball.vy);
var newAngle = Math.atan2(-ball.vy, ball.vx) + angle;
ball.vx = Math.cos(newAngle);
ball.vy = Math.abs(Math.sin(newAngle));
// Increase speed
ball.speed += 1.2;
// Flash paddle
LK.effects.flashObject(aiPaddle, 0xff99bb, 120);
}
// Ball out of bounds (top/bottom)
if (typeof ball !== "undefined" && ball && typeof ball.y !== "undefined" && typeof ball.height !== "undefined" && !ball.lastScored && ball.y < -ball.height / 2) {
// Player scores
playerScore += 1;
updateScore();
ball.lastScored = true;
LK.effects.flashScreen(0x3399ff, 400);
if (playerScore >= 7) {
LK.showYouWin();
return;
}
ball.reset(-1);
}
if (typeof ball !== "undefined" && ball && typeof ball.y !== "undefined" && typeof ball.height !== "undefined" && !ball.lastScored && ball.y > 2732 + ball.height / 2) {
// AI scores
aiScore += 1;
updateScore();
ball.lastScored = true;
LK.effects.flashScreen(0xff3366, 400);
if (aiScore >= 7) {
LK.showGameOver();
return;
}
ball.reset(1);
}
// --- AI paddle movement: difficulty-based ---
if (typeof aiMoveMode === "undefined") {
var aiMoveMode = "random"; // "random" or "track"
var aiMoveTimer = 0;
var aiMoveTargetX = typeof aiPaddle !== "undefined" && aiPaddle && typeof aiPaddle.x !== "undefined" ? aiPaddle.x : 2048 / 2;
var aiMoveDir = 0;
}
// Difficulty parameters
var aiTrackChance, aiRandomOffset, aiSpeedBase, aiSpeedMax, aiStandStillChance, aiReactDelay;
if (typeof aiDifficulty === "undefined") aiDifficulty = "normal";
if (aiDifficulty === "easy") {
aiTrackChance = 0.10;
aiRandomOffset = 700;
aiSpeedBase = 9;
aiSpeedMax = 18;
aiStandStillChance = 0.40;
aiReactDelay = 110;
} else if (aiDifficulty === "hard") {
aiTrackChance = 0.6;
aiRandomOffset = 180;
aiSpeedBase = 24;
aiSpeedMax = 44;
aiStandStillChance = 0.10;
aiReactDelay = 40;
} else {
// normal
aiTrackChance = 0.20;
aiRandomOffset = 500;
aiSpeedBase = 15;
aiSpeedMax = 28;
aiStandStillChance = 0.25;
aiReactDelay = 80;
}
aiMoveTimer--;
if (aiMoveTimer <= 0) {
// Switch mode every 0.5-2 seconds depending on difficulty
if (Math.random() < aiTrackChance) {
aiMoveMode = "track";
if (typeof ball !== "undefined" && ball && typeof ball.x !== "undefined") {
aiMoveTargetX = ball.x + (Math.random() - 0.5) * aiRandomOffset;
} else {
aiMoveTargetX = 2048 / 2;
}
aiMoveTimer = aiReactDelay + Math.floor(Math.random() * aiReactDelay);
} else {
aiMoveMode = "random";
if (typeof aiPaddle !== "undefined" && aiPaddle && typeof aiPaddle.width !== "undefined") {
aiMoveTargetX = Math.random() * (2048 - aiPaddle.width) + aiPaddle.width / 2;
} else {
aiMoveTargetX = 2048 / 2;
}
aiMoveTimer = aiReactDelay + Math.floor(Math.random() * (aiReactDelay + 30));
}
// Randomly sometimes just stand still
if (Math.random() < aiStandStillChance) {
if (typeof aiPaddle !== "undefined" && aiPaddle && typeof aiPaddle.x !== "undefined") {
aiMoveTargetX = aiPaddle.x;
} else {
aiMoveTargetX = 2048 / 2;
}
}
}
var aiSpeed = aiSpeedBase;
if (typeof ball !== "undefined" && ball && typeof ball.speed !== "undefined") {
aiSpeed += Math.min(ball.speed * 1.2, aiSpeedMax);
}
if (aiMoveMode === "track") {
if (typeof aiPaddle !== "undefined" && aiPaddle && typeof aiPaddle.x !== "undefined" && typeof aiMoveTargetX !== "undefined" && Math.abs(aiPaddle.x - aiMoveTargetX) > 8) {
if (aiPaddle.x < aiMoveTargetX) {
aiPaddle.x += aiSpeed;
if (aiPaddle.x > aiMoveTargetX) aiPaddle.x = aiMoveTargetX;
} else {
aiPaddle.x -= aiSpeed;
if (aiPaddle.x < aiMoveTargetX) aiPaddle.x = aiMoveTargetX;
}
aiPaddle.clamp();
}
} else {
// random mode: move toward random target
if (typeof aiPaddle !== "undefined" && aiPaddle && typeof aiPaddle.x !== "undefined" && typeof aiMoveTargetX !== "undefined" && Math.abs(aiPaddle.x - aiMoveTargetX) > 8) {
if (aiPaddle.x < aiMoveTargetX) {
aiPaddle.x += aiSpeed * 0.7;
if (aiPaddle.x > aiMoveTargetX) aiPaddle.x = aiMoveTargetX;
} else {
aiPaddle.x -= aiSpeed * 0.7;
if (aiPaddle.x < aiMoveTargetX) aiPaddle.x = aiMoveTargetX;
}
aiPaddle.clamp();
}
}
// (Gun and bullet logic removed)
};
// Initial score
updateScore();