/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
bestScore: 0,
bestReactionTime: 0
});
/****
* Classes
****/
var Target = Container.expand(function () {
var self = Container.call(this);
self.type = 'normal';
self.points = 10;
self.isActive = true;
self.creationTime = Date.now();
var targetGraphic = self.attachAsset('target', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0
});
self.resize = function (size) {
targetGraphic.width = size;
targetGraphic.height = size;
};
self.changeColor = function (color) {
targetGraphic.tint = color;
};
self.appear = function (duration) {
tween(targetGraphic, {
alpha: 1,
width: targetGraphic.width * 1.1,
height: targetGraphic.height * 1.1
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(targetGraphic, {
width: targetGraphic.width / 1.1,
height: targetGraphic.height / 1.1
}, {
duration: 200,
easing: tween.easeIn
});
}
});
};
self.disappear = function (callback) {
tween(targetGraphic, {
alpha: 0,
width: targetGraphic.width * 1.5,
height: targetGraphic.height * 1.5
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
if (callback) {
callback();
}
}
});
};
self.hit = function () {
if (!self.isActive) {
return false;
}
self.isActive = false;
var reactionTime = Date.now() - self.creationTime;
tween(targetGraphic, {
alpha: 0,
width: 0,
height: 0
}, {
duration: 200,
easing: tween.easeOut
});
return {
points: self.points,
reactionTime: reactionTime
};
};
self.down = function (x, y, obj) {
if (!self.isActive) {
return;
}
var hitResult = self.hit();
if (hitResult) {
LK.getSound('tap').play();
handleTargetHit(self, hitResult);
}
};
return self;
});
var SpecialTarget = Target.expand(function () {
var self = Target.call(this);
self.type = 'special';
self.points = 25;
var targetGraphic = self.getChildAt(0);
targetGraphic.texture = LK.getAsset('targetSpecial', {
anchorX: 0.5,
anchorY: 0.5
}).texture;
return self;
});
var BombTarget = Target.expand(function () {
var self = Target.call(this);
self.type = 'bomb';
self.points = -30;
// Override the down method to play fail sound when clicked
self.down = function (x, y, obj) {
if (!self.isActive) {
return;
}
// Play fail sound when bomb is tapped
LK.getSound('fail').play();
var hitResult = self.hit();
if (hitResult) {
handleTargetHit(self, hitResult);
}
};
// Override hit method to make bombs grow as they fade out when clicked
self.hit = function () {
if (!self.isActive) {
return false;
}
self.isActive = false;
var reactionTime = Date.now() - self.creationTime;
// Make bomb grow as it fades out - explosion effect
tween(targetGraphic, {
alpha: 0,
width: targetGraphic.width * 3,
height: targetGraphic.height * 3
}, {
duration: 500,
easing: tween.easeOut
});
return {
points: self.points,
reactionTime: reactionTime
};
};
self.removeChildAt(0);
// Add bomb asset
var targetGraphic = self.attachAsset('bomb', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0
});
// Override appear method to create fade in effect for bombs
self.appear = function (duration) {
tween(targetGraphic, {
alpha: 1,
width: targetGraphic.width * 1.1,
height: targetGraphic.height * 1.1
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(targetGraphic, {
width: targetGraphic.width / 1.1,
height: targetGraphic.height / 1.1
}, {
duration: 200,
easing: tween.easeIn
});
}
});
};
// Override disappear method to create fade out effect for bombs
self.disappear = function (callback) {
tween(targetGraphic, {
alpha: 0,
width: targetGraphic.width * 1.5,
height: targetGraphic.height * 1.5
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
if (callback) {
callback();
}
}
});
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x212121
});
/****
* Game Code
****/
// Function to handle target hits
function handleTargetHit(target, hitResult) {
totalTaps++;
if (target.type === 'bomb') {
LK.getSound('fail').play();
LK.effects.flashScreen(0xFF5722, 300);
hitBombs++;
updateBombDisplay();
// Check if player has hit too many bombs
if (hitBombs >= maxBombs) {
endGame();
}
} else {
successfulTaps++;
LK.getSound('success').play();
}
score += hitResult.points;
score = Math.max(0, score); // Ensure score doesn't go below 0
totalReactionTime += hitResult.reactionTime;
avgReactionTime = Math.round(totalReactionTime / successfulTaps);
if (successfulTaps > 0 && (bestReactionTime === 0 || avgReactionTime < bestReactionTime)) {
bestReactionTime = avgReactionTime;
storage.bestReactionTime = bestReactionTime;
}
if (score > storage.bestScore) {
storage.bestScore = score;
bestText.setText('Best Score: ' + storage.bestScore);
}
updateScoreText();
updateReactionTimeText();
removeTarget(target);
// Level progression
checkLevelProgression();
}
// Game state
var targets = [];
var score = 0;
var level = 1;
var totalTaps = 0;
var successfulTaps = 0;
var totalReactionTime = 0;
var avgReactionTime = 0;
var bestReactionTime = storage.bestReactionTime || 0;
var targetCount = 0;
var isGameActive = false;
var missedTargets = 0;
var hitBombs = 0;
var maxMisses = 3;
var maxBombs = 3;
var difficulty = {
minTimeout: 1200,
maxTimeout: 2500,
targetDuration: 1500,
targetChance: 0.7,
specialChance: 0.2,
bombChance: 0.1,
maxTargets: 2
};
// UI elements
var instructionText = new Text2('Tap the circles as fast as you can!\nTap anywhere to start', {
size: 60,
fill: 0xFFFFFF,
align: 'center'
});
instructionText.anchor.set(0.5, 0.5);
LK.gui.center.addChild(instructionText);
// Health icons
var healthContainer = new Container();
healthContainer.x = 120; // Position away from top-left corner menu icon
healthContainer.y = 20;
LK.gui.topLeft.addChild(healthContainer);
var healthIcons = [];
function updateHealthDisplay() {
// Clear existing icons
while (healthContainer.children.length > 0) {
healthContainer.removeChildAt(0);
}
// Create new health icons based on remaining lives
var remainingLives = maxMisses - missedTargets;
for (var i = 0; i < remainingLives; i++) {
var healthIcon = LK.getAsset('target', {
anchorX: 0.5,
anchorY: 0.5,
x: i * 100,
y: 0,
width: 70,
height: 70,
tint: 0x4CAF50 // Green color for health
});
healthContainer.addChild(healthIcon);
healthIcons.push(healthIcon);
}
// Show empty health slots for lost lives
for (var j = remainingLives; j < maxMisses; j++) {
var emptyIcon = LK.getAsset('target', {
anchorX: 0.5,
anchorY: 0.5,
x: j * 100,
y: 0,
width: 70,
height: 70,
tint: 0x757575,
// Gray color for lost health
alpha: 0.5
});
healthContainer.addChild(emptyIcon);
}
}
var scoreText = new Text2('Score: 0', {
size: 50,
fill: 0xFFFFFF
});
scoreText.anchor.set(1, 0);
scoreText.x = -20;
scoreText.y = 20;
LK.gui.topRight.addChild(scoreText);
var levelText = new Text2('Level: 1', {
size: 50,
fill: 0xFFFFFF
});
levelText.anchor.set(0, 0);
levelText.x = 120;
levelText.y = 20;
LK.gui.topRight.addChild(levelText);
var reactionTimeText = new Text2('Avg. Reaction: 0ms', {
size: 40,
fill: 0xFFFFFF
});
reactionTimeText.anchor.set(0.5, 0);
reactionTimeText.y = 20;
LK.gui.top.addChild(reactionTimeText);
var bestText = new Text2('Best Score: ' + storage.bestScore, {
size: 30,
fill: 0xFFD700
});
bestText.anchor.set(0, 1);
bestText.x = 20;
bestText.y = -20;
LK.gui.bottomLeft.addChild(bestText);
// Bomb health icons
var bombContainer = new Container();
bombContainer.x = 120;
bombContainer.y = 120; // Position below health icons with more spacing
LK.gui.topLeft.addChild(bombContainer);
var bombIcons = [];
function updateBombDisplay() {
// Clear existing icons
while (bombContainer.children.length > 0) {
bombContainer.removeChildAt(0);
}
// Create new bomb icons based on remaining bomb slots
var remainingBombSlots = maxBombs - hitBombs;
for (var i = 0; i < remainingBombSlots; i++) {
var bombIcon = LK.getAsset('bomb', {
anchorX: 0.5,
anchorY: 0.5,
x: i * 100,
y: 0,
width: 70,
height: 70
});
bombContainer.addChild(bombIcon);
bombIcons.push(bombIcon);
}
// Show empty bomb slots for used bombs
for (var j = remainingBombSlots; j < maxBombs; j++) {
var emptyIcon = LK.getAsset('bomb', {
anchorX: 0.5,
anchorY: 0.5,
x: j * 100,
y: 0,
width: 70,
height: 70,
tint: 0x757575,
// Gray color for used bomb slots
alpha: 0.5
});
bombContainer.addChild(emptyIcon);
}
}
// Game functions
function startGame() {
score = 0;
level = 1;
totalTaps = 0;
successfulTaps = 0;
totalReactionTime = 0;
avgReactionTime = 0;
targetCount = 0;
missedTargets = 0;
hitBombs = 0;
isGameActive = true;
// Reset difficulty for level 1
difficulty = {
minTimeout: 1200,
maxTimeout: 2500,
targetDuration: 1500,
targetChance: 0.7,
specialChance: 0.2,
bombChance: 0.1,
maxTargets: 2
};
updateScoreText();
updateLevelText();
updateReactionTimeText();
updateHealthDisplay();
updateBombDisplay();
instructionText.visible = false;
// Schedule first target
scheduleNextTarget();
// Play background music
LK.playMusic('bgMusic');
}
function scheduleNextTarget() {
if (!isGameActive) {
return;
}
var delay = Math.random() * (difficulty.maxTimeout - difficulty.minTimeout) + difficulty.minTimeout;
LK.setTimeout(function () {
if (!isGameActive) {
return;
}
// Always try to keep the maximum number of targets on screen
var targetsToSpawn = difficulty.maxTargets - targets.length;
for (var i = 0; i < targetsToSpawn; i++) {
// Add a slight delay between spawns
LK.setTimeout(function () {
if (isGameActive && targets.length < difficulty.maxTargets) {
spawnRandomTarget();
}
}, i * 150);
}
scheduleNextTarget();
}, delay);
}
function spawnRandomTarget() {
if (!isGameActive) {
return;
}
var rand = Math.random();
var newTarget;
if (rand < difficulty.targetChance) {
newTarget = new Target();
} else if (rand < difficulty.targetChance + difficulty.specialChance) {
newTarget = new SpecialTarget();
} else {
newTarget = new BombTarget();
}
// Position randomly on screen with margins to ensure visibility
newTarget.x = Math.random() * (2048 - 300) + 150;
newTarget.y = Math.random() * (2732 - 400) + 200;
// Ensure not too close to screen edges or other targets
// Top-left corner reserved for menu
if (newTarget.x < 200 && newTarget.y < 200) {
newTarget.x += 200;
newTarget.y += 200;
}
// Check for overlap with existing targets and reposition if needed
var attempts = 0;
var overlap = true;
var targetRadius = 125; // Half of target size
var minDistance = targetRadius * 2.5; // Minimum distance between targets
while (overlap && attempts < 10) {
overlap = false;
for (var i = 0; i < targets.length; i++) {
var existingTarget = targets[i];
var dx = newTarget.x - existingTarget.x;
var dy = newTarget.y - existingTarget.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < minDistance) {
overlap = true;
// Reposition and try again
newTarget.x = Math.random() * (2048 - 300) + 150;
newTarget.y = Math.random() * (2732 - 400) + 200;
// Still ensure not too close to screen edges
if (newTarget.x < 200 && newTarget.y < 200) {
newTarget.x += 200;
newTarget.y += 200;
}
break;
}
}
attempts++;
}
game.addChild(newTarget);
targets.push(newTarget);
targetCount++;
newTarget.appear();
// Schedule target disappearance
LK.setTimeout(function () {
if (newTarget.isActive) {
newTarget.isActive = false;
newTarget.disappear(function () {
// Miss penalty - only for normal and special targets
if (newTarget.type !== 'bomb') {
missedTargets++;
// Play gameover sound when a life is lost
LK.getSound('Gameover').play();
score = Math.max(0, score - 5); // Penalty for missing
updateScoreText();
updateHealthDisplay();
// Check if player has missed too many targets
if (missedTargets >= maxMisses) {
endGame();
}
}
removeTarget(newTarget);
});
}
}, difficulty.targetDuration);
}
function removeTarget(target) {
var index = targets.indexOf(target);
if (index !== -1) {
targets.splice(index, 1);
}
LK.setTimeout(function () {
target.destroy();
}, 300);
}
function checkLevelProgression() {
var nextLevel = Math.floor(score / 100) + 1;
if (nextLevel > level) {
level = nextLevel;
increaseDifficulty();
updateLevelText();
LK.getSound('levelUp').play();
LK.effects.flashScreen(0x4CAF50, 500);
var levelUpText = new Text2('LEVEL UP!', {
size: 80,
fill: 0x4CAF50
});
levelUpText.anchor.set(0.5, 0.5);
LK.gui.center.addChild(levelUpText);
// Animate and remove level up text
tween(levelUpText, {
alpha: 0,
y: levelUpText.y - 100
}, {
duration: 1000,
easing: tween.easeOut,
onFinish: function onFinish() {
levelUpText.destroy();
}
});
}
}
function increaseDifficulty() {
// Make game progressively harder with each level
difficulty.minTimeout = Math.max(400, difficulty.minTimeout - 100);
difficulty.maxTimeout = Math.max(1000, difficulty.maxTimeout - 150);
difficulty.targetDuration = Math.max(700, difficulty.targetDuration - 100);
// Adjust spawn chances
if (level % 2 === 0) {
difficulty.maxTargets = Math.min(4, difficulty.maxTargets + 1);
}
if (level > 5) {
difficulty.bombChance = Math.min(0.25, difficulty.bombChance + 0.03);
difficulty.specialChance = Math.min(0.35, difficulty.specialChance + 0.02);
difficulty.targetChance = 1 - difficulty.bombChance - difficulty.specialChance;
}
}
function updateScoreText() {
scoreText.setText('Score: ' + score);
}
function updateLevelText() {
levelText.setText('Level: ' + level);
}
function updateReactionTimeText() {
if (successfulTaps > 0) {
reactionTimeText.setText('Avg. Reaction: ' + avgReactionTime + 'ms');
} else {
reactionTimeText.setText('Avg. Reaction: -');
}
}
function endGame() {
isGameActive = false;
// Clean up any remaining targets
for (var i = targets.length - 1; i >= 0; i--) {
var target = targets[i];
target.isActive = false;
target.disappear(function () {
target.destroy();
});
}
targets = [];
// Show game over
LK.showGameOver();
}
// Global event handlers
game.down = function (x, y, obj) {
if (!isGameActive) {
startGame();
return;
}
// If we clicked on empty space, it counts as a miss
// We need to check if the click was on a target or empty space
var clickedOnTarget = false;
for (var i = 0; i < targets.length; i++) {
if (targets[i].getBounds().contains(x, y)) {
clickedOnTarget = true;
break;
}
}
if (!clickedOnTarget) {
totalTaps++;
score = Math.max(0, score - 2); // Small penalty for tapping blank space
updateScoreText();
}
};
// Game update function
game.update = function () {
// Check for game over conditions
if (isGameActive && (score < 0 || missedTargets >= maxMisses || hitBombs >= maxBombs)) {
endGame();
}
}; /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
bestScore: 0,
bestReactionTime: 0
});
/****
* Classes
****/
var Target = Container.expand(function () {
var self = Container.call(this);
self.type = 'normal';
self.points = 10;
self.isActive = true;
self.creationTime = Date.now();
var targetGraphic = self.attachAsset('target', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0
});
self.resize = function (size) {
targetGraphic.width = size;
targetGraphic.height = size;
};
self.changeColor = function (color) {
targetGraphic.tint = color;
};
self.appear = function (duration) {
tween(targetGraphic, {
alpha: 1,
width: targetGraphic.width * 1.1,
height: targetGraphic.height * 1.1
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(targetGraphic, {
width: targetGraphic.width / 1.1,
height: targetGraphic.height / 1.1
}, {
duration: 200,
easing: tween.easeIn
});
}
});
};
self.disappear = function (callback) {
tween(targetGraphic, {
alpha: 0,
width: targetGraphic.width * 1.5,
height: targetGraphic.height * 1.5
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
if (callback) {
callback();
}
}
});
};
self.hit = function () {
if (!self.isActive) {
return false;
}
self.isActive = false;
var reactionTime = Date.now() - self.creationTime;
tween(targetGraphic, {
alpha: 0,
width: 0,
height: 0
}, {
duration: 200,
easing: tween.easeOut
});
return {
points: self.points,
reactionTime: reactionTime
};
};
self.down = function (x, y, obj) {
if (!self.isActive) {
return;
}
var hitResult = self.hit();
if (hitResult) {
LK.getSound('tap').play();
handleTargetHit(self, hitResult);
}
};
return self;
});
var SpecialTarget = Target.expand(function () {
var self = Target.call(this);
self.type = 'special';
self.points = 25;
var targetGraphic = self.getChildAt(0);
targetGraphic.texture = LK.getAsset('targetSpecial', {
anchorX: 0.5,
anchorY: 0.5
}).texture;
return self;
});
var BombTarget = Target.expand(function () {
var self = Target.call(this);
self.type = 'bomb';
self.points = -30;
// Override the down method to play fail sound when clicked
self.down = function (x, y, obj) {
if (!self.isActive) {
return;
}
// Play fail sound when bomb is tapped
LK.getSound('fail').play();
var hitResult = self.hit();
if (hitResult) {
handleTargetHit(self, hitResult);
}
};
// Override hit method to make bombs grow as they fade out when clicked
self.hit = function () {
if (!self.isActive) {
return false;
}
self.isActive = false;
var reactionTime = Date.now() - self.creationTime;
// Make bomb grow as it fades out - explosion effect
tween(targetGraphic, {
alpha: 0,
width: targetGraphic.width * 3,
height: targetGraphic.height * 3
}, {
duration: 500,
easing: tween.easeOut
});
return {
points: self.points,
reactionTime: reactionTime
};
};
self.removeChildAt(0);
// Add bomb asset
var targetGraphic = self.attachAsset('bomb', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0
});
// Override appear method to create fade in effect for bombs
self.appear = function (duration) {
tween(targetGraphic, {
alpha: 1,
width: targetGraphic.width * 1.1,
height: targetGraphic.height * 1.1
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(targetGraphic, {
width: targetGraphic.width / 1.1,
height: targetGraphic.height / 1.1
}, {
duration: 200,
easing: tween.easeIn
});
}
});
};
// Override disappear method to create fade out effect for bombs
self.disappear = function (callback) {
tween(targetGraphic, {
alpha: 0,
width: targetGraphic.width * 1.5,
height: targetGraphic.height * 1.5
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
if (callback) {
callback();
}
}
});
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x212121
});
/****
* Game Code
****/
// Function to handle target hits
function handleTargetHit(target, hitResult) {
totalTaps++;
if (target.type === 'bomb') {
LK.getSound('fail').play();
LK.effects.flashScreen(0xFF5722, 300);
hitBombs++;
updateBombDisplay();
// Check if player has hit too many bombs
if (hitBombs >= maxBombs) {
endGame();
}
} else {
successfulTaps++;
LK.getSound('success').play();
}
score += hitResult.points;
score = Math.max(0, score); // Ensure score doesn't go below 0
totalReactionTime += hitResult.reactionTime;
avgReactionTime = Math.round(totalReactionTime / successfulTaps);
if (successfulTaps > 0 && (bestReactionTime === 0 || avgReactionTime < bestReactionTime)) {
bestReactionTime = avgReactionTime;
storage.bestReactionTime = bestReactionTime;
}
if (score > storage.bestScore) {
storage.bestScore = score;
bestText.setText('Best Score: ' + storage.bestScore);
}
updateScoreText();
updateReactionTimeText();
removeTarget(target);
// Level progression
checkLevelProgression();
}
// Game state
var targets = [];
var score = 0;
var level = 1;
var totalTaps = 0;
var successfulTaps = 0;
var totalReactionTime = 0;
var avgReactionTime = 0;
var bestReactionTime = storage.bestReactionTime || 0;
var targetCount = 0;
var isGameActive = false;
var missedTargets = 0;
var hitBombs = 0;
var maxMisses = 3;
var maxBombs = 3;
var difficulty = {
minTimeout: 1200,
maxTimeout: 2500,
targetDuration: 1500,
targetChance: 0.7,
specialChance: 0.2,
bombChance: 0.1,
maxTargets: 2
};
// UI elements
var instructionText = new Text2('Tap the circles as fast as you can!\nTap anywhere to start', {
size: 60,
fill: 0xFFFFFF,
align: 'center'
});
instructionText.anchor.set(0.5, 0.5);
LK.gui.center.addChild(instructionText);
// Health icons
var healthContainer = new Container();
healthContainer.x = 120; // Position away from top-left corner menu icon
healthContainer.y = 20;
LK.gui.topLeft.addChild(healthContainer);
var healthIcons = [];
function updateHealthDisplay() {
// Clear existing icons
while (healthContainer.children.length > 0) {
healthContainer.removeChildAt(0);
}
// Create new health icons based on remaining lives
var remainingLives = maxMisses - missedTargets;
for (var i = 0; i < remainingLives; i++) {
var healthIcon = LK.getAsset('target', {
anchorX: 0.5,
anchorY: 0.5,
x: i * 100,
y: 0,
width: 70,
height: 70,
tint: 0x4CAF50 // Green color for health
});
healthContainer.addChild(healthIcon);
healthIcons.push(healthIcon);
}
// Show empty health slots for lost lives
for (var j = remainingLives; j < maxMisses; j++) {
var emptyIcon = LK.getAsset('target', {
anchorX: 0.5,
anchorY: 0.5,
x: j * 100,
y: 0,
width: 70,
height: 70,
tint: 0x757575,
// Gray color for lost health
alpha: 0.5
});
healthContainer.addChild(emptyIcon);
}
}
var scoreText = new Text2('Score: 0', {
size: 50,
fill: 0xFFFFFF
});
scoreText.anchor.set(1, 0);
scoreText.x = -20;
scoreText.y = 20;
LK.gui.topRight.addChild(scoreText);
var levelText = new Text2('Level: 1', {
size: 50,
fill: 0xFFFFFF
});
levelText.anchor.set(0, 0);
levelText.x = 120;
levelText.y = 20;
LK.gui.topRight.addChild(levelText);
var reactionTimeText = new Text2('Avg. Reaction: 0ms', {
size: 40,
fill: 0xFFFFFF
});
reactionTimeText.anchor.set(0.5, 0);
reactionTimeText.y = 20;
LK.gui.top.addChild(reactionTimeText);
var bestText = new Text2('Best Score: ' + storage.bestScore, {
size: 30,
fill: 0xFFD700
});
bestText.anchor.set(0, 1);
bestText.x = 20;
bestText.y = -20;
LK.gui.bottomLeft.addChild(bestText);
// Bomb health icons
var bombContainer = new Container();
bombContainer.x = 120;
bombContainer.y = 120; // Position below health icons with more spacing
LK.gui.topLeft.addChild(bombContainer);
var bombIcons = [];
function updateBombDisplay() {
// Clear existing icons
while (bombContainer.children.length > 0) {
bombContainer.removeChildAt(0);
}
// Create new bomb icons based on remaining bomb slots
var remainingBombSlots = maxBombs - hitBombs;
for (var i = 0; i < remainingBombSlots; i++) {
var bombIcon = LK.getAsset('bomb', {
anchorX: 0.5,
anchorY: 0.5,
x: i * 100,
y: 0,
width: 70,
height: 70
});
bombContainer.addChild(bombIcon);
bombIcons.push(bombIcon);
}
// Show empty bomb slots for used bombs
for (var j = remainingBombSlots; j < maxBombs; j++) {
var emptyIcon = LK.getAsset('bomb', {
anchorX: 0.5,
anchorY: 0.5,
x: j * 100,
y: 0,
width: 70,
height: 70,
tint: 0x757575,
// Gray color for used bomb slots
alpha: 0.5
});
bombContainer.addChild(emptyIcon);
}
}
// Game functions
function startGame() {
score = 0;
level = 1;
totalTaps = 0;
successfulTaps = 0;
totalReactionTime = 0;
avgReactionTime = 0;
targetCount = 0;
missedTargets = 0;
hitBombs = 0;
isGameActive = true;
// Reset difficulty for level 1
difficulty = {
minTimeout: 1200,
maxTimeout: 2500,
targetDuration: 1500,
targetChance: 0.7,
specialChance: 0.2,
bombChance: 0.1,
maxTargets: 2
};
updateScoreText();
updateLevelText();
updateReactionTimeText();
updateHealthDisplay();
updateBombDisplay();
instructionText.visible = false;
// Schedule first target
scheduleNextTarget();
// Play background music
LK.playMusic('bgMusic');
}
function scheduleNextTarget() {
if (!isGameActive) {
return;
}
var delay = Math.random() * (difficulty.maxTimeout - difficulty.minTimeout) + difficulty.minTimeout;
LK.setTimeout(function () {
if (!isGameActive) {
return;
}
// Always try to keep the maximum number of targets on screen
var targetsToSpawn = difficulty.maxTargets - targets.length;
for (var i = 0; i < targetsToSpawn; i++) {
// Add a slight delay between spawns
LK.setTimeout(function () {
if (isGameActive && targets.length < difficulty.maxTargets) {
spawnRandomTarget();
}
}, i * 150);
}
scheduleNextTarget();
}, delay);
}
function spawnRandomTarget() {
if (!isGameActive) {
return;
}
var rand = Math.random();
var newTarget;
if (rand < difficulty.targetChance) {
newTarget = new Target();
} else if (rand < difficulty.targetChance + difficulty.specialChance) {
newTarget = new SpecialTarget();
} else {
newTarget = new BombTarget();
}
// Position randomly on screen with margins to ensure visibility
newTarget.x = Math.random() * (2048 - 300) + 150;
newTarget.y = Math.random() * (2732 - 400) + 200;
// Ensure not too close to screen edges or other targets
// Top-left corner reserved for menu
if (newTarget.x < 200 && newTarget.y < 200) {
newTarget.x += 200;
newTarget.y += 200;
}
// Check for overlap with existing targets and reposition if needed
var attempts = 0;
var overlap = true;
var targetRadius = 125; // Half of target size
var minDistance = targetRadius * 2.5; // Minimum distance between targets
while (overlap && attempts < 10) {
overlap = false;
for (var i = 0; i < targets.length; i++) {
var existingTarget = targets[i];
var dx = newTarget.x - existingTarget.x;
var dy = newTarget.y - existingTarget.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < minDistance) {
overlap = true;
// Reposition and try again
newTarget.x = Math.random() * (2048 - 300) + 150;
newTarget.y = Math.random() * (2732 - 400) + 200;
// Still ensure not too close to screen edges
if (newTarget.x < 200 && newTarget.y < 200) {
newTarget.x += 200;
newTarget.y += 200;
}
break;
}
}
attempts++;
}
game.addChild(newTarget);
targets.push(newTarget);
targetCount++;
newTarget.appear();
// Schedule target disappearance
LK.setTimeout(function () {
if (newTarget.isActive) {
newTarget.isActive = false;
newTarget.disappear(function () {
// Miss penalty - only for normal and special targets
if (newTarget.type !== 'bomb') {
missedTargets++;
// Play gameover sound when a life is lost
LK.getSound('Gameover').play();
score = Math.max(0, score - 5); // Penalty for missing
updateScoreText();
updateHealthDisplay();
// Check if player has missed too many targets
if (missedTargets >= maxMisses) {
endGame();
}
}
removeTarget(newTarget);
});
}
}, difficulty.targetDuration);
}
function removeTarget(target) {
var index = targets.indexOf(target);
if (index !== -1) {
targets.splice(index, 1);
}
LK.setTimeout(function () {
target.destroy();
}, 300);
}
function checkLevelProgression() {
var nextLevel = Math.floor(score / 100) + 1;
if (nextLevel > level) {
level = nextLevel;
increaseDifficulty();
updateLevelText();
LK.getSound('levelUp').play();
LK.effects.flashScreen(0x4CAF50, 500);
var levelUpText = new Text2('LEVEL UP!', {
size: 80,
fill: 0x4CAF50
});
levelUpText.anchor.set(0.5, 0.5);
LK.gui.center.addChild(levelUpText);
// Animate and remove level up text
tween(levelUpText, {
alpha: 0,
y: levelUpText.y - 100
}, {
duration: 1000,
easing: tween.easeOut,
onFinish: function onFinish() {
levelUpText.destroy();
}
});
}
}
function increaseDifficulty() {
// Make game progressively harder with each level
difficulty.minTimeout = Math.max(400, difficulty.minTimeout - 100);
difficulty.maxTimeout = Math.max(1000, difficulty.maxTimeout - 150);
difficulty.targetDuration = Math.max(700, difficulty.targetDuration - 100);
// Adjust spawn chances
if (level % 2 === 0) {
difficulty.maxTargets = Math.min(4, difficulty.maxTargets + 1);
}
if (level > 5) {
difficulty.bombChance = Math.min(0.25, difficulty.bombChance + 0.03);
difficulty.specialChance = Math.min(0.35, difficulty.specialChance + 0.02);
difficulty.targetChance = 1 - difficulty.bombChance - difficulty.specialChance;
}
}
function updateScoreText() {
scoreText.setText('Score: ' + score);
}
function updateLevelText() {
levelText.setText('Level: ' + level);
}
function updateReactionTimeText() {
if (successfulTaps > 0) {
reactionTimeText.setText('Avg. Reaction: ' + avgReactionTime + 'ms');
} else {
reactionTimeText.setText('Avg. Reaction: -');
}
}
function endGame() {
isGameActive = false;
// Clean up any remaining targets
for (var i = targets.length - 1; i >= 0; i--) {
var target = targets[i];
target.isActive = false;
target.disappear(function () {
target.destroy();
});
}
targets = [];
// Show game over
LK.showGameOver();
}
// Global event handlers
game.down = function (x, y, obj) {
if (!isGameActive) {
startGame();
return;
}
// If we clicked on empty space, it counts as a miss
// We need to check if the click was on a target or empty space
var clickedOnTarget = false;
for (var i = 0; i < targets.length; i++) {
if (targets[i].getBounds().contains(x, y)) {
clickedOnTarget = true;
break;
}
}
if (!clickedOnTarget) {
totalTaps++;
score = Math.max(0, score - 2); // Small penalty for tapping blank space
updateScoreText();
}
};
// Game update function
game.update = function () {
// Check for game over conditions
if (isGameActive && (score < 0 || missedTargets >= maxMisses || hitBombs >= maxBombs)) {
endGame();
}
};