/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Energy Core (collectible)
var Core = Container.expand(function () {
var self = Container.call(this);
var coreSprite = self.attachAsset('core', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 6;
self.update = function () {
self.y += self.speed;
};
return self;
});
// Enemy Robot
var Enemy = Container.expand(function () {
var self = Container.call(this);
var enemySprite = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 4 + Math.random() * 2;
self.shootCooldown = 60 + Math.floor(Math.random() * 60); // frames
self.update = function () {
self.y += self.speed;
self.shootCooldown--;
};
return self;
});
// Enemy Bullet
var EnemyBullet = Container.expand(function () {
var self = Container.call(this);
var bulletSprite = self.attachAsset('enemyBullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 13;
self.update = function () {
self.y += self.speed;
};
return self;
});
// Player Mech
var Mech = Container.expand(function () {
var self = Container.call(this);
var mechSprite = self.attachAsset('mech', {
anchorX: 0.5,
anchorY: 0.5
});
self.hp = 3;
self.invincible = false;
// For drag
self.down = function (x, y, obj) {};
self.up = function (x, y, obj) {};
// Flash when hit
self.flash = function () {
if (self.invincible) return;
self.invincible = true;
tween(mechSprite, {
alpha: 0.3
}, {
duration: 100,
easing: tween.linear,
onFinish: function onFinish() {
tween(mechSprite, {
alpha: 1
}, {
duration: 200,
easing: tween.linear,
onFinish: function onFinish() {
self.invincible = false;
}
});
}
});
};
return self;
});
// Player Bullet
var MechBullet = Container.expand(function () {
var self = Container.call(this);
var bulletSprite = self.attachAsset('mechBullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = -22;
self.update = function () {
self.y += self.speed;
};
return self;
});
// Repair Node (objective)
var RepairNode = Container.expand(function () {
var self = Container.call(this);
var repairSprite = self.attachAsset('repair', {
anchorX: 0.5,
anchorY: 0.5
});
self.repairing = false;
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x181c2c
});
/****
* Game Code
****/
// Music
// Sound effects
// Bullet (enemy)
// Bullet (player)
// Repair node (for "repair" objective)
// Energy core collectible
// Enemy robot
// Mech (player)
// Play background music
LK.playMusic('mechaverse_bgm');
// Game state variables
var mech;
var enemies = [];
var mechBullets = [];
var enemyBullets = [];
var cores = [];
var repairNodes = [];
var dragNode = null;
var lastMechX = 0,
lastMechY = 0;
var score = 0;
var coresCollected = 0;
var repairsDone = 0;
var hp = 3;
var gameOver = false;
var canShoot = true;
var shootCooldown = 0;
var level = 1;
var levelProgress = 0;
var levelTarget = 5; // Number of cores to collect or repairs to do per level
// GUI
var scoreTxt = new Text2('Cores: 0', {
size: 90,
fill: "#fff"
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
var hpTxt = new Text2('HP: 3', {
size: 90,
fill: 0xFF6B6B
});
hpTxt.anchor.set(1, 0);
LK.gui.topRight.addChild(hpTxt);
var levelTxt = new Text2('Level 1', {
size: 70,
fill: 0xFFE066
});
levelTxt.anchor.set(0, 0);
LK.gui.topLeft.addChild(levelTxt);
// Place player mech at bottom center
mech = new Mech();
mech.x = 2048 / 2;
mech.y = 2732 - 350;
game.addChild(mech);
// Place first repair node (for variety, alternate between core and repair objectives)
function spawnRepairNode() {
var node = new RepairNode();
node.x = 2048 / 2;
node.y = 600 + Math.random() * 600;
repairNodes.push(node);
game.addChild(node);
}
// Spawn an energy core at a random x
function spawnCore() {
var core = new Core();
core.x = 200 + Math.random() * (2048 - 400);
core.y = -100;
cores.push(core);
game.addChild(core);
}
// Spawn an enemy at a random x
function spawnEnemy() {
var enemy = new Enemy();
enemy.x = 200 + Math.random() * (2048 - 400);
enemy.y = -120;
enemies.push(enemy);
game.addChild(enemy);
}
// Fire a bullet from the mech
function fireMechBullet() {
if (!canShoot) return;
var bullet = new MechBullet();
bullet.x = mech.x;
bullet.y = mech.y - 120;
mechBullets.push(bullet);
game.addChild(bullet);
canShoot = false;
shootCooldown = 12;
LK.getSound('shoot').play();
}
// Touch/drag controls
function handleMove(x, y, obj) {
if (dragNode) {
// Clamp to game area, avoid top 100px (menu)
var nx = Math.max(100, Math.min(2048 - 100, x));
var ny = Math.max(300, Math.min(2732 - 100, y));
dragNode.x = nx;
dragNode.y = ny;
}
}
game.move = handleMove;
game.down = function (x, y, obj) {
// Only allow drag if touch is not in top left 100x100
if (x < 100 && y < 100) return;
dragNode = mech;
handleMove(x, y, obj);
};
game.up = function (x, y, obj) {
dragNode = null;
};
// Tap to shoot (anywhere except top left)
game.tap = function (x, y, obj) {
if (x < 100 && y < 100) return;
fireMechBullet();
};
// For compatibility, also allow shooting on down if not dragging
game.down = function (x, y, obj) {
if (x < 100 && y < 100) return;
dragNode = mech;
handleMove(x, y, obj);
fireMechBullet();
};
// Main game update loop
game.update = function () {
if (gameOver) return;
// Cooldown for shooting
if (!canShoot) {
shootCooldown--;
if (shootCooldown <= 0) {
canShoot = true;
}
}
// Spawn logic: enemies, cores, repair nodes
if (level % 2 === 1) {
// Odd levels: collect cores
if (LK.ticks % Math.max(60, 120 - level * 10) === 0) {
spawnCore();
}
if (LK.ticks % Math.max(90, 180 - level * 12) === 0) {
spawnEnemy();
}
} else {
// Even levels: repair nodes
if (repairNodes.length === 0) {
spawnRepairNode();
}
if (LK.ticks % Math.max(90, 180 - level * 12) === 0) {
spawnEnemy();
}
}
// Update enemies
for (var i = enemies.length - 1; i >= 0; i--) {
var enemy = enemies[i];
enemy.update();
// Enemy shoots
if (enemy.shootCooldown <= 0 && enemy.y > 0) {
var eb = new EnemyBullet();
eb.x = enemy.x;
eb.y = enemy.y + 80;
enemyBullets.push(eb);
game.addChild(eb);
enemy.shootCooldown = 60 + Math.floor(Math.random() * 60);
}
// Remove if off screen
if (enemy.y > 2732 + 100) {
enemy.destroy();
enemies.splice(i, 1);
continue;
}
// Collision: enemy with mech
if (!mech.invincible && enemy.intersects(mech)) {
LK.getSound('hit').play();
mech.flash();
hp--;
hpTxt.setText('HP: ' + hp);
enemy.destroy();
enemies.splice(i, 1);
if (hp <= 0) {
LK.effects.flashScreen(0xff0000, 1000);
LK.showGameOver();
gameOver = true;
return;
}
}
}
// Update mech bullets
for (var i = mechBullets.length - 1; i >= 0; i--) {
var b = mechBullets[i];
b.update();
// Remove if off screen
if (b.y < -60) {
b.destroy();
mechBullets.splice(i, 1);
continue;
}
// Collision: bullet with enemy
for (var j = enemies.length - 1; j >= 0; j--) {
var enemy = enemies[j];
if (b.intersects(enemy)) {
LK.getSound('hit').play();
enemy.destroy();
enemies.splice(j, 1);
b.destroy();
mechBullets.splice(i, 1);
break;
}
}
}
// Update enemy bullets
for (var i = enemyBullets.length - 1; i >= 0; i--) {
var eb = enemyBullets[i];
eb.update();
if (eb.y > 2732 + 60) {
eb.destroy();
enemyBullets.splice(i, 1);
continue;
}
// Collision: enemy bullet with mech
if (!mech.invincible && eb.intersects(mech)) {
LK.getSound('hit').play();
mech.flash();
hp--;
hpTxt.setText('HP: ' + hp);
eb.destroy();
enemyBullets.splice(i, 1);
if (hp <= 0) {
LK.effects.flashScreen(0xff0000, 1000);
LK.showGameOver();
gameOver = true;
return;
}
}
}
// Update cores
for (var i = cores.length - 1; i >= 0; i--) {
var core = cores[i];
core.update();
if (core.y > 2732 + 60) {
core.destroy();
cores.splice(i, 1);
continue;
}
// Collect core
if (core.intersects(mech)) {
LK.getSound('core').play();
score++;
coresCollected++;
scoreTxt.setText('Cores: ' + coresCollected);
core.destroy();
cores.splice(i, 1);
// Level up if enough cores
if (coresCollected >= levelTarget) {
level++;
levelTxt.setText('Level ' + level);
coresCollected = 0;
repairsDone = 0;
scoreTxt.setText(level % 2 === 1 ? 'Cores: 0' : 'Repairs: 0');
// Show win screen if level 5 reached
if (level > 5) {
LK.showYouWin();
gameOver = true;
return;
}
}
}
}
// Update repair nodes
for (var i = repairNodes.length - 1; i >= 0; i--) {
var node = repairNodes[i];
// "Repair" by holding mech over node for 1 second
if (node.intersects(mech)) {
if (!node.repairing) {
node.repairing = true;
tween(node, {
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 1000,
easing: tween.easeInOut,
onFinish: function onFinish() {
LK.getSound('repair').play();
repairsDone++;
scoreTxt.setText('Repairs: ' + repairsDone);
node.destroy();
repairNodes.splice(i, 1);
// Level up if enough repairs
if (repairsDone >= levelTarget) {
level++;
levelTxt.setText('Level ' + level);
coresCollected = 0;
repairsDone = 0;
scoreTxt.setText(level % 2 === 1 ? 'Cores: 0' : 'Repairs: 0');
if (level > 5) {
LK.showYouWin();
gameOver = true;
return;
}
}
}
});
}
} else {
if (node.repairing) {
node.repairing = false;
tween.stop(node, {
scaleX: true,
scaleY: true
});
node.scaleX = 1;
node.scaleY = 1;
}
}
}
};
// Fade in music at start
LK.playMusic('mechaverse_bgm', {
fade: {
start: 0,
end: 0.7,
duration: 1200
}
}); /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Energy Core (collectible)
var Core = Container.expand(function () {
var self = Container.call(this);
var coreSprite = self.attachAsset('core', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 6;
self.update = function () {
self.y += self.speed;
};
return self;
});
// Enemy Robot
var Enemy = Container.expand(function () {
var self = Container.call(this);
var enemySprite = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 4 + Math.random() * 2;
self.shootCooldown = 60 + Math.floor(Math.random() * 60); // frames
self.update = function () {
self.y += self.speed;
self.shootCooldown--;
};
return self;
});
// Enemy Bullet
var EnemyBullet = Container.expand(function () {
var self = Container.call(this);
var bulletSprite = self.attachAsset('enemyBullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 13;
self.update = function () {
self.y += self.speed;
};
return self;
});
// Player Mech
var Mech = Container.expand(function () {
var self = Container.call(this);
var mechSprite = self.attachAsset('mech', {
anchorX: 0.5,
anchorY: 0.5
});
self.hp = 3;
self.invincible = false;
// For drag
self.down = function (x, y, obj) {};
self.up = function (x, y, obj) {};
// Flash when hit
self.flash = function () {
if (self.invincible) return;
self.invincible = true;
tween(mechSprite, {
alpha: 0.3
}, {
duration: 100,
easing: tween.linear,
onFinish: function onFinish() {
tween(mechSprite, {
alpha: 1
}, {
duration: 200,
easing: tween.linear,
onFinish: function onFinish() {
self.invincible = false;
}
});
}
});
};
return self;
});
// Player Bullet
var MechBullet = Container.expand(function () {
var self = Container.call(this);
var bulletSprite = self.attachAsset('mechBullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = -22;
self.update = function () {
self.y += self.speed;
};
return self;
});
// Repair Node (objective)
var RepairNode = Container.expand(function () {
var self = Container.call(this);
var repairSprite = self.attachAsset('repair', {
anchorX: 0.5,
anchorY: 0.5
});
self.repairing = false;
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x181c2c
});
/****
* Game Code
****/
// Music
// Sound effects
// Bullet (enemy)
// Bullet (player)
// Repair node (for "repair" objective)
// Energy core collectible
// Enemy robot
// Mech (player)
// Play background music
LK.playMusic('mechaverse_bgm');
// Game state variables
var mech;
var enemies = [];
var mechBullets = [];
var enemyBullets = [];
var cores = [];
var repairNodes = [];
var dragNode = null;
var lastMechX = 0,
lastMechY = 0;
var score = 0;
var coresCollected = 0;
var repairsDone = 0;
var hp = 3;
var gameOver = false;
var canShoot = true;
var shootCooldown = 0;
var level = 1;
var levelProgress = 0;
var levelTarget = 5; // Number of cores to collect or repairs to do per level
// GUI
var scoreTxt = new Text2('Cores: 0', {
size: 90,
fill: "#fff"
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
var hpTxt = new Text2('HP: 3', {
size: 90,
fill: 0xFF6B6B
});
hpTxt.anchor.set(1, 0);
LK.gui.topRight.addChild(hpTxt);
var levelTxt = new Text2('Level 1', {
size: 70,
fill: 0xFFE066
});
levelTxt.anchor.set(0, 0);
LK.gui.topLeft.addChild(levelTxt);
// Place player mech at bottom center
mech = new Mech();
mech.x = 2048 / 2;
mech.y = 2732 - 350;
game.addChild(mech);
// Place first repair node (for variety, alternate between core and repair objectives)
function spawnRepairNode() {
var node = new RepairNode();
node.x = 2048 / 2;
node.y = 600 + Math.random() * 600;
repairNodes.push(node);
game.addChild(node);
}
// Spawn an energy core at a random x
function spawnCore() {
var core = new Core();
core.x = 200 + Math.random() * (2048 - 400);
core.y = -100;
cores.push(core);
game.addChild(core);
}
// Spawn an enemy at a random x
function spawnEnemy() {
var enemy = new Enemy();
enemy.x = 200 + Math.random() * (2048 - 400);
enemy.y = -120;
enemies.push(enemy);
game.addChild(enemy);
}
// Fire a bullet from the mech
function fireMechBullet() {
if (!canShoot) return;
var bullet = new MechBullet();
bullet.x = mech.x;
bullet.y = mech.y - 120;
mechBullets.push(bullet);
game.addChild(bullet);
canShoot = false;
shootCooldown = 12;
LK.getSound('shoot').play();
}
// Touch/drag controls
function handleMove(x, y, obj) {
if (dragNode) {
// Clamp to game area, avoid top 100px (menu)
var nx = Math.max(100, Math.min(2048 - 100, x));
var ny = Math.max(300, Math.min(2732 - 100, y));
dragNode.x = nx;
dragNode.y = ny;
}
}
game.move = handleMove;
game.down = function (x, y, obj) {
// Only allow drag if touch is not in top left 100x100
if (x < 100 && y < 100) return;
dragNode = mech;
handleMove(x, y, obj);
};
game.up = function (x, y, obj) {
dragNode = null;
};
// Tap to shoot (anywhere except top left)
game.tap = function (x, y, obj) {
if (x < 100 && y < 100) return;
fireMechBullet();
};
// For compatibility, also allow shooting on down if not dragging
game.down = function (x, y, obj) {
if (x < 100 && y < 100) return;
dragNode = mech;
handleMove(x, y, obj);
fireMechBullet();
};
// Main game update loop
game.update = function () {
if (gameOver) return;
// Cooldown for shooting
if (!canShoot) {
shootCooldown--;
if (shootCooldown <= 0) {
canShoot = true;
}
}
// Spawn logic: enemies, cores, repair nodes
if (level % 2 === 1) {
// Odd levels: collect cores
if (LK.ticks % Math.max(60, 120 - level * 10) === 0) {
spawnCore();
}
if (LK.ticks % Math.max(90, 180 - level * 12) === 0) {
spawnEnemy();
}
} else {
// Even levels: repair nodes
if (repairNodes.length === 0) {
spawnRepairNode();
}
if (LK.ticks % Math.max(90, 180 - level * 12) === 0) {
spawnEnemy();
}
}
// Update enemies
for (var i = enemies.length - 1; i >= 0; i--) {
var enemy = enemies[i];
enemy.update();
// Enemy shoots
if (enemy.shootCooldown <= 0 && enemy.y > 0) {
var eb = new EnemyBullet();
eb.x = enemy.x;
eb.y = enemy.y + 80;
enemyBullets.push(eb);
game.addChild(eb);
enemy.shootCooldown = 60 + Math.floor(Math.random() * 60);
}
// Remove if off screen
if (enemy.y > 2732 + 100) {
enemy.destroy();
enemies.splice(i, 1);
continue;
}
// Collision: enemy with mech
if (!mech.invincible && enemy.intersects(mech)) {
LK.getSound('hit').play();
mech.flash();
hp--;
hpTxt.setText('HP: ' + hp);
enemy.destroy();
enemies.splice(i, 1);
if (hp <= 0) {
LK.effects.flashScreen(0xff0000, 1000);
LK.showGameOver();
gameOver = true;
return;
}
}
}
// Update mech bullets
for (var i = mechBullets.length - 1; i >= 0; i--) {
var b = mechBullets[i];
b.update();
// Remove if off screen
if (b.y < -60) {
b.destroy();
mechBullets.splice(i, 1);
continue;
}
// Collision: bullet with enemy
for (var j = enemies.length - 1; j >= 0; j--) {
var enemy = enemies[j];
if (b.intersects(enemy)) {
LK.getSound('hit').play();
enemy.destroy();
enemies.splice(j, 1);
b.destroy();
mechBullets.splice(i, 1);
break;
}
}
}
// Update enemy bullets
for (var i = enemyBullets.length - 1; i >= 0; i--) {
var eb = enemyBullets[i];
eb.update();
if (eb.y > 2732 + 60) {
eb.destroy();
enemyBullets.splice(i, 1);
continue;
}
// Collision: enemy bullet with mech
if (!mech.invincible && eb.intersects(mech)) {
LK.getSound('hit').play();
mech.flash();
hp--;
hpTxt.setText('HP: ' + hp);
eb.destroy();
enemyBullets.splice(i, 1);
if (hp <= 0) {
LK.effects.flashScreen(0xff0000, 1000);
LK.showGameOver();
gameOver = true;
return;
}
}
}
// Update cores
for (var i = cores.length - 1; i >= 0; i--) {
var core = cores[i];
core.update();
if (core.y > 2732 + 60) {
core.destroy();
cores.splice(i, 1);
continue;
}
// Collect core
if (core.intersects(mech)) {
LK.getSound('core').play();
score++;
coresCollected++;
scoreTxt.setText('Cores: ' + coresCollected);
core.destroy();
cores.splice(i, 1);
// Level up if enough cores
if (coresCollected >= levelTarget) {
level++;
levelTxt.setText('Level ' + level);
coresCollected = 0;
repairsDone = 0;
scoreTxt.setText(level % 2 === 1 ? 'Cores: 0' : 'Repairs: 0');
// Show win screen if level 5 reached
if (level > 5) {
LK.showYouWin();
gameOver = true;
return;
}
}
}
}
// Update repair nodes
for (var i = repairNodes.length - 1; i >= 0; i--) {
var node = repairNodes[i];
// "Repair" by holding mech over node for 1 second
if (node.intersects(mech)) {
if (!node.repairing) {
node.repairing = true;
tween(node, {
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 1000,
easing: tween.easeInOut,
onFinish: function onFinish() {
LK.getSound('repair').play();
repairsDone++;
scoreTxt.setText('Repairs: ' + repairsDone);
node.destroy();
repairNodes.splice(i, 1);
// Level up if enough repairs
if (repairsDone >= levelTarget) {
level++;
levelTxt.setText('Level ' + level);
coresCollected = 0;
repairsDone = 0;
scoreTxt.setText(level % 2 === 1 ? 'Cores: 0' : 'Repairs: 0');
if (level > 5) {
LK.showYouWin();
gameOver = true;
return;
}
}
}
});
}
} else {
if (node.repairing) {
node.repairing = false;
tween.stop(node, {
scaleX: true,
scaleY: true
});
node.scaleX = 1;
node.scaleY = 1;
}
}
}
};
// Fade in music at start
LK.playMusic('mechaverse_bgm', {
fade: {
start: 0,
end: 0.7,
duration: 1200
}
});