/****
* Plugins
****/
var storage = LK.import("@upit/storage.v1");
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var CircularObstacle = Container.expand(function (starX, starY, radius, angleSpeed, initialAngle) {
var self = Container.call(this);
var obstacleGraphics = self.attachAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5
});
self.angle = initialAngle || 0;
self.radius = radius;
self.angleSpeed = angleSpeed * 0.75;
self.starX = starX;
self.starY = starY;
self.updatePosition = function () {
self.x = self.starX + Math.cos(self.angle) * self.radius;
self.y = self.starY + Math.sin(self.angle) * self.radius;
self.angle += self.angleSpeed * 0.35;
obstacleGraphics.rotation += self.angleSpeed * 0.35;
};
self.moveDown = function (distance, dotY) {
var speedMultiplier = dotY < 1200 ? 3 : dotY < 1400 ? 2 : dotY < 2000 ? 1.3 : 1.2;
self.starY += distance * speedMultiplier;
};
self.updatePosition();
});
var Dot = Container.expand(function () {
var self = Container.call(this);
var dotGraphics = self.attachAsset('dot', {
anchorX: 0.5,
anchorY: 0.5
});
self.destroyed = false;
self.velocityY = 0;
self.gravity = 0.7;
self.bounce = -15;
self._update_migrated = function () {
var previousY = self.y;
self.velocityY += self.gravity;
self.y += self.velocityY;
if (!self.firstTouch && !self.intersects(hand)) {
self.y = 2732 - self.height / 2 - 500;
} else if (self.intersects(hand)) {
self.bounceUp();
} else if (self.y > 2232 - self.height / 2 && self.firstTouch && !self.firstStarDestroyed) {
self.velocityY = self.bounce;
} else if (self.y > 2832 && !self.destroyed) {
createDotParticleEffect(self.x, self.y);
self.destroyed = true;
self.destroy();
LK.getSound('death').play(); // Play death sound
LK.setTimeout(function () {
triggerGameOver();
}, 1000);
}
self.movedDistance = self.y - previousY;
};
self.bounceUp = function () {
if (!self.destroyed) {
self.velocityY = self.bounce;
self.firstTouch = true;
if (!self.firstStarDestroyed) {
self.firstStarDestroyed = true;
}
}
};
});
var DotParticleEffect = Container.expand(function (x, y) {
var self = Container.call(this);
self.x = x;
self.y = y;
var particles = [];
var angleIncrement = Math.PI * 2 / 20;
var speed = 30;
for (var i = 0; i < 20; i++) {
var particle = self.attachAsset('dot', {
anchorX: 0.5,
anchorY: 0.5
});
particle.scaleX = particle.scaleY = 0.5;
particle.alpha = 1;
var angle = i * angleIncrement;
particle.vx = Math.cos(angle) * speed;
particle.vy = Math.sin(angle) * speed;
particles.push(particle);
}
self._update_migrated = function () {
for (var i = particles.length - 1; i >= 0; i--) {
particles[i].x += particles[i].vx;
particles[i].y += particles[i].vy;
particles[i].alpha -= 0.0167;
if (particles[i].alpha <= 0) {
particles[i].destroy();
particles.splice(i, 1);
}
}
if (particles.length === 0) {
self.destroy();
}
};
game.addChild(self);
});
// Add Hand asset below the dot
var Hand = Container.expand(function () {
var self = Container.call(this);
var handGraphics = self.attachAsset('hand', {
anchorX: 0.5,
anchorY: 0,
alpha: 0.9
});
self._update_migrated = function (distance) {
if (distance) {
self.y += distance;
}
};
});
var OrbitLine = Container.expand(function (starX, starY, radius) {
var self = Container.call(this);
self.starX = starX;
self.starY = starY;
self.radius = radius;
var segments = 50;
var angleIncrement = Math.PI * 2 / segments;
for (var i = 0; i < segments; i++) {
var angle = i * angleIncrement;
var dot = self.attachAsset('smallObstacle', {
x: starX + Math.cos(angle) * radius,
y: starY + Math.sin(angle) * radius,
anchorX: 0.5,
anchorY: 0.5
});
if (i % 2 === 0) {
dot.alpha = 0;
}
}
self.moveDown = function (distance, dotY) {
var speedMultiplier = dotY < 1200 ? 3 : dotY < 1400 ? 2 : dotY < 2000 ? 1.3 : dotY < 2300 ? 1.2 : 1;
self.y += distance * speedMultiplier;
};
});
var ParticleEffect = Container.expand(function (x, y) {
var self = Container.call(this);
self.x = x;
self.y = y;
var particles = [];
for (var i = 0; i < 20; i++) {
var particle = self.attachAsset('star', {
anchorX: 0.5,
anchorY: 0.5
});
particle.scaleX = particle.scaleY = Math.random() * 0.5 + 0.5;
particle.alpha = 0.7;
particle.vx = (Math.random() - 0.5) * 10;
particle.vy = (Math.random() - 0.5) * 10;
particles.push(particle);
}
self._update_migrated = function () {
for (var i = particles.length - 1; i >= 0; i--) {
particles[i].x += particles[i].vx;
particles[i].y += particles[i].vy;
particles[i].alpha -= 0.02;
if (particles[i].alpha <= 0) {
particles[i].destroy();
particles.splice(i, 1);
}
}
if (particles.length === 0) {
self.destroy();
}
};
game.addChild(self);
});
var ScorePopup = Container.expand(function (x, y) {
var self = Container.call(this);
self.x = x;
self.y = y;
var scoreText = new Text2('+1', {
size: 100,
fill: 0xFFFFFF,
anchorX: 0.5,
anchorY: 0.5
});
scoreText.x = -scoreText.width / 2;
scoreText.y = -scoreText.height / 2;
self.addChild(scoreText);
self.alpha = 1;
var duration = 120;
self._update_migrated = function () {
duration--;
self.y -= 2;
self.alpha -= 1 / 120;
if (duration <= 0) {
self.destroy();
}
};
game.addChild(self);
});
var Star = Container.expand(function () {
var self = Container.call(this);
var starGraphics = self.attachAsset('star', {
anchorX: 0.5,
anchorY: 0.5
});
self.scaleDirection = 1;
self.scaleSpeed = 0.005;
self.minScale = 1;
self.maxScale = 1.2;
self.moveDown = function (distance, dotY) {
var speedMultiplier = dotY < 1200 ? 3 : dotY < 1400 ? 2 : dotY < 2000 ? 1.3 : 1.2;
self.y += distance * speedMultiplier;
if (self.y > 2732 - self.height / 2) {
self.y = -self.height / 2;
}
};
self.updateScale = function () {
if (self.scaleDirection === 1 && starGraphics.scale.x < self.maxScale) {
starGraphics.scale.x += self.scaleSpeed;
starGraphics.scale.y += self.scaleSpeed;
} else if (self.scaleDirection === -1 && starGraphics.scale.x > self.minScale) {
starGraphics.scale.x -= self.scaleSpeed;
starGraphics.scale.y -= self.scaleSpeed;
}
if (starGraphics.scale.x >= self.maxScale || starGraphics.scale.x <= self.minScale) {
self.scaleDirection *= -1;
}
};
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
LK.playMusic('Backgroundmusic');
var gameState = 'title'; // 'title', 'playing', 'gameOver'
var gameTitleAsset;
var highScoreTextDisplay;
var tapToStartText;
var blinkIntervalId = null; // For "Tap to Start" blinking
// Game elements that will be initialized later or need visibility control
var dot;
var stars = [];
var obstacles = [];
var orbitLine;
var hand;
var handMoved = false;
var titleDot; // Declare titleDot here
var scoreTxt; // Declare scoreTxt here so it's globally accessible for title/game states
var highScoreGameTxt; // New: high score text for in-game display
var menuBackground; // For managing the title screen background overlay
// Function to setup the title screen
function setupTitleScreen() {
gameState = 'title';
LK.setScore(0); // Reset score for the title screen phase
// Add a black background overlay that covers the entire game area
menuBackground = LK.getAsset('smallObstacle', {
anchorX: 0.5,
anchorY: 0.5,
width: 2048,
height: 2732,
tint: 0x000000,
alpha: 1.0
});
menuBackground.width = 2048;
menuBackground.height = 2732;
menuBackground.x = 2048 / 2;
menuBackground.y = 2732 / 2;
game.addChild(menuBackground);
gameTitleAsset = game.addChild(LK.getAsset('gameTitle', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 3.5 // Position title a bit up
}));
// Add a large dot below the game title
titleDot = game.addChild(LK.getAsset('dot', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: gameTitleAsset.y + gameTitleAsset.height / 2 + 600,
// Position below the title, moved 400px down
// Position below the title
scaleX: 4,
// Make it bigger
scaleY: 4 // Make it bigger
}));
// Add a soft up and down bounce animation to the titleDot that always returns to the same position
(function bounceTitleDot() {
// Defensive: Only animate if titleDot exists, is not null, and is not destroyed
if (!(gameState === 'title' && titleDot && titleDot.parent && typeof titleDot.y === 'number')) return;
var originalY = titleDot.y;
// Always tween from originalY to originalY-60 and back, so the dot never drifts
tween(titleDot, {
y: originalY - 60
}, {
duration: 700,
easing: tween.sineInOut,
onFinish: function onFinish() {
// Defensive: Check again before next tween
if (!(gameState === 'title' && titleDot && titleDot.parent && typeof titleDot.y === 'number')) return;
tween(titleDot, {
y: originalY
}, {
duration: 700,
easing: tween.sineInOut,
onFinish: function onFinish() {
// Loop the bounce as long as we're on the title screen and titleDot is valid
bounceTitleDot();
}
});
}
});
})();
// Remove "TAP TO START" text from inside the titleDot (do not add it to the dot)
// Center "Tap to Start" horizontally and place it above the high score (if present), otherwise below the title
tapToStartText = new Text2('TAP TO START', {
size: 120,
fill: '#FFFFFF',
// White text
stroke: '#000000',
// Black outline for contrast
strokeThickness: 8,
alpha: 0 // Start invisible for soft fade animation
// anchorX and anchorY will be set via .anchor.set() for precise centering
});
tapToStartText.anchor.set(0.5, 0.5);
tapToStartText.x = 2048 / 2;
var tapTextYPosition;
if (highScoreTextDisplay && highScoreTextDisplay.parent) {
// Place "Tap to Start" above the high score
tapTextYPosition = highScoreTextDisplay.y - highScoreTextDisplay.height / 2 - 100;
} else if (gameTitleAsset && gameTitleAsset.parent) {
tapTextYPosition = gameTitleAsset.y + gameTitleAsset.height / 2 + 150;
} else {
tapTextYPosition = 2732 / 2 + 100; // Fallback y position
}
tapToStartText.y = tapTextYPosition + 1000;
game.addChild(tapToStartText);
if (blinkIntervalId) LK.clearInterval(blinkIntervalId);
// Soft fade in/out animation using tween
var fadeIn = true;
function animateTapToStart() {
if (!(gameState === 'title' && tapToStartText && tapToStartText.parent)) {
if (blinkIntervalId) {
LK.clearInterval(blinkIntervalId);
blinkIntervalId = null;
}
return;
}
tween(tapToStartText, {
alpha: fadeIn ? 1 : 0.3
}, {
duration: 700,
onFinish: function onFinish() {
fadeIn = !fadeIn;
animateTapToStart();
}
});
}
animateTapToStart();
if (scoreTxt && scoreTxt.parent) {
scoreTxt.visible = false;
}
// Hide all game elements when title screen is up - ensure they're properly hidden
if (dot) {
dot.visible = false;
}
stars.forEach(function (s) {
if (s) s.visible = false;
});
obstacles.forEach(function (o) {
if (o) o.visible = false;
});
if (orbitLine) orbitLine.visible = false;
if (hand) hand.visible = false;
}
// Function to initialize and start the actual game
function startGamePlay() {
if (gameState !== 'title') return;
// gameState = 'playing'; // Moved into completeGameSetupAfterFade
if (blinkIntervalId) {
LK.clearInterval(blinkIntervalId);
blinkIntervalId = null;
}
var elementsToFadeOut = [];
if (gameTitleAsset && gameTitleAsset.parent) elementsToFadeOut.push(gameTitleAsset);
if (highScoreTextDisplay && highScoreTextDisplay.parent) elementsToFadeOut.push(highScoreTextDisplay);
if (tapToStartText && tapToStartText.parent) elementsToFadeOut.push(tapToStartText);
if (menuBackground && menuBackground.parent) elementsToFadeOut.push(menuBackground);
if (titleDot && titleDot.parent) elementsToFadeOut.push(titleDot);
var fadeDuration = 500; // Duration of the fade in milliseconds
var tweensCompletedCount = 0;
function completeGameSetupAfterFade() {
// Destroy elements now that they are faded and nullify global references
if (gameTitleAsset && gameTitleAsset.parent) gameTitleAsset.destroy();
gameTitleAsset = null;
if (highScoreTextDisplay && highScoreTextDisplay.parent) highScoreTextDisplay.destroy();
highScoreTextDisplay = null;
if (tapToStartText && tapToStartText.parent) tapToStartText.destroy();
tapToStartText = null;
if (menuBackground && menuBackground.parent) {
menuBackground.destroy();
menuBackground = null;
}
if (titleDot && titleDot.parent) {
titleDot.destroy();
}
titleDot = null; // Nullify titleDot as well for good practice
// This is the logic that was originally AFTER the destroyed elements
if (!scoreTxt || !scoreTxt.parent) {
if (scoreTxt) scoreTxt.destroy(); // Ensure old one is gone if somehow exists
scoreTxt = new Text2('0', {
size: 120,
// Adjusted size for top-right placement
fill: 0xFFFFFF
}); //{2R} // Line identifier preserved
scoreTxt.anchor.set(1, 0); // Anchor to top-right of the text
scoreTxt.x = -20; // Position 20px from the right edge of the gui.topRight container
scoreTxt.y = 20; // Position 20px from the top edge of the gui.topRight container
LK.gui.topRight.addChild(scoreTxt);
}
scoreTxt.setText('0');
scoreTxt.alpha = 0; // Prepare scoreTxt for fade-in
scoreTxt.visible = true;
// Add high score below the score
if (highScoreGameTxt && highScoreGameTxt.parent) {
highScoreGameTxt.destroy();
highScoreGameTxt = null;
}
var currentHighScore = storage.highScore || 0;
if (currentHighScore > 0) {
highScoreGameTxt = new Text2('BEST: ' + currentHighScore, {
size: 40,
// Reduced size for the 'BEST' text
// Adjusted size for better visibility below score
fill: '#FFFFFF'
}); //{3i} // Preserving line identifier for the Text2 options object
highScoreGameTxt.anchor.set(1, 0); // Anchor to its top-right
highScoreGameTxt.x = -20; // Align with scoreTxt's x-position (20px from right edge of gui.topRight)
// Position highScoreGameTxt directly below scoreTxt
if (scoreTxt && scoreTxt.parent) {
// Ensure scoreTxt is available
highScoreGameTxt.y = scoreTxt.y + scoreTxt.height + 10; // Position 10px below scoreTxt
} else {
// Fallback if scoreTxt isn't ready (scoreTxt.y is 20, assuming its height ~120)
highScoreGameTxt.y = 20 + 120 + 10;
}
LK.gui.topRight.addChild(highScoreGameTxt); // Add to top right GUI container
highScoreGameTxt.alpha = 0; // Prepare for fade-in, similar to scoreTxt
}
LK.setScore(0);
initializeGameElements();
gameState = 'playing'; // Set gameState to 'playing' only after all setup is complete
}
if (elementsToFadeOut.length === 0) {
completeGameSetupAfterFade(); // No elements to fade, proceed directly
} else {
elementsToFadeOut.forEach(function (element) {
tween(element, {
alpha: 0
}, {
duration: fadeDuration,
onFinish: function onFinish() {
tweensCompletedCount++;
if (tweensCompletedCount === elementsToFadeOut.length) {
completeGameSetupAfterFade();
}
}
});
});
}
}
// Function to initialize and fade in game elements
function initializeGameElements() {
var elementsToFadeIn = [];
var fadeDuration = 500; // Duration for game elements fade-in in milliseconds
// Collect scoreTxt if it's ready (alpha is preset by completeGameSetupAfterFade)
if (scoreTxt && scoreTxt.parent) {
elementsToFadeIn.push(scoreTxt);
}
// Collect highScoreGameTxt if it exists and is part of the GUI
if (highScoreGameTxt && highScoreGameTxt.parent) {
elementsToFadeIn.push(highScoreGameTxt);
}
// Collect dot
if (dot) {
elementsToFadeIn.push(dot);
}
// Collect stars
stars.forEach(function (s) {
if (s) {
elementsToFadeIn.push(s);
}
});
// Collect obstacles (this includes the initial orbitLine and CircularObstacles)
obstacles.forEach(function (o) {
if (o) {
elementsToFadeIn.push(o);
}
});
// Collect hand
if (hand) {
elementsToFadeIn.push(hand);
}
if (elementsToFadeIn.length === 0) {
return; // No elements to fade
}
// Set initial properties and start tween for each element
elementsToFadeIn.forEach(function (element) {
// For elements other than scoreTxt, set initial alpha.
// scoreTxt's alpha is set to 0 by the calling function completeGameSetupAfterFade.
if (element !== scoreTxt) {
element.alpha = 0;
}
element.visible = true; // Ensure element is visible for alpha tween to have an effect
tween(element, {
// Tween to fade in
alpha: 1
}, {
duration: fadeDuration
// No specific onFinish needed here as gameState is handled by the caller
});
});
}
// Initialize game elements before setting up the title screen.
// This ensures they exist for the hiding logic in setupTitleScreen and are layered correctly.
// Dot (needed for Hand positioning)
dot = game.addChild(new Dot());
dot.x = 2048 / 2;
dot.y = 2732 - dot.height / 2 - 200;
// Stars
// global 'stars' array is already initialized as [] in variable declarations
star = game.addChild(new Star());
star.x = 2048 / 2;
star.y = 2732 / 2 - 500;
stars.push(star);
// Hand
// Global 'hand' is assigned within createHand. createHand function definition is hoisted.
// Global 'handMoved' is initialized at declaration; ensure it's correctly 'false' for a new game start.
createHand();
handMoved = false; // Explicitly set for game start logic
// Obstacles and OrbitLine
// global 'obstacles' array is already initialized as [] in variable declarations
// orbitLine needs 'star' to be defined.
orbitLine = game.addChild(new OrbitLine(star.x, star.y, 300));
obstacles.push(orbitLine);
// Call to spawnInitialObstacles moved to earlier consolidated block
spawnInitialObstacles();
setupTitleScreen(); // Show title screen on game load
// Initialize the offscreen threshold for destroying obstacles
var offscreenThreshold = 2732 + 1000;
function createParticleEffect(x, y) {
var effect = new ParticleEffect(x, y);
LK.on('tick', function () {
effect._update_migrated();
});
}
function createDotParticleEffect(x, y) {
var effect = new DotParticleEffect(x, y);
LK.on('tick', function () {
effect._update_migrated();
});
}
function updateObstacles() {
var obstacleCount = 5 + LK.getScore();
while (obstacles.length < obstacleCount) {
var obstacle = game.addChild(new Obstacle());
obstacle.x = obstacle.direction === 1 ? -obstacle.width / 2 : 2048 + obstacle.width / 2;
obstacle.y = Math.random() * (2732 - obstacle.height) + obstacle.height / 2;
obstacles.push(obstacle);
}
}
// scoreTxt is now declared globally as 'var scoreTxt;'
// It is initialized and added to LK.gui.top within the 'startGamePlay' function.
// This removes the original duplicate initialization and addition.
game.on('down', function (x, y, obj) {
if (gameState === 'title') {
startGamePlay();
LK.getSound('bounce').play(); // Play a sound on game start
} else if (gameState === 'playing' && dot && !dot.destroyed) {
dot.bounceUp();
LK.getSound('bounce').play();
}
});
// Star creation and stars array population moved to earlier consolidated block
// OrbitLine creation and addition to obstacles moved to earlier consolidated block
function spawnInitialObstacles() {
var starX = 2048 / 2;
var starY = 2732 / 2 - 500;
var radius = 300;
var angleSpeed = 0.05;
for (var i = 0; i < 20; i++) {
var obstacle = game.addChild(new CircularObstacle(starX, starY, radius, angleSpeed));
obstacles.push(obstacle);
}
}
function triggerGameOver() {
if (gameState === 'gameOver') return; // Already processing game over
gameState = 'gameOver';
var currentScore = LK.getScore();
var highScore = storage.highScore || 0;
if (currentScore > highScore) {
storage.highScore = currentScore;
}
LK.showGameOver(); // This will reset the game instance
}
LK.on('tick', function () {
if (gameState !== 'playing') {
// Title screen animations (like blinking text) are handled by their own LK.setInterval.
// No main game logic runs if not in 'playing' state.
return;
}
// Ensure dot exists and is not destroyed before trying to update it.
// Game over logic should handle the transition away from 'playing' state.
if (!dot || dot.destroyed) {
return;
}
dot._update_migrated();
for (var i = 0; i < obstacles.length; i++) {
if (obstacles[i] instanceof CircularObstacle) {
obstacles[i].updatePosition();
} else {
if (typeof obstacles[i]._move_migrated === 'function') {
obstacles[i]._move_migrated();
}
}
if (dot.y < 2300 && dot.movedDistance < 0) {
obstacles[i].moveDown(-dot.movedDistance, dot.y);
}
if (dot.intersects(obstacles[i]) && !(obstacles[i] instanceof OrbitLine)) {
if (!dot.destroyed) {
createDotParticleEffect(dot.x, dot.y);
dot.destroyed = true;
LK.getSound('death').play(); // Play death sound
}
dot.destroy();
LK.setTimeout(function () {
triggerGameOver();
}, 1000);
} else if (obstacles[i].y > offscreenThreshold) {
obstacles[i].destroy();
obstacles.splice(i, 1);
}
}
for (var j = stars.length - 1; j >= 0; j--) {
stars[j].updateScale();
if (dot.intersects(stars[j])) {
LK.getSound('star').play(); // Play star sound
LK.setScore(LK.getScore() + 1);
scoreTxt.setText(LK.getScore().toString());
scoreTxt.alpha = 0;
var originalY = scoreTxt.y;
LK.on('tick', function updateScoreTextFadeIn() {
if (!scoreTxt || !scoreTxt.parent) {
// Defensive check for scoreTxt
LK.off('tick', updateScoreTextFadeIn);
return;
}
scoreTxt.alpha += 0.05;
if (scoreTxt.alpha >= 1) {
scoreTxt.alpha = 1; // Cap alpha at 1
scoreTxt.y = originalY; // Explicitly reset Y to its original position for this animation cycle
LK.off('tick', updateScoreTextFadeIn); // Remove this animation listener
} else {
// Update Y position only while the animation is in progress
scoreTxt.y = originalY - (1 - scoreTxt.alpha) * 50;
}
});
var oldStarY = stars[j].y;
createParticleEffect(stars[j].x, stars[j].y);
var scorePopup = new ScorePopup(stars[j].x, stars[j].y);
LK.on('tick', function () {
scorePopup._update_migrated();
});
stars[j].destroy();
stars.splice(j, 1);
var newStar = game.addChild(new Star());
newStar.x = 2048 / 2;
var additionalYOffset = LK.getScore() > 1 ? (LK.getScore() - 1) * 200 : 0;
newStar.y = oldStarY - 2000 - additionalYOffset;
stars.push(newStar);
if (!handMoved && hand) {
var handMoveDistance = 0;
var handMoveInterval = LK.setInterval(function () {
handMoveDistance += 1;
hand._update_migrated(handMoveDistance);
if (handMoveDistance >= 1000) {
LK.clearInterval(handMoveInterval);
}
}, 10);
handMoved = true;
}
// Increase the offscreen threshold for destroying obstacles
offscreenThreshold += 300;
// Add a cumulative number of CircularObstacles at random positions around the new star
var cumulativeObstacles = 1 + LK.getScore();
for (var k = 0; k < cumulativeObstacles; k++) {
var randomAngle = Math.random() * Math.PI * 2; // Random angle in radians
var radiusOffset = k * 100;
var circularObstacle = game.addChild(new CircularObstacle(newStar.x, newStar.y, 300 + radiusOffset, 0.05 * (k % 2 === 0 ? 1 : -1), randomAngle));
obstacles.push(circularObstacle);
}
// Add orbit line after creating all obstacles
var orbitLine = game.addChild(new OrbitLine(newStar.x, newStar.y, 300 + radiusOffset));
obstacles.push(orbitLine);
} else if (dot.y < 2300 && dot.movedDistance < 0) {
stars[j].moveDown(-dot.movedDistance, dot.y);
}
}
});
// Dot creation moved to earlier consolidated block
// Redundant 'hand' and 'handMoved' declarations removed.
// createHand() call and handMoved reset moved to earlier consolidated block.
// createHand function definition remains and is hoisted.
function createHand() {
hand = game.addChild(new Hand());
hand.x = dot.x;
hand.y = dot.y + dot.height / 2;
} /****
* Plugins
****/
var storage = LK.import("@upit/storage.v1");
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var CircularObstacle = Container.expand(function (starX, starY, radius, angleSpeed, initialAngle) {
var self = Container.call(this);
var obstacleGraphics = self.attachAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5
});
self.angle = initialAngle || 0;
self.radius = radius;
self.angleSpeed = angleSpeed * 0.75;
self.starX = starX;
self.starY = starY;
self.updatePosition = function () {
self.x = self.starX + Math.cos(self.angle) * self.radius;
self.y = self.starY + Math.sin(self.angle) * self.radius;
self.angle += self.angleSpeed * 0.35;
obstacleGraphics.rotation += self.angleSpeed * 0.35;
};
self.moveDown = function (distance, dotY) {
var speedMultiplier = dotY < 1200 ? 3 : dotY < 1400 ? 2 : dotY < 2000 ? 1.3 : 1.2;
self.starY += distance * speedMultiplier;
};
self.updatePosition();
});
var Dot = Container.expand(function () {
var self = Container.call(this);
var dotGraphics = self.attachAsset('dot', {
anchorX: 0.5,
anchorY: 0.5
});
self.destroyed = false;
self.velocityY = 0;
self.gravity = 0.7;
self.bounce = -15;
self._update_migrated = function () {
var previousY = self.y;
self.velocityY += self.gravity;
self.y += self.velocityY;
if (!self.firstTouch && !self.intersects(hand)) {
self.y = 2732 - self.height / 2 - 500;
} else if (self.intersects(hand)) {
self.bounceUp();
} else if (self.y > 2232 - self.height / 2 && self.firstTouch && !self.firstStarDestroyed) {
self.velocityY = self.bounce;
} else if (self.y > 2832 && !self.destroyed) {
createDotParticleEffect(self.x, self.y);
self.destroyed = true;
self.destroy();
LK.getSound('death').play(); // Play death sound
LK.setTimeout(function () {
triggerGameOver();
}, 1000);
}
self.movedDistance = self.y - previousY;
};
self.bounceUp = function () {
if (!self.destroyed) {
self.velocityY = self.bounce;
self.firstTouch = true;
if (!self.firstStarDestroyed) {
self.firstStarDestroyed = true;
}
}
};
});
var DotParticleEffect = Container.expand(function (x, y) {
var self = Container.call(this);
self.x = x;
self.y = y;
var particles = [];
var angleIncrement = Math.PI * 2 / 20;
var speed = 30;
for (var i = 0; i < 20; i++) {
var particle = self.attachAsset('dot', {
anchorX: 0.5,
anchorY: 0.5
});
particle.scaleX = particle.scaleY = 0.5;
particle.alpha = 1;
var angle = i * angleIncrement;
particle.vx = Math.cos(angle) * speed;
particle.vy = Math.sin(angle) * speed;
particles.push(particle);
}
self._update_migrated = function () {
for (var i = particles.length - 1; i >= 0; i--) {
particles[i].x += particles[i].vx;
particles[i].y += particles[i].vy;
particles[i].alpha -= 0.0167;
if (particles[i].alpha <= 0) {
particles[i].destroy();
particles.splice(i, 1);
}
}
if (particles.length === 0) {
self.destroy();
}
};
game.addChild(self);
});
// Add Hand asset below the dot
var Hand = Container.expand(function () {
var self = Container.call(this);
var handGraphics = self.attachAsset('hand', {
anchorX: 0.5,
anchorY: 0,
alpha: 0.9
});
self._update_migrated = function (distance) {
if (distance) {
self.y += distance;
}
};
});
var OrbitLine = Container.expand(function (starX, starY, radius) {
var self = Container.call(this);
self.starX = starX;
self.starY = starY;
self.radius = radius;
var segments = 50;
var angleIncrement = Math.PI * 2 / segments;
for (var i = 0; i < segments; i++) {
var angle = i * angleIncrement;
var dot = self.attachAsset('smallObstacle', {
x: starX + Math.cos(angle) * radius,
y: starY + Math.sin(angle) * radius,
anchorX: 0.5,
anchorY: 0.5
});
if (i % 2 === 0) {
dot.alpha = 0;
}
}
self.moveDown = function (distance, dotY) {
var speedMultiplier = dotY < 1200 ? 3 : dotY < 1400 ? 2 : dotY < 2000 ? 1.3 : dotY < 2300 ? 1.2 : 1;
self.y += distance * speedMultiplier;
};
});
var ParticleEffect = Container.expand(function (x, y) {
var self = Container.call(this);
self.x = x;
self.y = y;
var particles = [];
for (var i = 0; i < 20; i++) {
var particle = self.attachAsset('star', {
anchorX: 0.5,
anchorY: 0.5
});
particle.scaleX = particle.scaleY = Math.random() * 0.5 + 0.5;
particle.alpha = 0.7;
particle.vx = (Math.random() - 0.5) * 10;
particle.vy = (Math.random() - 0.5) * 10;
particles.push(particle);
}
self._update_migrated = function () {
for (var i = particles.length - 1; i >= 0; i--) {
particles[i].x += particles[i].vx;
particles[i].y += particles[i].vy;
particles[i].alpha -= 0.02;
if (particles[i].alpha <= 0) {
particles[i].destroy();
particles.splice(i, 1);
}
}
if (particles.length === 0) {
self.destroy();
}
};
game.addChild(self);
});
var ScorePopup = Container.expand(function (x, y) {
var self = Container.call(this);
self.x = x;
self.y = y;
var scoreText = new Text2('+1', {
size: 100,
fill: 0xFFFFFF,
anchorX: 0.5,
anchorY: 0.5
});
scoreText.x = -scoreText.width / 2;
scoreText.y = -scoreText.height / 2;
self.addChild(scoreText);
self.alpha = 1;
var duration = 120;
self._update_migrated = function () {
duration--;
self.y -= 2;
self.alpha -= 1 / 120;
if (duration <= 0) {
self.destroy();
}
};
game.addChild(self);
});
var Star = Container.expand(function () {
var self = Container.call(this);
var starGraphics = self.attachAsset('star', {
anchorX: 0.5,
anchorY: 0.5
});
self.scaleDirection = 1;
self.scaleSpeed = 0.005;
self.minScale = 1;
self.maxScale = 1.2;
self.moveDown = function (distance, dotY) {
var speedMultiplier = dotY < 1200 ? 3 : dotY < 1400 ? 2 : dotY < 2000 ? 1.3 : 1.2;
self.y += distance * speedMultiplier;
if (self.y > 2732 - self.height / 2) {
self.y = -self.height / 2;
}
};
self.updateScale = function () {
if (self.scaleDirection === 1 && starGraphics.scale.x < self.maxScale) {
starGraphics.scale.x += self.scaleSpeed;
starGraphics.scale.y += self.scaleSpeed;
} else if (self.scaleDirection === -1 && starGraphics.scale.x > self.minScale) {
starGraphics.scale.x -= self.scaleSpeed;
starGraphics.scale.y -= self.scaleSpeed;
}
if (starGraphics.scale.x >= self.maxScale || starGraphics.scale.x <= self.minScale) {
self.scaleDirection *= -1;
}
};
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
LK.playMusic('Backgroundmusic');
var gameState = 'title'; // 'title', 'playing', 'gameOver'
var gameTitleAsset;
var highScoreTextDisplay;
var tapToStartText;
var blinkIntervalId = null; // For "Tap to Start" blinking
// Game elements that will be initialized later or need visibility control
var dot;
var stars = [];
var obstacles = [];
var orbitLine;
var hand;
var handMoved = false;
var titleDot; // Declare titleDot here
var scoreTxt; // Declare scoreTxt here so it's globally accessible for title/game states
var highScoreGameTxt; // New: high score text for in-game display
var menuBackground; // For managing the title screen background overlay
// Function to setup the title screen
function setupTitleScreen() {
gameState = 'title';
LK.setScore(0); // Reset score for the title screen phase
// Add a black background overlay that covers the entire game area
menuBackground = LK.getAsset('smallObstacle', {
anchorX: 0.5,
anchorY: 0.5,
width: 2048,
height: 2732,
tint: 0x000000,
alpha: 1.0
});
menuBackground.width = 2048;
menuBackground.height = 2732;
menuBackground.x = 2048 / 2;
menuBackground.y = 2732 / 2;
game.addChild(menuBackground);
gameTitleAsset = game.addChild(LK.getAsset('gameTitle', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 3.5 // Position title a bit up
}));
// Add a large dot below the game title
titleDot = game.addChild(LK.getAsset('dot', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: gameTitleAsset.y + gameTitleAsset.height / 2 + 600,
// Position below the title, moved 400px down
// Position below the title
scaleX: 4,
// Make it bigger
scaleY: 4 // Make it bigger
}));
// Add a soft up and down bounce animation to the titleDot that always returns to the same position
(function bounceTitleDot() {
// Defensive: Only animate if titleDot exists, is not null, and is not destroyed
if (!(gameState === 'title' && titleDot && titleDot.parent && typeof titleDot.y === 'number')) return;
var originalY = titleDot.y;
// Always tween from originalY to originalY-60 and back, so the dot never drifts
tween(titleDot, {
y: originalY - 60
}, {
duration: 700,
easing: tween.sineInOut,
onFinish: function onFinish() {
// Defensive: Check again before next tween
if (!(gameState === 'title' && titleDot && titleDot.parent && typeof titleDot.y === 'number')) return;
tween(titleDot, {
y: originalY
}, {
duration: 700,
easing: tween.sineInOut,
onFinish: function onFinish() {
// Loop the bounce as long as we're on the title screen and titleDot is valid
bounceTitleDot();
}
});
}
});
})();
// Remove "TAP TO START" text from inside the titleDot (do not add it to the dot)
// Center "Tap to Start" horizontally and place it above the high score (if present), otherwise below the title
tapToStartText = new Text2('TAP TO START', {
size: 120,
fill: '#FFFFFF',
// White text
stroke: '#000000',
// Black outline for contrast
strokeThickness: 8,
alpha: 0 // Start invisible for soft fade animation
// anchorX and anchorY will be set via .anchor.set() for precise centering
});
tapToStartText.anchor.set(0.5, 0.5);
tapToStartText.x = 2048 / 2;
var tapTextYPosition;
if (highScoreTextDisplay && highScoreTextDisplay.parent) {
// Place "Tap to Start" above the high score
tapTextYPosition = highScoreTextDisplay.y - highScoreTextDisplay.height / 2 - 100;
} else if (gameTitleAsset && gameTitleAsset.parent) {
tapTextYPosition = gameTitleAsset.y + gameTitleAsset.height / 2 + 150;
} else {
tapTextYPosition = 2732 / 2 + 100; // Fallback y position
}
tapToStartText.y = tapTextYPosition + 1000;
game.addChild(tapToStartText);
if (blinkIntervalId) LK.clearInterval(blinkIntervalId);
// Soft fade in/out animation using tween
var fadeIn = true;
function animateTapToStart() {
if (!(gameState === 'title' && tapToStartText && tapToStartText.parent)) {
if (blinkIntervalId) {
LK.clearInterval(blinkIntervalId);
blinkIntervalId = null;
}
return;
}
tween(tapToStartText, {
alpha: fadeIn ? 1 : 0.3
}, {
duration: 700,
onFinish: function onFinish() {
fadeIn = !fadeIn;
animateTapToStart();
}
});
}
animateTapToStart();
if (scoreTxt && scoreTxt.parent) {
scoreTxt.visible = false;
}
// Hide all game elements when title screen is up - ensure they're properly hidden
if (dot) {
dot.visible = false;
}
stars.forEach(function (s) {
if (s) s.visible = false;
});
obstacles.forEach(function (o) {
if (o) o.visible = false;
});
if (orbitLine) orbitLine.visible = false;
if (hand) hand.visible = false;
}
// Function to initialize and start the actual game
function startGamePlay() {
if (gameState !== 'title') return;
// gameState = 'playing'; // Moved into completeGameSetupAfterFade
if (blinkIntervalId) {
LK.clearInterval(blinkIntervalId);
blinkIntervalId = null;
}
var elementsToFadeOut = [];
if (gameTitleAsset && gameTitleAsset.parent) elementsToFadeOut.push(gameTitleAsset);
if (highScoreTextDisplay && highScoreTextDisplay.parent) elementsToFadeOut.push(highScoreTextDisplay);
if (tapToStartText && tapToStartText.parent) elementsToFadeOut.push(tapToStartText);
if (menuBackground && menuBackground.parent) elementsToFadeOut.push(menuBackground);
if (titleDot && titleDot.parent) elementsToFadeOut.push(titleDot);
var fadeDuration = 500; // Duration of the fade in milliseconds
var tweensCompletedCount = 0;
function completeGameSetupAfterFade() {
// Destroy elements now that they are faded and nullify global references
if (gameTitleAsset && gameTitleAsset.parent) gameTitleAsset.destroy();
gameTitleAsset = null;
if (highScoreTextDisplay && highScoreTextDisplay.parent) highScoreTextDisplay.destroy();
highScoreTextDisplay = null;
if (tapToStartText && tapToStartText.parent) tapToStartText.destroy();
tapToStartText = null;
if (menuBackground && menuBackground.parent) {
menuBackground.destroy();
menuBackground = null;
}
if (titleDot && titleDot.parent) {
titleDot.destroy();
}
titleDot = null; // Nullify titleDot as well for good practice
// This is the logic that was originally AFTER the destroyed elements
if (!scoreTxt || !scoreTxt.parent) {
if (scoreTxt) scoreTxt.destroy(); // Ensure old one is gone if somehow exists
scoreTxt = new Text2('0', {
size: 120,
// Adjusted size for top-right placement
fill: 0xFFFFFF
}); //{2R} // Line identifier preserved
scoreTxt.anchor.set(1, 0); // Anchor to top-right of the text
scoreTxt.x = -20; // Position 20px from the right edge of the gui.topRight container
scoreTxt.y = 20; // Position 20px from the top edge of the gui.topRight container
LK.gui.topRight.addChild(scoreTxt);
}
scoreTxt.setText('0');
scoreTxt.alpha = 0; // Prepare scoreTxt for fade-in
scoreTxt.visible = true;
// Add high score below the score
if (highScoreGameTxt && highScoreGameTxt.parent) {
highScoreGameTxt.destroy();
highScoreGameTxt = null;
}
var currentHighScore = storage.highScore || 0;
if (currentHighScore > 0) {
highScoreGameTxt = new Text2('BEST: ' + currentHighScore, {
size: 40,
// Reduced size for the 'BEST' text
// Adjusted size for better visibility below score
fill: '#FFFFFF'
}); //{3i} // Preserving line identifier for the Text2 options object
highScoreGameTxt.anchor.set(1, 0); // Anchor to its top-right
highScoreGameTxt.x = -20; // Align with scoreTxt's x-position (20px from right edge of gui.topRight)
// Position highScoreGameTxt directly below scoreTxt
if (scoreTxt && scoreTxt.parent) {
// Ensure scoreTxt is available
highScoreGameTxt.y = scoreTxt.y + scoreTxt.height + 10; // Position 10px below scoreTxt
} else {
// Fallback if scoreTxt isn't ready (scoreTxt.y is 20, assuming its height ~120)
highScoreGameTxt.y = 20 + 120 + 10;
}
LK.gui.topRight.addChild(highScoreGameTxt); // Add to top right GUI container
highScoreGameTxt.alpha = 0; // Prepare for fade-in, similar to scoreTxt
}
LK.setScore(0);
initializeGameElements();
gameState = 'playing'; // Set gameState to 'playing' only after all setup is complete
}
if (elementsToFadeOut.length === 0) {
completeGameSetupAfterFade(); // No elements to fade, proceed directly
} else {
elementsToFadeOut.forEach(function (element) {
tween(element, {
alpha: 0
}, {
duration: fadeDuration,
onFinish: function onFinish() {
tweensCompletedCount++;
if (tweensCompletedCount === elementsToFadeOut.length) {
completeGameSetupAfterFade();
}
}
});
});
}
}
// Function to initialize and fade in game elements
function initializeGameElements() {
var elementsToFadeIn = [];
var fadeDuration = 500; // Duration for game elements fade-in in milliseconds
// Collect scoreTxt if it's ready (alpha is preset by completeGameSetupAfterFade)
if (scoreTxt && scoreTxt.parent) {
elementsToFadeIn.push(scoreTxt);
}
// Collect highScoreGameTxt if it exists and is part of the GUI
if (highScoreGameTxt && highScoreGameTxt.parent) {
elementsToFadeIn.push(highScoreGameTxt);
}
// Collect dot
if (dot) {
elementsToFadeIn.push(dot);
}
// Collect stars
stars.forEach(function (s) {
if (s) {
elementsToFadeIn.push(s);
}
});
// Collect obstacles (this includes the initial orbitLine and CircularObstacles)
obstacles.forEach(function (o) {
if (o) {
elementsToFadeIn.push(o);
}
});
// Collect hand
if (hand) {
elementsToFadeIn.push(hand);
}
if (elementsToFadeIn.length === 0) {
return; // No elements to fade
}
// Set initial properties and start tween for each element
elementsToFadeIn.forEach(function (element) {
// For elements other than scoreTxt, set initial alpha.
// scoreTxt's alpha is set to 0 by the calling function completeGameSetupAfterFade.
if (element !== scoreTxt) {
element.alpha = 0;
}
element.visible = true; // Ensure element is visible for alpha tween to have an effect
tween(element, {
// Tween to fade in
alpha: 1
}, {
duration: fadeDuration
// No specific onFinish needed here as gameState is handled by the caller
});
});
}
// Initialize game elements before setting up the title screen.
// This ensures they exist for the hiding logic in setupTitleScreen and are layered correctly.
// Dot (needed for Hand positioning)
dot = game.addChild(new Dot());
dot.x = 2048 / 2;
dot.y = 2732 - dot.height / 2 - 200;
// Stars
// global 'stars' array is already initialized as [] in variable declarations
star = game.addChild(new Star());
star.x = 2048 / 2;
star.y = 2732 / 2 - 500;
stars.push(star);
// Hand
// Global 'hand' is assigned within createHand. createHand function definition is hoisted.
// Global 'handMoved' is initialized at declaration; ensure it's correctly 'false' for a new game start.
createHand();
handMoved = false; // Explicitly set for game start logic
// Obstacles and OrbitLine
// global 'obstacles' array is already initialized as [] in variable declarations
// orbitLine needs 'star' to be defined.
orbitLine = game.addChild(new OrbitLine(star.x, star.y, 300));
obstacles.push(orbitLine);
// Call to spawnInitialObstacles moved to earlier consolidated block
spawnInitialObstacles();
setupTitleScreen(); // Show title screen on game load
// Initialize the offscreen threshold for destroying obstacles
var offscreenThreshold = 2732 + 1000;
function createParticleEffect(x, y) {
var effect = new ParticleEffect(x, y);
LK.on('tick', function () {
effect._update_migrated();
});
}
function createDotParticleEffect(x, y) {
var effect = new DotParticleEffect(x, y);
LK.on('tick', function () {
effect._update_migrated();
});
}
function updateObstacles() {
var obstacleCount = 5 + LK.getScore();
while (obstacles.length < obstacleCount) {
var obstacle = game.addChild(new Obstacle());
obstacle.x = obstacle.direction === 1 ? -obstacle.width / 2 : 2048 + obstacle.width / 2;
obstacle.y = Math.random() * (2732 - obstacle.height) + obstacle.height / 2;
obstacles.push(obstacle);
}
}
// scoreTxt is now declared globally as 'var scoreTxt;'
// It is initialized and added to LK.gui.top within the 'startGamePlay' function.
// This removes the original duplicate initialization and addition.
game.on('down', function (x, y, obj) {
if (gameState === 'title') {
startGamePlay();
LK.getSound('bounce').play(); // Play a sound on game start
} else if (gameState === 'playing' && dot && !dot.destroyed) {
dot.bounceUp();
LK.getSound('bounce').play();
}
});
// Star creation and stars array population moved to earlier consolidated block
// OrbitLine creation and addition to obstacles moved to earlier consolidated block
function spawnInitialObstacles() {
var starX = 2048 / 2;
var starY = 2732 / 2 - 500;
var radius = 300;
var angleSpeed = 0.05;
for (var i = 0; i < 20; i++) {
var obstacle = game.addChild(new CircularObstacle(starX, starY, radius, angleSpeed));
obstacles.push(obstacle);
}
}
function triggerGameOver() {
if (gameState === 'gameOver') return; // Already processing game over
gameState = 'gameOver';
var currentScore = LK.getScore();
var highScore = storage.highScore || 0;
if (currentScore > highScore) {
storage.highScore = currentScore;
}
LK.showGameOver(); // This will reset the game instance
}
LK.on('tick', function () {
if (gameState !== 'playing') {
// Title screen animations (like blinking text) are handled by their own LK.setInterval.
// No main game logic runs if not in 'playing' state.
return;
}
// Ensure dot exists and is not destroyed before trying to update it.
// Game over logic should handle the transition away from 'playing' state.
if (!dot || dot.destroyed) {
return;
}
dot._update_migrated();
for (var i = 0; i < obstacles.length; i++) {
if (obstacles[i] instanceof CircularObstacle) {
obstacles[i].updatePosition();
} else {
if (typeof obstacles[i]._move_migrated === 'function') {
obstacles[i]._move_migrated();
}
}
if (dot.y < 2300 && dot.movedDistance < 0) {
obstacles[i].moveDown(-dot.movedDistance, dot.y);
}
if (dot.intersects(obstacles[i]) && !(obstacles[i] instanceof OrbitLine)) {
if (!dot.destroyed) {
createDotParticleEffect(dot.x, dot.y);
dot.destroyed = true;
LK.getSound('death').play(); // Play death sound
}
dot.destroy();
LK.setTimeout(function () {
triggerGameOver();
}, 1000);
} else if (obstacles[i].y > offscreenThreshold) {
obstacles[i].destroy();
obstacles.splice(i, 1);
}
}
for (var j = stars.length - 1; j >= 0; j--) {
stars[j].updateScale();
if (dot.intersects(stars[j])) {
LK.getSound('star').play(); // Play star sound
LK.setScore(LK.getScore() + 1);
scoreTxt.setText(LK.getScore().toString());
scoreTxt.alpha = 0;
var originalY = scoreTxt.y;
LK.on('tick', function updateScoreTextFadeIn() {
if (!scoreTxt || !scoreTxt.parent) {
// Defensive check for scoreTxt
LK.off('tick', updateScoreTextFadeIn);
return;
}
scoreTxt.alpha += 0.05;
if (scoreTxt.alpha >= 1) {
scoreTxt.alpha = 1; // Cap alpha at 1
scoreTxt.y = originalY; // Explicitly reset Y to its original position for this animation cycle
LK.off('tick', updateScoreTextFadeIn); // Remove this animation listener
} else {
// Update Y position only while the animation is in progress
scoreTxt.y = originalY - (1 - scoreTxt.alpha) * 50;
}
});
var oldStarY = stars[j].y;
createParticleEffect(stars[j].x, stars[j].y);
var scorePopup = new ScorePopup(stars[j].x, stars[j].y);
LK.on('tick', function () {
scorePopup._update_migrated();
});
stars[j].destroy();
stars.splice(j, 1);
var newStar = game.addChild(new Star());
newStar.x = 2048 / 2;
var additionalYOffset = LK.getScore() > 1 ? (LK.getScore() - 1) * 200 : 0;
newStar.y = oldStarY - 2000 - additionalYOffset;
stars.push(newStar);
if (!handMoved && hand) {
var handMoveDistance = 0;
var handMoveInterval = LK.setInterval(function () {
handMoveDistance += 1;
hand._update_migrated(handMoveDistance);
if (handMoveDistance >= 1000) {
LK.clearInterval(handMoveInterval);
}
}, 10);
handMoved = true;
}
// Increase the offscreen threshold for destroying obstacles
offscreenThreshold += 300;
// Add a cumulative number of CircularObstacles at random positions around the new star
var cumulativeObstacles = 1 + LK.getScore();
for (var k = 0; k < cumulativeObstacles; k++) {
var randomAngle = Math.random() * Math.PI * 2; // Random angle in radians
var radiusOffset = k * 100;
var circularObstacle = game.addChild(new CircularObstacle(newStar.x, newStar.y, 300 + radiusOffset, 0.05 * (k % 2 === 0 ? 1 : -1), randomAngle));
obstacles.push(circularObstacle);
}
// Add orbit line after creating all obstacles
var orbitLine = game.addChild(new OrbitLine(newStar.x, newStar.y, 300 + radiusOffset));
obstacles.push(orbitLine);
} else if (dot.y < 2300 && dot.movedDistance < 0) {
stars[j].moveDown(-dot.movedDistance, dot.y);
}
}
});
// Dot creation moved to earlier consolidated block
// Redundant 'hand' and 'handMoved' declarations removed.
// createHand() call and handMoved reset moved to earlier consolidated block.
// createHand function definition remains and is hoisted.
function createHand() {
hand = game.addChild(new Hand());
hand.x = dot.x;
hand.y = dot.y + dot.height / 2;
}