/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1", { highScore: 0, level: 1 }); /**** * Classes ****/ var ClockNeedle = Container.expand(function () { var self = Container.call(this); var needleGraphics = self.attachAsset('needle', { anchorX: 0.5, anchorY: 1.0, width: 10, height: 500 }); self.angle = 0; self.speed = 1; self.direction = 1; // 1 clockwise, -1 counterclockwise self.randomizeBehavior = false; self.behaviorChangeTimer = 0; self.update = function () { // Rotate the needle self.angle += self.speed * self.direction; // Keep angle between 0-360 if (self.angle >= 360) { self.angle -= 360; } else if (self.angle < 0) { self.angle += 360; } // Update the needle rotation self.rotation = (self.angle + 90) * (Math.PI / 180); // Random behavior changes if enabled if (self.randomizeBehavior && self.behaviorChangeTimer <= 0) { var randomChange = Math.random(); if (randomChange < 0.3) { // Change direction self.direction *= -1; } else if (randomChange < 0.6) { // Change speed (between 0.5 and 2.5) self.speed = 0.5 + Math.random() * 2; } // Set new timer for next behavior change (2-6 seconds) self.behaviorChangeTimer = 120 + Math.floor(Math.random() * 240); } if (self.behaviorChangeTimer > 0) { self.behaviorChangeTimer--; } }; self.setSpeed = function (newSpeed) { self.speed = newSpeed; }; self.enableRandomBehavior = function () { self.randomizeBehavior = true; }; self.disableRandomBehavior = function () { self.randomizeBehavior = false; self.direction = 1; self.speed = 1; }; return self; }); var Target = Container.expand(function (angle, distance) { var self = Container.call(this); var targetGraphics = self.attachAsset('target', { anchorX: 0.5, anchorY: 0.5, width: 60, height: 60 }); self.angle = angle || 0; self.distance = distance || 400; self.active = true; self.hit = false; // Calculate position based on angle and distance self.updatePosition = function () { var radian = self.angle * (Math.PI / 180); self.x = Math.cos(radian) * self.distance; self.y = Math.sin(radian) * self.distance; }; self.markHit = function () { if (!self.active) { return; } self.hit = true; self.active = false; // Replace target with hit effect targetGraphics.destroy(); var hitEffect = self.attachAsset('targetHit', { anchorX: 0.5, anchorY: 0.5, width: 80, height: 80 }); // Animate hit effect tween(hitEffect, { alpha: 0, scaleX: 1.5, scaleY: 1.5 }, { duration: 500, easing: tween.easeOut, onFinish: function onFinish() { // Target stays visible but faded tween(hitEffect, { alpha: 0.3, scaleX: 1, scaleY: 1 }, { duration: 300 }); } }); }; self.markMiss = function () { if (!self.active) { return; } self.active = false; // Replace target with miss effect targetGraphics.destroy(); var missEffect = self.attachAsset('targetMiss', { anchorX: 0.5, anchorY: 0.5, width: 80, height: 80 }); // Animate miss effect tween(missEffect, { alpha: 0 }, { duration: 800, easing: tween.easeOut }); }; self.reset = function () { self.active = true; self.hit = false; if (targetGraphics.parent) { targetGraphics.destroy(); } targetGraphics = self.attachAsset('target', { anchorX: 0.5, anchorY: 0.5, width: 60, height: 60 }); }; // Initialize position self.updatePosition(); return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x121212 }); /**** * Game Code ****/ // Game state variables var score = 0; var currentStreak = 0; var multiplier = 1; var level = storage.level || 1; var highScore = storage.highScore || 0; var targets = []; var needle = null; var clockFace = null; var clockCenter = null; var isGameStarted = false; var targetCount = 0; // UI elements var scoreTxt = new Text2('0', { size: 120, fill: 0xFFFFFF }); var streakTxt = new Text2('Streak: 0', { size: 60, fill: 0xFFFFFF }); var multiplierTxt = new Text2('x1', { size: 80, fill: 0xFFCC00 }); var levelTxt = new Text2('Level 1', { size: 60, fill: 0xFFFFFF }); var highScoreTxt = new Text2('High Score: 0', { size: 40, fill: 0xAAAAAA }); var instructionTxt = new Text2('Tap when the needle hits a target!', { size: 60, fill: 0xFFFFFF }); // Position UI elements scoreTxt.anchor.set(0.5, 0); scoreTxt.y = 100; LK.gui.top.addChild(scoreTxt); streakTxt.anchor.set(0.5, 0); streakTxt.y = 220; LK.gui.top.addChild(streakTxt); multiplierTxt.anchor.set(0.5, 0); multiplierTxt.y = 280; LK.gui.top.addChild(multiplierTxt); levelTxt.anchor.set(0.5, 1); levelTxt.y = -40; LK.gui.bottom.addChild(levelTxt); highScoreTxt.anchor.set(0.5, 1); highScoreTxt.y = -100; LK.gui.bottom.addChild(highScoreTxt); instructionTxt.anchor.set(0.5, 0.5); LK.gui.center.addChild(instructionTxt); // Function to initialize the game function initGame() { // Create clock face clockFace = LK.getAsset('clockFace', { anchorX: 0.5, anchorY: 0.5, width: 1200, height: 1200 }); game.addChild(clockFace); // Position clock face at center of screen clockFace.x = 2048 / 2; clockFace.y = 2732 / 2; // Create clock needle needle = new ClockNeedle(); needle.x = 2048 / 2; needle.y = 2732 / 2; game.addChild(needle); // Create clock center clockCenter = LK.getAsset('clockCenter', { anchorX: 0.5, anchorY: 0.5, width: 50, height: 50 }); clockCenter.x = 2048 / 2; clockCenter.y = 2732 / 2; game.addChild(clockCenter); // Create initial targets createTargetsForLevel(level); // Reset game variables score = 0; currentStreak = 0; multiplier = 1; // Update UI updateUI(); // Play background music LK.playMusic('bgMusic', { fade: { start: 0, end: 0.3, duration: 1000 } }); // Mark game as started isGameStarted = true; // Hide instruction text after 3 seconds LK.setTimeout(function () { tween(instructionTxt, { alpha: 0 }, { duration: 1000, easing: tween.easeOut }); }, 3000); } // Function to create targets based on current level function createTargetsForLevel(level) { // Clear existing targets for (var i = 0; i < targets.length; i++) { if (targets[i].parent) { targets[i].destroy(); } } targets = []; // Calculate number of targets based on level targetCount = Math.min(3 + Math.floor(level / 2), 12); // Create evenly spaced targets var angleStep = 360 / targetCount; var startAngle = Math.random() * 360; // Random starting angle for (var i = 0; i < targetCount; i++) { var angle = (startAngle + i * angleStep) % 360; var target = new Target(angle, 450); // Position relative to center of game target.x += 2048 / 2; target.y += 2732 / 2; targets.push(target); game.addChild(target); } // Set needle behavior based on level if (level >= 3) { needle.setSpeed(1 + level * 0.2); // Increase speed with level } if (level >= 5) { needle.enableRandomBehavior(); } else { needle.disableRandomBehavior(); } } // Function to update UI elements function updateUI() { scoreTxt.setText(score.toString()); streakTxt.setText("Streak: " + currentStreak); multiplierTxt.setText("x" + multiplier); levelTxt.setText("Level " + level); highScoreTxt.setText("High Score: " + highScore); // Update score in LK system LK.setScore(score); } // Function to check if needle is hitting any target function checkNeedleHit() { var needleAngle = needle.angle; var hitTarget = false; var hitAngleTolerance = 10; // Degrees of tolerance for hitting // Check each target for (var i = 0; i < targets.length; i++) { var target = targets[i]; if (!target.active) { continue; } // Calculate angle difference var angleDiff = Math.abs(needleAngle - target.angle); if (angleDiff > 180) { angleDiff = 360 - angleDiff; } // Check if needle is within tolerance range if (angleDiff <= hitAngleTolerance) { hitTarget = true; handleTargetHit(target); break; } } if (!hitTarget) { handleMiss(); } } // Function to handle successful target hit function handleTargetHit(target) { // Mark target as hit target.markHit(); // Play hit sound LK.getSound('hit').play(); // Update streak and multiplier currentStreak++; multiplier = Math.min(5, 1 + Math.floor(currentStreak / 3)); // Add score var pointsGained = 100 * multiplier; score += pointsGained; // Show points animation var pointsText = new Text2("+" + pointsGained, { size: 80, fill: 0x00FF00 }); pointsText.anchor.set(0.5, 0.5); pointsText.x = target.x; pointsText.y = target.y; game.addChild(pointsText); tween(pointsText, { y: pointsText.y - 100, alpha: 0 }, { duration: 800, easing: tween.easeOut, onFinish: function onFinish() { pointsText.destroy(); } }); // Update UI updateUI(); // Check if all targets are hit var allTargetsHit = true; for (var i = 0; i < targets.length; i++) { if (targets[i].active) { allTargetsHit = false; break; } } // If all targets hit, advance to next level if (allTargetsHit) { advanceLevel(); } } // Function to handle missed tap function handleMiss() { // Play miss sound LK.getSound('miss').play(); // Check if the player is on a streak if (currentStreak > 0) { // Reset streak and multiplier currentStreak = 0; multiplier = 1; // Show miss animation LK.effects.flashScreen(0xff0000, 300); // Maintain remaining targets when streak is lost for (var i = 0; i < targets.length; i++) { if (targets[i].active && !targets[i].hit) { targets[i].reset(); } } // Update UI updateUI(); } else { // Show miss animation LK.effects.flashScreen(0xff0000, 300); // Mark all active targets as missed for (var i = 0; i < targets.length; i++) { if (targets[i].active) { targets[i].markMiss(); } } // Update UI updateUI(); // Check if score is higher than high score if (score > highScore) { highScore = score; storage.highScore = highScore; } // Show game over LK.setTimeout(function () { LK.showGameOver(); }, 1000); } } // Function to advance to next level function advanceLevel() { level++; storage.level = level; // Play level up sound LK.getSound('levelUp').play(); // Show level up animation var levelUpText = new Text2("LEVEL UP!", { size: 120, fill: 0xFFCC00 }); levelUpText.anchor.set(0.5, 0.5); levelUpText.x = 2048 / 2; levelUpText.y = 2732 / 2; game.addChild(levelUpText); tween(levelUpText, { scaleX: 1.5, scaleY: 1.5 }, { duration: 1000, easing: tween.easeOut, onFinish: function onFinish() { tween(levelUpText, { alpha: 0 }, { duration: 500, onFinish: function onFinish() { levelUpText.destroy(); // Create new targets for next level createTargetsForLevel(level); } }); } }); // Update UI updateUI(); } // Game tap/click handler game.down = function (x, y, obj) { if (!isGameStarted) { // Start game on first tap initGame(); return; } checkNeedleHit(); }; // Game update function game.update = function () { if (!isGameStarted) { return; } // Nothing needs updating here as the needle has its own update method }; // Initialize the game elements highScore = storage.highScore || 0; level = 1; // Always start at level 1 for a new game highScoreTxt.setText("High Score: " + highScore); // Show start instructions instructionTxt.setText("Tap to start!");
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
highScore: 0,
level: 1
});
/****
* Classes
****/
var ClockNeedle = Container.expand(function () {
var self = Container.call(this);
var needleGraphics = self.attachAsset('needle', {
anchorX: 0.5,
anchorY: 1.0,
width: 10,
height: 500
});
self.angle = 0;
self.speed = 1;
self.direction = 1; // 1 clockwise, -1 counterclockwise
self.randomizeBehavior = false;
self.behaviorChangeTimer = 0;
self.update = function () {
// Rotate the needle
self.angle += self.speed * self.direction;
// Keep angle between 0-360
if (self.angle >= 360) {
self.angle -= 360;
} else if (self.angle < 0) {
self.angle += 360;
}
// Update the needle rotation
self.rotation = (self.angle + 90) * (Math.PI / 180);
// Random behavior changes if enabled
if (self.randomizeBehavior && self.behaviorChangeTimer <= 0) {
var randomChange = Math.random();
if (randomChange < 0.3) {
// Change direction
self.direction *= -1;
} else if (randomChange < 0.6) {
// Change speed (between 0.5 and 2.5)
self.speed = 0.5 + Math.random() * 2;
}
// Set new timer for next behavior change (2-6 seconds)
self.behaviorChangeTimer = 120 + Math.floor(Math.random() * 240);
}
if (self.behaviorChangeTimer > 0) {
self.behaviorChangeTimer--;
}
};
self.setSpeed = function (newSpeed) {
self.speed = newSpeed;
};
self.enableRandomBehavior = function () {
self.randomizeBehavior = true;
};
self.disableRandomBehavior = function () {
self.randomizeBehavior = false;
self.direction = 1;
self.speed = 1;
};
return self;
});
var Target = Container.expand(function (angle, distance) {
var self = Container.call(this);
var targetGraphics = self.attachAsset('target', {
anchorX: 0.5,
anchorY: 0.5,
width: 60,
height: 60
});
self.angle = angle || 0;
self.distance = distance || 400;
self.active = true;
self.hit = false;
// Calculate position based on angle and distance
self.updatePosition = function () {
var radian = self.angle * (Math.PI / 180);
self.x = Math.cos(radian) * self.distance;
self.y = Math.sin(radian) * self.distance;
};
self.markHit = function () {
if (!self.active) {
return;
}
self.hit = true;
self.active = false;
// Replace target with hit effect
targetGraphics.destroy();
var hitEffect = self.attachAsset('targetHit', {
anchorX: 0.5,
anchorY: 0.5,
width: 80,
height: 80
});
// Animate hit effect
tween(hitEffect, {
alpha: 0,
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 500,
easing: tween.easeOut,
onFinish: function onFinish() {
// Target stays visible but faded
tween(hitEffect, {
alpha: 0.3,
scaleX: 1,
scaleY: 1
}, {
duration: 300
});
}
});
};
self.markMiss = function () {
if (!self.active) {
return;
}
self.active = false;
// Replace target with miss effect
targetGraphics.destroy();
var missEffect = self.attachAsset('targetMiss', {
anchorX: 0.5,
anchorY: 0.5,
width: 80,
height: 80
});
// Animate miss effect
tween(missEffect, {
alpha: 0
}, {
duration: 800,
easing: tween.easeOut
});
};
self.reset = function () {
self.active = true;
self.hit = false;
if (targetGraphics.parent) {
targetGraphics.destroy();
}
targetGraphics = self.attachAsset('target', {
anchorX: 0.5,
anchorY: 0.5,
width: 60,
height: 60
});
};
// Initialize position
self.updatePosition();
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x121212
});
/****
* Game Code
****/
// Game state variables
var score = 0;
var currentStreak = 0;
var multiplier = 1;
var level = storage.level || 1;
var highScore = storage.highScore || 0;
var targets = [];
var needle = null;
var clockFace = null;
var clockCenter = null;
var isGameStarted = false;
var targetCount = 0;
// UI elements
var scoreTxt = new Text2('0', {
size: 120,
fill: 0xFFFFFF
});
var streakTxt = new Text2('Streak: 0', {
size: 60,
fill: 0xFFFFFF
});
var multiplierTxt = new Text2('x1', {
size: 80,
fill: 0xFFCC00
});
var levelTxt = new Text2('Level 1', {
size: 60,
fill: 0xFFFFFF
});
var highScoreTxt = new Text2('High Score: 0', {
size: 40,
fill: 0xAAAAAA
});
var instructionTxt = new Text2('Tap when the needle hits a target!', {
size: 60,
fill: 0xFFFFFF
});
// Position UI elements
scoreTxt.anchor.set(0.5, 0);
scoreTxt.y = 100;
LK.gui.top.addChild(scoreTxt);
streakTxt.anchor.set(0.5, 0);
streakTxt.y = 220;
LK.gui.top.addChild(streakTxt);
multiplierTxt.anchor.set(0.5, 0);
multiplierTxt.y = 280;
LK.gui.top.addChild(multiplierTxt);
levelTxt.anchor.set(0.5, 1);
levelTxt.y = -40;
LK.gui.bottom.addChild(levelTxt);
highScoreTxt.anchor.set(0.5, 1);
highScoreTxt.y = -100;
LK.gui.bottom.addChild(highScoreTxt);
instructionTxt.anchor.set(0.5, 0.5);
LK.gui.center.addChild(instructionTxt);
// Function to initialize the game
function initGame() {
// Create clock face
clockFace = LK.getAsset('clockFace', {
anchorX: 0.5,
anchorY: 0.5,
width: 1200,
height: 1200
});
game.addChild(clockFace);
// Position clock face at center of screen
clockFace.x = 2048 / 2;
clockFace.y = 2732 / 2;
// Create clock needle
needle = new ClockNeedle();
needle.x = 2048 / 2;
needle.y = 2732 / 2;
game.addChild(needle);
// Create clock center
clockCenter = LK.getAsset('clockCenter', {
anchorX: 0.5,
anchorY: 0.5,
width: 50,
height: 50
});
clockCenter.x = 2048 / 2;
clockCenter.y = 2732 / 2;
game.addChild(clockCenter);
// Create initial targets
createTargetsForLevel(level);
// Reset game variables
score = 0;
currentStreak = 0;
multiplier = 1;
// Update UI
updateUI();
// Play background music
LK.playMusic('bgMusic', {
fade: {
start: 0,
end: 0.3,
duration: 1000
}
});
// Mark game as started
isGameStarted = true;
// Hide instruction text after 3 seconds
LK.setTimeout(function () {
tween(instructionTxt, {
alpha: 0
}, {
duration: 1000,
easing: tween.easeOut
});
}, 3000);
}
// Function to create targets based on current level
function createTargetsForLevel(level) {
// Clear existing targets
for (var i = 0; i < targets.length; i++) {
if (targets[i].parent) {
targets[i].destroy();
}
}
targets = [];
// Calculate number of targets based on level
targetCount = Math.min(3 + Math.floor(level / 2), 12);
// Create evenly spaced targets
var angleStep = 360 / targetCount;
var startAngle = Math.random() * 360; // Random starting angle
for (var i = 0; i < targetCount; i++) {
var angle = (startAngle + i * angleStep) % 360;
var target = new Target(angle, 450);
// Position relative to center of game
target.x += 2048 / 2;
target.y += 2732 / 2;
targets.push(target);
game.addChild(target);
}
// Set needle behavior based on level
if (level >= 3) {
needle.setSpeed(1 + level * 0.2); // Increase speed with level
}
if (level >= 5) {
needle.enableRandomBehavior();
} else {
needle.disableRandomBehavior();
}
}
// Function to update UI elements
function updateUI() {
scoreTxt.setText(score.toString());
streakTxt.setText("Streak: " + currentStreak);
multiplierTxt.setText("x" + multiplier);
levelTxt.setText("Level " + level);
highScoreTxt.setText("High Score: " + highScore);
// Update score in LK system
LK.setScore(score);
}
// Function to check if needle is hitting any target
function checkNeedleHit() {
var needleAngle = needle.angle;
var hitTarget = false;
var hitAngleTolerance = 10; // Degrees of tolerance for hitting
// Check each target
for (var i = 0; i < targets.length; i++) {
var target = targets[i];
if (!target.active) {
continue;
}
// Calculate angle difference
var angleDiff = Math.abs(needleAngle - target.angle);
if (angleDiff > 180) {
angleDiff = 360 - angleDiff;
}
// Check if needle is within tolerance range
if (angleDiff <= hitAngleTolerance) {
hitTarget = true;
handleTargetHit(target);
break;
}
}
if (!hitTarget) {
handleMiss();
}
}
// Function to handle successful target hit
function handleTargetHit(target) {
// Mark target as hit
target.markHit();
// Play hit sound
LK.getSound('hit').play();
// Update streak and multiplier
currentStreak++;
multiplier = Math.min(5, 1 + Math.floor(currentStreak / 3));
// Add score
var pointsGained = 100 * multiplier;
score += pointsGained;
// Show points animation
var pointsText = new Text2("+" + pointsGained, {
size: 80,
fill: 0x00FF00
});
pointsText.anchor.set(0.5, 0.5);
pointsText.x = target.x;
pointsText.y = target.y;
game.addChild(pointsText);
tween(pointsText, {
y: pointsText.y - 100,
alpha: 0
}, {
duration: 800,
easing: tween.easeOut,
onFinish: function onFinish() {
pointsText.destroy();
}
});
// Update UI
updateUI();
// Check if all targets are hit
var allTargetsHit = true;
for (var i = 0; i < targets.length; i++) {
if (targets[i].active) {
allTargetsHit = false;
break;
}
}
// If all targets hit, advance to next level
if (allTargetsHit) {
advanceLevel();
}
}
// Function to handle missed tap
function handleMiss() {
// Play miss sound
LK.getSound('miss').play();
// Check if the player is on a streak
if (currentStreak > 0) {
// Reset streak and multiplier
currentStreak = 0;
multiplier = 1;
// Show miss animation
LK.effects.flashScreen(0xff0000, 300);
// Maintain remaining targets when streak is lost
for (var i = 0; i < targets.length; i++) {
if (targets[i].active && !targets[i].hit) {
targets[i].reset();
}
}
// Update UI
updateUI();
} else {
// Show miss animation
LK.effects.flashScreen(0xff0000, 300);
// Mark all active targets as missed
for (var i = 0; i < targets.length; i++) {
if (targets[i].active) {
targets[i].markMiss();
}
}
// Update UI
updateUI();
// Check if score is higher than high score
if (score > highScore) {
highScore = score;
storage.highScore = highScore;
}
// Show game over
LK.setTimeout(function () {
LK.showGameOver();
}, 1000);
}
}
// Function to advance to next level
function advanceLevel() {
level++;
storage.level = level;
// Play level up sound
LK.getSound('levelUp').play();
// Show level up animation
var levelUpText = new Text2("LEVEL UP!", {
size: 120,
fill: 0xFFCC00
});
levelUpText.anchor.set(0.5, 0.5);
levelUpText.x = 2048 / 2;
levelUpText.y = 2732 / 2;
game.addChild(levelUpText);
tween(levelUpText, {
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 1000,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(levelUpText, {
alpha: 0
}, {
duration: 500,
onFinish: function onFinish() {
levelUpText.destroy();
// Create new targets for next level
createTargetsForLevel(level);
}
});
}
});
// Update UI
updateUI();
}
// Game tap/click handler
game.down = function (x, y, obj) {
if (!isGameStarted) {
// Start game on first tap
initGame();
return;
}
checkNeedleHit();
};
// Game update function
game.update = function () {
if (!isGameStarted) {
return;
}
// Nothing needs updating here as the needle has its own update method
};
// Initialize the game elements
highScore = storage.highScore || 0;
level = 1; // Always start at level 1 for a new game
highScoreTxt.setText("High Score: " + highScore);
// Show start instructions
instructionTxt.setText("Tap to start!");