User prompt
towers throw bomb only one time when we use bomb after towers is normal ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
towers also throw bomb on enemy ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Please fix the bug: 'TypeError: Cannot read properties of null (reading 'takeDamage')' in or related to this line: 'self.target.takeDamage(finalDamage);' Line Number: 1483
User prompt
add start switch when i tap one switch game was start and enemy is coming
User prompt
missile use only one short after seconded time we use then missile can shot and after cannon damage is normal ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
missile can damage enemy hp 100
User prompt
cannon can launch missile ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Solider can throw bomb after 0.5 sec ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Solider can throw 1 bomb in 1 time and after 1 sec throw 2 bomb ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Solider can throw 1 bomb ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Bomb color is white ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Enemy throw bomb infinite time ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
special item more visible like round ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Please fix the bug: 'TypeError: Cannot use 'in' operator to search for 'tint' in null' in or related to this line: 'tween(tower.shieldGraphics, {' Line Number: 560 ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
bomb is throw on enemy when tower used it ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
protect shield when used it make a blue circle around the tower ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
cannon range is bigger than laser and smaller than sniper
User prompt
missile only shoot with cannon
User prompt
bomb make more visible missile look like calendar with corn on top
User prompt
shield is protect towers and it hp is 70
User prompt
bomb killed runner and missile damage is 120
User prompt
towers used special item to damage enemy
User prompt
show name under special item and it also used with dragged and place on tower
User prompt
special item used in bettle
User prompt
every wave special item dropped and when boss kill every special item dropped 2 times and special item see in vertical in side of cannon
/****
* 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) {
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;
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' && self.specialAbilityCooldown <= 0) {
self.throwBomb();
self.specialAbilityCooldown = 180; // 3 seconds
} 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() {
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() {
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('soldier', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.7,
scaleY: 0.7
});
specialItem.tint = 0x000000;
// Add pulsing effect to make bomb more visible
tween(specialItem, {
scaleX: 0.8,
scaleY: 0.8,
alpha: 0.8
}, {
duration: 800,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(specialItem, {
scaleX: 0.7,
scaleY: 0.7,
alpha: 1
}, {
duration: 800,
easing: tween.easeInOut
});
}
});
} else if (itemType === 'shield') {
specialItem = LK.getAsset('soldierShield', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.6,
scaleY: 0.6
});
specialItem.tint = 0xc0c0c0;
} else if (itemType === 'missile') {
// Create calendar base (rectangular)
specialItem = LK.getAsset('tank', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.6,
scaleY: 0.8
});
specialItem.tint = 0xffffff; // White calendar base
// Add corn on top (yellow ellipse)
var corn = specialItem.attachAsset('soldier', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.3,
scaleY: 0.5,
x: 0,
y: -25
});
corn.tint = 0xffdd00; // Yellow corn color
}
// 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;
// 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) {
// Create bomb projectile
var bomb = LK.getAsset('soldier', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 0.5
});
bomb.x = self.x;
bomb.y = self.y;
bomb.tint = 0x000000;
// 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() {
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() {
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() {
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 = 120;
self.hp = 190;
self.maxHp = 190;
} 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;
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 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
self.bombEnhancedTimer--;
if (self.bombEnhancedTimer <= 0) {
self.bombEnhanced = false;
}
}
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 {
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
};
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: 120 / 50,
// cannon range
scaleY: 120 / 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
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
if (!tower.shieldGraphics) {
tower.shieldGraphics = tower.attachAsset('shield', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.6,
scaleX: 1.2,
scaleY: 1.2
});
}
// 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;
}
}
// Start first wave
startWave(); ===================================================================
--- original.js
+++ change.js
@@ -2003,31 +2003,34 @@
});
}
});
} else if (itemType === 'missile') {
- // 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);
+ // 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++) {