User prompt
When upgrading base, splash, and slow towers, update the bonuses of newly placed towers to the new damage value.
User prompt
Adjust the damage dealt by the towers according to their level
User prompt
oyundaki boss yaratıklarının canını %10 azalt
User prompt
oyundaki boss yaratıklarının canını %20 azalt
User prompt
ekranın sağ üst tarafına oyundaki tüm sesleri açma kapatma kısmı koyulsun
User prompt
bu müziğin ses seviyesini %50 düşür
User prompt
yüklediğim muzik isimli müzik dosyası oyun başladığı anda arka planda sürekli çalsın
User prompt
oyundaki boss yaratıklarının canını 2 katı yap
User prompt
yol uzunluğunu yazan yazıyı kaldır. boss health yazısını yukarı kaydır
User prompt
"Select Wave" isimli açılır menüyü yok et
User prompt
Base Health değerini 50 yap
User prompt
oyundaki boss yaratıklarının canını 3 katı yap
User prompt
oyundaki boss yaratıklarının canını 10 kat arttır
User prompt
boss isimli kaydettiğim resmi oyundaki boss resmi yap
User prompt
oyundaki boss yaratığı çıkınca canının kaç olduğunu üst tarafa yaz
User prompt
oyunun boss kısımlarında çıkan bossların gücünü 5 kat arttır
User prompt
oyunda düzeltme yapmak istiyorum. boss olarak çıkanların canını 5 kat arttır
User prompt
boss ların canı 5 katına çıksın
User prompt
oyundaki boss ların canını 2 kat arttır
User prompt
boss isimli kaydettiğim resmi oyundakş boss resmi yap
User prompt
en üst sağdaki Base Health değeri 100 den 50 ye düşür
User prompt
Base health canı 100 den 50 ye düşsün
User prompt
base health 50 olsun
User prompt
boss ların canları 3 katına çıksın
User prompt
yolun uzunluğu 1. dalga 3000 px, 6. dalga 3500 px, 11. dalga 4000 px, 16. dalga 5000 px olsun
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.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 with stricter bounds to prevent visual issues
if (self.x < -50 || self.x > 2098 || self.y < -50 || self.y > 2782) {
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
});
}
});
// Update boss health display if this is a boss during boss wave
if (bossWave && bossSpawned && bossHealthText.visible) {
bossHealthText.setText('Boss Health: ' + Math.max(0, self.health));
}
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;
var newX = self.x + dx / distance * moveSpeed;
var newY = self.y + dy / distance * moveSpeed;
// Ensure enemy stays within screen bounds with safety margin
var enemySafetyMargin = 50;
self.x = Math.max(enemySafetyMargin, Math.min(2048 - enemySafetyMargin, newX));
self.y = Math.max(enemySafetyMargin, Math.min(2732 - enemySafetyMargin, newY));
}
} 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);
// Update global tower info stats
towerInfoStats[self.type].level = Math.max(towerInfoStats[self.type].level, self.level);
towerInfoStats[self.type].damage = Math.max(towerInfoStats[self.type].damage, self.damage);
towerInfoStats[self.type].upgradeCost = Math.max(towerInfoStats[self.type].upgradeCost, self.upgradeCost);
// 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();
};
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
****/
// Sounds
// Game elements
// Enemy assets
// Tower assets
// Game variables
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 = 100;
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;
var isInitialPath = true;
var wavePathLengths = storage.wavePathLengths || []; // Track path length for each wave - load from database
var previousWavePathLength = storage.previousWavePathLength || 3000; // Track path length from previous wave - load from database
// Initialize empty path - will be generated when wave starts
// Create base and baslangic placeholders - will be positioned when path is generated
var base = LK.getAsset('base', {
anchorX: 0.5,
anchorY: 0.5
});
base.x = 1800;
base.y = 1600;
game.addChild(base);
// Create baslangic image at spawn point
var baslangicImage = LK.getAsset('baslangic', {
anchorX: 0.5,
anchorY: 0.5
});
baslangicImage.x = 200;
baslangicImage.y = 1000;
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);
var pathLengthText = new Text2('Path Length: 0', {
size: 40,
fill: 0x00FFFF
});
pathLengthText.anchor.set(0.5, 0);
pathLengthText.y = 80;
LK.gui.top.addChild(pathLengthText);
var bossHealthText = new Text2('', {
size: 45,
fill: 0xFF0000
});
bossHealthText.anchor.set(0.5, 0);
bossHealthText.y = 130;
bossHealthText.visible = false;
LK.gui.top.addChild(bossHealthText);
// Wave selector dropdown
var waveDropdownBtn = new Text2('Select Wave ▼', {
size: 40,
fill: 0x00FF00
});
waveDropdownBtn.anchor.set(0.5, 0);
waveDropdownBtn.x = 0;
waveDropdownBtn.y = 170;
LK.gui.top.addChild(waveDropdownBtn);
// Dropdown container (initially hidden)
var dropdownContainer = new Container();
dropdownContainer.visible = false;
dropdownContainer.x = 0;
dropdownContainer.y = 220;
LK.gui.top.addChild(dropdownContainer);
// Dropdown background
var dropdownBg = LK.getAsset('pathTile', {
anchorX: 0.5,
anchorY: 0,
scaleX: 4,
scaleY: 8
});
dropdownBg.tint = 0x333333;
dropdownBg.alpha = 0.9;
dropdownContainer.addChild(dropdownBg);
// Wave selection buttons (create 20 wave options)
var waveButtons = [];
for (var i = 1; i <= 20; i++) {
var waveBtn = new Text2('Wave ' + i, {
size: 35,
fill: 0xFFFFFF
});
waveBtn.anchor.set(0.5, 0);
waveBtn.x = 0;
waveBtn.y = (i - 1) * 45 + 10;
waveBtn.waveNumber = i;
dropdownContainer.addChild(waveBtn);
waveButtons.push(waveBtn);
}
var dropdownVisible = false;
// Tower selection buttons
var basicTowerImage = LK.getAsset('basicTower', {
anchorX: 0.5,
anchorY: 1,
scaleX: 0.8,
scaleY: 0.8
});
basicTowerImage.x = 60;
basicTowerImage.y = -20;
LK.gui.bottomLeft.addChild(basicTowerImage);
var basicTowerBtn = new Text2('Basic (100)', {
size: 40,
fill: 0x4444FF
});
basicTowerBtn.anchor.set(0, 1);
basicTowerBtn.x = 100;
basicTowerBtn.y = -20;
LK.gui.bottomLeft.addChild(basicTowerBtn);
var basicTowerInfo = new Text2('Level: 1 | DMG: 25 | Upgrade: 75', {
size: 25,
fill: 0xFFFFFF
});
basicTowerInfo.anchor.set(0, 1);
basicTowerInfo.x = 350;
basicTowerInfo.y = -20;
LK.gui.bottomLeft.addChild(basicTowerInfo);
var splashTowerImage = LK.getAsset('splashTower', {
anchorX: 0.5,
anchorY: 1,
scaleX: 0.8,
scaleY: 0.8
});
splashTowerImage.x = 60;
splashTowerImage.y = -80;
LK.gui.bottomLeft.addChild(splashTowerImage);
var splashTowerBtn = new Text2('Splash (200)', {
size: 40,
fill: 0xFF4444
});
splashTowerBtn.anchor.set(0, 1);
splashTowerBtn.x = 100;
splashTowerBtn.y = -80;
LK.gui.bottomLeft.addChild(splashTowerBtn);
var splashTowerInfo = new Text2('Level: 1 | DMG: 40 | Upgrade: 150', {
size: 25,
fill: 0xFFFFFF
});
splashTowerInfo.anchor.set(0, 1);
splashTowerInfo.x = 350;
splashTowerInfo.y = -80;
LK.gui.bottomLeft.addChild(splashTowerInfo);
var slowTowerImage = LK.getAsset('slowTower', {
anchorX: 0.5,
anchorY: 1,
scaleX: 0.8,
scaleY: 0.8
});
slowTowerImage.x = 60;
slowTowerImage.y = -140;
LK.gui.bottomLeft.addChild(slowTowerImage);
var slowTowerBtn = new Text2('Slow (150)', {
size: 40,
fill: 0x44FF44
});
slowTowerBtn.anchor.set(0, 1);
slowTowerBtn.x = 100;
slowTowerBtn.y = -140;
LK.gui.bottomLeft.addChild(slowTowerBtn);
var slowTowerInfo = new Text2('Level: 1 | DMG: 15 | Upgrade: 100', {
size: 25,
fill: 0xFFFFFF
});
slowTowerInfo.anchor.set(0, 1);
slowTowerInfo.x = 350;
slowTowerInfo.y = -140;
LK.gui.bottomLeft.addChild(slowTowerInfo);
// Add levelatla upgrade buttons
var basicLevelatlaBtn = LK.getAsset('levelatla', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.3,
scaleY: 0.3
});
basicLevelatlaBtn.x = 770;
basicLevelatlaBtn.y = -40;
LK.gui.bottomLeft.addChild(basicLevelatlaBtn);
var splashLevelatlaBtn = LK.getAsset('levelatla', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.3,
scaleY: 0.3
});
splashLevelatlaBtn.x = 770;
splashLevelatlaBtn.y = -100;
LK.gui.bottomLeft.addChild(splashLevelatlaBtn);
var slowLevelatlaBtn = LK.getAsset('levelatla', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.3,
scaleY: 0.3
});
slowLevelatlaBtn.x = 770;
slowLevelatlaBtn.y = -160;
LK.gui.bottomLeft.addChild(slowLevelatlaBtn);
// Selected tower type
var selectedTowerType = 'basic';
// Tower info tracking
var towerInfoStats = {
basic: {
level: 1,
damage: 25,
upgradeCost: 75
},
splash: {
level: 1,
damage: 40,
upgradeCost: 150
},
slow: {
level: 1,
damage: 15,
upgradeCost: 100
}
};
// Add background highlights for tower selection
var basicTowerBg = LK.getAsset('pathTile', {
anchorX: 0,
anchorY: 0.5,
scaleX: 8.0,
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: 8.0,
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: 8.0,
scaleY: 0.8
});
slowTowerBg.tint = 0x00FF00;
slowTowerBg.alpha = 0;
slowTowerBg.x = 10;
slowTowerBg.y = -160;
LK.gui.bottomLeft.addChild(slowTowerBg);
// Helper functions
function calculatePathLength() {
if (!gamePath || gamePath.length < 2) return 0;
var totalLength = 0;
for (var i = 0; i < gamePath.length - 1; i++) {
var dx = gamePath[i + 1].x - gamePath[i].x;
var dy = gamePath[i + 1].y - gamePath[i].y;
totalLength += Math.sqrt(dx * dx + dy * dy);
}
return Math.round(totalLength);
}
function updateUI() {
coinsText.setText('Coins: ' + coins);
healthText.setText('Base Health: ' + baseHealth);
waveText.setText('Wave: ' + currentWave + (bossWave ? ' (BOSS)' : ''));
pathLengthText.setText('Path Length: ' + calculatePathLength() + ' px');
// "6th wave" text display removed per request
// 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 info displays
basicTowerInfo.setText('Level: ' + towerInfoStats.basic.level + ' | DMG: ' + towerInfoStats.basic.damage + ' | Upgrade: ' + towerInfoStats.basic.upgradeCost);
splashTowerInfo.setText('Level: ' + towerInfoStats.splash.level + ' | DMG: ' + towerInfoStats.splash.damage + ' | Upgrade: ' + towerInfoStats.splash.upgradeCost);
slowTowerInfo.setText('Level: ' + towerInfoStats.slow.level + ' | DMG: ' + towerInfoStats.slow.damage + ' | Upgrade: ' + towerInfoStats.slow.upgradeCost);
// 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;
}
// Show/hide levelatla buttons based on available coins
basicLevelatlaBtn.visible = coins >= towerInfoStats.basic.upgradeCost;
splashLevelatlaBtn.visible = coins >= towerInfoStats.splash.upgradeCost;
slowLevelatlaBtn.visible = coins >= towerInfoStats.slow.upgradeCost;
}
function canPlaceTower(x, y) {
// First check if position is within the blue frame boundaries
var screenWidth = 2048;
var screenHeight = 2732;
var marginPercent = 0.08; // 8% margin from sides and top
var bottomMarginPercent = 0.13; // 13% margin from bottom (8% + 5% increase)
var pathMinX = Math.floor(screenWidth * marginPercent); // Blue frame left boundary
var pathMaxX = screenWidth - Math.floor(screenWidth * marginPercent); // Blue frame right boundary
var pathMinY = Math.floor(screenHeight * marginPercent); // Blue frame top boundary
var pathMaxY = screenHeight - Math.floor(screenHeight * bottomMarginPercent); // Blue frame bottom boundary
// Check if tower position is within blue frame boundaries
if (x < pathMinX || x > pathMaxX || y < pathMinY || y > pathMaxY) {
return false; // Outside blue frame area
}
// 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 generateNewPath() {
// 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--;
}
}
// Use blue frame boundaries for path generation (same as tower placement restrictions)
var screenWidth = 2048;
var screenHeight = 2732;
var marginPercent = 0.08; // 8% margin from sides and top
var bottomMarginPercent = 0.13; // 13% margin from bottom (8% + 5% increase)
var minX = Math.floor(screenWidth * marginPercent); // Blue frame left boundary
var maxX = screenWidth - Math.floor(screenWidth * marginPercent); // Blue frame right boundary
var minY = Math.floor(screenHeight * marginPercent); // Blue frame top boundary
var maxY = screenHeight - Math.floor(screenHeight * bottomMarginPercent); // Blue frame bottom boundary
// Set path length based on wave with specific requirements
var targetPathLength;
if (currentWave === 1) {
targetPathLength = 3000; // Wave 1 is exactly 3000 pixels
} else if (currentWave === 6) {
targetPathLength = 3500; // Wave 6 is exactly 3500 pixels
} else if (currentWave === 11) {
targetPathLength = 4000; // Wave 11 is exactly 4000 pixels
} else if (currentWave === 16) {
targetPathLength = 5000; // Wave 16 is exactly 5000 pixels
} else {
// For other waves, use the actual path length from previous wave plus 300px
var prevWaveLength = wavePathLengths.length >= currentWave - 1 ? wavePathLengths[currentWave - 2] : previousWavePathLength;
targetPathLength = prevWaveLength + 300;
}
// New completely random path generation system
gamePath = [];
// Random starting position on the edges of blue frame for better space utilization
var edge = Math.floor(Math.random() * 4); // 0=top, 1=right, 2=bottom, 3=left
var startX, startY;
if (edge === 0) {
// top edge
startX = minX + 100 + Math.random() * (maxX - minX - 200);
startY = minY + 100;
} else if (edge === 1) {
// right edge
startX = maxX - 100;
startY = minY + 100 + Math.random() * (maxY - minY - 200);
} else if (edge === 2) {
// bottom edge
startX = minX + 100 + Math.random() * (maxX - minX - 200);
startY = maxY - 100;
} else {
// left edge
startX = minX + 100;
startY = minY + 100 + Math.random() * (maxY - minY - 200);
}
gamePath.push({
x: startX,
y: startY
});
var currentX = startX;
var currentY = startY;
var totalLength = 0;
var lastDirection = -1; // Track last direction to encourage turns
// Helper function to check if two line segments intersect
function segmentsIntersect(x1, y1, x2, y2, x3, y3, x4, y4) {
var denominator = (x2 - x1) * (y4 - y3) - (y2 - y1) * (x4 - x3);
if (Math.abs(denominator) < 0.0001) return false; // Parallel lines
var t1 = ((x3 - x1) * (y4 - y3) - (y3 - y1) * (x4 - x3)) / denominator;
var t2 = ((x3 - x1) * (y2 - y1) - (y3 - y1) * (x2 - x1)) / denominator;
return t1 > 0.01 && t1 < 0.99 && t2 > 0.01 && t2 < 0.99; // Exclude endpoints
}
// Helper function to check if a new segment would intersect with existing path
function wouldIntersectPath(fromX, fromY, toX, toY, currentPath) {
// Check against all existing segments in the path
for (var i = 0; i < currentPath.length - 1; i++) {
var segX1 = currentPath[i].x;
var segY1 = currentPath[i].y;
var segX2 = currentPath[i + 1].x;
var segY2 = currentPath[i + 1].y;
// Skip if checking against the last segment (which connects to current position)
if (i === currentPath.length - 2) continue;
if (segmentsIntersect(fromX, fromY, toX, toY, segX1, segY1, segX2, segY2)) {
return true;
}
}
return false;
}
// Generate random path segments until we reach target length
var maxAttempts = 200; // Increase attempts for more complex paths
while (totalLength < targetPathLength - 200 && maxAttempts > 0) {
var validSegmentFound = false;
var attempts = 0;
while (!validSegmentFound && attempts < 30) {
// Prefer directions that create turns (different from last direction)
var direction;
if (lastDirection >= 0 && Math.random() < 0.7) {
// 70% chance to turn
// Choose a different direction than last
var availableDirections = [0, 1, 2, 3].filter(function (d) {
return d !== lastDirection && d !== (lastDirection + 2) % 4;
});
direction = availableDirections[Math.floor(Math.random() * availableDirections.length)];
} else {
direction = Math.floor(Math.random() * 4);
}
// Vary segment lengths more - shorter segments for more curves
var segmentLength = 100 + Math.random() * 300;
// Make segments shorter near edges to allow more maneuvering
var edgeDistance = Math.min(currentX - minX, maxX - currentX, currentY - minY, maxY - currentY);
if (edgeDistance < 300) {
segmentLength = Math.min(segmentLength, 100 + Math.random() * 150);
}
// Make sure we don't exceed remaining target length
var remainingLength = targetPathLength - totalLength;
if (segmentLength > remainingLength - 100) {
segmentLength = Math.max(100, remainingLength - 100);
}
var nextX = currentX;
var nextY = currentY;
// Calculate next position based on random direction
if (direction === 0) {
// right
nextX = currentX + segmentLength;
} else if (direction === 1) {
// down
nextY = currentY + segmentLength;
} else if (direction === 2) {
// left
nextX = currentX - segmentLength;
} else if (direction === 3) {
// up
nextY = currentY - segmentLength;
}
// Ensure path stays within blue frame boundaries with minimum distance from edges
var minDistanceFromEdge = 50;
nextX = Math.max(minX + minDistanceFromEdge, Math.min(maxX - minDistanceFromEdge, nextX));
nextY = Math.max(minY + minDistanceFromEdge, Math.min(maxY - minDistanceFromEdge, nextY));
// Check minimum distance between path points (not too close to existing points)
var tooClose = false;
for (var i = 0; i < gamePath.length; i++) {
var dist = Math.sqrt(Math.pow(nextX - gamePath[i].x, 2) + Math.pow(nextY - gamePath[i].y, 2));
if (dist < 200) {
// Minimum 200px between path points
tooClose = true;
break;
}
}
// Check if this segment would intersect with existing path and not too close
if (!tooClose && !wouldIntersectPath(currentX, currentY, nextX, nextY, gamePath)) {
validSegmentFound = true;
// Add the new point to path
gamePath.push({
x: nextX,
y: nextY
});
// Update current position and total length
var segmentDx = nextX - currentX;
var segmentDy = nextY - currentY;
var actualSegmentLength = Math.sqrt(segmentDx * segmentDx + segmentDy * segmentDy);
totalLength += actualSegmentLength;
currentX = nextX;
currentY = nextY;
lastDirection = direction;
}
attempts++;
}
if (!validSegmentFound) {
// If we can't find a valid segment, try backtracking
if (gamePath.length > 2) {
// Remove last point and try again
gamePath.pop();
var prevPoint = gamePath[gamePath.length - 1];
currentX = prevPoint.x;
currentY = prevPoint.y;
// Recalculate total length
totalLength = 0;
for (var i = 0; i < gamePath.length - 1; i++) {
var dx = gamePath[i + 1].x - gamePath[i].x;
var dy = gamePath[i + 1].y - gamePath[i].y;
totalLength += Math.sqrt(dx * dx + dy * dy);
}
} else {
break;
}
}
maxAttempts--;
// Safety check to prevent infinite loops
if (gamePath.length > 80) break; // Allow more segments for complex paths
}
// Add final segment to reach target length and preferably opposite side
if (totalLength < targetPathLength - 50 && gamePath.length > 1) {
var finalAttempts = 0;
var finalSegmentAdded = false;
// Try to end on opposite edge from start
var startEdge = -1;
if (gamePath[0].y < minY + 200) startEdge = 0; // started at top
else if (gamePath[0].x > maxX - 200) startEdge = 1; // started at right
else if (gamePath[0].y > maxY - 200) startEdge = 2; // started at bottom
else if (gamePath[0].x < minX + 200) startEdge = 3; // started at left
while (!finalSegmentAdded && finalAttempts < 20) {
var finalX = currentX;
var finalY = currentY;
var remainingLength = targetPathLength - totalLength;
// Try to reach opposite edge
if (startEdge >= 0 && finalAttempts < 10) {
var targetEdge = (startEdge + 2) % 4; // Opposite edge
if (targetEdge === 0) {
// top edge
finalY = minY + 100;
finalX = currentX + (Math.random() - 0.5) * 400;
} else if (targetEdge === 1) {
// right edge
finalX = maxX - 100;
finalY = currentY + (Math.random() - 0.5) * 400;
} else if (targetEdge === 2) {
// bottom edge
finalY = maxY - 100;
finalX = currentX + (Math.random() - 0.5) * 400;
} else {
// left edge
finalX = minX + 100;
finalY = currentY + (Math.random() - 0.5) * 400;
}
} else {
// Random direction if opposite edge fails
var finalDirection = Math.floor(Math.random() * 4);
if (finalDirection === 0) {
finalX = Math.min(maxX - 100, currentX + remainingLength);
} else if (finalDirection === 1) {
finalY = Math.min(maxY - 100, currentY + remainingLength);
} else if (finalDirection === 2) {
finalX = Math.max(minX + 100, currentX - remainingLength);
} else {
finalY = Math.max(minY + 100, currentY - remainingLength);
}
}
// Ensure final position is within bounds
finalX = Math.max(minX + 100, Math.min(maxX - 100, finalX));
finalY = Math.max(minY + 100, Math.min(maxY - 100, finalY));
// Check if final segment would intersect with existing path
if (!wouldIntersectPath(currentX, currentY, finalX, finalY, gamePath)) {
gamePath.push({
x: finalX,
y: finalY
});
finalSegmentAdded = true;
}
finalAttempts++;
}
}
// Ensure gamePath has valid elements
if (!gamePath || gamePath.length === 0) {
// Fallback path if generation failed
gamePath = [{
x: 100,
y: 400
}, {
x: 1800,
y: 1600
}];
}
// Draw path tiles using circles for smoother appearance
for (var i = 0; i < gamePath.length; i++) {
var pathTile = LK.getAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 3.75,
scaleY: 3.75
});
pathTile.x = gamePath[i].x;
pathTile.y = gamePath[i].y;
pathTile.alpha = 0.9;
pathTile.tint = 0x8B4513;
game.addChild(pathTile);
}
// Draw connecting segments with circles for smoother road
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 / 20);
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('bullet', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 3.75,
scaleY: 3.75
});
segmentTile.x = segmentX;
segmentTile.y = segmentY;
segmentTile.alpha = 0.8;
segmentTile.tint = 0x8B4513;
game.addChild(segmentTile);
}
}
// Update base position to match final path endpoint exactly
if (base && gamePath.length > 0) {
// Set base position to exactly match the final path point
base.x = gamePath[gamePath.length - 1].x;
base.y = gamePath[gamePath.length - 1].y;
}
// Update baslangic position to new start position
if (baslangicImage) {
// Ensure baslangic stays within screen bounds with safety margin
var baslangicSafetyMargin = 100;
baslangicImage.x = Math.max(baslangicSafetyMargin, Math.min(2048 - baslangicSafetyMargin, gamePath[0].x));
baslangicImage.y = Math.max(baslangicSafetyMargin, Math.min(2732 - baslangicSafetyMargin, gamePath[0].y));
}
// Ensure base and baslangic stay in foreground
if (base) game.addChild(base);
if (baslangicImage) game.addChild(baslangicImage);
// Create path area border frame (10px thickness, blue color, in foreground)
// Calculate path area boundaries with 8% margin from sides/top, 13% margin from bottom (same as path generation)
var pathAreaMinX = Math.floor(screenWidth * marginPercent); // 8% from left
var pathAreaMaxX = screenWidth - Math.floor(screenWidth * marginPercent); // 8% from right
var pathAreaMinY = Math.floor(screenHeight * marginPercent); // 8% from top
var pathAreaMaxY = screenHeight - Math.floor(screenHeight * bottomMarginPercent); // 13% from bottom
// Top path area border
var pathAreaTopBorder = LK.getAsset('pathTile', {
anchorX: 0,
anchorY: 0,
scaleX: (pathAreaMaxX - pathAreaMinX) / 100,
scaleY: 0.1
});
pathAreaTopBorder.x = pathAreaMinX;
pathAreaTopBorder.y = pathAreaMinY;
pathAreaTopBorder.tint = 0x0000FF;
game.addChild(pathAreaTopBorder);
// Bottom path area border
var pathAreaBottomBorder = LK.getAsset('pathTile', {
anchorX: 0,
anchorY: 0,
scaleX: (pathAreaMaxX - pathAreaMinX) / 100,
scaleY: 0.1
});
pathAreaBottomBorder.x = pathAreaMinX;
pathAreaBottomBorder.y = pathAreaMaxY - 10;
pathAreaBottomBorder.tint = 0x0000FF;
game.addChild(pathAreaBottomBorder);
// Left path area border
var pathAreaLeftBorder = LK.getAsset('pathTile', {
anchorX: 0,
anchorY: 0,
scaleX: 0.1,
scaleY: (pathAreaMaxY - pathAreaMinY) / 100
});
pathAreaLeftBorder.x = pathAreaMinX;
pathAreaLeftBorder.y = pathAreaMinY;
pathAreaLeftBorder.tint = 0x0000FF;
game.addChild(pathAreaLeftBorder);
// Right path area border
var pathAreaRightBorder = LK.getAsset('pathTile', {
anchorX: 0,
anchorY: 0,
scaleX: 0.1,
scaleY: (pathAreaMaxY - pathAreaMinY) / 100
});
pathAreaRightBorder.x = pathAreaMaxX - 10;
pathAreaRightBorder.y = pathAreaMinY;
pathAreaRightBorder.tint = 0x0000FF;
game.addChild(pathAreaRightBorder);
// Update previous wave path length for waves after Wave 1
if (currentWave > 1) {
var actualPathLength = calculatePathLength();
if (actualPathLength > 0) {
previousWavePathLength = actualPathLength;
wavePathLengths[currentWave - 1] = actualPathLength; // Store current wave length
// Save to database
storage.previousWavePathLength = previousWavePathLength;
storage.wavePathLengths = wavePathLengths;
}
}
}
function startNextWave() {
// Generate new path when wave starts
generateNewPath();
// Ensure gamePath has valid elements after generation
if (!gamePath || gamePath.length === 0) {
// Fallback path if generation failed
gamePath = [{
x: 100,
y: 400
}, {
x: 1800,
y: 1600
}];
}
// Update base position to match path endpoint exactly
if (base && gamePath.length > 0) {
// Set base position to exactly match the final path point
base.x = gamePath[gamePath.length - 1].x;
base.y = gamePath[gamePath.length - 1].y;
}
if (baslangicImage) {
// Ensure baslangic stays within screen bounds with safety margin
var baslangicSafetyMargin = 100;
baslangicImage.x = Math.max(baslangicSafetyMargin, Math.min(2048 - baslangicSafetyMargin, gamePath[0].x));
baslangicImage.y = Math.max(baslangicSafetyMargin, Math.min(2732 - baslangicSafetyMargin, gamePath[0].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);
}
// Reduce boss health by 90% (keep only 10% of original health)
var bossHealth = Math.floor(totalEnemyHealth * 0.1);
var boss = new Enemy('boss', 0);
boss.x = gamePath[0].x;
boss.y = gamePath[0].y;
boss.maxHealth = bossHealth;
boss.health = bossHealth;
boss.reward = 200;
enemies.push(boss);
game.addChild(boss);
bossSpawned = true;
// Show boss health display
bossHealthText.setText('Boss Health: ' + boss.health);
bossHealthText.visible = 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;
// Ensure panel stays completely within screen bounds
var panelWidth = 120 * 2; // base width * scaleX
var panelHeight = 120 * 1.5; // base height * scaleY
var panelMargin = 50;
panel.x = Math.max(panelWidth / 2 + panelMargin, Math.min(2048 - panelWidth / 2 - panelMargin, tower.x));
panel.y = Math.max(panelHeight / 2 + panelMargin, Math.min(2732 - panelHeight / 2 - panelMargin, 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);
}
}
// Dropdown toggle handler
waveDropdownBtn.down = function () {
dropdownVisible = !dropdownVisible;
dropdownContainer.visible = dropdownVisible;
waveDropdownBtn.setText(dropdownVisible ? 'Select Wave ▲' : 'Select Wave ▼');
};
// Wave button handlers
for (var i = 0; i < waveButtons.length; i++) {
waveButtons[i].down = function (waveNum) {
return function () {
// Jump to selected wave
jumpToWave(waveNum);
// Hide dropdown
dropdownVisible = false;
dropdownContainer.visible = false;
waveDropdownBtn.setText('Select Wave ▼');
};
}(waveButtons[i].waveNumber);
}
// Function to jump to specific wave
function jumpToWave(targetWave) {
if (targetWave < 1) targetWave = 1;
// Clear current enemies and bullets
for (var i = enemies.length - 1; i >= 0; i--) {
enemies[i].destroy();
}
enemies = [];
for (var i = bullets.length - 1; i >= 0; i--) {
bullets[i].destroy();
}
bullets = [];
// Clear all towers
clearAllTowers();
// Set wave variables
currentWave = targetWave;
enemiesSpawned = 0;
bossWave = currentWave % 5 === 0;
bossSpawned = false;
waveDelay = 60;
// Reset base health and give appropriate coins for the wave
baseHealth = 100;
coins = 300 + (targetWave - 1) * 100; // Give coins based on wave
// Update path lengths array for target wave
while (wavePathLengths.length < targetWave) {
var baseLength = wavePathLengths.length > 0 ? wavePathLengths[wavePathLengths.length - 1] : 3000;
wavePathLengths.push(baseLength + 300);
}
if (targetWave > 1) {
previousWavePathLength = wavePathLengths[targetWave - 2];
} else {
previousWavePathLength = 3000;
}
// Save to database
storage.wavePathLengths = wavePathLengths;
storage.previousWavePathLength = previousWavePathLength;
// Generate new path for target wave
startNextWave();
}
// Event handlers
basicTowerBtn.down = function () {
selectedTowerType = 'basic';
if (selectedTower) {
selectedTower.hideRange();
selectedTower = null;
}
};
splashTowerBtn.down = function () {
selectedTowerType = 'splash';
if (selectedTower) {
selectedTower.hideRange();
selectedTower = null;
}
};
slowTowerBtn.down = function () {
selectedTowerType = 'slow';
if (selectedTower) {
selectedTower.hideRange();
selectedTower = null;
}
};
// Add click handlers for levelatla upgrade buttons
basicLevelatlaBtn.down = function () {
if (coins >= towerInfoStats.basic.upgradeCost) {
coins -= towerInfoStats.basic.upgradeCost;
towerInfoStats.basic.level++;
towerInfoStats.basic.damage = Math.floor(towerInfoStats.basic.damage * 1.5);
towerInfoStats.basic.upgradeCost = Math.floor(towerInfoStats.basic.upgradeCost * 1.8);
}
};
splashLevelatlaBtn.down = function () {
if (coins >= towerInfoStats.splash.upgradeCost) {
coins -= towerInfoStats.splash.upgradeCost;
towerInfoStats.splash.level++;
towerInfoStats.splash.damage = Math.floor(towerInfoStats.splash.damage * 1.5);
towerInfoStats.splash.upgradeCost = Math.floor(towerInfoStats.splash.upgradeCost * 1.8);
}
};
slowLevelatlaBtn.down = function () {
if (coins >= towerInfoStats.slow.upgradeCost) {
coins -= towerInfoStats.slow.upgradeCost;
towerInfoStats.slow.level++;
towerInfoStats.slow.damage = Math.floor(towerInfoStats.slow.damage * 1.5);
towerInfoStats.slow.upgradeCost = Math.floor(towerInfoStats.slow.upgradeCost * 1.8);
}
};
game.down = function (x, y, obj) {
// Hide dropdown if clicking on game area
if (dropdownVisible) {
dropdownVisible = false;
dropdownContainer.visible = false;
waveDropdownBtn.setText('Select Wave ▼');
}
// Hide tower range if clicking elsewhere
if (selectedTower) {
selectedTower.hideRange();
selectedTower = 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;
// Update tower info stats
towerInfoStats[selectedTowerType].level = Math.max(towerInfoStats[selectedTowerType].level, tower.level);
towerInfoStats[selectedTowerType].damage = Math.max(towerInfoStats[selectedTowerType].damage, tower.damage);
towerInfoStats[selectedTowerType].upgradeCost = Math.max(towerInfoStats[selectedTowerType].upgradeCost, tower.upgradeCost);
LK.getSound('place').play();
}
};
// Create black border frame around the game area (10px thickness)
// Top border
var topBorder = LK.getAsset('pathTile', {
anchorX: 0,
anchorY: 0,
scaleX: 20.48,
scaleY: 0.1
});
topBorder.x = 0;
topBorder.y = 0;
topBorder.tint = 0x000000;
game.addChild(topBorder);
// Bottom border
var bottomBorder = LK.getAsset('pathTile', {
anchorX: 0,
anchorY: 0,
scaleX: 20.48,
scaleY: 0.1
});
bottomBorder.x = 0;
bottomBorder.y = 2722;
bottomBorder.tint = 0x000000;
game.addChild(bottomBorder);
// Left border
var leftBorder = LK.getAsset('pathTile', {
anchorX: 0,
anchorY: 0,
scaleX: 0.1,
scaleY: 27.32
});
leftBorder.x = 0;
leftBorder.y = 0;
leftBorder.tint = 0x000000;
game.addChild(leftBorder);
// Right border
var rightBorder = LK.getAsset('pathTile', {
anchorX: 0,
anchorY: 0,
scaleX: 0.1,
scaleY: 27.32
});
rightBorder.x = 2038;
rightBorder.y = 0;
rightBorder.tint = 0x000000;
game.addChild(rightBorder);
// Start the first wave
startNextWave();
// 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) {
// Store current boss wave path length before generating new path
var bossWavePathLength = calculatePathLength();
wavePathLengths[currentWave - 1] = bossWavePathLength;
// Set previous wave path length to the boss wave path length
// so the new path will be boss wave length + 300px
previousWavePathLength = bossWavePathLength;
// Save to database
storage.wavePathLengths = wavePathLengths;
storage.previousWavePathLength = previousWavePathLength;
// Clear all towers when boss is killed
clearAllTowers();
// Reset path extension factor for new wave
pathExtensionFactor = 1.0;
currentWave++;
enemiesSpawned = 0;
// Generate new path with 300px increase when boss is killed
startNextWave();
coins += 100 * currentWave; // Bonus coins for completing boss wave
waveDelay = 300; // 5 second break after boss
bossWave = false;
bossSpawned = false;
// Hide boss health display
bossHealthText.visible = false;
}
} else {
// Normal wave completion - keep same path and towers
if (enemiesSpawned >= enemiesPerWave * currentWave && enemies.length === 0) {
// Check if this completes a set of 5 waves
if (currentWave % 5 === 0) {
// Start boss wave - don't regenerate path yet, wait for boss kill
bossWave = true;
bossSpawned = false;
waveDelay = 180; // 3 second break before boss
} else {
// Normal wave progression - no path regeneration, no tower clearing
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
// Check if this is a boss enemy - if so, game over immediately
if (bossWave && enemy.health > 0) {
// Boss reached base - game over
LK.setScore(currentWave - 1);
LK.showGameOver();
return;
}
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();
}; ===================================================================
--- original.js
+++ change.js
@@ -144,10 +144,10 @@
speed: 1,
reward: 25
},
boss: {
- asset: 'boss',
- health: 15000,
+ asset: 'tankEnemy',
+ health: 1000,
speed: 1.5,
reward: 100
}
};
@@ -207,8 +207,12 @@
duration: 100
});
}
});
+ // Update boss health display if this is a boss during boss wave
+ if (bossWave && bossSpawned && bossHealthText.visible) {
+ bossHealthText.setText('Boss Health: ' + Math.max(0, self.health));
+ }
return self.health <= 0;
};
self.applySlow = function (factor, duration) {
self.slowEffect = Math.min(self.slowEffect, factor);
@@ -445,9 +449,9 @@
var enemies = [];
var towers = [];
var bullets = [];
var coins = 300;
-var baseHealth = 50;
+var baseHealth = 100;
var currentWave = 1;
var enemiesSpawned = 0;
var enemiesPerWave = 5;
var waveDelay = 0;
@@ -505,8 +509,16 @@
});
pathLengthText.anchor.set(0.5, 0);
pathLengthText.y = 80;
LK.gui.top.addChild(pathLengthText);
+var bossHealthText = new Text2('', {
+ size: 45,
+ fill: 0xFF0000
+});
+bossHealthText.anchor.set(0.5, 0);
+bossHealthText.y = 130;
+bossHealthText.visible = false;
+LK.gui.top.addChild(bossHealthText);
// Wave selector dropdown
var waveDropdownBtn = new Text2('Select Wave ▼', {
size: 40,
fill: 0x00FF00
@@ -1259,8 +1271,11 @@
boss.reward = 200;
enemies.push(boss);
game.addChild(boss);
bossSpawned = true;
+ // Show boss health display
+ bossHealthText.setText('Boss Health: ' + boss.health);
+ bossHealthText.visible = true;
}
function showTowerInfo(tower) {
if (!tower) return;
if (towerInfoPanel) {
@@ -1524,8 +1539,10 @@
coins += 100 * currentWave; // Bonus coins for completing boss wave
waveDelay = 300; // 5 second break after boss
bossWave = false;
bossSpawned = false;
+ // Hide boss health display
+ bossHealthText.visible = false;
}
} else {
// Normal wave completion - keep same path and towers
if (enemiesSpawned >= enemiesPerWave * currentWave && enemies.length === 0) {