User prompt
Please make the following adjustments to improve gameplay balance: --- 1. **Bullet Speed:** - Increase the speed of all tower projectiles (bullets). - Currently, fast enemies can outrun or avoid projectiles. - Bullet speed should be high enough to consistently hit even the fastest enemies. --- 2. **Range Upgrade Scaling:** - Modify the range increase per upgrade level as follows: • Level 1 upgrade: +10% range • Level 2 upgrade: +5% range • Level 3 upgrade: +3.5% range • Levels 4 and above: +2% range per level (fixed) - This creates a diminishing return curve to prevent range from becoming too overpowered at high levels. - Apply the range increase multiplicatively based on the current base range if possible. --- These changes will ensure that towers remain effective against fast enemies and that range upgrades scale more reasonably throughout the game.
User prompt
Please update the enemy wave system to scale the number of enemies per round as follows: 1. Base Scaling: - For waves 1 to 9: Spawn between **10 and 15 enemies** randomly per wave. 2. Mid-Level Scaling: - From wave 10 onward: Increase the number to **between 15 and 20 enemies** per wave. 3. Maximum Limit: - As the game progresses further, the number of enemies can slightly increase, but **never exceed 30 enemies per wave**. 4. Notes: - The number of enemies should vary slightly within the range to keep each wave unpredictable. - You may use randomness within bounds (e.g., random between 15–20 after wave 10). - Ensure that enemy spawning pace keeps the game challenging but not overwhelming. This scaling will ensure progressive difficulty while maintaining performance and playability.
User prompt
Move the text Kale Canı down a little bit
User prompt
Move the castle life text a little higher
User prompt
Move the castle life text a little higher
User prompt
Move the castle life text a little higher and have it on top of all objects as a layer
User prompt
Let's move the gold text a little lower
User prompt
Let's move the gold text a little lower
User prompt
Please adjust the layout of the gold and castle health text displays as follows: 1. Gold Text: - Move the gold text display **slightly further down** on the screen, so it is no longer too close to the top edge. - Ensure it remains fully visible and does not overlap any other UI elements. 2. Castle Health Text: - Relocate the castle health display to the **very bottom of the screen**, near the lower edge of the map or screen area. - Reduce its **font size**, so it appears more compact and subtle. - The text should remain readable, but it should not dominate the bottom of the screen. These changes will improve screen balance and make the resource indicators less intrusive.
User prompt
Please improve the visibility of the gold and health text displays with the following changes: 1. Background Panel: - Add a **solid rectangular background panel** behind both the gold and health texts. - This panel should be slightly larger than the text, fully covering the text area to ensure readability. - Use a **dark color** (e.g., black or dark gray) for contrast, and no transparency. 2. Positioning: - Move the gold and health text slightly **lower on the screen**, so they are not too close to the top or overlapping other elements. 3. Layout: - Ensure the background panel is placed **behind the text**, not in front of it. - The panel and text should remain on the **UI layer above the game environment**, like trees or terrain. This will make the health and gold values always clearly visible, regardless of the map visuals behind them.
User prompt
There is a visibility issue with the game's UI: - The health and gold text displays are currently being **obscured by trees or other background elements** on the map. - This makes it difficult for players to see their current health and resources clearly. Please fix this by: 1. Ensuring that the **UI elements (health and gold text)** always render **above all map elements**, including trees and decorations. 2. Optionally, place the health and gold UI on a **solid or semi-transparent background** to make the text easier to read. 3. Confirm that the font color and size are readable against all backgrounds. This will make resource tracking much easier during gameplay.
User prompt
You should show the gold text and the castle life text on the top layer
User prompt
Let's bring the gold text and the castle can text to the top as a layer
User prompt
Let's make the size and font of the gold text the same as the font and size of the kale canı text.
User prompt
Let's take the gold and castle life text significantly higher
User prompt
Let's move the gold and castle life text to the middle of the map, aligned to the left.
User prompt
Just above the gold text, write how many lives the castle has left. Let it be a little thicker and easier to see.
User prompt
For the animation of the bar, only a green stripe should be used and it should start to turn red from the sides as it takes damage, and we need to move the position of the bar down a bit. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Please fix the bug: 'TypeError: tween.to is not a function' in or related to this line: 'tween.to(castleHealthBarFill, {' Line Number: 2512 ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
The health bar of the castle is in a very irrelevant place. Let's delete this. Let's position the new health bar right under the castle. And use the new animation you created, not the old one.
User prompt
There is no health bar visible above the castle. It may be under a different layer. Let's bring it to the top layer and make it visible.
User prompt
A health bar should be added just below the castle
User prompt
Let's write the names of the towers in the panel below the map to be placed in the lower position. Their names should be Infantry, Freezer, Air, Poison, Tesla, respectively.
User prompt
Add a health bar under the castle. When you click on the towers that are built, the distance they can shoot from will be displayed in a circle.
User prompt
Adjust the position of the tower charges on the panel so that they do not touch the bottom of the tower
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ var Enemy = Container.expand(function (type, wave, pathIndex, strengthLevel) { var self = Container.call(this); self.type = type; self.wave = wave; self.pathIndex = pathIndex || 1; self.strengthLevel = strengthLevel || 1; self.currentPath = self.pathIndex === 1 ? enemyPath1 : self.pathIndex === 2 ? enemyPath2 : enemyPath3; // Define base stats for each enemy type (reduced by 30% for balance) var baseStats = { goblin: { speed: 2.1, health: 28, coins: 7, flying: false }, crawler: { speed: 1.4, health: 42, coins: 10, flying: false }, bandit: { speed: 1.75, health: 35, coins: 12, flying: false }, golem: { speed: 0.7, health: 105, coins: 18, flying: false }, imp: { speed: 1.9, health: 49, coins: 15, flying: false }, knight: { speed: 1.05, health: 70, coins: 20, flying: false }, beast: { speed: 0.9, health: 84, coins: 22, flying: false }, mage: { speed: 1.2, health: 56, coins: 25, flying: false }, reaver: { speed: 2.3, health: 63, coins: 28, flying: true }, doom: { speed: 0.8, health: 140, coins: 35, flying: false } }; var stats = baseStats[type] || baseStats.goblin; // Scale stats by strength level var strengthMultiplier = 1 + (self.strengthLevel - 1) * 0.4; var speedMultiplier = 1 + (self.strengthLevel - 1) * 0.08; self.speed = stats.speed * speedMultiplier; self.maxHealth = Math.floor(stats.health * strengthMultiplier); self.health = self.maxHealth; self.pathStep = 0; self.coinValue = Math.floor(stats.coins * strengthMultiplier); self.flying = stats.flying; self.regeneration = type === 'beast' ? 0.5 : 0; self.poisonResistant = type === 'doom'; self.slowResistant = type === 'doom'; var graphics = self.attachAsset('enemy_' + type, { anchorX: 0.5, anchorY: 0.5 }); self.x = self.currentPath[0].x; self.y = self.currentPath[0].y; self.takeDamage = function (damage) { self.health -= damage; LK.effects.flashObject(self, 0xFF0000, 200); if (self.health <= 0) { LK.getSound('enemy_hit').play(); return true; } return false; }; self.update = function () { if (self.pathStep < self.currentPath.length - 1) { var target = self.currentPath[self.pathStep + 1]; var dx = target.x - self.x; var dy = target.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < 5) { self.pathStep++; if (self.pathStep >= self.currentPath.length - 1) { self.reachedCastle = true; } } else { self.x += dx / distance * self.speed; self.y += dy / distance * self.speed; } } }; return self; }); var Projectile = Container.expand(function (type, startX, startY, targetX, targetY, damage) { var self = Container.call(this); self.type = type; self.damage = damage; self.targetX = targetX; self.targetY = targetY; self.speed = type === 'arrow' ? 16 : type === 'cannonball' ? 12 : 14; var graphics = self.attachAsset('projectile_' + type, { anchorX: 0.5, anchorY: 0.5 }); self.x = startX; self.y = startY; var dx = targetX - startX; var dy = targetY - startY; var distance = Math.sqrt(dx * dx + dy * dy); self.velocityX = dx / distance * self.speed; self.velocityY = dy / distance * self.speed; self.update = function () { self.x += self.velocityX; self.y += self.velocityY; var distToTarget = Math.sqrt((self.x - self.targetX) * (self.x - self.targetX) + (self.y - self.targetY) * (self.y - self.targetY)); if (distToTarget < 10) { self.hasReachedTarget = true; } }; return self; }); var Tower = Container.expand(function (type) { var self = Container.call(this); self.type = type; self.level = 1; self.lastShotTime = 0; self.maxLevel = type === 'antiair' || type === 'poison' || type === 'tesla' ? 5 : 10; // Initialize tower stats based on type if (type === 'infantry') { self.damage = 20; self.range = 100; self.attackSpeed = 600; self.cost = 80; self.projectileType = 'arrow'; } else if (type === 'frost') { self.damage = 15; self.range = 90; self.attackSpeed = 800; self.cost = 120; self.projectileType = 'magic'; self.slowEffect = true; } else if (type === 'antiair') { self.damage = 40; self.range = 130; self.attackSpeed = 1000; self.cost = 150; self.projectileType = 'magic'; self.antiAir = true; } else if (type === 'poison') { self.damage = 10; self.range = 80; self.attackSpeed = 1200; self.cost = 100; self.projectileType = 'magic'; self.poisonEffect = true; } else if (type === 'tesla') { self.damage = 35; self.range = 95; self.attackSpeed = 900; self.cost = 180; self.projectileType = 'magic'; self.chainLightning = true; } var graphics = self.attachAsset('tower_' + type, { anchorX: 0.5, anchorY: 0.5 }); self.getUpgradeNames = function () { if (self.type === 'infantry') { return ['Rifleman', 'Marksman', 'Sharpshooter', 'Machine Gunner', 'Grenadier', 'Mortar Operator', 'Tank Commander', 'Bazooka Specialist', 'Rocket Launcher', 'Missile Battery']; } else if (self.type === 'frost') { return ['Frost Spire', 'Ice Shard Tower', 'Blizzard Bastion', 'Cryo Cannon', 'Glacier Fortress', 'Snowstorm Citadel', 'Avalanche Keep', 'Frozen Maelstrom', 'Arctic Warden', 'Absolute Zero']; } else if (self.type === 'antiair') { return ['Flak Cannon', 'SAM Launcher', 'Radar Missile Battery', 'Laser Air Defense', 'Plasma Barrier']; } else if (self.type === 'poison') { return ['Venom Sprayer', 'Toxic Spitter', 'Acid Dart Tower', 'Corrosive Spire', 'Plague Altar']; } else if (self.type === 'tesla') { return ['Static Coil', 'Dual Coil', 'Arc Emitter', 'Thunderstorm Core', 'Lightning God']; } }; self.canShoot = function () { return LK.ticks - self.lastShotTime > self.attackSpeed / (1000 / 60); }; self.findTarget = function () { var closestEnemy = null; var closestDistance = Infinity; for (var i = 0; i < enemies.length; i++) { var enemy = enemies[i]; var distance = Math.sqrt((enemy.x - self.x) * (enemy.x - self.x) + (enemy.y - self.y) * (enemy.y - self.y)); if (distance <= self.range && distance < closestDistance) { closestDistance = distance; closestEnemy = enemy; } } return closestEnemy; }; self.shoot = function (target) { if (!self.canShoot()) return; self.lastShotTime = LK.ticks; var projectile = new Projectile(self.projectileType, self.x, self.y, target.x, target.y, self.damage); projectile.towerType = self.type; projectile.slowEffect = self.slowEffect; projectile.poisonEffect = self.poisonEffect; projectile.chainLightning = self.chainLightning; projectiles.push(projectile); isoContainer.addChild(projectile); LK.getSound('shoot_arrow').play(); }; self.getUpgradeCost = function () { var baseCosts = [50, 100, 150, 200, 250, 300, 350, 400, 450, 500]; return baseCosts[self.level - 1] || 500; }; self.upgrade = function () { if (self.level < self.maxLevel) { self.level++; self.damage = Math.floor(self.damage * 1.3); // Calculate range increase with diminishing returns var rangeMultiplier = 1.0; if (self.level === 2) { rangeMultiplier = 1.10; // +10% for level 1 upgrade } else if (self.level === 3) { rangeMultiplier = 1.10 * 1.05; // +10% then +5% } else if (self.level === 4) { rangeMultiplier = 1.10 * 1.05 * 1.035; // +10%, +5%, then +3.5% } else if (self.level >= 5) { // +10%, +5%, +3.5%, then +2% for each additional level rangeMultiplier = 1.10 * 1.05 * 1.035; var additionalLevels = self.level - 4; rangeMultiplier *= Math.pow(1.02, additionalLevels); } // Apply range multiplier to base range for this tower type var baseRange; if (self.type === 'infantry') baseRange = 100;else if (self.type === 'frost') baseRange = 90;else if (self.type === 'antiair') baseRange = 130;else if (self.type === 'poison') baseRange = 80;else if (self.type === 'tesla') baseRange = 95; self.range = Math.floor(baseRange * rangeMultiplier); self.attackSpeed = Math.max(200, Math.floor(self.attackSpeed * 0.95)); // Visual upgrade indicators var tintColors = [0xFFFFFF, 0xFFD700, 0xFF6347, 0xFF4500, 0x8A2BE2, 0x00FF00, 0x00FFFF, 0xFF69B4, 0xFFFF00, 0xFF0000]; graphics.tint = tintColors[self.level - 1] || 0xFF0000; // Show range indicator for upgraded towers if (self.level > 1 && !self.rangeIndicator) { self.rangeIndicator = LK.getAsset('build_zone', { anchorX: 0.5, anchorY: 0.5, alpha: 0.1, scaleX: self.range / 40, scaleY: self.range / 40 }); self.rangeIndicator.x = self.x; self.rangeIndicator.y = self.y; self.rangeIndicator.tint = 0x00FF00; isoContainer.addChild(self.rangeIndicator); } else if (self.rangeIndicator) { self.rangeIndicator.scaleX = self.range / 40; self.rangeIndicator.scaleY = self.range / 40; } } }; self.down = function (x, y, obj) { selectedTower = self; // Clear any existing range indicator if (activeRangeIndicator && activeRangeIndicator.destroy) { activeRangeIndicator.destroy(); activeRangeIndicator = null; } // Create new range indicator activeRangeIndicator = LK.getAsset('build_zone', { anchorX: 0.5, anchorY: 0.5, alpha: 0.2, scaleX: self.range / 27.5, scaleY: self.range / 27.5 }); activeRangeIndicator.x = self.x; activeRangeIndicator.y = self.y; activeRangeIndicator.tint = 0x00FFFF; // Cyan color for range indicator isoContainer.addChild(activeRangeIndicator); if (upgradeText && upgradeText.destroy) { upgradeText.destroy(); } if (self.level < self.maxLevel) { var cost = self.getUpgradeCost(); var names = self.getUpgradeNames(); var nextName = names[self.level - 1] || 'Max Level'; upgradeText = new Text2(nextName + ': ' + cost + ' gold', { size: 24, fill: coins >= cost ? 0x00FF00 : 0xFF0000 }); upgradeText.anchor.set(0.5, 1); upgradeText.x = self.x; upgradeText.y = self.y - 40; isoContainer.addChild(upgradeText); if (coins >= cost) { upgradeText.down = function () { coins -= cost; coinsText.setText('Gold: ' + coins); self.upgrade(); if (upgradeText && upgradeText.destroy) { upgradeText.destroy(); } upgradeText = null; selectedTower = null; // Clear range indicator when upgrading if (activeRangeIndicator && activeRangeIndicator.destroy) { activeRangeIndicator.destroy(); activeRangeIndicator = null; } }; } } else { upgradeText = new Text2('MAX LEVEL', { size: 30, fill: 0xFFFFFF }); upgradeText.anchor.set(0.5, 1); upgradeText.x = self.x; upgradeText.y = self.y - 40; isoContainer.addChild(upgradeText); } }; self.update = function () { var target = self.findTarget(); if (target) { self.shoot(target); } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x228B22 }); /**** * Game Code ****/ // Use game container directly for top-down view var isoContainer = game; var gameState = 'setup'; var castleType = 'square'; var difficulty = 'easy'; var currentWave = 0; var maxWaves = 50; var waveEnemies = []; var enemies = []; var towers = []; var projectiles = []; var coins = 300; var castleHealth = 100; var waveInProgress = false; var nextWaveTimer = 0; var selectedTowerType = null; var selectedTower = null; var upgradeText = null; var activeRangeIndicator = null; // Enemy path 1: Detailed horizontal then vertical path var enemyPath1 = [{ x: 0, y: 200 }, { x: 25.6, y: 200 }, { x: 51.2, y: 200 }, { x: 76.8, y: 200 }, { x: 102.4, y: 200 }, { x: 128, y: 200 }, { x: 153.6, y: 200 }, { x: 179.2, y: 200 }, { x: 204.8, y: 200 }, { x: 230.4, y: 200 }, { x: 256, y: 200 }, { x: 281.6, y: 200 }, { x: 307.2, y: 200 }, { x: 332.8, y: 200 }, { x: 358.4, y: 200 }, { x: 384, y: 200 }, { x: 409.6, y: 200 }, { x: 435.2, y: 200 }, { x: 460.8, y: 200 }, { x: 486.4, y: 200 }, { x: 512, y: 200 }, { x: 537.6, y: 200 }, { x: 563.2, y: 200 }, { x: 588.8, y: 200 }, { x: 614.4, y: 200 }, { x: 640, y: 200 }, { x: 665.6, y: 200 }, { x: 691.2, y: 200 }, { x: 716.8, y: 200 }, { x: 742.4, y: 200 }, { x: 768, y: 200 }, { x: 793.6, y: 200 }, { x: 819.2, y: 200 }, { x: 844.8, y: 200 }, { x: 870.4, y: 200 }, { x: 896, y: 200 }, { x: 921.6, y: 200 }, { x: 947.2, y: 200 }, { x: 972.8, y: 200 }, { x: 998.4, y: 200 }, { x: 1024, y: 200 }, { x: 1024, y: 240 }, { x: 1024, y: 280 }, { x: 1024, y: 320 }, { x: 1024, y: 360 }, { x: 1024, y: 400 }, { x: 1024, y: 440 }, { x: 1024, y: 480 }, { x: 1024, y: 520 }, { x: 1024, y: 560 }, { x: 1024, y: 600 }, { x: 998.4, y: 600 }, { x: 972.8, y: 600 }, { x: 947.2, y: 600 }, { x: 921.6, y: 600 }, { x: 896, y: 600 }, { x: 870.4, y: 600 }, { x: 844.8, y: 600 }, { x: 819.2, y: 600 }, { x: 793.6, y: 600 }, { x: 768, y: 600 }, { x: 768, y: 640 }, { x: 768, y: 680 }, { x: 768, y: 720 }, { x: 768, y: 760 }, { x: 768, y: 800 }, { x: 768, y: 840 }, { x: 768, y: 880 }, { x: 768, y: 920 }, { x: 768, y: 960 }, { x: 768, y: 1000 }, { x: 768, y: 1040 }, { x: 768, y: 1080 }, { x: 768, y: 1120 }, { x: 768, y: 1160 }, { x: 768, y: 1200 }, { x: 768, y: 1240 }, { x: 768, y: 1280 }, { x: 768, y: 1320 }, { x: 768, y: 1360 }, { x: 768, y: 1400 }, { x: 768, y: 1440 }, { x: 768, y: 1480 }, { x: 768, y: 1520 }, { x: 768, y: 1560 }, { x: 768, y: 1600 }, { x: 768, y: 1640 }, { x: 768, y: 1680 }, { x: 768, y: 1720 }, { x: 768, y: 1760 }, { x: 768, y: 1800 }, { x: 793.6, y: 1800 }, { x: 819.2, y: 1800 }, { x: 844.8, y: 1800 }, { x: 870.4, y: 1800 }, { x: 896, y: 1800 }, { x: 921.6, y: 1800 }, { x: 947.2, y: 1800 }, { x: 972.8, y: 1800 }, { x: 998.4, y: 1800 }, { x: 1024, y: 1800 }, { x: 1024, y: 1840 }, { x: 1024, y: 1880 }, { x: 1024, y: 1920 }, { x: 1024, y: 1960 }, { x: 1024, y: 2000 }, { x: 1024, y: 2040 }, { x: 1024, y: 2080 }, { x: 1024, y: 2120 }, { x: 1024, y: 2160 }, { x: 1024, y: 2200 }]; // Enemy path 2: Detailed eastern route path var enemyPath2 = [{ x: 2048, y: 800 }, { x: 2022.4, y: 800 }, { x: 1996.8, y: 800 }, { x: 1971.2, y: 800 }, { x: 1945.6, y: 800 }, { x: 1920, y: 800 }, { x: 1894.4, y: 800 }, { x: 1868.8, y: 800 }, { x: 1843.2, y: 800 }, { x: 1817.6, y: 800 }, { x: 1792, y: 800 }, { x: 1766.4, y: 800 }, { x: 1740.8, y: 800 }, { x: 1715.2, y: 800 }, { x: 1689.6, y: 800 }, { x: 1664, y: 800 }, { x: 1638.4, y: 800 }, { x: 1612.8, y: 800 }, { x: 1587.2, y: 800 }, { x: 1561.6, y: 800 }, { x: 1536, y: 800 }, { x: 1510.4, y: 800 }, { x: 1484.8, y: 800 }, { x: 1459.2, y: 800 }, { x: 1433.6, y: 800 }, { x: 1408, y: 800 }, { x: 1382.4, y: 800 }, { x: 1356.8, y: 800 }, { x: 1331.2, y: 800 }, { x: 1305.6, y: 800 }, { x: 1280, y: 800 }, { x: 1254.4, y: 800 }, { x: 1228.8, y: 800 }, { x: 1203.2, y: 800 }, { x: 1177.6, y: 800 }, { x: 1152, y: 800 }, { x: 1126.4, y: 800 }, { x: 1100.8, y: 800 }, { x: 1075.2, y: 800 }, { x: 1049.6, y: 800 }, { x: 1024, y: 800 }, { x: 1024, y: 780 }, { x: 1024, y: 760 }, { x: 1024, y: 740 }, { x: 1024, y: 720 }, { x: 1024, y: 700 }, { x: 1024, y: 680 }, { x: 1024, y: 660 }, { x: 1024, y: 640 }, { x: 1024, y: 620 }, { x: 1024, y: 600 }, { x: 1049.6, y: 600 }, { x: 1075.2, y: 600 }, { x: 1100.8, y: 600 }, { x: 1126.4, y: 600 }, { x: 1152, y: 600 }, { x: 1177.6, y: 600 }, { x: 1203.2, y: 600 }, { x: 1228.8, y: 600 }, { x: 1254.4, y: 600 }, { x: 1280, y: 600 }, { x: 1280, y: 640 }, { x: 1280, y: 680 }, { x: 1280, y: 720 }, { x: 1280, y: 760 }, { x: 1280, y: 800 }, { x: 1280, y: 840 }, { x: 1280, y: 880 }, { x: 1280, y: 920 }, { x: 1280, y: 960 }, { x: 1280, y: 1000 }, { x: 1280, y: 1040 }, { x: 1280, y: 1080 }, { x: 1280, y: 1120 }, { x: 1280, y: 1160 }, { x: 1280, y: 1200 }, { x: 1280, y: 1240 }, { x: 1280, y: 1280 }, { x: 1280, y: 1320 }, { x: 1280, y: 1360 }, { x: 1280, y: 1400 }, { x: 1280, y: 1440 }, { x: 1280, y: 1480 }, { x: 1280, y: 1520 }, { x: 1280, y: 1560 }, { x: 1280, y: 1600 }, { x: 1280, y: 1640 }, { x: 1280, y: 1680 }, { x: 1280, y: 1720 }, { x: 1280, y: 1760 }, { x: 1280, y: 1800 }, { x: 1254.4, y: 1800 }, { x: 1228.8, y: 1800 }, { x: 1203.2, y: 1800 }, { x: 1177.6, y: 1800 }, { x: 1152, y: 1800 }, { x: 1126.4, y: 1800 }, { x: 1100.8, y: 1800 }, { x: 1075.2, y: 1800 }, { x: 1049.6, y: 1800 }, { x: 1024, y: 1800 }, { x: 1024, y: 1840 }, { x: 1024, y: 1880 }, { x: 1024, y: 1920 }, { x: 1024, y: 1960 }, { x: 1024, y: 2000 }, { x: 1024, y: 2040 }, { x: 1024, y: 2080 }, { x: 1024, y: 2120 }, { x: 1024, y: 2160 }, { x: 1024, y: 2200 }]; // Enemy path 3: Detailed western route path var enemyPath3 = [{ x: 2048, y: 50 }, { x: 2022.4, y: 50 }, { x: 1996.8, y: 50 }, { x: 1971.2, y: 50 }, { x: 1945.6, y: 50 }, { x: 1920, y: 50 }, { x: 1894.4, y: 50 }, { x: 1868.8, y: 50 }, { x: 1843.2, y: 50 }, { x: 1817.6, y: 50 }, { x: 1792, y: 50 }, { x: 1766.4, y: 50 }, { x: 1740.8, y: 50 }, { x: 1715.2, y: 50 }, { x: 1689.6, y: 50 }, { x: 1664, y: 50 }, { x: 1638.4, y: 50 }, { x: 1612.8, y: 50 }, { x: 1587.2, y: 50 }, { x: 1561.6, y: 50 }, { x: 1536, y: 50 }, { x: 1510.4, y: 50 }, { x: 1484.8, y: 50 }, { x: 1459.2, y: 50 }, { x: 1433.6, y: 50 }, { x: 1408, y: 50 }, { x: 1382.4, y: 50 }, { x: 1356.8, y: 50 }, { x: 1331.2, y: 50 }, { x: 1305.6, y: 50 }, { x: 1280, y: 50 }, { x: 1280, y: 90 }, { x: 1280, y: 130 }, { x: 1280, y: 170 }, { x: 1280, y: 210 }, { x: 1280, y: 250 }, { x: 1280, y: 290 }, { x: 1280, y: 330 }, { x: 1280, y: 370 }, { x: 1280, y: 410 }, { x: 1280, y: 450 }, { x: 1280, y: 490 }, { x: 1280, y: 530 }, { x: 1280, y: 570 }, { x: 1280, y: 610 }, { x: 1280, y: 650 }, { x: 1280, y: 690 }, { x: 1280, y: 730 }, { x: 1280, y: 770 }, { x: 1280, y: 810 }, { x: 1280, y: 850 }, { x: 1280, y: 890 }, { x: 1280, y: 930 }, { x: 1280, y: 970 }, { x: 1280, y: 1010 }, { x: 1280, y: 1050 }, { x: 1280, y: 1090 }, { x: 1280, y: 1130 }, { x: 1280, y: 1170 }, { x: 1280, y: 1210 }, { x: 1280, y: 1250 }, { x: 1280, y: 1290 }, { x: 1280, y: 1330 }, { x: 1280, y: 1370 }, { x: 1280, y: 1410 }, { x: 1280, y: 1450 }, { x: 1280, y: 1490 }, { x: 1280, y: 1530 }, { x: 1280, y: 1570 }, { x: 1280, y: 1610 }, { x: 1280, y: 1650 }, { x: 1280, y: 1690 }, { x: 1280, y: 1730 }, { x: 1280, y: 1770 }, { x: 1280, y: 1810 }, { x: 1280, y: 1850 }, { x: 1254.4, y: 1800 }, { x: 1228.8, y: 1800 }, { x: 1203.2, y: 1800 }, { x: 1177.6, y: 1800 }, { x: 1152, y: 1800 }, { x: 1126.4, y: 1800 }, { x: 1100.8, y: 1800 }, { x: 1075.2, y: 1800 }, { x: 1049.6, y: 1800 }, { x: 1024, y: 1800 }, { x: 1024, y: 1840 }, { x: 1024, y: 1880 }, { x: 1024, y: 1920 }, { x: 1024, y: 1960 }, { x: 1024, y: 2000 }, { x: 1024, y: 2040 }, { x: 1024, y: 2080 }, { x: 1024, y: 2120 }, { x: 1024, y: 2160 }, { x: 1024, y: 2200 }]; var buildZones = []; var terrainFeatures = []; function createPath() { // Create all paths visible from the start for strategic planning // Create path 1 for (var i = 0; i < enemyPath1.length; i++) { var pathTile = isoContainer.addChild(LK.getAsset('path_tile', { anchorX: 0.5, anchorY: 0.5, x: enemyPath1[i].x, y: enemyPath1[i].y, alpha: 0.7 })); } // Create path 2 - always visible for (var i = 0; i < enemyPath2.length; i++) { var pathTile = isoContainer.addChild(LK.getAsset('path_tile', { anchorX: 0.5, anchorY: 0.5, x: enemyPath2[i].x, y: enemyPath2[i].y, alpha: 0.7 })); } // Create path 3 - always visible for (var i = 0; i < enemyPath3.length; i++) { var pathTile = isoContainer.addChild(LK.getAsset('path_tile', { anchorX: 0.5, anchorY: 0.5, x: enemyPath3[i].x, y: enemyPath3[i].y, alpha: 0.7 })); } // Create build zones around paths createBuildZones(); } function createTerrain() { // Create lake var lake = isoContainer.addChild(LK.getAsset('lake', { anchorX: 0.5, anchorY: 0.5, x: 400, y: 500, alpha: 0.8 })); // Create waterfall (three parallel lines) var waterfall1 = isoContainer.addChild(LK.getAsset('waterfall_line', { anchorX: 0.5, anchorY: 0.5, x: 1550, y: 300 })); var waterfall2 = isoContainer.addChild(LK.getAsset('waterfall_line', { anchorX: 0.5, anchorY: 0.5, x: 1560, y: 300 })); var waterfall3 = isoContainer.addChild(LK.getAsset('waterfall_line', { anchorX: 0.5, anchorY: 0.5, x: 1570, y: 300 })); // Create lake in the middle of the map var centralLake = isoContainer.addChild(LK.getAsset('lake', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 1366, alpha: 0.8, scaleX: 1.5, scaleY: 1.5 })); // Create left village (further west of castle) var leftVillagePositions = [{ x: 450, y: 2050 }, { x: 700, y: 2300 }, { x: 300, y: 2350 }, { x: 650, y: 2100 }]; for (var i = 0; i < leftVillagePositions.length; i++) { var pos = leftVillagePositions[i]; // Create house base var house = isoContainer.addChild(LK.getAsset('village_house', { anchorX: 0.5, anchorY: 0.5, x: pos.x, y: pos.y, alpha: 0.9 })); // Create roof var roof = isoContainer.addChild(LK.getAsset('village_house_roof', { anchorX: 0.5, anchorY: 0.5, x: pos.x, y: pos.y - 15, alpha: 0.9 })); } // Create right village (further east of castle) var rightVillagePositions = [{ x: 1600, y: 2050 }, { x: 1350, y: 2300 }, { x: 1750, y: 2250 }, { x: 1500, y: 2450 }]; for (var i = 0; i < rightVillagePositions.length; i++) { var pos = rightVillagePositions[i]; // Create house base var house = isoContainer.addChild(LK.getAsset('village_house', { anchorX: 0.5, anchorY: 0.5, x: pos.x, y: pos.y, alpha: 0.9 })); // Create roof var roof = isoContainer.addChild(LK.getAsset('village_house_roof', { anchorX: 0.5, anchorY: 0.5, x: pos.x, y: pos.y - 15, alpha: 0.9 })); } // Create 50 trees scattered across the map avoiding all objects var treePositions = []; // Helper function to check if a position conflicts with existing objects function isPositionSafe(x, y, buffer) { buffer = buffer || 60; // Check boundaries if (x < 50 || x > 1998 || y < 50 || y > 2682) return false; // Check castle area if (Math.sqrt((x - 1024) * (x - 1024) + (y - 2200) * (y - 2200)) < 300) return false; // Check waterfall area if (x >= 1500 && x <= 1600) return false; // Check central lake area if (Math.sqrt((x - 1024) * (x - 1024) + (y - 1366) * (y - 1366)) < 200) return false; // Check original lake area if (Math.sqrt((x - 400) * (x - 400) + (y - 500) * (y - 500)) < 150) return false; // Check all enemy paths with buffer var allPaths = [enemyPath1, enemyPath2, enemyPath3]; for (var p = 0; p < allPaths.length; p++) { var path = allPaths[p]; for (var i = 0; i < path.length; i++) { if (Math.sqrt((x - path[i].x) * (x - path[i].x) + (y - path[i].y) * (y - path[i].y)) < buffer + 20) { return false; } } } // Check build zones for (var i = 0; i < buildZones.length; i++) { if (Math.sqrt((x - buildZones[i].x) * (x - buildZones[i].x) + (y - buildZones[i].y) * (y - buildZones[i].y)) < buffer) { return false; } } // Check village house areas var allVillagePositions = leftVillagePositions.concat(rightVillagePositions); for (var i = 0; i < allVillagePositions.length; i++) { var house = allVillagePositions[i]; if (Math.sqrt((x - house.x) * (x - house.x) + (y - house.y) * (y - house.y)) < 100) { return false; } } // Check against other trees for (var i = 0; i < treePositions.length; i++) { var tree = treePositions[i]; if (Math.sqrt((x - tree.x) * (x - tree.x) + (y - tree.y) * (y - tree.y)) < 80) { return false; } } return true; } // Generate 50 tree positions var attempts = 0; while (treePositions.length < 50 && attempts < 2000) { attempts++; var x = 50 + Math.random() * (1998 - 50); var y = 50 + Math.random() * (2682 - 50); if (isPositionSafe(x, y, 80)) { treePositions.push({ x: x, y: y }); } } // Create the trees for (var i = 0; i < treePositions.length; i++) { var pos = treePositions[i]; var tree = isoContainer.addChild(LK.getAsset('tree', { anchorX: 0.5, anchorY: 0.5, x: pos.x + (Math.random() - 0.5) * 40, // Add random offset y: pos.y + (Math.random() - 0.5) * 40, scaleX: 0.8 + Math.random() * 0.4, // Scale variation 0.8x to 1.2x scaleY: 0.8 + Math.random() * 0.4, alpha: 0.9 })); } } function createBuildZones() { var zones = [ // enemyPath1: y=200, both sides { x: 0, y: 150 }, { x: 0, y: 250 }, { x: 100, y: 150 }, { x: 100, y: 250 }, { x: 200, y: 150 }, { x: 200, y: 250 }, { x: 300, y: 150 }, { x: 300, y: 250 }, { x: 400, y: 150 }, { x: 400, y: 250 }, { x: 500, y: 150 }, { x: 500, y: 250 }, { x: 600, y: 150 }, { x: 600, y: 250 }, { x: 700, y: 150 }, { x: 700, y: 250 }, { x: 800, y: 150 }, { x: 800, y: 250 }, { x: 900, y: 150 }, { x: 900, y: 250 }, // enemyPath1: Corner (1024, 200, right turn) { x: 974, y: 150 }, { x: 1074, y: 150 }, { x: 974, y: 250 }, { x: 1074, y: 250 }, // enemyPath1: U-shape (x=1024, y=200-600) { x: 974, y: 300 }, { x: 1074, y: 300 }, { x: 974, y: 400 }, { x: 1074, y: 400 }, { x: 974, y: 500 }, { x: 1074, y: 500 }, // enemyPath1: Corner (1024, 600, left turn, intersection) { x: 974, y: 550 }, { x: 1074, y: 550 }, { x: 974, y: 650 }, { x: 1074, y: 650 }, // enemyPath1: U-shape (x=768, y=600-1800) { x: 718, y: 700 }, { x: 818, y: 700 }, { x: 718, y: 800 }, { x: 818, y: 800 }, { x: 718, y: 900 }, { x: 818, y: 900 }, { x: 718, y: 1000 }, { x: 818, y: 1000 }, { x: 718, y: 1100 }, { x: 818, y: 1100 }, { x: 718, y: 1200 }, { x: 818, y: 1200 }, { x: 718, y: 1300 }, { x: 818, y: 1300 }, { x: 718, y: 1400 }, { x: 818, y: 1400 }, { x: 718, y: 1500 }, { x: 818, y: 1500 }, { x: 718, y: 1600 }, { x: 818, y: 1600 }, { x: 718, y: 1700 }, { x: 818, y: 1700 }, // enemyPath1: Corner (768, 1800, right turn) { x: 718, y: 1750 }, { x: 818, y: 1750 }, { x: 718, y: 1850 }, { x: 818, y: 1850 }, // enemyPath1: Corner (896, 1800, right turn) { x: 846, y: 1750 }, { x: 946, y: 1750 }, { x: 846, y: 1850 }, { x: 946, y: 1850 }, // enemyPath1: Corner (1024, 1800, down, intersection) { x: 974, y: 1750 }, { x: 1074, y: 1750 }, { x: 974, y: 1850 }, { x: 1074, y: 1850 }, // enemyPath2: y=800, both sides { x: 2048, y: 750 }, { x: 2048, y: 850 }, { x: 1948, y: 750 }, { x: 1948, y: 850 }, { x: 1848, y: 750 }, { x: 1848, y: 850 }, { x: 1748, y: 750 }, { x: 1748, y: 850 }, { x: 1648, y: 750 }, { x: 1648, y: 850 }, { x: 1548, y: 750 }, { x: 1548, y: 850 }, { x: 1448, y: 750 }, { x: 1448, y: 850 }, { x: 1348, y: 750 }, { x: 1348, y: 850 }, { x: 1248, y: 750 }, { x: 1248, y: 850 }, { x: 1148, y: 750 }, { x: 1148, y: 850 }, // enemyPath2: Corner (1024, 800, down) { x: 974, y: 750 }, { x: 1074, y: 750 }, { x: 974, y: 850 }, { x: 1074, y: 850 }, // enemyPath2: U-shape (x=1024, y=600-800) { x: 974, y: 650 }, { x: 1074, y: 650 }, { x: 974, y: 700 }, { x: 1074, y: 700 }, // enemyPath2: Corner (1024, 600, right turn, intersection) { x: 974, y: 550 }, { x: 1074, y: 550 }, { x: 974, y: 650 }, { x: 1074, y: 650 }, // enemyPath2: U-shape (x=1280, y=600-1800) { x: 1230, y: 650 }, { x: 1330, y: 650 }, { x: 1230, y: 750 }, { x: 1330, y: 750 }, { x: 1230, y: 850 }, { x: 1330, y: 850 }, { x: 1230, y: 950 }, { x: 1330, y: 950 }, { x: 1230, y: 1050 }, { x: 1330, y: 1050 }, { x: 1230, y: 1150 }, { x: 1330, y: 1150 }, { x: 1230, y: 1250 }, { x: 1330, y: 1250 }, { x: 1230, y: 1350 }, { x: 1330, y: 1350 }, { x: 1230, y: 1450 }, { x: 1330, y: 1450 }, { x: 1230, y: 1550 }, { x: 1330, y: 1550 }, { x: 1230, y: 1650 }, { x: 1330, y: 1650 }, // enemyPath2: Corner (1280, 1800, left turn) { x: 1230, y: 1750 }, { x: 1330, y: 1750 }, { x: 1230, y: 1850 }, { x: 1330, y: 1850 }, // enemyPath2: Corner (1152, 1800, left turn) { x: 1102, y: 1750 }, { x: 1202, y: 1750 }, { x: 1102, y: 1850 }, { x: 1202, y: 1850 }, // enemyPath3: y=50, both sides { x: 2048, y: 0 }, { x: 2048, y: 100 }, { x: 1948, y: 0 }, { x: 1948, y: 100 }, { x: 1848, y: 0 }, { x: 1848, y: 100 }, { x: 1748, y: 0 }, { x: 1748, y: 100 }, { x: 1648, y: 0 }, { x: 1648, y: 100 }, { x: 1548, y: 0 }, { x: 1548, y: 100 }, { x: 1448, y: 0 }, { x: 1448, y: 100 }, // enemyPath3: Corner (1280, 50, down) { x: 1230, y: 0 }, { x: 1330, y: 0 }, { x: 1230, y: 100 }, { x: 1330, y: 100 }, // enemyPath3: U-shape (x=1280, y=50-1800) { x: 1230, y: 150 }, { x: 1330, y: 150 }, { x: 1230, y: 250 }, { x: 1330, y: 250 }, { x: 1230, y: 350 }, { x: 1330, y: 350 }, { x: 1230, y: 450 }, { x: 1330, y: 450 }, // enemyPath3: Corner (1280, 1800, left turn) { x: 1230, y: 1750 }, { x: 1330, y: 1750 }, { x: 1230, y: 1850 }, { x: 1330, y: 1850 }, // enemyPath3: Corner (1152, 1800, left turn) { x: 1102, y: 1750 }, { x: 1202, y: 1750 }, { x: 1102, y: 1850 }, { x: 1202, y: 1850 }]; for (var i = 0; i < zones.length; i++) { var zone = isoContainer.addChild(LK.getAsset('build_zone', { anchorX: 0.5, anchorY: 0.5, x: zones[i].x, y: zones[i].y, alpha: 0.3 })); zone.zoneData = { x: zones[i].x, y: zones[i].y, occupied: false, originalAlpha: 0.3 }; buildZones.push(zone); } } function updateBuildZoneVisuals() { for (var i = 0; i < buildZones.length; i++) { var zone = buildZones[i]; if (zone.zoneData.occupied) { // Occupied zones are invisible zone.alpha = 0; } else if (selectedTowerType) { // Available zones are highlighted when a tower is selected zone.alpha = 0.6; zone.tint = 0x00FF00; // Green highlight for available zones } else { // Normal state when no tower is selected zone.alpha = zone.zoneData.originalAlpha; zone.tint = 0xFFFFFF; // Reset tint } } } var castleHealthBarBg = null; var castleHealthBarFill = null; function createCastle() { var castle = isoContainer.addChild(LK.getAsset('castle_' + castleType, { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 2200 })); // Create health bar background positioned under the castle (moved down) castleHealthBarBg = isoContainer.addChild(LK.getAsset('build_zone', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 2350, scaleX: 4, scaleY: 0.4 })); castleHealthBarBg.tint = 0x333333; // Create health bar fill positioned under the castle (moved down) castleHealthBarFill = isoContainer.addChild(LK.getAsset('build_zone', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 2350, scaleX: 4, scaleY: 0.4 })); castleHealthBarFill.tint = 0x00FF00; // Store references for updating castle.healthBarBg = castleHealthBarBg; castle.healthBarFill = castleHealthBarFill; } function createShop() { var shopBg = LK.gui.bottom.addChild(LK.getAsset('shop_bg', { anchorX: 0.5, anchorY: 1, alpha: 1.0 })); shopBg.tint = 0x000000; var infantryBtn = LK.gui.bottom.addChild(LK.getAsset('tower_infantry', { anchorX: 0.5, anchorY: 1, x: -400, y: -60 })); var frostBtn = LK.gui.bottom.addChild(LK.getAsset('tower_frost', { anchorX: 0.5, anchorY: 1, x: -200, y: -60 })); var antiairBtn = LK.gui.bottom.addChild(LK.getAsset('tower_antiair', { anchorX: 0.5, anchorY: 1, x: 0, y: -60 })); var poisonBtn = LK.gui.bottom.addChild(LK.getAsset('tower_poison', { anchorX: 0.5, anchorY: 1, x: 200, y: -60 })); var teslaBtn = LK.gui.bottom.addChild(LK.getAsset('tower_tesla', { anchorX: 0.5, anchorY: 1, x: 400, y: -60 })); // Add cost labels with larger, more distinct text var infantryCost = LK.gui.bottom.addChild(new Text2('80g', { size: 32, fill: 0xFFD700 })); infantryCost.anchor.set(0.5, 1); infantryCost.x = -400; infantryCost.y = -130; var frostCost = LK.gui.bottom.addChild(new Text2('120g', { size: 32, fill: 0xFFD700 })); frostCost.anchor.set(0.5, 1); frostCost.x = -200; frostCost.y = -130; var antiairCost = LK.gui.bottom.addChild(new Text2('150g', { size: 32, fill: 0xFFD700 })); antiairCost.anchor.set(0.5, 1); antiairCost.x = 0; antiairCost.y = -130; var poisonCost = LK.gui.bottom.addChild(new Text2('100g', { size: 32, fill: 0xFFD700 })); poisonCost.anchor.set(0.5, 1); poisonCost.x = 200; poisonCost.y = -130; var teslaCost = LK.gui.bottom.addChild(new Text2('180g', { size: 32, fill: 0xFFD700 })); teslaCost.anchor.set(0.5, 1); teslaCost.x = 400; teslaCost.y = -130; // Add tower name labels below the cost text var infantryName = LK.gui.bottom.addChild(new Text2('Infantry', { size: 28, fill: 0xFFFFFF })); infantryName.anchor.set(0.5, 1); infantryName.x = -400; infantryName.y = -160; var frostName = LK.gui.bottom.addChild(new Text2('Freezer', { size: 28, fill: 0xFFFFFF })); frostName.anchor.set(0.5, 1); frostName.x = -200; frostName.y = -160; var antiairName = LK.gui.bottom.addChild(new Text2('Air', { size: 28, fill: 0xFFFFFF })); antiairName.anchor.set(0.5, 1); antiairName.x = 0; antiairName.y = -160; var poisonName = LK.gui.bottom.addChild(new Text2('Poison', { size: 28, fill: 0xFFFFFF })); poisonName.anchor.set(0.5, 1); poisonName.x = 200; poisonName.y = -160; var teslaName = LK.gui.bottom.addChild(new Text2('Tesla', { size: 28, fill: 0xFFFFFF })); teslaName.anchor.set(0.5, 1); teslaName.x = 400; teslaName.y = -160; infantryBtn.down = function () { if (coins >= 80) { selectedTowerType = 'infantry'; updateBuildZoneVisuals(); } }; frostBtn.down = function () { if (coins >= 120) { selectedTowerType = 'frost'; updateBuildZoneVisuals(); } }; antiairBtn.down = function () { if (coins >= 150) { selectedTowerType = 'antiair'; updateBuildZoneVisuals(); } }; poisonBtn.down = function () { if (coins >= 100) { selectedTowerType = 'poison'; updateBuildZoneVisuals(); } }; teslaBtn.down = function () { if (coins >= 180) { selectedTowerType = 'tesla'; updateBuildZoneVisuals(); } }; } function createWave() { if (currentWave >= maxWaves) { LK.showYouWin(); return; } currentWave++; waveEnemies = []; // Scale enemy count based on wave number var enemyCount; if (currentWave <= 9) { // Waves 1-9: 10-15 enemies enemyCount = 10 + Math.floor(Math.random() * 6); // Random between 10 and 15 } else { // Wave 10+: 15-20 enemies initially, with slight increase over time var baseCount = 15 + Math.floor(Math.random() * 6); // Random between 15 and 20 var extraEnemies = Math.floor((currentWave - 10) / 5); // +1 enemy every 5 waves after wave 10 enemyCount = Math.min(30, baseCount + extraEnemies); // Cap at 30 enemies max } // Calculate available enemy strength levels based on wave var maxStrengthLevel = Math.min(10, Math.floor((currentWave + 1) / 2)); var minStrengthLevel = Math.max(1, maxStrengthLevel - 2); for (var i = 0; i < enemyCount; i++) { // Define which enemies can appear based on wave number var availableEnemies = []; if (currentWave >= 1 && currentWave <= 5) availableEnemies.push('goblin'); if (currentWave >= 3 && currentWave <= 8) availableEnemies.push('crawler'); if (currentWave >= 5 && currentWave <= 10) availableEnemies.push('bandit'); if (currentWave >= 7 && currentWave <= 12) availableEnemies.push('golem'); if (currentWave >= 9 && currentWave <= 14) availableEnemies.push('imp'); if (currentWave >= 11 && currentWave <= 18) availableEnemies.push('knight'); if (currentWave >= 13 && currentWave <= 20) availableEnemies.push('beast'); if (currentWave >= 15 && currentWave <= 25) availableEnemies.push('mage'); if (currentWave >= 17) availableEnemies.push('reaver'); if (currentWave >= 45) availableEnemies.push('doom'); // Select random enemy from available types var enemyType = availableEnemies[Math.floor(Math.random() * availableEnemies.length)]; // Assign strength level within available range var strengthLevel = minStrengthLevel + Math.floor(Math.random() * (maxStrengthLevel - minStrengthLevel + 1)); var pathChoice = 1; if (currentWave >= 6) { pathChoice = Math.floor(Math.random() * 3) + 1; } else if (currentWave >= 3) { pathChoice = Math.floor(Math.random() * 2) + 1; } waveEnemies.push({ type: enemyType, pathIndex: pathChoice, strengthLevel: strengthLevel, spawnTime: LK.ticks + i * 90 }); } waveInProgress = true; LK.getSound('wave_start').play(); } // Create background panel for castle lives text var castleLivesTextBg = isoContainer.addChild(LK.getAsset('build_zone', { anchorX: 0, anchorY: 0.5, x: 30, y: 1900, scaleX: 8, scaleY: 1.8 })); castleLivesTextBg.tint = 0x000000; castleLivesTextBg.alpha = 0.8; var castleLivesText = new Text2('Castle Health: 100', { size: 70, fill: 0xFF4444, font: "'Arial Black', Impact, 'Arial Bold'" }); castleLivesText.anchor.set(0, 0.5); castleLivesText.x = 50; castleLivesText.y = 1900; isoContainer.addChild(castleLivesText); // Create background panel for gold text var coinsTextBg = isoContainer.addChild(LK.getAsset('build_zone', { anchorX: 0, anchorY: 0.5, x: 30, y: 2180, scaleX: 6, scaleY: 1.8 })); coinsTextBg.tint = 0x000000; coinsTextBg.alpha = 0.8; var coinsText = new Text2('Gold: 300', { size: 70, fill: 0xFFD700, font: "'Arial Black', Impact, 'Arial Bold'" }); coinsText.anchor.set(0, 0.5); coinsText.x = 50; coinsText.y = 2180; isoContainer.addChild(coinsText); var waveText = new Text2('Wave: 0/50', { size: 40, fill: 0xFFFFFF }); waveText.anchor.set(0.5, 0); LK.gui.top.addChild(waveText); createPath(); createTerrain(); createCastle(); createShop(); game.down = function (x, y, obj) { // Clear any existing upgrade text and range indicator when clicking elsewhere if (upgradeText && upgradeText.destroy && selectedTower && obj && !selectedTower.intersects(obj)) { upgradeText.destroy(); upgradeText = null; selectedTower = null; // Clear range indicator if (activeRangeIndicator && activeRangeIndicator.destroy) { activeRangeIndicator.destroy(); activeRangeIndicator = null; } } // Use screen coordinates directly for top-down view var isoPos = { x: x, y: y }; if (selectedTowerType && gameState === 'playing') { // Check if clicking on a valid, unoccupied build zone var validBuildZone = null; for (var i = 0; i < buildZones.length; i++) { if (!buildZones[i].zoneData.occupied) { var distance = Math.sqrt((buildZones[i].x - isoPos.x) * (buildZones[i].x - isoPos.x) + (buildZones[i].y - isoPos.y) * (buildZones[i].y - isoPos.y)); if (distance < 40) { validBuildZone = buildZones[i]; break; } } } // Only place tower if clicking on a valid build zone and player has enough coins if (validBuildZone) { var tower = new Tower(selectedTowerType); if (coins >= tower.cost) { // Place tower exactly at build zone center tower.x = validBuildZone.x; tower.y = validBuildZone.y; towers.push(tower); isoContainer.addChild(tower); coins -= tower.cost; coinsText.setText('Gold: ' + coins); selectedTowerType = null; // Mark build zone as occupied validBuildZone.zoneData.occupied = true; updateBuildZoneVisuals(); } } else { // Clear selection if clicking outside build zones selectedTowerType = null; updateBuildZoneVisuals(); } } }; if (gameState === 'setup') { gameState = 'playing'; createWave(); } game.update = function () { if (gameState !== 'playing') return; if (waveInProgress) { for (var i = waveEnemies.length - 1; i >= 0; i--) { var enemyData = waveEnemies[i]; if (LK.ticks >= enemyData.spawnTime) { var enemy = new Enemy(enemyData.type, currentWave, enemyData.pathIndex, enemyData.strengthLevel); enemies.push(enemy); isoContainer.addChild(enemy); waveEnemies.splice(i, 1); } } if (waveEnemies.length === 0 && enemies.length === 0) { waveInProgress = false; nextWaveTimer = LK.ticks + 180; } } if (!waveInProgress && LK.ticks >= nextWaveTimer && enemies.length === 0) { createWave(); } for (var i = enemies.length - 1; i >= 0; i--) { var enemy = enemies[i]; if (enemy.reachedCastle) { castleHealth -= 15; // Update castle lives text castleLivesText.setText('Castle Health: ' + castleHealth); // Update health bar with smooth animation var healthPercent = castleHealth / 100; if (castleHealthBarFill) { // Animate health bar scale change smoothly tween(castleHealthBarFill, { scaleX: 4 * healthPercent }, { duration: 300 }); // Update color based on health percentage - green to red gradient var redComponent = Math.floor(255 * (1 - healthPercent)); var greenComponent = Math.floor(255 * healthPercent); var healthColor = redComponent << 16 | greenComponent << 8 | 0x00; castleHealthBarFill.tint = healthColor; // Flash effect when taking damage LK.effects.flashObject(castleHealthBarFill, 0xFF0000, 200); } enemy.destroy(); enemies.splice(i, 1); if (castleHealth <= 0) { LK.showGameOver(); return; } } } for (var i = projectiles.length - 1; i >= 0; i--) { var projectile = projectiles[i]; if (projectile.hasReachedTarget) { if (projectile.type === 'cannonball') { for (var j = 0; j < enemies.length; j++) { var enemy = enemies[j]; var distance = Math.sqrt((enemy.x - projectile.x) * (enemy.x - projectile.x) + (enemy.y - projectile.y) * (enemy.y - projectile.y)); if (distance <= 50) { if (enemy.takeDamage(projectile.damage)) { coins += enemy.coinValue; coinsText.setText('Gold: ' + coins); enemy.destroy(); enemies.splice(j, 1); j--; } } } } else { for (var j = 0; j < enemies.length; j++) { var enemy = enemies[j]; var distance = Math.sqrt((enemy.x - projectile.x) * (enemy.x - projectile.x) + (enemy.y - projectile.y) * (enemy.y - projectile.y)); if (distance <= 25) { if (enemy.takeDamage(projectile.damage)) { coins += enemy.coinValue; coinsText.setText('Gold: ' + coins); enemy.destroy(); enemies.splice(j, 1); } break; } } } projectile.destroy(); projectiles.splice(i, 1); } else if (projectile.x < -50 || projectile.x > 2098 || projectile.y < -50 || projectile.y > 2782) { projectile.destroy(); projectiles.splice(i, 1); } } waveText.setText('Wave: ' + currentWave + '/' + maxWaves); };
===================================================================
--- original.js
+++ change.js
@@ -128,9 +128,9 @@
self.type = type;
self.damage = damage;
self.targetX = targetX;
self.targetY = targetY;
- self.speed = type === 'arrow' ? 8 : type === 'cannonball' ? 5 : 6;
+ self.speed = type === 'arrow' ? 16 : type === 'cannonball' ? 12 : 14;
var graphics = self.attachAsset('projectile_' + type, {
anchorX: 0.5,
anchorY: 0.5
});
@@ -245,9 +245,26 @@
self.upgrade = function () {
if (self.level < self.maxLevel) {
self.level++;
self.damage = Math.floor(self.damage * 1.3);
- self.range = Math.floor(self.range * (1 + (self.level - 1) * 0.1)); // 10% range increase per level
+ // Calculate range increase with diminishing returns
+ var rangeMultiplier = 1.0;
+ if (self.level === 2) {
+ rangeMultiplier = 1.10; // +10% for level 1 upgrade
+ } else if (self.level === 3) {
+ rangeMultiplier = 1.10 * 1.05; // +10% then +5%
+ } else if (self.level === 4) {
+ rangeMultiplier = 1.10 * 1.05 * 1.035; // +10%, +5%, then +3.5%
+ } else if (self.level >= 5) {
+ // +10%, +5%, +3.5%, then +2% for each additional level
+ rangeMultiplier = 1.10 * 1.05 * 1.035;
+ var additionalLevels = self.level - 4;
+ rangeMultiplier *= Math.pow(1.02, additionalLevels);
+ }
+ // Apply range multiplier to base range for this tower type
+ var baseRange;
+ if (self.type === 'infantry') baseRange = 100;else if (self.type === 'frost') baseRange = 90;else if (self.type === 'antiair') baseRange = 130;else if (self.type === 'poison') baseRange = 80;else if (self.type === 'tesla') baseRange = 95;
+ self.range = Math.floor(baseRange * rangeMultiplier);
self.attackSpeed = Math.max(200, Math.floor(self.attackSpeed * 0.95));
// Visual upgrade indicators
var tintColors = [0xFFFFFF, 0xFFD700, 0xFF6347, 0xFF4500, 0x8A2BE2, 0x00FF00, 0x00FFFF, 0xFF69B4, 0xFFFF00, 0xFF0000];
graphics.tint = tintColors[self.level - 1] || 0xFF0000;
A lake with a 3d view. In-Game asset. 2d. High contrast. No shadows
tree. In-Game asset. 2d. High contrast. No shadows
house. In-Game asset. 2d. High contrast. No shadows
magic ball. In-Game asset. 2d. High contrast. No shadows
arrow bullet. In-Game asset. 2d. High contrast. No shadows
realistic majestic castle. In-Game asset. 2d. High contrast. No shadows
dirt road. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
This is a button, the outer part is red, the middle circle is red, the 2nd circle is dark gray. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
village house. In-Game asset. 2d. High contrast. No shadows
poisonous tower realistic. In-Game asset. 2d. High contrast. No shadows
freezing tower realistic. In-Game asset. 2d. High contrast. No shadows
goblin. In-Game asset. 2d. High contrast. No shadows
goblin emperor. In-Game asset. 2d. High contrast. No shadows
haydut goblin. In-Game asset. 2d. High contrast. No shadows
titan goblin. In-Game asset. 2d. High contrast. No shadows
goblin tazısı. In-Game asset. 2d. High contrast. No shadows
gezgin goblin. In-Game asset. 2d. High contrast. No shadows
kıyamet canavarı goblin. In-Game asset. 2d. High contrast. No shadows
kemikli iskelet goblin. In-Game asset. 2d. High contrast. No shadows
buzul goblin. In-Game asset. 2d. High contrast. No shadows
golem goblin. In-Game asset. 2d. High contrast. No shadows
kadın goblin imparatoru. In-Game asset. 2d. High contrast. No shadows
demir goblin. In-Game asset. 2d. High contrast. No shadows
şövalye goblin. In-Game asset. 2d. High contrast. No shadows
obsidyen dev goblin. In-Game asset. 2d. High contrast. No shadows
üzerinde okçu olan, ahşap, okçu kulesi. In-Game asset. 2d. High contrast. No shadows
ahşap hava savunma kulesi, üstünde arbalet olsun. In-Game asset. 2d. High contrast. No shadows
antik elektrik atıcı kule. In-Game asset. 2d. High contrast. No shadows
goblin canavar. In-Game asset. 2d. High contrast. No shadows
volkanik gezgini goblin. In-Game asset. 2d. High contrast. No shadows
kahverengi gezgin eli bıçaklı goblin. In-Game asset. 2d. High contrast. No shadows
büyücü goblin. In-Game asset. 2d. High contrast. No shadows
paslı böcek goblin. In-Game asset. 2d. High contrast. No shadows
spektral yıkıcı goblin. In-Game asset. 2d. High contrast. No shadows
taş goblin. In-Game asset. 2d. High contrast. No shadows
kimyasalcı goblin, boydan. In-Game asset. 2d. High contrast. No shadows
yuvarlak havan topu mermisi. In-Game asset. 2d. High contrast. No shadows
karanlık büyücü goblin. In-Game asset. 2d. High contrast. No shadows
yeşil yapraklı dövüşçü goblin. In-Game asset. 2d. High contrast. No shadows
kayalık. In-Game asset. 2d. High contrast. No shadows
uçan küllü goblin. In-Game asset. 2d. High contrast. No shadows
uçan goblin yılan. In-Game asset. 2d. High contrast. No shadows
kırmızı kaslı uçan goblin. In-Game asset. 2d. High contrast. No shadows
uçan karanlık goblin. In-Game asset. 2d. High contrast. No shadows
uçan goblin yağmacı. In-Game asset. 2d. High contrast. No shadows
uçan fırtına golem. In-Game asset. 2d. High contrast. No shadows