/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1", { highScore: 0 }); /**** * Classes ****/ // Ball class: handles color, rotation, and animation var ColorBall = Container.expand(function () { var self = Container.call(this); // Color order: 0=Red, 1=Blue, 2=Green self.colors = ['red', 'blue', 'green']; self.colorIdx = 0; // --- Add shadow assets for each color, matching the ball shape and style --- self.shadowAssets = { red: self.attachAsset('ballRed', { anchorX: 0.5, anchorY: 0.5, x: 0, y: self.ballAssets && self.ballAssets.red ? self.ballAssets.red.height * 0.045 : 22, scaleX: 1.04, scaleY: 1.04, alpha: 0.32, tint: 0x000000 }), blue: self.attachAsset('ballBlue', { anchorX: 0.5, anchorY: 0.5, x: 0, y: self.ballAssets && self.ballAssets.blue ? self.ballAssets.blue.height * 0.045 : 22, scaleX: 1.04, scaleY: 1.04, alpha: 0.32, tint: 0x000000 }), green: self.attachAsset('ballGreen', { anchorX: 0.5, anchorY: 0.5, x: 0, y: self.ballAssets && self.ballAssets.green ? self.ballAssets.green.height * 0.045 : 22, scaleX: 1.04, scaleY: 1.04, alpha: 0.32, tint: 0x000000 }) }; // Attach all color assets, only one visible at a time self.ballAssets = { red: self.attachAsset('ballRed', { anchorX: 0.5, anchorY: 0.5 }), blue: self.attachAsset('ballBlue', { anchorX: 0.5, anchorY: 0.5 }), green: self.attachAsset('ballGreen', { anchorX: 0.5, anchorY: 0.5 }) }; // Place shadow behind the ball self.addChildAt(self.shadowAssets.red, 0); self.addChildAt(self.shadowAssets.blue, 0); self.addChildAt(self.shadowAssets.green, 0); self.setColor = function (color) { self.color = color; self.ballAssets.red.visible = color === 'red'; self.ballAssets.blue.visible = color === 'blue'; self.ballAssets.green.visible = color === 'green'; // Show only the shadow for the current color self.shadowAssets.red.visible = color === 'red'; self.shadowAssets.blue.visible = color === 'blue'; self.shadowAssets.green.visible = color === 'green'; }; self.setColor('red'); self.rotationSpeed = 0.02; // radians per tick self.update = function () { // Defensive: ensure rotationSpeed is always defined if (typeof self.rotationSpeed !== "number") self.rotationSpeed = 0.02; self.rotation += self.rotationSpeed; }; // Animate color change (optional: flash or scale) self.animateColorChange = function () { tween(self, { scaleX: 1.15, scaleY: 1.15 }, { duration: 80, easing: tween.cubicOut, onFinish: function onFinish() { tween(self, { scaleX: 1, scaleY: 1 }, { duration: 100, easing: tween.cubicIn }); } }); }; return self; }); // Button class: handles color, label, and press feedback var ColorButton = Container.expand(function () { var self = Container.call(this); // color: 'red', 'blue', 'green' self.setColor = function (color) { self.color = color; if (self.asset) { self.removeChild(self.asset); } if (self.label) { self.removeChild(self.label); } if (self.hitbox) { self.removeChild(self.hitbox); } var assetId = color === 'red' ? 'btnRed' : color === 'blue' ? 'btnBlue' : 'btnGreen'; self.asset = self.attachAsset(assetId, { anchorX: 0.5, anchorY: 0.5 }); // Add realistic shadow by duplicating the button asset, tinting and offsetting it if (self.shadow) { self.removeChild(self.shadow); } var shadowAssetId = assetId; self.shadow = self.attachAsset(shadowAssetId, { anchorX: 0.5, anchorY: 0.5, x: 0, y: self.asset.height * 0.06, // offset shadow downward (closer) scaleX: 1.03, scaleY: 1.03, alpha: 0.32, tint: 0x000000 }); self.shadow.zIndex = -2; self.addChild(self.shadow); self.addChild(self.asset); }; // Animate press feedback self.animatePress = function () { tween(self.asset, { scaleX: 0.93, scaleY: 0.93 }, { duration: 60, easing: tween.cubicOut, onFinish: function onFinish() { tween(self.asset, { scaleX: 1, scaleY: 1 }, { duration: 80, easing: tween.cubicIn }); } }); }; // For hit detection self.containsPoint = function (x, y) { var local = self.toLocal({ x: x, y: y }); return local.x > -self.hitbox.width / 2 && local.x < self.hitbox.width / 2 && local.y > -self.hitbox.height / 2 && local.y < self.hitbox.height / 2; }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0xffb638 }); /**** * Game Code ****/ // btnBlue image // btnGreen image // btnRed image // Health bar assets // --- Game State --- // Ball colors // Button colors // Sound // Music (optional, but initialized for future use) var ball; var buttons = []; var colorOrder = ['red', 'blue', 'green']; var currentColor = 'red'; var score = 0; var highScore = storage.highScore || 0; var timeLimit = 5000; // ms, initial time to respond (5 seconds) var minTimeLimit = 5000; // ms, minimum allowed (5 seconds) var timeDecrease = 80; // ms, decrease per correct var rotationSpeed = 0.02; // initial var maxRotationSpeed = 0.09; var rotationIncrease = 0.008; var waitingForInput = true; var responseTimer = null; var lastTapTime = 0; // --- UI Elements --- // Add shadow for score text (centered, slightly offset, behind main score) var scoreShadowTxt = new Text2('0', { size: 120, fill: "#000", alpha: 0.32 }); scoreShadowTxt.anchor.set(0.5, 0); scoreShadowTxt.y = 60 + 3; // offset shadow downward (closer) var scoreTxt = new Text2('0', { size: 120, fill: 0xFF4500 }); scoreTxt.anchor.set(0.5, 0); scoreTxt.y = 60; // Add scoreTxt after timer and hearts to ensure it is always on top LK.setTimeout(function () { LK.gui.top.addChild(scoreShadowTxt); LK.gui.top.addChild(scoreTxt); }, 0); var highScoreTxt = new Text2('BEST: ' + highScore, { size: 60, fill: 0xFF4500, font: "'GillSans-Bold',Impact,'Arial Black',Tahoma" }); highScoreTxt.anchor.set(0.5, 0); highScoreTxt.y = 190; LK.gui.top.addChild(highScoreTxt); // --- Timer Bar (under score) --- // --- Timer Bar (under score, in GUI) --- var timerBar = LK.getAsset('timerBar', { width: 900, height: 60, color: 0xffffff, anchorX: 0.5, anchorY: 0.0 }); timerBar.x = 0.5; // relative to gui width timerBar.y = scoreTxt.y + scoreTxt.height + 70; // 70px below score (moved further down) timerBar.alpha = 0.18; timerBar.isGUI = true; LK.gui.top.addChild(timerBar); var timerBarFill = LK.getAsset('timerBarFill', { width: 900, height: 60, color: 0x2ecc40, anchorX: 0.5, anchorY: 0.0 }); timerBarFill.x = 0.5; // relative to gui width timerBarFill.y = scoreTxt.y + scoreTxt.height + 70; // 70px below score (moved further down) timerBarFill.isGUI = true; LK.gui.top.addChild(timerBarFill); // --- Health Bars (under timer, in GUI) --- var health = 3; var maxHealth = 3; var healthBars = []; var healthBarSpacing = 130; var healthBarWidth = 120; var healthBarHeight = 120; var healthBarAssets = [{ id: 'healthBarRed', color: 0xd83318 }, // Red { id: 'healthBarBlue', color: 0x187ad8 }, // Blue { id: 'healthBarGreen', color: 0x2ecc40 } // Green ]; var healthBarY = timerBar.y + timerBar.height + 30; // 30px below timer bar // Calculate total width of all health bars and starting x for centering var totalHealthWidth = (maxHealth - 1) * healthBarSpacing; var healthStartX = 0.5 + -(totalHealthWidth / 2); // 0.5 is center in LK.gui coordinates for (var i = 0; i < maxHealth; i++) { var assetInfo = healthBarAssets[i]; var bar = LK.getAsset(assetInfo.id, { width: healthBarWidth, height: healthBarHeight, color: assetInfo.color, anchorX: 0.5, anchorY: 0.0 }); // Center health bars under the timer bar.x = healthStartX + i * healthBarSpacing; bar.y = healthBarY; bar.isGUI = true; LK.gui.top.addChild(bar); healthBars.push(bar); } // Helper to update health bar visuals function updateHealthBars() { for (var i = 0; i < maxHealth; i++) { // Each bar is independent: show full alpha if "alive", faded if "lost" healthBars[i].alpha = i < health ? 1 : 0.18; } } updateHealthBars(); // --- Setup Ball --- ball = new ColorBall(); ball.x = 2048 / 2; ball.y = 1100; game.addChild(ball); // --- Setup Buttons --- var btnSpacing = 480; var btnY = 2732 - 320; // Create btnRed var btnRed = new ColorButton(); btnRed.setColor('red'); btnRed.x = 2048 / 2 - btnSpacing; btnRed.y = btnY; game.addChild(btnRed); // Create btnBlue var btnBlue = new ColorButton(); btnBlue.setColor('blue'); btnBlue.x = 2048 / 2; btnBlue.y = btnY; game.addChild(btnBlue); // Create btnGreen var btnGreen = new ColorButton(); btnGreen.setColor('green'); btnGreen.x = 2048 / 2 + btnSpacing; btnGreen.y = btnY; game.addChild(btnGreen); // Place all buttons in the same positions as btnRed, btnBlue, btnGreen buttons = [btnRed, btnBlue, btnGreen]; // Ensure all color buttons are positioned exactly as btnRed, btnBlue, btnGreen buttons[0].x = btnRed.x; buttons[0].y = btnRed.y; buttons[1].x = btnBlue.x; buttons[1].y = btnBlue.y; buttons[2].x = btnGreen.x; buttons[2].y = btnGreen.y; // --- Helper Functions --- function setScore(val) { score = val; scoreTxt.setText(score); if (typeof scoreShadowTxt !== "undefined") { scoreShadowTxt.setText(score); } if (score > highScore) { highScore = score; storage.highScore = highScore; highScoreTxt.setText('BEST: ' + highScore); } } function randomColor() { // Never repeat the same color twice in a row var idx = colorOrder.indexOf(currentColor); var nextIdx = idx; while (nextIdx === idx) { nextIdx = Math.floor(Math.random() * 3); } return colorOrder[nextIdx]; } function startNewRound() { waitingForInput = true; // Pick new color currentColor = randomColor(); ball.setColor(currentColor); ball.animateColorChange(); // Timer always starts from 5 seconds (5000ms) for each round, and decreases by 0.1s for each correct answer timeLimit = 5000 - score * 100; if (timeLimit < 500) { timeLimit = 500; // minimum 0.5s to avoid negative/zero } // --- Smoothly increase rotation speed at every multiple of 10 points --- var baseSpeed = 0.02; var speedStep = 0.008; var maxSpeed = 0.09; var targetSpeed = baseSpeed + Math.floor(score / 10) * speedStep; if (targetSpeed > maxSpeed) targetSpeed = maxSpeed; // Only tween if the speed is changing if (Math.abs(ball.rotationSpeed - targetSpeed) > 0.0001) { tween.stop(ball, { rotationSpeed: true }); tween(ball, { rotationSpeed: targetSpeed }, { duration: 700, easing: tween.cubicInOut }); } rotationSpeed = targetSpeed; ball.targetRotationSpeed = targetSpeed; // Start timer timerBarFill.width = 900; if (responseTimer) { LK.clearTimeout(responseTimer); responseTimer = null; } responseTimer = LK.setTimeout(function () { // Time's up waitingForInput = false; LK.effects.flashScreen(0xff0000, 600); LK.getSound('wrong').play(); health--; updateHealthBars(); if (health <= 0) { endGame(); } else { // Give player another chance, start new round after short delay responseTimer = LK.setTimeout(function () { startNewRound(); }, 700); } }, timeLimit); lastTapTime = Date.now(); } function endGame() { if (responseTimer) { LK.clearTimeout(responseTimer); responseTimer = null; } waitingForInput = false; // Flash ball red LK.effects.flashObject(ball, 0xff0000, 600); // Show game over (handled by LK) LK.showGameOver(); } // --- Input Handling --- game.down = function (x, y, obj) { if (!waitingForInput) { return; } // Divide the screen into three vertical regions: left, middle, right var region; if (x < 2048 / 3) { region = 'left'; } else if (x > 2048 * 2 / 3) { region = 'right'; } else { region = 'middle'; } // Map region to color var color; if (region === 'left') { color = 'red'; } else if (region === 'right') { color = 'green'; } else { color = 'blue'; } // Find the button with this color (for animation/feedback) var btn = null; for (var i = 0; i < buttons.length; i++) { if (buttons[i].color === color) { btn = buttons[i]; break; } } if (btn) { handleButtonPress(btn); } }; function handleButtonPress(btn) { if (!waitingForInput) { return; } btn.animatePress(); LK.effects.flashObject(btn.asset, 0xffffff, 120); waitingForInput = false; if (btn.color === currentColor) { // Correct! setScore(score + 1); LK.getSound('correct').play(); // Animate ball tween(ball, { scaleX: 1.18, scaleY: 1.18 }, { duration: 80, easing: tween.cubicOut, onFinish: function onFinish() { tween(ball, { scaleX: 1, scaleY: 1 }, { duration: 100, easing: tween.cubicIn }); } }); // Decrease timer by 0.1 seconds (100ms) for each correct answer if (responseTimer) { LK.clearTimeout(responseTimer); responseTimer = null; } responseTimer = LK.setTimeout(function () { startNewRound(); }, 220); } else { // Wrong! LK.effects.flashScreen(0xff0000, 600); LK.getSound('wrong').play(); if (responseTimer) { LK.clearTimeout(responseTimer); responseTimer = null; } health--; updateHealthBars(); if (health <= 0) { endGame(); } else { // Decrease timer by 0.2 seconds (200ms) for each incorrect answer and reset to 3 seconds timeLimit = 3000 - score * 100 - (maxHealth - health) * 200; if (timeLimit < 500) { timeLimit = 500; // minimum 0.5s to avoid negative/zero } // Give player another chance, start new round after short delay responseTimer = LK.setTimeout(function () { startNewRound(); }, 700); } } } // --- Game Update Loop --- game.update = function () { // Animate timer bar if (waitingForInput) { var elapsed = Date.now() - lastTapTime; var frac = 1 - elapsed / timeLimit; if (frac < 0) { frac = 0; } timerBarFill.width = 900 * frac; // Do not change timerBarFill color or tint here to prevent fading } else { timerBarFill.width = 0; } }; // --- Start Game --- function startGame() { LK.playMusic('g'); setScore(0); timeLimit = 5000; rotationSpeed = 0.02; ball.rotationSpeed = rotationSpeed; ball.targetRotationSpeed = rotationSpeed; health = maxHealth; updateHealthBars(); startNewRound(); } startGame(); // --- Responsive UI (optional, handled by LK) --- // --- Music (optional, not started by default) --- // LK.playMusic('bgmusic', {fade: {start: 0, end: 1, duration: 1000}});
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
highScore: 0
});
/****
* Classes
****/
// Ball class: handles color, rotation, and animation
var ColorBall = Container.expand(function () {
var self = Container.call(this);
// Color order: 0=Red, 1=Blue, 2=Green
self.colors = ['red', 'blue', 'green'];
self.colorIdx = 0;
// --- Add shadow assets for each color, matching the ball shape and style ---
self.shadowAssets = {
red: self.attachAsset('ballRed', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: self.ballAssets && self.ballAssets.red ? self.ballAssets.red.height * 0.045 : 22,
scaleX: 1.04,
scaleY: 1.04,
alpha: 0.32,
tint: 0x000000
}),
blue: self.attachAsset('ballBlue', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: self.ballAssets && self.ballAssets.blue ? self.ballAssets.blue.height * 0.045 : 22,
scaleX: 1.04,
scaleY: 1.04,
alpha: 0.32,
tint: 0x000000
}),
green: self.attachAsset('ballGreen', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: self.ballAssets && self.ballAssets.green ? self.ballAssets.green.height * 0.045 : 22,
scaleX: 1.04,
scaleY: 1.04,
alpha: 0.32,
tint: 0x000000
})
};
// Attach all color assets, only one visible at a time
self.ballAssets = {
red: self.attachAsset('ballRed', {
anchorX: 0.5,
anchorY: 0.5
}),
blue: self.attachAsset('ballBlue', {
anchorX: 0.5,
anchorY: 0.5
}),
green: self.attachAsset('ballGreen', {
anchorX: 0.5,
anchorY: 0.5
})
};
// Place shadow behind the ball
self.addChildAt(self.shadowAssets.red, 0);
self.addChildAt(self.shadowAssets.blue, 0);
self.addChildAt(self.shadowAssets.green, 0);
self.setColor = function (color) {
self.color = color;
self.ballAssets.red.visible = color === 'red';
self.ballAssets.blue.visible = color === 'blue';
self.ballAssets.green.visible = color === 'green';
// Show only the shadow for the current color
self.shadowAssets.red.visible = color === 'red';
self.shadowAssets.blue.visible = color === 'blue';
self.shadowAssets.green.visible = color === 'green';
};
self.setColor('red');
self.rotationSpeed = 0.02; // radians per tick
self.update = function () {
// Defensive: ensure rotationSpeed is always defined
if (typeof self.rotationSpeed !== "number") self.rotationSpeed = 0.02;
self.rotation += self.rotationSpeed;
};
// Animate color change (optional: flash or scale)
self.animateColorChange = function () {
tween(self, {
scaleX: 1.15,
scaleY: 1.15
}, {
duration: 80,
easing: tween.cubicOut,
onFinish: function onFinish() {
tween(self, {
scaleX: 1,
scaleY: 1
}, {
duration: 100,
easing: tween.cubicIn
});
}
});
};
return self;
});
// Button class: handles color, label, and press feedback
var ColorButton = Container.expand(function () {
var self = Container.call(this);
// color: 'red', 'blue', 'green'
self.setColor = function (color) {
self.color = color;
if (self.asset) {
self.removeChild(self.asset);
}
if (self.label) {
self.removeChild(self.label);
}
if (self.hitbox) {
self.removeChild(self.hitbox);
}
var assetId = color === 'red' ? 'btnRed' : color === 'blue' ? 'btnBlue' : 'btnGreen';
self.asset = self.attachAsset(assetId, {
anchorX: 0.5,
anchorY: 0.5
});
// Add realistic shadow by duplicating the button asset, tinting and offsetting it
if (self.shadow) {
self.removeChild(self.shadow);
}
var shadowAssetId = assetId;
self.shadow = self.attachAsset(shadowAssetId, {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: self.asset.height * 0.06,
// offset shadow downward (closer)
scaleX: 1.03,
scaleY: 1.03,
alpha: 0.32,
tint: 0x000000
});
self.shadow.zIndex = -2;
self.addChild(self.shadow);
self.addChild(self.asset);
};
// Animate press feedback
self.animatePress = function () {
tween(self.asset, {
scaleX: 0.93,
scaleY: 0.93
}, {
duration: 60,
easing: tween.cubicOut,
onFinish: function onFinish() {
tween(self.asset, {
scaleX: 1,
scaleY: 1
}, {
duration: 80,
easing: tween.cubicIn
});
}
});
};
// For hit detection
self.containsPoint = function (x, y) {
var local = self.toLocal({
x: x,
y: y
});
return local.x > -self.hitbox.width / 2 && local.x < self.hitbox.width / 2 && local.y > -self.hitbox.height / 2 && local.y < self.hitbox.height / 2;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0xffb638
});
/****
* Game Code
****/
// btnBlue image
// btnGreen image
// btnRed image
// Health bar assets
// --- Game State ---
// Ball colors
// Button colors
// Sound
// Music (optional, but initialized for future use)
var ball;
var buttons = [];
var colorOrder = ['red', 'blue', 'green'];
var currentColor = 'red';
var score = 0;
var highScore = storage.highScore || 0;
var timeLimit = 5000; // ms, initial time to respond (5 seconds)
var minTimeLimit = 5000; // ms, minimum allowed (5 seconds)
var timeDecrease = 80; // ms, decrease per correct
var rotationSpeed = 0.02; // initial
var maxRotationSpeed = 0.09;
var rotationIncrease = 0.008;
var waitingForInput = true;
var responseTimer = null;
var lastTapTime = 0;
// --- UI Elements ---
// Add shadow for score text (centered, slightly offset, behind main score)
var scoreShadowTxt = new Text2('0', {
size: 120,
fill: "#000",
alpha: 0.32
});
scoreShadowTxt.anchor.set(0.5, 0);
scoreShadowTxt.y = 60 + 3; // offset shadow downward (closer)
var scoreTxt = new Text2('0', {
size: 120,
fill: 0xFF4500
});
scoreTxt.anchor.set(0.5, 0);
scoreTxt.y = 60;
// Add scoreTxt after timer and hearts to ensure it is always on top
LK.setTimeout(function () {
LK.gui.top.addChild(scoreShadowTxt);
LK.gui.top.addChild(scoreTxt);
}, 0);
var highScoreTxt = new Text2('BEST: ' + highScore, {
size: 60,
fill: 0xFF4500,
font: "'GillSans-Bold',Impact,'Arial Black',Tahoma"
});
highScoreTxt.anchor.set(0.5, 0);
highScoreTxt.y = 190;
LK.gui.top.addChild(highScoreTxt);
// --- Timer Bar (under score) ---
// --- Timer Bar (under score, in GUI) ---
var timerBar = LK.getAsset('timerBar', {
width: 900,
height: 60,
color: 0xffffff,
anchorX: 0.5,
anchorY: 0.0
});
timerBar.x = 0.5; // relative to gui width
timerBar.y = scoreTxt.y + scoreTxt.height + 70; // 70px below score (moved further down)
timerBar.alpha = 0.18;
timerBar.isGUI = true;
LK.gui.top.addChild(timerBar);
var timerBarFill = LK.getAsset('timerBarFill', {
width: 900,
height: 60,
color: 0x2ecc40,
anchorX: 0.5,
anchorY: 0.0
});
timerBarFill.x = 0.5; // relative to gui width
timerBarFill.y = scoreTxt.y + scoreTxt.height + 70; // 70px below score (moved further down)
timerBarFill.isGUI = true;
LK.gui.top.addChild(timerBarFill);
// --- Health Bars (under timer, in GUI) ---
var health = 3;
var maxHealth = 3;
var healthBars = [];
var healthBarSpacing = 130;
var healthBarWidth = 120;
var healthBarHeight = 120;
var healthBarAssets = [{
id: 'healthBarRed',
color: 0xd83318
},
// Red
{
id: 'healthBarBlue',
color: 0x187ad8
},
// Blue
{
id: 'healthBarGreen',
color: 0x2ecc40
} // Green
];
var healthBarY = timerBar.y + timerBar.height + 30; // 30px below timer bar
// Calculate total width of all health bars and starting x for centering
var totalHealthWidth = (maxHealth - 1) * healthBarSpacing;
var healthStartX = 0.5 + -(totalHealthWidth / 2); // 0.5 is center in LK.gui coordinates
for (var i = 0; i < maxHealth; i++) {
var assetInfo = healthBarAssets[i];
var bar = LK.getAsset(assetInfo.id, {
width: healthBarWidth,
height: healthBarHeight,
color: assetInfo.color,
anchorX: 0.5,
anchorY: 0.0
});
// Center health bars under the timer
bar.x = healthStartX + i * healthBarSpacing;
bar.y = healthBarY;
bar.isGUI = true;
LK.gui.top.addChild(bar);
healthBars.push(bar);
}
// Helper to update health bar visuals
function updateHealthBars() {
for (var i = 0; i < maxHealth; i++) {
// Each bar is independent: show full alpha if "alive", faded if "lost"
healthBars[i].alpha = i < health ? 1 : 0.18;
}
}
updateHealthBars();
// --- Setup Ball ---
ball = new ColorBall();
ball.x = 2048 / 2;
ball.y = 1100;
game.addChild(ball);
// --- Setup Buttons ---
var btnSpacing = 480;
var btnY = 2732 - 320;
// Create btnRed
var btnRed = new ColorButton();
btnRed.setColor('red');
btnRed.x = 2048 / 2 - btnSpacing;
btnRed.y = btnY;
game.addChild(btnRed);
// Create btnBlue
var btnBlue = new ColorButton();
btnBlue.setColor('blue');
btnBlue.x = 2048 / 2;
btnBlue.y = btnY;
game.addChild(btnBlue);
// Create btnGreen
var btnGreen = new ColorButton();
btnGreen.setColor('green');
btnGreen.x = 2048 / 2 + btnSpacing;
btnGreen.y = btnY;
game.addChild(btnGreen);
// Place all buttons in the same positions as btnRed, btnBlue, btnGreen
buttons = [btnRed, btnBlue, btnGreen];
// Ensure all color buttons are positioned exactly as btnRed, btnBlue, btnGreen
buttons[0].x = btnRed.x;
buttons[0].y = btnRed.y;
buttons[1].x = btnBlue.x;
buttons[1].y = btnBlue.y;
buttons[2].x = btnGreen.x;
buttons[2].y = btnGreen.y;
// --- Helper Functions ---
function setScore(val) {
score = val;
scoreTxt.setText(score);
if (typeof scoreShadowTxt !== "undefined") {
scoreShadowTxt.setText(score);
}
if (score > highScore) {
highScore = score;
storage.highScore = highScore;
highScoreTxt.setText('BEST: ' + highScore);
}
}
function randomColor() {
// Never repeat the same color twice in a row
var idx = colorOrder.indexOf(currentColor);
var nextIdx = idx;
while (nextIdx === idx) {
nextIdx = Math.floor(Math.random() * 3);
}
return colorOrder[nextIdx];
}
function startNewRound() {
waitingForInput = true;
// Pick new color
currentColor = randomColor();
ball.setColor(currentColor);
ball.animateColorChange();
// Timer always starts from 5 seconds (5000ms) for each round, and decreases by 0.1s for each correct answer
timeLimit = 5000 - score * 100;
if (timeLimit < 500) {
timeLimit = 500; // minimum 0.5s to avoid negative/zero
}
// --- Smoothly increase rotation speed at every multiple of 10 points ---
var baseSpeed = 0.02;
var speedStep = 0.008;
var maxSpeed = 0.09;
var targetSpeed = baseSpeed + Math.floor(score / 10) * speedStep;
if (targetSpeed > maxSpeed) targetSpeed = maxSpeed;
// Only tween if the speed is changing
if (Math.abs(ball.rotationSpeed - targetSpeed) > 0.0001) {
tween.stop(ball, {
rotationSpeed: true
});
tween(ball, {
rotationSpeed: targetSpeed
}, {
duration: 700,
easing: tween.cubicInOut
});
}
rotationSpeed = targetSpeed;
ball.targetRotationSpeed = targetSpeed;
// Start timer
timerBarFill.width = 900;
if (responseTimer) {
LK.clearTimeout(responseTimer);
responseTimer = null;
}
responseTimer = LK.setTimeout(function () {
// Time's up
waitingForInput = false;
LK.effects.flashScreen(0xff0000, 600);
LK.getSound('wrong').play();
health--;
updateHealthBars();
if (health <= 0) {
endGame();
} else {
// Give player another chance, start new round after short delay
responseTimer = LK.setTimeout(function () {
startNewRound();
}, 700);
}
}, timeLimit);
lastTapTime = Date.now();
}
function endGame() {
if (responseTimer) {
LK.clearTimeout(responseTimer);
responseTimer = null;
}
waitingForInput = false;
// Flash ball red
LK.effects.flashObject(ball, 0xff0000, 600);
// Show game over (handled by LK)
LK.showGameOver();
}
// --- Input Handling ---
game.down = function (x, y, obj) {
if (!waitingForInput) {
return;
}
// Divide the screen into three vertical regions: left, middle, right
var region;
if (x < 2048 / 3) {
region = 'left';
} else if (x > 2048 * 2 / 3) {
region = 'right';
} else {
region = 'middle';
}
// Map region to color
var color;
if (region === 'left') {
color = 'red';
} else if (region === 'right') {
color = 'green';
} else {
color = 'blue';
}
// Find the button with this color (for animation/feedback)
var btn = null;
for (var i = 0; i < buttons.length; i++) {
if (buttons[i].color === color) {
btn = buttons[i];
break;
}
}
if (btn) {
handleButtonPress(btn);
}
};
function handleButtonPress(btn) {
if (!waitingForInput) {
return;
}
btn.animatePress();
LK.effects.flashObject(btn.asset, 0xffffff, 120);
waitingForInput = false;
if (btn.color === currentColor) {
// Correct!
setScore(score + 1);
LK.getSound('correct').play();
// Animate ball
tween(ball, {
scaleX: 1.18,
scaleY: 1.18
}, {
duration: 80,
easing: tween.cubicOut,
onFinish: function onFinish() {
tween(ball, {
scaleX: 1,
scaleY: 1
}, {
duration: 100,
easing: tween.cubicIn
});
}
});
// Decrease timer by 0.1 seconds (100ms) for each correct answer
if (responseTimer) {
LK.clearTimeout(responseTimer);
responseTimer = null;
}
responseTimer = LK.setTimeout(function () {
startNewRound();
}, 220);
} else {
// Wrong!
LK.effects.flashScreen(0xff0000, 600);
LK.getSound('wrong').play();
if (responseTimer) {
LK.clearTimeout(responseTimer);
responseTimer = null;
}
health--;
updateHealthBars();
if (health <= 0) {
endGame();
} else {
// Decrease timer by 0.2 seconds (200ms) for each incorrect answer and reset to 3 seconds
timeLimit = 3000 - score * 100 - (maxHealth - health) * 200;
if (timeLimit < 500) {
timeLimit = 500; // minimum 0.5s to avoid negative/zero
}
// Give player another chance, start new round after short delay
responseTimer = LK.setTimeout(function () {
startNewRound();
}, 700);
}
}
}
// --- Game Update Loop ---
game.update = function () {
// Animate timer bar
if (waitingForInput) {
var elapsed = Date.now() - lastTapTime;
var frac = 1 - elapsed / timeLimit;
if (frac < 0) {
frac = 0;
}
timerBarFill.width = 900 * frac;
// Do not change timerBarFill color or tint here to prevent fading
} else {
timerBarFill.width = 0;
}
};
// --- Start Game ---
function startGame() {
LK.playMusic('g');
setScore(0);
timeLimit = 5000;
rotationSpeed = 0.02;
ball.rotationSpeed = rotationSpeed;
ball.targetRotationSpeed = rotationSpeed;
health = maxHealth;
updateHealthBars();
startNewRound();
}
startGame();
// --- Responsive UI (optional, handled by LK) ---
// --- Music (optional, not started by default) ---
// LK.playMusic('bgmusic', {fade: {start: 0, end: 1, duration: 1000}});