User prompt
Let the animations spread across the entire screen. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
When perfect and great are written on the screen, make an animation that matches the background. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
When you type perfect and great on the screen, a wave-shaped animation will appear from the perfect and great text on the screen. Change the background color as the animation progresses. The background color should never be the color of the buttons, notes, or lines. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Play a colorful animation that matches the background when you type Perfect and great on the screen. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
When perfect is written on the screen, play an animation around the last pressed button. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Code two and three buttons to be pressed simultaneously.
User prompt
Make explosion animation. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
When great and perfect are typed on the screen, play different animations on the last pressed key. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Play separate animations when perfect and great are written on the screen. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Add game ending menu.
User prompt
Add a start menu to the game.
User prompt
Add a stopwatch above the Score section. Let it go backwards from 60. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Add a counter slightly above the Score section.
User prompt
Move the score section up a little bit.
User prompt
Move the Score text up too high
User prompt
Print score to screen. In the middle where the notes fall
User prompt
Print a scoreboard to the screen.
User prompt
Write a scoreboard that is right next to the top center of the screen. When perfect is written on the screen, +2 points will be added. When you write Great, +1 point is added. If you write Missed, add -1 point. There should be no point limit. ↪💡 Consider importing and using the following plugins: @upit/storage.v1
User prompt
There is a scoreboard at the top of the middle part of the screen. When it says "perfect" on the screen, +2 points should be added, when it says "great", +1 points should be added. When it says "missed", -1 point should be added.
User prompt
Draw a very thin miss line adjacent to the bottom edge of the screen. When notes touch this line, the note that touched it will disappear and the screen will print as missed. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
When notes touch the miss line, print missed on the screen. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Once a note enters the pressing area, the note will disappear when the buttons are pressed.
User prompt
Print text return on screen with each press. According to previous rules. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Please fix the bug: 'Uncaught TypeError: tween.to is not a function' in or related to this line: 'tween.to(feedbackTxt, {' Line Number: 78 ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
If the button is pressed while a note is below the 1st horizontal line, the text great is displayed on the screen. But if the button is pressed while the note is above the button, the text perfect is displayed on the screen. Move the missed line at the bottom down very far.
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
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;
self.buttonGraphics.alpha = 0.7;
// Add to pressed buttons for chord detection
if (pressedButtons.indexOf(self.lane) === -1) {
pressedButtons.push(self.lane);
}
chordPressTimer = 10; // Reset chord timer (frames to detect simultaneous press)
// Check for chord notes first
var hitChordNote = null;
var bestChordDistance = Infinity;
for (var i = 0; i < chordNotes.length; i++) {
var chordNote = chordNotes[i];
if (!chordNote.hasBeenHit && chordNote.isInLane(self.lane) && chordNote.y >= timingLineY) {
var distance = Math.abs(chordNote.y - self.y);
if (distance < bestChordDistance) {
bestChordDistance = distance;
hitChordNote = chordNote;
}
}
}
// Check if all required buttons for chord are pressed
if (hitChordNote) {
var allPressed = true;
for (var j = 0; j < hitChordNote.lanes.length; j++) {
if (pressedButtons.indexOf(hitChordNote.lanes[j]) === -1) {
allPressed = false;
break;
}
}
if (allPressed) {
hitChordNote.hasBeenHit = true;
// Calculate points based on timing
var points = 0;
var feedbackText = '';
if (bestChordDistance <= 150) {
points = 200; // Higher points for chords
feedbackText = 'PERFECT CHORD';
} else if (bestChordDistance <= 250) {
points = 100;
feedbackText = 'GREAT CHORD';
}
if (points > 0) {
LK.setScore(LK.getScore() + points);
scoreTxt.setText(LK.getScore());
LK.getSound('hit').play();
// Create explosion at center of chord
var centerX = 0;
for (var k = 0; k < hitChordNote.lanes.length; k++) {
centerX += hitChordNote.lanes[k] * (2048 / 3) + 2048 / 6;
}
centerX /= hitChordNote.lanes.length;
createExplosion(centerX, hitChordNote.y, 0xFFD700);
// Show feedback text
feedbackTxt.setText(feedbackText);
feedbackTxt.alpha = 1;
tween(feedbackTxt, {
alpha: 0
}, {
duration: 800
});
}
return; // Don't check single notes if chord was hit
}
}
// 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) {
LK.setScore(LK.getScore() + points);
scoreTxt.setText(LK.getScore());
LK.getSound('hit').play();
// 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
});
// 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
});
}
});
} 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
LK.setTimeout(function () {
self.isPressed = false;
self.buttonGraphics.alpha = 1.0;
}, 100);
};
return self;
});
var ChordNote = Container.expand(function (lanes, chordType) {
var self = Container.call(this);
self.lanes = lanes; // Array of lane numbers
self.chordType = chordType; // '2chord' or '3chord'
self.hasBeenHit = false;
self.speed = 8;
self.requiredPresses = lanes.length;
self.actualPresses = 0;
// Create note graphics for each lane
self.noteGraphics = [];
for (var i = 0; i < lanes.length; i++) {
var lane = lanes[i];
var noteGraphic;
if (chordType === '2chord') {
noteGraphic = self.attachAsset('chordNote2', {
anchorX: 0.5,
anchorY: 0.5
});
} else {
noteGraphic = self.attachAsset('chordNote3', {
anchorX: 0.5,
anchorY: 0.5
});
}
noteGraphic.x = lane * (2048 / 3) + 2048 / 6;
noteGraphic.y = 0;
self.noteGraphics.push(noteGraphic);
}
self.update = function () {
for (var i = 0; i < self.noteGraphics.length; i++) {
self.noteGraphics[i].y += self.speed;
}
// Update main container position based on first note
if (self.noteGraphics.length > 0) {
self.y = self.noteGraphics[0].y;
}
};
self.isInLane = function (lane) {
return self.lanes.indexOf(lane) !== -1;
};
return self;
});
var Note = Container.expand(function (lane, type) {
var self = Container.call(this);
self.lane = lane;
self.type = type;
self.hasBeenHit = false;
// Set speed and asset based on type
if (type === 'slow') {
self.speed = 6;
self.noteGraphics = self.attachAsset('slowNote', {
anchorX: 0.5,
anchorY: 0.5
});
} else if (type === 'medium') {
self.speed = 10;
self.noteGraphics = self.attachAsset('mediumNote', {
anchorX: 0.5,
anchorY: 0.5
});
} else if (type === 'fast') {
self.speed = 14;
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 () {
self.y += self.speed;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
// Game state management
var gameState = 'menu'; // 'menu', 'playing', or 'gameOver'
var startButton;
var gameOverButton;
// Game variables
var notes = [];
var chordNotes = [];
var buttons = [];
var maxNotes = 4;
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
var pressedButtons = []; // Track currently pressed buttons
var chordPressTimer = 0; // Timer for chord press detection
// 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;
game.addChild(titleTxt);
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;
var startButtonTxt = new Text2('START', {
size: 60,
fill: 0xFFFFFF
});
startButtonTxt.anchor.set(0.5, 0.5);
startButtonTxt.x = 2048 / 2;
startButtonTxt.y = 2732 / 2 + 100;
game.addChild(startButtonTxt);
// Create start button handler
startButton = {
shape: startButtonShape,
text: startButtonTxt,
down: function down(x, y, obj) {
this.startGame();
},
startGame: function startGame() {
// 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);
}
// Start the game
gameState = 'playing';
timeRemaining = 60;
LK.setScore(0);
scoreTxt.setText('0');
timerTxt.setText('60');
}
};
// 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 = chordNotes.length - 1; i >= 0; i--) {
chordNotes[i].destroy();
chordNotes.splice(i, 1);
}
for (var i = buttons.length - 1; i >= 0; i--) {
buttons[i].destroy();
buttons.splice(i, 1);
}
// 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);
}
// Reset game state
gameState = 'playing';
timeRemaining = 60;
spawnTimer = 0;
spawnInterval = 120;
LK.setScore(0);
scoreTxt.setText('0');
timerTxt.setText('60');
}
};
// 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();
}
}
});
}
}
// Spawn note function
function spawnNote() {
if (notes.length + chordNotes.length >= maxNotes) return;
var spawnType = Math.random();
if (spawnType < 0.15) {
// 15% chance for 2-button chord
var lanes = [];
var lane1 = Math.floor(Math.random() * 3);
var lane2 = (lane1 + 1 + Math.floor(Math.random() * 2)) % 3;
lanes.push(lane1, lane2);
var chordNote = new ChordNote(lanes, '2chord');
chordNotes.push(chordNote);
game.addChild(chordNote);
} else if (spawnType < 0.25) {
// 10% chance for 3-button chord
var lanes = [0, 1, 2]; // All three lanes
var chordNote = new ChordNote(lanes, '3chord');
chordNotes.push(chordNote);
game.addChild(chordNote);
} else {
// 75% chance for single note
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);
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);
}
} 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 () {
// Only update game logic when playing
if (gameState !== 'playing') {
return;
}
// 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';
// 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;
}
// Show game over menu
gameOverTxt.visible = true;
finalScoreTxt.setText('Final Score: ' + LK.getScore());
finalScoreTxt.visible = true;
gameOverButton.shape.visible = true;
gameOverButton.text.visible = true;
}
// Spawn notes
spawnTimer++;
if (spawnTimer >= spawnInterval) {
spawnNote();
spawnTimer = 0;
// Gradually increase spawn rate
if (spawnInterval > 60) {
spawnInterval = Math.max(60, spawnInterval - 1);
}
}
// Update chord press timer
if (chordPressTimer > 0) {
chordPressTimer--;
} else {
pressedButtons = []; // Clear pressed buttons if timer expired
}
// Update and check chord notes
for (var i = chordNotes.length - 1; i >= 0; i--) {
var chordNote = chordNotes[i];
// Check if chord note reached bottom line (missed)
if (chordNote.y >= bottomLineY && !chordNote.hasBeenHit) {
LK.getSound('miss').play();
// Create red explosion effect for missed chord notes
var centerX = 0;
for (var k = 0; k < chordNote.lanes.length; k++) {
centerX += chordNote.lanes[k] * (2048 / 3) + 2048 / 6;
}
centerX /= chordNote.lanes.length;
createExplosion(centerX, chordNote.y, 0xFF0000);
// Show missed feedback text
feedbackTxt.setText('MISSED CHORD');
feedbackTxt.alpha = 1;
tween(feedbackTxt, {
alpha: 0
}, {
duration: 800
});
chordNote.destroy();
chordNotes.splice(i, 1);
continue;
}
// Remove chord notes that went off screen or were hit
if (chordNote.y > 2732 + 100 || chordNote.hasBeenHit) {
if (chordNote.hasBeenHit) {
// Small delay before removing hit chord notes for visual feedback
LK.setTimeout(function (chordNoteToRemove, index) {
return function () {
if (chordNoteToRemove && chordNoteToRemove.parent) {
chordNoteToRemove.destroy();
}
var chordNoteIndex = chordNotes.indexOf(chordNoteToRemove);
if (chordNoteIndex !== -1) {
chordNotes.splice(chordNoteIndex, 1);
}
};
}(chordNote, i), 200);
} else {
chordNote.destroy();
chordNotes.splice(i, 1);
}
}
}
// Update and check notes
for (var i = notes.length - 1; i >= 0; i--) {
var note = notes[i];
// 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);
// 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
@@ -23,8 +23,70 @@
self.press = function () {
if (self.isPressed) return;
self.isPressed = true;
self.buttonGraphics.alpha = 0.7;
+ // Add to pressed buttons for chord detection
+ if (pressedButtons.indexOf(self.lane) === -1) {
+ pressedButtons.push(self.lane);
+ }
+ chordPressTimer = 10; // Reset chord timer (frames to detect simultaneous press)
+ // Check for chord notes first
+ var hitChordNote = null;
+ var bestChordDistance = Infinity;
+ for (var i = 0; i < chordNotes.length; i++) {
+ var chordNote = chordNotes[i];
+ if (!chordNote.hasBeenHit && chordNote.isInLane(self.lane) && chordNote.y >= timingLineY) {
+ var distance = Math.abs(chordNote.y - self.y);
+ if (distance < bestChordDistance) {
+ bestChordDistance = distance;
+ hitChordNote = chordNote;
+ }
+ }
+ }
+ // Check if all required buttons for chord are pressed
+ if (hitChordNote) {
+ var allPressed = true;
+ for (var j = 0; j < hitChordNote.lanes.length; j++) {
+ if (pressedButtons.indexOf(hitChordNote.lanes[j]) === -1) {
+ allPressed = false;
+ break;
+ }
+ }
+ if (allPressed) {
+ hitChordNote.hasBeenHit = true;
+ // Calculate points based on timing
+ var points = 0;
+ var feedbackText = '';
+ if (bestChordDistance <= 150) {
+ points = 200; // Higher points for chords
+ feedbackText = 'PERFECT CHORD';
+ } else if (bestChordDistance <= 250) {
+ points = 100;
+ feedbackText = 'GREAT CHORD';
+ }
+ if (points > 0) {
+ LK.setScore(LK.getScore() + points);
+ scoreTxt.setText(LK.getScore());
+ LK.getSound('hit').play();
+ // Create explosion at center of chord
+ var centerX = 0;
+ for (var k = 0; k < hitChordNote.lanes.length; k++) {
+ centerX += hitChordNote.lanes[k] * (2048 / 3) + 2048 / 6;
+ }
+ centerX /= hitChordNote.lanes.length;
+ createExplosion(centerX, hitChordNote.y, 0xFFD700);
+ // Show feedback text
+ feedbackTxt.setText(feedbackText);
+ feedbackTxt.alpha = 1;
+ tween(feedbackTxt, {
+ alpha: 0
+ }, {
+ duration: 800
+ });
+ }
+ return; // Don't check single notes if chord was hit
+ }
+ }
// Check for notes in this lane
var hitNote = null;
var bestDistance = Infinity;
for (var i = 0; i < notes.length; i++) {
@@ -130,8 +192,50 @@
}, 100);
};
return self;
});
+var ChordNote = Container.expand(function (lanes, chordType) {
+ var self = Container.call(this);
+ self.lanes = lanes; // Array of lane numbers
+ self.chordType = chordType; // '2chord' or '3chord'
+ self.hasBeenHit = false;
+ self.speed = 8;
+ self.requiredPresses = lanes.length;
+ self.actualPresses = 0;
+ // Create note graphics for each lane
+ self.noteGraphics = [];
+ for (var i = 0; i < lanes.length; i++) {
+ var lane = lanes[i];
+ var noteGraphic;
+ if (chordType === '2chord') {
+ noteGraphic = self.attachAsset('chordNote2', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ } else {
+ noteGraphic = self.attachAsset('chordNote3', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ }
+ noteGraphic.x = lane * (2048 / 3) + 2048 / 6;
+ noteGraphic.y = 0;
+ self.noteGraphics.push(noteGraphic);
+ }
+ self.update = function () {
+ for (var i = 0; i < self.noteGraphics.length; i++) {
+ self.noteGraphics[i].y += self.speed;
+ }
+ // Update main container position based on first note
+ if (self.noteGraphics.length > 0) {
+ self.y = self.noteGraphics[0].y;
+ }
+ };
+ self.isInLane = function (lane) {
+ return self.lanes.indexOf(lane) !== -1;
+ };
+ return self;
+});
var Note = Container.expand(function (lane, type) {
var self = Container.call(this);
self.lane = lane;
self.type = type;
@@ -180,14 +284,17 @@
var startButton;
var gameOverButton;
// Game variables
var notes = [];
+var chordNotes = [];
var buttons = [];
var maxNotes = 4;
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
+var pressedButtons = []; // Track currently pressed buttons
+var chordPressTimer = 0; // Timer for chord press detection
// Create lane separators
var lane1 = game.addChild(LK.getAsset('lane', {
anchorX: 0.5,
anchorY: 0
@@ -360,8 +467,12 @@
for (var i = notes.length - 1; i >= 0; i--) {
notes[i].destroy();
notes.splice(i, 1);
}
+ for (var i = chordNotes.length - 1; i >= 0; i--) {
+ chordNotes[i].destroy();
+ chordNotes.splice(i, 1);
+ }
for (var i = buttons.length - 1; i >= 0; i--) {
buttons[i].destroy();
buttons.splice(i, 1);
}
@@ -428,15 +539,34 @@
}
}
// Spawn note function
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);
- notes.push(note);
- game.addChild(note);
+ if (notes.length + chordNotes.length >= maxNotes) return;
+ var spawnType = Math.random();
+ if (spawnType < 0.15) {
+ // 15% chance for 2-button chord
+ var lanes = [];
+ var lane1 = Math.floor(Math.random() * 3);
+ var lane2 = (lane1 + 1 + Math.floor(Math.random() * 2)) % 3;
+ lanes.push(lane1, lane2);
+ var chordNote = new ChordNote(lanes, '2chord');
+ chordNotes.push(chordNote);
+ game.addChild(chordNote);
+ } else if (spawnType < 0.25) {
+ // 10% chance for 3-button chord
+ var lanes = [0, 1, 2]; // All three lanes
+ var chordNote = new ChordNote(lanes, '3chord');
+ chordNotes.push(chordNote);
+ game.addChild(chordNote);
+ } else {
+ // 75% chance for single note
+ 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);
+ notes.push(note);
+ game.addChild(note);
+ }
}
// Handle clicks for start button
game.down = function (x, y, obj) {
if (gameState === 'menu') {
@@ -499,8 +629,60 @@
if (spawnInterval > 60) {
spawnInterval = Math.max(60, spawnInterval - 1);
}
}
+ // Update chord press timer
+ if (chordPressTimer > 0) {
+ chordPressTimer--;
+ } else {
+ pressedButtons = []; // Clear pressed buttons if timer expired
+ }
+ // Update and check chord notes
+ for (var i = chordNotes.length - 1; i >= 0; i--) {
+ var chordNote = chordNotes[i];
+ // Check if chord note reached bottom line (missed)
+ if (chordNote.y >= bottomLineY && !chordNote.hasBeenHit) {
+ LK.getSound('miss').play();
+ // Create red explosion effect for missed chord notes
+ var centerX = 0;
+ for (var k = 0; k < chordNote.lanes.length; k++) {
+ centerX += chordNote.lanes[k] * (2048 / 3) + 2048 / 6;
+ }
+ centerX /= chordNote.lanes.length;
+ createExplosion(centerX, chordNote.y, 0xFF0000);
+ // Show missed feedback text
+ feedbackTxt.setText('MISSED CHORD');
+ feedbackTxt.alpha = 1;
+ tween(feedbackTxt, {
+ alpha: 0
+ }, {
+ duration: 800
+ });
+ chordNote.destroy();
+ chordNotes.splice(i, 1);
+ continue;
+ }
+ // Remove chord notes that went off screen or were hit
+ if (chordNote.y > 2732 + 100 || chordNote.hasBeenHit) {
+ if (chordNote.hasBeenHit) {
+ // Small delay before removing hit chord notes for visual feedback
+ LK.setTimeout(function (chordNoteToRemove, index) {
+ return function () {
+ if (chordNoteToRemove && chordNoteToRemove.parent) {
+ chordNoteToRemove.destroy();
+ }
+ var chordNoteIndex = chordNotes.indexOf(chordNoteToRemove);
+ if (chordNoteIndex !== -1) {
+ chordNotes.splice(chordNoteIndex, 1);
+ }
+ };
+ }(chordNote, i), 200);
+ } else {
+ chordNote.destroy();
+ chordNotes.splice(i, 1);
+ }
+ }
+ }
// Update and check notes
for (var i = notes.length - 1; i >= 0; i--) {
var note = notes[i];
// Check if note reached bottom line (missed)