User prompt
bigger this weapon
User prompt
When the player hits a target: The word "HIT" will appear in the center of the screen It will be bold and bright, with a quick electric or flash effect It stays on screen for about 0.5 to 1 second, then fades out When the player misses a target: The word "MISS" will appear in the center of the screen It will be shown in neon red or magenta, possibly with a flicker or glitch animation It also stays visible for 0.5 to 1 second before disappearing
User prompt
Game start with 0 , 100 countdown
User prompt
Show my weapon on screen
User prompt
Increase totalTargets to 100 for 100 enemies per game session
User prompt
The game will record the fastest reaction time achieved during each session.
User prompt
target auto-miss timeout to 1.2 seconds for all target sizes and zones
User prompt
Each target will stay visible on screen for 1.5 seconds before disappearing (if not hit by the player). This duration applies to all target sizes and zones unless otherwise modified for difficulty scaling.
User prompt
Center Zone Target size: 80x80 px Spawn frequency: 65% Spawn position: Randomized within the central screen region
User prompt
Total Target Spawns: 100 enemies per game session
User prompt
Please fix the bug: 'Timeout.tick error: isGameActive is not defined' in or related to this line: 'if (enemy.isHit || !isGameActive) {' Line Number: 276
User prompt
create a new screen layout assets
User prompt
Targets can appear in three different sizes: 80x80, 160x160, and 320x320 pixels — but most of the time, they will be 80x80 in size.
Code edit (1 edits merged)
Please save this source code
User prompt
Reflexometry
Initial prompt
Create a game set in a cyberpunk-themed environment, displayed on a small screen with a photo-style background featuring futuristic neon elements. Enemies appear on the screen, designed in a "bad guy" aesthetic to match the cyberpunk style. Our hero uses an electric weapon to shoot. There are 15 targets total. One enemy appears at a time, spawning randomly from different positions. Each target stays visible for 1 second, then quickly drops down and disappears. The player's reaction time will be measured. The game could be named Reflexometry. Each shot has a unique sound effect. A main background music track with a cyberpunk/synthwave style plays throughout. Each time the player hits a target, a different hit sound is played. The number of targets hit is displayed on the screen at all times. Targets appear as paper-like cutouts, styled to fit the cyberpunk aesthetic (e.g., glitch effects, neon outlines, etc.).
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // Enemy target class var Enemy = Container.expand(function () { var self = Container.call(this); // Attach enemy image, anchor center var enemyImg = self.attachAsset('enemy', { anchorX: 0.5, anchorY: 0.5 }); // For hit animation self.isHit = false; // Show hit effect self.showHit = function () { if (self.isHit) return; self.isHit = true; // Add hit effect var hitFx = self.attachAsset('hit', { anchorX: 0.5, anchorY: 0.5, x: 0, y: 0, alpha: 0.8, scaleX: 0.7, scaleY: 0.7 }); tween(hitFx, { alpha: 0, scaleX: 1.3, scaleY: 1.3 }, { duration: 350, easing: tween.cubicOut, onFinish: function onFinish() { hitFx.destroy(); } }); }; // Show shot effect self.showShot = function (localX, localY) { var shotFx = self.attachAsset('shot', { anchorX: 0.1, anchorY: 0.5, x: localX, y: localY, alpha: 0.9, scaleX: 0.7 + Math.random() * 0.3, scaleY: 0.7 + Math.random() * 0.3, rotation: (Math.random() - 0.5) * 0.5 }); tween(shotFx, { alpha: 0, scaleX: 1.2, scaleY: 1.2 }, { duration: 180, easing: tween.linear, onFinish: function onFinish() { shotFx.destroy(); } }); }; // Handle tap self.down = function (x, y, obj) { if (self.isHit) return; self.showShot(0, 0); LK.getSound('shotSfx').play(); // Mark as hit self.showHit(); LK.getSound('hitSfx').play(); // Notify game if (typeof onEnemyHit === 'function') { onEnemyHit(self); } }; return self; }); // LaserTrail class: draws a fading laser from (x0, y0) to (x1, y1) var LaserTrail = Container.expand(function () { var self = Container.call(this); // Draw a rectangle as the laser beam between (x0, y0) and (x1, y1) // Call: .init(x0, y0, x1, y1) self.init = function (x0, y0, x1, y1) { // Calculate distance and angle var dx = x1 - x0; var dy = y1 - y0; var dist = Math.sqrt(dx * dx + dy * dy); var angle = Math.atan2(dy, dx); // Use a neon blue color for the laser var laserWidth = Math.max(24, weaponSize * 0.06); // scale with weapon var laserAsset = self.attachAsset('neon_sweep', { anchorX: 0, anchorY: 0.5, x: 0, y: 0, width: dist, height: laserWidth, alpha: 0.85 }); // Position and rotate the container self.x = x0; self.y = y0; self.rotation = angle; // Animate fade out and destroy tween(self, { alpha: 0 }, { duration: 320, easing: tween.cubicOut, onFinish: function onFinish() { self.destroy(); } }); }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x0a0a1a // Deep blue-black for cyberpunk }); /**** * Game Code ****/ // Music: synthwave background // Sound: glitch hit // Sound: electric shot // Neon-glitch hit effect // Electric shot effect (neon bolt) // Neon-glitch enemy (paper cutout style) // Add cyberpunk neon city photo-style background var bg = LK.getAsset('cyberpunk_bg', { anchorX: 0, anchorY: 0, x: 0, y: 0, width: 2048, height: 2732 }); game.addChildAt(bg, 0); // Ensure background is at the back // Add subtle animated neon light sweep overlay for ambiance var neonSweep = LK.getAsset('neon_sweep', { anchorX: 0, anchorY: 0, x: 0, y: 0, width: 2048, height: 2732, alpha: 0.18 }); game.addChild(neonSweep); tween(neonSweep, { x: 2048 }, { duration: 3200, repeat: Infinity, yoyo: true, easing: tween.sineInOut }); // Play synthwave music LK.playMusic('synthwave'); // Game state var isGameActive = true; // Track if the game is currently active var totalTargets = 100; var targetsHit = 0; var currentTarget = null; var targetIndex = 0; var reactionTimes = []; var targetAppearTime = 0; // Track consecutive accurate hits for encouragement message var consecutiveAccurateHits = 0; // Message text for encouragement, hidden by default var encouragementTxt = new Text2("You're doing great!", { size: 100, fill: 0xFFFC00 }); encouragementTxt.anchor.set(0.5, 0.5); encouragementTxt.visible = false; LK.gui.center.addChild(encouragementTxt); // Add neon-glitch border overlay for cyberpunk effect var borderOverlay = LK.getAsset('neon_border', { anchorX: 0, anchorY: 0, x: 0, y: 0, width: 2048, height: 2732, alpha: 0.7 }); game.addChild(borderOverlay); // Score text var scoreTxt = new Text2('0 / 15', { size: 120, fill: 0x00FFF7 }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); // Add a small neon-glitch logo at the top center for game branding var logo = LK.getAsset('reflexometry_logo', { anchorX: 0.5, anchorY: 0, x: 1024, y: 10, width: 400, height: 100 }); LK.gui.top.addChild(logo); // Reaction time text var reactionTxt = new Text2('Reaction: -- ms', { size: 70, fill: 0xFF00C8 }); reactionTxt.anchor.set(0.5, 0); LK.gui.top.addChild(reactionTxt); reactionTxt.y = 130; // Fastest reaction time text var fastestTxt = new Text2('Fastest: -- ms', { size: 60, fill: 0x00FFB0 }); fastestTxt.anchor.set(0.5, 0); LK.gui.top.addChild(fastestTxt); fastestTxt.y = 210; // Track fastest reaction time for this session var fastestReaction = null; // End screen text (hidden by default) var endTxt = new Text2('', { size: 110, fill: 0xFFF600 }); endTxt.anchor.set(0.5, 0.5); endTxt.visible = false; LK.gui.center.addChild(endTxt); // Used for event callback var onEnemyHit = null; // Generate random position for enemy (avoid top 200px and bottom 200px, and left 100px/right 100px) function getRandomEnemyPos(enemyW, enemyH) { var marginX = 160; var marginY = 220; var x = marginX + Math.random() * (2048 - 2 * marginX); var y = marginY + Math.random() * (2732 - 2 * marginY); return { x: x, y: y }; } // Show next target function showNextTarget() { if (currentTarget) { currentTarget.destroy(); currentTarget = null; } if (targetIndex >= totalTargets) { endGame(); return; } // Randomly select enemy size and spawn region var sizeRand = Math.random(); var enemySize = 80; var spawnInCenter = false; if (sizeRand < 0.65) { // 65%: 80x80 in center zone enemySize = 80; spawnInCenter = true; } else if (sizeRand < 0.90) { // 25%: 160x160 anywhere enemySize = 160; } else { // 15%: 320x320 anywhere enemySize = 320; } // Create enemy var enemy = new Enemy(); // Set scale so the base asset (320x320) matches the chosen size var scale = enemySize / 320; enemy.scale.set(scale * (1.1 + Math.random() * 0.2)); // Get random position, passing the actual size var pos; if (spawnInCenter) { // Center region: 2048x2732, center zone is 768px wide x 1024px high, centered // Center zone: x in [640, 1408], y in [854, 1878] var centerZoneW = 768; var centerZoneH = 1024; var minX = 1024 - centerZoneW / 2; var minY = 1366 - centerZoneH / 2; var x = minX + Math.random() * (centerZoneW - enemySize); var y = minY + Math.random() * (centerZoneH - enemySize); pos = { x: x + enemySize / 2, y: y + enemySize / 2 }; } else { pos = getRandomEnemyPos(enemySize, enemySize); } enemy.x = pos.x; enemy.y = pos.y; enemy.rotation = (Math.random() - 0.5) * 0.2; enemy.alpha = 0.0; game.addChild(enemy); // Animate in tween(enemy, { alpha: 1 }, { duration: 120, easing: tween.cubicOut }); // Set up hit callback onEnemyHit = function onEnemyHit(e) { if (!isGameActive) return; // Calculate reaction time var now = Date.now(); var react = now - targetAppearTime; reactionTimes.push(react); targetsHit++; scoreTxt.setText(targetsHit + ' / ' + totalTargets); reactionTxt.setText('Reaction: ' + react + ' ms'); // If hit is accurate (under 1000ms), increment consecutiveAccurateHits, else reset if (react < 1000) { consecutiveAccurateHits++; // Show encouragement every 10 consecutive accurate hits if (consecutiveAccurateHits > 0 && consecutiveAccurateHits % 10 === 0) { encouragementTxt.visible = true; // Hide after 1.2s LK.setTimeout(function () { encouragementTxt.visible = false; }, 1200); } } else { consecutiveAccurateHits = 0; } // Update fastest reaction time if this is the best so far if (fastestReaction === null || react < fastestReaction) { fastestReaction = react; fastestTxt.setText('Fastest: ' + fastestReaction + ' ms\nNew Record!'); // Remove 'New Record!' after 1.2s LK.setTimeout(function () { if (typeof fastestTxt.text === "string" && fastestTxt.text.indexOf('New Record!') !== -1) { fastestTxt.setText('Fastest: ' + fastestReaction + ' ms'); } }, 1200); } // Remove enemy after short delay LK.setTimeout(function () { if (currentTarget) { currentTarget.destroy(); currentTarget = null; } targetIndex++; showNextTarget(); }, 220); }; // Set up auto-miss (if not hit in 1.2s) LK.setTimeout(function () { if (enemy.isHit || !isGameActive) return; // Missed: just remove and go to next reactionTimes.push(1200); reactionTxt.setText('Reaction: MISS'); // Reset consecutive accurate hits on miss consecutiveAccurateHits = 0; tween(enemy, { alpha: 0 }, { duration: 120, onFinish: function onFinish() { if (currentTarget) { currentTarget.destroy(); currentTarget = null; } targetIndex++; showNextTarget(); } }); }, 1200); // Track for removal currentTarget = enemy; targetAppearTime = Date.now(); } // End game function endGame() { isGameActive = false; if (currentTarget) { currentTarget.destroy(); currentTarget = null; } // Calculate stats var hits = targetsHit; var avgReact = 0; var hitCount = 0; for (var i = 0; i < reactionTimes.length; i++) { if (reactionTimes[i] < 1000) { avgReact += reactionTimes[i]; hitCount++; } } avgReact = hitCount ? Math.round(avgReact / hitCount) : 0; var msg = ''; if (hits === totalTargets) { msg = 'PERFECT!\n'; } else if (hits >= totalTargets - 2) { msg = 'Great job!\n'; } else { msg = 'Keep practicing!\n'; } msg += 'You hit ' + hits + ' / ' + totalTargets + '\n'; // Show only the fastest hit time msg += 'Fastest Hit: ' + (fastestReaction !== null ? fastestReaction + ' ms' : '--') + '\n\nTap to play again!'; // Show 'Excellent performance!' at the end msg += '\n\nExcellent performance!'; endTxt.setText(msg); endTxt.visible = true; // Show win/lose popup if (hits === totalTargets) { LK.showYouWin(); } else { LK.showGameOver(); } } // Restart game on tap after end game.down = function (x, y, obj) { if (!isGameActive && endTxt.visible) { // Reset state targetsHit = 0; targetIndex = 0; reactionTimes = []; isGameActive = true; scoreTxt.setText('0 / ' + totalTargets); reactionTxt.setText('Reaction: -- ms'); fastestReaction = null; fastestTxt.setText('Fastest: -- ms'); endTxt.visible = false; showNextTarget(); return; } // --- Weapon fire with laser trail --- // Only fire if game is active if (isGameActive) { // Weapon origin: center bottom of weapon var x0 = weapon.x; var y0 = weapon.y + weaponSize / 2; // Target: where user tapped (x, y) var x1 = x; var y1 = y; // Clamp y1 to not go below the weapon if (y1 > y0) y1 = y0; // Create and show laser trail var laser = new LaserTrail(); laser.init(x0, y0, x1, y1); game.addChild(laser); // Optionally: play shot sound LK.getSound('shotSfx').play(); } }; // Start game showNextTarget(); // Add a bottom neon-glitch HUD bar for future UI elements var hudBar = LK.getAsset('neon_hud_bar', { anchorX: 0.5, anchorY: 1, x: 1024, y: 2732, width: 1800, height: 120, alpha: 0.85 }); LK.gui.bottom.addChild(hudBar); // Show weapon (electric shot) at bottom center, above HUD bar var weaponSize = 900; // Make weapon a much bigger square // Position weapon so its bottom edge touches the bottom line of the game area var weaponY = 2732 - weaponSize / 2; var weapon = LK.getAsset('shot', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: weaponY, width: weaponSize, height: weaponSize, alpha: 0.92, rotation: 0 }); game.addChild(weapon); // No dragging or move needed for this game game.move = function (x, y, obj) {}; game.up = function (x, y, obj) {}; // No per-frame update needed game.update = function () {}; ;
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Enemy target class
var Enemy = Container.expand(function () {
var self = Container.call(this);
// Attach enemy image, anchor center
var enemyImg = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5
});
// For hit animation
self.isHit = false;
// Show hit effect
self.showHit = function () {
if (self.isHit) return;
self.isHit = true;
// Add hit effect
var hitFx = self.attachAsset('hit', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: 0,
alpha: 0.8,
scaleX: 0.7,
scaleY: 0.7
});
tween(hitFx, {
alpha: 0,
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 350,
easing: tween.cubicOut,
onFinish: function onFinish() {
hitFx.destroy();
}
});
};
// Show shot effect
self.showShot = function (localX, localY) {
var shotFx = self.attachAsset('shot', {
anchorX: 0.1,
anchorY: 0.5,
x: localX,
y: localY,
alpha: 0.9,
scaleX: 0.7 + Math.random() * 0.3,
scaleY: 0.7 + Math.random() * 0.3,
rotation: (Math.random() - 0.5) * 0.5
});
tween(shotFx, {
alpha: 0,
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 180,
easing: tween.linear,
onFinish: function onFinish() {
shotFx.destroy();
}
});
};
// Handle tap
self.down = function (x, y, obj) {
if (self.isHit) return;
self.showShot(0, 0);
LK.getSound('shotSfx').play();
// Mark as hit
self.showHit();
LK.getSound('hitSfx').play();
// Notify game
if (typeof onEnemyHit === 'function') {
onEnemyHit(self);
}
};
return self;
});
// LaserTrail class: draws a fading laser from (x0, y0) to (x1, y1)
var LaserTrail = Container.expand(function () {
var self = Container.call(this);
// Draw a rectangle as the laser beam between (x0, y0) and (x1, y1)
// Call: .init(x0, y0, x1, y1)
self.init = function (x0, y0, x1, y1) {
// Calculate distance and angle
var dx = x1 - x0;
var dy = y1 - y0;
var dist = Math.sqrt(dx * dx + dy * dy);
var angle = Math.atan2(dy, dx);
// Use a neon blue color for the laser
var laserWidth = Math.max(24, weaponSize * 0.06); // scale with weapon
var laserAsset = self.attachAsset('neon_sweep', {
anchorX: 0,
anchorY: 0.5,
x: 0,
y: 0,
width: dist,
height: laserWidth,
alpha: 0.85
});
// Position and rotate the container
self.x = x0;
self.y = y0;
self.rotation = angle;
// Animate fade out and destroy
tween(self, {
alpha: 0
}, {
duration: 320,
easing: tween.cubicOut,
onFinish: function onFinish() {
self.destroy();
}
});
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x0a0a1a // Deep blue-black for cyberpunk
});
/****
* Game Code
****/
// Music: synthwave background
// Sound: glitch hit
// Sound: electric shot
// Neon-glitch hit effect
// Electric shot effect (neon bolt)
// Neon-glitch enemy (paper cutout style)
// Add cyberpunk neon city photo-style background
var bg = LK.getAsset('cyberpunk_bg', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0,
width: 2048,
height: 2732
});
game.addChildAt(bg, 0); // Ensure background is at the back
// Add subtle animated neon light sweep overlay for ambiance
var neonSweep = LK.getAsset('neon_sweep', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0,
width: 2048,
height: 2732,
alpha: 0.18
});
game.addChild(neonSweep);
tween(neonSweep, {
x: 2048
}, {
duration: 3200,
repeat: Infinity,
yoyo: true,
easing: tween.sineInOut
});
// Play synthwave music
LK.playMusic('synthwave');
// Game state
var isGameActive = true; // Track if the game is currently active
var totalTargets = 100;
var targetsHit = 0;
var currentTarget = null;
var targetIndex = 0;
var reactionTimes = [];
var targetAppearTime = 0;
// Track consecutive accurate hits for encouragement message
var consecutiveAccurateHits = 0;
// Message text for encouragement, hidden by default
var encouragementTxt = new Text2("You're doing great!", {
size: 100,
fill: 0xFFFC00
});
encouragementTxt.anchor.set(0.5, 0.5);
encouragementTxt.visible = false;
LK.gui.center.addChild(encouragementTxt);
// Add neon-glitch border overlay for cyberpunk effect
var borderOverlay = LK.getAsset('neon_border', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0,
width: 2048,
height: 2732,
alpha: 0.7
});
game.addChild(borderOverlay);
// Score text
var scoreTxt = new Text2('0 / 15', {
size: 120,
fill: 0x00FFF7
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Add a small neon-glitch logo at the top center for game branding
var logo = LK.getAsset('reflexometry_logo', {
anchorX: 0.5,
anchorY: 0,
x: 1024,
y: 10,
width: 400,
height: 100
});
LK.gui.top.addChild(logo);
// Reaction time text
var reactionTxt = new Text2('Reaction: -- ms', {
size: 70,
fill: 0xFF00C8
});
reactionTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(reactionTxt);
reactionTxt.y = 130;
// Fastest reaction time text
var fastestTxt = new Text2('Fastest: -- ms', {
size: 60,
fill: 0x00FFB0
});
fastestTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(fastestTxt);
fastestTxt.y = 210;
// Track fastest reaction time for this session
var fastestReaction = null;
// End screen text (hidden by default)
var endTxt = new Text2('', {
size: 110,
fill: 0xFFF600
});
endTxt.anchor.set(0.5, 0.5);
endTxt.visible = false;
LK.gui.center.addChild(endTxt);
// Used for event callback
var onEnemyHit = null;
// Generate random position for enemy (avoid top 200px and bottom 200px, and left 100px/right 100px)
function getRandomEnemyPos(enemyW, enemyH) {
var marginX = 160;
var marginY = 220;
var x = marginX + Math.random() * (2048 - 2 * marginX);
var y = marginY + Math.random() * (2732 - 2 * marginY);
return {
x: x,
y: y
};
}
// Show next target
function showNextTarget() {
if (currentTarget) {
currentTarget.destroy();
currentTarget = null;
}
if (targetIndex >= totalTargets) {
endGame();
return;
}
// Randomly select enemy size and spawn region
var sizeRand = Math.random();
var enemySize = 80;
var spawnInCenter = false;
if (sizeRand < 0.65) {
// 65%: 80x80 in center zone
enemySize = 80;
spawnInCenter = true;
} else if (sizeRand < 0.90) {
// 25%: 160x160 anywhere
enemySize = 160;
} else {
// 15%: 320x320 anywhere
enemySize = 320;
}
// Create enemy
var enemy = new Enemy();
// Set scale so the base asset (320x320) matches the chosen size
var scale = enemySize / 320;
enemy.scale.set(scale * (1.1 + Math.random() * 0.2));
// Get random position, passing the actual size
var pos;
if (spawnInCenter) {
// Center region: 2048x2732, center zone is 768px wide x 1024px high, centered
// Center zone: x in [640, 1408], y in [854, 1878]
var centerZoneW = 768;
var centerZoneH = 1024;
var minX = 1024 - centerZoneW / 2;
var minY = 1366 - centerZoneH / 2;
var x = minX + Math.random() * (centerZoneW - enemySize);
var y = minY + Math.random() * (centerZoneH - enemySize);
pos = {
x: x + enemySize / 2,
y: y + enemySize / 2
};
} else {
pos = getRandomEnemyPos(enemySize, enemySize);
}
enemy.x = pos.x;
enemy.y = pos.y;
enemy.rotation = (Math.random() - 0.5) * 0.2;
enemy.alpha = 0.0;
game.addChild(enemy);
// Animate in
tween(enemy, {
alpha: 1
}, {
duration: 120,
easing: tween.cubicOut
});
// Set up hit callback
onEnemyHit = function onEnemyHit(e) {
if (!isGameActive) return;
// Calculate reaction time
var now = Date.now();
var react = now - targetAppearTime;
reactionTimes.push(react);
targetsHit++;
scoreTxt.setText(targetsHit + ' / ' + totalTargets);
reactionTxt.setText('Reaction: ' + react + ' ms');
// If hit is accurate (under 1000ms), increment consecutiveAccurateHits, else reset
if (react < 1000) {
consecutiveAccurateHits++;
// Show encouragement every 10 consecutive accurate hits
if (consecutiveAccurateHits > 0 && consecutiveAccurateHits % 10 === 0) {
encouragementTxt.visible = true;
// Hide after 1.2s
LK.setTimeout(function () {
encouragementTxt.visible = false;
}, 1200);
}
} else {
consecutiveAccurateHits = 0;
}
// Update fastest reaction time if this is the best so far
if (fastestReaction === null || react < fastestReaction) {
fastestReaction = react;
fastestTxt.setText('Fastest: ' + fastestReaction + ' ms\nNew Record!');
// Remove 'New Record!' after 1.2s
LK.setTimeout(function () {
if (typeof fastestTxt.text === "string" && fastestTxt.text.indexOf('New Record!') !== -1) {
fastestTxt.setText('Fastest: ' + fastestReaction + ' ms');
}
}, 1200);
}
// Remove enemy after short delay
LK.setTimeout(function () {
if (currentTarget) {
currentTarget.destroy();
currentTarget = null;
}
targetIndex++;
showNextTarget();
}, 220);
};
// Set up auto-miss (if not hit in 1.2s)
LK.setTimeout(function () {
if (enemy.isHit || !isGameActive) return;
// Missed: just remove and go to next
reactionTimes.push(1200);
reactionTxt.setText('Reaction: MISS');
// Reset consecutive accurate hits on miss
consecutiveAccurateHits = 0;
tween(enemy, {
alpha: 0
}, {
duration: 120,
onFinish: function onFinish() {
if (currentTarget) {
currentTarget.destroy();
currentTarget = null;
}
targetIndex++;
showNextTarget();
}
});
}, 1200);
// Track for removal
currentTarget = enemy;
targetAppearTime = Date.now();
}
// End game
function endGame() {
isGameActive = false;
if (currentTarget) {
currentTarget.destroy();
currentTarget = null;
}
// Calculate stats
var hits = targetsHit;
var avgReact = 0;
var hitCount = 0;
for (var i = 0; i < reactionTimes.length; i++) {
if (reactionTimes[i] < 1000) {
avgReact += reactionTimes[i];
hitCount++;
}
}
avgReact = hitCount ? Math.round(avgReact / hitCount) : 0;
var msg = '';
if (hits === totalTargets) {
msg = 'PERFECT!\n';
} else if (hits >= totalTargets - 2) {
msg = 'Great job!\n';
} else {
msg = 'Keep practicing!\n';
}
msg += 'You hit ' + hits + ' / ' + totalTargets + '\n';
// Show only the fastest hit time
msg += 'Fastest Hit: ' + (fastestReaction !== null ? fastestReaction + ' ms' : '--') + '\n\nTap to play again!';
// Show 'Excellent performance!' at the end
msg += '\n\nExcellent performance!';
endTxt.setText(msg);
endTxt.visible = true;
// Show win/lose popup
if (hits === totalTargets) {
LK.showYouWin();
} else {
LK.showGameOver();
}
}
// Restart game on tap after end
game.down = function (x, y, obj) {
if (!isGameActive && endTxt.visible) {
// Reset state
targetsHit = 0;
targetIndex = 0;
reactionTimes = [];
isGameActive = true;
scoreTxt.setText('0 / ' + totalTargets);
reactionTxt.setText('Reaction: -- ms');
fastestReaction = null;
fastestTxt.setText('Fastest: -- ms');
endTxt.visible = false;
showNextTarget();
return;
}
// --- Weapon fire with laser trail ---
// Only fire if game is active
if (isGameActive) {
// Weapon origin: center bottom of weapon
var x0 = weapon.x;
var y0 = weapon.y + weaponSize / 2;
// Target: where user tapped (x, y)
var x1 = x;
var y1 = y;
// Clamp y1 to not go below the weapon
if (y1 > y0) y1 = y0;
// Create and show laser trail
var laser = new LaserTrail();
laser.init(x0, y0, x1, y1);
game.addChild(laser);
// Optionally: play shot sound
LK.getSound('shotSfx').play();
}
};
// Start game
showNextTarget();
// Add a bottom neon-glitch HUD bar for future UI elements
var hudBar = LK.getAsset('neon_hud_bar', {
anchorX: 0.5,
anchorY: 1,
x: 1024,
y: 2732,
width: 1800,
height: 120,
alpha: 0.85
});
LK.gui.bottom.addChild(hudBar);
// Show weapon (electric shot) at bottom center, above HUD bar
var weaponSize = 900; // Make weapon a much bigger square
// Position weapon so its bottom edge touches the bottom line of the game area
var weaponY = 2732 - weaponSize / 2;
var weapon = LK.getAsset('shot', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: weaponY,
width: weaponSize,
height: weaponSize,
alpha: 0.92,
rotation: 0
});
game.addChild(weapon);
// No dragging or move needed for this game
game.move = function (x, y, obj) {};
game.up = function (x, y, obj) {};
// No per-frame update needed
game.update = function () {};
;
A dark, cyberpunk-style photo background (city, alley, rooftop, etc.) Neon lighting, ambient flickers, and animated details for atmosphere.Creat more building and more windows could I see.. 2d
remove back ground
Looking like rough and dark with electrical weapon targets you a man.. make a yellow neon line to image 2d