/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ var Block = Container.expand(function (colorType) { var self = Container.call(this); self.colorType = colorType; self.isMatched = false; self.isDragging = false; self.velocity = { x: 0, y: 2 + Math.random() * 2 }; // Random initial falling speed // Colors map var colorMap = { 'red': 'redBlock', 'blue': 'blueBlock', 'green': 'greenBlock', 'yellow': 'yellowBlock' }; // Create and attach the block graphics self.blockGraphics = self.attachAsset(colorMap[colorType], { anchorX: 0.5, anchorY: 0.5 }); // Set initial alpha slightly transparent self.blockGraphics.alpha = 0.9; // Handle touch/mouse events self.down = function (x, y, obj) { self.isDragging = true; // Store initial touch position for swipe calculation self.touchStartX = x; self.touchStartY = y; }; self.up = function (x, y, obj) { if (self.isDragging) { // Calculate swipe direction var deltaX = x - self.touchStartX; var deltaY = y - self.touchStartY; // Apply a velocity based on swipe if (Math.abs(deltaX) > 20) { // Minimum swipe threshold self.velocity.x = deltaX / 10; // Scale down swipe to reasonable velocity } self.isDragging = false; } }; self.update = function () { if (!self.isMatched) { // Apply gravity and movement self.y += self.velocity.y; self.x += self.velocity.x; // Slow down horizontal velocity (drag) self.velocity.x *= 0.95; // Keep block within game boundaries if (self.x < 75) { self.x = 75; self.velocity.x *= -0.5; // Bounce off wall with reduced speed } else if (self.x > 2048 - 75) { self.x = 2048 - 75; self.velocity.x *= -0.5; // Bounce off wall with reduced speed } } }; return self; }); var TargetZone = Container.expand(function (colorType, position) { var self = Container.call(this); self.colorType = colorType; self.position = position; // Create and attach target zone base var zoneBase = self.attachAsset('targetZone', { anchorX: 0.5, anchorY: 0.5 }); // Set alpha for better visibility zoneBase.alpha = 0.5; // Tint the zone to match its color type switch (colorType) { case 'red': zoneBase.tint = 0xff0000; break; case 'blue': zoneBase.tint = 0x0000ff; break; case 'green': zoneBase.tint = 0x00ff00; break; case 'yellow': zoneBase.tint = 0xffff00; break; } // Add label to identify target var label = new Text2(colorType.toUpperCase(), { size: 40, fill: 0xFFFFFF }); label.anchor.set(0.5, 0.5); self.addChild(label); return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x333333 }); /**** * Game Code ****/ // Game variables var blocks = []; var targetZones = []; var score = 0; var level = 1; var spawnInterval = 2000; // Time in ms between block spawns var gameOver = false; var colors = ['red', 'blue', 'green', 'yellow']; // Initialize game function initGame() { // Set up score text var scoreTxt = new Text2('Score: 0', { size: 60, fill: 0xFFFFFF }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); scoreTxt.y = 50; // Add some padding from the top // Set up level indicator var levelTxt = new Text2('Level: 1', { size: 40, fill: 0xFFFFFF }); levelTxt.anchor.set(0, 0); LK.gui.topRight.addChild(levelTxt); levelTxt.x = -150; // Offset from right edge levelTxt.y = 30; // Add some padding from the top // Create target zones createTargetZones(); // Start spawning blocks startBlockSpawner(); // Play background music LK.playMusic('gameMusic', { fade: { start: 0, end: 0.7, duration: 1000 } }); } // Create the target zones at the bottom of the screen function createTargetZones() { var zoneWidth = 300; var totalWidth = zoneWidth * colors.length; var startX = (2048 - totalWidth) / 2 + zoneWidth / 2; for (var i = 0; i < colors.length; i++) { var zone = new TargetZone(colors[i], i); zone.x = startX + i * zoneWidth; zone.y = 2732 - 100; // Position near bottom targetZones.push(zone); game.addChild(zone); } } // Start the block spawner timer function startBlockSpawner() { // Spawn a block immediately spawnBlock(); // Set up interval for continued spawning var spawner = LK.setInterval(function () { if (!gameOver) { spawnBlock(); } }, spawnInterval); } // Spawn a new block function spawnBlock() { var colorIndex = Math.floor(Math.random() * colors.length); var newBlock = new Block(colors[colorIndex]); // Position randomly along the top newBlock.x = 200 + Math.random() * (2048 - 400); newBlock.y = -75; // Start above the screen blocks.push(newBlock); game.addChild(newBlock); } // Check for matches between blocks and target zones function checkMatches() { for (var i = blocks.length - 1; i >= 0; i--) { var block = blocks[i]; // Skip if already matched if (block.isMatched) { continue; } // Check if the block is at the bottom of the screen if (block.y >= 2732 - 200) { // Check for matches with target zones var matched = false; for (var j = 0; j < targetZones.length; j++) { var zone = targetZones[j]; if (block.intersects(zone)) { // Check if colors match if (block.colorType === zone.colorType) { // Match! Add score score += 10; matched = true; // Play match sound LK.getSound('match').play(); // Visual feedback for match LK.effects.flashObject(block, 0xFFFFFF, 300); tween(block, { alpha: 0, scaleX: 0.2, scaleY: 0.2 }, { duration: 300, easing: tween.easeOut, onFinish: function onFinish() { block.destroy(); blocks.splice(i, 1); } }); } else { // Mismatch! Subtract score score = Math.max(0, score - 5); matched = true; // Play mismatch sound LK.getSound('mismatch').play(); // Visual feedback for mismatch LK.effects.flashObject(block, 0xFF0000, 300); tween(block, { alpha: 0, rotation: Math.PI * 2 }, { duration: 300, easing: tween.easeIn, onFinish: function onFinish() { block.destroy(); blocks.splice(i, 1); } }); } // Mark as matched so we don't process it again block.isMatched = true; break; } } // If block reached bottom but didn't match any zone if (!matched && block.y >= 2732) { // Penalty for missing score = Math.max(0, score - 3); // Visual feedback tween(block, { alpha: 0 }, { duration: 200, onFinish: function onFinish() { block.destroy(); blocks.splice(i, 1); } }); } } } // Update score display updateScoreAndLevel(); } // Update the score and level display function updateScoreAndLevel() { // Update score text var scoreTxt = LK.gui.top.children[0]; scoreTxt.setText('Score: ' + score); // Check for level up (every 100 points) var newLevel = Math.floor(score / 100) + 1; if (newLevel > level) { level = newLevel; var levelTxt = LK.gui.topRight.children[0]; levelTxt.setText('Level: ' + level); // Increase difficulty spawnInterval = Math.max(500, 2000 - (level - 1) * 200); // Visual feedback for level up LK.effects.flashScreen(0x00FF00, 500); } // Set the official score in LK LK.setScore(score); // Check win condition (level 10 reached) if (level >= 10) { LK.showYouWin(); } } // Check if game is over (too many blocks on screen) function checkGameOver() { // Count active blocks var activeBlocks = 0; for (var i = 0; i < blocks.length; i++) { if (!blocks[i].isMatched) { activeBlocks++; } } // If too many active blocks, game over if (activeBlocks >= 15) { gameOver = true; LK.showGameOver(); } } // Track dragging var draggedBlock = null; // Game event handlers game.down = function (x, y, obj) { // Check if we clicked on a block for (var i = blocks.length - 1; i >= 0; i--) { var block = blocks[i]; var blockPos = { x: block.x, y: block.y }; var localPos = game.toLocal(blockPos); // Check if click is within block bounds if (Math.abs(x - localPos.x) < 75 && Math.abs(y - localPos.y) < 75) { draggedBlock = block; block.down(x, y, obj); break; } } }; game.up = function (x, y, obj) { if (draggedBlock) { draggedBlock.up(x, y, obj); draggedBlock = null; } }; game.move = function (x, y, obj) { if (draggedBlock) { // Calculate movement relative to game var deltaX = x - draggedBlock.touchStartX; // Apply horizontal movement only (vertical falls automatically) draggedBlock.x += deltaX; // Update start position for next move draggedBlock.touchStartX = x; } }; // Main game update loop game.update = function () { if (!gameOver) { // Update all blocks for (var i = 0; i < blocks.length; i++) { blocks[i].update(); } // Check for matches and update score checkMatches(); // Check for game over condition checkGameOver(); // Increase difficulty over time by adjusting block fall speed if (LK.ticks % 600 === 0) { // Every 10 seconds for (var i = 0; i < blocks.length; i++) { blocks[i].velocity.y *= 1.05; // 5% increase in speed } } } }; // Initialize the game initGame();
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Block = Container.expand(function (colorType) {
var self = Container.call(this);
self.colorType = colorType;
self.isMatched = false;
self.isDragging = false;
self.velocity = {
x: 0,
y: 2 + Math.random() * 2
}; // Random initial falling speed
// Colors map
var colorMap = {
'red': 'redBlock',
'blue': 'blueBlock',
'green': 'greenBlock',
'yellow': 'yellowBlock'
};
// Create and attach the block graphics
self.blockGraphics = self.attachAsset(colorMap[colorType], {
anchorX: 0.5,
anchorY: 0.5
});
// Set initial alpha slightly transparent
self.blockGraphics.alpha = 0.9;
// Handle touch/mouse events
self.down = function (x, y, obj) {
self.isDragging = true;
// Store initial touch position for swipe calculation
self.touchStartX = x;
self.touchStartY = y;
};
self.up = function (x, y, obj) {
if (self.isDragging) {
// Calculate swipe direction
var deltaX = x - self.touchStartX;
var deltaY = y - self.touchStartY;
// Apply a velocity based on swipe
if (Math.abs(deltaX) > 20) {
// Minimum swipe threshold
self.velocity.x = deltaX / 10; // Scale down swipe to reasonable velocity
}
self.isDragging = false;
}
};
self.update = function () {
if (!self.isMatched) {
// Apply gravity and movement
self.y += self.velocity.y;
self.x += self.velocity.x;
// Slow down horizontal velocity (drag)
self.velocity.x *= 0.95;
// Keep block within game boundaries
if (self.x < 75) {
self.x = 75;
self.velocity.x *= -0.5; // Bounce off wall with reduced speed
} else if (self.x > 2048 - 75) {
self.x = 2048 - 75;
self.velocity.x *= -0.5; // Bounce off wall with reduced speed
}
}
};
return self;
});
var TargetZone = Container.expand(function (colorType, position) {
var self = Container.call(this);
self.colorType = colorType;
self.position = position;
// Create and attach target zone base
var zoneBase = self.attachAsset('targetZone', {
anchorX: 0.5,
anchorY: 0.5
});
// Set alpha for better visibility
zoneBase.alpha = 0.5;
// Tint the zone to match its color type
switch (colorType) {
case 'red':
zoneBase.tint = 0xff0000;
break;
case 'blue':
zoneBase.tint = 0x0000ff;
break;
case 'green':
zoneBase.tint = 0x00ff00;
break;
case 'yellow':
zoneBase.tint = 0xffff00;
break;
}
// Add label to identify target
var label = new Text2(colorType.toUpperCase(), {
size: 40,
fill: 0xFFFFFF
});
label.anchor.set(0.5, 0.5);
self.addChild(label);
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x333333
});
/****
* Game Code
****/
// Game variables
var blocks = [];
var targetZones = [];
var score = 0;
var level = 1;
var spawnInterval = 2000; // Time in ms between block spawns
var gameOver = false;
var colors = ['red', 'blue', 'green', 'yellow'];
// Initialize game
function initGame() {
// Set up score text
var scoreTxt = new Text2('Score: 0', {
size: 60,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
scoreTxt.y = 50; // Add some padding from the top
// Set up level indicator
var levelTxt = new Text2('Level: 1', {
size: 40,
fill: 0xFFFFFF
});
levelTxt.anchor.set(0, 0);
LK.gui.topRight.addChild(levelTxt);
levelTxt.x = -150; // Offset from right edge
levelTxt.y = 30; // Add some padding from the top
// Create target zones
createTargetZones();
// Start spawning blocks
startBlockSpawner();
// Play background music
LK.playMusic('gameMusic', {
fade: {
start: 0,
end: 0.7,
duration: 1000
}
});
}
// Create the target zones at the bottom of the screen
function createTargetZones() {
var zoneWidth = 300;
var totalWidth = zoneWidth * colors.length;
var startX = (2048 - totalWidth) / 2 + zoneWidth / 2;
for (var i = 0; i < colors.length; i++) {
var zone = new TargetZone(colors[i], i);
zone.x = startX + i * zoneWidth;
zone.y = 2732 - 100; // Position near bottom
targetZones.push(zone);
game.addChild(zone);
}
}
// Start the block spawner timer
function startBlockSpawner() {
// Spawn a block immediately
spawnBlock();
// Set up interval for continued spawning
var spawner = LK.setInterval(function () {
if (!gameOver) {
spawnBlock();
}
}, spawnInterval);
}
// Spawn a new block
function spawnBlock() {
var colorIndex = Math.floor(Math.random() * colors.length);
var newBlock = new Block(colors[colorIndex]);
// Position randomly along the top
newBlock.x = 200 + Math.random() * (2048 - 400);
newBlock.y = -75; // Start above the screen
blocks.push(newBlock);
game.addChild(newBlock);
}
// Check for matches between blocks and target zones
function checkMatches() {
for (var i = blocks.length - 1; i >= 0; i--) {
var block = blocks[i];
// Skip if already matched
if (block.isMatched) {
continue;
}
// Check if the block is at the bottom of the screen
if (block.y >= 2732 - 200) {
// Check for matches with target zones
var matched = false;
for (var j = 0; j < targetZones.length; j++) {
var zone = targetZones[j];
if (block.intersects(zone)) {
// Check if colors match
if (block.colorType === zone.colorType) {
// Match! Add score
score += 10;
matched = true;
// Play match sound
LK.getSound('match').play();
// Visual feedback for match
LK.effects.flashObject(block, 0xFFFFFF, 300);
tween(block, {
alpha: 0,
scaleX: 0.2,
scaleY: 0.2
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
block.destroy();
blocks.splice(i, 1);
}
});
} else {
// Mismatch! Subtract score
score = Math.max(0, score - 5);
matched = true;
// Play mismatch sound
LK.getSound('mismatch').play();
// Visual feedback for mismatch
LK.effects.flashObject(block, 0xFF0000, 300);
tween(block, {
alpha: 0,
rotation: Math.PI * 2
}, {
duration: 300,
easing: tween.easeIn,
onFinish: function onFinish() {
block.destroy();
blocks.splice(i, 1);
}
});
}
// Mark as matched so we don't process it again
block.isMatched = true;
break;
}
}
// If block reached bottom but didn't match any zone
if (!matched && block.y >= 2732) {
// Penalty for missing
score = Math.max(0, score - 3);
// Visual feedback
tween(block, {
alpha: 0
}, {
duration: 200,
onFinish: function onFinish() {
block.destroy();
blocks.splice(i, 1);
}
});
}
}
}
// Update score display
updateScoreAndLevel();
}
// Update the score and level display
function updateScoreAndLevel() {
// Update score text
var scoreTxt = LK.gui.top.children[0];
scoreTxt.setText('Score: ' + score);
// Check for level up (every 100 points)
var newLevel = Math.floor(score / 100) + 1;
if (newLevel > level) {
level = newLevel;
var levelTxt = LK.gui.topRight.children[0];
levelTxt.setText('Level: ' + level);
// Increase difficulty
spawnInterval = Math.max(500, 2000 - (level - 1) * 200);
// Visual feedback for level up
LK.effects.flashScreen(0x00FF00, 500);
}
// Set the official score in LK
LK.setScore(score);
// Check win condition (level 10 reached)
if (level >= 10) {
LK.showYouWin();
}
}
// Check if game is over (too many blocks on screen)
function checkGameOver() {
// Count active blocks
var activeBlocks = 0;
for (var i = 0; i < blocks.length; i++) {
if (!blocks[i].isMatched) {
activeBlocks++;
}
}
// If too many active blocks, game over
if (activeBlocks >= 15) {
gameOver = true;
LK.showGameOver();
}
}
// Track dragging
var draggedBlock = null;
// Game event handlers
game.down = function (x, y, obj) {
// Check if we clicked on a block
for (var i = blocks.length - 1; i >= 0; i--) {
var block = blocks[i];
var blockPos = {
x: block.x,
y: block.y
};
var localPos = game.toLocal(blockPos);
// Check if click is within block bounds
if (Math.abs(x - localPos.x) < 75 && Math.abs(y - localPos.y) < 75) {
draggedBlock = block;
block.down(x, y, obj);
break;
}
}
};
game.up = function (x, y, obj) {
if (draggedBlock) {
draggedBlock.up(x, y, obj);
draggedBlock = null;
}
};
game.move = function (x, y, obj) {
if (draggedBlock) {
// Calculate movement relative to game
var deltaX = x - draggedBlock.touchStartX;
// Apply horizontal movement only (vertical falls automatically)
draggedBlock.x += deltaX;
// Update start position for next move
draggedBlock.touchStartX = x;
}
};
// Main game update loop
game.update = function () {
if (!gameOver) {
// Update all blocks
for (var i = 0; i < blocks.length; i++) {
blocks[i].update();
}
// Check for matches and update score
checkMatches();
// Check for game over condition
checkGameOver();
// Increase difficulty over time by adjusting block fall speed
if (LK.ticks % 600 === 0) {
// Every 10 seconds
for (var i = 0; i < blocks.length; i++) {
blocks[i].velocity.y *= 1.05; // 5% increase in speed
}
}
}
};
// Initialize the game
initGame();