/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // Car class var Car = Container.expand(function () { var self = Container.call(this); var carSprite = self.attachAsset('car', { anchorX: 0.5, anchorY: 0.5 }); // Store sprite reference for external access self.sprite = carSprite; // For possible future effects self.flash = function () { LK.effects.flashObject(self, 0xffffff, 200); }; // Camera shake animation self.shake = function () { var originalX = self.x; var originalY = self.y; // Quick shake sequence tween(self, { x: originalX - 15, y: originalY - 10 }, { duration: 50, easing: tween.easeOut, onFinish: function onFinish() { tween(self, { x: originalX + 15, y: originalY + 10 }, { duration: 50, easing: tween.easeIn, onFinish: function onFinish() { tween(self, { x: originalX - 10, y: originalY - 5 }, { duration: 50, easing: tween.easeOut, onFinish: function onFinish() { tween(self, { x: originalX, y: originalY }, { duration: 50, easing: tween.easeIn }); } }); } }); } }); }; return self; }); // LaneLine class (for visual effect) var LaneLine = Container.expand(function () { var self = Container.call(this); var lineSprite = self.attachAsset('laneLine', { anchorX: 0.5, anchorY: 0.5 }); // Store sprite reference self.sprite = lineSprite; // Add initial glow effect self.glowPhase = Math.random() * Math.PI * 2; // Pulse animation self.update = function () { self.glowPhase += 0.08; var glowIntensity = 0.7 + Math.sin(self.glowPhase) * 0.3; self.alpha = glowIntensity; }; return self; }); // Note class var Note = Container.expand(function () { var self = Container.call(this); // Pick a random note asset var assetIdx = Math.floor(Math.random() * NOTE_ASSET_IDS.length); var noteSprite = self.attachAsset(NOTE_ASSET_IDS[assetIdx], { anchorX: 0.5, anchorY: 0.5 }); // Lane index (0,1,2) self.lane = 1; // For state tracking self.lastY = undefined; self.lastIntersecting = false; // Store sprite reference for effects self.sprite = noteSprite; // Collection effect self.collect = function () { // Scale up and fade out with rotation tween(self, { scaleX: 2.5, scaleY: 2.5, alpha: 0, rotation: Math.PI * 2 }, { duration: 400, easing: tween.easeOut }); // Move up slightly tween(self, { y: self.y - 150 }, { duration: 400, easing: tween.easeOut }); }; return self; }); // Obstacle class var Obstacle = Container.expand(function () { var self = Container.call(this); var obsSprite = self.attachAsset('obstacle', { anchorX: 0.5, anchorY: 0.5 }); // Lane index (0,1,2) self.lane = 1; // For state tracking self.lastY = undefined; self.lastIntersecting = false; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x0a0020 // Deep synthwave night }); /**** * Game Code ****/ // Note class // --- Game constants --- // Neon synthwave colors // Sounds and music (IDs are placeholders, engine will load as used) // unique id // unique id // unique id // unique id // unique id // unique id // unique id var NOTE_ASSET_IDS = ['note', // 0 'note1', // 1 'note2', // 2 'note3', // 3 'note4', // 4 'note5', // 5 'note6', // 6 'note7', // 7 'note8' // 8 ]; var NUM_LANES = 3; var LANE_WIDTH = 410; // 2048 / 3 ≈ 682, but leave margins for neon effect var ROAD_TOP = 400; var ROAD_BOTTOM = 2732 - 200; var CAR_Y = 2200; var BASE_OBSTACLE_SPEED = 22; // px per frame var BASE_NOTE_SPEED = 22; var OBSTACLE_SPEED = BASE_OBSTACLE_SPEED; var NOTE_SPEED = BASE_NOTE_SPEED; var LANE_X = [410, // left 1024, // center 1638 // right ]; // --- Music BPM and spawn sync --- var BPM = 120; // Example BPM, can be changed if music changes var BASE_BEAT_INTERVAL = Math.round(60 / BPM * 60 * 2.5); // frames per beat (60fps) - increased spacing var BEAT_INTERVAL = BASE_BEAT_INTERVAL; var lastBeatTick = 0; // --- Game state --- var car = null; var carLane = 1; // 0: left, 1: center, 2: right var obstacles = []; var notes = []; var laneLines = []; var score = 0; var scoreTxt = null; var dragSide = null; // 'left' or 'right' for touch controls var isPowerUpActive = false; var powerUpEndTime = 0; var powerUpTimerText = null; var speedMultiplier = 1; var powerUpCooldownEndTime = 0; // --- Background music --- LK.playMusic('neonTrack'); // --- Draw neon road lanes --- function createLaneLines() { // Remove old lines for (var i = 0; i < laneLines.length; i++) { laneLines[i].destroy(); } laneLines = []; // Draw vertical lines between lanes for (var i = 1; i < NUM_LANES; i++) { var x = (LANE_X[i - 1] + LANE_X[i]) / 2; // Create dashed lines with varying opacity for depth for (var y = ROAD_TOP - 200; y < ROAD_BOTTOM + 400; y += 120) { var line = new LaneLine(); line.x = x; line.y = y; // Create gradient effect with opacity based on position var distanceFromCenter = Math.abs(y - (ROAD_TOP + ROAD_BOTTOM) / 2); var maxDistance = (ROAD_BOTTOM - ROAD_TOP) / 2; var baseAlpha = 1.0 - distanceFromCenter / maxDistance * 0.3; line.alpha = baseAlpha; // Add slight scale variation for perspective var scaleFactor = 1.0 - distanceFromCenter / maxDistance * 0.3; line.scaleX = scaleFactor; line.scaleY = scaleFactor * 1.5; // Make lines taller for better visibility game.addChild(line); laneLines.push(line); } } } createLaneLines(); // --- Create car --- car = new Car(); car.x = LANE_X[carLane]; car.y = CAR_Y; game.addChild(car); // --- Score display --- scoreTxt = new Text2('0', { size: 120, fill: 0xFFF200, font: "Impact" }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); // --- Power-up timer display --- powerUpTimerText = new Text2('', { size: 80, fill: 0xff00ff, font: "Impact" }); powerUpTimerText.anchor.set(0.5, 0); powerUpTimerText.y = 150; powerUpTimerText.visible = false; LK.gui.top.addChild(powerUpTimerText); // --- Touch controls --- // Touch/click left or right half of screen to move car function handleDown(x, y, obj) { // Only respond to touches below the top 200px (avoid menu) if (y < 200) { return; } // Expand touch logic: if tap is left of car center, go left; if right, go right // If tap is very close to car center (±80px), do nothing (prevents accidental moves) var carCenterX = car.x; if (x < carCenterX - 80) { dragSide = 'left'; moveCar(-1); } else if (x > carCenterX + 80) { dragSide = 'right'; moveCar(1); } else { dragSide = null; } } function handleUp(x, y, obj) { dragSide = null; } function handleMove(x, y, obj) { // Optional: swipe to move, but for now, tap only } game.down = handleDown; game.up = handleUp; game.move = handleMove; // --- Move car between lanes --- function moveCar(dir) { var newLane = carLane + dir; if (newLane < 0) { newLane = 0; } if (newLane > 2) { newLane = 2; } if (newLane !== carLane) { carLane = newLane; // Animate car to new lane tween(car, { x: LANE_X[carLane] }, { duration: 120, easing: tween.cubicOut }); } } // --- Spawn obstacles and notes in sync with music --- function spawnBeatObjects() { // Randomly decide: 1 or 2 obstacles, and 0 or 1 note var availableLanes = [0, 1, 2]; // Place 1 or 2 obstacles var numObstacles = Math.random() < 0.5 ? 1 : 2; for (var i = 0; i < numObstacles; i++) { if (availableLanes.length === 0) { break; } var idx = Math.floor(Math.random() * availableLanes.length); var lane = availableLanes[idx]; availableLanes.splice(idx, 1); var obs = new Obstacle(); obs.lane = lane; obs.x = LANE_X[lane]; obs.y = ROAD_TOP - 100; obs.lastY = obs.y; obs.lastIntersecting = false; obstacles.push(obs); game.addChild(obs); } // Place a note in a random free lane (if any) if (availableLanes.length > 0 && Math.random() < 0.7) { var idx = Math.floor(Math.random() * availableLanes.length); var lane = availableLanes[idx]; var note = new Note(); note.lane = lane; note.x = LANE_X[lane]; note.y = ROAD_TOP - 100; note.lastY = note.y; note.lastIntersecting = false; notes.push(note); game.addChild(note); } } // --- Main game update loop --- game.update = function () { // --- Dynamic difficulty: first 20 points easy, then increase every 20 points --- var currentScore = LK.getScore(); var difficultyLevel = 0; if (currentScore < 20) { difficultyLevel = 0; } else { difficultyLevel = Math.floor((currentScore - 20) / 20) + 1; } OBSTACLE_SPEED = (BASE_OBSTACLE_SPEED + difficultyLevel * 4) * speedMultiplier; NOTE_SPEED = (BASE_NOTE_SPEED + difficultyLevel * 4) * speedMultiplier; // Handle power-up timer if (isPowerUpActive) { var remainingTime = Math.max(0, powerUpEndTime - LK.ticks); var remainingSeconds = Math.ceil(remainingTime / 60); powerUpTimerText.setText('POWER: ' + remainingSeconds + 's'); // End power-up if (remainingTime <= 0) { isPowerUpActive = false; speedMultiplier = 1; car.sprite.tint = 0xffffff; tween(car, { scaleX: 1, scaleY: 1 }, { duration: 300, easing: tween.easeIn }); // Start cooldown timer (15 seconds) powerUpCooldownEndTime = LK.ticks + 900; // 15 seconds at 60fps } } // Handle cooldown timer if (powerUpCooldownEndTime > LK.ticks) { var cooldownRemaining = Math.max(0, powerUpCooldownEndTime - LK.ticks); var cooldownSeconds = Math.ceil(cooldownRemaining / 60); powerUpTimerText.setText('COOLDOWN: ' + cooldownSeconds + 's'); powerUpTimerText.visible = true; // Keep button disabled during cooldown glaudButton.alpha = 0.3; } else if (!isPowerUpActive && powerUpTimerText.visible) { // Hide timer and re-enable button when cooldown ends powerUpTimerText.visible = false; // Re-enable button with wing expansion tween(glaudButton, { alpha: 1 }, { duration: 300 }); // Expand wings back to normal tween(leftWing, { scaleX: 2, x: -100, alpha: 0.8 }, { duration: 600, easing: tween.elasticOut }); tween(rightWing, { scaleX: 2, x: 100, alpha: 0.8 }, { duration: 600, easing: tween.elasticOut }); // Flash edges to indicate ready LK.effects.flashObject(leftEdge, 0xffffff, 400); LK.effects.flashObject(rightEdge, 0xffffff, 400); } // Minimum beat interval: don't go below 30 frames (~0.5s at 60fps) for wider spacing BEAT_INTERVAL = Math.max(BASE_BEAT_INTERVAL - difficultyLevel * 8, 30); // Animate lane lines for neon effect for (var i = 0; i < laneLines.length; i++) { var line = laneLines[i]; line.y += OBSTACLE_SPEED; // Recycle lines that go off screen if (line.y > ROAD_BOTTOM + 400) { line.y = ROAD_TOP - 400; } // Update gradient effect based on new position var distanceFromCenter = Math.abs(line.y - (ROAD_TOP + ROAD_BOTTOM) / 2); var maxDistance = (ROAD_BOTTOM - ROAD_TOP) / 2; var baseAlpha = 1.0 - distanceFromCenter / maxDistance * 0.3; // Calculate distance to car for glow effect var distanceToCar = Math.abs(line.y - car.y); var glowRadius = 300; // Pixels around car where glow effect happens var glowIntensity = 0; if (distanceToCar < glowRadius) { // Calculate glow based on proximity (closer = brighter) glowIntensity = 1.0 - distanceToCar / glowRadius; // Add extra brightness baseAlpha = Math.min(1.0, baseAlpha + glowIntensity * 0.5); // Scale up slightly when glowing var glowScale = 1.0 + glowIntensity * 0.3; line.scaleX = line.scaleX * glowScale; line.scaleY = line.scaleY * glowScale; // Add tint effect for extra brightness var tintValue = Math.floor(glowIntensity * 100); var tintColor = 0xff << 16 | tintValue << 8 | 0xff; // Purple-ish white glow line.sprite.tint = tintColor; } else { // Reset tint when not glowing line.sprite.tint = 0xffffff; } // Apply base alpha but let the pulse effect in update() modulate it line.sprite.alpha = baseAlpha; // Update perspective scaling var scaleFactor = 1.0 - distanceFromCenter / maxDistance * 0.3; line.scaleX = scaleFactor; line.scaleY = scaleFactor * 1.5; } // Spawn obstacles/notes on beat if (LK.ticks - lastBeatTick >= BEAT_INTERVAL) { spawnBeatObjects(); lastBeatTick = LK.ticks; } // Move obstacles for (var i = obstacles.length - 1; i >= 0; i--) { var obs = obstacles[i]; obs.y += OBSTACLE_SPEED; // Off-screen removal if (obs.lastY < 2800 && obs.y >= 2800) { obs.destroy(); obstacles.splice(i, 1); continue; } // Collision detection var isIntersecting = obs.intersects(car); if (!obs.lastIntersecting && isIntersecting) { // If power-up is active, pass through obstacle if (isPowerUpActive) { // Flash the obstacle and make it semi-transparent LK.effects.flashObject(obs, 0xff00ff, 300); tween(obs, { alpha: 0.3, scaleX: 1.5, scaleY: 1.5 }, { duration: 300, easing: tween.easeOut }); } else { // Crash! LK.effects.flashScreen(0xff2a6d, 800); car.flash(); LK.showGameOver(); return; } } obs.lastY = obs.y; obs.lastIntersecting = isIntersecting; } // Move notes for (var i = notes.length - 1; i >= 0; i--) { var note = notes[i]; note.y += NOTE_SPEED; // Off-screen removal if (note.lastY < 2800 && note.y >= 2800) { note.destroy(); notes.splice(i, 1); continue; } // Collect note var isIntersecting = note.intersects(car); if (!note.lastIntersecting && isIntersecting) { // Collect! LK.setScore(LK.getScore() + 1); scoreTxt.setText(LK.getScore()); LK.getSound('noteCollect').play(); // Neon flash LK.effects.flashObject(car, 0xfff200, 200); // Camera shake on car car.shake(); // Note collection effect note.collect(); // Remove from array but destroy later after animation notes.splice(i, 1); // Destroy after animation completes LK.setTimeout(function () { note.destroy(); }, 450); continue; } note.lastY = note.y; note.lastIntersecting = isIntersecting; } }; // --- Glaud Effect Button --- var glaudButton = new Container(); // Create expanding side elements for futuristic effect var leftWing = glaudButton.attachAsset('laneLine', { anchorX: 1, anchorY: 0.5, scaleX: 2, scaleY: 1.4, tint: 0x9400d3, alpha: 0.8, x: -100 }); var rightWing = glaudButton.attachAsset('laneLine', { anchorX: 0, anchorY: 0.5, scaleX: 2, scaleY: 1.4, tint: 0x9400d3, alpha: 0.8, x: 100 }); // Create multi-layer button core for depth var buttonBgOuter = glaudButton.attachAsset('laneLine', { anchorX: 0.5, anchorY: 0.5, scaleX: 5, scaleY: 1.5, tint: 0x4a0080, alpha: 0.4 }); var buttonBgMiddle = glaudButton.attachAsset('laneLine', { anchorX: 0.5, anchorY: 0.5, scaleX: 4.5, scaleY: 1.3, tint: 0x6a0dad, alpha: 0.6 }); var buttonBgInner = glaudButton.attachAsset('laneLine', { anchorX: 0.5, anchorY: 0.5, scaleX: 4, scaleY: 1.1, tint: 0x9400d3, alpha: 0.8 }); var buttonCore = glaudButton.attachAsset('laneLine', { anchorX: 0.5, anchorY: 0.5, scaleX: 3.5, scaleY: 0.9, tint: 0xda70d6 }); // Create glowing edges var leftEdge = glaudButton.attachAsset('laneLine', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.3, scaleY: 1.6, tint: 0xff00ff, alpha: 0.8, x: -140 }); var rightEdge = glaudButton.attachAsset('laneLine', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.3, scaleY: 1.6, tint: 0xff00ff, alpha: 0.8, x: 140 }); var buttonText = new Text2('GLAUD EFFECT', { size: 48, fill: 0xffffff, font: "Impact" }); buttonText.anchor.set(0.5, 0.5); glaudButton.addChild(buttonText); // Add complex animation to button glaudButton.glowPhase = 0; glaudButton.expansionPhase = 0; glaudButton.update = function () { glaudButton.glowPhase += 0.05; glaudButton.expansionPhase += 0.03; // Glow pulsing var glow = 0.7 + Math.sin(glaudButton.glowPhase) * 0.3; buttonBgOuter.alpha = glow * 0.4; buttonBgMiddle.alpha = glow * 0.6; buttonBgInner.alpha = glow * 0.8; leftEdge.alpha = glow * 0.8 + 0.2; rightEdge.alpha = glow * 0.8 + 0.2; // Wing expansion animation var expansion = Math.sin(glaudButton.expansionPhase) * 0.2 + 1; leftWing.scaleX = 2 * expansion; rightWing.scaleX = 2 * expansion; leftWing.x = -100 - (expansion - 1) * 50; rightWing.x = 100 + (expansion - 1) * 50; // Edge glow animation var edgeGlow = Math.sin(glaudButton.glowPhase * 2) * 0.5 + 0.5; leftEdge.scaleY = 1.6 + edgeGlow * 0.4; rightEdge.scaleY = 1.6 + edgeGlow * 0.4; leftEdge.alpha = 0.5 + edgeGlow * 0.5; rightEdge.alpha = 0.5 + edgeGlow * 0.5; }; // Position button at bottom right glaudButton.x = -200; glaudButton.y = -120; LK.gui.bottomRight.addChild(glaudButton); // Initial animation when button appears tween(leftWing, { scaleX: 2, x: -100 }, { duration: 800, easing: tween.elasticOut }); tween(rightWing, { scaleX: 2, x: 100 }, { duration: 800, easing: tween.elasticOut }); // Button interaction glaudButton.down = function (x, y, obj) { // Don't activate if already active or on cooldown if (isPowerUpActive || powerUpCooldownEndTime > LK.ticks) { return; } // Flash effect when pressed LK.effects.flashObject(glaudButton, 0xffffff, 200); // Trigger glaud effect - flash screen with purple color LK.effects.flashScreen(0x9400d3, 1000); // Activate power-up isPowerUpActive = true; powerUpEndTime = LK.ticks + 600; // 10 seconds at 60fps speedMultiplier = 2; // Double speed powerUpTimerText.visible = true; // Make car glow continuously car.sprite.tint = 0xff00ff; tween(car, { scaleX: 1.2, scaleY: 1.2 }, { duration: 300, easing: tween.elasticOut }); // Enhanced button press animation // Expand wings outward tween(leftWing, { scaleX: 3, x: -150, alpha: 1 }, { duration: 200, easing: tween.easeOut }); tween(rightWing, { scaleX: 3, x: 150, alpha: 1 }, { duration: 200, easing: tween.easeOut }); // Pulse core tween(buttonCore, { scaleX: 4, scaleY: 1.1 }, { duration: 150, easing: tween.easeOut, onFinish: function onFinish() { tween(buttonCore, { scaleX: 3.5, scaleY: 0.9 }, { duration: 150, easing: tween.easeIn }); } }); // Animate button press and disable tween(glaudButton, { scaleX: 1.1, scaleY: 1.1, alpha: 0.3 }, { duration: 100, easing: tween.easeOut, onFinish: function onFinish() { tween(glaudButton, { scaleX: 1, scaleY: 1 }, { duration: 100, easing: tween.easeIn }); // Retract wings during cooldown tween(leftWing, { scaleX: 1, x: -80, alpha: 0.3 }, { duration: 400, easing: tween.easeIn }); tween(rightWing, { scaleX: 1, x: 80, alpha: 0.3 }, { duration: 400, easing: tween.easeIn }); } }); }; // --- Set initial score --- LK.setScore(0); scoreTxt.setText('0'); ;
===================================================================
--- original.js
+++ change.js