User prompt
Add reset button under best score to reset unlocked player ↪💡 Consider importing and using the following plugins: @upit/storage.v1
User prompt
Reset storage ↪💡 Consider importing and using the following plugins: @upit/storage.v1
User prompt
Reset game
User prompt
Move locked player background right 10
User prompt
Move locked player background right 20
User prompt
Make locked player background 50 wider
User prompt
Move locked player background left 50
User prompt
Make locked player background not translucent
User prompt
Move locked button up 100
User prompt
Move locked button up 400
User prompt
Move locked button up 200
User prompt
Use true asset size for locked button
User prompt
Add locked button to locked player ↪💡 Consider importing and using the following plugins: @upit/storage.v1
User prompt
Lock player2 and player3 and make it $100 to unlock them ↪💡 Consider importing and using the following plugins: @upit/storage.v1
User prompt
Speed up shotbar when score reaches 100
User prompt
Reset 60 seconds if player reaches 100
User prompt
Move best score left 100
User prompt
If player gets streak 10 award $20 if player gets 20 streak award $50 if player gets 30 streak award $75 if player gets 50 streak award $100 ↪💡 Consider importing and using the following plugins: @upit/storage.v1
User prompt
Ok nice player is over 50 points slightly speed up shotbar
User prompt
If you match all backboards the same colour award player with $100.00 ↪💡 Consider importing and using the following plugins: @upit/storage.v1
User prompt
Add best score display top right corner of screen ↪💡 Consider importing and using the following plugins: @upit/storage.v1
User prompt
If you score 50 points reset timer to 60 seconds
User prompt
Add a 60 seconds timer
User prompt
Make each scored basket worth 2 points
User prompt
Don't play music untill sound 1,2 and 3 has played
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ var BallReflection = Container.expand(function () { var self = Container.call(this); var reflectionGraphics = self.attachAsset('basketball', { anchorX: 0.5, anchorY: 0.5 }); // Make reflection more transparent and darker reflectionGraphics.alpha = 0.3; reflectionGraphics.tint = 0x666666; // Flip reflection vertically reflectionGraphics.scaleY = -0.8; // Flipped and slightly smaller reflectionGraphics.scaleX = 0.8; // Set z-index to render on floor reflectionGraphics.zIndex = -3; self.parentBall = null; self.floorY = 1832; // Move floor up 900 pixels (700 + 200) self.update = function () { if (!self.parentBall || !self.parentBall.isActive) { self.destroy(); return; } // Position reflection below the ball self.x = self.parentBall.x; self.y = self.floorY - (self.floorY - self.parentBall.y) * 0.2; // Reflection distance from floor // Match parent ball rotation self.rotation = self.parentBall.rotation; // Fade reflection based on ball height var heightFactor = Math.max(0, (self.floorY - self.parentBall.y) / 1000); reflectionGraphics.alpha = 0.3 * heightFactor; }; return self; }); var Basketball = Container.expand(function () { var self = Container.call(this); var ballGraphics = self.attachAsset('basketball', { anchorX: 0.5, anchorY: 0.5 }); // Set z-index to render in front of player1 ballGraphics.zIndex = 2; self.velocityX = 0; self.velocityY = 0; self.gravity = 0.5; self.bounceDecay = 0.7; self.hasScored = false; self.isActive = true; self.guaranteedSwish = false; self.update = function () { if (!self.isActive) return; self.velocityY += self.gravity; self.x += self.velocityX; self.y += self.velocityY; // Check hoop collision for all hoops var hoopsToCheck = [hoop]; if (typeof leftHoop !== 'undefined') { hoopsToCheck.push(leftHoop); } if (typeof rightHoop !== 'undefined') { hoopsToCheck.push(rightHoop); } for (var hoopIndex = 0; hoopIndex < hoopsToCheck.length; hoopIndex++) { var currentHoop = hoopsToCheck[hoopIndex]; if (currentHoop) { var hoopX = currentHoop.x; var hoopY = currentHoop.y - 350; // Adjust for hoop position within container var relativeX = self.x - hoopX; var relativeY = self.y - hoopY; // Check for scoring through hoop first (ball going through net area) // For guaranteed swish shots, use more lenient detection var hoopDetectionWidth = self.guaranteedSwish ? 80 : 60; var hoopDetectionTop = self.guaranteedSwish ? -20 : -10; var hoopDetectionBottom = self.guaranteedSwish ? 60 : 50; if (Math.abs(relativeX) <= hoopDetectionWidth && relativeY >= hoopDetectionTop && relativeY <= hoopDetectionBottom && self.velocityY > 0 && !self.hasScored) { // Ball is going through hoop - animate net and score self.hasScored = true; self.isActive = false; // Set ball to render behind net and hoop ballGraphics.zIndex = -2; // Animate ball going through and down tween(self, { y: self.y + 100, x: hoopX + (Math.random() - 0.5) * 20 }, { duration: 500, easing: tween.easeIn }); // Animate net pulsate tween(currentHoop.net, { scaleX: 1.8, scaleY: 1.8 }, { duration: 200, easing: tween.easeOut, onFinish: function onFinish() { tween(currentHoop.net, { scaleX: 1.5, scaleY: 1.5 }, { duration: 200, easing: tween.easeIn }); } }); // Score the basket currentStreak++; var points = 10 * currentStreak; if (self.isPerfectShot) { points *= 2; // Use cycling color for perfect shot screen flash var flashColor = basketColors[currentColorIndex]; LK.effects.flashScreen(flashColor, 400); } // Apply 2x multiplier if all backboards are same color if (allSameColorMultiplier) { points *= 2; } LK.setScore(LK.getScore() + points); // Play different sound based on which hoop scored if (currentHoop === leftHoop) { LK.getSound('1').play(); sound1Played = true; checkAndStartMusic(); } else if (currentHoop === rightHoop) { LK.getSound('3').play(); sound3Played = true; checkAndStartMusic(); } else { LK.getSound('2').play(); sound2Played = true; checkAndStartMusic(); } scoreTxt.setText('Score: ' + LK.getScore()); streakTxt.setText('Streak: ' + currentStreak); LK.effects.flashObject(currentHoop, self.isPerfectShot ? 0xFFD700 : 0x00FF00, 300); // Animate backboard color change var backboard = currentHoop.children[0]; // Get the backboard (first child) if (self.isPerfectShot) { // For perfect shots, use cycling color directly var flashColor = basketColors[currentColorIndex]; // Cycle to next color for next basket currentColorIndex = (currentColorIndex + 1) % basketColors.length; // Track which backboard was scored on and update color tracking var hoopIndex = 0; // Default to center hoop if (currentHoop === leftHoop) hoopIndex = 1; if (currentHoop === rightHoop) hoopIndex = 2; backboardColors[hoopIndex] = flashColor; tween(backboard, { tint: flashColor }, { duration: 300, easing: tween.easeOut }); // Check if all backboards now have same color checkAllBackboardsSameColor(); // Play different sound based on which hoop scored when backboard changes color if (currentHoop === leftHoop) { LK.getSound('1').play(); sound1Played = true; checkAndStartMusic(); } else if (currentHoop === rightHoop) { LK.getSound('3').play(); sound3Played = true; checkAndStartMusic(); } else { LK.getSound('2').play(); sound2Played = true; checkAndStartMusic(); } } else { // For regular shots, use cycling color directly var flashColor = basketColors[currentColorIndex]; // Cycle to next color for next basket currentColorIndex = (currentColorIndex + 1) % basketColors.length; // Track which backboard was scored on and update color tracking var hoopIndex = 0; // Default to center hoop if (currentHoop === leftHoop) hoopIndex = 1; if (currentHoop === rightHoop) hoopIndex = 2; backboardColors[hoopIndex] = flashColor; tween(backboard, { tint: flashColor }, { duration: 300, easing: tween.easeOut }); // Check if all backboards now have same color checkAllBackboardsSameColor(); // Play different sound based on which hoop scored when backboard changes color if (currentHoop === leftHoop) { LK.getSound('1').play(); sound1Played = true; checkAndStartMusic(); } else if (currentHoop === rightHoop) { LK.getSound('3').play(); sound3Played = true; checkAndStartMusic(); } else { LK.getSound('2').play(); sound2Played = true; checkAndStartMusic(); } } return; } // Check if ball is hitting the hoop rim if (relativeX >= -125 && relativeX <= 125 && relativeY >= -25 && relativeY <= 25) { // Determine which side of the hoop was hit if (Math.abs(relativeX) > 90) { // Hit the sides of the rim self.velocityX = -self.velocityX * self.bounceDecay; if (relativeX < 0) { self.x = hoopX - 125; } else { self.x = hoopX + 125; } LK.getSound('bounce').play(); } else if (relativeY >= -25 && relativeY <= 0 && self.velocityY > 0) { // Hit the top of the rim (bouncing up) self.velocityY = -Math.abs(self.velocityY) * self.bounceDecay; self.y = hoopY - 25; self.velocityX *= 0.8; // Reduce horizontal velocity slightly LK.getSound('bounce').play(); } } // Check if ball is resting on the rim (low velocity and on top) if (Math.abs(relativeX) <= 90 && relativeY >= -30 && relativeY <= -20 && Math.abs(self.velocityY) < 2 && Math.abs(self.velocityX) < 3) { // Ball is resting on rim, make it roll into the hoop self.isActive = false; // Stop normal physics self.hasScored = true; // Mark as scored // Set ball to render behind net and hoop ballGraphics.zIndex = -2; // Animate rolling into hoop tween(self, { x: hoopX, y: hoopY + 40, rotation: self.rotation + Math.PI * 2 }, { duration: 800, easing: tween.easeInOut, onFinish: function onFinish() { // Animate net pulsate tween(currentHoop.net, { scaleX: 1.8, scaleY: 1.8 }, { duration: 200, easing: tween.easeOut, onFinish: function onFinish() { tween(currentHoop.net, { scaleX: 1.5, scaleY: 1.5 }, { duration: 200, easing: tween.easeIn }); } }); // Score the basket currentStreak++; var points = 10 * currentStreak; // Apply 2x multiplier if all backboards are same color if (allSameColorMultiplier) { points *= 2; } LK.setScore(LK.getScore() + points); // Play different sound based on which hoop scored if (currentHoop === leftHoop) { LK.getSound('1').play(); sound1Played = true; checkAndStartMusic(); } else if (currentHoop === rightHoop) { LK.getSound('3').play(); sound3Played = true; checkAndStartMusic(); } else { LK.getSound('2').play(); sound2Played = true; checkAndStartMusic(); } // Update displays scoreTxt.setText('Score: ' + LK.getScore()); streakTxt.setText('Streak: ' + currentStreak); // Visual feedback LK.effects.flashObject(currentHoop, 0x00FF00, 300); // Animate backboard color change var backboard = currentHoop.children[0]; // Get the backboard (first child) var flashColor = basketColors[currentColorIndex]; // Cycle to next color for next basket currentColorIndex = (currentColorIndex + 1) % basketColors.length; // Track which backboard was scored on and update color tracking var hoopIndex = 0; // Default to center hoop if (currentHoop === leftHoop) hoopIndex = 1; if (currentHoop === rightHoop) hoopIndex = 2; backboardColors[hoopIndex] = flashColor; tween(backboard, { tint: flashColor }, { duration: 300, easing: tween.easeOut }); // Check if all backboards now have same color checkAllBackboardsSameColor(); // Play different sound based on which hoop scored when backboard changes color if (currentHoop === leftHoop) { LK.getSound('1').play(); sound1Played = true; checkAndStartMusic(); } else if (currentHoop === rightHoop) { LK.getSound('3').play(); sound3Played = true; checkAndStartMusic(); } else { LK.getSound('2').play(); sound2Played = true; checkAndStartMusic(); } } }); } } } // Ground collision removed - ball can fall through // Remove if off screen - this counts as a miss if (self.x < -100 || self.x > 2148 || self.y > 2800) { // Only reset streak if this ball hasn't scored if (!self.hasScored) { currentStreak = 0; streakTxt.setText('Streak: 0'); } self.isActive = false; } }; return self; }); var Hoop = Container.expand(function () { var self = Container.call(this); var backboard = self.attachAsset('backboard', { anchorX: 0.5, anchorY: 1 }); backboard.y = -20; var hoop = self.attachAsset('hoop', { anchorX: 0.5, anchorY: 0.5 }); hoop.y = -350; // Set z-index to render in front of basketball hoop.zIndex = 1; var net = self.attachAsset('net', { anchorX: 0.5, anchorY: 0, scaleX: 1.5, scaleY: 1.5 }); net.y = -325; net.alpha = 0.8; // Set z-index to render in front of basketball net.zIndex = 2; // Store net reference for animation access self.net = net; self.hoopBounds = { left: -90, right: 90, top: -20, bottom: 20 }; // Enable z-index sorting for proper rendering order self.sortableChildren = true; return self; }); var ShotBar = Container.expand(function () { var self = Container.call(this); // Background bar - rotated to be vertical var bgBar = self.attachAsset('shotBarBg', { anchorX: 0.5, anchorY: 0.5 }); bgBar.rotation = Math.PI / 2 + Math.PI; // Rotate 90 degrees + 180 degrees to make vertical and flip // Green zone - rotated to be vertical var greenZone = self.attachAsset('shotBarGreen', { anchorX: 0.5, anchorY: 0.5 }); greenZone.rotation = Math.PI / 2 + Math.PI; // Rotate 90 degrees + 180 degrees to make vertical and flip greenZone.y = 0; // Start at center, will move vertically // Moving basketball indicator var indicator = self.attachAsset('basketball', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.6, scaleY: 0.6 }); self.isActive = false; self.isMoving = false; self.moveDirection = 1; // 1 for right, -1 for left self.moveSpeed = 16; self.maxDistance = 240; // Half of bar width minus indicator size self.perfectShot = false; self.startMoving = function () { self.isActive = true; self.isMoving = true; self.perfectShot = false; // Position green zone randomly vertically greenZone.y = (Math.random() - 0.5) * 360; // Random position within vertical bar // Reset indicator position vertically - start at top indicator.y = -self.maxDistance; self.moveDirection = 1; // Start moving downward self.visible = true; }; self.stopMoving = function () { self.isMoving = false; // Check if indicator is within green zone vertically var greenTop = greenZone.y - 60; var greenBottom = greenZone.y + 60; self.perfectShot = indicator.y >= greenTop && indicator.y <= greenBottom; // Keep shot bar visible and restart movement after a brief pause var self_ref = self; LK.setTimeout(function () { self_ref.startMoving(); }, 200); return self.perfectShot; }; self.update = function () { if (!self.isMoving) return; // Move indicator back and forth vertically indicator.y += self.moveSpeed * self.moveDirection; // Bounce off edges if (indicator.y >= self.maxDistance) { indicator.y = self.maxDistance; self.moveDirection = -1; } else if (indicator.y <= -self.maxDistance) { indicator.y = -self.maxDistance; self.moveDirection = 1; } }; self.visible = true; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x87CEEB }); /**** * Game Code ****/ var basketballs = []; var ballReflections = []; var hoop = null; var leftHoop = null; var rightHoop = null; var ground = null; var scoreTxt = null; var streakTxt = null; var currentStreak = 0; var swipeStartX = 0; var swipeStartY = 0; var swipeStartTime = 0; var isSwipeStarted = false; var lastShotTime = 0; var musicNotes = ['note1', 'note2', 'note3', 'note4', 'note5']; var currentNoteIndex = 0; var shotBar = null; var shotBar2 = null; var shotBar3 = null; var isChargingShot = false; // Color cycling system for backboard changes var basketColors = [0xFF0000, 0x00FF00, 0x0000FF, 0xFFFF00, 0xFF00FF, 0x00FFFF, 0xFFA500, 0x800080, 0xFFB6C1, 0x90EE90, 0x87CEEB, 0xDDA0DD]; var currentColorIndex = 0; // Track backboard colors for multiplier system var backboardColors = [0xFFFFFF, 0xFFFFFF, 0xFFFFFF]; // [hoop, leftHoop, rightHoop] - start with white var allSameColorMultiplier = false; // Track which sounds have been played to control music start var sound1Played = false; var sound2Played = false; var sound3Played = false; var musicStarted = false; // Create multiplier indicator text var multiplierTxt = new Text2('', { size: 50, fill: 0xFFD700 }); multiplierTxt.anchor.set(0.5, 0); multiplierTxt.y = 180; LK.gui.top.addChild(multiplierTxt); // Enable z-index sorting for proper rendering order game.sortableChildren = true; // Add background with high definition scaling var background = game.addChild(LK.getAsset('Background', { anchorX: 0, anchorY: 0 })); background.x = 0; background.y = 0; // Ensure background covers full screen with crisp high definition rendering background.width = 2048; background.height = 2732; // Set background to render behind everything background.zIndex = -10; // Ground removed - no longer needed // Create hoop hoop = game.addChild(new Hoop()); hoop.x = 1024; hoop.y = 1300; // Create second hoop to the left of the backboard leftHoop = game.addChild(new Hoop()); leftHoop.x = 1024 - 375 - 350; // Position to the left of the backboard (half backboard width) moved 350 left leftHoop.y = 1300; // Create third hoop to the right of the backboard rightHoop = game.addChild(new Hoop()); rightHoop.x = 1024 + 375 + 350; // Position to the right of the backboard (half backboard width) moved 350 right rightHoop.y = 1300; // Create score display scoreTxt = new Text2('Score: 0', { size: 80, fill: 0xFFFFFF }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); // Create streak display streakTxt = new Text2('Streak: 0', { size: 60, fill: 0xFFD700 }); streakTxt.anchor.set(0.5, 0); streakTxt.y = 100; LK.gui.top.addChild(streakTxt); // Create shot bar for Player1 (center player) - positioned at Player1's location shotBar = game.addChild(new ShotBar()); shotBar.x = 1024; // Match Player1's x position shotBar.y = 1600; // Create shot bar for Player2 (left player) - positioned at Player2's location shotBar2 = game.addChild(new ShotBar()); shotBar2.x = 300; // Match Player2's x position shotBar2.y = 1600; // Create shot bar for right hoop (no dedicated player yet) shotBar3 = game.addChild(new ShotBar()); shotBar3.x = rightHoop.x; shotBar3.y = 1600; function createBasketball(startX, startY, velocityX, velocityY) { var ball = new Basketball(); ball.x = startX; ball.y = startY; ball.velocityX = velocityX; ball.velocityY = velocityY; ball.isPerfectShot = false; basketballs.push(ball); game.addChild(ball); // Create reflection for this ball var reflection = new BallReflection(); reflection.parentBall = ball; ballReflections.push(reflection); game.addChild(reflection); return ball; } function checkBasketScore(ball) { // Scoring is now handled in Basketball class update method // This function is kept for compatibility but does nothing return; } function checkAllBackboardsSameColor() { // Check if all three backboards have the same color var allSame = backboardColors[0] === backboardColors[1] && backboardColors[1] === backboardColors[2]; // Debug logging to see what's happening console.log('Checking colors:', backboardColors, 'All same:', allSame, 'Current multiplier:', allSameColorMultiplier); // Only activate multiplier if colors changed to same (not if already same) // Also ensure we're not comparing the initial white colors if (allSame && !allSameColorMultiplier && backboardColors[0] !== 0xFFFFFF) { allSameColorMultiplier = true; console.log('Activating 2x multiplier!'); multiplierTxt.setText('2X MULTIPLIER ACTIVE!'); // Visual feedback for achieving same colors LK.effects.flashScreen(0xFFD700, 500); // Gold flash } else if (!allSame && allSameColorMultiplier) { allSameColorMultiplier = false; console.log('Deactivating 2x multiplier'); multiplierTxt.setText(''); } } function checkAndStartMusic() { // Check if all three sounds have been played and start music if not already started if (sound1Played && sound2Played && sound3Played && !musicStarted) { LK.playMusic('backgroundMusic'); musicStarted = true; } } game.down = function (x, y, obj) { if (LK.ticks - lastShotTime < 60) return; // Prevent rapid firing // Determine which player/hoop to target based on tap position var targetHoop = hoop; var currentShotBar = shotBar; var activePlayer = 1; // Track which player is shooting if (x < 683) { // Left third of screen - Player2's area targetHoop = leftHoop; currentShotBar = shotBar2; activePlayer = 2; } else if (x > 1365) { // Right third of screen - Player3's area targetHoop = rightHoop; currentShotBar = shotBar3; activePlayer = 3; } if (currentShotBar.isActive) { // Stop the shot bar and check for perfect shot var isPerfect = currentShotBar.stopMoving(); isChargingShot = false; // Switch sprites for the appropriate player if (activePlayer === 1) { // Player1 shooting player1.visible = true; player1down.visible = false; // Switch back after 1 second LK.setTimeout(function () { player1.visible = false; player1down.visible = true; }, 1000); } else if (activePlayer === 2) { // Player2 shooting player2.visible = true; player2down.visible = false; // Switch back after 1 second LK.setTimeout(function () { player2.visible = false; player2down.visible = true; }, 1000); } else if (activePlayer === 3) { // Player3 shooting player3.visible = true; player3down.visible = false; // Switch back after 1 second LK.setTimeout(function () { player3.visible = false; player3down.visible = true; }, 1000); } // Calculate shooting parameters based on active player var startX, startY; if (activePlayer === 2) { // Launch from Player2's position (left player) startX = 300; // Player2's x position startY = 2000; } else if (activePlayer === 3) { // Launch from Player3's position (right player) startX = 1748; // Player3's x position startY = 2000; } else { // Launch from Player1's position (center) for main hoop startX = 1024; // Player1's x position startY = 2000; } var velocityX = 0; var velocityY = -20; // Base upward velocity if (isPerfect) { // Perfect shot - guaranteed swish trajectory var targetX = targetHoop.x; var targetY = targetHoop.y - 350; // Aim directly at hoop center for clean entry var deltaX = targetX - startX; var deltaY = targetY - startY; // Calculate perfect parabolic trajectory for guaranteed swish var time = 50; // Slightly more time for smoother arc var gravity = 0.5; // Match basketball gravity // Calculate initial velocities for perfect arc velocityX = deltaX / time; velocityY = deltaY / time - 0.5 * gravity * time; // Fine-tune for guaranteed swish - ensure ball enters hoop cleanly var horizontalAdjustment = 0; // No horizontal drift for perfect shot var verticalAdjustment = -2; // Slight downward bias for clean entry velocityX += horizontalAdjustment; velocityY += verticalAdjustment; // Create perfect shot basketball var ball = createBasketball(startX, startY, velocityX, velocityY); ball.isPerfectShot = true; // Ensure ball will score by setting special trajectory flag ball.guaranteedSwish = true; // Visual feedback for perfect shot LK.effects.flashScreen(0x00FF00, 200); // Ball color tinting removed to prevent color changes // Add vertical rotation animation tween(ball, { rotation: ball.rotation + Math.PI * 4 }, { duration: 1500, easing: tween.easeOut }); } else { // Regular shot with some randomness velocityX = (Math.random() - 0.5) * 8; velocityY = -16 - Math.random() * 8; var ball = createBasketball(startX, startY, velocityX, velocityY); // Add vertical rotation animation for regular shot tween(ball, { rotation: ball.rotation + Math.PI * 3 }, { duration: 1200, easing: tween.easeOut }); } lastShotTime = LK.ticks; } else { // Start charging shot isChargingShot = true; currentShotBar.startMoving(); swipeStartX = x; swipeStartY = y; swipeStartTime = LK.ticks; } }; game.up = function (x, y, obj) { // Shot bar system handles shooting now, remove swipe-based shooting }; game.update = function () { // Update basketballs for (var i = basketballs.length - 1; i >= 0; i--) { var ball = basketballs[i]; if (!ball.isActive) { ball.destroy(); basketballs.splice(i, 1); continue; } } // Clean up orphaned reflections for (var i = ballReflections.length - 1; i >= 0; i--) { var reflection = ballReflections[i]; if (!reflection.parentBall || !reflection.parentBall.isActive) { reflection.destroy(); ballReflections.splice(i, 1); } // Check for scoring checkBasketScore(ball); // Check collision with other basketballs for (var j = i + 1; j < basketballs.length; j++) { var otherBall = basketballs[j]; if (!otherBall.isActive) continue; var dx = ball.x - otherBall.x; var dy = ball.y - otherBall.y; var distance = Math.sqrt(dx * dx + dy * dy); var minDistance = 120; // Combined radius of two basketballs if (distance < minDistance && distance > 0) { // Calculate collision normal var normalX = dx / distance; var normalY = dy / distance; // Separate balls to prevent overlap var overlap = minDistance - distance; var separationX = normalX * overlap * 0.5; var separationY = normalY * overlap * 0.5; ball.x += separationX; ball.y += separationY; otherBall.x -= separationX; otherBall.y -= separationY; // Calculate relative velocity var relativeVelX = ball.velocityX - otherBall.velocityX; var relativeVelY = ball.velocityY - otherBall.velocityY; // Calculate relative velocity along collision normal var relativeSpeed = relativeVelX * normalX + relativeVelY * normalY; // Only resolve if objects are moving towards each other if (relativeSpeed > 0) continue; // Calculate impulse scalar (assuming equal mass) var impulse = -2 * relativeSpeed / 2; var impulseX = impulse * normalX; var impulseY = impulse * normalY; // Apply impulse with bounce decay var bounceDecay = 0.8; ball.velocityX += impulseX * bounceDecay; ball.velocityY += impulseY * bounceDecay; otherBall.velocityX -= impulseX * bounceDecay; otherBall.velocityY -= impulseY * bounceDecay; // Play bounce sound LK.getSound('bounce').play(); } } } // Reset streak if no balls scored recently if (basketballs.length === 0 && LK.ticks - lastShotTime > 180) { if (currentStreak > 0) { currentStreak = 0; streakTxt.setText('Streak: 0'); currentNoteIndex = 0; // Reset musical progression } } // Win condition removed - no score target }; // Add Player1 at bottom of screen var player1 = game.addChild(LK.getAsset('Player1', { anchorX: 0.5, anchorY: 1 })); player1.x = 1024; // Center horizontally player1.y = 2732; // Bottom of screen player1.zIndex = 1; // Render in front of background player1.visible = false; // Make invisible // Add Player1down at bottom of screen var player1down = game.addChild(LK.getAsset('Player1down', { anchorX: 0.5, anchorY: 1 })); player1down.x = 1024; // Center horizontally player1down.y = 2732; // Bottom of screen player1down.zIndex = 1; // Render in front of background player1down.visible = true; // Make visible // Add Player2 at bottom left of screen var player2 = game.addChild(LK.getAsset('Player2', { anchorX: 0.5, anchorY: 1 })); player2.x = 300; // Left side horizontally player2.y = 2732; // Bottom of screen player2.zIndex = 1; // Render in front of background player2.visible = false; // Make invisible // Add Player2down at bottom left of screen var player2down = game.addChild(LK.getAsset('Player2down', { anchorX: 0.5, anchorY: 1 })); player2down.x = 300; // Left side horizontally player2down.y = 2732; // Bottom of screen player2down.zIndex = 1; // Render in front of background player2down.visible = true; // Make visible // Add Player3 at bottom right of screen var player3 = game.addChild(LK.getAsset('Player3', { anchorX: 0.5, anchorY: 1 })); player3.x = 1748; // Right side horizontally player3.y = 2732; // Bottom of screen player3.zIndex = 1; // Render in front of background player3.visible = false; // Make invisible // Add Player3down at bottom right of screen var player3down = game.addChild(LK.getAsset('Player3down', { anchorX: 0.5, anchorY: 1 })); player3down.x = 1748; // Right side horizontally player3down.y = 2732; // Bottom of screen player3down.zIndex = 1; // Render in front of background player3down.visible = true; // Make visible // Background music will start automatically after sounds 1, 2, and 3 have been played // Make shotbar appear automatically - start all shotbars moving shotBar.startMoving(); shotBar2.startMoving(); shotBar3.startMoving(); ; ;
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var BallReflection = Container.expand(function () {
var self = Container.call(this);
var reflectionGraphics = self.attachAsset('basketball', {
anchorX: 0.5,
anchorY: 0.5
});
// Make reflection more transparent and darker
reflectionGraphics.alpha = 0.3;
reflectionGraphics.tint = 0x666666;
// Flip reflection vertically
reflectionGraphics.scaleY = -0.8; // Flipped and slightly smaller
reflectionGraphics.scaleX = 0.8;
// Set z-index to render on floor
reflectionGraphics.zIndex = -3;
self.parentBall = null;
self.floorY = 1832; // Move floor up 900 pixels (700 + 200)
self.update = function () {
if (!self.parentBall || !self.parentBall.isActive) {
self.destroy();
return;
}
// Position reflection below the ball
self.x = self.parentBall.x;
self.y = self.floorY - (self.floorY - self.parentBall.y) * 0.2; // Reflection distance from floor
// Match parent ball rotation
self.rotation = self.parentBall.rotation;
// Fade reflection based on ball height
var heightFactor = Math.max(0, (self.floorY - self.parentBall.y) / 1000);
reflectionGraphics.alpha = 0.3 * heightFactor;
};
return self;
});
var Basketball = Container.expand(function () {
var self = Container.call(this);
var ballGraphics = self.attachAsset('basketball', {
anchorX: 0.5,
anchorY: 0.5
});
// Set z-index to render in front of player1
ballGraphics.zIndex = 2;
self.velocityX = 0;
self.velocityY = 0;
self.gravity = 0.5;
self.bounceDecay = 0.7;
self.hasScored = false;
self.isActive = true;
self.guaranteedSwish = false;
self.update = function () {
if (!self.isActive) return;
self.velocityY += self.gravity;
self.x += self.velocityX;
self.y += self.velocityY;
// Check hoop collision for all hoops
var hoopsToCheck = [hoop];
if (typeof leftHoop !== 'undefined') {
hoopsToCheck.push(leftHoop);
}
if (typeof rightHoop !== 'undefined') {
hoopsToCheck.push(rightHoop);
}
for (var hoopIndex = 0; hoopIndex < hoopsToCheck.length; hoopIndex++) {
var currentHoop = hoopsToCheck[hoopIndex];
if (currentHoop) {
var hoopX = currentHoop.x;
var hoopY = currentHoop.y - 350; // Adjust for hoop position within container
var relativeX = self.x - hoopX;
var relativeY = self.y - hoopY;
// Check for scoring through hoop first (ball going through net area)
// For guaranteed swish shots, use more lenient detection
var hoopDetectionWidth = self.guaranteedSwish ? 80 : 60;
var hoopDetectionTop = self.guaranteedSwish ? -20 : -10;
var hoopDetectionBottom = self.guaranteedSwish ? 60 : 50;
if (Math.abs(relativeX) <= hoopDetectionWidth && relativeY >= hoopDetectionTop && relativeY <= hoopDetectionBottom && self.velocityY > 0 && !self.hasScored) {
// Ball is going through hoop - animate net and score
self.hasScored = true;
self.isActive = false;
// Set ball to render behind net and hoop
ballGraphics.zIndex = -2;
// Animate ball going through and down
tween(self, {
y: self.y + 100,
x: hoopX + (Math.random() - 0.5) * 20
}, {
duration: 500,
easing: tween.easeIn
});
// Animate net pulsate
tween(currentHoop.net, {
scaleX: 1.8,
scaleY: 1.8
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(currentHoop.net, {
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 200,
easing: tween.easeIn
});
}
});
// Score the basket
currentStreak++;
var points = 10 * currentStreak;
if (self.isPerfectShot) {
points *= 2;
// Use cycling color for perfect shot screen flash
var flashColor = basketColors[currentColorIndex];
LK.effects.flashScreen(flashColor, 400);
}
// Apply 2x multiplier if all backboards are same color
if (allSameColorMultiplier) {
points *= 2;
}
LK.setScore(LK.getScore() + points);
// Play different sound based on which hoop scored
if (currentHoop === leftHoop) {
LK.getSound('1').play();
sound1Played = true;
checkAndStartMusic();
} else if (currentHoop === rightHoop) {
LK.getSound('3').play();
sound3Played = true;
checkAndStartMusic();
} else {
LK.getSound('2').play();
sound2Played = true;
checkAndStartMusic();
}
scoreTxt.setText('Score: ' + LK.getScore());
streakTxt.setText('Streak: ' + currentStreak);
LK.effects.flashObject(currentHoop, self.isPerfectShot ? 0xFFD700 : 0x00FF00, 300);
// Animate backboard color change
var backboard = currentHoop.children[0]; // Get the backboard (first child)
if (self.isPerfectShot) {
// For perfect shots, use cycling color directly
var flashColor = basketColors[currentColorIndex];
// Cycle to next color for next basket
currentColorIndex = (currentColorIndex + 1) % basketColors.length;
// Track which backboard was scored on and update color tracking
var hoopIndex = 0; // Default to center hoop
if (currentHoop === leftHoop) hoopIndex = 1;
if (currentHoop === rightHoop) hoopIndex = 2;
backboardColors[hoopIndex] = flashColor;
tween(backboard, {
tint: flashColor
}, {
duration: 300,
easing: tween.easeOut
});
// Check if all backboards now have same color
checkAllBackboardsSameColor();
// Play different sound based on which hoop scored when backboard changes color
if (currentHoop === leftHoop) {
LK.getSound('1').play();
sound1Played = true;
checkAndStartMusic();
} else if (currentHoop === rightHoop) {
LK.getSound('3').play();
sound3Played = true;
checkAndStartMusic();
} else {
LK.getSound('2').play();
sound2Played = true;
checkAndStartMusic();
}
} else {
// For regular shots, use cycling color directly
var flashColor = basketColors[currentColorIndex];
// Cycle to next color for next basket
currentColorIndex = (currentColorIndex + 1) % basketColors.length;
// Track which backboard was scored on and update color tracking
var hoopIndex = 0; // Default to center hoop
if (currentHoop === leftHoop) hoopIndex = 1;
if (currentHoop === rightHoop) hoopIndex = 2;
backboardColors[hoopIndex] = flashColor;
tween(backboard, {
tint: flashColor
}, {
duration: 300,
easing: tween.easeOut
});
// Check if all backboards now have same color
checkAllBackboardsSameColor();
// Play different sound based on which hoop scored when backboard changes color
if (currentHoop === leftHoop) {
LK.getSound('1').play();
sound1Played = true;
checkAndStartMusic();
} else if (currentHoop === rightHoop) {
LK.getSound('3').play();
sound3Played = true;
checkAndStartMusic();
} else {
LK.getSound('2').play();
sound2Played = true;
checkAndStartMusic();
}
}
return;
}
// Check if ball is hitting the hoop rim
if (relativeX >= -125 && relativeX <= 125 && relativeY >= -25 && relativeY <= 25) {
// Determine which side of the hoop was hit
if (Math.abs(relativeX) > 90) {
// Hit the sides of the rim
self.velocityX = -self.velocityX * self.bounceDecay;
if (relativeX < 0) {
self.x = hoopX - 125;
} else {
self.x = hoopX + 125;
}
LK.getSound('bounce').play();
} else if (relativeY >= -25 && relativeY <= 0 && self.velocityY > 0) {
// Hit the top of the rim (bouncing up)
self.velocityY = -Math.abs(self.velocityY) * self.bounceDecay;
self.y = hoopY - 25;
self.velocityX *= 0.8; // Reduce horizontal velocity slightly
LK.getSound('bounce').play();
}
}
// Check if ball is resting on the rim (low velocity and on top)
if (Math.abs(relativeX) <= 90 && relativeY >= -30 && relativeY <= -20 && Math.abs(self.velocityY) < 2 && Math.abs(self.velocityX) < 3) {
// Ball is resting on rim, make it roll into the hoop
self.isActive = false; // Stop normal physics
self.hasScored = true; // Mark as scored
// Set ball to render behind net and hoop
ballGraphics.zIndex = -2;
// Animate rolling into hoop
tween(self, {
x: hoopX,
y: hoopY + 40,
rotation: self.rotation + Math.PI * 2
}, {
duration: 800,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Animate net pulsate
tween(currentHoop.net, {
scaleX: 1.8,
scaleY: 1.8
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(currentHoop.net, {
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 200,
easing: tween.easeIn
});
}
});
// Score the basket
currentStreak++;
var points = 10 * currentStreak;
// Apply 2x multiplier if all backboards are same color
if (allSameColorMultiplier) {
points *= 2;
}
LK.setScore(LK.getScore() + points);
// Play different sound based on which hoop scored
if (currentHoop === leftHoop) {
LK.getSound('1').play();
sound1Played = true;
checkAndStartMusic();
} else if (currentHoop === rightHoop) {
LK.getSound('3').play();
sound3Played = true;
checkAndStartMusic();
} else {
LK.getSound('2').play();
sound2Played = true;
checkAndStartMusic();
}
// Update displays
scoreTxt.setText('Score: ' + LK.getScore());
streakTxt.setText('Streak: ' + currentStreak);
// Visual feedback
LK.effects.flashObject(currentHoop, 0x00FF00, 300);
// Animate backboard color change
var backboard = currentHoop.children[0]; // Get the backboard (first child)
var flashColor = basketColors[currentColorIndex];
// Cycle to next color for next basket
currentColorIndex = (currentColorIndex + 1) % basketColors.length;
// Track which backboard was scored on and update color tracking
var hoopIndex = 0; // Default to center hoop
if (currentHoop === leftHoop) hoopIndex = 1;
if (currentHoop === rightHoop) hoopIndex = 2;
backboardColors[hoopIndex] = flashColor;
tween(backboard, {
tint: flashColor
}, {
duration: 300,
easing: tween.easeOut
});
// Check if all backboards now have same color
checkAllBackboardsSameColor();
// Play different sound based on which hoop scored when backboard changes color
if (currentHoop === leftHoop) {
LK.getSound('1').play();
sound1Played = true;
checkAndStartMusic();
} else if (currentHoop === rightHoop) {
LK.getSound('3').play();
sound3Played = true;
checkAndStartMusic();
} else {
LK.getSound('2').play();
sound2Played = true;
checkAndStartMusic();
}
}
});
}
}
}
// Ground collision removed - ball can fall through
// Remove if off screen - this counts as a miss
if (self.x < -100 || self.x > 2148 || self.y > 2800) {
// Only reset streak if this ball hasn't scored
if (!self.hasScored) {
currentStreak = 0;
streakTxt.setText('Streak: 0');
}
self.isActive = false;
}
};
return self;
});
var Hoop = Container.expand(function () {
var self = Container.call(this);
var backboard = self.attachAsset('backboard', {
anchorX: 0.5,
anchorY: 1
});
backboard.y = -20;
var hoop = self.attachAsset('hoop', {
anchorX: 0.5,
anchorY: 0.5
});
hoop.y = -350;
// Set z-index to render in front of basketball
hoop.zIndex = 1;
var net = self.attachAsset('net', {
anchorX: 0.5,
anchorY: 0,
scaleX: 1.5,
scaleY: 1.5
});
net.y = -325;
net.alpha = 0.8;
// Set z-index to render in front of basketball
net.zIndex = 2;
// Store net reference for animation access
self.net = net;
self.hoopBounds = {
left: -90,
right: 90,
top: -20,
bottom: 20
};
// Enable z-index sorting for proper rendering order
self.sortableChildren = true;
return self;
});
var ShotBar = Container.expand(function () {
var self = Container.call(this);
// Background bar - rotated to be vertical
var bgBar = self.attachAsset('shotBarBg', {
anchorX: 0.5,
anchorY: 0.5
});
bgBar.rotation = Math.PI / 2 + Math.PI; // Rotate 90 degrees + 180 degrees to make vertical and flip
// Green zone - rotated to be vertical
var greenZone = self.attachAsset('shotBarGreen', {
anchorX: 0.5,
anchorY: 0.5
});
greenZone.rotation = Math.PI / 2 + Math.PI; // Rotate 90 degrees + 180 degrees to make vertical and flip
greenZone.y = 0; // Start at center, will move vertically
// Moving basketball indicator
var indicator = self.attachAsset('basketball', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.6,
scaleY: 0.6
});
self.isActive = false;
self.isMoving = false;
self.moveDirection = 1; // 1 for right, -1 for left
self.moveSpeed = 16;
self.maxDistance = 240; // Half of bar width minus indicator size
self.perfectShot = false;
self.startMoving = function () {
self.isActive = true;
self.isMoving = true;
self.perfectShot = false;
// Position green zone randomly vertically
greenZone.y = (Math.random() - 0.5) * 360; // Random position within vertical bar
// Reset indicator position vertically - start at top
indicator.y = -self.maxDistance;
self.moveDirection = 1; // Start moving downward
self.visible = true;
};
self.stopMoving = function () {
self.isMoving = false;
// Check if indicator is within green zone vertically
var greenTop = greenZone.y - 60;
var greenBottom = greenZone.y + 60;
self.perfectShot = indicator.y >= greenTop && indicator.y <= greenBottom;
// Keep shot bar visible and restart movement after a brief pause
var self_ref = self;
LK.setTimeout(function () {
self_ref.startMoving();
}, 200);
return self.perfectShot;
};
self.update = function () {
if (!self.isMoving) return;
// Move indicator back and forth vertically
indicator.y += self.moveSpeed * self.moveDirection;
// Bounce off edges
if (indicator.y >= self.maxDistance) {
indicator.y = self.maxDistance;
self.moveDirection = -1;
} else if (indicator.y <= -self.maxDistance) {
indicator.y = -self.maxDistance;
self.moveDirection = 1;
}
};
self.visible = true;
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x87CEEB
});
/****
* Game Code
****/
var basketballs = [];
var ballReflections = [];
var hoop = null;
var leftHoop = null;
var rightHoop = null;
var ground = null;
var scoreTxt = null;
var streakTxt = null;
var currentStreak = 0;
var swipeStartX = 0;
var swipeStartY = 0;
var swipeStartTime = 0;
var isSwipeStarted = false;
var lastShotTime = 0;
var musicNotes = ['note1', 'note2', 'note3', 'note4', 'note5'];
var currentNoteIndex = 0;
var shotBar = null;
var shotBar2 = null;
var shotBar3 = null;
var isChargingShot = false;
// Color cycling system for backboard changes
var basketColors = [0xFF0000, 0x00FF00, 0x0000FF, 0xFFFF00, 0xFF00FF, 0x00FFFF, 0xFFA500, 0x800080, 0xFFB6C1, 0x90EE90, 0x87CEEB, 0xDDA0DD];
var currentColorIndex = 0;
// Track backboard colors for multiplier system
var backboardColors = [0xFFFFFF, 0xFFFFFF, 0xFFFFFF]; // [hoop, leftHoop, rightHoop] - start with white
var allSameColorMultiplier = false;
// Track which sounds have been played to control music start
var sound1Played = false;
var sound2Played = false;
var sound3Played = false;
var musicStarted = false;
// Create multiplier indicator text
var multiplierTxt = new Text2('', {
size: 50,
fill: 0xFFD700
});
multiplierTxt.anchor.set(0.5, 0);
multiplierTxt.y = 180;
LK.gui.top.addChild(multiplierTxt);
// Enable z-index sorting for proper rendering order
game.sortableChildren = true;
// Add background with high definition scaling
var background = game.addChild(LK.getAsset('Background', {
anchorX: 0,
anchorY: 0
}));
background.x = 0;
background.y = 0;
// Ensure background covers full screen with crisp high definition rendering
background.width = 2048;
background.height = 2732;
// Set background to render behind everything
background.zIndex = -10;
// Ground removed - no longer needed
// Create hoop
hoop = game.addChild(new Hoop());
hoop.x = 1024;
hoop.y = 1300;
// Create second hoop to the left of the backboard
leftHoop = game.addChild(new Hoop());
leftHoop.x = 1024 - 375 - 350; // Position to the left of the backboard (half backboard width) moved 350 left
leftHoop.y = 1300;
// Create third hoop to the right of the backboard
rightHoop = game.addChild(new Hoop());
rightHoop.x = 1024 + 375 + 350; // Position to the right of the backboard (half backboard width) moved 350 right
rightHoop.y = 1300;
// Create score display
scoreTxt = new Text2('Score: 0', {
size: 80,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Create streak display
streakTxt = new Text2('Streak: 0', {
size: 60,
fill: 0xFFD700
});
streakTxt.anchor.set(0.5, 0);
streakTxt.y = 100;
LK.gui.top.addChild(streakTxt);
// Create shot bar for Player1 (center player) - positioned at Player1's location
shotBar = game.addChild(new ShotBar());
shotBar.x = 1024; // Match Player1's x position
shotBar.y = 1600;
// Create shot bar for Player2 (left player) - positioned at Player2's location
shotBar2 = game.addChild(new ShotBar());
shotBar2.x = 300; // Match Player2's x position
shotBar2.y = 1600;
// Create shot bar for right hoop (no dedicated player yet)
shotBar3 = game.addChild(new ShotBar());
shotBar3.x = rightHoop.x;
shotBar3.y = 1600;
function createBasketball(startX, startY, velocityX, velocityY) {
var ball = new Basketball();
ball.x = startX;
ball.y = startY;
ball.velocityX = velocityX;
ball.velocityY = velocityY;
ball.isPerfectShot = false;
basketballs.push(ball);
game.addChild(ball);
// Create reflection for this ball
var reflection = new BallReflection();
reflection.parentBall = ball;
ballReflections.push(reflection);
game.addChild(reflection);
return ball;
}
function checkBasketScore(ball) {
// Scoring is now handled in Basketball class update method
// This function is kept for compatibility but does nothing
return;
}
function checkAllBackboardsSameColor() {
// Check if all three backboards have the same color
var allSame = backboardColors[0] === backboardColors[1] && backboardColors[1] === backboardColors[2];
// Debug logging to see what's happening
console.log('Checking colors:', backboardColors, 'All same:', allSame, 'Current multiplier:', allSameColorMultiplier);
// Only activate multiplier if colors changed to same (not if already same)
// Also ensure we're not comparing the initial white colors
if (allSame && !allSameColorMultiplier && backboardColors[0] !== 0xFFFFFF) {
allSameColorMultiplier = true;
console.log('Activating 2x multiplier!');
multiplierTxt.setText('2X MULTIPLIER ACTIVE!');
// Visual feedback for achieving same colors
LK.effects.flashScreen(0xFFD700, 500); // Gold flash
} else if (!allSame && allSameColorMultiplier) {
allSameColorMultiplier = false;
console.log('Deactivating 2x multiplier');
multiplierTxt.setText('');
}
}
function checkAndStartMusic() {
// Check if all three sounds have been played and start music if not already started
if (sound1Played && sound2Played && sound3Played && !musicStarted) {
LK.playMusic('backgroundMusic');
musicStarted = true;
}
}
game.down = function (x, y, obj) {
if (LK.ticks - lastShotTime < 60) return; // Prevent rapid firing
// Determine which player/hoop to target based on tap position
var targetHoop = hoop;
var currentShotBar = shotBar;
var activePlayer = 1; // Track which player is shooting
if (x < 683) {
// Left third of screen - Player2's area
targetHoop = leftHoop;
currentShotBar = shotBar2;
activePlayer = 2;
} else if (x > 1365) {
// Right third of screen - Player3's area
targetHoop = rightHoop;
currentShotBar = shotBar3;
activePlayer = 3;
}
if (currentShotBar.isActive) {
// Stop the shot bar and check for perfect shot
var isPerfect = currentShotBar.stopMoving();
isChargingShot = false;
// Switch sprites for the appropriate player
if (activePlayer === 1) {
// Player1 shooting
player1.visible = true;
player1down.visible = false;
// Switch back after 1 second
LK.setTimeout(function () {
player1.visible = false;
player1down.visible = true;
}, 1000);
} else if (activePlayer === 2) {
// Player2 shooting
player2.visible = true;
player2down.visible = false;
// Switch back after 1 second
LK.setTimeout(function () {
player2.visible = false;
player2down.visible = true;
}, 1000);
} else if (activePlayer === 3) {
// Player3 shooting
player3.visible = true;
player3down.visible = false;
// Switch back after 1 second
LK.setTimeout(function () {
player3.visible = false;
player3down.visible = true;
}, 1000);
}
// Calculate shooting parameters based on active player
var startX, startY;
if (activePlayer === 2) {
// Launch from Player2's position (left player)
startX = 300; // Player2's x position
startY = 2000;
} else if (activePlayer === 3) {
// Launch from Player3's position (right player)
startX = 1748; // Player3's x position
startY = 2000;
} else {
// Launch from Player1's position (center) for main hoop
startX = 1024; // Player1's x position
startY = 2000;
}
var velocityX = 0;
var velocityY = -20; // Base upward velocity
if (isPerfect) {
// Perfect shot - guaranteed swish trajectory
var targetX = targetHoop.x;
var targetY = targetHoop.y - 350; // Aim directly at hoop center for clean entry
var deltaX = targetX - startX;
var deltaY = targetY - startY;
// Calculate perfect parabolic trajectory for guaranteed swish
var time = 50; // Slightly more time for smoother arc
var gravity = 0.5; // Match basketball gravity
// Calculate initial velocities for perfect arc
velocityX = deltaX / time;
velocityY = deltaY / time - 0.5 * gravity * time;
// Fine-tune for guaranteed swish - ensure ball enters hoop cleanly
var horizontalAdjustment = 0; // No horizontal drift for perfect shot
var verticalAdjustment = -2; // Slight downward bias for clean entry
velocityX += horizontalAdjustment;
velocityY += verticalAdjustment;
// Create perfect shot basketball
var ball = createBasketball(startX, startY, velocityX, velocityY);
ball.isPerfectShot = true;
// Ensure ball will score by setting special trajectory flag
ball.guaranteedSwish = true;
// Visual feedback for perfect shot
LK.effects.flashScreen(0x00FF00, 200);
// Ball color tinting removed to prevent color changes
// Add vertical rotation animation
tween(ball, {
rotation: ball.rotation + Math.PI * 4
}, {
duration: 1500,
easing: tween.easeOut
});
} else {
// Regular shot with some randomness
velocityX = (Math.random() - 0.5) * 8;
velocityY = -16 - Math.random() * 8;
var ball = createBasketball(startX, startY, velocityX, velocityY);
// Add vertical rotation animation for regular shot
tween(ball, {
rotation: ball.rotation + Math.PI * 3
}, {
duration: 1200,
easing: tween.easeOut
});
}
lastShotTime = LK.ticks;
} else {
// Start charging shot
isChargingShot = true;
currentShotBar.startMoving();
swipeStartX = x;
swipeStartY = y;
swipeStartTime = LK.ticks;
}
};
game.up = function (x, y, obj) {
// Shot bar system handles shooting now, remove swipe-based shooting
};
game.update = function () {
// Update basketballs
for (var i = basketballs.length - 1; i >= 0; i--) {
var ball = basketballs[i];
if (!ball.isActive) {
ball.destroy();
basketballs.splice(i, 1);
continue;
}
}
// Clean up orphaned reflections
for (var i = ballReflections.length - 1; i >= 0; i--) {
var reflection = ballReflections[i];
if (!reflection.parentBall || !reflection.parentBall.isActive) {
reflection.destroy();
ballReflections.splice(i, 1);
}
// Check for scoring
checkBasketScore(ball);
// Check collision with other basketballs
for (var j = i + 1; j < basketballs.length; j++) {
var otherBall = basketballs[j];
if (!otherBall.isActive) continue;
var dx = ball.x - otherBall.x;
var dy = ball.y - otherBall.y;
var distance = Math.sqrt(dx * dx + dy * dy);
var minDistance = 120; // Combined radius of two basketballs
if (distance < minDistance && distance > 0) {
// Calculate collision normal
var normalX = dx / distance;
var normalY = dy / distance;
// Separate balls to prevent overlap
var overlap = minDistance - distance;
var separationX = normalX * overlap * 0.5;
var separationY = normalY * overlap * 0.5;
ball.x += separationX;
ball.y += separationY;
otherBall.x -= separationX;
otherBall.y -= separationY;
// Calculate relative velocity
var relativeVelX = ball.velocityX - otherBall.velocityX;
var relativeVelY = ball.velocityY - otherBall.velocityY;
// Calculate relative velocity along collision normal
var relativeSpeed = relativeVelX * normalX + relativeVelY * normalY;
// Only resolve if objects are moving towards each other
if (relativeSpeed > 0) continue;
// Calculate impulse scalar (assuming equal mass)
var impulse = -2 * relativeSpeed / 2;
var impulseX = impulse * normalX;
var impulseY = impulse * normalY;
// Apply impulse with bounce decay
var bounceDecay = 0.8;
ball.velocityX += impulseX * bounceDecay;
ball.velocityY += impulseY * bounceDecay;
otherBall.velocityX -= impulseX * bounceDecay;
otherBall.velocityY -= impulseY * bounceDecay;
// Play bounce sound
LK.getSound('bounce').play();
}
}
}
// Reset streak if no balls scored recently
if (basketballs.length === 0 && LK.ticks - lastShotTime > 180) {
if (currentStreak > 0) {
currentStreak = 0;
streakTxt.setText('Streak: 0');
currentNoteIndex = 0; // Reset musical progression
}
}
// Win condition removed - no score target
};
// Add Player1 at bottom of screen
var player1 = game.addChild(LK.getAsset('Player1', {
anchorX: 0.5,
anchorY: 1
}));
player1.x = 1024; // Center horizontally
player1.y = 2732; // Bottom of screen
player1.zIndex = 1; // Render in front of background
player1.visible = false; // Make invisible
// Add Player1down at bottom of screen
var player1down = game.addChild(LK.getAsset('Player1down', {
anchorX: 0.5,
anchorY: 1
}));
player1down.x = 1024; // Center horizontally
player1down.y = 2732; // Bottom of screen
player1down.zIndex = 1; // Render in front of background
player1down.visible = true; // Make visible
// Add Player2 at bottom left of screen
var player2 = game.addChild(LK.getAsset('Player2', {
anchorX: 0.5,
anchorY: 1
}));
player2.x = 300; // Left side horizontally
player2.y = 2732; // Bottom of screen
player2.zIndex = 1; // Render in front of background
player2.visible = false; // Make invisible
// Add Player2down at bottom left of screen
var player2down = game.addChild(LK.getAsset('Player2down', {
anchorX: 0.5,
anchorY: 1
}));
player2down.x = 300; // Left side horizontally
player2down.y = 2732; // Bottom of screen
player2down.zIndex = 1; // Render in front of background
player2down.visible = true; // Make visible
// Add Player3 at bottom right of screen
var player3 = game.addChild(LK.getAsset('Player3', {
anchorX: 0.5,
anchorY: 1
}));
player3.x = 1748; // Right side horizontally
player3.y = 2732; // Bottom of screen
player3.zIndex = 1; // Render in front of background
player3.visible = false; // Make invisible
// Add Player3down at bottom right of screen
var player3down = game.addChild(LK.getAsset('Player3down', {
anchorX: 0.5,
anchorY: 1
}));
player3down.x = 1748; // Right side horizontally
player3down.y = 2732; // Bottom of screen
player3down.zIndex = 1; // Render in front of background
player3down.visible = true; // Make visible
// Background music will start automatically after sounds 1, 2, and 3 have been played
// Make shotbar appear automatically - start all shotbars moving
shotBar.startMoving();
shotBar2.startMoving();
shotBar3.startMoving();
;
;
Make picture high definition
Remove everything but net
Remove basketball rings and backboards from picture
Shiny black rectangle frame. In-Game asset. 2d. High contrast. No shadows
Neon green basketball. In-Game asset. 2d. High contrast. No shadows
Change to black warriors uniform
Change to black warriors uniform
Padlock button that says *locked* Purchase for: $100. In-Game asset. 2d. High contrast. No shadows
Remove words "with basketball"
Number 1
Number 2
Number 3
Number 4
Number 5
Number 6
Number 7
Number 8
Make it say $HOP in big letters across the ball
Change it to say Rhythm
Make a shop backdrop with display shelves and framed areas to place items
Remove ball and put his hands down like he just caught a pass
Remove ball and fix hands
Record. In-Game asset. 2d. High contrast. No shadows
Make the net look like it's on fire