/****
* 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
});
self.velocityX = 0;
self.velocityY = 0;
self.hitCount = 0;
self.flames = [];
self.reset = function () {
self.x = 1024;
self.y = 1366;
self.velocityX = (Math.random() > 0.5 ? 1 : -1) * 6;
self.velocityY = (Math.random() - 0.5) * 4;
self.hitCount = 0;
self.clearFlames();
};
self.hit = function () {
self.hitCount++;
totalHits++;
// Increase speed
var speedMultiplier = 1 + self.hitCount * 0.05;
self.velocityX *= speedMultiplier;
self.velocityY *= speedMultiplier;
// Add flame trail after 5th hit
if (self.hitCount >= 5) {
self.addFlame();
}
LK.getSound('paddleHit').play();
};
self.addFlame = function () {
var flame = game.addChild(LK.getAsset('flame', {
anchorX: 0.5,
anchorY: 0.5,
x: self.x,
y: self.y,
alpha: 0.8
}));
self.flames.push(flame);
// Limit flame trail length (more flames as hit count increases)
var maxFlames = Math.min(self.hitCount - 4, 6);
if (self.flames.length > maxFlames) {
var oldFlame = self.flames.shift();
oldFlame.destroy();
}
// Fade out flame
tween(flame, {
alpha: 0
}, {
duration: 800,
onFinish: function onFinish() {
var index = self.flames.indexOf(flame);
if (index !== -1) {
self.flames.splice(index, 1);
flame.destroy();
}
}
});
};
self.clearFlames = function () {
for (var i = 0; i < self.flames.length; i++) {
self.flames[i].destroy();
}
self.flames = [];
};
self.update = function () {
self.x += self.velocityX;
self.y += self.velocityY;
// Bounce off top and bottom walls
if (self.y <= 10 || self.y >= 2722) {
self.velocityY *= -1;
LK.getSound('wallHit').play();
}
// Check scoring
if (self.x < 0) {
aiScore++;
self.scorePoint();
} else if (self.x > 2048) {
playerScore++;
self.scorePoint();
}
};
self.scorePoint = function () {
LK.getSound('score').play();
updateScore();
if (playerScore >= 5 || aiScore >= 5) {
endGame();
} else {
self.reset();
}
};
return self;
});
var DifficultyButton = Container.expand(function (text, difficulty) {
var self = Container.call(this);
var buttonGraphics = self.attachAsset('difficultyButton', {
anchorX: 0.5,
anchorY: 0.5
});
self.difficulty = difficulty;
var buttonText = new Text2(text, {
size: 40,
fill: 0xFFFFFF
});
buttonText.anchor.set(0.5, 0.5);
self.addChild(buttonText);
self.down = function (x, y, obj) {
selectedDifficulty = self.difficulty;
hideDifficultySelection();
startNewGame();
};
return self;
});
var Paddle = Container.expand(function () {
var self = Container.call(this);
var paddleGraphics = self.attachAsset('paddle', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 8;
self.isAI = false;
self.difficulty = 'medium';
self.moveToY = function (targetY) {
var diff = targetY - self.y;
if (Math.abs(diff) > 2) {
var moveSpeed = self.speed;
if (self.isAI) {
switch (self.difficulty) {
case 'easy':
moveSpeed = 4;
break;
case 'medium':
moveSpeed = 6;
break;
case 'hard':
moveSpeed = 8;
break;
case 'impossible':
moveSpeed = 20;
break;
}
}
self.y += Math.sign(diff) * Math.min(Math.abs(diff), moveSpeed);
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x001122
});
/****
* Game Code
****/
/****
* Game Variables
****/
var playerPaddle;
var aiPaddle;
var ball;
var playerScore = 0;
var aiScore = 0;
var totalHits = storage.totalHits || 0;
var gamesWon = storage.gamesWon || 0;
var matchNumber = storage.matchNumber || 1;
var selectedDifficulty = null;
var gameStarted = false;
var difficultyButtons = [];
var showingDifficulty = false;
// UI Elements
var playerScoreText;
var aiScoreText;
var gamesWonText;
var totalHitsText;
var matchNumberText;
var instructionText;
var scoreboardText;
function initializeUI() {
// Player score (left)
playerScoreText = new Text2('0', {
size: 80,
fill: 0xFFFFFF
});
playerScoreText.anchor.set(0.5, 0);
playerScoreText.x = 512;
playerScoreText.y = 100;
LK.gui.addChild(playerScoreText);
// AI score (right)
aiScoreText = new Text2('0', {
size: 80,
fill: 0xFFFFFF
});
aiScoreText.anchor.set(0.5, 0);
aiScoreText.x = 1536;
aiScoreText.y = 100;
LK.gui.addChild(aiScoreText);
// Games won (top-left)
gamesWonText = new Text2('Wins: ' + gamesWon, {
size: 30,
fill: 0xFFFFFF
});
gamesWonText.anchor.set(0, 0);
gamesWonText.x = 120;
gamesWonText.y = 20;
LK.gui.addChild(gamesWonText);
// Total hits (top-right)
totalHitsText = new Text2('Hits: ' + totalHits, {
size: 30,
fill: 0xFFFFFF
});
totalHitsText.anchor.set(1, 0);
totalHitsText.x = 1928;
totalHitsText.y = 20;
LK.gui.addChild(totalHitsText);
// Scoreboard (center top)
scoreboardText = new Text2('0 - 0', {
size: 60,
fill: 0xFFFFFF
});
scoreboardText.anchor.set(0.5, 0);
scoreboardText.x = 1024;
scoreboardText.y = 150;
LK.gui.addChild(scoreboardText);
// Match number (under scoreboard)
matchNumberText = new Text2('Match ' + matchNumber, {
size: 40,
fill: 0xCCCCCC
});
matchNumberText.anchor.set(0.5, 0);
matchNumberText.x = 1024;
matchNumberText.y = 220;
LK.gui.addChild(matchNumberText);
// Instruction text
instructionText = new Text2('Touch to start', {
size: 50,
fill: 0xFFFFFF
});
instructionText.anchor.set(0.5, 0.5);
instructionText.x = 1024;
instructionText.y = 1366;
LK.gui.addChild(instructionText);
}
function createGameObjects() {
// Player paddle (left)
playerPaddle = game.addChild(new Paddle());
playerPaddle.x = 50;
playerPaddle.y = 1366;
// AI paddle (right)
aiPaddle = game.addChild(new Paddle());
aiPaddle.x = 1998;
aiPaddle.y = 1366;
aiPaddle.isAI = true;
aiPaddle.difficulty = selectedDifficulty || 'medium';
// Ball
ball = game.addChild(new Ball());
ball.reset();
}
function showDifficultySelection() {
showingDifficulty = true;
var difficulties = [{
text: 'Easy',
difficulty: 'easy'
}, {
text: 'Medium',
difficulty: 'medium'
}, {
text: 'Hard',
difficulty: 'hard'
}, {
text: 'Impossible',
difficulty: 'impossible'
}];
for (var i = 0; i < difficulties.length; i++) {
var button = game.addChild(new DifficultyButton(difficulties[i].text, difficulties[i].difficulty));
button.x = 1024;
button.y = 800 + i * 120;
difficultyButtons.push(button);
}
instructionText.setText('Select Bot Difficulty');
}
function hideDifficultySelection() {
showingDifficulty = false;
for (var i = 0; i < difficultyButtons.length; i++) {
difficultyButtons[i].destroy();
}
difficultyButtons = [];
}
function startNewGame() {
playerScore = 0;
aiScore = 0;
gameStarted = true;
if (!playerPaddle) {
createGameObjects();
}
aiPaddle.difficulty = selectedDifficulty;
ball.reset();
updateScore();
instructionText.setText('');
}
function updateScore() {
playerScoreText.setText(playerScore.toString());
aiScoreText.setText(aiScore.toString());
scoreboardText.setText(playerScore + ' - ' + aiScore);
totalHitsText.setText('Hits: ' + totalHits);
storage.totalHits = totalHits;
}
function endGame() {
gameStarted = false;
if (playerScore >= 5) {
gamesWon++;
storage.gamesWon = gamesWon;
gamesWonText.setText('Wins: ' + gamesWon);
instructionText.setText('Congratulations on winning!\nTouch to play again');
} else {
instructionText.setText('Game Over\nTouch to play again');
}
matchNumber++;
storage.matchNumber = matchNumber;
matchNumberText.setText('Match ' + matchNumber);
ball.clearFlames();
// Reset difficulty selection for next game
selectedDifficulty = null;
}
function checkCollisions() {
// Ball vs player paddle
if (ball.velocityX < 0 && ball.intersects(playerPaddle)) {
ball.velocityX = Math.abs(ball.velocityX);
ball.velocityY += (ball.y - playerPaddle.y) * 0.1;
ball.hit();
}
// Ball vs AI paddle
if (ball.velocityX > 0 && ball.intersects(aiPaddle)) {
ball.velocityX = -Math.abs(ball.velocityX);
ball.velocityY += (ball.y - aiPaddle.y) * 0.1;
ball.hit();
}
}
// Initialize UI
initializeUI();
// Always show difficulty selection at start
showDifficultySelection();
game.move = function (x, y, obj) {
if (gameStarted) {
playerPaddle.y = y;
}
};
game.down = function (x, y, obj) {
if (showingDifficulty) {
return; // Let difficulty buttons handle this
}
if (!gameStarted) {
if (!selectedDifficulty) {
showDifficultySelection();
} else {
startNewGame();
}
} else {
playerPaddle.y = y;
}
};
game.update = function () {
if (gameStarted) {
// AI paddle movement with mistake probability
var targetY = ball.y;
var shouldMove = true;
if (selectedDifficulty === 'easy') {
// Add some delay and imperfection
targetY += (Math.random() - 0.5) * 100;
// 30% chance to make a mistake (not move at all)
shouldMove = Math.random() > 0.3;
} else if (selectedDifficulty === 'medium') {
targetY += (Math.random() - 0.5) * 50;
// 15% chance to make a mistake
shouldMove = Math.random() > 0.15;
} else if (selectedDifficulty === 'hard') {
targetY += (Math.random() - 0.5) * 20;
// 5% chance to make a mistake
shouldMove = Math.random() > 0.05;
}
// 'impossible' has perfect tracking with no mistakes
if (shouldMove) {
aiPaddle.moveToY(targetY);
}
checkCollisions();
}
}; /****
* 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
});
self.velocityX = 0;
self.velocityY = 0;
self.hitCount = 0;
self.flames = [];
self.reset = function () {
self.x = 1024;
self.y = 1366;
self.velocityX = (Math.random() > 0.5 ? 1 : -1) * 6;
self.velocityY = (Math.random() - 0.5) * 4;
self.hitCount = 0;
self.clearFlames();
};
self.hit = function () {
self.hitCount++;
totalHits++;
// Increase speed
var speedMultiplier = 1 + self.hitCount * 0.05;
self.velocityX *= speedMultiplier;
self.velocityY *= speedMultiplier;
// Add flame trail after 5th hit
if (self.hitCount >= 5) {
self.addFlame();
}
LK.getSound('paddleHit').play();
};
self.addFlame = function () {
var flame = game.addChild(LK.getAsset('flame', {
anchorX: 0.5,
anchorY: 0.5,
x: self.x,
y: self.y,
alpha: 0.8
}));
self.flames.push(flame);
// Limit flame trail length (more flames as hit count increases)
var maxFlames = Math.min(self.hitCount - 4, 6);
if (self.flames.length > maxFlames) {
var oldFlame = self.flames.shift();
oldFlame.destroy();
}
// Fade out flame
tween(flame, {
alpha: 0
}, {
duration: 800,
onFinish: function onFinish() {
var index = self.flames.indexOf(flame);
if (index !== -1) {
self.flames.splice(index, 1);
flame.destroy();
}
}
});
};
self.clearFlames = function () {
for (var i = 0; i < self.flames.length; i++) {
self.flames[i].destroy();
}
self.flames = [];
};
self.update = function () {
self.x += self.velocityX;
self.y += self.velocityY;
// Bounce off top and bottom walls
if (self.y <= 10 || self.y >= 2722) {
self.velocityY *= -1;
LK.getSound('wallHit').play();
}
// Check scoring
if (self.x < 0) {
aiScore++;
self.scorePoint();
} else if (self.x > 2048) {
playerScore++;
self.scorePoint();
}
};
self.scorePoint = function () {
LK.getSound('score').play();
updateScore();
if (playerScore >= 5 || aiScore >= 5) {
endGame();
} else {
self.reset();
}
};
return self;
});
var DifficultyButton = Container.expand(function (text, difficulty) {
var self = Container.call(this);
var buttonGraphics = self.attachAsset('difficultyButton', {
anchorX: 0.5,
anchorY: 0.5
});
self.difficulty = difficulty;
var buttonText = new Text2(text, {
size: 40,
fill: 0xFFFFFF
});
buttonText.anchor.set(0.5, 0.5);
self.addChild(buttonText);
self.down = function (x, y, obj) {
selectedDifficulty = self.difficulty;
hideDifficultySelection();
startNewGame();
};
return self;
});
var Paddle = Container.expand(function () {
var self = Container.call(this);
var paddleGraphics = self.attachAsset('paddle', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 8;
self.isAI = false;
self.difficulty = 'medium';
self.moveToY = function (targetY) {
var diff = targetY - self.y;
if (Math.abs(diff) > 2) {
var moveSpeed = self.speed;
if (self.isAI) {
switch (self.difficulty) {
case 'easy':
moveSpeed = 4;
break;
case 'medium':
moveSpeed = 6;
break;
case 'hard':
moveSpeed = 8;
break;
case 'impossible':
moveSpeed = 20;
break;
}
}
self.y += Math.sign(diff) * Math.min(Math.abs(diff), moveSpeed);
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x001122
});
/****
* Game Code
****/
/****
* Game Variables
****/
var playerPaddle;
var aiPaddle;
var ball;
var playerScore = 0;
var aiScore = 0;
var totalHits = storage.totalHits || 0;
var gamesWon = storage.gamesWon || 0;
var matchNumber = storage.matchNumber || 1;
var selectedDifficulty = null;
var gameStarted = false;
var difficultyButtons = [];
var showingDifficulty = false;
// UI Elements
var playerScoreText;
var aiScoreText;
var gamesWonText;
var totalHitsText;
var matchNumberText;
var instructionText;
var scoreboardText;
function initializeUI() {
// Player score (left)
playerScoreText = new Text2('0', {
size: 80,
fill: 0xFFFFFF
});
playerScoreText.anchor.set(0.5, 0);
playerScoreText.x = 512;
playerScoreText.y = 100;
LK.gui.addChild(playerScoreText);
// AI score (right)
aiScoreText = new Text2('0', {
size: 80,
fill: 0xFFFFFF
});
aiScoreText.anchor.set(0.5, 0);
aiScoreText.x = 1536;
aiScoreText.y = 100;
LK.gui.addChild(aiScoreText);
// Games won (top-left)
gamesWonText = new Text2('Wins: ' + gamesWon, {
size: 30,
fill: 0xFFFFFF
});
gamesWonText.anchor.set(0, 0);
gamesWonText.x = 120;
gamesWonText.y = 20;
LK.gui.addChild(gamesWonText);
// Total hits (top-right)
totalHitsText = new Text2('Hits: ' + totalHits, {
size: 30,
fill: 0xFFFFFF
});
totalHitsText.anchor.set(1, 0);
totalHitsText.x = 1928;
totalHitsText.y = 20;
LK.gui.addChild(totalHitsText);
// Scoreboard (center top)
scoreboardText = new Text2('0 - 0', {
size: 60,
fill: 0xFFFFFF
});
scoreboardText.anchor.set(0.5, 0);
scoreboardText.x = 1024;
scoreboardText.y = 150;
LK.gui.addChild(scoreboardText);
// Match number (under scoreboard)
matchNumberText = new Text2('Match ' + matchNumber, {
size: 40,
fill: 0xCCCCCC
});
matchNumberText.anchor.set(0.5, 0);
matchNumberText.x = 1024;
matchNumberText.y = 220;
LK.gui.addChild(matchNumberText);
// Instruction text
instructionText = new Text2('Touch to start', {
size: 50,
fill: 0xFFFFFF
});
instructionText.anchor.set(0.5, 0.5);
instructionText.x = 1024;
instructionText.y = 1366;
LK.gui.addChild(instructionText);
}
function createGameObjects() {
// Player paddle (left)
playerPaddle = game.addChild(new Paddle());
playerPaddle.x = 50;
playerPaddle.y = 1366;
// AI paddle (right)
aiPaddle = game.addChild(new Paddle());
aiPaddle.x = 1998;
aiPaddle.y = 1366;
aiPaddle.isAI = true;
aiPaddle.difficulty = selectedDifficulty || 'medium';
// Ball
ball = game.addChild(new Ball());
ball.reset();
}
function showDifficultySelection() {
showingDifficulty = true;
var difficulties = [{
text: 'Easy',
difficulty: 'easy'
}, {
text: 'Medium',
difficulty: 'medium'
}, {
text: 'Hard',
difficulty: 'hard'
}, {
text: 'Impossible',
difficulty: 'impossible'
}];
for (var i = 0; i < difficulties.length; i++) {
var button = game.addChild(new DifficultyButton(difficulties[i].text, difficulties[i].difficulty));
button.x = 1024;
button.y = 800 + i * 120;
difficultyButtons.push(button);
}
instructionText.setText('Select Bot Difficulty');
}
function hideDifficultySelection() {
showingDifficulty = false;
for (var i = 0; i < difficultyButtons.length; i++) {
difficultyButtons[i].destroy();
}
difficultyButtons = [];
}
function startNewGame() {
playerScore = 0;
aiScore = 0;
gameStarted = true;
if (!playerPaddle) {
createGameObjects();
}
aiPaddle.difficulty = selectedDifficulty;
ball.reset();
updateScore();
instructionText.setText('');
}
function updateScore() {
playerScoreText.setText(playerScore.toString());
aiScoreText.setText(aiScore.toString());
scoreboardText.setText(playerScore + ' - ' + aiScore);
totalHitsText.setText('Hits: ' + totalHits);
storage.totalHits = totalHits;
}
function endGame() {
gameStarted = false;
if (playerScore >= 5) {
gamesWon++;
storage.gamesWon = gamesWon;
gamesWonText.setText('Wins: ' + gamesWon);
instructionText.setText('Congratulations on winning!\nTouch to play again');
} else {
instructionText.setText('Game Over\nTouch to play again');
}
matchNumber++;
storage.matchNumber = matchNumber;
matchNumberText.setText('Match ' + matchNumber);
ball.clearFlames();
// Reset difficulty selection for next game
selectedDifficulty = null;
}
function checkCollisions() {
// Ball vs player paddle
if (ball.velocityX < 0 && ball.intersects(playerPaddle)) {
ball.velocityX = Math.abs(ball.velocityX);
ball.velocityY += (ball.y - playerPaddle.y) * 0.1;
ball.hit();
}
// Ball vs AI paddle
if (ball.velocityX > 0 && ball.intersects(aiPaddle)) {
ball.velocityX = -Math.abs(ball.velocityX);
ball.velocityY += (ball.y - aiPaddle.y) * 0.1;
ball.hit();
}
}
// Initialize UI
initializeUI();
// Always show difficulty selection at start
showDifficultySelection();
game.move = function (x, y, obj) {
if (gameStarted) {
playerPaddle.y = y;
}
};
game.down = function (x, y, obj) {
if (showingDifficulty) {
return; // Let difficulty buttons handle this
}
if (!gameStarted) {
if (!selectedDifficulty) {
showDifficultySelection();
} else {
startNewGame();
}
} else {
playerPaddle.y = y;
}
};
game.update = function () {
if (gameStarted) {
// AI paddle movement with mistake probability
var targetY = ball.y;
var shouldMove = true;
if (selectedDifficulty === 'easy') {
// Add some delay and imperfection
targetY += (Math.random() - 0.5) * 100;
// 30% chance to make a mistake (not move at all)
shouldMove = Math.random() > 0.3;
} else if (selectedDifficulty === 'medium') {
targetY += (Math.random() - 0.5) * 50;
// 15% chance to make a mistake
shouldMove = Math.random() > 0.15;
} else if (selectedDifficulty === 'hard') {
targetY += (Math.random() - 0.5) * 20;
// 5% chance to make a mistake
shouldMove = Math.random() > 0.05;
}
// 'impossible' has perfect tracking with no mistakes
if (shouldMove) {
aiPaddle.moveToY(targetY);
}
checkCollisions();
}
};