/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1", { highestDotCount: 0, autoSpawn: true, spawnInterval: 1000 }); /**** * Classes ****/ var AddButton = Container.expand(function () { var self = Container.call(this); var circle = self.attachAsset('addButton', { anchorX: 0.5, anchorY: 0.5 }); var plus1 = self.attachAsset('plusSign', { anchorX: 0.5, anchorY: 0.5 }); var plus2 = self.attachAsset('plusSign', { anchorX: 0.5, anchorY: 0.5, rotation: Math.PI / 2 }); self.down = function (x, y, obj) { // Visual feedback tween(circle, { scaleX: 0.9, scaleY: 0.9 }, { duration: 100, easing: tween.easeOut }); }; self.up = function (x, y, obj) { // Visual feedback tween(circle, { scaleX: 1, scaleY: 1 }, { duration: 200, easing: tween.elasticOut }); // Toggle auto spawn game.toggleAutoSpawn(); }; return self; }); var Dot = Container.expand(function () { var self = Container.call(this); var dotGraphics = self.attachAsset('dot', { anchorX: 0.5, anchorY: 0.5, scaleX: 1, scaleY: 1 }); self.angle = 0; self.speed = 0.02 + Math.random() * 0.02; self.radius = 250 + Math.random() * 100; self.verticalStretch = 0.7; self.alive = true; self.breakDirection = { x: 0, y: 0 }; self.resetPosition = function () { self.x = game.figureCenterX; self.y = game.figureCenterY; }; self.update = function () { if (!self.alive) { // Dot is breaking away from the orbit self.x += self.breakDirection.x; self.y += self.breakDirection.y; // Apply some gravity to make it fall off screen self.breakDirection.y += 0.2; // Check if dot is off screen if (self.y > 2732 + 100 || self.x < -100 || self.x > 2048 + 100) { self.destroy(); game.removeDeadDot(self); } return; } // Update angle based on game speed (gravity) self.angle += self.speed * game.gravityStrength; // Calculate position on figure 8 path var figureX = game.figureCenterX + Math.sin(self.angle) * self.radius; var figureY = game.figureCenterY + Math.sin(2 * self.angle) * self.radius * self.verticalStretch; // Smoothly move to the calculated position self.x = figureX; self.y = figureY; }; self.breakOrbit = function (directionX, directionY) { if (!self.alive) { return; } self.alive = false; // Set break direction based on current position and tap location self.breakDirection = { x: (directionX - self.x) * 0.1, y: (directionY - self.y) * 0.1 }; // Play break sound LK.getSound('break').play(); // Make dot fade out as it breaks orbit tween(dotGraphics, { alpha: 0 }, { duration: 1500, easing: tween.linear }); }; return self; }); var GravityControl = Container.expand(function () { var self = Container.call(this); var bar = self.attachAsset('gravityControl', { anchorX: 0.5, anchorY: 0.5 }); var slider = self.attachAsset('gravitySlider', { anchorX: 0.5, anchorY: 0.5 }); var minX = -bar.width / 2 + slider.width / 2; var maxX = bar.width / 2 - slider.width / 2; self.dragging = false; self.updateSliderPosition = function (value) { // Position the slider based on gravity strength value (0.1 to 2.0) var normalizedValue = (value - 0.1) / 1.9; // Normalize to 0-1 slider.x = minX + normalizedValue * (maxX - minX); }; self.getValueFromPosition = function () { // Calculate gravity value from slider position var normalizedPos = (slider.x - minX) / (maxX - minX); return 0.1 + normalizedPos * 1.9; // Map to 0.1-2.0 range }; self.down = function (x, y, obj) { var localPos = self.toLocal({ x: x, y: y }); if (Math.abs(localPos.x - slider.x) < slider.width && Math.abs(localPos.y - slider.y) < slider.height) { self.dragging = true; } }; self.up = function (x, y, obj) { self.dragging = false; }; self.move = function (x, y, obj) { if (self.dragging) { var localPos = self.toLocal({ x: x, y: y }); slider.x = Math.max(minX, Math.min(maxX, localPos.x)); game.gravityStrength = self.getValueFromPosition(); } }; return self; }); var PathVisualizer = Container.expand(function () { var self = Container.call(this); self.pathPoints = []; self.pathDots = []; self.createPath = function (dotCount) { // Clear existing path visualization for (var i = 0; i < self.pathDots.length; i++) { self.removeChild(self.pathDots[i]); } self.pathDots = []; // Calculate path points for figure 8 self.pathPoints = []; for (var angle = 0; angle < Math.PI * 2; angle += 0.05) { var x = game.figureCenterX + Math.sin(angle) * 300; var y = game.figureCenterY + Math.sin(2 * angle) * 300 * 0.7; self.pathPoints.push({ x: x, y: y }); } // Create visual path with dots for (var i = 0; i < self.pathPoints.length; i++) { var dot = LK.getAsset(i % 5 === 0 ? 'pathHighlight' : 'path', { anchorX: 0.5, anchorY: 0.5 }); dot.x = self.pathPoints[i].x; dot.y = self.pathPoints[i].y; self.pathDots.push(dot); self.addChild(dot); } }; self.isPointNearPath = function (x, y) { // Check if a point is near the figure 8 path for (var i = 0; i < self.pathPoints.length; i++) { var distance = Math.sqrt(Math.pow(x - self.pathPoints[i].x, 2) + Math.pow(y - self.pathPoints[i].y, 2)); if (distance < 50) { return true; } } return false; }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x111133 }); /**** * Game Code ****/ game.dots = []; game.gravityStrength = 1.0; game.figureCenterX = 2048 / 2; game.figureCenterY = 2732 / 2; game.autoSpawnEnabled = storage.autoSpawn; game.spawnInterval = storage.spawnInterval; game.lastSpawnTime = 0; game.bestTime = 0; game.currentTime = 0; game.stopwatchRunning = false; // Stopwatch display var stopwatchTxt = new Text2('Time: 0.00', { size: 60, fill: 0xFFFFFF }); stopwatchTxt.anchor.set(0.5, 0); LK.gui.top.addChild(stopwatchTxt); stopwatchTxt.x = 0; stopwatchTxt.y = 50; var background = LK.getAsset('background', { anchorX: 0, anchorY: 0 }); game.addChild(background); // Create path visualizer var pathVisualizer = new PathVisualizer(); game.addChild(pathVisualizer); pathVisualizer.createPath(50); // Create add button var addButton = new AddButton(); addButton.x = 2048 - 150; addButton.y = 2732 - 150; game.addChild(addButton); // Count display var countTxt = new Text2('Dots: 0', { size: 80, fill: 0xFFFFFF }); countTxt.anchor.set(0, 0); LK.gui.topRight.addChild(countTxt); countTxt.x = -countTxt.width - 50; countTxt.y = 50; // Auto spawn status display var autoSpawnTxt = new Text2('Auto: ' + (game.autoSpawnEnabled ? 'ON' : 'OFF'), { size: 60, fill: game.autoSpawnEnabled ? "#88ff88" : "#ff8888" }); autoSpawnTxt.anchor.set(0.5, 0.5); addButton.addChild(autoSpawnTxt); autoSpawnTxt.y = -100; // Game functions game.createDot = function (x, y) { var dot = new Dot(); // Randomize starting position on the path dot.angle = Math.random() * Math.PI * 2; // Add dot to game game.dots.push(dot); game.addChild(dot); // Update counter countTxt.setText('Dots: ' + game.dots.length); // Check high score if (game.dots.length > storage.highestDotCount) { storage.highestDotCount = game.dots.length; highScoreTxt.setText('Best: ' + storage.highestDotCount); } // Play pop sound LK.getSound('pop').play(); // Animate dot appearing dot.scale.set(0); tween(dot, { scaleX: 1, scaleY: 1 }, { duration: 300, easing: tween.elasticOut }); return dot; }; game.removeDeadDot = function (dot) { // Remove dead dot from array var index = game.dots.indexOf(dot); if (index !== -1) { game.dots.splice(index, 1); countTxt.setText('Dots: ' + game.dots.length); } }; game.toggleAutoSpawn = function () { game.autoSpawnEnabled = !game.autoSpawnEnabled; storage.autoSpawn = game.autoSpawnEnabled; // Update text autoSpawnTxt.setText('Auto: ' + (game.autoSpawnEnabled ? 'ON' : 'OFF')); autoSpawnTxt.setStyle({ fill: game.autoSpawnEnabled ? "#88ff88" : "#ff8888" }); }; // Event handlers game.down = function (x, y, obj) { // Check if the tap was on the path or outside var onPath = pathVisualizer.isPointNearPath(x, y); if (onPath) { // Create a new dot at the figure 8 center game.createDot(); } else { // Break orbits of nearby dots for (var i = 0; i < game.dots.length; i++) { var dot = game.dots[i]; var distance = Math.sqrt(Math.pow(x - dot.x, 2) + Math.pow(y - dot.y, 2)); if (distance < 150 && dot.alive) { dot.breakOrbit(x, y); } } } }; // Main update loop game.update = function () { // Auto spawn dots if (game.autoSpawnEnabled) { if (LK.ticks - game.lastSpawnTime > game.spawnInterval / 16.67) { game.lastSpawnTime = LK.ticks; game.createDot(); // Update stopwatch if (game.stopwatchRunning) { game.currentTime += 1 / 60; // Assuming 60 FPS stopwatchTxt.setText('Time: ' + game.currentTime.toFixed(2)); } // Check if all dots are still in orbit var allDotsInOrbit = game.dots.every(function (dot) { return dot.alive; }); if (allDotsInOrbit) { if (!game.stopwatchRunning) { game.stopwatchRunning = true; game.currentTime = 0; } } else { if (game.stopwatchRunning) { game.stopwatchRunning = false; if (game.currentTime > game.bestTime) { game.bestTime = game.currentTime; } game.currentTime = 0; } } } } }; // Start background music LK.playMusic('ambient', { fade: { start: 0, end: 0.5, duration: 2000 } });
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
highestDotCount: 0,
autoSpawn: true,
spawnInterval: 1000
});
/****
* Classes
****/
var AddButton = Container.expand(function () {
var self = Container.call(this);
var circle = self.attachAsset('addButton', {
anchorX: 0.5,
anchorY: 0.5
});
var plus1 = self.attachAsset('plusSign', {
anchorX: 0.5,
anchorY: 0.5
});
var plus2 = self.attachAsset('plusSign', {
anchorX: 0.5,
anchorY: 0.5,
rotation: Math.PI / 2
});
self.down = function (x, y, obj) {
// Visual feedback
tween(circle, {
scaleX: 0.9,
scaleY: 0.9
}, {
duration: 100,
easing: tween.easeOut
});
};
self.up = function (x, y, obj) {
// Visual feedback
tween(circle, {
scaleX: 1,
scaleY: 1
}, {
duration: 200,
easing: tween.elasticOut
});
// Toggle auto spawn
game.toggleAutoSpawn();
};
return self;
});
var Dot = Container.expand(function () {
var self = Container.call(this);
var dotGraphics = self.attachAsset('dot', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1,
scaleY: 1
});
self.angle = 0;
self.speed = 0.02 + Math.random() * 0.02;
self.radius = 250 + Math.random() * 100;
self.verticalStretch = 0.7;
self.alive = true;
self.breakDirection = {
x: 0,
y: 0
};
self.resetPosition = function () {
self.x = game.figureCenterX;
self.y = game.figureCenterY;
};
self.update = function () {
if (!self.alive) {
// Dot is breaking away from the orbit
self.x += self.breakDirection.x;
self.y += self.breakDirection.y;
// Apply some gravity to make it fall off screen
self.breakDirection.y += 0.2;
// Check if dot is off screen
if (self.y > 2732 + 100 || self.x < -100 || self.x > 2048 + 100) {
self.destroy();
game.removeDeadDot(self);
}
return;
}
// Update angle based on game speed (gravity)
self.angle += self.speed * game.gravityStrength;
// Calculate position on figure 8 path
var figureX = game.figureCenterX + Math.sin(self.angle) * self.radius;
var figureY = game.figureCenterY + Math.sin(2 * self.angle) * self.radius * self.verticalStretch;
// Smoothly move to the calculated position
self.x = figureX;
self.y = figureY;
};
self.breakOrbit = function (directionX, directionY) {
if (!self.alive) {
return;
}
self.alive = false;
// Set break direction based on current position and tap location
self.breakDirection = {
x: (directionX - self.x) * 0.1,
y: (directionY - self.y) * 0.1
};
// Play break sound
LK.getSound('break').play();
// Make dot fade out as it breaks orbit
tween(dotGraphics, {
alpha: 0
}, {
duration: 1500,
easing: tween.linear
});
};
return self;
});
var GravityControl = Container.expand(function () {
var self = Container.call(this);
var bar = self.attachAsset('gravityControl', {
anchorX: 0.5,
anchorY: 0.5
});
var slider = self.attachAsset('gravitySlider', {
anchorX: 0.5,
anchorY: 0.5
});
var minX = -bar.width / 2 + slider.width / 2;
var maxX = bar.width / 2 - slider.width / 2;
self.dragging = false;
self.updateSliderPosition = function (value) {
// Position the slider based on gravity strength value (0.1 to 2.0)
var normalizedValue = (value - 0.1) / 1.9; // Normalize to 0-1
slider.x = minX + normalizedValue * (maxX - minX);
};
self.getValueFromPosition = function () {
// Calculate gravity value from slider position
var normalizedPos = (slider.x - minX) / (maxX - minX);
return 0.1 + normalizedPos * 1.9; // Map to 0.1-2.0 range
};
self.down = function (x, y, obj) {
var localPos = self.toLocal({
x: x,
y: y
});
if (Math.abs(localPos.x - slider.x) < slider.width && Math.abs(localPos.y - slider.y) < slider.height) {
self.dragging = true;
}
};
self.up = function (x, y, obj) {
self.dragging = false;
};
self.move = function (x, y, obj) {
if (self.dragging) {
var localPos = self.toLocal({
x: x,
y: y
});
slider.x = Math.max(minX, Math.min(maxX, localPos.x));
game.gravityStrength = self.getValueFromPosition();
}
};
return self;
});
var PathVisualizer = Container.expand(function () {
var self = Container.call(this);
self.pathPoints = [];
self.pathDots = [];
self.createPath = function (dotCount) {
// Clear existing path visualization
for (var i = 0; i < self.pathDots.length; i++) {
self.removeChild(self.pathDots[i]);
}
self.pathDots = [];
// Calculate path points for figure 8
self.pathPoints = [];
for (var angle = 0; angle < Math.PI * 2; angle += 0.05) {
var x = game.figureCenterX + Math.sin(angle) * 300;
var y = game.figureCenterY + Math.sin(2 * angle) * 300 * 0.7;
self.pathPoints.push({
x: x,
y: y
});
}
// Create visual path with dots
for (var i = 0; i < self.pathPoints.length; i++) {
var dot = LK.getAsset(i % 5 === 0 ? 'pathHighlight' : 'path', {
anchorX: 0.5,
anchorY: 0.5
});
dot.x = self.pathPoints[i].x;
dot.y = self.pathPoints[i].y;
self.pathDots.push(dot);
self.addChild(dot);
}
};
self.isPointNearPath = function (x, y) {
// Check if a point is near the figure 8 path
for (var i = 0; i < self.pathPoints.length; i++) {
var distance = Math.sqrt(Math.pow(x - self.pathPoints[i].x, 2) + Math.pow(y - self.pathPoints[i].y, 2));
if (distance < 50) {
return true;
}
}
return false;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x111133
});
/****
* Game Code
****/
game.dots = [];
game.gravityStrength = 1.0;
game.figureCenterX = 2048 / 2;
game.figureCenterY = 2732 / 2;
game.autoSpawnEnabled = storage.autoSpawn;
game.spawnInterval = storage.spawnInterval;
game.lastSpawnTime = 0;
game.bestTime = 0;
game.currentTime = 0;
game.stopwatchRunning = false;
// Stopwatch display
var stopwatchTxt = new Text2('Time: 0.00', {
size: 60,
fill: 0xFFFFFF
});
stopwatchTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(stopwatchTxt);
stopwatchTxt.x = 0;
stopwatchTxt.y = 50;
var background = LK.getAsset('background', {
anchorX: 0,
anchorY: 0
});
game.addChild(background);
// Create path visualizer
var pathVisualizer = new PathVisualizer();
game.addChild(pathVisualizer);
pathVisualizer.createPath(50);
// Create add button
var addButton = new AddButton();
addButton.x = 2048 - 150;
addButton.y = 2732 - 150;
game.addChild(addButton);
// Count display
var countTxt = new Text2('Dots: 0', {
size: 80,
fill: 0xFFFFFF
});
countTxt.anchor.set(0, 0);
LK.gui.topRight.addChild(countTxt);
countTxt.x = -countTxt.width - 50;
countTxt.y = 50;
// Auto spawn status display
var autoSpawnTxt = new Text2('Auto: ' + (game.autoSpawnEnabled ? 'ON' : 'OFF'), {
size: 60,
fill: game.autoSpawnEnabled ? "#88ff88" : "#ff8888"
});
autoSpawnTxt.anchor.set(0.5, 0.5);
addButton.addChild(autoSpawnTxt);
autoSpawnTxt.y = -100;
// Game functions
game.createDot = function (x, y) {
var dot = new Dot();
// Randomize starting position on the path
dot.angle = Math.random() * Math.PI * 2;
// Add dot to game
game.dots.push(dot);
game.addChild(dot);
// Update counter
countTxt.setText('Dots: ' + game.dots.length);
// Check high score
if (game.dots.length > storage.highestDotCount) {
storage.highestDotCount = game.dots.length;
highScoreTxt.setText('Best: ' + storage.highestDotCount);
}
// Play pop sound
LK.getSound('pop').play();
// Animate dot appearing
dot.scale.set(0);
tween(dot, {
scaleX: 1,
scaleY: 1
}, {
duration: 300,
easing: tween.elasticOut
});
return dot;
};
game.removeDeadDot = function (dot) {
// Remove dead dot from array
var index = game.dots.indexOf(dot);
if (index !== -1) {
game.dots.splice(index, 1);
countTxt.setText('Dots: ' + game.dots.length);
}
};
game.toggleAutoSpawn = function () {
game.autoSpawnEnabled = !game.autoSpawnEnabled;
storage.autoSpawn = game.autoSpawnEnabled;
// Update text
autoSpawnTxt.setText('Auto: ' + (game.autoSpawnEnabled ? 'ON' : 'OFF'));
autoSpawnTxt.setStyle({
fill: game.autoSpawnEnabled ? "#88ff88" : "#ff8888"
});
};
// Event handlers
game.down = function (x, y, obj) {
// Check if the tap was on the path or outside
var onPath = pathVisualizer.isPointNearPath(x, y);
if (onPath) {
// Create a new dot at the figure 8 center
game.createDot();
} else {
// Break orbits of nearby dots
for (var i = 0; i < game.dots.length; i++) {
var dot = game.dots[i];
var distance = Math.sqrt(Math.pow(x - dot.x, 2) + Math.pow(y - dot.y, 2));
if (distance < 150 && dot.alive) {
dot.breakOrbit(x, y);
}
}
}
};
// Main update loop
game.update = function () {
// Auto spawn dots
if (game.autoSpawnEnabled) {
if (LK.ticks - game.lastSpawnTime > game.spawnInterval / 16.67) {
game.lastSpawnTime = LK.ticks;
game.createDot();
// Update stopwatch
if (game.stopwatchRunning) {
game.currentTime += 1 / 60; // Assuming 60 FPS
stopwatchTxt.setText('Time: ' + game.currentTime.toFixed(2));
}
// Check if all dots are still in orbit
var allDotsInOrbit = game.dots.every(function (dot) {
return dot.alive;
});
if (allDotsInOrbit) {
if (!game.stopwatchRunning) {
game.stopwatchRunning = true;
game.currentTime = 0;
}
} else {
if (game.stopwatchRunning) {
game.stopwatchRunning = false;
if (game.currentTime > game.bestTime) {
game.bestTime = game.currentTime;
}
game.currentTime = 0;
}
}
}
}
};
// Start background music
LK.playMusic('ambient', {
fade: {
start: 0,
end: 0.5,
duration: 2000
}
});