Code edit (1 edits merged)
Please save this source code
User prompt
Warden of the Woods: Grove Defense
Initial prompt
🎮 Game Title: "Warden of the Woods: Grove Defense" Genre: Tower Defense Light Action-Adventure 🧙♂️ Core Concept: You are a forest spirit defending the Heart Tree at the center of the magical grove. Waves of corrupted creatures and machines attack from the forest edges, and you must build, upgrade, and strategically place magical defenses to protect it! 🏗️ Gameplay Mechanics: Tower Building: Plant different magical plants and trees as towers: 🌳 Thornroot Tree — Fires spikes at enemies. 🍄 Shroom Puff — Explodes spores that slow enemies. 🌸 Healing Bloom — Creates a healing aura for nearby defenses. 🔥 Flame Fern — Short-range, burns groups of enemies. Spirit Powers (You): While towers defend, you can run around casting quick spells: Magic burst (tiny damage). Speed boost for towers. Root trap (briefly freezes enemies). Resources: Enemies drop Nature Essence. Use it to plant, upgrade, or heal your towers. Wave System: After each wave, you get a brief pause to rebuild. Some waves have boss monsters (giant corrupted stags, metallic boars, etc). Upgrades Between Waves: Buff your powers. Evolve plants into stronger versions (e.g., Thornroot ➔ Bramble Hydra). 🌳 Setting: Enchanted glades, misty swamps, crystal-clear rivers. Each "world" has slightly different enemy types and environmental hazards (ex: in the swamp, enemies move slower, but so do you).
/**** * 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 ****/ // 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 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 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; // 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 { 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; 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(); // Create game level createLevel(); // Set initial game state LK.setScore(0); scoreTxt.setText('0'); updateEssenceText(); updateHealthBar(); // 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 guardian guardian.update(); // Check if wave is complete checkWaveComplete(); }; // Initialize the game initGame();
===================================================================
--- original.js
+++ change.js
@@ -1,6 +1,1000 @@
-/****
+/****
+* 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: 0x000000
-});
\ No newline at end of file
+ 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 = [];
+// 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 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
+ 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;
+ // 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 {
+ 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;
+ 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();
+ // Create game level
+ createLevel();
+ // Set initial game state
+ LK.setScore(0);
+ scoreTxt.setText('0');
+ updateEssenceText();
+ updateHealthBar();
+ // 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 guardian
+ guardian.update();
+ // Check if wave is complete
+ checkWaveComplete();
+};
+// Initialize the game
+initGame();
\ No newline at end of file
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