User prompt
revert back last enemy health update
User prompt
decrease enemy health by 25%
User prompt
when enemies reach home base, they shall not damage the home base by 10 points. they shall damage the home based equal to their health left.
User prompt
make home base health 10000 increase enemy health by 100%
User prompt
revert back last change only. elemental damage over time was correct. just make it 50% instead of 20%. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
fire damage over time is not 20% of level 1 wizard. it shall be 20% of the hit enemy took. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
enemies hit by fire wizard takes an additional 20% damage of that hit over 5 seconds. this shall not effect gold award at all. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
divide enemy hp by 11
User prompt
increase enemy hp by 1000%
User prompt
make damage number text x3
User prompt
show damages on screen ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
decrease light wizard stun to 1 second. enemies hit by nature wizard takes 20% extra damage for 5 seconds. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
revert back light wizard change only
User prompt
enemies hit by light wizard takes 10% additional damage for 5 seconds ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
enemies hit by dark wizard are knockbacked for 1 second and knockbacked enemies returns exact same path they came from ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
revert back last change
User prompt
knocbacked enemies shall move backwards in the path they came from ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
enemies hit by dark wizard moves backwards for 1 second ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
enemies hit by light wizard is stunned for 3 seconds. stunned enemies cannot move. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
enemies hit by ice wizard is slowed by 50% for 3 seconds ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
revert back last two commands
User prompt
gold shall only be awarded after enemy is dead
User prompt
fire wizard's damage shall also have an additional burning damage over time equal to half of its elemental damage over 5 seconds ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
levels of wizard shall on their top with black and bold text
User prompt
upgrade cost of wizard shall be %60 of its initial cost based on its level. for example upgrading level 1 wizard to level 2 should not be 900 but 300.
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ var Enemy = Container.expand(function (enemyType, level) { var self = Container.call(this); self.enemyType = enemyType || 'basic'; self.level = level || 1; self.pathIndex = 0; self.pathProgress = 0; self.speed = 2; switch (self.enemyType) { case 'basic': self.maxHP = (100 + level * 50) * 5; self.speed = 2; break; case 'fast': self.maxHP = (50 + level * 25) * 5; self.speed = 4; break; case 'tank': self.maxHP = (300 + level * 100) * 5; self.speed = 1; break; } self.hp = self.maxHP; self.goldValue = self.maxHP; var enemyGraphics = self.attachAsset('enemy_' + self.enemyType, { anchorX: 0.5, anchorY: 0.5 }); var hpBar = new Container(); var hpBackground = LK.getAsset('ui_background', { width: 60, height: 8, anchorX: 0.5, anchorY: 0.5 }); hpBackground.tint = 0x000000; hpBar.addChild(hpBackground); var hpFill = LK.getAsset('ui_background', { width: 60, height: 8, anchorX: 0, anchorY: 0.5 }); hpFill.tint = 0x00ff00; hpFill.x = -30; hpBar.addChild(hpFill); hpBar.y = -40; self.addChild(hpBar); self.update = function () { if (currentPath && currentPath.length > 0) { self.followPath(); } // Update HP bar var hpPercent = self.hp / self.maxHP; hpFill.width = 60 * hpPercent; hpFill.tint = hpPercent > 0.5 ? 0x00ff00 : hpPercent > 0.25 ? 0xffff00 : 0xff0000; }; self.followPath = function () { if (self.pathIndex >= currentPath.length) { self.reachBase(); return; } var targetPoint = currentPath[self.pathIndex]; var dx = targetPoint.x - self.x; var dy = targetPoint.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < 5) { self.pathIndex++; return; } self.x += dx / distance * self.speed; self.y += dy / distance * self.speed; }; self.takeDamage = function (damage) { self.hp -= damage; LK.getSound('enemyHit').play(); if (self.hp <= 0) { self.die(); } }; self.die = function () { gold += self.goldValue / 10; updateGoldDisplay(); LK.getSound('enemyDeath').play(); for (var i = 0; i < enemies.length; i++) { if (enemies[i] === self) { enemies.splice(i, 1); break; } } self.destroy(); }; self.reachBase = function () { baseHP -= 10; updateBaseHPDisplay(); if (baseHP <= 0) { LK.showGameOver(); } for (var i = 0; i < enemies.length; i++) { if (enemies[i] === self) { enemies.splice(i, 1); break; } } self.destroy(); }; return self; }); var Projectile = Container.expand(function (startX, startY, target, damage) { var self = Container.call(this); self.x = startX; self.y = startY; self.target = target; self.damage = damage; self.speed = 8; var projectileGraphics = self.attachAsset('projectile', { anchorX: 0.5, anchorY: 0.5 }); self.update = function () { if (!self.target || self.target.destroyed) { self.destroy(); return; } var dx = self.target.x - self.x; var dy = self.target.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < 10) { self.target.takeDamage(self.damage); self.destroy(); return; } self.x += dx / distance * self.speed; self.y += dy / distance * self.speed; }; return self; }); var TowerSlot = Container.expand(function (x, y, towerNumber) { var self = Container.call(this); self.x = x; self.y = y; self.isEmpty = true; self.wizard = null; self.towerNumber = towerNumber; var slotGraphics = self.attachAsset('tower_slot', { anchorX: 0.5, anchorY: 0.5 }); var towerNumberText = new Text2(self.towerNumber.toString(), { size: 20, fill: 0xFFFFFF }); towerNumberText.anchor.set(0.5, 0.5); towerNumberText.y = 0; self.addChild(towerNumberText); self.down = function (x, y, obj) { if (self.isEmpty && (gameState === 'setup' || gameState === 'playing')) { selectedTowerSlot = self; gameState = 'wizard_selection'; createWizardSelectionPopup(); } else if (!self.isEmpty && self.wizard && (gameState === 'setup' || gameState === 'playing')) { // Upgrade existing wizard selectedTowerSlot = self; gameState = 'wizard_upgrade'; createWizardUpgradePopup(); } }; self.placeWizard = function (wizardType) { var cost = 500; if (gold >= cost) { gold -= cost; updateGoldDisplay(); self.wizard = new Wizard(wizardType, 1); self.wizard.x = self.x; self.wizard.y = self.y; self.isEmpty = false; game.addChild(self.wizard); wizards.push(self.wizard); draggedWizard = null; } }; return self; }); var Wizard = Container.expand(function (elementType, level) { var self = Container.call(this); self.elementType = elementType || 'fire'; self.level = level || 1; self.elementalDamage = 100 * self.level; self.magicDamage = 100 * self.level; self.range = (150 + self.level * 20) * 1.15; self.attackSpeed = (1000 - self.level * 50) * 0.9; // ms between attacks - 10% faster self.lastAttackTime = 0; self.cost = 500 * Math.pow(1.8, self.level - 1); var wizardGraphics = self.attachAsset('wizard_' + self.elementType, { anchorX: 0.5, anchorY: 0.5 }); var levelText = new Text2(self.level.toString(), { size: 24, fill: 0xFFFFFF }); levelText.anchor.set(0.5, 0.5); levelText.y = -10; self.addChild(levelText); self.update = function () { if (LK.ticks - self.lastAttackTime > self.attackSpeed / 16.67) { var target = self.findTarget(); if (target) { self.attack(target); self.lastAttackTime = LK.ticks; } } }; self.findTarget = function () { var closestEnemy = null; var closestDistance = Infinity; for (var i = 0; i < enemies.length; i++) { var enemy = enemies[i]; var distance = Math.sqrt(Math.pow(enemy.x - self.x, 2) + Math.pow(enemy.y - self.y, 2)); if (distance <= self.range && distance < closestDistance) { closestDistance = distance; closestEnemy = enemy; } } return closestEnemy; }; self.attack = function (target) { var projectile = new Projectile(self.x, self.y, target, self.elementalDamage + self.magicDamage); projectiles.push(projectile); game.addChild(projectile); LK.getSound('shoot').play(); }; return self; }); var WizardIcon = Container.expand(function (elementType) { var self = Container.call(this); self.elementType = elementType; var iconGraphics = self.attachAsset('wizard_' + elementType, { anchorX: 0.5, anchorY: 0.5, scaleX: 0.8, scaleY: 0.8 }); var costText = new Text2('500', { size: 20, fill: 0xFFFFFF }); costText.anchor.set(0.5, 0); costText.y = 45; self.addChild(costText); self.down = function (x, y, obj) { // Wizard icons are now handled by tower slots }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x228B22 }); /**** * Game Code ****/ // Add cobblestone background var cobblestoneBackground = game.addChild(LK.getAsset('cobblestone_bg', { anchorX: 0, anchorY: 0 })); cobblestoneBackground.x = 0; cobblestoneBackground.y = 0; // Game state variables var currentLevel = 1; var currentWave = 1; var maxWaves = 10; var gold = 1000; var baseHP = 1000; var gameState = 'setup'; // 'setup', 'playing', 'paused', 'between_waves', 'wizard_selection' var waveSpawnTimer = 0; var enemiesSpawnedThisWave = 0; var enemiesPerWave = 10; var draggedWizard = null; var gameStarted = false; var waveStartTime = 0; var lastWaveEndTime = 0; var lastEnemySpawnTime = 0; var selectedTowerSlot = null; var wizardSelectionPopup = null; var waveInProgress = false; var allEnemiesEnteredMaze = false; var lastEnemyEnteredTime = 0; var enemySpawnDelay = 60; // 1 second at 60fps var lastSpawnTime = 0; var currentWaveEnemyType; // Game object arrays var enemies = []; var wizards = []; var projectiles = []; var towerSlots = []; var wizardIcons = []; // Path system var currentPath = []; var pathPoints = [{ x: 50, y: 350 }, { x: 200, y: 350 }, { x: 200, y: 500 }, { x: 350, y: 500 }, { x: 350, y: 650 }, { x: 150, y: 650 }, { x: 150, y: 800 }, { x: 450, y: 800 }, { x: 450, y: 950 }, { x: 650, y: 950 }, { x: 650, y: 750 }, { x: 800, y: 750 }, { x: 800, y: 1100 }, { x: 550, y: 1100 }, { x: 550, y: 1250 }, { x: 900, y: 1250 }, { x: 900, y: 1400 }, { x: 700, y: 1400 }, { x: 700, y: 1550 }, { x: 1050, y: 1550 }, { x: 1050, y: 1200 }, { x: 1200, y: 1200 }, { x: 1200, y: 1700 }, { x: 1000, y: 1700 }, { x: 1000, y: 1850 }, { x: 1350, y: 1850 }, { x: 1350, y: 1500 }, { x: 1500, y: 1500 }, { x: 1500, y: 2000 }, { x: 1700, y: 2000 }]; // UI elements - Create scoreboard container without background var scoreboardContainer = new Container(); var goldText = new Text2('Gold: ' + gold, { size: 60, fill: 0x000000, font: "'GillSans-Bold',Impact,'Arial Black',Tahoma" }); goldText.anchor.set(1, 0); goldText.x = 2048 - 40; // 40px from right edge goldText.y = 40; scoreboardContainer.addChild(goldText); var baseHPText = new Text2('Base HP: ' + baseHP, { size: 60, fill: 0x000000, font: "'GillSans-Bold',Impact,'Arial Black',Tahoma" }); baseHPText.anchor.set(1, 0); baseHPText.x = 2048 - 40; // 40px from right edge baseHPText.y = 110; scoreboardContainer.addChild(baseHPText); var waveText = new Text2('Wave: ' + currentWave + '/' + maxWaves, { size: 60, fill: 0x000000, font: "'GillSans-Bold',Impact,'Arial Black',Tahoma" }); waveText.anchor.set(1, 0); waveText.x = 2048 - 40; // 40px from right edge waveText.y = 180; scoreboardContainer.addChild(waveText); game.addChild(scoreboardContainer); // Initialize path currentPath = pathPoints; // Create continuous road path between waypoints for (var i = 0; i < currentPath.length - 1; i++) { var startPoint = currentPath[i]; var endPoint = currentPath[i + 1]; // Calculate distance and direction between points var dx = endPoint.x - startPoint.x; var dy = endPoint.y - startPoint.y; var distance = Math.sqrt(dx * dx + dy * dy); var steps = Math.ceil(distance / 50); // Place road tiles every 50 pixels // Create road tiles along the path for (var j = 0; j <= steps; j++) { var progress = j / steps; var roadX = startPoint.x + dx * progress; var roadY = startPoint.y + dy * progress; var roadTile = game.addChild(LK.getAsset('road_tile', { anchorX: 0.5, anchorY: 0.5 })); roadTile.x = roadX; roadTile.y = roadY; roadTile.tint = 0x444444; // Darker road color } } // Create home base var homeBase = game.addChild(LK.getAsset('home_base', { anchorX: 0.5, anchorY: 0.5 })); homeBase.x = currentPath[currentPath.length - 1].x; homeBase.y = currentPath[currentPath.length - 1].y; // Create tower slots - positioned outside enemy path but within level 1 wizard range (170px) of enemy path var towerPositions = [{ x: 270, y: 285 }, { x: 430, y: 440 }, { x: 70, y: 730 }, { x: 520, y: 730 }, { x: 780, y: 1475 }, { x: 390, y: 1025 }, { x: 720, y: 1005 }, { x: 1125, y: 1110 }, { x: 750, y: 1175 }, { x: 1125, y: 1380 }, { x: 1110, y: 1775 }, { x: 1425, y: 1590 }]; for (var i = 0; i < towerPositions.length; i++) { var slot = new TowerSlot(towerPositions[i].x, towerPositions[i].y, i + 1); towerSlots.push(slot); game.addChild(slot); } // Create UI bottom panel var uiPanel = game.addChild(LK.getAsset('ui_background', { anchorX: 0, anchorY: 1 })); uiPanel.x = 0; uiPanel.y = 2732; // Create start wave button var startWaveButton = LK.getAsset('ui_background', { width: 200, height: 60, anchorX: 0.5, anchorY: 0.5 }); startWaveButton.tint = 0x00AA00; startWaveButton.x = 1024; startWaveButton.y = 200; var startWaveText = new Text2('Start Game', { size: 30, fill: 0xFFFFFF }); startWaveText.anchor.set(0.5, 0.5); startWaveButton.addChild(startWaveText); startWaveButton.visible = true; game.addChild(startWaveButton); // Wizard icons removed - now handled by tower slot clicks // Add click handlers startWaveButton.down = function () { if (gameState === 'menu' || gameState === 'setup') { gameState = 'playing'; startWaveButton.visible = false; gameStarted = true; // Calculate max waves based on level var wavesPerLevel = [7, 8, 10, 11, 13, 14, 16, 17, 19, 20, 22, 23, 25, 26, 28, 29, 31, 32, 34, 35]; maxWaves = wavesPerLevel[currentLevel - 1] || 10; enemiesPerWave = getEnemiesPerWave(); updateWaveDisplay(); // Start first wave automatically waveInProgress = true; allEnemiesEnteredMaze = false; lastSpawnTime = 0; } }; // Create wizard selection popup function createWizardSelectionPopup() { wizardSelectionPopup = new Container(); // Background - make it larger to accommodate level selection var popupBg = LK.getAsset('ui_background', { width: 2000, height: 1600, anchorX: 0.5, anchorY: 0.5 }); popupBg.tint = 0x222222; popupBg.x = 1024; popupBg.y = 1366; wizardSelectionPopup.addChild(popupBg); // Title var titleText = new Text2('Select Wizard & Level', { size: 50, fill: 0xFFFFFF }); titleText.anchor.set(0.5, 0.5); titleText.x = 1024; titleText.y = 1050; wizardSelectionPopup.addChild(titleText); // Add selected wizard type and level tracking wizardSelectionPopup.selectedWizardType = 'fire'; wizardSelectionPopup.selectedLevel = 1; // Wizard type buttons var wizardTypes = ['fire', 'ice', 'nature', 'dark', 'light']; var wizardNames = ['Fire Wizard', 'Ice Wizard', 'Nature Wizard', 'Dark Wizard', 'Light Wizard']; var wizardTypeButtons = []; for (var i = 0; i < wizardTypes.length; i++) { var wizardBtn = LK.getAsset('wizard_' + wizardTypes[i], { anchorX: 0.5, anchorY: 0.5 }); wizardBtn.x = 824; // Move wizard icon to left wizardBtn.y = 1150 + i * 120; // Arrange vertically from top to bottom wizardBtn.wizardType = wizardTypes[i]; wizardBtn.alpha = wizardTypes[i] === 'fire' ? 1.0 : 0.6; // Highlight selected // Add wizard name text var nameText = new Text2(wizardNames[i], { size: 40, fill: 0xFFFFFF }); nameText.anchor.set(0, 0.5); nameText.x = 924; // Position to right of wizard icon nameText.y = 1130 + i * 120; wizardSelectionPopup.addChild(nameText); // Add cost text var wizardCostText = new Text2('Cost: 500', { size: 32, fill: 0xFFFF00 }); wizardCostText.anchor.set(0, 0.5); wizardCostText.x = 924; wizardCostText.y = 1160 + i * 120; wizardSelectionPopup.addChild(wizardCostText); // Add level text var wizardLevelText = new Text2('Level: 1', { size: 32, fill: 0x00FF00 }); wizardLevelText.anchor.set(0, 0.5); wizardLevelText.x = 924; wizardLevelText.y = 1190 + i * 120; wizardSelectionPopup.addChild(wizardLevelText); // Add elemental damage text var elementNames = { 'fire': 'Fire', 'ice': 'Ice', 'nature': 'Nature', 'dark': 'Dark', 'light': 'Light' }; var wizardDamageText = new Text2(elementNames[wizardTypes[i]] + ': 100', { size: 32, fill: 0xFF6600 }); wizardDamageText.anchor.set(0, 0.5); wizardDamageText.x = 1224; wizardDamageText.y = 1160 + i * 120; wizardSelectionPopup.addChild(wizardDamageText); // Add magic damage text var wizardMagicDamageText = new Text2('Magic: 100', { size: 32, fill: 0x9966FF }); wizardMagicDamageText.anchor.set(0, 0.5); wizardMagicDamageText.x = 1224; wizardMagicDamageText.y = 1190 + i * 120; wizardSelectionPopup.addChild(wizardMagicDamageText); // Store references to text elements for updates wizardBtn.nameText = nameText; wizardBtn.costText = wizardCostText; wizardBtn.levelText = wizardLevelText; wizardBtn.damageText = wizardDamageText; wizardBtn.magicDamageText = wizardMagicDamageText; wizardBtn.down = function () { // Update selected wizard type wizardSelectionPopup.selectedWizardType = this.wizardType; // Update button highlighting for (var j = 0; j < wizardTypeButtons.length; j++) { wizardTypeButtons[j].alpha = wizardTypeButtons[j].wizardType === this.wizardType ? 1.0 : 0.6; } // Update cost display updateCostDisplay(); // Update individual wizard info updateWizardInfo(); }; wizardSelectionPopup.addChild(wizardBtn); wizardTypeButtons.push(wizardBtn); } // Level selection section var levelTitleText = new Text2('Level:', { size: 40, fill: 0xFFFFFF }); levelTitleText.anchor.set(0.5, 0.5); levelTitleText.x = 1024; levelTitleText.y = 1770; wizardSelectionPopup.addChild(levelTitleText); // Level buttons (1-10) var levelButtons = []; for (var i = 1; i <= 10; i++) { var levelBtn = LK.getAsset('ui_background', { width: 80, height: 60, anchorX: 0.5, anchorY: 0.5 }); levelBtn.tint = i === 1 ? 0x00AA00 : 0x666666; // Highlight level 1 levelBtn.x = 400 + (i - 1) * 80; levelBtn.y = 1850; levelBtn.level = i; // Add level number text var levelNumText = new Text2(i.toString(), { size: 30, fill: 0xFFFFFF }); levelNumText.anchor.set(0.5, 0.5); levelBtn.addChild(levelNumText); levelBtn.down = function () { // Update selected level wizardSelectionPopup.selectedLevel = this.level; // Update button highlighting for (var j = 0; j < levelButtons.length; j++) { levelButtons[j].tint = levelButtons[j].level === this.level ? 0x00AA00 : 0x666666; } // Update cost display updateCostDisplay(); // Update individual wizard info updateWizardInfo(); }; wizardSelectionPopup.addChild(levelBtn); levelButtons.push(levelBtn); } // Cost display var costText = new Text2('Cost: 500', { size: 35, fill: 0xFFFF00 }); costText.anchor.set(0.5, 0.5); costText.x = 1024; costText.y = 1930; wizardSelectionPopup.addChild(costText); // Function to update cost display function updateCostDisplay() { var cost = 500 * Math.pow(1.8, wizardSelectionPopup.selectedLevel - 1); costText.setText('Cost: ' + Math.floor(cost)); costText.tint = gold >= cost ? 0xFFFF00 : 0xFF0000; // Yellow if affordable, red if not } // Function to update wizard info display function updateWizardInfo() { var cost = 500 * Math.pow(1.8, wizardSelectionPopup.selectedLevel - 1); var elementalDamage = 100 * wizardSelectionPopup.selectedLevel; var magicDamage = 100 * wizardSelectionPopup.selectedLevel; var elementNames = { 'fire': 'Fire', 'ice': 'Ice', 'nature': 'Nature', 'dark': 'Dark', 'light': 'Light' }; for (var i = 0; i < wizardTypeButtons.length; i++) { var btn = wizardTypeButtons[i]; btn.costText.setText('Cost: ' + Math.floor(cost)); btn.costText.tint = gold >= cost ? 0xFFFF00 : 0xFF0000; btn.levelText.setText('Level: ' + wizardSelectionPopup.selectedLevel); btn.damageText.setText(elementNames[btn.wizardType] + ': ' + elementalDamage); btn.magicDamageText.setText('Magic: ' + magicDamage); } } // Hire button var hireBtn = LK.getAsset('ui_background', { width: 200, height: 60, anchorX: 0.5, anchorY: 0.5 }); hireBtn.tint = 0x00AA00; hireBtn.x = 924; hireBtn.y = 2000; var hireText = new Text2('Hire', { size: 30, fill: 0xFFFFFF }); hireText.anchor.set(0.5, 0.5); hireBtn.addChild(hireText); hireBtn.down = function () { placeWizardOnSelectedSlot(wizardSelectionPopup.selectedWizardType, wizardSelectionPopup.selectedLevel); }; wizardSelectionPopup.addChild(hireBtn); // Close button var closeBtn = LK.getAsset('ui_background', { width: 100, height: 50, anchorX: 0.5, anchorY: 0.5 }); closeBtn.tint = 0xff0000; closeBtn.x = 1400; closeBtn.y = 1050; var closeText = new Text2('X', { size: 30, fill: 0xFFFFFF }); closeText.anchor.set(0.5, 0.5); closeBtn.addChild(closeText); closeBtn.down = function () { closeWizardSelectionPopup(); }; wizardSelectionPopup.addChild(closeBtn); game.addChild(wizardSelectionPopup); } function closeWizardSelectionPopup() { if (wizardSelectionPopup) { wizardSelectionPopup.destroy(); wizardSelectionPopup = null; selectedTowerSlot = null; gameState = gameStarted ? 'playing' : 'setup'; } } function createWizardUpgradePopup() { var wizard = selectedTowerSlot.wizard; var upgradePopup = new Container(); // Background var popupBg = LK.getAsset('ui_background', { width: 1000, height: 800, anchorX: 0.5, anchorY: 0.5 }); popupBg.tint = 0x222222; popupBg.x = 1024; popupBg.y = 1366; upgradePopup.addChild(popupBg); // Title var titleText = new Text2('Upgrade Wizard', { size: 50, fill: 0xFFFFFF }); titleText.anchor.set(0.5, 0.5); titleText.x = 1024; titleText.y = 1100; upgradePopup.addChild(titleText); // Current wizard info var currentInfoText = new Text2('Current Level: ' + wizard.level, { size: 35, fill: 0xFFFFFF }); currentInfoText.anchor.set(0.5, 0.5); currentInfoText.x = 1024; currentInfoText.y = 1200; upgradePopup.addChild(currentInfoText); var elementNames = { 'fire': 'Fire', 'ice': 'Ice', 'nature': 'Nature', 'dark': 'Dark', 'light': 'Light' }; var currentDamageText = new Text2(elementNames[wizard.elementType] + ': ' + wizard.elementalDamage + ' | Magic: ' + wizard.magicDamage, { size: 30, fill: 0x00FF00 }); currentDamageText.anchor.set(0.5, 0.5); currentDamageText.x = 1024; currentDamageText.y = 1250; upgradePopup.addChild(currentDamageText); // Upgrade info (if not max level) if (wizard.level < 10) { var upgradeCost = Math.floor(500 * Math.pow(1.8, wizard.level) * 0.6); var newElementalDamage = 100 * (wizard.level + 1); var newMagicDamage = 100 * (wizard.level + 1); var upgradeInfoText = new Text2('Upgrade to Level ' + (wizard.level + 1), { size: 35, fill: 0xFFFF00 }); upgradeInfoText.anchor.set(0.5, 0.5); upgradeInfoText.x = 1024; upgradeInfoText.y = 1350; upgradePopup.addChild(upgradeInfoText); var upgradeDamageText = new Text2(elementNames[wizard.elementType] + ': ' + newElementalDamage + ' | Magic: ' + newMagicDamage, { size: 30, fill: 0x00FF00 }); upgradeDamageText.anchor.set(0.5, 0.5); upgradeDamageText.x = 1024; upgradeDamageText.y = 1400; upgradePopup.addChild(upgradeDamageText); var costText = new Text2('Cost: ' + upgradeCost, { size: 35, fill: gold >= upgradeCost ? 0xFFFF00 : 0xFF0000 }); costText.anchor.set(0.5, 0.5); costText.x = 1024; costText.y = 1450; upgradePopup.addChild(costText); // Upgrade button var upgradeBtn = LK.getAsset('ui_background', { width: 200, height: 60, anchorX: 0.5, anchorY: 0.5 }); upgradeBtn.tint = gold >= upgradeCost ? 0x00AA00 : 0x666666; upgradeBtn.x = 924; upgradeBtn.y = 1550; var upgradeText = new Text2('Upgrade', { size: 30, fill: 0xFFFFFF }); upgradeText.anchor.set(0.5, 0.5); upgradeBtn.addChild(upgradeText); upgradeBtn.down = function () { if (gold >= upgradeCost) { upgradeWizard(wizard, upgradeCost); upgradePopup.destroy(); selectedTowerSlot = null; gameState = gameStarted ? 'playing' : 'setup'; } }; upgradePopup.addChild(upgradeBtn); } else { // Max level reached var maxLevelText = new Text2('Maximum Level Reached!', { size: 40, fill: 0xFF6600 }); maxLevelText.anchor.set(0.5, 0.5); maxLevelText.x = 1024; maxLevelText.y = 1400; upgradePopup.addChild(maxLevelText); } // Close button var closeBtn = LK.getAsset('ui_background', { width: 100, height: 50, anchorX: 0.5, anchorY: 0.5 }); closeBtn.tint = 0xff0000; closeBtn.x = 1124; closeBtn.y = 1550; var closeText = new Text2('X', { size: 30, fill: 0xFFFFFF }); closeText.anchor.set(0.5, 0.5); closeBtn.addChild(closeText); closeBtn.down = function () { upgradePopup.destroy(); selectedTowerSlot = null; gameState = gameStarted ? 'playing' : 'setup'; }; upgradePopup.addChild(closeBtn); game.addChild(upgradePopup); } function upgradeWizard(wizard, cost) { gold -= cost; updateGoldDisplay(); // Upgrade wizard stats wizard.level++; wizard.elementalDamage = 100 * wizard.level; wizard.magicDamage = 100 * wizard.level; wizard.range = (150 + wizard.level * 20) * 1.15; wizard.attackSpeed = (1000 - wizard.level * 50) * 0.9; wizard.cost = 500 * Math.pow(1.8, wizard.level - 1); // Update level display on wizard var levelText = wizard.children.find(function (child) { return child instanceof Text2; }); if (levelText) { levelText.setText(wizard.level.toString()); } // Update range circle if it exists if (wizard.rangeCircle) { wizard.rangeCircle.scaleX = wizard.range / 45; wizard.rangeCircle.scaleY = wizard.range / 45; } } function placeWizardOnSelectedSlot(wizardType, level) { if (selectedTowerSlot && selectedTowerSlot.isEmpty) { var cost = 500 * Math.pow(1.8, level - 1); if (gold >= cost) { gold -= cost; updateGoldDisplay(); var wizard = new Wizard(wizardType, level); wizard.x = selectedTowerSlot.x; wizard.y = selectedTowerSlot.y; selectedTowerSlot.wizard = wizard; selectedTowerSlot.isEmpty = false; // Add range visualization var rangeCircle = LK.getAsset('tower_slot', { anchorX: 0.5, anchorY: 0.5, scaleX: wizard.range / 45, scaleY: wizard.range / 45 }); rangeCircle.tint = 0x00ff00; rangeCircle.alpha = 0.2; rangeCircle.x = wizard.x; rangeCircle.y = wizard.y; game.addChild(rangeCircle); wizard.rangeCircle = rangeCircle; game.addChild(wizard); wizards.push(wizard); closeWizardSelectionPopup(); } } } // Helper functions function updateGoldDisplay() { goldText.setText('Gold: ' + gold); } function updateBaseHPDisplay() { baseHPText.setText('Base HP: ' + baseHP); } function updateWaveDisplay() { waveText.setText('Wave: ' + currentWave + '/' + maxWaves); } function spawnEnemy() { // Determine enemy type for this wave - same type for all enemies in wave if (currentWaveEnemyType === undefined) { var rand = Math.random(); if (rand < 0.6) currentWaveEnemyType = 'basic';else if (rand < 0.85) currentWaveEnemyType = 'fast';else currentWaveEnemyType = 'tank'; } var enemy = new Enemy(currentWaveEnemyType, currentLevel); enemy.x = currentPath[0].x; enemy.y = currentPath[0].y; enemies.push(enemy); game.addChild(enemy); } function getEnemiesPerWave() { var min, max; if (currentLevel >= 1 && currentLevel <= 5) { min = 10; max = 20; } else if (currentLevel >= 6 && currentLevel <= 10) { min = 15; max = 25; } else if (currentLevel >= 11 && currentLevel <= 15) { min = 20; max = 35; } else if (currentLevel >= 16 && currentLevel <= 20) { min = 35; max = 50; } else { min = 50; max = 70; } return Math.floor(Math.random() * (max - min + 1)) + min; } function startNextWave() { currentWave++; enemiesSpawnedThisWave = 0; enemiesPerWave = getEnemiesPerWave(); lastWaveEndTime = LK.ticks; gameState = 'playing'; currentWaveEnemyType = undefined; // Reset enemy type for new wave updateWaveDisplay(); } // Game update loop game.update = function () { // Only update game logic if not in setup or wizard selection if (gameState === 'setup' || gameState === 'wizard_selection' || gameState === 'wizard_upgrade') { return; } // Spawn enemies with proper timing if (gameState === 'playing' && waveInProgress && enemiesSpawnedThisWave < enemiesPerWave) { // Only spawn if enough time has passed since last spawn if (LK.ticks - lastSpawnTime >= enemySpawnDelay) { spawnEnemy(); enemiesSpawnedThisWave++; lastSpawnTime = LK.ticks; // Check if all enemies have been spawned for this wave if (enemiesSpawnedThisWave >= enemiesPerWave) { allEnemiesEnteredMaze = true; lastEnemyEnteredTime = LK.ticks; } } } // Check if wave is complete - all enemies spawned and all enemies cleared if (waveInProgress && allEnemiesEnteredMaze && enemies.length === 0) { // Wait 5 seconds after last enemy entered before allowing next wave if (LK.ticks - lastEnemyEnteredTime >= 300) { // 5 seconds at 60fps waveInProgress = false; allEnemiesEnteredMaze = false; enemiesSpawnedThisWave = 0; gameState = 'between_waves'; lastWaveEndTime = LK.ticks; waveStartTime = 0; if (currentWave < maxWaves) { // Automatically start next wave after 5 seconds startNextWave(); waveInProgress = true; allEnemiesEnteredMaze = false; lastSpawnTime = 0; } else { // Level complete currentLevel++; currentWave = 0; // Will be incremented in startNextWave var wavesPerLevel = [7, 8, 10, 11, 13, 14, 16, 17, 19, 20, 22, 23, 25, 26, 28, 29, 31, 32, 34, 35]; maxWaves = wavesPerLevel[currentLevel - 1] || 10; enemiesPerWave = getEnemiesPerWave(); if (currentLevel > 20) { LK.showYouWin(); return; } // Automatically start next level startNextWave(); waveInProgress = true; allEnemiesEnteredMaze = false; lastSpawnTime = 0; } } } // Update projectiles for (var i = projectiles.length - 1; i >= 0; i--) { var projectile = projectiles[i]; if (projectile.destroyed) { projectiles.splice(i, 1); } } // Clean up destroyed enemies for (var i = enemies.length - 1; i >= 0; i--) { if (enemies[i].destroyed) { enemies.splice(i, 1); } } // Clean up destroyed wizards for (var i = wizards.length - 1; i >= 0; i--) { if (wizards[i].destroyed) { wizards.splice(i, 1); } } };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Enemy = Container.expand(function (enemyType, level) {
var self = Container.call(this);
self.enemyType = enemyType || 'basic';
self.level = level || 1;
self.pathIndex = 0;
self.pathProgress = 0;
self.speed = 2;
switch (self.enemyType) {
case 'basic':
self.maxHP = (100 + level * 50) * 5;
self.speed = 2;
break;
case 'fast':
self.maxHP = (50 + level * 25) * 5;
self.speed = 4;
break;
case 'tank':
self.maxHP = (300 + level * 100) * 5;
self.speed = 1;
break;
}
self.hp = self.maxHP;
self.goldValue = self.maxHP;
var enemyGraphics = self.attachAsset('enemy_' + self.enemyType, {
anchorX: 0.5,
anchorY: 0.5
});
var hpBar = new Container();
var hpBackground = LK.getAsset('ui_background', {
width: 60,
height: 8,
anchorX: 0.5,
anchorY: 0.5
});
hpBackground.tint = 0x000000;
hpBar.addChild(hpBackground);
var hpFill = LK.getAsset('ui_background', {
width: 60,
height: 8,
anchorX: 0,
anchorY: 0.5
});
hpFill.tint = 0x00ff00;
hpFill.x = -30;
hpBar.addChild(hpFill);
hpBar.y = -40;
self.addChild(hpBar);
self.update = function () {
if (currentPath && currentPath.length > 0) {
self.followPath();
}
// Update HP bar
var hpPercent = self.hp / self.maxHP;
hpFill.width = 60 * hpPercent;
hpFill.tint = hpPercent > 0.5 ? 0x00ff00 : hpPercent > 0.25 ? 0xffff00 : 0xff0000;
};
self.followPath = function () {
if (self.pathIndex >= currentPath.length) {
self.reachBase();
return;
}
var targetPoint = currentPath[self.pathIndex];
var dx = targetPoint.x - self.x;
var dy = targetPoint.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 5) {
self.pathIndex++;
return;
}
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
};
self.takeDamage = function (damage) {
self.hp -= damage;
LK.getSound('enemyHit').play();
if (self.hp <= 0) {
self.die();
}
};
self.die = function () {
gold += self.goldValue / 10;
updateGoldDisplay();
LK.getSound('enemyDeath').play();
for (var i = 0; i < enemies.length; i++) {
if (enemies[i] === self) {
enemies.splice(i, 1);
break;
}
}
self.destroy();
};
self.reachBase = function () {
baseHP -= 10;
updateBaseHPDisplay();
if (baseHP <= 0) {
LK.showGameOver();
}
for (var i = 0; i < enemies.length; i++) {
if (enemies[i] === self) {
enemies.splice(i, 1);
break;
}
}
self.destroy();
};
return self;
});
var Projectile = Container.expand(function (startX, startY, target, damage) {
var self = Container.call(this);
self.x = startX;
self.y = startY;
self.target = target;
self.damage = damage;
self.speed = 8;
var projectileGraphics = self.attachAsset('projectile', {
anchorX: 0.5,
anchorY: 0.5
});
self.update = function () {
if (!self.target || self.target.destroyed) {
self.destroy();
return;
}
var dx = self.target.x - self.x;
var dy = self.target.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 10) {
self.target.takeDamage(self.damage);
self.destroy();
return;
}
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
};
return self;
});
var TowerSlot = Container.expand(function (x, y, towerNumber) {
var self = Container.call(this);
self.x = x;
self.y = y;
self.isEmpty = true;
self.wizard = null;
self.towerNumber = towerNumber;
var slotGraphics = self.attachAsset('tower_slot', {
anchorX: 0.5,
anchorY: 0.5
});
var towerNumberText = new Text2(self.towerNumber.toString(), {
size: 20,
fill: 0xFFFFFF
});
towerNumberText.anchor.set(0.5, 0.5);
towerNumberText.y = 0;
self.addChild(towerNumberText);
self.down = function (x, y, obj) {
if (self.isEmpty && (gameState === 'setup' || gameState === 'playing')) {
selectedTowerSlot = self;
gameState = 'wizard_selection';
createWizardSelectionPopup();
} else if (!self.isEmpty && self.wizard && (gameState === 'setup' || gameState === 'playing')) {
// Upgrade existing wizard
selectedTowerSlot = self;
gameState = 'wizard_upgrade';
createWizardUpgradePopup();
}
};
self.placeWizard = function (wizardType) {
var cost = 500;
if (gold >= cost) {
gold -= cost;
updateGoldDisplay();
self.wizard = new Wizard(wizardType, 1);
self.wizard.x = self.x;
self.wizard.y = self.y;
self.isEmpty = false;
game.addChild(self.wizard);
wizards.push(self.wizard);
draggedWizard = null;
}
};
return self;
});
var Wizard = Container.expand(function (elementType, level) {
var self = Container.call(this);
self.elementType = elementType || 'fire';
self.level = level || 1;
self.elementalDamage = 100 * self.level;
self.magicDamage = 100 * self.level;
self.range = (150 + self.level * 20) * 1.15;
self.attackSpeed = (1000 - self.level * 50) * 0.9; // ms between attacks - 10% faster
self.lastAttackTime = 0;
self.cost = 500 * Math.pow(1.8, self.level - 1);
var wizardGraphics = self.attachAsset('wizard_' + self.elementType, {
anchorX: 0.5,
anchorY: 0.5
});
var levelText = new Text2(self.level.toString(), {
size: 24,
fill: 0xFFFFFF
});
levelText.anchor.set(0.5, 0.5);
levelText.y = -10;
self.addChild(levelText);
self.update = function () {
if (LK.ticks - self.lastAttackTime > self.attackSpeed / 16.67) {
var target = self.findTarget();
if (target) {
self.attack(target);
self.lastAttackTime = LK.ticks;
}
}
};
self.findTarget = function () {
var closestEnemy = null;
var closestDistance = Infinity;
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
var distance = Math.sqrt(Math.pow(enemy.x - self.x, 2) + Math.pow(enemy.y - self.y, 2));
if (distance <= self.range && distance < closestDistance) {
closestDistance = distance;
closestEnemy = enemy;
}
}
return closestEnemy;
};
self.attack = function (target) {
var projectile = new Projectile(self.x, self.y, target, self.elementalDamage + self.magicDamage);
projectiles.push(projectile);
game.addChild(projectile);
LK.getSound('shoot').play();
};
return self;
});
var WizardIcon = Container.expand(function (elementType) {
var self = Container.call(this);
self.elementType = elementType;
var iconGraphics = self.attachAsset('wizard_' + elementType, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.8
});
var costText = new Text2('500', {
size: 20,
fill: 0xFFFFFF
});
costText.anchor.set(0.5, 0);
costText.y = 45;
self.addChild(costText);
self.down = function (x, y, obj) {
// Wizard icons are now handled by tower slots
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x228B22
});
/****
* Game Code
****/
// Add cobblestone background
var cobblestoneBackground = game.addChild(LK.getAsset('cobblestone_bg', {
anchorX: 0,
anchorY: 0
}));
cobblestoneBackground.x = 0;
cobblestoneBackground.y = 0;
// Game state variables
var currentLevel = 1;
var currentWave = 1;
var maxWaves = 10;
var gold = 1000;
var baseHP = 1000;
var gameState = 'setup'; // 'setup', 'playing', 'paused', 'between_waves', 'wizard_selection'
var waveSpawnTimer = 0;
var enemiesSpawnedThisWave = 0;
var enemiesPerWave = 10;
var draggedWizard = null;
var gameStarted = false;
var waveStartTime = 0;
var lastWaveEndTime = 0;
var lastEnemySpawnTime = 0;
var selectedTowerSlot = null;
var wizardSelectionPopup = null;
var waveInProgress = false;
var allEnemiesEnteredMaze = false;
var lastEnemyEnteredTime = 0;
var enemySpawnDelay = 60; // 1 second at 60fps
var lastSpawnTime = 0;
var currentWaveEnemyType;
// Game object arrays
var enemies = [];
var wizards = [];
var projectiles = [];
var towerSlots = [];
var wizardIcons = [];
// Path system
var currentPath = [];
var pathPoints = [{
x: 50,
y: 350
}, {
x: 200,
y: 350
}, {
x: 200,
y: 500
}, {
x: 350,
y: 500
}, {
x: 350,
y: 650
}, {
x: 150,
y: 650
}, {
x: 150,
y: 800
}, {
x: 450,
y: 800
}, {
x: 450,
y: 950
}, {
x: 650,
y: 950
}, {
x: 650,
y: 750
}, {
x: 800,
y: 750
}, {
x: 800,
y: 1100
}, {
x: 550,
y: 1100
}, {
x: 550,
y: 1250
}, {
x: 900,
y: 1250
}, {
x: 900,
y: 1400
}, {
x: 700,
y: 1400
}, {
x: 700,
y: 1550
}, {
x: 1050,
y: 1550
}, {
x: 1050,
y: 1200
}, {
x: 1200,
y: 1200
}, {
x: 1200,
y: 1700
}, {
x: 1000,
y: 1700
}, {
x: 1000,
y: 1850
}, {
x: 1350,
y: 1850
}, {
x: 1350,
y: 1500
}, {
x: 1500,
y: 1500
}, {
x: 1500,
y: 2000
}, {
x: 1700,
y: 2000
}];
// UI elements - Create scoreboard container without background
var scoreboardContainer = new Container();
var goldText = new Text2('Gold: ' + gold, {
size: 60,
fill: 0x000000,
font: "'GillSans-Bold',Impact,'Arial Black',Tahoma"
});
goldText.anchor.set(1, 0);
goldText.x = 2048 - 40; // 40px from right edge
goldText.y = 40;
scoreboardContainer.addChild(goldText);
var baseHPText = new Text2('Base HP: ' + baseHP, {
size: 60,
fill: 0x000000,
font: "'GillSans-Bold',Impact,'Arial Black',Tahoma"
});
baseHPText.anchor.set(1, 0);
baseHPText.x = 2048 - 40; // 40px from right edge
baseHPText.y = 110;
scoreboardContainer.addChild(baseHPText);
var waveText = new Text2('Wave: ' + currentWave + '/' + maxWaves, {
size: 60,
fill: 0x000000,
font: "'GillSans-Bold',Impact,'Arial Black',Tahoma"
});
waveText.anchor.set(1, 0);
waveText.x = 2048 - 40; // 40px from right edge
waveText.y = 180;
scoreboardContainer.addChild(waveText);
game.addChild(scoreboardContainer);
// Initialize path
currentPath = pathPoints;
// Create continuous road path between waypoints
for (var i = 0; i < currentPath.length - 1; i++) {
var startPoint = currentPath[i];
var endPoint = currentPath[i + 1];
// Calculate distance and direction between points
var dx = endPoint.x - startPoint.x;
var dy = endPoint.y - startPoint.y;
var distance = Math.sqrt(dx * dx + dy * dy);
var steps = Math.ceil(distance / 50); // Place road tiles every 50 pixels
// Create road tiles along the path
for (var j = 0; j <= steps; j++) {
var progress = j / steps;
var roadX = startPoint.x + dx * progress;
var roadY = startPoint.y + dy * progress;
var roadTile = game.addChild(LK.getAsset('road_tile', {
anchorX: 0.5,
anchorY: 0.5
}));
roadTile.x = roadX;
roadTile.y = roadY;
roadTile.tint = 0x444444; // Darker road color
}
}
// Create home base
var homeBase = game.addChild(LK.getAsset('home_base', {
anchorX: 0.5,
anchorY: 0.5
}));
homeBase.x = currentPath[currentPath.length - 1].x;
homeBase.y = currentPath[currentPath.length - 1].y;
// Create tower slots - positioned outside enemy path but within level 1 wizard range (170px) of enemy path
var towerPositions = [{
x: 270,
y: 285
}, {
x: 430,
y: 440
}, {
x: 70,
y: 730
}, {
x: 520,
y: 730
}, {
x: 780,
y: 1475
}, {
x: 390,
y: 1025
}, {
x: 720,
y: 1005
}, {
x: 1125,
y: 1110
}, {
x: 750,
y: 1175
}, {
x: 1125,
y: 1380
}, {
x: 1110,
y: 1775
}, {
x: 1425,
y: 1590
}];
for (var i = 0; i < towerPositions.length; i++) {
var slot = new TowerSlot(towerPositions[i].x, towerPositions[i].y, i + 1);
towerSlots.push(slot);
game.addChild(slot);
}
// Create UI bottom panel
var uiPanel = game.addChild(LK.getAsset('ui_background', {
anchorX: 0,
anchorY: 1
}));
uiPanel.x = 0;
uiPanel.y = 2732;
// Create start wave button
var startWaveButton = LK.getAsset('ui_background', {
width: 200,
height: 60,
anchorX: 0.5,
anchorY: 0.5
});
startWaveButton.tint = 0x00AA00;
startWaveButton.x = 1024;
startWaveButton.y = 200;
var startWaveText = new Text2('Start Game', {
size: 30,
fill: 0xFFFFFF
});
startWaveText.anchor.set(0.5, 0.5);
startWaveButton.addChild(startWaveText);
startWaveButton.visible = true;
game.addChild(startWaveButton);
// Wizard icons removed - now handled by tower slot clicks
// Add click handlers
startWaveButton.down = function () {
if (gameState === 'menu' || gameState === 'setup') {
gameState = 'playing';
startWaveButton.visible = false;
gameStarted = true;
// Calculate max waves based on level
var wavesPerLevel = [7, 8, 10, 11, 13, 14, 16, 17, 19, 20, 22, 23, 25, 26, 28, 29, 31, 32, 34, 35];
maxWaves = wavesPerLevel[currentLevel - 1] || 10;
enemiesPerWave = getEnemiesPerWave();
updateWaveDisplay();
// Start first wave automatically
waveInProgress = true;
allEnemiesEnteredMaze = false;
lastSpawnTime = 0;
}
};
// Create wizard selection popup
function createWizardSelectionPopup() {
wizardSelectionPopup = new Container();
// Background - make it larger to accommodate level selection
var popupBg = LK.getAsset('ui_background', {
width: 2000,
height: 1600,
anchorX: 0.5,
anchorY: 0.5
});
popupBg.tint = 0x222222;
popupBg.x = 1024;
popupBg.y = 1366;
wizardSelectionPopup.addChild(popupBg);
// Title
var titleText = new Text2('Select Wizard & Level', {
size: 50,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 1024;
titleText.y = 1050;
wizardSelectionPopup.addChild(titleText);
// Add selected wizard type and level tracking
wizardSelectionPopup.selectedWizardType = 'fire';
wizardSelectionPopup.selectedLevel = 1;
// Wizard type buttons
var wizardTypes = ['fire', 'ice', 'nature', 'dark', 'light'];
var wizardNames = ['Fire Wizard', 'Ice Wizard', 'Nature Wizard', 'Dark Wizard', 'Light Wizard'];
var wizardTypeButtons = [];
for (var i = 0; i < wizardTypes.length; i++) {
var wizardBtn = LK.getAsset('wizard_' + wizardTypes[i], {
anchorX: 0.5,
anchorY: 0.5
});
wizardBtn.x = 824; // Move wizard icon to left
wizardBtn.y = 1150 + i * 120; // Arrange vertically from top to bottom
wizardBtn.wizardType = wizardTypes[i];
wizardBtn.alpha = wizardTypes[i] === 'fire' ? 1.0 : 0.6; // Highlight selected
// Add wizard name text
var nameText = new Text2(wizardNames[i], {
size: 40,
fill: 0xFFFFFF
});
nameText.anchor.set(0, 0.5);
nameText.x = 924; // Position to right of wizard icon
nameText.y = 1130 + i * 120;
wizardSelectionPopup.addChild(nameText);
// Add cost text
var wizardCostText = new Text2('Cost: 500', {
size: 32,
fill: 0xFFFF00
});
wizardCostText.anchor.set(0, 0.5);
wizardCostText.x = 924;
wizardCostText.y = 1160 + i * 120;
wizardSelectionPopup.addChild(wizardCostText);
// Add level text
var wizardLevelText = new Text2('Level: 1', {
size: 32,
fill: 0x00FF00
});
wizardLevelText.anchor.set(0, 0.5);
wizardLevelText.x = 924;
wizardLevelText.y = 1190 + i * 120;
wizardSelectionPopup.addChild(wizardLevelText);
// Add elemental damage text
var elementNames = {
'fire': 'Fire',
'ice': 'Ice',
'nature': 'Nature',
'dark': 'Dark',
'light': 'Light'
};
var wizardDamageText = new Text2(elementNames[wizardTypes[i]] + ': 100', {
size: 32,
fill: 0xFF6600
});
wizardDamageText.anchor.set(0, 0.5);
wizardDamageText.x = 1224;
wizardDamageText.y = 1160 + i * 120;
wizardSelectionPopup.addChild(wizardDamageText);
// Add magic damage text
var wizardMagicDamageText = new Text2('Magic: 100', {
size: 32,
fill: 0x9966FF
});
wizardMagicDamageText.anchor.set(0, 0.5);
wizardMagicDamageText.x = 1224;
wizardMagicDamageText.y = 1190 + i * 120;
wizardSelectionPopup.addChild(wizardMagicDamageText);
// Store references to text elements for updates
wizardBtn.nameText = nameText;
wizardBtn.costText = wizardCostText;
wizardBtn.levelText = wizardLevelText;
wizardBtn.damageText = wizardDamageText;
wizardBtn.magicDamageText = wizardMagicDamageText;
wizardBtn.down = function () {
// Update selected wizard type
wizardSelectionPopup.selectedWizardType = this.wizardType;
// Update button highlighting
for (var j = 0; j < wizardTypeButtons.length; j++) {
wizardTypeButtons[j].alpha = wizardTypeButtons[j].wizardType === this.wizardType ? 1.0 : 0.6;
}
// Update cost display
updateCostDisplay();
// Update individual wizard info
updateWizardInfo();
};
wizardSelectionPopup.addChild(wizardBtn);
wizardTypeButtons.push(wizardBtn);
}
// Level selection section
var levelTitleText = new Text2('Level:', {
size: 40,
fill: 0xFFFFFF
});
levelTitleText.anchor.set(0.5, 0.5);
levelTitleText.x = 1024;
levelTitleText.y = 1770;
wizardSelectionPopup.addChild(levelTitleText);
// Level buttons (1-10)
var levelButtons = [];
for (var i = 1; i <= 10; i++) {
var levelBtn = LK.getAsset('ui_background', {
width: 80,
height: 60,
anchorX: 0.5,
anchorY: 0.5
});
levelBtn.tint = i === 1 ? 0x00AA00 : 0x666666; // Highlight level 1
levelBtn.x = 400 + (i - 1) * 80;
levelBtn.y = 1850;
levelBtn.level = i;
// Add level number text
var levelNumText = new Text2(i.toString(), {
size: 30,
fill: 0xFFFFFF
});
levelNumText.anchor.set(0.5, 0.5);
levelBtn.addChild(levelNumText);
levelBtn.down = function () {
// Update selected level
wizardSelectionPopup.selectedLevel = this.level;
// Update button highlighting
for (var j = 0; j < levelButtons.length; j++) {
levelButtons[j].tint = levelButtons[j].level === this.level ? 0x00AA00 : 0x666666;
}
// Update cost display
updateCostDisplay();
// Update individual wizard info
updateWizardInfo();
};
wizardSelectionPopup.addChild(levelBtn);
levelButtons.push(levelBtn);
}
// Cost display
var costText = new Text2('Cost: 500', {
size: 35,
fill: 0xFFFF00
});
costText.anchor.set(0.5, 0.5);
costText.x = 1024;
costText.y = 1930;
wizardSelectionPopup.addChild(costText);
// Function to update cost display
function updateCostDisplay() {
var cost = 500 * Math.pow(1.8, wizardSelectionPopup.selectedLevel - 1);
costText.setText('Cost: ' + Math.floor(cost));
costText.tint = gold >= cost ? 0xFFFF00 : 0xFF0000; // Yellow if affordable, red if not
}
// Function to update wizard info display
function updateWizardInfo() {
var cost = 500 * Math.pow(1.8, wizardSelectionPopup.selectedLevel - 1);
var elementalDamage = 100 * wizardSelectionPopup.selectedLevel;
var magicDamage = 100 * wizardSelectionPopup.selectedLevel;
var elementNames = {
'fire': 'Fire',
'ice': 'Ice',
'nature': 'Nature',
'dark': 'Dark',
'light': 'Light'
};
for (var i = 0; i < wizardTypeButtons.length; i++) {
var btn = wizardTypeButtons[i];
btn.costText.setText('Cost: ' + Math.floor(cost));
btn.costText.tint = gold >= cost ? 0xFFFF00 : 0xFF0000;
btn.levelText.setText('Level: ' + wizardSelectionPopup.selectedLevel);
btn.damageText.setText(elementNames[btn.wizardType] + ': ' + elementalDamage);
btn.magicDamageText.setText('Magic: ' + magicDamage);
}
}
// Hire button
var hireBtn = LK.getAsset('ui_background', {
width: 200,
height: 60,
anchorX: 0.5,
anchorY: 0.5
});
hireBtn.tint = 0x00AA00;
hireBtn.x = 924;
hireBtn.y = 2000;
var hireText = new Text2('Hire', {
size: 30,
fill: 0xFFFFFF
});
hireText.anchor.set(0.5, 0.5);
hireBtn.addChild(hireText);
hireBtn.down = function () {
placeWizardOnSelectedSlot(wizardSelectionPopup.selectedWizardType, wizardSelectionPopup.selectedLevel);
};
wizardSelectionPopup.addChild(hireBtn);
// Close button
var closeBtn = LK.getAsset('ui_background', {
width: 100,
height: 50,
anchorX: 0.5,
anchorY: 0.5
});
closeBtn.tint = 0xff0000;
closeBtn.x = 1400;
closeBtn.y = 1050;
var closeText = new Text2('X', {
size: 30,
fill: 0xFFFFFF
});
closeText.anchor.set(0.5, 0.5);
closeBtn.addChild(closeText);
closeBtn.down = function () {
closeWizardSelectionPopup();
};
wizardSelectionPopup.addChild(closeBtn);
game.addChild(wizardSelectionPopup);
}
function closeWizardSelectionPopup() {
if (wizardSelectionPopup) {
wizardSelectionPopup.destroy();
wizardSelectionPopup = null;
selectedTowerSlot = null;
gameState = gameStarted ? 'playing' : 'setup';
}
}
function createWizardUpgradePopup() {
var wizard = selectedTowerSlot.wizard;
var upgradePopup = new Container();
// Background
var popupBg = LK.getAsset('ui_background', {
width: 1000,
height: 800,
anchorX: 0.5,
anchorY: 0.5
});
popupBg.tint = 0x222222;
popupBg.x = 1024;
popupBg.y = 1366;
upgradePopup.addChild(popupBg);
// Title
var titleText = new Text2('Upgrade Wizard', {
size: 50,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 1024;
titleText.y = 1100;
upgradePopup.addChild(titleText);
// Current wizard info
var currentInfoText = new Text2('Current Level: ' + wizard.level, {
size: 35,
fill: 0xFFFFFF
});
currentInfoText.anchor.set(0.5, 0.5);
currentInfoText.x = 1024;
currentInfoText.y = 1200;
upgradePopup.addChild(currentInfoText);
var elementNames = {
'fire': 'Fire',
'ice': 'Ice',
'nature': 'Nature',
'dark': 'Dark',
'light': 'Light'
};
var currentDamageText = new Text2(elementNames[wizard.elementType] + ': ' + wizard.elementalDamage + ' | Magic: ' + wizard.magicDamage, {
size: 30,
fill: 0x00FF00
});
currentDamageText.anchor.set(0.5, 0.5);
currentDamageText.x = 1024;
currentDamageText.y = 1250;
upgradePopup.addChild(currentDamageText);
// Upgrade info (if not max level)
if (wizard.level < 10) {
var upgradeCost = Math.floor(500 * Math.pow(1.8, wizard.level) * 0.6);
var newElementalDamage = 100 * (wizard.level + 1);
var newMagicDamage = 100 * (wizard.level + 1);
var upgradeInfoText = new Text2('Upgrade to Level ' + (wizard.level + 1), {
size: 35,
fill: 0xFFFF00
});
upgradeInfoText.anchor.set(0.5, 0.5);
upgradeInfoText.x = 1024;
upgradeInfoText.y = 1350;
upgradePopup.addChild(upgradeInfoText);
var upgradeDamageText = new Text2(elementNames[wizard.elementType] + ': ' + newElementalDamage + ' | Magic: ' + newMagicDamage, {
size: 30,
fill: 0x00FF00
});
upgradeDamageText.anchor.set(0.5, 0.5);
upgradeDamageText.x = 1024;
upgradeDamageText.y = 1400;
upgradePopup.addChild(upgradeDamageText);
var costText = new Text2('Cost: ' + upgradeCost, {
size: 35,
fill: gold >= upgradeCost ? 0xFFFF00 : 0xFF0000
});
costText.anchor.set(0.5, 0.5);
costText.x = 1024;
costText.y = 1450;
upgradePopup.addChild(costText);
// Upgrade button
var upgradeBtn = LK.getAsset('ui_background', {
width: 200,
height: 60,
anchorX: 0.5,
anchorY: 0.5
});
upgradeBtn.tint = gold >= upgradeCost ? 0x00AA00 : 0x666666;
upgradeBtn.x = 924;
upgradeBtn.y = 1550;
var upgradeText = new Text2('Upgrade', {
size: 30,
fill: 0xFFFFFF
});
upgradeText.anchor.set(0.5, 0.5);
upgradeBtn.addChild(upgradeText);
upgradeBtn.down = function () {
if (gold >= upgradeCost) {
upgradeWizard(wizard, upgradeCost);
upgradePopup.destroy();
selectedTowerSlot = null;
gameState = gameStarted ? 'playing' : 'setup';
}
};
upgradePopup.addChild(upgradeBtn);
} else {
// Max level reached
var maxLevelText = new Text2('Maximum Level Reached!', {
size: 40,
fill: 0xFF6600
});
maxLevelText.anchor.set(0.5, 0.5);
maxLevelText.x = 1024;
maxLevelText.y = 1400;
upgradePopup.addChild(maxLevelText);
}
// Close button
var closeBtn = LK.getAsset('ui_background', {
width: 100,
height: 50,
anchorX: 0.5,
anchorY: 0.5
});
closeBtn.tint = 0xff0000;
closeBtn.x = 1124;
closeBtn.y = 1550;
var closeText = new Text2('X', {
size: 30,
fill: 0xFFFFFF
});
closeText.anchor.set(0.5, 0.5);
closeBtn.addChild(closeText);
closeBtn.down = function () {
upgradePopup.destroy();
selectedTowerSlot = null;
gameState = gameStarted ? 'playing' : 'setup';
};
upgradePopup.addChild(closeBtn);
game.addChild(upgradePopup);
}
function upgradeWizard(wizard, cost) {
gold -= cost;
updateGoldDisplay();
// Upgrade wizard stats
wizard.level++;
wizard.elementalDamage = 100 * wizard.level;
wizard.magicDamage = 100 * wizard.level;
wizard.range = (150 + wizard.level * 20) * 1.15;
wizard.attackSpeed = (1000 - wizard.level * 50) * 0.9;
wizard.cost = 500 * Math.pow(1.8, wizard.level - 1);
// Update level display on wizard
var levelText = wizard.children.find(function (child) {
return child instanceof Text2;
});
if (levelText) {
levelText.setText(wizard.level.toString());
}
// Update range circle if it exists
if (wizard.rangeCircle) {
wizard.rangeCircle.scaleX = wizard.range / 45;
wizard.rangeCircle.scaleY = wizard.range / 45;
}
}
function placeWizardOnSelectedSlot(wizardType, level) {
if (selectedTowerSlot && selectedTowerSlot.isEmpty) {
var cost = 500 * Math.pow(1.8, level - 1);
if (gold >= cost) {
gold -= cost;
updateGoldDisplay();
var wizard = new Wizard(wizardType, level);
wizard.x = selectedTowerSlot.x;
wizard.y = selectedTowerSlot.y;
selectedTowerSlot.wizard = wizard;
selectedTowerSlot.isEmpty = false;
// Add range visualization
var rangeCircle = LK.getAsset('tower_slot', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: wizard.range / 45,
scaleY: wizard.range / 45
});
rangeCircle.tint = 0x00ff00;
rangeCircle.alpha = 0.2;
rangeCircle.x = wizard.x;
rangeCircle.y = wizard.y;
game.addChild(rangeCircle);
wizard.rangeCircle = rangeCircle;
game.addChild(wizard);
wizards.push(wizard);
closeWizardSelectionPopup();
}
}
}
// Helper functions
function updateGoldDisplay() {
goldText.setText('Gold: ' + gold);
}
function updateBaseHPDisplay() {
baseHPText.setText('Base HP: ' + baseHP);
}
function updateWaveDisplay() {
waveText.setText('Wave: ' + currentWave + '/' + maxWaves);
}
function spawnEnemy() {
// Determine enemy type for this wave - same type for all enemies in wave
if (currentWaveEnemyType === undefined) {
var rand = Math.random();
if (rand < 0.6) currentWaveEnemyType = 'basic';else if (rand < 0.85) currentWaveEnemyType = 'fast';else currentWaveEnemyType = 'tank';
}
var enemy = new Enemy(currentWaveEnemyType, currentLevel);
enemy.x = currentPath[0].x;
enemy.y = currentPath[0].y;
enemies.push(enemy);
game.addChild(enemy);
}
function getEnemiesPerWave() {
var min, max;
if (currentLevel >= 1 && currentLevel <= 5) {
min = 10;
max = 20;
} else if (currentLevel >= 6 && currentLevel <= 10) {
min = 15;
max = 25;
} else if (currentLevel >= 11 && currentLevel <= 15) {
min = 20;
max = 35;
} else if (currentLevel >= 16 && currentLevel <= 20) {
min = 35;
max = 50;
} else {
min = 50;
max = 70;
}
return Math.floor(Math.random() * (max - min + 1)) + min;
}
function startNextWave() {
currentWave++;
enemiesSpawnedThisWave = 0;
enemiesPerWave = getEnemiesPerWave();
lastWaveEndTime = LK.ticks;
gameState = 'playing';
currentWaveEnemyType = undefined; // Reset enemy type for new wave
updateWaveDisplay();
}
// Game update loop
game.update = function () {
// Only update game logic if not in setup or wizard selection
if (gameState === 'setup' || gameState === 'wizard_selection' || gameState === 'wizard_upgrade') {
return;
}
// Spawn enemies with proper timing
if (gameState === 'playing' && waveInProgress && enemiesSpawnedThisWave < enemiesPerWave) {
// Only spawn if enough time has passed since last spawn
if (LK.ticks - lastSpawnTime >= enemySpawnDelay) {
spawnEnemy();
enemiesSpawnedThisWave++;
lastSpawnTime = LK.ticks;
// Check if all enemies have been spawned for this wave
if (enemiesSpawnedThisWave >= enemiesPerWave) {
allEnemiesEnteredMaze = true;
lastEnemyEnteredTime = LK.ticks;
}
}
}
// Check if wave is complete - all enemies spawned and all enemies cleared
if (waveInProgress && allEnemiesEnteredMaze && enemies.length === 0) {
// Wait 5 seconds after last enemy entered before allowing next wave
if (LK.ticks - lastEnemyEnteredTime >= 300) {
// 5 seconds at 60fps
waveInProgress = false;
allEnemiesEnteredMaze = false;
enemiesSpawnedThisWave = 0;
gameState = 'between_waves';
lastWaveEndTime = LK.ticks;
waveStartTime = 0;
if (currentWave < maxWaves) {
// Automatically start next wave after 5 seconds
startNextWave();
waveInProgress = true;
allEnemiesEnteredMaze = false;
lastSpawnTime = 0;
} else {
// Level complete
currentLevel++;
currentWave = 0; // Will be incremented in startNextWave
var wavesPerLevel = [7, 8, 10, 11, 13, 14, 16, 17, 19, 20, 22, 23, 25, 26, 28, 29, 31, 32, 34, 35];
maxWaves = wavesPerLevel[currentLevel - 1] || 10;
enemiesPerWave = getEnemiesPerWave();
if (currentLevel > 20) {
LK.showYouWin();
return;
}
// Automatically start next level
startNextWave();
waveInProgress = true;
allEnemiesEnteredMaze = false;
lastSpawnTime = 0;
}
}
}
// Update projectiles
for (var i = projectiles.length - 1; i >= 0; i--) {
var projectile = projectiles[i];
if (projectile.destroyed) {
projectiles.splice(i, 1);
}
}
// Clean up destroyed enemies
for (var i = enemies.length - 1; i >= 0; i--) {
if (enemies[i].destroyed) {
enemies.splice(i, 1);
}
}
// Clean up destroyed wizards
for (var i = wizards.length - 1; i >= 0; i--) {
if (wizards[i].destroyed) {
wizards.splice(i, 1);
}
}
};
Fort to defend. In-Game asset. 2d. High contrast. No shadows
bulky warrior like a tank. In-Game asset. 2d. High contrast. No shadows
fast and slim warrior to represent speed. In-Game asset. 2d. High contrast. No shadows
in game asset, colored, 2d warrior, face covered with helmet
dark wizard 2d in game asset. In-Game asset. 2d. High contrast. No shadows
mostly transparent grey circle. In-Game asset. 2d. High contrast. No shadows
top view, stone high tower.. In-Game asset. 2d. High contrast. No shadows
a fire wizard. In-Game asset. 2d. High contrast. No shadows
cornerstone 2d in game asset from top view
2d in game asset ice wizard. In-Game asset. 2d. High contrast. No shadows
2d in game asset nature (green) wizard.
2d in game asset light (yellow) wizard.
magic bolt traveling in air
2d, top view, very light grey, very very very small tiled (at least 1000 tiles), cobblestone road texture