/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
level: 1,
highScores: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
});
/****
* Classes
****/
var AimingSystem = Container.expand(function () {
var self = Container.call(this);
// Create the aim arrow
var arrow = self.attachAsset('aimArrow', {
anchorX: 0,
anchorY: 0.5,
x: 0,
y: 0
});
// Create power meter background
var powerMeter = self.attachAsset('powerMeter', {
anchorX: 0.5,
anchorY: 1,
x: 0,
y: 0
});
// Create power indicator
var powerIndicator = self.attachAsset('powerIndicator', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: -powerMeter.height + 10
});
self.visible = false;
self.angle = -45;
self.power = 0;
self.powerIncreasing = true;
self.powerSpeed = 2;
self.maxPower = 100;
// Position offset from power meter to arrow
var offsetX = 100;
self.setPosition = function (x, y) {
self.x = x;
self.y = y;
powerMeter.x = x - offsetX;
powerMeter.y = y;
};
self.setAngle = function (angle) {
self.angle = angle;
arrow.rotation = angle * Math.PI / 180;
};
self.setPower = function (power) {
self.power = power;
var normalizedPower = power / self.maxPower;
powerIndicator.y = -powerMeter.height * normalizedPower + 10;
};
self.startPowerPhase = function () {
self.power = 0;
self.powerIncreasing = true;
self.visible = true;
};
self.update = function () {
if (gameState === "power" && self.visible) {
if (self.powerIncreasing) {
self.power += self.powerSpeed;
if (self.power >= self.maxPower) {
self.power = self.maxPower;
self.powerIncreasing = false;
}
} else {
self.power -= self.powerSpeed;
if (self.power <= 0) {
self.power = 0;
self.powerIncreasing = true;
}
}
self.setPower(self.power);
}
};
return self;
});
var BasketHoop = Container.expand(function () {
var self = Container.call(this);
// Create backboard
var backboard = self.attachAsset('backboard', {
anchorX: 0.5,
anchorY: 0,
x: 0,
y: 0
});
// Create rim
var hoop = self.attachAsset('hoop', {
anchorX: 0.5,
anchorY: 0,
x: 0,
y: backboard.height + 20
});
// Create net
self.net = self.attachAsset('net', {
anchorX: 0.5,
anchorY: 0,
x: 0,
y: backboard.height + 20 + hoop.height / 2,
alpha: 0.5
});
// For hoop movement
self.originalX = 0;
self.originalY = 0;
self.moveSpeed = 0;
self.moveRange = 0;
self.moveDir = 1;
self.setMoving = function (speed, range) {
self.moveSpeed = speed;
self.moveRange = range;
self.originalX = self.x;
};
self.update = function () {
if (self.moveSpeed > 0) {
self.x += self.moveSpeed * self.moveDir;
if (self.x > self.originalX + self.moveRange) {
self.x = self.originalX + self.moveRange;
self.moveDir = -1;
} else if (self.x < self.originalX - self.moveRange) {
self.x = self.originalX - self.moveRange;
self.moveDir = 1;
}
}
};
return self;
});
var Basketball = Container.expand(function () {
var self = Container.call(this);
var ball = self.attachAsset('ball', {
anchorX: 0.5,
anchorY: 0.5
});
var ballShine = self.attachAsset('ballShine', {
anchorX: 0.5,
anchorY: 0.5,
x: -20,
y: -20
});
self.velocity = {
x: 0,
y: 0
};
self.gravity = 0.5;
self.bounceDecay = 0.65;
self.friction = 0.98;
self.active = false;
self.scored = false;
self.update = function () {
if (!self.active) {
return;
}
// Apply physics
self.velocity.y += self.gravity;
self.x += self.velocity.x;
self.y += self.velocity.y;
// Apply wind if present
if (currentLevel.wind) {
self.velocity.x += currentLevel.wind / 50;
}
// Ball rotation based on horizontal movement
ball.rotation += self.velocity.x / 30;
// Check floor collision
if (self.y > floorY - ball.height / 2) {
self.y = floorY - ball.height / 2;
self.velocity.y = -self.velocity.y * self.bounceDecay;
self.velocity.x *= self.friction;
LK.getSound('bounce').play();
// Stop ball if it's bounced enough and is slow
if (Math.abs(self.velocity.y) < 2 && Math.abs(self.velocity.x) < 1) {
self.active = false;
shotsRemaining--;
updateShotsText();
if (shotsRemaining <= 0 && !levelComplete) {
LK.setTimeout(function () {
if (!levelComplete) {
LK.showGameOver();
}
}, 1500);
} else if (!levelComplete) {
resetShot();
}
}
}
// Check wall collisions
if (self.x < ball.width / 2) {
self.x = ball.width / 2;
self.velocity.x = -self.velocity.x * self.bounceDecay;
LK.getSound('bounce').play();
} else if (self.x > 2048 - ball.width / 2) {
self.x = 2048 - ball.width / 2;
self.velocity.x = -self.velocity.x * self.bounceDecay;
LK.getSound('bounce').play();
}
// Check obstacle collisions
for (var i = 0; i < obstacles.length; i++) {
if (self.intersects(obstacles[i])) {
// Basic collision response
var dx = self.x - obstacles[i].x;
var dy = self.y - obstacles[i].y;
var dist = Math.sqrt(dx * dx + dy * dy);
var nx = dx / dist;
var ny = dy / dist;
// Push ball away from obstacle
self.x += nx * 5;
self.y += ny * 5;
// Reflect velocity
var dotProduct = self.velocity.x * nx + self.velocity.y * ny;
self.velocity.x = (self.velocity.x - 2 * dotProduct * nx) * self.bounceDecay;
self.velocity.y = (self.velocity.y - 2 * dotProduct * ny) * self.bounceDecay;
LK.getSound('bounce').play();
}
}
// Check defenders
for (var i = 0; i < defenders.length; i++) {
if (self.intersects(defenders[i])) {
// Simplified collision with defenders - just bounce back
self.velocity.x = -self.velocity.x * self.bounceDecay;
self.velocity.y = -self.velocity.y * self.bounceDecay;
LK.getSound('bounce').play();
}
}
// Check hoop scoring
if (!self.scored && self.intersects(basketHoop.net)) {
// Ball position relative to hoop
var ballToHoop = self.toLocal(basketHoop.net.position, basketHoop);
// Only count if the ball is going down and is near the center of the hoop
if (self.velocity.y > 0 && Math.abs(ballToHoop.x) < basketHoop.net.width / 4) {
self.scored = true;
levelScore++;
LK.setScore(LK.getScore() + 100);
updateScoreText();
LK.getSound('swish').play();
// Check if level is complete
if (levelScore >= currentLevel.requiredScores) {
levelComplete = true;
// Update high score if necessary
var highScores = storage.highScores;
if (LK.getScore() > highScores[currentLevelIndex]) {
highScores[currentLevelIndex] = LK.getScore();
storage.highScores = highScores;
}
// Advance to next level
currentLevelIndex++;
if (currentLevelIndex < levels.length) {
LK.setTimeout(function () {
levelTitleText.setText("LEVEL " + (currentLevelIndex + 1) + " COMPLETE!");
LK.effects.flashScreen(0x27ae60, 1000);
LK.getSound('levelUp').play();
LK.setTimeout(function () {
loadLevel(currentLevelIndex);
}, 2000);
}, 1000);
} else {
// Game completed
LK.setTimeout(function () {
levelTitleText.setText("GAME COMPLETED!");
LK.effects.flashScreen(0x27ae60, 1000);
LK.getSound('levelUp').play();
LK.setTimeout(function () {
LK.showYouWin();
}, 2000);
}, 1000);
}
} else {
// Continue level with next shot
LK.setTimeout(function () {
if (!levelComplete) {
resetShot();
}
}, 1500);
}
}
}
};
return self;
});
var Defender = Container.expand(function () {
var self = Container.call(this);
// Create defender body
var body = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 1,
x: 0,
y: 0,
tint: 0xe74c3c
});
// Create defender head
var head = self.attachAsset('playerHead', {
anchorX: 0.5,
anchorY: 1,
x: 0,
y: -body.height,
tint: 0xe74c3c
});
// For defender movement
self.originalX = 0;
self.moveSpeed = 0;
self.moveRange = 0;
self.moveDir = 1;
self.setMoving = function (speed, range) {
self.moveSpeed = speed;
self.moveRange = range;
self.originalX = self.x;
};
self.update = function () {
if (self.moveSpeed > 0) {
self.x += self.moveSpeed * self.moveDir;
if (self.x > self.originalX + self.moveRange) {
self.x = self.originalX + self.moveRange;
self.moveDir = -1;
} else if (self.x < self.originalX - self.moveRange) {
self.x = self.originalX - self.moveRange;
self.moveDir = 1;
}
}
};
return self;
});
var Player = Container.expand(function () {
var self = Container.call(this);
// Create player body
var body = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 1,
x: 0,
y: 0
});
// Create player head
var head = self.attachAsset('playerHead', {
anchorX: 0.5,
anchorY: 1,
x: 0,
y: -body.height
});
self.canShoot = true;
self.down = function (x, y, obj) {
if (gameState === "aiming" && self.canShoot) {
startAiming(self.x, self.y - body.height / 2);
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x3498db
});
/****
* Game Code
****/
// Game state management
var gameState = "start"; // "start", "aiming", "power", "shooting"
var currentLevelIndex = storage.level - 1;
var currentLevel;
var levels = [];
var floorY = 2600;
var levelComplete = false;
var levelScore = 0;
var shotsRemaining = 5;
// Game elements
var basketball;
var basketHoop;
var player;
var aimingSystem;
var obstacles = [];
var defenders = [];
// UI elements
var levelTitleText;
var scoreText;
var shotsText;
var windText;
var levelInfoText;
// Level definitions
function initLevels() {
levels = [{
title: "Rookie Shot",
description: "Make simple shots to learn the basics",
hoopX: 1600,
hoopY: 1600,
playerX: 400,
playerY: floorY,
requiredScores: 3,
shotsAllowed: 5,
movingHoop: false,
wind: 0,
obstacles: [],
defenders: []
}, {
title: "Side Angle",
description: "Shoot from the side",
hoopX: 1700,
hoopY: 1600,
playerX: 300,
playerY: floorY,
requiredScores: 3,
shotsAllowed: 5,
movingHoop: false,
wind: 0,
obstacles: [],
defenders: []
}, {
title: "Moving Target",
description: "Hit a moving basket",
hoopX: 1500,
hoopY: 1600,
playerX: 400,
playerY: floorY,
requiredScores: 3,
shotsAllowed: 6,
movingHoop: true,
hoopSpeed: 3,
hoopRange: 300,
wind: 0,
obstacles: [],
defenders: []
}, {
title: "Obstacle Course",
description: "Navigate around obstacles",
hoopX: 1600,
hoopY: 1600,
playerX: 400,
playerY: floorY,
requiredScores: 2,
shotsAllowed: 5,
movingHoop: false,
wind: 0,
obstacles: [{
x: 1000,
y: 2000,
width: 100,
height: 100
}],
defenders: []
}, {
title: "Defender Pressure",
description: "Shoot past defenders",
hoopX: 1600,
hoopY: 1600,
playerX: 400,
playerY: floorY,
requiredScores: 2,
shotsAllowed: 5,
movingHoop: false,
wind: 0,
obstacles: [],
defenders: [{
x: 1000,
y: floorY,
speed: 0,
range: 0
}]
}, {
title: "Windy Day",
description: "Account for wind when shooting",
hoopX: 1600,
hoopY: 1600,
playerX: 400,
playerY: floorY,
requiredScores: 2,
shotsAllowed: 6,
movingHoop: false,
wind: 1.5,
obstacles: [],
defenders: []
}, {
title: "Moving Defenders",
description: "Time your shots carefully",
hoopX: 1600,
hoopY: 1600,
playerX: 400,
playerY: floorY,
requiredScores: 2,
shotsAllowed: 5,
movingHoop: false,
wind: 0,
obstacles: [],
defenders: [{
x: 800,
y: floorY,
speed: 4,
range: 200
}, {
x: 1200,
y: floorY,
speed: 3,
range: 150
}]
}, {
title: "Complex Challenge",
description: "Wind, moving hoop, and defenders",
hoopX: 1600,
hoopY: 1600,
playerX: 400,
playerY: floorY,
requiredScores: 2,
shotsAllowed: 6,
movingHoop: true,
hoopSpeed: 2,
hoopRange: 200,
wind: 1,
obstacles: [],
defenders: [{
x: 1000,
y: floorY,
speed: 3,
range: 200
}]
}, {
title: "Obstacle Maze",
description: "Navigate through multiple obstacles",
hoopX: 1600,
hoopY: 1600,
playerX: 400,
playerY: floorY,
requiredScores: 2,
shotsAllowed: 6,
movingHoop: false,
wind: 0.5,
obstacles: [{
x: 800,
y: 2000,
width: 100,
height: 100
}, {
x: 1200,
y: 1800,
width: 100,
height: 100
}],
defenders: []
}, {
title: "Championship",
description: "Use all your skills for the final challenge",
hoopX: 1600,
hoopY: 1500,
playerX: 400,
playerY: floorY,
requiredScores: 3,
shotsAllowed: 5,
movingHoop: true,
hoopSpeed: 4,
hoopRange: 300,
wind: 1.2,
obstacles: [{
x: 900,
y: 2000,
width: 100,
height: 100
}],
defenders: [{
x: 1200,
y: floorY,
speed: 5,
range: 250
}]
}];
}
// Function to load a specific level
function loadLevel(levelIndex) {
// Clear previous level objects
clearLevel();
// Set current level
currentLevel = levels[levelIndex];
storage.level = levelIndex + 1;
// Reset level state
levelComplete = false;
levelScore = 0;
shotsRemaining = currentLevel.shotsAllowed;
// Create floor
var floor = game.addChild(LK.getAsset('floor', {
anchorX: 0,
anchorY: 0,
x: 0,
y: floorY
}));
// Create basketball hoop
basketHoop = game.addChild(new BasketHoop());
basketHoop.x = currentLevel.hoopX;
basketHoop.y = currentLevel.hoopY;
if (currentLevel.movingHoop) {
basketHoop.setMoving(currentLevel.hoopSpeed, currentLevel.hoopRange);
}
// Create player
player = game.addChild(new Player());
player.x = currentLevel.playerX;
player.y = currentLevel.playerY;
// Create obstacles
for (var i = 0; i < currentLevel.obstacles.length; i++) {
var obstacleData = currentLevel.obstacles[i];
var obstacle = game.addChild(LK.getAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5,
x: obstacleData.x,
y: obstacleData.y
}));
obstacles.push(obstacle);
}
// Create defenders
for (var i = 0; i < currentLevel.defenders.length; i++) {
var defenderData = currentLevel.defenders[i];
var defender = game.addChild(new Defender());
defender.x = defenderData.x;
defender.y = defenderData.y;
if (defenderData.speed > 0) {
defender.setMoving(defenderData.speed, defenderData.range);
}
defenders.push(defender);
}
// Create basketball
basketball = game.addChild(new Basketball());
resetShot();
// Create aiming system
aimingSystem = game.addChild(new AimingSystem());
aimingSystem.visible = false;
// Update UI
levelTitleText.setText("LEVEL " + (levelIndex + 1) + ": " + currentLevel.title);
updateShotsText();
updateWindText();
levelInfoText.setText(currentLevel.description);
// Reset game state
gameState = "start";
// Show level intro effect
LK.effects.flashScreen(0x3498db, 1000);
}
// Function to clear current level objects
function clearLevel() {
if (basketball) {
basketball.destroy();
basketball = null;
}
if (basketHoop) {
basketHoop.destroy();
basketHoop = null;
}
if (player) {
player.destroy();
player = null;
}
if (aimingSystem) {
aimingSystem.destroy();
aimingSystem = null;
}
// Clear obstacles
for (var i = 0; i < obstacles.length; i++) {
obstacles[i].destroy();
}
obstacles = [];
// Clear defenders
for (var i = 0; i < defenders.length; i++) {
defenders[i].destroy();
}
defenders = [];
}
// Function to reset shot
function resetShot() {
basketball.x = player.x;
basketball.y = player.y - 150;
basketball.velocity.x = 0;
basketball.velocity.y = 0;
basketball.active = false;
basketball.scored = false;
gameState = "start";
// Make sure aiming system is hidden
if (aimingSystem) {
aimingSystem.visible = false;
}
}
// Function to start aiming
function startAiming(x, y) {
gameState = "aiming";
aimingSystem.setPosition(x, y);
aimingSystem.visible = true;
}
// Function to shoot the ball
function shootBall() {
var angle = aimingSystem.angle;
var power = aimingSystem.power / 20; // Scale power appropriately
basketball.velocity.x = Math.cos(angle * Math.PI / 180) * power;
basketball.velocity.y = Math.sin(angle * Math.PI / 180) * power;
basketball.active = true;
gameState = "shooting";
aimingSystem.visible = false;
}
// Setup UI
function setupUI() {
// Level title text
levelTitleText = new Text2("LEVEL 1", {
size: 80,
fill: 0xFFFFFF
});
levelTitleText.anchor.set(0.5, 0);
LK.gui.top.addChild(levelTitleText);
// Score text
scoreText = new Text2("SCORE: 0", {
size: 60,
fill: 0xFFFFFF
});
scoreText.anchor.set(0, 0);
scoreText.x = 150;
LK.gui.topRight.addChild(scoreText);
// Shots text
shotsText = new Text2("SHOTS: 5", {
size: 60,
fill: 0xFFFFFF
});
shotsText.anchor.set(0, 0);
shotsText.x = 150;
shotsText.y = 80;
LK.gui.topRight.addChild(shotsText);
// Wind text
windText = new Text2("WIND: 0", {
size: 60,
fill: 0xFFFFFF
});
windText.anchor.set(0, 0);
windText.x = 150;
windText.y = 160;
LK.gui.topRight.addChild(windText);
// Level info text
levelInfoText = new Text2("Make simple shots to learn the basics", {
size: 40,
fill: 0xFFFFFF
});
levelInfoText.anchor.set(0.5, 0);
levelInfoText.y = 100;
LK.gui.top.addChild(levelInfoText);
}
// Update UI helper functions
function updateScoreText() {
scoreText.setText("SCORE: " + LK.getScore());
}
function updateShotsText() {
shotsText.setText("SHOTS: " + shotsRemaining);
}
function updateWindText() {
if (currentLevel && currentLevel.wind) {
var direction = currentLevel.wind > 0 ? "→" : "←";
var strength = Math.abs(currentLevel.wind);
var windStr = "";
for (var i = 0; i < Math.ceil(strength); i++) {
windStr += direction;
}
windText.setText("WIND: " + windStr);
} else {
windText.setText("WIND: NONE");
}
}
// Input handling
game.down = function (x, y, obj) {
if (gameState === "aiming") {
gameState = "power";
aimingSystem.startPowerPhase();
} else if (gameState === "power") {
gameState = "shooting";
shootBall();
} else if (gameState === "start" && player && player.canShoot) {
startAiming(player.x, player.y - 150);
}
};
game.move = function (x, y, obj) {
if (gameState === "aiming" && aimingSystem && aimingSystem.visible) {
// Calculate angle based on mouse position relative to player
var dx = x - player.x;
var dy = y - player.y;
var angle = Math.atan2(dy, dx) * 180 / Math.PI;
// Constrain angle for shooting upward only
if (angle > 0) {
angle = 0;
}
if (angle < -90) {
angle = -90;
}
aimingSystem.setAngle(angle);
}
};
// Game update loop
game.update = function () {
// Update game objects
if (basketball) {
basketball.update();
}
if (basketHoop) {
basketHoop.update();
}
if (aimingSystem) {
aimingSystem.update();
}
// Update defenders
for (var i = 0; i < defenders.length; i++) {
defenders[i].update();
}
};
// Initialize game
function initGame() {
// Initialize levels
initLevels();
// Setup UI
setupUI();
// Reset score
LK.setScore(0);
updateScoreText();
// Load first level
loadLevel(currentLevelIndex);
// Play background music
LK.playMusic('gameMusic');
}
// Start the game
initGame(); /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
level: 1,
highScores: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
});
/****
* Classes
****/
var AimingSystem = Container.expand(function () {
var self = Container.call(this);
// Create the aim arrow
var arrow = self.attachAsset('aimArrow', {
anchorX: 0,
anchorY: 0.5,
x: 0,
y: 0
});
// Create power meter background
var powerMeter = self.attachAsset('powerMeter', {
anchorX: 0.5,
anchorY: 1,
x: 0,
y: 0
});
// Create power indicator
var powerIndicator = self.attachAsset('powerIndicator', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: -powerMeter.height + 10
});
self.visible = false;
self.angle = -45;
self.power = 0;
self.powerIncreasing = true;
self.powerSpeed = 2;
self.maxPower = 100;
// Position offset from power meter to arrow
var offsetX = 100;
self.setPosition = function (x, y) {
self.x = x;
self.y = y;
powerMeter.x = x - offsetX;
powerMeter.y = y;
};
self.setAngle = function (angle) {
self.angle = angle;
arrow.rotation = angle * Math.PI / 180;
};
self.setPower = function (power) {
self.power = power;
var normalizedPower = power / self.maxPower;
powerIndicator.y = -powerMeter.height * normalizedPower + 10;
};
self.startPowerPhase = function () {
self.power = 0;
self.powerIncreasing = true;
self.visible = true;
};
self.update = function () {
if (gameState === "power" && self.visible) {
if (self.powerIncreasing) {
self.power += self.powerSpeed;
if (self.power >= self.maxPower) {
self.power = self.maxPower;
self.powerIncreasing = false;
}
} else {
self.power -= self.powerSpeed;
if (self.power <= 0) {
self.power = 0;
self.powerIncreasing = true;
}
}
self.setPower(self.power);
}
};
return self;
});
var BasketHoop = Container.expand(function () {
var self = Container.call(this);
// Create backboard
var backboard = self.attachAsset('backboard', {
anchorX: 0.5,
anchorY: 0,
x: 0,
y: 0
});
// Create rim
var hoop = self.attachAsset('hoop', {
anchorX: 0.5,
anchorY: 0,
x: 0,
y: backboard.height + 20
});
// Create net
self.net = self.attachAsset('net', {
anchorX: 0.5,
anchorY: 0,
x: 0,
y: backboard.height + 20 + hoop.height / 2,
alpha: 0.5
});
// For hoop movement
self.originalX = 0;
self.originalY = 0;
self.moveSpeed = 0;
self.moveRange = 0;
self.moveDir = 1;
self.setMoving = function (speed, range) {
self.moveSpeed = speed;
self.moveRange = range;
self.originalX = self.x;
};
self.update = function () {
if (self.moveSpeed > 0) {
self.x += self.moveSpeed * self.moveDir;
if (self.x > self.originalX + self.moveRange) {
self.x = self.originalX + self.moveRange;
self.moveDir = -1;
} else if (self.x < self.originalX - self.moveRange) {
self.x = self.originalX - self.moveRange;
self.moveDir = 1;
}
}
};
return self;
});
var Basketball = Container.expand(function () {
var self = Container.call(this);
var ball = self.attachAsset('ball', {
anchorX: 0.5,
anchorY: 0.5
});
var ballShine = self.attachAsset('ballShine', {
anchorX: 0.5,
anchorY: 0.5,
x: -20,
y: -20
});
self.velocity = {
x: 0,
y: 0
};
self.gravity = 0.5;
self.bounceDecay = 0.65;
self.friction = 0.98;
self.active = false;
self.scored = false;
self.update = function () {
if (!self.active) {
return;
}
// Apply physics
self.velocity.y += self.gravity;
self.x += self.velocity.x;
self.y += self.velocity.y;
// Apply wind if present
if (currentLevel.wind) {
self.velocity.x += currentLevel.wind / 50;
}
// Ball rotation based on horizontal movement
ball.rotation += self.velocity.x / 30;
// Check floor collision
if (self.y > floorY - ball.height / 2) {
self.y = floorY - ball.height / 2;
self.velocity.y = -self.velocity.y * self.bounceDecay;
self.velocity.x *= self.friction;
LK.getSound('bounce').play();
// Stop ball if it's bounced enough and is slow
if (Math.abs(self.velocity.y) < 2 && Math.abs(self.velocity.x) < 1) {
self.active = false;
shotsRemaining--;
updateShotsText();
if (shotsRemaining <= 0 && !levelComplete) {
LK.setTimeout(function () {
if (!levelComplete) {
LK.showGameOver();
}
}, 1500);
} else if (!levelComplete) {
resetShot();
}
}
}
// Check wall collisions
if (self.x < ball.width / 2) {
self.x = ball.width / 2;
self.velocity.x = -self.velocity.x * self.bounceDecay;
LK.getSound('bounce').play();
} else if (self.x > 2048 - ball.width / 2) {
self.x = 2048 - ball.width / 2;
self.velocity.x = -self.velocity.x * self.bounceDecay;
LK.getSound('bounce').play();
}
// Check obstacle collisions
for (var i = 0; i < obstacles.length; i++) {
if (self.intersects(obstacles[i])) {
// Basic collision response
var dx = self.x - obstacles[i].x;
var dy = self.y - obstacles[i].y;
var dist = Math.sqrt(dx * dx + dy * dy);
var nx = dx / dist;
var ny = dy / dist;
// Push ball away from obstacle
self.x += nx * 5;
self.y += ny * 5;
// Reflect velocity
var dotProduct = self.velocity.x * nx + self.velocity.y * ny;
self.velocity.x = (self.velocity.x - 2 * dotProduct * nx) * self.bounceDecay;
self.velocity.y = (self.velocity.y - 2 * dotProduct * ny) * self.bounceDecay;
LK.getSound('bounce').play();
}
}
// Check defenders
for (var i = 0; i < defenders.length; i++) {
if (self.intersects(defenders[i])) {
// Simplified collision with defenders - just bounce back
self.velocity.x = -self.velocity.x * self.bounceDecay;
self.velocity.y = -self.velocity.y * self.bounceDecay;
LK.getSound('bounce').play();
}
}
// Check hoop scoring
if (!self.scored && self.intersects(basketHoop.net)) {
// Ball position relative to hoop
var ballToHoop = self.toLocal(basketHoop.net.position, basketHoop);
// Only count if the ball is going down and is near the center of the hoop
if (self.velocity.y > 0 && Math.abs(ballToHoop.x) < basketHoop.net.width / 4) {
self.scored = true;
levelScore++;
LK.setScore(LK.getScore() + 100);
updateScoreText();
LK.getSound('swish').play();
// Check if level is complete
if (levelScore >= currentLevel.requiredScores) {
levelComplete = true;
// Update high score if necessary
var highScores = storage.highScores;
if (LK.getScore() > highScores[currentLevelIndex]) {
highScores[currentLevelIndex] = LK.getScore();
storage.highScores = highScores;
}
// Advance to next level
currentLevelIndex++;
if (currentLevelIndex < levels.length) {
LK.setTimeout(function () {
levelTitleText.setText("LEVEL " + (currentLevelIndex + 1) + " COMPLETE!");
LK.effects.flashScreen(0x27ae60, 1000);
LK.getSound('levelUp').play();
LK.setTimeout(function () {
loadLevel(currentLevelIndex);
}, 2000);
}, 1000);
} else {
// Game completed
LK.setTimeout(function () {
levelTitleText.setText("GAME COMPLETED!");
LK.effects.flashScreen(0x27ae60, 1000);
LK.getSound('levelUp').play();
LK.setTimeout(function () {
LK.showYouWin();
}, 2000);
}, 1000);
}
} else {
// Continue level with next shot
LK.setTimeout(function () {
if (!levelComplete) {
resetShot();
}
}, 1500);
}
}
}
};
return self;
});
var Defender = Container.expand(function () {
var self = Container.call(this);
// Create defender body
var body = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 1,
x: 0,
y: 0,
tint: 0xe74c3c
});
// Create defender head
var head = self.attachAsset('playerHead', {
anchorX: 0.5,
anchorY: 1,
x: 0,
y: -body.height,
tint: 0xe74c3c
});
// For defender movement
self.originalX = 0;
self.moveSpeed = 0;
self.moveRange = 0;
self.moveDir = 1;
self.setMoving = function (speed, range) {
self.moveSpeed = speed;
self.moveRange = range;
self.originalX = self.x;
};
self.update = function () {
if (self.moveSpeed > 0) {
self.x += self.moveSpeed * self.moveDir;
if (self.x > self.originalX + self.moveRange) {
self.x = self.originalX + self.moveRange;
self.moveDir = -1;
} else if (self.x < self.originalX - self.moveRange) {
self.x = self.originalX - self.moveRange;
self.moveDir = 1;
}
}
};
return self;
});
var Player = Container.expand(function () {
var self = Container.call(this);
// Create player body
var body = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 1,
x: 0,
y: 0
});
// Create player head
var head = self.attachAsset('playerHead', {
anchorX: 0.5,
anchorY: 1,
x: 0,
y: -body.height
});
self.canShoot = true;
self.down = function (x, y, obj) {
if (gameState === "aiming" && self.canShoot) {
startAiming(self.x, self.y - body.height / 2);
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x3498db
});
/****
* Game Code
****/
// Game state management
var gameState = "start"; // "start", "aiming", "power", "shooting"
var currentLevelIndex = storage.level - 1;
var currentLevel;
var levels = [];
var floorY = 2600;
var levelComplete = false;
var levelScore = 0;
var shotsRemaining = 5;
// Game elements
var basketball;
var basketHoop;
var player;
var aimingSystem;
var obstacles = [];
var defenders = [];
// UI elements
var levelTitleText;
var scoreText;
var shotsText;
var windText;
var levelInfoText;
// Level definitions
function initLevels() {
levels = [{
title: "Rookie Shot",
description: "Make simple shots to learn the basics",
hoopX: 1600,
hoopY: 1600,
playerX: 400,
playerY: floorY,
requiredScores: 3,
shotsAllowed: 5,
movingHoop: false,
wind: 0,
obstacles: [],
defenders: []
}, {
title: "Side Angle",
description: "Shoot from the side",
hoopX: 1700,
hoopY: 1600,
playerX: 300,
playerY: floorY,
requiredScores: 3,
shotsAllowed: 5,
movingHoop: false,
wind: 0,
obstacles: [],
defenders: []
}, {
title: "Moving Target",
description: "Hit a moving basket",
hoopX: 1500,
hoopY: 1600,
playerX: 400,
playerY: floorY,
requiredScores: 3,
shotsAllowed: 6,
movingHoop: true,
hoopSpeed: 3,
hoopRange: 300,
wind: 0,
obstacles: [],
defenders: []
}, {
title: "Obstacle Course",
description: "Navigate around obstacles",
hoopX: 1600,
hoopY: 1600,
playerX: 400,
playerY: floorY,
requiredScores: 2,
shotsAllowed: 5,
movingHoop: false,
wind: 0,
obstacles: [{
x: 1000,
y: 2000,
width: 100,
height: 100
}],
defenders: []
}, {
title: "Defender Pressure",
description: "Shoot past defenders",
hoopX: 1600,
hoopY: 1600,
playerX: 400,
playerY: floorY,
requiredScores: 2,
shotsAllowed: 5,
movingHoop: false,
wind: 0,
obstacles: [],
defenders: [{
x: 1000,
y: floorY,
speed: 0,
range: 0
}]
}, {
title: "Windy Day",
description: "Account for wind when shooting",
hoopX: 1600,
hoopY: 1600,
playerX: 400,
playerY: floorY,
requiredScores: 2,
shotsAllowed: 6,
movingHoop: false,
wind: 1.5,
obstacles: [],
defenders: []
}, {
title: "Moving Defenders",
description: "Time your shots carefully",
hoopX: 1600,
hoopY: 1600,
playerX: 400,
playerY: floorY,
requiredScores: 2,
shotsAllowed: 5,
movingHoop: false,
wind: 0,
obstacles: [],
defenders: [{
x: 800,
y: floorY,
speed: 4,
range: 200
}, {
x: 1200,
y: floorY,
speed: 3,
range: 150
}]
}, {
title: "Complex Challenge",
description: "Wind, moving hoop, and defenders",
hoopX: 1600,
hoopY: 1600,
playerX: 400,
playerY: floorY,
requiredScores: 2,
shotsAllowed: 6,
movingHoop: true,
hoopSpeed: 2,
hoopRange: 200,
wind: 1,
obstacles: [],
defenders: [{
x: 1000,
y: floorY,
speed: 3,
range: 200
}]
}, {
title: "Obstacle Maze",
description: "Navigate through multiple obstacles",
hoopX: 1600,
hoopY: 1600,
playerX: 400,
playerY: floorY,
requiredScores: 2,
shotsAllowed: 6,
movingHoop: false,
wind: 0.5,
obstacles: [{
x: 800,
y: 2000,
width: 100,
height: 100
}, {
x: 1200,
y: 1800,
width: 100,
height: 100
}],
defenders: []
}, {
title: "Championship",
description: "Use all your skills for the final challenge",
hoopX: 1600,
hoopY: 1500,
playerX: 400,
playerY: floorY,
requiredScores: 3,
shotsAllowed: 5,
movingHoop: true,
hoopSpeed: 4,
hoopRange: 300,
wind: 1.2,
obstacles: [{
x: 900,
y: 2000,
width: 100,
height: 100
}],
defenders: [{
x: 1200,
y: floorY,
speed: 5,
range: 250
}]
}];
}
// Function to load a specific level
function loadLevel(levelIndex) {
// Clear previous level objects
clearLevel();
// Set current level
currentLevel = levels[levelIndex];
storage.level = levelIndex + 1;
// Reset level state
levelComplete = false;
levelScore = 0;
shotsRemaining = currentLevel.shotsAllowed;
// Create floor
var floor = game.addChild(LK.getAsset('floor', {
anchorX: 0,
anchorY: 0,
x: 0,
y: floorY
}));
// Create basketball hoop
basketHoop = game.addChild(new BasketHoop());
basketHoop.x = currentLevel.hoopX;
basketHoop.y = currentLevel.hoopY;
if (currentLevel.movingHoop) {
basketHoop.setMoving(currentLevel.hoopSpeed, currentLevel.hoopRange);
}
// Create player
player = game.addChild(new Player());
player.x = currentLevel.playerX;
player.y = currentLevel.playerY;
// Create obstacles
for (var i = 0; i < currentLevel.obstacles.length; i++) {
var obstacleData = currentLevel.obstacles[i];
var obstacle = game.addChild(LK.getAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5,
x: obstacleData.x,
y: obstacleData.y
}));
obstacles.push(obstacle);
}
// Create defenders
for (var i = 0; i < currentLevel.defenders.length; i++) {
var defenderData = currentLevel.defenders[i];
var defender = game.addChild(new Defender());
defender.x = defenderData.x;
defender.y = defenderData.y;
if (defenderData.speed > 0) {
defender.setMoving(defenderData.speed, defenderData.range);
}
defenders.push(defender);
}
// Create basketball
basketball = game.addChild(new Basketball());
resetShot();
// Create aiming system
aimingSystem = game.addChild(new AimingSystem());
aimingSystem.visible = false;
// Update UI
levelTitleText.setText("LEVEL " + (levelIndex + 1) + ": " + currentLevel.title);
updateShotsText();
updateWindText();
levelInfoText.setText(currentLevel.description);
// Reset game state
gameState = "start";
// Show level intro effect
LK.effects.flashScreen(0x3498db, 1000);
}
// Function to clear current level objects
function clearLevel() {
if (basketball) {
basketball.destroy();
basketball = null;
}
if (basketHoop) {
basketHoop.destroy();
basketHoop = null;
}
if (player) {
player.destroy();
player = null;
}
if (aimingSystem) {
aimingSystem.destroy();
aimingSystem = null;
}
// Clear obstacles
for (var i = 0; i < obstacles.length; i++) {
obstacles[i].destroy();
}
obstacles = [];
// Clear defenders
for (var i = 0; i < defenders.length; i++) {
defenders[i].destroy();
}
defenders = [];
}
// Function to reset shot
function resetShot() {
basketball.x = player.x;
basketball.y = player.y - 150;
basketball.velocity.x = 0;
basketball.velocity.y = 0;
basketball.active = false;
basketball.scored = false;
gameState = "start";
// Make sure aiming system is hidden
if (aimingSystem) {
aimingSystem.visible = false;
}
}
// Function to start aiming
function startAiming(x, y) {
gameState = "aiming";
aimingSystem.setPosition(x, y);
aimingSystem.visible = true;
}
// Function to shoot the ball
function shootBall() {
var angle = aimingSystem.angle;
var power = aimingSystem.power / 20; // Scale power appropriately
basketball.velocity.x = Math.cos(angle * Math.PI / 180) * power;
basketball.velocity.y = Math.sin(angle * Math.PI / 180) * power;
basketball.active = true;
gameState = "shooting";
aimingSystem.visible = false;
}
// Setup UI
function setupUI() {
// Level title text
levelTitleText = new Text2("LEVEL 1", {
size: 80,
fill: 0xFFFFFF
});
levelTitleText.anchor.set(0.5, 0);
LK.gui.top.addChild(levelTitleText);
// Score text
scoreText = new Text2("SCORE: 0", {
size: 60,
fill: 0xFFFFFF
});
scoreText.anchor.set(0, 0);
scoreText.x = 150;
LK.gui.topRight.addChild(scoreText);
// Shots text
shotsText = new Text2("SHOTS: 5", {
size: 60,
fill: 0xFFFFFF
});
shotsText.anchor.set(0, 0);
shotsText.x = 150;
shotsText.y = 80;
LK.gui.topRight.addChild(shotsText);
// Wind text
windText = new Text2("WIND: 0", {
size: 60,
fill: 0xFFFFFF
});
windText.anchor.set(0, 0);
windText.x = 150;
windText.y = 160;
LK.gui.topRight.addChild(windText);
// Level info text
levelInfoText = new Text2("Make simple shots to learn the basics", {
size: 40,
fill: 0xFFFFFF
});
levelInfoText.anchor.set(0.5, 0);
levelInfoText.y = 100;
LK.gui.top.addChild(levelInfoText);
}
// Update UI helper functions
function updateScoreText() {
scoreText.setText("SCORE: " + LK.getScore());
}
function updateShotsText() {
shotsText.setText("SHOTS: " + shotsRemaining);
}
function updateWindText() {
if (currentLevel && currentLevel.wind) {
var direction = currentLevel.wind > 0 ? "→" : "←";
var strength = Math.abs(currentLevel.wind);
var windStr = "";
for (var i = 0; i < Math.ceil(strength); i++) {
windStr += direction;
}
windText.setText("WIND: " + windStr);
} else {
windText.setText("WIND: NONE");
}
}
// Input handling
game.down = function (x, y, obj) {
if (gameState === "aiming") {
gameState = "power";
aimingSystem.startPowerPhase();
} else if (gameState === "power") {
gameState = "shooting";
shootBall();
} else if (gameState === "start" && player && player.canShoot) {
startAiming(player.x, player.y - 150);
}
};
game.move = function (x, y, obj) {
if (gameState === "aiming" && aimingSystem && aimingSystem.visible) {
// Calculate angle based on mouse position relative to player
var dx = x - player.x;
var dy = y - player.y;
var angle = Math.atan2(dy, dx) * 180 / Math.PI;
// Constrain angle for shooting upward only
if (angle > 0) {
angle = 0;
}
if (angle < -90) {
angle = -90;
}
aimingSystem.setAngle(angle);
}
};
// Game update loop
game.update = function () {
// Update game objects
if (basketball) {
basketball.update();
}
if (basketHoop) {
basketHoop.update();
}
if (aimingSystem) {
aimingSystem.update();
}
// Update defenders
for (var i = 0; i < defenders.length; i++) {
defenders[i].update();
}
};
// Initialize game
function initGame() {
// Initialize levels
initLevels();
// Setup UI
setupUI();
// Reset score
LK.setScore(0);
updateScoreText();
// Load first level
loadLevel(currentLevelIndex);
// Play background music
LK.playMusic('gameMusic');
}
// Start the game
initGame();