User prompt
bitiş noktası ekran dışına taşmıyor fakat yolun bir kısmı ekran dışına taşıyor. lütfen bunu düzeltirmisin
User prompt
oyunda düzeltme yapmak istiyorum tüm oyunun görüntüsü hiçbir zaman ekran dışına taşmasın.
User prompt
dalgalarda yolun uzunluğu arttıkça yolun bir kısmı ekrandan taşıyor ve görünmüyor. bunu düzeltirmisin
User prompt
her yol oluşturulurken yolun tamamı görünsün
User prompt
her yol oluşturulurken youlun başlangıç ve bitiş noktaları rastgele farklı yerlerde olabilir ve yolun tamamı görünsün
User prompt
özellikle 11. ve 16. dalgalarda oluşturulan yol ekran dışına kayıyor. lütfen bunu düzeltirmisin
User prompt
yol uzadıkça dönüş sayısı artsın. her yol oluşturulurken rastgele oluşturulsun ve yolun tamamı görünsün
User prompt
herhangi bir dalgada yol oluşturulurken oluşturulan yolun tamamı görünsün.
User prompt
herhangi bir dalgada yol oluşturulurken yolun başlangıç ve bitiş noktası görünsün.
User prompt
herhangi bir dalgada yol oluşturulurken yolu ekranın dışına görünmeyecek şekilde oluşmasını engelle
User prompt
6. dalgadaki "burası 6. dalga yazsısını kaldır"
User prompt
yol oluştururken lütfen ekran dışına taşmasın. yolun tamamı gözüksün. gerekirse daha fazla dönüşlü bir yol olsun
User prompt
özellikle 16. dalgada yol oluşturulurken ekran dışına taşıyor bir kısmı görünmüyor. lütfen bunu düzeltirmisin
User prompt
I want to update the game. When creating a new path, the path should not extend beyond the screen. It should be visible in its entirety. Can you please fix this?
User prompt
Oyunu güncellemek istiyorum. Yeni bir yol oluştururken, yol ekranın dışına taşmamalı. Bunun yerine, tur sayısı artmalı.
User prompt
I want to make a correction to the game. When creating a new path in the game, this new path should be 15% below the top of the screen. 5% from the left side should be on the right side. 15% from the bottom should be on the top side. 5% from the right side should be on the left side.
User prompt
I want to make a fix in the game. Sometimes when creating a new path, part of the path goes off-screen and is not visible. I don't want this. The path should always be on the screen and visible. Can you fix this please?
User prompt
oyunda yeni yol oluşturulurken oluşturulan yol ekranın dışına çıkmasın veya değmesin
User prompt
Regenerate the path in wave 16. This time, set the path length to 4000 px.
User prompt
Regenerate the path in wave 11. This time, set the path length to 3600 px.
User prompt
Regenerate the path in wave 6. This time, set the path length to 3300 px.
User prompt
In the 6th wave, write "6th wave" above.
User prompt
Just as the newly created path in the 1st wave was 3000 px long, the newly created path in the 6th wave will also be 3300 px long.
User prompt
Let the length of the newly created path in the 6th wave be 3300 px.
User prompt
I want to make a change to the game. If the bosses are killed, the path that will be recreated in the next wave will be 300 px longer than the path in the previous wave.
/****
* 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
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);
// 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
****/
// 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 = 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);
// 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');
// 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) {
// 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--;
}
}
// For Wave 1, generate a varied path that is exactly 3000 pixels long
if (currentWave === 1) {
// Generate multiple path variations for Wave 1
var pathVariations = [
// Variation 1: Zigzag pattern
[{
x: 150,
y: 800
}, {
x: 600,
y: 800
}, {
x: 600,
y: 1300
}, {
x: 1050,
y: 1300
}, {
x: 1050,
y: 800
}, {
x: 1500,
y: 800
}, {
x: 1500,
y: 1300
}, {
x: 1800,
y: 1300
}],
// Variation 2: L-shape pattern
[{
x: 200,
y: 1200
}, {
x: 800,
y: 1200
}, {
x: 800,
y: 600
}, {
x: 1400,
y: 600
}, {
x: 1400,
y: 1000
}, {
x: 1700,
y: 1000
}, {
x: 1700,
y: 1400
}],
// Variation 3: S-curve pattern
[{
x: 250,
y: 900
}, {
x: 650,
y: 900
}, {
x: 650,
y: 1400
}, {
x: 1150,
y: 1400
}, {
x: 1150,
y: 700
}, {
x: 1650,
y: 700
}, {
x: 1650,
y: 1200
}],
// Variation 4: Step pattern
[{
x: 180,
y: 1100
}, {
x: 530,
y: 1100
}, {
x: 530,
y: 800
}, {
x: 880,
y: 800
}, {
x: 880,
y: 1500
}, {
x: 1230,
y: 1500
}, {
x: 1230,
y: 1000
}, {
x: 1580,
y: 1000
}, {
x: 1750,
y: 1000
}]];
// Select a random variation
var selectedVariation = pathVariations[Math.floor(Math.random() * pathVariations.length)];
// Scale the selected variation to exactly 3000 pixels
var originalLength = 0;
for (var i = 0; i < selectedVariation.length - 1; i++) {
var dx = selectedVariation[i + 1].x - selectedVariation[i].x;
var dy = selectedVariation[i + 1].y - selectedVariation[i].y;
originalLength += Math.sqrt(dx * dx + dy * dy);
}
// Apply scaling factor to reach exactly 3000 pixels
var scaleFactor = 3000 / originalLength;
gamePath = [];
for (var i = 0; i < selectedVariation.length; i++) {
if (i === 0) {
// Keep first point as-is
gamePath.push({
x: selectedVariation[i].x,
y: selectedVariation[i].y
});
} else {
// Scale subsequent segments
var prevPoint = gamePath[i - 1];
var originalDx = selectedVariation[i].x - selectedVariation[i - 1].x;
var originalDy = selectedVariation[i].y - selectedVariation[i - 1].y;
var scaledDx = originalDx * scaleFactor;
var scaledDy = originalDy * scaleFactor;
gamePath.push({
x: prevPoint.x + scaledDx,
y: prevPoint.y + scaledDy
});
}
}
// Draw path tiles for Wave 1
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 for Wave 1
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 and baslangic positions for Wave 1
if (base) {
base.x = gamePath[gamePath.length - 1].x;
base.y = gamePath[gamePath.length - 1].y;
}
if (baslangicImage) {
baslangicImage.x = gamePath[0].x;
baslangicImage.y = gamePath[0].y;
}
// Ensure base and baslangic stay in foreground
if (base) game.addChild(base);
if (baslangicImage) game.addChild(baslangicImage);
// Update previous wave path length for Wave 1
var actualWave1Length = calculatePathLength();
if (actualWave1Length > 0) {
previousWavePathLength = actualWave1Length;
wavePathLengths[0] = actualWave1Length; // Store Wave 1 length
// Save to database
storage.previousWavePathLength = previousWavePathLength;
storage.wavePathLengths = wavePathLengths;
} else {
previousWavePathLength = 3000; // Fallback to expected length
wavePathLengths[0] = 3000; // Store Wave 1 fallback length
// Save to database
storage.previousWavePathLength = previousWavePathLength;
storage.wavePathLengths = wavePathLengths;
}
return; // Exit early for Wave 1
}
// Calculate middle 80% vertical bounds (10% margin top and bottom)
var screenHeight = 2732;
var topMargin = screenHeight * 0.1; // 10% from top
var bottomMargin = screenHeight * 0.9; // 90% from top (10% margin at bottom)
var minY = topMargin;
var maxY = bottomMargin;
// Set path length based on wave - Wave 1 is 3000px, others use previous wave length + 300px
var targetPathLength;
if (currentWave === 1) {
targetPathLength = 3000;
} 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;
// Special enforcement for Wave 6 to ensure it's exactly 3300 pixels (like Wave 1's 3000px requirement)
if (currentWave === 6) {
targetPathLength = 3300; // Wave 6 is always exactly 3300 pixels
}
}
// Function to calculate distance from point to line segment
function distanceToLineSegment(point, lineStart, lineEnd) {
var A = point.x - lineStart.x;
var B = point.y - lineStart.y;
var C = lineEnd.x - lineStart.x;
var D = lineEnd.y - lineStart.y;
var dot = A * C + B * D;
var lenSq = C * C + D * D;
if (lenSq === 0) return Math.sqrt(A * A + B * B);
var param = dot / lenSq;
var xx, yy;
if (param < 0) {
xx = lineStart.x;
yy = lineStart.y;
} else if (param > 1) {
xx = lineEnd.x;
yy = lineEnd.y;
} else {
xx = lineStart.x + param * C;
yy = lineStart.y + param * D;
}
var dx = point.x - xx;
var dy = point.y - yy;
return Math.sqrt(dx * dx + dy * dy);
}
// Function to check if two line segments intersect
function doLinesIntersect(p1, q1, p2, q2) {
function orientation(p, q, r) {
var val = (q.y - p.y) * (r.x - q.x) - (q.x - p.x) * (r.y - q.y);
if (val === 0) return 0; // collinear
return val > 0 ? 1 : 2; // clockwise or counterclockwise
}
function onSegment(p, q, r) {
return q.x <= Math.max(p.x, r.x) && q.x >= Math.min(p.x, r.x) && q.y <= Math.max(p.y, r.y) && q.y >= Math.min(p.y, r.y);
}
var o1 = orientation(p1, q1, p2);
var o2 = orientation(p1, q1, q2);
var o3 = orientation(p2, q2, p1);
var o4 = orientation(p2, q2, q1);
// General case
if (o1 !== o2 && o3 !== o4) return true;
// Special cases
if (o1 === 0 && onSegment(p1, p2, q1)) return true;
if (o2 === 0 && onSegment(p1, q2, q1)) return true;
if (o3 === 0 && onSegment(p2, p1, q2)) return true;
if (o4 === 0 && onSegment(p2, q1, q2)) return true;
return false;
}
// Function to check if proposed segment would intersect or get too close to existing path
function wouldIntersectPath(newStart, newEnd, existingPath) {
// Strict minimum distance to prevent any crossing or touching
var minDistance = 350; // Increased minimum distance significantly
var minSafeDistance = 200; // Minimum safe distance for any part of path
// Primary check: Direct line intersection with ANY existing segment (except immediate connection)
for (var i = 0; i < existingPath.length - 1; i++) {
var segStart = existingPath[i];
var segEnd = existingPath[i + 1];
// Skip only the immediate previous segment that we're connecting from
if (i === existingPath.length - 2) continue;
// Check for any intersection between the new segment and existing segments
if (doLinesIntersect(newStart, newEnd, segStart, segEnd)) {
return true;
}
}
// Secondary check: Ensure new segment maintains safe distance from ALL existing segments
for (var i = 0; i < existingPath.length - 1; i++) {
var segStart = existingPath[i];
var segEnd = existingPath[i + 1];
// Skip only the immediate previous segment
if (i === existingPath.length - 2) continue;
// Calculate minimum distance between the new segment and existing segment
var dist1 = distanceToLineSegment(newStart, segStart, segEnd);
var dist2 = distanceToLineSegment(newEnd, segStart, segEnd);
var dist3 = distanceToLineSegment(segStart, newStart, newEnd);
var dist4 = distanceToLineSegment(segEnd, newStart, newEnd);
var minimumSegmentDistance = Math.min(dist1, dist2, dist3, dist4);
// Reject if any part of the segments get too close
if (minimumSegmentDistance < minSafeDistance) {
return true;
}
}
// Tertiary check: Ensure new endpoint doesn't get too close to any existing path points
for (var i = 0; i < existingPath.length; i++) {
var pathPoint = existingPath[i];
// Skip only the immediate previous point we're connecting from
if (i === existingPath.length - 1) continue;
// Calculate distance from new endpoint to existing path points
var distToEnd = Math.sqrt((newEnd.x - pathPoint.x) * (newEnd.x - pathPoint.x) + (newEnd.y - pathPoint.y) * (newEnd.y - pathPoint.y));
// Use increasingly strict distance requirements for older path points
var ageMultiplier = 1.0 + (existingPath.length - 1 - i) * 0.2; // Older points need more distance
var requiredDistance = minDistance * ageMultiplier;
if (distToEnd < requiredDistance) {
return true;
}
}
// Quaternary check: Prevent sharp reversals that could lead to future self-intersection
if (existingPath.length >= 2) {
var prevPoint = existingPath[existingPath.length - 1];
var prevPrevPoint = existingPath[existingPath.length - 2];
// Calculate direction vectors
var prevDirX = prevPoint.x - prevPrevPoint.x;
var prevDirY = prevPoint.y - prevPrevPoint.y;
var newDirX = newEnd.x - newStart.x;
var newDirY = newEnd.y - newStart.y;
// Calculate angle between directions using dot product
var dotProduct = prevDirX * newDirX + prevDirY * newDirY;
var prevLength = Math.sqrt(prevDirX * prevDirX + prevDirY * prevDirY);
var newLength = Math.sqrt(newDirX * newDirX + newDirY * newDirY);
if (prevLength > 0 && newLength > 0) {
var cosAngle = dotProduct / (prevLength * newLength);
// Prevent sharp reversals (angles greater than 140 degrees)
if (cosAngle < -0.64) {
// cos(140°) ≈ -0.64
return true;
}
}
}
// Final comprehensive check: Ensure the new segment doesn't create potential for future loops
// by checking if it would "enclose" any existing path segments
if (existingPath.length >= 3) {
// Check if the new segment would create a potential enclosed area with any non-adjacent path segments
for (var i = 0; i < existingPath.length - 3; i++) {
// Check against segments that are not adjacent
var oldSegStart = existingPath[i];
var oldSegEnd = existingPath[i + 1];
// Calculate if new segment and old segment could form an enclosed area
// Check if segments are oriented in a way that could trap path points between them
var cross1 = (newEnd.x - newStart.x) * (oldSegStart.y - newStart.y) - (newEnd.y - newStart.y) * (oldSegStart.x - newStart.x);
var cross2 = (newEnd.x - newStart.x) * (oldSegEnd.y - newStart.y) - (newEnd.y - newStart.y) * (oldSegEnd.x - newStart.x);
var cross3 = (oldSegEnd.x - oldSegStart.x) * (newStart.y - oldSegStart.y) - (oldSegEnd.y - oldSegStart.y) * (newStart.x - oldSegStart.x);
var cross4 = (oldSegEnd.x - oldSegStart.x) * (newEnd.y - oldSegStart.y) - (oldSegEnd.y - oldSegStart.y) * (newEnd.x - oldSegStart.x);
// If segments create opposing orientations, they might enclose an area - reject this
if (cross1 > 0 !== cross2 > 0 && cross3 > 0 !== cross4 > 0) {
return true;
}
}
}
return false;
}
// Create varied path shapes while maintaining characteristics
gamePath = [];
var baseSegmentLength = currentWave === 1 ? 500 : Math.floor(300 * pathExtensionFactor); // Base segment length for Wave 1 calculation
// For Wave 1, calculate exact segment lengths to total exactly 3000 pixels
var numSegments = currentWave === 1 ? 6 : Math.max(6, Math.floor(5 + currentWave * 0.5)); // 6 segments for 3000 pixel Wave 1 path
var turnCount = 0; // Track number of turns
// Starting position - fixed for Wave 1, randomized for others
var startX = currentWave === 1 ? 200 : 100 + Math.random() * 200;
var startY = currentWave === 1 ? 1000 : minY + 100 + Math.random() * (maxY - minY - 200);
gamePath.push({
x: startX,
y: startY
});
var currentX = startX;
var currentY = startY;
var direction = 0; // 0=right, 1=down, 2=left, 3=up
var totalPathLength = 0; // Track total path length built so far
var segmentLengths = []; // Pre-calculate segment lengths to total exactly 3000 pixels
// Calculate exact segment lengths to achieve target path length
if (currentWave === 1) {
// For Wave 1, use simple fixed segments that always total exactly 3000 pixels
segmentLengths = [500, 500, 500, 500, 500, 500]; // 6 segments of 500 pixels each = 3000 total
} else {
// For subsequent waves, calculate segments to achieve exact target length (previous wave + 60px)
var baseSegmentLength = Math.floor(targetPathLength / numSegments);
var remainder = targetPathLength % numSegments;
segmentLengths = [];
for (var i = 0; i < numSegments; i++) {
segmentLengths.push(baseSegmentLength + (i < remainder ? 1 : 0));
}
}
// Generate varied path with guaranteed minimum 3 turns
// Create strategic turn points to ensure we get at least 3 turns
var mandatoryTurnSegments = [];
if (numSegments >= 5) {
// Place mandatory turns at strategic points
mandatoryTurnSegments.push(Math.floor(numSegments * 0.25)); // Turn at 25%
mandatoryTurnSegments.push(Math.floor(numSegments * 0.5)); // Turn at 50%
mandatoryTurnSegments.push(Math.floor(numSegments * 0.75)); // Turn at 75%
}
for (var seg = 0; seg < numSegments; seg++) {
// Use pre-calculated segment length for exact path length control
var segmentLength = segmentLengths[seg] || Math.max(250, baseSegmentLength + Math.random() * 200 - 100);
var attempts = 0;
var validSegmentFound = false;
var forceTurn = mandatoryTurnSegments.indexOf(seg) !== -1; // Force turn at strategic segments
while (!validSegmentFound && attempts < 50) {
// Increased max attempts
var shouldTurn = false;
// Force turn at mandatory segments or if we haven't turned enough
if (forceTurn || turnCount < 3 && seg >= numSegments - (3 - turnCount)) {
shouldTurn = true;
} else if (turnCount < 3 && seg > 0 && Math.random() < 0.9) {
// Very high chance of turning when we need more turns
shouldTurn = true;
} else if (turnCount >= 3 && seg > 0 && Math.random() < 0.4) {
// Lower chance after we have enough turns
shouldTurn = true;
}
if (shouldTurn && seg > 0) {
// Don't turn on first segment
// Change direction (90-degree turn)
var oldDirection = direction;
var possibleDirections = [];
// Calculate valid directions based on current position and boundaries
if (direction !== 2 && currentX + segmentLength < 1900) possibleDirections.push(0); // right
if (direction !== 3 && currentY + segmentLength < maxY) possibleDirections.push(1); // down
if (direction !== 0 && currentX - segmentLength > 100) possibleDirections.push(2); // left
if (direction !== 1 && currentY - segmentLength > minY) possibleDirections.push(3); // up
// Remove the current direction to force an actual turn
var filteredDirections = [];
for (var d = 0; d < possibleDirections.length; d++) {
if (possibleDirections[d] !== direction) {
filteredDirections.push(possibleDirections[d]);
}
}
if (filteredDirections.length > 0) {
direction = filteredDirections[Math.floor(Math.random() * filteredDirections.length)];
turnCount++;
} else if (possibleDirections.length > 0) {
// Fallback to any valid direction if no turn is possible
direction = possibleDirections[Math.floor(Math.random() * possibleDirections.length)];
}
}
// Calculate next position based on direction
var nextX = currentX;
var nextY = currentY;
if (direction === 0) {
// right
nextX = Math.min(1900, currentX + segmentLength);
} else if (direction === 1) {
// down
nextY = Math.min(maxY, currentY + segmentLength);
} else if (direction === 2) {
// left
nextX = Math.max(100, currentX - segmentLength);
} else if (direction === 3) {
// up
nextY = Math.max(minY, currentY - segmentLength);
}
// Ensure we're moving towards the general end area in later segments
if (seg > numSegments * 0.7) {
if (nextX < 1500) {
nextX = Math.min(1900, currentX + Math.abs(segmentLength));
direction = 0;
}
}
// Check if this segment would intersect existing path
var proposedStart = {
x: currentX,
y: currentY
};
var proposedEnd = {
x: nextX,
y: nextY
};
// Check intersection with much stricter criteria
var isValidSegment = false;
if (gamePath.length < 2) {
// First few segments are always safe
isValidSegment = true;
} else {
// For subsequent segments, apply strict intersection checking
isValidSegment = !wouldIntersectPath(proposedStart, proposedEnd, gamePath);
}
if (isValidSegment) {
gamePath.push({
x: nextX,
y: nextY
});
currentX = nextX;
currentY = nextY;
validSegmentFound = true;
} else {
// Segment would intersect - try different approach
attempts++;
// Reduce segment length for intersection avoidance (except for pre-calculated lengths)
if (seg >= segmentLengths.length || currentWave === 1) {
// Keep original length for pre-calculated segments
} else {
segmentLength = Math.max(150, segmentLength * 0.7);
}
// After several attempts, try completely different directions
if (attempts > 10) {
var availableDirections = [];
// Check all directions for validity
if (currentWave === 1) {
// For Wave 1, use original segment length for direction checking
var checkLength = segmentLengths[seg];
if (currentX + checkLength < 1900 && direction !== 2) availableDirections.push(0); // right
if (currentY + checkLength < maxY && direction !== 3) availableDirections.push(1); // down
if (currentX - checkLength > 100 && direction !== 0) availableDirections.push(2); // left
if (currentY - checkLength > minY && direction !== 1) availableDirections.push(3); // up
} else {
if (currentX + 200 < 1900 && direction !== 2) availableDirections.push(0); // right
if (currentY + 200 < maxY && direction !== 3) availableDirections.push(1); // down
if (currentX - 200 > 100 && direction !== 0) availableDirections.push(2); // left
if (currentY - 200 > minY && direction !== 1) availableDirections.push(3); // up
}
if (availableDirections.length > 0) {
direction = availableDirections[Math.floor(Math.random() * availableDirections.length)];
if (currentWave !== 1) {
segmentLength = 200; // Reset to smaller safe length
}
}
}
}
}
if (!validSegmentFound) {
// Force a valid segment if we can't find one - try multiple directions
var fallbackFound = false;
var fallbackDirections = [0, 1, 2, 3]; // right, down, left, up
for (var fd = 0; fd < fallbackDirections.length && !fallbackFound; fd++) {
var fallbackDir = fallbackDirections[fd];
var fallbackLength = seg < segmentLengths.length ? segmentLengths[seg] : 200;
var fallbackX = currentX;
var fallbackY = currentY;
if (fallbackDir === 0 && currentX + fallbackLength < 1900) {
// right
fallbackX = currentX + fallbackLength;
} else if (fallbackDir === 1 && currentY + fallbackLength < maxY) {
// down
fallbackY = currentY + fallbackLength;
} else if (fallbackDir === 2 && currentX - fallbackLength > 100) {
// left
fallbackX = currentX - fallbackLength;
} else if (fallbackDir === 3 && currentY - fallbackLength > minY) {
// up
fallbackY = currentY - fallbackLength;
} else {
continue; // Try next direction
}
var fallbackStart = {
x: currentX,
y: currentY
};
var fallbackEnd = {
x: fallbackX,
y: fallbackY
};
// Check if this fallback direction works
if (gamePath.length < 2 || !wouldIntersectPath(fallbackStart, fallbackEnd, gamePath)) {
gamePath.push({
x: fallbackX,
y: fallbackY
});
currentX = fallbackX;
currentY = fallbackY;
direction = fallbackDir;
fallbackFound = true;
}
}
// If still no valid segment found, break the loop to prevent infinite generation
if (!fallbackFound) {
break;
}
}
}
// Ensure final path reaches right side of screen
if (currentX < 1600) {
gamePath.push({
x: Math.min(1900, currentX + 300),
y: currentY
});
}
// Redraw 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);
}
}
// Ensure gamePath has valid elements
if (!gamePath || gamePath.length === 0) {
// Fallback path if generation failed
gamePath = [{
x: 100,
y: 400
}, {
x: 1800,
y: 1600
}];
}
// Update base position to new end position
if (base) {
base.x = gamePath[gamePath.length - 1].x;
base.y = gamePath[gamePath.length - 1].y;
}
// Update baslangic position to new start position
if (baslangicImage) {
baslangicImage.x = gamePath[0].x;
baslangicImage.y = gamePath[0].y;
}
// Ensure base and baslangic stay in foreground
if (base) game.addChild(base);
if (baslangicImage) game.addChild(baslangicImage);
// 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;
}
}
// Wave 1 path generation complete - no flag changes needed since we use currentWave condition
}
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 and baslangic positions to new path
if (base) {
base.x = gamePath[gamePath.length - 1].x;
base.y = gamePath[gamePath.length - 1].y;
}
if (baslangicImage) {
baslangicImage.x = gamePath[0].x;
baslangicImage.y = 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;
}
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);
}
}
// 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();
}
};
// 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;
}
} 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
@@ -407,13 +407,13 @@
/****
* Game Code
****/
-// Sounds
-// Game elements
-// Enemy assets
-// Tower assets
// Game variables
+// Tower assets
+// Enemy assets
+// Game elements
+// Sounds
var gamePath = [{
x: 100,
y: 400
}, {
@@ -1036,9 +1036,9 @@
} 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;
- // Special enforcement for Wave 6 to ensure it's exactly 3300 pixels
+ // Special enforcement for Wave 6 to ensure it's exactly 3300 pixels (like Wave 1's 3000px requirement)
if (currentWave === 6) {
targetPathLength = 3300; // Wave 6 is always exactly 3300 pixels
}
}