/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
money: 0,
unlockedSkins: ["ship_default"],
selectedSkin: "ship_default",
upgrades: {
fireRate: 1,
speed: 1,
shield: 1
}
});
/****
* Classes
****/
// Coin (Money)
var Coin = Container.expand(function () {
var self = Container.call(this);
var g = self.attachAsset('coin', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 10;
self.update = function () {
self.y += self.speed;
};
return self;
});
// Enemy Ship
var EnemyShip = Container.expand(function () {
var self = Container.call(this);
var g = self.attachAsset('enemy_ship', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = g.width;
self.height = g.height;
self.speed = 6 + Math.random() * 4 + wave * 0.5;
self.hp = 1 + Math.floor(wave / 3);
self.update = function () {
self.y += self.speed;
};
self.takeHit = function () {
self.hp--;
if (self.hp <= 0) {
LK.getSound('enemy_explode').play();
spawnCoin(self.x, self.y);
LK.setScore(LK.getScore() + 1);
updateScoreText();
self.destroy();
var idx = enemies.indexOf(self);
if (idx !== -1) enemies.splice(idx, 1);
} else {
LK.effects.flashObject(self, 0xffffff, 200);
}
};
return self;
});
// Player Bullet
var PlayerBullet = Container.expand(function () {
var self = Container.call(this);
var g = self.attachAsset('player_bullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = -36;
self.update = function () {
self.y += self.speed;
};
return self;
});
// Player Ship
var PlayerShip = Container.expand(function () {
var self = Container.call(this);
// Attach current skin asset
self.skinId = storage.selectedSkin || 'ship_default';
self.skin = self.attachAsset(self.skinId, {
anchorX: 0.5,
anchorY: 0.5
});
self.width = self.skin.width;
self.height = self.skin.height;
self.speed = 18 + 6 * (storage.upgrades.speed - 1); // base speed + upgrade
self.fireCooldown = 0;
self.fireRate = 18 - 3 * (storage.upgrades.fireRate - 1); // lower is faster
self.shield = storage.upgrades.shield;
self.invulnTicks = 0;
self.setSkin = function (skinId) {
if (self.skin) self.removeChild(self.skin);
self.skinId = skinId;
self.skin = self.attachAsset(skinId, {
anchorX: 0.5,
anchorY: 0.5
});
self.width = self.skin.width;
self.height = self.skin.height;
};
self.update = function () {
if (self.invulnTicks > 0) {
self.invulnTicks--;
if (self.invulnTicks % 6 < 3) {
self.skin.alpha = 0.4;
} else {
self.skin.alpha = 1;
}
} else {
self.skin.alpha = 1;
}
if (self.fireCooldown > 0) self.fireCooldown--;
};
self.shoot = function () {
if (self.fireCooldown > 0) return;
var bullet = new PlayerBullet();
bullet.x = self.x;
bullet.y = self.y - self.height / 2 - bullet.height / 2;
playerBullets.push(bullet);
game.addChild(bullet);
LK.getSound('shoot').play();
self.fireCooldown = self.fireRate;
if (typeof bulletsFired === "undefined") {
bulletsFired = 0;
}
bulletsFired++;
};
self.takeHit = function () {
if (self.invulnTicks > 0) return;
self.shield--;
if (self.shield <= 0) {
self.invulnTicks = 60;
LK.effects.flashObject(self, 0xff0000, 500);
// Do not trigger game over, just flash and keep playing
} else {
self.invulnTicks = 60;
LK.effects.flashObject(self, 0xff0000, 500);
}
updateShieldText();
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000010
});
/****
* Game Code
****/
// Game state variables
// Spaceship base skin (default)
// Spaceship skin 2 (unlockable)
// Spaceship skin 3 (unlockable)
// Player bullet
// Enemy ship
// Enemy bullet
// Coin (money)
// Sounds
// Music
var player;
var playerBullets = [];
var enemies = [];
var coins = [];
var dragNode = null;
var wave = 1;
var waveTicks = 0;
var waveCooldown = 120;
var scoreTxt, moneyTxt, shieldTxt;
var menuOpen = false;
// GUI
scoreTxt = new Text2('0', {
size: 100,
fill: "#fff"
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
moneyTxt = new Text2('$0', {
size: 80,
fill: 0xFFE066
});
moneyTxt.anchor.set(1, 0);
LK.gui.topRight.addChild(moneyTxt);
shieldTxt = new Text2('🛡️3', {
size: 80,
fill: 0x66CCFF
});
shieldTxt.anchor.set(0, 0);
LK.gui.topLeft.addChild(shieldTxt);
// Menu overlay
var menuOverlay = new Container();
menuOverlay.visible = false;
LK.gui.center.addChild(menuOverlay);
// Menu background
var menuBg = LK.getAsset('ship_default', {
width: 1200,
height: 1600,
color: 0x222233,
anchorX: 0.5,
anchorY: 0.5
});
menuBg.alpha = 0.98;
menuOverlay.addChild(menuBg);
// Menu title
var menuTitle = new Text2('Upgrades & Skins', {
size: 100,
fill: "#fff"
});
menuTitle.anchor.set(0.5, 0);
menuTitle.y = -700;
menuOverlay.addChild(menuTitle);
// Money display in menu
var menuMoney = new Text2('$0', {
size: 80,
fill: 0xFFE066
});
menuMoney.anchor.set(0.5, 0);
menuMoney.y = -600;
menuOverlay.addChild(menuMoney);
// Skins section
var skinTitle = new Text2('Skins', {
size: 80,
fill: "#fff"
});
skinTitle.anchor.set(0.5, 0);
skinTitle.y = -500;
menuOverlay.addChild(skinTitle);
var skinBtns = [];
var skinIds = ['ship_default', 'ship_red', 'ship_green'];
var skinCosts = {
ship_default: 0,
ship_red: 30,
ship_green: 60
};
for (var i = 0; i < skinIds.length; i++) {
(function (i) {
var sid = skinIds[i];
var btn = new Container();
btn.x = -300 + i * 300;
btn.y = -350;
var skinAsset = btn.attachAsset(sid, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.7,
scaleY: 0.7
});
var label = new Text2('', {
size: 40,
fill: "#fff"
});
label.anchor.set(0.5, 0);
label.y = 80;
btn.addChild(label);
btn.down = function (x, y, obj) {
if (storage.unlockedSkins.indexOf(sid) !== -1) {
storage.selectedSkin = sid;
player.setSkin(sid);
updateMenu();
} else if (storage.money >= skinCosts[sid]) {
storage.money -= skinCosts[sid];
storage.unlockedSkins.push(sid);
storage.selectedSkin = sid;
player.setSkin(sid);
updateMenu();
updateMoneyText();
} else {
showNotification("Not Enough Money!");
}
};
menuOverlay.addChild(btn);
skinBtns.push({
btn: btn,
label: label,
sid: sid
});
})(i);
}
// Upgrades section
var upgradeTitle = new Text2('Upgrades', {
size: 80,
fill: "#fff"
});
upgradeTitle.anchor.set(0.5, 0);
upgradeTitle.y = -150;
menuOverlay.addChild(upgradeTitle);
var upgradeNames = ['fireRate', 'speed', 'shield'];
var upgradeLabels = ['Fire Rate', 'Speed', 'Shield'];
var upgradeCosts = {
fireRate: 20,
speed: 20,
shield: 25
};
var upgradeMax = {
fireRate: 5,
speed: 5,
shield: 5
};
var upgradeBtns = [];
for (var i = 0; i < upgradeNames.length; i++) {
(function (i) {
var upg = upgradeNames[i];
var btn = new Container();
btn.x = -300 + i * 300;
btn.y = 0;
var label = new Text2('', {
size: 40,
fill: "#fff"
});
label.anchor.set(0.5, 0);
btn.addChild(label);
btn.down = function (x, y, obj) {
if (storage.upgrades[upg] < upgradeMax[upg] && storage.money >= upgradeCosts[upg]) {
storage.money -= upgradeCosts[upg];
storage.upgrades[upg]++;
updateMenu();
updateMoneyText();
if (upg === 'fireRate') player.fireRate = 18 - 3 * (storage.upgrades.fireRate - 1);
if (upg === 'speed') player.speed = 18 + 6 * (storage.upgrades.speed - 1);
if (upg === 'shield') {
player.shield = storage.upgrades.shield;
updateShieldText();
}
} else if (storage.upgrades[upg] < upgradeMax[upg] && storage.money < upgradeCosts[upg]) {
showNotification("Not Enough Money!");
}
};
btn.y = 100;
btn.x = -300 + i * 300;
menuOverlay.addChild(btn);
upgradeBtns.push({
btn: btn,
label: label,
upg: upg
});
})(i);
}
// Menu close button
var closeBtn = new Text2('Close', {
size: 80,
fill: "#fff"
});
closeBtn.anchor.set(0.5, 0);
closeBtn.y = 600;
closeBtn.down = function (x, y, obj) {
// If menu was opened from title, return to title screen only
if (menuOverlay._openedFromTitle) {
menuOverlay.visible = false;
menuOpen = false;
titleOverlay.visible = true;
// menuBtn.visible = false; // Do NOT hide menu button on title screen
} else {
// If menu was opened during gameplay, hide both overlays (return to gameplay)
menuOverlay.visible = false;
menuOpen = false;
titleOverlay.visible = false;
// menuBtn.visible = true; // Do NOT show menu button again in gameplay, it's always visible
}
};
menuOverlay.addChild(closeBtn);
// Menu button (for in-game, now added to GUI)
var menuBtn = new Text2('Menu', {
size: 80,
fill: "#fff"
});
menuBtn.anchor.set(1, 1); // anchor bottom right
menuBtn.x = 0;
menuBtn.y = 0;
// Add to GUI bottomRight (not topLeft, to avoid platform menu)
LK.gui.bottomRight.addChild(menuBtn);
// Menu button event
menuBtn.down = function (x, y, obj) {
// Only allow menuOverlay to open in-game, not from title except via Skins/Menu buttons
if (titleOverlay.visible) {
// Do nothing if on title screen (Skins/Menu buttons handle opening)
return;
} else {
// In-game: show title overlay and menu overlay together
titleOverlay.visible = true;
menuOverlay.visible = true;
menuOpen = true;
// menuBtn.visible = false; // Do NOT hide menu button when menu is open
updateMenu();
// Mark that menu was opened from gameplay
menuOverlay._openedFromTitle = false;
}
};
// Menu button is now always visible, even when menu is open
// Update menu info
function updateMenu() {
menuMoney.setText('$' + storage.money);
for (var i = 0; i < skinBtns.length; i++) {
var sid = skinBtns[i].sid;
if (storage.unlockedSkins.indexOf(sid) !== -1) {
skinBtns[i].label.setText(storage.selectedSkin === sid ? 'Selected' : 'Unlocked');
} else {
skinBtns[i].label.setText('Unlock: $' + skinCosts[sid]);
}
}
for (var i = 0; i < upgradeBtns.length; i++) {
var upg = upgradeBtns[i].upg;
var lvl = storage.upgrades[upg];
if (lvl >= upgradeMax[upg]) {
upgradeBtns[i].label.setText(upgradeLabels[i] + ': MAX');
} else {
upgradeBtns[i].label.setText(upgradeLabels[i] + ': ' + lvl + ' ($' + upgradeCosts[upg] + ')');
}
}
}
// Update score, money, shield
function updateScoreText() {
scoreTxt.setText(LK.getScore());
}
function updateMoneyText() {
moneyTxt.setText('$' + storage.money);
}
function updateShieldText() {
shieldTxt.setText('🛡️' + player.shield);
}
// Spawn coin
function spawnCoin(x, y) {
var coin = new Coin();
coin.x = x;
coin.y = y;
coins.push(coin);
game.addChild(coin);
}
// Start game
function startGame() {
// Reset state
LK.setScore(0);
updateScoreText();
wave = 1;
waveTicks = 0;
playerBullets = [];
enemies = [];
enemyBullets = [];
coins = [];
menuOverlay.visible = false;
menuOpen = false;
// Remove all children except GUI
for (var i = game.children.length - 1; i >= 0; i--) {
game.children[i].destroy();
}
// Create player
player = new PlayerShip();
player.x = 2048 / 2;
player.y = 2732 - 300;
game.addChild(player);
updateShieldText();
updateMoneyText();
updateMenu(); // Ensure menu money display is correct at game start
}
// Touch controls
game.down = function (x, y, obj) {
if (menuOpen) return;
if (!player) return;
// Only drag if touch is on player
var px = player.x,
py = player.y;
var dx = x - px,
dy = y - py;
if (dx * dx + dy * dy < player.width / 2 * (player.width / 2)) {
dragNode = player;
}
};
game.up = function (x, y, obj) {
dragNode = null;
};
game.move = function (x, y, obj) {
if (menuOpen) return;
if (dragNode === player) {
// Clamp to screen
var nx = Math.max(player.width / 2, Math.min(2048 - player.width / 2, x));
var ny = Math.max(player.height / 2 + 100, Math.min(2732 - player.height / 2, y));
player.x = nx;
player.y = ny;
}
};
// Tap to shoot
game.tap = function (x, y, obj) {
if (menuOpen) return;
if (!player) return;
player.shoot();
};
// Main update loop
game.update = function () {
if (menuOpen) return;
// Player update
if (player) player.update();
// Player bullets
if (typeof bulletsFired === "undefined") {
var bulletsFired = 0;
}
for (var i = playerBullets.length - 1; i >= 0; i--) {
var b = playerBullets[i];
b.update();
// Remove if off screen
if (b.y < -b.height) {
b.destroy();
playerBullets.splice(i, 1);
continue;
}
// Collide with enemies
for (var j = enemies.length - 1; j >= 0; j--) {
var e = enemies[j];
if (b.intersects(e)) {
e.takeHit();
b.destroy();
playerBullets.splice(i, 1);
break;
}
}
}
// Win if 10 bullets have been fired
if (bulletsFired >= 10) {
LK.showYouWin();
}
// Enemy update
for (var i = enemies.length - 1; i >= 0; i--) {
var e = enemies[i];
e.update();
if (e.y > 2732 + e.height) {
e.destroy();
enemies.splice(i, 1);
continue;
}
// Collide with player
if (player && e.intersects(player)) {
e.destroy();
enemies.splice(i, 1);
player.takeHit();
continue;
}
}
// (Enemy bullets removed)
// Coins
for (var i = coins.length - 1; i >= 0; i--) {
var c = coins[i];
c.update();
if (c.y > 2732 + c.height) {
c.destroy();
coins.splice(i, 1);
continue;
}
if (player && c.intersects(player)) {
LK.getSound('coin').play();
storage.money += 1;
updateMoneyText();
updateMenu(); // Also update menu money display
c.destroy();
coins.splice(i, 1);
continue;
}
}
// Spawn enemies in waves
if (enemies.length === 0 && waveCooldown === 0) {
wave++;
waveTicks = 0;
waveCooldown = 60;
}
if (waveCooldown > 0) {
waveCooldown--;
if (waveCooldown === 0) {
// Spawn new wave
var n = 2 + Math.floor(wave * 0.7);
for (var i = 0; i < n; i++) {
var e = new EnemyShip();
e.x = 200 + Math.random() * (2048 - 400);
e.y = -200 - i * 120;
enemies.push(e);
game.addChild(e);
}
}
}
};
// Title screen overlay
var titleOverlay = new Container();
LK.gui.center.addChild(titleOverlay);
var titleBg = LK.getAsset('ship_default', {
width: 1200,
height: 1600,
color: 0x222233,
anchorX: 0.5,
anchorY: 0.5
});
titleBg.alpha = 0.98;
titleOverlay.addChild(titleBg);
var titleText = new Text2('Space Shooter', {
size: 120,
fill: "#fff"
});
titleText.anchor.set(0.5, 0);
titleText.y = -600;
titleOverlay.addChild(titleText);
var notifText = new Text2('', {
size: 60,
fill: "#ff0"
});
notifText.anchor.set(0.5, 0);
notifText.y = -450;
titleOverlay.addChild(notifText);
function showNotification(msg) {
notifText.setText(msg);
notifText.visible = true;
LK.setTimeout(function () {
notifText.visible = false;
}, 2000);
}
// Settings button
var settingsBtn = new Text2('Settings', {
size: 100,
fill: "#fff"
});
settingsBtn.anchor.set(0.5, 0);
settingsBtn.y = -200;
// --- Settings Overlay ---
var settingsOverlay = new Container();
settingsOverlay.visible = false;
LK.gui.center.addChild(settingsOverlay);
var settingsBg = LK.getAsset('ship_default', {
width: 1000,
height: 1200,
color: 0x222233,
anchorX: 0.5,
anchorY: 0.5
});
settingsBg.alpha = 0.98;
settingsOverlay.addChild(settingsBg);
var settingsTitle = new Text2('Settings', {
size: 100,
fill: "#fff"
});
settingsTitle.anchor.set(0.5, 0);
settingsTitle.y = -500;
settingsOverlay.addChild(settingsTitle);
// Example setting: Music toggle
var musicLabel = new Text2('Music: On', {
size: 70,
fill: "#fff"
});
musicLabel.anchor.set(0.5, 0);
musicLabel.y = -200;
settingsOverlay.addChild(musicLabel);
var musicOn = true;
musicLabel.down = function () {
musicOn = !musicOn;
musicLabel.setText('Music: ' + (musicOn ? 'On' : 'Off'));
if (musicOn) {
LK.playMusic('bgmusic');
} else {
LK.stopMusic();
}
showNotification('Music ' + (musicOn ? 'enabled' : 'disabled'));
};
// Example setting: Reset progress
var resetBtn = new Text2('Reset Progress', {
size: 70,
fill: 0xFF6666
});
resetBtn.anchor.set(0.5, 0);
resetBtn.y = 0;
resetBtn.down = function () {
storage.money = 0;
storage.unlockedSkins = ["ship_default"];
storage.selectedSkin = "ship_default";
storage.upgrades = {
fireRate: 1,
speed: 1,
shield: 1
};
showNotification("Progress reset!");
updateMenu();
updateMoneyText();
if (player) {
player.setSkin("ship_default");
player.shield = 1;
updateShieldText();
}
};
// Settings close button
var settingsCloseBtn = new Text2('Close', {
size: 80,
fill: "#fff"
});
settingsCloseBtn.anchor.set(0.5, 0);
settingsCloseBtn.y = 400;
settingsCloseBtn.down = function () {
settingsOverlay.visible = false;
titleOverlay.visible = true;
};
settingsOverlay.addChild(settingsTitle);
settingsOverlay.addChild(musicLabel);
settingsOverlay.addChild(resetBtn);
settingsOverlay.addChild(settingsCloseBtn);
// Settings button
settingsBtn.down = function () {
titleOverlay.visible = false;
settingsOverlay.visible = true;
};
titleOverlay.addChild(settingsBtn);
// Skins button
var skinsBtn = new Text2('Skins', {
size: 100,
fill: "#fff"
});
skinsBtn.anchor.set(0.5, 0);
skinsBtn.y = 0;
skinsBtn.down = function () {
// Only show menuOverlay as skins menu if Skins is clicked
titleOverlay.visible = false;
menuOverlay.visible = true;
menuOpen = true;
// menuBtn.visible = false; // Do NOT hide menu button when menu is open from title
updateMenu();
// Mark that menu was opened from Skins
menuOverlay._openedFromTitle = true;
};
titleOverlay.addChild(skinsBtn);
// (Menu button on title screen removed as requested)
// Play button
var playBtn = new Text2('Play', {
size: 100,
fill: "#fff"
});
playBtn.anchor.set(0.5, 0);
playBtn.y = 200;
playBtn.down = function () {
titleOverlay.visible = false;
startGame();
LK.playMusic('bgmusic', {
fade: {
start: 0,
end: 1,
duration: 1000
}
});
};
titleOverlay.addChild(playBtn);
// Show title screen on load
titleOverlay.visible = true;
menuOverlay.visible = false;
// Ensure menuOpen is false on load (prevents accidental menu/skins open)
menuOpen = false;
// Start game and music only after Play is pressed
// (moved to Play button)
// Reset game on game over
LK.on('gameover', function () {
menuOverlay.visible = false;
menuOpen = false;
titleOverlay.visible = true;
settingsOverlay.visible = false;
});
LK.on('youwin', function () {
menuOverlay.visible = false;
menuOpen = false;
titleOverlay.visible = true;
settingsOverlay.visible = false;
}); /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
money: 0,
unlockedSkins: ["ship_default"],
selectedSkin: "ship_default",
upgrades: {
fireRate: 1,
speed: 1,
shield: 1
}
});
/****
* Classes
****/
// Coin (Money)
var Coin = Container.expand(function () {
var self = Container.call(this);
var g = self.attachAsset('coin', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 10;
self.update = function () {
self.y += self.speed;
};
return self;
});
// Enemy Ship
var EnemyShip = Container.expand(function () {
var self = Container.call(this);
var g = self.attachAsset('enemy_ship', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = g.width;
self.height = g.height;
self.speed = 6 + Math.random() * 4 + wave * 0.5;
self.hp = 1 + Math.floor(wave / 3);
self.update = function () {
self.y += self.speed;
};
self.takeHit = function () {
self.hp--;
if (self.hp <= 0) {
LK.getSound('enemy_explode').play();
spawnCoin(self.x, self.y);
LK.setScore(LK.getScore() + 1);
updateScoreText();
self.destroy();
var idx = enemies.indexOf(self);
if (idx !== -1) enemies.splice(idx, 1);
} else {
LK.effects.flashObject(self, 0xffffff, 200);
}
};
return self;
});
// Player Bullet
var PlayerBullet = Container.expand(function () {
var self = Container.call(this);
var g = self.attachAsset('player_bullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = -36;
self.update = function () {
self.y += self.speed;
};
return self;
});
// Player Ship
var PlayerShip = Container.expand(function () {
var self = Container.call(this);
// Attach current skin asset
self.skinId = storage.selectedSkin || 'ship_default';
self.skin = self.attachAsset(self.skinId, {
anchorX: 0.5,
anchorY: 0.5
});
self.width = self.skin.width;
self.height = self.skin.height;
self.speed = 18 + 6 * (storage.upgrades.speed - 1); // base speed + upgrade
self.fireCooldown = 0;
self.fireRate = 18 - 3 * (storage.upgrades.fireRate - 1); // lower is faster
self.shield = storage.upgrades.shield;
self.invulnTicks = 0;
self.setSkin = function (skinId) {
if (self.skin) self.removeChild(self.skin);
self.skinId = skinId;
self.skin = self.attachAsset(skinId, {
anchorX: 0.5,
anchorY: 0.5
});
self.width = self.skin.width;
self.height = self.skin.height;
};
self.update = function () {
if (self.invulnTicks > 0) {
self.invulnTicks--;
if (self.invulnTicks % 6 < 3) {
self.skin.alpha = 0.4;
} else {
self.skin.alpha = 1;
}
} else {
self.skin.alpha = 1;
}
if (self.fireCooldown > 0) self.fireCooldown--;
};
self.shoot = function () {
if (self.fireCooldown > 0) return;
var bullet = new PlayerBullet();
bullet.x = self.x;
bullet.y = self.y - self.height / 2 - bullet.height / 2;
playerBullets.push(bullet);
game.addChild(bullet);
LK.getSound('shoot').play();
self.fireCooldown = self.fireRate;
if (typeof bulletsFired === "undefined") {
bulletsFired = 0;
}
bulletsFired++;
};
self.takeHit = function () {
if (self.invulnTicks > 0) return;
self.shield--;
if (self.shield <= 0) {
self.invulnTicks = 60;
LK.effects.flashObject(self, 0xff0000, 500);
// Do not trigger game over, just flash and keep playing
} else {
self.invulnTicks = 60;
LK.effects.flashObject(self, 0xff0000, 500);
}
updateShieldText();
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000010
});
/****
* Game Code
****/
// Game state variables
// Spaceship base skin (default)
// Spaceship skin 2 (unlockable)
// Spaceship skin 3 (unlockable)
// Player bullet
// Enemy ship
// Enemy bullet
// Coin (money)
// Sounds
// Music
var player;
var playerBullets = [];
var enemies = [];
var coins = [];
var dragNode = null;
var wave = 1;
var waveTicks = 0;
var waveCooldown = 120;
var scoreTxt, moneyTxt, shieldTxt;
var menuOpen = false;
// GUI
scoreTxt = new Text2('0', {
size: 100,
fill: "#fff"
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
moneyTxt = new Text2('$0', {
size: 80,
fill: 0xFFE066
});
moneyTxt.anchor.set(1, 0);
LK.gui.topRight.addChild(moneyTxt);
shieldTxt = new Text2('🛡️3', {
size: 80,
fill: 0x66CCFF
});
shieldTxt.anchor.set(0, 0);
LK.gui.topLeft.addChild(shieldTxt);
// Menu overlay
var menuOverlay = new Container();
menuOverlay.visible = false;
LK.gui.center.addChild(menuOverlay);
// Menu background
var menuBg = LK.getAsset('ship_default', {
width: 1200,
height: 1600,
color: 0x222233,
anchorX: 0.5,
anchorY: 0.5
});
menuBg.alpha = 0.98;
menuOverlay.addChild(menuBg);
// Menu title
var menuTitle = new Text2('Upgrades & Skins', {
size: 100,
fill: "#fff"
});
menuTitle.anchor.set(0.5, 0);
menuTitle.y = -700;
menuOverlay.addChild(menuTitle);
// Money display in menu
var menuMoney = new Text2('$0', {
size: 80,
fill: 0xFFE066
});
menuMoney.anchor.set(0.5, 0);
menuMoney.y = -600;
menuOverlay.addChild(menuMoney);
// Skins section
var skinTitle = new Text2('Skins', {
size: 80,
fill: "#fff"
});
skinTitle.anchor.set(0.5, 0);
skinTitle.y = -500;
menuOverlay.addChild(skinTitle);
var skinBtns = [];
var skinIds = ['ship_default', 'ship_red', 'ship_green'];
var skinCosts = {
ship_default: 0,
ship_red: 30,
ship_green: 60
};
for (var i = 0; i < skinIds.length; i++) {
(function (i) {
var sid = skinIds[i];
var btn = new Container();
btn.x = -300 + i * 300;
btn.y = -350;
var skinAsset = btn.attachAsset(sid, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.7,
scaleY: 0.7
});
var label = new Text2('', {
size: 40,
fill: "#fff"
});
label.anchor.set(0.5, 0);
label.y = 80;
btn.addChild(label);
btn.down = function (x, y, obj) {
if (storage.unlockedSkins.indexOf(sid) !== -1) {
storage.selectedSkin = sid;
player.setSkin(sid);
updateMenu();
} else if (storage.money >= skinCosts[sid]) {
storage.money -= skinCosts[sid];
storage.unlockedSkins.push(sid);
storage.selectedSkin = sid;
player.setSkin(sid);
updateMenu();
updateMoneyText();
} else {
showNotification("Not Enough Money!");
}
};
menuOverlay.addChild(btn);
skinBtns.push({
btn: btn,
label: label,
sid: sid
});
})(i);
}
// Upgrades section
var upgradeTitle = new Text2('Upgrades', {
size: 80,
fill: "#fff"
});
upgradeTitle.anchor.set(0.5, 0);
upgradeTitle.y = -150;
menuOverlay.addChild(upgradeTitle);
var upgradeNames = ['fireRate', 'speed', 'shield'];
var upgradeLabels = ['Fire Rate', 'Speed', 'Shield'];
var upgradeCosts = {
fireRate: 20,
speed: 20,
shield: 25
};
var upgradeMax = {
fireRate: 5,
speed: 5,
shield: 5
};
var upgradeBtns = [];
for (var i = 0; i < upgradeNames.length; i++) {
(function (i) {
var upg = upgradeNames[i];
var btn = new Container();
btn.x = -300 + i * 300;
btn.y = 0;
var label = new Text2('', {
size: 40,
fill: "#fff"
});
label.anchor.set(0.5, 0);
btn.addChild(label);
btn.down = function (x, y, obj) {
if (storage.upgrades[upg] < upgradeMax[upg] && storage.money >= upgradeCosts[upg]) {
storage.money -= upgradeCosts[upg];
storage.upgrades[upg]++;
updateMenu();
updateMoneyText();
if (upg === 'fireRate') player.fireRate = 18 - 3 * (storage.upgrades.fireRate - 1);
if (upg === 'speed') player.speed = 18 + 6 * (storage.upgrades.speed - 1);
if (upg === 'shield') {
player.shield = storage.upgrades.shield;
updateShieldText();
}
} else if (storage.upgrades[upg] < upgradeMax[upg] && storage.money < upgradeCosts[upg]) {
showNotification("Not Enough Money!");
}
};
btn.y = 100;
btn.x = -300 + i * 300;
menuOverlay.addChild(btn);
upgradeBtns.push({
btn: btn,
label: label,
upg: upg
});
})(i);
}
// Menu close button
var closeBtn = new Text2('Close', {
size: 80,
fill: "#fff"
});
closeBtn.anchor.set(0.5, 0);
closeBtn.y = 600;
closeBtn.down = function (x, y, obj) {
// If menu was opened from title, return to title screen only
if (menuOverlay._openedFromTitle) {
menuOverlay.visible = false;
menuOpen = false;
titleOverlay.visible = true;
// menuBtn.visible = false; // Do NOT hide menu button on title screen
} else {
// If menu was opened during gameplay, hide both overlays (return to gameplay)
menuOverlay.visible = false;
menuOpen = false;
titleOverlay.visible = false;
// menuBtn.visible = true; // Do NOT show menu button again in gameplay, it's always visible
}
};
menuOverlay.addChild(closeBtn);
// Menu button (for in-game, now added to GUI)
var menuBtn = new Text2('Menu', {
size: 80,
fill: "#fff"
});
menuBtn.anchor.set(1, 1); // anchor bottom right
menuBtn.x = 0;
menuBtn.y = 0;
// Add to GUI bottomRight (not topLeft, to avoid platform menu)
LK.gui.bottomRight.addChild(menuBtn);
// Menu button event
menuBtn.down = function (x, y, obj) {
// Only allow menuOverlay to open in-game, not from title except via Skins/Menu buttons
if (titleOverlay.visible) {
// Do nothing if on title screen (Skins/Menu buttons handle opening)
return;
} else {
// In-game: show title overlay and menu overlay together
titleOverlay.visible = true;
menuOverlay.visible = true;
menuOpen = true;
// menuBtn.visible = false; // Do NOT hide menu button when menu is open
updateMenu();
// Mark that menu was opened from gameplay
menuOverlay._openedFromTitle = false;
}
};
// Menu button is now always visible, even when menu is open
// Update menu info
function updateMenu() {
menuMoney.setText('$' + storage.money);
for (var i = 0; i < skinBtns.length; i++) {
var sid = skinBtns[i].sid;
if (storage.unlockedSkins.indexOf(sid) !== -1) {
skinBtns[i].label.setText(storage.selectedSkin === sid ? 'Selected' : 'Unlocked');
} else {
skinBtns[i].label.setText('Unlock: $' + skinCosts[sid]);
}
}
for (var i = 0; i < upgradeBtns.length; i++) {
var upg = upgradeBtns[i].upg;
var lvl = storage.upgrades[upg];
if (lvl >= upgradeMax[upg]) {
upgradeBtns[i].label.setText(upgradeLabels[i] + ': MAX');
} else {
upgradeBtns[i].label.setText(upgradeLabels[i] + ': ' + lvl + ' ($' + upgradeCosts[upg] + ')');
}
}
}
// Update score, money, shield
function updateScoreText() {
scoreTxt.setText(LK.getScore());
}
function updateMoneyText() {
moneyTxt.setText('$' + storage.money);
}
function updateShieldText() {
shieldTxt.setText('🛡️' + player.shield);
}
// Spawn coin
function spawnCoin(x, y) {
var coin = new Coin();
coin.x = x;
coin.y = y;
coins.push(coin);
game.addChild(coin);
}
// Start game
function startGame() {
// Reset state
LK.setScore(0);
updateScoreText();
wave = 1;
waveTicks = 0;
playerBullets = [];
enemies = [];
enemyBullets = [];
coins = [];
menuOverlay.visible = false;
menuOpen = false;
// Remove all children except GUI
for (var i = game.children.length - 1; i >= 0; i--) {
game.children[i].destroy();
}
// Create player
player = new PlayerShip();
player.x = 2048 / 2;
player.y = 2732 - 300;
game.addChild(player);
updateShieldText();
updateMoneyText();
updateMenu(); // Ensure menu money display is correct at game start
}
// Touch controls
game.down = function (x, y, obj) {
if (menuOpen) return;
if (!player) return;
// Only drag if touch is on player
var px = player.x,
py = player.y;
var dx = x - px,
dy = y - py;
if (dx * dx + dy * dy < player.width / 2 * (player.width / 2)) {
dragNode = player;
}
};
game.up = function (x, y, obj) {
dragNode = null;
};
game.move = function (x, y, obj) {
if (menuOpen) return;
if (dragNode === player) {
// Clamp to screen
var nx = Math.max(player.width / 2, Math.min(2048 - player.width / 2, x));
var ny = Math.max(player.height / 2 + 100, Math.min(2732 - player.height / 2, y));
player.x = nx;
player.y = ny;
}
};
// Tap to shoot
game.tap = function (x, y, obj) {
if (menuOpen) return;
if (!player) return;
player.shoot();
};
// Main update loop
game.update = function () {
if (menuOpen) return;
// Player update
if (player) player.update();
// Player bullets
if (typeof bulletsFired === "undefined") {
var bulletsFired = 0;
}
for (var i = playerBullets.length - 1; i >= 0; i--) {
var b = playerBullets[i];
b.update();
// Remove if off screen
if (b.y < -b.height) {
b.destroy();
playerBullets.splice(i, 1);
continue;
}
// Collide with enemies
for (var j = enemies.length - 1; j >= 0; j--) {
var e = enemies[j];
if (b.intersects(e)) {
e.takeHit();
b.destroy();
playerBullets.splice(i, 1);
break;
}
}
}
// Win if 10 bullets have been fired
if (bulletsFired >= 10) {
LK.showYouWin();
}
// Enemy update
for (var i = enemies.length - 1; i >= 0; i--) {
var e = enemies[i];
e.update();
if (e.y > 2732 + e.height) {
e.destroy();
enemies.splice(i, 1);
continue;
}
// Collide with player
if (player && e.intersects(player)) {
e.destroy();
enemies.splice(i, 1);
player.takeHit();
continue;
}
}
// (Enemy bullets removed)
// Coins
for (var i = coins.length - 1; i >= 0; i--) {
var c = coins[i];
c.update();
if (c.y > 2732 + c.height) {
c.destroy();
coins.splice(i, 1);
continue;
}
if (player && c.intersects(player)) {
LK.getSound('coin').play();
storage.money += 1;
updateMoneyText();
updateMenu(); // Also update menu money display
c.destroy();
coins.splice(i, 1);
continue;
}
}
// Spawn enemies in waves
if (enemies.length === 0 && waveCooldown === 0) {
wave++;
waveTicks = 0;
waveCooldown = 60;
}
if (waveCooldown > 0) {
waveCooldown--;
if (waveCooldown === 0) {
// Spawn new wave
var n = 2 + Math.floor(wave * 0.7);
for (var i = 0; i < n; i++) {
var e = new EnemyShip();
e.x = 200 + Math.random() * (2048 - 400);
e.y = -200 - i * 120;
enemies.push(e);
game.addChild(e);
}
}
}
};
// Title screen overlay
var titleOverlay = new Container();
LK.gui.center.addChild(titleOverlay);
var titleBg = LK.getAsset('ship_default', {
width: 1200,
height: 1600,
color: 0x222233,
anchorX: 0.5,
anchorY: 0.5
});
titleBg.alpha = 0.98;
titleOverlay.addChild(titleBg);
var titleText = new Text2('Space Shooter', {
size: 120,
fill: "#fff"
});
titleText.anchor.set(0.5, 0);
titleText.y = -600;
titleOverlay.addChild(titleText);
var notifText = new Text2('', {
size: 60,
fill: "#ff0"
});
notifText.anchor.set(0.5, 0);
notifText.y = -450;
titleOverlay.addChild(notifText);
function showNotification(msg) {
notifText.setText(msg);
notifText.visible = true;
LK.setTimeout(function () {
notifText.visible = false;
}, 2000);
}
// Settings button
var settingsBtn = new Text2('Settings', {
size: 100,
fill: "#fff"
});
settingsBtn.anchor.set(0.5, 0);
settingsBtn.y = -200;
// --- Settings Overlay ---
var settingsOverlay = new Container();
settingsOverlay.visible = false;
LK.gui.center.addChild(settingsOverlay);
var settingsBg = LK.getAsset('ship_default', {
width: 1000,
height: 1200,
color: 0x222233,
anchorX: 0.5,
anchorY: 0.5
});
settingsBg.alpha = 0.98;
settingsOverlay.addChild(settingsBg);
var settingsTitle = new Text2('Settings', {
size: 100,
fill: "#fff"
});
settingsTitle.anchor.set(0.5, 0);
settingsTitle.y = -500;
settingsOverlay.addChild(settingsTitle);
// Example setting: Music toggle
var musicLabel = new Text2('Music: On', {
size: 70,
fill: "#fff"
});
musicLabel.anchor.set(0.5, 0);
musicLabel.y = -200;
settingsOverlay.addChild(musicLabel);
var musicOn = true;
musicLabel.down = function () {
musicOn = !musicOn;
musicLabel.setText('Music: ' + (musicOn ? 'On' : 'Off'));
if (musicOn) {
LK.playMusic('bgmusic');
} else {
LK.stopMusic();
}
showNotification('Music ' + (musicOn ? 'enabled' : 'disabled'));
};
// Example setting: Reset progress
var resetBtn = new Text2('Reset Progress', {
size: 70,
fill: 0xFF6666
});
resetBtn.anchor.set(0.5, 0);
resetBtn.y = 0;
resetBtn.down = function () {
storage.money = 0;
storage.unlockedSkins = ["ship_default"];
storage.selectedSkin = "ship_default";
storage.upgrades = {
fireRate: 1,
speed: 1,
shield: 1
};
showNotification("Progress reset!");
updateMenu();
updateMoneyText();
if (player) {
player.setSkin("ship_default");
player.shield = 1;
updateShieldText();
}
};
// Settings close button
var settingsCloseBtn = new Text2('Close', {
size: 80,
fill: "#fff"
});
settingsCloseBtn.anchor.set(0.5, 0);
settingsCloseBtn.y = 400;
settingsCloseBtn.down = function () {
settingsOverlay.visible = false;
titleOverlay.visible = true;
};
settingsOverlay.addChild(settingsTitle);
settingsOverlay.addChild(musicLabel);
settingsOverlay.addChild(resetBtn);
settingsOverlay.addChild(settingsCloseBtn);
// Settings button
settingsBtn.down = function () {
titleOverlay.visible = false;
settingsOverlay.visible = true;
};
titleOverlay.addChild(settingsBtn);
// Skins button
var skinsBtn = new Text2('Skins', {
size: 100,
fill: "#fff"
});
skinsBtn.anchor.set(0.5, 0);
skinsBtn.y = 0;
skinsBtn.down = function () {
// Only show menuOverlay as skins menu if Skins is clicked
titleOverlay.visible = false;
menuOverlay.visible = true;
menuOpen = true;
// menuBtn.visible = false; // Do NOT hide menu button when menu is open from title
updateMenu();
// Mark that menu was opened from Skins
menuOverlay._openedFromTitle = true;
};
titleOverlay.addChild(skinsBtn);
// (Menu button on title screen removed as requested)
// Play button
var playBtn = new Text2('Play', {
size: 100,
fill: "#fff"
});
playBtn.anchor.set(0.5, 0);
playBtn.y = 200;
playBtn.down = function () {
titleOverlay.visible = false;
startGame();
LK.playMusic('bgmusic', {
fade: {
start: 0,
end: 1,
duration: 1000
}
});
};
titleOverlay.addChild(playBtn);
// Show title screen on load
titleOverlay.visible = true;
menuOverlay.visible = false;
// Ensure menuOpen is false on load (prevents accidental menu/skins open)
menuOpen = false;
// Start game and music only after Play is pressed
// (moved to Play button)
// Reset game on game over
LK.on('gameover', function () {
menuOverlay.visible = false;
menuOpen = false;
titleOverlay.visible = true;
settingsOverlay.visible = false;
});
LK.on('youwin', function () {
menuOverlay.visible = false;
menuOpen = false;
titleOverlay.visible = true;
settingsOverlay.visible = false;
});
Make it a ship for a space shooting game with intense designing. In-Game asset. 2d. High contrast. No shadows
Make it a bullet for a space shooting game with intense designing. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
Make it a coin for a space shooting game with intense designing. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
Make it a ship for a space shooting game with intense designing. In-Game asset. 2d. High contrast. No shadows