/****
* 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
@@ -12,12 +12,54 @@
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 () {
@@ -25,8 +67,18 @@
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 () {
@@ -41,8 +93,30 @@
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 () {
@@ -68,19 +142,19 @@
/****
* 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
-// Sounds and music (IDs are placeholders, engine will load as used)
-// Neon synthwave colors
-// --- Game constants ---
-// Note class
var NOTE_ASSET_IDS = ['note',
// 0
'note1',
// 1
@@ -114,9 +188,9 @@
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); // frames per beat (60fps)
+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;
@@ -126,8 +200,13 @@
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() {
@@ -138,13 +217,22 @@
laneLines = [];
// Draw vertical lines between lanes
for (var i = 1; i < NUM_LANES; i++) {
var x = (LANE_X[i - 1] + LANE_X[i]) / 2;
- for (var y = ROAD_TOP; y < ROAD_BOTTOM; y += 400) {
+ // 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;
- line.alpha = 0.25;
+ // 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);
}
}
@@ -162,13 +250,25 @@
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;
+ 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) {
@@ -192,10 +292,14 @@
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 < 0) {
+ newLane = 0;
+ }
+ if (newLane > 2) {
+ newLane = 2;
+ }
if (newLane !== carLane) {
carLane = newLane;
// Animate car to new lane
tween(car, {
@@ -212,9 +316,11 @@
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;
+ 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();
@@ -249,18 +355,110 @@
difficultyLevel = 0;
} else {
difficultyLevel = Math.floor((currentScore - 20) / 20) + 1;
}
- OBSTACLE_SPEED = BASE_OBSTACLE_SPEED + difficultyLevel * 4;
- NOTE_SPEED = BASE_NOTE_SPEED + difficultyLevel * 4;
- // Minimum beat interval: don't go below 12 frames (~0.2s at 60fps)
- BEAT_INTERVAL = Math.max(BASE_BEAT_INTERVAL - difficultyLevel * 8, 12);
+ 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++) {
- laneLines[i].y += OBSTACLE_SPEED;
- if (laneLines[i].y > ROAD_BOTTOM + 200) {
- laneLines[i].y = ROAD_TOP - 200;
+ 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();
@@ -278,13 +476,27 @@
}
// Collision detection
var isIntersecting = obs.intersects(car);
if (!obs.lastIntersecting && isIntersecting) {
- // Crash!
- LK.effects.flashScreen(0xff2a6d, 800);
- car.flash();
- LK.showGameOver();
- return;
+ // 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;
}
@@ -306,15 +518,243 @@
scoreTxt.setText(LK.getScore());
LK.getSound('noteCollect').play();
// Neon flash
LK.effects.flashObject(car, 0xfff200, 200);
- note.destroy();
+ // 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');
\ No newline at end of file
+scoreTxt.setText('0');
+;
\ No newline at end of file