/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Bird class
var Bird = Container.expand(function () {
var self = Container.call(this);
// Attach asset (birdType: 'falcon', 'eagle', 'chough')
self.init = function (birdType) {
self.birdType = birdType;
self.asset = self.attachAsset(birdType, {
anchorX: 0.5,
anchorY: 0.5
});
self.hasApple = false;
self.cooldown = 0; // Used for apple rejection
self.name = '';
};
// Called every tick
self.update = function () {
// Cooldown for apple rejection
if (self.cooldown > 0) self.cooldown--;
};
// Called when bird grabs the apple
self.grabApple = function () {
self.hasApple = true;
self.cooldown = 60; // 1 second cooldown before apple can reject
LK.getSound('grab').play();
};
// Called when bird loses the apple
self.loseApple = function () {
self.hasApple = false;
self.cooldown = 30; // Short cooldown before can grab again
};
return self;
});
// Apple class
var MagicApple = Container.expand(function () {
var self = Container.call(this);
self.asset = self.attachAsset('magicApple', {
anchorX: 0.5,
anchorY: 0.5
});
self.holder = null; // Bird currently holding the apple
self.state = 'idle'; // 'idle', 'held', 'moving'
self.timer = 0; // Used for random events
// Called every tick
self.update = function () {
// If held, follow the bird
if (self.holder) {
// Tween apple to bird's position for smoothness
tween(self, {
x: self.holder.x,
y: self.holder.y - 90
}, {
duration: 120,
easing: tween.easeOut
});
self.state = 'held';
self.timer++;
// If held too long, apple may reject
if (self.timer > 180 && self.holder.cooldown === 0) {
// 3 seconds
// 50% chance to reject
if (LK.ticks % 60 === 0 && Math.random() < 0.5) {
self.rejectHolder();
}
}
} else {
self.state = 'idle';
self.timer = 0;
}
};
// Called when a bird tries to grab the apple
self.tryGrab = function (bird) {
if (self.holder === null && bird.cooldown === 0) {
self.holder = bird;
bird.grabApple();
self.timer = 0;
}
};
// Apple rejects the current holder
self.rejectHolder = function () {
if (self.holder) {
LK.getSound('reject').play();
// Apple jumps away to a random position
var oldHolder = self.holder;
oldHolder.loseApple();
self.holder = null;
self.state = 'moving';
var newX = 400 + Math.random() * 1248;
var newY = 800 + Math.random() * 1200;
tween(self, {
x: newX,
y: newY
}, {
duration: 500,
easing: tween.bounceOut,
onFinish: function onFinish() {
self.state = 'idle';
}
});
// Flash apple yellow
tween(self.asset, {
tint: 0xffff00
}, {
duration: 200,
onFinish: function onFinish() {
tween(self.asset, {
tint: 0xff2d55
}, {
duration: 200
});
}
});
}
};
// Called when a bird loses the apple (e.g. another bird grabs it)
self.loseApple = function () {
if (self.holder) {
self.holder.loseApple();
self.holder = null;
self.state = 'idle';
self.timer = 0;
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0xfbeee0 // Desert sand color
});
/****
* Game Code
****/
// Music (background, optional)
// Sound effects
// Sand Dune asset (background flavor, not interactive)
// Apple asset (magical apple)
// Bird assets (three birds, each a different color and shape)
// Play background music
LK.playMusic('desertTune');
// Add some dunes for flavor (not interactive)
for (var i = 0; i < 4; i++) {
var dune = LK.getAsset('dune', {
anchorX: 0.5,
anchorY: 0.5,
x: 400 + i * 400,
y: 2200 + Math.random() * 200,
scaleX: 1 + Math.random() * 0.5,
scaleY: 1 + Math.random() * 0.3
});
game.addChild(dune);
}
// Bird names
var birdNames = [{
type: 'falcon',
name: 'Richard'
}, {
type: 'eagle',
name: 'Geoffrey'
}, {
type: 'chough',
name: 'Quinn'
}];
// Bird starting positions (spread horizontally)
var birdStartX = [600, 1024, 1448];
var birdStartY = [2000, 2100, 2000];
// Create birds
var birds = [];
for (var i = 0; i < 3; i++) {
var bird = new Bird();
bird.init(birdNames[i].type);
bird.x = birdStartX[i];
bird.y = birdStartY[i];
bird.name = birdNames[i].name;
birds.push(bird);
game.addChild(bird);
// Add name label above each bird
var nameTxt = new Text2(bird.name, {
size: 60,
fill: 0x222222
});
nameTxt.anchor.set(0.5, 1);
nameTxt.x = 0;
nameTxt.y = -70;
bird.addChild(nameTxt);
}
// Create the magic apple, place at center
var apple = new MagicApple();
apple.x = 1024;
apple.y = 1200;
game.addChild(apple);
// Scoreboard (shows who is holding the apple)
var statusTxt = new Text2('Grab the magical apple!', {
size: 90,
fill: 0xD35400
});
statusTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(statusTxt);
// Timer for win condition
var winTimer = 0;
var WIN_HOLD_TIME = 300; // 5 seconds
// Track dragging
var dragBird = null;
// Helper: get which bird is at (x, y)
function getBirdAt(x, y) {
for (var i = 0; i < birds.length; i++) {
var b = birds[i];
// Use bounding box for hit test
var dx = x - b.x;
var dy = y - b.y;
if (Math.abs(dx) < b.asset.width / 2 && Math.abs(dy) < b.asset.height / 2) {
return b;
}
}
return null;
}
// Helper: clamp position to game area
function clamp(val, min, max) {
return Math.max(min, Math.min(max, val));
}
// Main move handler (drag birds)
function handleMove(x, y, obj) {
if (dragBird) {
// Clamp to game area (leave 100px margin)
dragBird.x = clamp(x, 100, 1948);
dragBird.y = clamp(y, 400, 2200);
// If not holding apple, check for apple grab
if (!dragBird.hasApple && apple.holder === null && dragBird.cooldown === 0) {
// If close enough to apple
var dx = dragBird.x - apple.x;
var dy = dragBird.y - apple.y;
if (dx * dx + dy * dy < 120 * 120) {
apple.tryGrab(dragBird);
}
}
// If another bird is holding apple, and this bird collides, steal!
if (apple.holder && apple.holder !== dragBird && dragBird.cooldown === 0) {
var dx2 = dragBird.x - apple.x;
var dy2 = dragBird.y - apple.y;
if (dx2 * dx2 + dy2 * dy2 < 120 * 120) {
// Steal apple
apple.loseApple();
apple.tryGrab(dragBird);
}
}
}
}
// Touch/mouse down: start dragging a bird
game.down = function (x, y, obj) {
var b = getBirdAt(x, y);
if (b) {
dragBird = b;
handleMove(x, y, obj);
}
};
// Touch/mouse up: stop dragging
game.up = function (x, y, obj) {
dragBird = null;
};
// Touch/mouse move
game.move = handleMove;
// Main game update
game.update = function () {
// Update birds
for (var i = 0; i < birds.length; i++) {
birds[i].update();
}
// Update apple
apple.update();
// Update status text
if (apple.holder) {
statusTxt.setText(apple.holder.name + " is holding the apple!");
// Win timer
winTimer++;
// If held for WIN_HOLD_TIME, win!
if (winTimer >= WIN_HOLD_TIME) {
LK.effects.flashScreen(0x00ff00, 800);
LK.showYouWin();
}
} else {
statusTxt.setText('Grab the magical apple!');
winTimer = 0;
}
};
// On game over or win, everything resets automatically by LK /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Bird class
var Bird = Container.expand(function () {
var self = Container.call(this);
// Attach asset (birdType: 'falcon', 'eagle', 'chough')
self.init = function (birdType) {
self.birdType = birdType;
self.asset = self.attachAsset(birdType, {
anchorX: 0.5,
anchorY: 0.5
});
self.hasApple = false;
self.cooldown = 0; // Used for apple rejection
self.name = '';
};
// Called every tick
self.update = function () {
// Cooldown for apple rejection
if (self.cooldown > 0) self.cooldown--;
};
// Called when bird grabs the apple
self.grabApple = function () {
self.hasApple = true;
self.cooldown = 60; // 1 second cooldown before apple can reject
LK.getSound('grab').play();
};
// Called when bird loses the apple
self.loseApple = function () {
self.hasApple = false;
self.cooldown = 30; // Short cooldown before can grab again
};
return self;
});
// Apple class
var MagicApple = Container.expand(function () {
var self = Container.call(this);
self.asset = self.attachAsset('magicApple', {
anchorX: 0.5,
anchorY: 0.5
});
self.holder = null; // Bird currently holding the apple
self.state = 'idle'; // 'idle', 'held', 'moving'
self.timer = 0; // Used for random events
// Called every tick
self.update = function () {
// If held, follow the bird
if (self.holder) {
// Tween apple to bird's position for smoothness
tween(self, {
x: self.holder.x,
y: self.holder.y - 90
}, {
duration: 120,
easing: tween.easeOut
});
self.state = 'held';
self.timer++;
// If held too long, apple may reject
if (self.timer > 180 && self.holder.cooldown === 0) {
// 3 seconds
// 50% chance to reject
if (LK.ticks % 60 === 0 && Math.random() < 0.5) {
self.rejectHolder();
}
}
} else {
self.state = 'idle';
self.timer = 0;
}
};
// Called when a bird tries to grab the apple
self.tryGrab = function (bird) {
if (self.holder === null && bird.cooldown === 0) {
self.holder = bird;
bird.grabApple();
self.timer = 0;
}
};
// Apple rejects the current holder
self.rejectHolder = function () {
if (self.holder) {
LK.getSound('reject').play();
// Apple jumps away to a random position
var oldHolder = self.holder;
oldHolder.loseApple();
self.holder = null;
self.state = 'moving';
var newX = 400 + Math.random() * 1248;
var newY = 800 + Math.random() * 1200;
tween(self, {
x: newX,
y: newY
}, {
duration: 500,
easing: tween.bounceOut,
onFinish: function onFinish() {
self.state = 'idle';
}
});
// Flash apple yellow
tween(self.asset, {
tint: 0xffff00
}, {
duration: 200,
onFinish: function onFinish() {
tween(self.asset, {
tint: 0xff2d55
}, {
duration: 200
});
}
});
}
};
// Called when a bird loses the apple (e.g. another bird grabs it)
self.loseApple = function () {
if (self.holder) {
self.holder.loseApple();
self.holder = null;
self.state = 'idle';
self.timer = 0;
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0xfbeee0 // Desert sand color
});
/****
* Game Code
****/
// Music (background, optional)
// Sound effects
// Sand Dune asset (background flavor, not interactive)
// Apple asset (magical apple)
// Bird assets (three birds, each a different color and shape)
// Play background music
LK.playMusic('desertTune');
// Add some dunes for flavor (not interactive)
for (var i = 0; i < 4; i++) {
var dune = LK.getAsset('dune', {
anchorX: 0.5,
anchorY: 0.5,
x: 400 + i * 400,
y: 2200 + Math.random() * 200,
scaleX: 1 + Math.random() * 0.5,
scaleY: 1 + Math.random() * 0.3
});
game.addChild(dune);
}
// Bird names
var birdNames = [{
type: 'falcon',
name: 'Richard'
}, {
type: 'eagle',
name: 'Geoffrey'
}, {
type: 'chough',
name: 'Quinn'
}];
// Bird starting positions (spread horizontally)
var birdStartX = [600, 1024, 1448];
var birdStartY = [2000, 2100, 2000];
// Create birds
var birds = [];
for (var i = 0; i < 3; i++) {
var bird = new Bird();
bird.init(birdNames[i].type);
bird.x = birdStartX[i];
bird.y = birdStartY[i];
bird.name = birdNames[i].name;
birds.push(bird);
game.addChild(bird);
// Add name label above each bird
var nameTxt = new Text2(bird.name, {
size: 60,
fill: 0x222222
});
nameTxt.anchor.set(0.5, 1);
nameTxt.x = 0;
nameTxt.y = -70;
bird.addChild(nameTxt);
}
// Create the magic apple, place at center
var apple = new MagicApple();
apple.x = 1024;
apple.y = 1200;
game.addChild(apple);
// Scoreboard (shows who is holding the apple)
var statusTxt = new Text2('Grab the magical apple!', {
size: 90,
fill: 0xD35400
});
statusTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(statusTxt);
// Timer for win condition
var winTimer = 0;
var WIN_HOLD_TIME = 300; // 5 seconds
// Track dragging
var dragBird = null;
// Helper: get which bird is at (x, y)
function getBirdAt(x, y) {
for (var i = 0; i < birds.length; i++) {
var b = birds[i];
// Use bounding box for hit test
var dx = x - b.x;
var dy = y - b.y;
if (Math.abs(dx) < b.asset.width / 2 && Math.abs(dy) < b.asset.height / 2) {
return b;
}
}
return null;
}
// Helper: clamp position to game area
function clamp(val, min, max) {
return Math.max(min, Math.min(max, val));
}
// Main move handler (drag birds)
function handleMove(x, y, obj) {
if (dragBird) {
// Clamp to game area (leave 100px margin)
dragBird.x = clamp(x, 100, 1948);
dragBird.y = clamp(y, 400, 2200);
// If not holding apple, check for apple grab
if (!dragBird.hasApple && apple.holder === null && dragBird.cooldown === 0) {
// If close enough to apple
var dx = dragBird.x - apple.x;
var dy = dragBird.y - apple.y;
if (dx * dx + dy * dy < 120 * 120) {
apple.tryGrab(dragBird);
}
}
// If another bird is holding apple, and this bird collides, steal!
if (apple.holder && apple.holder !== dragBird && dragBird.cooldown === 0) {
var dx2 = dragBird.x - apple.x;
var dy2 = dragBird.y - apple.y;
if (dx2 * dx2 + dy2 * dy2 < 120 * 120) {
// Steal apple
apple.loseApple();
apple.tryGrab(dragBird);
}
}
}
}
// Touch/mouse down: start dragging a bird
game.down = function (x, y, obj) {
var b = getBirdAt(x, y);
if (b) {
dragBird = b;
handleMove(x, y, obj);
}
};
// Touch/mouse up: stop dragging
game.up = function (x, y, obj) {
dragBird = null;
};
// Touch/mouse move
game.move = handleMove;
// Main game update
game.update = function () {
// Update birds
for (var i = 0; i < birds.length; i++) {
birds[i].update();
}
// Update apple
apple.update();
// Update status text
if (apple.holder) {
statusTxt.setText(apple.holder.name + " is holding the apple!");
// Win timer
winTimer++;
// If held for WIN_HOLD_TIME, win!
if (winTimer >= WIN_HOLD_TIME) {
LK.effects.flashScreen(0x00ff00, 800);
LK.showYouWin();
}
} else {
statusTxt.setText('Grab the magical apple!');
winTimer = 0;
}
};
// On game over or win, everything resets automatically by LK