User prompt
Add borders to make the balls follow a path the ro gates
User prompt
Make division max 2 and make multiply max 5
User prompt
Every restart gates should randomized both in position and modifiers
User prompt
Add more variety modifiers to the gates
User prompt
If two gates crashes with each other, make the go opposite directions
User prompt
Make the balls go slower and balls can spread after passing the gate
User prompt
On side of the gates, add physical borders
User prompt
Make the bottom gate as wide as the screen
User prompt
Add more gates
User prompt
Make the gates move side to side but they shouldnt overlap with each other
User prompt
Gates shouldnt collide with each other
User prompt
Make the balls go slower and gates can move side to side, add physical borders to the gates. The collecter cup needs to be as big as the screen from side to side
User prompt
Make the gates side by side and add more gates
User prompt
Gates needs to be bigger and the collective cups needs to have a border
User prompt
The gates needs to be positioned randomly
User prompt
As the balls multiply, show it in real time
Code edit (1 edits merged)
Please save this source code
User prompt
Cup Drop Multiplier
Initial prompt
I have cup in my hand and I empty the cup from top of the screen, Balls should be exiting from that cup in flow. I can select where I can empty the cup from top of the screen. As the balls are going down, they are passing from gates that multiplies or divides the number of the balls. At the bottom of the screen make another cup that collects the balls afterwards. Count all the balls
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // Ball class var Ball = Container.expand(function () { var self = Container.call(this); var ballGfx = self.attachAsset('ball', { anchorX: 0.5, anchorY: 0.5 }); self.radius = ballGfx.width / 2; self.vx = 0; self.vy = 0; self.active = true; self.update = function () { if (!self.active) return; // Simple gravity self.vy += 0.5; // slower gravity self.x += self.vx; self.y += self.vy; }; // Used to mark as collected or destroyed self.deactivate = function () { self.active = false; }; return self; }); // Bottom cup class (collector) var CupBottom = Container.expand(function () { var self = Container.call(this); // Add border using a slightly larger box behind var border = self.attachAsset('cupBottom', { anchorX: 0.5, anchorY: 0.5, width: 2048 + 16, height: 100 + 16, color: 0x222222 }); var cupGfx = self.attachAsset('cupBottom', { anchorX: 0.5, anchorY: 0.5 }); self.setChildIndex(border, 0); self.width = cupGfx.width; self.height = cupGfx.height; return self; }); // Top cup class (movable) var CupTop = Container.expand(function () { var self = Container.call(this); var cupGfx = self.attachAsset('cupTop', { anchorX: 0.5, anchorY: 0.5 }); self.width = cupGfx.width; self.height = cupGfx.height; // Add border using a slightly larger box behind var border = self.attachAsset('cupTop', { anchorX: 0.5, anchorY: 0.5, width: cupGfx.width + 12, height: cupGfx.height + 12, color: 0x222222 }); self.setChildIndex(border, 0); return self; }); // Gate class (multiplier or divider) var Gate = Container.expand(function () { var self = Container.call(this); self.type = 'mult'; // 'mult' or 'div' self.value = 2; // e.g. 2x or /2 self.width = 180; self.height = 40; self.gfx = null; self.label = null; self.leftBorder = null; self.rightBorder = null; self.init = function (type, value) { self.type = type; self.value = value; if (type === 'mult') { self.gfx = self.attachAsset('gateMult', { anchorX: 0.5, anchorY: 0.5 }); } else if (type === 'div') { self.gfx = self.attachAsset('gateDiv', { anchorX: 0.5, anchorY: 0.5 }); } else if (type === 'reverse') { self.gfx = self.attachAsset('gateDiv', { anchorX: 0.5, anchorY: 0.5, color: 0x3498db // blue for reverse }); } else if (type === 'speed') { self.gfx = self.attachAsset('gateMult', { anchorX: 0.5, anchorY: 0.5, color: 0x27ae60 // green for speed }); } else if (type === 'bounce') { self.gfx = self.attachAsset('gateMult', { anchorX: 0.5, anchorY: 0.5, color: 0xe74c3c // red for bounce }); } var txt = ''; if (type === 'mult') { txt = 'x' + value; } else if (type === 'div') { txt = '÷' + value; } else if (type === 'reverse') { txt = 'REV'; } else if (type === 'speed') { txt = value < 1 ? value + 'x SLOW' : value + 'x FAST'; } else if (type === 'bounce') { txt = 'BOUNCE'; } self.label = new Text2(txt, { size: 60, fill: 0xFFFFFF }); self.label.anchor.set(0.5, 0.5); self.addChild(self.label); // Add physical vertical borders to the sides of the gate // Use a tall, thin box for each border var borderWidth = 32; var borderHeight = self.gfx.height + 60; self.leftBorder = self.attachAsset('cupTop', { anchorX: 0.5, anchorY: 0.5, width: borderWidth, height: borderHeight, color: 0x222222 }); self.rightBorder = self.attachAsset('cupTop', { anchorX: 0.5, anchorY: 0.5, width: borderWidth, height: borderHeight, color: 0x222222 }); // Position borders at left/right edge of gate self.leftBorder.x = -self.gfx.width / 2 - borderWidth / 2 + 2; self.leftBorder.y = 0; self.rightBorder.x = self.gfx.width / 2 + borderWidth / 2 - 2; self.rightBorder.y = 0; // Ensure borders are behind the gate graphic self.setChildIndex(self.leftBorder, 0); self.setChildIndex(self.rightBorder, 0); }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x222b3a }); /**** * Game Code ****/ // Game constants // Top cup (where balls are dropped from) // Bottom cup (where balls are collected) // Ball // Multiplier gate // Divider gate var GAME_WIDTH = 2048; var GAME_HEIGHT = 2732; var BALL_DROP_COUNT = 10; var BALL_DROP_INTERVAL = 8; // frames between balls // Randomize gate positions and types var GATE_COUNT = 6; var GATE_MIN_Y = 500; var GATE_MAX_Y = GAME_HEIGHT - 600; var GATE_SPACING_MIN = 300; var GATE_SPACING_MAX = 500; var GATE_TYPES = [{ type: 'mult', values: [2, 3, 4, 5] }, { type: 'div', values: [2] }, { type: 'reverse', values: [1] // no value needed, but keep for consistency }, { type: 'speed', values: [0.5, 1.5, 2] // 0.5x (slower), 1.5x, 2x (faster) }, { type: 'bounce', values: [1] // no value needed, but keep for consistency }]; function randomizeGates() { var configs = []; var lastY = GATE_MIN_Y - 200; for (var i = 0; i < GATE_COUNT; i++) { // Randomly pick type var typeIdx = Math.floor(Math.random() * GATE_TYPES.length); var typeObj = GATE_TYPES[typeIdx]; var value = typeObj.values[Math.floor(Math.random() * typeObj.values.length)]; // Randomly space Y var spacing = GATE_SPACING_MIN + Math.floor(Math.random() * (GATE_SPACING_MAX - GATE_SPACING_MIN)); var y = lastY + spacing; if (y > GATE_MAX_Y) y = GATE_MAX_Y; lastY = y; configs.push({ type: typeObj.type, value: value, y: y }); } return configs; } var GATE_CONFIGS = randomizeGates(); // Game state var cupTop, cupBottom; var balls = []; var gates = []; var dropping = false; var dropTimer = 0; var ballsToDrop = 0; var scoreTxt, infoTxt; var collectedCount = 0; var gatePassed = {}; // ball.id -> {gateIndex: true} var ballIdCounter = 0; var dragNode = null; // Add border walls to constrain balls var leftBorder = game.attachAsset('cupTop', { anchorX: 0.5, anchorY: 0.5, width: 60, height: GAME_HEIGHT - 200, color: 0x222222 }); leftBorder.x = 30; leftBorder.y = GAME_HEIGHT / 2; game.addChild(leftBorder); var rightBorder = game.attachAsset('cupTop', { anchorX: 0.5, anchorY: 0.5, width: 60, height: GAME_HEIGHT - 200, color: 0x222222 }); rightBorder.x = GAME_WIDTH - 30; rightBorder.y = GAME_HEIGHT / 2; game.addChild(rightBorder); // Create top cup cupTop = new CupTop(); cupTop.x = GAME_WIDTH / 2; cupTop.y = 250; game.addChild(cupTop); // Create bottom cup cupBottom = new CupBottom(); cupBottom.x = GAME_WIDTH / 2; cupBottom.y = GAME_HEIGHT - 180; game.addChild(cupBottom); // Create gates for (var i = 0; i < GATE_CONFIGS.length; i++) { var g = new Gate(); g.init(GATE_CONFIGS[i].type, GATE_CONFIGS[i].value); g.x = GAME_WIDTH / 2; g.y = GATE_CONFIGS[i].y; // Add movement state g.moveDir = Math.random() < 0.5 ? 1 : -1; // 1:right, -1:left g.moveSpeed = 4 + Math.random() * 2; // px per frame g.lastX = g.x; game.addChild(g); gates.push(g); } // Score text (balls collected) scoreTxt = new Text2('Balls: 0', { size: 120, fill: 0xFFFFFF }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); // Info text (instructions) infoTxt = new Text2('Drag cup & tap to drop balls!', { size: 60, fill: 0xFFFFFF }); infoTxt.anchor.set(0.5, 0); LK.gui.top.addChild(infoTxt); // Position GUI elements scoreTxt.y = 120; infoTxt.y = 260; // Helper: spawn a single ball at cupTop function spawnBall(x, y, vx, vy) { var b = new Ball(); b.x = x; b.y = y; b.vx = vx || 0; b.vy = vy || 0; b.id = ++ballIdCounter; balls.push(b); game.addChild(b); gatePassed[b.id] = {}; return b; } // Start dropping balls function startDrop() { if (dropping) return; dropping = true; ballsToDrop = BALL_DROP_COUNT; dropTimer = 0; infoTxt.setText('Balls dropping...'); } // Handle drag/move of cup function handleMove(x, y, obj) { if (dragNode) { // Clamp cup within screen, avoid top left 100x100 var minX = 150 + dragNode.width / 2; var maxX = GAME_WIDTH - dragNode.width / 2 - 50; dragNode.x = Math.max(minX, Math.min(maxX, x)); } } // Touch/drag events game.down = function (x, y, obj) { // If touch is on cupTop, start drag var local = cupTop.toLocal(game.toGlobal({ x: x, y: y })); if (local.x > -cupTop.width / 2 && local.x < cupTop.width / 2 && local.y > -cupTop.height / 2 && local.y < cupTop.height / 2) { dragNode = cupTop; } }; game.move = function (x, y, obj) { handleMove(x, y, obj); }; game.up = function (x, y, obj) { dragNode = null; // If not dragging, and not dropping, start drop on tap if (!dropping) { startDrop(); } }; // Main update loop game.update = function () { // Handle dropping balls if (dropping && ballsToDrop > 0) { dropTimer--; if (dropTimer <= 0) { // Drop a ball from cupTop center, with smaller random vx and lower initial vy var vx = (Math.random() - 0.5) * 3; var b = spawnBall(cupTop.x, cupTop.y + cupTop.height / 2 + 10, vx, 1); ballsToDrop--; dropTimer = BALL_DROP_INTERVAL; } } // Move gates side to side and prevent overlap for (var i = 0; i < gates.length; i++) { var g = gates[i]; g.lastX = g.x; // Move g.x += g.moveDir * g.moveSpeed; // Clamp to screen var minX = g.gfx.width / 2 + 60; var maxX = GAME_WIDTH - g.gfx.width / 2 - 60; if (g.x < minX) { g.x = minX; g.moveDir = 1; } if (g.x > maxX) { g.x = maxX; g.moveDir = -1; } } // Prevent overlap between gates (simple separation, left-to-right) for (var i = 0; i < gates.length; i++) { for (var j = i + 1; j < gates.length; j++) { var g1 = gates[i]; var g2 = gates[j]; // Only check if y overlap (vertical proximity) if (Math.abs(g1.y - g2.y) < 120) { var minDist = (g1.gfx.width + g2.gfx.width) / 2 + 40; var dx = g2.x - g1.x; if (Math.abs(dx) < minDist) { // Push apart var overlap = minDist - Math.abs(dx); var push = overlap / 2; if (dx === 0) dx = (Math.random() < 0.5 ? -1 : 1) * 0.1; // avoid zero var dir = dx > 0 ? 1 : -1; g1.x -= push * dir; g2.x += push * dir; // Clamp after push var minX1 = g1.gfx.width / 2 + 60; var maxX1 = GAME_WIDTH - g1.gfx.width / 2 - 60; var minX2 = g2.gfx.width / 2 + 60; var maxX2 = GAME_WIDTH - g2.gfx.width / 2 - 60; g1.x = Math.max(minX1, Math.min(maxX1, g1.x)); g2.x = Math.max(minX2, Math.min(maxX2, g2.x)); // Reverse directions if they crash g1.moveDir *= -1; g2.moveDir *= -1; } } } } // Show current number of balls in play (real time, not just collected) scoreTxt.setText('Balls: ' + (collectedCount + balls.length)); for (var i = balls.length - 1; i >= 0; i--) { var b = balls[i]; b.update(); // Check for gate collisions for (var gidx = 0; gidx < gates.length; gidx++) { var g = gates[gidx]; // Only process if not already passed if (!gatePassed[b.id][gidx]) { // Simple AABB collision if (b.x + b.radius > g.x - g.width / 2 && b.x - b.radius < g.x + g.width / 2 && b.y + b.radius > g.y - g.height / 2 && b.y - b.radius < g.y + g.height / 2) { // Mark as passed gatePassed[b.id][gidx] = true; // Apply gate effect if (g.type === 'mult') { // Multiply: spawn (value-1) new balls at same position for (var m = 1; m < g.value; m++) { // Spread balls more: wider vx, add a little vy randomness var spreadVx = (Math.random() - 0.5) * 14; var spreadVy = b.vy * 0.7 + (Math.random() - 0.5) * 2; var nb = spawnBall(b.x, b.y, spreadVx, spreadVy); // Copy gatePassed so new balls don't re-trigger previous gates for (var k in gatePassed[b.id]) { gatePassed[nb.id][k] = true; } } } else if (g.type === 'div') { // Divide: only keep 1/value balls, remove others // Remove this ball if Math.random() > 1/value if (Math.random() > 1 / g.value) { b.deactivate(); } } else if (g.type === 'reverse') { // Reverse: flip vx and vy b.vx = -b.vx; b.vy = -Math.abs(b.vy) * 0.7; // send upward, reduce speed } else if (g.type === 'speed') { // Speed: multiply vy and vx by value b.vx *= g.value; b.vy *= g.value; } else if (g.type === 'bounce') { // Bounce: invert vy, reduce speed, add a little random b.vy = -Math.abs(b.vy) * (0.7 + Math.random() * 0.2); b.vx += (Math.random() - 0.5) * 6; } } } } // Remove deactivated balls if (!b.active) { b.destroy(); balls.splice(i, 1); continue; } // Check for collection in bottom cup if (b.y + b.radius > cupBottom.y - cupBottom.height / 2 && b.x > cupBottom.x - cupBottom.width / 2 && b.x < cupBottom.x + cupBottom.width / 2 && b.y < cupBottom.y + cupBottom.height / 2) { // Collected! collectedCount++; scoreTxt.setText('Balls: ' + collectedCount); b.deactivate(); b.destroy(); balls.splice(i, 1); continue; } // Remove balls that fall off screen if (b.y - b.radius > GAME_HEIGHT + 100) { b.deactivate(); b.destroy(); balls.splice(i, 1); continue; } // Ball-wall collision: bounce off left and right borders var leftWallX = leftBorder.x + leftBorder.width / 2; var rightWallX = rightBorder.x - rightBorder.width / 2; if (b.x - b.radius < leftWallX) { b.x = leftWallX + b.radius; b.vx = Math.abs(b.vx) * 0.8 + Math.random() * 0.5; // bounce right, dampen } if (b.x + b.radius > rightWallX) { b.x = rightWallX - b.radius; b.vx = -Math.abs(b.vx) * 0.8 - Math.random() * 0.5; // bounce left, dampen } } // End condition: all balls dropped and none left in play if (dropping && ballsToDrop === 0 && balls.length === 0) { dropping = false; // At end, show only collected balls as final score scoreTxt.setText('Balls: ' + collectedCount); infoTxt.setText('Final: ' + collectedCount + ' balls!\nTap to play again'); // Show game over (will reset game) LK.setScore(collectedCount); LK.showGameOver(); } }; // Reset state on game restart game.on('reset', function () { // Remove all balls for (var i = 0; i < balls.length; i++) { balls[i].destroy(); } balls = []; gatePassed = {}; ballIdCounter = 0; collectedCount = 0; dropping = false; ballsToDrop = 0; dropTimer = 0; scoreTxt.setText('Balls: 0'); infoTxt.setText('Drag cup & tap to drop balls!'); // Reset cup positions cupTop.x = GAME_WIDTH / 2; cupTop.y = 250; cupBottom.x = GAME_WIDTH / 2; cupBottom.y = GAME_HEIGHT - 180; // Remove old gates from game for (var i = 0; i < gates.length; i++) { gates[i].destroy(); } gates = []; // Randomize new gates GATE_CONFIGS = randomizeGates(); // Create new gates for (var i = 0; i < GATE_CONFIGS.length; i++) { var g = new Gate(); g.init(GATE_CONFIGS[i].type, GATE_CONFIGS[i].value); g.x = GAME_WIDTH / 2; g.y = GATE_CONFIGS[i].y; // Add movement state g.moveDir = Math.random() < 0.5 ? 1 : -1; // 1:right, -1:left g.moveSpeed = 4 + Math.random() * 2; // px per frame g.lastX = g.x; game.addChild(g); gates.push(g); } });
===================================================================
--- original.js
+++ change.js
@@ -236,8 +236,29 @@
var collectedCount = 0;
var gatePassed = {}; // ball.id -> {gateIndex: true}
var ballIdCounter = 0;
var dragNode = null;
+// Add border walls to constrain balls
+var leftBorder = game.attachAsset('cupTop', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ width: 60,
+ height: GAME_HEIGHT - 200,
+ color: 0x222222
+});
+leftBorder.x = 30;
+leftBorder.y = GAME_HEIGHT / 2;
+game.addChild(leftBorder);
+var rightBorder = game.attachAsset('cupTop', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ width: 60,
+ height: GAME_HEIGHT - 200,
+ color: 0x222222
+});
+rightBorder.x = GAME_WIDTH - 30;
+rightBorder.y = GAME_HEIGHT / 2;
+game.addChild(rightBorder);
// Create top cup
cupTop = new CupTop();
cupTop.x = GAME_WIDTH / 2;
cupTop.y = 250;
@@ -461,8 +482,19 @@
b.destroy();
balls.splice(i, 1);
continue;
}
+ // Ball-wall collision: bounce off left and right borders
+ var leftWallX = leftBorder.x + leftBorder.width / 2;
+ var rightWallX = rightBorder.x - rightBorder.width / 2;
+ if (b.x - b.radius < leftWallX) {
+ b.x = leftWallX + b.radius;
+ b.vx = Math.abs(b.vx) * 0.8 + Math.random() * 0.5; // bounce right, dampen
+ }
+ if (b.x + b.radius > rightWallX) {
+ b.x = rightWallX - b.radius;
+ b.vx = -Math.abs(b.vx) * 0.8 - Math.random() * 0.5; // bounce left, dampen
+ }
}
// End condition: all balls dropped and none left in play
if (dropping && ballsToDrop === 0 && balls.length === 0) {
dropping = false;