/****
* 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;
var targetGraphic = self.getChildAt(0);
targetGraphic.texture = LK.getAsset('targetBomb', {
anchorX: 0.5,
anchorY: 0.5
}).texture;
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x212121
});
/****
* Game Code
****/
// 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 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);
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);
// Game functions
function startGame() {
score = 0;
level = 1;
totalTaps = 0;
successfulTaps = 0;
totalReactionTime = 0;
avgReactionTime = 0;
targetCount = 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();
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 = 75; // 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') {
LK.getSound('fail').play();
score = Math.max(0, score - 5); // Penalty for missing
updateScoreText();
}
removeTarget(newTarget);
});
}
}, difficulty.targetDuration);
}
function handleTargetHit(target, hitResult) {
totalTaps++;
if (target.type === 'bomb') {
LK.getSound('fail').play();
LK.effects.flashScreen(0xFF5722, 300);
} 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();
}
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 condition
if (isGameActive && score < 0) {
endGame();
}
}; ===================================================================
--- original.js
+++ change.js
@@ -143,9 +143,9 @@
targetDuration: 1500,
targetChance: 0.7,
specialChance: 0.2,
bombChance: 0.1,
- maxTargets: 1
+ maxTargets: 2
};
// UI elements
var instructionText = new Text2('Tap the circles as fast as you can!\nTap anywhere to start', {
size: 60,
@@ -202,9 +202,9 @@
targetDuration: 1500,
targetChance: 0.7,
specialChance: 0.2,
bombChance: 0.1,
- maxTargets: 1
+ maxTargets: 2
};
updateScoreText();
updateLevelText();
updateReactionTimeText();
@@ -222,10 +222,17 @@
LK.setTimeout(function () {
if (!isGameActive) {
return;
}
- if (targets.length < difficulty.maxTargets) {
- spawnRandomTarget();
+ // 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);
}
@@ -250,8 +257,35 @@
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 = 75; // 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();
@@ -339,10 +373,10 @@
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 % 3 === 0) {
- difficulty.maxTargets = Math.min(3, difficulty.maxTargets + 1);
+ 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);