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"] }); /**** * 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(); } 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(); } }; 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; } } }; 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 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 }); self.speed = 1.2; self.maxHealth = 300; self.health = self.maxHealth; self.value = 100; 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 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 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.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; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x2E5C2E // Forest green }); /**** * Game Code ****/ // Wave environment effects var environmentEffects = []; // Apply themed environment based on wave type function applyWaveEnvironment(waveNumber) { // Clear any existing environment effects for (var i = 0; i < environmentEffects.length; i++) { environmentEffects[i].destroy(); } environmentEffects = []; var waveTheme = waveNumber % 5; var gameBackgroundColor; if (waveTheme === 0) { // Boss wave - dark and ominous gameBackgroundColor = 0x1A1A2E; // Add ambient particles for boss wave (dark energy) for (var i = 0; i < 20; i++) { var particle = new Container(); var graphics = particle.attachAsset('essence', { anchorX: 0.5, anchorY: 0.5, tint: 0xFF00FF, alpha: 0.3 + Math.random() * 0.3 }); particle.x = Math.random() * 2048; particle.y = Math.random() * 2732; particle.scale.set(0.5 + Math.random() * 1); particle.speedX = (Math.random() - 0.5) * 2; particle.speedY = (Math.random() - 0.5) * 2; particle.update = function () { this.x += this.speedX; this.y += this.speedY; // Wrap around screen if (this.x < 0) { this.x = 2048; } if (this.x > 2048) { this.x = 0; } if (this.y < 0) { this.y = 2732; } if (this.y > 2732) { this.y = 0; } // Pulsate var scale = 0.5 + Math.sin(LK.ticks / 20 + this.x) * 0.2; this.scale.set(scale, scale); }; game.addChild(particle); environmentEffects.push(particle); } } else if (waveTheme === 1) { // Forest - lush green gameBackgroundColor = 0x2E5C2E; // Add ambient particles for forest (leaves/pollen) for (var i = 0; i < 15; i++) { var particle = new Container(); var graphics = particle.attachAsset('essence', { anchorX: 0.5, anchorY: 0.5, tint: 0xAAFF88, alpha: 0.4 + Math.random() * 0.3 }); particle.x = Math.random() * 2048; particle.y = Math.random() * 2732; particle.scale.set(0.3 + Math.random() * 0.3); particle.speedX = (Math.random() - 0.3) * 1; particle.speedY = -0.5 - Math.random() * 1; particle.update = function () { this.x += this.speedX; this.y += this.speedY; // Reset when off screen if (this.y < -20) { this.y = 2732 + 20; this.x = Math.random() * 2048; } }; game.addChild(particle); environmentEffects.push(particle); } } else if (waveTheme === 2) { // Swamp - murky gameBackgroundColor = 0x3A5311; // Add ambient particles for swamp (mist) for (var i = 0; i < 10; i++) { var particle = new Container(); var graphics = particle.attachAsset('buildingSpot', { anchorX: 0.5, anchorY: 0.5, tint: 0xCCDDAA, alpha: 0.15 + Math.random() * 0.1 }); particle.x = Math.random() * 2048; particle.y = Math.random() * 2732; particle.scale.set(3 + Math.random() * 5); particle.speedX = (Math.random() - 0.5) * 0.3; particle.update = function () { this.x += this.speedX; // Wrap around screen if (this.x < -200) { this.x = 2248; } if (this.x > 2248) { this.x = -200; } // Slight pulsating var scale = this.scale.x + Math.sin(LK.ticks / 100) * 0.05; this.scale.set(scale, scale); }; game.addChild(particle); environmentEffects.push(particle); } } else if (waveTheme === 3) { // Crystal river - blue/clear gameBackgroundColor = 0x3A6A9F; // Add ambient particles for crystal (sparkles) for (var i = 0; i < 25; i++) { var particle = new Container(); var graphics = particle.attachAsset('projectile', { anchorX: 0.5, anchorY: 0.5, tint: 0xFFFFFF, alpha: 0.4 + Math.random() * 0.6 }); particle.x = Math.random() * 2048; particle.y = Math.random() * 2732; particle.scale.set(0.2 + Math.random() * 0.3); particle.lifeTime = 30 + Math.random() * 60; particle.age = Math.random() * particle.lifeTime; particle.update = function () { this.age++; if (this.age >= this.lifeTime) { this.age = 0; this.x = Math.random() * 2048; this.y = Math.random() * 2732; this.alpha = 0; } // Fade in and out if (this.age < this.lifeTime * 0.2) { this.alpha = this.age / (this.lifeTime * 0.2) * 0.8; } else if (this.age > this.lifeTime * 0.8) { this.alpha = (this.lifeTime - this.age) / (this.lifeTime * 0.2) * 0.8; } }; game.addChild(particle); environmentEffects.push(particle); } } else if (waveTheme === 4) { // Mechanical - industrial gameBackgroundColor = 0x4A4A4A; // Add ambient particles for mechanical (smoke) for (var i = 0; i < 8; i++) { var particle = new Container(); var graphics = particle.attachAsset('buildingSpot', { anchorX: 0.5, anchorY: 0.5, tint: 0x888888, alpha: 0.2 + Math.random() * 0.1 }); particle.x = 200 + Math.random() * 1648; particle.y = 2732 + Math.random() * 100; particle.scale.set(1 + Math.random() * 3); particle.speedY = -0.8 - Math.random() * 0.5; particle.update = function () { this.y += this.speedY; this.x += Math.sin(this.y / 200) * 0.5; // Grow slightly as it rises this.scale.x += 0.003; this.scale.y += 0.003; // Fade out as it rises if (this.y < 1000) { this.alpha -= 0.001; } // Reset when off screen or fully transparent if (this.y < -300 || this.alpha <= 0) { this.y = 2732 + Math.random() * 100; this.x = 200 + Math.random() * 1648; this.scale.set(1 + Math.random() * 3); this.alpha = 0.2 + Math.random() * 0.1; } }; game.addChild(particle); environmentEffects.push(particle); } } // Apply background color transition tween(game, { backgroundColor: gameBackgroundColor }, { duration: 1000 }); } // 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 = []; // Tower costs var towerCosts = { 'thorn': 50, 'shroom': 100 }; // 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('Essence: 100', { size: 50, fill: 0x00FFFF }); essenceTxt.anchor.set(0, 0); 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('buildingSpot', { anchorX: 0, anchorY: 0, width: 300, height: 40, tint: 0x333333 }); healthBarBg.addChild(healthBg); // Fill bar var healthFill = LK.getAsset('buildingSpot', { anchorX: 0, anchorY: 0, width: 300, height: 40, tint: 0x00FF00 }); 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.y = -60; healthBarFill.y = -60; healthBarTxt.y = -40; } // 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 } } // 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(); 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); // Add to GUI LK.gui.bottomRight.addChild(thornBtn); LK.gui.bottomRight.addChild(shroomBtn); // Position buttons thornBtn.x = -100; thornBtn.y = -100; shroomBtn.x = -220; shroomBtn.y = -100; // 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'); } }; // If shroom tower is not unlocked, gray it out if (!storage.unlockedTowers.includes('shroom')) { shroomBtn.alpha = 0.5; } // Highlight selected tower updateTowerButtons(); } // Start wave button var startWaveBtn = new Container(); function setupStartWaveButton() { var startWaveButtonBg = LK.getAsset('buildingSpot', { anchorX: 0.5, anchorY: 0.5, width: 200, height: 80, tint: 0x4CAF50 }); 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; // 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; } function setGameMode(mode) { currentMode = mode; updateModeButtons(); updateTowerButtons(); } function selectTower(tower) { selectedTower = tower; updateTowerButtons(); } function updateEssenceText() { essenceTxt.setText('Essence: ' + essence); } function generateLevelForWave(waveNumber) { // Level dimensions (in grid cells) var levelWidth = 15; var levelHeight = 19; // Create an empty grid var grid = []; for (var y = 0; y < levelHeight; y++) { grid[y] = []; for (var x = 0; x < levelWidth; x++) { grid[y][x] = 0; // 0 = empty } } // Different path patterns based on wave number var pathCoords = []; var heartTreeX = 0; var heartTreeY = 0; var spawnX = 0; var spawnY = 0; // Create different map layouts for different waves if (waveNumber % 5 === 1) { // First map type (waves 1, 6, 11, etc) // Zigzag path from left to right 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]]; spawnX = 9; spawnY = 0; heartTreeX = 7; heartTreeY = 14; } else if (waveNumber % 5 === 2) { // Second map type (waves 2, 7, 12, etc) // Spiral path around center pathCoords = [[7, 0], [7, 1], [7, 2], [7, 3], [7, 4], [7, 5], [7, 6], [7, 7], [6, 7], [5, 7], [4, 7], [3, 7], [3, 8], [3, 9], [3, 10], [3, 11], [4, 11], [5, 11], [6, 11], [7, 11], [8, 11], [9, 11], [10, 11], [11, 11], [11, 10], [11, 9], [11, 8], [11, 7], [10, 7], [9, 7], [8, 7]]; spawnX = 7; spawnY = 0; heartTreeX = 8; heartTreeY = 7; } else if (waveNumber % 5 === 3) { // Third map type (waves 3, 8, 13, etc) // U-shaped path pathCoords = [[0, 3], [1, 3], [2, 3], [3, 3], [4, 3], [5, 3], [6, 3], [7, 3], [8, 3], [9, 3], [10, 3], [11, 3], [12, 3], [13, 3], [14, 3], [14, 4], [14, 5], [14, 6], [14, 7], [14, 8], [14, 9], [14, 10], [14, 11], [14, 12], [14, 13], [14, 14], [14, 15], [13, 15], [12, 15], [11, 15], [10, 15], [9, 15], [8, 15], [7, 15], [6, 15], [5, 15], [4, 15], [3, 15], [2, 15], [1, 15], [0, 15], [0, 14], [0, 13], [0, 12], [0, 11], [0, 10], [0, 9], [0, 8], [0, 7], [0, 6], [0, 5], [0, 4]]; spawnX = 0; spawnY = 3; heartTreeX = 7; heartTreeY = 9; } else if (waveNumber % 5 === 4) { // Fourth map type (waves 4, 9, 14, etc) // X-shaped crossing paths pathCoords = [[0, 0], [1, 1], [2, 2], [3, 3], [4, 4], [5, 5], [6, 6], [7, 7], [8, 8], [9, 9], [10, 10], [11, 11], [12, 12], [13, 13], [14, 14], [0, 14], [1, 13], [2, 12], [3, 11], [4, 10], [5, 9], [6, 8], [8, 6], [9, 5], [10, 4], [11, 3], [12, 2], [13, 1], [14, 0]]; spawnX = 0; spawnY = 0; heartTreeX = 7; heartTreeY = 7; } else { // Fifth map type (waves 5, 10, 15, etc) - boss waves // Circular path around center with multiple entrances var centerX = 7; var centerY = 9; var radius = 5; // Create a circular path for (var angle = 0; angle < 360; angle += 15) { var rad = angle * (Math.PI / 180); var x = Math.round(centerX + radius * Math.cos(rad)); var y = Math.round(centerY + radius * Math.sin(rad)); // Ensure coordinates are within bounds x = Math.max(0, Math.min(x, levelWidth - 1)); y = Math.max(0, Math.min(y, levelHeight - 1)); pathCoords.push([x, y]); } // Add entrance paths from each side pathCoords = pathCoords.concat([[0, 9], [1, 9], [2, 9], [3, 9], [14, 9], [13, 9], [12, 9], [11, 9], [7, 0], [7, 1], [7, 2], [7, 3], [7, 4], [7, 18], [7, 17], [7, 16], [7, 15], [7, 14]]); spawnX = 0; spawnY = 9; heartTreeX = 7; heartTreeY = 9; } // Set path on grid (1 = path) for (var i = 0; i < pathCoords.length; i++) { var coords = pathCoords[i]; var x = coords[0]; var y = coords[1]; // Ensure coordinates are within bounds if (x >= 0 && x < levelWidth && y >= 0 && y < levelHeight) { grid[y][x] = 1; } } // Set spawn point (2 = spawn) grid[spawnY][spawnX] = 2; // Set heart tree location (3 = heart tree) grid[heartTreeY][heartTreeX] = 3; return { grid: grid, pathCoords: pathCoords }; } function createLevel() { // Clear any existing level elements for (var i = 0; i < buildingSpots.length; i++) { buildingSpots[i].destroy(); } // Clear existing game elements for (var i = 0; i < towers.length; i++) { towers[i].destroy(); } for (var i = 0; i < enemies.length; i++) { enemies[i].destroy(); } for (var i = 0; i < essenceItems.length; i++) { essenceItems[i].destroy(); } buildingSpots = []; path = []; spawnPoints = []; towers = []; enemies = []; essenceItems = []; // Generate level data based on current wave var levelData = generateLevelForWave(currentWave); var grid = levelData.grid; var pathCoords = levelData.pathCoords; var levelWidth = grid[0].length; var levelHeight = grid.length; // 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 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); // Get wave theme name var waveTheme = ""; var textColor = 0xFFFF00; switch (currentWave % 5) { case 1: waveTheme = "Forest"; textColor = 0x00FF00; break; case 2: waveTheme = "Swamp"; textColor = 0x99CC33; break; case 3: waveTheme = "Crystal River"; textColor = 0x33CCFF; break; case 4: waveTheme = "Mechanical"; textColor = 0xCCCCCC; break; case 0: waveTheme = "BOSS"; textColor = 0xFF00FF; break; } // Show wave transition message with theme var transitionText = new Text2('Wave ' + currentWave + ' - ' + waveTheme + ' Incoming!', { size: 90, fill: textColor }); transitionText.anchor.set(0.5, 0.5); transitionText.x = 2048 / 2; transitionText.y = 2732 / 2; game.addChild(transitionText); // Flash screen to signal wave transition (color based on theme) var flashColor; switch (currentWave % 5) { case 1: flashColor = 0x004400; break; // Forest - dark green case 2: flashColor = 0x445500; break; // Swamp - swampy green case 3: flashColor = 0x000088; break; // Crystal - blue case 4: flashColor = 0x444444; break; // Mechanical - grey case 0: flashColor = 0x440044; break; // Boss - purple } LK.effects.flashScreen(flashColor, 800); // Apply the environment effects for this wave applyWaveEnvironment(currentWave); // Animate the transition text tween(transitionText, { scaleX: 1.5, scaleY: 1.5, alpha: 0 }, { duration: 1500, easing: tween.easeOut, onFinish: function onFinish() { transitionText.destroy(); // Generate new level layout for this wave createLevel(); // Set game state for the new wave waveInProgress = true; startWaveBtn.alpha = 0.5; // 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(); // Make boss appearance based on wave type if (currentWave % 15 === 5) { // Forest boss (tint green) enemy.getChildAt(0).tint = 0x00AA00; } else if (currentWave % 15 === 10) { // Mechanical boss (tint silver) enemy.getChildAt(0).tint = 0xCCCCCC; } else { // Corrupted boss (tint purple) enemy.getChildAt(0).tint = 0x9900FF; } } else { 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; // Customize enemy appearance based on wave type var waveTheme = currentWave % 5; if (waveTheme === 1) { // Forest enemies (green) enemy.getChildAt(0).tint = 0x33CC33; } else if (waveTheme === 2) { // Swamp enemies (muddy brown) enemy.getChildAt(0).tint = 0x996633; enemy.speed *= 0.8; // Slower in swamp } else if (waveTheme === 3) { // Crystal enemies (blue) enemy.getChildAt(0).tint = 0x3399FF; enemy.maxHealth *= 0.8; // Less health but... enemy.health = enemy.maxHealth; enemy.speed *= 1.2; // ...faster } else if (waveTheme === 4) { // Mechanical enemies (gray) enemy.getChildAt(0).tint = 0x999999; enemy.maxHealth *= 1.5; // More health but... enemy.health = enemy.maxHealth; enemy.speed *= 0.9; // ...slower } else { // Corrupted enemies (purple) enemy.getChildAt(0).tint = 0x9933CC; } } // 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 with themed effects enemy.alpha = 0; enemy.scale.set(0.5, 0.5); // Different spawn animations based on wave type var waveTheme = currentWave % 5; if (waveTheme === 0 && isBoss) { // Boss wave - dramatic entrance tween(enemy, { alpha: 1, scaleX: 1.3, scaleY: 1.3 }, { duration: 800, easing: tween.elasticOut, onFinish: function onFinish() { // Return to normal size tween(enemy, { scaleX: 1, scaleY: 1 }, { duration: 300 }); } }); } else { // Regular spawn animation with different tweens based on theme var tweenConfig = { alpha: 1, scaleX: 1, scaleY: 1 }; var tweenOptions = { duration: 500 }; if (waveTheme === 2) { // Swamp - sluggish tweenOptions.duration = 800; tweenOptions.easing = tween.easeOut; } else if (waveTheme === 3) { // Crystal - bouncy tweenOptions.duration = 400; tweenOptions.easing = tween.bounceOut; } else if (waveTheme === 4) { // Mechanical - robotic tweenOptions.easing = function (t) { return t < 0.5 ? 2 * t : 2 * (1 - t); }; } tween(enemy, tweenConfig, tweenOptions); //{5K}{5L} } } function checkWaveComplete() { if (waveInProgress && enemies.length === 0 && enemiesRemaining === 0) { waveInProgress = false; startWaveBtn.alpha = 1.0; // Reward between waves var waveBonus = 50 + currentWave * 10; essence += waveBonus; 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 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(); } }); } } } 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(); // Set initial game state currentWave = 0; // Start with intro map LK.setScore(0); scoreTxt.setText('0'); updateEssenceText(); // Create initial game level createLevel(); updateHealthBar(); // Show welcome message var welcomeTxt = new Text2('Warden of the Woods\nDefend the Heart Tree!', { size: 80, fill: 0x00FF00 }); welcomeTxt.anchor.set(0.5, 0.5); welcomeTxt.x = 2048 / 2; welcomeTxt.y = 2732 / 2 - 200; game.addChild(welcomeTxt); // Animate welcome message tween(welcomeTxt, { alpha: 0, y: welcomeTxt.y - 100 }, { duration: 4000, easing: tween.easeOut, onFinish: function onFinish() { welcomeTxt.destroy(); } }); // Start background music LK.playMusic('forestAmbience', { loop: true }); } // Game event handlers game.down = function (x, y, obj) { 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 environment effects for (var i = 0; i < environmentEffects.length; i++) { if (environmentEffects[i].update) { environmentEffects[i].update(); } } // Update guardian guardian.update(); // Check if wave is complete checkWaveComplete(); }; // Initialize the game initGame();
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
highScore: 0,
unlockedTowers: ["thorn"]
});
/****
* 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();
}
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();
}
};
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;
}
}
};
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 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
});
self.speed = 1.2;
self.maxHealth = 300;
self.health = self.maxHealth;
self.value = 100;
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
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 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.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;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x2E5C2E // Forest green
});
/****
* Game Code
****/
// Wave environment effects
var environmentEffects = [];
// Apply themed environment based on wave type
function applyWaveEnvironment(waveNumber) {
// Clear any existing environment effects
for (var i = 0; i < environmentEffects.length; i++) {
environmentEffects[i].destroy();
}
environmentEffects = [];
var waveTheme = waveNumber % 5;
var gameBackgroundColor;
if (waveTheme === 0) {
// Boss wave - dark and ominous
gameBackgroundColor = 0x1A1A2E;
// Add ambient particles for boss wave (dark energy)
for (var i = 0; i < 20; i++) {
var particle = new Container();
var graphics = particle.attachAsset('essence', {
anchorX: 0.5,
anchorY: 0.5,
tint: 0xFF00FF,
alpha: 0.3 + Math.random() * 0.3
});
particle.x = Math.random() * 2048;
particle.y = Math.random() * 2732;
particle.scale.set(0.5 + Math.random() * 1);
particle.speedX = (Math.random() - 0.5) * 2;
particle.speedY = (Math.random() - 0.5) * 2;
particle.update = function () {
this.x += this.speedX;
this.y += this.speedY;
// Wrap around screen
if (this.x < 0) {
this.x = 2048;
}
if (this.x > 2048) {
this.x = 0;
}
if (this.y < 0) {
this.y = 2732;
}
if (this.y > 2732) {
this.y = 0;
}
// Pulsate
var scale = 0.5 + Math.sin(LK.ticks / 20 + this.x) * 0.2;
this.scale.set(scale, scale);
};
game.addChild(particle);
environmentEffects.push(particle);
}
} else if (waveTheme === 1) {
// Forest - lush green
gameBackgroundColor = 0x2E5C2E;
// Add ambient particles for forest (leaves/pollen)
for (var i = 0; i < 15; i++) {
var particle = new Container();
var graphics = particle.attachAsset('essence', {
anchorX: 0.5,
anchorY: 0.5,
tint: 0xAAFF88,
alpha: 0.4 + Math.random() * 0.3
});
particle.x = Math.random() * 2048;
particle.y = Math.random() * 2732;
particle.scale.set(0.3 + Math.random() * 0.3);
particle.speedX = (Math.random() - 0.3) * 1;
particle.speedY = -0.5 - Math.random() * 1;
particle.update = function () {
this.x += this.speedX;
this.y += this.speedY;
// Reset when off screen
if (this.y < -20) {
this.y = 2732 + 20;
this.x = Math.random() * 2048;
}
};
game.addChild(particle);
environmentEffects.push(particle);
}
} else if (waveTheme === 2) {
// Swamp - murky
gameBackgroundColor = 0x3A5311;
// Add ambient particles for swamp (mist)
for (var i = 0; i < 10; i++) {
var particle = new Container();
var graphics = particle.attachAsset('buildingSpot', {
anchorX: 0.5,
anchorY: 0.5,
tint: 0xCCDDAA,
alpha: 0.15 + Math.random() * 0.1
});
particle.x = Math.random() * 2048;
particle.y = Math.random() * 2732;
particle.scale.set(3 + Math.random() * 5);
particle.speedX = (Math.random() - 0.5) * 0.3;
particle.update = function () {
this.x += this.speedX;
// Wrap around screen
if (this.x < -200) {
this.x = 2248;
}
if (this.x > 2248) {
this.x = -200;
}
// Slight pulsating
var scale = this.scale.x + Math.sin(LK.ticks / 100) * 0.05;
this.scale.set(scale, scale);
};
game.addChild(particle);
environmentEffects.push(particle);
}
} else if (waveTheme === 3) {
// Crystal river - blue/clear
gameBackgroundColor = 0x3A6A9F;
// Add ambient particles for crystal (sparkles)
for (var i = 0; i < 25; i++) {
var particle = new Container();
var graphics = particle.attachAsset('projectile', {
anchorX: 0.5,
anchorY: 0.5,
tint: 0xFFFFFF,
alpha: 0.4 + Math.random() * 0.6
});
particle.x = Math.random() * 2048;
particle.y = Math.random() * 2732;
particle.scale.set(0.2 + Math.random() * 0.3);
particle.lifeTime = 30 + Math.random() * 60;
particle.age = Math.random() * particle.lifeTime;
particle.update = function () {
this.age++;
if (this.age >= this.lifeTime) {
this.age = 0;
this.x = Math.random() * 2048;
this.y = Math.random() * 2732;
this.alpha = 0;
}
// Fade in and out
if (this.age < this.lifeTime * 0.2) {
this.alpha = this.age / (this.lifeTime * 0.2) * 0.8;
} else if (this.age > this.lifeTime * 0.8) {
this.alpha = (this.lifeTime - this.age) / (this.lifeTime * 0.2) * 0.8;
}
};
game.addChild(particle);
environmentEffects.push(particle);
}
} else if (waveTheme === 4) {
// Mechanical - industrial
gameBackgroundColor = 0x4A4A4A;
// Add ambient particles for mechanical (smoke)
for (var i = 0; i < 8; i++) {
var particle = new Container();
var graphics = particle.attachAsset('buildingSpot', {
anchorX: 0.5,
anchorY: 0.5,
tint: 0x888888,
alpha: 0.2 + Math.random() * 0.1
});
particle.x = 200 + Math.random() * 1648;
particle.y = 2732 + Math.random() * 100;
particle.scale.set(1 + Math.random() * 3);
particle.speedY = -0.8 - Math.random() * 0.5;
particle.update = function () {
this.y += this.speedY;
this.x += Math.sin(this.y / 200) * 0.5;
// Grow slightly as it rises
this.scale.x += 0.003;
this.scale.y += 0.003;
// Fade out as it rises
if (this.y < 1000) {
this.alpha -= 0.001;
}
// Reset when off screen or fully transparent
if (this.y < -300 || this.alpha <= 0) {
this.y = 2732 + Math.random() * 100;
this.x = 200 + Math.random() * 1648;
this.scale.set(1 + Math.random() * 3);
this.alpha = 0.2 + Math.random() * 0.1;
}
};
game.addChild(particle);
environmentEffects.push(particle);
}
}
// Apply background color transition
tween(game, {
backgroundColor: gameBackgroundColor
}, {
duration: 1000
});
}
// 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 = [];
// Tower costs
var towerCosts = {
'thorn': 50,
'shroom': 100
};
// 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('Essence: 100', {
size: 50,
fill: 0x00FFFF
});
essenceTxt.anchor.set(0, 0);
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('buildingSpot', {
anchorX: 0,
anchorY: 0,
width: 300,
height: 40,
tint: 0x333333
});
healthBarBg.addChild(healthBg);
// Fill bar
var healthFill = LK.getAsset('buildingSpot', {
anchorX: 0,
anchorY: 0,
width: 300,
height: 40,
tint: 0x00FF00
});
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.y = -60;
healthBarFill.y = -60;
healthBarTxt.y = -40;
}
// 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
}
}
// 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();
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);
// Add to GUI
LK.gui.bottomRight.addChild(thornBtn);
LK.gui.bottomRight.addChild(shroomBtn);
// Position buttons
thornBtn.x = -100;
thornBtn.y = -100;
shroomBtn.x = -220;
shroomBtn.y = -100;
// 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');
}
};
// If shroom tower is not unlocked, gray it out
if (!storage.unlockedTowers.includes('shroom')) {
shroomBtn.alpha = 0.5;
}
// Highlight selected tower
updateTowerButtons();
}
// Start wave button
var startWaveBtn = new Container();
function setupStartWaveButton() {
var startWaveButtonBg = LK.getAsset('buildingSpot', {
anchorX: 0.5,
anchorY: 0.5,
width: 200,
height: 80,
tint: 0x4CAF50
});
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;
// 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;
}
function setGameMode(mode) {
currentMode = mode;
updateModeButtons();
updateTowerButtons();
}
function selectTower(tower) {
selectedTower = tower;
updateTowerButtons();
}
function updateEssenceText() {
essenceTxt.setText('Essence: ' + essence);
}
function generateLevelForWave(waveNumber) {
// Level dimensions (in grid cells)
var levelWidth = 15;
var levelHeight = 19;
// Create an empty grid
var grid = [];
for (var y = 0; y < levelHeight; y++) {
grid[y] = [];
for (var x = 0; x < levelWidth; x++) {
grid[y][x] = 0; // 0 = empty
}
}
// Different path patterns based on wave number
var pathCoords = [];
var heartTreeX = 0;
var heartTreeY = 0;
var spawnX = 0;
var spawnY = 0;
// Create different map layouts for different waves
if (waveNumber % 5 === 1) {
// First map type (waves 1, 6, 11, etc)
// Zigzag path from left to right
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]];
spawnX = 9;
spawnY = 0;
heartTreeX = 7;
heartTreeY = 14;
} else if (waveNumber % 5 === 2) {
// Second map type (waves 2, 7, 12, etc)
// Spiral path around center
pathCoords = [[7, 0], [7, 1], [7, 2], [7, 3], [7, 4], [7, 5], [7, 6], [7, 7], [6, 7], [5, 7], [4, 7], [3, 7], [3, 8], [3, 9], [3, 10], [3, 11], [4, 11], [5, 11], [6, 11], [7, 11], [8, 11], [9, 11], [10, 11], [11, 11], [11, 10], [11, 9], [11, 8], [11, 7], [10, 7], [9, 7], [8, 7]];
spawnX = 7;
spawnY = 0;
heartTreeX = 8;
heartTreeY = 7;
} else if (waveNumber % 5 === 3) {
// Third map type (waves 3, 8, 13, etc)
// U-shaped path
pathCoords = [[0, 3], [1, 3], [2, 3], [3, 3], [4, 3], [5, 3], [6, 3], [7, 3], [8, 3], [9, 3], [10, 3], [11, 3], [12, 3], [13, 3], [14, 3], [14, 4], [14, 5], [14, 6], [14, 7], [14, 8], [14, 9], [14, 10], [14, 11], [14, 12], [14, 13], [14, 14], [14, 15], [13, 15], [12, 15], [11, 15], [10, 15], [9, 15], [8, 15], [7, 15], [6, 15], [5, 15], [4, 15], [3, 15], [2, 15], [1, 15], [0, 15], [0, 14], [0, 13], [0, 12], [0, 11], [0, 10], [0, 9], [0, 8], [0, 7], [0, 6], [0, 5], [0, 4]];
spawnX = 0;
spawnY = 3;
heartTreeX = 7;
heartTreeY = 9;
} else if (waveNumber % 5 === 4) {
// Fourth map type (waves 4, 9, 14, etc)
// X-shaped crossing paths
pathCoords = [[0, 0], [1, 1], [2, 2], [3, 3], [4, 4], [5, 5], [6, 6], [7, 7], [8, 8], [9, 9], [10, 10], [11, 11], [12, 12], [13, 13], [14, 14], [0, 14], [1, 13], [2, 12], [3, 11], [4, 10], [5, 9], [6, 8], [8, 6], [9, 5], [10, 4], [11, 3], [12, 2], [13, 1], [14, 0]];
spawnX = 0;
spawnY = 0;
heartTreeX = 7;
heartTreeY = 7;
} else {
// Fifth map type (waves 5, 10, 15, etc) - boss waves
// Circular path around center with multiple entrances
var centerX = 7;
var centerY = 9;
var radius = 5;
// Create a circular path
for (var angle = 0; angle < 360; angle += 15) {
var rad = angle * (Math.PI / 180);
var x = Math.round(centerX + radius * Math.cos(rad));
var y = Math.round(centerY + radius * Math.sin(rad));
// Ensure coordinates are within bounds
x = Math.max(0, Math.min(x, levelWidth - 1));
y = Math.max(0, Math.min(y, levelHeight - 1));
pathCoords.push([x, y]);
}
// Add entrance paths from each side
pathCoords = pathCoords.concat([[0, 9], [1, 9], [2, 9], [3, 9], [14, 9], [13, 9], [12, 9], [11, 9], [7, 0], [7, 1], [7, 2], [7, 3], [7, 4], [7, 18], [7, 17], [7, 16], [7, 15], [7, 14]]);
spawnX = 0;
spawnY = 9;
heartTreeX = 7;
heartTreeY = 9;
}
// Set path on grid (1 = path)
for (var i = 0; i < pathCoords.length; i++) {
var coords = pathCoords[i];
var x = coords[0];
var y = coords[1];
// Ensure coordinates are within bounds
if (x >= 0 && x < levelWidth && y >= 0 && y < levelHeight) {
grid[y][x] = 1;
}
}
// Set spawn point (2 = spawn)
grid[spawnY][spawnX] = 2;
// Set heart tree location (3 = heart tree)
grid[heartTreeY][heartTreeX] = 3;
return {
grid: grid,
pathCoords: pathCoords
};
}
function createLevel() {
// Clear any existing level elements
for (var i = 0; i < buildingSpots.length; i++) {
buildingSpots[i].destroy();
}
// Clear existing game elements
for (var i = 0; i < towers.length; i++) {
towers[i].destroy();
}
for (var i = 0; i < enemies.length; i++) {
enemies[i].destroy();
}
for (var i = 0; i < essenceItems.length; i++) {
essenceItems[i].destroy();
}
buildingSpots = [];
path = [];
spawnPoints = [];
towers = [];
enemies = [];
essenceItems = [];
// Generate level data based on current wave
var levelData = generateLevelForWave(currentWave);
var grid = levelData.grid;
var pathCoords = levelData.pathCoords;
var levelWidth = grid[0].length;
var levelHeight = grid.length;
// 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
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);
// Get wave theme name
var waveTheme = "";
var textColor = 0xFFFF00;
switch (currentWave % 5) {
case 1:
waveTheme = "Forest";
textColor = 0x00FF00;
break;
case 2:
waveTheme = "Swamp";
textColor = 0x99CC33;
break;
case 3:
waveTheme = "Crystal River";
textColor = 0x33CCFF;
break;
case 4:
waveTheme = "Mechanical";
textColor = 0xCCCCCC;
break;
case 0:
waveTheme = "BOSS";
textColor = 0xFF00FF;
break;
}
// Show wave transition message with theme
var transitionText = new Text2('Wave ' + currentWave + ' - ' + waveTheme + ' Incoming!', {
size: 90,
fill: textColor
});
transitionText.anchor.set(0.5, 0.5);
transitionText.x = 2048 / 2;
transitionText.y = 2732 / 2;
game.addChild(transitionText);
// Flash screen to signal wave transition (color based on theme)
var flashColor;
switch (currentWave % 5) {
case 1:
flashColor = 0x004400;
break;
// Forest - dark green
case 2:
flashColor = 0x445500;
break;
// Swamp - swampy green
case 3:
flashColor = 0x000088;
break;
// Crystal - blue
case 4:
flashColor = 0x444444;
break;
// Mechanical - grey
case 0:
flashColor = 0x440044;
break;
// Boss - purple
}
LK.effects.flashScreen(flashColor, 800);
// Apply the environment effects for this wave
applyWaveEnvironment(currentWave);
// Animate the transition text
tween(transitionText, {
scaleX: 1.5,
scaleY: 1.5,
alpha: 0
}, {
duration: 1500,
easing: tween.easeOut,
onFinish: function onFinish() {
transitionText.destroy();
// Generate new level layout for this wave
createLevel();
// Set game state for the new wave
waveInProgress = true;
startWaveBtn.alpha = 0.5;
// 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();
// Make boss appearance based on wave type
if (currentWave % 15 === 5) {
// Forest boss (tint green)
enemy.getChildAt(0).tint = 0x00AA00;
} else if (currentWave % 15 === 10) {
// Mechanical boss (tint silver)
enemy.getChildAt(0).tint = 0xCCCCCC;
} else {
// Corrupted boss (tint purple)
enemy.getChildAt(0).tint = 0x9900FF;
}
} else {
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;
// Customize enemy appearance based on wave type
var waveTheme = currentWave % 5;
if (waveTheme === 1) {
// Forest enemies (green)
enemy.getChildAt(0).tint = 0x33CC33;
} else if (waveTheme === 2) {
// Swamp enemies (muddy brown)
enemy.getChildAt(0).tint = 0x996633;
enemy.speed *= 0.8; // Slower in swamp
} else if (waveTheme === 3) {
// Crystal enemies (blue)
enemy.getChildAt(0).tint = 0x3399FF;
enemy.maxHealth *= 0.8; // Less health but...
enemy.health = enemy.maxHealth;
enemy.speed *= 1.2; // ...faster
} else if (waveTheme === 4) {
// Mechanical enemies (gray)
enemy.getChildAt(0).tint = 0x999999;
enemy.maxHealth *= 1.5; // More health but...
enemy.health = enemy.maxHealth;
enemy.speed *= 0.9; // ...slower
} else {
// Corrupted enemies (purple)
enemy.getChildAt(0).tint = 0x9933CC;
}
}
// 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 with themed effects
enemy.alpha = 0;
enemy.scale.set(0.5, 0.5);
// Different spawn animations based on wave type
var waveTheme = currentWave % 5;
if (waveTheme === 0 && isBoss) {
// Boss wave - dramatic entrance
tween(enemy, {
alpha: 1,
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 800,
easing: tween.elasticOut,
onFinish: function onFinish() {
// Return to normal size
tween(enemy, {
scaleX: 1,
scaleY: 1
}, {
duration: 300
});
}
});
} else {
// Regular spawn animation with different tweens based on theme
var tweenConfig = {
alpha: 1,
scaleX: 1,
scaleY: 1
};
var tweenOptions = {
duration: 500
};
if (waveTheme === 2) {
// Swamp - sluggish
tweenOptions.duration = 800;
tweenOptions.easing = tween.easeOut;
} else if (waveTheme === 3) {
// Crystal - bouncy
tweenOptions.duration = 400;
tweenOptions.easing = tween.bounceOut;
} else if (waveTheme === 4) {
// Mechanical - robotic
tweenOptions.easing = function (t) {
return t < 0.5 ? 2 * t : 2 * (1 - t);
};
}
tween(enemy, tweenConfig, tweenOptions); //{5K}{5L}
}
}
function checkWaveComplete() {
if (waveInProgress && enemies.length === 0 && enemiesRemaining === 0) {
waveInProgress = false;
startWaveBtn.alpha = 1.0;
// Reward between waves
var waveBonus = 50 + currentWave * 10;
essence += waveBonus;
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 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();
}
});
}
}
}
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();
// Set initial game state
currentWave = 0; // Start with intro map
LK.setScore(0);
scoreTxt.setText('0');
updateEssenceText();
// Create initial game level
createLevel();
updateHealthBar();
// Show welcome message
var welcomeTxt = new Text2('Warden of the Woods\nDefend the Heart Tree!', {
size: 80,
fill: 0x00FF00
});
welcomeTxt.anchor.set(0.5, 0.5);
welcomeTxt.x = 2048 / 2;
welcomeTxt.y = 2732 / 2 - 200;
game.addChild(welcomeTxt);
// Animate welcome message
tween(welcomeTxt, {
alpha: 0,
y: welcomeTxt.y - 100
}, {
duration: 4000,
easing: tween.easeOut,
onFinish: function onFinish() {
welcomeTxt.destroy();
}
});
// Start background music
LK.playMusic('forestAmbience', {
loop: true
});
}
// Game event handlers
game.down = function (x, y, obj) {
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 environment effects
for (var i = 0; i < environmentEffects.length; i++) {
if (environmentEffects[i].update) {
environmentEffects[i].update();
}
}
// Update guardian
guardian.update();
// Check if wave is complete
checkWaveComplete();
};
// Initialize the game
initGame();
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