User prompt
Make the ship cards a bit bigger
User prompt
Move the start button down a bit more
User prompt
Hide game UI during the menu
User prompt
Polish up the menu
User prompt
In the menu add different ships you can choose from before you start
User prompt
Add an upgrade that increases the amount of damage your bullets do
User prompt
Only start the game once the start button is clicked
User prompt
Create a menu screen with a start button
User prompt
Please fix the bug: 'TypeError: undefined is not an object (evaluating 'ship.buzzSawCount')' in or related to this line: 'if (typeof ship.buzzSawCount === "undefined") {' Line Number: 677
User prompt
Please fix the bug: 'TypeError: undefined is not an object (evaluating 'ship.update')' in or related to this line: 'ship.update();' Line Number: 663
User prompt
Add a menu screen with a play button
User prompt
Please fix the bug: 'ReferenceError: Can't find variable: BuzzSaw' in or related to this line: 'if (b instanceof BuzzSaw && b.intersects(e)) {' Line Number: 530
User prompt
Please fix the bug: 'ReferenceError: Can't find variable: Enemy' in or related to this line: 'e = new Enemy();' Line Number: 632
User prompt
Please fix the bug: 'ReferenceError: Can't find variable: PlayerBullet' in or related to this line: 'var b = new PlayerBullet(size);' Line Number: 117
User prompt
Please fix the bug: 'Can't find variable: Ship' in or related to this line: 'ship = new Ship();' Line Number: 131
User prompt
Every five levels have a boss who is big and has a lot of hp and has a special gun
User prompt
Every five levels have a boss who is big and has a lot of hp and has a special gun
User prompt
Add the bigbullet upgrade to the pool
User prompt
Add an upgrade that makes the bullets bigger each time it’s upgraded
User prompt
Make the bomb explode as soon as it touches something g
User prompt
IX Add an upgrade that is a bomb that destroys stuff around where it collided and every time it is upgraded release more bombs each time
User prompt
If laser is selected twice make it bigger but have a limit to how big it can get (300)
User prompt
If buzzsaw is selected twice add one more every 5 secs
User prompt
Put the lazer and the buzzsaw back in the upgrades
User prompt
Make the buzzsaw fire every 5 secs and same with the lazer
/**** * 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; 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.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 ****/ // Music // Sound effects // Upgrade highlight // Upgrade card // Enemy bullet // Bullet (player) // Enemy // Ship (player) // Game state variables 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); } // --- MENU SCREEN --- var menuGroup = new Container(); game.addChild(menuGroup); // Title var titleTxt = new Text2("DEFENDER", { size: 180, fill: "#fff" }); titleTxt.anchor.set(0.5, 0.5); titleTxt.x = 2048 / 2; titleTxt.y = 700; menuGroup.addChild(titleTxt); // Play Button var playBtn = new Container(); var playBtnBg = playBtn.attachAsset('upgradeCard', { anchorX: 0.5, anchorY: 0.5 }); playBtnBg.width = 600; playBtnBg.height = 220; playBtnBg.tint = 0x44ff44; var playBtnTxt = new Text2("PLAY", { size: 120, fill: "#222" }); playBtnTxt.anchor.set(0.5, 0.5); playBtnTxt.x = 0; playBtnTxt.y = 0; playBtn.addChild(playBtnTxt); playBtn.x = 2048 / 2; playBtn.y = 1200; menuGroup.addChild(playBtn); // Hide menu and start game function startGameFromMenu() { menuGroup.visible = false; // 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; // 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(); } // Only allow play button to be pressed once var menuStarted = false; playBtn.down = function (x, y, obj) { if (menuStarted) return; menuStarted = true; startGameFromMenu(); }; // 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(); }; // 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)) { e.takeDamage(1); 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 } } }]; // 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 } });
===================================================================
--- original.js
+++ change.js
@@ -5,253 +5,406 @@
/****
* Classes
****/
-// BuzzSaw (Bouncing Blade) class
-var BuzzSaw = Container.expand(function () {
+// Big Enemy
+var BigEnemy = Container.expand(function () {
var self = Container.call(this);
- // Attach a bullet asset, but tint or scale to make it distinct
- var gfx = self.attachAsset('bullet', {
+ var gfx = self.attachAsset('bigEnemy', {
anchorX: 0.5,
- anchorY: 0.5,
- scaleX: 1.2,
- scaleY: 1.2
+ anchorY: 0.5
});
- self.width = gfx.width * 1.2;
- self.height = gfx.height * 1.2;
- self.speedX = 0;
+ 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 = 4;
- self.destroyed = false;
+ self.maxBounces = 6;
+ self.owner = null; // set to ship
self.update = function () {
- // Save last position for good practice (if needed)
- if (typeof self.lastX === "undefined") self.lastX = self.x;
- if (typeof self.lastY === "undefined") self.lastY = self.y;
- self.lastX = self.x;
- self.lastY = self.y;
self.x += self.speedX;
self.y += self.speedY;
- // Bounce off left/right walls
- if (self.x - self.width / 2 <= 0 && self.speedX < 0 || self.x + self.width / 2 >= 2048 && self.speedX > 0) {
+ // 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
- if (self.y - self.height / 2 <= 0 && self.speedY < 0) {
+ // 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 if too many bounces or off screen
- if (self.bounces > self.maxBounces || self.y > 2732 + self.height) {
- self.destroy();
+ // Remove after max bounces
+ if (self.bounces > self.maxBounces) {
+ self.destroyed = true;
}
};
- self.destroy = function () {
- self.destroyed = true;
- if (self.parent) self.parent.removeChild(self);
- };
return self;
});
-// Enemy class
+// Enemy
var Enemy = Container.expand(function () {
var self = Container.call(this);
- // Attach enemy asset
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.speedY = 6;
- self.shootRate = 100;
+ self.shootCooldown = 0;
+ self.shootRate = 90 + Math.floor(Math.random() * 60);
self.value = 1;
- self.shootCooldown = Math.floor(Math.random() * self.shootRate);
- self.destroyed = false;
self.update = function () {
- // Move down
- self.y += self.speedY;
- // Shooting
- if (self.shootCooldown > 0) {
- self.shootCooldown--;
+ // 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 () {
- // Shoot a single bullet straight down
- var eb = new EnemyBullet();
- eb.x = self.x;
- eb.y = self.y + self.height / 2 + 10;
- enemyBullets.push(eb);
- game.addChild(eb);
+ 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.effects.flashObject(self, 0xff0000, 200);
+ LK.getSound('enemyHit').play();
+ LK.effects.flashObject(self, 0xffffff, 200);
if (self.hp <= 0) {
self.destroyed = true;
}
};
- self.destroy = function () {
- if (self.parent) self.parent.removeChild(self);
+ 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;
});
-// PlayerBullet class
-var PlayerBullet = Container.expand(function (size) {
+// Laser: Piercing straight beam
+var Laser = Container.expand(function () {
var self = Container.call(this);
- // Default size is 1 if not provided
- if (typeof size === "undefined") size = 1;
- // Attach bullet asset, scale by size
+ // 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.5,
- scaleX: size,
- scaleY: size
+ anchorY: 0.0
});
- self.width = gfx.width * size;
- self.height = gfx.height * size;
- self.speedX = 0;
- self.speedY = -18;
- self.destroyed = false;
+ 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.x += self.speedX;
self.y += self.speedY;
+ self.lifetime--;
+ if (self.lifetime <= 0) {
+ self.destroyed = true;
+ }
};
- self.destroy = function () {
- self.destroyed = true;
- if (self.parent) self.parent.removeChild(self);
- };
return self;
});
-// Ship (player) class
-var Ship = Container.expand(function () {
+// Player Bullet
+var PlayerBullet = Container.expand(function () {
var self = Container.call(this);
- // Attach ship asset
- var gfx = self.attachAsset('ship', {
+ var gfx = self.attachAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5
});
- // Ship properties
+ // 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.hp = 3;
- self.maxHp = 3;
- self.bulletCount = 1;
- self.bulletSpread = 0.12;
- self.fireRate = 16;
+ self.speedY = -28;
+ self.speedX = 0;
+ 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.doubleShot = false;
self.tripleShot = false;
- self.buzzSaw = false;
self.laser = false;
- self.bomb = false;
+ self.hp = 100;
+ self.maxHp = 100;
self.invulnTicks = 0;
- self.bulletSize = 1; // Default bullet size
- // Update method
self.update = function () {
if (self.fireCooldown > 0) self.fireCooldown--;
- if (self.invulnTicks > 0) self.invulnTicks--;
- };
- // Take damage
- 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.showGameOver();
+ 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;
}
};
- // Shoot method
self.shoot = function () {
if (self.fireCooldown > 0) return;
- // BuzzSaw
- if (self.buzzSaw) {
- for (var i = 0; i < (self.buzzSawCount || 1); i++) {
- var buzz = new BuzzSaw();
- buzz.x = self.x;
- buzz.y = self.y - self.height / 2 - 30;
- buzz.speedX = Math.sin((i - (self.buzzSawCount - 1) / 2) * 0.3) * 12;
- buzz.speedY = -18;
- playerBullets.push(buzz);
- game.addChild(buzz);
- }
- self.fireCooldown = self.fireRate;
- 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;
}
- // Laser
+ // 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 - 30;
- laser.width = self.laserSize || 30;
+ laser.y = self.y - self.height / 2 - 10;
playerBullets.push(laser);
game.addChild(laser);
- self.fireCooldown = self.fireRate;
- return;
+ shots.push(laser);
}
- // Bomb
- if (self.bomb) {
- for (var i = 0; i < (self.bombCount || 1); i++) {
- var bomb = new Bomb();
- bomb.x = self.x;
- bomb.y = self.y - self.height / 2 - 30;
- bomb.speedY = -10 - i * 2;
- playerBullets.push(bomb);
- game.addChild(bomb);
- }
- self.fireCooldown = self.fireRate;
- return;
+ // 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
- var count = self.bulletCount;
- var spread = self.bulletSpread;
- var size = self.bulletSize || 1;
for (var i = 0; i < count; i++) {
var angle = (i - (count - 1) / 2) * spread;
- var b = new PlayerBullet(size);
- b.x = self.x + Math.sin(angle) * 40;
- b.y = self.y - self.height / 2 - 30;
- b.speedX = Math.sin(angle) * 10;
- b.speedY = -18;
- playerBullets.push(b);
- game.addChild(b);
+ 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);
}
- // Double shot
- if (self.doubleShot) {
- for (var i = 0; i < count; i++) {
- var angle = (i - (count - 1) / 2) * spread;
- var b = new PlayerBullet(size);
- b.x = self.x + Math.sin(angle) * 40 - 40;
- b.y = self.y - self.height / 2 - 30;
- b.speedX = Math.sin(angle) * 10;
- b.speedY = -18;
- playerBullets.push(b);
- game.addChild(b);
- }
+ 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();
}
- // Triple shot
- if (self.tripleShot) {
- for (var i = 0; i < count; i++) {
- var angle = (i - (count - 1) / 2) * spread;
- var b = new PlayerBullet(size);
- b.x = self.x + Math.sin(angle) * 40 + 40;
- b.y = self.y - self.height / 2 - 30;
- b.speedX = Math.sin(angle) * 10;
- b.speedY = -18;
- playerBullets.push(b);
- game.addChild(b);
- }
- }
- self.fireCooldown = self.fireRate;
};
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
****/
@@ -261,17 +414,17 @@
/****
* Game Code
****/
-// Game state variables
-// Ship (player)
-// Enemy
-// Bullet (player)
-// Enemy bullet
-// Upgrade card
-// Upgrade highlight
-// Sound effects
// Music
+// Sound effects
+// Upgrade highlight
+// Upgrade card
+// Enemy bullet
+// Bullet (player)
+// Enemy
+// Ship (player)
+// Game state variables
var ship;
var playerBullets = [];
var enemies = [];
var enemyBullets = [];
@@ -318,8 +471,85 @@
stars.push(star);
// Add behind everything
game.addChildAt(star, 0);
}
+// --- MENU SCREEN ---
+var menuGroup = new Container();
+game.addChild(menuGroup);
+// Title
+var titleTxt = new Text2("DEFENDER", {
+ size: 180,
+ fill: "#fff"
+});
+titleTxt.anchor.set(0.5, 0.5);
+titleTxt.x = 2048 / 2;
+titleTxt.y = 700;
+menuGroup.addChild(titleTxt);
+// Play Button
+var playBtn = new Container();
+var playBtnBg = playBtn.attachAsset('upgradeCard', {
+ anchorX: 0.5,
+ anchorY: 0.5
+});
+playBtnBg.width = 600;
+playBtnBg.height = 220;
+playBtnBg.tint = 0x44ff44;
+var playBtnTxt = new Text2("PLAY", {
+ size: 120,
+ fill: "#222"
+});
+playBtnTxt.anchor.set(0.5, 0.5);
+playBtnTxt.x = 0;
+playBtnTxt.y = 0;
+playBtn.addChild(playBtnTxt);
+playBtn.x = 2048 / 2;
+playBtn.y = 1200;
+menuGroup.addChild(playBtn);
+// Hide menu and start game
+function startGameFromMenu() {
+ menuGroup.visible = false;
+ // 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;
+ // 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();
+}
+// Only allow play button to be pressed once
+var menuStarted = false;
+playBtn.down = function (x, y, obj) {
+ if (menuStarted) return;
+ menuStarted = true;
+ startGameFromMenu();
+};
// Animate stars in game.update
var oldGameUpdate = game.update;
game.update = function () {
// Move stars downward, wrap to top
@@ -332,42 +562,8 @@
}
}
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;
-// 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)) {
@@ -649,47 +845,31 @@
waveTxt.setText('Wave: ' + wave);
}
// Spawn a single enemy
function spawnEnemy() {
- // Boss every 5th wave, only spawn one boss per boss wave
+ // 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 (wave % 5 === 0 && enemiesToSpawn === enemiesPerWave) {
- // Only spawn one boss at start of boss wave
- e = new BossEnemy();
- e.x = 2048 / 2;
+ 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 = 40 + Math.floor(wave * 3);
+ e.hp = 6 + Math.floor(wave * 1.5);
e.maxHp = e.hp;
- e.speedY = 3 + Math.floor(wave / 10);
- e.shootRate = Math.max(20, 40 - Math.floor(wave / 2));
- e.specialGunRate = Math.max(60, 120 - wave * 2);
- e.value = 50 + Math.floor(wave * 2.5);
- enemiesToSpawn = 1; // Only one boss
+ 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 {
- // 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);
- 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);
- }
+ 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--;