/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Basketball = Container.expand(function () {
var self = Container.call(this);
var ball = self.attachAsset('basketball', {
anchorX: 0.5,
anchorY: 0.5
});
self.velocityX = 0;
self.velocityY = 0;
self.isDragging = false;
self.isFlying = false;
self.gravity = 0.8;
self.bounce = 0.6;
self.startX = 200;
self.startY = 2000;
self.reset = function () {
self.x = self.startX;
self.y = self.startY;
self.velocityX = 0;
self.velocityY = 0;
self.isFlying = false;
self.isDragging = false;
};
self.shoot = function (targetX, targetY) {
var deltaX = targetX - self.x;
var deltaY = targetY - self.y;
var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
var power = Math.min(distance / 8, 25);
self.velocityX = deltaX / distance * power;
self.velocityY = deltaY / distance * power;
self.isFlying = true;
self.isDragging = false;
};
self.update = function () {
if (self.isFlying) {
self.x += self.velocityX;
self.y += self.velocityY;
self.velocityY += self.gravity;
// Check bounds
if (self.x < 0 || self.x > 2048 || self.y > 2732 + 100) {
self.reset();
}
// Simple ground bounce
if (self.y > 2600) {
self.y = 2600;
self.velocityY = -self.velocityY * self.bounce;
if (Math.abs(self.velocityY) < 2) {
self.reset();
}
}
}
};
return self;
});
var BasketballHoop = Container.expand(function () {
var self = Container.call(this);
var backboard = self.attachAsset('backboard', {
anchorX: 0.5,
anchorY: 0.5,
x: 180,
y: -50
});
var hoop = self.attachAsset('hoop', {
anchorX: 0.5,
anchorY: 0.5,
y: 50
});
var net = self.attachAsset('net', {
anchorX: 0.5,
anchorY: 0,
x: 0,
y: 65,
alpha: 0.3,
width: 240
});
self.hoopY = 50;
self.hoopLeft = -180;
self.hoopRight = 180;
self.netBottom = 160;
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x87CEEB
});
/****
* Game Code
****/
var basketball = null;
var hoop = null;
var scoreTxt = null;
var timerTxt = null;
var trajectoryDots = [];
var lastTouchedRim = false;
// Trail system variables
var trailDots = [];
var maxTrailDots = 50;
var trailGenerations = []; // Track which generation each trail dot belongs to
var currentGeneration = 0;
// Game state variables
var gameStarted = false;
var gameTime = 60; // 60 seconds
var gameTimer = null;
var bestScore = storage.bestScore || 0;
// Power bar variables
var powerBar = null;
var powerBarBackground = null;
var powerBallIndicator = null;
var powerValue = 0;
var powerDirection = 1; // 1 for increasing, -1 for decreasing
var showingPowerBar = false;
// Direction bar variables
var directionBar = null;
var directionBarBackground = null;
var directionValue = 0;
var directionDirection = 1;
var showingDirectionBar = false;
// Create score display
scoreTxt = new Text2('Score: 0', {
size: 60,
fill: 0x000000
});
scoreTxt.anchor.set(0.3, 0);
scoreTxt.y = 20;
LK.gui.top.addChild(scoreTxt);
// Create timer display
timerTxt = new Text2('Touch to Start', {
size: 60,
fill: 0x000000
});
timerTxt.anchor.set(1, 0);
timerTxt.x = -20;
timerTxt.y = 20;
LK.gui.topRight.addChild(timerTxt);
// Create best score display
var bestScoreTxt = new Text2('Best: ' + bestScore, {
size: 60,
fill: 0x000000
});
bestScoreTxt.anchor.set(1, 1);
bestScoreTxt.x = -20;
bestScoreTxt.y = -20;
LK.gui.bottomRight.addChild(bestScoreTxt);
// Create restart button
var restartButton = LK.getAsset('restartButton', {
anchorX: 0.5,
anchorY: 0.5
});
restartButton.x = scoreTxt.x + 100;
restartButton.y = scoreTxt.y + 100;
LK.gui.top.addChild(restartButton);
// Create restart button text
var restartButtonText = new Text2('RESTART', {
size: 24,
fill: 0xffffff
});
restartButtonText.anchor.set(0.5, 0.5);
restartButtonText.x = restartButton.x;
restartButtonText.y = restartButton.y;
LK.gui.top.addChild(restartButtonText);
// Restart button functionality
restartButton.down = function (x, y, obj) {
// Only reset ball position, keep time and score
basketball.reset();
// Hide any active bars
showingPowerBar = false;
showingDirectionBar = false;
powerBarBackground.alpha = 0;
powerBar.alpha = 0;
powerBallIndicator.alpha = 0;
directionBarBackground.alpha = 0;
directionBar.alpha = 0;
// Reset trail system
currentGeneration++;
for (var i = 0; i < trailDots.length; i++) {
trailDots[i].alpha = 0;
trailGenerations[i] = -1;
}
// Hide trajectory
hideTrajectory();
// Reset rim touch state
lastTouchedRim = false;
};
// Create power bar (initially hidden)
powerBarBackground = LK.getAsset('powerBarBG', {
anchorX: 0.5,
anchorY: 0.5
});
powerBarBackground.x = 300;
powerBarBackground.y = 1800;
powerBarBackground.rotation = Math.PI / 2; // Rotate 90 degrees to make it vertical
powerBarBackground.alpha = 0;
game.addChild(powerBarBackground);
powerBar = LK.getAsset('powerBarFill', {
anchorX: 0,
anchorY: 0.5
});
powerBar.x = powerBarBackground.x;
powerBar.y = powerBarBackground.y + 200;
powerBar.rotation = Math.PI / 2; // Rotate 90 degrees to make it vertical
powerBar.width = 0;
powerBar.alpha = 0;
game.addChild(powerBar);
// Create power bar ball indicator
powerBallIndicator = LK.getAsset('powerBallIndicator', {
anchorX: 0.5,
anchorY: 0.5
});
powerBallIndicator.x = powerBarBackground.x;
powerBallIndicator.y = powerBarBackground.y + 200;
powerBallIndicator.alpha = 0;
game.addChild(powerBallIndicator);
// Create direction bar (initially hidden)
directionBarBackground = LK.getAsset('directionBarBG', {
anchorX: 0.5,
anchorY: 0.5
});
directionBarBackground.x = 1024;
directionBarBackground.y = 2100;
directionBarBackground.alpha = 0;
game.addChild(directionBarBackground);
directionBar = LK.getAsset('directionBarFill', {
anchorX: 0,
anchorY: 0.5
});
directionBar.x = directionBarBackground.x - 200;
directionBar.y = directionBarBackground.y;
directionBar.width = 0;
directionBar.alpha = 0;
game.addChild(directionBar);
// Create basketball
basketball = game.addChild(new Basketball());
basketball.reset();
// Create hoop
hoop = game.addChild(new BasketballHoop());
hoop.x = 1700;
hoop.y = 1400;
// Create trajectory dots
for (var i = 0; i < 15; i++) {
var dot = LK.getAsset('trajectory', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0
});
trajectoryDots.push(dot);
game.addChild(dot);
}
// Initialize trail dots pool
for (var i = 0; i < maxTrailDots; i++) {
var trailDot = LK.getAsset('trailDot', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0
});
trailDots.push(trailDot);
trailGenerations.push(-1); // Initialize with invalid generation
game.addChild(trailDot);
}
function updateTrajectory(startX, startY, endX, endY) {
var deltaX = endX - startX;
var deltaY = endY - startY;
var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
var power = Math.min(distance / 8, 25);
var velX = deltaX / distance * power;
var velY = deltaY / distance * power;
for (var i = 0; i < trajectoryDots.length; i++) {
var t = (i + 1) * 3;
var x = startX + velX * t;
var y = startY + velY * t + 0.8 * t * t / 2;
trajectoryDots[i].x = x;
trajectoryDots[i].y = y;
trajectoryDots[i].alpha = Math.max(0, 0.8 - i * 0.05);
if (y > 2732) {
trajectoryDots[i].alpha = 0;
}
}
}
function hideTrajectory() {
for (var i = 0; i < trajectoryDots.length; i++) {
trajectoryDots[i].alpha = 0;
}
}
function updateTrail() {
if (basketball.isFlying) {
// Find next available trail dot
for (var i = 0; i < trailDots.length; i++) {
if (trailDots[i].alpha <= 0.05) {
trailDots[i].x = basketball.x;
trailDots[i].y = basketball.y;
trailGenerations[i] = currentGeneration;
// Set opacity based on generation: current=1.0, previous=0.5, older=0.0
if (trailGenerations[i] === currentGeneration) {
trailDots[i].alpha = 1.0;
}
break;
}
}
}
// Update all trail dots based on their generation
for (var i = 0; i < trailDots.length; i++) {
if (trailGenerations[i] >= 0) {
var generationDiff = currentGeneration - trailGenerations[i];
if (generationDiff === 0) {
// Current generation: full opacity
trailDots[i].alpha = Math.max(0, trailDots[i].alpha - 0.02);
} else if (generationDiff === 1) {
// Previous generation: semi-transparent
trailDots[i].alpha = Math.max(0, Math.min(0.5, trailDots[i].alpha - 0.01));
} else {
// Older generations: disappear
trailDots[i].alpha = 0;
trailGenerations[i] = -1;
}
}
}
}
function startNewTrail() {
// Increment generation counter for new trail
currentGeneration++;
}
function checkScore() {
if (!basketball.isFlying) return;
var ballX = basketball.x;
var ballY = basketball.y;
var hoopX = hoop.x;
var hoopY = hoop.y + hoop.hoopY;
// Ball speed reduction mechanism near hoop entrance
var distanceToHoop = Math.sqrt((ballX - hoopX) * (ballX - hoopX) + (ballY - hoopY) * (ballY - hoopY));
if (distanceToHoop < 120 && basketball.velocityY > 0) {
var speed = Math.sqrt(basketball.velocityX * basketball.velocityX + basketball.velocityY * basketball.velocityY);
// If ball is moving too fast near the hoop, slow it down progressively
if (speed > 8) {
var reductionFactor = 0.92; // Reduce speed by 8% each frame when near hoop
basketball.velocityX *= reductionFactor;
basketball.velocityY *= reductionFactor;
}
// Additional gentle guidance towards hoop center if ball is close
if (distanceToHoop < 80) {
var pullStrength = 0.15;
var directionX = (hoopX - ballX) / distanceToHoop;
var directionY = (hoopY - ballY) / distanceToHoop;
basketball.velocityX += directionX * pullStrength;
basketball.velocityY += directionY * pullStrength;
}
}
// Check if ball is passing through hoop area
if (ballX > hoopX + hoop.hoopLeft && ballX < hoopX + hoop.hoopRight && ballY > hoopY && ballY < hoopY + 20 && basketball.velocityY > 0) {
// Check if it's a swish (didn't touch rim)
var congratsText;
if (!lastTouchedRim) {
LK.setScore(LK.getScore() + 2);
LK.getSound('swish').play();
congratsText = new Text2('SWISH!', {
size: 80,
fill: 0x00ff00
});
} else {
LK.setScore(LK.getScore() + 1);
congratsText = new Text2('SCORE!', {
size: 80,
fill: 0xffffff
});
}
// Display congratulatory text with animation
congratsText.anchor.set(0.5, 0.5);
congratsText.x = hoopX;
congratsText.y = hoopY - 100;
congratsText.alpha = 0;
congratsText.scaleX = 0.5;
congratsText.scaleY = 0.5;
game.addChild(congratsText);
// Animate congratulatory text
tween(congratsText, {
alpha: 1,
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(congratsText, {
alpha: 0,
scaleX: 0.8,
scaleY: 0.8,
y: congratsText.y - 50
}, {
duration: 700,
easing: tween.easeIn,
onFinish: function onFinish() {
game.removeChild(congratsText);
}
});
}
});
scoreTxt.setText('Score: ' + LK.getScore());
// Update best score if current score is higher
if (LK.getScore() > bestScore) {
bestScore = LK.getScore();
storage.bestScore = bestScore;
bestScoreTxt.setText('Best: ' + bestScore);
}
basketball.reset();
lastTouchedRim = false;
}
// Check rim collision
if (Math.abs(ballX - hoopX) < 70 && Math.abs(ballY - hoopY) < 20) {
if (ballX < hoopX + hoop.hoopLeft + 10 || ballX > hoopX + hoop.hoopRight - 10) {
lastTouchedRim = true;
LK.getSound('bounce').play();
}
}
// Check backboard collision - backboard is 20px wide, positioned at x: 180 relative to hoop center
var backboardLeft = hoopX + 170; // Left edge of backboard (180 - 10 for half width)
var backboardRight = hoopX + 190; // Right edge of backboard (180 + 10 for half width)
var backboardTop = hoopY - 150; // Top of backboard (100px height, positioned at y: -50)
var backboardBottom = hoopY + 50; // Bottom of backboard
if (ballX > backboardLeft && ballX < backboardRight && ballY > backboardTop && ballY < backboardBottom && basketball.velocityX > 0) {
// Bounce off backboard with reduced horizontal velocity
basketball.velocityX = -basketball.velocityX * 0.7;
// Add slight upward velocity if ball is moving down
if (basketball.velocityY > 0) {
basketball.velocityY *= 0.9;
}
// Move ball away from backboard to prevent sticking
basketball.x = backboardLeft - 40; // Move ball away from backboard
lastTouchedRim = true;
LK.getSound('bounce').play();
}
}
game.down = function (x, y, obj) {
// Start game on first touch
if (!gameStarted) {
gameStarted = true;
startGameTimer();
return;
}
// Handle power bar selection
if (showingPowerBar && !basketball.isFlying) {
// Lock in power value and show direction bar
showingPowerBar = false;
powerBarBackground.alpha = 0;
powerBar.alpha = 0;
powerBallIndicator.alpha = 0;
showingDirectionBar = true;
directionBarBackground.alpha = 1;
directionBar.alpha = 1;
return;
}
// Handle direction bar selection
if (showingDirectionBar && !basketball.isFlying) {
// Lock in direction and shoot
showingDirectionBar = false;
directionBarBackground.alpha = 0;
directionBar.alpha = 0;
// Calculate combined power and direction with multiplicative scaling
var basePower = powerValue / 400; // Normalize to 0-1
var directionFactor = directionValue / 400; // Normalize to 0-1
// When both are in red (high values), multiply for dramatic effect
var combinedPowerMultiplier = 1 + basePower * directionFactor * 9; // Can go up to 10x when both are max
var finalVerticalPower = basePower * 25 * combinedPowerMultiplier;
var finalHorizontalPower = directionFactor * 15 * combinedPowerMultiplier;
basketball.velocityX = finalHorizontalPower; // Horizontal movement based on direction
basketball.velocityY = -finalVerticalPower; // Negative for upward movement
basketball.isFlying = true;
basketball.isDragging = false;
lastTouchedRim = false;
startNewTrail();
return;
}
// Start power bar if ball is ready and game is running
if (gameStarted && !basketball.isFlying && !showingPowerBar && !showingDirectionBar) {
showingPowerBar = true;
powerBarBackground.alpha = 1;
powerBar.alpha = 1;
powerBallIndicator.alpha = 1;
powerValue = 0;
powerDirection = 1;
// Reset ball position but keep time and score
basketball.reset();
}
};
game.move = function (x, y, obj) {
// No dragging mechanics needed anymore
};
game.up = function (x, y, obj) {
// No drag release mechanics needed anymore
};
function startGameTimer() {
timerTxt.setText('Time: 60');
gameTimer = LK.setInterval(function () {
gameTime--;
timerTxt.setText('Time: ' + gameTime);
if (gameTime <= 0) {
LK.clearInterval(gameTimer);
LK.showGameOver();
}
}, 1000);
}
function updatePowerBar() {
if (!showingPowerBar) return;
powerValue += powerDirection * 8;
if (powerValue >= 400) {
powerValue = 400;
powerDirection = -1;
} else if (powerValue <= 0) {
powerValue = 0;
powerDirection = 1;
}
powerBar.height = powerValue;
powerBar.y = powerBarBackground.y + 200 - powerValue;
// Color changes from green (low power) to red (high power) - matching direction bar
var greenAmount = Math.floor(255 * (1 - powerValue / 400));
var redAmount = Math.floor(255 * (powerValue / 400));
powerBar.tint = redAmount << 16 | greenAmount << 8 | 0;
// Update ball indicator position to move up and down with power value
powerBallIndicator.y = powerBarBackground.y + 200 - powerValue;
}
function updateDirectionBar() {
if (!showingDirectionBar) return;
directionValue += directionDirection * 6;
if (directionValue >= 400) {
directionValue = 400;
directionDirection = -1;
} else if (directionValue <= 0) {
directionValue = 0;
directionDirection = 1;
}
directionBar.width = directionValue;
// Red on the left side (more left direction)
var redAmount = Math.floor(255 * (directionValue / 400));
var greenAmount = Math.floor(255 * (1 - directionValue / 400));
directionBar.tint = redAmount << 16 | greenAmount << 8 | 0;
}
game.update = function () {
if (gameStarted) {
updatePowerBar();
updateDirectionBar();
}
// Keep ball always opaque
basketball.alpha = 1.0;
updateTrail();
checkScore();
}; /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Basketball = Container.expand(function () {
var self = Container.call(this);
var ball = self.attachAsset('basketball', {
anchorX: 0.5,
anchorY: 0.5
});
self.velocityX = 0;
self.velocityY = 0;
self.isDragging = false;
self.isFlying = false;
self.gravity = 0.8;
self.bounce = 0.6;
self.startX = 200;
self.startY = 2000;
self.reset = function () {
self.x = self.startX;
self.y = self.startY;
self.velocityX = 0;
self.velocityY = 0;
self.isFlying = false;
self.isDragging = false;
};
self.shoot = function (targetX, targetY) {
var deltaX = targetX - self.x;
var deltaY = targetY - self.y;
var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
var power = Math.min(distance / 8, 25);
self.velocityX = deltaX / distance * power;
self.velocityY = deltaY / distance * power;
self.isFlying = true;
self.isDragging = false;
};
self.update = function () {
if (self.isFlying) {
self.x += self.velocityX;
self.y += self.velocityY;
self.velocityY += self.gravity;
// Check bounds
if (self.x < 0 || self.x > 2048 || self.y > 2732 + 100) {
self.reset();
}
// Simple ground bounce
if (self.y > 2600) {
self.y = 2600;
self.velocityY = -self.velocityY * self.bounce;
if (Math.abs(self.velocityY) < 2) {
self.reset();
}
}
}
};
return self;
});
var BasketballHoop = Container.expand(function () {
var self = Container.call(this);
var backboard = self.attachAsset('backboard', {
anchorX: 0.5,
anchorY: 0.5,
x: 180,
y: -50
});
var hoop = self.attachAsset('hoop', {
anchorX: 0.5,
anchorY: 0.5,
y: 50
});
var net = self.attachAsset('net', {
anchorX: 0.5,
anchorY: 0,
x: 0,
y: 65,
alpha: 0.3,
width: 240
});
self.hoopY = 50;
self.hoopLeft = -180;
self.hoopRight = 180;
self.netBottom = 160;
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x87CEEB
});
/****
* Game Code
****/
var basketball = null;
var hoop = null;
var scoreTxt = null;
var timerTxt = null;
var trajectoryDots = [];
var lastTouchedRim = false;
// Trail system variables
var trailDots = [];
var maxTrailDots = 50;
var trailGenerations = []; // Track which generation each trail dot belongs to
var currentGeneration = 0;
// Game state variables
var gameStarted = false;
var gameTime = 60; // 60 seconds
var gameTimer = null;
var bestScore = storage.bestScore || 0;
// Power bar variables
var powerBar = null;
var powerBarBackground = null;
var powerBallIndicator = null;
var powerValue = 0;
var powerDirection = 1; // 1 for increasing, -1 for decreasing
var showingPowerBar = false;
// Direction bar variables
var directionBar = null;
var directionBarBackground = null;
var directionValue = 0;
var directionDirection = 1;
var showingDirectionBar = false;
// Create score display
scoreTxt = new Text2('Score: 0', {
size: 60,
fill: 0x000000
});
scoreTxt.anchor.set(0.3, 0);
scoreTxt.y = 20;
LK.gui.top.addChild(scoreTxt);
// Create timer display
timerTxt = new Text2('Touch to Start', {
size: 60,
fill: 0x000000
});
timerTxt.anchor.set(1, 0);
timerTxt.x = -20;
timerTxt.y = 20;
LK.gui.topRight.addChild(timerTxt);
// Create best score display
var bestScoreTxt = new Text2('Best: ' + bestScore, {
size: 60,
fill: 0x000000
});
bestScoreTxt.anchor.set(1, 1);
bestScoreTxt.x = -20;
bestScoreTxt.y = -20;
LK.gui.bottomRight.addChild(bestScoreTxt);
// Create restart button
var restartButton = LK.getAsset('restartButton', {
anchorX: 0.5,
anchorY: 0.5
});
restartButton.x = scoreTxt.x + 100;
restartButton.y = scoreTxt.y + 100;
LK.gui.top.addChild(restartButton);
// Create restart button text
var restartButtonText = new Text2('RESTART', {
size: 24,
fill: 0xffffff
});
restartButtonText.anchor.set(0.5, 0.5);
restartButtonText.x = restartButton.x;
restartButtonText.y = restartButton.y;
LK.gui.top.addChild(restartButtonText);
// Restart button functionality
restartButton.down = function (x, y, obj) {
// Only reset ball position, keep time and score
basketball.reset();
// Hide any active bars
showingPowerBar = false;
showingDirectionBar = false;
powerBarBackground.alpha = 0;
powerBar.alpha = 0;
powerBallIndicator.alpha = 0;
directionBarBackground.alpha = 0;
directionBar.alpha = 0;
// Reset trail system
currentGeneration++;
for (var i = 0; i < trailDots.length; i++) {
trailDots[i].alpha = 0;
trailGenerations[i] = -1;
}
// Hide trajectory
hideTrajectory();
// Reset rim touch state
lastTouchedRim = false;
};
// Create power bar (initially hidden)
powerBarBackground = LK.getAsset('powerBarBG', {
anchorX: 0.5,
anchorY: 0.5
});
powerBarBackground.x = 300;
powerBarBackground.y = 1800;
powerBarBackground.rotation = Math.PI / 2; // Rotate 90 degrees to make it vertical
powerBarBackground.alpha = 0;
game.addChild(powerBarBackground);
powerBar = LK.getAsset('powerBarFill', {
anchorX: 0,
anchorY: 0.5
});
powerBar.x = powerBarBackground.x;
powerBar.y = powerBarBackground.y + 200;
powerBar.rotation = Math.PI / 2; // Rotate 90 degrees to make it vertical
powerBar.width = 0;
powerBar.alpha = 0;
game.addChild(powerBar);
// Create power bar ball indicator
powerBallIndicator = LK.getAsset('powerBallIndicator', {
anchorX: 0.5,
anchorY: 0.5
});
powerBallIndicator.x = powerBarBackground.x;
powerBallIndicator.y = powerBarBackground.y + 200;
powerBallIndicator.alpha = 0;
game.addChild(powerBallIndicator);
// Create direction bar (initially hidden)
directionBarBackground = LK.getAsset('directionBarBG', {
anchorX: 0.5,
anchorY: 0.5
});
directionBarBackground.x = 1024;
directionBarBackground.y = 2100;
directionBarBackground.alpha = 0;
game.addChild(directionBarBackground);
directionBar = LK.getAsset('directionBarFill', {
anchorX: 0,
anchorY: 0.5
});
directionBar.x = directionBarBackground.x - 200;
directionBar.y = directionBarBackground.y;
directionBar.width = 0;
directionBar.alpha = 0;
game.addChild(directionBar);
// Create basketball
basketball = game.addChild(new Basketball());
basketball.reset();
// Create hoop
hoop = game.addChild(new BasketballHoop());
hoop.x = 1700;
hoop.y = 1400;
// Create trajectory dots
for (var i = 0; i < 15; i++) {
var dot = LK.getAsset('trajectory', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0
});
trajectoryDots.push(dot);
game.addChild(dot);
}
// Initialize trail dots pool
for (var i = 0; i < maxTrailDots; i++) {
var trailDot = LK.getAsset('trailDot', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0
});
trailDots.push(trailDot);
trailGenerations.push(-1); // Initialize with invalid generation
game.addChild(trailDot);
}
function updateTrajectory(startX, startY, endX, endY) {
var deltaX = endX - startX;
var deltaY = endY - startY;
var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
var power = Math.min(distance / 8, 25);
var velX = deltaX / distance * power;
var velY = deltaY / distance * power;
for (var i = 0; i < trajectoryDots.length; i++) {
var t = (i + 1) * 3;
var x = startX + velX * t;
var y = startY + velY * t + 0.8 * t * t / 2;
trajectoryDots[i].x = x;
trajectoryDots[i].y = y;
trajectoryDots[i].alpha = Math.max(0, 0.8 - i * 0.05);
if (y > 2732) {
trajectoryDots[i].alpha = 0;
}
}
}
function hideTrajectory() {
for (var i = 0; i < trajectoryDots.length; i++) {
trajectoryDots[i].alpha = 0;
}
}
function updateTrail() {
if (basketball.isFlying) {
// Find next available trail dot
for (var i = 0; i < trailDots.length; i++) {
if (trailDots[i].alpha <= 0.05) {
trailDots[i].x = basketball.x;
trailDots[i].y = basketball.y;
trailGenerations[i] = currentGeneration;
// Set opacity based on generation: current=1.0, previous=0.5, older=0.0
if (trailGenerations[i] === currentGeneration) {
trailDots[i].alpha = 1.0;
}
break;
}
}
}
// Update all trail dots based on their generation
for (var i = 0; i < trailDots.length; i++) {
if (trailGenerations[i] >= 0) {
var generationDiff = currentGeneration - trailGenerations[i];
if (generationDiff === 0) {
// Current generation: full opacity
trailDots[i].alpha = Math.max(0, trailDots[i].alpha - 0.02);
} else if (generationDiff === 1) {
// Previous generation: semi-transparent
trailDots[i].alpha = Math.max(0, Math.min(0.5, trailDots[i].alpha - 0.01));
} else {
// Older generations: disappear
trailDots[i].alpha = 0;
trailGenerations[i] = -1;
}
}
}
}
function startNewTrail() {
// Increment generation counter for new trail
currentGeneration++;
}
function checkScore() {
if (!basketball.isFlying) return;
var ballX = basketball.x;
var ballY = basketball.y;
var hoopX = hoop.x;
var hoopY = hoop.y + hoop.hoopY;
// Ball speed reduction mechanism near hoop entrance
var distanceToHoop = Math.sqrt((ballX - hoopX) * (ballX - hoopX) + (ballY - hoopY) * (ballY - hoopY));
if (distanceToHoop < 120 && basketball.velocityY > 0) {
var speed = Math.sqrt(basketball.velocityX * basketball.velocityX + basketball.velocityY * basketball.velocityY);
// If ball is moving too fast near the hoop, slow it down progressively
if (speed > 8) {
var reductionFactor = 0.92; // Reduce speed by 8% each frame when near hoop
basketball.velocityX *= reductionFactor;
basketball.velocityY *= reductionFactor;
}
// Additional gentle guidance towards hoop center if ball is close
if (distanceToHoop < 80) {
var pullStrength = 0.15;
var directionX = (hoopX - ballX) / distanceToHoop;
var directionY = (hoopY - ballY) / distanceToHoop;
basketball.velocityX += directionX * pullStrength;
basketball.velocityY += directionY * pullStrength;
}
}
// Check if ball is passing through hoop area
if (ballX > hoopX + hoop.hoopLeft && ballX < hoopX + hoop.hoopRight && ballY > hoopY && ballY < hoopY + 20 && basketball.velocityY > 0) {
// Check if it's a swish (didn't touch rim)
var congratsText;
if (!lastTouchedRim) {
LK.setScore(LK.getScore() + 2);
LK.getSound('swish').play();
congratsText = new Text2('SWISH!', {
size: 80,
fill: 0x00ff00
});
} else {
LK.setScore(LK.getScore() + 1);
congratsText = new Text2('SCORE!', {
size: 80,
fill: 0xffffff
});
}
// Display congratulatory text with animation
congratsText.anchor.set(0.5, 0.5);
congratsText.x = hoopX;
congratsText.y = hoopY - 100;
congratsText.alpha = 0;
congratsText.scaleX = 0.5;
congratsText.scaleY = 0.5;
game.addChild(congratsText);
// Animate congratulatory text
tween(congratsText, {
alpha: 1,
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(congratsText, {
alpha: 0,
scaleX: 0.8,
scaleY: 0.8,
y: congratsText.y - 50
}, {
duration: 700,
easing: tween.easeIn,
onFinish: function onFinish() {
game.removeChild(congratsText);
}
});
}
});
scoreTxt.setText('Score: ' + LK.getScore());
// Update best score if current score is higher
if (LK.getScore() > bestScore) {
bestScore = LK.getScore();
storage.bestScore = bestScore;
bestScoreTxt.setText('Best: ' + bestScore);
}
basketball.reset();
lastTouchedRim = false;
}
// Check rim collision
if (Math.abs(ballX - hoopX) < 70 && Math.abs(ballY - hoopY) < 20) {
if (ballX < hoopX + hoop.hoopLeft + 10 || ballX > hoopX + hoop.hoopRight - 10) {
lastTouchedRim = true;
LK.getSound('bounce').play();
}
}
// Check backboard collision - backboard is 20px wide, positioned at x: 180 relative to hoop center
var backboardLeft = hoopX + 170; // Left edge of backboard (180 - 10 for half width)
var backboardRight = hoopX + 190; // Right edge of backboard (180 + 10 for half width)
var backboardTop = hoopY - 150; // Top of backboard (100px height, positioned at y: -50)
var backboardBottom = hoopY + 50; // Bottom of backboard
if (ballX > backboardLeft && ballX < backboardRight && ballY > backboardTop && ballY < backboardBottom && basketball.velocityX > 0) {
// Bounce off backboard with reduced horizontal velocity
basketball.velocityX = -basketball.velocityX * 0.7;
// Add slight upward velocity if ball is moving down
if (basketball.velocityY > 0) {
basketball.velocityY *= 0.9;
}
// Move ball away from backboard to prevent sticking
basketball.x = backboardLeft - 40; // Move ball away from backboard
lastTouchedRim = true;
LK.getSound('bounce').play();
}
}
game.down = function (x, y, obj) {
// Start game on first touch
if (!gameStarted) {
gameStarted = true;
startGameTimer();
return;
}
// Handle power bar selection
if (showingPowerBar && !basketball.isFlying) {
// Lock in power value and show direction bar
showingPowerBar = false;
powerBarBackground.alpha = 0;
powerBar.alpha = 0;
powerBallIndicator.alpha = 0;
showingDirectionBar = true;
directionBarBackground.alpha = 1;
directionBar.alpha = 1;
return;
}
// Handle direction bar selection
if (showingDirectionBar && !basketball.isFlying) {
// Lock in direction and shoot
showingDirectionBar = false;
directionBarBackground.alpha = 0;
directionBar.alpha = 0;
// Calculate combined power and direction with multiplicative scaling
var basePower = powerValue / 400; // Normalize to 0-1
var directionFactor = directionValue / 400; // Normalize to 0-1
// When both are in red (high values), multiply for dramatic effect
var combinedPowerMultiplier = 1 + basePower * directionFactor * 9; // Can go up to 10x when both are max
var finalVerticalPower = basePower * 25 * combinedPowerMultiplier;
var finalHorizontalPower = directionFactor * 15 * combinedPowerMultiplier;
basketball.velocityX = finalHorizontalPower; // Horizontal movement based on direction
basketball.velocityY = -finalVerticalPower; // Negative for upward movement
basketball.isFlying = true;
basketball.isDragging = false;
lastTouchedRim = false;
startNewTrail();
return;
}
// Start power bar if ball is ready and game is running
if (gameStarted && !basketball.isFlying && !showingPowerBar && !showingDirectionBar) {
showingPowerBar = true;
powerBarBackground.alpha = 1;
powerBar.alpha = 1;
powerBallIndicator.alpha = 1;
powerValue = 0;
powerDirection = 1;
// Reset ball position but keep time and score
basketball.reset();
}
};
game.move = function (x, y, obj) {
// No dragging mechanics needed anymore
};
game.up = function (x, y, obj) {
// No drag release mechanics needed anymore
};
function startGameTimer() {
timerTxt.setText('Time: 60');
gameTimer = LK.setInterval(function () {
gameTime--;
timerTxt.setText('Time: ' + gameTime);
if (gameTime <= 0) {
LK.clearInterval(gameTimer);
LK.showGameOver();
}
}, 1000);
}
function updatePowerBar() {
if (!showingPowerBar) return;
powerValue += powerDirection * 8;
if (powerValue >= 400) {
powerValue = 400;
powerDirection = -1;
} else if (powerValue <= 0) {
powerValue = 0;
powerDirection = 1;
}
powerBar.height = powerValue;
powerBar.y = powerBarBackground.y + 200 - powerValue;
// Color changes from green (low power) to red (high power) - matching direction bar
var greenAmount = Math.floor(255 * (1 - powerValue / 400));
var redAmount = Math.floor(255 * (powerValue / 400));
powerBar.tint = redAmount << 16 | greenAmount << 8 | 0;
// Update ball indicator position to move up and down with power value
powerBallIndicator.y = powerBarBackground.y + 200 - powerValue;
}
function updateDirectionBar() {
if (!showingDirectionBar) return;
directionValue += directionDirection * 6;
if (directionValue >= 400) {
directionValue = 400;
directionDirection = -1;
} else if (directionValue <= 0) {
directionValue = 0;
directionDirection = 1;
}
directionBar.width = directionValue;
// Red on the left side (more left direction)
var redAmount = Math.floor(255 * (directionValue / 400));
var greenAmount = Math.floor(255 * (1 - directionValue / 400));
directionBar.tint = redAmount << 16 | greenAmount << 8 | 0;
}
game.update = function () {
if (gameStarted) {
updatePowerBar();
updateDirectionBar();
}
// Keep ball always opaque
basketball.alpha = 1.0;
updateTrail();
checkScore();
};