/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ var ColorMeter = Container.expand(function () { var self = Container.call(this); // Create meter background self.background = self.attachAsset('colorMeter', { anchorX: 0.5, anchorY: 1 }); // Create meter fill self.fill = self.attachAsset('colorMeterFill', { anchorX: 0.5, anchorY: 1, y: 5 }); // Set initial meter level self.level = 0; self.maxLevel = 100; // Update fill height based on level self.updateFill = function () { var fillHeight = self.level / self.maxLevel * self.background.height; self.fill.height = Math.max(0, fillHeight - 10); // Account for padding }; self.updateFill(); // Add points to meter self.addPoints = function (points) { self.level = Math.min(self.maxLevel, self.level + points); self.updateFill(); // Return true if meter is full return self.level >= self.maxLevel; }; // Reset meter self.reset = function () { self.level = 0; self.updateFill(); }; // Update fill height based on level self.updateFill = function () { var fillHeight = self.level / self.maxLevel * self.background.height; self.fill.height = Math.max(0, fillHeight - 10); // Account for padding }; return self; }); var ColorWheel = Container.expand(function () { var self = Container.call(this); // Color constants self.COLORS = [{ name: 'red', hex: 0xFF0000 }, { name: 'orange', hex: 0xFF8800 }, { name: 'yellow', hex: 0xFFFF00 }, { name: 'green', hex: 0x00FF00 }, { name: 'blue', hex: 0x0088FF }, { name: 'purple', hex: 0xAA00FF }]; // Create color segments self.segments = []; var segmentCount = self.COLORS.length; for (var i = 0; i < segmentCount; i++) { var segment = self.attachAsset('wheelSegment', { anchorX: 0.5, anchorY: 0, tint: self.COLORS[i].hex }); // Position segments in a circle var angle = i / segmentCount * Math.PI * 2; segment.rotation = angle; // Store segments with their color info segment.colorIndex = i; segment.colorName = self.COLORS[i].name; segment.colorHex = self.COLORS[i].hex; self.segments.push(segment); } // Add center piece self.center = self.attachAsset('wheelCenter', { anchorX: 0.5, anchorY: 0.5 }); // Wheel properties self.targetRotation = 0; self.rotationSpeed = 0; self.currentRotation = 0; self.decelerationRate = 0.92; self.isRotating = false; // Rotate wheel to specific segment self.rotateToSegment = function (segmentIndex) { var targetAngle = segmentIndex / segmentCount * Math.PI * 2; self.targetRotation = targetAngle; self.isRotating = true; }; // Rotate wheel by amount (in radians) self.rotate = function (amount) { self.rotationSpeed = amount; self.isRotating = true; LK.getSound('rotate').play(); }; // Get current top segment (for matching with drops) self.getTopSegment = function () { // The segment facing up (PI/2 or 90 degrees from wheel rotation) var topAngle = self.rotation + Math.PI / 2; // Normalize to positive angle while (topAngle < 0) { topAngle += Math.PI * 2; } // Find segment index var segmentSize = Math.PI * 2 / segmentCount; var index = Math.floor(topAngle % (Math.PI * 2) / segmentSize); return index % segmentCount; }; self.update = function () { if (self.isRotating) { // Apply rotation speed self.rotation += self.rotationSpeed; // Decelerate rotation self.rotationSpeed *= self.decelerationRate; // Stop rotating when speed becomes negligible if (Math.abs(self.rotationSpeed) < 0.001) { self.rotationSpeed = 0; self.isRotating = false; } } }; return self; }); var PaintDrop = Container.expand(function (colorIndex, colorName, colorHex, speed, special) { var self = Container.call(this); // Store drop properties self.colorIndex = colorIndex; self.colorName = colorName; self.colorHex = colorHex; self.speed = speed || 5; self.special = special || false; // Create drop graphics var dropGraphics = self.attachAsset('paintDrop', { anchorX: 0.5, anchorY: 0.5, tint: colorHex }); // For special drops, add visual effects if (special) { dropGraphics.alpha = 0.8; // Pulsate effect for special drops self.pulseDirection = 1; self.pulseSpeed = 0.03; } self.update = function () { // Move drop down self.y += self.speed; // Special drop effects if (self.special) { // Pulsating effect if (dropGraphics.scale.x > 1.2) { self.pulseDirection = -1; } else if (dropGraphics.scale.x < 0.8) { self.pulseDirection = 1; } dropGraphics.scale.x += self.pulseDirection * self.pulseSpeed; dropGraphics.scale.y = dropGraphics.scale.x; } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x111122 }); /**** * Game Code ****/ // Game variables var score = 0; var level = 1; var drops = []; var comboCount = 0; var dropSpeed = 5; var dropInterval = 60; // Frames between drops var dropCounter = 0; var isGameOver = false; // Create UI elements var scoreTxt = new Text2('Score: 0', { size: 70, fill: 0xFFFFFF }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); scoreTxt.y += 20; var levelTxt = new Text2('Level: 1', { size: 60, fill: 0xFFFFFF }); levelTxt.anchor.set(0.5, 0); LK.gui.top.addChild(levelTxt); levelTxt.y += 100; var comboTxt = new Text2('', { size: 80, fill: 0xFFDD00 }); comboTxt.anchor.set(0.5, 0.5); LK.gui.center.addChild(comboTxt); comboTxt.visible = false; // Create color meter var colorMeter = new ColorMeter(); colorMeter.x = 100; colorMeter.y = 2732 / 2; game.addChild(colorMeter); // Create the color wheel var wheel = new ColorWheel(); wheel.x = 2048 / 2; wheel.y = 2732 - 600; game.addChild(wheel); // Initialize gameplay function resetGame() { score = 0; level = 1; comboCount = 0; dropSpeed = 5; dropInterval = 60; drops = []; isGameOver = false; // Reset UI updateScoreDisplay(); updateLevelDisplay(); updateComboDisplay(); // Reset color meter colorMeter.reset(); // Play background music LK.playMusic('bgMusic', { fade: { start: 0, end: 0.5, duration: 1000 } }); } // Start new game resetGame(); // Update score display function updateScoreDisplay() { scoreTxt.setText('Score: ' + score); LK.setScore(score); } // Update level display function updateLevelDisplay() { levelTxt.setText('Level: ' + level); } // Update combo display function updateComboDisplay() { if (comboCount > 1) { comboTxt.setText('COMBO x' + comboCount + '!'); comboTxt.visible = true; // Animate combo text comboTxt.scale.set(1.5); tween(comboTxt, { scaleX: 1, scaleY: 1 }, { duration: 500, easing: tween.elasticOut }); // Hide after a delay LK.setTimeout(function () { comboTxt.visible = false; }, 1500); } else { comboTxt.visible = false; } } // Create a new paint drop function createDrop() { // Select random color from wheel's color array var randomIndex = Math.floor(Math.random() * wheel.COLORS.length); var color = wheel.COLORS[randomIndex]; // Determine if this is a special drop (1 in 10 chance) var isSpecial = Math.random() < 0.1; // Create drop var drop = new PaintDrop(randomIndex, color.name, color.hex, dropSpeed + Math.random() * 2, // Slight speed variation isSpecial); // Position drop at random horizontal position drop.x = 2048 / 2; // Center the drop horizontally drop.y = -100; // Start above screen // Add to game and track game.addChild(drop); drops.push(drop); } // Handle a match between drop and wheel function handleMatch(drop, index) { // Increase score based on combo var pointsGained = 10 * (comboCount + 1); score += pointsGained; updateScoreDisplay(); // Increase combo comboCount++; updateComboDisplay(); // Add to color meter var meterFull = colorMeter.addPoints(pointsGained); // Play match sound if (comboCount > 1) { LK.getSound('combo').play(); } else { LK.getSound('match').play(); } // Visual effects for match LK.effects.flashObject(wheel.segments[drop.colorIndex], 0xFFFFFF, 300); // If meter is full, level up if (meterFull) { levelUp(); } // Remove drop drop.destroy(); drops.splice(index, 1); } // Handle a mismatch between drop and wheel function handleMiss(drop, index) { // Reset combo comboCount = 0; // Play miss sound LK.getSound('miss').play(); // Visual effects for miss LK.effects.flashObject(wheel, 0xFF0000, 300); // Remove drop drop.destroy(); drops.splice(index, 1); // Penalty - decrease score score = Math.max(0, score - 5); updateScoreDisplay(); } // Level up function function levelUp() { level++; updateLevelDisplay(); // Reset color meter colorMeter.reset(); // Increase difficulty dropSpeed += 0.5; dropInterval = Math.max(20, dropInterval - 5); // Special effects for level up LK.effects.flashScreen(0xFFFFFF, 500); // Show level message var levelUpTxt = new Text2('LEVEL ' + level + '!', { size: 120, fill: 0xFFFFFF }); levelUpTxt.anchor.set(0.5, 0.5); LK.gui.center.addChild(levelUpTxt); // Animate and remove level message tween(levelUpTxt, { scaleX: 1.5, scaleY: 1.5, alpha: 0 }, { duration: 1500, easing: tween.easeOut, onFinish: function onFinish() { levelUpTxt.destroy(); } }); } // Handle wheel rotation from touch/mouse var startDragX = 0; var isDragging = false; game.down = function (x, y, obj) { startDragX = x; isDragging = true; }; game.move = function (x, y, obj) { if (isDragging) { // Calculate drag distance from center of wheel var wheelCenterX = wheel.x; var wheelCenterY = wheel.y; // Calculate angle change based on drag direction and distance from center var prevAngle = Math.atan2(startDragX - wheelCenterX, y - wheelCenterY); var newAngle = Math.atan2(x - wheelCenterX, y - wheelCenterY); var angleChange = newAngle - prevAngle; // Apply rotation to wheel wheel.rotate(angleChange * 0.8); // Scale for better feel // Update start position for next move startDragX = x; } }; game.up = function (x, y, obj) { isDragging = false; }; // Main game update loop game.update = function () { if (isGameOver) { return; } // Create a new drop only if there are no active drops if (drops.length === 0) { createDrop(); } // Update all drops and check for matches or misses for (var i = drops.length - 1; i >= 0; i--) { var drop = drops[i]; // Check if drop reaches wheel if (drop.y >= wheel.y - 100 && drop.y <= wheel.y) { // Get the wheel's top segment var topSegmentIndex = wheel.getTopSegment(); // Check if colors match if (drop.colorIndex === topSegmentIndex) { handleMatch(drop, i); score += 1; // Award a point for a successful match updateScoreDisplay(); // Update the score display } else { handleMiss(drop, i); } } // Check if drop goes off screen (missed completely) else if (drop.y > 2732 + 100) { handleMiss(drop, i); // Game over if too many drops are missed (3 misses) if (comboCount <= 0) { isGameOver = true; LK.effects.flashScreen(0xFF0000, 1000); LK.showGameOver(); LK.stopMusic(); break; } } } };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var ColorMeter = Container.expand(function () {
var self = Container.call(this);
// Create meter background
self.background = self.attachAsset('colorMeter', {
anchorX: 0.5,
anchorY: 1
});
// Create meter fill
self.fill = self.attachAsset('colorMeterFill', {
anchorX: 0.5,
anchorY: 1,
y: 5
});
// Set initial meter level
self.level = 0;
self.maxLevel = 100;
// Update fill height based on level
self.updateFill = function () {
var fillHeight = self.level / self.maxLevel * self.background.height;
self.fill.height = Math.max(0, fillHeight - 10); // Account for padding
};
self.updateFill();
// Add points to meter
self.addPoints = function (points) {
self.level = Math.min(self.maxLevel, self.level + points);
self.updateFill();
// Return true if meter is full
return self.level >= self.maxLevel;
};
// Reset meter
self.reset = function () {
self.level = 0;
self.updateFill();
};
// Update fill height based on level
self.updateFill = function () {
var fillHeight = self.level / self.maxLevel * self.background.height;
self.fill.height = Math.max(0, fillHeight - 10); // Account for padding
};
return self;
});
var ColorWheel = Container.expand(function () {
var self = Container.call(this);
// Color constants
self.COLORS = [{
name: 'red',
hex: 0xFF0000
}, {
name: 'orange',
hex: 0xFF8800
}, {
name: 'yellow',
hex: 0xFFFF00
}, {
name: 'green',
hex: 0x00FF00
}, {
name: 'blue',
hex: 0x0088FF
}, {
name: 'purple',
hex: 0xAA00FF
}];
// Create color segments
self.segments = [];
var segmentCount = self.COLORS.length;
for (var i = 0; i < segmentCount; i++) {
var segment = self.attachAsset('wheelSegment', {
anchorX: 0.5,
anchorY: 0,
tint: self.COLORS[i].hex
});
// Position segments in a circle
var angle = i / segmentCount * Math.PI * 2;
segment.rotation = angle;
// Store segments with their color info
segment.colorIndex = i;
segment.colorName = self.COLORS[i].name;
segment.colorHex = self.COLORS[i].hex;
self.segments.push(segment);
}
// Add center piece
self.center = self.attachAsset('wheelCenter', {
anchorX: 0.5,
anchorY: 0.5
});
// Wheel properties
self.targetRotation = 0;
self.rotationSpeed = 0;
self.currentRotation = 0;
self.decelerationRate = 0.92;
self.isRotating = false;
// Rotate wheel to specific segment
self.rotateToSegment = function (segmentIndex) {
var targetAngle = segmentIndex / segmentCount * Math.PI * 2;
self.targetRotation = targetAngle;
self.isRotating = true;
};
// Rotate wheel by amount (in radians)
self.rotate = function (amount) {
self.rotationSpeed = amount;
self.isRotating = true;
LK.getSound('rotate').play();
};
// Get current top segment (for matching with drops)
self.getTopSegment = function () {
// The segment facing up (PI/2 or 90 degrees from wheel rotation)
var topAngle = self.rotation + Math.PI / 2;
// Normalize to positive angle
while (topAngle < 0) {
topAngle += Math.PI * 2;
}
// Find segment index
var segmentSize = Math.PI * 2 / segmentCount;
var index = Math.floor(topAngle % (Math.PI * 2) / segmentSize);
return index % segmentCount;
};
self.update = function () {
if (self.isRotating) {
// Apply rotation speed
self.rotation += self.rotationSpeed;
// Decelerate rotation
self.rotationSpeed *= self.decelerationRate;
// Stop rotating when speed becomes negligible
if (Math.abs(self.rotationSpeed) < 0.001) {
self.rotationSpeed = 0;
self.isRotating = false;
}
}
};
return self;
});
var PaintDrop = Container.expand(function (colorIndex, colorName, colorHex, speed, special) {
var self = Container.call(this);
// Store drop properties
self.colorIndex = colorIndex;
self.colorName = colorName;
self.colorHex = colorHex;
self.speed = speed || 5;
self.special = special || false;
// Create drop graphics
var dropGraphics = self.attachAsset('paintDrop', {
anchorX: 0.5,
anchorY: 0.5,
tint: colorHex
});
// For special drops, add visual effects
if (special) {
dropGraphics.alpha = 0.8;
// Pulsate effect for special drops
self.pulseDirection = 1;
self.pulseSpeed = 0.03;
}
self.update = function () {
// Move drop down
self.y += self.speed;
// Special drop effects
if (self.special) {
// Pulsating effect
if (dropGraphics.scale.x > 1.2) {
self.pulseDirection = -1;
} else if (dropGraphics.scale.x < 0.8) {
self.pulseDirection = 1;
}
dropGraphics.scale.x += self.pulseDirection * self.pulseSpeed;
dropGraphics.scale.y = dropGraphics.scale.x;
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x111122
});
/****
* Game Code
****/
// Game variables
var score = 0;
var level = 1;
var drops = [];
var comboCount = 0;
var dropSpeed = 5;
var dropInterval = 60; // Frames between drops
var dropCounter = 0;
var isGameOver = false;
// Create UI elements
var scoreTxt = new Text2('Score: 0', {
size: 70,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
scoreTxt.y += 20;
var levelTxt = new Text2('Level: 1', {
size: 60,
fill: 0xFFFFFF
});
levelTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(levelTxt);
levelTxt.y += 100;
var comboTxt = new Text2('', {
size: 80,
fill: 0xFFDD00
});
comboTxt.anchor.set(0.5, 0.5);
LK.gui.center.addChild(comboTxt);
comboTxt.visible = false;
// Create color meter
var colorMeter = new ColorMeter();
colorMeter.x = 100;
colorMeter.y = 2732 / 2;
game.addChild(colorMeter);
// Create the color wheel
var wheel = new ColorWheel();
wheel.x = 2048 / 2;
wheel.y = 2732 - 600;
game.addChild(wheel);
// Initialize gameplay
function resetGame() {
score = 0;
level = 1;
comboCount = 0;
dropSpeed = 5;
dropInterval = 60;
drops = [];
isGameOver = false;
// Reset UI
updateScoreDisplay();
updateLevelDisplay();
updateComboDisplay();
// Reset color meter
colorMeter.reset();
// Play background music
LK.playMusic('bgMusic', {
fade: {
start: 0,
end: 0.5,
duration: 1000
}
});
}
// Start new game
resetGame();
// Update score display
function updateScoreDisplay() {
scoreTxt.setText('Score: ' + score);
LK.setScore(score);
}
// Update level display
function updateLevelDisplay() {
levelTxt.setText('Level: ' + level);
}
// Update combo display
function updateComboDisplay() {
if (comboCount > 1) {
comboTxt.setText('COMBO x' + comboCount + '!');
comboTxt.visible = true;
// Animate combo text
comboTxt.scale.set(1.5);
tween(comboTxt, {
scaleX: 1,
scaleY: 1
}, {
duration: 500,
easing: tween.elasticOut
});
// Hide after a delay
LK.setTimeout(function () {
comboTxt.visible = false;
}, 1500);
} else {
comboTxt.visible = false;
}
}
// Create a new paint drop
function createDrop() {
// Select random color from wheel's color array
var randomIndex = Math.floor(Math.random() * wheel.COLORS.length);
var color = wheel.COLORS[randomIndex];
// Determine if this is a special drop (1 in 10 chance)
var isSpecial = Math.random() < 0.1;
// Create drop
var drop = new PaintDrop(randomIndex, color.name, color.hex, dropSpeed + Math.random() * 2,
// Slight speed variation
isSpecial);
// Position drop at random horizontal position
drop.x = 2048 / 2; // Center the drop horizontally
drop.y = -100; // Start above screen
// Add to game and track
game.addChild(drop);
drops.push(drop);
}
// Handle a match between drop and wheel
function handleMatch(drop, index) {
// Increase score based on combo
var pointsGained = 10 * (comboCount + 1);
score += pointsGained;
updateScoreDisplay();
// Increase combo
comboCount++;
updateComboDisplay();
// Add to color meter
var meterFull = colorMeter.addPoints(pointsGained);
// Play match sound
if (comboCount > 1) {
LK.getSound('combo').play();
} else {
LK.getSound('match').play();
}
// Visual effects for match
LK.effects.flashObject(wheel.segments[drop.colorIndex], 0xFFFFFF, 300);
// If meter is full, level up
if (meterFull) {
levelUp();
}
// Remove drop
drop.destroy();
drops.splice(index, 1);
}
// Handle a mismatch between drop and wheel
function handleMiss(drop, index) {
// Reset combo
comboCount = 0;
// Play miss sound
LK.getSound('miss').play();
// Visual effects for miss
LK.effects.flashObject(wheel, 0xFF0000, 300);
// Remove drop
drop.destroy();
drops.splice(index, 1);
// Penalty - decrease score
score = Math.max(0, score - 5);
updateScoreDisplay();
}
// Level up function
function levelUp() {
level++;
updateLevelDisplay();
// Reset color meter
colorMeter.reset();
// Increase difficulty
dropSpeed += 0.5;
dropInterval = Math.max(20, dropInterval - 5);
// Special effects for level up
LK.effects.flashScreen(0xFFFFFF, 500);
// Show level message
var levelUpTxt = new Text2('LEVEL ' + level + '!', {
size: 120,
fill: 0xFFFFFF
});
levelUpTxt.anchor.set(0.5, 0.5);
LK.gui.center.addChild(levelUpTxt);
// Animate and remove level message
tween(levelUpTxt, {
scaleX: 1.5,
scaleY: 1.5,
alpha: 0
}, {
duration: 1500,
easing: tween.easeOut,
onFinish: function onFinish() {
levelUpTxt.destroy();
}
});
}
// Handle wheel rotation from touch/mouse
var startDragX = 0;
var isDragging = false;
game.down = function (x, y, obj) {
startDragX = x;
isDragging = true;
};
game.move = function (x, y, obj) {
if (isDragging) {
// Calculate drag distance from center of wheel
var wheelCenterX = wheel.x;
var wheelCenterY = wheel.y;
// Calculate angle change based on drag direction and distance from center
var prevAngle = Math.atan2(startDragX - wheelCenterX, y - wheelCenterY);
var newAngle = Math.atan2(x - wheelCenterX, y - wheelCenterY);
var angleChange = newAngle - prevAngle;
// Apply rotation to wheel
wheel.rotate(angleChange * 0.8); // Scale for better feel
// Update start position for next move
startDragX = x;
}
};
game.up = function (x, y, obj) {
isDragging = false;
};
// Main game update loop
game.update = function () {
if (isGameOver) {
return;
}
// Create a new drop only if there are no active drops
if (drops.length === 0) {
createDrop();
}
// Update all drops and check for matches or misses
for (var i = drops.length - 1; i >= 0; i--) {
var drop = drops[i];
// Check if drop reaches wheel
if (drop.y >= wheel.y - 100 && drop.y <= wheel.y) {
// Get the wheel's top segment
var topSegmentIndex = wheel.getTopSegment();
// Check if colors match
if (drop.colorIndex === topSegmentIndex) {
handleMatch(drop, i);
score += 1; // Award a point for a successful match
updateScoreDisplay(); // Update the score display
} else {
handleMiss(drop, i);
}
}
// Check if drop goes off screen (missed completely)
else if (drop.y > 2732 + 100) {
handleMiss(drop, i);
// Game over if too many drops are missed (3 misses)
if (comboCount <= 0) {
isGameOver = true;
LK.effects.flashScreen(0xFF0000, 1000);
LK.showGameOver();
LK.stopMusic();
break;
}
}
}
};