User prompt
Remove all +2 hp per second regen upgrades
Code edit (1 edits merged)
Please save this source code
User prompt
Make arrows tighter between each other
User prompt
Make desert boss come on wave 15 on arctic
User prompt
Make forest boss come on wave 10 on all maps
User prompt
add level 2 to unlock evolution for later evolution II
User prompt
rename bomb upgrade and bow upgrade to bomb and bow
User prompt
make bomb evolution I do +50% explosion radius
User prompt
make the more damage upgrade do +10% all damage and same with firing speed but +10% firerate
User prompt
the walk speed perma upgrade does not work
User prompt
make bomb evolution +75% explosion radius. star 1 +50% bomb damage. star 2 +1 bomb. star 3 +20% bomb fireing speed. star 4 +20% bomb speed. star 5 + 10% explosion radius ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
nerf coin gain by 75 %
User prompt
Make every permanent upgrade 1.3x more expensive per level
User prompt
Make bomb 100% faster ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Make bomb deal 200% more damage
User prompt
Make coins formula enemys defeated + 2000 ÷ time
User prompt
the bomb upgrade finally works but now i get evolutions without the permanent upgrade
User prompt
the bomb upgrade is still not there and now I get evolution even when i dont have the unlock evolution upgrade
User prompt
it is not as an choise. try making it the same as the other upgrades
User prompt
the upgrade is still not shoving up
User prompt
the bomb upgrade is just not there even after max level
User prompt
the bomb does not appear as a choice when leveling up
User prompt
bomb upgrade should sometimes show up when leveling up.
User prompt
make the bomb upgrade show up when leveling up
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var ArcticBoss = Container.expand(function () {
var self = Container.call(this);
var bossGraphics = self.attachAsset('arcticBoss', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 1.2; // Faster than other bosses
self.damage = 40; // Highest damage
self.targetX = 0;
self.targetY = 0;
self.lastPlayerCollision = false;
self.maxHealth = 60000; // 60000 HP as requested
self.health = self.maxHealth;
self.blizzardTimer = 0;
self.blizzardCooldown = 0;
self.isBlizzard = false;
self.teleportCooldown = 0;
// Create health bar background
var healthBarBg = self.attachAsset('healthBarBg', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.5,
scaleY: 0.8
});
healthBarBg.y = -130;
// Create health bar
var healthBar = self.attachAsset('healthBar', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.5,
scaleY: 0.8
});
healthBar.y = -130;
self.healthBar = healthBar;
self.healthBarBg = healthBarBg;
self.update = function () {
// Don't move when game is paused for upgrades
if (gamePaused) return;
// Blizzard attack behavior
self.blizzardCooldown--;
if (self.blizzardCooldown <= 0 && !self.isBlizzard) {
// Start blizzard attack
self.isBlizzard = true;
self.blizzardTimer = 240; // 4 seconds of blizzard
self.blizzardCooldown = 480; // 8 second cooldown
// Flash light blue to warn player
LK.effects.flashObject(self, 0x87CEEB, 800);
}
// Teleport ability
self.teleportCooldown--;
if (self.teleportCooldown <= 0) {
// Teleport to random position
self.x = 200 + Math.random() * 1648;
self.y = 200 + Math.random() * 1000;
self.teleportCooldown = 300; // 5 second cooldown
// Flash effect on teleport
LK.effects.flashObject(self, 0xFFFFFF, 300);
}
if (self.isBlizzard) {
self.blizzardTimer--;
if (self.blizzardTimer <= 0) {
self.isBlizzard = false;
}
// Move very fast during blizzard in straight lines
var angle = Math.floor(LK.ticks / 30) * (Math.PI / 4); // Change direction every 0.5 seconds
var blizzardSpeed = self.speed * 3;
self.x += Math.cos(angle) * blizzardSpeed;
self.y += Math.sin(angle) * blizzardSpeed;
// Keep boss on screen
self.x = Math.max(110, Math.min(1938, self.x));
self.y = Math.max(110, Math.min(2622, self.y));
} else {
// Normal movement toward player
var dx = self.targetX - self.x;
var dy = self.targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
}
}
var currentCollision = self.intersects(player);
if (!self.lastPlayerCollision && currentCollision) {
var bossAttackDamage = self.isBlizzard ? self.damage * 2 : self.damage;
player.takeDamage(bossAttackDamage);
}
self.lastPlayerCollision = currentCollision;
};
self.takeDamage = function (damage) {
self.health -= damage;
self.updateHealthBar();
if (self.health <= 0) {
self.health = 0;
// Drop multiple Yellow XP orbs (most of all bosses)
for (var i = 0; i < 12; i++) {
var xpOrb = new YellowXPOrb();
xpOrb.x = self.x + (Math.random() - 0.5) * 200;
xpOrb.y = self.y + (Math.random() - 0.5) * 200;
xpOrbs.push(xpOrb);
game.addChild(xpOrb);
// Add tween animation for orb spawn
tween(xpOrb, {
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(xpOrb, {
scaleX: 1,
scaleY: 1
}, {
duration: 150,
easing: tween.easeIn
});
}
});
}
return true; // Boss is dead
}
return false; // Boss is still alive
};
self.updateHealthBar = function () {
var healthPercentage = self.health / self.maxHealth;
self.healthBar.scaleX = 2.5 * healthPercentage;
if (healthPercentage > 0.6) {
self.healthBar.tint = 0x4CAF50;
} else if (healthPercentage > 0.3) {
self.healthBar.tint = 0xFFEB3B;
} else {
self.healthBar.tint = 0xFF5722;
}
};
return self;
});
var Arrow = Container.expand(function () {
var self = Container.call(this);
var arrowGraphics = self.attachAsset('arrow', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 12;
self.damage = baseArrowDamage;
self.directionX = 0;
self.directionY = 0;
self.lastEnemyCollisions = [];
self.isSeeking = false;
self.setDirection = function (dx, dy) {
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
self.directionX = dx / distance;
self.directionY = dy / distance;
self.rotation = Math.atan2(dy, dx);
}
};
self.update = function () {
var actualSpeed = self.speed * arrowSpeedMultiplier;
// Seeking behavior if bow evolution is active
if (self.isSeeking && enemies.length > 0) {
var nearestEnemy = null;
var nearestDistance = Infinity;
for (var e = 0; e < enemies.length; e++) {
var enemy = enemies[e];
var dx = enemy.x - self.x;
var dy = enemy.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < nearestDistance) {
nearestDistance = distance;
nearestEnemy = enemy;
}
}
if (nearestEnemy) {
var dx = nearestEnemy.x - self.x;
var dy = nearestEnemy.y - self.y;
self.setDirection(dx, dy);
}
}
self.x += self.directionX * actualSpeed;
self.y += self.directionY * actualSpeed;
// Check collisions with enemies
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
var currentCollision = self.intersects(enemy);
var lastCollision = self.lastEnemyCollisions[i] || false;
if (!lastCollision && currentCollision) {
// Hit enemy
LK.getSound('enemyHit').play();
LK.effects.flashObject(enemy, 0xFFFFFF, 200);
// Damage enemy
var isDead = enemy.takeDamage(self.damage);
if (isDead) {
// Remove enemy
enemies.splice(i, 1);
enemy.destroy();
}
// Remove arrow
if (arrows.indexOf(self) !== -1) {
arrows.splice(arrows.indexOf(self), 1);
self.destroy();
}
return;
}
self.lastEnemyCollisions[i] = currentCollision;
}
};
return self;
});
var Bomb = Container.expand(function () {
var self = Container.call(this);
var bombGraphics = self.attachAsset('bomb', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 6; // 100% faster movement (2x speed)
self.damage = baseBombDamage;
self.explosionRadius = baseBombExplosionRadius;
self.directionX = 0;
self.directionY = 0;
self.hasExploded = false;
self.setDirection = function (dx, dy) {
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
self.directionX = dx / distance;
self.directionY = dy / distance;
}
};
self.explode = function () {
if (self.hasExploded) return;
self.hasExploded = true;
// Create explosion visual effect
var explosion = LK.getAsset('explosion', {
anchorX: 0.5,
anchorY: 0.5
});
explosion.x = self.x;
explosion.y = self.y;
explosion.scaleX = self.explosionRadius / 50; // Scale based on explosion radius
explosion.scaleY = self.explosionRadius / 50;
game.addChild(explosion);
// Animate explosion
tween(explosion, {
scaleX: explosion.scaleX * 2,
scaleY: explosion.scaleY * 2,
alpha: 0
}, {
duration: 500,
easing: tween.easeOut,
onFinish: function onFinish() {
explosion.destroy();
}
});
// Play explosion sound
LK.getSound('bombExplode').play();
// Damage all enemies within explosion radius
for (var i = enemies.length - 1; i >= 0; i--) {
var enemy = enemies[i];
var dx = enemy.x - self.x;
var dy = enemy.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= self.explosionRadius) {
LK.effects.flashObject(enemy, 0xFF4500, 300);
var isDead = enemy.takeDamage(self.damage);
if (isDead) {
enemies.splice(i, 1);
enemy.destroy();
}
}
}
// Remove bomb
if (bombs.indexOf(self) !== -1) {
bombs.splice(bombs.indexOf(self), 1);
self.destroy();
}
};
self.update = function () {
var actualSpeed = self.speed * bombSpeedMultiplier;
self.x += self.directionX * actualSpeed;
self.y += self.directionY * actualSpeed;
// Check collisions with enemies
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
if (self.intersects(enemy)) {
self.explode();
return;
}
}
};
return self;
});
var DesertBoss = Container.expand(function () {
var self = Container.call(this);
var bossGraphics = self.attachAsset('desertBoss', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 0.8; // Slightly faster than forest boss
self.damage = 30; // Higher damage than forest boss
self.targetX = 0;
self.targetY = 0;
self.lastPlayerCollision = false;
self.maxHealth = 15000; // 15000 HP as requested
self.health = self.maxHealth;
self.sandStormTimer = 0;
self.sandStormCooldown = 0;
self.isSandStorm = false;
// Create health bar background
var healthBarBg = self.attachAsset('healthBarBg', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.0,
scaleY: 0.6
});
healthBarBg.y = -120;
// Create health bar
var healthBar = self.attachAsset('healthBar', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.0,
scaleY: 0.6
});
healthBar.y = -120;
self.healthBar = healthBar;
self.healthBarBg = healthBarBg;
self.update = function () {
// Don't move when game is paused for upgrades
if (gamePaused) return;
// Sandstorm attack behavior
self.sandStormCooldown--;
if (self.sandStormCooldown <= 0 && !self.isSandStorm) {
// Start sandstorm attack
self.isSandStorm = true;
self.sandStormTimer = 180; // 3 seconds of sandstorm
self.sandStormCooldown = 400; // 6.7 second cooldown
// Flash orange to warn player
LK.effects.flashObject(self, 0xFF8C00, 600);
}
if (self.isSandStorm) {
self.sandStormTimer--;
if (self.sandStormTimer <= 0) {
self.isSandStorm = false;
}
// Move in random directions during sandstorm
var randomAngle = Math.random() * Math.PI * 2;
var stormSpeed = self.speed * 2;
self.x += Math.cos(randomAngle) * stormSpeed;
self.y += Math.sin(randomAngle) * stormSpeed;
// Keep boss on screen
self.x = Math.max(100, Math.min(1948, self.x));
self.y = Math.max(100, Math.min(2632, self.y));
} else {
// Normal movement toward player
var dx = self.targetX - self.x;
var dy = self.targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
}
}
var currentCollision = self.intersects(player);
if (!self.lastPlayerCollision && currentCollision) {
var bossAttackDamage = self.isSandStorm ? self.damage * 1.5 : self.damage;
player.takeDamage(bossAttackDamage);
}
self.lastPlayerCollision = currentCollision;
};
self.takeDamage = function (damage) {
self.health -= damage;
self.updateHealthBar();
if (self.health <= 0) {
self.health = 0;
// Drop multiple Yellow XP orbs (more than forest boss)
for (var i = 0; i < 8; i++) {
var xpOrb = new YellowXPOrb();
xpOrb.x = self.x + (Math.random() - 0.5) * 150;
xpOrb.y = self.y + (Math.random() - 0.5) * 150;
xpOrbs.push(xpOrb);
game.addChild(xpOrb);
// Add tween animation for orb spawn
tween(xpOrb, {
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(xpOrb, {
scaleX: 1,
scaleY: 1
}, {
duration: 150,
easing: tween.easeIn
});
}
});
}
return true; // Boss is dead
}
return false; // Boss is still alive
};
self.updateHealthBar = function () {
var healthPercentage = self.health / self.maxHealth;
self.healthBar.scaleX = 2.0 * healthPercentage;
if (healthPercentage > 0.6) {
self.healthBar.tint = 0x4CAF50;
} else if (healthPercentage > 0.3) {
self.healthBar.tint = 0xFFEB3B;
} else {
self.healthBar.tint = 0xFF5722;
}
};
return self;
});
var Enemy = Container.expand(function () {
var self = Container.call(this);
var enemyGraphics = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 1.5;
self.damage = 10;
self.targetX = 0;
self.targetY = 0;
self.lastPlayerCollision = false;
self.maxHealth = 30;
self.health = self.maxHealth;
// Create health bar background
var healthBarBg = self.attachAsset('healthBarBg', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.15,
scaleY: 0.3
});
healthBarBg.y = -40;
// Create health bar
var healthBar = self.attachAsset('healthBar', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.15,
scaleY: 0.3
});
healthBar.y = -40;
self.healthBar = healthBar;
self.healthBarBg = healthBarBg;
self.update = function () {
// Don't move when game is paused for upgrades
if (gamePaused) return;
var dx = self.targetX - self.x;
var dy = self.targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
}
var currentCollision = self.intersects(player);
if (!self.lastPlayerCollision && currentCollision) {
player.takeDamage(self.damage);
}
self.lastPlayerCollision = currentCollision;
};
self.takeDamage = function (damage) {
self.health -= damage;
self.updateHealthBar();
if (self.health <= 0) {
self.health = 0;
// Drop XP orb
var xpOrb = new XPOrb();
xpOrb.x = self.x;
xpOrb.y = self.y;
xpOrbs.push(xpOrb);
game.addChild(xpOrb);
// Add tween animation for orb spawn
tween(xpOrb, {
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(xpOrb, {
scaleX: 1,
scaleY: 1
}, {
duration: 150,
easing: tween.easeIn
});
}
});
return true; // Enemy is dead
}
return false; // Enemy is still alive
};
self.updateHealthBar = function () {
var healthPercentage = self.health / self.maxHealth;
self.healthBar.scaleX = 0.15 * healthPercentage;
if (healthPercentage > 0.6) {
self.healthBar.tint = 0x4CAF50;
} else if (healthPercentage > 0.3) {
self.healthBar.tint = 0xFFEB3B;
} else {
self.healthBar.tint = 0xFF5722;
}
};
return self;
});
var FastEnemy = Container.expand(function () {
var self = Container.call(this);
var enemyGraphics = self.attachAsset('fastEnemy', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 3.0;
self.damage = 10;
self.targetX = 0;
self.targetY = 0;
self.lastPlayerCollision = false;
self.maxHealth = 24;
self.health = self.maxHealth;
// Create health bar background
var healthBarBg = self.attachAsset('healthBarBg', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.12,
scaleY: 0.25
});
healthBarBg.y = -35;
// Create health bar
var healthBar = self.attachAsset('healthBar', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.12,
scaleY: 0.25
});
healthBar.y = -35;
self.healthBar = healthBar;
self.healthBarBg = healthBarBg;
self.update = function () {
// Don't move when game is paused for upgrades
if (gamePaused) return;
var dx = self.targetX - self.x;
var dy = self.targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
}
var currentCollision = self.intersects(player);
if (!self.lastPlayerCollision && currentCollision) {
player.takeDamage(self.damage);
}
self.lastPlayerCollision = currentCollision;
};
self.takeDamage = function (damage) {
self.health -= damage;
self.updateHealthBar();
if (self.health <= 0) {
self.health = 0;
// Drop XP orb
var xpOrb = new XPOrb();
xpOrb.x = self.x;
xpOrb.y = self.y;
xpOrbs.push(xpOrb);
game.addChild(xpOrb);
// Add tween animation for orb spawn
tween(xpOrb, {
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(xpOrb, {
scaleX: 1,
scaleY: 1
}, {
duration: 150,
easing: tween.easeIn
});
}
});
return true; // Enemy is dead
}
return false; // Enemy is still alive
};
self.updateHealthBar = function () {
var healthPercentage = self.health / self.maxHealth;
self.healthBar.scaleX = 0.12 * healthPercentage;
if (healthPercentage > 0.6) {
self.healthBar.tint = 0x4CAF50;
} else if (healthPercentage > 0.3) {
self.healthBar.tint = 0xFFEB3B;
} else {
self.healthBar.tint = 0xFF5722;
}
};
return self;
});
var ForestBoss = Container.expand(function () {
var self = Container.call(this);
var bossGraphics = self.attachAsset('forestBoss', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 0.5; // Slow movement
self.damage = 25; // High damage
self.targetX = 0;
self.targetY = 0;
self.lastPlayerCollision = false;
self.maxHealth = 2500; // Very high health (5x)
self.health = self.maxHealth;
self.chargeTimer = 0;
self.chargeSpeed = 4; // Fast charge attack
self.isCharging = false;
self.chargeCooldown = 0;
// Create health bar background
var healthBarBg = self.attachAsset('healthBarBg', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 0.5
});
healthBarBg.y = -120;
// Create health bar
var healthBar = self.attachAsset('healthBar', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 0.5
});
healthBar.y = -120;
self.healthBar = healthBar;
self.healthBarBg = healthBarBg;
self.update = function () {
// Don't move when game is paused for upgrades
if (gamePaused) return;
// Charge attack behavior
self.chargeCooldown--;
if (self.chargeCooldown <= 0 && !self.isCharging) {
// Start charge attack
self.isCharging = true;
self.chargeTimer = 120; // 2 seconds of charging
self.chargeCooldown = 300; // 5 second cooldown
// Flash red to warn player
LK.effects.flashObject(self, 0xFF0000, 500);
}
if (self.isCharging) {
self.chargeTimer--;
if (self.chargeTimer <= 0) {
self.isCharging = false;
}
// Move faster during charge
var dx = self.targetX - self.x;
var dy = self.targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
self.x += dx / distance * self.chargeSpeed;
self.y += dy / distance * self.chargeSpeed;
}
} else {
// Normal slow movement
var dx = self.targetX - self.x;
var dy = self.targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
}
}
var currentCollision = self.intersects(player);
if (!self.lastPlayerCollision && currentCollision) {
var bossAttackDamage = self.isCharging ? self.damage * 2 : self.damage;
player.takeDamage(bossAttackDamage);
}
self.lastPlayerCollision = currentCollision;
};
self.takeDamage = function (damage) {
self.health -= damage;
self.updateHealthBar();
if (self.health <= 0) {
self.health = 0;
// Drop multiple Yellow XP orbs
for (var i = 0; i < 5; i++) {
var xpOrb = new YellowXPOrb();
xpOrb.x = self.x + (Math.random() - 0.5) * 100;
xpOrb.y = self.y + (Math.random() - 0.5) * 100;
xpOrbs.push(xpOrb);
game.addChild(xpOrb);
// Add tween animation for orb spawn
tween(xpOrb, {
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(xpOrb, {
scaleX: 1,
scaleY: 1
}, {
duration: 150,
easing: tween.easeIn
});
}
});
}
return true; // Boss is dead
}
return false; // Boss is still alive
};
self.updateHealthBar = function () {
var healthPercentage = self.health / self.maxHealth;
self.healthBar.scaleX = 1.5 * healthPercentage;
if (healthPercentage > 0.6) {
self.healthBar.tint = 0x4CAF50;
} else if (healthPercentage > 0.3) {
self.healthBar.tint = 0xFFEB3B;
} else {
self.healthBar.tint = 0xFF5722;
}
};
return self;
});
var HeavyEnemy = Container.expand(function () {
var self = Container.call(this);
var enemyGraphics = self.attachAsset('heavyEnemy', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 0.9; // Slower than regular enemies
self.damage = 15;
self.targetX = 0;
self.targetY = 0;
self.lastPlayerCollision = false;
self.maxHealth = 80; // More HP than regular enemies
self.health = self.maxHealth;
// Create health bar background
var healthBarBg = self.attachAsset('healthBarBg', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.2,
scaleY: 0.35
});
healthBarBg.y = -50;
// Create health bar
var healthBar = self.attachAsset('healthBar', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.2,
scaleY: 0.35
});
healthBar.y = -50;
self.healthBar = healthBar;
self.healthBarBg = healthBarBg;
self.update = function () {
// Don't move when game is paused for upgrades
if (gamePaused) return;
var dx = self.targetX - self.x;
var dy = self.targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
}
var currentCollision = self.intersects(player);
if (!self.lastPlayerCollision && currentCollision) {
player.takeDamage(self.damage);
}
self.lastPlayerCollision = currentCollision;
};
self.takeDamage = function (damage) {
self.health -= damage;
self.updateHealthBar();
if (self.health <= 0) {
self.health = 0;
// Drop Yellow XP orb worth 3x normal XP
var xpOrb = new YellowXPOrb();
xpOrb.x = self.x;
xpOrb.y = self.y;
xpOrbs.push(xpOrb);
game.addChild(xpOrb);
// Add tween animation for orb spawn
tween(xpOrb, {
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(xpOrb, {
scaleX: 1,
scaleY: 1
}, {
duration: 150,
easing: tween.easeIn
});
}
});
return true; // Enemy is dead
}
return false; // Enemy is still alive
};
self.updateHealthBar = function () {
var healthPercentage = self.health / self.maxHealth;
self.healthBar.scaleX = 0.2 * healthPercentage;
if (healthPercentage > 0.6) {
self.healthBar.tint = 0x4CAF50;
} else if (healthPercentage > 0.3) {
self.healthBar.tint = 0xFFEB3B;
} else {
self.healthBar.tint = 0xFF5722;
}
};
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;
// Create player health bar background
var playerHealthBarBg = self.attachAsset('playerHealthBarBg', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.6,
scaleY: 0.6
});
playerHealthBarBg.y = -50;
// Create player health bar
var playerHealthBar = self.attachAsset('playerHealthBar', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.6,
scaleY: 0.6
});
playerHealthBar.y = -50;
self.playerHealthBar = playerHealthBar;
self.playerHealthBarBg = playerHealthBarBg;
self.speed = 1;
self.invulnerable = false;
self.invulnerableTime = 0;
self.takeDamage = function (damage) {
if (self.invulnerable) {
return;
}
self.health -= damage;
self.updatePlayerHealthBar();
self.invulnerable = true;
self.invulnerableTime = 60; // 1 second of invulnerability
LK.getSound('playerDamage').play();
LK.effects.flashObject(self, 0xFF0000, 500);
if (self.health <= 0) {
self.health = 0;
gameOver = true;
}
};
self.heal = function (amount) {
self.health = Math.min(self.maxHealth, self.health + amount);
self.updatePlayerHealthBar();
};
self.updatePlayerHealthBar = function () {
var healthPercentage = self.health / self.maxHealth;
self.playerHealthBar.scaleX = 0.6 * healthPercentage;
if (healthPercentage > 0.6) {
self.playerHealthBar.tint = 0x4CAF50;
} else if (healthPercentage > 0.3) {
self.playerHealthBar.tint = 0xFFEB3B;
} else {
self.playerHealthBar.tint = 0xFF5722;
}
};
self.update = function () {
if (self.invulnerable) {
self.invulnerableTime--;
if (self.invulnerableTime <= 0) {
self.invulnerable = false;
}
}
};
return self;
});
var XPOrb = Container.expand(function () {
var self = Container.call(this);
var orbGraphics = self.attachAsset('xpOrb', {
anchorX: 0.5,
anchorY: 0.5
});
self.xpValue = 10;
self.attractDistance = baseMagnetRange;
self.attractSpeed = baseMagnetSpeed;
self.lastPlayerCollision = false;
self.update = function () {
// Don't attract when game is paused for upgrades
if (gamePaused) return;
// Check distance to player for attraction
var dx = player.x - self.x;
var dy = player.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// Attract to player if within range
if (distance < self.attractDistance && distance > 0) {
var moveSpeed = Math.min(self.attractSpeed, distance);
self.x += dx / distance * moveSpeed;
self.y += dy / distance * moveSpeed;
}
// Check collision with player
var currentCollision = self.intersects(player);
if (!self.lastPlayerCollision && currentCollision) {
// Collect XP
playerXP += self.xpValue;
checkLevelUp();
LK.getSound('xpCollect').play();
// Remove orb
if (xpOrbs.indexOf(self) !== -1) {
xpOrbs.splice(xpOrbs.indexOf(self), 1);
self.destroy();
}
}
self.lastPlayerCollision = currentCollision;
};
return self;
});
var YellowXPOrb = Container.expand(function () {
var self = Container.call(this);
var orbGraphics = self.attachAsset('yellowXpOrb', {
anchorX: 0.5,
anchorY: 0.5
});
self.xpValue = 30; // 3x the normal XP value (normal is 10)
self.attractDistance = baseMagnetRange;
self.attractSpeed = baseMagnetSpeed;
self.lastPlayerCollision = false;
self.update = function () {
// Don't attract when game is paused for upgrades
if (gamePaused) return;
// Check distance to player for attraction
var dx = player.x - self.x;
var dy = player.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// Yellow orbs need magnet evolution to be pulled at long range
// If player has magnet evolution (level 6+), use full attract distance
// Otherwise, use base magnet range (50) for yellow orbs
var effectiveAttractDistance = self.attractDistance;
if (upgradeLevels[5] < 6) {
// No magnet evolution - use base range for yellow orbs
effectiveAttractDistance = 50;
}
// Attract to player if within range
if (distance < effectiveAttractDistance && distance > 0) {
var moveSpeed = Math.min(self.attractSpeed, distance);
self.x += dx / distance * moveSpeed;
self.y += dy / distance * moveSpeed;
}
// Check collision with player
var currentCollision = self.intersects(player);
if (!self.lastPlayerCollision && currentCollision) {
// Collect XP
playerXP += self.xpValue;
checkLevelUp();
LK.getSound('xpCollect').play();
// Remove orb
if (xpOrbs.indexOf(self) !== -1) {
xpOrbs.splice(xpOrbs.indexOf(self), 1);
self.destroy();
}
}
self.lastPlayerCollision = currentCollision;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x1a1a1a
});
/****
* Game Code
****/
// Game state variables
var gameStarted = false;
var startScreen = null;
var playButton = null;
var upgradesScreen = null;
var upgradesScreenActive = false;
var upgradesScreenButtons = [];
var mapSelectionActive = false;
var mapSelectionButtons = [];
var selectedMap = 0; // Default map
// Create start screen
function createStartScreen() {
// Black background covering entire screen
startScreen = LK.getAsset('healthBarBg', {
anchorX: 0,
anchorY: 0,
scaleX: 12,
scaleY: 20
});
startScreen.x = 0;
startScreen.y = 0;
startScreen.tint = 0x000000;
game.addChild(startScreen);
// Play button
playButton = LK.getAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 4,
scaleY: 2
});
playButton.x = 1024;
playButton.y = 2200;
playButton.tint = 0x4CAF50;
game.addChild(playButton);
// Play button text
var playButtonText = new Text2('PLAY', {
size: 100,
fill: 0xFFFFFF
});
playButtonText.anchor.set(0.5, 0.5);
playButtonText.x = 1024;
playButtonText.y = 2200;
game.addChild(playButtonText);
// Store reference to text for cleanup
playButton.buttonText = playButtonText;
// Permanent upgrades button
var upgradesButton = LK.getAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 3,
scaleY: 1.5
});
upgradesButton.x = 1024;
upgradesButton.y = 2350;
upgradesButton.tint = 0xFF9800;
game.addChild(upgradesButton);
// Permanent upgrades button text
var upgradesButtonText = new Text2('UPGRADES', {
size: 60,
fill: 0xFFFFFF
});
upgradesButtonText.anchor.set(0.5, 0.5);
upgradesButtonText.x = 1024;
upgradesButtonText.y = 2350;
game.addChild(upgradesButtonText);
// Store references for cleanup
playButton.upgradesButton = upgradesButton;
playButton.upgradesButtonText = upgradesButtonText;
}
// Remove start screen and begin game
function startGame() {
if (startScreen) {
startScreen.destroy();
startScreen = null;
}
if (playButton) {
playButton.buttonText.destroy();
if (playButton.upgradesButton) {
playButton.upgradesButton.destroy();
playButton.upgradesButtonText.destroy();
}
playButton.destroy();
playButton = null;
}
// Apply permanent upgrades
if (permanentUpgrades.bow > 0) {
// Apply bow upgrades based on permanent level
for (var bowLevel = 0; bowLevel < permanentUpgrades.bow; bowLevel++) {
applyBowUpgrade();
upgradeLevels[6]++;
}
}
if (permanentUpgrades.magnet > 0) {
baseMagnetRange = 50 * Math.pow(1.5, permanentUpgrades.magnet); // +50% range per level
upgradeLevels[5] = permanentUpgrades.magnet; // Set magnet upgrade level
baseMagnetSpeed = 8 + permanentUpgrades.magnet * 2; // +2 speed per level
}
if (permanentUpgrades.speed > 0) {
player.speed = 1 + permanentUpgrades.speed * 0.3; // +30% speed per level
}
if (permanentUpgrades.shootingSpeed > 0) {
shootCooldown = Math.floor(shootCooldown * Math.pow(0.8, permanentUpgrades.shootingSpeed)); // 20% faster shooting per level
}
// Apply map effects
if (selectedMap === 1) {
// Desert map - visual only
game.setBackgroundColor(0x8B4513); // Brown desert color
} else if (selectedMap === 2) {
// Arctic map - visual only
game.setBackgroundColor(0x87CEEB); // Light blue arctic color
} else {
// Forest map - default
game.setBackgroundColor(0x1a1a1a); // Default dark color
}
player.updatePlayerHealthBar();
// Show UI elements and player
healthBarBg.visible = true;
healthBar.visible = true;
waveText.visible = true;
timeText.visible = true;
levelText.visible = true;
xpText.visible = true;
player.visible = true;
gameStarted = true;
}
// Permanent upgrades system
var permanentUpgrades = {
bow: storage.permanentBow || 0,
magnet: storage.permanentMagnet || 0,
speed: storage.permanentSpeed || 0,
shootingSpeed: storage.permanentShootingSpeed || 0,
unlockEvolution: storage.unlockEvolution || 0,
coins: storage.coins || 0
};
function showUpgradesScreen() {
upgradesScreenActive = true;
// Clear existing buttons
clearUpgradesScreen();
// Black background
upgradesScreen = LK.getAsset('healthBarBg', {
anchorX: 0,
anchorY: 0,
scaleX: 12,
scaleY: 20
});
upgradesScreen.x = 0;
upgradesScreen.y = 0;
upgradesScreen.tint = 0x000000;
game.addChild(upgradesScreen);
upgradesScreenButtons.push(upgradesScreen);
// Title
var titleText = new Text2('PERMANENT UPGRADES', {
size: 80,
fill: 0xFFD700
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 1024;
titleText.y = 400;
game.addChild(titleText);
upgradesScreenButtons.push(titleText);
// Coins display
var coinsText = new Text2('Coins: ' + permanentUpgrades.coins, {
size: 60,
fill: 0xFFFFFF
});
coinsText.anchor.set(0.5, 0.5);
coinsText.x = 1024;
coinsText.y = 500;
game.addChild(coinsText);
upgradesScreenButtons.push(coinsText);
// Upgrade buttons - show all upgrades with their levels
var upgradeData = [{
name: 'Bow',
current: permanentUpgrades.bow,
cost: Math.floor(100 * Math.pow(1.3, permanentUpgrades.bow)),
key: 'bow'
}, {
name: 'Magnet',
current: permanentUpgrades.magnet,
cost: Math.floor(150 * Math.pow(1.3, permanentUpgrades.magnet)),
key: 'magnet'
}, {
name: 'Walkspeed',
current: permanentUpgrades.speed,
cost: Math.floor(200 * Math.pow(1.3, permanentUpgrades.speed)),
key: 'speed'
}, {
name: 'Shooting Speed',
current: permanentUpgrades.shootingSpeed,
cost: Math.floor(250 * Math.pow(1.3, permanentUpgrades.shootingSpeed)),
key: 'shootingSpeed'
}, {
name: 'Unlock Evolution',
current: permanentUpgrades.unlockEvolution,
cost: Math.floor(250 * Math.pow(1.3, permanentUpgrades.unlockEvolution)),
key: 'unlockEvolution'
}];
for (var i = 0; i < upgradeData.length; i++) {
var upgrade = upgradeData[i];
var buttonY = 700 + i * 300;
var maxLevel = upgrade.key === 'unlockEvolution' ? 1 : 5;
var canAfford = permanentUpgrades.coins >= upgrade.cost && upgrade.current < maxLevel;
var isMaxLevel = upgrade.current >= maxLevel;
// Upgrade button
var upgradeButton = LK.getAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 12,
scaleY: 3
});
upgradeButton.x = 1024;
upgradeButton.y = buttonY;
if (isMaxLevel) {
upgradeButton.tint = 0x333333;
} else {
upgradeButton.tint = canAfford ? 0x4CAF50 : 0x666666;
}
upgradeButton.upgradeKey = upgrade.key;
upgradeButton.upgradeCost = upgrade.cost;
upgradeButton.isMaxLevel = isMaxLevel;
game.addChild(upgradeButton);
upgradesScreenButtons.push(upgradeButton);
// Upgrade text
var upgradeText = new Text2(upgrade.name + ' Lv.' + upgrade.current, {
size: 100,
fill: 0xFFFFFF
});
upgradeText.anchor.set(0.5, 0.5);
upgradeText.x = 1024;
upgradeText.y = buttonY - 40;
game.addChild(upgradeText);
upgradesScreenButtons.push(upgradeText);
// Cost text
var costText = new Text2(isMaxLevel ? 'MAX LEVEL' : 'Cost: ' + upgrade.cost, {
size: 70,
fill: isMaxLevel ? 0x999999 : canAfford ? 0xFFFFFF : 0x999999
});
costText.anchor.set(0.5, 0.5);
costText.x = 1024;
costText.y = buttonY + 40;
game.addChild(costText);
upgradesScreenButtons.push(costText);
// Add stars below the button
var starY = buttonY + 130;
var starsToShow = upgrade.key === 'unlockEvolution' ? 1 : 5;
for (var s = 0; s < starsToShow; s++) {
var star;
if (s < upgrade.current) {
star = LK.getAsset('star', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 1.5
});
} else {
star = LK.getAsset('starDark', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 1.5
});
}
star.x = 1024 - 100 + s * 70;
star.y = starY;
game.addChild(star);
upgradesScreenButtons.push(star);
}
}
// Back button
var backButton = LK.getAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 3,
scaleY: 1.5
});
backButton.x = 1024;
backButton.y = 2200;
backButton.tint = 0xFF5722;
backButton.isBackButton = true;
game.addChild(backButton);
upgradesScreenButtons.push(backButton);
var backButtonText = new Text2('BACK', {
size: 60,
fill: 0xFFFFFF
});
backButtonText.anchor.set(0.5, 0.5);
backButtonText.x = 1024;
backButtonText.y = 2200;
game.addChild(backButtonText);
upgradesScreenButtons.push(backButtonText);
}
function clearUpgradesScreen() {
for (var i = 0; i < upgradesScreenButtons.length; i++) {
upgradesScreenButtons[i].destroy();
}
upgradesScreenButtons = [];
}
function showMapSelection() {
mapSelectionActive = true;
// Clear existing buttons
clearMapSelection();
// Black background
var mapScreen = LK.getAsset('healthBarBg', {
anchorX: 0,
anchorY: 0,
scaleX: 12,
scaleY: 20
});
mapScreen.x = 0;
mapScreen.y = 0;
mapScreen.tint = 0x000000;
game.addChild(mapScreen);
mapSelectionButtons.push(mapScreen);
// Title
var titleText = new Text2('SELECT MAP', {
size: 80,
fill: 0x9C27B0
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 1024;
titleText.y = 400;
game.addChild(titleText);
mapSelectionButtons.push(titleText);
// Map options
var mapData = [{
name: 'Forest',
color: 0x4CAF50,
description: ''
}, {
name: 'Desert',
color: 0xFF9800,
description: ''
}, {
name: 'Arctic',
color: 0x2196F3,
description: ''
}];
for (var i = 0; i < mapData.length; i++) {
var map = mapData[i];
var buttonY = 800 + i * 400;
var isSelected = i === selectedMap;
// Map button
var mapButton = LK.getAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 10,
scaleY: 3
});
mapButton.x = 1024;
mapButton.y = buttonY;
mapButton.tint = isSelected ? 0xFFD700 : map.color;
mapButton.mapIndex = i;
game.addChild(mapButton);
mapSelectionButtons.push(mapButton);
// Map name
var mapText = new Text2(map.name, {
size: 100,
fill: 0xFFFFFF
});
mapText.anchor.set(0.5, 0.5);
mapText.x = 1024;
mapText.y = buttonY - 40;
game.addChild(mapText);
mapSelectionButtons.push(mapText);
// Map description
var descText = new Text2(map.description, {
size: 60,
fill: 0xCCCCCC
});
descText.anchor.set(0.5, 0.5);
descText.x = 1024;
descText.y = buttonY + 40;
game.addChild(descText);
mapSelectionButtons.push(descText);
// Selection indicator
if (isSelected) {
var indicator = new Text2('SELECTED', {
size: 50,
fill: 0xFFD700
});
indicator.anchor.set(0.5, 0.5);
indicator.x = 1024;
indicator.y = buttonY + 100;
game.addChild(indicator);
mapSelectionButtons.push(indicator);
}
}
// Start button
var startButton = LK.getAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 4,
scaleY: 2
});
startButton.x = 1024;
startButton.y = 2200;
startButton.tint = 0x4CAF50;
startButton.isStartButton = true;
game.addChild(startButton);
mapSelectionButtons.push(startButton);
var startButtonText = new Text2('START GAME', {
size: 80,
fill: 0xFFFFFF
});
startButtonText.anchor.set(0.5, 0.5);
startButtonText.x = 1024;
startButtonText.y = 2200;
game.addChild(startButtonText);
mapSelectionButtons.push(startButtonText);
// Back button
var backButton = LK.getAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 3,
scaleY: 1.5
});
backButton.x = 1024;
backButton.y = 2350;
backButton.tint = 0xFF5722;
backButton.isBackButton = true;
game.addChild(backButton);
mapSelectionButtons.push(backButton);
var backButtonText = new Text2('BACK', {
size: 60,
fill: 0xFFFFFF
});
backButtonText.anchor.set(0.5, 0.5);
backButtonText.x = 1024;
backButtonText.y = 2350;
game.addChild(backButtonText);
mapSelectionButtons.push(backButtonText);
}
function clearMapSelection() {
for (var i = 0; i < mapSelectionButtons.length; i++) {
mapSelectionButtons[i].destroy();
}
mapSelectionButtons = [];
}
function selectMap(mapIndex) {
selectedMap = mapIndex;
showMapSelection(); // Refresh screen to show new selection
}
function purchaseUpgrade(key, cost) {
if (permanentUpgrades.coins >= cost && permanentUpgrades[key] < 5) {
permanentUpgrades.coins -= cost;
permanentUpgrades[key]++; // Increase level by 1
// Save to storage
storage.coins = permanentUpgrades.coins;
if (key === 'bow') {
storage.permanentBow = permanentUpgrades[key];
} else if (key === 'magnet') {
storage.permanentMagnet = permanentUpgrades[key];
} else if (key === 'speed') {
storage.permanentSpeed = permanentUpgrades[key];
} else if (key === 'shootingSpeed') {
storage.permanentShootingSpeed = permanentUpgrades[key];
} else if (key === 'unlockEvolution') {
storage.unlockEvolution = permanentUpgrades[key];
}
// Refresh screen
showUpgradesScreen();
}
}
// Initialize start screen
createStartScreen();
var player = game.addChild(new Player());
player.x = 1024;
player.y = 1366;
player.visible = false;
player.updatePlayerHealthBar();
var enemies = [];
var arrows = [];
var bombs = [];
var xpOrbs = [];
var gameOver = false;
var lastShootTime = 0;
var lastBombTime = 0;
var shootCooldown = 90; // 50% slower shooting rate
var bombCooldown = 180; // 3 seconds between bombs
var waveNumber = 1;
var enemiesSpawned = 0;
var enemiesPerWave = 5;
var spawnTimer = 0;
var survivalTime = 0;
var dragNode = null;
var isBossWave = false;
var bossEnemy = null;
var playerXP = 0;
var playerLevel = 1;
var xpToNextLevel = 10;
var healthRegenTimer = 0;
var healthRegenCooldown = 60; // 60 ticks = 1 second at 60 FPS
var gamePaused = false;
var upgradeChoices = [];
var upgradeButtons = [];
var upgradeLevels = [0, 0, 0, 0, 0, 0, 0, 0]; // Track levels for each upgrade type (added bomb at index 7)
// Base arrow damage that will be used for all new arrows
var baseArrowDamage = 15;
// Base bomb stats
var baseBombDamage = 120; // 200% more damage (3x original)
var baseBombExplosionRadius = 80;
var bombSpeedMultiplier = 1.0;
var bombFiringSpeedMultiplier = 1.0;
var bombUnlocked = false;
// Base magnet range that will be used for all new XP orbs
var baseMagnetRange = 50;
// Base magnet speed that will be used for all new XP orbs
var baseMagnetSpeed = 8;
// Bow upgrade variables
var arrowsPerBurst = 1;
var arrowSpeedMultiplier = 1.0;
var bowEvolutionActive = false;
// Bomb upgrade variables
var bombsPerBurst = 1;
// Keyboard input state
var keys = {
w: false,
a: false,
s: false,
d: false
};
// UI Elements
var healthBarBg = LK.getAsset('healthBarBg', {
anchorX: 0.5,
anchorY: 0
});
var healthBar = LK.getAsset('healthBar', {
anchorX: 0.5,
anchorY: 0
});
healthBarBg.visible = false;
healthBar.visible = false;
LK.gui.top.addChild(healthBarBg);
LK.gui.top.addChild(healthBar);
var waveText = new Text2('Wave: 1', {
size: 80,
fill: 0xFFFFFF
});
waveText.anchor.set(0.5, 0);
waveText.visible = false;
LK.gui.top.addChild(waveText);
var timeText = new Text2('Time: 0s', {
size: 60,
fill: 0xFFFFFF
});
timeText.anchor.set(1, 0);
timeText.visible = false;
LK.gui.topRight.addChild(timeText);
var levelText = new Text2('Level: 1', {
size: 70,
fill: 0xFFD700
});
levelText.anchor.set(0, 0);
levelText.visible = false;
LK.gui.topLeft.addChild(levelText);
var xpText = new Text2('XP: 0/10', {
size: 50,
fill: 0x00BCD4
});
xpText.anchor.set(0, 0);
xpText.visible = false;
LK.gui.topLeft.addChild(xpText);
// Position UI elements
healthBarBg.y = 120;
healthBar.y = 120;
waveText.y = 200;
timeText.y = 280;
timeText.x = -20;
levelText.x = 120;
levelText.y = 20;
xpText.x = 120;
xpText.y = 100;
function checkLevelUp() {
if (playerXP >= xpToNextLevel) {
playerLevel++;
playerXP -= xpToNextLevel;
xpToNextLevel = Math.floor(xpToNextLevel * 1.15);
// Level up effects
LK.effects.flashScreen(0xFFD700, 800);
player.heal(20);
// Pause game and show upgrade choices
gamePaused = true;
showUpgradeChoices();
}
}
function showUpgradeChoices() {
// Clear any existing upgrade choices
clearUpgradeChoices();
// Define possible upgrades
var allUpgrades = [{
name: 'Health Boost',
description: '+20 Max Health',
index: 0,
effect: function effect() {
player.maxHealth += 20;
player.heal(20);
upgradeLevels[0]++;
}
}, {
name: 'Faster Shooting',
description: 'Shoot 25% Faster',
index: 1,
effect: function effect() {
shootCooldown = Math.max(30, shootCooldown - 15);
upgradeLevels[1]++;
}
}, {
name: 'More Damage',
description: '+50% Arrow Damage',
index: 2,
effect: function effect() {
baseArrowDamage = Math.floor(baseArrowDamage * 1.5);
upgradeLevels[2]++;
}
}, {
name: 'Walkspeed Boost',
description: 'Move 20% Faster',
index: 3,
effect: function effect() {
player.speed += 0.2;
upgradeLevels[3]++;
}
}, {
name: 'Health Regen',
description: 'Heal 2HP per second',
index: 4,
effect: function effect() {
healthRegenCooldown = Math.max(20, healthRegenCooldown - 10);
upgradeLevels[4]++;
}
}, {
name: upgradeLevels[5] === 5 ? 'Magnet Evolution I' : 'Magnet',
description: getMagnetUpgradeDescription(),
index: 5,
effect: function effect() {
applyMagnetUpgrade();
upgradeLevels[5]++;
}
}, {
name: upgradeLevels[6] === 5 ? 'Bow Evolution I' : 'Bow Upgrade',
description: getBowUpgradeDescription(),
index: 6,
effect: function effect() {
applyBowUpgrade();
upgradeLevels[6]++;
}
}, {
name: upgradeLevels[7] === 5 ? 'Bomb Evolution I' : 'Bomb Weapon',
description: getBombUpgradeDescription(),
index: 7,
effect: function effect() {
applyBombUpgrade();
upgradeLevels[7]++;
}
}];
// Filter out upgrades that have reached maximum level (11 levels for magnet, bow, and bomb, 5 for others)
var availableUpgrades = [];
for (var u = 0; u < allUpgrades.length; u++) {
var upgrade = allUpgrades[u];
var maxLevel = upgrade.index === 5 || upgrade.index === 6 || upgrade.index === 7 ? 11 : 5; // Magnet, bow, and bomb have 11 levels (6 + 5 evolution), others have 5
// Check if upgrade is available
var isAvailable = upgradeLevels[upgrade.index] < maxLevel;
// For evolution upgrades (magnet, bow, and bomb at level 5 trying to go to 6), check if evolution is unlocked
if ((upgrade.index === 5 || upgrade.index === 6 || upgrade.index === 7) && upgradeLevels[upgrade.index] === 5) {
isAvailable = isAvailable && permanentUpgrades.unlockEvolution > 0;
}
if (isAvailable) {
availableUpgrades.push(upgrade);
}
}
// If no upgrades are available, resume the game immediately
if (availableUpgrades.length === 0) {
gamePaused = false;
return;
}
// Create upgrade background
var upgradeBackground = LK.getAsset('healthBarBg', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 8,
scaleY: 10
});
upgradeBackground.x = 1024;
upgradeBackground.y = 1366;
upgradeBackground.alpha = 0.9;
game.addChild(upgradeBackground);
upgradeButtons.push(upgradeBackground);
// Create title
var titleText = new Text2('LEVEL UP!', {
size: 100,
fill: 0xFFD700
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 1024;
titleText.y = 1100;
game.addChild(titleText);
upgradeButtons.push(titleText);
// Select 3 random upgrades from available upgrades
upgradeChoices = [];
var upgradeCount = Math.min(3, availableUpgrades.length);
for (var i = 0; i < upgradeCount; i++) {
var randomIndex = Math.floor(Math.random() * availableUpgrades.length);
upgradeChoices.push(availableUpgrades[randomIndex]);
availableUpgrades.splice(randomIndex, 1);
}
// Create upgrade buttons
for (var i = 0; i < upgradeChoices.length; i++) {
var upgrade = upgradeChoices[i];
var buttonX = 350 + i * 700; // Horizontal spacing from left to right
var buttonY = 1330;
// Button background
var buttonBg = LK.getAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 6,
scaleY: 3
});
buttonBg.x = buttonX;
buttonBg.y = buttonY;
// Make button red if it's magnet, bow, or bomb evolution level 5 (evolution I)
if (upgrade.index === 5 && upgradeLevels[5] === 5 || upgrade.index === 6 && upgradeLevels[6] === 5 || upgrade.index === 7 && upgradeLevels[7] === 5) {
buttonBg.tint = 0xCC0000;
buttonBg.alpha = 0.7; // Make evolution upgrade box darker
} else {
buttonBg.tint = 0x4CAF50;
}
buttonBg.upgradeIndex = i;
game.addChild(buttonBg);
upgradeButtons.push(buttonBg);
// Button text
var buttonText = new Text2(upgrade.name, {
size: 80,
fill: 0xFFFFFF
});
buttonText.anchor.set(0.5, 0.5);
buttonText.x = buttonX;
buttonText.y = buttonY - 30;
game.addChild(buttonText);
upgradeButtons.push(buttonText);
// Description text
var descText = new Text2(upgrade.description, {
size: 60,
fill: 0xCCCCCC
});
descText.anchor.set(0.5, 0.5);
descText.x = buttonX;
descText.y = buttonY + 30;
game.addChild(descText);
upgradeButtons.push(descText);
// Add stars below button
var upgradeLevel = upgradeLevels[upgrade.index];
var starY = buttonY + 100;
var maxStars = upgrade.index === 5 || upgrade.index === 6 || upgrade.index === 7 ? 6 : 5; // Magnet, bow, and bomb have 6 stars, others have 5
var starsToShow = maxStars;
// For magnet, bow, and bomb upgrades, calculate display stars based on evolution
var displayStars = upgradeLevel;
if (upgrade.index === 5 || upgrade.index === 6 || upgrade.index === 7) {
// For evolution upgrades: levels 7+ show as stars 1-5, but with 6th star visible
if (upgradeLevel >= 7) {
displayStars = upgradeLevel - 6; // Convert level 7+ to display stars 1-5 (level 7 = 1 star, level 8 = 2 stars, etc.)
starsToShow = 6; // Show 6 stars when in post-evolution (5 regular + 1 red)
} else if (upgradeLevel <= 5) {
starsToShow = 5; // Only show 5 stars before evolution
} else if (upgradeLevel === 6) {
displayStars = 0; // Evolution level shows as 0 stars filled
starsToShow = 6; // Show 6 stars including red one
}
}
for (var s = 0; s < starsToShow; s++) {
var star;
// Special handling for magnet, bow, and bomb evolution (level 6 and above)
if ((upgrade.index === 5 || upgrade.index === 6 || upgrade.index === 7) && upgradeLevel >= 6) {
// When evolution is active, the 6th star should always be red
if (s === 5) {
star = LK.getAsset('star', {
anchorX: 0.5,
anchorY: 0.5
});
star.tint = 0xFF0000; // Red color for evolution I
} else if (s < displayStars) {
// Show filled stars for current evolution level
star = LK.getAsset('star', {
anchorX: 0.5,
anchorY: 0.5
});
} else {
// Show dark stars for unfilled evolution levels
star = LK.getAsset('starDark', {
anchorX: 0.5,
anchorY: 0.5
});
}
} else if ((upgrade.index === 5 || upgrade.index === 6 || upgrade.index === 7) && (upgradeLevel === 5 || upgradeLevel === 6)) {
// Special case for evolution being available or just picked - don't show red star for level 5, show it for level 6
if (s === 5) {
if (upgradeLevel === 6) {
// Show red star for level 6
star = LK.getAsset('star', {
anchorX: 0.5,
anchorY: 0.5
});
star.tint = 0xFF0000; // Red color for evolution I
} else {
// Don't show red star for level 5
star = LK.getAsset('starDark', {
anchorX: 0.5,
anchorY: 0.5
});
}
} else if (s < displayStars) {
star = LK.getAsset('star', {
anchorX: 0.5,
anchorY: 0.5
});
} else {
star = LK.getAsset('starDark', {
anchorX: 0.5,
anchorY: 0.5
});
}
} else {
// Normal star display logic for all other cases
if (s < displayStars) {
star = LK.getAsset('star', {
anchorX: 0.5,
anchorY: 0.5
});
} else {
star = LK.getAsset('starDark', {
anchorX: 0.5,
anchorY: 0.5
});
}
}
// Position stars - for magnet, bow, and bomb (6 stars), put 6th star below first star
if ((upgrade.index === 5 || upgrade.index === 6 || upgrade.index === 7) && s === 5) {
// 6th star for magnet/bow/bomb - position below the first star (star 0)
star.x = buttonX - 100 + 0 * 50; // Same x as first star
star.y = starY + 60; // Below the other stars
} else {
// Normal positioning for first 5 stars with increased spacing
star.x = buttonX - 100 + s * 50;
star.y = starY;
}
game.addChild(star);
upgradeButtons.push(star);
}
}
}
function getMagnetUpgradeDescription() {
var magnetLevel = upgradeLevels[5];
switch (magnetLevel) {
case 0:
case 1:
case 2:
case 3:
case 4:
return '+100% Magnet Range';
case 5:
return 'can pull yellow xp orbs';
case 6:
case 7:
case 8:
case 9:
case 10:
return '+25% Magnet Range';
case 11:
case 12:
case 13:
case 14:
return '+20% Pull Speed';
default:
return 'Max Level';
}
}
function getBowUpgradeDescription() {
var bowLevel = upgradeLevels[6];
switch (bowLevel) {
case 0:
return '+1 Arrow (Burst)';
case 1:
return '+100% Arrow Speed';
case 2:
return '+50% Arrow Damage';
case 3:
return '+25% Arrow Speed';
case 4:
return '+1 Arrow (Burst)';
case 5:
return 'seeking arrows';
case 6:
return '+25% Shoot Speed';
case 7:
return '+20% Arrow Speed';
case 8:
return '+1 Arrow';
case 9:
return '+50% Arrow Damage';
case 10:
return '+1 Arrow';
default:
return 'Max Level';
}
}
function applyMagnetUpgrade() {
var magnetLevel = upgradeLevels[5];
switch (magnetLevel) {
case 0:
case 1:
case 2:
case 3:
case 4:
// Levels 1-5: Double magnet range for all existing and future XP orbs
for (var o = 0; o < xpOrbs.length; o++) {
xpOrbs[o].attractDistance *= 2;
}
// Double base magnet range for new orbs
baseMagnetRange *= 2;
break;
case 5:
// Evolution: Double magnet pulling speed for all existing and future XP orbs
for (var o = 0; o < xpOrbs.length; o++) {
xpOrbs[o].attractSpeed *= 2;
}
// Double base magnet speed for new orbs
baseMagnetSpeed *= 2;
// Don't increment level for evolution - it stays at 5 until next upgrade
break;
case 6:
case 7:
case 8:
case 9:
case 10:
// Evolution level 6-10: +25% magnet range for all existing and future XP orbs
for (var o = 0; o < xpOrbs.length; o++) {
xpOrbs[o].attractDistance *= 1.25;
}
// +25% base magnet range for new orbs
baseMagnetRange *= 1.25;
break;
case 11:
case 12:
case 13:
case 14:
// Evolution levels 11-14: +20% pull speed for all existing and future XP orbs
for (var o = 0; o < xpOrbs.length; o++) {
xpOrbs[o].attractSpeed *= 1.2;
}
// +20% base magnet speed for new orbs
baseMagnetSpeed *= 1.2;
break;
}
}
function applyBowUpgrade() {
var bowLevel = upgradeLevels[6];
switch (bowLevel) {
case 0:
// Level 1: +1 arrow (burst)
arrowsPerBurst = 2;
break;
case 1:
// Level 2: +100% arrow speed
arrowSpeedMultiplier *= 2.0;
break;
case 2:
// Level 3: +50% arrow damage
baseArrowDamage = Math.floor(baseArrowDamage * 1.5);
break;
case 3:
// Level 4: +25% arrow speed
arrowSpeedMultiplier *= 1.25;
break;
case 4:
// Level 5: +1 arrow (burst)
arrowsPerBurst = 3;
break;
case 5:
// Evolution: seeking arrows
bowEvolutionActive = true;
break;
case 6:
// Evolution level 1: +25% shoot speed
shootCooldown = Math.floor(shootCooldown * 0.75);
break;
case 7:
// Evolution level 2: +20% arrow speed
arrowSpeedMultiplier *= 1.2;
break;
case 8:
// Evolution level 3: +1 arrow
arrowsPerBurst++;
break;
case 9:
// Evolution level 4: +50% arrow damage
baseArrowDamage = Math.floor(baseArrowDamage * 1.5);
break;
case 10:
// Evolution level 5: +1 arrow
arrowsPerBurst++;
break;
}
}
function clearUpgradeChoices() {
for (var i = 0; i < upgradeButtons.length; i++) {
upgradeButtons[i].destroy();
}
upgradeButtons = [];
upgradeChoices = [];
}
function selectUpgrade(index) {
if (index >= 0 && index < upgradeChoices.length) {
// Apply upgrade effect
upgradeChoices[index].effect();
// Clear upgrade UI
clearUpgradeChoices();
// Resume game
gamePaused = false;
}
}
function spawnEnemy() {
// Check for boss fight on wave 10 in forest map
if (waveNumber === 10 && selectedMap === 0 && !isBossWave && enemiesSpawned === 0) {
isBossWave = true;
bossEnemy = new ForestBoss();
bossEnemy.x = 1024; // Center of screen
bossEnemy.y = 300; // Top area
bossEnemy.targetX = player.x;
bossEnemy.targetY = player.y;
enemies.push(bossEnemy);
game.addChild(bossEnemy);
enemiesSpawned = 1; // Mark as spawned
// Flash screen to announce boss
LK.effects.flashScreen(0x2e7d32, 1000);
return;
}
// Check for boss fight on wave 15 in desert map
if (waveNumber === 15 && selectedMap === 1 && !isBossWave && enemiesSpawned === 0) {
isBossWave = true;
bossEnemy = new DesertBoss();
bossEnemy.x = 1024; // Center of screen
bossEnemy.y = 300; // Top area
bossEnemy.targetX = player.x;
bossEnemy.targetY = player.y;
enemies.push(bossEnemy);
game.addChild(bossEnemy);
enemiesSpawned = 1; // Mark as spawned
// Flash screen to announce desert boss
LK.effects.flashScreen(0x8b4513, 1000);
return;
}
// Check for boss fight on wave 20 in arctic map
if (waveNumber === 20 && selectedMap === 2 && !isBossWave && enemiesSpawned === 0) {
isBossWave = true;
bossEnemy = new ArcticBoss();
bossEnemy.x = 1024; // Center of screen
bossEnemy.y = 300; // Top area
bossEnemy.targetX = player.x;
bossEnemy.targetY = player.y;
enemies.push(bossEnemy);
game.addChild(bossEnemy);
enemiesSpawned = 1; // Mark as spawned
// Flash screen to announce arctic boss
LK.effects.flashScreen(0x87ceeb, 1000);
return;
}
var enemy;
if (waveNumber >= 5 && Math.random() < 0.15) {
enemy = new HeavyEnemy();
} else if (waveNumber >= 3 && Math.random() < 0.3) {
enemy = new FastEnemy();
} else {
enemy = new Enemy();
}
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;
}
enemy.targetX = player.x;
enemy.targetY = player.y;
enemy.speed += Math.floor(waveNumber / 3) * 0.5;
// Scale enemy health based on selected map
var healthMultiplier;
if (selectedMap === 1) {
// Desert map: 1.25x enemy HP per wave
healthMultiplier = Math.pow(1.25, waveNumber - 1);
} else if (selectedMap === 2) {
// Arctic map: 1.3x enemy HP per wave
healthMultiplier = Math.pow(1.3, waveNumber - 1);
} else {
// Forest map: 1.2x enemy HP per wave
healthMultiplier = Math.pow(1.2, waveNumber - 1);
}
enemy.maxHealth = Math.floor(enemy.maxHealth * healthMultiplier);
enemy.health = enemy.maxHealth;
enemy.updateHealthBar();
enemies.push(enemy);
game.addChild(enemy);
enemiesSpawned++;
}
function updateHealthBar() {
var healthPercentage = player.health / player.maxHealth;
healthBar.width = 400 * healthPercentage;
if (healthPercentage > 0.6) {
healthBar.tint = 0x4CAF50;
} else if (healthPercentage > 0.3) {
healthBar.tint = 0xFFEB3B;
} else {
healthBar.tint = 0xFF5722;
}
}
function handleMove(x, y, obj) {
if (dragNode && !gameOver) {
var newX = Math.max(40, Math.min(2008, x));
var newY = Math.max(40, Math.min(2692, y));
var dx = newX - dragNode.x;
var dy = newY - dragNode.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
var moveDistance = Math.min(distance, 8 * dragNode.speed);
dragNode.x += dx / distance * moveDistance;
dragNode.y += dy / distance * moveDistance;
}
}
}
game.move = handleMove;
game.down = function (x, y, obj) {
if (!gameStarted) {
if (mapSelectionActive) {
// Handle map selection clicks
for (var i = 0; i < mapSelectionButtons.length; i++) {
var button = mapSelectionButtons[i];
if (button.isStartButton) {
// Start button - begin game with selected map
var buttonWidth = 60 * 4;
var buttonHeight = 60 * 2;
var buttonLeft = button.x - buttonWidth / 2;
var buttonRight = button.x + buttonWidth / 2;
var buttonTop = button.y - buttonHeight / 2;
var buttonBottom = button.y + buttonHeight / 2;
if (x >= buttonLeft && x <= buttonRight && y >= buttonTop && y <= buttonBottom) {
mapSelectionActive = false;
clearMapSelection();
startGame();
return;
}
} else if (button.isBackButton) {
// Back button - return to main menu
var buttonWidth = 60 * 3;
var buttonHeight = 60 * 1.5;
var buttonLeft = button.x - buttonWidth / 2;
var buttonRight = button.x + buttonWidth / 2;
var buttonTop = button.y - buttonHeight / 2;
var buttonBottom = button.y + buttonHeight / 2;
if (x >= buttonLeft && x <= buttonRight && y >= buttonTop && y <= buttonBottom) {
mapSelectionActive = false;
clearMapSelection();
createStartScreen();
return;
}
} else if (button.mapIndex !== undefined) {
// Map selection button
var buttonWidth = 60 * 10;
var buttonHeight = 60 * 3;
var buttonLeft = button.x - buttonWidth / 2;
var buttonRight = button.x + buttonWidth / 2;
var buttonTop = button.y - buttonHeight / 2;
var buttonBottom = button.y + buttonHeight / 2;
if (x >= buttonLeft && x <= buttonRight && y >= buttonTop && y <= buttonBottom) {
selectMap(button.mapIndex);
return;
}
}
}
return;
} else if (upgradesScreenActive) {
// Handle upgrades screen clicks
for (var i = 0; i < upgradesScreenButtons.length; i++) {
var button = upgradesScreenButtons[i];
if (button.isBackButton) {
// Back button - return to main menu
var buttonWidth = 60 * 3;
var buttonHeight = 60 * 1.5;
var buttonLeft = button.x - buttonWidth / 2;
var buttonRight = button.x + buttonWidth / 2;
var buttonTop = button.y - buttonHeight / 2;
var buttonBottom = button.y + buttonHeight / 2;
if (x >= buttonLeft && x <= buttonRight && y >= buttonTop && y <= buttonBottom) {
upgradesScreenActive = false;
clearUpgradesScreen();
createStartScreen();
return;
}
} else if (button.upgradeKey) {
// Upgrade button
var buttonWidth = 60 * 12;
var buttonHeight = 60 * 3;
var buttonLeft = button.x - buttonWidth / 2;
var buttonRight = button.x + buttonWidth / 2;
var buttonTop = button.y - buttonHeight / 2;
var buttonBottom = button.y + buttonHeight / 2;
if (x >= buttonLeft && x <= buttonRight && y >= buttonTop && y <= buttonBottom && !button.isMaxLevel) {
purchaseUpgrade(button.upgradeKey, button.upgradeCost);
return;
}
}
}
return;
}
// Check if play button was clicked
if (playButton) {
var buttonWidth = 60 * 4; // 240
var buttonHeight = 60 * 2; // 120
var buttonLeft = playButton.x - buttonWidth / 2;
var buttonRight = playButton.x + buttonWidth / 2;
var buttonTop = playButton.y - buttonHeight / 2;
var buttonBottom = playButton.y + buttonHeight / 2;
if (x >= buttonLeft && x <= buttonRight && y >= buttonTop && y <= buttonBottom) {
// Hide start screen and show map selection
if (startScreen) {
startScreen.destroy();
startScreen = null;
}
playButton.buttonText.destroy();
if (playButton.upgradesButton) {
playButton.upgradesButton.destroy();
playButton.upgradesButtonText.destroy();
}
playButton.destroy();
playButton = null;
showMapSelection();
return;
}
// Check if upgrades button was clicked
if (playButton.upgradesButton) {
var upgradesButtonWidth = 60 * 3;
var upgradesButtonHeight = 60 * 1.5;
var upgradesButtonLeft = playButton.upgradesButton.x - upgradesButtonWidth / 2;
var upgradesButtonRight = playButton.upgradesButton.x + upgradesButtonWidth / 2;
var upgradesButtonTop = playButton.upgradesButton.y - upgradesButtonHeight / 2;
var upgradesButtonBottom = playButton.upgradesButton.y + upgradesButtonHeight / 2;
if (x >= upgradesButtonLeft && x <= upgradesButtonRight && y >= upgradesButtonTop && y <= upgradesButtonBottom) {
// Hide start screen and show upgrades screen
if (startScreen) {
startScreen.destroy();
startScreen = null;
}
playButton.buttonText.destroy();
playButton.upgradesButton.destroy();
playButton.upgradesButtonText.destroy();
playButton.destroy();
playButton = null;
showUpgradesScreen();
return;
}
}
}
return;
}
if (!gameOver) {
// Check if we're in upgrade selection mode
if (gamePaused && upgradeButtons.length > 0) {
// Check if clicked on an upgrade button
for (var i = 0; i < upgradeButtons.length; i++) {
var button = upgradeButtons[i];
if (button.upgradeIndex !== undefined) {
// Check if click position is within button bounds
// Button is scaled 6x horizontally and 3x vertically from base enemy size (60x60)
var buttonWidth = 60 * 6; // 360
var buttonHeight = 60 * 3; // 180
var buttonLeft = button.x - buttonWidth / 2;
var buttonRight = button.x + buttonWidth / 2;
var buttonTop = button.y - buttonHeight / 2;
var buttonBottom = button.y + buttonHeight / 2;
if (x >= buttonLeft && x <= buttonRight && y >= buttonTop && y <= buttonBottom) {
selectUpgrade(button.upgradeIndex);
return;
}
}
}
} else {
dragNode = player;
handleMove(x, y, obj);
}
}
};
game.up = function (x, y, obj) {
dragNode = null;
};
// Keyboard event listeners
LK.on('keydown', function (event) {
switch (event.key.toLowerCase()) {
case 'w':
keys.w = true;
break;
case 'a':
keys.a = true;
break;
case 's':
keys.s = true;
break;
case 'd':
keys.d = true;
break;
}
});
LK.on('keyup', function (event) {
switch (event.key.toLowerCase()) {
case 'w':
keys.w = false;
break;
case 'a':
keys.a = false;
break;
case 's':
keys.s = false;
break;
case 'd':
keys.d = false;
break;
}
});
game.update = function () {
if (!gameStarted || gameOver) return;
// Check pause state before any updates
if (gamePaused) return;
survivalTime++;
// Handle WASD movement
var moveSpeed = 5;
if (keys.w) {
player.y = Math.max(40, player.y - moveSpeed);
}
if (keys.s) {
player.y = Math.min(2692, player.y + moveSpeed);
}
if (keys.a) {
player.x = Math.max(40, player.x - moveSpeed);
}
if (keys.d) {
player.x = Math.min(2008, player.x + moveSpeed);
}
// Health regeneration
healthRegenTimer++;
if (healthRegenTimer >= healthRegenCooldown && player.health < player.maxHealth) {
player.heal(1);
healthRegenTimer = 0;
}
// Update UI
updateHealthBar();
waveText.setText('Wave: ' + waveNumber);
timeText.setText('Time: ' + Math.floor(survivalTime / 60) + 's');
levelText.setText('Level: ' + playerLevel);
xpText.setText('XP: ' + playerXP + '/' + xpToNextLevel);
// Enemy spawning
spawnTimer++;
var spawnRate = Math.max(15, 60 - waveNumber * 3);
// For boss wave on forest map (wave 10), desert map (wave 15), or arctic map (wave 20), only spawn the boss
if (waveNumber === 10 && selectedMap === 0 || waveNumber === 15 && selectedMap === 1 || waveNumber === 20 && selectedMap === 2) {
if (!isBossWave && enemiesSpawned === 0) {
spawnEnemy(); // This will spawn the boss
spawnTimer = 0;
}
} else {
// Normal enemy spawning for other waves/maps
if (spawnTimer >= spawnRate && enemiesSpawned < enemiesPerWave) {
spawnEnemy();
spawnTimer = 0;
}
}
// Auto-shoot arrows at nearest enemy
lastShootTime++;
if (lastShootTime >= shootCooldown && enemies.length > 0) {
var nearestEnemy = null;
var nearestDistance = Infinity;
for (var e = 0; e < enemies.length; e++) {
var enemy = enemies[e];
var dx = enemy.x - player.x;
var dy = enemy.y - player.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < nearestDistance) {
nearestDistance = distance;
nearestEnemy = enemy;
}
}
if (nearestEnemy) {
var baseDx = nearestEnemy.x - player.x;
var baseDy = nearestEnemy.y - player.y;
// Fire multiple arrows in a burst
for (var b = 0; b < arrowsPerBurst; b++) {
// Add delay for burst arrows so they fire sequentially
if (b > 0) {
LK.setTimeout(function (burstIndex) {
return function () {
var arrow = new Arrow();
arrow.x = player.x;
arrow.y = player.y;
var dx = baseDx;
var dy = baseDy;
// All arrows fire in same direction
arrow.setDirection(dx, dy);
// Enable seeking if bow evolution is active
arrow.isSeeking = bowEvolutionActive;
arrows.push(arrow);
game.addChild(arrow);
};
}(b), b * 100); // 100ms delay between each arrow
} else {
// Fire first arrow immediately
var arrow = new Arrow();
arrow.x = player.x;
arrow.y = player.y;
var dx = baseDx;
var dy = baseDy;
// All arrows fire in same direction
arrow.setDirection(dx, dy);
// Enable seeking if bow evolution is active
arrow.isSeeking = bowEvolutionActive;
arrows.push(arrow);
game.addChild(arrow);
}
}
LK.getSound('arrowShoot').play();
lastShootTime = 0;
}
}
// Auto-shoot bombs at nearest enemy (if bomb weapon is unlocked)
if (bombUnlocked) {
lastBombTime++;
var actualBombCooldown = Math.floor(bombCooldown / bombFiringSpeedMultiplier);
if (lastBombTime >= actualBombCooldown && enemies.length > 0) {
var nearestEnemy = null;
var nearestDistance = Infinity;
for (var e = 0; e < enemies.length; e++) {
var enemy = enemies[e];
var dx = enemy.x - player.x;
var dy = enemy.y - player.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < nearestDistance) {
nearestDistance = distance;
nearestEnemy = enemy;
}
}
if (nearestEnemy) {
var baseDx = nearestEnemy.x - player.x;
var baseDy = nearestEnemy.y - player.y;
// Fire multiple bombs in a burst
for (var b = 0; b < bombsPerBurst; b++) {
// Add delay for burst bombs so they fire sequentially
if (b > 0) {
LK.setTimeout(function (burstIndex) {
return function () {
var bomb = new Bomb();
bomb.x = player.x;
bomb.y = player.y;
var dx = baseDx;
var dy = baseDy;
bomb.setDirection(dx, dy);
bombs.push(bomb);
game.addChild(bomb);
};
}(b), b * 150); // 150ms delay between each bomb
} else {
// Fire first bomb immediately
var bomb = new Bomb();
bomb.x = player.x;
bomb.y = player.y;
var dx = baseDx;
var dy = baseDy;
bomb.setDirection(dx, dy);
bombs.push(bomb);
game.addChild(bomb);
}
}
lastBombTime = 0;
}
}
}
// Update arrows
for (var a = arrows.length - 1; a >= 0; a--) {
var arrow = arrows[a];
// Remove arrows that are off screen
if (arrow.x < -100 || arrow.x > 2148 || arrow.y < -100 || arrow.y > 2832) {
arrows.splice(a, 1);
arrow.destroy();
}
}
// Update bombs
for (var b = bombs.length - 1; b >= 0; b--) {
var bomb = bombs[b];
// Remove bombs that are off screen or explode them
if (bomb.x < -100 || bomb.x > 2148 || bomb.y < -100 || bomb.y > 2832) {
bomb.explode();
}
}
// Update XP orbs
for (var x = xpOrbs.length - 1; x >= 0; x--) {
var xpOrb = xpOrbs[x];
// Remove orbs that are too far off screen
if (xpOrb.x < -200 || xpOrb.x > 2248 || xpOrb.y < -200 || xpOrb.y > 2932) {
xpOrbs.splice(x, 1);
xpOrb.destroy();
}
}
// Update enemies
for (var i = enemies.length - 1; i >= 0; i--) {
var enemy = enemies[i];
enemy.targetX = player.x;
enemy.targetY = player.y;
// Remove enemies that are too far off screen
if (enemy.x < -200 || enemy.x > 2248 || enemy.y < -200 || enemy.y > 2932) {
enemies.splice(i, 1);
enemy.destroy();
}
}
// Check wave completion
var waveCompleted = false;
if (isBossWave && (selectedMap === 0 && waveNumber === 10 || selectedMap === 1 && waveNumber === 15 || selectedMap === 2 && waveNumber === 20)) {
// Boss wave completion - check if boss is defeated
waveCompleted = enemies.length === 0;
} else {
// Normal wave completion
waveCompleted = enemiesSpawned >= enemiesPerWave && enemies.length === 0;
}
if (waveCompleted) {
waveNumber++;
enemiesSpawned = 0;
enemiesPerWave = Math.min(20, 5 + waveNumber * 2);
// Reset boss wave flag
if (isBossWave) {
isBossWave = false;
bossEnemy = null;
// Extra bonus health and XP for defeating boss
player.heal(50);
playerXP += 100;
checkLevelUp();
} else {
// Normal wave completion bonus
player.heal(10);
}
LK.effects.flashScreen(0x4CAF50, 500);
// Check if player completed all waves based on map
var maxWaves = selectedMap === 1 ? 15 : selectedMap === 2 ? 20 : 10; // Desert has 15 waves, Arctic has 20 waves, Forest has 10
if (waveNumber > maxWaves) {
gameOver = true;
// Save best survival time
var bestTime = storage.bestTime || 0;
if (survivalTime > bestTime) {
storage.bestTime = survivalTime;
}
// Save total waves completed
var totalWaves = storage.totalWaves || 0;
storage.totalWaves = totalWaves + maxWaves; // Completed all waves for the map
// Award coins based on new formula: enemies defeated + 2000 ÷ time
var enemiesDefeated = (storage.totalWaves || 0) * 10; // Estimate enemies defeated based on waves
var coinsEarned = Math.floor((enemiesDefeated + 2000 / (survivalTime / 60)) * 0.25);
// Apply map coin multipliers
if (selectedMap === 1) {
// Desert map: 1.1x coins
coinsEarned = Math.floor(coinsEarned * 1.1);
} else if (selectedMap === 2) {
// Arctic map: 1.2x coins
coinsEarned = Math.floor(coinsEarned * 1.2);
}
permanentUpgrades.coins += coinsEarned;
storage.coins = permanentUpgrades.coins;
LK.setScore(Math.floor(survivalTime / 60) + maxWaves * 10);
LK.effects.flashScreen(0xFFD700, 1000);
LK.setTimeout(function () {
LK.showYouWin();
}, 1500);
}
}
// Game over check
if (player.health <= 0) {
gameOver = true;
// Save best survival time
var bestTime = storage.bestTime || 0;
if (survivalTime > bestTime) {
storage.bestTime = survivalTime;
}
// Save total waves completed
var totalWaves = storage.totalWaves || 0;
storage.totalWaves = totalWaves + (waveNumber - 1);
// Award coins based on new formula: enemies defeated + 2000 ÷ time
var enemiesDefeated = (storage.totalWaves || 0) * 10; // Estimate enemies defeated based on waves
var coinsEarned = Math.floor((enemiesDefeated + 2000 / (survivalTime / 60)) * 0.25);
// Apply map coin multipliers
if (selectedMap === 1) {
// Desert map: 1.1x coins
coinsEarned = Math.floor(coinsEarned * 1.1);
} else if (selectedMap === 2) {
// Arctic map: 1.2x coins
coinsEarned = Math.floor(coinsEarned * 1.2);
}
permanentUpgrades.coins += coinsEarned;
storage.coins = permanentUpgrades.coins;
LK.setScore(Math.floor(survivalTime / 60) + (waveNumber - 1) * 10);
LK.effects.flashScreen(0xFF0000, 1000);
LK.setTimeout(function () {
LK.showGameOver();
}, 1500);
}
};
function getBombUpgradeDescription() {
var bombLevel = upgradeLevels[7];
switch (bombLevel) {
case 0:
return 'Unlock Bomb Weapon';
case 1:
return '+25% Explosion Radius';
case 2:
return '+50% Firing Speed';
case 3:
return '+100% Bomb Speed';
case 4:
return '+75% Damage';
case 5:
return '+75% explosion radius';
case 6:
return '+50% Bomb Damage';
case 7:
return '+1 Bomb';
case 8:
return '+20% Bomb Firing Speed';
case 9:
return '+20% Bomb Speed';
case 10:
return '+10% Explosion Radius';
default:
return 'Max Level';
}
}
function applyBombUpgrade() {
var bombLevel = upgradeLevels[7];
switch (bombLevel) {
case 0:
// Unlock bomb weapon
bombUnlocked = true;
break;
case 1:
// Star 1: +25% explosion radius
baseBombExplosionRadius = Math.floor(baseBombExplosionRadius * 1.25);
break;
case 2:
// Star 2: +50% firing speed
bombFiringSpeedMultiplier *= 1.5;
break;
case 3:
// Star 3: +100% bomb speed
bombSpeedMultiplier *= 2.0;
break;
case 4:
// Star 4: +75% damage
baseBombDamage = Math.floor(baseBombDamage * 1.75);
break;
case 5:
// Bomb Evolution: +75% explosion radius
baseBombExplosionRadius = Math.floor(baseBombExplosionRadius * 1.75);
break;
case 6:
// Evolution Star 1: +50% bomb damage
baseBombDamage = Math.floor(baseBombDamage * 1.5);
break;
case 7:
// Evolution Star 2: +1 bomb
bombsPerBurst++;
break;
case 8:
// Evolution Star 3: +20% bomb firing speed
bombFiringSpeedMultiplier *= 1.2;
break;
case 9:
// Evolution Star 4: +20% bomb speed
bombSpeedMultiplier *= 1.2;
break;
case 10:
// Evolution Star 5: +10% explosion radius
baseBombExplosionRadius = Math.floor(baseBombExplosionRadius * 1.1);
break;
}
} ===================================================================
--- original.js
+++ change.js
@@ -1500,8 +1500,10 @@
// Bow upgrade variables
var arrowsPerBurst = 1;
var arrowSpeedMultiplier = 1.0;
var bowEvolutionActive = false;
+// Bomb upgrade variables
+var bombsPerBurst = 1;
// Keyboard input state
var keys = {
w: false,
a: false,
@@ -1633,31 +1635,27 @@
applyBowUpgrade();
upgradeLevels[6]++;
}
}, {
- name: 'Bomb Weapon',
+ name: upgradeLevels[7] === 5 ? 'Bomb Evolution I' : 'Bomb Weapon',
description: getBombUpgradeDescription(),
index: 7,
effect: function effect() {
applyBombUpgrade();
upgradeLevels[7]++;
}
}];
- // Filter out upgrades that have reached maximum level (11 levels for magnet and bow, 5 for others)
+ // Filter out upgrades that have reached maximum level (11 levels for magnet, bow, and bomb, 5 for others)
var availableUpgrades = [];
for (var u = 0; u < allUpgrades.length; u++) {
var upgrade = allUpgrades[u];
- var maxLevel = upgrade.index === 5 || upgrade.index === 6 ? 11 : 5; // Magnet and bow have 11 levels (6 + 5 evolution), others have 5
+ var maxLevel = upgrade.index === 5 || upgrade.index === 6 || upgrade.index === 7 ? 11 : 5; // Magnet, bow, and bomb have 11 levels (6 + 5 evolution), others have 5
// Check if upgrade is available
var isAvailable = upgradeLevels[upgrade.index] < maxLevel;
- // For evolution upgrades (magnet and bow at level 5 trying to go to 6), check if evolution is unlocked
- if ((upgrade.index === 5 || upgrade.index === 6) && upgradeLevels[upgrade.index] === 5) {
+ // For evolution upgrades (magnet, bow, and bomb at level 5 trying to go to 6), check if evolution is unlocked
+ if ((upgrade.index === 5 || upgrade.index === 6 || upgrade.index === 7) && upgradeLevels[upgrade.index] === 5) {
isAvailable = isAvailable && permanentUpgrades.unlockEvolution > 0;
}
- // For bomb weapon, make it available like other upgrades
- if (upgrade.index === 7) {
- isAvailable = upgradeLevels[upgrade.index] < maxLevel;
- }
if (isAvailable) {
availableUpgrades.push(upgrade);
}
}
@@ -1709,10 +1707,10 @@
scaleY: 3
});
buttonBg.x = buttonX;
buttonBg.y = buttonY;
- // Make button red if it's magnet or bow evolution level 5 (evolution I)
- if (upgrade.index === 5 && upgradeLevels[5] === 5 || upgrade.index === 6 && upgradeLevels[6] === 5) {
+ // Make button red if it's magnet, bow, or bomb evolution level 5 (evolution I)
+ if (upgrade.index === 5 && upgradeLevels[5] === 5 || upgrade.index === 6 && upgradeLevels[6] === 5 || upgrade.index === 7 && upgradeLevels[7] === 5) {
buttonBg.tint = 0xCC0000;
buttonBg.alpha = 0.7; // Make evolution upgrade box darker
} else {
buttonBg.tint = 0x4CAF50;
@@ -1742,13 +1740,13 @@
upgradeButtons.push(descText);
// Add stars below button
var upgradeLevel = upgradeLevels[upgrade.index];
var starY = buttonY + 100;
- var maxStars = upgrade.index === 5 || upgrade.index === 6 ? 6 : 5; // Magnet and bow have 6 stars, others have 5
+ var maxStars = upgrade.index === 5 || upgrade.index === 6 || upgrade.index === 7 ? 6 : 5; // Magnet, bow, and bomb have 6 stars, others have 5
var starsToShow = maxStars;
- // For magnet and bow upgrades, calculate display stars based on evolution
+ // For magnet, bow, and bomb upgrades, calculate display stars based on evolution
var displayStars = upgradeLevel;
- if (upgrade.index === 5 || upgrade.index === 6) {
+ if (upgrade.index === 5 || upgrade.index === 6 || upgrade.index === 7) {
// For evolution upgrades: levels 7+ show as stars 1-5, but with 6th star visible
if (upgradeLevel >= 7) {
displayStars = upgradeLevel - 6; // Convert level 7+ to display stars 1-5 (level 7 = 1 star, level 8 = 2 stars, etc.)
starsToShow = 6; // Show 6 stars when in post-evolution (5 regular + 1 red)
@@ -1760,10 +1758,10 @@
}
}
for (var s = 0; s < starsToShow; s++) {
var star;
- // Special handling for magnet and bow evolution (level 6 and above)
- if ((upgrade.index === 5 || upgrade.index === 6) && upgradeLevel >= 6) {
+ // Special handling for magnet, bow, and bomb evolution (level 6 and above)
+ if ((upgrade.index === 5 || upgrade.index === 6 || upgrade.index === 7) && upgradeLevel >= 6) {
// When evolution is active, the 6th star should always be red
if (s === 5) {
star = LK.getAsset('star', {
anchorX: 0.5,
@@ -1782,9 +1780,9 @@
anchorX: 0.5,
anchorY: 0.5
});
}
- } else if ((upgrade.index === 5 || upgrade.index === 6) && (upgradeLevel === 5 || upgradeLevel === 6)) {
+ } else if ((upgrade.index === 5 || upgrade.index === 6 || upgrade.index === 7) && (upgradeLevel === 5 || upgradeLevel === 6)) {
// Special case for evolution being available or just picked - don't show red star for level 5, show it for level 6
if (s === 5) {
if (upgradeLevel === 6) {
// Show red star for level 6
@@ -1824,11 +1822,11 @@
anchorY: 0.5
});
}
}
- // Position stars - for magnet and bow (6 stars), put 6th star below first star
- if ((upgrade.index === 5 || upgrade.index === 6) && s === 5) {
- // 6th star for magnet/bow - position below the first star (star 0)
+ // Position stars - for magnet, bow, and bomb (6 stars), put 6th star below first star
+ if ((upgrade.index === 5 || upgrade.index === 6 || upgrade.index === 7) && s === 5) {
+ // 6th star for magnet/bow/bomb - position below the first star (star 0)
star.x = buttonX - 100 + 0 * 50; // Same x as first star
star.y = starY + 60; // Below the other stars
} else {
// Normal positioning for first 5 stars with increased spacing
@@ -2451,16 +2449,38 @@
nearestEnemy = enemy;
}
}
if (nearestEnemy) {
- var bomb = new Bomb();
- bomb.x = player.x;
- bomb.y = player.y;
- var dx = nearestEnemy.x - player.x;
- var dy = nearestEnemy.y - player.y;
- bomb.setDirection(dx, dy);
- bombs.push(bomb);
- game.addChild(bomb);
+ var baseDx = nearestEnemy.x - player.x;
+ var baseDy = nearestEnemy.y - player.y;
+ // Fire multiple bombs in a burst
+ for (var b = 0; b < bombsPerBurst; b++) {
+ // Add delay for burst bombs so they fire sequentially
+ if (b > 0) {
+ LK.setTimeout(function (burstIndex) {
+ return function () {
+ var bomb = new Bomb();
+ bomb.x = player.x;
+ bomb.y = player.y;
+ var dx = baseDx;
+ var dy = baseDy;
+ bomb.setDirection(dx, dy);
+ bombs.push(bomb);
+ game.addChild(bomb);
+ };
+ }(b), b * 150); // 150ms delay between each bomb
+ } else {
+ // Fire first bomb immediately
+ var bomb = new Bomb();
+ bomb.x = player.x;
+ bomb.y = player.y;
+ var dx = baseDx;
+ var dy = baseDy;
+ bomb.setDirection(dx, dy);
+ bombs.push(bomb);
+ game.addChild(bomb);
+ }
+ }
lastBombTime = 0;
}
}
}
@@ -2603,9 +2623,19 @@
return '+100% Bomb Speed';
case 4:
return '+75% Damage';
case 5:
- return '+50% Explosion Radius';
+ return '+75% explosion radius';
+ case 6:
+ return '+50% Bomb Damage';
+ case 7:
+ return '+1 Bomb';
+ case 8:
+ return '+20% Bomb Firing Speed';
+ case 9:
+ return '+20% Bomb Speed';
+ case 10:
+ return '+10% Explosion Radius';
default:
return 'Max Level';
}
}
@@ -2632,9 +2662,29 @@
// Star 4: +75% damage
baseBombDamage = Math.floor(baseBombDamage * 1.75);
break;
case 5:
- // Star 5: +50% explosion radius
- baseBombExplosionRadius = Math.floor(baseBombExplosionRadius * 1.5);
+ // Bomb Evolution: +75% explosion radius
+ baseBombExplosionRadius = Math.floor(baseBombExplosionRadius * 1.75);
break;
+ case 6:
+ // Evolution Star 1: +50% bomb damage
+ baseBombDamage = Math.floor(baseBombDamage * 1.5);
+ break;
+ case 7:
+ // Evolution Star 2: +1 bomb
+ bombsPerBurst++;
+ break;
+ case 8:
+ // Evolution Star 3: +20% bomb firing speed
+ bombFiringSpeedMultiplier *= 1.2;
+ break;
+ case 9:
+ // Evolution Star 4: +20% bomb speed
+ bombSpeedMultiplier *= 1.2;
+ break;
+ case 10:
+ // Evolution Star 5: +10% explosion radius
+ baseBombExplosionRadius = Math.floor(baseBombExplosionRadius * 1.1);
+ break;
}
}
\ No newline at end of file