/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Fruit = Container.expand(function () {
var self = Container.call(this);
self.fruitType = 0; // 0: apple, 1: pear, 2: melon, 3: juice, 4: pitcher
self.assetId = 'apple';
self.isDragging = false;
self.startX = 0;
self.startY = 0;
var fruitGraphics = self.attachAsset('apple', {
anchorX: 0.5,
anchorY: 0.5
});
self.setFruitType = function (type) {
self.fruitType = type;
var assetMap = ['apple', 'pear', 'melon', 'orange', 'grape', 'peach', 'watermelonJuice', 'pitcher'];
self.assetId = assetMap[type];
// Remove old graphics
if (fruitGraphics.parent) {
fruitGraphics.parent.removeChild(fruitGraphics);
}
// Add new graphics
fruitGraphics = self.attachAsset(assetMap[type], {
anchorX: 0.5,
anchorY: 0.5
});
// Scale fruit based on type
var scaleMap = [2.0, 2.5, 3.2, 3.5, 3.7, 3.9, 4.0, 5.0]; // apple, pear, melon, orange, grape, peach, juice, pitcher
fruitGraphics.scaleX = scaleMap[type];
fruitGraphics.scaleY = scaleMap[type];
};
self.velocityY = 0; // Track vertical velocity for gravity
self.gravity = 0.5; // Gravity acceleration
self.friction = 0.95; // Friction/damping factor
self.velocityX = 0; // Track horizontal velocity for collisions
self.lastY = 0; // Track last Y position
self.update = function () {
// Update logic will be handled in main game loop
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0xf5e6d3
});
/****
* Game Code
****/
// Game variables
var board = null;
var fruits = [];
var draggedFruit = null;
var dragOffset = {
x: 0,
y: 0
};
var gameWon = false;
// Initialize board
var boardGraphics = LK.getAsset('board', {
anchorX: 0.5,
anchorY: 0.5
});
board = game.addChild(boardGraphics);
board.x = 2048 / 2;
board.y = 1024 + 200;
// Create title text
var titleText = new Text2('Fruity Cascade', {
size: 80,
fill: '#8b4513'
});
titleText.anchor.set(0.5, 0);
LK.gui.top.addChild(titleText);
// Create instruction text
var instructionText = new Text2('Drag fruits to match and merge!', {
size: 40,
fill: '#666666'
});
instructionText.anchor.set(0.5, 0);
instructionText.y = 100;
LK.gui.top.addChild(instructionText);
// Create score text
var scoreText = new Text2('Fruits Merged: 0', {
size: 60,
fill: '#ff6b6b'
});
scoreText.anchor.set(0.5, 1);
LK.gui.bottom.addChild(scoreText);
// Initialize with some starting fruits
function addFruit(x, y, type) {
var fruit = game.addChild(new Fruit());
fruit.setFruitType(type);
fruit.x = x;
fruit.y = y;
fruits.push(fruit);
return fruit;
}
// Create cursor fruit that follows mouse
var cursorFruit = game.addChild(new Fruit());
cursorFruit.setFruitType(Math.floor(Math.random() * 3)); // Random fruit 0-2 (apple, pear, melon)
cursorFruit.x = 1024;
cursorFruit.y = 1366;
// Play background music
LK.playMusic('music', {
loop: true
});
// Check if fruit is within board bounds
function isWithinBoard(x, y) {
var boardLeft = board.x - 900;
var boardRight = board.x + 900;
var boardTop = board.y - 1100;
var boardBottom = board.y + 1100;
return x >= boardLeft && x <= boardRight && y >= boardTop && y <= boardBottom;
}
// Check for fruit collisions and merging
function checkMerges() {
var merged = true;
while (merged) {
merged = false;
for (var i = 0; i < fruits.length; i++) {
if (!fruits[i]) {
continue;
}
for (var j = i + 1; j < fruits.length; j++) {
if (!fruits[j]) {
continue;
}
if (fruits[i].intersects(fruits[j]) && fruits[i].fruitType === fruits[j].fruitType) {
// Merge fruits
var mergeX = (fruits[i].x + fruits[j].x) / 2;
var mergeY = (fruits[i].y + fruits[j].y) / 2;
var newType = fruits[i].fruitType + 1;
// Remove old fruits
fruits[i].destroy();
fruits[j].destroy();
fruits.splice(j, 1);
fruits.splice(i, 1);
// Check for win condition
if (newType === 7) {
// Created a pitcher!
addFruit(mergeX, mergeY, newType);
LK.getSound('merge').play();
LK.setScore(LK.getScore() + 1);
scoreText.setText('Fruits Merged: ' + LK.getScore());
LK.showYouWin();
gameWon = true;
return;
} else {
// Create merged fruit
addFruit(mergeX, mergeY, newType);
LK.getSound('merge').play();
LK.setScore(LK.getScore() + 1);
scoreText.setText('Fruits Merged: ' + LK.getScore());
merged = true;
break;
}
}
}
if (merged) {
break;
}
}
}
}
// Handle game input
game.down = function (x, y, obj) {
if (gameWon) {
return;
}
// Convert to game coordinates
var gamePos = game.toLocal({
x: x,
y: y
});
// Check if cursor fruit clicked
if (cursorFruit) {
var cursorDist = Math.sqrt(Math.pow(cursorFruit.x - gamePos.x, 2) + Math.pow(cursorFruit.y - gamePos.y, 2));
if (cursorDist < 100) {
draggedFruit = cursorFruit;
dragOffset.x = gamePos.x - cursorFruit.x;
dragOffset.y = gamePos.y - cursorFruit.y;
return;
}
}
// Find clicked fruit
for (var i = fruits.length - 1; i >= 0; i--) {
var fruit = fruits[i];
var dist = Math.sqrt(Math.pow(fruit.x - gamePos.x, 2) + Math.pow(fruit.y - gamePos.y, 2));
// Check if within fruit bounds (approximately 50 pixel radius for click detection)
if (dist < 100) {
draggedFruit = fruit;
dragOffset.x = gamePos.x - fruit.x;
dragOffset.y = gamePos.y - fruit.y;
break;
}
}
};
game.move = function (x, y, obj) {
var gamePos = game.toLocal({
x: x,
y: y
});
// Move cursor fruit to follow mouse
if (!draggedFruit && cursorFruit) {
cursorFruit.x = gamePos.x;
cursorFruit.y = gamePos.y;
}
if (!draggedFruit || gameWon) {
return;
}
draggedFruit.x = gamePos.x - dragOffset.x;
draggedFruit.y = gamePos.y - dragOffset.y;
};
game.up = function (x, y, obj) {
if (!draggedFruit) {
return;
}
// Check if dragged fruit is the cursor fruit
if (draggedFruit === cursorFruit) {
// Check if fruit is within board
if (!isWithinBoard(draggedFruit.x, draggedFruit.y)) {
// Reset position to cursor
var gamePos = game.toLocal({
x: x,
y: y
});
draggedFruit.x = gamePos.x;
draggedFruit.y = gamePos.y;
} else {
// Place fruit on board - add to fruits array
fruits.push(cursorFruit);
LK.getSound('place').play();
// Create new cursor fruit
cursorFruit = game.addChild(new Fruit());
cursorFruit.setFruitType(Math.floor(Math.random() * 3)); // Random fruit 0-2
var gamePos = game.toLocal({
x: x,
y: y
});
cursorFruit.x = gamePos.x;
cursorFruit.y = gamePos.y;
}
} else {
// Original logic for board fruits
if (!isWithinBoard(draggedFruit.x, draggedFruit.y)) {
// Reset position
draggedFruit.x = 2048 / 2;
draggedFruit.y = 200;
} else {
LK.getSound('place').play();
}
}
draggedFruit = null;
checkMerges();
};
// Main game update loop
game.update = function () {
if (gameWon) {
return;
}
// Prevent fruits from going too far off board
for (var i = 0; i < fruits.length; i++) {
var fruit = fruits[i];
// Initialize last position tracking for edge detection
if (fruit.lastY === undefined) {
fruit.lastY = fruit.y;
}
// Apply gravity only if not being dragged
if (fruit !== draggedFruit) {
fruit.velocityY += fruit.gravity;
fruit.y += fruit.velocityY;
}
// Boundary constraints without bounce
var boardBottom = board.y + 1050;
if (fruit.y > boardBottom) {
fruit.y = boardBottom;
fruit.velocityY = 0; // Stop at ground instead of bouncing
} else if (fruit.y < board.y - 1050) {
fruit.y = board.y - 1050;
fruit.velocityY = 0; // Stop at ceiling instead of bouncing
}
// Check if fruit fell off bottom of screen (game over condition)
var screenBottom = 2732;
if (fruit.lastY <= screenBottom && fruit.y > screenBottom) {
LK.showGameOver();
return;
}
// Check if fruit stacked too high (game over condition)
var boardTop = board.y - 1050;
if (fruit.y < boardTop - 200) {
LK.showGameOver();
return;
}
if (fruit.x < board.x - 850) {
fruit.x = board.x - 850;
}
if (fruit.x > board.x + 850) {
fruit.x = board.x + 850;
}
// Update last position for next frame
fruit.lastY = fruit.y;
}
// Collision detection between fruits
for (var i = 0; i < fruits.length; i++) {
var fruitA = fruits[i];
if (!fruitA) {
continue;
}
for (var j = i + 1; j < fruits.length; j++) {
var fruitB = fruits[j];
if (!fruitB) {
continue;
}
// Check for collision between fruits
if (fruitA.intersects(fruitB)) {
// Calculate distance between fruit centers
var dx = fruitB.x - fruitA.x;
var dy = fruitB.y - fruitA.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// Estimate collision radius (average of both fruits)
var radiusA = 50;
var radiusB = 50;
var minDistance = radiusA + radiusB;
// Separate overlapping fruits
if (distance < minDistance && distance > 0) {
var overlap = minDistance - distance;
var separationX = dx / distance * overlap * 0.5;
var separationY = dy / distance * overlap * 0.5;
// Push fruits apart
if (fruitA !== draggedFruit) {
fruitA.x -= separationX;
fruitA.y -= separationY;
}
if (fruitB !== draggedFruit) {
fruitB.x += separationX;
fruitB.y += separationY;
}
}
}
}
}
// Check for merges every frame
checkMerges();
}; /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Fruit = Container.expand(function () {
var self = Container.call(this);
self.fruitType = 0; // 0: apple, 1: pear, 2: melon, 3: juice, 4: pitcher
self.assetId = 'apple';
self.isDragging = false;
self.startX = 0;
self.startY = 0;
var fruitGraphics = self.attachAsset('apple', {
anchorX: 0.5,
anchorY: 0.5
});
self.setFruitType = function (type) {
self.fruitType = type;
var assetMap = ['apple', 'pear', 'melon', 'orange', 'grape', 'peach', 'watermelonJuice', 'pitcher'];
self.assetId = assetMap[type];
// Remove old graphics
if (fruitGraphics.parent) {
fruitGraphics.parent.removeChild(fruitGraphics);
}
// Add new graphics
fruitGraphics = self.attachAsset(assetMap[type], {
anchorX: 0.5,
anchorY: 0.5
});
// Scale fruit based on type
var scaleMap = [2.0, 2.5, 3.2, 3.5, 3.7, 3.9, 4.0, 5.0]; // apple, pear, melon, orange, grape, peach, juice, pitcher
fruitGraphics.scaleX = scaleMap[type];
fruitGraphics.scaleY = scaleMap[type];
};
self.velocityY = 0; // Track vertical velocity for gravity
self.gravity = 0.5; // Gravity acceleration
self.friction = 0.95; // Friction/damping factor
self.velocityX = 0; // Track horizontal velocity for collisions
self.lastY = 0; // Track last Y position
self.update = function () {
// Update logic will be handled in main game loop
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0xf5e6d3
});
/****
* Game Code
****/
// Game variables
var board = null;
var fruits = [];
var draggedFruit = null;
var dragOffset = {
x: 0,
y: 0
};
var gameWon = false;
// Initialize board
var boardGraphics = LK.getAsset('board', {
anchorX: 0.5,
anchorY: 0.5
});
board = game.addChild(boardGraphics);
board.x = 2048 / 2;
board.y = 1024 + 200;
// Create title text
var titleText = new Text2('Fruity Cascade', {
size: 80,
fill: '#8b4513'
});
titleText.anchor.set(0.5, 0);
LK.gui.top.addChild(titleText);
// Create instruction text
var instructionText = new Text2('Drag fruits to match and merge!', {
size: 40,
fill: '#666666'
});
instructionText.anchor.set(0.5, 0);
instructionText.y = 100;
LK.gui.top.addChild(instructionText);
// Create score text
var scoreText = new Text2('Fruits Merged: 0', {
size: 60,
fill: '#ff6b6b'
});
scoreText.anchor.set(0.5, 1);
LK.gui.bottom.addChild(scoreText);
// Initialize with some starting fruits
function addFruit(x, y, type) {
var fruit = game.addChild(new Fruit());
fruit.setFruitType(type);
fruit.x = x;
fruit.y = y;
fruits.push(fruit);
return fruit;
}
// Create cursor fruit that follows mouse
var cursorFruit = game.addChild(new Fruit());
cursorFruit.setFruitType(Math.floor(Math.random() * 3)); // Random fruit 0-2 (apple, pear, melon)
cursorFruit.x = 1024;
cursorFruit.y = 1366;
// Play background music
LK.playMusic('music', {
loop: true
});
// Check if fruit is within board bounds
function isWithinBoard(x, y) {
var boardLeft = board.x - 900;
var boardRight = board.x + 900;
var boardTop = board.y - 1100;
var boardBottom = board.y + 1100;
return x >= boardLeft && x <= boardRight && y >= boardTop && y <= boardBottom;
}
// Check for fruit collisions and merging
function checkMerges() {
var merged = true;
while (merged) {
merged = false;
for (var i = 0; i < fruits.length; i++) {
if (!fruits[i]) {
continue;
}
for (var j = i + 1; j < fruits.length; j++) {
if (!fruits[j]) {
continue;
}
if (fruits[i].intersects(fruits[j]) && fruits[i].fruitType === fruits[j].fruitType) {
// Merge fruits
var mergeX = (fruits[i].x + fruits[j].x) / 2;
var mergeY = (fruits[i].y + fruits[j].y) / 2;
var newType = fruits[i].fruitType + 1;
// Remove old fruits
fruits[i].destroy();
fruits[j].destroy();
fruits.splice(j, 1);
fruits.splice(i, 1);
// Check for win condition
if (newType === 7) {
// Created a pitcher!
addFruit(mergeX, mergeY, newType);
LK.getSound('merge').play();
LK.setScore(LK.getScore() + 1);
scoreText.setText('Fruits Merged: ' + LK.getScore());
LK.showYouWin();
gameWon = true;
return;
} else {
// Create merged fruit
addFruit(mergeX, mergeY, newType);
LK.getSound('merge').play();
LK.setScore(LK.getScore() + 1);
scoreText.setText('Fruits Merged: ' + LK.getScore());
merged = true;
break;
}
}
}
if (merged) {
break;
}
}
}
}
// Handle game input
game.down = function (x, y, obj) {
if (gameWon) {
return;
}
// Convert to game coordinates
var gamePos = game.toLocal({
x: x,
y: y
});
// Check if cursor fruit clicked
if (cursorFruit) {
var cursorDist = Math.sqrt(Math.pow(cursorFruit.x - gamePos.x, 2) + Math.pow(cursorFruit.y - gamePos.y, 2));
if (cursorDist < 100) {
draggedFruit = cursorFruit;
dragOffset.x = gamePos.x - cursorFruit.x;
dragOffset.y = gamePos.y - cursorFruit.y;
return;
}
}
// Find clicked fruit
for (var i = fruits.length - 1; i >= 0; i--) {
var fruit = fruits[i];
var dist = Math.sqrt(Math.pow(fruit.x - gamePos.x, 2) + Math.pow(fruit.y - gamePos.y, 2));
// Check if within fruit bounds (approximately 50 pixel radius for click detection)
if (dist < 100) {
draggedFruit = fruit;
dragOffset.x = gamePos.x - fruit.x;
dragOffset.y = gamePos.y - fruit.y;
break;
}
}
};
game.move = function (x, y, obj) {
var gamePos = game.toLocal({
x: x,
y: y
});
// Move cursor fruit to follow mouse
if (!draggedFruit && cursorFruit) {
cursorFruit.x = gamePos.x;
cursorFruit.y = gamePos.y;
}
if (!draggedFruit || gameWon) {
return;
}
draggedFruit.x = gamePos.x - dragOffset.x;
draggedFruit.y = gamePos.y - dragOffset.y;
};
game.up = function (x, y, obj) {
if (!draggedFruit) {
return;
}
// Check if dragged fruit is the cursor fruit
if (draggedFruit === cursorFruit) {
// Check if fruit is within board
if (!isWithinBoard(draggedFruit.x, draggedFruit.y)) {
// Reset position to cursor
var gamePos = game.toLocal({
x: x,
y: y
});
draggedFruit.x = gamePos.x;
draggedFruit.y = gamePos.y;
} else {
// Place fruit on board - add to fruits array
fruits.push(cursorFruit);
LK.getSound('place').play();
// Create new cursor fruit
cursorFruit = game.addChild(new Fruit());
cursorFruit.setFruitType(Math.floor(Math.random() * 3)); // Random fruit 0-2
var gamePos = game.toLocal({
x: x,
y: y
});
cursorFruit.x = gamePos.x;
cursorFruit.y = gamePos.y;
}
} else {
// Original logic for board fruits
if (!isWithinBoard(draggedFruit.x, draggedFruit.y)) {
// Reset position
draggedFruit.x = 2048 / 2;
draggedFruit.y = 200;
} else {
LK.getSound('place').play();
}
}
draggedFruit = null;
checkMerges();
};
// Main game update loop
game.update = function () {
if (gameWon) {
return;
}
// Prevent fruits from going too far off board
for (var i = 0; i < fruits.length; i++) {
var fruit = fruits[i];
// Initialize last position tracking for edge detection
if (fruit.lastY === undefined) {
fruit.lastY = fruit.y;
}
// Apply gravity only if not being dragged
if (fruit !== draggedFruit) {
fruit.velocityY += fruit.gravity;
fruit.y += fruit.velocityY;
}
// Boundary constraints without bounce
var boardBottom = board.y + 1050;
if (fruit.y > boardBottom) {
fruit.y = boardBottom;
fruit.velocityY = 0; // Stop at ground instead of bouncing
} else if (fruit.y < board.y - 1050) {
fruit.y = board.y - 1050;
fruit.velocityY = 0; // Stop at ceiling instead of bouncing
}
// Check if fruit fell off bottom of screen (game over condition)
var screenBottom = 2732;
if (fruit.lastY <= screenBottom && fruit.y > screenBottom) {
LK.showGameOver();
return;
}
// Check if fruit stacked too high (game over condition)
var boardTop = board.y - 1050;
if (fruit.y < boardTop - 200) {
LK.showGameOver();
return;
}
if (fruit.x < board.x - 850) {
fruit.x = board.x - 850;
}
if (fruit.x > board.x + 850) {
fruit.x = board.x + 850;
}
// Update last position for next frame
fruit.lastY = fruit.y;
}
// Collision detection between fruits
for (var i = 0; i < fruits.length; i++) {
var fruitA = fruits[i];
if (!fruitA) {
continue;
}
for (var j = i + 1; j < fruits.length; j++) {
var fruitB = fruits[j];
if (!fruitB) {
continue;
}
// Check for collision between fruits
if (fruitA.intersects(fruitB)) {
// Calculate distance between fruit centers
var dx = fruitB.x - fruitA.x;
var dy = fruitB.y - fruitA.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// Estimate collision radius (average of both fruits)
var radiusA = 50;
var radiusB = 50;
var minDistance = radiusA + radiusB;
// Separate overlapping fruits
if (distance < minDistance && distance > 0) {
var overlap = minDistance - distance;
var separationX = dx / distance * overlap * 0.5;
var separationY = dy / distance * overlap * 0.5;
// Push fruits apart
if (fruitA !== draggedFruit) {
fruitA.x -= separationX;
fruitA.y -= separationY;
}
if (fruitB !== draggedFruit) {
fruitB.x += separationX;
fruitB.y += separationY;
}
}
}
}
}
// Check for merges every frame
checkMerges();
};
an apple. In-Game asset. 2d. High contrast. No shadows
a pear. In-Game asset. 2d. High contrast. No shadows
a 2D melon. In-Game asset. 2d. High contrast. No shadows
a watermelon juice in a glass. In-Game asset. 2d. High contrast. No shadows
an watermelon.. In-Game asset. 2d. High contrast. No shadows
some grapes in 2D. In-Game asset. 2d. High contrast. No shadows
an orange. In-Game asset. 2d. High contrast. No shadows
a table seen from above.. In-Game asset. 2d. High contrast. No shadows
an peach. In-Game asset. 2d. High contrast. No shadows