User prompt
If I hold it down I can fire
User prompt
I want to move our main character with the right click of the mouse.
User prompt
The bullets fired by our main character should be effective and move faster.
User prompt
Let our main character be at the center of the game
User prompt
Let our main character and enemies be bigger. Let giant enemies come every now and then. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Please fix the bug: 'Uncaught TypeError: Cannot read properties of undefined (reading 'cost')' in or related to this line: 'if (gamePoints >= weapon.cost && player.weapon.name !== weapon.name) {' Line Number: 335
User prompt
Please fix the bug: 'Uncaught TypeError: Cannot set properties of undefined (setting 'fill')' in or related to this line: 'weaponTexts[i].style.fill = color;' Line Number: 326
Code edit (1 edits merged)
Please save this source code
User prompt
Arsenal Defender
Initial prompt
I want to create a game where you eliminate incoming enemies one by one, and earn points for each enemy you eliminate. With those points, you can buy different weapons. I want to upload my own image for the shooting character and also the appearances of the enemies.
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Boss = Container.expand(function () {
var self = Container.call(this);
var bossGraphics = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 9; // Will be set to wave * 9 (3x giant health)
self.maxHealth = 9;
self.speed = 0.5; // Slower than giant enemies
self.points = 100;
self.isBoss = true;
self.velocityX = 0;
self.velocityY = 0;
self.update = function () {
if (player) {
var dx = player.x - self.x;
var dy = player.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
// Boss moves slowly and deliberately
var randomX = (Math.random() - 0.5) * 1;
var randomY = (Math.random() - 0.5) * 1;
self.velocityX = (dx / distance + randomX * 0.2) * self.speed;
self.velocityY = (dy / distance + randomY * 0.2) * self.speed;
}
}
self.x += self.velocityX;
self.y += self.velocityY;
};
self.takeDamage = function (damage) {
self.health -= damage;
LK.getSound('enemyHit').play();
// Flash red when hit
tween(bossGraphics, {
tint: 0xFF0000
}, {
duration: 100,
onFinish: function onFinish() {
tween(bossGraphics, {
tint: 0xFFFFFF
}, {
duration: 100
});
}
});
if (self.health <= 0) {
self.destroy();
return true;
}
return false;
};
self.makeBoss = function () {
// Boss has 3 times the health of giant enemies
self.health = wave * 9; // 3x giant health (wave * 3 * 3)
self.maxHealth = self.health;
self.speed = 0.5; // Slower than giants
self.points = self.points * 10; // High point value
// Scale up to twice the size of giant enemies (5x total)
tween(bossGraphics, {
scaleX: 5.0,
scaleY: 5.0,
tint: 0xFF6600 // Orange tint to distinguish from other enemies
}, {
duration: 1000,
easing: tween.bounceOut
});
};
return self;
});
var Bullet = Container.expand(function () {
var self = Container.call(this);
self.isSuper = false;
var bulletGraphics = self.attachAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.velocityX = 0;
self.velocityY = 0;
self.damage = 1;
self.makeSuper = function () {
self.isSuper = true;
self.removeChild(bulletGraphics);
bulletGraphics = self.attachAsset('superBullet', {
anchorX: 0.5,
anchorY: 0.5
});
// Add glowing effect with tween
tween(bulletGraphics, {
scaleX: 1.2,
scaleY: 1.2,
tint: 0xffff00
}, {
duration: 200,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(bulletGraphics, {
scaleX: 1.0,
scaleY: 1.0,
tint: 0xff4444
}, {
duration: 200,
easing: tween.easeInOut
});
}
});
};
self.update = function () {
self.x += self.velocityX;
self.y += self.velocityY;
};
return self;
});
var Enemy = Container.expand(function () {
var self = Container.call(this);
var enemyGraphics = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 3;
self.maxHealth = 3;
self.speed = 1;
self.points = 10;
self.isGiant = false;
self.velocityX = 0;
self.velocityY = 0;
self.update = function () {
if (player) {
var dx = player.x - self.x;
var dy = player.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
// Giant enemies change direction more aggressively
if (self.isGiant) {
// Add some randomness to giant enemy movement to make them more unpredictable
var randomX = (Math.random() - 0.5) * 2;
var randomY = (Math.random() - 0.5) * 2;
self.velocityX = (dx / distance + randomX * 0.3) * self.speed;
self.velocityY = (dy / distance + randomY * 0.3) * self.speed;
} else {
self.velocityX = dx / distance * self.speed;
self.velocityY = dy / distance * self.speed;
}
}
}
self.x += self.velocityX;
self.y += self.velocityY;
};
self.takeDamage = function (damage) {
self.health -= damage;
LK.getSound('enemyHit').play();
// Flash red when hit
tween(enemyGraphics, {
tint: 0xFF0000
}, {
duration: 100,
onFinish: function onFinish() {
tween(enemyGraphics, {
tint: 0xFFFFFF
}, {
duration: 100
});
}
});
if (self.health <= 0) {
self.destroy();
return true;
}
return false;
};
self.makeGiant = function () {
self.isGiant = true;
// Giant enemies have 3 times the health of regular enemies
self.health = wave * 3;
self.maxHealth = self.health;
self.speed = self.speed * 0.7;
self.points = self.points * 4;
// Scale up with tween animation
tween(enemyGraphics, {
scaleX: 2.5,
scaleY: 2.5
}, {
duration: 500,
easing: tween.bounceOut
});
};
return self;
});
var Player = Container.expand(function () {
var self = Container.call(this);
var playerGraphics = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 100;
self.maxHealth = 100;
self.weapon = {
damage: 1,
fireRate: 10,
name: 'Basic Gun'
};
self.lastShot = 0;
self.shoot = function (targetX, targetY) {
// Super bullets have much slower fire rate
var currentFireRate = superBulletActive ? 60 : self.weapon.fireRate; // 60 ticks = 1 second delay for super bullets
if (LK.ticks - self.lastShot < currentFireRate) return;
var bullet = new Bullet();
bullet.x = self.x;
bullet.y = self.y;
if (superBulletActive) {
bullet.makeSuper();
bullet.damage = superBulletDamage; // Use very high damage for super bullets
} else {
bullet.damage = self.weapon.damage;
}
var dx = targetX - self.x;
var dy = targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
bullet.velocityX = dx / distance * 15;
bullet.velocityY = dy / distance * 15;
bullets.push(bullet);
game.addChild(bullet);
self.lastShot = LK.ticks;
LK.getSound('shoot').play();
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x1a1a1a
});
/****
* Game Code
****/
// Game state variables
var player;
var enemies = [];
var bullets = [];
var gamePoints = storage.points || 0;
var wave = storage.wave || 1;
var enemiesKilled = 0;
var enemiesInWave = 5;
var waveComplete = false;
var smallEnemiesSpawned = 0;
var bossSpawned = false;
var bossActive = false;
var shopOpen = false;
var isMouseDown = false;
var lastMouseX = 0;
var lastMouseY = 0;
var superBulletActive = false;
var superBulletCooldown = 0;
var superBulletDuration = 10000; // 10 seconds
var superBulletCooldownTime = 60000; // 60 seconds (1 minute)
var superBulletDamage = 100; // Very high damage for devastating effect
var atomCooldown = 0;
var atomCooldownTime = 120000; // 120 seconds (2 minutes)
// Weapon shop data - all weapons deal 1 damage for consistent bullet counting
var weapons = [{
name: 'Basic Gun',
damage: 1,
fireRate: 10,
cost: 0
}, {
name: 'Rapid Fire',
damage: 1,
fireRate: 5,
cost: 50
}, {
name: 'Heavy Cannon',
damage: 1,
fireRate: 20,
cost: 100
}, {
name: 'Sniper Rifle',
damage: 1,
fireRate: 30,
cost: 200
}, {
name: 'Plasma Gun',
damage: 1,
fireRate: 8,
cost: 150
}];
// UI Elements
var pointsText = new Text2('Points: 0', {
size: 40,
fill: 0xFFFFFF
});
pointsText.anchor.set(0, 0);
pointsText.x = 120;
pointsText.y = 20;
LK.gui.topLeft.addChild(pointsText);
var waveText = new Text2('Wave: 1', {
size: 40,
fill: 0xFFFFFF
});
waveText.anchor.set(1, 0);
waveText.x = -20;
waveText.y = 20;
LK.gui.topRight.addChild(waveText);
var healthText = new Text2('Health: 100', {
size: 40,
fill: 0xFF4444
});
healthText.anchor.set(0.5, 1);
healthText.y = -80;
LK.gui.bottom.addChild(healthText);
// Life bar background
var lifeBarBackground = LK.getAsset('lifeBarBackground', {
anchorX: 0.5,
anchorY: 0.5
});
lifeBarBackground.x = 0;
lifeBarBackground.y = -40;
LK.gui.bottom.addChild(lifeBarBackground);
// Life bar foreground
var lifeBar = LK.getAsset('lifeBar', {
anchorX: 0,
anchorY: 0.5
});
lifeBar.x = -400; // Start from left edge of background (doubled size)
lifeBar.y = -40;
LK.gui.bottom.addChild(lifeBar);
// Power button in middle left
var powerButton = game.addChild(LK.getAsset('powerButton', {
anchorX: 0.5,
anchorY: 0.5,
x: 120,
y: 1200
}));
// Scale power button to 3.75 times bigger with smooth animation
tween(powerButton, {
scaleX: 3.75,
scaleY: 3.75
}, {
duration: 800,
easing: tween.bounceOut
});
var powerButtonText = new Text2('SUPER', {
size: 24,
fill: 0xFFFFFF
});
powerButtonText.anchor.set(0.5, 0.5);
powerButtonText.x = powerButton.x;
powerButtonText.y = powerButton.y;
game.addChild(powerButtonText);
var powerCooldownText = new Text2('', {
size: 20,
fill: 0xFFFFFF
});
powerCooldownText.anchor.set(0.5, 0.5);
powerCooldownText.x = powerButton.x;
powerCooldownText.y = powerButton.y + 50;
game.addChild(powerCooldownText);
// Restart button under the atom button
var restartButton = game.addChild(LK.getAsset('restartButton', {
anchorX: 0.5,
anchorY: 0.5,
x: 120,
y: 2200 // Position below atom button
}));
// Scale restart button to 3.75 times bigger with smooth animation
tween(restartButton, {
scaleX: 3.75,
scaleY: 3.75
}, {
duration: 800,
easing: tween.bounceOut
});
var restartButtonText = new Text2('RESTART', {
size: 18,
fill: 0xFFFFFF
});
restartButtonText.anchor.set(0.5, 0.5);
restartButtonText.x = restartButton.x;
restartButtonText.y = restartButton.y;
game.addChild(restartButtonText);
// Atom button under the power button
var atomButton = game.addChild(LK.getAsset('atomButton', {
anchorX: 0.5,
anchorY: 0.5,
x: 120,
y: 1700 // Further increased spacing to prevent overlap with scaled buttons
}));
// Scale atom button to 3.75 times bigger with smooth animation
tween(atomButton, {
scaleX: 3.75,
scaleY: 3.75
}, {
duration: 800,
easing: tween.bounceOut
});
var atomButtonText = new Text2('ATOM', {
size: 24,
fill: 0xFFFFFF
});
atomButtonText.anchor.set(0.5, 0.5);
atomButtonText.x = atomButton.x;
atomButtonText.y = atomButton.y;
game.addChild(atomButtonText);
var atomCooldownText = new Text2('', {
size: 20,
fill: 0xFFFFFF
});
atomCooldownText.anchor.set(0.5, 0.5);
atomCooldownText.x = atomButton.x;
atomCooldownText.y = atomButton.y + 50;
game.addChild(atomCooldownText);
// Shop button
var shopButton = game.addChild(LK.getAsset('shopButton', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 - 120,
y: 2732 - 100
}));
var shopButtonText = new Text2('SHOP', {
size: 32,
fill: 0xFFFFFF
});
shopButtonText.anchor.set(0.5, 0.5);
shopButtonText.x = shopButton.x;
shopButtonText.y = shopButton.y;
game.addChild(shopButtonText);
// Shop UI
var shopContainer = new Container();
shopContainer.visible = false;
game.addChild(shopContainer);
var shopBackground = shopContainer.addChild(LK.getAsset('shopButton', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1366,
scaleX: 6,
scaleY: 9
}));
var shopTitle = new Text2('WEAPON SHOP', {
size: 60,
fill: 0xFFFFFF
});
shopTitle.anchor.set(0.5, 0.5);
shopTitle.x = 1024;
shopTitle.y = 800;
shopContainer.addChild(shopTitle);
var closeShopButton = shopContainer.addChild(LK.getAsset('shopButton', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1800,
scaleX: 1.5,
scaleY: 0.75
}));
var closeShopText = new Text2('CLOSE', {
size: 40,
fill: 0xFFFFFF
});
closeShopText.anchor.set(0.5, 0.5);
closeShopText.x = 1024;
closeShopText.y = 1800;
shopContainer.addChild(closeShopText);
// Weapon shop items
var weaponButtons = [];
var weaponTexts = [];
for (var i = 0; i < weapons.length; i++) {
var weapon = weapons[i];
var buttonY = 1000 + i * 120;
var weaponButton = shopContainer.addChild(LK.getAsset('shopButton', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: buttonY,
scaleX: 2.25,
scaleY: 0.75
}));
weaponButton.weaponIndex = i;
weaponButtons.push(weaponButton);
var weaponText = new Text2(weapon.name + ' - ' + weapon.cost + ' pts', {
size: 32,
fill: 0xFFFFFF
});
weaponText.anchor.set(0.5, 0.5);
weaponText.x = 1024;
weaponText.y = buttonY;
shopContainer.addChild(weaponText);
weaponTexts.push(weaponText);
}
// Initialize player
player = game.addChild(new Player());
player.x = 1024;
player.y = 1366;
// Scale player to 3 times bigger with smooth animation
var playerGraphics = player.children[0];
playerGraphics.scaleX = 1;
playerGraphics.scaleY = 1;
tween(playerGraphics, {
scaleX: 3,
scaleY: 3
}, {
duration: 800,
easing: tween.bounceOut
});
// Game functions
function spawnEnemy() {
var enemy = new Enemy();
// Spawn from random edge
var side = Math.floor(Math.random() * 4);
switch (side) {
case 0:
// Top
enemy.x = Math.random() * 2048;
enemy.y = -50;
break;
case 1:
// Right
enemy.x = 2098;
enemy.y = Math.random() * 2732;
break;
case 2:
// Bottom
enemy.x = Math.random() * 2048;
enemy.y = 2782;
break;
case 3:
// Left
enemy.x = -50;
enemy.y = Math.random() * 2732;
break;
}
// Scale enemy with wave - health equals wave number for bullet count requirement
enemy.health = wave; // Wave 1 = 1 bullet, Wave 2 = 2 bullets, etc.
enemy.maxHealth = enemy.health;
enemy.speed = 1 + wave * 0.1;
enemy.points = 10 + wave * 2;
// Spawn giant enemy after every 10 small enemies
if (smallEnemiesSpawned >= 10) {
enemy.makeGiant();
smallEnemiesSpawned = 0; // Reset counter after spawning giant
} else {
smallEnemiesSpawned++; // Increment counter for small enemy
}
enemies.push(enemy);
game.addChild(enemy);
// Add spawn animation for all enemies
var enemyGraphics = enemy.children[0];
enemyGraphics.scaleX = 0.1;
enemyGraphics.scaleY = 0.1;
tween(enemyGraphics, {
scaleX: enemy.isGiant ? 2.5 : 1,
scaleY: enemy.isGiant ? 2.5 : 1
}, {
duration: 300,
easing: tween.elasticOut
});
}
function spawnBoss() {
var boss = new Boss();
// Spawn boss from center top
boss.x = 1024;
boss.y = -100;
boss.makeBoss();
enemies.push(boss);
game.addChild(boss);
bossSpawned = true;
bossActive = true;
// Add dramatic spawn animation for boss
var bossGraphics = boss.children[0];
bossGraphics.scaleX = 0.1;
bossGraphics.scaleY = 0.1;
tween(bossGraphics, {
scaleX: 5.0,
scaleY: 5.0
}, {
duration: 1500,
easing: tween.elasticOut
});
}
function updateUI() {
pointsText.setText('Points: ' + gamePoints);
waveText.setText('Wave: ' + wave);
healthText.setText('Health: ' + player.health);
// Update life bar width based on player health
var healthPercentage = player.health / player.maxHealth;
var newWidth = 800 * healthPercentage;
lifeBar.width = Math.max(0, newWidth);
// Change life bar color based on health percentage
if (healthPercentage > 0.6) {
lifeBar.tint = 0x4caf50; // Green
} else if (healthPercentage > 0.3) {
lifeBar.tint = 0xffc107; // Yellow
} else {
lifeBar.tint = 0xf44336; // Red
}
}
function openShop() {
shopOpen = true;
shopContainer.visible = true;
// Update weapon button states
for (var i = 0; i < weapons.length; i++) {
var weapon = weapons[i];
var canAfford = gamePoints >= weapon.cost;
var owned = player.weapon.name === weapon.name;
var colorHex = owned ? 0x00ff00 : canAfford ? 0xffffff : 0x666666;
var text = weapon.name + ' - ' + weapon.cost + ' pts';
if (owned) text = weapon.name + ' - EQUIPPED';
weaponTexts[i].setText(text);
weaponTexts[i].tint = colorHex;
}
}
function closeShop() {
shopOpen = false;
shopContainer.visible = false;
}
function buyWeapon(weaponIndex) {
var weapon = weapons[weaponIndex];
if (weapon && gamePoints >= weapon.cost && player.weapon.name !== weapon.name) {
gamePoints -= weapon.cost;
player.weapon = {
damage: weapon.damage,
fireRate: weapon.fireRate,
name: weapon.name
};
storage.points = gamePoints;
updateUI();
openShop(); // Refresh shop display
}
}
// Event handlers
shopButton.down = function (x, y, obj) {
openShop();
};
closeShopButton.down = function (x, y, obj) {
closeShop();
};
for (var i = 0; i < weaponButtons.length; i++) {
weaponButtons[i].down = function (x, y, obj) {
buyWeapon(obj.weaponIndex);
};
}
powerButton.down = function (x, y, obj) {
if (superBulletCooldown <= 0 && !superBulletActive) {
superBulletActive = true;
superBulletCooldown = superBulletCooldownTime;
// Visual feedback
tween(powerButton, {
scaleX: 1.2,
scaleY: 1.2,
tint: 0x00ff00
}, {
duration: 200,
easing: tween.bounceOut
});
LK.setTimeout(function () {
superBulletActive = false;
tween(powerButton, {
scaleX: 1.0,
scaleY: 1.0,
tint: 0xffffff
}, {
duration: 200
});
}, superBulletDuration);
}
};
restartButton.down = function (x, y, obj) {
// Reset all game state to starting values
gamePoints = 0;
wave = 1;
enemiesKilled = 0;
enemiesInWave = 5;
waveComplete = false;
smallEnemiesSpawned = 0;
bossSpawned = false;
bossActive = false;
superBulletActive = false;
superBulletCooldown = 0;
atomCooldown = 0;
isMouseDown = false;
// Reset player health and position
player.health = 100;
player.maxHealth = 100;
player.weapon = {
damage: 1,
fireRate: 10,
name: 'Basic Gun'
};
player.x = 1024;
player.y = 1366;
// Clear all enemies and bullets
for (var i = enemies.length - 1; i >= 0; i--) {
enemies[i].destroy();
}
enemies = [];
for (var i = bullets.length - 1; i >= 0; i--) {
bullets[i].destroy();
}
bullets = [];
// Reset storage
storage.points = 0;
storage.wave = 1;
// Visual feedback for restart button
tween(restartButton, {
scaleX: 1.2,
scaleY: 1.2,
tint: 0x00ff00
}, {
duration: 200,
easing: tween.bounceOut
});
LK.setTimeout(function () {
tween(restartButton, {
scaleX: 1.0,
scaleY: 1.0,
tint: 0xffffff
}, {
duration: 200
});
}, 500);
// Spawn initial enemies for wave 1
for (var i = 0; i < enemiesInWave; i++) {
LK.setTimeout(function () {
spawnEnemy();
}, i * 2000);
}
};
atomButton.down = function (x, y, obj) {
if (atomCooldown <= 0) {
// Make all enemies disappear by going up quickly with moving effect
for (var i = enemies.length - 1; i >= 0; i--) {
var enemy = enemies[i];
gamePoints += enemy.points;
enemiesKilled++;
LK.getSound('enemyDestroy').play();
// Create moving effect - enemies fly up quickly and fade out
tween(enemy, {
y: enemy.y - 1000,
alpha: 0,
scaleX: 0.1,
scaleY: 0.1
}, {
duration: 500,
easing: tween.easeIn,
onFinish: function onFinish() {
enemy.destroy();
}
});
enemies.splice(i, 1);
}
atomCooldown = atomCooldownTime;
// Visual feedback
tween(atomButton, {
scaleX: 1.2,
scaleY: 1.2,
tint: 0x00ff00
}, {
duration: 200,
easing: tween.bounceOut
});
LK.setTimeout(function () {
tween(atomButton, {
scaleX: 1.0,
scaleY: 1.0,
tint: 0xffffff
}, {
duration: 200
});
}, 500);
}
};
game.down = function (x, y, obj) {
if (!shopOpen && player) {
// Right click moves player, left click shoots
if (obj.event && obj.event.button === 2) {
// Right click - move player
player.x = x;
player.y = y;
} else {
// Left click or touch - shoot
isMouseDown = true;
lastMouseX = x;
lastMouseY = y;
player.shoot(x, y);
}
}
};
// Spawn initial enemies
for (var i = 0; i < enemiesInWave; i++) {
LK.setTimeout(function () {
spawnEnemy();
}, i * 2000);
}
game.move = function (x, y, obj) {
if (isMouseDown && !shopOpen) {
lastMouseX = x;
lastMouseY = y;
}
};
game.up = function (x, y, obj) {
isMouseDown = false;
};
// Prevent context menu on right click
game.on('rightdown', function (x, y, obj) {
if (obj.event) {
obj.event.preventDefault();
}
});
// Main game loop
game.update = function () {
if (shopOpen) return;
// Continuous firing when mouse is held down
if (isMouseDown && player) {
player.shoot(lastMouseX, lastMouseY);
}
// Update bullets
for (var i = bullets.length - 1; i >= 0; i--) {
var bullet = bullets[i];
// Check if bullet is off screen
if (bullet.x < -50 || bullet.x > 2098 || bullet.y < -50 || bullet.y > 2782) {
bullet.destroy();
bullets.splice(i, 1);
continue;
}
// Check bullet-enemy collisions
var hitEnemy = false;
for (var j = enemies.length - 1; j >= 0; j--) {
var enemy = enemies[j];
if (bullet.intersects(enemy)) {
if (bullet.isSuper) {
// Super bullet area damage - hit all enemies in area
var areaRadius = 200;
for (var k = enemies.length - 1; k >= 0; k--) {
var areaEnemy = enemies[k];
var dx = areaEnemy.x - bullet.x;
var dy = areaEnemy.y - bullet.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= areaRadius) {
var destroyed = areaEnemy.takeDamage(bullet.damage);
if (destroyed) {
gamePoints += areaEnemy.points;
enemiesKilled++;
if (areaEnemy.isBoss) {
bossActive = false;
}
LK.getSound('enemyDestroy').play();
enemies.splice(k, 1);
if (k < j) j--; // Adjust index if we removed an enemy before current
}
}
}
} else {
// Regular bullet single target damage
var destroyed = enemy.takeDamage(bullet.damage);
if (destroyed) {
gamePoints += enemy.points;
enemiesKilled++;
if (enemy.isBoss) {
bossActive = false;
}
LK.getSound('enemyDestroy').play();
enemies.splice(j, 1);
}
}
bullet.destroy();
bullets.splice(i, 1);
hitEnemy = true;
break;
}
}
if (hitEnemy) continue;
}
// Check enemy-player collisions
for (var i = enemies.length - 1; i >= 0; i--) {
var enemy = enemies[i];
if (enemy.intersects(player)) {
// Boss enemies deal 3x damage, giant enemies deal 2x damage compared to regular enemies
var damage = enemy.isBoss ? 30 : enemy.isGiant ? 20 : 10;
player.health -= damage;
if (enemy.isBoss) {
bossActive = false;
}
enemy.destroy();
enemies.splice(i, 1);
if (player.health <= 0) {
// Game over - reset to chapter 1
gamePoints = 0; // Reset score to 0 when player dies
storage.points = gamePoints;
storage.wave = 1; // Reset to chapter 1 when player dies
LK.showGameOver();
return;
}
}
}
// Check wave completion
if (enemiesKilled >= enemiesInWave && enemies.length === 0 && !waveComplete && !bossSpawned) {
// Only spawn boss at the end of wave 1 (first section)
if (wave === 1) {
bossSpawned = true;
waveComplete = true; // Mark wave as complete so no more regular enemies spawn
LK.setTimeout(function () {
spawnBoss();
}, 2000);
} else {
// For all other waves, advance directly to next wave without boss
waveComplete = true;
wave++;
enemiesKilled = 0;
smallEnemiesSpawned = 0;
bossSpawned = false;
bossActive = false;
enemiesInWave = Math.min(5 + wave, 15);
// Spawn next wave after delay
LK.setTimeout(function () {
waveComplete = false;
for (var i = 0; i < enemiesInWave; i++) {
LK.setTimeout(function () {
spawnEnemy();
}, i * 1500);
}
}, 3000);
}
} else if (bossSpawned && !bossActive && enemies.length === 0 && waveComplete) {
// Boss defeated, advance to next wave (this will be wave 2 after boss from wave 1)
wave++;
enemiesKilled = 0;
smallEnemiesSpawned = 0; // Reset small enemies counter for new wave
bossSpawned = false;
bossActive = false;
enemiesInWave = Math.min(5 + wave, 15);
// Spawn next wave after delay
LK.setTimeout(function () {
waveComplete = false;
for (var i = 0; i < enemiesInWave; i++) {
LK.setTimeout(function () {
spawnEnemy();
}, i * 1500);
}
}, 3000);
}
// Periodic enemy spawning during wave - but not when boss is active or wave is complete
if (!waveComplete && !bossActive && enemies.length < Math.min(enemiesInWave, 8)) {
if (Math.random() < 0.01) {
spawnEnemy();
}
}
// Update super bullet system
if (superBulletCooldown > 0) {
superBulletCooldown -= 16.67; // Approximately 1 frame at 60fps
if (superBulletCooldown < 0) superBulletCooldown = 0;
}
// Update atom button system
if (atomCooldown > 0) {
atomCooldown -= 16.67; // Approximately 1 frame at 60fps
if (atomCooldown < 0) atomCooldown = 0;
}
// Update power button UI
if (superBulletActive) {
var remaining = Math.ceil((superBulletDuration - LK.ticks * 16.67) / 1000);
powerButtonText.setText('ACTIVE');
powerCooldownText.setText('');
if (powerButton.tint !== 0x00ff00) {
tween(powerButton, {
tint: 0x00ff00,
scaleX: 3.75,
scaleY: 3.75
}, {
duration: 300,
easing: tween.easeOut
});
}
} else if (superBulletCooldown > 0) {
var cooldownSeconds = Math.ceil(superBulletCooldown / 1000);
powerButtonText.setText('SUPER');
powerCooldownText.setText(cooldownSeconds + 's');
if (powerButton.tint !== 0xff0000) {
tween(powerButton, {
tint: 0xff0000,
scaleX: 2.5,
scaleY: 2.5
}, {
duration: 300,
easing: tween.easeOut
});
}
} else {
powerButtonText.setText('SUPER');
powerCooldownText.setText('READY');
if (powerButton.tint !== 0x00ff00) {
tween(powerButton, {
tint: 0x00ff00,
scaleX: 3.75,
scaleY: 3.75
}, {
duration: 300,
easing: tween.easeOut
});
}
}
// Update atom button UI
if (atomCooldown > 0) {
var atomCooldownSeconds = Math.ceil(atomCooldown / 1000);
atomButtonText.setText('ATOM');
atomCooldownText.setText(atomCooldownSeconds + 's');
if (atomButton.tint !== 0xff0000) {
tween(atomButton, {
tint: 0xff0000,
scaleX: 2.5,
scaleY: 2.5
}, {
duration: 300,
easing: tween.easeOut
});
}
} else {
atomButtonText.setText('ATOM');
atomCooldownText.setText('READY');
if (atomButton.tint !== 0x00ff00) {
tween(atomButton, {
tint: 0x00ff00,
scaleX: 3.75,
scaleY: 3.75
}, {
duration: 300,
easing: tween.easeOut
});
}
}
updateUI();
// Save progress
if (LK.ticks % 300 === 0) {
storage.points = gamePoints;
storage.wave = wave;
}
}; /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Boss = Container.expand(function () {
var self = Container.call(this);
var bossGraphics = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 9; // Will be set to wave * 9 (3x giant health)
self.maxHealth = 9;
self.speed = 0.5; // Slower than giant enemies
self.points = 100;
self.isBoss = true;
self.velocityX = 0;
self.velocityY = 0;
self.update = function () {
if (player) {
var dx = player.x - self.x;
var dy = player.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
// Boss moves slowly and deliberately
var randomX = (Math.random() - 0.5) * 1;
var randomY = (Math.random() - 0.5) * 1;
self.velocityX = (dx / distance + randomX * 0.2) * self.speed;
self.velocityY = (dy / distance + randomY * 0.2) * self.speed;
}
}
self.x += self.velocityX;
self.y += self.velocityY;
};
self.takeDamage = function (damage) {
self.health -= damage;
LK.getSound('enemyHit').play();
// Flash red when hit
tween(bossGraphics, {
tint: 0xFF0000
}, {
duration: 100,
onFinish: function onFinish() {
tween(bossGraphics, {
tint: 0xFFFFFF
}, {
duration: 100
});
}
});
if (self.health <= 0) {
self.destroy();
return true;
}
return false;
};
self.makeBoss = function () {
// Boss has 3 times the health of giant enemies
self.health = wave * 9; // 3x giant health (wave * 3 * 3)
self.maxHealth = self.health;
self.speed = 0.5; // Slower than giants
self.points = self.points * 10; // High point value
// Scale up to twice the size of giant enemies (5x total)
tween(bossGraphics, {
scaleX: 5.0,
scaleY: 5.0,
tint: 0xFF6600 // Orange tint to distinguish from other enemies
}, {
duration: 1000,
easing: tween.bounceOut
});
};
return self;
});
var Bullet = Container.expand(function () {
var self = Container.call(this);
self.isSuper = false;
var bulletGraphics = self.attachAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.velocityX = 0;
self.velocityY = 0;
self.damage = 1;
self.makeSuper = function () {
self.isSuper = true;
self.removeChild(bulletGraphics);
bulletGraphics = self.attachAsset('superBullet', {
anchorX: 0.5,
anchorY: 0.5
});
// Add glowing effect with tween
tween(bulletGraphics, {
scaleX: 1.2,
scaleY: 1.2,
tint: 0xffff00
}, {
duration: 200,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(bulletGraphics, {
scaleX: 1.0,
scaleY: 1.0,
tint: 0xff4444
}, {
duration: 200,
easing: tween.easeInOut
});
}
});
};
self.update = function () {
self.x += self.velocityX;
self.y += self.velocityY;
};
return self;
});
var Enemy = Container.expand(function () {
var self = Container.call(this);
var enemyGraphics = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 3;
self.maxHealth = 3;
self.speed = 1;
self.points = 10;
self.isGiant = false;
self.velocityX = 0;
self.velocityY = 0;
self.update = function () {
if (player) {
var dx = player.x - self.x;
var dy = player.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
// Giant enemies change direction more aggressively
if (self.isGiant) {
// Add some randomness to giant enemy movement to make them more unpredictable
var randomX = (Math.random() - 0.5) * 2;
var randomY = (Math.random() - 0.5) * 2;
self.velocityX = (dx / distance + randomX * 0.3) * self.speed;
self.velocityY = (dy / distance + randomY * 0.3) * self.speed;
} else {
self.velocityX = dx / distance * self.speed;
self.velocityY = dy / distance * self.speed;
}
}
}
self.x += self.velocityX;
self.y += self.velocityY;
};
self.takeDamage = function (damage) {
self.health -= damage;
LK.getSound('enemyHit').play();
// Flash red when hit
tween(enemyGraphics, {
tint: 0xFF0000
}, {
duration: 100,
onFinish: function onFinish() {
tween(enemyGraphics, {
tint: 0xFFFFFF
}, {
duration: 100
});
}
});
if (self.health <= 0) {
self.destroy();
return true;
}
return false;
};
self.makeGiant = function () {
self.isGiant = true;
// Giant enemies have 3 times the health of regular enemies
self.health = wave * 3;
self.maxHealth = self.health;
self.speed = self.speed * 0.7;
self.points = self.points * 4;
// Scale up with tween animation
tween(enemyGraphics, {
scaleX: 2.5,
scaleY: 2.5
}, {
duration: 500,
easing: tween.bounceOut
});
};
return self;
});
var Player = Container.expand(function () {
var self = Container.call(this);
var playerGraphics = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 100;
self.maxHealth = 100;
self.weapon = {
damage: 1,
fireRate: 10,
name: 'Basic Gun'
};
self.lastShot = 0;
self.shoot = function (targetX, targetY) {
// Super bullets have much slower fire rate
var currentFireRate = superBulletActive ? 60 : self.weapon.fireRate; // 60 ticks = 1 second delay for super bullets
if (LK.ticks - self.lastShot < currentFireRate) return;
var bullet = new Bullet();
bullet.x = self.x;
bullet.y = self.y;
if (superBulletActive) {
bullet.makeSuper();
bullet.damage = superBulletDamage; // Use very high damage for super bullets
} else {
bullet.damage = self.weapon.damage;
}
var dx = targetX - self.x;
var dy = targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
bullet.velocityX = dx / distance * 15;
bullet.velocityY = dy / distance * 15;
bullets.push(bullet);
game.addChild(bullet);
self.lastShot = LK.ticks;
LK.getSound('shoot').play();
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x1a1a1a
});
/****
* Game Code
****/
// Game state variables
var player;
var enemies = [];
var bullets = [];
var gamePoints = storage.points || 0;
var wave = storage.wave || 1;
var enemiesKilled = 0;
var enemiesInWave = 5;
var waveComplete = false;
var smallEnemiesSpawned = 0;
var bossSpawned = false;
var bossActive = false;
var shopOpen = false;
var isMouseDown = false;
var lastMouseX = 0;
var lastMouseY = 0;
var superBulletActive = false;
var superBulletCooldown = 0;
var superBulletDuration = 10000; // 10 seconds
var superBulletCooldownTime = 60000; // 60 seconds (1 minute)
var superBulletDamage = 100; // Very high damage for devastating effect
var atomCooldown = 0;
var atomCooldownTime = 120000; // 120 seconds (2 minutes)
// Weapon shop data - all weapons deal 1 damage for consistent bullet counting
var weapons = [{
name: 'Basic Gun',
damage: 1,
fireRate: 10,
cost: 0
}, {
name: 'Rapid Fire',
damage: 1,
fireRate: 5,
cost: 50
}, {
name: 'Heavy Cannon',
damage: 1,
fireRate: 20,
cost: 100
}, {
name: 'Sniper Rifle',
damage: 1,
fireRate: 30,
cost: 200
}, {
name: 'Plasma Gun',
damage: 1,
fireRate: 8,
cost: 150
}];
// UI Elements
var pointsText = new Text2('Points: 0', {
size: 40,
fill: 0xFFFFFF
});
pointsText.anchor.set(0, 0);
pointsText.x = 120;
pointsText.y = 20;
LK.gui.topLeft.addChild(pointsText);
var waveText = new Text2('Wave: 1', {
size: 40,
fill: 0xFFFFFF
});
waveText.anchor.set(1, 0);
waveText.x = -20;
waveText.y = 20;
LK.gui.topRight.addChild(waveText);
var healthText = new Text2('Health: 100', {
size: 40,
fill: 0xFF4444
});
healthText.anchor.set(0.5, 1);
healthText.y = -80;
LK.gui.bottom.addChild(healthText);
// Life bar background
var lifeBarBackground = LK.getAsset('lifeBarBackground', {
anchorX: 0.5,
anchorY: 0.5
});
lifeBarBackground.x = 0;
lifeBarBackground.y = -40;
LK.gui.bottom.addChild(lifeBarBackground);
// Life bar foreground
var lifeBar = LK.getAsset('lifeBar', {
anchorX: 0,
anchorY: 0.5
});
lifeBar.x = -400; // Start from left edge of background (doubled size)
lifeBar.y = -40;
LK.gui.bottom.addChild(lifeBar);
// Power button in middle left
var powerButton = game.addChild(LK.getAsset('powerButton', {
anchorX: 0.5,
anchorY: 0.5,
x: 120,
y: 1200
}));
// Scale power button to 3.75 times bigger with smooth animation
tween(powerButton, {
scaleX: 3.75,
scaleY: 3.75
}, {
duration: 800,
easing: tween.bounceOut
});
var powerButtonText = new Text2('SUPER', {
size: 24,
fill: 0xFFFFFF
});
powerButtonText.anchor.set(0.5, 0.5);
powerButtonText.x = powerButton.x;
powerButtonText.y = powerButton.y;
game.addChild(powerButtonText);
var powerCooldownText = new Text2('', {
size: 20,
fill: 0xFFFFFF
});
powerCooldownText.anchor.set(0.5, 0.5);
powerCooldownText.x = powerButton.x;
powerCooldownText.y = powerButton.y + 50;
game.addChild(powerCooldownText);
// Restart button under the atom button
var restartButton = game.addChild(LK.getAsset('restartButton', {
anchorX: 0.5,
anchorY: 0.5,
x: 120,
y: 2200 // Position below atom button
}));
// Scale restart button to 3.75 times bigger with smooth animation
tween(restartButton, {
scaleX: 3.75,
scaleY: 3.75
}, {
duration: 800,
easing: tween.bounceOut
});
var restartButtonText = new Text2('RESTART', {
size: 18,
fill: 0xFFFFFF
});
restartButtonText.anchor.set(0.5, 0.5);
restartButtonText.x = restartButton.x;
restartButtonText.y = restartButton.y;
game.addChild(restartButtonText);
// Atom button under the power button
var atomButton = game.addChild(LK.getAsset('atomButton', {
anchorX: 0.5,
anchorY: 0.5,
x: 120,
y: 1700 // Further increased spacing to prevent overlap with scaled buttons
}));
// Scale atom button to 3.75 times bigger with smooth animation
tween(atomButton, {
scaleX: 3.75,
scaleY: 3.75
}, {
duration: 800,
easing: tween.bounceOut
});
var atomButtonText = new Text2('ATOM', {
size: 24,
fill: 0xFFFFFF
});
atomButtonText.anchor.set(0.5, 0.5);
atomButtonText.x = atomButton.x;
atomButtonText.y = atomButton.y;
game.addChild(atomButtonText);
var atomCooldownText = new Text2('', {
size: 20,
fill: 0xFFFFFF
});
atomCooldownText.anchor.set(0.5, 0.5);
atomCooldownText.x = atomButton.x;
atomCooldownText.y = atomButton.y + 50;
game.addChild(atomCooldownText);
// Shop button
var shopButton = game.addChild(LK.getAsset('shopButton', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 - 120,
y: 2732 - 100
}));
var shopButtonText = new Text2('SHOP', {
size: 32,
fill: 0xFFFFFF
});
shopButtonText.anchor.set(0.5, 0.5);
shopButtonText.x = shopButton.x;
shopButtonText.y = shopButton.y;
game.addChild(shopButtonText);
// Shop UI
var shopContainer = new Container();
shopContainer.visible = false;
game.addChild(shopContainer);
var shopBackground = shopContainer.addChild(LK.getAsset('shopButton', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1366,
scaleX: 6,
scaleY: 9
}));
var shopTitle = new Text2('WEAPON SHOP', {
size: 60,
fill: 0xFFFFFF
});
shopTitle.anchor.set(0.5, 0.5);
shopTitle.x = 1024;
shopTitle.y = 800;
shopContainer.addChild(shopTitle);
var closeShopButton = shopContainer.addChild(LK.getAsset('shopButton', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1800,
scaleX: 1.5,
scaleY: 0.75
}));
var closeShopText = new Text2('CLOSE', {
size: 40,
fill: 0xFFFFFF
});
closeShopText.anchor.set(0.5, 0.5);
closeShopText.x = 1024;
closeShopText.y = 1800;
shopContainer.addChild(closeShopText);
// Weapon shop items
var weaponButtons = [];
var weaponTexts = [];
for (var i = 0; i < weapons.length; i++) {
var weapon = weapons[i];
var buttonY = 1000 + i * 120;
var weaponButton = shopContainer.addChild(LK.getAsset('shopButton', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: buttonY,
scaleX: 2.25,
scaleY: 0.75
}));
weaponButton.weaponIndex = i;
weaponButtons.push(weaponButton);
var weaponText = new Text2(weapon.name + ' - ' + weapon.cost + ' pts', {
size: 32,
fill: 0xFFFFFF
});
weaponText.anchor.set(0.5, 0.5);
weaponText.x = 1024;
weaponText.y = buttonY;
shopContainer.addChild(weaponText);
weaponTexts.push(weaponText);
}
// Initialize player
player = game.addChild(new Player());
player.x = 1024;
player.y = 1366;
// Scale player to 3 times bigger with smooth animation
var playerGraphics = player.children[0];
playerGraphics.scaleX = 1;
playerGraphics.scaleY = 1;
tween(playerGraphics, {
scaleX: 3,
scaleY: 3
}, {
duration: 800,
easing: tween.bounceOut
});
// Game functions
function spawnEnemy() {
var enemy = new Enemy();
// Spawn from random edge
var side = Math.floor(Math.random() * 4);
switch (side) {
case 0:
// Top
enemy.x = Math.random() * 2048;
enemy.y = -50;
break;
case 1:
// Right
enemy.x = 2098;
enemy.y = Math.random() * 2732;
break;
case 2:
// Bottom
enemy.x = Math.random() * 2048;
enemy.y = 2782;
break;
case 3:
// Left
enemy.x = -50;
enemy.y = Math.random() * 2732;
break;
}
// Scale enemy with wave - health equals wave number for bullet count requirement
enemy.health = wave; // Wave 1 = 1 bullet, Wave 2 = 2 bullets, etc.
enemy.maxHealth = enemy.health;
enemy.speed = 1 + wave * 0.1;
enemy.points = 10 + wave * 2;
// Spawn giant enemy after every 10 small enemies
if (smallEnemiesSpawned >= 10) {
enemy.makeGiant();
smallEnemiesSpawned = 0; // Reset counter after spawning giant
} else {
smallEnemiesSpawned++; // Increment counter for small enemy
}
enemies.push(enemy);
game.addChild(enemy);
// Add spawn animation for all enemies
var enemyGraphics = enemy.children[0];
enemyGraphics.scaleX = 0.1;
enemyGraphics.scaleY = 0.1;
tween(enemyGraphics, {
scaleX: enemy.isGiant ? 2.5 : 1,
scaleY: enemy.isGiant ? 2.5 : 1
}, {
duration: 300,
easing: tween.elasticOut
});
}
function spawnBoss() {
var boss = new Boss();
// Spawn boss from center top
boss.x = 1024;
boss.y = -100;
boss.makeBoss();
enemies.push(boss);
game.addChild(boss);
bossSpawned = true;
bossActive = true;
// Add dramatic spawn animation for boss
var bossGraphics = boss.children[0];
bossGraphics.scaleX = 0.1;
bossGraphics.scaleY = 0.1;
tween(bossGraphics, {
scaleX: 5.0,
scaleY: 5.0
}, {
duration: 1500,
easing: tween.elasticOut
});
}
function updateUI() {
pointsText.setText('Points: ' + gamePoints);
waveText.setText('Wave: ' + wave);
healthText.setText('Health: ' + player.health);
// Update life bar width based on player health
var healthPercentage = player.health / player.maxHealth;
var newWidth = 800 * healthPercentage;
lifeBar.width = Math.max(0, newWidth);
// Change life bar color based on health percentage
if (healthPercentage > 0.6) {
lifeBar.tint = 0x4caf50; // Green
} else if (healthPercentage > 0.3) {
lifeBar.tint = 0xffc107; // Yellow
} else {
lifeBar.tint = 0xf44336; // Red
}
}
function openShop() {
shopOpen = true;
shopContainer.visible = true;
// Update weapon button states
for (var i = 0; i < weapons.length; i++) {
var weapon = weapons[i];
var canAfford = gamePoints >= weapon.cost;
var owned = player.weapon.name === weapon.name;
var colorHex = owned ? 0x00ff00 : canAfford ? 0xffffff : 0x666666;
var text = weapon.name + ' - ' + weapon.cost + ' pts';
if (owned) text = weapon.name + ' - EQUIPPED';
weaponTexts[i].setText(text);
weaponTexts[i].tint = colorHex;
}
}
function closeShop() {
shopOpen = false;
shopContainer.visible = false;
}
function buyWeapon(weaponIndex) {
var weapon = weapons[weaponIndex];
if (weapon && gamePoints >= weapon.cost && player.weapon.name !== weapon.name) {
gamePoints -= weapon.cost;
player.weapon = {
damage: weapon.damage,
fireRate: weapon.fireRate,
name: weapon.name
};
storage.points = gamePoints;
updateUI();
openShop(); // Refresh shop display
}
}
// Event handlers
shopButton.down = function (x, y, obj) {
openShop();
};
closeShopButton.down = function (x, y, obj) {
closeShop();
};
for (var i = 0; i < weaponButtons.length; i++) {
weaponButtons[i].down = function (x, y, obj) {
buyWeapon(obj.weaponIndex);
};
}
powerButton.down = function (x, y, obj) {
if (superBulletCooldown <= 0 && !superBulletActive) {
superBulletActive = true;
superBulletCooldown = superBulletCooldownTime;
// Visual feedback
tween(powerButton, {
scaleX: 1.2,
scaleY: 1.2,
tint: 0x00ff00
}, {
duration: 200,
easing: tween.bounceOut
});
LK.setTimeout(function () {
superBulletActive = false;
tween(powerButton, {
scaleX: 1.0,
scaleY: 1.0,
tint: 0xffffff
}, {
duration: 200
});
}, superBulletDuration);
}
};
restartButton.down = function (x, y, obj) {
// Reset all game state to starting values
gamePoints = 0;
wave = 1;
enemiesKilled = 0;
enemiesInWave = 5;
waveComplete = false;
smallEnemiesSpawned = 0;
bossSpawned = false;
bossActive = false;
superBulletActive = false;
superBulletCooldown = 0;
atomCooldown = 0;
isMouseDown = false;
// Reset player health and position
player.health = 100;
player.maxHealth = 100;
player.weapon = {
damage: 1,
fireRate: 10,
name: 'Basic Gun'
};
player.x = 1024;
player.y = 1366;
// Clear all enemies and bullets
for (var i = enemies.length - 1; i >= 0; i--) {
enemies[i].destroy();
}
enemies = [];
for (var i = bullets.length - 1; i >= 0; i--) {
bullets[i].destroy();
}
bullets = [];
// Reset storage
storage.points = 0;
storage.wave = 1;
// Visual feedback for restart button
tween(restartButton, {
scaleX: 1.2,
scaleY: 1.2,
tint: 0x00ff00
}, {
duration: 200,
easing: tween.bounceOut
});
LK.setTimeout(function () {
tween(restartButton, {
scaleX: 1.0,
scaleY: 1.0,
tint: 0xffffff
}, {
duration: 200
});
}, 500);
// Spawn initial enemies for wave 1
for (var i = 0; i < enemiesInWave; i++) {
LK.setTimeout(function () {
spawnEnemy();
}, i * 2000);
}
};
atomButton.down = function (x, y, obj) {
if (atomCooldown <= 0) {
// Make all enemies disappear by going up quickly with moving effect
for (var i = enemies.length - 1; i >= 0; i--) {
var enemy = enemies[i];
gamePoints += enemy.points;
enemiesKilled++;
LK.getSound('enemyDestroy').play();
// Create moving effect - enemies fly up quickly and fade out
tween(enemy, {
y: enemy.y - 1000,
alpha: 0,
scaleX: 0.1,
scaleY: 0.1
}, {
duration: 500,
easing: tween.easeIn,
onFinish: function onFinish() {
enemy.destroy();
}
});
enemies.splice(i, 1);
}
atomCooldown = atomCooldownTime;
// Visual feedback
tween(atomButton, {
scaleX: 1.2,
scaleY: 1.2,
tint: 0x00ff00
}, {
duration: 200,
easing: tween.bounceOut
});
LK.setTimeout(function () {
tween(atomButton, {
scaleX: 1.0,
scaleY: 1.0,
tint: 0xffffff
}, {
duration: 200
});
}, 500);
}
};
game.down = function (x, y, obj) {
if (!shopOpen && player) {
// Right click moves player, left click shoots
if (obj.event && obj.event.button === 2) {
// Right click - move player
player.x = x;
player.y = y;
} else {
// Left click or touch - shoot
isMouseDown = true;
lastMouseX = x;
lastMouseY = y;
player.shoot(x, y);
}
}
};
// Spawn initial enemies
for (var i = 0; i < enemiesInWave; i++) {
LK.setTimeout(function () {
spawnEnemy();
}, i * 2000);
}
game.move = function (x, y, obj) {
if (isMouseDown && !shopOpen) {
lastMouseX = x;
lastMouseY = y;
}
};
game.up = function (x, y, obj) {
isMouseDown = false;
};
// Prevent context menu on right click
game.on('rightdown', function (x, y, obj) {
if (obj.event) {
obj.event.preventDefault();
}
});
// Main game loop
game.update = function () {
if (shopOpen) return;
// Continuous firing when mouse is held down
if (isMouseDown && player) {
player.shoot(lastMouseX, lastMouseY);
}
// Update bullets
for (var i = bullets.length - 1; i >= 0; i--) {
var bullet = bullets[i];
// Check if bullet is off screen
if (bullet.x < -50 || bullet.x > 2098 || bullet.y < -50 || bullet.y > 2782) {
bullet.destroy();
bullets.splice(i, 1);
continue;
}
// Check bullet-enemy collisions
var hitEnemy = false;
for (var j = enemies.length - 1; j >= 0; j--) {
var enemy = enemies[j];
if (bullet.intersects(enemy)) {
if (bullet.isSuper) {
// Super bullet area damage - hit all enemies in area
var areaRadius = 200;
for (var k = enemies.length - 1; k >= 0; k--) {
var areaEnemy = enemies[k];
var dx = areaEnemy.x - bullet.x;
var dy = areaEnemy.y - bullet.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= areaRadius) {
var destroyed = areaEnemy.takeDamage(bullet.damage);
if (destroyed) {
gamePoints += areaEnemy.points;
enemiesKilled++;
if (areaEnemy.isBoss) {
bossActive = false;
}
LK.getSound('enemyDestroy').play();
enemies.splice(k, 1);
if (k < j) j--; // Adjust index if we removed an enemy before current
}
}
}
} else {
// Regular bullet single target damage
var destroyed = enemy.takeDamage(bullet.damage);
if (destroyed) {
gamePoints += enemy.points;
enemiesKilled++;
if (enemy.isBoss) {
bossActive = false;
}
LK.getSound('enemyDestroy').play();
enemies.splice(j, 1);
}
}
bullet.destroy();
bullets.splice(i, 1);
hitEnemy = true;
break;
}
}
if (hitEnemy) continue;
}
// Check enemy-player collisions
for (var i = enemies.length - 1; i >= 0; i--) {
var enemy = enemies[i];
if (enemy.intersects(player)) {
// Boss enemies deal 3x damage, giant enemies deal 2x damage compared to regular enemies
var damage = enemy.isBoss ? 30 : enemy.isGiant ? 20 : 10;
player.health -= damage;
if (enemy.isBoss) {
bossActive = false;
}
enemy.destroy();
enemies.splice(i, 1);
if (player.health <= 0) {
// Game over - reset to chapter 1
gamePoints = 0; // Reset score to 0 when player dies
storage.points = gamePoints;
storage.wave = 1; // Reset to chapter 1 when player dies
LK.showGameOver();
return;
}
}
}
// Check wave completion
if (enemiesKilled >= enemiesInWave && enemies.length === 0 && !waveComplete && !bossSpawned) {
// Only spawn boss at the end of wave 1 (first section)
if (wave === 1) {
bossSpawned = true;
waveComplete = true; // Mark wave as complete so no more regular enemies spawn
LK.setTimeout(function () {
spawnBoss();
}, 2000);
} else {
// For all other waves, advance directly to next wave without boss
waveComplete = true;
wave++;
enemiesKilled = 0;
smallEnemiesSpawned = 0;
bossSpawned = false;
bossActive = false;
enemiesInWave = Math.min(5 + wave, 15);
// Spawn next wave after delay
LK.setTimeout(function () {
waveComplete = false;
for (var i = 0; i < enemiesInWave; i++) {
LK.setTimeout(function () {
spawnEnemy();
}, i * 1500);
}
}, 3000);
}
} else if (bossSpawned && !bossActive && enemies.length === 0 && waveComplete) {
// Boss defeated, advance to next wave (this will be wave 2 after boss from wave 1)
wave++;
enemiesKilled = 0;
smallEnemiesSpawned = 0; // Reset small enemies counter for new wave
bossSpawned = false;
bossActive = false;
enemiesInWave = Math.min(5 + wave, 15);
// Spawn next wave after delay
LK.setTimeout(function () {
waveComplete = false;
for (var i = 0; i < enemiesInWave; i++) {
LK.setTimeout(function () {
spawnEnemy();
}, i * 1500);
}
}, 3000);
}
// Periodic enemy spawning during wave - but not when boss is active or wave is complete
if (!waveComplete && !bossActive && enemies.length < Math.min(enemiesInWave, 8)) {
if (Math.random() < 0.01) {
spawnEnemy();
}
}
// Update super bullet system
if (superBulletCooldown > 0) {
superBulletCooldown -= 16.67; // Approximately 1 frame at 60fps
if (superBulletCooldown < 0) superBulletCooldown = 0;
}
// Update atom button system
if (atomCooldown > 0) {
atomCooldown -= 16.67; // Approximately 1 frame at 60fps
if (atomCooldown < 0) atomCooldown = 0;
}
// Update power button UI
if (superBulletActive) {
var remaining = Math.ceil((superBulletDuration - LK.ticks * 16.67) / 1000);
powerButtonText.setText('ACTIVE');
powerCooldownText.setText('');
if (powerButton.tint !== 0x00ff00) {
tween(powerButton, {
tint: 0x00ff00,
scaleX: 3.75,
scaleY: 3.75
}, {
duration: 300,
easing: tween.easeOut
});
}
} else if (superBulletCooldown > 0) {
var cooldownSeconds = Math.ceil(superBulletCooldown / 1000);
powerButtonText.setText('SUPER');
powerCooldownText.setText(cooldownSeconds + 's');
if (powerButton.tint !== 0xff0000) {
tween(powerButton, {
tint: 0xff0000,
scaleX: 2.5,
scaleY: 2.5
}, {
duration: 300,
easing: tween.easeOut
});
}
} else {
powerButtonText.setText('SUPER');
powerCooldownText.setText('READY');
if (powerButton.tint !== 0x00ff00) {
tween(powerButton, {
tint: 0x00ff00,
scaleX: 3.75,
scaleY: 3.75
}, {
duration: 300,
easing: tween.easeOut
});
}
}
// Update atom button UI
if (atomCooldown > 0) {
var atomCooldownSeconds = Math.ceil(atomCooldown / 1000);
atomButtonText.setText('ATOM');
atomCooldownText.setText(atomCooldownSeconds + 's');
if (atomButton.tint !== 0xff0000) {
tween(atomButton, {
tint: 0xff0000,
scaleX: 2.5,
scaleY: 2.5
}, {
duration: 300,
easing: tween.easeOut
});
}
} else {
atomButtonText.setText('ATOM');
atomCooldownText.setText('READY');
if (atomButton.tint !== 0x00ff00) {
tween(atomButton, {
tint: 0x00ff00,
scaleX: 3.75,
scaleY: 3.75
}, {
duration: 300,
easing: tween.easeOut
});
}
}
updateUI();
// Save progress
if (LK.ticks % 300 === 0) {
storage.points = gamePoints;
storage.wave = wave;
}
};