User prompt
Remove all items from shop
User prompt
Remove items from shop
User prompt
Ensure cash display shows the same amount as you have been awarded in gameplay ↪💡 Consider importing and using the following plugins: @upit/storage.v1
User prompt
Show cash display in shopbackdrop
User prompt
Prevent from tapping tap to start when in shopbackdrop
User prompt
Remove shop interface all together
User prompt
Make shop interface visible on shopbackdrop
User prompt
Tap back asset to return to title screen
User prompt
Move back button to bottom right corner of shopbackdrop
User prompt
Add back asset to shopbackdrop as a button to return to title scene
User prompt
Change shop interface to shopbackdrop asset
User prompt
Move back button to middle of Shop interface
User prompt
Fix this please
User prompt
Render back button in front of backdrop
User prompt
Move back button to bottom right corner
User prompt
Add back button to shop interface
User prompt
Move shop asset to bottom right corner of screen
User prompt
Please fix the bug: 'Uncaught TypeError: LK.pauseGame is not a function' in or related to this line: 'LK.pauseGame();' Line Number: 697
User prompt
Replace shop text with shop asset
User prompt
Move how to play button left 100
User prompt
Move how to play button to bottom left corner of screen
User prompt
Replace how to play button text with how to play asset
User prompt
Make the visualisers change to different neon colours every correct tap ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Replace shotbargreen asset with visualiser asset for visualiser bars
User prompt
Increase tap sensitivity in rhythm mode
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
money: 0,
player2Unlocked: false,
player3Unlocked: false
});
/****
* 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 much more lenient detection
var hoopDetectionWidth = self.guaranteedSwish ? 120 : 60;
var hoopDetectionTop = self.guaranteedSwish ? -35 : -10;
var hoopDetectionBottom = self.guaranteedSwish ? 80 : 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++;
// Check for streak rewards
var moneyEarned = 0;
if (currentStreak === 10 && streakRewardsAwarded.indexOf(10) === -1) {
moneyEarned = 20.00;
streakRewardsAwarded.push(10);
} else if (currentStreak === 20 && streakRewardsAwarded.indexOf(20) === -1) {
moneyEarned = 50.00;
streakRewardsAwarded.push(20);
} else if (currentStreak === 30 && streakRewardsAwarded.indexOf(30) === -1) {
moneyEarned = 75.00;
streakRewardsAwarded.push(30);
} else if (currentStreak === 50 && streakRewardsAwarded.indexOf(50) === -1) {
moneyEarned = 100.00;
streakRewardsAwarded.push(50);
}
// Apply money multiplier if active
if (moneyEarned > 0) {
if (moneyMultiplierBaskets > 0) {
moneyEarned *= 2;
moneyMultiplierBaskets--;
}
currentMoney += moneyEarned;
storage.money = currentMoney;
moneyTxt.setText('$' + currentMoney.toFixed(2));
LK.effects.flashScreen(0x00FF00, 600); // Green flash for money
LK.getSound('Cash').play(); // Play cash sound when money is awarded
}
var points = 2;
if (self.isPerfectShot) {
// Use cycling color for perfect shot screen flash
var flashColor = basketColors[currentColorIndex];
LK.effects.flashScreen(flashColor, 400);
}
// 2x multiplier removed - no longer applied when all backboards are same color
// Apply rhythm multiplier if in rhythm mode
if (typeof rhythmMultiplier !== 'undefined' && rhythmMultiplier > 1) {
points = Math.floor(points * rhythmMultiplier);
}
LK.setScore(LK.getScore() + points);
// Check if score reached multiple of 50 points to reset timer
var currentScore = LK.getScore();
var previousScore = currentScore - points;
var currentFifties = Math.floor(currentScore / 50);
var previousFifties = Math.floor(previousScore / 50);
if (currentFifties > previousFifties) {
if (gameMode === 'rhythm') {
gameTimer = 15 * 60; // Reset to 15 seconds for rhythm mode at 60 FPS
gameTimeLeft = 15;
updateTimerDisplay(15);
} else {
gameTimer = 60 * 60; // Reset to 60 seconds for swish mode at 60 FPS
gameTimeLeft = 60;
updateTimerDisplay(60);
}
}
// Play different sound based on which hoop scored
if (currentHoop === leftHoop) {
LK.getSound('1').play();
} else if (currentHoop === rightHoop) {
LK.getSound('3').play();
} else {
LK.getSound('2').play();
}
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();
} else if (currentHoop === rightHoop) {
LK.getSound('3').play();
} else {
LK.getSound('2').play();
}
} 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();
} else if (currentHoop === rightHoop) {
LK.getSound('3').play();
} else {
LK.getSound('2').play();
}
}
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++;
// Check for streak rewards
if (currentStreak === 10 && streakRewardsAwarded.indexOf(10) === -1) {
currentMoney += 20.00;
storage.money = currentMoney;
moneyTxt.setText('$' + currentMoney.toFixed(2));
streakRewardsAwarded.push(10);
LK.effects.flashScreen(0x00FF00, 600); // Green flash for money
LK.getSound('Cash').play(); // Play cash sound when money is awarded
} else if (currentStreak === 20 && streakRewardsAwarded.indexOf(20) === -1) {
currentMoney += 50.00;
storage.money = currentMoney;
moneyTxt.setText('$' + currentMoney.toFixed(2));
streakRewardsAwarded.push(20);
LK.effects.flashScreen(0x00FF00, 600); // Green flash for money
LK.getSound('Cash').play(); // Play cash sound when money is awarded
} else if (currentStreak === 30 && streakRewardsAwarded.indexOf(30) === -1) {
currentMoney += 75.00;
storage.money = currentMoney;
moneyTxt.setText('$' + currentMoney.toFixed(2));
streakRewardsAwarded.push(30);
LK.effects.flashScreen(0x00FF00, 600); // Green flash for money
LK.getSound('Cash').play(); // Play cash sound when money is awarded
} else if (currentStreak === 50 && streakRewardsAwarded.indexOf(50) === -1) {
currentMoney += 100.00;
storage.money = currentMoney;
moneyTxt.setText('$' + currentMoney.toFixed(2));
streakRewardsAwarded.push(50);
LK.effects.flashScreen(0x00FF00, 600); // Green flash for money
LK.getSound('Cash').play(); // Play cash sound when money is awarded
}
var points = 2;
// 2x multiplier removed - no longer applied when all backboards are same color
LK.setScore(LK.getScore() + points);
// Check if score reached multiple of 50 points to reset timer
var currentScore = LK.getScore();
var previousScore = currentScore - points;
var currentFifties = Math.floor(currentScore / 50);
var previousFifties = Math.floor(previousScore / 50);
if (currentFifties > previousFifties) {
if (gameMode === 'rhythm') {
gameTimer = 15 * 60; // Reset to 15 seconds for rhythm mode at 60 FPS
gameTimeLeft = 15;
updateTimerDisplay(15);
} else {
gameTimer = 60 * 60; // Reset to 60 seconds for swish mode at 60 FPS
gameTimeLeft = 60;
updateTimerDisplay(60);
}
}
// Play different sound based on which hoop scored
if (currentHoop === leftHoop) {
LK.getSound('1').play();
} else if (currentHoop === rightHoop) {
LK.getSound('3').play();
} else {
LK.getSound('2').play();
}
// 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();
} else if (currentHoop === rightHoop) {
LK.getSound('3').play();
} else {
LK.getSound('2').play();
}
}
});
}
}
}
// Ground collision removed - ball can fall through
// Remove if off screen - ball is just removed, no streak penalty
if (self.x < -100 || self.x > 2148 || self.y > 2800) {
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 RhythmCircle = Container.expand(function () {
var self = Container.call(this);
// Create circle graphic using greenBall asset with scale and tint for rhythm indicator
var circleGraphics = self.attachAsset('greenBall', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 1.2
});
circleGraphics.tint = 0xFF6B6B; // Pink tint for rhythm circles
circleGraphics.alpha = 0.8;
// Set z-index to render above most elements
circleGraphics.zIndex = 15;
self.fallSpeed = 6;
self.isActive = true;
self.targetShotBar = null; // Which shot bar this circle is aligned with
self.wasInGreenZone = false;
self.hasPassed = false;
// Store the Y position of the green zone for this circle's target shot bar
self.greenZoneY = 0;
self.update = function () {
if (!self.isActive) return;
// Fall downward
self.y += self.fallSpeed;
// Check if circle has reached the green zone area of target shot bar
if (self.targetShotBar && self.targetShotBar.children && self.targetShotBar.children.length > 2) {
var greenZone = self.targetShotBar.children[1]; // Second child should be green zone
var indicator = self.targetShotBar.children[2]; // Third child is the basketball indicator
var shotBarWorldY = self.targetShotBar.y; // Shot bar's world Y position
var tolerance = 60; // Match the green zone tolerance
// Check if circle is in the green zone area (aligned with shot bar position)
var inGreenZone = Math.abs(self.y - shotBarWorldY) <= tolerance;
if (inGreenZone && !self.wasInGreenZone) {
self.wasInGreenZone = true;
// Store the green zone Y position for this circle
self.greenZoneY = shotBarWorldY;
// Pulse effect when entering green zone
tween(circleGraphics, {
scaleX: 1.6,
scaleY: 1.6,
alpha: 1.0
}, {
duration: 150,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(circleGraphics, {
scaleX: 1.2,
scaleY: 1.2,
alpha: 0.8
}, {
duration: 150,
easing: tween.easeIn
});
}
});
}
// Check if circle has passed the green zone
if (self.y > shotBarWorldY + tolerance && !self.hasPassed) {
self.hasPassed = true;
}
}
// Remove if off screen
if (self.y > 2800) {
self.isActive = false;
}
};
return self;
});
var Shop = Container.expand(function () {
var self = Container.call(this);
// Shop background
var shopBg = self.attachAsset('Player4', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 20,
scaleY: 25
});
shopBg.tint = 0x333333;
shopBg.alpha = 0.9;
self.isVisible = false;
self.visible = false;
// Shop title
var shopTitle = new Text2('SHOP', {
size: 120,
fill: 0xFFD700
});
shopTitle.anchor.set(0.5, 0.5);
shopTitle.y = -800;
self.addChild(shopTitle);
// Shop items array
self.shopItems = [{
name: 'Speed Boost',
description: 'Increase shot speed by 25%',
cost: 50.00,
purchased: false,
type: 'speed'
}, {
name: 'Perfect Shot',
description: 'Next 3 shots guaranteed perfect',
cost: 25.00,
purchased: false,
type: 'perfect'
}, {
name: 'Money Multiplier',
description: 'Double money for 10 baskets',
cost: 75.00,
purchased: false,
type: 'money'
}, {
name: 'Time Extension',
description: 'Add 30 seconds to timer',
cost: 100.00,
purchased: false,
type: 'time'
}];
// Create shop item buttons
self.itemButtons = [];
for (var i = 0; i < self.shopItems.length; i++) {
var item = self.shopItems[i];
var yOffset = -400 + i * 200;
// Item background
var itemBg = self.attachAsset('Player4', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 15,
scaleY: 3
});
itemBg.y = yOffset;
itemBg.tint = 0x555555;
itemBg.alpha = 0.8;
// Item name text
var nameText = new Text2(item.name, {
size: 60,
fill: 0xFFFFFF
});
nameText.anchor.set(0.5, 0.5);
nameText.y = yOffset - 30;
self.addChild(nameText);
// Item description text
var descText = new Text2(item.description, {
size: 40,
fill: 0xCCCCCC
});
descText.anchor.set(0.5, 0.5);
descText.y = yOffset + 10;
self.addChild(descText);
// Item cost text
var costText = new Text2('$' + item.cost.toFixed(2), {
size: 50,
fill: 0x00FF00
});
costText.anchor.set(0.5, 0.5);
costText.y = yOffset + 50;
self.addChild(costText);
// Store button info for click detection
self.itemButtons.push({
index: i,
bounds: {
x: -400,
y: yOffset - 75,
width: 800,
height: 150
},
costText: costText,
nameText: nameText
});
}
// Back button
var backBtn = new Text2('BACK', {
size: 80,
fill: 0xFFFFFF
});
backBtn.anchor.set(0.5, 0.5);
backBtn.x = 400;
backBtn.y = 600;
self.addChild(backBtn);
self.backBounds = {
x: 300,
y: 550,
width: 200,
height: 100
};
self.show = function () {
// Show shop background
var shopBackground = LK.getAsset('ShopBackground', {
anchorX: 0,
anchorY: 0
});
shopBackground.x = 0;
shopBackground.y = 0;
shopBackground.width = 2048;
shopBackground.height = 2732;
shopBackground.zIndex = 99;
self.shopBg = shopBackground;
game.addChild(shopBackground);
self.isVisible = true;
self.visible = true;
self.x = 1024;
self.y = 1366;
self.zIndex = 100;
// Update item availability based on money
for (var i = 0; i < self.itemButtons.length; i++) {
var button = self.itemButtons[i];
var item = self.shopItems[button.index];
if (currentMoney >= item.cost && !item.purchased) {
button.costText.fill = 0x00FF00;
button.nameText.fill = 0xFFFFFF;
} else if (item.purchased) {
button.costText.setText('OWNED');
button.costText.fill = 0xFFD700;
button.nameText.fill = 0xFFD700;
} else {
button.costText.fill = 0xFF0000;
button.nameText.fill = 0x888888;
}
}
};
self.hide = function () {
// Remove shop background
if (self.shopBg) {
self.shopBg.destroy();
self.shopBg = null;
}
self.isVisible = false;
self.visible = false;
};
self.handleClick = function (localX, localY) {
// Check back button
if (localX >= self.backBounds.x && localX <= self.backBounds.x + self.backBounds.width && localY >= self.backBounds.y && localY <= self.backBounds.y + self.backBounds.height) {
self.hide();
return true;
}
// Check item buttons
for (var i = 0; i < self.itemButtons.length; i++) {
var button = self.itemButtons[i];
var bounds = button.bounds;
if (localX >= bounds.x && localX <= bounds.x + bounds.width && localY >= bounds.y && localY <= bounds.y + bounds.height) {
var item = self.shopItems[button.index];
// Check if can purchase
if (currentMoney >= item.cost && !item.purchased) {
self.purchaseItem(button.index);
return true;
}
}
}
return false;
};
self.purchaseItem = function (itemIndex) {
var item = self.shopItems[itemIndex];
// Deduct money
currentMoney -= item.cost;
storage.money = currentMoney;
moneyTxt.setText('$' + currentMoney.toFixed(2));
// Apply item effect
switch (item.type) {
case 'speed':
// Increase shot bar speed for all bars
shotBar.moveSpeed *= 1.25;
shotBar2.moveSpeed *= 1.25;
shotBar3.moveSpeed *= 1.25;
break;
case 'perfect':
// Grant 3 guaranteed perfect shots
perfectShotsRemaining = 3;
break;
case 'money':
// Double money for next 10 baskets
moneyMultiplierBaskets = 10;
break;
case 'time':
// Add time based on game mode (15 seconds for rhythm, 30 seconds for swish)
if (gameMode === 'rhythm') {
gameTimer += 15 * 60; // 15 seconds at 60 FPS for rhythm mode
gameTimeLeft += 15;
} else {
gameTimer += 30 * 60; // 30 seconds at 60 FPS for swish mode
gameTimeLeft += 30;
}
updateTimerDisplay(gameTimeLeft);
break;
}
// Mark as purchased (except consumables)
if (item.type !== 'perfect' && item.type !== 'money' && item.type !== 'time') {
item.purchased = true;
}
// Visual feedback
LK.effects.flashScreen(0x00FF00, 300);
// Play cash sound when item is purchased
LK.getSound('Cash').play();
// Update display
self.show();
};
return self;
});
var ShotBar = Container.expand(function () {
var self = Container.call(this);
if (gameMode === 'rhythm') {
// In rhythm mode, create horizontal tap zone instead of vertical shot bar
var tapZone = self.attachAsset('shotBarBg', {
anchorX: 0.5,
anchorY: 0.5
});
// Keep horizontal orientation for tap zone
tapZone.scaleX = 1.2; // Make it wider for easier tapping
tapZone.scaleY = 2.0; // Make it taller for easier tapping
tapZone.tint = 0x00FF00; // Green color for tap zone
tapZone.alpha = 0.7; // Semi-transparent
} else {
// Swish mode: vertical shot bar (original implementation)
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.greenZone = greenZone;
self.indicator = indicator;
}
self.isActive = false;
self.isMoving = false;
self.moveDirection = 1; // 1 for right, -1 for left
self.moveSpeed = 8;
self.maxDistance = 240; // Half of bar width minus indicator size
self.perfectShot = false;
self.startMoving = function () {
if (gameMode === 'rhythm') {
// In rhythm mode, tap zone is always active and visible
self.isActive = true;
self.visible = true;
return;
}
// Swish mode logic
self.isActive = true;
self.isMoving = true;
self.perfectShot = false;
// Position green zone randomly vertically
if (self.greenZone) {
self.greenZone.y = (Math.random() - 0.5) * 360; // Random position within vertical bar
}
// Reset indicator position vertically - start at top
if (self.indicator) {
self.indicator.y = -self.maxDistance;
}
self.moveDirection = 1; // Start moving downward
self.visible = true;
};
self.stopMoving = function () {
if (gameMode === 'rhythm') {
// In rhythm mode, always return perfect shot for tap zone
self.perfectShot = true;
return true;
}
// Swish mode logic
self.isMoving = false;
// Check if indicator is within green zone vertically
if (self.greenZone && self.indicator) {
var greenTop = self.greenZone.y - 60;
var greenBottom = self.greenZone.y + 60;
self.perfectShot = self.indicator.y >= greenTop && self.indicator.y <= greenBottom;
}
// Use perfect shot bonus if available
if (perfectShotsRemaining > 0 && !self.perfectShot) {
self.perfectShot = true;
perfectShotsRemaining--;
}
// Reset streak if player misses the green zone
if (!self.perfectShot) {
currentStreak = 0;
streakTxt.setText('Streak: ' + currentStreak);
streakRewardsAwarded = []; // Reset streak rewards
}
// 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 (gameMode === 'rhythm' || !self.isMoving) return;
// Increase speed every 50 points scored
var currentSpeed = self.moveSpeed;
var scoreMultiplier = Math.floor(LK.getScore() / 50);
if (scoreMultiplier > 0) {
currentSpeed = self.moveSpeed * (1 + scoreMultiplier * 0.5); // 50% speed increase per 50 points
}
// Move indicator back and forth vertically
if (self.indicator) {
self.indicator.y += currentSpeed * self.moveDirection;
// Bounce off edges
if (self.indicator.y >= self.maxDistance) {
self.indicator.y = self.maxDistance;
self.moveDirection = -1;
} else if (self.indicator.y <= -self.maxDistance) {
self.indicator.y = -self.maxDistance;
self.moveDirection = 1;
}
}
};
self.visible = true;
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x87CEEB
});
/****
* Game Code
****/
function showHowToPlay() {
// Create semi-transparent overlay
var overlay = game.addChild(LK.getAsset('Player4', {
anchorX: 0,
anchorY: 0,
scaleX: 20.48,
scaleY: 27.32
}));
overlay.x = 0;
overlay.y = 0;
overlay.tint = 0x000000;
overlay.alpha = 0.8;
overlay.zIndex = 50;
// Create instructions text
var instructionsText = new Text2('HOW TO PLAY\n\n' + 'SWISH MODE:\n' + '• Tap to shoot basketballs\n' + '• Hit perfect timing for swish shots\n' + '• Score baskets to increase streak\n' + '• Match backboard colors for bonus\n' + '• Unlock new players with money\n\n' + 'RHYTHM MODE:\n' + '• Tap falling circles in the green zones\n' + '• Perfect timing gives bonus points\n' + '• Maintain rhythm for multipliers\n\n' + 'TAP TO CLOSE', {
size: 80,
fill: 0xFFFFFF
});
instructionsText.anchor.set(0.5, 0.5);
instructionsText.x = 1024;
instructionsText.y = 1366;
instructionsText.zIndex = 51;
game.addChild(instructionsText);
// Store references for cleanup
game.instructionsOverlay = overlay;
game.instructionsText = instructionsText;
}
function hideHowToPlay() {
if (game.instructionsOverlay) {
game.instructionsOverlay.destroy();
game.instructionsOverlay = null;
}
if (game.instructionsText) {
game.instructionsText.destroy();
game.instructionsText = null;
}
}
// Title screen is now initialized - gameplay will be initialized when start is pressed;
var gameState = 'title'; // 'title', 'playing', 'modeSelect'
var gameMode = 'swish'; // 'swish' or 'rhythm'
var modeSelectBackground = null;
var swishButton = null;
var rhythmButton = null;
var titleBackground = null;
var titleText = null;
var startButton = null;
var basketballs = [];
var ballReflections = [];
var hoop = null;
var leftHoop = null;
var rightHoop = null;
var ground = null;
var scoreTxt = null;
var streakTxt = null;
var timerTxt = null;
var player1 = null;
var player1down = null;
var player2 = null;
var player2down = null;
var player3 = null;
var player3down = null;
var currentMoney = 0;
var moneyTxt = null;
var multiplierTxt = null;
var bestScore = 0;
var bestScoreTxt = null;
var swishBestScore = 0;
var rhythmBestScore = 0;
var currentStreak = 0;
var streakRewardsAwarded = []; // Track which streak rewards have been given
var swipeStartX = 0;
var swipeStartY = 0;
var swipeStartTime = 0;
var isSwipeStarted = false;
var lastShotTime = 0;
var gameTimer = 60 * 60; // Will be set correctly in initializeGameplay
var gameTimeLeft = 60; // Will be set correctly in initializeGameplay
var gameEnded = false;
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;
var moneyAwarded = false; // Track if money has been awarded for current color match
var player2Unlocked = storage.player2Unlocked || false;
var player3Unlocked = storage.player3Unlocked || false;
var unlockCost = 100.00;
// Shop variables
var shop = null;
var perfectShotsRemaining = 0;
var moneyMultiplierBaskets = 0;
// Rhythm mode variables
var beatInterval = 60; // 60 frames = 1 second at 60 FPS
var lastBeatTime = 0;
var beatTolerance = 10; // frames of tolerance for rhythm timing
var rhythmStreak = 0;
var beatIndicator = null;
var rhythmMultiplier = 1;
var rhythmCircles = [];
var rhythmCircleSpawnTimer = 0;
var rhythmCircleSpawnInterval = 29; // Spawn at 124 BPM (29 frames = ~124 beats per minute)
var tapZoneGreenBalls = []; // Array to hold green balls in tap zones
var volumeVisualizerBars = []; // Array to hold volume visualizer bars
// Global timer variables
var timerNumbers = [];
// Global function to update timer display with number assets
function updateTimerDisplay(timeLeft) {
if (timerNumbers.length === 0) return; // Safety check
// Display seconds only (2 digits)
var digits = [Math.floor(timeLeft / 10),
// Tens digit
timeLeft % 10 // Units digit
];
for (var i = 0; i < timerNumbers.length && i < digits.length; i++) {
var digitValue = digits[i].toString();
var newAsset = LK.getAsset(digitValue, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 0.5
});
// Get the stored container reference
var container = timerNumbers[i].parentContainer;
if (container) {
// Replace the existing number with new asset
container.removeChild(timerNumbers[i]);
newAsset.x = [-30, 30][i];
newAsset.y = 90;
newAsset.parentContainer = container; // Store container reference
timerNumbers[i] = newAsset;
container.addChild(newAsset);
}
}
}
// Initialize title screen
initializeTitleScreen();
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 initializeTitleScreen() {
// Stop all music when returning to title
LK.stopMusic();
// Clean up tap zone green balls
for (var i = 0; i < tapZoneGreenBalls.length; i++) {
if (tapZoneGreenBalls[i]) {
tapZoneGreenBalls[i].destroy();
}
}
tapZoneGreenBalls = [];
// Clean up volume visualizer bars
for (var i = 0; i < volumeVisualizerBars.length; i++) {
if (volumeVisualizerBars[i]) {
volumeVisualizerBars[i].destroy();
}
}
volumeVisualizerBars = [];
// Create title background
titleBackground = game.addChild(LK.getAsset('TitleBackground', {
anchorX: 0,
anchorY: 0
}));
titleBackground.x = 0;
titleBackground.y = 0;
titleBackground.width = 2048;
titleBackground.height = 2732;
titleBackground.zIndex = -5;
// Create game title using asset
titleText = game.addChild(LK.getAsset('Title', {
anchorX: 0.5,
anchorY: 0.5
}));
titleText.x = 1024;
titleText.y = 1000;
titleText.zIndex = 10;
// Create start button using tap to start asset
startButton = game.addChild(LK.getAsset('Taptostart', {
anchorX: 0.5,
anchorY: 0.5
}));
startButton.x = 1294;
startButton.y = 1150;
startButton.zIndex = 10;
// Create how to play button using asset
var howToPlayButton = game.addChild(LK.getAsset('Howtoplay', {
anchorX: 0.5,
anchorY: 0.5
}));
howToPlayButton.x = 200;
howToPlayButton.y = 2550;
howToPlayButton.zIndex = 10;
// Store reference for cleanup
game.howToPlayButton = howToPlayButton;
// Create shop button using Shop asset
var shopButton = game.addChild(LK.getAsset('Shop', {
anchorX: 0.5,
anchorY: 0.5
}));
shopButton.x = 1848;
shopButton.y = 2550;
shopButton.zIndex = 10;
// Store reference for cleanup
game.shopButton = shopButton;
// Play make a wish sound when title screen loads
LK.getSound('Makeaswish').play();
// Animate start button pulsate continuously
function pulsateStartButton() {
if (!startButton || gameState !== 'title') return; // Safety check
tween(startButton, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 800,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (!startButton || gameState !== 'title') return; // Safety check
// Create continuous pulsating loop
tween(startButton, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 800,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (!startButton || gameState !== 'title') return; // Safety check
// Restart the pulsate cycle
pulsateStartButton();
}
});
}
});
}
pulsateStartButton();
}
function initializeModeSelection() {
// Hide title screen elements
if (titleBackground) {
titleBackground.destroy();
titleBackground = null;
}
if (titleText) {
titleText.destroy();
titleText = null;
}
if (startButton) {
tween.stop(startButton);
startButton.destroy();
startButton = null;
}
if (game.howToPlayButton) {
game.howToPlayButton.destroy();
game.howToPlayButton = null;
}
if (game.shopButton) {
game.shopButton.destroy();
game.shopButton = null;
}
// Hide instructions if showing
hideHowToPlay();
// Create mode selection background
modeSelectBackground = game.addChild(LK.getAsset('TitleBackground', {
anchorX: 0,
anchorY: 0
}));
modeSelectBackground.x = 0;
modeSelectBackground.y = 0;
modeSelectBackground.width = 2048;
modeSelectBackground.height = 2732;
modeSelectBackground.zIndex = -5;
// Create mode selection title
var modeTitle = new Text2('SELECT MODE', {
size: 120,
fill: 0xFFD700
});
modeTitle.anchor.set(0.5, 0.5);
modeTitle.x = 1024;
modeTitle.y = 800;
modeTitle.zIndex = 10;
game.addChild(modeTitle);
// Create Swish mode button
swishButton = game.addChild(LK.getAsset('Swish', {
anchorX: 0.5,
anchorY: 0.5
}));
swishButton.x = 1024;
swishButton.y = 1200;
swishButton.zIndex = 10;
// Create Rhythm mode button
rhythmButton = new Text2('RHYTHM', {
size: 100,
fill: 0xFF6B6B
});
rhythmButton.anchor.set(0.5, 0.5);
rhythmButton.x = 1024;
rhythmButton.y = 1600;
rhythmButton.zIndex = 10;
game.addChild(rhythmButton);
// Change game state to mode selection
gameState = 'modeSelect';
// Animate buttons
function animateModeButtons() {
if (gameState !== 'modeSelect') return;
tween(swishButton, {
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 600,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (gameState !== 'modeSelect') return;
tween(swishButton, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 600,
easing: tween.easeInOut,
onFinish: animateModeButtons
});
}
});
}
animateModeButtons();
}
function initializeGameplay() {
// Hide title screen elements
if (titleBackground) {
titleBackground.destroy();
titleBackground = null;
}
if (titleText) {
titleText.destroy();
titleText = null;
}
if (startButton) {
// Stop any active tweens on the start button before destroying
tween.stop(startButton);
startButton.destroy();
startButton = null;
}
// Hide mode selection elements
if (modeSelectBackground) {
modeSelectBackground.destroy();
modeSelectBackground = null;
}
if (swishButton) {
tween.stop(swishButton);
swishButton.destroy();
swishButton = null;
}
if (rhythmButton) {
rhythmButton.destroy();
rhythmButton = null;
}
// Initialize all game elements (moved from main game code)
// Create shop instance
shop = game.addChild(new Shop());
// Create multiplier indicator text
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;
background.width = 2048;
background.height = 2732;
background.zIndex = -10;
// Create hoops
hoop = game.addChild(new Hoop());
hoop.x = 1024;
hoop.y = 1300;
leftHoop = game.addChild(new Hoop());
leftHoop.x = 1024 - 375 - 350;
leftHoop.y = 1300;
rightHoop = game.addChild(new Hoop());
rightHoop.x = 1024 + 375 + 350;
rightHoop.y = 1300;
// Create UI displays
scoreTxt = new Text2('Score: 0', {
size: 80,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
streakTxt = new Text2('Streak: 0', {
size: 60,
fill: 0xFFD700
});
streakTxt.anchor.set(0.5, 0);
streakTxt.y = 100;
LK.gui.top.addChild(streakTxt);
// Create timer using number assets instead of text
timerNumbers = []; // Use global variable
var timerContainer = new Container();
timerContainer.x = 0;
timerContainer.y = 160;
LK.gui.top.addChild(timerContainer);
// Create shotclock background for timer numbers
var timerBg = LK.getAsset('Shotclock', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 0.5
});
timerBg.x = 0;
timerBg.y = 140;
timerContainer.addChild(timerBg);
// Create 2 number positions for seconds only
var numberPositions = [-30, 30]; // Positions for tens and units
// Initialize number assets
for (var i = 0; i < 2; i++) {
// 2 number positions for seconds
var numberAsset = LK.getAsset('0', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 0.5
});
numberAsset.x = numberPositions[i];
numberAsset.y = 90;
numberAsset.parentContainer = timerContainer; // Store container reference
timerNumbers.push(numberAsset);
timerContainer.addChild(numberAsset);
}
// Initialize timer display with correct values for each mode
if (gameMode === 'rhythm') {
gameTimer = 15 * 60; // 15 seconds at 60 FPS
gameTimeLeft = 15;
updateTimerDisplay(15);
} else {
gameTimer = 60 * 60; // 60 seconds at 60 FPS
gameTimeLeft = 60;
updateTimerDisplay(60);
}
// Separate best scores for each mode
swishBestScore = storage.swishBestScore || 0;
rhythmBestScore = storage.rhythmBestScore || 0;
bestScore = gameMode === 'rhythm' ? rhythmBestScore : swishBestScore;
bestScoreTxt = new Text2('Best: ' + bestScore, {
size: 60,
fill: 0xFFD700
});
bestScoreTxt.anchor.set(1, 0);
bestScoreTxt.x = -100;
LK.gui.topRight.addChild(bestScoreTxt);
currentMoney = storage.money || 0;
moneyTxt = new Text2('$' + currentMoney.toFixed(2), {
size: 50,
fill: 0x00FF00
});
moneyTxt.anchor.set(0, 0);
moneyTxt.x = 120;
moneyTxt.y = 10;
LK.gui.topLeft.addChild(moneyTxt);
// Create shot bars
shotBar = game.addChild(new ShotBar());
shotBar.x = 1024;
shotBar.y = 1600;
shotBar2 = game.addChild(new ShotBar());
shotBar2.x = 300;
shotBar2.y = 1600;
shotBar3 = game.addChild(new ShotBar());
shotBar3.x = rightHoop.x;
shotBar3.y = 1600;
// Add players
player1 = game.addChild(LK.getAsset('Player1', {
anchorX: 0.5,
anchorY: 1
}));
player1.x = 1024;
player1.y = 2732;
player1.zIndex = 1;
player1.visible = false;
player1down = game.addChild(LK.getAsset('Player1down', {
anchorX: 0.5,
anchorY: 1
}));
player1down.x = 1024;
player1down.y = 2732;
player1down.zIndex = 1;
player1down.visible = true;
player2 = game.addChild(LK.getAsset('Player2', {
anchorX: 0.5,
anchorY: 1
}));
player2.x = 300;
player2.y = 2732;
player2.zIndex = 1;
player2.visible = false;
player2down = game.addChild(LK.getAsset('Player2down', {
anchorX: 0.5,
anchorY: 1
}));
player2down.x = 300;
player2down.y = 2732;
player2down.zIndex = 1;
player2down.visible = true;
player3 = game.addChild(LK.getAsset('Player3', {
anchorX: 0.5,
anchorY: 1
}));
player3.x = 1748;
player3.y = 2732;
player3.zIndex = 1;
player3.visible = false;
player3down = game.addChild(LK.getAsset('Player3down', {
anchorX: 0.5,
anchorY: 1
}));
player3down.x = 1748;
player3down.y = 2732;
player3down.zIndex = 1;
player3down.visible = true;
// Create lock overlays
var player2Lock = null;
var player2UnlockBtn = null;
var player2LockImg = null;
if (!player2Unlocked) {
player2Lock = game.addChild(LK.getAsset('Player4', {
anchorX: 0.5,
anchorY: 1,
scaleX: 6.5,
scaleY: 10
}));
player2Lock.x = 280;
player2Lock.y = 2732;
player2Lock.zIndex = 3;
player2Lock.alpha = 1.0;
player2Lock.tint = 0x000000;
player2LockImg = game.addChild(LK.getAsset('Locked', {
anchorX: 0.5,
anchorY: 0.5
}));
player2LockImg.x = 300;
player2LockImg.y = 1700;
player2LockImg.zIndex = 5;
player2UnlockBtn = new Text2('UNLOCK\n$100', {
size: 40,
fill: 0xFFFFFF
});
player2UnlockBtn.anchor.set(0.5, 0.5);
player2UnlockBtn.x = 300;
player2UnlockBtn.y = 2400;
player2UnlockBtn.zIndex = 4;
game.addChild(player2UnlockBtn);
}
var player3Lock = null;
var player3UnlockBtn = null;
var player3LockImg = null;
if (!player3Unlocked) {
player3Lock = game.addChild(LK.getAsset('Player4', {
anchorX: 0.5,
anchorY: 1,
scaleX: 6.5,
scaleY: 10
}));
player3Lock.x = 1728;
player3Lock.y = 2732;
player3Lock.zIndex = 3;
player3Lock.alpha = 1.0;
player3Lock.tint = 0x000000;
player3LockImg = game.addChild(LK.getAsset('Locked', {
anchorX: 0.5,
anchorY: 0.5
}));
player3LockImg.x = 1748;
player3LockImg.y = 1700;
player3LockImg.zIndex = 5;
player3UnlockBtn = new Text2('UNLOCK\n$100', {
size: 40,
fill: 0xFFFFFF
});
player3UnlockBtn.anchor.set(0.5, 0.5);
player3UnlockBtn.x = 1748;
player3UnlockBtn.y = 2400;
player3UnlockBtn.zIndex = 4;
game.addChild(player3UnlockBtn);
}
// Start shot bars
shotBar.startMoving();
shotBar2.startMoving();
shotBar3.startMoving();
// Initialize rhythm mode elements if in rhythm mode
if (gameMode === 'rhythm') {
// Timer already set above, just initialize rhythm-specific elements
// Create beat indicator
beatIndicator = new Text2('♪', {
size: 200,
fill: 0xFFD700
});
beatIndicator.anchor.set(0.5, 0.5);
beatIndicator.x = 1024;
beatIndicator.y = 400;
beatIndicator.zIndex = 20;
game.addChild(beatIndicator);
// Create three green balls for tap zones
tapZoneGreenBalls = [];
var tapZonePositions = [300, 1024, 1748]; // Left, center, right positions
for (var i = 0; i < tapZonePositions.length; i++) {
var greenBall = game.addChild(LK.getAsset('greenBall', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.0,
scaleY: 1.0
}));
greenBall.x = tapZonePositions[i];
greenBall.y = 1600; // Same Y as shot bars
greenBall.tint = 0x00FF00; // Green tint
greenBall.alpha = 0.8;
greenBall.zIndex = 12;
tapZoneGreenBalls.push(greenBall);
}
// Create volume visualizer bars
volumeVisualizerBars = [];
var barCount = 8; // Number of visualizer bars
var barSpacing = 200;
var startX = 1024 - (barCount - 1) * barSpacing * 0.5; // Center the bars
for (var i = 0; i < barCount; i++) {
var visualizerBar = game.addChild(LK.getAsset('Visualisers', {
anchorX: 0.5,
anchorY: 1,
scaleX: 0.3,
scaleY: 0.1
}));
visualizerBar.x = startX + i * barSpacing;
visualizerBar.y = 300; // Position above beat indicator
visualizerBar.tint = 0x00FFFF; // Cyan color
visualizerBar.alpha = 0.7;
visualizerBar.zIndex = 18;
visualizerBar.baseHeight = 0.1;
visualizerBar.targetHeight = 0.1;
volumeVisualizerBars.push(visualizerBar);
}
// Start beat timing
lastBeatTime = LK.ticks;
}
// Change game state to playing
gameState = 'playing';
}
function spawnRhythmCircle() {
if (gameMode !== 'rhythm') return;
// Randomly choose which shot bar to align with (only unlocked players)
var availableShotBars = [shotBar]; // Always include center
if (player2Unlocked && shotBar2) {
availableShotBars.push(shotBar2);
}
if (player3Unlocked && shotBar3) {
availableShotBars.push(shotBar3);
}
var targetBar = availableShotBars[Math.floor(Math.random() * availableShotBars.length)];
var circle = new RhythmCircle();
circle.x = targetBar.x;
circle.y = 200; // Start from top of screen
circle.targetShotBar = targetBar;
rhythmCircles.push(circle);
game.addChild(circle);
}
function checkRhythmCircleTap(x, y) {
if (gameMode !== 'rhythm') return false;
var tappedCircle = null;
var tappedInGreenZone = false;
for (var i = rhythmCircles.length - 1; i >= 0; i--) {
var circle = rhythmCircles[i];
if (!circle.isActive) continue;
// Check if tap is within circle bounds
var distance = Math.sqrt((x - circle.x) * (x - circle.x) + (y - circle.y) * (y - circle.y));
if (distance <= 120) {
// Increased circle tap radius for better sensitivity
tappedCircle = circle;
// Check if circle is currently in the horizontal tap zone area
var tolerance = 120; // Further increased tolerance for horizontal tap zone for better responsiveness
if (circle.targetShotBar) {
var shotBarY = circle.targetShotBar.y;
tappedInGreenZone = Math.abs(circle.y - shotBarY) <= tolerance;
}
break;
}
}
return {
tapped: tappedCircle !== null,
circle: tappedCircle,
inGreenZone: tappedInGreenZone
};
}
function checkSimultaneousTap(x, y) {
if (gameMode !== 'rhythm') return false;
// Check for rhythm circle tap
var rhythmResult = checkRhythmCircleTap(x, y);
if (!rhythmResult.tapped) return false;
// In rhythm mode, if player taps rhythm circle in tap zone, they score a perfect swish
if (rhythmResult.tapped && rhythmResult.inGreenZone) {
// Perfect swish in rhythm mode!
rhythmStreak += 1; // Increase rhythm streak
rhythmMultiplier = Math.min(3, 1 + Math.floor(rhythmStreak / 5) * 0.5);
// Trigger volume visualizer pulse effect
for (var v = 0; v < volumeVisualizerBars.length; v++) {
var bar = volumeVisualizerBars[v];
var randomHeight = 0.3 + Math.random() * 1.2; // Random height between 0.3 and 1.5
bar.targetHeight = randomHeight;
// Cycle through neon colors
var neonColors = [0xFF0080, 0x00FF80, 0x8000FF, 0xFF8000, 0x0080FF, 0x80FF00, 0xFF0040, 0x40FF00];
var randomColor = neonColors[Math.floor(Math.random() * neonColors.length)];
// Animate bar to target height and change color
tween(bar, {
scaleY: randomHeight,
tint: randomColor
}, {
duration: 150,
easing: tween.easeOut,
onFinish: function () {
// Animate back to base height
tween(this, {
scaleY: 0.1
}, {
duration: 500,
easing: tween.easeIn
});
}.bind(bar)
});
}
// Remove screen flash to prevent excessive flashing
LK.setScore(LK.getScore() + 2); // Same scoring as swish mode
scoreTxt.setText('Score: ' + LK.getScore());
// Check and update rhythm mode best score when scoring
var currentScore = LK.getScore();
if (currentScore > rhythmBestScore) {
rhythmBestScore = currentScore;
storage.rhythmBestScore = rhythmBestScore;
bestScore = rhythmBestScore;
bestScoreTxt.setText('Best: ' + bestScore);
}
// Increase streak for perfect swish
currentStreak++;
streakTxt.setText('Streak: ' + currentStreak);
// Play swish sound
LK.getSound('swish').play();
// Make the player in that row shoot a perfect swish
if (rhythmResult.circle && rhythmResult.circle.targetShotBar) {
var targetBar = rhythmResult.circle.targetShotBar;
var targetHoop = hoop; // Default to center hoop
var activePlayer = 1;
var startX = 1024;
var startY = 2000;
// Determine which player/hoop based on target shot bar
if (targetBar === shotBar2) {
// Left player (Player2)
targetHoop = leftHoop;
activePlayer = 2;
startX = 300;
} else if (targetBar === shotBar3) {
// Right player (Player3)
targetHoop = rightHoop;
activePlayer = 3;
startX = 1748;
}
// Switch player sprites to shooting position
if (activePlayer === 1) {
player1.visible = true;
player1down.visible = false;
LK.setTimeout(function () {
player1.visible = false;
player1down.visible = true;
}, 1000);
} else if (activePlayer === 2) {
player2.visible = true;
player2down.visible = false;
LK.setTimeout(function () {
player2.visible = false;
player2down.visible = true;
}, 1000);
} else if (activePlayer === 3) {
player3.visible = true;
player3down.visible = false;
LK.setTimeout(function () {
player3.visible = false;
player3down.visible = true;
}, 1000);
}
// Calculate perfect shot trajectory with enhanced precision
var targetX = targetHoop.x;
var targetY = targetHoop.y - 350; // Aim directly at hoop center
var deltaX = targetX - startX;
var deltaY = targetY - startY;
// Use optimized timing for guaranteed success
var time = 45; // Slightly faster for better arc
var gravity = 0.5;
// Calculate perfect trajectory velocities with downward bias for clean entry
var velocityX = deltaX / time;
var velocityY = deltaY / time - 0.5 * gravity * time - 1.5; // Extra downward bias
// Fine-tune horizontal velocity to ensure center entry
var horizontalCorrection = (targetX - startX) * 0.02; // Small correction factor
velocityX += horizontalCorrection;
// Create perfect swish basketball
var ball = createBasketball(startX, startY, velocityX, velocityY);
ball.isPerfectShot = true;
ball.guaranteedSwish = true;
// Set additional properties to ensure scoring
ball.hasScored = false; // Ensure it can score
ball.isActive = true; // Ensure it's active
// Add rotation animation
tween(ball, {
rotation: ball.rotation + Math.PI * 4
}, {
duration: 1500,
easing: tween.easeOut
});
}
} else if (rhythmResult.tapped) {
// Rhythm circle tapped but not in tap zone - reset streaks
rhythmStreak = 0;
rhythmMultiplier = 1;
currentStreak = 0;
streakTxt.setText('Streak: ' + currentStreak);
streakRewardsAwarded = []; // Reset streak rewards
// Remove red flash to prevent excessive flashing
}
// Remove tapped circle with animation
if (rhythmResult.circle) {
tween(rhythmResult.circle, {
scaleX: 2.0,
scaleY: 2.0,
alpha: 0
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
rhythmResult.circle.isActive = false;
}
});
}
return rhythmResult.tapped;
}
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
// Award $100.00 if not already awarded for this color match
if (!moneyAwarded) {
currentMoney += 100.00;
storage.money = currentMoney;
moneyTxt.setText('$' + currentMoney.toFixed(2));
moneyAwarded = true;
// Additional visual feedback for money award
LK.effects.flashScreen(0x00FF00, 800); // Green flash for money
LK.getSound('Cash').play(); // Play cash sound when money is awarded
console.log('Awarded $100.00! Total money:', currentMoney);
}
} else if (!allSame && allSameColorMultiplier) {
allSameColorMultiplier = false;
moneyAwarded = false; // Reset money award flag when colors no longer match
console.log('Deactivating 2x multiplier');
multiplierTxt.setText('');
}
}
game.down = function (x, y, obj) {
// Handle title screen clicks
if (gameState === 'title') {
// Check if instructions are showing
if (game.instructionsOverlay) {
hideHowToPlay();
return;
}
// Check if clicked on start button area (tap to start button)
var buttonLeft = startButton.x - 250; // Half of button width
var buttonRight = startButton.x + 250;
var buttonTop = startButton.y - 250; // Half of button height
var buttonBottom = startButton.y + 250;
if (x >= buttonLeft && x <= buttonRight && y >= buttonTop && y <= buttonBottom) {
initializeModeSelection();
return;
}
// Check if clicked on how to play button
if (game.howToPlayButton) {
var howToLeft = game.howToPlayButton.x - 150;
var howToRight = game.howToPlayButton.x + 150;
var howToTop = game.howToPlayButton.y - 150;
var howToBottom = game.howToPlayButton.y + 150;
if (x >= howToLeft && x <= howToRight && y >= howToTop && y <= howToBottom) {
showHowToPlay();
return;
}
}
// Check if clicked on shop button
if (game.shopButton) {
var shopLeft = game.shopButton.x - 200;
var shopRight = game.shopButton.x + 200;
var shopTop = game.shopButton.y - 200;
var shopBottom = game.shopButton.y + 200;
if (x >= shopLeft && x <= shopRight && y >= shopTop && y <= shopBottom) {
// Initialize temporary shop and show it
if (!shop) {
shop = game.addChild(new Shop());
}
shop.show();
return;
}
}
return;
}
// Handle mode selection clicks
if (gameState === 'modeSelect') {
// Check Swish button
var swishLeft = swishButton.x - 200;
var swishRight = swishButton.x + 200;
var swishTop = swishButton.y - 150;
var swishBottom = swishButton.y + 150;
if (x >= swishLeft && x <= swishRight && y >= swishTop && y <= swishBottom) {
gameMode = 'swish';
LK.playMusic('backgroundMusic');
initializeGameplay();
return;
}
// Check Rhythm button
var rhythmLeft = rhythmButton.x - 200;
var rhythmRight = rhythmButton.x + 200;
var rhythmTop = rhythmButton.y - 50;
var rhythmBottom = rhythmButton.y + 50;
if (x >= rhythmLeft && x <= rhythmRight && y >= rhythmTop && y <= rhythmBottom) {
gameMode = 'rhythm';
LK.playMusic('Title');
initializeGameplay();
return;
}
return;
}
// In rhythm mode, only process rhythm circle taps, disable shooting
if (gameMode === 'rhythm') {
checkSimultaneousTap(x, y);
return; // Don't process shooting in rhythm mode
}
if (LK.ticks - lastShotTime < 30) return; // Reduce cooldown for better responsiveness
// Handle shop clicks if shop is visible
if (shop && shop.isVisible) {
var shopPos = shop.toLocal({
x: x,
y: y
});
if (shop.handleClick(shopPos.x, shopPos.y)) {
return;
}
}
// Check for unlock button clicks first - expanded touch area
if (!player2Unlocked && player2UnlockBtn && x >= 150 && x <= 450 && y >= 2200 && y <= 2600) {
if (currentMoney >= unlockCost) {
// Unlock Player2
currentMoney -= unlockCost;
storage.money = currentMoney;
player2Unlocked = true;
storage.player2Unlocked = true;
moneyTxt.setText('$' + currentMoney.toFixed(2));
// Remove lock overlay
if (player2Lock) {
player2Lock.destroy();
player2Lock = null;
}
if (player2LockImg) {
player2LockImg.destroy();
player2LockImg = null;
}
if (player2UnlockBtn) {
player2UnlockBtn.destroy();
player2UnlockBtn = null;
}
LK.effects.flashScreen(0x00FF00, 500);
}
return;
}
if (!player3Unlocked && player3UnlockBtn && x >= 1598 && x <= 1898 && y >= 2200 && y <= 2600) {
if (currentMoney >= unlockCost) {
// Unlock Player3
currentMoney -= unlockCost;
storage.money = currentMoney;
player3Unlocked = true;
storage.player3Unlocked = true;
moneyTxt.setText('$' + currentMoney.toFixed(2));
// Remove lock overlay
if (player3Lock) {
player3Lock.destroy();
player3Lock = null;
}
if (player3LockImg) {
player3LockImg.destroy();
player3LockImg = null;
}
if (player3UnlockBtn) {
player3UnlockBtn.destroy();
player3UnlockBtn = null;
}
LK.effects.flashScreen(0x00FF00, 500);
}
return;
}
// Check if tap is below the top of the backboard (backboards are at y=1300 and extend up ~726 pixels)
// Greatly increased tap sensitivity by expanding the detection area further
var backboardTopY = 1300 - 1100; // Expanded tap area by 374 pixels above backboard (200)
if (y <= backboardTopY) {
return; // Don't allow shooting if tapping above the backboard
}
// 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
if (!player2Unlocked) return; // Block shooting if locked
targetHoop = leftHoop;
currentShotBar = shotBar2;
activePlayer = 2;
} else if (x > 1365) {
// Right third of screen - Player3's area
if (!player3Unlocked) return; // Block shooting if locked
targetHoop = rightHoop;
currentShotBar = shotBar3;
activePlayer = 3;
}
if (currentShotBar.isActive) {
// Stop the shot bar and check for perfect shot
var isPerfect = currentShotBar.stopMoving();
isChargingShot = false;
// Check rhythm timing if in rhythm mode
var onBeat = false;
if (gameMode === 'rhythm') {
var timeSinceLastBeat = LK.ticks - lastBeatTime;
var timeToNextBeat = beatInterval - timeSinceLastBeat;
// Check if shot is within beat tolerance
if (timeSinceLastBeat <= beatTolerance || timeToNextBeat <= beatTolerance) {
onBeat = true;
rhythmStreak++;
rhythmMultiplier = Math.min(3, 1 + Math.floor(rhythmStreak / 5) * 0.5); // Max 3x multiplier
// Remove pink flash to prevent excessive flashing
} else {
rhythmStreak = 0;
rhythmMultiplier = 1;
}
}
// 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 = 45; // Optimized timing for better arc
var gravity = 0.5; // Match basketball gravity
// Calculate initial velocities for perfect arc
velocityX = deltaX / time;
velocityY = deltaY / time - 0.5 * gravity * time - 1.5; // Enhanced downward bias
// Fine-tune for guaranteed swish - ensure ball enters hoop cleanly
var horizontalAdjustment = (targetX - startX) * 0.02; // Small correction factor
var verticalAdjustment = -1; // Additional 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;
// Set additional properties to ensure scoring
ball.hasScored = false; // Ensure it can score
ball.isActive = true; // Ensure it's active
// 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 () {
// Only run gameplay logic when in playing state
if (gameState !== 'playing') {
return;
}
// 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();
}
}
}
// Streak is only reset when player misses a shot (handled in Basketball class)
// No automatic reset when no balls are active
// Update timer
if (!gameEnded) {
gameTimer--;
var newTimeLeft = Math.floor(gameTimer / 60);
if (newTimeLeft !== gameTimeLeft && newTimeLeft >= 0) {
gameTimeLeft = newTimeLeft;
// Update timer display using number assets
updateTimerDisplay(gameTimeLeft);
}
// Check if time is up
if (gameTimer <= 0 && !gameEnded) {
gameEnded = true;
updateTimerDisplay(0);
// Check and update best score for current mode
var currentScore = LK.getScore();
if (gameMode === 'rhythm') {
if (currentScore > rhythmBestScore) {
rhythmBestScore = currentScore;
storage.rhythmBestScore = rhythmBestScore;
bestScore = rhythmBestScore;
bestScoreTxt.setText('Best: ' + bestScore);
}
} else {
if (currentScore > swishBestScore) {
swishBestScore = currentScore;
storage.swishBestScore = swishBestScore;
bestScore = swishBestScore;
bestScoreTxt.setText('Best: ' + bestScore);
}
}
LK.showGameOver();
}
}
// Rhythm mode beat system
if (gameMode === 'rhythm' && beatIndicator) {
// Check for beat timing
if (LK.ticks - lastBeatTime >= beatInterval) {
lastBeatTime = LK.ticks;
// Animate beat indicator
tween(beatIndicator, {
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(beatIndicator, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 400,
easing: tween.easeIn
});
}
});
// Play beat sound
LK.getSound('1').play();
}
// Rhythm circle spawning and management
if (gameMode === 'rhythm') {
rhythmCircleSpawnTimer++;
if (rhythmCircleSpawnTimer >= rhythmCircleSpawnInterval) {
rhythmCircleSpawnTimer = 0;
spawnRhythmCircle();
}
// Update and cleanup rhythm circles
for (var i = rhythmCircles.length - 1; i >= 0; i--) {
var circle = rhythmCircles[i];
if (!circle.isActive) {
circle.destroy();
rhythmCircles.splice(i, 1);
} else {
// Check if circle has passed the tap zone without being tapped correctly
if (circle.targetShotBar && circle.hasPassed && circle.wasInGreenZone) {
// Circle was in green zone but wasn't tapped correctly - reset streak
currentStreak = 0;
streakTxt.setText('Streak: ' + currentStreak);
streakRewardsAwarded = []; // Reset streak rewards
rhythmStreak = 0;
rhythmMultiplier = 1;
// Mark as inactive to prevent multiple resets
circle.isActive = false;
}
}
}
}
}
// Win condition removed - no score target
};
// Title screen is now initialized - gameplay will be initialized when start is pressed ===================================================================
--- original.js
+++ change.js
@@ -603,12 +603,13 @@
size: 80,
fill: 0xFFFFFF
});
backBtn.anchor.set(0.5, 0.5);
+ backBtn.x = 400;
backBtn.y = 600;
self.addChild(backBtn);
self.backBounds = {
- x: -100,
+ x: 300,
y: 550,
width: 200,
height: 100
};
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
Make button teal and purple text
A UI button that says Ai Battle Mode in sports ESPN style fonts. In-Game asset. 2d. High contrast. No shadows
1
Sound effect
2
Sound effect
3
Sound effect
swish
Sound effect
bounce
Sound effect
backgroundMusic
Music
Makeaswish
Sound effect
Title
Music
Second
Music
Third
Music
Fourth
Music
Cash
Sound effect
Hypersonic
Music
swim
Music
Sunrise
Music
Onfire
Sound effect
Speedup
Sound effect
Fifth
Music