/****
* 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);