/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var ExplosionEffect = Container.expand(function () {
var self = Container.call(this);
var explosionGraphics = self.attachAsset('explosion', {
anchorX: 0.5,
anchorY: 0.5
});
self.explode = function () {
explosionGraphics.alpha = 0;
explosionGraphics.scaleX = 0.1;
explosionGraphics.scaleY = 0.1;
// Explosion animation
tween(explosionGraphics, {
scaleX: 1,
scaleY: 1,
alpha: 0.8
}, {
duration: 800,
easing: tween.easeOut,
onFinish: function onFinish() {
// Show "You Lost" text
var lostText = new Text2('YOU LOST!', {
size: 200,
fill: 0xFF0000
});
lostText.anchor.set(0.5, 0.5);
lostText.x = 2048 / 2;
lostText.y = 2732 / 2;
lostText.alpha = 0;
game.addChild(lostText);
tween(lostText, {
alpha: 1
}, {
duration: 500,
onFinish: function onFinish() {
LK.setTimeout(function () {
LK.showGameOver();
}, 1500);
}
});
}
});
};
return self;
});
var HitEffect = Container.expand(function () {
var self = Container.call(this);
var effectGraphics = self.attachAsset('hitEffect', {
anchorX: 0.5,
anchorY: 0.5
});
self.play = function () {
effectGraphics.alpha = 1;
effectGraphics.scaleX = 0.5;
effectGraphics.scaleY = 0.5;
tween(effectGraphics, {
scaleX: 1.5,
scaleY: 1.5,
alpha: 0
}, {
duration: 400,
easing: tween.easeOut,
onFinish: function onFinish() {
self.destroy();
}
});
};
return self;
});
var Note = Container.expand(function () {
var self = Container.call(this);
var noteGraphics = self.attachAsset('note', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 8;
self.lane = 0;
self.hitProcessed = false;
self.update = function () {
self.y += self.speed;
};
return self;
});
var OpponentCan = Container.expand(function () {
var self = Container.call(this);
var canGraphics = self.attachAsset('opponentCan', {
anchorX: 0.5,
anchorY: 0.5
});
self.isAnimating = false;
self.sing = function () {
if (self.isAnimating) return;
self.isAnimating = true;
tween(canGraphics, {
scaleY: 1.1,
scaleX: 1.1
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(canGraphics, {
scaleY: 1,
scaleX: 1
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
self.isAnimating = false;
}
});
}
});
};
return self;
});
var SodaCan = Container.expand(function () {
var self = Container.call(this);
var canGraphics = self.attachAsset('sodaCan', {
anchorX: 0.5,
anchorY: 0.5
});
self.isAnimating = false;
self.celebrate = function () {
if (self.isAnimating) return;
self.isAnimating = true;
tween(canGraphics, {
scaleY: 1.2,
scaleX: 0.9
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(canGraphics, {
scaleY: 1,
scaleX: 1
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
self.isAnimating = false;
}
});
}
});
};
self.miss = function () {
if (self.isAnimating) return;
self.isAnimating = true;
tween(canGraphics, {
rotation: -0.3
}, {
duration: 150,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(canGraphics, {
rotation: 0.3
}, {
duration: 150,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(canGraphics, {
rotation: 0
}, {
duration: 150,
easing: tween.easeOut,
onFinish: function onFinish() {
self.isAnimating = false;
}
});
}
});
}
});
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x2c3e50
});
/****
* Game Code
****/
// Game state
var gameState = 'playing'; // 'playing', 'gameover'
var combo = 0;
var maxCombo = 0;
var accuracy = 100;
var totalNotes = 0;
var hitNotes = 0;
var missCount = 0;
var maxMisses = 3;
// Game objects
var playerCan;
var opponentCan;
var notes = [];
var targetZones = [];
var beatPattern = [0, 1, 2, 1, 0, 2, 1, 0]; // Note lanes pattern
var patternIndex = 0;
var noteSpawnTimer = 0;
var noteSpawnInterval = 45; // Spawn note every 45 ticks (0.75 seconds at 60fps)
// Target zones positions
var lanePositions = [2048 / 4,
// Lane 0
2048 / 2,
// Lane 1
3 * 2048 / 4 // Lane 2
];
// UI Elements
var comboText = new Text2('Combo: 0', {
size: 120,
fill: 0xFFFFFF
});
comboText.anchor.set(0.5, 0);
LK.gui.top.addChild(comboText);
var accuracyText = new Text2('Accuracy: 100%', {
size: 100,
fill: 0xFFFFFF
});
accuracyText.anchor.set(1, 0);
LK.gui.topRight.addChild(accuracyText);
// Add skibidi toilet background
var background = LK.getAsset('skibidiToilet', {
anchorX: 0,
anchorY: 0
});
background.x = 0;
background.y = 0;
background.alpha = 0.7;
game.addChild(background);
// Initialize characters
playerCan = game.addChild(new SodaCan());
playerCan.x = 2048 / 2;
playerCan.y = 2200;
opponentCan = game.addChild(new OpponentCan());
opponentCan.x = 2048 / 2;
opponentCan.y = 400;
// Create target zones
for (var i = 0; i < 3; i++) {
var targetZone = LK.getAsset('targetZone', {
anchorX: 0.5,
anchorY: 0.5
});
targetZone.x = lanePositions[i];
targetZone.y = 2000;
targetZone.alpha = 0.7;
targetZones.push(targetZone);
game.addChild(targetZone);
}
// Input handling
game.down = function (x, y, obj) {
if (gameState !== 'playing') return;
// Determine which lane was tapped
var tappedLane = -1;
var minDistance = Infinity;
for (var i = 0; i < lanePositions.length; i++) {
var distance = Math.abs(x - lanePositions[i]);
if (distance < minDistance && distance < 600) {
minDistance = distance;
tappedLane = i;
}
}
if (tappedLane === -1) return;
// Check for notes in this lane
var hitNote = null;
var bestDistance = Infinity;
for (var j = 0; j < notes.length; j++) {
var note = notes[j];
if (note.lane === tappedLane && !note.hitProcessed) {
var noteDistance = Math.abs(note.y - 2000);
if (noteDistance < 600 && noteDistance < bestDistance) {
bestDistance = noteDistance;
hitNote = note;
}
}
}
if (hitNote) {
// Check if tap is within both the note and target zone bounds
var targetZone = targetZones[tappedLane];
var noteInTargetZone = Math.abs(hitNote.y - targetZone.y) < 240; // Note is close to target zone vertically
var tapInTargetZone = Math.abs(x - targetZone.x) < 240 && Math.abs(y - targetZone.y) < 200; // Tap is within target zone bounds
var tapInNote = Math.abs(x - hitNote.x) < 200 && Math.abs(y - hitNote.y) < 200; // Tap is within note bounds
if (noteInTargetZone && tapInTargetZone && tapInNote) {
// Great hit! Yellow note hit on blue target zone
hitNote.hitProcessed = true;
combo++;
hitNotes++;
LK.setScore(LK.getScore() + 10 * combo);
// Create hit effect
var effect = game.addChild(new HitEffect());
effect.x = lanePositions[tappedLane];
effect.y = 2000;
effect.play();
// Player animation
playerCan.celebrate();
// Play hit sound
LK.getSound('hit').play();
// Update combo text
comboText.setText('Combo: ' + combo);
if (combo > maxCombo) {
maxCombo = combo;
}
// Show "Great" feedback
var greatText = new Text2('GREAT!', {
size: 160,
fill: 0x00ff00
});
greatText.anchor.set(0.5, 0.5);
greatText.x = lanePositions[tappedLane];
greatText.y = 1800;
game.addChild(greatText);
tween(greatText, {
y: 1600,
alpha: 0
}, {
duration: 800,
onFinish: function onFinish() {
greatText.destroy();
}
});
// Make soda bottles disappear when getting Great
tween(playerCan, {
alpha: 0
}, {
duration: 200,
onFinish: function onFinish() {
tween(playerCan, {
alpha: 1
}, {
duration: 200
});
}
});
tween(opponentCan, {
alpha: 0
}, {
duration: 200,
onFinish: function onFinish() {
tween(opponentCan, {
alpha: 1
}, {
duration: 200
});
}
});
} else {
// Bad hit! Hitting elsewhere
hitNote.hitProcessed = true;
combo = 0;
missCount++;
playerCan.miss();
LK.getSound('miss').play();
comboText.setText('Combo: 0');
// Check if player has missed 3 times
if (missCount >= maxMisses) {
gameState = 'gameover';
var explosion = game.addChild(new ExplosionEffect());
explosion.x = playerCan.x;
explosion.y = playerCan.y;
explosion.explode();
return;
}
// Show "Bad" feedback
var badText = new Text2('BAD!', {
size: 160,
fill: 0xff0000
});
badText.anchor.set(0.5, 0.5);
badText.x = lanePositions[tappedLane];
badText.y = 1800;
game.addChild(badText);
tween(badText, {
y: 1600,
alpha: 0
}, {
duration: 800,
onFinish: function onFinish() {
badText.destroy();
}
});
}
} else {
// Miss!
combo = 0;
playerCan.miss();
LK.getSound('miss').play();
comboText.setText('Combo: 0');
}
// Update accuracy
if (totalNotes > 0) {
accuracy = Math.round(hitNotes / totalNotes * 100);
accuracyText.setText('Accuracy: ' + accuracy + '%');
}
};
// Main game update
game.update = function () {
if (gameState !== 'playing') return;
// Spawn notes based on pattern
noteSpawnTimer++;
if (noteSpawnTimer >= noteSpawnInterval) {
noteSpawnTimer = 0;
var newNote = new Note();
newNote.lane = beatPattern[patternIndex];
newNote.x = lanePositions[newNote.lane];
newNote.y = -50;
notes.push(newNote);
game.addChild(newNote);
totalNotes++;
patternIndex = (patternIndex + 1) % beatPattern.length;
// Opponent animation on note spawn
if (LK.ticks % 90 === 0) {
// Every 1.5 seconds
opponentCan.sing();
}
}
// Update notes
for (var i = notes.length - 1; i >= 0; i--) {
var note = notes[i];
// Remove notes that went off screen
if (note.y > 2732 + 50) {
if (!note.hitProcessed) {
// Missed note
combo = 0;
comboText.setText('Combo: 0');
// Update accuracy
if (totalNotes > 0) {
accuracy = Math.round(hitNotes / totalNotes * 100);
accuracyText.setText('Accuracy: ' + accuracy + '%');
}
}
note.destroy();
notes.splice(i, 1);
continue;
}
// Auto-miss notes that are too far past the target
if (!note.hitProcessed && note.y > 2400) {
note.hitProcessed = true;
combo = 0;
playerCan.miss();
comboText.setText('Combo: 0');
// Update accuracy
if (totalNotes > 0) {
accuracy = Math.round(hitNotes / totalNotes * 100);
accuracyText.setText('Accuracy: ' + accuracy + '%');
}
}
}
// Check win condition
if (LK.getScore() >= 1000) {
gameState = 'gameover';
LK.showYouWin();
}
// Check lose condition (very low accuracy after many notes)
if (totalNotes > 20 && accuracy < 30) {
gameState = 'gameover';
LK.showGameOver();
}
};
// Start background music with looping
LK.playMusic('bgtrack', {
loop: true
}); /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var ExplosionEffect = Container.expand(function () {
var self = Container.call(this);
var explosionGraphics = self.attachAsset('explosion', {
anchorX: 0.5,
anchorY: 0.5
});
self.explode = function () {
explosionGraphics.alpha = 0;
explosionGraphics.scaleX = 0.1;
explosionGraphics.scaleY = 0.1;
// Explosion animation
tween(explosionGraphics, {
scaleX: 1,
scaleY: 1,
alpha: 0.8
}, {
duration: 800,
easing: tween.easeOut,
onFinish: function onFinish() {
// Show "You Lost" text
var lostText = new Text2('YOU LOST!', {
size: 200,
fill: 0xFF0000
});
lostText.anchor.set(0.5, 0.5);
lostText.x = 2048 / 2;
lostText.y = 2732 / 2;
lostText.alpha = 0;
game.addChild(lostText);
tween(lostText, {
alpha: 1
}, {
duration: 500,
onFinish: function onFinish() {
LK.setTimeout(function () {
LK.showGameOver();
}, 1500);
}
});
}
});
};
return self;
});
var HitEffect = Container.expand(function () {
var self = Container.call(this);
var effectGraphics = self.attachAsset('hitEffect', {
anchorX: 0.5,
anchorY: 0.5
});
self.play = function () {
effectGraphics.alpha = 1;
effectGraphics.scaleX = 0.5;
effectGraphics.scaleY = 0.5;
tween(effectGraphics, {
scaleX: 1.5,
scaleY: 1.5,
alpha: 0
}, {
duration: 400,
easing: tween.easeOut,
onFinish: function onFinish() {
self.destroy();
}
});
};
return self;
});
var Note = Container.expand(function () {
var self = Container.call(this);
var noteGraphics = self.attachAsset('note', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 8;
self.lane = 0;
self.hitProcessed = false;
self.update = function () {
self.y += self.speed;
};
return self;
});
var OpponentCan = Container.expand(function () {
var self = Container.call(this);
var canGraphics = self.attachAsset('opponentCan', {
anchorX: 0.5,
anchorY: 0.5
});
self.isAnimating = false;
self.sing = function () {
if (self.isAnimating) return;
self.isAnimating = true;
tween(canGraphics, {
scaleY: 1.1,
scaleX: 1.1
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(canGraphics, {
scaleY: 1,
scaleX: 1
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
self.isAnimating = false;
}
});
}
});
};
return self;
});
var SodaCan = Container.expand(function () {
var self = Container.call(this);
var canGraphics = self.attachAsset('sodaCan', {
anchorX: 0.5,
anchorY: 0.5
});
self.isAnimating = false;
self.celebrate = function () {
if (self.isAnimating) return;
self.isAnimating = true;
tween(canGraphics, {
scaleY: 1.2,
scaleX: 0.9
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(canGraphics, {
scaleY: 1,
scaleX: 1
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
self.isAnimating = false;
}
});
}
});
};
self.miss = function () {
if (self.isAnimating) return;
self.isAnimating = true;
tween(canGraphics, {
rotation: -0.3
}, {
duration: 150,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(canGraphics, {
rotation: 0.3
}, {
duration: 150,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(canGraphics, {
rotation: 0
}, {
duration: 150,
easing: tween.easeOut,
onFinish: function onFinish() {
self.isAnimating = false;
}
});
}
});
}
});
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x2c3e50
});
/****
* Game Code
****/
// Game state
var gameState = 'playing'; // 'playing', 'gameover'
var combo = 0;
var maxCombo = 0;
var accuracy = 100;
var totalNotes = 0;
var hitNotes = 0;
var missCount = 0;
var maxMisses = 3;
// Game objects
var playerCan;
var opponentCan;
var notes = [];
var targetZones = [];
var beatPattern = [0, 1, 2, 1, 0, 2, 1, 0]; // Note lanes pattern
var patternIndex = 0;
var noteSpawnTimer = 0;
var noteSpawnInterval = 45; // Spawn note every 45 ticks (0.75 seconds at 60fps)
// Target zones positions
var lanePositions = [2048 / 4,
// Lane 0
2048 / 2,
// Lane 1
3 * 2048 / 4 // Lane 2
];
// UI Elements
var comboText = new Text2('Combo: 0', {
size: 120,
fill: 0xFFFFFF
});
comboText.anchor.set(0.5, 0);
LK.gui.top.addChild(comboText);
var accuracyText = new Text2('Accuracy: 100%', {
size: 100,
fill: 0xFFFFFF
});
accuracyText.anchor.set(1, 0);
LK.gui.topRight.addChild(accuracyText);
// Add skibidi toilet background
var background = LK.getAsset('skibidiToilet', {
anchorX: 0,
anchorY: 0
});
background.x = 0;
background.y = 0;
background.alpha = 0.7;
game.addChild(background);
// Initialize characters
playerCan = game.addChild(new SodaCan());
playerCan.x = 2048 / 2;
playerCan.y = 2200;
opponentCan = game.addChild(new OpponentCan());
opponentCan.x = 2048 / 2;
opponentCan.y = 400;
// Create target zones
for (var i = 0; i < 3; i++) {
var targetZone = LK.getAsset('targetZone', {
anchorX: 0.5,
anchorY: 0.5
});
targetZone.x = lanePositions[i];
targetZone.y = 2000;
targetZone.alpha = 0.7;
targetZones.push(targetZone);
game.addChild(targetZone);
}
// Input handling
game.down = function (x, y, obj) {
if (gameState !== 'playing') return;
// Determine which lane was tapped
var tappedLane = -1;
var minDistance = Infinity;
for (var i = 0; i < lanePositions.length; i++) {
var distance = Math.abs(x - lanePositions[i]);
if (distance < minDistance && distance < 600) {
minDistance = distance;
tappedLane = i;
}
}
if (tappedLane === -1) return;
// Check for notes in this lane
var hitNote = null;
var bestDistance = Infinity;
for (var j = 0; j < notes.length; j++) {
var note = notes[j];
if (note.lane === tappedLane && !note.hitProcessed) {
var noteDistance = Math.abs(note.y - 2000);
if (noteDistance < 600 && noteDistance < bestDistance) {
bestDistance = noteDistance;
hitNote = note;
}
}
}
if (hitNote) {
// Check if tap is within both the note and target zone bounds
var targetZone = targetZones[tappedLane];
var noteInTargetZone = Math.abs(hitNote.y - targetZone.y) < 240; // Note is close to target zone vertically
var tapInTargetZone = Math.abs(x - targetZone.x) < 240 && Math.abs(y - targetZone.y) < 200; // Tap is within target zone bounds
var tapInNote = Math.abs(x - hitNote.x) < 200 && Math.abs(y - hitNote.y) < 200; // Tap is within note bounds
if (noteInTargetZone && tapInTargetZone && tapInNote) {
// Great hit! Yellow note hit on blue target zone
hitNote.hitProcessed = true;
combo++;
hitNotes++;
LK.setScore(LK.getScore() + 10 * combo);
// Create hit effect
var effect = game.addChild(new HitEffect());
effect.x = lanePositions[tappedLane];
effect.y = 2000;
effect.play();
// Player animation
playerCan.celebrate();
// Play hit sound
LK.getSound('hit').play();
// Update combo text
comboText.setText('Combo: ' + combo);
if (combo > maxCombo) {
maxCombo = combo;
}
// Show "Great" feedback
var greatText = new Text2('GREAT!', {
size: 160,
fill: 0x00ff00
});
greatText.anchor.set(0.5, 0.5);
greatText.x = lanePositions[tappedLane];
greatText.y = 1800;
game.addChild(greatText);
tween(greatText, {
y: 1600,
alpha: 0
}, {
duration: 800,
onFinish: function onFinish() {
greatText.destroy();
}
});
// Make soda bottles disappear when getting Great
tween(playerCan, {
alpha: 0
}, {
duration: 200,
onFinish: function onFinish() {
tween(playerCan, {
alpha: 1
}, {
duration: 200
});
}
});
tween(opponentCan, {
alpha: 0
}, {
duration: 200,
onFinish: function onFinish() {
tween(opponentCan, {
alpha: 1
}, {
duration: 200
});
}
});
} else {
// Bad hit! Hitting elsewhere
hitNote.hitProcessed = true;
combo = 0;
missCount++;
playerCan.miss();
LK.getSound('miss').play();
comboText.setText('Combo: 0');
// Check if player has missed 3 times
if (missCount >= maxMisses) {
gameState = 'gameover';
var explosion = game.addChild(new ExplosionEffect());
explosion.x = playerCan.x;
explosion.y = playerCan.y;
explosion.explode();
return;
}
// Show "Bad" feedback
var badText = new Text2('BAD!', {
size: 160,
fill: 0xff0000
});
badText.anchor.set(0.5, 0.5);
badText.x = lanePositions[tappedLane];
badText.y = 1800;
game.addChild(badText);
tween(badText, {
y: 1600,
alpha: 0
}, {
duration: 800,
onFinish: function onFinish() {
badText.destroy();
}
});
}
} else {
// Miss!
combo = 0;
playerCan.miss();
LK.getSound('miss').play();
comboText.setText('Combo: 0');
}
// Update accuracy
if (totalNotes > 0) {
accuracy = Math.round(hitNotes / totalNotes * 100);
accuracyText.setText('Accuracy: ' + accuracy + '%');
}
};
// Main game update
game.update = function () {
if (gameState !== 'playing') return;
// Spawn notes based on pattern
noteSpawnTimer++;
if (noteSpawnTimer >= noteSpawnInterval) {
noteSpawnTimer = 0;
var newNote = new Note();
newNote.lane = beatPattern[patternIndex];
newNote.x = lanePositions[newNote.lane];
newNote.y = -50;
notes.push(newNote);
game.addChild(newNote);
totalNotes++;
patternIndex = (patternIndex + 1) % beatPattern.length;
// Opponent animation on note spawn
if (LK.ticks % 90 === 0) {
// Every 1.5 seconds
opponentCan.sing();
}
}
// Update notes
for (var i = notes.length - 1; i >= 0; i--) {
var note = notes[i];
// Remove notes that went off screen
if (note.y > 2732 + 50) {
if (!note.hitProcessed) {
// Missed note
combo = 0;
comboText.setText('Combo: 0');
// Update accuracy
if (totalNotes > 0) {
accuracy = Math.round(hitNotes / totalNotes * 100);
accuracyText.setText('Accuracy: ' + accuracy + '%');
}
}
note.destroy();
notes.splice(i, 1);
continue;
}
// Auto-miss notes that are too far past the target
if (!note.hitProcessed && note.y > 2400) {
note.hitProcessed = true;
combo = 0;
playerCan.miss();
comboText.setText('Combo: 0');
// Update accuracy
if (totalNotes > 0) {
accuracy = Math.round(hitNotes / totalNotes * 100);
accuracyText.setText('Accuracy: ' + accuracy + '%');
}
}
}
// Check win condition
if (LK.getScore() >= 1000) {
gameState = 'gameover';
LK.showYouWin();
}
// Check lose condition (very low accuracy after many notes)
if (totalNotes > 20 && accuracy < 30) {
gameState = 'gameover';
LK.showGameOver();
}
};
// Start background music with looping
LK.playMusic('bgtrack', {
loop: true
});