Code edit (1 edits merged)
Please save this source code
User prompt
F1 Reflex Lights
Initial prompt
We have a 5 lights ( formula1 start lights ) lights turn ln order. Then at a random time they all turn green. When we touch the screen, the time after the green light turns on should be displayed (in seconds and seconds, for example 1.134). If it is pressed before the green light is on, a warning should be given and there should be a button to restart the game. After the specified time, another notification will appear and it can be restarted.
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
// Light class: represents a single light (red or green)
var StartLight = Container.expand(function () {
var self = Container.call(this);
// By default, start as "off" (invisible)
self.state = 'off'; // 'off', 'red', 'green'
self.lightAsset = null;
// Set state: 'off', 'red', 'green'
self.setState = function (state) {
if (self.lightAsset) {
self.removeChild(self.lightAsset);
self.lightAsset = null;
}
self.state = state;
if (state === 'red') {
self.lightAsset = self.attachAsset('redLight', {
anchorX: 0.5,
anchorY: 0.5
});
self.lightAsset.alpha = 1;
} else if (state === 'green') {
self.lightAsset = self.attachAsset('greenLight', {
anchorX: 0.5,
anchorY: 0.5
});
self.lightAsset.alpha = 1;
}
// else: off, do not show anything
};
// Flash effect for green
self.flashGreen = function () {
if (self.state === 'green' && self.lightAsset) {
self.lightAsset.alpha = 0.5;
tween(self.lightAsset, {
alpha: 1
}, {
duration: 200,
easing: tween.easeOut
});
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
// No title, no description
// Always backgroundColor is black
backgroundColor: 0x000000
});
/****
* Game Code
****/
// --- Layout constants ---
// 5 red lights (circles), 1 green light (circle), warning text, result text
// We'll use shapes for the lights
// Red light asset
// Green light asset
// We'll use tween for light animations
// shape asset is only used as a background in background containers, not as a top shape
var NUM_LIGHTS = 5;
var LIGHT_SPACING = 260; // px between lights
var LIGHT_Y = 900; // vertical position of lights
// Center the row of lights, accounting for the width of a light
var LIGHT_WIDTH = 180; // matches asset width
var LIGHT_TABLE_WIDTH = (NUM_LIGHTS - 1) * LIGHT_SPACING + LIGHT_WIDTH;
var LIGHT_START_X = (2048 - LIGHT_TABLE_WIDTH) / 2 + LIGHT_WIDTH / 2;
// --- State variables ---
var lights = [];
var currentPhase = 'idle'; // 'idle', 'lights', 'waitGreen', 'green', 'result', 'falseStart'
var lightIndex = 0;
var timers = [];
var greenTimestamp = 0;
var tapTimestamp = 0;
var resultText = null;
var warningText = null;
var instructionText = null;
// --- GUI elements ---
var guiResultText = null;
// --- Best time tracking ---
var bestTime = null;
var BEST_TIME_KEY = "f1_best_time";
// Load best time from storage if available
var storedBest = storage[BEST_TIME_KEY];
if (storedBest !== null && storedBest !== undefined) {
bestTime = storedBest;
}
// --- Best time GUI text ---
var bestTimeText = new Text2("", {
size: 80,
fill: 0xFFD700
});
bestTimeText.anchor.set(0.5, 0);
LK.gui.top.addChild(bestTimeText);
bestTimeText.x = 2048 / 2;
bestTimeText.y = 80;
function updateBestTimeText() {
if (bestTime !== null) {
bestTimeText.setText("Best: " + formatReaction(bestTime) + "s");
bestTimeText.visible = true;
} else {
bestTimeText.setText("Best: --");
bestTimeText.visible = true;
}
}
updateBestTimeText();
// --- Background containers ---
// Pre-game and post-game backgrounds
var backgroundPre = LK.getAsset('background', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 1366 // center vertically, 2732/2
});
var backgroundPost = LK.getAsset('background', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 1366
});
backgroundPost.visible = false;
game.addChild(backgroundPre);
game.addChild(backgroundPost);
// --- Helper functions ---
function clearTimers() {
for (var i = 0; i < timers.length; ++i) {
LK.clearTimeout(timers[i]);
}
timers = [];
}
function resetLights() {
for (var i = 0; i < lights.length; ++i) {
lights[i].setState('off');
}
}
function showInstruction(msg) {
if (!instructionText) {
instructionText = new Text2(msg, {
size: 90,
fill: 0xFFFFFF
});
instructionText.anchor.set(0.5, 0);
LK.gui.top.addChild(instructionText);
instructionText.y = 180;
}
instructionText.setText(msg);
instructionText.visible = true;
}
function hideInstruction() {
if (instructionText) instructionText.visible = false;
}
function showWarning(msg) {
if (!warningText) {
warningText = new Text2(msg, {
size: 120,
fill: 0xFF4444
});
warningText.anchor.set(0.5, 0.5);
LK.gui.center.addChild(warningText);
}
warningText.setText(msg);
warningText.visible = true;
}
function hideWarning() {
if (warningText) warningText.visible = false;
}
function showResult(msg) {
if (!resultText) {
resultText = new Text2(msg, {
size: 120,
fill: 0x22FF22
});
resultText.anchor.set(0.5, 0.5);
LK.gui.center.addChild(resultText);
}
resultText.setText(msg);
resultText.visible = true;
}
function hideResult() {
if (resultText) resultText.visible = false;
}
// --- Game logic ---
function startSequence() {
clearTimers();
resetLights();
hideWarning();
hideResult();
backgroundPre.visible = true;
backgroundPost.visible = false;
showInstruction("Wait for green, then tap as fast as you can!");
// Play F1 lights sound slightly earlier before the first red light
LK.setTimeout(function () {
LK.getSound('f1lights').play();
}, 500);
currentPhase = 'lights';
lightIndex = 0;
// Start lighting up the reds, one by one
timers.push(LK.setTimeout(lightNext, 700));
}
function lightNext() {
if (lightIndex < NUM_LIGHTS) {
lights[lightIndex].setState('red');
// Play F1 lights sound for each red light as it turns on (5 times, one per light)
LK.getSound('f1lights').play();
lightIndex += 1;
timers.push(LK.setTimeout(lightNext, 700));
} else {
// All reds are on, now wait random time before green
currentPhase = 'waitGreen';
// Random delay: 0.7s to 2.2s
var delay = 700 + Math.floor(Math.random() * 1500);
timers.push(LK.setTimeout(showGreen, delay));
}
}
function showGreen() {
// Play green light sound at the same time as lights turn green
LK.getSound('f1greenlightsound').play();
// All lights turn green
for (var i = 0; i < lights.length; ++i) {
lights[i].setState('green');
lights[i].flashGreen();
}
currentPhase = 'green';
greenTimestamp = Date.now();
showInstruction("GO!");
}
function handleTap() {
if (currentPhase === 'idle' || currentPhase === 'result' || currentPhase === 'falseStart') {
// Start new round
startSequence();
return;
}
if (currentPhase === 'lights' || currentPhase === 'waitGreen') {
// False start!
clearTimers();
// Play false start sound
LK.getSound('f1falsestart').play();
currentPhase = 'falseStart';
hideInstruction();
showWarning("False Start!\nTap to try again");
resetLights();
backgroundPre.visible = false;
backgroundPost.visible = true;
return;
}
if (currentPhase === 'green') {
tapTimestamp = Date.now();
var reaction = tapTimestamp - greenTimestamp;
currentPhase = 'result';
hideInstruction();
// Check and update best time
var isBest = false;
if (bestTime === null || reaction < bestTime) {
bestTime = reaction;
storage[BEST_TIME_KEY] = bestTime;
isBest = true;
updateBestTimeText();
// Play new best score sound
LK.getSound('f1newbest').play();
}
var resultMsg = "Reaction Time:\n" + formatReaction(reaction);
if (bestTime !== null) {
resultMsg += "\nBest: " + formatReaction(bestTime);
}
if (isBest) {
resultMsg += "\n(New Record!)";
}
resultMsg += "\n\nTap to try again";
showResult(resultMsg);
resetLights();
backgroundPre.visible = false;
backgroundPost.visible = true;
return;
}
}
function formatReaction(ms) {
// Always show as seconds with 3 decimals, e.g. 0.287
var seconds = ms / 1000;
var str = seconds.toFixed(3);
return str;
}
// --- Add background behind the lights ---
// (Handled by backgroundPre and backgroundPost above)
// --- Setup lights ---
for (var i = 0; i < NUM_LIGHTS; ++i) {
var light = new StartLight();
light.x = LIGHT_START_X + i * LIGHT_SPACING;
light.y = LIGHT_Y;
light.setState('off');
game.addChild(light);
lights.push(light);
}
// --- Setup instruction text ---
showInstruction("Tap to start!");
// --- Input handling ---
game.down = function (x, y, obj) {
handleTap();
};
// --- Clean up on game over (not needed, but for completeness) ---
game.destroy = function () {
clearTimers();
};
// --- Start in idle state ---
currentPhase = 'idle';
// --- No update loop needed ---; /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
// Light class: represents a single light (red or green)
var StartLight = Container.expand(function () {
var self = Container.call(this);
// By default, start as "off" (invisible)
self.state = 'off'; // 'off', 'red', 'green'
self.lightAsset = null;
// Set state: 'off', 'red', 'green'
self.setState = function (state) {
if (self.lightAsset) {
self.removeChild(self.lightAsset);
self.lightAsset = null;
}
self.state = state;
if (state === 'red') {
self.lightAsset = self.attachAsset('redLight', {
anchorX: 0.5,
anchorY: 0.5
});
self.lightAsset.alpha = 1;
} else if (state === 'green') {
self.lightAsset = self.attachAsset('greenLight', {
anchorX: 0.5,
anchorY: 0.5
});
self.lightAsset.alpha = 1;
}
// else: off, do not show anything
};
// Flash effect for green
self.flashGreen = function () {
if (self.state === 'green' && self.lightAsset) {
self.lightAsset.alpha = 0.5;
tween(self.lightAsset, {
alpha: 1
}, {
duration: 200,
easing: tween.easeOut
});
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
// No title, no description
// Always backgroundColor is black
backgroundColor: 0x000000
});
/****
* Game Code
****/
// --- Layout constants ---
// 5 red lights (circles), 1 green light (circle), warning text, result text
// We'll use shapes for the lights
// Red light asset
// Green light asset
// We'll use tween for light animations
// shape asset is only used as a background in background containers, not as a top shape
var NUM_LIGHTS = 5;
var LIGHT_SPACING = 260; // px between lights
var LIGHT_Y = 900; // vertical position of lights
// Center the row of lights, accounting for the width of a light
var LIGHT_WIDTH = 180; // matches asset width
var LIGHT_TABLE_WIDTH = (NUM_LIGHTS - 1) * LIGHT_SPACING + LIGHT_WIDTH;
var LIGHT_START_X = (2048 - LIGHT_TABLE_WIDTH) / 2 + LIGHT_WIDTH / 2;
// --- State variables ---
var lights = [];
var currentPhase = 'idle'; // 'idle', 'lights', 'waitGreen', 'green', 'result', 'falseStart'
var lightIndex = 0;
var timers = [];
var greenTimestamp = 0;
var tapTimestamp = 0;
var resultText = null;
var warningText = null;
var instructionText = null;
// --- GUI elements ---
var guiResultText = null;
// --- Best time tracking ---
var bestTime = null;
var BEST_TIME_KEY = "f1_best_time";
// Load best time from storage if available
var storedBest = storage[BEST_TIME_KEY];
if (storedBest !== null && storedBest !== undefined) {
bestTime = storedBest;
}
// --- Best time GUI text ---
var bestTimeText = new Text2("", {
size: 80,
fill: 0xFFD700
});
bestTimeText.anchor.set(0.5, 0);
LK.gui.top.addChild(bestTimeText);
bestTimeText.x = 2048 / 2;
bestTimeText.y = 80;
function updateBestTimeText() {
if (bestTime !== null) {
bestTimeText.setText("Best: " + formatReaction(bestTime) + "s");
bestTimeText.visible = true;
} else {
bestTimeText.setText("Best: --");
bestTimeText.visible = true;
}
}
updateBestTimeText();
// --- Background containers ---
// Pre-game and post-game backgrounds
var backgroundPre = LK.getAsset('background', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 1366 // center vertically, 2732/2
});
var backgroundPost = LK.getAsset('background', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 1366
});
backgroundPost.visible = false;
game.addChild(backgroundPre);
game.addChild(backgroundPost);
// --- Helper functions ---
function clearTimers() {
for (var i = 0; i < timers.length; ++i) {
LK.clearTimeout(timers[i]);
}
timers = [];
}
function resetLights() {
for (var i = 0; i < lights.length; ++i) {
lights[i].setState('off');
}
}
function showInstruction(msg) {
if (!instructionText) {
instructionText = new Text2(msg, {
size: 90,
fill: 0xFFFFFF
});
instructionText.anchor.set(0.5, 0);
LK.gui.top.addChild(instructionText);
instructionText.y = 180;
}
instructionText.setText(msg);
instructionText.visible = true;
}
function hideInstruction() {
if (instructionText) instructionText.visible = false;
}
function showWarning(msg) {
if (!warningText) {
warningText = new Text2(msg, {
size: 120,
fill: 0xFF4444
});
warningText.anchor.set(0.5, 0.5);
LK.gui.center.addChild(warningText);
}
warningText.setText(msg);
warningText.visible = true;
}
function hideWarning() {
if (warningText) warningText.visible = false;
}
function showResult(msg) {
if (!resultText) {
resultText = new Text2(msg, {
size: 120,
fill: 0x22FF22
});
resultText.anchor.set(0.5, 0.5);
LK.gui.center.addChild(resultText);
}
resultText.setText(msg);
resultText.visible = true;
}
function hideResult() {
if (resultText) resultText.visible = false;
}
// --- Game logic ---
function startSequence() {
clearTimers();
resetLights();
hideWarning();
hideResult();
backgroundPre.visible = true;
backgroundPost.visible = false;
showInstruction("Wait for green, then tap as fast as you can!");
// Play F1 lights sound slightly earlier before the first red light
LK.setTimeout(function () {
LK.getSound('f1lights').play();
}, 500);
currentPhase = 'lights';
lightIndex = 0;
// Start lighting up the reds, one by one
timers.push(LK.setTimeout(lightNext, 700));
}
function lightNext() {
if (lightIndex < NUM_LIGHTS) {
lights[lightIndex].setState('red');
// Play F1 lights sound for each red light as it turns on (5 times, one per light)
LK.getSound('f1lights').play();
lightIndex += 1;
timers.push(LK.setTimeout(lightNext, 700));
} else {
// All reds are on, now wait random time before green
currentPhase = 'waitGreen';
// Random delay: 0.7s to 2.2s
var delay = 700 + Math.floor(Math.random() * 1500);
timers.push(LK.setTimeout(showGreen, delay));
}
}
function showGreen() {
// Play green light sound at the same time as lights turn green
LK.getSound('f1greenlightsound').play();
// All lights turn green
for (var i = 0; i < lights.length; ++i) {
lights[i].setState('green');
lights[i].flashGreen();
}
currentPhase = 'green';
greenTimestamp = Date.now();
showInstruction("GO!");
}
function handleTap() {
if (currentPhase === 'idle' || currentPhase === 'result' || currentPhase === 'falseStart') {
// Start new round
startSequence();
return;
}
if (currentPhase === 'lights' || currentPhase === 'waitGreen') {
// False start!
clearTimers();
// Play false start sound
LK.getSound('f1falsestart').play();
currentPhase = 'falseStart';
hideInstruction();
showWarning("False Start!\nTap to try again");
resetLights();
backgroundPre.visible = false;
backgroundPost.visible = true;
return;
}
if (currentPhase === 'green') {
tapTimestamp = Date.now();
var reaction = tapTimestamp - greenTimestamp;
currentPhase = 'result';
hideInstruction();
// Check and update best time
var isBest = false;
if (bestTime === null || reaction < bestTime) {
bestTime = reaction;
storage[BEST_TIME_KEY] = bestTime;
isBest = true;
updateBestTimeText();
// Play new best score sound
LK.getSound('f1newbest').play();
}
var resultMsg = "Reaction Time:\n" + formatReaction(reaction);
if (bestTime !== null) {
resultMsg += "\nBest: " + formatReaction(bestTime);
}
if (isBest) {
resultMsg += "\n(New Record!)";
}
resultMsg += "\n\nTap to try again";
showResult(resultMsg);
resetLights();
backgroundPre.visible = false;
backgroundPost.visible = true;
return;
}
}
function formatReaction(ms) {
// Always show as seconds with 3 decimals, e.g. 0.287
var seconds = ms / 1000;
var str = seconds.toFixed(3);
return str;
}
// --- Add background behind the lights ---
// (Handled by backgroundPre and backgroundPost above)
// --- Setup lights ---
for (var i = 0; i < NUM_LIGHTS; ++i) {
var light = new StartLight();
light.x = LIGHT_START_X + i * LIGHT_SPACING;
light.y = LIGHT_Y;
light.setState('off');
game.addChild(light);
lights.push(light);
}
// --- Setup instruction text ---
showInstruction("Tap to start!");
// --- Input handling ---
game.down = function (x, y, obj) {
handleTap();
};
// --- Clean up on game over (not needed, but for completeness) ---
game.destroy = function () {
clearTimers();
};
// --- Start in idle state ---
currentPhase = 'idle';
// --- No update loop needed ---;