Code edit (1 edits merged)
Please save this source code
Code edit (1 edits merged)
Please save this source code
User prompt
Update as needed with: function spawnNotesForCurrentTime(currentTime) { if (!currentSongData) { return; } currentSongData.notes.forEach(function (noteData, index) { var noteKey = index + '_' + noteData.time; var alreadySpawned = false; for (var i = 0; i < spawnedNotes.length; i++) { if (spawnedNotes[i] === noteKey) { alreadySpawned = true; break; } } if (!alreadySpawned && currentTime >= noteData.time - NOTE_SPAWN_AHEAD_MS && currentTime <= noteData.time - NOTE_SPAWN_AHEAD_MS + 100) { var calculatedY = calculateNotePosition(noteData.time); // Calculate end note Y position for hold notes var endNoteY; if (noteData.type === 'hold' && noteData.duration > 0) { var endTime = noteData.time + noteData.duration; endNoteY = calculateNotePosition(endTime); } var calculatedX = calculateZoneX(noteData.zone, calculatedY, endNoteY); // Check for overlapping notes and adjust X position if needed var attempts = 0; var maxAttempts = 10; var noteRadius = 150; // Approximate radius to avoid overlap while (attempts < maxAttempts) { var hasOverlap = false; // Check against existing active notes for (var j = 0; j < notes.length; j++) { var existingNote = notes[j]; if (!existingNote || !existingNote.active) continue; var distance = Math.sqrt( Math.pow(calculatedX - existingNote.x, 2) + Math.pow(calculatedY - existingNote.y, 2) ); if (distance < noteRadius) { hasOverlap = true; break; } } if (!hasOverlap) { break; // Good position found } // Try a new X position within the same zone calculatedX = calculateZoneX(noteData.zone, calculatedY, endNoteY); attempts++; } var processedNoteData = { time: noteData.time, type: noteData.type, x: calculatedX, y: calculatedY, color: noteData.color, duration: noteData.duration }; var note = new Note(processedNoteData, currentTime); notes.push(note); game.addChild(note); note.spawnIn(); spawnedNotes.push(noteKey); } }); } ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Update as needed with: function calculateZoneX(zone, noteY, endNoteY) { var sideMargin = GAME_WIDTH * SAFETY_MARGIN_SIDES_PERCENT; var randomSpan = GAME_WIDTH / 2 - 2 * sideMargin; if (randomSpan < 0) { randomSpan = 0; } // Check if note or its end note is in the bottom 15% of the play area var isInBottomArea = noteY > SCANNER_Y_MAX - PLAY_AREA_HEIGHT * 0.15; if (endNoteY !== undefined) { isInBottomArea = isInBottomArea || endNoteY > SCANNER_Y_MAX - PLAY_AREA_HEIGHT * 0.15; } var xPosition; if (zone === 'left') { xPosition = sideMargin; if (isInBottomArea) { // In bottom area, favor the right side of the left zone (more central) // Use the right 60% of the left zone's available space var centralSpan = randomSpan * 0.6; var offset = randomSpan * 0.4; // Skip the leftmost 40% xPosition += offset + Math.random() * centralSpan; } else { // Normal positioning for upper areas xPosition += Math.random() * randomSpan; } } else { xPosition = GAME_WIDTH / 2 + sideMargin; if (isInBottomArea) { // In bottom area, favor the left side of the right zone (more central) // Use the left 60% of the right zone's available space var centralSpan = randomSpan * 0.6; xPosition += Math.random() * centralSpan; } else { // Normal positioning for upper areas xPosition += Math.random() * randomSpan; } } return xPosition; } ``` And you’ll need to update the call to `calculateZoneX` in the `spawnNotesForCurrentTime` function: ```javascript function spawnNotesForCurrentTime(currentTime) { if (!currentSongData) { return; } currentSongData.notes.forEach(function (noteData, index) { var noteKey = index + '_' + noteData.time; var alreadySpawned = false; for (var i = 0; i < spawnedNotes.length; i++) { if (spawnedNotes[i] === noteKey) { alreadySpawned = true; break; } } if (!alreadySpawned && currentTime >= noteData.time - NOTE_SPAWN_AHEAD_MS && currentTime <= noteData.time - NOTE_SPAWN_AHEAD_MS + 100) { var calculatedY = calculateNotePosition(noteData.time); // Calculate end note Y position for hold notes var endNoteY; if (noteData.type === 'hold' && noteData.duration > 0) { var endTime = noteData.time + noteData.duration; endNoteY = calculateNotePosition(endTime); } var calculatedX = calculateZoneX(noteData.zone, calculatedY, endNoteY); var processedNoteData = { time: noteData.time, type: noteData.type, x: calculatedX, y: calculatedY, color: noteData.color, duration: noteData.duration }; var note = new Note(processedNoteData, currentTime); notes.push(note); game.addChild(note); note.spawnIn(); spawnedNotes.push(noteKey); } }); } ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Update with: self.spawnIn = function () { self.isSpawning = true; tween(self, { alpha: 1, scaleX: 1.3, // Overshoot the target scale scaleY: 1.3 }, { duration: 200, // Much faster initial pop easing: tween.easeOut, onFinish: function onFinish() { // Then settle back to normal size with a bounce tween(self, { scaleX: 1, scaleY: 1 }, { duration: 300, easing: tween.easeElasticOut, // This creates the settling bounce onFinish: function onFinish() { self.isSpawning = false; self._initiateBeatPulseLoop(); } }); } }); }; ↪💡 Consider importing and using the following plugins: @upit/tween.v1
Code edit (2 edits merged)
Please save this source code
User prompt
Update with: self.spawnIn = function () { self.isSpawning = true; tween(self, { alpha: 1, scaleX: 1, scaleY: 1 }, { duration: 800, // Increased duration for better elastic effect easing: tween.easeElasticOut, // Changed to elastic easing for bouncy appearance onFinish: function onFinish() { self.isSpawning = false; self._initiateBeatPulseLoop(); } }); }; ``` And modify the `showMissEffect` method to have a collapsing animation: ```javascript self.showMissEffect = function () { self._clearBeatPulseLoop(); LK.getSound('missSound').play(); // First change color to red tween(self.noteGraphics, { tint: 0xff0000 }, { duration: 150, onFinish: function onFinish() { // Then collapse inward with elastic ease-in for a crushing effect tween(self, { alpha: 0, scaleX: 0, scaleY: 0 }, { duration: 500, // Slightly longer for dramatic effect easing: tween.easeElasticIn, // Elastic ease-in for collapsing effect onFinish: function onFinish() { self.active = false; self._clearBeatPulseLoop(); } }); } }); }; ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Instead of just using notecore asset for background particle. Add small random chance to use supernova core particle, stellar debris and nebula. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Update with: self.updateParticle = function () { // Get current direction (smoothly interpolated if changing) var currentDirection = getCurrentParticleDirection(); // Calculate total speed (base * game speed * countdown acceleration) var totalSpeed = self.baseSpeed * baseParticleSpeed * (isCountdownActive ? countdownAcceleration : 1); // Move in the current direction - simple linear movement self.x += currentDirection.x * totalSpeed; self.y += currentDirection.y * totalSpeed; // Reset particle if it goes off screen - improved distribution logic var margin = 100; if (self.x < -margin || self.x > GAME_WIDTH + margin || self.y < -margin || self.y > GAME_HEIGHT + margin) { // Use weighted probability to spawn on different edges // 60% opposite edge, 40% distributed across other edges var spawnChoice = Math.random(); if (spawnChoice < 0.6) { // 60% - Spawn on opposite edge (current behavior) if (Math.abs(currentDirection.x) > Math.abs(currentDirection.y)) { if (currentDirection.x > 0) { self.x = -margin + Math.random() * 50; self.y = Math.random() * GAME_HEIGHT; } else { self.x = GAME_WIDTH + margin - Math.random() * 50; self.y = Math.random() * GAME_HEIGHT; } } else { if (currentDirection.y > 0) { self.x = Math.random() * GAME_WIDTH; self.y = -margin + Math.random() * 50; } else { self.x = Math.random() * GAME_WIDTH; self.y = GAME_HEIGHT + margin - Math.random() * 50; } } } else { // 40% - Spawn randomly on any edge for better distribution var edge = Math.floor(Math.random() * 4); switch(edge) { case 0: // Top edge self.x = Math.random() * GAME_WIDTH; self.y = -margin + Math.random() * 50; break; case 1: // Right edge self.x = GAME_WIDTH + margin - Math.random() * 50; self.y = Math.random() * GAME_HEIGHT; break; case 2: // Bottom edge self.x = Math.random() * GAME_WIDTH; self.y = GAME_HEIGHT + margin - Math.random() * 50; break; case 3: // Left edge self.x = -margin + Math.random() * 50; self.y = Math.random() * GAME_HEIGHT; break; } } } }; ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Update with: function changeParticleDirection() { if (isChangingDirection) return; // Calculate the turn direction var currentAngle = Math.atan2(globalParticleDirection.y, globalParticleDirection.x); var angleChange = (Math.PI / 4) + Math.random() * (Math.PI / 4); // 45-90 degrees if (Math.random() < 0.5) angleChange = -angleChange; var newAngle = currentAngle + angleChange; targetParticleDirection.x = Math.cos(newAngle); targetParticleDirection.y = Math.sin(newAngle); // Predict which corner will become bare and pre-fill it var turnDirection = angleChange; // Positive = turning counter-clockwise, Negative = turning clockwise // Determine which corner will be "left behind" by the turn var bareCornerX, bareCornerY; var currentDir = globalParticleDirection; var newDir = targetParticleDirection; // The bare corner is opposite to where particles are flowing after the turn if (newDir.x > 0 && newDir.y > 0) { // Flowing toward bottom-right, so top-left will be bare bareCornerX = 0; bareCornerY = 0; } else if (newDir.x < 0 && newDir.y > 0) { // Flowing toward bottom-left, so top-right will be bare bareCornerX = GAME_WIDTH; bareCornerY = 0; } else if (newDir.x < 0 && newDir.y < 0) { // Flowing toward top-left, so bottom-right will be bare bareCornerX = GAME_WIDTH; bareCornerY = GAME_HEIGHT; } else { // Flowing toward top-right, so bottom-left will be bare bareCornerX = 0; bareCornerY = GAME_HEIGHT; } // Spawn particles in the predicted bare corner area var cornerSize = Math.min(GAME_WIDTH, GAME_HEIGHT) * 0.3; // 30% of screen from corner for (var i = 0; i < 15; i++) { var extraParticle = new BackgroundParticle(); // Position in the bare corner area if (bareCornerX === 0) { extraParticle.x = Math.random() * cornerSize; } else { extraParticle.x = GAME_WIDTH - Math.random() * cornerSize; } if (bareCornerY === 0) { extraParticle.y = Math.random() * cornerSize; } else { extraParticle.y = GAME_HEIGHT - Math.random() * cornerSize; } backgroundParticles.push(extraParticle); game.addChildAt(extraParticle, 0); } isChangingDirection = true; directionChangeStartTime = Date.now(); } ↪💡 Consider importing and using the following plugins: @upit/tween.v1
Code edit (3 edits merged)
Please save this source code
User prompt
When notes scale in use elastic easing for a rubber like spawning. Instead of just fading out when missed, reverse the spawn animation. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Update as needed with: var BackgroundParticle = Container.expand(function () { var self = Container.call(this); self.particleGraphics = null; self.baseSpeed = 0; self.initialScale = 1; self.baseAlpha = 0.1; self._startTwinkleAnimation = function () { tween.stop(self, { alpha: true }); var currentAlpha = self.alpha; var pulseDelta = self.baseAlpha * 0.4; var targetMinAlpha = Math.max(0.05, self.baseAlpha - pulseDelta); var targetMaxAlpha = Math.min(1.0, self.baseAlpha + pulseDelta); if (targetMaxAlpha - targetMinAlpha < 0.05) { if (self.baseAlpha < 0.15) { targetMaxAlpha = Math.min(1.0, self.baseAlpha + 0.1); } else if (self.baseAlpha > 0.85) { targetMinAlpha = Math.max(0.05, self.baseAlpha - 0.1); } if (targetMaxAlpha - targetMinAlpha < 0.05) { return; } } var nextTargetAlpha; if (self.alpha === self.baseAlpha || Math.abs(currentAlpha - targetMaxAlpha) < Math.abs(currentAlpha - targetMinAlpha)) { nextTargetAlpha = targetMinAlpha; } else { nextTargetAlpha = targetMaxAlpha; } var pulseDuration = 300 + Math.random() * 400; tween(self, { alpha: nextTargetAlpha }, { duration: pulseDuration, easing: tween.easeInOut, onFinish: function onFinish() { if (self && self._startTwinkleAnimation) { self._startTwinkleAnimation(); } } }); }; self.init = function () { tween.stop(self, { alpha: true }); var scale = 0.312 + Math.random() * 1.248; self.initialScale = scale; self.scale.set(scale); var colors = [0xff00ff, 0x00ffff, 0xffff00, 0xff0000, 0x00ff00, 0x0000ff, 0xffa500, 0xda70d6]; var color = colors[Math.floor(Math.random() * colors.length)]; if (self.particleGraphics) { self.particleGraphics.destroy(); } self.particleGraphics = self.attachAsset('starParticle', { anchorX: 0.5, anchorY: 0.5 }); self.particleGraphics.tint = color; self.baseSpeed = (0.5 + Math.random() * 1.5) * scale; // Distribute particles evenly across the screen self.x = Math.random() * GAME_WIDTH; self.y = Math.random() * GAME_HEIGHT; // Start particles throughout the screen self.baseAlpha = 0.1 + Math.random() * 0.7; self.alpha = self.baseAlpha; self._startTwinkleAnimation(); }; self.updateParticle = function () { // Get current direction (smoothly interpolated if changing) var currentDirection = getCurrentParticleDirection(); // Calculate total speed (base * game speed * countdown acceleration) var totalSpeed = self.baseSpeed * baseParticleSpeed * (isCountdownActive ? countdownAcceleration : 1); // Move in the current direction - simple linear movement self.x += currentDirection.x * totalSpeed; self.y += currentDirection.y * totalSpeed; // Reset particle if it goes off screen - improved logic var margin = 100; if (self.x < -margin || self.x > GAME_WIDTH + margin || self.y < -margin || self.y > GAME_HEIGHT + margin) { // Respawn from the opposite side based on movement direction // and distribute evenly across that edge if (Math.abs(currentDirection.x) > Math.abs(currentDirection.y)) { // Primarily horizontal movement if (currentDirection.x > 0) { // Moving right, spawn from left edge self.x = -margin + Math.random() * 50; self.y = Math.random() * GAME_HEIGHT; } else { // Moving left, spawn from right edge self.x = GAME_WIDTH + margin - Math.random() * 50; self.y = Math.random() * GAME_HEIGHT; } } else { // Primarily vertical movement if (currentDirection.y > 0) { // Moving down, spawn from top edge self.x = Math.random() * GAME_WIDTH; self.y = -margin + Math.random() * 50; } else { // Moving up, spawn from bottom edge self.x = Math.random() * GAME_WIDTH; self.y = GAME_HEIGHT + margin - Math.random() * 50; } } } }; self.init(); return self; }); ``` Also, let’s update the setupGame function to ensure particles are properly distributed when starting a new game: ```javascript function setupGame(songName) { // ... existing code before particle setup ... // Reset particle speed for new game baseParticleSpeed = 1; isCountdownActive = false; countdownAcceleration = 1; // Reset particle direction to downward globalParticleDirection = { x: 0, y: 1 }; targetParticleDirection = { x: 0, y: 1 }; isChangingDirection = false; lastDirectionChangeTime = Date.now(); nextDirectionChangeDelay = 15000 + Math.random() * 15000; // Reinitialize particles for proper distribution if (backgroundParticles.length === 0) { for (var i = 0; i < NUM_BACKGROUND_PARTICLES; i++) { var particle = new BackgroundParticle(); // Let init() handle the positioning backgroundParticles.push(particle); game.addChildAt(particle, 0); } } else { // Reinitialize existing particles for new distribution backgroundParticles.forEach(function (p) { p.init(); }); } ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Update as needed with: // Add these global variables (replace the previous ones) var globalParticleDirection = { x: 0, y: 1 }; // Start moving downward var targetParticleDirection = { x: 0, y: 1 }; var directionChangeStartTime = 0; var directionChangeDuration = 1000; // 1 second var isChangingDirection = false; var lastDirectionChangeTime = 0; var nextDirectionChangeDelay = 15000 + Math.random() * 15000; // 15-30 seconds var baseParticleSpeed = 1; // Base speed multiplier var countdownAcceleration = 1; // Separate from base speed var isCountdownActive = false; // Fixed BackgroundParticle class var BackgroundParticle = Container.expand(function () { var self = Container.call(this); self.particleGraphics = null; self.baseSpeed = 0; // Store the original speed self.initialScale = 1; self.baseAlpha = 0.1; self.sineAmplitude = 0; self.sineFrequency = 0; self._startTwinkleAnimation = function () { tween.stop(self, { alpha: true }); var currentAlpha = self.alpha; var pulseDelta = self.baseAlpha * 0.4; var targetMinAlpha = Math.max(0.05, self.baseAlpha - pulseDelta); var targetMaxAlpha = Math.min(1.0, self.baseAlpha + pulseDelta); if (targetMaxAlpha - targetMinAlpha < 0.05) { if (self.baseAlpha < 0.15) { targetMaxAlpha = Math.min(1.0, self.baseAlpha + 0.1); } else if (self.baseAlpha > 0.85) { targetMinAlpha = Math.max(0.05, self.baseAlpha - 0.1); } if (targetMaxAlpha - targetMinAlpha < 0.05) { return; } } var nextTargetAlpha; if (self.alpha === self.baseAlpha || Math.abs(currentAlpha - targetMaxAlpha) < Math.abs(currentAlpha - targetMinAlpha)) { nextTargetAlpha = targetMinAlpha; } else { nextTargetAlpha = targetMaxAlpha; } var pulseDuration = 300 + Math.random() * 400; tween(self, { alpha: nextTargetAlpha }, { duration: pulseDuration, easing: tween.easeInOut, onFinish: function onFinish() { if (self && self._startTwinkleAnimation) { self._startTwinkleAnimation(); } } }); }; self.init = function () { tween.stop(self, { alpha: true }); var scale = 0.312 + Math.random() * 1.248; self.initialScale = scale; self.scale.set(scale); var colors = [0xff00ff, 0x00ffff, 0xffff00, 0xff0000, 0x00ff00, 0x0000ff, 0xffa500, 0xda70d6]; var color = colors[Math.floor(Math.random() * colors.length)]; if (self.particleGraphics) { self.particleGraphics.destroy(); } self.particleGraphics = self.attachAsset('starParticle', { anchorX: 0.5, anchorY: 0.5 }); self.particleGraphics.tint = color; self.baseSpeed = (0.5 + Math.random() * 1.5) * scale; self.sineAmplitude = 20 + Math.random() * 60; self.sineFrequency = 0.0015 + Math.random() * 0.003; self.baseAlpha = 0.1 + Math.random() * 0.7; self.alpha = self.baseAlpha; self._startTwinkleAnimation(); }; self.updateParticle = function () { // Get current direction (smoothly interpolated if changing) var currentDirection = getCurrentParticleDirection(); // Calculate total speed (base * game speed * countdown acceleration) var totalSpeed = self.baseSpeed * baseParticleSpeed * (isCountdownActive ? countdownAcceleration : 1); // Store previous position for sine calculation var prevX = self.x; var prevY = self.y; // Move in the current direction self.x += currentDirection.x * totalSpeed; self.y += currentDirection.y * totalSpeed; // Apply sine wave effect perpendicular to movement direction // But keep it subtle and relative to distance traveled var distanceThisFrame = totalSpeed; var sineOffset = self.sineAmplitude * Math.sin(self.sineFrequency * (self.x + self.y)); // Apply sine offset perpendicular to movement direction var perpX = -currentDirection.y; var perpY = currentDirection.x; self.x += perpX * sineOffset * 0.01; // Much smaller sine effect self.y += perpY * sineOffset * 0.01; // Reset particle if it goes off screen - improved logic var margin = 100; var shouldReset = false; var newX, newY; if (self.x < -margin || self.x > GAME_WIDTH + margin || self.y < -margin || self.y > GAME_HEIGHT + margin) { // Respawn from the opposite side based on movement direction if (currentDirection.x > 0.1) { // Moving right, spawn from left newX = -margin + Math.random() * 50; newY = Math.random() * GAME_HEIGHT; } else if (currentDirection.x < -0.1) { // Moving left, spawn from right newX = GAME_WIDTH + margin - Math.random() * 50; newY = Math.random() * GAME_HEIGHT; } else if (currentDirection.y > 0.1) { // Moving down, spawn from top newX = Math.random() * GAME_WIDTH; newY = -margin + Math.random() * 50; } else { // Moving up, spawn from bottom newX = Math.random() * GAME_WIDTH; newY = GAME_HEIGHT + margin - Math.random() * 50; } self.x = newX; self.y = newY; } }; self.init(); return self; }); // Helper function to get current particle direction with smooth interpolation function getCurrentParticleDirection() { if (!isChangingDirection) { return globalParticleDirection; } var currentTime = Date.now(); var elapsed = currentTime - directionChangeStartTime; var progress = Math.min(elapsed / directionChangeDuration, 1); // Smooth easing var easedProgress = progress * progress * (3 - 2 * progress); // Smoothstep // Interpolate between current and target direction var lerpX = globalParticleDirection.x + (targetParticleDirection.x - globalParticleDirection.x) * easedProgress; var lerpY = globalParticleDirection.y + (targetParticleDirection.y - globalParticleDirection.y) * easedProgress; // Normalize the direction var length = Math.sqrt(lerpX * lerpX + lerpY * lerpY); if (length > 0) { lerpX /= length; lerpY /= length; } // Check if transition is complete if (progress >= 1) { isChangingDirection = false; globalParticleDirection.x = targetParticleDirection.x; globalParticleDirection.y = targetParticleDirection.y; lastDirectionChangeTime = currentTime; nextDirectionChangeDelay = 15000 + Math.random() * 15000; // 15-30 seconds } return { x: lerpX, y: lerpY }; } // Function to initiate a direction change function changeParticleDirection() { if (isChangingDirection) return; // Already changing // Generate a new direction 45-90 degrees from current var angleChange = (Math.PI / 4) + Math.random() * (Math.PI / 4); // 45-90 degrees if (Math.random() < 0.5) angleChange = -angleChange; // Random direction var currentAngle = Math.atan2(globalParticleDirection.y, globalParticleDirection.x); var newAngle = currentAngle + angleChange; targetParticleDirection.x = Math.cos(newAngle); targetParticleDirection.y = Math.sin(newAngle); isChangingDirection = true; directionChangeStartTime = Date.now(); } // Modified startCountdown function function startCountdown() { countdownTxt = new Text2('3', { size: 200, fill: 0xFFFFFF }); countdownTxt.anchor.set(0.5, 0.5); countdownTxt.x = GAME_WIDTH / 2; countdownTxt.y = GAME_HEIGHT / 2; game.addChild(countdownTxt); // Start countdown acceleration isCountdownActive = true; countdownAcceleration = 1; var startTime = Date.now(); var countdownDuration = 4000; // 4 seconds total var count = 3; var countdownInterval = LK.setInterval(function () { count--; if (count > 0) { countdownTxt.setText(count.toString()); } else if (count === 0) { countdownTxt.setText('GO!'); } else { LK.clearInterval(countdownInterval); if (countdownTxt && countdownTxt.destroy) { countdownTxt.destroy(); } // Keep the accelerated speed when game starts baseParticleSpeed = 5; // Maintain 5x speed isCountdownActive = false; countdownAcceleration = 1; startGame(); } }, 1000); // Smooth acceleration during countdown var accelerationInterval = LK.setInterval(function() { if (!isCountdownActive) { LK.clearInterval(accelerationInterval); return; } var elapsed = Date.now() - startTime; var progress = Math.min(elapsed / countdownDuration, 1); // Smooth acceleration curve - starts slow, ramps up faster var easedProgress = progress * progress; countdownAcceleration = 1 + (easedProgress * 4); // 1x to 5x speed }, 50); // Update every 50ms for smooth acceleration } // Modified setupGame function - add this line to reset particles when starting a new game function setupGame(songName) { // ... existing setupGame code ... // Reset particle speed for new game baseParticleSpeed = 1; isCountdownActive = false; countdownAcceleration = 1; ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Update with: // Modify the startCountdown function function startCountdown() { countdownTxt = new Text2('3', { size: 200, fill: 0xFFFFFF }); countdownTxt.anchor.set(0.5, 0.5); countdownTxt.x = GAME_WIDTH / 2; countdownTxt.y = GAME_HEIGHT / 2; game.addChild(countdownTxt); // Start countdown acceleration isCountdownActive = true; var startTime = Date.now(); var countdownDuration = 4000; // 4 seconds total var count = 3; var countdownInterval = LK.setInterval(function () { count--; if (count > 0) { countdownTxt.setText(count.toString()); } else if (count === 0) { countdownTxt.setText('GO!'); } else { LK.clearInterval(countdownInterval); if (countdownTxt && countdownTxt.destroy) { countdownTxt.destroy(); } isCountdownActive = false; countdownSpeedMultiplier = 1; // Reset to normal speed startGame(); } }, 1000); // Smooth acceleration during countdown var accelerationInterval = LK.setInterval(function() { if (!isCountdownActive) { LK.clearInterval(accelerationInterval); return; } var elapsed = Date.now() - startTime; var progress = Math.min(elapsed / countdownDuration, 1); // Smooth acceleration curve - starts slow, ramps up faster var easedProgress = progress * progress; countdownSpeedMultiplier = 1 + (easedProgress * 4); // 1x to 5x speed }, 50); // Update every 50ms for smooth acceleration } // Add to the game.update function (add this before the existing update code) game.update = function () { // Update background particles with new space travel effects backgroundParticles.forEach(function (particle) { particle.updateParticle(); }); // Handle direction changes during gameplay if (currentGameState === GAME_STATE_PLAYING && gameStarted && !isCountdownActive) { var currentTime = Date.now(); if (currentTime - lastDirectionChangeTime > nextDirectionChangeDelay) { changeParticleDirection(); } } ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Update with: var BackgroundParticle = Container.expand(function () { var self = Container.call(this); self.particleGraphics = null; self.baseSpeed = 0; // Store the original speed self.speed = 0; self.initialScale = 1; self.baseAlpha = 0.1; self.initialX = 0; self.sineAmplitude = 0; self.sineFrequency = 0; self.distanceTraveled = 0; // For sine wave calculation self._startTwinkleAnimation = function () { tween.stop(self, { alpha: true }); var currentAlpha = self.alpha; var pulseDelta = self.baseAlpha * 0.4; var targetMinAlpha = Math.max(0.05, self.baseAlpha - pulseDelta); var targetMaxAlpha = Math.min(1.0, self.baseAlpha + pulseDelta); if (targetMaxAlpha - targetMinAlpha < 0.05) { if (self.baseAlpha < 0.15) { targetMaxAlpha = Math.min(1.0, self.baseAlpha + 0.1); } else if (self.baseAlpha > 0.85) { targetMinAlpha = Math.max(0.05, self.baseAlpha - 0.1); } if (targetMaxAlpha - targetMinAlpha < 0.05) { return; } } var nextTargetAlpha; if (self.alpha === self.baseAlpha || Math.abs(currentAlpha - targetMaxAlpha) < Math.abs(currentAlpha - targetMinAlpha)) { nextTargetAlpha = targetMinAlpha; } else { nextTargetAlpha = targetMaxAlpha; } var pulseDuration = 300 + Math.random() * 400; tween(self, { alpha: nextTargetAlpha }, { duration: pulseDuration, easing: tween.easeInOut, onFinish: function onFinish() { if (self && self._startTwinkleAnimation) { self._startTwinkleAnimation(); } } }); }; self.init = function () { tween.stop(self, { alpha: true }); var scale = 0.312 + Math.random() * 1.248; self.initialScale = scale; self.scale.set(scale); var colors = [0xff00ff, 0x00ffff, 0xffff00, 0xff0000, 0x00ff00, 0x0000ff, 0xffa500, 0xda70d6]; var color = colors[Math.floor(Math.random() * colors.length)]; if (self.particleGraphics) { self.particleGraphics.destroy(); } self.particleGraphics = self.attachAsset('starParticle', { anchorX: 0.5, anchorY: 0.5 }); self.particleGraphics.tint = color; self.baseSpeed = (0.5 + Math.random() * 1.5) * scale; self.speed = self.baseSpeed; self.x = Math.random() * GAME_WIDTH; self.initialX = self.x; self.sineAmplitude = 20 + Math.random() * 60; self.sineFrequency = 0.0015 + Math.random() * 0.003; self.y = -Math.random() * 100 - self.particleGraphics.height * scale; // Start above screen self.baseAlpha = 0.1 + Math.random() * 0.7; self.alpha = self.baseAlpha; self.distanceTraveled = 0; self._startTwinkleAnimation(); }; self.updateParticle = function () { // Apply current speed multiplier (for countdown effect) var currentSpeed = self.baseSpeed * countdownSpeedMultiplier; // Get current direction (smoothly interpolated if changing) var currentDirection = getCurrentParticleDirection(); // Move in the current direction self.x += currentDirection.x * currentSpeed; self.y += currentDirection.y * currentSpeed; // Update distance traveled for sine wave self.distanceTraveled += currentSpeed; // Apply sine wave perpendicular to movement direction var sineOffset = self.sineAmplitude * Math.sin(self.sineFrequency * self.distanceTraveled); // Calculate perpendicular direction for sine wave var perpX = -currentDirection.y; // Perpendicular to direction var perpY = currentDirection.x; // Apply sine offset var sineX = self.initialX + perpX * sineOffset; var sineY = self.y + perpY * sineOffset; // Only apply sine to X if moving primarily vertically, and vice versa if (Math.abs(currentDirection.y) > Math.abs(currentDirection.x)) { self.x = sineX; } else { self.y = sineY; } // Reset particle if it goes off screen var margin = self.particleGraphics.height * self.initialScale; if (self.x < -margin || self.x > GAME_WIDTH + margin || self.y < -margin || self.y > GAME_HEIGHT + margin) { self.init(); // Start from appropriate edge based on current direction if (currentDirection.y > 0.5) { // Moving down self.y = -Math.random() * 100 - margin; self.x = Math.random() * GAME_WIDTH; } else if (currentDirection.y < -0.5) { // Moving up self.y = GAME_HEIGHT + Math.random() * 100 + margin; self.x = Math.random() * GAME_WIDTH; } else if (currentDirection.x > 0.5) { // Moving right self.x = -Math.random() * 100 - margin; self.y = Math.random() * GAME_HEIGHT; } else { // Moving left self.x = GAME_WIDTH + Math.random() * 100 + margin; self.y = Math.random() * GAME_HEIGHT; } self.initialX = self.x; self.distanceTraveled = 0; } }; self.init(); return self; }); // Helper function to get current particle direction with smooth interpolation function getCurrentParticleDirection() { if (!isChangingDirection) { return globalParticleDirection; } var currentTime = Date.now(); var elapsed = currentTime - directionChangeStartTime; var progress = Math.min(elapsed / directionChangeDuration, 1); // Smooth easing var easedProgress = progress * progress * (3 - 2 * progress); // Smoothstep // Interpolate between current and target direction var lerpX = globalParticleDirection.x + (targetParticleDirection.x - globalParticleDirection.x) * easedProgress; var lerpY = globalParticleDirection.y + (targetParticleDirection.y - globalParticleDirection.y) * easedProgress; // Normalize the direction var length = Math.sqrt(lerpX * lerpX + lerpY * lerpY); if (length > 0) { lerpX /= length; lerpY /= length; } // Check if transition is complete if (progress >= 1) { isChangingDirection = false; globalParticleDirection.x = targetParticleDirection.x; globalParticleDirection.y = targetParticleDirection.y; lastDirectionChangeTime = currentTime; nextDirectionChangeDelay = 15000 + Math.random() * 15000; // 15-30 seconds } return { x: lerpX, y: lerpY }; } // Function to initiate a direction change function changeParticleDirection() { if (isChangingDirection) return; // Already changing // Generate a new direction 45-90 degrees from current var angleChange = (Math.PI / 4) + Math.random() * (Math.PI / 4); // 45-90 degrees if (Math.random() < 0.5) angleChange = -angleChange; // Random direction var currentAngle = Math.atan2(globalParticleDirection.y, globalParticleDirection.x); var newAngle = currentAngle + angleChange; targetParticleDirection.x = Math.cos(newAngle); targetParticleDirection.y = Math.sin(newAngle); isChangingDirection = true; directionChangeStartTime = Date.now(); } ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Implement a new background particles system using as needed: ```javascript // Add these global variables near the top with other game variables var globalParticleDirection = { x: 0, y: 1 }; // Start moving downward var targetParticleDirection = { x: 0, y: 1 }; var directionChangeStartTime = 0; var directionChangeDuration = 1000; // 1 second var isChangingDirection = false; var lastDirectionChangeTime = 0; var nextDirectionChangeDelay = 15000 + Math.random() * 15000; // 15-30 seconds var countdownSpeedMultiplier = 1; var isCountdownActive = false; // Modified BackgroundParticle class var BackgroundParticle = Container.expand(function () { var self = Container.call(this); self.particleGraphics = null; self.baseSpeed = 0; // Store the original speed self.speed = 0; self.initialScale = 1; self.baseAlpha = 0.1; self.initialX = 0; self.sineAmplitude = 0; self.sineFrequency = 0; self.distanceTraveled = 0; // For sine wave calculation self._startTwinkleAnimation = function () { tween.stop(self, { alpha: true }); var currentAlpha = self.alpha; var pulseDelta = self.baseAlpha * 0.4; var targetMinAlpha = Math.max(0.05, self.baseAlpha - pulseDelta); var targetMaxAlpha = Math.min(1.0, self.baseAlpha + pulseDelta); if (targetMaxAlpha - targetMinAlpha < 0.05) { if (self.baseAlpha < 0.15) { targetMaxAlpha = Math.min(1.0, self.baseAlpha + 0.1); } else if (self.baseAlpha > 0.85) { targetMinAlpha = Math.max(0.05, self.baseAlpha - 0.1); } if (targetMaxAlpha - targetMinAlpha < 0.05) { return; } } var nextTargetAlpha; if (self.alpha === self.baseAlpha || Math.abs(currentAlpha - targetMaxAlpha) < Math.abs(currentAlpha - targetMinAlpha)) { nextTargetAlpha = targetMinAlpha; } else { nextTargetAlpha = targetMaxAlpha; } var pulseDuration = 300 + Math.random() * 400; tween(self, { alpha: nextTargetAlpha }, { duration: pulseDuration, easing: tween.easeInOut, onFinish: function onFinish() { if (self && self._startTwinkleAnimation) { self._startTwinkleAnimation(); } } }); }; self.init = function () { tween.stop(self, { alpha: true }); var scale = 0.312 + Math.random() * 1.248; self.initialScale = scale; self.scale.set(scale); var colors = [0xff00ff, 0x00ffff, 0xffff00, 0xff0000, 0x00ff00, 0x0000ff, 0xffa500, 0xda70d6]; var color = colors[Math.floor(Math.random() * colors.length)]; if (self.particleGraphics) { self.particleGraphics.destroy(); } self.particleGraphics = self.attachAsset('starParticle', { anchorX: 0.5, anchorY: 0.5 }); self.particleGraphics.tint = color; self.baseSpeed = (0.5 + Math.random() * 1.5) * scale; self.speed = self.baseSpeed; self.x = Math.random() * GAME_WIDTH; self.initialX = self.x; self.sineAmplitude = 20 + Math.random() * 60; self.sineFrequency = 0.0015 + Math.random() * 0.003; self.y = -Math.random() * 100 - self.particleGraphics.height * scale; // Start above screen self.baseAlpha = 0.1 + Math.random() * 0.7; self.alpha = self.baseAlpha; self.distanceTraveled = 0; self._startTwinkleAnimation(); }; self.updateParticle = function () { // Apply current speed multiplier (for countdown effect) var currentSpeed = self.baseSpeed * countdownSpeedMultiplier; // Get current direction (smoothly interpolated if changing) var currentDirection = getCurrentParticleDirection(); // Move in the current direction self.x += currentDirection.x * currentSpeed; self.y += currentDirection.y * currentSpeed; // Update distance traveled for sine wave self.distanceTraveled += currentSpeed; // Apply sine wave perpendicular to movement direction var sineOffset = self.sineAmplitude * Math.sin(self.sineFrequency * self.distanceTraveled); // Calculate perpendicular direction for sine wave var perpX = -currentDirection.y; // Perpendicular to direction var perpY = currentDirection.x; // Apply sine offset var sineX = self.initialX + perpX * sineOffset; var sineY = self.y + perpY * sineOffset; // Only apply sine to X if moving primarily vertically, and vice versa if (Math.abs(currentDirection.y) > Math.abs(currentDirection.x)) { self.x = sineX; } else { self.y = sineY; } // Reset particle if it goes off screen var margin = self.particleGraphics.height * self.initialScale; if (self.x < -margin || self.x > GAME_WIDTH + margin || self.y < -margin || self.y > GAME_HEIGHT + margin) { self.init(); // Start from appropriate edge based on current direction if (currentDirection.y > 0.5) { // Moving down self.y = -Math.random() * 100 - margin; self.x = Math.random() * GAME_WIDTH; } else if (currentDirection.y < -0.5) { // Moving up self.y = GAME_HEIGHT + Math.random() * 100 + margin; self.x = Math.random() * GAME_WIDTH; } else if (currentDirection.x > 0.5) { // Moving right self.x = -Math.random() * 100 - margin; self.y = Math.random() * GAME_HEIGHT; } else { // Moving left self.x = GAME_WIDTH + Math.random() * 100 + margin; self.y = Math.random() * GAME_HEIGHT; } self.initialX = self.x; self.distanceTraveled = 0; } }; self.init(); return self; }); // Helper function to get current particle direction with smooth interpolation function getCurrentParticleDirection() { if (!isChangingDirection) { return globalParticleDirection; } var currentTime = Date.now(); var elapsed = currentTime - directionChangeStartTime; var progress = Math.min(elapsed / directionChangeDuration, 1); // Smooth easing var easedProgress = progress * progress * (3 - 2 * progress); // Smoothstep // Interpolate between current and target direction var lerpX = globalParticleDirection.x + (targetParticleDirection.x - globalParticleDirection.x) * easedProgress; var lerpY = globalParticleDirection.y + (targetParticleDirection.y - globalParticleDirection.y) * easedProgress; // Normalize the direction var length = Math.sqrt(lerpX * lerpX + lerpY * lerpY); if (length > 0) { lerpX /= length; lerpY /= length; } // Check if transition is complete if (progress >= 1) { isChangingDirection = false; globalParticleDirection.x = targetParticleDirection.x; globalParticleDirection.y = targetParticleDirection.y; lastDirectionChangeTime = currentTime; nextDirectionChangeDelay = 15000 + Math.random() * 15000; // 15-30 seconds } return { x: lerpX, y: lerpY }; } // Function to initiate a direction change function changeParticleDirection() { if (isChangingDirection) return; // Already changing // Generate a new direction 45-90 degrees from current var angleChange = (Math.PI / 4) + Math.random() * (Math.PI / 4); // 45-90 degrees if (Math.random() < 0.5) angleChange = -angleChange; // Random direction var currentAngle = Math.atan2(globalParticleDirection.y, globalParticleDirection.x); var newAngle = currentAngle + angleChange; targetParticleDirection.x = Math.cos(newAngle); targetParticleDirection.y = Math.sin(newAngle); isChangingDirection = true; directionChangeStartTime = Date.now(); } ``` Now I need to modify the countdown function and game update loop. Here are the changes: ```javascript // Modify the startCountdown function function startCountdown() { countdownTxt = new Text2('3', { size: 200, fill: 0xFFFFFF }); countdownTxt.anchor.set(0.5, 0.5); countdownTxt.x = GAME_WIDTH / 2; countdownTxt.y = GAME_HEIGHT / 2; game.addChild(countdownTxt); // Start countdown acceleration isCountdownActive = true; var startTime = Date.now(); var countdownDuration = 4000; // 4 seconds total var count = 3; var countdownInterval = LK.setInterval(function () { count--; if (count > 0) { countdownTxt.setText(count.toString()); } else if (count === 0) { countdownTxt.setText('GO!'); } else { LK.clearInterval(countdownInterval); if (countdownTxt && countdownTxt.destroy) { countdownTxt.destroy(); } isCountdownActive = false; countdownSpeedMultiplier = 1; // Reset to normal speed startGame(); } }, 1000); // Smooth acceleration during countdown var accelerationInterval = LK.setInterval(function() { if (!isCountdownActive) { LK.clearInterval(accelerationInterval); return; } var elapsed = Date.now() - startTime; var progress = Math.min(elapsed / countdownDuration, 1); // Smooth acceleration curve - starts slow, ramps up faster var easedProgress = progress * progress; countdownSpeedMultiplier = 1 + (easedProgress * 4); // 1x to 5x speed }, 50); // Update every 50ms for smooth acceleration } // Add to the game.update function (add this before the existing update code) game.update = function () { // Update background particles with new space travel effects backgroundParticles.forEach(function (particle) { particle.updateParticle(); }); // Handle direction changes during gameplay if (currentGameState === GAME_STATE_PLAYING && gameStarted && !isCountdownActive) { var currentTime = Date.now(); if (currentTime - lastDirectionChangeTime > nextDirectionChangeDelay) { changeParticleDirection(); } } // ... rest of existing update function remains the same ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
// Add these global variables near the top with other game variables var globalParticleDirection = { x: 0, y: 1 }; // Start moving downward var targetParticleDirection = { x: 0, y: 1 }; var directionChangeStartTime = 0; var directionChangeDuration = 1000; // 1 second var isChangingDirection = false; var lastDirectionChangeTime = 0; var nextDirectionChangeDelay = 15000 + Math.random() * 15000; // 15-30 seconds var countdownSpeedMultiplier = 1; var isCountdownActive = false;
Code edit (2 edits merged)
Please save this source code
User prompt
Allow misses for A grade but keep hit rate.
Code edit (1 edits merged)
Please save this source code
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ var BackgroundParticle = Container.expand(function () { var self = Container.call(this); self.particleGraphics = null; self.baseSpeed = 0; // Store the original speed self.initialScale = 1; self.baseAlpha = 0.1; self.sineAmplitude = 0; self.sineFrequency = 0; self._startTwinkleAnimation = function () { tween.stop(self, { alpha: true }); var currentAlpha = self.alpha; var pulseDelta = self.baseAlpha * 0.4; var targetMinAlpha = Math.max(0.05, self.baseAlpha - pulseDelta); var targetMaxAlpha = Math.min(1.0, self.baseAlpha + pulseDelta); if (targetMaxAlpha - targetMinAlpha < 0.05) { if (self.baseAlpha < 0.15) { targetMaxAlpha = Math.min(1.0, self.baseAlpha + 0.1); } else if (self.baseAlpha > 0.85) { targetMinAlpha = Math.max(0.05, self.baseAlpha - 0.1); } if (targetMaxAlpha - targetMinAlpha < 0.05) { return; } } var nextTargetAlpha; if (self.alpha === self.baseAlpha || Math.abs(currentAlpha - targetMaxAlpha) < Math.abs(currentAlpha - targetMinAlpha)) { nextTargetAlpha = targetMinAlpha; } else { nextTargetAlpha = targetMaxAlpha; } var pulseDuration = 300 + Math.random() * 400; tween(self, { alpha: nextTargetAlpha }, { duration: pulseDuration, easing: tween.easeInOut, onFinish: function onFinish() { if (self && self._startTwinkleAnimation) { self._startTwinkleAnimation(); } } }); }; self.init = function () { tween.stop(self, { alpha: true }); var scale = 0.312 + Math.random() * 1.248; self.initialScale = scale; self.scale.set(scale); var colors = [0xff00ff, 0x00ffff, 0xffff00, 0xff0000, 0x00ff00, 0x0000ff, 0xffa500, 0xda70d6]; var color = colors[Math.floor(Math.random() * colors.length)]; if (self.particleGraphics) { self.particleGraphics.destroy(); } self.particleGraphics = self.attachAsset('starParticle', { anchorX: 0.5, anchorY: 0.5 }); self.particleGraphics.tint = color; self.baseSpeed = (0.5 + Math.random() * 1.5) * scale; self.sineAmplitude = 20 + Math.random() * 60; self.sineFrequency = 0.0015 + Math.random() * 0.003; self.baseAlpha = 0.1 + Math.random() * 0.7; self.alpha = self.baseAlpha; self._startTwinkleAnimation(); }; self.updateParticle = function () { // Get current direction (smoothly interpolated if changing) var currentDirection = getCurrentParticleDirection(); // Calculate total speed (base * game speed * countdown acceleration) var totalSpeed = self.baseSpeed * baseParticleSpeed * (isCountdownActive ? countdownAcceleration : 1); // Store previous position for sine calculation var prevX = self.x; var prevY = self.y; // Move in the current direction self.x += currentDirection.x * totalSpeed; self.y += currentDirection.y * totalSpeed; // Apply sine wave effect perpendicular to movement direction // But keep it subtle and relative to distance traveled var distanceThisFrame = totalSpeed; var sineOffset = self.sineAmplitude * Math.sin(self.sineFrequency * (self.x + self.y)); // Apply sine offset perpendicular to movement direction var perpX = -currentDirection.y; var perpY = currentDirection.x; self.x += perpX * sineOffset * 0.01; // Much smaller sine effect self.y += perpY * sineOffset * 0.01; // Reset particle if it goes off screen - improved logic var margin = 100; var shouldReset = false; var newX, newY; if (self.x < -margin || self.x > GAME_WIDTH + margin || self.y < -margin || self.y > GAME_HEIGHT + margin) { // Respawn from the opposite side based on movement direction if (currentDirection.x > 0.1) { // Moving right, spawn from left newX = -margin + Math.random() * 50; newY = Math.random() * GAME_HEIGHT; } else if (currentDirection.x < -0.1) { // Moving left, spawn from right newX = GAME_WIDTH + margin - Math.random() * 50; newY = Math.random() * GAME_HEIGHT; } else if (currentDirection.y > 0.1) { // Moving down, spawn from top newX = Math.random() * GAME_WIDTH; newY = -margin + Math.random() * 50; } else { // Moving up, spawn from bottom newX = Math.random() * GAME_WIDTH; newY = GAME_HEIGHT + margin - Math.random() * 50; } self.x = newX; self.y = newY; } }; self.init(); return self; }); var Note = Container.expand(function (noteData, spawnTime) { var self = Container.call(this); self.noteData = noteData; self.type = noteData.type; self.targetX = noteData.x; self.targetY = noteData.y; self.hitTime = noteData.time; self.duration = noteData.duration || 0; self.spawnTime = spawnTime; self.zone = noteData.x < GAME_WIDTH / 2 ? 'left' : 'right'; if (noteData.color && noteData.color === SYNC_NOTE_COLOR) { if (self.type === 'tap') { self.color = COLOR_SYNC_TAP; } else if (self.type === 'hold') { self.color = COLOR_SYNC_HOLD; } else { self.color = self.zone === 'left' ? COLOR_LEFT_ZONE : COLOR_RIGHT_ZONE; } } else { self.color = self.zone === 'left' ? COLOR_LEFT_ZONE : COLOR_RIGHT_ZONE; } self.active = true; self.isHit = false; self.isMissed = false; self.isSpawning = true; self.isHolding = false; self.holdSuccessfullyCompleted = false; self.holdParticles = []; self.lastParticleEmissionTime = 0; self.PARTICLE_EMISSION_INTERVAL = 50; self.holdStarted = false; self.beatPulseInterval = null; var assetName = self.type === 'hold' ? 'holdNoteCore' : 'noteCore'; self.noteGraphics = self.attachAsset(assetName, { anchorX: 0.5, anchorY: 0.5 }); self.noteGraphics.tint = self.color; self._startBeatPulseAnimation = function () { if (!self || !self.active || self.isHit || self.isMissed || self.isSpawning) { tween.stop(self, { scaleX: true, scaleY: true }); return; } tween.stop(self, { scaleX: true, scaleY: true }); tween(self, { scaleX: 1.15, scaleY: 1.15 }, { duration: BEAT_DURATION_MS * 0.2, easing: tween.easeOut, onFinish: function onFinish() { if (self && self.active && !self.isHit && !self.isMissed && !self.isSpawning) { tween(self, { scaleX: 1.0, scaleY: 1.0 }, { duration: BEAT_DURATION_MS * 0.2, easing: tween.easeIn }); } } }); }; self._clearBeatPulseLoop = function () { if (self.beatPulseInterval) { LK.clearInterval(self.beatPulseInterval); self.beatPulseInterval = null; } tween.stop(self, { scaleX: true, scaleY: true }); }; self._initiateBeatPulseLoop = function () { self._clearBeatPulseLoop(); if (self && self.active && !self.isHit && !self.isMissed && !self.isSpawning) { self._startBeatPulseAnimation(); self.beatPulseInterval = LK.setInterval(function () { if (self && self.active && !self.isHit && !self.isMissed && !self.isSpawning) { self._startBeatPulseAnimation(); } else { self._clearBeatPulseLoop(); } }, BEAT_DURATION_MS); } }; self.x = self.targetX; self.y = self.targetY; self.alpha = 0; self.scaleX = 0.1; self.scaleY = 0.1; self.createHoldTrail = function () { var cycleTime = self.hitTime % SCANNER_CYCLE_DURATION; var scannerMovingUp = cycleTime < SCANNER_HALF_CYCLE; var direction = scannerMovingUp ? -1 : 1; var originalRequestedDuration = self.duration; var maxPossibleTrailPixelLength = originalRequestedDuration / SCANNER_HALF_CYCLE * PLAY_AREA_HEIGHT; var actualTrailPixelLength = maxPossibleTrailPixelLength; if (direction === -1) { if (self.y - actualTrailPixelLength < SCANNER_Y_MIN) { actualTrailPixelLength = self.y - SCANNER_Y_MIN; } } else { if (self.y + actualTrailPixelLength > SCANNER_Y_MAX) { actualTrailPixelLength = SCANNER_Y_MAX - self.y; } } actualTrailPixelLength = Math.max(0, actualTrailPixelLength); if (actualTrailPixelLength < maxPossibleTrailPixelLength) { if (PLAY_AREA_HEIGHT > 0 && SCANNER_HALF_CYCLE > 0) { self.duration = actualTrailPixelLength / PLAY_AREA_HEIGHT * SCANNER_HALF_CYCLE; } else if (actualTrailPixelLength === 0) { self.duration = 0; } } self.duration = Math.max(0, self.duration); // Create trail line first so it's below everything else var trailLine = self.attachAsset('holdTrail', { anchorX: 0.5, anchorY: 0 }); trailLine.tint = self.color; trailLine.alpha = 0.6; trailLine.height = actualTrailPixelLength; if (direction === -1) { trailLine.y = -actualTrailPixelLength; } else { trailLine.y = 0; } self.holdTrails.push(trailLine); // Move the trail to index 0 so it's below the note graphics self.setChildIndex(trailLine, 0); self.endNote = self.attachAsset('noteCore', { anchorX: 0.5, anchorY: 0.5 }); self.endNote.tint = self.color; self.endNote.alpha = 0.8; self.endNote.scaleX = 0.7; self.endNote.scaleY = 0.7; self.endNote.y = actualTrailPixelLength * direction; self.endGlow = self.attachAsset('glowRing', { anchorX: 0.5, anchorY: 0.5 }); self.endGlow.tint = self.color; self.endGlow.alpha = 0.3; self.endGlow.scaleX = 0.5; self.endGlow.scaleY = 0.5; self.endGlow.y = actualTrailPixelLength * direction; }; self.holdTrails = []; if (self.type === 'hold' && self.duration > 0) { self.createHoldTrail(); } self.spawnIn = function () { self.isSpawning = true; tween(self, { alpha: 1, scaleX: 1, scaleY: 1 }, { duration: 500, easing: tween.easeOut, onFinish: function onFinish() { self.isSpawning = false; self._initiateBeatPulseLoop(); } }); }; self.showHitEffect = function () { self._clearBeatPulseLoop(); LK.getSound('hitSound').play(); self.explodeIntoParticles(); tween(self, { scaleX: 1.5, scaleY: 1.5, alpha: 0 }, { duration: 200, onFinish: function onFinish() { self.active = false; self._clearBeatPulseLoop(); } }); }; self.startHoldEffect = function () { self._clearBeatPulseLoop(); LK.getSound('hitSound').play(); tween(self.noteGraphics, { scaleX: 1.2, scaleY: 1.2 }, { duration: 100, onFinish: function onFinish() { tween(self.noteGraphics, { scaleX: 1, scaleY: 1 }, { duration: 100 }); } }); }; self.showMissEffect = function () { self._clearBeatPulseLoop(); LK.getSound('missSound').play(); tween(self.noteGraphics, { tint: 0xff0000 }, { duration: 150, onFinish: function onFinish() { tween(self, { alpha: 0.3 }, { duration: 300, onFinish: function onFinish() { self.active = false; self._clearBeatPulseLoop(); } }); } }); }; self.explodeIntoParticles = function () { var noteScale = 1.2; // Base scale relative to noteCore (300px) // Stage 1: Initial Flash (0-200ms) var flash = game.attachAsset('supernovaFlash', { anchorX: 0.5, anchorY: 0.5 }); flash.tint = 0xFFFFFF; flash.x = self.x; flash.y = self.y; flash.alpha = 0; flash.scale.set(0.1 * noteScale); tween(flash, { alpha: 1, scaleX: 2.8 * noteScale, scaleY: 2.8 * noteScale }, { duration: 400, easing: tween.easeOut, onFinish: function onFinish() { tween(flash, { alpha: 0 }, { duration: 350, onFinish: function onFinish() { if (flash && flash.destroy) { flash.destroy(); } } }); } }); // Stage 2: Shockwave (100-600ms) LK.setTimeout(function () { var shockwave = game.attachAsset('supernovaShockwave', { anchorX: 0.5, anchorY: 0.5 }); shockwave.tint = 0x00FFFF; shockwave.x = self.x; shockwave.y = self.y; shockwave.alpha = 1; shockwave.scale.set(0.2 * noteScale); tween(shockwave, { scaleX: 5.0 * noteScale, scaleY: 5.0 * noteScale, alpha: 0 }, { duration: 800, easing: tween.easeOut, onFinish: function onFinish() { if (shockwave && shockwave.destroy) { shockwave.destroy(); } } }); }, 100); // Stage 3: Core Particles (50-800ms) var coreParticleCount = 15; for (var i = 0; i < coreParticleCount; i++) { (function (index) { LK.setTimeout(function () { var coreAsset = Math.random() < 0.5 ? 'supernovaCoreParticle' : 'supernovaCoreParticle2'; var particle = game.attachAsset(coreAsset, { anchorX: 0.5, anchorY: 0.5 }); particle.tint = Math.random() < 0.5 ? 0xFFFFFF : 0xFFFF88; particle.x = self.x; particle.y = self.y; var angle = index / coreParticleCount * Math.PI * 2 + (Math.random() - 0.5) * 0.5; var speed = 200 + Math.random() * 150; var targetX = self.x + Math.cos(angle) * speed; var targetY = self.y + Math.sin(angle) * speed; var finalScale = (0.5 + Math.random() * 0.4) * noteScale; particle.scale.set(0.2 * noteScale); tween(particle, { x: targetX, y: targetY, alpha: 0, scaleX: finalScale, scaleY: finalScale }, { duration: 900, easing: tween.easeOut, onFinish: function onFinish() { if (particle && particle.destroy) { particle.destroy(); } } }); }, 50 + Math.random() * 100); })(i); } // Stage 4: Nebula Trails (200-1200ms) var nebulaCount = 20; for (var j = 0; j < nebulaCount; j++) { (function (index) { LK.setTimeout(function () { var nebulaAssets = ['supernovaNebulaTrail', 'supernovaNebulaTrail2', 'supernovaNebulaTrail3']; var nebulaAsset = nebulaAssets[Math.floor(Math.random() * nebulaAssets.length)]; var particle = game.attachAsset(nebulaAsset, { anchorX: 0.5, anchorY: 0.5 }); var colors = [0x8844FF, 0xFF44AA, 0x4488FF, 0xAA44FF]; particle.tint = colors[Math.floor(Math.random() * colors.length)]; particle.x = self.x + (Math.random() - 0.5) * 40; particle.y = self.y + (Math.random() - 0.5) * 40; particle.alpha = 0.8 + Math.random() * 0.3; var angle = Math.random() * Math.PI * 2; var speed = 80 + Math.random() * 120; var targetX = self.x + Math.cos(angle) * speed; var targetY = self.y + Math.sin(angle) * speed; var finalScale = (0.4 + Math.random() * 0.3) * noteScale; particle.scale.set(0.2 * noteScale); tween(particle, { x: targetX, y: targetY, alpha: 0, scaleX: finalScale, scaleY: finalScale, rotation: Math.random() * Math.PI * 2 }, { duration: 1300, easing: tween.easeOut, onFinish: function onFinish() { if (particle && particle.destroy) { particle.destroy(); } } }); }, 200 + Math.random() * 200); })(j); } // Stage 5: Stellar Debris (150-1000ms) var debrisCount = 12; for (var k = 0; k < debrisCount; k++) { (function (index) { LK.setTimeout(function () { var debrisAsset = Math.random() < 0.5 ? 'supernovaStellarDebris' : 'supernovaStellarDebris2'; var particle = game.attachAsset(debrisAsset, { anchorX: 0.5, anchorY: 0.5 }); var colors = [0xFF4400, 0xFF6600, 0xFFAA00, 0xFFFFFF]; particle.tint = colors[Math.floor(Math.random() * colors.length)]; particle.x = self.x; particle.y = self.y; var angle = Math.random() * Math.PI * 2; var speed = 120 + Math.random() * 100; var targetX = self.x + Math.cos(angle) * speed; var targetY = self.y + Math.sin(angle) * speed; var finalScale = (0.25 + Math.random() * 0.25) * noteScale; particle.scale.set(0.10 * noteScale); tween(particle, { x: targetX, y: targetY, alpha: 0, scaleX: finalScale, scaleY: finalScale, rotation: Math.random() * Math.PI * 4 // Multiple rotations }, { duration: 1000, easing: tween.easeOut, onFinish: function onFinish() { if (particle && particle.destroy) { particle.destroy(); } } }); }, 150 + Math.random() * 150); })(k); } // Stage 6: Energy Sparks (100-700ms) var sparkCount = 25; for (var l = 0; l < sparkCount; l++) { (function (index) { LK.setTimeout(function () { var sparkAsset = Math.random() < 0.5 ? 'supernovaEnergySpark' : 'supernovaEnergySpark2'; var particle = game.attachAsset(sparkAsset, { anchorX: 0.5, anchorY: 0.5 }); particle.tint = Math.random() < 0.5 ? 0x00FFFF : 0x0088FF; particle.x = self.x + (Math.random() - 0.5) * 20; particle.y = self.y + (Math.random() - 0.5) * 20; var angle = Math.random() * Math.PI * 2; var speed = 180 + Math.random() * 220; var targetX = self.x + Math.cos(angle) * speed; var targetY = self.y + Math.sin(angle) * speed; var finalScale = (0.2 + Math.random() * 0.2) * noteScale; particle.scale.set(0.1 * noteScale); tween(particle, { x: targetX, y: targetY, alpha: 0, scaleX: finalScale, scaleY: finalScale }, { duration: 900, easing: tween.easeOut, onFinish: function onFinish() { if (particle && particle.destroy) { particle.destroy(); } } }); }, 100 + Math.random() * 100); })(l); } // Stage 7: Afterglow (300-1500ms) LK.setTimeout(function () { var afterglow = game.attachAsset('supernovaAfterglow', { anchorX: 0.5, anchorY: 0.5 }); afterglow.tint = 0xFF6600; afterglow.x = self.x; afterglow.y = self.y; afterglow.alpha = 0.4; afterglow.scale.set(1.2 * noteScale); tween(afterglow, { alpha: 0, scaleX: 3.0 * noteScale, scaleY: 3.0 * noteScale }, { duration: 1500, easing: tween.easeOut, onFinish: function onFinish() { if (afterglow && afterglow.destroy) { afterglow.destroy(); } } }); }, 400); }; self.explodeEndNoteParticles = function () { if (!self.endNote) { return; } var noteScale = 1.2; // Same scale as main note var endNoteGlobalX = self.x; var endNoteGlobalY = self.y + self.endNote.y; // Stage 1: Initial Flash (0-200ms) var flash = game.attachAsset('supernovaFlash', { anchorX: 0.5, anchorY: 0.5 }); flash.tint = 0xFFFFFF; flash.x = endNoteGlobalX; flash.y = endNoteGlobalY; flash.alpha = 0; flash.scale.set(0.1 * noteScale); tween(flash, { alpha: 1, scaleX: 2.5 * noteScale, scaleY: 2.5 * noteScale }, { duration: 200, easing: tween.easeOut, onFinish: function onFinish() { tween(flash, { alpha: 0 }, { duration: 300, onFinish: function onFinish() { if (flash && flash.destroy) { flash.destroy(); } } }); } }); // Stage 2: Shockwave (100-600ms) LK.setTimeout(function () { var shockwave = game.attachAsset('supernovaShockwave', { anchorX: 0.5, anchorY: 0.5 }); shockwave.tint = 0x00FFFF; shockwave.x = endNoteGlobalX; shockwave.y = endNoteGlobalY; shockwave.alpha = 0.9; shockwave.scale.set(0.2 * noteScale); tween(shockwave, { scaleX: 5.0 * noteScale, scaleY: 5.0 * noteScale, alpha: 0 }, { duration: 600, easing: tween.easeOut, onFinish: function onFinish() { if (shockwave && shockwave.destroy) { shockwave.destroy(); } } }); }, 100); // Stage 3: Core Particles (50-800ms) var coreParticleCount = 15; for (var i = 0; i < coreParticleCount; i++) { (function (index) { LK.setTimeout(function () { var coreAsset = Math.random() < 0.5 ? 'supernovaCoreParticle' : 'supernovaCoreParticle2'; var particle = game.attachAsset(coreAsset, { anchorX: 0.5, anchorY: 0.5 }); particle.tint = Math.random() < 0.5 ? 0xFFFFFF : 0xFFFF88; particle.x = endNoteGlobalX; particle.y = endNoteGlobalY; var angle = index / coreParticleCount * Math.PI * 2 + (Math.random() - 0.5) * 0.5; var speed = 200 + Math.random() * 150; var targetX = endNoteGlobalX + Math.cos(angle) * speed; var targetY = endNoteGlobalY + Math.sin(angle) * speed; var finalScale = (0.3 + Math.random() * 0.4) * noteScale; particle.scale.set(0.1 * noteScale); tween(particle, { x: targetX, y: targetY, alpha: 0, scaleX: finalScale, scaleY: finalScale }, { duration: 750, easing: tween.easeOut, onFinish: function onFinish() { if (particle && particle.destroy) { particle.destroy(); } } }); }, 50 + Math.random() * 100); })(i); } // Stage 4: Nebula Trails (200-1200ms) var nebulaCount = 20; for (var j = 0; j < nebulaCount; j++) { (function (index) { LK.setTimeout(function () { var nebulaAssets = ['supernovaNebulaTrail', 'supernovaNebulaTrail2', 'supernovaNebulaTrail3']; var nebulaAsset = nebulaAssets[Math.floor(Math.random() * nebulaAssets.length)]; var particle = game.attachAsset(nebulaAsset, { anchorX: 0.5, anchorY: 0.5 }); var colors = [0x8844FF, 0xFF44AA, 0x4488FF, 0xAA44FF]; particle.tint = colors[Math.floor(Math.random() * colors.length)]; particle.x = endNoteGlobalX + (Math.random() - 0.5) * 40; particle.y = endNoteGlobalY + (Math.random() - 0.5) * 40; particle.alpha = 0.7 + Math.random() * 0.3; var angle = Math.random() * Math.PI * 2; var speed = 80 + Math.random() * 120; var targetX = endNoteGlobalX + Math.cos(angle) * speed; var targetY = endNoteGlobalY + Math.sin(angle) * speed; var finalScale = (0.2 + Math.random() * 0.3) * noteScale; particle.scale.set(0.1 * noteScale); tween(particle, { x: targetX, y: targetY, alpha: 0, scaleX: finalScale, scaleY: finalScale, rotation: Math.random() * Math.PI * 2 }, { duration: 1000, easing: tween.easeOut, onFinish: function onFinish() { if (particle && particle.destroy) { particle.destroy(); } } }); }, 200 + Math.random() * 200); })(j); } // Stage 5: Stellar Debris (150-1000ms) var debrisCount = 12; for (var k = 0; k < debrisCount; k++) { (function (index) { LK.setTimeout(function () { var debrisAsset = Math.random() < 0.5 ? 'supernovaStellarDebris' : 'supernovaStellarDebris2'; var particle = game.attachAsset(debrisAsset, { anchorX: 0.5, anchorY: 0.5 }); var colors = [0xFF4400, 0xFF6600, 0xFFAA00, 0xFFFFFF]; particle.tint = colors[Math.floor(Math.random() * colors.length)]; particle.x = endNoteGlobalX; particle.y = endNoteGlobalY; var angle = Math.random() * Math.PI * 2; var speed = 120 + Math.random() * 100; var targetX = endNoteGlobalX + Math.cos(angle) * speed; var targetY = endNoteGlobalY + Math.sin(angle) * speed; var finalScale = (0.15 + Math.random() * 0.25) * noteScale; particle.scale.set(0.05 * noteScale); tween(particle, { x: targetX, y: targetY, alpha: 0, scaleX: finalScale, scaleY: finalScale, rotation: Math.random() * Math.PI * 4 }, { duration: 850, easing: tween.easeOut, onFinish: function onFinish() { if (particle && particle.destroy) { particle.destroy(); } } }); }, 150 + Math.random() * 150); })(k); } // Stage 6: Energy Sparks (100-700ms) var sparkCount = 25; for (var l = 0; l < sparkCount; l++) { (function (index) { LK.setTimeout(function () { var sparkAsset = Math.random() < 0.5 ? 'supernovaEnergySpark' : 'supernovaEnergySpark2'; var particle = game.attachAsset(sparkAsset, { anchorX: 0.5, anchorY: 0.5 }); particle.tint = Math.random() < 0.5 ? 0x00FFFF : 0x0088FF; particle.x = endNoteGlobalX + (Math.random() - 0.5) * 20; particle.y = endNoteGlobalY + (Math.random() - 0.5) * 20; var angle = Math.random() * Math.PI * 2; var speed = 180 + Math.random() * 220; var targetX = endNoteGlobalX + Math.cos(angle) * speed; var targetY = endNoteGlobalY + Math.sin(angle) * speed; var finalScale = (0.1 + Math.random() * 0.2) * noteScale; particle.scale.set(0.05 * noteScale); tween(particle, { x: targetX, y: targetY, alpha: 0, scaleX: finalScale, scaleY: finalScale }, { duration: 600, easing: tween.easeOut, onFinish: function onFinish() { if (particle && particle.destroy) { particle.destroy(); } } }); }, 100 + Math.random() * 100); })(l); } // Stage 7: Afterglow (300-1500ms) LK.setTimeout(function () { var afterglow = game.attachAsset('supernovaAfterglow', { anchorX: 0.5, anchorY: 0.5 }); afterglow.tint = 0xFF6600; afterglow.x = endNoteGlobalX; afterglow.y = endNoteGlobalY; afterglow.alpha = 0.4; afterglow.scale.set(1.2 * noteScale); tween(afterglow, { alpha: 0, scaleX: 2.0 * noteScale, scaleY: 2.0 * noteScale }, { duration: 1200, easing: tween.easeOut, onFinish: function onFinish() { if (afterglow && afterglow.destroy) { afterglow.destroy(); } } }); }, 300); }; self.updateHoldEffects = function (scannerCurrentY) { if (self.type === 'hold' && self.isHolding && self.active && !self.holdSuccessfullyCompleted && !self.isMissed) { var currentTime = Date.now(); if (currentTime - self.lastParticleEmissionTime > self.PARTICLE_EMISSION_INTERVAL) { self.lastParticleEmissionTime = currentTime; var trailStartY = self.y; var trailEndY = self.y + (self.endNote ? self.endNote.y : 0); var scannerIsOverTrail = self.endNote && scannerCurrentY >= Math.min(trailStartY, trailEndY) && scannerCurrentY <= Math.max(trailStartY, trailEndY); if (scannerIsOverTrail) { var particle = game.attachAsset('holdConnectionParticle', { anchorX: 0.5, anchorY: 0.5 }); particle.x = self.x; particle.y = scannerCurrentY; particle.tint = self.color; particle.alpha = 0.8; self.holdParticles.push(particle); tween(particle, { alpha: 0, scaleX: 0.2, scaleY: 0.2 }, { duration: 500, easing: tween.easeOut, onFinish: function onFinish() { if (particle && particle.destroy) { particle.destroy(); } var index = self.holdParticles.indexOf(particle); if (index > -1) { self.holdParticles.splice(index, 1); } } }); } } } }; self.cleanupHoldVisuals = function () { self.holdParticles.forEach(function (p) { if (p && p.destroy) { p.destroy(); } }); self.holdParticles = []; if (self.endNote && self.endNote.destroy) { self.endNote.destroy(); } if (self.endGlow && self.endGlow.destroy) { self.endGlow.destroy(); } self.endNote = null; self.endGlow = null; self.holdTrails.forEach(function (trail) { if (trail && trail.destroy) { trail.destroy(); } }); self.holdTrails = []; }; self.completeHold = function () { if (self.holdSuccessfullyCompleted || self.isMissed) { return; } self._clearBeatPulseLoop(); self.holdSuccessfullyCompleted = true; self.isHolding = false; self.explodeIntoParticles(); if (self.type === 'hold') { self.explodeEndNoteParticles(); } self.cleanupHoldVisuals(); tween(self, { alpha: 0 }, { duration: 100, delay: 100, onFinish: function onFinish() { self.active = false; self._clearBeatPulseLoop(); } }); }; self.failHold = function () { if (self.isMissed || self.holdSuccessfullyCompleted) { return; } self._clearBeatPulseLoop(); self.isMissed = true; self.isHolding = false; self.showMissEffect(); self.cleanupHoldVisuals(); }; return self; }); var Scanner = Container.expand(function () { var self = Container.call(this); self.glow = self.attachAsset('scannerGlow', { anchorX: 0, anchorY: 0.5 }); self.glow.alpha = 0.2; self.line = self.attachAsset('scannerLine', { anchorX: 0, anchorY: 0.5 }); self.x = 0; self.isMovingUp = true; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x000000 }); /**** * Game Code ****/ // Add these global variables (replace the previous ones) var globalParticleDirection = { x: 0, y: 1 }; // Start moving downward var targetParticleDirection = { x: 0, y: 1 }; var directionChangeStartTime = 0; var directionChangeDuration = 1000; // 1 second var isChangingDirection = false; var lastDirectionChangeTime = 0; var nextDirectionChangeDelay = 15000 + Math.random() * 15000; // 15-30 seconds var baseParticleSpeed = 1; // Base speed multiplier var countdownAcceleration = 1; // Separate from base speed var isCountdownActive = false; var GAME_WIDTH = 2048; var GAME_HEIGHT = 2732; var SAFETY_MARGIN_TOP_PERCENT = 0.05; var SAFETY_MARGIN_BOTTOM_PERCENT = 0.15; var SAFETY_MARGIN_SIDES_PERCENT = 0.10; var BPM = 120; var BEAT_DURATION_MS = 60 / BPM * 1000; var SCANNER_Y_MIN = GAME_HEIGHT * SAFETY_MARGIN_TOP_PERCENT; var SCANNER_Y_MAX = GAME_HEIGHT * (1 - SAFETY_MARGIN_BOTTOM_PERCENT); var PLAY_AREA_HEIGHT = SCANNER_Y_MAX - SCANNER_Y_MIN; var NOTE_SPAWN_AHEAD_MS = 800; var PERFECT_HIT_RATIO = 0.4; var POINTS_PERFECT_TAP = 30; var POINTS_GOOD_TAP = 15; var POINTS_PERFECT_HOLD_START = 10; var POINTS_GOOD_HOLD_START = 5; var ACCURACY_POPUP_DURATION = 750; var ACCURACY_POPUP_Y_OFFSET = -100; var ACCURACY_POPUP_FONT_SIZE = 80; var PERFECT_POPUP_COLOR = 0x00FF00; var GOOD_POPUP_COLOR = 0xFFFF00; var SCANNER_CYCLE_DURATION = 4000; var SCANNER_HALF_CYCLE = SCANNER_CYCLE_DURATION / 2; var HIT_TOLERANCE_PX_FOR_HOLD_END = 100; var HOLD_END_GRACE_PERIOD_MS = 250; var GAME_STATE_TITLE = 'title'; var GAME_STATE_SONG_SELECT = 'songSelect'; var GAME_STATE_PLAYING = 'playing'; var GAME_STATE_RESULTS = 'results'; var GAME_STATE_TUTORIAL = 'tutorial'; // New game state for the tutorial var currentGameState = GAME_STATE_TITLE; var resultsScreen = {}; var tutorialScreen = {}; // Holds tutorial screen elements var activeHoldNotes = {}; var scoreTxt; var countdownTxt; var perfectCount = 0; var goodCount = 0; var missCount = 0; var scanner; var scannerIsMovingUp = true; var gameStartTime; var gameStarted = false; var notes = []; var currentSongData; var spawnedNotes = []; var leftZone, rightZone; var isDragging = false; var dragStartX, dragStartY; var currentTrail = []; var backgroundParticles = []; var NUM_BACKGROUND_PARTICLES = 100; var titleScreen = {}; var songSelectScreen = {}; var songSelectScrollY = 0; var maxScrollY = 0; var isDraggingSongList = false; var dragStartYSongList = 0; var songSelectionInProgress = false; // Flag to prevent multiple song selections simultaneously var SONG_ITEM_HEIGHT = 330; var COLOR_LEFT_ZONE = 0x0077FF; var COLOR_RIGHT_ZONE = 0xFF3333; var COLOR_SYNC_TAP = 0xFF69B4; var COLOR_SYNC_HOLD = 0x9370DB; var SYNC_NOTE_COLOR = 0xFFA500; // Helper function to get current particle direction with smooth interpolation function getCurrentParticleDirection() { if (!isChangingDirection) { return globalParticleDirection; } var currentTime = Date.now(); var elapsed = currentTime - directionChangeStartTime; var progress = Math.min(elapsed / directionChangeDuration, 1); // Smooth easing var easedProgress = progress * progress * (3 - 2 * progress); // Smoothstep // Interpolate between current and target direction var lerpX = globalParticleDirection.x + (targetParticleDirection.x - globalParticleDirection.x) * easedProgress; var lerpY = globalParticleDirection.y + (targetParticleDirection.y - globalParticleDirection.y) * easedProgress; // Normalize the direction var length = Math.sqrt(lerpX * lerpX + lerpY * lerpY); if (length > 0) { lerpX /= length; lerpY /= length; } // Check if transition is complete if (progress >= 1) { isChangingDirection = false; globalParticleDirection.x = targetParticleDirection.x; globalParticleDirection.y = targetParticleDirection.y; lastDirectionChangeTime = currentTime; nextDirectionChangeDelay = 15000 + Math.random() * 15000; // 15-30 seconds } return { x: lerpX, y: lerpY }; } // Function to initiate a direction change function changeParticleDirection() { if (isChangingDirection) return; // Already changing // Generate a new direction 45-90 degrees from current var angleChange = Math.PI / 4 + Math.random() * (Math.PI / 4); // 45-90 degrees if (Math.random() < 0.5) angleChange = -angleChange; // Random direction var currentAngle = Math.atan2(globalParticleDirection.y, globalParticleDirection.x); var newAngle = currentAngle + angleChange; targetParticleDirection.x = Math.cos(newAngle); targetParticleDirection.y = Math.sin(newAngle); isChangingDirection = true; directionChangeStartTime = Date.now(); } var PatternTemplates = { tapLeft: function tapLeft(startTime) { return [{ time: startTime, type: 'tap', zone: 'left' }]; }, tapRight: function tapRight(startTime) { return [{ time: startTime, type: 'tap', zone: 'right' }]; }, syncTap: function syncTap(startTime) { return [{ time: startTime, type: 'tap', zone: 'left' }, { time: startTime, type: 'tap', zone: 'right' }]; }, leftRightTaps: function leftRightTaps(startTime, spacing) { spacing = spacing || 500; return [{ time: startTime, type: 'tap', zone: 'left' }, { time: startTime + spacing, type: 'tap', zone: 'right' }]; }, rightLeftTaps: function rightLeftTaps(startTime, spacing) { spacing = spacing || 500; return [{ time: startTime, type: 'tap', zone: 'right' }, { time: startTime + spacing, type: 'tap', zone: 'left' }]; }, holdLeft: function holdLeft(startTime, duration) { duration = duration || 1000; return [{ time: startTime, type: 'hold', zone: 'left', duration: duration }]; }, holdRight: function holdRight(startTime, duration) { duration = duration || 1000; return [{ time: startTime, type: 'hold', zone: 'right', duration: duration }]; }, syncHold: function syncHold(startTime, duration) { duration = duration || 1000; return [{ time: startTime, type: 'hold', zone: 'left', duration: duration }, { time: startTime, type: 'hold', zone: 'right', duration: duration }]; }, tripletTaps: function tripletTaps(startTime, zone, spacing) { spacing = spacing || 167; return [{ time: startTime, type: 'tap', zone: zone }, { time: startTime + spacing, type: 'tap', zone: zone }, { time: startTime + spacing * 2, type: 'tap', zone: zone }]; }, alternatingTriplets: function alternatingTriplets(startTime, spacing) { spacing = spacing || 167; return [{ time: startTime, type: 'tap', zone: 'left' }, { time: startTime + spacing, type: 'tap', zone: 'right' }, { time: startTime + spacing * 2, type: 'tap', zone: 'left' }]; }, buildUp: function buildUp(startTime) { return [{ time: startTime, type: 'tap', zone: 'left' }, { time: startTime + 250, type: 'tap', zone: 'right' }, { time: startTime + 500, type: 'tap', zone: 'left' }, { time: startTime + 625, type: 'tap', zone: 'right' }, { time: startTime + 750, type: 'tap', zone: 'left' }, { time: startTime + 875, type: 'tap', zone: 'right' }]; } }; var SongGenerator = { generateSong: function generateSong(config) { var notes = []; var totalLength = config.totalLength || 202136; var startTime = config.startDelay || 2000; notes = this.generateZoneAwareSong(startTime, totalLength); notes.sort(function (a, b) { return a.time - b.time; }); return notes; }, generateZoneAwareSong: function generateZoneAwareSong(startTime, songTotalLength) { var notes = []; var currentTime = startTime; var zoneFreeTime = { left: startTime, right: startTime }; var MIN_SAME_ZONE_SPACING = 400; var MIN_DIFFERENT_ZONE_SPACING = 200; var MIN_SYNC_SPACING = 1000; var lastSyncTime = startTime - MIN_SYNC_SPACING; var sections = [{ start: 0, end: 16000, complexity: 'simple' }, { start: 16000, end: 48000, complexity: 'medium' }, { start: 48000, end: 72000, complexity: 'medium' }, { start: 72000, end: 104000, complexity: 'medium' }, { start: 104000, end: 128000, complexity: 'complex' }, { start: 128000, end: 160000, complexity: 'medium' }, { start: 160000, end: 184000, complexity: 'complex' }, { start: 184000, end: songTotalLength, complexity: 'simple' }]; for (var s = 0; s < sections.length; s++) { var section = sections[s]; var sectionStartTime = startTime + section.start; var sectionEndTime = startTime + section.end; if (sectionEndTime > startTime + songTotalLength) { sectionEndTime = startTime + songTotalLength; } if (sectionStartTime >= sectionEndTime) { continue; } currentTime = Math.max(currentTime, sectionStartTime); while (currentTime < sectionEndTime - 1000) { var pattern = this.selectPattern(section.complexity, zoneFreeTime, currentTime, lastSyncTime); var patternResult = this.placePattern(pattern, currentTime, zoneFreeTime, MIN_SAME_ZONE_SPACING, MIN_DIFFERENT_ZONE_SPACING, sectionEndTime); if (patternResult.notes.length > 0) { var allNotesFit = true; for (var ni = 0; ni < patternResult.notes.length; ni++) { if (patternResult.notes[ni].time + (patternResult.notes[ni].duration || 0) > sectionEndTime - 200) { allNotesFit = false; break; } } if (allNotesFit) { notes = notes.concat(patternResult.notes); currentTime = patternResult.nextTime; if (pattern.type === 'sync' || pattern.type === 'syncHold') { lastSyncTime = patternResult.notes[0].time; } } else { currentTime += 300; } } else { currentTime += 300; } if (currentTime >= sectionEndTime - 1000) { break; } } } return notes; }, selectPattern: function selectPattern(complexity, zoneFreeTime, currentTime, lastSyncTime) { var patterns = []; var MIN_SYNC_SPACING_FOR_SELECTION = 1000; if (complexity === 'simple') { patterns = [{ type: 'single', zone: 'left', duration: 800 }, { type: 'single', zone: 'right', duration: 800 }, { type: 'rest', duration: 1200 }, { type: 'rest', duration: 1200 }]; if (currentTime - lastSyncTime >= MIN_SYNC_SPACING_FOR_SELECTION + 200) { patterns.push({ type: 'sync', duration: 1000 }); } } else if (complexity === 'medium') { patterns = [{ type: 'hold', zone: 'right', holdDuration: 1000, duration: 700 }, { type: 'single', zone: 'left', duration: 600 }, { type: 'single', zone: 'right', duration: 600 }, { type: 'single', zone: 'left', duration: 600 }, { type: 'single', zone: 'right', duration: 600 }, { type: 'alternating', duration: 750 }, { type: 'rest', duration: 1000 }]; if (currentTime - lastSyncTime >= MIN_SYNC_SPACING_FOR_SELECTION) { patterns.push({ type: 'sync', duration: 800 }); patterns.push({ type: 'sync', duration: 800 }); } } else { patterns = [{ type: 'hold', zone: 'right', holdDuration: 1200, duration: 800 }, { type: 'alternating', duration: 800 }, { type: 'alternating', duration: 800 }, { type: 'single', zone: 'left', duration: 700 }, { type: 'single', zone: 'right', duration: 700 }, { type: 'single', zone: 'left', duration: 700 }, { type: 'single', zone: 'right', duration: 700 }]; if (currentTime - lastSyncTime >= MIN_SYNC_SPACING_FOR_SELECTION - 200) { patterns.push({ type: 'sync', duration: 600 }); patterns.push({ type: 'sync', duration: 600 }); patterns.push({ type: 'syncHold', holdDuration: 1200, duration: 900 }); } } return patterns[Math.floor(Math.random() * patterns.length)]; }, placePattern: function placePattern(pattern, requestedTime, zoneFreeTime, minSameZoneSpacing, minDiffZoneSpacing, sectionEndTime) { var notes = []; var earliestTime = requestedTime; var nextTimeAdvance = pattern.duration || 500; if (pattern.type === 'single') { earliestTime = Math.max(requestedTime, zoneFreeTime[pattern.zone]); if (earliestTime + (pattern.duration || 0) < sectionEndTime) { notes.push({ time: earliestTime, type: 'tap', zone: pattern.zone }); zoneFreeTime[pattern.zone] = earliestTime + minSameZoneSpacing; return { notes: notes, nextTime: earliestTime + nextTimeAdvance }; } } else if (pattern.type === 'sync') { earliestTime = Math.max(requestedTime, zoneFreeTime.left, zoneFreeTime.right); if (earliestTime + (pattern.duration || 0) < sectionEndTime) { notes.push({ time: earliestTime, type: 'tap', zone: 'left', color: SYNC_NOTE_COLOR }); notes.push({ time: earliestTime, type: 'tap', zone: 'right', color: SYNC_NOTE_COLOR }); zoneFreeTime.left = earliestTime + minSameZoneSpacing; zoneFreeTime.right = earliestTime + minSameZoneSpacing; return { notes: notes, nextTime: earliestTime + nextTimeAdvance }; } } else if (pattern.type === 'alternating') { var firstZone = Math.random() < 0.5 ? 'left' : 'right'; var secondZone = firstZone === 'left' ? 'right' : 'left'; var t1 = Math.max(requestedTime, zoneFreeTime[firstZone]); var t2 = Math.max(t1 + minDiffZoneSpacing, zoneFreeTime[secondZone]); if (t2 + (pattern.duration || 0) - (t2 - t1) < sectionEndTime) { notes.push({ time: t1, type: 'tap', zone: firstZone }); notes.push({ time: t2, type: 'tap', zone: secondZone }); zoneFreeTime[firstZone] = t1 + minSameZoneSpacing; zoneFreeTime[secondZone] = t2 + minSameZoneSpacing; return { notes: notes, nextTime: t1 + nextTimeAdvance }; } } else if (pattern.type === 'syncHold') { earliestTime = Math.max(requestedTime, zoneFreeTime.left, zoneFreeTime.right); var holdDuration = pattern.holdDuration || 1200; if (earliestTime + holdDuration < sectionEndTime) { notes.push({ time: earliestTime, type: 'hold', zone: 'left', duration: holdDuration, color: SYNC_NOTE_COLOR }); notes.push({ time: earliestTime, type: 'hold', zone: 'right', duration: holdDuration, color: SYNC_NOTE_COLOR }); zoneFreeTime.left = earliestTime + holdDuration + 200; zoneFreeTime.right = earliestTime + holdDuration + 200; return { notes: notes, nextTime: earliestTime + (pattern.duration || 800) }; } } else if (pattern.type === 'hold') { earliestTime = Math.max(requestedTime, zoneFreeTime[pattern.zone]); var holdDuration = pattern.holdDuration || 1500; if (earliestTime + holdDuration < sectionEndTime) { notes.push({ time: earliestTime, type: 'hold', zone: pattern.zone, duration: holdDuration }); var otherZone = pattern.zone === 'left' ? 'right' : 'left'; zoneFreeTime[pattern.zone] = earliestTime + holdDuration + 200; zoneFreeTime[otherZone] = Math.max(zoneFreeTime[otherZone], earliestTime + holdDuration + minDiffZoneSpacing); return { notes: notes, nextTime: earliestTime + nextTimeAdvance }; } } else if (pattern.type === 'rest') { return { notes: [], nextTime: requestedTime + (pattern.duration || 500) }; } return { notes: [], nextTime: requestedTime + 100 }; } }; function _createTutorialText(text, size, yPos, parent, fill, align, customX) { var textObj = new Text2(text, { size: size, fill: fill || 0xFFFFFF, wordWrap: true, wordWrapWidth: GAME_WIDTH - 200, // 100px padding each side align: align || 'left' }); if (align === 'center') { textObj.anchor.set(0.5, 0); textObj.x = customX || GAME_WIDTH / 2; } else { // left align textObj.anchor.set(0, 0); textObj.x = customX || 100; } textObj.y = yPos; parent.addChild(textObj); return textObj; } function _createTutorialNoteVisual(assetId, color, xPos, yPos, scale, parent) { var noteVisual = LK.getAsset(assetId, { anchorX: 0.5, anchorY: 0.5, scaleX: scale || 0.35, scaleY: scale || 0.35 }); noteVisual.tint = color; noteVisual.x = xPos; noteVisual.y = yPos; parent.addChild(noteVisual); return noteVisual; } var SongDatabase = { 'Remember This': { bpm: 99, scannerCycleDuration: 4000, totalLength: 205078, highScore: storage.highScores && storage.highScores['Remember This'] || 0, highestGrade: storage.highestGrades && storage.highestGrades['Remember This'] || 'C', notes: [{ "time": 673, "type": "tap", "zone": "right" }, { "time": 1277, "type": "tap", "zone": "left" }, { "time": 2461, "type": "tap", "zone": "left" }, { "time": 3065, "type": "tap", "zone": "right" }, { "time": 4249, "type": "tap", "zone": "right" }, { "time": 4852, "type": "tap", "zone": "left" }, { "time": 6060, "type": "tap", "zone": "left" }, { "time": 6664, "type": "tap", "zone": "right" }, { "time": 7848, "type": "tap", "zone": "right" }, { "time": 8452, "type": "tap", "zone": "left" }, { "time": 9659, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 9659, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 10263, "type": "tap", "zone": "right" }, { "time": 10866, "type": "hold", "zone": "right", "duration": 1000 }, { "time": 12654, "type": "tap", "zone": "right" }, { "time": 13258, "type": "tap", "zone": "left" }, { "time": 13862, "type": "tap", "zone": "right" }, { "time": 14466, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 14466, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 15046, "type": "tap", "zone": "right" }, { "time": 15650, "type": "tap", "zone": "left" }, { "time": 16253, "type": "tap", "zone": "right" }, { "time": 16857, "type": "tap", "zone": "left" }, { "time": 17257, "type": "tap", "zone": "right" }, { "time": 18065, "type": "hold", "zone": "left", "duration": 1000 }, { "time": 19853, "type": "tap", "zone": "right" }, { "time": 20456, "type": "tap", "zone": "left" }, { "time": 21037, "type": "tap", "zone": "right" }, { "time": 21640, "type": "tap", "zone": "left" }, { "time": 22244, "type": "tap", "zone": "right" }, { "time": 22848, "type": "tap", "zone": "left" }, { "time": 24055, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 24055, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 24659, "type": "tap", "zone": "right" }, { "time": 25263, "type": "hold", "zone": "right", "duration": 1000 }, { "time": 27051, "type": "tap", "zone": "right" }, { "time": 27631, "type": "tap", "zone": "left" }, { "time": 28258, "type": "tap", "zone": "right" }, { "time": 28862, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 28862, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 29466, "type": "tap", "zone": "right" }, { "time": 30046, "type": "tap", "zone": "left" }, { "time": 31254, "type": "tap", "zone": "left" }, { "time": 31857, "type": "tap", "zone": "right" }, { "time": 32461, "type": "hold", "zone": "left", "duration": 1000 }, { "time": 34249, "type": "tap", "zone": "right" }, { "time": 34853, "type": "tap", "zone": "left" }, { "time": 35456, "type": "tap", "zone": "right" }, { "time": 36060, "type": "tap", "zone": "left" }, { "time": 36641, "type": "tap", "zone": "right" }, { "time": 37244, "type": "tap", "zone": "left" }, { "time": 37848, "type": "tap", "zone": "right" }, { "time": 38475, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 38475, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 39102, "type": "tap", "zone": "right" }, { "time": 39775, "type": "hold", "zone": "right", "duration": 1000 }, { "time": 41076, "type": "tap", "zone": "left" }, { "time": 42353, "type": "tap", "zone": "left" }, { "time": 42956, "type": "tap", "zone": "right" }, { "time": 44141, "type": "tap", "zone": "right" }, { "time": 44721, "type": "tap", "zone": "left" }, { "time": 45325, "type": "tap", "zone": "right" }, { "time": 45882, "type": "tap", "zone": "left" }, { "time": 46439, "type": "tap", "zone": "right" }, { "time": 47531, "type": "tap", "zone": "right" }, { "time": 48065, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 48065, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 48645, "type": "tap", "zone": "right" }, { "time": 49249, "type": "tap", "zone": "left" }, { "time": 49853, "type": "tap", "zone": "right" }, { "time": 50456, "type": "tap", "zone": "left" }, { "time": 50856, "type": "tap", "zone": "right" }, { "time": 51664, "type": "tap", "zone": "left" }, { "time": 52244, "type": "tap", "zone": "right" }, { "time": 52848, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 52848, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 53452, "type": "tap", "zone": "right" }, { "time": 54056, "type": "tap", "zone": "left" }, { "time": 54659, "type": "tap", "zone": "right" }, { "time": 55240, "type": "tap", "zone": "left" }, { "time": 55640, "type": "tap", "zone": "right" }, { "time": 56447, "type": "tap", "zone": "left" }, { "time": 57051, "type": "tap", "zone": "right" }, { "time": 57655, "type": "hold", "zone": "left", "duration": 1500, "color": 16753920 }, { "time": 57655, "type": "hold", "zone": "right", "duration": 1500, "color": 16753920 }, { "time": 59443, "type": "tap", "zone": "right" }, { "time": 60046, "type": "tap", "zone": "left" }, { "time": 60446, "type": "tap", "zone": "right" }, { "time": 61254, "type": "tap", "zone": "left" }, { "time": 61857, "type": "tap", "zone": "right" }, { "time": 62461, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 62461, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 63065, "type": "tap", "zone": "right" }, { "time": 63645, "type": "tap", "zone": "left" }, { "time": 64249, "type": "tap", "zone": "right" }, { "time": 64853, "type": "tap", "zone": "left" }, { "time": 65457, "type": "tap", "zone": "right" }, { "time": 66060, "type": "tap", "zone": "left" }, { "time": 66664, "type": "tap", "zone": "right" }, { "time": 67244, "type": "hold", "zone": "left", "duration": 1500, "color": 16753920 }, { "time": 67244, "type": "hold", "zone": "right", "duration": 1500, "color": 16753920 }, { "time": 69056, "type": "tap", "zone": "right" }, { "time": 69659, "type": "tap", "zone": "left" }, { "time": 70263, "type": "tap", "zone": "right" }, { "time": 70867, "type": "tap", "zone": "left" }, { "time": 71447, "type": "tap", "zone": "right" }, { "time": 72051, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 72051, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 72655, "type": "tap", "zone": "right" }, { "time": 73258, "type": "tap", "zone": "left" }, { "time": 73862, "type": "tap", "zone": "right" }, { "time": 74466, "type": "tap", "zone": "left" }, { "time": 75046, "type": "tap", "zone": "right" }, { "time": 75650, "type": "hold", "zone": "left", "duration": 1000 }, { "time": 76858, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 76858, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 78065, "type": "tap", "zone": "left" }, { "time": 78669, "type": "tap", "zone": "right" }, { "time": 79876, "type": "tap", "zone": "right" }, { "time": 80457, "type": "tap", "zone": "left" }, { "time": 81664, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 81664, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 82245, "type": "tap", "zone": "right" }, { "time": 82848, "type": "hold", "zone": "right", "duration": 1000 }, { "time": 84056, "type": "tap", "zone": "left" }, { "time": 84636, "type": "tap", "zone": "right" }, { "time": 85240, "type": "tap", "zone": "left" }, { "time": 85844, "type": "tap", "zone": "right" }, { "time": 86447, "type": "hold", "zone": "left", "duration": 1500, "color": 16753920 }, { "time": 86447, "type": "hold", "zone": "right", "duration": 1500, "color": 16753920 }, { "time": 88259, "type": "tap", "zone": "right" }, { "time": 88862, "type": "tap", "zone": "left" }, { "time": 89443, "type": "tap", "zone": "right" }, { "time": 90046, "type": "hold", "zone": "left", "duration": 1000 }, { "time": 91254, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 91254, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 91858, "type": "tap", "zone": "right" }, { "time": 92461, "type": "tap", "zone": "left" }, { "time": 93042, "type": "tap", "zone": "right" }, { "time": 93646, "type": "tap", "zone": "left" }, { "time": 94249, "type": "tap", "zone": "right" }, { "time": 94853, "type": "tap", "zone": "left" }, { "time": 95457, "type": "tap", "zone": "right" }, { "time": 96060, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 96060, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 96641, "type": "tap", "zone": "right" }, { "time": 97245, "type": "hold", "zone": "right", "duration": 1000 }, { "time": 98452, "type": "tap", "zone": "left" }, { "time": 99056, "type": "tap", "zone": "right" }, { "time": 99660, "type": "tap", "zone": "left" }, { "time": 100240, "type": "tap", "zone": "right" }, { "time": 100844, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 100844, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 101447, "type": "tap", "zone": "right" }, { "time": 102028, "type": "tap", "zone": "left" }, { "time": 103235, "type": "tap", "zone": "left" }, { "time": 103862, "type": "tap", "zone": "right" }, { "time": 105116, "type": "tap", "zone": "right" }, { "time": 105743, "type": "tap", "zone": "left" }, { "time": 106881, "type": "tap", "zone": "left" }, { "time": 107461, "type": "tap", "zone": "right" }, { "time": 108042, "type": "tap", "zone": "left" }, { "time": 108646, "type": "tap", "zone": "right" }, { "time": 109249, "type": "tap", "zone": "left" }, { "time": 109853, "type": "tap", "zone": "right" }, { "time": 110457, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 110457, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 111061, "type": "tap", "zone": "right" }, { "time": 111664, "type": "tap", "zone": "left" }, { "time": 112245, "type": "tap", "zone": "right" }, { "time": 112848, "type": "tap", "zone": "left" }, { "time": 113248, "type": "tap", "zone": "right" }, { "time": 114056, "type": "tap", "zone": "left" }, { "time": 114660, "type": "tap", "zone": "right" }, { "time": 115263, "type": "hold", "zone": "left", "duration": 1500, "color": 16753920 }, { "time": 115263, "type": "hold", "zone": "right", "duration": 1500, "color": 16753920 }, { "time": 117051, "type": "tap", "zone": "right" }, { "time": 117655, "type": "tap", "zone": "left" }, { "time": 118055, "type": "tap", "zone": "right" }, { "time": 118862, "type": "hold", "zone": "left", "duration": 1000 }, { "time": 120650, "type": "tap", "zone": "right" }, { "time": 121254, "type": "tap", "zone": "left" }, { "time": 121858, "type": "tap", "zone": "right" }, { "time": 122462, "type": "tap", "zone": "left" }, { "time": 122862, "type": "tap", "zone": "right" }, { "time": 123646, "type": "tap", "zone": "left" }, { "time": 124249, "type": "tap", "zone": "right" }, { "time": 124853, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 124853, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 125457, "type": "tap", "zone": "right" }, { "time": 126061, "type": "hold", "zone": "right", "duration": 1000 }, { "time": 128452, "type": "tap", "zone": "left" }, { "time": 129056, "type": "tap", "zone": "right" }, { "time": 129660, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 129660, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 130263, "type": "tap", "zone": "right" }, { "time": 130867, "type": "tap", "zone": "left" }, { "time": 131448, "type": "tap", "zone": "right" }, { "time": 132051, "type": "tap", "zone": "left" }, { "time": 132451, "type": "tap", "zone": "right" }, { "time": 133259, "type": "hold", "zone": "left", "duration": 1000 }, { "time": 134466, "type": "hold", "zone": "left", "duration": 1500, "color": 16753920 }, { "time": 134466, "type": "hold", "zone": "right", "duration": 1500, "color": 16753920 }, { "time": 136254, "type": "tap", "zone": "right" }, { "time": 136858, "type": "tap", "zone": "left" }, { "time": 137258, "type": "tap", "zone": "right" }, { "time": 138042, "type": "tap", "zone": "left" }, { "time": 138646, "type": "tap", "zone": "right" }, { "time": 139250, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 139250, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 139853, "type": "tap", "zone": "right" }, { "time": 140457, "type": "hold", "zone": "right", "duration": 1000 }, { "time": 141664, "type": "tap", "zone": "left" }, { "time": 142245, "type": "tap", "zone": "right" }, { "time": 142849, "type": "tap", "zone": "left" }, { "time": 143452, "type": "tap", "zone": "right" }, { "time": 144056, "type": "hold", "zone": "left", "duration": 1500, "color": 16753920 }, { "time": 144056, "type": "hold", "zone": "right", "duration": 1500, "color": 16753920 }, { "time": 145844, "type": "tap", "zone": "right" }, { "time": 146448, "type": "tap", "zone": "left" }, { "time": 147051, "type": "tap", "zone": "right" }, { "time": 148259, "type": "tap", "zone": "right" }, { "time": 148839, "type": "tap", "zone": "left" }, { "time": 150070, "type": "tap", "zone": "left" }, { "time": 150697, "type": "tap", "zone": "right" }, { "time": 151904, "type": "tap", "zone": "right" }, { "time": 152462, "type": "tap", "zone": "left" }, { "time": 153669, "type": "tap", "zone": "left" }, { "time": 154273, "type": "tap", "zone": "right" }, { "time": 155480, "type": "tap", "zone": "right" }, { "time": 156084, "type": "tap", "zone": "left" }, { "time": 157268, "type": "tap", "zone": "left" }, { "time": 157872, "type": "tap", "zone": "right" }, { "time": 159056, "type": "tap", "zone": "right" }, { "time": 159613, "type": "tap", "zone": "left" }, { "time": 160264, "type": "tap", "zone": "right" }, { "time": 160867, "type": "tap", "zone": "left" }, { "time": 161448, "type": "tap", "zone": "right" }, { "time": 162052, "type": "hold", "zone": "left", "duration": 1000 }, { "time": 163259, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 163259, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 163863, "type": "tap", "zone": "right" }, { "time": 164420, "type": "tap", "zone": "left" }, { "time": 164977, "type": "tap", "zone": "right" }, { "time": 165535, "type": "tap", "zone": "left" }, { "time": 166115, "type": "tap", "zone": "right" }, { "time": 166719, "type": "tap", "zone": "left" }, { "time": 167903, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 167903, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 168530, "type": "tap", "zone": "right" }, { "time": 169250, "type": "hold", "zone": "right", "duration": 1000 }, { "time": 170457, "type": "tap", "zone": "left" }, { "time": 170857, "type": "tap", "zone": "right" }, { "time": 171665, "type": "tap", "zone": "left" }, { "time": 172245, "type": "tap", "zone": "right" }, { "time": 172849, "type": "hold", "zone": "left", "duration": 1500, "color": 16753920 }, { "time": 172849, "type": "hold", "zone": "right", "duration": 1500, "color": 16753920 }, { "time": 174660, "type": "tap", "zone": "right" }, { "time": 175264, "type": "tap", "zone": "left" }, { "time": 175664, "type": "tap", "zone": "right" }, { "time": 176448, "type": "tap", "zone": "left" }, { "time": 177052, "type": "tap", "zone": "right" }, { "time": 177655, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 177655, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 178259, "type": "tap", "zone": "right" }, { "time": 178863, "type": "tap", "zone": "left" }, { "time": 179443, "type": "tap", "zone": "right" }, { "time": 180047, "type": "tap", "zone": "left" }, { "time": 180447, "type": "tap", "zone": "right" }, { "time": 181254, "type": "tap", "zone": "left" }, { "time": 181858, "type": "tap", "zone": "right" }, { "time": 182532, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 182532, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 183205, "type": "tap", "zone": "right" }, { "time": 184528, "type": "tap", "zone": "right" }, { "time": 185155, "type": "tap", "zone": "left" }, { "time": 185555, "type": "tap", "zone": "right" }, { "time": 186363, "type": "tap", "zone": "left" }, { "time": 186967, "type": "tap", "zone": "right" }, { "time": 187547, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 187547, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 188151, "type": "tap", "zone": "right" }, { "time": 188755, "type": "tap", "zone": "left" }, { "time": 189358, "type": "tap", "zone": "right" }, { "time": 189962, "type": "tap", "zone": "left" }, { "time": 190566, "type": "tap", "zone": "right" }, { "time": 191146, "type": "hold", "zone": "left", "duration": 1000 }, { "time": 192354, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 192354, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 192957, "type": "tap", "zone": "right" }, { "time": 193561, "type": "tap", "zone": "left" }, { "time": 194165, "type": "tap", "zone": "right" }, { "time": 194745, "type": "tap", "zone": "left" }, { "time": 195349, "type": "tap", "zone": "right" }, { "time": 195953, "type": "tap", "zone": "left" }, { "time": 196556, "type": "tap", "zone": "right" }, { "time": 197160, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 197160, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 197764, "type": "tap", "zone": "right" }, { "time": 198344, "type": "hold", "zone": "right", "duration": 1000 }, { "time": 199552, "type": "tap", "zone": "left" }, { "time": 200156, "type": "tap", "zone": "right" }, { "time": 200759, "type": "tap", "zone": "left" }] }, 'Arcade Heart': { bpm: 95, scannerCycleDuration: 4000, totalLength: 192446, highScore: storage.highScores && storage.highScores['Arcade Heart'] || 0, highestGrade: storage.highestGrades && storage.highestGrades['Arcade Heart'] || 'C', notes: [{ "time": 1602, "type": "tap", "zone": "right" }, { "time": 2229, "type": "tap", "zone": "left" }, { "time": 3482, "type": "tap", "zone": "left" }, { "time": 4133, "type": "tap", "zone": "right" }, { "time": 5387, "type": "tap", "zone": "right" }, { "time": 6013, "type": "tap", "zone": "left" }, { "time": 7291, "type": "tap", "zone": "left" }, { "time": 7918, "type": "tap", "zone": "right" }, { "time": 9171, "type": "tap", "zone": "right" }, { "time": 9798, "type": "tap", "zone": "left" }, { "time": 11052, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 11052, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 11702, "type": "tap", "zone": "right" }, { "time": 12329, "type": "hold", "zone": "right", "duration": 1000 }, { "time": 13583, "type": "tap", "zone": "left" }, { "time": 14210, "type": "tap", "zone": "right" }, { "time": 14837, "type": "tap", "zone": "left" }, { "time": 15487, "type": "tap", "zone": "right" }, { "time": 16114, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 16114, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 16741, "type": "tap", "zone": "right" }, { "time": 17368, "type": "tap", "zone": "left" }, { "time": 18018, "type": "tap", "zone": "right" }, { "time": 18622, "type": "tap", "zone": "left" }, { "time": 19272, "type": "tap", "zone": "right" }, { "time": 19899, "type": "hold", "zone": "left", "duration": 1000 }, { "time": 21153, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 21153, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 21803, "type": "tap", "zone": "right" }, { "time": 22430, "type": "tap", "zone": "left" }, { "time": 23057, "type": "tap", "zone": "right" }, { "time": 23684, "type": "tap", "zone": "left" }, { "time": 24334, "type": "tap", "zone": "right" }, { "time": 24938, "type": "tap", "zone": "left" }, { "time": 25588, "type": "tap", "zone": "right" }, { "time": 26215, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 26215, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 26865, "type": "tap", "zone": "right" }, { "time": 27492, "type": "hold", "zone": "right", "duration": 1000 }, { "time": 28746, "type": "tap", "zone": "left" }, { "time": 29373, "type": "tap", "zone": "right" }, { "time": 30000, "type": "tap", "zone": "left" }, { "time": 30650, "type": "tap", "zone": "right" }, { "time": 31277, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 31277, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 31904, "type": "tap", "zone": "right" }, { "time": 32531, "type": "tap", "zone": "left" }, { "time": 33181, "type": "tap", "zone": "right" }, { "time": 33808, "type": "tap", "zone": "left" }, { "time": 34435, "type": "tap", "zone": "right" }, { "time": 35062, "type": "hold", "zone": "left", "duration": 1000 }, { "time": 36316, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 36316, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 36966, "type": "tap", "zone": "right" }, { "time": 37593, "type": "tap", "zone": "left" }, { "time": 38220, "type": "tap", "zone": "right" }, { "time": 38846, "type": "tap", "zone": "left" }, { "time": 39497, "type": "tap", "zone": "right" }, { "time": 40124, "type": "tap", "zone": "left" }, { "time": 40751, "type": "tap", "zone": "right" }, { "time": 41377, "type": "hold", "zone": "left", "duration": 1500, "color": 16753920 }, { "time": 41377, "type": "hold", "zone": "right", "duration": 1500, "color": 16753920 }, { "time": 43281, "type": "tap", "zone": "right" }, { "time": 43908, "type": "tap", "zone": "left" }, { "time": 44535, "type": "tap", "zone": "right" }, { "time": 45162, "type": "tap", "zone": "left" }, { "time": 45812, "type": "tap", "zone": "right" }, { "time": 46439, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 46439, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 47066, "type": "tap", "zone": "right" }, { "time": 47693, "type": "tap", "zone": "left" }, { "time": 48320, "type": "tap", "zone": "right" }, { "time": 48947, "type": "tap", "zone": "left" }, { "time": 49347, "type": "tap", "zone": "right" }, { "time": 50224, "type": "tap", "zone": "left" }, { "time": 50851, "type": "tap", "zone": "right" }, { "time": 51478, "type": "hold", "zone": "left", "duration": 1500, "color": 16753920 }, { "time": 51478, "type": "hold", "zone": "right", "duration": 1500, "color": 16753920 }, { "time": 53382, "type": "tap", "zone": "right" }, { "time": 54009, "type": "tap", "zone": "left" }, { "time": 54409, "type": "tap", "zone": "right" }, { "time": 55263, "type": "tap", "zone": "left" }, { "time": 55913, "type": "tap", "zone": "right" }, { "time": 56540, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 56540, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 57167, "type": "tap", "zone": "right" }, { "time": 57794, "type": "tap", "zone": "left" }, { "time": 58444, "type": "tap", "zone": "right" }, { "time": 59071, "type": "tap", "zone": "left" }, { "time": 59698, "type": "tap", "zone": "right" }, { "time": 60325, "type": "tap", "zone": "left" }, { "time": 60952, "type": "tap", "zone": "right" }, { "time": 61602, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 61602, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 62229, "type": "tap", "zone": "right" }, { "time": 62856, "type": "tap", "zone": "left" }, { "time": 63483, "type": "tap", "zone": "right" }, { "time": 64110, "type": "tap", "zone": "left" }, { "time": 64510, "type": "tap", "zone": "right" }, { "time": 65387, "type": "tap", "zone": "left" }, { "time": 66014, "type": "tap", "zone": "right" }, { "time": 66641, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 66641, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 67268, "type": "tap", "zone": "right" }, { "time": 67918, "type": "tap", "zone": "left" }, { "time": 68545, "type": "tap", "zone": "right" }, { "time": 69799, "type": "tap", "zone": "right" }, { "time": 70426, "type": "tap", "zone": "left" }, { "time": 71703, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 71703, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 72330, "type": "tap", "zone": "right" }, { "time": 72957, "type": "hold", "zone": "right", "duration": 1000 }, { "time": 74210, "type": "tap", "zone": "left" }, { "time": 74837, "type": "tap", "zone": "right" }, { "time": 75488, "type": "tap", "zone": "left" }, { "time": 76115, "type": "tap", "zone": "right" }, { "time": 76741, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 76741, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 77392, "type": "tap", "zone": "right" }, { "time": 78019, "type": "tap", "zone": "left" }, { "time": 78645, "type": "tap", "zone": "right" }, { "time": 79272, "type": "tap", "zone": "left" }, { "time": 79899, "type": "tap", "zone": "right" }, { "time": 80526, "type": "hold", "zone": "left", "duration": 1000 }, { "time": 81803, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 81803, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 82430, "type": "tap", "zone": "right" }, { "time": 83057, "type": "tap", "zone": "left" }, { "time": 83684, "type": "tap", "zone": "right" }, { "time": 84311, "type": "tap", "zone": "left" }, { "time": 84961, "type": "tap", "zone": "right" }, { "time": 85588, "type": "tap", "zone": "left" }, { "time": 86215, "type": "tap", "zone": "right" }, { "time": 86842, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 86842, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 87492, "type": "tap", "zone": "right" }, { "time": 88119, "type": "hold", "zone": "right", "duration": 1000 }, { "time": 89373, "type": "tap", "zone": "left" }, { "time": 90000, "type": "tap", "zone": "right" }, { "time": 90650, "type": "tap", "zone": "left" }, { "time": 91277, "type": "tap", "zone": "right" }, { "time": 91904, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 91904, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 92531, "type": "tap", "zone": "right" }, { "time": 93158, "type": "tap", "zone": "left" }, { "time": 93808, "type": "tap", "zone": "right" }, { "time": 94435, "type": "tap", "zone": "left" }, { "time": 95062, "type": "tap", "zone": "right" }, { "time": 95689, "type": "hold", "zone": "left", "duration": 1000 }, { "time": 96966, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 96966, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 97593, "type": "tap", "zone": "right" }, { "time": 98220, "type": "tap", "zone": "left" }, { "time": 98870, "type": "tap", "zone": "right" }, { "time": 100101, "type": "tap", "zone": "right" }, { "time": 100751, "type": "tap", "zone": "left" }, { "time": 101378, "type": "tap", "zone": "right" }, { "time": 102005, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 102005, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 102632, "type": "tap", "zone": "right" }, { "time": 103282, "type": "hold", "zone": "right", "duration": 1000 }, { "time": 104536, "type": "tap", "zone": "left" }, { "time": 104936, "type": "tap", "zone": "right" }, { "time": 105790, "type": "tap", "zone": "left" }, { "time": 106440, "type": "tap", "zone": "right" }, { "time": 107067, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 107067, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 107694, "type": "tap", "zone": "right" }, { "time": 108321, "type": "tap", "zone": "left" }, { "time": 108948, "type": "tap", "zone": "right" }, { "time": 109574, "type": "tap", "zone": "left" }, { "time": 109974, "type": "tap", "zone": "right" }, { "time": 110852, "type": "tap", "zone": "left" }, { "time": 111479, "type": "tap", "zone": "right" }, { "time": 112129, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 112129, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 112756, "type": "tap", "zone": "right" }, { "time": 113383, "type": "tap", "zone": "left" }, { "time": 114009, "type": "tap", "zone": "right" }, { "time": 114636, "type": "tap", "zone": "left" }, { "time": 115036, "type": "tap", "zone": "right" }, { "time": 115890, "type": "tap", "zone": "left" }, { "time": 116540, "type": "tap", "zone": "right" }, { "time": 117167, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 117167, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 117794, "type": "tap", "zone": "right" }, { "time": 118421, "type": "tap", "zone": "left" }, { "time": 119071, "type": "tap", "zone": "right" }, { "time": 119698, "type": "tap", "zone": "left" }, { "time": 120302, "type": "tap", "zone": "right" }, { "time": 120952, "type": "tap", "zone": "left" }, { "time": 121602, "type": "tap", "zone": "right" }, { "time": 122229, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 122229, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 122856, "type": "tap", "zone": "right" }, { "time": 123483, "type": "tap", "zone": "left" }, { "time": 124110, "type": "tap", "zone": "right" }, { "time": 124737, "type": "tap", "zone": "left" }, { "time": 125137, "type": "tap", "zone": "right" }, { "time": 126014, "type": "tap", "zone": "left" }, { "time": 126641, "type": "tap", "zone": "right" }, { "time": 127268, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 127268, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 127895, "type": "tap", "zone": "right" }, { "time": 128522, "type": "tap", "zone": "left" }, { "time": 129172, "type": "tap", "zone": "right" }, { "time": 129799, "type": "tap", "zone": "left" }, { "time": 130199, "type": "tap", "zone": "right" }, { "time": 131053, "type": "tap", "zone": "left" }, { "time": 131703, "type": "tap", "zone": "right" }, { "time": 132330, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 132330, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 132957, "type": "tap", "zone": "right" }, { "time": 133584, "type": "tap", "zone": "left" }, { "time": 134211, "type": "tap", "zone": "right" }, { "time": 134838, "type": "tap", "zone": "left" }, { "time": 135238, "type": "tap", "zone": "right" }, { "time": 136115, "type": "tap", "zone": "left" }, { "time": 136672, "type": "tap", "zone": "right" }, { "time": 137856, "type": "tap", "zone": "right" }, { "time": 138483, "type": "tap", "zone": "left" }, { "time": 139760, "type": "tap", "zone": "left" }, { "time": 140387, "type": "tap", "zone": "right" }, { "time": 141664, "type": "tap", "zone": "right" }, { "time": 142291, "type": "tap", "zone": "left" }, { "time": 143592, "type": "tap", "zone": "left" }, { "time": 144288, "type": "tap", "zone": "right" }, { "time": 145705, "type": "tap", "zone": "right" }, { "time": 146308, "type": "tap", "zone": "left" }, { "time": 147516, "type": "tap", "zone": "left" }, { "time": 148143, "type": "tap", "zone": "right" }, { "time": 149397, "type": "tap", "zone": "right" }, { "time": 150024, "type": "tap", "zone": "left" }, { "time": 151278, "type": "tap", "zone": "left" }, { "time": 151904, "type": "tap", "zone": "right" }, { "time": 152531, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 152531, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 153205, "type": "tap", "zone": "right" }, { "time": 153948, "type": "tap", "zone": "left" }, { "time": 154598, "type": "tap", "zone": "right" }, { "time": 155225, "type": "tap", "zone": "left" }, { "time": 155852, "type": "tap", "zone": "right" }, { "time": 156502, "type": "tap", "zone": "left" }, { "time": 157129, "type": "tap", "zone": "right" }, { "time": 157756, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 157756, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 158383, "type": "tap", "zone": "right" }, { "time": 159010, "type": "tap", "zone": "left" }, { "time": 159567, "type": "tap", "zone": "right" }, { "time": 160682, "type": "tap", "zone": "right" }, { "time": 161378, "type": "tap", "zone": "left" }, { "time": 162005, "type": "tap", "zone": "right" }, { "time": 162655, "type": "hold", "zone": "left", "duration": 1500, "color": 16753920 }, { "time": 162655, "type": "hold", "zone": "right", "duration": 1500, "color": 16753920 }, { "time": 164536, "type": "tap", "zone": "right" }, { "time": 165163, "type": "tap", "zone": "left" }, { "time": 165563, "type": "tap", "zone": "right" }, { "time": 166440, "type": "tap", "zone": "left" }, { "time": 167067, "type": "tap", "zone": "right" }, { "time": 167694, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 167694, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 168321, "type": "tap", "zone": "right" }, { "time": 168948, "type": "tap", "zone": "left" }, { "time": 169598, "type": "tap", "zone": "right" }, { "time": 170225, "type": "tap", "zone": "left" }, { "time": 170625, "type": "tap", "zone": "right" }, { "time": 171479, "type": "tap", "zone": "left" }, { "time": 172106, "type": "tap", "zone": "right" }, { "time": 172756, "type": "hold", "zone": "left", "duration": 1500, "color": 16753920 }, { "time": 172756, "type": "hold", "zone": "right", "duration": 1500, "color": 16753920 }, { "time": 174637, "type": "tap", "zone": "right" }, { "time": 175264, "type": "tap", "zone": "left" }, { "time": 175664, "type": "tap", "zone": "right" }, { "time": 176541, "type": "tap", "zone": "left" }, { "time": 177168, "type": "tap", "zone": "right" }, { "time": 177795, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 177795, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 178422, "type": "tap", "zone": "right" }, { "time": 179049, "type": "tap", "zone": "left" }, { "time": 179629, "type": "tap", "zone": "right" }, { "time": 180210, "type": "tap", "zone": "left" }, { "time": 180813, "type": "tap", "zone": "right" }, { "time": 181440, "type": "tap", "zone": "left" }, { "time": 182067, "type": "tap", "zone": "right" }, { "time": 183344, "type": "tap", "zone": "right" }, { "time": 183994, "type": "tap", "zone": "left" }, { "time": 185225, "type": "tap", "zone": "left" }, { "time": 185852, "type": "tap", "zone": "right" }, { "time": 187129, "type": "tap", "zone": "right" }, { "time": 187733, "type": "tap", "zone": "left" }] }, 'Pixel Paradise': { bpm: 117, scannerCycleDuration: 4000, totalLength: 202106, highScore: storage.highScores && storage.highScores['Pixel Paradise'] || 0, highestGrade: storage.highestGrades && storage.highestGrades['Pixel Paradise'] || 'C', notes: [{ "time": 2000, "type": "tap", "zone": "left" }, { "time": 3000, "type": "tap", "zone": "left" }, { "time": 4000, "type": "tap", "zone": "left" }, { "time": 4500, "type": "tap", "zone": "right" }, { "time": 5000, "type": "tap", "zone": "left" }, { "time": 5500, "type": "tap", "zone": "right" }, { "time": 6000, "type": "tap", "zone": "left", "color": 0xFFA500 }, { "time": 6000, "type": "tap", "zone": "right", "color": 0xFFA500 }, { "time": 7000, "type": "tap", "zone": "right" }, { "time": 8707, "type": "tap", "zone": "right" }, { "time": 9218, "type": "tap", "zone": "left" }, { "time": 9705, "type": "tap", "zone": "right" }, { "time": 10216, "type": "tap", "zone": "left" }, { "time": 10704, "type": "tap", "zone": "right" }, { "time": 11215, "type": "hold", "zone": "left", "duration": 1000 }, { "time": 12701, "type": "tap", "zone": "right" }, { "time": 13212, "type": "tap", "zone": "left" }, { "time": 13699, "type": "tap", "zone": "right" }, { "time": 14210, "type": "tap", "zone": "left" }, { "time": 14610, "type": "tap", "zone": "right" }, { "time": 15209, "type": "tap", "zone": "left" }, { "time": 15696, "type": "tap", "zone": "right" }, { "time": 16207, "type": "hold", "zone": "left", "duration": 1500, "color": 16753920 }, { "time": 16207, "type": "hold", "zone": "right", "duration": 1500, "color": 16753920 }, { "time": 18204, "type": "tap", "zone": "left" }, { "time": 18692, "type": "tap", "zone": "right" }, { "time": 19202, "type": "tap", "zone": "left" }, { "time": 19690, "type": "tap", "zone": "right" }, { "time": 20201, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 20201, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 20712, "type": "tap", "zone": "right" }, { "time": 21199, "type": "tap", "zone": "left" }, { "time": 22198, "type": "tap", "zone": "left" }, { "time": 22709, "type": "tap", "zone": "right" }, { "time": 23196, "type": "hold", "zone": "left", "duration": 1000 }, { "time": 24682, "type": "tap", "zone": "right" }, { "time": 25193, "type": "tap", "zone": "left" }, { "time": 25704, "type": "tap", "zone": "right" }, { "time": 26215, "type": "tap", "zone": "left" }, { "time": 26702, "type": "tap", "zone": "right" }, { "time": 27213, "type": "tap", "zone": "left" }, { "time": 27701, "type": "tap", "zone": "right" }, { "time": 28212, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 28212, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 28699, "type": "tap", "zone": "right" }, { "time": 29210, "type": "tap", "zone": "left" }, { "time": 29698, "type": "tap", "zone": "right" }, { "time": 30209, "type": "tap", "zone": "left" }, { "time": 30609, "type": "tap", "zone": "right" }, { "time": 31161, "type": "tap", "zone": "left" }, { "time": 31672, "type": "tap", "zone": "right" }, { "time": 32182, "type": "hold", "zone": "left", "duration": 1500, "color": 16753920 }, { "time": 32182, "type": "hold", "zone": "right", "duration": 1500, "color": 16753920 }, { "time": 34202, "type": "tap", "zone": "left" }, { "time": 34602, "type": "tap", "zone": "right" }, { "time": 35201, "type": "hold", "zone": "left", "duration": 1000 }, { "time": 36710, "type": "tap", "zone": "right" }, { "time": 37198, "type": "tap", "zone": "left" }, { "time": 37709, "type": "tap", "zone": "right" }, { "time": 38196, "type": "tap", "zone": "left" }, { "time": 38707, "type": "tap", "zone": "right" }, { "time": 39195, "type": "tap", "zone": "left" }, { "time": 39682, "type": "tap", "zone": "right" }, { "time": 40216, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 40216, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 40704, "type": "tap", "zone": "right" }, { "time": 41215, "type": "hold", "zone": "right", "duration": 1000 }, { "time": 43212, "type": "tap", "zone": "left" }, { "time": 43699, "type": "tap", "zone": "right" }, { "time": 44721, "type": "tap", "zone": "right" }, { "time": 45209, "type": "tap", "zone": "left" }, { "time": 45696, "type": "tap", "zone": "right" }, { "time": 46207, "type": "tap", "zone": "left" }, { "time": 46718, "type": "tap", "zone": "right" }, { "time": 47693, "type": "tap", "zone": "right" }, { "time": 48204, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 48204, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 48715, "type": "tap", "zone": "right" }, { "time": 49203, "type": "tap", "zone": "left" }, { "time": 49713, "type": "tap", "zone": "right" }, { "time": 50201, "type": "tap", "zone": "left" }, { "time": 50712, "type": "tap", "zone": "right" }, { "time": 51200, "type": "tap", "zone": "left" }, { "time": 51710, "type": "tap", "zone": "right" }, { "time": 52221, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 52221, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 52709, "type": "tap", "zone": "right" }, { "time": 53220, "type": "hold", "zone": "right", "duration": 1000 }, { "time": 54706, "type": "tap", "zone": "right" }, { "time": 55217, "type": "tap", "zone": "left" }, { "time": 55704, "type": "tap", "zone": "right" }, { "time": 56192, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 56192, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 56703, "type": "tap", "zone": "right" }, { "time": 57190, "type": "tap", "zone": "left" }, { "time": 57701, "type": "tap", "zone": "right" }, { "time": 58212, "type": "tap", "zone": "left" }, { "time": 58700, "type": "tap", "zone": "right" }, { "time": 59210, "type": "hold", "zone": "left", "duration": 1000 }, { "time": 60696, "type": "tap", "zone": "right" }, { "time": 61207, "type": "tap", "zone": "left" }, { "time": 61695, "type": "tap", "zone": "right" }, { "time": 62183, "type": "tap", "zone": "left" }, { "time": 62693, "type": "tap", "zone": "right" }, { "time": 63204, "type": "tap", "zone": "left" }, { "time": 63715, "type": "tap", "zone": "right" }, { "time": 64203, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 64203, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 64714, "type": "tap", "zone": "right" }, { "time": 65201, "type": "hold", "zone": "right", "duration": 1000 }, { "time": 66710, "type": "tap", "zone": "right" }, { "time": 67198, "type": "tap", "zone": "left" }, { "time": 67709, "type": "tap", "zone": "right" }, { "time": 68220, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 68220, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 68707, "type": "tap", "zone": "right" }, { "time": 69195, "type": "tap", "zone": "left" }, { "time": 69706, "type": "tap", "zone": "right" }, { "time": 70217, "type": "tap", "zone": "left" }, { "time": 70617, "type": "tap", "zone": "right" }, { "time": 71215, "type": "hold", "zone": "left", "duration": 1000 }, { "time": 72701, "type": "tap", "zone": "right" }, { "time": 73189, "type": "tap", "zone": "left" }, { "time": 73700, "type": "tap", "zone": "right" }, { "time": 74210, "type": "tap", "zone": "left" }, { "time": 74698, "type": "tap", "zone": "right" }, { "time": 75209, "type": "tap", "zone": "left" }, { "time": 75697, "type": "tap", "zone": "right" }, { "time": 76207, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 76207, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 76695, "type": "tap", "zone": "right" }, { "time": 77206, "type": "hold", "zone": "right", "duration": 1000 }, { "time": 78692, "type": "tap", "zone": "right" }, { "time": 79203, "type": "tap", "zone": "left" }, { "time": 79690, "type": "tap", "zone": "right" }, { "time": 80201, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 80201, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 80689, "type": "tap", "zone": "right" }, { "time": 81200, "type": "tap", "zone": "left" }, { "time": 81711, "type": "tap", "zone": "right" }, { "time": 82221, "type": "tap", "zone": "left" }, { "time": 82709, "type": "tap", "zone": "right" }, { "time": 83197, "type": "tap", "zone": "left" }, { "time": 83707, "type": "tap", "zone": "right" }, { "time": 84218, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 84218, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 84706, "type": "tap", "zone": "right" }, { "time": 85217, "type": "tap", "zone": "left" }, { "time": 85704, "type": "tap", "zone": "right" }, { "time": 86215, "type": "tap", "zone": "left" }, { "time": 86615, "type": "tap", "zone": "right" }, { "time": 87167, "type": "tap", "zone": "left" }, { "time": 87678, "type": "tap", "zone": "right" }, { "time": 88166, "type": "hold", "zone": "left", "duration": 1500, "color": 16753920 }, { "time": 88166, "type": "hold", "zone": "right", "duration": 1500, "color": 16753920 }, { "time": 90209, "type": "tap", "zone": "left" }, { "time": 90609, "type": "tap", "zone": "right" }, { "time": 91184, "type": "tap", "zone": "left" }, { "time": 91695, "type": "tap", "zone": "right" }, { "time": 92206, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 92206, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 92694, "type": "tap", "zone": "right" }, { "time": 93204, "type": "tap", "zone": "left" }, { "time": 93692, "type": "tap", "zone": "right" }, { "time": 94203, "type": "tap", "zone": "left" }, { "time": 94714, "type": "tap", "zone": "right" }, { "time": 95201, "type": "hold", "zone": "left", "duration": 1000 }, { "time": 96711, "type": "tap", "zone": "right" }, { "time": 97221, "type": "tap", "zone": "left" }, { "time": 97709, "type": "tap", "zone": "right" }, { "time": 98197, "type": "tap", "zone": "left" }, { "time": 98708, "type": "tap", "zone": "right" }, { "time": 99195, "type": "tap", "zone": "left" }, { "time": 100217, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 100217, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 100704, "type": "tap", "zone": "right" }, { "time": 101703, "type": "tap", "zone": "right" }, { "time": 102214, "type": "tap", "zone": "left" }, { "time": 103212, "type": "tap", "zone": "left" }, { "time": 103700, "type": "tap", "zone": "right" }, { "time": 104211, "type": "hold", "zone": "left", "duration": 1500, "color": 16753920 }, { "time": 104211, "type": "hold", "zone": "right", "duration": 1500, "color": 16753920 }, { "time": 106208, "type": "tap", "zone": "left" }, { "time": 106608, "type": "tap", "zone": "right" }, { "time": 107206, "type": "tap", "zone": "left" }, { "time": 107694, "type": "tap", "zone": "right" }, { "time": 108204, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 108204, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 108692, "type": "tap", "zone": "right" }, { "time": 109203, "type": "tap", "zone": "left" }, { "time": 109691, "type": "tap", "zone": "right" }, { "time": 110201, "type": "tap", "zone": "left" }, { "time": 110601, "type": "tap", "zone": "right" }, { "time": 111200, "type": "tap", "zone": "left" }, { "time": 111711, "type": "tap", "zone": "right" }, { "time": 112198, "type": "hold", "zone": "left", "duration": 1500, "color": 16753920 }, { "time": 112198, "type": "hold", "zone": "right", "duration": 1500, "color": 16753920 }, { "time": 114195, "type": "tap", "zone": "left" }, { "time": 114595, "type": "tap", "zone": "right" }, { "time": 115194, "type": "tap", "zone": "left" }, { "time": 115705, "type": "tap", "zone": "right" }, { "time": 116215, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 116215, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 116703, "type": "tap", "zone": "right" }, { "time": 117191, "type": "tap", "zone": "left" }, { "time": 117701, "type": "tap", "zone": "right" }, { "time": 118189, "type": "tap", "zone": "left" }, { "time": 118700, "type": "tap", "zone": "right" }, { "time": 119698, "type": "tap", "zone": "right" }, { "time": 120209, "type": "hold", "zone": "left", "duration": 1500, "color": 16753920 }, { "time": 120209, "type": "hold", "zone": "right", "duration": 1500, "color": 16753920 }, { "time": 122206, "type": "tap", "zone": "left" }, { "time": 122606, "type": "tap", "zone": "right" }, { "time": 123205, "type": "tap", "zone": "left" }, { "time": 123715, "type": "tap", "zone": "right" }, { "time": 124203, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 124203, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 124714, "type": "tap", "zone": "right" }, { "time": 125201, "type": "tap", "zone": "left" }, { "time": 125712, "type": "tap", "zone": "right" }, { "time": 126200, "type": "tap", "zone": "left" }, { "time": 126600, "type": "tap", "zone": "right" }, { "time": 127198, "type": "tap", "zone": "left" }, { "time": 127709, "type": "tap", "zone": "right" }, { "time": 128684, "type": "tap", "zone": "right" }, { "time": 129195, "type": "tap", "zone": "left" }, { "time": 130217, "type": "tap", "zone": "left" }, { "time": 130705, "type": "tap", "zone": "right" }, { "time": 131750, "type": "tap", "zone": "right" }, { "time": 132260, "type": "tap", "zone": "left" }, { "time": 133189, "type": "tap", "zone": "left" }, { "time": 133677, "type": "tap", "zone": "right" }, { "time": 134675, "type": "tap", "zone": "right" }, { "time": 135186, "type": "tap", "zone": "left" }, { "time": 136208, "type": "tap", "zone": "left" }, { "time": 136719, "type": "tap", "zone": "right" }, { "time": 137717, "type": "tap", "zone": "right" }, { "time": 138228, "type": "tap", "zone": "left" }, { "time": 139226, "type": "tap", "zone": "left" }, { "time": 139714, "type": "tap", "zone": "right" }, { "time": 140202, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 140202, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 140712, "type": "tap", "zone": "right" }, { "time": 141223, "type": "tap", "zone": "left" }, { "time": 142222, "type": "tap", "zone": "left" }, { "time": 142709, "type": "tap", "zone": "right" }, { "time": 143708, "type": "tap", "zone": "right" }, { "time": 144195, "type": "tap", "zone": "left" }, { "time": 145194, "type": "tap", "zone": "left" }, { "time": 145705, "type": "tap", "zone": "right" }, { "time": 146216, "type": "tap", "zone": "left" }, { "time": 146703, "type": "tap", "zone": "right" }, { "time": 147214, "type": "tap", "zone": "left" }, { "time": 147702, "type": "tap", "zone": "right" }, { "time": 148212, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 148212, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 148700, "type": "tap", "zone": "right" }, { "time": 149699, "type": "tap", "zone": "right" }, { "time": 150209, "type": "tap", "zone": "left" }, { "time": 151208, "type": "tap", "zone": "left" }, { "time": 151695, "type": "tap", "zone": "right" }, { "time": 152206, "type": "hold", "zone": "left", "duration": 1500, "color": 16753920 }, { "time": 152206, "type": "hold", "zone": "right", "duration": 1500, "color": 16753920 }, { "time": 154203, "type": "tap", "zone": "left" }, { "time": 154714, "type": "tap", "zone": "right" }, { "time": 155202, "type": "tap", "zone": "left" }, { "time": 155713, "type": "tap", "zone": "right" }, { "time": 156200, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 156200, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 157222, "type": "tap", "zone": "left" }, { "time": 157709, "type": "tap", "zone": "right" }, { "time": 158220, "type": "tap", "zone": "left" }, { "time": 158708, "type": "tap", "zone": "right" }, { "time": 159219, "type": "tap", "zone": "left" }, { "time": 159706, "type": "tap", "zone": "right" }, { "time": 160217, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 160217, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 160728, "type": "tap", "zone": "right" }, { "time": 161726, "type": "tap", "zone": "right" }, { "time": 162214, "type": "tap", "zone": "left" }, { "time": 163213, "type": "tap", "zone": "left" }, { "time": 163700, "type": "tap", "zone": "right" }, { "time": 164211, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 164211, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 164699, "type": "tap", "zone": "right" }, { "time": 165209, "type": "tap", "zone": "left" }, { "time": 165697, "type": "tap", "zone": "right" }, { "time": 166208, "type": "tap", "zone": "left" }, { "time": 166608, "type": "tap", "zone": "right" }, { "time": 167206, "type": "tap", "zone": "left" }, { "time": 167717, "type": "tap", "zone": "right" }, { "time": 168205, "type": "hold", "zone": "left", "duration": 1500, "color": 16753920 }, { "time": 168205, "type": "hold", "zone": "right", "duration": 1500, "color": 16753920 }, { "time": 170202, "type": "tap", "zone": "left" }, { "time": 170602, "type": "tap", "zone": "right" }, { "time": 171200, "type": "tap", "zone": "left" }, { "time": 171711, "type": "tap", "zone": "right" }, { "time": 172199, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 172199, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 172710, "type": "tap", "zone": "right" }, { "time": 173197, "type": "tap", "zone": "left" }, { "time": 173708, "type": "tap", "zone": "right" }, { "time": 174196, "type": "tap", "zone": "left" }, { "time": 175217, "type": "tap", "zone": "left" }, { "time": 175705, "type": "tap", "zone": "right" }, { "time": 176216, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 176216, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 176703, "type": "tap", "zone": "right" }, { "time": 177214, "type": "tap", "zone": "left" }, { "time": 177702, "type": "tap", "zone": "right" }, { "time": 178213, "type": "tap", "zone": "left" }, { "time": 178700, "type": "tap", "zone": "right" }, { "time": 179211, "type": "hold", "zone": "left", "duration": 1000 }, { "time": 180697, "type": "tap", "zone": "right" }, { "time": 181208, "type": "tap", "zone": "left" }, { "time": 181719, "type": "tap", "zone": "right" }, { "time": 182206, "type": "tap", "zone": "left" }, { "time": 182606, "type": "tap", "zone": "right" }, { "time": 183205, "type": "tap", "zone": "left" }, { "time": 183716, "type": "tap", "zone": "right" }, { "time": 184203, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 184203, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 184714, "type": "tap", "zone": "right" }, { "time": 185202, "type": "hold", "zone": "right", "duration": 1000 }, { "time": 186711, "type": "tap", "zone": "right" }, { "time": 187222, "type": "tap", "zone": "left" }, { "time": 187710, "type": "tap", "zone": "right" }, { "time": 188197, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 188197, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 188708, "type": "tap", "zone": "right" }, { "time": 189219, "type": "tap", "zone": "left" }, { "time": 189823, "type": "tap", "zone": "right" }, { "time": 190403, "type": "tap", "zone": "left" }, { "time": 190803, "type": "tap", "zone": "right" }, { "time": 191448, "type": "hold", "zone": "left", "duration": 1000 }, { "time": 193468, "type": "tap", "zone": "left" }, { "time": 193956, "type": "tap", "zone": "right" }, { "time": 194954, "type": "tap", "zone": "right" }, { "time": 195465, "type": "tap", "zone": "left" }, { "time": 196464, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 196464, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 196974, "type": "tap", "zone": "right" }, { "time": 197973, "type": "tap", "zone": "right" }, { "time": 198460, "type": "tap", "zone": "left" }, { "time": 198971, "type": "tap", "zone": "right" }] }, 'Runner': { bpm: 92, scannerCycleDuration: 4000, totalLength: 230341, highScore: storage.highScores && storage.highScores['Runner'] || 0, highestGrade: storage.highestGrades && storage.highestGrades['Runner'] || 'C', notes: [{ "time": 208, "type": "tap", "zone": "left" }, { "time": 1671, "type": "tap", "zone": "left" }, { "time": 2972, "type": "tap", "zone": "left" }, { "time": 4272, "type": "tap", "zone": "left" }, { "time": 5247, "type": "tap", "zone": "right" }, { "time": 5572, "type": "tap", "zone": "left" }, { "time": 6873, "type": "tap", "zone": "left" }, { "time": 8173, "type": "tap", "zone": "left" }, { "time": 9496, "type": "tap", "zone": "left" }, { "time": 10472, "type": "tap", "zone": "right" }, { "time": 10797, "type": "tap", "zone": "left" }, { "time": 11772, "type": "tap", "zone": "right" }, { "time": 12097, "type": "hold", "zone": "right", "duration": 1000 }, { "time": 13397, "type": "tap", "zone": "left" }, { "time": 13722, "type": "tap", "zone": "right" }, { "time": 14048, "type": "tap", "zone": "left" }, { "time": 14721, "type": "tap", "zone": "left" }, { "time": 15046, "type": "tap", "zone": "right" }, { "time": 16021, "type": "tap", "zone": "left" }, { "time": 17322, "type": "tap", "zone": "left" }, { "time": 17647, "type": "tap", "zone": "right" }, { "time": 18622, "type": "tap", "zone": "left" }, { "time": 18947, "type": "tap", "zone": "right" }, { "time": 19272, "type": "tap", "zone": "left" }, { "time": 19945, "type": "tap", "zone": "left" }, { "time": 21223, "type": "tap", "zone": "left" }, { "time": 21548, "type": "tap", "zone": "right" }, { "time": 22523, "type": "tap", "zone": "left" }, { "time": 23846, "type": "tap", "zone": "left" }, { "time": 25147, "type": "tap", "zone": "left" }, { "time": 26447, "type": "tap", "zone": "left" }, { "time": 26772, "type": "tap", "zone": "right" }, { "time": 27747, "type": "tap", "zone": "left" }, { "time": 29048, "type": "tap", "zone": "left" }, { "time": 30348, "type": "tap", "zone": "left" }, { "time": 31672, "type": "tap", "zone": "left" }, { "time": 31997, "type": "tap", "zone": "right" }, { "time": 32322, "type": "tap", "zone": "left" }, { "time": 32647, "type": "tap", "zone": "right" }, { "time": 32972, "type": "tap", "zone": "left" }, { "time": 33947, "type": "tap", "zone": "right" }, { "time": 34272, "type": "tap", "zone": "left" }, { "time": 34597, "type": "tap", "zone": "right" }, { "time": 34922, "type": "tap", "zone": "left" }, { "time": 35247, "type": "tap", "zone": "right" }, { "time": 35572, "type": "tap", "zone": "left" }, { "time": 36873, "type": "tap", "zone": "left" }, { "time": 38173, "type": "tap", "zone": "left" }, { "time": 39497, "type": "tap", "zone": "left" }, { "time": 40797, "type": "tap", "zone": "left" }, { "time": 42097, "type": "tap", "zone": "left" }, { "time": 42422, "type": "tap", "zone": "right" }, { "time": 42747, "type": "tap", "zone": "left" }, { "time": 43073, "type": "tap", "zone": "right" }, { "time": 43398, "type": "tap", "zone": "left" }, { "time": 43723, "type": "tap", "zone": "right" }, { "time": 44048, "type": "tap", "zone": "left" }, { "time": 44373, "type": "tap", "zone": "right" }, { "time": 44698, "type": "tap", "zone": "left" }, { "time": 45023, "type": "tap", "zone": "right" }, { "time": 45348, "type": "tap", "zone": "left" }, { "time": 45673, "type": "tap", "zone": "right" }, { "time": 45998, "type": "tap", "zone": "left" }, { "time": 46323, "type": "tap", "zone": "right" }, { "time": 46648, "type": "tap", "zone": "left" }, { "time": 46985, "type": "tap", "zone": "right" }, { "time": 47322, "type": "tap", "zone": "left" }, { "time": 47647, "type": "tap", "zone": "right" }, { "time": 47972, "type": "tap", "zone": "left" }, { "time": 48622, "type": "tap", "zone": "left" }, { "time": 48947, "type": "tap", "zone": "right" }, { "time": 49272, "type": "tap", "zone": "left" }, { "time": 49597, "type": "tap", "zone": "right" }, { "time": 49922, "type": "tap", "zone": "left" }, { "time": 50247, "type": "tap", "zone": "right" }, { "time": 50573, "type": "tap", "zone": "left" }, { "time": 51223, "type": "tap", "zone": "left" }, { "time": 51896, "type": "tap", "zone": "left" }, { "time": 52221, "type": "tap", "zone": "right" }, { "time": 52546, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 52546, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 52871, "type": "tap", "zone": "right" }, { "time": 53196, "type": "tap", "zone": "left" }, { "time": 53521, "type": "tap", "zone": "right" }, { "time": 53847, "type": "tap", "zone": "left" }, { "time": 54172, "type": "tap", "zone": "right" }, { "time": 54822, "type": "tap", "zone": "right" }, { "time": 55147, "type": "tap", "zone": "left" }, { "time": 55472, "type": "tap", "zone": "right" }, { "time": 56122, "type": "tap", "zone": "right" }, { "time": 56447, "type": "tap", "zone": "left" }, { "time": 56772, "type": "tap", "zone": "right" }, { "time": 57422, "type": "tap", "zone": "right" }, { "time": 57748, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 57748, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 58073, "type": "tap", "zone": "right" }, { "time": 58398, "type": "tap", "zone": "left" }, { "time": 58723, "type": "tap", "zone": "right" }, { "time": 59048, "type": "hold", "zone": "right", "duration": 1000 }, { "time": 60371, "type": "tap", "zone": "left" }, { "time": 60696, "type": "tap", "zone": "right" }, { "time": 61022, "type": "tap", "zone": "left" }, { "time": 61347, "type": "tap", "zone": "right" }, { "time": 61672, "type": "tap", "zone": "left" }, { "time": 61997, "type": "tap", "zone": "right" }, { "time": 62322, "type": "tap", "zone": "left" }, { "time": 62647, "type": "tap", "zone": "right" }, { "time": 62972, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 62972, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 63297, "type": "tap", "zone": "right" }, { "time": 63622, "type": "tap", "zone": "left" }, { "time": 63947, "type": "tap", "zone": "right" }, { "time": 64272, "type": "tap", "zone": "left" }, { "time": 64597, "type": "tap", "zone": "right" }, { "time": 64922, "type": "tap", "zone": "left" }, { "time": 65248, "type": "tap", "zone": "right" }, { "time": 65573, "type": "tap", "zone": "left" }, { "time": 65898, "type": "tap", "zone": "right" }, { "time": 66223, "type": "tap", "zone": "left" }, { "time": 66548, "type": "tap", "zone": "right" }, { "time": 66873, "type": "hold", "zone": "left", "duration": 1000 }, { "time": 67871, "type": "tap", "zone": "right" }, { "time": 68197, "type": "tap", "zone": "left" }, { "time": 68522, "type": "tap", "zone": "right" }, { "time": 69172, "type": "tap", "zone": "right" }, { "time": 69497, "type": "tap", "zone": "left" }, { "time": 69822, "type": "tap", "zone": "right" }, { "time": 70147, "type": "tap", "zone": "left" }, { "time": 70472, "type": "tap", "zone": "right" }, { "time": 70797, "type": "tap", "zone": "left" }, { "time": 71122, "type": "tap", "zone": "right" }, { "time": 71447, "type": "tap", "zone": "left" }, { "time": 71772, "type": "tap", "zone": "right" }, { "time": 72097, "type": "tap", "zone": "left" }, { "time": 73398, "type": "tap", "zone": "left" }, { "time": 73723, "type": "tap", "zone": "right" }, { "time": 74048, "type": "tap", "zone": "left" }, { "time": 74373, "type": "tap", "zone": "right" }, { "time": 74698, "type": "tap", "zone": "left" }, { "time": 76022, "type": "tap", "zone": "left" }, { "time": 76347, "type": "tap", "zone": "right" }, { "time": 76997, "type": "tap", "zone": "right" }, { "time": 77322, "type": "tap", "zone": "left" }, { "time": 78297, "type": "tap", "zone": "right" }, { "time": 78622, "type": "tap", "zone": "left" }, { "time": 78947, "type": "tap", "zone": "right" }, { "time": 79272, "type": "tap", "zone": "left" }, { "time": 79923, "type": "tap", "zone": "left" }, { "time": 81223, "type": "tap", "zone": "left" }, { "time": 81548, "type": "tap", "zone": "right" }, { "time": 81873, "type": "tap", "zone": "left" }, { "time": 82198, "type": "tap", "zone": "right" }, { "time": 82523, "type": "tap", "zone": "left" }, { "time": 83173, "type": "tap", "zone": "left" }, { "time": 83510, "type": "tap", "zone": "right" }, { "time": 83847, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 83847, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 84172, "type": "tap", "zone": "right" }, { "time": 84497, "type": "tap", "zone": "left" }, { "time": 84822, "type": "tap", "zone": "right" }, { "time": 85147, "type": "tap", "zone": "left" }, { "time": 85472, "type": "tap", "zone": "right" }, { "time": 85797, "type": "tap", "zone": "left" }, { "time": 86122, "type": "tap", "zone": "right" }, { "time": 86447, "type": "tap", "zone": "left" }, { "time": 86772, "type": "tap", "zone": "right" }, { "time": 87098, "type": "tap", "zone": "left" }, { "time": 87423, "type": "tap", "zone": "right" }, { "time": 87748, "type": "tap", "zone": "left" }, { "time": 88073, "type": "tap", "zone": "right" }, { "time": 88398, "type": "tap", "zone": "left" }, { "time": 88723, "type": "tap", "zone": "right" }, { "time": 89048, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 89048, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 89373, "type": "tap", "zone": "right" }, { "time": 90348, "type": "tap", "zone": "left" }, { "time": 91335, "type": "tap", "zone": "right" }, { "time": 91672, "type": "tap", "zone": "left" }, { "time": 91997, "type": "tap", "zone": "right" }, { "time": 92322, "type": "tap", "zone": "left" }, { "time": 92647, "type": "tap", "zone": "right" }, { "time": 92972, "type": "tap", "zone": "left" }, { "time": 93297, "type": "tap", "zone": "right" }, { "time": 93622, "type": "tap", "zone": "left" }, { "time": 93947, "type": "tap", "zone": "right" }, { "time": 94273, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 94273, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 94598, "type": "tap", "zone": "right" }, { "time": 94923, "type": "tap", "zone": "left" }, { "time": 95248, "type": "tap", "zone": "right" }, { "time": 95573, "type": "tap", "zone": "left" }, { "time": 95898, "type": "tap", "zone": "right" }, { "time": 96223, "type": "tap", "zone": "left" }, { "time": 96548, "type": "tap", "zone": "right" }, { "time": 96873, "type": "tap", "zone": "left" }, { "time": 97198, "type": "tap", "zone": "right" }, { "time": 97523, "type": "tap", "zone": "left" }, { "time": 97848, "type": "tap", "zone": "right" }, { "time": 98173, "type": "tap", "zone": "left" }, { "time": 98499, "type": "tap", "zone": "right" }, { "time": 98824, "type": "tap", "zone": "left" }, { "time": 99160, "type": "tap", "zone": "right" }, { "time": 99497, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 99497, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 99822, "type": "tap", "zone": "right" }, { "time": 100147, "type": "tap", "zone": "left" }, { "time": 100472, "type": "tap", "zone": "right" }, { "time": 100797, "type": "tap", "zone": "left" }, { "time": 101122, "type": "tap", "zone": "right" }, { "time": 101447, "type": "tap", "zone": "left" }, { "time": 101773, "type": "tap", "zone": "right" }, { "time": 102098, "type": "tap", "zone": "left" }, { "time": 103421, "type": "tap", "zone": "left" }, { "time": 104396, "type": "tap", "zone": "right" }, { "time": 104721, "type": "hold", "zone": "left", "duration": 1500, "color": 16753920 }, { "time": 104721, "type": "hold", "zone": "right", "duration": 1500, "color": 16753920 }, { "time": 106672, "type": "tap", "zone": "left" }, { "time": 106997, "type": "tap", "zone": "right" }, { "time": 107322, "type": "tap", "zone": "left" }, { "time": 107647, "type": "tap", "zone": "right" }, { "time": 107972, "type": "tap", "zone": "left" }, { "time": 108297, "type": "tap", "zone": "right" }, { "time": 108622, "type": "tap", "zone": "left" }, { "time": 108948, "type": "tap", "zone": "right" }, { "time": 109273, "type": "tap", "zone": "left" }, { "time": 109598, "type": "tap", "zone": "right" }, { "time": 109923, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 109923, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 110248, "type": "tap", "zone": "right" }, { "time": 110573, "type": "tap", "zone": "left" }, { "time": 110898, "type": "tap", "zone": "right" }, { "time": 111223, "type": "tap", "zone": "left" }, { "time": 111548, "type": "tap", "zone": "right" }, { "time": 111873, "type": "tap", "zone": "left" }, { "time": 112210, "type": "tap", "zone": "right" }, { "time": 112547, "type": "tap", "zone": "left" }, { "time": 112872, "type": "tap", "zone": "right" }, { "time": 113197, "type": "tap", "zone": "left" }, { "time": 113522, "type": "tap", "zone": "right" }, { "time": 113847, "type": "hold", "zone": "left", "duration": 1000 }, { "time": 114822, "type": "tap", "zone": "right" }, { "time": 115147, "type": "hold", "zone": "left", "duration": 1500, "color": 16753920 }, { "time": 115147, "type": "hold", "zone": "right", "duration": 1500, "color": 16753920 }, { "time": 117098, "type": "tap", "zone": "left" }, { "time": 117423, "type": "tap", "zone": "right" }, { "time": 117748, "type": "tap", "zone": "left" }, { "time": 118073, "type": "tap", "zone": "right" }, { "time": 118398, "type": "tap", "zone": "left" }, { "time": 118723, "type": "tap", "zone": "right" }, { "time": 119048, "type": "tap", "zone": "left" }, { "time": 119385, "type": "tap", "zone": "right" }, { "time": 119722, "type": "tap", "zone": "left" }, { "time": 120047, "type": "tap", "zone": "right" }, { "time": 120372, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 120372, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 120697, "type": "tap", "zone": "right" }, { "time": 121022, "type": "tap", "zone": "left" }, { "time": 121347, "type": "tap", "zone": "right" }, { "time": 121672, "type": "hold", "zone": "right", "duration": 1000 }, { "time": 122322, "type": "tap", "zone": "left" }, { "time": 122972, "type": "tap", "zone": "left" }, { "time": 123297, "type": "tap", "zone": "right" }, { "time": 123623, "type": "tap", "zone": "left" }, { "time": 123948, "type": "tap", "zone": "right" }, { "time": 124273, "type": "tap", "zone": "left" }, { "time": 125573, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 125573, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 125898, "type": "tap", "zone": "right" }, { "time": 126548, "type": "tap", "zone": "right" }, { "time": 126873, "type": "tap", "zone": "left" }, { "time": 127198, "type": "tap", "zone": "right" }, { "time": 127523, "type": "tap", "zone": "left" }, { "time": 127849, "type": "tap", "zone": "right" }, { "time": 128174, "type": "tap", "zone": "left" }, { "time": 128510, "type": "tap", "zone": "right" }, { "time": 129172, "type": "tap", "zone": "right" }, { "time": 129497, "type": "hold", "zone": "left", "duration": 1000 }, { "time": 130472, "type": "tap", "zone": "right" }, { "time": 130798, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 130798, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 131123, "type": "tap", "zone": "right" }, { "time": 131448, "type": "tap", "zone": "left" }, { "time": 131773, "type": "tap", "zone": "right" }, { "time": 132098, "type": "tap", "zone": "left" }, { "time": 132423, "type": "tap", "zone": "right" }, { "time": 132748, "type": "tap", "zone": "left" }, { "time": 133073, "type": "tap", "zone": "right" }, { "time": 133398, "type": "tap", "zone": "left" }, { "time": 133723, "type": "tap", "zone": "right" }, { "time": 134373, "type": "tap", "zone": "right" }, { "time": 134698, "type": "tap", "zone": "left" }, { "time": 135035, "type": "tap", "zone": "right" }, { "time": 135372, "type": "tap", "zone": "left" }, { "time": 135697, "type": "tap", "zone": "right" }, { "time": 136022, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 136022, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 136347, "type": "tap", "zone": "right" }, { "time": 136672, "type": "tap", "zone": "left" }, { "time": 136997, "type": "tap", "zone": "right" }, { "time": 137322, "type": "hold", "zone": "right", "duration": 1000 }, { "time": 138623, "type": "tap", "zone": "left" }, { "time": 138948, "type": "tap", "zone": "right" }, { "time": 139598, "type": "tap", "zone": "right" }, { "time": 139923, "type": "tap", "zone": "left" }, { "time": 140248, "type": "tap", "zone": "right" }, { "time": 140573, "type": "tap", "zone": "left" }, { "time": 140898, "type": "tap", "zone": "right" }, { "time": 141223, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 141223, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 141548, "type": "tap", "zone": "right" }, { "time": 141873, "type": "tap", "zone": "left" }, { "time": 142199, "type": "tap", "zone": "right" }, { "time": 142524, "type": "tap", "zone": "left" }, { "time": 142849, "type": "tap", "zone": "right" }, { "time": 143174, "type": "tap", "zone": "left" }, { "time": 143510, "type": "tap", "zone": "right" }, { "time": 143847, "type": "tap", "zone": "left" }, { "time": 144172, "type": "tap", "zone": "right" }, { "time": 144497, "type": "tap", "zone": "left" }, { "time": 144822, "type": "tap", "zone": "right" }, { "time": 145147, "type": "hold", "zone": "left", "duration": 1000 }, { "time": 146123, "type": "tap", "zone": "right" }, { "time": 146448, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 146448, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 146773, "type": "tap", "zone": "right" }, { "time": 147098, "type": "tap", "zone": "left" }, { "time": 147423, "type": "tap", "zone": "right" }, { "time": 147748, "type": "tap", "zone": "left" }, { "time": 148073, "type": "tap", "zone": "right" }, { "time": 148398, "type": "tap", "zone": "left" }, { "time": 148723, "type": "tap", "zone": "right" }, { "time": 149048, "type": "tap", "zone": "left" }, { "time": 149373, "type": "tap", "zone": "right" }, { "time": 149699, "type": "tap", "zone": "left" }, { "time": 150024, "type": "tap", "zone": "right" }, { "time": 150349, "type": "tap", "zone": "left" }, { "time": 150685, "type": "tap", "zone": "right" }, { "time": 151022, "type": "tap", "zone": "left" }, { "time": 151347, "type": "tap", "zone": "right" }, { "time": 151672, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 151672, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 151997, "type": "tap", "zone": "right" }, { "time": 152322, "type": "tap", "zone": "left" }, { "time": 152647, "type": "tap", "zone": "right" }, { "time": 152973, "type": "hold", "zone": "right", "duration": 1000 }, { "time": 154273, "type": "tap", "zone": "left" }, { "time": 154598, "type": "tap", "zone": "right" }, { "time": 154923, "type": "tap", "zone": "left" }, { "time": 155573, "type": "tap", "zone": "left" }, { "time": 156874, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 156874, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 158174, "type": "tap", "zone": "left" }, { "time": 159161, "type": "tap", "zone": "right" }, { "time": 159497, "type": "tap", "zone": "left" }, { "time": 159822, "type": "tap", "zone": "right" }, { "time": 160148, "type": "tap", "zone": "left" }, { "time": 160473, "type": "tap", "zone": "right" }, { "time": 160798, "type": "hold", "zone": "left", "duration": 1000 }, { "time": 161773, "type": "tap", "zone": "right" }, { "time": 162098, "type": "tap", "zone": "left" }, { "time": 163398, "type": "tap", "zone": "left" }, { "time": 164374, "type": "tap", "zone": "right" }, { "time": 164699, "type": "tap", "zone": "left" }, { "time": 165024, "type": "tap", "zone": "right" }, { "time": 165349, "type": "tap", "zone": "left" }, { "time": 165674, "type": "tap", "zone": "right" }, { "time": 165999, "type": "tap", "zone": "left" }, { "time": 166324, "type": "tap", "zone": "right" }, { "time": 166649, "type": "tap", "zone": "left" }, { "time": 166986, "type": "tap", "zone": "right" }, { "time": 167322, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 167322, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 167973, "type": "tap", "zone": "left" }, { "time": 168623, "type": "hold", "zone": "right", "duration": 1000 }, { "time": 169923, "type": "tap", "zone": "left" }, { "time": 170573, "type": "tap", "zone": "left" }, { "time": 171223, "type": "tap", "zone": "left" }, { "time": 171874, "type": "tap", "zone": "left" }, { "time": 172199, "type": "tap", "zone": "right" }, { "time": 172524, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 172524, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 172849, "type": "tap", "zone": "right" }, { "time": 173174, "type": "tap", "zone": "left" }, { "time": 173499, "type": "tap", "zone": "right" }, { "time": 173824, "type": "tap", "zone": "left" }, { "time": 174161, "type": "tap", "zone": "right" }, { "time": 174497, "type": "tap", "zone": "left" }, { "time": 175148, "type": "tap", "zone": "left" }, { "time": 175473, "type": "tap", "zone": "right" }, { "time": 175798, "type": "tap", "zone": "left" }, { "time": 176587, "type": "tap", "zone": "left" }, { "time": 177528, "type": "tap", "zone": "right" }, { "time": 177818, "type": "hold", "zone": "left", "duration": 1500, "color": 16753920 }, { "time": 177818, "type": "hold", "zone": "right", "duration": 1500, "color": 16753920 }, { "time": 179699, "type": "tap", "zone": "left" }, { "time": 180024, "type": "tap", "zone": "right" }, { "time": 180349, "type": "tap", "zone": "left" }, { "time": 180674, "type": "tap", "zone": "right" }, { "time": 180999, "type": "tap", "zone": "left" }, { "time": 181324, "type": "tap", "zone": "right" }, { "time": 181649, "type": "tap", "zone": "left" }, { "time": 181974, "type": "tap", "zone": "right" }, { "time": 182299, "type": "tap", "zone": "left" }, { "time": 182624, "type": "tap", "zone": "right" }, { "time": 182950, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 182950, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 183275, "type": "tap", "zone": "right" }, { "time": 183600, "type": "tap", "zone": "left" }, { "time": 183925, "type": "tap", "zone": "right" }, { "time": 184250, "type": "hold", "zone": "right", "duration": 1000 }, { "time": 185550, "type": "tap", "zone": "left" }, { "time": 185887, "type": "tap", "zone": "right" }, { "time": 186224, "type": "tap", "zone": "left" }, { "time": 186549, "type": "tap", "zone": "right" }, { "time": 186874, "type": "tap", "zone": "left" }, { "time": 187524, "type": "tap", "zone": "left" }, { "time": 187849, "type": "tap", "zone": "right" }, { "time": 188174, "type": "hold", "zone": "left", "duration": 1500, "color": 16753920 }, { "time": 188174, "type": "hold", "zone": "right", "duration": 1500, "color": 16753920 }, { "time": 190124, "type": "tap", "zone": "left" }, { "time": 190450, "type": "tap", "zone": "right" }, { "time": 190775, "type": "tap", "zone": "left" }, { "time": 191100, "type": "tap", "zone": "right" }, { "time": 191425, "type": "tap", "zone": "left" }, { "time": 191750, "type": "tap", "zone": "right" }, { "time": 192075, "type": "hold", "zone": "left", "duration": 1000 }, { "time": 192400, "type": "tap", "zone": "right" }, { "time": 193399, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 193399, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 194699, "type": "tap", "zone": "left" }, { "time": 195674, "type": "tap", "zone": "right" }, { "time": 195999, "type": "tap", "zone": "left" }, { "time": 196324, "type": "tap", "zone": "right" }, { "time": 196649, "type": "tap", "zone": "left" }, { "time": 196974, "type": "tap", "zone": "right" }, { "time": 197299, "type": "tap", "zone": "left" }, { "time": 198600, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 198600, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 199900, "type": "tap", "zone": "left" }, { "time": 201200, "type": "tap", "zone": "left" }, { "time": 201851, "type": "tap", "zone": "left" }, { "time": 202524, "type": "tap", "zone": "left" }, { "time": 203174, "type": "tap", "zone": "left" }, { "time": 203499, "type": "tap", "zone": "right" }, { "time": 203824, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 203824, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 205125, "type": "tap", "zone": "left" }, { "time": 205775, "type": "tap", "zone": "left" }, { "time": 206425, "type": "tap", "zone": "left" }, { "time": 207075, "type": "tap", "zone": "left" }, { "time": 207725, "type": "hold", "zone": "left", "duration": 1000 }, { "time": 208724, "type": "tap", "zone": "right" }, { "time": 209049, "type": "tap", "zone": "left", "color": 16753920 }, { "time": 209049, "type": "tap", "zone": "right", "color": 16753920 }, { "time": 209699, "type": "tap", "zone": "left" }, { "time": 210349, "type": "tap", "zone": "left" }, { "time": 211649, "type": "tap", "zone": "left" }, { "time": 212950, "type": "tap", "zone": "left" }, { "time": 213600, "type": "tap", "zone": "left" }, { "time": 214250, "type": "tap", "zone": "left" }, { "time": 214900, "type": "tap", "zone": "left" }, { "time": 215550, "type": "hold", "zone": "right", "duration": 1000 }, { "time": 216874, "type": "tap", "zone": "left" }, { "time": 217524, "type": "tap", "zone": "left" }, { "time": 217849, "type": "tap", "zone": "right" }, { "time": 218174, "type": "tap", "zone": "left" }, { "time": 218499, "type": "tap", "zone": "right" }, { "time": 218824, "type": "tap", "zone": "left" }, { "time": 219428, "type": "tap", "zone": "left" }, { "time": 220636, "type": "tap", "zone": "left" }, { "time": 221936, "type": "tap", "zone": "left" }, { "time": 223236, "type": "tap", "zone": "left" }, { "time": 224536, "type": "tap", "zone": "left" }, { "time": 225837, "type": "tap", "zone": "left" }, { "time": 227137, "type": "tap", "zone": "left" }] } }; function getSongLength(songName) { var song = SongDatabase[songName]; if (!song) { return "0:00"; } var totalSeconds = Math.floor(song.totalLength / 1000); var minutes = Math.floor(totalSeconds / 60); var seconds = totalSeconds % 60; return minutes + ":" + (seconds < 10 ? "0" : "") + seconds; } function updateHighScore(songName, score, grade) { var updated = false; if (SongDatabase[songName]) { if (score > SongDatabase[songName].highScore) { SongDatabase[songName].highScore = score; // Save high scores to storage if (!storage.highScores) { storage.highScores = {}; } storage.highScores[songName] = score; updated = true; } // Update grade if it's better (S > A > B > C) var currentGrade = SongDatabase[songName].highestGrade || 'C'; var gradeOrder = ['C', 'B', 'A', 'S']; if (gradeOrder.indexOf(grade) > gradeOrder.indexOf(currentGrade)) { SongDatabase[songName].highestGrade = grade; // Save highest grades to storage if (!storage.highestGrades) { storage.highestGrades = {}; } storage.highestGrades[songName] = grade; updated = true; } } return updated; } function calculateNotePosition(noteTime) { var cycleTime = noteTime % SCANNER_CYCLE_DURATION; var y; if (cycleTime < SCANNER_HALF_CYCLE) { var progress = cycleTime / SCANNER_HALF_CYCLE; y = SCANNER_Y_MAX - progress * PLAY_AREA_HEIGHT; } else { var progress = (cycleTime - SCANNER_HALF_CYCLE) / SCANNER_HALF_CYCLE; y = SCANNER_Y_MIN + progress * PLAY_AREA_HEIGHT; } return y; } function calculateZoneX(zone, noteY) { var sideMargin = GAME_WIDTH * SAFETY_MARGIN_SIDES_PERCENT; var randomSpan = GAME_WIDTH / 2 - 2 * sideMargin; if (randomSpan < 0) { randomSpan = 0; } // Check if note is in the bottom 15% of the play area var isInBottomArea = noteY > SCANNER_Y_MAX - PLAY_AREA_HEIGHT * 0.15; var xPosition; if (zone === 'left') { xPosition = sideMargin; if (isInBottomArea) { // In bottom area, favor the right side of the left zone (more central) // Use the right 60% of the left zone's available space var centralSpan = randomSpan * 0.6; var offset = randomSpan * 0.4; // Skip the leftmost 40% xPosition += offset + Math.random() * centralSpan; } else { // Normal positioning for upper areas xPosition += Math.random() * randomSpan; } } else { xPosition = GAME_WIDTH / 2 + sideMargin; if (isInBottomArea) { // In bottom area, favor the left side of the right zone (more central) // Use the left 60% of the right zone's available space var centralSpan = randomSpan * 0.6; xPosition += Math.random() * centralSpan; } else { // Normal positioning for upper areas xPosition += Math.random() * randomSpan; } } return xPosition; } function createZoneIndicators() { leftZone = game.attachAsset('zoneIndicator', { anchorX: 0, anchorY: 0 }); leftZone.x = 0; leftZone.y = 0; leftZone.width = GAME_WIDTH / 2; leftZone.alpha = 0.1; leftZone.tint = 0x00ffff; rightZone = game.attachAsset('zoneIndicator', { anchorX: 0, anchorY: 0 }); rightZone.x = GAME_WIDTH / 2; rightZone.y = 0; rightZone.width = GAME_WIDTH / 2; rightZone.alpha = 0.1; rightZone.tint = 0xff00ff; } function showAccuracyPopup(text, x, y, color) { var popup = new Text2(text, { size: ACCURACY_POPUP_FONT_SIZE, fill: color, stroke: 0x000000, strokeThickness: 4 }); popup.anchor.set(0.5, 0.5); popup.x = x; popup.y = y + ACCURACY_POPUP_Y_OFFSET; popup.alpha = 0; game.addChild(popup); var initialY = popup.y; tween(popup, { alpha: 1, y: initialY - 30 }, { duration: ACCURACY_POPUP_DURATION * 0.25, easing: tween.easeOut, onFinish: function onFinish() { tween(popup, { alpha: 0, y: initialY - 130 }, { duration: ACCURACY_POPUP_DURATION * 0.75, easing: tween.easeIn, onFinish: function onFinish() { if (popup && popup.destroy) { popup.destroy(); } } }); } }); } function initializeTitleScreen() { titleScreen.container = new Container(); game.addChild(titleScreen.container); titleScreen.title = titleScreen.container.attachAsset('titleImage', { anchorX: 0.5, anchorY: 0.5 }); titleScreen.title.x = GAME_WIDTH / 2; titleScreen.title.y = GAME_HEIGHT / 2 - 200; titleScreen.subtitle = new Text2('Press to Start', { size: 100, fill: 0xCCCCCC }); titleScreen.subtitle.anchor.set(0.5, 0.5); titleScreen.subtitle.x = GAME_WIDTH / 2; titleScreen.subtitle.y = GAME_HEIGHT / 2 + 100 + GAME_HEIGHT * 0.15; titleScreen.container.addChild(titleScreen.subtitle); var pulseScale = 1; var pulseDirection = 1; titleScreen.pulseInterval = LK.setInterval(function () { pulseScale += pulseDirection * 0.02; if (pulseScale > 1.1) { pulseScale = 1.1; pulseDirection = -1; } else if (pulseScale < 0.9) { pulseScale = 0.9; pulseDirection = 1; } titleScreen.subtitle.scale.set(pulseScale); }, 50); } function initializeSongSelectScreen() { songSelectScreen.container = new Container(); game.addChild(songSelectScreen.container); songSelectScreen.container.visible = false; songSelectScreen.title = new Text2('Select Song', { size: 150, fill: 0xFFFFFF }); songSelectScreen.title.anchor.set(0.5, 0); songSelectScreen.title.x = GAME_WIDTH / 2; songSelectScreen.title.y = 100; songSelectScreen.container.addChild(songSelectScreen.title); songSelectScreen.scrollContainer = new Container(); songSelectScreen.scrollContainer.x = 0; songSelectScreen.scrollContainer.y = 300; songSelectScreen.container.addChild(songSelectScreen.scrollContainer); createSongItems(); updateScrollLimits(); songSelectScreen.tutorialButton = new Text2('How to Play', { size: 90, fill: 0xCCCCCC }); songSelectScreen.tutorialButton.anchor.set(0.5, 1); songSelectScreen.tutorialButton.x = GAME_WIDTH / 2; songSelectScreen.tutorialButton.y = GAME_HEIGHT - 60; // Positioned at the bottom songSelectScreen.container.addChild(songSelectScreen.tutorialButton); } function createSongItems() { songSelectScreen.items = []; var songNames = Object.keys(SongDatabase); for (var i = 0; i < songNames.length; i++) { var songName = songNames[i]; var song = SongDatabase[songName]; var yPos = i * (SONG_ITEM_HEIGHT + 50); var itemContainer = new Container(); itemContainer.x = 100; itemContainer.y = yPos; itemContainer.songName = songName; var bg = game.attachAsset('zoneIndicator', { anchorX: 0, anchorY: 0 }); bg.width = GAME_WIDTH - 200; bg.height = SONG_ITEM_HEIGHT; bg.alpha = 0.2; bg.tint = 0x333333; itemContainer.addChild(bg); var displayName = songName; if (songName === 'Runner') { displayName = songName + ' (Hard)'; } var titleText = new Text2(displayName, { size: 100, fill: 0xFFFFFF }); titleText.anchor.set(0.5, 0); // Center horizontally, align top vertically titleText.x = (GAME_WIDTH - 200) / 2; // Center within the background width titleText.y = 30; itemContainer.addChild(titleText); // Define vertical padding between text elements var verticalPadding = 20; var infoText = new Text2('BPM: ' + song.bpm + ' | Length: ' + getSongLength(songName), { size: 62.5, fill: 0xCCCCCC }); infoText.anchor.set(0.5, 0); // Center horizontally, align top vertically infoText.x = (GAME_WIDTH - 200) / 2; // Center within the background width // Position infoText below titleText (assuming titleText font size 100 is approx height) infoText.y = titleText.y + 100 + verticalPadding; itemContainer.addChild(infoText); var gradeDisplay = song.highestGrade || 'C'; var scoreText = new Text2('High Score: ' + song.highScore + ' | Grade: ' + gradeDisplay, { size: 62.5, fill: 0xFFDD00 }); scoreText.anchor.set(0.5, 0); // Center horizontally, align top vertically scoreText.x = (GAME_WIDTH - 200) / 2; // Center within the background width // Position scoreText below infoText (assuming infoText font size 62.5 is approx height) scoreText.y = infoText.y + 62.5 + verticalPadding; itemContainer.addChild(scoreText); songSelectScreen.scrollContainer.addChild(itemContainer); songSelectScreen.items.push(itemContainer); } } function updateScrollLimits() { var totalHeight = songSelectScreen.items.length * (SONG_ITEM_HEIGHT + 50); var containerHeight = GAME_HEIGHT - 400; maxScrollY = Math.max(0, totalHeight - containerHeight); } function showTitleScreen() { currentGameState = GAME_STATE_TITLE; if (titleScreen.container) { titleScreen.container.visible = true; } if (songSelectScreen.container) { songSelectScreen.container.visible = false; } if (resultsScreen.container) { resultsScreen.container.visible = false; } if (tutorialScreen.container) { tutorialScreen.container.visible = false; } hideGameplayUI(); } function showSongSelectScreen() { currentGameState = GAME_STATE_SONG_SELECT; if (titleScreen.container) { titleScreen.container.visible = false; } if (songSelectScreen.container) { songSelectScreen.container.visible = true; } if (resultsScreen.container) { resultsScreen.container.visible = false; } if (tutorialScreen.container) { tutorialScreen.container.visible = false; } songSelectionInProgress = false; // Reset flag when song select screen is shown hideGameplayUI(); songSelectScrollY = 0; songSelectScreen.scrollContainer.y = 300; } function hideGameplayUI() { if (scoreTxt) { scoreTxt.visible = false; } // Zone indicators removed - no longer hiding background zones // if (leftZone) { // leftZone.visible = false; // } // if (rightZone) { // rightZone.visible = false; // } if (scanner) { scanner.visible = false; } // Also ensure tutorial is hidden if gameplay UI is hidden for other reasons if (tutorialScreen.container && currentGameState !== GAME_STATE_TUTORIAL) { tutorialScreen.container.visible = false; } } function showGameplayUI() { if (scoreTxt) { scoreTxt.visible = true; } // Zone indicators removed - no longer showing background zones // if (leftZone) { // leftZone.visible = true; // } // if (rightZone) { // rightZone.visible = true; // } if (scanner) { scanner.visible = true; } if (titleScreen.container) { titleScreen.container.visible = false; } if (songSelectScreen.container) { songSelectScreen.container.visible = false; } if (tutorialScreen.container) { tutorialScreen.container.visible = false; } if (resultsScreen.container) { resultsScreen.container.visible = false; } } function startCountdown() { countdownTxt = new Text2('3', { size: 200, fill: 0xFFFFFF }); countdownTxt.anchor.set(0.5, 0.5); countdownTxt.x = GAME_WIDTH / 2; countdownTxt.y = GAME_HEIGHT / 2; game.addChild(countdownTxt); // Start countdown acceleration isCountdownActive = true; countdownAcceleration = 1; var startTime = Date.now(); var countdownDuration = 4000; // 4 seconds total var count = 3; var countdownInterval = LK.setInterval(function () { count--; if (count > 0) { countdownTxt.setText(count.toString()); } else if (count === 0) { countdownTxt.setText('GO!'); } else { LK.clearInterval(countdownInterval); if (countdownTxt && countdownTxt.destroy) { countdownTxt.destroy(); } // Keep the accelerated speed when game starts baseParticleSpeed = 5; // Maintain 5x speed isCountdownActive = false; countdownAcceleration = 1; startGame(); } }, 1000); // Smooth acceleration during countdown var accelerationInterval = LK.setInterval(function () { if (!isCountdownActive) { LK.clearInterval(accelerationInterval); return; } var elapsed = Date.now() - startTime; var progress = Math.min(elapsed / countdownDuration, 1); // Smooth acceleration curve - starts slow, ramps up faster var easedProgress = progress * progress; countdownAcceleration = 1 + easedProgress * 4; // 1x to 5x speed }, 50); // Update every 50ms for smooth acceleration } function showResultsScreen() { currentGameState = GAME_STATE_RESULTS; if (titleScreen.container) { titleScreen.container.visible = false; } if (songSelectScreen.container) { songSelectScreen.container.visible = false; } if (resultsScreen.container) { resultsScreen.container.visible = true; } if (tutorialScreen.container) { tutorialScreen.container.visible = false; } hideGameplayUI(); } function startGame() { currentGameState = GAME_STATE_PLAYING; songSelectionInProgress = false; // Selection process is complete, game is starting gameStarted = true; gameStartTime = Date.now(); showGameplayUI(); moveScanner(); // Determine which music to play based on the selected song var musicId = 'gameMusic'; // Default music if (currentSongData === SongDatabase['Remember This']) { musicId = 'rememberThis'; } else if (currentSongData === SongDatabase['Arcade Heart']) { musicId = 'arcadeHeart'; } else if (currentSongData === SongDatabase['Runner']) { musicId = 'runner'; } LK.playMusic(musicId, { loop: false }); } function moveScanner() { if (!scanner || !gameStarted || currentGameState !== GAME_STATE_PLAYING) { return; } tween.stop(scanner); var targetY = scannerIsMovingUp ? SCANNER_Y_MIN : SCANNER_Y_MAX; tween(scanner, { y: targetY }, { duration: SCANNER_CYCLE_DURATION / 2, easing: tween.linear, onFinish: function onFinish() { scannerIsMovingUp = !scannerIsMovingUp; moveScanner(); } }); } function spawnNotesForCurrentTime(currentTime) { if (!currentSongData) { return; } currentSongData.notes.forEach(function (noteData, index) { var noteKey = index + '_' + noteData.time; var alreadySpawned = false; for (var i = 0; i < spawnedNotes.length; i++) { if (spawnedNotes[i] === noteKey) { alreadySpawned = true; break; } } if (!alreadySpawned && currentTime >= noteData.time - NOTE_SPAWN_AHEAD_MS && currentTime <= noteData.time - NOTE_SPAWN_AHEAD_MS + 100) { var calculatedY = calculateNotePosition(noteData.time); var calculatedX = calculateZoneX(noteData.zone, calculatedY); // Pass Y position var processedNoteData = { time: noteData.time, type: noteData.type, x: calculatedX, y: calculatedY, color: noteData.color, duration: noteData.duration }; var note = new Note(processedNoteData, currentTime); notes.push(note); game.addChild(note); note.spawnIn(); spawnedNotes.push(noteKey); } }); } function setupGame(songName) { LK.setScore(0); gameStarted = false; perfectCount = 0; goodCount = 0; missCount = 0; // Reset particle speed for new game baseParticleSpeed = 1; isCountdownActive = false; countdownAcceleration = 1; if (scoreTxt && scoreTxt.destroy) { scoreTxt.destroy(); } scoreTxt = new Text2('0', { size: 100, fill: 0xFFFFFF }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); scoreTxt.setText(LK.getScore()); // createZoneIndicators(); // Removed zone indicators from background if (scanner && scanner.destroy) { scanner.destroy(); } scanner = new Scanner(); scanner.y = SCANNER_Y_MAX; game.addChild(scanner); notes.forEach(function (note) { if (note && note.destroy) { note.destroy(); } }); notes = []; spawnedNotes = []; currentSongData = SongDatabase[songName]; BPM = currentSongData.bpm; BEAT_DURATION_MS = 60 / BPM * 1000; SCANNER_CYCLE_DURATION = currentSongData.scannerCycleDuration; SCANNER_HALF_CYCLE = SCANNER_CYCLE_DURATION / 2; scannerIsMovingUp = true; startCountdown(); if (backgroundParticles.length === 0) { for (var i = 0; i < NUM_BACKGROUND_PARTICLES; i++) { var particle = new BackgroundParticle(); particle.y = Math.random() * GAME_HEIGHT; backgroundParticles.push(particle); game.addChildAt(particle, 0); } } else { backgroundParticles.forEach(function (p) { p.init(); p.y = Math.random() * GAME_HEIGHT; }); } } function checkZoneHit(x, y) { var hitZone = x < GAME_WIDTH / 2 ? 'left' : 'right'; var currentTime = Date.now() - gameStartTime; for (var i = notes.length - 1; i >= 0; i--) { var note = notes[i]; if (!note || !note.active || note.isHit || note.isSpawning || !note.noteGraphics) { continue; } if (note.zone === hitZone) { var noteCoreHeight = note.noteGraphics.height; var noteCenterY = note.y; var scannerY = scanner.y; var perfectHitRadius = noteCoreHeight * PERFECT_HIT_RATIO / 2; var goodHitRadius = noteCoreHeight / 2; var perfectHitTop = noteCenterY - perfectHitRadius; var perfectHitBottom = noteCenterY + perfectHitRadius; var goodHitTop = noteCenterY - goodHitRadius; var goodHitBottom = noteCenterY + goodHitRadius; var accuracyText = null; var hitPoints = 0; var popupColor = 0xFFFFFF; if (scannerY >= perfectHitTop && scannerY <= perfectHitBottom) { accuracyText = "PERFECT"; popupColor = PERFECT_POPUP_COLOR; if (note.type === 'tap') { hitPoints = POINTS_PERFECT_TAP; } else if (note.type === 'hold') { hitPoints = POINTS_PERFECT_HOLD_START; } } else if (scannerY >= goodHitTop && scannerY <= goodHitBottom) { accuracyText = "GOOD"; popupColor = GOOD_POPUP_COLOR; if (note.type === 'tap') { hitPoints = POINTS_GOOD_TAP; } else if (note.type === 'hold') { hitPoints = POINTS_GOOD_HOLD_START; } } if (accuracyText) { note.isHit = true; showAccuracyPopup(accuracyText, note.x, note.y, popupColor); if (accuracyText === "PERFECT") { perfectCount++; } else if (accuracyText === "GOOD") { goodCount++; } if (note.type === 'tap') { note.showHitEffect(); } else if (note.type === 'hold') { note.startHoldEffect(); } if (hitPoints > 0) { LK.setScore(LK.getScore() + hitPoints); scoreTxt.setText(LK.getScore()); } break; } } } } function initializeTutorialScreen() { tutorialScreen.container = new Container(); tutorialScreen.container.visible = false; game.addChild(tutorialScreen.container); var bg = tutorialScreen.container.attachAsset('tutorialBg', { anchorX: 0, anchorY: 0 }); bg.alpha = 0.9; bg.width = GAME_WIDTH; bg.height = GAME_HEIGHT; var currentY = 80; var smallGap = 20; var mediumGap = 40; var largeGap = 60; var textBlockGap = 10; var visualOffsetY = 30; // Offset Y for visuals relative to text center var visualScale = 0.3; var noteVisualXLeft = GAME_WIDTH / 2 - 150; var noteVisualXMid = GAME_WIDTH / 2; var noteVisualXRight = GAME_WIDTH / 2 + 150; // Title var title = _createTutorialText("How to Play", 120, currentY, tutorialScreen.container, 0x00FFFF, 'center'); currentY += title.height + largeGap; // Introduction var introText = _createTutorialText("The scanner line moves up and down the screen. Notes will appear on the screen; interact with them as the scanner aligns with each note!", 60, currentY, tutorialScreen.container, 0xFFFFFF, 'center'); currentY += introText.height + mediumGap; // Zones & Controls var zonesHeader = _createTutorialText("Play Zones & Controls", 80, currentY, tutorialScreen.container, 0xFFFF00, 'center'); currentY += zonesHeader.height + smallGap; var zonesText = _createTutorialText("The screen has a LEFT zone (Blue) and a RIGHT zone (Red). Tap ANYWHERE within the correct zone when a note aligns with the scanner line. You do NOT need to tap the note itself.", 55, currentY, tutorialScreen.container, 0xFFFFFF, 'left'); currentY += zonesText.height + mediumGap; // Tap Notes var tapHeader = _createTutorialText("Tap Notes", 80, currentY, tutorialScreen.container, 0xFFFF00, 'center'); currentY += tapHeader.height + smallGap; var tapDesc = _createTutorialText("Tap the zone once when the note aligns with the scanner.", 55, currentY, tutorialScreen.container, 0xFFFFFF, 'left'); currentY += tapDesc.height + textBlockGap; var regularTapHeader = _createTutorialText("- Regular Tap Notes:", 60, currentY, tutorialScreen.container, 0xFFFFFF, 'left'); currentY += regularTapHeader.height + textBlockGap; var leftTapText = _createTutorialText(" Left Zone (Blue): Tap LEFT.", 50, currentY, tutorialScreen.container, 0xFFFFFF, 'left', 150); _createTutorialNoteVisual('noteCore', COLOR_LEFT_ZONE, noteVisualXLeft, currentY + visualOffsetY, visualScale, tutorialScreen.container); currentY += leftTapText.height + textBlockGap; var rightTapText = _createTutorialText(" Right Zone (Red): Tap RIGHT.", 50, currentY, tutorialScreen.container, 0xFFFFFF, 'left', 150); _createTutorialNoteVisual('noteCore', COLOR_RIGHT_ZONE, noteVisualXRight, currentY + visualOffsetY, visualScale, tutorialScreen.container); currentY += rightTapText.height + mediumGap; var syncTapHeader = _createTutorialText("- Synced Tap Notes (Pink):", 60, currentY, tutorialScreen.container, 0xFFFFFF, 'left'); currentY += syncTapHeader.height + textBlockGap; var syncTapText = _createTutorialText(" Appear on both sides. Tap BOTH zones simultaneously.", 50, currentY, tutorialScreen.container, 0xFFFFFF, 'left', 150); currentY += syncTapText.height + mediumGap; // Space after text, before visuals var scaledVisualHeight = 300 * visualScale; // Assuming noteCore asset height is 300px var visualCenterYForTap = currentY + scaledVisualHeight / 2; _createTutorialNoteVisual('noteCore', COLOR_SYNC_TAP, noteVisualXLeft, visualCenterYForTap, visualScale, tutorialScreen.container); _createTutorialNoteVisual('noteCore', COLOR_SYNC_TAP, noteVisualXRight, visualCenterYForTap, visualScale, tutorialScreen.container); currentY += scaledVisualHeight + largeGap; // Space after visuals line // Hold Notes var holdHeader = _createTutorialText("Hold Notes", 80, currentY, tutorialScreen.container, 0xFFFF00, 'center'); currentY += holdHeader.height + smallGap; var holdDesc = _createTutorialText("Tap and HOLD for the note's trail duration. Release as the trail ends.", 55, currentY, tutorialScreen.container, 0xFFFFFF, 'left'); currentY += holdDesc.height + textBlockGap; var regularHoldHeader = _createTutorialText("- Regular Hold Notes:", 60, currentY, tutorialScreen.container, 0xFFFFFF, 'left'); currentY += regularHoldHeader.height + textBlockGap; var leftHoldText = _createTutorialText(" Left Zone (Blue): Hold LEFT.", 50, currentY, tutorialScreen.container, 0xFFFFFF, 'left', 150); _createTutorialNoteVisual('holdNoteCore', COLOR_LEFT_ZONE, noteVisualXLeft, currentY + visualOffsetY, visualScale, tutorialScreen.container); currentY += leftHoldText.height + textBlockGap; var rightHoldText = _createTutorialText(" Right Zone (Red): Hold RIGHT.", 50, currentY, tutorialScreen.container, 0xFFFFFF, 'left', 150); _createTutorialNoteVisual('holdNoteCore', COLOR_RIGHT_ZONE, noteVisualXRight, currentY + visualOffsetY, visualScale, tutorialScreen.container); currentY += rightHoldText.height + mediumGap; var syncHoldHeader = _createTutorialText("- Synced Hold Notes (Purple):", 60, currentY, tutorialScreen.container, 0xFFFFFF, 'left'); currentY += syncHoldHeader.height + textBlockGap; var syncHoldText = _createTutorialText(" Appear on both sides. Hold BOTH zones simultaneously.", 50, currentY, tutorialScreen.container, 0xFFFFFF, 'left', 150); currentY += syncHoldText.height + mediumGap; // Space after text, before visuals var scaledVisualHeight = 300 * visualScale; // Assuming holdNoteCore asset height is 300px var visualCenterYForHold = currentY + scaledVisualHeight / 2; _createTutorialNoteVisual('holdNoteCore', COLOR_SYNC_HOLD, noteVisualXLeft, visualCenterYForHold, visualScale, tutorialScreen.container); _createTutorialNoteVisual('holdNoteCore', COLOR_SYNC_HOLD, noteVisualXRight, visualCenterYForHold, visualScale, tutorialScreen.container); currentY += scaledVisualHeight + largeGap; // Space after visuals line // Close Button // Position close button near the bottom, ensure currentY doesn't push it off screen var closeButtonY = Math.max(currentY + mediumGap, GAME_HEIGHT - 150); tutorialScreen.closeButton = _createTutorialText("Tap to Close", 70, closeButtonY, tutorialScreen.container, 0xCCCCCC, 'center'); tutorialScreen.closeButton.interactive = true; // For clarity, though handled by game.down } function handleSongItemClick(x, y) { if (songSelectionInProgress) { return true; // Prevent new selections while one is processing } for (var i = 0; i < songSelectScreen.items.length; i++) { var item = songSelectScreen.items[i]; // Ensure item and item.scale exist before trying to use them for tweening if (!item || !item.scale) { continue; } var itemX = item.x; var itemY = item.y + songSelectScreen.scrollContainer.y; // Check if the click is within the bounds of the current item if (x >= itemX && x <= itemX + (GAME_WIDTH - 200) && y >= itemY && y <= itemY + SONG_ITEM_HEIGHT) { songSelectionInProgress = true; // Set flag // Stop any existing scale tweens on this item and reset scale before pulsing tween.stop(item.scale); item.scale.set(1.0, 1.0); // Pulse animation: scale down (shrink) tween(item.scale, { x: 0.9, y: 0.9 }, { duration: 100, // Duration of scale down easing: tween.easeOut, onFinish: function onFinish() { // Pulse animation: scale back to normal tween(item.scale, { x: 1.0, y: 1.0 }, { duration: 100, // Duration of scale back to normal easing: tween.easeIn, onFinish: function onFinish() { // Short timeout to allow pulse to be visible before screen change LK.setTimeout(function () { if (songSelectScreen.container) { songSelectScreen.container.visible = false; } setupGame(item.songName); // songSelectionInProgress will be reset by startGame or other screen transitions }, 150); // Delay before starting game setup } }); } }); return true; // Song clicked and processing started } } return false; // No song item was clicked } function initializeResultsScreen() { resultsScreen.container = new Container(); game.addChild(resultsScreen.container); resultsScreen.container.visible = false; resultsScreen.title = new Text2('Results', { size: 180, fill: 0xFFFFFF, stroke: 0x00FFFF, strokeThickness: 6 }); resultsScreen.title.anchor.set(0.5, 0.5); resultsScreen.title.x = GAME_WIDTH / 2; resultsScreen.title.y = GAME_HEIGHT / 2 - 350; resultsScreen.container.addChild(resultsScreen.title); resultsScreen.scoreText = new Text2('Final Score: 0', { size: 100, fill: 0xFFFFFF }); resultsScreen.scoreText.anchor.set(0.5, 0.5); resultsScreen.scoreText.x = GAME_WIDTH / 2; resultsScreen.scoreText.y = GAME_HEIGHT / 2 - 150; resultsScreen.container.addChild(resultsScreen.scoreText); resultsScreen.highScoreText = new Text2('High Score: 0', { size: 80, fill: 0xFFDD00 }); resultsScreen.highScoreText.anchor.set(0.5, 0.5); resultsScreen.highScoreText.x = GAME_WIDTH / 2; resultsScreen.highScoreText.y = GAME_HEIGHT / 2 - 50; resultsScreen.container.addChild(resultsScreen.highScoreText); resultsScreen.perfectText = new Text2('Perfect: 0', { size: 70, fill: 0x00FF00 }); resultsScreen.perfectText.anchor.set(0.5, 0.5); resultsScreen.perfectText.x = GAME_WIDTH / 2; resultsScreen.perfectText.y = GAME_HEIGHT / 2 + 50; resultsScreen.container.addChild(resultsScreen.perfectText); resultsScreen.goodText = new Text2('Good: 0', { size: 70, fill: 0xFFFF00 }); resultsScreen.goodText.anchor.set(0.5, 0.5); resultsScreen.goodText.x = GAME_WIDTH / 2; resultsScreen.goodText.y = GAME_HEIGHT / 2 + 130; resultsScreen.container.addChild(resultsScreen.goodText); resultsScreen.missText = new Text2('Miss: 0', { size: 70, fill: 0xFF0000 }); resultsScreen.missText.anchor.set(0.5, 0.5); resultsScreen.missText.x = GAME_WIDTH / 2; resultsScreen.missText.y = GAME_HEIGHT / 2 + 210; resultsScreen.container.addChild(resultsScreen.missText); resultsScreen.continueText = new Text2('Tap to Continue', { size: 90, fill: 0xCCCCCC }); resultsScreen.continueText.anchor.set(0.5, 0.5); resultsScreen.continueText.x = GAME_WIDTH / 2; resultsScreen.continueText.y = GAME_HEIGHT / 2 + 430; resultsScreen.container.addChild(resultsScreen.continueText); if (resultsScreen.pulseInterval) { LK.clearInterval(resultsScreen.pulseInterval); } resultsScreen.pulseInterval = LK.setInterval(function () { if (currentGameState === GAME_STATE_RESULTS && resultsScreen.continueText) { var pulseScale = resultsScreen.continueText.scale.x; var pulseDirection = resultsScreen.continueText.pulseDirection || 1; pulseScale += pulseDirection * 0.02; if (pulseScale > 1.1) { pulseScale = 1.1; pulseDirection = -1; } else if (pulseScale < 0.9) { pulseScale = 0.9; pulseDirection = 1; } resultsScreen.continueText.scale.set(pulseScale); resultsScreen.continueText.pulseDirection = pulseDirection; } }, 50); } function showTutorialScreen() { currentGameState = GAME_STATE_TUTORIAL; if (titleScreen.container) { titleScreen.container.visible = false; } if (songSelectScreen.container) { songSelectScreen.container.visible = false; } if (resultsScreen.container) { resultsScreen.container.visible = false; } if (tutorialScreen.container) { tutorialScreen.container.visible = true; } hideGameplayUI(); } initializeTitleScreen(); initializeSongSelectScreen(); initializeResultsScreen(); initializeTutorialScreen(); // Initialize the new tutorial screen for (var i = 0; i < NUM_BACKGROUND_PARTICLES; i++) { var particle = new BackgroundParticle(); particle.y = Math.random() * GAME_HEIGHT; backgroundParticles.push(particle); game.addChildAt(particle, 0); } showTitleScreen(); game.down = function (x, y, obj) { if (currentGameState === GAME_STATE_TITLE) { showSongSelectScreen(); return; } if (currentGameState === GAME_STATE_TUTORIAL) { // Check click on tutorial's close button (entire screen acts as close for simplicity) // More precise check if tutorialScreen.closeButton was an actual button object with bounds: // var cb = tutorialScreen.closeButton; // if (x >= cb.x - cb.width * cb.anchor.x && x <= cb.x + cb.width * (1-cb.anchor.x) && // y >= cb.y - cb.height * cb.anchor.y && y <= cb.y + cb.height * (1-cb.anchor.y)) { showSongSelectScreen(); // } return; } if (currentGameState === GAME_STATE_RESULTS) { showSongSelectScreen(); return; } if (currentGameState === GAME_STATE_SONG_SELECT) { // Check for tutorial button click var tb = songSelectScreen.tutorialButton; if (tb && x >= tb.x - tb.width * tb.anchor.x && x <= tb.x + tb.width * (1 - tb.anchor.x) && y >= tb.y - tb.height && y <= tb.y) { // anchor is 0.5, 1 showTutorialScreen(); return; } if (handleSongItemClick(x, y)) { return; } isDraggingSongList = true; dragStartYSongList = y; return; } if (currentGameState === GAME_STATE_PLAYING && gameStarted) { checkZoneHit(x, y); var hitZoneForDown = x < GAME_WIDTH / 2 ? 'left' : 'right'; for (var i = 0; i < notes.length; i++) { var note = notes[i]; if (!note.active || note.isSpawning || note.holdSuccessfullyCompleted || note.isMissed) { continue; } if (note.zone === hitZoneForDown && note.isHit) { if (note.type === 'hold' && !note.isHolding) { note.isHolding = true; activeHoldNotes[note.zone] = note; } } } isDragging = true; dragStartX = x; dragStartY = y; } }; game.move = function (x, y, obj) { if (currentGameState === GAME_STATE_SONG_SELECT && isDraggingSongList) { var deltaY = y - dragStartYSongList; songSelectScrollY = Math.max(0, Math.min(maxScrollY, songSelectScrollY - deltaY)); songSelectScreen.scrollContainer.y = 300 - songSelectScrollY; dragStartYSongList = y; } if (currentGameState === GAME_STATE_PLAYING && isDragging && gameStarted) {} }; game.up = function (x, y, obj) { if (currentGameState === GAME_STATE_SONG_SELECT) { isDraggingSongList = false; return; } if (currentGameState === GAME_STATE_PLAYING && gameStarted) { var releasedZone = x < GAME_WIDTH / 2 ? 'left' : 'right'; var heldNote = activeHoldNotes[releasedZone]; if (heldNote && heldNote.isHolding) { heldNote.isHolding = false; delete activeHoldNotes[releasedZone]; var currentTimeMs = Date.now() - gameStartTime; var noteExpectedEndTimeMs = heldNote.hitTime + heldNote.duration; var absoluteEndNoteY = heldNote.y + (heldNote.endNote ? heldNote.endNote.y : 0); var hitTimeCycle = heldNote.hitTime % SCANNER_CYCLE_DURATION; var noteScannerMovingUp = hitTimeCycle < SCANNER_HALF_CYCLE; var trailDirection = noteScannerMovingUp ? -1 : 1; var scannerAtEnd; if (trailDirection === -1) { scannerAtEnd = scanner.y <= absoluteEndNoteY + HIT_TOLERANCE_PX_FOR_HOLD_END && scanner.y >= absoluteEndNoteY - HIT_TOLERANCE_PX_FOR_HOLD_END / 2; } else { scannerAtEnd = scanner.y >= absoluteEndNoteY - HIT_TOLERANCE_PX_FOR_HOLD_END && scanner.y <= absoluteEndNoteY + HIT_TOLERANCE_PX_FOR_HOLD_END / 2; } var timeReachedOrPassedEnd = currentTimeMs >= noteExpectedEndTimeMs - HOLD_END_GRACE_PERIOD_MS; var timeNotTooLate = currentTimeMs <= noteExpectedEndTimeMs + HOLD_END_GRACE_PERIOD_MS + 200; if (scannerAtEnd && timeReachedOrPassedEnd && timeNotTooLate) { heldNote.completeHold(); LK.setScore(LK.getScore() + 50); scoreTxt.setText(LK.getScore()); } else { heldNote.failHold(); missCount++; } } isDragging = false; } }; game.update = function () { // Update background particles with new space travel effects backgroundParticles.forEach(function (particle) { particle.updateParticle(); }); // Handle direction changes during gameplay if (currentGameState === GAME_STATE_PLAYING && gameStarted && !isCountdownActive) { var currentTimeForDirectionChange = Date.now(); if (currentTimeForDirectionChange - lastDirectionChangeTime > nextDirectionChangeDelay) { changeParticleDirection(); } } // Handle global particle direction changes (original code) var currentTime = Date.now(); // This part is now handled by the block above for GAME_STATE_PLAYING // and the general update below might be redundant or could be simplified if only for non-playing states // For now, let's keep the existing logic structure for non-playing state direction changes or initial setup if (!isChangingDirection && currentGameState !== GAME_STATE_PLAYING && currentTime - lastDirectionChangeTime > nextDirectionChangeDelay) { changeParticleDirection(); // Call the unified function } // The following block for `if (isChangingDirection)` is now handled by `getCurrentParticleDirection` // which is called by `particle.updateParticle()`. // So, the direct manipulation of `globalParticleDirection` here might be redundant // if particles are always updating based on `getCurrentParticleDirection`. // However, `changeParticleDirection` sets `isChangingDirection = true` and `directionChangeStartTime`. // `getCurrentParticleDirection` uses these to interpolate and then resets `isChangingDirection`. // The logic seems okay as `getCurrentParticleDirection` will handle the smooth transition. if (currentGameState !== GAME_STATE_PLAYING || !gameStarted) { return; } var currentTime = Date.now() - gameStartTime; spawnNotesForCurrentTime(currentTime); for (var i = notes.length - 1; i >= 0; i--) { var note = notes[i]; if (!note) { notes.splice(i, 1); continue; } if (note.type === 'hold' && note.isHolding && note.active && !note.holdSuccessfullyCompleted && !note.isMissed) { note.updateHoldEffects(scanner.y); var currentTimeMsForUpdate = Date.now() - gameStartTime; var noteExpectedEndTimeMsForUpdate = note.hitTime + note.duration; if (currentTimeMsForUpdate > noteExpectedEndTimeMsForUpdate + HOLD_END_GRACE_PERIOD_MS + 500) { if (activeHoldNotes[note.zone] === note) { note.failHold(); delete activeHoldNotes[note.zone]; missCount++; } } } if (note.active && !note.isHit && !note.isMissed && !note.isSpawning) { var timeDiff = currentTime - note.hitTime; var scannerPassed = false; if (scannerIsMovingUp && scanner.y < note.y - 50) { scannerPassed = true; } else if (!scannerIsMovingUp && scanner.y > note.y + 50) { scannerPassed = true; } if (scannerPassed && timeDiff > 300) { note.isMissed = true; note.showMissEffect(); missCount++; } } if (!note.active) { notes.splice(i, 1); if (game.children.indexOf(note) > -1) { game.removeChild(note); } if (note.destroy) { note.destroy(); } } } if (currentSongData && currentTime > currentSongData.totalLength + 500 && currentGameState === GAME_STATE_PLAYING) { LK.stopMusic(); gameStarted = false; if (scanner) { tween.stop(scanner); } var finalScore = LK.getScore(); // --- Letter Grade Calculation (MOVED HERE) --- var totalNotes = 0; if (currentSongData && currentSongData.notes) { totalNotes = currentSongData.notes.length; } var grade = "C"; if (totalNotes > 0) { if (missCount === 0 && perfectCount === totalNotes) { grade = "S"; } else { var hitCount = perfectCount + goodCount; var ratio = hitCount / totalNotes; if (ratio >= 0.95) { grade = "A"; } else if (ratio >= 0.80) { grade = "B"; } else { grade = "C"; } } } else { grade = "-"; } var songName = null; for (var key in SongDatabase) { if (SongDatabase[key] === currentSongData) { songName = key; break; } } if (songName) { var wasUpdated = updateHighScore(songName, finalScore, grade); // Now grade is available if (wasUpdated) { // Update songSelectScreen items if they are already created if (songSelectScreen.items) { for (var j = 0; j < songSelectScreen.items.length; j++) { var item = songSelectScreen.items[j]; if (item.songName === songName) { // Assuming scoreText is the 4th child (index 3) var scoreTextDisplay = item.children[3]; if (scoreTextDisplay && scoreTextDisplay.setText) { var gradeDisplay = SongDatabase[songName].highestGrade || 'C'; scoreTextDisplay.setText('High Score: ' + SongDatabase[songName].highScore + ' | Grade: ' + gradeDisplay); } break; } } } } if (resultsScreen.highScoreText) { resultsScreen.highScoreText.setText("High Score: " + SongDatabase[songName].highScore); } } if (resultsScreen.scoreText) { resultsScreen.scoreText.setText("Final Score: " + finalScore); } if (resultsScreen.perfectText) { resultsScreen.perfectText.setText("Perfect: " + perfectCount); } if (resultsScreen.goodText) { resultsScreen.goodText.setText("Good: " + goodCount); } if (resultsScreen.missText) { resultsScreen.missText.setText("Miss: " + missCount); } if (!resultsScreen.gradeText) { resultsScreen.gradeText = new Text2('Grade: ' + grade, { size: 120, fill: 0x00FFFF }); resultsScreen.gradeText.anchor.set(0.5, 0.5); resultsScreen.gradeText.x = GAME_WIDTH / 2; resultsScreen.gradeText.y = GAME_HEIGHT / 2 + 280; resultsScreen.container.addChild(resultsScreen.gradeText); } else { resultsScreen.gradeText.setText('Grade: ' + grade); resultsScreen.gradeText.visible = true; } showResultsScreen(); } if (scanner && game.children.indexOf(scanner) > -1) { game.removeChild(scanner); game.addChild(scanner); } };
===================================================================
--- original.js
+++ change.js
@@ -10,15 +10,12 @@
var BackgroundParticle = Container.expand(function () {
var self = Container.call(this);
self.particleGraphics = null;
self.baseSpeed = 0; // Store the original speed
- self.speed = 0;
self.initialScale = 1;
self.baseAlpha = 0.1;
- self.initialX = 0;
self.sineAmplitude = 0;
self.sineFrequency = 0;
- self.distanceTraveled = 0; // For sine wave calculation
self._startTwinkleAnimation = function () {
tween.stop(self, {
alpha: true
});
@@ -72,70 +69,59 @@
anchorY: 0.5
});
self.particleGraphics.tint = color;
self.baseSpeed = (0.5 + Math.random() * 1.5) * scale;
- self.speed = self.baseSpeed; // Initialize speed with baseSpeed
- self.x = Math.random() * GAME_WIDTH;
- self.initialX = self.x;
self.sineAmplitude = 20 + Math.random() * 60;
self.sineFrequency = 0.0015 + Math.random() * 0.003;
- self.y = -Math.random() * 100 - self.particleGraphics.height * scale; // Start above screen
self.baseAlpha = 0.1 + Math.random() * 0.7;
self.alpha = self.baseAlpha;
- self.distanceTraveled = 0;
self._startTwinkleAnimation();
};
self.updateParticle = function () {
- // Apply current speed multiplier (for countdown effect)
- var currentSpeed = self.baseSpeed * countdownSpeedMultiplier;
// Get current direction (smoothly interpolated if changing)
var currentDirection = getCurrentParticleDirection();
+ // Calculate total speed (base * game speed * countdown acceleration)
+ var totalSpeed = self.baseSpeed * baseParticleSpeed * (isCountdownActive ? countdownAcceleration : 1);
+ // Store previous position for sine calculation
+ var prevX = self.x;
+ var prevY = self.y;
// Move in the current direction
- self.x += currentDirection.x * currentSpeed;
- self.y += currentDirection.y * currentSpeed;
- // Update distance traveled for sine wave
- self.distanceTraveled += currentSpeed;
- // Apply sine wave perpendicular to movement direction
- var sineOffset = self.sineAmplitude * Math.sin(self.sineFrequency * self.distanceTraveled);
- // Calculate perpendicular direction for sine wave
- var perpX = -currentDirection.y; // Perpendicular to direction
+ self.x += currentDirection.x * totalSpeed;
+ self.y += currentDirection.y * totalSpeed;
+ // Apply sine wave effect perpendicular to movement direction
+ // But keep it subtle and relative to distance traveled
+ var distanceThisFrame = totalSpeed;
+ var sineOffset = self.sineAmplitude * Math.sin(self.sineFrequency * (self.x + self.y));
+ // Apply sine offset perpendicular to movement direction
+ var perpX = -currentDirection.y;
var perpY = currentDirection.x;
- // Apply sine offset
- var sineX = self.initialX + perpX * sineOffset;
- var sineY = self.y + perpY * sineOffset; // Note: self.y is already updated, this adds sine relative to current y
- // Only apply sine to X if moving primarily vertically, and vice versa
- if (Math.abs(currentDirection.y) > Math.abs(currentDirection.x)) {
- self.x = sineX; // Apply sine to X
- } else {
- // Apply sine to Y (Adjusting initialY for horizontal movement)
- // This logic might need refinement if initialY is meant to be a fixed vertical baseline
- // For now, let's keep it simple and apply sine to the current y
- self.y = self.y + perpY * sineOffset; // Apply sine to Y
- }
- // Reset particle if it goes off screen
- var margin = self.particleGraphics.height * self.initialScale;
+ self.x += perpX * sineOffset * 0.01; // Much smaller sine effect
+ self.y += perpY * sineOffset * 0.01;
+ // Reset particle if it goes off screen - improved logic
+ var margin = 100;
+ var shouldReset = false;
+ var newX, newY;
if (self.x < -margin || self.x > GAME_WIDTH + margin || self.y < -margin || self.y > GAME_HEIGHT + margin) {
- self.init();
- // Start from appropriate edge based on current direction
- if (currentDirection.y > 0.5) {
- // Moving mostly down
- self.y = -Math.random() * 100 - margin;
- self.x = Math.random() * GAME_WIDTH;
- } else if (currentDirection.y < -0.5) {
- // Moving mostly up
- self.y = GAME_HEIGHT + Math.random() * 100 + margin;
- self.x = Math.random() * GAME_WIDTH;
- } else if (currentDirection.x > 0.5) {
- // Moving mostly right
- self.x = -Math.random() * 100 - margin;
- self.y = Math.random() * GAME_HEIGHT;
+ // Respawn from the opposite side based on movement direction
+ if (currentDirection.x > 0.1) {
+ // Moving right, spawn from left
+ newX = -margin + Math.random() * 50;
+ newY = Math.random() * GAME_HEIGHT;
+ } else if (currentDirection.x < -0.1) {
+ // Moving left, spawn from right
+ newX = GAME_WIDTH + margin - Math.random() * 50;
+ newY = Math.random() * GAME_HEIGHT;
+ } else if (currentDirection.y > 0.1) {
+ // Moving down, spawn from top
+ newX = Math.random() * GAME_WIDTH;
+ newY = -margin + Math.random() * 50;
} else {
- // Moving mostly left (or any other case)
- self.x = GAME_WIDTH + Math.random() * 100 + margin;
- self.y = Math.random() * GAME_HEIGHT;
+ // Moving up, spawn from bottom
+ newX = Math.random() * GAME_WIDTH;
+ newY = GAME_HEIGHT + margin - Math.random() * 50;
}
- self.initialX = self.x; // Reset initialX for sine wave calculation
- self.distanceTraveled = 0; // Reset distance for sine wave
+ self.x = newX;
+ self.y = newY;
}
};
self.init();
return self;
@@ -970,9 +956,9 @@
/****
* Game Code
****/
-// Add these global variables near the top with other game variables
+// Add these global variables (replace the previous ones)
var globalParticleDirection = {
x: 0,
y: 1
}; // Start moving downward
@@ -984,9 +970,10 @@
var directionChangeDuration = 1000; // 1 second
var isChangingDirection = false;
var lastDirectionChangeTime = 0;
var nextDirectionChangeDelay = 15000 + Math.random() * 15000; // 15-30 seconds
-var countdownSpeedMultiplier = 1;
+var baseParticleSpeed = 1; // Base speed multiplier
+var countdownAcceleration = 1; // Separate from base speed
var isCountdownActive = false;
var GAME_WIDTH = 2048;
var GAME_HEIGHT = 2732;
var SAFETY_MARGIN_TOP_PERCENT = 0.05;
@@ -7984,8 +7971,9 @@
countdownTxt.y = GAME_HEIGHT / 2;
game.addChild(countdownTxt);
// Start countdown acceleration
isCountdownActive = true;
+ countdownAcceleration = 1;
var startTime = Date.now();
var countdownDuration = 4000; // 4 seconds total
var count = 3;
var countdownInterval = LK.setInterval(function () {
@@ -7998,10 +7986,12 @@
LK.clearInterval(countdownInterval);
if (countdownTxt && countdownTxt.destroy) {
countdownTxt.destroy();
}
+ // Keep the accelerated speed when game starts
+ baseParticleSpeed = 5; // Maintain 5x speed
isCountdownActive = false;
- countdownSpeedMultiplier = 1; // Reset to normal speed
+ countdownAcceleration = 1;
startGame();
}
}, 1000);
// Smooth acceleration during countdown
@@ -8013,9 +8003,9 @@
var elapsed = Date.now() - startTime;
var progress = Math.min(elapsed / countdownDuration, 1);
// Smooth acceleration curve - starts slow, ramps up faster
var easedProgress = progress * progress;
- countdownSpeedMultiplier = 1 + easedProgress * 4; // 1x to 5x speed
+ countdownAcceleration = 1 + easedProgress * 4; // 1x to 5x speed
}, 50); // Update every 50ms for smooth acceleration
}
function showResultsScreen() {
currentGameState = GAME_STATE_RESULTS;
@@ -8107,8 +8097,12 @@
gameStarted = false;
perfectCount = 0;
goodCount = 0;
missCount = 0;
+ // Reset particle speed for new game
+ baseParticleSpeed = 1;
+ isCountdownActive = false;
+ countdownAcceleration = 1;
if (scoreTxt && scoreTxt.destroy) {
scoreTxt.destroy();
}
scoreTxt = new Text2('0', {
The word 'Pulsar' in a glowing neon SVG in futuristic font. The word is half blue on the left and half red on the right. In-Game asset. 2d. High contrast. No shadows
Remove the background.
A thin expanding ring with energy distortion ``` - Outer ring: 4-6 pixels thick, bright cyan (#00FFFF) - Inner ring: 2-3 pixels thick, white (#FFFFFF) - Ring thickness: Tapers from thick to thin as it expands - Transparency: Ring itself at 80% opacity - Background: Completely transparent - Edge treatment: Soft anti-aliased edges, slight glow effect - Optional: Subtle "energy crackle" texture within the ring. In-Game asset. 2d. High contrast. No shadows
Soft, lingering light effect ``` - Center: Warm orange (#FF6600) at 40% opacity - Middle: Yellow (#FFAA00) at 25% opacity - Edge: Transparent - Shape: Perfect circle with very soft, wide falloff. In-Game asset. 2d. High contrast. No shadows