/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // Cloud obstacle var Cloud = Container.expand(function () { var self = Container.call(this); var sprite = self.attachAsset('cloud', { anchorX: 0.5, anchorY: 0.5 }); self.radius = sprite.width / 2; self.vx = -10; self.update = function () { self.x += self.vx; }; return self; }); // Ember collectible var Ember = Container.expand(function () { var self = Container.call(this); var sprite = self.attachAsset('ember', { anchorX: 0.5, anchorY: 0.5 }); self.radius = sprite.width / 2; self.vx = -12; self.update = function () { self.x += self.vx; }; return self; }); // Falcon (Richard) class var Falcon = Container.expand(function () { var self = Container.call(this); var sprite = self.attachAsset('falcon', { anchorX: 0.5, anchorY: 0.5 }); self.radius = sprite.width / 2; self.vx = 18; self.update = function () { self.x += self.vx; }; return self; }); // Phoenix (Vixen) class var Phoenix = Container.expand(function () { var self = Container.call(this); var sprite = self.attachAsset('phoenix', { anchorX: 0.5, anchorY: 0.5 }); self.radius = sprite.width / 2; self.energy = 0; // 0-1 self.isTransformed = false; self.update = function () { // No auto-movement; movement is handled by drag/touch }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x1e2a44 }); /**** * Game Code ****/ // Energy bar fill // Energy bar background // Dark cloud (obstacle) // Ember collectible // Falcon (Richard) // Phoenix (Vixen) // Game state var phoenix, falcon; var embers = []; var clouds = []; var dragNode = null; var energyBarBg, energyBarFill; var score = 0; var scoreTxt; var energy = 0; // 0-1 var inBonusStage = false; var bonusTimer = 0; var lastPhoenixPos = { x: 0, y: 0 }; // Place Phoenix at start phoenix = new Phoenix(); phoenix.x = 400; phoenix.y = 1366; game.addChild(phoenix); // Energy bar UI energyBarBg = LK.getAsset('energyBarBg', { anchorX: 0, anchorY: 0.5, x: 724, y: 120 }); energyBarFill = LK.getAsset('energyBarFill', { anchorX: 0, anchorY: 0.5, x: 724, y: 120 }); energyBarFill.width = 0; LK.gui.top.addChild(energyBarBg); LK.gui.top.addChild(energyBarFill); // Score UI scoreTxt = new Text2('0', { size: 100, fill: '#fff' }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); scoreTxt.x = 2048 / 2; scoreTxt.y = 30; // Helper: spawn ember function spawnEmber() { var ember = new Ember(); ember.x = 2048 + 80; ember.y = 300 + Math.floor(Math.random() * (2732 - 600)); embers.push(ember); game.addChild(ember); } // Helper: spawn cloud function spawnCloud() { var cloud = new Cloud(); cloud.x = 2048 + 120; cloud.y = 200 + Math.floor(Math.random() * (2732 - 400)); clouds.push(cloud); game.addChild(cloud); } // Dragging Phoenix function handleMove(x, y, obj) { if (dragNode && !inBonusStage) { // Clamp to screen, avoid top left 100x100 var nx = Math.max(phoenix.radius + 100, Math.min(2048 - phoenix.radius, x)); var ny = Math.max(phoenix.radius, Math.min(2732 - phoenix.radius, y)); dragNode.x = nx; dragNode.y = ny; } } game.move = handleMove; game.down = function (x, y, obj) { // Only allow drag if not in bonus if (!inBonusStage) { dragNode = phoenix; handleMove(x, y, obj); } }; game.up = function (x, y, obj) { dragNode = null; }; // Main update loop game.update = function () { // Phoenix stage if (!inBonusStage) { // Spawn embers if (LK.ticks % 45 === 0) { spawnEmber(); } // Spawn clouds if (LK.ticks % 90 === 0) { spawnCloud(); } // Update embers for (var i = embers.length - 1; i >= 0; i--) { var ember = embers[i]; ember.update(); // Off screen if (ember.x < -80) { ember.destroy(); embers.splice(i, 1); continue; } // Collect if (phoenix.intersects(ember)) { ember.destroy(); embers.splice(i, 1); score += 1; if (energy < 1) { energy += 0.08; if (energy > 1) energy = 1; } scoreTxt.setText(score); // Animate energy bar tween(energyBarFill, { width: 600 * energy }, { duration: 200, easing: tween.cubicOut }); } } // Update clouds for (var j = clouds.length - 1; j >= 0; j--) { var cloud = clouds[j]; cloud.update(); if (cloud.x < -120) { cloud.destroy(); clouds.splice(j, 1); continue; } // Hit if (phoenix.intersects(cloud)) { // Flash, game over LK.effects.flashScreen(0x222244, 900); LK.showGameOver(); return; } } // Energy full: transform! if (energy >= 1 && !phoenix.isTransformed) { phoenix.isTransformed = true; // Animate: fade out phoenix, fade in falcon tween(phoenix, { alpha: 0 }, { duration: 600, easing: tween.linear, onFinish: function onFinish() { phoenix.visible = false; startBonusStage(); } }); } } // Bonus stage: Falcon if (inBonusStage && falcon) { falcon.update(); // End bonus after 4 seconds or off screen bonusTimer++; if (falcon.x > 2048 + 200 || bonusTimer > 240) { LK.showYouWin(); return; } } }; // Start bonus stage: Falcon function startBonusStage() { inBonusStage = true; // Remove embers/clouds for (var i = 0; i < embers.length; i++) embers[i].destroy(); for (var j = 0; j < clouds.length; j++) clouds[j].destroy(); embers = []; clouds = []; // Place Falcon at Phoenix's last position falcon = new Falcon(); falcon.x = phoenix.x; falcon.y = phoenix.y; game.addChild(falcon); bonusTimer = 0; // Animate falcon in falcon.alpha = 0; tween(falcon, { alpha: 1 }, { duration: 400, easing: tween.linear }); // Animate energy bar out tween(energyBarBg, { alpha: 0 }, { duration: 400 }); tween(energyBarFill, { alpha: 0 }, { duration: 400 }); } // Set initial energy bar width energyBarFill.width = 0; // Place UI elements energyBarBg.y = 120; energyBarFill.y = 120; energyBarBg.x = 724; energyBarFill.x = 724; // Make sure phoenix is above clouds/embers phoenix.zIndex = 10; // Initial score scoreTxt.setText(score); // Ensure all elements are visible and not in top left 100x100 phoenix.x = Math.max(phoenix.radius + 100, phoenix.x); phoenix.y = Math.max(phoenix.radius, phoenix.y);
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Cloud obstacle
var Cloud = Container.expand(function () {
var self = Container.call(this);
var sprite = self.attachAsset('cloud', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = sprite.width / 2;
self.vx = -10;
self.update = function () {
self.x += self.vx;
};
return self;
});
// Ember collectible
var Ember = Container.expand(function () {
var self = Container.call(this);
var sprite = self.attachAsset('ember', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = sprite.width / 2;
self.vx = -12;
self.update = function () {
self.x += self.vx;
};
return self;
});
// Falcon (Richard) class
var Falcon = Container.expand(function () {
var self = Container.call(this);
var sprite = self.attachAsset('falcon', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = sprite.width / 2;
self.vx = 18;
self.update = function () {
self.x += self.vx;
};
return self;
});
// Phoenix (Vixen) class
var Phoenix = Container.expand(function () {
var self = Container.call(this);
var sprite = self.attachAsset('phoenix', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = sprite.width / 2;
self.energy = 0; // 0-1
self.isTransformed = false;
self.update = function () {
// No auto-movement; movement is handled by drag/touch
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x1e2a44
});
/****
* Game Code
****/
// Energy bar fill
// Energy bar background
// Dark cloud (obstacle)
// Ember collectible
// Falcon (Richard)
// Phoenix (Vixen)
// Game state
var phoenix, falcon;
var embers = [];
var clouds = [];
var dragNode = null;
var energyBarBg, energyBarFill;
var score = 0;
var scoreTxt;
var energy = 0; // 0-1
var inBonusStage = false;
var bonusTimer = 0;
var lastPhoenixPos = {
x: 0,
y: 0
};
// Place Phoenix at start
phoenix = new Phoenix();
phoenix.x = 400;
phoenix.y = 1366;
game.addChild(phoenix);
// Energy bar UI
energyBarBg = LK.getAsset('energyBarBg', {
anchorX: 0,
anchorY: 0.5,
x: 724,
y: 120
});
energyBarFill = LK.getAsset('energyBarFill', {
anchorX: 0,
anchorY: 0.5,
x: 724,
y: 120
});
energyBarFill.width = 0;
LK.gui.top.addChild(energyBarBg);
LK.gui.top.addChild(energyBarFill);
// Score UI
scoreTxt = new Text2('0', {
size: 100,
fill: '#fff'
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
scoreTxt.x = 2048 / 2;
scoreTxt.y = 30;
// Helper: spawn ember
function spawnEmber() {
var ember = new Ember();
ember.x = 2048 + 80;
ember.y = 300 + Math.floor(Math.random() * (2732 - 600));
embers.push(ember);
game.addChild(ember);
}
// Helper: spawn cloud
function spawnCloud() {
var cloud = new Cloud();
cloud.x = 2048 + 120;
cloud.y = 200 + Math.floor(Math.random() * (2732 - 400));
clouds.push(cloud);
game.addChild(cloud);
}
// Dragging Phoenix
function handleMove(x, y, obj) {
if (dragNode && !inBonusStage) {
// Clamp to screen, avoid top left 100x100
var nx = Math.max(phoenix.radius + 100, Math.min(2048 - phoenix.radius, x));
var ny = Math.max(phoenix.radius, Math.min(2732 - phoenix.radius, y));
dragNode.x = nx;
dragNode.y = ny;
}
}
game.move = handleMove;
game.down = function (x, y, obj) {
// Only allow drag if not in bonus
if (!inBonusStage) {
dragNode = phoenix;
handleMove(x, y, obj);
}
};
game.up = function (x, y, obj) {
dragNode = null;
};
// Main update loop
game.update = function () {
// Phoenix stage
if (!inBonusStage) {
// Spawn embers
if (LK.ticks % 45 === 0) {
spawnEmber();
}
// Spawn clouds
if (LK.ticks % 90 === 0) {
spawnCloud();
}
// Update embers
for (var i = embers.length - 1; i >= 0; i--) {
var ember = embers[i];
ember.update();
// Off screen
if (ember.x < -80) {
ember.destroy();
embers.splice(i, 1);
continue;
}
// Collect
if (phoenix.intersects(ember)) {
ember.destroy();
embers.splice(i, 1);
score += 1;
if (energy < 1) {
energy += 0.08;
if (energy > 1) energy = 1;
}
scoreTxt.setText(score);
// Animate energy bar
tween(energyBarFill, {
width: 600 * energy
}, {
duration: 200,
easing: tween.cubicOut
});
}
}
// Update clouds
for (var j = clouds.length - 1; j >= 0; j--) {
var cloud = clouds[j];
cloud.update();
if (cloud.x < -120) {
cloud.destroy();
clouds.splice(j, 1);
continue;
}
// Hit
if (phoenix.intersects(cloud)) {
// Flash, game over
LK.effects.flashScreen(0x222244, 900);
LK.showGameOver();
return;
}
}
// Energy full: transform!
if (energy >= 1 && !phoenix.isTransformed) {
phoenix.isTransformed = true;
// Animate: fade out phoenix, fade in falcon
tween(phoenix, {
alpha: 0
}, {
duration: 600,
easing: tween.linear,
onFinish: function onFinish() {
phoenix.visible = false;
startBonusStage();
}
});
}
}
// Bonus stage: Falcon
if (inBonusStage && falcon) {
falcon.update();
// End bonus after 4 seconds or off screen
bonusTimer++;
if (falcon.x > 2048 + 200 || bonusTimer > 240) {
LK.showYouWin();
return;
}
}
};
// Start bonus stage: Falcon
function startBonusStage() {
inBonusStage = true;
// Remove embers/clouds
for (var i = 0; i < embers.length; i++) embers[i].destroy();
for (var j = 0; j < clouds.length; j++) clouds[j].destroy();
embers = [];
clouds = [];
// Place Falcon at Phoenix's last position
falcon = new Falcon();
falcon.x = phoenix.x;
falcon.y = phoenix.y;
game.addChild(falcon);
bonusTimer = 0;
// Animate falcon in
falcon.alpha = 0;
tween(falcon, {
alpha: 1
}, {
duration: 400,
easing: tween.linear
});
// Animate energy bar out
tween(energyBarBg, {
alpha: 0
}, {
duration: 400
});
tween(energyBarFill, {
alpha: 0
}, {
duration: 400
});
}
// Set initial energy bar width
energyBarFill.width = 0;
// Place UI elements
energyBarBg.y = 120;
energyBarFill.y = 120;
energyBarBg.x = 724;
energyBarFill.x = 724;
// Make sure phoenix is above clouds/embers
phoenix.zIndex = 10;
// Initial score
scoreTxt.setText(score);
// Ensure all elements are visible and not in top left 100x100
phoenix.x = Math.max(phoenix.radius + 100, phoenix.x);
phoenix.y = Math.max(phoenix.radius, phoenix.y);