/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Heart = Container.expand(function () {
var self = Container.call(this);
var heartGraphics = self.attachAsset('heart', {
anchorX: 0.5,
anchorY: 0.5
});
self.driftX = 3;
self.driftY = 2;
self.baseSize = 1;
self.lettersCollected = 0;
self.update = function () {
self.x += self.driftX;
self.y += self.driftY;
// Wrap around screen edges
if (self.x < 0) self.x = 2048;
if (self.x > 2048) self.x = 0;
if (self.y < 0) self.y = 2732;
if (self.y > 2732) self.y = 0;
};
self.changeDriftDirection = function () {
self.driftX = -self.driftX;
self.driftY = -self.driftY;
};
self.collectLetter = function () {
self.lettersCollected++;
var newScale = self.baseSize + self.lettersCollected * 0.1;
tween(heartGraphics, {
scaleX: newScale,
scaleY: newScale
}, {
duration: 300,
easing: tween.easeOut
});
// Change color based on letters collected
var colors = [0xff69b4, 0xff1493, 0xdc143c, 0xb22222];
var colorIndex = Math.min(Math.floor(self.lettersCollected / 3), colors.length - 1);
tween(heartGraphics, {
tint: colors[colorIndex]
}, {
duration: 500
});
};
return self;
});
var LoveLetter = Container.expand(function () {
var self = Container.call(this);
var letterGraphics = self.attachAsset('loveLetter', {
anchorX: 0.5,
anchorY: 0.5
});
self.collected = false;
self.update = function () {
letterGraphics.rotation += 0.05;
// Gentle floating animation
letterGraphics.y += Math.sin(LK.ticks * 0.1 + self.x * 0.01) * 0.5;
};
return self;
});
var Obstacle = Container.expand(function () {
var self = Container.call(this);
var obstacleGraphics = self.attachAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000022
});
/****
* Game Code
****/
var heart = game.addChild(new Heart());
heart.x = 1024;
heart.y = 1366;
var loveLetters = [];
var obstacles = [];
var targetLetters = 10;
var currentLevel = 1;
// UI Elements
var scoreTxt = new Text2('Letters: 0/' + targetLetters, {
size: 80,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
var levelTxt = new Text2('Level ' + currentLevel, {
size: 60,
fill: 0xFFB6C1
});
levelTxt.anchor.set(1, 0);
LK.gui.topRight.addChild(levelTxt);
levelTxt.x -= 50;
levelTxt.y += 50;
// Generate initial love letters
function spawnLoveLetters() {
for (var i = 0; i < targetLetters; i++) {
var letter = new LoveLetter();
letter.x = Math.random() * 1800 + 124;
letter.y = Math.random() * 2400 + 166;
loveLetters.push(letter);
game.addChild(letter);
}
}
// Generate obstacles
function spawnObstacles() {
var obstacleCount = currentLevel + 2;
for (var i = 0; i < obstacleCount; i++) {
var obstacle = new Obstacle();
obstacle.x = Math.random() * 1800 + 124;
obstacle.y = Math.random() * 2400 + 166;
// Make sure obstacles don't spawn too close to heart or letters
var tooClose = true;
var attempts = 0;
while (tooClose && attempts < 20) {
tooClose = false;
// Check distance from heart
var heartDist = Math.sqrt(Math.pow(obstacle.x - heart.x, 2) + Math.pow(obstacle.y - heart.y, 2));
if (heartDist < 200) tooClose = true;
// Check distance from letters
for (var j = 0; j < loveLetters.length; j++) {
var letterDist = Math.sqrt(Math.pow(obstacle.x - loveLetters[j].x, 2) + Math.pow(obstacle.y - loveLetters[j].y, 2));
if (letterDist < 150) tooClose = true;
}
if (tooClose) {
obstacle.x = Math.random() * 1800 + 124;
obstacle.y = Math.random() * 2400 + 166;
}
attempts++;
}
obstacles.push(obstacle);
game.addChild(obstacle);
}
}
function nextLevel() {
currentLevel++;
targetLetters += 3;
// Clear existing letters and obstacles
for (var i = loveLetters.length - 1; i >= 0; i--) {
loveLetters[i].destroy();
}
loveLetters = [];
for (var i = obstacles.length - 1; i >= 0; i--) {
obstacles[i].destroy();
}
obstacles = [];
// Reset heart position
heart.x = 1024;
heart.y = 1366;
heart.lettersCollected = 0;
// Update UI
scoreTxt.setText('Letters: 0/' + targetLetters);
levelTxt.setText('Level ' + currentLevel);
// Spawn new elements
spawnLoveLetters();
spawnObstacles();
// Flash screen to indicate level change
LK.effects.flashScreen(0x9370db, 800);
}
// Initialize first level
spawnLoveLetters();
spawnObstacles();
// Game input handling
game.down = function (x, y, obj) {
heart.changeDriftDirection();
};
// Main game loop
game.update = function () {
// Check collision with love letters
for (var i = loveLetters.length - 1; i >= 0; i--) {
var letter = loveLetters[i];
if (!letter.collected && heart.intersects(letter)) {
letter.collected = true;
heart.collectLetter();
LK.setScore(LK.getScore() + 10);
LK.getSound('collect').play();
// Remove letter with animation
tween(letter, {
alpha: 0,
scaleX: 2,
scaleY: 2
}, {
duration: 300,
onFinish: function onFinish() {
letter.destroy();
}
});
loveLetters.splice(i, 1);
// Update score display
scoreTxt.setText('Letters: ' + heart.lettersCollected + '/' + targetLetters);
// Check if level complete
if (heart.lettersCollected >= targetLetters) {
LK.setTimeout(function () {
nextLevel();
}, 500);
}
}
}
// Check collision with obstacles
for (var i = 0; i < obstacles.length; i++) {
var obstacle = obstacles[i];
if (heart.intersects(obstacle)) {
LK.getSound('hit').play();
LK.effects.flashScreen(0xff0000, 1000);
LK.showGameOver();
return;
}
}
// Victory condition
if (currentLevel > 5) {
LK.showYouWin();
}
}; /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Heart = Container.expand(function () {
var self = Container.call(this);
var heartGraphics = self.attachAsset('heart', {
anchorX: 0.5,
anchorY: 0.5
});
self.driftX = 3;
self.driftY = 2;
self.baseSize = 1;
self.lettersCollected = 0;
self.update = function () {
self.x += self.driftX;
self.y += self.driftY;
// Wrap around screen edges
if (self.x < 0) self.x = 2048;
if (self.x > 2048) self.x = 0;
if (self.y < 0) self.y = 2732;
if (self.y > 2732) self.y = 0;
};
self.changeDriftDirection = function () {
self.driftX = -self.driftX;
self.driftY = -self.driftY;
};
self.collectLetter = function () {
self.lettersCollected++;
var newScale = self.baseSize + self.lettersCollected * 0.1;
tween(heartGraphics, {
scaleX: newScale,
scaleY: newScale
}, {
duration: 300,
easing: tween.easeOut
});
// Change color based on letters collected
var colors = [0xff69b4, 0xff1493, 0xdc143c, 0xb22222];
var colorIndex = Math.min(Math.floor(self.lettersCollected / 3), colors.length - 1);
tween(heartGraphics, {
tint: colors[colorIndex]
}, {
duration: 500
});
};
return self;
});
var LoveLetter = Container.expand(function () {
var self = Container.call(this);
var letterGraphics = self.attachAsset('loveLetter', {
anchorX: 0.5,
anchorY: 0.5
});
self.collected = false;
self.update = function () {
letterGraphics.rotation += 0.05;
// Gentle floating animation
letterGraphics.y += Math.sin(LK.ticks * 0.1 + self.x * 0.01) * 0.5;
};
return self;
});
var Obstacle = Container.expand(function () {
var self = Container.call(this);
var obstacleGraphics = self.attachAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000022
});
/****
* Game Code
****/
var heart = game.addChild(new Heart());
heart.x = 1024;
heart.y = 1366;
var loveLetters = [];
var obstacles = [];
var targetLetters = 10;
var currentLevel = 1;
// UI Elements
var scoreTxt = new Text2('Letters: 0/' + targetLetters, {
size: 80,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
var levelTxt = new Text2('Level ' + currentLevel, {
size: 60,
fill: 0xFFB6C1
});
levelTxt.anchor.set(1, 0);
LK.gui.topRight.addChild(levelTxt);
levelTxt.x -= 50;
levelTxt.y += 50;
// Generate initial love letters
function spawnLoveLetters() {
for (var i = 0; i < targetLetters; i++) {
var letter = new LoveLetter();
letter.x = Math.random() * 1800 + 124;
letter.y = Math.random() * 2400 + 166;
loveLetters.push(letter);
game.addChild(letter);
}
}
// Generate obstacles
function spawnObstacles() {
var obstacleCount = currentLevel + 2;
for (var i = 0; i < obstacleCount; i++) {
var obstacle = new Obstacle();
obstacle.x = Math.random() * 1800 + 124;
obstacle.y = Math.random() * 2400 + 166;
// Make sure obstacles don't spawn too close to heart or letters
var tooClose = true;
var attempts = 0;
while (tooClose && attempts < 20) {
tooClose = false;
// Check distance from heart
var heartDist = Math.sqrt(Math.pow(obstacle.x - heart.x, 2) + Math.pow(obstacle.y - heart.y, 2));
if (heartDist < 200) tooClose = true;
// Check distance from letters
for (var j = 0; j < loveLetters.length; j++) {
var letterDist = Math.sqrt(Math.pow(obstacle.x - loveLetters[j].x, 2) + Math.pow(obstacle.y - loveLetters[j].y, 2));
if (letterDist < 150) tooClose = true;
}
if (tooClose) {
obstacle.x = Math.random() * 1800 + 124;
obstacle.y = Math.random() * 2400 + 166;
}
attempts++;
}
obstacles.push(obstacle);
game.addChild(obstacle);
}
}
function nextLevel() {
currentLevel++;
targetLetters += 3;
// Clear existing letters and obstacles
for (var i = loveLetters.length - 1; i >= 0; i--) {
loveLetters[i].destroy();
}
loveLetters = [];
for (var i = obstacles.length - 1; i >= 0; i--) {
obstacles[i].destroy();
}
obstacles = [];
// Reset heart position
heart.x = 1024;
heart.y = 1366;
heart.lettersCollected = 0;
// Update UI
scoreTxt.setText('Letters: 0/' + targetLetters);
levelTxt.setText('Level ' + currentLevel);
// Spawn new elements
spawnLoveLetters();
spawnObstacles();
// Flash screen to indicate level change
LK.effects.flashScreen(0x9370db, 800);
}
// Initialize first level
spawnLoveLetters();
spawnObstacles();
// Game input handling
game.down = function (x, y, obj) {
heart.changeDriftDirection();
};
// Main game loop
game.update = function () {
// Check collision with love letters
for (var i = loveLetters.length - 1; i >= 0; i--) {
var letter = loveLetters[i];
if (!letter.collected && heart.intersects(letter)) {
letter.collected = true;
heart.collectLetter();
LK.setScore(LK.getScore() + 10);
LK.getSound('collect').play();
// Remove letter with animation
tween(letter, {
alpha: 0,
scaleX: 2,
scaleY: 2
}, {
duration: 300,
onFinish: function onFinish() {
letter.destroy();
}
});
loveLetters.splice(i, 1);
// Update score display
scoreTxt.setText('Letters: ' + heart.lettersCollected + '/' + targetLetters);
// Check if level complete
if (heart.lettersCollected >= targetLetters) {
LK.setTimeout(function () {
nextLevel();
}, 500);
}
}
}
// Check collision with obstacles
for (var i = 0; i < obstacles.length; i++) {
var obstacle = obstacles[i];
if (heart.intersects(obstacle)) {
LK.getSound('hit').play();
LK.effects.flashScreen(0xff0000, 1000);
LK.showGameOver();
return;
}
}
// Victory condition
if (currentLevel > 5) {
LK.showYouWin();
}
};