/**** * 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