User prompt
yollar oluşturulurken hep düz olsun dönüşler 90 derece olsun
User prompt
yeni yol oluşturulurken ekranın en altı yüzde 10 unun ve en üst yüzde 10 da olmasınaradaki %80 lik kısımda oluşsun
User prompt
yeni yol oluşturulurken yollar birbiriyle kesişmesin ve yol dönüşleri 90 derece olsun
User prompt
her yol değişirken başlangıç ve bitiş noktalarının yerleri değişebilir
User prompt
yol değişirken sadece 90 derece dönüşler olsun
User prompt
bu yol değişirken lütfen köşeli olarak değişsin
User prompt
her dalgada tüm kuleler kaldırılsın ve her yolun şekli değişisin ve uzunluğu biraz artsın. yolun başında başlangıç kapısı resmi ve sonunda base kısmı her zaman dursun.
User prompt
yolu biraz daha uzatırmısın
User prompt
yol oyunda hiç bir zaman değişmesin
User prompt
her dalgadan sonra yol yüzde 5 uzasın ama bitiş noktası hep ön planda gözüksün
User prompt
her dalgadan sonra yol değişsin ama bitiş noktası hep ön planda gözüksün
User prompt
her boss tan sonra yolu uzatma. bunun yerine tüm kuleleri sil ve yolu %10 uzatıp köşeli olacak şekilde değiştir
User prompt
yerleştirilen kulelere tıklandığında sadece menziller belli olsun başka bişey çıkmasın
User prompt
yeşil seçili satırı tamamen yazıyı kaplasın
User prompt
hangi kule ektif ise o satır komple seçili dırsun
User prompt
iki karakter daha sağa yerleşsin
User prompt
bu resimler yazının bitiminde biraz daha sağa yerleşsin
User prompt
bu resimler yazının en sağında bir karakter boşluk bırakıldıktan sonra yerleştirilsin
User prompt
bu yazıların sonuna eklediğim levelatla isimli resmi koy. bu resme tıklanın o kule bir sonraki levele atlasın. anında değerler ve yazılar güncellensin. eğer level atlaması için kaynak yeterli değilse levelatla resmi gözükmesin
User prompt
bu yazıları biraz sağa kaydır
User prompt
kulelerin sağ tarafında kaçıncı level oldukları verdiği atak ve level atlaması için ne kadar coin gerektiği yazsın
User prompt
resimler yazının üzerinde ortalanmış olsun
User prompt
resimler alt alta aynı hisada olabilir mi lütfen
User prompt
hangi yazı aktif ise güzel belirtmişsin fakat tüm yazıyı yani satırı kaplasın ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
yazı bittikten sonra bir boşuk ve arkasından resim olacak
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Bullet = Container.expand(function (tower, target, towerType) {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.tower = tower;
self.target = target;
self.towerType = towerType;
self.speed = 8;
// Calculate direction from tower to target
var dx = target.x - tower.x;
var dy = target.y - tower.y;
var distance = Math.sqrt(dx * dx + dy * dy);
self.vx = dx / distance * self.speed;
self.vy = dy / distance * self.speed;
self.update = function () {
self.x += self.vx;
self.y += self.vy;
// Check collision with intended target only
if (self.target && self.target.parent && self.intersects(self.target)) {
self.hit();
return;
}
// Remove bullet if target is destroyed
if (!self.target || !self.target.parent) {
self.destroy();
for (var i = bullets.length - 1; i >= 0; i--) {
if (bullets[i] === self) {
bullets.splice(i, 1);
break;
}
}
return;
}
// Remove if off screen
if (self.x < -100 || self.x > 2148 || self.y < -100 || self.y > 2832) {
self.destroy();
for (var i = bullets.length - 1; i >= 0; i--) {
if (bullets[i] === self) {
bullets.splice(i, 1);
break;
}
}
}
};
self.hit = function () {
LK.getSound('hit').play();
if (self.towerType === 'splash') {
// Splash damage
for (var i = enemies.length - 1; i >= 0; i--) {
var enemy = enemies[i];
if (enemy && enemy.parent) {
var dx = enemy.x - self.x;
var dy = enemy.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= self.tower.splashRadius) {
var killed = enemy.takeDamage(self.tower.damage);
if (killed) {
coins += enemy.reward;
enemy.destroy();
enemies.splice(i, 1);
}
}
}
}
// Create explosion effect
var explosion = LK.getAsset('explosion', {
anchorX: 0.5,
anchorY: 0.5
});
explosion.x = self.x;
explosion.y = self.y;
explosion.alpha = 0.7;
game.addChild(explosion);
tween(explosion, {
scaleX: 2,
scaleY: 2,
alpha: 0
}, {
duration: 300,
onFinish: function onFinish() {
explosion.destroy();
}
});
} else {
// Regular damage
if (self.target && self.target.parent) {
var killed = self.target.takeDamage(self.tower.damage);
if (killed) {
coins += self.target.reward;
self.target.destroy();
var targetIndex = enemies.indexOf(self.target);
if (targetIndex !== -1) {
enemies.splice(targetIndex, 1);
}
}
// Apply slow effect if slow tower
if (self.towerType === 'slow' && !killed) {
self.target.applySlow(self.tower.slowFactor, self.tower.slowDuration);
}
}
}
// Remove bullet
self.destroy();
for (var i = bullets.length - 1; i >= 0; i--) {
if (bullets[i] === self) {
bullets.splice(i, 1);
break;
}
}
};
return self;
});
// Game variables
var Enemy = Container.expand(function (type, pathIndex) {
var self = Container.call(this);
// Enemy properties based on type
var enemyConfig = {
basic: {
asset: 'basicEnemy',
health: 100,
speed: 2,
reward: 10
},
fast: {
asset: 'fastEnemy',
health: 60,
speed: 4,
reward: 15
},
tank: {
asset: 'tankEnemy',
health: 300,
speed: 1,
reward: 25
},
boss: {
asset: 'tankEnemy',
health: 1000,
speed: 1.5,
reward: 100
}
};
var config = enemyConfig[type] || enemyConfig.basic;
var enemyGraphics = self.attachAsset(config.asset, {
anchorX: 0.5,
anchorY: 0.5
});
self.maxHealth = config.health;
self.health = config.health;
self.speed = config.speed;
self.reward = config.reward;
self.pathIndex = pathIndex || 0;
self.slowEffect = 1;
self.slowDuration = 0;
// Health bar
var healthBarBg = LK.getAsset('pathTile', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.6,
scaleY: 0.1,
y: -40
});
healthBarBg.tint = 0x333333;
self.addChild(healthBarBg);
var healthBar = LK.getAsset('pathTile', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.6,
scaleY: 0.1,
y: -40
});
healthBar.tint = 0x00FF00;
self.addChild(healthBar);
self.healthBar = healthBar;
self.takeDamage = function (damage) {
self.health -= damage;
var healthPercent = Math.max(0, self.health / self.maxHealth);
self.healthBar.scaleX = 0.6 * healthPercent;
// Health bar color changes
if (healthPercent > 0.6) {
self.healthBar.tint = 0x00FF00;
} else if (healthPercent > 0.3) {
self.healthBar.tint = 0xFFFF00;
} else {
self.healthBar.tint = 0xFF0000;
}
// Flash effect
tween(enemyGraphics, {
tint: 0xFF0000
}, {
duration: 100,
onFinish: function onFinish() {
tween(enemyGraphics, {
tint: 0xFFFFFF
}, {
duration: 100
});
}
});
return self.health <= 0;
};
self.applySlow = function (factor, duration) {
self.slowEffect = Math.min(self.slowEffect, factor);
self.slowDuration = Math.max(self.slowDuration, duration);
};
self.update = function () {
// Update slow effect
if (self.slowDuration > 0) {
self.slowDuration--;
if (self.slowDuration <= 0) {
self.slowEffect = 1;
}
}
// Move along path
if (self.pathIndex < gamePath.length - 1 && gamePath[self.pathIndex + 1]) {
var currentTarget = gamePath[self.pathIndex + 1];
var dx = currentTarget.x - self.x;
var dy = currentTarget.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 20) {
self.pathIndex++;
if (self.pathIndex >= gamePath.length - 1) {
// Reached base
baseHealth--;
return;
}
} else {
var moveSpeed = self.speed * self.slowEffect;
self.x += dx / distance * moveSpeed;
self.y += dy / distance * moveSpeed;
}
} else if (self.pathIndex >= gamePath.length - 1) {
// Reached base
baseHealth--;
return;
}
};
return self;
});
var Tower = Container.expand(function (type) {
var self = Container.call(this);
// Tower configurations
var towerConfig = {
basic: {
asset: 'basicTower',
damage: 25,
range: 180,
fireRate: 60,
cost: 100,
upgradeCost: 75
},
splash: {
asset: 'splashTower',
damage: 40,
range: 150,
fireRate: 90,
cost: 200,
upgradeCost: 150,
splashRadius: 80
},
slow: {
asset: 'slowTower',
damage: 15,
range: 200,
fireRate: 45,
cost: 150,
upgradeCost: 100,
slowFactor: 0.5,
slowDuration: 120
}
};
var config = towerConfig[type] || towerConfig.basic;
var towerGraphics = self.attachAsset(config.asset, {
anchorX: 0.5,
anchorY: 0.5
});
self.type = type;
self.damage = config.damage;
self.range = config.range;
self.fireRate = config.fireRate;
self.cost = config.cost;
self.upgradeCost = config.upgradeCost;
self.splashRadius = config.splashRadius;
self.slowFactor = config.slowFactor;
self.slowDuration = config.slowDuration;
self.lastShot = 0;
self.level = 1;
// Range indicator (hidden by default)
var rangeIndicator = LK.getAsset('pathTile', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: self.range / 50,
scaleY: self.range / 50
});
rangeIndicator.tint = 0x00FF00;
rangeIndicator.alpha = 0.2;
rangeIndicator.visible = false;
self.addChild(rangeIndicator);
self.rangeIndicator = rangeIndicator;
self.showRange = function () {
self.rangeIndicator.visible = true;
};
self.hideRange = function () {
self.rangeIndicator.visible = false;
};
self.canShoot = function () {
return LK.ticks - self.lastShot >= self.fireRate;
};
self.findTarget = function () {
var closestEnemy = null;
var closestDistance = self.range;
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
if (enemy && enemy.parent) {
var dx = enemy.x - self.x;
var dy = enemy.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= self.range && distance < closestDistance) {
closestEnemy = enemy;
closestDistance = distance;
}
}
}
return closestEnemy;
};
self.shoot = function (target) {
if (!self.canShoot()) return;
self.lastShot = LK.ticks;
// Create bullet
var bullet = new Bullet(self, target, self.type);
bullet.x = self.x;
bullet.y = self.y;
bullets.push(bullet);
game.addChild(bullet);
LK.getSound('shoot').play();
};
self.upgrade = function () {
if (coins >= self.upgradeCost) {
coins -= self.upgradeCost;
self.level++;
self.damage = Math.floor(self.damage * 1.5);
self.range = Math.floor(self.range * 1.1);
self.upgradeCost = Math.floor(self.upgradeCost * 1.8);
// Visual upgrade effect
tween(towerGraphics, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 200,
onFinish: function onFinish() {
tween(towerGraphics, {
scaleX: 1,
scaleY: 1
}, {
duration: 200
});
}
});
// Update range indicator
self.rangeIndicator.scaleX = self.range / 50;
self.rangeIndicator.scaleY = self.range / 50;
return true;
}
return false;
};
self.down = function (x, y, obj) {
selectedTower = self;
self.showRange();
showTowerInfo(self);
};
self.update = function () {
// Find target in range
var target = self.findTarget();
if (target) {
// Rotate tower towards target
var angle = Math.atan2(target.y - self.y, target.x - self.x);
towerGraphics.rotation = angle;
// Shoot if ready
if (self.canShoot()) {
self.shoot(target);
}
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x4a6b3a
});
/****
* Game Code
****/
// Game variables
// Tower assets
// Enemy assets
// Game elements
// Sounds
var gamePath = [{
x: 100,
y: 400
}, {
x: 500,
y: 400
}, {
x: 500,
y: 800
}, {
x: 900,
y: 800
}, {
x: 900,
y: 1200
}, {
x: 1400,
y: 1200
}, {
x: 1400,
y: 1600
}, {
x: 1800,
y: 1600
}];
var enemies = [];
var towers = [];
var bullets = [];
var coins = 300;
var baseHealth = 20;
var currentWave = 1;
var enemiesSpawned = 0;
var enemiesPerWave = 5;
var waveDelay = 0;
var selectedTower = null;
var towerInfoPanel = null;
var bossWave = false;
var bossSpawned = false;
var pathExtensionFactor = 1.0;
// Draw path with visible tiles
for (var i = 0; i < gamePath.length; i++) {
var pathTile = LK.getAsset('pathTile', {
anchorX: 0.5,
anchorY: 0.5
});
pathTile.x = gamePath[i].x;
pathTile.y = gamePath[i].y;
pathTile.alpha = 0.8;
pathTile.tint = 0x8B4513;
game.addChild(pathTile);
}
// Draw connecting path segments
for (var i = 0; i < gamePath.length - 1; i++) {
var startPoint = gamePath[i];
var endPoint = gamePath[i + 1];
var dx = endPoint.x - startPoint.x;
var dy = endPoint.y - startPoint.y;
var distance = Math.sqrt(dx * dx + dy * dy);
var segments = Math.ceil(distance / 50);
for (var j = 1; j < segments; j++) {
var t = j / segments;
var segmentX = startPoint.x + dx * t;
var segmentY = startPoint.y + dy * t;
var segmentTile = LK.getAsset('pathTile', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.8
});
segmentTile.x = segmentX;
segmentTile.y = segmentY;
segmentTile.alpha = 0.6;
segmentTile.tint = 0x8B4513;
game.addChild(segmentTile);
}
}
// Create base
var base = LK.getAsset('base', {
anchorX: 0.5,
anchorY: 0.5
});
base.x = gamePath[gamePath.length - 1].x;
base.y = gamePath[gamePath.length - 1].y;
game.addChild(base);
// Create baslangic image at spawn point
var baslangicImage = LK.getAsset('baslangic', {
anchorX: 0.5,
anchorY: 0.5
});
baslangicImage.x = gamePath[0].x;
baslangicImage.y = gamePath[0].y;
game.addChild(baslangicImage);
// UI Elements
var coinsText = new Text2('Coins: ' + coins, {
size: 50,
fill: 0xFFD700
});
coinsText.anchor.set(0, 0);
coinsText.x = 150;
coinsText.y = 20;
LK.gui.topLeft.addChild(coinsText);
var healthText = new Text2('Base Health: ' + baseHealth, {
size: 50,
fill: 0xFF0000
});
healthText.anchor.set(1, 0);
healthText.y = 20;
LK.gui.topRight.addChild(healthText);
var waveText = new Text2('Wave: ' + currentWave, {
size: 50,
fill: 0xFFFFFF
});
waveText.anchor.set(0.5, 0);
waveText.y = 20;
LK.gui.top.addChild(waveText);
// Tower selection buttons
var basicTowerBtn = new Text2('Basic (100)', {
size: 40,
fill: 0x4444FF
});
basicTowerBtn.anchor.set(0, 1);
basicTowerBtn.x = 20;
basicTowerBtn.y = -20;
LK.gui.bottomLeft.addChild(basicTowerBtn);
var basicTowerImage = LK.getAsset('basicTower', {
anchorX: 0,
anchorY: 1,
scaleX: 0.8,
scaleY: 0.8
});
basicTowerImage.x = 220;
basicTowerImage.y = -20;
LK.gui.bottomLeft.addChild(basicTowerImage);
var splashTowerBtn = new Text2('Splash (200)', {
size: 40,
fill: 0xFF4444
});
splashTowerBtn.anchor.set(0, 1);
splashTowerBtn.x = 20;
splashTowerBtn.y = -80;
LK.gui.bottomLeft.addChild(splashTowerBtn);
var splashTowerImage = LK.getAsset('splashTower', {
anchorX: 0,
anchorY: 1,
scaleX: 0.8,
scaleY: 0.8
});
splashTowerImage.x = 220;
splashTowerImage.y = -80;
LK.gui.bottomLeft.addChild(splashTowerImage);
var slowTowerBtn = new Text2('Slow (150)', {
size: 40,
fill: 0x44FF44
});
slowTowerBtn.anchor.set(0, 1);
slowTowerBtn.x = 20;
slowTowerBtn.y = -140;
LK.gui.bottomLeft.addChild(slowTowerBtn);
var slowTowerImage = LK.getAsset('slowTower', {
anchorX: 0,
anchorY: 1,
scaleX: 0.8,
scaleY: 0.8
});
slowTowerImage.x = 220;
slowTowerImage.y = -140;
LK.gui.bottomLeft.addChild(slowTowerImage);
// Selected tower type
var selectedTowerType = 'basic';
// Add background highlights for tower selection
var basicTowerBg = LK.getAsset('pathTile', {
anchorX: 0,
anchorY: 0.5,
scaleX: 2.5,
scaleY: 0.8
});
basicTowerBg.tint = 0x00FF00;
basicTowerBg.alpha = 0.3;
basicTowerBg.x = 10;
basicTowerBg.y = -40;
LK.gui.bottomLeft.addChild(basicTowerBg);
var splashTowerBg = LK.getAsset('pathTile', {
anchorX: 0,
anchorY: 0.5,
scaleX: 2.5,
scaleY: 0.8
});
splashTowerBg.tint = 0x00FF00;
splashTowerBg.alpha = 0;
splashTowerBg.x = 10;
splashTowerBg.y = -100;
LK.gui.bottomLeft.addChild(splashTowerBg);
var slowTowerBg = LK.getAsset('pathTile', {
anchorX: 0,
anchorY: 0.5,
scaleX: 2.5,
scaleY: 0.8
});
slowTowerBg.tint = 0x00FF00;
slowTowerBg.alpha = 0;
slowTowerBg.x = 10;
slowTowerBg.y = -160;
LK.gui.bottomLeft.addChild(slowTowerBg);
// Helper functions
function updateUI() {
coinsText.setText('Coins: ' + coins);
healthText.setText('Base Health: ' + baseHealth);
waveText.setText('Wave: ' + currentWave + (bossWave ? ' (BOSS)' : ''));
// Update button colors based on affordability
basicTowerBtn.tint = coins >= 100 ? 0x4444FF : 0x666666;
splashTowerBtn.tint = coins >= 200 ? 0xFF4444 : 0x666666;
slowTowerBtn.tint = coins >= 150 ? 0x44FF44 : 0x666666;
// Update tower selection highlighting
basicTowerBg.alpha = selectedTowerType === 'basic' ? 0.3 : 0;
splashTowerBg.alpha = selectedTowerType === 'splash' ? 0.3 : 0;
slowTowerBg.alpha = selectedTowerType === 'slow' ? 0.3 : 0;
// Add pulsing animation to selected tower
var selectedBg = selectedTowerType === 'basic' ? basicTowerBg : selectedTowerType === 'splash' ? splashTowerBg : slowTowerBg;
if (selectedBg.alpha > 0) {
var pulseAlpha = 0.3 + Math.sin(LK.ticks * 0.1) * 0.1;
selectedBg.alpha = pulseAlpha;
}
}
function canPlaceTower(x, y) {
// Check if position is not on path points
for (var i = 0; i < gamePath.length; i++) {
var pathPoint = gamePath[i];
var distance = Math.sqrt((x - pathPoint.x) * (x - pathPoint.x) + (y - pathPoint.y) * (y - pathPoint.y));
if (distance < 80) {
return false;
}
}
// Check if position is not on path segments
for (var i = 0; i < gamePath.length - 1; i++) {
var startPoint = gamePath[i];
var endPoint = gamePath[i + 1];
// Calculate distance from point to line segment
var A = x - startPoint.x;
var B = y - startPoint.y;
var C = endPoint.x - startPoint.x;
var D = endPoint.y - startPoint.y;
var dot = A * C + B * D;
var lenSq = C * C + D * D;
var param = lenSq != 0 ? dot / lenSq : -1;
var xx, yy;
if (param < 0) {
xx = startPoint.x;
yy = startPoint.y;
} else if (param > 1) {
xx = endPoint.x;
yy = endPoint.y;
} else {
xx = startPoint.x + param * C;
yy = startPoint.y + param * D;
}
var dx = x - xx;
var dy = y - yy;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 80) {
return false;
}
}
// Check if position is not too close to other towers
for (var i = 0; i < towers.length; i++) {
var tower = towers[i];
var distance = Math.sqrt((x - tower.x) * (x - tower.x) + (y - tower.y) * (y - tower.y));
if (distance < 100) {
return false;
}
}
return true;
}
function spawnEnemy() {
var enemyTypes = ['basic', 'fast', 'tank'];
var waveMultiplier = Math.floor((currentWave - 1) / 3);
// Choose enemy type based on wave
var enemyType = 'basic';
if (currentWave > 3) {
enemyType = enemyTypes[Math.floor(Math.random() * 2)]; // basic or fast
}
if (currentWave > 6) {
enemyType = enemyTypes[Math.floor(Math.random() * 3)]; // all types
}
var enemy = new Enemy(enemyType, 0);
enemy.x = gamePath[0].x;
enemy.y = gamePath[0].y;
// Scale health for later waves
enemy.maxHealth *= 1 + waveMultiplier * 0.5;
enemy.health = enemy.maxHealth;
enemies.push(enemy);
game.addChild(enemy);
enemiesSpawned++;
}
function extendPathOnly() {
// Clear existing path tiles
for (var i = 0; i < game.children.length; i++) {
var child = game.children[i];
if (child.tint === 0x8B4513) {
child.destroy();
i--;
}
}
// Extend path by 5%
pathExtensionFactor += 0.05;
// Keep the same path shape but add more points to extend length
var originalPath = [{
x: 100,
y: 400
}, {
x: 500,
y: 400
}, {
x: 500,
y: 800
}, {
x: 900,
y: 800
}, {
x: 900,
y: 1200
}, {
x: 1400,
y: 1200
}, {
x: 1400,
y: 1600
}, {
x: 1800,
y: 1600
}];
// Calculate total original path length
var totalLength = 0;
for (var i = 0; i < originalPath.length - 1; i++) {
var dx = originalPath[i + 1].x - originalPath[i].x;
var dy = originalPath[i + 1].y - originalPath[i].y;
totalLength += Math.sqrt(dx * dx + dy * dy);
}
// Extend the path by interpolating more points
var extendedLength = totalLength * pathExtensionFactor;
var segmentLength = extendedLength / (originalPath.length - 1);
gamePath = [];
for (var i = 0; i < originalPath.length - 1; i++) {
var start = originalPath[i];
var end = originalPath[i + 1];
var dx = end.x - start.x;
var dy = end.y - start.y;
var distance = Math.sqrt(dx * dx + dy * dy);
var segments = Math.ceil(segmentLength / distance);
for (var j = 0; j < segments; j++) {
var t = j / segments;
gamePath.push({
x: start.x + dx * t,
y: start.y + dy * t
});
}
}
// Add final point
gamePath.push(originalPath[originalPath.length - 1]);
// Redraw path tiles
for (var i = 0; i < gamePath.length; i++) {
var pathTile = LK.getAsset('pathTile', {
anchorX: 0.5,
anchorY: 0.5
});
pathTile.x = gamePath[i].x;
pathTile.y = gamePath[i].y;
pathTile.alpha = 0.8;
pathTile.tint = 0x8B4513;
game.addChild(pathTile);
}
// Draw connecting segments
for (var i = 0; i < gamePath.length - 1; i++) {
var startPoint = gamePath[i];
var endPoint = gamePath[i + 1];
var dx = endPoint.x - startPoint.x;
var dy = endPoint.y - startPoint.y;
var distance = Math.sqrt(dx * dx + dy * dy);
var segments = Math.ceil(distance / 50);
for (var j = 1; j < segments; j++) {
var t = j / segments;
var segmentX = startPoint.x + dx * t;
var segmentY = startPoint.y + dy * t;
var segmentTile = LK.getAsset('pathTile', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.8
});
segmentTile.x = segmentX;
segmentTile.y = segmentY;
segmentTile.alpha = 0.6;
segmentTile.tint = 0x8B4513;
game.addChild(segmentTile);
}
}
// Update base position
base.x = gamePath[gamePath.length - 1].x;
base.y = gamePath[gamePath.length - 1].y;
}
function clearAllTowers() {
for (var i = towers.length - 1; i >= 0; i--) {
towers[i].destroy();
}
towers = [];
}
function spawnBossEnemy() {
var waveMultiplier = Math.floor((currentWave - 1) / 3);
var totalEnemyHealth = 0;
var enemyCount = enemiesPerWave * currentWave;
// Calculate total health of all enemies that would spawn in wave 5
for (var i = 0; i < enemyCount; i++) {
var baseHealth = 100; // Basic enemy health
if (currentWave > 3) baseHealth = 80; // Mix of basic and fast
if (currentWave > 6) baseHealth = 120; // Mix of all types
totalEnemyHealth += baseHealth * (1 + waveMultiplier * 0.5);
}
var boss = new Enemy('boss', 0);
boss.x = gamePath[0].x;
boss.y = gamePath[0].y;
boss.maxHealth = totalEnemyHealth;
boss.health = totalEnemyHealth;
boss.reward = 200;
enemies.push(boss);
game.addChild(boss);
bossSpawned = true;
}
function showTowerInfo(tower) {
if (!tower) return;
if (towerInfoPanel) {
towerInfoPanel.destroy();
}
var panel = LK.getAsset('base', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2,
scaleY: 1.5
});
panel.tint = 0x333333;
panel.alpha = 0.8;
panel.x = tower.x;
panel.y = Math.max(150, tower.y - 150);
game.addChild(panel);
towerInfoPanel = panel;
var infoText = new Text2('Level: ' + tower.level + '\nDamage: ' + tower.damage + '\nUpgrade: ' + tower.upgradeCost, {
size: 25,
fill: 0xFFFFFF
});
infoText.anchor.set(0.5, 0.5);
infoText.x = 0;
infoText.y = -25;
panel.addChild(infoText);
if (coins >= tower.upgradeCost) {
var upgradeBtn = new Text2('UPGRADE', {
size: 30,
fill: 0x00FF00
});
upgradeBtn.anchor.set(0.5, 0.5);
upgradeBtn.y = 35;
panel.addChild(upgradeBtn);
}
}
// Event handlers
basicTowerBtn.down = function () {
selectedTowerType = 'basic';
if (selectedTower) {
selectedTower.hideRange();
selectedTower = null;
}
if (towerInfoPanel) {
towerInfoPanel.destroy();
towerInfoPanel = null;
}
};
splashTowerBtn.down = function () {
selectedTowerType = 'splash';
if (selectedTower) {
selectedTower.hideRange();
selectedTower = null;
}
if (towerInfoPanel) {
towerInfoPanel.destroy();
towerInfoPanel = null;
}
};
slowTowerBtn.down = function () {
selectedTowerType = 'slow';
if (selectedTower) {
selectedTower.hideRange();
selectedTower = null;
}
if (towerInfoPanel) {
towerInfoPanel.destroy();
towerInfoPanel = null;
}
};
game.down = function (x, y, obj) {
// Check if clicking on upgrade button
if (towerInfoPanel && selectedTower && obj && obj.position && obj.parent) {
try {
var localPos = towerInfoPanel.toLocal(obj.parent.toGlobal(obj.position));
if (Math.abs(localPos.x) < 60 && Math.abs(localPos.y - 30) < 20 && coins >= selectedTower.upgradeCost) {
selectedTower.upgrade();
showTowerInfo(selectedTower);
return;
}
} catch (e) {
// Ignore positioning errors
}
}
// Hide tower info if clicking elsewhere
if (selectedTower) {
selectedTower.hideRange();
selectedTower = null;
}
if (towerInfoPanel) {
towerInfoPanel.destroy();
towerInfoPanel = null;
}
// Try to place tower
var towerCost = selectedTowerType === 'basic' ? 100 : selectedTowerType === 'splash' ? 200 : 150;
if (coins >= towerCost && canPlaceTower(x, y)) {
var tower = new Tower(selectedTowerType);
tower.x = x;
tower.y = y;
towers.push(tower);
game.addChild(tower);
coins -= towerCost;
LK.getSound('place').play();
}
};
// Main game loop
game.update = function () {
// Spawn enemies
if (bossWave) {
// Boss wave spawning
if (waveDelay <= 0 && !bossSpawned) {
spawnBossEnemy();
waveDelay = 0;
} else {
waveDelay--;
}
} else {
// Normal wave spawning
if (waveDelay <= 0 && enemiesSpawned < enemiesPerWave * currentWave) {
spawnEnemy();
waveDelay = 60; // 1 second between spawns
} else {
waveDelay--;
}
}
// Check wave completion
if (bossWave) {
// Boss wave completion
if (bossSpawned && enemies.length === 0) {
currentWave++;
enemiesSpawned = 0;
coins += 100 * currentWave; // Bonus coins for completing boss wave
waveDelay = 300; // 5 second break after boss
bossWave = false;
bossSpawned = false;
// Only extend path length every 5 waves, keep towers
if (currentWave % 5 === 1) {
extendPathOnly();
}
}
} else {
// Normal wave completion
if (enemiesSpawned >= enemiesPerWave * currentWave && enemies.length === 0) {
// Check if this completes a set of 5 waves
if (currentWave % 5 === 0) {
// Start boss wave
bossWave = true;
bossSpawned = false;
waveDelay = 180; // 3 second break before boss
} else {
// Normal wave progression
currentWave++;
enemiesSpawned = 0;
coins += 25 * currentWave; // Bonus coins for completing wave
waveDelay = 180; // 3 second break between waves
}
}
}
// Update enemies
for (var i = enemies.length - 1; i >= 0; i--) {
var enemy = enemies[i];
if (enemy.pathIndex >= gamePath.length - 1) {
// Enemy reached base
baseHealth--;
enemy.destroy();
enemies.splice(i, 1);
}
}
// Update towers
for (var i = 0; i < towers.length; i++) {
towers[i].update();
}
// Update bullets
for (var i = 0; i < bullets.length; i++) {
bullets[i].update();
}
// Check game over
if (baseHealth <= 0) {
LK.setScore(currentWave - 1);
LK.showGameOver();
}
// Update UI
updateUI();
}; /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Bullet = Container.expand(function (tower, target, towerType) {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.tower = tower;
self.target = target;
self.towerType = towerType;
self.speed = 8;
// Calculate direction from tower to target
var dx = target.x - tower.x;
var dy = target.y - tower.y;
var distance = Math.sqrt(dx * dx + dy * dy);
self.vx = dx / distance * self.speed;
self.vy = dy / distance * self.speed;
self.update = function () {
self.x += self.vx;
self.y += self.vy;
// Check collision with intended target only
if (self.target && self.target.parent && self.intersects(self.target)) {
self.hit();
return;
}
// Remove bullet if target is destroyed
if (!self.target || !self.target.parent) {
self.destroy();
for (var i = bullets.length - 1; i >= 0; i--) {
if (bullets[i] === self) {
bullets.splice(i, 1);
break;
}
}
return;
}
// Remove if off screen
if (self.x < -100 || self.x > 2148 || self.y < -100 || self.y > 2832) {
self.destroy();
for (var i = bullets.length - 1; i >= 0; i--) {
if (bullets[i] === self) {
bullets.splice(i, 1);
break;
}
}
}
};
self.hit = function () {
LK.getSound('hit').play();
if (self.towerType === 'splash') {
// Splash damage
for (var i = enemies.length - 1; i >= 0; i--) {
var enemy = enemies[i];
if (enemy && enemy.parent) {
var dx = enemy.x - self.x;
var dy = enemy.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= self.tower.splashRadius) {
var killed = enemy.takeDamage(self.tower.damage);
if (killed) {
coins += enemy.reward;
enemy.destroy();
enemies.splice(i, 1);
}
}
}
}
// Create explosion effect
var explosion = LK.getAsset('explosion', {
anchorX: 0.5,
anchorY: 0.5
});
explosion.x = self.x;
explosion.y = self.y;
explosion.alpha = 0.7;
game.addChild(explosion);
tween(explosion, {
scaleX: 2,
scaleY: 2,
alpha: 0
}, {
duration: 300,
onFinish: function onFinish() {
explosion.destroy();
}
});
} else {
// Regular damage
if (self.target && self.target.parent) {
var killed = self.target.takeDamage(self.tower.damage);
if (killed) {
coins += self.target.reward;
self.target.destroy();
var targetIndex = enemies.indexOf(self.target);
if (targetIndex !== -1) {
enemies.splice(targetIndex, 1);
}
}
// Apply slow effect if slow tower
if (self.towerType === 'slow' && !killed) {
self.target.applySlow(self.tower.slowFactor, self.tower.slowDuration);
}
}
}
// Remove bullet
self.destroy();
for (var i = bullets.length - 1; i >= 0; i--) {
if (bullets[i] === self) {
bullets.splice(i, 1);
break;
}
}
};
return self;
});
// Game variables
var Enemy = Container.expand(function (type, pathIndex) {
var self = Container.call(this);
// Enemy properties based on type
var enemyConfig = {
basic: {
asset: 'basicEnemy',
health: 100,
speed: 2,
reward: 10
},
fast: {
asset: 'fastEnemy',
health: 60,
speed: 4,
reward: 15
},
tank: {
asset: 'tankEnemy',
health: 300,
speed: 1,
reward: 25
},
boss: {
asset: 'tankEnemy',
health: 1000,
speed: 1.5,
reward: 100
}
};
var config = enemyConfig[type] || enemyConfig.basic;
var enemyGraphics = self.attachAsset(config.asset, {
anchorX: 0.5,
anchorY: 0.5
});
self.maxHealth = config.health;
self.health = config.health;
self.speed = config.speed;
self.reward = config.reward;
self.pathIndex = pathIndex || 0;
self.slowEffect = 1;
self.slowDuration = 0;
// Health bar
var healthBarBg = LK.getAsset('pathTile', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.6,
scaleY: 0.1,
y: -40
});
healthBarBg.tint = 0x333333;
self.addChild(healthBarBg);
var healthBar = LK.getAsset('pathTile', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.6,
scaleY: 0.1,
y: -40
});
healthBar.tint = 0x00FF00;
self.addChild(healthBar);
self.healthBar = healthBar;
self.takeDamage = function (damage) {
self.health -= damage;
var healthPercent = Math.max(0, self.health / self.maxHealth);
self.healthBar.scaleX = 0.6 * healthPercent;
// Health bar color changes
if (healthPercent > 0.6) {
self.healthBar.tint = 0x00FF00;
} else if (healthPercent > 0.3) {
self.healthBar.tint = 0xFFFF00;
} else {
self.healthBar.tint = 0xFF0000;
}
// Flash effect
tween(enemyGraphics, {
tint: 0xFF0000
}, {
duration: 100,
onFinish: function onFinish() {
tween(enemyGraphics, {
tint: 0xFFFFFF
}, {
duration: 100
});
}
});
return self.health <= 0;
};
self.applySlow = function (factor, duration) {
self.slowEffect = Math.min(self.slowEffect, factor);
self.slowDuration = Math.max(self.slowDuration, duration);
};
self.update = function () {
// Update slow effect
if (self.slowDuration > 0) {
self.slowDuration--;
if (self.slowDuration <= 0) {
self.slowEffect = 1;
}
}
// Move along path
if (self.pathIndex < gamePath.length - 1 && gamePath[self.pathIndex + 1]) {
var currentTarget = gamePath[self.pathIndex + 1];
var dx = currentTarget.x - self.x;
var dy = currentTarget.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 20) {
self.pathIndex++;
if (self.pathIndex >= gamePath.length - 1) {
// Reached base
baseHealth--;
return;
}
} else {
var moveSpeed = self.speed * self.slowEffect;
self.x += dx / distance * moveSpeed;
self.y += dy / distance * moveSpeed;
}
} else if (self.pathIndex >= gamePath.length - 1) {
// Reached base
baseHealth--;
return;
}
};
return self;
});
var Tower = Container.expand(function (type) {
var self = Container.call(this);
// Tower configurations
var towerConfig = {
basic: {
asset: 'basicTower',
damage: 25,
range: 180,
fireRate: 60,
cost: 100,
upgradeCost: 75
},
splash: {
asset: 'splashTower',
damage: 40,
range: 150,
fireRate: 90,
cost: 200,
upgradeCost: 150,
splashRadius: 80
},
slow: {
asset: 'slowTower',
damage: 15,
range: 200,
fireRate: 45,
cost: 150,
upgradeCost: 100,
slowFactor: 0.5,
slowDuration: 120
}
};
var config = towerConfig[type] || towerConfig.basic;
var towerGraphics = self.attachAsset(config.asset, {
anchorX: 0.5,
anchorY: 0.5
});
self.type = type;
self.damage = config.damage;
self.range = config.range;
self.fireRate = config.fireRate;
self.cost = config.cost;
self.upgradeCost = config.upgradeCost;
self.splashRadius = config.splashRadius;
self.slowFactor = config.slowFactor;
self.slowDuration = config.slowDuration;
self.lastShot = 0;
self.level = 1;
// Range indicator (hidden by default)
var rangeIndicator = LK.getAsset('pathTile', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: self.range / 50,
scaleY: self.range / 50
});
rangeIndicator.tint = 0x00FF00;
rangeIndicator.alpha = 0.2;
rangeIndicator.visible = false;
self.addChild(rangeIndicator);
self.rangeIndicator = rangeIndicator;
self.showRange = function () {
self.rangeIndicator.visible = true;
};
self.hideRange = function () {
self.rangeIndicator.visible = false;
};
self.canShoot = function () {
return LK.ticks - self.lastShot >= self.fireRate;
};
self.findTarget = function () {
var closestEnemy = null;
var closestDistance = self.range;
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
if (enemy && enemy.parent) {
var dx = enemy.x - self.x;
var dy = enemy.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= self.range && distance < closestDistance) {
closestEnemy = enemy;
closestDistance = distance;
}
}
}
return closestEnemy;
};
self.shoot = function (target) {
if (!self.canShoot()) return;
self.lastShot = LK.ticks;
// Create bullet
var bullet = new Bullet(self, target, self.type);
bullet.x = self.x;
bullet.y = self.y;
bullets.push(bullet);
game.addChild(bullet);
LK.getSound('shoot').play();
};
self.upgrade = function () {
if (coins >= self.upgradeCost) {
coins -= self.upgradeCost;
self.level++;
self.damage = Math.floor(self.damage * 1.5);
self.range = Math.floor(self.range * 1.1);
self.upgradeCost = Math.floor(self.upgradeCost * 1.8);
// Visual upgrade effect
tween(towerGraphics, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 200,
onFinish: function onFinish() {
tween(towerGraphics, {
scaleX: 1,
scaleY: 1
}, {
duration: 200
});
}
});
// Update range indicator
self.rangeIndicator.scaleX = self.range / 50;
self.rangeIndicator.scaleY = self.range / 50;
return true;
}
return false;
};
self.down = function (x, y, obj) {
selectedTower = self;
self.showRange();
showTowerInfo(self);
};
self.update = function () {
// Find target in range
var target = self.findTarget();
if (target) {
// Rotate tower towards target
var angle = Math.atan2(target.y - self.y, target.x - self.x);
towerGraphics.rotation = angle;
// Shoot if ready
if (self.canShoot()) {
self.shoot(target);
}
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x4a6b3a
});
/****
* Game Code
****/
// Game variables
// Tower assets
// Enemy assets
// Game elements
// Sounds
var gamePath = [{
x: 100,
y: 400
}, {
x: 500,
y: 400
}, {
x: 500,
y: 800
}, {
x: 900,
y: 800
}, {
x: 900,
y: 1200
}, {
x: 1400,
y: 1200
}, {
x: 1400,
y: 1600
}, {
x: 1800,
y: 1600
}];
var enemies = [];
var towers = [];
var bullets = [];
var coins = 300;
var baseHealth = 20;
var currentWave = 1;
var enemiesSpawned = 0;
var enemiesPerWave = 5;
var waveDelay = 0;
var selectedTower = null;
var towerInfoPanel = null;
var bossWave = false;
var bossSpawned = false;
var pathExtensionFactor = 1.0;
// Draw path with visible tiles
for (var i = 0; i < gamePath.length; i++) {
var pathTile = LK.getAsset('pathTile', {
anchorX: 0.5,
anchorY: 0.5
});
pathTile.x = gamePath[i].x;
pathTile.y = gamePath[i].y;
pathTile.alpha = 0.8;
pathTile.tint = 0x8B4513;
game.addChild(pathTile);
}
// Draw connecting path segments
for (var i = 0; i < gamePath.length - 1; i++) {
var startPoint = gamePath[i];
var endPoint = gamePath[i + 1];
var dx = endPoint.x - startPoint.x;
var dy = endPoint.y - startPoint.y;
var distance = Math.sqrt(dx * dx + dy * dy);
var segments = Math.ceil(distance / 50);
for (var j = 1; j < segments; j++) {
var t = j / segments;
var segmentX = startPoint.x + dx * t;
var segmentY = startPoint.y + dy * t;
var segmentTile = LK.getAsset('pathTile', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.8
});
segmentTile.x = segmentX;
segmentTile.y = segmentY;
segmentTile.alpha = 0.6;
segmentTile.tint = 0x8B4513;
game.addChild(segmentTile);
}
}
// Create base
var base = LK.getAsset('base', {
anchorX: 0.5,
anchorY: 0.5
});
base.x = gamePath[gamePath.length - 1].x;
base.y = gamePath[gamePath.length - 1].y;
game.addChild(base);
// Create baslangic image at spawn point
var baslangicImage = LK.getAsset('baslangic', {
anchorX: 0.5,
anchorY: 0.5
});
baslangicImage.x = gamePath[0].x;
baslangicImage.y = gamePath[0].y;
game.addChild(baslangicImage);
// UI Elements
var coinsText = new Text2('Coins: ' + coins, {
size: 50,
fill: 0xFFD700
});
coinsText.anchor.set(0, 0);
coinsText.x = 150;
coinsText.y = 20;
LK.gui.topLeft.addChild(coinsText);
var healthText = new Text2('Base Health: ' + baseHealth, {
size: 50,
fill: 0xFF0000
});
healthText.anchor.set(1, 0);
healthText.y = 20;
LK.gui.topRight.addChild(healthText);
var waveText = new Text2('Wave: ' + currentWave, {
size: 50,
fill: 0xFFFFFF
});
waveText.anchor.set(0.5, 0);
waveText.y = 20;
LK.gui.top.addChild(waveText);
// Tower selection buttons
var basicTowerBtn = new Text2('Basic (100)', {
size: 40,
fill: 0x4444FF
});
basicTowerBtn.anchor.set(0, 1);
basicTowerBtn.x = 20;
basicTowerBtn.y = -20;
LK.gui.bottomLeft.addChild(basicTowerBtn);
var basicTowerImage = LK.getAsset('basicTower', {
anchorX: 0,
anchorY: 1,
scaleX: 0.8,
scaleY: 0.8
});
basicTowerImage.x = 220;
basicTowerImage.y = -20;
LK.gui.bottomLeft.addChild(basicTowerImage);
var splashTowerBtn = new Text2('Splash (200)', {
size: 40,
fill: 0xFF4444
});
splashTowerBtn.anchor.set(0, 1);
splashTowerBtn.x = 20;
splashTowerBtn.y = -80;
LK.gui.bottomLeft.addChild(splashTowerBtn);
var splashTowerImage = LK.getAsset('splashTower', {
anchorX: 0,
anchorY: 1,
scaleX: 0.8,
scaleY: 0.8
});
splashTowerImage.x = 220;
splashTowerImage.y = -80;
LK.gui.bottomLeft.addChild(splashTowerImage);
var slowTowerBtn = new Text2('Slow (150)', {
size: 40,
fill: 0x44FF44
});
slowTowerBtn.anchor.set(0, 1);
slowTowerBtn.x = 20;
slowTowerBtn.y = -140;
LK.gui.bottomLeft.addChild(slowTowerBtn);
var slowTowerImage = LK.getAsset('slowTower', {
anchorX: 0,
anchorY: 1,
scaleX: 0.8,
scaleY: 0.8
});
slowTowerImage.x = 220;
slowTowerImage.y = -140;
LK.gui.bottomLeft.addChild(slowTowerImage);
// Selected tower type
var selectedTowerType = 'basic';
// Add background highlights for tower selection
var basicTowerBg = LK.getAsset('pathTile', {
anchorX: 0,
anchorY: 0.5,
scaleX: 2.5,
scaleY: 0.8
});
basicTowerBg.tint = 0x00FF00;
basicTowerBg.alpha = 0.3;
basicTowerBg.x = 10;
basicTowerBg.y = -40;
LK.gui.bottomLeft.addChild(basicTowerBg);
var splashTowerBg = LK.getAsset('pathTile', {
anchorX: 0,
anchorY: 0.5,
scaleX: 2.5,
scaleY: 0.8
});
splashTowerBg.tint = 0x00FF00;
splashTowerBg.alpha = 0;
splashTowerBg.x = 10;
splashTowerBg.y = -100;
LK.gui.bottomLeft.addChild(splashTowerBg);
var slowTowerBg = LK.getAsset('pathTile', {
anchorX: 0,
anchorY: 0.5,
scaleX: 2.5,
scaleY: 0.8
});
slowTowerBg.tint = 0x00FF00;
slowTowerBg.alpha = 0;
slowTowerBg.x = 10;
slowTowerBg.y = -160;
LK.gui.bottomLeft.addChild(slowTowerBg);
// Helper functions
function updateUI() {
coinsText.setText('Coins: ' + coins);
healthText.setText('Base Health: ' + baseHealth);
waveText.setText('Wave: ' + currentWave + (bossWave ? ' (BOSS)' : ''));
// Update button colors based on affordability
basicTowerBtn.tint = coins >= 100 ? 0x4444FF : 0x666666;
splashTowerBtn.tint = coins >= 200 ? 0xFF4444 : 0x666666;
slowTowerBtn.tint = coins >= 150 ? 0x44FF44 : 0x666666;
// Update tower selection highlighting
basicTowerBg.alpha = selectedTowerType === 'basic' ? 0.3 : 0;
splashTowerBg.alpha = selectedTowerType === 'splash' ? 0.3 : 0;
slowTowerBg.alpha = selectedTowerType === 'slow' ? 0.3 : 0;
// Add pulsing animation to selected tower
var selectedBg = selectedTowerType === 'basic' ? basicTowerBg : selectedTowerType === 'splash' ? splashTowerBg : slowTowerBg;
if (selectedBg.alpha > 0) {
var pulseAlpha = 0.3 + Math.sin(LK.ticks * 0.1) * 0.1;
selectedBg.alpha = pulseAlpha;
}
}
function canPlaceTower(x, y) {
// Check if position is not on path points
for (var i = 0; i < gamePath.length; i++) {
var pathPoint = gamePath[i];
var distance = Math.sqrt((x - pathPoint.x) * (x - pathPoint.x) + (y - pathPoint.y) * (y - pathPoint.y));
if (distance < 80) {
return false;
}
}
// Check if position is not on path segments
for (var i = 0; i < gamePath.length - 1; i++) {
var startPoint = gamePath[i];
var endPoint = gamePath[i + 1];
// Calculate distance from point to line segment
var A = x - startPoint.x;
var B = y - startPoint.y;
var C = endPoint.x - startPoint.x;
var D = endPoint.y - startPoint.y;
var dot = A * C + B * D;
var lenSq = C * C + D * D;
var param = lenSq != 0 ? dot / lenSq : -1;
var xx, yy;
if (param < 0) {
xx = startPoint.x;
yy = startPoint.y;
} else if (param > 1) {
xx = endPoint.x;
yy = endPoint.y;
} else {
xx = startPoint.x + param * C;
yy = startPoint.y + param * D;
}
var dx = x - xx;
var dy = y - yy;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 80) {
return false;
}
}
// Check if position is not too close to other towers
for (var i = 0; i < towers.length; i++) {
var tower = towers[i];
var distance = Math.sqrt((x - tower.x) * (x - tower.x) + (y - tower.y) * (y - tower.y));
if (distance < 100) {
return false;
}
}
return true;
}
function spawnEnemy() {
var enemyTypes = ['basic', 'fast', 'tank'];
var waveMultiplier = Math.floor((currentWave - 1) / 3);
// Choose enemy type based on wave
var enemyType = 'basic';
if (currentWave > 3) {
enemyType = enemyTypes[Math.floor(Math.random() * 2)]; // basic or fast
}
if (currentWave > 6) {
enemyType = enemyTypes[Math.floor(Math.random() * 3)]; // all types
}
var enemy = new Enemy(enemyType, 0);
enemy.x = gamePath[0].x;
enemy.y = gamePath[0].y;
// Scale health for later waves
enemy.maxHealth *= 1 + waveMultiplier * 0.5;
enemy.health = enemy.maxHealth;
enemies.push(enemy);
game.addChild(enemy);
enemiesSpawned++;
}
function extendPathOnly() {
// Clear existing path tiles
for (var i = 0; i < game.children.length; i++) {
var child = game.children[i];
if (child.tint === 0x8B4513) {
child.destroy();
i--;
}
}
// Extend path by 5%
pathExtensionFactor += 0.05;
// Keep the same path shape but add more points to extend length
var originalPath = [{
x: 100,
y: 400
}, {
x: 500,
y: 400
}, {
x: 500,
y: 800
}, {
x: 900,
y: 800
}, {
x: 900,
y: 1200
}, {
x: 1400,
y: 1200
}, {
x: 1400,
y: 1600
}, {
x: 1800,
y: 1600
}];
// Calculate total original path length
var totalLength = 0;
for (var i = 0; i < originalPath.length - 1; i++) {
var dx = originalPath[i + 1].x - originalPath[i].x;
var dy = originalPath[i + 1].y - originalPath[i].y;
totalLength += Math.sqrt(dx * dx + dy * dy);
}
// Extend the path by interpolating more points
var extendedLength = totalLength * pathExtensionFactor;
var segmentLength = extendedLength / (originalPath.length - 1);
gamePath = [];
for (var i = 0; i < originalPath.length - 1; i++) {
var start = originalPath[i];
var end = originalPath[i + 1];
var dx = end.x - start.x;
var dy = end.y - start.y;
var distance = Math.sqrt(dx * dx + dy * dy);
var segments = Math.ceil(segmentLength / distance);
for (var j = 0; j < segments; j++) {
var t = j / segments;
gamePath.push({
x: start.x + dx * t,
y: start.y + dy * t
});
}
}
// Add final point
gamePath.push(originalPath[originalPath.length - 1]);
// Redraw path tiles
for (var i = 0; i < gamePath.length; i++) {
var pathTile = LK.getAsset('pathTile', {
anchorX: 0.5,
anchorY: 0.5
});
pathTile.x = gamePath[i].x;
pathTile.y = gamePath[i].y;
pathTile.alpha = 0.8;
pathTile.tint = 0x8B4513;
game.addChild(pathTile);
}
// Draw connecting segments
for (var i = 0; i < gamePath.length - 1; i++) {
var startPoint = gamePath[i];
var endPoint = gamePath[i + 1];
var dx = endPoint.x - startPoint.x;
var dy = endPoint.y - startPoint.y;
var distance = Math.sqrt(dx * dx + dy * dy);
var segments = Math.ceil(distance / 50);
for (var j = 1; j < segments; j++) {
var t = j / segments;
var segmentX = startPoint.x + dx * t;
var segmentY = startPoint.y + dy * t;
var segmentTile = LK.getAsset('pathTile', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.8
});
segmentTile.x = segmentX;
segmentTile.y = segmentY;
segmentTile.alpha = 0.6;
segmentTile.tint = 0x8B4513;
game.addChild(segmentTile);
}
}
// Update base position
base.x = gamePath[gamePath.length - 1].x;
base.y = gamePath[gamePath.length - 1].y;
}
function clearAllTowers() {
for (var i = towers.length - 1; i >= 0; i--) {
towers[i].destroy();
}
towers = [];
}
function spawnBossEnemy() {
var waveMultiplier = Math.floor((currentWave - 1) / 3);
var totalEnemyHealth = 0;
var enemyCount = enemiesPerWave * currentWave;
// Calculate total health of all enemies that would spawn in wave 5
for (var i = 0; i < enemyCount; i++) {
var baseHealth = 100; // Basic enemy health
if (currentWave > 3) baseHealth = 80; // Mix of basic and fast
if (currentWave > 6) baseHealth = 120; // Mix of all types
totalEnemyHealth += baseHealth * (1 + waveMultiplier * 0.5);
}
var boss = new Enemy('boss', 0);
boss.x = gamePath[0].x;
boss.y = gamePath[0].y;
boss.maxHealth = totalEnemyHealth;
boss.health = totalEnemyHealth;
boss.reward = 200;
enemies.push(boss);
game.addChild(boss);
bossSpawned = true;
}
function showTowerInfo(tower) {
if (!tower) return;
if (towerInfoPanel) {
towerInfoPanel.destroy();
}
var panel = LK.getAsset('base', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2,
scaleY: 1.5
});
panel.tint = 0x333333;
panel.alpha = 0.8;
panel.x = tower.x;
panel.y = Math.max(150, tower.y - 150);
game.addChild(panel);
towerInfoPanel = panel;
var infoText = new Text2('Level: ' + tower.level + '\nDamage: ' + tower.damage + '\nUpgrade: ' + tower.upgradeCost, {
size: 25,
fill: 0xFFFFFF
});
infoText.anchor.set(0.5, 0.5);
infoText.x = 0;
infoText.y = -25;
panel.addChild(infoText);
if (coins >= tower.upgradeCost) {
var upgradeBtn = new Text2('UPGRADE', {
size: 30,
fill: 0x00FF00
});
upgradeBtn.anchor.set(0.5, 0.5);
upgradeBtn.y = 35;
panel.addChild(upgradeBtn);
}
}
// Event handlers
basicTowerBtn.down = function () {
selectedTowerType = 'basic';
if (selectedTower) {
selectedTower.hideRange();
selectedTower = null;
}
if (towerInfoPanel) {
towerInfoPanel.destroy();
towerInfoPanel = null;
}
};
splashTowerBtn.down = function () {
selectedTowerType = 'splash';
if (selectedTower) {
selectedTower.hideRange();
selectedTower = null;
}
if (towerInfoPanel) {
towerInfoPanel.destroy();
towerInfoPanel = null;
}
};
slowTowerBtn.down = function () {
selectedTowerType = 'slow';
if (selectedTower) {
selectedTower.hideRange();
selectedTower = null;
}
if (towerInfoPanel) {
towerInfoPanel.destroy();
towerInfoPanel = null;
}
};
game.down = function (x, y, obj) {
// Check if clicking on upgrade button
if (towerInfoPanel && selectedTower && obj && obj.position && obj.parent) {
try {
var localPos = towerInfoPanel.toLocal(obj.parent.toGlobal(obj.position));
if (Math.abs(localPos.x) < 60 && Math.abs(localPos.y - 30) < 20 && coins >= selectedTower.upgradeCost) {
selectedTower.upgrade();
showTowerInfo(selectedTower);
return;
}
} catch (e) {
// Ignore positioning errors
}
}
// Hide tower info if clicking elsewhere
if (selectedTower) {
selectedTower.hideRange();
selectedTower = null;
}
if (towerInfoPanel) {
towerInfoPanel.destroy();
towerInfoPanel = null;
}
// Try to place tower
var towerCost = selectedTowerType === 'basic' ? 100 : selectedTowerType === 'splash' ? 200 : 150;
if (coins >= towerCost && canPlaceTower(x, y)) {
var tower = new Tower(selectedTowerType);
tower.x = x;
tower.y = y;
towers.push(tower);
game.addChild(tower);
coins -= towerCost;
LK.getSound('place').play();
}
};
// Main game loop
game.update = function () {
// Spawn enemies
if (bossWave) {
// Boss wave spawning
if (waveDelay <= 0 && !bossSpawned) {
spawnBossEnemy();
waveDelay = 0;
} else {
waveDelay--;
}
} else {
// Normal wave spawning
if (waveDelay <= 0 && enemiesSpawned < enemiesPerWave * currentWave) {
spawnEnemy();
waveDelay = 60; // 1 second between spawns
} else {
waveDelay--;
}
}
// Check wave completion
if (bossWave) {
// Boss wave completion
if (bossSpawned && enemies.length === 0) {
currentWave++;
enemiesSpawned = 0;
coins += 100 * currentWave; // Bonus coins for completing boss wave
waveDelay = 300; // 5 second break after boss
bossWave = false;
bossSpawned = false;
// Only extend path length every 5 waves, keep towers
if (currentWave % 5 === 1) {
extendPathOnly();
}
}
} else {
// Normal wave completion
if (enemiesSpawned >= enemiesPerWave * currentWave && enemies.length === 0) {
// Check if this completes a set of 5 waves
if (currentWave % 5 === 0) {
// Start boss wave
bossWave = true;
bossSpawned = false;
waveDelay = 180; // 3 second break before boss
} else {
// Normal wave progression
currentWave++;
enemiesSpawned = 0;
coins += 25 * currentWave; // Bonus coins for completing wave
waveDelay = 180; // 3 second break between waves
}
}
}
// Update enemies
for (var i = enemies.length - 1; i >= 0; i--) {
var enemy = enemies[i];
if (enemy.pathIndex >= gamePath.length - 1) {
// Enemy reached base
baseHealth--;
enemy.destroy();
enemies.splice(i, 1);
}
}
// Update towers
for (var i = 0; i < towers.length; i++) {
towers[i].update();
}
// Update bullets
for (var i = 0; i < bullets.length; i++) {
bullets[i].update();
}
// Check game over
if (baseHealth <= 0) {
LK.setScore(currentWave - 1);
LK.showGameOver();
}
// Update UI
updateUI();
};