/****
* 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;
}
}
}
};