User prompt
Remove bouncing bullets upgrade
User prompt
Remove the buzzsaw upgrade
User prompt
Add an upgrade that lets the bullet bounce on a wall once
User prompt
Add wing to all enemy classes
User prompt
When the enemy planes stop give them a movement pattern specific to their class
User prompt
Add wings for the enemy planes
User prompt
Make the yellow explosion effect stay for about 2 secs and make it have about 200 width and height
User prompt
Make the yellow explosion effect way way bigger
User prompt
Give the bomb a yellow explosion effect when it collides
User prompt
Put the upgrade pool in 3 rows so it is easier to see
User prompt
For the rapid ship class make them go a little bit slower
User prompt
Every time you get the buzzsaw upgrade add another buzzsaw that goes the opposite way and make them stay for longer each time ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Make the buzzsaw look more like a saw
User prompt
For the bomb give it a yellow blast radius that deals a lot of damage ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Make the fast enemy and tank enemy assets and add them in game
User prompt
Add more types of enemys
User prompt
Add special upgrades where it can upgrade 2x as much as normal and colour it yellow
User prompt
Enhance the fonts
User prompt
Make the start button look polished
User prompt
Please fix the bug: 'tween.to is not a function. (In 'tween.to(glowGfx, { alpha: 0.15 }, 700)', 'tween.to' is undefined)' in or related to this line: 'tween.to(glowGfx, {' Line Number: 1230 ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
When a ship is selected make a glow effect behind it instead
User prompt
Move the start button up more
User prompt
Move the start button above the ship selection
User prompt
Move the start button even lower down
User prompt
Put the ship selection menu in rows of 2
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // Big Enemy var BigEnemy = Container.expand(function () { var self = Container.call(this); var gfx = self.attachAsset('bigEnemy', { anchorX: 0.5, anchorY: 0.5 }); self.width = gfx.width; self.height = gfx.height; self.scaleX = 1; self.scaleY = 1; self.alpha = 1; self.speedY = 4; self.hp = 6; self.maxHp = 6; self.shootCooldown = 0; self.shootRate = 70 + Math.floor(Math.random() * 40); self.value = 5; self.update = function () { if (self.lastY === undefined) self.lastY = self.y; var stopY = 2732 / 2.5; // stops a bit higher than regular enemy if (self.y < stopY) { self.y += self.speedY; } else { self.y = stopY; } if (self.shootCooldown > 0) self.shootCooldown--; if (self.shootCooldown === 0 && Math.random() < 0.04) { self.shoot(); self.shootCooldown = self.shootRate; } self.lastY = self.y; }; self.shoot = function () { var bullet = new EnemyBullet(); bullet.x = self.x; bullet.y = self.y + self.height / 2 + 10; bullet.speedY = 22; // slightly faster bullet enemyBullets.push(bullet); game.addChild(bullet); LK.getSound('enemyShoot').play(); }; self.takeDamage = function (amount) { self.hp -= amount; LK.getSound('enemyHit').play(); LK.effects.flashObject(self, 0xffffff, 200); if (self.hp <= 0) { self.destroyed = true; } }; return self; }); // Bomb: Explosive weapon that destroys enemies in an area var Bomb = Container.expand(function () { var self = Container.call(this); var gfx = self.attachAsset('enemy', { anchorX: 0.5, anchorY: 0.5 }); gfx.width = 60; gfx.height = 60; gfx.tint = 0xff3300; self.width = gfx.width; self.height = gfx.height; self.speedY = -18; self.speedX = 0; self.blastRadius = 200; self.owner = null; // set to ship self.update = function () { self.x += self.speedX; self.y += self.speedY; // Check for collision with enemies directly in update method for (var i = 0; i < enemies.length; i++) { if (self.intersects(enemies[i])) { self.explode(); break; } } }; self.explode = function () { // Flash effect for explosion LK.effects.flashObject(self, 0xffaa00, 200); self.destroyed = true; }; return self; }); // BuzzSaw: Bouncing melee weapon var BuzzSaw = Container.expand(function () { var self = Container.call(this); var gfx = self.attachAsset('enemy', { anchorX: 0.5, anchorY: 0.5 }); gfx.width = 80; gfx.height = 80; gfx.tint = 0xffee00; self.width = gfx.width; self.height = gfx.height; self.speedX = 18 * (Math.random() < 0.5 ? 1 : -1); self.speedY = -22; self.bounces = 0; self.maxBounces = 6; self.owner = null; // set to ship self.update = function () { self.x += self.speedX; self.y += self.speedY; // Bounce off walls if (self.x < self.width / 2 && self.speedX < 0 || self.x > 2048 - self.width / 2 && self.speedX > 0) { self.speedX *= -1; self.bounces++; } // Bounce off top/bottom if (self.y < self.height / 2 && self.speedY < 0 || self.y > 2732 - self.height / 2 && self.speedY > 0) { self.speedY *= -1; self.bounces++; } // Remove after max bounces if (self.bounces > self.maxBounces) { self.destroyed = true; } }; return self; }); // Enemy var Enemy = Container.expand(function () { var self = Container.call(this); var gfx = self.attachAsset('enemy', { anchorX: 0.5, anchorY: 0.5 }); self.width = gfx.width; self.height = gfx.height; self.scaleX = 1; self.scaleY = 1; self.alpha = 1; self.speedY = 6; self.hp = 1; self.maxHp = 1; self.shootCooldown = 0; self.shootRate = 90 + Math.floor(Math.random() * 60); self.value = 1; self.update = function () { // Store lastY for transition detection if (self.lastY === undefined) self.lastY = self.y; var stopY = 2732 / 2; // halfway down the screen if (self.y < stopY) { self.y += self.speedY; } else { self.y = stopY; } if (self.shootCooldown > 0) self.shootCooldown--; if (self.shootCooldown === 0 && Math.random() < 0.02) { self.shoot(); self.shootCooldown = self.shootRate; } self.lastY = self.y; }; self.shoot = function () { var bullet = new EnemyBullet(); bullet.x = self.x; bullet.y = self.y + self.height / 2 + 10; enemyBullets.push(bullet); game.addChild(bullet); LK.getSound('enemyShoot').play(); }; self.takeDamage = function (amount) { self.hp -= amount; LK.getSound('enemyHit').play(); LK.effects.flashObject(self, 0xffffff, 200); if (self.hp <= 0) { self.destroyed = true; } }; return self; }); // Enemy Bullet var EnemyBullet = Container.expand(function () { var self = Container.call(this); var gfx = self.attachAsset('enemyBullet', { anchorX: 0.5, anchorY: 0.5 }); self.width = gfx.width; self.height = gfx.height; self.speedY = 18; self.update = function () { self.y += self.speedY; }; return self; }); // Laser: Piercing straight beam var Laser = Container.expand(function () { var self = Container.call(this); // Determine laser width based on ship.laserSize, default to 30, max 300 var laserWidth = 30; if (typeof ship !== "undefined" && typeof ship.laserSize !== "undefined") { laserWidth = Math.min(300, ship.laserSize); } var gfx = self.attachAsset('bullet', { anchorX: 0.5, anchorY: 0.0 }); gfx.width = laserWidth; gfx.height = 400; gfx.tint = 0x00ffff; self.width = gfx.width; self.height = gfx.height; self.speedY = -40; self.lifetime = 60; self.update = function () { self.y += self.speedY; self.lifetime--; if (self.lifetime <= 0) { self.destroyed = true; } }; return self; }); // Player Bullet var PlayerBullet = Container.expand(function () { var self = Container.call(this); var gfx = self.attachAsset('bullet', { anchorX: 0.5, anchorY: 0.5 }); // Determine bullet size based on ship.bulletSize var sizeMultiplier = 1; if (typeof ship !== "undefined" && typeof ship.bulletSize !== "undefined") { sizeMultiplier = ship.bulletSize; } gfx.width = gfx.width * sizeMultiplier; gfx.height = gfx.height * sizeMultiplier; self.width = gfx.width; self.height = gfx.height; self.speedY = -28; self.speedX = 0; // Set bullet damage based on ship.bulletDamage, default to 1 self.damage = 1; if (typeof ship !== "undefined" && typeof ship.bulletDamage !== "undefined") { self.damage = ship.bulletDamage; } self.update = function () { self.y += self.speedY; self.x += self.speedX; }; return self; }); // Player Ship var Ship = Container.expand(function () { var self = Container.call(this); // Add left wing var leftWing = self.attachAsset('ship', { anchorX: 0.5, anchorY: 0.5 }); leftWing.width = 60; leftWing.height = 120; leftWing.x = -70; leftWing.y = 20; leftWing.rotation = -0.35; leftWing.alpha = 0.7; // Add right wing var rightWing = self.attachAsset('ship', { anchorX: 0.5, anchorY: 0.5 }); rightWing.width = 60; rightWing.height = 120; rightWing.x = 70; rightWing.y = 20; rightWing.rotation = 0.35; rightWing.alpha = 0.7; // Main ship body var shipGfx = self.attachAsset('ship', { anchorX: 0.5, anchorY: 0.5 }); self.width = shipGfx.width; self.height = shipGfx.height; self.fireCooldown = 0; self.fireRate = 30; // ticks between shots self.bulletSpeed = -28; self.bulletCount = 1; self.bulletSpread = 0; self.bulletSize = 1; // Default bullet size multiplier self.bulletDamage = 1; // Default bullet damage self.doubleShot = false; self.tripleShot = false; self.laser = false; self.hp = 100; self.maxHp = 100; self.invulnTicks = 0; self.update = function () { if (self.fireCooldown > 0) self.fireCooldown--; if (self.invulnTicks > 0) { self.invulnTicks--; shipGfx.alpha = LK.ticks % 8 < 4 ? 0.4 : 1; leftWing.alpha = shipGfx.alpha * 0.7; rightWing.alpha = shipGfx.alpha * 0.7; } else { shipGfx.alpha = 1; leftWing.alpha = 0.7; rightWing.alpha = 0.7; } }; self.shoot = function () { if (self.fireCooldown > 0) return; self.fireCooldown = self.fireRate; self._buzzSawFiredThisShot = false; self._bombFiredThisShot = false; var shots = []; var spread = self.bulletSpread; var count = self.bulletCount; if (self.tripleShot) { count = 3; spread = 0.25; } else if (self.doubleShot) { count = 2; spread = 0.18; } // BuzzSaw upgrade if (self.buzzSaw && !self._buzzSawFiredThisShot) { var saw = new BuzzSaw(); saw.x = self.x; saw.y = self.y - self.height / 2 - 10; saw.owner = self; playerBullets.push(saw); game.addChild(saw); shots.push(saw); self._buzzSawFiredThisShot = true; } // Laser upgrade if (self.laser) { var laser = new Laser(); laser.x = self.x; laser.y = self.y - self.height / 2 - 10; playerBullets.push(laser); game.addChild(laser); shots.push(laser); } // Bomb upgrade if (self.bomb && !self._bombFiredThisShot) { var bomb = new Bomb(); bomb.x = self.x; bomb.y = self.y - self.height / 2 - 10; bomb.owner = self; playerBullets.push(bomb); game.addChild(bomb); shots.push(bomb); self._bombFiredThisShot = true; } // Normal bullets for (var i = 0; i < count; i++) { var angle = (i - (count - 1) / 2) * spread; var bullet = new PlayerBullet(); bullet.x = self.x; bullet.y = self.y - self.height / 2 - 10; bullet.speedY = self.bulletSpeed * Math.cos(angle); bullet.speedX = self.bulletSpeed * Math.sin(angle); playerBullets.push(bullet); game.addChild(bullet); shots.push(bullet); } LK.getSound('shoot').play(); return shots; }; self.takeDamage = function (amount) { if (self.invulnTicks > 0) return; self.hp -= amount; self.invulnTicks = 60; LK.effects.flashObject(self, 0xff0000, 400); if (self.hp <= 0) { LK.effects.flashScreen(0xff0000, 1000); LK.showGameOver(); } }; return self; }); // Upgrade Card var UpgradeCard = Container.expand(function () { var self = Container.call(this); var highlight = self.attachAsset('upgradeHighlight', { anchorX: 0.5, anchorY: 0.5 }); highlight.visible = false; var card = self.attachAsset('upgradeCard', { anchorX: 0.5, anchorY: 0.5 }); self.text = new Text2('', { size: 70, fill: 0xFFFFFF }); self.text.anchor.set(0.5, 0.5); self.addChild(self.text); self.setText = function (t) { self.text.setText(t); }; self.setHighlighted = function (v) { highlight.visible = v; }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x000000 }); /**** * Game Code ****/ // Game state variables // Ship (player) // Enemy // Bullet (player) // Enemy bullet // Upgrade card // Upgrade highlight // Sound effects // Music var ship; var playerBullets = []; var enemies = []; var enemyBullets = []; var wave = 1; var enemiesToSpawn = 0; var enemiesDefeated = 0; var enemiesPerWave = 8; var waveInProgress = false; var upgradePending = false; var upgradeOptions = []; var upgradeCards = []; var dragging = false; var dragOffsetX = 0; var dragOffsetY = 0; var score = 0; var scoreTxt; var hpTxt; var waveTxt; var upgradeGroup; var lastTouch = { x: 0, y: 0 }; // Set up background color game.setBackgroundColor(0x0a0a18); // --- Starfield background --- var starCount = 120; var stars = []; for (var i = 0; i < starCount; i++) { var star = new Container(); // Use a white ellipse for stars, randomize size for variety var size = 2 + Math.random() * 3; var gfx = star.attachAsset('bullet', { anchorX: 0.5, anchorY: 0.5, width: size, height: size, color: 0xffffff }); star.x = Math.random() * 2048; star.y = Math.random() * 2732; star.alpha = 0.4 + Math.random() * 0.6; star.speed = 0.7 + Math.random() * 1.3; // Parallax: slower for dimmer stars stars.push(star); // Add behind everything game.addChildAt(star, 0); } // Animate stars in game.update var oldGameUpdate = game.update; game.update = function () { // Move stars downward, wrap to top for (var i = 0; i < stars.length; i++) { var s = stars[i]; s.y += s.speed; if (s.y > 2732) { s.y = -5; s.x = Math.random() * 2048; } } if (typeof oldGameUpdate === "function") oldGameUpdate(); }; // Score display scoreTxt = new Text2('Score: 0', { size: 90, fill: "#fff" }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); // HP display hpTxt = new Text2('HP: 3', { size: 70, fill: "#fff" }); hpTxt.anchor.set(0.5, 0); LK.gui.top.addChild(hpTxt); hpTxt.y = 100; // Wave display waveTxt = new Text2('Wave: 1', { size: 70, fill: "#fff" }); waveTxt.anchor.set(0.5, 0); LK.gui.top.addChild(waveTxt); waveTxt.y = 200; // Hide UI during menu, show after game start scoreTxt.visible = false; hpTxt.visible = false; waveTxt.visible = false; // Upgrade group (overlay) upgradeGroup = new Container(); upgradeGroup.visible = false; game.addChild(upgradeGroup); // Initialize player ship ship = new Ship(); game.addChild(ship); ship.x = 2048 / 2; ship.y = 2732 - 180; // Start first wave // startWave(); // Handle dragging ship game.down = function (x, y, obj) { if (upgradePending) return; if (pointInShip(x, y)) { dragging = true; dragOffsetX = ship.x - x; dragOffsetY = ship.y - y; } lastTouch.x = x; lastTouch.y = y; }; game.move = function (x, y, obj) { if (upgradePending) { // Handle upgrade card selection for (var i = 0; i < upgradeCards.length; i++) { var card = upgradeCards[i]; var bounds = getCardBounds(card); if (x >= bounds.x && x <= bounds.x + bounds.w && y >= bounds.y && y <= bounds.y + bounds.h) { for (var j = 0; j < upgradeCards.length; j++) { upgradeCards[j].setHighlighted(j === i); } } } lastTouch.x = x; lastTouch.y = y; return; } if (dragging) { var nx = x + dragOffsetX; var ny = y + dragOffsetY; // Clamp to screen nx = Math.max(ship.width / 2, Math.min(2048 - ship.width / 2, nx)); ny = Math.max(2732 - 400, Math.min(2732 - ship.height / 2, ny)); ship.x = nx; ship.y = ny; } lastTouch.x = x; lastTouch.y = y; }; game.up = function (x, y, obj) { if (upgradePending) { // Check if an upgrade card was selected for (var i = 0; i < upgradeCards.length; i++) { var card = upgradeCards[i]; var bounds = getCardBounds(card); if (x >= bounds.x && x <= bounds.x + bounds.w && y >= bounds.y && y <= bounds.y + bounds.h) { selectUpgrade(i); break; } } return; } dragging = false; }; // Main game update loop game.update = function () { if (upgradePending) return; // Ship update ship.update(); // Timers for buzzsaw and laser firing if (typeof buzzSawTimer === "undefined") { buzzSawTimer = 0; } if (typeof laserTimer === "undefined") { laserTimer = 0; } if (typeof bombTimer === "undefined") { bombTimer = 0; } // Track buzzsaw upgrade count if (typeof ship.buzzSawCount === "undefined") { ship.buzzSawCount = ship.buzzSaw ? 1 : 0; } if (ship.buzzSaw && ship.buzzSawCount === 0) ship.buzzSawCount = 1; // Track bomb upgrade count if (typeof ship.bombCount === "undefined") { ship.bombCount = ship.bomb ? 1 : 0; } if (ship.bomb && ship.bombCount === 0) ship.bombCount = 1; // Ship auto-fire (normal bullets only) if (ship.fireCooldown === 0) { // Temporarily disable buzzSaw and laser for normal auto-fire var hadBuzz = ship.buzzSaw, hadLaser = ship.laser, hadBomb = ship.bomb; var fireBuzz = false, fireLaser = false, fireBomb = false; if (ship.buzzSaw) { if (buzzSawTimer <= 0) { fireBuzz = true; buzzSawTimer = 300; // 5 seconds at 60fps } else { ship.buzzSaw = false; } } if (ship.laser) { if (laserTimer <= 0) { fireLaser = true; laserTimer = 300; } else { ship.laser = false; } } if (ship.bomb) { if (bombTimer <= 0) { fireBomb = true; bombTimer = 300; // 5 seconds at 60fps } else { ship.bomb = false; } } ship.shoot(); // Restore buzzSaw/laser flags for next time ship.buzzSaw = hadBuzz; ship.laser = hadLaser; ship.bomb = hadBomb; // If it's time, fire buzzSaw and/or laser if (fireBuzz) { var oldBuzz = ship.buzzSaw; ship.buzzSaw = true; // Fire as many buzzsaws as upgrades selected var buzzCount = ship.buzzSawCount || 1; for (var i = 0; i < buzzCount; i++) { ship.shoot(); } ship.buzzSaw = oldBuzz; } if (fireLaser) { var oldLaser = ship.laser; ship.laser = true; ship.shoot(); ship.laser = oldLaser; } if (fireBomb) { var oldBomb = ship.bomb; ship.bomb = true; // Fire as many bombs as upgrades selected var bombCount = ship.bombCount || 1; for (var i = 0; i < bombCount; i++) { ship.shoot(); } ship.bomb = oldBomb; } } if (buzzSawTimer > 0) buzzSawTimer--; if (laserTimer > 0) laserTimer--; if (bombTimer > 0) bombTimer--; // Player bullets for (var i = playerBullets.length - 1; i >= 0; i--) { var b = playerBullets[i]; b.update(); // Remove if off screen or destroyed if (b.y < -b.height || b.y > 2732 + (b.height || 0) || b.x < -200 || b.x > 2048 + 200 || b.destroyed) { b.destroy(); playerBullets.splice(i, 1); continue; } // Check collision with enemies for (var j = enemies.length - 1; j >= 0; j--) { var e = enemies[j]; // BuzzSaw: deal damage, bounce, but don't destroy on hit if (b instanceof BuzzSaw && b.intersects(e)) { e.takeDamage(2); b.speedX *= -1; b.speedY *= -1; b.bounces++; if (e.hp <= 0 && !e.destroyed) { e.destroyed = true; } // Don't remove buzzsaw, allow multiple hits continue; } // Laser: deal damage, pierce, don't destroy on hit if (b instanceof Laser && b.intersects(e)) { e.takeDamage(2); if (e.hp <= 0 && !e.destroyed) { e.destroyed = true; } // Don't remove laser, allow piercing continue; } // Bomb: explode on impact, damage all enemies in radius if (b instanceof Bomb && b.intersects(e)) { // First handle the enemy that was directly hit e.takeDamage(3); // Direct hit does more damage if (e.hp <= 0 && !e.destroyed) { e.destroyed = true; } // Then trigger explosion for area damage b.explode(); // Get all enemies in blast radius for (var k = enemies.length - 1; k >= 0; k--) { if (k === j) continue; // Skip the one we already hit var target = enemies[k]; var dx = target.x - b.x; var dy = target.y - b.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance <= b.blastRadius) { // Damage falls off with distance var damageMultiplier = 1 - distance / b.blastRadius; var damage = Math.max(1, Math.floor(2 * damageMultiplier)); target.takeDamage(damage); } } playerBullets.splice(i, 1); break; } // Normal bullet if (b.intersects(e)) { var dmg = typeof b.damage !== "undefined" ? b.damage : 1; e.takeDamage(dmg); b.destroy(); playerBullets.splice(i, 1); if (e.hp <= 0 && !e.destroyed) { e.destroyed = true; } break; } } } // Enemies for (var i = enemies.length - 1; i >= 0; i--) { var e = enemies[i]; e.update(); // Remove if off screen if (e.y > 2732 + e.height) { e.destroy(); enemies.splice(i, 1); continue; } // Remove if destroyed if (e.destroyed) { score += e.value; enemiesDefeated++; scoreTxt.setText('Score: ' + score); e.destroy(); enemies.splice(i, 1); LK.getSound('hit').play(); // Check for wave end if (enemiesDefeated >= enemiesPerWave) { setTimeout(showUpgrade, 600); } continue; } // Check collision with ship if (e.intersects(ship) && ship.invulnTicks === 0) { ship.takeDamage(1); e.destroyed = true; } } // Enemy bullets for (var i = enemyBullets.length - 1; i >= 0; i--) { var eb = enemyBullets[i]; eb.update(); if (eb.y > 2732 + eb.height) { eb.destroy(); enemyBullets.splice(i, 1); continue; } if (eb.intersects(ship) && ship.invulnTicks === 0) { ship.takeDamage(1); eb.destroy(); enemyBullets.splice(i, 1); continue; } } // Spawn enemies if (waveInProgress && enemiesToSpawn > 0 && enemies.length < Math.min(5 + wave, 10)) { spawnEnemy(); } // Update HP and wave display hpTxt.setText('HP: ' + ship.hp); waveTxt.setText('Wave: ' + wave); }; // Start a new wave function startWave() { waveInProgress = true; enemiesToSpawn = enemiesPerWave; enemiesDefeated = 0; waveTxt.setText('Wave: ' + wave); } // Spawn a single enemy function spawnEnemy() { // 20% chance to spawn a BigEnemy, but at least 1 per wave after wave 2 var spawnBig = wave > 2 && (Math.random() < 0.2 || enemiesToSpawn === enemiesPerWave - 1 && wave > 4); var e; if (spawnBig) { e = new BigEnemy(); e.x = 200 + Math.random() * (2048 - 400); e.y = -e.height / 2 - 10; // Scale up HP and value with wave e.hp = 6 + Math.floor(wave * 1.5); e.maxHp = e.hp; e.speedY = 4 + Math.floor(wave / 6); e.shootRate = Math.max(40, 90 - wave * 2 + Math.floor(Math.random() * 30)); e.value = 5 + Math.floor(wave * 1.2); } else { e = new Enemy(); e.x = 180 + Math.random() * (2048 - 360); e.y = -e.height / 2 - 10; // Increase difficulty per wave e.hp = 1 + Math.floor(wave / 2); e.maxHp = e.hp; e.speedY = 6 + Math.floor(wave / 3); e.shootRate = Math.max(60, 120 - wave * 4 + Math.floor(Math.random() * 40)); e.value = 1 + Math.floor(wave / 2); } enemies.push(e); game.addChild(e); enemiesToSpawn--; } // Show upgrade selection function showUpgrade() { if (upgradePending) return; upgradePending = true; waveInProgress = false; upgradeGroup.visible = true; // Generate 3 upgrade options upgradeOptions = getUpgradeOptions(); // Remove old cards for (var i = 0; i < upgradeCards.length; i++) { upgradeCards[i].destroy(); } upgradeCards = []; // Layout cards var centerX = 2048 / 2; var centerY = 2732 / 2; var spacing = 700; for (var i = 0; i < 3; i++) { var card = new UpgradeCard(); card.x = centerX + (i - 1) * spacing; card.y = centerY; card.setText(upgradeOptions[i].label); card.setHighlighted(false); upgradeGroup.addChild(card); upgradeCards.push(card); } } // Select an upgrade function selectUpgrade(idx) { if (!upgradePending) return; var upg = upgradeOptions[idx]; applyUpgrade(upg); LK.getSound('upgrade').play(); // Hide upgrade UI for (var i = 0; i < upgradeCards.length; i++) { upgradeCards[i].destroy(); } upgradeCards = []; upgradeGroup.visible = false; upgradePending = false; // Next wave wave++; enemiesPerWave = Math.min(20, 8 + Math.floor(wave * 1.5)); startWave(); } // Generate upgrade options function getUpgradeOptions() { var pool = [{ label: "+1 Max HP", apply: function apply() { ship.maxHp += 1; ship.hp = ship.maxHp; } }, { label: "+1 Bullet per shot", apply: function apply() { ship.bulletCount = Math.min(5, ship.bulletCount + 1); } }, { label: "Faster Fire Rate", apply: function apply() { ship.fireRate = Math.max(8, ship.fireRate - 4); } }, { label: "Wider Spread", apply: function apply() { ship.bulletSpread = Math.min(0.5, ship.bulletSpread + 0.08); } }, { label: "Double Shot", apply: function apply() { ship.doubleShot = true; } }, { label: "Triple Shot", apply: function apply() { ship.tripleShot = true; } }, { label: "Buzz Saw (Bouncing Blade)", apply: function apply() { ship.buzzSaw = true; if (typeof ship.buzzSawCount === "undefined") { ship.buzzSawCount = 1; } else { ship.buzzSawCount++; } } }, { label: "Laser (Piercing Beam)", apply: function apply() { ship.laser = true; if (typeof ship.laserSize === "undefined") { ship.laserSize = 30; ship.laserUpgradeCount = 1; } else { ship.laserUpgradeCount++; // Each upgrade increases width by 60, up to 300 ship.laserSize = Math.min(300, 30 + (ship.laserUpgradeCount - 1) * 60); } } }, { label: "Bomb (Area Explosion)", apply: function apply() { ship.bomb = true; if (typeof ship.bombCount === "undefined") { ship.bombCount = 1; } else { ship.bombCount++; } } }, { label: "Big Bullets", apply: function apply() { if (typeof ship.bulletSize === "undefined") { ship.bulletSize = 1.5; // First upgrade: 50% bigger } else { ship.bulletSize += 0.5; // Each upgrade makes bullets bigger } } }, { label: "Big Bullets", apply: function apply() { if (typeof ship.bulletSize === "undefined") { ship.bulletSize = 1.5; // First upgrade: 50% bigger } else { ship.bulletSize += 0.5; // Each upgrade makes bullets bigger } } }, { label: "Bullet Damage Up", apply: function apply() { if (typeof ship.bulletDamage === "undefined") { ship.bulletDamage = 2; } else { ship.bulletDamage += 1; } } }]; // Remove upgrades already owned if (ship.doubleShot) pool = pool.filter(function (u) { return u.label !== "Double Shot"; }); if (ship.tripleShot) pool = pool.filter(function (u) { return u.label !== "Triple Shot"; }); // Buzz Saw and Laser upgrades are always available for selection // Pick 3 random upgrades var options = []; var used = {}; for (var i = 0; i < 3; i++) { var idx = Math.floor(Math.random() * pool.length); while (used[idx]) idx = (idx + 1) % pool.length; used[idx] = true; options.push(pool[idx]); } return options; } // Apply upgrade function applyUpgrade(upg) { upg.apply(); } // Utility: check if point is in ship function pointInShip(x, y) { return x >= ship.x - ship.width / 2 && x <= ship.x + ship.width / 2 && y >= ship.y - ship.height / 2 && y <= ship.y + ship.height / 2; } // Utility: get card bounds function getCardBounds(card) { return { x: card.x - 310, y: card.y - 160, w: 620, h: 320 }; } // Utility: setTimeout using LK function setTimeout(fn, ms) { return LK.setTimeout(fn, ms); } // Start music LK.playMusic('bgmusic', { fade: { start: 0, end: 1, duration: 1200 } }); // --- Ship Selection Menu --- // --- Game Title and Instructions --- var titleTxt = new Text2("GALACTIC DEFENSE", { size: 120, fill: "#fff" }); titleTxt.anchor.set(0.5, 0); titleTxt.x = 2048 / 2; titleTxt.y = 80; game.addChild(titleTxt); var instrTxt = new Text2("Choose your ship and tap Start!\nDrag your ship to dodge and shoot.\nSurvive waves, collect upgrades.", { size: 60, fill: "#fff" }); instrTxt.anchor.set(0.5, 0); instrTxt.x = 2048 / 2; instrTxt.y = 240; game.addChild(instrTxt); // Define available ships var shipTypes = [{ name: "Balanced", color: 0x66ccff, hp: 100, fireRate: 30, bulletCount: 1, bulletSpread: 0, bulletSize: 1, bulletDamage: 1, desc: "Balanced all-rounder" }, { name: "Tank", color: 0x44bb44, hp: 160, fireRate: 44, bulletCount: 1, bulletSpread: 0, bulletSize: 1.2, bulletDamage: 2, desc: "High HP, slow fire, strong bullets" }, { name: "Rapid", color: 0xffaa00, hp: 70, fireRate: 14, bulletCount: 1, bulletSpread: 0, bulletSize: 0.8, bulletDamage: 1, desc: "Low HP, very fast fire" }, { name: "Spread", color: 0xdd33cc, hp: 90, fireRate: 32, bulletCount: 3, bulletSpread: 0.22, bulletSize: 0.9, bulletDamage: 1, desc: "Triple shot, moderate stats" }]; var selectedShipType = 0; // Ship selection group var shipSelectGroup = new Container(); game.addChild(shipSelectGroup); // Create ship cards var shipCards = []; var cardSpacing = 500; var centerX = 2048 / 2; var centerY = 2732 / 2 - 200; for (var i = 0; i < shipTypes.length; i++) { var card = new Container(); var cardGfx = card.attachAsset('upgradeCard', { anchorX: 0.5, anchorY: 0.5 }); cardGfx.width = 540; cardGfx.height = 670; cardGfx.tint = shipTypes[i].color; // Ship name var nameTxt = new Text2(shipTypes[i].name, { size: 90, fill: "#fff" }); nameTxt.anchor.set(0.5, 0); nameTxt.y = -270; card.addChild(nameTxt); // Ship preview var preview = card.attachAsset('ship', { anchorX: 0.5, anchorY: 0.5 }); preview.width = 180; preview.height = 180; preview.tint = shipTypes[i].color; preview.y = -100; // Ship stats var statsTxt = new Text2("HP: " + shipTypes[i].hp + "\nFire: " + (60 / shipTypes[i].fireRate).toFixed(1) + "/s" + "\nBullets: " + shipTypes[i].bulletCount + "\nDamage: " + shipTypes[i].bulletDamage, { size: 48, fill: "#fff" }); statsTxt.anchor.set(0.5, 0); statsTxt.y = 100; card.addChild(statsTxt); // Description var descTxt = new Text2(shipTypes[i].desc, { size: 52, fill: "#fff" }); descTxt.anchor.set(0.5, 0); descTxt.y = 300; card.addChild(descTxt); // Arrange in rows of 2 var row = Math.floor(i / 2); var col = i % 2; var rowSpacing = 800; var colSpacing = 700; // Center the two columns var totalCols = 2; var xOffset = (col - (totalCols - 1) / 2) * colSpacing; var yOffset = row * rowSpacing; card.x = centerX + xOffset; card.y = centerY + yOffset; card.cardIndex = i; shipSelectGroup.addChild(card); shipCards.push(card); } // Glow effect for selected card var selectGlow = new Container(); var glowGfx = selectGlow.attachAsset('ship', { anchorX: 0.5, anchorY: 0.5 }); glowGfx.width = 320; glowGfx.height = 320; glowGfx.tint = 0xffff66; glowGfx.alpha = 0.45; selectGlow.visible = true; shipSelectGroup.addChild(selectGlow); // Animate the glow (pulsing) function pulseGlow() { tween(glowGfx, { alpha: 0.15 }, { duration: 700, easing: tween.linear, onFinish: function onFinish() { tween(glowGfx, { alpha: 0.45 }, { duration: 700, easing: tween.linear, onFinish: pulseGlow }); } }); } pulseGlow(); function updateShipHighlight() { selectGlow.x = shipCards[selectedShipType].x; selectGlow.y = shipCards[selectedShipType].y - 100; // Center behind ship preview } updateShipHighlight(); // Ship card touch selection shipSelectGroup.down = function (x, y, obj) { for (var i = 0; i < shipCards.length; i++) { var card = shipCards[i]; var bx = card.x - 210, by = card.y - 260, bw = 420, bh = 520; if (x >= bx && x <= bx + bw && y >= by && y <= by + bh) { selectedShipType = i; updateShipHighlight(); break; } } }; // Ship card swipe (left/right) shipSelectGroup.move = function (x, y, obj) { // Optional: could implement swipe, but for now tap to select }; // --- Start Button --- var startButton = new Container(); var buttonGfx = startButton.attachAsset('upgradeCard', { anchorX: 0.5, anchorY: 0.5 }); buttonGfx.width = 400; buttonGfx.height = 200; buttonGfx.tint = 0x00ff00; // Green color for the start button var buttonText = new Text2('Start', { size: 100, fill: 0xFFFFFF }); buttonText.anchor.set(0.5, 0.5); startButton.addChild(buttonText); // Position the start button higher above the ship cards startButton.x = 2048 / 2; startButton.y = 2732 / 2 - 600; game.addChild(startButton); // Add event listener for the start button startButton.down = function (x, y, obj) { // Hide menu UI startButton.visible = false; shipSelectGroup.visible = false; if (typeof titleTxt !== "undefined") titleTxt.visible = false; if (typeof instrTxt !== "undefined") instrTxt.visible = false; // Show game UI scoreTxt.visible = true; hpTxt.visible = true; waveTxt.visible = true; // Initialize game state wave = 1; enemiesToSpawn = 0; enemiesDefeated = 0; enemiesPerWave = 8; waveInProgress = false; upgradePending = false; upgradeOptions = []; upgradeCards = []; dragging = false; dragOffsetX = 0; dragOffsetY = 0; score = 0; scoreTxt.setText('Score: 0'); // Set up ship with selected type var t = shipTypes[selectedShipType]; // Remove old ship if present if (ship && typeof ship.destroy === "function") ship.destroy(); ship = new Ship(); game.addChild(ship); ship.x = 2048 / 2; ship.y = 2732 - 180; ship.hp = t.hp; ship.maxHp = t.hp; ship.fireRate = t.fireRate; ship.bulletCount = t.bulletCount; ship.bulletSpread = t.bulletSpread; ship.bulletSize = t.bulletSize; ship.bulletDamage = t.bulletDamage; // Tint ship body and wings for (var i = 0; i < ship.children.length; i++) { if (ship.children[i] && ship.children[i].tint !== undefined) { ship.children[i].tint = t.color; } } hpTxt.setText('HP: ' + ship.hp); waveTxt.setText('Wave: ' + wave); // Start first wave startWave(); };
===================================================================
--- original.js
+++ change.js
@@ -1167,11 +1167,26 @@
glowGfx.alpha = 0.45;
selectGlow.visible = true;
shipSelectGroup.addChild(selectGlow);
// Animate the glow (pulsing)
-tween.to(glowGfx, {
- alpha: 0.15
-}, 700).yoyo(true).repeat(Infinity).start();
+function pulseGlow() {
+ tween(glowGfx, {
+ alpha: 0.15
+ }, {
+ duration: 700,
+ easing: tween.linear,
+ onFinish: function onFinish() {
+ tween(glowGfx, {
+ alpha: 0.45
+ }, {
+ duration: 700,
+ easing: tween.linear,
+ onFinish: pulseGlow
+ });
+ }
+ });
+}
+pulseGlow();
function updateShipHighlight() {
selectGlow.x = shipCards[selectedShipType].x;
selectGlow.y = shipCards[selectedShipType].y - 100; // Center behind ship preview
}