User prompt
Please fix the bug: 'ReferenceError: Can't find variable: guardian' in or related to this line: 'guardian.update();' Line Number: 2120
User prompt
Please fix the bug: 'ReferenceError: Can't find variable: guardian' in or related to this line: 'guardian.update();' Line Number: 2120
User prompt
Please fix the bug: 'ReferenceError: Can't find variable: guardian' in or related to this line: 'guardian.update();' Line Number: 2120
User prompt
Add a title screen but remove all instances of initgame and replace it
User prompt
Please fix the bug: 'TypeError: self.target.takeDamage is not a function' in or related to this line: 'self.target.takeDamage(self.damage);' Line Number: 677
User prompt
Please fix the bug: 'TypeError: Cannot read properties of undefined (reading 'call')' in or related to this line: 'Enemy.prototype.update.call(self);' Line Number: 379
User prompt
it's still not using it, it is just a re-colored mushroom
User prompt
make the farm tower use the farm tower asset instead of the shreoom asset
User prompt
there is still no asset for both of them
User prompt
also there is no flame tower asset or farm tower asset, make them their own assets
User prompt
MORE ENEMIES
User prompt
make the healing shroom a farm tower that gives you extra cash at the end of a wave. and make the farm tower and the fire tower their own assets
User prompt
add a currency text and make the sell button work and give you back the money you spent on said tower
User prompt
make the menu BIGGER
User prompt
make the sell and upgrade button work
User prompt
make the edit menu larger and make it go to the front layer infront of everything
User prompt
make the towers editable by tapping on them when they're placed, so you can upgrade and delete them
User prompt
move the green healthbar not the text to the CENTER
User prompt
thats above, move it under
User prompt
mnove the healthbar under start wave button
User prompt
move the healthbar to the left to center the text
User prompt
move the healthbar over a bit
User prompt
add a healthbar asset and a button asset for start wave
User prompt
add more tower types and enemies
User prompt
make every wave a unique map ↪💡 Consider importing and using the following plugins: @upit/tween.v1
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1", { highScore: 0, unlockedTowers: ["thorn", "shroom", "flame", "healing"] }); /**** * Classes ****/ var BuildingSpot = Container.expand(function () { var self = Container.call(this); var spotGraphics = self.attachAsset('buildingSpot', { anchorX: 0.5, anchorY: 0.5, alpha: 0.4 }); self.gridX = 0; self.gridY = 0; self.occupied = false; self.tower = null; self.down = function (x, y, obj) { if (!self.occupied && currentMode === 'building' && selectedTower !== null && essence >= towerCosts[selectedTower]) { self.buildTower(selectedTower); } }; self.buildTower = function (towerType) { var tower; if (towerType === 'thorn') { tower = new ThornTower(); } else if (towerType === 'shroom') { tower = new ShroomTower(); } else if (towerType === 'flame') { tower = new FlameTower(); } else if (towerType === 'healing') { tower = new FarmTower(); } if (tower) { tower.x = self.x; tower.y = self.y; tower.gridX = self.gridX; tower.gridY = self.gridY; game.addChild(tower); towers.push(tower); essence -= towerCosts[towerType]; updateEssenceText(); self.occupied = true; self.tower = tower; LK.getSound('towerPlaced').play(); // Hide tower menu if it's visible if (towerMenu && towerMenu.visible) { towerMenu.hide(); } } }; return self; }); var Enemy = Container.expand(function () { var self = Container.call(this); var enemyGraphics = self.attachAsset('enemy', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 2; self.maxHealth = 50; self.health = self.maxHealth; self.value = 10; // Essence value when defeated self.pathIndex = 0; self.alive = true; self.update = function () { if (!self.alive) { return; } // Follow path if (self.pathIndex < path.length) { var targetPoint = path[self.pathIndex]; var dx = targetPoint.x - self.x; var dy = targetPoint.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); // If near target point, move to next point if (distance < 20) { self.pathIndex++; // If reached the end of path (HeartTree) if (self.pathIndex >= path.length) { heartTree.takeDamage(10); self.alive = false; return; } } else { // Move towards current target point var angle = Math.atan2(dy, dx); self.x += Math.cos(angle) * self.speed; self.y += Math.sin(angle) * self.speed; } } }; // Add a static method to Enemy to use for inheritance Enemy.prototype.update = function () { if (!this.alive) { return; } // Follow path if (this.pathIndex < path.length) { var targetPoint = path[this.pathIndex]; var dx = targetPoint.x - this.x; var dy = targetPoint.y - this.y; var distance = Math.sqrt(dx * dx + dy * dy); // If near target point, move to next point if (distance < 20) { this.pathIndex++; // If reached the end of path (HeartTree) if (this.pathIndex >= path.length) { heartTree.takeDamage(10); this.alive = false; return; } } else { // Move towards current target point var angle = Math.atan2(dy, dx); this.x += Math.cos(angle) * this.speed; this.y += Math.sin(angle) * this.speed; } } }; self.takeDamage = function (amount) { self.health -= amount; if (self.health <= 0) { self.die(); } else { // Flash enemy when hit LK.effects.flashObject(self, 0xFF0000, 200); } }; self.die = function () { self.alive = false; // Drop essence var essenceObj = new Essence(); essenceObj.value = self.value; essenceObj.x = self.x; essenceObj.y = self.y; game.addChild(essenceObj); essenceItems.push(essenceObj); // Increase score LK.setScore(LK.getScore() + self.value); scoreTxt.setText(LK.getScore()); LK.getSound('enemyDeath').play(); }; return self; }); var TankEnemy = Enemy.expand(function () { var self = Enemy.call(this); // Replace with custom graphics self.removeChildAt(0); // Remove default enemy graphics var tankGraphics = self.attachAsset('enemy', { anchorX: 0.5, anchorY: 0.5, tint: 0x666666, // Grey tint for tank enemies scaleX: 1.4, scaleY: 1.4 // Larger }); self.speed = 1; // Slower self.maxHealth = 120; // Higher health self.health = self.maxHealth; self.value = 25; // Higher value return self; }); var SplitEnemy = Enemy.expand(function () { var self = Enemy.call(this); // Replace with custom graphics self.removeChildAt(0); // Remove default enemy graphics var splitGraphics = self.attachAsset('enemy', { anchorX: 0.5, anchorY: 0.5, tint: 0xFF6347, // Tomato red for split enemies scaleX: 1.3, scaleY: 1.3 }); self.speed = 1.5; self.maxHealth = 70; self.health = self.maxHealth; self.value = 12; self.splitCount = 2; // Number of smaller enemies to spawn when killed self.size = 'large'; // Size tracking for spawned children // Override die method to create smaller enemies when killed self.die = function () { self.alive = false; // Only split if this is the original large enemy if (self.size === 'large') { // Spawn smaller enemies for (var i = 0; i < self.splitCount; i++) { var smallEnemy = new SplitEnemy(); // Make it smaller smallEnemy.scale.set(0.6, 0.6); // Reduce stats for smaller version smallEnemy.size = 'small'; smallEnemy.maxHealth = 25; smallEnemy.health = smallEnemy.maxHealth; smallEnemy.speed = 2.2; smallEnemy.value = 8; smallEnemy.pathIndex = self.pathIndex; // Position with slight offset smallEnemy.x = self.x + (Math.random() * 60 - 30); smallEnemy.y = self.y + (Math.random() * 60 - 30); game.addChild(smallEnemy); enemies.push(smallEnemy); enemiesRemaining++; } } // Create explosion effect var explosion = LK.getAsset('projectile', { anchorX: 0.5, anchorY: 0.5, x: self.x, y: self.y, tint: 0xFF4500, scaleX: 2, scaleY: 2 }); game.addChild(explosion); // Animate explosion tween(explosion, { alpha: 0, scaleX: 3, scaleY: 3 }, { duration: 300, onFinish: function onFinish() { explosion.destroy(); } }); // Drop essence var essenceObj = new Essence(); essenceObj.value = self.value; essenceObj.x = self.x; essenceObj.y = self.y; game.addChild(essenceObj); essenceItems.push(essenceObj); // Increase score LK.setScore(LK.getScore() + self.value); scoreTxt.setText(LK.getScore()); LK.getSound('enemyDeath').play(); }; return self; }); var SpeedyEnemy = Enemy.expand(function () { var self = Enemy.call(this); // Replace with custom graphics self.removeChildAt(0); // Remove default enemy graphics var speedyGraphics = self.attachAsset('enemy', { anchorX: 0.5, anchorY: 0.5, tint: 0xFFFF00, // Yellow tint for speedy enemies scaleX: 0.8, scaleY: 1.2 // Tall and thin }); self.speed = 4; // Much faster self.maxHealth = 30; // Lower health self.health = self.maxHealth; self.value = 15; // Slightly higher value return self; }); var PoisonEnemy = Enemy.expand(function () { var self = Enemy.call(this); // Replace with custom graphics self.removeChildAt(0); // Remove default enemy graphics var poisonGraphics = self.attachAsset('enemy', { anchorX: 0.5, anchorY: 0.5, tint: 0x00FF00, // Green tint for poison enemies scaleX: 1.1, scaleY: 1.1 }); self.speed = 1.8; self.maxHealth = 80; self.health = self.maxHealth; self.value = 20; self.lastShotTime = 0; self.shotInterval = 3; // Seconds between shots // Override update to add shooting behavior self.update = function () { if (!self.alive) { return; } // Call parent update method for movement Enemy.prototype.update.call(self); // Add shooting behavior var currentTime = LK.ticks / 60; if (currentTime - self.lastShotTime >= self.shotInterval) { self.lastShotTime = currentTime; self.shootProjectile(); } }; self.shootProjectile = function () { // Create a poison projectile aimed at heart tree var projectile = new Projectile(); projectile.x = self.x; projectile.y = self.y; projectile.damage = 5; projectile.speed = 8; projectile.target = heartTree; // Make it green projectile.getChildAt(0).tint = 0x00FF00; game.addChild(projectile); // Visual effect LK.effects.flashObject(self, 0x00FF00, 200); }; return self; }); var FlyingEnemy = Enemy.expand(function () { var self = Enemy.call(this); // Replace with custom graphics self.removeChildAt(0); // Remove default enemy graphics var flyingGraphics = self.attachAsset('enemy', { anchorX: 0.5, anchorY: 0.5, tint: 0x9370DB, // Purple tint for flying enemies scaleX: 0.9, scaleY: 0.6 // Flatter shape }); self.speed = 3; self.maxHealth = 40; self.health = self.maxHealth; self.value = 18; self.flyingMode = true; // Override the update method to go directly to heart tree self.update = function () { if (!self.alive) { return; } // Flying enemies ignore the path and go straight for the heart tree var dx = heartTree.x - self.x; var dy = heartTree.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); // If near heart tree, attack it if (distance < 20) { heartTree.takeDamage(15); self.alive = false; return; } // Move towards heart tree var angle = Math.atan2(dy, dx); self.x += Math.cos(angle) * self.speed; self.y += Math.sin(angle) * self.speed; // Add a slight wave pattern to flight self.y += Math.sin(LK.ticks / 10) * 0.5; }; return self; }); var BossEnemy = Enemy.expand(function () { var self = Enemy.call(this); // Replace with boss graphics self.removeChildAt(0); // Remove default enemy graphics var bossGraphics = self.attachAsset('bossEnemy', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.8, // Make boss bigger scaleY: 1.8 // Make boss bigger }); self.speed = 1.2; self.maxHealth = 300 + currentWave * 30; self.health = self.maxHealth; self.value = 100 + currentWave * 15; self.lastAttackTime = 0; self.attackCooldown = 3; // seconds between special attacks // Override update method to add special attacks self.update = function () { if (!self.alive) { return; } // Call parent update for movement Enemy.prototype.update.call(self); // Special attacks var currentTime = LK.ticks / 60; if (currentTime - self.lastAttackTime >= self.attackCooldown) { self.lastAttackTime = currentTime; self.specialAttack(); } // Pulsate effect var pulse = 1 + Math.sin(LK.ticks / 15) * 0.1; self.scale.set(1.8 * pulse, 1.8 * pulse); }; // Special attack based on boss wave level self.specialAttack = function () { // Boss becomes more dangerous in later waves var attackType = Math.floor(Math.random() * 3); // Flash effect for attack LK.effects.flashObject(self, 0xFF0000, 300); if (attackType === 0) { // Projectile burst attack var projectileCount = 8; for (var i = 0; i < projectileCount; i++) { var angle = i / projectileCount * Math.PI * 2; var projectile = new Projectile(); projectile.x = self.x; projectile.y = self.y; projectile.damage = 8; // Set a fixed position as target based on angle var targetDummy = { x: self.x + Math.cos(angle) * 500, y: self.y + Math.sin(angle) * 500, alive: true }; projectile.target = targetDummy; // Give projectile red color projectile.getChildAt(0).tint = 0xFF0000; game.addChild(projectile); } } else if (attackType === 1) { // Spawn minions var minionCount = 2 + Math.floor(currentWave / 5); for (var i = 0; i < minionCount; i++) { var minion = new SpeedyEnemy(); minion.x = self.x; minion.y = self.y; minion.pathIndex = self.pathIndex; minion.maxHealth = 20; minion.health = minion.maxHealth; minion.speed = 5; minion.value = 5; // Make minions smaller minion.scale.set(0.7, 0.7); game.addChild(minion); enemies.push(minion); enemiesRemaining++; } } else { // Heal self var healAmount = Math.min(self.maxHealth - self.health, self.maxHealth * 0.1); if (healAmount > 0) { self.health += healAmount; // Create healing effect var healEffect = new Text2("+" + Math.floor(healAmount), { size: 40, fill: 0x00FF00 }); healEffect.anchor.set(0.5, 0.5); healEffect.x = self.x; healEffect.y = self.y - 50; game.addChild(healEffect); // Animate healing text tween(healEffect, { alpha: 0, y: healEffect.y - 50 }, { duration: 1000, onFinish: function onFinish() { healEffect.destroy(); } }); } } }; // Override takeDamage to get enraged at low health self.takeDamage = function (amount) { self.health -= amount; // Enrage when below 30% health if (self.health <= self.maxHealth * 0.3 && self.speed < 1.8) { self.speed *= 1.5; // Move faster self.attackCooldown *= 0.6; // Attack more often // Visual effect for enrage LK.effects.flashObject(self, 0xFF0000, 1000); // Create enrage message var enrageText = new Text2("BOSS ENRAGED!", { size: 70, fill: 0xFF0000 }); enrageText.anchor.set(0.5, 0.5); enrageText.x = 2048 / 2; enrageText.y = 2732 / 2; game.addChild(enrageText); // Animate message tween(enrageText, { alpha: 0, scaleX: 2, scaleY: 2 }, { duration: 1500, onFinish: function onFinish() { enrageText.destroy(); } }); } if (self.health <= 0) { self.die(); } else { // Flash when hit LK.effects.flashObject(self, 0xFF0000, 200); } }; return self; }); var Essence = Container.expand(function () { var self = Container.call(this); var essenceGraphics = self.attachAsset('essence', { anchorX: 0.5, anchorY: 0.5 }); self.value = 10; self.lifeTime = 10; // seconds self.creationTime = LK.ticks / 60; self.collected = false; self.update = function () { if (self.collected) { return; } // Check if guardian is close enough to collect var dx = guardian.x - self.x; var dy = guardian.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < 80) { self.collect(); return; } // Check if timed out var currentTime = LK.ticks / 60; if (currentTime - self.creationTime > self.lifeTime) { self.collected = true; } // Pulse animation var pulseFactor = 1 + Math.sin(LK.ticks / 20) * 0.1; self.scale.set(pulseFactor, pulseFactor); }; self.collect = function () { essence += self.value; updateEssenceText(); self.collected = true; LK.getSound('essenceCollected').play(); // Animate collection tween(self, { alpha: 0, scaleX: 2, scaleY: 2 }, { duration: 300, onFinish: function onFinish() { self.destroy(); } }); }; return self; }); var Guardian = Container.expand(function () { var self = Container.call(this); var guardianGraphics = self.attachAsset('guardian', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 7; self.targetX = null; self.targetY = null; self.moving = false; self.update = function () { if (self.moving && self.targetX !== null && self.targetY !== null) { var dx = self.targetX - self.x; var dy = self.targetY - self.y; var distance = Math.sqrt(dx * dx + dy * dy); // If near target, stop moving if (distance < 10) { self.moving = false; self.targetX = null; self.targetY = null; } else { // Move towards target var angle = Math.atan2(dy, dx); var nextX = self.x + Math.cos(angle) * self.speed; var nextY = self.y + Math.sin(angle) * self.speed; // Check if the next position is valid if (isPositionValid(nextX, nextY)) { self.x = nextX; self.y = nextY; } else { self.moving = false; } } } }; self.moveTo = function (x, y) { if (isPositionValid(x, y)) { self.targetX = x; self.targetY = y; self.moving = true; } }; return self; }); var HeartTree = Container.expand(function () { var self = Container.call(this); var treeGraphics = self.attachAsset('heartTree', { anchorX: 0.5, anchorY: 0.5 }); self.maxHealth = 100; self.health = self.maxHealth; self.takeDamage = function (amount) { self.health -= amount; updateHealthBar(); LK.getSound('heartTreeDamage').play(); LK.effects.flashObject(self, 0xFF0000, 300); if (self.health <= 0) { // Game over LK.showGameOver(); // Update high score if needed if (LK.getScore() > storage.highScore) { storage.highScore = LK.getScore(); } } }; return self; }); var PathTile = Container.expand(function () { var self = Container.call(this); var pathGraphics = self.attachAsset('path', { anchorX: 0.5, anchorY: 0.5, alpha: 0.8 }); self.gridX = 0; self.gridY = 0; return self; }); var Projectile = Container.expand(function () { var self = Container.call(this); var projectileGraphics = self.attachAsset('projectile', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 12; self.damage = 10; self.target = null; self.alive = true; self.update = function () { if (!self.target || !self.target.alive) { self.alive = false; 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 < 20) { // Hit target if (typeof self.target.takeDamage === 'function') { self.target.takeDamage(self.damage); } self.alive = false; return; } // Move towards target var angle = Math.atan2(dy, dx); self.x += Math.cos(angle) * self.speed; self.y += Math.sin(angle) * self.speed; }; return self; }); var SpawnPoint = Container.expand(function () { var self = Container.call(this); var spawnGraphics = self.attachAsset('spawnPoint', { anchorX: 0.5, anchorY: 0.5 }); self.active = true; return self; }); var TitleScreen = Container.expand(function () { var self = Container.call(this); // Background overlay for title screen var overlay = self.attachAsset('waveButton', { anchorX: 0.5, anchorY: 0.5, width: 2048, height: 2732, tint: 0x122112, alpha: 0.8 }); // Game title self.title = new Text2('Forest Defender', { size: 180, fill: 0x83de44 }); self.title.anchor.set(0.5, 0.5); self.title.y = -500; self.addChild(self.title); // Subtitle self.subtitle = new Text2('Protect the Heart Tree!', { size: 80, fill: 0xFFFFFF }); self.subtitle.anchor.set(0.5, 0.5); self.subtitle.y = -350; self.addChild(self.subtitle); // Instructions self.instructions = new Text2('Build towers, collect essence\nand survive enemy waves', { size: 60, fill: 0xCCCCCC }); self.instructions.anchor.set(0.5, 0.5); self.instructions.y = -150; self.addChild(self.instructions); // Play button self.playBtn = new Container(); var playBtnBg = LK.getAsset('waveButton', { anchorX: 0.5, anchorY: 0.5, width: 400, height: 120, tint: 0x83de44 }); self.playBtn.addChild(playBtnBg); var playBtnText = new Text2('START GAME', { size: 70, fill: 0xFFFFFF }); playBtnText.anchor.set(0.5, 0.5); self.playBtn.addChild(playBtnText); self.playBtn.y = 200; self.addChild(self.playBtn); // High score display self.highScoreText = new Text2('High Score: ' + storage.highScore, { size: 50, fill: 0xFFD700 }); self.highScoreText.anchor.set(0.5, 0.5); self.highScoreText.y = 350; self.addChild(self.highScoreText); // Add pulsing animation to play button self.update = function () { var pulseFactor = 1 + Math.sin(LK.ticks / 20) * 0.05; self.playBtn.scale.set(pulseFactor, pulseFactor); }; // Play button event handler self.playBtn.down = function () { // Create fade out effect tween(self, { alpha: 0 }, { duration: 800, onFinish: function onFinish() { self.destroy(); initGame(); } }); }; return self; }); var Tower = Container.expand(function () { var self = Container.call(this); var baseGraphics = self.attachAsset('towerBase', { anchorX: 0.5, anchorY: 0.5 }); self.gridX = 0; self.gridY = 0; self.range = 300; self.damage = 10; self.attackSpeed = 1; // attacks per second self.lastAttackTime = 0; self.level = 1; self.target = null; self.projectiles = []; self.findTarget = function () { var closestEnemy = null; var closestDistance = self.range; for (var i = 0; i < enemies.length; i++) { var enemy = enemies[i]; var dx = enemy.x - self.x; var dy = enemy.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < closestDistance) { closestDistance = distance; closestEnemy = enemy; } } return closestEnemy; }; self.attack = function () { if (!self.target || !self.target.alive) { self.target = self.findTarget(); } if (self.target) { var dx = self.target.x - self.x; var dy = self.target.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > self.range) { self.target = null; return; } var currentTime = LK.ticks / 60; if (currentTime - self.lastAttackTime >= 1 / self.attackSpeed) { self.lastAttackTime = currentTime; self.fireProjectile(); } } }; self.fireProjectile = function () { var projectile = new Projectile(); projectile.damage = self.damage; projectile.target = self.target; projectile.x = self.x; projectile.y = self.y; game.addChild(projectile); self.projectiles.push(projectile); LK.getSound('projectileShot').play(); }; self.down = function (x, y, obj) { // Show tower menu when tower is tapped if (towerMenu && currentMode === 'building') { towerMenu.showForTower(self); } }; self.update = function () { self.attack(); // Update projectiles for (var i = self.projectiles.length - 1; i >= 0; i--) { var projectile = self.projectiles[i]; if (!projectile.alive) { projectile.destroy(); self.projectiles.splice(i, 1); } } }; return self; }); var ThornTower = Tower.expand(function () { var self = Tower.call(this); var towerGraphics = self.attachAsset('thornTower', { anchorX: 0.5, anchorY: 0.5, y: -20 // Offset to position on base }); self.range = 350; self.damage = 15; self.attackSpeed = 1.2; return self; }); var ShroomTower = Tower.expand(function () { var self = Tower.call(this); var towerGraphics = self.attachAsset('shroomTower', { anchorX: 0.5, anchorY: 0.5, y: -20 // Offset to position on base }); self.range = 250; self.damage = 25; self.attackSpeed = 0.8; return self; }); var FlameTower = Tower.expand(function () { var self = Tower.call(this); // Remove default base and add custom base with red tint self.removeChildAt(0); var baseGraphics = self.attachAsset('towerBase', { anchorX: 0.5, anchorY: 0.5, tint: 0xFF8800 }); var towerGraphics = self.attachAsset('flameTower', { anchorX: 0.5, anchorY: 0.5, y: -20, // Offset to position on base tint: 0xFF3300 }); self.range = 200; // Short range self.damage = 8; // Lower damage per hit self.attackSpeed = 2.5; // Fast attack speed self.areaOfEffect = true; // This tower hits multiple enemies // Override the fireProjectile method for area attack self.fireProjectile = function () { // Find all enemies in range var hitEnemies = []; for (var i = 0; i < enemies.length; i++) { var enemy = enemies[i]; var dx = enemy.x - self.x; var dy = enemy.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance <= self.range) { hitEnemies.push(enemy); // Visual effect - flame burst var flameBurst = LK.getAsset('projectile', { anchorX: 0.5, anchorY: 0.5, x: enemy.x, y: enemy.y, tint: 0xFF4500, scaleX: 1.5, scaleY: 1.5 }); game.addChild(flameBurst); // Animate and remove flame effect tween(flameBurst, { alpha: 0, scaleX: 0.5, scaleY: 0.5 }, { duration: 300, onFinish: function onFinish() { flameBurst.destroy(); } }); // Damage the enemy enemy.takeDamage(self.damage); } } if (hitEnemies.length > 0) { LK.getSound('projectileShot').play(); } }; return self; }); var FarmTower = Tower.expand(function () { var self = Tower.call(this); // Remove default base and add custom base with gold tint self.removeChildAt(0); var baseGraphics = self.attachAsset('towerBase', { anchorX: 0.5, anchorY: 0.5, tint: 0xFFD700 }); var towerGraphics = self.attachAsset('farmTower', { anchorX: 0.5, anchorY: 0.5, y: -20 // Offset to position on base }); self.range = 280; self.incomeAmount = 25; // Base income per wave self.level = 1; // Override the attack method to do nothing - this tower doesn't attack self.attack = function () { // Instead, just pulsate to show it's active if (LK.ticks % 60 == 0) { // Visual effect on self - gold coins visual var coinEffect = LK.getAsset('essence', { anchorX: 0.5, anchorY: 0.5, x: self.x + (Math.random() * 40 - 20), y: self.y + (Math.random() * 40 - 20), tint: 0xFFD700, alpha: 0.7 }); game.addChild(coinEffect); // Animate and remove coin effect tween(coinEffect, { alpha: 0, y: coinEffect.y - 50, scaleX: 0.5, scaleY: 0.5 }, { duration: 800, onFinish: function onFinish() { coinEffect.destroy(); } }); } }; // Get income amount based on level self.getIncomePerWave = function () { return Math.floor(self.incomeAmount * (1 + (self.level - 1) * 0.5)); }; // Override the fireProjectile to do nothing self.fireProjectile = function () {}; return self; }); var TowerMenu = Container.expand(function () { var self = Container.call(this); self.tower = null; // Background for menu var menuBg = self.attachAsset('waveButton', { anchorX: 0.5, anchorY: 0.5, width: 600, height: 450, tint: 0x333333 }); // Title text self.titleText = new Text2('Tower Menu', { size: 70, fill: 0xFFFFFF }); self.titleText.anchor.set(0.5, 0); self.titleText.y = -180; self.addChild(self.titleText); // Level text self.levelText = new Text2('Level: 1', { size: 60, fill: 0xFFFFFF }); self.levelText.anchor.set(0.5, 0); self.levelText.y = -80; self.addChild(self.levelText); // Stats text self.statsText = new Text2('Damage: 10\nRange: 300', { size: 55, fill: 0xFFFFFF }); self.statsText.anchor.set(0.5, 0); self.statsText.y = 20; self.addChild(self.statsText); // Upgrade button self.upgradeBtn = new Container(); var upgradeBtnBg = LK.getAsset('waveButton', { anchorX: 0.5, anchorY: 0.5, width: 220, height: 100, tint: 0x00AA00 }); self.upgradeBtn.addChild(upgradeBtnBg); self.upgradeCostText = new Text2('Upgrade\n50', { size: 40, fill: 0xFFFFFF }); self.upgradeCostText.anchor.set(0.5, 0.5); self.upgradeBtn.addChild(self.upgradeCostText); self.upgradeBtn.x = -140; self.upgradeBtn.y = 170; self.addChild(self.upgradeBtn); // Sell button self.sellBtn = new Container(); var sellBtnBg = LK.getAsset('waveButton', { anchorX: 0.5, anchorY: 0.5, width: 220, height: 100, tint: 0xAA0000 }); self.sellBtn.addChild(sellBtnBg); self.sellValueText = new Text2('Sell\n25', { size: 40, fill: 0xFFFFFF }); self.sellValueText.anchor.set(0.5, 0.5); self.sellBtn.addChild(self.sellValueText); self.sellBtn.x = 140; self.sellBtn.y = 170; self.addChild(self.sellBtn); // Close button self.closeBtn = new Container(); var closeBtnBg = LK.getAsset('waveButton', { anchorX: 0.5, anchorY: 0.5, width: 50, height: 50, tint: 0x666666 }); self.closeBtn.addChild(closeBtnBg); var closeBtnText = new Text2('X', { size: 35, fill: 0xFFFFFF }); closeBtnText.anchor.set(0.5, 0.5); self.closeBtn.addChild(closeBtnText); self.closeBtn.x = 270; self.closeBtn.y = -200; self.addChild(self.closeBtn); // Initialize event handlers self.upgradeBtn.down = function () { if (self.tower && essence >= self.getUpgradeCost()) { self.upgradeTower(); } }; self.sellBtn.down = function () { if (self.tower) { self.sellTower(); } }; self.closeBtn.down = function () { self.hide(); }; // Upgrade tower self.upgradeTower = function () { if (self.tower && essence >= self.getUpgradeCost()) { var upgradeCost = self.getUpgradeCost(); essence -= upgradeCost; self.tower.level++; // Different upgrade effects based on tower type if (self.tower instanceof FarmTower) { self.tower.incomeAmount += self.tower.level * 10; self.tower.range += self.tower.level * 15; } else { self.tower.damage += self.tower.level * 5; self.tower.range += self.tower.level * 15; self.tower.attackSpeed += self.tower.level * 0.1; } updateEssenceText(); self.updateDisplay(); // Flash effect LK.effects.flashObject(self.tower, 0x00FF00, 500); } }; // Sell tower self.sellTower = function () { if (self.tower) { var sellValue = self.getSellValue(); essence += sellValue; updateEssenceText(); // Show visual effect for essence gain var sellTxt = new Text2("+" + sellValue, { size: 40, fill: 0x00FFFF }); sellTxt.anchor.set(0.5, 0.5); sellTxt.x = self.tower.x; sellTxt.y = self.tower.y; game.addChild(sellTxt); // Animate text tween(sellTxt, { alpha: 0, y: sellTxt.y - 50 }, { duration: 1000, onFinish: function onFinish() { sellTxt.destroy(); } }); // Find building spot and free it for (var i = 0; i < buildingSpots.length; i++) { if (buildingSpots[i].gridX === self.tower.gridX && buildingSpots[i].gridY === self.tower.gridY) { buildingSpots[i].occupied = false; buildingSpots[i].tower = null; break; } } // Find and remove tower from array for (var i = 0; i < towers.length; i++) { if (towers[i] === self.tower) { towers.splice(i, 1); break; } } // Remove tower with fade effect tween(self.tower, { alpha: 0, scaleX: 0.5, scaleY: 0.5 }, { duration: 300, onFinish: function onFinish() { self.tower.destroy(); } }); self.hide(); } }; // Calculate upgrade cost self.getUpgradeCost = function () { if (!self.tower) { return 0; } var baseCost = 0; if (self.tower instanceof ThornTower) { baseCost = 50; } else if (self.tower instanceof ShroomTower) { baseCost = 100; } else if (self.tower instanceof FlameTower) { baseCost = 150; } else if (self.tower instanceof FarmTower) { baseCost = 200; } return Math.floor(baseCost * (1 + 0.5 * self.tower.level)); }; // Calculate sell value self.getSellValue = function () { if (!self.tower) { return 0; } var towerType = ''; if (self.tower instanceof ThornTower) { towerType = 'thorn'; } else if (self.tower instanceof ShroomTower) { towerType = 'shroom'; } else if (self.tower instanceof FlameTower) { towerType = 'flame'; } else if (self.tower instanceof FarmTower) { towerType = 'healing'; } // Calculate sell value based on tower cost var baseCost = towerCosts[towerType]; // Tower sells for 70% of initial cost plus 50% of upgrade costs var initialValue = Math.floor(baseCost * 0.7); var upgradeValue = Math.floor((self.tower.level - 1) * baseCost * 0.5); return initialValue + upgradeValue; }; // Show menu for a specific tower self.showForTower = function (tower) { self.tower = tower; self.updateDisplay(); // Position menu near tower but ensure it's on screen self.x = Math.min(Math.max(tower.x, 300), 2048 - 300); self.y = Math.min(Math.max(tower.y, 300), 2732 - 300); self.visible = true; // Bring to front by removing and re-adding to parent if (self.parent) { var parent = self.parent; parent.removeChild(self); parent.addChild(self); } // Highlight selected tower tower.alpha = 0.7; }; // Update display with current tower info self.updateDisplay = function () { if (!self.tower) { return; } var towerType = ''; if (self.tower instanceof ThornTower) { towerType = 'Thorn Tower'; } else if (self.tower instanceof ShroomTower) { towerType = 'Shroom Tower'; } else if (self.tower instanceof FlameTower) { towerType = 'Flame Tower'; } else if (self.tower instanceof FarmTower) { towerType = 'Farm Tower'; } self.titleText.setText(towerType); self.levelText.setText('Level: ' + self.tower.level); if (self.tower instanceof FarmTower) { self.statsText.setText('Income: ' + self.tower.getIncomePerWave() + '\nRange: ' + self.tower.range + '\nPer Wave'); } else { self.statsText.setText('Damage: ' + self.tower.damage + '\nRange: ' + self.tower.range + '\nSpeed: ' + self.tower.attackSpeed.toFixed(1)); } var upgradeCost = self.getUpgradeCost(); self.upgradeCostText.setText('Upgrade\n' + upgradeCost); if (essence >= upgradeCost) { self.upgradeBtn.alpha = 1.0; } else { self.upgradeBtn.alpha = 0.5; } var sellValue = self.getSellValue(); self.sellValueText.setText('Sell\n' + sellValue); // Make the sell button more/less prominent based on value var sellBtn = self.sellBtn.getChildAt(0); if (sellValue > 100) { sellBtn.tint = 0xFF0000; // Bright red for high value } else { sellBtn.tint = 0xAA0000; // Default red } }; // Hide menu self.hide = function () { self.visible = false; if (self.tower) { self.tower.alpha = 1.0; self.tower = null; } }; self.visible = false; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x2E5C2E // Forest green }); /**** * Game Code ****/ // Game variables function _slicedToArray(r, e) { return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest(); } function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) { return _arrayLikeToArray(r, a); } var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } } function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) { n[e] = r[e]; } return n; } function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) { return; } f = !1; } else { for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0) { ; } } } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t["return"] && (u = t["return"](), Object(u) !== u)) { return; } } finally { if (o) { throw n; } } } return a; } } function _arrayWithHoles(r) { if (Array.isArray(r)) { return r; } } var gridSize = 120; var currentMode = 'guardian'; // 'guardian' or 'building' var selectedTower = 'thorn'; var essence = 100; var currentWave = 0; var waveInProgress = false; var waveTimer = null; var enemiesRemaining = 0; var towers = []; var enemies = []; var essenceItems = []; var buildingSpots = []; var path = []; var spawnPoints = []; var towerMenu = null; // Tower editing menu // Tower costs var towerCosts = { 'thorn': 50, 'shroom': 100, 'flame': 150, 'healing': 200 }; // Tower sell values (will be calculated based on cost and level) var towerSellValues = { 'thorn': Math.floor(towerCosts['thorn'] * 0.7), 'shroom': Math.floor(towerCosts['shroom'] * 0.7), 'flame': Math.floor(towerCosts['flame'] * 0.7), 'healing': Math.floor(towerCosts['healing'] * 0.7) }; // Create UI elements var scoreTxt = new Text2('0', { size: 70, fill: 0xFFFFFF }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); var essenceTxt = new Text2('💎 100', { size: 70, fill: 0x00FFFF }); essenceTxt.anchor.set(0, 0); essenceTxt.x = -250; // Move it away from the edge LK.gui.topRight.addChild(essenceTxt); var waveTxt = new Text2('Wave: 0', { size: 50, fill: 0xFFFFFF }); waveTxt.anchor.set(1, 0); LK.gui.topLeft.addChild(waveTxt); // Position away from the top left corner where menu icon is waveTxt.x = 150; var healthBarBg = new Container(); var healthBarFill = new Container(); var healthBarTxt = new Text2('100/100', { size: 40, fill: 0xFFFFFF }); healthBarTxt.anchor.set(0.5, 0.5); // Setup health bar function setupHealthBar() { // Background bar var healthBg = LK.getAsset('healthBarBg', { anchorX: 0, anchorY: 0, width: 300, height: 40 }); healthBarBg.addChild(healthBg); // Fill bar var healthFill = LK.getAsset('healthBar', { anchorX: 0, anchorY: 0, width: 300, height: 40 }); healthBarFill.addChild(healthFill); // Add to GUI LK.gui.bottom.addChild(healthBarBg); LK.gui.bottom.addChild(healthBarFill); LK.gui.bottom.addChild(healthBarTxt); // Position health bar healthBarBg.x = -150; // Center the healthbar healthBarFill.x = -150; // Center the healthbar fill healthBarTxt.x = 0; // Center the text healthBarBg.y = -80; // Position below start wave button (which is at -150) healthBarFill.y = -80; // Match background position healthBarTxt.y = -60; // Position text in middle of healthbar } // Update health bar function updateHealthBar() { var healthPercent = heartTree.health / heartTree.maxHealth; healthBarFill.scale.x = healthPercent; healthBarTxt.setText(heartTree.health + '/' + heartTree.maxHealth); // Update color based on health percentage var fill = healthBarFill.getChildAt(0); if (healthPercent > 0.6) { fill.tint = 0x00FF00; // Green } else if (healthPercent > 0.3) { fill.tint = 0xFFFF00; // Yellow } else { fill.tint = 0xFF0000; // Red } // Flash effect when health is low if (healthPercent < 0.2 && LK.ticks % 60 < 30) { healthBarFill.alpha = 0.7; } else { healthBarFill.alpha = 1.0; } } // Create game mode buttons var guardianBtn = new Container(); var buildBtn = new Container(); function setupModeButtons() { // Guardian mode button var guardianBtnBg = LK.getAsset('guardian', { anchorX: 0.5, anchorY: 0.5, width: 80, height: 80 }); guardianBtn.addChild(guardianBtnBg); var guardianBtnTxt = new Text2('Move', { size: 30, fill: 0xFFFFFF }); guardianBtnTxt.anchor.set(0.5, 0.5); guardianBtnTxt.y = 50; guardianBtn.addChild(guardianBtnTxt); // Building mode button var buildBtnBg = LK.getAsset('towerBase', { anchorX: 0.5, anchorY: 0.5, width: 80, height: 80 }); buildBtn.addChild(buildBtnBg); var buildBtnTxt = new Text2('Build', { size: 30, fill: 0xFFFFFF }); buildBtnTxt.anchor.set(0.5, 0.5); buildBtnTxt.y = 50; buildBtn.addChild(buildBtnTxt); // Add to GUI LK.gui.bottomLeft.addChild(guardianBtn); LK.gui.bottomLeft.addChild(buildBtn); // Position buttons guardianBtn.x = 100; guardianBtn.y = -100; buildBtn.x = 220; buildBtn.y = -100; // Add event listeners guardianBtn.interactive = true; guardianBtn.down = function () { setGameMode('guardian'); }; buildBtn.interactive = true; buildBtn.down = function () { setGameMode('building'); }; // Highlight current mode updateModeButtons(); } // Create tower selection buttons var thornBtn = new Container(); var shroomBtn = new Container(); var flameBtn = new Container(); var healingBtn = new Container(); function setupTowerButtons() { // Thorn tower button var thornBtnBg = LK.getAsset('thornTower', { anchorX: 0.5, anchorY: 0.5, width: 80, height: 80 }); thornBtn.addChild(thornBtnBg); var thornBtnTxt = new Text2('50', { size: 30, fill: 0xFFFFFF }); thornBtnTxt.anchor.set(0.5, 0.5); thornBtnTxt.y = 50; thornBtn.addChild(thornBtnTxt); // Shroom tower button var shroomBtnBg = LK.getAsset('shroomTower', { anchorX: 0.5, anchorY: 0.5, width: 80, height: 80 }); shroomBtn.addChild(shroomBtnBg); var shroomBtnTxt = new Text2('100', { size: 30, fill: 0xFFFFFF }); shroomBtnTxt.anchor.set(0.5, 0.5); shroomBtnTxt.y = 50; shroomBtn.addChild(shroomBtnTxt); // Flame tower button var flameBtnBg = LK.getAsset('flameTower', { anchorX: 0.5, anchorY: 0.5, width: 80, height: 80, tint: 0xFF3300 }); flameBtn.addChild(flameBtnBg); var flameBtnTxt = new Text2('150', { size: 30, fill: 0xFFFFFF }); flameBtnTxt.anchor.set(0.5, 0.5); flameBtnTxt.y = 50; flameBtn.addChild(flameBtnTxt); // Farm tower button var healingBtnBg = LK.getAsset('farmTower', { anchorX: 0.5, anchorY: 0.5, width: 80, height: 80 }); healingBtn.addChild(healingBtnBg); var healingBtnTxt = new Text2('200', { size: 30, fill: 0xFFFFFF }); healingBtnTxt.anchor.set(0.5, 0.5); healingBtnTxt.y = 50; healingBtn.addChild(healingBtnTxt); // Add to GUI LK.gui.bottomRight.addChild(thornBtn); LK.gui.bottomRight.addChild(shroomBtn); LK.gui.bottomRight.addChild(flameBtn); LK.gui.bottomRight.addChild(healingBtn); // Position buttons thornBtn.x = -100; thornBtn.y = -100; shroomBtn.x = -220; shroomBtn.y = -100; flameBtn.x = -100; flameBtn.y = -200; healingBtn.x = -220; healingBtn.y = -200; // Add event listeners thornBtn.interactive = true; thornBtn.down = function () { if (currentMode === 'building') { selectTower('thorn'); } }; shroomBtn.interactive = true; shroomBtn.down = function () { if (currentMode === 'building' && storage.unlockedTowers.includes('shroom')) { selectTower('shroom'); } }; flameBtn.interactive = true; flameBtn.down = function () { if (currentMode === 'building' && storage.unlockedTowers.includes('flame')) { selectTower('flame'); } }; healingBtn.interactive = true; healingBtn.down = function () { if (currentMode === 'building' && storage.unlockedTowers.includes('healing')) { selectTower('healing'); } }; // If towers are not unlocked, gray them out if (!storage.unlockedTowers.includes('shroom')) { shroomBtn.alpha = 0.5; } if (!storage.unlockedTowers.includes('flame')) { flameBtn.alpha = 0.5; } if (!storage.unlockedTowers.includes('healing')) { healingBtn.alpha = 0.5; } // Highlight selected tower updateTowerButtons(); } // Start wave button var startWaveBtn = new Container(); function setupStartWaveButton() { var startWaveButtonBg = LK.getAsset('waveButton', { anchorX: 0.5, anchorY: 0.5, width: 200, height: 80 }); startWaveBtn.addChild(startWaveButtonBg); var startWaveBtnTxt = new Text2('Start Wave', { size: 40, fill: 0xFFFFFF }); startWaveBtnTxt.anchor.set(0.5, 0.5); startWaveBtn.addChild(startWaveBtnTxt); // Add to GUI LK.gui.bottom.addChild(startWaveBtn); // Position button startWaveBtn.y = -150; // Pulsing animation for wave button when not in wave var pulseInterval = LK.setInterval(function () { if (!waveInProgress) { var pulseFactor = 1 + Math.sin(LK.ticks / 20) * 0.1; startWaveBtn.scale.set(pulseFactor, pulseFactor); } else { startWaveBtn.scale.set(1, 1); } }, 100); // Add event listener startWaveBtn.interactive = true; startWaveBtn.down = function () { if (!waveInProgress) { startWave(); } }; } function updateModeButtons() { guardianBtn.alpha = currentMode === 'guardian' ? 1.0 : 0.6; buildBtn.alpha = currentMode === 'building' ? 1.0 : 0.6; } function updateTowerButtons() { thornBtn.alpha = currentMode === 'building' && selectedTower === 'thorn' ? 1.0 : 0.6; shroomBtn.alpha = currentMode === 'building' && selectedTower === 'shroom' && storage.unlockedTowers.includes('shroom') ? 1.0 : 0.3; flameBtn.alpha = currentMode === 'building' && selectedTower === 'flame' && storage.unlockedTowers.includes('flame') ? 1.0 : 0.3; healingBtn.alpha = currentMode === 'building' && selectedTower === 'healing' && storage.unlockedTowers.includes('healing') ? 1.0 : 0.3; } function setGameMode(mode) { currentMode = mode; updateModeButtons(); updateTowerButtons(); } function selectTower(tower) { selectedTower = tower; updateTowerButtons(); } function updateEssenceText() { essenceTxt.setText('💎 ' + essence); // Flash effect when essence is gained/lost LK.effects.flashObject(essenceTxt, 0x00FFFF, 300); } function createLevel() { // Clear any existing level elements for (var i = 0; i < buildingSpots.length; i++) { buildingSpots[i].destroy(); } buildingSpots = []; path = []; spawnPoints = []; // Level dimensions (in grid cells) var levelWidth = 15; var levelHeight = 19; // Create a grid for the level var grid = []; for (var y = 0; y < levelHeight; y++) { grid[y] = []; for (var x = 0; x < levelWidth; x++) { grid[y][x] = 0; // 0 = empty } } // Define path through the level (1 = path) var pathCoords = [[0, 9], [1, 9], [2, 9], [3, 9], [4, 9], [5, 9], [6, 9], [6, 8], [6, 7], [6, 6], [6, 5], [7, 5], [8, 5], [9, 5], [9, 6], [9, 7], [9, 8], [9, 9], [9, 10], [9, 11], [8, 11], [7, 11], [6, 11], [5, 11], [4, 11], [4, 12], [4, 13], [4, 14], [4, 15], [5, 15], [6, 15], [7, 15], [8, 15], [9, 15], [10, 15], [11, 15], [11, 14], [11, 13], [11, 12], [11, 11], [11, 10], [11, 9], [11, 8], [11, 7], [12, 7], [13, 7], [14, 7]]; for (var i = 0; i < pathCoords.length; i++) { var _pathCoords$i = _slicedToArray(pathCoords[i], 2), x = _pathCoords$i[0], y = _pathCoords$i[1]; grid[y][x] = 1; } // Set spawn point (2 = spawn) grid[9][0] = 2; // Set heart tree location (3 = heart tree) grid[7][14] = 3; // Create level elements based on grid for (var y = 0; y < levelHeight; y++) { for (var x = 0; x < levelWidth; x++) { var gridValue = grid[y][x]; var worldX = x * gridSize + gridSize / 2 + 174; // Center in grid cell with some offset var worldY = y * gridSize + gridSize / 2 + 150; if (gridValue === 1) { // Path tile var pathTile = new PathTile(); pathTile.x = worldX; pathTile.y = worldY; pathTile.gridX = x; pathTile.gridY = y; game.addChild(pathTile); // Add to path array for enemy movement path.push(pathTile); } else if (gridValue === 2) { // Spawn point var spawnPoint = new SpawnPoint(); spawnPoint.x = worldX; spawnPoint.y = worldY; game.addChild(spawnPoint); spawnPoints.push(spawnPoint); // Also add as first path point path.unshift(spawnPoint); } else if (gridValue === 3) { // Heart Tree heartTree = new HeartTree(); heartTree.x = worldX; heartTree.y = worldY; game.addChild(heartTree); } else if (gridValue === 0) { // Check if adjacent to path (can build here) var adjacentToPath = false; // Check all adjacent cells var directions = [[-1, 0], [1, 0], [0, -1], [0, 1]]; for (var _i = 0, _directions = directions; _i < _directions.length; _i++) { var dir = _directions[_i]; var adjX = x + dir[0]; var adjY = y + dir[1]; if (adjX >= 0 && adjX < levelWidth && adjY >= 0 && adjY < levelHeight && grid[adjY][adjX] === 1) { adjacentToPath = true; break; } } if (adjacentToPath) { // Create building spot var spot = new BuildingSpot(); spot.x = worldX; spot.y = worldY; spot.gridX = x; spot.gridY = y; game.addChild(spot); buildingSpots.push(spot); } } } } // Sort path from start to end path.sort(function (a, b) { return pathCoords.findIndex(function (coords) { return coords[0] === a.gridX && coords[1] === a.gridY; }) - pathCoords.findIndex(function (coords) { return coords[0] === b.gridX && coords[1] === b.gridY; }); }); // Create guardian var guardian = new Guardian(); guardian.x = path[Math.floor(path.length / 2)].x; guardian.y = path[Math.floor(path.length / 2)].y; game.addChild(guardian); } function startWave() { if (waveInProgress) { return; } currentWave++; waveTxt.setText('Wave: ' + currentWave); waveInProgress = true; startWaveBtn.alpha = 0.5; // Display wave start message var waveStartTxt = new Text2('Wave ' + currentWave + ' Starting!', { size: 80, fill: 0xFF8C00 }); waveStartTxt.anchor.set(0.5, 0.5); waveStartTxt.x = 2048 / 2; waveStartTxt.y = 2732 / 2; game.addChild(waveStartTxt); // Animate and remove wave start message tween(waveStartTxt, { alpha: 0, scaleX: 1.5, scaleY: 1.5 }, { duration: 1500, onFinish: function onFinish() { waveStartTxt.destroy(); } }); // Calculate enemies for this wave var numEnemies = 5 + Math.floor(currentWave * 1.5); var hasBoss = currentWave % 5 === 0; enemiesRemaining = numEnemies + (hasBoss ? 1 : 0); LK.getSound('waveStart').play(); // Spawn enemies periodically var enemiesSpawned = 0; waveTimer = LK.setInterval(function () { if (enemiesSpawned < numEnemies) { spawnEnemy(false); enemiesSpawned++; } else if (hasBoss && enemiesSpawned === numEnemies) { spawnEnemy(true); // Spawn boss enemiesSpawned++; } else { LK.clearInterval(waveTimer); } }, 1500); } function spawnEnemy(isBoss) { var enemy; if (isBoss) { enemy = new BossEnemy(); } else { // Randomly choose enemy type based on wave progression var randomType = Math.random(); if (currentWave >= 6 && randomType < 0.15) { // 15% chance of spawning flying enemy after wave 6 enemy = new FlyingEnemy(); // Scale difficulty with wave number enemy.maxHealth = 40 + currentWave * 5; enemy.health = enemy.maxHealth; enemy.speed = 3 + currentWave * 0.12; enemy.value = 18 + currentWave; } else if (currentWave >= 7 && randomType < 0.3) { // 15% chance of spawning poison enemy after wave 7 enemy = new PoisonEnemy(); // Scale difficulty with wave number enemy.maxHealth = 80 + currentWave * 8; enemy.health = enemy.maxHealth; enemy.speed = 1.8 + currentWave * 0.08; enemy.value = 20 + currentWave; enemy.shotInterval = Math.max(1.5, 3 - currentWave * 0.2); // Shoot faster at higher waves } else if (currentWave >= 5 && randomType < 0.45) { // 15% chance of spawning split enemy after wave 5 enemy = new SplitEnemy(); // Scale difficulty with wave number enemy.maxHealth = 70 + currentWave * 12; enemy.health = enemy.maxHealth; enemy.speed = 1.5 + currentWave * 0.06; enemy.value = 12 + currentWave; enemy.splitCount = 2 + Math.floor(currentWave / 10); // More splits at higher waves, max 4 } else if (currentWave >= 2 && randomType < 0.65) { // 20% chance of spawning speedy enemy after wave 2 enemy = new SpeedyEnemy(); // Scale difficulty with wave number enemy.maxHealth = 30 + currentWave * 6; enemy.health = enemy.maxHealth; enemy.speed = 4 + currentWave * 0.15; enemy.value = 15 + currentWave; } else if (currentWave >= 4 && randomType < 0.8) { // 15% chance of spawning tank enemy after wave 4 enemy = new TankEnemy(); // Scale difficulty with wave number enemy.maxHealth = 120 + currentWave * 15; enemy.health = enemy.maxHealth; enemy.speed = 1 + currentWave * 0.05; enemy.value = 25 + currentWave; } else { // Regular enemy enemy = new Enemy(); // Scale difficulty with wave number enemy.maxHealth = 50 + currentWave * 10; enemy.health = enemy.maxHealth; enemy.speed = 2 + currentWave * 0.1; enemy.value = 10 + currentWave; } } // Set initial position at spawn point var spawnPoint = spawnPoints[0]; enemy.x = spawnPoint.x; enemy.y = spawnPoint.y; game.addChild(enemy); enemies.push(enemy); // Apply animation for spawning enemy.alpha = 0; enemy.scale.set(0.5, 0.5); tween(enemy, { alpha: 1, scaleX: 1, scaleY: 1 }, { duration: 500 }); } function checkWaveComplete() { if (waveInProgress && enemies.length === 0 && enemiesRemaining === 0) { waveInProgress = false; startWaveBtn.alpha = 1.0; // Reward between waves var waveBonus = 50 + currentWave * 10; // Add farm tower income var farmIncome = 0; for (var i = 0; i < towers.length; i++) { if (towers[i] instanceof FarmTower) { var income = towers[i].getIncomePerWave(); farmIncome += income; // Show visual effect for farm income var incomeTxt = new Text2("+" + income, { size: 40, fill: 0xFFD700 }); incomeTxt.anchor.set(0.5, 0.5); incomeTxt.x = towers[i].x; incomeTxt.y = towers[i].y; game.addChild(incomeTxt); // Animate text tween(incomeTxt, { alpha: 0, y: incomeTxt.y - 50 }, { duration: 1000, onFinish: function onFinish() { incomeTxt.destroy(); } }); } } essence += waveBonus + farmIncome; updateEssenceText(); // Show wave complete message var waveBonusTxt = new Text2('Wave Complete! +' + waveBonus + ' Essence', { size: 80, fill: 0xFFFFFF }); waveBonusTxt.anchor.set(0.5, 0.5); waveBonusTxt.x = 2048 / 2; waveBonusTxt.y = 2732 / 2; game.addChild(waveBonusTxt); // Animate and remove message tween(waveBonusTxt, { alpha: 0, y: waveBonusTxt.y - 100 }, { duration: 2000, onFinish: function onFinish() { waveBonusTxt.destroy(); } }); // Unlock new tower types based on wave progress if (currentWave === 3 && !storage.unlockedTowers.includes('shroom')) { storage.unlockedTowers.push('shroom'); shroomBtn.alpha = 0.6; // Show unlock message var unlockTxt = new Text2('New Tower Unlocked: Shroom Puff!', { size: 60, fill: 0xFFFF00 }); unlockTxt.anchor.set(0.5, 0.5); unlockTxt.x = 2048 / 2; unlockTxt.y = 2732 / 2 + 100; game.addChild(unlockTxt); // Animate and remove message tween(unlockTxt, { alpha: 0, y: unlockTxt.y - 100 }, { duration: 3000, onFinish: function onFinish() { unlockTxt.destroy(); } }); } else if (currentWave === 5 && !storage.unlockedTowers.includes('flame')) { storage.unlockedTowers.push('flame'); flameBtn.alpha = 0.6; // Show unlock message var unlockTxt = new Text2('New Tower Unlocked: Flame Fern!', { size: 60, fill: 0xFF5500 }); unlockTxt.anchor.set(0.5, 0.5); unlockTxt.x = 2048 / 2; unlockTxt.y = 2732 / 2 + 100; game.addChild(unlockTxt); // Animate and remove message tween(unlockTxt, { alpha: 0, y: unlockTxt.y - 100 }, { duration: 3000, onFinish: function onFinish() { unlockTxt.destroy(); } }); } else if (currentWave === 8 && !storage.unlockedTowers.includes('healing')) { storage.unlockedTowers.push('healing'); healingBtn.alpha = 0.6; // Show unlock message var unlockTxt = new Text2('New Tower Unlocked: Farm Shroom!', { size: 60, fill: 0xFFD700 }); unlockTxt.anchor.set(0.5, 0.5); unlockTxt.x = 2048 / 2; unlockTxt.y = 2732 / 2 + 100; game.addChild(unlockTxt); // Animate and remove message tween(unlockTxt, { alpha: 0, y: unlockTxt.y - 100 }, { duration: 3000, onFinish: function onFinish() { unlockTxt.destroy(); } }); } } } function isPositionValid(x, y) { // Check if position is within game bounds if (x < 0 || x > 2048 || y < 0 || y > 2732) { return false; } return true; } // Initialize game function initGame() { // Setup UI elements setupHealthBar(); setupModeButtons(); setupTowerButtons(); setupStartWaveButton(); // Create game level createLevel(); // Set initial game state LK.setScore(0); scoreTxt.setText('0'); updateEssenceText(); updateHealthBar(); // Create tower menu towerMenu = new TowerMenu(); game.addChild(towerMenu); // Make tower menu larger and more noticeable towerMenu.scale.set(1.2, 1.2); // Start background music LK.playMusic('forestAmbience', { loop: true }); } // Game event handlers game.down = function (x, y, obj) { // Hide tower menu when clicking on game area if (towerMenu && towerMenu.visible && obj.target === game) { towerMenu.hide(); } if (currentMode === 'guardian') { guardian.moveTo(x, y); } }; // Game update loop game.update = function () { // Update all game objects for (var i = 0; i < towers.length; i++) { towers[i].update(); } // Update enemies (backwards to safely remove) for (var i = enemies.length - 1; i >= 0; i--) { enemies[i].update(); if (!enemies[i].alive) { enemies[i].destroy(); enemies.splice(i, 1); enemiesRemaining--; } } // Update essence items (backwards to safely remove) for (var i = essenceItems.length - 1; i >= 0; i--) { essenceItems[i].update(); if (essenceItems[i].collected) { essenceItems[i].destroy(); essenceItems.splice(i, 1); } } // Update guardian guardian.update(); // Check if wave is complete checkWaveComplete(); }; // Display title screen instead of directly initializing game var titleScreen = new TitleScreen(); titleScreen.x = 2048 / 2; titleScreen.y = 2732 / 2; game.addChild(titleScreen); // Play background music on title screen LK.playMusic('forestAmbience', { loop: true });
===================================================================
--- original.js
+++ change.js
@@ -1771,9 +1771,9 @@
return coords[0] === b.gridX && coords[1] === b.gridY;
});
});
// Create guardian
- guardian = new Guardian();
+ var guardian = new Guardian();
guardian.x = path[Math.floor(path.length / 2)].x;
guardian.y = path[Math.floor(path.length / 2)].y;
game.addChild(guardian);
}
top-down angle of a tree. In-Game asset. 2d. High contrast. No shadows
thorn flower. In-Game asset. 2d. High contrast. No shadows
purple mushroom. In-Game asset. 2d. High contrast. No shadows
guardian of the forest. In-Game asset. 2d. High contrast. No shadows
mini cave invader that is covered in crystals and is made of stone. In-Game asset. 2d. High contrast. No shadows
GIANT cave boss invader that is covered in crystal diamonds and is made of stone.. In-Game asset. 2d. High contrast. No shadows
mini farm tile. In-Game asset. 2d. High contrast. No shadows
flaming flower. In-Game asset. 2d. High contrast. No shadows
cave enterance. In-Game asset. 2d. High contrast. No shadows
car parked at an angle. In-Game asset. 2d. High contrast. No shadows
top-down angle of a tire. In-Game asset. 2d. High contrast. No shadows
finish line. In-Game asset. 2d. High contrast. No shadows
spilt acid barrel fallen over. In-Game asset. 2d. High contrast. No shadows
mutant tree. In-Game asset. 2d. High contrast. No shadows
soul. In-Game asset. 2d. High contrast. No shadows
planty spike ball with no stem. In-Game asset. 2d. High contrast. No shadows
Abandoned building that’s boarded up. In-Game asset. 2d. High contrast. No shadows
Rectangle button. In-Game asset. 2d. High contrast. No shadows
https://api.upit.com/img/IwopnnYt58_k_iLtUBxBq3JSiO4=/fit-in/256x256/filters:format(png):quality(85)/https://cdn.frvr.ai/680fe4cf833660e79135a83b.png%3F3 But empty. In-Game asset. 2d. High contrast. No shadows
Empty healthbar with no heart. In-Game asset. 2d. High contrast. No shadows
Hammer icon. In-Game asset. 2d. High contrast. No shadows
Garbage can icon. In-Game asset. 2d. High contrast. No shadows
Running feet icon. In-Game asset. 2d. High contrast. No shadows