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