Code edit (1 edits merged)
Please save this source code
User prompt
update with: // Reduce spawn ahead time even more var NOTE_SPAWN_AHEAD_MS = 400; // Reduced from 800ms to 400ms // Fix the tap feedback positioning in game.down function game.down = function (x, y, obj) { if (playerHealth <= 0 || !gameStartTime) { return; } var currentTime = Date.now() - gameStartTime; // Show tap feedback AT THE TAP LOCATION, not on scanner var feedback = game.attachAsset('tapFeedback', { anchorX: 0.5, anchorY: 0.5 }); feedback.x = x; feedback.y = y; // Changed from scanner.y to actual tap y position feedback.alpha = 0.7; tween(feedback, { alpha: 0, scaleX: 1.5, scaleY: 1.5 }, { duration: 300, onFinish: function onFinish() { if (feedback && feedback.destroy) { feedback.destroy(); } } }); // Rest of the hit detection code stays the same... var hitOccurred = false; var tapRadius = 100; for (var i = notes.length - 1; i >= 0; i--) { var note = notes[i]; if (!note || !note.active || note.isHit || note.isSpawning) { continue; } // Check distance from tap var dx = x - note.x; var dy = y - note.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < tapRadius) { // Check timing - note should be near scanner var scannerDiff = Math.abs(scanner.y - note.y); if (scannerDiff < HIT_TOLERANCE_PX) { note.isHit = true; note.showHitEffect(); var timeDiff = Math.abs(currentTime - note.hitTime); var points = 10; if (timeDiff < 50) points = 20; // Perfect hit else if (timeDiff < 100) points = 15; // Great hit LK.setScore(LK.getScore() + points); scoreTxt.setText(LK.getScore()); hitOccurred = true; if (LK.getScore() >= TARGET_SCORE_TO_WIN) { LK.showYouWin(); gameStartTime = null; tween.stop(scanner); return; } break; } } } }; // Also improve the spawn timing logic function spawnNotesForCurrentTime(currentTime) { if (!currentSongData) { return; } currentSongData.notes.forEach(function (noteData, index) { var noteKey = index + '_' + noteData.time; if (spawnedNotes.indexOf(noteKey) === -1) { var timeUntilHit = noteData.time - currentTime; // More precise spawning - only spawn when really needed if (timeUntilHit <= NOTE_SPAWN_AHEAD_MS && timeUntilHit > -200) { if (!noteData.color) { noteData.color = NOTE_COLORS[Math.floor(Math.random() * NOTE_COLORS.length)]; } var properY = calculateNoteYFromTime(noteData.time, currentTime); var correctedNoteData = { time: noteData.time, type: noteData.type, x: noteData.x, y: properY, color: noteData.color, duration: noteData.duration, path: noteData.path }; var note = new Note(correctedNoteData, 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: // Game Constants (update these) var NOTE_SPAWN_AHEAD_MS = 800; // Reduce from 2000 to 800ms - notes appear closer to hit time var SCANNER_CYCLE_DURATION = 4000; // 4 seconds total cycle var SCANNER_HALF_CYCLE = SCANNER_CYCLE_DURATION / 2; // 2 seconds each direction // Modified spawnNotesForCurrentTime function function spawnNotesForCurrentTime(currentTime) { if (!currentSongData) { return; } currentSongData.notes.forEach(function (noteData, index) { var noteKey = index + '_' + noteData.time; // Only spawn if not already spawned and within spawn window if (spawnedNotes.indexOf(noteKey) === -1) { // Calculate when this note should spawn based on scanner position var timeUntilHit = noteData.time - currentTime; // Spawn note when scanner is approaching (not all at once) if (timeUntilHit <= NOTE_SPAWN_AHEAD_MS && timeUntilHit > -500) { // Assign random color if not specified if (!noteData.color) { noteData.color = NOTE_COLORS[Math.floor(Math.random() * NOTE_COLORS.length)]; } // Calculate proper Y position based on scanner cycle var properY = calculateNoteYFromTime(noteData.time, currentTime); // Override the Y position with calculated value var correctedNoteData = { time: noteData.time, type: noteData.type, x: noteData.x, y: properY, // Use calculated Y position color: noteData.color, duration: noteData.duration, path: noteData.path }; var note = new Note(correctedNoteData, currentTime); notes.push(note); game.addChild(note); note.spawnIn(); spawnedNotes.push(noteKey); } } }); } // Fixed calculateNoteYFromTime function function calculateNoteYFromTime(noteTime, currentTime) { // Determine where in the scanner cycle this note should be hit var cycleTime = noteTime % SCANNER_CYCLE_DURATION; if (cycleTime < SCANNER_HALF_CYCLE) { // First half of cycle: scanner moving down var progressInPhase = cycleTime / SCANNER_HALF_CYCLE; return SCANNER_Y_MIN + (progressInPhase * PLAY_AREA_HEIGHT); } else { // Second half of cycle: scanner moving up var progressInPhase = (cycleTime - SCANNER_HALF_CYCLE) / SCANNER_HALF_CYCLE; return SCANNER_Y_MAX - (progressInPhase * PLAY_AREA_HEIGHT); } } // Update the default song data with better timing spread var defaultSongData = { bpm: 120, scannerCycleDuration: 4000, notes: [ // First cycle (0-4000ms) - spread across scanner movement { time: 500, type: 'tap', x: 500, y: 0 }, // Early in down movement { time: 1200, type: 'tap', x: 800, y: 0 }, // Mid down movement { time: 2000, type: 'tap', x: 1200, y: 0 }, // Bottom turn { time: 2800, type: 'hold', x: 600, y: 0, duration: 500 }, // Mid up movement { time: 3500, type: 'tap', x: 900, y: 0 }, // Near top // Second cycle (4000-8000ms) { time: 4500, type: 'tap', x: 400, y: 0 }, { time: 5200, type: 'drag', x: 1000, y: 0, path: [{ x: 1200, y: 0 }] }, { time: 6000, type: 'tap', x: 700, y: 0 }, { time: 6800, type: 'hold', x: 800, y: 0, duration: 400 }, { time: 7500, type: 'tap', x: 500, y: 0 }, // Third cycle (8000-12000ms) { time: 8500, type: 'tap', x: 600, y: 0 }, { time: 9200, type: 'tap', x: 1100, y: 0 }, { time: 10000, type: 'drag', x: 400, y: 0, path: [{ x: 600, y: 0 }] }, { time: 10800, type: 'tap', x: 900, y: 0 }, { time: 11500, type: 'tap', x: 800, y: 0 } ] };
User prompt
the scanline should start at the bottom and move upwards
User prompt
Please fix the bug: 'TypeError: self.createHoldTrail is not a function' in or related to this line: 'self.createHoldTrail();' Line Number: 68
User prompt
Please fix the bug: 'Set is not a constructor' in or related to this line: 'var spawnedNotes = new Set(); // Track which notes have been spawned' Line Number: 216
Code edit (1 edits merged)
Please save this source code
User prompt
Do not remove player health if enemy hit is succrssful.
User prompt
Do not spawn enemies at the very top and bottom 10%.
User prompt
Please fix the bug: 'LK.gui.add is not a function. (In 'LK.gui.add(pip)', 'LK.gui.add' is undefined)' in or related to this line: 'LK.gui.add(pip); // Add to general GUI layer' Line Number: 327
Code edit (1 edits merged)
Please save this source code
User prompt
Rhythm Lane Defender
Initial prompt
**RHYTHM TOWER DEFENSE PROTOTYPE - GAME DESIGN SPECIFICATION** Create a single-screen rhythm tower defense game with these specifications: **Core Layout:** - 3 vertical lanes of equal width spanning the screen - Horizontal timing bar that spans full screen width - Timing bar continuously travels up and down the screen in smooth, deliberate motion - Enemies spawn as stationary objects positioned throughout the lane areas **Game Mechanics:** - Timing bar completes one full up-down cycle over 8 beats at 100-110 BPM (slower, more strategic pace) - Enemies appear as stationary targets in lanes, aligned to beat timing - **Maximum 2 enemies can share the same horizontal position (row) across all lanes** - respecting two-thumb input limitation - Different enemy patterns can appear on upstroke vs downstroke passes - Player presses lane key when timing bar intersects stationary enemy AND on the beat - Successful hits destroy enemies with visual destruction effect - Score system tracks successful hits with end-game rating **Enemy System:** - Simple colored shapes positioned at various heights within lanes - Spawn timing aligned to beat structure with strategic positioning - Stationary once spawned - timing bar does the moving - Visual destruction effect when successfully eliminated - Spawn logic ensures never more than 2 enemies at same horizontal level **Controls & Feedback:** - 3 lane input keys (suggest A, S, D) - Visual feedback for key presses - Clear indication when timing bar aligns with enemies - Beat-synchronized visual cues throughout game **UI Elements:** - Score display - Health indicator (reduced when enemies are missed) - Lane key indicators at bottom - End-game rating system based on performance **Technical Focus:** - Smooth timing bar animation at deliberate pace (8-beat cycles) - Precise hit detection when bar intersects stationary enemies on beat - Enemy spawn system that respects 2-enemy-per-row maximum - Clean visual feedback for successful hits and misses
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ var Note = Container.expand(function (noteData, spawnTime) { var self = Container.call(this); // Note properties self.noteData = noteData; self.type = noteData.type; // 'tap', 'hold', 'flick' self.targetX = noteData.x; self.targetY = noteData.y; self.color = noteData.color || 0x00ffff; self.hitTime = noteData.time; self.duration = noteData.duration || 0; // For hold notes self.flickTarget = noteData.flickTarget || null; // For flick notes self.spawnTime = spawnTime; self.zone = noteData.x < GAME_WIDTH / 2 ? 'left' : 'right'; // Determine zone // State self.active = true; self.isHit = false; self.isMissed = false; self.isSpawning = true; self.isHolding = false; // For hold notes self.holdStarted = false; // Visual components var assetName = self.type === 'hold' ? 'holdNoteCore' : self.type === 'flick' ? 'flickNoteCore' : 'noteCore'; self.noteGraphics = self.attachAsset(assetName, { anchorX: 0.5, anchorY: 0.5 }); self.noteGraphics.tint = self.color; self.glowRing = self.attachAsset('glowRing', { anchorX: 0.5, anchorY: 0.5 }); self.glowRing.tint = self.color; self.glowRing.alpha = 0.3; // Position self.x = self.targetX; self.y = self.targetY; // Start invisible and scale up self.alpha = 0; self.scaleX = 0.1; self.scaleY = 0.1; // Hold trail for hold notes self.holdTrails = []; if (self.type === 'hold' && self.duration > 0) { self.createHoldTrail(); } self.createHoldTrail = function () { var endY = self.targetY + self.duration / 4 * 100; var trailLength = Math.abs(endY - self.targetY); var segments = Math.max(1, Math.floor(trailLength / 20)); for (var i = 0; i < segments; i++) { var trail = self.attachAsset('holdTrail', { anchorX: 0.5, anchorY: 0 }); trail.tint = self.color; trail.alpha = 0.6; trail.y = i * 20; trail.height = 20; self.holdTrails.push(trail); } }; 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.showHitEffect = function () { 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.showFlickEffect = function (targetX, targetY) { LK.getSound('flickSound').play(); self.explodeIntoParticles(); // Create shooting trail effect self.createFlickTrail(targetX, targetY); tween(self, { scaleX: 1.5, scaleY: 1.5, alpha: 0 }, { duration: 200, onFinish: function onFinish() { self.active = false; } }); }; self.createFlickTrail = function (targetX, targetY) { var trailParticles = []; var particleCount = 20; var startX = self.x; var startY = self.y; for (var i = 0; i < particleCount; i++) { var particle = game.attachAsset('flickTrail', { anchorX: 0.5, anchorY: 0.5 }); particle.tint = self.color; particle.x = startX; particle.y = startY; particle.alpha = 0.8; trailParticles.push(particle); var delay = i * 30; // Stagger the particles var progress = i / particleCount; var finalX = startX + (targetX - startX) * progress; var finalY = startY + (targetY - startY) * progress; setTimeout(function (p, fx, fy) { tween(p, { x: fx, y: fy, alpha: 0, scaleX: 0.3, scaleY: 0.3 }, { duration: 800, easing: tween.easeOut, onFinish: function onFinish() { if (p && p.destroy) { p.destroy(); } } }); }, delay, particle, finalX, finalY); } // Create new note at target location after delay setTimeout(function () { if (targetX && targetY) { createFlickResultNote(targetX, targetY, self.color); } }, 600); }; self.showMissEffect = function () { 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.explodeIntoParticles = function () { var particleCount = 12; for (var i = 0; i < particleCount; i++) { var particle = game.attachAsset('particle', { anchorX: 0.5, anchorY: 0.5 }); particle.tint = self.color; particle.x = self.x; particle.y = self.y; var angle = i / particleCount * Math.PI * 2; var speed = 150 + Math.random() * 100; var targetX = self.x + Math.cos(angle) * speed; var targetY = self.y + Math.sin(angle) * speed; tween(particle, { x: targetX, y: targetY, alpha: 0, scaleX: 0.3, scaleY: 0.3 }, { duration: 600, easing: tween.easeOut, onFinish: function onFinish() { if (particle && particle.destroy) { particle.destroy(); } } }); } }; 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.3; self.line = self.attachAsset('scannerLine', { anchorX: 0, anchorY: 0.5 }); self.x = 0; self.isMovingUp = true; // Start moving up return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x000000 }); /**** * Game Code ****/ // Game Constants var GAME_WIDTH = 2048; var GAME_HEIGHT = 2732; var BPM = 120; var BEAT_DURATION_MS = 60 / BPM * 1000; var SCANNER_Y_MIN = 200; var SCANNER_Y_MAX = GAME_HEIGHT - 300; var PLAY_AREA_HEIGHT = SCANNER_Y_MAX - SCANNER_Y_MIN; var INITIAL_PLAYER_HEALTH = 5; var HIT_TOLERANCE_PX = 80; var TARGET_SCORE_TO_WIN = 1500; var NOTE_SPAWN_AHEAD_MS = 3000; // 3 seconds ahead for better reaction time var SCANNER_CYCLE_DURATION = 4000; // Game State Variables var playerHealth; var healthPips = []; var scoreTxt; var countdownTxt; var scanner; var scannerIsMovingUp = true; var gameStartTime; var gameStarted = false; var notes = []; var currentSongData; var spawnedNotes = []; // Using array instead of Set var leftZone, rightZone; var isDragging = false; var dragStartX, dragStartY; var currentTrail = []; // Color palette for notes var NOTE_COLORS = [0x00ffff, // Cyan 0xff00ff, // Magenta 0xffff00, // Yellow 0x00ff00, // Green 0xff8800, // Orange 0x8800ff // Purple ]; // Better spaced test song data var defaultSongData = { bpm: 120, scannerCycleDuration: 4000, notes: [ // Measure 1 - spread throughout 4 seconds { time: 1000, type: 'tap', x: 400, y: 400 }, { time: 2000, type: 'tap', x: 1200, y: 500 }, { time: 3000, type: 'hold', x: 600, y: 350, duration: 800 }, { time: 4000, type: 'tap', x: 1000, y: 600 }, // Measure 2 - 4-8 seconds { time: 5000, type: 'flick', x: 500, y: 300, flickTarget: { x: 800, y: 450 } }, { time: 6000, type: 'tap', x: 1400, y: 550 }, { time: 7000, type: 'tap', x: 300, y: 400 }, { time: 8000, type: 'hold', x: 1100, y: 350, duration: 600 }, // Measure 3 - 8-12 seconds { time: 9000, type: 'tap', x: 700, y: 500 }, { time: 10000, type: 'flick', x: 1300, y: 400, flickTarget: { x: 500, y: 300 } }, { time: 11000, type: 'tap', x: 800, y: 600 }, { time: 12000, type: 'tap', x: 400, y: 350 }, // Measure 4 - 12-16 seconds { time: 13000, type: 'hold', x: 1200, y: 450, duration: 1000 }, { time: 14500, type: 'tap', x: 600, y: 300 }, { time: 15500, type: 'flick', x: 900, y: 550, flickTarget: { x: 1100, y: 400 } }, { time: 16000, type: 'tap', x: 500, y: 500 }] }; function createZoneIndicators() { // Left zone indicator 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; // Right zone indicator 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 createFlickResultNote(x, y, color) { var resultNote = game.attachAsset('noteCore', { anchorX: 0.5, anchorY: 0.5 }); resultNote.x = x; resultNote.y = y; resultNote.tint = color; resultNote.alpha = 0; resultNote.scaleX = 0.1; resultNote.scaleY = 0.1; // Scale in effect tween(resultNote, { alpha: 1, scaleX: 1, scaleY: 1 }, { duration: 300, easing: tween.easeOut, onFinish: function onFinish() { // Fade out after short display setTimeout(function () { tween(resultNote, { alpha: 0, scaleX: 1.2, scaleY: 1.2 }, { duration: 500, onFinish: function onFinish() { if (resultNote && resultNote.destroy) { resultNote.destroy(); } } }); }, 1000); } }); } 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); var count = 3; var countdownInterval = setInterval(function () { count--; if (count > 0) { countdownTxt.setText(count.toString()); } else if (count === 0) { countdownTxt.setText('GO!'); } else { clearInterval(countdownInterval); if (countdownTxt && countdownTxt.destroy) { countdownTxt.destroy(); } startGame(); } }, 1000); } function startGame() { gameStarted = true; gameStartTime = Date.now(); moveScanner(); LK.playMusic('gameMusic', { loop: true }); } function initHealthDisplay() { healthPips.forEach(function (pip) { if (pip && pip.destroy) { pip.destroy(); } }); healthPips = []; var pipSpacing = 70; var totalPipsWidth = (INITIAL_PLAYER_HEALTH - 1) * pipSpacing + 60; var startX = GAME_WIDTH - totalPipsWidth - 30; for (var i = 0; i < INITIAL_PLAYER_HEALTH; i++) { var pip = LK.getAsset('healthPip', { anchorX: 0.5, anchorY: 0.5 }); pip.x = startX + i * pipSpacing; pip.y = 70; LK.gui.top.addChild(pip); healthPips.push(pip); } } function updateHealthDisplay() { for (var i = 0; i < healthPips.length; i++) { if (healthPips[i]) { healthPips[i].visible = i < playerHealth; } } if (playerHealth <= 0) { LK.showGameOver(); gameStartTime = null; gameStarted = false; tween.stop(scanner); } } function moveScanner() { if (!scanner || !gameStarted) 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; // Check if already spawned 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 + 1000) { if (!noteData.color) { noteData.color = NOTE_COLORS[Math.floor(Math.random() * NOTE_COLORS.length)]; } var note = new Note(noteData, currentTime); notes.push(note); game.addChild(note); note.spawnIn(); spawnedNotes.push(noteKey); } }); } function setupGame() { LK.setScore(0); playerHealth = INITIAL_PLAYER_HEALTH; gameStarted = false; // Score Text 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()); // Health Display initHealthDisplay(); updateHealthDisplay(); // Zone indicators createZoneIndicators(); // Scanner - start at bottom if (scanner && scanner.destroy) { scanner.destroy(); } scanner = new Scanner(); scanner.y = SCANNER_Y_MAX; // Start at bottom game.addChild(scanner); // Clear previous notes notes.forEach(function (note) { if (note && note.destroy) { note.destroy(); } }); notes = []; spawnedNotes = []; // Load song data currentSongData = defaultSongData; BPM = currentSongData.bpm; BEAT_DURATION_MS = 60 / BPM * 1000; scannerIsMovingUp = true; // Will move up first // Start countdown instead of immediate game start startCountdown(); } 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) continue; // Check if note is in the same zone if (note.zone === hitZone) { var scannerDiff = Math.abs(scanner.y - note.y); var timeDiff = Math.abs(currentTime - note.hitTime); if (scannerDiff < HIT_TOLERANCE_PX) { note.isHit = true; if (note.type === 'flick' && note.flickTarget) { note.showFlickEffect(note.flickTarget.x, note.flickTarget.y); } else { note.showHitEffect(); } var points = 10; if (timeDiff < 50) points = 20;else if (timeDiff < 100) points = 15; LK.setScore(LK.getScore() + points); scoreTxt.setText(LK.getScore()); if (LK.getScore() >= TARGET_SCORE_TO_WIN) { LK.showYouWin(); gameStartTime = null; gameStarted = false; tween.stop(scanner); return; } break; } } } } // Initial setup call setupGame(); game.down = function (x, y, obj) { if (playerHealth <= 0 || !gameStarted) return; // Show tap feedback in the zone var feedback = game.attachAsset('tapFeedback', { anchorX: 0.5, anchorY: 0.5 }); feedback.x = x; feedback.y = scanner.y; feedback.alpha = 0.7; tween(feedback, { alpha: 0, scaleX: 1.5, scaleY: 1.5 }, { duration: 300, onFinish: function onFinish() { if (feedback && feedback.destroy) { feedback.destroy(); } } }); checkZoneHit(x, y); // Start drag tracking for flick notes isDragging = true; dragStartX = x; dragStartY = y; currentTrail = []; }; game.move = function (x, y, obj) { if (!isDragging || !gameStarted) return; // Create trail particles during drag var trailParticle = game.attachAsset('flickTrail', { anchorX: 0.5, anchorY: 0.5 }); trailParticle.x = x; trailParticle.y = y; trailParticle.alpha = 0.6; trailParticle.tint = 0xffffff; currentTrail.push(trailParticle); // Fade out trail particle tween(trailParticle, { alpha: 0, scaleX: 0.5, scaleY: 0.5 }, { duration: 500, onFinish: function onFinish() { if (trailParticle && trailParticle.destroy) { trailParticle.destroy(); } } }); }; game.up = function (x, y, obj) { if (!isDragging || !gameStarted) return; isDragging = false; // Clean up current trail currentTrail.forEach(function (particle) { if (particle && particle.destroy) { particle.destroy(); } }); currentTrail = []; }; game.update = function () { if (playerHealth <= 0 || !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; } // Check for missed notes 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(); playerHealth--; updateHealthDisplay(); if (playerHealth <= 0) { return; } } } // Cleanup inactive notes if (!note.active) { notes.splice(i, 1); if (game.children.indexOf(note) > -1) { game.removeChild(note); } if (note.destroy) { note.destroy(); } } } };
===================================================================
--- original.js
+++ change.js
@@ -9,25 +9,26 @@
var Note = Container.expand(function (noteData, spawnTime) {
var self = Container.call(this);
// Note properties
self.noteData = noteData;
- self.type = noteData.type; // 'tap', 'hold', 'drag'
+ self.type = noteData.type; // 'tap', 'hold', 'flick'
self.targetX = noteData.x;
self.targetY = noteData.y;
self.color = noteData.color || 0x00ffff;
self.hitTime = noteData.time;
self.duration = noteData.duration || 0; // For hold notes
- self.path = noteData.path || []; // For drag notes
+ self.flickTarget = noteData.flickTarget || null; // For flick notes
self.spawnTime = spawnTime;
+ self.zone = noteData.x < GAME_WIDTH / 2 ? 'left' : 'right'; // Determine zone
// State
self.active = true;
self.isHit = false;
self.isMissed = false;
self.isSpawning = true;
self.isHolding = false; // For hold notes
self.holdStarted = false;
// Visual components
- var assetName = self.type === 'hold' ? 'holdNoteCore' : self.type === 'drag' ? 'dragNoteCore' : 'noteCore';
+ var assetName = self.type === 'hold' ? 'holdNoteCore' : self.type === 'flick' ? 'flickNoteCore' : 'noteCore';
self.noteGraphics = self.attachAsset(assetName, {
anchorX: 0.5,
anchorY: 0.5
});
@@ -44,11 +45,15 @@
// Start invisible and scale up
self.alpha = 0;
self.scaleX = 0.1;
self.scaleY = 0.1;
- // Define createHoldTrail method first
+ // Hold trail for hold notes
+ self.holdTrails = [];
+ if (self.type === 'hold' && self.duration > 0) {
+ self.createHoldTrail();
+ }
self.createHoldTrail = function () {
- var endY = self.targetY + self.duration / 4 * 100; // Rough calculation
+ var endY = self.targetY + self.duration / 4 * 100;
var trailLength = Math.abs(endY - self.targetY);
var segments = Math.max(1, Math.floor(trailLength / 20));
for (var i = 0; i < segments; i++) {
var trail = self.attachAsset('holdTrail', {
@@ -61,13 +66,8 @@
trail.height = 20;
self.holdTrails.push(trail);
}
};
- // Hold trail for hold notes
- self.holdTrails = [];
- if (self.type === 'hold' && self.duration > 0) {
- self.createHoldTrail();
- }
self.spawnIn = function () {
self.isSpawning = true;
tween(self, {
alpha: 1,
@@ -83,9 +83,8 @@
};
self.showHitEffect = function () {
LK.getSound('hitSound').play();
self.explodeIntoParticles();
- // Scale and fade effect
tween(self, {
scaleX: 1.5,
scaleY: 1.5,
alpha: 0
@@ -95,11 +94,70 @@
self.active = false;
}
});
};
+ self.showFlickEffect = function (targetX, targetY) {
+ LK.getSound('flickSound').play();
+ self.explodeIntoParticles();
+ // Create shooting trail effect
+ self.createFlickTrail(targetX, targetY);
+ tween(self, {
+ scaleX: 1.5,
+ scaleY: 1.5,
+ alpha: 0
+ }, {
+ duration: 200,
+ onFinish: function onFinish() {
+ self.active = false;
+ }
+ });
+ };
+ self.createFlickTrail = function (targetX, targetY) {
+ var trailParticles = [];
+ var particleCount = 20;
+ var startX = self.x;
+ var startY = self.y;
+ for (var i = 0; i < particleCount; i++) {
+ var particle = game.attachAsset('flickTrail', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ particle.tint = self.color;
+ particle.x = startX;
+ particle.y = startY;
+ particle.alpha = 0.8;
+ trailParticles.push(particle);
+ var delay = i * 30; // Stagger the particles
+ var progress = i / particleCount;
+ var finalX = startX + (targetX - startX) * progress;
+ var finalY = startY + (targetY - startY) * progress;
+ setTimeout(function (p, fx, fy) {
+ tween(p, {
+ x: fx,
+ y: fy,
+ alpha: 0,
+ scaleX: 0.3,
+ scaleY: 0.3
+ }, {
+ duration: 800,
+ easing: tween.easeOut,
+ onFinish: function onFinish() {
+ if (p && p.destroy) {
+ p.destroy();
+ }
+ }
+ });
+ }, delay, particle, finalX, finalY);
+ }
+ // Create new note at target location after delay
+ setTimeout(function () {
+ if (targetX && targetY) {
+ createFlickResultNote(targetX, targetY, self.color);
+ }
+ }, 600);
+ };
self.showMissEffect = function () {
LK.getSound('missSound').play();
- // Flash red then fade
tween(self.noteGraphics, {
tint: 0xff0000
}, {
duration: 150,
@@ -149,29 +207,27 @@
return self;
});
var Scanner = Container.expand(function () {
var self = Container.call(this);
- // Scanner glow (background)
self.glow = self.attachAsset('scannerGlow', {
anchorX: 0,
anchorY: 0.5
});
self.glow.alpha = 0.3;
- // Scanner line (foreground)
self.line = self.attachAsset('scannerLine', {
anchorX: 0,
anchorY: 0.5
});
self.x = 0;
- self.isMovingDown = true;
+ self.isMovingUp = true; // Start moving up
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
- backgroundColor: 0x000000 // Pure black background
+ backgroundColor: 0x000000
});
/****
* Game Code
@@ -185,22 +241,27 @@
var SCANNER_Y_MAX = GAME_HEIGHT - 300;
var PLAY_AREA_HEIGHT = SCANNER_Y_MAX - SCANNER_Y_MIN;
var INITIAL_PLAYER_HEALTH = 5;
var HIT_TOLERANCE_PX = 80;
-var TARGET_SCORE_TO_WIN = 1000;
-var NOTE_SPAWN_AHEAD_MS = 400; // Reduced from 800ms to 400ms
-var SCANNER_CYCLE_DURATION = 4000; // 4 seconds total cycle
-var SCANNER_HALF_CYCLE = SCANNER_CYCLE_DURATION / 2; // 2 seconds each direction
+var TARGET_SCORE_TO_WIN = 1500;
+var NOTE_SPAWN_AHEAD_MS = 3000; // 3 seconds ahead for better reaction time
+var SCANNER_CYCLE_DURATION = 4000;
// Game State Variables
var playerHealth;
var healthPips = [];
var scoreTxt;
+var countdownTxt;
var scanner;
-var scannerIsMovingDown = true;
+var scannerIsMovingUp = true;
var gameStartTime;
+var gameStarted = false;
var notes = [];
var currentSongData;
-var spawnedNotes = []; // Track which notes have been spawned
+var spawnedNotes = []; // Using array instead of Set
+var leftZone, rightZone;
+var isDragging = false;
+var dragStartX, dragStartY;
+var currentTrail = [];
// Color palette for notes
var NOTE_COLORS = [0x00ffff,
// Cyan
0xff00ff,
@@ -212,115 +273,210 @@
0xff8800,
// Orange
0x8800ff // Purple
];
-// Default test song data
+// Better spaced test song data
var defaultSongData = {
bpm: 120,
scannerCycleDuration: 4000,
- // This should match SCANNER_CYCLE_DURATION
notes: [
- // First cycle (0-4000ms) - spread across scanner movement
+ // Measure 1 - spread throughout 4 seconds
{
- time: 500,
+ time: 1000,
type: 'tap',
- x: 500,
- y: 0
- },
- // Early in down movement
- {
- time: 1200,
- type: 'tap',
- x: 800,
- y: 0
- },
- // Mid down movement
- {
+ x: 400,
+ y: 400
+ }, {
time: 2000,
type: 'tap',
x: 1200,
- y: 0
- },
- // Bottom turn
- {
- time: 2800,
+ y: 500
+ }, {
+ time: 3000,
type: 'hold',
x: 600,
- y: 0,
- duration: 500
- },
- // Mid up movement
- {
- time: 3500,
+ y: 350,
+ duration: 800
+ }, {
+ time: 4000,
type: 'tap',
- x: 900,
- y: 0
+ x: 1000,
+ y: 600
},
- // Near top
- // Second cycle (4000-8000ms)
+ // Measure 2 - 4-8 seconds
{
- time: 4500,
- type: 'tap',
- x: 400,
- y: 0
+ time: 5000,
+ type: 'flick',
+ x: 500,
+ y: 300,
+ flickTarget: {
+ x: 800,
+ y: 450
+ }
}, {
- time: 5200,
- type: 'drag',
- x: 1000,
- y: 0,
- path: [{
- x: 1200,
- y: 0
- }]
- }, {
time: 6000,
type: 'tap',
- x: 700,
- y: 0
+ x: 1400,
+ y: 550
}, {
- time: 6800,
- type: 'hold',
- x: 800,
- y: 0,
- duration: 400
- }, {
- time: 7500,
+ time: 7000,
type: 'tap',
- x: 500,
- y: 0
+ x: 300,
+ y: 400
+ }, {
+ time: 8000,
+ type: 'hold',
+ x: 1100,
+ y: 350,
+ duration: 600
},
- // Third cycle (8000-12000ms)
+ // Measure 3 - 8-12 seconds
{
- time: 8500,
+ time: 9000,
type: 'tap',
- x: 600,
- y: 0
+ x: 700,
+ y: 500
}, {
- time: 9200,
+ time: 10000,
+ type: 'flick',
+ x: 1300,
+ y: 400,
+ flickTarget: {
+ x: 500,
+ y: 300
+ }
+ }, {
+ time: 11000,
type: 'tap',
- x: 1100,
- y: 0
+ x: 800,
+ y: 600
}, {
- time: 10000,
- type: 'drag',
+ time: 12000,
+ type: 'tap',
x: 400,
- y: 0,
- path: [{
- x: 600,
- y: 0
- }]
+ y: 350
+ },
+ // Measure 4 - 12-16 seconds
+ {
+ time: 13000,
+ type: 'hold',
+ x: 1200,
+ y: 450,
+ duration: 1000
}, {
- time: 10800,
+ time: 14500,
type: 'tap',
+ x: 600,
+ y: 300
+ }, {
+ time: 15500,
+ type: 'flick',
x: 900,
- y: 0
+ y: 550,
+ flickTarget: {
+ x: 1100,
+ y: 400
+ }
}, {
- time: 11500,
+ time: 16000,
type: 'tap',
- x: 800,
- y: 0
+ x: 500,
+ y: 500
}]
};
+function createZoneIndicators() {
+ // Left zone indicator
+ 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;
+ // Right zone indicator
+ 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 createFlickResultNote(x, y, color) {
+ var resultNote = game.attachAsset('noteCore', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ resultNote.x = x;
+ resultNote.y = y;
+ resultNote.tint = color;
+ resultNote.alpha = 0;
+ resultNote.scaleX = 0.1;
+ resultNote.scaleY = 0.1;
+ // Scale in effect
+ tween(resultNote, {
+ alpha: 1,
+ scaleX: 1,
+ scaleY: 1
+ }, {
+ duration: 300,
+ easing: tween.easeOut,
+ onFinish: function onFinish() {
+ // Fade out after short display
+ setTimeout(function () {
+ tween(resultNote, {
+ alpha: 0,
+ scaleX: 1.2,
+ scaleY: 1.2
+ }, {
+ duration: 500,
+ onFinish: function onFinish() {
+ if (resultNote && resultNote.destroy) {
+ resultNote.destroy();
+ }
+ }
+ });
+ }, 1000);
+ }
+ });
+}
+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);
+ var count = 3;
+ var countdownInterval = setInterval(function () {
+ count--;
+ if (count > 0) {
+ countdownTxt.setText(count.toString());
+ } else if (count === 0) {
+ countdownTxt.setText('GO!');
+ } else {
+ clearInterval(countdownInterval);
+ if (countdownTxt && countdownTxt.destroy) {
+ countdownTxt.destroy();
+ }
+ startGame();
+ }
+ }, 1000);
+}
+function startGame() {
+ gameStarted = true;
+ gameStartTime = Date.now();
+ moveScanner();
+ LK.playMusic('gameMusic', {
+ loop: true
+ });
+}
function initHealthDisplay() {
healthPips.forEach(function (pip) {
if (pip && pip.destroy) {
pip.destroy();
@@ -349,72 +505,55 @@
}
if (playerHealth <= 0) {
LK.showGameOver();
gameStartTime = null;
+ gameStarted = false;
tween.stop(scanner);
}
}
function moveScanner() {
- if (!scanner) return;
+ if (!scanner || !gameStarted) return;
tween.stop(scanner);
- var targetY = scannerIsMovingDown ? SCANNER_Y_MAX : SCANNER_Y_MIN;
- // Set initial position - if moving down, start at top, if moving up, start at bottom
- if (scanner.y === undefined || scanner.y === SCANNER_Y_MIN) {
- scanner.y = scannerIsMovingDown ? SCANNER_Y_MIN : SCANNER_Y_MAX;
- }
+ var targetY = scannerIsMovingUp ? SCANNER_Y_MIN : SCANNER_Y_MAX;
tween(scanner, {
y: targetY
}, {
- duration: currentSongData.scannerCycleDuration / 2,
+ duration: SCANNER_CYCLE_DURATION / 2,
easing: tween.linear,
onFinish: function onFinish() {
- scannerIsMovingDown = !scannerIsMovingDown;
+ scannerIsMovingUp = !scannerIsMovingUp;
moveScanner();
}
});
}
function spawnNotesForCurrentTime(currentTime) {
- if (!currentSongData) {
- return;
- }
+ if (!currentSongData) return;
currentSongData.notes.forEach(function (noteData, index) {
var noteKey = index + '_' + noteData.time;
- // Only spawn if not already spawned and within spawn window
- if (spawnedNotes.indexOf(noteKey) === -1) {
- // Calculate when this note should spawn based on scanner position
- var timeUntilHit = noteData.time - currentTime;
- // More precise spawning - only spawn when really needed
- if (timeUntilHit <= NOTE_SPAWN_AHEAD_MS && timeUntilHit > -200) {
- // Assign random color if not specified
- if (!noteData.color) {
- noteData.color = NOTE_COLORS[Math.floor(Math.random() * NOTE_COLORS.length)];
- }
- // Calculate proper Y position based on scanner cycle
- var properY = calculateNoteYFromTime(noteData.time, currentTime);
- // Override the Y position with calculated value
- var correctedNoteData = {
- time: noteData.time,
- type: noteData.type,
- x: noteData.x,
- y: properY,
- // Use calculated Y position
- color: noteData.color,
- duration: noteData.duration,
- path: noteData.path
- };
- var note = new Note(correctedNoteData, currentTime);
- notes.push(note);
- game.addChild(note);
- note.spawnIn();
- spawnedNotes.push(noteKey);
+ // Check if already spawned
+ 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 + 1000) {
+ if (!noteData.color) {
+ noteData.color = NOTE_COLORS[Math.floor(Math.random() * NOTE_COLORS.length)];
+ }
+ var note = new Note(noteData, currentTime);
+ notes.push(note);
+ game.addChild(note);
+ note.spawnIn();
+ spawnedNotes.push(noteKey);
+ }
});
}
function setupGame() {
LK.setScore(0);
playerHealth = INITIAL_PLAYER_HEALTH;
- gameStartTime = Date.now();
+ gameStarted = false;
// Score Text
if (scoreTxt && scoreTxt.destroy) {
scoreTxt.destroy();
}
@@ -427,9 +566,11 @@
scoreTxt.setText(LK.getScore());
// Health Display
initHealthDisplay();
updateHealthDisplay();
- // Scanner
+ // Zone indicators
+ createZoneIndicators();
+ // Scanner - start at bottom
if (scanner && scanner.destroy) {
scanner.destroy();
}
scanner = new Scanner();
@@ -446,46 +587,56 @@
// Load song data
currentSongData = defaultSongData;
BPM = currentSongData.bpm;
BEAT_DURATION_MS = 60 / BPM * 1000;
- // Start scanner movement - begin at bottom moving up
- scannerIsMovingDown = false;
- moveScanner();
- LK.playMusic('gameMusic', {
- loop: true
- });
+ scannerIsMovingUp = true; // Will move up first
+ // Start countdown instead of immediate game start
+ startCountdown();
}
-function getScannerProgress() {
- if (!scanner) return 0;
- var totalRange = SCANNER_Y_MAX - SCANNER_Y_MIN;
- var currentPos = scanner.y - SCANNER_Y_MIN;
- return currentPos / totalRange;
-}
-function calculateNoteYFromTime(noteTime, currentTime) {
- // Determine where in the scanner cycle this note should be hit
- var cycleTime = noteTime % SCANNER_CYCLE_DURATION; // Use global constant
- if (cycleTime < SCANNER_HALF_CYCLE) {
- // First half of cycle: scanner moving down
- var progressInPhase = cycleTime / SCANNER_HALF_CYCLE;
- return SCANNER_Y_MIN + progressInPhase * PLAY_AREA_HEIGHT;
- } else {
- // Second half of cycle: scanner moving up
- var progressInPhase = (cycleTime - SCANNER_HALF_CYCLE) / SCANNER_HALF_CYCLE;
- return SCANNER_Y_MAX - progressInPhase * PLAY_AREA_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) continue;
+ // Check if note is in the same zone
+ if (note.zone === hitZone) {
+ var scannerDiff = Math.abs(scanner.y - note.y);
+ var timeDiff = Math.abs(currentTime - note.hitTime);
+ if (scannerDiff < HIT_TOLERANCE_PX) {
+ note.isHit = true;
+ if (note.type === 'flick' && note.flickTarget) {
+ note.showFlickEffect(note.flickTarget.x, note.flickTarget.y);
+ } else {
+ note.showHitEffect();
+ }
+ var points = 10;
+ if (timeDiff < 50) points = 20;else if (timeDiff < 100) points = 15;
+ LK.setScore(LK.getScore() + points);
+ scoreTxt.setText(LK.getScore());
+ if (LK.getScore() >= TARGET_SCORE_TO_WIN) {
+ LK.showYouWin();
+ gameStartTime = null;
+ gameStarted = false;
+ tween.stop(scanner);
+ return;
+ }
+ break;
+ }
+ }
}
}
// Initial setup call
setupGame();
game.down = function (x, y, obj) {
- if (playerHealth <= 0 || !gameStartTime) return;
- var currentTime = Date.now() - gameStartTime;
- // Show tap feedback AT THE TAP LOCATION, not on scanner
+ if (playerHealth <= 0 || !gameStarted) return;
+ // Show tap feedback in the zone
var feedback = game.attachAsset('tapFeedback', {
anchorX: 0.5,
anchorY: 0.5
});
feedback.x = x;
- feedback.y = y; // Changed from scanner.y to actual tap y position
+ feedback.y = scanner.y;
feedback.alpha = 0.7;
tween(feedback, {
alpha: 0,
scaleX: 1.5,
@@ -497,48 +648,56 @@
feedback.destroy();
}
}
});
- // Check for note hits
- var hitOccurred = false;
- var tapRadius = 100; // Radius for tap detection
- for (var i = notes.length - 1; i >= 0; i--) {
- var note = notes[i];
- if (!note || !note.active || note.isHit || note.isSpawning) continue;
- // Check distance from tap
- var dx = x - note.x;
- var dy = y - note.y;
- var distance = Math.sqrt(dx * dx + dy * dy);
- if (distance < tapRadius) {
- // Check timing - note should be near scanner
- var scannerDiff = Math.abs(scanner.y - note.y);
- if (scannerDiff < HIT_TOLERANCE_PX) {
- note.isHit = true;
- note.showHitEffect();
- var timeDiff = Math.abs(currentTime - note.hitTime);
- var points = 10;
- if (timeDiff < 50) points = 20; // Perfect hit
- else if (timeDiff < 100) points = 15; // Great hit
- LK.setScore(LK.getScore() + points);
- scoreTxt.setText(LK.getScore());
- hitOccurred = true;
- if (LK.getScore() >= TARGET_SCORE_TO_WIN) {
- LK.showYouWin();
- gameStartTime = null;
- tween.stop(scanner);
- return;
- }
- break;
+ checkZoneHit(x, y);
+ // Start drag tracking for flick notes
+ isDragging = true;
+ dragStartX = x;
+ dragStartY = y;
+ currentTrail = [];
+};
+game.move = function (x, y, obj) {
+ if (!isDragging || !gameStarted) return;
+ // Create trail particles during drag
+ var trailParticle = game.attachAsset('flickTrail', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ trailParticle.x = x;
+ trailParticle.y = y;
+ trailParticle.alpha = 0.6;
+ trailParticle.tint = 0xffffff;
+ currentTrail.push(trailParticle);
+ // Fade out trail particle
+ tween(trailParticle, {
+ alpha: 0,
+ scaleX: 0.5,
+ scaleY: 0.5
+ }, {
+ duration: 500,
+ onFinish: function onFinish() {
+ if (trailParticle && trailParticle.destroy) {
+ trailParticle.destroy();
}
}
- }
+ });
};
+game.up = function (x, y, obj) {
+ if (!isDragging || !gameStarted) return;
+ isDragging = false;
+ // Clean up current trail
+ currentTrail.forEach(function (particle) {
+ if (particle && particle.destroy) {
+ particle.destroy();
+ }
+ });
+ currentTrail = [];
+};
game.update = function () {
- if (playerHealth <= 0 || !gameStartTime) return;
+ if (playerHealth <= 0 || !gameStarted) return;
var currentTime = Date.now() - gameStartTime;
- // Spawn new notes
spawnNotesForCurrentTime(currentTime);
- // Update existing notes
for (var i = notes.length - 1; i >= 0; i--) {
var note = notes[i];
if (!note) {
notes.splice(i, 1);
@@ -547,15 +706,14 @@
// Check for missed notes
if (note.active && !note.isHit && !note.isMissed && !note.isSpawning) {
var timeDiff = currentTime - note.hitTime;
var scannerPassed = false;
- if (scannerIsMovingDown && scanner.y > note.y + 50) {
+ if (scannerIsMovingUp && scanner.y < note.y - 50) {
scannerPassed = true;
- } else if (!scannerIsMovingDown && scanner.y < note.y - 50) {
+ } else if (!scannerIsMovingUp && scanner.y > note.y + 50) {
scannerPassed = true;
}
- if (scannerPassed && timeDiff > 200) {
- // Grace period
+ if (scannerPassed && timeDiff > 300) {
note.isMissed = true;
note.showMissEffect();
playerHealth--;
updateHealthDisplay();
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