Code edit (1 edits merged)
Please save this source code
User prompt
3 tane ddaha spike ekle yukarı asağı hareket eden ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
bölüm 13 de spike da yukarı aşağı hızlıca hareket etsin ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Level 14 te walls yukarı aşağı hareket etsin potayla aynı hızda ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Bölüm 11 de topun sağa sola hareket mesafesini kısalt ve hızı arttır ↪💡 Consider importing and using the following plugins: @upit/tween.v1
Code edit (2 edits merged)
Please save this source code
User prompt
Level 11 de top sağa sola hareket etsin hızlıca ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
bölüm 10 da walls ve spike kalsın ama bölümü geçebilmek için ilk önce top sol duvara çarpsın eğer ilk başka bir kenara dokunuras fail olsun
User prompt
son promtu geri al
User prompt
level 10 da topun ilk sol kenardan sektirilerek baket atılmaı zorunlu olsun
User prompt
level 10 da top ve pota asagı yukarı hareket etsin ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
level 19 da 2 kere duvardan sekmesinide iste oyunculardan
Code edit (6 edits merged)
Please save this source code
User prompt
bölümlerde potanın altından basket atılması sayılmasın
User prompt
level 9 da fizik kurallarına uygun olsun ortadaki wall engeli sektirerek atılabilsin
User prompt
level 9 da wallsdan sekebilsin
Code edit (1 edits merged)
Please save this source code
User prompt
genellikle walls , spikes ve rotatingblocksları potaya yakın kordinatlara yerleştir , bölüm 10 dan sonra pota aşağı yukarı hareket etsin ve 10.bölümlerden sonra geçilen her bölümdde potanın aşağı yukarı hareket hızı kademe kademe artsın. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
bölümlerde spikes, rotatingblocks ve wallsları oorta nokta haricinde sabit kordinatlara koy random olmasın
User prompt
level 5 te top asağı yukarı hareket etsin ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
level 3 te potayı yukarı taşı
User prompt
level 2 de potayı yukarı taşı
User prompt
bütün bölümlerde sadece top ortada olsun diğer engelleri rastgele koy
Code edit (10 edits merged)
Please save this source code
User prompt
admin butonu ekle ekstradan önceki bölüme dönmek için
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Backboard = Container.expand(function () {
var self = Container.call(this);
var pole = self.attachAsset('backboardPole', {
anchorX: 0.5,
anchorY: 1
});
pole.x = 0;
pole.y = 0;
var board = self.attachAsset('backboard', {
anchorX: 0.5,
anchorY: 1
});
board.x = 0;
board.y = -50;
return self;
});
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.isLaunched = false;
self.gravity = 0.3;
self.bounceDecay = 0.8;
self.startX = 0;
self.startY = 0;
self.reset = function () {
self.x = self.startX;
self.y = self.startY;
self.velocityX = 0;
self.velocityY = 0;
self.isLaunched = false;
// Update shadow position if it exists
if (self.shadow) {
self.shadow.x = self.x;
self.shadow.y = self.y + 90;
}
};
self.launch = function (forceX, forceY) {
self.velocityX = forceX;
self.velocityY = forceY;
self.isLaunched = true;
LK.getSound('launch').play();
};
self.update = function () {
// Handle vertical movement when not launched (level 5)
if (!self.isLaunched && self.isMovingVertically) {
self.y += self.moveSpeed * self.moveDirection;
if (self.y <= self.minY || self.y >= self.maxY) {
self.moveDirection *= -1;
}
// Update shadow position during movement
if (self.shadow) {
self.shadow.y = self.y + 90;
}
// Update start position for reset
self.startY = self.y;
}
if (self.isLaunched) {
self.x += self.velocityX;
self.y += self.velocityY;
self.velocityY += self.gravity;
// Realistic physics for all four screen edges
var ballRadius = 80; // Half of basketball width/height
var energyLoss = 0.85; // Energy retained after bounce (realistic friction)
var minVelocity = 0.5; // Minimum velocity to prevent infinite micro-bounces
// Left wall collision
if (self.x <= ballRadius) {
self.x = ballRadius;
if (self.velocityX < 0) {
// Only bounce if moving toward wall
self.velocityX = -self.velocityX * energyLoss;
self.velocityY = self.velocityY * energyLoss; // Slight energy loss on Y too
if (Math.abs(self.velocityX) < minVelocity) {
self.velocityX = 0;
}
LK.getSound('bounce').play();
}
}
// Right wall collision
if (self.x >= 2048 - ballRadius) {
self.x = 2048 - ballRadius;
if (self.velocityX > 0) {
// Only bounce if moving toward wall
self.velocityX = -self.velocityX * energyLoss;
self.velocityY = self.velocityY * energyLoss; // Slight energy loss on Y too
if (Math.abs(self.velocityX) < minVelocity) {
self.velocityX = 0;
}
LK.getSound('bounce').play();
}
}
// Top wall collision
if (self.y <= ballRadius) {
self.y = ballRadius;
if (self.velocityY < 0) {
// Only bounce if moving toward wall
self.velocityY = -self.velocityY * energyLoss;
self.velocityX = self.velocityX * energyLoss; // Slight energy loss on X too
if (Math.abs(self.velocityY) < minVelocity) {
self.velocityY = 0;
}
LK.getSound('bounce').play();
}
}
// Bottom wall collision
if (self.y >= 2732 - ballRadius) {
self.y = 2732 - ballRadius;
if (self.velocityY > 0) {
// Only bounce if moving toward wall
self.velocityY = -self.velocityY * energyLoss;
self.velocityX = self.velocityX * energyLoss; // Slight energy loss on X too
if (Math.abs(self.velocityY) < minVelocity) {
self.velocityY = 0;
}
LK.getSound('bounce').play();
}
}
}
// Update shadow position if it exists
if (self.shadow) {
self.shadow.x = self.x;
self.shadow.y = self.y + 90;
}
};
return self;
});
var Hoop = Container.expand(function () {
var self = Container.call(this);
// Create backboard as slim vertical bar (only thickness visible) - flush mounted to wall
var backboard = self.attachAsset('wall', {
anchorX: 1.0,
// Right edge anchor for wall attachment
anchorY: 0.5,
scaleX: 0.2,
// Slightly thicker for better visibility
scaleY: 2.8,
// Much taller backboard extending well above rim
tint: 0xFFFFFF
});
backboard.x = 0; // Completely flush with wall position (no gap)
backboard.y = -80; // Positioned higher so it extends above rim
// Create support bracket connecting backboard to wall
var wallBracket = self.attachAsset('wall', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.3,
scaleY: 0.2,
tint: 0x808080
});
wallBracket.x = 12; // Closer to wall for flush mounting
wallBracket.y = -50;
// Create horizontal rim extending toward left (side view) - made extra wide
var rim = self.attachAsset('hoop', {
anchorX: 1.0,
// Right edge anchored to backboard
anchorY: 0.5,
scaleX: 2.4,
// Extended horizontally toward player - extra wide for very forgiving scoring
scaleY: 0.2,
// Thin rim profile from side
tint: 0xFF4500
});
rim.x = -2; // Closer to backboard for flush mounting
rim.y = -80;
// Create rim support bracket
var rimBracket = self.attachAsset('wall', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.6,
scaleY: 0.15,
tint: 0x808080
});
rimBracket.x = -35; // Adjusted for better alignment
rimBracket.y = -70;
// Create vertical backboard support line behind the rim - taller and thicker
var supportLine = self.attachAsset('backboardSupport', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
// Thicker for more visual solidity
scaleY: 2.0,
// Taller extending well above rim
tint: 0xC0C0C0,
alpha: 0.9
});
supportLine.x = -8; // Closer to backboard for flush mounting
supportLine.y = -130; // Positioned much higher above the rim, never dipping below
// Create realistic side-view net hanging from rim - positioned to hang naturally below
var netSegments = [];
for (var i = 0; i < 8; i++) {
var netSegment = self.attachAsset('netSegment', {
anchorX: 0.5,
anchorY: 0,
scaleX: 0.8 - i * 0.06,
// Tapers as it hangs down
scaleY: 1.0,
tint: 0xFFFFFF,
alpha: 0.7
});
netSegment.x = -140 + i * 10; // Spread across rim width
netSegment.y = -60 + i * 12; // Hangs lower below rim with more natural drape
netSegments.push(netSegment);
}
// Store references for animations
self.netSegments = netSegments;
self.rim = rim;
self.backboard = backboard;
self.supportLine = supportLine;
// Physics collision zones for realistic ball interaction - ultra forgiving
self.rimCollisionZone = {
x: -110,
// Extends even further left from backboard
y: -80,
width: 320,
// Ultra wide rim width for maximum forgiving scoring
height: 50 // Extra thick rim height for easiest scoring
};
self.backboardCollisionZone = {
x: 0,
// At wall position - completely flush
y: -80,
// Positioned higher to match visual backboard
width: 40,
// Thicker to match visual appearance
height: 420 // Much taller backboard height matching visual
};
// Vertical support line collision zone for soft redirection
self.supportLineCollisionZone = {
x: -8,
// Closer to backboard for flush mounting
y: -130,
// Positioned higher above rim
width: 12,
// Thicker support line width
height: 240 // Taller support line height extending well above rim
};
self.isMoving = false;
self.moveSpeed = 2;
self.moveDirection = 1;
self.minX = 1700;
self.maxX = 1900;
self.minY = 200;
self.maxY = 2000;
self.moveHorizontal = true;
self.update = function () {
if (self.isMoving) {
if (self.moveHorizontal) {
self.x += self.moveSpeed * self.moveDirection;
if (self.x <= self.minX || self.x >= self.maxX) {
self.moveDirection *= -1;
}
} else {
self.y += self.moveSpeed * self.moveDirection;
if (self.y <= self.minY || self.y >= self.maxY) {
self.moveDirection *= -1;
}
}
}
};
return self;
});
var RotatingBlock = Container.expand(function () {
var self = Container.call(this);
var blockGraphics = self.attachAsset('rotatingBlock', {
anchorX: 0.5,
anchorY: 0.5
});
self.rotationSpeed = 0.05;
self.update = function () {
blockGraphics.rotation += self.rotationSpeed;
};
return self;
});
var Spike = Container.expand(function () {
var self = Container.call(this);
var spikeGraphics = self.attachAsset('spike', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
var TrajectoryDot = Container.expand(function () {
var self = Container.call(this);
var dot = self.attachAsset('trajectory', {
anchorX: 0.5,
anchorY: 0.5
});
dot.alpha = 0.6;
return self;
});
var Wall = Container.expand(function () {
var self = Container.call(this);
var wallGraphics = self.attachAsset('wall', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x2F4F4F
});
/****
* Game Code
****/
// Game state
var currentLevel = storage.currentLevel || 1;
var maxLevel = storage.maxLevel || 1;
var gameState = 'playing'; // playing, levelComplete, gameOver
var attempts = 0;
var maxAttempts = 0;
var scored = false;
var levelGoal = '';
// Objective tracking
var wallBounces = 0;
var requiredBounces = 0;
var touchedWall = false;
var objectiveFailed = false;
var failureReason = '';
// Timer challenge system
var ballReleaseTime = 0;
var timerChallenge = false;
var timerCountdown = 0;
var inputDisabled = false;
var timerCountdownText = null;
// Global 3-second timer for all levels
var globalTimerActive = false;
var globalTimerStartTime = 0;
// Game objects
var basketball = null;
var hoop = null;
var walls = [];
var spikes = [];
var rotatingBlocks = [];
var trajectoryDots = [];
// Input tracking
var isDragging = false;
var dragStartX = 0;
var dragStartY = 0;
// Helper function to generate random positions for obstacles
function getRandomPosition() {
return {
x: Math.random() * 1200 + 300,
// Random x between 300 and 1500
y: Math.random() * 1000 + 800 // Random y between 800 and 1800
};
}
// Level definitions
var levels = [
// Level 1-4: Basic shots
{
ballX: 1024,
ballY: 1366,
hoopX: 1024,
hoopY: 1366,
goal: 'Make your first shot!',
objective: 'score',
// Just score
walls: [],
spikes: [],
rotatingBlocks: [],
movingHoop: false,
maxAttempts: 0
}, {
ballX: 1024,
ballY: 1366,
hoopX: 1024,
hoopY: 800,
goal: 'Higher target challenge!',
objective: 'score',
// Just score
walls: [],
spikes: [],
rotatingBlocks: [],
movingHoop: false,
maxAttempts: 0
}, {
ballX: 1024,
ballY: 1366,
hoopX: 1024,
hoopY: 800,
goal: 'Straight shot challenge!',
objective: 'no_wall',
// Don't touch any wall
walls: [],
spikes: [],
rotatingBlocks: [],
movingHoop: false,
maxAttempts: 0
}, {
ballX: 1024,
ballY: 1366,
hoopX: 1024,
hoopY: 1366,
goal: 'Bounce off one wall to score!',
objective: 'bounce_once',
// Must bounce off exactly one wall
walls: [],
spikes: [],
rotatingBlocks: [],
movingHoop: false,
maxAttempts: 0
},
// Level 5-10: Bank shots and obstacles
{
ballX: 1024,
ballY: 1366,
hoopX: 1024,
hoopY: 1366,
goal: 'Bounce off one wall!',
objective: 'bounce_once',
// Must bounce off exactly one wall
walls: [],
spikes: [],
rotatingBlocks: [],
movingHoop: false,
maxAttempts: 0
}, {
ballX: 1024,
ballY: 1366,
hoopX: 1024,
hoopY: 1366,
goal: 'Avoid the spikes!',
objective: 'no_wall',
// Don't touch any wall (spikes auto-fail)
walls: [],
spikes: [{
x: 1550,
y: 1100
}],
rotatingBlocks: [],
movingHoop: false,
maxAttempts: 0
}, {
ballX: 1024,
ballY: 1366,
hoopX: 1024,
hoopY: 1366,
goal: 'Navigate the rotating block!',
objective: 'score',
// Just score while avoiding blocks
walls: [],
spikes: [],
rotatingBlocks: [{
x: 1700,
y: 1050
}],
movingHoop: false,
maxAttempts: 0
}, {
ballX: 1024,
ballY: 1366,
hoopX: 1024,
hoopY: 1366,
goal: 'Avoid walls challenge!',
objective: 'no_wall',
// Don't touch any wall
walls: [{}, {
x: 1700,
y: 1100,
width: 30,
height: 200
}],
spikes: [],
rotatingBlocks: [],
movingHoop: false,
maxAttempts: 0
}, {
ballX: 1024,
ballY: 1366,
hoopX: 1024,
hoopY: 1366,
goal: 'Precision bank shot!',
walls: [{
x: 1600,
y: 800,
width: 400,
height: 30
}],
spikes: [{
x: 1650,
y: 1200
}],
rotatingBlocks: [],
movingHoop: false,
maxAttempts: 0
}, {
ballX: 1024,
ballY: 1366,
hoopX: 1024,
hoopY: 1366,
goal: 'Complex obstacle course!',
walls: [{
x: 1700,
y: 900,
width: 30,
height: 300
}],
spikes: [{
x: 1600,
y: 1200
}, {
x: 1800,
y: 950
}],
rotatingBlocks: [{
x: 1750,
y: 1100
}],
movingHoop: false,
maxAttempts: 0
},
// Level 11-19: Moving hoops and restrictions
{
ballX: 1024,
ballY: 1366,
hoopX: 1024,
hoopY: 1366,
goal: 'Hit the moving hoop!',
walls: [],
spikes: [],
rotatingBlocks: [],
movingHoop: true,
maxAttempts: 0
}, {
ballX: 1024,
ballY: 1366,
hoopX: 1024,
hoopY: 1366,
goal: 'Moving target with walls!',
walls: [{
x: 1700,
y: 1000,
width: 30,
height: 300
}],
spikes: [],
rotatingBlocks: [],
movingHoop: true,
maxAttempts: 0
}, {
ballX: 1024,
ballY: 1366,
hoopX: 1024,
hoopY: 1366,
goal: 'One shot only!',
walls: [],
spikes: [{
x: 1800,
y: 1200
}],
rotatingBlocks: [],
movingHoop: false,
maxAttempts: 1
}, {
ballX: 1024,
ballY: 1366,
hoopX: 1024,
hoopY: 1366,
goal: 'Moving hoop precision!',
walls: [{
x: 1600,
y: 1000,
width: 200,
height: 30
}],
spikes: [{
x: 1750,
y: 1150
}],
rotatingBlocks: [],
movingHoop: true,
maxAttempts: 3
}, {
ballX: 1024,
ballY: 1366,
hoopX: 1024,
hoopY: 1366,
goal: 'Vertical moving hoop!',
walls: [{
x: 1600,
y: 900,
width: 30,
height: 400
}, {
x: 1800,
y: 1100,
width: 30,
height: 400
}],
spikes: [],
rotatingBlocks: [],
movingHoop: true,
maxAttempts: 0
}, {
ballX: 1024,
ballY: 1366,
hoopX: 1024,
hoopY: 1366,
goal: 'Ultimate obstacle course!',
walls: [{
x: 1700,
y: 1000,
width: 30,
height: 200
}, {
x: 1800,
y: 800,
width: 200,
height: 30
}],
spikes: [{
x: 1650,
y: 1200
}, {
x: 1850,
y: 950
}],
rotatingBlocks: [{
x: 1750,
y: 1100
}],
movingHoop: false,
maxAttempts: 2
}, {
ballX: 1024,
ballY: 1366,
hoopX: 1024,
hoopY: 1366,
goal: 'Moving maze challenge!',
walls: [{
x: 1650,
y: 900,
width: 30,
height: 300
}, {
x: 1850,
y: 1000,
width: 30,
height: 300
}],
spikes: [{
x: 1700,
y: 1150
}, {
x: 1800,
y: 1050
}],
rotatingBlocks: [{
x: 1750,
y: 1200
}],
movingHoop: true,
maxAttempts: 3
}, {
ballX: 1024,
ballY: 1366,
hoopX: 1024,
hoopY: 1366,
goal: 'Narrow passage challenge!',
walls: [{
x: 1700,
y: 800,
width: 30,
height: 600
}, {
x: 1800,
y: 900,
width: 30,
height: 600
}],
spikes: [{
x: 1650,
y: 1100
}, {
x: 1850,
y: 1200
}],
rotatingBlocks: [],
movingHoop: false,
maxAttempts: 1
}, {
ballX: 1024,
ballY: 1366,
hoopX: 1024,
hoopY: 1366,
goal: 'Complex moving target!',
walls: [{
x: 1600,
y: 900,
width: 200,
height: 30
}, {
x: 1700,
y: 1100,
width: 200,
height: 30
}],
spikes: [{
x: 1750,
y: 1200
}, {
x: 1800,
y: 1000
}],
rotatingBlocks: [{
x: 1850,
y: 1050
}],
movingHoop: true,
maxAttempts: 2
},
// Level 20: Ultimate challenge
{
ballX: 1024,
ballY: 1366,
hoopX: 1024,
hoopY: 1366,
goal: 'Ultimate Basket Challenge!',
walls: [{
x: 1600,
y: 800,
width: 30,
height: 400
}, {
x: 1700,
y: 1000,
width: 200,
height: 30
}, {
x: 1750,
y: 900,
width: 200,
height: 30
}, {
x: 1850,
y: 1100,
width: 30,
height: 400
}],
spikes: [{
x: 1650,
y: 1200
}, {
x: 1750,
y: 1050
}, {
x: 1800,
y: 1250
}],
rotatingBlocks: [{
x: 1700,
y: 1100
}, {
x: 1850,
y: 950
}],
movingHoop: true,
maxAttempts: 1
}];
// UI elements
var levelText = new Text2('Level 1', {
size: 60,
fill: 0xFFFFFF
});
levelText.anchor.set(0.5, 0);
LK.gui.top.addChild(levelText);
var goalText = new Text2('', {
size: 40,
fill: 0xFFFF00
});
goalText.anchor.set(0.5, 0);
goalText.y = 80;
LK.gui.top.addChild(goalText);
var attemptsText = new Text2('', {
size: 35,
fill: 0xFFFFFF
});
attemptsText.anchor.set(1, 0);
LK.gui.topRight.addChild(attemptsText);
// Timer countdown text
timerCountdownText = new Text2('', {
size: 80,
fill: 0xFF0000
});
timerCountdownText.anchor.set(0.5, 0.5);
LK.gui.center.addChild(timerCountdownText);
timerCountdownText.visible = false;
// Next Level button
var nextLevelButton = new Text2('Next Level →', {
size: 60,
fill: 0x00FF00
});
nextLevelButton.anchor.set(0.5, 0.5);
nextLevelButton.x = 1024;
nextLevelButton.y = 2200;
nextLevelButton.visible = false;
game.addChild(nextLevelButton);
// Add button click functionality
nextLevelButton.down = function (x, y, obj) {
if (nextLevelButton.visible && gameState === 'levelComplete') {
// Hide button
nextLevelButton.visible = false;
// Proceed to next level
if (currentLevel < levels.length) {
currentLevel++;
if (currentLevel > maxLevel) {
maxLevel = currentLevel;
storage.maxLevel = maxLevel;
}
storage.currentLevel = currentLevel;
initializeLevel(currentLevel);
} else {
LK.showYouWin();
}
}
};
// Admin Next Level button for testing
var adminNextButton = new Text2('ADMIN: Next →', {
size: 40,
fill: 0xFF00FF
});
adminNextButton.anchor.set(1, 0);
adminNextButton.x = -20;
adminNextButton.y = 120;
adminNextButton.visible = true;
LK.gui.topRight.addChild(adminNextButton);
// Admin Previous Level button for testing
var adminPrevButton = new Text2('ADMIN: ← Prev', {
size: 40,
fill: 0xFF00FF
});
adminPrevButton.anchor.set(1, 0);
adminPrevButton.x = -20;
adminPrevButton.y = 170;
adminPrevButton.visible = true;
LK.gui.topRight.addChild(adminPrevButton);
// Admin next button click functionality
adminNextButton.down = function (x, y, obj) {
// Always allow advancing to next level for testing
if (currentLevel < levels.length) {
currentLevel++;
if (currentLevel > maxLevel) {
maxLevel = currentLevel;
storage.maxLevel = maxLevel;
}
storage.currentLevel = currentLevel;
initializeLevel(currentLevel);
} else {
// Reset to level 1 if at the end
currentLevel = 1;
storage.currentLevel = 1;
initializeLevel(currentLevel);
}
};
// Admin previous button click functionality
adminPrevButton.down = function (x, y, obj) {
// Always allow going back to previous level for testing
if (currentLevel > 1) {
currentLevel--;
storage.currentLevel = currentLevel;
initializeLevel(currentLevel);
} else {
// Go to last level if at the beginning
currentLevel = levels.length;
storage.currentLevel = currentLevel;
initializeLevel(currentLevel);
}
};
function initializeLevel(levelNum) {
if (levelNum > levels.length || levelNum < 1) {
return;
}
// Clear existing objects
if (basketball) {
if (basketball.shadow) {
basketball.shadow.destroy();
}
basketball.destroy();
}
if (hoop) {
hoop.destroy();
}
walls.forEach(function (wall) {
wall.destroy();
});
spikes.forEach(function (spike) {
spike.destroy();
});
rotatingBlocks.forEach(function (block) {
block.destroy();
});
trajectoryDots.forEach(function (dot) {
dot.destroy();
});
walls = [];
spikes = [];
rotatingBlocks = [];
trajectoryDots = [];
var level = levels[levelNum - 1];
// Create basketball
basketball = game.addChild(new Basketball());
basketball.x = level.ballX;
basketball.y = level.ballY;
basketball.startX = level.ballX;
basketball.startY = level.ballY;
// Add vertical movement for level 5
if (levelNum === 5) {
basketball.isMovingVertically = true;
basketball.moveDirection = 1;
basketball.moveSpeed = 3;
basketball.minY = 1000;
basketball.maxY = 1700;
}
// Add shadow ring under basketball to indicate it's draggable
var shadow = LK.getAsset('basketball', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 0.3,
tint: 0x000000,
alpha: 0.3
});
shadow.x = basketball.x;
shadow.y = basketball.y + 90; // Position below ball
game.addChild(shadow);
basketball.shadow = shadow;
// Create hoop
hoop = game.addChild(new Hoop());
hoop.x = 2048; // Always position flush against rightmost wall
hoop.y = level.hoopY;
hoop.isMoving = level.movingHoop;
// Configure movement for levels after 10
if (levelNum > 10) {
hoop.isMoving = true;
hoop.moveHorizontal = false; // Always vertical movement after level 10
hoop.minY = 300;
hoop.maxY = 1500;
// Gradually increase speed based on level
var speedMultiplier = Math.max(1, (levelNum - 10) * 0.5); // Increase by 0.5 each level after 10
hoop.moveSpeed = 2 + speedMultiplier;
}
// Configure vertical movement for level 17 (keeping original logic for specific level)
if (levelNum === 17) {
hoop.moveHorizontal = false;
hoop.minY = 300;
hoop.maxY = 1500;
hoop.moveSpeed = 3;
}
// Create walls
level.walls.forEach(function (wallData) {
var wall = game.addChild(new Wall());
wall.x = wallData.x;
wall.y = wallData.y;
if (wallData.width) {
wall.scaleX = wallData.width / 200;
}
if (wallData.height) {
wall.scaleY = wallData.height / 30;
}
walls.push(wall);
});
// Create spikes
level.spikes.forEach(function (spikeData) {
var spike = game.addChild(new Spike());
spike.x = spikeData.x;
spike.y = spikeData.y;
spikes.push(spike);
});
// Create rotating blocks
level.rotatingBlocks.forEach(function (blockData) {
var block = game.addChild(new RotatingBlock());
block.x = blockData.x;
block.y = blockData.y;
rotatingBlocks.push(block);
});
// Reset game state
gameState = 'playing';
attempts = 0;
maxAttempts = level.maxAttempts;
scored = false;
levelGoal = level.goal;
// Reset objective tracking
wallBounces = 0;
requiredBounces = level.objective === 'bounce_once' ? 1 : 0;
touchedWall = false;
objectiveFailed = false;
failureReason = '';
// Reset timer challenge variables
timerChallenge = level.objective === 'timer_challenge';
ballReleaseTime = 0;
timerCountdown = 0;
inputDisabled = false;
globalTimerActive = false;
globalTimerStartTime = 0;
if (timerCountdownText) {
timerCountdownText.visible = false;
}
// Hide Next Level button
if (nextLevelButton) {
nextLevelButton.visible = false;
}
// Update UI
levelText.setText('Level ' + levelNum);
goalText.setText(levelGoal);
updateAttemptsText();
}
function updateAttemptsText() {
if (maxAttempts > 0) {
attemptsText.setText('Attempts: ' + attempts + '/' + maxAttempts);
} else {
attemptsText.setText('Attempts: ' + attempts);
}
}
function checkCollisions() {
if (!basketball.isLaunched) {
return;
}
// Check wall collisions (level walls)
walls.forEach(function (wall) {
if (basketball.intersects(wall)) {
var ballCenterX = basketball.x;
var ballCenterY = basketball.y;
var wallCenterX = wall.x;
var wallCenterY = wall.y;
var wallWidth = wall.width * wall.scaleX;
var wallHeight = wall.height * wall.scaleY;
var overlapX = Math.abs(ballCenterX - wallCenterX) - (80 + wallWidth / 2);
var overlapY = Math.abs(ballCenterY - wallCenterY) - (80 + wallHeight / 2);
// Realistic physics collision with angle-based reflection
// For level 9, use better bouncing physics with proper angle calculation
var energyLoss = currentLevel === 9 ? 0.85 : 0.85;
var minVelocity = 0.5;
// Calculate collision normal for proper physics reflection
var dx = ballCenterX - wallCenterX;
var dy = ballCenterY - wallCenterY;
var distance = Math.sqrt(dx * dx + dy * dy);
if (currentLevel === 9 && distance > 0) {
// Level 9: Use proper physics reflection based on collision angle
var normalX = dx / distance;
var normalY = dy / distance;
// Calculate relative velocity
var relativeVelocityX = basketball.velocityX;
var relativeVelocityY = basketball.velocityY;
// Calculate velocity component along normal
var velocityAlongNormal = relativeVelocityX * normalX + relativeVelocityY * normalY;
// Only reflect if moving toward the wall
if (velocityAlongNormal < 0) {
// Calculate reflection with proper physics
basketball.velocityX = basketball.velocityX - 2 * velocityAlongNormal * normalX;
basketball.velocityY = basketball.velocityY - 2 * velocityAlongNormal * normalY;
// Apply energy loss
basketball.velocityX *= energyLoss;
basketball.velocityY *= energyLoss;
// Separate ball from wall to prevent sticking
var separation = 85; // Ball radius + small margin
basketball.x = wallCenterX + normalX * separation;
basketball.y = wallCenterY + normalY * separation;
}
} else {
// Other levels: Use original collision logic
if (overlapX < overlapY) {
if (ballCenterX < wallCenterX && basketball.velocityX > 0 || ballCenterX > wallCenterX && basketball.velocityX < 0) {
basketball.velocityX = -basketball.velocityX * energyLoss;
basketball.velocityY = basketball.velocityY * energyLoss; // Apply energy loss to perpendicular component too
if (Math.abs(basketball.velocityX) < minVelocity) {
basketball.velocityX = 0;
}
}
basketball.x += ballCenterX < wallCenterX ? -5 : 5;
} else {
if (ballCenterY < wallCenterY && basketball.velocityY > 0 || ballCenterY > wallCenterY && basketball.velocityY < 0) {
basketball.velocityY = -basketball.velocityY * energyLoss;
basketball.velocityX = basketball.velocityX * energyLoss; // Apply energy loss to perpendicular component too
if (Math.abs(basketball.velocityY) < minVelocity) {
basketball.velocityY = 0;
}
}
basketball.y += ballCenterY < wallCenterY ? -5 : 5;
}
}
LK.getSound('bounce').play();
// Track wall bounces and check objectives
wallBounces++;
touchedWall = true;
var level = levels[currentLevel - 1];
if (level.objective === 'no_wall') {
objectiveFailed = true;
failureReason = 'You touched the wall!';
}
}
});
// Check spike collisions
spikes.forEach(function (spike) {
if (basketball.intersects(spike)) {
objectiveFailed = true;
failureReason = 'You hit the spikes!';
}
});
// Check rotating block collisions
rotatingBlocks.forEach(function (block) {
if (basketball.intersects(block)) {
var ballCenterX = basketball.x;
var ballCenterY = basketball.y;
var blockCenterX = block.x;
var blockCenterY = block.y;
var overlapX = Math.abs(ballCenterX - blockCenterX) - 120;
var overlapY = Math.abs(ballCenterY - blockCenterY) - 120;
if (overlapX < overlapY) {
basketball.velocityX = -basketball.velocityX * basketball.bounceDecay;
basketball.x += ballCenterX < blockCenterX ? -5 : 5;
} else {
basketball.velocityY = -basketball.velocityY * basketball.bounceDecay;
basketball.y += ballCenterY < blockCenterY ? -5 : 5;
}
LK.getSound('bounce').play();
}
});
// Check backboard collision for realistic physics
var ballCenterX = basketball.x;
var ballCenterY = basketball.y;
var hoopCenterX = hoop.x + hoop.backboardCollisionZone.x;
var hoopCenterY = hoop.y + hoop.backboardCollisionZone.y;
if (Math.abs(ballCenterX - hoopCenterX) < 80 + hoop.backboardCollisionZone.width / 2 && Math.abs(ballCenterY - hoopCenterY) < 80 + hoop.backboardCollisionZone.height / 2) {
// Ball hit backboard
basketball.velocityX = -basketball.velocityX * basketball.bounceDecay * 0.9;
basketball.velocityY = basketball.velocityY * 0.95;
basketball.x += basketball.velocityX > 0 ? -8 : 8;
LK.getSound('bounce').play();
// Flash backboard
LK.effects.flashObject(hoop, 0xFFFFFF, 200);
}
// Check vertical support line collision for soft redirection helping
var supportLineCenterX = hoop.x + hoop.supportLineCollisionZone.x;
var supportLineCenterY = hoop.y + hoop.supportLineCollisionZone.y;
if (Math.abs(ballCenterX - supportLineCenterX) < 80 + hoop.supportLineCollisionZone.width / 2 && Math.abs(ballCenterY - supportLineCenterY) < 80 + hoop.supportLineCollisionZone.height / 2) {
// Ball hit vertical support line - apply soft downward redirection instead of hard bounce
var distanceFromRim = Math.abs(ballCenterY - (hoop.y + hoop.rimCollisionZone.y));
var redirectionStrength = Math.max(0.1, 1.0 - distanceFromRim / 80); // Stronger redirection when closer to rim
// Gently redirect ball downward into the hoop if shot is close
if (basketball.velocityX < 0) {
// Ball moving toward hoop from right side
var rimCenterX = hoop.x + hoop.rimCollisionZone.x;
var rimCenterY = hoop.y + hoop.rimCollisionZone.y;
var directionToRimX = rimCenterX - ballCenterX;
var directionToRimY = rimCenterY - ballCenterY;
// Apply gentle redirection toward rim center
basketball.velocityX += directionToRimX * 0.003 * redirectionStrength;
basketball.velocityY += Math.abs(directionToRimY) * 0.002 * redirectionStrength; // Gentle downward push
// Reduce horizontal velocity to prevent harsh deflection
basketball.velocityX *= 0.85;
// Small position adjustment to prevent getting stuck
basketball.x -= 3;
// Subtle flash effect for support line hit
LK.effects.flashObject(hoop.supportLine, 0xFFFFAA, 150);
}
}
// Check rim collision for realistic bouncing - extra forgiving scoring zone
var rimCenterX = hoop.x + hoop.rimCollisionZone.x;
var rimCenterY = hoop.y + hoop.rimCollisionZone.y;
if (Math.abs(ballCenterX - rimCenterX) < 80 + hoop.rimCollisionZone.width / 2 && Math.abs(ballCenterY - rimCenterY) < 80 + hoop.rimCollisionZone.height / 2) {
// Check if ball is going through the hoop (scoring) - ultra wide scoring zone with auto-absorption
var scoringZoneWidth = 140; // Even wider invisible scoring zone for maximum forgiveness
var scoringZoneHeight = 40; // Taller scoring zone for easier front rim entry
var autoAbsorptionZone = 80; // Larger zone where ball gets gently pulled toward center
// Auto-absorption: if ball is close to center, gently guide it inward
var distanceFromCenterX = Math.abs(ballCenterX - rimCenterX);
var distanceFromCenterY = Math.abs(ballCenterY - rimCenterY);
if (distanceFromCenterX < autoAbsorptionZone && distanceFromCenterY < autoAbsorptionZone && basketball.velocityY > 0) {
// Gently pull ball toward center for easier scoring
var pullStrength = 0.15; // Subtle pull force
if (ballCenterX < rimCenterX) {
basketball.velocityX += pullStrength;
} else if (ballCenterX > rimCenterX) {
basketball.velocityX -= pullStrength;
}
}
if (basketball.velocityY > 0 && ballCenterX > rimCenterX - scoringZoneWidth && ballCenterX < rimCenterX + scoringZoneWidth && ballCenterY > rimCenterY - scoringZoneHeight && ballCenterY < rimCenterY + scoringZoneHeight) {
// Check if objective was met before scoring
var level = levels[currentLevel - 1];
var objectiveMet = true;
var objectiveFailureMessage = '';
if (level.objective === 'bounce_once' && wallBounces !== 1) {
objectiveMet = false;
objectiveFailureMessage = wallBounces === 0 ? 'You must bounce off one wall!' : 'You bounced too many times!';
} else if (level.objective === 'no_wall' && touchedWall) {
objectiveMet = false;
objectiveFailureMessage = 'You were not supposed to touch any walls!';
}
if (!objectiveMet) {
// Objective failed
LK.effects.flashScreen(0xff0000, 1000);
LK.setTimeout(function () {
currentLevel = 1;
storage.currentLevel = 1;
LK.showGameOver();
}, 2000);
return; // Don't continue with scoring
}
// Score!
scored = true;
LK.getSound('score').play();
// Check if it's a clean swish (ball passes through center of rim)
var distanceFromCenter = Math.abs(ballCenterX - rimCenterX);
var isCleanShot = distanceFromCenter < 30; // Within 30 pixels of center
if (isCleanShot) {
// Clean swish - special effects
LK.effects.flashObject(hoop, 0x00FFFF, 800); // Cyan flash for perfect shot
// Slow down ball slightly for dramatic effect
basketball.velocityX *= 0.8;
basketball.velocityY *= 0.9;
} else {
// Regular score
LK.effects.flashObject(hoop, 0x00FF00, 500);
}
// Let global timer handle level progression for all levels
// Animate net swish effect with enhanced 3D movement
if (hoop.netSegments) {
hoop.netSegments.forEach(function (segment, index) {
var swishIntensity = isCleanShot ? 1.5 : 1.0; // More dramatic swish for clean shots
// First phase: compress and sway
tween(segment, {
scaleY: 0.05 * swishIntensity,
alpha: 0.2,
rotation: segment.rotation + 0.3 * swishIntensity,
y: segment.y + 15 // Pull net segments down more
}, {
duration: isCleanShot ? 200 : 150,
easing: tween.easeOut
});
LK.setTimeout(function () {
// Second phase: bounce back with realistic sway
tween(segment, {
scaleY: 0.15,
alpha: 0.8,
rotation: segment.rotation - 0.1 * swishIntensity,
y: segment.y - 10 // Natural bounce back position
}, {
duration: isCleanShot ? 600 : 400,
easing: tween.bounceOut
});
}, index * (isCleanShot ? 40 : 30));
});
}
// Level complete - show success display and Next Level button
gameState = 'levelComplete';
// Show immediate success feedback
if (timerCountdownText) {
timerCountdownText.setText('Level Complete!');
timerCountdownText.tint = 0x00FF00;
timerCountdownText.visible = true;
timerCountdownText.scaleX = 1.5;
timerCountdownText.scaleY = 1.5;
// Animate success text
tween(timerCountdownText, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 300,
easing: tween.bounceOut
});
}
// Show Next Level button
nextLevelButton.visible = true;
tween(nextLevelButton, {
scaleX: 1.2,
scaleY: 1.2,
alpha: 1
}, {
duration: 500,
easing: tween.bounceOut
});
// Lock input immediately upon scoring
inputDisabled = true;
} else {
// Ball hit rim but didn't score - ultra soft rim bounce for maximum forgiveness
var overlapX = Math.abs(ballCenterX - rimCenterX) - (80 + hoop.rimCollisionZone.width / 2);
var overlapY = Math.abs(ballCenterY - rimCenterY) - (80 + hoop.rimCollisionZone.height / 2);
// Ultra soft bounces with minimal energy loss for maximum forgiveness
var softBounceDecay = 0.2; // Even softer bounce - less harsh rejection
var energyLoss = 0.9; // Higher energy retention - keep more momentum
var distanceFromCenter = Math.abs(ballCenterX - rimCenterX);
var distanceFromFrontEdge = Math.abs(ballCenterX - (rimCenterX - hoop.rimCollisionZone.width / 2));
// Make front edge (facing player) extremely forgiving
if (distanceFromFrontEdge < 30 && basketball.velocityX < 0) {
// Front edge hit - minimal bounce with downward guidance
softBounceDecay = 0.1;
energyLoss = 0.95;
// Add slight downward velocity to help ball drop into hoop
basketball.velocityY += 0.5;
} else if (distanceFromCenter < 50) {
// Very close to center - barely bounce at all
softBounceDecay = 0.15;
energyLoss = 0.92;
} else if (distanceFromCenter < 100) {
// Moderately close - gentle bounce
softBounceDecay = 0.25;
energyLoss = 0.88;
}
if (overlapX < overlapY) {
basketball.velocityX = -basketball.velocityX * softBounceDecay;
basketball.velocityY = basketball.velocityY * energyLoss; // Keep more perpendicular velocity
basketball.x += ballCenterX < rimCenterX ? -1 : 1; // Minimal position adjustment
} else {
basketball.velocityY = -basketball.velocityY * softBounceDecay;
basketball.velocityX = basketball.velocityX * energyLoss; // Keep more perpendicular velocity
basketball.y += ballCenterY < rimCenterY ? -1 : 1; // Minimal position adjustment
}
LK.getSound('bounce').play();
// Flash rim on hit with gentler color
LK.effects.flashObject(hoop.rim, 0xFFAA00, 300);
}
}
}
function resetBall() {
basketball.reset();
clearTrajectory();
clearPowerIndicators();
gameState = 'playing';
// Reset objective tracking for new attempt
wallBounces = 0;
touchedWall = false;
objectiveFailed = false;
failureReason = '';
// Reset timer challenge variables
ballReleaseTime = 0;
timerCountdown = 0;
inputDisabled = false;
globalTimerActive = false;
globalTimerStartTime = 0;
if (timerCountdownText) {
timerCountdownText.visible = false;
}
// Hide Next Level button
if (nextLevelButton) {
nextLevelButton.visible = false;
}
}
function showTrajectory(forceX, forceY) {
clearTrajectory();
var simX = basketball.x;
var simY = basketball.y;
var simVelX = forceX;
var simVelY = forceY;
var simGravity = 0.3;
var forceMagnitude = Math.sqrt(forceX * forceX + forceY * forceY);
var powerPercent = Math.min(forceMagnitude / 40, 1.0);
for (var i = 0; i < 30; i++) {
simX += simVelX;
simY += simVelY;
simVelY += simGravity;
if (i % 3 === 0) {
var dot = game.addChild(new TrajectoryDot());
dot.x = simX;
dot.y = simY;
// Scale dots based on power level
dot.scaleX = 0.5 + powerPercent * 0.8;
dot.scaleY = 0.5 + powerPercent * 0.8;
// Color dots based on power
dot.tint = powerPercent > 0.8 ? 0xff0000 : powerPercent > 0.5 ? 0xffff00 : 0x00ff00;
trajectoryDots.push(dot);
}
if (simX <= 80 || simX >= 1968 || simY <= 80) {
break;
}
}
}
function clearTrajectory() {
trajectoryDots.forEach(function (dot) {
dot.destroy();
});
trajectoryDots = [];
}
function updatePowerIndicators(currentX, currentY, dragDistance, forceMagnitude) {
// Clear existing indicators
clearPowerIndicators();
// Calculate power percentage (0 to 1)
var powerPercent = Math.min(forceMagnitude / 40, 1.0);
// Create aim line from ball to current drag position
var aimLineAsset = LK.getAsset('trajectory', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: Math.max(1, dragDistance / 8),
scaleY: Math.max(2, 2 + powerPercent * 3)
});
aimLineAsset.x = (basketball.x + currentX) / 2;
aimLineAsset.y = (basketball.y + currentY) / 2;
aimLineAsset.rotation = Math.atan2(currentY - basketball.y, currentX - basketball.x);
aimLineAsset.alpha = 0.8;
aimLineAsset.tint = powerPercent > 0.8 ? 0xff0000 : powerPercent > 0.5 ? 0xffff00 : 0x00ff00;
game.addChild(aimLineAsset);
aimLine = aimLineAsset;
// Create power bar indicator
var powerBarWidth = 300;
var powerBarHeight = 20;
var powerBarX = basketball.x - powerBarWidth / 2;
var powerBarY = basketball.y - 120;
// Power bar background
var powerBarBg = LK.getAsset('wall', {
anchorX: 0,
anchorY: 0,
scaleX: powerBarWidth / 200,
scaleY: powerBarHeight / 30,
tint: 0x333333
});
powerBarBg.x = powerBarX;
powerBarBg.y = powerBarY;
powerBarBg.alpha = 0.7;
game.addChild(powerBarBg);
// Power bar fill
var fillWidth = powerBarWidth * powerPercent;
if (fillWidth > 0) {
var powerBarFill = LK.getAsset('wall', {
anchorX: 0,
anchorY: 0,
scaleX: fillWidth / 200,
scaleY: powerBarHeight / 30,
tint: powerPercent > 0.8 ? 0xff0000 : powerPercent > 0.5 ? 0xffff00 : 0x00ff00
});
powerBarFill.x = powerBarX;
powerBarFill.y = powerBarY;
powerBarFill.alpha = 0.9;
game.addChild(powerBarFill);
powerBar = [powerBarBg, powerBarFill];
} else {
powerBar = [powerBarBg];
}
}
function clearPowerIndicators() {
if (aimLine) {
aimLine.destroy();
aimLine = null;
}
if (powerBar) {
powerBar.forEach(function (bar) {
bar.destroy();
});
powerBar = null;
}
}
// Power indicator variables
var powerBar = null;
var aimLine = null;
var maxDragDistance = 400; // Maximum pixel distance for full power
// Input handling
game.down = function (x, y, obj) {
if (gameState !== 'playing' || basketball.isLaunched || inputDisabled) {
return;
}
isDragging = true;
dragStartX = x;
dragStartY = y;
};
game.move = function (x, y, obj) {
if (!isDragging || gameState !== 'playing' || basketball.isLaunched || inputDisabled) {
return;
}
// Calculate drag distance and force with increased sensitivity
var dragX = dragStartX - x;
var dragY = dragStartY - y;
var dragDistance = Math.sqrt(dragX * dragX + dragY * dragY);
var dragMultiplier = Math.min(dragDistance / maxDragDistance, 1.0);
var forceX = dragX * 0.08 * dragMultiplier;
var forceY = dragY * 0.08 * dragMultiplier;
// Limit force to maximum power
var magnitude = Math.sqrt(forceX * forceX + forceY * forceY);
if (magnitude > 40) {
forceX = forceX / magnitude * 40;
forceY = forceY / magnitude * 40;
}
// Update visual feedback
updatePowerIndicators(x, y, dragDistance, magnitude);
showTrajectory(forceX, forceY);
};
game.up = function (x, y, obj) {
if (!isDragging || gameState !== 'playing' || basketball.isLaunched || inputDisabled) {
return;
}
isDragging = false;
// Calculate final force with increased sensitivity
var dragX = dragStartX - x;
var dragY = dragStartY - y;
var dragDistance = Math.sqrt(dragX * dragX + dragY * dragY);
var dragMultiplier = Math.min(dragDistance / maxDragDistance, 1.0);
var forceX = dragX * 0.08 * dragMultiplier;
var forceY = dragY * 0.08 * dragMultiplier;
// Limit force to maximum power
var magnitude = Math.sqrt(forceX * forceX + forceY * forceY);
if (magnitude > 40) {
forceX = forceX / magnitude * 40;
forceY = forceY / magnitude * 40;
}
// Clear visual indicators
clearPowerIndicators();
if (magnitude > 1) {
attempts++;
updateAttemptsText();
basketball.launch(forceX, forceY);
clearTrajectory();
// Start global 3-second timer for all levels
ballReleaseTime = LK.ticks;
globalTimerActive = true;
globalTimerStartTime = LK.ticks;
inputDisabled = true;
timerCountdownText.visible = true;
// Check if out of attempts
if (maxAttempts > 0 && attempts >= maxAttempts && !scored) {
LK.setTimeout(function () {
if (!scored) {
currentLevel = 1;
storage.currentLevel = 1;
LK.showGameOver();
}
}, 3000);
}
}
};
// Main update loop
game.update = function () {
if (gameState === 'playing') {
// Handle global 3-second timer countdown for all levels
if (globalTimerActive && globalTimerStartTime > 0) {
var timeSinceRelease = (LK.ticks - globalTimerStartTime) * (1000 / 60); // Convert to milliseconds
var remainingTime = Math.max(0, 3000 - timeSinceRelease);
var secondsLeft = Math.ceil(remainingTime / 1000);
if (timerCountdownText) {
timerCountdownText.setText(secondsLeft.toString());
timerCountdownText.visible = true;
timerCountdownText.tint = 0xFFFFFF; // Reset tint to white
// Animate countdown text
var scale = 1.0 + Math.sin(LK.ticks * 0.3) * 0.2;
timerCountdownText.scaleX = scale;
timerCountdownText.scaleY = scale;
}
// Check if 3 seconds have passed - end level regardless of success/failure
if (remainingTime <= 0) {
globalTimerActive = false;
var level = levels[currentLevel - 1];
var wasSuccessful = scored;
// Check objective completion
if (level.objective === 'bounce_once' && wallBounces !== 1) {
wasSuccessful = false;
} else if (level.objective === 'no_wall' && touchedWall) {
wasSuccessful = false;
} else if (level.objective === 'timer_challenge') {
// For timer challenge, must score within 1 second
var timeSinceReleaseForTimer = (LK.ticks - ballReleaseTime) * (1000 / 60);
if (!scored || timeSinceReleaseForTimer > 1000) {
wasSuccessful = false;
}
}
if (wasSuccessful) {
// Flash screen with success color
LK.effects.flashScreen(0x00FF00, 500);
// Add sparkle effect to hoop
if (hoop) {
tween(hoop, {
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 200,
easing: tween.easeOut
});
LK.setTimeout(function () {
tween(hoop, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 300,
easing: tween.bounceOut
});
}, 200);
}
// Hide timer countdown as level is complete
if (timerCountdownText) {
timerCountdownText.visible = false;
}
// Lock input but don't auto-progress - wait for button click
inputDisabled = true;
} else {
// Level failed
if (timerCountdownText) {
timerCountdownText.setText('FAIL');
timerCountdownText.tint = 0xFF0000;
}
LK.effects.flashScreen(0xff0000, 1000);
LK.setTimeout(function () {
currentLevel = 1;
storage.currentLevel = 1;
LK.showGameOver();
}, 2000);
}
return;
}
}
// Track screen edge bounces for objectives (before calling checkCollisions)
if (basketball.isLaunched) {
var ballRadius = 80;
var lastWallBounces = wallBounces;
// Check if ball just bounced off any screen edge this frame
if (basketball.x <= ballRadius && basketball.velocityX > 0 || basketball.x >= 2048 - ballRadius && basketball.velocityX < 0 || basketball.y <= ballRadius && basketball.velocityY > 0 || basketball.y >= 2732 - ballRadius && basketball.velocityY < 0) {
// Ball just bounced off a screen edge
if (wallBounces === lastWallBounces) {
// Prevent double counting
wallBounces++;
touchedWall = true;
var level = levels[currentLevel - 1];
if (level.objective === 'no_wall') {
objectiveFailed = true;
failureReason = 'You touched the wall!';
}
}
}
}
checkCollisions();
// Check for objective failures
if (objectiveFailed && !scored) {
LK.effects.flashScreen(0xff0000, 1000);
LK.setTimeout(function () {
currentLevel = 1;
storage.currentLevel = 1;
LK.showGameOver();
}, 2000);
return;
}
// Check if ball is off screen (reset)
if (basketball.isLaunched && basketball.y > 2800) {
if (!scored) {
// Ball missed - check if this constitutes objective failure
var level = levels[currentLevel - 1];
var missedObjectiveMessage = 'Missed the hoop!';
if (level.objective === 'bounce_once' && wallBounces !== 1) {
missedObjectiveMessage = wallBounces === 0 ? 'You must bounce off one wall!' : 'You bounced too many times!';
} else if (level.objective === 'no_wall' && touchedWall) {
missedObjectiveMessage = 'You touched the wall!';
}
LK.effects.flashScreen(0xff0000, 1000);
LK.setTimeout(function () {
currentLevel = 1;
storage.currentLevel = 1;
LK.showGameOver();
}, 2000);
} else if (maxAttempts > 0 && attempts >= maxAttempts) {
currentLevel = 1;
storage.currentLevel = 1;
LK.showGameOver();
} else {
resetBall();
}
}
}
};
// Initialize first level
initializeLevel(currentLevel); ===================================================================
--- original.js
+++ change.js
@@ -1044,29 +1044,58 @@
var wallHeight = wall.height * wall.scaleY;
var overlapX = Math.abs(ballCenterX - wallCenterX) - (80 + wallWidth / 2);
var overlapY = Math.abs(ballCenterY - wallCenterY) - (80 + wallHeight / 2);
// Realistic physics collision with angle-based reflection
- // For level 9, use better bouncing physics
- var energyLoss = currentLevel === 9 ? 0.75 : 0.85;
+ // For level 9, use better bouncing physics with proper angle calculation
+ var energyLoss = currentLevel === 9 ? 0.85 : 0.85;
var minVelocity = 0.5;
- if (overlapX < overlapY) {
- if (ballCenterX < wallCenterX && basketball.velocityX > 0 || ballCenterX > wallCenterX && basketball.velocityX < 0) {
- basketball.velocityX = -basketball.velocityX * energyLoss;
- basketball.velocityY = basketball.velocityY * energyLoss; // Apply energy loss to perpendicular component too
- if (Math.abs(basketball.velocityX) < minVelocity) {
- basketball.velocityX = 0;
- }
+ // Calculate collision normal for proper physics reflection
+ var dx = ballCenterX - wallCenterX;
+ var dy = ballCenterY - wallCenterY;
+ var distance = Math.sqrt(dx * dx + dy * dy);
+ if (currentLevel === 9 && distance > 0) {
+ // Level 9: Use proper physics reflection based on collision angle
+ var normalX = dx / distance;
+ var normalY = dy / distance;
+ // Calculate relative velocity
+ var relativeVelocityX = basketball.velocityX;
+ var relativeVelocityY = basketball.velocityY;
+ // Calculate velocity component along normal
+ var velocityAlongNormal = relativeVelocityX * normalX + relativeVelocityY * normalY;
+ // Only reflect if moving toward the wall
+ if (velocityAlongNormal < 0) {
+ // Calculate reflection with proper physics
+ basketball.velocityX = basketball.velocityX - 2 * velocityAlongNormal * normalX;
+ basketball.velocityY = basketball.velocityY - 2 * velocityAlongNormal * normalY;
+ // Apply energy loss
+ basketball.velocityX *= energyLoss;
+ basketball.velocityY *= energyLoss;
+ // Separate ball from wall to prevent sticking
+ var separation = 85; // Ball radius + small margin
+ basketball.x = wallCenterX + normalX * separation;
+ basketball.y = wallCenterY + normalY * separation;
}
- basketball.x += ballCenterX < wallCenterX ? -5 : 5;
} else {
- if (ballCenterY < wallCenterY && basketball.velocityY > 0 || ballCenterY > wallCenterY && basketball.velocityY < 0) {
- basketball.velocityY = -basketball.velocityY * energyLoss;
- basketball.velocityX = basketball.velocityX * energyLoss; // Apply energy loss to perpendicular component too
- if (Math.abs(basketball.velocityY) < minVelocity) {
- basketball.velocityY = 0;
+ // Other levels: Use original collision logic
+ if (overlapX < overlapY) {
+ if (ballCenterX < wallCenterX && basketball.velocityX > 0 || ballCenterX > wallCenterX && basketball.velocityX < 0) {
+ basketball.velocityX = -basketball.velocityX * energyLoss;
+ basketball.velocityY = basketball.velocityY * energyLoss; // Apply energy loss to perpendicular component too
+ if (Math.abs(basketball.velocityX) < minVelocity) {
+ basketball.velocityX = 0;
+ }
}
+ basketball.x += ballCenterX < wallCenterX ? -5 : 5;
+ } else {
+ if (ballCenterY < wallCenterY && basketball.velocityY > 0 || ballCenterY > wallCenterY && basketball.velocityY < 0) {
+ basketball.velocityY = -basketball.velocityY * energyLoss;
+ basketball.velocityX = basketball.velocityX * energyLoss; // Apply energy loss to perpendicular component too
+ if (Math.abs(basketball.velocityY) < minVelocity) {
+ basketball.velocityY = 0;
+ }
+ }
+ basketball.y += ballCenterY < wallCenterY ? -5 : 5;
}
- basketball.y += ballCenterY < wallCenterY ? -5 : 5;
}
LK.getSound('bounce').play();
// Track wall bounces and check objectives
wallBounces++;
Modern App Store icon, high definition, square with rounded corners, for a game titled "Basket Bounce Challenge" and with the description "A colorful basketball puzzle game with slingshot mechanics across 20 challenging levels. Drag and release the ball to score, progressing from simple shots to complex bank shot puzzles with moving hoops and obstacles.". No text on icon!
Fullscreen modern App Store landscape banner, 16:9, high definition, for a game titled "Basket Bounce Challenge" and with the description "A colorful basketball puzzle game with slingshot mechanics across 20 challenging levels. Drag and release the ball to score, progressing from simple shots to complex bank shot puzzles with moving hoops and obstacles.". No text on banner!
A minimalist black and white basketball court background. The court is shown from a side view or slightly isometric angle. It includes clear outlines for the hoop, backboard, half court line, and three-point arc, all in clean black lines on a white surface (or vice versa). The style is modern, simple, and flat — no players, just the environment.. In-Game asset. 2d. High contrast. No shadows