/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1", { bestTime: 99999, lastFiveAttempts: "[]" }); /**** * Classes ****/ var ResultText = Container.expand(function () { var self = Container.call(this); var textObj = new Text2("", { size: 80, fill: 0xFFFFFF }); textObj.anchor.set(0.5, 0.5); self.addChild(textObj); self.updateText = function (message) { textObj.setText(message); }; return self; }); var ScoreCard = Container.expand(function () { var self = Container.call(this); // Create card background var cardBg = self.attachAsset('card', { anchorX: 0.5, anchorY: 0.5 }); cardBg.alpha = 0.15; // Previous score text var previousScoreLabel = new Text2("PREVIOUS ATTEMPT", { size: 42, fill: 0xEAEAEA }); previousScoreLabel.anchor.set(0.5, 0.5); previousScoreLabel.y = -80; self.addChild(previousScoreLabel); var previousScoreText = new Text2("--", { size: 80, fill: 0xFFFFFF }); previousScoreText.anchor.set(0.5, 0.5); previousScoreText.y = -20; self.addChild(previousScoreText); // Best score text var bestScoreLabel = new Text2("BEST SCORE", { size: 42, fill: 0xEAEAEA }); bestScoreLabel.anchor.set(0.5, 0.5); bestScoreLabel.y = 40; self.addChild(bestScoreLabel); var bestScoreText = new Text2("--", { size: 80, fill: 0xFFEB3B }); bestScoreText.anchor.set(0.5, 0.5); bestScoreText.y = 100; self.addChild(bestScoreText); // Update method self.updateScores = function (previousScore, bestScore) { if (previousScore === -1) { previousScoreText.setText("FALSE START"); previousScoreText.tint = 0xEA4335; } else if (previousScore <= 0) { previousScoreText.setText("--"); previousScoreText.tint = 0xFFFFFF; } else { previousScoreText.setText(previousScore + " ms"); // Set color based on performance if (previousScore < 250) { previousScoreText.tint = 0x34A853; // Green for good times } else if (previousScore < 350) { previousScoreText.tint = 0xFBBC05; // Yellow for average times } else { previousScoreText.tint = 0xFFFFFF; // White for slower times } } if (bestScore === 99999 || bestScore <= 0) { bestScoreText.setText("--"); } else { bestScoreText.setText(bestScore + " ms"); } // Animate on update self.scale.set(0.95); tween(self.scale, { x: 1, y: 1 }, { duration: 300, easing: tween.easeOutBack }); }; return self; }); var Target = Container.expand(function () { var self = Container.call(this); var targetGraphic = self.attachAsset('target', { anchorX: 0.5, anchorY: 0.5 }); // Add inner circle for modern flat design var innerCircle = LK.getAsset('target', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.7, scaleY: 0.7, tint: 0x222222 }); self.addChild(innerCircle); self.isActive = false; self.isGreen = false; self.startTime = 0; self.activate = function () { self.isActive = true; self.isGreen = false; targetGraphic.tint = 0x4285F4; // Modern blue color innerCircle.tint = 0x2b569e; // Darker blue for inner circle tween(self.scale, { x: 0.85, y: 0.85 }, { duration: 300, easing: tween.easeOutBack }); }; self.deactivate = function () { self.isActive = false; tween(self.scale, { x: 0.8, y: 0.8 }, { duration: 200, easing: tween.easeInSine }); }; self.turnGreen = function () { if (self.isActive && !self.isGreen) { self.isGreen = true; targetGraphic.tint = 0x34A853; // Modern green color innerCircle.tint = 0x1e7e34; // Darker green for inner circle tween(self.scale, { x: 0.9, y: 0.9 }, { duration: 100, easing: tween.easeOutElastic }); self.startTime = Date.now(); } }; self.down = function (x, y, obj) { if (!self.isActive) return; if (self.isGreen) { // Successful click - calculate reaction time var reactionTime = Date.now() - self.startTime; self.deactivate(); LK.getSound('success').play(); game.recordAttempt(reactionTime); } else { // Clicked too early - false start self.deactivate(); LK.getSound('fail').play(); game.recordAttempt(-1); // -1 indicates false start } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x000000 }); /**** * Game Code ****/ // Set modern background color game.setBackgroundColor(0x1D1D28); // Game state function _toConsumableArray(r) { return _arrayWithoutHoles(r) || _iterableToArray(r) || _unsupportedIterableToArray(r) || _nonIterableSpread(); } function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } } function _iterableToArray(r) { if ("undefined" != typeof Symbol && null != r[Symbol.iterator] || null != r["@@iterator"]) return Array.from(r); } function _arrayWithoutHoles(r) { if (Array.isArray(r)) return _arrayLikeToArray(r); } function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; } var gameState = "waiting"; // waiting, ready, testing, results var currentAttempt = 0; var maxAttempts = 5; var attemptResults = []; var delayMin = 2000; // Min delay before turning green (2 seconds) var delayMax = 5000; // Max delay before turning green (5 seconds) var delayTimer = null; var endTestTimer = null; var lastAttemptScore = 0; // Create score card for previous and best scores var scoreCard = new ScoreCard(); scoreCard.x = 2048 / 2; scoreCard.y = 250; game.addChild(scoreCard); scoreCard.updateScores(0, storage.bestTime); // Create game elements var target = new Target(); target.x = 2048 / 2; target.y = 2732 / 2; target.scale.set(0.8); game.addChild(target); // Instructions text var instructionsText = new Text2("Tap to start\n\nWhen the circle turns GREEN, tap it as fast as you can!", { size: 70, fill: 0xF5F5F5 }); instructionsText.anchor.set(0.5, 0.5); instructionsText.x = 2048 / 2; instructionsText.y = 2732 / 2 - 500; game.addChild(instructionsText); // Result text var resultText = new ResultText(); resultText.x = 2048 / 2; resultText.y = 2732 / 2 + 400; game.addChild(resultText); // Attempt counter text var attemptText = new Text2("", { size: 60, fill: 0xEAEAEA }); attemptText.anchor.set(0.5, 0); attemptText.y = 20; // Add some padding from the top LK.gui.top.addChild(attemptText); updateAttemptText(); // Functions function startGame() { gameState = "ready"; currentAttempt = 0; attemptResults = []; lastAttemptScore = 0; updateAttemptText(); // Reset score card scoreCard.updateScores(0, storage.bestTime); instructionsText.setText("GET READY...\n\nWait for the circle to turn GREEN, then tap it as quickly as possible."); LK.playMusic('bgmusic', { fade: { start: 0, end: 1, duration: 1000 } }); startNextAttempt(); } function startNextAttempt() { if (currentAttempt >= maxAttempts) { showResults(); return; } currentAttempt++; updateAttemptText(); gameState = "testing"; target.activate(); resultText.updateText(""); // Random delay before turning green var delay = delayMin + Math.random() * (delayMax - delayMin); if (delayTimer) { LK.clearTimeout(delayTimer); } delayTimer = LK.setTimeout(function () { if (gameState === "testing" && target.isActive) { target.turnGreen(); } }, delay); } function updateAttemptText() { attemptText.setText("Attempt: " + currentAttempt + " / " + maxAttempts); } function showResults() { gameState = "results"; target.deactivate(); LK.getSound('complete').play(); // Calculate average (excluding false starts) var validAttempts = attemptResults.filter(function (time) { return time > 0; }); var average = 0; if (validAttempts.length > 0) { average = validAttempts.reduce(function (sum, time) { return sum + time; }, 0) / validAttempts.length; average = Math.round(average); } // Count false starts var falseStarts = attemptResults.filter(function (time) { return time === -1; }).length; // Update best time var bestTime = storage.bestTime; if (validAttempts.length > 0) { var bestThisRound = Math.min.apply(Math, _toConsumableArray(validAttempts)); if (bestThisRound < bestTime) { storage.bestTime = bestThisRound; bestTime = bestThisRound; } } // Save last five attempts for history try { var lastFiveAttempts = JSON.parse(storage.lastFiveAttempts); if (!Array.isArray(lastFiveAttempts)) { lastFiveAttempts = []; } } catch (e) { lastFiveAttempts = []; } lastFiveAttempts.unshift({ date: Date.now(), average: average, falseStarts: falseStarts }); // Keep only last 5 if (lastFiveAttempts.length > 5) { lastFiveAttempts = lastFiveAttempts.slice(0, 5); } storage.lastFiveAttempts = JSON.stringify(lastFiveAttempts); // Generate rating var rating = getRating(average); // Update score card with final results scoreCard.updateScores(lastAttemptScore, bestTime); // Display results with styled formatting var resultMessage = "RESULTS\n\n"; // Show individual attempts for (var i = 0; i < attemptResults.length; i++) { var time = attemptResults[i]; resultMessage += "Attempt " + (i + 1) + ": "; if (time === -1) { resultMessage += "False Start"; } else { resultMessage += time + " ms"; } resultMessage += "\n"; } resultMessage += "\nAverage: " + average + " ms\n"; resultMessage += "Rating: " + rating + "\n"; resultMessage += "\nTap anywhere to try again"; instructionsText.setText("TEST COMPLETE"); resultText.updateText(resultMessage); // Update score for leaderboard (best time is the score) if (validAttempts.length > 0) { LK.setScore(Math.min.apply(Math, _toConsumableArray(validAttempts))); } } function getRating(averageTime) { if (averageTime <= 0) return "N/A"; if (averageTime < 200) return "Lightning Fast!"; if (averageTime < 250) return "Exceptional"; if (averageTime < 300) return "Very Good"; if (averageTime < 350) return "Above Average"; if (averageTime < 400) return "Average"; if (averageTime < 500) return "Below Average"; return "Needs Practice"; } game.recordAttempt = function (time) { if (gameState !== "testing") return; attemptResults.push(time); lastAttemptScore = time; // Update score card with the latest attempt scoreCard.updateScores(time, storage.bestTime); if (time === -1) { resultText.updateText("False Start!\n\nTap to continue"); } else { resultText.updateText("Reaction Time: " + time + " ms\n\nTap to continue"); } gameState = "ready"; // Clear any pending timers if (delayTimer) { LK.clearTimeout(delayTimer); } // Flash effect based on result if (time === -1) { LK.effects.flashScreen(0xEA4335, 300); // Modern red for false start } else if (time < 250) { LK.effects.flashScreen(0x34A853, 300); // Modern green for great time } else { LK.effects.flashScreen(0xFBBC05, 300); // Modern yellow for average/slow time } // If this was the last attempt, show results after a delay if (currentAttempt >= maxAttempts) { endTestTimer = LK.setTimeout(function () { showResults(); }, 1500); } }; game.down = function (x, y, obj) { // If we're showing results, start a new game if (gameState === "results") { startGame(); return; } // If we're waiting for player to start if (gameState === "waiting") { startGame(); return; } // If we're ready for next attempt if (gameState === "ready" && currentAttempt < maxAttempts) { // Cancel end test timer if it's running if (endTestTimer) { LK.clearTimeout(endTestTimer); endTestTimer = null; } startNextAttempt(); return; } };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
bestTime: 99999,
lastFiveAttempts: "[]"
});
/****
* Classes
****/
var ResultText = Container.expand(function () {
var self = Container.call(this);
var textObj = new Text2("", {
size: 80,
fill: 0xFFFFFF
});
textObj.anchor.set(0.5, 0.5);
self.addChild(textObj);
self.updateText = function (message) {
textObj.setText(message);
};
return self;
});
var ScoreCard = Container.expand(function () {
var self = Container.call(this);
// Create card background
var cardBg = self.attachAsset('card', {
anchorX: 0.5,
anchorY: 0.5
});
cardBg.alpha = 0.15;
// Previous score text
var previousScoreLabel = new Text2("PREVIOUS ATTEMPT", {
size: 42,
fill: 0xEAEAEA
});
previousScoreLabel.anchor.set(0.5, 0.5);
previousScoreLabel.y = -80;
self.addChild(previousScoreLabel);
var previousScoreText = new Text2("--", {
size: 80,
fill: 0xFFFFFF
});
previousScoreText.anchor.set(0.5, 0.5);
previousScoreText.y = -20;
self.addChild(previousScoreText);
// Best score text
var bestScoreLabel = new Text2("BEST SCORE", {
size: 42,
fill: 0xEAEAEA
});
bestScoreLabel.anchor.set(0.5, 0.5);
bestScoreLabel.y = 40;
self.addChild(bestScoreLabel);
var bestScoreText = new Text2("--", {
size: 80,
fill: 0xFFEB3B
});
bestScoreText.anchor.set(0.5, 0.5);
bestScoreText.y = 100;
self.addChild(bestScoreText);
// Update method
self.updateScores = function (previousScore, bestScore) {
if (previousScore === -1) {
previousScoreText.setText("FALSE START");
previousScoreText.tint = 0xEA4335;
} else if (previousScore <= 0) {
previousScoreText.setText("--");
previousScoreText.tint = 0xFFFFFF;
} else {
previousScoreText.setText(previousScore + " ms");
// Set color based on performance
if (previousScore < 250) {
previousScoreText.tint = 0x34A853; // Green for good times
} else if (previousScore < 350) {
previousScoreText.tint = 0xFBBC05; // Yellow for average times
} else {
previousScoreText.tint = 0xFFFFFF; // White for slower times
}
}
if (bestScore === 99999 || bestScore <= 0) {
bestScoreText.setText("--");
} else {
bestScoreText.setText(bestScore + " ms");
}
// Animate on update
self.scale.set(0.95);
tween(self.scale, {
x: 1,
y: 1
}, {
duration: 300,
easing: tween.easeOutBack
});
};
return self;
});
var Target = Container.expand(function () {
var self = Container.call(this);
var targetGraphic = self.attachAsset('target', {
anchorX: 0.5,
anchorY: 0.5
});
// Add inner circle for modern flat design
var innerCircle = LK.getAsset('target', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.7,
scaleY: 0.7,
tint: 0x222222
});
self.addChild(innerCircle);
self.isActive = false;
self.isGreen = false;
self.startTime = 0;
self.activate = function () {
self.isActive = true;
self.isGreen = false;
targetGraphic.tint = 0x4285F4; // Modern blue color
innerCircle.tint = 0x2b569e; // Darker blue for inner circle
tween(self.scale, {
x: 0.85,
y: 0.85
}, {
duration: 300,
easing: tween.easeOutBack
});
};
self.deactivate = function () {
self.isActive = false;
tween(self.scale, {
x: 0.8,
y: 0.8
}, {
duration: 200,
easing: tween.easeInSine
});
};
self.turnGreen = function () {
if (self.isActive && !self.isGreen) {
self.isGreen = true;
targetGraphic.tint = 0x34A853; // Modern green color
innerCircle.tint = 0x1e7e34; // Darker green for inner circle
tween(self.scale, {
x: 0.9,
y: 0.9
}, {
duration: 100,
easing: tween.easeOutElastic
});
self.startTime = Date.now();
}
};
self.down = function (x, y, obj) {
if (!self.isActive) return;
if (self.isGreen) {
// Successful click - calculate reaction time
var reactionTime = Date.now() - self.startTime;
self.deactivate();
LK.getSound('success').play();
game.recordAttempt(reactionTime);
} else {
// Clicked too early - false start
self.deactivate();
LK.getSound('fail').play();
game.recordAttempt(-1); // -1 indicates false start
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
// Set modern background color
game.setBackgroundColor(0x1D1D28);
// Game state
function _toConsumableArray(r) {
return _arrayWithoutHoles(r) || _iterableToArray(r) || _unsupportedIterableToArray(r) || _nonIterableSpread();
}
function _nonIterableSpread() {
throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
}
function _unsupportedIterableToArray(r, a) {
if (r) {
if ("string" == typeof r) return _arrayLikeToArray(r, a);
var t = {}.toString.call(r).slice(8, -1);
return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0;
}
}
function _iterableToArray(r) {
if ("undefined" != typeof Symbol && null != r[Symbol.iterator] || null != r["@@iterator"]) return Array.from(r);
}
function _arrayWithoutHoles(r) {
if (Array.isArray(r)) return _arrayLikeToArray(r);
}
function _arrayLikeToArray(r, a) {
(null == a || a > r.length) && (a = r.length);
for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e];
return n;
}
var gameState = "waiting"; // waiting, ready, testing, results
var currentAttempt = 0;
var maxAttempts = 5;
var attemptResults = [];
var delayMin = 2000; // Min delay before turning green (2 seconds)
var delayMax = 5000; // Max delay before turning green (5 seconds)
var delayTimer = null;
var endTestTimer = null;
var lastAttemptScore = 0;
// Create score card for previous and best scores
var scoreCard = new ScoreCard();
scoreCard.x = 2048 / 2;
scoreCard.y = 250;
game.addChild(scoreCard);
scoreCard.updateScores(0, storage.bestTime);
// Create game elements
var target = new Target();
target.x = 2048 / 2;
target.y = 2732 / 2;
target.scale.set(0.8);
game.addChild(target);
// Instructions text
var instructionsText = new Text2("Tap to start\n\nWhen the circle turns GREEN, tap it as fast as you can!", {
size: 70,
fill: 0xF5F5F5
});
instructionsText.anchor.set(0.5, 0.5);
instructionsText.x = 2048 / 2;
instructionsText.y = 2732 / 2 - 500;
game.addChild(instructionsText);
// Result text
var resultText = new ResultText();
resultText.x = 2048 / 2;
resultText.y = 2732 / 2 + 400;
game.addChild(resultText);
// Attempt counter text
var attemptText = new Text2("", {
size: 60,
fill: 0xEAEAEA
});
attemptText.anchor.set(0.5, 0);
attemptText.y = 20; // Add some padding from the top
LK.gui.top.addChild(attemptText);
updateAttemptText();
// Functions
function startGame() {
gameState = "ready";
currentAttempt = 0;
attemptResults = [];
lastAttemptScore = 0;
updateAttemptText();
// Reset score card
scoreCard.updateScores(0, storage.bestTime);
instructionsText.setText("GET READY...\n\nWait for the circle to turn GREEN, then tap it as quickly as possible.");
LK.playMusic('bgmusic', {
fade: {
start: 0,
end: 1,
duration: 1000
}
});
startNextAttempt();
}
function startNextAttempt() {
if (currentAttempt >= maxAttempts) {
showResults();
return;
}
currentAttempt++;
updateAttemptText();
gameState = "testing";
target.activate();
resultText.updateText("");
// Random delay before turning green
var delay = delayMin + Math.random() * (delayMax - delayMin);
if (delayTimer) {
LK.clearTimeout(delayTimer);
}
delayTimer = LK.setTimeout(function () {
if (gameState === "testing" && target.isActive) {
target.turnGreen();
}
}, delay);
}
function updateAttemptText() {
attemptText.setText("Attempt: " + currentAttempt + " / " + maxAttempts);
}
function showResults() {
gameState = "results";
target.deactivate();
LK.getSound('complete').play();
// Calculate average (excluding false starts)
var validAttempts = attemptResults.filter(function (time) {
return time > 0;
});
var average = 0;
if (validAttempts.length > 0) {
average = validAttempts.reduce(function (sum, time) {
return sum + time;
}, 0) / validAttempts.length;
average = Math.round(average);
}
// Count false starts
var falseStarts = attemptResults.filter(function (time) {
return time === -1;
}).length;
// Update best time
var bestTime = storage.bestTime;
if (validAttempts.length > 0) {
var bestThisRound = Math.min.apply(Math, _toConsumableArray(validAttempts));
if (bestThisRound < bestTime) {
storage.bestTime = bestThisRound;
bestTime = bestThisRound;
}
}
// Save last five attempts for history
try {
var lastFiveAttempts = JSON.parse(storage.lastFiveAttempts);
if (!Array.isArray(lastFiveAttempts)) {
lastFiveAttempts = [];
}
} catch (e) {
lastFiveAttempts = [];
}
lastFiveAttempts.unshift({
date: Date.now(),
average: average,
falseStarts: falseStarts
});
// Keep only last 5
if (lastFiveAttempts.length > 5) {
lastFiveAttempts = lastFiveAttempts.slice(0, 5);
}
storage.lastFiveAttempts = JSON.stringify(lastFiveAttempts);
// Generate rating
var rating = getRating(average);
// Update score card with final results
scoreCard.updateScores(lastAttemptScore, bestTime);
// Display results with styled formatting
var resultMessage = "RESULTS\n\n";
// Show individual attempts
for (var i = 0; i < attemptResults.length; i++) {
var time = attemptResults[i];
resultMessage += "Attempt " + (i + 1) + ": ";
if (time === -1) {
resultMessage += "False Start";
} else {
resultMessage += time + " ms";
}
resultMessage += "\n";
}
resultMessage += "\nAverage: " + average + " ms\n";
resultMessage += "Rating: " + rating + "\n";
resultMessage += "\nTap anywhere to try again";
instructionsText.setText("TEST COMPLETE");
resultText.updateText(resultMessage);
// Update score for leaderboard (best time is the score)
if (validAttempts.length > 0) {
LK.setScore(Math.min.apply(Math, _toConsumableArray(validAttempts)));
}
}
function getRating(averageTime) {
if (averageTime <= 0) return "N/A";
if (averageTime < 200) return "Lightning Fast!";
if (averageTime < 250) return "Exceptional";
if (averageTime < 300) return "Very Good";
if (averageTime < 350) return "Above Average";
if (averageTime < 400) return "Average";
if (averageTime < 500) return "Below Average";
return "Needs Practice";
}
game.recordAttempt = function (time) {
if (gameState !== "testing") return;
attemptResults.push(time);
lastAttemptScore = time;
// Update score card with the latest attempt
scoreCard.updateScores(time, storage.bestTime);
if (time === -1) {
resultText.updateText("False Start!\n\nTap to continue");
} else {
resultText.updateText("Reaction Time: " + time + " ms\n\nTap to continue");
}
gameState = "ready";
// Clear any pending timers
if (delayTimer) {
LK.clearTimeout(delayTimer);
}
// Flash effect based on result
if (time === -1) {
LK.effects.flashScreen(0xEA4335, 300); // Modern red for false start
} else if (time < 250) {
LK.effects.flashScreen(0x34A853, 300); // Modern green for great time
} else {
LK.effects.flashScreen(0xFBBC05, 300); // Modern yellow for average/slow time
}
// If this was the last attempt, show results after a delay
if (currentAttempt >= maxAttempts) {
endTestTimer = LK.setTimeout(function () {
showResults();
}, 1500);
}
};
game.down = function (x, y, obj) {
// If we're showing results, start a new game
if (gameState === "results") {
startGame();
return;
}
// If we're waiting for player to start
if (gameState === "waiting") {
startGame();
return;
}
// If we're ready for next attempt
if (gameState === "ready" && currentAttempt < maxAttempts) {
// Cancel end test timer if it's running
if (endTestTimer) {
LK.clearTimeout(endTestTimer);
endTestTimer = null;
}
startNextAttempt();
return;
}
};