User prompt
Scroll down a bit for the music selection buttons.
User prompt
Please fix the bug: 'TypeError: Cannot read properties of undefined (reading 'buttonGraphics')' in or related to this line: 'tween(buttons[b].buttonGraphics, {' Line Number: 1640 ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Please fix the bug: 'TypeError: Cannot read properties of undefined (reading 'buttonGraphics')' in or related to this line: 'tween(buttons[b].buttonGraphics, {' Line Number: 1640 ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Please fix the bug: 'TypeError: Cannot read properties of undefined (reading 'buttonGraphics')' in or related to this line: 'tween(buttons[b].buttonGraphics, {' Line Number: 1640 ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Synchronize buttons with music. Add animations according to music beats. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Please fix the bug: 'Error: Error: Invalid color format. Expected 0xRRGGBB format, received: 20054015' in or related to this line: 'tween(self.buttonGraphics, {' Line Number: 247 ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Make animations more synchronized with music. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Add animations and effects that sync with music. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Add animation according to beats in music ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Perfect and great articles should always appear in the same place. The text "missed" should always appear in the same place. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Add animations and effects for the great and perfect texts that appear on the screen. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Slightly reduce the intensity of the jolt effect when missing. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Reduced the intensity of the jolt effect when missing. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
After pressing the "Start" button, the pause menu should always be visible until the game is over.
User prompt
Add pause menu. Place it in the top left corner.
User prompt
Add settings menu.
User prompt
Prevent game from starting without music selected
User prompt
Add fluid background effects and animations. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
When the end game screen appears, stop all music.
User prompt
When a music is selected, play it. When the "Start" button is pressed, stop all music. Then play the selected music again.
User prompt
When the game starts, stop all music and restart the selected music.
User prompt
When the game starts, play the selected music from the beginning to the end. If the music ends before the game is over, play it again. When the game is over, stop the music.
User prompt
When a music is selected in the start menu, play it for 5 seconds. When the game starts, continue playing the selected music until the game ends.
User prompt
Add music selection feature to start menu.
User prompt
Add smoother animations. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var BackgroundParticle = Container.expand(function () {
var self = Container.call(this);
// Create particle using small note asset
self.particleGraphics = self.attachAsset('chordNote2', {
anchorX: 0.5,
anchorY: 0.5
});
// Random properties
self.speed = 1 + Math.random() * 3;
self.rotationSpeed = (Math.random() - 0.5) * 0.05;
self.floatAmplitude = 20 + Math.random() * 30;
self.floatSpeed = 0.02 + Math.random() * 0.03;
self.initialY = self.y;
// Set random position
self.x = Math.random() * 2048;
self.y = Math.random() * 2732;
self.initialY = self.y;
// Set random size and transparency
var scale = 0.1 + Math.random() * 0.3;
self.particleGraphics.scaleX = scale;
self.particleGraphics.scaleY = scale;
self.particleGraphics.alpha = 0.1 + Math.random() * 0.3;
// Random color tint
var colors = [0x8800ff, 0xff00ff, 0x00ffff, 0xffffff];
self.particleGraphics.tint = colors[Math.floor(Math.random() * colors.length)];
self.update = function () {
// Slow upward movement
self.y -= self.speed;
// Floating motion
self.x += Math.sin(LK.ticks * self.floatSpeed) * 0.5;
// Gentle rotation
self.particleGraphics.rotation += self.rotationSpeed;
// Music-reactive pulsing during gameplay
var basePulse = 1 + Math.sin(LK.ticks * 0.05) * 0.1;
var musicPulse = 1;
if (gameState === 'playing' && beatIntensity > 0) {
musicPulse = 1 + beatIntensity * 0.3;
beatIntensity *= 0.95; // Decay beat intensity
}
var finalPulse = basePulse * musicPulse;
self.particleGraphics.scaleX = scale * finalPulse;
self.particleGraphics.scaleY = scale * finalPulse;
// Music-reactive color shifting
if (gameState === 'playing') {
var beatColor = Math.sin(musicBeat * 0.1) * 0.5 + 0.5;
var musicColorShift = Math.floor(beatColor * 100);
self.particleGraphics.tint = self.particleGraphics.tint & 0xFF00FF | musicColorShift << 8;
}
// Reset particle when it goes off screen
if (self.y < -100) {
self.y = 2732 + 100;
self.x = Math.random() * 2048;
}
};
return self;
});
var BackgroundWave = Container.expand(function () {
var self = Container.call(this);
// Create wave using lane asset
self.waveGraphics = self.attachAsset('lane', {
anchorX: 0.5,
anchorY: 0.5
});
// Wave properties
self.amplitude = 50 + Math.random() * 100;
self.frequency = 0.01 + Math.random() * 0.02;
self.speed = 0.5 + Math.random() * 1.5;
self.initialX = Math.random() * 2048;
self.direction = Math.random() > 0.5 ? 1 : -1;
// Set position and appearance
self.x = self.initialX;
self.y = Math.random() * 2732;
self.waveGraphics.alpha = 0.05 + Math.random() * 0.1;
self.waveGraphics.scaleX = 0.5 + Math.random() * 1.5;
self.waveGraphics.scaleY = 0.1 + Math.random() * 0.3;
// Random color tint
var colors = [0x8800ff, 0xff00ff, 0x00ffff];
self.waveGraphics.tint = colors[Math.floor(Math.random() * colors.length)];
self.update = function () {
// Wave movement
self.x += self.speed * self.direction;
// Sine wave motion
self.y += Math.sin(LK.ticks * self.frequency) * 0.5;
// Gentle rotation
self.waveGraphics.rotation = Math.sin(LK.ticks * 0.02) * 0.2;
// Reset wave when it goes off screen
if (self.x < -200 || self.x > 2248) {
self.x = self.direction > 0 ? -200 : 2248;
self.y = Math.random() * 2732;
}
};
return self;
});
var Button = Container.expand(function (lane) {
var self = Container.call(this);
self.lane = lane;
self.isPressed = false;
self.buttonGraphics = self.attachAsset('button', {
anchorX: 0.5,
anchorY: 0.5
});
// Position at bottom of lane
self.x = lane * (2048 / 3) + 2048 / 6;
self.y = 2732 - 200;
self.down = function (x, y, obj) {
self.press();
};
self.press = function () {
if (self.isPressed) return;
self.isPressed = true;
// Smooth press animation
tween(self.buttonGraphics, {
alpha: 0.7,
scaleX: 0.9,
scaleY: 0.9
}, {
duration: 100,
easing: tween.easeOut
});
// Check for notes in this lane
var hitNote = null;
var bestDistance = Infinity;
for (var i = 0; i < notes.length; i++) {
var note = notes[i];
if (note.lane === self.lane && !note.hasBeenHit && note.y >= timingLineY) {
var distance = Math.abs(note.y - self.y);
if (distance < bestDistance) {
bestDistance = distance;
hitNote = note;
}
}
}
if (hitNote) {
hitNote.hasBeenHit = true;
// Calculate points based on timing
var points = 0;
var feedbackText = '';
if (bestDistance <= 150) {
// Perfect hit - note is on button
points = 100;
feedbackText = 'PERFECT';
} else if (bestDistance <= 250) {
// Great hit - note is between timing line and button
points = 50;
feedbackText = 'GREAT';
}
if (points > 0 && gameState === 'playing') {
LK.setScore(LK.getScore() + points);
scoreTxt.setText(LK.getScore());
LK.getSound('hit').play();
// Update combo (hit)
updateCombo(true);
// Visual feedback
LK.effects.flashObject(hitNote, 0xffffff, 200);
// Create explosion effect at note position
var explosionColor = feedbackText === 'PERFECT' ? 0xFFD700 : 0x00FFFF;
createExplosion(hitNote.x, hitNote.y, explosionColor);
// Show feedback text
feedbackTxt.setText(feedbackText);
feedbackTxt.alpha = 1;
tween(feedbackTxt, {
alpha: 0
}, {
duration: 800
});
// Create floating score animation
var scoreText = new Text2('+' + points, {
size: 50,
fill: explosionColor
});
scoreText.anchor.set(0.5, 0.5);
scoreText.x = hitNote.x;
scoreText.y = hitNote.y;
scoreText.alpha = 1;
game.addChild(scoreText);
// Animate floating score
tween(scoreText, {
y: scoreText.y - 150,
alpha: 0,
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 1000,
easing: tween.easeOut,
onFinish: function onFinish() {
if (scoreText && scoreText.parent) {
scoreText.destroy();
}
}
});
// Special animations for GREAT and PERFECT
if (feedbackText === 'PERFECT') {
// Perfect animation: scale up and spin with color change
var originalScale = self.buttonGraphics.scaleX;
var originalTint = self.buttonGraphics.tint;
self.buttonGraphics.tint = 0xFFD700; // Gold color
tween(self.buttonGraphics, {
scaleX: originalScale * 1.5,
scaleY: originalScale * 1.5,
rotation: Math.PI * 2
}, {
duration: 600,
easing: tween.elasticOut,
onFinish: function onFinish() {
tween(self.buttonGraphics, {
scaleX: originalScale,
scaleY: originalScale,
rotation: 0,
tint: originalTint
}, {
duration: 200
});
}
});
// Create perfect ring animation around button
var perfectRing = game.addChild(LK.getAsset('button', {
anchorX: 0.5,
anchorY: 0.5
}));
perfectRing.x = self.x;
perfectRing.y = self.y;
perfectRing.tint = 0xFFD700;
perfectRing.alpha = 0.8;
perfectRing.scaleX = 0.5;
perfectRing.scaleY = 0.5;
// Animate ring expanding outward while fading
tween(perfectRing, {
scaleX: 3.0,
scaleY: 3.0,
alpha: 0
}, {
duration: 800,
easing: tween.easeOut,
onFinish: function onFinish() {
if (perfectRing && perfectRing.parent) {
perfectRing.destroy();
}
}
});
} else if (feedbackText === 'GREAT') {
// Great animation: pulse and glow effect
var originalScale = self.buttonGraphics.scaleX;
var originalTint = self.buttonGraphics.tint;
self.buttonGraphics.tint = 0x00FFFF; // Cyan color
tween(self.buttonGraphics, {
scaleX: originalScale * 1.3,
scaleY: originalScale * 1.3
}, {
duration: 300,
easing: tween.bounceOut,
onFinish: function onFinish() {
tween(self.buttonGraphics, {
scaleX: originalScale,
scaleY: originalScale,
tint: originalTint
}, {
duration: 300,
easing: tween.easeOut
});
}
});
}
// Remove the note immediately
hitNote.destroy();
var noteIndex = notes.indexOf(hitNote);
if (noteIndex !== -1) {
notes.splice(noteIndex, 1);
}
}
}
// Reset button after short delay with smooth animation
LK.setTimeout(function () {
self.isPressed = false;
// Smooth release animation
tween(self.buttonGraphics, {
alpha: 1.0,
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 200,
easing: tween.elasticOut
});
}, 100);
};
return self;
});
var Note = Container.expand(function (lane, type) {
var self = Container.call(this);
self.lane = lane;
self.type = type;
self.hasBeenHit = false;
// Get speed multiplier from current difficulty
var speedMultiplier = currentDifficulty !== -1 ? difficulties[currentDifficulty].noteSpeedMultiplier : 1.0;
// Set speed and asset based on type
if (type === 'slow') {
self.speed = 8 * speedMultiplier;
self.noteGraphics = self.attachAsset('slowNote', {
anchorX: 0.5,
anchorY: 0.5
});
} else if (type === 'medium') {
self.speed = 12 * speedMultiplier;
self.noteGraphics = self.attachAsset('mediumNote', {
anchorX: 0.5,
anchorY: 0.5
});
} else if (type === 'fast') {
self.speed = 16 * speedMultiplier;
self.noteGraphics = self.attachAsset('fastNote', {
anchorX: 0.5,
anchorY: 0.5
});
}
// Position in lane
self.x = lane * (2048 / 3) + 2048 / 6;
self.y = 0;
self.update = function () {
// Add slight wave motion to notes for more dynamic movement
var waveOffset = Math.sin(LK.ticks * 0.1 + self.lane) * 2;
self.x = self.lane * (2048 / 3) + 2048 / 6 + waveOffset;
self.y += self.speed;
// Add subtle rotation based on movement
self.noteGraphics.rotation = Math.sin(LK.ticks * 0.08 + self.lane) * 0.1;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
// Game state management
var gameState = 'menu'; // 'menu', 'playing', or 'gameOver'
// Background effects
var backgroundParticles = [];
var backgroundWaves = [];
var startButton;
var gameOverButton;
var difficultyButtons = [];
var currentDifficulty = -1; // -1=None selected, 0=Easy, 1=Medium, 2=Hard, 3=Expert
var difficultySelected = false;
var musicButtons = [];
var currentMusic = -1; // -1=None selected, 0=Music1, 1=Music2, 2=Music3
var musicTracks = [{
name: 'CLASSIC',
id: 'GameMusic1',
color: 0x8800ff
}, {
name: 'ENERGY',
id: 'GameMusic2',
color: 0xff8800
}, {
name: 'CHILL',
id: 'GameMusic3',
color: 0x0088ff
}];
var difficulties = [{
name: 'EASY',
spawnInterval: 120,
maxNotes: 4,
timeLimit: 60,
noteSpeedMultiplier: 0.8,
color: 0x00FF00
}, {
name: 'MEDIUM',
spawnInterval: 80,
maxNotes: 6,
timeLimit: 60,
noteSpeedMultiplier: 1.2,
color: 0xFFFF00
}, {
name: 'HARD',
spawnInterval: 50,
maxNotes: 8,
timeLimit: 60,
noteSpeedMultiplier: 1.6,
color: 0xFF8800
}, {
name: 'EXPERT',
spawnInterval: 30,
maxNotes: 12,
timeLimit: 60,
noteSpeedMultiplier: 2.0,
color: 0xFF0000
}];
// Game variables
var notes = [];
var buttons = [];
var maxNotes = 5;
var spawnTimer = 0;
var spawnInterval = 120; // frames between spawns
var timingLineY = 2732 - 500;
var bottomLineY = 2732 - 2; // Position miss line at bottom edge of screen
// Music synchronization variables
var musicBeat = 0;
var beatInterval = 30; // frames per beat (adjustable based on music tempo)
var beatIntensity = 0;
var musicPulseElements = [];
// Create lane separators
var lane1 = game.addChild(LK.getAsset('lane', {
anchorX: 0.5,
anchorY: 0
}));
lane1.x = 2048 / 3;
lane1.y = 0;
lane1.visible = false; // Hide initially
var lane2 = game.addChild(LK.getAsset('lane', {
anchorX: 0.5,
anchorY: 0
}));
lane2.x = 2048 / 3 * 2;
lane2.y = 0;
lane2.visible = false; // Hide initially
// Create timing line
var timingLine = game.addChild(LK.getAsset('timingLine', {
anchorX: 0,
anchorY: 0.5
}));
timingLine.x = 0;
timingLine.y = timingLineY;
timingLine.visible = false; // Hide initially
// Create bottom line
var bottomLine = game.addChild(LK.getAsset('bottomLine', {
anchorX: 0,
anchorY: 0.5
}));
bottomLine.x = 0;
bottomLine.y = bottomLineY;
bottomLine.visible = false; // Hide initially
// Create start menu elements
var titleTxt = new Text2('RHYTHM GAME', {
size: 120,
fill: 0xFFFFFF
});
titleTxt.anchor.set(0.5, 0.5);
titleTxt.x = 2048 / 2;
titleTxt.y = 2732 / 2 - 200;
titleTxt.alpha = 0;
titleTxt.scaleX = 0.5;
titleTxt.scaleY = 0.5;
game.addChild(titleTxt);
// Animate title appearing
tween(titleTxt, {
alpha: 1,
scaleX: 1,
scaleY: 1
}, {
duration: 1000,
easing: tween.elasticOut
});
// Add continuous floating animation
function animateTitleFloat() {
tween(titleTxt, {
y: titleTxt.y - 20
}, {
duration: 2000,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(titleTxt, {
y: titleTxt.y + 20
}, {
duration: 2000,
easing: tween.easeInOut,
onFinish: animateTitleFloat
});
}
});
}
LK.setTimeout(animateTitleFloat, 1500);
// Add breathing effect to background elements
function createBreathingEffect() {
// Animate lane separators with subtle breathing
tween(lane1, {
alpha: 0.8,
scaleY: 1.05
}, {
duration: 3000,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(lane1, {
alpha: 0.5,
scaleY: 1.0
}, {
duration: 3000,
easing: tween.easeInOut,
onFinish: createBreathingEffect
});
}
});
// Offset timing for lane2
LK.setTimeout(function () {
tween(lane2, {
alpha: 0.8,
scaleY: 1.05
}, {
duration: 3000,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(lane2, {
alpha: 0.5,
scaleY: 1.0
}, {
duration: 3000,
easing: tween.easeInOut
});
}
});
}, 1500);
}
// Start breathing effect when game elements are visible
var breathingEffectStarted = false;
var startButtonShape = game.addChild(LK.getAsset('button', {
anchorX: 0.5,
anchorY: 0.5
}));
startButtonShape.x = 2048 / 2;
startButtonShape.y = 2732 / 2 + 100;
startButtonShape.tint = 0x00AA00;
startButtonShape.alpha = 0;
startButtonShape.scaleX = 0;
startButtonShape.scaleY = 0;
// Animate start button appearing
tween(startButtonShape, {
alpha: 1,
scaleX: 1,
scaleY: 1
}, {
duration: 800,
easing: tween.bounceOut,
delay: 1200
});
// Add pulsing glow effect to start button
function animateStartButtonPulse() {
tween(startButtonShape, {
scaleX: 1.1,
scaleY: 1.1,
tint: 0x00FF00
}, {
duration: 1500,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(startButtonShape, {
scaleX: 1,
scaleY: 1,
tint: 0x00AA00
}, {
duration: 1500,
easing: tween.easeInOut,
onFinish: animateStartButtonPulse
});
}
});
}
LK.setTimeout(animateStartButtonPulse, 2000);
var startButtonTxt = new Text2('START', {
size: 60,
fill: 0xFFFFFF
});
startButtonTxt.anchor.set(0.5, 0.5);
startButtonTxt.x = 2048 / 2 + 300;
startButtonTxt.y = 2732 / 2 + 100;
startButtonTxt.alpha = 0;
game.addChild(startButtonTxt);
// Animate start button text sliding in
tween(startButtonTxt, {
x: 2048 / 2,
alpha: 1
}, {
duration: 600,
easing: tween.easeOut,
delay: 1400
});
// Create difficulty selection text
var difficultyTitleTxt = new Text2('SELECT DIFFICULTY', {
size: 80,
fill: 0xFFFFFF
});
difficultyTitleTxt.anchor.set(0.5, 0.5);
difficultyTitleTxt.x = 2048 / 2;
difficultyTitleTxt.y = 2732 / 2 + 300;
game.addChild(difficultyTitleTxt);
// Create difficulty selection indicator text
var difficultyIndicatorTxt = new Text2('None selected', {
size: 60,
fill: 0xFF0000
});
difficultyIndicatorTxt.anchor.set(0.5, 0.5);
difficultyIndicatorTxt.x = 2048 / 2;
difficultyIndicatorTxt.y = 2732 / 2 + 500;
difficultyIndicatorTxt.alpha = 0;
game.addChild(difficultyIndicatorTxt);
// Create music selection text
var musicTitleTxt = new Text2('SELECT MUSIC', {
size: 70,
fill: 0xFFFFFF
});
musicTitleTxt.anchor.set(0.5, 0.5);
musicTitleTxt.x = 2048 / 2;
musicTitleTxt.y = 2732 / 2 + 600;
game.addChild(musicTitleTxt);
// Create music selection indicator text
var musicIndicatorTxt = new Text2('None selected', {
size: 50,
fill: 0xFF0000
});
musicIndicatorTxt.anchor.set(0.5, 0.5);
musicIndicatorTxt.x = 2048 / 2;
musicIndicatorTxt.y = 2732 / 2 + 800;
musicIndicatorTxt.alpha = 0;
game.addChild(musicIndicatorTxt);
// Create difficulty buttons
for (var d = 0; d < difficulties.length; d++) {
var diffButton = {
index: d,
text: new Text2(difficulties[d].name, {
size: 50,
fill: difficulties[d].color
})
};
diffButton.text.anchor.set(0.5, 0.5);
diffButton.text.x = d * 400 + 424; // Spread across screen
diffButton.text.y = 2732 / 2 + 400; // Below the "select difficulty" text
diffButton.text.scaleX = 0;
diffButton.text.scaleY = 0;
diffButton.text.alpha = 0;
game.addChild(diffButton.text);
difficultyButtons.push(diffButton);
// Animate text appearing with staggered timing
tween(diffButton.text, {
scaleX: 1,
scaleY: 1,
alpha: 1
}, {
duration: 600,
easing: tween.elasticOut,
delay: d * 150
});
}
// Animate difficulty title appearing
difficultyTitleTxt.alpha = 0;
difficultyTitleTxt.scaleY = 0;
tween(difficultyTitleTxt, {
alpha: 1,
scaleY: 1
}, {
duration: 800,
easing: tween.bounceOut,
delay: 200
});
// Animate difficulty indicator appearing
tween(difficultyIndicatorTxt, {
alpha: 1,
scaleY: 1
}, {
duration: 800,
easing: tween.bounceOut,
delay: 800
});
// Create music selection buttons
for (var m = 0; m < musicTracks.length; m++) {
var musicButton = {
index: m,
shape: game.addChild(LK.getAsset('musicButton', {
anchorX: 0.5,
anchorY: 0.5
})),
text: new Text2(musicTracks[m].name, {
size: 40,
fill: 0xFFFFFF
})
};
musicButton.shape.x = m * 300 + 724; // Spread across screen
musicButton.shape.y = 2732 / 2 + 700;
musicButton.shape.tint = musicTracks[m].color;
musicButton.shape.scaleX = 0;
musicButton.shape.scaleY = 0;
musicButton.shape.alpha = 0;
musicButton.text.anchor.set(0.5, 0.5);
musicButton.text.x = musicButton.shape.x;
musicButton.text.y = musicButton.shape.y;
musicButton.text.scaleX = 0;
musicButton.text.scaleY = 0;
musicButton.text.alpha = 0;
game.addChild(musicButton.text);
musicButtons.push(musicButton);
// Animate buttons appearing with staggered timing
tween(musicButton.shape, {
scaleX: 1,
scaleY: 1,
alpha: 1
}, {
duration: 600,
easing: tween.elasticOut,
delay: 1000 + m * 100
});
tween(musicButton.text, {
scaleX: 1,
scaleY: 1,
alpha: 1
}, {
duration: 600,
easing: tween.elasticOut,
delay: 1200 + m * 100
});
}
// Animate music title appearing
musicTitleTxt.alpha = 0;
musicTitleTxt.scaleY = 0;
tween(musicTitleTxt, {
alpha: 1,
scaleY: 1
}, {
duration: 800,
easing: tween.bounceOut,
delay: 400
});
// Animate music indicator appearing
tween(musicIndicatorTxt, {
alpha: 1
}, {
duration: 800,
easing: tween.bounceOut,
delay: 1000
});
// No initial music selection - all buttons start at normal size
// Initialize background effects
function createBackgroundEffects() {
// Create floating particles
for (var p = 0; p < 15; p++) {
var particle = new BackgroundParticle();
backgroundParticles.push(particle);
game.addChildAt(particle, 0); // Add behind other elements
}
// Create background waves
for (var w = 0; w < 5; w++) {
var wave = new BackgroundWave();
backgroundWaves.push(wave);
game.addChildAt(wave, 0); // Add behind other elements
}
}
// Start background effects
createBackgroundEffects();
// Add dynamic background color transitions
var backgroundColors = [0x001122, 0x220011, 0x112200, 0x001100];
var currentBgColorIndex = 0;
var bgColorTransitionTimer = 0;
function updateBackgroundColor() {
bgColorTransitionTimer++;
// Music-reactive beat pulsing
if (gameState === 'playing') {
musicBeat++;
if (musicBeat >= beatInterval) {
musicBeat = 0;
beatIntensity = 1.0;
// Flash background on beat
var currentBgColor = backgroundColors[currentBgColorIndex];
var brightColor = {
r: Math.min(255, (currentBgColor >> 16 & 0xFF) + 30),
g: Math.min(255, (currentBgColor >> 8 & 0xFF) + 30),
b: Math.min(255, (currentBgColor & 0xFF) + 30)
};
var flashColor = brightColor.r << 16 | brightColor.g << 8 | brightColor.b;
game.setBackgroundColor(flashColor);
// Fade back to normal
tween({
intensity: 1
}, {
intensity: 0
}, {
duration: beatInterval * 16,
easing: tween.easeOut,
onUpdate: function onUpdate() {
var fade = this.intensity;
var newR = Math.floor((currentBgColor >> 16 & 0xFF) + (brightColor.r - (currentBgColor >> 16 & 0xFF)) * fade);
var newG = Math.floor((currentBgColor >> 8 & 0xFF) + (brightColor.g - (currentBgColor >> 8 & 0xFF)) * fade);
var newB = Math.floor((currentBgColor & 0xFF) + (brightColor.b - (currentBgColor & 0xFF)) * fade);
game.setBackgroundColor(newR << 16 | newG << 8 | newB);
}
});
}
}
if (bgColorTransitionTimer >= 1800) {
// Change every 30 seconds
bgColorTransitionTimer = 0;
var nextColorIndex = (currentBgColorIndex + 1) % backgroundColors.length;
// Smooth color transition using tween
var currentColor = backgroundColors[currentBgColorIndex];
var nextColor = backgroundColors[nextColorIndex];
// Create temporary object to animate color values
var colorTransition = {
r: currentColor >> 16 & 0xFF,
g: currentColor >> 8 & 0xFF,
b: currentColor & 0xFF
};
var targetR = nextColor >> 16 & 0xFF;
var targetG = nextColor >> 8 & 0xFF;
var targetB = nextColor & 0xFF;
tween(colorTransition, {
r: targetR,
g: targetG,
b: targetB
}, {
duration: 3000,
easing: tween.easeInOut,
onUpdate: function onUpdate() {
var newColor = Math.floor(colorTransition.r) << 16 | Math.floor(colorTransition.g) << 8 | Math.floor(colorTransition.b);
game.setBackgroundColor(newColor);
},
onFinish: function onFinish() {
currentBgColorIndex = nextColorIndex;
}
});
}
}
// No initial difficulty highlighting since none is selected
// Create start button handler
startButton = {
shape: startButtonShape,
text: startButtonTxt,
down: function down(x, y, obj) {
this.startGame();
},
startGame: function startGame() {
// Check if difficulty is selected
if (currentDifficulty === -1) {
// Flash difficulty title to indicate selection is required
tween(difficultyTitleTxt, {
scaleX: 1.2,
scaleY: 1.2,
tint: 0xFF0000
}, {
duration: 200,
easing: tween.bounceOut,
onFinish: function onFinish() {
tween(difficultyTitleTxt, {
scaleX: 1.0,
scaleY: 1.0,
tint: 0xFFFFFF
}, {
duration: 200
});
}
});
return; // Don't start the game
}
// Check if music is selected
if (currentMusic === -1) {
// Flash music title to indicate selection is required
tween(musicTitleTxt, {
scaleX: 1.2,
scaleY: 1.2,
tint: 0xFF0000
}, {
duration: 200,
easing: tween.bounceOut,
onFinish: function onFinish() {
tween(musicTitleTxt, {
scaleX: 1.0,
scaleY: 1.0,
tint: 0xFFFFFF
}, {
duration: 200
});
}
});
return; // Don't start the game
}
// Hide menu elements
titleTxt.visible = false;
this.shape.visible = false;
this.text.visible = false;
// Show game elements
timerTxt.visible = true;
scoreTxt.visible = true;
timingLine.visible = true;
bottomLine.visible = true;
lane1.visible = true;
lane2.visible = true;
// Create game buttons
for (var i = 0; i < 3; i++) {
var button = new Button(i);
buttons.push(button);
game.addChild(button);
}
// Hide difficulty selection
difficultyTitleTxt.visible = false;
difficultyIndicatorTxt.visible = false;
for (var d = 0; d < difficultyButtons.length; d++) {
difficultyButtons[d].text.visible = false;
}
// Hide music selection
musicTitleTxt.visible = false;
musicIndicatorTxt.visible = false;
for (var m = 0; m < musicButtons.length; m++) {
musicButtons[m].shape.visible = false;
musicButtons[m].text.visible = false;
}
// Stop all music then play selected music
LK.stopMusic();
LK.playMusic(musicTracks[currentMusic].id);
// Apply difficulty settings
var difficulty = difficulties[currentDifficulty];
maxNotes = difficulty.maxNotes;
spawnInterval = difficulty.spawnInterval;
timeRemaining = difficulty.timeLimit;
// Start the game
gameState = 'playing';
LK.setScore(0);
scoreTxt.setText('0');
timerTxt.setText(timeRemaining.toString());
}
};
// Initially hide game elements
var gameElements = [];
// Create countdown timer
var timeRemaining = 60;
// Create timer display above score
var timerTxt = new Text2('60', {
size: 80,
fill: 0xFFFFFF
});
timerTxt.anchor.set(0.5, 0.5);
timerTxt.x = 2048 / 2;
timerTxt.y = 2732 / 2 - 300;
timerTxt.visible = false; // Hide initially
game.addChild(timerTxt);
// Create score display in center of game area
var scoreTxt = new Text2('0', {
size: 100,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0.5);
scoreTxt.x = 2048 / 2;
scoreTxt.y = 2732 / 2 - 200;
scoreTxt.visible = false; // Hide initially
game.addChild(scoreTxt);
// Create feedback text display
var feedbackTxt = new Text2('', {
size: 80,
fill: 0xFFFFFF
});
feedbackTxt.anchor.set(0.5, 0.5);
LK.gui.center.addChild(feedbackTxt);
feedbackTxt.alpha = 0;
// Create game over menu elements
var gameOverTxt = new Text2('GAME OVER', {
size: 120,
fill: 0xFF0000
});
gameOverTxt.anchor.set(0.5, 0.5);
gameOverTxt.x = 2048 / 2;
gameOverTxt.y = 2732 / 2 - 300;
gameOverTxt.visible = false;
game.addChild(gameOverTxt);
var finalScoreTxt = new Text2('', {
size: 80,
fill: 0xFFFFFF
});
finalScoreTxt.anchor.set(0.5, 0.5);
finalScoreTxt.x = 2048 / 2;
finalScoreTxt.y = 2732 / 2 - 150;
finalScoreTxt.visible = false;
game.addChild(finalScoreTxt);
var restartButtonShape = game.addChild(LK.getAsset('button', {
anchorX: 0.5,
anchorY: 0.5
}));
restartButtonShape.x = 2048 / 2;
restartButtonShape.y = 2732 / 2 + 100;
restartButtonShape.tint = 0x0066CC;
restartButtonShape.visible = false;
var restartButtonTxt = new Text2('RESTART', {
size: 60,
fill: 0xFFFFFF
});
restartButtonTxt.anchor.set(0.5, 0.5);
restartButtonTxt.x = 2048 / 2;
restartButtonTxt.y = 2732 / 2 + 100;
restartButtonTxt.visible = false;
game.addChild(restartButtonTxt);
// Create restart button handler
gameOverButton = {
shape: restartButtonShape,
text: restartButtonTxt,
down: function down(x, y, obj) {
this.restartGame();
},
restartGame: function restartGame() {
// Hide game over elements
gameOverTxt.visible = false;
finalScoreTxt.visible = false;
this.shape.visible = false;
this.text.visible = false;
// Clear existing notes and buttons
for (var i = notes.length - 1; i >= 0; i--) {
notes[i].destroy();
notes.splice(i, 1);
}
for (var i = buttons.length - 1; i >= 0; i--) {
buttons[i].destroy();
buttons.splice(i, 1);
}
// Show menu elements with smooth animations
titleTxt.visible = true;
titleTxt.alpha = 0;
titleTxt.scaleX = 0.8;
titleTxt.scaleY = 0.8;
startButton.shape.visible = true;
startButton.shape.alpha = 0;
startButton.shape.scaleX = 0;
startButton.shape.scaleY = 0;
startButton.text.visible = true;
startButton.text.alpha = 0;
startButton.text.x = startButton.text.x + 200;
difficultyTitleTxt.visible = true;
difficultyTitleTxt.alpha = 0;
difficultyTitleTxt.scaleY = 0;
difficultyIndicatorTxt.visible = true;
difficultyIndicatorTxt.alpha = 0;
musicTitleTxt.visible = true;
musicTitleTxt.alpha = 0;
musicTitleTxt.scaleY = 0;
musicIndicatorTxt.visible = true;
musicIndicatorTxt.alpha = 0;
// Animate title appearing
tween(titleTxt, {
alpha: 1,
scaleX: 1,
scaleY: 1
}, {
duration: 800,
easing: tween.elasticOut
});
// Animate start button
tween(startButton.shape, {
alpha: 1,
scaleX: 1,
scaleY: 1
}, {
duration: 600,
easing: tween.bounceOut,
delay: 400
});
// Animate start button text sliding in
tween(startButton.text, {
alpha: 1,
x: startButton.text.x - 200
}, {
duration: 500,
easing: tween.easeOut,
delay: 600
});
// Animate difficulty title
tween(difficultyTitleTxt, {
alpha: 1,
scaleY: 1
}, {
duration: 600,
easing: tween.bounceOut,
delay: 200
});
// Animate difficulty indicator
tween(difficultyIndicatorTxt, {
alpha: 1
}, {
duration: 400,
delay: 800
});
// Animate difficulty buttons
for (var d = 0; d < difficultyButtons.length; d++) {
difficultyButtons[d].text.visible = true;
difficultyButtons[d].text.alpha = 0;
difficultyButtons[d].text.scaleX = 0;
difficultyButtons[d].text.scaleY = 0;
// Staggered animation for each button
tween(difficultyButtons[d].text, {
alpha: 1,
scaleX: 1,
scaleY: 1
}, {
duration: 500,
easing: tween.elasticOut,
delay: 600 + d * 100
});
}
// Animate music title
tween(musicTitleTxt, {
alpha: 1,
scaleY: 1
}, {
duration: 600,
easing: tween.bounceOut,
delay: 400
});
// Animate music indicator
tween(musicIndicatorTxt, {
alpha: 1
}, {
duration: 400,
delay: 1000
});
// Animate music buttons
for (var m = 0; m < musicButtons.length; m++) {
musicButtons[m].shape.visible = true;
musicButtons[m].shape.alpha = 0;
musicButtons[m].shape.scaleX = currentMusic === m && currentMusic !== -1 ? 1.2 : 0;
musicButtons[m].shape.scaleY = currentMusic === m && currentMusic !== -1 ? 1.2 : 0;
musicButtons[m].text.visible = true;
musicButtons[m].text.alpha = 0;
musicButtons[m].text.scaleX = 0;
musicButtons[m].text.scaleY = 0;
// Staggered animation for each button
tween(musicButtons[m].shape, {
alpha: 1,
scaleX: currentMusic === m && currentMusic !== -1 ? 1.2 : 1,
scaleY: currentMusic === m && currentMusic !== -1 ? 1.2 : 1
}, {
duration: 500,
easing: tween.elasticOut,
delay: 800 + m * 100
});
tween(musicButtons[m].text, {
alpha: 1,
scaleX: 1,
scaleY: 1
}, {
duration: 500,
easing: tween.elasticOut,
delay: 1000 + m * 100
});
}
// Hide game elements
timerTxt.visible = false;
scoreTxt.visible = false;
timingLine.visible = false;
bottomLine.visible = false;
lane1.visible = false;
lane2.visible = false;
// Reset game state to menu
gameState = 'menu';
spawnTimer = 0;
LK.setScore(0);
}
};
// Create explosion animation function
function createExplosion(x, y, color) {
var particleCount = 8;
var particles = [];
for (var i = 0; i < particleCount; i++) {
// Create particle using small ellipse shape
var particle = game.addChild(LK.getAsset('fastNote', {
anchorX: 0.5,
anchorY: 0.5
}));
particle.x = x;
particle.y = y;
particle.tint = color || 0xFFFFFF;
particle.scaleX = 0.3;
particle.scaleY = 0.3;
particles.push(particle);
// Calculate random direction for each particle
var angle = i / particleCount * Math.PI * 2 + (Math.random() - 0.5) * 0.5;
var distance = 100 + Math.random() * 50;
var targetX = x + Math.cos(angle) * distance;
var targetY = y + Math.sin(angle) * distance;
// Animate particle outward and fade out
tween(particle, {
x: targetX,
y: targetY,
scaleX: 0,
scaleY: 0,
alpha: 0
}, {
duration: 600 + Math.random() * 400,
easing: tween.easeOut,
onFinish: function onFinish() {
if (particle && particle.parent) {
particle.destroy();
}
}
});
}
}
// Create screen shake effect
function shakeScreen(intensity, duration) {
var originalX = game.x;
var originalY = game.y;
var shakeStartTime = LK.ticks;
var shakeDuration = duration || 300;
var shakeIntensity = intensity || 20;
// Create shake animation
function shakeUpdate() {
var elapsed = LK.ticks - shakeStartTime;
if (elapsed < shakeDuration) {
var progress = elapsed / shakeDuration;
var currentIntensity = shakeIntensity * (1 - progress);
game.x = originalX + (Math.random() - 0.5) * currentIntensity * 2;
game.y = originalY + (Math.random() - 0.5) * currentIntensity * 2;
LK.setTimeout(shakeUpdate, 16); // ~60fps
} else {
game.x = originalX;
game.y = originalY;
}
}
shakeUpdate();
}
// Create combo streak effects
var comboCount = 0;
var lastHitTime = 0;
function updateCombo(isHit) {
if (isHit) {
var currentTime = LK.ticks;
if (currentTime - lastHitTime < 180) {
// Within 3 seconds
comboCount++;
} else {
comboCount = 1;
}
lastHitTime = currentTime;
// Show combo effects for streaks
if (comboCount >= 5 && comboCount % 5 === 0) {
// Create combo text
var comboText = new Text2('COMBO x' + comboCount, {
size: 60,
fill: 0xFFD700
});
comboText.anchor.set(0.5, 0.5);
comboText.x = 2048 / 2;
comboText.y = 2732 / 2 + 100;
comboText.alpha = 0;
game.addChild(comboText);
// Animate combo text
tween(comboText, {
alpha: 1,
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 300,
easing: tween.bounceOut,
onFinish: function onFinish() {
tween(comboText, {
alpha: 0,
y: comboText.y - 100
}, {
duration: 500,
onFinish: function onFinish() {
if (comboText && comboText.parent) {
comboText.destroy();
}
}
});
}
});
}
} else {
comboCount = 0;
}
}
// Spawn note function with music synchronization
function spawnNote() {
if (notes.length >= maxNotes) return;
var lane = Math.floor(Math.random() * 3);
var noteTypes = ['slow', 'medium', 'fast'];
var type = noteTypes[Math.floor(Math.random() * noteTypes.length)];
var note = new Note(lane, type);
// Music-reactive entrance animation
var musicSpawnScale = 1;
var musicSpawnColor = 0xFFFFFF;
if (beatIntensity > 0.7) {
// Spawn with extra flair on strong beats
musicSpawnScale = 1.3;
musicSpawnColor = 0xFFD700; // Gold color for beat spawns
note.noteGraphics.tint = musicSpawnColor;
}
// Add entrance animation - start small and grow with music sync
note.noteGraphics.scaleX = 0;
note.noteGraphics.scaleY = 0;
note.noteGraphics.alpha = 0;
tween(note.noteGraphics, {
scaleX: musicSpawnScale,
scaleY: musicSpawnScale,
alpha: 1
}, {
duration: 300,
easing: tween.elasticOut,
onFinish: function onFinish() {
// Return to normal scale after spawn
if (musicSpawnScale > 1) {
tween(note.noteGraphics, {
scaleX: 1,
scaleY: 1,
tint: 0xFFFFFF
}, {
duration: 200,
easing: tween.easeOut
});
}
}
});
// Add music-reactive spawn effects
if (beatIntensity > 0.5) {
// Create spawn burst effect
createExplosion(note.x, note.y - 100, 0x00FFFF);
}
notes.push(note);
game.addChild(note);
}
// Handle clicks for start button
game.down = function (x, y, obj) {
if (gameState === 'menu') {
// Check if click is on start button
var dx = x - startButton.shape.x;
var dy = y - startButton.shape.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= 150) {
// Button radius
startButton.down(x, y, obj);
}
// Check if click is on difficulty buttons
for (var d = 0; d < difficultyButtons.length; d++) {
var diffButton = difficultyButtons[d];
var ddx = x - diffButton.text.x;
var ddy = y - diffButton.text.y;
var ddistance = Math.sqrt(ddx * ddx + ddy * ddy);
if (ddistance <= 100) {
// Text button hit area
// Reset previous selection if any
if (currentDifficulty !== -1) {
difficultyButtons[currentDifficulty].text.scaleX = 1.0;
difficultyButtons[currentDifficulty].text.scaleY = 1.0;
}
// Set new selection
currentDifficulty = d;
difficultySelected = true;
diffButton.text.scaleX = 1.0;
diffButton.text.scaleY = 1.0;
// Update indicator text to show selected difficulty
if (d === 0) {
// Easy mode - show in red
difficultyIndicatorTxt.setText('Easy mode selected');
difficultyIndicatorTxt.fill = 0xFF0000;
difficultyIndicatorTxt.tint = 0xFF0000;
} else {
difficultyIndicatorTxt.setText(difficulties[d].name.toLowerCase() + ' mode selected');
difficultyIndicatorTxt.fill = difficulties[d].color;
difficultyIndicatorTxt.tint = difficulties[d].color;
}
// Add selection effect
tween(diffButton.text, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 200,
easing: tween.bounceOut,
onFinish: function onFinish() {
tween(diffButton.text, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 200
});
}
});
// Animate the indicator text to show selection
tween(difficultyIndicatorTxt, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 300,
easing: tween.bounceOut,
onFinish: function onFinish() {
tween(difficultyIndicatorTxt, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 200
});
}
});
break;
}
}
// Check if click is on music buttons
for (var m = 0; m < musicButtons.length; m++) {
var musicButton = musicButtons[m];
var mdx = x - musicButton.shape.x;
var mdy = y - musicButton.shape.y;
var mdistance = Math.sqrt(mdx * mdx + mdy * mdy);
if (mdistance <= 90) {
// Button radius
// Reset previous selection
if (currentMusic !== -1) {
musicButtons[currentMusic].shape.scaleX = 1.0;
musicButtons[currentMusic].shape.scaleY = 1.0;
}
// Set new selection
currentMusic = m;
musicButton.shape.scaleX = 1.0;
musicButton.shape.scaleY = 1.0;
// Update indicator text
musicIndicatorTxt.setText(musicTracks[m].name + ' selected');
musicIndicatorTxt.fill = musicTracks[m].color;
musicIndicatorTxt.tint = musicTracks[m].color;
// Add selection effect
tween(musicButton.shape, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 200,
easing: tween.bounceOut
});
// Animate the indicator text
tween(musicIndicatorTxt, {
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 200,
easing: tween.bounceOut,
onFinish: function onFinish() {
tween(musicIndicatorTxt, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 200
});
}
});
// Play the selected music
LK.stopMusic();
LK.playMusic(musicTracks[m].id);
break;
}
}
} else if (gameState === 'gameOver') {
// Check if click is on restart button
var dx = x - gameOverButton.shape.x;
var dy = y - gameOverButton.shape.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= 150) {
// Button radius
gameOverButton.down(x, y, obj);
}
}
};
game.update = function () {
// Update background effects regardless of game state
updateBackgroundColor();
// Update background particles
for (var p = 0; p < backgroundParticles.length; p++) {
if (backgroundParticles[p] && backgroundParticles[p].update) {
backgroundParticles[p].update();
}
}
// Update background waves
for (var w = 0; w < backgroundWaves.length; w++) {
if (backgroundWaves[w] && backgroundWaves[w].update) {
backgroundWaves[w].update();
}
}
// Start breathing effect when appropriate
if (!breathingEffectStarted && gameState === 'playing') {
breathingEffectStarted = true;
createBreathingEffect();
}
// Add music-synchronized button effects during menu
if (gameState === 'menu' && currentMusic !== -1) {
// Create gentle music-reactive effects for menu buttons
var menuMusicBeat = Math.sin(LK.ticks * 0.1) * 0.5 + 0.5;
// Animate start button with music
if (startButton && startButton.shape && startButton.shape.visible) {
var startPulse = 1 + menuMusicBeat * 0.05;
startButton.shape.scaleX = startPulse;
startButton.shape.scaleY = startPulse;
// Subtle color shift
var startColorShift = Math.floor(menuMusicBeat * 50);
startButton.shape.tint = 0x00AA00 + (startColorShift << 8);
}
// Animate music selection buttons with staggered timing
for (var mb = 0; mb < musicButtons.length; mb++) {
if (musicButtons[mb] && musicButtons[mb].shape && musicButtons[mb].shape.visible) {
var staggeredBeat = Math.sin(LK.ticks * 0.08 + mb * 0.8) * 0.5 + 0.5;
var buttonPulse = 1 + staggeredBeat * 0.03;
// Scale animation
if (currentMusic !== mb) {
musicButtons[mb].shape.scaleX = buttonPulse;
musicButtons[mb].shape.scaleY = buttonPulse;
}
// Color breathing effect
var colorIntensity = staggeredBeat * 0.3 + 0.7;
var originalColor = musicTracks[mb].color;
var r = Math.floor((originalColor >> 16 & 0xFF) * colorIntensity);
var g = Math.floor((originalColor >> 8 & 0xFF) * colorIntensity);
var b = Math.floor((originalColor & 0xFF) * colorIntensity);
musicButtons[mb].shape.tint = r << 16 | g << 8 | b;
}
}
}
// Only update game logic when playing
if (gameState !== 'playing') {
return;
}
// Add fluid animation to timing line with music synchronization
if (timingLine.visible) {
// Pulsing effect based on notes approaching
var nearbyNotes = 0;
for (var t = 0; t < notes.length; t++) {
var distanceToLine = Math.abs(notes[t].y - timingLineY);
if (distanceToLine < 200) {
nearbyNotes++;
}
}
var pulseIntensity = 1 + nearbyNotes * 0.1;
var basePulse = 1 + Math.sin(LK.ticks * 0.2) * 0.1 * pulseIntensity;
// Add music beat synchronization
var musicPulse = 1;
if (beatIntensity > 0) {
musicPulse = 1 + beatIntensity * 0.4;
// Create beat wave along timing line
timingLine.scaleX = 1 + beatIntensity * 0.1;
}
timingLine.scaleY = basePulse * musicPulse;
timingLine.alpha = 0.8 + Math.sin(LK.ticks * 0.15) * 0.2;
// Music-reactive color cycling
var musicColorCycle = Math.sin(musicBeat * 0.05) * 0.5 + 0.5;
var baseColor = 0xFFFFFF;
if (nearbyNotes > 0) {
var colorShift = Math.sin(LK.ticks * 0.3) * 0.5 + 0.5;
baseColor = 0xFFFFFF + (Math.floor(colorShift * 50) << 16);
}
// Add music-reactive blue/cyan tint
var musicTint = Math.floor(musicColorCycle * 255);
timingLine.tint = baseColor & 0xFF0000 | musicTint << 8 | 0xFF;
}
// Add music-synchronized glow effect to buttons
for (var b = 0; b < buttons.length; b++) {
if (buttons[b] && buttons[b].buttonGraphics) {
var baseGlow = Math.sin(LK.ticks * 0.08 + b) * 0.1 + 0.9;
var musicGlow = 1;
// Music beat detection for synchronized effects
var isOnBeat = beatIntensity > 0.7;
var isMediumBeat = beatIntensity > 0.4;
if (beatIntensity > 0) {
musicGlow = 1 + beatIntensity * 0.3;
// Enhanced beat-reactive scaling with smooth transitions
var targetScaleX = 1 + beatIntensity * 0.15;
var targetScaleY = 1 + beatIntensity * 0.15;
// Smooth scaling animation to target
tween(buttons[b].buttonGraphics, {
scaleX: targetScaleX,
scaleY: targetScaleY
}, {
duration: 100,
easing: tween.easeOut
});
// Strong beat effects
if (isOnBeat) {
// Pulse ring effect around button
var beatRing = game.addChild(LK.getAsset('button', {
anchorX: 0.5,
anchorY: 0.5
}));
beatRing.x = buttons[b].x;
beatRing.y = buttons[b].y;
beatRing.alpha = 0.6;
beatRing.scaleX = 0.8;
beatRing.scaleY = 0.8;
beatRing.tint = 0x00FFFF;
// Animate ring expanding and fading
tween(beatRing, {
scaleX: 2.0,
scaleY: 2.0,
alpha: 0
}, {
duration: 600,
easing: tween.easeOut,
onFinish: function onFinish() {
if (beatRing && beatRing.parent) {
beatRing.destroy();
}
}
});
// Color flash effect on strong beats
var originalTint = buttons[b].buttonGraphics.tint;
buttons[b].buttonGraphics.tint = 0xFFFFFF;
tween(buttons[b].buttonGraphics, {
tint: originalTint
}, {
duration: 200,
easing: tween.easeOut
});
}
// Medium beat effects
if (isMediumBeat && !isOnBeat) {
// Subtle glow pulse
var originalAlpha = buttons[b].buttonGraphics.alpha;
tween(buttons[b].buttonGraphics, {
alpha: Math.min(1.0, originalAlpha * 1.3)
}, {
duration: 150,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(buttons[b].buttonGraphics, {
alpha: originalAlpha
}, {
duration: 150,
easing: tween.easeOut
});
}
});
}
}
// Continuous music-reactive color cycling
var musicColorCycle = Math.sin(musicBeat * 0.1 + b * 1.2) * 0.5 + 0.5;
var baseColor = 0x00FF00; // Default green
// Apply music-reactive color shifts
if (beatIntensity > 0.2) {
var beatColorR = Math.floor((musicColorCycle + beatIntensity * 0.5) * 150) % 255;
var beatColorG = Math.floor(musicColorCycle * 255);
var beatColorB = Math.floor((1 - musicColorCycle + beatIntensity * 0.3) * 200) % 255;
baseColor = beatColorR << 16 | beatColorG << 8 | beatColorB;
}
// Apply subtle rotation on beats
if (beatIntensity > 0.5) {
var rotationAmount = beatIntensity * 0.1 * Math.sin(LK.ticks * 0.2 + b);
buttons[b].buttonGraphics.rotation = rotationAmount;
} else {
// Return to neutral rotation smoothly
if (Math.abs(buttons[b].buttonGraphics.rotation) > 0.01) {
tween(buttons[b].buttonGraphics, {
rotation: 0
}, {
duration: 300,
easing: tween.easeOut
});
}
}
// Apply final alpha and color
buttons[b].buttonGraphics.alpha = buttons[b].isPressed ? 0.7 : baseGlow * musicGlow;
if (!buttons[b].isPressed) {
buttons[b].buttonGraphics.tint = baseColor;
}
}
}
// Add music-synchronized flowing effects to lane separators
if (lane1.visible && lane2.visible) {
// Flowing light effect down the lanes with music sync
var baseFlow = Math.sin(LK.ticks * 0.1) * 0.3 + 0.7;
var musicFlow = 1;
if (beatIntensity > 0) {
musicFlow = 1 + beatIntensity * 0.5;
}
lane1.alpha = baseFlow * musicFlow;
lane2.alpha = baseFlow * musicFlow;
// Music-reactive width pulsing
var baseWidthPulse = 1 + Math.sin(LK.ticks * 0.05) * 0.2;
var musicWidthPulse = 1;
if (beatIntensity > 0) {
musicWidthPulse = 1 + beatIntensity * 0.3;
}
lane1.scaleX = baseWidthPulse * musicWidthPulse;
lane2.scaleX = baseWidthPulse * musicWidthPulse;
// Music-reactive color shifting
var tempo = Math.sin(LK.ticks * 0.2) * 0.5 + 0.5;
var musicTempo = Math.sin(musicBeat * 0.1) * 0.5 + 0.5;
var colorR = Math.floor((tempo + musicTempo) * 100) % 255;
var colorG = Math.floor(musicTempo * 150) % 255;
var colorTint = 0xFF0000 | colorG << 8 | 0xFF;
lane1.tint = colorTint;
lane2.tint = colorTint;
}
// Update timer - countdown every second (60 frames)
if (LK.ticks % 60 === 0 && timeRemaining > 0) {
timeRemaining--;
timerTxt.setText(timeRemaining.toString());
}
// Check for game over when timer reaches 0
if (timeRemaining <= 0 && gameState === 'playing') {
gameState = 'gameOver';
// Clean up all remaining notes without awarding points
for (var n = notes.length - 1; n >= 0; n--) {
var note = notes[n];
if (!note.hasBeenHit) {
note.hasBeenHit = true;
// Create visual effects for remaining notes without scoring
LK.effects.flashObject(note, 0xFFD700, 200);
createExplosion(note.x, note.y, 0xFFD700);
// Remove the note
note.destroy();
notes.splice(n, 1);
}
}
// Update final score display
scoreTxt.setText(LK.getScore());
// Hide game elements
timerTxt.visible = false;
scoreTxt.visible = false;
timingLine.visible = false;
bottomLine.visible = false;
lane1.visible = false;
lane2.visible = false;
// Hide buttons
for (var i = 0; i < buttons.length; i++) {
buttons[i].visible = false;
}
// Stop all music when game over screen appears
LK.stopMusic();
// Show game over menu with smooth animations
gameOverTxt.visible = true;
gameOverTxt.alpha = 0;
gameOverTxt.scaleX = 0.5;
gameOverTxt.scaleY = 0.5;
finalScoreTxt.setText('Final Score: ' + LK.getScore() + '\nDifficulty: ' + difficulties[currentDifficulty].name);
finalScoreTxt.visible = true;
finalScoreTxt.alpha = 0;
finalScoreTxt.y = finalScoreTxt.y + 50;
gameOverButton.shape.visible = true;
gameOverButton.shape.alpha = 0;
gameOverButton.shape.scaleX = 0;
gameOverButton.shape.scaleY = 0;
gameOverButton.text.visible = true;
gameOverButton.text.alpha = 0;
// Animate game over text
tween(gameOverTxt, {
alpha: 1,
scaleX: 1,
scaleY: 1
}, {
duration: 800,
easing: tween.bounceOut
});
// Animate final score text sliding up
tween(finalScoreTxt, {
alpha: 1,
y: finalScoreTxt.y - 50
}, {
duration: 600,
easing: tween.easeOut,
delay: 400
});
// Animate restart button
tween(gameOverButton.shape, {
alpha: 1,
scaleX: 1,
scaleY: 1
}, {
duration: 600,
easing: tween.elasticOut,
delay: 800
});
tween(gameOverButton.text, {
alpha: 1
}, {
duration: 400,
delay: 1000
});
}
// Spawn notes
spawnTimer++;
if (spawnTimer >= spawnInterval) {
spawnNote();
spawnTimer = 0;
// Gradually increase spawn rate
if (spawnInterval > 60) {
spawnInterval = Math.max(60, spawnInterval - 1);
}
}
// Update and check notes with music synchronization
for (var i = notes.length - 1; i >= 0; i--) {
var note = notes[i];
// Add music-reactive pulsing effect to notes as they approach timing line
var distanceToTimingLine = Math.abs(note.y - timingLineY);
if (distanceToTimingLine < 300) {
var proximityPulse = 1 + (300 - distanceToTimingLine) / 300 * 0.3;
var musicNotePulse = 1;
if (beatIntensity > 0) {
musicNotePulse = 1 + beatIntensity * 0.2;
// Add music-reactive glow
note.noteGraphics.alpha = 1 + beatIntensity * 0.3;
}
note.noteGraphics.scaleX = proximityPulse * musicNotePulse;
note.noteGraphics.scaleY = proximityPulse * musicNotePulse;
// Music-reactive color shifting for approaching notes
if (distanceToTimingLine < 150) {
var beatColor = Math.sin(musicBeat * 0.2) * 0.5 + 0.5;
var colorShift = Math.floor(beatColor * 100);
note.noteGraphics.tint = 0xFFFFFF + (colorShift << 16) + (colorShift << 8);
}
}
// Check if note reached bottom line (missed)
if (note.y >= bottomLineY && !note.hasBeenHit) {
LK.getSound('miss').play();
// Create red explosion effect for missed notes
createExplosion(note.x, note.y, 0xFF0000);
// Add screen shake for missed notes
shakeScreen(5, 100);
// Update combo (miss)
updateCombo(false);
// Show missed feedback text
feedbackTxt.setText('MISSED');
feedbackTxt.alpha = 1;
tween(feedbackTxt, {
alpha: 0
}, {
duration: 800
});
note.destroy();
notes.splice(i, 1);
continue;
}
// Remove notes that went off screen or were hit
if (note.y > 2732 + 100 || note.hasBeenHit) {
if (note.hasBeenHit) {
// Small delay before removing hit notes for visual feedback
LK.setTimeout(function (noteToRemove, index) {
return function () {
if (noteToRemove && noteToRemove.parent) {
noteToRemove.destroy();
}
var noteIndex = notes.indexOf(noteToRemove);
if (noteIndex !== -1) {
notes.splice(noteIndex, 1);
}
};
}(note, i), 200);
} else {
note.destroy();
notes.splice(i, 1);
}
}
}
}; ===================================================================
--- original.js
+++ change.js
@@ -37,35 +37,23 @@
// Floating motion
self.x += Math.sin(LK.ticks * self.floatSpeed) * 0.5;
// Gentle rotation
self.particleGraphics.rotation += self.rotationSpeed;
- // Enhanced music-reactive effects during gameplay
+ // Music-reactive pulsing during gameplay
var basePulse = 1 + Math.sin(LK.ticks * 0.05) * 0.1;
var musicPulse = 1;
if (gameState === 'playing' && beatIntensity > 0) {
- // More dynamic pulsing based on beat strength and phase
- var beatPhaseMod = Math.sin(beatPhase * Math.PI * 0.5) * 0.2 + 0.8;
- musicPulse = 1 + beatIntensity * beatStrength * 0.5 * beatPhaseMod;
- // Add rhythmic movement sync
- self.speed = (1 + Math.random() * 3) * (1 + beatIntensity * 0.3);
+ musicPulse = 1 + beatIntensity * 0.3;
+ beatIntensity *= 0.95; // Decay beat intensity
}
var finalPulse = basePulse * musicPulse;
self.particleGraphics.scaleX = scale * finalPulse;
self.particleGraphics.scaleY = scale * finalPulse;
- // Enhanced music-reactive color and transparency effects
+ // Music-reactive color shifting
if (gameState === 'playing') {
- // Complex color cycling based on multiple beat parameters
- var beatColor = Math.sin(musicBeat * 0.1 + beatPhase) * 0.5 + 0.5;
- var tempoColor = Math.cos(LK.ticks * 0.02 * musicTempo) * 0.3 + 0.7;
- var intensityColor = beatIntensity * beatStrength;
- // Multi-layered color shifting
- var colorR = Math.floor((beatColor + intensityColor) * 127) + 128;
- var colorG = Math.floor((tempoColor + beatIntensity) * 127) + 128;
- var colorB = Math.floor((Math.sin(beatPhase * 0.5) * 0.5 + 0.5 + intensityColor) * 127) + 128;
- self.particleGraphics.tint = colorR << 16 | colorG << 8 | colorB;
- // Beat-synchronized transparency pulsing
- var baseAlpha = 0.1 + Math.random() * 0.3;
- self.particleGraphics.alpha = baseAlpha * (1 + beatIntensity * 0.4);
+ var beatColor = Math.sin(musicBeat * 0.1) * 0.5 + 0.5;
+ var musicColorShift = Math.floor(beatColor * 100);
+ self.particleGraphics.tint = self.particleGraphics.tint & 0xFF00FF | musicColorShift << 8;
}
// Reset particle when it goes off screen
if (self.y < -100) {
self.y = 2732 + 100;
@@ -211,9 +199,9 @@
// Special animations for GREAT and PERFECT
if (feedbackText === 'PERFECT') {
// Perfect animation: scale up and spin with color change
var originalScale = self.buttonGraphics.scaleX;
- var originalTint = self.buttonGraphics.tint || 0xFFFFFF;
+ var originalTint = self.buttonGraphics.tint;
self.buttonGraphics.tint = 0xFFD700; // Gold color
tween(self.buttonGraphics, {
scaleX: originalScale * 1.5,
scaleY: originalScale * 1.5,
@@ -225,9 +213,9 @@
tween(self.buttonGraphics, {
scaleX: originalScale,
scaleY: originalScale,
rotation: 0,
- tint: originalTint || 0xFFFFFF
+ tint: originalTint
}, {
duration: 200
});
}
@@ -259,9 +247,9 @@
});
} else if (feedbackText === 'GREAT') {
// Great animation: pulse and glow effect
var originalScale = self.buttonGraphics.scaleX;
- var originalTint = self.buttonGraphics.tint || 0xFFFFFF;
+ var originalTint = self.buttonGraphics.tint;
self.buttonGraphics.tint = 0x00FFFF; // Cyan color
tween(self.buttonGraphics, {
scaleX: originalScale * 1.3,
scaleY: originalScale * 1.3
@@ -271,9 +259,9 @@
onFinish: function onFinish() {
tween(self.buttonGraphics, {
scaleX: originalScale,
scaleY: originalScale,
- tint: originalTint || 0xFFFFFF
+ tint: originalTint
}, {
duration: 300,
easing: tween.easeOut
});
@@ -421,16 +409,8 @@
var musicBeat = 0;
var beatInterval = 30; // frames per beat (adjustable based on music tempo)
var beatIntensity = 0;
var musicPulseElements = [];
-// Enhanced music sync variables
-var beatTimer = 0;
-var lastBeatTime = 0;
-var beatStrength = 0;
-var musicTempo = 1.0;
-var beatPhase = 0;
-var musicIntensityDecay = 0.92;
-var strongBeatThreshold = 0.8;
// Create lane separators
var lane1 = game.addChild(LK.getAsset('lane', {
anchorX: 0.5,
anchorY: 0
@@ -779,42 +759,31 @@
var currentBgColorIndex = 0;
var bgColorTransitionTimer = 0;
function updateBackgroundColor() {
bgColorTransitionTimer++;
- // Enhanced music-reactive beat pulsing
+ // Music-reactive beat pulsing
if (gameState === 'playing') {
- beatTimer++;
musicBeat++;
- // Dynamic beat detection with varying intervals
- var adaptiveBeatInterval = Math.floor(beatInterval * musicTempo);
- if (beatTimer >= adaptiveBeatInterval) {
- beatTimer = 0;
- var currentTime = LK.ticks;
- // Calculate beat strength based on time since last beat
- var timeSinceLastBeat = currentTime - lastBeatTime;
- beatStrength = Math.min(1.0, Math.max(0.3, 1.0 - (timeSinceLastBeat - adaptiveBeatInterval) / adaptiveBeatInterval));
- beatIntensity = beatStrength;
- lastBeatTime = currentTime;
- beatPhase = (beatPhase + 1) % 4; // Track beat phases for complex rhythms
- // Flash background on beat with intensity-based brightness
+ if (musicBeat >= beatInterval) {
+ musicBeat = 0;
+ beatIntensity = 1.0;
+ // Flash background on beat
var currentBgColor = backgroundColors[currentBgColorIndex];
- var intensityMultiplier = beatStrength > strongBeatThreshold ? 50 : 30;
var brightColor = {
- r: Math.min(255, (currentBgColor >> 16 & 0xFF) + intensityMultiplier * beatStrength),
- g: Math.min(255, (currentBgColor >> 8 & 0xFF) + intensityMultiplier * beatStrength),
- b: Math.min(255, (currentBgColor & 0xFF) + intensityMultiplier * beatStrength)
+ r: Math.min(255, (currentBgColor >> 16 & 0xFF) + 30),
+ g: Math.min(255, (currentBgColor >> 8 & 0xFF) + 30),
+ b: Math.min(255, (currentBgColor & 0xFF) + 30)
};
var flashColor = brightColor.r << 16 | brightColor.g << 8 | brightColor.b;
game.setBackgroundColor(flashColor);
- // Dynamic fade duration based on beat strength
- var fadeDuration = Math.floor(adaptiveBeatInterval * 16 * (0.5 + beatStrength * 0.5));
+ // Fade back to normal
tween({
- intensity: beatStrength
+ intensity: 1
}, {
intensity: 0
}, {
- duration: fadeDuration,
- easing: beatStrength > strongBeatThreshold ? tween.easeOut : tween.linear,
+ duration: beatInterval * 16,
+ easing: tween.easeOut,
onUpdate: function onUpdate() {
var fade = this.intensity;
var newR = Math.floor((currentBgColor >> 16 & 0xFF) + (brightColor.r - (currentBgColor >> 16 & 0xFF)) * fade);
var newG = Math.floor((currentBgColor >> 8 & 0xFF) + (brightColor.g - (currentBgColor >> 8 & 0xFF)) * fade);
@@ -822,10 +791,8 @@
game.setBackgroundColor(newR << 16 | newG << 8 | newB);
}
});
}
- // Gradual decay of beat intensity for smoother transitions
- beatIntensity *= musicIntensityDecay;
}
if (bgColorTransitionTimer >= 1800) {
// Change every 30 seconds
bgColorTransitionTimer = 0;
@@ -1309,29 +1276,15 @@
var lane = Math.floor(Math.random() * 3);
var noteTypes = ['slow', 'medium', 'fast'];
var type = noteTypes[Math.floor(Math.random() * noteTypes.length)];
var note = new Note(lane, type);
- // Enhanced music-reactive entrance animation
+ // Music-reactive entrance animation
var musicSpawnScale = 1;
var musicSpawnColor = 0xFFFFFF;
- var musicSpawnRotation = 0;
- var musicSpawnDuration = 300;
- // Adapt to current beat strength and phase
- if (beatIntensity > 0.5) {
- var spawnIntensity = beatIntensity * beatStrength;
- musicSpawnScale = 1 + spawnIntensity * 0.5;
- musicSpawnDuration = Math.floor(300 * (1 + spawnIntensity * 0.3));
- musicSpawnRotation = spawnIntensity * Math.PI * 0.25;
- // Color based on beat phase and intensity
- if (beatPhase === 0 || beatStrength > strongBeatThreshold) {
- musicSpawnColor = 0xFFD700; // Gold for strong beats
- } else if (beatPhase === 1) {
- musicSpawnColor = 0xFF6600; // Orange for second beat
- } else if (beatPhase === 2) {
- musicSpawnColor = 0x00FFFF; // Cyan for third beat
- } else {
- musicSpawnColor = 0xFF00FF; // Magenta for fourth beat
- }
+ if (beatIntensity > 0.7) {
+ // Spawn with extra flair on strong beats
+ musicSpawnScale = 1.3;
+ musicSpawnColor = 0xFFD700; // Gold color for beat spawns
note.noteGraphics.tint = musicSpawnColor;
}
// Add entrance animation - start small and grow with music sync
note.noteGraphics.scaleX = 0;
@@ -1522,89 +1475,185 @@
if (!breathingEffectStarted && gameState === 'playing') {
breathingEffectStarted = true;
createBreathingEffect();
}
+ // Add music-synchronized button effects during menu
+ if (gameState === 'menu' && currentMusic !== -1) {
+ // Create gentle music-reactive effects for menu buttons
+ var menuMusicBeat = Math.sin(LK.ticks * 0.1) * 0.5 + 0.5;
+ // Animate start button with music
+ if (startButton && startButton.shape && startButton.shape.visible) {
+ var startPulse = 1 + menuMusicBeat * 0.05;
+ startButton.shape.scaleX = startPulse;
+ startButton.shape.scaleY = startPulse;
+ // Subtle color shift
+ var startColorShift = Math.floor(menuMusicBeat * 50);
+ startButton.shape.tint = 0x00AA00 + (startColorShift << 8);
+ }
+ // Animate music selection buttons with staggered timing
+ for (var mb = 0; mb < musicButtons.length; mb++) {
+ if (musicButtons[mb] && musicButtons[mb].shape && musicButtons[mb].shape.visible) {
+ var staggeredBeat = Math.sin(LK.ticks * 0.08 + mb * 0.8) * 0.5 + 0.5;
+ var buttonPulse = 1 + staggeredBeat * 0.03;
+ // Scale animation
+ if (currentMusic !== mb) {
+ musicButtons[mb].shape.scaleX = buttonPulse;
+ musicButtons[mb].shape.scaleY = buttonPulse;
+ }
+ // Color breathing effect
+ var colorIntensity = staggeredBeat * 0.3 + 0.7;
+ var originalColor = musicTracks[mb].color;
+ var r = Math.floor((originalColor >> 16 & 0xFF) * colorIntensity);
+ var g = Math.floor((originalColor >> 8 & 0xFF) * colorIntensity);
+ var b = Math.floor((originalColor & 0xFF) * colorIntensity);
+ musicButtons[mb].shape.tint = r << 16 | g << 8 | b;
+ }
+ }
+ }
// Only update game logic when playing
if (gameState !== 'playing') {
return;
}
- // Enhanced timing line animation with sophisticated music synchronization
+ // Add fluid animation to timing line with music synchronization
if (timingLine.visible) {
// Pulsing effect based on notes approaching
var nearbyNotes = 0;
- var urgencyLevel = 0;
for (var t = 0; t < notes.length; t++) {
var distanceToLine = Math.abs(notes[t].y - timingLineY);
if (distanceToLine < 200) {
nearbyNotes++;
- urgencyLevel += (200 - distanceToLine) / 200;
}
}
- var pulseIntensity = 1 + nearbyNotes * 0.1 + urgencyLevel * 0.05;
- var basePulse = 1 + Math.sin(LK.ticks * 0.2 * musicTempo) * 0.1 * pulseIntensity;
- // Enhanced music beat synchronization with phase-based effects
+ var pulseIntensity = 1 + nearbyNotes * 0.1;
+ var basePulse = 1 + Math.sin(LK.ticks * 0.2) * 0.1 * pulseIntensity;
+ // Add music beat synchronization
var musicPulse = 1;
- var musicWave = 1;
- var musicColorIntensity = 0;
if (beatIntensity > 0) {
- // Multi-dimensional pulsing based on beat phase
- var phasePulse = Math.sin(beatPhase * Math.PI * 0.5) * 0.3 + 0.7;
- musicPulse = 1 + beatIntensity * beatStrength * 0.6 * phasePulse;
- musicWave = 1 + beatIntensity * 0.2;
- musicColorIntensity = beatIntensity * beatStrength;
- // Create dynamic beat wave along timing line
- var waveEffect = Math.sin(LK.ticks * 0.3 + beatPhase) * beatIntensity * 0.15;
- timingLine.scaleX = musicWave + waveEffect;
+ musicPulse = 1 + beatIntensity * 0.4;
+ // Create beat wave along timing line
+ timingLine.scaleX = 1 + beatIntensity * 0.1;
}
timingLine.scaleY = basePulse * musicPulse;
- timingLine.alpha = 0.8 + Math.sin(LK.ticks * 0.15 * musicTempo) * 0.2 + musicColorIntensity * 0.3;
- // Enhanced music-reactive color system
- var musicColorCycle = Math.sin(musicBeat * 0.05 + beatPhase * 0.5) * 0.5 + 0.5;
- var tempoColorCycle = Math.cos(LK.ticks * 0.1 * musicTempo) * 0.5 + 0.5;
+ timingLine.alpha = 0.8 + Math.sin(LK.ticks * 0.15) * 0.2;
+ // Music-reactive color cycling
+ var musicColorCycle = Math.sin(musicBeat * 0.05) * 0.5 + 0.5;
var baseColor = 0xFFFFFF;
if (nearbyNotes > 0) {
- // Urgency-based color shifting
- var urgencyColor = Math.min(1, urgencyLevel * 0.3);
- var colorShift = Math.sin(LK.ticks * 0.3 + urgencyColor) * 0.5 + 0.5;
- baseColor = 0xFFFFFF + (Math.floor(colorShift * urgencyColor * 100) << 16);
+ var colorShift = Math.sin(LK.ticks * 0.3) * 0.5 + 0.5;
+ baseColor = 0xFFFFFF + (Math.floor(colorShift * 50) << 16);
}
- // Complex music-reactive color mixing
- var redComponent = Math.floor((musicColorCycle + musicColorIntensity) * 127) + 128;
- var greenComponent = Math.floor((tempoColorCycle + beatIntensity) * 127) + 128;
- var blueComponent = Math.floor((Math.sin(beatPhase) * 0.5 + 0.5 + musicColorIntensity) * 127) + 128;
- timingLine.tint = redComponent << 16 | greenComponent << 8 | blueComponent;
+ // Add music-reactive blue/cyan tint
+ var musicTint = Math.floor(musicColorCycle * 255);
+ timingLine.tint = baseColor & 0xFF0000 | musicTint << 8 | 0xFF;
}
- // Enhanced music-synchronized effects for buttons
+ // Add music-synchronized glow effect to buttons
for (var b = 0; b < buttons.length; b++) {
if (buttons[b] && buttons[b].buttonGraphics) {
- // Multi-layered glow effects
var baseGlow = Math.sin(LK.ticks * 0.08 + b) * 0.1 + 0.9;
- var tempoGlow = Math.cos(LK.ticks * 0.05 * musicTempo + b * 1.2) * 0.15 + 0.85;
var musicGlow = 1;
- var musicScale = 1;
- var musicRotation = 0;
+ // Music beat detection for synchronized effects
+ var isOnBeat = beatIntensity > 0.7;
+ var isMediumBeat = beatIntensity > 0.4;
if (beatIntensity > 0) {
- // Dynamic effects based on beat strength and phase
- var phaseOffset = (beatPhase + b) % 4;
- var phaseMod = Math.sin(phaseOffset * Math.PI * 0.5) * 0.5 + 0.5;
- musicGlow = 1 + beatIntensity * beatStrength * 0.4 * phaseMod;
- musicScale = 1 + beatIntensity * beatStrength * 0.15 * phaseMod;
- musicRotation = beatIntensity * beatStrength * 0.1 * Math.sin(phaseOffset);
+ musicGlow = 1 + beatIntensity * 0.3;
+ // Enhanced beat-reactive scaling with smooth transitions
+ var targetScaleX = 1 + beatIntensity * 0.15;
+ var targetScaleY = 1 + beatIntensity * 0.15;
+ // Smooth scaling animation to target
+ tween(buttons[b].buttonGraphics, {
+ scaleX: targetScaleX,
+ scaleY: targetScaleY
+ }, {
+ duration: 100,
+ easing: tween.easeOut
+ });
// Strong beat effects
- if (beatStrength > strongBeatThreshold) {
- musicGlow += 0.2;
- musicScale += 0.1;
- // Add tint flash on strong beats
- var flashTint = 0xFFFFFF + (Math.floor(beatStrength * 50) << 16);
- buttons[b].buttonGraphics.tint = flashTint;
- } else {
+ if (isOnBeat) {
+ // Pulse ring effect around button
+ var beatRing = game.addChild(LK.getAsset('button', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ }));
+ beatRing.x = buttons[b].x;
+ beatRing.y = buttons[b].y;
+ beatRing.alpha = 0.6;
+ beatRing.scaleX = 0.8;
+ beatRing.scaleY = 0.8;
+ beatRing.tint = 0x00FFFF;
+ // Animate ring expanding and fading
+ tween(beatRing, {
+ scaleX: 2.0,
+ scaleY: 2.0,
+ alpha: 0
+ }, {
+ duration: 600,
+ easing: tween.easeOut,
+ onFinish: function onFinish() {
+ if (beatRing && beatRing.parent) {
+ beatRing.destroy();
+ }
+ }
+ });
+ // Color flash effect on strong beats
+ var originalTint = buttons[b].buttonGraphics.tint;
buttons[b].buttonGraphics.tint = 0xFFFFFF;
+ tween(buttons[b].buttonGraphics, {
+ tint: originalTint
+ }, {
+ duration: 200,
+ easing: tween.easeOut
+ });
}
- buttons[b].buttonGraphics.scaleX = musicScale;
- buttons[b].buttonGraphics.scaleY = musicScale;
- buttons[b].buttonGraphics.rotation = musicRotation;
+ // Medium beat effects
+ if (isMediumBeat && !isOnBeat) {
+ // Subtle glow pulse
+ var originalAlpha = buttons[b].buttonGraphics.alpha;
+ tween(buttons[b].buttonGraphics, {
+ alpha: Math.min(1.0, originalAlpha * 1.3)
+ }, {
+ duration: 150,
+ easing: tween.easeOut,
+ onFinish: function onFinish() {
+ tween(buttons[b].buttonGraphics, {
+ alpha: originalAlpha
+ }, {
+ duration: 150,
+ easing: tween.easeOut
+ });
+ }
+ });
+ }
}
- buttons[b].buttonGraphics.alpha = buttons[b].isPressed ? 0.7 : baseGlow * tempoGlow * musicGlow;
+ // Continuous music-reactive color cycling
+ var musicColorCycle = Math.sin(musicBeat * 0.1 + b * 1.2) * 0.5 + 0.5;
+ var baseColor = 0x00FF00; // Default green
+ // Apply music-reactive color shifts
+ if (beatIntensity > 0.2) {
+ var beatColorR = Math.floor((musicColorCycle + beatIntensity * 0.5) * 150) % 255;
+ var beatColorG = Math.floor(musicColorCycle * 255);
+ var beatColorB = Math.floor((1 - musicColorCycle + beatIntensity * 0.3) * 200) % 255;
+ baseColor = beatColorR << 16 | beatColorG << 8 | beatColorB;
+ }
+ // Apply subtle rotation on beats
+ if (beatIntensity > 0.5) {
+ var rotationAmount = beatIntensity * 0.1 * Math.sin(LK.ticks * 0.2 + b);
+ buttons[b].buttonGraphics.rotation = rotationAmount;
+ } else {
+ // Return to neutral rotation smoothly
+ if (Math.abs(buttons[b].buttonGraphics.rotation) > 0.01) {
+ tween(buttons[b].buttonGraphics, {
+ rotation: 0
+ }, {
+ duration: 300,
+ easing: tween.easeOut
+ });
+ }
+ }
+ // Apply final alpha and color
+ buttons[b].buttonGraphics.alpha = buttons[b].isPressed ? 0.7 : baseGlow * musicGlow;
+ if (!buttons[b].isPressed) {
+ buttons[b].buttonGraphics.tint = baseColor;
+ }
}
}
// Add music-synchronized flowing effects to lane separators
if (lane1.visible && lane2.visible) {