User prompt
Implement a global high score tracking system for the game with the following features: When the game ends (e.g., when player's score reaches -20 and the game resets), save the player's final score. Compare this score with the currently saved highest score. If the player's score is higher than the saved highest score, update the high score. Display the highest score prominently on the main menu or game over screen for all players to see. Ensure that the high score persists between game sessions and resets only if manually cleared. Optionally, include a timestamp or player ID for the record but keep it simple if unnecessary. This system will motivate players to beat the highest score and provide a competitive edge. ↪💡 Consider importing and using the following plugins: @upit/storage.v1
User prompt
Adjust the placement of the zero-point slots in the scoring area as follows: Place one zero-point slot at the bottom-left corner of the scoring slots. Place another zero-point slot at the bottom-right corner of the scoring slots. All other scoring slots (e.g., 10, 30, 50, 100, 500 points) should be arranged between these two zero-point slots. The zero-point slots should remain fixed at these positions and visually distinct. Ensure that balls falling into these zero-point slots trigger the zero-point logic (no score gain but ball cost deduction).
User prompt
Add a new type of scoring slot called a "Zero Point Trap": Visually, it should look like a regular slot but clearly marked with a "0" or "No Points". When a ball lands in this slot: The player does not gain any score. The game should still subtract the ball cost (e.g., 10 points) from the player's total. This makes the zero-point slot a risk zone: Players want to avoid it. But it adds challenge and makes scoring harder. These zero-point slots should be placed in strategic/random positions to increase difficulty and unpredictability. Ensure that balls landing in this slot do not bounce or redirect to a valid scoring slot (as per previous prompt).
User prompt
Fix the ball behavior to prevent unrealistic bouncing: When the ball enters a scoring slot (the bottom slot area), it should: Immediately stop moving and stay in that slot. Not bounce or roll into another scoring slot after landing. The ball should be considered "landed" once it fully passes into a scoring zone. Scoring must be based only on the first slot the ball enters, no further changes. 🛑 This is to maintain probability integrity and ensure that high-point slots (e.g., 500 pts) are not reached by accident through bouncing.
User prompt
Add a game over condition based on the player's score: If the player's score falls below -20, the game should immediately trigger a Game Over event. When Game Over is triggered: Display a "Game Over" message on the screen. Reset the player's score to 0. Clear any ongoing ball drops or animations. Provide a "Restart" button or automatically restart the game after a short delay (e.g., 2-3 seconds). ✅ Example: Player drops a ball → score becomes -25 → trigger Game Over → reset game state → start fresh.
User prompt
Please implement a point deduction system for each ball drop attempt: Every time a player drops a ball, 10 points should be deducted from their current score, regardless of where the ball lands. If the ball lands in a score bin (e.g., 10, 30, 100, 500), the bin’s score should still be added to the total score. This means: The player loses 10 points by dropping a ball (cost of play). The player gains points depending on the final landing zone. ✅ Example: A ball is dropped → player loses 10 points Ball lands in the 100-point bin → player gains 100 points Total score increases by +90 This system should encourage players to aim carefully and conserve their score, creating a better gameplay balance.
User prompt
Please revert the layout of the point zones back to the previous mirrored layout, where the score bins were symmetrically arranged across the screen. Here’s how it should look: Both the far-left and far-right sides had 10-point bins Next to them (on both sides): 50-point bins Then 100-point bins Then 300-point bins And finally, at the center, a single 500-point bin This symmetrical layout made the game more balanced and visually organized. ⚠️ Do not modify the current score system logic — the score should still increase correctly based on which bin the ball lands in. Only change the visual and spatial arrangement of the bins back to the mirrored layout.
User prompt
Please fix the bug: 'ReferenceError: slotHeight is not defined' in or related to this line: 'if (ball.y + ball.radius >= slotsY - slotHeight / 2) {' Line Number: 398
User prompt
Update the game logic so that each falling ball lands in a score zone based on defined probability weights. For example: 10 points: 60% chance 30 points: 25% chance 100 points: 10% chance 250 points: 4% chance 500 points: 1% chance The ball should fall into one of these zones randomly based on these probabilities, not equally or deterministically. Once the ball lands, the score should immediately increase based on the value of the zone it landed in. For instance, if it lands in the 100-point zone, add 100 to the total score and display the updated score on screen. The player must clearly see which zone the ball landed in and how the score changed. The score update must reflect accurately in real-time.
User prompt
In the current version of the game, the player drops a ball from the top and it randomly falls into one of several score zones at the bottom. Each score zone has a different point value (e.g., 10, 30, 100, 250, 500). However, currently, only the 10-point zone correctly updates the score. The other zones do not update the player's score even when a ball lands in them. Please update the game logic so that every score zone correctly adds its associated score to the total score when a ball lands in it. The update should work dynamically, meaning if more score zones or different values are added later, the system will still work. Make sure the score is displayed correctly on screen immediately after it changes, regardless of which score zone the ball landed in.
Code edit (1 edits merged)
Please save this source code
User prompt
Peg Drop: Lucky Bounce
Initial prompt
Goal: Implement a system where balls fall from the top, bounce off multiple obstacles, and randomly land in bottom reward zones with decreasing probability for higher rewards. System Description: A ball is released from the top of the screen. Below it, there is a grid of small round pegs (placed in a staggered or triangle formation). The ball bounces off pegs as it falls due to gravity, changing direction randomly on each hit. At the bottom, there are multiple scoring slots (e.g. 0 pts, 100 pts, 250 pts, 500 pts). Each slot has a different probability weight: Common rewards (e.g., 0 pts, 100 pts) have higher drop chances. Rare rewards (e.g., 500 pts) have much lower drop chances, e.g., 1% or lower. Weighted Scoring (Example Distribution): 0 pts → 35% chance 100 pts → 30% 250 pts → 20% 400 pts → 10% 500 pts → 5% or less Mechanics & Physics: Use 2D physics with gravity and collision detection. Each peg alters the trajectory of the ball slightly/randomly. Peg positions should be fixed in a pyramid/grid layout to ensure dynamic randomness. Once the ball lands in a scoring slot, display the reward with animation/sound. Optional Enhancements: Use different colors for each reward slot. Add particle/sound effects on landing. Show drop probability (as tooltip or info popup) when user hovers/clicks on reward slots.
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // Ball class var Ball = Container.expand(function () { var self = Container.call(this); var ballAsset = self.attachAsset('ball', { anchorX: 0.5, anchorY: 0.5 }); self.radius = ballAsset.width / 2; self.vx = 0; self.vy = 0; self.isActive = true; self.update = function () { if (!self.isActive) return; // If the ball has landed in a slot, freeze its position and velocity if (self.hasLandedInSlot) { self.vx = 0; self.vy = 0; return; } // Gravity self.vy += 1.2; // Clamp vy if (self.vy > 40) self.vy = 40; // Move self.x += self.vx; self.y += self.vy; // Wall bounce if (self.x < self.radius) { self.x = self.radius; self.vx = -self.vx * 0.7; } if (self.x > 2048 - self.radius) { self.x = 2048 - self.radius; self.vx = -self.vx * 0.7; } }; // Animate on slot win self.celebrate = function (color) { tween(ballAsset, { tint: color }, { duration: 300, easing: tween.easeOut, onFinish: function onFinish() { tween(ballAsset, { tint: 0xf1c40f }, { duration: 400 }); } }); }; return self; }); // Peg class var Peg = Container.expand(function () { var self = Container.call(this); var pegAsset = self.attachAsset('peg', { anchorX: 0.5, anchorY: 0.5 }); return self; }); // Slot class var Slot = Container.expand(function () { var self = Container.call(this); // Slot types: common, rare, epic, legendary self.type = 'common'; self.points = 10; self.prob = 0.5; self.index = 0; self.width = 220; self.height = 80; self.bg = null; self.label = null; self.highlight = null; self.showProb = false; self.init = function (type, points, prob, index) { self.type = type; self.points = points; self.prob = prob; self.index = index; var assetId = 'slot_' + type; self.bg = self.attachAsset(assetId, { anchorX: 0.5, anchorY: 0.5 }); // Custom label for zero-point trap if (type === 'zero') { self.label = new Text2("0", { size: 60, fill: 0xFF6666 }); // Optionally, add a "No Points" sublabel for clarity self.sublabel = new Text2("No Points", { size: 32, fill: 0xFF6666 }); self.sublabel.anchor.set(0.5, 0.5); self.sublabel.y = 32; self.addChild(self.sublabel); } else { self.label = new Text2(points + '', { size: 60, fill: "#fff" }); } self.label.anchor.set(0.5, 0.5); self.label.y = 0; self.addChild(self.label); self.width = self.bg.width; self.height = self.bg.height; }; self.showHighlight = function () { if (self.highlight) return; self.highlight = self.attachAsset('slot_highlight', { anchorX: 0.5, anchorY: 0.5, alpha: 0.18 }); self.setChildIndex(self.highlight, 0); }; self.hideHighlight = function () { if (self.highlight) { self.removeChild(self.highlight); self.highlight = null; } }; self.showProbability = function () { if (self.showProb) return; self.showProb = true; self.probTxt = new Text2(Math.round(self.prob * 100) + "%", { size: 38, fill: "#fff" }); self.probTxt.anchor.set(0.5, 0.5); self.probTxt.y = 38; self.addChild(self.probTxt); }; self.hideProbability = function () { if (!self.showProb) return; self.showProb = false; if (self.probTxt) { self.removeChild(self.probTxt); self.probTxt = null; } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x222b3a }); /**** * Game Code ****/ // visually distinct for zero-point trap // Music // Sound effects // Slot highlight // Slot backgrounds (different colors for rarity) // Ball // Pegs // Play background music LK.playMusic('bgmusic', { fade: { start: 0, end: 1, duration: 1200 } }); // Score display var scoreTxt = new Text2('0', { size: 120, fill: "#fff" }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); // Peg grid parameters var pegRows = 8; var pegsPerRow = [7, 8, 7, 8, 7, 8, 7, 8]; var pegSpacingX = 240; var pegSpacingY = 180; var pegStartY = 400; var pegStartX = 2048 / 2 - pegSpacingX * 3.5; // Slot definitions (mirrored, symmetric layout with zero-point slots at both ends) // 0 - 10 - 50 - 100 - 300 - 500 - 300 - 100 - 50 - 10 - 0 // Probability weights for each slot (should sum to 1.0) var slotDefs = [ // Far left: zero-point trap { type: 'zero', points: 0, prob: 0.08 }, // Next: 10 { type: 'common', points: 10, prob: 0.13 }, // Next: 50 { type: 'rare', points: 50, prob: 0.10 }, // Next: 100 { type: 'epic', points: 100, prob: 0.08 }, // Next: 300 { type: 'legendary', points: 300, prob: 0.07 }, // Center: 500 { type: 'legendary', points: 500, prob: 0.04 }, // Next: 300 { type: 'legendary', points: 300, prob: 0.07 }, // Next: 100 { type: 'epic', points: 100, prob: 0.08 }, // Next: 50 { type: 'rare', points: 50, prob: 0.10 }, // Next: 10 { type: 'common', points: 10, prob: 0.13 }, // Far right: zero-point trap { type: 'zero', points: 0, prob: 0.08 }]; // (Total: 0.08+0.13+0.10+0.08+0.07+0.04+0.07+0.08+0.10+0.13+0.08 = 0.96, close to 1.0) // Slot positions var slotCount = slotDefs.length; var slotWidth = 220; var slotSpacing = 20; var slotHeight = LK.getAsset('slot_common', { anchorX: 0.5, anchorY: 0.5 }).height; var slotsTotalWidth = slotCount * slotWidth + (slotCount - 1) * slotSpacing; var slotsStartX = 2048 / 2 - slotsTotalWidth / 2 + slotWidth / 2; var slotsY = 2732 - 180; // Arrays for game objects var pegs = []; var slots = []; var balls = []; var activeBall = null; var canDrop = true; var lastTouchedSlot = null; // Create pegs grid for (var row = 0; row < pegRows; row++) { var count = pegsPerRow[row]; var y = pegStartY + row * pegSpacingY; var x0 = 2048 / 2 - (count - 1) / 2 * pegSpacingX; for (var col = 0; col < count; col++) { var peg = new Peg(); peg.x = x0 + col * pegSpacingX; peg.y = y; game.addChild(peg); pegs.push(peg); } } // Create slots for (var i = 0; i < slotCount; i++) { var def = slotDefs[i]; var slot = new Slot(); slot.init(def.type, def.points, def.prob, i); slot.x = slotsStartX + i * (slotWidth + slotSpacing); slot.y = slotsY; game.addChild(slot); slots.push(slot); } // Draw slot walls (visual separation) for (var i = 0; i <= slotCount; i++) { var wall = LK.getAsset('peg', { anchorX: 0.5, anchorY: 0.5, width: 30, height: 120, color: 0x34495e }); wall.x = slotsStartX - slotWidth / 2 + i * (slotWidth + slotSpacing); wall.y = slotsY - 40; game.addChild(wall); } // Ball drop area var dropY = 180; var dropX = 2048 / 2; // Drop indicator var dropIndicator = LK.getAsset('ball', { anchorX: 0.5, anchorY: 0.5, alpha: 0.25 }); dropIndicator.x = dropX; dropIndicator.y = dropY; game.addChild(dropIndicator); // Allow dragging drop indicator var draggingDrop = false; game.down = function (x, y, obj) { // Only allow drop if no active ball if (canDrop && y < 400) { draggingDrop = true; moveDropIndicator(x); } }; game.move = function (x, y, obj) { if (draggingDrop) { moveDropIndicator(x); } // Slot hover for probability var slot = getSlotAt(x, y); if (slot !== lastTouchedSlot) { if (lastTouchedSlot) { lastTouchedSlot.hideHighlight(); lastTouchedSlot.hideProbability(); } if (slot) { slot.showHighlight(); slot.showProbability(); } lastTouchedSlot = slot; } }; game.up = function (x, y, obj) { if (draggingDrop) { draggingDrop = false; // Drop ball if (canDrop) { spawnBall(dropIndicator.x, dropIndicator.y); } } // Remove slot highlight if (lastTouchedSlot) { lastTouchedSlot.hideHighlight(); lastTouchedSlot.hideProbability(); lastTouchedSlot = null; } }; function moveDropIndicator(x) { // Clamp to game area var minX = 80; var maxX = 2048 - 80; dropIndicator.x = Math.max(minX, Math.min(maxX, x)); } // Spawn a new ball function spawnBall(x, y) { canDrop = false; // Deduct 10 points for each drop LK.setScore(LK.getScore() - 10); scoreTxt.setText(LK.getScore()); var ball = new Ball(); ball.x = x; ball.y = y; ball.vx = 0; ball.vy = 0; balls.push(ball); activeBall = ball; game.addChild(ball); } // Get slot at position function getSlotAt(x, y) { for (var i = 0; i < slots.length; i++) { var slot = slots[i]; if (x > slot.x - slot.width / 2 && x < slot.x + slot.width / 2 && y > slot.y - slot.height / 2 && y < slot.y + slot.height / 2) { return slot; } } return null; } // Ball-peg collision function ballPegCollision(ball, peg) { var dx = ball.x - peg.x; var dy = ball.y - peg.y; var dist = Math.sqrt(dx * dx + dy * dy); var minDist = ball.radius + peg.width / 2 - 2; if (dist < minDist) { // Nudge ball out var angle = Math.atan2(dy, dx); var overlap = minDist - dist; ball.x += Math.cos(angle) * overlap; ball.y += Math.sin(angle) * overlap; // Bounce var speed = Math.sqrt(ball.vx * ball.vx + ball.vy * ball.vy); var normalAngle = angle; var tangentAngle = angle + Math.PI / 2; // Randomize bounce var bounceAngle = normalAngle + (Math.random() - 0.5) * 0.7; var newVx = Math.cos(bounceAngle) * speed * 0.7; var newVy = Math.sin(bounceAngle) * speed * 0.7; ball.vx = newVx; ball.vy = newVy; // Play sound LK.getSound('peg_hit').play(); } } // Ball-slot detection and reward function checkBallSlot(ball) { // Only trigger once per ball if (!ball.isActive) return false; // Check if ball has reached the slot area (bottom of the screen) if (ball.y + ball.radius >= slotsY - slotHeight / 2) { ball.isActive = false; ball.hasLandedInSlot = true; // Mark as landed to freeze movement // Weighted random selection of slot based on defined probabilities var r = Math.random(); var acc = 0; var chosenSlot = null; for (var i = 0; i < slots.length; i++) { acc += slots[i].prob; if (r < acc) { chosenSlot = slots[i]; break; } } // Fallback in case of floating point error if (!chosenSlot) chosenSlot = slots[slots.length - 1]; // Instantly move ball to chosen slot center and freeze it there ball.x = chosenSlot.x; ball.y = chosenSlot.y; ball.vx = 0; ball.vy = 0; // Celebrate and update score if (chosenSlot.type !== 'zero') { LK.setScore(LK.getScore() + chosenSlot.points); scoreTxt.setText(LK.getScore()); ball.celebrate(0x2ecc40); LK.getSound('slot_win').play(); } else { // No points, visually indicate trap ball.celebrate(0xff6666); // Optionally, play a "fail" sound if available // LK.getSound('fail').play(); // Score already deducted on drop, nothing to add scoreTxt.setText(LK.getScore()); } // Fade out and destroy ball tween(ball, { alpha: 0 }, { duration: 300, onFinish: function onFinish() { ball.destroy(); } }); // Allow next drop after short delay LK.setTimeout(function () { canDrop = true; }, 700); return true; } return false; } // Remove balls that fall out of bounds function cleanupBalls() { for (var i = balls.length - 1; i >= 0; i--) { var ball = balls[i]; if (ball.y > 2732 + 100 || ball.alpha === 0) { ball.destroy(); balls.splice(i, 1); if (activeBall === ball) activeBall = null; canDrop = true; } } } // Game over state var isGameOver = false; var gameOverTxt = null; var restartTimeout = null; function triggerGameOver() { if (isGameOver) return; isGameOver = true; // Show Game Over message if (!gameOverTxt) { gameOverTxt = new Text2("Game Over", { size: 180, fill: 0xFF4444 }); gameOverTxt.anchor.set(0.5, 0.5); } gameOverTxt.x = 2048 / 2; gameOverTxt.y = 2732 / 2 - 100; LK.gui.center.addChild(gameOverTxt); // Reset score LK.setScore(0); scoreTxt.setText("0"); // Remove all balls and stop drops for (var i = balls.length - 1; i >= 0; i--) { if (balls[i] && typeof balls[i].destroy === "function") { balls[i].destroy(); } balls.splice(i, 1); } activeBall = null; canDrop = false; // Remove any slot highlights if (lastTouchedSlot) { lastTouchedSlot.hideHighlight(); lastTouchedSlot.hideProbability(); lastTouchedSlot = null; } // Restart after 2.2 seconds if (restartTimeout) LK.clearTimeout(restartTimeout); restartTimeout = LK.setTimeout(function () { // Remove Game Over text if (gameOverTxt && gameOverTxt.parent) { gameOverTxt.parent.removeChild(gameOverTxt); } // Reset drop indicator dropIndicator.x = dropX; dropIndicator.y = dropY; // Allow new drop canDrop = true; isGameOver = false; // Show score as 0 scoreTxt.setText("0"); }, 2200); } // Main game update game.update = function () { // Check for game over if (!isGameOver && LK.getScore() < -20) { triggerGameOver(); return; } // Update balls for (var i = 0; i < balls.length; i++) { var ball = balls[i]; ball.update(); // Collide with pegs for (var j = 0; j < pegs.length; j++) { ballPegCollision(ball, pegs[j]); } // Check slot if (checkBallSlot(ball)) continue; } cleanupBalls(); }; // Show initial score scoreTxt.setText(LK.getScore());
===================================================================
--- original.js
+++ change.js
@@ -194,76 +194,79 @@
var pegSpacingX = 240;
var pegSpacingY = 180;
var pegStartY = 400;
var pegStartX = 2048 / 2 - pegSpacingX * 3.5;
-// Slot definitions (mirrored, symmetric layout)
-// 10 - 50 - 100 - 300 - 500 - 300 - 100 - 50 - 10
+// Slot definitions (mirrored, symmetric layout with zero-point slots at both ends)
+// 0 - 10 - 50 - 100 - 300 - 500 - 300 - 100 - 50 - 10 - 0
// Probability weights for each slot (should sum to 1.0)
-// Insert zero-point trap slots at strategic positions (e.g., 3rd and 7th slot)
-var slotDefs = [{
+var slotDefs = [
+// Far left: zero-point trap
+{
+ type: 'zero',
+ points: 0,
+ prob: 0.08
+},
+// Next: 10
+{
type: 'common',
points: 10,
- prob: 0.16
+ prob: 0.13
},
-// Far left
+// Next: 50
{
type: 'rare',
points: 50,
- prob: 0.12
+ prob: 0.10
},
-// Next left
+// Next: 100
{
- type: 'zero',
- // Zero-point trap
- points: 0,
- prob: 0.07
-},
-// Next left
-{
type: 'epic',
points: 100,
- prob: 0.09
+ prob: 0.08
},
-// Next left
+// Next: 300
{
type: 'legendary',
+ points: 300,
+ prob: 0.07
+},
+// Center: 500
+{
+ type: 'legendary',
points: 500,
prob: 0.04
},
-// Center
+// Next: 300
{
type: 'legendary',
points: 300,
prob: 0.07
},
-// Next right
+// Next: 100
{
- type: 'zero',
- // Zero-point trap
- points: 0,
- prob: 0.07
-},
-// Next right
-{
type: 'epic',
points: 100,
- prob: 0.09
+ prob: 0.08
},
-// Next right
+// Next: 50
{
type: 'rare',
points: 50,
- prob: 0.12
+ prob: 0.10
},
-// Next right
+// Next: 10
{
type: 'common',
points: 10,
- prob: 0.16
-} // Far right
-];
-// (Total: 0.16+0.12+0.07+0.09+0.04+0.07+0.07+0.09+0.12+0.16 = 0.99, close to 1.0)
-// (Total: 0.18+0.13+0.10+0.07+0.04+0.07+0.10+0.13+0.18 = 1.0)
+ prob: 0.13
+},
+// Far right: zero-point trap
+{
+ type: 'zero',
+ points: 0,
+ prob: 0.08
+}];
+// (Total: 0.08+0.13+0.10+0.08+0.07+0.04+0.07+0.08+0.10+0.13+0.08 = 0.96, close to 1.0)
// Slot positions
var slotCount = slotDefs.length;
var slotWidth = 220;
var slotSpacing = 20;