/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Bullet = Container.expand(function (startX, startY, target, damage) {
var self = Container.call(this);
self.attachAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.x = startX;
self.y = startY;
self.target = target;
self.damage = damage;
self.speed = 8;
self.update = function () {
if (!self.target || self.target.destroyed) {
self.removeBullet();
return;
}
var dx = self.target.x - self.x;
var dy = self.target.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 10) {
if (self.target && !self.target.destroyed) {
self.target.takeDamage(self.damage);
}
// If this is an explosive bullet, create area damage
if (self.isExplosive) {
// Create explosion effect
var explosionEffect = LK.getAsset('soldier', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.8,
alpha: 0.8
});
explosionEffect.x = self.target.x;
explosionEffect.y = self.target.y;
explosionEffect.tint = 0xff4444;
game.addChild(explosionEffect);
// Animate explosion
tween(explosionEffect, {
scaleX: 2.5,
scaleY: 2.5,
alpha: 0
}, {
duration: 400,
easing: tween.easeOut,
onFinish: function onFinish() {
explosionEffect.destroy();
}
});
// Damage nearby enemies
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
if (enemy !== self.target) {
var expDistance = Math.sqrt(Math.pow(enemy.x - self.target.x, 2) + Math.pow(enemy.y - self.target.y, 2));
if (expDistance <= 80) {
// Explosion radius
enemy.takeDamage(Math.floor(self.damage * 0.6)); // 60% damage to nearby enemies
}
}
}
}
self.removeBullet();
} else {
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
}
};
self.removeBullet = function () {
for (var i = bullets.length - 1; i >= 0; i--) {
if (bullets[i] === self) {
bullets.splice(i, 1);
break;
}
}
self.destroy();
};
return self;
});
var Enemy = Container.expand(function (type) {
var self = Container.call(this);
self.type = type;
self.pathIndex = 0;
self.x = path[0].x;
self.y = path[0].y;
self.lastPathIndex = 0;
if (type === 'soldier') {
self.attachAsset('soldier', {
anchorX: 0.5,
anchorY: 0.5
});
var soldierEyeLeft = self.attachAsset('soldierEyeLeft', {
anchorX: 0.5,
anchorY: 0.5,
x: -12,
y: -8
});
var soldierEyeRight = self.attachAsset('soldierEyeRight', {
anchorX: 0.5,
anchorY: 0.5,
x: 12,
y: -8
});
var soldierMouth = self.attachAsset('soldierMouth', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: 8
});
self.health = 100;
self.maxHealth = 100;
self.speed = 2.5;
self.reward = 10;
// Add shield for soldiers after 2 bosses killed
self.hasShield = false;
self.shieldGraphics = null;
// Track bomb throws - soldiers can throw 1 bomb initially, then 2 more after 1 second
self.bombsThrown = 0;
self.maxBombs = 1;
self.bombDelayTimer = 0;
self.hasUnlockedExtraBombs = false;
if (bossKillCount >= 2 && Math.random() < 0.6) {
// 60% chance for shield after 2 bosses
self.hasShield = true;
self.shieldGraphics = self.attachAsset('soldierShield', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: 0,
alpha: 0.8
});
self.health = 150; // Increased health with shield
self.maxHealth = 150;
}
} else if (type === 'tank') {
self.attachAsset('tank', {
anchorX: 0.5,
anchorY: 0.5
});
var tankNozzle = self.attachAsset('tankNozzle', {
anchorX: 0,
anchorY: 0.5,
x: 40,
y: 0
});
self.health = 250;
self.maxHealth = 250;
self.speed = 2;
self.reward = 15;
} else if (type === 'runner') {
var runnerWingLeft = self.attachAsset('runnerWingLeft', {
anchorX: 0.5,
anchorY: 0.5,
x: -28,
y: 0,
rotation: 0
});
var runnerWingRight = self.attachAsset('runnerWingRight', {
anchorX: 0.5,
anchorY: 0.5,
x: 28,
y: 0,
rotation: 0
});
self.attachAsset('runner', {
anchorX: 0.5,
anchorY: 0.5
});
// Add wing flapping animation to make wings more visible
self.wingFlapTimer = 0;
tween(runnerWingLeft, {
rotation: -0.3
}, {
duration: 300,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(runnerWingLeft, {
rotation: 0.3
}, {
duration: 300,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(runnerWingLeft, {
rotation: 0
}, {
duration: 300,
easing: tween.easeInOut
});
}
});
}
});
tween(runnerWingRight, {
rotation: 0.3
}, {
duration: 300,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(runnerWingRight, {
rotation: -0.3
}, {
duration: 300,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(runnerWingRight, {
rotation: 0
}, {
duration: 300,
easing: tween.easeInOut
});
}
});
}
});
var runnerEyeLeft = self.attachAsset('runnerEyeLeft', {
anchorX: 0.5,
anchorY: 0.5,
x: -8,
y: -6
});
var runnerEyeRight = self.attachAsset('runnerEyeRight', {
anchorX: 0.5,
anchorY: 0.5,
x: 8,
y: -6
});
self.health = 50;
self.maxHealth = 50;
self.speed = 4;
self.reward = 5;
} else if (type === 'boss') {
self.attachAsset('boss', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 1.5
});
var bossEyeLeft = self.attachAsset('bossEyeLeft', {
anchorX: 0.5,
anchorY: 0.5,
x: -18,
y: -12
});
var bossEyeRight = self.attachAsset('bossEyeRight', {
anchorX: 0.5,
anchorY: 0.5,
x: 18,
y: -12
});
var bossMouth = self.attachAsset('bossMouth', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: 12
});
var bossHealth = 500 + bossKillCount * 200;
self.health = bossHealth;
self.maxHealth = bossHealth;
self.speed = 1;
self.reward = 200;
}
// Create HP display text for enemy
self.hpText = new Text2(self.health + ' HP', {
size: 20,
fill: '#ff0000'
});
self.hpText.anchor.set(0.5, 0.5);
self.hpText.x = 0;
self.hpText.y = -40;
self.addChild(self.hpText);
// Add attack cooldown and special abilities
self.attackCooldown = 0;
self.specialAbilityCooldown = 0;
self.update = function () {
self.move();
// Update HP display
self.hpText.setText(self.health + ' HP');
// Reduce cooldowns
if (self.attackCooldown > 0) {
self.attackCooldown--;
}
if (self.specialAbilityCooldown > 0) {
self.specialAbilityCooldown--;
}
// Special abilities for different enemy types
if (self.type === 'soldier') {
// Update bomb delay timer to unlock additional bombs after 0.5 second
if (!self.hasUnlockedExtraBombs) {
self.bombDelayTimer++;
if (self.bombDelayTimer >= 30) {
// 0.5 second at 60 FPS
self.maxBombs = 3; // Allow 2 more bombs (3 total)
self.hasUnlockedExtraBombs = true;
}
}
self.throwBomb();
} else if (self.type === 'tank' && self.attackCooldown <= 0) {
self.fireTower();
self.attackCooldown = 120; // 2 seconds
} else if (self.type === 'boss' && self.attackCooldown <= 0) {
self.fireMissile();
self.attackCooldown = 90; // 1.5 seconds
}
// Continuous wing flapping for runners to make wings more visible
if (self.type === 'runner') {
self.wingFlapTimer++;
if (self.wingFlapTimer >= 90) {
// Every 1.5 seconds
var wingLeft = self.children[0]; // runnerWingLeft
var wingRight = self.children[1]; // runnerWingRight
tween(wingLeft, {
rotation: -0.4
}, {
duration: 200,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(wingLeft, {
rotation: 0.4
}, {
duration: 200,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(wingLeft, {
rotation: 0
}, {
duration: 200,
easing: tween.easeInOut
});
}
});
}
});
tween(wingRight, {
rotation: 0.4
}, {
duration: 200,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(wingRight, {
rotation: -0.4
}, {
duration: 200,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(wingRight, {
rotation: 0
}, {
duration: 200,
easing: tween.easeInOut
});
}
});
}
});
self.wingFlapTimer = 0;
}
}
};
self.move = function () {
if (self.pathIndex >= path.length - 1) {
self.reachedEnd();
return;
}
// Check for tower collisions and damage them
for (var i = 0; i < towers.length; i++) {
var tower = towers[i];
var distance = Math.sqrt(Math.pow(tower.x - self.x, 2) + Math.pow(tower.y - self.y, 2));
if (distance < 50) {
// Collision distance
var damage = 0;
if (self.type === 'runner') {
damage = 20;
} else if (self.type === 'soldier') {
damage = 35;
} else if (self.type === 'tank') {
damage = 45;
} else if (self.type === 'boss') {
damage = 70;
}
// Apply damage to shield first if active
if (tower.shieldProtection && tower.shieldHp > 0) {
var shieldDamage = Math.min(damage, tower.shieldHp);
tower.shieldHp -= shieldDamage;
damage -= shieldDamage;
// Flash shield when taking damage
if (tower.shieldGraphics) {
tween(tower.shieldGraphics, {
tint: 0xff0000
}, {
duration: 150,
onFinish: function onFinish() {
if (tower.shieldGraphics) {
tween(tower.shieldGraphics, {
tint: 0xffffff
}, {
duration: 150
});
}
}
});
}
// Remove shield when depleted
if (tower.shieldHp <= 0) {
tower.shieldProtection = false;
if (tower.shieldGraphics) {
tween(tower.shieldGraphics, {
alpha: 0,
scaleX: 2,
scaleY: 2
}, {
duration: 300,
onFinish: function onFinish() {
if (tower.shieldGraphics) {
tower.shieldGraphics.destroy();
tower.shieldGraphics = null;
}
}
});
}
}
}
// Apply remaining damage to tower HP
tower.hp -= damage;
if (tower.hp <= 0) {
// Remove tower from game
for (var j = towers.length - 1; j >= 0; j--) {
if (towers[j] === tower) {
towers.splice(j, 1);
break;
}
}
tower.destroy();
}
// Enemy dies after attacking tower
self.die();
return;
}
}
var targetPoint = path[self.pathIndex + 1];
var dx = targetPoint.x - self.x;
var dy = targetPoint.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 5) {
self.pathIndex++;
self.lastPathIndex = self.pathIndex;
} else {
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
}
};
self.takeDamage = function (damage) {
// Shield absorbs damage for soldiers
if (self.type === 'soldier' && self.hasShield && self.shieldGraphics) {
// Shield absorbs 50% of damage
var actualDamage = Math.ceil(damage * 0.5);
self.health -= actualDamage;
// Flash shield when taking damage
tween(self.shieldGraphics, {
tint: 0xff0000
}, {
duration: 150,
onFinish: function onFinish() {
tween(self.shieldGraphics, {
tint: 0xffffff
}, {
duration: 150
});
}
});
// Remove shield when health gets low
if (self.health <= 50 && self.shieldGraphics) {
// Shield breaks animation
tween(self.shieldGraphics, {
alpha: 0,
scaleX: 2,
scaleY: 2
}, {
duration: 300,
onFinish: function onFinish() {
if (self.shieldGraphics) {
self.shieldGraphics.destroy();
self.shieldGraphics = null;
}
self.hasShield = false;
}
});
}
} else {
self.health -= damage;
}
if (self.health <= 0) {
self.die();
}
};
self.blastTowers = function (blastRadius, blastDamage) {
// Create visual blast effect
var blastEffect = LK.getAsset('soldier', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 0.5,
alpha: 0.8
});
blastEffect.x = self.x;
blastEffect.y = self.y;
blastEffect.tint = 0xff4444;
game.addChild(blastEffect);
// Animate blast expanding
tween(blastEffect, {
scaleX: 3,
scaleY: 3,
alpha: 0
}, {
duration: 500,
easing: tween.easeOut,
onFinish: function onFinish() {
blastEffect.destroy();
}
});
// Damage towers within blast radius
for (var i = 0; i < towers.length; i++) {
var tower = towers[i];
var distance = Math.sqrt(Math.pow(tower.x - self.x, 2) + Math.pow(tower.y - self.y, 2));
if (distance <= blastRadius) {
var damage = blastDamage;
// Apply damage to shield first if active
if (tower.shieldProtection && tower.shieldHp > 0) {
var shieldDamage = Math.min(damage, tower.shieldHp);
tower.shieldHp -= shieldDamage;
damage -= shieldDamage;
// Flash shield when taking damage
if (tower.shieldGraphics) {
tween(tower.shieldGraphics, {
tint: 0xff0000
}, {
duration: 150,
onFinish: function onFinish() {
if (tower.shieldGraphics) {
tween(tower.shieldGraphics, {
tint: 0xffffff
}, {
duration: 150
});
}
}
});
}
// Remove shield when depleted
if (tower.shieldHp <= 0) {
tower.shieldProtection = false;
if (tower.shieldGraphics) {
tween(tower.shieldGraphics, {
alpha: 0,
scaleX: 2,
scaleY: 2
}, {
duration: 300,
onFinish: function onFinish() {
if (tower.shieldGraphics) {
tower.shieldGraphics.destroy();
tower.shieldGraphics = null;
}
}
});
}
}
}
// Apply remaining damage to tower HP
tower.hp -= damage;
// Flash tower red to show damage
tween(tower, {
tint: 0xff0000
}, {
duration: 200,
onFinish: function onFinish() {
tween(tower, {
tint: 0xffffff
}, {
duration: 200
});
}
});
if (tower.hp <= 0) {
// Remove tower from game
for (var j = towers.length - 1; j >= 0; j--) {
if (towers[j] === tower) {
towers.splice(j, 1);
break;
}
}
tower.destroy();
}
}
}
};
self.die = function () {
if (self.type === 'boss') {
bossKillCount++;
}
// Drop chances for different enemy types
var dropChance = Math.random();
var shouldDrop = false;
var dropMultiplier = 1;
// Every wave special item drops guaranteed
if (waveSpecialItemDropped === false) {
shouldDrop = true;
waveSpecialItemDropped = true;
}
// Boss kills drop 2 special items
if (self.type === 'boss') {
shouldDrop = true;
dropMultiplier = 2;
}
// Regular drop chances (if not already dropping from wave/boss)
if (!shouldDrop) {
if (self.type === 'runner' && dropChance < 0.008) {
shouldDrop = true;
} else if (self.type === 'soldier' && dropChance < 0.002) {
shouldDrop = true;
} else if (self.type === 'tank' && dropChance < 0.0004) {
shouldDrop = true;
}
}
if (shouldDrop) {
for (var dropCount = 0; dropCount < dropMultiplier; dropCount++) {
var itemType = ['bomb', 'shield', 'missile'][Math.floor(Math.random() * 3)];
var specialItem;
if (itemType === 'bomb') {
specialItem = LK.getAsset('bombProjectile', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 1.2
});
// Add continuous round pulsing effect to make bomb more visible
var _bombPulse = function bombPulse() {
if (specialItem && !specialItem.destroyed) {
tween(specialItem, {
scaleX: 1.0,
scaleY: 1.0,
alpha: 0.7
}, {
duration: 600,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (specialItem && !specialItem.destroyed) {
tween(specialItem, {
scaleX: 0.8,
scaleY: 0.8,
alpha: 1
}, {
duration: 600,
easing: tween.easeInOut,
onFinish: _bombPulse
});
}
}
});
}
};
_bombPulse();
} else if (itemType === 'shield') {
specialItem = LK.getAsset('soldierShield', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.8
});
specialItem.tint = 0x4a90e2;
// Add continuous glowing effect to make shield more visible
var _shieldGlow = function shieldGlow() {
if (specialItem && !specialItem.destroyed) {
tween(specialItem, {
tint: 0x00ccff,
alpha: 0.8,
scaleX: 0.9,
scaleY: 0.9
}, {
duration: 700,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (specialItem && !specialItem.destroyed) {
tween(specialItem, {
tint: 0x4a90e2,
alpha: 1,
scaleX: 0.8,
scaleY: 0.8
}, {
duration: 700,
easing: tween.easeInOut,
onFinish: _shieldGlow
});
}
}
});
}
};
_shieldGlow();
} else if (itemType === 'missile') {
// Create round missile base
specialItem = LK.getAsset('boss', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.7,
scaleY: 0.7
});
specialItem.tint = 0xff4444; // Red missile base
// Add missile tip (smaller round element)
var tip = specialItem.attachAsset('runner', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.4,
scaleY: 0.4,
x: 0,
y: -20
});
tip.tint = 0xffaa00; // Orange tip color
// Add continuous missile glow effect
var _missileGlow = function missileGlow() {
if (specialItem && !specialItem.destroyed) {
tween(specialItem, {
tint: 0xff0000,
scaleX: 0.8,
scaleY: 0.8,
alpha: 0.9
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (specialItem && !specialItem.destroyed) {
tween(specialItem, {
tint: 0xff4444,
scaleX: 0.7,
scaleY: 0.7,
alpha: 1
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: _missileGlow
});
}
}
});
}
};
_missileGlow();
}
// Position item vertically beside cannon (left side)
var cannonX = 500; // Cannon button X position
var baseY = 2400; // Above cannon button
specialItem.x = cannonX - 150; // Left side of cannon
specialItem.y = baseY - specialItems.length * 80; // Stack vertically
// Add name text below special item
var nameText = new Text2(itemType.charAt(0).toUpperCase() + itemType.slice(1), {
size: 25,
fill: '#ffffff'
});
nameText.anchor.set(0.5, 0.5);
nameText.x = 0;
nameText.y = 45;
specialItem.addChild(nameText);
// Store item type and dragging state
specialItem.itemType = itemType;
specialItem.isDragging = false;
specialItem.startDragX = 0;
specialItem.startDragY = 0;
// Add to special items array for tracking
specialItems.push(specialItem);
game.addChild(specialItem);
// Add down handler for dragging special items
specialItem.down = function (localX, localY, obj) {
this.isDragging = true;
this.startDragX = localX;
this.startDragY = localY;
this.alpha = 0.7; // Make slightly transparent while dragging
};
// Animate special item with gentle floating effect
tween(specialItem, {
y: specialItem.y - 10,
alpha: 0.9
}, {
duration: 1000,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(specialItem, {
y: specialItem.y + 10,
alpha: 1
}, {
duration: 1000,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Loop the floating animation
if (specialItem && !specialItem.destroyed) {
tween(specialItem, {
y: specialItem.y - 10,
alpha: 0.9
}, {
duration: 1000,
easing: tween.easeInOut
});
}
}
});
}
});
}
}
// Special death effects for different enemy types
if (self.type === 'runner') {
// Runner blasts with smaller radius and damage
self.blastTowers(120, 15);
} else if (self.type === 'soldier') {
// Soldier blasts with larger radius and damage
self.blastTowers(150, 25);
} else if (self.type === 'boss') {
// Boss blasts with massive radius and damage
self.blastTowers(200, 60);
}
money += self.reward;
moneyText.setText('Money: ' + money);
for (var i = enemies.length - 1; i >= 0; i--) {
if (enemies[i] === self) {
enemies.splice(i, 1);
break;
}
}
self.destroy();
LK.getSound('enemyHit').play();
};
self.throwBomb = function () {
if (self.type !== 'soldier') return;
// Check if soldier has already thrown maximum bombs
if (self.bombsThrown >= self.maxBombs) return;
// Find nearest tower to throw bomb at
var nearestTower = null;
var nearestDistance = Infinity;
for (var i = 0; i < towers.length; i++) {
var tower = towers[i];
var distance = Math.sqrt(Math.pow(tower.x - self.x, 2) + Math.pow(tower.y - self.y, 2));
if (distance < nearestDistance && distance <= 300) {
// 300 pixel throw range
nearestTower = tower;
nearestDistance = distance;
}
}
if (nearestTower) {
// Increment bomb throw counter
self.bombsThrown++;
// Create bomb projectile
var bomb = LK.getAsset('bombProjectile', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.0,
scaleY: 1.0
});
bomb.x = self.x;
bomb.y = self.y;
// Add pulsing effect to flying bomb
tween(bomb, {
scaleX: 0.6,
scaleY: 0.6
}, {
duration: 400,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(bomb, {
scaleX: 0.5,
scaleY: 0.5
}, {
duration: 400,
easing: tween.easeInOut
});
}
});
game.addChild(bomb);
// Animate bomb flying to tower
tween(bomb, {
x: nearestTower.x,
y: nearestTower.y
}, {
duration: 800,
easing: tween.easeOut,
onFinish: function onFinish() {
// Explode at tower location
var blastEffect = LK.getAsset('soldier', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 0.5,
alpha: 0.8
});
blastEffect.x = bomb.x;
blastEffect.y = bomb.y;
blastEffect.tint = 0xff8800;
game.addChild(blastEffect);
// Animate explosion
tween(blastEffect, {
scaleX: 2.5,
scaleY: 2.5,
alpha: 0
}, {
duration: 400,
easing: tween.easeOut,
onFinish: function onFinish() {
blastEffect.destroy();
}
});
// Damage towers in blast radius
for (var j = 0; j < towers.length; j++) {
var tower = towers[j];
var distance = Math.sqrt(Math.pow(tower.x - bomb.x, 2) + Math.pow(tower.y - bomb.y, 2));
if (distance <= 100) {
// Bomb blast radius
var damage = 30;
// Apply damage to shield first if active
if (tower.shieldProtection && tower.shieldHp > 0) {
var shieldDamage = Math.min(damage, tower.shieldHp);
tower.shieldHp -= shieldDamage;
damage -= shieldDamage;
// Flash shield when taking damage
if (tower.shieldGraphics) {
tween(tower.shieldGraphics, {
tint: 0xff0000
}, {
duration: 150,
onFinish: function onFinish() {
if (tower.shieldGraphics) {
tween(tower.shieldGraphics, {
tint: 0xffffff
}, {
duration: 150
});
}
}
});
}
// Remove shield when depleted
if (tower.shieldHp <= 0) {
tower.shieldProtection = false;
if (tower.shieldGraphics) {
tween(tower.shieldGraphics, {
alpha: 0,
scaleX: 2,
scaleY: 2
}, {
duration: 300,
onFinish: function onFinish() {
if (tower.shieldGraphics) {
tower.shieldGraphics.destroy();
tower.shieldGraphics = null;
}
}
});
}
}
}
// Apply remaining damage to tower HP
tower.hp -= damage;
// Flash tower red
tween(tower, {
tint: 0xff0000
}, {
duration: 200,
onFinish: function onFinish() {
tween(tower, {
tint: 0xffffff
}, {
duration: 200
});
}
});
if (tower.hp <= 0) {
// Remove tower from game
for (var k = towers.length - 1; k >= 0; k--) {
if (towers[k] === tower) {
towers.splice(k, 1);
break;
}
}
tower.destroy();
}
}
}
bomb.destroy();
}
});
}
};
self.fireTower = function () {
if (self.type !== 'tank') return;
// Find nearest tower to fire at
var nearestTower = null;
var nearestDistance = Infinity;
for (var i = 0; i < towers.length; i++) {
var tower = towers[i];
var distance = Math.sqrt(Math.pow(tower.x - self.x, 2) + Math.pow(tower.y - self.y, 2));
if (distance < nearestDistance && distance <= 400) {
// 400 pixel firing range
nearestTower = tower;
nearestDistance = distance;
}
}
if (nearestTower) {
// Create tank projectile
var tankBullet = LK.getAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2,
scaleY: 2
});
tankBullet.x = self.x;
tankBullet.y = self.y;
tankBullet.tint = 0x8b572a;
game.addChild(tankBullet);
// Animate bullet flying to tower
tween(tankBullet, {
x: nearestTower.x,
y: nearestTower.y
}, {
duration: 600,
easing: tween.linear,
onFinish: function onFinish() {
// Damage the tower
var damage = 40;
// Apply damage to shield first if active
if (nearestTower.shieldProtection && nearestTower.shieldHp > 0) {
var shieldDamage = Math.min(damage, nearestTower.shieldHp);
nearestTower.shieldHp -= shieldDamage;
damage -= shieldDamage;
// Flash shield when taking damage
if (nearestTower.shieldGraphics) {
tween(nearestTower.shieldGraphics, {
tint: 0xff0000
}, {
duration: 150,
onFinish: function onFinish() {
if (nearestTower.shieldGraphics) {
tween(nearestTower.shieldGraphics, {
tint: 0xffffff
}, {
duration: 150
});
}
}
});
}
// Remove shield when depleted
if (nearestTower.shieldHp <= 0) {
nearestTower.shieldProtection = false;
if (nearestTower.shieldGraphics) {
tween(nearestTower.shieldGraphics, {
alpha: 0,
scaleX: 2,
scaleY: 2
}, {
duration: 300,
onFinish: function onFinish() {
if (nearestTower.shieldGraphics) {
nearestTower.shieldGraphics.destroy();
nearestTower.shieldGraphics = null;
}
}
});
}
}
}
// Apply remaining damage to tower HP
nearestTower.hp -= damage;
// Flash tower red
tween(nearestTower, {
tint: 0xff0000
}, {
duration: 200,
onFinish: function onFinish() {
tween(nearestTower, {
tint: 0xffffff
}, {
duration: 200
});
}
});
if (nearestTower.hp <= 0) {
// Remove tower from game
for (var j = towers.length - 1; j >= 0; j--) {
if (towers[j] === nearestTower) {
towers.splice(j, 1);
break;
}
}
nearestTower.destroy();
}
tankBullet.destroy();
}
});
}
};
self.fireMissile = function () {
if (self.type !== 'boss') return;
// Find nearest tower to fire missile at
var nearestTower = null;
var nearestDistance = Infinity;
for (var i = 0; i < towers.length; i++) {
var tower = towers[i];
var distance = Math.sqrt(Math.pow(tower.x - self.x, 2) + Math.pow(tower.y - self.y, 2));
if (distance < nearestDistance && distance <= 500) {
// 500 pixel missile range
nearestTower = tower;
nearestDistance = distance;
}
}
if (nearestTower) {
// Create boss missile
var missile = LK.getAsset('boss', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.4,
scaleY: 0.4
});
missile.x = self.x;
missile.y = self.y;
missile.tint = 0xff0000;
game.addChild(missile);
// Animate missile flying to tower
tween(missile, {
x: nearestTower.x,
y: nearestTower.y
}, {
duration: 1000,
easing: tween.easeOut,
onFinish: function onFinish() {
// Create explosion effect
var explosionEffect = LK.getAsset('boss', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.6,
scaleY: 0.6,
alpha: 0.9
});
explosionEffect.x = missile.x;
explosionEffect.y = missile.y;
explosionEffect.tint = 0xffaa00;
game.addChild(explosionEffect);
// Animate explosion
tween(explosionEffect, {
scaleX: 3,
scaleY: 3,
alpha: 0
}, {
duration: 600,
easing: tween.easeOut,
onFinish: function onFinish() {
explosionEffect.destroy();
}
});
// Damage towers in missile blast radius
for (var j = 0; j < towers.length; j++) {
var tower = towers[j];
var distance = Math.sqrt(Math.pow(tower.x - missile.x, 2) + Math.pow(tower.y - missile.y, 2));
if (distance <= 120) {
// Missile blast radius
var damage = 50;
// Apply damage to shield first if active
if (tower.shieldProtection && tower.shieldHp > 0) {
var shieldDamage = Math.min(damage, tower.shieldHp);
tower.shieldHp -= shieldDamage;
damage -= shieldDamage;
// Flash shield when taking damage
if (tower.shieldGraphics) {
tween(tower.shieldGraphics, {
tint: 0xff0000
}, {
duration: 150,
onFinish: function onFinish() {
if (tower.shieldGraphics) {
tween(tower.shieldGraphics, {
tint: 0xffffff
}, {
duration: 150
});
}
}
});
}
// Remove shield when depleted
if (tower.shieldHp <= 0) {
tower.shieldProtection = false;
if (tower.shieldGraphics) {
tween(tower.shieldGraphics, {
alpha: 0,
scaleX: 2,
scaleY: 2
}, {
duration: 300,
onFinish: function onFinish() {
if (tower.shieldGraphics) {
tower.shieldGraphics.destroy();
tower.shieldGraphics = null;
}
}
});
}
}
}
// Apply remaining damage to tower HP
tower.hp -= damage;
// Flash tower red
tween(tower, {
tint: 0xff0000
}, {
duration: 200,
onFinish: function onFinish() {
tween(tower, {
tint: 0xffffff
}, {
duration: 200
});
}
});
if (tower.hp <= 0) {
// Remove tower from game
for (var k = towers.length - 1; k >= 0; k--) {
if (towers[k] === tower) {
towers.splice(k, 1);
break;
}
}
tower.destroy();
}
}
}
missile.destroy();
}
});
}
};
self.reachedEnd = function () {
lives--;
livesText.setText('Lives: ' + lives);
for (var i = enemies.length - 1; i >= 0; i--) {
if (enemies[i] === self) {
enemies.splice(i, 1);
break;
}
}
self.destroy();
if (lives <= 0) {
LK.showGameOver();
}
};
return self;
});
var Tower = Container.expand(function (type, x, y) {
var self = Container.call(this);
self.type = type;
self.x = x;
self.y = y;
self.range = 200;
self.shootCooldown = 0;
self.target = null;
if (type === 'cannon') {
self.attachAsset('cannon', {
anchorX: 0.5,
anchorY: 0.5
});
self.damage = 30;
self.fireRate = 60;
self.range = 250;
self.hp = 190;
self.maxHp = 190;
// Missile usage tracking
self.missileUsed = false;
self.missileSecondTime = false;
self.missileTimer = 0;
} else if (type === 'sniper') {
self.attachAsset('sniper', {
anchorX: 0.5,
anchorY: 0.5
});
var sniperTriangle1 = self.attachAsset('sniperTriangle1', {
anchorX: 0.5,
anchorY: 0.5,
x: -15,
y: -10,
rotation: Math.PI / 4
});
var sniperTriangle2 = self.attachAsset('sniperTriangle2', {
anchorX: 0.5,
anchorY: 0.5,
x: 15,
y: -10,
rotation: -Math.PI / 4
});
self.damage = 80;
self.fireRate = 120;
self.range = 350;
self.hp = 240;
self.maxHp = 240;
} else if (type === 'laser') {
self.attachAsset('laser', {
anchorX: 0.5,
anchorY: 0.5
});
self.damage = 15;
self.fireRate = 20;
self.hp = 150;
self.maxHp = 150;
}
// Create HP display text
self.hpText = new Text2(self.hp + ' HP', {
size: 25,
fill: '#ffffff'
});
self.hpText.anchor.set(0.5, 0.5);
self.hpText.x = 0;
self.hpText.y = -60;
self.addChild(self.hpText);
// Add HP regeneration timer
self.hpRegenTimer = 0;
if (type === 'laser') {
self.hpRegenRate = 120; // 2 seconds at 60 FPS
} else if (type === 'cannon') {
self.hpRegenRate = 165; // 2.75 seconds at 60 FPS
} else if (type === 'sniper') {
self.hpRegenRate = 225; // 3.75 seconds at 60 FPS
}
self.hpRegenAmount = Math.ceil(self.maxHp * 0.1); // Regenerate 10% of max HP
// Special item enhancement properties
self.bombEnhanced = false;
self.bombEnhancedTimer = 0;
self.shieldEnhanced = false;
self.shieldEnhancedTimer = 0;
self.shieldProtection = false;
self.shieldGraphics = null;
self.shieldHp = 0;
self.shieldMaxHp = 70;
self.missileEnhanced = false;
self.missileEnhancedTimer = 0;
// Bomb usage tracking - towers can throw only one bomb initially
self.bombUsed = false;
self.bombUsageTimer = 0;
self.bombSecondTime = false;
self.update = function () {
if (self.shootCooldown > 0) {
self.shootCooldown--;
}
self.findTarget();
if (self.target && self.shootCooldown <= 0) {
self.shoot();
self.shootCooldown = self.fireRate;
}
// HP regeneration logic
if (self.hp < self.maxHp) {
self.hpRegenTimer++;
if (self.hpRegenTimer >= self.hpRegenRate) {
self.hp = Math.min(self.hp + self.hpRegenAmount, self.maxHp);
self.hpRegenTimer = 0;
// Visual regeneration effect
tween(self, {
tint: 0x00ff00
}, {
duration: 200,
onFinish: function onFinish() {
tween(self, {
tint: 0xffffff
}, {
duration: 200
});
}
});
}
}
// Update missile timer
if (self.missileTimer > 0) {
self.missileTimer--;
if (self.missileTimer <= 0 && self.missileUsed && !self.missileSecondTime) {
self.missileSecondTime = true; // Unlock second missile usage
}
}
// Update bomb usage timer
if (self.bombUsageTimer > 0) {
self.bombUsageTimer--;
if (self.bombUsageTimer <= 0 && self.bombUsed && !self.bombSecondTime) {
self.bombSecondTime = true; // Unlock second bomb usage
}
}
// Update HP display
if (self.shieldProtection && self.shieldHp > 0) {
self.hpText.setText(self.hp + ' HP + ' + self.shieldHp + ' Shield');
} else {
self.hpText.setText(self.hp + ' HP');
}
};
self.findTarget = function () {
var closestEnemy = null;
var closestDistance = Infinity;
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
var distance = Math.sqrt(Math.pow(enemy.x - self.x, 2) + Math.pow(enemy.y - self.y, 2));
if (distance <= self.range && distance < closestDistance) {
closestEnemy = enemy;
closestDistance = distance;
}
}
self.target = closestEnemy;
};
self.shoot = function () {
if (self.target) {
var finalDamage = self.damage;
// Check if tower has special item enhancements active
if (self.bombEnhanced && self.bombEnhancedTimer > 0) {
finalDamage = Math.floor(finalDamage * 1.5); // 50% damage boost from bomb
// Create bomb projectile that flies to enemy
var bomb = LK.getAsset('bombProjectile', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.0,
scaleY: 1.0
});
bomb.x = self.x;
bomb.y = self.y;
game.addChild(bomb);
// Add pulsing effect to flying bomb
tween(bomb, {
scaleX: 0.7,
scaleY: 0.7
}, {
duration: 300,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(bomb, {
scaleX: 0.6,
scaleY: 0.6
}, {
duration: 300,
easing: tween.easeInOut
});
}
});
// Animate bomb flying to target enemy
tween(bomb, {
x: self.target.x,
y: self.target.y
}, {
duration: 600,
easing: tween.easeOut,
onFinish: function onFinish() {
// Apply damage to target
if (self.target && !self.target.destroyed) {
self.target.takeDamage(finalDamage);
}
// Create explosion effect at target location
var explosionEffect = LK.getAsset('soldier', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 0.5,
alpha: 0.8
});
explosionEffect.x = bomb.x;
explosionEffect.y = bomb.y;
explosionEffect.tint = 0xff8800;
game.addChild(explosionEffect);
// Animate explosion
tween(explosionEffect, {
scaleX: 2.0,
scaleY: 2.0,
alpha: 0
}, {
duration: 400,
easing: tween.easeOut,
onFinish: function onFinish() {
explosionEffect.destroy();
}
});
bomb.destroy();
}
});
self.bombEnhancedTimer--;
if (self.bombEnhancedTimer <= 0) {
self.bombEnhanced = false;
}
} else if (self.missileEnhanced && self.missileEnhancedTimer > 0) {
// Missile enhancement: chance for explosive bullets
if (Math.random() < 0.3) {
// 30% chance for explosive shot
finalDamage = Math.floor(finalDamage * 2); // Double damage for explosive shot
// Visual effect for explosive bullet
var explosiveBullet = new Bullet(self.x, self.y, self.target, finalDamage);
explosiveBullet.isExplosive = true;
bullets.push(explosiveBullet);
game.addChild(explosiveBullet);
} else {
var bullet = new Bullet(self.x, self.y, self.target, finalDamage);
bullets.push(bullet);
game.addChild(bullet);
}
self.missileEnhancedTimer--;
if (self.missileEnhancedTimer <= 0) {
self.missileEnhanced = false;
}
} else if (self.type === 'cannon') {
// Cannon missile usage logic: 1 missile initially, then after 1 second cooldown, normal damage
if (!self.missileUsed) {
// First missile - use immediately
self.launchMissile();
self.missileUsed = true;
self.missileTimer = 60; // 1 second cooldown at 60 FPS
} else if (self.missileSecondTime && self.missileTimer <= 0) {
// Second time - can use missile again but then revert to normal damage
self.launchMissile();
self.missileSecondTime = false;
} else {
var bullet = new Bullet(self.x, self.y, self.target, finalDamage);
bullets.push(bullet);
game.addChild(bullet);
}
} else {
// Bomb usage logic: 1 bomb initially, then after 1 second cooldown, normal shooting
if (!self.bombUsed) {
// First bomb - use immediately
self.throwBomb();
self.bombUsed = true;
self.bombUsageTimer = 60; // 1 second cooldown at 60 FPS
} else if (self.bombSecondTime && self.bombUsageTimer <= 0) {
// Second time - can use bomb again but then revert to normal shooting
self.throwBomb();
self.bombSecondTime = false;
} else {
var bullet = new Bullet(self.x, self.y, self.target, finalDamage);
bullets.push(bullet);
game.addChild(bullet);
}
}
LK.getSound('shoot').play();
}
};
self.updateFireArrow = function () {
// Arrow display disabled
};
self.launchMissile = function () {
if (self.target) {
var finalDamage = self.damage; // Missiles do normal damage
// Create missile projectile
var missile = LK.getAsset('boss', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 0.5
});
missile.x = self.x;
missile.y = self.y;
missile.tint = 0xff4444; // Red missile color
game.addChild(missile);
// Add missile trail effect
var trail = LK.getAsset('runner', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.3,
scaleY: 0.3,
alpha: 0.7
});
trail.x = self.x;
trail.y = self.y;
trail.tint = 0xffaa00; // Orange trail
game.addChild(trail);
// Animate missile flying to target
tween(missile, {
x: self.target.x,
y: self.target.y
}, {
duration: 800,
easing: tween.easeOut,
onFinish: function onFinish() {
// Create large explosion effect
var explosionEffect = LK.getAsset('boss', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.8,
alpha: 0.9
});
explosionEffect.x = missile.x;
explosionEffect.y = missile.y;
explosionEffect.tint = 0xff0000;
game.addChild(explosionEffect);
// Animate explosion
tween(explosionEffect, {
scaleX: 2.5,
scaleY: 2.5,
alpha: 0
}, {
duration: 500,
easing: tween.easeOut,
onFinish: function onFinish() {
explosionEffect.destroy();
}
});
// Apply damage to target
if (self.target && !self.target.destroyed) {
self.target.takeDamage(finalDamage);
}
// Area damage to nearby enemies
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
if (enemy !== self.target) {
var distance = Math.sqrt(Math.pow(enemy.x - missile.x, 2) + Math.pow(enemy.y - missile.y, 2));
if (distance <= 100) {
// Missile blast radius
enemy.takeDamage(Math.floor(finalDamage * 0.5)); // 50% damage to nearby enemies
}
}
}
missile.destroy();
}
});
// Animate trail following missile
tween(trail, {
x: self.target.x,
y: self.target.y,
alpha: 0
}, {
duration: 900,
easing: tween.linear,
onFinish: function onFinish() {
trail.destroy();
}
});
}
};
self.throwBomb = function () {
if (self.target) {
var finalDamage = Math.floor(self.damage * 1.2); // Bombs do 20% more damage
// Create bomb projectile
var bomb = LK.getAsset('bombProjectile', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 1.2
});
bomb.x = self.x;
bomb.y = self.y;
bomb.tint = 0xff8800; // Orange bomb color
game.addChild(bomb);
// Add pulsing effect to flying bomb
tween(bomb, {
scaleX: 0.8,
scaleY: 0.8,
alpha: 0.8
}, {
duration: 400,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(bomb, {
scaleX: 1.0,
scaleY: 1.0,
alpha: 1.0
}, {
duration: 400,
easing: tween.easeInOut
});
}
});
// Animate bomb flying to target with arc trajectory
tween(bomb, {
x: self.target.x,
y: self.target.y
}, {
duration: 700,
easing: tween.easeOut,
onFinish: function onFinish() {
// Create large explosion effect
var explosionEffect = LK.getAsset('soldier', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.6,
scaleY: 0.6,
alpha: 0.9
});
explosionEffect.x = bomb.x;
explosionEffect.y = bomb.y;
explosionEffect.tint = 0xff4444;
game.addChild(explosionEffect);
// Animate explosion
tween(explosionEffect, {
scaleX: 2.8,
scaleY: 2.8,
alpha: 0
}, {
duration: 500,
easing: tween.easeOut,
onFinish: function onFinish() {
explosionEffect.destroy();
}
});
// Apply damage to target
if (self.target && !self.target.destroyed) {
self.target.takeDamage(finalDamage);
}
// Area damage to nearby enemies
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
if (enemy !== self.target) {
var distance = Math.sqrt(Math.pow(enemy.x - bomb.x, 2) + Math.pow(enemy.y - bomb.y, 2));
if (distance <= 120) {
// Bomb blast radius
enemy.takeDamage(Math.floor(finalDamage * 0.6)); // 60% damage to nearby enemies
}
}
}
bomb.destroy();
}
});
}
};
return self;
});
var TowerSpot = Container.expand(function (x, y) {
var self = Container.call(this);
self.attachAsset('towerSpot', {
anchorX: 0.5,
anchorY: 0.5
});
self.x = x;
self.y = y;
self.occupied = false;
self.tower = null;
self.down = function (localX, localY, obj) {
if (!self.occupied && selectedTowerType && canAffordTower(selectedTowerType)) {
self.placeTower(selectedTowerType);
}
};
self.placeTower = function (type) {
var cost = getTowerCost(type);
if (money >= cost) {
money -= cost;
moneyText.setText('Money: ' + money);
var tower = new Tower(type, self.x, self.y);
towers.push(tower);
game.addChild(tower);
self.occupied = true;
self.tower = tower;
self.alpha = 0.3;
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x1a1a2e
});
/****
* Game Code
****/
var money = 350;
var lives = 20;
var wave = 1;
var waveTimer = 0;
var spawnTimer = 0;
var enemiesInWave = 0;
var enemiesSpawned = 0;
var selectedTowerType = null;
var selectedTower = null;
var rangeIndicator = null;
var bossKillCount = 0;
var draggedTower = null;
var draggedTowerType = null;
var isDragging = false;
var towers = [];
var enemies = [];
var bullets = [];
var towerSpots = [];
var specialItems = [];
var waveSpecialItemDropped = false;
var path = [{
x: 1024,
y: 0
}, {
x: 1024,
y: 200
}, {
x: 1600,
y: 200
}, {
x: 1600,
y: 400
}, {
x: 400,
y: 400
}, {
x: 400,
y: 600
}, {
x: 1700,
y: 600
}, {
x: 1700,
y: 800
}, {
x: 300,
y: 800
}, {
x: 300,
y: 1000
}, {
x: 1500,
y: 1000
}, {
x: 1500,
y: 1200
}, {
x: 500,
y: 1200
}, {
x: 500,
y: 1400
}, {
x: 1600,
y: 1400
}, {
x: 1600,
y: 1600
}, {
x: 200,
y: 1600
}, {
x: 200,
y: 1800
}, {
x: 1700,
y: 1800
}, {
x: 1700,
y: 2000
}, {
x: 600,
y: 2000
}, {
x: 600,
y: 2200
}, {
x: 1400,
y: 2200
}, {
x: 1400,
y: 2400
}, {
x: 1024,
y: 2400
}, {
x: 1024,
y: 2600
}];
// Create path visualization
for (var i = 0; i < path.length - 1; i++) {
var pathSegment = LK.getAsset('path', {
anchorX: 0.5,
anchorY: 0.5
});
pathSegment.x = (path[i].x + path[i + 1].x) / 2;
pathSegment.y = (path[i].y + path[i + 1].y) / 2;
var dx = path[i + 1].x - path[i].x;
var dy = path[i + 1].y - path[i].y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (Math.abs(dx) > Math.abs(dy)) {
pathSegment.width = distance;
pathSegment.height = 120;
} else {
pathSegment.width = 120;
pathSegment.height = distance;
}
game.addChild(pathSegment);
}
// UI Elements
var moneyText = new Text2('Money: ' + money, {
size: 60,
fill: '#ffffff'
});
moneyText.anchor.set(0, 0);
LK.gui.topLeft.addChild(moneyText);
moneyText.x = 120;
moneyText.y = 20;
var livesText = new Text2('Lives: ' + lives, {
size: 60,
fill: '#ffffff'
});
livesText.anchor.set(0, 0);
LK.gui.topLeft.addChild(livesText);
livesText.x = 120;
livesText.y = 100;
var waveText = new Text2('Wave: ' + wave, {
size: 60,
fill: '#ffffff'
});
waveText.anchor.set(0, 0);
LK.gui.topLeft.addChild(waveText);
waveText.x = 120;
waveText.y = 180;
// Tower selection buttons
var cannonButton = LK.getAsset('cannon', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.8
});
cannonButton.x = 500;
cannonButton.y = 2500;
game.addChild(cannonButton);
var sniperButton = LK.getAsset('sniper', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.8
});
sniperButton.x = 900;
sniperButton.y = 2500;
game.addChild(sniperButton);
var laserButton = LK.getAsset('laser', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.8
});
laserButton.x = 1300;
laserButton.y = 2500;
game.addChild(laserButton);
var cannonNameText = new Text2('Cannon', {
size: 35,
fill: '#ffffff'
});
cannonNameText.anchor.set(0.5, 0.5);
cannonNameText.x = 500;
cannonNameText.y = 2660;
game.addChild(cannonNameText);
var sniperNameText = new Text2('Sniper', {
size: 35,
fill: '#ffffff'
});
sniperNameText.anchor.set(0.5, 0.5);
sniperNameText.x = 900;
sniperNameText.y = 2660;
game.addChild(sniperNameText);
var laserNameText = new Text2('Laser', {
size: 35,
fill: '#ffffff'
});
laserNameText.anchor.set(0.5, 0.5);
laserNameText.x = 1300;
laserNameText.y = 2660;
game.addChild(laserNameText);
var cannonCostText = new Text2('50', {
size: 40,
fill: '#ffffff'
});
cannonCostText.anchor.set(0.5, 0.5);
cannonCostText.x = 500;
cannonCostText.y = 2620;
game.addChild(cannonCostText);
var sniperCostText = new Text2('100', {
size: 40,
fill: '#ffffff'
});
sniperCostText.anchor.set(0.5, 0.5);
sniperCostText.x = 900;
sniperCostText.y = 2620;
game.addChild(sniperCostText);
var laserCostText = new Text2('30', {
size: 40,
fill: '#ffffff'
});
laserCostText.anchor.set(0.5, 0.5);
laserCostText.x = 1300;
laserCostText.y = 2620;
game.addChild(laserCostText);
function getTowerCost(type) {
if (type === 'cannon') return 50;
if (type === 'sniper') return 100;
if (type === 'laser') return 30;
return 0;
}
function canAffordTower(type) {
return money >= getTowerCost(type);
}
function updateButtonColors() {
cannonButton.tint = canAffordTower('cannon') ? 0xffffff : 0x666666;
sniperButton.tint = canAffordTower('sniper') ? 0xffffff : 0x666666;
laserButton.tint = canAffordTower('laser') ? 0xffffff : 0x666666;
}
function spawnEnemy() {
var enemyType;
if (wave >= 6 && enemiesSpawned === 0) {
// Spawn boss as first enemy after wave 6
enemyType = 'boss';
} else {
var rand = Math.random();
if (rand < 0.4) {
enemyType = 'soldier';
} else if (rand < 0.6) {
enemyType = 'tank';
} else {
enemyType = 'runner';
}
}
var enemy = new Enemy(enemyType);
enemies.push(enemy);
game.addChild(enemy);
enemiesSpawned++;
}
function startWave() {
enemiesInWave = 5 + wave * 2;
enemiesSpawned = 0;
spawnTimer = 0;
waveSpecialItemDropped = false; // Reset special item drop flag for new wave
}
game.move = function (x, y, obj) {
if (isDragging && draggedTowerType) {
// Update range indicator position during drag
if (rangeIndicator) {
rangeIndicator.x = x;
rangeIndicator.y = y;
}
}
// Handle special item dragging
for (var i = 0; i < specialItems.length; i++) {
var item = specialItems[i];
if (item.isDragging) {
item.x = x;
item.y = y;
break;
}
}
};
game.down = function (x, y, obj) {
// Clear previous selection
if (rangeIndicator && !isDragging) {
rangeIndicator.destroy();
rangeIndicator = null;
}
selectedTower = null;
// Check tower button clicks
var buttonSize = 80;
if (y >= 2500 - buttonSize / 2 && y <= 2500 + buttonSize / 2) {
if (x >= 500 - buttonSize / 2 && x <= 500 + buttonSize / 2 && canAffordTower('cannon')) {
draggedTowerType = 'cannon';
isDragging = true;
// Show range indicator for dragged tower
rangeIndicator = LK.getAsset('towerSpot', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 250 / 50,
// cannon range
scaleY: 250 / 50,
alpha: 0.4
});
rangeIndicator.x = x;
rangeIndicator.y = y;
rangeIndicator.tint = 0x4a90e2;
game.addChild(rangeIndicator);
} else if (x >= 900 - buttonSize / 2 && x <= 900 + buttonSize / 2 && canAffordTower('sniper')) {
draggedTowerType = 'sniper';
isDragging = true;
// Show range indicator for dragged tower
rangeIndicator = LK.getAsset('towerSpot', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 350 / 50,
// sniper range
scaleY: 350 / 50,
alpha: 0.4
});
rangeIndicator.x = x;
rangeIndicator.y = y;
rangeIndicator.tint = 0x7ed321;
game.addChild(rangeIndicator);
} else if (x >= 1300 - buttonSize / 2 && x <= 1300 + buttonSize / 2 && canAffordTower('laser')) {
draggedTowerType = 'laser';
isDragging = true;
// Show range indicator for dragged tower
rangeIndicator = LK.getAsset('towerSpot', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 200 / 50,
// laser range
scaleY: 200 / 50,
alpha: 0.4
});
rangeIndicator.x = x;
rangeIndicator.y = y;
rangeIndicator.tint = 0xf5a623;
game.addChild(rangeIndicator);
}
} else {
// Check if clicking on existing tower
var clickedTower = null;
for (var i = 0; i < towers.length; i++) {
var tower = towers[i];
var distance = Math.sqrt(Math.pow(tower.x - x, 2) + Math.pow(tower.y - y, 2));
if (distance <= 40) {
// Tower click radius
clickedTower = tower;
break;
}
}
if (clickedTower) {
// Select tower and show range
selectedTower = clickedTower;
rangeIndicator = LK.getAsset('towerSpot', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: selectedTower.range / 50,
scaleY: selectedTower.range / 50,
alpha: 0.3
});
rangeIndicator.x = selectedTower.x;
rangeIndicator.y = selectedTower.y;
rangeIndicator.tint = 0x00ff00;
game.addChild(rangeIndicator);
}
}
};
game.up = function (x, y, obj) {
if (isDragging && draggedTowerType) {
// Place tower at release position if affordable
var cost = getTowerCost(draggedTowerType);
if (money >= cost) {
money -= cost;
moneyText.setText('Money: ' + money);
var tower = new Tower(draggedTowerType, x, y);
towers.push(tower);
game.addChild(tower);
}
// Clean up dragging state
isDragging = false;
draggedTowerType = null;
if (rangeIndicator) {
rangeIndicator.destroy();
rangeIndicator = null;
}
}
// Handle special item placement on towers
for (var i = 0; i < specialItems.length; i++) {
var item = specialItems[i];
if (item.isDragging) {
var targetTower = null;
// Check if dropped on a tower
for (var j = 0; j < towers.length; j++) {
var tower = towers[j];
var distance = Math.sqrt(Math.pow(tower.x - x, 2) + Math.pow(tower.y - y, 2));
if (distance <= 60) {
// Tower placement radius
targetTower = tower;
break;
}
}
if (targetTower) {
// Apply special item effect to the tower
applySpecialItemToTower(item.itemType, targetTower);
// Remove the special item
specialItems.splice(i, 1);
item.destroy();
// Reposition remaining special items
repositionSpecialItems();
} else {
// Return item to original position if not dropped on tower
var cannonX = 500;
var baseY = 2400;
item.x = cannonX - 150;
item.y = baseY - i * 80;
}
item.isDragging = false;
item.alpha = 1; // Restore full opacity
break;
}
}
};
game.update = function () {
// Update all towers
for (var i = 0; i < towers.length; i++) {
towers[i].update();
}
// Update all enemies
for (var i = 0; i < enemies.length; i++) {
enemies[i].update();
}
// Update all bullets
for (var i = 0; i < bullets.length; i++) {
bullets[i].update();
}
// Wave management - only run when game is started
if (gameStarted) {
if (enemies.length === 0 && enemiesSpawned >= enemiesInWave) {
waveTimer++;
if (waveTimer >= 180) {
// 3 seconds between waves
wave++;
waveText.setText('Wave: ' + wave);
startWave();
waveTimer = 0;
}
}
// Enemy spawning
if (enemiesSpawned < enemiesInWave) {
spawnTimer++;
if (spawnTimer >= 45) {
// Spawn every 0.75 seconds
spawnEnemy();
spawnTimer = 0;
}
}
}
// Update button colors based on affordability
updateButtonColors();
};
// Add down handlers for special items
for (var i = 0; i < specialItems.length; i++) {
var item = specialItems[i];
item.down = function (localX, localY, obj) {
useSpecialItem(this);
};
}
function useSpecialItem(item) {
// Use stored itemType property instead of tint detection
var itemType = item.itemType;
// Apply special item effects
if (itemType === 'bomb') {
// Bomb: Damages all enemies on screen
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
enemy.takeDamage(100);
// Visual explosion effect on each enemy
var explosionEffect = LK.getAsset('soldier', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 0.5,
alpha: 0.8
});
explosionEffect.x = enemy.x;
explosionEffect.y = enemy.y;
explosionEffect.tint = 0xff4444;
game.addChild(explosionEffect);
tween(explosionEffect, {
scaleX: 2,
scaleY: 2,
alpha: 0
}, {
duration: 400,
easing: tween.easeOut,
onFinish: function onFinish() {
explosionEffect.destroy();
}
});
}
} else if (itemType === 'shield') {
// Shield: Heals all towers to full HP
for (var i = 0; i < towers.length; i++) {
var tower = towers[i];
tower.hp = tower.maxHp;
// Visual healing effect
tween(tower, {
tint: 0x00ff00
}, {
duration: 300,
onFinish: function onFinish() {
tween(tower, {
tint: 0xffffff
}, {
duration: 300
});
}
});
}
} else if (itemType === 'missile') {
var _shake = function shake() {
if (shakeTimer < shakeDuration) {
game.x = originalX + (Math.random() - 0.5) * shakeAmount;
game.y = originalY + (Math.random() - 0.5) * shakeAmount;
shakeTimer += 16; // Approximate frame time
LK.setTimeout(_shake, 16);
} else {
game.x = originalX;
game.y = originalY;
}
};
// Missile: Massive damage to all enemies and screen shake effect
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
enemy.takeDamage(120);
// Large missile explosion effect
var missileEffect = LK.getAsset('boss', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.8,
alpha: 0.9
});
missileEffect.x = enemy.x;
missileEffect.y = enemy.y;
missileEffect.tint = 0xff0000;
game.addChild(missileEffect);
tween(missileEffect, {
scaleX: 3,
scaleY: 3,
alpha: 0
}, {
duration: 600,
easing: tween.easeOut,
onFinish: function onFinish() {
missileEffect.destroy();
}
});
}
// Screen shake effect
var originalX = game.x;
var originalY = game.y;
var shakeAmount = 10;
var shakeDuration = 500;
var shakeTimer = 0;
_shake();
}
// Remove used special item
for (var i = specialItems.length - 1; i >= 0; i--) {
if (specialItems[i] === item) {
specialItems.splice(i, 1);
break;
}
}
item.destroy();
// Reposition remaining special items
for (var i = 0; i < specialItems.length; i++) {
var cannonX = 500;
var baseY = 2400;
specialItems[i].y = baseY - i * 80;
}
}
function applySpecialItemToTower(itemType, tower) {
if (itemType === 'bomb') {
// Bomb: Enhanced damage for battle effectiveness
tower.bombEnhanced = true;
tower.bombEnhancedTimer = 600; // 10 seconds at 60 FPS
// Visual effect
tween(tower, {
tint: 0xff4444
}, {
duration: 300,
onFinish: function onFinish() {
tween(tower, {
tint: 0xffffff
}, {
duration: 300
});
}
});
} else if (itemType === 'shield') {
// Shield: Protect tower with 70 HP shield
tower.shieldProtection = true;
tower.shieldHp = tower.shieldMaxHp; // 70 HP shield
// Create visual shield graphics - blue circle around tower
if (!tower.shieldGraphics) {
tower.shieldGraphics = tower.attachAsset('shield', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.6,
scaleX: 1.2,
scaleY: 1.2
});
tower.shieldGraphics.tint = 0x0000ff; // Blue color for shield
}
// Animate shield with pulsing blue effect
tween(tower.shieldGraphics, {
alpha: 0.3,
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 800,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(tower.shieldGraphics, {
alpha: 0.6,
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 800,
easing: tween.easeInOut
});
}
});
// Visual healing effect
tween(tower, {
tint: 0x00ff00
}, {
duration: 300,
onFinish: function onFinish() {
tween(tower, {
tint: 0xffffff
}, {
duration: 300
});
}
});
} else if (itemType === 'missile') {
// Missile: Only works with cannon towers
if (tower.type === 'cannon') {
// Missile: Enhanced explosive shots for battle
tower.missileEnhanced = true;
tower.missileEnhancedTimer = 900; // 15 seconds at 60 FPS
tower.fireRate = Math.floor(tower.fireRate * 0.8); // Slightly faster firing
// Visual effect
tween(tower, {
tint: 0xff0000
}, {
duration: 300,
onFinish: function onFinish() {
tween(tower, {
tint: 0xffffff
}, {
duration: 300
});
}
});
// Reset fire rate after enhancement ends
LK.setTimeout(function () {
if (!tower.missileEnhanced) {
tower.fireRate = Math.floor(tower.fireRate / 0.8); // Reset to original
}
}, 15000);
}
}
}
function repositionSpecialItems() {
for (var i = 0; i < specialItems.length; i++) {
var cannonX = 500;
var baseY = 2400;
specialItems[i].x = cannonX - 150;
specialItems[i].y = baseY - i * 80;
}
}
// Game state management
var gameStarted = false;
var gameStartButton = null;
// Create start button
gameStartButton = LK.getAsset('towerSpot', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2,
scaleY: 2
});
gameStartButton.x = 1024; // Center of screen
gameStartButton.y = 1366; // Center of screen
gameStartButton.tint = 0x00ff00; // Green color
game.addChild(gameStartButton);
// Add start button text
var startButtonText = new Text2('TAP TO START', {
size: 80,
fill: '#ffffff'
});
startButtonText.anchor.set(0.5, 0.5);
startButtonText.x = 0;
startButtonText.y = 0;
gameStartButton.addChild(startButtonText);
// Add pulsing animation to start button
var _startButtonPulse = function startButtonPulse() {
if (gameStartButton && !gameStarted) {
tween(gameStartButton, {
scaleX: 2.2,
scaleY: 2.2,
alpha: 0.8
}, {
duration: 800,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (gameStartButton && !gameStarted) {
tween(gameStartButton, {
scaleX: 2,
scaleY: 2,
alpha: 1
}, {
duration: 800,
easing: tween.easeInOut,
onFinish: _startButtonPulse
});
}
}
});
}
};
_startButtonPulse();
// Add click handler for start button
gameStartButton.down = function (localX, localY, obj) {
if (!gameStarted) {
gameStarted = true;
// Remove start button
gameStartButton.destroy();
gameStartButton = null;
// Start first wave
startWave();
}
};
// Don't start waves automatically anymore - wait for player to tap start /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Bullet = Container.expand(function (startX, startY, target, damage) {
var self = Container.call(this);
self.attachAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.x = startX;
self.y = startY;
self.target = target;
self.damage = damage;
self.speed = 8;
self.update = function () {
if (!self.target || self.target.destroyed) {
self.removeBullet();
return;
}
var dx = self.target.x - self.x;
var dy = self.target.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 10) {
if (self.target && !self.target.destroyed) {
self.target.takeDamage(self.damage);
}
// If this is an explosive bullet, create area damage
if (self.isExplosive) {
// Create explosion effect
var explosionEffect = LK.getAsset('soldier', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.8,
alpha: 0.8
});
explosionEffect.x = self.target.x;
explosionEffect.y = self.target.y;
explosionEffect.tint = 0xff4444;
game.addChild(explosionEffect);
// Animate explosion
tween(explosionEffect, {
scaleX: 2.5,
scaleY: 2.5,
alpha: 0
}, {
duration: 400,
easing: tween.easeOut,
onFinish: function onFinish() {
explosionEffect.destroy();
}
});
// Damage nearby enemies
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
if (enemy !== self.target) {
var expDistance = Math.sqrt(Math.pow(enemy.x - self.target.x, 2) + Math.pow(enemy.y - self.target.y, 2));
if (expDistance <= 80) {
// Explosion radius
enemy.takeDamage(Math.floor(self.damage * 0.6)); // 60% damage to nearby enemies
}
}
}
}
self.removeBullet();
} else {
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
}
};
self.removeBullet = function () {
for (var i = bullets.length - 1; i >= 0; i--) {
if (bullets[i] === self) {
bullets.splice(i, 1);
break;
}
}
self.destroy();
};
return self;
});
var Enemy = Container.expand(function (type) {
var self = Container.call(this);
self.type = type;
self.pathIndex = 0;
self.x = path[0].x;
self.y = path[0].y;
self.lastPathIndex = 0;
if (type === 'soldier') {
self.attachAsset('soldier', {
anchorX: 0.5,
anchorY: 0.5
});
var soldierEyeLeft = self.attachAsset('soldierEyeLeft', {
anchorX: 0.5,
anchorY: 0.5,
x: -12,
y: -8
});
var soldierEyeRight = self.attachAsset('soldierEyeRight', {
anchorX: 0.5,
anchorY: 0.5,
x: 12,
y: -8
});
var soldierMouth = self.attachAsset('soldierMouth', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: 8
});
self.health = 100;
self.maxHealth = 100;
self.speed = 2.5;
self.reward = 10;
// Add shield for soldiers after 2 bosses killed
self.hasShield = false;
self.shieldGraphics = null;
// Track bomb throws - soldiers can throw 1 bomb initially, then 2 more after 1 second
self.bombsThrown = 0;
self.maxBombs = 1;
self.bombDelayTimer = 0;
self.hasUnlockedExtraBombs = false;
if (bossKillCount >= 2 && Math.random() < 0.6) {
// 60% chance for shield after 2 bosses
self.hasShield = true;
self.shieldGraphics = self.attachAsset('soldierShield', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: 0,
alpha: 0.8
});
self.health = 150; // Increased health with shield
self.maxHealth = 150;
}
} else if (type === 'tank') {
self.attachAsset('tank', {
anchorX: 0.5,
anchorY: 0.5
});
var tankNozzle = self.attachAsset('tankNozzle', {
anchorX: 0,
anchorY: 0.5,
x: 40,
y: 0
});
self.health = 250;
self.maxHealth = 250;
self.speed = 2;
self.reward = 15;
} else if (type === 'runner') {
var runnerWingLeft = self.attachAsset('runnerWingLeft', {
anchorX: 0.5,
anchorY: 0.5,
x: -28,
y: 0,
rotation: 0
});
var runnerWingRight = self.attachAsset('runnerWingRight', {
anchorX: 0.5,
anchorY: 0.5,
x: 28,
y: 0,
rotation: 0
});
self.attachAsset('runner', {
anchorX: 0.5,
anchorY: 0.5
});
// Add wing flapping animation to make wings more visible
self.wingFlapTimer = 0;
tween(runnerWingLeft, {
rotation: -0.3
}, {
duration: 300,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(runnerWingLeft, {
rotation: 0.3
}, {
duration: 300,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(runnerWingLeft, {
rotation: 0
}, {
duration: 300,
easing: tween.easeInOut
});
}
});
}
});
tween(runnerWingRight, {
rotation: 0.3
}, {
duration: 300,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(runnerWingRight, {
rotation: -0.3
}, {
duration: 300,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(runnerWingRight, {
rotation: 0
}, {
duration: 300,
easing: tween.easeInOut
});
}
});
}
});
var runnerEyeLeft = self.attachAsset('runnerEyeLeft', {
anchorX: 0.5,
anchorY: 0.5,
x: -8,
y: -6
});
var runnerEyeRight = self.attachAsset('runnerEyeRight', {
anchorX: 0.5,
anchorY: 0.5,
x: 8,
y: -6
});
self.health = 50;
self.maxHealth = 50;
self.speed = 4;
self.reward = 5;
} else if (type === 'boss') {
self.attachAsset('boss', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 1.5
});
var bossEyeLeft = self.attachAsset('bossEyeLeft', {
anchorX: 0.5,
anchorY: 0.5,
x: -18,
y: -12
});
var bossEyeRight = self.attachAsset('bossEyeRight', {
anchorX: 0.5,
anchorY: 0.5,
x: 18,
y: -12
});
var bossMouth = self.attachAsset('bossMouth', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: 12
});
var bossHealth = 500 + bossKillCount * 200;
self.health = bossHealth;
self.maxHealth = bossHealth;
self.speed = 1;
self.reward = 200;
}
// Create HP display text for enemy
self.hpText = new Text2(self.health + ' HP', {
size: 20,
fill: '#ff0000'
});
self.hpText.anchor.set(0.5, 0.5);
self.hpText.x = 0;
self.hpText.y = -40;
self.addChild(self.hpText);
// Add attack cooldown and special abilities
self.attackCooldown = 0;
self.specialAbilityCooldown = 0;
self.update = function () {
self.move();
// Update HP display
self.hpText.setText(self.health + ' HP');
// Reduce cooldowns
if (self.attackCooldown > 0) {
self.attackCooldown--;
}
if (self.specialAbilityCooldown > 0) {
self.specialAbilityCooldown--;
}
// Special abilities for different enemy types
if (self.type === 'soldier') {
// Update bomb delay timer to unlock additional bombs after 0.5 second
if (!self.hasUnlockedExtraBombs) {
self.bombDelayTimer++;
if (self.bombDelayTimer >= 30) {
// 0.5 second at 60 FPS
self.maxBombs = 3; // Allow 2 more bombs (3 total)
self.hasUnlockedExtraBombs = true;
}
}
self.throwBomb();
} else if (self.type === 'tank' && self.attackCooldown <= 0) {
self.fireTower();
self.attackCooldown = 120; // 2 seconds
} else if (self.type === 'boss' && self.attackCooldown <= 0) {
self.fireMissile();
self.attackCooldown = 90; // 1.5 seconds
}
// Continuous wing flapping for runners to make wings more visible
if (self.type === 'runner') {
self.wingFlapTimer++;
if (self.wingFlapTimer >= 90) {
// Every 1.5 seconds
var wingLeft = self.children[0]; // runnerWingLeft
var wingRight = self.children[1]; // runnerWingRight
tween(wingLeft, {
rotation: -0.4
}, {
duration: 200,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(wingLeft, {
rotation: 0.4
}, {
duration: 200,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(wingLeft, {
rotation: 0
}, {
duration: 200,
easing: tween.easeInOut
});
}
});
}
});
tween(wingRight, {
rotation: 0.4
}, {
duration: 200,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(wingRight, {
rotation: -0.4
}, {
duration: 200,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(wingRight, {
rotation: 0
}, {
duration: 200,
easing: tween.easeInOut
});
}
});
}
});
self.wingFlapTimer = 0;
}
}
};
self.move = function () {
if (self.pathIndex >= path.length - 1) {
self.reachedEnd();
return;
}
// Check for tower collisions and damage them
for (var i = 0; i < towers.length; i++) {
var tower = towers[i];
var distance = Math.sqrt(Math.pow(tower.x - self.x, 2) + Math.pow(tower.y - self.y, 2));
if (distance < 50) {
// Collision distance
var damage = 0;
if (self.type === 'runner') {
damage = 20;
} else if (self.type === 'soldier') {
damage = 35;
} else if (self.type === 'tank') {
damage = 45;
} else if (self.type === 'boss') {
damage = 70;
}
// Apply damage to shield first if active
if (tower.shieldProtection && tower.shieldHp > 0) {
var shieldDamage = Math.min(damage, tower.shieldHp);
tower.shieldHp -= shieldDamage;
damage -= shieldDamage;
// Flash shield when taking damage
if (tower.shieldGraphics) {
tween(tower.shieldGraphics, {
tint: 0xff0000
}, {
duration: 150,
onFinish: function onFinish() {
if (tower.shieldGraphics) {
tween(tower.shieldGraphics, {
tint: 0xffffff
}, {
duration: 150
});
}
}
});
}
// Remove shield when depleted
if (tower.shieldHp <= 0) {
tower.shieldProtection = false;
if (tower.shieldGraphics) {
tween(tower.shieldGraphics, {
alpha: 0,
scaleX: 2,
scaleY: 2
}, {
duration: 300,
onFinish: function onFinish() {
if (tower.shieldGraphics) {
tower.shieldGraphics.destroy();
tower.shieldGraphics = null;
}
}
});
}
}
}
// Apply remaining damage to tower HP
tower.hp -= damage;
if (tower.hp <= 0) {
// Remove tower from game
for (var j = towers.length - 1; j >= 0; j--) {
if (towers[j] === tower) {
towers.splice(j, 1);
break;
}
}
tower.destroy();
}
// Enemy dies after attacking tower
self.die();
return;
}
}
var targetPoint = path[self.pathIndex + 1];
var dx = targetPoint.x - self.x;
var dy = targetPoint.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 5) {
self.pathIndex++;
self.lastPathIndex = self.pathIndex;
} else {
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
}
};
self.takeDamage = function (damage) {
// Shield absorbs damage for soldiers
if (self.type === 'soldier' && self.hasShield && self.shieldGraphics) {
// Shield absorbs 50% of damage
var actualDamage = Math.ceil(damage * 0.5);
self.health -= actualDamage;
// Flash shield when taking damage
tween(self.shieldGraphics, {
tint: 0xff0000
}, {
duration: 150,
onFinish: function onFinish() {
tween(self.shieldGraphics, {
tint: 0xffffff
}, {
duration: 150
});
}
});
// Remove shield when health gets low
if (self.health <= 50 && self.shieldGraphics) {
// Shield breaks animation
tween(self.shieldGraphics, {
alpha: 0,
scaleX: 2,
scaleY: 2
}, {
duration: 300,
onFinish: function onFinish() {
if (self.shieldGraphics) {
self.shieldGraphics.destroy();
self.shieldGraphics = null;
}
self.hasShield = false;
}
});
}
} else {
self.health -= damage;
}
if (self.health <= 0) {
self.die();
}
};
self.blastTowers = function (blastRadius, blastDamage) {
// Create visual blast effect
var blastEffect = LK.getAsset('soldier', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 0.5,
alpha: 0.8
});
blastEffect.x = self.x;
blastEffect.y = self.y;
blastEffect.tint = 0xff4444;
game.addChild(blastEffect);
// Animate blast expanding
tween(blastEffect, {
scaleX: 3,
scaleY: 3,
alpha: 0
}, {
duration: 500,
easing: tween.easeOut,
onFinish: function onFinish() {
blastEffect.destroy();
}
});
// Damage towers within blast radius
for (var i = 0; i < towers.length; i++) {
var tower = towers[i];
var distance = Math.sqrt(Math.pow(tower.x - self.x, 2) + Math.pow(tower.y - self.y, 2));
if (distance <= blastRadius) {
var damage = blastDamage;
// Apply damage to shield first if active
if (tower.shieldProtection && tower.shieldHp > 0) {
var shieldDamage = Math.min(damage, tower.shieldHp);
tower.shieldHp -= shieldDamage;
damage -= shieldDamage;
// Flash shield when taking damage
if (tower.shieldGraphics) {
tween(tower.shieldGraphics, {
tint: 0xff0000
}, {
duration: 150,
onFinish: function onFinish() {
if (tower.shieldGraphics) {
tween(tower.shieldGraphics, {
tint: 0xffffff
}, {
duration: 150
});
}
}
});
}
// Remove shield when depleted
if (tower.shieldHp <= 0) {
tower.shieldProtection = false;
if (tower.shieldGraphics) {
tween(tower.shieldGraphics, {
alpha: 0,
scaleX: 2,
scaleY: 2
}, {
duration: 300,
onFinish: function onFinish() {
if (tower.shieldGraphics) {
tower.shieldGraphics.destroy();
tower.shieldGraphics = null;
}
}
});
}
}
}
// Apply remaining damage to tower HP
tower.hp -= damage;
// Flash tower red to show damage
tween(tower, {
tint: 0xff0000
}, {
duration: 200,
onFinish: function onFinish() {
tween(tower, {
tint: 0xffffff
}, {
duration: 200
});
}
});
if (tower.hp <= 0) {
// Remove tower from game
for (var j = towers.length - 1; j >= 0; j--) {
if (towers[j] === tower) {
towers.splice(j, 1);
break;
}
}
tower.destroy();
}
}
}
};
self.die = function () {
if (self.type === 'boss') {
bossKillCount++;
}
// Drop chances for different enemy types
var dropChance = Math.random();
var shouldDrop = false;
var dropMultiplier = 1;
// Every wave special item drops guaranteed
if (waveSpecialItemDropped === false) {
shouldDrop = true;
waveSpecialItemDropped = true;
}
// Boss kills drop 2 special items
if (self.type === 'boss') {
shouldDrop = true;
dropMultiplier = 2;
}
// Regular drop chances (if not already dropping from wave/boss)
if (!shouldDrop) {
if (self.type === 'runner' && dropChance < 0.008) {
shouldDrop = true;
} else if (self.type === 'soldier' && dropChance < 0.002) {
shouldDrop = true;
} else if (self.type === 'tank' && dropChance < 0.0004) {
shouldDrop = true;
}
}
if (shouldDrop) {
for (var dropCount = 0; dropCount < dropMultiplier; dropCount++) {
var itemType = ['bomb', 'shield', 'missile'][Math.floor(Math.random() * 3)];
var specialItem;
if (itemType === 'bomb') {
specialItem = LK.getAsset('bombProjectile', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 1.2
});
// Add continuous round pulsing effect to make bomb more visible
var _bombPulse = function bombPulse() {
if (specialItem && !specialItem.destroyed) {
tween(specialItem, {
scaleX: 1.0,
scaleY: 1.0,
alpha: 0.7
}, {
duration: 600,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (specialItem && !specialItem.destroyed) {
tween(specialItem, {
scaleX: 0.8,
scaleY: 0.8,
alpha: 1
}, {
duration: 600,
easing: tween.easeInOut,
onFinish: _bombPulse
});
}
}
});
}
};
_bombPulse();
} else if (itemType === 'shield') {
specialItem = LK.getAsset('soldierShield', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.8
});
specialItem.tint = 0x4a90e2;
// Add continuous glowing effect to make shield more visible
var _shieldGlow = function shieldGlow() {
if (specialItem && !specialItem.destroyed) {
tween(specialItem, {
tint: 0x00ccff,
alpha: 0.8,
scaleX: 0.9,
scaleY: 0.9
}, {
duration: 700,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (specialItem && !specialItem.destroyed) {
tween(specialItem, {
tint: 0x4a90e2,
alpha: 1,
scaleX: 0.8,
scaleY: 0.8
}, {
duration: 700,
easing: tween.easeInOut,
onFinish: _shieldGlow
});
}
}
});
}
};
_shieldGlow();
} else if (itemType === 'missile') {
// Create round missile base
specialItem = LK.getAsset('boss', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.7,
scaleY: 0.7
});
specialItem.tint = 0xff4444; // Red missile base
// Add missile tip (smaller round element)
var tip = specialItem.attachAsset('runner', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.4,
scaleY: 0.4,
x: 0,
y: -20
});
tip.tint = 0xffaa00; // Orange tip color
// Add continuous missile glow effect
var _missileGlow = function missileGlow() {
if (specialItem && !specialItem.destroyed) {
tween(specialItem, {
tint: 0xff0000,
scaleX: 0.8,
scaleY: 0.8,
alpha: 0.9
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (specialItem && !specialItem.destroyed) {
tween(specialItem, {
tint: 0xff4444,
scaleX: 0.7,
scaleY: 0.7,
alpha: 1
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: _missileGlow
});
}
}
});
}
};
_missileGlow();
}
// Position item vertically beside cannon (left side)
var cannonX = 500; // Cannon button X position
var baseY = 2400; // Above cannon button
specialItem.x = cannonX - 150; // Left side of cannon
specialItem.y = baseY - specialItems.length * 80; // Stack vertically
// Add name text below special item
var nameText = new Text2(itemType.charAt(0).toUpperCase() + itemType.slice(1), {
size: 25,
fill: '#ffffff'
});
nameText.anchor.set(0.5, 0.5);
nameText.x = 0;
nameText.y = 45;
specialItem.addChild(nameText);
// Store item type and dragging state
specialItem.itemType = itemType;
specialItem.isDragging = false;
specialItem.startDragX = 0;
specialItem.startDragY = 0;
// Add to special items array for tracking
specialItems.push(specialItem);
game.addChild(specialItem);
// Add down handler for dragging special items
specialItem.down = function (localX, localY, obj) {
this.isDragging = true;
this.startDragX = localX;
this.startDragY = localY;
this.alpha = 0.7; // Make slightly transparent while dragging
};
// Animate special item with gentle floating effect
tween(specialItem, {
y: specialItem.y - 10,
alpha: 0.9
}, {
duration: 1000,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(specialItem, {
y: specialItem.y + 10,
alpha: 1
}, {
duration: 1000,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Loop the floating animation
if (specialItem && !specialItem.destroyed) {
tween(specialItem, {
y: specialItem.y - 10,
alpha: 0.9
}, {
duration: 1000,
easing: tween.easeInOut
});
}
}
});
}
});
}
}
// Special death effects for different enemy types
if (self.type === 'runner') {
// Runner blasts with smaller radius and damage
self.blastTowers(120, 15);
} else if (self.type === 'soldier') {
// Soldier blasts with larger radius and damage
self.blastTowers(150, 25);
} else if (self.type === 'boss') {
// Boss blasts with massive radius and damage
self.blastTowers(200, 60);
}
money += self.reward;
moneyText.setText('Money: ' + money);
for (var i = enemies.length - 1; i >= 0; i--) {
if (enemies[i] === self) {
enemies.splice(i, 1);
break;
}
}
self.destroy();
LK.getSound('enemyHit').play();
};
self.throwBomb = function () {
if (self.type !== 'soldier') return;
// Check if soldier has already thrown maximum bombs
if (self.bombsThrown >= self.maxBombs) return;
// Find nearest tower to throw bomb at
var nearestTower = null;
var nearestDistance = Infinity;
for (var i = 0; i < towers.length; i++) {
var tower = towers[i];
var distance = Math.sqrt(Math.pow(tower.x - self.x, 2) + Math.pow(tower.y - self.y, 2));
if (distance < nearestDistance && distance <= 300) {
// 300 pixel throw range
nearestTower = tower;
nearestDistance = distance;
}
}
if (nearestTower) {
// Increment bomb throw counter
self.bombsThrown++;
// Create bomb projectile
var bomb = LK.getAsset('bombProjectile', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.0,
scaleY: 1.0
});
bomb.x = self.x;
bomb.y = self.y;
// Add pulsing effect to flying bomb
tween(bomb, {
scaleX: 0.6,
scaleY: 0.6
}, {
duration: 400,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(bomb, {
scaleX: 0.5,
scaleY: 0.5
}, {
duration: 400,
easing: tween.easeInOut
});
}
});
game.addChild(bomb);
// Animate bomb flying to tower
tween(bomb, {
x: nearestTower.x,
y: nearestTower.y
}, {
duration: 800,
easing: tween.easeOut,
onFinish: function onFinish() {
// Explode at tower location
var blastEffect = LK.getAsset('soldier', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 0.5,
alpha: 0.8
});
blastEffect.x = bomb.x;
blastEffect.y = bomb.y;
blastEffect.tint = 0xff8800;
game.addChild(blastEffect);
// Animate explosion
tween(blastEffect, {
scaleX: 2.5,
scaleY: 2.5,
alpha: 0
}, {
duration: 400,
easing: tween.easeOut,
onFinish: function onFinish() {
blastEffect.destroy();
}
});
// Damage towers in blast radius
for (var j = 0; j < towers.length; j++) {
var tower = towers[j];
var distance = Math.sqrt(Math.pow(tower.x - bomb.x, 2) + Math.pow(tower.y - bomb.y, 2));
if (distance <= 100) {
// Bomb blast radius
var damage = 30;
// Apply damage to shield first if active
if (tower.shieldProtection && tower.shieldHp > 0) {
var shieldDamage = Math.min(damage, tower.shieldHp);
tower.shieldHp -= shieldDamage;
damage -= shieldDamage;
// Flash shield when taking damage
if (tower.shieldGraphics) {
tween(tower.shieldGraphics, {
tint: 0xff0000
}, {
duration: 150,
onFinish: function onFinish() {
if (tower.shieldGraphics) {
tween(tower.shieldGraphics, {
tint: 0xffffff
}, {
duration: 150
});
}
}
});
}
// Remove shield when depleted
if (tower.shieldHp <= 0) {
tower.shieldProtection = false;
if (tower.shieldGraphics) {
tween(tower.shieldGraphics, {
alpha: 0,
scaleX: 2,
scaleY: 2
}, {
duration: 300,
onFinish: function onFinish() {
if (tower.shieldGraphics) {
tower.shieldGraphics.destroy();
tower.shieldGraphics = null;
}
}
});
}
}
}
// Apply remaining damage to tower HP
tower.hp -= damage;
// Flash tower red
tween(tower, {
tint: 0xff0000
}, {
duration: 200,
onFinish: function onFinish() {
tween(tower, {
tint: 0xffffff
}, {
duration: 200
});
}
});
if (tower.hp <= 0) {
// Remove tower from game
for (var k = towers.length - 1; k >= 0; k--) {
if (towers[k] === tower) {
towers.splice(k, 1);
break;
}
}
tower.destroy();
}
}
}
bomb.destroy();
}
});
}
};
self.fireTower = function () {
if (self.type !== 'tank') return;
// Find nearest tower to fire at
var nearestTower = null;
var nearestDistance = Infinity;
for (var i = 0; i < towers.length; i++) {
var tower = towers[i];
var distance = Math.sqrt(Math.pow(tower.x - self.x, 2) + Math.pow(tower.y - self.y, 2));
if (distance < nearestDistance && distance <= 400) {
// 400 pixel firing range
nearestTower = tower;
nearestDistance = distance;
}
}
if (nearestTower) {
// Create tank projectile
var tankBullet = LK.getAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2,
scaleY: 2
});
tankBullet.x = self.x;
tankBullet.y = self.y;
tankBullet.tint = 0x8b572a;
game.addChild(tankBullet);
// Animate bullet flying to tower
tween(tankBullet, {
x: nearestTower.x,
y: nearestTower.y
}, {
duration: 600,
easing: tween.linear,
onFinish: function onFinish() {
// Damage the tower
var damage = 40;
// Apply damage to shield first if active
if (nearestTower.shieldProtection && nearestTower.shieldHp > 0) {
var shieldDamage = Math.min(damage, nearestTower.shieldHp);
nearestTower.shieldHp -= shieldDamage;
damage -= shieldDamage;
// Flash shield when taking damage
if (nearestTower.shieldGraphics) {
tween(nearestTower.shieldGraphics, {
tint: 0xff0000
}, {
duration: 150,
onFinish: function onFinish() {
if (nearestTower.shieldGraphics) {
tween(nearestTower.shieldGraphics, {
tint: 0xffffff
}, {
duration: 150
});
}
}
});
}
// Remove shield when depleted
if (nearestTower.shieldHp <= 0) {
nearestTower.shieldProtection = false;
if (nearestTower.shieldGraphics) {
tween(nearestTower.shieldGraphics, {
alpha: 0,
scaleX: 2,
scaleY: 2
}, {
duration: 300,
onFinish: function onFinish() {
if (nearestTower.shieldGraphics) {
nearestTower.shieldGraphics.destroy();
nearestTower.shieldGraphics = null;
}
}
});
}
}
}
// Apply remaining damage to tower HP
nearestTower.hp -= damage;
// Flash tower red
tween(nearestTower, {
tint: 0xff0000
}, {
duration: 200,
onFinish: function onFinish() {
tween(nearestTower, {
tint: 0xffffff
}, {
duration: 200
});
}
});
if (nearestTower.hp <= 0) {
// Remove tower from game
for (var j = towers.length - 1; j >= 0; j--) {
if (towers[j] === nearestTower) {
towers.splice(j, 1);
break;
}
}
nearestTower.destroy();
}
tankBullet.destroy();
}
});
}
};
self.fireMissile = function () {
if (self.type !== 'boss') return;
// Find nearest tower to fire missile at
var nearestTower = null;
var nearestDistance = Infinity;
for (var i = 0; i < towers.length; i++) {
var tower = towers[i];
var distance = Math.sqrt(Math.pow(tower.x - self.x, 2) + Math.pow(tower.y - self.y, 2));
if (distance < nearestDistance && distance <= 500) {
// 500 pixel missile range
nearestTower = tower;
nearestDistance = distance;
}
}
if (nearestTower) {
// Create boss missile
var missile = LK.getAsset('boss', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.4,
scaleY: 0.4
});
missile.x = self.x;
missile.y = self.y;
missile.tint = 0xff0000;
game.addChild(missile);
// Animate missile flying to tower
tween(missile, {
x: nearestTower.x,
y: nearestTower.y
}, {
duration: 1000,
easing: tween.easeOut,
onFinish: function onFinish() {
// Create explosion effect
var explosionEffect = LK.getAsset('boss', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.6,
scaleY: 0.6,
alpha: 0.9
});
explosionEffect.x = missile.x;
explosionEffect.y = missile.y;
explosionEffect.tint = 0xffaa00;
game.addChild(explosionEffect);
// Animate explosion
tween(explosionEffect, {
scaleX: 3,
scaleY: 3,
alpha: 0
}, {
duration: 600,
easing: tween.easeOut,
onFinish: function onFinish() {
explosionEffect.destroy();
}
});
// Damage towers in missile blast radius
for (var j = 0; j < towers.length; j++) {
var tower = towers[j];
var distance = Math.sqrt(Math.pow(tower.x - missile.x, 2) + Math.pow(tower.y - missile.y, 2));
if (distance <= 120) {
// Missile blast radius
var damage = 50;
// Apply damage to shield first if active
if (tower.shieldProtection && tower.shieldHp > 0) {
var shieldDamage = Math.min(damage, tower.shieldHp);
tower.shieldHp -= shieldDamage;
damage -= shieldDamage;
// Flash shield when taking damage
if (tower.shieldGraphics) {
tween(tower.shieldGraphics, {
tint: 0xff0000
}, {
duration: 150,
onFinish: function onFinish() {
if (tower.shieldGraphics) {
tween(tower.shieldGraphics, {
tint: 0xffffff
}, {
duration: 150
});
}
}
});
}
// Remove shield when depleted
if (tower.shieldHp <= 0) {
tower.shieldProtection = false;
if (tower.shieldGraphics) {
tween(tower.shieldGraphics, {
alpha: 0,
scaleX: 2,
scaleY: 2
}, {
duration: 300,
onFinish: function onFinish() {
if (tower.shieldGraphics) {
tower.shieldGraphics.destroy();
tower.shieldGraphics = null;
}
}
});
}
}
}
// Apply remaining damage to tower HP
tower.hp -= damage;
// Flash tower red
tween(tower, {
tint: 0xff0000
}, {
duration: 200,
onFinish: function onFinish() {
tween(tower, {
tint: 0xffffff
}, {
duration: 200
});
}
});
if (tower.hp <= 0) {
// Remove tower from game
for (var k = towers.length - 1; k >= 0; k--) {
if (towers[k] === tower) {
towers.splice(k, 1);
break;
}
}
tower.destroy();
}
}
}
missile.destroy();
}
});
}
};
self.reachedEnd = function () {
lives--;
livesText.setText('Lives: ' + lives);
for (var i = enemies.length - 1; i >= 0; i--) {
if (enemies[i] === self) {
enemies.splice(i, 1);
break;
}
}
self.destroy();
if (lives <= 0) {
LK.showGameOver();
}
};
return self;
});
var Tower = Container.expand(function (type, x, y) {
var self = Container.call(this);
self.type = type;
self.x = x;
self.y = y;
self.range = 200;
self.shootCooldown = 0;
self.target = null;
if (type === 'cannon') {
self.attachAsset('cannon', {
anchorX: 0.5,
anchorY: 0.5
});
self.damage = 30;
self.fireRate = 60;
self.range = 250;
self.hp = 190;
self.maxHp = 190;
// Missile usage tracking
self.missileUsed = false;
self.missileSecondTime = false;
self.missileTimer = 0;
} else if (type === 'sniper') {
self.attachAsset('sniper', {
anchorX: 0.5,
anchorY: 0.5
});
var sniperTriangle1 = self.attachAsset('sniperTriangle1', {
anchorX: 0.5,
anchorY: 0.5,
x: -15,
y: -10,
rotation: Math.PI / 4
});
var sniperTriangle2 = self.attachAsset('sniperTriangle2', {
anchorX: 0.5,
anchorY: 0.5,
x: 15,
y: -10,
rotation: -Math.PI / 4
});
self.damage = 80;
self.fireRate = 120;
self.range = 350;
self.hp = 240;
self.maxHp = 240;
} else if (type === 'laser') {
self.attachAsset('laser', {
anchorX: 0.5,
anchorY: 0.5
});
self.damage = 15;
self.fireRate = 20;
self.hp = 150;
self.maxHp = 150;
}
// Create HP display text
self.hpText = new Text2(self.hp + ' HP', {
size: 25,
fill: '#ffffff'
});
self.hpText.anchor.set(0.5, 0.5);
self.hpText.x = 0;
self.hpText.y = -60;
self.addChild(self.hpText);
// Add HP regeneration timer
self.hpRegenTimer = 0;
if (type === 'laser') {
self.hpRegenRate = 120; // 2 seconds at 60 FPS
} else if (type === 'cannon') {
self.hpRegenRate = 165; // 2.75 seconds at 60 FPS
} else if (type === 'sniper') {
self.hpRegenRate = 225; // 3.75 seconds at 60 FPS
}
self.hpRegenAmount = Math.ceil(self.maxHp * 0.1); // Regenerate 10% of max HP
// Special item enhancement properties
self.bombEnhanced = false;
self.bombEnhancedTimer = 0;
self.shieldEnhanced = false;
self.shieldEnhancedTimer = 0;
self.shieldProtection = false;
self.shieldGraphics = null;
self.shieldHp = 0;
self.shieldMaxHp = 70;
self.missileEnhanced = false;
self.missileEnhancedTimer = 0;
// Bomb usage tracking - towers can throw only one bomb initially
self.bombUsed = false;
self.bombUsageTimer = 0;
self.bombSecondTime = false;
self.update = function () {
if (self.shootCooldown > 0) {
self.shootCooldown--;
}
self.findTarget();
if (self.target && self.shootCooldown <= 0) {
self.shoot();
self.shootCooldown = self.fireRate;
}
// HP regeneration logic
if (self.hp < self.maxHp) {
self.hpRegenTimer++;
if (self.hpRegenTimer >= self.hpRegenRate) {
self.hp = Math.min(self.hp + self.hpRegenAmount, self.maxHp);
self.hpRegenTimer = 0;
// Visual regeneration effect
tween(self, {
tint: 0x00ff00
}, {
duration: 200,
onFinish: function onFinish() {
tween(self, {
tint: 0xffffff
}, {
duration: 200
});
}
});
}
}
// Update missile timer
if (self.missileTimer > 0) {
self.missileTimer--;
if (self.missileTimer <= 0 && self.missileUsed && !self.missileSecondTime) {
self.missileSecondTime = true; // Unlock second missile usage
}
}
// Update bomb usage timer
if (self.bombUsageTimer > 0) {
self.bombUsageTimer--;
if (self.bombUsageTimer <= 0 && self.bombUsed && !self.bombSecondTime) {
self.bombSecondTime = true; // Unlock second bomb usage
}
}
// Update HP display
if (self.shieldProtection && self.shieldHp > 0) {
self.hpText.setText(self.hp + ' HP + ' + self.shieldHp + ' Shield');
} else {
self.hpText.setText(self.hp + ' HP');
}
};
self.findTarget = function () {
var closestEnemy = null;
var closestDistance = Infinity;
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
var distance = Math.sqrt(Math.pow(enemy.x - self.x, 2) + Math.pow(enemy.y - self.y, 2));
if (distance <= self.range && distance < closestDistance) {
closestEnemy = enemy;
closestDistance = distance;
}
}
self.target = closestEnemy;
};
self.shoot = function () {
if (self.target) {
var finalDamage = self.damage;
// Check if tower has special item enhancements active
if (self.bombEnhanced && self.bombEnhancedTimer > 0) {
finalDamage = Math.floor(finalDamage * 1.5); // 50% damage boost from bomb
// Create bomb projectile that flies to enemy
var bomb = LK.getAsset('bombProjectile', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.0,
scaleY: 1.0
});
bomb.x = self.x;
bomb.y = self.y;
game.addChild(bomb);
// Add pulsing effect to flying bomb
tween(bomb, {
scaleX: 0.7,
scaleY: 0.7
}, {
duration: 300,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(bomb, {
scaleX: 0.6,
scaleY: 0.6
}, {
duration: 300,
easing: tween.easeInOut
});
}
});
// Animate bomb flying to target enemy
tween(bomb, {
x: self.target.x,
y: self.target.y
}, {
duration: 600,
easing: tween.easeOut,
onFinish: function onFinish() {
// Apply damage to target
if (self.target && !self.target.destroyed) {
self.target.takeDamage(finalDamage);
}
// Create explosion effect at target location
var explosionEffect = LK.getAsset('soldier', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 0.5,
alpha: 0.8
});
explosionEffect.x = bomb.x;
explosionEffect.y = bomb.y;
explosionEffect.tint = 0xff8800;
game.addChild(explosionEffect);
// Animate explosion
tween(explosionEffect, {
scaleX: 2.0,
scaleY: 2.0,
alpha: 0
}, {
duration: 400,
easing: tween.easeOut,
onFinish: function onFinish() {
explosionEffect.destroy();
}
});
bomb.destroy();
}
});
self.bombEnhancedTimer--;
if (self.bombEnhancedTimer <= 0) {
self.bombEnhanced = false;
}
} else if (self.missileEnhanced && self.missileEnhancedTimer > 0) {
// Missile enhancement: chance for explosive bullets
if (Math.random() < 0.3) {
// 30% chance for explosive shot
finalDamage = Math.floor(finalDamage * 2); // Double damage for explosive shot
// Visual effect for explosive bullet
var explosiveBullet = new Bullet(self.x, self.y, self.target, finalDamage);
explosiveBullet.isExplosive = true;
bullets.push(explosiveBullet);
game.addChild(explosiveBullet);
} else {
var bullet = new Bullet(self.x, self.y, self.target, finalDamage);
bullets.push(bullet);
game.addChild(bullet);
}
self.missileEnhancedTimer--;
if (self.missileEnhancedTimer <= 0) {
self.missileEnhanced = false;
}
} else if (self.type === 'cannon') {
// Cannon missile usage logic: 1 missile initially, then after 1 second cooldown, normal damage
if (!self.missileUsed) {
// First missile - use immediately
self.launchMissile();
self.missileUsed = true;
self.missileTimer = 60; // 1 second cooldown at 60 FPS
} else if (self.missileSecondTime && self.missileTimer <= 0) {
// Second time - can use missile again but then revert to normal damage
self.launchMissile();
self.missileSecondTime = false;
} else {
var bullet = new Bullet(self.x, self.y, self.target, finalDamage);
bullets.push(bullet);
game.addChild(bullet);
}
} else {
// Bomb usage logic: 1 bomb initially, then after 1 second cooldown, normal shooting
if (!self.bombUsed) {
// First bomb - use immediately
self.throwBomb();
self.bombUsed = true;
self.bombUsageTimer = 60; // 1 second cooldown at 60 FPS
} else if (self.bombSecondTime && self.bombUsageTimer <= 0) {
// Second time - can use bomb again but then revert to normal shooting
self.throwBomb();
self.bombSecondTime = false;
} else {
var bullet = new Bullet(self.x, self.y, self.target, finalDamage);
bullets.push(bullet);
game.addChild(bullet);
}
}
LK.getSound('shoot').play();
}
};
self.updateFireArrow = function () {
// Arrow display disabled
};
self.launchMissile = function () {
if (self.target) {
var finalDamage = self.damage; // Missiles do normal damage
// Create missile projectile
var missile = LK.getAsset('boss', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 0.5
});
missile.x = self.x;
missile.y = self.y;
missile.tint = 0xff4444; // Red missile color
game.addChild(missile);
// Add missile trail effect
var trail = LK.getAsset('runner', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.3,
scaleY: 0.3,
alpha: 0.7
});
trail.x = self.x;
trail.y = self.y;
trail.tint = 0xffaa00; // Orange trail
game.addChild(trail);
// Animate missile flying to target
tween(missile, {
x: self.target.x,
y: self.target.y
}, {
duration: 800,
easing: tween.easeOut,
onFinish: function onFinish() {
// Create large explosion effect
var explosionEffect = LK.getAsset('boss', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.8,
alpha: 0.9
});
explosionEffect.x = missile.x;
explosionEffect.y = missile.y;
explosionEffect.tint = 0xff0000;
game.addChild(explosionEffect);
// Animate explosion
tween(explosionEffect, {
scaleX: 2.5,
scaleY: 2.5,
alpha: 0
}, {
duration: 500,
easing: tween.easeOut,
onFinish: function onFinish() {
explosionEffect.destroy();
}
});
// Apply damage to target
if (self.target && !self.target.destroyed) {
self.target.takeDamage(finalDamage);
}
// Area damage to nearby enemies
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
if (enemy !== self.target) {
var distance = Math.sqrt(Math.pow(enemy.x - missile.x, 2) + Math.pow(enemy.y - missile.y, 2));
if (distance <= 100) {
// Missile blast radius
enemy.takeDamage(Math.floor(finalDamage * 0.5)); // 50% damage to nearby enemies
}
}
}
missile.destroy();
}
});
// Animate trail following missile
tween(trail, {
x: self.target.x,
y: self.target.y,
alpha: 0
}, {
duration: 900,
easing: tween.linear,
onFinish: function onFinish() {
trail.destroy();
}
});
}
};
self.throwBomb = function () {
if (self.target) {
var finalDamage = Math.floor(self.damage * 1.2); // Bombs do 20% more damage
// Create bomb projectile
var bomb = LK.getAsset('bombProjectile', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 1.2
});
bomb.x = self.x;
bomb.y = self.y;
bomb.tint = 0xff8800; // Orange bomb color
game.addChild(bomb);
// Add pulsing effect to flying bomb
tween(bomb, {
scaleX: 0.8,
scaleY: 0.8,
alpha: 0.8
}, {
duration: 400,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(bomb, {
scaleX: 1.0,
scaleY: 1.0,
alpha: 1.0
}, {
duration: 400,
easing: tween.easeInOut
});
}
});
// Animate bomb flying to target with arc trajectory
tween(bomb, {
x: self.target.x,
y: self.target.y
}, {
duration: 700,
easing: tween.easeOut,
onFinish: function onFinish() {
// Create large explosion effect
var explosionEffect = LK.getAsset('soldier', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.6,
scaleY: 0.6,
alpha: 0.9
});
explosionEffect.x = bomb.x;
explosionEffect.y = bomb.y;
explosionEffect.tint = 0xff4444;
game.addChild(explosionEffect);
// Animate explosion
tween(explosionEffect, {
scaleX: 2.8,
scaleY: 2.8,
alpha: 0
}, {
duration: 500,
easing: tween.easeOut,
onFinish: function onFinish() {
explosionEffect.destroy();
}
});
// Apply damage to target
if (self.target && !self.target.destroyed) {
self.target.takeDamage(finalDamage);
}
// Area damage to nearby enemies
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
if (enemy !== self.target) {
var distance = Math.sqrt(Math.pow(enemy.x - bomb.x, 2) + Math.pow(enemy.y - bomb.y, 2));
if (distance <= 120) {
// Bomb blast radius
enemy.takeDamage(Math.floor(finalDamage * 0.6)); // 60% damage to nearby enemies
}
}
}
bomb.destroy();
}
});
}
};
return self;
});
var TowerSpot = Container.expand(function (x, y) {
var self = Container.call(this);
self.attachAsset('towerSpot', {
anchorX: 0.5,
anchorY: 0.5
});
self.x = x;
self.y = y;
self.occupied = false;
self.tower = null;
self.down = function (localX, localY, obj) {
if (!self.occupied && selectedTowerType && canAffordTower(selectedTowerType)) {
self.placeTower(selectedTowerType);
}
};
self.placeTower = function (type) {
var cost = getTowerCost(type);
if (money >= cost) {
money -= cost;
moneyText.setText('Money: ' + money);
var tower = new Tower(type, self.x, self.y);
towers.push(tower);
game.addChild(tower);
self.occupied = true;
self.tower = tower;
self.alpha = 0.3;
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x1a1a2e
});
/****
* Game Code
****/
var money = 350;
var lives = 20;
var wave = 1;
var waveTimer = 0;
var spawnTimer = 0;
var enemiesInWave = 0;
var enemiesSpawned = 0;
var selectedTowerType = null;
var selectedTower = null;
var rangeIndicator = null;
var bossKillCount = 0;
var draggedTower = null;
var draggedTowerType = null;
var isDragging = false;
var towers = [];
var enemies = [];
var bullets = [];
var towerSpots = [];
var specialItems = [];
var waveSpecialItemDropped = false;
var path = [{
x: 1024,
y: 0
}, {
x: 1024,
y: 200
}, {
x: 1600,
y: 200
}, {
x: 1600,
y: 400
}, {
x: 400,
y: 400
}, {
x: 400,
y: 600
}, {
x: 1700,
y: 600
}, {
x: 1700,
y: 800
}, {
x: 300,
y: 800
}, {
x: 300,
y: 1000
}, {
x: 1500,
y: 1000
}, {
x: 1500,
y: 1200
}, {
x: 500,
y: 1200
}, {
x: 500,
y: 1400
}, {
x: 1600,
y: 1400
}, {
x: 1600,
y: 1600
}, {
x: 200,
y: 1600
}, {
x: 200,
y: 1800
}, {
x: 1700,
y: 1800
}, {
x: 1700,
y: 2000
}, {
x: 600,
y: 2000
}, {
x: 600,
y: 2200
}, {
x: 1400,
y: 2200
}, {
x: 1400,
y: 2400
}, {
x: 1024,
y: 2400
}, {
x: 1024,
y: 2600
}];
// Create path visualization
for (var i = 0; i < path.length - 1; i++) {
var pathSegment = LK.getAsset('path', {
anchorX: 0.5,
anchorY: 0.5
});
pathSegment.x = (path[i].x + path[i + 1].x) / 2;
pathSegment.y = (path[i].y + path[i + 1].y) / 2;
var dx = path[i + 1].x - path[i].x;
var dy = path[i + 1].y - path[i].y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (Math.abs(dx) > Math.abs(dy)) {
pathSegment.width = distance;
pathSegment.height = 120;
} else {
pathSegment.width = 120;
pathSegment.height = distance;
}
game.addChild(pathSegment);
}
// UI Elements
var moneyText = new Text2('Money: ' + money, {
size: 60,
fill: '#ffffff'
});
moneyText.anchor.set(0, 0);
LK.gui.topLeft.addChild(moneyText);
moneyText.x = 120;
moneyText.y = 20;
var livesText = new Text2('Lives: ' + lives, {
size: 60,
fill: '#ffffff'
});
livesText.anchor.set(0, 0);
LK.gui.topLeft.addChild(livesText);
livesText.x = 120;
livesText.y = 100;
var waveText = new Text2('Wave: ' + wave, {
size: 60,
fill: '#ffffff'
});
waveText.anchor.set(0, 0);
LK.gui.topLeft.addChild(waveText);
waveText.x = 120;
waveText.y = 180;
// Tower selection buttons
var cannonButton = LK.getAsset('cannon', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.8
});
cannonButton.x = 500;
cannonButton.y = 2500;
game.addChild(cannonButton);
var sniperButton = LK.getAsset('sniper', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.8
});
sniperButton.x = 900;
sniperButton.y = 2500;
game.addChild(sniperButton);
var laserButton = LK.getAsset('laser', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.8
});
laserButton.x = 1300;
laserButton.y = 2500;
game.addChild(laserButton);
var cannonNameText = new Text2('Cannon', {
size: 35,
fill: '#ffffff'
});
cannonNameText.anchor.set(0.5, 0.5);
cannonNameText.x = 500;
cannonNameText.y = 2660;
game.addChild(cannonNameText);
var sniperNameText = new Text2('Sniper', {
size: 35,
fill: '#ffffff'
});
sniperNameText.anchor.set(0.5, 0.5);
sniperNameText.x = 900;
sniperNameText.y = 2660;
game.addChild(sniperNameText);
var laserNameText = new Text2('Laser', {
size: 35,
fill: '#ffffff'
});
laserNameText.anchor.set(0.5, 0.5);
laserNameText.x = 1300;
laserNameText.y = 2660;
game.addChild(laserNameText);
var cannonCostText = new Text2('50', {
size: 40,
fill: '#ffffff'
});
cannonCostText.anchor.set(0.5, 0.5);
cannonCostText.x = 500;
cannonCostText.y = 2620;
game.addChild(cannonCostText);
var sniperCostText = new Text2('100', {
size: 40,
fill: '#ffffff'
});
sniperCostText.anchor.set(0.5, 0.5);
sniperCostText.x = 900;
sniperCostText.y = 2620;
game.addChild(sniperCostText);
var laserCostText = new Text2('30', {
size: 40,
fill: '#ffffff'
});
laserCostText.anchor.set(0.5, 0.5);
laserCostText.x = 1300;
laserCostText.y = 2620;
game.addChild(laserCostText);
function getTowerCost(type) {
if (type === 'cannon') return 50;
if (type === 'sniper') return 100;
if (type === 'laser') return 30;
return 0;
}
function canAffordTower(type) {
return money >= getTowerCost(type);
}
function updateButtonColors() {
cannonButton.tint = canAffordTower('cannon') ? 0xffffff : 0x666666;
sniperButton.tint = canAffordTower('sniper') ? 0xffffff : 0x666666;
laserButton.tint = canAffordTower('laser') ? 0xffffff : 0x666666;
}
function spawnEnemy() {
var enemyType;
if (wave >= 6 && enemiesSpawned === 0) {
// Spawn boss as first enemy after wave 6
enemyType = 'boss';
} else {
var rand = Math.random();
if (rand < 0.4) {
enemyType = 'soldier';
} else if (rand < 0.6) {
enemyType = 'tank';
} else {
enemyType = 'runner';
}
}
var enemy = new Enemy(enemyType);
enemies.push(enemy);
game.addChild(enemy);
enemiesSpawned++;
}
function startWave() {
enemiesInWave = 5 + wave * 2;
enemiesSpawned = 0;
spawnTimer = 0;
waveSpecialItemDropped = false; // Reset special item drop flag for new wave
}
game.move = function (x, y, obj) {
if (isDragging && draggedTowerType) {
// Update range indicator position during drag
if (rangeIndicator) {
rangeIndicator.x = x;
rangeIndicator.y = y;
}
}
// Handle special item dragging
for (var i = 0; i < specialItems.length; i++) {
var item = specialItems[i];
if (item.isDragging) {
item.x = x;
item.y = y;
break;
}
}
};
game.down = function (x, y, obj) {
// Clear previous selection
if (rangeIndicator && !isDragging) {
rangeIndicator.destroy();
rangeIndicator = null;
}
selectedTower = null;
// Check tower button clicks
var buttonSize = 80;
if (y >= 2500 - buttonSize / 2 && y <= 2500 + buttonSize / 2) {
if (x >= 500 - buttonSize / 2 && x <= 500 + buttonSize / 2 && canAffordTower('cannon')) {
draggedTowerType = 'cannon';
isDragging = true;
// Show range indicator for dragged tower
rangeIndicator = LK.getAsset('towerSpot', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 250 / 50,
// cannon range
scaleY: 250 / 50,
alpha: 0.4
});
rangeIndicator.x = x;
rangeIndicator.y = y;
rangeIndicator.tint = 0x4a90e2;
game.addChild(rangeIndicator);
} else if (x >= 900 - buttonSize / 2 && x <= 900 + buttonSize / 2 && canAffordTower('sniper')) {
draggedTowerType = 'sniper';
isDragging = true;
// Show range indicator for dragged tower
rangeIndicator = LK.getAsset('towerSpot', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 350 / 50,
// sniper range
scaleY: 350 / 50,
alpha: 0.4
});
rangeIndicator.x = x;
rangeIndicator.y = y;
rangeIndicator.tint = 0x7ed321;
game.addChild(rangeIndicator);
} else if (x >= 1300 - buttonSize / 2 && x <= 1300 + buttonSize / 2 && canAffordTower('laser')) {
draggedTowerType = 'laser';
isDragging = true;
// Show range indicator for dragged tower
rangeIndicator = LK.getAsset('towerSpot', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 200 / 50,
// laser range
scaleY: 200 / 50,
alpha: 0.4
});
rangeIndicator.x = x;
rangeIndicator.y = y;
rangeIndicator.tint = 0xf5a623;
game.addChild(rangeIndicator);
}
} else {
// Check if clicking on existing tower
var clickedTower = null;
for (var i = 0; i < towers.length; i++) {
var tower = towers[i];
var distance = Math.sqrt(Math.pow(tower.x - x, 2) + Math.pow(tower.y - y, 2));
if (distance <= 40) {
// Tower click radius
clickedTower = tower;
break;
}
}
if (clickedTower) {
// Select tower and show range
selectedTower = clickedTower;
rangeIndicator = LK.getAsset('towerSpot', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: selectedTower.range / 50,
scaleY: selectedTower.range / 50,
alpha: 0.3
});
rangeIndicator.x = selectedTower.x;
rangeIndicator.y = selectedTower.y;
rangeIndicator.tint = 0x00ff00;
game.addChild(rangeIndicator);
}
}
};
game.up = function (x, y, obj) {
if (isDragging && draggedTowerType) {
// Place tower at release position if affordable
var cost = getTowerCost(draggedTowerType);
if (money >= cost) {
money -= cost;
moneyText.setText('Money: ' + money);
var tower = new Tower(draggedTowerType, x, y);
towers.push(tower);
game.addChild(tower);
}
// Clean up dragging state
isDragging = false;
draggedTowerType = null;
if (rangeIndicator) {
rangeIndicator.destroy();
rangeIndicator = null;
}
}
// Handle special item placement on towers
for (var i = 0; i < specialItems.length; i++) {
var item = specialItems[i];
if (item.isDragging) {
var targetTower = null;
// Check if dropped on a tower
for (var j = 0; j < towers.length; j++) {
var tower = towers[j];
var distance = Math.sqrt(Math.pow(tower.x - x, 2) + Math.pow(tower.y - y, 2));
if (distance <= 60) {
// Tower placement radius
targetTower = tower;
break;
}
}
if (targetTower) {
// Apply special item effect to the tower
applySpecialItemToTower(item.itemType, targetTower);
// Remove the special item
specialItems.splice(i, 1);
item.destroy();
// Reposition remaining special items
repositionSpecialItems();
} else {
// Return item to original position if not dropped on tower
var cannonX = 500;
var baseY = 2400;
item.x = cannonX - 150;
item.y = baseY - i * 80;
}
item.isDragging = false;
item.alpha = 1; // Restore full opacity
break;
}
}
};
game.update = function () {
// Update all towers
for (var i = 0; i < towers.length; i++) {
towers[i].update();
}
// Update all enemies
for (var i = 0; i < enemies.length; i++) {
enemies[i].update();
}
// Update all bullets
for (var i = 0; i < bullets.length; i++) {
bullets[i].update();
}
// Wave management - only run when game is started
if (gameStarted) {
if (enemies.length === 0 && enemiesSpawned >= enemiesInWave) {
waveTimer++;
if (waveTimer >= 180) {
// 3 seconds between waves
wave++;
waveText.setText('Wave: ' + wave);
startWave();
waveTimer = 0;
}
}
// Enemy spawning
if (enemiesSpawned < enemiesInWave) {
spawnTimer++;
if (spawnTimer >= 45) {
// Spawn every 0.75 seconds
spawnEnemy();
spawnTimer = 0;
}
}
}
// Update button colors based on affordability
updateButtonColors();
};
// Add down handlers for special items
for (var i = 0; i < specialItems.length; i++) {
var item = specialItems[i];
item.down = function (localX, localY, obj) {
useSpecialItem(this);
};
}
function useSpecialItem(item) {
// Use stored itemType property instead of tint detection
var itemType = item.itemType;
// Apply special item effects
if (itemType === 'bomb') {
// Bomb: Damages all enemies on screen
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
enemy.takeDamage(100);
// Visual explosion effect on each enemy
var explosionEffect = LK.getAsset('soldier', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 0.5,
alpha: 0.8
});
explosionEffect.x = enemy.x;
explosionEffect.y = enemy.y;
explosionEffect.tint = 0xff4444;
game.addChild(explosionEffect);
tween(explosionEffect, {
scaleX: 2,
scaleY: 2,
alpha: 0
}, {
duration: 400,
easing: tween.easeOut,
onFinish: function onFinish() {
explosionEffect.destroy();
}
});
}
} else if (itemType === 'shield') {
// Shield: Heals all towers to full HP
for (var i = 0; i < towers.length; i++) {
var tower = towers[i];
tower.hp = tower.maxHp;
// Visual healing effect
tween(tower, {
tint: 0x00ff00
}, {
duration: 300,
onFinish: function onFinish() {
tween(tower, {
tint: 0xffffff
}, {
duration: 300
});
}
});
}
} else if (itemType === 'missile') {
var _shake = function shake() {
if (shakeTimer < shakeDuration) {
game.x = originalX + (Math.random() - 0.5) * shakeAmount;
game.y = originalY + (Math.random() - 0.5) * shakeAmount;
shakeTimer += 16; // Approximate frame time
LK.setTimeout(_shake, 16);
} else {
game.x = originalX;
game.y = originalY;
}
};
// Missile: Massive damage to all enemies and screen shake effect
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
enemy.takeDamage(120);
// Large missile explosion effect
var missileEffect = LK.getAsset('boss', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.8,
alpha: 0.9
});
missileEffect.x = enemy.x;
missileEffect.y = enemy.y;
missileEffect.tint = 0xff0000;
game.addChild(missileEffect);
tween(missileEffect, {
scaleX: 3,
scaleY: 3,
alpha: 0
}, {
duration: 600,
easing: tween.easeOut,
onFinish: function onFinish() {
missileEffect.destroy();
}
});
}
// Screen shake effect
var originalX = game.x;
var originalY = game.y;
var shakeAmount = 10;
var shakeDuration = 500;
var shakeTimer = 0;
_shake();
}
// Remove used special item
for (var i = specialItems.length - 1; i >= 0; i--) {
if (specialItems[i] === item) {
specialItems.splice(i, 1);
break;
}
}
item.destroy();
// Reposition remaining special items
for (var i = 0; i < specialItems.length; i++) {
var cannonX = 500;
var baseY = 2400;
specialItems[i].y = baseY - i * 80;
}
}
function applySpecialItemToTower(itemType, tower) {
if (itemType === 'bomb') {
// Bomb: Enhanced damage for battle effectiveness
tower.bombEnhanced = true;
tower.bombEnhancedTimer = 600; // 10 seconds at 60 FPS
// Visual effect
tween(tower, {
tint: 0xff4444
}, {
duration: 300,
onFinish: function onFinish() {
tween(tower, {
tint: 0xffffff
}, {
duration: 300
});
}
});
} else if (itemType === 'shield') {
// Shield: Protect tower with 70 HP shield
tower.shieldProtection = true;
tower.shieldHp = tower.shieldMaxHp; // 70 HP shield
// Create visual shield graphics - blue circle around tower
if (!tower.shieldGraphics) {
tower.shieldGraphics = tower.attachAsset('shield', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.6,
scaleX: 1.2,
scaleY: 1.2
});
tower.shieldGraphics.tint = 0x0000ff; // Blue color for shield
}
// Animate shield with pulsing blue effect
tween(tower.shieldGraphics, {
alpha: 0.3,
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 800,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(tower.shieldGraphics, {
alpha: 0.6,
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 800,
easing: tween.easeInOut
});
}
});
// Visual healing effect
tween(tower, {
tint: 0x00ff00
}, {
duration: 300,
onFinish: function onFinish() {
tween(tower, {
tint: 0xffffff
}, {
duration: 300
});
}
});
} else if (itemType === 'missile') {
// Missile: Only works with cannon towers
if (tower.type === 'cannon') {
// Missile: Enhanced explosive shots for battle
tower.missileEnhanced = true;
tower.missileEnhancedTimer = 900; // 15 seconds at 60 FPS
tower.fireRate = Math.floor(tower.fireRate * 0.8); // Slightly faster firing
// Visual effect
tween(tower, {
tint: 0xff0000
}, {
duration: 300,
onFinish: function onFinish() {
tween(tower, {
tint: 0xffffff
}, {
duration: 300
});
}
});
// Reset fire rate after enhancement ends
LK.setTimeout(function () {
if (!tower.missileEnhanced) {
tower.fireRate = Math.floor(tower.fireRate / 0.8); // Reset to original
}
}, 15000);
}
}
}
function repositionSpecialItems() {
for (var i = 0; i < specialItems.length; i++) {
var cannonX = 500;
var baseY = 2400;
specialItems[i].x = cannonX - 150;
specialItems[i].y = baseY - i * 80;
}
}
// Game state management
var gameStarted = false;
var gameStartButton = null;
// Create start button
gameStartButton = LK.getAsset('towerSpot', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2,
scaleY: 2
});
gameStartButton.x = 1024; // Center of screen
gameStartButton.y = 1366; // Center of screen
gameStartButton.tint = 0x00ff00; // Green color
game.addChild(gameStartButton);
// Add start button text
var startButtonText = new Text2('TAP TO START', {
size: 80,
fill: '#ffffff'
});
startButtonText.anchor.set(0.5, 0.5);
startButtonText.x = 0;
startButtonText.y = 0;
gameStartButton.addChild(startButtonText);
// Add pulsing animation to start button
var _startButtonPulse = function startButtonPulse() {
if (gameStartButton && !gameStarted) {
tween(gameStartButton, {
scaleX: 2.2,
scaleY: 2.2,
alpha: 0.8
}, {
duration: 800,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (gameStartButton && !gameStarted) {
tween(gameStartButton, {
scaleX: 2,
scaleY: 2,
alpha: 1
}, {
duration: 800,
easing: tween.easeInOut,
onFinish: _startButtonPulse
});
}
}
});
}
};
_startButtonPulse();
// Add click handler for start button
gameStartButton.down = function (localX, localY, obj) {
if (!gameStarted) {
gameStarted = true;
// Remove start button
gameStartButton.destroy();
gameStartButton = null;
// Start first wave
startWave();
}
};
// Don't start waves automatically anymore - wait for player to tap start