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;
// 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);
card.x = centerX + (i - (shipTypes.length - 1) / 2) * cardSpacing;
card.y = centerY;
card.cardIndex = i;
shipSelectGroup.addChild(card);
shipCards.push(card);
}
// Highlight for selected card
var selectHighlight = new Container();
var highlightGfx = selectHighlight.attachAsset('upgradeHighlight', {
anchorX: 0.5,
anchorY: 0.5
});
highlightGfx.width = 560;
highlightGfx.height = 690;
selectHighlight.visible = true;
shipSelectGroup.addChild(selectHighlight);
// Position highlight initially
function updateShipHighlight() {
selectHighlight.x = shipCards[selectedShipType].x;
selectHighlight.y = shipCards[selectedShipType].y;
}
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 further down below 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
@@ -1103,43 +1103,43 @@
var cardGfx = card.attachAsset('upgradeCard', {
anchorX: 0.5,
anchorY: 0.5
});
- cardGfx.width = 420;
- cardGfx.height = 520;
+ cardGfx.width = 540;
+ cardGfx.height = 670;
cardGfx.tint = shipTypes[i].color;
// Ship name
var nameTxt = new Text2(shipTypes[i].name, {
- size: 70,
+ size: 90,
fill: "#fff"
});
nameTxt.anchor.set(0.5, 0);
- nameTxt.y = -210;
+ nameTxt.y = -270;
card.addChild(nameTxt);
// Ship preview
var preview = card.attachAsset('ship', {
anchorX: 0.5,
anchorY: 0.5
});
- preview.width = 120;
- preview.height = 120;
+ preview.width = 180;
+ preview.height = 180;
preview.tint = shipTypes[i].color;
- preview.y = -60;
+ 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 = 60;
+ statsTxt.y = 100;
card.addChild(statsTxt);
// Description
var descTxt = new Text2(shipTypes[i].desc, {
- size: 40,
+ size: 52,
fill: "#fff"
});
descTxt.anchor.set(0.5, 0);
- descTxt.y = 200;
+ descTxt.y = 300;
card.addChild(descTxt);
card.x = centerX + (i - (shipTypes.length - 1) / 2) * cardSpacing;
card.y = centerY;
card.cardIndex = i;
@@ -1151,10 +1151,10 @@
var highlightGfx = selectHighlight.attachAsset('upgradeHighlight', {
anchorX: 0.5,
anchorY: 0.5
});
-highlightGfx.width = 440;
-highlightGfx.height = 540;
+highlightGfx.width = 560;
+highlightGfx.height = 690;
selectHighlight.visible = true;
shipSelectGroup.addChild(selectHighlight);
// Position highlight initially
function updateShipHighlight() {