User prompt
When I step on the 6th die, let the score increase by +2 points per second
User prompt
I'm correcting it - let there be a 2-second penalty
User prompt
When I step on the bomb, let 10 seconds be subtracted during the rewinding time
User prompt
When I step on the bomb, let a penalty be applied during the rewinding time as well, and indicate this
User prompt
The hourglass looks upside down, can you fix it ?
User prompt
Could you code this? I will add the visuals later
User prompt
Let's increase the size of the dice
User prompt
I want to add a photo to the background
Code edit (3 edits merged)
Please save this source code
User prompt
Let the sizing of the bomb and the dice be the same
User prompt
Please fix the bug: 'TypeError: LK.effects.shakeScreen is not a function' in or related to this line: 'LK.effects.shakeScreen(400, 16); // Screen shake: 400ms, 16px amplitude' Line Number: 75
User prompt
and screen shake effect ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
quick flash effect
User prompt
yes
User prompt
Let the trap bomb die behave like the 6th die
User prompt
Let the first die fall more slowly, while the sixth, which is the last die, falls more quickly
User prompt
Let the numbers written on the dice be very small." or "Make the numbers written on the dice very small
User prompt
The scoring should be set so that if the die has an internal value of 1, it gives 1 point, and if it has a value of 6, it gives 6 points
User prompt
Reduce the score when a bomb is tapped
Code edit (1 edits merged)
Please save this source code
User prompt
Please save this source code
User prompt
please save this source code
Code edit (1 edits merged)
Please save this source code
User prompt
Desert Dice Dash
Initial prompt
In a mobile arcade game set against a desert-like background, dice fall randomly from the top. Each die has a value between 1 and 6. The player taps high-value dice to score points. Some dice are traps—bomb dice—that penalize the player when tapped. The goal is to get the highest score within 1 minute. Suggest a game AI logic that manages real-time tap detection, score tracking, and random dice spawning, including rare but strategic appearances of bomb dice.
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // BombDice class var BombDice = Container.expand(function () { var self = Container.call(this); self.isBomb = true; // Attach bomb asset, anchor center self.bombAsset = self.attachAsset('bomb', { anchorX: 0.5, anchorY: 0.5 }); // Add bomb label self.label = new Text2('', { size: 100, fill: 0xFFFFFF }); self.label.anchor.set(0.5, 0.5); self.label.x = 0; self.label.y = 0; self.addChild(self.label); // Called every tick self.update = function () { self.y += self.fallSpeed; }; // On tap self.down = function (x, y, obj) { // Score: subtract 5 (minimum 0) var newScore = LK.getScore() - 5; if (newScore < 0) { newScore = 0; } LK.setScore(newScore); LK.getSound('bombTap').play(); // Set penalty state for 90 ticks (~1.5s at 60fps) isPenalty = true; penaltyTicksLeft = 90; // penaltyTxt.visible = true; // Do not show penalty text // Subtract 2 seconds from timer (minimum 0) timeLeft -= 2; if (timeLeft < 0) { timeLeft = 0; } timerTxt.setText(timeLeft); // Animate: shake and fade out tween(self, { x: self.x + 30 }, { duration: 60, easing: tween.easeOut, onFinish: function onFinish() { tween(self, { x: self.x - 60 }, { duration: 80, easing: tween.easeIn, onFinish: function onFinish() { tween(self, { x: self.x + 30, alpha: 0 }, { duration: 100, onFinish: function onFinish() { self.destroy(); LK.effects.flashScreen(0xffffff, 120); // Quick white flash after explosion // Manual screen shake using tween plugin var originalX = game.x || 0; var originalY = game.y || 0; var shakeDuration = 400; var shakeAmplitude = 16; var shakeTicks = Math.floor(shakeDuration / 16); // ~60fps var shakeCount = 0; function doShake() { if (shakeCount >= shakeTicks) { game.x = originalX; game.y = originalY; return; } // Random offset game.x = originalX + (Math.random() - 0.5) * shakeAmplitude; game.y = originalY + (Math.random() - 0.5) * shakeAmplitude; shakeCount++; LK.setTimeout(doShake, 16); } doShake(); } }); } }); } }); // Remove from dice array in main game removeDice(self); }; return self; }); // Dice class (for dice 1-6) var Dice = Container.expand(function () { var self = Container.call(this); // Dice value (1-6) self.value = 1; self.isBomb = false; // Attach dice asset, anchor center self.diceAsset = null; // Dice label (number) self.label = null; // Set up dice self.setup = function (value) { self.value = value; self.isBomb = false; var assetId = 'dice' + value; self.diceAsset = self.attachAsset(assetId, { anchorX: 0.5, anchorY: 0.5 }); // Add number label self.label = new Text2('' + value, { size: 28, fill: 0x222222 }); self.label.anchor.set(0.5, 0.5); self.label.x = 0; self.label.y = 0; self.addChild(self.label); }; // Called every tick self.update = function () { self.y += self.fallSpeed; }; // On tap self.down = function (x, y, obj) { if (self.isBomb) { return; } // Should not happen, but safety // Score: add dice value (1-6) LK.setScore(LK.getScore() + self.value); LK.getSound('diceTap').play(); // If this is the 6th die, add +2 seconds to timer (max 999 for safety) if (self.value === 6) { timeLeft += 2; if (timeLeft > 999) { timeLeft = 999; } timerTxt.setText(timeLeft); } // Animate: scale up and fade out tween(self, { scaleX: 1.4, scaleY: 1.4, alpha: 0 }, { duration: 200, easing: tween.easeOut, onFinish: function onFinish() { self.destroy(); } }); // Remove from dice array in main game removeDice(self); }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ // No title, no description // Always backgroundColor is black backgroundColor: 0x000000 }); /**** * Game Code ****/ // Add background photo image var bgPhoto = LK.getAsset('backgroundPhoto', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2, scaleX: 2048 / 1500, scaleY: 2732 / 4000 }); game.addChild(bgPhoto); // Play background music // Dice assets: 6 dice (1-6), all box shapes, different colors // Bomb dice: ellipse, red // Sounds // Music (background, desert style) LK.playMusic('desertbg'); // Dice array (all falling dice) var diceArr = []; // Remove dice helper function removeDice(diceObj) { for (var i = diceArr.length - 1; i >= 0; i--) { if (diceArr[i] === diceObj) { diceArr.splice(i, 1); break; } } } // Score label var scoreLabelTxt = new Text2('Score', { size: 60, fill: 0x7C4A03 }); scoreLabelTxt.anchor.set(0.5, 0); scoreLabelTxt.x = 0; scoreLabelTxt.y = 0; LK.gui.top.addChild(scoreLabelTxt); // Score value var scoreTxt = new Text2('0', { size: 120, fill: 0x7C4A03 }); scoreTxt.anchor.set(0.5, 0); scoreTxt.x = 0; scoreTxt.y = 60; LK.gui.top.addChild(scoreTxt); // Timer label var timerLabelTxt = new Text2('Time Left', { size: 60, fill: 0x7C4A03 }); timerLabelTxt.anchor.set(0.5, 0); timerLabelTxt.x = 0; timerLabelTxt.y = 200; LK.gui.top.addChild(timerLabelTxt); // Timer value var timerTxt = new Text2('60', { size: 90, fill: 0x7C4A03 }); timerTxt.anchor.set(0.5, 0); timerTxt.x = 0; timerTxt.y = 260; LK.gui.top.addChild(timerTxt); // Penalty indicator (hidden and never shown) var penaltyTxt = new Text2('PENALTY!', { size: 90, fill: 0xff2222 }); penaltyTxt.anchor.set(0.5, 0); penaltyTxt.visible = false; penaltyTxt.x = 0; penaltyTxt.y = 140; // Do NOT add penaltyTxt to LK.gui.top, so it never appears // Penalty state var isPenalty = false; var penaltyTicksLeft = 0; // Score bonus state removed // Game timer var timeLeft = 60; // seconds var timerInterval = LK.setInterval(function () { timeLeft--; if (timeLeft < 0) { timeLeft = 0; } timerTxt.setText(timeLeft); if (timeLeft === 0) { // Play timer end sound effect LK.getSound('timerEnd').play(); // End game LK.showGameOver(); } }, 1000); // Dice spawn timer var spawnTick = 0; var spawnInterval = 36; // ~0.6s at 60fps, will randomize // Helper: spawn a dice (random type) function spawnDice() { // Random X position (avoid edges) var margin = 120; var x = margin + Math.random() * (2048 - 2 * margin); var y = -100; // Bomb chance: 1 in 8 var isBomb = Math.random() < 0.125; var diceObj; if (isBomb) { diceObj = new BombDice(); } else { // Weighted random dice value: 2,3,4,5 are most likely, 1 and 6 are less likely // Probabilities: 1 (10%), 2 (20%), 3 (20%), 4 (20%), 5 (20%), 6 (10%) // Prevent 6 from spawning in first 10 seconds var r = Math.random(); var value; var elapsedTime = 60 - timeLeft; if (elapsedTime < 10) { // Only allow 1-5, reweight probabilities // 1 (11.1%), 2 (22.2%), 3 (22.2%), 4 (22.2%), 5 (22.2%) if (r < 0.111) { value = 1; } else if (r < 0.333) { value = 2; } else if (r < 0.555) { value = 3; } else if (r < 0.777) { value = 4; } else { value = 5; } } else { if (r < 0.10) { value = 1; } else if (r < 0.30) { value = 2; } else if (r < 0.50) { value = 3; } else if (r < 0.70) { value = 4; } else if (r < 0.90) { value = 5; } else { value = 6; } } diceObj = new Dice(); diceObj.setup(value); } diceObj.x = x; diceObj.y = y; diceObj.scaleX = 1; diceObj.scaleY = 1; diceObj.alpha = 1; // Set fall speed: dice value 1 falls slowest, 6 fastest if (!isBomb) { // Value 1: slowest (6 px/frame), Value 6: fastest (12 px/frame) diceObj.fallSpeed = 6 + (diceObj.value - 1) * (6 / 5); } else { // Bombs: fall speed matches die value 6 (fastest) diceObj.fallSpeed = 12; } // Add to game game.addChild(diceObj); diceArr.push(diceObj); } // Touch/click handler game.down = function (x, y, obj) { // Check if any dice is tapped (from topmost to bottom) for (var i = diceArr.length - 1; i >= 0; i--) { var d = diceArr[i]; // Convert global to local for dice var local = d.toLocal(game.toGlobal({ x: x, y: y })); // Check if inside dice bounds (centered) if (Math.abs(local.x) <= d.width / 2 && Math.abs(local.y) <= d.height / 2) { // Call dice down handler d.down(x, y, obj); return; } } }; // Main update loop game.update = function () { // Play sound every 100 points milestone if (typeof lastScoreSound === "undefined") { lastScoreSound = 0; } var currentScore = LK.getScore(); if (currentScore >= 100 && Math.floor(currentScore / 100) > Math.floor(lastScoreSound / 100)) { LK.getSound('milestone100').play(); lastScoreSound = currentScore; } // Penalty effect: apply penalty during penaltyTicksLeft if (isPenalty) { penaltyTicksLeft--; // During penalty, subtract 1 point every 15 ticks (~0.25s) if (penaltyTicksLeft % 15 === 0 && LK.getScore() > 0) { LK.setScore(Math.max(0, LK.getScore() - 1)); } if (penaltyTicksLeft <= 0) { isPenalty = false; // penaltyTxt.visible = false;//{29} // Already hidden, do nothing } } // Score bonus effect removed // Update score text scoreTxt.setText(LK.getScore()); // Spawn dice at intervals (randomize interval for variety) spawnTick++; if (spawnTick >= spawnInterval) { // Determine how many dice to spawn based on elapsed time // Start with 1, add 1 every 20 seconds (max 6 at 100+ seconds) var elapsed = 60 - timeLeft; var diceToSpawn = 1 + Math.floor(elapsed / 20); if (diceToSpawn > 6) diceToSpawn = 6; for (var i = 0; i < diceToSpawn; i++) { spawnDice(); } spawnTick = 0; // Next interval: 24-48 ticks (~0.4-0.8s) spawnInterval = 24 + Math.floor(Math.random() * 25); } // Update all dice, remove if off screen for (var i = diceArr.length - 1; i >= 0; i--) { var d = diceArr[i]; d.update(); // --- Dice collision detection and response --- // Now allow all dice (including bombs) to collide with each other for (var j = i - 1; j >= 0; j--) { var d2 = diceArr[j]; // Only check if both are not destroyed and not the same object if (d !== d2) { // Simple circle collision (use half width as radius) var dx = d.x - d2.x; var dy = d.y - d2.y; var dist = Math.sqrt(dx * dx + dy * dy); var minDist = (d.width + d2.width) / 2 * 0.85; // 0.85 fudge for overlap if (dist < minDist && dist > 0) { // Push them apart equally var overlap = (minDist - dist) / 2; var nx = dx / dist; var ny = dy / dist; d.x += nx * overlap; d.y += ny * overlap; d2.x -= nx * overlap; d2.y -= ny * overlap; // Exchange a bit of fallSpeed (simulate bounce) var temp = d.fallSpeed; d.fallSpeed = d2.fallSpeed; d2.fallSpeed = temp; } } } if (d.y > 2732 + 120) { // Missed dice, just remove d.destroy(); diceArr.splice(i, 1); } } }; // On game over, clear timers game.on('destroy', function () { LK.clearInterval(timerInterval); // Remove all dice for (var i = diceArr.length - 1; i >= 0; i--) { diceArr[i].destroy(); } diceArr = []; });
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// BombDice class
var BombDice = Container.expand(function () {
var self = Container.call(this);
self.isBomb = true;
// Attach bomb asset, anchor center
self.bombAsset = self.attachAsset('bomb', {
anchorX: 0.5,
anchorY: 0.5
});
// Add bomb label
self.label = new Text2('', {
size: 100,
fill: 0xFFFFFF
});
self.label.anchor.set(0.5, 0.5);
self.label.x = 0;
self.label.y = 0;
self.addChild(self.label);
// Called every tick
self.update = function () {
self.y += self.fallSpeed;
};
// On tap
self.down = function (x, y, obj) {
// Score: subtract 5 (minimum 0)
var newScore = LK.getScore() - 5;
if (newScore < 0) {
newScore = 0;
}
LK.setScore(newScore);
LK.getSound('bombTap').play();
// Set penalty state for 90 ticks (~1.5s at 60fps)
isPenalty = true;
penaltyTicksLeft = 90;
// penaltyTxt.visible = true; // Do not show penalty text
// Subtract 2 seconds from timer (minimum 0)
timeLeft -= 2;
if (timeLeft < 0) {
timeLeft = 0;
}
timerTxt.setText(timeLeft);
// Animate: shake and fade out
tween(self, {
x: self.x + 30
}, {
duration: 60,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(self, {
x: self.x - 60
}, {
duration: 80,
easing: tween.easeIn,
onFinish: function onFinish() {
tween(self, {
x: self.x + 30,
alpha: 0
}, {
duration: 100,
onFinish: function onFinish() {
self.destroy();
LK.effects.flashScreen(0xffffff, 120); // Quick white flash after explosion
// Manual screen shake using tween plugin
var originalX = game.x || 0;
var originalY = game.y || 0;
var shakeDuration = 400;
var shakeAmplitude = 16;
var shakeTicks = Math.floor(shakeDuration / 16); // ~60fps
var shakeCount = 0;
function doShake() {
if (shakeCount >= shakeTicks) {
game.x = originalX;
game.y = originalY;
return;
}
// Random offset
game.x = originalX + (Math.random() - 0.5) * shakeAmplitude;
game.y = originalY + (Math.random() - 0.5) * shakeAmplitude;
shakeCount++;
LK.setTimeout(doShake, 16);
}
doShake();
}
});
}
});
}
});
// Remove from dice array in main game
removeDice(self);
};
return self;
});
// Dice class (for dice 1-6)
var Dice = Container.expand(function () {
var self = Container.call(this);
// Dice value (1-6)
self.value = 1;
self.isBomb = false;
// Attach dice asset, anchor center
self.diceAsset = null;
// Dice label (number)
self.label = null;
// Set up dice
self.setup = function (value) {
self.value = value;
self.isBomb = false;
var assetId = 'dice' + value;
self.diceAsset = self.attachAsset(assetId, {
anchorX: 0.5,
anchorY: 0.5
});
// Add number label
self.label = new Text2('' + value, {
size: 28,
fill: 0x222222
});
self.label.anchor.set(0.5, 0.5);
self.label.x = 0;
self.label.y = 0;
self.addChild(self.label);
};
// Called every tick
self.update = function () {
self.y += self.fallSpeed;
};
// On tap
self.down = function (x, y, obj) {
if (self.isBomb) {
return;
} // Should not happen, but safety
// Score: add dice value (1-6)
LK.setScore(LK.getScore() + self.value);
LK.getSound('diceTap').play();
// If this is the 6th die, add +2 seconds to timer (max 999 for safety)
if (self.value === 6) {
timeLeft += 2;
if (timeLeft > 999) {
timeLeft = 999;
}
timerTxt.setText(timeLeft);
}
// Animate: scale up and fade out
tween(self, {
scaleX: 1.4,
scaleY: 1.4,
alpha: 0
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
self.destroy();
}
});
// Remove from dice array in main game
removeDice(self);
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
// No title, no description
// Always backgroundColor is black
backgroundColor: 0x000000
});
/****
* Game Code
****/
// Add background photo image
var bgPhoto = LK.getAsset('backgroundPhoto', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 2,
scaleX: 2048 / 1500,
scaleY: 2732 / 4000
});
game.addChild(bgPhoto);
// Play background music
// Dice assets: 6 dice (1-6), all box shapes, different colors
// Bomb dice: ellipse, red
// Sounds
// Music (background, desert style)
LK.playMusic('desertbg');
// Dice array (all falling dice)
var diceArr = [];
// Remove dice helper
function removeDice(diceObj) {
for (var i = diceArr.length - 1; i >= 0; i--) {
if (diceArr[i] === diceObj) {
diceArr.splice(i, 1);
break;
}
}
}
// Score label
var scoreLabelTxt = new Text2('Score', {
size: 60,
fill: 0x7C4A03
});
scoreLabelTxt.anchor.set(0.5, 0);
scoreLabelTxt.x = 0;
scoreLabelTxt.y = 0;
LK.gui.top.addChild(scoreLabelTxt);
// Score value
var scoreTxt = new Text2('0', {
size: 120,
fill: 0x7C4A03
});
scoreTxt.anchor.set(0.5, 0);
scoreTxt.x = 0;
scoreTxt.y = 60;
LK.gui.top.addChild(scoreTxt);
// Timer label
var timerLabelTxt = new Text2('Time Left', {
size: 60,
fill: 0x7C4A03
});
timerLabelTxt.anchor.set(0.5, 0);
timerLabelTxt.x = 0;
timerLabelTxt.y = 200;
LK.gui.top.addChild(timerLabelTxt);
// Timer value
var timerTxt = new Text2('60', {
size: 90,
fill: 0x7C4A03
});
timerTxt.anchor.set(0.5, 0);
timerTxt.x = 0;
timerTxt.y = 260;
LK.gui.top.addChild(timerTxt);
// Penalty indicator (hidden and never shown)
var penaltyTxt = new Text2('PENALTY!', {
size: 90,
fill: 0xff2222
});
penaltyTxt.anchor.set(0.5, 0);
penaltyTxt.visible = false;
penaltyTxt.x = 0;
penaltyTxt.y = 140;
// Do NOT add penaltyTxt to LK.gui.top, so it never appears
// Penalty state
var isPenalty = false;
var penaltyTicksLeft = 0;
// Score bonus state removed
// Game timer
var timeLeft = 60; // seconds
var timerInterval = LK.setInterval(function () {
timeLeft--;
if (timeLeft < 0) {
timeLeft = 0;
}
timerTxt.setText(timeLeft);
if (timeLeft === 0) {
// Play timer end sound effect
LK.getSound('timerEnd').play();
// End game
LK.showGameOver();
}
}, 1000);
// Dice spawn timer
var spawnTick = 0;
var spawnInterval = 36; // ~0.6s at 60fps, will randomize
// Helper: spawn a dice (random type)
function spawnDice() {
// Random X position (avoid edges)
var margin = 120;
var x = margin + Math.random() * (2048 - 2 * margin);
var y = -100;
// Bomb chance: 1 in 8
var isBomb = Math.random() < 0.125;
var diceObj;
if (isBomb) {
diceObj = new BombDice();
} else {
// Weighted random dice value: 2,3,4,5 are most likely, 1 and 6 are less likely
// Probabilities: 1 (10%), 2 (20%), 3 (20%), 4 (20%), 5 (20%), 6 (10%)
// Prevent 6 from spawning in first 10 seconds
var r = Math.random();
var value;
var elapsedTime = 60 - timeLeft;
if (elapsedTime < 10) {
// Only allow 1-5, reweight probabilities
// 1 (11.1%), 2 (22.2%), 3 (22.2%), 4 (22.2%), 5 (22.2%)
if (r < 0.111) {
value = 1;
} else if (r < 0.333) {
value = 2;
} else if (r < 0.555) {
value = 3;
} else if (r < 0.777) {
value = 4;
} else {
value = 5;
}
} else {
if (r < 0.10) {
value = 1;
} else if (r < 0.30) {
value = 2;
} else if (r < 0.50) {
value = 3;
} else if (r < 0.70) {
value = 4;
} else if (r < 0.90) {
value = 5;
} else {
value = 6;
}
}
diceObj = new Dice();
diceObj.setup(value);
}
diceObj.x = x;
diceObj.y = y;
diceObj.scaleX = 1;
diceObj.scaleY = 1;
diceObj.alpha = 1;
// Set fall speed: dice value 1 falls slowest, 6 fastest
if (!isBomb) {
// Value 1: slowest (6 px/frame), Value 6: fastest (12 px/frame)
diceObj.fallSpeed = 6 + (diceObj.value - 1) * (6 / 5);
} else {
// Bombs: fall speed matches die value 6 (fastest)
diceObj.fallSpeed = 12;
}
// Add to game
game.addChild(diceObj);
diceArr.push(diceObj);
}
// Touch/click handler
game.down = function (x, y, obj) {
// Check if any dice is tapped (from topmost to bottom)
for (var i = diceArr.length - 1; i >= 0; i--) {
var d = diceArr[i];
// Convert global to local for dice
var local = d.toLocal(game.toGlobal({
x: x,
y: y
}));
// Check if inside dice bounds (centered)
if (Math.abs(local.x) <= d.width / 2 && Math.abs(local.y) <= d.height / 2) {
// Call dice down handler
d.down(x, y, obj);
return;
}
}
};
// Main update loop
game.update = function () {
// Play sound every 100 points milestone
if (typeof lastScoreSound === "undefined") {
lastScoreSound = 0;
}
var currentScore = LK.getScore();
if (currentScore >= 100 && Math.floor(currentScore / 100) > Math.floor(lastScoreSound / 100)) {
LK.getSound('milestone100').play();
lastScoreSound = currentScore;
}
// Penalty effect: apply penalty during penaltyTicksLeft
if (isPenalty) {
penaltyTicksLeft--;
// During penalty, subtract 1 point every 15 ticks (~0.25s)
if (penaltyTicksLeft % 15 === 0 && LK.getScore() > 0) {
LK.setScore(Math.max(0, LK.getScore() - 1));
}
if (penaltyTicksLeft <= 0) {
isPenalty = false;
// penaltyTxt.visible = false;//{29} // Already hidden, do nothing
}
}
// Score bonus effect removed
// Update score text
scoreTxt.setText(LK.getScore());
// Spawn dice at intervals (randomize interval for variety)
spawnTick++;
if (spawnTick >= spawnInterval) {
// Determine how many dice to spawn based on elapsed time
// Start with 1, add 1 every 20 seconds (max 6 at 100+ seconds)
var elapsed = 60 - timeLeft;
var diceToSpawn = 1 + Math.floor(elapsed / 20);
if (diceToSpawn > 6) diceToSpawn = 6;
for (var i = 0; i < diceToSpawn; i++) {
spawnDice();
}
spawnTick = 0;
// Next interval: 24-48 ticks (~0.4-0.8s)
spawnInterval = 24 + Math.floor(Math.random() * 25);
}
// Update all dice, remove if off screen
for (var i = diceArr.length - 1; i >= 0; i--) {
var d = diceArr[i];
d.update();
// --- Dice collision detection and response ---
// Now allow all dice (including bombs) to collide with each other
for (var j = i - 1; j >= 0; j--) {
var d2 = diceArr[j];
// Only check if both are not destroyed and not the same object
if (d !== d2) {
// Simple circle collision (use half width as radius)
var dx = d.x - d2.x;
var dy = d.y - d2.y;
var dist = Math.sqrt(dx * dx + dy * dy);
var minDist = (d.width + d2.width) / 2 * 0.85; // 0.85 fudge for overlap
if (dist < minDist && dist > 0) {
// Push them apart equally
var overlap = (minDist - dist) / 2;
var nx = dx / dist;
var ny = dy / dist;
d.x += nx * overlap;
d.y += ny * overlap;
d2.x -= nx * overlap;
d2.y -= ny * overlap;
// Exchange a bit of fallSpeed (simulate bounce)
var temp = d.fallSpeed;
d.fallSpeed = d2.fallSpeed;
d2.fallSpeed = temp;
}
}
}
if (d.y > 2732 + 120) {
// Missed dice, just remove
d.destroy();
diceArr.splice(i, 1);
}
}
};
// On game over, clear timers
game.on('destroy', function () {
LK.clearInterval(timerInterval);
// Remove all dice
for (var i = diceArr.length - 1; i >= 0; i--) {
diceArr[i].destroy();
}
diceArr = [];
});